Skip to content

fix(protocols): a chain of ~80+ IPv6 extension headers exhausts recursion and leaves the payload undissected #1604

Closed

Description

@JarryShaw

With the default engine, a long chain of IPv6 extension headers stops dissection partway: the TCP behind it is never found, and no warning is given.

Repro on #1600's head a532960cc (the same code path as main). Each frame is Ethernet, then IPv4 with protocol 60, then N Destination Options headers (each next header 60 except the last, which is 6; Hdr Ext Len 0; PadN 4), then TCP carrying data. TCP datagrams from the default engine, on 3.14.8 with recursion limit 1000:

N 60–80 100 1,000
TCP datagrams 1 0 0

The #1600 review measured the split at 79 on 3.11.

Probable cause (inferred from the depth dependence, not traced): each extension header dissects its next layer recursively, and the dissection stops when it reaches Python's recursion limit. pypcapfile's walk in #1600 is iterative, so beyond that depth the two engines disagree.

Decision needed:

  1. Accept a documented depth bound and make the other engines stop at the same depth.
  2. Make next-layer dissection iterative, a large change across pcapkit/protocols/.

Chains this long do not occur in real traffic; they come only from crafted or hostile captures.

Activity

  1. added
    fixPull requests that fix a defect (fix: subject prefix)
    needs: decisionWaiting on the maintainer to decide — not blocked by other work
    on Oct 10, 2026
  2. moved this to Needs decision in PyPCAPKiton Oct 10, 2026
  3. JarryShaw commented on Oct 10, 2026

    @JarryShaw
    OwnerAuthor

    Recommendation?

  4. JarryShaw commented on Oct 10, 2026

    @JarryShaw
    OwnerAuthor

    Recommendation: option 1, a fixed depth bound shared by every engine.

    If you agree, I'll dispatch it with 32 as the bound unless you prefer another number.

  5. JarryShaw commented on Oct 10, 2026

    @JarryShaw
    OwnerAuthor

    Yea sounds good. Take the recursion limit.

  6. JarryShaw commented on Oct 10, 2026

    @JarryShaw
    OwnerAuthor

    Recorded: the owner accepted option 1, bounding the extension-header depth instead of making dissection iterative. No other number was named, so the bound is 32 headers, shared by the default engine and every toolkit adapter. On reaching it the walk stops, the rest of the frame stays raw, and one warning is emitted. A worker is on it.

  7. added
    wipWork in flight - a covering PR is open or an agent is actively on it
    and removed
    needs: decisionWaiting on the maintainer to decide — not blocked by other work
    on Oct 10, 2026
  8. moved this from Needs decision to WIP in PyPCAPKiton Oct 10, 2026
  9. JarryShaw commented on Oct 10, 2026

    @JarryShaw
    OwnerAuthor

    Do we only limit IPv6 or extend to all protocols from top level?

  10. JarryShaw commented on Oct 10, 2026

    @JarryShaw
    OwnerAuthor

    Not IPv6-only. The stack runs out for any protocol at about 105–109 layers. Measured on #1608's head, Python 3.14:

    • 200 stacked 802.1Q tags: dissection stops at layer 109.
    • 108 nested IPv4-in-IPv4: dissection stops at an IPv4 layer.

    In both cases TCP is lost and nothing is warned. #1608 bounds only the IPv6 extension-header chain, so a top-level bound is needed either way.

    My recommendation: keep #1608 as it is, and add one per-frame layer bound in ProtocolBase._parse_next_layer (pcapkit/protocols/protocol.py:2323), which every protocol passes through. I've widened #1610 to cover that and put the question there.

  11. removed
    wipWork in flight - a covering PR is open or an agent is actively on it
    on Oct 10, 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