Accueil > Réseau, Sécurité > Fragmented IP packet forwarding

Fragmented IP packet forwarding

29/08/2023 Categories: Réseau, Sécurité Tags: , , ,
Print Friendly, PDF & Email

About Fragmented IP packet forwarding.

I couldn’t really find a suitable topic for this post actually but I will try to find answers for the following questions:

  • How can we fragment an IP packet manually in scapy
  • How does a fragmented packet look like and how the transport layer (TCP/UDP) header is located
  • How do we forward fragmented packets, do we reassemle them?
  • If we don’t reassemble, can we force reassembly?

First of all a bit of a theory: if an incoming IP packet is to be forwarded to another next hop and the MTU of this new path is smaller than the packet to be transmitted, we must find a way to forward the packet. If the packet has DF (Don’t Fragment) bit on i.e we are instructed not to fragment the packet most probably by the source, then normally we are expected to send an ICMP packet with type “Fragmentation needed” and pray that on the way back to the source no devices block all ICMP type of traffic. Second scenario is that what if the source lets us fragment the packet. Then we need to fragment it and story from now on is about this part of the scenario and the topology we will use is something like below.

fragmented_packets

 

Scapy is a fantastic tool to generate your own packets. It is exremely flexible and in our example, we will perform the fragmentation of a packet via our script.

Once you install scapy, you create the following lovely script.

#!/usr/bin/python
from scapy.all import *
dip="173.63.1.2"
payload="A"*496+"B"*500
packet=IP(dst=dip,id=12345)/UDP(sport=1500,dport=1501)/payload
frags=fragment(packet,fragsize=500)
counter=1
for fragment in frags:
 print "Packet no#"+str(counter)
 print "==================================================="
 fragment.show() #displays each fragment
 counter+=1
 send(fragment)

What do we do here? In a nutshell, we create an IP packet transport protocol of which is UDP.  Our payload contains only characters A and B:) I put 496 A and 500 B character so we have around ~1000bytes of payload and we instruct scapy to devide this total IP packet in 500 byte segments by fragmenting it and send it to IP address 173.63.1.2. Isn’t it so cool?

Lire aussi:  Reduce firewall configuration complexity using iptables with chains

Now we will run the script and capture(tcpdump) on Debian1 device to see how the fragmented packets look like.

root@debian1:~/Python/Scapy# python frag.py 
WARNING: No route found for IPv6 destination :: (no default route?)
Packet no#1
===================================================
###[ IP ]###
 version = 4
 ihl = None
 tos = 0x0
 len = None
 id = 12345
 flags = MF
 frag = 0
 ttl = 64
 proto = udp
 chksum = None
 src = 144.2.3.2
 dst = 173.63.1.2
 \options \
###[ Raw ]###
 load = '\x05\xdc\x05\xdd\x03\xec\xbf+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
.
Sent 1 packets.
Packet no#2
===================================================
###[ IP ]###
 version = 4
 ihl = None
 tos = 0x0
 len = None
 id = 12345
 flags = 
 frag = 63
 ttl = 64
 proto = udp
 chksum = None
 src = 144.2.3.2
 dst = 173.63.1.2
 \options \
###[ Raw ]###
 load = 'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB'
.
Sent 1 packets.

We have run the script and 496bytes of A characters are transmitted on packet 1 and all 500bytes of B characters are transmitted on the second packet i.e we split the packet into two. Let’s see how the packet looks like on the wire.

root@debian1:~# tcpdump -tvvnni eth1.956 host 173.63.1.2
tcpdump: listening on eth1.956, link-type EN10MB (Ethernet), capture size 65535 bytes

IP (tos 0x0, ttl 64, id 12345, offset 0, flags [+], proto UDP (17), length 524)
 144.2.3.2.1500 > 173.63.1.2.1501: UDP, length 996
IP (tos 0x0, ttl 64, id 12345, offset 504, flags [none], proto UDP (17), length 520)
 144.2.3.2 > 173.63.1.2: ip-proto-17
IP (tos 0xc0, ttl 62, id 17715, offset 0, flags [none], proto ICMP (1), length 576)
 173.63.1.2 > 144.2.3.2: ICMP 173.63.1.2 udp port 1501 unreachable, length 556
 IP (tos 0x0, ttl 62, id 12345, offset 0, flags [none], proto UDP (17), length 1024, bad cksum e962 (->76f)!)
 144.2.3.2.1500 > 173.63.1.2.1501: UDP, length 996

3 packets captured
3 packets received by filter
0 packets dropped by kernelLet me explain each line what happens here.

1st Packet

IP (tos 0x0, ttl 64, id 12345, offset 0, flags [+], proto UDP (17), length 524)
 144.2.3.2.1500 > 173.63.1.2.1501: UDP, length 996

We pushed 496A+500B bytes of payload of data to scapy. Dear scapy took 496bytes of this data which is all A characters and encapsulated with 8  bytes of UDP header + 20 bytes of IP header which is in total = 524 bytes. Pay attention to the port numbers. Those are the UDP port numbers we set in the code. UDP length shows 996bytes since our payload is this number of bytes in total. ID number is 12345 and it is the same on 1st and 2nd packet. Offset is also 0 as this is the first packet. Although we can’t see on this output, we have also More Fragment bit is on.

Lire aussi:  Configuring Log Rotation of Apache2 and Other Logs

2nd packet

IP (tos 0x0, ttl 64, id 12345, offset 504, flags [none], proto UDP (17), length 520)
 144.2.3.2 > 173.63.1.2: ip-proto-17

Real fun begins here. Where are the port numbers? We don’t have them on the second packet as the UDP header is on the first packet. You can see this from the packet size. 500bytes(B) payload + 20 bytes IP header i.e no room for header. The evidence of fragmentation is the offset but why is it 504? This field specifies how far we are from the beginning of the unfragmented IP packet and I believe it counts UDP header too:) so our offset should be 496A + 8bytes UDP header = 504.

Note: If you display the same packets in Wireshark, due to the default setting “Reassemble fragmented IPv4 datagrams“, it misleads you to think that UDP header is on the second packet instead of the first one. Be careful!

3rd packet

This isn’t even a UDP segment. Because we are sending UDP traffic to a port on which there isn’t any listening socket, remote side sends back an ICMP (Destination port unreachable) notification message.

So far we have found the answers to first two questions. Now we need to see how our stateful firewall forwards these packets. I am running tcpdump on hostE destination device this time.

root@hostE:~# tcpdump -tnni eth1.963
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth1.963, link-type EN10MB (Ethernet), capture size 65535 bytes
IP 144.2.3.2.1500 > 173.63.1.2.1501: UDP, length 996
IP 144.2.3.2 > 173.63.1.2: ip-proto-17
^C
2 packets captured
2 packets received by filter
0 packets dropped by kernel

We can see that we have received 2 fragmented packets first of which has the UDP header.

Lire aussi:  Howto: Geolocation for Fail2ban

Note: You don’t see the ICMP destination reachable message as I blocked it via iptables otherwise, it clears SRX session immediately and we can’t display the session.

Session ID: 132, Policy name: allow-from-Internet/4, Timeout: 58, Valid
 In: 144.2.3.2/1500 --> 173.63.1.2/1501;udp, If: ge-0/0/0.951, Pkts: 2, Bytes: 1044
 Out: 173.63.1.2/1501 --> 144.2.3.2/1500;udp, If: ge-0/0/0.963, Pkts: 0, Bytes: 0
Total sessions: 1

and the one above is the flow session entry. 2 packets have been forwarded in total 1044 bytes. We don’t have return packet as it is one way traffic.

IP fragmentation reassembly normally is performed at the destination host unless there is a device in the path which needs this reassembly (e.g IDP) but there is a command which does this for us. This is just to demonstrate the option. You don’t really need to use it

set security flow force-ip-reassembly
commit

You can see reassembly being done if you enable flow traceoptions as the firewall forwards fragmented packets as is to the destination host.

I have tried to give a bit of information about fragmentation and its relation to transport layer protocol UDP. I hope this was helpful!

Source: RtooDtoo.net

Categories: Réseau, Sécurité Tags: , , ,
Les commentaires sont fermés.