|
@@ -405,6 +405,10 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
|
|
|
[NL80211_ATTR_PBSS] = { .type = NLA_FLAG },
|
|
|
[NL80211_ATTR_BSS_SELECT] = { .type = NLA_NESTED },
|
|
|
[NL80211_ATTR_STA_SUPPORT_P2P_PS] = { .type = NLA_U8 },
|
|
|
+ [NL80211_ATTR_MU_MIMO_GROUP_DATA] = {
|
|
|
+ .len = VHT_MUMIMO_GROUPS_DATA_LEN
|
|
|
+ },
|
|
|
+ [NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR] = { .len = ETH_ALEN },
|
|
|
};
|
|
|
|
|
|
/* policy for the key attributes */
|
|
@@ -2695,6 +2699,38 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
|
|
|
change = true;
|
|
|
}
|
|
|
|
|
|
+ if (info->attrs[NL80211_ATTR_MU_MIMO_GROUP_DATA]) {
|
|
|
+ const u8 *mumimo_groups;
|
|
|
+ u32 cap_flag = NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER;
|
|
|
+
|
|
|
+ if (!wiphy_ext_feature_isset(&rdev->wiphy, cap_flag))
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+
|
|
|
+ mumimo_groups =
|
|
|
+ nla_data(info->attrs[NL80211_ATTR_MU_MIMO_GROUP_DATA]);
|
|
|
+
|
|
|
+ /* bits 0 and 63 are reserved and must be zero */
|
|
|
+ if ((mumimo_groups[0] & BIT(7)) ||
|
|
|
+ (mumimo_groups[VHT_MUMIMO_GROUPS_DATA_LEN - 1] & BIT(0)))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ memcpy(params.vht_mumimo_groups, mumimo_groups,
|
|
|
+ VHT_MUMIMO_GROUPS_DATA_LEN);
|
|
|
+ change = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (info->attrs[NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR]) {
|
|
|
+ u32 cap_flag = NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER;
|
|
|
+
|
|
|
+ if (!wiphy_ext_feature_isset(&rdev->wiphy, cap_flag))
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+
|
|
|
+ nla_memcpy(params.macaddr,
|
|
|
+ info->attrs[NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR],
|
|
|
+ ETH_ALEN);
|
|
|
+ change = true;
|
|
|
+ }
|
|
|
+
|
|
|
if (flags && (*flags & MONITOR_FLAG_ACTIVE) &&
|
|
|
!(rdev->wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR))
|
|
|
return -EOPNOTSUPP;
|
|
@@ -4410,6 +4446,12 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
|
|
|
nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]);
|
|
|
if (params.plink_state >= NUM_NL80211_PLINK_STATES)
|
|
|
return -EINVAL;
|
|
|
+ if (info->attrs[NL80211_ATTR_MESH_PEER_AID]) {
|
|
|
+ params.peer_aid = nla_get_u16(
|
|
|
+ info->attrs[NL80211_ATTR_MESH_PEER_AID]);
|
|
|
+ if (params.peer_aid > IEEE80211_MAX_AID)
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
params.sta_modify_mask |= STATION_PARAM_APPLY_PLINK_STATE;
|
|
|
}
|
|
|
|
|
@@ -5287,6 +5329,51 @@ static const struct nla_policy
|
|
|
[NL80211_MESH_SETUP_USERSPACE_AMPE] = { .type = NLA_FLAG },
|
|
|
};
|
|
|
|
|
|
+static int nl80211_check_bool(const struct nlattr *nla, u8 min, u8 max, bool *out)
|
|
|
+{
|
|
|
+ u8 val = nla_get_u8(nla);
|
|
|
+ if (val < min || val > max)
|
|
|
+ return -EINVAL;
|
|
|
+ *out = val;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int nl80211_check_u8(const struct nlattr *nla, u8 min, u8 max, u8 *out)
|
|
|
+{
|
|
|
+ u8 val = nla_get_u8(nla);
|
|
|
+ if (val < min || val > max)
|
|
|
+ return -EINVAL;
|
|
|
+ *out = val;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int nl80211_check_u16(const struct nlattr *nla, u16 min, u16 max, u16 *out)
|
|
|
+{
|
|
|
+ u16 val = nla_get_u16(nla);
|
|
|
+ if (val < min || val > max)
|
|
|
+ return -EINVAL;
|
|
|
+ *out = val;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int nl80211_check_u32(const struct nlattr *nla, u32 min, u32 max, u32 *out)
|
|
|
+{
|
|
|
+ u32 val = nla_get_u32(nla);
|
|
|
+ if (val < min || val > max)
|
|
|
+ return -EINVAL;
|
|
|
+ *out = val;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int nl80211_check_s32(const struct nlattr *nla, s32 min, s32 max, s32 *out)
|
|
|
+{
|
|
|
+ s32 val = nla_get_s32(nla);
|
|
|
+ if (val < min || val > max)
|
|
|
+ return -EINVAL;
|
|
|
+ *out = val;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int nl80211_parse_mesh_config(struct genl_info *info,
|
|
|
struct mesh_config *cfg,
|
|
|
u32 *mask_out)
|
|
@@ -5297,9 +5384,8 @@ static int nl80211_parse_mesh_config(struct genl_info *info,
|
|
|
#define FILL_IN_MESH_PARAM_IF_SET(tb, cfg, param, min, max, mask, attr, fn) \
|
|
|
do { \
|
|
|
if (tb[attr]) { \
|
|
|
- if (fn(tb[attr]) < min || fn(tb[attr]) > max) \
|
|
|
+ if (fn(tb[attr], min, max, &cfg->param)) \
|
|
|
return -EINVAL; \
|
|
|
- cfg->param = fn(tb[attr]); \
|
|
|
mask |= (1 << (attr - 1)); \
|
|
|
} \
|
|
|
} while (0)
|
|
@@ -5318,99 +5404,99 @@ do { \
|
|
|
/* Fill in the params struct */
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout, 1, 255,
|
|
|
mask, NL80211_MESHCONF_RETRY_TIMEOUT,
|
|
|
- nla_get_u16);
|
|
|
+ nl80211_check_u16);
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout, 1, 255,
|
|
|
mask, NL80211_MESHCONF_CONFIRM_TIMEOUT,
|
|
|
- nla_get_u16);
|
|
|
+ nl80211_check_u16);
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout, 1, 255,
|
|
|
mask, NL80211_MESHCONF_HOLDING_TIMEOUT,
|
|
|
- nla_get_u16);
|
|
|
+ nl80211_check_u16);
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks, 0, 255,
|
|
|
mask, NL80211_MESHCONF_MAX_PEER_LINKS,
|
|
|
- nla_get_u16);
|
|
|
+ nl80211_check_u16);
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries, 0, 16,
|
|
|
mask, NL80211_MESHCONF_MAX_RETRIES,
|
|
|
- nla_get_u8);
|
|
|
+ nl80211_check_u8);
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL, 1, 255,
|
|
|
- mask, NL80211_MESHCONF_TTL, nla_get_u8);
|
|
|
+ mask, NL80211_MESHCONF_TTL, nl80211_check_u8);
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, element_ttl, 1, 255,
|
|
|
mask, NL80211_MESHCONF_ELEMENT_TTL,
|
|
|
- nla_get_u8);
|
|
|
+ nl80211_check_u8);
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks, 0, 1,
|
|
|
mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
|
|
|
- nla_get_u8);
|
|
|
+ nl80211_check_bool);
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshNbrOffsetMaxNeighbor,
|
|
|
1, 255, mask,
|
|
|
NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR,
|
|
|
- nla_get_u32);
|
|
|
+ nl80211_check_u32);
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries, 0, 255,
|
|
|
mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
|
|
|
- nla_get_u8);
|
|
|
+ nl80211_check_u8);
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time, 1, 65535,
|
|
|
mask, NL80211_MESHCONF_PATH_REFRESH_TIME,
|
|
|
- nla_get_u32);
|
|
|
+ nl80211_check_u32);
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout, 1, 65535,
|
|
|
mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
|
|
|
- nla_get_u16);
|
|
|
+ nl80211_check_u16);
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout,
|
|
|
1, 65535, mask,
|
|
|
NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
|
|
|
- nla_get_u32);
|
|
|
+ nl80211_check_u32);
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval,
|
|
|
1, 65535, mask,
|
|
|
NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
|
|
|
- nla_get_u16);
|
|
|
+ nl80211_check_u16);
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPperrMinInterval,
|
|
|
1, 65535, mask,
|
|
|
NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL,
|
|
|
- nla_get_u16);
|
|
|
+ nl80211_check_u16);
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
|
|
|
dot11MeshHWMPnetDiameterTraversalTime,
|
|
|
1, 65535, mask,
|
|
|
NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
|
|
|
- nla_get_u16);
|
|
|
+ nl80211_check_u16);
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRootMode, 0, 4,
|
|
|
mask, NL80211_MESHCONF_HWMP_ROOTMODE,
|
|
|
- nla_get_u8);
|
|
|
+ nl80211_check_u8);
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRannInterval, 1, 65535,
|
|
|
mask, NL80211_MESHCONF_HWMP_RANN_INTERVAL,
|
|
|
- nla_get_u16);
|
|
|
+ nl80211_check_u16);
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
|
|
|
dot11MeshGateAnnouncementProtocol, 0, 1,
|
|
|
mask, NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
|
|
|
- nla_get_u8);
|
|
|
+ nl80211_check_bool);
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshForwarding, 0, 1,
|
|
|
mask, NL80211_MESHCONF_FORWARDING,
|
|
|
- nla_get_u8);
|
|
|
+ nl80211_check_bool);
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, rssi_threshold, -255, 0,
|
|
|
mask, NL80211_MESHCONF_RSSI_THRESHOLD,
|
|
|
- nla_get_s32);
|
|
|
+ nl80211_check_s32);
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, ht_opmode, 0, 16,
|
|
|
mask, NL80211_MESHCONF_HT_OPMODE,
|
|
|
- nla_get_u16);
|
|
|
+ nl80211_check_u16);
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathToRootTimeout,
|
|
|
1, 65535, mask,
|
|
|
NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT,
|
|
|
- nla_get_u32);
|
|
|
+ nl80211_check_u32);
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMProotInterval, 1, 65535,
|
|
|
mask, NL80211_MESHCONF_HWMP_ROOT_INTERVAL,
|
|
|
- nla_get_u16);
|
|
|
+ nl80211_check_u16);
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
|
|
|
dot11MeshHWMPconfirmationInterval,
|
|
|
1, 65535, mask,
|
|
|
NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
|
|
|
- nla_get_u16);
|
|
|
+ nl80211_check_u16);
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, power_mode,
|
|
|
NL80211_MESH_POWER_ACTIVE,
|
|
|
NL80211_MESH_POWER_MAX,
|
|
|
mask, NL80211_MESHCONF_POWER_MODE,
|
|
|
- nla_get_u32);
|
|
|
+ nl80211_check_u32);
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshAwakeWindowDuration,
|
|
|
0, 65535, mask,
|
|
|
- NL80211_MESHCONF_AWAKE_WINDOW, nla_get_u16);
|
|
|
+ NL80211_MESHCONF_AWAKE_WINDOW, nl80211_check_u16);
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, plink_timeout, 0, 0xffffffff,
|
|
|
mask, NL80211_MESHCONF_PLINK_TIMEOUT,
|
|
|
- nla_get_u32);
|
|
|
+ nl80211_check_u32);
|
|
|
if (mask_out)
|
|
|
*mask_out = mask;
|
|
|
|
|
@@ -6143,6 +6229,19 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ if (info->attrs[NL80211_ATTR_MEASUREMENT_DURATION]) {
|
|
|
+ if (!wiphy_ext_feature_isset(wiphy,
|
|
|
+ NL80211_EXT_FEATURE_SET_SCAN_DWELL)) {
|
|
|
+ err = -EOPNOTSUPP;
|
|
|
+ goto out_free;
|
|
|
+ }
|
|
|
+
|
|
|
+ request->duration =
|
|
|
+ nla_get_u16(info->attrs[NL80211_ATTR_MEASUREMENT_DURATION]);
|
|
|
+ request->duration_mandatory =
|
|
|
+ nla_get_flag(info->attrs[NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY]);
|
|
|
+ }
|
|
|
+
|
|
|
if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) {
|
|
|
request->flags = nla_get_u32(
|
|
|
info->attrs[NL80211_ATTR_SCAN_FLAGS]);
|
|
@@ -6976,6 +7075,13 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
|
|
|
jiffies_to_msecs(jiffies - intbss->ts)))
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
+ if (intbss->parent_tsf &&
|
|
|
+ (nla_put_u64_64bit(msg, NL80211_BSS_PARENT_TSF,
|
|
|
+ intbss->parent_tsf, NL80211_BSS_PAD) ||
|
|
|
+ nla_put(msg, NL80211_BSS_PARENT_BSSID, ETH_ALEN,
|
|
|
+ intbss->parent_bssid)))
|
|
|
+ goto nla_put_failure;
|
|
|
+
|
|
|
if (intbss->ts_boottime &&
|
|
|
nla_put_u64_64bit(msg, NL80211_BSS_LAST_SEEN_BOOTTIME,
|
|
|
intbss->ts_boottime, NL80211_BSS_PAD))
|
|
@@ -11749,6 +11855,13 @@ static int nl80211_add_scan_req(struct sk_buff *msg,
|
|
|
nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, req->flags))
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
+ if (req->info.scan_start_tsf &&
|
|
|
+ (nla_put_u64_64bit(msg, NL80211_ATTR_SCAN_START_TIME_TSF,
|
|
|
+ req->info.scan_start_tsf, NL80211_BSS_PAD) ||
|
|
|
+ nla_put(msg, NL80211_ATTR_SCAN_START_TIME_TSF_BSSID, ETH_ALEN,
|
|
|
+ req->info.tsf_bssid)))
|
|
|
+ goto nla_put_failure;
|
|
|
+
|
|
|
return 0;
|
|
|
nla_put_failure:
|
|
|
return -ENOBUFS;
|