systemd-networkd 与 DHCP Option 81:DNS 动态更新失效的排查指南

发布时间:2026/9/3 9:28:05
systemd-networkd 与 DHCP Option 81:DNS 动态更新失效的排查指南 systemd-networkd 是当前大量 Linux 发行版默认使用的网络管理组件配置简单、依赖少、和 systemd 的生态结合紧密但它对 DHCP Option 81Client FQDN的处理长期处于一种“功能存在、文档没说清”的状态。这个问题在日常拿 IP 时基本看不出来一旦你需要 DHCP 服务器把主机名自动注册进 DNS就会变成非常难定位的障碍。很多人检查了防火墙、域名后缀、Windows DHCP 作用域选项最后才发现问题出在客户端发出的 Option 81 标志位上。Option 81 是 DHCP 协议里用来传递客户端 FQDN 的选项DNS 服务器在收到 DHCP 服务器的更新请求时会结合这个选项里的标志位和域名内容来决定如何注册 A/AAAA 记录和 PTR 记录。systemd-networkd 会发送这个选项但发送的触发条件、标志位的取值逻辑、FQDN 怎么拼装在 systemd.network(5) 手册里一直没有完整展开和传统 dhclient 的行为也不完全一致在线讨论往往散落在各个 issue 和论坛帖子里。所以这篇文章把协议背景、实际行为、抓包验证、配置思路和排查清单整理到一起方便你按图索骥。文章会先讲清楚 Option 81 在 RFC 4702 里的报文结构和标志位含义再分析 systemd-networkd 实际是怎么处理这个选项的然后给出一套可以在隔离网络里完整复现的抓包流程最后是常见问题和配置建议。适合对 systemd-networkd 有基本认识、遇到 DHCP 自动注册 DNS 不生效或者正在考虑从 dhclient 迁移到 systemd-networkd 的读者。1. systemd-networkd 核心能力速览这里先从整体看一下 systemd-networkd 在 DHCP 场景里的定位以及它对 Option 81 的支持程度。很多人误以为“支持 DHCP”就等于“支持 DNS 动态更新”这是本篇文章最需要纠正的判断。能力项说明项目类型Linux 网络管理服务组件属于 systemd 项目主要功能管理网络设备、DHCP 客户端、DHCP 服务端、VLAN、网桥、隧道、路由策略、LLDP 等DHCP 客户端内置 DHCPv4 / DHCPv6 客户端通过 .network 文件配置Option 81 支持支持发送 Client FQDN但触发条件和标志位取值在历史版本中说明不完整配置位置/etc/systemd/network/.network、.netdev、*.link启动方式systemctl enable --now systemd-networkd日志查看journalctl -u systemd-networkd常用查询命令networkctl status、networkctl renew适用场景服务器、容器、嵌入式设备、不需要桌面网络管理工具的 Linux 主机与 DNS 动态更新关系是否真正生效取决于 DHCP 服务器对 Option 81 标志位的处理策略与 networkd 本身关系不大从表格能看出systemd-networkd 本身是一个功能很宽的网络管理组件。对于本文讨论的主题核心结论是它会作为 DHCP 客户端发出 Option 81 报文但并不会像某些老牌 DHCP 客户端那样主动去完成 DNS 注册动作。DNS 更新是否发生取决于 DHCP 服务器以及 DNS 服务器的配置。这就引出了“未文档化行为”的关键问题客户端发送 Option 81 时声明的标志位通常决定了服务器愿不愿意接管更新。2. DHCP Option 81 协议背景Client FQDN 选项到底做了什么DHCP 选项的编号体系在 RFC 2132 中定义Option 81 是后来由 RFC 4702 补充的 Client FQDN 选项。它的作用比 Option 12Host Name更复杂。Option 12 只传一个短主机名字符串比如web01Option 81 则携带完整的 FQDN、以及一组控制 DNS 更新行为的标志位。Option 81 的报文结构如下字段长度说明Code1 字节固定为 81Length1 字节后续数据总长度Flags1 字节控制 DNS 更新的标志位RCODE11 字节正向更新响应码客户端请求中为 0RCODE21 字节反向更新响应码客户端请求中为 0Domain Name变长FQDN 的 DNS wire format比如test.example.com编码为04 74 65 73 74 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00其中 Flags 字段是最关键的部分。RFC 4702 对标志位的定义如下Bit名称含义RFC 4702 简述bit 0S设为 1 时服务器应该执行正向更新A/AAAA 记录bit 1O设为 1 时服务器应该执行反向更新PTR 记录bit 2E设为 1 时FQDN 字段使用截断编码与 DNS wire format 兼容bit 3N设为 1 时客户端自己执行 DNS 更新且不希望服务器执行更新bit 4A设为 1 时服务器应该忽略客户端提供的部分名字改用服务器配置bit 5-7保留通常为 0组合起来常见的情况有几种如果 flags 为 0表示客户端没有明确要求服务器做正向或反向更新也没有声明自己会做更新这个状态最容易造成两边都不管如果 flags1表示客户端希望服务器做正向更新如果 flags3表示客户端希望服务器同时做正向和反向更新如果 flags 的 bit 3N1被置位则明确告知服务器“我自己会处理 DNS 更新”服务器一般不会再接管。在实际生产环境里Windows DHCP 客户端和 Linux 下的 dhclient、systemd-networkd 对标志位的选择并不一样这直接导致了同一个 DHCP 服务器在不同客户端上的 DNS 自动注册表现不同。理解这一点是排查 DNS 注册问题的前提。还有一点要注意DHCP 服务器并不是一定要按选项去执行很多服务器实现会忽略标志位或采用自己的策略所以文档标准与实际表现会有差异这也再次说明抓包确认的重要性。3. systemd-networkd 对 Option 81 的实际处理逻辑从 systemd 的源码结构和社区反馈来看systemd-networkd 对 Option 81 的发送是存在的但行为集中在 DHCP 客户端模块里。在实际使用中是否发送 Option 81与主机名是否能被获取以及[DHCPv4] 段的 SendHostname 配置有直接关系。如果主机名可以拿到且 SendHostname 没有被关闭networkd 会在 DHCP Discover 和 DHCP Request 报文中一并携带 Option 12 和 Option 81如果主机名为空或者关闭了 SendHostname则 Option 81 大概率不会出现。这中间有一个容易踩到的坑systemd.network(5) 手册里对SendHostname和Hostname的说明只是从“是否发送主机名”的角度描述并没有明确告诉用户这个开关还会联动触发 Option 81 的携带。对很多从 dhclient 迁移过来的管理员来说这就是典型的未文档化行为你在 .network 文件里写着SendHostnametrue结果发现 DHCP 报文里多了一个 Option 81而 DHCP 服务器和 DNS 服务器又因为这个选项的存在改变了原本的更新策略。再看标志位。从当前可见的 systemd 客户端实现来看networkd 在构造 Option 81 时默认 flags 接近于 0也就是客户端没有明确要求服务器执行正向或反向更新。这样一来如果 DHCP 服务器是一个对标志位敏感的实现它就认为客户端可能要自己做更新于是不会主动写 DNS 记录。而 systemd-networkd 本身并不具备 DDNS 更新能力最终结果就是主机拿到了 DHCP 地址但 DNS 里始终没有记录。另外还有 FQDN 拼装细节。系统 hostname 如果只填了短主机名比如web01networkd 在发送 Option 81 时是否会自动附加 DNS 后缀在不同版本和不同配置下并不一致。这会导致一个问题DNS 服务器收到 Option 81 后注册的名字可能是web01而不是web01.example.com造成主机名和预期 FQDN 对不上。这个行为同样缺乏完整文档只有通过抓包才能确认当前版本到底发出去了什么。4. 环境准备与验证工具要验证 systemd-networkd 里的 Option 81 行为推荐准备一个隔离的测试网络避免在生产环境里制造多余的 DNS 记录。如果只有一台 Linux 机器用虚拟网桥加网络命名空间也能模拟但最简单的方案是准备一台测试机、一个小型交换机或直连网线以及一台可以跑 dnsmasq 的机器作为 DHCP/DNS 服务器。客户端需要安装抓包工具。Debian/Ubuntu 下可以执行apt update apt install -y tcpdump dhcpdumpRHEL/Fedora/CentOS 下执行dnf install -y tcpdump dhcpdump如果发行版软件源里没有 dhcpdump也可以只用 tcpdump它已经能够解码 Option 81。另外建议安装 tshark用于对抓包文件做字段级过滤分析 Option 81 的 flags 更方便dnf install -y wireshark-cli测试环境里建议用 dnsmasq 起一个最小的 DHCP/DNS 服务这样服务端行为完全可控。下面是一个最小 dnsmasq 配置示例# /etc/dnsmasq.d/test-dhcp.conf interfaceenp0s8 bind-interfaces dhcp-range192.168.56.100,192.168.56.200,12h dhcp-optionoption:router,192.168.56.1 port53 no-resolv log-dhcp log-queries启动服务systemctl enable --now dnsmasq客户端机器需要确认 systemd-networkd 已启用并确认当前网卡由 networkd 接管systemctl status systemd-networkd networkctl status如果网卡由 NetworkManager 等其他组件管理需要先停用相应服务或者在一个没有图形界面的测试机上做验证避免两套网络管理组件互相干扰。5. 抓包复现 Option 81 的未文档化行为完成环境准备后在客户端机器上写一个最小 .network 配置显式指定要发送的 hostname# /etc/systemd/network/20-dhcp.network [Match] Nameenp0s3 [Network] DHCPipv4 [DHCPv4] UseDNStrue UseNTPfalse SendHostnametrue Hostnameweb01配置写好后重启网络服务让配置生效systemctl restart systemd-networkd随后用 tcpdump 在客户端网卡上抓取 DHCP 报文。DHCPv4 使用 UDP 67/68 端口抓包命令如下tcpdump -i enp0s3 -n -vv -s 0 -w option81.pcap port 67 or port 68开启抓包后让客户端重新走一次 DHCP 流程。重启 networkd 会触发重新获取地址也可以用 networkctl 主动续租networkctl renew enp0s3等抓包结束后用 tcpdump 直接检查 pcap 文件里的 Option 81 字段。不同版本的 tcpdump 输出格式有差异通常能搜到 fqdn 或 client-fqdn 关键字tcpdump -r option81.pcap -nn -vv port 67 or port 68如果安装了 tshark可以更精确地过滤字段tshark -r option81.pcap -Y dhcp.option.fqdn -V在这个输出里重点看两个内容第一DHCP Discover 和 DHCP Request 报文中是否出现了 Option 81第二Flag 字段的值是多少。如果Hostnameweb01配置生效报文里应该同时包含 Option 12Host Name和 Option 81Client FQDN。Flags 字段如果在当前版本中表现为 0x00就说明客户端没有要求服务器执行正向或反向更新这很可能就是 DNS 记录不生成的直接原因。需要再强调一次由于 systemd 版本之间存在差异实际观察结果可能不同。如果当前版本已经调整了 flags 的取值逻辑你看到的可能是 0x01 或其他值。这正好说明脱离版本讨论“systemd-networkd 的 Option 81 行为”是不严谨的一定要以实际抓包为准。6. 配置与调优如何控制 Option 81 的发送如果经过抓包确认 systemd-networkd 发送的 Option 81 不是你想要的行为可以按下面几个方向调整。6.1 完全不发送 Option 81如果网络环境不需要 DNS 动态更新或者你希望把主机名注册策略完全交给 DHCP 服务器最简单的办法是关闭 SendHostname。在 [DHCPv4] 段里设置[DHCPv4] SendHostnamefalse这样 systemd-networkd 在构造 DHCP 报文时不会携带主机名相关选项Option 81 通常也不会出现。这个配置对排查问题很有用可以先关闭它对比一下 DHCP 服务器在收到和收不到 Option 81 两种情况下的 DNS 更新行为。6.2 修改发送的 FQDN如果你确实需要在报文中携带 Option 81但希望使用不同的主机名可以通过 Hostname 指定[DHCPv4] SendHostnametrue Hostnameweb01.lab.example.com注意主机名中是否包含点号会影响 FQDN 的拼装方式。如果只写短主机名systemd-networkd 在部分版本里不会自动带上 DNS 后缀DNS 服务器最终注册的名字可能不是你预期的完整域名。因此建议在测试阶段把完整的 FQDN 写进去再通过抓包确认实际发送的 Domain Name 字段。6.3 在 DHCP 服务器端调整更新策略客户端发送的 Option 81 只是一个请求最终是否做 DNS 更新由服务器端决定。如果服务器端对标志位比较敏感可以考虑在服务端静态绑定主机名或者强制开启 DDNS。以 dnsmasq 为例可以通过 DHCP host 文件固定主机名和 IP 的对应关系再让 dnsmasq 自动写 DNS 记录# /etc/dnsmasq.d/test-dhcp.conf 追加 dhcp-host52:54:00:12:34:56,web01,192.168.56.101 dhcp-fqdndnsmasq 会优先参考 Option 81 中的标志位来决定是否注册所以在条件允许时把测试用的 DHCP 环境尽量贴近生产环境才能得出可靠的结论。如果是 Windows DHCP 服务器需要在 IPv4 作用域的“DNS”选项卡里勾选“根据此连接的 DNS 后缀在 DNS 中注册此连接的地址”并且确认“动态更新 DNS A 和 PTR 记录”没有被禁用。不同服务器实现差异较大最终都要用抓包和 DNS 查询来验证。6.4 如果必须由客户端自己更新 DNSsystemd-networkd 本身不会在执行完 DHCP 流程后调用 nsupdate 去更新 DNS。如果网络策略要求客户端主动注册 DNS比如Windows 域环境里的某些特殊情况就需要额外写一个 systemd 服务在拿到 IP 后调用 nsupdate 更新记录。这个方案比依赖 DHCP 服务器做 DDNS 更复杂但胜在不受 Option 81 标志位影响。可以简单跑一个脚本监视 .network 文件启动后的地址变化然后调用 nsupdate#!/bin/bash # /usr/local/bin/ddns-update.sh HOSTweb01 DOMAINlab.example.com IP$1 nsupdate EOF server 192.168.56.10 update delete $HOST.$DOMAIN. A update add $HOST.$DOMAIN. 300 A $IP send EOF这种方式要求 DNS 服务器允许来自客户端的 nsupdate 请求生产环境需要单独认证配置不建议在不了解安全边界的情况下直接启用。7. 从 DHCP 服务器侧验证 DNS 动态更新是否生效只抓客户端的包还不够要在服务端确认 DHCP 服务器到底有没有根据 Option 81 执行 DNS 更新。这里以 dnsmasq 为例启动时打开日志systemctl stop dnsmasq dnsmasq --conf-file/etc/dnsmasq.conf --log-dhcp --log-queries当客户端完成 DHCP 流程后dnsmasq 日志会显示租约分配信息。如果 DNS 更新成功日志里会有类似“DHCPACK(eth0) 192.168.56.101 52:54:00:12:34:56 web01”或写 A/PTR 记录的提示如果 Option 81 标志位让 dnsmasq 认为客户端自己会更新则可能看不到任何 DNS 写入动作。在客户端机器上用 dig 验证 DNS 记录dig 192.168.56.10 web01.lab.example.com dig 192.168.56.10 -x 192.168.56.101如果 A 记录和 PTR 记录都没有返回 NXDOMAIN说明 DNS 注册成功。如果抓包时看到客户端已经发了 Option 81但 dnsmasq 不写入 DNS主要怀疑点就是 flags 值不符合服务器预期。另外Windows DHCP 服务器的排查路径略有不同。可以在 DHCP 管理控制台里看租约记录的“DNS 更新”列也可以启动 DHCP 服务器上的审核日志跟踪是否有动态注册请求被拒绝。由于 Windows DHCP 服务器对 Option 81 的处理相当严格很多时候会直接把注册行为交给客户端如果你的 Linux 主机用的是 systemd-networkd就需要特别关注这一点。8. 常见问题与排查方法这里整理一份直接可用的排查清单按“客户端、服务端、DNS”三个层面排查。下面表格里的问题在 systemd-networkd 部署中比较有代表性。问题现象可能原因排查方式解决方案DHCP 能获取 IP但 DNS 查不到主机名客户端发送的 Option 81 标志位让服务器不做更新或服务器端 DDNS 未启用抓包确认 Option 81 的 Flags检查 DHCP 服务器日志在服务端强制更新或用其他 DHCP 客户端对比.network 里设置了 Hostname但报文里没有 Option 81