[RFC 06/56] NAN: Add support for parsing NAN action frames
Andrei Otcheretianski
andrei.otcheretianski at intel.com
Sun Dec 7 03:18:15 PST 2025
From: Ilan Peer <ilan.peer at intel.com>
Add support for parsing a NAN Action Frame (NAF) and
storing the relevant parsed attributes.
Signed-off-by: Ilan Peer <ilan.peer at intel.com>
---
src/common/ieee802_11_defs.h | 11 ++
src/nan/Makefile | 2 +-
src/nan/nan_i.h | 51 ++++++
src/nan/nan_util.c | 308 +++++++++++++++++++++++++++++++++++
wpa_supplicant/Makefile | 1 +
5 files changed, 372 insertions(+), 1 deletion(-)
create mode 100644 src/nan/nan_util.c
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 28032bbc82..1a87f976fe 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -1210,12 +1210,23 @@ struct ieee80211_mgmt {
* Basic Multi-Link element (optional) */
u8 variable[];
} STRUCT_PACKED link_reconf_resp;
+ struct {
+ u8 action_code;
+ u8 oui[3];
+ u8 oui_type;
+ u8 subtype;
+ u8 variable[0];
+ } STRUCT_PACKED naf;
} u;
} STRUCT_PACKED action;
} u;
} STRUCT_PACKED;
+#define IEEE80211_MIN_ACTION_LEN(type) \
+ (offsetof(struct ieee80211_mgmt, u.action.u.type) + \
+ sizeof(((struct ieee80211_mgmt *)0)->u.action.u.type))
+
#define IEEE80211_MAX_MMPDU_SIZE 2304
/* Rx MCS bitmask is in the first 77 bits of supported_mcs_set */
diff --git a/src/nan/Makefile b/src/nan/Makefile
index bd8a66a5b3..2c58f9cb6c 100644
--- a/src/nan/Makefile
+++ b/src/nan/Makefile
@@ -1,3 +1,3 @@
-LIB_OBJS= nan.o
+LIB_OBJS= nan.o nan_util.o
include ../lib.rules
diff --git a/src/nan/nan_i.h b/src/nan/nan_i.h
index 1efed82ff5..62b27881e9 100644
--- a/src/nan/nan_i.h
+++ b/src/nan/nan_i.h
@@ -9,6 +9,9 @@
#ifndef NAN_I_H
#define NAN_I_H
+#include "common/ieee802_11_defs.h"
+#include "common/nan_defs.h"
+#include "nan.h"
#include "list.h"
struct nan_config;
@@ -38,5 +41,53 @@ struct nan_data {
struct dl_list peer_list;
};
+struct nan_attrs_entry {
+ struct dl_list list;
+ const u8 *ptr;
+ u16 len;
+};
+
+struct nan_attrs {
+ struct dl_list serv_desc_ext;
+ struct dl_list avail;
+ struct dl_list ndc;
+ struct dl_list dev_capa;
+ struct dl_list element_container;
+
+ const u8 *ndp;
+ const u8 *ndl;
+ const u8 *ndl_qos;
+ const u8 *cipher_suite_info;
+ const u8 *sec_ctxt_info;
+ const u8 *shared_key_desc;
+
+ u16 ndp_len;
+ u16 ndl_len;
+ u16 ndl_qos_len;
+ u16 cipher_suite_info_len;
+ u16 sec_ctxt_info_len;
+ u16 shared_key_desc_len;
+};
+
+struct nan_msg {
+ u8 oui_type;
+ u8 oui_subtype;
+ struct nan_attrs attrs;
+
+ /* the full frame is required for the NDP security flows, that compute
+ * the NDP authentication token over the entire frame body.
+ */
+ const struct ieee80211_mgmt *mgmt;
+ size_t len;
+};
+
struct nan_peer * nan_get_peer(struct nan_data *nan, const u8 *addr);
+bool nan_is_naf(struct nan_data *nan, const struct ieee80211_mgmt *mgmt,
+ size_t len);
+int nan_parse_attrs(struct nan_data *nan, const u8 *data, size_t len,
+ struct nan_attrs *attrs);
+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);
+
#endif
diff --git a/src/nan/nan_util.c b/src/nan/nan_util.c
new file mode 100644
index 0000000000..4e034ecfd9
--- /dev/null
+++ b/src/nan/nan_util.c
@@ -0,0 +1,308 @@
+/*
+ * Wi-Fi Aware - NAN module utils
+ * 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 void nan_attrs_clear_list(struct nan_data *nan,
+ struct dl_list *list)
+{
+ struct nan_attrs_entry *entry, *pentry;
+
+ dl_list_for_each_safe(entry, pentry, list,
+ struct nan_attrs_entry,
+ list) {
+ dl_list_del(&entry->list);
+ os_free(entry);
+ }
+}
+
+
+/*
+ * nan_attrs_clear - Free data from nan parsing
+ *
+ * @nan: NAN module context from nan_init()
+ * @attrs: Parsed nan_attrs
+ */
+void nan_attrs_clear(struct nan_data *nan, struct nan_attrs *attrs)
+{
+ nan_attrs_clear_list(nan, &attrs->serv_desc_ext);
+ nan_attrs_clear_list(nan, &attrs->avail);
+ nan_attrs_clear_list(nan, &attrs->ndc);
+ nan_attrs_clear_list(nan, &attrs->dev_capa);
+ nan_attrs_clear_list(nan, &attrs->element_container);
+
+ os_memset(attrs, 0, sizeof(*attrs));
+}
+
+
+/*
+ * nan_parse_attrs - Parse NAN attributes
+ *
+ * @nan: NAN module context from nan_init()
+ * @data: Buffer holding the attributes
+ * @len: Length of &data
+ * @attrs: On return would hold the parsed attributes
+ * Returns: 0 on success; positive or negative indicate an error
+ *
+ * Note: In case of success, the caller must free temporary memory allocations
+ * by calling nan_attrs_clear() when the parsed data is not needed anymore.
+ */
+int nan_parse_attrs(struct nan_data *nan, const u8 *data, size_t len,
+ struct nan_attrs *attrs)
+{
+ struct nan_attrs_entry *entry;
+ const u8 *pos = data;
+ const u8 *end = pos + len;
+
+ os_memset(attrs, 0, sizeof(*attrs));
+
+ dl_list_init(&attrs->serv_desc_ext);
+ dl_list_init(&attrs->avail);
+ dl_list_init(&attrs->ndc);
+ dl_list_init(&attrs->dev_capa);
+ dl_list_init(&attrs->element_container);
+
+ while (pos + 3 < end) {
+ u8 id = *pos++;
+ u16 attr_len = WPA_GET_LE16(pos);
+
+ pos += 2;
+ if ((pos + attr_len) > end)
+ goto fail;
+
+ switch (id) {
+ case NAN_ATTR_SDEA:
+ entry = os_malloc(sizeof(*entry));
+ if (!entry)
+ goto fail;
+
+ entry->ptr = pos;
+ entry->len = attr_len;
+ dl_list_add_tail(&attrs->serv_desc_ext, &entry->list);
+ break;
+ case NAN_ATTR_DEVICE_CAPABILITY:
+ /* Validate Device Capability attribute length */
+ if (attr_len != sizeof(struct nan_device_capa))
+ break;
+
+ entry = os_malloc(sizeof(*entry));
+ if (!entry)
+ goto fail;
+
+ entry->ptr = pos;
+ entry->len = attr_len;
+ dl_list_add_tail(&attrs->dev_capa, &entry->list);
+ break;
+ case NAN_ATTR_NDP:
+ /* Validate minimal NDP attribute length */
+ if (attr_len < sizeof(struct ieee80211_ndp))
+ break;
+
+ attrs->ndp = pos;
+ attrs->ndp_len = attr_len;
+ break;
+ case NAN_ATTR_NAN_AVAILABILITY:
+ /* Validate minimal Availability attribute length */
+ if (attr_len < sizeof(struct nan_avail))
+ break;
+
+ entry = os_malloc(sizeof(*entry));
+ if (!entry)
+ goto fail;
+
+ entry->ptr = pos;
+ entry->len = attr_len;
+ dl_list_add_tail(&attrs->avail, &entry->list);
+ break;
+ case NAN_ATTR_NDC:
+ /* Validate minimal NDC attribute length */
+ if (attr_len < sizeof(struct ieee80211_ndc))
+ break;
+
+ entry = os_malloc(sizeof(*entry));
+ if (!entry)
+ goto fail;
+
+ entry->ptr = pos;
+ entry->len = attr_len;
+ dl_list_add_tail(&attrs->ndc, &entry->list);
+ break;
+ case NAN_ATTR_NDL:
+ /* Validate minimal NDL attribute length */
+ if (attr_len < sizeof(struct ieee80211_ndl))
+ break;
+
+ attrs->ndl = pos;
+ attrs->ndl_len = attr_len;
+ break;
+ case NAN_ATTR_NDL_QOS:
+ /* Validate QoS attribute length */
+ if (attr_len != sizeof(struct ieee80211_nan_qos))
+ break;
+
+ attrs->ndl_qos = pos;
+ attrs->ndl_qos_len = attr_len;
+ break;
+ case NAN_ATTR_ELEM_CONTAINER:
+ /*
+ * Validate minimal Element Container attribute length
+ */
+ if (attr_len < 1)
+ break;
+
+ entry = os_malloc(sizeof(*entry));
+ if (!entry)
+ goto fail;
+
+ entry->ptr = pos;
+ entry->len = attr_len;
+ dl_list_add_tail(&attrs->element_container,
+ &entry->list);
+ break;
+ case NAN_ATTR_CSIA:
+ attrs->cipher_suite_info = pos;
+ attrs->cipher_suite_info_len = attr_len;
+ break;
+ case NAN_ATTR_SCIA:
+ attrs->sec_ctxt_info = pos;
+ attrs->sec_ctxt_info_len = attr_len;
+ break;
+ case NAN_ATTR_SHARED_KEY_DESCR:
+ attrs->shared_key_desc = pos;
+ attrs->shared_key_desc_len = attr_len;
+ break;
+ case NAN_ATTR_MASTER_INDICATION:
+ case NAN_ATTR_CLUSTER:
+ case NAN_ATTR_NAN_ATTR_SERVICE_ID_LIST:
+ case NAN_ATTR_SDA:
+ case NAN_ATTR_CONN_CAPA:
+ case NAN_ATTR_WLAN_INFRA:
+ case NAN_ATTR_P2P_OPER:
+ case NAN_ATTR_IBSS:
+ case NAN_ATTR_MESH:
+ case NAN_ATTR_FURTHER_NAN_SD:
+ case NAN_ATTR_FURTHER_AVAIL_MAP:
+ case NAN_ATTR_COUNTRY_CODE:
+ case NAN_ATTR_RANGING:
+ case NAN_ATTR_CLUSTER_DISCOVERY:
+ case NAN_ATTR_UNALIGNED_SCHEDULE:
+ case NAN_ATTR_RANGING_INFO:
+ case NAN_ATTR_RANGING_SETUP:
+ case NAN_ATTR_FTM_RANGING_REPORT:
+ case NAN_ATTR_EXT_WLAN_INFRA:
+ case NAN_ATTR_EXT_P2P_OPER:
+ case NAN_ATTR_EXT_IBSS:
+ case NAN_ATTR_EXT_MESH:
+ case NAN_ATTR_PUBLIC_AVAILABILITY:
+ case NAN_ATTR_SUBSC_SERVICE_ID_LIST:
+ case NAN_ATTR_NDP_EXT:
+ case NAN_ATTR_DCEA:
+ case NAN_ATTR_NIRA:
+ case NAN_ATTR_BPBA:
+ case NAN_ATTR_S3:
+ case NAN_ATTR_TPEA:
+ case NAN_ATTR_VENDOR_SPECIFIC:
+ wpa_printf(MSG_DEBUG, "NAN: ignore attr=%u", id);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "NAN: unknown attr=%u", id);
+ break;
+ }
+
+ pos += attr_len;
+ }
+
+ /* Parsing is considered success only if all attributes were consumed */
+ if (pos == end)
+ return 0;
+
+fail:
+ nan_attrs_clear(nan, attrs);
+ return -1;
+}
+
+
+/*
+ * nan_is_naf - Check if a given frame is a NAN action frame
+ *
+ * @nan: NAN module context from nan_init()
+ * @mgmt: NAN action frame
+ * @len: Length of the management frame in octets
+ * Returns: true if NAF; otherwise false
+ */
+bool nan_is_naf(struct nan_data *nan, const struct ieee80211_mgmt *mgmt,
+ size_t len)
+{
+ u8 subtype;
+
+ /*
+ * 802.11 header + category + NAN action frame minimal + subtype (1)
+ */
+ if (len < IEEE80211_MIN_ACTION_LEN(naf)) {
+ wpa_printf(MSG_DEBUG, "Too short NAN frame");
+ return false;
+ }
+
+ if (mgmt->u.action.u.naf.action_code != WLAN_PA_VENDOR_SPECIFIC ||
+ mgmt->u.action.u.naf.oui[0] != ((OUI_WFA >> 16) & 0xff) ||
+ mgmt->u.action.u.naf.oui[1] != ((OUI_WFA >> 8) & 0xff) ||
+ mgmt->u.action.u.naf.oui[2] != (OUI_WFA & 0xff) ||
+ mgmt->u.action.u.naf.oui_type != NAN_TYPE_NAF)
+ return false;
+
+ subtype = mgmt->u.action.u.naf.subtype;
+
+ if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
+ !(subtype >= NAN_SUBTYPE_DATA_PATH_REQUEST &&
+ subtype <= NAN_SUBTYPE_DATA_PATH_TERMINATION &&
+ mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL)) {
+ wpa_printf(MSG_DEBUG, "NAN: Invalid action category for NAF");
+ return false;
+ }
+
+ return true;
+}
+
+
+/*
+ * nan_parse_naf - Parse a NAN Action frame content
+ *
+ * @nan: NAN module context from nan_init()
+ * @mgmt: NAN action frame.
+ * @len: Length of the management frame in octets
+ * @msg: Buffer for returning parsed attributes
+ * Returns: 0 on success; positive or negative indicate an error
+ *
+ * Note: in case of success, the caller must free temporary memory allocations
+ * by calling nan_attrs_clear() when the parsed data is not needed anymore. In
+ * addition, as the &mgmt is referenced from the returned structure, the caller
+ * must ensure that the frame buffer remains valid an unmodified as long as the
+ * &msg object is used.
+ */
+int nan_parse_naf(struct nan_data *nan, const struct ieee80211_mgmt *mgmt,
+ size_t len, struct nan_msg *msg)
+{
+ if (!nan_is_naf(nan, mgmt, len))
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "NAN: Parse NAF");
+
+ msg->oui_type = mgmt->u.action.u.naf.oui_type;
+ msg->oui_subtype = mgmt->u.action.u.naf.subtype;
+
+ msg->mgmt = mgmt;
+ msg->len = len;
+
+ return nan_parse_attrs(nan,
+ mgmt->u.action.u.naf.variable,
+ len - IEEE80211_MIN_ACTION_LEN(naf),
+ &msg->attrs);
+}
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index c8941a80eb..3eb338106b 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -338,6 +338,7 @@ ifdef NEED_NAN
OBJS += nan_supplicant.o
OBJS += ../src/nan/nan.o
OBJS += ../src/common/nan_de.o
+OBJS += ../src/nan/nan_util.o
endif
ifdef CONFIG_OWE
--
2.49.0
More information about the Hostap
mailing list