网络子系统51_ip协议报文分片

来源:互联网 发布:阿里云域名管理登录 编辑:程序博客网 时间:2024/05/21 17:59
//ip分片//快速路径的条件://1.skb//1.skb的数据长度(主缓存区+frags缓存区)小于输出路径的mtu//2.skb的数据长度对齐到8字节的边界//3.skb没有被分片//4.skb没有被共享//2.skb->frag_list//1.长度小于(mtu-ip报头-选项)//2.除最后一个分片外,长度都需要对齐到8字节边界//3.head-data之间的空间,可以容纳ip报头//注:skb->frag_list的skb,没有填充ip头,skb填充有ip头////慢速路径条件://只要不满足快速路径其中的一条,使用慢速路径//快速路径处理过程://1.第一个分片使用完整的ip选项//2.其余分片使用部分ip选项//3.除最后一个分片外,设置MF标志//4.设置offset//5.使用相同的路由信息,向下层传递//慢速路径处理过程://1.分配新的skb,长度为mtu,或者剩余数据量,对齐到8字节边界//2.预留l2帧头空间//3.拷贝l3报头,以及数据到新skb中//4.除第一个分片使用完整的ip选项,其余分片使用部分ip选项//5.设置offset//5.使用相同的路由信息,向下层传递//对比快速路径与慢速路径://1.慢速路径的慢主要表现在分配新的缓存区,从旧缓存区中拷贝数据//注:ip报头的offset字段,只针对有效载荷(ip头,ip选项不包括在内)//调用路径ip_output/ip_mc_output->ip_fragment1.1 int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*)){struct iphdr *iph;int raw = 0;int ptr;struct net_device *dev;struct sk_buff *skb2;unsigned int mtu, hlen, left, len, ll_rs;int offset;int not_last_frag;struct rtable *rt = (struct rtable*)skb->dst;int err = 0;//出口设备dev = rt->u.dst.dev;//ip头iph = skb->nh.iph;//ip报头设置有DF标志,禁止分片////skb->local_df如果被设置,则在需要分片,但是设置DF标志,不向发送方传送ICMP消息if (unlikely((iph->frag_off & htons(IP_DF)) && !skb->local_df)) {icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,//需要分片,但是设置DF标志,通知发送方不可达,原因是需要分片,并告知对方mtu  htonl(dst_pmtu(&rt->u.dst)));kfree_skb(skb);return -EMSGSIZE;}//可以快速分片的条件://skb//1.skb的数据长度(主缓存区+frags缓存区)小于输出路径的mtu//2.skb的数据长度对齐到8字节的边界//3.skb没有被分片//4.skb没有被共享//skb->frag_list//1.长度小于(mtu-ip报头)//2.除最后一个分片外,长度都需要对齐到8字节边界//3.head-data之间的空间,可以容纳ip报头//注:skb->frag_list的skb,没有填充ip头,skb填充有ip头hlen = iph->ihl * 4;//ip头长度mtu = dst_pmtu(&rt->u.dst) - hlen;//数据空间的大小if (skb_shinfo(skb)->frag_list) {//frag_list存在skbstruct sk_buff *frag;int first_len = skb_pagelen(skb);//skb主缓存区,frags片段中的数据长度,不包括frag_list中的skbif (first_len - hlen > mtu ||//超过允许的最大数据量    ((first_len - hlen) & 7) ||//数据长度没有对齐到8字节    (iph->frag_off & htons(IP_MF|IP_OFFSET)) ||//此skb是一个分片    skb_cloned(skb))//克隆一份skb,慢速分片goto slow_path;//检查frag_list中的skb是否可以快速分片for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next) {//frag_list中的skb没有ip头if (frag->len > mtu ||    ((frag->len & 7) && frag->next) ||//除最后一个分片外,其他分片的长度必须对其到8字节    skb_headroom(frag) < hlen)//头空间不够容纳ip报头(ip头+选项)    goto slow_path;if (skb_shared(frag))//skb被共享goto slow_path;}//快速路径:err = 0;offset = 0;frag = skb_shinfo(skb)->frag_list;skb_shinfo(skb)->frag_list = NULL;//更新skb->data_len为skb->frags中数据的大小,原始skb->data_len包括frags,frag_list中所有数据的长度skb->data_len = first_len - skb_headlen(skb);skb->len = first_len;//更新总长度为主缓存区,frags中数据的大小,原始skb->len包括主缓存区,frags,frag_list中所有数据的长度iph->tot_len = htons(first_len);//ip报头的数据包长度iph->frag_off |= htons(IP_MF);//表示有更多的分片,第一个分片,offset=0ip_send_check(iph);//计算ip校验和for (;;) {if (frag) {//处理skb->frag_list中的skb,为其准备分片的ip报头frag->ip_summed = CHECKSUM_NONE;//表示校验和没有计算frag->h.raw = frag->data;frag->nh.raw = __skb_push(frag, hlen);//移动skb->data指针,填充ip报头和选项memcpy(frag->nh.raw, iph, hlen);iph = frag->nh.iph;iph->tot_len = htons(frag->len);//总长度ip_copy_metadata(frag, skb);//使分片skb与头skb有一样的出口设备,路由信息if (offset == 0)//第一个分片具有完整的选项,其他分片将所有非copied的选项,均设置为NOOPip_options_fragment(frag);offset += skb->len - hlen;//计算本分片的偏移量iph->frag_off = htons(offset>>3);//偏移量对齐在8字节if (frag->next != NULL)iph->frag_off |= htons(IP_MF);//设置还有更多skbip_send_check(iph);//计算ip校验和}err = output(skb);//向下传递前一个skb,调用ip_finish_outputif (err || !frag)break;skb = frag;//保留指向前一个skb的指针frag = skb->next;//frag为下一个待处理的skbskb->next = NULL;//}if (err == 0) {IP_INC_STATS(IPSTATS_MIB_FRAGOKS);return 0;}//快速路径分片出现错误,释放所有skbwhile (frag) {skb = frag->next;kfree_skb(frag);frag = skb;}IP_INC_STATS(IPSTATS_MIB_FRAGFAILS);return err;}//慢速路径slow_path:left = skb->len - hlen;//(主缓存区,frags,frag_list)数据的总大小(不包括ip头,选项)ptr = raw + hlen;//新分片的数据在原skb中的起始位置offset = (ntohs(iph->frag_off) & IP_OFFSET) << 3;//当前分片的偏移量not_last_frag = iph->frag_off & htons(IP_MF);//判断是否为最后一个分片//开始进行分片while(left > 0){len = left;if (len > mtu)//使用mtu(此处的mtu去掉ip报头和选项长度)len = mtu;if (len < left){len &= ~7;//长度对齐到8字节边界}//分配新的skb,长度包括l2帧头,l3报头,l3有效载荷if ((skb2 = alloc_skb(len+hlen+ll_rs, GFP_ATOMIC)) == NULL) {NETDEBUG(printk(KERN_INFO "IP: frag: no memory for new fragment!\n"));err = -ENOMEM;goto fail;}//使所有分片都使用相同的路由信息,出口设备ip_copy_metadata(skb2, skb);skb_reserve(skb2, ll_rs);//预留l2帧头skb_put(skb2, len + hlen);//data-tail之间空间大小为(ip报头+ip选项+ip有效载荷)skb2->nh.raw = skb2->data;//设置l3报头起始地址skb2->h.raw = skb2->data + hlen;//l3有效载荷//设置新创建的skb所属的sockif (skb->sk)skb_set_owner_w(skb2, skb->sk);//拷贝ip头,选项memcpy(skb2->nh.raw, skb->data, hlen);//将skb起始地址为ptr的len个字节拷贝到skb2中//由skb_copy_bits处理frag,frag_listif (skb_copy_bits(skb, ptr, skb2->h.raw, len))BUG();left -= len;//更新剩余待分片的数据量iph = skb2->nh.iph;iph->frag_off = htons((offset >> 3));//偏移量//1.只有第一个分片需要完整的选项,其他分片将非copied的选项设置为NOOP//2.由于第一个分片已经拷贝完整的ip报头以及选项到其分片中//3.优化效率,在第一个分片拷贝完整的选项后,更新选项,非第一个分片都使用相同的ip选项if (offset == 0)ip_options_fragment(skb);if (left > 0 || not_last_frag)iph->frag_off |= htons(IP_MF);//还有跟多的分片ptr += len;//下一个分片的数据在原skb中的起始位置offset += len;//偏移量IP_INC_STATS(IPSTATS_MIB_FRAGCREATES);iph->tot_len = htons(len + hlen);ip_send_check(iph);err = output(skb2);if (err)goto fail;}kfree_skb(skb);IP_INC_STATS(IPSTATS_MIB_FRAGOKS);return err;fail:kfree_skb(skb); IP_INC_STATS(IPSTATS_MIB_FRAGFAILS);return err;}//调用路径 ip_fragment->ip_options_fragment//修改ip_option(skb->cb),将非copied类型的选项,均设置为NOOP类型2.1 void ip_options_fragment(struct sk_buff * skb) {unsigned char * optptr = skb->nh.raw;struct ip_options * opt = &(IPCB(skb)->opt);int  l = opt->optlen;int  optlen;while (l > 0) {switch (*optptr) {case IPOPT_END:return;case IPOPT_NOOP:l--;optptr++;continue;}optlen = optptr[1];if (optlen<2 || optlen>l)  return;if (!IPOPT_COPIED(*optptr))//在选项type的第8个比特,指出该选项是否应该复制到ip分片中memset(optptr, IPOPT_NOOP, optlen);//对不需要拷贝到除第一个分片外的选项,设置为NOOPl -= optlen;optptr += optlen;}opt->ts = 0;//均设置为0,表明不需要time stamp,record routeopt->rr = 0;opt->rr_needaddr = 0;opt->ts_needaddr = 0;opt->ts_needtime = 0;return;}

原创粉丝点击