IP 碎片重组
IP 碎片重组
内核中的IP重组函数. struct sk_buff * ip_defrag(struct sk_buff * skb, u32 user) { ...... //如果内核范围超出限制 if (atomic_read(&ip_frag_mem) > sysctl_ipfrag_high_thresh) ip_evictor(); //回收内存到限制之内 ...... if ((qp = ip_find(iph, user)) != NULL) { //查找或者创建一个队列头 ...... ip_frag_queue(qp, skb); //收集IP碎片 if (qp->last_in == (FIRST_IN|LAST_IN) && qp->meat == qp->len) //IP碎片收集完毕 ret = ip_frag_reasm(qp, dev); //把收集的碎片重组,主要是更新头结点 ...... return ret; //返回新重组后的数据报头 } ...... return NULL; //队列头创建失败 } 我们看到主要有三个函数最重要,我们一个一个看. 首先看一下数据结构 struct ipq { struct hlist_node list; //hash list ...... struct sk_buff *fragments; // linked list of received fragments int len; // 应该接收数据长度 int meat; // 实际接收数据长度 ...... }; static inline struct ipq * ip_find(struct iphdr * iph, u32 user) { ...... //根据ip中的信息,计算出一个hash值 hash = ipqhashfn(id, saddr, daddr, protocol); //在hash表中查找相关队列头 hlist_for_each_entry(qp, n, &ipq_hash[hash], list) { if(......) //找到匹配 return qp; } ...... //没找到,创建一个然后放入hash表中 return ip_frag_create(iph, user); } //好好看看,不太难 static void ip_frag_queue(struct ipq * qp, struct sk_buff * skb) { struct sk_buff * prev, * next; int flags, offset; int ihl, end; ...... offset = ntohs(skb->nh.iph->frag_off); flags = offset & ~IP_OFFSET; // 获取标志位 offset &= IP_OFFSET; offset <<= 3; // 确定偏移位置 ihl = skb->nh.iph->ihl * 4; // ip 头长度 end = offset + skb->len - ihl; // 确定这个IP包数据在完整包中的结束点 if ((flags & IP_MF) == 0) { //是最后一个IP包吗 ...... qp->last_in |= LAST_IN; //也许没有受到全部的包,但这个报一定是那最后一个,所以加标志 ...... } else { ...... if (end > qp->len) { ....... qp->len = end; //记录最后的位置 } } ....... //找到这个数据报在全部数据报中的位置 prev = NULL; for(next = qp->fragments; next != NULL; next = next->next) { if (FRAG_CB(next)->offset >= offset) break; /* bingo! */ prev = next; } if (prev) { //有上个位置的包 //计算复盖的长度 int i = (FRAG_CB(prev)->offset + prev->len) - offset; if (i > 0) { //有复盖 offset += i; //调整这个包的偏移 ....... } } //有下个位置的包 while (next && FRAG_CB(next)->offset < end) { int i = end - FRAG_CB(next)->offset; //计算复盖的长度 // 没有复盖全部的next包 if (i < next->len) { ...... FRAG_CB(next)->offset += i; //调整next的偏移 qp->meat -= i; //调整实际数据长度 ...... } else { struct sk_buff * free_it = next; ...... frag_kfree_skb(free_it, NULL); //释放掉她 } } //记录这个偏移 FRAG_CB(skb)->offset = offset; //插入这个数据包 skb->next = next; if (prev) prev->next = skb; else qp->fragments = skb; ...... qp->meat += skb->len; //记录实际接收数据的长度 ...... } //重组一个新包,并不实际拷贝数据,因为包已经被连接好 skb->netx->next->next static struct sk_buff * ip_frag_reasm(struct ipq * qp, struct net_device * dev) { ...... struct sk_buff * fp, * head = qp->fragments; ...... ihlen = head->nh.iph->ihl * 4; //IP头长度 len = ihlen + qp->len; //头加数据的总长度 ...... skb_shinfo(head)->frag_list = head->next; //指向头下的一个数据包 skb_push(head, head->data - head->nh.raw); //调整指针 ...... for (fp = head->next; fp; fp = fp->next) { //长度收集 head->data_len += fp->len; head->len += fp->len; ....... head->truesize += fp->truesize; atomic_sub(fp->truesize, &ip_frag_mem); //使用内存调整 } head->next = NULL; //next NULL ....... qp->fragments = NULL; // set NULL return head; ....... } IP 分片 ip_output->ip_finish_output 中 ...... if (skb->len > dst_mtu(skb->dst) && !skb_is_gso(skb)) //包长度超过mtu 且没有开启 gso (gso参考: http://lwn.net/Articles/189970/) return ip_fragment(skb, ip_finish_output2); //分片 else return ip_finish_output2(skb); 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; __be16 not_last_frag; struct rtable *rt = (struct rtable*)skb->dst; //路由项 int err = 0; dev = rt->u.dst.dev; iph = skb->nh.iph; //获取ip头 //设置了不分片标志 if (unlikely((iph->frag_off & htons(IP_DF)) && !skb->local_df)) { IP_INC_STATS(IPSTATS_MIB_FRAGFAILS); icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(dst_mtu(&rt->u.dst))); kfree_skb(skb); return -EMSGSIZE; } hlen = iph->ihl * 4; //ip头长度 mtu = dst_mtu(&rt->u.dst) - hlen; //没有ip头的mtu大小 IPCB(skb)->flags |= IPSKB_FRAG_COMPLETE; if (skb_shinfo(skb)->frag_list) { //如果skb已经被分片了 struct sk_buff *frag; int first_len = skb_pagelen(skb); //返回skb数据长度 //数据长度大于mtu 或 没有8字节对齐 或 ip头中标志了分片 或 skb被cloned if (first_len - hlen > mtu || ((first_len - hlen) & 7) || (iph->frag_off & htons(IP_MF|IP_OFFSET)) || skb_cloned(skb)) goto slow_path; //进入慢速路径 //循环每一个碎片 for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next) { //长度检查 if (frag->len > mtu || ((frag->len & 7) && frag->next) || skb_headroom(frag) < hlen) goto slow_path; /* Partially cloned skb? */ if (skb_shared(frag)) //碎片被共享 goto slow_path; BUG_ON(frag->sk); if (skb->sk) { //skb持有 sock结构 sock_hold(skb->sk); frag->sk = skb->sk; frag->destructor = sock_wfree; skb->truesize -= frag->truesize; //长度减少 } } /* Everything is OK. Generate! */ err = 0; offset = 0; frag = skb_shinfo(skb)->frag_list; //碎片头 skb_shinfo(skb)->frag_list = NULL; //skb 碎片头指针清空 skb->data_len = first_len - skb_headlen(skb); skb->len = first_len; //更新长度 iph->tot_len = htons(first_len); iph->frag_off = htons(IP_MF); //设置标志 ip_send_check(iph); //计算skb的校验和 for (;;) { /* Prepare header of the next frame, before previous one went down. */ if (frag) { //碎片 frag->ip_summed = CHECKSUM_NONE; frag->h.raw = frag->data; //传输头 frag->nh.raw = __skb_push(frag, hlen); //留出网络头空间 memcpy(frag->nh.raw, iph, hlen); //copy skb中的ip头到这个碎片中当作网络头 iph = frag->nh.iph; iph->tot_len = htons(frag->len); //更新长度 ip_copy_metadata(frag, skb); //copy skb中的一些其他字段的值到碎片 if (offset == 0) //第一个碎片 ip_options_fragment(frag); //构建选项 offset += skb->len - hlen; //更新偏移 iph->frag_off = htons(offset>>3); //ip头中保存偏移 if (frag->next != NULL) //还有下一个碎片 iph->frag_off |= htons(IP_MF); /* Ready, complete checksum */ ip_send_check(iph); //计算校验和 } err = output(skb);//发送数据包 if (!err) IP_INC_STATS(IPSTATS_MIB_FRAGCREATES); if (err || !frag) break; //循环到下一个 skb = frag; frag = skb->next; skb->next = NULL; } if (err == 0) { //正确 IP_INC_STATS(IPSTATS_MIB_FRAGOKS); return 0; } while (frag) { //发送不正确 skb = frag->next; kfree_skb(frag); frag = skb; } IP_INC_STATS(IPSTATS_MIB_FRAGFAILS); return err; } slow_path: //慢速路径 left = skb->len - hlen; //ip头后面数据长度 ptr = raw + hlen; // 0 + ip头长度 #ifdef CONFIG_BRIDGE_NETFILTER /* for bridged IP traffic encapsulated inside f.e. a vlan header, * we need to make room for the encapsulating header */ ll_rs = LL_RESERVED_SPACE_EXTRA(rt->u.dst.dev, nf_bridge_pad(skb)); mtu -= nf_bridge_pad(skb); #else ll_rs = LL_RESERVED_SPACE(rt->u.dst.dev); //预留头空间 #endif /* Fragment the datagram. */ offset = (ntohs(iph->frag_off) & IP_OFFSET) << 3; //碎片偏移 not_last_frag = iph->frag_off & htons(IP_MF); //是否是最后一个碎片 while(left > 0) { len = left; /* IF: it doesn't fit, use 'mtu' - the data space left */ if (len > mtu) //数据包长度 > mtu len = mtu; /* IF: we are not sending upto and including the packet end then align the next start on an eight byte boundary */ if (len < left) { len &= ~7; //8 字节对齐 } //分配碎片 if ((skb2 = alloc_skb(len + hlen + ll_rs, GFP_ATOMIC)) == NULL) { NETDEBUG(KERN_INFO "IP: frag: no memory for new fragment!\n"); err = -ENOMEM; goto fail; } ip_copy_metadata(skb2, skb); //copy skb中的其他数据到skb2 skb_reserve(skb2, ll_rs); //skb2->data += ll_rs 保留空间 skb_put(skb2, len + hlen); //设置skb长度 skb2->nh.raw = skb2->data; //指向网络头 skb2->h.raw = skb2->data + hlen; //指向传输头 if (skb->sk) //skb属于一个 sock skb_set_owner_w(skb2, skb->sk); memcpy(skb2->nh.raw, skb->data, hlen); //copy网络头 if (skb_copy_bits(skb, ptr, skb2->h.raw, len))//copy网络头后面的数据 长度是len,不超过mtu BUG(); left -= len; //总长度减少 iph = skb2->nh.iph; //获取ip头 iph->frag_off = htons((offset >> 3)); //设置碎片偏移 if (offset == 0) //第一个碎片,构建ip选项 ip_options_fragment(skb); if (left > 0 || not_last_frag) //还有碎片需要处理 iph->frag_off |= htons(IP_MF); ptr += len;//更新copy位置 offset += len;//偏移增加 iph->tot_len = htons(len + hlen); //更新 ip头中长度 ip_send_check(iph); //计算校验和 err = output(skb2); //发送数据包 if (err) goto fail; IP_INC_STATS(IPSTATS_MIB_FRAGCREATES); } kfree_skb(skb); IP_INC_STATS(IPSTATS_MIB_FRAGOKS); return err; fail: kfree_skb(skb); IP_INC_STATS(IPSTATS_MIB_FRAGFAILS); return err; }
posted on 2013-08-27 10:29 SuperKing 阅读(1378) 评论(0) 编辑 收藏 举报