[PATCH 16/18] NAN: Keep the NDC across local schedule updates

Andrei Otcheretianski andrei.otcheretianski at intel.com
Mon Oct 12 08:46:50 PDT 2026


Configuring new local schedule that doesn't include the NDC schedule
would break existing NDLs. Don't allow that.
A proper way to do that is by using 3-way NDL schedule update
negotiation, but this is not supported yet.

Signed-off-by: Andrei Otcheretianski <andrei.otcheretianski at intel.com>
---
 src/nan/nan.h                   |  2 +
 src/nan/nan_util.c              | 35 +++++++++++++
 wpa_supplicant/nan_supplicant.c | 93 +++++++++++++++++++++++++++------
 3 files changed, 113 insertions(+), 17 deletions(-)

diff --git a/src/nan/nan.h b/src/nan/nan.h
index d44d5f51ba..23a19b6254 100644
--- a/src/nan/nan.h
+++ b/src/nan/nan.h
@@ -960,6 +960,8 @@ bool nan_peer_sched_conflicts_with_ndc(struct nan_data *nan,
 				       const struct nan_peer_schedule *peer_sched,
 				       int freq,
 				       const struct nan_time_bitmap *ndc);
+bool nan_tbm_covers(struct nan_data *nan, const struct nan_time_bitmap *a,
+		    const struct nan_time_bitmap *b);
 bool nan_tbm_equal(struct nan_data *nan, const struct nan_time_bitmap *a,
 		   const struct nan_time_bitmap *b);
 void nan_write_ndc_attr(struct wpabuf *buf, const u8 *ndc_id, u8 map_id,
diff --git a/src/nan/nan_util.c b/src/nan/nan_util.c
index 77e01f6a5f..3ea284cbd1 100644
--- a/src/nan/nan_util.c
+++ b/src/nan/nan_util.c
@@ -1984,6 +1984,41 @@ bool nan_peer_sched_conflicts_with_ndc(struct nan_data *nan,
 }
 
 
+/**
+ * nan_tbm_covers - Check if a time bitmap covers all slots of another one
+ * @nan: Pointer to NAN data struct
+ * @a: The covering time bitmap
+ * @b: The time bitmap that has to be covered
+ * Returns: true if every slot set in @b is also set in @a
+ *
+ * Different duration, period, offset and length combinations can describe the
+ * same slots, so the comparison is done on the normalized bitmaps.
+ */
+bool nan_tbm_covers(struct nan_data *nan, const struct nan_time_bitmap *a,
+		    const struct nan_time_bitmap *b)
+{
+	struct bitfield *a_bf, *b_bf;
+	bool covers = false;
+
+	/* An empty bitmap is not a schedule, so never report it as a match */
+	if (!a->len || !b->len)
+		return false;
+
+	a_bf = nan_tbm_to_bf(nan, a);
+	b_bf = nan_tbm_to_bf(nan, b);
+
+	if (a_bf && b_bf)
+		covers = bitfield_is_subset(a_bf, b_bf) == 1;
+	else
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Failed to convert time bitmap for comparison");
+
+	bitfield_free(a_bf);
+	bitfield_free(b_bf);
+	return covers;
+}
+
+
 /**
  * nan_tbm_equal - Check if two time bitmaps describe the same slots
  * @nan: Pointer to NAN data struct
diff --git a/wpa_supplicant/nan_supplicant.c b/wpa_supplicant/nan_supplicant.c
index 46cae9df97..a74ad4ec9d 100644
--- a/wpa_supplicant/nan_supplicant.c
+++ b/wpa_supplicant/nan_supplicant.c
@@ -2617,6 +2617,62 @@ static void wpas_nan_update_local_schedule(struct wpa_supplicant *wpa_s)
 }
 
 
+/* The configured bitmaps use the slot duration and period of the local capa */
+static int wpas_nan_chan_to_tbm(struct wpa_supplicant *wpa_s,
+				const struct wpabuf *bitmap,
+				struct nan_time_bitmap *tbm)
+{
+	size_t len = wpabuf_len(bitmap);
+
+	if (len > NAN_TIME_BITMAP_MAX_LEN) {
+		wpa_printf(MSG_INFO,
+			   "NAN: Time bitmap length %zu exceeds maximum %d",
+			   len, NAN_TIME_BITMAP_MAX_LEN);
+		return -1;
+	}
+
+	os_memset(tbm, 0, sizeof(*tbm));
+	tbm->duration = wpa_s->nan_capa.slot_duration >> 5;
+	tbm->period = ffs(wpa_s->nan_capa.schedule_period) - 7;
+	tbm->offset = 0;
+	tbm->len = len;
+	os_memcpy(tbm->bitmap, wpabuf_head(bitmap), len);
+
+	return 0;
+}
+
+
+/*
+ * The NDC is the part of the schedule the connected peers rely on, so it must
+ * survive a local schedule update. Only a 3-way renegotiation, which is not
+ * supported, could move it.
+ */
+static bool wpas_nan_sched_covers_ndc(struct wpa_supplicant *wpa_s, u8 map_id,
+				      const struct nan_schedule_config *sched)
+{
+	int i;
+
+	if (!wpa_s->nan_ndc.valid || wpa_s->nan_ndc.map_id != map_id)
+		return true;
+
+	for (i = 0; i < sched->num_channels; i++) {
+		struct nan_time_bitmap tbm;
+
+		if (sched->channels[i].freq != wpa_s->nan_ndc.freq ||
+		    !sched->channels[i].time_bitmap)
+			continue;
+
+		if (wpas_nan_chan_to_tbm(wpa_s, sched->channels[i].time_bitmap,
+					 &tbm))
+			return false;
+
+		return nan_tbm_covers(wpa_s->nan, &tbm, &wpa_s->nan_ndc.tbm);
+	}
+
+	return false;
+}
+
+
 /* Parse format NAN_SCHED_CONFIG_MAP map_id=<id> [freq:bitmap_hex]..
  * If no bitmaps provided - clear the map */
 int wpas_nan_sched_config_map(struct wpa_supplicant *wpa_s, const char *cmd)
@@ -2677,6 +2733,14 @@ int wpas_nan_sched_config_map(struct wpa_supplicant *wpa_s, const char *cmd)
 	if (!pos) {
 		wpa_printf(MSG_INFO,
 			   "NAN: Missing freq:timebitmap pairs - cleanup schedule");
+
+		if (wpa_s->nan_ndc.valid && wpa_s->nan_ndc.map_id == map_id) {
+			wpa_printf(MSG_INFO,
+				   "NAN: Cannot clear map %u - it holds the established NDC",
+				   map_id);
+			return -1;
+		}
+
 		ret = wpa_drv_nan_config_schedule(wpa_s, map_id, sched_cfg);
 		if (!ret) {
 			clear_sched_config(&wpa_s->nan_sched[map_id - 1]);
@@ -2836,6 +2900,13 @@ int wpas_nan_sched_config_map(struct wpa_supplicant *wpa_s, const char *cmd)
 		}
 	}
 
+	if (!wpas_nan_sched_covers_ndc(wpa_s, map_id, sched_cfg)) {
+		wpa_printf(MSG_INFO,
+			   "NAN: New schedule for map %u doesn't cover the established NDC",
+			   map_id);
+		goto out;
+	}
+
 	sched_cfg->avail_attr = wpabuf_alloc(NAN_AVAIL_ATTR_MAX_LEN);
 	if (!sched_cfg->avail_attr) {
 		wpa_printf(MSG_INFO,
@@ -3000,9 +3071,7 @@ wpas_nan_fill_ndp_schedule_chan(struct wpa_supplicant *wpa_s,
 				const struct nan_schedule_channel *chan)
 {
 	struct nan_chan_schedule *chan_sched;
-	struct nan_time_bitmap *tbm;
-	const u8 *bitmap_data;
-	size_t bitmap_len;
+	struct nan_time_bitmap tbm, *dst;
 
 	/* None of these should happen */
 	if (!chan->time_bitmap) {
@@ -3012,14 +3081,8 @@ wpas_nan_fill_ndp_schedule_chan(struct wpa_supplicant *wpa_s,
 		return -1;
 	}
 
-	bitmap_len = wpabuf_len(chan->time_bitmap);
-	bitmap_data = wpabuf_head(chan->time_bitmap);
-	if (bitmap_len > NAN_TIME_BITMAP_MAX_LEN) {
-		wpa_printf(MSG_INFO,
-			   "NAN: Time bitmap length %zu exceeds maximum %d",
-			   bitmap_len, NAN_TIME_BITMAP_MAX_LEN);
+	if (wpas_nan_chan_to_tbm(wpa_s, chan->time_bitmap, &tbm))
 		return -1;
-	}
 
 	chan_sched = &sched->chans[sched->n_chans++];
 	chan_sched->map_id = map_id + 1;
@@ -3028,20 +3091,16 @@ wpas_nan_fill_ndp_schedule_chan(struct wpa_supplicant *wpa_s,
 	chan_sched->chan.center_freq2 = chan->center_freq2;
 	chan_sched->chan.bandwidth = chan->bandwidth;
 
-	tbm = &chan_sched->committed;
+	dst = &chan_sched->committed;
 #ifdef CONFIG_TESTING_OPTIONS
 	if (wpa_s->nan_force_conditional_sched) {
 		wpa_printf(MSG_DEBUG,
 			   "NAN: Using conditional TBM for schedule channel");
-		tbm = &chan_sched->conditional;
+		dst = &chan_sched->conditional;
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
 
-	tbm->duration = wpa_s->nan_capa.slot_duration >> 5;
-	tbm->period = ffs(wpa_s->nan_capa.schedule_period) - 7;
-	tbm->offset = 0;
-	tbm->len = bitmap_len;
-	os_memcpy(tbm->bitmap, bitmap_data, bitmap_len);
+	os_memcpy(dst, &tbm, sizeof(*dst));
 
 	wpa_printf(MSG_DEBUG,
 		   "NAN: NDP schedule channel added: map_id=%d freq=%d center_freq1=%d center_freq2=%d bandwidth=%d",
-- 
2.53.0




More information about the Hostap mailing list