From bbd612e9f7090fd25e5729beddc04ae375ea5aa9 Mon Sep 17 00:00:00 2001 From: "wangtao@espressif.com" Date: Thu, 30 Apr 2026 11:50:12 +0800 Subject: [PATCH] feat(lwip): dhcp server add support for dhcp inform --- components/lwip/apps/dhcpserver/dhcpserver.c | 135 ++++++++++++++++-- .../lwip/include/apps/dhcpserver/dhcpserver.h | 1 + 2 files changed, 126 insertions(+), 10 deletions(-) diff --git a/components/lwip/apps/dhcpserver/dhcpserver.c b/components/lwip/apps/dhcpserver/dhcpserver.c index 8f3f602d5e3..e7262d96829 100644 --- a/components/lwip/apps/dhcpserver/dhcpserver.c +++ b/components/lwip/apps/dhcpserver/dhcpserver.c @@ -5,6 +5,7 @@ */ #include #include +#include #include #include "lwip/dhcp.h" #include "lwip/err.h" @@ -48,6 +49,7 @@ #define DHCPACK 5 #define DHCPNAK 6 #define DHCPRELEASE 7 +#define DHCPINFORM 8 #define DHCP_OPTION_PAD 0 #define DHCP_OPTION_SUBNET_MASK 1 @@ -100,6 +102,7 @@ #define DHCPS_STATE_NAK 4 #define DHCPS_STATE_IDLE 5 #define DHCPS_STATE_RELEASE 6 +#define DHCPS_STATE_INFORM_ACK 7 typedef enum { DHCPS_HANDLE_CREATED = 0, @@ -416,7 +419,7 @@ static u8_t *add_msg_type(u8_t *optptr, u8_t type) * Parameters : optptr -- the addr of DHCP message option * Returns : the addr of DHCP message option *******************************************************************************/ -static u8_t *add_offer_options(dhcps_t *dhcps, u8_t *optptr) +static u8_t *add_offer_options(dhcps_t *dhcps, u8_t *optptr, bool include_lease_time) { ip4_addr_t ipadd; @@ -426,12 +429,14 @@ static u8_t *add_offer_options(dhcps_t *dhcps, u8_t *optptr) *optptr++ = 4; optptr = dhcps_option_ip(optptr, &dhcps->dhcps_mask); - *optptr++ = DHCP_OPTION_LEASE_TIME; - *optptr++ = 4; - *optptr++ = ((dhcps->dhcps_lease_time * DHCPS_LEASE_UNIT) >> 24) & 0xFF; - *optptr++ = ((dhcps->dhcps_lease_time * DHCPS_LEASE_UNIT) >> 16) & 0xFF; - *optptr++ = ((dhcps->dhcps_lease_time * DHCPS_LEASE_UNIT) >> 8) & 0xFF; - *optptr++ = ((dhcps->dhcps_lease_time * DHCPS_LEASE_UNIT) >> 0) & 0xFF; + if (include_lease_time) { + *optptr++ = DHCP_OPTION_LEASE_TIME; + *optptr++ = 4; + *optptr++ = ((dhcps->dhcps_lease_time * DHCPS_LEASE_UNIT) >> 24) & 0xFF; + *optptr++ = ((dhcps->dhcps_lease_time * DHCPS_LEASE_UNIT) >> 16) & 0xFF; + *optptr++ = ((dhcps->dhcps_lease_time * DHCPS_LEASE_UNIT) >> 8) & 0xFF; + *optptr++ = ((dhcps->dhcps_lease_time * DHCPS_LEASE_UNIT) >> 0) & 0xFF; + } *optptr++ = DHCP_OPTION_SERVER_ID; *optptr++ = 4; @@ -512,6 +517,44 @@ static u8_t *add_end(u8_t *optptr) return optptr; } +/** + * Read DHCP message type (option 53) from the variable-length option area + * (starts after the magic cookie at options[4]). + */ +static u8_t dhcps_get_option_msg_type(u8_t *optptr, u16_t len) +{ + u8_t *end; + + if (len <= 0) { + return 0; + } + end = optptr + len; + + while (optptr < end) { + u8_t code = *optptr; + if (code == DHCP_OPTION_END) { + break; + } + if (code == 0) { + /* Pad */ + optptr++; + continue; + } + if (optptr + 1 >= end) { + break; + } + u8_t optlen = optptr[1]; + if ((optptr + 2 + optlen) > end) { + break; + } + if (code == DHCP_OPTION_MSG_TYPE && optlen >= 1) { + return optptr[2]; + } + optptr += (u16_t)optlen + 2; + } + return 0; +} + /****************************************************************************** * FunctionName : create_msg * Description : create response message @@ -569,7 +612,11 @@ static void dhcps_response_ip_set(dhcps_t *dhcps, struct dhcps_msg *m, ip4_addr_ /* If the 'giaddr' field is zero and the 'ciaddr' is nonzero, * the server unicasts DHCPOFFER and DHCPACK message to the address in 'ciaddr'*/ ip4_addr_set(ip4_out, &ip4_ciaddr); - etharp_add_static_entry(&ip4_ciaddr, &chaddr); + /* when 'yiaddr' is zero, means DHCPACK INFO, no need to add static entry */ + if (!ip4_addr_isany_val(ip4_yiaddr)) { + /* add the IP<->MAC as static entry into the arp table. */ + etharp_add_static_entry(&ip4_ciaddr, &chaddr); + } } else if (!BROADCAST_BIT_IS_SET(htons(m->flags))) { /* If the 'giaddr' is zero and 'ciaddr' is zero, and the broadcast bit is not set, * the server unicasts DHCPOFFER and DHCPACK message to the client's hardware address and @@ -620,7 +667,7 @@ static void send_offer(dhcps_t *dhcps, struct dhcps_msg *m, u16_t len) create_msg(dhcps, m); end = add_msg_type(&m->options[4], DHCPOFFER); - end = add_offer_options(dhcps, end); + end = add_offer_options(dhcps, end, true); LWIP_HOOK_DHCPS_POST_APPEND_OPTS(dhcps->dhcps_netif, dhcps, DHCPOFFER, &end) end = add_end(end); @@ -803,7 +850,7 @@ static void send_ack(dhcps_t *dhcps, struct dhcps_msg *m, u16_t len) create_msg(dhcps, m); end = add_msg_type(&m->options[4], DHCPACK); - end = add_offer_options(dhcps, end); + end = add_offer_options(dhcps, end, true); LWIP_HOOK_DHCPS_POST_APPEND_OPTS(dhcps->dhcps_netif, dhcps, DHCPACK, &end) end = add_end(end); @@ -870,6 +917,50 @@ static void send_ack(dhcps_t *dhcps, struct dhcps_msg *m, u16_t len) } } +/****************************************************************************** + * FunctionName : send_ack_inform + * Description : DHCPACK response to DHCPINFORM (RFC 2131: yiaddr = 0, no lease time) + * Parameters : m -- DHCP message info + * Returns : none +*******************************************************************************/ +static void send_ack_inform(dhcps_t *dhcps, struct dhcps_msg *m, u16_t len) +{ + u8_t *end; + struct pbuf *p, *q; + u8_t *data; + u16_t cnt = 0; + u16_t i; + create_msg(dhcps, m); + memset(m->yiaddr, 0, sizeof(m->yiaddr)); + + end = add_msg_type(&m->options[4], DHCPACK); + end = add_offer_options(dhcps, end, false); + LWIP_HOOK_DHCPS_POST_APPEND_OPTS(dhcps->dhcps_netif, dhcps, DHCPACK, &end) + end = add_end(end); + + p = dhcps_pbuf_alloc(len); + if (p == NULL) { + return; + } + + q = p; + while (q != NULL) { + data = (u8_t *)q->payload; + for (i = 0; i < q->len; i++) { + data[i] = ((u8_t *) m)[cnt++]; + } + q = q->next; + } + + ip_addr_t ip_temp = IPADDR4_INIT(0x0); + dhcps_response_ip_set(dhcps, m, ip_2_ip4(&ip_temp)); + udp_sendto(dhcps->dhcps_pcb, p, &ip_temp, DHCPS_CLIENT_PORT); + + if (p->ref != 0) { + pbuf_free(p); + } +} + /****************************************************************************** * FunctionName : parse_options * Description : parse DHCP message options @@ -1002,6 +1093,23 @@ static s16_t parse_msg(dhcps_t *dhcps, struct dhcps_msg *m, u16_t len) #if DHCPS_DEBUG DHCPS_LOG("dhcps: len = %d\n", len); #endif + { + u8_t msg_type = dhcps_get_option_msg_type(&m->options[4], (s16_t)len); + if (msg_type == DHCPINFORM) { + ip4_addr_t ciaddr; + memcpy(&ciaddr.addr, m->ciaddr, sizeof(ciaddr.addr)); + if (ip4_addr_isany(&ciaddr)) { + /* RFC 2131: client MUST set ciaddr to its configured address */ + return 0; + } + if (!ip4_addr_netcmp(&ciaddr, &dhcps->server_address, &dhcps->dhcps_mask)) { + /* No configuration for this client */ + return 0; + } + return DHCPS_STATE_INFORM_ACK; + } + } + ip4_addr_t addr_tmp; struct dhcps_pool *pdhcps_pool = NULL; @@ -1226,6 +1334,13 @@ static void handle_dhcp(void *arg, send_nak(dhcps, pmsg_dhcps, malloc_len); break; + case DHCPS_STATE_INFORM_ACK://7 +#if DHCPS_DEBUG + DHCPS_LOG("dhcps: handle_dhcp-> DHCPD_STATE_INFORM_ACK\n"); +#endif + send_ack_inform(dhcps, pmsg_dhcps, malloc_len); + break; + default : break; } diff --git a/components/lwip/include/apps/dhcpserver/dhcpserver.h b/components/lwip/include/apps/dhcpserver/dhcpserver.h index 975f00c73f5..aee3aef4523 100644 --- a/components/lwip/include/apps/dhcpserver/dhcpserver.h +++ b/components/lwip/include/apps/dhcpserver/dhcpserver.h @@ -61,6 +61,7 @@ typedef enum * - MAX_STATION_NUM: Maximum number of clients, set to Kconfig value CONFIG_LWIP_DHCPS_MAX_STATION_NUM * - LWIP_HOOK_DHCPS_POST_STATE: Used to inject user code after parsing DHCP message, not defined * - could be enabled in lwipopts.h or via CC_FLAGS + * - parse states include DHCPS_STATE_OFFER (1), ACK (3), NAK (4), INFORM_ACK (7), etc. * - basic usage of the hook to print hex representation of the entire option field is below: * #define LWIP_HOOK_DHCPS_POST_STATE(msg, len, state) \ * ({ s16_t ret = state; if (state == DHCPS_STATE_ACK) { ESP_LOG_BUFFER_HEXDUMP("DHCPS",msg->options, 312, ESP_LOG_INFO);} ret; })