Server stops sending data to clients in -R after 1024 connection attempts
A means to measure network responsiveness and throughput
Brought to you by:
rjmcmahon
iperf version 2.1.n (15 March 2023) pthreads
Scenario:
40 network devices behind a NAT perform
2 second test downlink - iperf -c test_server%wwanX -e -w 1M -t 2 -yC -R
2 second test uplink - iperf -c test_server%wwanX -e -w 1M -t 2 -yC -R
After 1024 attemps both uplink and downlink on the server, the downlink connections stop getting data. We see the following error on the server side:
shutdown failed: Transport endpoint is not connected
When we Ctl-C on the server 1024 entries are shown (Uplink data output)
-0400:20230315151745.144,firewall-ip,3558,server-ip,5001,403,0.0-1048.6,145604,1110,114,114,0,0,0,0,0,0,0
Additionally the memory used on the server continues to grow until it reaches this point, at which 3.8G have been used.
For Reference System Specs:
12 core system with 30GB of ram.
Also the Server output never shows the uplink output until Ctl-C
Edit:
Took a closer look with top and 14.1G Virtual memory in use and 101% of a single core when this state is reached.
Last edit: Sean Farner 2023-03-15
We also tried breaking it up to 2 servers, one for downlink and one for uplink on another port:
This partially fixes the problem. We are able to continue receiving uplink and downlink data so far (currently around the 115th iteration out of 500), but we only have seen the first output on the uplink test server.
I assume that we wil probably see the rest after we hit Ctrl-C
We are seeing another issue as well, 2 processes with the same
iperf -s -w 1M -yC -e -p 5003command are at 100% CPU usage and a third has appeared that never goes above .7%.It stopped increasing in memory at 14.7G.
Our goal is to be able to do a test with 600 active devices 500 consecutive times with the 2s tcp down, 2s up iperf test.
I will continue retrying changing one of the arguments at a time -yC, -e and -w to see if any of these make a difference in the reporting of uplink.
When we remove the -yC and replaced it with --reportstyle C it showed reports of 1 device across 17 uplink transfers, the other 39 devices were held back until Ctl-C
We then removed the -yC all together, we got the first part of the server message:
[ 24] local 172.16.2.200 port 5003 connected with 172.16.10.135 port 13177 (icwnd/mss/irtt=12/1288/45059)but the
[ ID] Interval Transfer Bandwidth [157] 0.00-3.26 sec 440 KBytes 1.11 Mbits/sec [158] 0.00-2.75 sec 800 KBytes 2.38 Mbits/sec [159] 0.00-2.41 sec 1.38 MBytes 4.78 Mbits/secpart of the message did not appear until the Ctrl+C
removing -e had no change to behavior
Removed all args other than -s and -p 5003 (uplink) (both client and server)
Same behavior as removing the -yC it follows maybe 1 or 2 clients but hangs on reporting the rest until control C.
What operating system is being used here? Thanks.
Ubuntu 22.04
kernel 6.2.1-060201-generic
We was able to recreate this with Alma 8.4 as well 4.18 kernel.
Some additional information:
We used a third box with Centos 7 behind a different NAT and were able to recreate the issue.
Easy way to test, put the client behind a router and spawn 600 connections that repeat every 5 seconds. There will be a lack of reporting and you should see the memory usage of the process grow.
We may have found a work around for our test by interleaving TCP uplink and UDP uplink tests. If we follow a TCP uplink test with a UDP uplink test, all of the hung connections are cleaned up immediately. We well add more info as we find it.
Let us know if there is anything we can do to provide more information, like rebuild with a debug mode or something.
Thanks,
Sean
Have you used valgrind before? That could help with any memory leaks. You'll need to compile from source with symbols, e.g, make -j CFLAGS=-g CXXFLAGS=-g
I am familiar with valgrind. I'll take a look as soon as I get a chance.
Thanks, memory leaks are near impossible to find without tools like valgrind.
Also, there is an option to enable thread debugging. It's another compile time option, i.e. ./configure --enable-thread-debug Output looks something like:
Thanks for all of this. I haven't had a chance to investigate yet. Hopefully soon.
Sorry for the delay, we ended up altering our test procedure to work around the issue as much as we could.(We started 64 servers to spread the load) I can also provide a little more insight on what is happening after digging in to the network side a little more. During our network exercise, we encountered this problem primarily on UDP downlink testing (-R)
When we had 400-600 connections behind a NAT, whether it be a router or the EPC of a cellular network, there appears to be a problem with getting the socket FIN packet through that tells iperf the client is closing the socket. (Verified in tcpdump/wireshark). Since the client never sees the acknowledgement the socket is stuck in FIN_WAIT_1 state waiting for the ack from the server.
I was browsing the iperf2 source a little and noticed that it calls recv without any sort of select or timeout. The only escape from the recv while loop is a socket error when the socket is closed. Since we never get the FIN Packet through the NAT, the socket never knows to shut down, and the connection remains up.
We have two suggestions for ways to possibly solve this.
Add a -t flag for server side execution which would limit the time the socket stays open for the duration or duration+10% (I've seen a lot of iperf tests for 600 seconds run 605-610). Then at the end of time it would close the socket and report the results.
Add an optional flag for an adjustable timeout for the socket. If we go 15,30,60 seconds without receiving anything shutdown the socket, report and move on.
I realize this is probably not a use case many will encounter, but if I can get some time I will look at creating a new function
recvn_timed( int inSock, char *outBuf, int inLen, int flags, int timeout)
in socket_io.c. and see if this potentially solves the problem we see.
Thanks for the support.
Sean
Last edit: Sean Farner 2023-04-05