网站备案 幕布宁波seo公司网站推广
上次分析了ip分片重组,这次分析一下ip分片。首先我们要先了解为什么需要分片。比如在以太网中,使用CSMA/CD协议(由网卡实现),他规定了一个链路层数据包(不包括mac头,但是这一版内核实现的时候是包括了mac头的大小)的最大值(MTU)和最小值。所以如果上层的包大于这个阈值就需要被分片。而分片和组包的实现是在ip层。我们看一下具体的逻辑。ip分片的逻辑在ip_fragment函数里实现。
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;// mac首地址raw = skb->data;// ip头首地址,hard_header_len为mac头大小iph = (struct iphdr *) (raw + dev->hard_header_len);skb->ip_hdr = iph;// ip头的大小,不包括数据部分hlen = (iph->ihl * sizeof(unsigned long));// ip包总大小减去ip层等于ip报文的数据长度,即需要分片的部分的大小left = ntohs(iph->tot_len) - hlen; // ip头+mac头hlen += dev->hard_header_len; // 每个分片的数据部分长度等于mac层的mtu减去mac头和ip头,即mac层的mtu包括了mac头、ip头、ip数据部分的总和。mtu = (dev->mtu - hlen); // 数据部分首地址ptr = (raw + hlen);
判断是否可以分片。
// 设置了不能分片则发送icmp报文,可以对照ip报文格式看if (ntohs(iph->frag_off) & IP_DF){icmp_send(skb,ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, dev->mtu, dev);return;}
判断即将被分片的ip包是否本身也是一个分片。即经过了多次ip分片。
/*该ip报文本身就是一个分片,现在需要进行再次分片,偏移的首地址是该报文的首地址乘以8,因为再次被分片的报文,他的偏移是基于原来未被分片的数据的偏移。而不是针对当前这个分片的偏移
*/if (is_frag & 2)offset = (ntohs(iph->frag_off) & 0x1fff) << 3;elseoffset = 0
开始处理分片。
// 还有则继续处理while(left > 0){ // ip包默认承载的字节数,但是如果大于mtu的话则取小的值,即mtulen = left;// 大于mtu则还要分片,即只能承载mtu大小的字节,否则就是最后一个分片if (len > mtu)len = mtu;/*剩下的字节比mtu大的时候下面的判断会成立,即剩下的字节还不能在这次发送完,还要继续分片除8乘8即取8的倍数大小,不一定等于mtu */if (len < left){len/=8;len*=8;}// len 为这一分片承载的数据大小// 申请新的skb,大小为mac头+ip头+数据部分长度if ((skb2 = alloc_skb(len + hlen,GFP_ATOMIC)) == NULL){return;}skb2->arp = skb->arp;skb2->free = 1;// 总大小是mac头+ip头+数据部分长度skb2->len = len + hlen;// 指向刚分配的内存首地址,开始复制数据skb2->h.raw=(char *) skb2->data;save_flags(flags);restore_flags(flags);// ip地址skb2->raddr = skb->raddr; // raw指向mac头首地址,这里把mac报头和ip报头+选项都复制到skb中,ip选项应该只复制到第一个分片,这里会复制到每一个分片中memcpy(skb2->h.raw, raw, hlen);// 复制数据部分,长度为len,ptr指向原ip报文中数据部分的首地址,memcpy(skb2->h.raw + hlen, ptr, len);// 剩下需要分片的字节数left -= len;// 指向ip头首地址skb2->h.raw+=dev->hard_header_len;iph = (struct iphdr *)(skb2->h.raw);// 设置该分片的偏移,除以8,见ip协议的规定iph->frag_off = htons((offset >> 3));/*1. 还有数据,则置MF,还要更多分片2. is_frag =1;说明该分片后面还有更多分片。表示被分片的数据本身就是一个ip分片,即再分片。所以该报文下的所有分片MF都是1。*/if (left > 0 || (is_frag & 1))iph->frag_off |= htons(IP_MF);// 更新数据指针和偏移ptr += len;offset += len;// 发送分片ip_queue_xmit(sk, dev, skb2, 2);}
分片主要的逻辑是
1 申请一个新的内存,把待分片报文中的mac头、ip头,复制到新内存,然后数据部分切一块继续复制到内存后面。如此,直到分片完毕
2 修改ip报文中的一些字段的值 ,比如MF。
3 调底层接口逐个发送分片
分片的逻辑不算复杂,不讲解的太详细了。