1. 从一道软考真题说起:DhcpDecline 到底是谁发给谁的
先看这道软考网络工程师的经典选择题:如果发送给 DHCP 客户端的地址已经被其他 DHCP 客户端使用,客户端会向服务器发送( )信息包拒绝接受已经分配的地址信息。选项里躺着 DhcpPack、DhcpOffer、DhcpDecline、DhcpNack 四个报文名,正确答案是 C、DhcpDecline。
这道题真正想考的不是背单词,而是搞清楚 DHCP 八种报文的方向性。很多人第一次做会选 DhcpNack,因为直觉上"拒绝"应该是服务器干的活。但这里的主语是客户端——是客户端发现地址被占用后,主动向服务器报告"这个地址我不能用"。方向搞反,整道题就崩了。
DHCP 一共八种报文,按发送者可以分成两拨。客户端发出的有四种:Discover(广播找服务器)、Request(请求使用某地址)、Decline(拒绝已分配地址)、Release(主动释放地址)。服务器发出的也有四种:Offer(提供候选地址)、ACK(确认分配)、NAK(拒绝请求)、Inform(客户端已有 IP 只求扩展配置时的应答场景)。
DhcpDecline 的触发链条是这样的:客户端收到 ACK 后并不立刻信任这个地址,它会先发一个免费 ARP(Gratuitous ARP)探测局域网内有没有人已经用了这个 IP。如果收到 ARP 回应,说明地址冲突,客户端就发 DhcpDecline 告诉服务器"这个地址废了",然后重新走 Discover 流程。服务器收到 Decline 后,会把该地址标记为冲突状态,通常冻结一段时间不再分配。
这个机制在软考里是高频考点,在实际网络排障里更是天天遇到。地址池莫名其妙变小、用户反复掉线重连、日志里刷屏 DhcpDecline,背后往往就是地址冲突在作祟。接下来我会带你在模拟环境里把整个冲突复现出来,用抓包过滤表达式亲眼看到 DhcpDecline 报文长什么样,再验证服务器地址池的恢复动作。
2. 搭建可复现的 DHCP 冲突实验环境与抓包过滤表达式
要复现 DhcpDecline,你需要三个角色:一台 DHCP 服务器、一台正常客户端、一台"捣乱"的设备(手动配置了某个 IP,制造冲突)。我试过用 Linux 上的 isc-dhcp-server 配合两台虚拟机来做,成本最低,也最接近真实排障场景。
先说服务器侧。在 Ubuntu 上装 isc-dhcp-server,配置文件/etc/dhcp/dhcpd.conf里定义一个很小的地址池,方便快速耗尽和观察:
subnet 192.168.56.0 netmask 255.255.255.0 { range 192.168.56.100 192.168.56.102; option routers 192.168.56.1; option domain-name-servers 8.8.8.8; default-lease-time 600; max-lease-time 7200; }地址池只有三个地址(.100 到 .102),这样冲突一发生,地址池很快见底,DhcpDecline 的效果特别明显。改完配置记得systemctl restart isc-dhcp-server,然后用journalctl -u isc-dhcp-server -f盯着日志。
客户端侧,用一台虚拟机把网卡设成 DHCP 自动获取。第三台"捣乱"设备,手动把 IP 固定成 192.168.56.100——也就是地址池里的第一个地址。注意,这台设备要先于正常客户端上线,或者至少保证在客户端做 ARP 探测时它在线并会回应 ARP。
抓包是这次实验的核心。在服务器或客户端所在网段跑 tcpdump,过滤表达式这样写:
tcpdump -i eth0 -n -vvv 'udp port 67 or udp port 68' -w dhcp.pcapDHCP 服务器用 UDP 67 端口,客户端用 UDP 68 端口,抓这两个端口就能覆盖全部八种报文。如果你只想看 Decline,可以加更精确的过滤:
tcpdump -i eth0 -n -vvv 'udp port 67 or udp port 68' and 'udp[240:1] = 4'这里udp[240:1] = 4是在匹配 DHCP 报文的 option 53(Message Type),值为 4 就代表 Decline。DHCP 报文类型在 option 53 里用数字表示:1=Discover、2=Offer、3=Request、4=Decline、5=ACK、6=NAK、7=Release、8=Inform。记住这组数字,抓包和看日志都用得上。
如果你更习惯用 Wireshark 图形界面,过滤表达式写bootp.option.dhcp == 4就能只看 Decline 报文。bootp 是 DHCP 的前身协议名,Wireshark 里沿用了这个过滤器前缀。
环境搭好后,先别急着制造冲突。让正常客户端先获取一次地址,确认 DORA 四步走通,抓包里能看到 Discover、Offer、Request、ACK 四个报文,这一步是基线。基线跑通,再让捣乱设备上线占住 .100,然后让客户端重新申请地址,这时候 DhcpDecline 才会登场。
3. 可复制的冲突复现配置与 DhcpDecline 触发验证步骤
现在进入实操。整个复现流程分五步,每一步都有明确的观察点。
第一步,确认捣乱设备占住地址。在第三台设备上手动配置:
ip addr add 192.168.56.100/24 dev eth0 ip link set eth0 up配完后在服务器上 ping 一下ping 192.168.56.100,能通说明地址确实被占用了。这一步很关键,如果捣乱设备没真正在线,客户端的 ARP 探测就收不到回应,DhcpDecline 不会触发。
第二步,让正常客户端释放当前地址并重新申请。Linux 下可以这样操作:
dhclient -r eth0 # 释放 dhclient -v eth0 # 重新申请,-v 输出详细过程-v参数会打印完整的报文交互,你能在终端里直接看到客户端发了 Discover、收到 Offer、发 Request、收 ACK,然后做 ARP 探测、发现冲突、发 Decline 的全过程。
第三步,盯住服务器日志。isc-dhcp-server 收到 Decline 后会打印类似这样的记录:
DHCPDECLINE on 192.168.56.0 from 08:00:27:xx:xx:xx via eth0 Abandoning IP address 192.168.56.100: declined."Abandoning"这个词是重点,服务器把这个地址标记为废弃,放进一个冲突列表,默认一段时间内(isc-dhcp-server 里由abandon-lease-time控制)不再分配。
第四步,看抓包文件。用 Wireshark 打开 dhcp.pcap,过滤bootp.option.dhcp == 4,你会看到一条 Decline 报文,源端口 68、目的端口 67,源 IP 是 0.0.0.0(客户端还没正式拿到地址),目的 IP 是 255.255.255.255 广播。展开报文详情,option 53 的值是 4,option 50(Requested IP Address)里填的就是那个冲突地址 192.168.56.100。
第五步,验证地址池恢复。客户端发完 Decline 会重新走 Discover,服务器从剩下的 .101、.102 里挑一个给客户端。这时候再抓包,能看到新一轮 DORA,最终客户端拿到 .101 或 .102。而 .100 在服务器日志里会显示为 abandoned 状态,直到冻结时间到期才重新可用。
如果你想更直观地看地址池状态,isc-dhcp-server 的租约文件/var/lib/dhcp/dhcpd.leases里会记录每个地址的状态。冲突地址会有一条binding state abandoned的记录,正常分配的则是binding state active。用grep -A5 "192.168.56.100" /var/lib/dhcp/dhcpd.leases就能看到。
这里有个容易踩的坑:有些客户端(尤其是 Windows)的免费 ARP 探测有超时和重试机制,如果捣乱设备响应慢,客户端可能误判地址可用,直接开始用,结果就是两台设备抢同一个 IP,网络时通时断。所以复现时确保捣乱设备是稳定在线的。
4. 从抓包结果反推 DHCP 八种报文在冲突场景的完整交互
把抓包结果摊开看,一次完整的地址冲突处理会涉及五种报文,按时间顺序是:Discover → Offer → Request → ACK → Decline,然后客户端重新 Discover,开启第二轮。
第一轮里,客户端广播 Discover(option 53=1),服务器回 Offer(option 53=2)提供 .100,客户端广播 Request(option 53=3)确认要 .100,服务器回 ACK(option 53=5)确认分配。到这里 DORA 走完,但客户端还没真正启用地址,它先发免费 ARP 探测 .100。捣乱设备回应了 ARP,客户端判定冲突,发 Decline(option 53=4)。
Decline 报文里有两个关键字段:option 50 填冲突的 IP 地址,option 54 填服务器标识(Server Identifier),这样服务器才知道是哪个地址、由哪个服务器分配的。服务器收到后把 .100 标记为 abandoned,客户端重新 Discover,服务器这次从剩余地址里挑一个 Offer 回来。
这里要区分清楚 Decline 和 NAK。Decline 是客户端发给服务器的,方向是"客户端→服务器",含义是"你给我的地址有问题,我拒收"。NAK 是服务器发给客户端的,方向是"服务器→客户端",含义是"你请求的地址我不能给,比如租约过期、跨网段了、或者地址已经分给别人了"。软考里经常把这两个放一起考方向性,记住"Decline 是客户端拒收,NAK 是服务器拒给"就不会错。
Release 又是另一回事。Release 是客户端主动提前释放地址,比如你关机前优雅地归还 IP,方向也是客户端→服务器,但它发生在正常使用之后,不是冲突场景。Inform 更特殊,客户端已经有 IP 了(可能是手动配的),只想问服务器要 DNS、网关这些扩展参数,发 Inform,服务器回 ACK 但不分配新地址。
把这八种报文按"谁发的、什么时候发、干什么用"三个维度列个表,记忆会牢很多:
| 报文 | 发送者 | 触发时机 | 核心作用 |
|---|---|---|---|
| Discover | 客户端 | 首次申请/重新申请 | 广播寻找服务器 |
| Offer | 服务器 | 收到 Discover | 提供候选地址 |
| Request | 客户端 | 收到 Offer 后 | 确认选用某地址 |
| ACK | 服务器 | 收到 Request | 确认分配并给租约 |
| NAK | 服务器 | 请求地址无效 | 拒绝客户端请求 |
| Decline | 客户端 | ARP 探测发现冲突 | 拒收冲突地址 |
| Release | 客户端 | 主动放弃地址 | 提前归还 IP |
| Inform | 客户端 | 已有 IP 求扩展配置 | 只取 DNS/网关等 |
回到冲突场景,整个交互的本质是客户端帮服务器做了一次"地址可用性二次校验"。服务器分配前虽然也可能 ping 探测,但服务器和客户端不在同一广播域时(跨网段走中继),服务器的探测不一定准,所以最终校验交给客户端做。客户端用免费 ARP 探测,发现冲突就 Decline,这是 DHCP 协议设计里很聪明的一环。
5. 排障实录:DhcpDecline 相关的典型报错与地址池恢复异常
实际排障中,DhcpDecline 相关的报错有几类特别常见,我按现象和原因拆开说。
第一类,服务器日志刷屏Abandoning IP address ... declined,地址池快速耗尽。这通常意味着网段里有多台设备手动配置了地址池范围内的静态 IP,或者有设备配置了错误的静态地址。排查方法是看日志里被 abandoned 的地址有哪些,然后逐个 ping 或 arp-scan 找出占用者。命令arp-scan --localnet能列出网段内所有活跃设备的 IP 和 MAC,对照被废弃的地址就能定位。
第二类,客户端反复DHCPDECLINE但服务器地址池明明有空闲。这种情况多半是客户端自己的 ARP 缓存有问题,或者网段里有设备在乱回 ARP。可以在客户端上arp -d 192.168.56.100清缓存后重试,或者抓包看是谁回应了 ARP 请求。如果回应者的 MAC 和预期不符,就是它在捣乱。
第三类,地址池恢复异常。isc-dhcp-server 默认的abandon-lease-time比较长,被 Decline 的地址可能几小时都不回收。如果地址池本来就小,几次冲突就分不出地址了。可以在dhcpd.conf里显式设置:
abandon-lease-time 300;把废弃地址的冻结时间缩短到 5 分钟,加快回收。但要注意,设太短可能导致冲突地址被反复分配,形成"分配-冲突-Decline-再分配"的死循环。合理值取决于你网段里静态 IP 的混乱程度。
第四类,跨网段场景下 Decline 丢失。如果客户端和服务器之间隔着 DHCP 中继,Decline 报文要经过中继转发才能到服务器。有些中继设备对 Decline 的处理不完整,导致服务器收不到,地址池里那个地址一直显示 active,实际却冲突。排查时在中继两侧分别抓包,对比 Decline 是否被正确转发。中继配置里要确保ip helper-address指向正确的服务器地址。
第五类,Windows 客户端的特殊行为。Windows 在收到 ACK 后会做 ARP 探测,如果探测到冲突,除了发 Decline,还会在事件日志里记录 IP 地址冲突。用ipconfig /release和ipconfig /renew可以手动触发重新申请。有时候 Windows 的探测超时设置导致它误判,可以临时禁用冲突检测(不推荐长期用)来验证是不是探测机制的问题。
排障时有个通用思路:先在客户端抓包确认 Decline 是否发出,再在服务器抓包确认是否收到,最后看服务器日志确认地址池状态是否更新。三个环节逐一排除,问题基本跑不掉。
6. 把 DHCP 报文机制用起来:从软考考点到真实网络运维
软考考 DHCP,考的是你对协议交互的理解,但真实网络运维里,这套知识是用来救火的。地址冲突导致用户上不了网,是中小企业网络里最高频的故障之一,而 DhcpDecline 就是定位这类问题的钥匙。
我建议你把这次实验的抓包文件存下来,作为以后排障的对照样本。遇到用户报"上不了网",先抓包看有没有 Decline,有的话直接去查冲突地址的占用者,比盲目重启设备高效得多。同时,在 DHCP 服务器上开启冲突检测(isc-dhcp-server 的ping-check true),让服务器分配前先 ping 一下,能挡掉一部分冲突,减轻客户端的负担。
对于需要固定 IP 的服务器、打印机,别用静态配置硬占地址池,而是在 DHCP 服务器上做 MAC 绑定:
host printer { hardware ethernet 08:00:27:aa:bb:cc; fixed-address 192.168.56.200; }这样地址由 DHCP 统一管理,不会和动态池冲突,也方便集中维护。地址池规划时,把动态范围和静态范围分开,比如动态用 .100-.200,静态用 .201-.250,从源头上减少冲突概率。
如果你在备考软考的同时也在做实际网络,这套 DHCP 报文机制值得反复琢磨。八种报文的方向、触发条件、字段含义,背下来只是第一步,能在抓包里认出来、在日志里读出来、在故障里用起来,才算真正掌握。下次遇到地址冲突,别慌,打开抓包工具,过滤bootp.option.dhcp == 4,让 DhcpDecline 告诉你真相。