路由 TCP/IP 卷一 第三章:Static Routing 总结
一个来自第一章的观察是,不论是物理层/数据链路层,还是网络层/传输层,都是在某种链路上完成报文的传递任务。差别在于,前者是在物理链路上,后者是在逻辑链路上,层次不同。
此外,第一章还展示了,为了在物理路径通信,有一些信息必须要获得,比如链路的标识符或者是封装所需的信息。这些信息存放在 ARP 缓存中。在网络层,这样的准备也是需要的,信息存储在路由表,或者在路由信息数据库(RIB)中。
这章展示了路由表所需的信息、如何记录这些信息、这些信息如何存储的,以及最终它们如何发挥作用的。
理解多种路由协议,最简单的出发点就是静态路由。通过静态路由掌握路由的大致机制,之后便不难理解现代互联网实际采用的多种动态路由协议。
Route Table
为了理解路由表中包含的信息,考察一下一个报文到达路由器会经历什么是很有帮助的。
当一个报文来到路由器,链路层首先会检查 MAC 地址。如果这个地址指向了路由器的接口,或者是广播地址,那么链路层会把报文传递给网络层。网络层再考察 IP 是不是给自己的,要么是广播,要么 IP 指向自己,之后传递给上层适当的应用。
要是网络层发现 IP 不属于上述两种情况,就要进行路由了。因为虽然 IP 没有指向你,但是链路层却指向了你,说明这个包需要你来处理。对于这种包的处理方式有两种,要么它的目的地和本路由器直连,要么在别的子网内。
对包进行路由,需要查看路由表。路由表最少要有两项内容:
- 目的地址,这是这个路由器能够到达的地址。
- 指向地址的指针,这是下一跳路由器的地址。
路由器将会贪心匹配目的地址,按照如下顺序:
- 主机地址
- 子网
- 一组子网(summary route)
- 主网络号
- 一组主网络号(超网,supernet)
- 默认地址
前四种在本章都会涉及。超网在第 6 章讲解,默认地址在第 12 章讲解。
要是目的地址没法被任何一条规则匹配,那么包就会被丢掉,并返回一个 ICMP 错误消息。我之前在 GNS3 配置环境的时候,就是因为忘记配置路由,导致虚拟机内的路由器连不上外网。
路由的过程就像接力一样,路由器查看 IP 报头的目的地址,根据路由表确定下一跳 IP,在 ARP 缓存或者 NDP 缓存中找到对应 MAC 地址,最后发送链路层报文。
在 Cisco IOS 中查看路由的命令是 show ip route。路由表的记录分成许多类型,S 表示静态配置的;C 表示直连的等等。
除了直连的路由外,每个路由还有一组方括号,表示 [管理距离/度量]。前者决定了有不同协议同时提供路由时,听谁的,越小越优先;后者决定了一个协议内部有不同路由时,选择哪个,越小越优先。
Configuring Static Routes
路由表通过如下三种方式获得路由信息:
- 对于直连的子网,它自动获知信息,不需要手动配置;
- 信息可以通过静态路由记录的方式手动输入;
- 信息可以通过某些动态路由协议自动写入。
这本书的重点是动态路由协议,然而静态路由协议的讲解可以作为理解动态协议的基础。而且,在某些场合中,静态路由协议更好,因为它能提供更细粒度的控制。毕竟,自动意味着缺少控制。当然,细粒度的代价是,每次网络的拓扑结构改变时,需要手动修改路由信息。
Case Study: Simple IPv4 Static Routes
静态配置路由分为三步:
- 对于每个网络中的数据链路,识别出所有的子网或者网络地址;
- 对于每个路由器,找出所有非直连的子网;
- 对每个路由器,每个非直连的子网地址对应一条路由记录。
对一个直连数据链路配置路由是无效的,因为在给路由器网卡配置 IP 与子网掩码时已经隐含了这一路由信息。比如,路由器的一个网卡的 IP 是 10.20.30.4 255.255.255.0,路由器就知道通过这个网卡,它和 10.20.30.0/24 直连,路由表中自动会出现一条 C 记录。
当然,在配置静态路由之前,请确保 ip classless 和 ip subnet-zero 两个全局的配置命令被设置到 IOS 中。它们的具体含义将在第 6 章中讲解。
写入静态路由有三种方式:
- 写下一跳的 IP,如
ip route 192.168.1.0 255.255.255.224 192.168.1.193; - 写从哪个端口可达下一跳,如
ip route 192.168.1.0 255.255.255.224 E0; - 前两种方式的组合,如
ip route 192.168.1.0 255.255.255.224 E0 192.168.1.193。
其中,第二种不推荐,除非这个端口是非广播端口,即链路是一对一的,只有一个可能目的地。在这本书里,Serial 表示一对一端口。
如果配置静态路由指向广播接口,同时不配置下一跳地址,路由器会假设这个链路是直连的。这样,每请求这个子网内的一个地址,ARP 协议将会发出,链路上没有主机能够应答,下一跳路由器知道能够到达目的地,会通过代理 ARP 的形式返回信息。
这会造成两点负面作用,一方面会造成链路上大量的 ARP 报文,另一方面会导致路由器的 ARP 缓存中存放了大量内容。
如果在指明端口的同时设定了下一跳路由器的地址,则只会存在目标地址为下一跳路由器的 ARP 流量。而且如果接口失效了,即使下一跳地址可以通过递归查路由表由其他路径可达,该条路由也会被删除。换句话说,就是指明下一跳地址必须通过本接口到达。
后两种配置的方式还有一个好处,可以减少递归查表的次数。即如果一个目的地址匹配到这两种记录,(递归)查表直接结束,而第一种仍然会触发递归查表。递归查表结束有几种触发方式,一种是上文所述的,另一种是 C 记录。鉴于第二种的不足,如果为了减少递归查表的次数,第三种是更好的选择。
Case Study: Simple IPv6 Static Routes
IPv6 静态路由的配置方法和 IPv4 类似,除了 IPv6 前缀的长度是通过 bit-count 方式输入的。在输入一个 IPv6 路由之前,一定要先启用 IPv6 路由,通过 ipv6 unicast-routing 命令。
确定下一跳的地址,可以通过一个细节的网络拓扑图得到,然而它可能会过时。如果通过手动指定 Interface ID,很好,下一跳是可预测的。然而要是使用 EUI-64 来生成 Interface ID,就只需要控制 64 位前缀了。路由器会自动获得后 64 位部分。
一种获得完整 128 位 IPv6 地址的方式是用思科的邻居协议(不是之前讲过的 NDP)。show cdp neighbor detail。另一种是 show ipv6 interface serial0/0.1。具体命令了解即可,现在注重理论知识。
配置路由的方式和 IPv4 的那三种一样。还是推荐第一种和第三种。第二种在 IPv6 中有了进步,IPv6 不再会认为绑定端口的记录是直连的了。但是,该发请求报文还是会发送。而且一个致命的问题是,IPv6 并不存在代理响应。换句话说,路由器知道它可以连接到某个子网的服务器,但接收到另一个子网发来的邻居请求时,它不会代替响应。
最终的结论还是那句话,如果是点对点接口,这种设置没有问题,因为目的地只有一个,不会造成混淆。但如果是广播接口,例如以太网,则必须在设定端口的同时设定 IP 地址,否则肯定不会有响应。啰嗦一句,现在不是大量缓存、大量邻居请求的问题了,而是 100% 接收不到回应,因为 IPv6 不存在代理响应。
推荐和端口一起设定的 IP 地址是这个端口出发的下一跳路由器的链路局部 IP 地址。好处有二,一方面链路局部地址只会在路由器被换掉的时候改变,修改全局前缀都不会影响它;另一方面,这样设定可以和路由器广播出来的地址一致。
关于后者的具体讲解是,如果主机给一个路由器发送了消息,这个路由器发现另一个路由器是更好的下一跳,会转发一个重定向消息,且 ICMP 重定向报文中的目标地址是更好路由器的链路局部地址。接收到这个消息的主机如果发现更好路由器在自己的路由器表中,则记下下次转发的更好方案,否则会忽略这条消息。
所以,要是路由表没有设定为路由器的链路局部地址,这种机制便不能奏效了。
在 IPv6 的路由表记录中,多出来一种 L 类型,表示局部地址,例如 FEC0 开头的链路局部地址。
Case Study: Summary Routes
Summary Routes 的路由匹配优先级是第三名,前面两名是主机地址和子网。它的思想很简单,就是通过减小子网掩码的位数来覆盖相同指向的路由,从而显著降低路由表的长度。比如 10.20.1.0/24 和 10.20.3.0/24 的下一跳路由器 IP 都是 A,那么就可以合并为 10.20.0.0/16。
注意,要确定合并后的范围是个准确概括了合并前的路由,并且没有错误包含其他路由。假如路由表中还有个 10.20.4.0/24 把 IP B 作为下一跳,那上述合并就太激进,造成了路由错误。而子网掩码为 22 仍然是可行的。
Summary Routes 导致的错误在后文 Case Study: Tracing a Failed Route 会给出个例子,更深入的讲解在第 7 章和第 8 章。
Case Study: Alternative Routes
这个例子主要说明了,一个包在发包时走了新路线,但是返回的时候却还是走了老路线。原因是路由器并不知道建立的新链路能够到达包的来源 IP,还以为只有老路线才能到达。给我们的启示是:
- 如果网络的拓扑结构改变了,路由器必须被重新配置以知晓更改;
- 静态路由可以用来创建非常具体的路由模式。
Case Study: Floating Static Routes
一个浮动静态路由起到容灾功能,它相较于一般路由的优先级更低,只有主要路由失效后才会使用它。
配置浮动静态路由的方法也很简单,相当于对同一个目标配置多个路由。对于浮动静态路由,在 ip route 的最后加一个数字,比如 50,来表示它的管理距离。前文讲过,管理距离大,当存在多个路由协议时,大管理距离协议的优先级低,且默认静态路由的管理距离为 1。所以只有主要路由坏了才会轮到浮动静态路由。
第 11 章将会讲解一些动态路由协议的管理距离,所有的动态路由协议的管理距离都远远大于 1。所以,静态路由记录肯定是优先于动态路由记录的。
Case Study: IPv6 Floating Static Routes
这个例子和上个例子说明的事情大致相同。IPv6 的静态路由的管理距离是 1,不管指明的是出口接口还是下一跳地址。当对于同一个目的地出现两种指明方式都存在的路由时,负载分享将会出现。
Case Study: Load Sharing
负载分享解决了上一个例子中备用路线浪费的问题。负载分享有两种方式:
- 相等代价负载分享把流量平均分布在多个具有相同度量的路线中,在这种情形下,负载分享也叫负载均衡;
- 不等代价负载分享根据路径的不同度量,按照度量的反比分配,度量越大的分配的流量越少,反之亦然。
有一些路由协议两种负载分享都支持,但是像静态路由这样没有度量的协议就只支持相等代价的。
配置负载分享的路由很简单,只需要把浮动路由中 ip route 最后的数字省略,这样多条路由有相同的管理距离(1),也有相同的度量(0),实现负载均衡。
Load Sharing and Cisco Express Forwarding
负载分享有两种情况,逐报文或者逐地址。
前者的意思是,报文交替使用路径,第一个使用路径 1,下一个使用路径 2,再一个使用路径 1。
后者的意思是,对于目的地交替使用路径,更准确些,是针对源-目的地对交替使用路径。第一种地址对使用路径 1,第二种使用路径 2,第三种可能再次使用路径 1。
Cisco Express Forwarding 是一个非常高效的交换方式,因为在需要交换某个包之前,所需的所有信息都已经提前准备好了。从上两段的描述中也可以理解,CEF 决策所需信息通过路由表就能获得,而路由表在转发包之前就已经建好。所以 CEF 交换也不需要在接收到包输入后才明确路径。
从空间的角度考察,CEF 根据路由表生成一个 CEF 转发信息库(Forwarding Information Base, FIB)。CEF FIB 和路由表是绑定的,如果路由表是静态的,FIB 也不会改变。除此之外,CEF 还有一个邻接表,用来维护 L2 转发信息,这一信息是通过 ARP 或者邻居发现得到的。
再强调一遍,不论是路由表、FIB 还是邻接表,在转发第一个包之前,它们已经建好了。
CEF 默认执行的是逐地址负载分享,更准确来说,是逐源-目的地址对负载分享。对于 IPv4,CEF 也支持逐包负载分享,但是 IPv6 则只有逐地址方式。
判断 CEF 是否在路由器上全局开启,使用 show ip cef 或者 show ipv6 cef。打开 CEF 的方式是 ip cef 对 IPv4,ipv6 cef对 IPv6。
逐包负载共享采用指令 ip load-sharing per-packet,逐目的地是 ip load-sharing per-destination。对 IPv6,没有设置的必要,因为只有逐地址方式。可以通过 show cef interface 看看在某个接口上应用的转发方式。
全局开启 CEF 后,还需要在某个输入接口上启用 CEF。如果某个包无法通过 CEF 交换,CEF 可以把包用次优方式交换,对 IPv4 默认是快速交换,对 IPv6 默认是处理交换。
Per Destination Load Sharing and Fast Switching
快速交换有以下四个步骤:
- 当一个路由器第一次交换某个包到特定目的地时,它会查看路由表并选定一个出口接口;
- 选定接口的必要数据链路层信息被检索,包被封装并发送;
- 检索的路由和数据链路信息被写入快速交换缓存;
- 随后去往同一个目的地的包将直接利用快速缓存确定链路目的地,而不需要再查看路由表或者 ARP 缓存。
3 和 4 步是快速交换快的原因。不过这种机制也意味着,去往同一个目的地的包都走一条路。快速交换能做到的负载共享顶天了是逐地址,不可能是逐报文。
Per Packet Load Sharing and Process Switching
对于处理交换来说,每接收一个报文输入,路由器都会进行查路由表、选择接口、查数据链路信息这三个操作,这样,去往同一个地址的报文不一定要走一个接口。报文与报文之间是相互独立处理的。
在 IPv4 启用处理交换的命令是 no ip route-cache。对 IPv6 来说,什么也不用做,因为处理交换是默认启用的。
通过 debug ip packet 可以查看 IP 报文信息。
留意,
debug ip packet只会展示处理交换报文。
处理交换能够让报文更均匀地分布在每条链路中,但时间和处理器的利用效率略逊一筹。
Which Switching Method Will Be Used
IOS 的交换决策基于入口端口和出口端口的设定,如果入口端口设定了 CEF,则交换方案是 CEF,不论出口设定的什么。如果入口没有设定 CEF,则取决于出口的设定,出口是 Fast 则交换方案是 Fast,是 Process 则同理。
如果入口没有设定 CEF,但是出口设定了 CEF,这是没有道理可言的。因为 CEF 的设定只有在入口端口才有效。此时,路由器会采用默认交换方案,对 IPv4 是快速交换,对 IPv6 是处理交换。
Case Study: Recursive Table Lookups
路由记录的下一跳地址并不一定是下一跳路由器的地址,可以是另一个子网中的地址。路由器此时会进行递归查表,直到遇到其直连的记录为止,比如一条 C 记录,或者指明了接口的记录等等。
这样设计当然有可取的地方,这种分层的设计可以让你修改某层的时候,其余层都不受影响。具体看书本中这节的例子吧,不再赘述。但是这种修改起来的方便和网络转发速度需要取舍。
Trobleshooting Static Routes
Case Study: Tracing a Failed Route
这个例子描述的情景是,一个路由器 R 连接的子网中的主机 A 可以和另一个子网中的主机 B 通信,但这个路由器 R 却无法 ping 通那台主机。
问题处在报文的返回阶段,链路上的某个路由器知道 A 的路由方式,但却不知道 R 的路由方式。问题在于子网掩码长度对应的子网覆盖了 A 的 IP,但没有覆盖 R 的 IP。这就是错误 Summary Routes 导致的问题。
更细节一些,这个路由器知道 192.168.1.0/27 的路由方式,所以能发包给 A,但 R 的 IP 是 192.168.1.33,属于 192.168.1.32/27 这个子网,路由器中没有对应的路由方式记录。
两种修改方案是,要么修改子网掩码长度,覆盖路由器 R;要么专门为 R 增加一条记录。
心得:当一条路径失效时,检查上行和下行两个方向。
Case Study: A Protocol Conflict
这个例子描述的情景是,在路由器 A 上为某一个主机地址 B 配置了路由后,反而路由不到该主机了。
问题在于,路由器 A 可以通过两个端口,即原始的和新配置的,到达主机地址 B。新配置的路径中有一个路由器 C,接收到 A 的报文后会发出一个目标地址为 B 的 ARP 请求。B 会回应,但 A 代理 ARP 回应的更慢,因为 A 回应的路径上有个网桥。
A 为什么会回应?因为它有两个端口到 B。在一个端口上听到了 ARP 请求,发现它还能从另一个端口到达 B。
C 接收到 B 的响应,记录到 ARP 缓存中,接收到 A 更慢的响应,覆盖了 ARP 记录。最后,发给 B 的报文会在 A B 之间辗转。
解决的方案有两种,一种是写入一个静态的 ARP 缓存记录,不允许被覆盖;另一个是禁用路由器 A 在那个端口上的代理 ARP 响应。
Case Study: A Replaced Router
这个例子描述的情景是,IPv4 和 IPv6 同时启用的网络中,一个路由器被替换后,IPv4 一切正常。IPv6 ping 不通原来路由器的网卡地址 A ,仍然能 ping 通以 A 作为下一跳的某个子网中的主机,
ping 不通原来路由器的网卡地址是因为根据 MAC-EUI64 的规则,原来的网卡地址已经不存在了。能 ping 通子网中的主机,是因为新网卡和旧网卡都在同一个子网中,通过递归查表,能够确定通过 Serial0/0.2 发出。这个端口是个点对点端口,只有一个目的地,因此不会像以太网一样出现和响应有关的各种问题。
1 | |
这里讲解一下递归查表的进行方式。要给目的地址 FEC0:0:0:A::1 发消息,先匹配了最后一个记录。如果不理解为什么,请用二进制比对一下两个地址。
为什么匹配到最后一个记录后不停止?因为这既不是一个 C 记录,也不是绑定了端口的记录。再次递归查表,符合第三条,而且是个 C 记录,直接确定了从 Serial0/0.2 发出,递归结束。
不过还没确定链路层的地址呢?好在这是个点对点接口,只有一个邻居,因此可以确定邻居的链路层地址。
这里你可能会疑惑,诶,不是说网卡已经换了吗,现在找不到原来对应的邻居地址了。非也,经过刚才递归查路由表的分析,我们知道,送到邻居发现协议的消息是 FEC0::3 这个子网地址,和原来的那个接口已经没关系了。这也就是为什么课本强调新旧两个路由器接口虽然接口 ID 不同,但都处在相同子网的原因。
再啰嗦一下,现在不应该对邻居发现协议如何发现旧的网卡地址有疑问,因为送到邻居发现协议的不是旧的网卡地址,而是子网地址。
你可能还会想,为什么最终匹配的记录中,没有下一跳地址,而是 via ::?之前不是说 IPv6 路由必须要配置和端口匹配的 IP 吗?这是因为这条记录是 C 记录,是路由器自动配置的,而不是我们手动写入的 S 记录。
最后的问题是,凭什么点对点接口就能够确定邻居?底层的原理是什么?说实话,我不是很清楚,但我想,既然只有一个邻居,发个链路层的广播消息也许就没问题了。
Case Study: Tracing An IPv6 Failed Route
这个例子描述的情景是,一个构成三角形的三个路由器,给某个子网发消息,时通时不通。
原因在于每个路由器都可以通过两个途径到达目的子网,路由器会进行负载共享。如果发送来自某个端口,并转发给了另一个端口,则通信成功;如果从这个端口原路返回,则通信失败。
