[RFC v2 25/99] NAN: Properly set the NDL status in a response and confirm

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


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

Validate the local schedule versus the peer's schedule,
and determine the status of the NDL setup:

- Check if the intersection of the local availability and the peer
  availability is not empty and adheres to the peer QoS
  requirements.
- Check if local NDC is a subset of the local schedule.
- Check if local NDC is identical to the peer NDC.
- Check if peer's immutable is covered by the local schedule.

If these conditions are met, accept the NDL. Otherwise:

- If the frame is a response frame, continue the NDL establishment.
- If the frame is a confirm frame, reject the NDL establishment.

Signed-off-by: Ilan Peer <ilan.peer at intel.com>
---
 src/nan/nan_i.h    |  16 +-
 src/nan/nan_ndl.c  | 459 ++++++++++++++++++++++++++++++++++++++++++++-
 src/nan/nan_util.c | 253 ++++++++++++++++++++++---
 3 files changed, 691 insertions(+), 37 deletions(-)

diff --git a/src/nan/nan_i.h b/src/nan/nan_i.h
index 6f3cfdb77b..edaf6910e2 100644
--- a/src/nan/nan_i.h
+++ b/src/nan/nan_i.h
@@ -446,8 +446,16 @@ bool nan_sched_covered_by_avail_entry(struct nan_data *nan,
 				      struct nan_avail_entry *avail,
 				      struct bitfield *sched_bf,
 				      u8 map_id);
-int nan_sched_covered_by_avail_entries(struct nan_data *nan,
-				       struct dl_list *avail_entries,
-				       u8 *sched,
-				       u8 sched_len);
+bool nan_sched_covered_by_avail_entries(struct nan_data *nan,
+					struct dl_list *avail_entries,
+					u8 *sched, u8 sched_len);
+bool
+nan_sched_bf_covered_by_avail_entries_and_chan(struct nan_data *nan,
+					       const struct dl_list *avail_entries,
+					       struct bitfield *sched_bf,
+					       u8 map_id,
+					       u8 op_class, u16 cbm);
+struct bitfield * nan_avail_entries_to_bf(struct nan_data *nan,
+					  const struct dl_list *avail_entries,
+					  u8 op_class, u16 cbm, u16 pri_cbm);
 #endif
diff --git a/src/nan/nan_ndl.c b/src/nan/nan_ndl.c
index 4b43c13077..cf95c5ebf4 100644
--- a/src/nan/nan_ndl.c
+++ b/src/nan/nan_ndl.c
@@ -10,6 +10,7 @@
 #include "common.h"
 #include "common/ieee802_11_common.h"
 #include "nan_i.h"
+#include "bitfield.h"
 
 struct ndl_attr_params {
 	u8 dialog_token;
@@ -222,11 +223,454 @@ static int nan_ndl_validate_peer_avail(struct nan_data *nan,
 }
 
 
-static enum nan_ndl_status nan_ndl_res_status(struct nan_data *nan,
-					      struct nan_peer *peer)
+/*
+ * nan_ndl_convert_chan_sched_to_bf - Convert channel schedule to bitfield
+ * and get the channel information.
+ *
+ * @nan: NAN module context from nan_init()
+ * @chan: Channel schedule to convert
+ * @avail_bf: On successful return would hold the availability bit map of the
+ *     given channel schedule
+ * @map_id: On successful return would hold the map ID for the schedule
+ * @op_class: On successful return would hold the operating class for the
+ *     schedule with the peer
+ * @cbm: On successful return holds the channel bitmap for the operating
+ *     class
+ * @pcbm: On successful return holds the primary channel bitmap for the
+ *     channel in case of bandwidth greater than 40 MHz
+ * Returns 0 on success; -1 on failure
+ */
+static int nan_ndl_convert_chan_sched_to_bf(struct nan_data *nan,
+					    struct nan_chan_schedule *chan,
+					    struct bitfield **avail_bf,
+					    u8 *map_id, u8 *op_class,
+					    u16 *cbm, u16 *pcbm)
 {
-	/* TODO: properly set the status */
-	return NAN_NDL_STATUS_ACCEPTED;
+	struct bitfield *committed_bf, *conditional_bf;
+	int ret;
+
+	*op_class = 0;
+	*cbm = 0;
+	*pcbm = 0;
+	*map_id = chan->map_id;
+
+	ret = nan_get_chan_bm(nan, &chan->chan, op_class, cbm, pcbm);
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: NDL: failed to convert channel info");
+		return -1;
+	}
+
+	committed_bf = nan_tbm_to_bf(nan, &chan->committed);
+	if (!committed_bf) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: NDL: failed to build committed bitfield");
+		return -1;
+	}
+
+	conditional_bf = nan_tbm_to_bf(nan, &chan->conditional);
+	if (!conditional_bf) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: NDL: failed to build conditional bitfield");
+		bitfield_free(committed_bf);
+		return -1;
+	}
+
+	*avail_bf = bitfield_union(committed_bf, conditional_bf);
+	bitfield_free(committed_bf);
+	bitfield_free(conditional_bf);
+
+	if (!*avail_bf) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: NDL: failed to unify committed and conditional bitfields");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "NAN: NDL: map_id=%u, op_class=%u, cbm=0x%x",
+		   *map_id, *op_class, *cbm);
+	return 0;
+}
+
+
+/**
+ * enum nan_ndl_ver - Verdict of comparing local availability to given schedule
+ *
+ * @NAN_NDL_VER_SCHED_SUBSET_OF_LOCAL: The schedule is a subset of the local one
+ * @NAN_NDL_VER_SCHED_SUPERSET_OF_LOCAL: The schedule is a superset of the local
+ *     one
+ * @NAN_NDL_VER_SCHED_IDENTICAL_OF_LOCAL: The schedule is identical to the local
+ * @NAN_NDL_VER_SCHED_NONE: None of the above. This means that there was no
+ *     match or an error occurred.
+ */
+enum nan_ndl_ver {
+	NAN_NDL_VER_SCHED_SUBSET_OF_LOCAL,
+	NAN_NDL_VER_SCHED_SUPERSET_OF_LOCAL,
+	NAN_NDL_VER_SCHED_IDENTICAL_OF_LOCAL,
+	NAN_NDL_VER_SCHED_NONE,
+};
+
+
+/*
+ * nan_ndl_match_sched_vs_common - Compare the given schedule to the common
+ * availability of the local device and the peer device, comparing channel
+ * configuration and bitmap.
+ *
+ * @nan: NAN module context from nan_init()
+ * @avail_entries: Peer availability entries
+ * @sched: A byte array with 0 or more &struct nan_sched_entry entries
+ * @sched_len: Length of the &sched array
+ * @common_bf: Bitfield representing the intersection of local and peer
+ *     availability
+ * @op_class: Local operating class
+ * @cbm: Local channel bitmap
+ *
+ * Returns one of enum nan_ndl_ver. In case of on an error
+ * NAN_NDL_VER_SCHED_NONE is returned.
+ *
+ * The function first verifies that the given schedule is covered by the peer
+ * availability. Then, the function verifies that the schedule is covered by
+ * the common availability between local and peer.
+ *
+ * The function can (and should be used) to check the schedule constraints of
+ * an NDC schedule and/or immutable schedule.
+ *
+ * Note: In case that &sched is NULL or &sched_len is 0, returns
+ * NAN_NDL_VER_SCHED_IDENTICAL_OF_LOCAL, meaning no constraints.
+ */
+static enum nan_ndl_ver
+nan_ndl_match_sched_vs_common(struct nan_data *nan,
+			      struct dl_list *avail_entries,
+			      u8 *sched, u8 sched_len,
+			      struct bitfield *common_bf,
+			      u8 op_class, u16 cbm)
+{
+	struct dl_list sched_entries;
+	struct bitfield *sched_bf;
+	u8 map_id;
+	enum nan_ndl_ver verdict;
+	int ret;
+
+	/* No constraints */
+	if (!sched || !sched_len)
+		return NAN_NDL_VER_SCHED_IDENTICAL_OF_LOCAL;
+
+	/* Convert the schedule entries to availability entries  */
+	ret = nan_sched_entries_to_avail_entries(nan,
+						 &sched_entries,
+						 sched,
+						 sched_len);
+	if (ret)
+		return NAN_NDL_VER_SCHED_NONE;
+
+	/* Convert the schedule availability entries to map ID and bitfield */
+	sched_bf = nan_sched_to_bf(nan, &sched_entries, &map_id);
+	nan_flush_avail_entries(&sched_entries);
+
+	/*
+	 * Now that the schedule is represented as bitfield and the map ID is
+	 * obtained, compare these against the peer availability entries and
+	 * channel configuration. A successful match means that the schedule is
+	 * covered by the peer availability entries for the given channel.
+	 */
+	ret = nan_sched_bf_covered_by_avail_entries_and_chan(nan,
+							     avail_entries,
+							     sched_bf,
+							     map_id,
+							     op_class,
+							     cbm);
+	if (ret) {
+		int ret2;
+
+		/*
+		 * After validating the schedule against the peer availability,
+		 * match the peer availability with the local one.
+		 */
+		ret = bitfield_is_subset(common_bf, sched_bf);
+		ret2 = bitfield_is_subset(sched_bf, common_bf);
+
+		if (ret < 0 || ret2 < 0)
+			verdict = NAN_NDL_VER_SCHED_NONE;
+		else if (ret == 1 && ret2 == 1)
+			verdict = NAN_NDL_VER_SCHED_IDENTICAL_OF_LOCAL;
+		else if (ret == 1)
+			verdict = NAN_NDL_VER_SCHED_SUBSET_OF_LOCAL;
+		else
+			verdict = NAN_NDL_VER_SCHED_SUPERSET_OF_LOCAL;
+	} else {
+		verdict = NAN_NDL_VER_SCHED_NONE;
+	}
+
+	bitfield_free(sched_bf);
+	return verdict;
+}
+
+
+static enum nan_ndl_status nan_ndl_determine_status(struct nan_data *nan,
+						    struct nan_peer *peer,
+						    bool can_counter,
+						    enum nan_reason *reason)
+{
+	struct nan_schedule *sched = &peer->ndl->sched;
+	struct bitfield *common_bf = NULL, *ndc_bf = NULL, *track_ndc_bf = NULL;
+	enum nan_ndl_ver verdict;
+	size_t size, max_latency, i;
+	u16 crbs;
+	int ret;
+
+	*reason = NAN_REASON_RESERVED;
+
+	ndc_bf = nan_tbm_to_bf(nan, &sched->ndc);
+	if (!ndc_bf) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: NDL: Failed to build NDC bitfield from schedule");
+
+		*reason = NAN_REASON_UNSPECIFIED_REASON;
+		return NAN_NDL_STATUS_REJECTED;
+	}
+
+	track_ndc_bf = bitfield_alloc(bitfield_size(ndc_bf));
+	if (!track_ndc_bf) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: NDL: Failed to allocate bitfield for tracking NDC");
+
+		bitfield_free(ndc_bf);
+		*reason = NAN_REASON_UNSPECIFIED_REASON;
+		return NAN_NDL_STATUS_REJECTED;
+	}
+
+	/*
+	 * Iterate over all the channels included in the local schedule. For
+	 * each channel convert the committed and conditional slots to a
+	 * bitfield object and extract the operating class and channel bitmap.
+	 *
+	 * Using the operating class and channel bitmap find the peer
+	 * availability on that channel and intersect it with the local one:
+	 *
+	 * - Accumulate the intersection of the NDC bitmap with the bitmap all
+	 *   channels with the same map ID as the NDC, so later it can be
+	 *   verified that the NDC is covered by the local availability.
+	 * - Accumulate the intersection of local and peer availability for
+	 *   all channels, so later the QoS requirements can be verified.
+	 */
+	wpa_printf(MSG_DEBUG, "NAN: NDL: n_chans=%u, ndc_map_id=%u",
+		   sched->n_chans, sched->ndc_map_id);
+
+	for (i = 0; i < sched->n_chans; i++) {
+		struct bitfield *own_chan_bf = NULL, *peer_chan_bf = NULL;
+		u16 cbm, pri_cbm;
+		u8 map_id, op_class;
+
+		/* Convert the schedule for the current channel to bitfield */
+		ret = nan_ndl_convert_chan_sched_to_bf(nan, &sched->chans[i],
+						       &own_chan_bf, &map_id,
+						       &op_class, &cbm,
+						       &pri_cbm);
+		if (ret) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: NDL: Failed to convert chan sched to bitfield");
+
+			*reason = NAN_REASON_UNSPECIFIED_REASON;
+			ret = NAN_NDL_STATUS_REJECTED;
+			goto out;
+		}
+
+		if (sched->ndc_map_id == map_id) {
+			struct bitfield *tmp = bitfield_dup(ndc_bf);
+
+			if (tmp) {
+				bitfield_intersect_in_place(tmp, own_chan_bf);
+				bitfield_union_in_place(track_ndc_bf, tmp);
+				bitfield_free(tmp);
+			}
+		}
+
+		/* Get the peer availability for the current channel */
+		peer_chan_bf = nan_avail_entries_to_bf(nan,
+						       &peer->info.avail_entries,
+						       op_class, cbm, pri_cbm);
+		if (!peer_chan_bf) {
+			bitfield_free(own_chan_bf);
+			continue;
+		}
+
+		ret = bitfield_intersect_in_place(own_chan_bf, peer_chan_bf);
+		bitfield_free(peer_chan_bf);
+		peer_chan_bf = NULL;
+
+		if (ret < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: NDL: Failed to intersect own and peer chan bitfields");
+
+			bitfield_free(own_chan_bf);
+
+			*reason = NAN_REASON_INVALID_AVAILABILITY;
+			ret = NAN_NDL_STATUS_REJECTED;
+			goto out;
+		}
+
+		/*
+		 * Accumulate schedule common for local and peer device, so it
+		 * can later be used to verify QoS requirements etc.
+		 */
+		if (common_bf) {
+			ret = bitfield_union_in_place(common_bf, own_chan_bf);
+			if (ret) {
+				wpa_printf(MSG_DEBUG,
+					   "NAN: NDL: Failed to unify own chan bitfields");
+
+				bitfield_free(own_chan_bf);
+
+				*reason = NAN_REASON_UNSPECIFIED_REASON;
+				ret = NAN_NDL_STATUS_REJECTED;
+				goto out;
+			}
+		} else {
+			common_bf = bitfield_dup(own_chan_bf);
+			if (!common_bf) {
+				wpa_printf(MSG_DEBUG,
+					   "NAN: NDL: Failed to dup own chan bitfield");
+
+				bitfield_free(own_chan_bf);
+
+				*reason = NAN_REASON_UNSPECIFIED_REASON;
+				ret = NAN_NDL_STATUS_REJECTED;
+				goto out;
+			}
+		}
+
+		bitfield_free(own_chan_bf);
+		own_chan_bf = NULL;
+	}
+
+	if (!common_bf) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: NDL: No common availability between local and peer");
+
+		*reason = NAN_REASON_INVALID_AVAILABILITY;
+		ret = NAN_NDL_STATUS_CONTINUED;
+		goto out;
+	}
+
+	/*
+	 * Verify that the schedule NDC bitmap is covered by the local
+	 * availability for the map used for the NDC.
+	 */
+	if (!bitfield_is_subset(ndc_bf, track_ndc_bf)) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: NDL: NDC bitmap is not covered by local availability on NDC channel");
+
+		*reason = NAN_REASON_UNSPECIFIED_REASON;
+		ret = NAN_NDL_STATUS_REJECTED;
+		goto out;
+	}
+
+
+	bitfield_free(ndc_bf);
+	ndc_bf = track_ndc_bf;
+	track_ndc_bf = NULL;
+
+	/*
+	 * In case the peer included an immutable, the immutable must be
+	 * covered by the common schedule. If not reject.
+	 */
+	verdict = nan_ndl_match_sched_vs_common(nan,
+						&peer->info.avail_entries,
+						peer->ndl->immut_sched,
+						peer->ndl->immut_sched_len,
+						common_bf, 0, 0);
+	if (verdict != NAN_NDL_VER_SCHED_IDENTICAL_OF_LOCAL &&
+	    verdict != NAN_NDL_VER_SCHED_SUBSET_OF_LOCAL) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Schedule does not cover immutable. Reject");
+
+		*reason = NAN_REASON_IMMUTABLE_UNACCEPTABLE;
+		ret = NAN_NDL_STATUS_REJECTED;
+		goto out;
+	}
+
+	/*
+	 * In case the peer included an NDC, check if it is identical to
+	 * the locally generated one.
+	 *
+	 * Note: the list of peer availability entries needs to be iterated
+	 * again, as the NDC map ID must also be matched.
+	 */
+	verdict = nan_ndl_match_sched_vs_common(nan,
+						&peer->info.avail_entries,
+						peer->ndl->ndc_sched,
+						peer->ndl->ndc_sched_len,
+						ndc_bf, 0, 0);
+	if (verdict != NAN_NDL_VER_SCHED_IDENTICAL_OF_LOCAL) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Response NDC does not match req NDC");
+
+		*reason = NAN_REASON_INVALID_AVAILABILITY;
+		ret = NAN_NDL_STATUS_CONTINUED;
+		goto out;
+	}
+
+	/* No QoS requirements. Accept */
+	if (peer->ndl->peer_qos.min_slots == NAN_QOS_MIN_SLOTS_NO_PREF &&
+	    peer->ndl->peer_qos.max_latency == NAN_QOS_MAX_LATENCY_NO_PREF) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: No QoS requirements from Peer. Accept");
+
+		ret = NAN_NDL_STATUS_ACCEPTED;
+		goto out;
+	}
+
+	size = bitfield_size(common_bf);
+	wpa_printf(MSG_DEBUG,
+		   "NAN: size of avail intersection map=%zu", size);
+
+	for (i = 0, crbs = 0, max_latency = 0; i < size; i++) {
+		if (bitfield_is_set(common_bf, i)) {
+			crbs++;
+			max_latency = 0;
+		} else if (peer->ndl->peer_qos.max_latency !=
+			   NAN_QOS_MAX_LATENCY_NO_PREF) {
+			max_latency++;
+			if (max_latency > peer->ndl->peer_qos.max_latency) {
+				wpa_printf(MSG_DEBUG,
+					   "NAN: failed to meet max latency");
+
+				*reason = NAN_REASON_QOS_UNACCEPTABLE;
+				ret = NAN_NDL_STATUS_CONTINUED;
+				goto out;
+			}
+		}
+	}
+
+	if (peer->ndl->peer_qos.min_slots != NAN_QOS_MIN_SLOTS_NO_PREF &&
+	    peer->ndl->peer_qos.min_slots >= crbs) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: failed to meet min slots");
+
+		*reason = NAN_REASON_QOS_UNACCEPTABLE;
+		ret = NAN_NDL_STATUS_CONTINUED;
+		goto out;
+	}
+
+	ret = NAN_NDL_STATUS_ACCEPTED;
+out:
+	bitfield_free(common_bf);
+	bitfield_free(ndc_bf);
+	bitfield_free(track_ndc_bf);
+
+	wpa_printf(MSG_DEBUG,
+		   "NAN: NDL: Response status=%s (%u)",
+		   ret == NAN_NDL_STATUS_ACCEPTED ? "ACCEPTED" :
+		   ret == NAN_NDL_STATUS_CONTINUED ? "CONTINUED" :
+		   "REJECTED",
+		   ret);
+
+	if (ret == NAN_NDL_STATUS_CONTINUED && !can_counter) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Cannot counter, change verdict to REJECTED");
+		ret = NAN_NDL_STATUS_REJECTED;
+	}
+
+	return ret;
 }
 
 
@@ -328,7 +772,12 @@ int nan_ndl_setup(struct nan_data *nan, struct nan_peer *peer,
 		nan_ndl_set_state(nan, ndl, NAN_NDL_STATE_START);
 		ndl->status = NAN_NDL_STATUS_CONTINUED;
 	} else {
-		ndl->status = nan_ndl_res_status(nan, peer);
+		ndl->status = nan_ndl_determine_status(nan, peer,
+						       ndl->state ==
+						       NAN_NDL_STATE_REQ_RECV,
+						       &reason);
+		if (ndl->status == NAN_NDL_STATUS_REJECTED)
+			goto out_fail;
 	}
 
 	ndl->setup_reason = NAN_NDL_SETUP_REASON_NDP;
diff --git a/src/nan/nan_util.c b/src/nan/nan_util.c
index ced188fc47..096c7ad57f 100644
--- a/src/nan/nan_util.c
+++ b/src/nan/nan_util.c
@@ -1137,14 +1137,14 @@ bool nan_sched_covered_by_avail_entry(struct nan_data *nan,
 }
 
 
-static int nan_sched_bf_covered_by_avail_entries(struct nan_data *nan,
-						 struct dl_list *avail_entries,
-						 struct bitfield *sched_bf,
-						 u8 map_id)
+static struct bitfield *
+nan_sched_bf_from_avail_and_chan(struct nan_data *nan,
+				 const struct dl_list *avail_entries,
+				 u8 map_id,
+				 u8 op_class, u16 cbm)
 {
 	struct nan_avail_entry *avail;
 	struct bitfield *res_bf = NULL;
-	int ret = 0;
 
 	dl_list_for_each(avail, avail_entries, struct nan_avail_entry,
 			 list) {
@@ -1158,12 +1158,19 @@ static int nan_sched_bf_covered_by_avail_entries(struct nan_data *nan,
 		    avail->type != NAN_AVAIL_ENTRY_CTRL_TYPE_COND)
 			continue;
 
+		/* Now check channel, if it is given */
+		if (op_class && cbm) {
+			if (avail->n_band_chan < 1 ||
+			    avail->band_chan_type != NAN_TYPE_CHANNEL ||
+			    avail->band_chan[0].u.chan.op_class != op_class ||
+			    !(avail->band_chan[0].u.chan.chan_bitmap & cbm))
+				continue;
+		}
+
 		/* convert the availability entry to bf */
 		avail_bf = nan_tbm_to_bf(nan, &avail->tbm);
-		if (!avail_bf) {
-			ret = -1;
+		if (!avail_bf)
 			goto fail;
-		}
 
 		bitfield_dump(avail_bf, "NAN: Availability entry bitmap");
 		if (!res_bf) {
@@ -1174,22 +1181,18 @@ static int nan_sched_bf_covered_by_avail_entries(struct nan_data *nan,
 			tmp_bf = bitfield_union(res_bf, avail_bf);
 			bitfield_free(avail_bf);
 
-			if (!tmp_bf) {
-				ret = -1;
+			if (!tmp_bf)
 				goto fail;
-			}
 
 			bitfield_free(res_bf);
 			res_bf = tmp_bf;
 		}
 	}
 
-	ret = bitfield_is_subset(res_bf, sched_bf);
+	return res_bf;
 fail:
-	wpa_printf(MSG_DEBUG,
-		   "NAN: Is bitfield schedule subset of entries=%d", ret);
 	bitfield_free(res_bf);
-	return ret;
+	return NULL;
 }
 
 
@@ -1201,20 +1204,19 @@ fail:
  * @avail_entries: A list of availability entries (see &struct nan_avail_entry)
  * @sched: An array with 0 or more &struct nan_sched_entry entries
  * @sched_len: Length of the &sched array
- * Returns 1 of schedule is covered by the entries; 0 if not and -1 on error
+ * Returns true if schedule is covered by the entries; otherwise false.
  */
-int nan_sched_covered_by_avail_entries(struct nan_data *nan,
-				       struct dl_list *avail_entries,
-				       u8 *sched,
-				       u8 sched_len)
+bool nan_sched_covered_by_avail_entries(struct nan_data *nan,
+					struct dl_list *avail_entries,
+					u8 *sched, u8 sched_len)
 {
 	struct dl_list sched_entries;
-	struct bitfield *sched_bf;
+	struct bitfield *sched_bf, *avail_bf;
 	u8 map_id;
-	int ret;
+	bool ret = false;
 
 	if (!sched || !sched_len)
-		return 1;
+		return true;
 
 	dl_list_init(&sched_entries);
 	ret = nan_sched_entries_to_avail_entries(nan,
@@ -1224,12 +1226,207 @@ int nan_sched_covered_by_avail_entries(struct nan_data *nan,
 	sched_bf = nan_sched_to_bf(nan, &sched_entries, &map_id);
 	nan_flush_avail_entries(&sched_entries);
 
-	ret = nan_sched_bf_covered_by_avail_entries(nan, avail_entries,
-						    sched_bf, map_id);
+	avail_bf = nan_sched_bf_from_avail_and_chan(nan, avail_entries,
+						    map_id, 0, 0);
+	if (avail_bf)
+		ret = bitfield_is_subset(avail_bf, sched_bf) ? true : false;
 
-	wpa_printf(MSG_DEBUG,
-		   "NAN: NDC schedule is %s a subset of entries",
-		   ret == 1 ? "" : "NOT");
+	wpa_printf(MSG_DEBUG, "NAN: Schedule is %s a subset of entries",
+		   ret ? "" : "NOT");
+
+	bitfield_free(avail_bf);
 	bitfield_free(sched_bf);
+
+	return ret;
+}
+
+
+/**
+ * nan_sched_bf_covered_by_avail_entries_and_chan - Check if schedule is covered
+ * by the list of availability attributes matching the channel configurations
+ *
+ * @nan: NAN module context from nan_init()
+ * @avail_entries: A list of availability entries. See &struct nan_avail_entry
+ * @sched_bf: the bitfield representing the schedule
+ * @map_id: Map ID associated with the schedule
+ * @op_class: Operating class to match against
+ * @cbm: Channel bit map to match against
+ * Returns true of schedule is covered by the entries; otherwise false
+ */
+bool
+nan_sched_bf_covered_by_avail_entries_and_chan(struct nan_data *nan,
+					       const struct dl_list *avail_entries,
+					       struct bitfield *sched_bf,
+					       u8 map_id,
+					       u8 op_class, u16 cbm)
+{
+	struct bitfield *avail_bf;
+	bool ret = false;
+
+	/*
+	 * Build a schedule bitfield from all the availability entries matching
+	 * the map id and channel configuration
+	 */
+	avail_bf = nan_sched_bf_from_avail_and_chan(nan, avail_entries,
+						    map_id,
+						    op_class, cbm);
+
+	/*
+	 * If there is such schedule, verify that it is a superset of the given
+	 * schedule
+	 */
+	if (avail_bf && bitfield_is_subset(avail_bf, sched_bf))
+		ret = true;
+
+	wpa_printf(MSG_DEBUG,
+		   "NAN: Is schedule covered by entries and chan=%u", ret);
+
+	bitfield_free(avail_bf);
 	return ret;
 }
+
+
+static int nan_get_control_channel(struct nan_data *nan, u8 op_class,
+				   u16 cbm, u16 pri_cbm)
+{
+	const struct oper_class_map *op = get_oper_class(NULL, op_class);
+	int freq = 0, idx;
+	u8 chan_id;
+
+	if (!op || op_class > 130)
+		return -1;
+
+	idx = ffs(cbm) - 1;
+	if (idx < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: No channel found in chan_bitmap 0x%04x for oper_class %u",
+			   cbm, op_class);
+		return -1;
+	}
+
+	chan_id = op_class_idx_to_chan(op, idx);
+	if (!chan_id) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: No channel found for oper_class %u idx %u",
+			   op_class, idx);
+		return -1;
+	}
+
+	freq = ieee80211_chan_to_freq(NULL, op_class, chan_id);
+
+	/*
+	 * For operating classes with bandwidth < 80 the frequency is the
+	 * control channel frequency. For operating classes with
+	 * bandwidth >= 80, the frequency is the center frequency of the
+	 * primary segment, so we need to derive the control channel frequency
+	 * from the primary channel bitmap.
+	 */
+	if (op->bw == BW20 || op->bw == BW40 ||
+	    op->bw == BW40PLUS || op->bw == BW40MINUS)
+		return freq;
+
+	if (!pri_cbm) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: No primary channel bitmap provided for oper_class %u",
+			   op_class);
+		return -1;
+	}
+
+	idx = ffs(pri_cbm) - 1;
+
+	if (op->bw == BW80 || op->bw == BW80P80)
+		return freq - 30 + idx * 20;
+
+	if (op->bw == BW160)
+		return freq - 70 + idx * 20;
+
+	return -1;
+}
+
+
+/**
+ * nan_avail_entries_to_bf - Convert availability entries that match the given
+ * channel configuration to a bitfield.
+ *
+ * @nan: NAN module context from nan_init()
+ * @avail_entries: A list of availability entries. See &struct nan_avail_entry
+ * @op_class: Operating class to match against
+ * @cbm: Channel bit map to match against
+ * @pri_cbm: Primary channel bit map to match against
+ * Returns NULL on error or no match; otherwise returns a bitfield describing
+ * all the available slots.
+ */
+struct bitfield * nan_avail_entries_to_bf(struct nan_data *nan,
+					  const struct dl_list *avail_entries,
+					  u8 op_class, u16 cbm, u16 pri_cbm)
+{
+	struct nan_avail_entry *avail;
+	struct bitfield *res_bf = NULL;
+
+	dl_list_for_each(avail, avail_entries, struct nan_avail_entry,
+			 list) {
+		struct bitfield *avail_bf;
+
+		/* Schedule can only be covered by committed/conditional */
+		if (avail->type != NAN_AVAIL_ENTRY_CTRL_TYPE_COMMITTED &&
+		    avail->type != NAN_AVAIL_ENTRY_CTRL_TYPE_COND)
+			continue;
+
+		/*
+		 * Committed/conditional entries should only have a single
+		 * channel entry
+		 */
+		if (avail->n_band_chan != 1 ||
+		    avail->band_chan_type != NAN_TYPE_CHANNEL)
+			continue;
+
+		/*
+		 * Check that the availability entry channel matches. If it does
+		 * not match, check if the channels are compatible, i.e., have
+		 * the same control channel
+		 */
+		if (avail->band_chan[0].u.chan.op_class != op_class ||
+		    avail->band_chan[0].u.chan.chan_bitmap != cbm ||
+		    avail->band_chan[0].u.chan.pri_chan_bitmap != pri_cbm) {
+			int freq1, freq2;
+
+			freq1 = nan_get_control_channel(nan, op_class,
+							cbm, pri_cbm);
+			freq2 = nan_get_control_channel(nan,
+							avail->band_chan[0].u.chan.op_class,
+							le_to_host16(avail->band_chan[0].u.chan.chan_bitmap),
+							le_to_host16(avail->band_chan[0].u.chan.pri_chan_bitmap));
+
+			if (freq2 == -1 || freq1 != freq2)
+				continue;
+
+			wpa_printf(MSG_DEBUG,
+				   "NAN: Availability entry channel is compatible. Control channel freq=%d MHz",
+				   freq1);
+		}
+
+		/* Convert the availability entry to bf */
+		avail_bf = nan_tbm_to_bf(nan, &avail->tbm);
+		if (!avail_bf)
+			goto fail;
+
+		if (!res_bf) {
+			res_bf = avail_bf;
+		} else {
+			struct bitfield *tmp_bf;
+
+			tmp_bf = bitfield_union(res_bf, avail_bf);
+			if (!tmp_bf)
+				goto fail;
+
+			bitfield_free(res_bf);
+			bitfield_free(avail_bf);
+			res_bf = tmp_bf;
+		}
+	}
+
+	return res_bf;
+fail:
+	bitfield_free(res_bf);
+	return NULL;
+}
-- 
2.49.0




More information about the Hostap mailing list