]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - net/wireless/nl80211.c
Merge tag 'mac80211-next-for-davem-2019-04-26' of git://git.kernel.org/pub/scm/linux...
[linux.git] / net / wireless / nl80211.c
index e7ee18ab6cb7143d2ff826644ffb9c980d25e6bd..e74d21f4108a0688a50a5bd315fea4dc3361ea85 100644 (file)
@@ -331,6 +331,11 @@ const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
                                               .len = NL80211_MAX_SUPP_RATES },
        [NL80211_ATTR_STA_PLINK_ACTION] =
                NLA_POLICY_MAX(NLA_U8, NUM_NL80211_PLINK_ACTIONS - 1),
+       [NL80211_ATTR_STA_TX_POWER_SETTING] =
+               NLA_POLICY_RANGE(NLA_U8,
+                                NL80211_TX_POWER_AUTOMATIC,
+                                NL80211_TX_POWER_FIXED),
+       [NL80211_ATTR_STA_TX_POWER] = { .type = NLA_S16 },
        [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 },
        [NL80211_ATTR_MNTR_FLAGS] = { /* NLA_NESTED can't be empty */ },
        [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY,
@@ -553,6 +558,7 @@ static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = {
        [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG },
        [NL80211_KEY_TYPE] = NLA_POLICY_MAX(NLA_U32, NUM_NL80211_KEYTYPES - 1),
        [NL80211_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED },
+       [NL80211_KEY_MODE] = NLA_POLICY_RANGE(NLA_U8, 0, NL80211_KEY_SET_TX),
 };
 
 /* policy for the key default flags */
@@ -617,12 +623,21 @@ nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = {
        [NL80211_REKEY_DATA_REPLAY_CTR] = { .len = NL80211_REPLAY_CTR_LEN },
 };
 
+static const struct nla_policy
+nl80211_match_band_rssi_policy[NUM_NL80211_BANDS] = {
+       [NL80211_BAND_2GHZ] = { .type = NLA_S32 },
+       [NL80211_BAND_5GHZ] = { .type = NLA_S32 },
+       [NL80211_BAND_60GHZ] = { .type = NLA_S32 },
+};
+
 static const struct nla_policy
 nl80211_match_policy[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1] = {
        [NL80211_SCHED_SCAN_MATCH_ATTR_SSID] = { .type = NLA_BINARY,
                                                 .len = IEEE80211_MAX_SSID_LEN },
        [NL80211_SCHED_SCAN_MATCH_ATTR_BSSID] = { .len = ETH_ALEN },
        [NL80211_SCHED_SCAN_MATCH_ATTR_RSSI] = { .type = NLA_U32 },
+       [NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI] =
+               NLA_POLICY_NESTED(nl80211_match_band_rssi_policy),
 };
 
 static const struct nla_policy
@@ -958,6 +973,9 @@ static int nl80211_parse_key_new(struct genl_info *info, struct nlattr *key,
                k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST];
        }
 
+       if (tb[NL80211_KEY_MODE])
+               k->p.mode = nla_get_u8(tb[NL80211_KEY_MODE]);
+
        return 0;
 }
 
@@ -3634,8 +3652,11 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
        if (key.idx < 0)
                return -EINVAL;
 
-       /* only support setting default key */
-       if (!key.def && !key.defmgmt)
+       /* Only support setting default key and
+        * Extended Key ID action NL80211_KEY_SET_TX.
+        */
+       if (!key.def && !key.defmgmt &&
+           !(key.p.mode == NL80211_KEY_SET_TX))
                return -EINVAL;
 
        wdev_lock(dev->ieee80211_ptr);
@@ -3659,7 +3680,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
 #ifdef CONFIG_CFG80211_WEXT
                dev->ieee80211_ptr->wext.default_key = key.idx;
 #endif
-       } else {
+       } else if (key.defmgmt) {
                if (key.def_uni || !key.def_multi) {
                        err = -EINVAL;
                        goto out;
@@ -3681,8 +3702,25 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
 #ifdef CONFIG_CFG80211_WEXT
                dev->ieee80211_ptr->wext.default_mgmt_key = key.idx;
 #endif
-       }
+       } else if (key.p.mode == NL80211_KEY_SET_TX &&
+                  wiphy_ext_feature_isset(&rdev->wiphy,
+                                          NL80211_EXT_FEATURE_EXT_KEY_ID)) {
+               u8 *mac_addr = NULL;
+
+               if (info->attrs[NL80211_ATTR_MAC])
+                       mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+               if (!mac_addr || key.idx < 0 || key.idx > 1) {
+                       err = -EINVAL;
+                       goto out;
+               }
 
+               err = rdev_add_key(rdev, dev, key.idx,
+                                  NL80211_KEYTYPE_PAIRWISE,
+                                  mac_addr, &key.p);
+       } else {
+               err = -EINVAL;
+       }
  out:
        wdev_unlock(dev->ieee80211_ptr);
 
@@ -3843,8 +3881,7 @@ static struct cfg80211_acl_data *parse_acl_data(struct wiphy *wiphy,
        if (n_entries > wiphy->max_acl_mac_addrs)
                return ERR_PTR(-ENOTSUPP);
 
-       acl = kzalloc(sizeof(*acl) + (sizeof(struct mac_address) * n_entries),
-                     GFP_KERNEL);
+       acl = kzalloc(struct_size(acl, mac_addrs, n_entries), GFP_KERNEL);
        if (!acl)
                return ERR_PTR(-ENOMEM);
 
@@ -4889,6 +4926,7 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
        PUT_SINFO(TX_RETRIES, tx_retries, u32);
        PUT_SINFO(TX_FAILED, tx_failed, u32);
        PUT_SINFO(EXPECTED_THROUGHPUT, expected_throughput, u32);
+       PUT_SINFO(AIRTIME_LINK_METRIC, airtime_link_metric, u32);
        PUT_SINFO(BEACON_LOSS, beacon_loss_count, u32);
        PUT_SINFO(LOCAL_PM, local_pm, u32);
        PUT_SINFO(PEER_PM, peer_pm, u32);
@@ -5387,6 +5425,36 @@ static int nl80211_set_station_tdls(struct genl_info *info,
        return nl80211_parse_sta_wme(info, params);
 }
 
+static int nl80211_parse_sta_txpower_setting(struct genl_info *info,
+                                            struct station_parameters *params)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       int idx;
+
+       if (info->attrs[NL80211_ATTR_STA_TX_POWER_SETTING]) {
+               if (!rdev->ops->set_tx_power ||
+                   !wiphy_ext_feature_isset(&rdev->wiphy,
+                                        NL80211_EXT_FEATURE_STA_TX_PWR))
+                       return -EOPNOTSUPP;
+
+               idx = NL80211_ATTR_STA_TX_POWER_SETTING;
+               params->txpwr.type = nla_get_u8(info->attrs[idx]);
+
+               if (params->txpwr.type == NL80211_TX_POWER_LIMITED) {
+                       idx = NL80211_ATTR_STA_TX_POWER;
+
+                       if (info->attrs[idx])
+                               params->txpwr.power =
+                                       nla_get_s16(info->attrs[idx]);
+                       else
+                               return -EINVAL;
+               }
+               params->sta_modify_mask |= STATION_PARAM_APPLY_STA_TXPOWER;
+       }
+
+       return 0;
+}
+
 static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -5480,6 +5548,10 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
                                     NL80211_EXT_FEATURE_AIRTIME_FAIRNESS))
                return -EOPNOTSUPP;
 
+       err = nl80211_parse_sta_txpower_setting(info, &params);
+       if (err)
+               return err;
+
        /* Include parameters for TDLS peer (will check later) */
        err = nl80211_set_station_tdls(info, &params);
        if (err)
@@ -5617,6 +5689,10 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
                                     NL80211_EXT_FEATURE_AIRTIME_FAIRNESS))
                return -EOPNOTSUPP;
 
+       err = nl80211_parse_sta_txpower_setting(info, &params);
+       if (err)
+               return err;
+
        err = nl80211_parse_sta_channel_info(info, &params);
        if (err)
                return err;
@@ -6882,7 +6958,7 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
        struct nlattr *nl_reg_rule;
        char *alpha2;
        int rem_reg_rules, r;
-       u32 num_rules = 0, rule_idx = 0, size_of_regd;
+       u32 num_rules = 0, rule_idx = 0;
        enum nl80211_dfs_regions dfs_region = NL80211_DFS_UNSET;
        struct ieee80211_regdomain *rd;
 
@@ -6907,10 +6983,7 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
        if (!reg_is_valid_request(alpha2))
                return -EINVAL;
 
-       size_of_regd = sizeof(struct ieee80211_regdomain) +
-                      num_rules * sizeof(struct ieee80211_reg_rule);
-
-       rd = kzalloc(size_of_regd, GFP_KERNEL);
+       rd = kzalloc(struct_size(rd, reg_rules, num_rules), GFP_KERNEL);
        if (!rd)
                return -ENOMEM;
 
@@ -7537,6 +7610,41 @@ nl80211_parse_sched_scan_plans(struct wiphy *wiphy, int n_plans,
        return 0;
 }
 
+static int
+nl80211_parse_sched_scan_per_band_rssi(struct wiphy *wiphy,
+                                      struct cfg80211_match_set *match_sets,
+                                      struct nlattr *tb_band_rssi,
+                                      s32 rssi_thold)
+{
+       struct nlattr *attr;
+       int i, tmp, ret = 0;
+
+       if (!wiphy_ext_feature_isset(wiphy,
+                   NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD)) {
+               if (tb_band_rssi)
+                       ret = -EOPNOTSUPP;
+               else
+                       for (i = 0; i < NUM_NL80211_BANDS; i++)
+                               match_sets->per_band_rssi_thold[i] =
+                                       NL80211_SCAN_RSSI_THOLD_OFF;
+               return ret;
+       }
+
+       for (i = 0; i < NUM_NL80211_BANDS; i++)
+               match_sets->per_band_rssi_thold[i] = rssi_thold;
+
+       nla_for_each_nested(attr, tb_band_rssi, tmp) {
+               enum nl80211_band band = nla_type(attr);
+
+               if (band < 0 || band >= NUM_NL80211_BANDS)
+                       return -EINVAL;
+
+               match_sets->per_band_rssi_thold[band] = nla_get_s32(attr);
+       }
+
+       return 0;
+}
+
 static struct cfg80211_sched_scan_request *
 nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev,
                         struct nlattr **attrs, int max_match_sets)
@@ -7776,43 +7884,55 @@ nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev,
                                goto out_free;
                        ssid = tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID];
                        bssid = tb[NL80211_SCHED_SCAN_MATCH_ATTR_BSSID];
-                       if (ssid || bssid) {
-                               if (WARN_ON(i >= n_match_sets)) {
-                                       /* this indicates a programming error,
-                                        * the loop above should have verified
-                                        * things properly
-                                        */
+
+                       if (!ssid && !bssid) {
+                               i++;
+                               continue;
+                       }
+
+                       if (WARN_ON(i >= n_match_sets)) {
+                               /* this indicates a programming error,
+                                * the loop above should have verified
+                                * things properly
+                                */
+                               err = -EINVAL;
+                               goto out_free;
+                       }
+
+                       if (ssid) {
+                               if (nla_len(ssid) > IEEE80211_MAX_SSID_LEN) {
                                        err = -EINVAL;
                                        goto out_free;
                                }
-
-                               if (ssid) {
-                                       if (nla_len(ssid) > IEEE80211_MAX_SSID_LEN) {
-                                               err = -EINVAL;
-                                               goto out_free;
-                                       }
-                                       memcpy(request->match_sets[i].ssid.ssid,
-                                              nla_data(ssid), nla_len(ssid));
-                                       request->match_sets[i].ssid.ssid_len =
-                                               nla_len(ssid);
-                               }
-                               if (bssid) {
-                                       if (nla_len(bssid) != ETH_ALEN) {
-                                               err = -EINVAL;
-                                               goto out_free;
-                                       }
-                                       memcpy(request->match_sets[i].bssid,
-                                              nla_data(bssid), ETH_ALEN);
+                               memcpy(request->match_sets[i].ssid.ssid,
+                                      nla_data(ssid), nla_len(ssid));
+                               request->match_sets[i].ssid.ssid_len =
+                                       nla_len(ssid);
+                       }
+                       if (bssid) {
+                               if (nla_len(bssid) != ETH_ALEN) {
+                                       err = -EINVAL;
+                                       goto out_free;
                                }
+                               memcpy(request->match_sets[i].bssid,
+                                      nla_data(bssid), ETH_ALEN);
+                       }
 
-                               /* special attribute - old implementation w/a */
+                       /* special attribute - old implementation w/a */
+                       request->match_sets[i].rssi_thold = default_match_rssi;
+                       rssi = tb[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI];
+                       if (rssi)
                                request->match_sets[i].rssi_thold =
-                                       default_match_rssi;
-                               rssi = tb[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI];
-                               if (rssi)
-                                       request->match_sets[i].rssi_thold =
-                                               nla_get_s32(rssi);
-                       }
+                                       nla_get_s32(rssi);
+
+                       /* Parse per band RSSI attribute */
+                       err = nl80211_parse_sched_scan_per_band_rssi(wiphy,
+                               &request->match_sets[i],
+                               tb[NL80211_SCHED_SCAN_MATCH_PER_BAND_RSSI],
+                               request->match_sets[i].rssi_thold);
+                       if (err)
+                               goto out_free;
+
                        i++;
                }
 
@@ -8061,7 +8181,7 @@ static int nl80211_notify_radar_detection(struct sk_buff *skb,
 
        cfg80211_sched_dfs_chan_update(rdev);
 
-       memcpy(&rdev->radar_chandef, &chandef, sizeof(chandef));
+       rdev->radar_chandef = chandef;
 
        /* Propagate this notification to other radios as well */
        queue_work(cfg80211_wq, &rdev->propagate_radar_detect_wk);
@@ -13259,6 +13379,72 @@ static int nl80211_get_ftm_responder_stats(struct sk_buff *skb,
        return -ENOBUFS;
 }
 
+static int nl80211_update_owe_info(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct cfg80211_update_owe_info owe_info;
+       struct net_device *dev = info->user_ptr[1];
+
+       if (!rdev->ops->update_owe_info)
+               return -EOPNOTSUPP;
+
+       if (!info->attrs[NL80211_ATTR_STATUS_CODE] ||
+           !info->attrs[NL80211_ATTR_MAC])
+               return -EINVAL;
+
+       memset(&owe_info, 0, sizeof(owe_info));
+       owe_info.status = nla_get_u16(info->attrs[NL80211_ATTR_STATUS_CODE]);
+       nla_memcpy(owe_info.peer, info->attrs[NL80211_ATTR_MAC], ETH_ALEN);
+
+       if (info->attrs[NL80211_ATTR_IE]) {
+               owe_info.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+               owe_info.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+       }
+
+       return rdev_update_owe_info(rdev, dev, &owe_info);
+}
+
+static int nl80211_probe_mesh_link(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev = info->user_ptr[0];
+       struct net_device *dev = info->user_ptr[1];
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct station_info sinfo = {};
+       const u8 *buf;
+       size_t len;
+       u8 *dest;
+       int err;
+
+       if (!rdev->ops->probe_mesh_link || !rdev->ops->get_station)
+               return -EOPNOTSUPP;
+
+       if (!info->attrs[NL80211_ATTR_MAC] ||
+           !info->attrs[NL80211_ATTR_FRAME]) {
+               GENL_SET_ERR_MSG(info, "Frame or MAC missing");
+               return -EINVAL;
+       }
+
+       if (wdev->iftype != NL80211_IFTYPE_MESH_POINT)
+               return -EOPNOTSUPP;
+
+       dest = nla_data(info->attrs[NL80211_ATTR_MAC]);
+       buf = nla_data(info->attrs[NL80211_ATTR_FRAME]);
+       len = nla_len(info->attrs[NL80211_ATTR_FRAME]);
+
+       if (len < sizeof(struct ethhdr))
+               return -EINVAL;
+
+       if (!ether_addr_equal(buf, dest) || is_multicast_ether_addr(buf) ||
+           !ether_addr_equal(buf + ETH_ALEN, dev->dev_addr))
+               return -EINVAL;
+
+       err = rdev_get_station(rdev, dev, dest, &sinfo);
+       if (err)
+               return err;
+
+       return rdev_probe_mesh_link(rdev, dev, dest, buf, len);
+}
+
 #define NL80211_FLAG_NEED_WIPHY                0x01
 #define NL80211_FLAG_NEED_NETDEV       0x02
 #define NL80211_FLAG_NEED_RTNL         0x04
@@ -14095,6 +14281,20 @@ static const struct genl_ops nl80211_ops[] = {
                .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
                                  NL80211_FLAG_NEED_RTNL,
        },
+       {
+               .cmd = NL80211_CMD_UPDATE_OWE_INFO,
+               .doit = nl80211_update_owe_info,
+               .flags = GENL_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
+       {
+               .cmd = NL80211_CMD_PROBE_MESH_LINK,
+               .doit = nl80211_probe_mesh_link,
+               .flags = GENL_UNS_ADMIN_PERM,
+               .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+                                 NL80211_FLAG_NEED_RTNL,
+       },
 };
 
 static struct genl_family nl80211_fam __ro_after_init = {
@@ -15624,6 +15824,11 @@ void cfg80211_ch_switch_notify(struct net_device *dev,
 
        wdev->chandef = *chandef;
        wdev->preset_chandef = *chandef;
+
+       if (wdev->iftype == NL80211_IFTYPE_STATION &&
+           !WARN_ON(!wdev->current_bss))
+               wdev->current_bss->pub.channel = chandef->chan;
+
        nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL,
                                 NL80211_CMD_CH_SWITCH_NOTIFY, 0);
 }
@@ -16267,6 +16472,46 @@ int cfg80211_external_auth_request(struct net_device *dev,
 }
 EXPORT_SYMBOL(cfg80211_external_auth_request);
 
+void cfg80211_update_owe_info_event(struct net_device *netdev,
+                                   struct cfg80211_update_owe_info *owe_info,
+                                   gfp_t gfp)
+{
+       struct wiphy *wiphy = netdev->ieee80211_ptr->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+       struct sk_buff *msg;
+       void *hdr;
+
+       trace_cfg80211_update_owe_info_event(wiphy, netdev, owe_info);
+
+       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+       if (!msg)
+               return;
+
+       hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_UPDATE_OWE_INFO);
+       if (!hdr)
+               goto nla_put_failure;
+
+       if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+           nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
+           nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, owe_info->peer))
+               goto nla_put_failure;
+
+       if (!owe_info->ie_len ||
+           nla_put(msg, NL80211_ATTR_IE, owe_info->ie_len, owe_info->ie))
+               goto nla_put_failure;
+
+       genlmsg_end(msg, hdr);
+
+       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+                               NL80211_MCGRP_MLME, gfp);
+       return;
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_update_owe_info_event);
+
 /* initialisation/exit functions */
 
 int __init nl80211_init(void)