Linux 路由排障:流量到底走了哪条路?
在排查网络问题时,我们经常会遇到这类疑问:
- 访问这个 IP,到底从哪个网卡出去?
- 使用的是哪个网关?
- 为什么明明配置了默认路由,流量却走了 VPN?
- 同一个目标 IP,为什么不同源地址走的路径不一样?
ip route显示正常,为什么实际还是访问失败?- 宿主机和容器访问同一个 IP,为什么结果不同?
最简单的回答是:
ip route get <目标IP>
但严格来说,"访问某个 IP 走什么路由"并不是一个单层问题。完整排查通常需要回答四个不同层次的问题:
- Linux 内核选择了哪张路由表和哪条路由;
- 数据包从哪个网卡、使用哪个源 IP、经由哪个网关发出;
- 数据包离开本机后,经过了哪些中间网络节点;
- 实际业务流量是否真的按照预期路径发出。
本文将从最常用的 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 场景下最常见的坑。
内核为出站数据包选择源地址的顺序大致是:
- 路由条目显式指定的
src属性优先。例如路由10.0.0.0/8 via 192.168.1.254 dev enp3s0 src 10.0.0.20,就明确要求使用10.0.0.20作为源地址; - 路由没有指定
src时,从出口接口的地址中选择。内核会优先选择与目标处于同一子网的接口地址;scope 更小的地址也更受偏好(host<link<global); - 仍有多个候选时,按 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
这些规则的含义是:
- 优先查询
local表; - 如果没有找到结果,再查询
main表; - 最后查询
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 抢走流量""为什么容器与宿主机结果不一致"等问题,都可以被快速定位。