]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - net/mac80211/cfg.c
mac80211: allow WDS mode
[mirror_ubuntu-bionic-kernel.git] / net / mac80211 / cfg.c
index e9ba6fcc0e45754a42cc27ec7cd93d2744a5399a..699d97b8de5eb97637ff7d9a98a708a76e03594f 100644 (file)
 #include <net/cfg80211.h>
 #include "ieee80211_i.h"
 #include "cfg.h"
-#include "ieee80211_rate.h"
+#include "rate.h"
 #include "mesh.h"
 
-#define DEFAULT_RATES 0
-
 static enum ieee80211_if_types
 nl80211_type_to_mac80211_type(enum nl80211_iftype type)
 {
@@ -35,6 +33,8 @@ nl80211_type_to_mac80211_type(enum nl80211_iftype type)
        case NL80211_IFTYPE_MESH_POINT:
                return IEEE80211_IF_TYPE_MESH_POINT;
 #endif
+       case NL80211_IFTYPE_WDS:
+               return IEEE80211_IF_TYPE_WDS;
        default:
                return IEEE80211_IF_TYPE_INVALID;
        }
@@ -137,6 +137,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
        struct sta_info *sta = NULL;
        enum ieee80211_key_alg alg;
        struct ieee80211_key *key;
+       int err;
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
@@ -159,17 +160,24 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
        if (!key)
                return -ENOMEM;
 
+       rcu_read_lock();
+
        if (mac_addr) {
                sta = sta_info_get(sdata->local, mac_addr);
                if (!sta) {
                        ieee80211_key_free(key);
-                       return -ENOENT;
+                       err = -ENOENT;
+                       goto out_unlock;
                }
        }
 
        ieee80211_key_link(key, sdata, sta);
 
-       return 0;
+       err = 0;
+ out_unlock:
+       rcu_read_unlock();
+
+       return err;
 }
 
 static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
@@ -181,28 +189,37 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
+       rcu_read_lock();
+
        if (mac_addr) {
+               ret = -ENOENT;
+
                sta = sta_info_get(sdata->local, mac_addr);
                if (!sta)
-                       return -ENOENT;
+                       goto out_unlock;
 
-               ret = 0;
                if (sta->key) {
                        ieee80211_key_free(sta->key);
                        WARN_ON(sta->key);
-               } else
-                       ret = -ENOENT;
+                       ret = 0;
+               }
 
-               return ret;
+               goto out_unlock;
        }
 
-       if (!sdata->keys[key_idx])
-               return -ENOENT;
+       if (!sdata->keys[key_idx]) {
+               ret = -ENOENT;
+               goto out_unlock;
+       }
 
        ieee80211_key_free(sdata->keys[key_idx]);
        WARN_ON(sdata->keys[key_idx]);
 
-       return 0;
+       ret = 0;
+ out_unlock:
+       rcu_read_unlock();
+
+       return ret;
 }
 
 static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
@@ -219,6 +236,8 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
        u16 iv16;
        int err = -ENOENT;
 
+       rcu_read_lock();
+
        if (mac_addr) {
                sta = sta_info_get(sdata->local, mac_addr);
                if (!sta)
@@ -282,6 +301,7 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
        err = 0;
 
  out:
+       rcu_read_unlock();
        return err;
 }
 
@@ -291,9 +311,13 @@ static int ieee80211_config_default_key(struct wiphy *wiphy,
 {
        struct ieee80211_sub_if_data *sdata;
 
+       rcu_read_lock();
+
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        ieee80211_set_default_key(sdata, key_idx);
 
+       rcu_read_unlock();
+
        return 0;
 }
 
@@ -571,6 +595,12 @@ static void sta_apply_parameters(struct ieee80211_local *local,
        struct ieee80211_supported_band *sband;
        struct ieee80211_sub_if_data *sdata = sta->sdata;
 
+       /*
+        * FIXME: updating the flags is racy when this function is
+        *        called from ieee80211_change_station(), this will
+        *        be resolved in a future patch.
+        */
+
        if (params->station_flags & STATION_FLAG_CHANGED) {
                sta->flags &= ~WLAN_STA_AUTHORIZED;
                if (params->station_flags & STATION_FLAG_AUTHORIZED)
@@ -585,6 +615,13 @@ static void sta_apply_parameters(struct ieee80211_local *local,
                        sta->flags |= WLAN_STA_WME;
        }
 
+       /*
+        * FIXME: updating the following information is racy when this
+        *        function is called from ieee80211_change_station().
+        *        However, all this information should be static so
+        *        maybe we should just reject attemps to change it.
+        */
+
        if (params->aid) {
                sta->aid = params->aid;
                if (sta->aid > IEEE80211_MAX_AID)
@@ -626,6 +663,7 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
        struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
        struct sta_info *sta;
        struct ieee80211_sub_if_data *sdata;
+       int err;
 
        /* Prevent a race with changing the rate control algorithm */
        if (!netif_running(dev))
@@ -640,17 +678,15 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
        } else
                sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-       if (ieee80211_vif_is_mesh(&sdata->vif))
-               sta = mesh_plink_add(mac, DEFAULT_RATES, sdata);
-       else
-               sta = sta_info_add(sdata, mac);
+       if (compare_ether_addr(mac, dev->dev_addr) == 0)
+               return -EINVAL;
 
-       if (IS_ERR(sta))
-               return PTR_ERR(sta);
+       if (is_multicast_ether_addr(mac))
+               return -EINVAL;
 
-       if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN ||
-           sdata->vif.type == IEEE80211_IF_TYPE_AP)
-               ieee80211_send_layer2_update(sta);
+       sta = sta_info_alloc(sdata, mac, GFP_KERNEL);
+       if (!sta)
+               return -ENOMEM;
 
        sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC;
 
@@ -658,6 +694,21 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
 
        rate_control_rate_init(sta, local);
 
+       rcu_read_lock();
+
+       err = sta_info_insert(sta);
+       if (err) {
+               /* STA has been freed */
+               rcu_read_unlock();
+               return err;
+       }
+
+       if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN ||
+           sdata->vif.type == IEEE80211_IF_TYPE_AP)
+               ieee80211_send_layer2_update(sta);
+
+       rcu_read_unlock();
+
        return 0;
 }
 
@@ -669,17 +720,19 @@ static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
        struct sta_info *sta;
 
        if (mac) {
+               rcu_read_lock();
+
                /* XXX: get sta belonging to dev */
                sta = sta_info_get(local, mac);
-               if (!sta)
+               if (!sta) {
+                       rcu_read_unlock();
                        return -ENOENT;
+               }
 
                sta_info_unlink(&sta);
+               rcu_read_unlock();
 
-               if (sta) {
-                       synchronize_rcu();
-                       sta_info_destroy(sta);
-               }
+               sta_info_destroy(sta);
        } else
                sta_info_flush(local, sdata);
 
@@ -695,17 +748,23 @@ static int ieee80211_change_station(struct wiphy *wiphy,
        struct sta_info *sta;
        struct ieee80211_sub_if_data *vlansdata;
 
+       rcu_read_lock();
+
        /* XXX: get sta belonging to dev */
        sta = sta_info_get(local, mac);
-       if (!sta)
+       if (!sta) {
+               rcu_read_unlock();
                return -ENOENT;
+       }
 
        if (params->vlan && params->vlan != sta->sdata->dev) {
                vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
 
                if (vlansdata->vif.type != IEEE80211_IF_TYPE_VLAN ||
-                   vlansdata->vif.type != IEEE80211_IF_TYPE_AP)
+                   vlansdata->vif.type != IEEE80211_IF_TYPE_AP) {
+                       rcu_read_unlock();
                        return -EINVAL;
+               }
 
                sta->sdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
                ieee80211_send_layer2_update(sta);
@@ -713,6 +772,8 @@ static int ieee80211_change_station(struct wiphy *wiphy,
 
        sta_apply_parameters(local, sta, params);
 
+       rcu_read_unlock();
+
        return 0;
 }
 
@@ -760,7 +821,7 @@ static int ieee80211_del_mpath(struct wiphy *wiphy, struct net_device *dev,
                                 u8 *dst)
 {
        if (dst)
-               return mesh_path_del(dst, dev, false);
+               return mesh_path_del(dst, dev);
 
        mesh_path_flush(dev);
        return 0;