只要网关一栏为空,路由表就常被误读成坏了,其实那一行本身通常是完整的。via 字段只在内核把数据包交给下一跳地址时出现;绑定到接口的路由则通过链路层解析或点对点对端到达目的地。从左到右读各列就能判断属于哪种情况,而这次读取应当发生在任何人修改默认路由之前。大多数关于网关缺失的工单都止于这一区别。

逐列读一行路由

每条记录都以目标及其前缀长度开头,随后用固定词汇追加修饰项:via、dev、proto、scope、src 和 metric。像 198.51.100.0/24 dev tun0 proto kernel scope link src 198.51.100.2 这样的行虽然密集,但已经完整指定。它什么也不缺,也没有暗示任何路由器地址。

有意思的地方是缺少 via。没有下一跳时,内核直接在指定接口上解析目标,对隧道而言就是把帧交给隧道设备,让封装从那里接手。proto kernel 记录的是该条目在分配地址时自动安装,而不是管理员手输的。scope link 表示该目标在该链路上无需经过路由器即可到达,src 则指明内核会优先选作匹配流量源地址的地址。

现在用同样的方式读一条默认路由:default via 192.0.2.254 dev eth0 proto static metric 100。这里出现 via,是因为数据包确实需要下一跳,而该下一跳自身必须在 eth0 上可达。两行并不是彼此的变体;它们回答的是流量去往何处的不同问题。

两种容易被混淆的路由形态

隧道子网路由和默认路由在截图里看着相似,行为却完全不同。把下表当作读字段的辅助工具,然后在你正在调试的机器上确认实际数字,而不是相信指南里的示例。

Piece of the lineWhat it settlesWhat it says nothing about
The destination and maskWhich packets are eligible to matchWhere those packets leave the host
via plus its addressWhich next hop receives them, and therefore that a router is involvedWhether that router is reachable or sane
devWhich interface carries the encapsulated or raw packetWhether the interface is up or has a carrier
proto and scopeWho installed the entry and how far the destination is considered to beWhether a firewall later drops the traffic

表格说不出来的是意图。经隧道通往私有网段的直连路由是有意设计的分流隧道;同样的形态出现在本应把所有流量都送进 VPN 的主机上,就是症状。意图存在于生成该条目的配置中,所以在断定内核出错之前,先看看是哪个工具安装了它。

同样,缺少网关并不等于可以凭空造一个。如果一条路由确实需要下一跳,该下一跳必须在承载它的接口上可达,而对点对点隧道来说,对端地址与本地地址不是同一个对象。把本地隧道地址当成以太网路由器,是造出一条看起来正确却什么都不转发的路由的常见做法。

问内核它实际会用哪一行

读表和真正执行查找不是一回事,因为内核在回答之前会查询策略规则、多张表和缓存条目。执行真正查找的动词是 ip route get,它会打印具有这些属性的数据包会走的路由。

ip route get 203.0.113.37 from 198.51.100.2 iif tun0

换成你真正关心的目标,然后在流量按源地址选择或被转发时加上 from 或 iif,因为普通本地查找可能选出与被转发客户端不同的路径。答复会给出接口、选定的源地址以及任何下一跳,这正是那一刻数据包被路由时所用的信息。

如果答复让你意外,先打印规则再动手:ip -4 rule show 会按优先级列出它们,而安装了自建表或为带标记数据包设置规则的 VPN 客户端根本不会出现在主表里。列出所有表就能补上这个缺口。

ip -4 route show table all | grep -E 'tun|default'

替换默认路由之前先检查底层

隧道自身的包必须到达远程端点,而它们是经物理接口、使用 VPN 起来之前就已存在的路由做到的。不固定该端点就替换整条默认路由,会把封装后的包送进它们本该建立的那条隧道,连接随即崩溃。先用只针对端点地址的查找确认端点路径。

做改动时把三件事分开:必须留在物理接口上的端点路由、应当进入隧道的私有网段,以及决定其余一切的默认路由。按这个顺序拆开,一次只应用一条,每步之后重跑查找。事后 ping 失败本身说明不了什么,因为权限、回程路由、DNS 和服务本身都在正确路由的下游。

把改动前该行给出的四个值记下来:目标、下一跳或其缺失、设备,以及选定的源地址。如果之后的改动移动了其中任何一个,你就知道究竟改动了哪个决定,也能只恢复一条条目,而不必去猜整张表。

相关文章