Commit df881293 authored by Jouni Malinen's avatar Jouni Malinen Committed by Johannes Berg

cfg80211: Pass TDLS peer's QoS/HT/VHT information during set_station

The information of the peer's capabilities is required for the driver
to perform TDLS Peer UAPSD operations. This information of the peer is
passed by the supplicant using NL80211_CMD_SET_STATION command. This
commit enhances the function nl80211_set_station to pass this
information of the peer to the driver in case this command is used
with the TDLS peer STA.

In addition, make the HT/VHT capability configuration handled more
consistently for other STA cases (reject both instead of just HT).
Signed-off-by: default avatarJouni Malinen <jouni@qca.qualcomm.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 9d62a986
...@@ -1412,9 +1412,11 @@ static int ieee80211_change_station(struct wiphy *wiphy, ...@@ -1412,9 +1412,11 @@ static int ieee80211_change_station(struct wiphy *wiphy,
return -ENOENT; return -ENOENT;
} }
/* in station mode, supported rates are only valid with TDLS */ /* in station mode, some updates are only valid with TDLS */
if (sdata->vif.type == NL80211_IFTYPE_STATION && if (sdata->vif.type == NL80211_IFTYPE_STATION &&
params->supported_rates && (params->supported_rates || params->ht_capa || params->vht_capa ||
params->sta_modify_mask ||
(params->sta_flags_mask & BIT(NL80211_STA_FLAG_WME))) &&
!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) { !test_sta_flag(sta, WLAN_STA_TDLS_PEER)) {
mutex_unlock(&local->sta_mtx); mutex_unlock(&local->sta_mtx);
return -EINVAL; return -EINVAL;
......
...@@ -3409,6 +3409,63 @@ static struct net_device *get_vlan(struct genl_info *info, ...@@ -3409,6 +3409,63 @@ static struct net_device *get_vlan(struct genl_info *info,
return ERR_PTR(ret); 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) static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
{ {
struct cfg80211_registered_device *rdev = info->user_ptr[0]; 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) ...@@ -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]); nla_len(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]);
} }
if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL] || if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
info->attrs[NL80211_ATTR_HT_CAPABILITY])
return -EINVAL; return -EINVAL;
if (!rdev->ops->change_station) if (!rdev->ops->change_station)
...@@ -3524,6 +3580,9 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) ...@@ -3524,6 +3580,9 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
return -EINVAL; return -EINVAL;
if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]) if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY])
return -EINVAL; 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 */ /* must be last in here for error handling */
params.vlan = get_vlan(info, rdev); params.vlan = get_vlan(info, rdev);
...@@ -3539,13 +3598,29 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) ...@@ -3539,13 +3598,29 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
* to change the flag. * to change the flag.
*/ */
params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER); 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, &params);
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: case NL80211_IFTYPE_ADHOC:
/* disallow things sta doesn't support */ /* disallow things sta doesn't support */
if (params.plink_action) if (params.plink_action)
return -EINVAL; return -EINVAL;
if (params.local_pm) if (params.local_pm)
return -EINVAL; return -EINVAL;
if (info->attrs[NL80211_ATTR_HT_CAPABILITY] ||
info->attrs[NL80211_ATTR_VHT_CAPABILITY])
return -EINVAL;
/* reject any changes other than AUTHORIZED */ /* reject any changes other than AUTHORIZED */
if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED)) if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED))
return -EINVAL; return -EINVAL;
...@@ -3560,6 +3635,9 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) ...@@ -3560,6 +3635,9 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
return -EINVAL; return -EINVAL;
if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]) if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY])
return -EINVAL; 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 * No special handling for TDLS here -- the userspace
* mesh code doesn't have this bug. * mesh code doesn't have this bug.
...@@ -3584,12 +3662,6 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) ...@@ -3584,12 +3662,6 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
return err; 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) static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
{ {
struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct cfg80211_registered_device *rdev = info->user_ptr[0];
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment