cfg80211: Pass TDLS peer's QoS/HT/VHT information during set_station
authorJouni Malinen <jouni@qca.qualcomm.com>
Thu, 14 Feb 2013 19:10:54 +0000 (21:10 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Fri, 15 Feb 2013 08:41:43 +0000 (09:41 +0100)
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>
net/mac80211/cfg.c
net/wireless/nl80211.c

index 0800fb331ce56f79ba7f808f56b26efd8aca80b9..179dcbd8be1c730b616c69c31f0e6a6ef3c60c6d 100644 (file)
@@ -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;
index be9f2b5a403f19c209dbeab6eb89d557d72384ac..580ffeaef3d5a2b5665c3491c0282ce571a4ab35 100644 (file)
@@ -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];