[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