Skip to content

fix(toolkit): scapy cuts 4in6 at each Payload Length and skips ICMP quotes (#1623, #1627) - #1631

Merged
JarryShaw merged 1 commit into
mainfrom
fix/1623-1627-scapy-ipv6-payload-and-icmp-quote
Oct 13, 2026
Merged

JarryShaw merged 1 commit into
mainfrom
fix/1623-1627-scapy-ipv6-payload-and-icmp-quote

Conversation

@JarryShaw

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

#1623. Scapy files a frame's trailer as padding at the end of the packet, so bytes() of IPv4 carried in IPv6 ran past each Payload Length. ipv4_reassembly now takes _within_datagrams(ipv4) (pcapkit/toolkit/scapy.py:544) and gives no input when those octets cut the header (:547). The issue's two rows now match the default engine: payloads 16, 8 (were 22, 14), and 10, 4 with the datagram partial (were 16, 8, complete).

#1627. IPerror (protocol 41) and IPerror6 (next header 4 or 41) bind full IPv6/IP layers, so _tcp_carrier found TCP in an ICMP error's quote. The walk now stops at either (_quoted, :337, :482). ipv4_reassembly finds its layer by the same walk (_first_ipv4, :365): packet['IP'] reached IPv4 in ICMPv6 quotes and in a Redirected Header's packet field.

Measured on 3.14 (scapy 2.7.0): scapy differed from the default engine in 7 of 21 #1622 IPv4 cases and 20 of 31 probe cases; afterwards only #1609's 33-header case and the out-of-scope rows below. dpkt agrees throughout.

New tests/toolkit/test_scapy_ipv6_payload_and_icmp_quote_1623_1627_unit.py: 29 IPv4 and 21 TCP cases, scapy and dpkt against the default engine. Reverting pcapkit/ gives 42 failures; each of the 5 guards fails it alone. Also green on 3.11 (scapy 2.8.0).

Not fixed here, identical before and after:

Issues for the first two are being filed.

Closes #1623
Closes #1627

…uotes (#1623, #1627)

- #1623: ipv4_reassembly handed on bytes() of the IPv4 layer, which Scapy
  ends with the frame's trailer however deep the layer, so IPv4 carried in
  IPv6 ran past each Payload Length above it. It now takes the octets
  _within_datagrams() bounds, and gives no input when they cut the header.
- #1627: IPerror (protocol 41) and IPerror6 (next header 4 or 41) bind full
  IPv6 and IP layers, so _tcp_carrier found TCP inside an ICMP error's
  quote. Its walk now stops at either (_quoted). ipv4_reassembly finds its
  layer by the same walk (_first_ipv4) instead of packet['IP'], which
  reached into ICMPv6 quotes and into a Redirected Header's packet field.

New tests/toolkit/test_scapy_ipv6_payload_and_icmp_quote_1623_1627_unit.py
compares scapy and dpkt with the default engine over 29 IPv4 and 21 TCP
cases; it fails without the fix, and each guard is load-bearing.
@JarryShaw JarryShaw added the fix Pull requests that fix a defect (fix: subject prefix) label Oct 13, 2026
@JarryShaw

Copy link
Copy Markdown
Owner Author

Verdict: GOOD TO GO at 6a838d074.

Sonnet cross-review, on a 53-case IPv4 probe and a 51-case TCP probe under scapy 2.7.0 and 2.8.0. It confirmed 7 of 21 cases differing before and 1 after, the 42 failures with pcapkit/ reverted, and both out-of-scope items.

Non-blocking notes:

  • The body undercounted the rows that still differ (4in6 behind AH, in GRE, in VXLAN). They are now listed.
  • _quoted imports per layer. That follows the module's lazy-import pattern.

Not checked: scapy 2.6.0 (the floor), and the author's own 31-case probe.

I re-ran the new test file at this head: 6 passed, 110 subtests.

@JarryShaw JarryShaw added the review: good-to-go Cross-review at the current head says ready; CI state is separate label Oct 13, 2026
@JarryShaw JarryShaw moved this to In review in PyPCAPKit Oct 13, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Coverage: 89.66% (unit tier, Python 3.14, 6a838d074, 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 2884 145 1010 35 94.14%
pcapkit/interface 138 7 52 5 93.68%
pcapkit/protocols 17152 226 4578 191 98.06%
pcapkit/toolkit 1036 168 386 5 82.35%
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
JarryShaw merged commit b375656 into main Oct 13, 2026
38 checks passed
@github-project-automation github-project-automation Bot moved this from In review to Done in PyPCAPKit Oct 13, 2026
@JarryShaw
JarryShaw deleted the fix/1623-1627-scapy-ipv6-payload-and-icmp-quote branch October 13, 2026 01:41
@JarryShaw JarryShaw removed the review: good-to-go Cross-review at the current head says ready; CI state is separate label Oct 13, 2026
JarryShaw added a commit that referenced this pull request Oct 14, 2026
Third-party engines are best effort (#1687), so the scapy code that
exists only to match the default engine comes out:

- revert #1683, #1663, #1652 and #1631, and the scapy hunks of #1614: the
  link-type and loopback-family walk, the frame and extension-header
  bounds, the IPv6 chain walk over the octets, the stops at GRE, VXLAN,
  PPPoE and the other layers the default engine leaves raw and at ICMP
  quotes, and the IPv4 header checks
- keep #1624's walk (TCP behind AH or an IPv6 extension header in IPv4,
  and the later-fragment stop), #1605's keying by the carrying IP header
  and #1589's trailer cut; the walk calls _walk_extension_headers, which
  the pypcapfile revert keeps
- re-land as guards: a zero-length frame is no IPv4 input and has no
  packet2dict octets (#1676); IPv4 in IPv6 ends at the Payload Length
  (#1623); a packet built by hand is read off its octets (#1676);
  ipv6_reassembly takes only its own IPv6 layer's Fragment header, and
  none in a later IPv4 fragment
- delete the four scapy agreement modules and test_scapy_unit's
  default-engine checks; the guards' tests assert constants
- docs: scapy applies the extension-header bound only behind IPv4 and the
  frame-layer bound nowhere, and its silent gaps are listed
  where gaps are said to be announced

The scapy toolkit and engine modules, the shared modules that run scapy,
and the project and tier guards pass on 3.14 (scapy 2.7.0) and 3.11
(scapy 2.8.0).
JarryShaw added a commit that referenced this pull request Oct 14, 2026
Third-party engines are best effort (#1687), so the scapy code that
exists only to match the default engine comes out:

- revert #1683, #1663, #1652 and #1631, and the scapy hunks of #1614: the
  link-type and loopback-family walk, the frame and extension-header
  bounds, the IPv6 chain walk over the octets, the stops at GRE, VXLAN,
  PPPoE and the other layers the default engine leaves raw and at ICMP
  quotes, and the IPv4 header checks
- keep #1624's walk (TCP behind AH or an IPv6 extension header in IPv4,
  and the later-fragment stop), #1605's keying by the carrying IP header
  and #1589's trailer cut; the walk calls _walk_extension_headers, which
  the pypcapfile revert keeps
- re-land as guards: a zero-length frame is no IPv4 input and has no
  packet2dict octets (#1676); IPv4 in IPv6 ends at the Payload Length
  (#1623); a packet built by hand is read off its octets (#1676);
  ipv6_reassembly takes only its own IPv6 layer's Fragment header, and
  none in a later IPv4 fragment
- delete the four scapy agreement modules and test_scapy_unit's
  default-engine checks; the guards' tests assert constants
- docs: scapy applies the extension-header bound only behind IPv4 and the
  frame-layer bound nowhere, and its silent gaps are listed
  where gaps are said to be announced

The scapy toolkit and engine modules, the shared modules that run scapy,
and the project and tier guards pass on 3.14 (scapy 2.7.0) and 3.11
(scapy 2.8.0).
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

1 participant