Merge branch 'upstream-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jgarzi...
[cascardo/linux.git] / net / mac80211 / cfg.c
index 6ac4923..a9fce4a 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;
        }
@@ -136,8 +136,8 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
        struct ieee80211_sub_if_data *sdata;
        struct sta_info *sta = NULL;
        enum ieee80211_key_alg alg;
-       int ret;
        struct ieee80211_key *key;
+       int err;
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
@@ -160,22 +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);
 
-       ret = 0;
-
-       if (sta)
-               sta_info_put(sta);
+       err = 0;
+ out_unlock:
+       rcu_read_unlock();
 
-       return ret;
+       return err;
 }
 
 static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
@@ -184,35 +186,40 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
        struct ieee80211_sub_if_data *sdata;
        struct sta_info *sta;
        int ret;
-       struct ieee80211_key *key;
 
        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) {
-                       key = sta->key;
-                       ieee80211_key_free(key);
+                       ieee80211_key_free(sta->key);
                        WARN_ON(sta->key);
-               } else
-                       ret = -ENOENT;
+                       ret = 0;
+               }
 
-               sta_info_put(sta);
-               return ret;
+               goto out_unlock;
        }
 
-       if (!sdata->keys[key_idx])
-               return -ENOENT;
+       if (!sdata->keys[key_idx]) {
+               ret = -ENOENT;
+               goto out_unlock;
+       }
 
-       key = sdata->keys[key_idx];
-       ieee80211_key_free(key);
+       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,
@@ -229,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)
@@ -292,8 +301,7 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
        err = 0;
 
  out:
-       if (sta)
-               sta_info_put(sta);
+       rcu_read_unlock();
        return err;
 }
 
@@ -303,15 +311,19 @@ 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;
 }
 
 static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
 {
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
+       struct ieee80211_sub_if_data *sdata = sta->sdata;
 
        sinfo->filled = STATION_INFO_INACTIVE_TIME |
                        STATION_INFO_RX_BYTES |
@@ -340,16 +352,20 @@ static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev,
 {
        struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
        struct sta_info *sta;
+       int ret = -ENOENT;
+
+       rcu_read_lock();
 
        sta = sta_info_get_by_idx(local, idx, dev);
-       if (!sta)
-               return -ENOENT;
+       if (sta) {
+               ret = 0;
+               memcpy(mac, sta->addr, ETH_ALEN);
+               sta_set_sinfo(sta, sinfo);
+       }
 
-       memcpy(mac, sta->addr, ETH_ALEN);
-       sta_set_sinfo(sta, sinfo);
-       sta_info_put(sta);
+       rcu_read_unlock();
 
-       return 0;
+       return ret;
 }
 
 static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev,
@@ -357,16 +373,21 @@ static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev,
 {
        struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
        struct sta_info *sta;
+       int ret = -ENOENT;
 
-       sta = sta_info_get(local, mac);
-       if (!sta)
-               return -ENOENT;
+       rcu_read_lock();
 
        /* XXX: verify sta->dev == dev */
-       sta_set_sinfo(sta, sinfo);
-       sta_info_put(sta);
 
-       return 0;
+       sta = sta_info_get(local, mac);
+       if (sta) {
+               ret = 0;
+               sta_set_sinfo(sta, sinfo);
+       }
+
+       rcu_read_unlock();
+
+       return ret;
 }
 
 /*
@@ -559,8 +580,8 @@ static void ieee80211_send_layer2_update(struct sta_info *sta)
        msg->xid_info[1] = 1;   /* LLC types/classes: Type 1 LLC */
        msg->xid_info[2] = 0;   /* XID sender's receive window size (RW) */
 
-       skb->dev = sta->dev;
-       skb->protocol = eth_type_trans(skb, sta->dev);
+       skb->dev = sta->sdata->dev;
+       skb->protocol = eth_type_trans(skb, sta->sdata->dev);
        memset(skb->cb, 0, sizeof(skb->cb));
        netif_rx(skb);
 }
@@ -572,7 +593,13 @@ static void sta_apply_parameters(struct ieee80211_local *local,
        u32 rates;
        int i, j;
        struct ieee80211_supported_band *sband;
-       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
+       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;
@@ -588,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)
@@ -629,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))
@@ -637,24 +672,21 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
        if (params->vlan) {
                sdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
 
-               if (sdata->vif.type != IEEE80211_IF_TYPE_VLAN ||
+               if (sdata->vif.type != IEEE80211_IF_TYPE_VLAN &&
                    sdata->vif.type != IEEE80211_IF_TYPE_AP)
                        return -EINVAL;
        } else
                sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
-       if (ieee80211_vif_is_mesh(&sdata->vif))
-               sta = mesh_plink_add(mac, DEFAULT_RATES, dev);
-       else
-               sta = sta_info_add(local, dev, mac, GFP_KERNEL);
+       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;
 
-       sta->dev = sdata->dev;
-       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;
 
@@ -662,7 +694,20 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
 
        rate_control_rate_init(sta, local);
 
-       sta_info_put(sta);
+       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;
 }
@@ -670,19 +715,26 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
 static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
                                 u8 *mac)
 {
-       struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+       struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct ieee80211_local *local = sdata->local;
        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_free(sta);
-               sta_info_put(sta);
+               sta_info_unlink(&sta);
+               rcu_read_unlock();
+
+               sta_info_destroy(sta);
        } else
-               sta_info_flush(local, dev);
+               sta_info_flush(local, sdata);
 
        return 0;
 }
@@ -696,25 +748,31 @@ 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->dev) {
+       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)
+               if (vlansdata->vif.type != IEEE80211_IF_TYPE_VLAN &&
+                   vlansdata->vif.type != IEEE80211_IF_TYPE_AP) {
+                       rcu_read_unlock();
                        return -EINVAL;
+               }
 
-               sta->dev = params->vlan;
+               sta->sdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
                ieee80211_send_layer2_update(sta);
        }
 
        sta_apply_parameters(local, sta, params);
 
-       sta_info_put(sta);
+       rcu_read_unlock();
 
        return 0;
 }
@@ -735,23 +793,26 @@ static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev,
        if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)
                return -ENOTSUPP;
 
+       rcu_read_lock();
        sta = sta_info_get(local, next_hop);
-       if (!sta)
+       if (!sta) {
+               rcu_read_unlock();
                return -ENOENT;
+       }
 
        err = mesh_path_add(dst, dev);
-       if (err)
+       if (err) {
+               rcu_read_unlock();
                return err;
+       }
 
-       rcu_read_lock();
        mpath = mesh_path_lookup(dst, dev);
        if (!mpath) {
                rcu_read_unlock();
-               sta_info_put(sta);
                return -ENXIO;
        }
        mesh_path_fix_nexthop(mpath, sta);
-       sta_info_put(sta);
+
        rcu_read_unlock();
        return 0;
 }
@@ -781,20 +842,22 @@ static int ieee80211_change_mpath(struct wiphy *wiphy,
        if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)
                return -ENOTSUPP;
 
+       rcu_read_lock();
+
        sta = sta_info_get(local, next_hop);
-       if (!sta)
+       if (!sta) {
+               rcu_read_unlock();
                return -ENOENT;
+       }
 
-       rcu_read_lock();
        mpath = mesh_path_lookup(dst, dev);
        if (!mpath) {
                rcu_read_unlock();
-               sta_info_put(sta);
                return -ENOENT;
        }
 
        mesh_path_fix_nexthop(mpath, sta);
-       sta_info_put(sta);
+
        rcu_read_unlock();
        return 0;
 }