IP 协议
Author: 柏喵樱
copyright reserved
IP 协议数据包结构
IP 协议是网络层最主要的协议,几乎所有的上层数据包都需要 IP 协议的承载,我们网络层的第一块内容将从 IP 协议讲起。
下图展示了 IP 协议的报文头部格式,这张图每行代表 32 个 bit,也就是 4 个字节(byte)。如果不计算 Options 这种可选项的话,IP 协议的报头长度为 20 个字节。
在头部的后面,会携带负载(payload),这个词可能略显晦涩,但实际上就是我们需要发送的数据。
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |Version| IHL |Type of Service| Total Length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Identification |Flags| Fragment Offset | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Time to Live | Protocol | Header Checksum | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Source Address | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Destination Address | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Options | Padding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Example Internet Datagram Header
版本
第一个字段是 IP 协议版本号,用于标识 IPv4 和 IPv6 两个版本,这两个版本的报文格式是不同的。上图展示的是 IPv4 的报文格式
IHL (Internet Header Length)
就是 IP 协议的头部部分的长度,单位是 4byte,由于 IP 头一般长度为 20byte,所以这个值一般为 5。如果存在 Options 字段,这个值会相应增加。
Type of Service
服务类型,标记服务类型最初是为了对不同服务的数据包做不同的转发策略,比如说哪些数据包应该被优先转发,应该更注重转发延时还是更注重吞吐量一类的问题,除非到运营商层面一般接触不太到。
数据包总长度
Total Length 很好理解就是包括 IP 头在内的 IP 报文的总长度,报文长度上限就是 MTU。
Identification & Flags & Fragment Offset
这三个字段放在一起讲,他们都是用于控制分片的。
我们的 MTU 他一般是 1500,去除 20 bytes 的头部他还能够携带 1480 bytes 的数据,但是现实中的负载很容易就能够超出这个大小,这时候就需要将内容切片分块发送。
切片时,会切出许多 1480 bytes 的切片,和剩下的不足 1480 bytes 的部分。对于以上的所有切片,都会给他们加一个自己的 IP 头。这些 IP 头有一个共同点,就是他们具有一个相同的 Identification 用于识别(identify)他们原先是一块的。
现在还存在两个问题,
- 服务器接收到一个分片后,不知道要不要继续接收
- 分片很有可能乱序抵达,服务器不知道分片的顺序
对于第二个问题,引入了 Fragment Offset(分片偏移量),他的单位是 byte,用于标识这一个分片的数据,是从原来超长负载的哪个位置开始的。比如对于一个长度为 5000 bytes 的负载,他的几个分片的 Fragment Offset 依次为 0,1480,2960,4440。在收到数据后,服务器只需要按照这个字段重新组装即可。
对于第一个问题,引入了 Flags(标志位), Flags 里有三个 Flag,其中最后一位的意义是 “后面还有更多分片 “,这就相当于告诉服务器,你要继续等待接收其他分片。而当最后的分片抵达时,服务器在知道这是最后的分片的同时,他也能从这个分片的 Fragment Offset 里得知前面应该有多少数据,如果缺失,服务器也能继续等待接收。
关于 Flags 的其他两位,最高位是目前保留不使用,永远为 0。后面那位的意义是不要分片,这一般是不设置的,他这句话是对传输过程中的路由设备讲的,因为如果途径 MTU 低于当前包长度的链路可能会进行二次分片。如果设置了这个 Flag,转发时再碰到 MTU 低的链路这种包会被直接丢弃同时返回一个 ICMP 包告诉源 IP 地址的设备发生了什么。
TTL(Time To Live)
TTL 是一个 unsigned int,代表数据包的剩余生存时间,指的是数据包在互联网中还能转发多少时间,单位是秒。
虽然单位是秒,但是一般情况下转发耗时是远远低于一秒的,根据 IP 协议的规定,无论转发耗时多少,TTL 应当至少 -1,所以在实践中 TTL 往往指的是转发了几次。
一般发往互联网的数据包初始 TTL 为 64 或 128,你可以试试 ping 命令,他会显示一个 TTL,这是对面服务器回复的包到自己的设备的时候的 TTL。
PS C:\Users\bs> ping baidu.com
正在 Ping baidu.com [39.156.66.10] 具有 32 字节的数据:
来自 39.156.66.10 的回复: 字节=32 时间=43ms TTL=47
来自 39.156.66.10 的回复: 字节=32 时间=144ms TTL=47
来自 39.156.66.10 的回复: 字节=32 时间=43ms TTL=47
来自 39.156.66.10 的回复: 字节=32 时间=42ms TTL=47
39.156.66.10 的 Ping 统计信息:
数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失),
往返行程的估计时间(以毫秒为单位):
最短 = 42ms,最长 = 144ms,平均 = 68ms
2
3
4
5
6
7
8
9
10
11
12
TTL 减少到 0 的时候,这个数据包会被丢弃,所以如果互联网中出现路由配置错误,以至于产生了一个环,TTL 能够帮助数据包及时停止转发,避免事故扩大最终导致网络设施瘫痪。
当路由设备由于 TTL 为 0,而丢弃这个数据包的时候,路由设备还会向源 IP 回复一个 TTL超时
的 ICMP 报文
PS C:\Users\bs> ping baidu.com -i 2
正在 Ping baidu.com [39.156.66.10] 具有 32 字节的数据:
来自 192.168.1.1 的回复: TTL 传输中过期。
来自 192.168.1.1 的回复: TTL 传输中过期。
来自 192.168.1.1 的回复: TTL 传输中过期。
来自 192.168.1.1 的回复: TTL 传输中过期。
39.156.66.10 的 Ping 统计信息:
数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失),
2
3
4
5
6
7
8
9
10
traceroute 就是利用这个性质来实现的,通过从 0 开始遍历 TTL 的方式,可以拿到转发过程中的所有路由器的 ICMP 回复,从这些回复中可以读取到这些路由器的 IP。
当然 traceroute 不是万能的,真实互联网中广泛存在拦截,不响应,改写等等行为,会导致 traceroute 产生丢失或者藏跳等等比较迷惑的结果。
PS C:\Users\bs> TRACERT.EXE bilibili.com
通过最多 30 个跃点跟踪
到 bilibili.com [8.134.50.24] 的路由:
1 8 ms 13 ms 2 ms 192.168.0.1 [192.168.0.1]
2 1 ms 1 ms 2 ms 192.168.1.1 [192.168.1.1]
3 8 ms 8 ms * 100.66.0.1
4 * * * 请求超时。
5 * * * 请求超时。
6 * * * 请求超时。
7 * * * 请求超时。
8 * * 8 ms 61.139.121.77
9 * * * 请求超时。
10 78 ms * * 113.96.5.134
11 * 83 ms 43 ms 121.14.50.241
12 45 ms 42 ms 41 ms 121.14.24.82
13 * * * 请求超时。
14 * * * 请求超时。
15 * * * 请求超时。
16 * * * 请求超时。
17 42 ms 50 ms 40 ms 8.134.50.24
跟踪完成。
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
上面就是一个真实案例,有很多路由设备在丢弃数据包时并没有给出 ICMP 回复。
协议
协议(Protocol)指的是负载中的数据所使用的协议。这就很广泛了,比如
TCP
UDP
ICMP
Telnet
OSPF
具体可以参考 List of IP protocol numbers - Wikipedia
头部校验和
头部校验和(Header Checksum)用于检验传输的数据是否出错,一旦出错直接丢弃。
在计算头部的校验和的时候,头部校验和这个字段会被置零,再使用特定算法计算得到校验和,填写到这个位置。
这个校验和算法比较简单,就是头部以 16 bits 为单位,取补码再求和
注意,由于 TTL 在每次转发中都会改变,所以实际上,每次转发都会重新计算校验和。
IP 地址
正如上图的 Source Address
和 Destination Address
字段所展示的一样,一个 IP 地址占用 4 byte 的存储空间。在书面的写法上,我们用 .
将每一个 byte 分割开,例如 1.1.1.1
这样更便于人类处理和分类。
有一些 IP 地址被用作特殊用途,除了这些 IP 地址外的地址都是公网 IP,一个公网 IP 在整个互联网范围内按照规范来说,只能由一台网络设备占有(也有例外,anycast)。但互联网是复杂的,也有人占用这些地址作为自己的内网地址(例如 tr069),这个由网络运维人员自行分析情景把握。
网络地址的分类需要更多的前置知识,我们后续再介绍。
可选项
可选参数(Options)比较繁琐,一般没啥用,有兴趣的可以看看下面参考资料中 RFC 791 对此的描述。