Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -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;
+81 −9
Original line number Diff line number Diff line
@@ -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, &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;
@@ -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];