[RFC PATCH 22/34] Add support to handle PR IE in Auth M1 and prepare M2
Peddolla Harshavardhan Reddy
peddolla at qti.qualcomm.com
Thu May 15 00:17:45 PDT 2025
Signed-off-by: Peddolla Harshavardhan Reddy <peddolla at qti.qualcomm.com>
---
src/common/proximity_ranging.c | 494 ++++++++++++++++++++++++++++++++-
src/common/proximity_ranging.h | 13 +
2 files changed, 505 insertions(+), 2 deletions(-)
diff --git a/src/common/proximity_ranging.c b/src/common/proximity_ranging.c
index a94c51bf0..2290369ec 100644
--- a/src/common/proximity_ranging.c
+++ b/src/common/proximity_ranging.c
@@ -23,6 +23,7 @@
static void pr_device_free(struct pr_data *pr, struct pr_device *dev)
{
+ wpabuf_free(dev->ranging_wrapper);
#ifdef CONFIG_PASN
if (dev->pasn) {
wpa_pasn_reset(dev->pasn);
@@ -449,6 +450,65 @@ static void pr_copy_channels(struct pr_channels *dst,
dst->reg_classes = j;
}
+
+static void pr_reg_class_intersect(const struct pr_reg_class *a,
+ const struct pr_reg_class *b,
+ struct pr_reg_class *res)
+{
+ size_t i, j;
+
+ res->reg_class = a->reg_class;
+ for (i = 0; i < a->channels; i++) {
+ for (j = 0; j < b->channels; j++) {
+ if (a->channel[i] != b->channel[j])
+ continue;
+ res->channel[res->channels] = a->channel[i];
+ res->channels++;
+ if (res->channels == PR_MAX_REG_CLASS_CHANNELS)
+ return;
+ }
+ }
+}
+
+
+/**
+ * pr_channels_intersect - Intersection of supported channel lists
+ * @a: First set of supported channels
+ * @b: Second set of supported channels
+ * @res: Data structure for returning the intersection of support channels
+ *
+ * This function can be used to find a common set of supported channels. Both
+ * input channels sets are assumed to use the same country code. If different
+ * country codes are used, the regulatory class numbers may not be matched
+ * correctly and results are undefined.
+ */
+static void pr_channels_intersect(const struct pr_channels *a,
+ const struct pr_channels *b,
+ struct pr_channels *res)
+{
+ size_t i, j;
+ const struct pr_reg_class *a_reg;
+ const struct pr_reg_class *b_reg;
+
+ os_memset(res, 0, sizeof(*res));
+
+ for (i = 0; i < a->reg_classes; i++) {
+ a_reg = &a->reg_class[i];
+ for (j = 0; j < b->reg_classes; j++) {
+ b_reg = &b->reg_class[j];
+ if (a_reg->reg_class != b_reg->reg_class)
+ continue;
+ pr_reg_class_intersect(a_reg, b_reg,
+ &res->reg_class[res->reg_classes]);
+ if (res->reg_class[res->reg_classes].channels) {
+ res->reg_classes++;
+ if (res->reg_classes == PR_MAX_REG_CLASSES)
+ return;
+ }
+ }
+ }
+}
+
#endif /* CONFIG_PASN */
static void pr_buf_add_channel_list(struct wpabuf *buf, const char *country,
@@ -1039,6 +1099,210 @@ static void pr_buf_add_operation_mode(struct wpabuf *buf,
}
+static void pr_buf_add_ranging_neg_status(struct wpabuf *buf, u8 status)
+{
+ u8 *len;
+
+ wpabuf_put_u8(buf, PR_ATTR_STATUS);
+ len = wpabuf_put(buf, 2);
+
+ wpabuf_put_u8(buf, status);
+
+ WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
+ wpa_hexdump(MSG_DEBUG, "PR: * Role negotiation status",
+ len + 2, (u8 *) wpabuf_put(buf, 0) - len - 2);
+}
+
+
+static void pr_process_op_mode(const u8 *caps, size_t caps_len,
+ struct operation_mode *op_mode)
+{
+ const u8 *pos, *end;
+
+ if (!caps)
+ return;
+
+ pos = caps;
+ end = caps + caps_len;
+
+ op_mode->protocol_type = *pos;
+
+ pos++;
+ op_mode->role = *pos;
+
+ pos++;
+ os_memcpy(op_mode->country, pos, 3);
+
+ pos += 3;
+ pr_process_channels(pos, end - pos, &op_mode->channels);
+
+ wpa_printf(MSG_INFO, "protocol type=%u, role type=%u, "
+ "country=%c%c, channel size=%lu",
+ op_mode->protocol_type, op_mode->role,
+ op_mode->country[0] ? op_mode->country[0] : '_',
+ op_mode->country[1] ? op_mode->country[1] : '_',
+ op_mode->channels.reg_classes);
+}
+
+
+static int pr_pasn_get_best_op_mode(struct pr_data *pr, u8 supp_roles,
+ struct operation_mode *op_mode,
+ struct operation_mode *res_op_mode)
+{
+ int status = -1;
+ struct pr_channels common_chan;
+
+ if (op_mode->protocol_type & PR_NTB_SECURE_LTF_BASED_RANGING ||
+ op_mode->protocol_type & PR_NTB_OPEN_BASED_RANGING) {
+ if ((!pr->cfg->ntb_ista_support &&
+ !pr->cfg->ntb_rsta_support) ||
+ (op_mode->protocol_type & PR_NTB_SECURE_LTF_BASED_RANGING &&
+ !pr->cfg->secure_he_ltf)) {
+ wpa_printf(MSG_DEBUG,
+ "PR: Device can't fulfill requested NTB role");
+ return -1;
+ }
+
+ if (op_mode->protocol_type & PR_NTB_SECURE_LTF_BASED_RANGING &&
+ !pr->cfg->secure_he_ltf) {
+ wpa_printf(MSG_DEBUG, "PR: Secure HE-LTF not supported");
+ return -1;
+ }
+
+ pr_channels_intersect(&pr->cfg->ntb_channels,
+ &op_mode->channels, &common_chan);
+ if (!common_chan.reg_classes) {
+ wpa_printf(MSG_DEBUG,
+ "PR: No common channels found to do ranging");
+ return -1;
+ }
+
+ if (op_mode->role == PR_ISTA_SUPPORT &&
+ !pr->cfg->ntb_rsta_support &&
+ !(supp_roles & PR_RSTA_SUPPORT)) {
+ wpa_printf(MSG_DEBUG, "PR: Device cannot act as NTB RSTA");
+ return -1;
+ } else if (op_mode->role == PR_RSTA_SUPPORT &&
+ !pr->cfg->ntb_ista_support &&
+ !(supp_roles & PR_ISTA_SUPPORT)) {
+ wpa_printf(MSG_DEBUG, "PR: Device cannot act as NTB ISTA");
+ return -1;
+ }
+
+ if (op_mode->role == PR_ISTA_SUPPORT &&
+ pr->cfg->ntb_rsta_support) {
+ res_op_mode->role = PR_RSTA_SUPPORT;
+ status = 0;
+ } else if (op_mode->role == PR_RSTA_SUPPORT &&
+ pr->cfg->ntb_ista_support) {
+ res_op_mode->role = PR_ISTA_SUPPORT;
+ status = 0;
+ } else if (op_mode->role == PR_ISTA_SUPPORT &&
+ !pr->cfg->ntb_rsta_support &&
+ supp_roles & PR_RSTA_SUPPORT &&
+ pr->cfg->ntb_ista_support) {
+ res_op_mode->role = PR_ISTA_SUPPORT;
+ status = 1;
+ } else if (op_mode->role == PR_RSTA_SUPPORT &&
+ !pr->cfg->ntb_ista_support &&
+ (supp_roles & PR_ISTA_SUPPORT) &&
+ pr->cfg->ntb_rsta_support) {
+ res_op_mode->role = PR_RSTA_SUPPORT;
+ status = 1;
+ } else if (op_mode->role == (PR_RSTA_SUPPORT |
+ PR_ISTA_SUPPORT)) {
+ if (pr->cfg->ntb_rsta_support) {
+ res_op_mode->role = PR_RSTA_SUPPORT;
+ status = 0;
+ wpa_printf(MSG_DEBUG, "PR: Device choosing RSTA role");
+ } else if (pr->cfg->ntb_rsta_support) {
+ res_op_mode->role = PR_ISTA_SUPPORT;
+ status = 0;
+ wpa_printf(MSG_DEBUG, "PR: Device choosing ISTA role");
+ }
+ }
+
+ res_op_mode->protocol_type = op_mode->protocol_type;
+ os_memcpy(res_op_mode->country, pr->cfg->country, 3);
+ pr_copy_channels(&res_op_mode->channels, &common_chan, false);
+
+ wpa_printf(MSG_DEBUG,
+ "NTB attr Ranging Protocol=%u, Role=%u, country=%c%c,"
+ " status=%d, op class size= %lu",
+ res_op_mode->protocol_type, res_op_mode->role,
+ res_op_mode->country[0] ? res_op_mode->country[0] : '_',
+ res_op_mode->country[1] ? res_op_mode->country[1] : '_',
+ status, res_op_mode->channels.reg_classes);
+
+ return status;
+ } else if (op_mode->protocol_type & PR_EDCA_BASED_RANGING) {
+ if (!pr->cfg->edca_ista_support &&
+ !pr->cfg->edca_rsta_support) {
+ wpa_printf(MSG_DEBUG,
+ "PR: Device can't fulfill requested EDCA role");
+ return -1;
+ }
+ pr_channels_intersect(&pr->cfg->edca_channels,
+ &op_mode->channels, &common_chan);
+ if (!common_chan.reg_classes) {
+ wpa_printf(MSG_DEBUG,
+ "PR: No common channels to perform ranging");
+ return -1;
+ }
+
+ if (op_mode->role == PR_ISTA_SUPPORT &&
+ !pr->cfg->edca_rsta_support &&
+ !(supp_roles & PR_RSTA_SUPPORT)) {
+ wpa_printf(MSG_DEBUG, "PR: Device cannot act as NTB RSTA");
+ return -1;
+ } else if (op_mode->role == PR_RSTA_SUPPORT &&
+ !pr->cfg->edca_ista_support &&
+ !(supp_roles & PR_ISTA_SUPPORT)) {
+ wpa_printf(MSG_DEBUG, "PR: Device cannot act as NTB ISTA");
+ return -1;
+ }
+
+ if (op_mode->role == PR_ISTA_SUPPORT &&
+ pr->cfg->edca_rsta_support) {
+ res_op_mode->role = PR_RSTA_SUPPORT;
+ status = 0;
+ } else if (op_mode->role == PR_RSTA_SUPPORT &&
+ pr->cfg->edca_ista_support) {
+ res_op_mode->role = PR_ISTA_SUPPORT;
+ status = 0;
+ } else if (op_mode->role == PR_ISTA_SUPPORT &&
+ !pr->cfg->edca_rsta_support &&
+ (supp_roles & PR_RSTA_SUPPORT) &&
+ pr->cfg->edca_ista_support) {
+ res_op_mode->role = PR_ISTA_SUPPORT;
+ status = 1;
+ } else if (op_mode->role == PR_RSTA_SUPPORT &&
+ !pr->cfg->edca_ista_support &&
+ (supp_roles & PR_ISTA_SUPPORT) &&
+ pr->cfg->edca_rsta_support) {
+ res_op_mode->role = PR_RSTA_SUPPORT;
+ status = 1;
+ }
+
+ res_op_mode->protocol_type = op_mode->protocol_type;
+ os_memcpy(res_op_mode->country, pr->cfg->country, 3);
+ pr_copy_channels(&res_op_mode->channels, &common_chan, false);
+
+ wpa_printf(MSG_DEBUG,
+ "EDCA attr Ranging Protocol=%u, Role=%u, country=%c%c,"
+ " status=%d, op class size=%lu",
+ res_op_mode->protocol_type, res_op_mode->role,
+ res_op_mode->country[0] ? res_op_mode->country[0] : '_',
+ res_op_mode->country[1] ? res_op_mode->country[1] : '_',
+ status, res_op_mode->channels.reg_classes);
+
+ return status;
+ }
+
+ return status;
+}
+
+
static int pr_prepare_pasn_pr_ie(struct pr_data *pr, struct wpabuf *extra_ies,
bool add_dira, u8 ranging_role,
u8 ranging_type, int forced_pr_freq)
@@ -1497,11 +1761,233 @@ int pr_pasn_auth_tx_status(struct pr_data *pr, const u8 *data, size_t data_len,
}
+static int pr_process_pasn_ranging_wrapper(struct pr_data *pr,
+ struct pr_device *dev,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, int trans_seq)
+{
+ u32 ie_type;
+ const u8 *ies;
+ size_t ies_len;
+ int status = -1;
+ bool success = false;
+ struct wpabuf *buf, *buf2;
+ struct pr_message msg;
+ struct pr_capabilities caps;
+ struct edca_capabilities edca;
+ struct ntb_capabilities ntb;
+ struct operation_mode op_mode;
+ u8 supp_ranging_role = 0;
+ struct operation_mode res_op_mode;
+ bool ntb_caps_valid = false, edca_caps_valid = false;
+
+ buf = wpabuf_alloc(1000);
+ if (!buf) {
+ wpa_printf(MSG_DEBUG, "PR: Memory allocation failed");
+ return -1;
+ }
+
+ os_memset(&msg, 0, sizeof(msg));
+ ies = mgmt->u.auth.variable;
+ ies_len = len - offsetof(struct ieee80211_mgmt, u.auth.variable);
+
+ if (pr_parse_ies(ies, ies_len, &msg) || !msg.op_mode) {
+ wpa_printf(MSG_DEBUG,
+ "PR PASN: Failed to parse PR IE in Auth1");
+ goto end;
+ }
+
+ if (msg.dira && msg.dira_len)
+ pr_validate_dira(pr, dev, msg.dira, msg.dira_len);
+
+ if (trans_seq == 2) {
+ if (!msg.status_ie || !msg.status_ie_len) {
+ wpa_printf(MSG_DEBUG, "PR PASN: * No status attribute");
+ goto end;
+ }
+ if (*msg.status_ie == PR_NEGOTIATION_FAIL) {
+ wpa_printf(MSG_DEBUG,
+ "PR PASN: * Ranging Negotiation status fail");
+ goto end;
+ }
+ }
+
+ if (!msg.op_mode || !msg.op_mode_len ||
+ !msg.pr_capability || !msg.pr_capability_len ||
+ ((!msg.edca_capability || !msg.edca_capability_len) &&
+ (!msg.ntb_capability || !msg.ntb_capability_len))) {
+ goto end;
+ }
+
+ pr_process_op_mode(msg.op_mode, msg.op_mode_len, &op_mode);
+ if (!op_mode.channels.reg_classes) {
+ wpa_printf(MSG_DEBUG, "PR: Received empty channel list");
+ goto end;
+ }
+
+ os_memset(&caps, 0, sizeof(struct pr_capabilities));
+ pr_process_ranging_capabilities(msg.pr_capability,
+ msg.pr_capability_len, &caps);
+
+ pr_get_ranging_capabilities(pr, &caps);
+
+ if ((op_mode.protocol_type & PR_EDCA_BASED_RANGING) &&
+ (!msg.edca_capability || !msg.edca_capability_len)) {
+ goto end;
+ } else if ((op_mode.protocol_type & PR_NTB_OPEN_BASED_RANGING) &&
+ (!msg.ntb_capability || !msg.ntb_capability_len)) {
+ goto end;
+ } else if ((op_mode.protocol_type & PR_NTB_SECURE_LTF_BASED_RANGING) &&
+ (!msg.ntb_capability || !msg.ntb_capability_len)) {
+ goto end;
+ }
+
+ if (op_mode.protocol_type & PR_NTB_SECURE_LTF_BASED_RANGING ||
+ op_mode.protocol_type & PR_NTB_OPEN_BASED_RANGING) {
+ pr_process_ntb_capabilities(msg.ntb_capability,
+ msg.ntb_capability_len, &ntb,
+ caps.secure_he_ltf);
+
+ if (dev->ntb_caps.ista_support)
+ supp_ranging_role |= PR_ISTA_SUPPORT;
+ if (dev->ntb_caps.rsta_support)
+ supp_ranging_role |= PR_RSTA_SUPPORT;
+
+ pr_get_ntb_capabilities(pr, &ntb);
+ ntb_caps_valid = true;
+
+ } else if (op_mode.protocol_type & PR_EDCA_BASED_RANGING) {
+ pr_process_edca_capabilities(msg.edca_capability,
+ msg.edca_capability_len, &edca);
+
+ if (dev->edca_caps.ista_support)
+ supp_ranging_role |= PR_ISTA_SUPPORT;
+ if (dev->edca_caps.rsta_support)
+ supp_ranging_role |= PR_RSTA_SUPPORT;
+
+ pr_get_edca_capabilities(pr, &edca);
+ edca_caps_valid = true;
+ }
+
+ if (trans_seq == 1)
+ status = pr_pasn_get_best_op_mode(pr, supp_ranging_role,
+ &op_mode, &res_op_mode);
+
+ if (status != 0 && status != 1) {
+ wpa_printf(MSG_DEBUG, "PR: Couldn't derive suitable operation mode");
+ goto end;
+ }
+
+ pr_buf_add_ranging_capa_info(buf, &caps);
+ if (edca_caps_valid)
+ pr_buf_add_edca_capa_info(buf, &edca);
+ if (ntb_caps_valid)
+ pr_buf_add_ntb_capa_info(buf, &ntb);
+
+ if (status == 0) {
+ pr_buf_add_ranging_neg_status(buf, PR_NEGOTIATION_SUCCESS);
+ pr_buf_add_operation_mode(buf, &res_op_mode);
+ } else if (status == 1) {
+ pr_buf_add_ranging_neg_status(buf, PR_NEGOTIATION_UPDATE);
+ pr_buf_add_operation_mode(buf, &res_op_mode);
+ }
+
+ if (trans_seq == 2) {
+ dev->ranging_role = res_op_mode.role;
+ dev->protocol_type = res_op_mode.protocol_type;
+ dev->final_op_channel = res_op_mode.channels.reg_class[0].channel[0];
+ dev->final_op_class = res_op_mode.channels.reg_class[0].reg_class;
+ }
+ success = true;
+end:
+ if (!success)
+ pr_buf_add_ranging_neg_status(buf, PR_NEGOTIATION_FAIL);
+
+ ie_type = (OUI_WFA << 8) | PR_OUI_TYPE;
+ buf2 = pr_encaps_ie(buf, ie_type);
+ wpabuf_free(buf);
+
+ dev->ranging_wrapper = buf2;
+ pr_parse_free(&msg);
+ return 0;
+}
+
+
+static int pr_pasn_handle_auth_1(struct pr_data *pr, struct pr_device *dev,
+ const struct ieee80211_mgmt *mgmt, size_t len,
+ int freq)
+{
+ int ret = -1;
+ u8 pasn_type;
+ u8 auth_mode = 0;
+ int pasn_groups[4] = { 0 };
+ struct wpa_ie_data rsn_data;
+ struct ieee802_11_elems elems;
+
+ pasn_type = pr->cfg->pasn_type;
+ if (pasn_type & 0xc && pasn_type & 0x3) {
+ pasn_groups[0] = 20;
+ pasn_groups[1] = 19;
+ } else if (pasn_type & 0xc) {
+ pasn_groups[0] = 20;
+ } else {
+ pasn_groups[0] = 19;
+ }
+
+ if (pr_process_pasn_ranging_wrapper(pr, dev, mgmt, len, 1)) {
+ wpa_printf(MSG_ERROR,
+ "PR PASN: Handle Auth1 action wrapper failed");
+ return -1;
+ }
+
+ if (ieee802_11_parse_elems(mgmt->u.auth.variable,
+ len - offsetof(struct ieee80211_mgmt,
+ u.auth.variable),
+ &elems, 0) == ParseFailed) {
+ wpa_printf(MSG_DEBUG,
+ "PR PASN: Failed parsing elements in Auth1 frame");
+ goto fail;
+ }
+
+ if (wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+ &rsn_data) == 0) {
+ if (rsn_data.key_mgmt == WPA_KEY_MGMT_SAE && rsn_data.num_pmkid)
+ auth_mode = PR_PASN_AUTH_MODE_PMK;
+ else if (rsn_data.key_mgmt == WPA_KEY_MGMT_SAE)
+ auth_mode = PR_PASN_AUTH_MODE_SAE;
+ else
+ auth_mode = PR_PASN_AUTH_MODE_PASN;
+ }
+
+ dev->pasn_role = PR_ROLE_PASN_RESPONDER;
+ if (pr_pasn_initialize(pr, dev, mgmt->sa, auth_mode, freq,
+ rsn_data.pmkid)) {
+ wpa_printf(MSG_ERROR, "PR PASN: Initialize failed");
+ goto fail;
+ }
+ pasn_set_extra_ies(dev->pasn, wpabuf_head_u8(dev->ranging_wrapper),
+ wpabuf_len(dev->ranging_wrapper));
+ dev->pasn->pasn_groups = pasn_groups;
+ if (handle_auth_pasn_1(dev->pasn, pr->cfg->dev_addr, mgmt->sa, mgmt,
+ len, false) < 0) {
+ wpa_printf(MSG_ERROR, "PR PASN: Handle Auth1 failed");
+ goto fail;
+ }
+
+ ret = 0;
+
+fail:
+ wpabuf_free(dev->ranging_wrapper);
+ dev->ranging_wrapper = NULL;
+ return ret;
+}
+
+
int pr_pasn_auth_rx(struct pr_data *pr, const struct ieee80211_mgmt *mgmt,
size_t len, int freq)
{
struct pr_device *dev;
- u16 auth_alg;
+ u16 auth_alg, auth_transaction;
dev = pr_get_device(pr, mgmt->sa);
if (!dev) {
@@ -1525,7 +2011,11 @@ int pr_pasn_auth_rx(struct pr_data *pr, const struct ieee80211_mgmt *mgmt,
return -1;
}
- return 0;
+ auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
+
+ if (auth_transaction == 1)
+ return pr_pasn_handle_auth_1(pr, dev, mgmt, len, freq);
+ return -1;
}
#endif /* CONFIG_PASN */
diff --git a/src/common/proximity_ranging.h b/src/common/proximity_ranging.h
index 3a351821a..92bf56f6e 100644
--- a/src/common/proximity_ranging.h
+++ b/src/common/proximity_ranging.h
@@ -38,6 +38,13 @@
#define PR_MAX_PEER 100
+/**
+ * Proximity Ranging negotiation status
+ */
+#define PR_NEGOTIATION_SUCCESS 0
+#define PR_NEGOTIATION_FAIL 1
+#define PR_NEGOTIATION_UPDATE 2
+
enum pr_pasn_role {
PR_ROLE_IDLE = 0,
PR_ROLE_PASN_INITIATOR,
@@ -228,7 +235,13 @@ struct pr_device {
/* PASN data structure */
struct pasn_data *pasn;
+ struct wpabuf *ranging_wrapper;
+
enum pr_pasn_role pasn_role;
+ u8 ranging_role;
+ u8 protocol_type;
+ u8 final_op_class;
+ u8 final_op_channel;
};
--
2.34.1
More information about the Hostap
mailing list