Skip to content

fix(esp): short-circuit read() when parsed as an IPv6 extension header (#895) - #928

Closed
JarryShaw wants to merge 1 commit into
mainfrom
fix/895-esp-extension-short-circuit
Closed

JarryShaw wants to merge 1 commit into
mainfrom
fix/895-esp-extension-short-circuit

Conversation

@JarryShaw

Copy link
Copy Markdown
Owner

Closes #895.

The maintainer's ruling: "i say the short-circuit must be done in ESP cause it's part of the IPv6 extension headers."

#924 discharged this issue's substantive half — _extf became load-bearing, so payload, protochain and protocol consult it. This is the other half: the if extension: return guard its seven siblings all have before calling _decode_next_layer. When the header is one link of an IPv6 chain, IPv6._decode_next_layer walks the rest of the chain itself, so decoding it again from inside the header produces a result nobody reads.

ESP needed the guard in two places, not one. The decrypted path returns at read's tail; every undecrypted path — no security association, wrong key, malformed trailer — returns through _make_opaque instead. Guarding only read's tail would leave the common case unfixed, since a capture taken without keys is the expected case rather than the rare one. _make_opaque has no extension parameter, so it consults self._extf, which __post_init__ sets before handing off to read.

Measured, patching Internet — which is what actually defines _decode_next_layer; patching ProtocolBase intercepts nothing and silently reads zero:

extension=True  -> _decode_next_layer called 0x
extension=False -> _decode_next_layer called 1x

Reverting both guards fails the new tests with 1 != 0 and with the family sweep naming esp.py:1194.

One note on the tests themselves: the family sweep pairs each return self._decode_next_layer( with the two lines above it. A first draft merely looked for if extension: anywhere in the file and passed on unfixed ESP, because ESP.protocol already consults self._extf for an unrelated reason — true and useless. The committed version distinguishes a guarded return from an unguarded one.

Verified: 5 new tests pass; test_esp_unit, test_ah_unit, test_ipv6_ext_unit and test_isort_clean green at 65 tests.

#895)

- `ESP.read`'s decrypted tail now returns the parsed header instead of
  calling `_decode_next_layer` when `extension=True`, matching the seven
  other IPv6 extension headers. `IPv6._decode_next_layer` walks the rest
  of the chain itself, so decoding it again here is discarded work.
- `ESP._make_opaque` gets the same guard, consulting `self._extf` since it
  takes no `extension` parameter. It is the undecrypted return, reached
  from several places in `read`, so guarding only `read`'s tail would
  leave the common case — a capture taken without keys — unfixed.
- New `test_esp_extension_short_circuit_895_unit.py` counts calls into
  `Internet._decode_next_layer` rather than asserting on output, since the
  change is work not done. Patching is on `Internet`, which defines the
  method; patching `ProtocolBase` intercepts nothing and reads zero.

Verified: 5 new tests pass, and reverting either guard fails them —
`1 != 0` on the call count, plus the family sweep naming `esp.py:1194`.
`test_esp_unit`, `test_ah_unit`, `test_ipv6_ext_unit` and isort all green.
@JarryShaw JarryShaw added fix Pull requests that fix a defect (fix: subject prefix) perf Pull requests that improve performance (perf: subject prefix) test Pull requests that add or correct tests (test: subject prefix) review: pending No verdict for the current head - never reviewed, or the head moved since the last one labels Sep 29, 2026
@JarryShaw JarryShaw added review: needs-changes Cross-review at the current head says changes are required; see the verdict comment and removed review: pending No verdict for the current head - never reviewed, or the head moved since the last one labels Sep 29, 2026
@JarryShaw

Copy link
Copy Markdown
Owner Author

Closed unmerged per the ruling on #895 — "okay, that makes sense now." ESP is an IPv6 extension header per IANA, but RFC 4303 puts its Next Header inside the encrypted trailer, so the chain walk cannot continue past it and the short-circuit loses every parsed inner layer rather than deferring the work. Measured: info.ipv4 absent and protochain raising UnsupportedCall on RFC 3602 case 7. The exclusion is already documented at pcapkit/protocols/internet/ipv6.py:90-100.

@JarryShaw JarryShaw closed this Sep 29, 2026
@JarryShaw
JarryShaw deleted the fix/895-esp-extension-short-circuit branch September 29, 2026 18:49
@JarryShaw JarryShaw removed the review: needs-changes Cross-review at the current head says changes are required; see the verdict comment label Sep 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fix Pull requests that fix a defect (fix: subject prefix) perf Pull requests that improve performance (perf: subject prefix) test Pull requests that add or correct tests (test: subject prefix)

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

fix(esp): the extension= keyword is accepted, stored and never read

1 participant