Skip to content

fix(link)!: L2TPv2 drops the reserved flag bits on rebuild, and make() drops a lone ns or nr - #1226

Merged
JarryShaw merged 1 commit into
mainfrom
fix/l2tpv2-roundtrip-1207-1208
Oct 7, 2026
Merged

JarryShaw merged 1 commit into
mainfrom
fix/l2tpv2-roundtrip-1207-1208

Conversation

@JarryShaw

Copy link
Copy Markdown
Owner
  • Searched for similar pull requests
  • Followed the coding style (make pylint, make mypy, make isort) — pylint/mypy/isort run with the Makefile flags on the three changed source files; no new messages
  • make test passes, and a test case covers the change — the modules listed below only
  • Added a changelog entry — N/A, added centrally

What is the purpose of your pull request?

  • fix — corrects a defect

Description

Closes #1207
Closes #1208

#1207: the flags BitField gains reserved_1 (bits 2-3), reserved_2 (bit 5) and reserved_3 (bits 8-11); Flags.reserved holds them in on-wire positions (mask 0x34F0), and make(reserved=...) writes them back. 200200010002ff03002145000000 was rebuilt as 000200…; it now rebuilds byte-exactly, as do masks 0x1000, 0x0400, 0x00f0 and 0x34f0, also inside UDP/1701.

#1208: RFC 2661 §3.1 has S mark Ns and Nr together, so a lone value cannot be represented. I chose ProtocolError over defaulting the other to 0: a default would write an Nr/Ns the caller never asked for, and the library's other builders refuse to invent wire values. L2TPv2(ns=7) used to return 000200000000; it now raises ProtocolError: L2TPv2: ns and nr must be given together.

Breaking: data.link.l2tp.Flags has a new required field reserved, and the schema flags dict three new keys. make() now raises where it used to drop a lone ns/nr, and also raises for reserved bits outside 0x34F0. tests/protocols/link/test_link_unit.py hand-built fixtures gained the new keys (they did not pin wrong behaviour, but they omitted keys the real schema always produces).

Tests: new tests/protocols/link/test_l2tpv2_roundtrip_unit.py: 6 passed / 12 subtests with the fix; reverting the fix gives 12 failed, 3 passed (the 3 being a positive control and the outer tests whose subtests failed). Also passed: test_link_unit (24), test_l2tpv2_length_roundtrip_unit (4), test_l2tpv2_type_unit (8), test_l2tp_version_unit (7), test_make_data_keys_unit (2), test_construction_keyword_check_unit (21), test_option_roundtrip_unit (6, no L2TP entry in EXPECTED_FAILURES); run_unittest_leg.py protocols --exclude protocols/internet: 856 tests, 0 failures; tests/project: 389 passed, 1 skipped.

…) drops a lone ns or nr

- Declare the reserved bits 2-3, 5 and 8-11 as BitField subfields, carry
  them on Flags.reserved (on-wire positions, mask 0x34F0) and pass them
  back through _make_data/make(), so a rebuild keeps them (#1207).
- make() raises ProtocolError when only one of ns/nr is given, since
  RFC 2661 section 3.1 has S mark both fields together (#1208); it also
  refuses a reserved value outside 0x34F0.

Closes #1207
Closes #1208
@JarryShaw JarryShaw added fix Pull requests that fix a defect (fix: subject prefix) breaking Breaks public-facing behaviour or API (apply alongside the type label) review: running A cross-review is in flight against the current head - no verdict yet labels Oct 7, 2026
@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Coverage: 88.91% (unit tier, Python 3.14, 1c11bb347, Unit Tests run success)

Package Statements Missed Branches Partial Cover
pcapkit (top level) 104 4 20 4 93.55%
pcapkit/const 18757 1034 2342 845 90.24%
pcapkit/corekit 1878 91 580 22 94.34%
pcapkit/dumpkit 142 0 42 0 100.00%
pcapkit/foundation 2442 102 852 39 94.38%
pcapkit/interface 112 7 40 5 92.11%
pcapkit/protocols 15781 190 4040 165 98.18%
pcapkit/toolkit 487 65 144 1 85.42%
pcapkit/utilities 429 4 122 4 98.55%
pcapkit/vendor 4409 2359 1006 158 42.84%

Per-file detail: the coverage-html artifact of this run.

@JarryShaw

Copy link
Copy Markdown
Owner Author

Cross-review verdict on 1c11bb347: GOOD TO GO (Sonnet reviewed; the author ran on Opus).

  • RFC 2661 §3.1 layout: checked against the RFC text. The reserved bits are 2–3, 5 and 8–11, MSB-first, so the mask is 0x34F0. T, L, S, O and P sit at bits 0, 1, 4, 6 and 7.
  • Reserved bits survive a rebuild: 200200010002ff03002145000000, and masks 0x1000, 0x0400, 0x00f0 and 0x34f0, all rebuild byte-exactly, both bare and inside UDP port 1701. I re-ran the first case myself.
  • ns/nr handling: §3.1 says Ns and Nr are present together when S is set. L2TPv2(ns=7) now raises ProtocolError, which I re-ran, and ns=7, nr=0 round-trips. No other caller passes only one of the two.
  • Existing behaviour: no fixture contains L2TP. In 7 synthetic UDP/1701 frames, the parsed info is identical to main apart from the new reserved key.
  • Tests: the edited test_link_unit.py hunks only add keys. The new tests fail on main (12 of 15). run_unittest_leg.py protocols --exclude protocols/internet: 856 tests, 0 failures.
  • Nit: the module docstring table in l2tpv2.py still lists the reserved bits as unnamed.

@JarryShaw JarryShaw added review: good-to-go Cross-review at the current head says ready; CI state is separate and removed review: running A cross-review is in flight against the current head - no verdict yet labels Oct 7, 2026
@JarryShaw
JarryShaw merged commit a176c71 into main Oct 7, 2026
42 checks passed
@JarryShaw
JarryShaw deleted the fix/l2tpv2-roundtrip-1207-1208 branch October 7, 2026 19:05
@JarryShaw JarryShaw removed the review: good-to-go Cross-review at the current head says ready; CI state is separate label Oct 7, 2026
@JarryShaw JarryShaw added this to the PyPCAPKit v1.5 milestone Oct 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking Breaks public-facing behaviour or API (apply alongside the type label) fix Pull requests that fix a defect (fix: subject prefix)

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

fix(link): L2TPv2 make() silently drops ns when nr is not given, and the reverse fix(link): L2TPv2 drops the reserved flag bits on rebuild

1 participant