feat(nan): pin datapath IPv6 neighbor via esp_netif static entry

- Add esp_wifi_netif_set_static_neighbor() that to add/remove IPv6
  static entry using netif API to skip Neighbor Discovery Protocol
- Move the fe80::/64 + EUI-64 derivation to esp_wifi_netif.c
This commit is contained in:
Sarvesh Bodakhe
2026-07-04 00:05:20 +08:00
committed by BOT
parent 310b3d6571
commit added3a060
4 changed files with 114 additions and 18 deletions
@@ -81,6 +81,42 @@ bool esp_wifi_is_if_ready_when_started(wifi_netif_driver_t ifx);
*/
esp_err_t esp_wifi_register_if_rxcb(wifi_netif_driver_t ifx, esp_netif_receive_t fn, void * arg);
/**
* @brief Derive an IPv6 link-local address from a link-layer (MAC) address
*
* Computes fe80::/64 combined with the EUI-64 form of the given MAC (the 802
* group bit complemented) into an esp_ip6_addr_t (zone 0). Interface-agnostic.
*
* @param[out] ip6 destination, set to the derived IPv6 link-local address
* @param[in] mac source link-layer (MAC) address (6 bytes)
*/
void esp_wifi_netif_get_ip6_linklocal_from_mac(esp_ip6_addr_t *ip6, const uint8_t mac[6]);
#if CONFIG_LWIP_ND6_SUPPORT_STATIC_ENTRIES
/**
* @brief Pin (or remove) a static IPv6 link-local neighbor mapping on a wifi netif
*
* Installs a fixed link-local IPv6 -> MAC mapping for a peer reachable on the
* given wifi interface so that traffic to the peer bypasses Neighbor Discovery
* (no NS/NA exchanged), or removes a previously installed one. This layer owns
* both the netif lookup (from the interface type) and the derivation of the
* peer's link-local address from its MAC, so the caller only supplies the
* interface and the peer MAC. Only available when lwIP static ND6 entries are
* enabled.
*
* @param[in] wifi_if wifi interface the peer is reachable on
* @param[in] mac peer's link-layer (MAC) address
* @param[in] add true to add the mapping, false to remove it
*
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_ARG if mac is NULL or wifi_if is out of range
* - ESP_ERR_INVALID_STATE if the interface's netif is not up
* - error code from the underlying esp_netif call otherwise
*/
esp_err_t esp_wifi_netif_set_static_neighbor(wifi_interface_t wifi_if, const uint8_t mac[6], bool add);
#endif /* CONFIG_LWIP_ND6_SUPPORT_STATIC_ENTRIES */
#ifdef __cplusplus
}
#endif
+4
View File
@@ -184,6 +184,10 @@ static void wifi_default_action_nan_started(void *arg, esp_event_base_t base, in
if (s_wifi_netifs[WIFI_IF_NAN] != NULL) {
wifi_start(s_wifi_netifs[WIFI_IF_NAN], base, event_id, data);
esp_nan_action_start(s_wifi_netifs[WIFI_IF_NAN]);
/* Bring the netif up before creating the link-local address;
* esp_netif_create_ip6_linklocal() is a no-op unless netif_is_up(). */
esp_netif_action_connected(s_wifi_netifs[WIFI_IF_NAN], base, event_id, data);
esp_netif_create_ip6_linklocal(s_wifi_netifs[WIFI_IF_NAN]);
}
}
+43
View File
@@ -3,6 +3,7 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "esp_wifi.h"
#include "esp_netif.h"
#include "esp_log.h"
@@ -181,3 +182,45 @@ esp_err_t esp_wifi_register_if_rxcb(wifi_netif_driver_t ifx, esp_netif_receive_t
}
return ESP_OK;
}
void esp_wifi_netif_get_ip6_linklocal_from_mac(esp_ip6_addr_t *ip6, const uint8_t mac[6])
{
if (ip6 == NULL || mac == NULL) {
return;
}
/* fe80::/64 + EUI-64 of the MAC (802 group bit complemented), laid out in
* network byte order straight into esp_ip6_addr_t. Zone stays 0. */
const uint8_t linklocal[16] = {
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
(uint8_t)(mac[0] ^ 0x02), mac[1], mac[2], 0xff,
0xfe, mac[3], mac[4], mac[5],
};
memset(ip6, 0, sizeof(*ip6));
memcpy(ip6->addr, linklocal, sizeof(linklocal));
}
#if CONFIG_LWIP_ND6_SUPPORT_STATIC_ENTRIES
esp_err_t esp_wifi_netif_set_static_neighbor(wifi_interface_t wifi_if, const uint8_t mac[6], bool add)
{
if (mac == NULL || wifi_if >= MAX_WIFI_IFS) {
return ESP_ERR_INVALID_ARG;
}
/* The netif handle is owned by this layer (recorded when the interface's RX
* callback is registered), so callers only need to supply the interface and
* the peer MAC. */
esp_netif_t *esp_netif = s_wifi_netifs[wifi_if];
if (esp_netif == NULL) {
return ESP_ERR_INVALID_STATE;
}
/* Derive the peer's link-local address; esp_netif copies only the address
* words for static neighbor entries, so no zone handling is needed here. */
esp_ip6_addr_t addr6;
esp_wifi_netif_get_ip6_linklocal_from_mac(&addr6, mac);
return add ? esp_netif_add_static_neighbor(esp_netif, &addr6, mac)
: esp_netif_remove_static_neighbor(esp_netif, &addr6);
}
#endif /* CONFIG_LWIP_ND6_SUPPORT_STATIC_ENTRIES */
@@ -351,20 +351,12 @@ void esp_wifi_nan_get_ipv6_linklocal_from_mac(ip6_addr_t *ip6, uint8_t *mac_addr
if (ip6 == NULL || mac_addr == NULL) {
return;
}
/* Link-local prefix. */
ip6->addr[0] = htonl(0xfe800000ul);
ip6->addr[1] = 0;
/* Assume hwaddr is a 48-bit IEEE 802 MAC. Convert to EUI-64 address. Complement Group bit. */
ip6->addr[2] = htonl((((uint32_t)(mac_addr[0] ^ 0x02)) << 24) |
((uint32_t)(mac_addr[1]) << 16) |
((uint32_t)(mac_addr[2]) << 8) |
(0xff));
ip6->addr[3] = htonl((uint32_t)(0xfeul << 24) |
((uint32_t)(mac_addr[3]) << 16) |
((uint32_t)(mac_addr[4]) << 8) |
(mac_addr[5]));
/* Reuse the interface-agnostic derivation in the esp_wifi netif layer, then
* copy the address words into the lwIP ip6_addr_t. The two structures share
* the same layout, which is how esp_netif converts between them. */
esp_ip6_addr_t esp_ip6;
esp_wifi_netif_get_ip6_linklocal_from_mac(&esp_ip6, mac_addr);
memcpy(ip6->addr, esp_ip6.addr, sizeof(ip6->addr));
ip6->zone = IP6_NO_ZONE;
}
@@ -1063,8 +1055,8 @@ static void nan_app_replied_cb(uint8_t pub_id, struct nan_cb_peer_info *peer_inf
}
static void nan_app_receive_cb(uint8_t svc_id, struct nan_cb_peer_info *peer_info,
uint8_t *shared_key_attr, uint16_t shared_key_attr_buf_len,
struct nan_cb_npba_t *npba)
uint8_t *shared_key_attr, uint16_t shared_key_attr_buf_len,
struct nan_cb_npba_t *npba)
{
if (!peer_info) {
return;
@@ -1525,8 +1517,6 @@ static void nan_app_ndp_confirm_cb(uint8_t status, struct ndp_cb_peer_info *peer
ESP_LOG_BUFFER_HEXDUMP(TAG, ssi, ssi_len, ESP_LOG_DEBUG);
}
esp_netif_action_connected(s_nan_ctx.nan_netif, WIFI_EVENT, WIFI_EVENT_NDP_CONFIRM, evt);
esp_netif_create_ip6_linklocal(s_nan_ctx.nan_netif);
NAN_DATA_UNLOCK();
ip6_addr_t peer_ip6 = {0};
@@ -1537,6 +1527,20 @@ static void nan_app_ndp_confirm_cb(uint8_t status, struct ndp_cb_peer_info *peer
ESP_LOGI(TAG, "NDP confirmed with Peer "MACSTR" [NDP ID - %d, Peer IPv6 - %s]",
MAC2STR(peer_nmi), ndp_id, inet6_ntoa(peer_ip6));
#if CONFIG_LWIP_ND6_SUPPORT_STATIC_ENTRIES
/* Pin the peer's link-local -> NDI mapping so traffic to the peer skips
* Neighbor Discovery (no NS/NA) on the NAN link. The esp_wifi netif layer
* owns the netif lookup and derives the peer's link-local from its NDI
* (the address the peer actually sources from). */
esp_err_t nbr_err = esp_wifi_netif_set_static_neighbor(WIFI_IF_NAN, peer_ndi, true);
if (nbr_err != ESP_OK) {
ESP_LOGW(TAG, "static nbr ADD failed: %s", esp_err_to_name(nbr_err));
}
#else
esp_netif_action_connected(s_nan_ctx.nan_netif, WIFI_EVENT, WIFI_EVENT_NDP_CONFIRM, evt);
esp_netif_create_ip6_linklocal(s_nan_ctx.nan_netif);
#endif
os_event_group_set_bits(nan_event_group, NDP_ACCEPTED);
nan_app_post_event(WIFI_EVENT_NDP_CONFIRM, evt, evt_data_len);
os_free(evt);
@@ -1559,6 +1563,15 @@ static void nan_app_ndp_terminated_cb(uint8_t reason, uint8_t ndp_id, uint8_t in
s_nan_ctx.event &= ~(NDP_INDICATION);
NAN_DATA_UNLOCK();
#if CONFIG_LWIP_ND6_SUPPORT_STATIC_ENTRIES
/* Drop the peer's static neighbor mapping added on NDP confirm. (It is also
* cleared automatically if the NAN netif goes down on the last datapath.) */
esp_err_t nbr_err = esp_wifi_netif_set_static_neighbor(WIFI_IF_NAN, init_ndi, false);
if (nbr_err != ESP_OK) {
ESP_LOGW(TAG, "static nbr DEL failed: %s", esp_err_to_name(nbr_err));
}
#endif
wifi_event_ndp_terminated_t *evt = (wifi_event_ndp_terminated_t *)os_zalloc(sizeof(wifi_event_ndp_terminated_t));
if (!evt) {
ESP_LOGE(TAG, "Failed to allocate for event");