IPv4数据报:首部、TTL、MTU与分片
IPv4数据报格式
网络层的分组称为数据报(datagram)。IPv4数据报由首部和数据两部分组成:首部的固定部分长20字节,其后可以跟随长度可变的选项;数据部分通常是一个TCP段或UDP数据报。下图按照每行32位排列首部字段,点击字段可以查看它的作用。
寿命TTL:每台路由器把它减1;减到0时丢弃数据报。
| 字段 | 长度 | 作用 |
|---|---|---|
| 版本 | 4位 | IPv4为4 |
| 首部长度 | 4位 | 以4字节为单位,无选项时为5,即20字节 |
| 服务类型 | 8位 | 前6位为区分服务(DSCP),后2位为显式拥塞通知(ECN) |
| 总长度 | 16位 | 首部加数据的字节数,最大为65535 |
| 标识 | 16位 | 同一数据报的所有分片使用相同的标识 |
| 标志 | 3位 | 保留位、DF(不分片)、MF(还有分片) |
| 片偏移 | 13位 | 分片的数据在原数据报数据部分中的位置,以8字节为单位 |
| 寿命(TTL) | 8位 | 每经过一台路由器减1,减为0时丢弃 |
| 协议 | 8位 | 数据部分交给哪个上层协议:6为TCP,17为UDP,1为ICMP |
| 首部检验和 | 16位 | 只检验首部 |
| 源地址、目的地址 | 各32位 | 发送方和接收方的IP地址 |
协议字段的作用类似于传输层的端口号:传输层用端口号把数据交给正确的进程,网络层则用协议字段把数据报的数据部分交给正确的上层协议。
寿命TTL与首部检验和
如果路由出现环路,没有任何机制的数据报会在网络中永远兜圈子。寿命(TTL, Time-To-Live)字段解决了这个问题:数据报每经过一台路由器,TTL就减1;当TTL被减为0时,路由器丢弃该数据报,并通常向源主机返回一个ICMP超时报文(见4.6节)。
首部检验和的计算方法与第3章的UDP校验和相同:把首部看作一串16位的字,求和时把溢出位回卷,最后取反。由于TTL在每一跳都会改变,每台路由器都必须重新计算并写回首部检验和。
其中是首部的第个16位字(计算时检验和字段先置0),fold表示把超出16位的进位回卷加到低16位,右式是接收方的验证条件。
IP只检验首部,数据部分的差错由TCP、UDP等上层协议自己检测。这样路由器只需处理20字节左右的首部,转发速度更快。
跟随一个数据报的首部穿过三台路由器
解锁完整互动实验、进阶解析与精选参考答案。
知识测验
MTU与分片
链路层帧能够承载的最大数据量称为最大传送单元(MTU, Maximum Transmission Unit)。例如以太网的MTU为1500字节。一条端到端路径上的各段链路可能使用不同的链路层协议,MTU也可能不同。当数据报的总长度大于下一条链路的MTU时,路由器会把它分成若干个较小的数据报,称为分片(fragment)。
每个分片都带有自己的IP首部,并按以下规则填写:
- 所有分片复制原数据报的标识字段,接收方据此判断哪些分片属于同一个数据报。
- 除最后一片外,每片的数据长度都必须是8字节的整数倍。
- 片偏移等于该片数据在原数据报数据部分中的起始字节数除以8。
- 除最后一片外,各片的MF标志都为1。
例如,一个总长度4000字节(首部20字节、数据3980字节)的数据报要通过MTU为1500字节的链路。每片最多携带1480字节数据,因此被分成3片:
| 分片 | 总长度 | 数据字节 | 片偏移 | MF |
|---|---|---|---|---|
| 1 | 1500 | 1480 | 0 | 1 |
| 2 | 1500 | 1480 | 185 | 1 |
| 3 | 1040 | 1020 | 370 | 0 |
分片在途中可能走不同的路径,因此重组只在目的主机进行,路由器不负责重组。只要有一个分片丢失,目的主机最终就会丢弃已经收到的所有分片,整个数据报都要由上层重传。分片还会增加首部开销并消耗路由器资源,因此现代主机通常设置DF标志并进行路径MTU发现:当路由器发现数据报过大但DF=1时,丢弃它并返回ICMP“需要分片”报文,主机据此减小数据报长度。IPv6更进一步,路由器根本不进行分片(见4.7节)。
把数据报切成适合小MTU的分片
解锁完整互动实验、进阶解析与精选参考答案。
知识测验
为5000字节数据报分片
主机发送一个总长度5000字节、首部20字节、标识为777的IPv4数据报,下一条链路的MTU为1500字节。列出每个分片的总长度、数据字节数、片偏移与MF标志。该链路上一共传送多少字节?如果第3片丢失会怎样?
用Python计算IPv4首部检验和
抓到一个IPv4首部(检验和字段已置为0):
4500 0073 0000 4000 4011 0000 c0a8 0001 c0a8 00c7
编写Python程序,把它看作10个16位字,计算因特网检验和并验证。打印四行:原始和、回卷后的和、检验和以及验证和。预期检验和为0xb861。