[PATCH 08/18] NAN: Store and report NDCs advertised by peers

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


A peer may advertise NDCs it participates in outside of an NDL
negotiation, e.g. in a Publish SDF.

Parse and expose it in the peer schedule dump.

Signed-off-by: Andrei Otcheretianski <andrei.otcheretianski at intel.com>
---
 src/nan/nan.c      | 126 +++++++++++++++++++++++++++++++++++++++++----
 src/nan/nan.h      |  11 +++-
 src/nan/nan_i.h    |  17 ++++++
 src/nan/nan_util.c |  53 +++++++++----------
 4 files changed, 167 insertions(+), 40 deletions(-)

diff --git a/src/nan/nan.c b/src/nan/nan.c
index ef35646782..929e8ea7cf 100644
--- a/src/nan/nan.c
+++ b/src/nan/nan.c
@@ -129,6 +129,18 @@ static void nan_peer_flush_ulw(struct nan_peer_info *info)
 }
 
 
+static void nan_peer_flush_ndc(struct nan_peer_info *info)
+{
+	struct nan_ndc_entry *cur, *next;
+
+	dl_list_for_each_safe(cur, next, &info->ndc,
+			      struct nan_ndc_entry, list) {
+		dl_list_del(&cur->list);
+		os_free(cur);
+	}
+}
+
+
 static struct wpabuf *
 nan_peer_build_ulw_attrs(const struct nan_peer_info *info)
 {
@@ -265,6 +277,7 @@ static void nan_del_peer(struct nan_data *nan, struct nan_peer *peer)
 	nan_peer_flush_ulw(&peer->info);
 	nan_peer_flush_dev_capa(&peer->info);
 	nan_peer_flush_elem_container(&peer->info);
+	nan_peer_flush_ndc(&peer->info);
 	nan_remove_group_keys(nan, peer);
 
 	nan_ndl_reset(nan, peer);
@@ -1108,6 +1121,7 @@ static void nan_merge_peer_info(struct nan_data *nan, struct nan_peer *peer,
 				struct nan_peer_info *old,
 				struct nan_peer_info *new)
 {
+	struct nan_ndc_entry *ndc, *tmp_ndc;
 	bool schedule_changed = false;
 
 	if (!dl_list_empty(&new->avail_entries)) {
@@ -1142,6 +1156,15 @@ static void nan_merge_peer_info(struct nan_data *nan, struct nan_peer *peer,
 
 	old->last_seen = new->last_seen;
 
+	/* NDCs the peer only advertises are informational, so just store them */
+	nan_peer_flush_ndc(old);
+
+	dl_list_for_each_safe(ndc, tmp_ndc, &new->ndc,
+			      struct nan_ndc_entry, list) {
+		dl_list_del(&ndc->list);
+		dl_list_add(&old->ndc, &ndc->list);
+	}
+
 	if (!schedule_changed)
 		return;
 
@@ -1367,6 +1390,51 @@ static int nan_parse_peer_ulw(const struct nan_attrs *attrs,
 }
 
 
+/*
+ * Selected NDCs are tracked via NDL negotiation instead; this is only for
+ * NDCs a peer merely advertises, e.g. in a Publish SDF.
+ */
+static int nan_parse_peer_ndc(const struct nan_attrs *attrs,
+			      struct nan_peer_info *info)
+{
+	const struct nan_attrs_entry *attr;
+
+	dl_list_for_each(attr, &attrs->ndc, struct nan_attrs_entry, list) {
+		const struct ieee80211_ndc *ndc_attr;
+		struct nan_ndc_entry *entry;
+		u16 sched_len;
+
+		if (attr->len < sizeof(*ndc_attr))
+			continue;
+
+		ndc_attr = (const struct ieee80211_ndc *) attr->ptr;
+
+		if (ndc_attr->ctrl & NAN_NDC_CTRL_SELECTED)
+			continue;
+
+		sched_len = attr->len - sizeof(*ndc_attr);
+		if (sched_len <= sizeof(struct nan_sched_entry))
+			continue;
+
+		entry = os_zalloc(sizeof(*entry) + sched_len);
+		if (!entry) {
+			wpa_printf(MSG_INFO,
+				   "NAN: Failed to allocate NDC entry");
+			nan_peer_flush_ndc(info);
+			return -1;
+		}
+
+		dl_list_init(&entry->list);
+		dl_list_add(&info->ndc, &entry->list);
+		os_memcpy(entry->ndc_id, ndc_attr->ndc_id, ETH_ALEN);
+		entry->len = sched_len;
+		os_memcpy(entry->sched, ndc_attr + 1, sched_len);
+	}
+
+	return 0;
+}
+
+
 void nan_parse_peer_dev_capa_ext(struct nan_data *nan, struct nan_peer *peer,
 				 struct nan_attrs *attrs)
 {
@@ -1493,6 +1561,7 @@ int nan_parse_device_attrs(struct nan_data *nan, struct nan_peer *peer,
 	os_memset(&info, 0, sizeof(info));
 	dl_list_init(&info.avail_entries);
 	dl_list_init(&info.ulw);
+	dl_list_init(&info.ndc);
 	os_get_reltime(&info.last_seen);
 
 	if (nan_parse_attrs(nan, attrs_data, attrs_len, &attrs)) {
@@ -1512,6 +1581,11 @@ int nan_parse_device_attrs(struct nan_data *nan, struct nan_peer *peer,
 		goto out;
 	}
 
+	if (nan_parse_peer_ndc(&attrs, &info)) {
+		ret = -1;
+		goto out;
+	}
+
 	nan_merge_peer_info(nan, peer, &peer->info, &info);
 	if (attrs.conn_capa_valid) {
 		peer->info.conn_capa = attrs.conn_capa;
@@ -1535,6 +1609,7 @@ int nan_parse_device_attrs(struct nan_data *nan, struct nan_peer *peer,
 out:
 	nan_peer_flush_avail(&info);
 	nan_peer_flush_ulw(&info);
+	nan_peer_flush_ndc(&info);
 	nan_attrs_clear(nan, &attrs);
 	return ret;
 }
@@ -1580,6 +1655,7 @@ static struct nan_peer * nan_alloc_peer(struct nan_data *nan)
 	dl_list_init(&peer->info.dev_capa);
 	dl_list_init(&peer->info.element_container);
 	dl_list_init(&peer->info.sec);
+	dl_list_init(&peer->info.ndc);
 
 	dl_list_add(&nan->peer_list, &peer->list);
 	dl_list_init(&peer->ndps);
@@ -3326,10 +3402,16 @@ nan_peer_get_committed_avail(const struct nan_data *nan,
 }
 
 
+enum nan_map_sched_dest {
+	NAN_MAP_SCHED_NDC,
+	NAN_MAP_SCHED_NDC_ADVERTISED,
+	NAN_MAP_SCHED_IMMUTABLE,
+};
+
 static void nan_peer_set_sched(struct nan_data *nan, struct nan_peer *peer,
 			       struct nan_peer_schedule *sched,
 			       const u8 *sched_buf, size_t sched_buf_len,
-			       bool ndc)
+			       enum nan_map_sched_dest dest, const u8 *ndc_id)
 {
 	struct dl_list sched_entries;
 	struct nan_avail_entry *cur;
@@ -3378,10 +3460,29 @@ static void nan_peer_set_sched(struct nan_data *nan, struct nan_peer *peer,
 			continue;
 		}
 
-		if (ndc)
+		switch (dest) {
+		case NAN_MAP_SCHED_NDC:
 			os_memcpy(&map->ndc, &cur->tbm, sizeof(cur->tbm));
-		else
+			break;
+		case NAN_MAP_SCHED_IMMUTABLE:
 			os_memcpy(&map->immutable, &cur->tbm, sizeof(cur->tbm));
+			break;
+		case NAN_MAP_SCHED_NDC_ADVERTISED: {
+			struct nan_advertised_ndc *adv;
+
+			if (map->n_ndc_advertised >= NAN_MAX_NDC_ADVERTISED) {
+				wpa_printf(MSG_DEBUG,
+					   "NAN: Too many advertised NDCs for map_id=%u",
+					   map->map_id);
+				break;
+			}
+
+			adv = &map->ndc_advertised[map->n_ndc_advertised++];
+			os_memcpy(adv->id, ndc_id, ETH_ALEN);
+			os_memcpy(&adv->tbm, &cur->tbm, sizeof(cur->tbm));
+			break;
+		}
+		}
 	}
 
 	nan_flush_avail_entries(&sched_entries);
@@ -3392,12 +3493,18 @@ static void nan_peer_get_ndc_sched(struct nan_data *nan,
 				   struct nan_peer *peer,
 				   struct nan_peer_schedule *sched)
 {
-	if (!peer->ndl)
-		return;
+	struct nan_ndc_entry *entry;
 
-	nan_peer_set_sched(nan, peer, sched,
-			   peer->ndl->ndc_sched,
-			   peer->ndl->ndc_sched_len, true);
+	if (peer->ndl)
+		nan_peer_set_sched(nan, peer, sched,
+				   peer->ndl->ndc_sched,
+				   peer->ndl->ndc_sched_len,
+				   NAN_MAP_SCHED_NDC, NULL);
+
+	/* Also dump any NDCs the peer merely advertises */
+	dl_list_for_each(entry, &peer->info.ndc, struct nan_ndc_entry, list)
+		nan_peer_set_sched(nan, peer, sched, entry->sched, entry->len,
+				   NAN_MAP_SCHED_NDC_ADVERTISED, entry->ndc_id);
 }
 
 
@@ -3410,7 +3517,8 @@ static void nan_peer_get_immut_sched(struct nan_data *nan,
 
 	nan_peer_set_sched(nan, peer, sched,
 			   peer->ndl->immut_sched,
-			   peer->ndl->immut_sched_len, false);
+			   peer->ndl->immut_sched_len,
+			   NAN_MAP_SCHED_IMMUTABLE, NULL);
 }
 
 
diff --git a/src/nan/nan.h b/src/nan/nan.h
index 088f98019c..36e7a2ddd7 100644
--- a/src/nan/nan.h
+++ b/src/nan/nan.h
@@ -374,6 +374,7 @@ struct nan_ndp_action_notif_params {
 
 #define NAN_MAX_MAPS 8
 #define NAN_MAX_CHAN_ENTRIES 16
+#define NAN_MAX_NDC_ADVERTISED 4
 
 /**
  * struct nan_peer_schedule - NAN peer schedule information
@@ -387,7 +388,10 @@ struct nan_ndp_action_notif_params {
  *     channel
  * @chan: Channel information
  * @tbm: Time bitmap for the channel
- * @ndc: NDC time bitmap for the map
+ * @ndc: Negotiated NDC time bitmap for the map, if any
+ * @n_ndc_advertised: Number of entries in @ndc_advertised
+ * @ndc_advertised: NDCs the peer merely advertised for the map, each with its
+ *	ID and time bitmap
  * @immutable: Immutable time bitmap for the map
  * @max_idle_period: Maximal NDL idle period in seconds that the peer indicated
  */
@@ -404,6 +408,11 @@ struct nan_peer_schedule {
 		} chans[NAN_MAX_CHAN_ENTRIES];
 
 		struct nan_time_bitmap ndc;
+		u8 n_ndc_advertised;
+		struct nan_advertised_ndc {
+			u8 id[ETH_ALEN];
+			struct nan_time_bitmap tbm;
+		} ndc_advertised[NAN_MAX_NDC_ADVERTISED];
 		struct nan_time_bitmap immutable;
 	} maps[NAN_MAX_MAPS];
 
diff --git a/src/nan/nan_i.h b/src/nan/nan_i.h
index 09f33db23b..2fa012d826 100644
--- a/src/nan/nan_i.h
+++ b/src/nan/nan_i.h
@@ -305,6 +305,20 @@ struct nan_ulw_entry {
 	u8 data[];
 };
 
+/**
+ * struct nan_ndc_entry - A NDC a peer merely advertised, e.g. in a Publish SDF
+ * @list: Linked in struct nan_peer_info::ndc
+ * @ndc_id: NDC ID, for information only
+ * @len: Length of @sched
+ * @sched: Schedule entries payload
+ */
+struct nan_ndc_entry {
+	struct dl_list list;
+	u8 ndc_id[ETH_ALEN];
+	u16 len;
+	u8 sched[];
+};
+
 /**
  * struct nan_peer_sec_info_entry - NAN peer security information entry
  *
@@ -350,6 +364,8 @@ struct nan_peer_sec_info_entry {
  *	(struct nan_elem_container_entry::list entries)
  * @sec: List of security information entries of the peer
  *	(struct nan_peer_sec_info_entry::list entries)
+ * @ndc: List of NDC attributes seen in the peer's SDFs
+ *	(struct nan_ndc_entry::list entries)
  */
 struct nan_peer_info {
 	struct os_reltime last_seen;
@@ -361,6 +377,7 @@ struct nan_peer_info {
 	struct dl_list dev_capa;
 	struct dl_list element_container;
 	struct dl_list sec;
+	struct dl_list ndc;
 };
 
 /**
diff --git a/src/nan/nan_util.c b/src/nan/nan_util.c
index 954c3ade00..e04325e469 100644
--- a/src/nan/nan_util.c
+++ b/src/nan/nan_util.c
@@ -1697,6 +1697,19 @@ fail:
 }
 
 
+/* Dump a labeled time bitmap of a map, advancing the output position */
+static void nan_dump_tbm_to_buf(char **pos, char *end, const char *label,
+				const struct nan_time_bitmap *tbm)
+{
+	*pos += wpa_scnprintf(*pos, end - *pos,
+			      "\t%s: period=%u duration=%u offset=%u bitmap=",
+			      label, BIT(6 + tbm->period),
+			      BIT(4 + tbm->duration), 16 * tbm->offset);
+	*pos += wpa_snprintf_hex(*pos, end - *pos, tbm->bitmap, tbm->len);
+	*pos += wpa_scnprintf(*pos, end - *pos, "\n");
+}
+
+
 /**
  * nan_peer_dump_sched_to_buf - Dump peer schedule to a buffer
  * @sched: Peer schedule
@@ -1763,40 +1776,20 @@ int nan_peer_dump_sched_to_buf(struct nan_peer_schedule *sched,
 			pos += ret;
 		}
 
-		ret = wpa_scnprintf(pos, end - pos,
-				    "\tndc: period=%u duration=%u offset=%u bitmap=",
-				    BIT(6 + map->ndc.period),
-				    BIT(4 + map->ndc.duration),
-				    16 * map->ndc.offset);
-		if (os_snprintf_error(end - pos, ret))
-			goto err;
-		pos += ret;
+		nan_dump_tbm_to_buf(&pos, end, "ndc", &map->ndc);
 
-		ret = wpa_snprintf_hex(pos, end - pos, map->ndc.bitmap,
-				       map->ndc.len);
-		if (os_snprintf_error(end - pos, ret))
-			goto err;
-		pos += ret;
+		for (j = 0; j < map->n_ndc_advertised; j++) {
+			char label[64];
 
-		ret = wpa_scnprintf(pos, end - pos,
-				    "\n\timmutable: period=%u duration=%u offset=%u bitmap=",
-				    1 << (6 + map->immutable.period),
-				    1 << (4 + map->immutable.duration),
-				    16 * map->immutable.offset);
-		if (os_snprintf_error(end - pos, ret))
-			goto err;
-		pos += ret;
+			os_snprintf(label, sizeof(label),
+				    "ndc_advertised[%u] id=" MACSTR, j,
+				    MAC2STR(map->ndc_advertised[j].id));
 
-		ret = wpa_snprintf_hex(pos, end - pos, map->immutable.bitmap,
-				       map->immutable.len);
-		if (os_snprintf_error(end - pos, ret))
-			goto err;
-		pos += ret;
+			nan_dump_tbm_to_buf(&pos, end, label,
+					    &map->ndc_advertised[j].tbm);
+		}
 
-		ret = wpa_scnprintf(pos, end - pos, "\n");
-		if (os_snprintf_error(end - pos, ret))
-			goto err;
-		pos += ret;
+		nan_dump_tbm_to_buf(&pos, end, "immutable", &map->immutable);
 	}
 
 	ret = wpa_scnprintf(pos, end - pos, "max_idle_period=%u",
-- 
2.53.0




More information about the Hostap mailing list