[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