Merge tag 'wireless-next-2022-03-11' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-microblaze.git] / net / wireless / nl80211.c
index c01fbcc..ee1c2b6 100644 (file)
@@ -5,7 +5,7 @@
  * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
  * Copyright 2015-2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2021 Intel Corporation
+ * Copyright (C) 2018-2022 Intel Corporation
  */
 
 #include <linux/if.h>
@@ -285,6 +285,15 @@ static int validate_ie_attr(const struct nlattr *attr,
        return -EINVAL;
 }
 
+static int validate_he_capa(const struct nlattr *attr,
+                           struct netlink_ext_ack *extack)
+{
+       if (!ieee80211_he_capa_size_ok(nla_data(attr), nla_len(attr)))
+               return -EINVAL;
+
+       return 0;
+}
+
 /* policy for the attributes */
 static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR];
 
@@ -730,9 +739,8 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
        [NL80211_ATTR_TXQ_MEMORY_LIMIT] = { .type = NLA_U32 },
        [NL80211_ATTR_TXQ_QUANTUM] = { .type = NLA_U32 },
        [NL80211_ATTR_HE_CAPABILITY] =
-               NLA_POLICY_RANGE(NLA_BINARY,
-                                NL80211_HE_MIN_CAPABILITY_LEN,
-                                NL80211_HE_MAX_CAPABILITY_LEN),
+               NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_he_capa,
+                                      NL80211_HE_MAX_CAPABILITY_LEN),
        [NL80211_ATTR_FTM_RESPONDER] =
                NLA_POLICY_NESTED(nl80211_ftm_responder_policy),
        [NL80211_ATTR_TIMEOUT] = NLA_POLICY_MIN(NLA_U32, 1),
@@ -778,6 +786,10 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
        [NL80211_ATTR_MBSSID_ELEMS] = { .type = NLA_NESTED },
        [NL80211_ATTR_RADAR_BACKGROUND] = { .type = NLA_FLAG },
        [NL80211_ATTR_AP_SETTINGS_FLAGS] = { .type = NLA_U32 },
+       [NL80211_ATTR_EHT_CAPABILITY] =
+               NLA_POLICY_RANGE(NLA_BINARY,
+                                NL80211_EHT_MIN_CAPABILITY_LEN,
+                                NL80211_EHT_MAX_CAPABILITY_LEN),
 };
 
 /* policy for the key attributes */
@@ -1148,6 +1160,12 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, struct wiphy *wiphy,
                if ((chan->flags & IEEE80211_CHAN_16MHZ) &&
                    nla_put_flag(msg, NL80211_FREQUENCY_ATTR_16MHZ))
                        goto nla_put_failure;
+               if ((chan->flags & IEEE80211_CHAN_NO_320MHZ) &&
+                   nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_320MHZ))
+                       goto nla_put_failure;
+               if ((chan->flags & IEEE80211_CHAN_NO_EHT) &&
+                   nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_EHT))
+                       goto nla_put_failure;
        }
 
        if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
@@ -1729,6 +1747,7 @@ nl80211_send_iftype_data(struct sk_buff *msg,
                         const struct ieee80211_sband_iftype_data *iftdata)
 {
        const struct ieee80211_sta_he_cap *he_cap = &iftdata->he_cap;
+       const struct ieee80211_sta_eht_cap *eht_cap = &iftdata->eht_cap;
 
        if (nl80211_put_iftypes(msg, NL80211_BAND_IFTYPE_ATTR_IFTYPES,
                                iftdata->types_mask))
@@ -1749,6 +1768,32 @@ nl80211_send_iftype_data(struct sk_buff *msg,
                        return -ENOBUFS;
        }
 
+       if (eht_cap->has_eht && he_cap->has_he) {
+               u8 mcs_nss_size, ppe_thresh_size;
+               u16 ppe_thres_hdr;
+
+               mcs_nss_size =
+                       ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem,
+                                                  &eht_cap->eht_cap_elem);
+
+               ppe_thres_hdr = get_unaligned_le16(&eht_cap->eht_ppe_thres[0]);
+               ppe_thresh_size =
+                       ieee80211_eht_ppe_size(ppe_thres_hdr,
+                                              eht_cap->eht_cap_elem.phy_cap_info);
+
+               if (nla_put(msg, NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC,
+                           sizeof(eht_cap->eht_cap_elem.mac_cap_info),
+                           eht_cap->eht_cap_elem.mac_cap_info) ||
+                   nla_put(msg, NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY,
+                           sizeof(eht_cap->eht_cap_elem.phy_cap_info),
+                           eht_cap->eht_cap_elem.phy_cap_info) ||
+                   nla_put(msg, NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MCS_SET,
+                           mcs_nss_size, &eht_cap->eht_mcs_nss_supp) ||
+                   nla_put(msg, NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE,
+                           ppe_thresh_size, eht_cap->eht_ppe_thres))
+                       return -ENOBUFS;
+       }
+
        if (sband->band == NL80211_BAND_6GHZ &&
            nla_put(msg, NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA,
                    sizeof(iftdata->he_6ghz_capa),
@@ -5919,6 +5964,14 @@ bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info, int attr)
        case RATE_INFO_BW_HE_RU:
                rate_flg = 0;
                WARN_ON(!(info->flags & RATE_INFO_FLAGS_HE_MCS));
+               break;
+       case RATE_INFO_BW_320:
+               rate_flg = NL80211_RATE_INFO_320_MHZ_WIDTH;
+               break;
+       case RATE_INFO_BW_EHT_RU:
+               rate_flg = 0;
+               WARN_ON(!(info->flags & RATE_INFO_FLAGS_EHT_MCS));
+               break;
        }
 
        if (rate_flg && nla_put_flag(msg, rate_flg))
@@ -5951,6 +6004,17 @@ bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info, int attr)
                    nla_put_u8(msg, NL80211_RATE_INFO_HE_RU_ALLOC,
                               info->he_ru_alloc))
                        return false;
+       } else if (info->flags & RATE_INFO_FLAGS_EHT_MCS) {
+               if (nla_put_u8(msg, NL80211_RATE_INFO_EHT_MCS, info->mcs))
+                       return false;
+               if (nla_put_u8(msg, NL80211_RATE_INFO_EHT_NSS, info->nss))
+                       return false;
+               if (nla_put_u8(msg, NL80211_RATE_INFO_EHT_GI, info->eht_gi))
+                       return false;
+               if (info->bw == RATE_INFO_BW_EHT_RU &&
+                   nla_put_u8(msg, NL80211_RATE_INFO_EHT_RU_ALLOC,
+                              info->eht_ru_alloc))
+                       return false;
        }
 
        nla_nest_end(msg, rate);
@@ -6365,7 +6429,7 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
                if (params->supported_rates)
                        return -EINVAL;
                if (params->ext_capab || params->ht_capa || params->vht_capa ||
-                   params->he_capa)
+                   params->he_capa || params->eht_capa)
                        return -EINVAL;
        }
 
@@ -6568,6 +6632,18 @@ static int nl80211_set_station_tdls(struct genl_info *info,
                        nla_data(info->attrs[NL80211_ATTR_HE_CAPABILITY]);
                params->he_capa_len =
                        nla_len(info->attrs[NL80211_ATTR_HE_CAPABILITY]);
+
+               if (info->attrs[NL80211_ATTR_EHT_CAPABILITY]) {
+                       params->eht_capa =
+                               nla_data(info->attrs[NL80211_ATTR_EHT_CAPABILITY]);
+                       params->eht_capa_len =
+                               nla_len(info->attrs[NL80211_ATTR_EHT_CAPABILITY]);
+
+                       if (!ieee80211_eht_capa_size_ok((const u8 *)params->he_capa,
+                                                       (const u8 *)params->eht_capa,
+                                                       params->eht_capa_len))
+                               return -EINVAL;
+               }
        }
 
        err = nl80211_parse_sta_channel_info(info, params);
@@ -6825,6 +6901,18 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
                        nla_data(info->attrs[NL80211_ATTR_HE_CAPABILITY]);
                params.he_capa_len =
                        nla_len(info->attrs[NL80211_ATTR_HE_CAPABILITY]);
+
+               if (info->attrs[NL80211_ATTR_EHT_CAPABILITY]) {
+                       params.eht_capa =
+                               nla_data(info->attrs[NL80211_ATTR_EHT_CAPABILITY]);
+                       params.eht_capa_len =
+                               nla_len(info->attrs[NL80211_ATTR_EHT_CAPABILITY]);
+
+                       if (!ieee80211_eht_capa_size_ok((const u8 *)params.he_capa,
+                                                       (const u8 *)params.eht_capa,
+                                                       params.eht_capa_len))
+                               return -EINVAL;
+               }
        }
 
        if (info->attrs[NL80211_ATTR_HE_6GHZ_CAPABILITY])
@@ -6874,8 +6962,9 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
                params.ht_capa = NULL;
                params.vht_capa = NULL;
 
-               /* HE requires WME */
-               if (params.he_capa_len || params.he_6ghz_capa)
+               /* HE and EHT require WME */
+               if (params.he_capa_len || params.he_6ghz_capa ||
+                   params.eht_capa_len)
                        return -EINVAL;
        }
 
@@ -7948,6 +8037,7 @@ static int nl80211_get_reg_do(struct sk_buff *skb, struct genl_info *info)
        struct cfg80211_registered_device *rdev;
        struct wiphy *wiphy = NULL;
        struct sk_buff *msg;
+       int err = -EMSGSIZE;
        void *hdr;
 
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
@@ -7966,34 +8056,35 @@ static int nl80211_get_reg_do(struct sk_buff *skb, struct genl_info *info)
 
                rdev = cfg80211_get_dev_from_info(genl_info_net(info), info);
                if (IS_ERR(rdev)) {
-                       nlmsg_free(msg);
-                       rtnl_unlock();
-                       return PTR_ERR(rdev);
+                       err = PTR_ERR(rdev);
+                       goto nla_put_failure;
                }
 
                wiphy = &rdev->wiphy;
                self_managed = wiphy->regulatory_flags &
                               REGULATORY_WIPHY_SELF_MANAGED;
+
+               rcu_read_lock();
+
                regdom = get_wiphy_regdom(wiphy);
 
                /* a self-managed-reg device must have a private regdom */
                if (WARN_ON(!regdom && self_managed)) {
-                       nlmsg_free(msg);
-                       rtnl_unlock();
-                       return -EINVAL;
+                       err = -EINVAL;
+                       goto nla_put_failure_rcu;
                }
 
                if (regdom &&
                    nla_put_u32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy)))
-                       goto nla_put_failure;
+                       goto nla_put_failure_rcu;
+       } else {
+               rcu_read_lock();
        }
 
        if (!wiphy && reg_last_request_cell_base() &&
            nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE,
                        NL80211_USER_REG_HINT_CELL_BASE))
-               goto nla_put_failure;
-
-       rcu_read_lock();
+               goto nla_put_failure_rcu;
 
        if (!regdom)
                regdom = rcu_dereference(cfg80211_regdomain);
@@ -8013,7 +8104,7 @@ nla_put_failure:
        rtnl_unlock();
 put_failure:
        nlmsg_free(msg);
-       return -EMSGSIZE;
+       return err;
 }
 
 static int nl80211_send_regdom(struct sk_buff *msg, struct netlink_callback *cb,
@@ -8059,19 +8150,19 @@ static int nl80211_get_reg_dump(struct sk_buff *skb,
        struct cfg80211_registered_device *rdev;
        int err, reg_idx, start = cb->args[2];
 
-       rtnl_lock();
+       rcu_read_lock();
 
        if (cfg80211_regdomain && start == 0) {
                err = nl80211_send_regdom(skb, cb, cb->nlh->nlmsg_seq,
                                          NLM_F_MULTI, NULL,
-                                         rtnl_dereference(cfg80211_regdomain));
+                                         rcu_dereference(cfg80211_regdomain));
                if (err < 0)
                        goto out_err;
        }
 
        /* the global regdom is idx 0 */
        reg_idx = 1;
-       list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+       list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) {
                regdom = get_wiphy_regdom(&rdev->wiphy);
                if (!regdom)
                        continue;
@@ -8090,7 +8181,7 @@ static int nl80211_get_reg_dump(struct sk_buff *skb,
        cb->args[2] = reg_idx;
        err = skb->len;
 out_err:
-       rtnl_unlock();
+       rcu_read_unlock();
        return err;
 }
 
@@ -10552,6 +10643,8 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
                                             NL80211_EXT_FEATURE_VHT_IBSS))
                        return -EINVAL;
                break;
+       case NL80211_CHAN_WIDTH_320:
+               return -EINVAL;
        default:
                return -EINVAL;
        }