DIY TCP/IP IP模块和ICMP模块的实现4

上一篇:DIY TCP/IP IP模块和ICMP模块的实现3
8.6 IP分片
在介绍DIY TCP/IP接收IP分片的代码实现之前,本节先通过wireshark抓包来分析,如何判断一个IP数据帧是一个IP分片,如何判断分片的开始和结束,收到IP分片后如何重组分片。通过wireshark抓取指定长度(超过MTU 1500字节)的PING数据帧过程,便于描述,将该过程称为Large Packet PING。本人所在的局域网内,有一台windows主机B (192.168.0.105),之前章节的测试方法已经反复用到该主机,在主机B上指定PING的参数l,使ICMP Echo (Ping) Request携带的数据超过1500字节,主机B PING局域网内另外一台手机设备C (192.168.0.111),wireshark抓取完整的Large Packet Ping的过程。
主机B 上Large Packet PING的Log
DIY TCP/IP IP模块和ICMP模块的实现4
window命令行ping的参数l,用于指定Echo Ping Request数据部分的长度,在ubuntu 主机上该参数是s。朋友们自己实现时根据使用的操作系统命令行,通过ping –help查看相应设置Echo Ping Request数据部分长度的参数。主机B PING主机C(192.168.0.111)时,设置ping –l 5000,从运行结果也可以看出,主机B发出了4个Echo Ping Request数据帧,每个Echo Ping Reueqst携带的数据长度为5000字节,且均得到了主机C的回复,再来看wireshark在主机B上抓取Large Packet Ping的交互过程。
DIY TCP/IP IP模块和ICMP模块的实现4
DIY TCP/IP IP模块和ICMP模块的实现4
上图可以看出主机B发出的每个ICMP Echo Ping Requst和主机C回复的ICMP Echo Ping Reply都被分成了4个IP分片,每一组Large Packet Ping均有8个数据帧的交互。以第一组Large Packet PING为例,主机B发出的ICMP Echo Ping Requset被分成了4个以太网数据帧,前三个长度为1514(包括以太网头部长度14字节),这三个以太网数据帧被wireshark解析为IP分片。最后一个以太网数据帧长度为602,被wireshark解析为ICMP Echo(Ping) Rquest。不能只看wireshark解析的结果,wireshark已经做了IP分片的重组,将最后一个602字节的以太网数据帧解析为ICMP Echo Ping Request,数据长度为5000,ICMP的sequence number是0xb。
以太网的最大协议传输单元为1500字节,不包括以太网头部长度,1514减去14个字节的以太网头部,长度为1500,再减去20个字节的IP头部,剩下的数据部分是Echo Ping Request的数据1480字节,3*1480为4440。长度为602的以太网数据帧,同样减去14个字节的以太网头部,20个字节的IP头部,长度为568,4440 + 568 = 5008,第一个IP分片携带了8个字节的ICMP头部数据,再减去8个字节,刚好是主机B上指定的Echo Ping Request携带的数据的长度5000字节。读者可以按照该计算方法同样计算出ICMP Echo Ping Reply携带的数据长度为5000字节。将上图中红色矩形标记的3个1514字节的以太网数据帧,和一个长度为602字节的以太网数据帧展开。
DIY TCP/IP IP模块和ICMP模块的实现4
第一个以太网数据帧展开后,IP头部中的total_len为1500字节,包括20个字节的IP头部,表明该IP数据帧携带的数据长度为1480字节。从flags字段可以看出,该IP数据帧的Don’t fragment Bit没有被设置,More fragement Bit被设置为1,表明该IP数据帧是一个IP分片,且不是最后一个IP分片,fragment offset为0,表面该分片携带的数据在整个数据部分的偏移量为0,是第一个分片数据。IP头部的identification为0x1572,属于同一个IP数据帧的所有IP分片的identification值相等。IP头部中的上层协议字段为0x01,表明该IP分片携带的数据是ICMP数据帧的部分数据,查看数据部分的前8个字节,可以看出IP 头部后面8个字节是ICMP头部,0x08 0x00 0x41 0x44 0x00 0x01 0x00 0x0b,0x08是ICMP头部的type,代表Echo Ping Request,0x00是ICMP code,0x41 0x44是大端格式的ICMP头部校验和,参与该ICMP 头部的校验和字段的计算的数据,包括8个字节的ICMP头部,和5000字节的Echo Ping Request的数据,0x00 0x01是大端格式的ICMP Identification,0x00 0x0b是大端格式的ICMP Sequence Number。
将第二个,第三个以太网数据帧展开
DIY TCP/IP IP模块和ICMP模块的实现4
同样IP首部中的total length字段为1500,该IP数据帧也是携带1480字节的数据,identificaiton是0x1572,与第一个IP分片的identificaiton相等,Don’t fragment 为0,more fragment为1,表明该IP数据帧同样也是一个IP分片,且不是最后一个分片,identificaton表明,该IP分片与第一个IP分片同属于一个IP数据帧。分片的数据偏移为185,8.1节已经介绍过,分片数据偏移是以8位单位的,185 * 8得到分片数据的偏移量为1480,刚好是第一个IP分片的数据部分的长度。IP首部中的上层协议字段为0x01,1480字节的数据只包含ICMP Echo Ping Rquest的部分数据,不包含ICMP头部。
DIY TCP/IP IP模块和ICMP模块的实现4
第三个以太网数据帧展开后,查看IP头部,可以看到identification与第1,2个以太网数据帧相等,don’t fragment为0,more fragment为1,该IP数据帧也是一个IP分片,与第1,2个IP分片属于同一个IP数据帧,携带的数据为1480,分片的数据偏移量是2960,是前两个分片数据长度之和和,上层协议类型为0x01,同样不包含ICMP头部数据。
DIY TCP/IP IP模块和ICMP模块的实现4
第四个以太网数据帧展开后,查看IP头部,identificaiton与前三个IP分片相等,不同的是,more fragment bit为0,fragment offset 为555 * 8 = 4440,表明该IP数据帧是identification为0x1572的IP数据帧的最后一个IP分片,分片的数据偏移量是前三个分片的数据长度之和1480 * 3,上层协议类型是0x01。在IP头部后面看到的ICMP的头部是wireshark将IP分片重组后的显示结果,实际上该头部数据是在第一个IP分片携带的数据中。
IP分片的重组规则总结如下:
IP首部的identification相等的前提下,如果IP首部中more fragment为1且fragment offset为0,则说明该IP数据帧是第一个IP分片;如果more fragment为0且fragment offset不为0,则说明是最后一个IP分片;如果more fragment 为1且fragment offset不为0,则说明是中间的IP分片。DIY TCP/IP的IP模块收到IP分片后需根据IP头部中的分片偏移量,将分片数据重组在对应的位置。
下一篇:DIY TCP/IP IP模块和ICMP模块的实现5