netcat: Strange behaviour with UDP - only receives first packet sent
I was playing around with netcat yesterday to create a client and server which would communicate via UDP packets and I rediscovered some "weird" behaviour which I’d previously encountered but not explained.
I started up a netcat server listening for UDP packets on port 9000 of my machine:
$ nc -kluv localhost 9000
We can check with lsof what running that command has done:
$ lsof -Pni :9000
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nc 63289 markhneedham 5u IPv6 0xc99222a54b3975b5 0t0 UDP [::1]:9000
We can see that the netcat process is listening on port 9000 so let’s send it a UDP packet, using another netcat process:
$ echo "mark" | nc -vvu localhost 9000 -4
Connection to localhost 9000 port [udp/cslistener] succeeded!
We can see the sending of the UDP packet was successful and it shows up on the netcat server’s terminal as well.
$ nc -kluv localhost 9000
XXXXmark
If we 'Ctrl-C' the netcat client and run it again we’ll notice that it’s still able to connect to the port but we don’t see the message in the netcat server’s terminal:
$ echo "mike" | nc -vvu localhost 9000 -4
Connection to localhost 9000 port [udp/cslistener] succeeded!
$ nc -kluv localhost 9000
XXXXmark
I wasn’t sure what was going on but eventually came across an explanation by David Schwartz:
When nc is listening to a UDP socket, it 'locks on' to the source port and source IP of the first packet it receives. As soon as it received its first datagram (from port 52832), it issued a connect system call 'connecting' it to the 127.0.0.1:52832. For UDP, a connect rejects all packets that don’t match the IP and port in the connect.
If we use lsof again we can see that we have exactly the same problem:
$ lsof -Pni :9000
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nc 63508 markhneedham 5u IPv6 0xc99222a54b394c5d 0t0 UDP [::1]:9000->[::1]:63732
In our case the netcat server has created a connection between port 9000 and port 63732 and rejects packets coming in from any other IP/port combination.
I thought 'Ctrl-C' on the netcat client would kill the connection but it doesn’t.
At this stage I wasn’t sure what to do but Nathan pointed out that if I made use of timeouts on the client and server then it would be possible to send multiple UDP packets.
I restarted the netcat server but this time with a 1 second timeout:
$ nc -kluvw 1 localhost 9000
And then started sending UDP packets from a netcat client also with a 1 second timeout:
$ echo -e "all" | nc -vvuw 1 localhost 9000
Connection to localhost 9000 port [udp/cslistener] succeeded!
$ echo -e "the" | nc -vvuw 1 localhost 9000
Connection to localhost 9000 port [udp/cslistener] succeeded!
$ echo -e "udp" | nc -vvuw 1 localhost 9000
Connection to localhost 9000 port [udp/cslistener] succeeded!
$ echo -e "packets" | nc -vvuw 1 localhost 9000
Connection to localhost 9000 port [udp/cslistener] succeeded!
And the netcat server now receives all of them:
$ nc -kluvw 1 localhost 9000
XXXXall
XXXXthe
XXXXudp
XXXXpackets
If we increase the client timeout a bit so that we can run lsof while it’s connected we can see that the connection exists for the duration of the timeout:
$ lsof -Pni :9000
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nc 64317 markhneedham 5u IPv6 0xc99222a54b394c5d 0t0 UDP [::1]:9000
nc 64541 markhneedham 5u IPv6 0xc99222a54cc0101d 0t0 UDP [::1]:55792->[::1]:9000
But then once it’s finished it goes back to generally listening for UDP packets on that port which is exactly what we want:
$ lsof -Pni :9000
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nc 64317 markhneedham 5u IPv6 0xc99222a54b394c5d 0t0 UDP [::1]:9000
About the author
I'm currently working on short form content at ClickHouse. I publish short 5 minute videos showing how to solve data problems on YouTube @LearnDataWithMark. I previously worked on graph analytics at Neo4j, where I also co-authored the O'Reilly Graph Algorithms Book with Amy Hodler.