Skip to content

fix(toolkit): the default engine ends the extraction on an IPv6 header whose Version is 4 #1666

Closed

Description

@JarryShaw

Describe the bug
With the default engine, PCAP and PCAP-NG alike, an IPv6 layer whose Version is 4 and Next Header is TCP, carrying a TCP header the TCP parser rejects, raises AttributeError: 'IPv6' object has no attribute 'offset' at pcapkit/toolkit/pcap.py:168. It comes through tcp_reassembly (:324), tcp_traceflow (:371) and their PCAP-NG twins (pcapkit/toolkit/pcapng.py:167, :216), and ends the extraction, so later frames are lost. dpkt and scapy read the frame.

Root cause: tcp_segment takes the IPv4 branch of its later-fragment check by the header's Version, if ip_info.version == 4: (pcap.py:167, added by #1556 for #1518), not by the layer's class. IPv6.read copies the Version unchecked (pcapkit/protocols/internet/ipv6.py:206), though Data_IPv6.version is typed Literal[6] (pcapkit/protocols/data/internet/ipv6.py:38) and IPv4.read rejects any Version but 4 (pcapkit/protocols/internet/ipv4.py:296). Versions 0, 5 and 15 take the IPv6 branch, and a TCP header that parses returns before line 167.

Reproduction
main (85e4198c8, 1.5.0b10), CPython 3.14.8 (dpkt 1.9.8, scapy 2.7.0) and 3.11.15 (dpkt 1.9.8, scapy 2.8.0, pypcapfile 0.12.0, pyshark 0.6, tshark 4.6.9), same output on both:

import struct, pcapkit
tcp = struct.pack('!HHIIBBHHH', 1234, 80, 1000, 0, 0xF0, 0x18, 65535, 0, 0) + b'hello'  # Data Offset 15
ip = struct.pack('!IHBB', 4 << 28, len(tcp), 6, 64) + bytes.fromhex('fe80' + '00' * 13 + '01' + 'fe80' + '00' * 13 + '02')
frame = bytes(12) + b'\x86\xdd' + ip + tcp  # EtherType IPv6, header Version 4
rec = struct.pack('<IIII', 0, 0, len(frame), len(frame)) + frame
open('bad.pcap', 'wb').write(struct.pack('<IHHiIII', 0xA1B2C3D4, 2, 4, 0, 0, 65535, 1) + rec * 2)
for engine in ('default', 'dpkt', 'scapy'):
    try:
        ext = pcapkit.extract(fin='bad.pcap', engine=engine, nofile=True, trace=True, tcp=True,
                              trace_format='json', trace_fout='flows')
        print(engine, 'traced frames:', [flow.index for flow in ext.trace.tcp])
    except AttributeError as exc:
        print(engine, 'AttributeError:', exc)
default AttributeError: 'IPv6' object has no attribute 'offset'
dpkt traced frames: [(1, 2)]
scapy traced frames: [(1, 2)]

Family survey. H4 is the 40-octet IPv6 header above (Version 4, Next Header 6); bad TCP is the segment above; TCP is a valid one. Cells are TCP reassembly / trace inputs, or IP reassembly inputs where named; - is no such adapter.

# Frame default dpkt scapy pypcapfile pyshark
1 0x86DD, H4, bad TCP AttributeError 1 / 1 1 / 1 0 / 0 - / 0
2 802.1Q, 0x86DD, H4, bad TCP AttributeError 1 / 1 1 / 1 0 / 0 - / 0
3 IPv4 protocol 41, H4, bad TCP AttributeError 1 / 1 1 / 1 0 / 0 - / 0
4 802.1Q, IPv4 protocol 41, H4, bad TCP AttributeError 1 / 1 1 / 1 0 / 0 - / 0
5 IPv6 Next Header 41, H4, bad TCP AttributeError 1 / 1 1 / 1 0 / 0 - / 0
6 row 1, PCAP-NG AttributeError 1 / 1 1 / 1 not run not run
7 row 1, Version 0, 5 or 15 1 / 1 1 / 1 1 / 1 0 / 0 - / 0
8 0x86DD, H4, TCP (control) 1 / 1 1 / 1 1 / 1 0 / 0 - / 0
9 0x86DD, IPv4, TCP 0 / 0 0 / 0 0 / 0 0 / 0 - / 0
10 IPv4 protocol 41 or IPv6 Next Header 41, IPv4, TCP 0 / 0 0 / 0 0 / 0 0 / 0 - / 0
11 0x0800 or IPv4 protocol 4, IPv4 header with Version 6, TCP 0 / 0 0 / 0 0 / 0 0 / 0 - / 0
12 0x0800 (also behind 802.1Q), IPv6, TCP 0 / 0 0 / 0 0 / 0 0 / 0 - / 1
13 IPv4 protocol 4 or IPv6 Next Header 4, IPv6, TCP 0 / 0 0 / 0 0 / 0 0 / 0 - / 1
14 0x86DD or IPv6 Next Header 41, two IPv4 fragments: IPv4 reassembly 0 0 0 0 -
15 0x86DD, two H4 fragments with a Fragment header: IPv6 reassembly 2 2 2 - -
16 0x0800 or IPv4 protocol 4, two IPv6 fragments: IPv6 reassembly 0 0 0 - -

Without tcp=True the extraction does not raise; rows 9-16 read without error. pypcapfile has no IPv6 decoder. pyshark differs separately: tshark stops at "Bogus IPv6 version" in rows 1-8, and in rows 12-13 dissects the IPv6 header that _carrier (pcapkit/toolkit/pyshark.py:541) then keeps. #1639 has that shape but not these rows.

Expected behavior
The default engine reads rows 1-6 without raising and hands dpkt's and scapy's inputs, as it already does for rows 7 and 8. Testing isinstance(ip, IPv4) at pcap.py:167, patched at runtime only, gives inputs identical to dpkt's and scapy's on rows 1-5. Whether IPv6.read should reject a Version other than 6, as IPv4.read and tshark do, is a separate decision that changes rows 1-8 and 15 for every engine.

Activity

  1. added
    fixPull requests that fix a defect (fix: subject prefix)
    pendingAccepted and unstarted - nobody is on it and nothing blocks it
    on Oct 13, 2026
  2. moved this to Pending in PyPCAPKiton Oct 13, 2026
  3. added
    wipWork in flight - a covering PR is open or an agent is actively on it
    and removed
    pendingAccepted and unstarted - nobody is on it and nothing blocks it
    on Oct 13, 2026
  4. JarryShaw commented on Oct 13, 2026

    @JarryShaw
    OwnerAuthor

    A worker has this. The fix picks tcp_segment's branch by the layer's class rather than the header's Version, in pcap.py and pcapng.py, with a test covering the 16 rows above.

    IPv6.read keeps accepting any Version for now. Whether it should reject one other than 6, as IPv4.read and tshark do, is your call, because that changes every engine's rows 1-8 and 15.

  5. moved this from Pending to WIP in PyPCAPKiton Oct 13, 2026
  6. added
    needs: decisionWaiting on the maintainer to decide — not blocked by other work
    on Oct 13, 2026
  7. moved this from WIP to Needs decision in PyPCAPKiton Oct 13, 2026
  8. moved this from Needs decision to Done in PyPCAPKiton Oct 13, 2026
  9. removed
    wipWork in flight - a covering PR is open or an agent is actively on it
    needs: decisionWaiting on the maintainer to decide — not blocked by other work
    on Oct 13, 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