[PATCH 05/18] NAN: Validate the NDC carried in a received Confirm
Andrei Otcheretianski
andrei.otcheretianski at intel.com
Mon Oct 12 08:46:39 PDT 2026
Table 18 in the Wi-Fi Aware Specification v4.0 leaves the initiator no
freedom in the NDC it confirms: the NDL Schedule Confirm Proposal shall
carry the same NDC attribute as the one proposed in the response.
Nothing verified this. A peer echoing a different NDC was accepted and
its schedule was stored as the agreed one, silently moving the NDL to
an NDC the local device never proposed.
Remember the NDC that was proposed to the peer. No legitimate
negotiation can lead to a confirm carrying a different one, and a
confirm cannot be answered with a rejecting frame anyway, so drop it
the same way an invalid frame is dropped and let the setup timeout
tear the NDP down.
The NDC ID is now treated the same way. A confirm carrying a different
NDC ID is dropped.
Signed-off-by: Andrei Otcheretianski <andrei.otcheretianski at intel.com>
---
src/nan/nan_i.h | 4 ++
src/nan/nan_ndl.c | 133 +++++++++++++++++++++++++++++++++++++++++++---
2 files changed, 130 insertions(+), 7 deletions(-)
diff --git a/src/nan/nan_i.h b/src/nan/nan_i.h
index 635a65817a..09f33db23b 100644
--- a/src/nan/nan_i.h
+++ b/src/nan/nan_i.h
@@ -434,6 +434,10 @@ struct nan_ndl {
struct nan_qos peer_qos, local_qos;
+ /* The NDC proposed to the peer in the last transmitted NDL frame */
+ struct nan_time_bitmap local_ndc;
+ u8 local_ndc_map_id;
+
u8 *ndc_sched;
u16 ndc_sched_len;
diff --git a/src/nan/nan_ndl.c b/src/nan/nan_ndl.c
index 8ff1cc10af..69abcd82de 100644
--- a/src/nan/nan_ndl.c
+++ b/src/nan/nan_ndl.c
@@ -133,6 +133,8 @@ static void nan_ndl_clear(struct nan_data *nan, struct nan_peer *peer)
ndl->max_idle_period = 0;
os_memset(ndl->ndc_id, 0, sizeof(ndl->ndc_id));
+ os_memset(&ndl->local_ndc, 0, sizeof(ndl->local_ndc));
+ ndl->local_ndc_map_id = 0;
ndl->peer_qos.max_latency = NAN_QOS_MAX_LATENCY_NO_PREF;
ndl->local_qos.max_latency = NAN_QOS_MAX_LATENCY_NO_PREF;
@@ -807,6 +809,9 @@ int nan_ndl_setup(struct nan_data *nan, struct nan_peer *peer,
MAC2STR(ndl->ndc_id));
}
+ os_memcpy(&ndl->local_ndc, &nan->sched.ndc, sizeof(ndl->local_ndc));
+ ndl->local_ndc_map_id = nan->sched.ndc_map_id;
+
ndl->local_qos.min_slots = params->qos.min_slots;
ndl->local_qos.max_latency = params->qos.max_latency;
@@ -1008,6 +1013,116 @@ fail:
}
+/*
+ * The NDC attribute only carries a time bitmap, so the channel it refers to is
+ * the one the availability entries advertise for its slots. Find the local
+ * channel hosting the NDC that was proposed to the peer.
+ */
+static const struct nan_sched_chan *
+nan_ndl_local_ndc_chan(struct nan_data *nan, const struct nan_ndl *ndl)
+{
+ const struct nan_schedule *sched = &nan->sched;
+ const struct nan_sched_chan *chan = NULL;
+ struct bitfield *ndc_bf;
+ u8 i;
+
+ ndc_bf = nan_tbm_to_bf(nan, &ndl->local_ndc);
+ if (!ndc_bf)
+ return NULL;
+
+ for (i = 0; !chan && i < sched->n_chans; i++) {
+ struct bitfield *chan_bf;
+ u16 cbm, pri_cbm;
+ u8 map_id, op_class;
+
+ if (sched->chans[i].map_id != ndl->local_ndc_map_id)
+ continue;
+
+ if (nan_convert_chan_sched_to_bf(nan, &sched->chans[i],
+ &chan_bf, &map_id, &op_class,
+ &cbm, &pri_cbm))
+ continue;
+
+ /* The NDC cannot be split across channels */
+ if (bitfield_is_subset(chan_bf, ndc_bf) == 1)
+ chan = &sched->chans[i].chan;
+
+ bitfield_free(chan_bf);
+ }
+
+ bitfield_free(ndc_bf);
+
+ return chan;
+}
+
+
+/*
+ * Table 18 in the Wi-Fi Aware Specification v4.0 requires the NDC carried in a
+ * Confirm to be the same one that was proposed in the Response, so compare it
+ * against the NDC that was last transmitted to the peer.
+ */
+static bool nan_ndl_ndc_matches_proposed(struct nan_data *nan,
+ struct nan_peer *peer,
+ const u8 *ndc_sched,
+ size_t ndc_sched_len)
+{
+ const struct nan_ndl *ndl = peer->ndl;
+ struct bitfield *peer_bf, *local_bf = NULL;
+ const struct nan_sched_chan *chan;
+ struct dl_list sched_entries;
+ enum nan_reason reason;
+ bool match = false;
+ u8 peer_map_id;
+
+ /*
+ * The NDC attribute is optional in a Request, so an initiator that
+ * proposed nothing leaves the selection to the peer
+ */
+ if (!ndl->local_ndc.len)
+ return true;
+
+ if (!ndc_sched || !ndc_sched_len) {
+ wpa_printf(MSG_DEBUG, "NAN: NDL: No NDC schedule in the frame");
+ return false;
+ }
+
+ if (nan_sched_entries_to_avail_entries(nan, &sched_entries, ndc_sched,
+ ndc_sched_len)) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: Failed to parse the NDC schedule");
+ return false;
+ }
+
+ peer_bf = nan_sched_to_bf(nan, &sched_entries, &peer_map_id, &reason);
+ nan_flush_avail_entries(&sched_entries);
+ if (!peer_bf) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: Failed to convert the NDC schedule");
+ return false;
+ }
+
+ local_bf = nan_tbm_to_bf(nan, &ndl->local_ndc);
+ if (!local_bf)
+ goto out;
+
+ if (bitfield_is_subset(local_bf, peer_bf) != 1 ||
+ bitfield_is_subset(peer_bf, local_bf) != 1)
+ goto out;
+
+ chan = nan_ndl_local_ndc_chan(nan, ndl);
+ if (!chan)
+ goto out;
+
+ match = nan_sched_bf_covered_by_avail_entries_and_chan(
+ nan, &peer->info.avail_entries, peer_bf, peer_map_id, chan);
+out:
+ bitfield_free(local_bf);
+ bitfield_free(peer_bf);
+
+ return match;
+}
+
+
static int nan_ndl_attr_handle_resp(struct nan_data *nan, struct nan_peer *peer,
const struct ndl_attr_params *params)
{
@@ -1184,19 +1299,23 @@ static int nan_ndl_attr_handle_conf(struct nan_data *nan, struct nan_peer *peer,
return -1;
}
- if (!is_zero_ether_addr(params->ndc_id) &&
- os_memcmp(ndl->ndc_id, params->ndc_id, sizeof(ndl->ndc_id)) != 0) {
- wpa_printf(MSG_DEBUG, "NAN: NDL: Confirm: ndc_id changed");
- os_memcpy(ndl->ndc_id, params->ndc_id, sizeof(ndl->ndc_id));
+ if (os_memcmp(ndl->ndc_id, params->ndc_id, sizeof(ndl->ndc_id)) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: Confirm: NDC ID differs from the proposed one");
+ return -1;
+ }
+
+ if (!nan_ndl_ndc_matches_proposed(nan, peer, params->ndc_sched,
+ params->ndc_sched_len)) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: Confirm: NDC differs from the proposed one");
+ return -1;
}
ndl->max_idle_period = params->max_idle_period;
ndl->peer_qos.min_slots = params->min_slots;
ndl->peer_qos.max_latency = params->max_latency;
- /* TODO: validate that the NDC schedule is covered by the local device
- * committed availability.
- */
if (params->ndc_sched && params->ndc_sched_len) {
os_free(ndl->ndc_sched);
ndl->ndc_sched_len = 0;
--
2.53.0
More information about the Hostap
mailing list