Linux 路由排障:流量到底走了哪条路?

Linux 路由排障:流量到底走了哪条路?

在排查网络问题时,我们经常会遇到这类疑问:

  • 访问这个 IP,到底从哪个网卡出去?
  • 使用的是哪个网关?
  • 为什么明明配置了默认路由,流量却走了 VPN?
  • 同一个目标 IP,为什么不同源地址走的路径不一样?
  • ip route 显示正常,为什么实际还是访问失败?
  • 宿主机和容器访问同一个 IP,为什么结果不同?

最简单的回答是:

ip route get <目标IP>

但严格来说,"访问某个 IP 走什么路由"并不是一个单层问题。完整排查通常需要回答四个不同层次的问题:

  1. Linux 内核选择了哪张路由表和哪条路由;
  2. 数据包从哪个网卡、使用哪个源 IP、经由哪个网关发出;
  3. 数据包离开本机后,经过了哪些中间网络节点;
  4. 实际业务流量是否真的按照预期路径发出。

本文将从最常用的 ip route get 开始,逐步讲清 Linux 的路由选择机制,以及多网卡、策略路由、VPN、容器和复杂网络环境下的排查方法。掌握这套方法后,大多数"为什么流量走错网卡""为什么 VPN 抢走流量""为什么容器与宿主机结果不一致"等问题,都可以被快速定位。


一、最快的判断方法:ip route get

假设要判断访问 8.8.8.8 时使用什么路由:

ip route get 8.8.8.8

典型输出如下:

8.8.8.8 via 192.168.1.1 dev enp3s0 src 192.168.1.100 uid 1000
    cache

这行输出已经包含了最关键的信息:

字段 含义
8.8.8.8 目标 IP
via 192.168.1.1 下一跳网关
dev enp3s0 出口网卡
src 192.168.1.100 内核选择的源 IP
uid 1000 发起查询的用户 ID
cache 当前解析出的目标路由状态

因此,这条结果可以理解为:

从本机访问 8.8.8.8 时,数据包将使用源地址 192.168.1.100,通过网卡 enp3s0,交给下一跳网关 192.168.1.1。

ip route get 并不是简单地从路由表中搜索一行文本,而是让内核按照真实的路由选择逻辑执行一次查询。它返回的是"内核最终会怎样发送这个数据包",但不会真的向网络发送数据包。

直连网络的情况

如果目标和本机处于同一个二层网络,结果可能没有 via:

192.168.1.50 dev enp3s0 src 192.168.1.100

这表示:

  • 目标通过 enp3s0 直接可达;
  • 不需要经过三层网关;
  • 系统会通过 ARP(IPv4)或邻居发现协议(IPv6)查找目标设备的链路层地址。

判断时可以记住:

存在 via:通过网关转发
没有 via:目标通常位于直连网络

二、ip route get 和 ip route 有什么区别?

查看当前主路由表通常使用:

ip route

或者:

ip route show

例如:

default via 192.168.1.1 dev enp3s0 proto dhcp metric 100
192.168.1.0/24 dev enp3s0 proto kernel scope link src 192.168.1.100
10.0.0.0/8 via 192.168.1.254 dev enp3s0

ip route show 展示的是路由表中的已有配置,而 ip route get 会执行一次真实的内核路由解析。换句话说:

ip route show:有哪些路由?
ip route get:访问这个具体目标时,最终使用哪条路由?

在简单的单网卡主机上,两者看起来差别不大。但在下列场景中,只查看 ip route 很容易误判:

  • 存在多张路由表;
  • 使用策略路由;
  • 多网卡配置了不同源地址;
  • VPN 使用 fwmark 控制流量;
  • 存在 VRF;
  • 程序运行在容器或网络命名空间中;
  • 路由选择与源端口、目标端口或协议有关。

因此,排查一个具体目标时,应优先使用:

ip route get <目标IP>

三、源地址是怎么选出来的?

ip route get 输出中的 src 字段往往被忽略,但"用哪个源 IP 发出"恰恰是多网卡和 VIP 场景下最常见的坑。

内核为出站数据包选择源地址的顺序大致是:

  1. 路由条目显式指定的 src 属性优先。例如路由 10.0.0.0/8 via 192.168.1.254 dev enp3s0 src 10.0.0.20,就明确要求使用 10.0.0.20 作为源地址;
  2. 路由没有指定 src 时,从出口接口的地址中选择。内核会优先选择与目标处于同一子网的接口地址;scope 更小的地址也更受偏好(host < link < global);
  3. 仍有多个候选时,按 RFC 6724 的地址选择规则进一步排序(偏好相同 scope、偏好与目标前缀一致等)。

这也是为什么"指定源地址"和"不指定源地址"查询结果可能完全不同:

ip route get 8.8.8.8
ip route get 8.8.8.8 from 10.0.0.20

后者的 src、出口接口甚至路由表都可能发生变化。

一个相关的经典现象是:当服务使用绑定在 lo 上的 VIP(如 keepalived 或云内网 VIP)作为源地址时,ip route get 可能返回 dev lo——此时需要为 VIP 流量配置策略路由指定实际出口,否则会出现"服务能监听、但主动外联的流量路径诡异"的问题。


四、Linux 到底是怎样选择路由的?

可以把 Linux 的路由选择过程简化为下面这条链路:

网络命名空间或 VRF
        ↓
策略路由规则 ip rule
        ↓
选择某张路由表
        ↓
最长前缀匹配
        ↓
比较路由优先级或 metric
        ↓
选择下一跳、出口网卡和源 IP

这比"找默认路由"复杂得多。

1. 首先匹配策略路由规则

Linux 维护了一套 Routing Policy Database,简称 RPDB。管理员通过 ip rule 管理这些规则。

查看当前规则:

ip rule show

普通系统通常会看到:

0:      from all lookup local
32766:  from all lookup main
32767:  from all lookup default

这些规则的含义是:

  1. 优先查询 local 表;
  2. 如果没有找到结果,再查询 main 表;
  3. 最后查询 default 表。

规则编号越小,优先级越高。内核会按照规则优先级依次执行,匹配到能够返回有效路由的规则后结束查询。

策略规则不仅能匹配目标 IP,还能根据以下信息决定路由:

  • 源 IP;
  • 目标 IP;
  • 入接口;
  • 出接口;
  • 防火墙标记 fwmark;
  • 用户 ID;
  • IP 协议;
  • 源端口;
  • 目标端口。

这也是为什么在复杂系统上,"只看目标地址"有时并不能得到真实答案。

2. 然后查询对应路由表

Linux 支持多张路由表。常见的内置表包括:

路由表 ID 用途
local 255 本机地址、广播地址等本地路由
main 254 普通路由默认所在的表
default 253 默认保留表,通常为空

查看主路由表:

ip route show table main

查看本地路由表:

ip route show table local

查看全部路由表:

ip route show table all

查看指定编号的表:

ip route show table 100

路由表名称通常配置在:

/etc/iproute2/rt_tables
/usr/lib/iproute2/rt_tables

普通路由默认进入 main 表,而本机地址和广播地址等由内核维护在 local 表。只有启用策略路由后,多张自定义路由表才会大量参与实际选路。

3. 路由表内部执行最长前缀匹配

假设存在以下路由:

default via 192.168.1.1
10.0.0.0/8 via 192.168.1.2
10.20.0.0/16 via 192.168.1.3
10.20.30.40/32 via 192.168.1.4

访问:

10.20.30.40

最终会匹配:

10.20.30.40/32 via 192.168.1.4

因为 /32 比 /16、/8 和 /0 更具体。

访问:

10.20.50.60

则匹配:

10.20.0.0/16 via 192.168.1.3

这就是最长前缀匹配:

匹配范围越精确的路由,优先级越高。

因此,一个常见误区是:

默认路由的 metric 更低,所以一定走默认路由。

这是错误的。路由首先比较前缀长度,只有在路由同样具体时,metric 才通常用于进一步选择。在常见路由配置中,数值较低的 metric 更优先。

4. 多路径路由(ECMP)

当同一前缀配置了多个下一跳时,就形成了 ECMP(等价多路径):

ip route add default \
  nexthop via 192.168.1.1 dev eth0 weight 1 \
  nexthop via 192.168.2.1 dev eth1 weight 1

此时:

  • ip route get 会列出所有候选 nexthop,而不是其中某一个;
  • 真实数据包则按流的哈希(基于源/目标 IP、协议、端口等字段)选择其中一个 nexthop 发出。

因此,在 ECMP 环境下,用 ip route get 加上 sport/dport/ipproto 模拟具体的流,可以更接近地观察某一类流量实际会命中哪个下一跳:

ip route get 203.0.113.10 ipproto tcp sport 40000 dport 443

五、多网卡环境必须指定源 IP

假设服务器拥有两块网卡:

eth0:192.168.10.20
eth1:10.10.10.20

系统可能根据源地址选择不同的运营商、网关或路由表。

只执行:

ip route get 8.8.8.8

查询的是内核自动选择源地址时的结果。

要判断从指定源 IP 发出时的路由,应使用:

ip route get 8.8.8.8 from 192.168.10.20

以及:

ip route get 8.8.8.8 from 10.10.10.20

这两个结果可能完全不同。

例如:

8.8.8.8 from 192.168.10.20 via 192.168.10.1 dev eth0

另一条则可能是:

8.8.8.8 from 10.10.10.20 via 10.10.10.1 dev eth1

这种情况通常由类似下面的策略规则控制:

100: from 192.168.10.0/24 lookup isp_a
200: from 10.10.10.0/24 lookup isp_b

因此,在多网卡服务器中,推荐把下面这条命令作为标准操作:

ip route get <目标IP> from <实际源IP>

否则,你查询到的可能只是"内核默认源地址"的路由,而不是业务程序实际使用的路由。


六、模拟端口、协议和防火墙标记

现代 Linux 的路由策略不一定只依赖源、目标 IP。VPN、透明代理、分流网关和容器网络经常使用 fwmark、协议或端口选择路由。

ip route get 支持模拟这些条件。

模拟 TCP 443 连接

ip route get 203.0.113.10 \
  ipproto tcp \
  sport 40000 \
  dport 443

模拟 UDP DNS 请求

ip route get 8.8.8.8 \
  ipproto udp \
  sport 40000 \
  dport 53

模拟带有防火墙标记的流量

ip route get 8.8.8.8 mark 0x1

这在下列场景中特别有用:

  • WireGuard 或其他 VPN 分流;
  • nftables、iptables 设置了 packet mark;
  • 透明代理根据 mark 选择路由表;
  • 不同业务端口走不同出口;
  • 多路径路由根据流信息选择下一跳。

例如,看到下面的规则:

100: from all fwmark 0x1 lookup vpn

就意味着带有 0x1 标记的数据包会查询 vpn 路由表。

此时普通查询:

ip route get 8.8.8.8

和带标记查询:

ip route get 8.8.8.8 mark 0x1

很可能得到不同结果。


七、判断转发流量,而不是本机发出的流量

默认情况下,ip route get 模拟的是本机发起的流量。

如果这台 Linux 主机充当路由器、网关或 Kubernetes 节点,需要判断"从某个接口进入的数据包将从哪里转发出去",可以使用 iif:

ip route get 203.0.113.10 \
  from 10.0.0.20 \
  iif eth1

这里的含义是:

假设一个源地址为 10.0.0.20 的数据包从 eth1 进入,它要访问 203.0.113.10,内核将如何转发?

当提供 iif 参数时,内核会把查询模拟为从指定接口进入的转发数据包;不提供 iif 时,则模拟本机产生的输出流量。

这一点在以下场景中非常重要:

  • Linux 软件路由器;
  • NAT 网关;
  • Docker 或 Kubernetes 节点;
  • 双网卡防火墙;
  • 旁路网关;
  • 云主机转发节点。

本机访问正常,并不能证明转发流量的路由也正常。


八、使用 fibmatch 查看真正匹配的 FIB 路由

普通的 ip route get 返回解析后的目标路由。如果希望查看底层 FIB 中匹配到的完整路由,可以使用:

ip route get fibmatch 8.8.8.8

fibmatch 返回完整的 FIB 匹配项,而普通查询默认返回解析后的目标条目。

它适合排查:

  • 路由属性被解析或继承;
  • 多路径路由;
  • 特殊封装路由;
  • 路由输出和路由表原始配置看起来不一致;
  • 希望确认究竟匹配了哪个前缀。

九、VPN、容器和 VRF 为什么经常"查错路由"?

1. VPN 可能使用独立路由表

很多 VPN 并不简单地修改主路由表,而是组合使用:

  • 独立路由表;
  • ip rule;
  • fwmark;
  • 特定目标前缀;
  • 特定接口。

因此,发现流量意外进入 VPN 时,不能只执行:

ip route

而应该同时检查:

ip rule show
ip route show table all
ip route get <目标IP>

如果存在 mark,再模拟:

ip route get <目标IP> mark <MARK>

2. 容器拥有自己的网络命名空间

Linux 网络命名空间拥有独立的:

  • 网络接口;
  • IP 地址;
  • 路由表;
  • 防火墙规则;
  • 网络协议栈。

所以宿主机执行:

ip route get 8.8.8.8

得到的结果,不一定与容器中的结果相同。

查看命名空间:

ip netns list

在指定命名空间中查询:

ip netns exec ns1 ip route get 8.8.8.8

如果是 Docker 或 Kubernetes 容器,最可靠的方法通常是在对应容器或 Pod 的网络命名空间中执行查询:

docker exec <容器> ip route get 8.8.8.8
kubectl exec <pod> -- ip route get 8.8.8.8

3. VRF 使用独立路由域

如果系统使用 VRF,可以指定 VRF 查询:

ip route get 8.8.8.8 vrf blue

查看 VRF 对应路由:

ip route show vrf blue

同一个目标地址,在默认路由域和不同 VRF 中可能拥有完全不同的出口。


十、域名访问要分别判断 IPv4 和 IPv6

路由选择针对的是 IP 地址,而不是域名。

一个域名可能同时解析出:

  • 多个 IPv4 地址;
  • 多个 IPv6 地址;
  • CDN 的不同节点地址。

先查看解析结果:

getent ahostsv4 example.com

查看 IPv6:

getent ahostsv6 example.com

然后分别查询:

ip -4 route get <IPv4地址>
ip -6 route get <IPv6地址>

有时会出现这种现象:

ping example.com 正常
curl example.com 失败

原因可能不是"域名路由不同",而是两个程序选择了不同的解析地址或不同的地址族(例如 curl 优先尝试 IPv6,而 IPv6 路由/连接存在问题)。

因此,排查域名访问问题时,应先确认业务程序实际连接的是哪个 IP、哪个地址族。


十一、ip route get 只能看到本机出口,不能看到完整路径

ip route get 回答的是:

数据包如何离开本机?

它不能告诉你数据包离开本机后经过哪些运营商、路由器、隧道或网络设备。

要查看后续路径,可以使用:

traceroute -n 8.8.8.8

traceroute 通过逐步增加数据包 TTL,并接收中间网关返回的 ICMP Time Exceeded 响应,尝试识别数据包经过的网络节点。

使用 ICMP 探测

sudo traceroute -I -n 8.8.8.8

使用 TCP 443 探测

sudo traceroute -T -p 443 -n 8.8.8.8

传统 traceroute 通常使用较高的 UDP 端口,而很多防火墙会过滤这些 UDP 数据包或 ICMP 响应。TCP traceroute 可以使用业务实际开放的端口,例如 HTTPS 的 443,从而获得更有参考价值的结果。

星号不一定表示网络断开

如果看到:

5  * * *

它只表示探测包在超时时间内没有获得响应,并不能直接证明这一跳不存在或数据包无法通过。

中间设备可能:

  • 不响应 TTL 超时;
  • 对 ICMP 响应限速;
  • 被防火墙过滤;
  • 只转发业务流量,但不响应探测流量。

十二、使用 tracepath 同时检查路径 MTU

另一个常用工具是:

tracepath -n 8.8.8.8

tracepath 与 traceroute 类似,但它还会尝试发现路径 MTU,并且通常不要求超级用户权限。

典型输出可能包含:

1?: [LOCALHOST]                      pmtu 1500
1:  192.168.1.1                       0.8ms
2:  10.0.0.1                          2.1ms pmtu 1480
...
Resume: pmtu 1480 hops 8 back 9

这里的:

pmtu 1480

表示探测到路径中的有效 MTU 可能是 1480。

如果出现以下现象:

  • 小包可以访问;
  • 大包失败;
  • SSH 可以连接但传输卡住;
  • HTTPS 握手后停滞;
  • VPN 环境下部分网站打不开;

就应该怀疑 Path MTU Discovery 或隧道 MTU 问题,此时 tracepath 往往比普通 traceroute 更有价值。


十三、用 tcpdump 和 ip neigh 验证实际数据包

ip route get 是内核的路由查询结果。要确认真实程序的数据包是否确实从预期接口发出,可以使用抓包。

监听所有接口:

sudo tcpdump -ni any host 8.8.8.8

然后在另一个终端发起访问:

ping -c 3 8.8.8.8

只监听指定网卡:

sudo tcpdump -ni enp3s0 host 8.8.8.8

只监听出方向流量:

sudo tcpdump -ni enp3s0 -Q out host 8.8.8.8

抓包可以帮助确认:

  • 数据包究竟从哪个接口出去;
  • 实际使用了哪个源 IP;
  • 是否发生了 NAT;
  • 请求是否发出;
  • 是否收到响应;
  • 返回流量是否进入了另一块网卡;
  • VPN 或隧道封装是否生效。

别忘了检查邻居表

路由选对了,下一跳的链路层地址解析失败,数据包一样发不出去。查看邻居表:

ip neigh show

正常状态是 REACHABLE 或 STALE;如果网关对应的邻居条目长期处于 FAILED 或 INCOMPLETE,说明 ARP(或 IPv6 的 NDP)解析失败,需要排查二层连通性、网关 IP 是否正确。

清空邻居缓存强制重新解析:

sudo ip neigh flush all

在复杂问题中,可以这样理解几个工具的分工:

ip route get:内核计划怎么走
traceroute:网络路径大致怎么走
tcpdump:真实数据包实际上怎么走
ip neigh:下一跳的链路层解析是否成功

十四、返回路径:conntrack、NAT 与非对称路由

前面的排查都在回答"去程怎么走",但连通性是双向的——返回流量走错路,同样会导致访问失败。

NAT 场景下的返回流量

在 NAT 网关上,返回流量并不按普通路由逻辑逐包查找——它直接命中连接跟踪(conntrack)中已建立的条目:

sudo conntrack -L | head

或者查看内核接口:

cat /proc/net/nf_conntrack | head

这意味着:去程做了 NAT 或走了某条路径后,回程必须能命中对应的 conntrack 条目,否则数据包会被当作"新连接"处理,可能被丢弃或无法还原地址。

非对称路由与反向路径过滤

当去程和回程经过不同接口时,就形成了非对称路由。常见于多网卡、策略路由、旁路网关场景。

Linux 默认开启的反向路径过滤(rp_filter)会检查:这个数据包的源地址,是否能够通过它进入的接口按路由返回?如果不能,数据包会被直接丢弃。

sysctl net.ipv4.conf.all.rp_filter

排查思路:

  • 用 tcpdump 分别在两个接口抓包,确认去程、回程各走了哪个接口;
  • 如果发现回程从"意外"的接口进入,检查 rp_filter 设置,以及策略路由是否覆盖了返回方向;
  • 网关设备上,去程和回程的选路规则往往需要成对配置(例如同时匹配 from 和 to)。

记住一个原则:

在策略路由环境里,选路是对每个方向分别生效的。去程走通了,不代表回程能原路返回。


十五、监控路由是否被动态修改

DHCP、NetworkManager、systemd-networkd、VPN 客户端和路由协议都可能动态修改路由。

可以实时监听路由和规则变化:

ip monitor route rule

或者监听更多网络状态:

ip monitor link address route rule

它非常适合排查这种问题:

  • VPN 连接后默认路由突然变化;
  • 网卡重连后路由消失;
  • DHCP 重新续租后 metric 改变;
  • 网络偶尔正常、偶尔走错出口;
  • 容器或编排系统不断添加和删除路由。

十六、几个最常见的误区

误区一:只看默认路由

看到:

default via 192.168.1.1 dev eth0

不能证明所有流量都走 eth0。

更具体的目标路由、策略规则、VPN 路由表或 VRF 都可能优先于默认路由。

正确做法:

ip route get <目标IP>

误区二:metric 最低的路由一定优先

不同前缀长度的路由,首先比较前缀是否更具体。

例如:

default via 192.168.1.1 metric 10
10.0.0.0/8 via 192.168.1.2 metric 500

访问 10.1.2.3 时,仍然优先匹配 10.0.0.0/8。

误区三:宿主机的路由就是容器路由

容器可能处于独立网络命名空间,拥有完全不同的接口和路由表。

应在容器或对应 namespace 内查询。

误区四:traceroute 出现星号就是断网

星号只表示探测没有收到响应。中间节点可能继续正常转发业务流量。

误区五:路由正确就一定能访问

路由正确只说明内核知道数据包应该从哪里发出。

访问仍可能因为以下问题失败:

  • 接口未连接;
  • 网关不可达;
  • ARP 或邻居解析失败;
  • 本机防火墙丢包;
  • 中间防火墙丢包;
  • 目标端口未监听;
  • 返回路由错误;
  • 非对称路由;
  • NAT 配置错误;
  • MTU 不匹配;
  • 服务自身异常。

因此,路由正确是网络连通的必要条件之一,但不是充分条件。

误区六:ping 通就代表网络没问题

ping 只验证了 ICMP 和当前路径。实际业务用的可能是 TCP/UDP、特定端口、特定源地址或经过 NAT 的路径,这些都可能与 ping 的路径不同。

应尽量用业务本身的协议和端口做连通性验证。

误区七:只查主路由表就够

存在策略路由时,真正生效的路由可能位于自定义表、VRF 或由 fwmark 决定。只看 ip route show(main 表)会漏掉真相。


十七、一套推荐的标准排查流程

假设目标地址是:

TARGET=8.8.8.8

第一步:确认本机地址和接口状态

ip -br address
ip -br link

第二步:查询内核最终选路

ip route get "$TARGET"

多网卡时指定源地址:

ip route get "$TARGET" from 192.168.1.100

第三步:检查策略规则

ip rule show

重点关注:

  • from;
  • to;
  • fwmark;
  • uidrange;
  • iif;
  • 自定义路由表。

第四步:查看所有相关路由表

ip route show table all

路由较多时,分别查看:

ip route show table main
ip route show table 100
ip route show table 200

第五步:检查邻居解析

ip neigh show

确认下一跳网关的链路层解析是否成功。

第六步:查看网络路径

traceroute -n "$TARGET"

如果 UDP 或 ICMP 被过滤:

sudo traceroute -T -p 443 -n "$TARGET"

检查路径 MTU:

tracepath -n "$TARGET"

第七步:抓取真实流量

sudo tcpdump -ni any host "$TARGET"

然后发起真实业务请求,观察:

  • 出接口;
  • 源地址;
  • 请求包;
  • 返回包;
  • 重传和 ICMP 错误。

必要时同时在两个接口抓包,确认是否存在非对称路径。

第八步:确认是否存在动态变化

ip monitor route rule

十八、两个真实排障案例

案例 A:VPN 突然"抢走"了所有流量

现象:服务器接入 WireGuard 后,某业务客户端无法访问其外网服务;ip route 显示默认路由仍指向物理网关。

排查:

ip route get 203.0.113.10

输出却指向了 wg0 接口。继续检查策略规则:

ip rule show
ip route show table all

发现 WireGuard 配置了 Table = auto(或 0.0.0.0/0 的 AllowedIPs),把默认路由装进了自定义表,并通过规则让其优先于 main 表。

根因:默认路由"看起来"还在 main 表,但策略规则把更优先的查询指向了 VPN 表——这正是"只看 ip route 会误判"的典型案例。

解决:让业务网段绕过 VPN:

ip rule add to 203.0.113.0/24 priority 100 lookup main

或者给业务流量打上不同的 fwmark,走独立路由表。

案例 B:宿主机能上网,容器不能

现象:Docker 容器内 curl 外网超时,宿主机正常。

排查:先确认容器自己的选路:

docker exec <容器> ip route get 8.8.8.8

容器默认路由指向 docker 网关,说明选路本身正常。再在宿主机上确认转发是否开启:

sysctl net.ipv4.ip_forward
iptables -L FORWARD -n

常见根因是宿主机的 FORWARD 链策略为 DROP(或 firewalld 未放行 docker 网段),以及 MASQUERADE 规则缺失导致容器出网后无法回程。

验证:在宿主机抓包确认容器流量是否真的到达了出接口,以及回包是否被丢弃:

sudo tcpdump -ni eth0 host 8.8.8.8 and not port 22

这个案例说明:容器和宿主机虽然共享内核,但"路由是否正确"要分别看命名空间和转发路径两个层次。


十九、常用命令速查表

# 查询访问目标 IP 的最终路由
ip route get 8.8.8.8

# 指定源 IP
ip route get 8.8.8.8 from 192.168.1.100

# 模拟 TCP 443 流量
ip route get 8.8.8.8 ipproto tcp sport 40000 dport 443

# 模拟 fwmark
ip route get 8.8.8.8 mark 0x1

# 模拟转发流量
ip route get 8.8.8.8 from 10.0.0.20 iif eth1

# 查看底层 FIB 匹配
ip route get fibmatch 8.8.8.8

# 查看主路由表
ip route show table main

# 查看全部路由表
ip route show table all

# 查看策略路由规则
ip rule show

# 查看 IPv4 和 IPv6 路由
ip -4 route
ip -6 route

# 在网络命名空间中查询
ip netns exec ns1 ip route get 8.8.8.8

# 在容器或 Pod 中查询
docker exec <容器> ip route get 8.8.8.8
kubectl exec <pod> -- ip route get 8.8.8.8

# 查询 VRF 路由
ip route get 8.8.8.8 vrf blue

# 查看邻居表 / 清空邻居缓存
ip neigh show
sudo ip neigh flush all

# 查看连接跟踪表
sudo conntrack -L

# 查看反向路径过滤设置
sysctl net.ipv4.conf.all.rp_filter

# 查看网络路径
traceroute -n 8.8.8.8

# 使用 TCP 443 进行路径探测
sudo traceroute -T -p 443 -n 8.8.8.8

# 检查路径 MTU
tracepath -n 8.8.8.8

# 抓取实际流量
sudo tcpdump -ni any host 8.8.8.8

# 实时监控路由变化
ip monitor route rule

总结

判断访问某个 IP 时使用什么路由,最核心的命令是:

ip route get <目标IP>

但在真实生产环境中,还应建立更完整的判断框架:

先用 ip route get 查看内核选路
再用 ip rule 和路由表解释为什么这样选
使用 traceroute 或 tracepath 查看后续路径
用 ip neigh 确认下一跳可达
最后用 tcpdump 验证真实数据包

对于多网卡、VPN、策略路由、容器、VRF 和网关设备,还必须把源 IP、入接口、协议、端口、mark 和网络命名空间纳入查询条件;对于 NAT 网关,还要确认返回路径和 conntrack 状态,警惕非对称路由。

真正可靠的网络排障,不是看到一条默认路由就结束,而是逐层验证:

规则是否匹配
路由是否选对
邻居是否可达
接口是否正确
数据包是否发出
返回流量是否到达

掌握这套方法后,大多数"为什么流量走错网卡""为什么 VPN 抢走流量""为什么容器与宿主机结果不一致"等问题,都可以被快速定位。