1
0
mirror of git://projects.qi-hardware.com/openwrt-xburst.git synced 2025-04-21 12:27:27 +03:00

New: mac80211 stack from the wireless-dev tree

git-svn-id: svn://svn.openwrt.org/openwrt/trunk@7692 3c298f89-4303-0410-b956-a3cf2f4a3e73
This commit is contained in:
noz
2007-06-21 20:42:32 +00:00
parent 6546ee2d5f
commit 9aa53bb177
50 changed files with 23705 additions and 0 deletions

View File

@@ -0,0 +1,31 @@
config CFG80211
tristate "Improved wireless configuration API"
config NL80211
bool "nl80211 new netlink interface support"
depends CFG80211
default y
---help---
This option turns on the new netlink interface
(nl80211) support in cfg80211.
If =n, drivers using mac80211 will be configured via
wireless extension support provided by that subsystem.
If unsure, say Y.
config WIRELESS_EXT
bool "Wireless extensions"
default n
---help---
This option enables the legacy wireless extensions
(wireless network interface configuration via ioctls.)
Wireless extensions will be replaced by cfg80211 and
will be required only by legacy drivers that implement
wireless extension handlers. This option does not
affect the wireless-extension backward compatibility
code in cfg80211.
Say N (if you can) unless you know you need wireless
extensions for external modules.

View File

@@ -0,0 +1,5 @@
obj-$(CONFIG_WIRELESS_EXT) += wext.o
obj-$(CONFIG_CFG80211) += cfg80211.o
cfg80211-y += core.o sysfs.o
cfg80211-$(CONFIG_NL80211) += nl80211.o

View File

@@ -0,0 +1,367 @@
/*
* This is the linux wireless configuration interface.
*
* Copyright 2006, 2007 Johannes Berg <johannes@sipsolutions.net>
*/
#include <linux/if.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/list.h>
#include <linux/nl80211.h>
#include <linux/debugfs.h>
#include <linux/notifier.h>
#include <linux/device.h>
#include <net/genetlink.h>
#include <net/cfg80211.h>
#include <net/wireless.h>
#include "nl80211.h"
#include "core.h"
#include "sysfs.h"
/* name for sysfs, %d is appended */
#define PHY_NAME "phy"
MODULE_AUTHOR("Johannes Berg");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("wireless configuration support");
/* RCU might be appropriate here since we usually
* only read the list, and that can happen quite
* often because we need to do it for each command */
LIST_HEAD(cfg80211_drv_list);
DEFINE_MUTEX(cfg80211_drv_mutex);
static int wiphy_counter;
/* for debugfs */
static struct dentry *ieee80211_debugfs_dir;
/* requires cfg80211_drv_mutex to be held! */
static struct cfg80211_registered_device *cfg80211_drv_by_wiphy(int wiphy)
{
struct cfg80211_registered_device *result = NULL, *drv;
list_for_each_entry(drv, &cfg80211_drv_list, list) {
if (drv->idx == wiphy) {
result = drv;
break;
}
}
return result;
}
/* requires cfg80211_drv_mutex to be held! */
static struct cfg80211_registered_device *
__cfg80211_drv_from_info(struct genl_info *info)
{
int ifindex;
struct cfg80211_registered_device *bywiphy = NULL, *byifidx = NULL;
struct net_device *dev;
int err = -EINVAL;
if (info->attrs[NL80211_ATTR_WIPHY]) {
bywiphy = cfg80211_drv_by_wiphy(
nla_get_u32(info->attrs[NL80211_ATTR_WIPHY]));
err = -ENODEV;
}
if (info->attrs[NL80211_ATTR_IFINDEX]) {
ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
dev = dev_get_by_index(ifindex);
if (dev) {
if (dev->ieee80211_ptr)
byifidx =
wiphy_to_dev(dev->ieee80211_ptr->wiphy);
dev_put(dev);
}
err = -ENODEV;
}
if (bywiphy && byifidx) {
if (bywiphy != byifidx)
return ERR_PTR(-EINVAL);
else
return bywiphy; /* == byifidx */
}
if (bywiphy)
return bywiphy;
if (byifidx)
return byifidx;
return ERR_PTR(err);
}
struct cfg80211_registered_device *
cfg80211_get_dev_from_info(struct genl_info *info)
{
struct cfg80211_registered_device *drv;
mutex_lock(&cfg80211_drv_mutex);
drv = __cfg80211_drv_from_info(info);
/* if it is not an error we grab the lock on
* it to assure it won't be going away while
* we operate on it */
if (!IS_ERR(drv))
mutex_lock(&drv->mtx);
mutex_unlock(&cfg80211_drv_mutex);
return drv;
}
struct cfg80211_registered_device *
cfg80211_get_dev_from_ifindex(int ifindex)
{
struct cfg80211_registered_device *drv = ERR_PTR(-ENODEV);
struct net_device *dev;
mutex_lock(&cfg80211_drv_mutex);
dev = dev_get_by_index(ifindex);
if (!dev)
goto out;
if (dev->ieee80211_ptr) {
drv = wiphy_to_dev(dev->ieee80211_ptr->wiphy);
mutex_lock(&drv->mtx);
} else
drv = ERR_PTR(-ENODEV);
dev_put(dev);
out:
mutex_unlock(&cfg80211_drv_mutex);
return drv;
}
void cfg80211_put_dev(struct cfg80211_registered_device *drv)
{
BUG_ON(IS_ERR(drv));
mutex_unlock(&drv->mtx);
}
int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
char *newname)
{
int idx, taken = -1, result, digits;
/* prohibit calling the thing phy%d when %d is not its number */
sscanf(newname, PHY_NAME "%d%n", &idx, &taken);
if (taken == strlen(newname) && idx != rdev->idx) {
/* count number of places needed to print idx */
digits = 1;
while (idx /= 10)
digits++;
/*
* deny the name if it is phy<idx> where <idx> is printed
* without leading zeroes. taken == strlen(newname) here
*/
if (taken == strlen(PHY_NAME) + digits)
return -EINVAL;
}
/* this will check for collisions */
result = device_rename(&rdev->wiphy.dev, newname);
if (!result)
return result;
/* TODO: do debugfs rename! */
nl80211_notify_dev_rename(rdev);
return 0;
}
/* exported functions */
struct wiphy *wiphy_new(struct cfg80211_ops *ops, int sizeof_priv)
{
struct cfg80211_registered_device *drv;
int alloc_size;
alloc_size = sizeof(*drv) + sizeof_priv;
drv = kzalloc(alloc_size, GFP_KERNEL);
if (!drv)
return NULL;
drv->ops = ops;
mutex_lock(&cfg80211_drv_mutex);
drv->idx = wiphy_counter;
/* now increase counter for the next device unless
* it has wrapped previously */
if (wiphy_counter >= 0)
wiphy_counter++;
mutex_unlock(&cfg80211_drv_mutex);
if (unlikely(drv->idx < 0)) {
/* ugh, wrapped! */
kfree(drv);
return NULL;
}
/* give it a proper name */
snprintf(drv->wiphy.dev.bus_id, BUS_ID_SIZE,
PHY_NAME "%d", drv->idx);
mutex_init(&drv->mtx);
mutex_init(&drv->devlist_mtx);
INIT_LIST_HEAD(&drv->netdev_list);
device_initialize(&drv->wiphy.dev);
drv->wiphy.dev.class = &ieee80211_class;
drv->wiphy.dev.platform_data = drv;
return &drv->wiphy;
}
EXPORT_SYMBOL(wiphy_new);
int wiphy_register(struct wiphy *wiphy)
{
struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy);
int res;
mutex_lock(&cfg80211_drv_mutex);
res = device_add(&drv->wiphy.dev);
if (res)
goto out_unlock;
list_add(&drv->list, &cfg80211_drv_list);
/* add to debugfs */
drv->wiphy.debugfsdir =
debugfs_create_dir(wiphy_name(&drv->wiphy),
ieee80211_debugfs_dir);
res = 0;
out_unlock:
mutex_unlock(&cfg80211_drv_mutex);
return res;
}
EXPORT_SYMBOL(wiphy_register);
void wiphy_unregister(struct wiphy *wiphy)
{
struct cfg80211_registered_device *drv = wiphy_to_dev(wiphy);
/* protect the device list */
mutex_lock(&cfg80211_drv_mutex);
BUG_ON(!list_empty(&drv->netdev_list));
/*
* Try to grab drv->mtx. If a command is still in progress,
* hopefully the driver will refuse it since it's tearing
* down the device already. We wait for this command to complete
* before unlinking the item from the list.
* Note: as codified by the BUG_ON above we cannot get here if
* a virtual interface is still associated. Hence, we can only
* get to lock contention here if userspace issues a command
* that identified the hardware by wiphy index.
*/
mutex_lock(&drv->mtx);
/* unlock again before freeing */
mutex_unlock(&drv->mtx);
list_del(&drv->list);
device_del(&drv->wiphy.dev);
debugfs_remove(drv->wiphy.debugfsdir);
mutex_unlock(&cfg80211_drv_mutex);
}
EXPORT_SYMBOL(wiphy_unregister);
void cfg80211_dev_free(struct cfg80211_registered_device *drv)
{
mutex_destroy(&drv->mtx);
mutex_destroy(&drv->devlist_mtx);
kfree(drv);
}
void wiphy_free(struct wiphy *wiphy)
{
put_device(&wiphy->dev);
}
EXPORT_SYMBOL(wiphy_free);
static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
unsigned long state,
void *ndev)
{
struct net_device *dev = ndev;
struct cfg80211_registered_device *rdev;
if (!dev->ieee80211_ptr)
return 0;
rdev = wiphy_to_dev(dev->ieee80211_ptr->wiphy);
switch (state) {
case NETDEV_REGISTER:
mutex_lock(&rdev->devlist_mtx);
list_add(&dev->ieee80211_ptr->list, &rdev->netdev_list);
if (sysfs_create_link(&dev->dev.kobj, &rdev->wiphy.dev.kobj,
"phy80211")) {
printk(KERN_ERR "wireless: failed to add phy80211 "
"symlink to netdev!\n");
}
dev->ieee80211_ptr->netdev = dev;
mutex_unlock(&rdev->devlist_mtx);
break;
case NETDEV_UNREGISTER:
mutex_lock(&rdev->devlist_mtx);
if (!list_empty(&dev->ieee80211_ptr->list)) {
sysfs_remove_link(&dev->dev.kobj, "phy80211");
list_del_init(&dev->ieee80211_ptr->list);
}
mutex_unlock(&rdev->devlist_mtx);
break;
}
return 0;
}
static struct notifier_block cfg80211_netdev_notifier = {
.notifier_call = cfg80211_netdev_notifier_call,
};
static int cfg80211_init(void)
{
int err = wiphy_sysfs_init();
if (err)
goto out_fail_sysfs;
err = register_netdevice_notifier(&cfg80211_netdev_notifier);
if (err)
goto out_fail_notifier;
err = nl80211_init();
if (err)
goto out_fail_nl80211;
ieee80211_debugfs_dir = debugfs_create_dir("ieee80211", NULL);
return 0;
out_fail_nl80211:
unregister_netdevice_notifier(&cfg80211_netdev_notifier);
out_fail_notifier:
wiphy_sysfs_exit();
out_fail_sysfs:
return err;
}
module_init(cfg80211_init);
static void cfg80211_exit(void)
{
debugfs_remove(ieee80211_debugfs_dir);
nl80211_exit();
unregister_netdevice_notifier(&cfg80211_netdev_notifier);
wiphy_sysfs_exit();
}
module_exit(cfg80211_exit);

View File

@@ -0,0 +1,81 @@
/*
* Wireless configuration interface internals.
*
* Copyright 2006, 2007 Johannes Berg <johannes@sipsolutions.net>
*/
#ifndef __NET_WIRELESS_CORE_H
#define __NET_WIRELESS_CORE_H
#include <linux/mutex.h>
#include <linux/list.h>
#include <linux/netdevice.h>
#include <net/genetlink.h>
#include <net/wireless.h>
#include <net/cfg80211.h>
struct cfg80211_registered_device {
struct cfg80211_ops *ops;
struct list_head list;
/* we hold this mutex during any call so that
* we cannot do multiple calls at once, and also
* to avoid the deregister call to proceed while
* any call is in progress */
struct mutex mtx;
/* wiphy index, internal only */
int idx;
/* associate netdev list */
struct mutex devlist_mtx;
struct list_head netdev_list;
/* must be last because of the way we do wiphy_priv(),
* and it should at least be aligned to NETDEV_ALIGN */
struct wiphy wiphy __attribute__((__aligned__(NETDEV_ALIGN)));
};
static inline
struct cfg80211_registered_device *wiphy_to_dev(struct wiphy *wiphy)
{
BUG_ON(!wiphy);
return container_of(wiphy, struct cfg80211_registered_device, wiphy);
}
extern struct mutex cfg80211_drv_mutex;
extern struct list_head cfg80211_drv_list;
/*
* This function returns a pointer to the driver
* that the genl_info item that is passed refers to.
* If successful, it returns non-NULL and also locks
* the driver's mutex!
*
* This means that you need to call cfg80211_put_dev()
* before being allowed to acquire &cfg80211_drv_mutex!
*
* This is necessary because we need to lock the global
* mutex to get an item off the list safely, and then
* we lock the drv mutex so it doesn't go away under us.
*
* We don't want to keep cfg80211_drv_mutex locked
* for all the time in order to allow requests on
* other interfaces to go through at the same time.
*
* The result of this can be a PTR_ERR and hence must
* be checked with IS_ERR() for errors.
*/
extern struct cfg80211_registered_device *
cfg80211_get_dev_from_info(struct genl_info *info);
/* identical to cfg80211_get_dev_from_info but only operate on ifindex */
extern struct cfg80211_registered_device *
cfg80211_get_dev_from_ifindex(int ifindex);
extern void cfg80211_put_dev(struct cfg80211_registered_device *drv);
/* free object */
extern void cfg80211_dev_free(struct cfg80211_registered_device *drv);
extern int cfg80211_dev_rename(struct cfg80211_registered_device *drv,
char *newname);
#endif /* __NET_WIRELESS_CORE_H */

View File

@@ -0,0 +1,994 @@
/*
* This is the new netlink-based wireless configuration interface.
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
*/
#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 <net/genetlink.h>
#include <net/cfg80211.h>
#include "core.h"
#include "nl80211.h"
/* the netlink family */
static struct genl_family nl80211_fam = {
.id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */
.name = "nl80211", /* have users key off the name instead */
.hdrsize = 0, /* no private header */
.version = 1, /* no particular meaning now */
.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,
struct net_device **dev)
{
int ifindex;
if (!info->attrs[NL80211_ATTR_IFINDEX])
return -EINVAL;
ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
*dev = dev_get_by_index(ifindex);
if (!dev)
return -ENODEV;
*drv = cfg80211_get_dev_from_ifindex(ifindex);
if (IS_ERR(*drv)) {
dev_put(*dev);
return PTR_ERR(*drv);
}
return 0;
}
/* 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 },
};
/* netlink command implementations */
#define CHECK_CMD(ptr, cmd) \
if (drv->ops->ptr) \
NLA_PUT_FLAG(msg, NL80211_CMD_##cmd);
static int nl80211_get_cmdlist(struct sk_buff *skb, struct genl_info *info)
{
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;
}
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, drv->idx);
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_nest_end(msg, start);
genlmsg_end(msg, hdr);
err = genlmsg_unicast(msg, info->snd_pid);
goto put_drv;
nla_put_failure:
err = -ENOBUFS;
nlmsg_free(msg);
put_drv:
cfg80211_put_dev(drv);
return err;
}
#undef CHECK_CMD
static int nl80211_get_wiphys(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);
start = nla_nest_start(msg, NL80211_ATTR_WIPHY_LIST);
if (!start)
goto nla_outer_nest_failure;
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);
nla_nest_end(msg, start);
genlmsg_end(msg, hdr);
return genlmsg_unicast(msg, info->snd_pid);
nla_put_failure:
mutex_unlock(&cfg80211_drv_mutex);
nla_outer_nest_failure:
nlmsg_free(msg);
return -ENOBUFS;
}
static int addifidx(struct net_device *dev, struct sk_buff *skb, int *idx)
{
int err = -ENOBUFS;
struct nlattr *start;
dev_hold(dev);
start = nla_nest_start(skb, *idx++);
if (!start)
goto nla_put_failure;
NLA_PUT_U32(skb, NL80211_ATTR_IFINDEX, dev->ifindex);
NLA_PUT_STRING(skb, NL80211_ATTR_IFNAME, dev->name);
nla_nest_end(skb, start);
err = 0;
nla_put_failure:
dev_put(dev);
return err;
}
static int nl80211_get_intfs(struct sk_buff *skb, struct genl_info *info)
{
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);
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_failure:
err = -ENOBUFS;
msg_free:
nlmsg_free(msg);
put_drv:
cfg80211_put_dev(drv);
return err;
}
static int nl80211_add_virt_intf(struct sk_buff *skb, struct genl_info *info)
{
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);
if (!drv->ops->add_virtual_intf) {
err = -EOPNOTSUPP;
goto unlock;
}
rtnl_lock();
err = drv->ops->add_virtual_intf(&drv->wiphy,
nla_data(info->attrs[NL80211_ATTR_IFNAME]), type);
rtnl_unlock();
unlock:
cfg80211_put_dev(drv);
return err;
}
static int nl80211_del_virt_intf(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *drv;
int ifindex, err;
struct net_device *dev;
err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
if (err)
return err;
ifindex = dev->ifindex;
dev_put(dev);
if (!drv->ops->del_virtual_intf) {
err = -EOPNOTSUPP;
goto out;
}
rtnl_lock();
err = drv->ops->del_virtual_intf(&drv->wiphy, ifindex);
rtnl_unlock();
out:
cfg80211_put_dev(drv);
return err;
}
static int nl80211_change_virt_intf(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *drv;
int err, ifindex;
unsigned int type;
struct net_device *dev;
if (info->attrs[NL80211_ATTR_IFTYPE]) {
type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
if (type > NL80211_IFTYPE_MAX)
return -EINVAL;
} else
return -EINVAL;
err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
if (err)
return err;
ifindex = dev->ifindex;
dev_put(dev);
if (!drv->ops->change_virtual_intf) {
err = -EOPNOTSUPP;
goto unlock;
}
rtnl_lock();
err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex, type);
rtnl_unlock();
unlock:
cfg80211_put_dev(drv);
return err;
}
static int nl80211_get_association(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;
}
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])
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;
}
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;
if (!drv->ops->get_auth_list) {
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;
}
cb.skb = msg;
cb.idx = 1;
rtnl_lock();
err = drv->ops->get_auth_list(&drv->wiphy, dev, &cb, add_bssid);
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:
cfg80211_put_dev(drv);
dev_put(dev);
return err;
}
static int nl80211_initiate_scan(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *drv;
int 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);
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) {
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);
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,
.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,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
},
{
.cmd = NL80211_CMD_GET_ASSOCIATION,
.doit = nl80211_get_association,
.policy = nl80211_policy,
/* can be retrieved by unprivileged users */
},
{
.cmd = NL80211_CMD_ASSOCIATE,
.doit = nl80211_associate,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
},
{
.cmd = NL80211_CMD_DISASSOCIATE,
.doit = nl80211_disassoc_deauth,
.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,
.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);
/* 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))
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;
nla_put_failure:
nlmsg_free(msg);
}
/* initialisation/exit functions */
int nl80211_init(void)
{
int err, i;
err = genl_register_family(&nl80211_fam);
if (err)
return err;
for (i = 0; i < ARRAY_SIZE(nl80211_ops); i++) {
err = genl_register_ops(&nl80211_fam, &nl80211_ops[i]);
if (err)
goto err_out;
}
return 0;
err_out:
genl_unregister_family(&nl80211_fam);
return err;
}
void nl80211_exit(void)
{
genl_unregister_family(&nl80211_fam);
}

View File

@@ -0,0 +1,24 @@
#ifndef __NET_WIRELESS_NL80211_H
#define __NET_WIRELESS_NL80211_H
#include "core.h"
#ifdef CONFIG_NL80211
extern int nl80211_init(void);
extern void nl80211_exit(void);
extern void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev);
#else
static inline int nl80211_init(void)
{
return 0;
}
static inline void nl80211_exit(void)
{
}
static inline void nl80211_notify_dev_rename(
struct cfg80211_registered_device *rdev)
{
}
#endif /* CONFIG_NL80211 */
#endif /* __NET_WIRELESS_NL80211_H */

View File

@@ -0,0 +1,130 @@
/*
* This file provides /sys/class/ieee80211/<wiphy name>/
* and some default attributes.
*
* Copyright 2005-2006 Jiri Benc <jbenc@suse.cz>
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
*
* This file is GPLv2 as found in COPYING.
*/
#include <linux/device.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/nl80211.h>
#include <linux/rtnetlink.h>
#include <net/cfg80211.h>
#include "sysfs.h"
#include "core.h"
static inline struct cfg80211_registered_device *dev_to_rdev(
struct device *dev)
{
return container_of(dev, struct cfg80211_registered_device, wiphy.dev);
}
static ssize_t _show_index(struct device *dev, struct device_attribute *attr,
char *buf)
{
return sprintf(buf, "%d\n", dev_to_rdev(dev)->idx);
}
static ssize_t _show_permaddr(struct device *dev,
struct device_attribute *attr,
char *buf)
{
unsigned char *addr = dev_to_rdev(dev)->wiphy.perm_addr;
return sprintf(buf, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
}
static ssize_t _store_add_iface(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
struct cfg80211_registered_device *rdev = dev_to_rdev(dev);
int res;
if (len > IFNAMSIZ)
return -EINVAL;
if (!rdev->ops->add_virtual_intf)
return -ENOSYS;
rtnl_lock();
res = rdev->ops->add_virtual_intf(&rdev->wiphy, (char*)buf,
NL80211_IFTYPE_UNSPECIFIED);
rtnl_unlock();
return res ? res : len;
}
static ssize_t _store_remove_iface(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
struct cfg80211_registered_device *rdev = dev_to_rdev(dev);
int res, ifidx;
struct net_device *netdev;
if (len > IFNAMSIZ)
return -EINVAL;
if (!rdev->ops->del_virtual_intf)
return -ENOSYS;
netdev = dev_get_by_name(buf);
if (!netdev)
return -ENODEV;
ifidx = netdev->ifindex;
dev_put(netdev);
rtnl_lock();
res = rdev->ops->del_virtual_intf(&rdev->wiphy, ifidx);
rtnl_unlock();
return res ? res : len;
}
static struct device_attribute ieee80211_dev_attrs[] = {
__ATTR(index, S_IRUGO, _show_index, NULL),
__ATTR(macaddress, S_IRUGO, _show_permaddr, NULL),
__ATTR(add_iface, S_IWUGO, NULL, _store_add_iface),
__ATTR(remove_iface, S_IWUGO, NULL, _store_remove_iface),
{}
};
static void wiphy_dev_release(struct device *dev)
{
struct cfg80211_registered_device *rdev = dev_to_rdev(dev);
cfg80211_dev_free(rdev);
}
static int wiphy_uevent(struct device *dev, char **envp,
int num_envp, char *buf, int size)
{
/* TODO, we probably need stuff here */
return 0;
}
struct class ieee80211_class = {
.name = "ieee80211",
.owner = THIS_MODULE,
.dev_release = wiphy_dev_release,
.dev_attrs = ieee80211_dev_attrs,
#ifdef CONFIG_HOTPLUG
.dev_uevent = wiphy_uevent,
#endif
};
int wiphy_sysfs_init(void)
{
return class_register(&ieee80211_class);
}
void wiphy_sysfs_exit(void)
{
class_unregister(&ieee80211_class);
}

View File

@@ -0,0 +1,9 @@
#ifndef __WIRELESS_SYSFS_H
#define __WIRELESS_SYSFS_H
extern int wiphy_sysfs_init(void);
extern void wiphy_sysfs_exit(void);
extern struct class ieee80211_class;
#endif /* __WIRELESS_SYSFS_H */

File diff suppressed because it is too large Load Diff