小编给大家分享一下TCP/IP协议如何实现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; else offset = 0
开始处理分片。
// 还有则继续处理 while(left > 0) { // ip包默认承载的字节数,但是如果大于mtu的话则取小的值,即mtu len = 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 调底层接口逐个发送分片
分片的逻辑不算复杂,不讲解的太详细了。
以上是“TCP/IP协议如何实现ip分片”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注亿速云行业资讯频道!
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。
原文链接:https://my.oschina.net/u/4217331/blog/4379091