Skip to content

Commit

Permalink
cfg80211: Pass TDLS peer's QoS/HT/VHT information during set_station
Browse files Browse the repository at this point in the history
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: Jouni Malinen <jouni@qca.qualcomm.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
  • Loading branch information
Jouni Malinen authored and Johannes Berg committed Feb 15, 2013
1 parent 9d62a98 commit df88129
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 11 deletions.
6 changes: 4 additions & 2 deletions net/mac80211/cfg.c
Original file line number Diff line number Diff line change
Expand Up @@ -1412,9 +1412,11 @@ static int ieee80211_change_station(struct wiphy *wiphy,
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 &&
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)) {
mutex_unlock(&local->sta_mtx);
return -EINVAL;
Expand Down
90 changes: 81 additions & 9 deletions net/wireless/nl80211.c
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand All @@ -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, &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:
/* 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;
Expand All @@ -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.
Expand All @@ -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];
Expand Down

0 comments on commit df88129

Please sign in to comment.