mirror of
https://github.com/espressif/esp-idf.git
synced 2025-10-03 02:20:57 +02:00
feat(lwip/dhcps): Add support for reporting clients hostname
This commit is contained in:
@@ -152,11 +152,31 @@ typedef struct {
|
|||||||
bool preferred; /*!< The default preference of the address */
|
bool preferred; /*!< The default preference of the address */
|
||||||
} ip_event_add_ip6_t;
|
} ip_event_add_ip6_t;
|
||||||
|
|
||||||
/** Event structure for IP_EVENT_ASSIGNED_IP_TO_CLIENT event */
|
/** Event structure for IP_EVENT_ASSIGNED_IP_TO_CLIENT event
|
||||||
|
*
|
||||||
|
* This event is posted when a local DHCP server (e.g., SoftAP) assigns an IPv4
|
||||||
|
* address to a client. The structure carries the assigned IPv4 address and the
|
||||||
|
* client's MAC address. If DHCP server support is disabled via Kconfig
|
||||||
|
* (CONFIG_LWIP_DHCPS=n), the \c ip field is not populated.
|
||||||
|
*
|
||||||
|
* If enabled by Kconfig (CONFIG_LWIP_DHCPS_REPORT_CLIENT_HOSTNAME=y), the
|
||||||
|
* optional DHCP client hostname (option 12) is also included in the \c hostname
|
||||||
|
* field. The hostname is a null-terminated UTF-8 string and may be empty if the
|
||||||
|
* client did not provide one. Its maximum length (including the terminator) is
|
||||||
|
* bounded by CONFIG_LWIP_DHCPS_MAX_HOSTNAME_LEN; longer hostnames are truncated.
|
||||||
|
* The value is sanitized to contain only letters, digits, dot, dash, and
|
||||||
|
* underscore. Applications should treat this field as untrusted input.
|
||||||
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
esp_netif_t *esp_netif; /*!< Pointer to the associated netif handle */
|
esp_netif_t *esp_netif; /*!< Pointer to the associated netif handle */
|
||||||
esp_ip4_addr_t ip; /*!< IP address which was assigned to the station */
|
esp_ip4_addr_t ip; /*!< IP address which was assigned to the station */
|
||||||
uint8_t mac[6]; /*!< MAC address of the connected client */
|
uint8_t mac[6]; /*!< MAC address of the connected client */
|
||||||
|
/* Client hostname as provided via DHCP option 12 (if available). */
|
||||||
|
#ifndef CONFIG_LWIP_DHCPS_MAX_HOSTNAME_LEN
|
||||||
|
#define CONFIG_LWIP_DHCPS_MAX_HOSTNAME_LEN 64
|
||||||
|
#endif
|
||||||
|
#define ESP_NETIF_HOSTNAME_MAX_LEN CONFIG_LWIP_DHCPS_MAX_HOSTNAME_LEN
|
||||||
|
char hostname[ESP_NETIF_HOSTNAME_MAX_LEN]; /*!< Optional DHCP client hostname (may be empty string) */
|
||||||
} ip_event_assigned_ip_to_client_t;
|
} ip_event_assigned_ip_to_client_t;
|
||||||
|
|
||||||
/** Compatibility event structure for ip_event_ap_staipassigned_t event
|
/** Compatibility event structure for ip_event_ap_staipassigned_t event
|
||||||
|
@@ -1129,6 +1129,18 @@ static void esp_netif_dhcps_cb(void* arg, uint8_t ip[4], uint8_t mac[6])
|
|||||||
ESP_LOGI(TAG, "DHCP server assigned IP to a client, IP is: " IPSTR, IP2STR(&evt.ip));
|
ESP_LOGI(TAG, "DHCP server assigned IP to a client, IP is: " IPSTR, IP2STR(&evt.ip));
|
||||||
ESP_LOGD(TAG, "Client's MAC: %x:%x:%x:%x:%x:%x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
ESP_LOGD(TAG, "Client's MAC: %x:%x:%x:%x:%x:%x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||||
|
|
||||||
|
#if CONFIG_LWIP_DHCPS_REPORT_CLIENT_HOSTNAME
|
||||||
|
/* Try to fetch hostname for this MAC if available */
|
||||||
|
if (esp_netif && esp_netif->dhcps) {
|
||||||
|
/* Ensure zero-terminated even if not found */
|
||||||
|
if (!dhcps_get_hostname_on_mac(esp_netif->dhcps, mac, evt.hostname, sizeof(evt.hostname))) {
|
||||||
|
if (sizeof(evt.hostname) > 0) {
|
||||||
|
evt.hostname[0] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
int ret = esp_event_post(IP_EVENT, IP_EVENT_ASSIGNED_IP_TO_CLIENT, &evt, sizeof(evt), 0);
|
int ret = esp_event_post(IP_EVENT, IP_EVENT_ASSIGNED_IP_TO_CLIENT, &evt, sizeof(evt), 0);
|
||||||
if (ESP_OK != ret) {
|
if (ESP_OK != ret) {
|
||||||
ESP_LOGE(TAG, "dhcps cb: failed to post IP_EVENT_ASSIGNED_IP_TO_CLIENT (%x)", ret);
|
ESP_LOGE(TAG, "dhcps cb: failed to post IP_EVENT_ASSIGNED_IP_TO_CLIENT (%x)", ret);
|
||||||
|
@@ -405,6 +405,15 @@ menu "LWIP"
|
|||||||
Enabling this option allows the device to run the DHCP server
|
Enabling this option allows the device to run the DHCP server
|
||||||
(to dynamically assign IPv4 addresses to clients).
|
(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
|
config LWIP_DHCPS_LEASE_UNIT
|
||||||
int "Multiplier for lease time, in seconds"
|
int "Multiplier for lease time, in seconds"
|
||||||
range 1 3600
|
range 1 3600
|
||||||
@@ -425,6 +434,16 @@ menu "LWIP"
|
|||||||
After this number is exceeded, DHCP server removes of the oldest device
|
After this number is exceeded, DHCP server removes of the oldest device
|
||||||
from it's address pool, without notification.
|
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
|
config LWIP_DHCPS_STATIC_ENTRIES
|
||||||
bool "Enable ARP static entries"
|
bool "Enable ARP static entries"
|
||||||
default y
|
default y
|
||||||
|
@@ -50,6 +50,7 @@
|
|||||||
#define DHCPRELEASE 7
|
#define DHCPRELEASE 7
|
||||||
|
|
||||||
#define DHCP_OPTION_SUBNET_MASK 1
|
#define DHCP_OPTION_SUBNET_MASK 1
|
||||||
|
#define DHCP_OPTION_HOST_NAME 12
|
||||||
#define DHCP_OPTION_ROUTER 3
|
#define DHCP_OPTION_ROUTER 3
|
||||||
#define DHCP_OPTION_DNS_SERVER 6
|
#define DHCP_OPTION_DNS_SERVER 6
|
||||||
#define DHCP_OPTION_REQ_IPADDR 50
|
#define DHCP_OPTION_REQ_IPADDR 50
|
||||||
@@ -142,6 +143,11 @@ struct dhcps_t {
|
|||||||
struct udp_pcb *dhcps_pcb;
|
struct udp_pcb *dhcps_pcb;
|
||||||
dhcps_handle_state state;
|
dhcps_handle_state state;
|
||||||
bool has_declined_ip;
|
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);
|
type = *(optptr + 2);
|
||||||
break;
|
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
|
case DHCP_OPTION_REQ_IPADDR://50
|
||||||
if (memcmp((char *) &client.addr, (char *) optptr + 2, 4) == 0) {
|
if (memcmp((char *) &client.addr, (char *) optptr + 2, 4) == 0) {
|
||||||
#if DHCPS_DEBUG
|
#if DHCPS_DEBUG
|
||||||
@@ -1111,7 +1144,7 @@ POOL_CHECK:
|
|||||||
return 4;
|
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 (ret == DHCPS_STATE_RELEASE || ret == DHCPS_STATE_NAK || ret == DHCPS_STATE_DECLINE) {
|
||||||
if (pnode != NULL) {
|
if (pnode != NULL) {
|
||||||
@@ -1131,6 +1164,21 @@ POOL_CHECK:
|
|||||||
memset(&dhcps->client_address, 0x0, sizeof(dhcps->client_address));
|
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
|
#if DHCPS_DEBUG
|
||||||
DHCPS_LOG("dhcps: xid changed\n");
|
DHCPS_LOG("dhcps: xid changed\n");
|
||||||
DHCPS_LOG("dhcps: client_address.addr = %x\n", dhcps->client_address.addr);
|
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);
|
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
|
#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
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
#include "lwip/ip_addr.h"
|
#include "lwip/ip_addr.h"
|
||||||
#include "lwip/err.h"
|
#include "lwip/err.h"
|
||||||
|
|
||||||
@@ -78,6 +79,12 @@ struct dhcps_pool{
|
|||||||
ip4_addr_t ip;
|
ip4_addr_t ip;
|
||||||
u8_t mac[6];
|
u8_t mac[6];
|
||||||
u32_t lease_timer;
|
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;
|
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);
|
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
|
* @brief Sets the DNS server address for the DHCP server
|
||||||
* @param dhcps Pointer to the DHCP handle
|
* @param dhcps Pointer to the DHCP handle
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
*/
|
*/
|
||||||
@@ -103,6 +103,15 @@ static void wifi_event_handler(void *arg, esp_event_base_t event_base,
|
|||||||
ESP_LOGI(TAG_STA, "Got IP:" IPSTR, IP2STR(&event->ip_info.ip));
|
ESP_LOGI(TAG_STA, "Got IP:" IPSTR, IP2STR(&event->ip_info.ip));
|
||||||
s_retry_num = 0;
|
s_retry_num = 0;
|
||||||
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
|
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
|
||||||
|
} else if (event_base == IP_EVENT && event_id == IP_EVENT_ASSIGNED_IP_TO_CLIENT) {
|
||||||
|
const ip_event_assigned_ip_to_client_t *e = (const ip_event_assigned_ip_to_client_t *)event_data;
|
||||||
|
#if CONFIG_LWIP_DHCPS_REPORT_CLIENT_HOSTNAME
|
||||||
|
ESP_LOGI(TAG_AP, "Assigned IP to client: " IPSTR ", MAC=" MACSTR ", hostname='%s'",
|
||||||
|
IP2STR(&e->ip), MAC2STR(e->mac), e->hostname);
|
||||||
|
#else
|
||||||
|
ESP_LOGI(TAG_AP, "Assigned IP to client: " IPSTR ", MAC=" MACSTR,
|
||||||
|
IP2STR(&e->ip), MAC2STR(e->mac));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,6 +212,11 @@ void app_main(void)
|
|||||||
&wifi_event_handler,
|
&wifi_event_handler,
|
||||||
NULL,
|
NULL,
|
||||||
NULL));
|
NULL));
|
||||||
|
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
|
||||||
|
IP_EVENT_ASSIGNED_IP_TO_CLIENT,
|
||||||
|
&wifi_event_handler,
|
||||||
|
NULL,
|
||||||
|
NULL));
|
||||||
|
|
||||||
/*Initialize WiFi */
|
/*Initialize WiFi */
|
||||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||||
|
Reference in New Issue
Block a user