- 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, pad;
- int offset;
- __be16 not_last_frag;
- /* 取得路由表 */
- struct rtable *rt = skb->rtable;
- int err = 0;
- /* 网络设备 */
- dev = rt->u.dst.dev;
- /*
- * Point into the IP datagram header.
- */
- /* 取得ip头 */
- iph = ip_hdr(skb);
- /*
- * 判断DF位,知道如果df位被设置了话就表示不要被分片,
- * 这时ip_fragment将会发送一个icmp报文返回到源主机。这里
- * 主要是为forward数据所判断。
- */
- if (unlikely((iph->frag_off & htons(IP_DF)) && !skb->local_df)) {
- IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGFAILS);
- icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
- htonl(ip_skb_dst_mtu(skb)));
- kfree_skb(skb);
- return -EMSGSIZE;
- }
- /*
- * Setup starting values.
- */
- /* 得到ip头的长度 */
- hlen = iph->ihl * 4;
- /*
- * 得到mtu的大小。这里要注意,他的大小减去了hlen,也就是ip头的大小
- */
- mtu = dst_mtu(&rt->u.dst) - hlen; /* Size of data space */
- IPCB(skb)->flags |= IPSKB_FRAG_COMPLETE;
- /* When frag_list is given, use it. First, check its validity:
- * some transformers could create wrong frag_list or break existing
- * one, it is not prohibited. In this case fall back to copying.
- *
- * LATER: this step can be merged to real generation of fragments,
- * we can switch to copy when see the first bad fragment.
- */
- /*
- * 如果4层将数据包分片了,那么就会把这些数据包放到skb的frag_list链表中,
- * 因此这里首先先判断frag_list链表是否为空,为空的话将会进行slow 分片
- */
- if (skb_shinfo(skb)->frag_list) {
- struct sk_buff *frag;
- /*
- * 取得第一个数据报的len.当sk_write_queue队列被flush后,
- * 除了第一个切好包的另外的包都会加入到frag_list中,而这里
- * 需要得到的第一个包(也就是本身这个sk_buff)的长度。
- */
- int first_len = skb_pagelen(skb);
- int truesizes = 0;
- /*
- * 接下来的判断都是为了确定能进行fast分片。分片不能被共享,
- * 这是因为在fast path 中,需要加给每个分片不同的ip头(而并
- * 不会复制每个分片)。因此在fast path中是不可接受的。而在
- * slow path中,就算有共享也无所谓,因为他会复制每一个分片,
- * 使用一个新的buff。
- */
- /*
- * 判断第一个包长度是否符合一些限制(包括mtu,mf位等一些限制).
- * 如果第一个数据报的len没有包含mtu的大小这里之所以要把第一个
- * 切好片的数据包单独拿出来检测,是因为一些域是第一个包所独有
- * 的(比如IP_MF要为1)。这里由于这个mtu是不包括hlen的mtu,因此
- * 需要减去一个hlen。
- */
- if (first_len - hlen > mtu ||
- ((first_len - hlen) & 7) ||
- (iph->frag_off & htons(IP_MF|IP_OFFSET)) ||
- skb_cloned(skb))
- goto slow_path;
- /* 遍历剩余的frag */
- for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next) {
- /* Correct geometry. */
- /*
- * 判断每个帧的mtu,以及相关的东西,如果不符合条件则要
- * 进行slow path,基本和上面的第一个skb的判断类似
- */
- 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);
- /* 进行socket的一些操作 */
- if (skb->sk) {
- sock_hold(skb->sk);
- frag->sk = skb->sk;
- frag->destructor = sock_wfree;
- truesizes += frag->truesize;
- }
- }
- /* Everything is OK. Generate! */
- /* 通过上面的检测,都通过了,因此可以进行fast path分片了 */
- err = 0;
- offset = 0;
- /* 取得frag_list列表 */
- frag = skb_shinfo(skb)->frag_list;
- skb_shinfo(skb)->frag_list = NULL;
- /* 得到数据(不包括头)的大小 */
- skb->data_len = first_len - skb_headlen(skb);
- skb->truesize -= truesizes;
- skb->len = first_len;
- iph->tot_len = htons(first_len);
- /* 设置mf位 */
- iph->frag_off = htons(IP_MF);
- ip_send_check(iph);
- /* 开始进行发送 */
- for (;;) {
- /* Prepare header of the next frame,
- * before previous one went down. */
- if (frag) {
- frag->ip_summed = CHECKSUM_NONE;
- /* 设置相应的头部 */
- skb_reset_transport_header(frag);
- /* 预留ddos header 空间 */
- // hlen += DDOS_HDR_LEN;
- __skb_push(frag, hlen);
- skb_reset_network_header(frag);
- // hlen -= DDOS_HDR_LEN;
- /* 复制ip头 */
- memcpy(skb_network_header(frag), iph, hlen);
- iph = ip_hdr(frag);
- iph->tot_len = htons(frag->len);
- /* 将当前skb的一些属性付给将要传递的分片好的帧 */
- ip_copy_metadata(frag, skb);
- /* 处理ip_option */
- if (offset == 0)
- ip_options_fragment(frag);
- offset += skb->len - hlen;
- /* 设置位移 */
- iph->frag_off = htons(offset>>3);
- 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(dev_net(dev), IPSTATS_MIB_FRAGCREATES);
- if (err || !frag)
- break ;
- /* 处理链表中下一个buf */
- skb = frag;
- frag = skb->next;
- skb->next = NULL;
- }
- if (err == 0) {
- IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGOKS);
- return 0;
- }
- /* 释放内存 */
- while (frag) {
- skb = frag->next;
- kfree_skb(frag);
- frag = skb;
- }
- IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGFAILS);
- return err;
- }
- slow_path:
- /* 分片的数据剩余长度 */
- left = skb->len - hlen; /* Space per frame */
- /* 而ptr就是分片开始的数据指针 */
- ptr = raw + hlen; /* Where to start from */
- /* for bridged IP traffic encapsulated inside f.e. a vlan header,
- * we need to make room for the encapsulating header
- */
- /* 处理桥接、VLAN、PPPOE相关MTU */
- pad = nf_bridge_pad(skb);
- ll_rs = LL_RESERVED_SPACE_EXTRA(rt->u.dst.dev, pad);
- mtu -= pad;
- /*
- * Fragment the datagram.
- */
- /* 取出ip offset域 */
- offset = (ntohs(iph->frag_off) & IP_OFFSET) << 3;
- /* not_last_frag表明这个帧是否是最后一个分片 */
- not_last_frag = iph->frag_off & htons(IP_MF);
- /*
- * Keep copying data until we run out.
- */
- /* 开始为循环处理,每一个分片创建一个skb buffer */
- while (left > 0) {
- len = left;
- /* IF: it doesn't fit, use 'mtu' - the data space left */
- /* 如果len大于mtu,设置当前的将要分片的数据大小为mtu */
- if (len > 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;
- }
- /*
- * Allocate buffer.
- */
- /* malloc一个新的buff,它的大小包括ip payload,ip head,以及L2 head */
- 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;
- }
- /*
- * Set up data on packet
- */
- /* 调用ip_copy_metadata复制一些相同的值的域 */
- ip_copy_metadata(skb2, skb);
- /* 保留L2 header空间 */
- skb_reserve(skb2, ll_rs);
- /* 设置ip header & ddos header & ip paylod 空间 */
- skb_put(skb2, len + hlen);
- skb_reset_network_header(skb2);
- /* L4 header指针为ip header + ddos header数据偏移位置,用于复制原始payload */
- skb2->transport_header = skb2->network_header + hlen;
- /*
- * Charge the memory for the fragment to any owner
- * it might possess
- */
- /* 将每一个分片的ip包都关联到源包的socket */
- if (skb->sk)
- skb_set_owner_w(skb2, skb->sk);
- /*
- * Copy the packet header into the new buffer.
- */
- /* 拷贝ip header */
- skb_copy_from_linear_data(skb, skb_network_header(skb2), hlen);
- /*
- * Copy a block of the IP datagram.
- */
- /* 拷贝ip payload数据 */
- if (skb_copy_bits(skb, ptr, skb_transport_header(skb2), len))
- BUG();
- /* 分片的数据剩余长度 */
- left -= len;
- /*
- * Fill in the new header fields.
- */
- /* 填充相应的ip头 */
- iph = ip_hdr(skb2);
- iph->frag_off = htons((offset >> 3));
- /* ANK: dirty, but effective trick. Upgrade options only if
- * the segment to be fragmented was THE FIRST (otherwise,
- * options are already fixed) and make it ONCE
- * on the initial skb, so that all the following fragments
- * will inherit fixed options.
- */
- /* 第一个包,因此进行ip_option处理 */
- if (offset == 0)
- ip_options_fragment(skb);
- /*
- * Added AC : If we are fragmenting a fragment that's not the
- * last fragment then keep MF on each bit
- */
- /* 不是最后一个包,因此设置mf位 */
- if (left > 0 || not_last_frag)
- iph->frag_off |= htons(IP_MF);
- /* 移动数据指针以及更改数据偏移 */
- ptr += len;
- offset += len;
- /*
- * Put this fragment into the sending queue.
- */
- /* 增加ddos header 长度 */
- // hlen += DDOS_HDR_LEN;
- /* 更新包头的数据长度 */
- iph->tot_len = htons(len + hlen);
- /* 重新计算校验 */
- ip_send_check(iph);
- /* 复位ip header大小 */
- // hlen -= DDOS_HDR_LEN;
- err = output(skb2);
- if (err)
- goto fail;
- IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGCREATES);
- }
- kfree_skb(skb);
- IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGOKS);
- return err;
- fail:
- kfree_skb(skb);
- IP_INC_STATS(dev_net(dev), IPSTATS_MIB_FRAGFAILS);
- return err;
- }
linux内核ip分片函数ip_fragment解析(转贴)
最新推荐文章于 2021-08-14 11:09:33 发布