0%

TCP/IP 路由技术 01:TCP/IP 回顾

本系列文章是 《TCP/IP 路由技术》(英文名:Routing TCP/IP)的读书笔记。该系列图书共有两卷,第一卷主要讲解 IGP(内部网关协议),第二卷主要讲解 BGP(边界网关协议,或者称为外部网关协议)以及 IP 多播。对于一个从事网络行业的专业开发人员,路由选择协议应该是知识库中非常重要的一部分,这也是我学习该系列图书的初衷。

这篇文章主要回顾了启用、控制或帮助 TCP/IP 路由选择的协议,对 TCP/IP 协议族本身并不作深入讨论。

TCP/IP 协议层

如下展示了 TCP/IP 协议族与 OSI 参考模型的相互关系。在 TCP/IP 协议族中,网络接口层对应 OSI 的物理和数据链路层。

物理层包含了多种与物理介质相关的协议,这些物理介质用以支撑 TCP/IP 通信。物理层协议按照正式的分类可以分为 4 类,这 4 类涵盖了物理介质的所有方面:

  • 电子/光学协议:描述信号的各种特性
  • 机械协议:规定了连接器的尺寸或导线的金属成分
  • 功能性协议:描述了做什么
  • 程序性协议:描述了怎么做

数据链路层协议包含了控制物理层的协议:如何访问和共享机制、怎么标识介质上的设备、以及在介质上发送数据之前如何完成数据帧。典型的数据链路层协议有 IEEE 802.3/以太网、帧中继、ATM 和 SONET。

Internet 层与 OSI 的网络层对应,主要负责定义数据包格式和地址格式,为经过逻辑网络路径的数据进行路由选择。

与 OSI 传输层相对应的是主机到主机层,传输层控制逻辑链路上的流量,即两台设备的端到端连接,这种逻辑连接可能跨越一连串数据链路。

应用层与 OSI 的会话层、表示层、应用层相对应。虽然一些路由选择协议使用这一层,例如边界网关协议(BGP)、路由选择信息协议(RIP)等,但是应用层最常用的服务是向用户应用提供访问网络的接口。

协议族的各层之间允许多路复用。

IP

如下给出了 IP 包头的格式,相应的标准为 RFC791。数据包中的大多数字段对路由选择都很重要。

  • 版本(Version):4 位,标识了数据包的 IP 版本号
  • 报头长度(header length): 4 位,表示 32 位字长(即以 4 字节为单位)的 IP 报头长度。由于 IP 数据包的包头可以包含可选字段,因此报头长度会发生变化。IP 报头最小长度为 20 字节,最长可扩展到 60 字节(15 * 4)
  • 服务类型字段(Type of Service,ToS):8 位,用来指定特殊的的数据包处理方式。服务类型字段划分为两个子字段:优先权和 ToS。优先权用来设置数据包的优先级。ToS 允许按照吞吐量、时延、可靠性和费用方式选择传输服务
  • 总长度(Total Length):16 位,以字节为单位,表示 IP 数据包的总长度(包括 IP 头)。接收者用 IP 数据包总长度减去 IP 报头长度,就可以确定数据包有效载荷的大小。IP 数据包的最大长度为 65535
  • 标识符(Identifier):16 位,通常与 标记字段 和 分段偏移字段 一起用于数据包的分段。如果数据包的原始长度超过数据包所要经过的数据链路的最大传输单元(MTU),那么必须将数据包分段为更小的数据包
  • 标记字段(Flag): 3 位,其中第 1 位没有使用,第 2 位是不分段位(DF)。如果该位被设置时,表示路由器不能对数据包进行分段处理。如果数据包由于不能被分段而未能被转发,那么路由器将丢弃该数据包并向源点发送错误信息。这一功能可以在网络上用于测试 MTU 值。第 3 位表示还有更多分段位(MF),除了最后一个分段的 MF 位设置为 0 外,其他所有分段的 MF 位均设置为 1,接收者使用该字段来确定是否已经收到最后一个分段
  • 分段偏移(Fragment Offset):13 位,以 8 个字节为单位。用于指明分段起始点相对于报文起始点的偏移量。由于分段到达时可能乱序,因此接收者使用该字段按照正确的顺序重组数据包
  • 生存时间(Time To Live,TTL):8 位。创建数据包时该字段被设置为某个特定值,数据包沿路由器被传输时,每台路由器都会将 TTL 值减 1。当 TTL 值减为 0 时,路由器将会丢弃该数据包并向源点发送错误信息。这种方法可以防止数据包在网络中无休止地传输。Cisco IOS 的 trace 命令就使用了 TTL 字段
  • 协议(Protocol):8 位,给出了上层协议的协议号。协议字段指出了数据包中的有效承载信息的类型
  • 报头校验和(Header Checksum):16 位,针对 IP 报头的纠错字段。校验和不计算被封装的数据。UDP、TCP 和 ICMP 都有各自的校验和。该字段由数据包发送者计算得到,接收者将连同原始校验和重新计算 16 位二进制补码和。如果数据包传输没有发生错误,那么结果应该 16 位全为 1。由于每台路由器都会降低数据包的 TTL 值,因此每台路由器都必须重新计算校验和
  • 源地址(Source Address):32 位,表示数据包的源 IP 地址
  • 目的地址(destination Address):32 位,表示数据包的目的 IP 地址
  • 可选项:是一个长度可变的字段,它是可选的。可选项被添加到报头中,包括源点产生的信息和其他路由器加入的信息。可选项主要用于测试,常用的可选项包括:
    • 松散源路由选择(Loose Source Routing):它给出了一连串路由器接口的 IP 地址序列,数据包必须沿着 IP 地址序列传送,但是允许在相继的两个地址之间跳过多台路由器
    • 严格源路由选择(Strict Source Routing):它给出了一连串路由器接口的 IP 地址序列,不同于松散源路由选择,数据包必须严格按照路由转发。如果下一跳不在列表中,那么将发生错误
    • 记录路由选项(Record Route):当数据包离开时为每台路由器提供空间以记录数据包的出站接口地址,以便保存数据包经过的所有路由器的记录。
    • 时间戳(Timestamp):时间戳选项使得路由器记录数据包的到达时间
  • 填充项(Padding):该字段通过在可选项后面添加 0 来补足 32 位,这样保证报头长度是 32 位的倍数

最近,ToS 字段已经作为区分服务(DiffServ)架构的一部分被重新定义了。区分服务架构为 IP 数据包所创建的处理比通过相对严格的 ToS 定义所允许的处理灵活得多。在 DiffServ 下,可以在路由器上定义服务分类,将数据包归类到这些分类中去。路由器可以根据它们的分类使用不同的优先级对数据包进行排序和转发。每一个排序和转发的处理称为一个 Per-Hop Behavior(PHB)。虽然 DiffServ 定义了这个架构或体系,但这个机制本身称为区分服务类别或简单地称为服务类别(QoS)。此时,ToS 字段按照如下方式被重新定义:

前 6 位构成了区分代码点(DiffServ Code Point, DSCP)。使用这 6 个位,可以使用在区分服务体系结构中预先定义的服务类别。最多可定义 64 个不同的服务类别,并整理到 PHB 中。

后 2 位为显式拥塞通知(Explicit Congestion Notification,ECN),是某些路由器用来支持显式支持拥塞通知的。如果支持该特性,这些位可用于拥塞信号(ECN=11)

IPv4 地址

IPv4 地址长度为 32 位,其包括网络号和主机号两部分。网络号唯一地标识了一条物理链路或逻辑链路。对于与该链路相连的所有设备来说,网络号部分是相同的,而主机号部分唯一地标识了该逻辑链路上连接的具体设备。

32 位地址包含 4 个字节,每个字节可以用 0-255 之间的十进制数表示,而每个十进制数之间用点号分隔。这种表示方法称为点分十进制。点分十进制表示法便于人们阅读和书写,而路由器更适合使用 32 位的二进制。

首个八位组字节规则

如果不对网络作太过精确的划分,那么网络可以按照主机数量分为 3 类:

  • 大型网络:包含大量主机的网络,大型网络的数量相对较少
  • 中型网络:包含主机的数量中等,而且中型网络的数量也中等
  • 小型网络:仅包含少量主机,但小型网络的数目很多

对于 3 种规模的网络,高层的地址划分要求有 3 种类型的网络地址:

  • A 类网络地址:用于大型网络,第 1 个字节表示网络号,后 3 个字节是主机号
  • B 类网络地址:用于中型网络,前 2 个字节表示网络号,后 2 个字节是主机号
  • C 类网络地址:用于小型网络,前 3 个字节表示网络号,后 1 个字节是主机号

由于所有的 IPv4 地址都是 32 位二进制字符串,所以需要某种方法来区分一个特定地址到底是属于哪一类地址:

  • 对于 A 类地址,首个八位组的第 1 位,即 32 位字符串最左边的一位,总是设置为 0。因此 A 类地址的首个字节的范围为 0 - 127。但是 0 被保留作为缺省地址部分,127 被保留为内部环回地址
  • 对于 B 类地址,首个八位组的前 2 位,总是设置为 10。因此 B 类地址的首个字节的范围为 128 - 191
  • 对于 C 类地址,首个八位组的前 3 位,总是设置为 110,因此 C 类地址的首个字节的范围为 192 - 223

地址掩码

网络地址表示整个数据链路的地址(而非某台主机的地址),其可以使用 IP 地址的网络部分来表示,其中主机位全为 0。IP 地址管理机构可以将一个网路地址分配给申请者,而申请者有权决定其中主机位的使用。

每一台设备和接口都将分配一个唯一的、主机号明确的地址。每个接口都需要知道自身的地址,还需要能够确定它所属的网络。这是通过地址掩码来完成的。地址掩码是个 32 位字符串,与 IPv4 的每一位相对应,其也可以使用点分十进制表示。地址掩码为 1 的位对应于地址的网络位,值为 0 的位对应于主机位。因此通在 IPv4 地址和地址掩码的每一位上执行逻辑与操作,就可以得到该 IPv4 地址对应的网络地址。

子网

为了完成路由选择,每个数据链路(网络)都必须有一个唯一的地址,另外数据链路上的每台主机也必须由一个地址,这个地址不仅标识主机为一个网络成员,还可以把主机和网络上的其他主机区分开来。为了建立一个网络,每个数据链路都必须使用不同的地址,以便这些网络可以被唯一地标识。

为了更合理地使用 IPv4 地址,可以进行子网划分,即把 IPv4 地址中的部分主机位用作子网位。于是现在 IPv4 地址包含 3 个部分:网络部分、子网部分和主机部分。地址掩码现在变成子网掩码。可以使用以下任何一种方式表示子网掩码:

  • 点分十进制
  • 位计数
  • 十六进制数

需要注意,并不是所有路由选择协议都支持子网地址。因为这些协议是有类别化的协议,他们不能区分一个全 0 子网和主网络号。同样,有类别选择协议也不能区分全 1 子网的广播地址和一个所有子网的广播地址。

子网掩码

在有类别地址环境中,子网位不能全为 0 或全部为 1。同样的,一个主机的 IPv4 地址也不能将主机位全部设置为 0,这种用法是为路由器保留的,用于表示网络和子网本身。当然 IPv4 地址的主机位也不能全部设置为 1,因为它用于表示广播地址。

在规划子网和子网掩码时,可以使用如下公式计算一个主网地址下可用的子网数:

1
2 ** n - 2,其中 n 为子网位数

使用如下公式可以计算每个子网内可用的主机数

1
2 ** n - 2,其中 n 为主机位数

下面是 IPv4 地址子网划分的步骤:

  • 确定需要多少个子网,每个子网需要多少台主机
  • 使用如上两个公式,确定子网位数和主机位数。如果存在多个满足需求的子网掩码,那么选择最能够符合未来需求的一个
  • 使用二进制进行计算,在子网空间中确定所有的位组合方式。在每种组合方式中,将所有的主机位都设置为 0,将得到的子网地址转换为点分十进制格式,最终结果就是子网地址
  • 对于每一个子网地址,继续使用二进制计算,在保持子网位不变的情况下写出所有的主机位组合,并将结果转换为点分十进制格式,最终结果就是每个子网的可用主机地址

如果只给出一个地址,如 192.168.100.160,无法确定它是否是一个主机地址、子网地址或广播地址。只有同时给出子网掩码的情况下,经过一番计算才能得出结论。

地址解析协议(ARP)

通过读取和操作数据包的网络地址,路由器可以沿着逻辑路径传送数据包,其中逻辑路径包含多个数据链路。沿独立的数据链路传送数据包时,需要把数据包封装在帧中,并且使用数据链路标识符(如 MAC 地址)让帧可以从链路的源点到达目的地。数据链路上的设备需要一种方法发现邻居的数据链路标识,以便将数据帧传送到正确的目的地。

IPv4 使用地址解析协议(ARP),详见 RFC826。当一台设备需要发现领一台设备的数据链路标识符时,它将发送一个 ARP 请求数据包。这个请求数据包中包括目标设备的 IPv4 地址以及请求设备(发送者)的源 IPv4 地址和数据链路标识符(MAC 地址)。然后 ARP 请求被封装在数据帧中,源 MAC 地址为发送者 MAC 地址,目标 MAC 地址为广播地址。

数据链路上的所有设备都将收到该 ARP 请求报文,并且检查帧内封装的数据包。除了目标设备可以处理该数据包外,其他所有设备都会丢弃该数据包。目标设备向源地址发送 ARP 响应报文,提供它的 MAC 地址。

Cisco 路由器的 debug arp 可以调试 ARP 功能。

ARP 数据包包含如下字段:

  • 硬件类型(Hardware Type):指定了硬件的类型
  • 协议类型(Protocol Type):指定了发送者映射到数据链路标识符的网络层协议的类型
  • 硬件地址长度(Hardware Address Length):指定了数据链路标识符的长度,单位为字节
  • 协议地址长度(Protocol Address Length):指定了网络层地址的长度,单位为字节
  • 操作(Operation):指明数据包是 ARP 请求还是 ARP 应答
  • 发送者硬件地址
  • 发送者 IP 地址
  • 目标硬件地址
  • 目标 IP 地址

使用 show arp 命令可以检查 Cisco 路由器内的 ARP 表内容。为了防止陈旧信息充满 ARP 表,每经过一个特定的时间间隔,ARP 信息将会被刷新。Cisco 路由器保存 ARP 表项的时间为 4 个小时(14400 秒),该缺省值可以修改。

ARP 表项还可以永久地保存在表中。如下所示,为了实现地址 172.21.5.131 到硬件地址 0000.00a4.b74c 的静态映射,并且采用 SNAP(Subnetwork Access Protocol)封装类型:

1
Martha(config)# arp 172.21.5.131 0000.00a4.b74c snap

命令 clear arp-cache 可以从 ARP 表中强制删除所有动态表项,并且此命令也可以清除快速交换高速缓冲区和 IP 路由高速缓冲区中的内容。

代理 ARP

代理 ARP 有时也被叫做混杂 ARP,详见 RFC925 和 RFC1027,代理 ARP 被路由器作为向主机表明自身可用的一种手段。举个例子,主机 192.168.12.5/24 需要向主机 192.168.20.101/24 发送数据包,这两个主机不在同一个网段,因此需要路由器进行转发。但是假设源主机没有配置默认网关信息,因此也就不知道如何到达路由器。

这时,它可以向 192.168.20.101 发送一个 ARP 请求,本地路由器收到这一请求,并且路由器知道如何到达网络 192.168.20.0 网段,因此路由器将回复以上请求,把自己的数据链路标识符作为 ARP 响应数据包中的硬件地址。事实上,路由器欺骗了本地的主机,让它认为路由器的接口就是 192.168.20.101 的接口。最终所有发向 192.168.20.101 的数据包都被发送到路由器。

如下给出了代理 ARP 的另一种用途,需要特别关注地址掩码,路由器配置的掩码是 28 位掩码,而主机配置的是标准的 C 类地址掩码(24 位掩码)。结果是主机并不知道子网的存在,当主机 192.168.20.66 想发送数据包到 192.168.20.25 时,它首先将发送 ARP 请求,这时路由器识别出数据包的目标地址属于另一个子网,因而向请求主机回复自己的硬件地址。这种代理 ARP 使得子网化网络拓扑结构对主机来说是透明的。

在 IOS 系统中,缺省情况下,代理 ARP 功能是打开的。可以在每个接口上使用 no ip proxy-arp 命令关闭此功能。

无故 ARP

主机偶尔也会使用自己的 IPv4 地址作为目标地址发送 ARP 请求,这种 ARP 请求称为无故 ARP。主要有如下用途:

  • 无故 ARP 可以用于检查重复地址。一台设备可以向自己的 IPv4 地址发送 ARP 请求,如果收到 ARP 响应则表明存在重复地址
  • 无故 ARP 可以用于通告一个新的数据链路标识符。当一台设备收到一个 ARP 请求,如果 ARP 高速缓存中已有发送者的 IPv4 地址,那么此 IPv4 地址相对应的硬件地址将会被发送者新的硬件地址所更新
  • 某个子网内运行热备份路由器协议(HSRP 协议)的路由器如果从其他路由器变成主路由器,它就会发出一个无故 ARP 来更新子网上的主机的 ARP 缓存

很多 IP 实现都没有实现无故 ARP,在 IOS 系统中,缺省情况下是关闭的,可以通过命令 ip gratuitous-arps 激活该功能。

反向 ARP

反向 ARP(RARP)可以实现已知硬件地址到 IPv4 地址的映射。某些设备(例如无盘工作站)在启动时可能不知道自己的 IPv4 地址,嵌入这些设备固件中的 RARP 程序可以发送 RARP 请求,其中硬件地址为设备的硬件地址,RARP 服务器将会向这些设备回复相应的 IPv4 地址。

RARP 在很大程度上正在被动态主机配置协议(DHCP)和自举协议(BOOTP)的扩展协议所替代。不同于 RARP,这两种协议都可以提供 IPv4 地址以外的更多信息,而且还可以跨越本地数据链路。

ICMP

Internet 控制报文协议(ICMP)指定了多种消息类型,这些消息的共同目的就是管理网络,详见 RFC792。ICMP 的消息可以分为错误消息、请求消息和响应消息。如下给出了 ICMP 数据包格式,数据包可以通过类型来标识,许多数据包类型都有多个指定的类型,可以通过代码字段来标识。

在 ping 命令中,就使用到了两种 ICMP 消息:Echo 请求和 Echo 回复。

虽然大部分 ICMP 类型都与路由选择功能有关,但是有 3 个类型特别重要:

  • 路由器通告(Router Advertisement)和路由器选择(Router Selection):分别是类型 9 和类型 10,它们用于 ICMP 路由器发现协议(IRDP)。IRDP 协议用于某些操作系统发现本地的路由器(例如 Windows 操作系统)
  • 重定向(Redirect):ICMP 类型为 5,被路由器用于通知主机去往指定目标的网关,是数据链路上的另一台路由器

当为数据链路连接多台路由器时,避免数据包重定向的一个窍门是将每一台主机的缺省网关设置为自己的 IPv4 地址,于是主机对任何目的地址都会发送 ARP 请求,当目的地址不属于本地数据链路时,合适的路由器将会通过代理 ARP 功能回复请求。使用这种策略避免重定向是有争议的,因为重定向会被减少或消除,但是 ARP 流量又增加了。

在 IOS 系统中,缺省状态下重定向功能是打开的,在接口上使用命令 no ip redirects 可以关闭此功能。

主机到主机层

TCP/IP 协议族中的网络层负责网络之间的逻辑路径,而主机到主机层(传输层)为应用程序提供端到端的通信服务。从另一个角度看,主机到主机层向应用提供了一个到协议族下一层的接口,使应用不必关心它们的数据实际上是如何被传送的。

主机到主机层提供两个主要的服务:TCP 和 UDP。

TCP

传输控制协议(TCP),向应用提供了可靠的、面向连接的服务,详见 RFC793。换句话说,TCP 提供了一个类似于点到点的连接,点到点的连接有 2 个特点:

  • 仅存在一条到达目的地的路径。进入连接的数据包不会丢失,因为数据包唯一可去的地方就是连接的另一端
  • 数据包到达的顺序与发送顺序相同

TCP 提供的点对点连接是逻辑上的连接,物理上这条连接并不存在。TCP 使用网络层提供的无连接、尽力而为的转发服务。在无连接服务之上,TCP 使用了 3 种基础的机制实现面向连接的服务:

  • 使用序列号对数据包进行标记,以便 TCP 接收服务在向目的应用传递数据之前修正错序的数据包排序
  • TCP 使用确认、校验和定时器系统提供可靠性。当接收者按照顺序识别出数据包未能到达或发生错误时,接收者将通知发送者;或者接收者在特定时间内没有发送确认消息,那么发送者就认为数据包没有到达接收方。在这两种情况下,发送者都会考虑重传数据包
  • TCP 使用窗口机制调整数据包的流量;窗口机制可以减少因为接收方缓冲区满而造成丢失数据包的可能性

TCP 在应用层数据上附加了一个报头。为了传送数据,应用数据及附加的 TCP 报头被封装在 IP 数据包内。如下显示了 TCP 报头格式:

  • 源端口(Source Port):字段长度为 16 位,为封装的数据指定了源端口
  • 目的端口(Destination Port):字段长度为 16 位,为封装的数据指定了目的端口
  • 序列号(Sequence Number):字段长度为 32 位,序列号确定了被封装的数据在发送方发送的数据流中的位置
  • 确认号(Acknowledgment Number):字段长度为 32 位,确认号确定了接收方下一次希望从发送方接收的序列号
  • 报头长度(Header Length):长度为 4 位,报头长度指定了以 4 字节为单位的报头长度。由于可选字段的长度可变,因此必须通过该字段标识出承载数据的起点
  • 保留(Reserved):长度为 4 位,通常设置为 0
  • 标记(Flag):包括 8 个 1 位的标记,用于流和连接控制。从左到右分别为:拥塞窗口减少(Congestion Window Reduced,CWR)、ECN-Echo(ECE)、紧急(URG)、确认(ACK)、弹出(PSH)、复位(RST)、同步(SYN)和结束(FIN)
  • 窗口大小(Window Size):字段长度为 16 位,主要用于流控制。窗口大小指明了发送方在必须停止传输并等待确认之前,接收方还可以接收的数据段的长度(以字节为单位)
  • 校验和:字段长度为 16 位,它包括对报头和被封装数据的校验和,校验和允许错误检测
  • 紧急指针:字段仅当 URG 标记置位时才被使用。这个 16 位数被添加到序列号上用于指明紧急数据的结束
  • 可选项(Options):字段用于指明 TCP 发送方要求的选项。
  • 填充项(Padding):为了保证报头的长度是 4 字节的整数倍,使用 0 填充该字段

UDP

用户数据报协议(UDP)提供了一种无连接、尽力而为的数据包转发服务,详见 RFC768。UDP 的优点是:不花时间建立连接,直接发送数据。用 UDP 代替 TCP,可以使发送小数据量的应用取得更好的性能优势。UDP 另一个优点是:UDP 报头长度远远小于 TCP 报头长度。

如下显示了 UDP 报头的格式:

  • 源端口(Source Port):字段长度为 16 位,为封装的数据指定了源端口
  • 目的端口(Destination Port):字段长度为 16 位,为封装的数据指定了目的端口
  • UDP 长度(Length):字段长度为 16 位,指定了整个 UDP 数据包的长度,以字节为单位
  • 校验和(Checksum):字段长度为 16 位,该校验和是针对整个 UDP 数据包计算的。不同于 TCP,UDP 的校验和是可选的,当不使用校验和时,该字段全部设置为 0