2.3 IP分片
前文曾提到,当IP数据报的长度超过帧的MTU时,它将被分片传输。分片可能发生在发送端,也可能发生在中转路由器上,而且可能在传输过程中被多次分片,但只有在最终的目标机器上,这些分片才会被内核中的IP模块重新组装。
IP头部中的如下三个字段给IP的分片和重组提供了足够的信息:数据报标识、标志和片偏移。一个IP数据报的每个分片都具有自己的IP头部,它们具有相同的标识值,但具有不同的片偏移。并且除了最后一个分片外,其他分片都将设置MF标志。此外,每个分片的IP头部的总长度字段将被设置为该分片的长度。
以太网帧的MTU是1500字节(可以通过ifconfig命令或者netstat命令查看),因此它携带的IP数据报的数据部分最多是1480字节(IP头部占用20字节)。考虑用IP数据报封装一个长度为1481字节的ICMP报文(包括8字节的ICMP头部,所以其数据部分长度为1473字节),则该数据报在使用以太网帧传输时必须被分片,如图2-2所示。
图2-2中,长度为1501字节的IP数据报被拆分成两个IP分片,第一个IP分片长度为1500字节,第二个IP分片的长度为21字节。每个IP分片都包含自己的IP头部(20字节),且第一个IP分片的IP头部设置了MF标志,而第二个IP分片的IP头部则没有设置该标志,因为它已经是最后一个分片了。原始IP数据报中的ICMP头部内容被完整地复制到了第一个IP分片中。第二个IP分片不包含ICMP头部信息,因为IP模块重组该ICMP报文的时候只需要一份ICMP头部信息,重复传送这个信息没有任何益处。1473字节的ICMP报文数据的前1472字节被IP模块复制到第一个IP分片中,使其总长度为1500字节,从而满足MTU的要求;而多出的最后1字节则被复制到第二个IP分片中。
需要指出的是,ICMP报文的头部长度取决于报文的类型,其变化范围很大。图2-2以8字节为例,因为后面的例子用到了ping程序,而ping程序使用的ICMP回显和应答报文的头部长度是8字节。
为了看清楚IP分片的具体过程,考虑从ernest-laptop来ping机器Kongming20,每次传送1473字节的数据(这是ICMP报文的数据部分)以强制引起IP分片,并用tcpdump抓取这一过程中双方交换的数据包。具体操作过程如下:
$ sudo tcpdump -ntv -i eth0 icmp #只抓取ICMP报文
$ ping Kongming20 –s 1473 #用-s选项指定每次发送1473字节的数据
下面我们考察tcpdump输出的一个IP数据报的两个分片,其内容如下:
1. IP(tos 0x0, ttl 64, id 61197, offset 0, flags [+], proto ICMP (1), length 1500)
192.168.1.108 > 192.168.1.110: ICMP echo request, id 41737, seq 1, length 1480
2. IP(tos 0x0, ttl 64, id 61197, offset 1480, flags [none], proto ICMP (1), length 21)
192.168.1.108 > 192.168.1.110: icmp
这两个IP分片的标识值都是61197,说明它们是同一个IP数据报的分片。第一个分片的片偏移值为0,而第二个则是1480。很显然,第二个分片的片偏移值实际上也是第一个分片的ICMP报文的长度。第一个分片设置了MF标志以表示还有后续分片,所以tcpdump输出“flags[+]”。而第二个分片则没有设置任何标志,所以tcpdump输出“flags[none]”。这个两个分片的长度分别为1500字节和21字节,这与图2-2描述的一致。
最后,IP层传递给数据链路层的数据可能是一个完整的IP数据报,也可能是一个IP分片,它们统称为IP分组(packet)。本书如无特殊声明,不区分IP数据报和IP分组。