protocols: size HIP NAT_TRAVERSAL_MODE and ESP_TRANSFORM list entries at 16 bits, not 8 - #475
Conversation
… at 16 bits, not 8 NATTraversalModeParameter.modes and ESPTransformParameter.suites declared their list item_type as EnumField(length=1), while RFC 5770 5.4 and RFC 7402 5.1.2 both specify a 16-bit Mode ID / Suite ID per entry, and the makers already compute len=2 + 2*count on that assumption. The mismatch is the same class of defect #463/#466 fixed for TRANSPORT_FORMAT_LIST, at two sites that fix did not cover. Measured before this fix: modes=[1] packed to 0260000400000100000000 (11 octets, declared len=4) and read back as [1, 0], a phantom trailing entry; suites=[1] showed the same shape (0fff000400000100000000 -> [1, 0]). Full HIP() round trips made it worse still: HIP.make() on two aligned copies raised ProtocolError: HIPv2: invalid format, and a hand-built two-parameter packet corrupted the second copy with SchemaWarning: packet length < 0. Fix: item_type=EnumField(length=2, ...) at both sites, matching HIPTransportModeParameter.mode, which already used the correct width. Added maker round-trip tests for both parameters (0-3 entries) plus a full-HIP()-parser test with two aligned copies each; reverting either fix line fails the corresponding new tests. Confirmed the fix does not change hip-parameter/NAT_TRAVERSAL_MODE or hip-parameter/ESP_TRANSFORM's EXPECTED_FAILURES classification in test_option_roundtrip_unit.py (both stay in the tuple/list RECONSTRUCT gap group, an unrelated pre-existing defect). Build/test: full suite green -- 1023 passed, 17 skipped, 0 failed.
Review of PR #475 at
|
| Line | Class / field | Parameter | RFC · section | Field | Width per RFC | Verdict |
|---|---|---|---|---|---|---|
| 501 | DHGroupListParameter.groups (list item) |
DH_GROUP_LIST | RFC 7401 §5.2.6 | "DH GROUP ID" | 8 bits ("Each DH Group ID is one octet long") | correct as-is |
| 515 | DiffieHellmanParameter.group |
DIFFIE_HELLMAN | RFC 7401 §5.2.7 | "Group ID" | 8 bits (packed with 16-bit Public Value Length in the same 32-bit row) | correct as-is |
| 750 | HITSuiteListParameter.suites (list item) |
HIT_SUITE_LIST | RFC 7401 §5.2.10 | "ID" | 8 bits, explicit: "The ID field in the HIT_SUITE_LIST is defined as an eight-bit field" | correct as-is |
| 764 | CertParameter.cert_group |
CERT | RFC 8002 §... (Figure) | "CERT group" | 8 bits (4 fields packed into one 32-bit row: group/count/ID/type) | correct as-is |
| 770 | CertParameter.cert_type |
CERT | RFC 8002 | "CERT type" | 8 bits (same row) | correct as-is |
| 822 | RegInfoParameter.reg_info (list item) |
REG_INFO | RFC 8003 §4.2 | "Reg Type" | 8 bits (4 per 32-bit row) | correct as-is |
| 841 | RegRequestParameter.reg_request (list item) |
REG_REQUEST | RFC 8003 §4.3 | "Reg Type" | 8 bits | correct as-is |
| 859 | RegResponseParameter.reg_response (list item) |
REG_RESPONSE | RFC 8003 §4.4 | "Reg Type" | 8 bits | correct as-is |
| 877 | RegFailedParameter.reg_failed (list item) |
REG_FAILED | RFC 8003 §4.5 | "Reg Type" | 8 bits | correct as-is |
| 893 | RegFromParameter.protocol |
REG_FROM | RFC 5770 §5.3/Fig.8 | "Protocol" | 8 bits (Port(16)+Protocol(8)+Reserved(8)) | correct as-is |
| 984 | PayloadMICParameter.next |
PAYLOAD_MIC | RFC 6078 §4.3 | "Next Header" | 8 bits (Next Header(8)+Reserved(24)) | correct as-is |
| 1154 | RelayFromParameter.protocol |
RELAY_FROM | RFC 5770 §5.3/Fig.8 (shares format with REG_FROM) | "Protocol" | 8 bits | correct as-is |
| 1171 | RelayToParameter.protocol |
RELAY_TO | RFC 5770 §5.3/Fig.8 | "Protocol" | 8 bits | correct as-is |
| 1279 | HIP.next (fixed header) |
— | RFC 7401 §5.1 | "Next Header" | 8 bits, standard HIP fixed header | correct as-is |
No follow-up issue is owed. Every other EnumField(length=1, ...) site in this file matches its RFC exactly; the two this PR touched (NAT_TRAVERSAL_MODE.modes, ESP_TRANSFORM.suites) were the only ones that were 16-bit-per-entry and had been left at 1. Note also that the hand-written wire diagrams in pcapkit/protocols/internet/hip.py's _read_param_nat_traversal_mode/_read_param_esp_transform docstrings (untouched by this PR) already correctly depicted the 16-bit Mode ID/Suite ID layout — the schema's item_type was the only place the width was wrong, and it's now fixed.
CI
gh pr view 475 --json statusCheckRollup (waited it out to completion): 21 CheckRun SUCCESS + 2 SKIPPED (Docs test gate, Gate (full suite, Python 3.14) — skip-by-design on pull_request) + 1 StatusContext (pyup.io/safety-ci) SUCCESS. Fully green.
Environment note
pcapkit.__file__ was asserted to resolve inside the worktree (/local/home/jarryx/GitHub/PyPCAPKit/.claude/worktrees/agent-ad427fe0d81e7dd4c/pcapkit/__init__.py) before every measurement, per the PYTHONPATH-forced-import discipline. Fixtures were regenerated with examples/generators/make_samples.py before any suite run (unrelated pre-existing warnings for MPTCP/TCP-option/IPv4-SEC/HIP-ENCRYPTED samples, none touching the two parameters this PR changes).
GOOD TO MERGE at 07ef9db8b
|
Accepting this, with one of its claims independently re-run and one explicitly not re-derived. I reproduced the revert-proof myself, because a review-only mandate cannot do it — #468's reviewer hit exactly that wall on the same kind of claim, defeated by Restored: Your claim that nothing besides What I did not re-derive: the 14-site RFC width table. Your finding that every other Also noted and not re-reported: |
Summary
NATTraversalModeParameter.modesandESPTransformParameter.suites(pcapkit/protocols/schema/internet/hip.py:567-570and:939-942) declared their listitem_typeasEnumField(length=1)-- one octet per entry -- while RFC 5770 5.4 and RFC 7402 5.1.2 both specify a 16-bit Mode ID / Suite ID per entry. The makers (_make_param_nat_traversal_mode,_make_param_esp_transform) already computelen=2 + 2 * len(entries)on the 16-bit assumption, so the mismatch produced a phantom trailing entry on every round trip. This is the same defect class Four more HIP parameters underflow pkt['len'] - 2 and silently return an empty list #463/protocols: floor four more HIP list-length callbacks at zero #466 fixed forTRANSPORT_FORMAT_LIST, at two sites that fix did not cover.item_type=EnumField(length=2, ...)at both sites, matchingHIPTransportModeParameter.mode, which already used the correct width.HIP()-parser test with two 8-aligned copies each.Verification
Verified the RFC widths directly against the RFC text (both specify 16 bits) before changing anything, and reproduced the reported defect byte-for-byte on
main:I could not reproduce the issue's literal
AttributeError: 'NATTraversalModeParameter' object has no attribute 'modes'through several full-HIP()round-trip constructions -- instead I hitProtocolError: HIPv2: invalid format(viaHIP.make()on two aligned copies, since the pre-fix per-parameter length isn't 8-aligned) andSchemaWarning: packet length < 0with silent data loss (via a hand-built two-parameter packet). Both are consistent with "the full parser makes it worse," just not textually identical to the quoted error -- noting this plainly rather than claiming an exact repro.Confirmed the fix does not change
hip-parameter/NAT_TRAVERSAL_MODE/hip-parameter/ESP_TRANSFORM'sEXPECTED_FAILURESentry intests/protocols/test_option_roundtrip_unit.py-- both remain in the pre-existing tuple/listRECONSTRUCTgap group (an unrelated defect:_read_param_*returns a tuple where_make_param_*needs a list), which is orthogonal to this item-width fix.Reverting either fix line fails the corresponding new regression test (checked directly).
Test plan
pytest tests/protocols/internet/test_hip_unit.py-- 21 passed, 68 subtests passedpytest tests/protocols/test_option_roundtrip_unit.py-- 6 passed, 358 subtests passed (no change to HIP's recorded gaps)pytest tests/-- 1023 passed, 17 skipped, 0 failedCloses #472