Skip to content

fix(toolkit): the dpkt, scapy and pyshark engines do not stop an extension-header chain at 32 #1609

Closed

Description

@JarryShaw

#1604's ruling is a fixed bound of 32 IPv6 extension headers, shared by every engine. #1608 applies it to the default engine and pypcapfile. dpkt, scapy and pyshark have their own parsers walk the chain, so they still find TCP past the bound.

Repro on #1608's head (e6d87c64c). Each capture is Ethernet, then IPv6, then n Hop-by-Hop headers (PadN 4), then TCP carrying data. TCP datagrams reassembled:

n default dpkt scapy
32 1 1 1
33 0 1 1
1,000 0 1 0

The #1608 worker also measured where each parser gives up: scapy at about 245 layers, and tshark (the pyshark engine) at about 496. I have not re-measured those two figures.

Expected: the dpkt, scapy and pyshark adapters count the headers they step over and stop at EXTENSION_HEADER_LIMIT (pcapkit/protocols/internet/internet.py, added by #1608), as the default engine does. #1608 records the gaps as Gap rows in tests/foundation/engines/test_ext_chain_limit_agreement_1604_unit.py.

Blocked until #1605 and #1608 merge. The fix goes in the tcp_reassembly/tcp_traceflow helpers of pcapkit/toolkit/{dpkt,scapy}.py, which #1605 rewrites, and it needs #1608's constant.

Activity

  1. JarryShaw commented on Oct 10, 2026

    @JarryShaw
    OwnerAuthor

    Blocked until #1605 and #1608 merge: #1605 rewrites the dpkt and scapy TCP helpers, and #1608 adds the bound constant.

  2. added
    fixPull requests that fix a defect (fix: subject prefix)
    blockedDeferred pending another issue or decision; see the last comment for what unblocks it
    on Oct 10, 2026
  3. JarryShaw commented on Oct 10, 2026

    @JarryShaw
    OwnerAuthor

    Widened per the #1610 ruling. The adapters must also stop at the new per-frame bound of 64 layers, so that they agree with the default engine on deep nesting of any protocol, not only on extension-header chains. pypcapfile is in scope too, because its own walk does not go through ProtocolBase._parse_next_layer.

    Blocked until #1605, #1608 and the #1610 PR merge: #1605 rewrites the adapters' TCP helpers, and the other two add the constants.

  4. JarryShaw commented on Oct 10, 2026

    @JarryShaw
    OwnerAuthor

    #1605 and #1608 have merged. Still blocked until #1620 (the #1610 PR) merges, since the adapters must stop at its per-frame bound.

  5. JarryShaw commented on Oct 10, 2026

    @JarryShaw
    OwnerAuthor

    Widening: the same gap holds for IPv4 reassembly input, not only TCP. Behind 33 Destination Options headers in IPv6, the default engine hands IPv4 reassembly nothing, while scapy still hands the IPv4 fragment over (measured on ee324e1dc). dpkt stops there once #1622 lands. So the scapy and pyshark adapters' IPv4 (and IPv6) reassembly inputs must also stop at EXTENSION_HEADER_LIMIT. Still blocked until #1620 merges.

  6. JarryShaw commented on Oct 10, 2026

    @JarryShaw
    OwnerAuthor

    Also blocked until the #1625 PR merges, since both edit the extension-header walk in pcapkit/toolkit/pyshark.py.

  7. JarryShaw commented on Oct 12, 2026

    @JarryShaw
    OwnerAuthor

    #1620 has merged. Still blocked until the #1625 PR merges (it replaces #1628, closed for conflicts).

  8. JarryShaw commented on Oct 13, 2026

    @JarryShaw
    OwnerAuthor

    Widening: dpkt's IPv6 reassembly input also runs past the bound, not only scapy's and pyshark's. Measured on main (b375656e1), 3.14 and 3.11. Each capture is Ethernet, then IPv6, then Destination Options headers (PadN 4), then a first-fragment Fragment header. IPv6 reassembly inputs (header length):

    Fragment header is the default dpkt scapy
    32nd extension header 1 (288) 1 (288) 1 (288)
    33rd extension header 0 1 (296) 1 (296)

    Neither adapter counts the headers before the Fragment header. dpkt reads ipv6.extension_hdrs.get(44) (pcapkit/toolkit/dpkt.py:902) and scapy tests IPv6ExtHdrFragment in ipv6 (pcapkit/toolkit/scapy.py:536). pyshark and pypcapfile decline IPv6 reassembly, so there is nothing for them to bound.

  9. JarryShaw commented on Oct 13, 2026

    @JarryShaw
    OwnerAuthor

    Widening: the dpkt and scapy walkers do not apply the 64-layer bound (FRAME_LAYER_LIMIT, pcapkit/protocols/protocol.py:412, #1610). The walkers are:

    • dpkt: _tcp_carrier (pcapkit/toolkit/dpkt.py:572) and _first_ipv4 (:717)
    • scapy: _tcp_carrier (pcapkit/toolkit/scapy.py:429), _first_ipv4 (:365) and ipv6_reassembly (:608)

    Measured on main (b375656e1) with Python 3.14.8 and 3.11.15 (dpkt 1.9.8; scapy 2.7.0 and 2.8.0). Each frame is Ethernet, then the nesting named. The nested headers are IP-in-IP carrying protocol 4, or 41 for IPv6 in IPv4. Ethernet counts as layer 1. Cells count the inputs handed to TCP, IPv4 or IPv6 reassembly.

    Frame Layer of the TCP or fragment header default dpkt scapy
    TCP in 62 IPv4 64 1 1 1
    TCP in 63 IPv4 65 0 1 1
    TCP behind 31 / 32 (IPv4, AH) pairs 64 / 66 1 / 0 1 / 1 1 / 1
    TCP behind 31 / 32 (IPv6, Hop-by-Hop) pairs 64 / 66 1 / 0 1 / 1 1 / 1
    IPv4 fragment in 62 / 63 IPv6 64 / 65 1 / 0 1 / 1 1 / 1
    IPv6 fragment in 61 / 62 IPv4 64 / 65 1 / 0 0 / 0 (no 6in4 read on main; #1637) 1 / 1

    The default engine's rule: each VLAN tag, IP header, AH and IPv6 extension header adds one layer. A Fragment header sits at the IPv6 header's depth, plus the headers before it, plus 1. scapy also reads TCP behind 62 VLAN tags, which puts it at layer 65; the default engine reads 0 there.

    On 3.11 with tshark 4.6.9, pypcapfile reads TCP in 63 and in 100 IPv4 (1/1, against the default engine's 0/0). pyshark traces it at both depths (0/1, since it declines reassembly).

  10. 1 remaining item

  11. added
    wipWork in flight - a covering PR is open or an agent is actively on it
    and removed
    blockedDeferred pending another issue or decision; see the last comment for what unblocks it
    on Oct 13, 2026
  12. JarryShaw commented on Oct 13, 2026

    @JarryShaw
    OwnerAuthor

    Unblocked: #1652 and #1655 have merged. Workers now have the remaining dpkt and scapy parts, one PR per engine file: dpkt together with #1651 and #1654, and scapy together with #1650. Each PR cites this issue as Part of, so this closes only after both merge.

  13. moved this from Blocked to WIP in PyPCAPKiton Oct 13, 2026
  14. JarryShaw commented on Oct 13, 2026

    @JarryShaw
    OwnerAuthor

    Where each part stands:

  15. removed
    wipWork in flight - a covering PR is open or an agent is actively on it
    on Oct 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    fixPull requests that fix a defect (fix: subject prefix)

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions