Skip to content

LOCATOR_SET is conformant only by accident: nested Locator.len shadows the parameter in padding, and len is in 4-octet units where RFC Length is bytes #679

Description

@JarryShaw

Two pre-existing defects in LOCATOR_SET cancel each other exactly, so the parameter is RFC-conformant on main by coincidence rather than by construction. Fixing either one alone makes it non-conformant. #664 fixes the padding half and the parameter is now four octets short at every locator count.

Measured on both trees, independently of the PR author

CPython 3.14.7, __editable__* stripped from sys.meta_path, pcapkit.__file__ asserted to each worktree before any other import, HIP._make_param_locator_set(Parameter.LOCATOR_SET, version=2, locator_set=[...]) then .pack():

locators contents schema.len main @ 0c7f2b7c9 #664 @ 819dbbd6a RFC 7401 for Length = 24n
1 24 4 32 OK 28 short by 4 32
2 48 8 56 OK 52 short by 4 56
5 120 20 128 OK 124 short by 4 128

RFC total from §5.2.1's Total Length = 11 + Length - (Length + 3) % 8.

The two defects, and why they cancel

1. LocatorSetParameter.padding reads the nested Locator.len, not the parameter's. ListField packs each Locator into the shared packet context, and the nested len shadows the parameter's own. An IPv6 locator always has Locator.len == 4, so the old padding expression always appended exactly 4 octets regardless of locator count.

2. The parameter's len is sum(Locator.len) — in 4-octet units — where the RFC's Length is a byte count. Visible in the table above: contents of 24, 48 and 120 octets give schema.len of 4, 8 and 20, i.e. 4n rather than 24n.

Old total: 4 + 24n + 4 = 24n + 8. And because 24n ≡ 0 (mod 8), the RFC total for Length = 24n is also 24n + 8. Identical for every n — which is why no test and no round trip has ever caught either defect.

Why this is filed rather than folded into #664

The repair is not a one-expression change like EncryptedParameter.data was. It changes a Length field on the wire, changes the public Data_LocatorSetParameter.length, and requires re-checking the reader's ListField(length=lambda pkt: pkt['len']), which consumes the same quantity. That is the same class of change as #672, not a coupled fix that #664 can absorb safely.

Consequence for #664 as it stands

#664 must not merge in its current state. It would take LOCATOR_SET from conformant to four octets short, which is a regression in on-wire output even though every individual change in it is correct. Two ways out, and the choice is the owner's:

Found by the #664 worker while fixing a stale test pin, disclosed rather than absorbed, and reproduced here independently before filing.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions