nl80211: Generate deauth/disassoc event for locally generated frames
[pandora-kernel.git] / net / wireless / nl80211.c
index ab9d8f1..195424e 100644 (file)
@@ -111,8 +111,43 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
                              .len = IEEE80211_MAX_DATA_LEN },
        [NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED },
        [NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED },
+
+       [NL80211_ATTR_SSID] = { .type = NLA_BINARY,
+                               .len = IEEE80211_MAX_SSID_LEN },
+       [NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 },
+       [NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 },
 };
 
+/* IE validation */
+static bool is_valid_ie_attr(const struct nlattr *attr)
+{
+       const u8 *pos;
+       int len;
+
+       if (!attr)
+               return true;
+
+       pos = nla_data(attr);
+       len = nla_len(attr);
+
+       while (len) {
+               u8 elemlen;
+
+               if (len < 2)
+                       return false;
+               len -= 2;
+
+               elemlen = pos[1];
+               if (elemlen > len)
+                       return false;
+
+               len -= elemlen;
+               pos += 2 + elemlen;
+       }
+
+       return true;
+}
+
 /* message building helper */
 static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq,
                                   int flags, u8 cmd)
@@ -131,6 +166,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
        struct nlattr *nl_freqs, *nl_freq;
        struct nlattr *nl_rates, *nl_rate;
        struct nlattr *nl_modes;
+       struct nlattr *nl_cmds;
        enum ieee80211_band band;
        struct ieee80211_channel *chan;
        struct ieee80211_rate *rate;
@@ -242,6 +278,35 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
        }
        nla_nest_end(msg, nl_bands);
 
+       nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
+       if (!nl_cmds)
+               goto nla_put_failure;
+
+       i = 0;
+#define CMD(op, n)                                             \
+        do {                                                   \
+               if (dev->ops->op) {                             \
+                       i++;                                    \
+                       NLA_PUT_U32(msg, i, NL80211_CMD_ ## n); \
+               }                                               \
+       } while (0)
+
+       CMD(add_virtual_intf, NEW_INTERFACE);
+       CMD(change_virtual_intf, SET_INTERFACE);
+       CMD(add_key, NEW_KEY);
+       CMD(add_beacon, NEW_BEACON);
+       CMD(add_station, NEW_STATION);
+       CMD(add_mpath, NEW_MPATH);
+       CMD(set_mesh_params, SET_MESH_PARAMS);
+       CMD(change_bss, SET_BSS);
+       CMD(auth, AUTHENTICATE);
+       CMD(assoc, ASSOCIATE);
+       CMD(deauth, DEAUTHENTICATE);
+       CMD(disassoc, DISASSOCIATE);
+
+#undef CMD
+       nla_nest_end(msg, nl_cmds);
+
        return genlmsg_end(msg, hdr);
 
  nla_put_failure:
@@ -331,16 +396,26 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
        int result = 0, rem_txq_params = 0;
        struct nlattr *nl_txq_params;
 
-       rdev = cfg80211_get_dev_from_info(info);
-       if (IS_ERR(rdev))
-               return PTR_ERR(rdev);
+       rtnl_lock();
+
+       mutex_lock(&cfg80211_mutex);
+
+       rdev = __cfg80211_drv_from_info(info);
+       if (IS_ERR(rdev)) {
+               result = PTR_ERR(rdev);
+               goto unlock;
+       }
 
-       if (info->attrs[NL80211_ATTR_WIPHY_NAME]) {
+       mutex_lock(&rdev->mtx);
+
+       if (info->attrs[NL80211_ATTR_WIPHY_NAME])
                result = cfg80211_dev_rename(
                        rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
-               if (result)
-                       goto bad_res;
-       }
+
+       mutex_unlock(&cfg80211_mutex);
+
+       if (result)
+               goto bad_res;
 
        if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
                struct ieee80211_txq_params txq_params;
@@ -436,7 +511,9 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
 
 
  bad_res:
-       cfg80211_put_dev(rdev);
+       mutex_unlock(&rdev->mtx);
+ unlock:
+       rtnl_unlock();
        return result;
 }
 
@@ -572,21 +649,31 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
        enum nl80211_iftype type;
        struct net_device *dev;
        u32 _flags, *flags = NULL;
+       bool change = false;
 
        memset(&params, 0, sizeof(params));
 
+       rtnl_lock();
+
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto unlock_rtnl;
+
        ifindex = dev->ifindex;
        type = dev->ieee80211_ptr->iftype;
        dev_put(dev);
 
-       err = -EINVAL;
        if (info->attrs[NL80211_ATTR_IFTYPE]) {
-               type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
-               if (type > NL80211_IFTYPE_MAX)
+               enum nl80211_iftype ntype;
+
+               ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
+               if (type != ntype)
+                       change = true;
+               type = ntype;
+               if (type > NL80211_IFTYPE_MAX) {
+                       err = -EINVAL;
                        goto unlock;
+               }
        }
 
        if (!drv->ops->change_virtual_intf ||
@@ -602,6 +689,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
                }
                params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
                params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
+               change = true;
        }
 
        if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) {
@@ -611,20 +699,26 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
                }
                err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS],
                                          &_flags);
-               if (!err)
-                       flags = &_flags;
+               if (err)
+                       goto unlock;
+
+               flags = &_flags;
+               change = true;
        }
-       rtnl_lock();
-       err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex,
-                                           type, flags, &params);
+
+       if (change)
+               err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex,
+                                                   type, flags, &params);
+       else
+               err = 0;
 
        dev = __dev_get_by_index(&init_net, ifindex);
        WARN_ON(!dev || (!err && dev->ieee80211_ptr->iftype != type));
 
-       rtnl_unlock();
-
  unlock:
        cfg80211_put_dev(drv);
+ unlock_rtnl:
+       rtnl_unlock();
        return err;
 }
 
@@ -647,9 +741,13 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
                        return -EINVAL;
        }
 
+       rtnl_lock();
+
        drv = cfg80211_get_dev_from_info(info);
-       if (IS_ERR(drv))
-               return PTR_ERR(drv);
+       if (IS_ERR(drv)) {
+               err = PTR_ERR(drv);
+               goto unlock_rtnl;
+       }
 
        if (!drv->ops->add_virtual_intf ||
            !(drv->wiphy.interface_modes & (1 << type))) {
@@ -663,18 +761,17 @@ 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]);
        }
 
-       rtnl_lock();
        err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
                                  info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
                                  &flags);
        err = drv->ops->add_virtual_intf(&drv->wiphy,
                nla_data(info->attrs[NL80211_ATTR_IFNAME]),
                type, err ? NULL : &flags, &params);
-       rtnl_unlock();
-
 
  unlock:
        cfg80211_put_dev(drv);
+ unlock_rtnl:
+       rtnl_unlock();
        return err;
 }
 
@@ -684,9 +781,11 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
        int ifindex, err;
        struct net_device *dev;
 
+       rtnl_lock();
+
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto unlock_rtnl;
        ifindex = dev->ifindex;
        dev_put(dev);
 
@@ -695,12 +794,12 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       rtnl_lock();
        err = drv->ops->del_virtual_intf(&drv->wiphy, ifindex);
-       rtnl_unlock();
 
  out:
        cfg80211_put_dev(drv);
+ unlock_rtnl:
+       rtnl_unlock();
        return err;
 }
 
@@ -752,9 +851,11 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
        if (info->attrs[NL80211_ATTR_MAC])
                mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
 
+       rtnl_lock();
+
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto unlock_rtnl;
 
        if (!drv->ops->get_key) {
                err = -EOPNOTSUPP;
@@ -782,10 +883,8 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
        if (mac_addr)
                NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
 
-       rtnl_lock();
        err = drv->ops->get_key(&drv->wiphy, dev, key_idx, mac_addr,
                                &cookie, get_key_callback);
-       rtnl_unlock();
 
        if (err)
                goto out;
@@ -803,6 +902,9 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
  out:
        cfg80211_put_dev(drv);
        dev_put(dev);
+ unlock_rtnl:
+       rtnl_unlock();
+
        return err;
 }
 
@@ -831,9 +933,11 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
            !info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT])
                return -EINVAL;
 
+       rtnl_lock();
+
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto unlock_rtnl;
 
        if (info->attrs[NL80211_ATTR_KEY_DEFAULT])
                func = drv->ops->set_default_key;
@@ -845,13 +949,15 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       rtnl_lock();
        err = func(&drv->wiphy, dev, key_idx);
-       rtnl_unlock();
 
  out:
        cfg80211_put_dev(drv);
        dev_put(dev);
+
+ unlock_rtnl:
+       rtnl_unlock();
+
        return err;
 }
 
@@ -921,22 +1027,25 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
                return -EINVAL;
        }
 
+       rtnl_lock();
+
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto unlock_rtnl;
 
        if (!drv->ops->add_key) {
                err = -EOPNOTSUPP;
                goto out;
        }
 
-       rtnl_lock();
        err = drv->ops->add_key(&drv->wiphy, dev, key_idx, mac_addr, &params);
-       rtnl_unlock();
 
  out:
        cfg80211_put_dev(drv);
        dev_put(dev);
+ unlock_rtnl:
+       rtnl_unlock();
+
        return err;
 }
 
@@ -957,22 +1066,26 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
        if (info->attrs[NL80211_ATTR_MAC])
                mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
 
+       rtnl_lock();
+
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto unlock_rtnl;
 
        if (!drv->ops->del_key) {
                err = -EOPNOTSUPP;
                goto out;
        }
 
-       rtnl_lock();
        err = drv->ops->del_key(&drv->wiphy, dev, key_idx, mac_addr);
-       rtnl_unlock();
 
  out:
        cfg80211_put_dev(drv);
        dev_put(dev);
+
+ unlock_rtnl:
+       rtnl_unlock();
+
        return err;
 }
 
@@ -986,9 +1099,19 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
        struct beacon_parameters params;
        int haveinfo = 0;
 
+       if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]))
+               return -EINVAL;
+
+       rtnl_lock();
+
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto unlock_rtnl;
+
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
 
        switch (info->genlhdr->cmd) {
        case NL80211_CMD_NEW_BEACON:
@@ -1049,13 +1172,14 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       rtnl_lock();
        err = call(&drv->wiphy, dev, &params);
-       rtnl_unlock();
 
  out:
        cfg80211_put_dev(drv);
        dev_put(dev);
+ unlock_rtnl:
+       rtnl_unlock();
+
        return err;
 }
 
@@ -1065,22 +1189,29 @@ static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info)
        int err;
        struct net_device *dev;
 
+       rtnl_lock();
+
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto unlock_rtnl;
 
        if (!drv->ops->del_beacon) {
                err = -EOPNOTSUPP;
                goto out;
        }
 
-       rtnl_lock();
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
        err = drv->ops->del_beacon(&drv->wiphy, dev);
-       rtnl_unlock();
 
  out:
        cfg80211_put_dev(drv);
        dev_put(dev);
+ unlock_rtnl:
+       rtnl_unlock();
+
        return err;
 }
 
@@ -1246,30 +1377,32 @@ static int nl80211_dump_station(struct sk_buff *skb,
                        return -EINVAL;
        }
 
-       netdev = dev_get_by_index(&init_net, ifidx);
-       if (!netdev)
-               return -ENODEV;
+       rtnl_lock();
+
+       netdev = __dev_get_by_index(&init_net, ifidx);
+       if (!netdev) {
+               err = -ENODEV;
+               goto out_rtnl;
+       }
 
        dev = cfg80211_get_dev_from_ifindex(ifidx);
        if (IS_ERR(dev)) {
                err = PTR_ERR(dev);
-               goto out_put_netdev;
+               goto out_rtnl;
        }
 
        if (!dev->ops->dump_station) {
-               err = -ENOSYS;
+               err = -EOPNOTSUPP;
                goto out_err;
        }
 
-       rtnl_lock();
-
        while (1) {
                err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx,
                                             mac_addr, &sinfo);
                if (err == -ENOENT)
                        break;
                if (err)
-                       goto out_err_rtnl;
+                       goto out_err;
 
                if (nl80211_send_station(skb,
                                NETLINK_CB(cb->skb).pid,
@@ -1285,12 +1418,10 @@ static int nl80211_dump_station(struct sk_buff *skb,
  out:
        cb->args[1] = sta_idx;
        err = skb->len;
- out_err_rtnl:
-       rtnl_unlock();
  out_err:
        cfg80211_put_dev(dev);
- out_put_netdev:
-       dev_put(netdev);
+ out_rtnl:
+       rtnl_unlock();
 
        return err;
 }
@@ -1311,19 +1442,18 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
 
        mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
 
+       rtnl_lock();
+
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto out_rtnl;
 
        if (!drv->ops->get_station) {
                err = -EOPNOTSUPP;
                goto out;
        }
 
-       rtnl_lock();
        err = drv->ops->get_station(&drv->wiphy, dev, mac_addr, &sinfo);
-       rtnl_unlock();
-
        if (err)
                goto out;
 
@@ -1340,10 +1470,12 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
 
  out_free:
        nlmsg_free(msg);
-
  out:
        cfg80211_put_dev(drv);
        dev_put(dev);
+ out_rtnl:
+       rtnl_unlock();
+
        return err;
 }
 
@@ -1411,9 +1543,11 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
                params.plink_action =
                    nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
 
+       rtnl_lock();
+
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto out_rtnl;
 
        err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, &params.vlan);
        if (err)
@@ -1424,15 +1558,16 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       rtnl_lock();
        err = drv->ops->change_station(&drv->wiphy, dev, mac_addr, &params);
-       rtnl_unlock();
 
  out:
        if (params.vlan)
                dev_put(params.vlan);
        cfg80211_put_dev(drv);
        dev_put(dev);
+ out_rtnl:
+       rtnl_unlock();
+
        return err;
 }
 
@@ -1474,9 +1609,11 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
                                &params.station_flags))
                return -EINVAL;
 
+       rtnl_lock();
+
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto out_rtnl;
 
        err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, &params.vlan);
        if (err)
@@ -1487,15 +1624,21 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       rtnl_lock();
+       if (!netif_running(dev)) {
+               err = -ENETDOWN;
+               goto out;
+       }
+
        err = drv->ops->add_station(&drv->wiphy, dev, mac_addr, &params);
-       rtnl_unlock();
 
  out:
        if (params.vlan)
                dev_put(params.vlan);
        cfg80211_put_dev(drv);
        dev_put(dev);
+ out_rtnl:
+       rtnl_unlock();
+
        return err;
 }
 
@@ -1509,22 +1652,25 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
        if (info->attrs[NL80211_ATTR_MAC])
                mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
 
+       rtnl_lock();
+
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto out_rtnl;
 
        if (!drv->ops->del_station) {
                err = -EOPNOTSUPP;
                goto out;
        }
 
-       rtnl_lock();
        err = drv->ops->del_station(&drv->wiphy, dev, mac_addr);
-       rtnl_unlock();
 
  out:
        cfg80211_put_dev(drv);
        dev_put(dev);
+ out_rtnl:
+       rtnl_unlock();
+
        return err;
 }
 
@@ -1605,22 +1751,29 @@ static int nl80211_dump_mpath(struct sk_buff *skb,
                        return -EINVAL;
        }
 
-       netdev = dev_get_by_index(&init_net, ifidx);
-       if (!netdev)
-               return -ENODEV;
+       rtnl_lock();
+
+       netdev = __dev_get_by_index(&init_net, ifidx);
+       if (!netdev) {
+               err = -ENODEV;
+               goto out_rtnl;
+       }
 
        dev = cfg80211_get_dev_from_ifindex(ifidx);
        if (IS_ERR(dev)) {
                err = PTR_ERR(dev);
-               goto out_put_netdev;
+               goto out_rtnl;
        }
 
        if (!dev->ops->dump_mpath) {
-               err = -ENOSYS;
+               err = -EOPNOTSUPP;
                goto out_err;
        }
 
-       rtnl_lock();
+       if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
 
        while (1) {
                err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx,
@@ -1628,7 +1781,7 @@ static int nl80211_dump_mpath(struct sk_buff *skb,
                if (err == -ENOENT)
                        break;
                if (err)
-                       goto out_err_rtnl;
+                       goto out_err;
 
                if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).pid,
                                       cb->nlh->nlmsg_seq, NLM_F_MULTI,
@@ -1643,12 +1796,10 @@ static int nl80211_dump_mpath(struct sk_buff *skb,
  out:
        cb->args[1] = path_idx;
        err = skb->len;
- out_err_rtnl:
-       rtnl_unlock();
  out_err:
        cfg80211_put_dev(dev);
- out_put_netdev:
-       dev_put(netdev);
+ out_rtnl:
+       rtnl_unlock();
 
        return err;
 }
@@ -1670,19 +1821,23 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
 
        dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
 
+       rtnl_lock();
+
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto out_rtnl;
 
        if (!drv->ops->get_mpath) {
                err = -EOPNOTSUPP;
                goto out;
        }
 
-       rtnl_lock();
-       err = drv->ops->get_mpath(&drv->wiphy, dev, dst, next_hop, &pinfo);
-       rtnl_unlock();
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
 
+       err = drv->ops->get_mpath(&drv->wiphy, dev, dst, next_hop, &pinfo);
        if (err)
                goto out;
 
@@ -1699,10 +1854,12 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
 
  out_free:
        nlmsg_free(msg);
-
  out:
        cfg80211_put_dev(drv);
        dev_put(dev);
+ out_rtnl:
+       rtnl_unlock();
+
        return err;
 }
 
@@ -1723,22 +1880,35 @@ static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)
        dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
        next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
 
+       rtnl_lock();
+
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto out_rtnl;
 
        if (!drv->ops->change_mpath) {
                err = -EOPNOTSUPP;
                goto out;
        }
 
-       rtnl_lock();
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       if (!netif_running(dev)) {
+               err = -ENETDOWN;
+               goto out;
+       }
+
        err = drv->ops->change_mpath(&drv->wiphy, dev, dst, next_hop);
-       rtnl_unlock();
 
  out:
        cfg80211_put_dev(drv);
        dev_put(dev);
+ out_rtnl:
+       rtnl_unlock();
+
        return err;
 }
 static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
@@ -1758,22 +1928,35 @@ static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
        dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
        next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
 
+       rtnl_lock();
+
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto out_rtnl;
 
        if (!drv->ops->add_mpath) {
                err = -EOPNOTSUPP;
                goto out;
        }
 
-       rtnl_lock();
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       if (!netif_running(dev)) {
+               err = -ENETDOWN;
+               goto out;
+       }
+
        err = drv->ops->add_mpath(&drv->wiphy, dev, dst, next_hop);
-       rtnl_unlock();
 
  out:
        cfg80211_put_dev(drv);
        dev_put(dev);
+ out_rtnl:
+       rtnl_unlock();
+
        return err;
 }
 
@@ -1787,22 +1970,25 @@ static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
        if (info->attrs[NL80211_ATTR_MAC])
                dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
 
+       rtnl_lock();
+
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto out_rtnl;
 
        if (!drv->ops->del_mpath) {
                err = -EOPNOTSUPP;
                goto out;
        }
 
-       rtnl_lock();
        err = drv->ops->del_mpath(&drv->wiphy, dev, dst);
-       rtnl_unlock();
 
  out:
        cfg80211_put_dev(drv);
        dev_put(dev);
+ out_rtnl:
+       rtnl_unlock();
+
        return err;
 }
 
@@ -1835,22 +2021,30 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
                        nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
        }
 
+       rtnl_lock();
+
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto out_rtnl;
 
        if (!drv->ops->change_bss) {
                err = -EOPNOTSUPP;
                goto out;
        }
 
-       rtnl_lock();
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
        err = drv->ops->change_bss(&drv->wiphy, dev, &params);
-       rtnl_unlock();
 
  out:
        cfg80211_put_dev(drv);
        dev_put(dev);
+ out_rtnl:
+       rtnl_unlock();
+
        return err;
 }
 
@@ -1945,10 +2139,12 @@ static int nl80211_get_mesh_params(struct sk_buff *skb,
        struct nlattr *pinfoattr;
        struct sk_buff *msg;
 
+       rtnl_lock();
+
        /* Look up our device */
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto out_rtnl;
 
        if (!drv->ops->get_mesh_params) {
                err = -EOPNOTSUPP;
@@ -1956,9 +2152,7 @@ static int nl80211_get_mesh_params(struct sk_buff *skb,
        }
 
        /* Get the mesh params */
-       rtnl_lock();
        err = drv->ops->get_mesh_params(&drv->wiphy, dev, &cur_params);
-       rtnl_unlock();
        if (err)
                goto out;
 
@@ -2007,13 +2201,16 @@ static int nl80211_get_mesh_params(struct sk_buff *skb,
        err = genlmsg_unicast(msg, info->snd_pid);
        goto out;
 
-nla_put_failure:
+ nla_put_failure:
        genlmsg_cancel(msg, hdr);
        err = -EMSGSIZE;
-out:
+ out:
        /* Cleanup */
        cfg80211_put_dev(drv);
        dev_put(dev);
+ out_rtnl:
+       rtnl_unlock();
+
        return err;
 }
 
@@ -2060,9 +2257,11 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info)
                        parent_attr, nl80211_meshconf_params_policy))
                return -EINVAL;
 
+       rtnl_lock();
+
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto out_rtnl;
 
        if (!drv->ops->set_mesh_params) {
                err = -EOPNOTSUPP;
@@ -2109,14 +2308,15 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info)
                        nla_get_u16);
 
        /* Apply changes */
-       rtnl_lock();
        err = drv->ops->set_mesh_params(&drv->wiphy, dev, &cfg, mask);
-       rtnl_unlock();
 
  out:
        /* cleanup */
        cfg80211_put_dev(drv);
        dev_put(dev);
+ out_rtnl:
+       rtnl_unlock();
+
        return err;
 }
 
@@ -2262,44 +2462,7 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
        return -EINVAL;
 }
 
-static int nl80211_set_mgmt_extra_ie(struct sk_buff *skb,
-                                    struct genl_info *info)
-{
-       struct cfg80211_registered_device *drv;
-       int err;
-       struct net_device *dev;
-       struct mgmt_extra_ie_params params;
-
-       memset(&params, 0, sizeof(params));
-
-       if (!info->attrs[NL80211_ATTR_MGMT_SUBTYPE])
-               return -EINVAL;
-       params.subtype = nla_get_u8(info->attrs[NL80211_ATTR_MGMT_SUBTYPE]);
-       if (params.subtype > 15)
-               return -EINVAL; /* FC Subtype field is 4 bits (0..15) */
-
-       if (info->attrs[NL80211_ATTR_IE]) {
-               params.ies = nla_data(info->attrs[NL80211_ATTR_IE]);
-               params.ies_len = nla_len(info->attrs[NL80211_ATTR_IE]);
-       }
-
-       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
-       if (err)
-               return err;
-
-       if (drv->ops->set_mgmt_extra_ie) {
-               rtnl_lock();
-               err = drv->ops->set_mgmt_extra_ie(&drv->wiphy, dev, &params);
-               rtnl_unlock();
-       } else
-               err = -EOPNOTSUPP;
-
-       cfg80211_put_dev(drv);
-       dev_put(dev);
-       return err;
-}
-
-static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
+static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *drv;
        struct net_device *dev;
@@ -2312,9 +2475,14 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
        enum ieee80211_band band;
        size_t ie_len;
 
+       if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+               return -EINVAL;
+
+       rtnl_lock();
+
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
-               return err;
+               goto out_rtnl;
 
        wiphy = &drv->wiphy;
 
@@ -2323,11 +2491,14 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
                goto out;
        }
 
-       rtnl_lock();
+       if (!netif_running(dev)) {
+               err = -ENETDOWN;
+               goto out;
+       }
 
        if (drv->scan_req) {
                err = -EBUSY;
-               goto out_unlock;
+               goto out;
        }
 
        if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
@@ -2335,7 +2506,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
                        n_channels++;
                if (!n_channels) {
                        err = -EINVAL;
-                       goto out_unlock;
+                       goto out;
                }
        } else {
                for (band = 0; band < IEEE80211_NUM_BANDS; band++)
@@ -2349,7 +2520,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
 
        if (n_ssids > wiphy->max_scan_ssids) {
                err = -EINVAL;
-               goto out_unlock;
+               goto out;
        }
 
        if (info->attrs[NL80211_ATTR_IE])
@@ -2363,7 +2534,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
                        + ie_len, GFP_KERNEL);
        if (!request) {
                err = -ENOMEM;
-               goto out_unlock;
+               goto out;
        }
 
        request->channels = (void *)((char *)request + sizeof(*request));
@@ -2434,11 +2605,12 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
                drv->scan_req = NULL;
                kfree(request);
        }
- out_unlock:
-       rtnl_unlock();
  out:
        cfg80211_put_dev(drv);
        dev_put(dev);
+ out_rtnl:
+       rtnl_unlock();
+
        return err;
 }
 
@@ -2558,6 +2730,292 @@ static int nl80211_dump_scan(struct sk_buff *skb,
        return err;
 }
 
+static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type)
+{
+       return auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM ||
+               auth_type == NL80211_AUTHTYPE_SHARED_KEY ||
+               auth_type == NL80211_AUTHTYPE_FT ||
+               auth_type == NL80211_AUTHTYPE_NETWORK_EAP;
+}
+
+static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *drv;
+       struct net_device *dev;
+       struct cfg80211_auth_request req;
+       struct wiphy *wiphy;
+       int err;
+
+       if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+               return -EINVAL;
+
+       if (!info->attrs[NL80211_ATTR_MAC])
+               return -EINVAL;
+
+       if (!info->attrs[NL80211_ATTR_AUTH_TYPE])
+               return -EINVAL;
+
+       rtnl_lock();
+
+       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       if (err)
+               goto unlock_rtnl;
+
+       if (!drv->ops->auth) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       if (!netif_running(dev)) {
+               err = -ENETDOWN;
+               goto out;
+       }
+
+       wiphy = &drv->wiphy;
+       memset(&req, 0, sizeof(req));
+
+       req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+       if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
+               req.chan = ieee80211_get_channel(
+                       wiphy,
+                       nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
+               if (!req.chan) {
+                       err = -EINVAL;
+                       goto out;
+               }
+       }
+
+       if (info->attrs[NL80211_ATTR_SSID]) {
+               req.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
+               req.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
+       }
+
+       if (info->attrs[NL80211_ATTR_IE]) {
+               req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+               req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+       }
+
+       req.auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
+       if (!nl80211_valid_auth_type(req.auth_type)) {
+               err = -EINVAL;
+               goto out;
+       }
+
+       err = drv->ops->auth(&drv->wiphy, dev, &req);
+
+out:
+       cfg80211_put_dev(drv);
+       dev_put(dev);
+unlock_rtnl:
+       rtnl_unlock();
+       return err;
+}
+
+static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *drv;
+       struct net_device *dev;
+       struct cfg80211_assoc_request req;
+       struct wiphy *wiphy;
+       int err;
+
+       if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+               return -EINVAL;
+
+       if (!info->attrs[NL80211_ATTR_MAC] ||
+           !info->attrs[NL80211_ATTR_SSID])
+               return -EINVAL;
+
+       rtnl_lock();
+
+       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       if (err)
+               goto unlock_rtnl;
+
+       if (!drv->ops->assoc) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       if (!netif_running(dev)) {
+               err = -ENETDOWN;
+               goto out;
+       }
+
+       wiphy = &drv->wiphy;
+       memset(&req, 0, sizeof(req));
+
+       req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+       if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
+               req.chan = ieee80211_get_channel(
+                       wiphy,
+                       nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
+               if (!req.chan) {
+                       err = -EINVAL;
+                       goto out;
+               }
+       }
+
+       req.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
+       req.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
+
+       if (info->attrs[NL80211_ATTR_IE]) {
+               req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+               req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+       }
+
+       err = drv->ops->assoc(&drv->wiphy, dev, &req);
+
+out:
+       cfg80211_put_dev(drv);
+       dev_put(dev);
+unlock_rtnl:
+       rtnl_unlock();
+       return err;
+}
+
+static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *drv;
+       struct net_device *dev;
+       struct cfg80211_deauth_request req;
+       struct wiphy *wiphy;
+       int err;
+
+       if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+               return -EINVAL;
+
+       if (!info->attrs[NL80211_ATTR_MAC])
+               return -EINVAL;
+
+       if (!info->attrs[NL80211_ATTR_REASON_CODE])
+               return -EINVAL;
+
+       rtnl_lock();
+
+       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       if (err)
+               goto unlock_rtnl;
+
+       if (!drv->ops->deauth) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       if (!netif_running(dev)) {
+               err = -ENETDOWN;
+               goto out;
+       }
+
+       wiphy = &drv->wiphy;
+       memset(&req, 0, sizeof(req));
+
+       req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+       req.reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
+       if (req.reason_code == 0) {
+               /* Reason Code 0 is reserved */
+               err = -EINVAL;
+               goto out;
+       }
+
+       if (info->attrs[NL80211_ATTR_IE]) {
+               req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+               req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+       }
+
+       err = drv->ops->deauth(&drv->wiphy, dev, &req);
+
+out:
+       cfg80211_put_dev(drv);
+       dev_put(dev);
+unlock_rtnl:
+       rtnl_unlock();
+       return err;
+}
+
+static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
+{
+       struct cfg80211_registered_device *drv;
+       struct net_device *dev;
+       struct cfg80211_disassoc_request req;
+       struct wiphy *wiphy;
+       int err;
+
+       if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
+               return -EINVAL;
+
+       if (!info->attrs[NL80211_ATTR_MAC])
+               return -EINVAL;
+
+       if (!info->attrs[NL80211_ATTR_REASON_CODE])
+               return -EINVAL;
+
+       rtnl_lock();
+
+       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       if (err)
+               goto unlock_rtnl;
+
+       if (!drv->ops->disassoc) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       if (!netif_running(dev)) {
+               err = -ENETDOWN;
+               goto out;
+       }
+
+       wiphy = &drv->wiphy;
+       memset(&req, 0, sizeof(req));
+
+       req.peer_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+       req.reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
+       if (req.reason_code == 0) {
+               /* Reason Code 0 is reserved */
+               err = -EINVAL;
+               goto out;
+       }
+
+       if (info->attrs[NL80211_ATTR_IE]) {
+               req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
+               req.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+       }
+
+       err = drv->ops->disassoc(&drv->wiphy, dev, &req);
+
+out:
+       cfg80211_put_dev(drv);
+       dev_put(dev);
+unlock_rtnl:
+       rtnl_unlock();
+       return err;
+}
+
 static struct genl_ops nl80211_ops[] = {
        {
                .cmd = NL80211_CMD_GET_WIPHY,
@@ -2724,12 +3182,6 @@ static struct genl_ops nl80211_ops[] = {
                .policy = nl80211_policy,
                .flags = GENL_ADMIN_PERM,
        },
-       {
-               .cmd = NL80211_CMD_SET_MGMT_EXTRA_IE,
-               .doit = nl80211_set_mgmt_extra_ie,
-               .policy = nl80211_policy,
-               .flags = GENL_ADMIN_PERM,
-       },
        {
                .cmd = NL80211_CMD_TRIGGER_SCAN,
                .doit = nl80211_trigger_scan,
@@ -2741,6 +3193,33 @@ static struct genl_ops nl80211_ops[] = {
                .policy = nl80211_policy,
                .dumpit = nl80211_dump_scan,
        },
+       {
+               .cmd = NL80211_CMD_AUTHENTICATE,
+               .doit = nl80211_authenticate,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+       },
+       {
+               .cmd = NL80211_CMD_ASSOCIATE,
+               .doit = nl80211_associate,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+       },
+       {
+               .cmd = NL80211_CMD_DEAUTHENTICATE,
+               .doit = nl80211_deauthenticate,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+       },
+       {
+               .cmd = NL80211_CMD_DISASSOCIATE,
+               .doit = nl80211_disassociate,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+       },
+};
+static struct genl_multicast_group nl80211_mlme_mcgrp = {
+       .name = "mlme",
 };
 
 /* multicast groups */
@@ -2887,6 +3366,70 @@ nla_put_failure:
        nlmsg_free(msg);
 }
 
+static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
+                                   struct net_device *netdev,
+                                   const u8 *buf, size_t len,
+                                   enum nl80211_commands cmd)
+{
+       struct sk_buff *msg;
+       void *hdr;
+
+       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
+       if (!msg)
+               return;
+
+       hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
+       if (!hdr) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
+       NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
+
+       if (genlmsg_end(msg, hdr) < 0) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_ATOMIC);
+       return;
+
+ nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       nlmsg_free(msg);
+}
+
+void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
+                         struct net_device *netdev, const u8 *buf, size_t len)
+{
+       nl80211_send_mlme_event(rdev, netdev, buf, len,
+                               NL80211_CMD_AUTHENTICATE);
+}
+
+void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
+                          struct net_device *netdev, const u8 *buf,
+                          size_t len)
+{
+       nl80211_send_mlme_event(rdev, netdev, buf, len, NL80211_CMD_ASSOCIATE);
+}
+
+void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
+                        struct net_device *netdev, const u8 *buf, size_t len)
+{
+       nl80211_send_mlme_event(rdev, netdev, buf, len,
+                               NL80211_CMD_DEAUTHENTICATE);
+}
+
+void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
+                          struct net_device *netdev, const u8 *buf,
+                          size_t len)
+{
+       nl80211_send_mlme_event(rdev, netdev, buf, len,
+                               NL80211_CMD_DISASSOCIATE);
+}
+
 /* initialisation/exit functions */
 
 int nl80211_init(void)
@@ -2915,6 +3458,10 @@ int nl80211_init(void)
        if (err)
                goto err_out;
 
+       err = genl_register_mc_group(&nl80211_fam, &nl80211_mlme_mcgrp);
+       if (err)
+               goto err_out;
+
        return 0;
  err_out:
        genl_unregister_family(&nl80211_fam);