模块四 · 网络层:控制平面
路由与最长前缀匹配
你见过 route print 那一大坨输出,但看不出头绪。而你的机器上有两条默认路由,凭什么其中一条赢?
本章要解决的问题:你见过 route print 那一大坨输出,但看不出头绪。
而你的机器上有两条默认路由,凭什么其中一条赢?
本章的证据来源:captures/lab_route_lookup_output.txt
(我自己的实现 vs Windows 的判定,逐条对照)和本机快照。
先从一个例子理解概念
电脑既连Wi-Fi又有虚拟接口时,同一个目标可能有多个出口。路由表是在这些条件下选择下一步的依据。
目的地与下一步:路由
路由回答“去这个地址范围,从哪个接口交给谁”。路由表是这些规则的记录,不是已经走过的路线日志。
写得更具体的范围:前缀
10.0.0.0/8和10.1.0.0/16是不同大小的地址范围。对同时匹配的普通路由,优先更长、更具体的前缀,叫最长前缀匹配。
同等候选再比较:度量
度量(metric)是比较某些同等候选路由的数值,具体组合由实现规定。它不是带宽单位,也不能跨前缀直接比较大小。
没有更具体规则时:默认路由
默认路由匹配剩余未被更具体规则选中的目的;IPv4常写0.0.0.0/0。它可以把包交给默认网关,但本地、绑定和其他策略仍要区分。
下面再用这些概念看原理、命令和结果。规范与官方资料列在本章末尾,类比只帮助理解,不代替协议规则。
路由表在说什么
一条路由就是一句话:「要去这个网段,从哪个接口、交给谁」。
本机 IPv4 路由表共 36 条(实测)。字段含义:
| 字段 | 含义 |
|---|---|
| DestinationPrefix | 目的网段,如 192.168.31.0/24 |
| NextHop | 下一跳交给谁。0.0.0.0 表示「直连,不用经过路由器」 |
| InterfaceAlias | 从哪个网卡出去 |
| RouteMetric | 这条路由自己的度量 |
| Protocol | 来源:Local(系统自动加的)/ NetMgmt(网络管理配置的) |
看到 36 条不用慌。真正决策的规则只有两条。
全部规则只有两条
规则 1:在所有「目标地址落在其网段内」的路由里,选前缀最长的(最具体的)
规则 2:前缀长度相同时,选有效度量最小的
有效度量 = 路由度量 + 接口度量顺序绝对不能颠倒。这是本章最重要的一句话, 4.4 节有一个绝妙的实测证据来证明它。
实测:15 个目标,逐条对照
我写了个脚本(labs/lab_route_lookup.py), 用 Python 自己实现一遍上面两条规则,再调用 Windows 的 Find-NetRoute 问同一个问题,然后对照。结果:
| 目标地址 | 我算出的命中路由 | 接口 | Windows 判定 | 一致? |
|---|---|---|---|---|
| 192.168.31.1 | 192.168.31.0/24 | WLAN | WLAN | ✔ |
| 192.168.31.51 | 192.168.31.0/24 | WLAN | WLAN | ✔ |
| 192.168.31.215 | 192.168.31.215/32 | WLAN | WLAN | ✔ |
| 192.168.31.255 | 192.168.31.255/32 | WLAN | WLAN | ✔ |
| 198.18.0.2 | 198.18.0.0/30 | FlClash | FlClash | ✔ |
| 198.18.0.1 | 198.18.0.1/32 | FlClash | FlClash | ✔ |
| 198.18.0.3 | 198.18.0.3/32 | FlClash | FlClash | ✔ |
| 198.18.0.205 | 0.0.0.0/0 | FlClash | FlClash | ✔ |
| 1.1.1.1 | 0.0.0.0/0 | FlClash | FlClash | ✔ |
| 8.8.8.8 | 0.0.0.0/0 | FlClash | FlClash | ✔ |
| 127.0.0.1 | 127.0.0.1/32 | Loopback | Loopback | ✔ |
| 127.0.0.2 | 127.0.0.0/8 | Loopback | Loopback | ✔ |
| 172.30.14.213 | 172.30.0.0/20 | WSL | WSL | ✔ |
| 224.0.0.251 | 224.0.0.0/4 | FlClash | *查询失败* | — |
| 255.255.255.255 | 255.255.255.255/32 | FlClash | FlClash | ✔ |
14/15 完全一致,剩下一个是多播地址 —— Find-NetRoute 对多播查不了, 这是它自身的局限(多播要按接口分别处理,见 4.8 节),不是规则算错了。
逐个看几个典型
去局域网里的另一台设备:
目标 192.168.31.51 (局域网内另一台设备)
命中的路由 3 条,按优先级排列:
← 选中 192.168.31.0/24 接口=WLAN 下一跳=0.0.0.0 路由度量=256 接口度量=30 有效=286
0.0.0.0/0 接口=FlClash 下一跳=198.18.0.2 路由度量=0 接口度量=0 有效=0
0.0.0.0/0 接口=WLAN 下一跳=192.168.31.1 路由度量=0 接口度量=30 有效=30下一跳是 0.0.0.0 → 直连,不经过任何路由器, 自己用 ARP 找到对方的 MAC 直接发帧(4.6 节详述)。
去本机自己的地址:
目标 192.168.31.215 (本机 WLAN 地址)
命中的路由 4 条
← 选中 192.168.31.215/32 接口=WLAN 下一跳=0.0.0.0 有效=286
192.168.31.0/24 接口=WLAN 下一跳=0.0.0.0 有效=286命中的是 /32,不是 /24。两个路由的度量完全一样(都是 286), 区别只在前缀长度。/32 更具体,所以赢 —— 这就是规则 1 的体现。
去 fake-IP(《DNS 与 Fake-IP》里的那个地址):
目标 198.18.0.205 (fake-IP)
命中的路由 2 条
← 选中 0.0.0.0/0 接口=FlClash 下一跳=198.18.0.2 有效=0
0.0.0.0/0 接口=WLAN 下一跳=192.168.31.1 有效=30只命中默认路由。为什么不是 198.18.0.0/30? 因为 /30 的范围是 198.18.0.0 ~ 198.18.0.3,装不下 .205。 所以假地址是靠默认路由进 TUN 的。
绝妙证据:度量小 ≠ 赢
看这个对比,这是证明「前缀长度优先」最有力的数据:
去 192.168.31.1(局域网网关):
192.168.31.0/24 接口=WLAN 有效度量 = 286 ← Windows 选了这个
0.0.0.0/0 接口=WLAN 有效度量 = 30/24 的有效度量是 286,默认路由只有 30。
如果规则是「度量小者赢」,那么在自己家里的局域网里访问网关, 就应该走默认路由 —— 也就是把包发给网关,好让网关再转回给自己。 这显然是荒谬的。
实测结果:Windows 选了 /24。 这就证明了:前缀长度先比,度量只在同前缀长度之间才参与比较。
两条默认路由的竞争
Windows普通IP路由选择在匹配前缀中优先更具体的路由,同等前缀通常再比较路由与接口度量组合。还需考虑地址族、源地址约束、接口状态、socket绑定及其他策略;不能说所有系统全部只有两条规则。
历史Find-NetRoute记录选择FlClash;WLAN接口度量记录为30,而FlClash接口度量字段为空。字段为空不等于数值0,也不证明代理手工设为0。 实际选择是观察,具体度量成因仍需完整输出/对象值与配置。
Get-NetRoute -AddressFamily IPv4 -DestinationPrefix '0.0.0.0/0'
Get-NetIPInterface -AddressFamily IPv4 | Select-Object InterfaceIndex,InterfaceAlias,InterfaceMetric,AutomaticMetric,ConnectionState
Find-NetRoute -RemoteIPAddress 1.1.1.1低度量默认路由是一种接管流量的方式,但代理也可能通过更具体路由或过滤框架处理流量;不能断言所有代理必须赢默认路由。Microsoft 自动度量
下一跳 0.0.0.0 = 直连 + ARP
路由表里在Windows路由输出中,下一跳0.0.0.0表示on-link类处理;对普通单播邻居可以解释为 「目标就在我所连的网段里,不需要路由器,我自己找它」。
普通以太网IPv4单播邻居通常靠ARP;本机回环、广播、多播与TUN需要分别处理,不是全部靠ARP:把 IP 地址换成 MAC 地址。
为什么需要这一步:以太网帧的头部只有 MAC 地址,没有 IP 地址。 你知道对方的 IP,但要在二层把帧发出去,必须知道它的 MAC。
本机 ARP 表(实测):
Interface: 192.168.31.215 --- 0x11
192.168.31.1 a4-39-b3-0b-ab-19 dynamic ← 网关
192.168.31.51 e8-51-77-27-5a-9a dynamic
192.168.31.248 fa-aa-ff-92-e6-3f dynamic
192.168.31.255 ff-ff-ff-ff-ff-ff static ← 广播
224.0.0.22 01-00-5e-00-00-16 static ← 多播
224.0.0.251 01-00-5e-00-00-fb static
239.255.255.250 01-00-5e-7f-ff-fa staticMAC 地址第一字节藏着两个开关
第0位I/G区分个体/组地址,第1位U/L区分通用/本地管理格式。FA=11111010是单播、本地管理;01=00000001是组地址、通用管理。
位值不证明地址来自硬件烧录或随机化,也不能确认具体厂商与端点身份。Hyper-V等软件同样可以使用通用管理格式。OUI信息应查IEEE登记,不能用操作系统型号倒推后再称为两个独立证据。IEEE Registration Authority
多播 MAC 的映射规则(用本机数据验证)
IPv4 多播地址映射到 MAC,规则是:01-00-5E + 组播 IP 的低 23 位。
拿本机 ARP 表里的真实条目验证,四条全部吻合:
224.0.0.22 -> 01-00-5e-00-00-16
224.0.0.251 -> 01-00-5e-00-00-fb
224.0.0.252 -> 01-00-5e-00-00-fc
239.255.255.250 -> 01-00-5e-7f-ff-fa为什么用 01-00-5E 开头:第一个字节 01 的最低两位是 01, 表示I/G=1(组地址)、U/L=0(通用管理格式),不是本地管理。
为什么只取低 23 位(而不是全部 28 位组播地址位): 因为该映射使用固定25位,其中第四字节最高位为0;余下23位嵌入IPv4多播地址。代价是不同的多播 IP 可能映射到同一个 MAC, 这在二层是可以接受的(收到的帧再按 IP 层过滤即可)。
/32 主机路由是哪来的
路由表里有一堆 /32,看着奇怪:
192.168.31.215/32 WLAN Local ← 本机地址
192.168.31.255/32 WLAN Local ← 广播地址
198.18.0.1/32 FlClash Local
198.18.0.3/32 FlClash Local
127.0.0.1/32 Loopback Local
127.255.255.255/32 Loopback Local
255.255.255.255/32 FlClash Local
172.30.0.1/32 WSL Local
172.30.15.255/32 WSL Local规律很清楚:每个接口的本机地址、广播地址,各加一条 /32 主机路由。 Protocol 都是 Local,说明是系统自动加的。
为什么要加:以本机地址 192.168.31.215 为例, 不加以 /32 的话,去这个地址的包会命中 192.168.31.0/24 这条直连路由, 然后被发到网卡上、发给别人。加了 /32 之后,系统知道 该地址有专门的本地处理;但不能由删除某条显示路由的设想保证报文一定发到别人,实际本地地址处理取决于协议栈。
多播和广播为什么各有 13 条路由
224.0.0.0/4 接口=FlClash / 以太网 / 本地连接* 9 / 本地连接* 10 / ...
255.255.255.255/32 接口=FlClash / 以太网 / 本地连接* 9 / 本地连接* 10 / ...每个接口一条,本机有 13 个接口,所以各 13 条。
为什么不像单播那样只留一条:多播/广播的语义是「发给一组接收者」, 而每个接口连接的是不同的二层网络。要在哪个网络上发多播, 取决于你的目标多播组有哪些成员在哪个网段 —— 所以系统为每个接口都留一条, 由具体应用指定走哪个接口。
这也解释了为什么 Find-NetRoute 查多播地址会失败: 多播的出口不是唯一确定的,得由应用层来指定,不是一个能"算出来"的答案。
排错命令速查
# 看完整路由表(传统命令,输出较长但通用)
route print
# 结构化查询(可过滤)
Get-NetRoute -AddressFamily IPv4 | Sort-Object DestinationPrefix
# 【最好用】直接问:去某个地址实际走哪条路?
Find-NetRoute -RemoteIPAddress 1.1.1.1
# 只有默认路由
Get-NetRoute -DestinationPrefix '0.0.0.0/0'
# 接口度量(多网卡优先级问题必查)
Get-NetIPInterface -AddressFamily IPv4 | Sort-Object InterfaceMetric
# 看 ARP 表
arp -a
# 清 ARP 缓存(怀疑 MAC 对不上时)
netsh interface ip delete arpcache
# 测试连通性(比 ping 信息更全,能看到走的哪个源地址)
Test-NetConnection 1.1.1.1 -Port 443 -InformationLevel Detailed
# 看路径(每一跳)
tracert 1.1.1.1Find-NetRoute 是最好用的一个,它直接告诉你「实际会选哪条路由、 用哪个源地址」,不用你自己在 36 条里找。
本章小结
| 结论 | 状态 |
|---|---|
| 此例的普通IPv4选路以最长前缀和有效度量为主,不能排除绑定/策略等条件 | 教学范围 |
| 我自己的实现和 Windows 判定 14/15 一致 | 实测 |
/24 度量 286 打败 /0 度量 30,证明前缀长度优先于度量 | 实测 |
| 两条默认路由靠接口度量分胜负,FlClash 胜出 | 实测 |
| 历史选路结果为FlClash,空字段不证明接口度量0或手工设置原因 | 观察与限制 |
| 下一跳0.0.0.0需区分普通邻居、本机、广播/多播和TUN,不一律使用ARP | 实现范围 |
| 本机链路层单播要 ARP,多播/广播有固定映射 | 实测 |
多播 MAC = 01-00-5E + 组播 IP 低 23 位 | 实测(4 条全部吻合) |
| MAC 第一字节的 U/L 位可识别随机 MAC | 实测(fa-... 为本地管理) |
每个接口的自身地址与广播地址各有一条 /32 主机路由 | 实测 |
| 多播/广播每个接口一条路由,出口由应用指定 | 实测 + 原理 |
00-15-5D 属于 Microsoft/Hyper-V | 判断(未查 OUI 库) |
自测题
5 题 · 先自己答,再展开对照
路由表里有
10.0.0.0/8(度量 100)和10.1.0.0/16(度量 5000)两条。去10.1.2.3走哪条?为什么?走10.1.0.0/16。因为它的前缀更长(16 > 8),更具体。
度量 5000 在这里完全不参与比较 —— 度量只在两条路由前缀长度相同时才比。
这正是 4.4 节实测证明的规则。一台机器有两条
0.0.0.0/0路由,度量完全相同。会发生什么?先确认所比较的是路由度量还是有效度量,还需考虑源地址、绑定、接口状态和实现的同等候选选择;不要猜接口序号等固定规则。用实际选路和流量观察确认。为什么每个接口的本机地址都需要一条
/32路由?Windows为本地地址提供相应路由与本地交付处理;不能由这条显示记录反推删除后报文必然绕网关,更不能把它当成所有协议栈都需要同样记录的标准。Find-NetRoute -RemoteIPAddress 224.0.0.251查不出结果。这算 bug 吗?不算 bug。 多播的出口不是唯一确定的:224.0.0.0/4有 13 条路由(每个接口一条),走哪条取决于
哪个网段上有该多播组的成员,这需要应用层指定接口。
所以没有一个"算出来"的答案,Find-NetRoute也就无法给出唯一结果。ARP 表里出现了一个第一字节是
02的 MAC。这说明什么?02的I/G=0、U/L=1,表示单播且本地管理;可能随机化、人工配置或虚拟接口,不能从位值确定生成方式、厂商或安全性。
参考资料与适用范围
修订核对日期:2026-10-13。规则按所列协议与实现范围理解;文中的原始命令输出是历史观察,不表示本次重新实测。跨版本、代理/隧道及不同捕获位置需要另行核对。