[PATCH 09/18] NAN: Track the NDC established for the data path
Andrei Otcheretianski
andrei.otcheretianski at intel.com
Mon Oct 12 08:46:43 PDT 2026
The NDC that the first NDP settles on is a property of the device
rather than of a single NDL, but nothing kept track of it. Remember it
in wpa_s->nan_ndc once an NDP connects and forget it once no NDP is
left, so that a new one can be established from scratch.
The NDC ID is now picked by the caller and passed down through the
schedule instead of being generated in the NDL code, as the
identity of the NDC has to outlive the NDL that created it.
Adjust NAN module tests accordingly.
Report the tracked NDC in NAN_STATUS.
Signed-off-by: Andrei Otcheretianski <andrei.otcheretianski at intel.com>
---
src/nan/nan.c | 23 +++++
src/nan/nan.h | 4 +
src/nan/nan_module_test_cases.c | 4 +
src/nan/nan_ndl.c | 17 +++-
wpa_supplicant/nan_supplicant.c | 164 ++++++++++++++++++++++++++++--
wpa_supplicant/wpa_supplicant_i.h | 13 +++
6 files changed, 212 insertions(+), 13 deletions(-)
diff --git a/src/nan/nan.c b/src/nan/nan.c
index 929e8ea7cf..b4a23cdf97 100644
--- a/src/nan/nan.c
+++ b/src/nan/nan.c
@@ -3555,6 +3555,29 @@ int nan_peer_get_schedule_info(struct nan_data *nan, const u8 *addr,
}
+/**
+ * nan_peer_get_ndc_id - Get the NDC ID established for a peer's NDL
+ * @nan: NAN module context from nan_init()
+ * @addr: NAN MAC address of the peer
+ * @ndc_id: On success, would hold the NDC ID
+ * Returns: 0 on success; -1 if the peer or its NDL/NDC ID is not found
+ */
+int nan_peer_get_ndc_id(struct nan_data *nan, const u8 *addr, u8 *ndc_id)
+{
+ struct nan_peer *peer;
+
+ if (!nan || !addr || !ndc_id)
+ return -1;
+
+ peer = nan_get_peer(nan, addr);
+ if (!peer || !peer->ndl || is_zero_ether_addr(peer->ndl->ndc_id))
+ return -1;
+
+ os_memcpy(ndc_id, peer->ndl->ndc_id, ETH_ALEN);
+ return 0;
+}
+
+
/*
* nan_peer_get_pot_avail - Get peer's potential availability entries
* @nan: NAN module context from nan_init()
diff --git a/src/nan/nan.h b/src/nan/nan.h
index 36e7a2ddd7..5245a9baaa 100644
--- a/src/nan/nan.h
+++ b/src/nan/nan.h
@@ -188,6 +188,8 @@ struct nan_sched_qos {
* order.
* @ndc: NDC bitmap schedule
* @ndc_map_id: The NDC map ID
+ * @ndc_id: NDC ID to use for the schedule above; must be set by the caller
+ * whenever @ndc is not empty
* @sequence_id: Schedule sequence ID
* @elems: Additional elements to be set in an element container attribute
*/
@@ -197,6 +199,7 @@ struct nan_schedule {
struct nan_chan_schedule chans[NAN_SCHEDULE_MAX_CHANNELS];
struct nan_time_bitmap ndc;
u8 ndc_map_id;
+ u8 ndc_id[ETH_ALEN];
u8 sequence_id;
struct wpabuf *elems;
};
@@ -927,6 +930,7 @@ int nan_peer_get_conn_capa(struct nan_data *nan, const u8 *addr,
u16 *capa, bool *valid);
int nan_peer_get_schedule_info(struct nan_data *nan, const u8 *addr,
struct nan_peer_schedule *sched);
+int nan_peer_get_ndc_id(struct nan_data *nan, const u8 *addr, u8 *ndc_id);
int nan_peer_dump_sched_to_buf(struct nan_peer_schedule *sched,
char *buf, size_t buflen);
int nan_peer_get_pot_avail(struct nan_data *nan, const u8 *addr,
diff --git a/src/nan/nan_module_test_cases.c b/src/nan/nan_module_test_cases.c
index 943fb829d0..5a7bab61c7 100644
--- a/src/nan/nan_module_test_cases.c
+++ b/src/nan/nan_module_test_cases.c
@@ -13,6 +13,9 @@
#include "nan_module_tests.h"
static u8 default_bitmap[] = { 0xfe, 0xff, 0xfe, 0xff };
+static const u8 default_ndc_id[ETH_ALEN] = {
+ 0x02, 0x11, 0x22, 0x33, 0x44, 0x55
+};
static void nan_test_schedule_cb(struct nan_schedule *sched, bool ndc,
int freq, int center_freq1, int center_freq2,
@@ -47,6 +50,7 @@ static void nan_test_schedule_cb(struct nan_schedule *sched, bool ndc,
sched->ndc.offset = 0;
sched->ndc.len = 1;
sched->ndc.bitmap[0] = 0xfe;
+ os_memcpy(sched->ndc_id, default_ndc_id, ETH_ALEN);
}
diff --git a/src/nan/nan_ndl.c b/src/nan/nan_ndl.c
index b652279047..410b744476 100644
--- a/src/nan/nan_ndl.c
+++ b/src/nan/nan_ndl.c
@@ -799,14 +799,18 @@ int nan_ndl_setup(struct nan_data *nan, struct nan_peer *peer,
nan_ndl_sched_print(nan, &nan->sched);
+ /* The NDC ID is selected by the caller, not generated here */
if (is_zero_ether_addr(ndl->ndc_id)) {
- if (os_get_random(ndl->ndc_id, ETH_ALEN) < 0) {
+ if (!is_zero_ether_addr(nan->sched.ndc_id)) {
+ os_memcpy(ndl->ndc_id, nan->sched.ndc_id, ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "NAN: NDL: using NDC ID " MACSTR,
+ MAC2STR(ndl->ndc_id));
+ } else if (nan->sched.ndc.len) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: Missing NDC ID for the NDC");
reason = NAN_REASON_UNSPECIFIED_REASON;
goto out_fail;
}
- wpa_printf(MSG_DEBUG,
- "NAN: NDL: generated NDC ID " MACSTR,
- MAC2STR(ndl->ndc_id));
}
os_memcpy(&ndl->local_ndc, &nan->sched.ndc, sizeof(ndl->local_ndc));
@@ -1782,6 +1786,11 @@ int nan_ndl_add_ndc_attr(struct nan_data *nan, const struct nan_peer *peer,
return 0;
}
+ if (is_zero_ether_addr(ndl->ndc_id)) {
+ wpa_printf(MSG_DEBUG, "NAN: NDL: No NDC ID for the NDC");
+ return -1;
+ }
+
nan_write_ndc_attr(buf, ndl->ndc_id, sched->ndc_map_id, &sched->ndc,
true);
diff --git a/wpa_supplicant/nan_supplicant.c b/wpa_supplicant/nan_supplicant.c
index 6758f58c5d..e19b0413d9 100644
--- a/wpa_supplicant/nan_supplicant.c
+++ b/wpa_supplicant/nan_supplicant.c
@@ -181,6 +181,13 @@ static void clear_sched_config(struct nan_schedule_config *sched_cfg)
}
+static void wpas_nan_release_ndc(struct wpa_supplicant *wpa_s)
+{
+ /* Also drops the state of an NDC that was selected but never used */
+ os_memset(&wpa_s->nan_ndc, 0, sizeof(wpa_s->nan_ndc));
+}
+
+
static void wpas_nan_sched_update_done_timeout(void *eloop_ctx,
void *timeout_ctx)
{
@@ -201,6 +208,8 @@ static void wpas_nan_stop_cb(void *ctx)
clear_sched_config(&wpa_s->nan_sched_update.sched);
nan_set_sched_update_pending(wpa_s->nan, false);
+ wpas_nan_release_ndc(wpa_s);
+
for (i = 0; i < MAX_NAN_RADIOS; i++) {
if (wpa_s->nan_sched[i].num_channels) {
wpa_drv_nan_config_schedule(wpa_s, i + 1, NULL);
@@ -768,6 +777,82 @@ static void wpas_nan_remove_ndi_sta(struct wpa_supplicant *wpa_s,
}
+static int wpas_nan_get_ndc_map_id(struct wpa_supplicant *wpa_s,
+ const struct nan_peer_schedule *peer_sched,
+ u8 peer_map_id);
+
+static void wpas_nan_store_peer_ndc(struct wpa_supplicant *wpa_s,
+ const u8 *peer_nmi)
+{
+ struct nan_peer_schedule peer_sched;
+ u8 ndc_id[ETH_ALEN];
+ u8 map_id;
+ int freq, local_map_id;
+
+ if (wpa_s->nan_ndc.valid) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: An NDC is already established. Keep using it");
+ return;
+ }
+
+ if (nan_peer_get_schedule_info(wpa_s->nan, peer_nmi, &peer_sched))
+ return;
+
+ for (map_id = 0; map_id < peer_sched.n_maps; map_id++) {
+ if (!peer_sched.maps[map_id].ndc.len)
+ continue;
+
+ freq = nan_get_peer_ndc_freq(wpa_s->nan, &peer_sched, map_id);
+ if (freq < 0)
+ continue;
+
+ local_map_id = wpas_nan_get_ndc_map_id(wpa_s, &peer_sched,
+ map_id);
+ if (local_map_id < 0)
+ continue;
+
+ /* Without the ID the NDC can't be proposed to other peers */
+ if (nan_peer_get_ndc_id(wpa_s->nan, peer_nmi, ndc_id)) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: Failed to get established NDC ID");
+ return;
+ }
+
+ os_memset(&wpa_s->nan_ndc, 0, sizeof(wpa_s->nan_ndc));
+ os_memcpy(wpa_s->nan_ndc.id, ndc_id, ETH_ALEN);
+ wpa_s->nan_ndc.map_id = local_map_id;
+ wpa_s->nan_ndc.freq = freq;
+ os_memcpy(&wpa_s->nan_ndc.tbm, &peer_sched.maps[map_id].ndc,
+ sizeof(wpa_s->nan_ndc.tbm));
+ wpa_s->nan_ndc.valid = true;
+
+ wpa_printf(MSG_DEBUG,
+ "NAN: Established single NDC: id=" MACSTR
+ " map_id=%u freq=%d MHz",
+ MAC2STR(wpa_s->nan_ndc.id), local_map_id, freq);
+
+ return;
+ }
+
+ /* We may have selected this NDC ourselves; just confirm it now */
+ if (!ether_addr_equal(wpa_s->nan_ndc.pending_peer_nmi, peer_nmi)) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: No NDC found for connected peer " MACSTR,
+ MAC2STR(peer_nmi));
+ return;
+ }
+
+ wpa_s->nan_ndc.valid = true;
+ os_memset(wpa_s->nan_ndc.pending_peer_nmi, 0, ETH_ALEN);
+
+ wpa_printf(MSG_DEBUG,
+ "NAN: Established single NDC (locally selected): id="
+ MACSTR " map_id=%u freq=%d MHz",
+ MAC2STR(wpa_s->nan_ndc.id), wpa_s->nan_ndc.map_id,
+ wpa_s->nan_ndc.freq);
+}
+
+
static int wpas_nan_ndp_connected_cb(void *ctx,
struct nan_ndp_connection_params *params)
{
@@ -787,7 +872,9 @@ static int wpas_nan_ndp_connected_cb(void *ctx,
params->ssi, params->ssi_len,
params->interface_id);
- return 0;
+ wpas_nan_store_peer_ndc(wpa_s, params->ndp_id.peer_nmi);
+
+ return 0;
}
@@ -805,6 +892,9 @@ static void wpas_nan_ndp_disconnected_cb(void *ctx, struct nan_ndp_id *ndp_id,
wpas_notify_nan_ndp_disconnected(wpa_s, ndp_id->peer_nmi,
ndp_id->id, local_ndi, peer_ndi,
reason, locally_generated, failure);
+
+ if (!nan_has_active_ndp(wpa_s->nan))
+ wpas_nan_release_ndc(wpa_s);
}
@@ -1573,6 +1663,8 @@ int wpas_nan_init(struct wpa_supplicant *wpa_s)
return -1;
}
+ wpas_nan_release_ndc(wpa_s);
+
/* Set the default configuration */
os_memset(&wpa_s->nan_cluster_config, 0,
sizeof(wpa_s->nan_cluster_config));
@@ -1632,6 +1724,8 @@ void wpas_nan_deinit(struct wpa_supplicant *wpa_s)
for (i = 0; i < MAX_NAN_RADIOS; i++)
clear_sched_config(&wpa_s->nan_sched[i]);
+ wpas_nan_release_ndc(wpa_s);
+
nan_deinit(wpa_s->nan);
os_free(wpa_s->nan_disallowed_freqs.range);
os_memset(&wpa_s->nan_disallowed_freqs, 0,
@@ -3002,6 +3096,7 @@ static int wpas_nan_select_ndc(struct wpa_supplicant *wpa_s,
struct nan_ndp_params *ndp)
{
struct nan_time_bitmap *tbm;
+ bool slot_selected = false;
int i;
/* NDC attribute in request is optional, let the peer decide */
@@ -3026,6 +3121,15 @@ static int wpas_nan_select_ndc(struct wpa_supplicant *wpa_s,
os_memset(ndp->sched.ndc.bitmap, 0, sizeof(ndp->sched.ndc.bitmap));
ndp->sched.ndc_map_id = ndp->sched.chans[0].map_id;
+ /*
+ * Generate the new NDC's ID now, while we know everything else
+ * about it
+ */
+ if (os_get_random(ndp->sched.ndc_id, sizeof(ndp->sched.ndc_id)) < 0) {
+ wpa_printf(MSG_INFO, "NAN: Failed to generate NDC ID");
+ return -1;
+ }
+
/*
* For default NDC channels (6, 149, 44) take the first slot after DW.
* Note that if the slot duration is 16 TUs we need to select the next
@@ -3043,25 +3147,42 @@ static int wpas_nan_select_ndc(struct wpa_supplicant *wpa_s,
if (tbm->bitmap[byte_idx] & BIT(bit_in_byte)) {
ndp->sched.ndc.bitmap[byte_idx] = BIT(bit_in_byte);
- return 0;
+ slot_selected = true;
}
} else if (ndp->sched.chans[0].chan.freq == 2437 &&
wpa_s->nan_capa.slot_duration == 16) {
if (tbm->bitmap[0] & 0x02) {
ndp->sched.ndc.bitmap[0] = 0x02;
- return 0;
+ slot_selected = true;
}
}
- /* For other cases, select the first available slot */
- for (i = 0; i < NAN_TIME_BITMAP_MAX_LEN; i++) {
- if (tbm->bitmap[i]) {
- ndp->sched.ndc.bitmap[i] =
- tbm->bitmap[i] & (~tbm->bitmap[i] + 1);
- break;
+ /* Otherwise, select the first available slot */
+ if (!slot_selected) {
+ for (i = 0; i < NAN_TIME_BITMAP_MAX_LEN; i++) {
+ if (tbm->bitmap[i]) {
+ ndp->sched.ndc.bitmap[i] =
+ tbm->bitmap[i] & (~tbm->bitmap[i] + 1);
+ slot_selected = true;
+ break;
+ }
}
}
+ if (!slot_selected) {
+ wpa_printf(MSG_INFO, "NAN: No slot available for a new NDC");
+ return -1;
+ }
+
+ /* nan_ndc.valid stays false until the NDP actually connects */
+ os_memcpy(wpa_s->nan_ndc.pending_peer_nmi, ndp->ndp_id.peer_nmi,
+ ETH_ALEN);
+ os_memcpy(wpa_s->nan_ndc.id, ndp->sched.ndc_id, ETH_ALEN);
+ wpa_s->nan_ndc.map_id = ndp->sched.ndc_map_id;
+ wpa_s->nan_ndc.freq = ndp->sched.chans[0].chan.freq;
+ os_memcpy(&wpa_s->nan_ndc.tbm, &ndp->sched.ndc,
+ sizeof(wpa_s->nan_ndc.tbm));
+
return 0;
}
@@ -3839,6 +3960,31 @@ int wpas_nan_status(struct wpa_supplicant *wpa_s, char *reply,
ret = nan_de_get_status(wpa_s->nan_de, pos, end - pos);
if (ret > 0)
pos += ret;
+
+ pos += wpa_scnprintf(pos, end - pos, "nan_ndc_valid=%d\n",
+ wpa_s->nan_ndc.valid);
+
+ if (wpa_s->nan_ndc.valid) {
+ const struct nan_time_bitmap *tbm = &wpa_s->nan_ndc.tbm;
+
+ pos += wpa_scnprintf(pos, end - pos,
+ "nan_ndc_id=" MACSTR "\n"
+ "nan_ndc_freq=%d\n"
+ "nan_ndc_map_id=%u\n"
+ "nan_ndc_tbm_duration=%u\n"
+ "nan_ndc_tbm_period=%u\n"
+ "nan_ndc_tbm_offset=%u\n"
+ "nan_ndc_tbm_bitmap=",
+ MAC2STR(wpa_s->nan_ndc.id),
+ wpa_s->nan_ndc.freq,
+ wpa_s->nan_ndc.map_id,
+ BIT(4 + tbm->duration),
+ tbm->period ? BIT(6 + tbm->period) : 0,
+ 16 * tbm->offset);
+ pos += wpa_snprintf_hex(pos, end - pos, tbm->bitmap, tbm->len);
+ pos += wpa_scnprintf(pos, end - pos, "\n");
+ }
+
return pos - reply;
}
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 4f30cff1c9..8021a62b29 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -1795,6 +1795,19 @@ struct wpa_supplicant {
struct nan_channels nan_override_potential_avail;
unsigned int nan_ndi_ndp_refcount; /* Active NDP count on this NDI */
struct nan_gtk ndi_gtk;
+ /*
+ * Single NDC shared by all peers. The fields are set when the NDC is
+ * selected or adopted, and @valid is set once an NDP using it connects.
+ */
+ struct {
+ bool valid;
+ u8 id[ETH_ALEN];
+ u8 map_id;
+ int freq;
+ struct nan_time_bitmap tbm;
+ /* Peer of a locally selected NDC that didn't connect yet */
+ u8 pending_peer_nmi[ETH_ALEN];
+ } nan_ndc;
#ifdef CONFIG_TESTING_OPTIONS
bool nan_force_conditional_sched;
#endif /* CONFIG_TESTING_OPTIONS */
--
2.53.0
More information about the Hostap
mailing list