| 1 |
|
| 2 | |
| 3 | * Copyright (C) 2018 - 2021, 2023 - 2026 Intel Corporation
|
| 4 |
|
| 5 | #include <net/cfg80211.h>
|
| 6 | #include "core.h"
|
| 7 | #include "nl80211.h"
|
| 8 | #include "rdev-ops.h"
|
| 9 |
|
| 10 | static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev,
|
| 11 | struct nlattr *ftmreq,
|
| 12 | struct cfg80211_pmsr_request_peer *out,
|
| 13 | struct genl_info *info)
|
| 14 | {
|
| 15 | const struct cfg80211_pmsr_capabilities *capa = rdev->wiphy.pmsr_capa;
|
| 16 | struct nlattr *tb[NL80211_PMSR_FTM_REQ_ATTR_MAX + 1];
|
| 17 | u32 preamble = NL80211_PREAMBLE_DMG;
|
| 18 |
|
| 19 |
|
| 20 | if (out->ftm.request_type == NL80211_PMSR_FTM_REQ_TYPE_INFRA &&
|
| 21 | !(capa->ftm.bandwidths & BIT(out->chandef.width))) {
|
| 22 | NL_SET_ERR_MSG(info->extack, "FTM: unsupported bandwidth");
|
| 23 | return -EINVAL;
|
| 24 | }
|
| 25 |
|
| 26 | if (out->ftm.request_type == NL80211_PMSR_FTM_REQ_TYPE_PD &&
|
| 27 | !(capa->ftm.pd_bandwidths & BIT(out->chandef.width))) {
|
| 28 | NL_SET_ERR_MSG(info->extack,
|
| 29 | "FTM: unsupported bandwidth for PD request");
|
| 30 | return -EINVAL;
|
| 31 | }
|
| 32 |
|
| 33 |
|
| 34 | nla_parse_nested_deprecated(tb, NL80211_PMSR_FTM_REQ_ATTR_MAX, ftmreq,
|
| 35 | NULL, NULL);
|
| 36 |
|
| 37 | if (tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE])
|
| 38 | preamble = nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE]);
|
| 39 |
|
| 40 |
|
| 41 | out->ftm.requested = true;
|
| 42 |
|
| 43 | switch (out->chandef.chan->band) {
|
| 44 | case NL80211_BAND_60GHZ:
|
| 45 |
|
| 46 | break;
|
| 47 | default:
|
| 48 | if (!tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE]) {
|
| 49 | NL_SET_ERR_MSG(info->extack,
|
| 50 | "FTM: must specify preamble");
|
| 51 | return -EINVAL;
|
| 52 | }
|
| 53 | }
|
| 54 |
|
| 55 | if (out->ftm.request_type == NL80211_PMSR_FTM_REQ_TYPE_INFRA &&
|
| 56 | !(capa->ftm.preambles & BIT(preamble))) {
|
| 57 | NL_SET_ERR_MSG_ATTR(info->extack,
|
| 58 | tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE],
|
| 59 | "FTM: invalid preamble");
|
| 60 | return -EINVAL;
|
| 61 | }
|
| 62 |
|
| 63 | if (out->ftm.request_type == NL80211_PMSR_FTM_REQ_TYPE_PD &&
|
| 64 | !(capa->ftm.pd_preambles & BIT(preamble))) {
|
| 65 | NL_SET_ERR_MSG_ATTR(info->extack,
|
| 66 | tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE],
|
| 67 | "FTM: invalid preamble for PD request");
|
| 68 | return -EINVAL;
|
| 69 | }
|
| 70 |
|
| 71 | out->ftm.preamble = preamble;
|
| 72 |
|
| 73 | out->ftm.burst_period = 0;
|
| 74 | if (tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD])
|
| 75 | out->ftm.burst_period =
|
| 76 | nla_get_u16(tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD]);
|
| 77 |
|
| 78 | out->ftm.asap = !!tb[NL80211_PMSR_FTM_REQ_ATTR_ASAP];
|
| 79 | if (out->ftm.asap && !capa->ftm.asap) {
|
| 80 | NL_SET_ERR_MSG_ATTR(info->extack,
|
| 81 | tb[NL80211_PMSR_FTM_REQ_ATTR_ASAP],
|
| 82 | "FTM: ASAP mode not supported");
|
| 83 | return -EINVAL;
|
| 84 | }
|
| 85 |
|
| 86 | if (!out->ftm.asap && !capa->ftm.non_asap) {
|
| 87 | NL_SET_ERR_MSG(info->extack,
|
| 88 | "FTM: non-ASAP mode not supported");
|
| 89 | return -EINVAL;
|
| 90 | }
|
| 91 |
|
| 92 | out->ftm.num_bursts_exp = 0;
|
| 93 | if (tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP])
|
| 94 | out->ftm.num_bursts_exp =
|
| 95 | nla_get_u8(tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP]);
|
| 96 |
|
| 97 | if (capa->ftm.max_bursts_exponent >= 0 &&
|
| 98 | out->ftm.num_bursts_exp > capa->ftm.max_bursts_exponent) {
|
| 99 | NL_SET_ERR_MSG_ATTR(info->extack,
|
| 100 | tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP],
|
| 101 | "FTM: max NUM_BURSTS_EXP must be set lower than the device limit");
|
| 102 | return -EINVAL;
|
| 103 | }
|
| 104 |
|
| 105 | out->ftm.ftms_per_burst = 0;
|
| 106 | if (tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST])
|
| 107 | out->ftm.ftms_per_burst =
|
| 108 | nla_get_u8(tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST]);
|
| 109 |
|
| 110 | if (capa->ftm.max_ftms_per_burst &&
|
| 111 | out->ftm.ftms_per_burst > capa->ftm.max_ftms_per_burst) {
|
| 112 | NL_SET_ERR_MSG_ATTR(info->extack,
|
| 113 | tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST],
|
| 114 | "FTM: FTMs per burst must be set lower than the device limit");
|
| 115 | return -EINVAL;
|
| 116 | }
|
| 117 |
|
| 118 | out->ftm.ftmr_retries = 3;
|
| 119 | if (tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES])
|
| 120 | out->ftm.ftmr_retries =
|
| 121 | nla_get_u8(tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES]);
|
| 122 |
|
| 123 | out->ftm.request_lci = !!tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI];
|
| 124 | if (out->ftm.request_lci && !capa->ftm.request_lci) {
|
| 125 | NL_SET_ERR_MSG_ATTR(info->extack,
|
| 126 | tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI],
|
| 127 | "FTM: LCI request not supported");
|
| 128 | return -EOPNOTSUPP;
|
| 129 | }
|
| 130 |
|
| 131 | out->ftm.request_civicloc =
|
| 132 | !!tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC];
|
| 133 | if (out->ftm.request_civicloc && !capa->ftm.request_civicloc) {
|
| 134 | NL_SET_ERR_MSG_ATTR(info->extack,
|
| 135 | tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC],
|
| 136 | "FTM: civic location request not supported");
|
| 137 | return -EOPNOTSUPP;
|
| 138 | }
|
| 139 |
|
| 140 | out->ftm.trigger_based =
|
| 141 | !!tb[NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED];
|
| 142 | if (out->ftm.trigger_based && !capa->ftm.trigger_based) {
|
| 143 | NL_SET_ERR_MSG_ATTR(info->extack,
|
| 144 | tb[NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED],
|
| 145 | "FTM: trigger based ranging is not supported");
|
| 146 | return -EINVAL;
|
| 147 | }
|
| 148 |
|
| 149 | if (out->ftm.request_type == NL80211_PMSR_FTM_REQ_TYPE_PD &&
|
| 150 | out->ftm.trigger_based) {
|
| 151 | NL_SET_ERR_MSG_ATTR(info->extack,
|
| 152 | ftmreq,
|
| 153 | "FTM: TB ranging is not supported for PD request type");
|
| 154 | return -EINVAL;
|
| 155 | }
|
| 156 |
|
| 157 | out->ftm.non_trigger_based =
|
| 158 | !!tb[NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED];
|
| 159 | if (out->ftm.non_trigger_based && !capa->ftm.non_trigger_based) {
|
| 160 | NL_SET_ERR_MSG_ATTR(info->extack,
|
| 161 | tb[NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED],
|
| 162 | "FTM: trigger based ranging is not supported");
|
| 163 | return -EINVAL;
|
| 164 | }
|
| 165 |
|
| 166 | if (out->ftm.trigger_based && out->ftm.non_trigger_based) {
|
| 167 | NL_SET_ERR_MSG(info->extack,
|
| 168 | "FTM: can't set both trigger based and non trigger based");
|
| 169 | return -EINVAL;
|
| 170 | }
|
| 171 |
|
| 172 | if (out->ftm.request_type == NL80211_PMSR_FTM_REQ_TYPE_PD &&
|
| 173 | out->ftm.non_trigger_based && out->ftm.ftms_per_burst > 4) {
|
| 174 | NL_SET_ERR_MSG_ATTR(info->extack,
|
| 175 | tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST],
|
| 176 | "FTM: FTMs per burst must not exceed 4 for PD NTB ranging");
|
| 177 | return -ERANGE;
|
| 178 | }
|
| 179 |
|
| 180 | if (out->ftm.ftms_per_burst > 31 && !out->ftm.non_trigger_based &&
|
| 181 | !out->ftm.trigger_based) {
|
| 182 | NL_SET_ERR_MSG_ATTR(info->extack,
|
| 183 | tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST],
|
| 184 | "FTM: FTMs per burst must be set lower than 31");
|
| 185 | return -ERANGE;
|
| 186 | }
|
| 187 |
|
| 188 | if ((out->ftm.trigger_based || out->ftm.non_trigger_based) &&
|
| 189 | out->ftm.preamble != NL80211_PREAMBLE_HE) {
|
| 190 | NL_SET_ERR_MSG_ATTR(info->extack,
|
| 191 | tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE],
|
| 192 | "FTM: non EDCA based ranging must use HE preamble");
|
| 193 | return -EINVAL;
|
| 194 | }
|
| 195 |
|
| 196 | if (tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION])
|
| 197 | out->ftm.burst_duration =
|
| 198 | nla_get_u8(tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION]);
|
| 199 | else if (!out->ftm.non_trigger_based && !out->ftm.trigger_based)
|
| 200 | out->ftm.burst_duration = 15;
|
| 201 |
|
| 202 | out->ftm.lmr_feedback =
|
| 203 | !!tb[NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK];
|
| 204 | if (!out->ftm.trigger_based && !out->ftm.non_trigger_based &&
|
| 205 | out->ftm.lmr_feedback) {
|
| 206 | NL_SET_ERR_MSG_ATTR(info->extack,
|
| 207 | tb[NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK],
|
| 208 | "FTM: LMR feedback set for EDCA based ranging");
|
| 209 | return -EINVAL;
|
| 210 | }
|
| 211 |
|
| 212 | if (tb[NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR]) {
|
| 213 | if (!out->ftm.non_trigger_based && !out->ftm.trigger_based) {
|
| 214 | NL_SET_ERR_MSG_ATTR(info->extack,
|
| 215 | tb[NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR],
|
| 216 | "FTM: BSS color set for EDCA based ranging");
|
| 217 | return -EINVAL;
|
| 218 | }
|
| 219 |
|
| 220 | out->ftm.bss_color =
|
| 221 | nla_get_u8(tb[NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR]);
|
| 222 | }
|
| 223 |
|
| 224 | out->ftm.rsta = !!tb[NL80211_PMSR_FTM_REQ_ATTR_RSTA];
|
| 225 | if (out->ftm.rsta && out->ftm.non_trigger_based &&
|
| 226 | !capa->ftm.rsta.support_ntb) {
|
| 227 | NL_SET_ERR_MSG_ATTR(info->extack,
|
| 228 | tb[NL80211_PMSR_FTM_REQ_ATTR_RSTA],
|
| 229 | "FTM: NTB RSTA not supported by device");
|
| 230 | return -EOPNOTSUPP;
|
| 231 | }
|
| 232 |
|
| 233 | if (out->ftm.rsta && out->ftm.trigger_based &&
|
| 234 | !capa->ftm.rsta.support_tb) {
|
| 235 | NL_SET_ERR_MSG_ATTR(info->extack,
|
| 236 | tb[NL80211_PMSR_FTM_REQ_ATTR_RSTA],
|
| 237 | "FTM: TB RSTA not supported by device");
|
| 238 | return -EOPNOTSUPP;
|
| 239 | }
|
| 240 |
|
| 241 | if (out->ftm.rsta && !out->ftm.non_trigger_based &&
|
| 242 | !out->ftm.trigger_based &&
|
| 243 | !capa->ftm.rsta.support_edca) {
|
| 244 | NL_SET_ERR_MSG_ATTR(info->extack,
|
| 245 | tb[NL80211_PMSR_FTM_REQ_ATTR_RSTA],
|
| 246 | "FTM: EDCA RSTA not supported by device");
|
| 247 | return -EOPNOTSUPP;
|
| 248 | }
|
| 249 |
|
| 250 | if (out->ftm.rsta &&
|
| 251 | (out->ftm.non_trigger_based || out->ftm.trigger_based) &&
|
| 252 | !out->ftm.lmr_feedback) {
|
| 253 | NL_SET_ERR_MSG_ATTR(info->extack,
|
| 254 | tb[NL80211_PMSR_FTM_REQ_ATTR_RSTA],
|
| 255 | "FTM: RSTA set without LMR feedback");
|
| 256 | return -EINVAL;
|
| 257 | }
|
| 258 |
|
| 259 | if (out->ftm.non_trigger_based) {
|
| 260 | if (out->ftm.request_type == NL80211_PMSR_FTM_REQ_TYPE_PD &&
|
| 261 | !tb[NL80211_PMSR_FTM_REQ_ATTR_NOMINAL_TIME]) {
|
| 262 | NL_SET_ERR_MSG(info->extack,
|
| 263 | "FTM: nominal time is required for PD NTB ranging");
|
| 264 | return -EINVAL;
|
| 265 | }
|
| 266 | if (tb[NL80211_PMSR_FTM_REQ_ATTR_NOMINAL_TIME])
|
| 267 | out->ftm.nominal_time =
|
| 268 | nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_NOMINAL_TIME]);
|
| 269 |
|
| 270 | if (tb[NL80211_PMSR_FTM_REQ_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS])
|
| 271 | out->ftm.min_time_between_measurements =
|
| 272 | nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS]);
|
| 273 |
|
| 274 | if (tb[NL80211_PMSR_FTM_REQ_ATTR_MAX_TIME_BETWEEN_MEASUREMENTS])
|
| 275 | out->ftm.max_time_between_measurements =
|
| 276 | nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_MAX_TIME_BETWEEN_MEASUREMENTS]);
|
| 277 |
|
| 278 | if (tb[NL80211_PMSR_FTM_REQ_ATTR_AW_DURATION])
|
| 279 | out->ftm.availability_window =
|
| 280 | nla_get_u8(tb[NL80211_PMSR_FTM_REQ_ATTR_AW_DURATION]);
|
| 281 |
|
| 282 | if (tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_MEASUREMENTS])
|
| 283 | out->ftm.num_measurements =
|
| 284 | nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_MEASUREMENTS]);
|
| 285 | }
|
| 286 |
|
| 287 | if (tb[NL80211_PMSR_FTM_REQ_ATTR_INGRESS])
|
| 288 | out->ftm.ingress_distance =
|
| 289 | nla_get_u64(tb[NL80211_PMSR_FTM_REQ_ATTR_INGRESS]);
|
| 290 |
|
| 291 | if (tb[NL80211_PMSR_FTM_REQ_ATTR_EGRESS])
|
| 292 | out->ftm.egress_distance =
|
| 293 | nla_get_u64(tb[NL80211_PMSR_FTM_REQ_ATTR_EGRESS]);
|
| 294 |
|
| 295 | out->ftm.pd_suppress_range_results =
|
| 296 | nla_get_flag(tb[NL80211_PMSR_FTM_REQ_ATTR_PD_SUPPRESS_RESULTS]);
|
| 297 |
|
| 298 | if (out->ftm.request_type != NL80211_PMSR_FTM_REQ_TYPE_PD &&
|
| 299 | out->ftm.pd_suppress_range_results) {
|
| 300 | NL_SET_ERR_MSG_ATTR(info->extack,
|
| 301 | tb[NL80211_PMSR_FTM_REQ_ATTR_PD_SUPPRESS_RESULTS],
|
| 302 | "FTM: suppress range result flag only valid for PD requests");
|
| 303 | return -EINVAL;
|
| 304 | }
|
| 305 |
|
| 306 | return 0;
|
| 307 | }
|
| 308 |
|
| 309 | static int pmsr_parse_peer(struct cfg80211_registered_device *rdev,
|
| 310 | struct nlattr *peer,
|
| 311 | struct cfg80211_pmsr_request_peer *out,
|
| 312 | struct genl_info *info)
|
| 313 | {
|
| 314 | struct nlattr *tb[NL80211_PMSR_PEER_ATTR_MAX + 1];
|
| 315 | struct nlattr *req[NL80211_PMSR_REQ_ATTR_MAX + 1];
|
| 316 | bool have_measurement_type = false;
|
| 317 | struct nlattr *treq;
|
| 318 | int err, rem;
|
| 319 |
|
| 320 |
|
| 321 | nla_parse_nested_deprecated(tb, NL80211_PMSR_PEER_ATTR_MAX, peer,
|
| 322 | NULL, NULL);
|
| 323 |
|
| 324 | if (!tb[NL80211_PMSR_PEER_ATTR_ADDR] ||
|
| 325 | !tb[NL80211_PMSR_PEER_ATTR_CHAN] ||
|
| 326 | !tb[NL80211_PMSR_PEER_ATTR_REQ]) {
|
| 327 | NL_SET_ERR_MSG_ATTR(info->extack, peer,
|
| 328 | "insufficient peer data");
|
| 329 | return -EINVAL;
|
| 330 | }
|
| 331 |
|
| 332 | memcpy(out->addr, nla_data(tb[NL80211_PMSR_PEER_ATTR_ADDR]), ETH_ALEN);
|
| 333 |
|
| 334 | if (tb[NL80211_PMSR_PEER_ATTR_REQ_TYPE])
|
| 335 | out->ftm.request_type =
|
| 336 | nla_get_u32(tb[NL80211_PMSR_PEER_ATTR_REQ_TYPE]);
|
| 337 | else
|
| 338 | out->ftm.request_type = NL80211_PMSR_FTM_REQ_TYPE_INFRA;
|
| 339 |
|
| 340 | if (out->ftm.request_type == NL80211_PMSR_FTM_REQ_TYPE_PD &&
|
| 341 | !rdev->wiphy.pmsr_capa->ftm.type.pd_support) {
|
| 342 | NL_SET_ERR_MSG_ATTR(info->extack,
|
| 343 | tb[NL80211_PMSR_PEER_ATTR_REQ_TYPE],
|
| 344 | "FTM: PD request type not supported by device");
|
| 345 | return -EINVAL;
|
| 346 | }
|
| 347 |
|
| 348 | memset(info->attrs, 0, sizeof(*info->attrs) * (NL80211_ATTR_MAX + 1));
|
| 349 | err = nla_parse_nested_deprecated(info->attrs, NL80211_ATTR_MAX,
|
| 350 | tb[NL80211_PMSR_PEER_ATTR_CHAN],
|
| 351 | NULL, info->extack);
|
| 352 | if (err)
|
| 353 | return err;
|
| 354 |
|
| 355 | err = nl80211_parse_chandef(rdev, info->extack, info->attrs,
|
| 356 | &out->chandef, false);
|
| 357 | if (err)
|
| 358 | return err;
|
| 359 |
|
| 360 |
|
| 361 | nla_parse_nested_deprecated(req, NL80211_PMSR_REQ_ATTR_MAX,
|
| 362 | tb[NL80211_PMSR_PEER_ATTR_REQ], NULL,
|
| 363 | NULL);
|
| 364 |
|
| 365 | if (!req[NL80211_PMSR_REQ_ATTR_DATA]) {
|
| 366 | NL_SET_ERR_MSG_ATTR(info->extack,
|
| 367 | tb[NL80211_PMSR_PEER_ATTR_REQ],
|
| 368 | "missing request type/data");
|
| 369 | return -EINVAL;
|
| 370 | }
|
| 371 |
|
| 372 | if (req[NL80211_PMSR_REQ_ATTR_GET_AP_TSF])
|
| 373 | out->report_ap_tsf = true;
|
| 374 |
|
| 375 | if (out->report_ap_tsf && !rdev->wiphy.pmsr_capa->report_ap_tsf) {
|
| 376 | NL_SET_ERR_MSG_ATTR(info->extack,
|
| 377 | req[NL80211_PMSR_REQ_ATTR_GET_AP_TSF],
|
| 378 | "reporting AP TSF is not supported");
|
| 379 | return -EINVAL;
|
| 380 | }
|
| 381 |
|
| 382 | nla_for_each_nested(treq, req[NL80211_PMSR_REQ_ATTR_DATA], rem) {
|
| 383 | if (have_measurement_type) {
|
| 384 | NL_SET_ERR_MSG_ATTR(info->extack, treq,
|
| 385 | "multiple measurement types in request data");
|
| 386 | return -EINVAL;
|
| 387 | }
|
| 388 |
|
| 389 | have_measurement_type = true;
|
| 390 |
|
| 391 | switch (nla_type(treq)) {
|
| 392 | case NL80211_PMSR_TYPE_FTM:
|
| 393 | err = pmsr_parse_ftm(rdev, treq, out, info);
|
| 394 | break;
|
| 395 | default:
|
| 396 | NL_SET_ERR_MSG_ATTR(info->extack, treq,
|
| 397 | "unsupported measurement type");
|
| 398 | err = -EINVAL;
|
| 399 | }
|
| 400 | if (err)
|
| 401 | return err;
|
| 402 | }
|
| 403 |
|
| 404 | if (!have_measurement_type) {
|
| 405 | NL_SET_ERR_MSG_ATTR(info->extack,
|
| 406 | req[NL80211_PMSR_REQ_ATTR_DATA],
|
| 407 | "missing measurement type in request data");
|
| 408 | return -EINVAL;
|
| 409 | }
|
| 410 |
|
| 411 | return 0;
|
| 412 | }
|
| 413 |
|
| 414 | int nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info)
|
| 415 | {
|
| 416 | struct nlattr *reqattr = info->attrs[NL80211_ATTR_PEER_MEASUREMENTS];
|
| 417 | struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
| 418 | int count, rem, err, idx, peer_count;
|
| 419 | struct wireless_dev *wdev = info->user_ptr[1];
|
| 420 | const struct cfg80211_pmsr_capabilities *capa;
|
| 421 | struct cfg80211_pmsr_request *req;
|
| 422 | struct nlattr *peers, *peer;
|
| 423 | u64 cookie;
|
| 424 |
|
| 425 | capa = rdev->wiphy.pmsr_capa;
|
| 426 |
|
| 427 | if (!capa)
|
| 428 | return -EOPNOTSUPP;
|
| 429 |
|
| 430 | if (!reqattr)
|
| 431 | return -EINVAL;
|
| 432 |
|
| 433 | peers = nla_find(nla_data(reqattr), nla_len(reqattr),
|
| 434 | NL80211_PMSR_ATTR_PEERS);
|
| 435 | if (!peers)
|
| 436 | return -EINVAL;
|
| 437 |
|
| 438 | count = 0;
|
| 439 | nla_for_each_nested(peer, peers, rem) {
|
| 440 | count++;
|
| 441 |
|
| 442 | if (count > capa->max_peers) {
|
| 443 | NL_SET_ERR_MSG_ATTR(info->extack, peer,
|
| 444 | "Too many peers used");
|
| 445 | return -EINVAL;
|
| 446 | }
|
| 447 | }
|
| 448 |
|
| 449 | if (!count) {
|
| 450 | NL_SET_ERR_MSG_ATTR(info->extack, peers, "No peers specified");
|
| 451 | return -EINVAL;
|
| 452 | }
|
| 453 |
|
| 454 | req = kzalloc_flex(*req, peers, count);
|
| 455 | if (!req)
|
| 456 | return -ENOMEM;
|
| 457 | req->n_peers = count;
|
| 458 |
|
| 459 | if (info->attrs[NL80211_ATTR_TIMEOUT])
|
| 460 | req->timeout = nla_get_u32(info->attrs[NL80211_ATTR_TIMEOUT]);
|
| 461 |
|
| 462 | if (info->attrs[NL80211_ATTR_MAC]) {
|
| 463 | if (!capa->randomize_mac_addr) {
|
| 464 | NL_SET_ERR_MSG_ATTR(info->extack,
|
| 465 | info->attrs[NL80211_ATTR_MAC],
|
| 466 | "device cannot randomize MAC address");
|
| 467 | err = -EINVAL;
|
| 468 | goto out_err;
|
| 469 | }
|
| 470 |
|
| 471 | err = nl80211_parse_random_mac(info->attrs, req->mac_addr,
|
| 472 | req->mac_addr_mask);
|
| 473 | if (err)
|
| 474 | goto out_err;
|
| 475 | } else {
|
| 476 | memcpy(req->mac_addr, wdev_address(wdev), ETH_ALEN);
|
| 477 | eth_broadcast_addr(req->mac_addr_mask);
|
| 478 | }
|
| 479 |
|
| 480 | idx = 0;
|
| 481 | nla_for_each_nested(peer, peers, rem) {
|
| 482 |
|
| 483 | err = pmsr_parse_peer(rdev, peer, &req->peers[idx], info);
|
| 484 | if (err)
|
| 485 | goto out_err;
|
| 486 | idx++;
|
| 487 | }
|
| 488 |
|
| 489 |
|
| 490 | if (capa->ftm.ista.max_peers) {
|
| 491 | peer_count = 0;
|
| 492 |
|
| 493 | for (idx = 0; idx < req->n_peers; idx++) {
|
| 494 | if (!req->peers[idx].ftm.rsta) {
|
| 495 | peer_count++;
|
| 496 |
|
| 497 | if (peer_count > capa->ftm.ista.max_peers) {
|
| 498 | NL_SET_ERR_MSG(info->extack,
|
| 499 | "Too many ISTA peers for device limit");
|
| 500 | err = -EINVAL;
|
| 501 | goto out_err;
|
| 502 | }
|
| 503 | }
|
| 504 | }
|
| 505 | }
|
| 506 |
|
| 507 | if (capa->ftm.rsta.max_peers) {
|
| 508 | peer_count = 0;
|
| 509 |
|
| 510 | for (idx = 0; idx < req->n_peers; idx++) {
|
| 511 | if (req->peers[idx].ftm.rsta) {
|
| 512 | peer_count++;
|
| 513 |
|
| 514 | if (peer_count > capa->ftm.rsta.max_peers) {
|
| 515 | NL_SET_ERR_MSG(info->extack,
|
| 516 | "Too many RSTA peers for device limit");
|
| 517 | err = -EINVAL;
|
| 518 | goto out_err;
|
| 519 | }
|
| 520 | }
|
| 521 | }
|
| 522 | }
|
| 523 | req->cookie = cfg80211_assign_cookie(rdev);
|
| 524 | req->nl_portid = info->snd_portid;
|
| 525 | cookie = req->cookie;
|
| 526 |
|
| 527 | |
| 528 | * Add to the list before the driver call; under races or broken
|
| 529 | * drivers, completion may free the request before rdev_start_pmsr()
|
| 530 | * returns. Use the saved cookie below.
|
| 531 |
|
| 532 | spin_lock_bh(&wdev->pmsr_lock);
|
| 533 | list_add_tail(&req->list, &wdev->pmsr_list);
|
| 534 | spin_unlock_bh(&wdev->pmsr_lock);
|
| 535 |
|
| 536 | err = rdev_start_pmsr(rdev, wdev, req);
|
| 537 | if (err) {
|
| 538 |
|
| 539 | spin_lock_bh(&wdev->pmsr_lock);
|
| 540 | list_del(&req->list);
|
| 541 | spin_unlock_bh(&wdev->pmsr_lock);
|
| 542 | goto out_err;
|
| 543 | }
|
| 544 |
|
| 545 | nl_set_extack_cookie_u64(info->extack, cookie);
|
| 546 | return 0;
|
| 547 | out_err:
|
| 548 | kfree(req);
|
| 549 | return err;
|
| 550 | }
|
| 551 |
|
| 552 | void cfg80211_pmsr_complete(struct wireless_dev *wdev,
|
| 553 | struct cfg80211_pmsr_request *req,
|
| 554 | gfp_t gfp)
|
| 555 | {
|
| 556 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
| 557 | struct cfg80211_pmsr_request *tmp, *prev, *to_free = NULL;
|
| 558 | struct sk_buff *msg;
|
| 559 | void *hdr;
|
| 560 |
|
| 561 | trace_cfg80211_pmsr_complete(wdev->wiphy, wdev, req->cookie);
|
| 562 |
|
| 563 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
| 564 | if (!msg)
|
| 565 | goto free_request;
|
| 566 |
|
| 567 | hdr = nl80211hdr_put(msg, 0, 0, 0,
|
| 568 | NL80211_CMD_PEER_MEASUREMENT_COMPLETE);
|
| 569 | if (!hdr)
|
| 570 | goto free_msg;
|
| 571 |
|
| 572 | if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
|
| 573 | nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
|
| 574 | NL80211_ATTR_PAD))
|
| 575 | goto free_msg;
|
| 576 |
|
| 577 | if (nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, req->cookie,
|
| 578 | NL80211_ATTR_PAD))
|
| 579 | goto free_msg;
|
| 580 |
|
| 581 | genlmsg_end(msg, hdr);
|
| 582 | genlmsg_unicast(wiphy_net(wdev->wiphy), msg, req->nl_portid);
|
| 583 | goto free_request;
|
| 584 | free_msg:
|
| 585 | nlmsg_free(msg);
|
| 586 | free_request:
|
| 587 | spin_lock_bh(&wdev->pmsr_lock);
|
| 588 | |
| 589 | * cfg80211_pmsr_process_abort() may have already moved this request
|
| 590 | * to the free list, and will free it later. In this case, don't free
|
| 591 | * it here.
|
| 592 |
|
| 593 | list_for_each_entry_safe(tmp, prev, &wdev->pmsr_list, list) {
|
| 594 | if (tmp == req) {
|
| 595 | list_del(&req->list);
|
| 596 | to_free = req;
|
| 597 | break;
|
| 598 | }
|
| 599 | }
|
| 600 | spin_unlock_bh(&wdev->pmsr_lock);
|
| 601 | kfree(to_free);
|
| 602 | }
|
| 603 | EXPORT_SYMBOL_GPL(cfg80211_pmsr_complete);
|
| 604 |
|
| 605 | static int nl80211_pmsr_send_ftm_res(struct sk_buff *msg,
|
| 606 | struct cfg80211_pmsr_result *res)
|
| 607 | {
|
| 608 | if (res->status == NL80211_PMSR_STATUS_FAILURE) {
|
| 609 | if (nla_put_u32(msg, NL80211_PMSR_FTM_RESP_ATTR_FAIL_REASON,
|
| 610 | res->ftm.failure_reason))
|
| 611 | goto error;
|
| 612 |
|
| 613 | if (res->ftm.failure_reason ==
|
| 614 | NL80211_PMSR_FTM_FAILURE_PEER_BUSY &&
|
| 615 | res->ftm.busy_retry_time &&
|
| 616 | nla_put_u32(msg, NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME,
|
| 617 | res->ftm.busy_retry_time))
|
| 618 | goto error;
|
| 619 |
|
| 620 | return 0;
|
| 621 | }
|
| 622 |
|
| 623 | #define PUT(tp, attr, val) \
|
| 624 | do { \
|
| 625 | if (nla_put_##tp(msg, \
|
| 626 | NL80211_PMSR_FTM_RESP_ATTR_##attr, \
|
| 627 | res->ftm.val)) \
|
| 628 | goto error; \
|
| 629 | } while (0)
|
| 630 |
|
| 631 | #define PUTOPT(tp, attr, val) \
|
| 632 | do { \
|
| 633 | if (res->ftm.val##_valid) \
|
| 634 | PUT(tp, attr, val); \
|
| 635 | } while (0)
|
| 636 |
|
| 637 | #define PUT_U64(attr, val) \
|
| 638 | do { \
|
| 639 | if (nla_put_u64_64bit(msg, \
|
| 640 | NL80211_PMSR_FTM_RESP_ATTR_##attr,\
|
| 641 | res->ftm.val, \
|
| 642 | NL80211_PMSR_FTM_RESP_ATTR_PAD)) \
|
| 643 | goto error; \
|
| 644 | } while (0)
|
| 645 |
|
| 646 | #define PUTOPT_U64(attr, val) \
|
| 647 | do { \
|
| 648 | if (res->ftm.val##_valid) \
|
| 649 | PUT_U64(attr, val); \
|
| 650 | } while (0)
|
| 651 |
|
| 652 | if (res->ftm.burst_index >= 0)
|
| 653 | PUT(u32, BURST_INDEX, burst_index);
|
| 654 | PUTOPT(u32, NUM_FTMR_ATTEMPTS, num_ftmr_attempts);
|
| 655 | PUTOPT(u32, NUM_FTMR_SUCCESSES, num_ftmr_successes);
|
| 656 | PUT(u8, NUM_BURSTS_EXP, num_bursts_exp);
|
| 657 | PUT(u8, BURST_DURATION, burst_duration);
|
| 658 | PUT(u8, FTMS_PER_BURST, ftms_per_burst);
|
| 659 | PUT(u16, BURST_PERIOD, burst_period);
|
| 660 | PUTOPT(s32, RSSI_AVG, rssi_avg);
|
| 661 | PUTOPT(s32, RSSI_SPREAD, rssi_spread);
|
| 662 | if (res->ftm.tx_rate_valid &&
|
| 663 | !nl80211_put_sta_rate(msg, &res->ftm.tx_rate,
|
| 664 | NL80211_PMSR_FTM_RESP_ATTR_TX_RATE))
|
| 665 | goto error;
|
| 666 | if (res->ftm.rx_rate_valid &&
|
| 667 | !nl80211_put_sta_rate(msg, &res->ftm.rx_rate,
|
| 668 | NL80211_PMSR_FTM_RESP_ATTR_RX_RATE))
|
| 669 | goto error;
|
| 670 | PUTOPT_U64(RTT_AVG, rtt_avg);
|
| 671 | PUTOPT_U64(RTT_VARIANCE, rtt_variance);
|
| 672 | PUTOPT_U64(RTT_SPREAD, rtt_spread);
|
| 673 | PUTOPT_U64(DIST_AVG, dist_avg);
|
| 674 | PUTOPT_U64(DIST_VARIANCE, dist_variance);
|
| 675 | PUTOPT_U64(DIST_SPREAD, dist_spread);
|
| 676 | PUTOPT(u32, TX_LTF_REPETITION_COUNT, tx_ltf_repetition_count);
|
| 677 | PUTOPT(u32, RX_LTF_REPETITION_COUNT, rx_ltf_repetition_count);
|
| 678 | PUTOPT(u32, MAX_TIME_BETWEEN_MEASUREMENTS,
|
| 679 | max_time_between_measurements);
|
| 680 | PUTOPT(u32, MIN_TIME_BETWEEN_MEASUREMENTS,
|
| 681 | min_time_between_measurements);
|
| 682 | PUTOPT(u8, NUM_TX_SPATIAL_STREAMS, num_tx_spatial_streams);
|
| 683 | PUTOPT(u8, NUM_RX_SPATIAL_STREAMS, num_rx_spatial_streams);
|
| 684 | PUTOPT(u32, NOMINAL_TIME, nominal_time);
|
| 685 | PUTOPT(u8, AVAILABILITY_WINDOW, availability_window);
|
| 686 | PUTOPT(u32, CHANNEL_WIDTH, chan_width);
|
| 687 | PUTOPT(u32, PREAMBLE, preamble);
|
| 688 | if (res->ftm.is_delayed_lmr &&
|
| 689 | nla_put_flag(msg, NL80211_PMSR_FTM_RESP_ATTR_IS_DELAYED_LMR))
|
| 690 | goto error;
|
| 691 | if (res->ftm.lci && res->ftm.lci_len &&
|
| 692 | nla_put(msg, NL80211_PMSR_FTM_RESP_ATTR_LCI,
|
| 693 | res->ftm.lci_len, res->ftm.lci))
|
| 694 | goto error;
|
| 695 | if (res->ftm.civicloc && res->ftm.civicloc_len &&
|
| 696 | nla_put(msg, NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC,
|
| 697 | res->ftm.civicloc_len, res->ftm.civicloc))
|
| 698 | goto error;
|
| 699 | #undef PUT
|
| 700 | #undef PUTOPT
|
| 701 | #undef PUT_U64
|
| 702 | #undef PUTOPT_U64
|
| 703 |
|
| 704 | return 0;
|
| 705 | error:
|
| 706 | return -ENOSPC;
|
| 707 | }
|
| 708 |
|
| 709 | static int nl80211_pmsr_send_result(struct sk_buff *msg,
|
| 710 | struct cfg80211_pmsr_result *res)
|
| 711 | {
|
| 712 | struct nlattr *pmsr, *peers, *peer, *resp, *data, *typedata;
|
| 713 |
|
| 714 | pmsr = nla_nest_start_noflag(msg, NL80211_ATTR_PEER_MEASUREMENTS);
|
| 715 | if (!pmsr)
|
| 716 | goto error;
|
| 717 |
|
| 718 | peers = nla_nest_start_noflag(msg, NL80211_PMSR_ATTR_PEERS);
|
| 719 | if (!peers)
|
| 720 | goto error;
|
| 721 |
|
| 722 | peer = nla_nest_start_noflag(msg, 1);
|
| 723 | if (!peer)
|
| 724 | goto error;
|
| 725 |
|
| 726 | if (nla_put(msg, NL80211_PMSR_PEER_ATTR_ADDR, ETH_ALEN, res->addr))
|
| 727 | goto error;
|
| 728 |
|
| 729 | resp = nla_nest_start_noflag(msg, NL80211_PMSR_PEER_ATTR_RESP);
|
| 730 | if (!resp)
|
| 731 | goto error;
|
| 732 |
|
| 733 | if (nla_put_u32(msg, NL80211_PMSR_RESP_ATTR_STATUS, res->status) ||
|
| 734 | nla_put_u64_64bit(msg, NL80211_PMSR_RESP_ATTR_HOST_TIME,
|
| 735 | res->host_time, NL80211_PMSR_RESP_ATTR_PAD))
|
| 736 | goto error;
|
| 737 |
|
| 738 | if (res->ap_tsf_valid &&
|
| 739 | nla_put_u64_64bit(msg, NL80211_PMSR_RESP_ATTR_AP_TSF,
|
| 740 | res->ap_tsf, NL80211_PMSR_RESP_ATTR_PAD))
|
| 741 | goto error;
|
| 742 |
|
| 743 | if (res->final && nla_put_flag(msg, NL80211_PMSR_RESP_ATTR_FINAL))
|
| 744 | goto error;
|
| 745 |
|
| 746 | data = nla_nest_start_noflag(msg, NL80211_PMSR_RESP_ATTR_DATA);
|
| 747 | if (!data)
|
| 748 | goto error;
|
| 749 |
|
| 750 | typedata = nla_nest_start_noflag(msg, res->type);
|
| 751 | if (!typedata)
|
| 752 | goto error;
|
| 753 |
|
| 754 | switch (res->type) {
|
| 755 | case NL80211_PMSR_TYPE_FTM:
|
| 756 | if (nl80211_pmsr_send_ftm_res(msg, res))
|
| 757 | goto error;
|
| 758 | break;
|
| 759 | default:
|
| 760 | WARN_ON(1);
|
| 761 | }
|
| 762 |
|
| 763 | nla_nest_end(msg, typedata);
|
| 764 | nla_nest_end(msg, data);
|
| 765 | nla_nest_end(msg, resp);
|
| 766 | nla_nest_end(msg, peer);
|
| 767 | nla_nest_end(msg, peers);
|
| 768 | nla_nest_end(msg, pmsr);
|
| 769 |
|
| 770 | return 0;
|
| 771 | error:
|
| 772 | return -ENOSPC;
|
| 773 | }
|
| 774 |
|
| 775 | void cfg80211_pmsr_report(struct wireless_dev *wdev,
|
| 776 | struct cfg80211_pmsr_request *req,
|
| 777 | struct cfg80211_pmsr_result *result,
|
| 778 | gfp_t gfp)
|
| 779 | {
|
| 780 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
| 781 | struct sk_buff *msg;
|
| 782 | void *hdr;
|
| 783 | int err;
|
| 784 |
|
| 785 | trace_cfg80211_pmsr_report(wdev->wiphy, wdev, req->cookie,
|
| 786 | result->addr);
|
| 787 |
|
| 788 | |
| 789 | * Currently, only variable items are LCI and civic location,
|
| 790 | * both of which are reasonably short so we don't need to
|
| 791 | * worry about them here for the allocation.
|
| 792 |
|
| 793 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
| 794 | if (!msg)
|
| 795 | return;
|
| 796 |
|
| 797 | hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PEER_MEASUREMENT_RESULT);
|
| 798 | if (!hdr)
|
| 799 | goto free;
|
| 800 |
|
| 801 | if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
|
| 802 | nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
|
| 803 | NL80211_ATTR_PAD))
|
| 804 | goto free;
|
| 805 |
|
| 806 | if (nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, req->cookie,
|
| 807 | NL80211_ATTR_PAD))
|
| 808 | goto free;
|
| 809 |
|
| 810 | err = nl80211_pmsr_send_result(msg, result);
|
| 811 | if (err) {
|
| 812 | pr_err_ratelimited("peer measurement result: message didn't fit!");
|
| 813 | goto free;
|
| 814 | }
|
| 815 |
|
| 816 | genlmsg_end(msg, hdr);
|
| 817 | genlmsg_unicast(wiphy_net(wdev->wiphy), msg, req->nl_portid);
|
| 818 | return;
|
| 819 | free:
|
| 820 | nlmsg_free(msg);
|
| 821 | }
|
| 822 | EXPORT_SYMBOL_GPL(cfg80211_pmsr_report);
|
| 823 |
|
| 824 | static void cfg80211_pmsr_process_abort(struct wireless_dev *wdev)
|
| 825 | {
|
| 826 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
| 827 | struct cfg80211_pmsr_request *req, *tmp;
|
| 828 | LIST_HEAD(free_list);
|
| 829 |
|
| 830 | lockdep_assert_wiphy(wdev->wiphy);
|
| 831 |
|
| 832 | spin_lock_bh(&wdev->pmsr_lock);
|
| 833 | list_for_each_entry_safe(req, tmp, &wdev->pmsr_list, list) {
|
| 834 | if (req->nl_portid)
|
| 835 | continue;
|
| 836 | list_move_tail(&req->list, &free_list);
|
| 837 | }
|
| 838 | spin_unlock_bh(&wdev->pmsr_lock);
|
| 839 |
|
| 840 | list_for_each_entry_safe(req, tmp, &free_list, list) {
|
| 841 | rdev_abort_pmsr(rdev, wdev, req);
|
| 842 |
|
| 843 | kfree(req);
|
| 844 | }
|
| 845 | }
|
| 846 |
|
| 847 | void cfg80211_pmsr_free_wk(struct wiphy *wiphy, struct wiphy_work *work)
|
| 848 | {
|
| 849 | struct wireless_dev *wdev = container_of(work, struct wireless_dev,
|
| 850 | pmsr_free_wk);
|
| 851 |
|
| 852 | cfg80211_pmsr_process_abort(wdev);
|
| 853 | }
|
| 854 |
|
| 855 | void cfg80211_pmsr_wdev_down(struct wireless_dev *wdev)
|
| 856 | {
|
| 857 | struct cfg80211_pmsr_request *req;
|
| 858 | bool found = false;
|
| 859 |
|
| 860 | spin_lock_bh(&wdev->pmsr_lock);
|
| 861 | list_for_each_entry(req, &wdev->pmsr_list, list) {
|
| 862 | found = true;
|
| 863 | req->nl_portid = 0;
|
| 864 | }
|
| 865 | spin_unlock_bh(&wdev->pmsr_lock);
|
| 866 |
|
| 867 | wiphy_work_cancel(wdev->wiphy, &wdev->pmsr_free_wk);
|
| 868 | if (found)
|
| 869 | cfg80211_pmsr_process_abort(wdev);
|
| 870 |
|
| 871 | WARN_ON(!list_empty(&wdev->pmsr_list));
|
| 872 | }
|
| 873 |
|
| 874 | void cfg80211_release_pmsr(struct wireless_dev *wdev, u32 portid)
|
| 875 | {
|
| 876 | struct cfg80211_pmsr_request *req;
|
| 877 |
|
| 878 | spin_lock_bh(&wdev->pmsr_lock);
|
| 879 | list_for_each_entry(req, &wdev->pmsr_list, list) {
|
| 880 | if (req->nl_portid == portid) {
|
| 881 | req->nl_portid = 0;
|
| 882 | wiphy_work_queue(wdev->wiphy, &wdev->pmsr_free_wk);
|
| 883 | }
|
| 884 | }
|
| 885 | spin_unlock_bh(&wdev->pmsr_lock);
|
| 886 | }
|