[PATCH 76/92] NAN: Support transmission and reception of NAFs in a secure manner

Andrei Otcheretianski andrei.otcheretianski at intel.com
Wed Apr 22 05:24:07 PDT 2026


From: Ilan Peer <ilan.peer at intel.com>

When a secure NDP is established between two non paired devices,
all NAN Action Frames (NAFs) must be transmitted in a secure manner
on the secure channel established between the peers. Thus, add
support for transmitting and receiving NAFs over NAN Data
interfaces:

- When sending a NAF, set the category to 'Protected Dual of Public',
  set the source and destination addresses to those of the NDIs
  for which a secure NDP was established, and use the corresponding
  NAN Data interface for the Tx.
- When receiving NAF over a NAN Data interface, tunnel it to the NAN
  management interface for processing.

Signed-off-by: Ilan Peer <ilan.peer at intel.com>
---
 src/nan/nan.c                   | 155 +++++++++++++++++++++++++++++++-
 wpa_supplicant/nan_supplicant.c |  39 ++++++++
 2 files changed, 190 insertions(+), 4 deletions(-)

diff --git a/src/nan/nan.c b/src/nan/nan.c
index b4f16bfc6b..e8556f3638 100644
--- a/src/nan/nan.c
+++ b/src/nan/nan.c
@@ -1158,7 +1158,8 @@ static void nan_action_build_header(struct nan_data *nan, struct nan_peer *peer,
 {
 	u8 category = WLAN_ACTION_PUBLIC;
 
-	if (nan_pairing_is_peer_paired(nan, peer->nmi_addr))
+	if (nan_pairing_is_peer_paired(nan, peer->nmi_addr) ||
+	    !dl_list_empty(&peer->info.sec))
 		category = WLAN_ACTION_PROTECTED_DUAL;
 
 	wpabuf_put_u8(buf, category);
@@ -1217,6 +1218,8 @@ static int nan_action_send(struct nan_data *nan, struct nan_peer *peer,
 			   enum nan_subtype subtype)
 {
 	struct wpabuf *buf;
+	struct nan_peer_sec_info_entry *cur, *next;
+	const u8 *src, *dst;
 	int ret;
 
 	buf = wpabuf_alloc(NAN_MAX_NAF_LEN);
@@ -1233,9 +1236,27 @@ static int nan_action_send(struct nan_data *nan, struct nan_peer *peer,
 
 	if (!nan->cfg->send_naf)
 		goto out;
-	ret = nan->cfg->send_naf(nan->cfg->cb_ctx, peer->nmi_addr, NULL,
-				 nan->cluster_id, buf);
 
+	/*
+	 * By default, the NAN management interface is used for the NAF
+	 * transmission. However, when pairing was not established with the peer
+	 * and there is a secure NDP with the peer, need to use the NDIs so that
+	 * the NAF would be sent in a secure manner
+	 */
+	src = NULL;
+	dst = peer->nmi_addr;
+
+	if (!(peer->pairing.flags & NAN_PAIRING_FLAG_PAIRED)) {
+		dl_list_for_each_safe(cur, next, &peer->info.sec,
+				      struct nan_peer_sec_info_entry, list) {
+			src = cur->local_ndi;
+			dst = cur->peer_ndi;
+			break;
+		}
+	}
+
+	ret = nan->cfg->send_naf(nan->cfg->cb_ctx, dst, src,
+				 nan->cluster_id, buf);
 out:
 	wpa_printf(MSG_DEBUG, "NAN: send_naf: ret=%d", ret);
 	wpabuf_free(buf);
@@ -1601,6 +1622,67 @@ static int nan_action_rx_ndp(struct nan_data *nan, struct nan_peer *peer,
 }
 
 
+/*
+ * nan_action_substitute_src - Substitute the source address in the NAF if
+ * it matches an NDI of an existing NDP
+ *
+ * @nan: NAN module context from nan_init()
+ * @mgmt: Pointer to the IEEE 802.11 management frame
+ * @len: Length of the management frame in octets
+ *
+ * NAFs can be sent and received on NDIs. In such cases, the source address
+ * in the 802.11 header would be the NDI address. This function checks if
+ * the source address matches any known NDI address and if so, substitutes
+ * it with the NMI address of the corresponding peer.
+ */
+static void nan_action_substitute_src(struct nan_data *nan,
+				      const struct ieee80211_mgmt *mgmt,
+				      size_t len)
+{
+	struct nan_peer *peer;
+
+	/* If the peer is known, nothing needs to be changed */
+	peer = nan_get_peer(nan, mgmt->sa);
+	if (peer)
+		return;
+
+	/*
+	 * Find a peer with which we have an NDI that matches the source address
+	 * in the frame, and if found, substitute the frames source address with
+	 * the peer NMI
+	 */
+	dl_list_for_each(peer, &nan->peer_list, struct nan_peer, list) {
+		struct nan_ndp *pndp;
+
+		/* When a peer is paired, NAFs are not allowed on NDIs */
+		if (peer->pairing.flags & NAN_PAIRING_FLAG_PAIRED)
+			continue;
+
+		dl_list_for_each(pndp, &peer->ndps, struct nan_ndp, list) {
+			const u8 *addr;
+
+			if (pndp->initiator)
+				addr = pndp->resp_ndi;
+			else
+				addr = pndp->init_ndi;
+
+			if (os_memcmp(addr, mgmt->sa, ETH_ALEN))
+				continue;
+
+			wpa_printf(MSG_DEBUG,
+				   "NAN: NAF from=" MACSTR " Received on NDI=" MACSTR,
+				   MAC2STR(peer->nmi_addr), MAC2STR(mgmt->sa));
+
+			os_memcpy((void *)mgmt->sa, peer->nmi_addr, ETH_ALEN);
+		}
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "NAN: NAF from unknown peer=" MACSTR,
+		   MAC2STR(mgmt->sa));
+}
+
+
 /*
  * nan_action_rx - Process a received NAN Action Frame
  * @nan: NAN module context from nan_init()
@@ -1619,7 +1701,10 @@ int nan_action_rx(struct nan_data *nan, const struct ieee80211_mgmt *mgmt,
 	if (!nan_ndp_supported(nan))
 		return -1;
 
+	nan_action_substitute_src(nan, mgmt, len);
+
 	/* Parse the NAF and validate its general structure */
+
 	ret = nan_parse_naf(nan, mgmt, len, &msg);
 	if (ret)
 		return ret;
@@ -1706,6 +1791,68 @@ void nan_set_cluster_id(struct nan_data *nan, const u8 *cluster_id)
 }
 
 
+/*
+ * nan_tx_status_get_peer - Get the peer for a transmitted NAF
+ *
+ * @nan: NAN module context from nan_init()
+ * @dst: Destination address of the transmitted frame
+ * Return: Pointer to the peer or NULL if not found
+ */
+static struct nan_peer *nan_tx_status_get_peer(struct nan_data *nan,
+					       const u8 *dst)
+{
+	struct nan_peer *peer;
+
+	peer = nan_get_peer(nan, dst);
+	if (peer)
+		return peer;
+
+	/*
+	 * It is possible that the NAF was transmitted over an NDI, e.g.,
+	 * in case that a secure NDP was established with the peer
+	 */
+	dl_list_for_each(peer, &nan->peer_list, struct nan_peer, list) {
+		struct nan_ndp *pndp;
+		const u8 *paddr;
+
+		/* When a peer is paired, NAFs are not allowed on NDIs */
+		if (peer->pairing.flags & NAN_PAIRING_FLAG_PAIRED)
+			continue;
+
+		/*
+		 * When an NDP termination is initiated locally, the NDP is
+		 * removed from the list and is set to the 'ndp_setup' object
+		 * so need to also check that one.
+		 */
+		if (peer->ndp_setup.ndp) {
+			pndp = peer->ndp_setup.ndp;
+
+			if (pndp->initiator)
+				paddr = pndp->resp_ndi;
+			else
+				paddr = pndp->init_ndi;
+
+			if (!os_memcmp(dst, paddr, ETH_ALEN))
+				return peer;
+		}
+
+		dl_list_for_each(pndp, &peer->ndps, struct nan_ndp, list) {
+			if (pndp->initiator)
+				paddr = pndp->resp_ndi;
+			else
+				paddr = pndp->init_ndi;
+
+			if (os_memcmp(dst, paddr, ETH_ALEN))
+				continue;
+
+			return peer;
+		}
+	}
+
+	return NULL;
+}
+
+
 /*
  * nan_tx_status - Notification of the result of a transmitted NAN Action frame
  * @nan: NAN module context from nan_init()
@@ -1729,7 +1876,7 @@ int nan_tx_status(struct nan_data *nan, const u8 *dst, const u8 *data,
 	wpa_printf(MSG_DEBUG, "NAN: TX status: peer=" MACSTR ", acked=%u",
 		   MAC2STR(dst), acked);
 
-	peer = nan_get_peer(nan, dst);
+	peer = nan_tx_status_get_peer(nan, dst);
 	if (!peer) {
 		wpa_printf(MSG_DEBUG, "NAN: TX status: peer not found");
 		return 0;
diff --git a/wpa_supplicant/nan_supplicant.c b/wpa_supplicant/nan_supplicant.c
index 404e28e74d..af1a21ce63 100644
--- a/wpa_supplicant/nan_supplicant.c
+++ b/wpa_supplicant/nan_supplicant.c
@@ -653,6 +653,18 @@ static int wpas_nan_send_naf_cb(void *ctx, const u8 *dst, const u8 *src,
 
 	a2 = src ? src : wpa_s->own_addr;
 
+	if (src && src != wpa_s->own_addr) {
+		wpa_printf(MSG_DEBUG, "NAN: Use NDI iface for sending NAF");
+
+		wpa_s = wpas_nan_get_ndi_iface(wpa_s, src);
+		if (!wpa_s) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: No NDI interface found for addr "
+				   MACSTR, MAC2STR(src));
+			wpa_s = ctx;
+		}
+	}
+
 	wpa_printf(MSG_DEBUG, "NAN: Send NAF - dst=" MACSTR " src=" MACSTR
 		   " cluster_id=" MACSTR, MAC2STR(dst), MAC2STR(a2),
 		   MAC2STR(cluster_id));
@@ -3848,6 +3860,22 @@ void wpas_nan_usd_state_change_notif(struct wpa_supplicant *wpa_s)
 }
 
 
+static struct wpa_supplicant *
+wpas_nan_get_mgmt_iface(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_supplicant *nmi_wpa_s;
+
+	for (nmi_wpa_s = wpa_s->global->ifaces; nmi_wpa_s;
+	     nmi_wpa_s = nmi_wpa_s->next) {
+		if (!nmi_wpa_s->nan_mgmt)
+			continue;
+		return nmi_wpa_s;
+	}
+
+	return wpa_s;
+}
+
+
 int wpas_nan_tx_status(struct wpa_supplicant *wpa_s,
 			const u8 *data, size_t data_len, int acked)
 {
@@ -3855,6 +3883,7 @@ int wpas_nan_tx_status(struct wpa_supplicant *wpa_s,
 	const struct ieee80211_mgmt *mgmt =
 		(const struct ieee80211_mgmt *) data;
 
+	wpa_s = wpas_nan_get_mgmt_iface(wpa_s);
 	if (!wpas_nan_ndp_allowed(wpa_s))
 		return -1;
 
@@ -3875,6 +3904,16 @@ int wpas_nan_tx_status(struct wpa_supplicant *wpa_s,
 void wpas_nan_rx_naf(struct wpa_supplicant *wpa_s,
 		     const struct ieee80211_mgmt *mgmt, size_t len)
 {
+	if (mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL) {
+		wpa_printf(MSG_DEBUG, "NAN: RX NAF: ifname=%s: DUAL protected",
+			   wpa_s->ifname);
+
+		wpa_s = wpas_nan_get_mgmt_iface(wpa_s);
+
+		wpa_printf(MSG_DEBUG, "NAN: Rx NAF: continue processing on %s",
+			   wpa_s->ifname);
+	}
+
 	if (!wpas_nan_ndp_allowed(wpa_s))
 		return;
 
-- 
2.53.0




More information about the Hostap mailing list