[PATCH 79/97] NAN: Add support for tracking the status of transmit requests

Andrei Otcheretianski andrei.otcheretianski at intel.com
Tue Apr 28 13:06:20 PDT 2026


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

Extend the NAN Discovery Engine (DE) to track the status of
transmit requests coming from higher layers:

- For transmit requests with valid cookie number, add the
  cookie and a digest of the transmitted frame to a list.
- Once a Tx status for a transmitted frame is received, search
  for the corresponding entry in the list of tracked frames,
  and when found report to the higher layer whether the frame
  was acknowledged or not.

Signed-off-by: Ilan Peer <ilan.peer at intel.com>
---
 src/ap/nan_usd_ap.c             |   2 +-
 src/common/nan_de.c             | 166 ++++++++++++++++++++++++++++++--
 src/common/nan_de.h             |   3 +-
 wpa_supplicant/nan_supplicant.c |   4 +-
 4 files changed, 162 insertions(+), 13 deletions(-)

diff --git a/src/ap/nan_usd_ap.c b/src/ap/nan_usd_ap.c
index 8620644cb3..63696a8d19 100644
--- a/src/ap/nan_usd_ap.c
+++ b/src/ap/nan_usd_ap.c
@@ -277,5 +277,5 @@ int hostapd_nan_usd_transmit(struct hostapd_data *hapd, int handle,
 	if (!hapd->nan_de)
 		return -1;
 	return nan_de_transmit(hapd->nan_de, handle, ssi, elems, peer_addr,
-			       req_instance_id, NULL);
+			       req_instance_id, NULL, NULL);
 }
diff --git a/src/common/nan_de.c b/src/common/nan_de.c
index 696aa97556..f15836bebe 100644
--- a/src/common/nan_de.c
+++ b/src/common/nan_de.c
@@ -18,6 +18,7 @@
 #include "nan/nan.h"
 #include "nan_defs.h"
 #include "nan_de.h"
+#include "utils/crc32.h"
 
 static const u8 nan_network_id[ETH_ALEN] =
 { 0x51, 0x6f, 0x9a, 0x01, 0x00, 0x00 };
@@ -104,6 +105,19 @@ struct nan_de_service {
 
 #define NAN_DE_RSSI_CLOSE_PROXIMITY (-70) /* dBm */
 
+struct nan_de_tracked_tx {
+	struct dl_list list;
+	u8 dst[ETH_ALEN];
+	u32 cookie;
+	u32 digest;
+	bool with_wait;
+};
+
+enum nan_de_flush_tracked_tx_reason {
+	NAN_DE_FLUSH_TRACKED_TX_FLUSH_ALL,
+	NAN_DE_FLUSH_TRACKED_TX_WAIT_EXPIRED,
+};
+
 struct nan_de {
 	u8 nmi[ETH_ALEN];
 	u8 cluster_id[ETH_ALEN];
@@ -131,6 +145,12 @@ struct nan_de {
 	/* RSSI threshold for close proximity, or zero if not limited */
 	int rssi_threshold;
 
+	/*
+	 * list of transmit requests for which the caller requested
+	 * status indicating if the frame was acknowledged or not.
+	 */
+	struct dl_list tracked_tx;
+
 #ifdef CONFIG_TESTING_OPTIONS
 	/*
 	 * When set, multicast follow up SDFs would be sent as
@@ -174,6 +194,7 @@ struct nan_de * nan_de_init(const u8 *nmi, bool offload, bool ap,
 	de->cfg.n_max = NAN_DE_N_MAX;
 
 	de->rssi_threshold = NAN_DE_RSSI_CLOSE_PROXIMITY;
+	dl_list_init(&de->tracked_tx);
 
 	return de;
 }
@@ -209,14 +230,117 @@ static void nan_de_service_deinit(struct nan_de *de, struct nan_de_service *srv,
 }
 
 
+static void nan_de_flush_tracked_tx(struct nan_de *de,
+				    enum nan_de_flush_tracked_tx_reason reason)
+{
+	struct nan_de_tracked_tx *tx, *tmp;
+
+	dl_list_for_each_safe(tx, tmp, &de->tracked_tx,
+			      struct nan_de_tracked_tx,
+			      list) {
+		if (reason == NAN_DE_FLUSH_TRACKED_TX_WAIT_EXPIRED &&
+		    !tx->with_wait)
+			continue;
+
+		de->cb.transmit_req_status(de->cb.ctx, tx->cookie,
+					   false);
+		dl_list_del(&tx->list);
+		os_free(tx);
+	}
+}
+
+
 static void nan_de_clear_pending(struct nan_de *de)
 {
+	nan_de_flush_tracked_tx(de,
+				NAN_DE_FLUSH_TRACKED_TX_FLUSH_ALL);
+
 	de->listen_freq = 0;
 	de->tx_wait_status_freq = 0;
 	de->tx_wait_end_freq = 0;
 }
 
 
+static u32 nan_de_track_tx_digest(const u8 *data, size_t len)
+{
+	return ieee80211_crc32(data, len);
+}
+
+
+static struct nan_de_tracked_tx *
+nan_de_add_tracked_tx(struct nan_de *de, const u8 *dst, bool with_wait,
+		      u32 cookie, const struct wpabuf *buf)
+{
+	struct nan_de_tracked_tx *tx;
+
+	if (!de->cb.transmit_req_status) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: No tx_status callback, cannot track Tx");
+		return NULL;
+	}
+
+	if (!cookie) {
+		wpa_printf(MSG_DEBUG, "NAN: Invalid cookie for Tx tracking");
+		return NULL;
+	}
+
+	dl_list_for_each(tx, &de->tracked_tx, struct nan_de_tracked_tx, list) {
+		if (ether_addr_equal(tx->dst, dst) && tx->cookie == cookie) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: Already tracking Tx cookie %u to " MACSTR,
+				   tx->cookie, MAC2STR(tx->dst));
+			return NULL;
+		}
+	}
+
+	tx = os_zalloc(sizeof(*tx));
+	if (!tx)
+		return NULL;
+
+	os_memcpy(tx->dst, dst, ETH_ALEN);
+	tx->cookie = cookie;
+	tx->with_wait = with_wait;
+	tx->digest = nan_de_track_tx_digest(wpabuf_head(buf),
+					    wpabuf_len(buf));
+
+	dl_list_add(&de->tracked_tx, &tx->list);
+
+	wpa_printf(MSG_DEBUG, "NAN: Track Tx cookie %u digest 0x%08x",
+		   tx->cookie, tx->digest);
+
+	return tx;
+}
+
+
+static void nan_de_tx_status_match(struct nan_de *de, const u8 *data,
+				   size_t len, u8 acked)
+{
+	struct nan_de_tracked_tx *tx;
+	const struct ieee80211_mgmt *mgmt = (void *)data;
+	const u8 *pos = (void *)&mgmt->u.action;
+
+	if (len <= offsetof(struct ieee80211_mgmt, u.action))
+		return;
+
+	len = data + len - pos;
+
+	dl_list_for_each(tx, &de->tracked_tx, struct nan_de_tracked_tx, list) {
+		if (ether_addr_equal(tx->dst, mgmt->da) &&
+		    tx->digest == nan_de_track_tx_digest(pos, len)) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: Tx status for cookie=%u ack=%u",
+				   tx->cookie, acked);
+
+			de->cb.transmit_req_status(de->cb.ctx, tx->cookie,
+						   acked);
+			dl_list_del(&tx->list);
+			os_free(tx);
+			return;
+		}
+	}
+}
+
+
 void nan_de_flush(struct nan_de *de)
 {
 	unsigned int i;
@@ -293,16 +417,30 @@ static struct wpabuf * nan_de_alloc_sdf(struct nan_de *de, const u8 *dst,
 static int nan_de_tx(struct nan_de *de, unsigned int freq,
 		     unsigned int wait_time,
 		     const u8 *dst, const u8 *src, const u8 *bssid,
-		     const struct wpabuf *buf)
+		     const struct wpabuf *buf,
+		     u32 *cookie)
 {
+	struct nan_de_tracked_tx *tracked_tx = NULL;
 	int res;
 
 	if (!de->cb.tx)
 		return -1;
 
+	if (cookie) {
+		tracked_tx = nan_de_add_tracked_tx(de, dst, !!wait_time,
+						   *cookie, buf);
+		if (!tracked_tx)
+			return -1;
+	}
+
 	res = de->cb.tx(de->cb.ctx, freq, wait_time, dst, src, bssid, buf);
-	if (res < 0)
+	if (res < 0) {
+		if (tracked_tx) {
+			dl_list_del(&tracked_tx->list);
+			os_free(tracked_tx);
+		}
 		return res;
+	}
 
 	de->tx_wait_status_freq = freq;
 	de->tx_wait_end_freq = wait_time ? freq : 0;
@@ -336,7 +474,8 @@ static void nan_de_tx_sdf(struct nan_de *de, struct nan_de_service *srv,
 			  enum nan_service_control_type type,
 			  const u8 *dst, const u8 *a3, u8 req_instance_id,
 			  const struct wpabuf *ssi,
-			  const struct wpabuf *attrs)
+			  const struct wpabuf *attrs,
+			  u32 *cookie)
 {
 	struct wpabuf *buf;
 	size_t len = 0, sda_len, sdea_len;
@@ -511,7 +650,7 @@ static void nan_de_tx_sdf(struct nan_de *de, struct nan_de_service *srv,
 	}
 
 	nan_de_tx(de, srv->sync ? 0 : srv->freq, srv->sync ? 0 : wait_time,
-		  dst, forced_addr, a3, buf);
+		  dst, forced_addr, a3, buf, cookie);
 	wpabuf_free(buf);
 }
 
@@ -616,7 +755,7 @@ static void nan_de_tx_multicast(struct nan_de *de, struct nan_de_service *srv,
 	}
 
 	nan_de_tx_sdf(de, srv, wait_time, type, network_id, bssid,
-		      req_instance_id, srv->ssi, NULL);
+		      req_instance_id, srv->ssi, NULL, NULL);
 	os_get_reltime(&srv->last_multicast);
 }
 
@@ -1055,6 +1194,8 @@ void nan_de_tx_status(struct nan_de *de, unsigned int freq, const u8 *dst,
 {
 	if (freq == de->tx_wait_status_freq)
 		de->tx_wait_status_freq = 0;
+
+	nan_de_tx_status_match(de, data, data_len, ack);
 }
 
 
@@ -1064,6 +1205,10 @@ void nan_de_tx_wait_ended(struct nan_de *de)
 		wpa_printf(MSG_DEBUG,
 			   "NAN: TX wait for response ended (freq=%u)",
 			   de->tx_wait_end_freq);
+
+	nan_de_flush_tracked_tx(de,
+				NAN_DE_FLUSH_TRACKED_TX_WAIT_EXPIRED);
+
 	de->tx_wait_end_freq = 0;
 	nan_de_run_timer(de);
 }
@@ -1522,7 +1667,7 @@ static bool nan_de_rx_publish(struct nan_de *de, struct nan_de_service *srv,
 		 * Service Specific Info field if it received a matching
 		 * unsolicited Publish message. */
 		nan_de_transmit(de, srv->id, NULL, NULL, peer_addr,
-				instance_id, NULL);
+				instance_id, NULL, NULL);
 	}
 
 send_event:
@@ -1626,7 +1771,8 @@ static bool nan_de_rx_subscribe(struct nan_de *de, struct nan_de_service *srv,
 
 	nan_de_tx_sdf(de, srv, 100, NAN_SRV_CTRL_PUBLISH,
 		      srv->publish.solicited_multicast ?
-		      network_id : peer_addr, a3, instance_id, srv->ssi, NULL);
+		      network_id : peer_addr, a3, instance_id, srv->ssi, NULL,
+		      NULL);
 
 	if (!srv->is_p2p && !srv->sync)
 		nan_de_pause_state(srv, peer_addr, instance_id);
@@ -2452,7 +2598,8 @@ void nan_de_cancel_subscribe(struct nan_de *de, int subscribe_id)
 int nan_de_transmit(struct nan_de *de, int handle,
 		    const struct wpabuf *ssi, const struct wpabuf *elems,
 		    const u8 *peer_addr, u8 req_instance_id,
-		    const struct wpabuf *nan_attrs)
+		    const struct wpabuf *nan_attrs,
+		    u32 *cookie)
 {
 	struct nan_de_service *srv;
 	const u8 *a3;
@@ -2483,7 +2630,8 @@ int nan_de_transmit(struct nan_de *de, int handle,
 	else
 		a3 = network_id;
 	nan_de_tx_sdf(de, srv, 100, NAN_SRV_CTRL_FOLLOW_UP,
-		      peer_addr, a3, req_instance_id, ssi, nan_attrs);
+		      peer_addr, a3, req_instance_id, ssi, nan_attrs,
+		      cookie);
 
 	srv->listen_stopped = false;
 	return 0;
diff --git a/src/common/nan_de.h b/src/common/nan_de.h
index 184f725507..bf11659efb 100644
--- a/src/common/nan_de.h
+++ b/src/common/nan_de.h
@@ -81,6 +81,7 @@ struct nan_callbacks {
 				     unsigned int freq);
 	void (*add_extra_attrs)(void *ctx, struct wpabuf *buf);
 	bool (*is_peer_paired)(void *ctx, const u8 *addr);
+	void (*transmit_req_status)(void *ctx, u32 cookie, bool ack);
 };
 
 bool nan_de_is_nan_network_id(const u8 *addr);
@@ -266,7 +267,7 @@ void nan_de_cancel_subscribe(struct nan_de *de, int subscribe_id);
 int nan_de_transmit(struct nan_de *de, int handle,
 		    const struct wpabuf *ssi, const struct wpabuf *elems,
 		    const u8 *peer_addr, u8 req_instance_id,
-		    const struct wpabuf *nan_attrs);
+		    const struct wpabuf *nan_attrs, u32 *cookie);
 
 void nan_de_dw_trigger(struct nan_de *de, int freq);
 void nan_de_set_cluster_id(struct nan_de *de, const u8 *cluster_id);
diff --git a/wpa_supplicant/nan_supplicant.c b/wpa_supplicant/nan_supplicant.c
index a8595d0d23..a4cc5597fd 100644
--- a/wpa_supplicant/nan_supplicant.c
+++ b/wpa_supplicant/nan_supplicant.c
@@ -996,7 +996,7 @@ static int wpas_nan_transmit_followup_cb(void *ctx, const u8 *peer_nmi,
 		return -1;
 
 	return nan_de_transmit(wpa_s->nan_de, handle, NULL, NULL,
-			       peer_nmi, req_instance_id, attrs);
+			       peer_nmi, req_instance_id, attrs, NULL);
 }
 
 
@@ -4392,7 +4392,7 @@ int wpas_nan_transmit(struct wpa_supplicant *wpa_s, int handle,
 	if (!wpa_s->nan_de)
 		return -1;
 	return nan_de_transmit(wpa_s->nan_de, handle, ssi, elems, peer_addr,
-			       req_instance_id, NULL);
+			       req_instance_id, NULL, NULL);
 }
 
 
-- 
2.53.0




More information about the Hostap mailing list