数据包怎么选路
一句话总结
如果目标地址位于本机子网内,主机就直接发送;如果在子网外,则发给默认网关。路由表是一组用于做出这一判断的规则,其中最长前缀优先。
为什么需要理解路由
“能 ping 通却连接不上”“只有某个网段不通”之类的报障,大多与路由有关。但如果不会阅读路由表,甚至都不知道应该检查什么。
它是如何工作的
ip route
# default via 192.168.219.1 dev eth0
# 10.244.0.0/16 dev cilium_host scope link
# 192.168.219.0/24 dev eth0 proto kernel scope link src 192.168.219.50
这三行分别表示:
192.168.219.0/24 dev eth0—— 这个网段与主机直接相连,主机通过 ARP 找到对端 MAC 后直接发送。10.244.0.0/16 dev cilium_host—— Pod 网段通过 cilium 接口发送。default via 192.168.219.1—— 其余所有流量都交给网关。
当一个目标同时匹配多行时,最长前缀胜出。10.244.1.5 同时匹配 default(/0)与 10.244.0.0/16,但 /16 更长,因此选择后者。这称为最长前缀匹配(longest prefix match)。
可以直接查询某个目标的实际路径。
ip route get 8.8.8.8
ip route get 10.244.1.5
这条命令直接询问内核:“向这个地址发送时,会从哪里出去?”它比人工阅读路由表并进行推断更加准确。
阅读路由的顺序
不能从上到下依次读取 ip route 的列表。内核会先选择最长前缀
(most-specific),如果长度相同,则使用 metric 更小的路由。
10.0.5.0/24 dev eth1 ← /24. 10.0.5.7 은 여기로
10.0.0.0/8 via 10.0.0.1 dev eth0 ← /8. 10.0.5.7 도 여기 포함되지만 진다
default via 192.168.1.1 dev eth0 ← /0. 아무 데도 안 맞을 때
与其用眼睛逐行检查,不如直接询问内核,结果更加可靠。
ip route get 10.0.5.7
10.0.5.7 dev eth1 src 10.0.5.2 uid 1000
src 非常重要。 出口接口确定后,源 IP 也随之确定;如果对端防火墙没有允许
这个地址,通信就会失败。“我的 IP 明明是 203.x,为什么却从 10.x 发出去”之类的
问题,会在这里显现。
规则 → 表 → 路由
Linux 中可以有多个路由表,具体查看哪个表由 ip rule 决定。
ip rule show
0: from all lookup local ← 자기 주소. 건드리지 않는다
32766: from all lookup main ← ip route 가 기본으로 보여 주는 것
32767: from all lookup default
在 VPN、多线路或策略路由环境中,这里还会添加其他规则。
100: from 10.8.0.0/24 lookup vpn ← VPN 대역은 다른 테이블을 본다
这种情况下,如果只查看 ip route(main 表),就会觉得“配置明明正确,流量却发不出去”。
必须通过 ip route show table vpn 单独查看对应的表。规则按编号从小到大
依次评估,并使用第一条匹配规则指定的表。
ARP 与邻居缓存
目标位于同一子网时,主机不会经过路由器,而是直接使用 MAC 地址发送。保存这种 对应关系的表就是邻居缓存。
ip neigh show
10.0.5.1 dev eth1 lladdr 00:1a:2b:3c:4d:5e REACHABLE
10.0.5.9 dev eth1 FAILED ← 응답이 없다
FAILED 表示不存在拥有该 IP 的设备,或者设备没有响应。路由正确却无法通信时,
应该检查这里。IP 刚迁移后,旧 MAC 还可能在缓存中残留,导致几分钟内无法通信——
可以使用 ip neigh flush dev eth1 清除缓存。
常见误解
网关必须位于同一子网。 默认网关需要通过 ARP 查找,因此必须处在直接相连的网段内。如果把其他网段的地址设为网关,就会出现 Network is unreachable。
能 ping 通就说明网络正常。 ping 使用 ICMP,而服务使用 TCP。如果防火墙只允许 ICMP,就会出现 ping 通却连接不上的情况。反过来,如果只阻止 ICMP,也会出现 ping 不通但服务完全正常的情况。
实际工作中真正重要的事
在容器内查看 ip route,结果通常像下面这样简单。
default via 10.244.1.1 dev eth0
10.244.1.0/24 dev eth0
Pod 只知道自己所在节点的网关。前往其他节点上 Pod 的路径由节点掌握。因此,Pod 间通信失败时,应该检查节点的路由与 CNI,而不是只看 Pod 内部。
Linux 中可以有多个路由表,由 ip rule 决定查看哪一个。在 VPN 或多线路环境中,如果“路由表明明正确,流量却发不出去”,就必须检查 ip rule。