开机,连上网线或 WiFi,两秒后就能打开网页。中间发生的事没人注意,但它是整个网络里最基础的一次谈判:这台设备还没有任何地址,却必须先跟一台它也不知道在哪里的服务器,把地址要到手。
这一步叫 DHCP(Dynamic Host Configuration Protocol,动态主机配置协议)。报文只有四种,一来一回四次,业内按首字母叫 DORA:Discover、Offer、Request、Ack。看懂这四步,绝大多数「连上了但没有网」和「突然上不了网」都能自己定位。
一、没有地址的时候,包怎么发出去
所有 IP 通信的前提是源地址和目的地址。可刚开机的电脑三样东西全缺:自己的 IP、网关、DNS。没有源地址的包发不出去,这是一个先有鸡还是先有蛋的死循环。
DHCP 的解法是借用两个特殊地址,让第一个包不需要任何前置知识就能上线:
- 源地址写
0.0.0.0,意思是「我现在没有地址」,不是「我是 0 号」 - 目的地址写
255.255.255.255,受限广播,同一广播域所有人都收到,路由器不转发 - 目的 MAC 写
FF:FF:FF:FF:FF:FF,网卡连 IP 层都不用懂就能收下
于是第一个包被扔给同一根线上的所有设备,谁有 DHCP 服务谁来应答。传输层用 UDP,客户端固定 68 端口,服务器固定 67 端口。(IPv6 的 DHCPv6 是 546/547,且用组播 ff02::1:2 而不是广播,逻辑一样。)
注意这里有个前提:客户端和服务器必须在同一个广播域。广播不跨路由器,跨网段的情况要请中继出场,见第五节。
二、DORA 四步
| 步骤 | 方向 | 源 → 目的 | 关键内容 |
|---|---|---|---|
| Discover | 客户端 → 广播 | 0.0.0.0:68 → 255.255.255.255:67 |
我是谁(chaddr 里的 MAC)、我想要哪些参数(option 55) |
| Offer | 服务器 → 客户端 | 服务器 IP:67 → 提议给你的地址(或广播) | 给你这个地址(yiaddr)、租期(51)、网关(3)、DNS(6)、我是哪台服务器(54) |
| Request | 客户端 → 广播 | 0.0.0.0:68 → 255.255.255.255:67 |
我选了 54 这台服务器、我要 50 这个地址 |
| Ack | 服务器 → 客户端 | 服务器 IP:67 → 你的地址 | 确认生效,附完整配置参数和租期倒计时 |
第一步 Discover:0.0.0.0 → 255.255.255.255。内容非常少,主要是自己的 MAC 和一个 transaction ID(xid),后者用来把这四步串成一次协商。option 55 是「参数请求列表」,客户端在这里告诉服务器它想要什么:掩码、网关、DNS、域名、NTP……服务器返回时按这个列表挑。有些客户端还带 option 61(客户端标识),用 UUID 而不是 MAC 当身份,这解释了为什么换了网卡却还能拿到同一个地址的预留。
第二步 Offer:服务器挑一个地址给你。如果有多台 DHCP 服务器,你会收到多个 Offer,每个都带着自己的 option 54(服务器标识)。这时候客户端还没决定用哪个,地址只是「预留」状态,服务器暂时不会分给别人,但有超时。
Offer 通常是广播的,也可以用单播。为什么默认广播:客户端此刻还没有 IP 地址,单播发不回去。RFC 里那个 broadcast flag 就是干这个用的:客户端在 Discover 里置位,服务器就必须广播回复。很多实现干脆一律广播,反正只是一个小包。
第三步 Request:为什么只需要一台服务器,还得广播给所有人。这是最容易被略过、却最关键的设计。假设网里有两台 DHCP 服务器都回了 Offer,客户端挑了 A,可是B 并不知道自己落选了。B 那边那块地址已经标记成「已提供」,要等一段时间才回收。如果客户端只在单播里通知 A,B 就会一直以为地址被占着,久而久之池子里全是「幽灵租约」,真正需要地址的机器拿不到。
所以 Request 必须广播,作用是一次性通告全体服务器「我选了 option 54 里的这一台,其它的请释放」。收到这条广播后,落选服务器立刻把预留还回池子。一条广播解掉了所有服务器的状态同步问题——这就是为什么 Client 明明只要一个地址,却要对着所有人喊一遍。
第四步 Ack:确认并交出配置。Ack 里除了你早已知道的地址,还有一整套参数:租期(51)、网关(3)、DNS(6)、域名(15),以及 T1/T2 两个续租时间点(58/59)。到这一步地址才算真正可用。
如果服务器觉得不行,会回 DHCPNAK,客户端收到后必须立刻放弃手里的地址从头再来。常见于「你搬到了另一个网段,却还在请求旧网段地址」。
三、抓一次包,看里面的字段
Linux 上最省事的抓法,注意要抓 67 和 68 两个端口:
tcpdump -i eth0 -n -vv -e 'udp port 67 or udp port 68'
Discover 长这样:
IP 0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP, Request, length 300
xid 0x5f3a1b21, Flags [Broadcast]
Client-Ethernet-Address 3c:7c:3f:11:22:33
DHCP-Message Option 53: Discover
Parameter-Request Option 55: Subnet-Mask, Router, Domain-Name-Server,
Domain-Name, Broadcast-Address, NTP, Classless-Static-Route
Ack 长这样:
IP 192.168.1.10.67 > 192.168.1.50.68: BOOTP/DHCP, Reply, length 300
xid 0x5f3a1b21, Your-IP 192.168.1.50
DHCP-Message Option 53: ACK
Server-ID Option 54: 192.168.1.10
Lease-Time Option 51: 86400
Subnet-Mask Option 1: 255.255.255.0
Router Option 3: 192.168.1.1
Domain-Name-Server Option 6: 223.5.5.5, 119.29.29.29
Renewal-Time Option 58: 43200
Rebinding-Time Option 59: 75600
排错时最值得盯的字段:
| 字段 | 含义 | 为什么看它 |
|---|---|---|
xid |
本次协商的编号 | 四步应该同号;出现第二组号说明客户端重传或重来 |
chaddr |
客户端 MAC | 确认是不是你以为的那台机器在要地址 |
yiaddr |
服务器给你的地址 | 是否落在预期网段,是否和池子对得上 |
option 54 |
服务器标识 | 有多台 DHCP 时,一眼看出是谁抢答了 |
option 51 |
租期(秒) | 800 秒这种短租,通常出自访客网络或酒店 |
option 3 / 6 |
网关 / DNS | 「有 IP 不能上网」十有八九错在这两个 |
option 50 |
客户端请求的地址 | 续租时它会明确点名要原来那一个 |
还有一步容易被忽略:拿到地址后先做 ARP 探测。客户端在正式启用前会对这个地址发一次 ARP 探测(ARP probe),看有没有人已经在用。如果收到应答,说明地址冲突,客户端立刻发 DHCPDECLINE 声明这个地址不能用,然后重新 Discover。
所以「有人把静态 IP 配在了 DHCP 池里」不是小毛病:冲突会让池子里留下 DECLINE 记录,那个地址被服务器拉黑一段时间,池子越大越容易被慢慢吃掉。正确做法是把静态地址移出池子,或者在服务器上做地址保留(reservation)加池内排除。
四、租期与续租:为什么总在某些时刻集体掉线
DHCP 给你的不是永久所有权,是租期,从几小时到几天不等。三个时间点,动作完全不同:
| 时间点 | 默认比例 | 客户端动作 | 报文 |
|---|---|---|---|
| T1 续租 | 租期的 50% | 单播给原来那台服务器,我要续 | Request → Ack,安静完成 |
| T2 重绑定 | 租期的 87.5% | 还没回音就广播问任何一台服务器 | Request → Ack(广播) |
| 到期 | 100% | 停止使用该地址,从 Discover 重来 | 先发 Release,再走整套 DORA |
两个真实的现象都出在这里:
- 为什么早上九点一片人上不了网。公司电脑同时开机、工作时长接近,租期也接近同时到期,续租和重新 Discover 全挤在一个时间窗里。这不是「DHCP 服务器坏了」,是负载尖峰。缓解办法:扩充地址池、把租期按区域错开、或者把 T1 提前。
- 为什么拔了网线换个地方,一开始还能用。租约没到期,客户端会继续拿旧地址工作,然后在新网段发 Request;单播到旧服务器自然没有回音,一路退到 T2 广播,新网段的服务器 Ack 之后地址才换掉。表现就是「刚接上能上,但网页打不开」,几秒到几十秒后自愈。
五、跨网段:DHCP 中继
广播不跨路由器。所以当客户端和 DHCP 服务器不在同一个广播域时,中间那台三层设备要当中继(relay agent):把广播转成单播发给服务器,并把自己入接口的地址写进 giaddr 字段。服务器靠 giaddr 判断该从哪个池子发地址,回包再交给中继转给客户端。
Cisco 的写法:
interface Vlan10
ip address 192.168.10.1 255.255.255.0
ip helper-address 192.168.1.10
华为 / H3C 是在接口下 dhcp select relay 加 dhcp relay server-ip 192.168.1.10。
中继带来两个衍生问题:
- giaddr 对不上任何池子。服务器会直接丢弃,不会回 NAK——客户端一直收不到 Offer,表现就是死等,直到超时退到 169.254 地址。这时候抓包能看到 Discover 发出去了、服务器也收到了,就是没回音。
- option 82(中继代理信息)。不少交换机默认开启,要求服务器回包时原样带回;如果服务器不支持 option 82,会把包丢掉。典型症状:换了一台新交换机之后,只有某个 VLAN 拿不到地址,别的 VLAN 一切正常。
六、七个真实的坑
| 症状 | 原因 | 处理 |
|---|---|---|
拿到 169.254.x.x |
一个 Offer 都没收到,系统退到自动私有地址(APIPA) | 查同 VLAN、中继、地址池、交换机 snooping |
时好时坏,随机拿到 192.168.0.x |
有人私接路由器,它的 DHCP 抢先应答 | 关掉私接设备的 DHCP;交换机开 DHCP snooping,只信任上联口 |
| 有 IP,但网关 ping 不通 | 池子掩码和网关不在同一网段,或 option 3 配错 | 把 yiaddr、option 3、前缀放一起对一遍 |
| 只有某几台机器拿不到,其它正常 | 端口开了 DHCP snooping 却设成非信任,Offer 被丢 | 把上联口设为 trust,或临时关 snooping 验证 |
| 过一阵子就有人拿不到地址 | 地址池耗尽,或幽灵租约占着 | 看服务器租约表,缩租期、扩池、清理无效租约 |
| 时断时续,日志里有地址冲突 | 有人在池内配了静态地址 | 静态地址移出池子;用保留地址代替手工配置 |
| 新装的虚拟机 / 双网卡机器拿不到 | Discover 从错网卡出去了(桥接选错、有多个默认广播域) | 确认桥接网卡,先只留一块网卡测试 |
七、一套四步排错顺序
不用背命令,按这个顺序问自己四个问题,绝大多数问题在前两步就能定位:
- 有没有地址?是
169.254.x.x→ 一个 Offer 都没收到,问题在二层、中继或服务器;有地址但不是预期的网段 → 有人在抢答,抓包看 option 54。 - 能 ping 通网关吗?不通 → 掩码和网关(option 1 / 3)配错了,或者 VLAN 划错。IP 和掩码怎么核对,可以拿子网划分练习里的算法手算一遍。
- 能 ping 通 IP、但域名不行?那就不是 DHCP 分配失败,是 DNS(option 6)的问题,或者分辨率顺序的问题。
- 都对,但只有某些机器不行?去看交换机:端口 VLAN、DHCP snooping、私接设备。
Windows 上四条命令够用:
ipconfig /all :: 看租约起止时间、DHCP 服务器、网关、DNS
ipconfig /release :: 主动释放
ipconfig /renew :: 重新走一遍 DORA
ipconfig /flushdns :: 顺手清个解析缓存
Linux 上先看日志再看报文:
sudo dhclient -v eth0
sudo networkctl status
sudo journalctl -u systemd-networkd -b | grep -i dhcp
cat /var/lib/dhcp/dhclient.leases
八、一句话
DHCP 只负责「借一个地址」。接下来每一步都靠它借来的东西运转:有了地址,要用 ARP 找到网关的 MAC;出网要过 NAT 换成公网地址;把域名换成 IP 得靠 DNS。这四件事串起来,才是一次完整的「打开网页」。
接着往下看:ARP 与 MAC 地址(地址有了,怎么找到对方)、NAT 如何用一个公网 IP(出门时怎么换身份)、一次 DNS 解析要走多少步(名字怎么变成地址)。端口号可以直接查站内的TCP/UDP 常用端口速查。










暂无评论内容