|
@@ -201,6 +201,15 @@ cfg80211_get_dev_from_info(struct net *netns, struct genl_info *info)
|
|
|
}
|
|
|
|
|
|
/* policy for the attributes */
|
|
|
+static const struct nla_policy
|
|
|
+nl80211_ftm_responder_policy[NL80211_FTM_RESP_ATTR_MAX + 1] = {
|
|
|
+ [NL80211_FTM_RESP_ATTR_ENABLED] = { .type = NLA_FLAG, },
|
|
|
+ [NL80211_FTM_RESP_ATTR_LCI] = { .type = NLA_BINARY,
|
|
|
+ .len = U8_MAX },
|
|
|
+ [NL80211_FTM_RESP_ATTR_CIVICLOC] = { .type = NLA_BINARY,
|
|
|
+ .len = U8_MAX },
|
|
|
+};
|
|
|
+
|
|
|
static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
|
|
|
[NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
|
|
|
[NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
|
|
@@ -430,6 +439,11 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
|
|
|
[NL80211_ATTR_TXQ_QUANTUM] = { .type = NLA_U32 },
|
|
|
[NL80211_ATTR_HE_CAPABILITY] = { .type = NLA_BINARY,
|
|
|
.len = NL80211_HE_MAX_CAPABILITY_LEN },
|
|
|
+
|
|
|
+ [NL80211_ATTR_FTM_RESPONDER] = {
|
|
|
+ .type = NLA_NESTED,
|
|
|
+ .validation_data = nl80211_ftm_responder_policy,
|
|
|
+ },
|
|
|
};
|
|
|
|
|
|
/* policy for the key attributes */
|
|
@@ -3989,10 +4003,12 @@ static int validate_beacon_tx_rate(struct cfg80211_registered_device *rdev,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int nl80211_parse_beacon(struct nlattr *attrs[],
|
|
|
+static int nl80211_parse_beacon(struct cfg80211_registered_device *rdev,
|
|
|
+ struct nlattr *attrs[],
|
|
|
struct cfg80211_beacon_data *bcn)
|
|
|
{
|
|
|
bool haveinfo = false;
|
|
|
+ int err;
|
|
|
|
|
|
if (!is_valid_ie_attr(attrs[NL80211_ATTR_BEACON_TAIL]) ||
|
|
|
!is_valid_ie_attr(attrs[NL80211_ATTR_IE]) ||
|
|
@@ -4043,6 +4059,35 @@ static int nl80211_parse_beacon(struct nlattr *attrs[],
|
|
|
bcn->probe_resp_len = nla_len(attrs[NL80211_ATTR_PROBE_RESP]);
|
|
|
}
|
|
|
|
|
|
+ if (attrs[NL80211_ATTR_FTM_RESPONDER]) {
|
|
|
+ struct nlattr *tb[NL80211_FTM_RESP_ATTR_MAX + 1];
|
|
|
+
|
|
|
+ err = nla_parse_nested(tb, NL80211_FTM_RESP_ATTR_MAX,
|
|
|
+ attrs[NL80211_ATTR_FTM_RESPONDER],
|
|
|
+ NULL, NULL);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ if (tb[NL80211_FTM_RESP_ATTR_ENABLED] &&
|
|
|
+ wiphy_ext_feature_isset(&rdev->wiphy,
|
|
|
+ NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER))
|
|
|
+ bcn->ftm_responder = 1;
|
|
|
+ else
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+
|
|
|
+ if (tb[NL80211_FTM_RESP_ATTR_LCI]) {
|
|
|
+ bcn->lci = nla_data(tb[NL80211_FTM_RESP_ATTR_LCI]);
|
|
|
+ bcn->lci_len = nla_len(tb[NL80211_FTM_RESP_ATTR_LCI]);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (tb[NL80211_FTM_RESP_ATTR_CIVICLOC]) {
|
|
|
+ bcn->civicloc = nla_data(tb[NL80211_FTM_RESP_ATTR_CIVICLOC]);
|
|
|
+ bcn->civicloc_len = nla_len(tb[NL80211_FTM_RESP_ATTR_CIVICLOC]);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ bcn->ftm_responder = -1;
|
|
|
+ }
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -4189,7 +4234,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
|
|
|
!info->attrs[NL80211_ATTR_BEACON_HEAD])
|
|
|
return -EINVAL;
|
|
|
|
|
|
- err = nl80211_parse_beacon(info->attrs, ¶ms.beacon);
|
|
|
+ err = nl80211_parse_beacon(rdev, info->attrs, ¶ms.beacon);
|
|
|
if (err)
|
|
|
return err;
|
|
|
|
|
@@ -4373,7 +4418,7 @@ static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info)
|
|
|
if (!wdev->beacon_interval)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- err = nl80211_parse_beacon(info->attrs, ¶ms);
|
|
|
+ err = nl80211_parse_beacon(rdev, info->attrs, ¶ms);
|
|
|
if (err)
|
|
|
return err;
|
|
|
|
|
@@ -7935,7 +7980,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
|
|
|
if (!need_new_beacon)
|
|
|
goto skip_beacons;
|
|
|
|
|
|
- err = nl80211_parse_beacon(info->attrs, ¶ms.beacon_after);
|
|
|
+ err = nl80211_parse_beacon(rdev, info->attrs, ¶ms.beacon_after);
|
|
|
if (err)
|
|
|
return err;
|
|
|
|
|
@@ -7945,7 +7990,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
|
|
|
if (err)
|
|
|
return err;
|
|
|
|
|
|
- err = nl80211_parse_beacon(csa_attrs, ¶ms.beacon_csa);
|
|
|
+ err = nl80211_parse_beacon(rdev, csa_attrs, ¶ms.beacon_csa);
|
|
|
if (err)
|
|
|
return err;
|
|
|
|
|
@@ -12984,6 +13029,76 @@ static int nl80211_tx_control_port(struct sk_buff *skb, struct genl_info *info)
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
+static int nl80211_get_ftm_responder_stats(struct sk_buff *skb,
|
|
|
+ struct genl_info *info)
|
|
|
+{
|
|
|
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
+ struct net_device *dev = info->user_ptr[1];
|
|
|
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
+ struct cfg80211_ftm_responder_stats ftm_stats = {};
|
|
|
+ struct sk_buff *msg;
|
|
|
+ void *hdr;
|
|
|
+ struct nlattr *ftm_stats_attr;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ if (wdev->iftype != NL80211_IFTYPE_AP || !wdev->beacon_interval)
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+
|
|
|
+ err = rdev_get_ftm_responder_stats(rdev, dev, &ftm_stats);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ if (!ftm_stats.filled)
|
|
|
+ return -ENODATA;
|
|
|
+
|
|
|
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
|
+ if (!msg)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
|
|
|
+ NL80211_CMD_GET_FTM_RESPONDER_STATS);
|
|
|
+ if (!hdr)
|
|
|
+ return -ENOBUFS;
|
|
|
+
|
|
|
+ if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex))
|
|
|
+ goto nla_put_failure;
|
|
|
+
|
|
|
+ ftm_stats_attr = nla_nest_start(msg, NL80211_ATTR_FTM_RESPONDER_STATS);
|
|
|
+ if (!ftm_stats_attr)
|
|
|
+ goto nla_put_failure;
|
|
|
+
|
|
|
+#define SET_FTM(field, name, type) \
|
|
|
+ do { if ((ftm_stats.filled & BIT(NL80211_FTM_STATS_ ## name)) && \
|
|
|
+ nla_put_ ## type(msg, NL80211_FTM_STATS_ ## name, \
|
|
|
+ ftm_stats.field)) \
|
|
|
+ goto nla_put_failure; } while (0)
|
|
|
+#define SET_FTM_U64(field, name) \
|
|
|
+ do { if ((ftm_stats.filled & BIT(NL80211_FTM_STATS_ ## name)) && \
|
|
|
+ nla_put_u64_64bit(msg, NL80211_FTM_STATS_ ## name, \
|
|
|
+ ftm_stats.field, NL80211_FTM_STATS_PAD)) \
|
|
|
+ goto nla_put_failure; } while (0)
|
|
|
+
|
|
|
+ SET_FTM(success_num, SUCCESS_NUM, u32);
|
|
|
+ SET_FTM(partial_num, PARTIAL_NUM, u32);
|
|
|
+ SET_FTM(failed_num, FAILED_NUM, u32);
|
|
|
+ SET_FTM(asap_num, ASAP_NUM, u32);
|
|
|
+ SET_FTM(non_asap_num, NON_ASAP_NUM, u32);
|
|
|
+ SET_FTM_U64(total_duration_ms, TOTAL_DURATION_MSEC);
|
|
|
+ SET_FTM(unknown_triggers_num, UNKNOWN_TRIGGERS_NUM, u32);
|
|
|
+ SET_FTM(reschedule_requests_num, RESCHEDULE_REQUESTS_NUM, u32);
|
|
|
+ SET_FTM(out_of_window_triggers_num, OUT_OF_WINDOW_TRIGGERS_NUM, u32);
|
|
|
+#undef SET_FTM
|
|
|
+
|
|
|
+ nla_nest_end(msg, ftm_stats_attr);
|
|
|
+
|
|
|
+ genlmsg_end(msg, hdr);
|
|
|
+ return genlmsg_reply(msg, info);
|
|
|
+
|
|
|
+nla_put_failure:
|
|
|
+ nlmsg_free(msg);
|
|
|
+ return -ENOBUFS;
|
|
|
+}
|
|
|
+
|
|
|
#define NL80211_FLAG_NEED_WIPHY 0x01
|
|
|
#define NL80211_FLAG_NEED_NETDEV 0x02
|
|
|
#define NL80211_FLAG_NEED_RTNL 0x04
|
|
@@ -13895,6 +14010,13 @@ static const struct genl_ops nl80211_ops[] = {
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
|
|
NL80211_FLAG_NEED_RTNL,
|
|
|
},
|
|
|
+ {
|
|
|
+ .cmd = NL80211_CMD_GET_FTM_RESPONDER_STATS,
|
|
|
+ .doit = nl80211_get_ftm_responder_stats,
|
|
|
+ .policy = nl80211_policy,
|
|
|
+ .internal_flags = NL80211_FLAG_NEED_NETDEV |
|
|
|
+ NL80211_FLAG_NEED_RTNL,
|
|
|
+ },
|
|
|
};
|
|
|
|
|
|
static struct genl_family nl80211_fam __ro_after_init = {
|