Merge branch 'fix/backport_nan_fixes_v6.1' into 'release/v6.1'

Backport NAN features and a few fixes to v6.1 (Backport v6.1)

See merge request espressif/esp-idf!50349
This commit is contained in:
Jiang Jiang Jian
2026-07-04 01:03:16 +08:00
33 changed files with 3011 additions and 682 deletions
+37
View File
@@ -862,6 +862,43 @@ int esp_netif_get_all_ip6(esp_netif_t *esp_netif, esp_ip6_addr_t if_ip6[]);
*/
int esp_netif_get_all_preferred_ip6(esp_netif_t *esp_netif, esp_ip6_addr_t if_ip6[]);
#if CONFIG_LWIP_ND6_SUPPORT_STATIC_ENTRIES
/**
* @brief Add or update a static (permanent) IPv6 neighbor cache entry
*
* Installs a fixed IPv6 -> link-layer address mapping that bypasses Neighbor
* Discovery: no Neighbor Solicitation/Advertisement is exchanged for this
* address, the entry never ages out and is never overwritten by incoming
* advertisements. Calling again with the same address updates the mapping.
*
* @param[in] esp_netif Handle to esp-netif instance
* @param[in] addr Neighbor's IPv6 address
* @param[in] mac Neighbor's link-layer address (interface hwaddr_len bytes)
*
* @return
* - ESP_OK on success
* - ESP_ERR_ESP_NETIF_INVALID_PARAMS on invalid arguments
* - ESP_FAIL if the entry could not be installed (e.g. neighbor cache full)
*/
esp_err_t esp_netif_add_static_neighbor(esp_netif_t *esp_netif, const esp_ip6_addr_t *addr, const uint8_t *mac);
/**
* @brief Remove a static IPv6 neighbor cache entry
*
* Removes an entry previously added with esp_netif_add_static_neighbor().
* Dynamic (non-static) entries are left untouched.
*
* @param[in] esp_netif Handle to esp-netif instance
* @param[in] addr Neighbor's IPv6 address
*
* @return
* - ESP_OK on success
* - ESP_ERR_ESP_NETIF_INVALID_PARAMS on invalid arguments
* - ESP_FAIL if no matching static entry exists
*/
esp_err_t esp_netif_remove_static_neighbor(esp_netif_t *esp_netif, const esp_ip6_addr_t *addr);
#endif /* CONFIG_LWIP_ND6_SUPPORT_STATIC_ENTRIES */
/**
* @brief Cause the TCP/IP stack to add an IPv6 address to the interface
*
@@ -2478,6 +2478,56 @@ int esp_netif_get_all_preferred_ip6(esp_netif_t *esp_netif, esp_ip6_addr_t if_ip
}
return addr_count;
}
#if CONFIG_LWIP_ND6_SUPPORT_STATIC_ENTRIES
typedef struct {
const esp_ip6_addr_t *addr;
const uint8_t *mac;
} esp_netif_static_neighbor_t;
static esp_err_t esp_netif_add_static_neighbor_api(esp_netif_api_msg_t *msg)
{
esp_netif_t *esp_netif = msg->esp_netif;
const esp_netif_static_neighbor_t *nbr = msg->data;
ip6_addr_t ip6;
memcpy(&ip6, nbr->addr, sizeof(ip6_addr_t));
if (nd6_add_static_neighbor(esp_netif->lwip_netif, &ip6, nbr->mac) != ERR_OK) {
return ESP_FAIL;
}
return ESP_OK;
}
esp_err_t esp_netif_add_static_neighbor(esp_netif_t *esp_netif, const esp_ip6_addr_t *addr, const uint8_t *mac)
{
if (esp_netif == NULL || esp_netif->lwip_netif == NULL || addr == NULL || mac == NULL) {
return ESP_ERR_ESP_NETIF_INVALID_PARAMS;
}
esp_netif_static_neighbor_t nbr = { .addr = addr, .mac = mac };
return esp_netif_lwip_ipc_call(esp_netif_add_static_neighbor_api, esp_netif, &nbr);
}
static esp_err_t esp_netif_remove_static_neighbor_api(esp_netif_api_msg_t *msg)
{
esp_netif_t *esp_netif = msg->esp_netif;
const esp_ip6_addr_t *addr = msg->data;
ip6_addr_t ip6;
memcpy(&ip6, addr, sizeof(ip6_addr_t));
if (nd6_remove_static_neighbor(esp_netif->lwip_netif, &ip6) != ERR_OK) {
return ESP_FAIL;
}
return ESP_OK;
}
esp_err_t esp_netif_remove_static_neighbor(esp_netif_t *esp_netif, const esp_ip6_addr_t *addr)
{
if (esp_netif == NULL || esp_netif->lwip_netif == NULL || addr == NULL) {
return ESP_ERR_ESP_NETIF_INVALID_PARAMS;
}
return esp_netif_lwip_ipc_call(esp_netif_remove_static_neighbor_api, esp_netif, (void *)addr);
}
#endif /* CONFIG_LWIP_ND6_SUPPORT_STATIC_ENTRIES */
#endif
esp_netif_flags_t esp_netif_get_flags(esp_netif_t *esp_netif)
+58 -11
View File
@@ -67,8 +67,8 @@ typedef struct {
uint16_t csid_bitmap; /**< Selected Cipher Suite ID bit (WIFI_NAN_CSID_BIT_*) */
uint8_t nd_pmk[ESP_WIFI_NAN_NDP_PMK_LEN]; /**< ND-PMK */
uint8_t nd_pmkid[ESP_WIFI_NAN_NDP_PMKID_LEN]; /**< ND-PMKID */
uint8_t group_data_prot: 1; /**< Group addressed data frame protection. Reserved: not supported right now. */
uint8_t group_mgmt_prot: 1; /**< Group addressed management frame protection. Reserved: not supported right now. */
uint8_t group_data_prot: 1; /**< Group addressed data frame protection (GTKSA): distribute a GTK on the secured NDP so multicast data frames are protected. */
uint8_t group_mgmt_prot: 1; /**< Group addressed management frame protection (IGTKSA/BIGTKSA): BIP-protect multicast SDFs and Beacons. */
uint8_t reserved: 6; /**< Reserved */
} wifi_nan_security_params_t;
@@ -89,10 +89,11 @@ typedef struct {
uint8_t num_pmkids; /**< Number of parsed PMKIDs */
uint8_t pmkids[NAN_PEER_MAX_PMKIDS][ESP_WIFI_NAN_NDP_PMKID_LEN]; /**< Parsed ND-PMKIDs */
uint8_t group_data_prot: 1; /**< Peer advertises group data frame protection */
uint8_t group_mgmt_prot: 1; /**< Peer advertises group mgmt frame protection */
uint8_t group_mgmt_prot: 1; /**< Peer advertises IGTKSA (CSIA caps bits 1-2 != 0) */
uint8_t pairing_setup: 1; /**< Pairing setup: 0 - disabled, 1 - enabled */
uint8_t npk_nik_caching: 1; /**< NPK/NIK caching: 0 - disabled, 1 - enabled (valid if pairing_setup) */
uint8_t reserved: 4; /**< Reserved */
uint8_t group_bigtk_prot: 1; /**< Peer advertises BIGTKSA (CSIA caps bits 1-2 == 10) */
uint8_t reserved: 3; /**< Reserved */
} wifi_nan_peer_sdf_security_t;
/* NAN Peer info parsed from SDF */
@@ -106,6 +107,7 @@ struct nan_cb_peer_info {
uint16_t ssi_len; /**< SSI length in bytes */
wifi_nan_peer_sdf_security_t *peer_security_params; /**< Peer's discovery security params parsed from SDF */
nan_vendor_ie_t *vendor_ie; /**< Vendor-specific IE, if any */
bool nira_verified; /**< true when received NIRA tag verified against cached NIK */
};
/* NDP Peer info parsed from NAF. */
@@ -196,6 +198,7 @@ struct nan_sync_callbacks {
uint32_t (* get_nira_len)(void);
int (* construct_nira)(uint8_t *frm);
bool (*verify_nira)(uint8_t *peer_mac, uint8_t *nira_attr, uint16_t nira_attr_len);
bool (*peer_nik_cached)(uint8_t *peer_mac);
};
/* Host helpers for NAN encrypted-datapath, registered via
@@ -228,9 +231,11 @@ struct nan_secure_dp_funcs {
* with the given Key Data field length. */
uint32_t (*get_shared_key_desc_attr_len)(uint16_t key_data_len);
/* Byte length of the M4 Shared Key Descriptor including any
* encrypted KDE payload (GTK/IGTK/BIGTK). */
int (*ndp_security_install_get_shared_desc_len)(void);
/* Exact byte length of the M4 (NDP Security Install) Shared Key Descriptor
* attribute for this NDP, including any encrypted KDE payload (GTK/IGTK/BIGTK).
* @ndp_id + @peer_nmi identify the NDL so the host returns the exact length the
* builder will write (no over-reservation / on-air zero-padding). */
int (*ndp_security_install_get_shared_desc_len)(uint8_t ndp_id, const uint8_t *peer_nmi);
uint8_t (*get_ndp_resp_num_pmkids)(uint8_t ndp_id, const uint8_t *peer_nmi);
uint32_t (*get_ndp_resp_shared_key_desc_len)(uint8_t ndp_id, const uint8_t *peer_nmi);
@@ -360,10 +365,21 @@ struct nan_secure_dp_funcs {
const wifi_nan_discovery_security_params_t *sec_cfg,
wifi_nan_security_params_t *out_derived);
/* Returns the cipher-suite bitmap negotiated for an in-flight NDP,
* or 0 if the NDP is open. Used by RX/TX paths to decide whether
* to apply CCMP/GCMP and which key length to use. */
/* Returns the full cipher-suite bitmap negotiated for an in-flight NDP
* (including the group cipher NCS-GTK when group-addressed data protection
* is in use), or 0 if the NDP is open. The blob treats this as an opaque
* value passed straight to the host CSIA callbacks (construct_csia /
* get_csia_len) to build the on-air M1/M3 CSIA own-bitmap; it must NOT
* interpret individual CSID bits. Pairwise cipher selection and key install
* are host-driven and independent of this value. */
uint16_t (*get_ndp_security_csid)(uint8_t ndp_id, const uint8_t *peer_nmi);
/* Exact byte length of the M3 (NDP Confirm) Shared Key Descriptor attribute for
* this NDP, including any encrypted KDE payload (GTK/IGTK/BIGTK). @ndp_id +
* @peer_nmi identify the NDL. Mirrors ndp_security_install_get_shared_desc_len
* for the initiator path. Appended at the end of the struct to preserve the
* layout of the fields above. */
int (*ndp_confirm_get_shared_desc_len)(uint8_t ndp_id, const uint8_t *peer_nmi);
};
/**
@@ -1193,7 +1209,7 @@ esp_err_t esp_wifi_disconnect_internal(void);
uint32_t esp_nan_get_nira_len(void);
/**
* @brief Construct dummy NAN Identity Resolution Attribute (NIRA)
* @brief Construct NAN Identity Resolution Attribute (NIRA)
*
* @param[out] frm Buffer to write the attribute to
*
@@ -1201,6 +1217,19 @@ uint32_t esp_nan_get_nira_len(void);
*/
int esp_nan_construct_nira(uint8_t *frm);
/**
* @brief Construct a NAN Cipher Suite Info Attribute (CSIA)
*
* @param[out] frm Buffer to write the attribute to
* @param[in] pub_id Publish service instance id
* @param[in] own_csid_bitmap Locally supported cipher suite bitmap
* @param[in] peer_csid_bitmap Peer cipher suite bitmap, or 0 to use own bitmap
*
* @return Number of bytes written, or 0 on failure/no cipher suite
*/
int esp_nan_construct_csia(uint8_t *frm, uint8_t pub_id,
uint16_t own_csid_bitmap, uint16_t peer_csid_bitmap);
/**
* @brief Verify a received NAN Identity Resolution Attribute (NIRA)
*
@@ -1212,6 +1241,24 @@ int esp_nan_construct_nira(uint8_t *frm);
*/
bool esp_nan_verify_nira(uint8_t *peer_mac, uint8_t *nira_attr, uint16_t nira_attr_len);
/**
* @brief Verify a received NIRA and resolve the matched own service id
*
* Behaves like @ref esp_nan_verify_nira but additionally outputs the local
* service instance id the verifying NIK maps to, used to anchor a pairing
* verify-session flag. @p own_inst_id is set to 0 when the identity does not
* resolve to an active local service.
*
* @param[in] peer_mac NMI of the sender
* @param[in] nira_attr NIRA attribute buffer
* @param[in] nira_attr_len Attribute length in bytes
* @param[out] own_inst_id Resolved own service instance id (0 if none)
*
* @return true if the tag matches, false otherwise
*/
bool esp_nan_verify_nira_get_own_svc(uint8_t *peer_mac, uint8_t *nira_attr,
uint16_t nira_attr_len, uint8_t *own_inst_id);
/**
* @brief Get the time information from the MAC clock. The time is precise only if modem sleep or light sleep is not enabled.
*
@@ -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
@@ -606,6 +606,7 @@ typedef struct {
bool disable_random_mac;/**< Disable the MAC Randomisation in NAN */
bool reset_current_nvs_creds; /**< Erase all NAN credentials (own NIK and cached peer NIK/NPK entries) saved in NVS before starting. */
bool use_nvs_for_caching; /**< Persist newly-learned peer credentials (NIK/NPK) to NVS so they survive across reboots. */
bool group_mgmt_prot; /**< Device-global group management protection (IGTKSA/BIGTKSA): BIP-protect Beacons + multicast SDFs. Forces GTKSA on all secured services (CSIA caps cannot encode IGTK/BIGTK without GTKSA). */
} wifi_nan_sync_config_t;
/**
@@ -932,9 +933,9 @@ typedef enum {
WIFI_NAN_CSID_NCS_SK_256 = 2, /**< NCS-SK-256 (PSK/Passphrase). Reserved: not supported right now. */
WIFI_NAN_CSID_NCS_PK_2WDH_128 = 3, /**< NCS-PK-2WDH-128. Reserved: not supported right now. */
WIFI_NAN_CSID_NCS_PK_2WDH_256 = 4, /**< NCS-PK-2WDH-256. Reserved: not supported right now. */
WIFI_NAN_CSID_NCS_GTK_CCM_128 = 5,
WIFI_NAN_CSID_NCS_GTK_GCM_256 = 6,
WIFI_NAN_CSID_NCS_PK_PASN_128 = 7, /**< NCS-PK-PASN-128. Reserved: not supported right now. */
WIFI_NAN_CSID_NCS_GTK_CCMP_128 = 5, /**< NCS-GTK-CCMP-128, the group-data cipher (GTKSA). Selected internally when group_data_prot is set; not user-selectable via csid_bitmap. */
WIFI_NAN_CSID_NCS_GTK_GCMP_256 = 6, /**< NCS-GTK-GCMP-256. Reserved: not supported right now. */
WIFI_NAN_CSID_NCS_PK_PASN_128 = 7, /**< NCS-PK-PASN-128 (NAN Pairing). Requires CONFIG_ESP_WIFI_NAN_PAIRING and the Wi-Fi Aware component (esp-wifi-apps); not usable with stand-alone ESP-IDF. */
WIFI_NAN_CSID_NCS_PK_PASN_256 = 8, /**< NCS-PK-PASN-256. Reserved: not supported right now. */
} wifi_nan_cipher_suite_id_t;
@@ -942,8 +943,8 @@ typedef enum {
#define WIFI_NAN_CSID_BIT_NCS_SK_256 (1 << WIFI_NAN_CSID_NCS_SK_256)
#define WIFI_NAN_CSID_BIT_NCS_PK_2WDH_128 (1 << WIFI_NAN_CSID_NCS_PK_2WDH_128)
#define WIFI_NAN_CSID_BIT_NCS_PK_2WDH_256 (1 << WIFI_NAN_CSID_NCS_PK_2WDH_256)
#define WIFI_NAN_CSID_BIT_NCS_GTK_CCM_128 (1 << WIFI_NAN_CSID_NCS_GTK_CCM_128)
#define WIFI_NAN_CSID_BIT_NCS_GTK_GCM_256 (1 << WIFI_NAN_CSID_NCS_GTK_GCM_256)
#define WIFI_NAN_CSID_BIT_NCS_GTK_CCMP_128 (1 << WIFI_NAN_CSID_NCS_GTK_CCMP_128)
#define WIFI_NAN_CSID_BIT_NCS_GTK_GCMP_256 (1 << WIFI_NAN_CSID_NCS_GTK_GCMP_256)
#define WIFI_NAN_CSID_BIT_NCS_PK_PASN_128 (1 << WIFI_NAN_CSID_NCS_PK_PASN_128)
#define WIFI_NAN_CSID_BIT_NCS_PK_PASN_256 (1 << WIFI_NAN_CSID_NCS_PK_PASN_256)
@@ -974,8 +975,8 @@ typedef struct {
* is computed by the stack as the union of each credential's @c csid.
*/
typedef struct {
uint8_t group_data_prot: 1; /**< Group addressed data frame protection. Reserved: not supported right now. */
uint8_t group_mgmt_prot: 1; /**< Group addressed management frame protection. Reserved: not supported right now. */
uint8_t group_data_prot: 1; /**< Group addressed data frame protection (GTKSA): distribute a GTK on the secured NDP so multicast data frames are protected. */
uint8_t group_mgmt_prot: 1; /**< Group addressed management frame protection (IGTKSA/BIGTKSA): BIP-protect multicast SDFs and Beacons. */
uint8_t reserved: 6; /**< Reserved */
uint8_t num_credentials; /**< Number of valid entries in @c creds (0..ESP_WIFI_NAN_MAX_CREDS_PER_SVC). 0 = open service. */
wifi_nan_credential_t creds[ESP_WIFI_NAN_MAX_CREDS_PER_SVC]; /**< Credentials list. */
@@ -1305,8 +1306,6 @@ typedef enum {
WIFI_EVENT_DPP_URI_READY, /**< DPP URI is ready through Bootstrapping */
WIFI_EVENT_DPP_CFG_RECVD, /**< DPP Configuration Response; payload is wifi_event_dpp_config_received_t */
WIFI_EVENT_DPP_FAILED, /**< DPP failed */
WIFI_EVENT_NAN_BOOTSTRAP_INDICATION, /**< Received NAN Pairing Bootstrapping Request from a Peer */
WIFI_EVENT_NAN_BOOTSTRAP_COMPLETED, /**< NAN Pairing Bootstrapping completed (success/failure) */
WIFI_EVENT_NAN_PAIRING_INDICATION, /**< Received NAN Pairing indication (reserved) */
WIFI_EVENT_NAN_PAIRING_CONFIRM, /**< NAN pairing completed after NIK follow-up exchange */
WIFI_EVENT_NAN_CLUSTER_JOIN, /**< Posted when the device joins, starts, or merges into a NAN cluster */
@@ -1637,38 +1636,6 @@ typedef struct {
uint8_t init_ndi[6]; /**< Initiator's NAN Data Interface MAC */
} wifi_event_ndp_terminated_t;
/**
* @brief Argument structure for WIFI_EVENT_NAN_BOOTSTRAP_INDICATION event
*
* Posted when a NAN Pairing Bootstrapping Request is received from a peer.
* The application should respond using esp_wifi_nan_bootstrap_response().
*/
typedef struct {
uint8_t peer_svc_id; /**< Peer's service instance id */
uint8_t own_svc_id; /**< Own service instance id */
uint8_t peer_nmi[6]; /**< Peer's NAN Management Interface MAC */
uint16_t selected_method; /**< Bootstrapping method selected by initiator (one WIFI_NAN_BOOTSTRAP_* bit) */
uint8_t is_comeback; /**< 1 if this is a comeback retry with cookie */
uint32_t cookie; /**< Comeback cookie from initiator (0 if none) */
} wifi_event_nan_bootstrap_indication_t;
/**
* @brief Argument structure for WIFI_EVENT_NAN_BOOTSTRAP_COMPLETED event
*
* Posted when a NAN Pairing Bootstrapping Response is received,
* or when the bootstrapping handshake completes/fails.
*/
typedef struct {
uint8_t status; /**< 0=Accepted, 1=Rejected, 2=Comeback (wifi_nan_pairing_status_t) */
uint8_t peer_svc_id; /**< Peer's service instance id */
uint8_t own_svc_id; /**< Own service instance id */
uint8_t peer_nmi[6]; /**< Peer's NAN Management Interface MAC */
uint16_t matched_method; /**< Matched bootstrapping method, one WIFI_NAN_BOOTSTRAP_* bit (valid if accepted) */
uint8_t reason_code; /**< Rejection reason (valid if rejected) */
uint16_t comeback_after; /**< Comeback deferral time in TUs (valid if comeback) */
uint32_t cookie; /**< Comeback cookie from responder (0 if none) */
} wifi_event_nan_bootstrap_complete_t;
/**
* @brief Argument structure for WIFI_EVENT_NAN_PAIRING_CONFIRM event
*/
@@ -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
@@ -606,6 +606,7 @@ typedef struct {
bool disable_random_mac;/**< Disable the MAC Randomisation in NAN */
bool reset_current_nvs_creds; /**< Erase all NAN credentials (own NIK and cached peer NIK/NPK entries) saved in NVS before starting. */
bool use_nvs_for_caching; /**< Persist newly-learned peer credentials (NIK/NPK) to NVS so they survive across reboots. */
bool group_mgmt_prot; /**< Device-global group management protection (IGTKSA/BIGTKSA): BIP-protect Beacons + multicast SDFs. Forces GTKSA on all secured services (CSIA caps cannot encode IGTK/BIGTK without GTKSA). */
} wifi_nan_sync_config_t;
/**
@@ -932,9 +933,9 @@ typedef enum {
WIFI_NAN_CSID_NCS_SK_256 = 2, /**< NCS-SK-256 (PSK/Passphrase). Reserved: not supported right now. */
WIFI_NAN_CSID_NCS_PK_2WDH_128 = 3, /**< NCS-PK-2WDH-128. Reserved: not supported right now. */
WIFI_NAN_CSID_NCS_PK_2WDH_256 = 4, /**< NCS-PK-2WDH-256. Reserved: not supported right now. */
WIFI_NAN_CSID_NCS_GTK_CCM_128 = 5,
WIFI_NAN_CSID_NCS_GTK_GCM_256 = 6,
WIFI_NAN_CSID_NCS_PK_PASN_128 = 7, /**< NCS-PK-PASN-128. Reserved: not supported right now. */
WIFI_NAN_CSID_NCS_GTK_CCMP_128 = 5, /**< NCS-GTK-CCMP-128, the group-data cipher (GTKSA). Selected internally when group_data_prot is set; not user-selectable via csid_bitmap. */
WIFI_NAN_CSID_NCS_GTK_GCMP_256 = 6, /**< NCS-GTK-GCMP-256. Reserved: not supported right now. */
WIFI_NAN_CSID_NCS_PK_PASN_128 = 7, /**< NCS-PK-PASN-128 (NAN Pairing). Requires CONFIG_WIFI_RMT_NAN_PAIRING and the Wi-Fi Aware component (esp-wifi-apps); not usable with stand-alone ESP-IDF. */
WIFI_NAN_CSID_NCS_PK_PASN_256 = 8, /**< NCS-PK-PASN-256. Reserved: not supported right now. */
} wifi_nan_cipher_suite_id_t;
@@ -942,8 +943,8 @@ typedef enum {
#define WIFI_NAN_CSID_BIT_NCS_SK_256 (1 << WIFI_NAN_CSID_NCS_SK_256)
#define WIFI_NAN_CSID_BIT_NCS_PK_2WDH_128 (1 << WIFI_NAN_CSID_NCS_PK_2WDH_128)
#define WIFI_NAN_CSID_BIT_NCS_PK_2WDH_256 (1 << WIFI_NAN_CSID_NCS_PK_2WDH_256)
#define WIFI_NAN_CSID_BIT_NCS_GTK_CCM_128 (1 << WIFI_NAN_CSID_NCS_GTK_CCM_128)
#define WIFI_NAN_CSID_BIT_NCS_GTK_GCM_256 (1 << WIFI_NAN_CSID_NCS_GTK_GCM_256)
#define WIFI_NAN_CSID_BIT_NCS_GTK_CCMP_128 (1 << WIFI_NAN_CSID_NCS_GTK_CCMP_128)
#define WIFI_NAN_CSID_BIT_NCS_GTK_GCMP_256 (1 << WIFI_NAN_CSID_NCS_GTK_GCMP_256)
#define WIFI_NAN_CSID_BIT_NCS_PK_PASN_128 (1 << WIFI_NAN_CSID_NCS_PK_PASN_128)
#define WIFI_NAN_CSID_BIT_NCS_PK_PASN_256 (1 << WIFI_NAN_CSID_NCS_PK_PASN_256)
@@ -974,8 +975,8 @@ typedef struct {
* is computed by the stack as the union of each credential's @c csid.
*/
typedef struct {
uint8_t group_data_prot: 1; /**< Group addressed data frame protection. Reserved: not supported right now. */
uint8_t group_mgmt_prot: 1; /**< Group addressed management frame protection. Reserved: not supported right now. */
uint8_t group_data_prot: 1; /**< Group addressed data frame protection (GTKSA): distribute a GTK on the secured NDP so multicast data frames are protected. */
uint8_t group_mgmt_prot: 1; /**< Group addressed management frame protection (IGTKSA/BIGTKSA): BIP-protect multicast SDFs and Beacons. */
uint8_t reserved: 6; /**< Reserved */
uint8_t num_credentials; /**< Number of valid entries in @c creds (0..ESP_WIFI_NAN_MAX_CREDS_PER_SVC). 0 = open service. */
wifi_nan_credential_t creds[ESP_WIFI_NAN_MAX_CREDS_PER_SVC]; /**< Credentials list. */
@@ -1305,8 +1306,6 @@ typedef enum {
WIFI_EVENT_DPP_URI_READY, /**< DPP URI is ready through Bootstrapping */
WIFI_EVENT_DPP_CFG_RECVD, /**< DPP Configuration Response; payload is wifi_event_dpp_config_received_t */
WIFI_EVENT_DPP_FAILED, /**< DPP failed */
WIFI_EVENT_NAN_BOOTSTRAP_INDICATION, /**< Received NAN Pairing Bootstrapping Request from a Peer */
WIFI_EVENT_NAN_BOOTSTRAP_COMPLETED, /**< NAN Pairing Bootstrapping completed (success/failure) */
WIFI_EVENT_NAN_PAIRING_INDICATION, /**< Received NAN Pairing indication (reserved) */
WIFI_EVENT_NAN_PAIRING_CONFIRM, /**< NAN pairing completed after NIK follow-up exchange */
WIFI_EVENT_NAN_CLUSTER_JOIN, /**< Posted when the device joins, starts, or merges into a NAN cluster */
@@ -1637,38 +1636,6 @@ typedef struct {
uint8_t init_ndi[6]; /**< Initiator's NAN Data Interface MAC */
} wifi_event_ndp_terminated_t;
/**
* @brief Argument structure for WIFI_EVENT_NAN_BOOTSTRAP_INDICATION event
*
* Posted when a NAN Pairing Bootstrapping Request is received from a peer.
* The application should respond using esp_wifi_nan_bootstrap_response().
*/
typedef struct {
uint8_t peer_svc_id; /**< Peer's service instance id */
uint8_t own_svc_id; /**< Own service instance id */
uint8_t peer_nmi[6]; /**< Peer's NAN Management Interface MAC */
uint16_t selected_method; /**< Bootstrapping method selected by initiator (one WIFI_NAN_BOOTSTRAP_* bit) */
uint8_t is_comeback; /**< 1 if this is a comeback retry with cookie */
uint32_t cookie; /**< Comeback cookie from initiator (0 if none) */
} wifi_event_nan_bootstrap_indication_t;
/**
* @brief Argument structure for WIFI_EVENT_NAN_BOOTSTRAP_COMPLETED event
*
* Posted when a NAN Pairing Bootstrapping Response is received,
* or when the bootstrapping handshake completes/fails.
*/
typedef struct {
uint8_t status; /**< 0=Accepted, 1=Rejected, 2=Comeback (wifi_nan_pairing_status_t) */
uint8_t peer_svc_id; /**< Peer's service instance id */
uint8_t own_svc_id; /**< Own service instance id */
uint8_t peer_nmi[6]; /**< Peer's NAN Management Interface MAC */
uint16_t matched_method; /**< Matched bootstrapping method, one WIFI_NAN_BOOTSTRAP_* bit (valid if accepted) */
uint8_t reason_code; /**< Rejection reason (valid if rejected) */
uint16_t comeback_after; /**< Comeback deferral time in TUs (valid if comeback) */
uint32_t cookie; /**< Comeback cookie from responder (0 if none) */
} wifi_event_nan_bootstrap_complete_t;
/**
* @brief Argument structure for WIFI_EVENT_NAN_PAIRING_CONFIRM event
*/
+6 -1
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2019-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -184,6 +184,11 @@ 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 esp_netif_create_ip6_linklocal() (a no-op unless
* netif_is_up()). esp_netif_up() is private, so use the public action handler;
* NAN is non-DHCP, so it only calls esp_netif_up() and ignores the event args. */
esp_netif_action_connected(s_wifi_netifs[WIFI_IF_NAN], NULL, 0, NULL);
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 */
@@ -53,6 +53,8 @@ void esp_nan_action_stop(void);
#ifdef CONFIG_ESP_WIFI_NAN_PAIRING
#include "esp_private/wifi_types.h"
#define WIFI_NAN_PAIRING_REASON_NIK_FUP_TIMEOUT 1 /**< Local reason: peer NIK follow-up not received within timeout.
See Wi-Fi Aware v4.0 §7.6.4.2 for the NIK-exchange procedure. */
@@ -76,9 +78,46 @@ typedef struct {
uint8_t peer_svc_id;
uint8_t peer_nmi[6];
enum nan_pairing_role self_role;
uint8_t pairing_verification: 1; /**< 1 - PASN verify (re-pair), 0 - PASN auth (bootstrap pairing) */
uint8_t reserved: 7;
union pairing_cred_t cred;
} wifi_nan_pairing_config_t;
/**
* @brief NAN Pairing Bootstrap frame event (request or response).
*
* @p type is WIFI_NAN_NPBA_TYPE_REQUEST (1) for a bootstrapping request from a peer,
* or WIFI_NAN_NPBA_TYPE_RESPONSE (2) for a bootstrapping response.
* For requests, @p methods is the selected bootstrapping method.
* For responses, @p methods is the matched method, @p status and @p reason_code are valid.
*/
typedef struct {
uint8_t type; /**< WIFI_NAN_NPBA_TYPE_REQUEST or WIFI_NAN_NPBA_TYPE_RESPONSE */
uint8_t peer_svc_id; /**< Peer's service instance id */
uint8_t own_svc_id; /**< Own service instance id */
uint8_t peer_nmi[6]; /**< Peer's NAN Management Interface MAC */
uint16_t methods; /**< selected_method (request) or matched_method (response) */
uint8_t status; /**< wifi_nan_pairing_status_t; valid for response */
uint8_t reason_code; /**< Rejection reason; valid for response when rejected */
} wifi_nan_bootstrap_event_t;
/**
* @brief Callback invoked when a NAN Pairing Bootstrap request or response is received.
*
* @param evt Bootstrap frame event data.
*/
typedef void (*esp_nan_app_bootstrap_cb_t)(const wifi_nan_bootstrap_event_t *evt);
/**
* @brief Set the callback for NAN bootstrap request/response frames.
*
* @param cb Bootstrap callback, or NULL to clear.
*
* @return
* - ESP_OK: succeed
*/
esp_err_t esp_nan_app_set_bootstrap_cb(esp_nan_app_bootstrap_cb_t cb);
/**
* @brief NAN Pairing Bootstrapping status values
*/
@@ -141,7 +180,7 @@ esp_err_t esp_wifi_nan_bootstrap_request(wifi_nan_pairing_bootstrap_req_t *req);
* @brief Respond to a NAN Pairing Bootstrapping request from a peer
*
* @attention This API should be called by the Publisher after receiving a
* WIFI_EVENT_NAN_BOOTSTRAP_INDICATION event.
* bootstrap indication via the registered NAN bootstrap callback.
*
* @param resp Pairing bootstrapping response parameters.
*
@@ -185,6 +224,24 @@ struct nan_pasn_data *esp_nan_app_get_pasn_data(void);
*/
void esp_nan_app_set_pasn_data(struct nan_pasn_data *pd);
/**
* @brief Clear NM-TK and ND-TK in firmware and host state.
*
* @param peer_nmi Peer NMI (6 octets), or NULL to clear all peers on
* @p service_id.
* @param service_id Own service id; used only when @p peer_nmi is NULL.
*/
void esp_nan_app_clear_peer_tks(const uint8_t *peer_nmi, uint8_t service_id);
/**
* @brief Terminate all active NDPs with a peer (PASN verify prep).
*
* @param publish_id Own publish id
*
* @return ESP_OK if all ends succeeded or none were active.
*/
esp_err_t esp_nan_app_end_peer_datapaths(uint8_t publish_id);
#endif /* CONFIG_ESP_WIFI_NAN_PAIRING */
#ifdef __cplusplus
@@ -24,6 +24,7 @@ extern "C" {
.disable_random_mac = false, \
.reset_current_nvs_creds = false, \
.use_nvs_for_caching = false, \
.group_mgmt_prot = false, \
};
#define NDP_STATUS_ACCEPTED 1
@@ -5,6 +5,7 @@
*/
#include <ctype.h>
#include <stdio.h>
#include "esp_wifi.h"
#include "esp_private/wifi.h"
#include "esp_wifi_netif.h"
@@ -43,6 +44,18 @@ bool esp_nan_verify_nira(uint8_t *peer_mac, uint8_t *nira_attr, uint16_t nira_at
(void)nira_attr_len;
return false;
}
bool esp_nan_verify_nira_get_own_svc(uint8_t *peer_mac, uint8_t *nira_attr,
uint16_t nira_attr_len, uint8_t *own_inst_id)
{
(void)peer_mac;
(void)nira_attr;
(void)nira_attr_len;
if (own_inst_id) {
*own_inst_id = 0;
}
return false;
}
#endif
#if defined(CONFIG_ESP_WIFI_NAN_SYNC_ENABLE) && defined(CONFIG_ESP_WIFI_PASN_SUPPORT)
@@ -220,6 +233,138 @@ void nan_app_clear_paired_peers(void)
}
#endif /* CONFIG_ESP_WIFI_NAN_SECURITY */
static void nan_app_clear_one_peer_tks(const uint8_t *peer_nmi)
{
uint8_t key_rsc[8] = {0};
static const uint8_t zero_mac[6] = {0};
if (!peer_nmi || memcmp(peer_nmi, zero_mac, 6) == 0) {
return;
}
NAN_DATA_LOCK();
/* NM-TK is bound to peer NMI (see nan_pasn_install_nan_pairwise_tk). */
esp_wifi_set_nan_key_internal(NAN_WIFI_WPA_ALG_CCMP,
(uint8_t *)peer_nmi, 1, 1,
key_rsc, sizeof(key_rsc),
NULL, 0, NAN_KEY_NM_TK);
#ifdef CONFIG_ESP_WIFI_NAN_SECURITY
struct ndl_info *ndl = nan_find_ndl(0, (uint8_t *)peer_nmi);
uint8_t *key_addr = (uint8_t *)peer_nmi;
if (ndl) {
if (memcmp(ndl->peer_ndi, zero_mac, 6) != 0) {
key_addr = ndl->peer_ndi;
}
esp_wifi_set_nan_key_internal(NAN_WIFI_WPA_ALG_CCMP,
key_addr, 0, 1,
key_rsc, sizeof(key_rsc),
NULL, 0, NAN_KEY_ND_TK);
/* Drop the peer's RX GTK (bound to the peer NDI) and wipe local GTK
* state. The TX GTK keyed on the local NDI is released by the blob
* when the NDI/interface is torn down. */
esp_wifi_set_nan_key_internal(NAN_WIFI_WPA_ALG_CCMP,
key_addr, ndl->gtk_keyid, 0,
key_rsc, sizeof(key_rsc),
NULL, 0, NAN_KEY_ND_GTK);
/* Drop the peer's RX IGTK/BIGTK (BIP-CMAC-128, installed against the
* peer NMI at NDP confirm) so no stale BIP keys linger in the blob. */
if (ndl->igtk_set) {
esp_wifi_set_nan_key_internal(NAN_WIFI_WPA_ALG_BIP_CMAC_128,
(uint8_t *)peer_nmi, ndl->igtk_keyid, 0,
key_rsc, 6,
NULL, 0, NAN_KEY_ND_IGTK);
}
if (ndl->bigtk_set) {
esp_wifi_set_nan_key_internal(NAN_WIFI_WPA_ALG_BIP_CMAC_128,
(uint8_t *)peer_nmi, ndl->bigtk_keyid, 0,
key_rsc, 6,
NULL, 0, NAN_KEY_ND_BIGTK);
}
forced_memzero(ndl->nd_tk, sizeof(ndl->nd_tk));
forced_memzero(ndl->nd_kck, sizeof(ndl->nd_kck));
forced_memzero(ndl->nd_kek, sizeof(ndl->nd_kek));
forced_memzero(ndl->gtk, sizeof(ndl->gtk));
forced_memzero(ndl->own_gtk, sizeof(ndl->own_gtk));
forced_memzero(ndl->igtk, sizeof(ndl->igtk));
forced_memzero(ndl->bigtk, sizeof(ndl->bigtk));
ndl->ptk_set = 0;
ndl->tk_len = 0;
ndl->kck_len = 0;
ndl->kek_len = 0;
ndl->gtk_set = 0;
ndl->own_gtk_set = 0;
ndl->gtk_len = 0;
ndl->own_gtk_len = 0;
ndl->igtk_set = 0;
ndl->bigtk_set = 0;
ndl->igtk_len = 0;
ndl->bigtk_len = 0;
}
/* Fallback when no NDL slot tracks peer_ndi yet. */
esp_wifi_set_nan_key_internal(NAN_WIFI_WPA_ALG_CCMP,
(uint8_t *)peer_nmi, 0, 1,
key_rsc, sizeof(key_rsc),
NULL, 0, NAN_KEY_ND_TK);
#endif /* CONFIG_ESP_WIFI_NAN_SECURITY */
NAN_DATA_UNLOCK();
}
void esp_nan_app_clear_peer_tks(const uint8_t *peer_nmi, uint8_t service_id)
{
if (peer_nmi) {
nan_app_clear_one_peer_tks(peer_nmi);
return;
}
if (service_id == 0) {
return;
}
#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE
uint8_t peer_nmis[NAN_MAX_PEERS_RECORD][MACADDR_LEN];
int peer_count = 0;
struct own_svc_info *p_own_svc;
struct peer_svc_info *temp;
NAN_DATA_LOCK();
p_own_svc = nan_find_own_svc(service_id);
if (!p_own_svc) {
NAN_DATA_UNLOCK();
return;
}
SLIST_FOREACH(temp, &(p_own_svc->peer_list), next) {
bool dup = false;
for (int i = 0; i < peer_count; i++) {
if (MACADDR_EQUAL(peer_nmis[i], temp->peer_nmi)) {
dup = true;
break;
}
}
if (!dup && peer_count < NAN_MAX_PEERS_RECORD) {
MACADDR_COPY(peer_nmis[peer_count], temp->peer_nmi);
peer_count++;
}
}
NAN_DATA_UNLOCK();
for (int i = 0; i < peer_count; i++) {
nan_app_clear_one_peer_tks(peer_nmis[i]);
}
#else
(void)service_id;
#endif /* CONFIG_ESP_WIFI_NAN_SYNC_ENABLE */
}
#endif /* CONFIG_ESP_WIFI_NAN_PAIRING */
void esp_wifi_nan_get_ipv6_linklocal_from_mac(ip6_addr_t *ip6, uint8_t *mac_addr)
@@ -227,20 +372,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;
}
@@ -491,14 +628,29 @@ static struct own_svc_info *nan_claim_own_svc_slot(uint8_t type, const char svc_
forced_memzero(&p_svc->derived_security, sizeof(p_svc->derived_security));
if (security_cfg) {
memcpy(&p_svc->user_cfg, security_cfg, sizeof(*security_cfg));
/* Device-global group_mgmt_prot (IGTKSA/BIGTKSA) forces GTKSA on every
* secured service: the CSIA capability field has no "IGTK/BIGTK without
* GTKSA" encoding (§9.5.21.2 Table 122), so advertising group-management
* protection mandates advertising GTKSA. Warn and force it on if the app
* requested group_data_prot=0. Forcing support never blocks a peer that
* lacks protection — keys activate only after capability negotiation. */
if (s_nan_ctx.group_mgmt_prot && !p_svc->user_cfg.group_data_prot) {
ESP_LOGW(TAG, "group_data_prot forced ON for '%s': group_mgmt_prot is "
"enabled device-wide; GTKSA cannot be advertised without it",
svc_name);
p_svc->user_cfg.group_data_prot = 1;
}
}
#ifdef CONFIG_ESP_WIFI_NAN_PAIRING
if (pairing) {
memcpy(&p_svc->pairing, pairing, sizeof(*pairing));
}
#else
(void)pairing;
#endif
#else
(void)security_cfg;
(void)pairing;
#endif
return p_svc;
}
@@ -568,6 +720,7 @@ static void nan_record_new_ndl(uint8_t ndp_id, uint8_t publish_id, uint8_t peer_
if (ndl && reuse_slot) {
ndl->ndp_id = ndp_id;
ndl->own_role = own_role;
ndl->device_caps = device_caps;
return;
}
if (ndl) {
@@ -843,6 +996,9 @@ static void nan_app_service_match_cb(uint8_t sub_id, struct nan_cb_peer_info *pe
#ifdef CONFIG_ESP_WIFI_NAN_PAIRING
evt->bootstrapping_methods = nan_app_parse_npba_from_publish(npba);
if (peer_info->nira_verified) {
evt->already_paired = 1;
}
#endif
evt->ssi_version = ssi_ver;
@@ -869,13 +1025,31 @@ static void nan_app_replied_cb(uint8_t pub_id, struct nan_cb_peer_info *peer_inf
}
uint8_t sub_id = peer_info->peer_svc_id;
uint8_t *sub_nmi = peer_info->peer_mac;
uint8_t *ssi = peer_info->ssi;
uint16_t ssi_len = peer_info->ssi_len;
uint32_t device_caps = peer_info->device_caps;
NAN_DATA_LOCK();
if (!nan_find_peer_svc(pub_id, sub_id, sub_nmi)) {
struct peer_svc_info *p_peer_svc = nan_find_peer_svc(pub_id, sub_id, sub_nmi);
if (!p_peer_svc) {
p_peer_svc = nan_find_peer_svc(pub_id, 0, sub_nmi);
}
if (!p_peer_svc) {
nan_record_peer_svc(pub_id, sub_id, sub_nmi, device_caps);
} else {
if (p_peer_svc->svc_id != sub_id) {
p_peer_svc->svc_id = sub_id;
}
if (p_peer_svc->own_svc_id != pub_id) {
p_peer_svc->own_svc_id = pub_id;
}
if (p_peer_svc->device_caps != device_caps) {
p_peer_svc->device_caps = device_caps;
}
if (!MACADDR_EQUAL(p_peer_svc->peer_nmi, sub_nmi)) {
MACADDR_COPY(p_peer_svc->peer_nmi, sub_nmi);
}
}
NAN_DATA_UNLOCK();
@@ -890,7 +1064,6 @@ static void nan_app_replied_cb(uint8_t pub_id, struct nan_cb_peer_info *peer_inf
evt->subscribe_id = sub_id;
MACADDR_COPY(evt->sub_if_mac, sub_nmi);
ESP_LOGI(TAG, "Sent Publish to Peer "MACSTR" [Peer Subscribe id - %d]", MAC2STR(sub_nmi), sub_id);
if (ssi && ssi_len) {
memcpy(evt->ssi, ssi, ssi_len);
evt->ssi_len = ssi_len;
@@ -902,8 +1075,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;
@@ -915,8 +1088,25 @@ static void nan_app_receive_cb(uint8_t svc_id, struct nan_cb_peer_info *peer_inf
uint32_t device_caps = peer_info->device_caps;
NAN_DATA_LOCK();
if (!nan_find_peer_svc(svc_id, peer_svc_id, peer_mac)) {
struct peer_svc_info *p_peer_svc = nan_find_peer_svc(svc_id, peer_svc_id, peer_mac);
if (!p_peer_svc) {
p_peer_svc = nan_find_peer_svc(svc_id, 0, peer_mac);
}
if (!p_peer_svc) {
nan_record_peer_svc(svc_id, peer_svc_id, peer_mac, device_caps);
} else {
if (p_peer_svc->svc_id != peer_svc_id) {
p_peer_svc->svc_id = peer_svc_id;
}
if (p_peer_svc->own_svc_id != svc_id) {
p_peer_svc->own_svc_id = svc_id;
}
if (p_peer_svc->device_caps != device_caps) {
p_peer_svc->device_caps = device_caps;
}
if (!MACADDR_EQUAL(p_peer_svc->peer_nmi, peer_mac)) {
MACADDR_COPY(p_peer_svc->peer_nmi, peer_mac);
}
}
NAN_DATA_UNLOCK();
@@ -931,6 +1121,7 @@ static void nan_app_receive_cb(uint8_t svc_id, struct nan_cb_peer_info *peer_inf
#if defined(CONFIG_ESP_WIFI_NAN_PAIRING)
if (npba) {
nan_app_parse_npba_from_receive(svc_id, peer_svc_id, peer_mac, npba);
return;
}
#endif
@@ -995,12 +1186,15 @@ static void nan_app_ndp_indication_cb(uint8_t pub_id, struct ndp_cb_peer_info *p
nan_record_new_ndl(ndp_id, pub_id, peer_nmi, ESP_WIFI_NDP_ROLE_RESPONDER, device_caps);
if (!nan_find_peer_svc(pub_id, 0, peer_nmi)) {
struct peer_svc_info *p_peer_svc = nan_find_peer_svc(pub_id, 0, peer_nmi);
if (!p_peer_svc) {
nan_record_peer_svc(pub_id, 0, peer_nmi, device_caps);
} else {
p_peer_svc->device_caps = device_caps;
}
struct ndl_info *ndl = nan_find_ndl(ndp_id, (uint8_t *)peer_nmi);
if (ndl && peer_ndi) {
struct ndl_info *ndl = nan_find_ndl(ndp_id, peer_nmi);
if (ndl) {
MACADDR_COPY(ndl->peer_ndi, peer_ndi);
}
@@ -1024,8 +1218,13 @@ static void nan_app_ndp_indication_cb(uint8_t pub_id, struct ndp_cb_peer_info *p
uint8_t own_bssid[6];
esp_err_t err = esp_wifi_get_mac(WIFI_IF_NAN, own_bssid);
if (err != ESP_OK) {
/* Cannot build the auto-response: free the NDL slot and deny the
* peer so it does not wait indefinitely. Send outside the lock. */
ESP_LOGE(TAG, "get own NAN MAC failed, rc=0x%x; denying NDP ndp_id=%d", err, ndp_id);
nan_reset_ndl(ndp_id, false);
NAN_DATA_UNLOCK();
ESP_LOGE(TAG, "Cannot get own BSSID!");
ndp_resp.accept = false;
esp_nan_internal_datapath_resp(&ndp_resp, (uint8_t *)&own_ipv6.u_addr.ip6.addr[2]);
return;
}
esp_wifi_nan_get_ipv6_linklocal_from_mac(&own_ipv6.u_addr.ip6, own_bssid);
@@ -1207,10 +1406,6 @@ static void nan_app_ndp_confirm_cb(uint8_t status, struct ndp_cb_peer_info *peer
goto done;
}
#ifndef NAN_KEY_ND_TK
#define NAN_KEY_ND_TK 0
#endif
#ifdef CONFIG_ESP_WIFI_NAN_SECURITY
if (ndl->security_ctx.type == WIFI_NAN_SECURITY_ENCRYPTED) {
uint8_t key_rsc[8] = {0};
@@ -1223,12 +1418,103 @@ static void nan_app_ndp_confirm_cb(uint8_t status, struct ndp_cb_peer_info *peer
ndl->nd_tk,
NAN_NCS_SK_128_TK_LEN,
NAN_KEY_ND_TK);
ESP_LOG_BUFFER_HEXDUMP("ND-TK", ndl->nd_tk, NAN_NCS_SK_128_TK_LEN, ESP_LOG_DEBUG);
if (ret != 0) {
ESP_LOGE(TAG, "NDP confirm: failed to install NAN pairwise key (ndp_id=%d, ret=%d)", ndp_id, ret);
ESP_LOGE(TAG, "NDP confirm: failed to install ND-TK (ndp_id=%d, ret=%d)", ndp_id, ret);
os_free(evt);
nan_ndp_confirm_teardown(peer_nmi, ndp_id);
goto done;
}
ret = esp_wifi_set_nan_key_internal(NAN_WIFI_WPA_ALG_CCMP,
peer_nmi,
0,
1,
key_rsc,
sizeof(key_rsc),
ndl->nd_tk,
NAN_NCS_SK_128_TK_LEN,
NAN_KEY_NM_TK);
if (ret != 0) {
ESP_LOGE(TAG, "NDP confirm: failed to install NM-TK (ndp_id=%d, ret=%d)", ndp_id, ret);
os_free(evt);
nan_ndp_confirm_teardown(peer_nmi, ndp_id);
goto done;
}
/* Group keys (ND-GTK) exchanged during NDP setup (§7.1.3.2): our GTK
* is the TX key bound to the local NDI; the peer's GTK is the RX key
* bound to the peer NDI. Best-effort — a GTK install failure must not
* tear down the working unicast datapath. */
if (ndl->own_gtk_set && ndl->own_gtk_len) {
int gret = esp_wifi_set_nan_key_internal(NAN_WIFI_WPA_ALG_CCMP,
own_ndi, ndl->own_gtk_keyid, 1,
ndl->own_gtk_rsc, NAN_KEY_RSC_LEN,
ndl->own_gtk, ndl->own_gtk_len,
NAN_KEY_ND_GTK);
if (gret != 0) {
ESP_LOGW(TAG, "NDP confirm: own GTK (TX) install failed (ndp_id=%d, ret=%d)", ndp_id, gret);
} else {
ESP_LOGI(TAG, "NDP confirm: own GTK (TX) installed (keyid=%d)", ndl->own_gtk_keyid);
}
ESP_LOG_BUFFER_HEXDUMP("ND-GTK", ndl->own_gtk, ndl->own_gtk_len, ESP_LOG_DEBUG);
}
if (ndl->gtk_set && ndl->gtk_len) {
int gret = esp_wifi_set_nan_key_internal(NAN_WIFI_WPA_ALG_CCMP,
peer_ndi, ndl->gtk_keyid, 0,
ndl->gtk_rsc, NAN_KEY_RSC_LEN,
ndl->gtk, ndl->gtk_len,
NAN_KEY_ND_GTK);
if (gret != 0) {
ESP_LOGW(TAG, "NDP confirm: peer GTK (RX) install failed (ndp_id=%d, ret=%d)", ndp_id, gret);
} else {
ESP_LOGI(TAG, "NDP confirm: peer GTK (RX) installed (keyid=%d)", ndl->gtk_keyid);
}
}
/* Own IGTK/BIGTK (TX) are installed once at NAN start (see
* nan_security_install_own_group_integrity_keys); not re-installed here,
* to preserve the blob's monotonic BIPN/IPN across the session. Only the
* peer RX keys are bound at NDP confirm. §7.1.3.3/§7.1.3.4; NMI==NDI today.
* Peer IGTK/BIGTK install RX-only against the peer NMI, seeding the BIP
* RX replay counter with the peer's advertised IPN/BIPN from the KDE. */
if (ndl->igtk_set && ndl->igtk_len) {
if (ndl->igtk_len != NAN_ND_GTK_LEN) {
ESP_LOGW(TAG, "NDP confirm: peer IGTK len=%d unsupported (BIP-CMAC-128 only); skipping",
ndl->igtk_len);
} else {
int r = esp_wifi_set_nan_key_internal(NAN_WIFI_WPA_ALG_BIP_CMAC_128,
peer_nmi, ndl->igtk_keyid, 0,
ndl->igtk_ipn, 6,
ndl->igtk, ndl->igtk_len,
NAN_KEY_ND_IGTK);
if (r != 0) {
ESP_LOGW(TAG, "NDP confirm: peer IGTK (RX) install failed, rc=0x%x (ndp_id=%d)", r, ndp_id);
} else {
ESP_LOGI(TAG, "NDP confirm: peer IGTK (RX) installed (keyid=%d)", ndl->igtk_keyid);
}
/* Peer IGTK bytes for sniffer MIC cross-check vs the peer's multicast SDFs. */
ESP_LOG_BUFFER_HEXDUMP("PEER ND-IGTK", ndl->igtk, ndl->igtk_len, ESP_LOG_DEBUG);
}
}
if (ndl->bigtk_set && ndl->bigtk_len) {
if (ndl->bigtk_len != NAN_ND_GTK_LEN) {
ESP_LOGW(TAG, "NDP confirm: peer BIGTK len=%d unsupported (BIP-CMAC-128 only); skipping",
ndl->bigtk_len);
} else {
int r = esp_wifi_set_nan_key_internal(NAN_WIFI_WPA_ALG_BIP_CMAC_128,
peer_nmi, ndl->bigtk_keyid, 0,
ndl->bigtk_ipn, 6,
ndl->bigtk, ndl->bigtk_len,
NAN_KEY_ND_BIGTK);
if (r != 0) {
ESP_LOGW(TAG, "NDP confirm: peer BIGTK (RX) install failed, rc=0x%x (ndp_id=%d)", r, ndp_id);
} else {
ESP_LOGI(TAG, "NDP confirm: peer BIGTK (RX) installed (keyid=%d)", ndl->bigtk_keyid);
}
/* Peer BIGTK bytes for sniffer MIC cross-check vs the peer's protected Beacons. */
ESP_LOG_BUFFER_HEXDUMP("PEER ND-BIGTK", ndl->bigtk, ndl->bigtk_len, ESP_LOG_DEBUG);
}
}
}
#endif /* CONFIG_ESP_WIFI_NAN_SECURITY */
evt->status = status;
@@ -1250,8 +1536,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};
@@ -1262,6 +1546,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);
@@ -1284,6 +1582,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");
@@ -1311,6 +1618,7 @@ static void nan_action_txdone_cb(uint32_t context, bool tx_status)
static void esp_nan_ndp_tx_done_cb(uint8_t ndp_id, const uint8_t *peer_nmi, uint8_t msg_type, bool tx_status)
{
NAN_DATA_LOCK();
struct ndl_info *ndl = nan_find_ndl(ndp_id, (uint8_t *)peer_nmi);
@@ -1353,6 +1661,7 @@ static struct nan_secure_dp_funcs s_nan_secure_dp_funcs = {
.ndp_security_install_get_shared_desc_len = esp_nan_ndp_security_install_get_shared_desc_len,
.get_ndp_resp_num_pmkids = esp_nan_get_ndp_resp_num_pmkids,
.get_ndp_resp_shared_key_desc_len = esp_nan_get_ndp_resp_shared_key_desc_len,
.ndp_confirm_get_shared_desc_len = esp_nan_ndp_confirm_get_shared_desc_len,
/* CSIA / SCIA construction */
.construct_csia = esp_nan_construct_csia,
@@ -1440,6 +1749,71 @@ void esp_nan_app_init(void)
esp_nan_internal_register_secure_dp_funcs(&s_nan_secure_dp_funcs);
}
#ifdef CONFIG_ESP_WIFI_NAN_PAIRING
#if defined(CONFIG_ESP_WIFI_NAN_SECURITY)
static bool nan_peer_cred_npk_present(const wifi_nan_peer_creds_t *c)
{
static const uint8_t zero_npk[ESP_WIFI_NAN_NPK_LEN] = {0};
if (!c || !c->is_valid) {
return false;
}
return memcmp(c->npk, zero_npk, ESP_WIFI_NAN_NPK_LEN) != 0;
}
#endif
static bool nan_peer_nik_cached_cb(uint8_t *peer_mac)
{
bool cached = false;
struct peer_svc_info *peer;
if (!peer_mac) {
return false;
}
NAN_DATA_LOCK();
peer = nan_find_peer_svc(0, 0, peer_mac);
#if defined(CONFIG_ESP_WIFI_NAN_SECURITY)
if (peer) {
struct own_svc_info *own = nan_find_own_svc(peer->own_svc_id);
for (uint8_t i = 0; i < s_nan_ctx.num_peer_creds; i++) {
if (!s_nan_ctx.peer_creds[i].is_valid) {
continue;
}
if (!nan_peer_cred_npk_present(&s_nan_ctx.peer_creds[i])) {
continue;
}
if (own && memcmp(s_nan_ctx.peer_creds[i].service_hash, own->svc_hash, 6) == 0) {
cached = true;
break;
}
}
}
if (!cached) {
uint8_t npk_slots = 0;
for (uint8_t i = 0; i < s_nan_ctx.num_peer_creds; i++) {
if (s_nan_ctx.peer_creds[i].is_valid &&
nan_peer_cred_npk_present(&s_nan_ctx.peer_creds[i])) {
npk_slots++;
}
}
if (npk_slots == 1) {
cached = true;
}
}
#else
if (peer && peer->has_nik) {
cached = true;
}
#endif
NAN_DATA_UNLOCK();
return cached;
}
#endif
void esp_nan_action_start(esp_netif_t *nan_netif)
{
nan_set_app_default_handlers();
@@ -1462,12 +1836,24 @@ void esp_nan_action_start(esp_netif_t *nan_netif)
.get_nira_len = esp_nan_get_nira_len,
.construct_nira = esp_nan_construct_nira,
.verify_nira = esp_nan_verify_nira,
.peer_nik_cached = nan_peer_nik_cached_cb,
.receive_pasn = handle_auth_pasn,
#endif
};
esp_nan_internal_register_callbacks(&nan_cb);
#ifdef CONFIG_ESP_WIFI_NAN_SECURITY
/* s_nan_ctx.group_mgmt_prot (device-global IGTKSA/BIGTKSA, one per NMI) was
* captured from the user's start config in esp_wifi_nan_sync_start().
* Install the device-global IGTK/BIGTK for TX now (when enabled) so Beacons
* (BIGTK) and group-addressed SDFs (IGTK) are BIP-protected from the first
* frame, like iOS. The blob gates beacon BIP-TX on an active BIGTK index
* only (no NDP state), so installing here is sufficient. */
nan_security_install_own_group_integrity_keys();
#endif
ESP_LOGI(TAG, "NAN Discovery started.");
os_event_group_clear_bits(nan_event_group, NAN_STOPPED_BIT);
os_event_group_set_bits(nan_event_group, NAN_STARTED_BIT);
}
@@ -1493,7 +1879,15 @@ void esp_nan_action_stop(void)
nan_app_clear_paired_peers();
#endif
#ifdef CONFIG_ESP_WIFI_NAN_SECURITY
/* Drop the device-global IGTK/BIGTK so the next start regenerates fresh
* keys instead of re-installing a stale key with IPN/BIPN=0 (which would
* reset the blob's replay counter) — see nan_security_reset_own_group_keys. */
nan_security_reset_own_group_keys();
#endif
esp_nan_internal_register_callbacks(NULL);
os_event_group_clear_bits(nan_event_group, NAN_STARTED_BIT);
os_event_group_set_bits(nan_event_group, NAN_STOPPED_BIT);
}
@@ -1516,7 +1910,6 @@ esp_err_t esp_wifi_nan_sync_start(const wifi_nan_sync_config_t *nan_cfg)
return ret;
}
/* XXX: For now, NAN-USD and NAN-Sync can not coexist. */
/* NAN-Synchronization Only */
wifi_config_t config = {0};
@@ -1536,10 +1929,17 @@ esp_err_t esp_wifi_nan_sync_start(const wifi_nan_sync_config_t *nan_cfg)
s_nan_ctx.num_peer_creds = 0;
memset(s_nan_ctx.peer_creds, 0, sizeof(s_nan_ctx.peer_creds));
s_nan_ctx.use_nvs_for_caching = nan_cfg->use_nvs_for_caching;
s_nan_ctx.group_mgmt_prot = nan_cfg->group_mgmt_prot;
s_nan_ctx.nik_lifetime = 0;
if (nan_cfg->reset_current_nvs_creds) {
/* Start from a clean slate: drop every credential persisted in NVS. */
esp_wifi_nan_erase_all_creds();
ret = esp_wifi_nan_erase_all_creds();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to erase NAN credentials from NVS");
NAN_DATA_UNLOCK();
return ret;
}
} else if (esp_wifi_nan_load_saved_creds(s_nan_ctx.own_nik, &s_nan_ctx.own_nik_valid,
s_nan_ctx.peer_creds, &s_nan_ctx.num_peer_creds) != ESP_OK) {
ESP_LOGW(TAG, "Failed to load saved NAN credentials");
@@ -1557,7 +1957,12 @@ esp_err_t esp_wifi_nan_sync_start(const wifi_nan_sync_config_t *nan_cfg)
/* Persist the freshly generated NIK only when NVS caching is enabled;
* otherwise the identity stays ephemeral for this session. */
if (s_nan_ctx.use_nvs_for_caching) {
esp_wifi_nan_save_own_nik(s_nan_ctx.own_nik);
ret = esp_wifi_nan_save_own_nik(s_nan_ctx.own_nik);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to persist own NIK to NVS");
NAN_DATA_UNLOCK();
return ret;
}
}
}
/* Drop the cached NIRA tag; it was derived from the previous NIK. */
@@ -1570,6 +1975,7 @@ esp_err_t esp_wifi_nan_sync_start(const wifi_nan_sync_config_t *nan_cfg)
memcpy(&config.nan, nan_cfg, sizeof(wifi_nan_sync_config_t));
ESP_RETURN_ON_ERROR(esp_wifi_set_config(WIFI_IF_NAN, &config), TAG, "Setting NAN config failed");
os_event_group_clear_bits(nan_event_group, NAN_STARTED_BIT);
if (esp_wifi_start() != ESP_OK) {
ESP_LOGE(TAG, "Starting wifi failed");
NAN_DATA_LOCK();
@@ -1616,6 +2022,8 @@ esp_err_t esp_wifi_nan_sync_stop(void)
NAN_DATA_UNLOCK();
}
/* Wait for a fresh stop event, not a stale bit from prior run. */
os_event_group_clear_bits(nan_event_group, NAN_STOPPED_BIT);
ESP_RETURN_ON_ERROR(esp_wifi_stop(), TAG, "Stopping NAN failed");
EventBits_t bits = os_event_group_wait_bits(nan_event_group, NAN_STOPPED_BIT, pdFALSE, pdFALSE, portMAX_DELAY);
@@ -1650,7 +2058,6 @@ static bool nan_check_paired_service_hash(uint8_t service_hash[6])
uint8_t esp_wifi_nan_publish_service(const wifi_nan_publish_cfg_t *publish_cfg)
{
int pub_id = 0;
uint8_t service_id[6] = {0};
if (publish_cfg->usd_discovery_flag && !s_usd_in_progress) {
ESP_LOGE(TAG, "Can not start Publish function with USD Discovery "
@@ -1711,6 +2118,7 @@ uint8_t esp_wifi_nan_publish_service(const wifi_nan_publish_cfg_t *publish_cfg)
#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE
wifi_nan_publish_cfg_t *cfg = NULL;
uint8_t service_id[6] = {0};
NAN_DATA_LOCK();
if (!(s_nan_ctx.state & NAN_STARTED_BIT)) {
@@ -1788,7 +2196,7 @@ uint8_t esp_wifi_nan_publish_service(const wifi_nan_publish_cfg_t *publish_cfg)
}
#ifdef CONFIG_ESP_WIFI_NAN_PAIRING
if (nan_check_paired_service_hash(service_id)) {
if (cfg->pairing && nan_check_paired_service_hash(service_id)) {
cfg->pairing->pairing_setup = false;
}
#endif
@@ -1840,7 +2248,6 @@ fail:
uint8_t esp_wifi_nan_subscribe_service(const wifi_nan_subscribe_cfg_t *subscribe_cfg)
{
int sub_id = 0;
uint8_t service_id[6] = {0};
if (subscribe_cfg->usd_discovery_flag && !s_usd_in_progress) {
ESP_LOGE(TAG, "Can not start Subscribe function with USD Discovery "
@@ -1895,6 +2302,9 @@ uint8_t esp_wifi_nan_subscribe_service(const wifi_nan_subscribe_cfg_t *subscribe
#endif /* CONFIG_ESP_WIFI_NAN_USD_ENABLE */
#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE
wifi_nan_subscribe_cfg_t *cfg = NULL;
uint8_t service_id[6] = {0};
if (subscribe_cfg->security_reqd) {
#ifndef CONFIG_ESP_WIFI_NAN_SECURITY
ESP_LOGE(TAG, "Encrypted datapath not enabled (CONFIG_ESP_WIFI_NAN_SECURITY)");
@@ -1939,38 +2349,64 @@ uint8_t esp_wifi_nan_subscribe_service(const wifi_nan_subscribe_cfg_t *subscribe
goto fail;
}
cfg = os_zalloc(sizeof(*cfg));
if (!cfg) {
ESP_LOGE(TAG, "Failed to allocate subscribe config");
goto fail;
}
memcpy(cfg, subscribe_cfg, sizeof(*cfg));
cfg->pairing = NULL;
if (subscribe_cfg->pairing) {
cfg->pairing = os_malloc(sizeof(*cfg->pairing));
if (!cfg->pairing) {
ESP_LOGE(TAG, "Failed to copy pairing config");
goto fail;
}
memcpy(cfg->pairing, subscribe_cfg->pairing, sizeof(*cfg->pairing));
}
/* Pre-claim host slot BEFORE the blob's subscribe call; see comment on
* the publish path for the watchdog rationale. */
if (!nan_compute_service_id(subscribe_cfg->service_name, service_id)) {
ESP_LOGE(TAG, "Failed to compute Service ID for %s", subscribe_cfg->service_name);
if (!nan_compute_service_id(cfg->service_name, service_id)) {
ESP_LOGE(TAG, "Failed to compute Service ID for %s", cfg->service_name);
goto fail;
}
#ifdef CONFIG_ESP_WIFI_NAN_PAIRING
if (nan_check_paired_service_hash(service_id)) {
subscribe_cfg->pairing->pairing_setup = false;
if (cfg->pairing && nan_check_paired_service_hash(service_id)) {
cfg->pairing->pairing_setup = false;
}
#endif
if (!nan_claim_own_svc_slot(ESP_NAN_SUBSCRIBE, subscribe_cfg->service_name,
subscribe_cfg->security_cfg, subscribe_cfg->pairing)) {
if (!nan_claim_own_svc_slot(ESP_NAN_SUBSCRIBE, cfg->service_name,
cfg->security_cfg, cfg->pairing)) {
ESP_LOGE(TAG, "No free service slot");
goto fail;
}
if (esp_nan_internal_subscribe_service(subscribe_cfg, (uint8_t *) &sub_id, false) != ESP_OK) {
ESP_LOGE(TAG, "Failed to subscribe to service '%s'", subscribe_cfg->service_name);
nan_abort_own_svc(subscribe_cfg->service_name);
if (esp_nan_internal_subscribe_service(cfg, (uint8_t *) &sub_id, false) != ESP_OK) {
ESP_LOGE(TAG, "Failed to subscribe to service '%s'", cfg->service_name);
nan_abort_own_svc(cfg->service_name);
goto fail;
}
ESP_LOGI(TAG, "Started Subscribing to %s [Service ID - %u]", subscribe_cfg->service_name, sub_id);
nan_finalize_own_svc(subscribe_cfg->service_name, (uint8_t) sub_id, false, service_id);
ESP_LOGI(TAG, "Started Subscribing to %s [Service ID - %u]", cfg->service_name, sub_id);
nan_finalize_own_svc(cfg->service_name, (uint8_t) sub_id, false, service_id);
if (cfg->pairing) {
os_free(cfg->pairing);
}
os_free(cfg);
NAN_DATA_UNLOCK();
return sub_id;
fail:
if (cfg) {
if (cfg->pairing) {
os_free(cfg->pairing);
}
os_free(cfg);
}
NAN_DATA_UNLOCK();
return 0;
#endif /* CONFIG_ESP_WIFI_NAN_SYNC_ENABLE */
@@ -2071,6 +2507,12 @@ esp_err_t esp_wifi_nan_cancel_service(uint8_t service_id)
}
#endif /* CONFIG_ESP_WIFI_NAN_USD_ENABLE */
#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE
#if CONFIG_ESP_WIFI_NAN_PAIRING
/* Snapshot peer NMIs before cancel; clear TKs only after a successful cancel
* so a failed attempt does not leave an active service without keys. */
uint8_t peer_nmis[NAN_MAX_PEERS_RECORD][MACADDR_LEN];
int peer_count = 0;
#endif
NAN_DATA_LOCK();
struct own_svc_info *p_own_svc = nan_find_own_svc(service_id);
@@ -2079,6 +2521,27 @@ esp_err_t esp_wifi_nan_cancel_service(uint8_t service_id)
goto fail;
}
#if CONFIG_ESP_WIFI_NAN_PAIRING
{
struct peer_svc_info *temp;
SLIST_FOREACH(temp, &(p_own_svc->peer_list), next) {
bool dup = false;
for (int i = 0; i < peer_count; i++) {
if (MACADDR_EQUAL(peer_nmis[i], temp->peer_nmi)) {
dup = true;
break;
}
}
if (!dup && peer_count < NAN_MAX_PEERS_RECORD) {
MACADDR_COPY(peer_nmis[peer_count], temp->peer_nmi);
peer_count++;
}
}
}
#endif
if (p_own_svc->type == ESP_NAN_PUBLISH) {
if (esp_nan_internal_publish_service(NULL, &service_id, true) == ESP_OK) {
nan_reset_service(service_id, false);
@@ -2101,18 +2564,24 @@ fail:
done:
NAN_DATA_UNLOCK();
#if CONFIG_ESP_WIFI_NAN_PAIRING
/* Cancel succeeded; now safe to wipe pairwise keys for the collected peers. */
for (int i = 0; i < peer_count; i++) {
nan_app_clear_one_peer_tks(peer_nmis[i]);
}
#endif
return ESP_OK;
#endif /* CONFIG_ESP_WIFI_NAN_SYNC_ENABLE */
return ESP_FAIL;
}
#ifdef CONFIG_ESP_WIFI_NAN_SYNC_ENABLE
uint8_t esp_wifi_nan_datapath_req(wifi_nan_datapath_req_t *req)
{
uint8_t ndp_id = 0;
uint8_t own_bssid[6];
ip_addr_t own_ipv6 = {0};
NAN_DATA_LOCK();
struct peer_svc_info *p_peer_svc = nan_find_peer_svc(0, req->pub_id, req->peer_mac);
@@ -2230,7 +2699,6 @@ esp_err_t esp_wifi_nan_datapath_resp(wifi_nan_datapath_resp_t *resp)
ESP_LOGE(TAG, "Need NDP Indication before NDP Response can be sent");
goto fail;
}
if (MACADDR_EQUAL(resp->peer_mac, null_mac)) {
MACADDR_COPY(resp->peer_mac, ndl->peer_nmi);
}
@@ -2302,6 +2770,30 @@ esp_err_t esp_wifi_nan_datapath_end(wifi_nan_datapath_end_req_t *req)
return ESP_OK;
}
#ifdef CONFIG_ESP_WIFI_NAN_PAIRING
esp_err_t esp_nan_app_end_peer_datapaths(uint8_t publish_id)
{
wifi_nan_datapath_end_req_t ndp_end;
int i;
NAN_DATA_LOCK();
for (i = 0; i < ESP_WIFI_NAN_DATAPATH_MAX_PEERS; i++) {
struct ndl_info *ndl = &s_nan_ctx.ndl[i];
if (ndl->publisher_id == publish_id) {
ndp_end.ndp_id = ndl->ndp_id;
MACADDR_COPY(ndp_end.peer_mac, ndl->peer_nmi);
break;
}
}
NAN_DATA_UNLOCK();
if (i == ESP_WIFI_NAN_DATAPATH_MAX_PEERS)
return ESP_FAIL;
return esp_wifi_nan_datapath_end(&ndp_end);
}
#endif /* CONFIG_ESP_WIFI_NAN_PAIRING */
esp_err_t esp_wifi_nan_get_own_svc_info(uint8_t *own_svc_id, char *svc_name, int *num_peer_records)
{
struct own_svc_info *own_svc = NULL;
@@ -130,6 +130,40 @@ extern void *s_nan_data_lock;
#define NAN_KEY_INFO_ENC_KEY BIT(12)
#define NAN_KEY_INFO_KEY_TYPE BIT(3) /* 1=Pairwise, 0=Group */
/* NAN KDE OUIs and Data Types carried in the Key Data field (Wi-Fi Aware
* v4.0 §9.5.21.5 Table 126; formats per 802.11 Fig 12-36/12-42/12-47). */
#define NAN_KDE_OUI_RSN 0x000FACUL
#define NAN_KDE_OUI_WFA 0x506F9AUL
#define NAN_KDE_TYPE_GTK 1 /* 00-0F-AC GTK KDE */
#define NAN_KDE_TYPE_MAC 3 /* 00-0F-AC MAC address KDE */
#define NAN_KDE_TYPE_IGTK 9 /* 00-0F-AC IGTK KDE */
#define NAN_KDE_TYPE_BIGTK 14 /* 00-0F-AC BIGTK KDE */
#define NAN_KDE_TYPE_NIK 36 /* 50-6F-9A NIK KDE */
#define NAN_KDE_TYPE_KEY_LIFE 37 /* 50-6F-9A NAN Key Lifetime KDE */
/* KDE inner-prefix lengths (bytes before the actual key material). */
#define NAN_KDE_HDR_LEN 6 /* DD(1) + len(1) + OUI(3) + DataType(1) */
#define NAN_GTK_KDE_PREFIX_LEN 2 /* KeyID/Tx(1) + Reserved(1) */
#define NAN_IGTK_KDE_PREFIX_LEN 8 /* KeyID(2) + IPN(6) */
#define NAN_BIGTK_KDE_PREFIX_LEN 8 /* KeyID(2) + BIPN(6) */
/* CSIA Capabilities group-SA support (§9.5.21.2 Table 122, bits 1-2, bit 2 high
* order). Mirrors hostap nan_defs.h NAN_CS_INFO_CAPA_GTK_SUPP_*. */
#define NAN_CSIA_CAP_GTK_SUPP_POS 1
#define NAN_CSIA_CAP_GTK_SUPP_MASK 0x06
#define NAN_CSIA_CAP_GTK_SUPP_NONE 0 /* 00: no group SA */
#define NAN_CSIA_CAP_GTK_SUPP_IGTK 1 /* 01: GTKSA + IGTKSA */
#define NAN_CSIA_CAP_GTK_SUPP_ALL 2 /* 10: GTKSA + IGTKSA + BIGTKSA */
/* ND-GTK is the data-path group key (CCMP-128). */
#define NAN_ND_GTK_LEN 16
/* Host-side bound for the group Key Data scratch buffers (plaintext + AES-wrap),
* sized for GTK+IGTK+BIGTK: GTK 24B + IGTK 30B + BIGTK 30B + optional Key Lifetime
* KDE(s), padded to a multiple of 8, + 8B NIST AES Key Wrap overhead (~104B worst
* case). NOT the descriptor-len reservation — the getters now return the EXACT
* per-NDP length, so the blob allocates exactly what the builder writes. */
#define NAN_GROUP_KEY_DATA_MAX 128
/* NCS-SK-128 only for now (Table 21): KCK 128 bits, KEK 128 bits, TK 128 bits, MIC 16 bytes */
#define NAN_NCS_SK_128_KCK_LEN 16
#define NAN_NCS_SK_128_KEK_LEN 16
@@ -138,11 +172,16 @@ extern void *s_nan_data_lock;
#define NAN_NCS_SK_128_PTK_LEN (NAN_NCS_SK_128_KCK_LEN + NAN_NCS_SK_128_KEK_LEN + NAN_NCS_SK_128_TK_LEN)
/* Internal key-install constants matching esp_wifi_set_sta_key_internal semantics. */
#define NAN_WIFI_WPA_ALG_CCMP 3
#define NAN_WIFI_WPA_ALG_CCMP 3
#define NAN_WIFI_WPA_ALG_BIP_CMAC_128 7 /* IGTK/BIGTK BIP = blob WIFI_WPA_ALG_IGTK; 4 is SMS4 */
#define NAN_KEY_FLAG_RX BIT(2)
#define NAN_KEY_FLAG_TX BIT(3)
#define NAN_KEY_FLAG_PAIRWISE BIT(5)
/* NAN key-type selector (nan_key_type_t: NAN_KEY_ND_TK / ND_GTK / NM_TK / ND_IGTK /
* ND_BIGTK), passed as the last arg of esp_wifi_set_nan_key_internal, is defined in
* esp_wifi_driver.h (included above) and shared with the blob. */
/* Handshake state */
enum nan_handshake_state {
NAN_HANDSHAKE_IDLE = 0,
@@ -195,6 +234,13 @@ struct peer_svc_info {
* whose ND-PMKID matched the publisher SCIA. The initiator NDP-req path
* uses this to pick the right credential for M1's pair-PMKID. */
uint8_t matched_cred_idx;
/* Set at SDF match if the publisher advertised an NCS-GTK suite in its CSIA;
* the initiator NDP-req path uses it (with our own group_data_prot) to
* decide whether to distribute a GTK during NDP setup. */
uint8_t peer_group_data_cap: 1;
uint8_t peer_group_mgmt_cap: 1; /* peer advertised IGTKSA (CSIA caps bits 1-2 != 0) */
uint8_t peer_group_bigtk_cap: 1; /* peer advertised BIGTKSA (CSIA caps bits 1-2 == 10) */
uint8_t peer_sec_reserved: 5;
#endif
#if CONFIG_ESP_WIFI_NAN_PAIRING
/* Peer NIK / cipher version / lifetime extracted from a NAN Shared Key
@@ -231,6 +277,11 @@ struct own_svc_info {
#if CONFIG_ESP_WIFI_NAN_PAIRING
bool nik_fup_pending;
uint8_t nik_fup_pending_peer_nmi[MACADDR_LEN];
/* Set when the current PASN session for @c verify_session_peer_nmi is a
* pairing verification (re-pair). Consumed once in the key-installed
* callback to skip the NIK follow-up exchange. */
bool verify_session_pending;
uint8_t verify_session_peer_nmi[MACADDR_LEN];
#endif
uint8_t svc_hash[6];
};
@@ -268,20 +319,38 @@ struct ndl_info {
uint8_t handshake_state;
/* Group key state (unsupported -- pairwise-only M1-M4 flow today;
* fields kept to match the spec-defined RSNA key descriptor layout
* and stay forward-compatible). */
/* Received peer group keys (RX GTKSA). GTK protects group-addressed data
* frames the peer transmits; installed against the peer NDI. IGTK/BIGTK
* protect group-addressed management/Beacon frames (NMI plane). */
uint8_t gtk[NAN_GTK_MAX_LEN];
uint8_t igtk[NAN_GTK_MAX_LEN];
uint8_t bigtk[NAN_GTK_MAX_LEN];
uint8_t gtk_len;
uint8_t igtk_len;
uint8_t bigtk_len;
uint8_t gtk_keyid; /* peer GTK Key ID (1 or 2) */
uint8_t igtk_keyid; /* peer IGTK Key ID (4 or 5) */
uint8_t bigtk_keyid; /* peer BIGTK Key ID (6 or 7) */
uint8_t gtk_rsc[NAN_KEY_RSC_LEN]; /* peer GTK RSC from Key RSC field */
uint8_t igtk_ipn[6]; /* peer IGTK IPN (seeds BIP RX replay counter) */
uint8_t bigtk_ipn[6]; /* peer BIGTK BIPN (seeds BIP RX replay counter) */
uint8_t gtk_set: 1;
uint8_t igtk_set: 1;
uint8_t bigtk_set: 1;
uint8_t group_keys_reserved: 5;
/* Own group key (TX GTKSA) distributed to the peer in M3 (initiator) or
* M4 (responder); installed against the local NDI as the TX group key. */
uint8_t own_gtk[NAN_ND_GTK_LEN];
uint8_t own_gtk_len;
uint8_t own_gtk_keyid; /* own GTK Key ID (1 or 2) */
uint8_t own_gtk_rsc[NAN_KEY_RSC_LEN];
uint8_t own_gtk_set: 1;
uint8_t gtk_required: 1; /* GTKSA negotiated for this NDP */
uint8_t igtk_required: 1; /* IGTKSA negotiated for this NDP */
uint8_t bigtk_required: 1; /* BIGTKSA negotiated for this NDP */
uint8_t own_group_reserved: 4;
uint8_t key_rsc[NAN_KEY_RSC_LEN];
#endif
};
@@ -299,6 +368,28 @@ typedef struct {
#ifdef CONFIG_ESP_WIFI_NAN_SECURITY
uint8_t own_nik[ESP_WIFI_NAN_NIK_LEN];
bool own_nik_valid;
/* Device-global IGTKSA/BIGTKSA (one per NMI, §7.1.3.3/§7.1.3.4). Generated
* once via os_get_random and reused across all secured NDPs; copied into
* each M3/M4 and installed against the local NMI. BIP-CMAC-128 (16-byte).
* On ESP the NMI and NDI are the same MAC today. */
uint8_t own_igtk[NAN_ND_GTK_LEN];
uint8_t own_igtk_ipn[6];
uint8_t own_igtk_keyid; /* 4 or 5 */
bool own_igtk_set;
uint8_t own_bigtk[NAN_ND_GTK_LEN];
uint8_t own_bigtk_bipn[6];
uint8_t own_bigtk_keyid; /* 6 or 7 */
bool own_bigtk_set;
/* Device-global "support + use group management protection (IGTKSA/BIGTKSA)".
* One setting per NMI, not per service: Beacons are NMI-level and there is
* exactly one IGTKSA/BIGTKSA per NMI (§7.1.3.3/§7.1.3.4). Sourced from
* wifi_nan_sync_config_t.group_mgmt_prot at NAN start. When set it: forces
* GTKSA on every secured service (the CSIA caps field cannot encode IGTK/
* BIGTK without GTKSA, §9.5.21.2 Table 122), generates own IGTK+BIGTK,
* advertises caps 0x04, and BIP-protects Beacons + multicast SDFs.
* Advertising never blocks a non-supporting peer — keys and protection are
* set up only after capability negotiation (§7.1.3.5). */
bool group_mgmt_prot;
uint8_t cached_nira_nonce[8];
uint8_t cached_nira_tag[8];
bool nira_cached;
@@ -308,6 +399,7 @@ typedef struct {
wifi_nan_peer_creds_t peer_creds[ESP_WIFI_NAN_MAX_PEER_CREDS];
uint8_t num_peer_creds;
bool use_nvs_for_caching;
uint32_t nik_lifetime;
#endif
#ifdef CONFIG_ESP_WIFI_PASN_SUPPORT
struct nan_pasn_data *nan_pasn_data;
@@ -334,7 +426,8 @@ bool nan_compute_service_id(const char *service_name, uint8_t service_id[6]);
uint32_t esp_nan_get_csia_len(uint16_t own_csid_bitmap, uint16_t peer_csid_bitmap);
uint32_t esp_nan_get_scia_len(uint8_t num_pmkids);
uint32_t esp_nan_get_shared_key_desc_attr_len(uint16_t key_data_len);
int esp_nan_ndp_security_install_get_shared_desc_len(void);
int esp_nan_ndp_security_install_get_shared_desc_len(uint8_t ndp_id, const uint8_t *peer_nmi);
int esp_nan_ndp_confirm_get_shared_desc_len(uint8_t ndp_id, const uint8_t *peer_nmi);
uint8_t esp_nan_get_ndp_resp_num_pmkids(uint8_t ndp_id, const uint8_t *peer_nmi);
uint32_t esp_nan_get_ndp_resp_shared_key_desc_len(uint8_t ndp_id, const uint8_t *peer_nmi);
@@ -422,6 +515,17 @@ esp_err_t nan_security_populate_initiator_ndl(struct ndl_info *ndl,
const struct peer_svc_info *peer_svc,
const uint8_t *peer_nmi);
/* Generate (once) and TX-install the device-global IGTK/BIGTK so Beacons and
* group-addressed SDFs are BIP-protected from NAN start (§7.1.3.3/§7.1.3.4).
* Call once at NAN start; never per-NDP (would reset the blob's BIPN counter). */
void nan_security_install_own_group_integrity_keys(void);
/* Clear the device-global IGTK/BIGTK state on NAN stop so the next NAN start
* regenerates fresh keys. Prevents re-installing a stale key with IPN/BIPN=0
* (which resets the blob's monotonic replay counter) and key reuse if the NMI
* is re-randomized on restart. */
void nan_security_reset_own_group_keys(void);
/*
* Match subscriber discovery security to a publisher's params.
* NCS-SK: Compare locally derived ND-PMKID (subscriber passphrase, publisher NMI) to
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+9
View File
@@ -116,6 +116,15 @@ menu "LWIP"
This option is used to disable the Network Discovery Protocol (NDP) if it is not required.
Please use this option with caution, as the NDP is essential for IPv6 functionality within a local network.
config LWIP_ND6_SUPPORT_STATIC_ENTRIES
bool "Enable API for static IPv6 neighbor cache entries"
default n
depends on LWIP_ND6
help
Enable APIs to add/remove permanent IPv6->MAC neighbor cache entries
that bypass Neighbor Discovery (no NS/NA). IPv6 counterpart of
ETHARP_SUPPORT_STATIC_ENTRIES.
config LWIP_FORCE_ROUTER_FORWARDING
bool "LWIP Force Router Forwarding Enable/Disable"
default n
+13
View File
@@ -1242,6 +1242,19 @@ static inline uint32_t timeout_from_offered(uint32_t lease, uint32_t min)
#define LWIP_ND6 0
#endif
/**
* LWIP_ND6_SUPPORT_STATIC_ENTRIES==1: enable nd6_add_static_neighbor() and
* nd6_remove_static_neighbor() to manage permanent IPv6->MAC neighbor cache
* entries that bypass Neighbor Discovery (no NS/NA). Applied per-(netif,address)
* and does not affect NDP on other interfaces. IPv6 counterpart of
* ETHARP_SUPPORT_STATIC_ENTRIES.
*/
#ifdef CONFIG_LWIP_ND6_SUPPORT_STATIC_ENTRIES
#define LWIP_ND6_SUPPORT_STATIC_ENTRIES 1
#else
#define LWIP_ND6_SUPPORT_STATIC_ENTRIES 0
#endif
/**
* LWIP_FORCE_ROUTER_FORWARDING==1: the router flag in NA packet will always set to 1,
* otherwise, never set router flag for NA packets.
@@ -61,7 +61,10 @@ enum nan_role {
* Pairing Initiator NMI || Pairing Responder NMI)).
*
* @param peer_nmi Peer NMI (6 bytes).
* @param role enum nan_role value for the local device.
* @param role @c NAN_ROLE_PAIRING_INITIATOR or @c NAN_ROLE_PAIRING_RESPONDER.
* @param pairing_verification 1 when PASN ran as re-pair verification (NIRA verified on
* Auth1 for responder, or @c cfg->pairing_verification on
* initiator); 0 for bootstrap auth pairing.
* @param ndp_csid NCS-SK CSID for paired-peer NDP (WIFI_NAN_CSID_NCS_SK_128
* or _SK_256), 0 if no usable cipher mapping was available.
* @param nd_pmk ND-PMK bytes (32) or NULL if KDK was absent.
@@ -73,6 +76,7 @@ enum nan_role {
*/
typedef void (*esp_nan_pairing_key_installed_cb_t)(const uint8_t *peer_nmi,
uint8_t role,
uint8_t pairing_verification,
uint8_t ndp_csid,
const uint8_t *nd_pmk,
size_t nd_pmk_len,
@@ -133,6 +137,23 @@ int esp_nan_supp_pasn_initiator_auth(const uint8_t *peer_nmi, uint32_t pincode,
uint32_t nik_lifetime_sec,
esp_nan_pairing_key_installed_cb_t pairing_key_installed_cb);
/**
* @brief Schedule NAN PASN initiator verification (re-pair with cached NIK/PMK).
*
* Same role as @ref esp_nan_supp_pasn_initiator_auth but uses PASN verify instead of auth.
*
* @param peer_nmi Peer NMI (6 bytes).
* @param pairing_key_installed_cb Callback invoked after pairwise key installation with peer NMI.
* @return 0 on success, -1 on failure.
*/
int esp_nan_supp_pasn_initiator_verify(const uint8_t *peer_nmi,
esp_nan_pairing_key_installed_cb_t pairing_key_installed_cb);
/**
* @brief Default pairing key-installed callback (NAN app layer).
*/
esp_nan_pairing_key_installed_cb_t esp_nan_pairing_get_key_installed_cb(void);
/**
* Schedule @ref handle_auth_pasn from NAN app callback table.
*/
@@ -141,6 +162,20 @@ void handle_auth_pasn(uint8_t *buf, size_t len, uint16_t trans_seq, uint16_t sta
const struct nan_pasn_key_material *nan_pasn_get_saved_keys(void);
void nan_pasn_clear_saved_keys(void);
/** Look up cached NPK for pairing verification (§7.6.5). Returns 0 on success.
* Lookup matches peer_cred.service_hash against active own-service hashes,
* or uses a single cached slot when only one NPK is present. */
int nan_global_peer_npk_lookup(uint8_t *npk, size_t *npk_len, int *akmp);
/**
* Mark/clear that the current PASN session for @a peer_nmi is pairing
* verification (re-pair) and must not trigger NIK follow-up exchange. The
* marker is anchored to own service @a own_inst_id (as resolved from the
* verifying NIK), so it survives even when no peer_svc_info exists yet.
*/
void esp_nan_pairing_mark_verify_session(uint8_t own_inst_id, const uint8_t *peer_nmi);
void esp_nan_pairing_clear_verify_session(const uint8_t *peer_nmi);
/**
* Decrypt a NAN Shared Key Descriptor attribute received in a follow-up frame.
*
@@ -9,6 +9,7 @@
#if CONFIG_ESP_WIFI_NAN_PAIRING
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include "esp_private/esp_supp_nan.h"
@@ -17,6 +18,8 @@ struct wpabuf;
struct pasn_data;
struct rsn_pmksa_cache;
#define NAN_PASN_PMKID_LEN 16
typedef esp_nan_pairing_key_installed_cb_t nan_pasn_pairing_key_installed_cb_t;
struct nan_config {
@@ -43,20 +46,20 @@ struct nan_pasn_data {
size_t pasn_ptk_len;
struct pasn_data *pasn;
nan_pasn_pairing_key_installed_cb_t pairing_key_installed_cb;
uint8_t pairing_verification;
uint32_t nik_lifetime_sec;
bool pasn_auth2_done; /**< Initiator: Auth2 already processed (dedup) */
uint8_t peer_npkid[NAN_PASN_PMKID_LEN]; /**< Nonce||Tag from peer NIRA; for PMKID cross-check */
bool peer_npkid_valid;
};
int nan_initiate_pasn_verify(struct nan_pasn_data *pd, const uint8_t *peer_addr,
int freq, int role, const uint8_t *bssid,
int role, const uint8_t *bssid,
const uint8_t *ssid, size_t ssid_len);
int nan_initiate_pasn_auth(struct nan_pasn_data *pd, const uint8_t *addr, int freq);
int nan_initiate_pasn_auth(struct nan_pasn_data *pd, const uint8_t *addr);
struct nan_pasn_data *nan_pasn_data_init(void);
void nan_pasn_data_deinit(struct nan_pasn_data *pd);
int nan_pasn_auth_initiate(struct nan_pasn_data *pd, const uint8_t *peer_addr, int freq);
int nan_pasn_auth(struct nan_pasn_data **pd_out, const uint8_t *peer_addr, int freq);
int nan_pasn_verify_eloop(unsigned int secs, unsigned int usecs,
const uint8_t *peer_addr, int freq, int role,
const uint8_t *bssid,
const uint8_t *ssid, size_t ssid_len);
int nan_pasn_auth_initiate(struct nan_pasn_data *pd, const uint8_t *peer_addr);
int nan_pasn_auth(struct nan_pasn_data **pd_out, const uint8_t *peer_addr);
#endif /* CONFIG_ESP_WIFI_NAN_PAIRING */
@@ -16,6 +16,7 @@
#include "utils/includes.h"
#include "utils/common.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "common/nan.h"
#include "esp_wifi_driver.h"
#include "crypto/crypto.h"
@@ -49,12 +50,74 @@
static struct nan_pasn_key_material g_nan_pasn_saved_keys;
/* Key index for esp_wifi_set_nan_key_internal (NAN PASN pairwise TK). */
int temp = 1;
#define NAN_NIRA_NONCE_LEN 8
#define NAN_NIRA_TAG_LEN 8
#define NAN_NIRA_ATTR_LEN 20
#define NAN_PASN_CSIA_ATTR_MAX_LEN (3 + 1 + 2 * 8)
#define NAN_PASN_PAIRING_BOOTSTRAP_METHODS WIFI_NAN_BOOTSTRAP_PIN_CODE_DISPLAY
#define NAN_PASN_PAIRING_CSIA_PUB_ID 5
static int nan_pasn_npkid_from_nira_attr(const u8 *nira, u16 nira_len, u8 *npkid)
{
if (!nira || !npkid ||
nira_len < 4 + NAN_NIRA_NONCE_LEN + NAN_NIRA_TAG_LEN) {
return -1;
}
/* NPKID = Nonce || Tag (wire order matches NIRA body after Cipher Version;
* PMKID is carried little-endian in the RSNE). */
os_memcpy(npkid, nira + 4, NAN_NIRA_NONCE_LEN);
os_memcpy(npkid + NAN_NIRA_NONCE_LEN,
nira + 4 + NAN_NIRA_NONCE_LEN, NAN_NIRA_TAG_LEN);
return 0;
}
static int nan_pasn_build_local_npkid(u8 *npkid)
{
uint8_t nira_frm[NAN_NIRA_ATTR_LEN];
if (!npkid ||
esp_nan_construct_nira(nira_frm) <
(int)(4 + NAN_NIRA_NONCE_LEN + NAN_NIRA_TAG_LEN)) {
return -1;
}
return nan_pasn_npkid_from_nira_attr(nira_frm, NAN_NIRA_ATTR_LEN, npkid);
}
static int nan_validate_custom_pmkid(void *ctx, const u8 *addr, const u8 *pmkid)
{
struct nan_pasn_data *nan = ctx;
if (!pmkid) {
return -1;
}
/*
* Pairing verification: NIK is already checked via NIRA and NPK via
* PMKSA lookup, but §7.6.5 also binds RSNE PMKID to the same-frame NIRA
* (NPKID = Nonce||Tag). Cross-check when we cached NPKID from that NIRA.
*/
if (!nan || !nan->peer_npkid_valid) {
return 0;
}
if (os_memcmp(pmkid, nan->peer_npkid, PMKID_LEN) != 0) {
wpa_printf(MSG_INFO,
"NAN PASN verify: PMKID/NPKID mismatch for " MACSTR,
MAC2STR(addr));
return -1;
}
return 0;
}
#define NAN_PASN_AES_WRAP_OVERHEAD 8
#define NAN_PASN_AES_WRAP_MIN_CIPHERTEXT (NAN_PASN_AES_WRAP_OVERHEAD + 8)
static int pasn_responder_init(const uint8_t *peer_addr, uint32_t pincode,
esp_nan_pairing_key_installed_cb_t pairing_key_installed_cb);
/**
* Map PASN pairwise cipher to the NCS-SK CSID used for paired-peer NDPs
* (Wi-Fi Aware v4.0 §7.6.4.2: paired NDP runs NCS-SK style M1-M4, key length
@@ -239,7 +302,6 @@ static int nan_pasn_install_nan_pairwise_tk(struct nan_pasn_data *nan, struct pa
struct wpa_ptk *ptk;
uint8_t key_rsc[8] = {0};
int kret;
(void)nan;
if (!pasn) {
return -1;
@@ -258,7 +320,7 @@ static int nan_pasn_install_nan_pairwise_tk(struct nan_pasn_data *nan, struct pa
wpa_hexdump_key(MSG_DEBUG, "NAN PASN: NM-TK", ptk->tk, ptk->tk_len);
kret = esp_wifi_set_nan_key_internal(
NAN_PASN_WIFI_ALG_CCMP, pasn->peer_addr, temp, 1, key_rsc, sizeof(key_rsc),
NAN_PASN_WIFI_ALG_CCMP, pasn->peer_addr, 1, 1, key_rsc, sizeof(key_rsc),
ptk->tk, ptk->tk_len,
NAN_KEY_NM_TK);
if (kret != 0) {
@@ -428,6 +490,21 @@ void nan_pasn_clear_saved_keys(void)
forced_memzero(&g_nan_pasn_saved_keys, sizeof(g_nan_pasn_saved_keys));
}
static void nan_pasn_clear_peer_tks_for_verify_start(const u8 *peer_nmi)
{
if (!peer_nmi) {
return;
}
esp_nan_app_clear_peer_tks(peer_nmi, 0);
nan_pasn_clear_saved_keys();
}
static void nan_pasn_responder_verify_prepare(const u8 *peer_nmi, uint8_t publish_id)
{
esp_nan_app_end_peer_datapaths(publish_id);
nan_pasn_clear_peer_tks_for_verify_start(peer_nmi);
}
/* NAN KDE OUI Type values from Wi-Fi Aware spec v4.0, Table 126. */
#define NAN_PASN_KDE_OUI_TYPE_NIK 36
#define NAN_PASN_KDE_OUI_TYPE_LIFETIME 37
@@ -749,28 +826,6 @@ out:
return ret;
}
static int nan_chan_to_freq_mhz(uint8_t chan)
{
if (chan >= 1 && chan <= 13) {
return 2407 + (int)chan * 5;
}
if (chan == 14) {
return 2484;
}
return 0;
}
static int nan_pasn_get_current_freq_mhz(void)
{
uint8_t primary = 0;
wifi_second_chan_t second = WIFI_SECOND_CHAN_NONE;
if (esp_wifi_get_channel(&primary, &second) != ESP_OK || primary == 0) {
return 0;
}
return nan_chan_to_freq_mhz(primary);
}
static void nan_pasn_auth_timeout_cancel(struct nan_pasn_data *nan);
static void nan_pasn_auth_timeout_cb(void *eloop_ctx, void *user_data)
@@ -846,48 +901,97 @@ static int nan_set_dev_sae_pin(struct nan_pasn_data *nan, const char *digits)
}
/*
* Build Wi-Fi Alliance NAN vendor IE (EID 221) with DCEA / BPBA / CSIA for PASN
* and pass to @a pasn via pasn_set_extra_ies() so wpa_pasn_add_extra_ies() appends
* Build Wi-Fi Alliance NAN vendor IE (EID 221) for PASN.
* Bootstrap auth: DCEA + NPBA + CSIA.
* Pairing verification: CSIA + NIRA only (no DCEA/NPBA).
* Passed to @a pasn via pasn_set_extra_ies() so wpa_pasn_add_extra_ies() appends
* it after PASN Parameters on Auth 1/3 (and after prepare_data_element on Auth 2).
*/
static int nan_prepare_pasn_extra_ie(struct nan_pasn_data *nan, struct pasn_data *pasn,
const struct wpabuf *frame, bool add_dira)
const struct wpabuf *frame, bool include_nira)
{
static const u8 nan_pasn_attr_payload[] = {
NAN_ATTR_DCEA, 0x02, 0x00, 0x00, 0x03,
NAN_ATTR_BPBA, 0x05, 0x00, 0x90, 0x02, 0x00, 0x02, 0x00,
NAN_ATTR_CSIA, 0x03, 0x00, 0x00, 0x07, 0x05,
};
u8 fixed[2 + 3 + 1 + sizeof(nan_pasn_attr_payload)];
u8 csia_attr[NAN_PASN_CSIA_ATTR_MAX_LEN];
u8 nira_attr[NAN_NIRA_ATTR_LEN];
uint8_t *pairing_attrs = NULL;
u8 *buf = NULL;
u8 *pos;
size_t fr_len = 0;
size_t csia_len;
size_t nira_len = 0;
size_t attr_payload_len;
size_t vendor_ie_len;
size_t total_len;
uint32_t npba_len = 0;
uint32_t dcea_len = 0;
uint32_t pairing_attrs_len = 0;
int ret;
(void)nan;
(void)add_dira;
fixed[0] = WLAN_EID_VENDOR_SPECIFIC;
fixed[1] = 3 + 1 + sizeof(nan_pasn_attr_payload);
WPA_PUT_BE24(&fixed[2], OUI_WFA);
fixed[5] = NAN_OUI_TYPE;
os_memcpy(&fixed[6], nan_pasn_attr_payload, sizeof(nan_pasn_attr_payload));
ret = esp_nan_construct_csia(csia_attr, NAN_PASN_PAIRING_CSIA_PUB_ID,
WIFI_NAN_CSID_BIT_NCS_PK_PASN_128, 0);
if (ret <= 0 || ret > (int)sizeof(csia_attr)) {
wpa_printf(MSG_INFO, "NAN PASN: CSIA build failed");
return -1;
}
csia_len = (size_t) ret;
attr_payload_len = csia_len;
if (include_nira) {
ret = esp_nan_construct_nira(nira_attr);
if (ret < (int)NAN_NIRA_ATTR_LEN) {
wpa_printf(MSG_INFO, "NAN PASN: NIRA build failed");
return -1;
}
nira_len = (size_t) ret;
attr_payload_len += nira_len;
} else {
pairing_attrs = esp_wifi_nan_get_pairing_attrs(NAN_PASN_PAIRING_BOOTSTRAP_METHODS,
true, true, &npba_len,
&dcea_len, &pairing_attrs_len);
if (!pairing_attrs || !pairing_attrs_len) {
wpa_printf(MSG_INFO, "NAN PASN: pairing attrs build failed");
return -1;
}
attr_payload_len += pairing_attrs_len;
}
if (frame) {
fr_len = wpabuf_len(frame);
}
if (!fr_len) {
return pasn_set_extra_ies(pasn, fixed, sizeof(fixed));
vendor_ie_len = 3 + 1 + attr_payload_len;
if (vendor_ie_len > UINT8_MAX) {
wpa_printf(MSG_INFO, "NAN PASN: extra IE too long");
return -1;
}
total_len = sizeof(fixed) + fr_len;
total_len = 2 + vendor_ie_len + fr_len;
buf = os_malloc(total_len);
if (!buf) {
return -1;
}
os_memcpy(buf, fixed, sizeof(fixed));
os_memcpy(buf + sizeof(fixed), wpabuf_head_u8(frame), fr_len);
pos = buf;
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
*pos++ = (u8) vendor_ie_len;
WPA_PUT_BE24(pos, OUI_WFA);
pos += 3;
*pos++ = NAN_OUI_TYPE;
if (pairing_attrs_len) {
os_memcpy(pos, pairing_attrs, pairing_attrs_len);
pos += pairing_attrs_len;
}
os_memcpy(pos, csia_attr, csia_len);
pos += csia_len;
if (nira_len) {
os_memcpy(pos, nira_attr, nira_len);
pos += nira_len;
}
if (fr_len) {
os_memcpy(pos, wpabuf_head_u8(frame), fr_len);
}
ret = pasn_set_extra_ies(pasn, buf, total_len);
os_free(buf);
return ret;
@@ -962,7 +1066,7 @@ static void nan_pairing_apply_sae_pin(struct pasn_data *pasn, u8 pasn_type,
pasn->password = nan->dev_sae_pin;
}
void nan_pasn_initialize(struct nan_pasn_data *nan, const u8 *addr, int freq, bool verify, bool derive_kek)
void nan_pasn_initialize(struct nan_pasn_data *nan, const u8 *addr)
{
struct pasn_data *pasn;
struct wpabuf *rsnxe;
@@ -971,6 +1075,8 @@ void nan_pasn_initialize(struct nan_pasn_data *nan, const u8 *addr, int freq, bo
return;
}
nan->peer_npkid_valid = false;
if (nan->pasn) {
wpa_pasn_reset(nan->pasn);
} else {
@@ -1021,15 +1127,13 @@ void nan_pasn_initialize(struct nan_pasn_data *nan, const u8 *addr, int freq, bo
* ND-PMK is filled by pasn_nd_pmk_derive_from_kdk_store (hostap
* nan_crypto_derive_nd_pmk_from_kdk). Matches hostap nan_pairing.c.
*/
(void)derive_kek;
pasn->derive_kek = false;
pasn->kek_len = 0;
/* Wi-Fi Aware pairing: PASN Auth frames always use SAE as the base AKM. */
pasn->akmp = WPA_KEY_MGMT_SAE;
if (nan->dev_sae_pin_len > 0) {
pasn->akmp = WPA_KEY_MGMT_SAE;
nan_pairing_apply_sae_pin(pasn, nan->cfg->pasn_type, nan);
} else if (!verify) {
pasn->akmp = WPA_KEY_MGMT_PASN;
}
pasn->rsn_pairwise = pasn->cipher;
@@ -1059,16 +1163,19 @@ void nan_pasn_initialize(struct nan_pasn_data *nan, const u8 *addr, int freq, bo
pasn->parse_data_element = nan->cfg->parse_data_element;
pasn->validate_custom_pmkid = nan->cfg->pasn_validate_pmkid;
pasn->freq = freq;
}
int nan_initiate_pasn_verify(struct nan_pasn_data *pd, const u8 *peer_addr,
int freq, int role,
int role,
const u8 *bssid, const u8 *ssid, size_t ssid_len)
{
struct nan_pasn_data *nan;
struct pasn_data *pasn;
uint8_t npk[NAN_PASN_KEY_PMK_MAX];
uint8_t own_addr[ETH_ALEN];
u8 npkid[PMKID_LEN];
size_t npk_len = 0;
int akmp = WPA_KEY_MGMT_SAE;
int ret = 0;
(void)role;
@@ -1087,29 +1194,55 @@ int nan_initiate_pasn_verify(struct nan_pasn_data *pd, const u8 *peer_addr,
}
nan->dev_role = NAN_ROLE_PAIRING_INITIATOR;
nan_pasn_initialize(nan, peer_addr, freq, true, true);
nan->pasn_auth2_done = false;
nan->pairing_verification = 1;
nan_pasn_clear_peer_tks_for_verify_start(peer_addr);
nan_pasn_initialize(nan, peer_addr);
pasn = nan->pasn;
if (nan_prepare_pasn_extra_ie(nan, pasn, NULL, false) != 0) {
wpa_printf(MSG_INFO, "NAN PASN: extra IE failed");
if (!pasn) {
return -1;
}
nan->cfg->pasn_validate_pmkid = nan_validate_custom_pmkid;
pasn->validate_custom_pmkid = nan_validate_custom_pmkid;
/* §7.6.5: seed PMKSA with cached NPK and local NPKID (Nonce||Tag). */
if (nan_global_peer_npk_lookup(npk, &npk_len, &akmp) != 0 ||
esp_wifi_get_mac(WIFI_IF_NAN, own_addr) != ESP_OK ||
nan_pasn_build_local_npkid(npkid) != 0) {
ret = -1;
goto out;
}
if (pasn_initiator_pmksa_cache_add(nan->initiator_pmksa, own_addr,
(u8 *) peer_addr, npk, npk_len, npkid) != 0) {
ret = -1;
goto out;
}
pasn_set_akmp(pasn, akmp);
pasn->wpa_key_mgmt = akmp;
pasn_set_custom_pmkid(pasn, npkid);
if (nan_prepare_pasn_extra_ie(nan, pasn, NULL, true) != 0) {
ret = -1;
goto out;
}
if (wpa_pasn_verify(pasn, pasn->own_addr, pasn->peer_addr, pasn->bssid,
pasn->akmp, pasn->cipher, pasn->group, pasn->freq,
pasn->akmp, pasn->cipher, pasn->group, 0,
NULL, 0, NULL, 0, NULL)) {
wpa_printf(MSG_INFO, "PASN verify failed");
ret = -1;
}
if (pasn->extra_ies) {
os_free((u8 *) pasn->extra_ies);
pasn->extra_ies = NULL;
pasn->extra_ies_len = 0;
}
out:
forced_memzero(npk, sizeof(npk));
return ret;
}
int nan_initiate_pasn_auth(struct nan_pasn_data *pd, const u8 *addr, int freq)
int nan_initiate_pasn_auth(struct nan_pasn_data *pd, const u8 *addr)
{
struct nan_pasn_data *nan;
struct pasn_data *pasn;
@@ -1126,10 +1259,16 @@ int nan_initiate_pasn_auth(struct nan_pasn_data *pd, const u8 *addr, int freq)
}
nan->dev_role = NAN_ROLE_PAIRING_INITIATOR;
nan->pasn_auth2_done = false;
nan->pairing_verification = 0;
nan_pasn_initialize(nan, addr, freq, false, true);
nan_pasn_initialize(nan, addr);
pasn = nan->pasn;
if (!pasn) {
return -1;
}
pasn_initiator_pmksa_cache_remove(pasn->pmksa, (u8 *)addr);
if (nan_prepare_pasn_extra_ie(nan, pasn, NULL, false) != 0) {
@@ -1138,7 +1277,7 @@ int nan_initiate_pasn_auth(struct nan_pasn_data *pd, const u8 *addr, int freq)
}
if (wpas_pasn_start(pasn, pasn->own_addr, pasn->peer_addr, pasn->bssid,
pasn->akmp, pasn->cipher, pasn->group, pasn->freq,
pasn->akmp, pasn->cipher, pasn->group, 0,
NULL, 0, NULL, 0, NULL)) {
wpa_printf(MSG_INFO, "Failed to start PASN");
ret = -1;
@@ -1167,15 +1306,13 @@ int * int_array_dup(const int *a)
}
static int nan_handle_pasn_auth(struct nan_pasn_data *nan,
const struct ieee80211_auth *mgmt, size_t len,
int freq)
const struct ieee80211_auth *mgmt, size_t len)
{
struct pasn_data *pasn;
u8 pasn_type;
int pasn_groups[4] = { 0 };
u16 auth_alg, auth_transaction, status_code;
(void)freq;
if (!nan || !nan->pasn) {
return -1;
}
@@ -1246,6 +1383,7 @@ static int nan_handle_pasn_auth(struct nan_pasn_data *nan,
nan_pasn_store_ptk(nan, &pasn->ptk);
#endif /* CONFIG_TESTING_OPTIONS */
nan_pasn_copy_keys_from_pasn(nan, pasn);
/* Install NM-TK only after Auth3 is successfully validated. */
if (nan_pasn_install_nan_pairwise_tk(nan, pasn) == 0 &&
nan->pairing_key_installed_cb) {
const uint8_t *nd_pmk = pasn_nd_pmk_global.valid ?
@@ -1253,6 +1391,7 @@ static int nan_handle_pasn_auth(struct nan_pasn_data *nan,
size_t nd_pmk_len = pasn_nd_pmk_global.valid ? PMK_LEN : 0;
nan->pairing_key_installed_cb(pasn->peer_addr,
(uint8_t)nan->dev_role,
nan->pairing_verification,
nan_pasn_pasn_cipher_to_ndp_csid(pasn->cipher),
nd_pmk, nd_pmk_len,
nan->nik_lifetime_sec);
@@ -1263,8 +1402,108 @@ static int nan_handle_pasn_auth(struct nan_pasn_data *nan,
return 0;
}
/**
* Find a NIRA attribute inside a WFA NAN vendor-specific IE (OUI + type 0x13).
* Returns a pointer to the attribute (ID + length + body) or NULL.
*/
static const u8 *nan_pasn_find_nira_attr(const u8 *ies, size_t ies_len, u16 *attr_len)
{
const u8 *ie = ies;
const u8 *end = ies + ies_len;
while (ie + 2 <= end) {
u8 id = ie[0];
u8 elen = ie[1];
if (ie + 2 + elen > end) {
break;
}
if (id == WLAN_EID_VENDOR_SPECIFIC && elen >= 4 &&
WPA_GET_BE24(ie + 2) == OUI_WFA && ie[5] == NAN_OUI_TYPE) {
const u8 *pos = ie + 6;
const u8 *attrs_end = ie + 2 + elen;
while (attrs_end - pos >= NAN_ATTR_HDR_LEN) {
u8 attr_id = pos[0];
u16 attr_body_len = WPA_GET_LE16(pos + 1);
u16 total_attr_len;
if (attr_body_len > (size_t)(attrs_end - pos - NAN_ATTR_HDR_LEN)) {
break;
}
total_attr_len = NAN_ATTR_HDR_LEN + attr_body_len;
if (attr_id == NAN_ATTR_NIRA) {
if (attr_len) {
*attr_len = total_attr_len;
}
return pos;
}
pos += total_attr_len;
}
}
ie += 2 + elen;
}
return NULL;
}
/* Find NIRA in @a mgmt once, verify identity, and stash NPKID for PMKID
* cross-check. When @a own_inst_id is non-NULL the matched own service id is
* also returned (0 if none). */
static bool nan_pasn_auth_process_nira(struct nan_pasn_data *nan,
const u8 *peer_addr,
const struct ieee80211_auth *mgmt,
size_t len, u16 auth_trans,
uint8_t *own_inst_id)
{
const u8 *var;
size_t var_len;
const u8 *nira;
u16 nira_len = 0;
if (own_inst_id) {
*own_inst_id = 0;
}
if (!nan || !peer_addr || !mgmt) {
return false;
}
nan->peer_npkid_valid = false;
if (len < offsetof(struct ieee80211_auth, auth.variable)) {
return false;
}
var = mgmt->auth.variable;
var_len = len - offsetof(struct ieee80211_auth, auth.variable);
nira = nan_pasn_find_nira_attr(var, var_len, &nira_len);
if (!nira) {
wpa_printf(MSG_INFO, "NAN PASN verify: NIRA missing in Auth%u from "
MACSTR, auth_trans, MAC2STR(peer_addr));
return false;
}
if (!esp_nan_verify_nira_get_own_svc((u8 *) peer_addr, (u8 *) nira, nira_len,
own_inst_id)) {
wpa_printf(MSG_INFO, "NAN PASN verify: NIRA verification failed in "
"Auth%u for " MACSTR, auth_trans, MAC2STR(peer_addr));
return false;
}
if (nan_pasn_npkid_from_nira_attr(nira, nira_len, nan->peer_npkid) == 0) {
nan->peer_npkid_valid = true;
}
wpa_printf(MSG_DEBUG, "NAN PASN verify: NIRA OK in Auth%u for " MACSTR,
auth_trans, MAC2STR(peer_addr));
return true;
}
int nan_pasn_auth_rx(struct nan_pasn_data *nan, const struct ieee80211_auth *mgmt,
size_t len, int freq)
size_t len)
{
int ret = 0;
u16 auth_transaction;
@@ -1293,6 +1532,23 @@ int nan_pasn_auth_rx(struct nan_pasn_data *nan, const struct ieee80211_auth *mgm
if (nan->dev_role == NAN_ROLE_PAIRING_INITIATOR &&
auth_transaction == WLAN_AUTH_TR_SEQ_PASN_AUTH2) {
/*
* Duplicate Auth2: responder may retransmit if it didn't get Auth3
* ACK. wpa_pasn_auth_rx() would reject with trans_seq mismatch and
* we would also lose NM-TK that was already installed; silently
* accept the retransmit instead.
*/
if (nan->pasn_auth2_done) {
return 0;
}
if (pasn->custom_pmkid_valid &&
!nan_pasn_auth_process_nira(nan, mgmt->sa, mgmt, len, 2, NULL)) {
wpa_printf(MSG_INFO, "PASN: Auth2 NIRA verify failed");
nan->dev_role = NAN_ROLE_IDLE;
return -1;
}
ret = wpa_pasn_auth_rx(pasn, (const u8 *) mgmt, len, &pasn_data);
if (ret < 0) {
wpa_printf(MSG_INFO, "PASN: wpa_pasn_auth_rx() failed");
@@ -1304,13 +1560,15 @@ int nan_pasn_auth_rx(struct nan_pasn_data *nan, const struct ieee80211_auth *mgm
* successfully built/transmitted by wpa_pasn_auth_rx().
*/
nan_pasn_copy_keys_from_pasn(nan, pasn);
if (nan_pasn_install_nan_pairwise_tk(nan, pasn) == 0 &&
nan->pairing_key_installed_cb) {
nan->pasn_auth2_done = true;
int tk_ret = nan_pasn_install_nan_pairwise_tk(nan, pasn);
if (tk_ret == 0 && nan->pairing_key_installed_cb) {
const uint8_t *nd_pmk = pasn_nd_pmk_global.valid ?
pasn_nd_pmk_global.nd_pmk : NULL;
size_t nd_pmk_len = pasn_nd_pmk_global.valid ? PMK_LEN : 0;
nan->pairing_key_installed_cb(pasn->peer_addr,
(uint8_t)nan->dev_role,
nan->pairing_verification,
nan_pasn_pasn_cipher_to_ndp_csid(pasn->cipher),
nd_pmk, nd_pmk_len,
nan->nik_lifetime_sec);
@@ -1321,7 +1579,7 @@ int nan_pasn_auth_rx(struct nan_pasn_data *nan, const struct ieee80211_auth *mgm
#endif /* CONFIG_TESTING_OPTIONS */
forced_memzero(pasn_get_ptk(pasn), sizeof(pasn->ptk));
} else {
ret = nan_handle_pasn_auth(nan, mgmt, len, freq);
ret = nan_handle_pasn_auth(nan, mgmt, len);
}
return ret;
}
@@ -1330,7 +1588,8 @@ void handle_auth_pasn(uint8_t *buf, size_t len, uint16_t trans_seq, uint16_t sta
{
const struct ieee80211_auth *mgmt;
struct nan_pasn_data *nan;
int rx_freq;
bool auth1_verify = false;
u8 auth1_pmkid[PMKID_LEN];
(void)trans_seq;
(void)status;
@@ -1340,15 +1599,114 @@ void handle_auth_pasn(uint8_t *buf, size_t len, uint16_t trans_seq, uint16_t sta
}
mgmt = (const struct ieee80211_auth *)buf;
nan = esp_nan_app_get_pasn_data();
rx_freq = nan_pasn_get_current_freq_mhz();
if (rx_freq <= 0) {
rx_freq = 2412;
if (!nan) {
/*
* Only lazy-init on Auth1: a stray Auth2/Auth3 (e.g. retransmit after
* we already completed PASN and tore down the session) must be
* dropped, otherwise we would init a fresh context and try to process
* a mid-handshake frame against it, which fails noisily.
*/
if (le_to_host16(mgmt->auth.auth_transaction) !=
WLAN_AUTH_TR_SEQ_PASN_AUTH1) {
return;
}
if (pasn_responder_init(mgmt->sa, UINT32_MAX,
esp_nan_pairing_get_key_installed_cb()) != 0) {
return;
}
nan = esp_nan_app_get_pasn_data();
}
if (!nan) {
wpa_printf(MSG_DEBUG, "NAN PASN: receive_pasn: no context");
return;
}
nan_pasn_auth_rx(nan, mgmt, len, rx_freq);
if (le_to_host16(mgmt->auth.auth_transaction) == 1) {
struct ieee802_11_elems elems;
struct wpa_ie_data rsn_data;
struct wpa_pasn_params_data pasn_params;
if (ieee802_11_parse_elems(mgmt->auth.variable,
len - offsetof(struct ieee80211_auth, auth.variable),
&elems, 0) != ParseFailed &&
elems.rsn_ie && elems.pasn_params &&
wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
&rsn_data) == 0 &&
rsn_data.num_pmkid &&
wpa_pasn_parse_parameter_ie(elems.pasn_params - 3,
elems.pasn_params_len + 3,
false, &pasn_params) == 0 &&
pasn_params.wrapped_data_format == WPA_PASN_WRAPPED_DATA_NO) {
auth1_verify = true;
os_memcpy(auth1_pmkid, rsn_data.pmkid, PMKID_LEN);
}
}
if (auth1_verify) {
uint8_t npk[NAN_PASN_KEY_PMK_MAX];
uint8_t own_addr[ETH_ALEN];
size_t npk_len = 0;
int akmp = WPA_KEY_MGMT_SAE;
struct pasn_data *pasn = nan->pasn;
bool auth1_ok = false;
uint8_t verify_own_inst_id = 0;
if (!nan_pasn_auth_process_nira(nan, mgmt->sa, mgmt, len, 1,
&verify_own_inst_id)) {
goto auth1_verify_done;
}
nan_pasn_responder_verify_prepare(mgmt->sa, verify_own_inst_id);
if (!pasn) {
wpa_printf(MSG_INFO, "NAN PASN verify: no PASN context for "
MACSTR, MAC2STR(mgmt->sa));
goto auth1_verify_done;
}
if (nan_global_peer_npk_lookup(npk, &npk_len, &akmp) != 0) {
wpa_printf(MSG_INFO, "NAN PASN verify: no cached NPK for "
MACSTR, MAC2STR(mgmt->sa));
goto auth1_verify_done;
}
if (esp_wifi_get_mac(WIFI_IF_NAN, own_addr) != ESP_OK) {
wpa_printf(MSG_INFO, "NAN PASN verify: failed to read NAN NMI");
goto auth1_verify_done;
}
if (pasn_responder_pmksa_cache_add(nan->responder_pmksa, own_addr,
(u8 *) mgmt->sa, npk, npk_len,
auth1_pmkid) != 0) {
wpa_printf(MSG_INFO, "NAN PASN verify: PMKSA cache add failed for "
MACSTR, MAC2STR(mgmt->sa));
goto auth1_verify_done;
}
{
u8 npkid[PMKID_LEN];
nan->cfg->pasn_validate_pmkid = nan_validate_custom_pmkid;
pasn->validate_custom_pmkid = nan_validate_custom_pmkid;
pasn_set_akmp(pasn, akmp);
pasn->wpa_key_mgmt = akmp;
if (nan_pasn_build_local_npkid(npkid) == 0) {
pasn_set_custom_pmkid(pasn, npkid);
}
if (nan_prepare_pasn_extra_ie(nan, pasn, NULL, true) != 0) {
wpa_printf(MSG_INFO, "NAN PASN verify: extra IE (NIRA) build "
"failed for " MACSTR, MAC2STR(mgmt->sa));
goto auth1_verify_done;
}
auth1_ok = true;
nan->pairing_verification = 1;
}
auth1_verify_done:
forced_memzero(npk, sizeof(npk));
if (!auth1_ok) {
return;
}
esp_nan_pairing_mark_verify_session(verify_own_inst_id, mgmt->sa);
}
nan_pasn_auth_rx(nan, mgmt, len);
}
void nan_pasn_pmksa_set_pmk(struct nan_pasn_data *nan, const u8 *src, const u8 *dst,
@@ -1539,16 +1897,14 @@ void nan_pasn_data_deinit(struct nan_pasn_data *pd)
os_free(pd);
}
int nan_pasn_auth_initiate(struct nan_pasn_data *pd, const uint8_t *peer_addr, int freq)
int nan_pasn_auth_initiate(struct nan_pasn_data *pd, const uint8_t *peer_addr)
{
if (!pd || !peer_addr) {
return -1;
}
return nan_initiate_pasn_auth(pd, peer_addr, freq);
return nan_initiate_pasn_auth(pd, peer_addr);
}
#define NAN_PASN_VERIFY_ELOOP_SSID_MAX 32
struct nan_pasn_eloop_ctx {
uint8_t peer_addr[ETH_ALEN];
uint32_t pincode;
@@ -1556,10 +1912,37 @@ struct nan_pasn_eloop_ctx {
esp_nan_pairing_key_installed_cb_t pairing_key_installed_cb;
};
static struct nan_pasn_data *nan_pasn_eloop_prepare(struct nan_pasn_eloop_ctx *ctx,
const char *pin_to_apply)
{
struct nan_pasn_data *old;
struct nan_pasn_data *pd;
old = esp_nan_app_get_pasn_data();
if (old) {
nan_pasn_data_deinit(old);
}
pd = nan_pasn_data_init();
if (!pd) {
return NULL;
}
esp_nan_app_set_pasn_data(pd);
pd->pairing_key_installed_cb = ctx->pairing_key_installed_cb;
pd->nik_lifetime_sec = ctx->nik_lifetime_sec;
if (pin_to_apply && nan_set_dev_sae_pin(pd, pin_to_apply) != 0) {
nan_pasn_data_deinit(pd);
return NULL;
}
return pd;
}
static void nan_pasn_auth_eloop_cb(void *eloop_ctx, void *user_data)
{
struct nan_pasn_eloop_ctx *ctx = user_data;
struct nan_pasn_data *old;
struct nan_pasn_data *pd;
char pin_digits[16];
int n;
@@ -1570,46 +1953,23 @@ static void nan_pasn_auth_eloop_cb(void *eloop_ctx, void *user_data)
return;
}
old = esp_nan_app_get_pasn_data();
if (old) {
nan_pasn_data_deinit(old);
}
pd = nan_pasn_data_init();
if (!pd) {
os_free(ctx);
return;
}
esp_nan_app_set_pasn_data(pd);
pd->pairing_key_installed_cb = ctx->pairing_key_installed_cb;
pd->nik_lifetime_sec = ctx->nik_lifetime_sec;
if (ctx->pincode != UINT32_MAX) {
n = os_snprintf(pin_digits, sizeof(pin_digits), "%06u",
(unsigned)ctx->pincode);
if (os_snprintf_error(sizeof(pin_digits), n)) {
nan_pasn_data_deinit(pd);
os_free(ctx);
return;
}
pin_to_apply = pin_digits;
}
if (pin_to_apply && nan_set_dev_sae_pin(pd, pin_to_apply) != 0) {
nan_pasn_data_deinit(pd);
pd = nan_pasn_eloop_prepare(ctx, pin_to_apply);
if (!pd) {
os_free(ctx);
return;
}
{
int freq = nan_pasn_get_current_freq_mhz();
if (freq <= 0) {
freq = 2412;
}
nan_pasn_auth_initiate(pd, ctx->peer_addr, freq);
}
nan_pasn_auth_initiate(pd, ctx->peer_addr);
os_free(ctx);
}
@@ -1619,8 +1979,12 @@ int esp_nan_supp_pasn_initiator_auth(const uint8_t *peer_nmi, uint32_t pincode,
{
struct nan_pasn_eloop_ctx *ctx;
if (!peer_nmi) {
return -1;
}
ctx = os_zalloc(sizeof(*ctx));
if (!ctx || !peer_nmi) {
if (!ctx) {
return -1;
}
@@ -1637,49 +2001,32 @@ int esp_nan_supp_pasn_initiator_auth(const uint8_t *peer_nmi, uint32_t pincode,
return 0;
}
struct nan_pasn_verify_eloop_ctx {
uint8_t peer_addr[ETH_ALEN];
int freq;
int role;
uint8_t bssid[ETH_ALEN];
uint8_t ssid[NAN_PASN_VERIFY_ELOOP_SSID_MAX];
size_t ssid_len;
};
static void nan_pasn_verify_eloop_cb(void *eloop_ctx, void *user_data)
static void nan_pasn_verify_init_eloop_cb(void *eloop_ctx, void *user_data)
{
struct nan_pasn_verify_eloop_ctx *ctx = user_data;
struct nan_pasn_eloop_ctx *ctx = user_data;
struct nan_pasn_data *pd;
const uint8_t *ssid_arg;
(void)eloop_ctx;
if (!ctx) {
return;
}
pd = esp_nan_app_get_pasn_data();
pd = nan_pasn_eloop_prepare(ctx, NULL);
if (!pd) {
os_free(ctx);
return;
}
ssid_arg = ctx->ssid_len ? ctx->ssid : NULL;
nan_initiate_pasn_verify(pd, ctx->peer_addr, ctx->freq, ctx->role,
ctx->bssid, ssid_arg, ctx->ssid_len);
nan_initiate_pasn_verify(pd, ctx->peer_addr, 0, NULL, NULL, 0);
os_free(ctx);
}
int nan_pasn_verify_eloop(unsigned int secs, unsigned int usecs,
const uint8_t *peer_addr, int freq, int role,
const uint8_t *bssid,
const uint8_t *ssid, size_t ssid_len)
int esp_nan_supp_pasn_initiator_verify(const uint8_t *peer_nmi,
esp_nan_pairing_key_installed_cb_t pairing_key_installed_cb)
{
struct nan_pasn_verify_eloop_ctx *ctx;
struct nan_pasn_eloop_ctx *ctx;
if (!peer_addr) {
return -1;
}
if (ssid_len > NAN_PASN_VERIFY_ELOOP_SSID_MAX) {
if (!peer_nmi) {
return -1;
}
@@ -1688,20 +2035,10 @@ int nan_pasn_verify_eloop(unsigned int secs, unsigned int usecs,
return -1;
}
os_memcpy(ctx->peer_addr, peer_addr, ETH_ALEN);
ctx->freq = freq;
ctx->role = role;
if (bssid) {
os_memcpy(ctx->bssid, bssid, ETH_ALEN);
} else {
os_memcpy(ctx->bssid, peer_addr, ETH_ALEN);
}
if (ssid && ssid_len) {
os_memcpy(ctx->ssid, ssid, ssid_len);
ctx->ssid_len = ssid_len;
}
os_memcpy(ctx->peer_addr, peer_nmi, ETH_ALEN);
ctx->pairing_key_installed_cb = pairing_key_installed_cb;
if (eloop_register_timeout(secs, usecs, nan_pasn_verify_eloop_cb, NULL, ctx) != 0) {
if (eloop_register_timeout(0, 0, nan_pasn_verify_init_eloop_cb, NULL, ctx) != 0) {
os_free(ctx);
return -1;
}
@@ -1717,9 +2054,11 @@ int nan_pasn_verify_eloop(unsigned int secs, unsigned int usecs,
*
* @param peer_addr Peer NAN address, or NULL to use a broadcast placeholder until Auth1.
* @param pincode 6-digit value 0..999999, or @c UINT32_MAX to keep default PIN from @ref nan_pasn_data_init.
* @param pairing_key_installed_cb Callback stored on the responder PASN context.
* Returns 0 on success, -1 on failure.
*/
int pasn_responder_init(const uint8_t *peer_addr, uint32_t pincode)
int pasn_responder_init(const uint8_t *peer_addr, uint32_t pincode,
esp_nan_pairing_key_installed_cb_t pairing_key_installed_cb)
{
struct nan_pasn_data *pd;
struct nan_pasn_data *old;
@@ -1728,11 +2067,20 @@ int pasn_responder_init(const uint8_t *peer_addr, uint32_t pincode)
const u8 *peer = peer_addr ? peer_addr : bcast;
const char *pin_to_apply = NULL;
int n;
int freq;
os_memset(pin_digits, 0, sizeof(pin_digits));
old = esp_nan_app_get_pasn_data();
/* Lazy Auth1 init and scheduled bootstrap init can both call this; do not
* tear down an in-progress responder session for the same peer. */
if (old && old->dev_role == NAN_ROLE_PAIRING_RESPONDER && old->pasn &&
!is_broadcast_ether_addr(peer) &&
os_memcmp(old->pasn->peer_addr, peer, ETH_ALEN) == 0) {
if (pairing_key_installed_cb) {
old->pairing_key_installed_cb = pairing_key_installed_cb;
}
return 0;
}
if (old) {
nan_pasn_data_deinit(old);
}
@@ -1743,6 +2091,7 @@ int pasn_responder_init(const uint8_t *peer_addr, uint32_t pincode)
}
esp_nan_app_set_pasn_data(pd);
pd->pairing_key_installed_cb = pairing_key_installed_cb;
if (pincode != UINT32_MAX) {
n = os_snprintf(pin_digits, sizeof(pin_digits), "%06u",
@@ -1757,13 +2106,8 @@ int pasn_responder_init(const uint8_t *peer_addr, uint32_t pincode)
goto fail;
}
freq = nan_pasn_get_current_freq_mhz();
if (freq <= 0) {
freq = 2412;
}
pd->dev_role = NAN_ROLE_PAIRING_RESPONDER;
nan_pasn_initialize(pd, peer, freq, false, true);
nan_pasn_initialize(pd, peer);
if (!pd->pasn || nan_prepare_pasn_extra_ie(pd, pd->pasn, NULL, false) != 0) {
goto fail;
@@ -1786,16 +2130,15 @@ struct pasn_responder_eloop_ctx {
static void pasn_responder_init_eloop_cb(void *eloop_ctx, void *user_data)
{
struct pasn_responder_eloop_ctx *ctx = user_data;
struct nan_pasn_data *pd;
(void)eloop_ctx;
if (!ctx) {
return;
}
if (pasn_responder_init(ctx->peer_addr, ctx->pincode) == 0) {
pd = esp_nan_app_get_pasn_data();
if (pasn_responder_init(ctx->peer_addr, ctx->pincode,
ctx->pairing_key_installed_cb) == 0) {
struct nan_pasn_data *pd = esp_nan_app_get_pasn_data();
if (pd) {
pd->pairing_key_installed_cb = ctx->pairing_key_installed_cb;
pd->nik_lifetime_sec = ctx->nik_lifetime_sec;
}
}
@@ -238,6 +238,8 @@ typedef enum {
NAN_KEY_ND_TK = 0,
NAN_KEY_ND_GTK,
NAN_KEY_NM_TK,
NAN_KEY_ND_IGTK, /* 3 - NAN Integrity Group Temporal Key (BIP-CMAC-128) */
NAN_KEY_ND_BIGTK, /* 4 - NAN Beacon Integrity Group Temporal Key (BIP-CMAC-128) */
} nan_key_type_t;
typedef struct {
@@ -341,7 +343,10 @@ void esp_wifi_ap_set_group_mgmt_cipher_internal(wifi_cipher_type_t cipher);
uint8_t esp_wifi_op_class_supported_internal(uint8_t op_class, uint8_t min_chan, uint8_t max_chan, uint8_t inc, uint8_t bw, channel_bitmap_t *non_pref_channels);
bool esp_wifi_is_wpa3_compatible_mode_enabled(uint8_t if_index);
uint8_t esp_wifi_ap_get_owe_config_internal(void);
esp_err_t esp_nan_complete_pairing(uint8_t svc_id, uint8_t peer_svc_id);
esp_err_t esp_nan_set_pairing_status(uint8_t svc_id, uint8_t peer_svc_id, uint8_t peer_nmi[6], bool pairing_complete);
uint8_t *esp_wifi_nan_get_pairing_attrs(uint16_t bootstrap_methods, bool pairing_enabled,
bool nik_cache_enabled, uint32_t *npba_len,
uint32_t *dcea_len, uint32_t *total_len);
esp_err_t esp_wifi_nan_load_saved_creds(uint8_t own_nik[ESP_WIFI_NAN_NIK_LEN], bool *own_nik_valid,
wifi_nan_peer_creds_t peer_creds[ESP_WIFI_NAN_MAX_PEER_CREDS], uint8_t *num_peer_creds);
esp_err_t esp_wifi_nan_save_own_nik(const uint8_t own_nik[ESP_WIFI_NAN_NIK_LEN]);
@@ -10,11 +10,12 @@
#define IEEE802_11_H
enum wpa_validate_result;
int auth_sae_queued_addr(struct hostapd_data *hapd, const u8 *addr);
#ifdef CONFIG_SAE
int auth_sae_queue(struct hostapd_data *hapd, u8 *buf, size_t len, u8 *bssid, u16 status, u32 auth_transaction);
int handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
u8 *buf, size_t len, u8 *bssid,
u16 auth_transaction, u16 status);
#endif /* CONFIG_SAE */
u16 wpa_res_to_status_code(enum wpa_validate_result res);
#ifdef CONFIG_OWE_SOFTAP
uint16_t owe_process_assoc_req(struct hostapd_data *hapd, struct sta_info *sta, const u8 *owe_dh,
@@ -52,7 +52,8 @@ void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
unsigned int hash;
pmksa->pmksa_count--;
pmksa->free_cb(entry, pmksa->ctx);
if (pmksa->free_cb)
pmksa->free_cb(entry, pmksa->ctx);
/* unlink from hash list */
hash = PMKID_HASH(entry->pmkid);
+1 -1
View File
@@ -48,7 +48,7 @@ enum nan_attr_id {
NAN_ATTR_NDP_EXT = 0x29,
NAN_ATTR_DCEA = 0x2A, /* Device Capability Extension attribute */
NAN_ATTR_NIRA = 0x2B, /* NAN Identity Resolution attribute */
NAN_ATTR_BPBA = 0x2C, /* NAN Pairing Bootstrapping attribute */
NAN_ATTR_NPBA = 0x2C, /* NAN Pairing Bootstrapping attribute */
NAN_ATTR_S3 = 0x2D,
NAN_ATTR_TPEA = 0x2E, /* Transmit Power Envelope attribute */
NAN_ATTR_VENDOR_SPECIFIC = 0xDD,
@@ -624,6 +624,9 @@ static struct wpabuf * wpas_pasn_build_auth_1(struct pasn_data *pasn,
#else /* CONFIG_IEEE80211R */
goto fail;
#endif /* CONFIG_IEEE80211R */
} else if (verify && pasn->custom_pmkid_valid) {
/* Wi-Fi Aware pairing verification: NPKID in RSNE, no wrapped data */
pmkid = pasn->custom_pmkid;
} else if (wrapped_data != WPA_PASN_WRAPPED_DATA_NO) {
struct rsn_pmksa_cache_entry *pmksa;
@@ -46,7 +46,8 @@ static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
enum pmksa_free_reason reason)
{
pmksa->pmksa_count--;
pmksa->free_cb(entry, pmksa->ctx, reason);
if (pmksa->free_cb)
pmksa->free_cb(entry, pmksa->ctx, reason);
_pmksa_cache_free_entry(entry);
}
+10 -15
View File
@@ -249,7 +249,7 @@ int wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len,
MAC2STR(dest));
}
#else
return ret;
goto out;
#endif
}
if (key_mic &&
@@ -263,8 +263,13 @@ int wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len,
wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", kck, kck_len);
wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC", key_mic, wpa_mic_len(sm->key_mgmt, sm->pmk_len));
wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key", msg, msg_len);
return wpa_sm_ether_send(sm, dest, proto, msg, msg_len);
ret = wpa_sm_ether_send(sm, dest, proto, msg, msg_len);
out:
#ifdef ESP_SUPPLICANT
wpa_sm_free_eapol(msg);
#else
os_free(msg);
#endif
return ret;
}
@@ -338,7 +343,6 @@ static void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise)
error, pairwise, sm->ptk_set, (unsigned long) rlen);
wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, wpa_sm_get_auth_addr(sm),
ETH_P_EAPOL, rbuf, rlen, key_mic);
wpa_sm_free_eapol(rbuf);
}
static void wpa_sm_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry,
@@ -680,11 +684,8 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
wpa_printf(MSG_DEBUG, "WPA Send EAPOL-Key 2/4");
wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, ETH_P_EAPOL,
return wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, ETH_P_EAPOL,
rbuf, rlen, key_mic);
wpa_sm_free_eapol(rbuf);
return 0;
}
static int wpa_derive_ptk(struct wpa_sm *sm, const unsigned char *src_addr,
@@ -1353,11 +1354,8 @@ static int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *ds
WPA_PUT_BE16(reply->key_data_length, 0);
wpa_printf(MSG_DEBUG, "WPA Send EAPOL-Key 4/4");
wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, ETH_P_EAPOL,
return wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, ETH_P_EAPOL,
rbuf, rlen, key_mic);
wpa_sm_free_eapol(rbuf);
return 0;
}
static void wpa_sm_set_seq(struct wpa_sm *sm, struct wpa_eapol_key *key, u8 isptk)
@@ -1869,11 +1867,8 @@ static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm,
wpa_printf(MSG_DEBUG, "WPA Send 2/2 Group key");
wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, sm->bssid, ETH_P_EAPOL,
return wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, sm->bssid, ETH_P_EAPOL,
rbuf, rlen, key_mic);
wpa_sm_free_eapol(rbuf);
return 0;
}
static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm,
@@ -35,6 +35,16 @@ menu "Example Configuration"
the same credential (passphrase or PMK).
Disable to advertise an open (unencrypted) service.
config EXAMPLE_NAN_GROUP_DATA_PROT
bool "Protect group-addressed datapath traffic (ND-GTK)"
depends on EXAMPLE_NAN_SECURITY_ENABLED
default y
help
Negotiate and install an ND-GTK so group-addressed (multicast/
broadcast) frames on the NAN datapath are encrypted. This is
required for IPv6 over the secured datapath (Neighbor Discovery,
MLD). Both peers must enable this for group keys to be exchanged.
choice EXAMPLE_NAN_SECURITY_METHOD
prompt "Security Method"
depends on EXAMPLE_NAN_SECURITY_ENABLED
@@ -133,6 +133,9 @@ void wifi_nan_publish(void)
};
#ifdef CONFIG_EXAMPLE_NAN_SECURITY_ENABLED
wifi_nan_discovery_security_params_t security_cfg = {
#ifdef CONFIG_EXAMPLE_NAN_GROUP_DATA_PROT
.group_data_prot = 1, /* distribute/accept ND-GTK for group-addressed data */
#endif
.num_credentials = 1,
.creds = {
{
@@ -45,6 +45,16 @@ menu "Example Configuration"
(passphrase or PMK) and sets up an encrypted NDP.
Disable to discover open (unencrypted) services.
config EXAMPLE_NAN_GROUP_DATA_PROT
bool "Protect group-addressed datapath traffic (ND-GTK)"
depends on EXAMPLE_NAN_SECURITY_ENABLED
default y
help
Negotiate and install an ND-GTK so group-addressed (multicast/
broadcast) frames on the NAN datapath are encrypted. This is
required for IPv6 over the secured datapath (Neighbor Discovery,
MLD). Both peers must enable this for group keys to be exchanged.
choice EXAMPLE_NAN_SECURITY_METHOD
prompt "Security Method"
depends on EXAMPLE_NAN_SECURITY_ENABLED
@@ -227,6 +227,9 @@ void wifi_nan_subscribe(void)
};
#ifdef CONFIG_EXAMPLE_NAN_SECURITY_ENABLED
wifi_nan_discovery_security_params_t security_cfg = {
#ifdef CONFIG_EXAMPLE_NAN_GROUP_DATA_PROT
.group_data_prot = 1, /* distribute/accept ND-GTK for group-addressed data */
#endif
.num_credentials = 1,
.creds = {
{