Interpreting DHCP packets

To capture DHCP packets I ran:

 tcpdump port 67 -i vnet0 -vvvv  -w /tmp/packets.bin

That gave me a binary file 940 bytes long. This is actually 2 packets: the request and the response. This has the IP header, the UDP header, and the DHCP packet payload in it.

First, lets let tcpdump interpret the whole thing for us, and then we can starp picking it apart:

#  tcpdump -r /tmp/packets.bin -vvvv
reading from file /tmp/packets.bin, link-type EN10MB (Ethernet)
19:42:46.742937 IP (tos 0x0, ttl 64, id 278, offset 0, flags [none], proto UDP (17), length 428)
    0.0.0.0.bootpc > 255.255.255.255.bootps: [udp sum ok] BOOTP/DHCP, Request from 52:54:00:94:9e:f2 (oui Unknown), length 400, xid 0xff77df41, secs 4, Flags [none] (0x0000)
	  Client-Ethernet-Address 52:54:00:94:9e:f2 (oui Unknown)
	  Vendor-rfc1048 Extensions
	    Magic Cookie 0x63825363
	    DHCP-Message Option 53, length 1: Discover
	    MSZ Option 57, length 2: 1472
	    ARCH Option 93, length 2: 0
	    NDI Option 94, length 3: 1.2.1
	    Vendor-Class Option 60, length 32: "PXEClient:Arch:00000:UNDI:002001"
	    User-Class Option 77, length 4: 
	      instance#1: ERROR: invalid option
	    Parameter-Request Option 55, length 23: 
	      Subnet-Mask, Default-Gateway, Domain-Name-Server, LOG
	      Hostname, Domain-Name, RP, MTU
	      Vendor-Option, Vendor-Class, TFTP, BF
	      Option 119, Option 128, Option 129, Option 130
	      Option 131, Option 132, Option 133, Option 134
	      Option 135, Option 175, Option 203
	    T175 Option 175, length 48: 2969895296,2249199339,50397184,385941794,16847617,17891585,654377241,16846849,35717377,352387352,16852481,17957121
	    Client-ID Option 61, length 7: ether 52:54:00:94:9e:f2
	    GUID Option 97, length 17: 0.178.35.76.56.225.195.173.69.183.151.210.221.34.14.27.157
	    END Option 255, length 0
19:42:50.798338 IP (tos 0x0, ttl 64, id 666, offset 0, flags [none], proto UDP (17), length 428) Vendor-Class Option 60, length 32: "PXEClient:Arch:00000:UNDI:002001"
    0.0.0.0.bootpc > 255.255.255.255.bootps: [udp sum ok] BOOTP/DHCP, Request from 52:54:00:94:9e:f2 (oui Unknown), length 400, xid 0xff77df41, secs 10, Flags [none] (0x0000)
	  Client-Ethernet-Address 52:54:00:94:9e:f2 (oui Unknown)
	  Vendor-rfc1048 Extensions
	    Magic Cookie 0x63825363
	    DHCP-Message Option 53, length 1: Discover
	    MSZ Option 57, length 2: 1472
	    ARCH Option 93, length 2: 0
	    NDI Option 94, length 3: 1.2.1
	    Vendor-Class Option 60, length 32: "PXEClient:Arch:00000:UNDI:002001"
	    User-Class Option 77, length 4: 
	      instance#1: ERROR: invalid option
	    Parameter-Request Option 55, length 23: 
	      Subnet-Mask, Default-Gateway, Domain-Name-Server, LOG
	      Hostname, Domain-Name, RP, MTU
	      Vendor-Option, Vendor-Class, TFTP, BF
	      Option 119, Option 128, Option 129, Option 130
	      Option 131, Option 132, Option 133, Option 134
	      Option 135, Option 175, Option 203
	    T175 Option 175, length 48: 2969895296,2249199339,50397184,385941794,16847617,17891585,654377241,16846849,35717377,352387352,16852481,17957121
	    Client-ID Option 61, length 7: ether 52:54:00:94:9e:f2
	    GUID Option 97, length 17: 0.178.35.76.56.225.195.173.69.183.151.210.221.34.14.27.157
	    END Option 255, length 0

My last post was a one liner I use to look at the packets with hexdump. This is the first couple lines of output from the packets I captured.

00000000  212 195 178 161 002 000 004 000  000 000 000 000 000 000 000 000  |................|
00000016  000 000 004 000 001 000 000 000  246 092 093 095 025 086 011 000  |.........\]_.V..|

Another way to look at the packets is using emacs and hexl-mode. Both have their uses. Most valuable is the ability to run the file through tcpdump with various flags to see what it gives.

For example, I can run:

#  tcpdump -r /tmp/packets.bin -X
reading from file /tmp/packets.bin, link-type EN10MB (Ethernet)
19:42:46.742937 IP 0.0.0.0.bootpc > 255.255.255.255.bootps: BOOTP/DHCP, Request from 52:54:00:94:9e:f2 (oui Unknown), length 400
	0x0000:  4500 01ac 0116 0000 4011 782c 0000 0000  E.......@.x,....

This shows the first two bytes of the UDP packet data as the 4500 (hex). The same is true of the second packet:

19:42:50.798338 IP 0.0.0.0.bootpc > 255.255.255.255.bootps: BOOTP/DHCP, Request from 52:54:00:94:9e:f2 (oui Unknown), length 400
	0x0000:  4500 01ac 029a 0000 4011 76a8 0000 0000  E.......@.v.....

Hex 45 is Decimal 69. Looking with Hexdump:

[root@nuzleaf ~]# hexdump  /tmp/packets.bin  -e'"%07.8_ad  " 8/1 "%03d " "  " 8/1 "%03d " "  |"' -e'16/1  "%_p"  "|\n"' -v | grep 69
00000048  000 148 158 242 008 000 069 000  001 172 001 022 000 000 064 017  |......E.......@.|

We see 069 000. That matches the 4500.

Next in the decimal sequence is 001 172. In the hex we have 01ac which matches. This offset is 54. We know the whole file is 940 bytes long. That means each packet is probably 470 bytes. 470 minus 54 is still 416 is still longer than the 300 that we know is the length of the DHCP packet.

tcpdump shows us that the length is 400, which is, I think the length of the IP packet.

Possibly the best pattern to match is the MAC address, which we know to be

82, 84, 0, 230, 8, 49 (converted from hex 0x52,0x54,0x00,0xE6,0x08,0x031)

The MAC address is supposed to be put in the packet at an offset of 32 bytes in. The length is 6. We see this pattern on the line that starts with offset 00000096 and continues onto the next line:

00000096  000 000 000 000 000 000 000 000  000 000 000 000 000 000 082 084  |..............RT|
00000112  000 148 158 242 000 000 000 000  000 000 000 000 000 000 000 000  |................|

Working backwards, now, we should be able to pick out the patter for the start of the packet: 01 for the Opcode, 01 for the Hardware type, and 06 for the HW length. We see that here:

00000080  077 216 001 001 006 000 255 119  223 065 000 004 000 000 000 000  |M......w.A......|

So our DHCP payload starts at offset 82. It should continue for 300 bytes.

00000368  050 048 048 049 077 004 105 080  088 069 055 023 001 003 006 007  |2001M.iPXE7.....|
00000384  012 015 017 026 043 060 066 067  119 128 129 130 131 132 133 134  |....+<BCw.......|

That actually lines up with the error reported in the tcpdump:

instance#1: ERROR: invalid option Parameter-Request Option 55, length 23:

The 55 is followed by the length 23, the two values 001 and 003…and that is the end of the packet. 006 007 012 and so on might be part of the packet, but the BOOTP protocol hardcodes the length at 300 and so they get ignored. If we could find the length of the data section of the UPD header, we might know better.

Note that the bad checksum is reported as well. That value should be in the 2 bytes right before the start of the actual UDP data. And the two bytes prior to that is the UDP data length. Looking at the dump above we see that the line off set 00000080 starts with 077 216 before going into the 001 001 006 of the Boot packet. This is the checksum. The line before that ends with the two values 001 152. In Hex that is 0198 (I looked in hexl-mode) which in decimal is 408.

Our DHCP packet, which is supposed to be 300 Bytes long, is 408 bytes long. Is this legal?

Apparently, yes: RFC1531 extended the BOOTP definition. https://tools.ietf.org/html/rfc1531#section-2 DHCP extended the BOOTP packet size by renaming the vendor-specific area to options and giving it a size of 312.

 The options field is now variable length, with the minimum extended
   to 312 octets.

This seems to mean that tcpdump is working with the older format, which is no longer valid. I also need to extend the size of my packet in rust.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.