Last time we analyzed IP fragmentation reassembly, this time we will analyze IP fragmentation. First, we need to understand why fragmentation is necessary. For example, in Ethernet, the CSMA/CD protocol (implemented by the network card) specifies a maximum (MTU) and minimum size for link layer packets (excluding the MAC header, but in this kernel version, the size of the MAC header is included). Therefore, if the upper layer packet exceeds this threshold, it needs to be fragmented. The implementation of fragmentation and reassembly occurs at the IP layer. Let’s take a look at the specific logic. The logic for IP fragmentation is implemented in the ip_fragment function.
void ip_fragment(
struct sock *sk,
struct sk_buff *skb,
struct device *dev,
int is_frag
)
Some variables are defined.
struct iphdr *iph;
unsigned char *raw;
unsigned char *ptr;
struct sk_buff *skb2;
int left, mtu, hlen, len;
int offset;
unsigned long flags;
// MAC address
raw = skb->data;
// IP header address, hard_header_len is the size of the MAC header
iph = (struct iphdr *) (raw + dev->hard_header_len);
skb->ip_hdr = iph;
// Size of the IP header, excluding the data part
hlen = (iph->ihl * sizeof(unsigned long));
// Total size of the IP packet minus the IP layer equals the length of the data part of the IP message that needs to be fragmented
left = ntohs(iph->tot_len) - hlen;
// IP header + MAC header
hlen += dev->hard_header_len;
// The length of the data part of each fragment equals the MAC layer MTU minus the MAC header and IP header, i.e., the MAC layer MTU includes the total size of the MAC header, IP header, and IP data part.
mtu = (dev->mtu - hlen);
// Address of the data part
ptr = (raw + hlen);
Check if fragmentation is possible.
// If fragmentation is not allowed, send an ICMP message, which can be compared with the IP message format
if (ntohs(iph->frag_off) & IP_DF)
{
icmp_send(skb,ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, dev->mtu, dev);
return;
}
Check if the IP packet to be fragmented is itself a fragment, i.e., it has undergone multiple IP fragmentations.
/*
This IP packet is itself a fragment, and now needs to be fragmented again,
the offset address is the address of this packet multiplied by 8, because the offset of the packet being fragmented again is
based on the offset of the original unfragmented data, not the offset of the current fragment
*/
if (is_frag & 2)
offset = (ntohs(iph->frag_off) & 0x1fff) << 3;
else
offset = 0
Start processing fragmentation.
// If there are still bytes left, continue processing
while(left > 0)
{
// The default number of bytes carried by the IP packet, but if it exceeds the MTU, take the smaller value, i.e., MTU
len = left;
// If greater than MTU, it needs to be fragmented, i.e., can only carry bytes of MTU size, otherwise it is the last fragment
if (len > mtu)
len = mtu;
/*
When the remaining bytes are greater than MTU, the following judgment will hold,
i.e., the remaining bytes cannot be sent in this transmission, and further fragmentation is needed
Dividing by 8 and multiplying by 8 to take a multiple of 8, not necessarily equal to MTU
*/
if (len < left)
{
len/=8;
len*=8;
}
// len is the size of the data carried by this fragment
// Allocate new skb, size is MAC header + IP header + data part length
if ((skb2 = alloc_skb(len + hlen,GFP_ATOMIC)) == NULL)
{
return;
}
skb2->arp = skb->arp;
skb2->free = 1;
// Total size is MAC header + IP header + data part length
skb2->len = len + hlen;
// Point to the newly allocated memory address, start copying data
skb2->h.raw=(char *) skb2->data;
save_flags(flags);
restore_flags(flags);
// IP address
skb2->raddr = skb->raddr;
// raw points to the MAC header address, here we copy the MAC header and IP header + options into skb, IP options should only be copied to the first fragment, here it will be copied to each fragment
memcpy(skb2->h.raw, raw, hlen);
// Copy the data part, length is len, ptr points to the address of the data part in the original IP message,
memcpy(skb2->h.raw + hlen, ptr, len);
// Remaining bytes to be fragmented
left -= len;
// Point to the IP header address
skb2->h.raw+=dev->hard_header_len;
iph = (struct iphdr *)(skb2->h.raw);
// Set the offset of this fragment, divided by 8, see the IP protocol specification
iph->frag_off = htons((offset >> 3));
/*
1. If there is still data, set MF, indicating more fragments are needed
2. is_frag =1; indicates that there are more fragments after this one.
This means that the data being fragmented is itself an IP fragment, i.e., fragmented again.
Therefore, all fragments under this message have MF set to 1.
*/
if (left > 0 || (is_frag & 1))
iph->frag_off |= htons(IP_MF);
// Update data pointer and offset
ptr += len;
offset += len;
// Send the fragment
ip_queue_xmit(sk, dev, skb2, 2);
}
The main logic of fragmentation is: 1. Allocate new memory, copy the MAC header, IP header from the packet to be fragmented into the new memory, then cut a piece of the data part and continue copying to the memory afterwards. This continues until fragmentation is complete. 2. Modify some field values in the IP message, such as MF. 3. Call the lower-level interface to send each fragment one by one. The logic of fragmentation is not complex, so I won’t explain it in too much detail.