[PATCH 04/15] NAN: Configure a peer NMI station for unicast transmissions

Andrei Otcheretianski andrei.otcheretianski at intel.com
Thu Oct 8 09:05:13 PDT 2026


Add transient NMI station and schedule for unicast transmissions.
Otherwise follow up frames can be transmitted during DWs only without
considering actual peer presence.
Since it is common to have multiple frames in follow up exchanges,
remove this transient peer schedule upon timeout (2s) to avoid re-adding
the same schedule/peer for the subsequent frames.
If the peer is already configured due to active pairing or NDP - do
nothing.

Signed-off-by: Andrei Otcheretianski <andrei.otcheretianski at intel.com>
---
 src/nan/nan.c                   | 139 +++++++++++++++++++++++++++++---
 src/nan/nan.h                   |   3 +
 src/nan/nan_i.h                 |   3 +-
 src/nan/nan_pairing.c           |   4 +-
 wpa_supplicant/nan_supplicant.c |   5 ++
 5 files changed, 142 insertions(+), 12 deletions(-)

diff --git a/src/nan/nan.c b/src/nan/nan.c
index 4eabe56808..1f77ba40f5 100644
--- a/src/nan/nan.c
+++ b/src/nan/nan.c
@@ -21,7 +21,16 @@
 #define NAN_NDP_SETUP_TIMEOUT_LONG  30
 #define NAN_NDP_SETUP_TIMEOUT_SHORT 2
 
+/*
+ * Time to keep a transient peer NMI station configured after it is set up for a
+ * one-time unicast transmission to the peer (seconds).
+ */
+#define NAN_TRANSIENT_PEER_TIMEOUT 2
+
 static void nan_peer_state_timeout(void *eloop_ctx, void *timeout_ctx);
+static void nan_transient_peer_timeout(void *eloop_ctx, void *timeout_ctx);
+static void nan_arm_transient_timeout(struct nan_data *nan,
+				      struct nan_peer *peer);
 static void nan_idle_period_timeout(void *eloop_ctx, void *timeout_ctx);
 static void nan_ndp_disconnected(struct nan_data *nan, struct nan_peer *peer,
 				 enum nan_reason reason,
@@ -261,6 +270,7 @@ static void nan_del_peer(struct nan_data *nan, struct nan_peer *peer)
 	nan_ndl_reset(nan, peer);
 	nan_peer_flush_sec(&peer->info);
 	eloop_cancel_timeout(nan_peer_state_timeout, nan, peer);
+	eloop_cancel_timeout(nan_transient_peer_timeout, nan, peer);
 	nan_pairing_deinit_peer(nan, peer, true);
 	os_free(peer);
 }
@@ -1075,7 +1085,7 @@ static void nan_peer_update_schedule(struct nan_data *nan,
 	common_bf = nan_peer_schedule_intersection(nan, peer, sched);
 	if (common_bf && nan_ndl_meets_qos(nan, peer, common_bf) &&
 	    nan_ndl_validate_peer_avail(nan, peer))
-		ret = nan_configure_peer_schedule(nan, peer, sched);
+		ret = nan_configure_peer_schedule(nan, peer, sched, false);
 	else
 		wpa_printf(MSG_DEBUG, "NAN: New peer schedule breaks NDL");
 
@@ -1132,9 +1142,21 @@ static void nan_merge_peer_info(struct nan_data *nan, struct nan_peer *peer,
 
 	old->last_seen = new->last_seen;
 
-	if (schedule_changed && peer->ndl &&
-	    peer->ndl->state == NAN_NDL_STATE_DONE)
+	if (!schedule_changed)
+		return;
+
+	/*
+	 * Update the configured NMI station with the new peer availability. For
+	 * an NDL peer validate it against the NDL; otherwise reconfigure the
+	 * schedule, keeping any pending transient teardown.
+	 */
+	if (peer->ndl && peer->ndl->state == NAN_NDL_STATE_DONE)
 		nan_peer_update_schedule(nan, peer, &nan->sched);
+	else if (peer->configured)
+		nan_configure_peer_schedule(nan, peer, &nan->sched,
+					    eloop_is_timeout_registered(
+						nan_transient_peer_timeout,
+						nan, peer));
 }
 
 
@@ -1792,7 +1814,8 @@ nan_peer_get_committed_avail(const struct nan_data *nan,
 
 
 int nan_configure_peer_schedule(struct nan_data *nan, struct nan_peer *peer,
-				const struct nan_schedule *local_sched)
+				const struct nan_schedule *local_sched,
+				bool transient)
 {
 	int ret;
 	struct nan_dev_capa_entry *cur;
@@ -1850,6 +1873,16 @@ int nan_configure_peer_schedule(struct nan_data *nan, struct nan_peer *peer,
 	}
 
 	peer->configured = true;
+
+	/*
+	 * Unless this is a transient (one-time TX) configuration, an explicit
+	 * schedule configuration (e.g., for a pairing or NDP session) takes
+	 * ownership of the NMI station. Cancel any pending transient teardown
+	 * so the station is not removed.
+	 */
+	if (!transient)
+		eloop_cancel_timeout(nan_transient_peer_timeout, nan, peer);
+
 	return 0;
 }
 
@@ -1875,6 +1908,93 @@ int nan_clear_peer_schedule(struct nan_data *nan, struct nan_peer *peer)
 }
 
 
+static void nan_transient_peer_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct nan_data *nan = eloop_ctx;
+	struct nan_peer *peer = timeout_ctx;
+	struct wpabuf *pending_auth1;
+
+	wpa_printf(MSG_DEBUG,
+		   "NAN: Transient peer " MACSTR
+		   " expired - remove NMI station", MAC2STR(peer->nmi_addr));
+
+	/*
+	 * Clearing peer schedule removes NMI station and resets pairing state.
+	 * Since we may have a pending pairing request, when this timeout fires,
+	 * detach the pending Auth1 buffer so it is not freed when clearing the
+	 * peer schedule. This allows the pending pairing request to still be
+	 * accepted or aborted later.
+	 */
+	pending_auth1 = peer->pairing.pending_auth1;
+	peer->pairing.pending_auth1 = NULL;
+	nan_clear_peer_schedule(nan, peer);
+	peer->pairing.pending_auth1 = pending_auth1;
+}
+
+
+static void nan_arm_transient_timeout(struct nan_data *nan,
+				      struct nan_peer *peer)
+{
+	eloop_cancel_timeout(nan_transient_peer_timeout, nan, peer);
+	eloop_register_timeout(NAN_TRANSIENT_PEER_TIMEOUT, 0,
+			       nan_transient_peer_timeout, nan, peer);
+}
+
+
+/**
+ * nan_setup_transient_peer - Configure a peer NMI station for one-time TX
+ * @nan: NAN module context from nan_init()
+ * @nmi_addr: Peer NMI
+ * Returns: 0 if a peer NMI station is configured (newly added or already
+ *	present), -1 otherwise
+ *
+ * Adds a dedicated NMI peer station (and schedule, when local and peer
+ * schedules are available) so that one-time unicast transmissions to the peer
+ * can use the peer's schedule, e.g., transmit during the peer's CDW (Committed
+ * DW information). The station is removed automatically a short time after it
+ * is set up, unless a pairing or NDP session takes ownership of the peer in the
+ * meantime. If the peer is already configured, the existing station is reused.
+ */
+int nan_setup_transient_peer(struct nan_data *nan, const u8 *nmi_addr)
+{
+	struct nan_peer *peer;
+
+	if (!nan || !nmi_addr)
+		return -1;
+
+	peer = nan_get_peer(nan, nmi_addr);
+	if (!peer) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: No known peer " MACSTR " for unicast TX",
+			   MAC2STR(nmi_addr));
+		return -1;
+	}
+
+	if (peer->configured) {
+		/*
+		 * If a transient teardown is pending, this is a transient peer;
+		 * refresh the timeout so multi-frame exchanges keep the same
+		 * NMI station. Otherwise the peer is owned by a session and
+		 * there is nothing to do.
+		 */
+		if (eloop_is_timeout_registered(nan_transient_peer_timeout,
+						nan, peer))
+			nan_arm_transient_timeout(nan, peer);
+		return 0;
+	}
+
+	if (nan_configure_peer_schedule(nan, peer, &nan->sched, true)) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Failed to configure transient peer " MACSTR,
+			   MAC2STR(nmi_addr));
+		return -1;
+	}
+
+	nan_arm_transient_timeout(nan, peer);
+	return 0;
+}
+
+
 /**
  * nan_process_followup - Process a received NAN Follow-up Action frame
  * @nan: NAN module context from nan_init()
@@ -2387,7 +2507,7 @@ static int nan_action_rx_ndp(struct nan_data *nan, struct nan_peer *peer,
 	if (peer->ndp_setup.state == NAN_NDP_STATE_DONE &&
 	    peer->ndl->state == NAN_NDL_STATE_DONE) {
 		wpa_printf(MSG_DEBUG, "NAN: NAF: NDP setup done");
-		if (nan_configure_peer_schedule(nan, peer, &nan->sched) ||
+		if (nan_configure_peer_schedule(nan, peer, &nan->sched, false) ||
 		    nan_ndp_connected(nan, peer))
 			nan_ndp_disconnected(nan, peer,
 					     NAN_REASON_UNSPECIFIED_REASON,
@@ -2693,7 +2813,7 @@ int nan_tx_status(struct nan_data *nan, const u8 *dst, const u8 *data,
 	    peer->ndl->state == NAN_NDL_STATE_DONE) {
 		wpa_printf(MSG_DEBUG, "NAN: TX status: NDP setup done");
 
-		if (nan_configure_peer_schedule(nan, peer, &nan->sched) ||
+		if (nan_configure_peer_schedule(nan, peer, &nan->sched, false) ||
 		    nan_ndp_connected(nan, peer))
 			nan_ndp_disconnected(nan, peer,
 					     NAN_REASON_UNSPECIFIED_REASON,
@@ -2753,7 +2873,7 @@ int nan_handle_ndp_setup(struct nan_data *nan, struct nan_ndp_params *params)
 
 		naf_oui = NAN_SUBTYPE_DATA_PATH_REQUEST;
 		timeout = NAN_NDP_SETUP_TIMEOUT_LONG;
-		ret = nan_configure_peer_schedule(nan, peer, &nan->sched);
+		ret = nan_configure_peer_schedule(nan, peer, &nan->sched, false);
 		if (ret) {
 			nan_ndp_setup_stop(nan, peer);
 			return ret;
@@ -2776,7 +2896,8 @@ int nan_handle_ndp_setup(struct nan_data *nan, struct nan_ndp_params *params)
 					    peer->ndp_setup.dialog_token);
 			if (!ret) {
 				ret = nan_configure_peer_schedule(nan, peer,
-								  &nan->sched);
+								  &nan->sched,
+								  false);
 				if (ret)
 					peer->ndl->send_naf_on_error = 1;
 			}
@@ -2811,7 +2932,7 @@ int nan_handle_ndp_setup(struct nan_data *nan, struct nan_ndp_params *params)
 				    peer->ndp_setup.dialog_token);
 		if (!ret) {
 			ret = nan_configure_peer_schedule(nan, peer,
-							  &nan->sched);
+							  &nan->sched, false);
 			if (ret)
 				peer->ndl->send_naf_on_error = 1;
 		}
diff --git a/src/nan/nan.h b/src/nan/nan.h
index 71a54d3ce0..088f98019c 100644
--- a/src/nan/nan.h
+++ b/src/nan/nan.h
@@ -878,6 +878,9 @@ int nan_add_peer(struct nan_data *nan, const u8 *addr,
 		 const u8 *device_attrs, size_t device_attrs_len);
 bool nan_peer_no_shared_cluster(struct nan_data *nan, const u8 *addr);
 bool nan_peer_nm_tk_installed(struct nan_data *nan, const u8 *addr);
+
+int nan_setup_transient_peer(struct nan_data *nan, const u8 *nmi_addr);
+
 bool nan_process_followup(struct nan_data *nan, const u8 *addr, const u8 *buf,
 			  size_t len, u8 req_instance_id, int handle);
 int nan_bootstrap_request(struct nan_data *nan, int handle,
diff --git a/src/nan/nan_i.h b/src/nan/nan_i.h
index 7fdf692833..18db961980 100644
--- a/src/nan/nan_i.h
+++ b/src/nan/nan_i.h
@@ -887,7 +887,8 @@ int nan_add_nira(struct wpabuf *buf, const u8 *tag, const u8 *nonce);
 void nan_parse_peer_dev_capa_ext(struct nan_data *nan, struct nan_peer *peer,
 				 struct nan_attrs *attrs);
 int nan_configure_peer_schedule(struct nan_data *nan, struct nan_peer *peer,
-				const struct nan_schedule *local_sched);
+				const struct nan_schedule *local_sched,
+				bool transient);
 bool nan_is_ndpe_supported(struct nan_data *nan, const struct nan_peer *peer);
 void nan_add_kde_hdr(struct wpabuf *buf, u32 kde, size_t data_len);
 int nan_clear_peer_schedule(struct nan_data *nan, struct nan_peer *peer);
diff --git a/src/nan/nan_pairing.c b/src/nan/nan_pairing.c
index 2f894f6bbf..c1d41c8484 100644
--- a/src/nan/nan_pairing.c
+++ b/src/nan/nan_pairing.c
@@ -190,7 +190,7 @@ int nan_pairing_abort(struct nan_data *nan_data, const u8 *peer_addr)
 			   wpabuf_len(extra_ies));
 	wpabuf_free(extra_ies);
 
-	nan_configure_peer_schedule(nan_data, peer, &nan_data->sched);
+	nan_configure_peer_schedule(nan_data, peer, &nan_data->sched, false);
 
 	ret = handle_auth_pasn_resp(peer->pairing.pasn, nan_data->cfg->nmi_addr,
 				    peer_addr, NULL,
@@ -800,7 +800,7 @@ int nan_pairing_initiate_pasn_auth(struct nan_data *nan_data, const u8 *addr,
 
 	/* Skip DW schedule negotiation for non-cluster (USD) peers. */
 	if (!nan_peer_no_shared_cluster(nan_data, peer->nmi_addr) &&
-	    nan_configure_peer_schedule(nan_data, peer, sched))
+	    nan_configure_peer_schedule(nan_data, peer, sched, false))
 		wpa_printf(MSG_DEBUG, "NAN: Could not configure peer schedule");
 	if (responder) {
 		if (peer->pairing.pending_auth1) {
diff --git a/wpa_supplicant/nan_supplicant.c b/wpa_supplicant/nan_supplicant.c
index bd7367d1b8..0b5148dff8 100644
--- a/wpa_supplicant/nan_supplicant.c
+++ b/wpa_supplicant/nan_supplicant.c
@@ -1034,6 +1034,8 @@ static int wpas_nan_transmit_followup_cb(void *ctx, const u8 *peer_nmi,
 		return -1;
 	wpa_printf(MSG_DEBUG,
 		   "NAN: Sending follow-up frame (peer_freq=%u)", peer_freq);
+
+	nan_setup_transient_peer(wpa_s->nan, peer_nmi);
 	return nan_de_transmit(wpa_s->nan_de, handle, NULL, NULL,
 			       peer_nmi, req_instance_id, attrs, NULL,
 			       peer_freq);
@@ -5371,6 +5373,9 @@ int wpas_nan_transmit(struct wpa_supplicant *wpa_s, int handle,
 {
 	if (!wpa_s->nan_de)
 		return -1;
+
+	nan_setup_transient_peer(wpa_s->nan, peer_addr);
+
 	return nan_de_transmit(wpa_s->nan_de, handle, ssi, elems, peer_addr,
 			       req_instance_id, NULL, cookie, 0);
 }
-- 
2.53.0




More information about the Hostap mailing list