[RFC v2 97/99] wpa_supplicant: Configure NAN data station

Andrei Otcheretianski andrei.otcheretianski at intel.com
Tue Dec 23 03:52:41 PST 2025


Add/remove NDI station upon NDP connected/disconnected notification and
install keys in case of secure connection.

Signed-off-by: Andrei Otcheretianski <andrei.otcheretianski at intel.com>
---
 wpa_supplicant/nan_supplicant.c | 196 ++++++++++++++++++++++++++++++++
 1 file changed, 196 insertions(+)

diff --git a/wpa_supplicant/nan_supplicant.c b/wpa_supplicant/nan_supplicant.c
index 0cf3faf0fa..d101f31dc2 100644
--- a/wpa_supplicant/nan_supplicant.c
+++ b/wpa_supplicant/nan_supplicant.c
@@ -346,12 +346,207 @@ static void wpas_nan_ndp_action_notif_cb(void *ctx,
 }
 
 
+static int wpas_nan_set_ndi_keys(struct wpa_supplicant *wpa_s,
+				  const u8 *ndi_addr,
+				  enum nan_cipher_suite_id csid,
+				  const u8 *tk, size_t tk_len)
+{
+	enum wpa_alg alg;
+	u8 rsc[6];
+
+	os_memset(rsc, 0, sizeof(rsc));
+	switch (csid) {
+	case NAN_CS_SK_CCM_128:
+		alg = WPA_ALG_CCMP;
+		break;
+	case NAN_CS_SK_GCM_256:
+		alg = WPA_ALG_CCMP_256;
+		break;
+	default:
+		wpa_printf(MSG_ERROR, "NAN: Unsupported CSID %d for NDI keys",
+			   csid);
+		return -1;
+	}
+
+	return wpa_drv_set_key(wpa_s, -1, alg, ndi_addr, 0, 1,
+			       rsc, sizeof(rsc), tk, tk_len,
+			       KEY_FLAG_PAIRWISE);
+}
+
+
+static int wpas_nan_remove_ndi_keys(struct wpa_supplicant *wpa_s,
+				    const u8 *ndi_addr)
+{
+	return wpa_drv_set_key(wpa_s, -1, WPA_ALG_NONE, ndi_addr, 0, 0,
+			       NULL, 0, NULL, 0,
+			       KEY_FLAG_PAIRWISE);
+}
+
+
+static struct wpa_supplicant *
+wpas_nan_get_ndi_iface(struct wpa_supplicant *wpa_s, const u8 *ndi_addr)
+{
+	struct wpa_supplicant *ndi_wpa_s;
+
+	for (ndi_wpa_s = wpa_s->global->ifaces; ndi_wpa_s;
+	     ndi_wpa_s = ndi_wpa_s->next) {
+		if (!ndi_wpa_s->nan_data ||
+		    os_memcmp(ndi_wpa_s->own_addr, ndi_addr, ETH_ALEN))
+			continue;
+
+		return ndi_wpa_s;
+	}
+
+	return NULL;
+}
+
+
+static int wpas_nan_configure_nmi_sta_capa(struct wpa_supplicant *wpa_s,
+					  const u8 *nmi_addr)
+{
+	struct hostapd_sta_add_params sta_params;
+	const u8 *ie;
+	u8 *elems;
+	int elems_len;
+
+	elems_len = nan_get_peer_elems(wpa_s->nan, nmi_addr, &elems);
+	if (elems_len < 0) {
+		wpa_printf(MSG_ERROR,
+			   "NAN: Failed to get peer elems for NMI station");
+		return -1;
+	}
+
+	os_memset(&sta_params, 0, sizeof(sta_params));
+	sta_params.addr = nmi_addr;
+	sta_params.set = 1;
+	sta_params.flags = WPA_STA_AUTHORIZED;
+	sta_params.flags_mask = sta_params.flags;
+
+	ie = get_ie(elems, elems_len, WLAN_EID_HT_CAP);
+	if (!ie) {
+		wpa_printf(MSG_ERROR,
+			   "NAN: No HT capabilities in peer elems for NMI station");
+		return -1;
+	}
+
+	sta_params.ht_capabilities = (const void *)(ie + 2);
+	ie = get_ie(elems, elems_len, WLAN_EID_VHT_CAP);
+	sta_params.vht_capabilities = ie ? (const void *)(ie + 2) : NULL;
+
+	return wpa_drv_sta_add(wpa_s, &sta_params);
+}
+
+
+static int wpas_nan_add_ndi_sta(struct wpa_supplicant *wpa_s,
+				const u8 *peer_nmi, const u8 *local_ndi,
+				const u8 *peer_ndi, bool install_keys,
+				bool first_ndp)
+{
+	u8 tk[WPA_TK_MAX_LEN];
+	size_t tk_len;
+	enum nan_cipher_suite_id csid;
+	struct wpa_supplicant *ndi_wpa_s;
+	struct hostapd_sta_add_params sta_params;
+
+	ndi_wpa_s = wpas_nan_get_ndi_iface(wpa_s, local_ndi);
+	if (!ndi_wpa_s) {
+		wpa_printf(MSG_ERROR,
+			   "NAN: No NDI interface found for addr " MACSTR,
+			   MAC2STR(local_ndi));
+		return -1;
+	}
+
+	/* HT/VHT capabilities are configured per NMI station only for
+	 * the first NDP. After that it is assumed that capablities are not
+	 * changing.
+	 */
+	if (first_ndp &&
+	    wpas_nan_configure_nmi_sta_capa(wpa_s, peer_nmi)) {
+		wpa_printf(MSG_ERROR,
+			   "NAN: Failed to configure NMI station capabilities");
+		return -1;
+	}
+
+	os_memset(&sta_params, 0, sizeof(sta_params));
+	sta_params.addr = peer_ndi;
+	sta_params.nmi_addr = peer_nmi;
+	sta_params.flags = WPA_STA_AUTHENTICATED | WPA_STA_ASSOCIATED;
+
+	/* Set MFP flag early, to prevent races until keys are installed */
+	if (install_keys)
+		sta_params.flags |= WPA_STA_MFP;
+	else
+		sta_params.flags |= WPA_STA_AUTHORIZED;
+
+	if (wpa_drv_sta_add(ndi_wpa_s, &sta_params)) {
+		wpa_printf(MSG_ERROR,
+			   "NAN: Failed to add NDI station for peer " MACSTR,
+			   MAC2STR(peer_ndi));
+		return -1;
+	}
+
+	if (!install_keys) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: NDI station added without keys for peer "
+			   MACSTR, MAC2STR(peer_ndi));
+		return 0;
+	}
+
+	if (nan_peer_get_tk(wpa_s->nan, peer_nmi, peer_ndi, local_ndi, tk,
+			    &tk_len, &csid)) {
+		wpa_printf(MSG_ERROR,
+			   "NAN: Failed to get TK for NDI station");
+		wpa_drv_sta_remove(ndi_wpa_s, peer_ndi);
+		return -1;
+	}
+
+	if (wpas_nan_set_ndi_keys(ndi_wpa_s, peer_ndi, csid, tk, tk_len)) {
+		wpa_printf(MSG_ERROR,
+			   "NAN: Failed to set NDI keys for peer " MACSTR,
+			   MAC2STR(peer_ndi));
+		wpa_drv_sta_remove(ndi_wpa_s, peer_ndi);
+		return -1;
+	}
+
+	return wpa_drv_sta_set_flags(ndi_wpa_s, peer_ndi, WPA_STA_AUTHORIZED,
+				     WPA_STA_AUTHORIZED, ~0);
+}
+
+
+static void wpas_nan_remove_ndi_sta(struct wpa_supplicant *wpa_s,
+				    const u8 *local_ndi,
+				    const u8 *peer_ndi)
+{
+	struct wpa_supplicant *ndi_wpa_s;
+
+	ndi_wpa_s = wpas_nan_get_ndi_iface(wpa_s, local_ndi);
+	if (!ndi_wpa_s) {
+		wpa_printf(MSG_ERROR,
+			   "NAN: No NDI interface found for addr " MACSTR,
+			   MAC2STR(local_ndi));
+		return;
+	}
+
+	wpas_nan_remove_ndi_keys(ndi_wpa_s, peer_ndi);
+	wpa_drv_sta_remove(ndi_wpa_s, peer_ndi);
+}
+
+
 static int wpas_nan_ndp_connected_cb(void *ctx,
 				     struct nan_ndp_connection_params *params)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 	char *ssi_hex = NULL;
 
+	if (wpas_nan_add_ndi_sta(wpa_s, params->ndp_id.peer_nmi,
+			         params->local_ndi, params->peer_ndi,
+				 params->install_keys,
+				 params->first_ndp) < 0) {
+		wpa_printf(MSG_ERROR,
+			   "NAN: Failed to add NDI station for NDP connection");
+		return -1;
+	}
+
 	if (params->ssi) {
 		ssi_hex = os_zalloc(2 * params->ssi_len + 1);
 		if (!ssi_hex)
@@ -379,6 +574,7 @@ static void wpas_nan_ndp_disconnected_cb(void *ctx, struct nan_ndp_id *ndp_id,
 {
 	struct wpa_supplicant *wpa_s = ctx;
 
+	wpas_nan_remove_ndi_sta(wpa_s, local_ndi, peer_ndi);
 	wpa_msg_global(wpa_s, MSG_INFO, NAN_NDP_DISCONNECTED
 		       "peer=" MACSTR " ndp_id=%u local_ndi=" MACSTR
 		       " peer_ndi=" MACSTR " reason=%u",
-- 
2.49.0




More information about the Hostap mailing list