IP数据报分片-fragmentation 和 重组
在TCP/IP分层中,数据链路层用MTU(Maximum Transmission Unit,最大传输单元)来限制所能传输的数据包大小,MTU是指一次传送的数据最大长度,不包括数据链路层数据帧的帧头,如以太网的MTU为1500字节,实际上数据帧的最大长度为1512字节,其中以太网数据帧的帧头为12字节。
当发送的IP数据报的大小超过了MTU时,IP层就需要对数据进行分片,否则数据将无法发送成功。
MTU (最大传输单元)决定 IP报文是否分片。
上图IP报文格式
如图所示,IP协议理论上允许的最大IP数据报为65535字节(16位来表示包总长)。但是因为协议栈网络层下面的数据链路层一般允许的帧长远远小于这个值,例如以太网的MTU(即Maximum Transmission Unit,最大传输单元)通常在1500字节左右。所以较大的IP数据包会被分片传递给数据链路层发送,分片的IP数据报可能会以不同的路径传输到接收主机,接收主机通过一系列的重组,将其还原为一个完整的IP数据报,再提交给上层协议处理。上图中的红色字段便是被设计用来处理IP数据包分片和重组的。
那么,这三个字段如何实现对分片进行表示呢?
首先是标识符(16位),协议栈应该保证来自同一个数据报的若干分片必须有一样的值。
其次是标志位3位分别是R(保留位,未使用)位、DF(Do not Fragment,不允许分段)位和MF(More Fragment)位。MF位为1表示当前数据报还有更多的分片,为0表示当前分片是该数据报最后一个分片。
最后是偏移量(13位),表示当前数据报分片数据起始位置在完整数据报的偏移,注意这里一个单位代表8个字节。即这里的值如果是185,则代表该分片在完整数据报的偏移是185*8=1480字节。
操作系统内核协议栈(以下简称协议栈)只需要申请一块和原始数据报相同大小的内存空间,然后将这些数据报分片按照其偏移拷贝到指定的位置就能恢复出原先的数据报了。目前看起来一切都很清晰,不是么?但我的问题就出在这个判别数据报分片的方法上。因为标识符字段只有16位,所以理论上只有65536个不同的表示。当一台拥有着超过65536个活跃连接用户的服务器时,理论上会出现重复的数据报分片。即使连接的客户没这么多,但是从概率上如果只用这个标示符的话,依旧会出现可能造成混乱的数据报分片。
下面这行代码明确的指出了协议栈判断IP分片的依据:
hash = ipqhashfn(iph->id, iph->saddr, iph->daddr, iph->protocol);
ipqhashfn函数依靠(标示符、源地址、目标地址、协议)这个四元组来唯一的表示一个IP数据报分片,这就解决了单单依赖表示符无法确定一个数据报的问题。那么这个四元组怎么表示呢?查找的效率问题如何解决呢?答案就在ipqhashfn这个hash函数里,其代码如下(Linux-3.12.6)
static unsigned int ipqhashfn(__be16 id, __be32 saddr, __be32 daddr, u8 prot) { return jhash_3words((__force u32)id << 16 | prot, (__force u32)saddr, (__force u32)daddr, ip4_frags.rnd) & (INETFRAGS_HASHSZ - 1); }
在网络编程中,我们要避免出现IP分片,那么为什么要避免呢?原因是IP层是没有超时重传机制的 ,如果IP层对一个数据包进行了分片,只要有一个分片丢失了,只能依赖于传输层进行重传,结果是所有的分片都要重传一遍,这个代价有点大。由此可见,IP分片会大大降低传输层传送数据的成功率,所以我们要避免IP分片。
对于TCP协议,应用层就不需要考虑这个问题了,因为传输层已经帮我们做了。在建立连接的TCP三次握手的过程中,连接双方会相互通告MSS(Maximum Segment Size,最大报文段长度1460),MSS一般是MTU - IP首部(20) - TCP首部(20),每次发送的TCP数据都不会超过双方MSS的值,所以就保证了IP数据报不会超过MTU,避免了IP分片。
如果发送一段2000字节的TCP报文,那么会导致TCP分段,因为其超过了最大报文段的长度,一般是MTU - IP首部(20) - TCP首部(20)。
分段后的每一段TCP报文段再加上IP首部后的长度不可能超过MTU,因此也就不需要在网络层进行IP分片了。因此TCP报文段很少会发生IP分片的情况。
====================================================================
对于UDP包,我们需要在应用层去限制每个包的大小,一般不要超过1472字节,即以太网MTU(1500) - IP首部(20) - UDP首部(8)。
UDP数据报,由于UDP数据报不会自己进行分段,因此当长度超过了MTU时,会在网络层进行IP分片。
需要注意的,在分片的数据中,传输层的首部只会出现在第一个分片中,如下图所示。因为传输层的数据格式对IP层是透明的,传输层的首部只有在传输层才会有它的作用,IP层不知道也不需要保证在每个分片中都有传输层首部。所以,在网络上传输的数据包是有可能没有传输层首部的。
图:表示IP的分片
实例:从10.224.142.166向10.137.133.101发送3000字节的UDP数据,由于UDP数据报超过了MTU的大小,所以要在IP层分片。抓包的结果如下图。从图中可以看到这个UDP数据包被分成了3个IP片,从各IP分片的偏移量可以看出,3片包含的UDP数据大小分别是1480、1480、48(加上UDP首部8个字节),各分片加上IP首部的大小分别就是1500、1500、68,传送的总的UDP数据大小为3008,由此也看出只有一个分片包含UDP首部。
=====================================================================
接收方在收到经过IP层分片的数据报文后,首先根据分片标志中的 MF(More Fragment)位 (MF位为1表示当前数据报还有更多的分片,为0表示当前分片是该数据报最后一个分片。)判断是否是最后一个分片报文,如果是,则根据分片偏移量计算各个分片报文在原始数据报中的位置,进行重组。如果不是最后一个分片,则需等待所有分片到达后再完成重组。
分片带来的问题:
1.分片带来的性能消耗
分片和重组会消耗发送方、接收方一定的CPU等资源,如果存在大量的分片报文的话,可能会造成较为严重的资源消耗;
分片对接收方内存资源的消耗较多,因为接收方要为接收到的每个分片报文分配内存空间,以便于最后一个分片报文到达后完成重组。
2.分片丢包导致的重传问题
如果某个分片报文在网络传输过程中丢失,那么接收方将无法完成重组,如果应用进程要求重传的话,发送方必须重传所有分片报文而不是仅重传被丢弃的那个分片报文,这种效率低下的重传行为会给端系统和网络资源带来额外的消耗。
3.分片攻击
黑客构造的分片报文,但是不向接收方发送最后一个分片报文,导致接收方要为所有的分片报文分配内存空间,可由于最后一个分片报文永远不会达到,接收方的内存得不到及时的释放(接收方会启动一个分片重组的定时器,在一定时间内如果无法完成重组,将向发送方发送ICMP重组超时差错报文,,只要这种攻击的分片报文发送的足够多、足够快,很容易占满接收方内存,让接收方无内存资源处理正常的业务,从而达到DOS的攻击效果。
4.安全隐患
由于分片只有第一个分片报文具有四层信息而其他分片没有,这给路由器、防火墙等中间设备在做访问控制策略匹配的时候带来了麻烦。
如果路由器、防火墙等中间设备不对分片报文进行安全策略的匹配检测而直接放行IP分片报文,则有可能给接收方带来安全隐患和威胁,因为黑客可以利用这个特性,绕过路由器、防火墙的安全策略检查对接收方实施攻击;
如果路由器、防火墙等中间设备对这些分片报文进行重组后在匹配其安全策略,那么又会对这些中间设备的资源带来极大的消耗,特别是在遇到分片攻击的时候,这些中间设备会在第一时间内消耗完其所有内存资源,从而导致全网中断的严重后果。
引用:
http://www.cnblogs.com/glacierh/p/3653442.html
http://support.huawei.com/ecommunity/bbs/10161111.html
http://blog.csdn.net/ns_code/article/details/30109789
==========================END==========================