一、IP 协议简述
IP协议(Internet Protocol),又称之为网际协议,IP协议处于IP层工作,它是整个TCP/IP协议栈的核心协议之一,上层协议都要依赖IP协议提供的服务,IP协议负责将数据报从源主机发送到目标主机,通过IP地址作为唯一识别码,简单来说,不同主机之间的IP地址是不一样的,在发送数据报的过程中,IP协议还可能对数据报进行分片处理,同时在接收数据报的时候还可能需要对分片的数据报进行重装等等。注意:分片仅在IPv4中实现,而IPv6是没有分片的。
二、IP 协议格式
从上图可以看出,IP 首部中包含了非常多的信息,包括版本、协议、IP 地址等。上边的每一行都是 32 个比特位,也就是 4 个字节,前边五行是必须存在的内容,第六行是可有可无的,第七行是 IP 数据。因此对 IP 首部来说,至少会有 20 个字节的长度
- IP 首部中每一部分的含义
- 版本:占 4 位,指的是 IP 协议的版本,通信双方的版本必须一致,当前主流版本是 4,即 IPv4,也有 IPv6
- 首部长度:单位是 4 个字节,length * 4的字节数,因为这一字段共4个比特位,所以这一字段最大值为2 ^ 4 - 1即15,所以IP首部最大长度为15 * 4即60字节;在默认情况下,该字段被设置为5,所以默认IP首部20字节。
- 服务类型:这个一般是不需要关心的
- 总长度:占 16 位,最大数值为 65535,表示的是 IP 数据报总长度(IP 首部+IP 数据) (在前边介绍数据链路层的时候,也提到过一个长度。对于数据链路层的长度,称之为 MTU,一般为 1500 字节。而 IP 数据报的最大长度有 65535 个字节,比 MTU 要大。如果真正传输的时候,如果出现这种情况,数据链路层会对 IP 数据报进行分片,也就是将一个较长的 IP 数据报拆分成多个数据帧来进行传输)
- 标识:协议内部所使用的,无需关心
- 标记:标记 IP 报文是否可以进行分片
- 片偏移:前边有提到,如果 IP 数据报的长度过长,会进行 IP 报文的分片,把一个 IP 报文拆分成多个数据帧进行数据链路层的传输。因此,如果拆分的话,就需要使用片偏移来记录当前的数据帧,保存的第几个偏移的 IP 数据
- TTL:占 8 位,表明 IP 数据报文在网络中的寿命,每经过一个设备(不管是路由器还是计算机),TTL 减一,当 TTL=0 时,网络设备必须丢弃该报文(它解决的就是,当网络报文找不到终点的时候,避免网络报文在网络中无限的传输,以消耗带宽)
- 协议:占 8 位,表明 IP 数据所携带的具体数据是什么协议的(如 TCP、UDP 等,一些协议对应的值,可参考下图)
- 校验和:占 16 位,校验 IP 首部是否有出错(接收方接收到 IP 首部之后也会进行校验,如果有错,则直接丢弃)
- 源 IP 地址:发送 IP 数据报的网络设备的 IP 地址
- 目的 IP 地址:IP 数据报要到达的目的网络设备的 IP 地址
三、分片与组装
MTU(Maximum Transmission Unit最大传输单元)是在 IP 层下面的 MAC 协议中的概念,MAC 协议我们可以理解为是物理层的一些协议,它位于 IP 协议的下层,那么在发送数据时相当于是
- 用户数据 + 应用层协议报头(如HTTP请求报头)作为有效载荷交给传输层(如TCP协议)
- TCP 协议再将 TCP 报头 + 应用层传来的数据下交给IP层
- IP 层再将IP协议首部 + TCP 层传来的 TCP 报文交付给 MAC 帧
因此每个 MAC 帧其实是 IP 协议首部 + IP层的有效载荷。而 MAC 帧是有长度限制的,所以就要求 IP 数据报向下交付时并不是随心所欲想发多长就发多长,如果 MAC 帧要求 MTU 为1500字节,而 IP 数据包总长度有 2000 字节,那么就需要分片,将原有的IP数据包分成两片,依次发送,对端的主机在接收后,由对端的 IP 层再完成组装。
我们在Linux环境下可以使用 ifconfig 命令查看到 MTU
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 # MTU
inet 10.3.0.224 netmask 255.255.255.0 broadcast 10.3.0.255
inet6 fe80::8f56:1ca3:e03e:6766 prefixlen 64 scopeid 0x20<link>
inet6 fe80::a606:b02b:c0e2:2208 prefixlen 64 scopeid 0x20<link>
ether fa:eb:40:37:39:00 txqueuelen 1000 (Ethernet)
RX packets 51933403 bytes 25765598594 (23.9 GiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 43679448 bytes 141442078143 (131.7 GiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
(1)分片的过程
假设IP层要发送4500字节的数据,由于该数据超过了MAC帧规定的MTU,因此IP需要先将该数据进行分片,然后再将一个个的分片交给MAC帧进行发送。
IP报头如果不携带选项字段,那么其大小就是20字节,假设IP层添加的IP报头的长度就是20字节,并按下列方式将数据分片后形成了四个分片报文:
分片报文 | 总字节数 | IP报头字节数 | 数据字节数 |
1 | 1500 | 20 | 1480 |
2 | 1500 | 20 | 1480 |
3 | 1500 | 20 | 1480 |
4 | 80 | 20 | 60 |
需要注意的是,分片后的每一个分片数据都需要封装上对应的IP报头,因此4500字节的数据至少需要分为四个分片报文进行发送。
分片报文到达对方的IP层后需要被重新组装起来,因此IP层在对数据进行分片时需要记录分片的信息,而IP报头当中的16位标识、3位标志和13位片偏移实际就是与数据分片相关的字段。
- 16位标识:唯一标识主机发送的报文,如果数据在IP层进行了分片,那么每一个分片报文的16位标识是相同的。
- 3位标志:第一位保留,表示暂时没有规定该字段的意义。第二位表示禁止分片,表示如果报文长度超过MTU,IP模块就会丢弃该报文。第三位表示 "更多分片",如果报文没有进行分片,则该字段设置为0,如果报文进行了分片,则除了最后一个分片报文设置为0以外,其余分片报文均设置为1。
- 13位片偏移:分片相对于原始数据开始处的偏移,表示当前分片在原数据中的偏移位置,实际偏移的字节数是这个值 × 8 得到的。因此除了最后一个报文之外,其他报文的长度必须是8的整数倍,否则报文就不连续了。
因此上述四个分片报文对应的16位标识都是一样的,假设四个分片报文的16位标识都是123,则这四个报文对应的16位标识、3位标志中的 "更多分片" 和13位片偏移分别如下:
分片报文 | 总字节数 | IP报头字节数 | 数据字节数 | 16位标识 | “更多分片” | 13位片偏移 |
1 | 1500 | 20 | 1480 | 123 | 1 | 0 |
2 | 1500 | 20 | 1480 | 123 | 1 | 185 |
3 | 1500 | 20 | 1480 | 123 | 1 | 370 |
4 | 80 | 20 | 60 | 123 | 0 | 555 |
需要注意的是,13位片偏移当中记录的字节数是当前分片在原数据开始处的偏移字节数的值 ÷ 8 得到的,比如分片报文2在原始数据开始处的偏移字节数是1480,其对应的13位片偏移的值就是1480 ÷ 8 = 185,分片报文3 = (1480+1480) ÷ 8 = 370
字节数 | 1480 | 1480 | 1480 | 60 |
分片 | 1 | 2 | 3 | 4 |
片偏移 | 0 | 185 | 370 | 555 |