inet: choose the payload class from reassembled fragments - #5143
inet: choose the payload class from reassembled fragments#5143KernelClint wants to merge 2 commits into
Conversation
AI-Assisted: yes (GPT-5.6-Cyber)
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #5143 +/- ##
==========================================
+ Coverage 80.63% 80.81% +0.18%
==========================================
Files 390 390
Lines 96936 96972 +36
==========================================
+ Hits 78168 78372 +204
+ Misses 18768 18600 -168
🚀 New features to boost your workflow:
|
Can you give a real world example of that? (not something too artificial) |
AI-Assisted: yes (GPT-5.6-Cyber)
|
Scapy's own >>> pkt = IP(id=1, src="10.0.0.1", dst="10.0.0.2")/TCP(sport=1234, dport=80)/Raw(b"B"*100)
>>> for size in (8, 16, 24, 40):
... print(size, TCP in defragment([IP(raw(f)) for f in fragment(pkt, size)])[0])
8 False
16 False
24 True
40 TrueThe threshold is 20 bytes. The first fragment has to carry the fixed TCP header before Scapy can read it as TCP, and truncated options are tolerated, so a 40-byte header behaves the same. 24 is the smallest fragment size that is both a multiple of 8 and at least 20. Below it the first fragment comes back as The other is the tiny-fragment attack in RFC 1858. The sender picks a fragment size small enough to push the TCP flags into the second fragment, so a filter matching on those flags cannot see them. Reading traffic like that is a reason to reach for Scapy, and today the reassembled packet has no TCP layer at all. |
|
I have pushed a second commit. Taking the class from |
_defrag_ip_pkt()puts a fragmented IPv4 datagram back together and then has to decide what thereassembled bytes are. At
scapy/layers/inet.py:1467-1505it reuses the class Scapy guessed for the first fragment's payload:
The first fragment is often too short to classify correctly — with a small enough first fragment
Scapy has not seen a complete TCP header, so the guess is whatever it managed from those bytes. The
complete datagram is then forced into that class regardless of what the assembled bytes actually
are. Through
TCPSession, an HTTP response that arrived in valid ordered fragments comes backwithout its application layer, while the byte-identical unfragmented response comes back correctly.
The change asks the reassembled IP layer to classify the complete bytes, the same way ordinary
dissection does:
Every valid fragment layout is still reassembled; what changes is that the result is classified from
what was actually received.
The added regression reassembles the same HTTP response at two fragment sizes, including one whose
first fragment is too small to classify, and asserts the response body is delivered in both. Without
the source change it fails.
Performance was measured on one computer, before and after the fix: reassembling a valid ordered
datagram took 232.0 µs before and 176.3 µs after — 24% faster. Repeat runs moved by less than
that, so it is a real difference. Dispatching normally avoids the work the old path spent forcing
bytes into a class that could not parse them.