drm/radeon: bump the UMS driver version for r6xx/r7xx const buffer support
[safe/jmp/linux-2.6] / net / wireless / nl80211.c
index 50cf593..a602843 100644 (file)
 #include <linux/rtnetlink.h>
 #include <linux/netlink.h>
 #include <linux/etherdevice.h>
+#include <net/net_namespace.h>
 #include <net/genetlink.h>
 #include <net/cfg80211.h>
+#include <net/sock.h>
 #include "core.h"
 #include "nl80211.h"
 #include "reg.h"
@@ -27,24 +29,26 @@ static struct genl_family nl80211_fam = {
        .hdrsize = 0,           /* no private header */
        .version = 1,           /* no particular meaning now */
        .maxattr = NL80211_ATTR_MAX,
+       .netnsok = true,
 };
 
 /* internal helper: get rdev and dev */
-static int get_rdev_dev_by_info_ifindex(struct nlattr **attrs,
+static int get_rdev_dev_by_info_ifindex(struct genl_info *info,
                                       struct cfg80211_registered_device **rdev,
                                       struct net_device **dev)
 {
+       struct nlattr **attrs = info->attrs;
        int ifindex;
 
        if (!attrs[NL80211_ATTR_IFINDEX])
                return -EINVAL;
 
        ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]);
-       *dev = dev_get_by_index(&init_net, ifindex);
+       *dev = dev_get_by_index(genl_info_net(info), ifindex);
        if (!*dev)
                return -ENODEV;
 
-       *rdev = cfg80211_get_dev_from_ifindex(ifindex);
+       *rdev = cfg80211_get_dev_from_ifindex(genl_info_net(info), ifindex);
        if (IS_ERR(*rdev)) {
                dev_put(*dev);
                return PTR_ERR(*rdev);
@@ -133,13 +137,16 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
        [NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG },
        [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 },
        [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 },
+       [NL80211_ATTR_PID] = { .type = NLA_U32 },
+       [NL80211_ATTR_4ADDR] = { .type = NLA_U8 },
+       [NL80211_ATTR_PMKID] = { .type = NLA_BINARY,
+                                .len = WLAN_PMKID_LEN },
 };
 
 /* policy for the attributes */
 static struct nla_policy
 nl80211_key_policy[NL80211_KEY_MAX + 1] __read_mostly = {
-       [NL80211_KEY_DATA] = { .type = NLA_BINARY,
-                                   .len = WLAN_MAX_KEY_LEN },
+       [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN },
        [NL80211_KEY_IDX] = { .type = NLA_U8 },
        [NL80211_KEY_CIPHER] = { .type = NLA_U32 },
        [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 },
@@ -147,6 +154,26 @@ nl80211_key_policy[NL80211_KEY_MAX + 1] __read_mostly = {
        [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG },
 };
 
+/* ifidx get helper */
+static int nl80211_get_ifidx(struct netlink_callback *cb)
+{
+       int res;
+
+       res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
+                         nl80211_fam.attrbuf, nl80211_fam.maxattr,
+                         nl80211_policy);
+       if (res)
+               return res;
+
+       if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
+               return -EINVAL;
+
+       res = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
+       if (!res)
+               return -EINVAL;
+       return res;
+}
+
 /* IE validation */
 static bool is_valid_ie_attr(const struct nlattr *attr)
 {
@@ -305,6 +332,83 @@ static int nl80211_parse_key(struct genl_info *info, struct key_parse *k)
        return 0;
 }
 
+static struct cfg80211_cached_keys *
+nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
+                      struct nlattr *keys)
+{
+       struct key_parse parse;
+       struct nlattr *key;
+       struct cfg80211_cached_keys *result;
+       int rem, err, def = 0;
+
+       result = kzalloc(sizeof(*result), GFP_KERNEL);
+       if (!result)
+               return ERR_PTR(-ENOMEM);
+
+       result->def = -1;
+       result->defmgmt = -1;
+
+       nla_for_each_nested(key, keys, rem) {
+               memset(&parse, 0, sizeof(parse));
+               parse.idx = -1;
+
+               err = nl80211_parse_key_new(key, &parse);
+               if (err)
+                       goto error;
+               err = -EINVAL;
+               if (!parse.p.key)
+                       goto error;
+               if (parse.idx < 0 || parse.idx > 4)
+                       goto error;
+               if (parse.def) {
+                       if (def)
+                               goto error;
+                       def = 1;
+                       result->def = parse.idx;
+               } else if (parse.defmgmt)
+                       goto error;
+               err = cfg80211_validate_key_settings(rdev, &parse.p,
+                                                    parse.idx, NULL);
+               if (err)
+                       goto error;
+               result->params[parse.idx].cipher = parse.p.cipher;
+               result->params[parse.idx].key_len = parse.p.key_len;
+               result->params[parse.idx].key = result->data[parse.idx];
+               memcpy(result->data[parse.idx], parse.p.key, parse.p.key_len);
+       }
+
+       return result;
+ error:
+       kfree(result);
+       return ERR_PTR(err);
+}
+
+static int nl80211_key_allowed(struct wireless_dev *wdev)
+{
+       ASSERT_WDEV_LOCK(wdev);
+
+       if (!netif_running(wdev->netdev))
+               return -ENETDOWN;
+
+       switch (wdev->iftype) {
+       case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_AP_VLAN:
+               break;
+       case NL80211_IFTYPE_ADHOC:
+               if (!wdev->current_bss)
+                       return -ENOLINK;
+               break;
+       case NL80211_IFTYPE_STATION:
+               if (wdev->sme_state != CFG80211_SME_CONNECTED)
+                       return -ENOLINK;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
                              struct cfg80211_registered_device *dev)
 {
@@ -327,6 +431,9 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
        NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx);
        NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
 
+       NLA_PUT_U32(msg, NL80211_ATTR_GENERATION,
+                   cfg80211_rdev_list_generation);
+
        NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
                   dev->wiphy.retry_short);
        NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
@@ -345,6 +452,9 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
                sizeof(u32) * dev->wiphy.n_cipher_suites,
                dev->wiphy.cipher_suites);
 
+       NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_PMKIDS,
+                  dev->wiphy.max_num_pmkids);
+
        nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES);
        if (!nl_modes)
                goto nla_put_failure;
@@ -456,6 +566,13 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
        CMD(deauth, DEAUTHENTICATE);
        CMD(disassoc, DISASSOCIATE);
        CMD(join_ibss, JOIN_IBSS);
+       CMD(set_pmksa, SET_PMKSA);
+       CMD(del_pmksa, DEL_PMKSA);
+       CMD(flush_pmksa, FLUSH_PMKSA);
+       if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
+               i++;
+               NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS);
+       }
 
 #undef CMD
 
@@ -486,6 +603,8 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
 
        mutex_lock(&cfg80211_mutex);
        list_for_each_entry(dev, &cfg80211_rdev_list, list) {
+               if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk)))
+                       continue;
                if (++idx <= start)
                        continue;
                if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid,
@@ -614,15 +733,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
 
        if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
                enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
-               struct ieee80211_channel *chan;
-               struct ieee80211_sta_ht_cap *ht_cap;
                u32 freq;
 
-               if (!rdev->ops->set_channel) {
-                       result = -EOPNOTSUPP;
-                       goto bad_res;
-               }
-
                result = -EINVAL;
 
                if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
@@ -636,38 +748,10 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
                }
 
                freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
-               chan = ieee80211_get_channel(&rdev->wiphy, freq);
 
-               /* Primary channel not allowed */
-               if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
-                       goto bad_res;
-
-               if (channel_type == NL80211_CHAN_HT40MINUS &&
-                   (chan->flags & IEEE80211_CHAN_NO_HT40MINUS))
-                       goto bad_res;
-               else if (channel_type == NL80211_CHAN_HT40PLUS &&
-                        (chan->flags & IEEE80211_CHAN_NO_HT40PLUS))
-                       goto bad_res;
-
-               /*
-                * At this point we know if that if HT40 was requested
-                * we are allowed to use it and the extension channel
-                * exists.
-                */
-
-               ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
-
-               /* no HT capabilities or intolerant */
-               if (channel_type != NL80211_CHAN_NO_HT) {
-                       if (!ht_cap->ht_supported)
-                               goto bad_res;
-                       if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
-                           (ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT))
-                               goto bad_res;
-               }
-
-               result = rdev->ops->set_channel(&rdev->wiphy, chan,
-                                               channel_type);
+               mutex_lock(&rdev->devlist_mtx);
+               result = rdev_set_freq(rdev, NULL, freq, channel_type);
+               mutex_unlock(&rdev->devlist_mtx);
                if (result)
                        goto bad_res;
        }
@@ -773,6 +857,11 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
        NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
        NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name);
        NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, dev->ieee80211_ptr->iftype);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_GENERATION,
+                   rdev->devlist_generation ^
+                       (cfg80211_rdev_list_generation << 2));
+
        return genlmsg_end(msg, hdr);
 
  nla_put_failure:
@@ -786,32 +875,34 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *
        int if_idx = 0;
        int wp_start = cb->args[0];
        int if_start = cb->args[1];
-       struct cfg80211_registered_device *dev;
+       struct cfg80211_registered_device *rdev;
        struct wireless_dev *wdev;
 
        mutex_lock(&cfg80211_mutex);
-       list_for_each_entry(dev, &cfg80211_rdev_list, list) {
+       list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
+               if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk)))
+                       continue;
                if (wp_idx < wp_start) {
                        wp_idx++;
                        continue;
                }
                if_idx = 0;
 
-               mutex_lock(&dev->devlist_mtx);
-               list_for_each_entry(wdev, &dev->netdev_list, list) {
+               mutex_lock(&rdev->devlist_mtx);
+               list_for_each_entry(wdev, &rdev->netdev_list, list) {
                        if (if_idx < if_start) {
                                if_idx++;
                                continue;
                        }
                        if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid,
                                               cb->nlh->nlmsg_seq, NLM_F_MULTI,
-                                              dev, wdev->netdev) < 0) {
-                               mutex_unlock(&dev->devlist_mtx);
+                                              rdev, wdev->netdev) < 0) {
+                               mutex_unlock(&rdev->devlist_mtx);
                                goto out;
                        }
                        if_idx++;
                }
-               mutex_unlock(&dev->devlist_mtx);
+               mutex_unlock(&rdev->devlist_mtx);
 
                wp_idx++;
        }
@@ -831,7 +922,7 @@ static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
        struct net_device *netdev;
        int err;
 
-       err = get_rdev_dev_by_info_ifindex(info->attrs, &dev, &netdev);
+       err = get_rdev_dev_by_info_ifindex(info, &dev, &netdev);
        if (err)
                return err;
 
@@ -885,6 +976,32 @@ static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags)
        return 0;
 }
 
+static int nl80211_valid_4addr(struct cfg80211_registered_device *rdev,
+                              struct net_device *netdev, u8 use_4addr,
+                              enum nl80211_iftype iftype)
+{
+       if (!use_4addr) {
+               if (netdev && netdev->br_port)
+                       return -EBUSY;
+               return 0;
+       }
+
+       switch (iftype) {
+       case NL80211_IFTYPE_AP_VLAN:
+               if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_AP)
+                       return 0;
+               break;
+       case NL80211_IFTYPE_STATION:
+               if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_STATION)
+                       return 0;
+               break;
+       default:
+               break;
+       }
+
+       return -EOPNOTSUPP;
+}
+
 static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev;
@@ -899,7 +1016,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
+       err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
        if (err)
                goto unlock_rtnl;
 
@@ -915,12 +1032,6 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
                }
        }
 
-       if (!rdev->ops->change_virtual_intf ||
-           !(rdev->wiphy.interface_modes & (1 << ntype))) {
-               err = -EOPNOTSUPP;
-               goto unlock;
-       }
-
        if (info->attrs[NL80211_ATTR_MESH_ID]) {
                if (ntype != NL80211_IFTYPE_MESH_POINT) {
                        err = -EINVAL;
@@ -931,6 +1042,16 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
                change = true;
        }
 
+       if (info->attrs[NL80211_ATTR_4ADDR]) {
+               params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
+               change = true;
+               err = nl80211_valid_4addr(rdev, dev, params.use_4addr, ntype);
+               if (err)
+                       goto unlock;
+       } else {
+               params.use_4addr = -1;
+       }
+
        if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) {
                if (ntype != NL80211_IFTYPE_MONITOR) {
                        err = -EINVAL;
@@ -946,17 +1067,12 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
        }
 
        if (change)
-               err = rdev->ops->change_virtual_intf(&rdev->wiphy, dev,
-                                                   ntype, flags, &params);
+               err = cfg80211_change_iface(rdev, dev, ntype, flags, &params);
        else
                err = 0;
 
-       WARN_ON(!err && dev->ieee80211_ptr->iftype != ntype);
-
-       if (!err && (ntype != otype)) {
-               if (otype == NL80211_IFTYPE_ADHOC)
-                       cfg80211_clear_ibss(dev, false);
-       }
+       if (!err && params.use_4addr != -1)
+               dev->ieee80211_ptr->use_4addr = params.use_4addr;
 
  unlock:
        dev_put(dev);
@@ -1005,6 +1121,13 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
                params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
        }
 
+       if (info->attrs[NL80211_ATTR_4ADDR]) {
+               params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
+               err = nl80211_valid_4addr(rdev, NULL, params.use_4addr, type);
+               if (err)
+                       goto unlock;
+       }
+
        err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
                                  info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
                                  &flags);
@@ -1022,26 +1145,25 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
 static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev;
-       int ifindex, err;
+       int err;
        struct net_device *dev;
 
        rtnl_lock();
 
-       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
+       err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
        if (err)
                goto unlock_rtnl;
-       ifindex = dev->ifindex;
-       dev_put(dev);
 
        if (!rdev->ops->del_virtual_intf) {
                err = -EOPNOTSUPP;
                goto out;
        }
 
-       err = rdev->ops->del_virtual_intf(&rdev->wiphy, ifindex);
+       err = rdev->ops->del_virtual_intf(&rdev->wiphy, dev);
 
  out:
        cfg80211_unlock_rdev(rdev);
+       dev_put(dev);
  unlock_rtnl:
        rtnl_unlock();
        return err;
@@ -1119,7 +1241,7 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
+       err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
        if (err)
                goto unlock_rtnl;
 
@@ -1198,7 +1320,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
+       err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
        if (err)
                goto unlock_rtnl;
 
@@ -1212,8 +1334,12 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       err = func(&rdev->wiphy, dev, key.idx);
-#ifdef CONFIG_WIRELESS_EXT
+       wdev_lock(dev->ieee80211_ptr);
+       err = nl80211_key_allowed(dev->ieee80211_ptr);
+       if (!err)
+               err = func(&rdev->wiphy, dev, key.idx);
+
+#ifdef CONFIG_CFG80211_WEXT
        if (!err) {
                if (func == rdev->ops->set_default_key)
                        dev->ieee80211_ptr->wext.default_key = key.idx;
@@ -1221,6 +1347,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
                        dev->ieee80211_ptr->wext.default_mgmt_key = key.idx;
        }
 #endif
+       wdev_unlock(dev->ieee80211_ptr);
 
  out:
        cfg80211_unlock_rdev(rdev);
@@ -1235,7 +1362,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
 static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev;
-       int err, i;
+       int err;
        struct net_device *dev;
        struct key_parse key;
        u8 *mac_addr = NULL;
@@ -1250,29 +1377,28 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
        if (info->attrs[NL80211_ATTR_MAC])
                mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
 
-       if (cfg80211_validate_key_settings(&key.p, key.idx, mac_addr))
-               return -EINVAL;
-
        rtnl_lock();
 
-       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
+       err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
        if (err)
                goto unlock_rtnl;
 
-       for (i = 0; i < rdev->wiphy.n_cipher_suites; i++)
-               if (key.p.cipher == rdev->wiphy.cipher_suites[i])
-                       break;
-       if (i == rdev->wiphy.n_cipher_suites) {
-               err = -EINVAL;
+       if (!rdev->ops->add_key) {
+               err = -EOPNOTSUPP;
                goto out;
        }
 
-       if (!rdev->ops->add_key) {
-               err = -EOPNOTSUPP;
+       if (cfg80211_validate_key_settings(rdev, &key.p, key.idx, mac_addr)) {
+               err = -EINVAL;
                goto out;
        }
 
-       err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx, mac_addr, &key.p);
+       wdev_lock(dev->ieee80211_ptr);
+       err = nl80211_key_allowed(dev->ieee80211_ptr);
+       if (!err)
+               err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx,
+                                        mac_addr, &key.p);
+       wdev_unlock(dev->ieee80211_ptr);
 
  out:
        cfg80211_unlock_rdev(rdev);
@@ -1300,7 +1426,7 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
+       err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
        if (err)
                goto unlock_rtnl;
 
@@ -1309,9 +1435,12 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx, mac_addr);
+       wdev_lock(dev->ieee80211_ptr);
+       err = nl80211_key_allowed(dev->ieee80211_ptr);
+       if (!err)
+               err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx, mac_addr);
 
-#ifdef CONFIG_WIRELESS_EXT
+#ifdef CONFIG_CFG80211_WEXT
        if (!err) {
                if (key.idx == dev->ieee80211_ptr->wext.default_key)
                        dev->ieee80211_ptr->wext.default_key = -1;
@@ -1319,6 +1448,7 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
                        dev->ieee80211_ptr->wext.default_mgmt_key = -1;
        }
 #endif
+       wdev_unlock(dev->ieee80211_ptr);
 
  out:
        cfg80211_unlock_rdev(rdev);
@@ -1345,7 +1475,7 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
+       err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
        if (err)
                goto unlock_rtnl;
 
@@ -1432,7 +1562,7 @@ static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
+       err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
        if (err)
                goto unlock_rtnl;
 
@@ -1555,6 +1685,8 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
        NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
 
+       NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, sinfo->generation);
+
        sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO);
        if (!sinfoattr)
                goto nla_put_failure;
@@ -1625,30 +1757,20 @@ static int nl80211_dump_station(struct sk_buff *skb,
        int sta_idx = cb->args[1];
        int err;
 
-       if (!ifidx) {
-               err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
-                                 nl80211_fam.attrbuf, nl80211_fam.maxattr,
-                                 nl80211_policy);
-               if (err)
-                       return err;
-
-               if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
-                       return -EINVAL;
-
-               ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
-               if (!ifidx)
-                       return -EINVAL;
-       }
+       if (!ifidx)
+               ifidx = nl80211_get_ifidx(cb);
+       if (ifidx < 0)
+               return ifidx;
 
        rtnl_lock();
 
-       netdev = __dev_get_by_index(&init_net, ifidx);
+       netdev = __dev_get_by_index(sock_net(skb->sk), ifidx);
        if (!netdev) {
                err = -ENODEV;
                goto out_rtnl;
        }
 
-       dev = cfg80211_get_dev_from_ifindex(ifidx);
+       dev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
        if (IS_ERR(dev)) {
                err = PTR_ERR(dev);
                goto out_rtnl;
@@ -1707,7 +1829,7 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
+       err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
        if (err)
                goto out_rtnl;
 
@@ -1743,22 +1865,26 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
 }
 
 /*
- * Get vlan interface making sure it is on the right wiphy.
+ * Get vlan interface making sure it is running and on the right wiphy.
  */
-static int get_vlan(struct nlattr *vlanattr,
+static int get_vlan(struct genl_info *info,
                    struct cfg80211_registered_device *rdev,
                    struct net_device **vlan)
 {
+       struct nlattr *vlanattr = info->attrs[NL80211_ATTR_STA_VLAN];
        *vlan = NULL;
 
        if (vlanattr) {
-               *vlan = dev_get_by_index(&init_net, nla_get_u32(vlanattr));
+               *vlan = dev_get_by_index(genl_info_net(info),
+                                        nla_get_u32(vlanattr));
                if (!*vlan)
                        return -ENODEV;
                if (!(*vlan)->ieee80211_ptr)
                        return -EINVAL;
                if ((*vlan)->ieee80211_ptr->wiphy != &rdev->wiphy)
                        return -EINVAL;
+               if (!netif_running(*vlan))
+                       return -ENETDOWN;
        }
        return 0;
 }
@@ -1807,11 +1933,11 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
+       err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
        if (err)
                goto out_rtnl;
 
-       err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], rdev, &params.vlan);
+       err = get_vlan(info, rdev, &params.vlan);
        if (err)
                goto out;
 
@@ -1920,11 +2046,11 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
+       err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
        if (err)
                goto out_rtnl;
 
-       err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], rdev, &params.vlan);
+       err = get_vlan(info, rdev, &params.vlan);
        if (err)
                goto out;
 
@@ -1995,7 +2121,7 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
+       err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
        if (err)
                goto out_rtnl;
 
@@ -2038,15 +2164,17 @@ static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq,
        NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst);
        NLA_PUT(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop);
 
+       NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, pinfo->generation);
+
        pinfoattr = nla_nest_start(msg, NL80211_ATTR_MPATH_INFO);
        if (!pinfoattr)
                goto nla_put_failure;
        if (pinfo->filled & MPATH_INFO_FRAME_QLEN)
                NLA_PUT_U32(msg, NL80211_MPATH_INFO_FRAME_QLEN,
                            pinfo->frame_qlen);
-       if (pinfo->filled & MPATH_INFO_DSN)
-               NLA_PUT_U32(msg, NL80211_MPATH_INFO_DSN,
-                           pinfo->dsn);
+       if (pinfo->filled & MPATH_INFO_SN)
+               NLA_PUT_U32(msg, NL80211_MPATH_INFO_SN,
+                           pinfo->sn);
        if (pinfo->filled & MPATH_INFO_METRIC)
                NLA_PUT_U32(msg, NL80211_MPATH_INFO_METRIC,
                            pinfo->metric);
@@ -2084,30 +2212,20 @@ static int nl80211_dump_mpath(struct sk_buff *skb,
        int path_idx = cb->args[1];
        int err;
 
-       if (!ifidx) {
-               err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
-                                 nl80211_fam.attrbuf, nl80211_fam.maxattr,
-                                 nl80211_policy);
-               if (err)
-                       return err;
-
-               if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
-                       return -EINVAL;
-
-               ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
-               if (!ifidx)
-                       return -EINVAL;
-       }
+       if (!ifidx)
+               ifidx = nl80211_get_ifidx(cb);
+       if (ifidx < 0)
+               return ifidx;
 
        rtnl_lock();
 
-       netdev = __dev_get_by_index(&init_net, ifidx);
+       netdev = __dev_get_by_index(sock_net(skb->sk), ifidx);
        if (!netdev) {
                err = -ENODEV;
                goto out_rtnl;
        }
 
-       dev = cfg80211_get_dev_from_ifindex(ifidx);
+       dev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
        if (IS_ERR(dev)) {
                err = PTR_ERR(dev);
                goto out_rtnl;
@@ -2120,7 +2238,7 @@ static int nl80211_dump_mpath(struct sk_buff *skb,
 
        if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
                err = -EOPNOTSUPP;
-               goto out;
+               goto out_err;
        }
 
        while (1) {
@@ -2171,7 +2289,7 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
+       err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
        if (err)
                goto out_rtnl;
 
@@ -2230,7 +2348,7 @@ static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
+       err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
        if (err)
                goto out_rtnl;
 
@@ -2278,7 +2396,7 @@ static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
+       err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
        if (err)
                goto out_rtnl;
 
@@ -2320,7 +2438,7 @@ static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
+       err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
        if (err)
                goto out_rtnl;
 
@@ -2371,7 +2489,7 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
+       err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
        if (err)
                goto out_rtnl;
 
@@ -2490,7 +2608,7 @@ static int nl80211_get_mesh_params(struct sk_buff *skb,
        rtnl_lock();
 
        /* Look up our device */
-       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
+       err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
        if (err)
                goto out_rtnl;
 
@@ -2544,6 +2662,8 @@ static int nl80211_get_mesh_params(struct sk_buff *skb,
                        cur_params.dot11MeshHWMPpreqMinInterval);
        NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
                        cur_params.dot11MeshHWMPnetDiameterTraversalTime);
+       NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_ROOTMODE,
+                       cur_params.dot11MeshHWMPRootMode);
        nla_nest_end(msg, pinfoattr);
        genlmsg_end(msg, hdr);
        err = genlmsg_reply(msg, info);
@@ -2607,7 +2727,7 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
+       err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
        if (err)
                goto out_rtnl;
 
@@ -2654,6 +2774,10 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info)
                        dot11MeshHWMPnetDiameterTraversalTime,
                        mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
                        nla_get_u16);
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
+                       dot11MeshHWMPRootMode, mask,
+                       NL80211_MESHCONF_HWMP_ROOTMODE,
+                       nla_get_u8);
 
        /* Apply changes */
        err = rdev->ops->set_mesh_params(&rdev->wiphy, dev, &cfg, mask);
@@ -2863,7 +2987,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
+       err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
        if (err)
                goto out_rtnl;
 
@@ -2927,10 +3051,8 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       request->channels = (void *)((char *)request + sizeof(*request));
-       request->n_channels = n_channels;
        if (n_ssids)
-               request->ssids = (void *)(request->channels + n_channels);
+               request->ssids = (void *)&request->channels[n_channels];
        request->n_ssids = n_ssids;
        if (ie_len) {
                if (request->ssids)
@@ -2939,32 +3061,53 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
                        request->ie = (void *)(request->channels + n_channels);
        }
 
+       i = 0;
        if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
                /* user specified, bail out if channel not found */
-               request->n_channels = n_channels;
-               i = 0;
                nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) {
-                       request->channels[i] = ieee80211_get_channel(wiphy, nla_get_u32(attr));
-                       if (!request->channels[i]) {
+                       struct ieee80211_channel *chan;
+
+                       chan = ieee80211_get_channel(wiphy, nla_get_u32(attr));
+
+                       if (!chan) {
                                err = -EINVAL;
                                goto out_free;
                        }
+
+                       /* ignore disabled channels */
+                       if (chan->flags & IEEE80211_CHAN_DISABLED)
+                               continue;
+
+                       request->channels[i] = chan;
                        i++;
                }
        } else {
                /* all channels */
-               i = 0;
                for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
                        int j;
                        if (!wiphy->bands[band])
                                continue;
                        for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
-                               request->channels[i] = &wiphy->bands[band]->channels[j];
+                               struct ieee80211_channel *chan;
+
+                               chan = &wiphy->bands[band]->channels[j];
+
+                               if (chan->flags & IEEE80211_CHAN_DISABLED)
+                                       continue;
+
+                               request->channels[i] = chan;
                                i++;
                        }
                }
        }
 
+       if (!i) {
+               err = -EINVAL;
+               goto out_free;
+       }
+
+       request->n_channels = i;
+
        i = 0;
        if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
                nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) {
@@ -2985,14 +3128,16 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
                       request->ie_len);
        }
 
-       request->ifidx = dev->ifindex;
+       request->dev = dev;
        request->wiphy = &rdev->wiphy;
 
        rdev->scan_req = request;
        err = rdev->ops->scan(&rdev->wiphy, dev, request);
 
-       if (!err)
+       if (!err) {
                nl80211_send_scan_start(rdev, dev);
+               dev_hold(dev);
+       }
 
  out_free:
        if (err) {
@@ -3010,20 +3155,23 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
 
 static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
                            struct cfg80211_registered_device *rdev,
-                           struct net_device *dev,
-                           struct cfg80211_bss *res)
+                           struct wireless_dev *wdev,
+                           struct cfg80211_internal_bss *intbss)
 {
+       struct cfg80211_bss *res = &intbss->pub;
        void *hdr;
        struct nlattr *bss;
+       int i;
+
+       ASSERT_WDEV_LOCK(wdev);
 
        hdr = nl80211hdr_put(msg, pid, seq, flags,
                             NL80211_CMD_NEW_SCAN_RESULTS);
        if (!hdr)
                return -1;
 
-       NLA_PUT_U32(msg, NL80211_ATTR_SCAN_GENERATION,
-                   rdev->bss_generation);
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
+       NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, rdev->bss_generation);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex);
 
        bss = nla_nest_start(msg, NL80211_ATTR_BSS);
        if (!bss)
@@ -3040,6 +3188,8 @@ static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
                NLA_PUT_U16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval);
        NLA_PUT_U16(msg, NL80211_BSS_CAPABILITY, res->capability);
        NLA_PUT_U32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq);
+       NLA_PUT_U32(msg, NL80211_BSS_SEEN_MS_AGO,
+               jiffies_to_msecs(jiffies - intbss->ts));
 
        switch (rdev->wiphy.signal_type) {
        case CFG80211_SIGNAL_TYPE_MBM:
@@ -3052,6 +3202,28 @@ static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
                break;
        }
 
+       switch (wdev->iftype) {
+       case NL80211_IFTYPE_STATION:
+               if (intbss == wdev->current_bss)
+                       NLA_PUT_U32(msg, NL80211_BSS_STATUS,
+                                   NL80211_BSS_STATUS_ASSOCIATED);
+               else for (i = 0; i < MAX_AUTH_BSSES; i++) {
+                       if (intbss != wdev->auth_bsses[i])
+                               continue;
+                       NLA_PUT_U32(msg, NL80211_BSS_STATUS,
+                                   NL80211_BSS_STATUS_AUTHENTICATED);
+                       break;
+               }
+               break;
+       case NL80211_IFTYPE_ADHOC:
+               if (intbss == wdev->current_bss)
+                       NLA_PUT_U32(msg, NL80211_BSS_STATUS,
+                                   NL80211_BSS_STATUS_IBSS_JOINED);
+               break;
+       default:
+               break;
+       }
+
        nla_nest_end(msg, bss);
 
        return genlmsg_end(msg, hdr);
@@ -3064,66 +3236,161 @@ static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
 static int nl80211_dump_scan(struct sk_buff *skb,
                             struct netlink_callback *cb)
 {
-       struct cfg80211_registered_device *dev;
-       struct net_device *netdev;
+       struct cfg80211_registered_device *rdev;
+       struct net_device *dev;
        struct cfg80211_internal_bss *scan;
+       struct wireless_dev *wdev;
        int ifidx = cb->args[0];
        int start = cb->args[1], idx = 0;
        int err;
 
-       if (!ifidx) {
-               err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
-                                 nl80211_fam.attrbuf, nl80211_fam.maxattr,
-                                 nl80211_policy);
-               if (err)
-                       return err;
-
-               if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
-                       return -EINVAL;
-
-               ifidx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
-               if (!ifidx)
-                       return -EINVAL;
-               cb->args[0] = ifidx;
-       }
+       if (!ifidx)
+               ifidx = nl80211_get_ifidx(cb);
+       if (ifidx < 0)
+               return ifidx;
+       cb->args[0] = ifidx;
 
-       netdev = dev_get_by_index(&init_net, ifidx);
-       if (!netdev)
+       dev = dev_get_by_index(sock_net(skb->sk), ifidx);
+       if (!dev)
                return -ENODEV;
 
-       dev = cfg80211_get_dev_from_ifindex(ifidx);
-       if (IS_ERR(dev)) {
-               err = PTR_ERR(dev);
+       rdev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
+       if (IS_ERR(rdev)) {
+               err = PTR_ERR(rdev);
                goto out_put_netdev;
        }
 
-       spin_lock_bh(&dev->bss_lock);
-       cfg80211_bss_expire(dev);
+       wdev = dev->ieee80211_ptr;
+
+       wdev_lock(wdev);
+       spin_lock_bh(&rdev->bss_lock);
+       cfg80211_bss_expire(rdev);
 
-       list_for_each_entry(scan, &dev->bss_list, list) {
+       list_for_each_entry(scan, &rdev->bss_list, list) {
                if (++idx <= start)
                        continue;
                if (nl80211_send_bss(skb,
                                NETLINK_CB(cb->skb).pid,
                                cb->nlh->nlmsg_seq, NLM_F_MULTI,
-                               dev, netdev, &scan->pub) < 0) {
+                               rdev, wdev, scan) < 0) {
                        idx--;
                        goto out;
                }
        }
 
  out:
-       spin_unlock_bh(&dev->bss_lock);
+       spin_unlock_bh(&rdev->bss_lock);
+       wdev_unlock(wdev);
 
        cb->args[1] = idx;
        err = skb->len;
-       cfg80211_unlock_rdev(dev);
+       cfg80211_unlock_rdev(rdev);
  out_put_netdev:
-       dev_put(netdev);
+       dev_put(dev);
 
        return err;
 }
 
+static int nl80211_send_survey(struct sk_buff *msg, u32 pid, u32 seq,
+                               int flags, struct net_device *dev,
+                               struct survey_info *survey)
+{
+       void *hdr;
+       struct nlattr *infoattr;
+
+       /* Survey without a channel doesn't make sense */
+       if (!survey->channel)
+               return -EINVAL;
+
+       hdr = nl80211hdr_put(msg, pid, seq, flags,
+                            NL80211_CMD_NEW_SURVEY_RESULTS);
+       if (!hdr)
+               return -ENOMEM;
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
+
+       infoattr = nla_nest_start(msg, NL80211_ATTR_SURVEY_INFO);
+       if (!infoattr)
+               goto nla_put_failure;
+
+       NLA_PUT_U32(msg, NL80211_SURVEY_INFO_FREQUENCY,
+                   survey->channel->center_freq);
+       if (survey->filled & SURVEY_INFO_NOISE_DBM)
+               NLA_PUT_U8(msg, NL80211_SURVEY_INFO_NOISE,
+                           survey->noise);
+
+       nla_nest_end(msg, infoattr);
+
+       return genlmsg_end(msg, hdr);
+
+ nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       return -EMSGSIZE;
+}
+
+static int nl80211_dump_survey(struct sk_buff *skb,
+                       struct netlink_callback *cb)
+{
+       struct survey_info survey;
+       struct cfg80211_registered_device *dev;
+       struct net_device *netdev;
+       int ifidx = cb->args[0];
+       int survey_idx = cb->args[1];
+       int res;
+
+       if (!ifidx)
+               ifidx = nl80211_get_ifidx(cb);
+       if (ifidx < 0)
+               return ifidx;
+       cb->args[0] = ifidx;
+
+       rtnl_lock();
+
+       netdev = __dev_get_by_index(sock_net(skb->sk), ifidx);
+       if (!netdev) {
+               res = -ENODEV;
+               goto out_rtnl;
+       }
+
+       dev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
+       if (IS_ERR(dev)) {
+               res = PTR_ERR(dev);
+               goto out_rtnl;
+       }
+
+       if (!dev->ops->dump_survey) {
+               res = -EOPNOTSUPP;
+               goto out_err;
+       }
+
+       while (1) {
+               res = dev->ops->dump_survey(&dev->wiphy, netdev, survey_idx,
+                                           &survey);
+               if (res == -ENOENT)
+                       break;
+               if (res)
+                       goto out_err;
+
+               if (nl80211_send_survey(skb,
+                               NETLINK_CB(cb->skb).pid,
+                               cb->nlh->nlmsg_seq, NLM_F_MULTI,
+                               netdev,
+                               &survey) < 0)
+                       goto out;
+               survey_idx++;
+       }
+
+ out:
+       cb->args[1] = survey_idx;
+       res = skb->len;
+ out_err:
+       cfg80211_unlock_rdev(dev);
+ out_rtnl:
+       rtnl_unlock();
+
+       return res;
+}
+
 static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type)
 {
        return auth_type <= NL80211_AUTHTYPE_MAX;
@@ -3159,6 +3426,7 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
        const u8 *bssid, *ssid, *ie = NULL;
        int err, ssid_len, ie_len = 0;
        enum nl80211_auth_type auth_type;
+       struct key_parse key;
 
        if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
                return -EINVAL;
@@ -3175,9 +3443,28 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
        if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
                return -EINVAL;
 
+       err = nl80211_parse_key(info, &key);
+       if (err)
+               return err;
+
+       if (key.idx >= 0) {
+               if (!key.p.key || !key.p.key_len)
+                       return -EINVAL;
+               if ((key.p.cipher != WLAN_CIPHER_SUITE_WEP40 ||
+                    key.p.key_len != WLAN_KEY_LEN_WEP40) &&
+                   (key.p.cipher != WLAN_CIPHER_SUITE_WEP104 ||
+                    key.p.key_len != WLAN_KEY_LEN_WEP104))
+                       return -EINVAL;
+               if (key.idx > 4)
+                       return -EINVAL;
+       } else {
+               key.p.key_len = 0;
+               key.p.key = NULL;
+       }
+
        rtnl_lock();
 
-       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
+       err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
        if (err)
                goto unlock_rtnl;
 
@@ -3219,7 +3506,8 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
        }
 
        err = cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
-                                ssid, ssid_len, ie, ie_len);
+                                ssid, ssid_len, ie, ie_len,
+                                key.p.key, key.p.key_len, key.idx);
 
 out:
        cfg80211_unlock_rdev(rdev);
@@ -3233,6 +3521,8 @@ static int nl80211_crypto_settings(struct genl_info *info,
                                   struct cfg80211_crypto_settings *settings,
                                   int cipher_limit)
 {
+       memset(settings, 0, sizeof(*settings));
+
        settings->control_port = info->attrs[NL80211_ATTR_CONTROL_PORT];
 
        if (info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]) {
@@ -3297,7 +3587,7 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
        struct cfg80211_registered_device *rdev;
        struct net_device *dev;
        struct cfg80211_crypto_settings crypto;
-       struct ieee80211_channel *chan;
+       struct ieee80211_channel *chan, *fixedchan;
        const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL;
        int err, ssid_len, ie_len = 0;
        bool use_mfp = false;
@@ -3312,7 +3602,7 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
+       err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
        if (err)
                goto unlock_rtnl;
 
@@ -3340,6 +3630,15 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
+       mutex_lock(&rdev->devlist_mtx);
+       fixedchan = rdev_fixed_channel(rdev, NULL);
+       if (fixedchan && chan != fixedchan) {
+               err = -EBUSY;
+               mutex_unlock(&rdev->devlist_mtx);
+               goto out;
+       }
+       mutex_unlock(&rdev->devlist_mtx);
+
        ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
        ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
 
@@ -3395,7 +3694,7 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
+       err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
        if (err)
                goto unlock_rtnl;
 
@@ -3457,7 +3756,7 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
+       err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
        if (err)
                goto unlock_rtnl;
 
@@ -3506,6 +3805,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
        struct net_device *dev;
        struct cfg80211_ibss_params ibss;
        struct wiphy *wiphy;
+       struct cfg80211_cached_keys *connkeys = NULL;
        int err;
 
        memset(&ibss, 0, sizeof(ibss));
@@ -3529,7 +3829,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
+       err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
        if (err)
                goto unlock_rtnl;
 
@@ -3570,13 +3870,26 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
        }
 
        ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
+       ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
+
+       if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) {
+               connkeys = nl80211_parse_connkeys(rdev,
+                                       info->attrs[NL80211_ATTR_KEYS]);
+               if (IS_ERR(connkeys)) {
+                       err = PTR_ERR(connkeys);
+                       connkeys = NULL;
+                       goto out;
+               }
+       }
 
-       err = cfg80211_join_ibss(rdev, dev, &ibss);
+       err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
 
 out:
        cfg80211_unlock_rdev(rdev);
        dev_put(dev);
 unlock_rtnl:
+       if (err)
+               kfree(connkeys);
        rtnl_unlock();
        return err;
 }
@@ -3589,7 +3902,7 @@ static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
+       err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
        if (err)
                goto unlock_rtnl;
 
@@ -3746,6 +4059,7 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
        struct net_device *dev;
        struct cfg80211_connect_params connect;
        struct wiphy *wiphy;
+       struct cfg80211_cached_keys *connkeys = NULL;
        int err;
 
        memset(&connect, 0, sizeof(connect));
@@ -3773,7 +4087,7 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
                return err;
        rtnl_lock();
 
-       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
+       err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
        if (err)
                goto unlock_rtnl;
 
@@ -3810,12 +4124,24 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
                }
        }
 
-       err = cfg80211_connect(rdev, dev, &connect);
+       if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) {
+               connkeys = nl80211_parse_connkeys(rdev,
+                                       info->attrs[NL80211_ATTR_KEYS]);
+               if (IS_ERR(connkeys)) {
+                       err = PTR_ERR(connkeys);
+                       connkeys = NULL;
+                       goto out;
+               }
+       }
+
+       err = cfg80211_connect(rdev, dev, &connect, connkeys);
 
 out:
        cfg80211_unlock_rdev(rdev);
        dev_put(dev);
 unlock_rtnl:
+       if (err)
+               kfree(connkeys);
        rtnl_unlock();
        return err;
 }
@@ -3837,7 +4163,7 @@ static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info)
 
        rtnl_lock();
 
-       err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev);
+       err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
        if (err)
                goto unlock_rtnl;
 
@@ -3861,6 +4187,141 @@ unlock_rtnl:
        return err;
 }
 
+static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev;
+       struct net *net;
+       int err;
+       u32 pid;
+
+       if (!info->attrs[NL80211_ATTR_PID])
+               return -EINVAL;
+
+       pid = nla_get_u32(info->attrs[NL80211_ATTR_PID]);
+
+       rtnl_lock();
+
+       rdev = cfg80211_get_dev_from_info(info);
+       if (IS_ERR(rdev)) {
+               err = PTR_ERR(rdev);
+               goto out_rtnl;
+       }
+
+       net = get_net_ns_by_pid(pid);
+       if (IS_ERR(net)) {
+               err = PTR_ERR(net);
+               goto out;
+       }
+
+       err = 0;
+
+       /* check if anything to do */
+       if (net_eq(wiphy_net(&rdev->wiphy), net))
+               goto out_put_net;
+
+       err = cfg80211_switch_netns(rdev, net);
+ out_put_net:
+       put_net(net);
+ out:
+       cfg80211_unlock_rdev(rdev);
+ out_rtnl:
+       rtnl_unlock();
+       return err;
+}
+
+static int nl80211_setdel_pmksa(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev;
+       int (*rdev_ops)(struct wiphy *wiphy, struct net_device *dev,
+                       struct cfg80211_pmksa *pmksa) = NULL;
+       int err;
+       struct net_device *dev;
+       struct cfg80211_pmksa pmksa;
+
+       memset(&pmksa, 0, sizeof(struct cfg80211_pmksa));
+
+       if (!info->attrs[NL80211_ATTR_MAC])
+               return -EINVAL;
+
+       if (!info->attrs[NL80211_ATTR_PMKID])
+               return -EINVAL;
+
+       rtnl_lock();
+
+       err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
+       if (err)
+               goto out_rtnl;
+
+       pmksa.pmkid = nla_data(info->attrs[NL80211_ATTR_PMKID]);
+       pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       switch (info->genlhdr->cmd) {
+       case NL80211_CMD_SET_PMKSA:
+               rdev_ops = rdev->ops->set_pmksa;
+               break;
+       case NL80211_CMD_DEL_PMKSA:
+               rdev_ops = rdev->ops->del_pmksa;
+               break;
+       default:
+               WARN_ON(1);
+               break;
+       }
+
+       if (!rdev_ops) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       err = rdev_ops(&rdev->wiphy, dev, &pmksa);
+
+ out:
+       cfg80211_unlock_rdev(rdev);
+       dev_put(dev);
+ out_rtnl:
+       rtnl_unlock();
+
+       return err;
+}
+
+static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *rdev;
+       int err;
+       struct net_device *dev;
+
+       rtnl_lock();
+
+       err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
+       if (err)
+               goto out_rtnl;
+
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       if (!rdev->ops->flush_pmksa) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       err = rdev->ops->flush_pmksa(&rdev->wiphy, dev);
+
+ out:
+       cfg80211_unlock_rdev(rdev);
+       dev_put(dev);
+ out_rtnl:
+       rtnl_unlock();
+
+       return err;
+
+}
+
 static struct genl_ops nl80211_ops[] = {
        {
                .cmd = NL80211_CMD_GET_WIPHY,
@@ -4094,6 +4555,36 @@ static struct genl_ops nl80211_ops[] = {
                .policy = nl80211_policy,
                .flags = GENL_ADMIN_PERM,
        },
+       {
+               .cmd = NL80211_CMD_SET_WIPHY_NETNS,
+               .doit = nl80211_wiphy_netns,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+       },
+       {
+               .cmd = NL80211_CMD_GET_SURVEY,
+               .policy = nl80211_policy,
+               .dumpit = nl80211_dump_survey,
+       },
+       {
+               .cmd = NL80211_CMD_SET_PMKSA,
+               .doit = nl80211_setdel_pmksa,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+       },
+       {
+               .cmd = NL80211_CMD_DEL_PMKSA,
+               .doit = nl80211_setdel_pmksa,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+       },
+       {
+               .cmd = NL80211_CMD_FLUSH_PMKSA,
+               .doit = nl80211_flush_pmksa,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+       },
+
 };
 static struct genl_multicast_group nl80211_mlme_mcgrp = {
        .name = "mlme",
@@ -4125,7 +4616,8 @@ void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
                return;
        }
 
-       genlmsg_multicast(msg, 0, nl80211_config_mcgrp.id, GFP_KERNEL);
+       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+                               nl80211_config_mcgrp.id, GFP_KERNEL);
 }
 
 static int nl80211_add_scan_req(struct sk_buff *msg,
@@ -4202,7 +4694,8 @@ void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
                return;
        }
 
-       genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL);
+       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+                               nl80211_scan_mcgrp.id, GFP_KERNEL);
 }
 
 void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
@@ -4220,7 +4713,8 @@ void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
                return;
        }
 
-       genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL);
+       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+                               nl80211_scan_mcgrp.id, GFP_KERNEL);
 }
 
 void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
@@ -4238,7 +4732,8 @@ void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
                return;
        }
 
-       genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL);
+       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+                               nl80211_scan_mcgrp.id, GFP_KERNEL);
 }
 
 /*
@@ -4287,7 +4782,10 @@ void nl80211_send_reg_change_event(struct regulatory_request *request)
                return;
        }
 
-       genlmsg_multicast(msg, 0, nl80211_regulatory_mcgrp.id, GFP_KERNEL);
+       rcu_read_lock();
+       genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
+                               GFP_ATOMIC);
+       rcu_read_unlock();
 
        return;
 
@@ -4323,7 +4821,8 @@ static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
                return;
        }
 
-       genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+                               nl80211_mlme_mcgrp.id, gfp);
        return;
 
  nla_put_failure:
@@ -4390,7 +4889,8 @@ static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
                return;
        }
 
-       genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+                               nl80211_mlme_mcgrp.id, gfp);
        return;
 
  nla_put_failure:
@@ -4448,7 +4948,8 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
                return;
        }
 
-       genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+                               nl80211_mlme_mcgrp.id, gfp);
        return;
 
  nla_put_failure:
@@ -4488,7 +4989,8 @@ void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
                return;
        }
 
-       genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+                               nl80211_mlme_mcgrp.id, gfp);
        return;
 
  nla_put_failure:
@@ -4528,7 +5030,8 @@ void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
                return;
        }
 
-       genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_KERNEL);
+       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+                               nl80211_mlme_mcgrp.id, GFP_KERNEL);
        return;
 
  nla_put_failure:
@@ -4563,7 +5066,8 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
                return;
        }
 
-       genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+                               nl80211_mlme_mcgrp.id, gfp);
        return;
 
  nla_put_failure:
@@ -4603,7 +5107,8 @@ void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
                return;
        }
 
-       genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
+       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+                               nl80211_mlme_mcgrp.id, gfp);
        return;
 
  nla_put_failure:
@@ -4656,7 +5161,10 @@ void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
                return;
        }
 
-       genlmsg_multicast(msg, 0, nl80211_regulatory_mcgrp.id, GFP_ATOMIC);
+       rcu_read_lock();
+       genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
+                               GFP_ATOMIC);
+       rcu_read_unlock();
 
        return;