[RFC 09/56] NAN: Support handling NDP state machine and operations

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


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

Add support for managing NDP state and operations,
including parsing NDP attributes, construction of NDP
attribute based on state etc.

This only includes the NDP state machine, later patches
will also introduce the NDL state machine and overall flow
to establish NAN Data Path with a peer NAN device.
---
 src/nan/Makefile        |   2 +-
 src/nan/nan.c           |  21 +-
 src/nan/nan.h           | 162 +++++++++
 src/nan/nan_i.h         |  28 ++
 src/nan/nan_ndp.c       | 759 ++++++++++++++++++++++++++++++++++++++++
 src/utils/common.h      |   2 +
 wpa_supplicant/Makefile |   1 +
 7 files changed, 973 insertions(+), 2 deletions(-)
 create mode 100644 src/nan/nan_ndp.c

diff --git a/src/nan/Makefile b/src/nan/Makefile
index 2c58f9cb6c..64e1ae4849 100644
--- a/src/nan/Makefile
+++ b/src/nan/Makefile
@@ -1,3 +1,3 @@
-LIB_OBJS= nan.o nan_util.o
+LIB_OBJS= nan.o nan_util.o nan_ndp.o
 
 include ../lib.rules
diff --git a/src/nan/nan.c b/src/nan/nan.c
index 7b28df54b8..07aab530cb 100644
--- a/src/nan/nan.c
+++ b/src/nan/nan.c
@@ -63,7 +63,7 @@ static void nan_del_peer(struct nan_data *nan, struct nan_peer *peer)
 	if (peer->ndp_setup.ndp) {
 		wpa_printf(MSG_DEBUG,
 			   "NAN: Peer delete while ndp setup is WIP");
-		os_free(peer->ndp_setup.ndp);
+		nan_ndp_setup_reset(nan, peer);
 	}
 
 	dl_list_del(&peer->list);
@@ -243,3 +243,22 @@ int nan_add_peer(struct nan_data *nan, const u8 *addr,
 	os_get_reltime(&peer->last_seen);
 	return 0;
 }
+
+
+/*
+ * nan_publish_instance_id_valid - Check is instance ID is a valid publish ID
+ *
+ * @nan: NAN module context from nan_init()
+ * @instance_id: Instance ID to check
+ * @service_id: On return would hold the service ID if instance ID is valid
+ * Return true iff there is a local publish service ID with the given instance
+ * ID.
+ */
+bool nan_publish_instance_id_valid(struct nan_data *nan, u8 instance_id,
+				   u8 *service_id)
+{
+	/* TODO: Need implement this logic */
+	wpa_printf(MSG_DEBUG,
+		   "NAN: TODO: Publish instance ID validation not implemented");
+	return true;
+}
diff --git a/src/nan/nan.h b/src/nan/nan.h
index 2f4aa5e078..74c110b5e3 100644
--- a/src/nan/nan.h
+++ b/src/nan/nan.h
@@ -9,7 +9,10 @@
 #ifndef NAN_H
 #define NAN_H
 
+#include "common/nan_defs.h"
+
 struct nan_cluster_config;
+enum nan_reason;
 
 /*
  * struct nan_device_capabilities - NAN device capabilities.
@@ -42,6 +45,125 @@ struct nan_qos {
 	u16 max_latency;
 };
 
+/*
+ * enum nan_ndp_action - NDP action
+ *
+ * @NAN_NDP_ACTION_REQ: Request NDP establishment.
+ * @NAN_NDP_ACTION_RESP: Response to NDP establishment request.
+ * @NAN_NDP_ACTION_CONF: Confirm NDP establishment.
+ * @NAN_NDP_ACTION_TERM: Request NDP termination.
+ */
+enum nan_ndp_action {
+	NAN_NDP_ACTION_REQ = 0,
+	NAN_NDP_ACTION_RESP,
+	NAN_NDP_ACTION_CONF,
+	NAN_NDP_ACTION_TERM,
+};
+
+/**
+ * struct nan_ndp_id - Unique identifier of an NDP
+ *
+ * @peer_nmi: peer NAN Management Interface (NMI)
+ * @init_ndi: initiator NAN Data Interface (NDI)
+ * @id: NDP identifier
+ */
+struct nan_ndp_id {
+	u8 peer_nmi[ETH_ALEN];
+	u8 init_ndi[ETH_ALEN];
+	u8 id;
+};
+
+/*
+ * struct nan_ndp_params - Holds the ndp parameters for setting up or
+ * terminating an NDP.
+ *
+ * @type: The request type. See &enum nan_ndp_action
+ * @ndp_id: The NDP identifier
+ * @qos: The NDP QoS parameters. In case there is no requirement for
+ *     max_latency, max_latency should be set to NAN_QOS_MAX_LATENCY_NO_PREF.
+ *     Should be set only with NAN_NDP_ACTION_REQ and NAN_NDP_ACTION_RESP.
+ *     Ignored for other types.
+ * @ssi: Service specific information. Should be set only with
+ *     NAN_NDP_ACTION_REQ and NAN_NDP_ACTION_RESP. Ignored for other types.
+ * @ssi_len: Service specific information length
+ * @publish_inst_id: Identifier for the instance of the Publisher function
+ *     associated with the data path setup request.
+ * @service_id: Service identifier of the service associated with the data path
+ *     setup request.
+ * @resp_ndi: In case of successful response, the responder's NDI. In case of
+ *     response to a counter proposal, the initiator's NDI (the one used with
+ *     NAN_NDP_ACTION_REQ).
+ * @status: Response status.
+ * @reason_code: In case of rejected response, the rejection reason.
+ */
+struct nan_ndp_params {
+	enum nan_ndp_action type;
+
+	struct nan_ndp_id ndp_id;
+	struct nan_qos qos;
+	const u8 *ssi;
+	u16 ssi_len;
+
+	union {
+		struct nan_ndp_setup_req {
+			u8 publish_inst_id;
+			u8 service_id[NAN_SERVICE_ID_LEN];
+		} req;
+
+		/*
+		 * 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;
+};
+
+/*
+ * struct nan_ndp_connection_params - Parameters for NDP connection
+ *
+ * @ndp_id: NDP identifier
+ * @peer_ndi: peer NDI MAC address
+ * @local_ndi: local NDI MAC address
+ * @ssi: service specific information
+ * @ssi_len: service specific information length
+ */
+struct nan_ndp_connection_params {
+	struct nan_ndp_id ndp_id;
+	const u8 *peer_ndi;
+	const u8 *local_ndi;
+	const u8 *ssi;
+	size_t ssi_len;
+};
+
+/*
+ * struct nan_ndp_action_notif_params - Parameters for NDP action notification
+ *
+ * @ndp_id: NDP identifier
+ * @is_request: Whether the data is associated with an NDP request frame (true)
+ *     or with an NDP response (false).
+ * @ndp_status: NDP status
+ * @ndl_status: NDL status
+ * @publish_inst_id: identifier for the publish instance function
+ * @ssi: service specific information
+ * @ssi_len: service specific information length
+ */
+struct nan_ndp_action_notif_params {
+	struct nan_ndp_id ndp_id;
+	bool is_request;
+
+	enum nan_ndp_status ndp_status;
+	enum nan_ndl_status ndl_status;
+
+	u8 publish_inst_id;
+	const u8 *ssi;
+	size_t ssi_len;
+};
+
 struct nan_config {
 	void *cb_ctx;
 
@@ -67,6 +189,43 @@ struct nan_config {
 	 */
 	int (*update_config)(void *ctx, struct nan_cluster_config *config);
 
+	/**
+	 * ndp_action_notif - Notify NDP action is required
+	 *
+	 * @ctx: Callback context from cb_ctx
+	 * @params: NDP action notification parameters
+	 *
+	 * A notification sent when an NDP establishment frame is received, and
+	 * upper layer input is required to continue the flow.
+	 */
+	void (*ndp_action_notif)(void *ctx,
+				 struct nan_ndp_action_notif_params *params);
+
+	/**
+	 * ndp_connected - Notify that NDP was successfully connected
+	 *
+	 * @ctx: Callback context from cb_ctx
+	 * @params: NDP connection parameters
+	 */
+	void (*ndp_connected)(void *ctx,
+			      struct nan_ndp_connection_params *params);
+
+	/**
+	 * ndp_disconnected - Notify that NDP was disconnected.
+	 *
+	 * @ctx: Callback context from cb_ctx
+	 * @ndp_id: NDP identifier
+	 * @local_ndi: Local NDI MAC address
+	 * @peer_ndi: Peer NDI MAC address
+	 * @reason: Disconnection reason
+	 *
+	 * This callback notifies that an NDP has been disconnected. It can be
+	 * called both during NDP establishment (indicating failure) or after
+	 * successful establishment (indicating termination).
+	 */
+	void (*ndp_disconnected)(void *ctx, struct nan_ndp_id *ndp_id,
+				 const u8 *local_ndi, const u8 *peer_ndi,
+				 enum nan_reason reason);
 };
 
 struct nan_data * nan_init(const struct nan_config *cfg);
@@ -79,4 +238,7 @@ void nan_flush(struct nan_data *nan);
 int nan_add_peer(struct nan_data *nan, const u8 *addr,
 		 const u8 *device_attrs, size_t device_attrs_len);
 
+bool nan_publish_instance_id_valid(struct nan_data *nan, u8 instance_id,
+				   u8 *service_id);
+
 #endif /* NAN_H */
diff --git a/src/nan/nan_i.h b/src/nan/nan_i.h
index ae8833c94e..52f1f59c78 100644
--- a/src/nan/nan_i.h
+++ b/src/nan/nan_i.h
@@ -82,6 +82,9 @@ struct nan_ndp {
  * @publisher_inst_id: Publish function instance ID
  * @conf_req: True iff the NDP exchange requires confirm message.
  * @reason: Reject reason. Only valid when status is rejected.
+ * @ssi: Service specific information
+ * @ssi_len: Service specific information length
+ * @service_id: Service ID of the service used for NDP setup
  */
 struct nan_ndp_setup {
 	struct nan_ndp *ndp;
@@ -91,6 +94,10 @@ struct nan_ndp_setup {
 	u8 publish_inst_id;
 	bool conf_req;
 	enum nan_reason reason;
+	u8 *ssi;
+	u16 ssi_len;
+
+	u8 service_id[NAN_SERVICE_ID_LEN];
 };
 
 /**
@@ -117,11 +124,18 @@ struct nan_peer {
  * @cfg: Pointer to the NAN configuration structure.
  * @nan_started: Flag indicating if NAN has been started.
  * @peer_list: List of known peers.
+ * @ndp_id_counter: NDP identifier counter. Incremented for each NDP request,
+ *     and is used to set ndp_id in &struct nan_ndp.
+ * @next_dialog_token: Dialog token for NDP and NDL negotiations. Incremented
+ *     for each NDP and NDL request.
  */
 struct nan_data {
 	struct nan_config *cfg;
 	u8 nan_started:1;
 	struct dl_list peer_list;
+
+	u8 ndp_id_counter;
+	u8 next_dialog_token;
 };
 
 struct nan_attrs_entry {
@@ -173,4 +187,18 @@ int nan_parse_naf(struct nan_data *nan, const struct ieee80211_mgmt *mgmt,
 		  size_t len, struct nan_msg *msg);
 void nan_attrs_clear(struct nan_data *nan, struct nan_attrs *attrs);
 void nan_add_dev_capa_attr(struct nan_data *nan, struct wpabuf *buf);
+
+int nan_ndp_setup_req(struct nan_data *nan, struct nan_peer *peer,
+		      struct nan_ndp_params *params);
+int nan_ndp_setup_resp(struct nan_data *nan, struct nan_peer *peer,
+		       struct nan_ndp_params *params);
+int nan_ndp_handle_ndp_attr(struct nan_data *nan, struct nan_peer *peer,
+			    struct nan_msg *msg);
+int nan_ndp_add_ndp_attr(struct nan_data *nan, struct nan_peer *peer,
+			 struct wpabuf *buf);
+void nan_ndp_setup_reset(struct nan_data *nan, struct nan_peer *peer);
+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);
 #endif
diff --git a/src/nan/nan_ndp.c b/src/nan/nan_ndp.c
new file mode 100644
index 0000000000..c18e9e8ef5
--- /dev/null
+++ b/src/nan/nan_ndp.c
@@ -0,0 +1,759 @@
+/*
+ * Wi-Fi Aware - NAN Data Path
+ * 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"
+
+static const char *nan_ndp_state_str(enum nan_ndp_state state)
+{
+#define C2S(x) case x: return #x;
+	switch (state) {
+	C2S(NAN_NDP_STATE_NONE)
+	C2S(NAN_NDP_STATE_START)
+	C2S(NAN_NDP_STATE_REQ_SENT)
+	C2S(NAN_NDP_STATE_REQ_RECV)
+	C2S(NAN_NDP_STATE_RES_SENT)
+	C2S(NAN_NDP_STATE_RES_RECV)
+	C2S(NAN_NDP_STATE_CON_SENT)
+	C2S(NAN_NDP_STATE_CON_RECV)
+	C2S(NAN_NDP_STATE_DONE)
+	default:
+		return "Invalid NAN NDP state";
+	}
+}
+
+
+static void nan_ndp_set_state(struct nan_data *nan,
+			      struct nan_ndp_setup *ndp_setup,
+			      enum nan_ndp_state state)
+{
+	wpa_printf(MSG_DEBUG,
+		   "NAN: NDP: state: %s (%u) --> %s (%u)",
+		   nan_ndp_state_str(ndp_setup->state),
+		   ndp_setup->state, nan_ndp_state_str(state),
+		   state);
+
+	ndp_setup->state = state;
+}
+
+
+static struct nan_ndp *
+nan_ndp_alloc(struct nan_data *nan, struct nan_peer *peer,
+	      u8 initiator, u8 *init_ndi, u8 ndp_id, u8 min_slots,
+	      u16 max_latency)
+{
+	struct nan_ndp *ndp = os_zalloc(sizeof(*ndp));
+
+	if (!ndp)
+		return NULL;
+
+	ndp->peer = peer;
+	ndp->initiator = initiator;
+	ndp->ndp_id = ndp_id;
+
+	os_memcpy(ndp->init_ndi, init_ndi,
+		  sizeof(ndp->init_ndi));
+
+	ndp->qos.min_slots = min_slots;
+	ndp->qos.max_latency = max_latency;
+
+	return ndp;
+}
+
+
+static int nan_ndp_ssi(struct nan_data *nan, struct nan_ndp_setup *ndp_setup,
+		       const u8 *ssi, u16 ssi_len)
+{
+	os_free(ndp_setup->ssi);
+	ndp_setup->ssi = NULL;
+	ndp_setup->ssi_len = 0;
+
+	if (!ssi || !ssi_len)
+		return 0;
+
+	ndp_setup->ssi = os_memdup(ssi, ssi_len);
+	if (!ndp_setup->ssi) {
+		wpa_printf(MSG_DEBUG, "NAN: NDP: Failed to allocate NDP ssi");
+		return -1;
+	}
+
+	ndp_setup->ssi_len = ssi_len;
+	return 0;
+}
+
+
+/*
+ * nan_ndp_setup_req - Start handling of NDP setup request
+ *
+ * On successful request, the data structures would be ready to
+ * continue the NDP establishment.
+ *
+ * @nan: NAN module context from nan_init()
+ * @peer: The peer to initiate the NDP setup with
+ * @params: NDP setup request parameters
+ * Returns: 0 on success, negative on failure
+ */
+int nan_ndp_setup_req(struct nan_data *nan, struct nan_peer *peer,
+		      struct nan_ndp_params *params)
+{
+	int ret;
+
+	if (peer->ndp_setup.ndp) {
+		wpa_printf(MSG_DEBUG, "NAN: NDP: already WIP with peer");
+		return -1;
+	}
+
+	peer->ndp_setup.ndp = nan_ndp_alloc(nan, peer, 1,
+					    params->ndp_id.init_ndi,
+					    params->ndp_id.id,
+					    params->qos.min_slots,
+					    params->qos.max_latency);
+	if (!peer->ndp_setup.ndp) {
+		wpa_printf(MSG_DEBUG, "NAN: NDP: Failed allocation");
+		return -1;
+	}
+
+	peer->ndp_setup.dialog_token = ++nan->next_dialog_token;
+	peer->ndp_setup.publish_inst_id = params->u.req.publish_inst_id;
+
+	/* Require confirmation for all locally initiated NDPs */
+	peer->ndp_setup.conf_req = 1;
+
+	/* Store service specific information */
+	ret = nan_ndp_ssi(nan, &peer->ndp_setup, params->ssi, params->ssi_len);
+	if (ret) {
+		os_free(peer->ndp_setup.ndp);
+		peer->ndp_setup.ndp = NULL;
+		return ret;
+	}
+
+	nan_ndp_set_state(nan, &peer->ndp_setup, NAN_NDP_STATE_START);
+	peer->ndp_setup.status = NAN_NDP_STATUS_CONTINUED;
+	return 0;
+}
+
+
+/*
+ * nan_ndp_setup_resp - Handle higher layer response for an NDP request
+ *
+ * @nan: NAN module context from nan_init()
+ * @peer: Peer that originated the NDP setup request
+ * @params: NDP setup parameters
+ * Returns: 0 on success, negative on failure
+ *
+ * The response status can be either ACCEPTED or REJECTED. The internal logic of
+ * the NDP state machine adjusts its own state according to the response status
+ * and its internal state.
+ */
+int nan_ndp_setup_resp(struct nan_data *nan, struct nan_peer *peer,
+		       struct nan_ndp_params *params)
+{
+	int ret;
+
+	if (!peer->ndp_setup.ndp ||
+	    peer->ndp_setup.ndp->ndp_id != params->ndp_id.id ||
+	    os_memcmp(peer->ndp_setup.ndp->init_ndi,
+		      params->ndp_id.init_ndi, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: NDP: No matching NDP found for NDP response");
+		return -1;
+	}
+
+	if (peer->ndp_setup.state != NAN_NDP_STATE_REQ_RECV) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: NDP: Unexpected state for response");
+		return -1;
+	}
+
+	if (params->u.resp.status != NAN_NDP_STATUS_ACCEPTED &&
+	    params->u.resp.status != NAN_NDP_STATUS_REJECTED) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: NDP: Unexpected response status=%u",
+			params->u.resp.status);
+		return -1;
+	}
+
+	/* Store service specific information */
+	ret = nan_ndp_ssi(nan, &peer->ndp_setup, params->ssi, params->ssi_len);
+	if (ret)
+		return ret;
+
+	/* TODO: In case of security and status accept, need to change to
+	 * continue
+	 */
+	peer->ndp_setup.status = params->u.resp.status;
+	peer->ndp_setup.reason = params->u.resp.reason_code;
+
+	if (peer->ndp_setup.status == NAN_NDP_STATUS_ACCEPTED) {
+		if (peer->ndp_setup.conf_req)
+			peer->ndp_setup.status = NAN_NDP_STATUS_CONTINUED;
+
+		os_memcpy(peer->ndp_setup.ndp->resp_ndi,
+			  params->u.resp.resp_ndi, ETH_ALEN);
+	}
+
+	return 0;
+}
+
+
+static int nan_ndp_attr_handle_req(struct nan_data *nan, struct nan_peer *peer,
+				   struct ieee80211_ndp *ndp_attr, u16 ndp_len,
+				   u8 status)
+{
+	struct nan_ndp_setup *ndp_setup = &peer->ndp_setup;
+	u16 exp_len = sizeof(struct ieee80211_ndp);
+	u8 publish_inst_id;
+
+	if (ndp_setup->ndp) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: NDP: req: While already WIP with peer");
+		return -1;
+	}
+
+	if (!(ndp_attr->ndp_ctrl & NAN_NDP_CTRL_PUBLISH_ID_PRESENT)) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: NDP: req: Without publish ID");
+		return -1;
+	}
+
+	exp_len++;
+	if (ndp_len < exp_len) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: NDP: req: Length too short: ndp_len=%u, exp_len=%u",
+			   ndp_len, exp_len);
+		return -1;
+	}
+
+	publish_inst_id = *ndp_attr->optional;
+	if (!nan_publish_instance_id_valid(nan, publish_inst_id,
+					   ndp_setup->service_id)) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: NDP: req: Invalid publish instance ID=%u",
+			publish_inst_id);
+		return -1;
+	}
+
+	/* Note: the qos setting should be set in the response */
+	ndp_setup->ndp = nan_ndp_alloc(nan, peer, 0,
+				       ndp_attr->initiator_ndi,
+				       ndp_attr->ndp_id,
+				       NAN_QOS_MIN_SLOTS_NO_PREF,
+				       NAN_QOS_MAX_LATENCY_NO_PREF);
+	if (!ndp_setup->ndp)
+		return -1;
+
+	nan_ndp_set_state(nan, ndp_setup, NAN_NDP_STATE_REQ_RECV);
+
+	ndp_setup->status = NAN_NDP_STATUS_CONTINUED;
+	ndp_setup->conf_req =
+		!!(ndp_attr->ndp_ctrl & NAN_NDP_CTRL_CONFIRM_REQUIRED);
+	ndp_setup->dialog_token = ndp_attr->dialog_token;
+	ndp_setup->publish_inst_id = publish_inst_id;
+
+	/* Handle service specific information */
+	ndp_len -= exp_len;
+
+	if (ndp_attr->ndp_ctrl & NAN_NDP_CTRL_SPEC_INFO_PRESENT && ndp_len) {
+		int ret;
+
+		wpa_printf(MSG_DEBUG,
+			   "NAN: NDP: req: Handle NDP service specific information");
+		ret = nan_ndp_ssi(nan, &peer->ndp_setup,
+				  (u8 *)ndp_attr->optional + 1,
+				  ndp_len);
+		if (ret)
+			wpa_printf(MSG_DEBUG,
+				   "NAN: NDP: req: Failed to save ssi. continue");
+	}
+
+	return 0;
+}
+
+
+static int nan_ndp_attr_handle_res(struct nan_data *nan, struct nan_peer *peer,
+				   struct ieee80211_ndp *ndp_attr, u16 ndp_len,
+				   u8 status)
+{
+	struct nan_ndp_setup *ndp_setup = &peer->ndp_setup;
+	u16 opt_len;
+
+	if (!ndp_setup->ndp) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: NDP: resp: While no NDP WIP with peer");
+		return -1;
+	}
+
+	if (ndp_setup->state != NAN_NDP_STATE_REQ_SENT) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: NDP: resp: While not expecting one");
+
+		if (ndp_setup->state != NAN_NDP_STATE_START)
+			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: NDP: resp: Received before Tx status.");
+	}
+
+	if (ndp_attr->ndp_ctrl & NAN_NDP_CTRL_PUBLISH_ID_PRESENT) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: NDP: resp: Unexpected publish ID");
+		return -1;
+	}
+
+	opt_len = 0;
+	if (status == NAN_NDP_STATUS_CONTINUED ||
+	    status == NAN_NDP_STATUS_ACCEPTED) {
+		if (!(ndp_attr->ndp_ctrl &
+		      NAN_NDP_CTRL_RESPONDER_NDI_PRESENT)) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: NDP: resp: without responder NDI");
+			return -1;
+		}
+		opt_len += ETH_ALEN;
+	}
+
+	if (ndp_len < (sizeof(struct ieee80211_ndp) + opt_len)) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: NDP: resp: Length too short");
+		return -1;
+	}
+
+	if (ndp_setup->ndp->ndp_id != ndp_attr->ndp_id ||
+	    ndp_setup->dialog_token != ndp_attr->dialog_token ||
+	    os_memcmp(ndp_setup->ndp->init_ndi, ndp_attr->initiator_ndi,
+		      ETH_ALEN)) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: NDP: resp: Invalid NDP ID, dialog token or addr");
+		return -1;
+	}
+
+	if (ndp_setup->state == NAN_NDP_STATE_START) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: NDP: resp: Continue though no TX status yet.");
+		nan_ndp_set_state(nan, &peer->ndp_setup,
+				  NAN_NDP_STATE_REQ_SENT);
+	}
+
+	ndp_setup->status = status;
+
+	if (status == NAN_NDP_STATUS_REJECTED) {
+		ndp_setup->reason = ndp_attr->reason_code;
+		nan_ndp_set_state(nan, &peer->ndp_setup,
+				  NAN_NDP_STATE_DONE);
+		goto store_ssi;
+	}
+
+	if (((ndp_attr->ndp_ctrl & NAN_NDP_CTRL_SECURITY_PRESENT) ||
+	     ndp_setup->conf_req) && status != NAN_NDP_STATUS_CONTINUED) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: NDP: Security present and status != continue");
+		return -1;
+	}
+
+	if (status == NAN_NDP_STATUS_ACCEPTED)
+		nan_ndp_set_state(nan, &peer->ndp_setup,
+				  NAN_NDP_STATE_DONE);
+	else
+		nan_ndp_set_state(nan, &peer->ndp_setup,
+				  NAN_NDP_STATE_RES_RECV);
+
+	os_memcpy(ndp_setup->ndp->resp_ndi, &ndp_attr->optional[0],
+		  sizeof(ndp_setup->ndp->resp_ndi));
+
+	/*
+	 * In case that security is not configured, move the status to accepted.
+	 * The state machine would transition to the 'done' state after the
+	 * confirm is acked.
+	 * TODO: Once security is configured, need to validate the security must
+	 * be present.
+	 */
+	if (!(ndp_attr->ndp_ctrl & NAN_NDP_CTRL_SECURITY_PRESENT))
+		ndp_setup->status = NAN_NDP_STATUS_ACCEPTED;
+
+store_ssi:
+	/* Handle service specific information */
+	ndp_len -= (sizeof(struct ieee80211_ndp) + opt_len);
+	if (ndp_attr->ndp_ctrl & NAN_NDP_CTRL_SPEC_INFO_PRESENT && ndp_len) {
+		int ret;
+
+		wpa_printf(MSG_DEBUG, "NAN: NDP: resp: Handle NDP ssi");
+		ret = nan_ndp_ssi(nan, &peer->ndp_setup,
+				  ndp_attr->optional + opt_len,
+				  ndp_len);
+		if (ret)
+			wpa_printf(MSG_DEBUG,
+				   "NAN: NDP: resp: Failed to save ssi. continue");
+	}
+
+	return 0;
+}
+
+
+static int nan_ndp_attr_handle_confirm(struct nan_data *nan,
+				       struct nan_peer *peer,
+				       struct ieee80211_ndp *ndp_attr,
+				       u8 status)
+{
+	struct nan_ndp_setup *ndp_setup = &peer->ndp_setup;
+
+	if (!ndp_setup->ndp) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: NDP: Received confirm while no NDP WIP with peer");
+		return -1;
+	}
+
+	if (ndp_setup->state != NAN_NDP_STATE_RES_SENT) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: NDP: confirm: While not expecting one");
+
+		if (ndp_setup->state != NAN_NDP_STATE_REQ_RECV ||
+		    ndp_setup->status != NAN_NDP_STATUS_CONTINUED)
+			return -1;
+
+		/*
+		 * Due to races with the driver, it is possible that the
+		 * confirm 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: NDP: Confirm received before Tx status.");
+	}
+
+	if (ndp_setup->ndp->ndp_id != ndp_attr->ndp_id ||
+	    ndp_setup->dialog_token != ndp_attr->dialog_token ||
+	    os_memcmp(ndp_setup->ndp->init_ndi, ndp_attr->initiator_ndi,
+		      ETH_ALEN)) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: NDP: confirm: Invalid NDP ID, dialog token or init ID");
+		return -1;
+	}
+
+	if ((ndp_attr->ndp_ctrl & NAN_NDP_CTRL_SECURITY_PRESENT) &&
+	    status != NAN_NDP_STATUS_CONTINUED) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: NDP: confirm: status != continue with security");
+		return -1;
+	}
+
+	if (ndp_setup->state == NAN_NDP_STATE_REQ_RECV) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: NDP: confirm: Continue though no TX status yet.");
+		nan_ndp_set_state(nan, &peer->ndp_setup,
+				  NAN_NDP_STATE_RES_SENT);
+	}
+
+	ndp_setup->status = status;
+
+	/* TODO: handle security */
+	if (status == NAN_NDP_STATUS_ACCEPTED)
+		nan_ndp_set_state(nan, &peer->ndp_setup,
+				  NAN_NDP_STATE_DONE);
+	else
+		nan_ndp_set_state(nan, &peer->ndp_setup,
+				  NAN_NDP_STATE_DONE);
+
+	return 0;
+}
+
+
+static int nan_ndp_attr_handle_term(struct nan_data *nan, struct nan_peer *peer,
+				    struct ieee80211_ndp *ndp_attr, u8 status)
+{
+	struct nan_ndp_setup *ndp_setup = &peer->ndp_setup;
+
+	/*
+	 * This should not really happen, but just in case,
+	 * terminate the establishment.
+	 */
+	if (ndp_setup->ndp) {
+		if (ndp_setup->ndp->ndp_id == ndp_attr->ndp_id &&
+		    os_memcmp(ndp_setup->ndp->init_ndi,
+			      ndp_attr->initiator_ndi,
+			      ETH_ALEN) == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: NDP: term: WIP with peer. Terminate");
+
+			nan_ndp_set_state(nan, &peer->ndp_setup,
+					  NAN_NDP_STATE_DONE);
+			ndp_setup->status = NAN_NDP_STATUS_REJECTED;
+			ndp_setup->reason = NAN_REASON_UNSPECIFIED_REASON;
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: NDP: term: different NDP WIP with peer. Ignore");
+		}
+		return 0;
+	}
+
+	/* TODO: handle already established NDPs */
+	wpa_printf(MSG_DEBUG, "NAN: NDP: Terminate established NDP");
+	return 0;
+}
+
+
+/*
+ * nan_ndp_handle_ndp_attr - Handle NDP 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
+ */
+int nan_ndp_handle_ndp_attr(struct nan_data *nan, struct nan_peer *peer,
+			    struct nan_msg *msg)
+{
+	struct ieee80211_ndp *ndp_attr;
+	u8 type, status;
+
+	if (!msg || !peer || !msg->attrs.ndp)
+		return -1;
+
+	ndp_attr = (struct ieee80211_ndp *)msg->attrs.ndp;
+	type = BITS(ndp_attr->type_and_status, NAN_NDP_TYPE_MASK,
+		    NAN_NDP_TYPE_POS);
+	status = BITS(ndp_attr->type_and_status, NAN_NDP_STATUS_MASK,
+		      NAN_NDP_STATUS_POS);
+
+	if (peer->ndp_setup.ndp) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: NDP: attr: state=%s (%d), status=%d",
+			   nan_ndp_state_str(peer->ndp_setup.state),
+			   peer->ndp_setup.state,
+			   peer->ndp_setup.status);
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: NDP: attr: no active NDP setup");
+	}
+
+	wpa_printf(MSG_DEBUG, "NAN: NDP: attr: type=0x%x, status=0x%x",
+		   type, status);
+
+	switch (type) {
+	case NAN_NDP_TYPE_REQUEST:
+		return nan_ndp_attr_handle_req(nan, peer, ndp_attr,
+					       msg->attrs.ndp_len, status);
+	case NAN_NDP_TYPE_RESPONSE:
+		return nan_ndp_attr_handle_res(nan, peer, ndp_attr,
+					       msg->attrs.ndp_len,
+					       status);
+	case NAN_NDP_TYPE_CONFIRM:
+		return nan_ndp_attr_handle_confirm(nan, peer, ndp_attr, status);
+	case NAN_NDP_TYPE_SECURITY_INSTALL:
+		/* TODO: Handle security */
+		return -1;
+	case NAN_NDP_TYPE_TERMINATE:
+		return nan_ndp_attr_handle_term(nan, peer, ndp_attr, status);
+	default:
+		return -1;
+	}
+}
+
+
+/*
+ * nan_ndp_add_ndp_attr - Add NDP attribute to frame
+ *
+ * @nan: NAN module context from nan_init()
+ * @peer: The peer to which the NAF should be sent
+ * @buf: wpabuf to which the attribute would be added
+ * Returns: 0 on success, negative on failure
+ */
+int nan_ndp_add_ndp_attr(struct nan_data *nan, struct nan_peer *peer,
+			 struct wpabuf *buf)
+{
+	struct nan_ndp_setup *ndp_setup;
+	u8 type, ndp_ctrl = 0;
+	u8 *len_ptr;
+
+	if (!peer || !peer->ndp_setup.ndp)
+		return -1;
+
+	ndp_setup = &peer->ndp_setup;
+
+	switch (ndp_setup->state) {
+	case NAN_NDP_STATE_START:
+		type = NAN_NDP_TYPE_REQUEST;
+		ndp_ctrl = NAN_NDP_CTRL_PUBLISH_ID_PRESENT;
+		if (ndp_setup->conf_req)
+			ndp_ctrl |= NAN_NDP_CTRL_CONFIRM_REQUIRED;
+		if (ndp_setup->ssi && ndp_setup->ssi_len)
+			ndp_ctrl |= NAN_NDP_CTRL_SPEC_INFO_PRESENT;
+		break;
+	case NAN_NDP_STATE_REQ_RECV:
+		type = NAN_NDP_TYPE_RESPONSE;
+		if (ndp_setup->status != NAN_NDP_STATUS_REJECTED)
+			ndp_ctrl |= NAN_NDP_CTRL_RESPONDER_NDI_PRESENT;
+		if (ndp_setup->ssi && ndp_setup->ssi_len)
+			ndp_ctrl |= NAN_NDP_CTRL_SPEC_INFO_PRESENT;
+		break;
+	case NAN_NDP_STATE_RES_RECV:
+		type = NAN_NDP_TYPE_CONFIRM;
+		break;
+	case NAN_NDP_STATE_CON_RECV:
+		/* TODO: Integrate NDP security flows */
+		type = NAN_NDP_TYPE_SECURITY_INSTALL;
+		break;
+	case NAN_NDP_STATE_NONE:
+	case NAN_NDP_STATE_REQ_SENT:
+	case NAN_NDP_STATE_RES_SENT:
+	case NAN_NDP_STATE_CON_SENT:
+	case NAN_NDP_STATE_DONE:
+	default:
+		return 0;
+	}
+
+	wpabuf_put_u8(buf, NAN_ATTR_NDP);
+	len_ptr = wpabuf_put(buf, 2);
+
+	wpabuf_put_u8(buf, ndp_setup->dialog_token);
+	wpabuf_put_u8(buf, type |
+		      (ndp_setup->status << NAN_NDP_STATUS_POS));
+	wpabuf_put_u8(buf, ndp_setup->reason);
+	wpabuf_put_data(buf, ndp_setup->ndp->init_ndi, ETH_ALEN);
+	wpabuf_put_u8(buf, ndp_setup->ndp->ndp_id);
+	wpabuf_put_u8(buf, ndp_ctrl);
+
+	if (ndp_ctrl & NAN_NDP_CTRL_PUBLISH_ID_PRESENT)
+		wpabuf_put_u8(buf, ndp_setup->publish_inst_id);
+
+	if (ndp_ctrl & NAN_NDP_CTRL_RESPONDER_NDI_PRESENT)
+		wpabuf_put_data(buf, ndp_setup->ndp->resp_ndi, ETH_ALEN);
+
+	if (ndp_ctrl & NAN_NDP_CTRL_SPEC_INFO_PRESENT)
+		wpabuf_put_data(buf, ndp_setup->ssi, ndp_setup->ssi_len);
+
+	WPA_PUT_LE16(len_ptr, (u8 *)wpabuf_put(buf, 0) - len_ptr - 2);
+	return 0;
+}
+
+
+/*
+ * nan_ndp_setup_reset - Reset the ndp_setup state
+ *
+ * @nan: NAN module context from nan_init()
+ * @peer: The peer that requires ndp setup reset
+ */
+void nan_ndp_setup_reset(struct nan_data *nan, struct nan_peer *peer)
+{
+	if (!peer)
+		return;
+
+	os_free(peer->ndp_setup.ssi);
+	os_free(peer->ndp_setup.ndp);
+
+	os_memset(&peer->ndp_setup, 0, sizeof(peer->ndp_setup));
+	nan_ndp_set_state(nan, &peer->ndp_setup, NAN_NDP_STATE_NONE);
+}
+
+
+/*
+ * nan_ndp_setup_failure - Indicate failure during NDP setup
+ *
+ * @nan: NAN module context from nan_init()
+ * @peer: The peer from which the original message was received
+ * @reason: The failure reason
+ * @reset_state: Reset the NDP state iff equals to true.
+ */
+void nan_ndp_setup_failure(struct nan_data *nan, struct nan_peer *peer,
+			   enum nan_reason reason, bool reset_state)
+{
+	struct nan_ndp_setup *ndp_setup = &peer->ndp_setup;
+
+	wpa_printf(MSG_DEBUG,
+		   "NAN: NDP: setup failure: peer " MACSTR ". state=%s (%u). reason=%u",
+		   MAC2STR(peer->nmi_addr), nan_ndp_state_str(ndp_setup->state),
+		   ndp_setup->state, reason);
+
+	if (reset_state) {
+		nan_ndp_setup_reset(nan, peer);
+	} else {
+		ndp_setup->status = NAN_NDP_STATUS_REJECTED;
+		ndp_setup->reason = reason;
+	}
+}
+
+
+/*
+ * nan_ndp_naf_sent - Indicate a NAF has been sent
+ *
+ * @nan: NAN module context from nan_init()
+ * @peer: The peer with whom the NDP is being setup
+ * @subtype: The NAN OUI subtype. See &enum nan_subtype
+ *
+ * Notification, indicating to the NDP SM to that a NAF was sent, so the
+ * NDP SM could update its state.
+ */
+int nan_ndp_naf_sent(struct nan_data *nan, struct nan_peer *peer,
+		     enum nan_subtype subtype)
+{
+	struct nan_ndp_setup *ndp_setup;
+
+	if (!peer || !peer->ndp_setup.ndp)
+		return -1;
+
+	ndp_setup = &peer->ndp_setup;
+
+	wpa_printf(MSG_DEBUG, "NAN: NDP: Tx done: tate=%s (%d), status=%d",
+		   nan_ndp_state_str(ndp_setup->state),
+		   ndp_setup->state, ndp_setup->status);
+
+	/*
+	 * Note: due to races between the Tx status and Rx path, it is possible
+	 * that the Tx status is received after the peer response was already
+	 * processed (which can result with another frame being sent). In such a
+	 * case the logic above fast-forwards the state, and the transitions
+	 * here need to take this into consideration.
+	 */
+	switch (ndp_setup->state) {
+	case NAN_NDP_STATE_START:
+		if (subtype == NAN_SUBTYPE_DATA_PATH_REQUEST)
+			nan_ndp_set_state(nan, &peer->ndp_setup,
+					  NAN_NDP_STATE_REQ_SENT);
+		break;
+	case NAN_NDP_STATE_REQ_RECV:
+		if (subtype == NAN_SUBTYPE_DATA_PATH_RESPONSE) {
+			if (ndp_setup->status == NAN_NDP_STATUS_ACCEPTED)
+				nan_ndp_set_state(nan, &peer->ndp_setup,
+						  NAN_NDP_STATE_DONE);
+			else
+				nan_ndp_set_state(nan, &peer->ndp_setup,
+						  NAN_NDP_STATE_RES_SENT);
+		}
+		break;
+	case NAN_NDP_STATE_RES_RECV:
+		if (subtype == NAN_SUBTYPE_DATA_PATH_CONFIRM) {
+			if (ndp_setup->status == NAN_NDP_STATUS_ACCEPTED)
+				nan_ndp_set_state(nan, &peer->ndp_setup,
+						  NAN_NDP_STATE_DONE);
+			else
+				nan_ndp_set_state(nan, &peer->ndp_setup,
+						  NAN_NDP_STATE_CON_SENT);
+		}
+		break;
+	case NAN_NDP_STATE_CON_RECV:
+		if (subtype == NAN_SUBTYPE_DATA_PATH_KEY_INSTALL &&
+		    ndp_setup->status == NAN_NDP_STATUS_ACCEPTED)
+			nan_ndp_set_state(nan, &peer->ndp_setup,
+					  NAN_NDP_STATE_DONE);
+		break;
+	case NAN_NDP_STATE_REQ_SENT:
+	case NAN_NDP_STATE_RES_SENT:
+	case NAN_NDP_STATE_CON_SENT:
+	case NAN_NDP_STATE_DONE:
+	default:
+		break;
+	}
+
+	return 0;
+}
diff --git a/src/utils/common.h b/src/utils/common.h
index 52fd6cf8bc..9ebab39438 100644
--- a/src/utils/common.h
+++ b/src/utils/common.h
@@ -462,6 +462,8 @@ void perror(const char *s);
 #define BIT_ULL(x) (1ULL << (x))
 #endif
 
+#define BITS(src, mask, pos) ((src & mask) >> pos)
+
 #ifndef MIN
 #define MIN(a, b) ((a) < (b) ? (a) : (b))
 #endif
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 3eb338106b..16b9832c82 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -339,6 +339,7 @@ OBJS += nan_supplicant.o
 OBJS += ../src/nan/nan.o
 OBJS += ../src/common/nan_de.o
 OBJS += ../src/nan/nan_util.o
+OBJS += ../src/nan/nan_ndp.o
 endif
 
 ifdef CONFIG_OWE
-- 
2.49.0




More information about the Hostap mailing list