|
@@ -3409,6 +3409,63 @@ static struct net_device *get_vlan(struct genl_info *info,
|
|
|
return ERR_PTR(ret);
|
|
|
}
|
|
|
|
|
|
+static struct nla_policy
|
|
|
+nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] __read_mostly = {
|
|
|
+ [NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 },
|
|
|
+ [NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 },
|
|
|
+};
|
|
|
+
|
|
|
+static int nl80211_set_station_tdls(struct genl_info *info,
|
|
|
+ struct station_parameters *params)
|
|
|
+{
|
|
|
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
+ struct nlattr *tb[NL80211_STA_WME_MAX + 1];
|
|
|
+ struct nlattr *nla;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ /* Can only set if TDLS ... */
|
|
|
+ if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS))
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+
|
|
|
+ /* ... with external setup is supported */
|
|
|
+ if (!(rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP))
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+
|
|
|
+ /* Dummy STA entry gets updated once the peer capabilities are known */
|
|
|
+ if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
|
|
|
+ params->ht_capa =
|
|
|
+ nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
|
|
|
+ if (info->attrs[NL80211_ATTR_VHT_CAPABILITY])
|
|
|
+ params->vht_capa =
|
|
|
+ nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
|
|
|
+
|
|
|
+ /* parse WME attributes if present */
|
|
|
+ if (!info->attrs[NL80211_ATTR_STA_WME])
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ nla = info->attrs[NL80211_ATTR_STA_WME];
|
|
|
+ err = nla_parse_nested(tb, NL80211_STA_WME_MAX, nla,
|
|
|
+ nl80211_sta_wme_policy);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ if (tb[NL80211_STA_WME_UAPSD_QUEUES])
|
|
|
+ params->uapsd_queues = nla_get_u8(
|
|
|
+ tb[NL80211_STA_WME_UAPSD_QUEUES]);
|
|
|
+ if (params->uapsd_queues & ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (tb[NL80211_STA_WME_MAX_SP])
|
|
|
+ params->max_sp = nla_get_u8(tb[NL80211_STA_WME_MAX_SP]);
|
|
|
+
|
|
|
+ if (params->max_sp & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ params->sta_modify_mask |= STATION_PARAM_APPLY_UAPSD;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
|
|
|
{
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
@@ -3450,8 +3507,7 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
|
|
|
nla_len(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]);
|
|
|
}
|
|
|
|
|
|
- if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL] ||
|
|
|
- info->attrs[NL80211_ATTR_HT_CAPABILITY])
|
|
|
+ if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
|
|
|
return -EINVAL;
|
|
|
|
|
|
if (!rdev->ops->change_station)
|
|
@@ -3524,6 +3580,9 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
|
|
|
return -EINVAL;
|
|
|
if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY])
|
|
|
return -EINVAL;
|
|
|
+ if (info->attrs[NL80211_ATTR_HT_CAPABILITY] ||
|
|
|
+ info->attrs[NL80211_ATTR_VHT_CAPABILITY])
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
/* must be last in here for error handling */
|
|
|
params.vlan = get_vlan(info, rdev);
|
|
@@ -3539,13 +3598,29 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
|
|
|
* to change the flag.
|
|
|
*/
|
|
|
params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
|
|
|
- /* fall through */
|
|
|
+ /* Include parameters for TDLS peer (driver will check) */
|
|
|
+ err = nl80211_set_station_tdls(info, ¶ms);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+ /* disallow things sta doesn't support */
|
|
|
+ if (params.plink_action)
|
|
|
+ return -EINVAL;
|
|
|
+ if (params.local_pm)
|
|
|
+ return -EINVAL;
|
|
|
+ /* reject any changes other than AUTHORIZED or WME (for TDLS) */
|
|
|
+ if (params.sta_flags_mask & ~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
|
|
|
+ BIT(NL80211_STA_FLAG_WME)))
|
|
|
+ return -EINVAL;
|
|
|
+ break;
|
|
|
case NL80211_IFTYPE_ADHOC:
|
|
|
/* disallow things sta doesn't support */
|
|
|
if (params.plink_action)
|
|
|
return -EINVAL;
|
|
|
if (params.local_pm)
|
|
|
return -EINVAL;
|
|
|
+ if (info->attrs[NL80211_ATTR_HT_CAPABILITY] ||
|
|
|
+ info->attrs[NL80211_ATTR_VHT_CAPABILITY])
|
|
|
+ return -EINVAL;
|
|
|
/* reject any changes other than AUTHORIZED */
|
|
|
if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED))
|
|
|
return -EINVAL;
|
|
@@ -3560,6 +3635,9 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
|
|
|
return -EINVAL;
|
|
|
if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY])
|
|
|
return -EINVAL;
|
|
|
+ if (info->attrs[NL80211_ATTR_HT_CAPABILITY] ||
|
|
|
+ info->attrs[NL80211_ATTR_VHT_CAPABILITY])
|
|
|
+ return -EINVAL;
|
|
|
/*
|
|
|
* No special handling for TDLS here -- the userspace
|
|
|
* mesh code doesn't have this bug.
|
|
@@ -3584,12 +3662,6 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
-static struct nla_policy
|
|
|
-nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] __read_mostly = {
|
|
|
- [NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 },
|
|
|
- [NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 },
|
|
|
-};
|
|
|
-
|
|
|
static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
|
|
|
{
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|