feat(lwip/dhcps): Add support for reporting clients hostname

This commit is contained in:
David Cermak
2025-09-22 14:29:49 +02:00
parent b789198c9b
commit 75e0d62739
6 changed files with 173 additions and 7 deletions
+19
View File
@@ -405,6 +405,15 @@ menu "LWIP"
Enabling this option allows the device to run the DHCP server
(to dynamically assign IPv4 addresses to clients).
config LWIP_DHCPS_REPORT_CLIENT_HOSTNAME
bool "DHCPS: Report client hostname in assigned-IP event"
default y
depends on LWIP_DHCPS
help
When enabled, the DHCP server parses client hostname (DHCP option 12)
and makes it available via IP_EVENT_ASSIGNED_IP_TO_CLIENT. This adds
a small amount of RAM usage per lease to store the hostname.
config LWIP_DHCPS_LEASE_UNIT
int "Multiplier for lease time, in seconds"
range 1 3600
@@ -425,6 +434,16 @@ menu "LWIP"
After this number is exceeded, DHCP server removes of the oldest device
from it's address pool, without notification.
config LWIP_DHCPS_MAX_HOSTNAME_LEN
int "Maximum client hostname length stored by DHCPS"
range 1 255
default 64
depends on LWIP_DHCPS_REPORT_CLIENT_HOSTNAME
help
Maximum number of bytes stored per-client for DHCP option 12 (hostname),
including the terminating null when used in esp-netif events. Longer hostnames
will be truncated.
config LWIP_DHCPS_STATIC_ENTRIES
bool "Enable ARP static entries"
default y
+85 -1
View File
@@ -50,6 +50,7 @@
#define DHCPRELEASE 7
#define DHCP_OPTION_SUBNET_MASK 1
#define DHCP_OPTION_HOST_NAME 12
#define DHCP_OPTION_ROUTER 3
#define DHCP_OPTION_DNS_SERVER 6
#define DHCP_OPTION_REQ_IPADDR 50
@@ -142,6 +143,11 @@ struct dhcps_t {
struct udp_pcb *dhcps_pcb;
dhcps_handle_state state;
bool has_declined_ip;
#if CONFIG_LWIP_DHCPS_REPORT_CLIENT_HOSTNAME
/* Temporary storage for option 12 parsed from the current packet */
char opt_hostname[CONFIG_LWIP_DHCPS_MAX_HOSTNAME_LEN];
bool opt_hostname_present;
#endif
};
@@ -923,6 +929,33 @@ static u8_t parse_options(dhcps_t *dhcps, u8_t *optptr, s16_t len)
type = *(optptr + 2);
break;
#if CONFIG_LWIP_DHCPS_REPORT_CLIENT_HOSTNAME
case DHCP_OPTION_HOST_NAME: {
/* option format: code(1) len(1) value(len) */
u8_t olen = *(optptr + 1);
const u8_t *oval = optptr + 2;
if (olen > 0) {
/* clamp to configured max, keep room for NUL */
size_t copy_len = olen;
if (copy_len >= CONFIG_LWIP_DHCPS_MAX_HOSTNAME_LEN) {
copy_len = CONFIG_LWIP_DHCPS_MAX_HOSTNAME_LEN - 1;
}
size_t j = 0;
for (; j < copy_len; ++j) {
char c = (char)oval[j];
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '.' || c == '-' || c == '_') {
dhcps->opt_hostname[j] = c;
} else {
dhcps->opt_hostname[j] = '-';
}
}
dhcps->opt_hostname[j] = '\0';
dhcps->opt_hostname_present = true;
}
break;
}
#endif
case DHCP_OPTION_REQ_IPADDR://50
if (memcmp((char *) &client.addr, (char *) optptr + 2, 4) == 0) {
#if DHCPS_DEBUG
@@ -1111,7 +1144,7 @@ POOL_CHECK:
return 4;
}
s16_t ret = parse_options(dhcps, &m->options[4], len);;
s16_t ret = parse_options(dhcps, &m->options[4], len);
if (ret == DHCPS_STATE_RELEASE || ret == DHCPS_STATE_NAK || ret == DHCPS_STATE_DECLINE) {
if (pnode != NULL) {
@@ -1131,6 +1164,21 @@ POOL_CHECK:
memset(&dhcps->client_address, 0x0, sizeof(dhcps->client_address));
}
#if CONFIG_LWIP_DHCPS_REPORT_CLIENT_HOSTNAME
/* If we parsed a hostname from options and we have a lease entry, store it */
if (pdhcps_pool != NULL) {
if (dhcps->opt_hostname_present) {
size_t n = strnlen(dhcps->opt_hostname, CONFIG_LWIP_DHCPS_MAX_HOSTNAME_LEN);
if (n > 0) {
memset(pdhcps_pool->hostname, 0, sizeof(pdhcps_pool->hostname));
memcpy(pdhcps_pool->hostname, dhcps->opt_hostname, n);
} else {
pdhcps_pool->hostname[0] = '\0';
}
}
}
#endif
#if DHCPS_DEBUG
DHCPS_LOG("dhcps: xid changed\n");
DHCPS_LOG("dhcps: client_address.addr = %x\n", dhcps->client_address.addr);
@@ -1619,4 +1667,40 @@ err_t dhcps_dns_getserver(dhcps_t *dhcps, ip4_addr_t *dnsserver)
return dhcps_dns_getserver_by_type(dhcps, dnsserver, DNS_TYPE_MAIN);
}
#if CONFIG_LWIP_DHCPS_REPORT_CLIENT_HOSTNAME
bool dhcps_get_hostname_on_mac(dhcps_t *dhcps, const u8_t *mac, char *out, size_t out_len)
{
if ((dhcps == NULL) || (mac == NULL) || (out == NULL) || (out_len == 0)) {
return false;
}
list_node *pnode = dhcps->plist;
while (pnode) {
struct dhcps_pool *pool = pnode->pnode;
if (memcmp(pool->mac, mac, sizeof(pool->mac)) == 0) {
size_t maxcpy = (out_len > 0) ? out_len - 1 : 0;
size_t srclen = 0;
/* hostname may be empty string */
srclen = strnlen(pool->hostname, CONFIG_LWIP_DHCPS_MAX_HOSTNAME_LEN);
if (maxcpy > 0) {
if (srclen > maxcpy) srclen = maxcpy;
if (srclen) memcpy(out, pool->hostname, srclen);
out[srclen] = '\0';
}
return true;
}
pnode = pnode->pnext;
}
return false;
}
#else
bool dhcps_get_hostname_on_mac(dhcps_t *dhcps, const u8_t *mac, char *out, size_t out_len)
{
LWIP_UNUSED_ARG(dhcps);
LWIP_UNUSED_ARG(mac);
if (out && out_len) {
out[0] = '\0';
}
return false;
}
#endif
#endif // ESP_DHCPS
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -8,6 +8,7 @@
#include "sdkconfig.h"
#include <stdbool.h>
#include <stddef.h>
#include "lwip/ip_addr.h"
#include "lwip/err.h"
@@ -75,9 +76,15 @@ typedef enum
#define DHCPS_LEASE_UNIT CONFIG_LWIP_DHCPS_LEASE_UNIT
struct dhcps_pool{
ip4_addr_t ip;
u8_t mac[6];
u32_t lease_timer;
ip4_addr_t ip;
u8_t mac[6];
u32_t lease_timer;
/* Optional: client's hostname from DHCP option 12, if provided */
#if CONFIG_LWIP_DHCPS
#if CONFIG_LWIP_DHCPS_REPORT_CLIENT_HOSTNAME
char hostname[CONFIG_LWIP_DHCPS_MAX_HOSTNAME_LEN];
#endif
#endif
};
typedef u32_t dhcps_time_t;
@@ -167,6 +174,16 @@ err_t dhcps_set_option_info(dhcps_t *dhcps, u8_t op_id, void *opt_info, u32_t op
*/
bool dhcp_search_ip_on_mac(dhcps_t *dhcps, u8_t *mac, ip4_addr_t *ip);
/**
* @brief Tries to find client hostname corresponding to the supplied MAC
* @param dhcps Pointer to the DHCP handle
* @param mac Supplied MAC address
* @param out Output buffer to receive the hostname (null-terminated)
* @param out_len Size of the output buffer
* @return True if the hostname has been found and copied (may be empty string if not provided by client)
*/
bool dhcps_get_hostname_on_mac(dhcps_t *dhcps, const u8_t *mac, char *out, size_t out_len);
/**
* @brief Sets the DNS server address for the DHCP server
* @param dhcps Pointer to the DHCP handle