最近学习网络层协议的时候,注意到了IP协议中数据包分片的问题。下图是IP协议头的数据字段的示意: 如图所示,IP协议理论上允许的最大IP数据报为65535字节(16位来表示包总长)。但是因为协议栈网络层下面的数据链路层一般允许的帧长远远小于这个值,例如以太网的MTU(即Maximum Transmission Unit,最大传输单元)通常在1500字节左右。
如果IP层有数据包要传,而且数据包的长度超过了MTU,那么IP层就要对数据包进行分片(fragmentation)操作,使每一片的长度都小于或等于MTU。我们假设要传输一个UDP数据包,以太网的MTU为1500字节,一般IP首部为20字节,UDP首部为8字节,数据的净荷(payload)部分预留是1500-20-8=1472字节。如果数据部分大于1472字节,就会出现分片现象。
当提交给数据链路层进行传送时,一个IP分片或一个很小的无需分片的IP数据报称为分组。数据链路层在分组前面加上它自己的首部,并发送得到的帧。
标识符(16位)
Identification:发送端发送的IP数据包标识字段都是一个唯一值,该值在分片时被复制到每个片中。
标志(3位)
(1)R:保留未用。
(2)DF:Don't Fragment,“不分片”位, DF设置为0,表示可以分片,如果置1 ,IP层将不对数据报分片。
(3)MF:More Fragment,“更多的片”, 除了最后一片置0外,其他每个组成数据报的片都要置1。
偏移量(13位)
Fragment Offset:该片偏移原始数据包开始处的位置。偏移的字节数是该值乘以8。
两个Flags和Fragment Offset结合使用
如果MF为1而 Fragment Offset = 0,表示该IP报文为第一个分片,而且后续有分片;
如果MF为1而Fragment Offset不是0,表示该IP报文为中间的一个分片;
如果MF为0而Fragment Offset不是0,表示该报文是最后一个分片。
另外,当数据报被分片后,每个片的总长度值要改为该片的长度值。
例如,假设分片前报文IHL=5, Total Length = 800, MF = 0, Fragment Offset = 0, MTU为512;
注:IHL(Internet Header Length 报头长度),位于IP报文的第二个字段,4位, 单位是4字节,也即报文头的长度等于IHL的值乘以4,所以4*5=20 字节;
则分片
报文1的IHL1 = 5, Total Length1 = 508, MF = 1, Fragment Offset1 = 0;
报文2的IHL2 = 5, Total Length2 = 312, MF = 0, Fragment Offset2 = 61。
由于偏移量的单位为8Byte,所以非尾片的净荷长度都是8Byte的整数倍,MTU为512Byte,去除IP头IHL*4 = 5*4 = 20Byte后得到512-20 = 492Byte, 492Byte不是8Byte的整数倍,就取488Byte,加上IP头得到488+20 = 508Byte。
由于报文1的净荷为488Byte,所以报文2的Fragment Offset就是 488/8 = 61,
Total Length2 为总净荷长度(Total Length -IHL*4 = 800-20 = 780Byte)
减去报文1的净荷长度488Byte加上报文2的IP头20Byte得到780-488+20 = 312Byte。
例如: 假设分片后的两个报文为:报文1的IHL = 5, Total Length1 = 1012 , MF = 1, Fragment Offset1 = 0;
报文2的IHL = 5, Total Length2 = 312, MF = 0, Fragment Offset2 = 124。
则重组
报文IHL = 5, Total Length = 1304, MF = 0, Fragment Offset = 0。
其中报文1的净荷长度为Total Length1 – IHL1*4 = 1012-20 = 992Byte,
而报文2的 Fragment Offset2 = 124,则偏移量为124 * 8 = 992Byte,所以报文1和报文2是相连的,并且MF = 0 表示报文2为最后一个分片,此时就得到了原来的报文重组完成,重组后的报文 MF = 0, Fragment Offset = 0,报文2的净荷长度(Total Length2 –IHL*4 = 312 - 20 = 292Byte)加上IP报文头20Byte为992 + 292 + 20 = 1304Byte。