路由基础:数据包如何找到下一跳

一个 IP 包离开本机网卡之后,到底是怎么一步步走到地球另一端的服务器的?很多人脑子里的画面是「源主机算好一条完整路线,沿途照着走」。这个画面是错的,而且错得很关键。真实的机制朴素得多:每一台设备都只回答一个极小的问题——「这个包,下一步交给谁」,然后把它甩出去;下一跳收到后再问同样的问题,如此接力,直到抵达目标。 没有任何一台设备掌握全程路线。

所以这一篇我们先把机制讲透:一台主机或路由器收到一个 IP 包后,是怎么拿目标地址去查路由表的、表项里有什么、多条都匹配时靠什么规则选一条、选中后又怎么转发出去。 把这套「查表—选路—转发」的流程看清楚,再回头解释默认路由、TTL 这些设计是为了补哪个洞。

路由不是在计算「整条最终路径」,而是在每一跳上做一次「下一步往哪转」的局部决策。全局可达,是无数个局部决策接力出来的结果。

一、先把「下一跳」这个词抠清楚

「下一跳(next hop)」是整篇的核心,先精确定义它。当一台设备要发一个 IP 包,它面对的目标 IP 可能远在天边,它并不知道也不需要知道通往那里的完整路径。它只需要回答:为了靠近这个目标,我应该把包先交给直连网络里的哪一个 IP 地址? 这个地址就是下一跳。

下一跳只可能是两种情况之一:

  • 目标就在本地直连网络里:那下一跳就是目标主机自己,直接二层(用 MAC)发过去即可,不用经过任何路由器;
  • 目标在别的网络:那下一跳是某个网关路由器——它和我在同一个直连网络里,我把包交给它,剩下的路由它接着替我操心。

关键认知:下一跳一定是和当前设备「直连可达」的地址。路由的本质,就是不断把包推给「离目标更近一点」的直连邻居。

二、路由表:决策依据存在哪里

既然每跳都要做决策,决策依据就得有地方存——这就是路由表。它本质是一张「目标网络 → 该怎么走」的查找表,每个表项核心包含四样东西:

  • 目标网络前缀:比如 192.168.1.0/2410.0.0.0/8,描述「这条规则负责哪一片地址」;
  • 下一跳地址:匹配这条规则的包,交给哪个直连 IP;
  • 出接口:从本机哪块网卡发出去(如 eth0);
  • 度量值(metric):这条路的「代价」,多条等价路由时用来排序。

在 Linux 上 ip route 看到的就是它。一台普通主机的路由表通常很简单,最重要的是两类表项:

  • 直连路由:网卡配了 192.168.1.10/24,内核自动生成一条「192.168.1.0/24 走 eth0,直连」的路由——发往本网段的包不需要网关;
  • 默认路由0.0.0.0/0,下一跳指向默认网关——所有「没有更具体规则匹配」的包都走它。

路由器的表则复杂得多,可能有成千上万条,由静态配置或动态路由协议(下一篇讲)填充。

三、查表的核心机制:最长前缀匹配

现在到了最关键的机制。一个包来了,内核拿它的目标 IP 去和路由表里每一条的目标网络前缀比对。问题是:经常有多条规则同时匹配。比如目标是 10.1.2.3,而表里既有 10.0.0.0/8、又有 10.1.0.0/16、还有 10.1.2.0/24,三条都包含这个地址,选哪条?

规则是:最长前缀匹配(Longest Prefix Match, LPM)——在所有匹配的表项里,选前缀最长(掩码位数最多、即最具体)的那一条。 上面的例子里 /24 最长,胜出。

为什么是「最长 = 最具体 = 优先」?因为前缀越长,描述的地址范围越小、越精确,代表这条路由对「这个具体目标」的信息越确切。/8 是「整个 10 网段大概走这边」的粗略指引,/24 是「10.1.2.x 这个具体子网明确走这里」的精确指引——精确的当然该压过粗略的。默认路由 0.0.0.0/0 前缀长度为 0,是最短、最不具体的,所以它天然排在最后,成了兜底出口:只有当没有任何更具体规则匹配时,才轮到它。

我们把这套「拆地址—查表—LPM—转发」的流程画出来:

flowchart TD A[收到 IP 包] --> B[取出目标 IP] B --> C[与路由表所有表项的前缀比对] C --> D{有匹配项?} D -->|无| E[丢弃, 回 ICMP 不可达] D -->|有, 可能多条| F[选前缀最长的一条<br/>最长前缀匹配] F --> G{下一跳是直连目标<br/>还是网关?} G -->|直连| H[ARP 解析目标 MAC, 直接发] G -->|网关| I[ARP 解析网关 MAC, 发给网关] H --> J[从出接口发出] I --> J

图里有一处容易被忽略但极重要:选出下一跳(一个 IP)之后,真正发包要用的是二层 MAC 地址。 所以转发前还要用 ARP(IPv6 是 NDP)把下一跳 IP 解析成 MAC,再封装成帧发出。也就是说,「路由决策」决定下一跳 IP,「ARP/邻居发现」负责把这个 IP 落实到具体网卡——两者配合才完成一次转发。

四、一次完整转发:包到了路由器身上发生了什么

把视角切到中间的路由器,看它收到一个不是发给自己的包时怎么处理,机制会更立体:

  1. 校验与拆解:检查 IP 首部(IPv4 还要校验首部校验和),取出目标 IP;
  2. 查路由表 + LPM:按上一节的规则选出下一跳和出接口;
  3. TTL 减一:把首部里的 TTL 减 1(下一节细讲),IPv4 还要因此重算首部校验和;
  4. 改写二层帧头:把帧的源 MAC 改成自己出接口的 MAC、目的 MAC 改成下一跳的 MAC(必要时先 ARP 解析);
  5. 从出接口发出

注意第 4 步:包在转发过程中,源/目的 IP 自始至终不变(NAT 除外),但每经过一跳,二层 MAC 都会被整段换掉。 这是「IP 管端到端寻址、MAC 管单跳投递」分工的直接体现,也是很多人最容易混淆的地方——下一篇讲交换机和路由器时还会回到这里。

五、默认路由:为什么主机不用存全世界

理解了 LPM,默认路由的意义就顺理成章了。全球路由表有近百万条,让每台主机都存一份显然荒唐。于是普通主机只配几条直连路由,加上一条默认路由 0.0.0.0/0 指向网关:凡是本地没有更具体规则的目标(也就是几乎所有外网地址),统统交给网关,由它替你向上接力。

家庭网络、办公网、云主机里,一条默认路由几乎承担了全部出网流量。ip route 输出里那行 default via 192.168.1.1 dev eth0 就是它。理解这点,你就明白为什么「网关配错」会导致「本网段互通正常、但一访问外网就全挂」——因为本网段走直连路由不受影响,外网全卡在那条错误的默认路由上。

六、TTL:给包设一个寿命

路由是无数局部决策的接力,这带来一个风险:如果几台路由器的路由表互相矛盾,包可能在它们之间来回打转,形成路由环路,永远到不了头。 为了不让这种包在网络里无限漂着耗尽带宽,IP 首部里设了一个 TTL(Time To Live) 字段。

机制很简单:每经过一跳路由器,TTL 减 1;一旦减到 0,这台路由器就丢弃该包,并给源主机回一个 ICMP「超时」消息。 它本质是个跳数上限,给包设了「最多走多少跳」的寿命,环路里的包转几十圈就会被自然清除。

这个机制还被巧妙地反用了——traceroute 就是靠它工作的:先发一个 TTL=1 的包,第一跳路由器把它减到 0 并回 ICMP 超时,于是你拿到了第一跳的地址;再发 TTL=2 的,拿到第二跳……逐步加大 TTL,就把整条路径上的每一跳都「点亮」了。

TTL 既是防环路的安全阀,也是 traceroute 探测路径的工具。一个字段,两种用途,是网络设计里「机制复用」的经典例子。

七、静态路由 vs 动态路由:表是怎么来的

路由表里的条目从哪来?两条途径:

  • 静态路由:人工一条条配死。简单、可控、无协议开销,适合小网络或明确的特殊需求(比如强制某流量走专线)。缺点是网络一变就得手工改,规模大了根本维护不动。
  • 动态路由:路由器之间运行路由协议(RIP、OSPF、BGP),自动交换信息、自动算出并更新表项。规模化网络全靠它。

这一篇你只要知道「表项要么手配、要么协议自动填」就够了,下一篇专门讲动态路由的两大思路。

八、工程视角:路由排障的基本功

把机制落到日常排障,会看路由表是后端/SRE 的硬功夫:

  • 「有网卡、有 IP,但访问不到某网段」:先别怀疑应用,先 ip route 看路由。是默认网关错了?少了某条静态路由?还是策略路由把包导偏了?大量内网互通问题,根因都是路由决策和预期不符。
  • 判断包会从哪走ip route get 10.1.2.3 直接告诉你内核对这个目标会选哪条路由、走哪个网关和接口——这是验证 LPM 结果最快的命令。
  • 容器/云网络:Kubernetes、Docker 的网络本质是在宿主机上插了一堆路由规则和虚拟网卡。Pod 不通时,沿着「源 Pod → 宿主路由表 → 目标」一跳跳查路由,往往能定位问题。
  • 不对称路由:去和回走了不同路径,可能导致有状态防火墙/NAT 看不到完整连接而丢包。排查时要同时看两个方向的路由。

九、学习这一部分最容易踩的坑

1. 以为源主机算好了整条路径

源主机只决定「第一跳交给谁」,后面每一跳都是各设备独立决策。没有任何设备掌握全程路线——这是理解路由的总开关,搞反了后面全错。

2. 多条匹配时不知道选最长前缀

只要记住「越具体越优先,前缀越长越具体」。默认路由 /0 最不具体,永远是兜底的最后选择。

3. 以为转发时 IP 和 MAC 都不变

恰恰相反:源/目的 IP 端到端不变,但每跳的二层 MAC 都会被整段改写。混淆这点,二层三层排障就会找错方向。

4. 忽略转发还要靠 ARP 落地

路由选出的下一跳是个 IP,真正发包得先把它解析成 MAC。下一跳 IP 的 ARP 解析失败,路由再对也发不出去。

总结

这一篇我们先讲机制、后讲设计,核心是看清「包是怎么一跳跳被推向目标」的:

  • 路由是逐跳的局部决策——每台设备只决定「下一跳交给谁」,全局可达由无数局部决策接力实现;
  • 路由表项含「目标前缀、下一跳、出接口、度量值」,主机最关键的是直连路由和默认路由;
  • 查表用最长前缀匹配:多条匹配时选最具体(前缀最长)的,默认路由 /0 是兜底;
  • 一次转发 = 查表选下一跳 + TTL 减一 + 改写二层 MAC + 发出,期间 IP 不变、MAC 每跳换;
  • 默认路由让主机不必存全网路由,TTL 给包设寿命防环路、还被 traceroute 反用;
  • 工程上「通不到某网段」先查路由表,ip route get 是验证选路的利器。

当你能对着一条 ip route 输出说清「这个目标 IP 会命中哪条规则、下一跳是谁、为什么是这条而不是那条」,路由这部分就真正属于你了。

参考资源