[RFC v2 20/99] NAN: Add support for adding availability attributes

Andrei Otcheretianski andrei.otcheretianski at intel.com
Tue Dec 23 03:51:24 PST 2025


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

Add support for constructing NAN availability attributes
from the NAN schedule.
---
 src/nan/nan_i.h    |  17 +++
 src/nan/nan_ndl.c  |  59 +++++++++++
 src/nan/nan_util.c | 253 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 329 insertions(+)

diff --git a/src/nan/nan_i.h b/src/nan/nan_i.h
index a01d318fe7..5c48865b1b 100644
--- a/src/nan/nan_i.h
+++ b/src/nan/nan_i.h
@@ -16,6 +16,8 @@
 
 struct nan_config;
 
+#define NAN_INVALID_MAP_ID 0xff
+
 /*
  * enum nan_ndp_state - State of NDP establishment
  * @NAN_NDP_STATE_NONE: No NDP establishment in progress.
@@ -124,6 +126,11 @@ enum nan_band_chan_type {
 	NAN_TYPE_CHANNEL,
 };
 
+/* Default availability entry parameter values */
+#define NAN_AVAIL_ENTRY_DEF_UTIL NAN_AVAIL_ENTRY_CTRL_UTIL_UNKNOWN
+#define NAN_AVAIL_ENTRY_DEF_NSS  2
+#define NAN_AVAIL_ENTRY_DEF_PREF 3
+
 /*
  * struct nan_avail_entry - NAN availability entry
  *
@@ -414,4 +421,14 @@ int nan_chan_to_chan_idx_map(struct nan_data *nan,
 			     u8 op_class, u8 channel, u16 *chan_idx_map);
 int nan_ndl_naf_sent(struct nan_data *nan, struct nan_peer *peer,
 		     enum nan_subtype subtype);
+int nan_ndl_add_avail_attrs(struct nan_data *nan,
+			    const struct nan_peer *peer,
+			    struct wpabuf *buf);
+int nan_get_chan_bm(struct nan_data *nan, struct nan_sched_chan *chan,
+		    u8 *op_class, u16 *chan_bm, u16 *pri_chan_bm);
+int nan_add_avail_attrs(struct nan_data *nan, u8 sequence_id,
+			u32 map_ids_bitmap,
+			u8 type_for_conditional,
+			size_t n_chans, struct nan_chan_schedule *chans,
+			struct wpabuf *buf);
 #endif
diff --git a/src/nan/nan_ndl.c b/src/nan/nan_ndl.c
index e3cb8f0ed3..afca5e55a2 100644
--- a/src/nan/nan_ndl.c
+++ b/src/nan/nan_ndl.c
@@ -8,6 +8,7 @@
 
 #include "includes.h"
 #include "common.h"
+#include "common/ieee802_11_common.h"
 #include "nan_i.h"
 
 struct ndl_attr_params {
@@ -862,6 +863,64 @@ int nan_ndl_handle_ndl_attr(struct nan_data *nan, struct nan_peer *peer,
 	}
 }
 
+/*
+ * nan_ndl_add_avail_attrs - Add availability attributes
+ *
+ * @nan: NAN module context from nan_init()
+ * @peer: NAN peer for NDL establishment
+ * @buf: Frame buffer to which the attribute would be added
+ * Returns: 0 on success, negative on failure.
+ *
+ * An availability attribute is added for each map (identified by map ID) in the
+ * NDL schedule. Each attribute would hold an availability entry for committed
+ * slots and an availability entry for conditional slots.
+ */
+int nan_ndl_add_avail_attrs(struct nan_data *nan,
+			    const struct nan_peer *peer,
+			    struct wpabuf *buf)
+{
+	struct nan_schedule *sched;
+	u8 type_for_conditional = NAN_AVAIL_ENTRY_CTRL_TYPE_COND;
+
+	if (!peer || !peer->ndl)
+		return -1;
+
+	sched = &peer->ndl->sched;
+
+	wpa_printf(MSG_DEBUG,
+		   "NAN: NDL: Add Avail attribute. state=%s, status=%u",
+		   nan_ndl_state_str(peer->ndl->state), peer->ndl->status);
+
+	if (sched->n_chans < 1) {
+		if (peer->ndl->status == NAN_NDL_STATUS_REJECTED) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: NDL: Rejected. Not adding availability attributes");
+			return 0;
+		}
+
+		wpa_printf(MSG_DEBUG,
+			   "NAN: NDL: Cannot build availability without channels");
+		return -1;
+	}
+
+	/* In case that NDL exchange was complete successfully,
+	 * consider the conditional entries as committed, as
+	 * this is expected by the spec.
+	 */
+	if (peer->ndl->status == NAN_NDL_STATUS_ACCEPTED) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: NDL: Add conditional entry as committed");
+		type_for_conditional = NAN_AVAIL_ENTRY_CTRL_TYPE_COMMITTED;
+	}
+
+	return nan_add_avail_attrs(nan, sched->sequence_id,
+				   sched->map_ids_bitmap,
+				   type_for_conditional,
+				   sched->n_chans,
+				   sched->chans, buf);
+
+}
+
 
 /*
  * nan_ndl_add_ndl_attr - Add NDL attribute to frame
diff --git a/src/nan/nan_util.c b/src/nan/nan_util.c
index f7c35a3155..57414cdc37 100644
--- a/src/nan/nan_util.c
+++ b/src/nan/nan_util.c
@@ -367,3 +367,256 @@ int nan_chan_to_chan_idx_map(struct nan_data *nan,
 	*chan_idx_map = BIT(ret);
 	return 0;
 }
+
+
+static u16 nan_add_avail_entry(struct nan_data *nan,
+			       struct nan_time_bitmap *tbm,
+			       u8 type, u8 op_class,
+			       u16 chan_bm, u8 prim_chan_bm,
+			       struct wpabuf *buf)
+{
+	u16 ctrl;
+	u8 chan_ctrl;
+	u8 *len_ptr;
+	u8 nss = BITS(nan->cfg->dev_capa.n_antennas, NAN_DEV_CAPA_RX_ANT_MASK,
+		      NAN_DEV_CAPA_RX_ANT_POS);
+
+	len_ptr = wpabuf_put(buf, 2);
+
+	/*
+	 * TODO: Need to also add potential entries as otherwise the peer would
+	 * not be able to counter
+	 */
+	if (type != NAN_AVAIL_ENTRY_CTRL_TYPE_COMMITTED &&
+	    type != NAN_AVAIL_ENTRY_CTRL_TYPE_COND) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Cannot add non committed/conditional entry");
+		return 0;
+	}
+
+	/*
+	 * Add the entry control field
+	 * - usage preference is not set for committed and conditional
+	 * - utilization is max.
+	 */
+	ctrl = type;
+	ctrl |= NAN_AVAIL_ENTRY_DEF_UTIL << NAN_AVAIL_ENTRY_CTRL_UTIL_POS;
+	ctrl |= nss << NAN_AVAIL_ENTRY_CTRL_RX_NSS_POS;
+	ctrl |= NAN_AVAIL_ENTRY_CTRL_TBM_PRESENT;
+	wpabuf_put_le16(buf, ctrl);
+
+	/* Add the time bitmap control field */
+	ctrl = tbm->duration << NAN_TIME_BM_CTRL_BIT_DURATION_POS;
+	ctrl |= tbm->period << NAN_TIME_BM_CTRL_PERIOD_POS;
+	ctrl |= tbm->offset << NAN_TIME_BM_CTRL_START_OFFSET_POS;
+	wpabuf_put_le16(buf, ctrl);
+
+	wpabuf_put_u8(buf, tbm->len);
+
+	wpabuf_put_data(buf, tbm->bitmap, tbm->len);
+
+	/* Add the channel entry: single contiguous channel entry */
+	chan_ctrl = NAN_BAND_CHAN_CTRL_TYPE;
+	chan_ctrl |= 1 << NAN_BAND_CHAN_CTRL_NUM_ENTRIES_POS;
+	wpabuf_put_u8(buf, chan_ctrl);
+	wpabuf_put_u8(buf, op_class);
+	wpabuf_put_le16(buf, chan_bm);
+	wpabuf_put_u8(buf, prim_chan_bm);
+
+	WPA_PUT_LE16(len_ptr, (u8 *)wpabuf_put(buf, 0) - len_ptr - 2);
+	return (u8 *)wpabuf_put(buf, 0) - len_ptr;
+}
+
+
+int nan_get_chan_bm(struct nan_data *nan, struct nan_sched_chan *chan,
+		    u8 *op_class, u16 *chan_bm, u16 *pri_chan_bm)
+{
+	u8 bandwidth, channel;
+	enum hostapd_hw_mode mode;
+	int ret, sec_channel_offset;
+	int freq_offsset = chan->freq - chan->center_freq1;
+	u32 idx;
+
+	switch (chan->bandwidth) {
+	case 20:
+	case 40:
+	default:
+		bandwidth = CHANWIDTH_USE_HT;
+		break;
+	case 80:
+		bandwidth = CHANWIDTH_80MHZ;
+
+		idx = (freq_offsset + 30) / 20;
+		*pri_chan_bm = BIT(idx);
+		break;
+	case 160:
+		if (chan->center_freq2) {
+			bandwidth = CHANWIDTH_80P80MHZ;
+
+			/* TODO: Need to support auxiliary channel bitmap */
+			idx = (freq_offsset + 30) / 20;
+			*pri_chan_bm = BIT(idx);
+		} else {
+			bandwidth = CHANWIDTH_160MHZ;
+			idx = (freq_offsset + 70) / 20;
+			*pri_chan_bm = BIT(idx);
+		}
+		break;
+	}
+
+	if (freq_offsset > 0)
+		sec_channel_offset = 1;
+	else if (freq_offsset < 0)
+		sec_channel_offset = -1;
+	else
+		sec_channel_offset = 0;
+
+	/* For bandwidths >= 80 need to use the center frequency */
+	mode = ieee80211_freq_to_channel_ext(bandwidth == CHANWIDTH_USE_HT ?
+					     chan->freq : chan->center_freq1,
+					     sec_channel_offset,
+					     bandwidth,
+					     op_class, &channel);
+	if (mode == NUM_HOSTAPD_MODES) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Cannot get channel and op_class");
+		return -1;
+	}
+
+	ret = nan_chan_to_chan_idx_map(nan, *op_class, channel, chan_bm);
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Failed to derive channel bitmap");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/**
+ * nan_add_avail_attrs - Add NAN availability attributes
+ * @nan: NAN module context from nan_init()
+ * @sequence_id: Sequence ID to be used in the availability attributes
+ * @map_ids_bitmap: Bitmap of map IDs to be included in the availability
+ *	attributes
+ * @type_for_conditional: Type field to be used for conditional entries
+ * @n_chans: Number of channels in chans
+ * @chans: Channel schedules
+ * @buf: Frame buffer to which the attribute would be added
+ * Returns: 0 on success, negative on failure.
+ *
+ * An availability attribute is added for each map (identified by map ID) in the
+ * schedule. All channels with the same map ID are added to the same
+ * availability attribute. Each attribute will hold an availability entry for
+ * committed slots and an availability entry for conditional slots.
+ */
+int nan_add_avail_attrs(struct nan_data *nan, u8 sequence_id,
+			u32 map_ids_bitmap,
+			u8 type_for_conditional,
+			size_t n_chans, struct nan_chan_schedule *chans,
+			struct wpabuf *buf)
+{
+	u8 last_map_id = NAN_INVALID_MAP_ID;
+	u8 *len_ptr = NULL;
+	u8 i;
+
+	wpa_printf(MSG_DEBUG, "NAN: Add availability attrs. n_chans=%zu",
+		   n_chans);
+
+	for (i = 0; i < n_chans; i++) {
+		struct nan_chan_schedule *chan = &chans[i];
+		u8 op_class;
+		u16 chan_bm, pri_chan_bm;
+		int ret;
+
+		if (!chan->conditional.len && !chan->committed.len) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: committed and conditional are empty");
+			continue;
+		}
+
+		ret = nan_get_chan_bm(nan, &chan->chan, &op_class,
+				      &chan_bm, &pri_chan_bm);
+		if (ret)
+			continue;
+
+		/*
+		 * All channels with the same map ID should be added to the same
+		 * availability attribute, so verify that the map IDs are
+		 * sorted.
+		 */
+		if (last_map_id != NAN_INVALID_MAP_ID &&
+		    last_map_id > chan->map_id) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: map IDs not sorted properly");
+			return -1;
+		}
+
+		if (!(map_ids_bitmap & BIT(chan->map_id))) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: skip adding availability for map_id=%u",
+				   chan->map_id);
+			continue;
+		}
+
+		if (last_map_id != chan->map_id) {
+			u16 ctrl;
+
+			if (last_map_id != NAN_INVALID_MAP_ID) {
+				wpa_printf(MSG_DEBUG,
+					   "NAN: Add avail attr done: map_id=%u",
+					   last_map_id);
+
+				WPA_PUT_LE16(len_ptr,
+					     (u8 *)wpabuf_put(buf, 0) - len_ptr - 2);
+			}
+
+			last_map_id = chan->map_id;
+			map_ids_bitmap &= ~BIT(last_map_id);
+
+			wpa_printf(MSG_DEBUG, "NAN: Add avail attr map_id=%u",
+				   last_map_id);
+
+			wpabuf_put_u8(buf, NAN_ATTR_NAN_AVAILABILITY);
+			len_ptr = wpabuf_put(buf, 2);
+			wpabuf_put_u8(buf, sequence_id);
+
+			ctrl = last_map_id << NAN_AVAIL_CTRL_MAP_ID_POS;
+
+			/*
+			 * The spec states that this bit should be set if the
+			 * committed changed or if conditional is included. Set
+			 * it anyway, as it is not known what information the
+			 * peer has on our schedule.
+			 */
+			ctrl |= NAN_AVAIL_CTRL_COMMITTED_CHANGED;
+			wpabuf_put_le16(buf, ctrl);
+		}
+
+		/* TODO: handle primary channel configuration */
+		if (chan->committed.len)
+			nan_add_avail_entry(nan, &chan->committed,
+					    NAN_AVAIL_ENTRY_CTRL_TYPE_COMMITTED,
+					    op_class, chan_bm, pri_chan_bm,
+					    buf);
+
+		if (chan->conditional.len)
+			nan_add_avail_entry(nan, &chan->conditional,
+					    type_for_conditional,
+					    op_class, chan_bm, 0, buf);
+	}
+
+	if (last_map_id == NAN_INVALID_MAP_ID) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: No valid availability entries added");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "NAN: Add avail attr done: map_id=%u",
+		   last_map_id);
+
+	WPA_PUT_LE16(len_ptr, (u8 *)wpabuf_put(buf, 0) - len_ptr - 2);
+
+	return 0;
+}
-- 
2.49.0




More information about the Hostap mailing list