计算机网络 06:地址从哪里来,ARP、DHCP、默认网关与邻居缓存
一台刚配置好地址的主机准备访问另一子网的服务。目的 IP 已经确定,但邻居表还是空的。此时需要查询远端服务器的 MAC,还是默认网关的 MAC?如果地址来自 DHCP,提供租约的服务器是否必须承担这次转发?
这几个地址属于不同决策:地址配置确定本机可使用的参数,路由选择确定当前报文的下一跳,邻居解析把下一跳 IP 对应到当前链路上的地址。第 04 篇已经解释了以太网转发,本篇沿着这三个决策,观察一个真正跨越两段隔离链路的请求。
目的地、下一跳与配置来源
设 A 的接口地址为 10.6.1.2/24,R 的两个接口分别为 10.6.1.1/24 和 10.6.2.1/24,B 为 10.6.2.2/24。A 的默认路由经 R 的左侧接口,B 的默认路由经 R 的右侧接口。这里的 /24 表示前二十四位是前缀,具体路由匹配留到第 07 篇。
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 2132,yiaddr 的报文布局见 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 | |
这只是比例手算,不是客户端定时器实测。实际续租成功会改变后续租约时间线,不能仍按第一次分配的到期点判断。
邻居缓存维护的则是下一跳映射及可达性状态。ip-neighbour 手册中的 INCOMPLETE 表示尚未完成解析,REACHABLE 表示当前有效,STALE 表示映射仍有效但需要重新确认;DELAY、PROBE 属于进一步验证过程,FAILED 表示探测失败。
因此 DHCP 租约仍有效,不保证默认网关可达;邻居项显示 STALE,也不能直接写成网络已断。即使它显示 REACHABLE,远端服务可能尚未监听、返回路径可能有误,或者应用正在等待其他资源。每张表只描述它负责的状态。
在空邻居表上发出一次真实请求
附件 lab.py 只使用 Python 标准库和 Linux 已有的 ip 工具。它创建随机前缀的三个网络命名空间,以两对 veth 构成上图;地址和默认路由只写入这些命名空间,IPv4 转发也只在 R 内开启。没有把接口接入物理网络。
在具有相应权限的专用 Linux 虚拟机中,从仓库根目录运行:
1 | |
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.9 与 10.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 的超时给出根因。本篇实际运行没有注入这一故障。
参考资料
- RFC 826:ARP 字段与 Packet Generation 中的下一跳解析顺序。
- RFC 2131:DHCP 报文、分配、relay 与租约状态;RFC 2132:配置选项。
- RFC 3442:Classless Static Route Option 与 Option 3 的关系。
- ip-neighbour(8)、ip-route(8):Linux 查询与邻居状态。
- ip-netns(8)、packet(7):隔离命名空间与链路层 socket。




