Skip to content

scapy toolkit adapter passes raw 13-bit fragment offset unscaled to IP reassembly #483

Description

@JarryShaw

What

Found while fixing #477. pcapkit/toolkit/scapy.py:160:

```python
data = IP_Packet(
...
fo=ipv4.frag, # fragment offset
...
)
```

`ipv4.frag` is scapy's raw IPv4 fragment-offset field — a 13-bit value counted in 8-octet units, per the wire format — not a byte offset. Every other pcapkit toolkit adapter either multiplies by 8 explicitly or uses a value the underlying engine has already scaled:

  • `pcapkit/toolkit/dpkt.py:215`: `fo=ipv4.offset * 8`
  • `pcapkit/toolkit/pypcapfile.py:326`: `fo=ipv4.off * 8`
  • `pcapkit/toolkit/pcap.py:67` / `pcapkit/toolkit/pcapng.py:70`: `fo=ipv4_info.offset` (pcapkit's own `ipv4.py` sets `offset=int(schema.flags['offset']) * 8` at `pcapkit/protocols/internet/ipv4.py:307`, i.e. already in octets)
  • `pcapkit/toolkit/scapy.py:225` (IPv6 fragment header, same file): `fo=ipv6_frag.offset * 8` — scapy's IPv6 fragment path does scale it; only the IPv4 path at line 160 does not

Reproduction (not yet executed against the reassembler, confirmed only at the field level)

```python

import scapy.all as sc
p = sc.IP(frag=5, flags='MF')
bytes(p)[6:8].hex()
'2005'
```

`0x2005` decodes as flags=`001` (MF set) + a 13-bit offset of `5` — i.e. scapy stored the raw unit count `5`, not a byte offset of `40`. `pcapkit.toolkit.scapy.ipv4_reassembly` would hand this packet to `IP.reassembly()` as `fo=5`, when every other engine reading the same bytes would report `fo=40`.

Expected impact

Any capture read through the scapy engine (`pcapkit.foundation.engines.scapy`) containing IPv4 fragments would compute fragment placement 8x too small, corrupting reassembly for that engine specifically — silently, since nothing validates `fo` against the fragment's own length. IPv6 fragments read through the same engine are unaffected (the Fragment-header path already multiplies by 8).

Scope

Not fixing here — this is a single-line, single-adapter fix (`fo=ipv4.frag * 8`) but wants its own verification against an actual reassembly run through the scapy engine (e.g. a scapy-crafted fragmented IPv4 packet fed through `pcapkit.foundation.engines.scapy`) and its own test, rather than being folded into #477/#482, which own `pcapkit/foundation/reassembly/ip.py` and its data model, not `pcapkit/toolkit/scapy.py`.

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