Skip to content

fix(toolkit): pyshark traces TCP behind an IPv4 extension header as the default engine does (#1625) - #1628

Closed
JarryShaw wants to merge 1 commit into
mainfrom
fix/1625-pyshark-ipv4-extension-headers
Closed

JarryShaw wants to merge 1 commit into
mainfrom
fix/1625-pyshark-ipv4-extension-headers

Conversation

@JarryShaw

@JarryShaw JarryShaw commented Oct 11, 2026 •

Copy link
Copy Markdown
Owner

Please follow the guide below

What is the purpose of your pull request?

Tick the commit type your subject line carries.

  • fix — corrects a defect
  • feat — adds a feature
  • perf — changes performance, not behaviour
  • refactor — changes neither behaviour nor performance
  • test — tests only
  • docs — documentation only
  • ci — workflows or build tooling
  • release — bumps the version or rolls up a distribution
  • chore — anything else

Description of your pull request and other information

Closes #1625.

@JarryShaw JarryShaw moved this to In review in PyPCAPKit Oct 11, 2026
@JarryShaw JarryShaw added fix Pull requests that fix a defect (fix: subject prefix) review: running A cross-review is in flight against the current head - no verdict yet labels Oct 11, 2026
@github-actions

github-actions Bot commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Coverage: 89.73% (unit tier, Python 3.14, e9d316ad8, Unit Tests run success)

Package Statements Missed Branches Partial Cover
pcapkit (top level) 104 3 20 3 95.16%
pcapkit/const 18858 954 2366 868 91.06%
pcapkit/corekit 2366 67 760 35 96.48%
pcapkit/dumpkit 275 3 100 3 98.40%
pcapkit/foundation 2961 153 1042 35 94.05%
pcapkit/interface 138 7 52 5 93.68%
pcapkit/protocols 17115 226 4570 191 98.06%
pcapkit/toolkit 992 134 364 6 85.40%
pcapkit/utilities 494 4 146 4 98.75%
pcapkit/vendor 4409 2342 1006 157 43.25%

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

@JarryShaw

Copy link
Copy Markdown
Owner Author

Verdict: NEEDS CHANGES at 7d346ac4e.

Sonnet cross-review. The fix itself holds, and the review checked it against its own frames: 58 hand-built frames and 564 truncation cut points match the default engine field by field, and frame alignment holds on every pcapng shape tried. But the second tshark pass adds failure modes that main does not have:

  1. Crash. A pcapng Simple Packet Block, which has no timestamp, that matches OCTETS_FILTER makes the JSON pass raise KeyError: 'frame.time_epoch' (pyshark tshark_json.py:111), which aborts extract(). I reproduced it: on main the same capture extracts and agrees with the default engine (Hop-by-Hop case); on the PR it raises.
  2. Lifecycle. On early close with the second pass started, the first pass is left as an unreaped zombie (3 of 3 runs; main leaves none). A second pass that tshark rejects aborts the extraction, and one that dies mid-run silently drops the trace of every later matching frame.

Required: a second pass that fails or cannot be parsed must not crash the extraction or silently drop frames. Fall back to tshark's dissection with a warning, or raise from pcapkit.utilities.exceptions. Reap both processes on close. Add tests: an SPB frame (fails on this head), and a real-tshark lifecycle test, since test_close_closes_both_passes uses fakes. Nit: #1624 is not merged yet, so the body should say the approach matches it, not that it already applies to dpkt and scapy.

@JarryShaw JarryShaw added review: needs-changes Cross-review at the current head says changes are required; see the verdict comment and removed review: running A cross-review is in flight against the current head - no verdict yet labels Oct 11, 2026
…he default engine does (#1625)

- tshark dissects TCP behind an AH or IPv6 extension header in IPv4 by its
  own rules, and leaves the payload of a Mobility or HIP header out of the
  PDML, so the pyshark engine traced TCP the default engine rejects there
  and missed the TCP behind protocols 135 and 139.
- tcp_traceflow now reads such a frame's TCP from its octets, by the rules
  the other engines share (pypcapfile's _upper_layer and _default_accepts),
  up to EXTENSION_HEADER_LIMIT headers and through IP-in-IP tunnels.
- The engine reads those octets from a second tshark process of its own
  (-T ek -x -J frame, filtered to such frames), started on first use, read
  forward by frame number, and reaped on close. If it fails, the engine
  warns with PySharkWarning and traces such frames as tshark dissects them.
- Drop ipv4-33/pyshark/trace from the #1609 Gap row, which this fixes.

Tests: new tests/toolkit/test_pyshark_ipv4_extension_headers_1625_unit.py.
@JarryShaw
JarryShaw force-pushed the fix/1625-pyshark-ipv4-extension-headers branch from 7d346ac to e9d316a Compare October 11, 2026 00:54
@JarryShaw JarryShaw added review: pending No verdict for the current head - never reviewed, or the head moved since the last one review: running A cross-review is in flight against the current head - no verdict yet and removed review: needs-changes Cross-review at the current head says changes are required; see the verdict comment review: pending No verdict for the current head - never reviewed, or the head moved since the last one labels Oct 11, 2026
@JarryShaw

Copy link
Copy Markdown
Owner Author

Verdict: GOOD TO GO at e9d316ad8.

Sonnet cross-review, round 2. Both round-1 findings are fixed. Simple Packet Blocks trace as the default engine does, and early close leaves no tshark processes and no zombie.

  • Re-run sets: the 58 frames, the 564 truncation points, every frame-alignment and pcapng shape, and 200 frames up to 65 KB show no new disagreement. The 4 remaining diffs, GRE and VXLAN carrying IPv4, also disagree on main.
  • Fallback: killing the second pass mid-run gives exactly one PySharkWarning. Frames before that point match the default engine and frames after it match main, with 0 diffs over 2000 frames. A rejected filter, or a tshark without -J, also warns and finishes.
  • Zombie cause: confirmed with raw pyshark. Each Capture replaces the asyncio child watcher, so two live captures leave a zombie and one capture plus a plain subprocess does not.

I reproduced the round-1 SPB probe agreeing on all 4 captures, the new tests passing (17) and failing against main's pyshark files (79), and no tshark left afterwards. Non-blocking: an engine dropped without close() leaves its subprocess to GC, as the first pass already does on main. tshark versions other than 4.6.9 are unverified, though an unexpected output shape takes the warned fallback. This conflicts with #1624 in tests/foundation/engines/test_ext_chain_limit_agreement_1604_unit.py, so whichever merges second needs a rebase.

@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 11, 2026
@JarryShaw

Copy link
Copy Markdown
Owner Author

Resolve conflicts

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)

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

fix(toolkit): pyshark traces TCP behind an extension header in IPv4 that the default engine rejects, and misses it behind Mobility and HIP

1 participant