小编给大家分享一下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分片”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注亿速云行业资讯频道!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。