Archive for the ‘networking’ tag
A network daemon that runs on the Node.js platform and listens for statistics, like counters and timers, sent over UDP and sends aggregates to one or more pluggable backend services
We configured it to listen on its default port 8125 and then used netcat to send UDP packets to see if it was working like so:
echo -n "blah:36|c" | nc -w 1 -u -4 localhost 8125
We used tcpdump to capture any UDP packets on port 8125 like so:
tcpdump -i lo udp port 8125 -vv -X
To briefly explain the options we passed to it:
- -i lo only captures packets on the local loopback i.e. packets sent to localhost
- udp means that only UDP packets will be captured. Other types of packets we might capture could be tcp or icmp for example.
- -vv just gives us more verbose output
- -X prints out the data in the UDP packets in ASCII as well as hex. If we just wanted the latter we could use the -x option
This is what one of the messages received by tcpdump looks like:
13:16:40.317636 IP (tos 0x0, ttl 64, id 58103, offset 0, flags [DF], proto UDP (17), length 37) localhost.48358 > localhost.8125: [bad udp cksum 7c8f!] UDP, length 9 0x0000: 4500 0025 e2f7 4000 4011 59ce 7f00 0001 E..%..@.@.Y..... 0x0010: 7f00 0001 bce6 1fbd 0011 fe24 626c 6168 ...........$blah 0x0020: 3a33 367c 63 :36|c
The last three lines of this output detail the IP header, UDP header and the data in the packet. The following diagram is quite useful for understanding what each part of the IP header is defining:
This diagram defines things in terms of bits whereas the tcpdump output is in hexidecimal. Each block of 4 hexidecimal digits is equivalent to 16 bits.
There are a couple of parts of the IP header that might be interesting to us in this case.
The first 4 bits/1 digit define the IP version which is 4 in this case since we’re using IPv4.
The next 4 bits define the Internet Header length – the number of 32 bit words in the header. In this case the value is 5 so we know the total length of the IP header will be 160 bits (5 * 32 = 160).
The next few bits aren’t that interesting but we can see the source IP address at an offset of 96 bits and covers the next 32 bits:
|0x0000: 4500 0025 e2f7 4000 4011 59ce 7f00 0001|
We know this is going to represent an IPv4 address so it will be represented as ‘x.x.x.x’ where the maximum value of x is 255.
In this case since we’re just sending packets locally it translates to 127.0.0.1:
- 7f => 127
- 00 => 0
- 00 => 0
- 01 => 1
The next 32 bits are the destination IP address which has now gone onto the next line but is exactly the same:
|0x0010: 7f00 0001 bce6 1fbd 0011 fe24 626c 6168|
We’ve now covered 160 bits which means that the IP header is complete and we can move onto the IP payload which starts with the UDP header:
We start with the source port which is ‘bce6’ or 48358 in decimal. We can see that value referenced in the 2nd line of the tcpdump output as well.
The next 16 bits/4 digits are the destination port which is ‘1fbd’ or 8125 in decimal – exactly what we’d expect.
The next 32 bits/2 blocks of 4 digits define the length and checksum but after that we reach the data part of the packet which should contain ‘blah:36|c’.
The word ‘blah’ is defined like so:
00×62 is 98 in decimal and we can use a UTF-8 encoding table to see that 98 maps to the letter ‘b’.
00x6c is 108 or the letter ‘l’, 00×61 is 97 or the letter ‘a’ and 00×68 is 104 or the letter ‘h’
We wrap onto the last line for the rest of the data we wanted to send to statsd:
0x0020: 3a33 367c 63
It follows the same pattern though where 00x3a is 58 or the ‘:’ character and so on.
And now I have a slightly better idea of how to read tcpdump’s output than I did when I started writing this! As usual any tips or hints are welcome.
As part of some work we were doing last week Phil and I needed to send UDP packets to a local port and check that they were being picked up.
We initially tried sending a UDP packet to localhost port 8125 using netcat like so:
echo -n "hello" | nc -w 1 -u localhost 8125
That message wasn’t being received by the application listening on the port so Phil decided to try and send the same packet from Ruby which worked fine:
require 'socket' udp = UDPSocket.new udp.send("hello", 0, "localhost", 8125)
We eventually worked out that ‘localhost’ wasn’t being resolved by netcat which we thought was weird since it is correctly mapped:
~$ ping localhost PING localhost (127.0.0.1) 56(84) bytes of data. 64 bytes from localhost (127.0.0.1): icmp_seq=1 ttl=64 time=0.019 ms
We dumped the packets sent to port 8125 using tcpdump and noticed that the structure of the packets received when using localhost was massively different than when we used 127.0.0.1 but we couldn’t work out what exactly was going wrong.
A better way would actually have been to listen on the port with netcat like so:
nc -l -u 8125
In this case netcat doesn’t output anything when we use the netcat command but does with the Ruby one.
I came across this post which explains exactly what’s going on:
netcat is well behaved in that it will make the getaddrinfo() system call based on the arguments given to it.
In the case of giving it the name “localhost”, a port number and telling it to use UDP, netcat is not aware of the socket family to use for the getaddrinfo() system, so it will leave at it’s default 0.
getaddrinfo() will return all possible results for the query. In this case, where you aren’t either specifying an actual IPv4 address (127.0.0.1), getaddrinfo() is also returning the IPv6 result, which is showing up first and therefore being used.
If we try that out in Ruby we can see what’s going on:
> Socket.getaddrinfo("localhost", 10002, nil, :DGRAM) => [["AF_INET6", 10002, "::1", "::1", 10, 2, 17], ["AF_INET", 10002, "127.0.0.1", "127.0.0.1", 2, 2, 17]]
netcat picks the first result which is “::1” – the IPv6 version of ‘localhost’ – and sends a UDP packet to that address.
If we were using TCP then netcat would initially send a packet to that address, realise it had failed and then try the next address which is the IPv4 one.
However since UDP is connectionless netcat can fire the packet and then forget about it, which is exactly what happens!
We can get around the problem by passing the -4 flag to netcat which forces it to use IPv4 addresses only.
The ‘getaddrinfo’ lookup would presumably then look like this:
Socket.getaddrinfo("localhost", 10002, :INET, :DGRAM) => [["AF_INET", 10002, "127.0.0.1", "127.0.0.1", 2, 2, 17], ["AF_INET", 10002, "127.0.0.1", "127.0.0.1", 2, 2, 17]]
Our final netcat command to send UDP packets therefore looks like this:
echo -n "hello" | nc -w 1 -u -4 localhost 8125