[RFC 11/56] NAN: Add support for parsing NAN availability attribute

Andrei Otcheretianski andrei.otcheretianski at intel.com
Sun Dec 7 03:18:20 PST 2025


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

Support parsing availability attributes and store the
result for the given peer.

Signed-off-by: Ilan Peer <ilan.peer at intel.com>
---
 src/nan/nan.c   | 648 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/nan/nan_i.h |  77 ++++++
 2 files changed, 725 insertions(+)

diff --git a/src/nan/nan.c b/src/nan/nan.c
index 07aab530cb..e8797bcb1c 100644
--- a/src/nan/nan.c
+++ b/src/nan/nan.c
@@ -38,6 +38,25 @@ struct nan_data * nan_init(const struct nan_config *cfg)
 }
 
 
+static void nan_del_avail_entry(struct nan_avail_entry *entry)
+{
+	os_free(entry->band_chan);
+	os_free(entry);
+}
+
+
+static void nan_peer_flush_avail(struct nan_peer_info *info)
+{
+	struct nan_avail_entry *cur, *next;
+
+	dl_list_for_each_safe(cur, next, &info->avail_entries,
+			      struct nan_avail_entry, list) {
+		dl_list_del(&cur->list);
+		nan_del_avail_entry(cur);
+	}
+}
+
+
 static void nan_del_peer(struct nan_data *nan, struct nan_peer *peer)
 {
 	if (!peer)
@@ -67,6 +86,7 @@ static void nan_del_peer(struct nan_data *nan, struct nan_peer *peer)
 	}
 
 	dl_list_del(&peer->list);
+	nan_peer_flush_avail(&peer->info);
 	os_free(peer);
 }
 
@@ -172,6 +192,633 @@ struct nan_peer * nan_get_peer(struct nan_data *nan, const u8 *addr)
 }
 
 
+/*
+ * nan_parse_tbm - Parse NAN Time Bitmap attribute
+ *
+ * @nan: NAN module context from nan_init()
+ * @tbm: On return would hold the parsed time bitmap
+ * @buf: Buffer holding the time bitmap
+ * @buf_len: Length of &buf
+ * Return 0 on success; otherwise -1
+ */
+static int nan_parse_tbm(struct nan_data *nan, struct nan_time_bitmap *tbm,
+			 const u8 *buf, u16 buf_len)
+{
+	u32 period;
+	u16 ctrl;
+	struct nan_tbm *bm;
+	u8 duration_bit;
+
+	if (buf_len < sizeof(*bm)) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Too short time bitmap length (%u)",
+			   buf_len);
+		return -1;
+	}
+
+	bm = (void *)buf;
+	if (!bm->len || bm->len + sizeof(*bm) > buf_len) {
+		wpa_printf(MSG_DEBUG, "NAN: Invalid tbm length (%hu)",
+			   bm->len);
+		return -1;
+	}
+
+	if (bm->len > sizeof(tbm->bitmap)) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: tbm len=%hu exceeds supported len=%zu",
+			   bm->len, sizeof(tbm->bitmap));
+		return -1;
+	}
+
+	ctrl = le_to_host16(bm->ctrl);
+
+	duration_bit = BITS(ctrl, NAN_TIME_BM_CTRL_BIT_DURATION_MASK,
+			    NAN_TIME_BM_CTRL_BIT_DURATION_POS);
+
+	if (duration_bit > NAN_TIME_BM_CTRL_BIT_DURATION_128_TU) {
+		wpa_printf(MSG_DEBUG, "NAN: Invalid time bitmap duration");
+		return -1;
+	}
+
+	tbm->duration = duration_bit;
+
+	tbm->period = BITS(ctrl, NAN_TIME_BM_CTRL_PERIOD_MASK,
+			   NAN_TIME_BM_CTRL_PERIOD_POS);
+	if (tbm->period) {
+		period = BIT(6 + tbm->period);
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Bitmap with period=0 is not supported");
+		return -1;
+	}
+
+	tbm->offset = BITS(ctrl, NAN_TIME_BM_CTRL_START_OFFSET_MASK,
+			   NAN_TIME_BM_CTRL_START_OFFSET_POS);
+
+	if (bm->len * 8 * BIT(4 + tbm->duration) > period) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: tbm is longer than the repeat period");
+		return -1;
+	}
+
+	if (tbm->offset * 16 > period) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: tbm offset %u exceeds period %u",
+			   tbm->offset, period);
+		return -1;
+	}
+
+	tbm->len = bm->len;
+	os_memcpy(tbm->bitmap, bm->bitmap, tbm->len);
+	return 0;
+}
+
+
+/*
+ * nan_parse_band_chan_list - Parse NAN Band/Channel List attribute
+ *
+ * @nan: NAN module context from nan_init()
+ * @entry: On return would hold the parsed band/channel list
+ * @list: Buffer holding the band/channel list
+ * @len: Length of &list
+ * Return 0 on success; otherwise -1
+ */
+static int nan_parse_band_chan_list(struct nan_data *nan,
+				    struct nan_avail_entry *entry,
+				    const struct nan_band_chan_list *list,
+				    u16 len)
+{
+	u8 band_chan_size, non_cont, i;
+	const u8 *pos;
+
+	if (len < sizeof(*list)) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Too short channel/band list=%hu", len);
+		return -1;
+	}
+
+	entry->band_chan_type = list->ctrl & NAN_BAND_CHAN_CTRL_TYPE;
+	entry->n_band_chan = BITS(list->ctrl,
+				  NAN_BAND_CHAN_CTRL_NUM_ENTRIES_MASK,
+				  NAN_BAND_CHAN_CTRL_NUM_ENTRIES_POS);
+
+	len -= sizeof(*list);
+	pos = list->entries;
+
+	if (entry->band_chan_type == NAN_TYPE_BAND) {
+		if (!len || !entry->n_band_chan || len < entry->n_band_chan) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: Truncated band list. len=%u, band_chan=%u",
+				   len, entry->n_band_chan);
+			return -1;
+		}
+
+		entry->band_chan = os_zalloc(sizeof(*entry->band_chan) *
+					     entry->n_band_chan);
+		if (!entry->band_chan) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: Failed to allocate band list");
+			return -1;
+		}
+
+		for (i = 0; i < entry->n_band_chan; i++)
+			entry->band_chan[i].u.band_id = pos[i];
+
+		return 0;
+	}
+
+	non_cont = !!(list->ctrl & NAN_BAND_CHAN_CTRL_NON_CONT_BW);
+	band_chan_size = non_cont ? NAN_CHAN_ENRTY_80P80_LEN :
+		NAN_CHAN_ENRTY_MIN_LEN;
+
+	if (len < entry->n_band_chan * band_chan_size) {
+		wpa_printf(MSG_DEBUG, "NAN: Truncated channel list");
+		return -1;
+	}
+
+	entry->band_chan = os_zalloc(sizeof(*entry->band_chan) *
+				     entry->n_band_chan);
+	if (!entry->band_chan) {
+		wpa_printf(MSG_DEBUG, "NAN: Failed to allocate channel list");
+		return -1;
+	}
+
+	for (i = 0; i < entry->n_band_chan; i++) {
+		struct nan_band_chan *curr = &entry->band_chan[i];
+		struct nan_chan_entry *chan = (void *)pos;
+
+		curr->u.chan.op_class = chan->op_class;
+		curr->u.chan.chan_bitmap = chan->chan_bitmap;
+		curr->u.chan.pri_chan_bitmap = chan->pri_chan_bitmap;
+		if (non_cont)
+			curr->u.chan.aux_chan_bitmap = chan->aux_chan_bitmap;
+
+		pos += band_chan_size;
+	}
+
+	return 0;
+}
+
+
+/*
+ * nan_split_avail_entry - Split an availability entry
+ *
+ * @nan: NAN module context from nan_init()
+ * @entry: Original entry to split
+ * Returns a newly allocated potential entry on success, otherwise NULL.
+ *
+ * The function expects an availability entry which is both
+ * committed/conditional and potential and has more then one channel entry. It
+ * splits the original entry such that:
+ *
+ * - The original entry is only committed/conditional with one channel entry
+ * - A new potential entry with the rest of the channels specified in the
+ *   original entry.
+ */
+static struct nan_avail_entry *
+nan_split_avail_entry(struct nan_data *nan,
+		      struct nan_avail_entry *entry)
+{
+	struct nan_avail_entry *pot;
+	struct nan_band_chan *tmp;
+
+	wpa_printf(MSG_DEBUG,
+		   "NAN: Split Committed/Conditional and potential entry");
+
+	pot = os_zalloc(sizeof(*pot));
+	if (!pot) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Failed to allocate availability entry");
+		return NULL;
+	}
+
+	pot->map_id = entry->map_id;
+	pot->type = NAN_AVAIL_ENTRY_CTRL_TYPE_POTENTIAL;
+	pot->preference = entry->preference;
+	pot->utilization = entry->utilization;
+	pot->rx_nss = entry->rx_nss;
+
+	dl_list_init(&pot->list);
+
+	pot->tbm.duration = entry->tbm.duration;
+	pot->tbm.period = entry->tbm.period;
+	pot->tbm.offset = entry->tbm.offset;
+	pot->tbm.len = entry->tbm.len;
+
+	os_memcpy(pot->tbm.bitmap, entry->tbm.bitmap, pot->tbm.len);
+
+	pot->band_chan_type = entry->band_chan_type;
+	pot->n_band_chan = entry->n_band_chan - 1;
+	pot->band_chan = os_zalloc(sizeof(*pot->band_chan) *
+				   pot->n_band_chan);
+	if (!pot->band_chan) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Failed to allocate channel list: potential");
+		os_free(pot);
+		return NULL;
+	}
+
+	os_memcpy(pot->band_chan, &entry->band_chan[1],
+		  sizeof(*pot->band_chan) * pot->n_band_chan);
+
+	tmp = entry->band_chan;
+
+	/* Clear potential from the original entry */
+	entry->type &= ~(NAN_AVAIL_ENTRY_CTRL_TYPE_POTENTIAL);
+	entry->band_chan = os_malloc(sizeof(*entry->band_chan));
+	if (!entry->band_chan) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Failed to allocate channel list: committed");
+		os_free(pot->band_chan);
+		os_free(pot);
+		return NULL;
+	}
+
+	os_memcpy(entry->band_chan, tmp, sizeof(*entry->band_chan));
+	entry->n_band_chan = 1;
+
+	os_free(tmp);
+	return pot;
+}
+
+
+/*
+ * nan_parse_avail_entry - Parse a NAN availability entry
+ *
+ * @nan: NAN module context from nan_init()
+ * @peer_info: Peer info where the parsed entry would be added
+ * @avail_entry: Pointer to the availability entry
+ * @entry_len: Length of the availability entry
+ * @map_id: Map ID of the availability attribute that this entry belongs to
+ * Returns: 0 on success, -1 on failure, or 0 to skip the entry
+ */
+static int nan_parse_avail_entry(struct nan_data *nan,
+				 struct nan_peer_info *peer_info,
+				 const struct nan_avail_ent *avail_entry,
+				 u16 entry_len, u8 map_id)
+{
+	struct nan_avail_entry *entry;
+	const u8 *pos;
+	u16 ctrl, len;
+	u8 type, preference, utilization;
+
+	if (entry_len < MIN_AVAIL_ENTRY_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Too short availability entry len=%hu",
+			   entry_len);
+		return -1;
+	}
+
+	ctrl = le_to_host16(avail_entry->ctrl);
+
+	type = ctrl & NAN_AVAIL_ENTRY_CTRL_TYPE_MASK;
+	if (!type ||
+	    ((type & NAN_AVAIL_ENTRY_CTRL_TYPE_COMMITTED) &&
+	     (type & NAN_AVAIL_ENTRY_CTRL_TYPE_COND))) {
+		wpa_printf(MSG_DEBUG, "NAN: Invalid entry type=0x%x", type);
+		return -1;
+	}
+
+	preference = BITS(ctrl, NAN_AVAIL_ENTRY_CTRL_USAGE_PREF_MASK,
+			  NAN_AVAIL_ENTRY_CTRL_USAGE_PREF_POS);
+	if (!preference && type == NAN_AVAIL_ENTRY_CTRL_TYPE_POTENTIAL) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Skip potential entry with 0 usage preference");
+		return 0;
+	}
+
+	utilization = BITS(ctrl, NAN_AVAIL_ENTRY_CTRL_UTIL_MASK,
+			   NAN_AVAIL_ENTRY_CTRL_UTIL_POS);
+
+	if (utilization > NAN_AVAIL_ENTRY_CTRL_UTIL_MAX &&
+	    utilization != NAN_AVAIL_ENTRY_CTRL_UTIL_UNKNOWN) {
+		wpa_printf(MSG_DEBUG, "NAN: Invalid tbm utilization");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "NAN: Avail entry: map_id=%u, entry_len=%u, type=0x%x, pref=0x%x",
+		   map_id, entry_len, type, preference);
+
+	entry = os_zalloc(sizeof(*entry));
+	if (!entry) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Failed to allocate availability entry");
+		return -1;
+	}
+
+	entry->map_id = map_id;
+	entry->type = type;
+	entry->preference = preference;
+	entry->utilization = utilization;
+	dl_list_init(&entry->list);
+
+	entry->rx_nss = BITS(ctrl, NAN_AVAIL_ENTRY_CTRL_RX_NSS_MASK,
+			     NAN_AVAIL_ENTRY_CTRL_RX_NSS_POS);
+
+	len = entry_len - MIN_AVAIL_ENTRY_LEN;
+	pos = avail_entry->optional;
+
+	if (ctrl & NAN_AVAIL_ENTRY_CTRL_TBM_PRESENT) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Availability entry: Time bitmap is set");
+
+		if (len < sizeof(struct nan_tbm)) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: Time bitmap set: length too short");
+			goto out;
+		}
+
+		if (nan_parse_tbm(nan, &entry->tbm, pos, len))
+			goto out;
+
+		pos += entry->tbm.len + sizeof(struct nan_tbm);
+		len -= entry->tbm.len + sizeof(struct nan_tbm);
+	} else {
+		entry->tbm.len = 0;
+		os_memset(entry->tbm.bitmap, 0, sizeof(entry->tbm.bitmap));
+	}
+
+	if (nan_parse_band_chan_list(nan, entry, (void *)pos, len))
+		goto out;
+
+	/*
+	 * An entry with committed/conditional can either have a single channel
+	 * entry, or multiple channel entries. The later case is allowed only if
+	 * the entry is also potential, in which case the fist channel entry
+	 * belongs to the committed entry and the other channels are potential
+	 */
+	if ((entry->type & NAN_AVAIL_ENTRY_CTRL_TYPE_COMMITTED) ||
+	    (entry->type & NAN_AVAIL_ENTRY_CTRL_TYPE_COND)) {
+		if (entry->band_chan_type != NAN_TYPE_CHANNEL) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: Committed/cond avail entry with band");
+			goto out;
+		} else if (entry->n_band_chan < 1) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: Committed/cond avail entry: no channels");
+			goto out;
+		} else if (entry->n_band_chan > 1) {
+			struct nan_avail_entry *pot_avail;
+
+			if (!(entry->type &
+			      NAN_AVAIL_ENTRY_CTRL_TYPE_POTENTIAL)) {
+				wpa_printf(MSG_DEBUG,
+					   "NAN: Committed/cond avail entry: %u chans",
+					   entry->n_band_chan);
+				goto out;
+			}
+
+			pot_avail = nan_split_avail_entry(nan, entry);
+			if (!pot_avail)
+				goto out;
+
+			dl_list_add(&peer_info->avail_entries,
+				    &pot_avail->list);
+		} else {
+			/*
+			 * Committed/conditional with single channel entry.
+			 * Clear the potential in case that it is set.
+			 */
+			entry->type &= ~(NAN_AVAIL_ENTRY_CTRL_TYPE_POTENTIAL);
+		}
+	}
+
+	dl_list_add(&peer_info->avail_entries, &entry->list);
+	return 0;
+
+out:
+	nan_del_avail_entry(entry);
+	return -1;
+}
+
+
+/*
+ * nan_parse_avail_attr - Parse NAN Availability attribute
+ *
+ * @nan: NAN module context from nan_init()
+ * @peer_info: Peer info where the parsed entries would be added
+ * @avail_attr: Pointer to the availability attribute
+ *
+ * Parse availability attribute as defined in Wi-Fi Aware Specification
+ * v4.0 section 9.5.17.1
+ */
+static int nan_parse_avail_attr(struct nan_data *nan,
+				struct nan_peer_info *peer_info,
+				const struct nan_avail *avail_attr,
+				u16 attr_len)
+{
+	u8 map_id;
+	const u8 *entries;
+	u16 ctrl, entries_len;
+
+	wpa_printf(MSG_DEBUG, "NAN: Parse avail attr: len=%u", attr_len);
+
+	ctrl = le_to_host16(avail_attr->ctrl);
+	map_id = ctrl & NAN_AVAIL_CTRL_MAP_ID_MASK;
+
+	entries = avail_attr->optional;
+	entries_len = attr_len - sizeof(*avail_attr);
+	if (!entries_len) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Availability attribute without any entries");
+		return -1;
+	}
+
+	while (entries_len > 2) {
+		u16 entry_len = WPA_GET_LE16(entries);
+		const struct nan_avail_ent *avail_entry = (void *)entries;
+
+		if (entry_len + 2 > entries_len) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: Truncated availability entry");
+			return -1;
+		}
+
+		if (nan_parse_avail_entry(nan, peer_info, avail_entry,
+					  entry_len, map_id))
+			return -1;
+
+		entries += entry_len + 2;
+		entries_len -= entry_len + 2;
+	}
+
+	if (entries_len) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Availability entries list truncated");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static void nan_peer_dump_info(struct nan_data *nan, struct nan_peer_info *info)
+{
+	struct nan_avail_entry *entry;
+
+	wpa_printf(MSG_DEBUG,
+		   "NAN: info: seen=%lu.%lu, seq_id=%u",
+		   info->last_seen.sec, info->last_seen.usec,
+		   info->seq_id);
+
+	dl_list_for_each(entry, &info->avail_entries, struct nan_avail_entry,
+			 list) {
+		u8 i;
+
+		wpa_printf(MSG_DEBUG,
+			   "NAN: entry: map_id=%u, type=0x%x, pref=%u, util=%u",
+			   entry->map_id, entry->type, entry->preference,
+			   entry->utilization);
+		wpa_printf(MSG_DEBUG,
+			   "NAN: entry: band_channel_type=%u, n_band_chan=%u",
+			   entry->band_chan_type, entry->n_band_chan);
+
+		for (i = 0; i < entry->n_band_chan; i++) {
+			struct nan_band_chan *bc = &entry->band_chan[i];
+
+			if (entry->type == NAN_TYPE_BAND)
+				wpa_printf(MSG_DEBUG,
+					   "NAN: band: %u", bc->u.band_id);
+			else
+				wpa_printf(MSG_DEBUG,
+					   "NAN: channel: oc=%u, cbtm=0x%x, pcbtm=0x%x",
+					   bc->u.chan.op_class,
+					   bc->u.chan.chan_bitmap,
+					   bc->u.chan.pri_chan_bitmap);
+		}
+	}
+}
+
+
+static void nan_peer_dump(struct nan_data *nan, struct nan_peer *peer)
+{
+	wpa_printf(MSG_DEBUG,
+		   "NAN: peer: " MACSTR " last_seen=%lu.%lu",
+		   MAC2STR(peer->nmi_addr), peer->last_seen.sec,
+		   peer->last_seen.usec);
+
+	nan_peer_dump_info(nan, &peer->info);
+}
+
+
+/*
+ * Update the old peer info with information from the new peer info.
+ * Information that is available in the old peer info but is not available
+ * in the new peer info will not be changed.
+ */
+static void nan_merge_peer_info(struct nan_peer_info *old,
+				struct nan_peer_info *new)
+{
+	if (!dl_list_empty(&new->avail_entries)) {
+		struct nan_avail_entry *avail, *tmp;
+
+		nan_peer_flush_avail(old);
+		dl_list_init(&old->avail_entries);
+
+		dl_list_for_each_safe(avail, tmp, &new->avail_entries,
+				      struct nan_avail_entry, list) {
+			dl_list_del(&avail->list);
+			dl_list_add(&old->avail_entries, &avail->list);
+		}
+		old->seq_id = new->seq_id;
+	}
+
+	old->last_seen = new->last_seen;
+}
+
+
+static int nan_avail_info(struct nan_data *nan, struct nan_peer *peer,
+			  struct nan_attrs *attrs, struct nan_peer_info *info)
+{
+	struct nan_avail *avail_attr;
+	struct nan_attrs_entry *attr;
+
+	attr = dl_list_first(&attrs->avail, struct nan_attrs_entry, list);
+	if (!attr)
+		return 0;
+
+	avail_attr = (void *)attr->ptr;
+
+	/*
+	 * The sequence id may wrap around, so if the received
+	 * sequence id is much smaller than the sequence id of
+	 * the last update, assume it has wrapped around and
+	 * accept the new schedule. Otherwise ignore it as an old
+	 * schedule.
+	 */
+	if (!dl_list_empty(&peer->info.avail_entries) &&
+	    peer->info.seq_id >= avail_attr->seq_id &&
+	    peer->info.seq_id - avail_attr->seq_id < 128) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Ignore peer avail update: seq_id=%hhu, seq_id=%hhu",
+			   avail_attr->seq_id, peer->info.seq_id);
+		return 0;
+	}
+
+	info->seq_id = avail_attr->seq_id;
+
+	dl_list_for_each(attr, &attrs->avail, struct nan_attrs_entry, list) {
+		avail_attr = (void *)attr->ptr;
+
+		if (avail_attr->seq_id != info->seq_id) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: Not all avail attributes have the same seq_id");
+			goto out;
+		}
+
+		if (nan_parse_avail_attr(nan, info, avail_attr, attr->len))
+			goto out;
+	}
+
+	return 0;
+out:
+	nan_peer_flush_avail(info);
+	return -1;
+}
+
+
+/*
+ * nan_parse_device_attrs - Parse device attributes and build availability info
+ *
+ * @nan: NAN module context from nan_init()
+ * @peer: NAN peer
+ * @attrs_data: Buffer holding the device attributes
+ * @attrs_len: Length of &attrs_data in octets
+ * Return 0 on success; -1 otherwise.
+ */
+int nan_parse_device_attrs(struct nan_data *nan, struct nan_peer *peer,
+			   const u8 *attrs_data, size_t attrs_len)
+{
+	struct nan_peer_info info;
+	struct nan_attrs attrs;
+	int ret;
+
+	os_memset(&info, 0, sizeof(info));
+	dl_list_init(&info.avail_entries);
+	os_get_reltime(&info.last_seen);
+
+	if (nan_parse_attrs(nan, attrs_data, attrs_len, &attrs)) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Failed to parse peer " MACSTR " attributes",
+			   MAC2STR(peer->nmi_addr));
+		return -1;
+	}
+
+	if (nan_avail_info(nan, peer, &attrs, &info)) {
+		ret = -1;
+		goto out;
+	}
+
+	nan_merge_peer_info(&peer->info, &info);
+	nan_peer_dump(nan, peer);
+	ret = 0;
+out:
+	nan_attrs_clear(nan, &attrs);
+	return ret;
+}
+
+
 static struct nan_peer *nan_alloc_peer(struct nan_data *nan)
 {
 	struct nan_peer *peer, *oldest = NULL;
@@ -207,6 +854,7 @@ static struct nan_peer *nan_alloc_peer(struct nan_data *nan)
 	if (!peer)
 		return NULL;
 
+	dl_list_init(&peer->info.avail_entries);
 	dl_list_add(&nan->peer_list, &peer->list);
 	dl_list_init(&peer->ndps);
 	return peer;
diff --git a/src/nan/nan_i.h b/src/nan/nan_i.h
index 52f1f59c78..f535c901b1 100644
--- a/src/nan/nan_i.h
+++ b/src/nan/nan_i.h
@@ -100,11 +100,84 @@ struct nan_ndp_setup {
 	u8 service_id[NAN_SERVICE_ID_LEN];
 };
 
+/*
+ * struct nan_band_chan - NAN channel/band entry
+ *
+ * @band_id: Band ID as specified by enum nan_band_entry.
+ * @chan: Channel entry as specified by &struct nan_chan_entry.
+ */
+struct nan_band_chan {
+	union {
+		u8 band_id;
+		struct nan_chan_entry chan;
+	} u;
+};
+
+/**
+ * enum nan_band_chan_type - NAN band or channel
+ *
+ * @NAN_TYPE_BAND: The entry is a band entry
+ * @NAN_TYPE_CHANNEL: The entry is a channel entry
+ */
+enum nan_band_chan_type {
+	NAN_TYPE_BAND,
+	NAN_TYPE_CHANNEL,
+};
+
+/*
+ * struct nan_avail_entry - NAN availability entry
+ *
+ * @list: Used for linking in the availability entries list.
+ * @map_id: Map id of the availability attribute that this entry belongs to.
+ * @type: Availability type. One of NAN_AVAIL_ENTRY_CTRL_TYPE_*.
+ * @preference: Preference of being available in the NAN slots specified by
+ *	the associated time bitmap. The preference is higher when the value is
+ *	set larger. Valid values are 0 - 3.
+ * @utilization: Indicating proportion within the NAN slots specified by the
+ *	associated time bitmap that are already utilized for other purposes,
+ *	quantized to 20%. Valid values are 0 - 5.
+ * @rx_nss: Max number of special streams the NAN device can receive during
+ *	the NAN slots specified by the associated time bitmap.
+ * @tbm: Time bitmap specifying the NAN slots in which the device will be
+ *	available for NAN operations.
+ * @band_chan_type: Type of entries in &band_chan array, as specified by
+ *	enum nan_band_chan_type.
+ * @n_band_chan: Number of entries in &band_chan array.
+ * @band_chan: Array of bands/channels on which the NAN device will be
+ *	available.
+ */
+struct nan_avail_entry {
+	struct dl_list list;
+	u8 map_id;
+	u8 type;
+	u8 preference;
+	u8 utilization;
+	u8 rx_nss;
+	struct nan_time_bitmap tbm;
+	enum nan_band_chan_type band_chan_type;
+	u8 n_band_chan;
+	struct nan_band_chan *band_chan;
+};
+
+/*
+ * struct nan_peer_info - NAN peer information
+ *
+ * @last_seen: Timestamp of the last update of the peer info.
+ * @seq_id: Sequence id of the last availability update.
+ * @avail_entries: List of availability entries of the peer.
+ */
+struct nan_peer_info {
+	struct os_reltime last_seen;
+	u8 seq_id;
+	struct dl_list avail_entries;
+};
+
 /**
  * struct nan_peer - Represents a known NAN peer
  * @list: List node for linking peers.
  * @nmi_addr: NAN MAC address of the peer.
  * @last_seen: Timestamp of the last time this peer was seen.
+ * @info: Information about the peer.
  * @ndps: List of NDPs associated with this peer.
  * @ndp_setup: Used to hold an NDP object while NDP establishment is in
  *     progress.
@@ -113,6 +186,8 @@ struct nan_peer {
 	struct dl_list list;
 	u8 nmi_addr[ETH_ALEN];
 	struct os_reltime last_seen;
+	struct nan_peer_info info;
+
 	struct dl_list ndps;
 
 	struct nan_ndp_setup ndp_setup;
@@ -201,4 +276,6 @@ void nan_ndp_setup_failure(struct nan_data *nan, struct nan_peer *peer,
 			   enum nan_reason reason, bool reset_state);
 int nan_ndp_naf_sent(struct nan_data *nan, struct nan_peer *peer,
 		     enum nan_subtype subtype);
+int nan_parse_device_attrs(struct nan_data *nan, struct nan_peer *peer,
+			   const u8 *attrs_data, size_t attrs_len);
 #endif
-- 
2.49.0




More information about the Hostap mailing list