add git checkout support
[openwrt/openwrt.git] / package / mac80211 / src / wireless / nl80211.c
index d6a44a386c2b86b81514b08d3c9b324dd2c7d229..58717f303763c7d9811a1dad762716bee8f0367f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This is the new netlink-based wireless configuration interface.
  *
- * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2006, 2007        Johannes Berg <johannes@sipsolutions.net>
  */
 
 #include <linux/if.h>
@@ -13,6 +13,7 @@
 #include <linux/ieee80211.h>
 #include <linux/nl80211.h>
 #include <linux/rtnetlink.h>
+#include <linux/netlink.h>
 #include <net/genetlink.h>
 #include <net/cfg80211.h>
 #include "core.h"
@@ -27,22 +28,6 @@ static struct genl_family nl80211_fam = {
        .maxattr = NL80211_ATTR_MAX,
 };
 
-/* internal helper: validate an information element attribute */
-static int check_information_element(struct nlattr *nla)
-{
-       int len = nla_len(nla);
-       u8 *data = nla_data(nla);
-       int elementlen;
-
-       while (len >= 2) {
-               /* 1 byte ID, 1 byte len, `len' bytes data */
-               elementlen = *(data+1) + 2;
-               data += elementlen;
-               len -= elementlen;
-       }
-       return len ? -EINVAL : 0;
-}
-
 /* internal helper: get drv and dev */
 static int get_drv_dev_by_info_ifindex(struct genl_info *info,
                                       struct cfg80211_registered_device **drv,
@@ -55,7 +40,7 @@ static int get_drv_dev_by_info_ifindex(struct genl_info *info,
 
        ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
        *dev = dev_get_by_index(ifindex);
-       if (!dev)
+       if (!*dev)
                return -ENODEV;
 
        *drv = cfg80211_get_dev_from_ifindex(ifindex);
@@ -69,291 +54,198 @@ static int get_drv_dev_by_info_ifindex(struct genl_info *info,
 
 /* policy for the attributes */
 static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
-       [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
-       [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
        [NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
        [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
                                      .len = BUS_ID_SIZE-1 },
+
        [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
-       [NL80211_ATTR_BSSID] = { .len = ETH_ALEN },
-       [NL80211_ATTR_SSID] = { .type = NLA_BINARY,
-                               .len = IEEE80211_MAX_SSID_LEN },
-       [NL80211_ATTR_CHANNEL] = { .type = NLA_U32 },
-       [NL80211_ATTR_PHYMODE] = { .type = NLA_U32 },
-       [NL80211_ATTR_CHANNEL_LIST] = { .type = NLA_NESTED },
-       [NL80211_ATTR_BSS_LIST] = { .type = NLA_NESTED },
-       [NL80211_ATTR_BSSTYPE] = { .type = NLA_U32 },
-       [NL80211_ATTR_BEACON_PERIOD] = { .type = NLA_U32 },
-       [NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 },
-       [NL80211_ATTR_TIMESTAMP] = { .type = NLA_U64 },
-       [NL80211_ATTR_IE] = { .type = NLA_BINARY, .len = NL80211_MAX_IE_LEN },
-       [NL80211_ATTR_AUTH_ALGORITHM] = { .type = NLA_U32 },
-       [NL80211_ATTR_TIMEOUT_TU] = { .type = NLA_U32 },
-       [NL80211_ATTR_REASON_CODE] = { .type = NLA_U32 },
-       [NL80211_ATTR_ASSOCIATION_ID] = { .type = NLA_U16 },
-       [NL80211_ATTR_DEAUTHENTICATED] = { .type = NLA_FLAG },
-       [NL80211_ATTR_RX_SENSITIVITY] = { .type = NLA_U32 },
-       [NL80211_ATTR_TRANSMIT_POWER] = { .type = NLA_U32 },
-       [NL80211_ATTR_FRAG_THRESHOLD] = { .type = NLA_U32 },
-       [NL80211_ATTR_FLAG_SCAN_ACTIVE] = { .type = NLA_FLAG },
-       [NL80211_ATTR_BEACON_HEAD] = { .type = NLA_BINARY },
-       [NL80211_ATTR_BEACON_TAIL] = { .type = NLA_BINARY },
-       [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY,
-                                   .len = WLAN_MAX_KEY_LEN },
-       [NL80211_ATTR_KEY_ID] = { .type = NLA_U32 },
-       [NL80211_ATTR_KEY_TYPE] = { .type = NLA_U32 },
-       [NL80211_ATTR_MAC] = { .len = ETH_ALEN },
-       [NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 },
+       [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
+       [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
 };
 
-/* netlink command implementations */
+/* message building helper */
+static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq,
+                                  int flags, u8 cmd)
+{
+       /* since there is no private header just add the generic one */
+       return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd);
+}
 
-#define CHECK_CMD(ptr, cmd)                            \
-       if (drv->ops->ptr)                              \
-               NLA_PUT_FLAG(msg, NL80211_CMD_##cmd);
+/* netlink command implementations */
 
-static int nl80211_get_cmdlist(struct sk_buff *skb, struct genl_info *info)
+static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
+                             struct cfg80211_registered_device *dev)
 {
-       struct cfg80211_registered_device *drv;
-       struct sk_buff *msg;
        void *hdr;
-       int err;
-       struct nlattr *start;
 
-       drv = cfg80211_get_dev_from_info(info);
-       if (IS_ERR(drv))
-               return PTR_ERR(drv);
-
-       hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
-                            NL80211_CMD_NEW_CMDLIST);
-       if (IS_ERR(hdr)) {
-               err = PTR_ERR(hdr);
-               goto put_drv;
-       }
+       hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY);
+       if (!hdr)
+               return -1;
 
-       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, drv->idx);
+       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->idx);
+       NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
+       return genlmsg_end(msg, hdr);
 
-       start = nla_nest_start(msg, NL80211_ATTR_CMDS);
-       if (!start)
-               goto nla_put_failure;
-
-       /* unconditionally allow some common commands we handle centrally
-        * or where we require the implementation */
-       NLA_PUT_FLAG(msg, NL80211_CMD_GET_CMDLIST);
-       NLA_PUT_FLAG(msg, NL80211_CMD_GET_WIPHYS);
-       NLA_PUT_FLAG(msg, NL80211_CMD_GET_INTERFACES);
-       NLA_PUT_FLAG(msg, NL80211_CMD_RENAME_WIPHY);
-
-       CHECK_CMD(add_virtual_intf, ADD_VIRTUAL_INTERFACE);
-       CHECK_CMD(del_virtual_intf, DEL_VIRTUAL_INTERFACE);
-       CHECK_CMD(associate, ASSOCIATE);
-       CHECK_CMD(disassociate, DISASSOCIATE);
-       CHECK_CMD(deauth, DEAUTH);
-       CHECK_CMD(initiate_scan, INITIATE_SCAN);
-       CHECK_CMD(get_association, GET_ASSOCIATION);
-       CHECK_CMD(get_auth_list, GET_AUTH_LIST);
-       CHECK_CMD(add_key, ADD_KEY);
-       CHECK_CMD(del_key, DEL_KEY);
+ nla_put_failure:
+       return genlmsg_cancel(msg, hdr);
+}
 
-       nla_nest_end(msg, start);
+static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
+{
+       int idx = 0;
+       int start = cb->args[0];
+       struct cfg80211_registered_device *dev;
 
-       genlmsg_end(msg, hdr);
+       mutex_lock(&cfg80211_drv_mutex);
+       list_for_each_entry(dev, &cfg80211_drv_list, list) {
+               if (++idx < start)
+                       continue;
+               if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid,
+                                      cb->nlh->nlmsg_seq, NLM_F_MULTI,
+                                      dev) < 0)
+                       break;
+       }
+       mutex_unlock(&cfg80211_drv_mutex);
 
-       err = genlmsg_unicast(msg, info->snd_pid);
-       goto put_drv;
+       cb->args[0] = idx;
 
- nla_put_failure:
-       err = -ENOBUFS;
-       nlmsg_free(msg);
- put_drv:
-       cfg80211_put_dev(drv);
-       return err;
+       return skb->len;
 }
-#undef CHECK_CMD
 
-static int nl80211_get_wiphys(struct sk_buff *skb, struct genl_info *info)
+static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
 {
        struct sk_buff *msg;
-       void *hdr;
-       struct nlattr *start, *indexstart;
-       struct cfg80211_registered_device *drv;
-       int idx = 1;
-
-       hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
-                            NL80211_CMD_NEW_WIPHYS);
-       if (IS_ERR(hdr))
-               return PTR_ERR(hdr);
+       struct cfg80211_registered_device *dev;
 
-       start = nla_nest_start(msg, NL80211_ATTR_WIPHY_LIST);
-       if (!start)
-               goto nla_outer_nest_failure;
+       dev = cfg80211_get_dev_from_info(info);
+       if (IS_ERR(dev))
+               return PTR_ERR(dev);
 
-       mutex_lock(&cfg80211_drv_mutex);
-       list_for_each_entry(drv, &cfg80211_drv_list, list) {
-               indexstart = nla_nest_start(msg, idx++);
-               if (!indexstart)
-                       goto nla_put_failure;
-               NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, drv->idx);
-               nla_nest_end(msg, indexstart);
-       }
-       mutex_unlock(&cfg80211_drv_mutex);
+       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (!msg)
+               goto out_err;
 
-       nla_nest_end(msg, start);
+       if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0)
+               goto out_free;
 
-       genlmsg_end(msg, hdr);
+       cfg80211_put_dev(dev);
 
        return genlmsg_unicast(msg, info->snd_pid);
 
- nla_put_failure:
-       mutex_unlock(&cfg80211_drv_mutex);
- nla_outer_nest_failure:
+ out_free:
        nlmsg_free(msg);
+ out_err:
+       cfg80211_put_dev(dev);
        return -ENOBUFS;
 }
 
-static int addifidx(struct net_device *dev, struct sk_buff *skb, int *idx)
+static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
 {
-       int err = -ENOBUFS;
-       struct nlattr *start;
-
-       dev_hold(dev);
+       struct cfg80211_registered_device *rdev;
+       int result;
 
-       start = nla_nest_start(skb, *idx++);
-       if (!start)
-               goto nla_put_failure;
+       if (!info->attrs[NL80211_ATTR_WIPHY_NAME])
+               return -EINVAL;
 
-       NLA_PUT_U32(skb, NL80211_ATTR_IFINDEX, dev->ifindex);
-       NLA_PUT_STRING(skb, NL80211_ATTR_IFNAME, dev->name);
+       rdev = cfg80211_get_dev_from_info(info);
+       if (IS_ERR(rdev))
+               return PTR_ERR(rdev);
 
-       nla_nest_end(skb, start);
-       err = 0;
+       result = cfg80211_dev_rename(rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
 
- nla_put_failure:
-       dev_put(dev);
-       return err;
+       cfg80211_put_dev(rdev);
+       return result;
 }
 
-static int nl80211_get_intfs(struct sk_buff *skb, struct genl_info *info)
+
+static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
+                             struct net_device *dev)
 {
-       struct cfg80211_registered_device *drv;
-       struct sk_buff *msg;
        void *hdr;
-       int err, array_idx;
-       struct nlattr *start;
-       struct wireless_dev *wdev;
-
-       drv = cfg80211_get_dev_from_info(info);
-       if (IS_ERR(drv))
-               return PTR_ERR(drv);
-
-       hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
-                            NL80211_CMD_NEW_INTERFACES);
-       if (IS_ERR(hdr)) {
-               err = PTR_ERR(hdr);
-               goto put_drv;
-       }
 
-       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, drv->idx);
+       hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE);
+       if (!hdr)
+               return -1;
 
-       start = nla_nest_start(msg, NL80211_ATTR_INTERFACE_LIST);
-       if (!start) {
-               err = -ENOBUFS;
-               goto msg_free;
-       }
-
-       array_idx = 1;
-       err = 0;
-       mutex_lock(&drv->devlist_mtx);
-       list_for_each_entry(wdev, &drv->netdev_list, list) {
-               err = addifidx(wdev->netdev, msg, &array_idx);
-               if (err)
-                       break;
-       }
-       mutex_unlock(&drv->devlist_mtx);
-       if (err)
-               goto msg_free;
-
-       nla_nest_end(msg, start);
-
-       genlmsg_end(msg, hdr);
-
-       err = genlmsg_unicast(msg, info->snd_pid);
-       goto put_drv;
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
+       NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name);
+       /* TODO: interface type */
+       return genlmsg_end(msg, hdr);
 
  nla_put_failure:
-       err = -ENOBUFS;
- msg_free:
-       nlmsg_free(msg);
- put_drv:
-       cfg80211_put_dev(drv);
-       return err;
+       return genlmsg_cancel(msg, hdr);
 }
 
-static int nl80211_add_virt_intf(struct sk_buff *skb, struct genl_info *info)
+static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
 {
-       struct cfg80211_registered_device *drv;
-       int err;
-       unsigned int type = NL80211_IFTYPE_UNSPECIFIED;
-
-       if (!info->attrs[NL80211_ATTR_IFNAME])
-               return -EINVAL;
-
-       if (info->attrs[NL80211_ATTR_IFTYPE]) {
-               type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
-               if (type > NL80211_IFTYPE_MAX)
-                       return -EINVAL;
-       }
-
-       drv = cfg80211_get_dev_from_info(info);
-       if (IS_ERR(drv))
-               return PTR_ERR(drv);
+       int wp_idx = 0;
+       int if_idx = 0;
+       int wp_start = cb->args[0];
+       int if_start = cb->args[1];
+       struct cfg80211_registered_device *dev;
+       struct wireless_dev *wdev;
 
-       if (!drv->ops->add_virtual_intf) {
-               err = -EOPNOTSUPP;
-               goto unlock;
+       mutex_lock(&cfg80211_drv_mutex);
+       list_for_each_entry(dev, &cfg80211_drv_list, list) {
+               if (++wp_idx < wp_start)
+                       continue;
+               if_idx = 0;
+
+               mutex_lock(&dev->devlist_mtx);
+               list_for_each_entry(wdev, &dev->netdev_list, list) {
+                       if (++if_idx < if_start)
+                               continue;
+                       if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid,
+                                              cb->nlh->nlmsg_seq, NLM_F_MULTI,
+                                              wdev->netdev) < 0)
+                               break;
+               }
+               mutex_unlock(&dev->devlist_mtx);
        }
+       mutex_unlock(&cfg80211_drv_mutex);
 
-       rtnl_lock();
-       err = drv->ops->add_virtual_intf(&drv->wiphy,
-               nla_data(info->attrs[NL80211_ATTR_IFNAME]), type);
-       rtnl_unlock();
+       cb->args[0] = wp_idx;
+       cb->args[1] = if_idx;
 
- unlock:
-       cfg80211_put_dev(drv);
-       return err;
+       return skb->len;
 }
 
-static int nl80211_del_virt_intf(struct sk_buff *skb, struct genl_info *info)
+static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
 {
-       struct cfg80211_registered_device *drv;
-       int ifindex, err;
-       struct net_device *dev;
+       struct sk_buff *msg;
+       struct cfg80211_registered_device *dev;
+       struct net_device *netdev;
+       int err;
 
-       err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
+       err = get_drv_dev_by_info_ifindex(info, &dev, &netdev);
        if (err)
                return err;
-       ifindex = dev->ifindex;
-       dev_put(dev);
 
-       if (!drv->ops->del_virtual_intf) {
-               err = -EOPNOTSUPP;
-               goto out;
-       }
+       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (!msg)
+               goto out_err;
 
-       rtnl_lock();
-       err = drv->ops->del_virtual_intf(&drv->wiphy, ifindex);
-       rtnl_unlock();
+       if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0, netdev) < 0)
+               goto out_free;
 
- out:
-       cfg80211_put_dev(drv);
-       return err;
+       dev_put(netdev);
+       cfg80211_put_dev(dev);
+
+       return genlmsg_unicast(msg, info->snd_pid);
+
+ out_free:
+       nlmsg_free(msg);
+ out_err:
+       dev_put(netdev);
+       cfg80211_put_dev(dev);
+       return -ENOBUFS;
 }
 
-static int nl80211_change_virt_intf(struct sk_buff *skb, struct genl_info *info)
+static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *drv;
        int err, ifindex;
-       unsigned int type;
+       enum nl80211_iftype type;
        struct net_device *dev;
 
        if (info->attrs[NL80211_ATTR_IFTYPE]) {
@@ -383,588 +275,128 @@ static int nl80211_change_virt_intf(struct sk_buff *skb, struct genl_info *info)
        return err;
 }
 
-static int nl80211_get_association(struct sk_buff *skb, struct genl_info *info)
+static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *drv;
        int err;
-       struct net_device *dev;
-       struct sk_buff *msg;
-       void *hdr;
-       u8 bssid[ETH_ALEN];
-
-       err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
-       if (err)
-               return err;
-
-       if (!drv->ops->get_association) {
-               err = -EOPNOTSUPP;
-               goto out_put_drv;
-       }
-
-       rtnl_lock();
-       err = drv->ops->get_association(&drv->wiphy, dev, bssid);
-       rtnl_unlock();
-       if (err < 0)
-               goto out_put_drv;
-
-       hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
-                            NL80211_CMD_ASSOCIATION_CHANGED);
-
-       if (IS_ERR(hdr)) {
-               err = PTR_ERR(hdr);
-               goto out_put_drv;
-       }
+       enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
 
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
-       if (err == 1)
-               NLA_PUT(msg, NL80211_ATTR_BSSID, ETH_ALEN, bssid);
-
-       genlmsg_end(msg, hdr);
-       err = genlmsg_unicast(msg, info->snd_pid);
-       goto out_put_drv;
-
- nla_put_failure:
-       err = -ENOBUFS;
-       nlmsg_free(msg);
- out_put_drv:
-       cfg80211_put_dev(drv);
-       dev_put(dev);
-       return err;
-}
-
-static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
-{
-       struct cfg80211_registered_device *drv;
-       int err;
-       struct net_device *dev;
-       struct association_params assoc_params;
-
-       memset(&assoc_params, 0, sizeof(assoc_params));
-
-       err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
-       if (err)
-               return err;
-
-       if (!drv->ops->associate) {
-               err = -EOPNOTSUPP;
-               goto out;
-       }
-
-       if (!info->attrs[NL80211_ATTR_SSID])
+       if (!info->attrs[NL80211_ATTR_IFNAME])
                return -EINVAL;
 
-       assoc_params.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
-       assoc_params.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
-
-       if (info->attrs[NL80211_ATTR_BSSID])
-               assoc_params.bssid = nla_data(info->attrs[NL80211_ATTR_BSSID]);
-
-       if (info->attrs[NL80211_ATTR_IE]) {
-               err = check_information_element(info->attrs[NL80211_ATTR_IE]);
-               if (err)
-                       goto out;
-               assoc_params.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
-               assoc_params.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
-       }
-
-       if (info->attrs[NL80211_ATTR_TIMEOUT_TU]) {
-               assoc_params.timeout =
-                       nla_get_u32(info->attrs[NL80211_ATTR_TIMEOUT_TU]);
-               assoc_params.valid |= ASSOC_PARAMS_TIMEOUT;
-       }
-
-       rtnl_lock();
-       err = drv->ops->associate(&drv->wiphy, dev, &assoc_params);
-       rtnl_unlock();
-
- out:
-       cfg80211_put_dev(drv);
-       dev_put(dev);
-       return err;
-}
-
-static int nl80211_disassoc_deauth(struct sk_buff *skb, struct genl_info *info)
-{
-       struct cfg80211_registered_device *drv;
-       int err;
-       struct net_device *dev;
-       int (*act)(struct wiphy *wiphy, struct net_device *dev);
-
-       err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
-       if (err)
-               return err;
-
-       switch (info->genlhdr->cmd) {
-       case NL80211_CMD_DISASSOCIATE:
-               act = drv->ops->disassociate;
-               break;
-       case NL80211_CMD_DEAUTH:
-               act = drv->ops->deauth;
-               break;
-       default:
-               act = NULL;
-       }
-
-       if (!act) {
-               err = -EOPNOTSUPP;
-               goto out;
+       if (info->attrs[NL80211_ATTR_IFTYPE]) {
+               type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
+               if (type > NL80211_IFTYPE_MAX)
+                       return -EINVAL;
        }
 
-       rtnl_lock();
-       err = act(&drv->wiphy, dev);
-       rtnl_unlock();
- out:
-       cfg80211_put_dev(drv);
-       dev_put(dev);
-       return err;
-}
-
-struct add_cb_data {
-       int idx;
-       struct sk_buff *skb;
-};
-
-static int add_bssid(void *data, u8 *bssid)
-{
-       struct add_cb_data *cb = data;
-       int err = -ENOBUFS;
-       struct nlattr *start;
-
-       start = nla_nest_start(cb->skb, cb->idx++);
-       if (!start)
-               goto nla_put_failure;
-
-       NLA_PUT(cb->skb, NL80211_ATTR_BSSID, ETH_ALEN, bssid);
-
-       nla_nest_end(cb->skb, start);
-       err = 0;
-
- nla_put_failure:
-       return err;
-}
-
-static int nl80211_get_auth_list(struct sk_buff *skb, struct genl_info *info)
-{
-       struct cfg80211_registered_device *drv;
-       struct net_device *dev;
-       struct sk_buff *msg;
-       void *hdr;
-       int err;
-       struct nlattr *start;
-       struct add_cb_data cb;
-
-       err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
-       if (err)
-               return err;
+       drv = cfg80211_get_dev_from_info(info);
+       if (IS_ERR(drv))
+               return PTR_ERR(drv);
 
-       if (!drv->ops->get_auth_list) {
+       if (!drv->ops->add_virtual_intf) {
                err = -EOPNOTSUPP;
-               goto put_drv;
-       }
-
-       hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
-                            NL80211_CMD_NEW_AUTH_LIST);
-       if (IS_ERR(hdr)) {
-               err = PTR_ERR(hdr);
-               goto put_drv;
-       }
-
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
-
-       start = nla_nest_start(msg, NL80211_ATTR_BSS_LIST);
-       if (!start) {
-               err = -ENOBUFS;
-               goto msg_free;
+               goto unlock;
        }
 
-       cb.skb = msg;
-       cb.idx = 1;
        rtnl_lock();
-       err = drv->ops->get_auth_list(&drv->wiphy, dev, &cb, add_bssid);
+       err = drv->ops->add_virtual_intf(&drv->wiphy,
+               nla_data(info->attrs[NL80211_ATTR_IFNAME]), type);
        rtnl_unlock();
-       if (err)
-               goto msg_free;
-
-       nla_nest_end(msg, start);
-
-       genlmsg_end(msg, hdr);
 
-       err = genlmsg_unicast(msg, info->snd_pid);
-       goto put_drv;
-
- nla_put_failure:
-       err = -ENOBUFS;
- msg_free:
-       nlmsg_free(msg);
- put_drv:
+ unlock:
        cfg80211_put_dev(drv);
-       dev_put(dev);
        return err;
 }
 
-static int nl80211_initiate_scan(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 *drv;
-       int err;
+       int ifindex, err;
        struct net_device *dev;
-       struct scan_params params;
-       struct scan_channel *channels = NULL;
-       int count = -1;
-
-       if (info->attrs[NL80211_ATTR_PHYMODE])
-               params.phymode = nla_get_u32(info->attrs[NL80211_ATTR_PHYMODE]);
-
-       if (params.phymode > NL80211_PHYMODE_MAX)
-               return -EINVAL;
 
        err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
        if (err)
                return err;
-
-       if (!drv->ops->initiate_scan) {
-               err = -EOPNOTSUPP;
-               goto out;
-       }
-
-       params.active = nla_get_flag(info->attrs[NL80211_ATTR_FLAG_SCAN_ACTIVE]);
-
-       if (info->attrs[NL80211_ATTR_CHANNEL_LIST]) {
-               struct nlattr *attr = info->attrs[NL80211_ATTR_CHANNEL_LIST];
-               struct nlattr *nla;
-               int rem;
-               struct nlattr **tb;
-
-               /* let's count first */
-               count = 0;
-               nla_for_each_attr(nla, nla_data(attr), nla_len(attr), rem)
-                       count++;
-
-               if (count == 0) {
-                       /* assume we should actually scan all channels,
-                        * scanning no channels make no sense */
-                       count = -1;
-                       goto done_channels;
-               }
-
-               if (count > NL80211_MAX_CHANNEL_LIST_ITEM) {
-                       err = -EINVAL;
-                       goto out;
-               }
-
-               channels = kmalloc(count * sizeof(struct scan_channel),
-                                  GFP_KERNEL);
-               tb = kmalloc((NL80211_ATTR_MAX+1) * sizeof(struct nlattr),
-                            GFP_KERNEL);
-
-               count = 0;
-               nla_for_each_attr(nla, nla_data(attr), nla_len(attr), rem) {
-                       err = nla_parse(tb, NL80211_ATTR_MAX, nla_data(nla),
-                                       nla_len(nla), nl80211_policy);
-
-                       if (err || !tb[NL80211_ATTR_CHANNEL]) {
-                               err = -EINVAL;
-                               kfree(tb);
-                               kfree(channels);
-                               goto out;
-                       }
-
-                       channels[count].phymode = params.phymode;
-
-                       if (tb[NL80211_ATTR_PHYMODE])
-                               channels[count].phymode =
-                                       nla_get_u32(tb[NL80211_ATTR_PHYMODE]);
-
-                       if (channels[count].phymode > NL80211_PHYMODE_MAX) {
-                               err = -EINVAL;
-                               kfree(tb);
-                               kfree(channels);
-                               goto out;
-                       }
-
-                       channels[count].channel =
-                               nla_get_u32(tb[NL80211_ATTR_CHANNEL]);
-
-                       channels[count].active =
-                               nla_get_flag(tb[NL80211_ATTR_FLAG_SCAN_ACTIVE]);
-                       count++;
-               }
-               kfree(tb);
-       }
-
- done_channels:
-       params.channels = channels;
-       params.n_channels = count;
-
-       rtnl_lock();
-       err = drv->ops->initiate_scan(&drv->wiphy, dev, &params);
-       rtnl_unlock();
-
-       kfree(channels);
- out:
-       cfg80211_put_dev(drv);
+       ifindex = dev->ifindex;
        dev_put(dev);
-       return err;
-}
-
-static int nl80211_rename_wiphy(struct sk_buff *skb, struct genl_info *info)
-{
-       struct cfg80211_registered_device *rdev;
-       int result;
-
-       if (!info->attrs[NL80211_ATTR_WIPHY_NAME])
-               return -EINVAL;
-
-       rdev = cfg80211_get_dev_from_info(info);
-       if (IS_ERR(rdev))
-               return PTR_ERR(rdev);
-
-       result = cfg80211_dev_rename(rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
-
-       cfg80211_put_dev(rdev);
-       return result;
-}
-
-static int nl80211_key_cmd(struct sk_buff *skb, struct genl_info *info)
-{
-       struct cfg80211_registered_device *drv;
-       int err, del;
-       struct net_device *dev;
-       struct key_params params;
-       int (*act)(struct wiphy *wiphy, struct net_device *dev,
-                  struct key_params *params);
-
-       memset(&params, 0, sizeof(params));
-
-       if (!info->attrs[NL80211_ATTR_KEY_TYPE])
-               return -EINVAL;
-
-       if (!info->attrs[NL80211_ATTR_KEY_CIPHER])
-               return -EINVAL;
 
-       params.key_type = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]);
-       if (params.key_type > NL80211_KEYTYPE_MAX)
-               return -EINVAL;
-
-       err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
-       if (err)
-               return err;
-
-       switch (info->genlhdr->cmd) {
-       case NL80211_CMD_ADD_KEY:
-               act = drv->ops->add_key;
-               del = 0;
-               break;
-       case NL80211_CMD_DEL_KEY:
-               act = drv->ops->del_key;
-               del = 1;
-               break;
-       default:
-               act = NULL;
-       }
-
-       if (!act) {
+       if (!drv->ops->del_virtual_intf) {
                err = -EOPNOTSUPP;
                goto out;
        }
 
-       if (info->attrs[NL80211_ATTR_KEY_DATA]) {
-               params.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]);
-               params.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]);
-       }
-
-       if (info->attrs[NL80211_ATTR_KEY_ID]) {
-               params.key_id = nla_get_u32(info->attrs[NL80211_ATTR_KEY_ID]);
-       } else {
-               params.key_id = -1;
-       }
-
-       params.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]);
-
-       if (info->attrs[NL80211_ATTR_MAC]) {
-               params.macaddress = nla_data(info->attrs[NL80211_ATTR_MAC]);
-       } else {
-               params.macaddress = NULL;
-       }
-
        rtnl_lock();
-       err = act(&drv->wiphy, dev, &params);
+       err = drv->ops->del_virtual_intf(&drv->wiphy, ifindex);
        rtnl_unlock();
 
  out:
        cfg80211_put_dev(drv);
-       dev_put(dev);
        return err;
 }
 
 static struct genl_ops nl80211_ops[] = {
        {
-               .cmd = NL80211_CMD_RENAME_WIPHY,
-               .doit = nl80211_rename_wiphy,
-               .policy = nl80211_policy,
-               .flags = GENL_ADMIN_PERM,
-       },
-       {
-               .cmd = NL80211_CMD_GET_CMDLIST,
-               .doit = nl80211_get_cmdlist,
-               .policy = nl80211_policy,
-               /* can be retrieved by unprivileged users */
-       },
-       {
-               .cmd = NL80211_CMD_ADD_VIRTUAL_INTERFACE,
-               .doit = nl80211_add_virt_intf,
-               .policy = nl80211_policy,
-               .flags = GENL_ADMIN_PERM,
-       },
-       {
-               .cmd = NL80211_CMD_DEL_VIRTUAL_INTERFACE,
-               .doit = nl80211_del_virt_intf,
-               .policy = nl80211_policy,
-               .flags = GENL_ADMIN_PERM,
-       },
-       {
-               .cmd = NL80211_CMD_CHANGE_VIRTUAL_INTERFACE,
-               .doit = nl80211_change_virt_intf,
-               .policy = nl80211_policy,
-               .flags = GENL_ADMIN_PERM,
-       },
-       {
-               .cmd = NL80211_CMD_GET_WIPHYS,
-               .doit = nl80211_get_wiphys,
+               .cmd = NL80211_CMD_GET_WIPHY,
+               .doit = nl80211_get_wiphy,
+               .dumpit = nl80211_dump_wiphy,
                .policy = nl80211_policy,
                /* can be retrieved by unprivileged users */
        },
        {
-               .cmd = NL80211_CMD_GET_INTERFACES,
-               .doit = nl80211_get_intfs,
-               .policy = nl80211_policy,
-               /* can be retrieved by unprivileged users */
-       },
-       {
-               .cmd = NL80211_CMD_INITIATE_SCAN,
-               .doit = nl80211_initiate_scan,
+               .cmd = NL80211_CMD_SET_WIPHY,
+               .doit = nl80211_set_wiphy,
                .policy = nl80211_policy,
                .flags = GENL_ADMIN_PERM,
        },
        {
-               .cmd = NL80211_CMD_GET_ASSOCIATION,
-               .doit = nl80211_get_association,
+               .cmd = NL80211_CMD_GET_INTERFACE,
+               .doit = nl80211_get_interface,
+               .dumpit = nl80211_dump_interface,
                .policy = nl80211_policy,
                /* can be retrieved by unprivileged users */
        },
        {
-               .cmd = NL80211_CMD_ASSOCIATE,
-               .doit = nl80211_associate,
+               .cmd = NL80211_CMD_SET_INTERFACE,
+               .doit = nl80211_set_interface,
                .policy = nl80211_policy,
                .flags = GENL_ADMIN_PERM,
        },
        {
-               .cmd = NL80211_CMD_DISASSOCIATE,
-               .doit = nl80211_disassoc_deauth,
+               .cmd = NL80211_CMD_NEW_INTERFACE,
+               .doit = nl80211_new_interface,
                .policy = nl80211_policy,
                .flags = GENL_ADMIN_PERM,
        },
        {
-               .cmd = NL80211_CMD_DEAUTH,
-               .doit = nl80211_disassoc_deauth,
-               .policy = nl80211_policy,
-               .flags = GENL_ADMIN_PERM,
-       },
-       {
-               .cmd = NL80211_CMD_GET_AUTH_LIST,
-               .doit = nl80211_get_auth_list,
-               .policy = nl80211_policy,
-               /* can be retrieved by unprivileged users */
-       },
-/*
-       {
-               .cmd = NL80211_CMD_AP_SET_BEACON,
-               .policy = nl80211_policy,
-               .flags = GENL_ADMIN_PERM,
-       },
-       {
-               .cmd = NL80211_CMD_AP_ADD_STA,
-               .policy = nl80211_policy,
-               .flags = GENL_ADMIN_PERM,
-       },
-       {
-               .cmd = NL80211_CMD_AP_UPDATE_STA,
-               .policy = nl80211_policy,
-               .flags = GENL_ADMIN_PERM,
-       },
-       {
-               .cmd = NL80211_CMD_AP_GET_STA_INFO,
-               .policy = nl80211_policy,
-               .flags = GENL_ADMIN_PERM,
-       },
-       {
-               .cmd = NL80211_CMD_AP_SET_RATESETS,
-               .policy = nl80211_policy,
-               .flags = GENL_ADMIN_PERM,
-       },
-*/
-       {
-               .cmd = NL80211_CMD_ADD_KEY,
-               .doit = nl80211_key_cmd,
-               .policy = nl80211_policy,
-               .flags = GENL_ADMIN_PERM,
-       },
-       {
-               .cmd = NL80211_CMD_DEL_KEY,
-               .doit = nl80211_key_cmd,
+               .cmd = NL80211_CMD_DEL_INTERFACE,
+               .doit = nl80211_del_interface,
                .policy = nl80211_policy,
                .flags = GENL_ADMIN_PERM,
        },
 };
 
-
-/* exported functions */
-
-void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq, int flags, u8 cmd)
-{
-       /* since there is no private header just add the generic one */
-       return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd);
-}
-EXPORT_SYMBOL_GPL(nl80211hdr_put);
-
-void *nl80211msg_new(struct sk_buff **skb, u32 pid, u32 seq, int flags, u8 cmd)
-{
-       void *hdr;
-
-       *skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
-       if (!*skb)
-               return ERR_PTR(-ENOBUFS);
-
-       hdr = nl80211hdr_put(*skb, pid, seq, flags, cmd);
-       if (!hdr) {
-               nlmsg_free(*skb);
-               return ERR_PTR(-ENOBUFS);
-       }
-
-       return hdr;
-}
-EXPORT_SYMBOL_GPL(nl80211msg_new);
+/* multicast groups */
+static struct genl_multicast_group nl80211_config_mcgrp = {
+       .name = "config",
+};
 
 /* notification functions */
 
 void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
 {
        struct sk_buff *msg;
-       void *hdr;
 
-       hdr = nl80211msg_new(&msg, 0, 0, 0, NL80211_CMD_WIPHY_NEWNAME);
-       if (IS_ERR(hdr))
+       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (!msg)
                return;
 
-       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->idx);
-       NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&rdev->wiphy));
-
-       genlmsg_end(msg, hdr);
-       genlmsg_multicast(msg, 0, NL80211_GROUP_CONFIG, GFP_KERNEL);
-
-       return;
+       if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) {
+               nlmsg_free(msg);
+               return;
+       }
 
- nla_put_failure:
-       nlmsg_free(msg);
+       genlmsg_multicast(msg, 0, nl80211_config_mcgrp.id, GFP_KERNEL);
 }
 
 /* initialisation/exit functions */
@@ -982,6 +414,11 @@ int nl80211_init(void)
                if (err)
                        goto err_out;
        }
+
+       err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp);
+       if (err)
+               goto err_out;
+
        return 0;
  err_out:
        genl_unregister_family(&nl80211_fam);