一台刚配置好地址的主机准备访问另一子网的服务。目的 IP 已经确定,但邻居表还是空的。此时需要查询远端服务器的 MAC,还是默认网关的 MAC?如果地址来自 DHCP,提供租约的服务器是否必须承担这次转发?

这几个地址属于不同决策:地址配置确定本机可使用的参数,路由选择确定当前报文的下一跳,邻居解析把下一跳 IP 对应到当前链路上的地址。第 04 篇已经解释了以太网转发,本篇沿着这三个决策,观察一个真正跨越两段隔离链路的请求。

目的地、下一跳与配置来源

设 A 的接口地址为 10.6.1.2/24,R 的两个接口分别为 10.6.1.1/2410.6.2.1/24,B 为 10.6.2.2/24。A 的默认路由经 R 的左侧接口,B 的默认路由经 R 的右侧接口。这里的 /24 表示前二十四位是前缀,具体路由匹配留到第 07 篇。

1
2
3
A                         R                          B
10.6.1.2/24 ─── 10.6.1.1/24 10.6.2.1/24 ─── 10.6.2.2/24
默认下一跳 .1 开启 IPv4 转发 默认下一跳 .1

在这个没有 NAT 的拓扑里,A 发给 B 的 IP 数据报以 10.6.2.2 为目的地址。A 的以太网帧却需要交给 R 的左侧接口。两个目的字段不相同并不是异常:IP 目的地描述这份数据报要到哪里,链路层目的地址描述当前这一步要交给谁。

RFC 826 的 Packet Generation先讨论路由确定下一跳协议地址及出口,再讨论地址解析。这一先后关系很关键。如果先拿最终目的 IP 去做 ARP,再决定走哪个网关,就颠倒了本例的决策顺序。

默认路由也不是所有流量的必经规则。若目标在直连网段,或存在更具体的匹配路由,就可能选择别的下一跳。本篇只使用两段直连网络和一条默认路由,避免把策略路由、多网卡选择或代理 ARP 混进基本案例。

空邻居表中缺少的是什么

A 的路由已经选中了 10.6.1.1,但尚不知道该接口的 MAC。这时 A 在当前以太网链路发出 ARP 请求,目标协议地址填写 10.6.1.1。广播目的 MAC 让同一广播域内的接收者有机会处理这个请求;不是让互联网中的全部主机参与查询。

ARP 请求与随后的 IP 数据帧是两份不同的报文。前者询问下一跳地址映射,后者携带面向 B 的数据。观察时应分别记录:

报文或状态 本例要检查的值 能回答的问题
A 的路由查询 via 10.6.1.1 内核为此次查询选择了谁
ARP 请求的目标协议地址 10.6.1.1 A 正在解析谁的链路层地址
发往 B 的 IPv4 目的地址 10.6.2.2 数据报最终发给谁
该数据帧的 Ethernet 目的 MAC R 左侧接口的 MAC 当前链路交给谁
A 的邻居项 网关 IP 与 MAC 哪个下一跳已有映射

A 不需要先得到 B 的 MAC 才能把这份报文交给 R。R 收到后,在自己的出口继续进行路由和邻居解析;B 回应时也需要返回路径。因此 A 成功解析网关,只证明局部前提已经具备,还不能证明 R 会转发或 B 的应用会响应。

若在 A 抓到对 10.6.2.2 的 ARP 请求,应先检查实际路由和前缀,而不是直接认定协议失效。例如误把 A 配成更宽的直连前缀,可能使内核把 B 当成链路上的邻居。代理 ARP 等配置还会改变应答者,本篇实验未开启这些功能。

DHCP 提供的参数不是一种地址

DHCP 可以提供主机配置,而不只是填入一个 IP。以不包含 Classless Static Route Option 的简化租约为例,以下字段应分开阅读:

参数 协议位置 假设值与含义
分配给客户端的地址 DHCP 报文 yiaddr 10.6.1.2,不是服务器地址
子网掩码 Option 1 255.255.255.0,用于本例前缀配置
路由器列表 Option 3 10.6.1.1,客户端子网上的路由器
租期 Option 51 3600 秒,有效期参数
服务器标识 Option 54 10.6.1.254,DHCP 服务器标识

这些是假设输入,不是本次捕获的 DHCPACK。字段定义来自 RFC 2132yiaddr 的报文布局见 RFC 2131 §2。提供配置的主机和负责转发的路由器可以是不同设备;DHCP relay 还允许服务端不在客户端所在链路。

初次分配通常可沿 DISCOVER、OFFER、REQUEST、ACK 理解:客户端寻找配置来源,服务器给出候选配置,客户端表达选择,服务器确认分配。这个简图不涵盖复用旧地址、续租、拒绝配置和所有重传分支,也不能推导出每次联网都必然出现同样四个广播包。

地址来源并不决定每个数据报的即时下一跳。租约参数被应用后,实际发送仍要查询当前路由。即使租约写有某个路由器,如果配置没有安装成功,或者更具体的规则覆盖了它,报文也未必照着租约中的第一项走。

还有一个明确反例:RFC 3442规定,支持 Classless Static Route Option 的客户端同时收到 Option 121 和 Option 3 时,应忽略 Option 3。因而“DHCP 下发的默认路由永远只看 Option 3”不是普遍结论。排查配置要保留选项集合和客户端行为,不能只截取一个网关字段。

租约计时与邻居老化不是同一件事

动态租约有自己的续租过程。RFC 2131 §4.4.5区分 T1 的 RENEWING 和 T2 的 REBINDING:前者向原服务器请求续租,后者广播寻求服务器。默认比例为租期的二分之一和八分之七,也允许配置其他值;到期仍未续租,必须停止使用相关配置。

对一个 3600 秒租约,假定采用这两个默认比例、期间没有成功续租,名义时间线为:

1
2
3
T1 = 3600 × 1/2 = 1800 秒
T2 = 3600 × 7/8 = 3150 秒
T2 到到期还剩 450 秒

这只是比例手算,不是客户端定时器实测。实际续租成功会改变后续租约时间线,不能仍按第一次分配的到期点判断。

邻居缓存维护的则是下一跳映射及可达性状态。ip-neighbour 手册中的 INCOMPLETE 表示尚未完成解析,REACHABLE 表示当前有效,STALE 表示映射仍有效但需要重新确认;DELAYPROBE 属于进一步验证过程,FAILED 表示探测失败。

因此 DHCP 租约仍有效,不保证默认网关可达;邻居项显示 STALE,也不能直接写成网络已断。即使它显示 REACHABLE,远端服务可能尚未监听、返回路径可能有误,或者应用正在等待其他资源。每张表只描述它负责的状态。

在空邻居表上发出一次真实请求

附件 lab.py 只使用 Python 标准库和 Linux 已有的 ip 工具。它创建随机前缀的三个网络命名空间,以两对 veth 构成上图;地址和默认路由只写入这些命名空间,IPv4 转发也只在 R 内开启。没有把接口接入物理网络。

在具有相应权限的专用 Linux 虚拟机中,从仓库根目录运行:

1
sudo python3 source/_posts/2026-09-19-计算机网络06-地址从哪里来/lab.py

macOS 不能直接执行这组 Linux 网络操作。本次使用已有 Podman Linux 虚拟机,不安装额外软件;实验结果适用于记录中的内核与工具版本,不代表物理路由器或公网行为。

程序先保存 A 的邻居表,再执行 ip route get,确认查询后邻居表仍为空。ip-route 手册明确该命令解析路由但不实际发送数据包。因此路由查询成功可以作为选路证据,不能当作连通性测试。

捕获器使用 AF_PACKET,在 A 的接口就绪后才启动请求。B 上的自建 UDP 服务回显收到的数据;A 比较回显与原始载荷。使用 UDP 是为了减少本篇额外状态,不预先讨论第 13 篇的可靠性问题,也不由一次回显推出 UDP 具有可靠交付保证。

程序保存原始帧与解码字段,核对 ARP 的目标、IPv4 的目的地、帧目的 MAC,以及请求后的邻居表。应用回显与包级证据分别保留:前者证明这次自建服务完成了往返,后者说明它在 A 这段链路采用的地址。

实验结束时停止自己创建的子进程,只删除本次随机前缀的命名空间,并检查清理结果。这里不提供清空宿主邻居表或修改宿主默认路由的命令;复跑直接使用新建的空命名空间。

本次运行的对应关系

完整原始输出保存在 run.jsonl。本次环境为 Linux 6.18.10-200.fc43.aarch64、Python 3.14.3、iproute2 6.14.0。脚本为 R 左侧接口明确设置了 02:00:00:00:06:01,这个 MAC 是实验配置,不是设备厂商标识。

观察点 本次结果
A 初始 IPv4 邻居表 空数组
查询 B 路由后 下一跳 10.6.1.1,邻居表仍为空
ARP 请求 目标 10.6.1.1
发出的 IPv4 数据帧 IP 目的 10.6.2.2,MAC 目的 02:00:00:00:06:01
应用与最终邻居表 20 字节回显相等,唯一 IPv4 邻居为网关,状态 REACHABLE

保存的四帧依次包含 ARP 请求、ARP 应答、出站 UDP 数据和返回 UDP 数据。这里的捕获代码只对本实验无 VLAN 标签的 Ethernet 帧使用固定偏移;它不是通用抓包解析器,不能拿去解码第 03 篇提到的其他链路类型。

ip route get、ARP、应用回显分别提供了选路、局部地址映射和本次应用往返的证据。只有把这些记录对应起来,才能说明“这次请求经过了哪个下一跳”;仅看到最终回显成功,不能证明此前邻居缓存一定为空。

验证边界

本篇真实实验采用静态地址,没有启动 DHCP 客户端、服务器或 relay,因此没有 DORA 交换、租约续期和 Option 121 安装结果。租约部分仅有规范核查与明确假设下的手算,不能称为 DHCP 实测。

ARP 和 UDP 回显发生在同一专用 Linux 虚拟机内的隔离拓扑。它们可以验证这次路由、邻居解析和自建应用往返,但不能外推物理交换设备、跨公网路径、生产业务或性能。捕获只覆盖指定接口和有限时间,也不是整个网络的完整记录。

练习

第一题:保持 A 的 10.6.1.2/24 和默认网关 10.6.1.1,分别访问 10.6.1.910.6.2.9。在没有更具体路由、代理 ARP 等额外配置的前提下,写出两次 ARP 目标与两次 IP 目的地址。哪一次需要让这两个 IP 不相等?

校验:前者直接解析 10.6.1.9;后者解析 10.6.1.1,但数据报目的仍为 10.6.2.9。这个推导还没有证明任何目标在线。

第二题:假设租期为 800 秒,采用默认 T1、T2。计算两个时刻,构造一个“租约仍有效、远端请求失败”的反例,并指出需要哪项附加证据才能确认失败位置。

校验:T1 为 400 秒,T2 为 700 秒。例如 R 的邻居映射已经存在,但 R 没有开启转发;A 的租约与邻居状态都不足以排除该故障。需要核查 R 的转发配置及入口、出口观察,不能只凭 A 的超时给出根因。本篇实际运行没有注入这一故障。

参考资料