IP 与路由(二)

IP 使用 IP 数据报运送上层协议的交付的数据到目标主机,IP 数据报由 IP 头部和数据载荷两部分组成。

IP 头部

IP 头部位于每个 IP 数据报的起始位置,IP 头部中包含 IP 数据报的版本、源 IP 地址、目的 IP 地址、TTL 等重要信息。IPv4 与 IPv6 的头部结构有所不同,如无特殊说明,本文中所讨论的 IP 头部均指 IPv4 的头部。IP 头部中一共有 14 个字段,其中前 13 个字段是必选的。一个 IP 头部的结构如下所示[1]

    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  |    DSCP   |ECN|          Total Length         |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |         Identification        |Flags|      Fragment Offset    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |  Time to Live |    Protocol   |         Header Checksum       |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                       Source Address                          |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Destination Address                        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Options                    |    Padding    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Version

IP 头部最起始部分的 4 bits 是 Version 字段,这个字段与 IPv6 头部兼容,用于标识数据报的版本号。对于 IPv4 的数据报来说,这个字段的值为 4。

IHL

IHL 字段表示 IP 数据报头部的长度(Internet Header Length),是一个 4 bits 长的字段。IP 数据报是按 32 bits(4 字节)对齐的,它的位长度一定是 32 的整数倍,IHL 的值表示的是 IP 数据报长度对 32 bits 的倍数。例如,假设一个 IP 数据报的 IHL 字段值是 6,则说明这个数据报 IP 头部的长度是 6 x 32 bits = 192 bits(24 字节)。IP 数据报头部的长度不会短于 160 bits(20 字节),因此一个有效的 IHL 字段值不小于 5。

DSCP

DSCP 字段是一个 6 bits 长的差异化服务代码(Differentiated Services Code Point)。在 Internet 发展的过程中,人们发现互联网中开始同时传送着多种不同类型服务的数据,包括语音、视频、音乐流媒体、网页和电子邮件等[2]。为了保证提供尽量好的服务质量,不同互联网服务本身的一些特性决定了它们的数据报在被转发时应当采取不同的策略——例如网络语音服务对延迟比较敏感,因此它们的数据报需要优先于文件传送服务的数据报被转发。DSCP 字段标志了一个 IP 数据报所属的服务类型,网络设备可以依据这个字段的值提供差异化的转发行为。默认的 DSCP 值是 0。

ECN

ECN 是一个 2 bits 长的字段,用于为 IP 和 TCP 提供显式的拥塞通知,以取代用丢弃数据报的方式控制拥塞。关于 ECN 更详细的信息,请参考 RFC 3168 文档及维基百科 Explicit Congestion Notification 词条。

Total Length

Total Length 是一个 16 bits 长的字段,用于表示整个 IP 数据报的长度,包括头部和数据载荷,单位是字节。由于一个 IP 数据报至少应包含头部,而上文中提到最短的 IP 头部长度是 160 bits,因此 IP 数据报总长度不会小于 160 bits,一个有效的 IP 数据报中 Total Length 字段的值不会小于 20。Total Length 可以表示的最大的数是 216 – 1 = 65,535,因此理论上最大可以有长达 65,535 字节的 IP 数据报。

值得注意的是,尽管 IP 是建立在分组交换链路层上的数据报协议,下层协议也会为 IP 提供明确的边界,但 Total Length 字段的存在是有意义的。这是因为下层协议可能会对每个分组有最短长度的限制或者对对齐方式有特殊的要求,因此在传送 IP 数据报时可能会在原始的数据报末尾增加多余的填充位,此时必须依靠 IP 头部中的 Total Length 来确定数据报的真实长度。例如以太网限制了每一帧的最小长度为 64 字节,除去以太网帧头部的 18 字节之后的数据部分的最小长度是 46 字节。当以太网发送一个总长度小于 46 字节的 IP 数据报时,会在其末尾填充 0 以符合最短 46 字节的要求。

Identification

Identification 字段占 16 bits,是一个唯一标识字段。在一段时间内 IP 每发出的一个数据报都有一个唯一的 Identification,这个字段主要用于标记一组 IP 分片中的每个单独的 IP 数据报以便于接收端重新拼装。

Flags

Flags 字段占 3 bits,是一个标志字段,用于标志 IP 数据报有关分片的信息。这三个标志位的定义分别如下:

  • 第 0 位:保留位,必须置为 0;
  • 第 1 位:DF(Dont’t Fragment),禁止分片位。0 表示允许分片,1 表示不允许分片;
  • 第 2 位:MF(More Fragments),更多分片位。当一个 IP 数据报被分片为多个时,只有最后一个 IP 数据报的 MF 标志为 0,其余的 IP 数据报都将置 MF 标志为 1。

关于 IP 分片的话题,下文中还会有更详细的讨论。

Fragment Offset

Fragment Offset 是一个 13 bits 长的分片偏移字段。该字段用于在分片发生时定位一个分片所携带的数据在原始 IP 数据报的载荷中的偏移,以 8 字节(64 bits)为单位。在未分片的 IP 数据报中,这个字段的值为 0。

Time To Live

Time To Live(TTL)是一个 8 bits 的字段,标识 IP 数据报最多还能在网络中生存多长时间。当一个 IP 数据报在合理的时间内还没有被送到目的主机上时,它可能是经历了错误的路由或进入了环路,此时网络应该丢弃它。在最初的定义(RFC 791)中,TTL 字段的单位是秒,当 IP 数据报被一个模块处理时,应在处理后修改 TTL 字段的值,使其等于原来的值减去这个数据报在处理中消耗的时间(单位秒);RFC 还规定处理时间一律按向上取整计算,例如不足 1 秒的按 1 秒计。

在实践中,由于路由器对 IP 数据报处理的时间通常小于 1 秒,因此实际上路由器对其每个转发的 IP 数据报都是将 TTL 作减 1 处理,最后 TTL 字段变成了按跳数(hops)计的生存时间字段[3]。例如一个 IP 数据报的 TTL 字段值是 128,这往往意味着如果这个数据再经历 128 跳还没到达目的地就将被丢弃。当 IP 协议遇到一个 TTL 为 0 的数据报时,应当马上将其丢弃,并向数据报的源地址发送 TTL 超时的 ICMP 报文以通知发送者。

一个充分利用了 TTL 特性实现的网络工具是 traceroute。traceroute 程序利用逐次构造并发送 TTL 从 1 开始依次递增的 IP 数据报,并接收中途路由器返回的 ICMP TTL Timeout 报文来探测从当前主机到任一目标 IP 地址所经历的所有路由器的地址。

Protocol

Protocol 是一个 8 bits 的字段,它用于标识 IP 数据报所运送的载荷属于何种上层协议,最常见的 Protocol 字段值有 1 – ICMP、6 – TCP、17 – UDP 等。这个列表中列出了所有 Protocol 字段取值对应的上层协议及它们的详细信息。

Header Checksum

Header Checksum 字段长 16 bits,是 IP 头部的校验和字段。该用于对 IP 头部进行校验,以帮助 IP 协议检查出 IP 数据报头部在链路层转发过程中可能出现的误码情况。校验和是一种简单的差错检测方法,校验和值是一个无符号整数。以要求的校验和的字长为单位对原始数据分组,然后分别将它们相加,忽略掉在求和中溢出的部分,将得到的结果按位取反即为这段数据的校验和。如果原始数据的长度不是校验和字长的整数倍,那么对原始数据的末尾进行补 0 处理,直到其长度等于校验和字长的整数倍。下面是一个用 C 语言实现 16 位校验和计算的例子:

IP 头部 Checksum 字段的计算方法是,先将 IP 头部其他字段填充好,再将 Checksum 字段置为 0,对整个头部执行一次校验和计算,最后将计算结果填充到 Checksum 字段中。考察校验和的计算方法后不难发现,如果此时再对整个 IP 头部执行一次校验和计算,计算结果必为 0。当 IP 网络中一个设备收到 IP 数据报时,它首先会检验数据报头部是否在传输过程中发生了差错;检测方法就是利用了上述校验和的性质,只需对 IP 数据报头部部分求一次检验和,判断计算结果是否为 0,如果不为 0,则说明头部中出现了差错,必须丢弃这个数据报。

需要注意的是,IP 只对它的头部进行校验,计算校验和并不包括 IP 携带的载荷数据部分,IP 不保证它携带的载荷在传输过程中不发生差错。也就是说,如果一个 IP 数据报在传输过程中发生了误码,但误码只发生在载荷部分,IP 并不能检测这个差错,这个包含差错的数据报同样将被送到目的主机。对数据载荷的差错检验应该由上层协议来完成。

Source Address

32 bits 长,数据报发送者的 IP 地址。

Destination Address

32 bits 长,数据报目的主机的 IP 地址。

Options

Options 是 IP 头部中最后一个字段,它是不定长字段,也是一个可选的选项,一个 IP 头部中可以包含零个或多个 Options,用于在 IP 头部中添加附加的选项信息。IP 头部中的选项一共有如下两种情形:

  • 仅由一字节的选项类型(option-type)组成,没有附加数据;
  • 由一字节选项类型、一字节选项数据长度和不定长的选项数据组成。

其中选项类型依次由以下三部分构成:

  • 1 bit 拷贝标志(copied flag),这个标志位决定 IP 数据报发生分片时是否在分片的数据报中拷贝本选项,取值 0 表示不拷贝,取值 1 表示拷贝。
  • 2 bits 选项类别(option class)
  • 5 bits 选项号(option number)

RFC 791 中定义了如下几种选项:

类别 选项号 数据长度 描述
0 0 标志选项字段的结束,只占一个字节
0 1 没有选项,在选项与选项之间起填充作用,用于对齐各个选项的起始位置,占一个字节
0 2 11 安全选项。
0 3 变长 松散源路由选项。
0 9 变长 严格源路由选项。
0 7 变长 路由追踪。用于追踪数据报在网络中经历的路由。
0 8 4 Stream ID。用于运载卫星网络(SATNET)数据流的 id。
2 4 变长 Internet 时间戳。

有关 options 字段更详细的信息,请参阅 RFC 791 文档。

IP 数据报分片与重组

上文中提到,理论上最大的 IP 数据报可以长达 65,535 字节。IP 是建立在数据链路层之上的网络层协议,一个 IP 数据报从源主机出发到达目的主机的过程中,往往会经历多个不同物理网络,其链路层实现可能各不相同。在分组交换网链路层中,IP 数据报与链路层的帧往往是一一对应的,一个链路层帧携带一个 IP 数据报。而链路层对一帧的最大长度是有限制的,这个最大长度通常远小于 65,535 字节,例如 IEEE802.3 以太网的最大帧长是 1500 字节。在软件中,会根据链路层最大帧长的实际情况为链路层接口配置一个 MTU(最大传输单元) 值,表示这个接口一次最大能传输的字节数。

不同链路层的最大帧长度可能不尽相同,因此在互联网中很可能会出现下面的情形:某主机从 A 接口收到一个数据报,在查路由表之后决定将其从 B 接口转发给下一站,而 B 接口所在的链路层的 MTU 小于数据报的长度,不足以直接运载这个数据报。当这种情形发生时,IP 将根据该数据报头部中 flag 字段的情况作如下两种处理:

  1. 如果 DF 位是 1,说明该数据报不允许分片,丢弃这个数据报并向它的源地址发送“目标不可达:要求分片”的 ICMP 报文。
  2. 如果 DF 位是 0,则根据具体情况将数据报拆分成两片或多片,依次发往下一站。

当一个数据报需要分片时,首先根据具体的 MTU 值将原始数据报的载荷数据拆分成两份或多份,除最后一份之外,每一份的长度应该是 8 字节的整数倍以保证对齐。然后创建相同数量的新 IP 数据报,将原始数据报的头部复制到这些新数据报的头部中,将拆分后的数据依次填入到新的数据报的载荷中。再依次修改新数据报头部中的 flag、total length、fragment offset 等字段,除最后一个数据报外的所有数据报 flag 字段的 MF 标志都修改为 1,所有数据报的 total length 字段都修改为数据报实际的总长度,所有数据报的 fragment offset 字段修改为分片载荷在原数据报载荷中的偏移。最后一个数据报做必要的补 0 对齐处理。

将分片后的数据报依次发送给下一站。

当 IP 收到分片的 IP 数据报时,在必要时(例如分片已到达目的主机)将它们收集起来并把各自的载荷连接为整体。IP 把头部中 identification、source address、destination address 和 protocol 相同的 IP 数据报归为一组,并根据 fragment offset 字段的值决定各个分片的先后顺序,在所有分片都到达时,原始数据报的载荷就拼装好了。

后续博文还将继续讨论与 IP 路由相关的话题。

(允许转载本文,转载请注明出处:http://luodichen.com/blog/?p=476 )

参考文献
[1] Information Sciences Institute of USC, INTERNET PROTOCOL DARPA INTERNET PROGRAM PROTOCOL SPECIFICATION, RFC 791, Sep 1981; tools.ietf.org/html/rfc791
[2] Wikipedia. Differentiated services[EB/OL]. https://en.wikipedia.org/wiki/Differentiated_services
[3] Wikipedia. IPv4[EB/OL]. https://en.wikipedia.org/wiki/IPv4
[4] W. Richard Stevens, TCP/IP 详解 卷1:协议[M]. 机械工业出版社, 2000.