Signed-off-by: Peddolla Harshavardhan Reddy <peddolla@xxxxxxxxxxxxxxxx> --- 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 _______________________________________________ Hostap mailing list Hostap@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/hostap