路由 TCP/IP 卷一 第六章:RIPv2, RIPng and Classless Routing 总结
RIPv2 在 RFC 1723 中定义,不过现在最新的 RFC 是 2453。
RIPv2 不是一个新协议,不配有一个类似于 Enhanced RIP 这样的新名字,只配称之为版本二。RIPv2 是对 RIPv1 的一些扩展,并令其适应现代路由环境。总的来说,它带来了以下这些扩展:
- 每个路由记录配有子网掩码;
- 路由更新的认证机制;
- 每个路由记录带有下一条地址;
- 外部路由标记;
- 组播路由更新。
其中最重要的扩展是子网掩码字段,它让 RIPv2 成为了一个无类别的路由协议。
RIPv2 是这本书讲到的第一个无类别路由协议,因此,这一章作为无类别协议和 RIPv2 的介绍。
RIP 下一代(RIPng) 是为了让 RIPv2 适应 IPv6 做出的修改。IPv6 本身就是无类别的,不存在什么 A 类 B 类 C 类地址,或者是类似的地址组。所以 RIPng 也是一个无类别路由协议。
Operation of RIPv2
RIPv2 的所有操作流程、计时器和稳定性功能(分割地平线、限制计时器、触发更新间的停顿等等)和 RIPv1 一致,唯一的区别在于广播更新。
RIPv2 采用组播更新通知其他使用 RIPv2 的路由器,使用保留 D 类地址 224.0.0.9。组播的优势是,其他不在意 RIP 路由信息的路由器不需要花时间拆开广播包。组播更新会在 Compatibility with RIPv1 中进一步分析。
看完了 RIP 的消息格式如何被版本二使用,本节主要关注这些扩展特性的好处。
RIPv2 Message Format
1 | |
这是新的 RIPv2 消息格式。和第一版相比,许多未使用的字段都被利用起来。和版本一一样的地方是,一个报文最多承载 25 条路由记录,以及运行在 UDP 的 520 端口上。
- Command 要么是 1,表示请求消息;要么是 2,表示响应消息;
- Version 对 RIPv2 来说是 2。如果设定为 0,或者设定为 1 但不是合法的 RIPv1 格式,包会丢掉。RIPv2 会处理合法的 RIPv1 报文。那,反过来呢?后面的兼容性章节会展开讲解。
- Address Family Identifier 对 IPv4 是 2,唯一的特例是请求一个路由器的完整路由表,此时设定为 0。
- Route Tag 提供了一个字段,用来标记外部路由或者记录重分配到 RIPv2 进程的外部路由。对此字段的一个建议用法是记录引入这条路由的外部协议所在的 AS 号。即使 RIP 不使用这个字段,外部协议可能会利用它穿过 RIP 区域交换信息。
- IP Address 是路由目的地的 IPv4 地址,可以是主网络号、子网或者主机路由。
- Subnet Mask 是 32 位的子网掩码,表示 IPv4 地址的子网部分,这个字段的重要性在 Variable-Length Subnet Masking 中讲解。
- Next Hop 表示一个更好的下一跳地址,前提是发送这条路由的路由器并不是最好的下一跳。如果字段设定为全 0,这发送这条路由的路由器就是最好的下一跳。
- Metric 是跳数,1 - 16 之间。
详细解释一下上面的某些字段。
Route Tag 的作用主要在后续章节中讲解,即 Part III 的某些章节。RIP 协议只能在使用 RIP 协议的路由器之间交换路由信息,如果一个网络中,并不是所有的路由器都使用 RIP 协议,就需要通过重分布来交换信息。
每种路由协议就像一门语言。说中文的人之间可以交换情报,说英文、日语、法文的同理。它们之间各自有小圈子。现在如果想让他们协作起来,就需要有一个翻译,把中文的信息翻译为英文,这就是重分布的作用。
Next Hop 在重分配中就有利用。一个只会说 OSPF 的路由器如果是一个只会说 RIP 协议的下一跳,则后者永远无法知道前者的存在。然而,如果链路上还有一个同时运行两种协议的路由器,就可以通知 RIP 路由器,在 Next Hop 中写入 OSPF 路由器的地址,告知它更好的下一跳。
上面引用部分讲述的道理就是课本上的那个例子。就算在同一个链路上,如果路由器之间运行的协议不同,一方永远无法直接知道另一方是更好的下一跳,必须经过第三方翻译的告知。
Compatibility with RIPv1
RIPv1 处理更新很灵活。如果版本字段显示报文是版本 1,但是未使用位不是全 0,则更新被舍弃。如果版本比 1 大,那些在版本 1 中未使用的字段被忽略,消息被处理。所以更新版本的 RIP 可以向后兼容。
这里需要指出,RIPv1 不一定会忽略 RIPv2 报文中未使用的字段,可能会利用它们,如子网掩码等。
在古老的 IOS 11 版本中,课本的描述是正确的;但在我模拟器的 IOS 16 版本中,一个“只发送 RIPv1,同时接收 RIPv1 和 RIPv2”(即默认行为)的路由器并不会忽略 RIPv2 消息中的子网掩码。
这就导致课后 Troubleshooting 的答案 “RTC 会把 v2 报文中的地址理解为本地链路地址” 这一说法错误,实际上由于 RTC 处理了子网掩码,所以能够区分 v2 报文中的地址。
而且课后题第 2 问的端接口似乎写错了,导致我沉思了好几分钟。
RFC 1723 定义了一个有 4 种选项的兼容开关,允许版本 1、2 之间协作:
- RIP-1,只发送 RIPv1 消息;
- RIP-1 Compatibility,导致 RIPv2 广播消息,而不是组播,这样 RIPv1 的路由器能接收到它们;
- RIP-2,其中 RIPv2 的消息有组播目的地址 224.0.0.9;
- None,没有更新会发送。
RFC 推荐上面的设置可以逐接口设定,Cisco 对前三条的实现在 Configuring RIPv2 中会讲解,第四条通过使用 passive-interface 实现。
此外,RFC 1723 还定义了接收控制开关,控制更新的接收。有 4 种推荐的设置:
- RIP-1 Only
- RIP-2 Only
- Both
- None
这个开关也应该是以接口为单位配置。1 - 3 条的设定在后文会展示,设定第四条可以通过过滤 UDP 源端口 520,且不允许这个接口的流量来实现,或者在 13 章中有更细节的讲解。
第 1 种实现方法的限制是,其他端口不能和正在配置的端口连接同一个子网,否则那些端口的 RIP 流量也无法进入。因为 RIPv1 和 RIPv2 没有进程编号这一概念,一个路由器上只能有一个 RIP 进程,那过滤规则也是全局适用,针对所有端口的。
Classless Route Lookups
上一章讲到,有类别的路由查找需要先匹配主网络号,再匹配子网。要是这两步中哪一步失败了,则匹配失败。
无类别路由查找不在意目的地址的类别,只是通过一位一位地比较来得到目的地址和已知路由之间最好的匹配。这个能力在处理默认地址时很有用,第 12 章会讲解。
Classless Routing Protocols
无类别路由协议的确定性特征是在路由广播中携带子网掩码的能力。路由 + 掩码的好处是全 0 和全 1 的子网现在可以使用了。
可以使用全 0 和全 1 的子网与主机个数为 $2^n - 2$ 不矛盾。主机部分永远不能全 0 或者全 1。
在第 1 章中,我们说这两种子网无法使用,是因为路由器无法区分到底是全 0 子网还是主网络号,以及是在子网范围内的广播还是主网络号范围的广播。
有了子网掩码,这一不同可以被区分。当然,即使能够区分,Cisco 默认的行为是禁止全 0 子网。禁止这一限制的方法是 ip subnet-zero。
子网掩码带来的更大好处是,路由可以分配可变长度的子网掩码。引入的新技术是聚合地址,在下一章会讲到。
Variable-Length Subnet Masking
如果每个目的地址都配有子网掩码,那没有理由要求子网掩码的长度必须相同。这一理念是 VLSM 的基础。
课本举了一个简单的 VLSM 应用。其中,一个 C 类地址 192.168.50.0/24 可以被按主机数量划分给不同类型的子网,大大提高了地址的利用率。
具体的划分过程就不复述了。其中的思想类似于 Huffman 树,即前缀码。当一个子网占用了某个前缀后,后续的子网只能利用未使用的前缀。
你会想,凭什么一定要按照前缀码的方式来划分,使得不同子网之间没有交集、泾渭分明。我就想让不同子网之间有交集,会怎么样?
举个例子,对于
127.10.0.0/16这个地址,我利用其中127.10.128.0/17和127.10.192.0/18这两个地址表示我管辖的网络中两个不同的子网。这两个子网可以自由使用其剩余主机位的所有空间给主机分配地址。问题来了。现在有个目的地址为
127.10.193.233的报文来了。转发给哪个子网?如果只有这些信息,管理员不知道该转给谁,不过路由器知道。根据最长匹配原则,会转发给 18 位子网掩码的子网。但这不一定正确,因为这地址可能是 17 位子网里主机的地址。总结一下,重叠子网导致的问题是 IP 地址的不唯一性。这里就举这一个异常,至于说 ARP 方面、环路方面会不会有其他问题,留给读者自行想象。要是所有 IP 相关的协议都使用地址 + 子网掩码的方式来确定主机,问题就不会出现了。
有关 ARP 方面的问题,后续 Troubleshooting 的第一个例子会涉及。
话说回来,之前提到,有类别协议和子网并不冲突。如果一个路由协议是 Classful 的,子网划分也是可以实现的。但是,可变长度的子网划分无法实现。
上面提到的 192.168.50.0 这个地址,如果按 25 位子网掩码划分,就只能产生两个子网。不过,如果是可变长度的子网掩码,则 25 位、26 位、28 位、30 位等等均可。
课本还给出了另一个例子,主要阐述对于串行链路这种只需要两个主机地址的场景中,选择一个子网掩码位数小的子网(如 24 位),把它再次划分为多个子网掩码 30 位的子网,能够大大提高地址利用效率、节省主机地址。
Authentication
1 | |
每个路由协议都要考虑安全问题:接收到的路由信息是否来自于可信来源。一个攻击者可能给路由器发送更新,把本来该转发给某些目的地的报文转发给攻击者,实现报文嗅探的功能。当然,更无聊的非法更新可能来自于一个故障的路由器。
RIPv2 认证的方式来自于利用第一个路由记录部分,这样,有认证的 RIPv2 报文最多只能携带 24 个路由记录。
地址族标识符的 0xFFFF 表示这条路由记录用来进行认证。认证类型中,0x0002 表示简单明文密码认证,剩下的 16 个字节携带一个最大长度为 16 的密码。
当然,明文认证很不安全,如果一个嗅探器能捕获到 RIPv2 更新消息,它也就能捕获明文密码。
Cisco IOS 通过第一个路由和最后一个路由的位置实现了 MD5 认证。MD5 是一个单向的消息概要或者叫加密哈希函数。有时候它也叫做加密校验和,因为它和算数校验和有类似的机制。
MD5 会对任意长度的明文信息和一个密码计算一个 128 位的哈希值,这就是这条消息的指纹。接收者知道同样的密码。如果它通过计算明文消息和密码得到的哈希值和接收到的相同,则认证通过。
MD5 不是一种加密算法。因为加密意味着解密,MD5 无法实现复原原有消息的功能。
Operation of RIPng
RIPng 基于 RIPv2,不过它不算是 RIPv2 的扩展,因为它完全是一个独立的协议。RIPng 只支持 IPv6,所以如果有同时在 IPv4 和 IPv6 路由的需求,需要为 IPv6 启用 RIPng,对 IPv4 启用 RIPv1 或者 RIPv2。
RIPng 和 RIPv2 大同小异,比如时钟、过程和消息类型。时钟的时间以及跳数作为度量都是一样的。路由消息大部分也是组播的,利用 FF02::9 这个地址。记得它是什么吗?它表示本地链路所有的 RIPng 路由器。
一个差别是,RIPng 的认证部分有所区别,它不需要靠自己实现认证功能,而是借助 IPv6 的实现。
1 | |
上面是 RIPng 的报文格式。
- Command 用 1 表示请求;2 表示响应;
- Version 是 1,表示这是 RIPng 的版本 1;
- IPv6 Prefix 是 128 位的 IPv6 目的地址前缀;
- Route Tag 也是用来传输其他协议的外部路由;
- Prefix Length 表示地址前缀的有效部分,比如表示子网掩码的位数;
- Metric 是跳数,只不过因为最大只有 16,字段的宽度相较于 RIPv1 和 RIPv2 小了些。
对于下一跳路由器的地址,RIPng 采用和 RIPv2 相同的表示方式:非 0 表示当前字段的地址才是更好的下一跳,而不是路由信息的发出者。
区别在于,RIPng 并没有对每条路由都设计下一跳字段,它采用打包的方式。有一个特殊记录用来表示下一跳地址。在这个特殊记录后,且在下一个特殊记录前(或者直接到消息末尾)的所有路由记录都以这个特殊记录的地址作为下一跳。
下面是这个特殊记录的格式,它和一般路由记录的区别是,Metric 部分是全 1。
1 | |
Configuring RIPv2
RIPv2 只是 RIPv1 的扩展,所以有关计时器、邻居单播更新等例子是完全一致的。在看完一个简单的 RIPv2 配置命令后,我们来看一些扩展设置。
Case Study: A Basic RIPv2 Configuration
默认来说,Cisco 路由器只发送 RIPv1 消息,但监听 RIPv1 和 RIPv2 的信息。这一行为可以通过
1 | |
其中的 version 2 来修改。这样,路由器只会发送和接受 RIPv2 的消息。
改成 version 1 则路由器只会发送和接受 RIPv1 的消息。
恢复路由器的默认行为,用 no version。
Case Study: Compatibility with RIPv1
上面讲过,RFC 2453 提议了一个接口级别的兼容开关。Cisco 实现这一开关的方式是 ip rip send version 和 ip rip receive version。
课本给出了一个网络,其中有的路由器用 RIPv1,有的用 RIPv2,还有一台只会 RIPv1 的主机。
不同端口有不同的配置,下面展示的是中心的 Taos 的配置。如下所示。
1 | |
最下方的配置块表明当前路由器全局的配置是 RIPv2。端口 2 没有特别的设置,所以发送和接收都采用 RIPv2。
端口 1 的发送端口兼容 RIPv1 和 RIPv2。和 RFC 倡导的 RIPv2 消息广播发送不同,Cisco 采用先后发送两个版本报文的方式。RIPv2 的报文还是组播,RIPv1 的报文才是广播。
端口 0 的发送和接收报文都是 RIPv1 的。
课本还指出,这样的配置会导致问题。在 E1 端口连接的子网中,有一台只用 RIPv2 的路由器,和那台只会 RIPv1 的主机。现在主机学不到 RIPv2 路由器直连的路由,由以下两点原因造成。
- RIPv2 路由器组播的消息,RIPv1 接收不到(其实也能收到);
- Taos 有分割地平线原则,不会把从 RIPv2 那里学到的内容通过同一端口转发给 RIPv1 主机。
解释一下第一点。之前不是讲了,RIPv1 能够处理 RIPv2 的格式吗?不是向后兼容吗?
要理解两件事。第一个,运行 RIPv1 的主机是否会监听
224.0.0.9这个组播地址?第二个,这个主机是否遵循 RFC,从而忽略 v2 报文的 unused 字段,而不是认为 v2 版本非法?
如果要弥补 RIPv1 路由器接收不到路由这一问题,有两种解决方式。一种是让 RIPv2 路由器用两个版本的协议分发路由信息;另一种是把 Taos 的分割地平线关掉。
此外,在练习题中提到了另一种方式,是把 RIPv1 主机在 Taos 中添加为 neighbor,不过这需要确保只听得懂 RIPv1 的主机能够遵循 RFC 忽略 v2 报文的字段,而不是认为 v2 报文是非法版本。
Case Study: Using VLSM
还是刚刚的网络,这次,一个 192.168.250.0/24 的地址空间被分配给例子中的网络。首先把子网掩码扩展到 28 位,划分一些子网。第一步,某些子网被使用。第二步,某些 28 位的子网可以合并成 26 位的来使用,这个例子展示了地址聚集。第三步,28 位的子网可以被扩展为多个 30 位的子网,分配给串行链路。
这就是 VLSM 的好处,相较于有类别的路由,能更加充分地利用地址空间。
Case Study: Discontiguous Subnets and Classless Routing
之前讲,无类别路由对不连续的子网无法处理。有类别的路由不需要担心这一问题。
在上一个例子的基础上,本例刻意添加了不连续的子网。由于广播路由的时候会带上掩码,所以子网可以被正确广播。不过 RIPv2 默认的行为是在网络边界进行路由汇总,关闭它才能在子网间正确转发路由信息,通过 no auto-summary。
本例还讲到了一件事,即虽然 RIPv2 的兼容模式会给 RIPv1 的 Linux 主机发送 RIPv1 的路由信息,但只会发送和发送端口子网掩码长度一样的路由记录。因此有些路由 Linux 主机学不到。不过经过分析这些子网地址,Linux 主机能够认出它们不属于自己的子网,会把报文发送给 Taos。
这里我有些不太理解。我推测 Linux 主机知道把不属于自己子网的报文转发给 Taos 基于默认路径。
Case Study: Authentication
思科实现的 RIPv2 消息认证包括简单密码认证和 MD5 认证。总的来说,分成下面几步。
- 用某个名字定义一串钥匙链;
- 定义钥匙链上的一个或多个钥匙;
- 在接口上启用认证,并明确要使用的钥匙链;
- 指定接口使用明文或 MD5 认证;
- 可选配置钥匙管理。
整体来看,比较简单。我们看下面这个比较完整的配置例子。
1 | |
先定义了一个钥匙链,钥匙链上的钥匙自小到大使用第一个合法的钥匙。每个钥匙的合法接收时间和合法发送时间是分开设置的。每个钥匙和合法时间有重叠,推荐使用时间同步协议来管理。
对于接口,分配地址后,要指明钥匙链和认证方式。
Configuring RIPng
RIPng 的配置过程和 RIPv1 和 RIPv2 类似,不过指令差别很大。在后面的案例研究中,我们关注流程中类似的地方,以及指令有差异的地方。
Case Study: Basic RIPng Configuration
RIPng 和前面两个 IPv4 的 RIP 版本最大的区别在于一个路由器可以运行多个 RIPng 进程,且进程之间不会相互干扰。这和 EIGRP 是类似的。
之前两种 RIP 协议的配置方法是 router rip 启用 RIP,之后指定版本和网络信息。之后在每个接口上配置兼容开关或者认证部分。RIPng 不一样,启用 IPv6 路由后,可以按端口为单位启用 RIPng。比如下面的例子。
1 | |
在某个端口上启用 RIPng 进程后,有两个效果。第一个效果是某个名字的 RIPng 进程创建了,此外在配置部分中 IOS 自动为其创建了一条记录(ipv6 router rip process-name)。
如果想让一片网络中运行多个进程,一定要注意使用不同的端口号。同一个端口号的 RIPng 进程之间会交换信息,反之亦然。另外,每个进程的名字是本地的,路由器之间不交换。就算两个路由器创建的进程名字不同,只要端口号一样,它们就会交换信息。
复习一下,RIPv1 和 RIPv2 默认的端口号是 520,RIPng 默认的端口号是 521。我们可以使用 522、523 等端口号分配给其他 RIPng 进程。
Case Study: RIPng Process Customization
本节讲了一些自定义 RIPng 参数的方式。可以修改计时器、最大负载均衡路径数量和管理距离等等。
修改的时候应该注意,计时器应该所有路由器统一,危害之前讲过了。另一个地方在于管理距离,它虽然只对本地有效,但会改变路由器选取路由的偏好。有着更小管理距离的路由将会被写入路由表,它失效了,更高管理距离的路由才会写入。
恢复默认设置,在参数配置命令前加 no 就好了。
Case Study: Metric Manipulation
和 RIPv1 以及 RIPv2 一样,RIPng 也可以通过偏移量的方式修改路由的度量。差别在于,RIPng 不根据一个 access-list 修改链路上路由的度量,而是以接口为依据修改度量。看看配置的命令。
1 | |
这段命令的意思是,对于所有输入接口 1 的路由记录,如果属于 smallMountain 进程(注意一个路由器可以运行许多 RIPng 进程),则度量在原来的基础上加 3。
对比一下,RIPv1 或者 RIPv2 允许在输入和输出方向修改度量,但 RIPng 只允许在输入方向上修改。想想,如果只能保留一种修改方式,为什么保留输入方向的更好?
Case Study: Route Summarization
和 RIPv2 一样,在给邻居广播路由之前,可以把子网掩码更长的地址汇总为长度更小的。看看这个配置命令。
1 | |
这条命令就把子网是 10、11、12、13 的都合并成了一条,极大降低了内存占用。
Troubleshooting RIPv2 and RIPng
有两个常见的 RIPv2 配置问题,一个是不匹配的版本号,一个是错误配置的认证。这两个都可以很轻松地通过 debug ip rip events 发现。
对 RIPv2 和 RIPng,更准确地说,是对所有无类别路由协议来说,更可能的问题来源是错误配置的可变长子网掩码。VLSM 不难,但要是没仔细配置,可能会导致不寻常的路由难题。
Case Study: Misconfigured VLSM
本节讲述了一个错误配置的子网掩码问题。问题是,某台机器 B 无法和链路上其他机器 C 交流,而同一链路上 A C 之间交流正常。
因为发生在本地链路,所以看看 ARP 缓存。机器 B 的 ARP 缓存正常,但 C 却把链路上某台路由器的 MAC 地址当作了 B 的地址。这也能解释为什么 C ping B 第一次成功,但后续失败,因为 B 一开始给出的 ARP 响应被路由器的覆盖。路由器响应更慢是因为它需要查路由表。
查看 C 的路由表,发现以下两条路径很关键。
1 | |
| Representation | Decimal |
|---|---|
| 10101100000100110010001101001000 | 172.19.35.72/25 |
| 10101100000100110010001101000000 | 172.19.35.64/27 |
机器 B 的 IP 是 172.19.35.72/25。比对二进制,就理解了路由器的选择。当它接收到 ARP 请求时,查路由表,通过最长匹配原则,发现子网掩码长度是 27 的路由更精确,且端口和接收到 ARP 请求的端口不是同一个,便返回 ARP 响应。
这样,本来发给 B 的报文被转发给这个路由器,并定向到本地链路以外。这有可能有两个结果:如果链路外的那个子网中没有主机 IP 和 B 的相同,则报文丢失,无人响应;否则,那台主机可能会响应,更难调试。
再分析一下子网的分配,看看为什么会有问题呢?
| Binary Representation | Dotted Decimal |
|---|---|
| 11111111111111111111111111111111 | 255.255.255.224 |
| 10101100000100110010001100000000 | 172.19.35.0/27 |
| 10101100000100110010001100100000 | 172.19.35.32/27 |
| 10101100000100110010001101000000 | 172.19.35.64/27 |
| 10101100000100110010001101100000 | 172.19.35.96/27 |
| 10101100000100110010001110000000 | 172.19.35.128/27 |
| 10101100000100110010001110100000 | 172.19.35.160/27 |
| 10101100000100110010001111000000 | 172.19.35.192/27 |
| 10101100000100110010001111100000 | 172.19.35.224/27 |
表格的 1 - 5 行被合并为 25 长度的子网,分配给骨干以太网。128 和 160 被两个 remote 子网使用,192 被分成 30 长度的子网,用来给串行链路编址。现在就剩下 224 没使用了,可剩下的两个 remote 子网却选择了 64 和 96,导致冲突。毕竟这两个子网已经被骨干以太网使用了。
修复的方式有两种,第一种是重新给以太网编址,牺牲一些主机空间。或者是给那两个有问题的 remote 子网重新编址,牺牲一些它们的主机空间。
最后本例的解决方案是,把 224 这个子网扩展到 28 位 分配给那两个 remote 子网。
也可以选择其他解决方案,但本例选择了第二种。因为这个 Case Study 是基于真实案例的,所以课本讲述了真实案例中的解决方案。
