TL;DR
| |
| |
The format
Every route in option 121 is three fields glued together, no separators:
| Field | Size | Example 192.0.2.0/24 via 192.168.1.254 |
|---|---|---|
| Prefix length | 1 octet | 18 (24) |
| Destination, significant octets only: ⌈prefix / 8⌉ | 0 to 4 octets | c0 00 02 (192.0.2) |
| Router | 4 octets | c0 a8 01 fe |
A /24 carries three destination octets, a /16 two, a /32 four, and the default route /0 none: 00 followed directly by the router. Several routes are concatenated; the whole option fits in 255 octets (about 28 /24 routes).
The rule that breaks the network
RFC 3442, section 3: “If the DHCP server returns both a Classless Static Routes option and a Router option, the DHCP client MUST ignore the Router option.” If you start sending 121 with a single route to the other site, clients that understand it lose the gateway from option 3 and are left with no way out to the Internet. The default route goes inside 121, always, as in the example. Clients that do not understand 121 keep using option 3, so leave that configured too.
Where it goes
ISC dhcpd (dhcpd.conf): the option has to be declared, and it is worth sending it as 249 as well, which is what older Windows ask for:
| |
Kea (2.4 or later) understands the option by name with a readable notation, no hex:
| |
pfSense / OPNsense: with the ISC backend, Services → DHCP Server → Additional BOOTP/DHCP Options, number 121, type String, value in colon-separated hex (18:c0:00:02:…); add another with 249 for Windows. With the Kea backend, option 121 is not in the UI yet (as of this post there are open requests in both projects); if your version allows custom Kea options, use the JSON above; if not, that is a reason to stay on ISC.
MikroTik RouterOS:
| |
Which clients understand it
| Client | Option 121 |
|---|---|
| Windows (Vista and later) | Yes; also accepts 249 |
| Linux with NetworkManager or systemd-networkd | Yes, nothing to configure |
Linux with dhclient | Only if rfc3442-classless-static-routes is in the request list of dhclient.conf and a hook installs the routes (Debian and Ubuntu ship one in /etc/dhcp/dhclient-exit-hooks.d/) |
| macOS and iOS | Yes |
| Android | Historically ignores it; do not count on it |
FreeBSD dhclient | Not without a custom dhclient-script; dhcpcd does |
Check on the client with ip route (Linux), route print (Windows) or netstat -rn (macOS) after renewing the lease.
Traps
- A
/32with a router in another subnet: valid in the encoding, but the client needs a prior route to that router; order routes from most to least specific and put the local segment’s first if needed. - Routes to networks the DHCP server does not control (a DHCP server on one VLAN advertising routes through another) are accepted anyway: 121 is a list of bytes, nobody validates it. A one-octet mistake becomes a route to a made-up network.
- Clients do not revisit routes until they renew the lease; after changing the option,
T1(half the lease) is how long they take to see it, or force a renewal. - Windows and 249: if you only send 121, modern Windows uses it; 249 is for compatibility and carries exactly the same bytes.
/0advertisements from a rogue DHCP: any DHCP server on the LAN can rewrite every client’s default route with 121; DHCP snooping on the switch is the only real defense.
Reference: RFC 3442.
See also
- GCP Cloud VPN will not come up — the VPN these routes usually point at.
- pfSense: NAT reflection and split DNS — the other LAN setting clients receive without noticing.