void ip_fragment(struct sock *sk, struct sk_buff *skb, struct device *dev, int is_frag) { struct iphdr *iph; unsigned char *raw; unsigned char *ptr; struct sk_buff *skb2; int left, mtu, hlen, len; int offset; unsigned long flags;
/* * Point into the IP datagram header. */
raw = skb->data; iph = (struct iphdr *) (raw + dev->hard_header_len);
skb->ip_hdr = iph;
/* * Setup starting values. */
hlen = (iph->ihl * sizeof(unsigned long)); left = ntohs(iph->tot_len) - hlen; /* Space per frame */ hlen += dev->hard_header_len; /* Total header size */ mtu = (dev->mtu - hlen); /* Size of data space */ ptr = (raw + hlen); /* Where to start from */
/* * Check for any "DF" flag. [DF means do not fragment] */
if (ntohs(iph->frag_off) & IP_DF) { /* * Reply giving the MTU of the failed hop. */ ip_statistics.IpFragFails++; icmp_send(skb,ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, dev->mtu, dev); return; }
/* * The protocol doesn't seem to say what to do in the case that the * frame + options doesn't fit the mtu. As it used to fall down dead * in this case we were fortunate it didn't happen */
if(mtu<8) { /* It's wrong but it's better than nothing */ icmp_send(skb,ICMP_DEST_UNREACH,ICMP_FRAG_NEEDED,dev->mtu, dev); ip_statistics.IpFragFails++; return; }
/* * Fragment the datagram. */
/* * The initial offset is 0 for a complete frame. When * fragmenting fragments it's wherever this one starts. */
if (is_frag & 2) offset = (ntohs(iph->frag_off) & 0x1fff) << 3; else offset = 0;
/* * Keep copying data until we run out. */
while(left > 0) { len = left; /* IF: it doesn't fit, use 'mtu' - the data space left */ if (len > mtu) len = mtu; /* IF: we are not sending upto and including the packet end then align the next start on an eight byte boundary */ if (len < left) { len/=8; len*=8; } /* * Allocate buffer. */
if ((skb2 = alloc_skb(len + hlen,GFP_ATOMIC)) == NULL) { printk("IP: frag: no memory for new fragment!\n"); ip_statistics.IpFragFails++; return; }
/* * Set up data on packet */
skb2->arp = skb->arp; if(skb->free==0) printk("IP fragmenter: BUG free!=1 in fragmenter\n"); skb2->free = 1; skb2->len = len + hlen; skb2->h.raw=(char *) skb2->data; /* * Charge the memory for the fragment to any owner * it might possess */
save_flags(flags); if (sk) { cli(); sk->wmem_alloc += skb2->mem_len; skb2->sk=sk; } restore_flags(flags); skb2->raddr = skb->raddr; /* For rebuild_header - must be here */
/* * Copy the packet header into the new buffer. */
memcpy(skb2->h.raw, raw, hlen);
/* * Copy a block of the IP datagram. */ memcpy(skb2->h.raw + hlen, ptr, len); left -= len;
skb2->h.raw+=dev->hard_header_len;
/* * Fill in the new header fields. */ iph = (struct iphdr *)(skb2->h.raw/*+dev->hard_header_len*/); iph->frag_off = htons((offset >> 3)); /* * Added AC : If we are fragmenting a fragment thats not the * last fragment then keep MF on each bit */ if (left > 0 || (is_frag & 1)) iph->frag_off |= htons(IP_MF); ptr += len; offset += len;
/* * Put this fragment into the sending queue. */
ip_statistics.IpFragCreates++;
ip_queue_xmit(sk, dev, skb2, 2);//还是调用ip_queue_xmit()函数来发送分片后的数据 } ip_statistics.IpFragOKs++; } |