你的设备一连上 Wi-Fi 就有了地址、网关和 DNS——是谁给的、怎么给的?租约到期之前设备做了什么?这篇解码本机的真实租约,向网关的 DHCP 服务器发一次真实的 DISCOVER 收下 OFFER,并用 30 分钟的被动抓包抓下本机自己的续租。
op = BOOTREPLY
htype = 1
flags = 0x0
hlen = 6
hops = 0
xid = 0x4be3ab17
secs = 0
ciaddr = <本机局域网IP>
yiaddr = <本机局域网IP>
siaddr = <本机局域网IP>
giaddr = 0.0.0.0
chaddr = 7e:aa:6f:xx:xx:xx
sname =
file =
options:
Options count is 11
dhcp_message_type (uint8): ACK 0x5
server_identifier (ip): <本机局域网IP>
lease_time (uint32): 0x708
renewal_t1_time_value (uint32): 0x331
rebinding_t2_time_value (uint32): 0x5d4
subnet_mask (ip): 255.255.252.0
broadcast_address (ip): <本机局域网IP>
router (ip_mult): {<本机局域网IP>}
domain_name (string): lan
domain_name_server (ip_mult): {114.114.114.114, 223.5.5.5}
end (none):
租约时间 1800 秒(30 分钟);T1(开始续租)= 817 秒;T2(开始向任何服务器广播请求)= 1492 秒
T1 / 租约 = 0.454,T2 / 租约 = 0.829(规范建议的默认值是 0.5 和 0.875)
这个应答的 ciaddr 不是 0.0.0.0:是 —— ciaddr 非 0 说明它回应的是一次续租(客户端已有地址时发的 REQUEST),而不是首次获取DHCP 沿用了 BOOTP 的 236 字节固定头(op、xid、ciaddr、yiaddr、siaddr、giaddr、chaddr、sname、file),后面是 4 字节 magic cookie 和 TLV 格式的选项。租约、掩码、网关、DNS 等都是选项。
DISCOVER 共 279 字节:固定头 236 + magic cookie 4 + 选项 39;op=1(BOOTREQUEST),xid=0x09562ce3,flags=0x8000(广播标志:请服务器用广播回应),chaddr=02:00:5e:xx:xx:xx(本地管理位 MAC)
选项:53=报文类型;61=客户端标识;12=主机名;55=请求的参数列表;57=最大报文长度
3 秒内在 en0 上抓到 2 个 DHCP 包,其中 xid 与我的 DISCOVER 相同的 2 个(其余是别的设备的 DHCP 流量,只统计个数)
+ 0.1 ms DISCOVER UDP 52440 → 67,IP 源 本机 en0 地址 → 目的 255.255.255.255(受限广播),IP 总长 307,DHCP 载荷 279 字节
+ 114.2 ms OFFER UDP 67 → 68,IP 源 默认网关 → 目的 255.255.255.255(受限广播),IP 总长 329,DHCP 载荷 301 字节
OFFER 内容:yiaddr = 同网段的其它地址(给我的地址),siaddr = 默认网关,chaddr 是我伪造的:True,flags=0x8000
选项(顺序 [53, 54, 51, 58, 59, 1, 28, 3, 15, 6]):租约 1800 秒,T1=900,T2=1575,子网掩码 255.255.252.0,路由器 1 个,DNS 2 个,服务器标识 默认网关
没有发 REQUEST,所以没有完成 DORA 的后两步,也没有占用这个地址;下一节用 scapy 交叉解码固定头加选项:选项是类型、长度、值的重复。自己的解码器与 scapy 对同一个 OFFER 解出同样的 10 个选项。
自己解码出的选项编号:[53, 54, 51, 58, 59, 1, 28, 3, 15, 6] scapy 解码出的选项名:['message-type', 'server_id', 'lease_time', 'renewal_time', 'rebinding_time', 'subnet_mask', 'broadcast_address', 'router', 'domain', 'name_server'] 选项个数:自己 10 / scapy 10;scapy 看到的 xid 0x09562ce3 与自己的 0x09562ce3一致;op=2,chaddr 前 6 字节一致:True DHCP 包总长 301 字节 = 固定头 236 + cookie 4 + 选项 61;BOOTP 的 sname(64 字节)和 file(128 字节)两个字段在 DHCP 里基本空着:非 0 字节数 sname 0、file 0
客户端广播 DISCOVER,服务器回 OFFER(这里是 114.2 ms),客户端再发 REQUEST,服务器回 ACK。实验只做了前两步,没有发 REQUEST,所以没有占用地址。
按 ipconfig 给出的值:租约 1800 秒:在 T1=817 秒时(约 13.6 分钟)向原服务器单播 REQUEST 续租;若没回应,到 T2=1492 秒(24.9 分钟)改为广播 REQUEST;到 1800 秒(30 分钟)租约过期就要放弃这个地址、重新从 DISCOVER 开始
被动抓包(从 09:55:46 起约 30 分钟):属于本机 MAC 的 DHCP 报文 4 个,其它设备的 157 个(只统计个数)
起点后 309.5 s REQUEST xid=0x4be3ab15 IP 本机 en0 地址 → 默认网关 以太网单播 ciaddr=本机地址
起点后 309.5 s ACK xid=0x4be3ab15 IP 默认网关 → 本机 en0 地址 以太网单播 ciaddr=本机地址
起点后 1110.5 s REQUEST xid=0x4be3ab16 IP 本机 en0 地址 → 默认网关 以太网单播 ciaddr=本机地址
起点后 1110.5 s ACK xid=0x4be3ab16 IP 默认网关 → 本机 en0 地址 以太网单播 ciaddr=本机地址
两次 REQUEST 之间的间隔:[801.0] 秒(T1 = 817 秒)本机租约 1800 秒。到 T1(ACK 里给 817 秒,实测续租间隔 801 秒)时单播 REQUEST 给原服务器,ACK 回来租约重置;没回应就在 T2 广播,到期则重新 DISCOVER。
子网掩码:DHCP 255.255.252.0 / ifconfig 255.255.252.0 → 一致(前缀长度 /22) 广播地址:DHCP 与 ifconfig 一致 路由器:DHCP 给了 1 个,默认路由的网关就是其中第一个 DNS:DHCP 给了 2 个(公共 DNS、公共 DNS);scutil --dns 里出现的 nameserver 共 2 个,其中 DHCP 给的 2 个都在里面 地址、掩码、网关、DNS、域名这些都不用手配,是 DHCP 一次交给客户端的
掩码、网关、DNS 都和 ifconfig、route、scutil 里的一致,说明系统直接采用 DHCP 给的配置。