skip to content

How is the DHCPv4 options field encoded after the magic cookie, and how does a parser find option 53 inside it?

level: middleimportance: should knowfreq 30%

answer

  1. four octets before the first option
  2. code, length, value
  3. two options have no length
  4. 53 is the message type
  5. repeats are concatenated

basics

~20 s

The DHCPv4 options field opens with the magic cookie 99.130.83.99, then holds code-length-value options until option 255 marks the end. Pad (0) and end (255) are a single octet; option 53 carries the message type, 1 DHCPDISCOVER to 8 DHCPINFORM.

solid answer

~50 s

After the 236-octet fixed header, the DHCPv4 options field begins with the four-octet magic cookie `99.130.83.99` (`63 82 53 63`), which says the rest is to be read as options (RFC 2132 §2). Each option is one code octet, one length octet and that many value octets; the length counts only the value. Two codes break the pattern: pad (`0`) and end (`255`) are a single octet with no length. A parser reads the code, skips pads, stops at end, and otherwise jumps `2 + length` octets to the next option. Option 53, DHCP Message Type, is one octet whose value names the message: 1 DHCPDISCOVER, 2 DHCPOFFER, 3 DHCPREQUEST, 4 DHCPDECLINE, 5 DHCPACK, 6 DHCPNAK, 7 DHCPRELEASE, 8 DHCPINFORM. Options are not sorted, and RFC 2131 has the client concatenate multiple instances of the same option into one value.

code

pseudocode · 16 lines
pseudocode
pos = 236                                  // options follow the fixed header
if msg[pos .. pos+3] != [99, 130, 83, 99]: reject("no magic cookie")
pos = pos + 4
while pos < size(msg):
    code = msg[pos]
    if code == 0:                          // pad: one octet, no length
        pos = pos + 1
        continue
    if code == 255:                        // end: one octet, stop here
        break
    if pos + 1 >= size(msg): reject("truncated option")
    n = msg[pos + 1]                       // counts value octets only
    if pos + 2 + n > size(msg): reject("option overruns field")
    opts[code] = opts[code] ++ msg[pos+2 .. pos+1+n]   // repeats concatenate
    pos = pos + 2 + n
msgType = opts[53][0]                      // 1 DISCOVER .. 8 INFORM

go deeper

for a junior

Remember the magic cookie opens the options, each option is code, length, value, and option 53 says which DHCP message you are looking at.

for a middle

Walk a hex dump aloud: convert the codes from hex, use the length to skip, handle pad and end as single octets, and decode option 53's value to a message name.

for a senior

Name the parser traps: repeated codes that must be concatenated, options returned only when requested in option 55, unknown codes skipped by length, and options overloaded into sname or file.

for a principal

Explain why the format has lasted since BOOTP: a length on every new option keeps old parsers working, so the catalogue grows without a protocol version change.

## Where the options start A DHCPv4 message reuses the BOOTP layout: a fixed header of **236 octets** (`op`, `htype`, `hlen`, `hops`, `xid`, `secs`, `flags`, four address fields, the 16-octet `chaddr`, the 64-octet `sname` and the 128-octet `file`), followed by the variable **options** field. RFC 2131 §2 requires a client to accept an options field of at least **312 octets**, which keeps the whole message within 576 octets, the smallest datagram every IP host must accept. A client that can take more says so with option 57, Maximum DHCP Message Size, whose minimum legal value is 576. The options field begins with the **magic cookie**, the four octets `99.130.83.99` (hexadecimal `63 82 53 63`). It comes from BOOTP, where it told a receiver how to interpret the vendor area; in DHCP it marks that what follows is a sequence of options. ## The code-length-value rule RFC 2132 §2 defines one format for every option: - a **code** octet naming the option; - a **length** octet counting the **value octets only**, never the code or length themselves; - `length` octets of **value**, with multi-octet numbers in network byte order. Two codes are the exception, and both are exactly one octet: - **Pad, code 0**, fills space, for example to align later fields; - **End, code 255**, marks the end of valid options. RFC 2131 says the last option must always be end. Every option defined after RFC 2132 must carry a length octet, even if its length is fixed or zero, so a parser can skip codes it does not understand. That rule is what lets old clients survive new options. ## Walking a captured DHCPOFFER Here is the options field of an offer, octet by octet: | Octets (hex) | Code | Meaning | |---|---|---| | `63 82 53 63` | (cookie) | magic cookie 99.130.83.99 | | `35 01 02` | 53 | DHCP Message Type, value 2 = **DHCPOFFER** | | `36 04 C0 A8 0A 02` | 54 | Server Identifier 192.168.10.2 | | `01 04 FF FF FF 00` | 1 | Subnet Mask 255.255.255.0 | | `03 04 C0 A8 0A 01` | 3 | Router 192.168.10.1 | | `06 08 C0 A8 0A 02 C0 A8 0A 03` | 6 | DNS servers 192.168.10.2 and .3 | | `33 04 00 00 0E 10` | 51 | Lease Time `0x0E10` = 3600 s | | `FF` | 255 | End | Note that `0x35` is 53, `0x36` is 54 and `0x33` is 51: hex dumps catch out people who read the codes as decimal. The DNS option has length 8 because it carries two four-octet addresses. ## Option 53 and the message types The `op` field in the header only says BOOTREQUEST or BOOTREPLY, which cannot tell a DHCPDISCOVER from a DHCPREQUEST. **Option 53** does that. It has length 1 and RFC 2132 §9.6 lists these values: | Value | Message | |---|---| | 1 | DHCPDISCOVER | | 2 | DHCPOFFER | | 3 | DHCPREQUEST | | 4 | DHCPDECLINE | | 5 | DHCPACK | | 6 | DHCPNAK | | 7 | DHCPRELEASE | | 8 | DHCPINFORM | RFC 2131 §3 says option 53 must be included in every DHCP message, and messages are named after its value: a message whose option 53 is 1 is a DHCPDISCOVER. A parser therefore walks the options until it meets code 53; there is no fixed offset, because options are **not** sorted by code. The one ordering rule in RFC 2132 is that the subnet mask comes before the router option when both are present. ## Rules that trip up parsers 1. **Repeated codes.** RFC 2131 lets a value too long for one option (255 octets maximum) be split across several instances of the same code; the client **concatenates** them into a single value. A parser that keeps only the last instance silently truncates long route lists. 2. **Parameter request list.** Option 55 is a list of codes the client wants. A server MUST try to return them in the requested order but is not required to, and some options are only sent when asked: RFC 8925 forbids a server to send option 108, IPv6-Only Preferred, unless the client listed 108 in option 55. 3. **Unknown codes.** Because every option except 0 and 255 has a length, a client skips an unknown code by its length rather than failing. 4. **Overloading.** If option 52 is present, more options continue in the header's `file` and/or `sname` fields after the options field has been read. The code sketch below shows the walk with its bounds check.

  • Why must every option defined after RFC 2132 carry a length octet, even a fixed-size one?
    A DHCPv4 client meets codes it was never programmed for. With a length octet on every option except pad and end, it can skip an unknown option by jumping `2 + length` octets and keep parsing. Without one, a single new fixed-length option would make every older parser lose its place.
  • A route list does not fit in 255 octets; how does DHCPv4 carry it?
    The server repeats the same option code with consecutive pieces of the value, and RFC 2131 has the client concatenate all instances of that code into one parameter. RFC 3442 marks the classless static route option as one that requires this concatenation, so a client that keeps only one instance loses routes.

saying these in an interview costs you the question

  • The length octet counts the code and length octets as well as the value.
  • The op field already distinguishes DISCOVER from REQUEST, so option 53 is redundant.
  • Options must appear in ascending order of their codes.
  • Every option, pad and end included, has a length octet.
  • A client should keep only the last instance of a repeated option code.