Skip to content

IPv6 reassembly: the default/pcapng adapters keep the Fragment header in ihl and header, dpkt/scapy do not #415

Description

@JarryShaw

The four toolkit adapters disagree about whether the 8-octet Fragment header
belongs to the IPv6 reassembly packet's ihl and header. Measured on
examples/captures/ipv6.pcap, frame 13:

adapter ihl len(header) tl Fragment header inside header?
toolkit/pcap.py (default) 48 48 1496 yes
toolkit/pcapng.py 48 48 1496 yes
toolkit/dpkt.py 40 40 1488 no
toolkit/scapy.py 40 40 1496 no

Note tl is a three-way disagreement: scapy and the default agree at 1496
while dpkt reports 1488.

Why the default path includes it

pcapkit/protocols/internet/ipv6.py:341 adds each extension header's length to
hdr_len before the Fragment-header check breaks the loop at line 354:

hdr_len += next_.length          # line 341
...
if ex_proto == Enum_ExtensionHeader.IPv6_Frag:   # line 354
    ipv6.__update__({'fragment': self._read_packet(header=hdr_len, payload=raw_len)})
    break

So by the time _read_packet(header=hdr_len, ...) cuts the header, hdr_len
already counts the Fragment header — and ipv6_info.fragment.header, which
toolkit/pcap.py:119 passes as header, therefore contains it. The data
model's own comment agrees that this is hdr_len's meaning: "Header length
(including extension headers)".

dpkt and scapy compute the boundary independently and land on the other
side: hdr_len + ipv6_frag.__hdr_len__ is where dpkt starts the payload, and
scapy uses bytes(ipv6)[:-len(ipv6_frag)].

The user-visible consequence

The reassembled datagram presents a Fragment header on a datagram that is by
definition no longer a fragment:

reassembled datagram, default engine:
  len(header)=48  next-header byte=44 (44 = IPv6-Frag)
  payload len=4778

:rfc:8200#section-4.5 — "The Fragment header is not present in the
reassembled packet." So the default and pcapng adapters are the wrong ones
here, and dpkt/scapy are right.

Suggested fix

Have toolkit/pcap.py and toolkit/pcapng.py report the header up to but not
including the Fragment header, matching the other two adapters and the RFC.
ipv6_info.hdr_len itself should keep its documented meaning (it is correct as
a header length; it is just not the right value for this field), so the
adapters need the pre-Fragment length rather than a change to the data model.
tl wants settling in the same pass, since no two adapters currently agree on
it either.

Worth a test asserting all four adapters produce the same ihl, header and
tl for the same capture — the inconsistency survived because each adapter is
only ever tested against itself.

Found while fixing the documentation for these fields in #413, where the
docstring's "only headers before IPv6-Frag" comment turned out to be wrong for
the adapter it describes. That comment is repeated verbatim in all four
adapters and should be corrected alongside the behaviour.

Activity

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

    bugIssues reporting a defect (set by the bug report template; a default, not an assessment)

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions