From: Kavita Kavita Extend the peer measurement framework with a NAN FTM request type to allow userspace (e.g., wpa_supplicant) to initiate a NAN Ranging Request in offload mode. Extend the nl80211 parser to handle NAN-specific FTM parameters and validate the request type so the driver/firmware can identify and trigger the NAN Ranging Session on behalf of userspace. Reject the request if it is a NAN Ranging Request and the interface is not a NAN interface, as mandated by the Wi-Fi Aware Specification, or if the driver does not advertise NAN ranging capability. Signed-off-by: Kavita Kavita Signed-off-by: Shivani Baranwal --- include/net/cfg80211.h | 10 ++- include/uapi/linux/nl80211.h | 11 ++++ net/wireless/nl80211.c | 5 +- net/wireless/nl80211.h | 3 + net/wireless/pmsr.c | 120 ++++++++++++++++++++++++++++++++++- 5 files changed, 146 insertions(+), 3 deletions(-) diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 124480276b6b..59f921c578c2 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4726,6 +4726,8 @@ struct cfg80211_pmsr_result { * @trigger_based or @non_trigger_based is set. * @rsta: Operate as the RSTA in the measurement. Only valid if @lmr_feedback * and either @trigger_based or @non_trigger_based is set. + * Also valid when @range_report is set and @request_type is + * %NL80211_PMSR_FTM_REQ_TYPE_NAN. * @bss_color: the bss color of the responder. Optional. Set to zero to * indicate the driver should set the BSS color. Only valid if * @non_trigger_based or @trigger_based is set. @@ -4771,6 +4773,10 @@ struct cfg80211_pmsr_result { * provide ranging services to a peer (e.g. in RSTA role) but does * not report the measurement results to userspace. Only valid when * @request_type is %NL80211_PMSR_FTM_REQ_TYPE_PD. + * @range_report: a NAN Ranging Report is required after the FTM session. + * Only valid for a NAN RSTA request. + * @min_delta_ftm: NAN Min Delta FTM value in units of 100 microseconds. + * Zero indicates no preference. * * See also nl80211 for the respective attribute documentation. */ @@ -4787,6 +4793,7 @@ struct cfg80211_pmsr_ftm_request_peer { rsta:1; u8 num_bursts_exp; u8 burst_duration; + u8 min_delta_ftm; u8 ftms_per_burst; u8 ftmr_retries; u8 bss_color; @@ -4799,7 +4806,8 @@ struct cfg80211_pmsr_ftm_request_peer { u32 num_measurements; u64 ingress_distance; u64 egress_distance; - u8 pd_suppress_range_results:1; + u8 pd_suppress_range_results:1, + range_report:1; }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 793015750f3f..ab35ff8082ad 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -8219,12 +8219,16 @@ enum nl80211_peer_measurement_resp { * @NL80211_PMSR_FTM_REQ_TYPE_PD: peer-to-peer ranging as defined in the * Wi-Fi Alliance specification "Proximity Ranging (PR) Implementation * Consideration Draft 1.9 Rev 1" + * @NL80211_PMSR_FTM_REQ_TYPE_NAN: NAN ranging between two NAN devices as + * defined in the "Wi-Fi Aware Specification v4.0, clause 8". + * Must be issued on a %NL80211_IFTYPE_NAN interface. * @NUM_NL80211_PMSR_FTM_REQ_TYPE: internal * @NL80211_PMSR_FTM_REQ_TYPE_MAX: highest request type value */ enum nl80211_peer_measurement_ftm_req_type { NL80211_PMSR_FTM_REQ_TYPE_INFRA, NL80211_PMSR_FTM_REQ_TYPE_PD, + NL80211_PMSR_FTM_REQ_TYPE_NAN, /* keep last */ NUM_NL80211_PMSR_FTM_REQ_TYPE, @@ -8592,6 +8596,11 @@ enum nl80211_peer_measurement_ftm_type_capa { * but results are not reported to userspace, regardless of ranging role * or type. Only valid when %NL80211_PMSR_PEER_ATTR_REQ_TYPE is set to * %NL80211_PMSR_FTM_REQ_TYPE_PD. + * @NL80211_PMSR_FTM_REQ_ATTR_RANGE_REPORT: Flag indicating that a NAN + * Ranging Report is required after the FTM session. Only valid for a NAN + * RSTA request. + * @NL80211_PMSR_FTM_REQ_ATTR_MIN_DELTA_FTM: NAN Min Delta FTM value in units + * of 100 microseconds (u8, 0-63). A value of 0 indicates no preference. * * @NUM_NL80211_PMSR_FTM_REQ_ATTR: internal * @NL80211_PMSR_FTM_REQ_ATTR_MAX: highest attribute number @@ -8622,6 +8631,8 @@ enum nl80211_peer_measurement_ftm_req { NL80211_PMSR_FTM_REQ_ATTR_INGRESS, NL80211_PMSR_FTM_REQ_ATTR_EGRESS, NL80211_PMSR_FTM_REQ_ATTR_PD_SUPPRESS_RESULTS, + NL80211_PMSR_FTM_REQ_ATTR_RANGE_REPORT, + NL80211_PMSR_FTM_REQ_ATTR_MIN_DELTA_FTM, /* keep last */ NUM_NL80211_PMSR_FTM_REQ_ATTR, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 18da5cd456ef..412d4e599046 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -557,7 +557,7 @@ nl80211_ftm_responder_policy[NL80211_FTM_RESP_ATTR_MAX + 1] = { .len = U8_MAX }, }; -static const struct nla_policy +const struct nla_policy nl80211_pmsr_ftm_req_attr_policy[NL80211_PMSR_FTM_REQ_ATTR_MAX + 1] = { [NL80211_PMSR_FTM_REQ_ATTR_ASAP] = { .type = NLA_FLAG }, [NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE] = @@ -589,6 +589,9 @@ nl80211_pmsr_ftm_req_attr_policy[NL80211_PMSR_FTM_REQ_ATTR_MAX + 1] = { [NL80211_PMSR_FTM_REQ_ATTR_INGRESS] = { .type = NLA_U64 }, [NL80211_PMSR_FTM_REQ_ATTR_EGRESS] = { .type = NLA_U64 }, [NL80211_PMSR_FTM_REQ_ATTR_PD_SUPPRESS_RESULTS] = { .type = NLA_FLAG }, + [NL80211_PMSR_FTM_REQ_ATTR_RANGE_REPORT] = { .type = NLA_FLAG }, + [NL80211_PMSR_FTM_REQ_ATTR_MIN_DELTA_FTM] = + NLA_POLICY_MAX(NLA_U8, 63), }; static const struct nla_policy diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index bdb065d14054..31a2c2eb6276 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -126,6 +126,9 @@ void cfg80211_free_coalesce(struct cfg80211_coalesce *coalesce); /* peer measurement */ int nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info); +extern const struct nla_policy +nl80211_pmsr_ftm_req_attr_policy[NL80211_PMSR_FTM_REQ_ATTR_MAX + 1]; + void nl80211_mlo_reconf_add_done(struct net_device *dev, struct cfg80211_mlo_reconf_done_data *data); diff --git a/net/wireless/pmsr.c b/net/wireless/pmsr.c index 97449bcb9a22..bcf5390174e4 100644 --- a/net/wireless/pmsr.c +++ b/net/wireless/pmsr.c @@ -306,11 +306,108 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev, return 0; } +static int pmsr_parse_ftm_nan(struct cfg80211_registered_device *rdev, + struct nlattr *ftmreq, + struct cfg80211_pmsr_request_peer *out, + struct genl_info *info) +{ + const struct cfg80211_pmsr_capabilities *capa = rdev->wiphy.pmsr_capa; + struct nlattr *tb[NL80211_PMSR_FTM_REQ_ATTR_MAX + 1]; + int err; + + err = nla_parse_nested(tb, NL80211_PMSR_FTM_REQ_ATTR_MAX, ftmreq, + nl80211_pmsr_ftm_req_attr_policy, + info->extack); + if (err) + return err; + + out->ftm.requested = true; + + /* NAN ranging requires ASAP; reject if not set by userspace */ + out->ftm.asap = nla_get_flag(tb[NL80211_PMSR_FTM_REQ_ATTR_ASAP]); + if (!out->ftm.asap) { + NL_SET_ERR_MSG(info->extack, + "FTM: NAN ranging requires ASAP mode"); + return -EINVAL; + } + if (!capa->ftm.asap) { + NL_SET_ERR_MSG(info->extack, + "FTM: NAN ASAP mode is not supported by device"); + return -EOPNOTSUPP; + } + + /* NAN ranging requires single burst (num_bursts_exp must be 0) */ + if (!tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP] || + nla_get_u8(tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP]) != 0) { + NL_SET_ERR_MSG(info->extack, + "FTM: NAN ranging requires single burst"); + return -EINVAL; + } + + out->ftm.rsta = !!tb[NL80211_PMSR_FTM_REQ_ATTR_RSTA]; + out->ftm.range_report = + !!tb[NL80211_PMSR_FTM_REQ_ATTR_RANGE_REPORT]; + if (out->ftm.range_report && !out->ftm.rsta) { + NL_SET_ERR_MSG_ATTR(info->extack, + tb[NL80211_PMSR_FTM_REQ_ATTR_RANGE_REPORT], + "FTM: range report is valid only for NAN RSTA"); + return -EINVAL; + } + + if (tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE]) { + out->ftm.preamble = + nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE]); + if (!(capa->ftm.preambles & BIT(out->ftm.preamble))) { + NL_SET_ERR_MSG_ATTR(info->extack, + tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE], + "FTM: unsupported preamble for NAN ranging"); + return -EINVAL; + } + } + + if (tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST]) { + out->ftm.ftms_per_burst = + nla_get_u8(tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST]); + if (out->ftm.ftms_per_burst > 31) { + NL_SET_ERR_MSG_ATTR(info->extack, + tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST], + "FTM: NAN FTMs per burst exceeds limit of 31"); + return -EINVAL; + } + if (capa->ftm.max_ftms_per_burst && + out->ftm.ftms_per_burst > capa->ftm.max_ftms_per_burst) { + NL_SET_ERR_MSG_ATTR(info->extack, + tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST], + "FTM: FTMs per burst exceeds device limit"); + return -EINVAL; + } + } + + if (tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION]) + out->ftm.burst_duration = + nla_get_u8(tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION]); + + if (tb[NL80211_PMSR_FTM_REQ_ATTR_MIN_DELTA_FTM]) + out->ftm.min_delta_ftm = + nla_get_u8(tb[NL80211_PMSR_FTM_REQ_ATTR_MIN_DELTA_FTM]); + + if (tb[NL80211_PMSR_FTM_REQ_ATTR_INGRESS]) + out->ftm.ingress_distance = + nla_get_u64(tb[NL80211_PMSR_FTM_REQ_ATTR_INGRESS]); + + if (tb[NL80211_PMSR_FTM_REQ_ATTR_EGRESS]) + out->ftm.egress_distance = + nla_get_u64(tb[NL80211_PMSR_FTM_REQ_ATTR_EGRESS]); + + return 0; +} + static int pmsr_parse_peer(struct cfg80211_registered_device *rdev, struct nlattr *peer, struct cfg80211_pmsr_request_peer *out, struct genl_info *info) { + struct wireless_dev *wdev = info->user_ptr[1]; struct nlattr *tb[NL80211_PMSR_PEER_ATTR_MAX + 1]; struct nlattr *req[NL80211_PMSR_REQ_ATTR_MAX + 1]; bool have_measurement_type = false; @@ -344,6 +441,22 @@ static int pmsr_parse_peer(struct cfg80211_registered_device *rdev, "FTM: PD request type not supported by device"); return -EINVAL; } + + if (out->ftm.request_type == NL80211_PMSR_FTM_REQ_TYPE_NAN) { + if (!rdev->wiphy.pmsr_capa->ftm.type.nan_support) { + NL_SET_ERR_MSG_ATTR(info->extack, + tb[NL80211_PMSR_PEER_ATTR_REQ_TYPE], + "FTM: NAN ranging not supported by device"); + return -EOPNOTSUPP; + } + if (wdev->iftype != NL80211_IFTYPE_NAN) { + NL_SET_ERR_MSG_ATTR(info->extack, + tb[NL80211_PMSR_PEER_ATTR_REQ_TYPE], + "FTM: NAN ranging requires a NAN interface"); + return -EOPNOTSUPP; + } + } + /* reuse info->attrs */ memset(info->attrs, 0, sizeof(*info->attrs) * (NL80211_ATTR_MAX + 1)); err = nla_parse_nested_deprecated(info->attrs, NL80211_ATTR_MAX, @@ -390,7 +503,12 @@ static int pmsr_parse_peer(struct cfg80211_registered_device *rdev, switch (nla_type(treq)) { case NL80211_PMSR_TYPE_FTM: - err = pmsr_parse_ftm(rdev, treq, out, info); + if (out->ftm.request_type == + NL80211_PMSR_FTM_REQ_TYPE_NAN) + err = pmsr_parse_ftm_nan(rdev, treq, + out, info); + else + err = pmsr_parse_ftm(rdev, treq, out, info); break; default: NL_SET_ERR_MSG_ATTR(info->extack, treq, -- 2.34.1