[RFC v2 17/99] NAN: Initial support for NDL state machine and operations
Andrei Otcheretianski
andrei.otcheretianski at intel.com
Tue Dec 23 03:51:21 PST 2025
From: Ilan Peer <ilan.peer at intel.com>
Add NDL state machine and states and support for parsing
the NDL, NDC and QoS attributes.
This is only a partial implementation of the state machine and
not all operations are supported. Following patches would complete
the implementation.
Signed-off-by: Ilan Peer <ilan.peer at intel.com>
---
src/nan/Makefile | 2 +-
src/nan/nan.c | 2 +
src/nan/nan.h | 7 +-
src/nan/nan_i.h | 91 +++++
src/nan/nan_ndl.c | 863 ++++++++++++++++++++++++++++++++++++++++
wpa_supplicant/Makefile | 1 +
6 files changed, 964 insertions(+), 2 deletions(-)
create mode 100644 src/nan/nan_ndl.c
diff --git a/src/nan/Makefile b/src/nan/Makefile
index 64e1ae4849..38665ea234 100644
--- a/src/nan/Makefile
+++ b/src/nan/Makefile
@@ -1,3 +1,3 @@
-LIB_OBJS= nan.o nan_util.o nan_ndp.o
+LIB_OBJS= nan.o nan_util.o nan_ndp.o nan_ndl.o
include ../lib.rules
diff --git a/src/nan/nan.c b/src/nan/nan.c
index 0cc2c0ca16..1f642ad4d4 100644
--- a/src/nan/nan.c
+++ b/src/nan/nan.c
@@ -113,6 +113,8 @@ static void nan_del_peer(struct nan_data *nan, struct nan_peer *peer)
nan_peer_flush_avail(&peer->info);
nan_peer_flush_dev_capa(&peer->info);
nan_peer_flush_elem_container(&peer->info);
+
+ nan_ndl_reset(nan, peer);
os_free(peer);
}
diff --git a/src/nan/nan.h b/src/nan/nan.h
index 234eaa83ca..530bf83890 100644
--- a/src/nan/nan.h
+++ b/src/nan/nan.h
@@ -200,6 +200,8 @@ struct nan_schedule {
* NAN_NDP_ACTION_REQ).
* @status: Response status.
* @reason_code: In case of rejected response, the rejection reason.
+ * @sched_valid: Indicates whether the schedule field is valid.
+ * @sched: The NAN schedule associated with the NDP parameters.
*/
struct nan_ndp_params {
enum nan_ndp_action type;
@@ -219,13 +221,16 @@ struct nan_ndp_params {
* Used with both NAN_NDP_ACTION_RESP (as a response to an NDP
* request) and NAN_NDP_ACTION_CONF (as a response to an NDP
* response with a counter).
- * */
+ */
struct nan_ndp_setup_resp {
u8 resp_ndi[ETH_ALEN];
u8 status;
u8 reason_code;
} resp;
} u;
+
+ bool sched_valid;
+ struct nan_schedule sched;
};
/*
diff --git a/src/nan/nan_i.h b/src/nan/nan_i.h
index 9608de244d..c0f9b41be4 100644
--- a/src/nan/nan_i.h
+++ b/src/nan/nan_i.h
@@ -205,6 +205,87 @@ struct nan_peer_info {
};
/**
+ * enum nan_ndl_state - State of NDL establishment
+ *
+ * @NAN_NDL_STATE_NONE: No NDL with the peer
+ * @NAN_NDL_STATE_START: NDL setup initiated by local device
+ * @NAN_NDL_STATE_REQ_SENT: Sent NDL request
+ * @NAN_NDL_STATE_REQ_RECV: Got NDL request
+ * @NAN_NDL_STATE_RES_SENT: Sent NDL response
+ * @NAN_NDL_STATE_RES_RECV: Got NDL response
+ * @NAN_NDL_STATE_CON_SENT: Sent NDL confirm
+ * @NAN_NDL_STATE_CON_RECV: Got NDL confirm
+ * @NAN_NDL_STATE_DONE: NDL establishment is done (either success or reject).
+ */
+enum nan_ndl_state {
+ NAN_NDL_STATE_NONE = 0,
+ NAN_NDL_STATE_START,
+ NAN_NDL_STATE_REQ_SENT,
+ NAN_NDL_STATE_REQ_RECV,
+ NAN_NDL_STATE_RES_SENT,
+ NAN_NDL_STATE_RES_RECV,
+ NAN_NDL_STATE_CON_SENT,
+ NAN_NDL_STATE_CON_RECV,
+ NAN_NDL_STATE_DONE,
+};
+
+/**
+ * enum nan_ndl_setup_reason - NAN NDL setup reason
+ * @NAN_NDL_SETUP_REASON_NONE: none
+ * @NAN_NDL_SETUP_REASON_NDP: NDL setup request for NDP operation
+ */
+enum nan_ndl_setup_reason {
+ NAN_NDL_SETUP_REASON_NONE = 0,
+ NAN_NDL_SETUP_REASON_NDP,
+};
+
+/**
+ * struct nan_ndl - NAN NDL data
+ *
+ * @state: Current state. See &enum nan_ndl_state
+ * @status: Current status. See &enum nan_ndl_status
+ * @send_naf_on_error: When set, indicates that in case that the NDL processing
+ * returned an error, a NAF still needs to be sent to the peer, i.e., the
+ * error cannot be silently ignored.
+ * @reason: In case of status == NAN_NDL_STATUS_REJECTED, indicates the reason.
+ * See &enum nan_ndl_status
+ * @sched: Holds the local schedule. See &struct nan_schedule.
+ * @dialog_token: The dialog token for the current NDL negotiation.
+ * @max_idle_period: Indicate a period of time in units of 1024TU during which
+ * the peer device can refrain from transmitting over the NDL without
+ * being terminated.
+ * @setup_reason: The reason for the NDL setup. See &enum nan_ndl_setup_reason.
+ * @ndc_id: NDC identifier
+ * @peer_qos: Peer QoS requirements
+ * @local_qos: Local QoS requirements (for the current NDP establishment)
+ * @ndc_sched: The NDC schedule entries. See &struct nan_sched_entry
+ * @ndc_sched_len: The length in octets of ndc_sched.
+ * @immut_sched: The immutable schedule entries. See &enum nan_sched_entry
+ * @immut_sched_len: The length in octets of immut_sched.
+ */
+struct nan_ndl {
+ enum nan_ndl_state state;
+ enum nan_ndl_status status;
+ u8 send_naf_on_error;
+ enum nan_reason reason;
+ struct nan_schedule sched;
+
+ u8 dialog_token;
+ u16 max_idle_period;
+ enum nan_ndl_setup_reason setup_reason;
+
+ u8 ndc_id[ETH_ALEN];
+
+ struct nan_qos peer_qos, local_qos;
+
+ u8 *ndc_sched;
+ u16 ndc_sched_len;
+
+ u8 *immut_sched;
+ u16 immut_sched_len;
+};
+
+/*
* struct nan_peer - Represents a known NAN peer
* @list: List node for linking peers.
* @nmi_addr: NAN MAC address of the peer.
@@ -213,6 +294,7 @@ struct nan_peer_info {
* @ndps: List of NDPs associated with this peer.
* @ndp_setup: Used to hold an NDP object while NDP establishment is in
* progress.
+ * @ndl: NDL data associated with this peer.
*/
struct nan_peer {
struct dl_list list;
@@ -223,6 +305,8 @@ struct nan_peer {
struct dl_list ndps;
struct nan_ndp_setup ndp_setup;
+
+ struct nan_ndl *ndl;
};
/**
@@ -310,6 +394,13 @@ 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);
+int nan_ndl_setup(struct nan_data *nan, struct nan_peer *peer,
+ struct nan_ndp_params *params);
+void nan_ndl_setup_failure(struct nan_data *nan, struct nan_peer *peer,
+ enum nan_reason reason, bool reset_state);
+void nan_ndl_reset(struct nan_data *nan, struct nan_peer *peer);
+int nan_ndl_handle_ndl_attr(struct nan_data *nan, struct nan_peer *peer,
+ struct nan_msg *msg);
int nan_chan_to_chan_idx_map(struct nan_data *nan,
u8 op_class, u8 channel, u16 *chan_idx_map);
#endif
diff --git a/src/nan/nan_ndl.c b/src/nan/nan_ndl.c
new file mode 100644
index 0000000000..83e2d1e15c
--- /dev/null
+++ b/src/nan/nan_ndl.c
@@ -0,0 +1,863 @@
+/*
+ * Wi-Fi Aware - NAN Data link
+ * Copyright (C) 2025 Intel Corporation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include "common.h"
+#include "nan_i.h"
+
+struct ndl_attr_params {
+ u8 dialog_token;
+ u8 type;
+ u8 status;
+ u8 reason;
+ enum nan_ndl_setup_reason setup_reason;
+
+ u16 max_idle_period;
+ u8 min_slots;
+ u16 max_latency;
+
+ u8 ndc_id[ETH_ALEN];
+ const u8 *ndc_sched;
+ u16 ndc_sched_len;
+
+ const u8 *immut_sched; u16 immut_sched_len;
+};
+
+static const char *nan_ndl_state_str(enum nan_ndl_state state)
+{
+#define C2S(x) case x: return #x;
+ switch (state) {
+ C2S(NAN_NDL_STATE_NONE)
+ C2S(NAN_NDL_STATE_START)
+ C2S(NAN_NDL_STATE_REQ_SENT)
+ C2S(NAN_NDL_STATE_REQ_RECV)
+ C2S(NAN_NDL_STATE_RES_SENT)
+ C2S(NAN_NDL_STATE_RES_RECV)
+ C2S(NAN_NDL_STATE_CON_SENT)
+ C2S(NAN_NDL_STATE_CON_RECV)
+ C2S(NAN_NDL_STATE_DONE)
+ default:
+ return "Invalid NAN NDL state";
+ }
+}
+
+
+static void nan_ndl_set_state(struct nan_data *nan, struct nan_ndl *ndl,
+ enum nan_ndl_state state)
+{
+ wpa_printf(MSG_DEBUG, "NAN: NDL: State %s (%u) --> %s (%u)",
+ nan_ndl_state_str(ndl->state), ndl->state,
+ nan_ndl_state_str(state), state);
+
+ ndl->state = state;
+}
+
+
+static void nan_ndl_time_bitmap_print(struct nan_data *nan,
+ struct nan_time_bitmap *tbm,
+ const char *type)
+{
+ if (!tbm->len)
+ return;
+
+ wpa_printf(MSG_DEBUG,
+ "channel sched: %s: dur=%u, per=%u, off=%u, len=%u",
+ type, tbm->duration, tbm->period, tbm->offset, tbm->len);
+
+ wpa_printf(MSG_DEBUG,
+ "bitmap[0:3]: 0x%x:0x%x:0x%x:0x%x",
+ tbm->bitmap[0], tbm->bitmap[1],
+ tbm->bitmap[2], tbm->bitmap[3]);
+}
+
+
+static void nan_ndl_chan_sched_print(struct nan_data *nan, size_t idx,
+ struct nan_chan_schedule *cs)
+{
+ wpa_printf(MSG_DEBUG, "NAN: sched: index=%zu, map_id=%u",
+ idx, cs->map_id);
+
+ wpa_printf(MSG_DEBUG,
+ "channel sched: chan: freq=%u, c1=%d, c2=%d, bandwidth=%d",
+ cs->chan.freq, cs->chan.center_freq1, cs->chan.center_freq2,
+ cs->chan.bandwidth);
+
+ nan_ndl_time_bitmap_print(nan, &cs->committed, "committed");
+ nan_ndl_time_bitmap_print(nan, &cs->conditional, "conditional");
+}
+
+
+static void nan_ndl_sched_print(struct nan_data *nan,
+ struct nan_schedule *sched)
+{
+ size_t i;
+
+ wpa_printf(MSG_DEBUG,
+ "NAN: sched: map_ids=0x%x, n_chans=%u, seq_id=%u",
+ sched->map_ids_bitmap,
+ sched->n_chans, sched->sequence_id);
+
+ for (i = 0; i < sched->n_chans; i++)
+ nan_ndl_chan_sched_print(nan, i, &sched->chans[i]);
+}
+
+
+static void nan_ndl_clear(struct nan_data *nan, struct nan_peer *peer)
+{
+ struct nan_ndl *ndl = peer->ndl;
+
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: Clear info for peer=" MACSTR " state=%s (%u)",
+ MAC2STR(peer->nmi_addr),
+ nan_ndl_state_str(peer->ndl->state), peer->ndl->state);
+
+ os_free(ndl->ndc_sched);
+ ndl->ndc_sched = NULL;
+ ndl->ndc_sched_len = 0;
+
+ os_free(ndl->immut_sched);
+ ndl->immut_sched = NULL;
+ ndl->immut_sched_len = 0;
+
+ ndl->dialog_token = 0;
+ ndl->max_idle_period = 0;
+
+ os_memset(ndl->ndc_id, 0, sizeof(ndl->ndc_id));
+
+ ndl->peer_qos.max_latency = NAN_QOS_MAX_LATENCY_NO_PREF;
+ ndl->local_qos.max_latency = NAN_QOS_MAX_LATENCY_NO_PREF;
+
+ ndl->setup_reason = NAN_NDL_SETUP_REASON_NONE;
+
+ wpabuf_free(ndl->sched.elems);
+ os_memset(&ndl->sched, 0, sizeof(ndl->sched));
+}
+
+
+/*
+ * nan_ndl_reset - Reset the NDL state
+ *
+ * @nan: NAN module context from nan_init()
+ * @peer: The peer that requires NDL setup reset
+ */
+void nan_ndl_reset(struct nan_data *nan, struct nan_peer *peer)
+{
+ wpa_printf(MSG_DEBUG, "NAN: NDL: Reset state for peer=" MACSTR,
+ MAC2STR(peer->nmi_addr));
+
+ if (!peer->ndl) {
+ wpa_printf(MSG_DEBUG, "NAN: NDL: Reset: no NDL");
+ return;
+ }
+
+ nan_ndl_clear(nan, peer);
+
+ os_free(peer->ndl);
+ peer->ndl = NULL;
+}
+
+
+struct nan_ndl *nan_ndl_alloc(struct nan_data *nan)
+{
+ struct nan_ndl *ndl;
+
+ wpa_printf(MSG_DEBUG, "NAN: NDL: Allocating a new NDL");
+
+ ndl = os_zalloc(sizeof(struct nan_ndl));
+ if (!ndl) {
+ wpa_printf(MSG_DEBUG, "NAN: NDL: Failed to allocate NDL");
+ return NULL;
+ }
+
+ ndl->status = NAN_NDL_STATUS_ACCEPTED;
+ ndl->reason = NAN_REASON_RESERVED;
+
+ ndl->local_qos.min_slots = NAN_QOS_MIN_SLOTS_NO_PREF;
+ ndl->local_qos.max_latency = NAN_QOS_MAX_LATENCY_NO_PREF;
+
+ ndl->peer_qos.min_slots = NAN_QOS_MIN_SLOTS_NO_PREF;
+ ndl->peer_qos.max_latency = NAN_QOS_MAX_LATENCY_NO_PREF;
+
+ nan_ndl_set_state(nan, ndl, NAN_NDL_STATE_NONE);
+
+ return ndl;
+}
+
+
+static enum nan_ndl_status nan_ndl_res_status(struct nan_data *nan,
+ struct nan_peer *peer)
+{
+ /* TODO: properly set the status */
+ return NAN_NDL_STATUS_ACCEPTED;
+}
+
+
+/*
+ * nan_ndl_setup - Handle NDL setup either as an initiator or a responder
+ *
+ * @nan: NAN module context from nan_init()
+ * @peer: The peer for which the NDL is being setup
+ * @params: NDP setup request parameters
+ * Returns: 0 on success, negative on failure.
+
+ * It is possible that an NDL with the peer already exists in which case it
+ * would be reused. Otherwise, new NDL establishment will be started.
+ */
+int nan_ndl_setup(struct nan_data *nan, struct nan_peer *peer,
+ struct nan_ndp_params *params)
+{
+ struct nan_ndl *ndl;
+ enum nan_reason reason;
+
+ if (!peer->ndl) {
+ if (params->type != NAN_NDP_ACTION_REQ) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: Invalid action; expecting request");
+ return -1;
+ }
+
+ peer->ndl = nan_ndl_alloc(nan);
+ if (!peer->ndl)
+ return -1;
+
+ ndl = peer->ndl;
+ } else {
+ ndl = peer->ndl;
+
+ ndl->send_naf_on_error = 0;
+
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: peer=" MACSTR ". state=%s (%u)",
+ MAC2STR(peer->nmi_addr),
+ nan_ndl_state_str(ndl->state),
+ ndl->state);
+
+ if (ndl->state == NAN_NDL_STATE_DONE) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: Already established");
+ return 0;
+ }
+
+ if (!((params->type == NAN_NDP_ACTION_RESP &&
+ ndl->state == NAN_NDL_STATE_REQ_RECV) ||
+ (params->type == NAN_NDP_ACTION_CONF &&
+ ndl->state == NAN_NDL_STATE_RES_RECV))) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: Invalid action type=%u, state=%u",
+ params->type, ndl->state);
+ return -1;
+ }
+
+ if (params->u.resp.status == NAN_NDP_STATUS_REJECTED) {
+ reason = params->u.resp.reason_code;
+ goto out_fail;
+ }
+ }
+
+ if (!params->sched_valid) {
+ wpa_printf(MSG_DEBUG, "NAN: NDL: no valid schedule");
+ reason = NAN_REASON_INVALID_PARAMETERS;
+ goto out_fail;
+ }
+
+ wpabuf_free(ndl->sched.elems);
+ os_memcpy(&ndl->sched, ¶ms->sched, sizeof(ndl->sched));
+ nan_ndl_sched_print(nan, &peer->ndl->sched);
+
+ /* Copy elems buffer */
+ if (params->sched.elems) {
+ ndl->sched.elems =
+ wpabuf_alloc_copy(wpabuf_head(params->sched.elems),
+ wpabuf_len(params->sched.elems));
+ if (!ndl->sched.elems) {
+ reason = NAN_REASON_UNSPECIFIED_REASON;
+ goto out_fail;
+ }
+ }
+
+ if (is_zero_ether_addr(ndl->ndc_id)) {
+ os_get_random(ndl->ndc_id, ETH_ALEN);
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: generated NDC ID " MACSTR,
+ MAC2STR(ndl->ndc_id));
+ }
+
+ ndl->local_qos.min_slots = params->qos.min_slots;
+ ndl->local_qos.max_latency = params->qos.max_latency;
+
+ if (ndl->state == NAN_NDL_STATE_NONE) {
+ ndl->dialog_token = ++nan->next_dialog_token;
+ 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->setup_reason = NAN_NDL_SETUP_REASON_NDP;
+
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: success: state=%s (%u). Dialog token=%u",
+ nan_ndl_state_str(ndl->state), ndl->state,
+ ndl->dialog_token);
+
+ return 0;
+out_fail:
+ wpa_printf(MSG_DEBUG, "NAN: NDL: Failed. reason=%u", reason);
+ if (ndl->state == NAN_NDL_STATE_REQ_RECV ||
+ ndl->state == NAN_NDL_STATE_RES_RECV) {
+ ndl->status = NAN_NDL_STATUS_REJECTED;
+ ndl->reason = reason;
+ ndl->send_naf_on_error = 1;
+ }
+
+ /* clear the NDL info but leave the state, status and reason, full
+ * cleanup would be done on Tx status handling
+ */
+ nan_ndl_clear(nan, peer);
+ return -1;
+}
+
+
+/*
+ * nan_ndl_setup_failure - Indicate failure during NDL setup
+ *
+ * @nan: NAN module context from nan_init()
+ * @peer: NAN peer.
+ * @reason: Failure reason
+ * @reset_state: Reset the NDL state if true.
+ */
+void nan_ndl_setup_failure(struct nan_data *nan, struct nan_peer *peer,
+ enum nan_reason reason, bool reset_state)
+{
+ struct nan_ndl *ndl = peer->ndl;
+
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: Setup failure: peer " MACSTR ". state=%s (%u). reason=%u",
+ MAC2STR(peer->nmi_addr), nan_ndl_state_str(ndl->state),
+ ndl->state, reason);
+
+ if (reset_state) {
+ nan_ndl_reset(nan, peer);
+ } else {
+ ndl->status = NAN_NDL_STATUS_REJECTED;
+ ndl->reason = reason;
+ }
+}
+
+
+static int nan_ndl_parse_ndc_attr(struct nan_data *nan,
+ const struct ieee80211_ndc *ndc_attr,
+ u16 ndc_attr_len, u8 *ndc_id,
+ const u8 **ndc_schedule,
+ u16 *ndc_schedule_len)
+{
+ u16 ext_ndc_len;
+
+ /* consider only the selected NDC */
+ if (!(ndc_attr->ctrl & NAN_NDC_CTRL_SELECTED))
+ return -1;
+
+ ext_ndc_len = ndc_attr_len - sizeof(*ndc_attr);
+
+ if (ext_ndc_len <= sizeof(struct nan_sched_entry)) {
+ wpa_printf(MSG_DEBUG, "NAN: NDL: Request with invalid len=%u",
+ ndc_attr_len);
+ return -1;
+ }
+
+ os_memcpy(ndc_id, ndc_attr->ndc_id, sizeof(ndc_attr->ndc_id));
+ *ndc_schedule_len = ext_ndc_len;
+ *ndc_schedule = (u8 *)(ndc_attr + 1);
+
+ wpa_printf(MSG_DEBUG, "NAN: NDL: ndc_id=" MACSTR " schedule_len=%u",
+ MAC2STR(ndc_id), *ndc_schedule_len);
+ return 0;
+}
+
+
+static int nan_ndl_parse_qos_attr(struct nan_data *nan,
+ struct ieee80211_nan_qos *qos_attr,
+ u16 qos_attr_len,
+ u8 *min_slots, u16 *max_latency)
+{
+ *min_slots = qos_attr->min_slots;
+ *max_latency = le_to_host16(qos_attr->max_latency);
+
+ wpa_printf(MSG_DEBUG, "NAN: QoS attr: min_slots=%u, max_latency=%u",
+ *min_slots, *max_latency);
+ return 0;
+}
+
+
+static int nan_ndl_attr_handle_req(struct nan_data *nan, struct nan_peer *peer,
+ const struct ndl_attr_params *params)
+{
+ struct nan_ndl *ndl;
+
+ wpa_printf(MSG_DEBUG, "NAN: NDL: Handle request");
+
+ if (peer->ndl) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: Request while another establishment is ongoing");
+ return -1;
+ }
+
+ if (params->status != NAN_NDL_STATUS_CONTINUED) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: Request with invalid status=%u",
+ params->status);
+ return -1;
+ }
+
+ peer->ndl = nan_ndl_alloc(nan);
+ if (!peer->ndl)
+ return -1;
+
+ ndl = peer->ndl;
+
+ ndl->status = NAN_NDL_STATUS_CONTINUED;
+
+ ndl->dialog_token = params->dialog_token;
+ ndl->max_idle_period = params->max_idle_period;
+ ndl->setup_reason = params->setup_reason;
+
+ if (!is_zero_ether_addr(params->ndc_id)) {
+ os_memcpy(ndl->ndc_id, params->ndc_id, sizeof(ndl->ndc_id));
+
+ if (params->ndc_sched && params->ndc_sched_len) {
+ os_free(ndl->ndc_sched);
+ ndl->ndc_sched_len = 0;
+
+ ndl->ndc_sched = os_memdup(params->ndc_sched,
+ params->ndc_sched_len);
+ if (!ndl->ndc_sched) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: Failed to copy NDC schedule");
+ goto fail;
+ }
+
+ ndl->ndc_sched_len = params->ndc_sched_len;
+ }
+ }
+
+ ndl->peer_qos.min_slots = params->min_slots;
+ ndl->peer_qos.max_latency = params->max_latency;
+
+ if (params->immut_sched && params->immut_sched_len) {
+ ndl->immut_sched = os_memdup(params->immut_sched,
+ params->immut_sched_len);
+ if (!ndl->immut_sched) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: Failed to copy immutable schedule");
+ goto fail;
+ }
+ ndl->immut_sched_len = params->immut_sched_len;
+ }
+
+ nan_ndl_set_state(nan, ndl, NAN_NDL_STATE_REQ_RECV);
+
+ wpa_printf(MSG_DEBUG, "NAN: NDL: Handle request done");
+ return 0;
+fail:
+ nan_ndl_reset(nan, peer);
+ return -1;
+}
+
+
+static int nan_ndl_attr_handle_resp(struct nan_data *nan, struct nan_peer *peer,
+ const struct ndl_attr_params *params)
+{
+ struct nan_ndl *ndl = peer->ndl;
+
+ wpa_printf(MSG_DEBUG, "NAN: NDL: Handle response");
+
+ if (!ndl) {
+ wpa_printf(MSG_DEBUG, "NAN: NDL: unexpected response");
+ return -1;
+ }
+
+ if (ndl->state != NAN_NDL_STATE_REQ_SENT) {
+ if (ndl->state != NAN_NDL_STATE_START) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: Response while state=%s (%u)",
+ nan_ndl_state_str(ndl->state), ndl->state);
+ return -1;
+ }
+
+ /*
+ * Due to races with the driver, it is possible that the
+ * response is received before an ACK is indicated. Allow the
+ * processing of the attribute, and if all parameters are OK,
+ * fast forward the state machine below.
+ */
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: Response received before Tx status.");
+ }
+
+ ndl->send_naf_on_error = 0;
+ ndl->status = params->status;
+ nan_ndl_set_state(nan, ndl, NAN_NDL_STATE_RES_RECV);
+
+ if (ndl->dialog_token != params->dialog_token) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: Resp: Invalid dialog token (%u != %u)",
+ ndl->dialog_token, params->dialog_token);
+ return -1;
+ }
+
+ if (params->status != NAN_NDL_STATUS_CONTINUED &&
+ params->status != NAN_NDL_STATUS_ACCEPTED) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: Resp: Rejected. status=%u, reason=%u",
+ params->status, params->reason);
+
+ ndl->reason = params->reason;
+ ndl->status = NAN_NDL_STATUS_REJECTED;
+ return 0;
+ }
+
+ if (is_zero_ether_addr(params->ndc_id)) {
+ wpa_printf(MSG_DEBUG, "NAN: NDL: Response without NDC");
+ return -1;
+ }
+
+ if (os_memcmp(ndl->ndc_id, params->ndc_id, sizeof(ndl->ndc_id)) != 0) {
+ wpa_printf(MSG_DEBUG, "NAN: NDL: Resp: ndc_id changed");
+ os_memcpy(ndl->ndc_id, params->ndc_id, sizeof(ndl->ndc_id));
+ }
+
+ 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 in case an ACCEPTED NDL and an NDC, 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;
+
+ ndl->ndc_sched = os_memdup(params->ndc_sched,
+ params->ndc_sched_len);
+ if (!ndl->ndc_sched) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: Resp: Failed to allocate NDC schedule");
+ goto fail;
+ }
+
+ ndl->ndc_sched_len = params->ndc_sched_len;
+ }
+
+ if (params->immut_sched && params->immut_sched_len) {
+ if (params->status == NAN_NDL_STATUS_ACCEPTED) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: Resp: Immutable not allowed with status == accept");
+ return -1;
+ }
+
+ os_free(ndl->immut_sched);
+ ndl->immut_sched_len = 0;
+ ndl->immut_sched = os_memdup(params->immut_sched,
+ params->immut_sched_len);
+ if (!ndl->immut_sched) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: Resp: fail allocate immutable schedule");
+ goto fail;
+ }
+ ndl->immut_sched_len = params->immut_sched_len;
+ }
+
+ wpa_printf(MSG_DEBUG, "NAN: NDL: Resp: status=%u", params->status);
+
+ ndl->status = params->status;
+ if (params->status == NAN_NDL_STATUS_ACCEPTED)
+ nan_ndl_set_state(nan, ndl, NAN_NDL_STATE_DONE);
+
+ return 0;
+fail:
+ nan_ndl_clear(nan, peer);
+ ndl->status = NAN_NDL_STATUS_REJECTED;
+ ndl->reason = NAN_REASON_RESOURCE_LIMITATION;
+ ndl->send_naf_on_error = 1;
+ return 0;
+}
+
+
+static int nan_ndl_attr_handle_conf(struct nan_data *nan, struct nan_peer *peer,
+ const struct ndl_attr_params *params)
+{
+ struct nan_ndl *ndl = peer->ndl;
+
+ if (!ndl) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: Confirm without an NDL");
+ return -1;
+ }
+
+ if (ndl->state != NAN_NDL_STATE_RES_SENT) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: Confirm while not expecting one");
+
+ if (ndl->state != NAN_NDL_STATE_REQ_RECV ||
+ ndl->status != NAN_NDL_STATUS_CONTINUED)
+ return -1;
+
+ /*
+ * Due to races with the driver, it is possible that the
+ * response is received before an ACK is indicated. Allow the
+ * processing of the attribute, and if all parameters are OK,
+ * fast forward the state machine below.
+ */
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: Confirm received before Tx status");
+ }
+
+ ndl->send_naf_on_error = 0;
+ ndl->status = params->status;
+ nan_ndl_set_state(nan, ndl, NAN_NDL_STATE_CON_RECV);
+
+ if (params->status != NAN_NDL_STATUS_ACCEPTED) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: Confirm was not accepted. status=%u, reason=%u",
+ params->status, params->reason);
+ ndl->status = NAN_NDL_STATUS_REJECTED;
+ ndl->reason = params->reason;
+ return 0;
+ }
+
+ if (ndl->dialog_token != params->dialog_token) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: Confirm with invalid dialog token (%u != %u)",
+ ndl->dialog_token, params->dialog_token);
+ 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));
+ }
+
+ 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;
+
+ ndl->ndc_sched = os_memdup(params->ndc_sched,
+ params->ndc_sched_len);
+ if (!ndl->ndc_sched) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: Failed to allocate NDC schedule");
+ goto fail;
+ }
+ ndl->ndc_sched_len = params->ndc_sched_len;
+ }
+
+ /* TODO: validate that the immutable schedule is covered by the local
+ * device committed availability.
+ */
+ if (params->immut_sched && params->immut_sched_len) {
+ os_free(ndl->immut_sched);
+ ndl->immut_sched_len = 0;
+
+ ndl->immut_sched = os_memdup(params->immut_sched,
+ params->immut_sched_len);
+ if (!ndl->immut_sched) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: Failed to allocate immutable schedule");
+ goto fail;
+ }
+ ndl->immut_sched_len = params->immut_sched_len;
+ }
+
+ nan_ndl_set_state(nan, ndl, NAN_NDL_STATE_DONE);
+ return 0;
+
+fail:
+ ndl->reason = NAN_REASON_RESOURCE_LIMITATION;
+ ndl->send_naf_on_error = 1;
+ return -1;
+}
+
+
+/*
+ * nan_ndl_handle_ndl_attr - Handle ndl attribute and update local state
+ *
+ * @nan: NAN module context from nan_init()
+ * @peer: the peer from which the original message was received
+ * @msg: parsed nan action frame
+ * Returns: 0 on success, negative on failure to parse the attributes etc.
+ *
+ * As part of the NDL attribute handling, the function also parses NDC and QoS
+ * attributes.
+ */
+int nan_ndl_handle_ndl_attr(struct nan_data *nan, struct nan_peer *peer,
+ struct nan_msg *msg)
+{
+ struct ieee80211_ndl *ndl_attr;
+ const u8 *ndl_attr_ext;
+ struct ndl_attr_params params;
+ u16 ndl_attr_len, ndl_attr_ext_len;
+ u16 control;
+ u8 ctrl_setup_reason;
+ u8 ndc_ok;
+ int ret;
+
+ os_memset(¶ms, 0, sizeof(params));
+
+ if (!msg || !peer)
+ return -1;
+
+ if (peer->ndl && peer->ndl->state == NAN_NDL_STATE_DONE) {
+ wpa_printf(MSG_DEBUG, "NAN: NDL: NDL already done");
+ return 0;
+ }
+
+ if (!msg->attrs.ndl)
+ return -1;
+
+ ndl_attr = (struct ieee80211_ndl *)msg->attrs.ndl;
+ ndl_attr_len = msg->attrs.ndl_len;
+
+ ndl_attr_ext = (u8 *)(ndl_attr + 1);
+ ndl_attr_ext_len = ndl_attr_len - sizeof(struct ieee80211_ndl);
+
+ params.type = BITS(ndl_attr->type_and_status, NAN_NDL_TYPE_MASK,
+ NAN_NDL_TYPE_POS);
+
+ params.status = BITS(ndl_attr->type_and_status, NAN_NDL_STATUS_MASK,
+ NAN_NDL_STATUS_POS);
+ control = le_to_host16(ndl_attr->ctrl);
+
+ if (peer->ndl)
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: curr: state=%s (%d), status=%d",
+ nan_ndl_state_str(peer->ndl->state),
+ peer->ndl->state, peer->ndl->status);
+ else
+ wpa_printf(MSG_DEBUG, "NAN: NDL: NDL does not exist");
+
+ params.dialog_token = ndl_attr->dialog_token;
+
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: dialog=%u, type=0x%x, status=0x%x, ctrl=0x%x",
+ params.dialog_token, params.type, params.status, control);
+
+ if (control & NAN_NDL_CTRL_PEER_ID_PRESENT) {
+ if (ndl_attr_ext_len < 1) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: Request with invalid len=%u, control=0x%x",
+ ndl_attr_len, control);
+ return -1;
+ }
+
+ /*
+ * Peer ID is not longer used. It is considered as reserved in
+ * Wi-Fi Aware Specification v4.0 Table 107. Just skip it
+ */
+ ndl_attr_ext++;
+ ndl_attr_ext_len--;
+ }
+
+ if (control & NAN_NDL_CTRL_MAX_IDLE_PERIOD_PRESENT) {
+ if (ndl_attr_ext_len <= 2) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: Request with invalid len=%u, control=0x%x",
+ ndl_attr_len, control);
+ return -1;
+ }
+
+ params.max_idle_period = WPA_GET_LE16(ndl_attr_ext);
+ ndl_attr_ext += 2;
+ ndl_attr_ext_len -= 2;
+ }
+
+ ndc_ok = 1;
+ if (control & NAN_NDL_CTRL_NDC_ATTR_PRESENT) {
+ struct nan_attrs_entry *n;
+
+ ret = -1;
+ dl_list_for_each(n, &msg->attrs.ndc, struct nan_attrs_entry,
+ list) {
+ ret = nan_ndl_parse_ndc_attr(nan,
+ (struct ieee80211_ndc *)n->ptr,
+ n->len,
+ params.ndc_id,
+ ¶ms.ndc_sched,
+ ¶ms.ndc_sched_len);
+ if (!ret)
+ break;
+ }
+
+ if (ret)
+ ndc_ok = 0;
+ } else if (params.type == NAN_NDL_TYPE_RESPONSE) {
+ ndc_ok = 0;
+ }
+
+ if (!ndc_ok) {
+ wpa_printf(MSG_DEBUG, "NAN: NDL: Missing valid selected NDC");
+ return -1;
+ }
+
+ params.min_slots = NAN_QOS_MIN_SLOTS_NO_PREF;
+ params.max_latency = NAN_QOS_MAX_LATENCY_NO_PREF;
+ if (control & NAN_NDL_CTRL_NDL_QOS_ATTR_PRESENT) {
+ ret = nan_ndl_parse_qos_attr(nan,
+ (struct ieee80211_nan_qos *)msg->attrs.ndl_qos,
+ msg->attrs.ndl_qos_len,
+ ¶ms.min_slots,
+ ¶ms.max_latency);
+ if (ret)
+ return ret;
+ }
+
+ if (control & NAN_NDL_CTRL_IMMUT_SCHED_PRESENT) {
+ if (ndl_attr_ext_len <= sizeof(struct nan_sched_entry)) {
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: Request with invalid len=%u, control=0x%x",
+ ndl_attr_len, control);
+ return -1;
+ }
+
+ params.immut_sched = ndl_attr_ext;
+ params.immut_sched_len = ndl_attr_ext_len;
+ }
+
+ ctrl_setup_reason = (control & NAN_NDL_CTRL_NDL_SETUP_REASON_MASK) >>
+ NAN_NDL_CTRL_NDL_SETUP_REASON_POS;
+
+ if (ctrl_setup_reason == NAN_NDL_CTRL_NDL_SETUP_REASON_NDP) {
+ params.setup_reason = NAN_NDL_SETUP_REASON_NDP;
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: Unknown setup reason. Assume NDP");
+ params.setup_reason = NAN_NDL_SETUP_REASON_NDP;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "NAN: NDL: max_idle_period=%u, immutable len=%u",
+ params.max_idle_period, params.immut_sched_len);
+
+ switch (params.type) {
+ case NAN_NDL_TYPE_REQUEST:
+ return nan_ndl_attr_handle_req(nan, peer, ¶ms);
+ case NAN_NDL_TYPE_RESPONSE:
+ return nan_ndl_attr_handle_resp(nan, peer, ¶ms);
+ case NAN_NDL_TYPE_CONFIRM:
+ return nan_ndl_attr_handle_conf(nan, peer, ¶ms);
+ default:
+ return -1;
+ }
+}
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index c8480a8441..2fdee32f1b 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -340,6 +340,7 @@ OBJS += ../src/nan/nan.o
OBJS += ../src/common/nan_de.o
OBJS += ../src/nan/nan_util.o
OBJS += ../src/nan/nan_ndp.o
+OBJS += ../src/nan/nan_ndl.o
endif
ifdef CONFIG_OWE
--
2.49.0
More information about the Hostap
mailing list