#include <linux/if.h>
#include <linux/module.h>
#include <linux/err.h>
-#include <linux/mutex.h>
#include <linux/list.h>
#include <linux/if_ether.h>
#include <linux/ieee80211.h>
#include <linux/nl80211.h>
#include <linux/rtnetlink.h>
#include <linux/netlink.h>
+#include <linux/etherdevice.h>
#include <net/genetlink.h>
#include <net/cfg80211.h>
#include "core.h"
[NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 },
[NL80211_ATTR_IE] = { .type = NLA_BINARY,
.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 },
};
/* message building helper */
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;
if (!hdr)
return -1;
- NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->idx);
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx);
NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
+ NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
+ dev->wiphy.max_scan_ssids);
nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES);
if (!nl_modes)
}
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(set_mgmt_extra_ie, SET_MGMT_EXTRA_IE);
+ 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:
int start = cb->args[0];
struct cfg80211_registered_device *dev;
- mutex_lock(&cfg80211_drv_mutex);
+ mutex_lock(&cfg80211_mutex);
list_for_each_entry(dev, &cfg80211_drv_list, list) {
if (++idx <= start)
continue;
break;
}
}
- mutex_unlock(&cfg80211_drv_mutex);
+ mutex_unlock(&cfg80211_mutex);
cb->args[0] = idx;
struct cfg80211_registered_device *dev;
struct wireless_dev *wdev;
- mutex_lock(&cfg80211_drv_mutex);
+ mutex_lock(&cfg80211_mutex);
list_for_each_entry(dev, &cfg80211_drv_list, list) {
if (wp_idx < wp_start) {
wp_idx++;
wp_idx++;
}
out:
- mutex_unlock(&cfg80211_drv_mutex);
+ mutex_unlock(&cfg80211_mutex);
cb->args[0] = wp_idx;
cb->args[1] = if_idx;
memset(¶ms, 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);
if (!err)
flags = &_flags;
}
- rtnl_lock();
+
err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex,
type, flags, ¶ms);
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;
}
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))) {
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, ¶ms);
- rtnl_unlock();
-
unlock:
cfg80211_put_dev(drv);
+ unlock_rtnl:
+ rtnl_unlock();
return err;
}
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);
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;
}
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;
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;
out:
cfg80211_put_dev(drv);
dev_put(dev);
+ unlock_rtnl:
+ rtnl_unlock();
+
return err;
}
!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;
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;
}
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, ¶ms);
- rtnl_unlock();
out:
cfg80211_put_dev(drv);
dev_put(dev);
+ unlock_rtnl:
+ rtnl_unlock();
+
return err;
}
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;
}
struct beacon_parameters params;
int haveinfo = 0;
+ rtnl_lock();
+
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
if (err)
- return err;
+ goto unlock_rtnl;
switch (info->genlhdr->cmd) {
case NL80211_CMD_NEW_BEACON:
goto out;
}
- rtnl_lock();
err = call(&drv->wiphy, dev, ¶ms);
- rtnl_unlock();
out:
cfg80211_put_dev(drv);
dev_put(dev);
+ unlock_rtnl:
+ rtnl_unlock();
+
return err;
}
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();
err = drv->ops->del_beacon(&drv->wiphy, dev);
- rtnl_unlock();
out:
cfg80211_put_dev(drv);
dev_put(dev);
+ unlock_rtnl:
+ rtnl_unlock();
+
return err;
}
nla_nest_end(msg, txrate);
}
+ if (sinfo->filled & STATION_INFO_RX_PACKETS)
+ NLA_PUT_U32(msg, NL80211_STA_INFO_RX_PACKETS,
+ sinfo->rx_packets);
+ if (sinfo->filled & STATION_INFO_TX_PACKETS)
+ NLA_PUT_U32(msg, NL80211_STA_INFO_TX_PACKETS,
+ sinfo->tx_packets);
nla_nest_end(msg, sinfoattr);
return genlmsg_end(msg, hdr);
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) {
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,
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;
}
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;
out_free:
nlmsg_free(msg);
-
out:
cfg80211_put_dev(drv);
dev_put(dev);
+ out_rtnl:
+ rtnl_unlock();
+
return err;
}
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, ¶ms.vlan);
if (err)
goto out;
}
- rtnl_lock();
err = drv->ops->change_station(&drv->wiphy, dev, mac_addr, ¶ms);
- rtnl_unlock();
out:
if (params.vlan)
dev_put(params.vlan);
cfg80211_put_dev(drv);
dev_put(dev);
+ out_rtnl:
+ rtnl_unlock();
+
return err;
}
¶ms.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, ¶ms.vlan);
if (err)
goto out;
}
- rtnl_lock();
err = drv->ops->add_station(&drv->wiphy, dev, mac_addr, ¶ms);
- rtnl_unlock();
out:
if (params.vlan)
dev_put(params.vlan);
cfg80211_put_dev(drv);
dev_put(dev);
+ out_rtnl:
+ rtnl_unlock();
+
return err;
}
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;
}
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) {
goto out_err;
}
- rtnl_lock();
-
while (1) {
err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx,
dst, next_hop, &pinfo);
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,
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;
}
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 (err)
goto out;
out_free:
nlmsg_free(msg);
-
out:
cfg80211_put_dev(drv);
dev_put(dev);
+ out_rtnl:
+ rtnl_unlock();
+
return err;
}
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();
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)
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();
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;
}
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;
}
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();
err = drv->ops->change_bss(&drv->wiphy, dev, ¶ms);
- rtnl_unlock();
out:
cfg80211_put_dev(drv);
dev_put(dev);
+ out_rtnl:
+ rtnl_unlock();
+
return err;
}
int r;
char *data = NULL;
+ /*
+ * You should only get this when cfg80211 hasn't yet initialized
+ * completely when built-in to the kernel right between the time
+ * window between nl80211_init() and regulatory_init(), if that is
+ * even possible.
+ */
+ mutex_lock(&cfg80211_mutex);
+ if (unlikely(!cfg80211_regdomain)) {
+ mutex_unlock(&cfg80211_mutex);
+ return -EINPROGRESS;
+ }
+ mutex_unlock(&cfg80211_mutex);
+
if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
return -EINVAL;
if (is_world_regdom(data))
return -EINVAL;
#endif
- mutex_lock(&cfg80211_drv_mutex);
- r = __regulatory_hint(NULL, REGDOM_SET_BY_USER, data, 0, ENVIRON_ANY);
- mutex_unlock(&cfg80211_drv_mutex);
- /* This means the regulatory domain was already set, however
- * we don't want to confuse userspace with a "successful error"
- * message so lets just treat it as a success */
- if (r == -EALREADY)
- r = 0;
+
+ r = regulatory_hint_user(data);
+
return r;
}
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;
+ goto out;
+ }
/* Get the mesh params */
- rtnl_lock();
err = drv->ops->get_mesh_params(&drv->wiphy, dev, &cur_params);
- rtnl_unlock();
if (err)
goto out;
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;
}
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;
+ goto out;
+ }
/* This makes sure that there aren't more than 32 mesh config
* parameters (otherwise our bitfield scheme would not work.) */
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;
}
unsigned int i;
int err = -EINVAL;
- mutex_lock(&cfg80211_drv_mutex);
+ mutex_lock(&cfg80211_mutex);
if (!cfg80211_regdomain)
goto out;
genlmsg_cancel(msg, hdr);
err = -EMSGSIZE;
out:
- mutex_unlock(&cfg80211_drv_mutex);
+ mutex_unlock(&cfg80211_mutex);
return err;
}
BUG_ON(rule_idx != num_rules);
- mutex_lock(&cfg80211_drv_mutex);
+ mutex_lock(&cfg80211_mutex);
r = set_regdom(rd);
- mutex_unlock(&cfg80211_drv_mutex);
+ mutex_unlock(&cfg80211_mutex);
return r;
bad_reg:
params.ies_len = nla_len(info->attrs[NL80211_ATTR_IE]);
}
+ rtnl_lock();
+
err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
if (err)
- return err;
+ goto out_rtnl;
- if (drv->ops->set_mgmt_extra_ie) {
- rtnl_lock();
+ if (drv->ops->set_mgmt_extra_ie)
err = drv->ops->set_mgmt_extra_ie(&drv->wiphy, dev, ¶ms);
- rtnl_unlock();
- } else
+ else
err = -EOPNOTSUPP;
cfg80211_put_dev(drv);
dev_put(dev);
+ out_rtnl:
+ rtnl_unlock();
+
return err;
}
-static struct genl_ops nl80211_ops[] = {
- {
- .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_SET_WIPHY,
- .doit = nl80211_set_wiphy,
- .policy = nl80211_policy,
- .flags = GENL_ADMIN_PERM,
- },
- {
- .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_SET_INTERFACE,
- .doit = nl80211_set_interface,
- .policy = nl80211_policy,
- .flags = GENL_ADMIN_PERM,
- },
- {
- .cmd = NL80211_CMD_NEW_INTERFACE,
- .doit = nl80211_new_interface,
- .policy = nl80211_policy,
- .flags = GENL_ADMIN_PERM,
- },
- {
- .cmd = NL80211_CMD_DEL_INTERFACE,
- .doit = nl80211_del_interface,
- .policy = nl80211_policy,
- .flags = GENL_ADMIN_PERM,
- },
- {
- .cmd = NL80211_CMD_GET_KEY,
- .doit = nl80211_get_key,
- .policy = nl80211_policy,
- .flags = GENL_ADMIN_PERM,
- },
- {
- .cmd = NL80211_CMD_SET_KEY,
- .doit = nl80211_set_key,
- .policy = nl80211_policy,
- .flags = GENL_ADMIN_PERM,
- },
- {
- .cmd = NL80211_CMD_NEW_KEY,
+static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *drv;
+ struct net_device *dev;
+ struct cfg80211_scan_request *request;
+ struct cfg80211_ssid *ssid;
+ struct ieee80211_channel *channel;
+ struct nlattr *attr;
+ struct wiphy *wiphy;
+ int err, tmp, n_ssids = 0, n_channels = 0, i;
+ enum ieee80211_band band;
+ size_t ie_len;
+
+ rtnl_lock();
+
+ err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+ if (err)
+ goto out_rtnl;
+
+ wiphy = &drv->wiphy;
+
+ if (!drv->ops->scan) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (drv->scan_req) {
+ err = -EBUSY;
+ goto out;
+ }
+
+ if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
+ nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp)
+ n_channels++;
+ if (!n_channels) {
+ err = -EINVAL;
+ goto out;
+ }
+ } else {
+ for (band = 0; band < IEEE80211_NUM_BANDS; band++)
+ if (wiphy->bands[band])
+ n_channels += wiphy->bands[band]->n_channels;
+ }
+
+ if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
+ nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp)
+ n_ssids++;
+
+ if (n_ssids > wiphy->max_scan_ssids) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (info->attrs[NL80211_ATTR_IE])
+ ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+ else
+ ie_len = 0;
+
+ request = kzalloc(sizeof(*request)
+ + sizeof(*ssid) * n_ssids
+ + sizeof(channel) * n_channels
+ + ie_len, GFP_KERNEL);
+ if (!request) {
+ err = -ENOMEM;
+ 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->n_ssids = n_ssids;
+ if (ie_len) {
+ if (request->ssids)
+ request->ie = (void *)(request->ssids + n_ssids);
+ else
+ request->ie = (void *)(request->channels + n_channels);
+ }
+
+ 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]) {
+ err = -EINVAL;
+ goto out_free;
+ }
+ 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];
+ i++;
+ }
+ }
+ }
+
+ i = 0;
+ if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
+ nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) {
+ if (request->ssids[i].ssid_len > IEEE80211_MAX_SSID_LEN) {
+ err = -EINVAL;
+ goto out_free;
+ }
+ memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr));
+ request->ssids[i].ssid_len = nla_len(attr);
+ i++;
+ }
+ }
+
+ if (info->attrs[NL80211_ATTR_IE]) {
+ request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+ memcpy(request->ie, nla_data(info->attrs[NL80211_ATTR_IE]),
+ request->ie_len);
+ }
+
+ request->ifidx = dev->ifindex;
+ request->wiphy = &drv->wiphy;
+
+ drv->scan_req = request;
+ err = drv->ops->scan(&drv->wiphy, dev, request);
+
+ out_free:
+ if (err) {
+ drv->scan_req = NULL;
+ kfree(request);
+ }
+ out:
+ cfg80211_put_dev(drv);
+ dev_put(dev);
+ out_rtnl:
+ rtnl_unlock();
+
+ return err;
+}
+
+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)
+{
+ void *hdr;
+ struct nlattr *bss;
+
+ 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);
+
+ bss = nla_nest_start(msg, NL80211_ATTR_BSS);
+ if (!bss)
+ goto nla_put_failure;
+ if (!is_zero_ether_addr(res->bssid))
+ NLA_PUT(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid);
+ if (res->information_elements && res->len_information_elements)
+ NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS,
+ res->len_information_elements,
+ res->information_elements);
+ if (res->tsf)
+ NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf);
+ if (res->beacon_interval)
+ 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);
+
+ switch (rdev->wiphy.signal_type) {
+ case CFG80211_SIGNAL_TYPE_MBM:
+ NLA_PUT_U32(msg, NL80211_BSS_SIGNAL_MBM, res->signal);
+ break;
+ case CFG80211_SIGNAL_TYPE_UNSPEC:
+ NLA_PUT_U8(msg, NL80211_BSS_SIGNAL_UNSPEC, res->signal);
+ break;
+ default:
+ break;
+ }
+
+ nla_nest_end(msg, bss);
+
+ return genlmsg_end(msg, hdr);
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ return -EMSGSIZE;
+}
+
+static int nl80211_dump_scan(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ struct cfg80211_registered_device *dev;
+ struct net_device *netdev;
+ struct cfg80211_internal_bss *scan;
+ 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;
+ }
+
+ netdev = dev_get_by_index(&init_net, ifidx);
+ if (!netdev)
+ return -ENODEV;
+
+ dev = cfg80211_get_dev_from_ifindex(ifidx);
+ if (IS_ERR(dev)) {
+ err = PTR_ERR(dev);
+ goto out_put_netdev;
+ }
+
+ spin_lock_bh(&dev->bss_lock);
+ cfg80211_bss_expire(dev);
+
+ list_for_each_entry(scan, &dev->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) {
+ idx--;
+ goto out;
+ }
+ }
+
+ out:
+ spin_unlock_bh(&dev->bss_lock);
+
+ cb->args[1] = idx;
+ err = skb->len;
+ cfg80211_put_dev(dev);
+ out_put_netdev:
+ dev_put(netdev);
+
+ return err;
+}
+
+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;
+
+ 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 (!info->attrs[NL80211_ATTR_MAC]) {
+ err = -EINVAL;
+ 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]);
+ }
+
+ if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
+ req.auth_type =
+ nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
+ }
+
+ 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;
+
+ 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 (!info->attrs[NL80211_ATTR_MAC] ||
+ !info->attrs[NL80211_ATTR_SSID]) {
+ err = -EINVAL;
+ 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 (nla_len(info->attrs[NL80211_ATTR_SSID]) > IEEE80211_MAX_SSID_LEN) {
+ 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;
+
+ 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 (!info->attrs[NL80211_ATTR_MAC]) {
+ err = -EINVAL;
+ 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_REASON_CODE])
+ req.reason_code =
+ nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
+
+ 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;
+
+ 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 (!info->attrs[NL80211_ATTR_MAC]) {
+ err = -EINVAL;
+ 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_REASON_CODE])
+ req.reason_code =
+ nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
+
+ 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,
+ .doit = nl80211_get_wiphy,
+ .dumpit = nl80211_dump_wiphy,
+ .policy = nl80211_policy,
+ /* can be retrieved by unprivileged users */
+ },
+ {
+ .cmd = NL80211_CMD_SET_WIPHY,
+ .doit = nl80211_set_wiphy,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .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_SET_INTERFACE,
+ .doit = nl80211_set_interface,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = NL80211_CMD_NEW_INTERFACE,
+ .doit = nl80211_new_interface,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = NL80211_CMD_DEL_INTERFACE,
+ .doit = nl80211_del_interface,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = NL80211_CMD_GET_KEY,
+ .doit = nl80211_get_key,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = NL80211_CMD_SET_KEY,
+ .doit = nl80211_set_key,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = NL80211_CMD_NEW_KEY,
.doit = nl80211_new_key,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
.doit = nl80211_get_station,
.dumpit = nl80211_dump_station,
.policy = nl80211_policy,
- .flags = GENL_ADMIN_PERM,
},
{
.cmd = NL80211_CMD_SET_STATION,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
},
+ {
+ .cmd = NL80211_CMD_TRIGGER_SCAN,
+ .doit = nl80211_trigger_scan,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = NL80211_CMD_GET_SCAN,
+ .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 */
static struct genl_multicast_group nl80211_config_mcgrp = {
.name = "config",
};
+static struct genl_multicast_group nl80211_scan_mcgrp = {
+ .name = "scan",
+};
+static struct genl_multicast_group nl80211_regulatory_mcgrp = {
+ .name = "regulatory",
+};
/* notification functions */
genlmsg_multicast(msg, 0, nl80211_config_mcgrp.id, GFP_KERNEL);
}
+static int nl80211_send_scan_donemsg(struct sk_buff *msg,
+ struct cfg80211_registered_device *rdev,
+ struct net_device *netdev,
+ u32 pid, u32 seq, int flags,
+ u32 cmd)
+{
+ void *hdr;
+
+ hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
+ if (!hdr)
+ return -1;
+
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
+
+ /* XXX: we should probably bounce back the request? */
+
+ return genlmsg_end(msg, hdr);
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ return -EMSGSIZE;
+}
+
+void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev)
+{
+ struct sk_buff *msg;
+
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!msg)
+ return;
+
+ if (nl80211_send_scan_donemsg(msg, rdev, netdev, 0, 0, 0,
+ NL80211_CMD_NEW_SCAN_RESULTS) < 0) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL);
+}
+
+void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev)
+{
+ struct sk_buff *msg;
+
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!msg)
+ return;
+
+ if (nl80211_send_scan_donemsg(msg, rdev, netdev, 0, 0, 0,
+ NL80211_CMD_SCAN_ABORTED) < 0) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL);
+}
+
+/*
+ * This can happen on global regulatory changes or device specific settings
+ * based on custom world regulatory domains.
+ */
+void nl80211_send_reg_change_event(struct regulatory_request *request)
+{
+ struct sk_buff *msg;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ /* Userspace can always count this one always being set */
+ NLA_PUT_U8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator);
+
+ if (request->alpha2[0] == '0' && request->alpha2[1] == '0')
+ NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
+ NL80211_REGDOM_TYPE_WORLD);
+ else if (request->alpha2[0] == '9' && request->alpha2[1] == '9')
+ NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
+ NL80211_REGDOM_TYPE_CUSTOM_WORLD);
+ else if ((request->alpha2[0] == '9' && request->alpha2[1] == '8') ||
+ request->intersect)
+ NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
+ NL80211_REGDOM_TYPE_INTERSECTION);
+ else {
+ NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
+ NL80211_REGDOM_TYPE_COUNTRY);
+ NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, request->alpha2);
+ }
+
+ if (wiphy_idx_valid(request->wiphy_idx))
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx);
+
+ if (genlmsg_end(msg, hdr) < 0) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ genlmsg_multicast(msg, 0, nl80211_regulatory_mcgrp.id, GFP_KERNEL);
+
+ return;
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ 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_KERNEL);
+ 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_KERNEL);
+ 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_rx_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_rx_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)
if (err)
goto err_out;
+ err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp);
+ if (err)
+ goto err_out;
+
+ err = genl_register_mc_group(&nl80211_fam, &nl80211_regulatory_mcgrp);
+ 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);