You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(protocols): a chain of ~80+ IPv6 extension headers exhausts recursion and leaves the payload undissected #1604
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:
Accept a documented depth bound and make the other engines stop at the same depth.
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.
Recommendation: option 1, a fixed depth bound shared by every engine.
No real chain gets close. RFC 8200 §4.1 allows each extension header at most once, except Destination Options, which may appear at most twice. A compliant chain is therefore under about 10 headers. A bound of 32 leaves ample headroom.
Why not option 2: making next-layer dissection iterative rewrites the recursion that every protocol in pcapkit/protocols/ relies on. That is a large, high-risk change for input that only crafted or hostile captures produce.
If you agree, I'll dispatch it with 32 as the bound unless you prefer another number.
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.
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.
Activity
JarryShaw commentedon Oct 10, 2026
Recommendation?
JarryShaw commentedon Oct 10, 2026
Recommendation: option 1, a fixed depth bound shared by every engine.
pcapkit/protocols/relies on. That is a large, high-risk change for input that only crafted or hostile captures produce.If you agree, I'll dispatch it with 32 as the bound unless you prefer another number.
JarryShaw commentedon Oct 10, 2026
Yea sounds good. Take the recursion limit.
JarryShaw commentedon Oct 10, 2026
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.
fix(protocols): bound the IPv6 extension-header chain at 32 headers (#…
JarryShaw commentedon Oct 10, 2026
Do we only limit IPv6 or extend to all protocols from top level?
JarryShaw commentedon Oct 10, 2026
Not IPv6-only. The stack runs out for any protocol at about 105–109 layers. Measured on #1608's head, Python 3.14:
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.fix(protocols): bound the IPv6 extension-header chain at 32 headers (#…
fix(protocols): bound a frame's dissection at 64 layers (#1610)
fix(protocols): bound a frame's dissection at 64 layers (#1610)
fix(protocols): bound a frame's dissection at 64 layers (#1610)
fix(protocols): bound the IPv6 extension-header chain at 32 headers (#…
fix(protocols): bound a frame's dissection at 64 layers (#1610)
fix(toolkit): dpkt and scapy find TCP in IPv4 where the default engin…
fix(toolkit): dpkt and scapy find TCP in IPv4 where the default engin…
fix(protocols): bound a frame's dissection at 64 layers (#1610) (#1620)
fix(toolkit): dpkt and scapy find TCP in IPv4 where the default engin…
fix(toolkit): pyshark traces no TCP inside encapsulations the default…