概述

ip_fragment函数用于判断是否进行分片,在没有设置DF标记的情况下进入分片,如果设置了DF标记,则继续判断,如果不允许DF分片或者收到的最大分片大于MTU大小,则回复ICMP,释放skb,其余情况仍然需要走分片;

ip_do_fragment是详细的分片流程,整个过程分为快速分片和慢速分片两种,如果存在分片列表frag_list,并且通过检查,则走快速路径,复制每个分片的ip头等信息之后,发送出去;如果不存在分片列表,或者分片列表检查失败,则走慢速路径,慢速路径会根据MTU大小,对整个数据进行重新划分,分配skb,进行数据拷贝,设置ip头等信息,然后发送出去;

源码分析
 static int ip_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
unsigned int mtu,
int (*output)(struct net *, struct sock *, struct sk_buff *))
{
struct iphdr *iph = ip_hdr(skb); /* 如果没有DF标记,则进行分片 */
if ((iph->frag_off & htons(IP_DF)) == )
return ip_do_fragment(net, sk, skb, output); /* 有DF标记则继续判断 */ /* 不允许本地分片 || 分片最大长度>MTU */
if (unlikely(!skb->ignore_df ||
(IPCB(skb)->frag_max_size &&
IPCB(skb)->frag_max_size > mtu))) {
IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS);
/* ICMP错误 */
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
htonl(mtu));
/* 释放skb */
kfree_skb(skb);
return -EMSGSIZE;
} /* 其他情况,继续分片 */
return ip_do_fragment(net, sk, skb, output);
}
 int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
int (*output)(struct net *, struct sock *, struct sk_buff *))
{
struct iphdr *iph;
int ptr;
struct sk_buff *skb2;
unsigned int mtu, hlen, left, len, ll_rs;
int offset;
__be16 not_last_frag;
struct rtable *rt = skb_rtable(skb);
int err = ; /* for offloaded checksums cleanup checksum before fragmentation */
/* PARTIAL类型需要清除校验和 */
if (skb->ip_summed == CHECKSUM_PARTIAL &&
(err = skb_checksum_help(skb)))
goto fail; /*
* Point into the IP datagram header.
*/ iph = ip_hdr(skb); /* 获取mtu */
mtu = ip_skb_dst_mtu(sk, skb); /* 接收到的最大分片长度 < mtu,则将mtu设置为该值 */
if (IPCB(skb)->frag_max_size && IPCB(skb)->frag_max_size < mtu)
mtu = IPCB(skb)->frag_max_size; /*
* Setup starting values.
*/ hlen = iph->ihl * ;
mtu = mtu - 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.
*/
/* 有分片列表 */
if (skb_has_frag_list(skb)) {
struct sk_buff *frag, *frag2; /* 线性区域和分页区的数据长度 */
unsigned int first_len = skb_pagelen(skb); /* 以下情况,进入慢路处理 */
if (first_len - hlen > mtu || /* 分片长度>MTU */
((first_len - hlen) & ) || /* 没有8字节对齐 */
ip_is_fragment(iph) || /* 是一个分片 */
skb_cloned(skb)) /* 是克隆的 */
goto slow_path; /* 遍历分片列表 */
skb_walk_frags(skb, frag) {
/* Correct geometry. */
/* 以下情况,恢复状态,进入慢速路径 */
if (frag->len > mtu || /* 分片长度>mtu */
((frag->len & ) && frag->next) || /* 除最后一个分片外,其余有非8字节对齐的 */
skb_headroom(frag) < hlen) /* 头部长度过小 */
goto slow_path_clean; /* Partially cloned skb? */
/* 克隆的,恢复状态,进入慢速路径 */
if (skb_shared(frag))
goto slow_path_clean; BUG_ON(frag->sk); /* 分片关联控制块 */
if (skb->sk) {
frag->sk = skb->sk;
frag->destructor = sock_wfree;
} /* 第一个skb的长度去掉当前分片的长度 */
skb->truesize -= frag->truesize;
} /* Everything is OK. Generate! */ /* 现在分片没问题了,设置分片信息 */
err = ;
offset = ;
frag = skb_shinfo(skb)->frag_list;
skb_frag_list_init(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); /* 循环设置分片信息,并发送 */
for (;;) {
/* Prepare header of the next frame,
* before previous one went down. */
/* 为每一片都拷贝ip头,设置偏移信息 */
if (frag) {
frag->ip_summed = CHECKSUM_NONE;
skb_reset_transport_header(frag);
__skb_push(frag, hlen);
skb_reset_network_header(frag);
memcpy(skb_network_header(frag), iph, hlen);
iph = ip_hdr(frag);
iph->tot_len = htons(frag->len);
ip_copy_metadata(frag, skb);
if (offset == )
ip_options_fragment(frag);
offset += skb->len - hlen;
iph->frag_off = htons(offset>>);
if (frag->next)
iph->frag_off |= htons(IP_MF);
/* Ready, complete checksum */
ip_send_check(iph);
} /* 调用发送回调 */
err = output(net, sk, skb); if (!err)
IP_INC_STATS(net, IPSTATS_MIB_FRAGCREATES);
if (err || !frag)
break; skb = frag;
frag = skb->next;
skb->next = NULL;
} if (err == ) {
IP_INC_STATS(net, IPSTATS_MIB_FRAGOKS);
return ;
} /* 出错,释放分片 */
while (frag) {
skb = frag->next;
kfree_skb(frag);
frag = skb;
}
IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS);
return err; slow_path_clean:
/* 将分片恢复原状态 */
skb_walk_frags(skb, frag2) {
if (frag2 == frag)
break;
frag2->sk = NULL;
frag2->destructor = NULL;
skb->truesize += frag2->truesize;
}
} slow_path:
/* 慢速分片路径 */ iph = ip_hdr(skb); /* 除去首部的剩余空间 */
left = skb->len - hlen; /* Space per frame */
ptr = hlen; /* Where to start from */ /* 二层头部空间 */
ll_rs = LL_RESERVED_SPACE(rt->dst.dev); /*
* Fragment the datagram.
*/ /* 初始化mf和offset */
offset = (ntohs(iph->frag_off) & IP_OFFSET) << ;
not_last_frag = iph->frag_off & htons(IP_MF); /*
* Keep copying data until we run out.
*/ /* 开始分片了 */
while (left > ) {
/* len初始为剩余长度 */
len = left;
/* IF: it doesn't fit, use 'mtu' - the data space left */
/* 根据mtu确认长度 */
if (len > mtu)
len = mtu;
/* IF: we are not sending up to and including the packet end
then align the next start on an eight byte boundary */
/* 除最后分片外,其余8字节对齐 */
if (len < left) {
len &= ~;
} /* Allocate buffer */
/* 分配skb */
skb2 = alloc_skb(len + hlen + ll_rs, GFP_ATOMIC);
if (!skb2) {
err = -ENOMEM;
goto fail;
} /*
* Set up data on packet
*/ /* 拷贝元数据 */
ip_copy_metadata(skb2, skb); /* 预留空间,设置头部偏移 */
skb_reserve(skb2, ll_rs);
skb_put(skb2, len + hlen);
skb_reset_network_header(skb2);
skb2->transport_header = skb2->network_header + hlen; /*
* Charge the memory for the fragment to any owner
* it might possess
*/
/* 关联sk */
if (skb->sk)
skb_set_owner_w(skb2, skb->sk); /*
* Copy the packet header into the new buffer.
*/ /* 拷贝头部 */
skb_copy_from_linear_data(skb, skb_network_header(skb2), hlen); /*
* Copy a block of the IP datagram.
*/
/* 拷贝数据 */
if (skb_copy_bits(skb, ptr, skb_transport_header(skb2), len))
BUG();
left -= len; /*
* Fill in the new header fields.
*/
iph = ip_hdr(skb2); /* 设置偏移 *//
iph->frag_off = htons((offset >> )); /* 转发的数据包,带有FRAG_PMTU标记,则打上DF */
if (IPCB(skb)->flags & IPSKB_FRAG_PMTU)
iph->frag_off |= htons(IP_DF); /* 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选项 */
if (offset == )
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 > || not_last_frag)
iph->frag_off |= htons(IP_MF); /* 指针和偏移更新 */
ptr += len;
offset += len; /*
* Put this fragment into the sending queue.
*/
/* 设置数据长度 */
iph->tot_len = htons(len + hlen); /* 校验和 */
ip_send_check(iph); /* 发送分片 */
err = output(net, sk, skb2);
if (err)
goto fail; IP_INC_STATS(net, IPSTATS_MIB_FRAGCREATES);
} /* 分片完成并发送,释放skb */
consume_skb(skb);
IP_INC_STATS(net, IPSTATS_MIB_FRAGOKS);
return err; fail: /* 出错,释放skb */
kfree_skb(skb);
IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS);
return err;
}

IP输出 之 分片ip_fragment、ip_do_fragment的更多相关文章

  1. 转自:Tsihang 三层网络设备对于IP报文的分片和重组处理原理

    三层网络设备对于IP报文的分片和重组处理原理 对于网络分片,我一年前就想整理出来,虽然说网络上的资料很多,但是真正掌握精髓的除非真正做过分片程序,不然很难将协议栈整体联系起来理解.这篇文章,包括设计分 ...

  2. TCP层的分段和IP层的分片之间的关系 & MTU和MSS之间的关系 (转载)

    首先说明:数据报的分段和分片确实发生,分段发生在传输层,分片发生在网络层.但是对于分段来说,这是经常发生在UDP传输层协议上的情况,对于传输层使用TCP协议的通道来说,这种事情很少发生. 1,MTU( ...

  3. 原 TCP层的分段和IP层的分片之间的关系 & MTU和MSS之间的关系

    首先说明:数据报的分段和分片确实发生,分段发生在传输层,分片发生在网络层.但是对于分段来说,这是经常发生在UDP传输层协议上的情况,对于传输层使用TCP协议的通道来说,这种事情很少发生. 1,MTU( ...

  4. IP分组和分片

    本文讨论两个问题①IP数据报的首部②IP数据报的分片 TCP/IP模型分为五层,从上到下依次是应用层.传输层.网络层.数据链路层和物理层. IP数据报是网络层的概念. IP数据报的首部 版本号:0~3 ...

  5. IP输出 之 ip_output、ip_finish_output、ip_finish_output2

    概述 ip_output-设置输出设备和协议,然后经过POST_ROUTING钩子点,最后调用ip_finish_output: ip_finish_output-对skb进行分片判断,需要分片,则分 ...

  6. TCP/IP协议原理与应用笔记26:网际协议(IP)之 分片(Fragmentation)

    1. 分片(Fragmentation) 适应在不同的MTU的物理网上传输. 备注: MTU:最大传输单元,Maximum Transmission Unit,它是指一种通信协议的某一层上面所能通过的 ...

  7. TCP/IP协议栈 --- 网络层(IP 首部 和分片)

    IP 是TCP/IP协议栈中重要的层次, TCP UDP ICMP IGMP都是依赖IP层进行传输的.首先它是一种不可靠,无连接的协议.不可靠:它不保证IP包能正确到达目的地,无连接:表示IP并不会维 ...

  8. IP输出 之 ip_local_out

    概述 将要从本地发出的数据包,会在构造了ip头之后,调用ip_local_out函数,该函数设置数据包的总长度和校验和,然后经过netfilter的LOCAL_OUT钩子点进行检查过滤,如果通过,则调 ...

  9. Windows 网络监测ping IP输出时间

    本文出自:https://www.cnblogs.com/2186009311CFF/p/9489374.html 持续监测网络,打印时间的方法,不足没有精确到毫秒 vbs文件内容如下 Dim arg ...

随机推荐

  1. RGB2GRAY 各种算法速度比较,整形乘法比查表法快!

    1.  查表法,外循环用 这种格式 :  //for(int j = 0; j != h; ++j)// for(int i = 0; i!=w;++i)//. for(int j = 0; j != ...

  2. Windows地址栏的妙用

    主角: 它就是windows自带的一个小工具->地址栏,可以通过在任务栏右键选择工具栏-地址栏添加使用. 妙用: 一.打开文件 使用方法:D:\Temp(文件路径) 小提示:快速进入回收站:Re ...

  3. Qt常用的登录界面设计

    记录一下Qt常用的登录界面的设计 方便以后使用! 1.QpushButton改变一个按钮的颜色,当鼠标放上去和移开时显示不同的颜色.QPushButton { background-color: rg ...

  4. JavaWeb【七、JSP状态管理】

    http协议无状态性 当提交请求,服务器返回响应.但当同一用户同一浏览器再次提交请求,服务器并不知道与刚才的请求是同一用户浏览器发起. 保存用户状态的两大机制 Session-保存在服务器端 Cook ...

  5. 7、Nginx基础Http原理

    1Http协议概述 HTTP全称HyperText Transfer Protocol中文名为超文本传输协议 1.1.什么是超文本? 包含有超链接(Link)和各种多媒体元素标记的文本.这些超文本文件 ...

  6. 前端入门Js笔记

    T 001 ____________--信息页面展示 需求分析: 有一个页面,在页面上有很多文字信息,且格式不一. 技术分析: html: 文字标签: 字体标签: 标题标签: 其他标签: 排版标签: ...

  7. postman 接口测试(一)

    一.postman 应用场景 开发接口快速的调用接口,以便调试 方便的调用接口,通过不同的参数去测试接口的输出 这些接口调用时需要保存下来的反复运行的 在运行中如果有断言(检查点 <预期 和现实 ...

  8. Tourist's Notes CodeForces - 538C (贪心)

    A tourist hiked along the mountain range. The hike lasted for n days, during each day the tourist no ...

  9. 第六章 组件 59 组件切换-使用Vue提供的component元素实现组件切换

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8&quo ...

  10. Acwing-167-木棒(搜索, 剪枝)

    链接: https://www.acwing.com/problem/content/169/ 题意: 乔治拿来一组等长的木棒,将它们随机地砍断,使得每一节木棍的长度都不超过50个长度单位. 然后他又 ...