dhcp.c
DHCP_run(),第 691-824 行)4.2 超时与重试(check_DHCP_timeout(),第 832-898 行)4.3 IP 冲突检测(check_DHCP_leasedIP(),第 900-936 行)4.4 报文帧格式与组帧(第 290-566 行)4.5 应答解析(parseDHCPMSG(),第 569-689 行)4.6 初始化与复位(第 938-979 行)4.7 与上层(ethernet.c)的时序配合5. 对外接口6. 依赖关系7. 备注与风险点源文件路径:
midware/ethernt/lan/dhcp.c所属模块:midware/ethernt/lan(W5500 以太网协议栈应用层 — DHCP/DNS/Socket) 代码行数:1017
1. 文件职责
本文件是 WIZnet 官方 DHCP 客户端协议库(W5500 版)的实现,在 EMS 固件中承担"上电时为网口申请 IP 地址"的职责:完成 DISCOVER / OFFER / REQUEST / ACK / NACK / DECLINE 六种报文的组帧、收发、解析与超时重发,并把协商结果落到 W5500 的 SIPR(本机 IP)/ SUBR(掩码)/ GAR(网关)寄存器。
它处在网络接入层的最上游:只有 DHCP 得到地址后,task_ethernet 才会进入 DNS 解析与 TCP 客户端建链环节。文件内部是标准的六态状态机(INIT/DISCOVER/REQUEST/LEASED/REREQUEST/RELEASE/STOP),由 DHCP_run() 单函数驱动,被 task_ethernet 以 1 秒周期调用(ethernet.c:1022 → dhcp_function() → dhcp_pro_fun() → DHCP_run())。
计时由外部秒计数 dhcp_tick_1s 提供,DHCP_time_handler() 由更高优先级的 task_system_aux 每秒调用一次(ethernet.c:1086)。地址分配/变更/冲突三件事通过函数指针回调(默认实现写 W5500 寄存器,工程里被 my_ip_assign() 替换)通知上层。
代码基本保持 WIZnet 原版,工程仅做了两处环境适配:主机名后拼 MAC 低 3 字节保证多机区分,以及 check_DHCP_leasedIP() 中依赖外部秒计数做忙等。
2. 关键数据结构与宏
| 名称 | 类型 | 说明 |
|---|---|---|
STATE_DHCP_INIT / _DISCOVER / _REQUEST / _LEASED / _REREQUEST / _RELEASE / _STOP | 宏 | 状态机取值 0~6(第 62-68 行);_RELEASE(5) 注释为 No use,实际未使用 |
DHCP_FLAGSBROADCAST / DHCP_FLAGSUNICAST | 宏 | 报文 flags 字段广播位 0x8000 / 单播 0x0000(第 70-71 行) |
DHCP_BOOTREQUEST / DHCP_BOOTREPLY | 宏 | BOOTP 的 op 字段:1=请求,2=应答(第 74-75 行) |
DHCP_DISCOVER … DHCP_INFORM | 宏 | option 53 报文类型:1 DISCOVER、2 OFFER、3 REQUEST、4 DECLINE、5 ACK、6 NAK、7 RELEASE(未用)、8 INFORM(未用)(第 78-85 行) |
DHCP_HTYPE10MB / DHCP_HTYPE100MB | 宏 | 硬件类型 1/2(第 87-88 行),本文件固定填 1 |
DHCP_HLENETHERNET / DHCP_HOPS / DHCP_SECS | 宏 | 硬件地址长度 6 / 跳数 0 / 秒数 0(第 90-92 行) |
INFINITE_LEASETIME | 宏 | 无限租期 0xffffffff,也是 dhcp_lease_time 的初值(第 94 行) |
OPT_SIZE / RIP_MSG_SIZE | 宏 | option 区 312 字节 / 总报文 236+312 = 548 字节(第 96-97 行) |
| option 枚举 | 枚举 | 匿名枚举(第 102-167 行),列出 RFC1533 全部 option 号;本文件实际用到 padOption(0)、subnetMask(1)、routersOnSubnet(3)、dns(6)、hostName(12)、dhcpRequestedIPaddr(50)、dhcpIPaddrLeaseTime(51)、dhcpMessageType(53)、dhcpServerIdentifier(54)、dhcpParamRequest(55)、dhcpT1value(58)、dhcpT2value(59)、dhcpClientIdentifier(61)、endOption(255) |
RIP_MSG | 结构体 | DHCP/BOOTP 报文(第 172-188 行):op,htype,hlen,hops(4B) + xid(4B) + secs,flags(4B) + ciaddr,yiaddr,siaddr,giaddr(16B) + chaddr[16] + sname[64] + file[128] + OPT[312],共 548 字节,天然对齐无填充 |
全局变量(第 190-215 行):
| 名称 | 类型 | 说明 |
|---|---|---|
DHCP_SOCKET | uint8_t | DHCP 使用的 W5500 socket 号,本工程为 SOCK_DHCP=1 |
DHCP_SIP[4] | uint8_t[] | DHCP 服务器 IP,dhcpServerIdentifier 解析结果 |
OLD_allocated_ip[4] | uint8_t[] | 续租前的旧 IP,用于判断续租后地址是否变化 |
DHCP_allocated_ip/gw/sn/dns[4] | uint8_t[] | 服务器下发的 IP / 网关 / 掩码 / DNS |
dhcp_state | int8_t | 当前状态机状态,初值 STATE_DHCP_INIT |
dhcp_retry_count | int8_t | 当前状态已超时重发次数 |
dhcp_lease_time | uint32_t | 租期(秒),初值 INFINITE_LEASETIME |
dhcp_tick_1s | volatile uint32_t | 秒计数,由 DHCP_time_handler() 递增;volatile 是 v1.1.1 的修正 |
dhcp_tick_next | uint32_t | 下一次超时阈值,初值 DHCP_WAIT_TIME(10) |
DHCP_XID | uint32_t | 事务 ID,初值 0x12345678,每次续租 ++ |
pDHCPMSG | RIP_MSG* | 指向外部传入的收发共用缓冲区 |
HOST_NAME[] | uint8_t[] | 由宏 DCHP_HOST_NAME 初始化,即 "WIZnet\0" |
DHCP_CHADDR[6] | uint8_t[] | 客户端 MAC,DHCP_init() 时从 W5500 SHAR 读出 |
dhcp_ip_assign / dhcp_ip_update / dhcp_ip_conflict | 函数指针 | 三个事件回调,默认指向 default_ip_* |
3. 函数清单
| 函数名 | 作用域 | 说明(一句话) |
|---|---|---|
default_ip_assign | 全局(第 252 行) | 默认"首次分到 IP"回调:把 IP/掩码/网关写进 W5500 的 SIPR/SUBR/GAR |
default_ip_update | 全局(第 260 行) | 默认"IP 变更"回调:先软复位 W5500,再重新下发地址与 MAC |
default_ip_conflict | 全局(第 270 行) | 默认"IP 冲突"回调:软复位 W5500 并重写 MAC |
reg_dhcp_cbfunc | 全局(第 279 行) | 注册三个回调;非空参数才覆盖默认实现 |
makeDHCPMSG | 全局(第 290 行) | 填充所有请求报文共用的 BOOTP 头 + MAGIC_COOKIE,清空 chaddr 尾部/sname/file |
send_DHCP_DISCOVER | 全局(第 350 行) | 组 DISCOVER(含 client-id、hostname、param-request 列表)并广播到 255.255.255.255:67 |
send_DHCP_REQUEST | 全局(第 413 行) | 组 REQUEST;续租态用单播 + ciaddr,初始态用广播 + option 50/54 |
send_DHCP_DECLINE | 全局(第 509 行) | 组 DECLINE(声明分配的 IP 不可用)并广播 |
parseDHCPMSG | 全局(第 569 行) | 收包并遍历 option 区,提取消息类型/掩码/网关/DNS/租期/服务器 IP,返回消息类型 |
DHCP_run | 全局(第 691 行) | DHCP 状态机主函数,每次调用收一次包并按状态推进,返回 DHCP_* 事件码 |
DHCP_stop | 全局(第 826 行) | 关闭 DHCP socket 并把状态置为 STATE_DHCP_STOP |
check_DHCP_timeout | 全局(第 832 行) | 超时判定:未超重试上限则重发,超了则降级/回退状态并返回 DHCP_FAILED |
check_DHCP_leasedIP | 全局(第 900 行) | 用 UDP 触发 ARP 的方式检测分配到的 IP 是否与局域网内其他设备冲突 |
DHCP_init | 全局(第 938 行) | 初始化:读 MAC(全 0 时填默认 MAC)、绑定 socket 与缓冲区、清空本机 IP/网关、复位计时与状态 |
reset_DHCP_timeout | 全局(第 969 行) | 秒计数清零、阈值复位为 DHCP_WAIT_TIME、重试计数清零 |
DHCP_time_handler | 全局(第 976 行) | 秒计数 dhcp_tick_1s++ |
getIPfromDHCP | 全局(第 981 行) | 拷贝 DHCP_allocated_ip 到调用者缓冲区 |
getGWfromDHCP | 全局(第 989 行) | 拷贝 DHCP_allocated_gw 到调用者缓冲区 |
getSNfromDHCP | 全局(第 997 行) | 拷贝 DHCP_allocated_sn 到调用者缓冲区 |
getDNSfromDHCP | 全局(第 1005 行) | 拷贝 DHCP_allocated_dns 到调用者缓冲区 |
getDHCPLeasetime | 全局(第 1013 行) | 返回 dhcp_lease_time |
第 218-249 行是本文件内部函数的前置声明区(default_ip_*、send_DHCP_*、check_DHCP_leasedIP、check_DHCP_timeout、reset_DHCP_timeout、parseDHCPCMSG)。文件内没有 static 函数,也没有 static 全局变量。
4. 核心逻辑解析
4.1 状态机与迁移条件(DHCP_run(),第 691-824 行)
每调用一次,函数做三件事:① 若状态为 STATE_DHCP_STOP 直接返回 DHCP_STOPPED;② 若 socket 不是 SOCK_UDP 则重新 socket(DHCP_SOCKET, Sn_MR_UDP, DHCP_CLIENT_PORT(68), 0);③ 先无条件调用 parseDHCPMSG() 收一包得到 type,再按 dhcp_state 分派。
| 当前状态 | 输入事件 | 动作 | 次态 |
|---|---|---|---|
STATE_DHCP_INIT(0) | — | 清零 DHCP_allocated_ip,send_DHCP_DISCOVER() | STATE_DHCP_DISCOVER |
STATE_DHCP_DISCOVER(1) | type == DHCP_OFFER(2) | 从 pDHCPMSG->yiaddr 取分配 IP,send_DHCP_REQUEST() | STATE_DHCP_REQUEST |
| 其它/超时 | check_DHCP_timeout()(重发或判失败) | 不变或回 INIT | |
STATE_DHCP_REQUEST(2) | type == DHCP_ACK(5) | check_DHCP_leasedIP() 通过 → dhcp_ip_assign() + 复位计时 | STATE_DHCP_LEASED,返回 DHCP_RUNNING |
type == DHCP_ACK 但 ARP 冲突 | 复位计时、dhcp_ip_conflict() | STATE_DHCP_INIT | |
type == DHCP_NAK(6) | 复位计时 | STATE_DHCP_DISCOVER | |
STATE_DHCP_LEASED(3) | 每次进入即 ret = DHCP_IP_LEASED | 当 dhcp_lease_time != 0xFFFFFFFF 且 dhcp_lease_time/2 < dhcp_tick_1s(即租期过半):保存 OLD_allocated_ip、DHCP_XID++、send_DHCP_REQUEST()、复位计时 | STATE_DHCP_REREQUEST |
STATE_DHCP_REREQUEST(4) | type == DHCP_ACK | dhcp_retry_count=0;若新旧 IP 不同 → ret = DHCP_IP_CHANGED 并 dhcp_ip_update();复位计时 | STATE_DHCP_LEASED |
type == DHCP_NAK | 复位计时 | STATE_DHCP_DISCOVER | |
STATE_DHCP_STOP(6) | — | 立即返回 DHCP_STOPPED | 不变(需重新 DHCP_init()) |
注意 STATE_DHCP_RELEASE(5) 从未被赋值到 dhcp_state,switch 中也没有对应分支,落到 default: 空操作。
4.2 超时与重试(check_DHCP_timeout(),第 832-898 行)
判据为 dhcp_tick_next < dhcp_tick_1s(严格小于)。未超时返回 DHCP_RUNNING;超时后分两档:
dhcp_retry_count < MAX_DHCP_RETRY(2):按当前状态重发对应报文(DISCOVER/REQUEST/REREQUEST都发send_DHCP_REQUEST()),并把dhcp_tick_1s = 0、dhcp_tick_next = dhcp_tick_1s + DHCP_WAIT_TIME(10)、dhcp_retry_count++。即每 10 秒重发一次,最多 2 次。- 重试次数用尽:
STATE_DHCP_DISCOVER→STATE_DHCP_INIT且返回DHCP_FAILED(该值被上层ethernet.c用作"整轮 DHCP 失败"计数);STATE_DHCP_REQUEST/STATE_DHCP_REREQUEST→ 退回send_DHCP_DISCOVER()且状态置STATE_DHCP_DISCOVER(不返回失败,直接重新走一轮)。两种情况最后都reset_DHCP_timeout()。
所以一次失败周期约为 2×10 s,DHCP_FAILED 只有"DISCOVER 阶段两次重发都拿不到 OFFER"才产生。
4.3 IP 冲突检测(check_DHCP_leasedIP(),第 900-936 行)
利用 W5500 的 ARP 硬件行为做冲突探测,技巧是"发一个 UDP 包到刚分配到的 IP:5000":
- 保存并临时把 W5500 的 RCR(重试次数寄存器,控制 ARP 超时)改为
0x03; sendto(DHCP_SOCKET, "CHECK_IP_CONFLICT", 17, DHCP_allocated_ip, 5000),此时协议栈会先发 ARP 请求;- 恢复 RCR;
- 若返回
SOCKERR_TIMEOUT→ 认为"ARP 无人应答,IP 唯一",返回 1(成功);否则 → 发send_DHCP_DECLINE()声明该地址不可用,并做最长 2 秒的忙等while((dhcp_tick_1s - ret) < 2);等待 DECLINE 发出,返回 0。
第 4 步的忙等依赖 dhcp_tick_1s 被别的任务递增。在本工程中成立:DHCP_run() 运行在 task_ethernet(app_cfg.h:68,优先级 17),而 DHCP_time_handler() 由 task_system_aux(app_cfg.h:59,优先级 8,数值更小=优先级更高)每秒调用,因此忙等可以被抢占推进而不会死锁——但代价是 task_ethernet 最多被占满 2 秒。
4.4 报文帧格式与组帧(第 290-566 行)
所有请求报文都基于 makeDHCPMSG() 生成的 BOOTP 头,再用同一个 k 下标填充 option 区(k = 4 起,因为前 4 字节是魔数):
偏移 内容 本文件的取值
0 op 1 (DHCP_BOOTREQUEST)
1 htype 1 (DHCP_HTYPE10MB)
2 hlen 6
3 hops 0
4-7 xid DHCP_XID,大端手工拆字节
8-9 secs 0
10-11 flags 0x8000 广播 / 0x0000 单播(大端手工拆字节)
12-15 ciaddr 0;续租时填 DHCP_allocated_ip
16-19 yiaddr 0(收到 OFFER 后由 parseDHCPMSG 读出)
20-27 siaddr / giaddr 全 0
28-43 chaddr[16] MAC(6B) + 10 个 0
44-107 sname[64] / file[128] 全 0
236-239 MAGIC_COOKIE 0x63825363
240-547 OPT[312] 见下
三种报文的 option 序列(k 从 4 开始写,即实际偏移 240):
send_DHCP_DISCOVER()(第 350-410 行):53/1/DISCOVER→61/7/1+MAC(client identifier)→12/len/hostname+MAC[3..5]→55/6/{1,3,6,15,58,59}(请求参数列表)→255;剩余 OPT 清零。目标地址硬编码255.255.255.255,端口67,长度RIP_MSG_SIZE=548。send_DHCP_REQUEST()(第 413-506 行):若状态为LEASED/REREQUEST则 flags 改单播、ciaddr填已分配 IP、目标为DHCP_SIP;否则广播。option:53/1/REQUEST→61/7/1+MAC→ 若目标为广播(即ip[3]==255)追加50/4/IP与54/4/服务器IP→12/...hostname →55/8/{1,3,6,15,58,59,31,33}→255。send_DHCP_DECLINE()(第 509-566 行):flags 强制单播值,option 为53/1/DECLINE→61/7/1+MAC→50/4/IP→54/4/服务器IP→255,目标广播。
主机名 option 的构造是"先写长度占位再回填":先 OPT[k++]=hostName; OPT[k++]=0;,抄完 HOST_NAME("WIZnet",循环以 \0 结束,i=6)后再补 3 个 MAC 字节,最后 OPT[k-(i+3+1)] = i+3 把长度改写为"主机名长度 + 3"(第 384、484 行)。宏 DCHP_HOST_NAME 写作 "WIZnet\0",正是为了让 for(i=0; HOST_NAME[i]!=0; i++) 在显式 \0 处停下。
4.5 应答解析(parseDHCPMSG(),第 569-689 行)
getSn_RX_RSR(DHCP_SOCKET)无数据直接返回 0(不阻塞);recvfrom()收包,得到对端 IP/端口与长度;- 端口必须是
DHCP_SERVER_PORT(67); - 逐字节比对
chaddr[0..5]与DHCP_CHADDR,不一致返回 0(过滤其它客户端的广播应答,也是本实现唯一的报文归属校验,未校验xid一致性); - option 遍历:
p = &pDHCPMSG->op + 240(240 = 236 头部 + 4 魔数),e = p + (len - 240),while(p < e)按 option 号分派:endOption(255):p = e退出循环;padOption(0):p++;dhcpMessageType(53):跳过 2 字节取类型,存入type;subnetMask(1):取 4 字节到DHCP_allocated_sn(未读长度字段,隐式假设长度 4);routersOnSubnet(3)/dns(6):读长度后取前 4 字节,再p += opt_len - 4跳过剩余;dhcpIPaddrLeaseTime(51):4 字节大端拼成dhcp_lease_time;dhcpServerIdentifier(54):4 字节到DHCP_SIP(opt_len读出但未用于跳过剩余字节);default:p++跳 option 号,读长度,p += opt_len。
- 返回
type。
4.6 初始化与复位(第 938-979 行)
DHCP_init(s, buf):getSHAR(DHCP_CHADDR) 读 W5500 的 MAC;如果 6 字节全 0,则填入临时 MAC 00:08:DC:00:00:00 并回写 W5500("必须先设 SHAR 再调用本函数"的官方注释,本工程在 ethernet.c:289 先 network_init() 保证了这点);随后保存 socket 号、把 buf 强转为 RIP_MSG*、DHCP_XID = 0x12345678、把 W5500 的 SIPR(写两次,疑似笔误,setSUBR 被写成了 setSIPR,见风险点)/ GAR 清零、reset_DHCP_timeout()、状态置 STATE_DHCP_INIT。
reset_DHCP_timeout():dhcp_tick_1s = 0、dhcp_tick_next = DHCP_WAIT_TIME(10)、dhcp_retry_count = 0。
4.7 与上层(ethernet.c)的时序配合
ethernet.c:292:reg_dhcp_cbfunc(my_ip_assign, my_ip_assign, net_init)—— 首次分到 IP 与 IP 变更都走my_ip_assign()(它把DHCP_allocated_*拷进g_stNetInfo并重做network_init()),冲突则走net_init()。ethernet.c:361:dhcp_function()以OSTimeGet() - s_u32_dhcp_ticks > SENCOND_1S为闸门,每 1 秒调用dhcp_pro_fun()一次;后者调DHCP_run()并把DHCP_IP_LEASED翻成g_stNetSysInfo.u8DHCPOk = TRUE。ethernet.c:330-343:累计DHCP_FAILED超过MY_MAX_DHCP_RETRY(3) 次后DHCP_stop(),并回落到静态网络参数。DHCP_stop()之后必须重新DHCP_init()才能再跑。- 秒计数由
ethernet.c:1086(task_system_aux)递增,与状态机不在同一任务——注意dhcp_state、dhcp_tick_1s之间存在跨任务读写,但没有临界区保护。
5. 对外接口
本文件导出的函数(声明见 dhcp.h):DHCP_init、DHCP_run、DHCP_stop、DHCP_time_handler、reg_dhcp_cbfunc、getIPfromDHCP、getGWfromDHCP、getSNfromDHCP、getDNSfromDHCP、getDHCPLeasetime。
本文件导出的全局变量(均未加 static,符号全局可见):DHCP_SOCKET、DHCP_SIP、OLD_allocated_ip、DHCP_allocated_ip、DHCP_allocated_gw、DHCP_allocated_sn、DHCP_allocated_dns、dhcp_state、dhcp_retry_count、dhcp_lease_time、dhcp_tick_1s、dhcp_tick_next、DHCP_XID、pDHCPMSG、HOST_NAME、DHCP_CHADDR,以及三个回调指针 dhcp_ip_assign / dhcp_ip_update / dhcp_ip_conflict。
另外 makeDHCPMSG、send_DHCP_DISCOVER、send_DHCP_REQUEST、send_DHCP_DECLINE、parseDHCPMSG、check_DHCP_timeout、check_DHCP_leasedIP、reset_DHCP_timeout 也是非 static 的全局符号(头文件未声明),理论上可被其它文件链接,但工程内没有其它调用者。
本文件调用的外部函数/变量:
- 来自
socket.c(经socket.h):socket()、close()、sendto()、recvfrom()、getSn_RX_RSR()、getSn_SR(); - 来自
w5500.c(经wizchip_conf.h/w5500.h):getSHAR、setSHAR、getRCR、setRCR、setMR、getMR、setSIPR、setSUBR、setGAR; - 来自
wizchip_conf.h的 socket 状态/模式宏:SOCK_UDP、Sn_MR_UDP、MR_RST; - 调试打印
printf(仅在_DHCP_DEBUG_打开时,本工程未定义)。
6. 依赖关系
- 依赖(include):
socket.h(第 52 行)、dhcp.h(第 53 行);打开_DHCP_DEBUG_时还要<stdio.h>(第 58 行)。 - 依赖(间接):经
socket.h→wizchip_conf.h→w5500.h→includes.h获得uint8_t等类型定义及 uCOS-II 环境。 - 被依赖:
midware/ethernt/ethernet.c(第 16 行 includedhcp.h,第 292/293/310/341/1086 行调用);本工程内无其它调用者。
7. 备注与风险点
- 原型与定义名字不一致:第 249 行前置声明的是
int8_t parseDHCPCMSG(void);,而第 569 行定义、第 703 行调用的是parseDHCPMSG()(少了一个C)。前者是一个既无定义、也无调用者的悬空声明(dhcp.h中并未声明任何一个名字),属官方遗留笔误;若将来有人按这行声明去调用parseDHCPCMSG,会直接链接失败。 DHCP_init()中setSIPR重复、setSUBR缺失(第 959-961 行):setSIPR(zeroip); setSIPR(zeroip); setGAR(zeroip);第二句很可能是setSUBR(zeroip)的笔误,导致子网掩码未被清零。subnetMask分支不读长度字段(第 620-630 行):按固定 4 字节推进,若服务器下发长度不为 4 的掩码 option,后续 option 解析会整体错位。- 只校验
chaddr,不校验xid(第 592-595 行):理论上可被同网段伪造的广播应答欺骗,导致误接受地址。 check_DHCP_leasedIP()的 2 秒忙等(第 932 行while((dhcp_tick_1s - ret) < 2);):占用task_ethernet(优先级 17)CPU 且不主动让出,靠task_system_aux(优先级 8)抢占递增秒计数才得以退出;若将来把DHCP_time_handler()挪进同一任务,这里会直接死锁。- 冲突检测语义被
socket.c削弱:本函数把sendto()返回SOCKERR_TIMEOUT解释为"ARP 无应答=IP 可用",但socket.c的sendto()在既无Sn_IR_SENDOK也无Sn_IR_TIMEOUT时会立即返回SOCKERR_TIMEOUT(socket.c:446-449),因此该判定会偏向"判定为无冲突",冲突检测并不可靠。 - 超时判据用严格小于(第 838 行
if (dhcp_tick_next < dhcp_tick_1s)):复位时dhcp_tick_next = 10、dhcp_tick_1s = 0,实际要等dhcp_tick_1s涨到 11 才触发,真实等待为 11 秒而非 10 秒。 getSn_SR()的隐形重开 socket(第 699-700 行):每次DHCP_run()都检查 socket 是否为 UDP 模式,不是就整掉重建;若上层的其它流程误用了 socket 1,会导致 DHCP 与业务互抢 socket。- 全局符号污染:除文档化的 API 外,
dhcp_state、pDHCPMSG、HOST_NAME、DHCP_CHADDR等内部状态都是全局符号,与工程中其它模块存在命名冲突可能,也让调试期难以判断谁改了状态。 dhcp.c与ethernet.h的缓冲区尺寸隐式耦合:RIP_MSG_SIZE(548) 必须等于MAX_DHCP_BUF_SIZE(ethernet.h:103,548),两边任一改动都会造成越界读写,建议加编译期断言。- 未使用的常量与状态:
DHCP_SECS、DHCP_HTYPE100MB、DHCP_RELEASE、DHCP_INFORM、INFINITE_LEASETIME之外的部分 option 枚举、STATE_DHCP_RELEASE均为死代码或残留定义。 DHCP_XID只在续租时递增:STATE_DHCP_INIT不会重新生成xid(只有DHCP_init()时置为固定值0x12345678),同一设备重启后xid恒为同值,RFC 上不推荐,实际影响有限。