Reputation: 103
I tried to simplify my problem with the following setup.
Scapy sniffer running with root privileges:
from scapy.all import sniff, IP, UDP
def print_package(packet):
packet.show()
sniff(filter="ip dst host 192.168.183.130 and dst port 1337", iface="ens33", prn=print_package)
Sending IP packets / UDP frames with the 1500 Bytes MTU limit works like a charm and the packets are printed to std-out as expected. As soon as I succeed the limit and the IP protocol creates fragments, the sniffer only catches the first packet (correct flags, len etc.)
In the following example I sent a simple string 'next message will be 3200 * "A"' from the nc client to the listener before sending 3200 * "A" with netcat. The packet gets fragmented into three IP packets and correctly reassembled by the stack, before the UDP socket netcat is using receives it, so everything works as i would expect it network-wise. Scapy only sniffs the first of the three packets and I do not understand why this happens.
The screenshot shows the expected/correct handling of the text message and the three IP fragments in wireshark:
The following snippet shows the scapy output to stdout:
sudo python3 scapy_test.py
###[ Ethernet ]###
dst = 00:0c:29:fa:93:72
src = 00:0c:29:15:2a:11
type = IPv4
###[ IP ]###
version = 4
ihl = 5
tos = 0x0
len = 59
id = 18075
flags = DF
frag = 0
ttl = 64
proto = udp
chksum = 0x3c3
src = 192.168.183.128
dst = 192.168.183.130
\options \
###[ UDP ]###
sport = 59833
dport = 1337
len = 39
chksum = 0xdaa0
###[ Raw ]###
load = 'next message will be 3200 * "A"\n'
###[ Ethernet ]###
dst = 00:0c:29:fa:93:72
src = 00:0c:29:15:2a:11
type = IPv4
###[ IP ]###
version = 4
ihl = 5
tos = 0x0
len = 1500
id = 20389
flags = MF
frag = 0
ttl = 64
proto = udp
chksum = 0x1518
src = 192.168.183.128
dst = 192.168.183.130
\options \
###[ UDP ]###
sport = 59833
dport = 1337
len = 3209
chksum = 0x25bd
###[ Raw ]###
load = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
Why are the other IP fragments missing and how can I sniff them?
I know about the session parameter in sniff but did I not have any luck with actually reassembling the packets with session=IPSession
. (This is not what I want to achieve anyway, for my application I want to sniff all fragments, change parts of them and forward them to another address/socket.)
Upvotes: 2
Views: 1893
Reputation: 103
I finally figured this out myself, so I am gonna leave an answer:
The problem lies in the filter of the sniffer:
sniff(filter="ip dst host 192.168.183.130 and dst port 1337", iface="ens33", prn=print_package)
IP fragments after the first do not have a UDP part and therefore do not have a destination port, therefore the scapy filter does not catch them. To work around this problem I extended the filter to catch dst port 1337 or Fragments with an offset. I stumbled across this cheatsheet https://github.com/SergK/cheatsheat-tcpdump/blob/master/tcpdump_advanced_filters.txt, that offers a valid berkeley syntax for this problem and ended up with this filter (for the simplified problem).
sniff(filter="ip dst host 192.168.183.130 and ((src port 1337) or (((ip[6:2] > 0) or (ip[7] > 0)) and (not ip[6] = 64))", iface="ens33", prn=print_package)
This checks if the fragment offset of the packet is >0 (anything after the first three bit of the sixth byte (flags) or the seventh byte are >0) and if the "don't fragment" bit is not set. If this is true, it is an IP fragment and the sniffer shall sniff it.
Upvotes: 2