netcat: Strange behaviour with UDP - only receives first packet sent
Date: 2013-07-30 06:01:47

I was playing around with http://nc110.sourceforge.net/[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:

[source,bash]
----

$ nc -kluv localhost 9000
----

We can check with lsof what running that command has done:

[source,bash]
----

$ 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:

[source,bash]
----

$ 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.

[source,bash]
----

$ 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:

[source,bash]
----

$ echo "mike" | nc -vvu localhost 9000 -4
Connection to localhost 9000 port [udp/cslistener] succeeded!
----

[source,bash]
----

$ nc -kluv localhost 9000
XXXXmark
----

I wasn't sure what was going on but eventually came across http://stackoverflow.com/questions/7696862/strange-behavoiur-of-netcat-with-udp[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:

[source,bash]
----

$ 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 https://twitter.com/natbobc[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:

[source,bash]
----

$ nc -kluvw 1 localhost 9000
----

And then started sending UDP packets from a netcat client also with a 1 second timeout:

[source,bash]
----

$ 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:

[source,bash]
----

$ 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:

[source,bash]
----

$ 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:

[source,bash]
----

$ lsof -Pni :9000
COMMAND   PID         USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
nc      64317 markhneedham    5u  IPv6 0xc99222a54b394c5d      0t0  UDP [::1]:9000
----