Skip to content

Fix TCP reassembly coordinates (#349); add an end-to-end test tier - #372

Closed
JarryShaw wants to merge 3 commits into
fix/pcapng-parser-defectsfrom
fix/reassembly-and-e2e-tests
Closed

JarryShaw wants to merge 3 commits into
fix/pcapng-parser-defectsfrom
fix/reassembly-and-e2e-tests

Conversation

@JarryShaw

Copy link
Copy Markdown
Owner

Draft. Stacked on #371 — GitHub retargets this to main when that merges. Stacked because the end-to-end tests assert on PCAP-NG behaviour #371 corrects.

TCP reassembly (#349)

Hole descriptors carried absolute sequence numbers from the toolkits, the first hole was seeded from a payload length, and submit() sliced a buffer indexed from its own initial sequence number using those bounds. On any capture with a SYN and a realistic ISN every slice landed past the end of the buffer, so an incomplete datagram was dropped silently instead of coming back with completed=False — the completed=False / packet is None branches of the public API were unreachable in practice.

Measured, 5 frames with two segments permanently lost:

case before after
SYN, ISN 0xC0DE1234 0 datagrams 1, completed=False, fragments [10, 10, 10]
no SYN, same ISN 1, fragments [10] fragments [10, 10, 10]
SYN, ISN 0 fragments [11, 11, 10] [10, 10, 10]

Descriptors are now absolute and inclusive throughout, with submit() the only place they become buffer offsets; holes outside a buffer are dropped rather than indexing from its far end. Absolute was chosen over relative because hdl lives on Buffer, shared across Fragments, while a relative origin would have to live on Fragment — whose isn mutates when a segment arrives below the data already buffered.

A fourth symptom the issue did not name is also fixed: a complete stream whose SYN shares an acknowledgement number came back one octet too long, the SYN's own sequence number showing up as a leading NUL.

Four of the five toolkits needed the inclusive-bound change (pyshark has no reassembly at all), and four existing tests were pinning the broken behaviour.

End-to-end tier

tests/integration/ was three modules; the real end-to-end spectrums existed only as the assertion-free demonstrations in examples/legacy_smoke/. Seven modules added — 60 tests, 73 subtests, 23s — covering report formats written to disk and read back (tree/json/plist, files=True), manual frame iteration, TCP and IP reassembly, flow tracing, engine parity across default/dpkt/scapy, PCAP-NG including both byte orders, and the CLI as a subprocess.

Assertions are on content rather than liveness: reassembled bodies match their own Content-Length, and the 331 traced flows of http.pcap partition all 1117 frames exactly once.

Four tests are skipped rather than pinning defects — one each for the plist <date> format, layer=/protocol= being ignored, the IPv6 fragment offset, and unescaped JSON mapping keys. Each names the file and line, so they can be un-skipped by whoever fixes it. That choice was deliberate: test_ipv6_extension_runtime.py had pinned a bug, which is why fixing #348 required editing a test.

Verification

Full suite on this branch in a clean worktree, fixtures regenerated from scratch: 429 passed, 4 skipped, 183 subtests passed.

Closes #349.

Hole descriptors carried absolute sequence numbers from the toolkits, the
first hole was seeded from a payload length, and submit() sliced a buffer
indexed from its own initial sequence number with those bounds. On any capture
with a SYN and a realistic ISN every slice landed past the end of the buffer,
so an incomplete datagram was dropped silently instead of being returned with
completed=False.

Descriptors are now absolute and inclusive throughout, submit() is the only
place they are converted into buffer offsets, and holes outside a buffer are
dropped rather than indexing from its far end. A SYN no longer contributes its
own sequence number to the payload, which had been prepending a NUL octet to
complete datagrams.

Closes #349.
The integration tier was three modules; the real end-to-end spectrums lived
only as the assertion-free demonstrations in examples/legacy_smoke. Added
seven modules covering report formats on disk, manual frame iteration, TCP and
IP reassembly, flow tracing, engine parity, PCAP-NG including both byte
orders, and the CLI as a subprocess.

Assertions are on content rather than liveness: reassembled bodies match
their own Content-Length, and the 331 traced flows partition all 1117 frames
of http.pcap exactly once. Four tests are skipped against defects they would
otherwise pin, each naming the file and line to fix.
test_sample_capture_reassembles_every_stream_byte_exactly reads test.pcap,
which examples/generators/make_samples.py builds rather than the repository
carrying it, so the unit-test workflow -- which deliberately runs without
generated fixtures -- failed on every Python version with FileNotFoundError.

Moved to tests/foundation/reassembly/test_tcp_runtime.py, matching the
convention the ignore globs already encode: fixture-dependent cases live in
*_runtime.py and *_regression.py, and the integration job generates the
fixtures before running them.

CI selection: 318 passed, 103 subtests passed, with no fixtures present.
@JarryShaw
JarryShaw deleted the branch fix/pcapng-parser-defects September 14, 2026 16:25
@JarryShaw JarryShaw closed this Sep 14, 2026
@JarryShaw JarryShaw added the fix Pull requests that fix a defect (fix: subject prefix) label Sep 23, 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)

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant