Skip to content

dhcp: decode and encode option 41 (NIS servers) - #1488

Open
dylanpulver wants to merge 1 commit into
svinota:masterfrom
dylanpulver:dhcp-nis-servers-option-41
Open

dhcp: decode and encode option 41 (NIS servers)#1488
dylanpulver wants to merge 1 commit into
svinota:masterfrom
dylanpulver:dhcp-nis-servers-option-41

Conversation

@dylanpulver

@dylanpulver dylanpulver commented Sep 1, 2026

Copy link
Copy Markdown

dhcp4msg.options is an RFC 2132 ordered table of option code → wire format, and _register_options() turns it into the decode and encode maps. Codes missing from it fall through dhcpmsg.decode() to array8.

Option 41 is absent. dhcp4msg.py:78-80 runs 40 → 85 → 42: the slot between NIS_DOMAIN (40) and NTP_SERVERS (42) holds NDS_SERVERS, which is code 85.

RFC 2132 §8.2: "The code for this option is 41. Its minimum length is 4, and the length MUST be a multiple of 4." That is byte-for-byte the same shape as NTP_SERVERS in §8.3, which makes 42 an exact control. Feeding the same eight bytes (192.168.1.10, 192.168.1.11) under codes 41, 42 and 85 into dhcp4msg(buf=...).decode():

before                                                  after
nds_servers  ['192.168.1.10', '192.168.1.11']           ['192.168.1.10', '192.168.1.11']
nis_servers  [192, 168, 1, 10, 192, 168, 1, 11]   <--   ['192.168.1.10', '192.168.1.11']
ntp_servers  ['192.168.1.10', '192.168.1.11']           ['192.168.1.10', '192.168.1.11']

decode_map[41]   None                                   CodeMapping(name='nis_servers', format='ip4list')
encode_map       nis_servers=False                      nis_servers=True

The fix is additive: NDS_SERVERS is correct in its own right (RFC 2241 §3 gives code 85 the same format), so nothing is removed, and the second test row pins that.

grep -rn "nds_servers\|NDS_SERVERS" . returns two hits repo-wide: the enum definition and that one table line. grep -rn "nis_servers" tests/ returns none. The only NIS in the tests is test_parser.py:355-356, where Option.NIS_SERVERS appears inside an asserted parameter_list — that is option 55's array of requested codes, decoded by array8, which never touches the decode map. The tests prove the client can ask for NIS servers; nothing proves it can read the answer.

Mutants

  • A — revert the table line, keep both new test rows: nis_servers fails with KeyError: 'nis_servers' (it cannot be encoded).
  • B — treat it as a typo and replace NDS_SERVERS with NIS_SERVERS rather than adding: nds_servers fails with KeyError: 'nds_servers'. That is what pins the fix as additive rather than a substitution.

What I ran, and what I could not

The two rows go into the existing test_encode_decode_options table. I could not run the repo's suite. make test is Linux-only and tests/test_linux/ cannot even be collected on macOS — pyroute2/ext/rawsocket.py:3 fails with ImportError: cannot import name 'AF_PACKET' from 'socket'. So I drove the parametrized table directly instead: a small runner reads the @pytest.mark.parametrize tuple out of test_encode.py with ast.literal_eval and executes the same test body. All 13 cases pass with the change; the mutants above are the same runner on the same table. Every claim about the repo's own nox sessions is therefore untested by me — CI is the first real run.

pre-commit run --files pyroute2/dhcp/dhcp4msg.py tests/test_linux/test_dhcp/test_encode.py passes (isort, black, flake8, whitespace, end-of-file).

Also not done: no live DHCP server exchange, and I did not touch Lease, which has no nis_servers accessor. Related but deliberately left alone: leases.py:173 annotates domain_search as list[str], but option 119 is likewise absent from this table so it decodes via array8 to a list of ints — that one needs an RFC 3397 name-compression decoder, not a table entry, so it is not in this PR.

Not from a bug report; found checking protocol constant tables against their RFCs. AI assistance: this change was written with an AI coding assistant.

dhcp4msg.options is an RFC 2132 ordered table of option code to wire
format, and _register_options() turns it into the decode and encode
maps. Option 41 is absent from it: the slot between NIS_DOMAIN (40) and
NTP_SERVERS (42) holds NDS_SERVERS, which is code 85.

RFC 2132 8.2: "This option specifies a list of IP addresses indicating
NIS servers available to the client. [...] The code for this option is
41. Its minimum length is 4, and the length MUST be a multiple of 4."
That is the same wire format as NTP_SERVERS in 8.3, so the two are a
controlled comparison. Feeding the same eight bytes under codes 41, 42
and 85 into dhcp4msg().decode():

  nds_servers  ['192.168.1.10', '192.168.1.11']
  nis_servers  [192, 168, 1, 10, 192, 168, 1, 11]   <- falls back to array8
  ntp_servers  ['192.168.1.10', '192.168.1.11']

and 'nis_servers' is not in the encode map at all, so it cannot be sent.

The fix is additive. NDS_SERVERS is correct in its own right (RFC 2241
section 3 gives code 85 the same list-of-addresses format), so it stays.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant