日本工作的拼劲常常被大家津津乐道,不过实地接触后才真正感觉拼的无以复加了,这边有一个同事据说一天只吃一顿量大的晚餐,然后据观察每晚最后一封邮件都基本凌晨1点之后发出,然后每天第一封邮件早晨5点多发出,也就是说中间睡眠4个小时不到,而有的时候甚至早晨4点多就有邮件发出。
跟国内同事聊此,大家调侃着应该是做了定时任务吧,还有同事说这应该是北辰一刀流体系的人员,忍术修习到了一定阶段了吧。
果然这是一个比较疯狂的国度,管中窥豹略见一斑吧。
说了些杂七杂八的东西,接下来正题,前几天聊完了IPv4的分片,今天就聊聊IPv6的分片和重组情况。
首先来看一下RFC2460中定义的如下IPv6 Header Format:


Note: 在最新的定义中Type 59——No Next Header不再作为Extension Header出现。
首先IPv6和IPv4不同的是IPv6只允许在源节点分片和目的节点重组,中间节点路由器只做转发,不再对IPv6数据包重组或再次分片,当收到的分片数据包依然大于PMTU(Path MTU Discovery)的时候,给源端发送ICMPv6的Packet Too Big消息来告知其MTU,消息体如下:

而对于IPv4来说,中间节点路由器可以针对分片消息进行重组和重新分片等操作。
对比IPv4和IPv6的Header Format可以看出,IPv6中包头中移除了IPv4中Fragment的相关位如Identifier(16)、Flags(3)、Framented Offset(13)。
IPv6中引入了扩展包头(Extension Headers),而Fragment Header作为其中的一个类别表示了IPv6中的分片信息。
IPv4中分片之后依然使用IPv4中的Total Length,并且Total Length包含了IPv4的包头长和数据净长度。
IPv6中分片之后依然使用Payload Length这个字段,但是此字段不包括IPv6的包头长,但是却包括扩展包头Fragment Header的长度和数据净长度,下边就来聊聊Fragment Header。
首先在IPv6的Header中Next Header值需要为Fragment Header for IPv6(44)。
根据RFC2460中的定义,Fragment Header格式如下:

其中:
当源节点决定发送一个数据包,并且大于其设定的MTU时,需要对数据进行分片之后再发送。
此时,源数据包可分为如下两部分:


然后源节点开始进行构造各个分片数据包并发送到目的地:

其中每一个分片数据包由如下部分构成:
Note: 由于中间节点路由器不针对分片数据包重组和再分片,所以源节点的MTU最好定义为所有节点的MTU最小值。
当目的节点收到各个分片数据包,通过源和目的地址、Identification、Fragment Offset和M Flag进行连接得到重组数据包:

重组后的数据包的不可分片部分(Unfragmentable Part)包括如下部分:
1. 第一个分片包的IPv6包头:其中需要移除Fragment Header部分
2. 最后一个Next Header为第一个分片的Fragment Header中的Next Header;
3. Payload Length可以按照如下的方式计算:
PL.orig = PL.first - FL.first - 8 + (8 * FO.last) + FL.last
其中:
PL.orig = 组合包的走长度就是分片之前源数据包的;
PL.first = 第一个分片包的Payload Length;
FL.first = 第一个分片包的Fragment Header之后的数据长度;
FO.last = 最后一个分片中的Fragment Header内的Fragment Offset;
FL.last = 最后一个分片包Fragment Header之后的数据长度。
然后个人根据观察和总结可以使用如下公式:
PL.orig = 8 * FO.last+ (PL.last-8)
PL.last = 最后一个分片包的Payload Length。例如有一个定义了IPv6的节点需要发送Payload Length=1764的数据给另一个IPv6的终节点,要经过一个使用默认MTU=1500的路由器:
由于Payload Length加上IPv6 Header Length一共长1804大于MTU 1500,所以当数据包到达路由器时,由于MTU限制和IPv6只有源和目的节点可以分组数据包,因此路由器需要通过ICMPv6且Type为Package Too Big(2)告知源节点,并且告知源节点目前MTU为1500。
源节点收到Packge Too Big的ICMPv6消息后根据MTU开始进行分片:
由于MTU是1500,应该包含发送数据中的IPv6包头,因此可以发送的Payload Length为1460=1500-40,此时1460/8=182.5不是8的整数倍,所以向下四位取1456/8=182是8的整数倍,所以可以发送最大的Payload Length为1456。
这样分片之后的数据包如下:
这样,新的两个分片数据包可以通过路由器完美到达目的地。
例子中加深对偏移量的理解:偏移量(Offset)的计算需要数据净长度除以8得到,因此偏移量为原始数据包Payload Length的偏移位数。
目的节点收到了如下三个数据包:
那么源数据包的Payload Length是多少?从源到目的地中最小MTU是多少呢?
首先,根据三个分片包的Identification可以判断出这三个数据包同属一个源数据包,再由Offset和M Flag可以判断其连续性。
这样,路径中最小的MTU就为第一个分片包的Payload Length加上IPv6 Header Length得到:MTU=1392+40=1432。
那么原始数据包的Payload Length是多少呢?由公式如下公式可以计算:
PL.orig = 8 * FO.last+ (PL.last-8)
PL.orig=8*346+(228-8)=2988所以原始数据包的Payload Length为2988,即目的节点重组后得到Payload Length为2988.
以上就是IPv6分片的内容。