[RFC 10/13] NAN: Implement bootstrapping

Andrei Otcheretianski andrei.otcheretianski at intel.com
Tue Dec 23 03:57:22 PST 2025


Implement boostrapping handshake:

- Add support for processing followup messages that might
  hold the NPBA attribute.
- Add support for initiating bootstrapping with a peer.
- As initiator, internally handle bootstrap comeback
  from a peer.

Signed-off-by: Andrei Otcheretianski <andrei.otcheretianski at intel.com>
Signed-off-by: Ilan Peer <ilan.peer at intel.com>
---
 src/nan/nan.c           |  38 +++
 src/nan/nan.h           |   6 +
 src/nan/nan_bootstrap.c | 678 ++++++++++++++++++++++++++++++++++++++++
 src/nan/nan_i.h         |  37 ++-
 wpa_supplicant/Makefile |   1 +
 5 files changed, 759 insertions(+), 1 deletion(-)
 create mode 100644 src/nan/nan_bootstrap.c

diff --git a/src/nan/nan.c b/src/nan/nan.c
index cb048e843b..9ee7ec4f1a 100644
--- a/src/nan/nan.c
+++ b/src/nan/nan.c
@@ -130,6 +130,7 @@ static void nan_del_peer(struct nan_data *nan, struct nan_peer *peer)
 		nan_ndp_setup_stop(nan, peer);
 	}
 
+	nan_bootstrap_reset(nan, peer);
 	dl_list_del(&peer->list);
 	nan_peer_flush_avail(&peer->info);
 	nan_peer_flush_dev_capa(&peer->info);
@@ -1227,6 +1228,43 @@ static int nan_configure_peer_schedule(struct nan_data *nan,
 }
 
 
+/**
+ * nan_process_followup - Process a received NAN Followup action frame
+ *
+ * @nan: NAN module context from nan_init()
+ * @addr: Source address of the received frame
+ * @buf: Buffer containing the received frame
+ * @len: Length of the received frame in octets
+ * @req_instance_id: Instance ID of the request that triggered this followup
+ * @handle: Service handle of the service associated with this followup
+ *
+ * Returns: true if the frame was processed successfully, false on failure
+ */
+bool nan_process_followup(struct nan_data *nan, const u8 *addr, const u8 *buf,
+			  size_t len, u8 req_instance_id, int handle)
+{
+	struct nan_attrs attrs;
+	bool ret;
+
+	if (nan_parse_attrs(nan, buf, len, &attrs)) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Followup: Failed parsing attributes");
+		return false;
+	}
+
+	if (!attrs.bpba || !attrs.bpba_len) {
+		nan_attrs_clear(nan, &attrs);
+		return false;
+	}
+
+	ret = nan_bootstrap_handle_rx(nan, addr, attrs.bpba, attrs.bpba_len,
+				      buf, len, handle, req_instance_id);
+
+	nan_attrs_clear(nan, &attrs);
+	return ret;
+}
+
+
 static void nan_peer_state_timeout(void *eloop_ctx, void *timeout_ctx)
 {
 	struct nan_data *nan = eloop_ctx;
diff --git a/src/nan/nan.h b/src/nan/nan.h
index 9ce3448444..78762b15b2 100644
--- a/src/nan/nan.h
+++ b/src/nan/nan.h
@@ -597,6 +597,12 @@ 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_process_followup(struct nan_data *nan, const u8 *addr, const u8 *buf,
+			  size_t len, u8 req_instance_id, int handle);
+int nan_bootstrap_request(struct nan_data *nan, int handle,
+			  const u8 *peer_addr, u8 req_instance_id, u16 pbm,
+			  bool auth);
+int nan_bootstrap_peer_reset(struct nan_data *nan, const u8 *peer_nmi);
 
 bool nan_publish_instance_id_valid(struct nan_data *nan, u8 instance_id,
 				   u8 *service_id);
diff --git a/src/nan/nan_bootstrap.c b/src/nan/nan_bootstrap.c
new file mode 100644
index 0000000000..86944860ad
--- /dev/null
+++ b/src/nan/nan_bootstrap.c
@@ -0,0 +1,678 @@
+/*
+ * Wi-Fi Aware - NAN Bootstrap
+ * 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 "eloop.h"
+#include "nan_i.h"
+
+#define NAN_BOOTSTRAP_RETRY_TIMEOUT_US (1 * 1000 * 1000) /* 1 second */
+#define NAN_BOOTSTRAP_COOKIE_LEN 4
+
+static void nan_bootstrap_timeout(void *eloop_data, void *user_ctx);
+
+/*
+ * nan_complement_pbm - Get complement pairing bootstrapping method
+ *
+ * @pbm: Pairing bootstrapping method
+ * Returns: Complement pairing bootstrapping method
+ */
+static u16 nan_complement_pbm(u16 pbm)
+{
+	if (pbm == NAN_PBA_METHOD_OPPORTUNISTIC)
+		return NAN_PBA_METHOD_OPPORTUNISTIC;
+	if (pbm == NAN_PBA_METHOD_PIN_DISPLAY)
+		return NAN_PBA_METHOD_PIN_KEYPAD;
+	if (pbm == NAN_PBA_METHOD_PASSPHRASE_DISPLAY)
+		return NAN_PBA_METHOD_PASSPHRASE_KEYPAD;
+	if (pbm == NAN_PBA_METHOD_QR_DISPLAY)
+		return NAN_PBA_METHOD_QR_SCAN;
+	if (pbm == NAN_PBA_METHOD_NFC_TAG)
+		return NAN_PBA_METHOD_NFC_READER;
+	if (pbm == NAN_PBA_METHOD_PIN_KEYPAD)
+		return NAN_PBA_METHOD_PIN_DISPLAY;
+	if (pbm == NAN_PBA_METHOD_PASSPHRASE_KEYPAD)
+		return NAN_PBA_METHOD_PASSPHRASE_DISPLAY;
+	if (pbm == NAN_PBA_METHOD_QR_SCAN)
+		return NAN_PBA_METHOD_QR_DISPLAY;
+	if (pbm == NAN_PBA_METHOD_NFC_READER)
+		return NAN_PBA_METHOD_NFC_TAG;
+	if (pbm == NAN_PBA_METHOD_SERVICE_MANAGED)
+		return NAN_PBA_METHOD_SERVICE_MANAGED;
+	if (pbm == NAN_PBA_METHOD_HANDSHAKE_SKIPPED)
+		return NAN_PBA_METHOD_HANDSHAKE_SKIPPED;
+
+	return 0;
+}
+
+
+/*
+ * nan_bootstrap_reset - Reset bootstrap state
+ *
+ * @nan: NAN module context from nan_init()
+ * @peer: Peer to reset bootstrap state
+ */
+void nan_bootstrap_reset(struct nan_data *nan, struct nan_peer *peer)
+{
+	wpa_printf(MSG_DEBUG, "NAN: Bootstrap: Reset state");
+
+	eloop_cancel_timeout(nan_bootstrap_timeout, nan, peer);
+	os_free(peer->bootstrap.cookie);
+
+	/*
+	 * Do not use memset to reset all data, to preserve the peer's
+	 * supported bootstrap methods
+	 */
+	peer->bootstrap.cookie = NULL;
+	peer->bootstrap.cookie_len = 0;
+	peer->bootstrap.initiator = false;
+	peer->bootstrap.dialog_token = 0;
+	peer->bootstrap.requested_pbm = 0;
+	peer->bootstrap.authorized = 0;
+	peer->bootstrap.status = 0;
+	peer->bootstrap.comeback_required = false;
+	peer->bootstrap.in_progress = false;
+	peer->bootstrap.reason_code = 0;
+	peer->bootstrap.handle = -1;
+	peer->bootstrap.req_instance_id = 0;
+}
+
+
+/*
+ * nan_bootstrap_build_npba - Build NAN Pairing Bootstrap Attribute
+ *
+ * @nan: NAN module context from nan_init()
+ * @peer: Peer for which to build the attribute
+ */
+static struct wpabuf * nan_bootstrap_build_npba(struct nan_data *nan,
+						struct nan_peer *peer)
+{
+	struct wpabuf *buf;
+	u16 pbm = peer->bootstrap.requested_pbm;
+	u8 type_and_status;
+	u8 *len;
+
+	/*
+	 * Allocate max possible size: header (3) + dialog token (1) + type and
+	 * status (1) + reason (1) + comeback (2 + 1 + comeback token) + pbm (2)
+	 */
+	buf = wpabuf_alloc(11 + peer->bootstrap.cookie_len);
+	if (!buf)
+		return NULL;
+
+	wpa_printf(MSG_DEBUG, "NAN: Bootstrap: Build NPBA attribute");
+
+	if (peer->bootstrap.initiator) {
+		type_and_status = NAN_PBA_TYPE_RESPONSE;
+		if (peer->bootstrap.status)
+			pbm = 0;
+	} else {
+		type_and_status = NAN_PBA_TYPE_REQUEST;
+	}
+
+	type_and_status |= (peer->bootstrap.status << NAN_PBA_STATUS_POS);
+
+	wpabuf_put_u8(buf, NAN_ATTR_BPBA);
+	len = wpabuf_put(buf, 2);
+
+	wpabuf_put_u8(buf, peer->bootstrap.dialog_token);
+	wpabuf_put_u8(buf, type_and_status);
+	wpabuf_put_u8(buf, peer->bootstrap.reason_code);
+
+	if (peer->bootstrap.status == NAN_PBA_STATUS_COMEBACK) {
+		if (peer->bootstrap.initiator)
+			wpabuf_put_le16(buf, peer->bootstrap.comeback_after);
+
+		wpabuf_put_u8(buf, peer->bootstrap.cookie_len);
+		if (peer->bootstrap.cookie)
+			wpabuf_put_data(buf, peer->bootstrap.cookie,
+					peer->bootstrap.cookie_len);
+	}
+
+	wpabuf_put_le16(buf, pbm);
+	WPA_PUT_LE16(len, wpabuf_len(buf) - 3);
+
+	return buf;
+}
+
+
+/*
+ * nan_bootstrap_timeout - Bootstrap timeout handler
+ *
+ * @eloop_data: NAN module context from nan_init()
+ * @user_ctx: Peer for which the timeout occurred
+ */
+static void nan_bootstrap_timeout(void *eloop_data, void *user_ctx)
+{
+	struct nan_data *nan = eloop_data;
+	struct nan_peer *peer = user_ctx;
+	struct wpabuf *attr;
+
+	wpa_printf(MSG_DEBUG, "NAN: Bootstrap: timeout. status=%u",
+		   peer->bootstrap.status);
+
+	attr = nan_bootstrap_build_npba(nan, peer);
+	if (!attr) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Bootstrap: Failed to build attribute");
+
+		nan_bootstrap_reset(nan, peer);
+
+		nan->cfg->bootstrap_completed(nan->cfg->cb_ctx, peer->nmi_addr,
+					      0, false,
+					      NAN_REASON_UNSPECIFIED_REASON);
+		return;
+	}
+
+	if (nan->cfg->transmit_followup(nan->cfg->cb_ctx, peer->nmi_addr, attr,
+					peer->bootstrap.handle,
+					peer->bootstrap.req_instance_id)) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Bootstrap: Failed to transmit followup");
+
+		nan_bootstrap_reset(nan, peer);
+
+		nan->cfg->bootstrap_completed(nan->cfg->cb_ctx, peer->nmi_addr,
+					      0, false,
+					      NAN_REASON_UNSPECIFIED_REASON);
+	}
+
+	wpabuf_free(attr);
+
+	/* If the peer didn't reply, try again */
+	if (peer->bootstrap.status != NAN_PBA_STATUS_COMEBACK)
+		eloop_register_timeout(0, NAN_BOOTSTRAP_RETRY_TIMEOUT_US,
+				       nan_bootstrap_timeout, nan, peer);
+}
+
+
+/*
+ * nan_bootstrap_handle_rx_request - Process received bootstrap request
+ *
+ * @nan: NAN module context from nan_init()
+ * @peer: Peer from which the request was received
+ * @dialog_token: Dialog token from the request
+ * @pbm: Pairing bootstrapping method bitmap from the request
+ * @cookie: Comeback cookie from the request (if any)
+ * @cookie_len: Length of the comeback cookie
+ * @status: Status field from the request
+ * @handle: Follow up handle
+ * @req_instance_id: Follow up instance ID
+ */
+static void nan_bootstrap_handle_rx_request(struct nan_data *nan,
+					    struct nan_peer *peer,
+					    u8 dialog_token, u16 pbm,
+					    const u8 *cookie,
+					    u8 cookie_len, u8 status,
+					    int handle, u8 req_instance_id)
+{
+	struct wpabuf *attr = NULL;
+	u16 supported_methods;
+
+	wpa_printf(MSG_DEBUG, "NAN: Bootstrap: RX request");
+
+	if (peer->bootstrap.initiator && peer->bootstrap.comeback_required) {
+		if (status != NAN_PBA_STATUS_COMEBACK) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: Bootstrap: Missing comeback cookie. Ignore");
+			return;
+		}
+
+		if (cookie_len != peer->bootstrap.cookie_len ||
+		    !!cookie ^ !!peer->bootstrap.cookie ||
+		    (cookie && os_memcmp(cookie, peer->bootstrap.cookie,
+					 cookie_len) != 0)) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: Bootstrap: Invalid comeback cookie. Ignore");
+			return;
+		}
+
+		if (dialog_token != peer->bootstrap.dialog_token) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: Bootstrap: Invalid dialog token. Ignore");
+			return;
+		}
+
+		if (handle != peer->bootstrap.handle ||
+		    req_instance_id != peer->bootstrap.req_instance_id) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: Bootstrap: Invalid handle or instance ID");
+			return;
+		}
+
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Bootstrap: Valid comeback request(pbm=0x%04x) authorized=0x%04x",
+			   pbm, peer->bootstrap.authorized);
+
+		if (peer->bootstrap.authorized == nan_complement_pbm(pbm)) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: Bootstrap: Peer bootstrap authorized. Accept.");
+
+			peer->bootstrap.status = NAN_PBA_STATUS_ACCEPTED;
+			peer->bootstrap.reason_code = 0;
+			peer->bootstrap.comeback_required = false;
+			peer->bootstrap.requested_pbm = nan_complement_pbm(pbm);
+		}
+
+		goto send_response;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "NAN: Bootstrap: New bootstrap request. pbm=0x%04x", pbm);
+
+	nan_bootstrap_reset(nan, peer);
+
+	peer->bootstrap.initiator = true;
+	peer->bootstrap.dialog_token = dialog_token;
+	peer->bootstrap.requested_pbm = nan_complement_pbm(pbm);
+	peer->bootstrap.in_progress = true;
+	peer->bootstrap.handle = handle;
+	peer->bootstrap.req_instance_id = req_instance_id;
+	supported_methods =
+		nan->cfg->get_supported_bootstrap_methods(nan->cfg->cb_ctx,
+							  handle);
+
+	if (!(supported_methods & peer->bootstrap.requested_pbm)) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Bootstrap: No supported bootstrap methods in request");
+
+		peer->bootstrap.status = NAN_PBA_STATUS_REJECTED;
+		peer->bootstrap.reason_code =
+			NAN_REASON_PAIR_BOOTSTRAP_REJECTED;
+		goto send_response;
+	}
+
+	if (peer->bootstrap.requested_pbm &
+	    nan->cfg->auto_accept_bootstrap_methods) {
+		peer->bootstrap.authorized = peer->bootstrap.requested_pbm;
+		peer->bootstrap.status = NAN_PBA_STATUS_ACCEPTED;
+		goto send_response;
+	}
+
+	peer->bootstrap.status = NAN_PBA_STATUS_COMEBACK;
+	peer->bootstrap.comeback_required = true;
+	peer->bootstrap.cookie_len = NAN_BOOTSTRAP_COOKIE_LEN;
+	peer->bootstrap.cookie = os_malloc(peer->bootstrap.cookie_len);
+
+	if (peer->bootstrap.cookie)
+		os_get_random(peer->bootstrap.cookie,
+			      peer->bootstrap.cookie_len);
+	else
+		peer->bootstrap.cookie_len = 0;
+
+	peer->bootstrap.comeback_after =
+		nan->cfg->bootstrap_comeback_timeout;
+
+	if (nan->cfg->bootstrap_request)
+		nan->cfg->bootstrap_request(nan->cfg->cb_ctx, peer->nmi_addr,
+					    peer->bootstrap.requested_pbm);
+
+send_response:
+	attr = nan_bootstrap_build_npba(nan, peer);
+	if (!attr) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Bootstrap: Failed to build bootstrap attribute");
+		goto done;
+	}
+
+	if (nan->cfg->transmit_followup(nan->cfg->cb_ctx, peer->nmi_addr, attr,
+					handle, req_instance_id)) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Bootstrap: Failed to transmit bootstrap followup");
+
+		nan->cfg->bootstrap_completed(nan->cfg->cb_ctx, peer->nmi_addr,
+					      0, false,
+					      NAN_REASON_UNSPECIFIED_REASON);
+		goto done;
+	}
+
+
+	if (peer->bootstrap.status == NAN_PBA_STATUS_COMEBACK) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Bootstrap: Wait for comeback bootstrap request");
+
+		wpabuf_free(attr);
+		return;
+	}
+
+	nan->cfg->bootstrap_completed(nan->cfg->cb_ctx,
+				      peer->nmi_addr,
+				      peer->bootstrap.requested_pbm,
+				      peer->bootstrap.status ==
+				      NAN_PBA_STATUS_ACCEPTED,
+				      peer->bootstrap.reason_code);
+done:
+	wpabuf_free(attr);
+	nan_bootstrap_reset(nan, peer);
+}
+
+
+/*
+ * nan_bootstrap_supported - Check if bootstrap operations are supported
+ *
+ * @nan: NAN module context from nan_init()
+ * Returns: true if all required bootstrap operations are supported
+ */
+static bool nan_bootstrap_supported(struct nan_data *nan)
+{
+	return nan->cfg->bootstrap_completed &&
+		nan->cfg->bootstrap_request &&
+		nan->cfg->transmit_followup &&
+		nan->cfg->get_supported_bootstrap_methods;
+}
+
+
+/*
+ * nan_bootstrap_handle_rx - Process received bootstrap follow up
+ *
+ * @nan: NAN module context from nan_init()
+ * @peer_nmi: Peer address from which the follow up was received
+ * @npba: Pointer to the NPBA attribute in the follow up
+ * @npba_len: Length of the NPBA attribute
+ * @buf: Complete follow up frame
+ * @len: Length of the complete follow up frame
+ * @handle: Follow up handle
+ * @req_instance_id: Follow up instance ID
+ * Returns: true if the follow up was processed, false on error
+ */
+bool nan_bootstrap_handle_rx(struct nan_data *nan, const u8 *peer_nmi,
+			     const u8 *npba, u16 npba_len,
+			     const u8 *buf, size_t len,
+			     int handle, u8 req_instance_id)
+{
+	const u8 *cookie = NULL;
+	u8 dialog_token, type, status, reason_code, cookie_len = 0;
+	u16 pbm, comeback_after = 0;
+	struct nan_peer *peer;
+
+	if (!nan_bootstrap_supported(nan)) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Bootstrap: Not all bootstrap operations are supported. Ignore");
+		return false;
+	}
+
+	dialog_token = *npba++;
+	type = *npba & NAN_PBA_TYPE_MSK;
+	status = (*npba++ >> NAN_PBA_STATUS_POS) & NAN_PBA_STATUS_MSK;
+	reason_code = *npba++;
+	npba_len -= 3;
+
+	if (type == NAN_PBA_TYPE_ADVERTISE) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Bootstrap: type == Advertise. Ignore");
+		return false;
+	}
+
+	if (type != NAN_PBA_TYPE_REQUEST &&
+	    type != NAN_PBA_TYPE_RESPONSE) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Bootstrap: Unknown NPBA type %u", type);
+		return false;
+	}
+
+	if (status == NAN_PBA_STATUS_COMEBACK) {
+		if (type == NAN_PBA_TYPE_RESPONSE) {
+			if (npba_len < 2) {
+				wpa_printf(MSG_DEBUG,
+					   "NAN: Bootstrap: Response too short for comeback after");
+				return false;
+			}
+
+			comeback_after = WPA_GET_LE16(npba);
+			npba += 2;
+			npba_len -= 2;
+		}
+
+		if (npba_len < 1) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: Bootstrap: Response too short for comeback cookie length");
+			return false;
+		}
+
+		cookie_len = *npba++;
+		npba_len -= 1;
+		if (cookie_len > npba_len) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: Bootstrap: comeback field is too long");
+			return false;
+		}
+
+		cookie = npba;
+		npba += cookie_len;
+	}
+
+	pbm = WPA_GET_LE16(npba);
+	peer = nan_get_peer(nan, peer_nmi);
+
+	/* Handle NAN bootstrap request */
+	if (type == NAN_PBA_TYPE_REQUEST) {
+		if (!peer) {
+			nan_add_peer(nan, peer_nmi, buf, len);
+			peer = nan_get_peer(nan, peer_nmi);
+
+			if (!peer) {
+				wpa_printf(MSG_DEBUG,
+					   "NAN: Bootstrap: Failed alloc peer from bootstrap request");
+				return false;
+			}
+		}
+
+		nan_bootstrap_handle_rx_request(nan, peer, dialog_token,
+						pbm, cookie, cookie_len,
+						status, handle,
+						req_instance_id);
+		return true;
+	}
+
+	/* Handle NAN bootstrap response */
+	if (!peer || !peer->bootstrap.in_progress ||
+	    peer->bootstrap.initiator) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Bootstrap: Unexpected bootstrap response");
+		return false;
+	}
+
+	if (dialog_token != peer->bootstrap.dialog_token) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Bootstrap: Response with invalid dialog token. Ignore");
+		return false;
+	}
+
+	if (handle != peer->bootstrap.handle ||
+	    req_instance_id != peer->bootstrap.req_instance_id) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Bootstrap: Response with invalid handle or instance ID. Ignore");
+		return false;
+	}
+
+	peer->bootstrap.status = status;
+	peer->bootstrap.reason_code = reason_code;
+	peer->bootstrap.comeback_required = (status == NAN_PBA_STATUS_COMEBACK);
+	peer->bootstrap.comeback_after = comeback_after;
+
+	if (cookie && cookie_len) {
+		os_free(peer->bootstrap.cookie);
+		peer->bootstrap.cookie = os_memdup(cookie, cookie_len);
+
+		if (!peer->bootstrap.cookie) {
+			nan_bootstrap_reset(nan, peer);
+			return false;
+		}
+		peer->bootstrap.cookie_len = cookie_len;
+	}
+
+	if (status == NAN_PBA_STATUS_ACCEPTED &&
+	    nan_complement_pbm(pbm) == peer->bootstrap.requested_pbm) {
+		wpa_printf(MSG_DEBUG, "NAN: Bootstrap: Accepted. Complete.");
+
+		nan->cfg->bootstrap_completed(nan->cfg->cb_ctx,
+					      peer->nmi_addr,
+					      peer->bootstrap.requested_pbm,
+					      true, 0);
+		nan_bootstrap_reset(nan, peer);
+	} else if (status == NAN_PBA_STATUS_REJECTED) {
+		wpa_printf(MSG_DEBUG, "NAN: Bootstrap: Rejected. Complete");
+
+		nan->cfg->bootstrap_completed(nan->cfg->cb_ctx, peer->nmi_addr,
+					      0, false, reason_code);
+		nan_bootstrap_reset(nan, peer);
+	} else if (status == NAN_PBA_STATUS_COMEBACK) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Bootstrap: Comeback required. Schedule timeout for %u TU",
+			   peer->bootstrap.comeback_after);
+
+		eloop_cancel_timeout(nan_bootstrap_timeout, nan, peer);
+		eloop_register_timeout(peer->bootstrap.comeback_after / 1024,
+				       (peer->bootstrap.comeback_after % 1024) * 1000,
+				       nan_bootstrap_timeout, nan, peer);
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Bootstrap: Unknown status=%u. Abort.",
+			   status);
+
+		nan->cfg->bootstrap_completed(nan->cfg->cb_ctx, peer->nmi_addr,
+					      0, false,
+					      NAN_REASON_UNSPECIFIED_REASON);
+		nan_bootstrap_reset(nan, peer);
+	}
+
+	return true;
+}
+
+
+/*
+ * nan_bootstrap_request - Initiate NAN bootstrap request to a peer
+ *
+ * @nan: NAN module context from nan_init()
+ * @handle: Follow up handle
+ * @peer_nmi: Peer address to which to send the bootstrap request
+ * @req_instance_id: Follow up instance ID
+ * @pbm: Pairing bootstrapping method bitmap to use
+ * @auth: If true, authorize the bootstrap request for the peer instead of
+ *        sending the request
+ * Returns: 0 on success, -1 on failure
+ */
+int nan_bootstrap_request(struct nan_data *nan, int handle,
+			  const u8 *peer_nmi, u8 req_instance_id, u16 pbm,
+			  bool auth)
+{
+	struct nan_peer *peer;
+	struct wpabuf *npba;
+	int ret;
+
+	if (!nan || !nan->nan_started)
+		return -1;
+
+	if (!nan_bootstrap_supported(nan)) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Bootstrap: Not all bootstrap operations are supported. Ignore");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "NAN: Bootstrap: Request bootstrap to peer " MACSTR
+		   " pbm=0x%x auth=%d",
+		   MAC2STR(peer_nmi), pbm, auth);
+
+	peer = nan_get_peer(nan, peer_nmi);
+	if (!peer) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Bootstrap: Request for unknown peer");
+		return -1;
+	}
+
+	if (!(pbm & nan->cfg->supported_bootstrap_methods)) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Bootstrap: Unsupported method=0x%x (local support=0x%x)",
+			   pbm, nan->cfg->supported_bootstrap_methods);
+		return -1;
+	}
+
+	if (pbm & (pbm - 1)) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Bootstrap: Multiple bootstrap methods=0x%x in request",
+			   pbm);
+		return -1;
+	}
+
+	if (auth) {
+		wpa_printf(MSG_DEBUG, "NAN: Bootstrap: Authorize request");
+
+		peer->bootstrap.authorized = pbm;
+		return 0;
+	}
+
+	if (peer->bootstrap.in_progress) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Bootstrap: already in progress with the peer");
+		return -1;
+	}
+
+	if (!(nan_complement_pbm(pbm) & peer->bootstrap.supported_methods)) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Bootstrap: Peer doesn't support method=0x%x",
+			   pbm);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "NAN: Prepare bootstrap request");
+
+	nan_bootstrap_reset(nan, peer);
+
+	peer->bootstrap.dialog_token = ++nan->next_dialog_token;
+	peer->bootstrap.requested_pbm = pbm;
+	peer->bootstrap.in_progress = true;
+	peer->bootstrap.handle = handle;
+	peer->bootstrap.req_instance_id = req_instance_id;
+
+	npba = nan_bootstrap_build_npba(nan, peer);
+	if (!npba) {
+		nan_bootstrap_reset(nan, peer);
+		return -1;
+	}
+
+	ret = nan->cfg->transmit_followup(nan->cfg->cb_ctx, peer->nmi_addr,
+					  npba, handle, req_instance_id);
+	wpabuf_free(npba);
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Bootstrap: Failed to transmit followup");
+
+		nan_bootstrap_reset(nan, peer);
+		return -1;
+	}
+
+	eloop_register_timeout(0, NAN_BOOTSTRAP_RETRY_TIMEOUT_US,
+			       nan_bootstrap_timeout, nan, peer);
+	return ret;
+}
+
+
+/*
+ * nan_bootstrap_peer_reset - Reset bootstrap state for a peer
+ *
+ * @nan: NAN module context from nan_init()
+ * @peer_nmi: Peer address for which to reset the bootstrap state
+ * Returns: 0 on success, -1 on failure
+ */
+int nan_bootstrap_peer_reset(struct nan_data *nan, const u8 *peer_nmi)
+{
+	struct nan_peer *peer;
+
+	if (!nan || !nan->nan_started)
+		return -1;
+
+	peer = nan_get_peer(nan, peer_nmi);
+	if (!peer) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Bootstrap: Reset for unknown peer");
+		return -1;
+	}
+
+	nan_bootstrap_reset(nan, peer);
+	return 0;
+}
diff --git a/src/nan/nan_i.h b/src/nan/nan_i.h
index 019f6d73e0..8dac2e42df 100644
--- a/src/nan/nan_i.h
+++ b/src/nan/nan_i.h
@@ -404,9 +404,40 @@ struct nan_ndl {
  *
  * @supported_methods: Bitmap of supported bootstrap methods. See
  *     &enum nan_pairing_bootstrapping_method.
+ * @initiator: True iff this device is the initiator.
+ * @requested_pbm: Bitmap of requested bootstrap methods. See
+ *     &enum nan_pairing_bootstrapping_method.
+ * @dialog_token: Dialog token of the bootstrap exchange.
+ * @status: Status of the bootstrap exchange. See &enum nan_pba_status.
+ * @reason_code: Reason code for the bootstrap exchange. See &enum nan_reason.
+ * @comeback_required: True iff the peer requested a comeback.
+ * @comeback_after: Time after which the comeback is requested.
+ * @cookie: Pointer to the cookie received from the peer.
+ * @cookie_len: Length of the cookie.
+ * @authorized: Authorized bootstrap method. See &enum
+ *     nan_pairing_bootstrapping_method.
+ * @in_progress: True iff a bootstrap exchange is in progress.
+ * @handle: Follow up context handle for the ongoing bootstrap request.
+ * @req_instance_id: Instance ID of the bootstrap request.
  */
 struct nan_bootstrap {
 	u16 supported_methods;
+	bool initiator;
+	u16 requested_pbm;
+	u8 dialog_token;
+	u8 status;
+	u8 reason_code;
+
+	bool comeback_required;
+	u16 comeback_after;
+	u8 *cookie;
+	u8 cookie_len;
+
+	u16 authorized;
+	bool in_progress;
+
+	int handle;
+	u8 req_instance_id;
 };
 
 /*
@@ -620,5 +651,9 @@ int nan_sec_get_tk(struct nan_data *nan, struct nan_peer *peer,
 		   u8 *tk, size_t *tk_len,
 		   enum nan_cipher_suite_id *csid);
 void nan_add_dev_capa_ext_attr(struct nan_data *nan, struct wpabuf *buf);
-
+void nan_bootstrap_reset(struct nan_data *nan, struct nan_peer *peer);
+bool nan_bootstrap_handle_rx(struct nan_data *nan, const u8 *peer_nmi,
+			     const u8 *npba, u16 npba_len,
+			     const u8 *buf, size_t len,
+			     int handle, u8 req_instance_id);
 #endif
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index cf8a3fe2eb..cddf3c4897 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -343,6 +343,7 @@ OBJS += ../src/nan/nan_ndp.o
 OBJS += ../src/nan/nan_ndl.o
 OBJS += ../src/nan/nan_crypto.o
 OBJS += ../src/nan/nan_sec.o
+OBJS += ../src/nan/nan_bootstrap.o
 endif
 
 ifdef CONFIG_OWE
-- 
2.49.0




More information about the Hostap mailing list