网络数据接收过程分析(四)--…

ip_frag_reasm函数

 

static struct sk_buff *ip_frag_reasm(struct ipq *qp, struct net_device *dev)

{

       struct iphdr *iph;

       struct sk_buff *fp, *head = qp->fragments;

       int len;

       int ihlen;

 

       ipq_kill(qp);

 

       BUG_TRAP(head != NULL);

       BUG_TRAP(FRAG_CB(head)->offset == 0);

      

       //取得ipq中fragments头节点的ip头长度

       ihlen = ip_hdrlen(head);

 

       //如果把头节点的IP首部长度加上ipq结构中的碎片总长度相加,就得到了重组之

       //后报文的长度

       len = ihlen + qp->len;

 

       if (len > 65535)

              goto out_oversize;

 

       //头节点必须没有被克隆过

       if (skb_cloned(head) && pskb_expand_head(head, 0, 0, GFP_ATOMIC))

              goto out_nomem;

 

       //碎片中第一个节点如果是分片的,需要特殊处理,这里的”分片”并不是指IP包的

       //碎片,而是指skb存储结构离散分布,并不在一个连续的内存空间内

 

       if (skb_shinfo(head)->frag_list) {

              struct sk_buff *clone;

              int i, plen = 0;

              //如果头节点是分片的,那么需要重新申请一个skb,并且把这个新的skb放到

              //第一个skb end指针之后skb_shared_info结构的frag_list链表上

              if ((clone = alloc_skb(0, GFP_ATOMIC)) == NULL)

                     goto out_nomem;

              clone->next = head->next;

              head->next = clone;

 

              //把head原来的分片放在新申请的skb的frag_list里面

              skb_shinfo(clone)->frag_list = skb_shinfo(head)->frag_list;

              skb_shinfo(head)->frag_list = NULL;

 

              //计算head中总的分片长度

              for (i=0; i<skb_shinfo(head)->nr_frags; i++)

                     plen += skb_shinfo(head)->frags[i].size;

             

              //实际上最后生成了一个自身数据为0.不包含任何数据,但是

              //这个新的的frag_list中却包含了所有的分片

              clone->len = clone->data_len = head->data_len - plen;

              head->data_len -= clone->len;

              head->len -= clone->len;

              clone->csum = 0;

              clone->ip_summed = head->ip_summed;

              atomic_add(clone->truesize, &ip_frag_mem);

       }

    

       //把head以后所有的碎片都当作是head frag_list里面的分片来处理

       skb_shinfo(head)->frag_list = head->next;

 

 

       skb_push(head, head->data - skb_network_header(head));

       atomic_sub(head->truesize, &ip_frag_mem);

 

       //协议栈的处理会通过skb_linearize()函数将head报文的frag_list链表里面的数据包

       //都合并成一个报文,所以将链表里面所有skb的len和data_len,以及true_size都

       //和head中相应的值相加,最后得到了合并后数据包的长度

       for (fp=head->next; fp; fp = fp->next) {

              head->data_len += fp->len;

              head->len += fp->len;

              if (head->ip_summed != fp->ip_summed)

                     head->ip_summed = CHECKSUM_NONE;

              else if (head->ip_summed == CHECKSUM_COMPLETE)

                     head->csum = csum_add(head->csum, fp->csum);

              head->truesize += fp->truesize;

              atomic_sub(fp->truesize, &ip_frag_mem);

       }

 

       head->next = NULL;

       head->dev = dev;

       head->tstamp = qp->stamp;

 

       iph = ip_hdr(head);

       iph->frag_off = 0;

       iph->tot_len = htons(len);

       IP_INC_STATS_BH(IPSTATS_MIB_REASMOKS);

       qp->fragments = NULL;

       return head;

 

      //省去部分代码

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值