2007-09-20 17:09:35 +00:00
|
|
|
/*
|
|
|
|
* This is the new netlink-based wireless configuration interface.
|
|
|
|
*
|
2010-02-15 10:53:10 +00:00
|
|
|
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
|
2007-09-20 17:09:35 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/if.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/err.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 08:04:11 +00:00
|
|
|
#include <linux/slab.h>
|
2007-09-20 17:09:35 +00:00
|
|
|
#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>
|
2009-02-10 20:25:55 +00:00
|
|
|
#include <linux/etherdevice.h>
|
2009-07-13 22:33:35 +00:00
|
|
|
#include <net/net_namespace.h>
|
2007-09-20 17:09:35 +00:00
|
|
|
#include <net/genetlink.h>
|
|
|
|
#include <net/cfg80211.h>
|
2009-07-13 22:33:35 +00:00
|
|
|
#include <net/sock.h>
|
2007-09-20 17:09:35 +00:00
|
|
|
#include "core.h"
|
|
|
|
#include "nl80211.h"
|
2008-09-10 06:19:48 +00:00
|
|
|
#include "reg.h"
|
2007-09-20 17:09:35 +00:00
|
|
|
|
|
|
|
/* 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,
|
2009-07-13 22:33:35 +00:00
|
|
|
.netnsok = true,
|
2007-09-20 17:09:35 +00:00
|
|
|
};
|
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
/* internal helper: get rdev and dev */
|
2009-07-13 22:33:35 +00:00
|
|
|
static int get_rdev_dev_by_info_ifindex(struct genl_info *info,
|
2009-07-07 01:56:12 +00:00
|
|
|
struct cfg80211_registered_device **rdev,
|
2007-09-20 17:09:35 +00:00
|
|
|
struct net_device **dev)
|
|
|
|
{
|
2009-07-13 22:33:35 +00:00
|
|
|
struct nlattr **attrs = info->attrs;
|
2007-09-20 17:09:35 +00:00
|
|
|
int ifindex;
|
|
|
|
|
2008-07-29 11:22:51 +00:00
|
|
|
if (!attrs[NL80211_ATTR_IFINDEX])
|
2007-09-20 17:09:35 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2008-07-29 11:22:51 +00:00
|
|
|
ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]);
|
2009-07-13 22:33:35 +00:00
|
|
|
*dev = dev_get_by_index(genl_info_net(info), ifindex);
|
2007-09-20 17:09:35 +00:00
|
|
|
if (!*dev)
|
|
|
|
return -ENODEV;
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
*rdev = cfg80211_get_dev_from_ifindex(genl_info_net(info), ifindex);
|
2009-07-07 01:56:12 +00:00
|
|
|
if (IS_ERR(*rdev)) {
|
2007-09-20 17:09:35 +00:00
|
|
|
dev_put(*dev);
|
2009-07-07 01:56:12 +00:00
|
|
|
return PTR_ERR(*rdev);
|
2007-09-20 17:09:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* policy for the attributes */
|
2010-02-18 08:14:31 +00:00
|
|
|
static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
|
2007-09-20 17:09:35 +00:00
|
|
|
[NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
|
|
|
|
[NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
|
2009-05-27 04:15:00 +00:00
|
|
|
.len = 20-1 },
|
2008-10-30 14:59:24 +00:00
|
|
|
[NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED },
|
2008-11-26 14:15:24 +00:00
|
|
|
[NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 },
|
2008-12-12 06:27:43 +00:00
|
|
|
[NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 },
|
2009-04-20 16:39:05 +00:00
|
|
|
[NL80211_ATTR_WIPHY_RETRY_SHORT] = { .type = NLA_U8 },
|
|
|
|
[NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 },
|
|
|
|
[NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 },
|
|
|
|
[NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 },
|
2009-12-21 21:50:47 +00:00
|
|
|
[NL80211_ATTR_WIPHY_COVERAGE_CLASS] = { .type = NLA_U8 },
|
2007-09-20 17:09:35 +00:00
|
|
|
|
|
|
|
[NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
|
|
|
|
[NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
|
|
|
|
[NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
|
2007-12-19 01:03:29 +00:00
|
|
|
|
|
|
|
[NL80211_ATTR_MAC] = { .type = NLA_BINARY, .len = ETH_ALEN },
|
2009-07-07 12:37:26 +00:00
|
|
|
[NL80211_ATTR_PREV_BSSID] = { .type = NLA_BINARY, .len = ETH_ALEN },
|
2007-12-19 01:03:29 +00:00
|
|
|
|
2009-07-08 11:29:08 +00:00
|
|
|
[NL80211_ATTR_KEY] = { .type = NLA_NESTED, },
|
2007-12-19 01:03:29 +00:00
|
|
|
[NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY,
|
|
|
|
.len = WLAN_MAX_KEY_LEN },
|
|
|
|
[NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 },
|
|
|
|
[NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 },
|
|
|
|
[NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG },
|
2009-05-15 09:38:32 +00:00
|
|
|
[NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 },
|
2007-12-19 01:03:32 +00:00
|
|
|
|
|
|
|
[NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 },
|
|
|
|
[NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 },
|
|
|
|
[NL80211_ATTR_BEACON_HEAD] = { .type = NLA_BINARY,
|
|
|
|
.len = IEEE80211_MAX_DATA_LEN },
|
|
|
|
[NL80211_ATTR_BEACON_TAIL] = { .type = NLA_BINARY,
|
|
|
|
.len = IEEE80211_MAX_DATA_LEN },
|
2007-12-19 01:03:34 +00:00
|
|
|
[NL80211_ATTR_STA_AID] = { .type = NLA_U16 },
|
|
|
|
[NL80211_ATTR_STA_FLAGS] = { .type = NLA_NESTED },
|
|
|
|
[NL80211_ATTR_STA_LISTEN_INTERVAL] = { .type = NLA_U16 },
|
|
|
|
[NL80211_ATTR_STA_SUPPORTED_RATES] = { .type = NLA_BINARY,
|
|
|
|
.len = NL80211_MAX_SUPP_RATES },
|
2008-02-23 14:17:06 +00:00
|
|
|
[NL80211_ATTR_STA_PLINK_ACTION] = { .type = NLA_U8 },
|
2007-12-19 01:03:34 +00:00
|
|
|
[NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 },
|
2008-10-15 09:54:04 +00:00
|
|
|
[NL80211_ATTR_MNTR_FLAGS] = { /* NLA_NESTED can't be empty */ },
|
2008-02-23 14:17:06 +00:00
|
|
|
[NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY,
|
|
|
|
.len = IEEE80211_MAX_MESH_ID_LEN },
|
|
|
|
[NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 },
|
2008-08-07 17:07:01 +00:00
|
|
|
|
2008-09-10 06:19:48 +00:00
|
|
|
[NL80211_ATTR_REG_ALPHA2] = { .type = NLA_STRING, .len = 2 },
|
|
|
|
[NL80211_ATTR_REG_RULES] = { .type = NLA_NESTED },
|
|
|
|
|
2008-08-07 17:07:01 +00:00
|
|
|
[NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 },
|
|
|
|
[NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 },
|
|
|
|
[NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 },
|
2008-10-30 14:59:22 +00:00
|
|
|
[NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY,
|
|
|
|
.len = NL80211_MAX_SUPP_RATES },
|
2008-08-25 08:58:58 +00:00
|
|
|
|
2008-10-21 19:03:48 +00:00
|
|
|
[NL80211_ATTR_MESH_PARAMS] = { .type = NLA_NESTED },
|
|
|
|
|
2008-08-25 08:58:58 +00:00
|
|
|
[NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY,
|
|
|
|
.len = NL80211_HT_CAPABILITY_LEN },
|
2009-01-13 14:03:29 +00:00
|
|
|
|
|
|
|
[NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 },
|
|
|
|
[NL80211_ATTR_IE] = { .type = NLA_BINARY,
|
|
|
|
.len = IEEE80211_MAX_DATA_LEN },
|
2009-02-10 20:25:55 +00:00
|
|
|
[NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED },
|
|
|
|
[NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED },
|
2009-03-19 11:39:22 +00:00
|
|
|
|
|
|
|
[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 },
|
2009-04-19 19:24:32 +00:00
|
|
|
[NL80211_ATTR_FREQ_FIXED] = { .type = NLA_FLAG },
|
2009-04-22 18:38:25 +00:00
|
|
|
[NL80211_ATTR_TIMED_OUT] = { .type = NLA_FLAG },
|
2009-05-06 19:09:37 +00:00
|
|
|
[NL80211_ATTR_USE_MFP] = { .type = NLA_U32 },
|
2009-05-11 18:57:56 +00:00
|
|
|
[NL80211_ATTR_STA_FLAGS2] = {
|
|
|
|
.len = sizeof(struct nl80211_sta_flag_update),
|
|
|
|
},
|
2009-05-11 18:57:57 +00:00
|
|
|
[NL80211_ATTR_CONTROL_PORT] = { .type = NLA_FLAG },
|
2009-07-01 19:26:54 +00:00
|
|
|
[NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG },
|
|
|
|
[NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 },
|
|
|
|
[NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 },
|
2009-07-13 22:33:35 +00:00
|
|
|
[NL80211_ATTR_PID] = { .type = NLA_U32 },
|
2009-11-10 17:53:10 +00:00
|
|
|
[NL80211_ATTR_4ADDR] = { .type = NLA_U8 },
|
2009-11-24 22:59:15 +00:00
|
|
|
[NL80211_ATTR_PMKID] = { .type = NLA_BINARY,
|
|
|
|
.len = WLAN_PMKID_LEN },
|
2009-12-23 12:15:41 +00:00
|
|
|
[NL80211_ATTR_DURATION] = { .type = NLA_U32 },
|
|
|
|
[NL80211_ATTR_COOKIE] = { .type = NLA_U64 },
|
2009-12-29 10:59:45 +00:00
|
|
|
[NL80211_ATTR_TX_RATES] = { .type = NLA_NESTED },
|
2010-02-15 10:53:10 +00:00
|
|
|
[NL80211_ATTR_FRAME] = { .type = NLA_BINARY,
|
|
|
|
.len = IEEE80211_MAX_DATA_LEN },
|
|
|
|
[NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, },
|
nl80211: add power save commands
The most needed command from nl80211, which Wireless Extensions had,
is support for power save mode. Add a simple command to make it possible
to enable and disable power save via nl80211.
I was also planning about extending the interface, for example adding the
timeout value, but after thinking more about this I decided not to do it.
Basically there were three reasons:
Firstly, the parameters for power save are very much hardware dependent.
Trying to find a unified interface which would work with all hardware, and
still make sense to users, will be very difficult.
Secondly, IEEE 802.11 power save implementation in Linux is still in state
of flux. We have a long way to still to go and there is no way to predict
what kind of implementation we will have after few years. And because we
need to support nl80211 interface a long time, practically forever, adding
now parameters to nl80211 might create maintenance problems later on.
Third issue are the users. Power save parameters are mostly used for
debugging, so debugfs is better, more flexible, interface for this.
For example, wpa_supplicant currently doesn't configure anything related
to power save mode. It's better to strive that kernel can automatically
optimise the power save parameters, like with help of pm qos network
and other traffic parameters.
Later on, when we have better understanding of power save, we can extend
this command with more features, if there's a need for that.
Signed-off-by: Kalle Valo <kalle.valo@nokia.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2010-02-17 15:58:10 +00:00
|
|
|
[NL80211_ATTR_PS_STATE] = { .type = NLA_U32 },
|
2010-03-23 07:02:33 +00:00
|
|
|
[NL80211_ATTR_CQM] = { .type = NLA_NESTED, },
|
2010-04-04 06:37:19 +00:00
|
|
|
[NL80211_ATTR_LOCAL_STATE_CHANGE] = { .type = NLA_FLAG },
|
2010-04-26 23:23:35 +00:00
|
|
|
[NL80211_ATTR_AP_ISOLATE] = { .type = NLA_U8 },
|
2010-06-23 09:12:38 +00:00
|
|
|
|
|
|
|
[NL80211_ATTR_WIPHY_TX_POWER_SETTING] = { .type = NLA_U32 },
|
|
|
|
[NL80211_ATTR_WIPHY_TX_POWER_LEVEL] = { .type = NLA_U32 },
|
2007-09-20 17:09:35 +00:00
|
|
|
};
|
|
|
|
|
2009-07-08 11:29:08 +00:00
|
|
|
/* policy for the attributes */
|
2010-02-18 08:14:31 +00:00
|
|
|
static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = {
|
cfg80211: rework key operation
This reworks the key operation in cfg80211, and now only
allows, from userspace, configuring keys (via nl80211)
after the connection has been established (in managed
mode), the IBSS been joined (in IBSS mode), at any time
(in AP[_VLAN] modes) or never for all the other modes.
In order to do shared key authentication correctly, it
is now possible to give a WEP key to the AUTH command.
To configure static WEP keys, these are given to the
CONNECT or IBSS_JOIN command directly, for a userspace
SME it is assumed it will configure it properly after
the connection has been established.
Since mac80211 used to check the default key in IBSS
mode to see whether or not the network is protected,
it needs an update in that area, as well as an update
to make use of the WEP key passed to auth() for shared
key authentication.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-07-08 12:22:54 +00:00
|
|
|
[NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN },
|
2009-07-08 11:29:08 +00:00
|
|
|
[NL80211_KEY_IDX] = { .type = NLA_U8 },
|
|
|
|
[NL80211_KEY_CIPHER] = { .type = NLA_U32 },
|
|
|
|
[NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 },
|
|
|
|
[NL80211_KEY_DEFAULT] = { .type = NLA_FLAG },
|
|
|
|
[NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG },
|
|
|
|
};
|
|
|
|
|
2009-11-11 10:30:02 +00:00
|
|
|
/* ifidx get helper */
|
|
|
|
static int nl80211_get_ifidx(struct netlink_callback *cb)
|
|
|
|
{
|
|
|
|
int res;
|
|
|
|
|
|
|
|
res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
|
|
|
|
nl80211_fam.attrbuf, nl80211_fam.maxattr,
|
|
|
|
nl80211_policy);
|
|
|
|
if (res)
|
|
|
|
return res;
|
|
|
|
|
|
|
|
if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
res = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
|
|
|
|
if (!res)
|
|
|
|
return -EINVAL;
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2009-03-27 11:40:28 +00:00
|
|
|
/* IE validation */
|
|
|
|
static bool is_valid_ie_attr(const struct nlattr *attr)
|
|
|
|
{
|
|
|
|
const u8 *pos;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
if (!attr)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
pos = nla_data(attr);
|
|
|
|
len = nla_len(attr);
|
|
|
|
|
|
|
|
while (len) {
|
|
|
|
u8 elemlen;
|
|
|
|
|
|
|
|
if (len < 2)
|
|
|
|
return false;
|
|
|
|
len -= 2;
|
|
|
|
|
|
|
|
elemlen = pos[1];
|
|
|
|
if (elemlen > len)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
len -= elemlen;
|
|
|
|
pos += 2 + elemlen;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2007-09-20 17:09:35 +00:00
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
|
2009-04-02 18:08:08 +00:00
|
|
|
static int nl80211_msg_put_channel(struct sk_buff *msg,
|
|
|
|
struct ieee80211_channel *chan)
|
|
|
|
{
|
|
|
|
NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_FREQ,
|
|
|
|
chan->center_freq);
|
|
|
|
|
|
|
|
if (chan->flags & IEEE80211_CHAN_DISABLED)
|
|
|
|
NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_DISABLED);
|
|
|
|
if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
|
|
|
|
NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN);
|
|
|
|
if (chan->flags & IEEE80211_CHAN_NO_IBSS)
|
|
|
|
NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_NO_IBSS);
|
|
|
|
if (chan->flags & IEEE80211_CHAN_RADAR)
|
|
|
|
NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR);
|
|
|
|
|
|
|
|
NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
|
|
|
|
DBM_TO_MBM(chan->max_power));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
return -ENOBUFS;
|
|
|
|
}
|
|
|
|
|
2007-09-20 17:09:35 +00:00
|
|
|
/* netlink command implementations */
|
|
|
|
|
2009-07-08 11:29:08 +00:00
|
|
|
struct key_parse {
|
|
|
|
struct key_params p;
|
|
|
|
int idx;
|
|
|
|
bool def, defmgmt;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int nl80211_parse_key_new(struct nlattr *key, struct key_parse *k)
|
|
|
|
{
|
|
|
|
struct nlattr *tb[NL80211_KEY_MAX + 1];
|
|
|
|
int err = nla_parse_nested(tb, NL80211_KEY_MAX, key,
|
|
|
|
nl80211_key_policy);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
k->def = !!tb[NL80211_KEY_DEFAULT];
|
|
|
|
k->defmgmt = !!tb[NL80211_KEY_DEFAULT_MGMT];
|
|
|
|
|
|
|
|
if (tb[NL80211_KEY_IDX])
|
|
|
|
k->idx = nla_get_u8(tb[NL80211_KEY_IDX]);
|
|
|
|
|
|
|
|
if (tb[NL80211_KEY_DATA]) {
|
|
|
|
k->p.key = nla_data(tb[NL80211_KEY_DATA]);
|
|
|
|
k->p.key_len = nla_len(tb[NL80211_KEY_DATA]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tb[NL80211_KEY_SEQ]) {
|
|
|
|
k->p.seq = nla_data(tb[NL80211_KEY_SEQ]);
|
|
|
|
k->p.seq_len = nla_len(tb[NL80211_KEY_SEQ]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tb[NL80211_KEY_CIPHER])
|
|
|
|
k->p.cipher = nla_get_u32(tb[NL80211_KEY_CIPHER]);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_parse_key_old(struct genl_info *info, struct key_parse *k)
|
|
|
|
{
|
|
|
|
if (info->attrs[NL80211_ATTR_KEY_DATA]) {
|
|
|
|
k->p.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]);
|
|
|
|
k->p.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_KEY_SEQ]) {
|
|
|
|
k->p.seq = nla_data(info->attrs[NL80211_ATTR_KEY_SEQ]);
|
|
|
|
k->p.seq_len = nla_len(info->attrs[NL80211_ATTR_KEY_SEQ]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_KEY_IDX])
|
|
|
|
k->idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_KEY_CIPHER])
|
|
|
|
k->p.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]);
|
|
|
|
|
|
|
|
k->def = !!info->attrs[NL80211_ATTR_KEY_DEFAULT];
|
|
|
|
k->defmgmt = !!info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT];
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_parse_key(struct genl_info *info, struct key_parse *k)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
memset(k, 0, sizeof(*k));
|
|
|
|
k->idx = -1;
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_KEY])
|
|
|
|
err = nl80211_parse_key_new(info->attrs[NL80211_ATTR_KEY], k);
|
|
|
|
else
|
|
|
|
err = nl80211_parse_key_old(info, k);
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (k->def && k->defmgmt)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (k->idx != -1) {
|
|
|
|
if (k->defmgmt) {
|
|
|
|
if (k->idx < 4 || k->idx > 5)
|
|
|
|
return -EINVAL;
|
|
|
|
} else if (k->def) {
|
|
|
|
if (k->idx < 0 || k->idx > 3)
|
|
|
|
return -EINVAL;
|
|
|
|
} else {
|
|
|
|
if (k->idx < 0 || k->idx > 5)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
cfg80211: rework key operation
This reworks the key operation in cfg80211, and now only
allows, from userspace, configuring keys (via nl80211)
after the connection has been established (in managed
mode), the IBSS been joined (in IBSS mode), at any time
(in AP[_VLAN] modes) or never for all the other modes.
In order to do shared key authentication correctly, it
is now possible to give a WEP key to the AUTH command.
To configure static WEP keys, these are given to the
CONNECT or IBSS_JOIN command directly, for a userspace
SME it is assumed it will configure it properly after
the connection has been established.
Since mac80211 used to check the default key in IBSS
mode to see whether or not the network is protected,
it needs an update in that area, as well as an update
to make use of the WEP key passed to auth() for shared
key authentication.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-07-08 12:22:54 +00:00
|
|
|
static struct cfg80211_cached_keys *
|
|
|
|
nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
|
|
|
|
struct nlattr *keys)
|
|
|
|
{
|
|
|
|
struct key_parse parse;
|
|
|
|
struct nlattr *key;
|
|
|
|
struct cfg80211_cached_keys *result;
|
|
|
|
int rem, err, def = 0;
|
|
|
|
|
|
|
|
result = kzalloc(sizeof(*result), GFP_KERNEL);
|
|
|
|
if (!result)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
|
|
result->def = -1;
|
|
|
|
result->defmgmt = -1;
|
|
|
|
|
|
|
|
nla_for_each_nested(key, keys, rem) {
|
|
|
|
memset(&parse, 0, sizeof(parse));
|
|
|
|
parse.idx = -1;
|
|
|
|
|
|
|
|
err = nl80211_parse_key_new(key, &parse);
|
|
|
|
if (err)
|
|
|
|
goto error;
|
|
|
|
err = -EINVAL;
|
|
|
|
if (!parse.p.key)
|
|
|
|
goto error;
|
|
|
|
if (parse.idx < 0 || parse.idx > 4)
|
|
|
|
goto error;
|
|
|
|
if (parse.def) {
|
|
|
|
if (def)
|
|
|
|
goto error;
|
|
|
|
def = 1;
|
|
|
|
result->def = parse.idx;
|
|
|
|
} else if (parse.defmgmt)
|
|
|
|
goto error;
|
|
|
|
err = cfg80211_validate_key_settings(rdev, &parse.p,
|
|
|
|
parse.idx, NULL);
|
|
|
|
if (err)
|
|
|
|
goto error;
|
|
|
|
result->params[parse.idx].cipher = parse.p.cipher;
|
|
|
|
result->params[parse.idx].key_len = parse.p.key_len;
|
|
|
|
result->params[parse.idx].key = result->data[parse.idx];
|
|
|
|
memcpy(result->data[parse.idx], parse.p.key, parse.p.key_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
error:
|
|
|
|
kfree(result);
|
|
|
|
return ERR_PTR(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_key_allowed(struct wireless_dev *wdev)
|
|
|
|
{
|
|
|
|
ASSERT_WDEV_LOCK(wdev);
|
|
|
|
|
|
|
|
if (!netif_running(wdev->netdev))
|
|
|
|
return -ENETDOWN;
|
|
|
|
|
|
|
|
switch (wdev->iftype) {
|
|
|
|
case NL80211_IFTYPE_AP:
|
|
|
|
case NL80211_IFTYPE_AP_VLAN:
|
|
|
|
break;
|
|
|
|
case NL80211_IFTYPE_ADHOC:
|
|
|
|
if (!wdev->current_bss)
|
|
|
|
return -ENOLINK;
|
|
|
|
break;
|
|
|
|
case NL80211_IFTYPE_STATION:
|
|
|
|
if (wdev->sme_state != CFG80211_SME_CONNECTED)
|
|
|
|
return -ENOLINK;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-09-20 17:09:35 +00:00
|
|
|
static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
|
|
|
|
struct cfg80211_registered_device *dev)
|
|
|
|
{
|
|
|
|
void *hdr;
|
2008-01-24 18:38:39 +00:00
|
|
|
struct nlattr *nl_bands, *nl_band;
|
|
|
|
struct nlattr *nl_freqs, *nl_freq;
|
|
|
|
struct nlattr *nl_rates, *nl_rate;
|
2008-08-29 23:26:43 +00:00
|
|
|
struct nlattr *nl_modes;
|
2009-03-14 08:34:01 +00:00
|
|
|
struct nlattr *nl_cmds;
|
2008-01-24 18:38:39 +00:00
|
|
|
enum ieee80211_band band;
|
|
|
|
struct ieee80211_channel *chan;
|
|
|
|
struct ieee80211_rate *rate;
|
|
|
|
int i;
|
2008-08-29 23:26:43 +00:00
|
|
|
u16 ifmodes = dev->wiphy.interface_modes;
|
2007-09-20 17:09:35 +00:00
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY);
|
|
|
|
if (!hdr)
|
|
|
|
return -1;
|
|
|
|
|
2009-02-21 05:04:19 +00:00
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx);
|
2007-09-20 17:09:35 +00:00
|
|
|
NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
|
2009-04-20 16:39:05 +00:00
|
|
|
|
2009-08-07 14:17:38 +00:00
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_GENERATION,
|
|
|
|
cfg80211_rdev_list_generation);
|
|
|
|
|
2009-04-20 16:39:05 +00:00
|
|
|
NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
|
|
|
|
dev->wiphy.retry_short);
|
|
|
|
NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
|
|
|
|
dev->wiphy.retry_long);
|
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
|
|
|
|
dev->wiphy.frag_threshold);
|
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD,
|
|
|
|
dev->wiphy.rts_threshold);
|
2009-12-21 21:50:47 +00:00
|
|
|
NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS,
|
|
|
|
dev->wiphy.coverage_class);
|
2009-04-20 16:39:05 +00:00
|
|
|
|
2009-02-10 20:25:55 +00:00
|
|
|
NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
|
|
|
|
dev->wiphy.max_scan_ssids);
|
2009-03-31 10:12:05 +00:00
|
|
|
NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
|
|
|
|
dev->wiphy.max_scan_ie_len);
|
2008-01-24 18:38:39 +00:00
|
|
|
|
2009-04-02 18:14:06 +00:00
|
|
|
NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES,
|
|
|
|
sizeof(u32) * dev->wiphy.n_cipher_suites,
|
|
|
|
dev->wiphy.cipher_suites);
|
|
|
|
|
2009-11-24 22:59:15 +00:00
|
|
|
NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_PMKIDS,
|
|
|
|
dev->wiphy.max_num_pmkids);
|
|
|
|
|
2008-08-29 23:26:43 +00:00
|
|
|
nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES);
|
|
|
|
if (!nl_modes)
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
while (ifmodes) {
|
|
|
|
if (ifmodes & 1)
|
|
|
|
NLA_PUT_FLAG(msg, i);
|
|
|
|
ifmodes >>= 1;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
nla_nest_end(msg, nl_modes);
|
|
|
|
|
2008-01-24 18:38:39 +00:00
|
|
|
nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
|
|
|
|
if (!nl_bands)
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
|
|
|
|
if (!dev->wiphy.bands[band])
|
|
|
|
continue;
|
|
|
|
|
|
|
|
nl_band = nla_nest_start(msg, band);
|
|
|
|
if (!nl_band)
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
2008-10-09 10:20:13 +00:00
|
|
|
/* add HT info */
|
|
|
|
if (dev->wiphy.bands[band]->ht_cap.ht_supported) {
|
|
|
|
NLA_PUT(msg, NL80211_BAND_ATTR_HT_MCS_SET,
|
|
|
|
sizeof(dev->wiphy.bands[band]->ht_cap.mcs),
|
|
|
|
&dev->wiphy.bands[band]->ht_cap.mcs);
|
|
|
|
NLA_PUT_U16(msg, NL80211_BAND_ATTR_HT_CAPA,
|
|
|
|
dev->wiphy.bands[band]->ht_cap.cap);
|
|
|
|
NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
|
|
|
|
dev->wiphy.bands[band]->ht_cap.ampdu_factor);
|
|
|
|
NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
|
|
|
|
dev->wiphy.bands[band]->ht_cap.ampdu_density);
|
|
|
|
}
|
|
|
|
|
2008-01-24 18:38:39 +00:00
|
|
|
/* add frequencies */
|
|
|
|
nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS);
|
|
|
|
if (!nl_freqs)
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
for (i = 0; i < dev->wiphy.bands[band]->n_channels; i++) {
|
|
|
|
nl_freq = nla_nest_start(msg, i);
|
|
|
|
if (!nl_freq)
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
chan = &dev->wiphy.bands[band]->channels[i];
|
2009-04-02 18:08:08 +00:00
|
|
|
|
|
|
|
if (nl80211_msg_put_channel(msg, chan))
|
|
|
|
goto nla_put_failure;
|
2008-11-21 17:01:30 +00:00
|
|
|
|
2008-01-24 18:38:39 +00:00
|
|
|
nla_nest_end(msg, nl_freq);
|
|
|
|
}
|
|
|
|
|
|
|
|
nla_nest_end(msg, nl_freqs);
|
|
|
|
|
|
|
|
/* add bitrates */
|
|
|
|
nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
|
|
|
|
if (!nl_rates)
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
for (i = 0; i < dev->wiphy.bands[band]->n_bitrates; i++) {
|
|
|
|
nl_rate = nla_nest_start(msg, i);
|
|
|
|
if (!nl_rate)
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
rate = &dev->wiphy.bands[band]->bitrates[i];
|
|
|
|
NLA_PUT_U32(msg, NL80211_BITRATE_ATTR_RATE,
|
|
|
|
rate->bitrate);
|
|
|
|
if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE)
|
|
|
|
NLA_PUT_FLAG(msg,
|
|
|
|
NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE);
|
|
|
|
|
|
|
|
nla_nest_end(msg, nl_rate);
|
|
|
|
}
|
|
|
|
|
|
|
|
nla_nest_end(msg, nl_rates);
|
|
|
|
|
|
|
|
nla_nest_end(msg, nl_band);
|
|
|
|
}
|
|
|
|
nla_nest_end(msg, nl_bands);
|
|
|
|
|
2009-03-14 08:34:01 +00:00
|
|
|
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);
|
2009-03-19 11:39:22 +00:00
|
|
|
CMD(auth, AUTHENTICATE);
|
|
|
|
CMD(assoc, ASSOCIATE);
|
|
|
|
CMD(deauth, DEAUTHENTICATE);
|
|
|
|
CMD(disassoc, DISASSOCIATE);
|
2009-04-19 19:24:32 +00:00
|
|
|
CMD(join_ibss, JOIN_IBSS);
|
2009-11-24 22:59:15 +00:00
|
|
|
CMD(set_pmksa, SET_PMKSA);
|
|
|
|
CMD(del_pmksa, DEL_PMKSA);
|
|
|
|
CMD(flush_pmksa, FLUSH_PMKSA);
|
2009-12-23 12:15:41 +00:00
|
|
|
CMD(remain_on_channel, REMAIN_ON_CHANNEL);
|
2009-12-29 10:59:45 +00:00
|
|
|
CMD(set_bitrate_mask, SET_TX_BITRATE_MASK);
|
2010-02-15 10:53:10 +00:00
|
|
|
CMD(action, ACTION);
|
2009-11-18 23:56:28 +00:00
|
|
|
if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
|
2009-07-13 22:33:35 +00:00
|
|
|
i++;
|
|
|
|
NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS);
|
|
|
|
}
|
cfg80211/mac80211: better channel handling
Currently (all tested with hwsim) you can do stupid
things like setting up an AP on a certain channel,
then adding another virtual interface and making
that associate on another channel -- this will make
the beaconing to move channel but obviously without
the necessary IEs data update.
In order to improve this situation, first make the
configuration APIs (cfg80211 and nl80211) aware of
multi-channel operation -- we'll eventually need
that in the future anyway. There's one userland API
change and one API addition. The API change is that
now SET_WIPHY must be called with virtual interface
index rather than only wiphy index in order to take
effect for that interface -- luckily all current
users (hostapd) do that. For monitor interfaces, the
old setting is preserved, but monitors are always
slaved to other devices anyway so no guarantees.
The second userland API change is the introduction
of a per virtual interface SET_CHANNEL command, that
hostapd should use going forward to make it easier
to understand what's going on (it can automatically
detect a kernel with this command).
Other than mac80211, no existing cfg80211 drivers
are affected by this change because they only allow
a single virtual interface.
mac80211, however, now needs to be aware that the
channel settings are per interface now, and needs
to disallow (for now) real multi-channel operation,
which is another important part of this patch.
One of the immediate benefits is that you can now
start hostapd to operate on a hardware that already
has a connection on another virtual interface, as
long as you specify the same channel.
Note that two things are left unhandled (this is an
improvement -- not a complete fix):
* different HT/no-HT modes
currently you could start an HT AP and then
connect to a non-HT network on the same channel
which would configure the hardware for no HT;
that can be fixed fairly easily
* CSA
An AP we're connected to on a virtual interface
might indicate switching channels, and in that
case we would follow it, regardless of how many
other interfaces are operating; this requires
more effort to fix but is pretty rare after all
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2010-05-05 13:25:02 +00:00
|
|
|
CMD(set_channel, SET_CHANNEL);
|
2009-03-14 08:34:01 +00:00
|
|
|
|
|
|
|
#undef CMD
|
2009-07-01 19:26:54 +00:00
|
|
|
|
2009-07-02 07:13:27 +00:00
|
|
|
if (dev->ops->connect || dev->ops->auth) {
|
2009-07-01 19:26:54 +00:00
|
|
|
i++;
|
|
|
|
NLA_PUT_U32(msg, i, NL80211_CMD_CONNECT);
|
|
|
|
}
|
|
|
|
|
2009-07-02 07:13:27 +00:00
|
|
|
if (dev->ops->disconnect || dev->ops->deauth) {
|
2009-07-01 19:26:54 +00:00
|
|
|
i++;
|
|
|
|
NLA_PUT_U32(msg, i, NL80211_CMD_DISCONNECT);
|
|
|
|
}
|
|
|
|
|
2009-03-14 08:34:01 +00:00
|
|
|
nla_nest_end(msg, nl_cmds);
|
|
|
|
|
2007-09-20 17:09:35 +00:00
|
|
|
return genlmsg_end(msg, hdr);
|
|
|
|
|
|
|
|
nla_put_failure:
|
2008-06-03 23:36:54 +00:00
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
return -EMSGSIZE;
|
2007-09-20 17:09:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2009-02-21 05:04:21 +00:00
|
|
|
mutex_lock(&cfg80211_mutex);
|
2009-07-07 01:56:12 +00:00
|
|
|
list_for_each_entry(dev, &cfg80211_rdev_list, list) {
|
2009-07-13 22:33:35 +00:00
|
|
|
if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk)))
|
|
|
|
continue;
|
net/wireless/nl80211.c: fix endless Netlink callback loop.
Although I only tested similar code (I don't use any of this wireless
code), the state maintainance between Netlink dump callback invocations
seems wrong here and should lead to an endless loop. There are also other
examples in the same file which might have the same problem. Perhaps someone
can actually test this (or refute my logic).
Take the simple example with only one element in the list (which should fit
into the message):
1. invocation:
Start:
idx = 0, start = 0
Loop:
condition (++idx < start) => (1 < 0) => false
=> no continue, fill one entry, exit loop, return skb->len > 0
2. invocation:
Start:
idx = 0, start = 1
Loop:
condition (++idx < start) => (1 < 1) => false
=> no continue, fill the same entry again, exit loop, return skb->len > 0
3. invocation:
Same as 2. invocation, endless invocation of callback.
Also, iterations where the filling of an element fails should not be counted as
completed, so idx should not be incremented in this case.
Signed-off-by: Julius Volz <juliusv@google.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2008-07-08 12:02:19 +00:00
|
|
|
if (++idx <= start)
|
2007-09-20 17:09:35 +00:00
|
|
|
continue;
|
|
|
|
if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid,
|
|
|
|
cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
net/wireless/nl80211.c: fix endless Netlink callback loop.
Although I only tested similar code (I don't use any of this wireless
code), the state maintainance between Netlink dump callback invocations
seems wrong here and should lead to an endless loop. There are also other
examples in the same file which might have the same problem. Perhaps someone
can actually test this (or refute my logic).
Take the simple example with only one element in the list (which should fit
into the message):
1. invocation:
Start:
idx = 0, start = 0
Loop:
condition (++idx < start) => (1 < 0) => false
=> no continue, fill one entry, exit loop, return skb->len > 0
2. invocation:
Start:
idx = 0, start = 1
Loop:
condition (++idx < start) => (1 < 1) => false
=> no continue, fill the same entry again, exit loop, return skb->len > 0
3. invocation:
Same as 2. invocation, endless invocation of callback.
Also, iterations where the filling of an element fails should not be counted as
completed, so idx should not be incremented in this case.
Signed-off-by: Julius Volz <juliusv@google.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2008-07-08 12:02:19 +00:00
|
|
|
dev) < 0) {
|
|
|
|
idx--;
|
2007-09-20 17:09:35 +00:00
|
|
|
break;
|
net/wireless/nl80211.c: fix endless Netlink callback loop.
Although I only tested similar code (I don't use any of this wireless
code), the state maintainance between Netlink dump callback invocations
seems wrong here and should lead to an endless loop. There are also other
examples in the same file which might have the same problem. Perhaps someone
can actually test this (or refute my logic).
Take the simple example with only one element in the list (which should fit
into the message):
1. invocation:
Start:
idx = 0, start = 0
Loop:
condition (++idx < start) => (1 < 0) => false
=> no continue, fill one entry, exit loop, return skb->len > 0
2. invocation:
Start:
idx = 0, start = 1
Loop:
condition (++idx < start) => (1 < 1) => false
=> no continue, fill the same entry again, exit loop, return skb->len > 0
3. invocation:
Same as 2. invocation, endless invocation of callback.
Also, iterations where the filling of an element fails should not be counted as
completed, so idx should not be incremented in this case.
Signed-off-by: Julius Volz <juliusv@google.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2008-07-08 12:02:19 +00:00
|
|
|
}
|
2007-09-20 17:09:35 +00:00
|
|
|
}
|
2009-02-21 05:04:21 +00:00
|
|
|
mutex_unlock(&cfg80211_mutex);
|
2007-09-20 17:09:35 +00:00
|
|
|
|
|
|
|
cb->args[0] = idx;
|
|
|
|
|
|
|
|
return skb->len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct sk_buff *msg;
|
|
|
|
struct cfg80211_registered_device *dev;
|
|
|
|
|
|
|
|
dev = cfg80211_get_dev_from_info(info);
|
|
|
|
if (IS_ERR(dev))
|
|
|
|
return PTR_ERR(dev);
|
|
|
|
|
2009-05-19 22:27:55 +00:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
2007-09-20 17:09:35 +00:00
|
|
|
if (!msg)
|
|
|
|
goto out_err;
|
|
|
|
|
|
|
|
if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0)
|
|
|
|
goto out_free;
|
|
|
|
|
2009-07-07 01:56:09 +00:00
|
|
|
cfg80211_unlock_rdev(dev);
|
2007-09-20 17:09:35 +00:00
|
|
|
|
2009-07-10 09:51:34 +00:00
|
|
|
return genlmsg_reply(msg, info);
|
2007-09-20 17:09:35 +00:00
|
|
|
|
|
|
|
out_free:
|
|
|
|
nlmsg_free(msg);
|
|
|
|
out_err:
|
2009-07-07 01:56:09 +00:00
|
|
|
cfg80211_unlock_rdev(dev);
|
2007-09-20 17:09:35 +00:00
|
|
|
return -ENOBUFS;
|
|
|
|
}
|
|
|
|
|
2008-10-30 14:59:24 +00:00
|
|
|
static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = {
|
|
|
|
[NL80211_TXQ_ATTR_QUEUE] = { .type = NLA_U8 },
|
|
|
|
[NL80211_TXQ_ATTR_TXOP] = { .type = NLA_U16 },
|
|
|
|
[NL80211_TXQ_ATTR_CWMIN] = { .type = NLA_U16 },
|
|
|
|
[NL80211_TXQ_ATTR_CWMAX] = { .type = NLA_U16 },
|
|
|
|
[NL80211_TXQ_ATTR_AIFS] = { .type = NLA_U8 },
|
|
|
|
};
|
|
|
|
|
|
|
|
static int parse_txq_params(struct nlattr *tb[],
|
|
|
|
struct ieee80211_txq_params *txq_params)
|
|
|
|
{
|
|
|
|
if (!tb[NL80211_TXQ_ATTR_QUEUE] || !tb[NL80211_TXQ_ATTR_TXOP] ||
|
|
|
|
!tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] ||
|
|
|
|
!tb[NL80211_TXQ_ATTR_AIFS])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
txq_params->queue = nla_get_u8(tb[NL80211_TXQ_ATTR_QUEUE]);
|
|
|
|
txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]);
|
|
|
|
txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]);
|
|
|
|
txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]);
|
|
|
|
txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
cfg80211/mac80211: better channel handling
Currently (all tested with hwsim) you can do stupid
things like setting up an AP on a certain channel,
then adding another virtual interface and making
that associate on another channel -- this will make
the beaconing to move channel but obviously without
the necessary IEs data update.
In order to improve this situation, first make the
configuration APIs (cfg80211 and nl80211) aware of
multi-channel operation -- we'll eventually need
that in the future anyway. There's one userland API
change and one API addition. The API change is that
now SET_WIPHY must be called with virtual interface
index rather than only wiphy index in order to take
effect for that interface -- luckily all current
users (hostapd) do that. For monitor interfaces, the
old setting is preserved, but monitors are always
slaved to other devices anyway so no guarantees.
The second userland API change is the introduction
of a per virtual interface SET_CHANNEL command, that
hostapd should use going forward to make it easier
to understand what's going on (it can automatically
detect a kernel with this command).
Other than mac80211, no existing cfg80211 drivers
are affected by this change because they only allow
a single virtual interface.
mac80211, however, now needs to be aware that the
channel settings are per interface now, and needs
to disallow (for now) real multi-channel operation,
which is another important part of this patch.
One of the immediate benefits is that you can now
start hostapd to operate on a hardware that already
has a connection on another virtual interface, as
long as you specify the same channel.
Note that two things are left unhandled (this is an
improvement -- not a complete fix):
* different HT/no-HT modes
currently you could start an HT AP and then
connect to a non-HT network on the same channel
which would configure the hardware for no HT;
that can be fixed fairly easily
* CSA
An AP we're connected to on a virtual interface
might indicate switching channels, and in that
case we would follow it, regardless of how many
other interfaces are operating; this requires
more effort to fix but is pretty rare after all
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2010-05-05 13:25:02 +00:00
|
|
|
static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* You can only set the channel explicitly for AP, mesh
|
|
|
|
* and WDS type interfaces; all others have their channel
|
|
|
|
* managed via their respective "establish a connection"
|
|
|
|
* command (connect, join, ...)
|
|
|
|
*
|
|
|
|
* Monitors are special as they are normally slaved to
|
|
|
|
* whatever else is going on, so they behave as though
|
|
|
|
* you tried setting the wiphy channel itself.
|
|
|
|
*/
|
|
|
|
return !wdev ||
|
|
|
|
wdev->iftype == NL80211_IFTYPE_AP ||
|
|
|
|
wdev->iftype == NL80211_IFTYPE_WDS ||
|
|
|
|
wdev->iftype == NL80211_IFTYPE_MESH_POINT ||
|
|
|
|
wdev->iftype == NL80211_IFTYPE_MONITOR;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
|
|
|
|
struct wireless_dev *wdev,
|
|
|
|
struct genl_info *info)
|
|
|
|
{
|
|
|
|
enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
|
|
|
|
u32 freq;
|
|
|
|
int result;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!nl80211_can_set_dev_channel(wdev))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
|
|
|
|
channel_type = nla_get_u32(info->attrs[
|
|
|
|
NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
|
|
|
|
if (channel_type != NL80211_CHAN_NO_HT &&
|
|
|
|
channel_type != NL80211_CHAN_HT20 &&
|
|
|
|
channel_type != NL80211_CHAN_HT40PLUS &&
|
|
|
|
channel_type != NL80211_CHAN_HT40MINUS)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
|
|
|
|
|
|
|
|
mutex_lock(&rdev->devlist_mtx);
|
|
|
|
if (wdev) {
|
|
|
|
wdev_lock(wdev);
|
|
|
|
result = cfg80211_set_freq(rdev, wdev, freq, channel_type);
|
|
|
|
wdev_unlock(wdev);
|
|
|
|
} else {
|
|
|
|
result = cfg80211_set_freq(rdev, NULL, freq, channel_type);
|
|
|
|
}
|
|
|
|
mutex_unlock(&rdev->devlist_mtx);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev;
|
|
|
|
struct net_device *netdev;
|
|
|
|
int result;
|
|
|
|
|
|
|
|
rtnl_lock();
|
|
|
|
|
|
|
|
result = get_rdev_dev_by_info_ifindex(info, &rdev, &netdev);
|
|
|
|
if (result)
|
|
|
|
goto unlock;
|
|
|
|
|
|
|
|
result = __nl80211_set_channel(rdev, netdev->ieee80211_ptr, info);
|
|
|
|
|
|
|
|
unlock:
|
|
|
|
rtnl_unlock();
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2007-09-20 17:09:35 +00:00
|
|
|
static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev;
|
cfg80211/mac80211: better channel handling
Currently (all tested with hwsim) you can do stupid
things like setting up an AP on a certain channel,
then adding another virtual interface and making
that associate on another channel -- this will make
the beaconing to move channel but obviously without
the necessary IEs data update.
In order to improve this situation, first make the
configuration APIs (cfg80211 and nl80211) aware of
multi-channel operation -- we'll eventually need
that in the future anyway. There's one userland API
change and one API addition. The API change is that
now SET_WIPHY must be called with virtual interface
index rather than only wiphy index in order to take
effect for that interface -- luckily all current
users (hostapd) do that. For monitor interfaces, the
old setting is preserved, but monitors are always
slaved to other devices anyway so no guarantees.
The second userland API change is the introduction
of a per virtual interface SET_CHANNEL command, that
hostapd should use going forward to make it easier
to understand what's going on (it can automatically
detect a kernel with this command).
Other than mac80211, no existing cfg80211 drivers
are affected by this change because they only allow
a single virtual interface.
mac80211, however, now needs to be aware that the
channel settings are per interface now, and needs
to disallow (for now) real multi-channel operation,
which is another important part of this patch.
One of the immediate benefits is that you can now
start hostapd to operate on a hardware that already
has a connection on another virtual interface, as
long as you specify the same channel.
Note that two things are left unhandled (this is an
improvement -- not a complete fix):
* different HT/no-HT modes
currently you could start an HT AP and then
connect to a non-HT network on the same channel
which would configure the hardware for no HT;
that can be fixed fairly easily
* CSA
An AP we're connected to on a virtual interface
might indicate switching channels, and in that
case we would follow it, regardless of how many
other interfaces are operating; this requires
more effort to fix but is pretty rare after all
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2010-05-05 13:25:02 +00:00
|
|
|
struct net_device *netdev = NULL;
|
|
|
|
struct wireless_dev *wdev;
|
|
|
|
int result, rem_txq_params = 0;
|
2008-10-30 14:59:24 +00:00
|
|
|
struct nlattr *nl_txq_params;
|
2009-04-20 16:39:05 +00:00
|
|
|
u32 changed;
|
|
|
|
u8 retry_short = 0, retry_long = 0;
|
|
|
|
u32 frag_threshold = 0, rts_threshold = 0;
|
2009-12-21 21:50:47 +00:00
|
|
|
u8 coverage_class = 0;
|
2007-09-20 17:09:35 +00:00
|
|
|
|
2009-03-24 08:35:46 +00:00
|
|
|
rtnl_lock();
|
2007-09-20 17:09:35 +00:00
|
|
|
|
cfg80211/mac80211: better channel handling
Currently (all tested with hwsim) you can do stupid
things like setting up an AP on a certain channel,
then adding another virtual interface and making
that associate on another channel -- this will make
the beaconing to move channel but obviously without
the necessary IEs data update.
In order to improve this situation, first make the
configuration APIs (cfg80211 and nl80211) aware of
multi-channel operation -- we'll eventually need
that in the future anyway. There's one userland API
change and one API addition. The API change is that
now SET_WIPHY must be called with virtual interface
index rather than only wiphy index in order to take
effect for that interface -- luckily all current
users (hostapd) do that. For monitor interfaces, the
old setting is preserved, but monitors are always
slaved to other devices anyway so no guarantees.
The second userland API change is the introduction
of a per virtual interface SET_CHANNEL command, that
hostapd should use going forward to make it easier
to understand what's going on (it can automatically
detect a kernel with this command).
Other than mac80211, no existing cfg80211 drivers
are affected by this change because they only allow
a single virtual interface.
mac80211, however, now needs to be aware that the
channel settings are per interface now, and needs
to disallow (for now) real multi-channel operation,
which is another important part of this patch.
One of the immediate benefits is that you can now
start hostapd to operate on a hardware that already
has a connection on another virtual interface, as
long as you specify the same channel.
Note that two things are left unhandled (this is an
improvement -- not a complete fix):
* different HT/no-HT modes
currently you could start an HT AP and then
connect to a non-HT network on the same channel
which would configure the hardware for no HT;
that can be fixed fairly easily
* CSA
An AP we're connected to on a virtual interface
might indicate switching channels, and in that
case we would follow it, regardless of how many
other interfaces are operating; this requires
more effort to fix but is pretty rare after all
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2010-05-05 13:25:02 +00:00
|
|
|
/*
|
|
|
|
* Try to find the wiphy and netdev. Normally this
|
|
|
|
* function shouldn't need the netdev, but this is
|
|
|
|
* done for backward compatibility -- previously
|
|
|
|
* setting the channel was done per wiphy, but now
|
|
|
|
* it is per netdev. Previous userland like hostapd
|
|
|
|
* also passed a netdev to set_wiphy, so that it is
|
|
|
|
* possible to let that go to the right netdev!
|
|
|
|
*/
|
2009-03-24 08:35:46 +00:00
|
|
|
mutex_lock(&cfg80211_mutex);
|
|
|
|
|
cfg80211/mac80211: better channel handling
Currently (all tested with hwsim) you can do stupid
things like setting up an AP on a certain channel,
then adding another virtual interface and making
that associate on another channel -- this will make
the beaconing to move channel but obviously without
the necessary IEs data update.
In order to improve this situation, first make the
configuration APIs (cfg80211 and nl80211) aware of
multi-channel operation -- we'll eventually need
that in the future anyway. There's one userland API
change and one API addition. The API change is that
now SET_WIPHY must be called with virtual interface
index rather than only wiphy index in order to take
effect for that interface -- luckily all current
users (hostapd) do that. For monitor interfaces, the
old setting is preserved, but monitors are always
slaved to other devices anyway so no guarantees.
The second userland API change is the introduction
of a per virtual interface SET_CHANNEL command, that
hostapd should use going forward to make it easier
to understand what's going on (it can automatically
detect a kernel with this command).
Other than mac80211, no existing cfg80211 drivers
are affected by this change because they only allow
a single virtual interface.
mac80211, however, now needs to be aware that the
channel settings are per interface now, and needs
to disallow (for now) real multi-channel operation,
which is another important part of this patch.
One of the immediate benefits is that you can now
start hostapd to operate on a hardware that already
has a connection on another virtual interface, as
long as you specify the same channel.
Note that two things are left unhandled (this is an
improvement -- not a complete fix):
* different HT/no-HT modes
currently you could start an HT AP and then
connect to a non-HT network on the same channel
which would configure the hardware for no HT;
that can be fixed fairly easily
* CSA
An AP we're connected to on a virtual interface
might indicate switching channels, and in that
case we would follow it, regardless of how many
other interfaces are operating; this requires
more effort to fix but is pretty rare after all
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2010-05-05 13:25:02 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_IFINDEX]) {
|
|
|
|
int ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
|
|
|
|
|
|
|
|
netdev = dev_get_by_index(genl_info_net(info), ifindex);
|
|
|
|
if (netdev && netdev->ieee80211_ptr) {
|
|
|
|
rdev = wiphy_to_dev(netdev->ieee80211_ptr->wiphy);
|
|
|
|
mutex_lock(&rdev->mtx);
|
|
|
|
} else
|
|
|
|
netdev = NULL;
|
2009-03-24 08:35:46 +00:00
|
|
|
}
|
|
|
|
|
cfg80211/mac80211: better channel handling
Currently (all tested with hwsim) you can do stupid
things like setting up an AP on a certain channel,
then adding another virtual interface and making
that associate on another channel -- this will make
the beaconing to move channel but obviously without
the necessary IEs data update.
In order to improve this situation, first make the
configuration APIs (cfg80211 and nl80211) aware of
multi-channel operation -- we'll eventually need
that in the future anyway. There's one userland API
change and one API addition. The API change is that
now SET_WIPHY must be called with virtual interface
index rather than only wiphy index in order to take
effect for that interface -- luckily all current
users (hostapd) do that. For monitor interfaces, the
old setting is preserved, but monitors are always
slaved to other devices anyway so no guarantees.
The second userland API change is the introduction
of a per virtual interface SET_CHANNEL command, that
hostapd should use going forward to make it easier
to understand what's going on (it can automatically
detect a kernel with this command).
Other than mac80211, no existing cfg80211 drivers
are affected by this change because they only allow
a single virtual interface.
mac80211, however, now needs to be aware that the
channel settings are per interface now, and needs
to disallow (for now) real multi-channel operation,
which is another important part of this patch.
One of the immediate benefits is that you can now
start hostapd to operate on a hardware that already
has a connection on another virtual interface, as
long as you specify the same channel.
Note that two things are left unhandled (this is an
improvement -- not a complete fix):
* different HT/no-HT modes
currently you could start an HT AP and then
connect to a non-HT network on the same channel
which would configure the hardware for no HT;
that can be fixed fairly easily
* CSA
An AP we're connected to on a virtual interface
might indicate switching channels, and in that
case we would follow it, regardless of how many
other interfaces are operating; this requires
more effort to fix but is pretty rare after all
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2010-05-05 13:25:02 +00:00
|
|
|
if (!netdev) {
|
|
|
|
rdev = __cfg80211_rdev_from_info(info);
|
|
|
|
if (IS_ERR(rdev)) {
|
|
|
|
mutex_unlock(&cfg80211_mutex);
|
|
|
|
result = PTR_ERR(rdev);
|
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
wdev = NULL;
|
|
|
|
netdev = NULL;
|
|
|
|
result = 0;
|
|
|
|
|
|
|
|
mutex_lock(&rdev->mtx);
|
|
|
|
} else if (netif_running(netdev) &&
|
|
|
|
nl80211_can_set_dev_channel(netdev->ieee80211_ptr))
|
|
|
|
wdev = netdev->ieee80211_ptr;
|
|
|
|
else
|
|
|
|
wdev = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* end workaround code, by now the rdev is available
|
|
|
|
* and locked, and wdev may or may not be NULL.
|
|
|
|
*/
|
2009-03-24 08:35:46 +00:00
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_WIPHY_NAME])
|
2008-10-30 14:59:24 +00:00
|
|
|
result = cfg80211_dev_rename(
|
|
|
|
rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
|
2009-03-24 08:35:46 +00:00
|
|
|
|
|
|
|
mutex_unlock(&cfg80211_mutex);
|
|
|
|
|
|
|
|
if (result)
|
|
|
|
goto bad_res;
|
2008-10-30 14:59:24 +00:00
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
|
|
|
|
struct ieee80211_txq_params txq_params;
|
|
|
|
struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1];
|
|
|
|
|
|
|
|
if (!rdev->ops->set_txq_params) {
|
|
|
|
result = -EOPNOTSUPP;
|
|
|
|
goto bad_res;
|
|
|
|
}
|
|
|
|
|
|
|
|
nla_for_each_nested(nl_txq_params,
|
|
|
|
info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
|
|
|
|
rem_txq_params) {
|
|
|
|
nla_parse(tb, NL80211_TXQ_ATTR_MAX,
|
|
|
|
nla_data(nl_txq_params),
|
|
|
|
nla_len(nl_txq_params),
|
|
|
|
txq_params_policy);
|
|
|
|
result = parse_txq_params(tb, &txq_params);
|
|
|
|
if (result)
|
|
|
|
goto bad_res;
|
|
|
|
|
|
|
|
result = rdev->ops->set_txq_params(&rdev->wiphy,
|
|
|
|
&txq_params);
|
|
|
|
if (result)
|
|
|
|
goto bad_res;
|
|
|
|
}
|
|
|
|
}
|
2007-09-20 17:09:35 +00:00
|
|
|
|
2008-11-26 14:15:24 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
|
cfg80211/mac80211: better channel handling
Currently (all tested with hwsim) you can do stupid
things like setting up an AP on a certain channel,
then adding another virtual interface and making
that associate on another channel -- this will make
the beaconing to move channel but obviously without
the necessary IEs data update.
In order to improve this situation, first make the
configuration APIs (cfg80211 and nl80211) aware of
multi-channel operation -- we'll eventually need
that in the future anyway. There's one userland API
change and one API addition. The API change is that
now SET_WIPHY must be called with virtual interface
index rather than only wiphy index in order to take
effect for that interface -- luckily all current
users (hostapd) do that. For monitor interfaces, the
old setting is preserved, but monitors are always
slaved to other devices anyway so no guarantees.
The second userland API change is the introduction
of a per virtual interface SET_CHANNEL command, that
hostapd should use going forward to make it easier
to understand what's going on (it can automatically
detect a kernel with this command).
Other than mac80211, no existing cfg80211 drivers
are affected by this change because they only allow
a single virtual interface.
mac80211, however, now needs to be aware that the
channel settings are per interface now, and needs
to disallow (for now) real multi-channel operation,
which is another important part of this patch.
One of the immediate benefits is that you can now
start hostapd to operate on a hardware that already
has a connection on another virtual interface, as
long as you specify the same channel.
Note that two things are left unhandled (this is an
improvement -- not a complete fix):
* different HT/no-HT modes
currently you could start an HT AP and then
connect to a non-HT network on the same channel
which would configure the hardware for no HT;
that can be fixed fairly easily
* CSA
An AP we're connected to on a virtual interface
might indicate switching channels, and in that
case we would follow it, regardless of how many
other interfaces are operating; this requires
more effort to fix but is pretty rare after all
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2010-05-05 13:25:02 +00:00
|
|
|
result = __nl80211_set_channel(rdev, wdev, info);
|
2008-11-26 14:15:24 +00:00
|
|
|
if (result)
|
|
|
|
goto bad_res;
|
|
|
|
}
|
|
|
|
|
2010-06-23 09:12:38 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_WIPHY_TX_POWER_SETTING]) {
|
|
|
|
enum nl80211_tx_power_setting type;
|
|
|
|
int idx, mbm = 0;
|
|
|
|
|
|
|
|
if (!rdev->ops->set_tx_power) {
|
2010-07-07 13:02:46 +00:00
|
|
|
result = -EOPNOTSUPP;
|
2010-06-23 09:12:38 +00:00
|
|
|
goto bad_res;
|
|
|
|
}
|
|
|
|
|
|
|
|
idx = NL80211_ATTR_WIPHY_TX_POWER_SETTING;
|
|
|
|
type = nla_get_u32(info->attrs[idx]);
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_WIPHY_TX_POWER_LEVEL] &&
|
|
|
|
(type != NL80211_TX_POWER_AUTOMATIC)) {
|
|
|
|
result = -EINVAL;
|
|
|
|
goto bad_res;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type != NL80211_TX_POWER_AUTOMATIC) {
|
|
|
|
idx = NL80211_ATTR_WIPHY_TX_POWER_LEVEL;
|
|
|
|
mbm = nla_get_u32(info->attrs[idx]);
|
|
|
|
}
|
|
|
|
|
|
|
|
result = rdev->ops->set_tx_power(&rdev->wiphy, type, mbm);
|
|
|
|
if (result)
|
|
|
|
goto bad_res;
|
|
|
|
}
|
|
|
|
|
2009-04-20 16:39:05 +00:00
|
|
|
changed = 0;
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]) {
|
|
|
|
retry_short = nla_get_u8(
|
|
|
|
info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]);
|
|
|
|
if (retry_short == 0) {
|
|
|
|
result = -EINVAL;
|
|
|
|
goto bad_res;
|
|
|
|
}
|
|
|
|
changed |= WIPHY_PARAM_RETRY_SHORT;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]) {
|
|
|
|
retry_long = nla_get_u8(
|
|
|
|
info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]);
|
|
|
|
if (retry_long == 0) {
|
|
|
|
result = -EINVAL;
|
|
|
|
goto bad_res;
|
|
|
|
}
|
|
|
|
changed |= WIPHY_PARAM_RETRY_LONG;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]) {
|
|
|
|
frag_threshold = nla_get_u32(
|
|
|
|
info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]);
|
|
|
|
if (frag_threshold < 256) {
|
|
|
|
result = -EINVAL;
|
|
|
|
goto bad_res;
|
|
|
|
}
|
|
|
|
if (frag_threshold != (u32) -1) {
|
|
|
|
/*
|
|
|
|
* Fragments (apart from the last one) are required to
|
|
|
|
* have even length. Make the fragmentation code
|
|
|
|
* simpler by stripping LSB should someone try to use
|
|
|
|
* odd threshold value.
|
|
|
|
*/
|
|
|
|
frag_threshold &= ~0x1;
|
|
|
|
}
|
|
|
|
changed |= WIPHY_PARAM_FRAG_THRESHOLD;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]) {
|
|
|
|
rts_threshold = nla_get_u32(
|
|
|
|
info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]);
|
|
|
|
changed |= WIPHY_PARAM_RTS_THRESHOLD;
|
|
|
|
}
|
|
|
|
|
2009-12-21 21:50:47 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]) {
|
|
|
|
coverage_class = nla_get_u8(
|
|
|
|
info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]);
|
|
|
|
changed |= WIPHY_PARAM_COVERAGE_CLASS;
|
|
|
|
}
|
|
|
|
|
2009-04-20 16:39:05 +00:00
|
|
|
if (changed) {
|
|
|
|
u8 old_retry_short, old_retry_long;
|
|
|
|
u32 old_frag_threshold, old_rts_threshold;
|
2009-12-21 21:50:47 +00:00
|
|
|
u8 old_coverage_class;
|
2009-04-20 16:39:05 +00:00
|
|
|
|
|
|
|
if (!rdev->ops->set_wiphy_params) {
|
|
|
|
result = -EOPNOTSUPP;
|
|
|
|
goto bad_res;
|
|
|
|
}
|
|
|
|
|
|
|
|
old_retry_short = rdev->wiphy.retry_short;
|
|
|
|
old_retry_long = rdev->wiphy.retry_long;
|
|
|
|
old_frag_threshold = rdev->wiphy.frag_threshold;
|
|
|
|
old_rts_threshold = rdev->wiphy.rts_threshold;
|
2009-12-21 21:50:47 +00:00
|
|
|
old_coverage_class = rdev->wiphy.coverage_class;
|
2009-04-20 16:39:05 +00:00
|
|
|
|
|
|
|
if (changed & WIPHY_PARAM_RETRY_SHORT)
|
|
|
|
rdev->wiphy.retry_short = retry_short;
|
|
|
|
if (changed & WIPHY_PARAM_RETRY_LONG)
|
|
|
|
rdev->wiphy.retry_long = retry_long;
|
|
|
|
if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
|
|
|
|
rdev->wiphy.frag_threshold = frag_threshold;
|
|
|
|
if (changed & WIPHY_PARAM_RTS_THRESHOLD)
|
|
|
|
rdev->wiphy.rts_threshold = rts_threshold;
|
2009-12-21 21:50:47 +00:00
|
|
|
if (changed & WIPHY_PARAM_COVERAGE_CLASS)
|
|
|
|
rdev->wiphy.coverage_class = coverage_class;
|
2009-04-20 16:39:05 +00:00
|
|
|
|
|
|
|
result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed);
|
|
|
|
if (result) {
|
|
|
|
rdev->wiphy.retry_short = old_retry_short;
|
|
|
|
rdev->wiphy.retry_long = old_retry_long;
|
|
|
|
rdev->wiphy.frag_threshold = old_frag_threshold;
|
|
|
|
rdev->wiphy.rts_threshold = old_rts_threshold;
|
2009-12-21 21:50:47 +00:00
|
|
|
rdev->wiphy.coverage_class = old_coverage_class;
|
2009-04-20 16:39:05 +00:00
|
|
|
}
|
|
|
|
}
|
2008-11-26 14:15:24 +00:00
|
|
|
|
2008-12-08 11:39:04 +00:00
|
|
|
bad_res:
|
2009-03-24 08:35:46 +00:00
|
|
|
mutex_unlock(&rdev->mtx);
|
cfg80211/mac80211: better channel handling
Currently (all tested with hwsim) you can do stupid
things like setting up an AP on a certain channel,
then adding another virtual interface and making
that associate on another channel -- this will make
the beaconing to move channel but obviously without
the necessary IEs data update.
In order to improve this situation, first make the
configuration APIs (cfg80211 and nl80211) aware of
multi-channel operation -- we'll eventually need
that in the future anyway. There's one userland API
change and one API addition. The API change is that
now SET_WIPHY must be called with virtual interface
index rather than only wiphy index in order to take
effect for that interface -- luckily all current
users (hostapd) do that. For monitor interfaces, the
old setting is preserved, but monitors are always
slaved to other devices anyway so no guarantees.
The second userland API change is the introduction
of a per virtual interface SET_CHANNEL command, that
hostapd should use going forward to make it easier
to understand what's going on (it can automatically
detect a kernel with this command).
Other than mac80211, no existing cfg80211 drivers
are affected by this change because they only allow
a single virtual interface.
mac80211, however, now needs to be aware that the
channel settings are per interface now, and needs
to disallow (for now) real multi-channel operation,
which is another important part of this patch.
One of the immediate benefits is that you can now
start hostapd to operate on a hardware that already
has a connection on another virtual interface, as
long as you specify the same channel.
Note that two things are left unhandled (this is an
improvement -- not a complete fix):
* different HT/no-HT modes
currently you could start an HT AP and then
connect to a non-HT network on the same channel
which would configure the hardware for no HT;
that can be fixed fairly easily
* CSA
An AP we're connected to on a virtual interface
might indicate switching channels, and in that
case we would follow it, regardless of how many
other interfaces are operating; this requires
more effort to fix but is pretty rare after all
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2010-05-05 13:25:02 +00:00
|
|
|
if (netdev)
|
|
|
|
dev_put(netdev);
|
2009-03-24 08:35:46 +00:00
|
|
|
unlock:
|
|
|
|
rtnl_unlock();
|
2007-09-20 17:09:35 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
|
2009-04-19 14:23:20 +00:00
|
|
|
struct cfg80211_registered_device *rdev,
|
2007-09-20 17:09:35 +00:00
|
|
|
struct net_device *dev)
|
|
|
|
{
|
|
|
|
void *hdr;
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE);
|
|
|
|
if (!hdr)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
|
2009-04-19 14:23:20 +00:00
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
|
2007-09-20 17:09:35 +00:00
|
|
|
NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name);
|
2008-09-16 12:55:09 +00:00
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, dev->ieee80211_ptr->iftype);
|
2009-08-07 14:17:38 +00:00
|
|
|
|
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_GENERATION,
|
|
|
|
rdev->devlist_generation ^
|
|
|
|
(cfg80211_rdev_list_generation << 2));
|
|
|
|
|
2007-09-20 17:09:35 +00:00
|
|
|
return genlmsg_end(msg, hdr);
|
|
|
|
|
|
|
|
nla_put_failure:
|
2008-06-03 23:36:54 +00:00
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
return -EMSGSIZE;
|
2007-09-20 17:09:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
|
|
|
|
{
|
|
|
|
int wp_idx = 0;
|
|
|
|
int if_idx = 0;
|
|
|
|
int wp_start = cb->args[0];
|
|
|
|
int if_start = cb->args[1];
|
2009-08-07 14:17:38 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
2007-09-20 17:09:35 +00:00
|
|
|
struct wireless_dev *wdev;
|
|
|
|
|
2009-02-21 05:04:21 +00:00
|
|
|
mutex_lock(&cfg80211_mutex);
|
2009-08-07 14:17:38 +00:00
|
|
|
list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
|
|
|
|
if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk)))
|
2009-07-13 22:33:35 +00:00
|
|
|
continue;
|
2008-07-29 11:22:51 +00:00
|
|
|
if (wp_idx < wp_start) {
|
|
|
|
wp_idx++;
|
2007-09-20 17:09:35 +00:00
|
|
|
continue;
|
2008-07-29 11:22:51 +00:00
|
|
|
}
|
2007-09-20 17:09:35 +00:00
|
|
|
if_idx = 0;
|
|
|
|
|
2009-08-07 14:17:38 +00:00
|
|
|
mutex_lock(&rdev->devlist_mtx);
|
|
|
|
list_for_each_entry(wdev, &rdev->netdev_list, list) {
|
2008-07-29 11:22:51 +00:00
|
|
|
if (if_idx < if_start) {
|
|
|
|
if_idx++;
|
2007-09-20 17:09:35 +00:00
|
|
|
continue;
|
2008-07-29 11:22:51 +00:00
|
|
|
}
|
2007-09-20 17:09:35 +00:00
|
|
|
if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid,
|
|
|
|
cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
2009-08-07 14:17:38 +00:00
|
|
|
rdev, wdev->netdev) < 0) {
|
|
|
|
mutex_unlock(&rdev->devlist_mtx);
|
2008-07-29 11:22:51 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if_idx++;
|
2007-09-20 17:09:35 +00:00
|
|
|
}
|
2009-08-07 14:17:38 +00:00
|
|
|
mutex_unlock(&rdev->devlist_mtx);
|
2008-07-29 11:22:51 +00:00
|
|
|
|
|
|
|
wp_idx++;
|
2007-09-20 17:09:35 +00:00
|
|
|
}
|
2008-07-29 11:22:51 +00:00
|
|
|
out:
|
2009-02-21 05:04:21 +00:00
|
|
|
mutex_unlock(&cfg80211_mutex);
|
2007-09-20 17:09:35 +00:00
|
|
|
|
|
|
|
cb->args[0] = wp_idx;
|
|
|
|
cb->args[1] = if_idx;
|
|
|
|
|
|
|
|
return skb->len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct sk_buff *msg;
|
|
|
|
struct cfg80211_registered_device *dev;
|
|
|
|
struct net_device *netdev;
|
|
|
|
int err;
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &dev, &netdev);
|
2007-09-20 17:09:35 +00:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2009-05-19 22:27:55 +00:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
2007-09-20 17:09:35 +00:00
|
|
|
if (!msg)
|
|
|
|
goto out_err;
|
|
|
|
|
2009-04-19 14:23:20 +00:00
|
|
|
if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0,
|
|
|
|
dev, netdev) < 0)
|
2007-09-20 17:09:35 +00:00
|
|
|
goto out_free;
|
|
|
|
|
|
|
|
dev_put(netdev);
|
2009-07-07 01:56:09 +00:00
|
|
|
cfg80211_unlock_rdev(dev);
|
2007-09-20 17:09:35 +00:00
|
|
|
|
2009-07-10 09:51:34 +00:00
|
|
|
return genlmsg_reply(msg, info);
|
2007-09-20 17:09:35 +00:00
|
|
|
|
|
|
|
out_free:
|
|
|
|
nlmsg_free(msg);
|
|
|
|
out_err:
|
|
|
|
dev_put(netdev);
|
2009-07-07 01:56:09 +00:00
|
|
|
cfg80211_unlock_rdev(dev);
|
2007-09-20 17:09:35 +00:00
|
|
|
return -ENOBUFS;
|
|
|
|
}
|
|
|
|
|
2008-01-31 18:48:22 +00:00
|
|
|
static const struct nla_policy mntr_flags_policy[NL80211_MNTR_FLAG_MAX + 1] = {
|
|
|
|
[NL80211_MNTR_FLAG_FCSFAIL] = { .type = NLA_FLAG },
|
|
|
|
[NL80211_MNTR_FLAG_PLCPFAIL] = { .type = NLA_FLAG },
|
|
|
|
[NL80211_MNTR_FLAG_CONTROL] = { .type = NLA_FLAG },
|
|
|
|
[NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG },
|
|
|
|
[NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG },
|
|
|
|
};
|
|
|
|
|
|
|
|
static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags)
|
|
|
|
{
|
|
|
|
struct nlattr *flags[NL80211_MNTR_FLAG_MAX + 1];
|
|
|
|
int flag;
|
|
|
|
|
|
|
|
*mntrflags = 0;
|
|
|
|
|
|
|
|
if (!nla)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (nla_parse_nested(flags, NL80211_MNTR_FLAG_MAX,
|
|
|
|
nla, mntr_flags_policy))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
for (flag = 1; flag <= NL80211_MNTR_FLAG_MAX; flag++)
|
|
|
|
if (flags[flag])
|
|
|
|
*mntrflags |= (1<<flag);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-11-19 10:55:19 +00:00
|
|
|
static int nl80211_valid_4addr(struct cfg80211_registered_device *rdev,
|
cfg80211: disallow bridging managed/adhoc interfaces
A number of people have tried to add a wireless interface
(in managed mode) to a bridge and then complained that it
doesn't work. It cannot work, however, because in 802.11
networks all packets need to be acknowledged and as such
need to be sent to the right address. Promiscuous doesn't
help here. The wireless address format used for these
links has only space for three addresses, the
* transmitter, which must be equal to the sender (origin)
* receiver (on the wireless medium), which is the AP in
the case of managed mode
* the recipient (destination), which is on the APs local
network segment
In an IBSS, it is similar, but the receiver and recipient
must match and the third address is used as the BSSID.
To avoid such mistakes in the future, disallow adding a
wireless interface to a bridge.
Felix has recently added a four-address mode to the AP
and client side that can be used (after negotiating that
it is possible, which must happen out-of-band by setting
up both sides) for bridging, so allow that case.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Acked-by: Stephen Hemminger <shemminger@vyatta.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-11-18 23:56:30 +00:00
|
|
|
struct net_device *netdev, u8 use_4addr,
|
|
|
|
enum nl80211_iftype iftype)
|
2009-11-19 10:55:19 +00:00
|
|
|
{
|
cfg80211: disallow bridging managed/adhoc interfaces
A number of people have tried to add a wireless interface
(in managed mode) to a bridge and then complained that it
doesn't work. It cannot work, however, because in 802.11
networks all packets need to be acknowledged and as such
need to be sent to the right address. Promiscuous doesn't
help here. The wireless address format used for these
links has only space for three addresses, the
* transmitter, which must be equal to the sender (origin)
* receiver (on the wireless medium), which is the AP in
the case of managed mode
* the recipient (destination), which is on the APs local
network segment
In an IBSS, it is similar, but the receiver and recipient
must match and the third address is used as the BSSID.
To avoid such mistakes in the future, disallow adding a
wireless interface to a bridge.
Felix has recently added a four-address mode to the AP
and client side that can be used (after negotiating that
it is possible, which must happen out-of-band by setting
up both sides) for bridging, so allow that case.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Acked-by: Stephen Hemminger <shemminger@vyatta.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-11-18 23:56:30 +00:00
|
|
|
if (!use_4addr) {
|
|
|
|
if (netdev && netdev->br_port)
|
|
|
|
return -EBUSY;
|
2009-11-19 10:55:19 +00:00
|
|
|
return 0;
|
cfg80211: disallow bridging managed/adhoc interfaces
A number of people have tried to add a wireless interface
(in managed mode) to a bridge and then complained that it
doesn't work. It cannot work, however, because in 802.11
networks all packets need to be acknowledged and as such
need to be sent to the right address. Promiscuous doesn't
help here. The wireless address format used for these
links has only space for three addresses, the
* transmitter, which must be equal to the sender (origin)
* receiver (on the wireless medium), which is the AP in
the case of managed mode
* the recipient (destination), which is on the APs local
network segment
In an IBSS, it is similar, but the receiver and recipient
must match and the third address is used as the BSSID.
To avoid such mistakes in the future, disallow adding a
wireless interface to a bridge.
Felix has recently added a four-address mode to the AP
and client side that can be used (after negotiating that
it is possible, which must happen out-of-band by setting
up both sides) for bridging, so allow that case.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Acked-by: Stephen Hemminger <shemminger@vyatta.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-11-18 23:56:30 +00:00
|
|
|
}
|
2009-11-19 10:55:19 +00:00
|
|
|
|
|
|
|
switch (iftype) {
|
|
|
|
case NL80211_IFTYPE_AP_VLAN:
|
|
|
|
if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_AP)
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case NL80211_IFTYPE_STATION:
|
|
|
|
if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_STATION)
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
2007-09-20 17:09:35 +00:00
|
|
|
static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2009-07-07 01:56:12 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
2008-02-23 14:17:06 +00:00
|
|
|
struct vif_params params;
|
2009-06-09 19:04:43 +00:00
|
|
|
int err;
|
2009-04-19 19:24:32 +00:00
|
|
|
enum nl80211_iftype otype, ntype;
|
2007-09-20 17:09:35 +00:00
|
|
|
struct net_device *dev;
|
2008-09-16 18:39:36 +00:00
|
|
|
u32 _flags, *flags = NULL;
|
2009-03-21 16:07:59 +00:00
|
|
|
bool change = false;
|
2007-09-20 17:09:35 +00:00
|
|
|
|
2008-02-23 14:17:06 +00:00
|
|
|
memset(¶ms, 0, sizeof(params));
|
|
|
|
|
2009-03-12 08:55:09 +00:00
|
|
|
rtnl_lock();
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
2007-09-20 17:09:35 +00:00
|
|
|
if (err)
|
2009-03-12 08:55:09 +00:00
|
|
|
goto unlock_rtnl;
|
|
|
|
|
2009-04-19 19:24:32 +00:00
|
|
|
otype = ntype = dev->ieee80211_ptr->iftype;
|
2007-09-20 17:09:35 +00:00
|
|
|
|
2008-09-16 18:22:09 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_IFTYPE]) {
|
2009-03-21 16:07:59 +00:00
|
|
|
ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
|
2009-04-19 19:24:32 +00:00
|
|
|
if (otype != ntype)
|
2009-03-21 16:07:59 +00:00
|
|
|
change = true;
|
2009-04-19 19:24:32 +00:00
|
|
|
if (ntype > NL80211_IFTYPE_MAX) {
|
2009-03-21 16:07:59 +00:00
|
|
|
err = -EINVAL;
|
2008-09-16 18:22:09 +00:00
|
|
|
goto unlock;
|
2009-03-21 16:07:59 +00:00
|
|
|
}
|
2008-09-16 18:22:09 +00:00
|
|
|
}
|
|
|
|
|
2008-09-16 18:39:36 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_MESH_ID]) {
|
2009-04-19 19:24:32 +00:00
|
|
|
if (ntype != NL80211_IFTYPE_MESH_POINT) {
|
2008-09-16 18:39:36 +00:00
|
|
|
err = -EINVAL;
|
|
|
|
goto unlock;
|
|
|
|
}
|
2008-02-23 14:17:06 +00:00
|
|
|
params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
|
|
|
|
params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
|
2009-03-21 16:07:59 +00:00
|
|
|
change = true;
|
2008-02-23 14:17:06 +00:00
|
|
|
}
|
|
|
|
|
2009-11-10 17:53:10 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_4ADDR]) {
|
|
|
|
params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
|
|
|
|
change = true;
|
cfg80211: disallow bridging managed/adhoc interfaces
A number of people have tried to add a wireless interface
(in managed mode) to a bridge and then complained that it
doesn't work. It cannot work, however, because in 802.11
networks all packets need to be acknowledged and as such
need to be sent to the right address. Promiscuous doesn't
help here. The wireless address format used for these
links has only space for three addresses, the
* transmitter, which must be equal to the sender (origin)
* receiver (on the wireless medium), which is the AP in
the case of managed mode
* the recipient (destination), which is on the APs local
network segment
In an IBSS, it is similar, but the receiver and recipient
must match and the third address is used as the BSSID.
To avoid such mistakes in the future, disallow adding a
wireless interface to a bridge.
Felix has recently added a four-address mode to the AP
and client side that can be used (after negotiating that
it is possible, which must happen out-of-band by setting
up both sides) for bridging, so allow that case.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Acked-by: Stephen Hemminger <shemminger@vyatta.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-11-18 23:56:30 +00:00
|
|
|
err = nl80211_valid_4addr(rdev, dev, params.use_4addr, ntype);
|
2009-11-19 10:55:19 +00:00
|
|
|
if (err)
|
|
|
|
goto unlock;
|
2009-11-10 17:53:10 +00:00
|
|
|
} else {
|
|
|
|
params.use_4addr = -1;
|
|
|
|
}
|
|
|
|
|
2008-09-16 18:39:36 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) {
|
2009-04-19 19:24:32 +00:00
|
|
|
if (ntype != NL80211_IFTYPE_MONITOR) {
|
2008-09-16 18:39:36 +00:00
|
|
|
err = -EINVAL;
|
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS],
|
|
|
|
&_flags);
|
2009-03-21 16:07:59 +00:00
|
|
|
if (err)
|
|
|
|
goto unlock;
|
|
|
|
|
|
|
|
flags = &_flags;
|
|
|
|
change = true;
|
2008-09-16 18:39:36 +00:00
|
|
|
}
|
2009-03-12 08:55:09 +00:00
|
|
|
|
2009-03-21 16:07:59 +00:00
|
|
|
if (change)
|
2009-08-21 12:51:05 +00:00
|
|
|
err = cfg80211_change_iface(rdev, dev, ntype, flags, ¶ms);
|
2009-03-21 16:07:59 +00:00
|
|
|
else
|
|
|
|
err = 0;
|
2008-09-16 12:55:09 +00:00
|
|
|
|
2009-11-19 10:55:19 +00:00
|
|
|
if (!err && params.use_4addr != -1)
|
|
|
|
dev->ieee80211_ptr->use_4addr = params.use_4addr;
|
|
|
|
|
2007-09-20 17:09:35 +00:00
|
|
|
unlock:
|
2009-06-09 19:04:43 +00:00
|
|
|
dev_put(dev);
|
2009-07-07 01:56:12 +00:00
|
|
|
cfg80211_unlock_rdev(rdev);
|
2009-03-12 08:55:09 +00:00
|
|
|
unlock_rtnl:
|
|
|
|
rtnl_unlock();
|
2007-09-20 17:09:35 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2009-07-07 01:56:12 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
2008-02-23 14:17:06 +00:00
|
|
|
struct vif_params params;
|
2007-09-20 17:09:35 +00:00
|
|
|
int err;
|
|
|
|
enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
|
2008-01-31 18:48:22 +00:00
|
|
|
u32 flags;
|
2007-09-20 17:09:35 +00:00
|
|
|
|
2008-02-23 14:17:06 +00:00
|
|
|
memset(¶ms, 0, sizeof(params));
|
|
|
|
|
2007-09-20 17:09:35 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2009-03-12 08:55:09 +00:00
|
|
|
rtnl_lock();
|
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
rdev = cfg80211_get_dev_from_info(info);
|
|
|
|
if (IS_ERR(rdev)) {
|
|
|
|
err = PTR_ERR(rdev);
|
2009-03-12 08:55:09 +00:00
|
|
|
goto unlock_rtnl;
|
|
|
|
}
|
2007-09-20 17:09:35 +00:00
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
if (!rdev->ops->add_virtual_intf ||
|
|
|
|
!(rdev->wiphy.interface_modes & (1 << type))) {
|
2007-09-20 17:09:35 +00:00
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
|
2008-02-23 14:17:06 +00:00
|
|
|
if (type == NL80211_IFTYPE_MESH_POINT &&
|
|
|
|
info->attrs[NL80211_ATTR_MESH_ID]) {
|
|
|
|
params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
|
|
|
|
params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
|
|
|
|
}
|
|
|
|
|
2009-11-19 10:55:19 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_4ADDR]) {
|
2009-11-10 17:53:10 +00:00
|
|
|
params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
|
cfg80211: disallow bridging managed/adhoc interfaces
A number of people have tried to add a wireless interface
(in managed mode) to a bridge and then complained that it
doesn't work. It cannot work, however, because in 802.11
networks all packets need to be acknowledged and as such
need to be sent to the right address. Promiscuous doesn't
help here. The wireless address format used for these
links has only space for three addresses, the
* transmitter, which must be equal to the sender (origin)
* receiver (on the wireless medium), which is the AP in
the case of managed mode
* the recipient (destination), which is on the APs local
network segment
In an IBSS, it is similar, but the receiver and recipient
must match and the third address is used as the BSSID.
To avoid such mistakes in the future, disallow adding a
wireless interface to a bridge.
Felix has recently added a four-address mode to the AP
and client side that can be used (after negotiating that
it is possible, which must happen out-of-band by setting
up both sides) for bridging, so allow that case.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Acked-by: Stephen Hemminger <shemminger@vyatta.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-11-18 23:56:30 +00:00
|
|
|
err = nl80211_valid_4addr(rdev, NULL, params.use_4addr, type);
|
2009-11-19 10:55:19 +00:00
|
|
|
if (err)
|
|
|
|
goto unlock;
|
|
|
|
}
|
2009-11-10 17:53:10 +00:00
|
|
|
|
2008-01-31 18:48:22 +00:00
|
|
|
err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
|
|
|
|
info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
|
|
|
|
&flags);
|
2009-07-07 01:56:12 +00:00
|
|
|
err = rdev->ops->add_virtual_intf(&rdev->wiphy,
|
2008-01-31 18:48:22 +00:00
|
|
|
nla_data(info->attrs[NL80211_ATTR_IFNAME]),
|
2008-02-23 14:17:06 +00:00
|
|
|
type, err ? NULL : &flags, ¶ms);
|
|
|
|
|
2007-09-20 17:09:35 +00:00
|
|
|
unlock:
|
2009-07-07 01:56:12 +00:00
|
|
|
cfg80211_unlock_rdev(rdev);
|
2009-03-12 08:55:09 +00:00
|
|
|
unlock_rtnl:
|
|
|
|
rtnl_unlock();
|
2007-09-20 17:09:35 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2009-07-07 01:56:12 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
2009-07-13 22:33:35 +00:00
|
|
|
int err;
|
2007-09-20 17:09:35 +00:00
|
|
|
struct net_device *dev;
|
|
|
|
|
2009-03-12 08:55:09 +00:00
|
|
|
rtnl_lock();
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
2007-09-20 17:09:35 +00:00
|
|
|
if (err)
|
2009-03-12 08:55:09 +00:00
|
|
|
goto unlock_rtnl;
|
2007-09-20 17:09:35 +00:00
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
if (!rdev->ops->del_virtual_intf) {
|
2007-09-20 17:09:35 +00:00
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
err = rdev->ops->del_virtual_intf(&rdev->wiphy, dev);
|
2007-09-20 17:09:35 +00:00
|
|
|
|
|
|
|
out:
|
2009-07-07 01:56:12 +00:00
|
|
|
cfg80211_unlock_rdev(rdev);
|
2009-07-13 22:33:35 +00:00
|
|
|
dev_put(dev);
|
2009-03-12 08:55:09 +00:00
|
|
|
unlock_rtnl:
|
|
|
|
rtnl_unlock();
|
2007-09-20 17:09:35 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2007-12-19 01:03:29 +00:00
|
|
|
struct get_key_cookie {
|
|
|
|
struct sk_buff *msg;
|
|
|
|
int error;
|
2009-07-08 11:29:08 +00:00
|
|
|
int idx;
|
2007-12-19 01:03:29 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static void get_key_callback(void *c, struct key_params *params)
|
|
|
|
{
|
2009-07-08 11:29:08 +00:00
|
|
|
struct nlattr *key;
|
2007-12-19 01:03:29 +00:00
|
|
|
struct get_key_cookie *cookie = c;
|
|
|
|
|
|
|
|
if (params->key)
|
|
|
|
NLA_PUT(cookie->msg, NL80211_ATTR_KEY_DATA,
|
|
|
|
params->key_len, params->key);
|
|
|
|
|
|
|
|
if (params->seq)
|
|
|
|
NLA_PUT(cookie->msg, NL80211_ATTR_KEY_SEQ,
|
|
|
|
params->seq_len, params->seq);
|
|
|
|
|
|
|
|
if (params->cipher)
|
|
|
|
NLA_PUT_U32(cookie->msg, NL80211_ATTR_KEY_CIPHER,
|
|
|
|
params->cipher);
|
|
|
|
|
2009-07-08 11:29:08 +00:00
|
|
|
key = nla_nest_start(cookie->msg, NL80211_ATTR_KEY);
|
|
|
|
if (!key)
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
if (params->key)
|
|
|
|
NLA_PUT(cookie->msg, NL80211_KEY_DATA,
|
|
|
|
params->key_len, params->key);
|
|
|
|
|
|
|
|
if (params->seq)
|
|
|
|
NLA_PUT(cookie->msg, NL80211_KEY_SEQ,
|
|
|
|
params->seq_len, params->seq);
|
|
|
|
|
|
|
|
if (params->cipher)
|
|
|
|
NLA_PUT_U32(cookie->msg, NL80211_KEY_CIPHER,
|
|
|
|
params->cipher);
|
|
|
|
|
|
|
|
NLA_PUT_U8(cookie->msg, NL80211_ATTR_KEY_IDX, cookie->idx);
|
|
|
|
|
|
|
|
nla_nest_end(cookie->msg, key);
|
|
|
|
|
2007-12-19 01:03:29 +00:00
|
|
|
return;
|
|
|
|
nla_put_failure:
|
|
|
|
cookie->error = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2009-07-07 01:56:12 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
2007-12-19 01:03:29 +00:00
|
|
|
int err;
|
|
|
|
struct net_device *dev;
|
|
|
|
u8 key_idx = 0;
|
|
|
|
u8 *mac_addr = NULL;
|
|
|
|
struct get_key_cookie cookie = {
|
|
|
|
.error = 0,
|
|
|
|
};
|
|
|
|
void *hdr;
|
|
|
|
struct sk_buff *msg;
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_KEY_IDX])
|
|
|
|
key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
|
|
|
|
|
2009-01-08 11:32:02 +00:00
|
|
|
if (key_idx > 5)
|
2007-12-19 01:03:29 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_MAC])
|
|
|
|
mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
|
|
|
|
2009-03-12 08:55:09 +00:00
|
|
|
rtnl_lock();
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
2007-12-19 01:03:29 +00:00
|
|
|
if (err)
|
2009-03-12 08:55:09 +00:00
|
|
|
goto unlock_rtnl;
|
2007-12-19 01:03:29 +00:00
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
if (!rdev->ops->get_key) {
|
2007-12-19 01:03:29 +00:00
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-05-19 22:27:55 +00:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
2007-12-19 01:03:29 +00:00
|
|
|
if (!msg) {
|
|
|
|
err = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
|
|
|
|
NL80211_CMD_NEW_KEY);
|
|
|
|
|
|
|
|
if (IS_ERR(hdr)) {
|
|
|
|
err = PTR_ERR(hdr);
|
2009-07-15 08:00:53 +00:00
|
|
|
goto free_msg;
|
2007-12-19 01:03:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
cookie.msg = msg;
|
2009-07-08 11:29:08 +00:00
|
|
|
cookie.idx = key_idx;
|
2007-12-19 01:03:29 +00:00
|
|
|
|
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
|
|
|
|
NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
|
|
|
|
if (mac_addr)
|
|
|
|
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
|
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
err = rdev->ops->get_key(&rdev->wiphy, dev, key_idx, mac_addr,
|
2007-12-19 01:03:29 +00:00
|
|
|
&cookie, get_key_callback);
|
|
|
|
|
|
|
|
if (err)
|
2009-07-15 08:00:53 +00:00
|
|
|
goto free_msg;
|
2007-12-19 01:03:29 +00:00
|
|
|
|
|
|
|
if (cookie.error)
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
genlmsg_end(msg, hdr);
|
2009-07-10 09:51:34 +00:00
|
|
|
err = genlmsg_reply(msg, info);
|
2007-12-19 01:03:29 +00:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
err = -ENOBUFS;
|
2009-07-15 08:00:53 +00:00
|
|
|
free_msg:
|
2007-12-19 01:03:29 +00:00
|
|
|
nlmsg_free(msg);
|
|
|
|
out:
|
2009-07-07 01:56:12 +00:00
|
|
|
cfg80211_unlock_rdev(rdev);
|
2007-12-19 01:03:29 +00:00
|
|
|
dev_put(dev);
|
2009-03-12 08:55:09 +00:00
|
|
|
unlock_rtnl:
|
|
|
|
rtnl_unlock();
|
|
|
|
|
2007-12-19 01:03:29 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2009-07-07 01:56:12 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
2009-07-08 11:29:08 +00:00
|
|
|
struct key_parse key;
|
2007-12-19 01:03:29 +00:00
|
|
|
int err;
|
|
|
|
struct net_device *dev;
|
2009-01-08 11:32:02 +00:00
|
|
|
int (*func)(struct wiphy *wiphy, struct net_device *netdev,
|
|
|
|
u8 key_index);
|
2007-12-19 01:03:29 +00:00
|
|
|
|
2009-07-08 11:29:08 +00:00
|
|
|
err = nl80211_parse_key(info, &key);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2007-12-19 01:03:29 +00:00
|
|
|
|
2009-07-08 11:29:08 +00:00
|
|
|
if (key.idx < 0)
|
2007-12-19 01:03:29 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2009-07-08 11:29:08 +00:00
|
|
|
/* only support setting default key */
|
|
|
|
if (!key.def && !key.defmgmt)
|
2007-12-19 01:03:29 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2009-03-12 08:55:09 +00:00
|
|
|
rtnl_lock();
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
2007-12-19 01:03:29 +00:00
|
|
|
if (err)
|
2009-03-12 08:55:09 +00:00
|
|
|
goto unlock_rtnl;
|
2007-12-19 01:03:29 +00:00
|
|
|
|
2009-07-08 11:29:08 +00:00
|
|
|
if (key.def)
|
2009-07-07 01:56:12 +00:00
|
|
|
func = rdev->ops->set_default_key;
|
2009-01-08 11:32:02 +00:00
|
|
|
else
|
2009-07-07 01:56:12 +00:00
|
|
|
func = rdev->ops->set_default_mgmt_key;
|
2009-01-08 11:32:02 +00:00
|
|
|
|
|
|
|
if (!func) {
|
2007-12-19 01:03:29 +00:00
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
cfg80211: rework key operation
This reworks the key operation in cfg80211, and now only
allows, from userspace, configuring keys (via nl80211)
after the connection has been established (in managed
mode), the IBSS been joined (in IBSS mode), at any time
(in AP[_VLAN] modes) or never for all the other modes.
In order to do shared key authentication correctly, it
is now possible to give a WEP key to the AUTH command.
To configure static WEP keys, these are given to the
CONNECT or IBSS_JOIN command directly, for a userspace
SME it is assumed it will configure it properly after
the connection has been established.
Since mac80211 used to check the default key in IBSS
mode to see whether or not the network is protected,
it needs an update in that area, as well as an update
to make use of the WEP key passed to auth() for shared
key authentication.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-07-08 12:22:54 +00:00
|
|
|
wdev_lock(dev->ieee80211_ptr);
|
|
|
|
err = nl80211_key_allowed(dev->ieee80211_ptr);
|
|
|
|
if (!err)
|
|
|
|
err = func(&rdev->wiphy, dev, key.idx);
|
|
|
|
|
2009-09-29 21:27:28 +00:00
|
|
|
#ifdef CONFIG_CFG80211_WEXT
|
2009-05-11 11:54:58 +00:00
|
|
|
if (!err) {
|
2009-07-07 01:56:12 +00:00
|
|
|
if (func == rdev->ops->set_default_key)
|
2009-07-08 11:29:08 +00:00
|
|
|
dev->ieee80211_ptr->wext.default_key = key.idx;
|
2009-05-11 11:54:58 +00:00
|
|
|
else
|
2009-07-08 11:29:08 +00:00
|
|
|
dev->ieee80211_ptr->wext.default_mgmt_key = key.idx;
|
2009-05-11 11:54:58 +00:00
|
|
|
}
|
|
|
|
#endif
|
cfg80211: rework key operation
This reworks the key operation in cfg80211, and now only
allows, from userspace, configuring keys (via nl80211)
after the connection has been established (in managed
mode), the IBSS been joined (in IBSS mode), at any time
(in AP[_VLAN] modes) or never for all the other modes.
In order to do shared key authentication correctly, it
is now possible to give a WEP key to the AUTH command.
To configure static WEP keys, these are given to the
CONNECT or IBSS_JOIN command directly, for a userspace
SME it is assumed it will configure it properly after
the connection has been established.
Since mac80211 used to check the default key in IBSS
mode to see whether or not the network is protected,
it needs an update in that area, as well as an update
to make use of the WEP key passed to auth() for shared
key authentication.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-07-08 12:22:54 +00:00
|
|
|
wdev_unlock(dev->ieee80211_ptr);
|
2007-12-19 01:03:29 +00:00
|
|
|
|
|
|
|
out:
|
2009-07-07 01:56:12 +00:00
|
|
|
cfg80211_unlock_rdev(rdev);
|
2007-12-19 01:03:29 +00:00
|
|
|
dev_put(dev);
|
2009-03-12 08:55:09 +00:00
|
|
|
|
|
|
|
unlock_rtnl:
|
|
|
|
rtnl_unlock();
|
|
|
|
|
2007-12-19 01:03:29 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2009-07-07 01:56:12 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
cfg80211: rework key operation
This reworks the key operation in cfg80211, and now only
allows, from userspace, configuring keys (via nl80211)
after the connection has been established (in managed
mode), the IBSS been joined (in IBSS mode), at any time
(in AP[_VLAN] modes) or never for all the other modes.
In order to do shared key authentication correctly, it
is now possible to give a WEP key to the AUTH command.
To configure static WEP keys, these are given to the
CONNECT or IBSS_JOIN command directly, for a userspace
SME it is assumed it will configure it properly after
the connection has been established.
Since mac80211 used to check the default key in IBSS
mode to see whether or not the network is protected,
it needs an update in that area, as well as an update
to make use of the WEP key passed to auth() for shared
key authentication.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-07-08 12:22:54 +00:00
|
|
|
int err;
|
2007-12-19 01:03:29 +00:00
|
|
|
struct net_device *dev;
|
2009-07-08 11:29:08 +00:00
|
|
|
struct key_parse key;
|
2007-12-19 01:03:29 +00:00
|
|
|
u8 *mac_addr = NULL;
|
|
|
|
|
2009-07-08 11:29:08 +00:00
|
|
|
err = nl80211_parse_key(info, &key);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2007-12-19 01:03:29 +00:00
|
|
|
|
2009-07-08 11:29:08 +00:00
|
|
|
if (!key.p.key)
|
2007-12-19 01:03:29 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_MAC])
|
|
|
|
mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
|
|
|
|
2009-03-12 08:55:09 +00:00
|
|
|
rtnl_lock();
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
2007-12-19 01:03:29 +00:00
|
|
|
if (err)
|
2009-03-12 08:55:09 +00:00
|
|
|
goto unlock_rtnl;
|
2007-12-19 01:03:29 +00:00
|
|
|
|
cfg80211: rework key operation
This reworks the key operation in cfg80211, and now only
allows, from userspace, configuring keys (via nl80211)
after the connection has been established (in managed
mode), the IBSS been joined (in IBSS mode), at any time
(in AP[_VLAN] modes) or never for all the other modes.
In order to do shared key authentication correctly, it
is now possible to give a WEP key to the AUTH command.
To configure static WEP keys, these are given to the
CONNECT or IBSS_JOIN command directly, for a userspace
SME it is assumed it will configure it properly after
the connection has been established.
Since mac80211 used to check the default key in IBSS
mode to see whether or not the network is protected,
it needs an update in that area, as well as an update
to make use of the WEP key passed to auth() for shared
key authentication.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-07-08 12:22:54 +00:00
|
|
|
if (!rdev->ops->add_key) {
|
|
|
|
err = -EOPNOTSUPP;
|
2009-04-02 18:14:06 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
cfg80211: rework key operation
This reworks the key operation in cfg80211, and now only
allows, from userspace, configuring keys (via nl80211)
after the connection has been established (in managed
mode), the IBSS been joined (in IBSS mode), at any time
(in AP[_VLAN] modes) or never for all the other modes.
In order to do shared key authentication correctly, it
is now possible to give a WEP key to the AUTH command.
To configure static WEP keys, these are given to the
CONNECT or IBSS_JOIN command directly, for a userspace
SME it is assumed it will configure it properly after
the connection has been established.
Since mac80211 used to check the default key in IBSS
mode to see whether or not the network is protected,
it needs an update in that area, as well as an update
to make use of the WEP key passed to auth() for shared
key authentication.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-07-08 12:22:54 +00:00
|
|
|
if (cfg80211_validate_key_settings(rdev, &key.p, key.idx, mac_addr)) {
|
|
|
|
err = -EINVAL;
|
2007-12-19 01:03:29 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
cfg80211: rework key operation
This reworks the key operation in cfg80211, and now only
allows, from userspace, configuring keys (via nl80211)
after the connection has been established (in managed
mode), the IBSS been joined (in IBSS mode), at any time
(in AP[_VLAN] modes) or never for all the other modes.
In order to do shared key authentication correctly, it
is now possible to give a WEP key to the AUTH command.
To configure static WEP keys, these are given to the
CONNECT or IBSS_JOIN command directly, for a userspace
SME it is assumed it will configure it properly after
the connection has been established.
Since mac80211 used to check the default key in IBSS
mode to see whether or not the network is protected,
it needs an update in that area, as well as an update
to make use of the WEP key passed to auth() for shared
key authentication.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-07-08 12:22:54 +00:00
|
|
|
wdev_lock(dev->ieee80211_ptr);
|
|
|
|
err = nl80211_key_allowed(dev->ieee80211_ptr);
|
|
|
|
if (!err)
|
|
|
|
err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx,
|
|
|
|
mac_addr, &key.p);
|
|
|
|
wdev_unlock(dev->ieee80211_ptr);
|
2007-12-19 01:03:29 +00:00
|
|
|
|
|
|
|
out:
|
2009-07-07 01:56:12 +00:00
|
|
|
cfg80211_unlock_rdev(rdev);
|
2007-12-19 01:03:29 +00:00
|
|
|
dev_put(dev);
|
2009-03-12 08:55:09 +00:00
|
|
|
unlock_rtnl:
|
|
|
|
rtnl_unlock();
|
|
|
|
|
2007-12-19 01:03:29 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2009-07-07 01:56:12 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
2007-12-19 01:03:29 +00:00
|
|
|
int err;
|
|
|
|
struct net_device *dev;
|
|
|
|
u8 *mac_addr = NULL;
|
2009-07-08 11:29:08 +00:00
|
|
|
struct key_parse key;
|
2007-12-19 01:03:29 +00:00
|
|
|
|
2009-07-08 11:29:08 +00:00
|
|
|
err = nl80211_parse_key(info, &key);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2007-12-19 01:03:29 +00:00
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_MAC])
|
|
|
|
mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
|
|
|
|
2009-03-12 08:55:09 +00:00
|
|
|
rtnl_lock();
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
2007-12-19 01:03:29 +00:00
|
|
|
if (err)
|
2009-03-12 08:55:09 +00:00
|
|
|
goto unlock_rtnl;
|
2007-12-19 01:03:29 +00:00
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
if (!rdev->ops->del_key) {
|
2007-12-19 01:03:29 +00:00
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
cfg80211: rework key operation
This reworks the key operation in cfg80211, and now only
allows, from userspace, configuring keys (via nl80211)
after the connection has been established (in managed
mode), the IBSS been joined (in IBSS mode), at any time
(in AP[_VLAN] modes) or never for all the other modes.
In order to do shared key authentication correctly, it
is now possible to give a WEP key to the AUTH command.
To configure static WEP keys, these are given to the
CONNECT or IBSS_JOIN command directly, for a userspace
SME it is assumed it will configure it properly after
the connection has been established.
Since mac80211 used to check the default key in IBSS
mode to see whether or not the network is protected,
it needs an update in that area, as well as an update
to make use of the WEP key passed to auth() for shared
key authentication.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-07-08 12:22:54 +00:00
|
|
|
wdev_lock(dev->ieee80211_ptr);
|
|
|
|
err = nl80211_key_allowed(dev->ieee80211_ptr);
|
|
|
|
if (!err)
|
|
|
|
err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx, mac_addr);
|
2007-12-19 01:03:29 +00:00
|
|
|
|
2009-09-29 21:27:28 +00:00
|
|
|
#ifdef CONFIG_CFG80211_WEXT
|
2009-05-11 11:54:58 +00:00
|
|
|
if (!err) {
|
2009-07-08 11:29:08 +00:00
|
|
|
if (key.idx == dev->ieee80211_ptr->wext.default_key)
|
2009-05-11 11:54:58 +00:00
|
|
|
dev->ieee80211_ptr->wext.default_key = -1;
|
2009-07-08 11:29:08 +00:00
|
|
|
else if (key.idx == dev->ieee80211_ptr->wext.default_mgmt_key)
|
2009-05-11 11:54:58 +00:00
|
|
|
dev->ieee80211_ptr->wext.default_mgmt_key = -1;
|
|
|
|
}
|
|
|
|
#endif
|
cfg80211: rework key operation
This reworks the key operation in cfg80211, and now only
allows, from userspace, configuring keys (via nl80211)
after the connection has been established (in managed
mode), the IBSS been joined (in IBSS mode), at any time
(in AP[_VLAN] modes) or never for all the other modes.
In order to do shared key authentication correctly, it
is now possible to give a WEP key to the AUTH command.
To configure static WEP keys, these are given to the
CONNECT or IBSS_JOIN command directly, for a userspace
SME it is assumed it will configure it properly after
the connection has been established.
Since mac80211 used to check the default key in IBSS
mode to see whether or not the network is protected,
it needs an update in that area, as well as an update
to make use of the WEP key passed to auth() for shared
key authentication.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-07-08 12:22:54 +00:00
|
|
|
wdev_unlock(dev->ieee80211_ptr);
|
2009-05-11 11:54:58 +00:00
|
|
|
|
2007-12-19 01:03:29 +00:00
|
|
|
out:
|
2009-07-07 01:56:12 +00:00
|
|
|
cfg80211_unlock_rdev(rdev);
|
2007-12-19 01:03:29 +00:00
|
|
|
dev_put(dev);
|
2009-03-12 08:55:09 +00:00
|
|
|
|
|
|
|
unlock_rtnl:
|
|
|
|
rtnl_unlock();
|
|
|
|
|
2007-12-19 01:03:29 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2007-12-19 01:03:32 +00:00
|
|
|
static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
int (*call)(struct wiphy *wiphy, struct net_device *dev,
|
|
|
|
struct beacon_parameters *info);
|
2009-07-07 01:56:12 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
2007-12-19 01:03:32 +00:00
|
|
|
int err;
|
|
|
|
struct net_device *dev;
|
|
|
|
struct beacon_parameters params;
|
|
|
|
int haveinfo = 0;
|
|
|
|
|
2009-03-27 11:40:28 +00:00
|
|
|
if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2009-03-12 08:55:09 +00:00
|
|
|
rtnl_lock();
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
2007-12-19 01:03:32 +00:00
|
|
|
if (err)
|
2009-03-12 08:55:09 +00:00
|
|
|
goto unlock_rtnl;
|
2007-12-19 01:03:32 +00:00
|
|
|
|
2009-03-20 19:21:19 +00:00
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2007-12-19 01:03:32 +00:00
|
|
|
switch (info->genlhdr->cmd) {
|
|
|
|
case NL80211_CMD_NEW_BEACON:
|
|
|
|
/* these are required for NEW_BEACON */
|
|
|
|
if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] ||
|
|
|
|
!info->attrs[NL80211_ATTR_DTIM_PERIOD] ||
|
|
|
|
!info->attrs[NL80211_ATTR_BEACON_HEAD]) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
call = rdev->ops->add_beacon;
|
2007-12-19 01:03:32 +00:00
|
|
|
break;
|
|
|
|
case NL80211_CMD_SET_BEACON:
|
2009-07-07 01:56:12 +00:00
|
|
|
call = rdev->ops->set_beacon;
|
2007-12-19 01:03:32 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
WARN_ON(1);
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!call) {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(¶ms, 0, sizeof(params));
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
|
|
|
|
params.interval =
|
|
|
|
nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
|
|
|
|
haveinfo = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_DTIM_PERIOD]) {
|
|
|
|
params.dtim_period =
|
|
|
|
nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
|
|
|
|
haveinfo = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_BEACON_HEAD]) {
|
|
|
|
params.head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]);
|
|
|
|
params.head_len =
|
|
|
|
nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]);
|
|
|
|
haveinfo = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_BEACON_TAIL]) {
|
|
|
|
params.tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]);
|
|
|
|
params.tail_len =
|
|
|
|
nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]);
|
|
|
|
haveinfo = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!haveinfo) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
err = call(&rdev->wiphy, dev, ¶ms);
|
2007-12-19 01:03:32 +00:00
|
|
|
|
|
|
|
out:
|
2009-07-07 01:56:12 +00:00
|
|
|
cfg80211_unlock_rdev(rdev);
|
2007-12-19 01:03:32 +00:00
|
|
|
dev_put(dev);
|
2009-03-12 08:55:09 +00:00
|
|
|
unlock_rtnl:
|
|
|
|
rtnl_unlock();
|
|
|
|
|
2007-12-19 01:03:32 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2009-07-07 01:56:12 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
2007-12-19 01:03:32 +00:00
|
|
|
int err;
|
|
|
|
struct net_device *dev;
|
|
|
|
|
2009-03-12 08:55:09 +00:00
|
|
|
rtnl_lock();
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
2007-12-19 01:03:32 +00:00
|
|
|
if (err)
|
2009-03-12 08:55:09 +00:00
|
|
|
goto unlock_rtnl;
|
2007-12-19 01:03:32 +00:00
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
if (!rdev->ops->del_beacon) {
|
2007-12-19 01:03:32 +00:00
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-03-20 19:21:19 +00:00
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
2009-07-07 01:56:12 +00:00
|
|
|
err = rdev->ops->del_beacon(&rdev->wiphy, dev);
|
2007-12-19 01:03:32 +00:00
|
|
|
|
|
|
|
out:
|
2009-07-07 01:56:12 +00:00
|
|
|
cfg80211_unlock_rdev(rdev);
|
2007-12-19 01:03:32 +00:00
|
|
|
dev_put(dev);
|
2009-03-12 08:55:09 +00:00
|
|
|
unlock_rtnl:
|
|
|
|
rtnl_unlock();
|
|
|
|
|
2007-12-19 01:03:32 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2007-12-19 01:03:34 +00:00
|
|
|
static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = {
|
|
|
|
[NL80211_STA_FLAG_AUTHORIZED] = { .type = NLA_FLAG },
|
|
|
|
[NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG },
|
|
|
|
[NL80211_STA_FLAG_WME] = { .type = NLA_FLAG },
|
2009-05-11 18:57:55 +00:00
|
|
|
[NL80211_STA_FLAG_MFP] = { .type = NLA_FLAG },
|
2007-12-19 01:03:34 +00:00
|
|
|
};
|
|
|
|
|
2009-05-11 18:57:56 +00:00
|
|
|
static int parse_station_flags(struct genl_info *info,
|
|
|
|
struct station_parameters *params)
|
2007-12-19 01:03:34 +00:00
|
|
|
{
|
|
|
|
struct nlattr *flags[NL80211_STA_FLAG_MAX + 1];
|
2009-05-11 18:57:56 +00:00
|
|
|
struct nlattr *nla;
|
2007-12-19 01:03:34 +00:00
|
|
|
int flag;
|
|
|
|
|
2009-05-11 18:57:56 +00:00
|
|
|
/*
|
|
|
|
* Try parsing the new attribute first so userspace
|
|
|
|
* can specify both for older kernels.
|
|
|
|
*/
|
|
|
|
nla = info->attrs[NL80211_ATTR_STA_FLAGS2];
|
|
|
|
if (nla) {
|
|
|
|
struct nl80211_sta_flag_update *sta_flags;
|
|
|
|
|
|
|
|
sta_flags = nla_data(nla);
|
|
|
|
params->sta_flags_mask = sta_flags->mask;
|
|
|
|
params->sta_flags_set = sta_flags->set;
|
|
|
|
if ((params->sta_flags_mask |
|
|
|
|
params->sta_flags_set) & BIT(__NL80211_STA_FLAG_INVALID))
|
|
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if present, parse the old attribute */
|
2007-12-19 01:03:34 +00:00
|
|
|
|
2009-05-11 18:57:56 +00:00
|
|
|
nla = info->attrs[NL80211_ATTR_STA_FLAGS];
|
2007-12-19 01:03:34 +00:00
|
|
|
if (!nla)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (nla_parse_nested(flags, NL80211_STA_FLAG_MAX,
|
|
|
|
nla, sta_flags_policy))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2009-05-11 18:57:56 +00:00
|
|
|
params->sta_flags_mask = (1 << __NL80211_STA_FLAG_AFTER_LAST) - 1;
|
|
|
|
params->sta_flags_mask &= ~1;
|
2007-12-19 01:03:34 +00:00
|
|
|
|
|
|
|
for (flag = 1; flag <= NL80211_STA_FLAG_MAX; flag++)
|
|
|
|
if (flags[flag])
|
2009-05-11 18:57:56 +00:00
|
|
|
params->sta_flags_set |= (1<<flag);
|
2007-12-19 01:03:34 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-12-19 01:03:36 +00:00
|
|
|
static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
|
|
|
|
int flags, struct net_device *dev,
|
2009-12-23 12:15:44 +00:00
|
|
|
const u8 *mac_addr, struct station_info *sinfo)
|
2007-12-19 01:03:36 +00:00
|
|
|
{
|
|
|
|
void *hdr;
|
2008-12-11 21:04:19 +00:00
|
|
|
struct nlattr *sinfoattr, *txrate;
|
|
|
|
u16 bitrate;
|
2007-12-19 01:03:36 +00:00
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
|
|
|
|
if (!hdr)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
|
|
|
|
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
|
|
|
|
|
2009-08-07 14:17:38 +00:00
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, sinfo->generation);
|
|
|
|
|
2008-02-23 14:17:06 +00:00
|
|
|
sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO);
|
|
|
|
if (!sinfoattr)
|
2007-12-19 01:03:36 +00:00
|
|
|
goto nla_put_failure;
|
2008-02-23 14:17:06 +00:00
|
|
|
if (sinfo->filled & STATION_INFO_INACTIVE_TIME)
|
|
|
|
NLA_PUT_U32(msg, NL80211_STA_INFO_INACTIVE_TIME,
|
|
|
|
sinfo->inactive_time);
|
|
|
|
if (sinfo->filled & STATION_INFO_RX_BYTES)
|
|
|
|
NLA_PUT_U32(msg, NL80211_STA_INFO_RX_BYTES,
|
|
|
|
sinfo->rx_bytes);
|
|
|
|
if (sinfo->filled & STATION_INFO_TX_BYTES)
|
|
|
|
NLA_PUT_U32(msg, NL80211_STA_INFO_TX_BYTES,
|
|
|
|
sinfo->tx_bytes);
|
|
|
|
if (sinfo->filled & STATION_INFO_LLID)
|
|
|
|
NLA_PUT_U16(msg, NL80211_STA_INFO_LLID,
|
|
|
|
sinfo->llid);
|
|
|
|
if (sinfo->filled & STATION_INFO_PLID)
|
|
|
|
NLA_PUT_U16(msg, NL80211_STA_INFO_PLID,
|
|
|
|
sinfo->plid);
|
|
|
|
if (sinfo->filled & STATION_INFO_PLINK_STATE)
|
|
|
|
NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE,
|
|
|
|
sinfo->plink_state);
|
2008-12-11 21:04:19 +00:00
|
|
|
if (sinfo->filled & STATION_INFO_SIGNAL)
|
|
|
|
NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL,
|
|
|
|
sinfo->signal);
|
|
|
|
if (sinfo->filled & STATION_INFO_TX_BITRATE) {
|
|
|
|
txrate = nla_nest_start(msg, NL80211_STA_INFO_TX_BITRATE);
|
|
|
|
if (!txrate)
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
2009-12-09 21:43:52 +00:00
|
|
|
/* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */
|
|
|
|
bitrate = cfg80211_calculate_bitrate(&sinfo->txrate);
|
2008-12-11 21:04:19 +00:00
|
|
|
if (bitrate > 0)
|
|
|
|
NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate);
|
2008-02-23 14:17:06 +00:00
|
|
|
|
2008-12-11 21:04:19 +00:00
|
|
|
if (sinfo->txrate.flags & RATE_INFO_FLAGS_MCS)
|
|
|
|
NLA_PUT_U8(msg, NL80211_RATE_INFO_MCS,
|
|
|
|
sinfo->txrate.mcs);
|
|
|
|
if (sinfo->txrate.flags & RATE_INFO_FLAGS_40_MHZ_WIDTH)
|
|
|
|
NLA_PUT_FLAG(msg, NL80211_RATE_INFO_40_MHZ_WIDTH);
|
|
|
|
if (sinfo->txrate.flags & RATE_INFO_FLAGS_SHORT_GI)
|
|
|
|
NLA_PUT_FLAG(msg, NL80211_RATE_INFO_SHORT_GI);
|
|
|
|
|
|
|
|
nla_nest_end(msg, txrate);
|
|
|
|
}
|
2009-02-17 11:24:57 +00:00
|
|
|
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);
|
2008-02-23 14:17:06 +00:00
|
|
|
nla_nest_end(msg, sinfoattr);
|
2007-12-19 01:03:36 +00:00
|
|
|
|
|
|
|
return genlmsg_end(msg, hdr);
|
|
|
|
|
|
|
|
nla_put_failure:
|
2008-06-03 23:36:54 +00:00
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
return -EMSGSIZE;
|
2007-12-19 01:03:36 +00:00
|
|
|
}
|
|
|
|
|
2008-02-23 14:17:06 +00:00
|
|
|
static int nl80211_dump_station(struct sk_buff *skb,
|
2008-07-29 11:22:51 +00:00
|
|
|
struct netlink_callback *cb)
|
2008-02-23 14:17:06 +00:00
|
|
|
{
|
|
|
|
struct station_info sinfo;
|
|
|
|
struct cfg80211_registered_device *dev;
|
2008-07-29 11:22:51 +00:00
|
|
|
struct net_device *netdev;
|
2008-02-23 14:17:06 +00:00
|
|
|
u8 mac_addr[ETH_ALEN];
|
2008-07-29 11:22:51 +00:00
|
|
|
int ifidx = cb->args[0];
|
|
|
|
int sta_idx = cb->args[1];
|
2008-02-23 14:17:06 +00:00
|
|
|
int err;
|
|
|
|
|
2009-11-11 10:30:02 +00:00
|
|
|
if (!ifidx)
|
|
|
|
ifidx = nl80211_get_ifidx(cb);
|
|
|
|
if (ifidx < 0)
|
|
|
|
return ifidx;
|
2008-02-23 14:17:06 +00:00
|
|
|
|
2009-03-12 08:55:09 +00:00
|
|
|
rtnl_lock();
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
netdev = __dev_get_by_index(sock_net(skb->sk), ifidx);
|
2009-03-12 08:55:09 +00:00
|
|
|
if (!netdev) {
|
|
|
|
err = -ENODEV;
|
|
|
|
goto out_rtnl;
|
|
|
|
}
|
2008-02-23 14:17:06 +00:00
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
dev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
|
2008-07-29 11:22:51 +00:00
|
|
|
if (IS_ERR(dev)) {
|
|
|
|
err = PTR_ERR(dev);
|
2009-03-12 08:55:09 +00:00
|
|
|
goto out_rtnl;
|
2008-07-29 11:22:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!dev->ops->dump_station) {
|
2009-03-20 19:21:19 +00:00
|
|
|
err = -EOPNOTSUPP;
|
2008-07-29 11:22:51 +00:00
|
|
|
goto out_err;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx,
|
|
|
|
mac_addr, &sinfo);
|
|
|
|
if (err == -ENOENT)
|
|
|
|
break;
|
|
|
|
if (err)
|
2009-03-12 08:55:09 +00:00
|
|
|
goto out_err;
|
2008-07-29 11:22:51 +00:00
|
|
|
|
|
|
|
if (nl80211_send_station(skb,
|
|
|
|
NETLINK_CB(cb->skb).pid,
|
|
|
|
cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
|
|
|
netdev, mac_addr,
|
|
|
|
&sinfo) < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
sta_idx++;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
cb->args[1] = sta_idx;
|
|
|
|
err = skb->len;
|
|
|
|
out_err:
|
2009-07-07 01:56:09 +00:00
|
|
|
cfg80211_unlock_rdev(dev);
|
2009-03-12 08:55:09 +00:00
|
|
|
out_rtnl:
|
|
|
|
rtnl_unlock();
|
2008-07-29 11:22:51 +00:00
|
|
|
|
|
|
|
return err;
|
2008-02-23 14:17:06 +00:00
|
|
|
}
|
2007-12-19 01:03:36 +00:00
|
|
|
|
2007-12-19 01:03:34 +00:00
|
|
|
static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2009-07-07 01:56:12 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
2007-12-19 01:03:36 +00:00
|
|
|
int err;
|
|
|
|
struct net_device *dev;
|
2008-02-23 14:17:06 +00:00
|
|
|
struct station_info sinfo;
|
2007-12-19 01:03:36 +00:00
|
|
|
struct sk_buff *msg;
|
|
|
|
u8 *mac_addr = NULL;
|
|
|
|
|
2008-02-23 14:17:06 +00:00
|
|
|
memset(&sinfo, 0, sizeof(sinfo));
|
2007-12-19 01:03:36 +00:00
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_MAC])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
|
|
|
|
2009-03-12 08:55:09 +00:00
|
|
|
rtnl_lock();
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
2007-12-19 01:03:36 +00:00
|
|
|
if (err)
|
2009-03-12 08:55:09 +00:00
|
|
|
goto out_rtnl;
|
2007-12-19 01:03:36 +00:00
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
if (!rdev->ops->get_station) {
|
2007-12-19 01:03:36 +00:00
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
err = rdev->ops->get_station(&rdev->wiphy, dev, mac_addr, &sinfo);
|
2008-02-23 14:17:06 +00:00
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
2009-05-19 22:27:55 +00:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
2007-12-19 01:03:36 +00:00
|
|
|
if (!msg)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (nl80211_send_station(msg, info->snd_pid, info->snd_seq, 0,
|
2008-02-23 14:17:06 +00:00
|
|
|
dev, mac_addr, &sinfo) < 0)
|
2007-12-19 01:03:36 +00:00
|
|
|
goto out_free;
|
|
|
|
|
2009-07-10 09:51:34 +00:00
|
|
|
err = genlmsg_reply(msg, info);
|
2007-12-19 01:03:36 +00:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
out_free:
|
|
|
|
nlmsg_free(msg);
|
|
|
|
out:
|
2009-07-07 01:56:12 +00:00
|
|
|
cfg80211_unlock_rdev(rdev);
|
2007-12-19 01:03:36 +00:00
|
|
|
dev_put(dev);
|
2009-03-12 08:55:09 +00:00
|
|
|
out_rtnl:
|
|
|
|
rtnl_unlock();
|
|
|
|
|
2007-12-19 01:03:36 +00:00
|
|
|
return err;
|
2007-12-19 01:03:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2009-11-11 16:23:31 +00:00
|
|
|
* Get vlan interface making sure it is running and on the right wiphy.
|
2007-12-19 01:03:34 +00:00
|
|
|
*/
|
2009-07-13 22:33:35 +00:00
|
|
|
static int get_vlan(struct genl_info *info,
|
2007-12-19 01:03:34 +00:00
|
|
|
struct cfg80211_registered_device *rdev,
|
|
|
|
struct net_device **vlan)
|
|
|
|
{
|
2009-07-13 22:33:35 +00:00
|
|
|
struct nlattr *vlanattr = info->attrs[NL80211_ATTR_STA_VLAN];
|
2007-12-19 01:03:34 +00:00
|
|
|
*vlan = NULL;
|
|
|
|
|
|
|
|
if (vlanattr) {
|
2009-07-13 22:33:35 +00:00
|
|
|
*vlan = dev_get_by_index(genl_info_net(info),
|
|
|
|
nla_get_u32(vlanattr));
|
2007-12-19 01:03:34 +00:00
|
|
|
if (!*vlan)
|
|
|
|
return -ENODEV;
|
|
|
|
if (!(*vlan)->ieee80211_ptr)
|
|
|
|
return -EINVAL;
|
|
|
|
if ((*vlan)->ieee80211_ptr->wiphy != &rdev->wiphy)
|
|
|
|
return -EINVAL;
|
2009-11-11 16:23:31 +00:00
|
|
|
if (!netif_running(*vlan))
|
|
|
|
return -ENETDOWN;
|
2007-12-19 01:03:34 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2009-07-07 01:56:12 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
2007-12-19 01:03:34 +00:00
|
|
|
int err;
|
|
|
|
struct net_device *dev;
|
|
|
|
struct station_parameters params;
|
|
|
|
u8 *mac_addr = NULL;
|
|
|
|
|
|
|
|
memset(¶ms, 0, sizeof(params));
|
|
|
|
|
|
|
|
params.listen_interval = -1;
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_STA_AID])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_MAC])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) {
|
|
|
|
params.supported_rates =
|
|
|
|
nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
|
|
|
|
params.supported_rates_len =
|
|
|
|
nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
|
|
|
|
params.listen_interval =
|
|
|
|
nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
|
|
|
|
|
2008-08-25 08:58:58 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
|
|
|
|
params.ht_capa =
|
|
|
|
nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
|
|
|
|
|
2009-05-11 18:57:56 +00:00
|
|
|
if (parse_station_flags(info, ¶ms))
|
2007-12-19 01:03:34 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2008-02-23 14:17:06 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
|
|
|
|
params.plink_action =
|
|
|
|
nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
|
|
|
|
|
2009-03-12 08:55:09 +00:00
|
|
|
rtnl_lock();
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
2007-12-19 01:03:34 +00:00
|
|
|
if (err)
|
2009-03-12 08:55:09 +00:00
|
|
|
goto out_rtnl;
|
2007-12-19 01:03:34 +00:00
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
err = get_vlan(info, rdev, ¶ms.vlan);
|
2009-06-18 15:23:43 +00:00
|
|
|
if (err)
|
2009-05-27 08:35:29 +00:00
|
|
|
goto out;
|
2009-06-18 15:23:43 +00:00
|
|
|
|
|
|
|
/* validate settings */
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
switch (dev->ieee80211_ptr->iftype) {
|
|
|
|
case NL80211_IFTYPE_AP:
|
|
|
|
case NL80211_IFTYPE_AP_VLAN:
|
|
|
|
/* disallow mesh-specific things */
|
|
|
|
if (params.plink_action)
|
|
|
|
err = -EINVAL;
|
|
|
|
break;
|
|
|
|
case NL80211_IFTYPE_STATION:
|
|
|
|
/* disallow everything but AUTHORIZED flag */
|
|
|
|
if (params.plink_action)
|
|
|
|
err = -EINVAL;
|
|
|
|
if (params.vlan)
|
|
|
|
err = -EINVAL;
|
|
|
|
if (params.supported_rates)
|
|
|
|
err = -EINVAL;
|
|
|
|
if (params.ht_capa)
|
|
|
|
err = -EINVAL;
|
|
|
|
if (params.listen_interval >= 0)
|
|
|
|
err = -EINVAL;
|
|
|
|
if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED))
|
|
|
|
err = -EINVAL;
|
|
|
|
break;
|
|
|
|
case NL80211_IFTYPE_MESH_POINT:
|
|
|
|
/* disallow things mesh doesn't support */
|
|
|
|
if (params.vlan)
|
|
|
|
err = -EINVAL;
|
|
|
|
if (params.ht_capa)
|
|
|
|
err = -EINVAL;
|
|
|
|
if (params.listen_interval >= 0)
|
|
|
|
err = -EINVAL;
|
|
|
|
if (params.supported_rates)
|
|
|
|
err = -EINVAL;
|
|
|
|
if (params.sta_flags_mask)
|
|
|
|
err = -EINVAL;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
err = -EINVAL;
|
2009-05-27 08:35:29 +00:00
|
|
|
}
|
|
|
|
|
2007-12-19 01:03:34 +00:00
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
if (!rdev->ops->change_station) {
|
2007-12-19 01:03:34 +00:00
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
err = rdev->ops->change_station(&rdev->wiphy, dev, mac_addr, ¶ms);
|
2007-12-19 01:03:34 +00:00
|
|
|
|
|
|
|
out:
|
|
|
|
if (params.vlan)
|
|
|
|
dev_put(params.vlan);
|
2009-07-07 01:56:12 +00:00
|
|
|
cfg80211_unlock_rdev(rdev);
|
2007-12-19 01:03:34 +00:00
|
|
|
dev_put(dev);
|
2009-03-12 08:55:09 +00:00
|
|
|
out_rtnl:
|
|
|
|
rtnl_unlock();
|
|
|
|
|
2007-12-19 01:03:34 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2009-07-07 01:56:12 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
2007-12-19 01:03:34 +00:00
|
|
|
int err;
|
|
|
|
struct net_device *dev;
|
|
|
|
struct station_parameters params;
|
|
|
|
u8 *mac_addr = NULL;
|
|
|
|
|
|
|
|
memset(¶ms, 0, sizeof(params));
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_MAC])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES])
|
|
|
|
return -EINVAL;
|
|
|
|
|
2010-02-12 14:34:50 +00:00
|
|
|
if (!info->attrs[NL80211_ATTR_STA_AID])
|
|
|
|
return -EINVAL;
|
|
|
|
|
2007-12-19 01:03:34 +00:00
|
|
|
mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
|
|
|
params.supported_rates =
|
|
|
|
nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
|
|
|
|
params.supported_rates_len =
|
|
|
|
nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
|
|
|
|
params.listen_interval =
|
|
|
|
nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
|
2009-05-24 14:42:30 +00:00
|
|
|
|
2010-02-12 14:34:50 +00:00
|
|
|
params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
|
|
|
|
if (!params.aid || params.aid > IEEE80211_MAX_AID)
|
|
|
|
return -EINVAL;
|
2009-05-24 14:42:30 +00:00
|
|
|
|
2008-08-25 08:58:58 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
|
|
|
|
params.ht_capa =
|
|
|
|
nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
|
2007-12-19 01:03:34 +00:00
|
|
|
|
2009-05-11 18:57:56 +00:00
|
|
|
if (parse_station_flags(info, ¶ms))
|
2007-12-19 01:03:34 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2009-03-12 08:55:09 +00:00
|
|
|
rtnl_lock();
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
2007-12-19 01:03:34 +00:00
|
|
|
if (err)
|
2009-03-12 08:55:09 +00:00
|
|
|
goto out_rtnl;
|
2007-12-19 01:03:34 +00:00
|
|
|
|
2010-02-12 14:34:50 +00:00
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
|
|
|
|
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
err = get_vlan(info, rdev, ¶ms.vlan);
|
2009-06-18 15:23:43 +00:00
|
|
|
if (err)
|
2009-05-11 12:43:13 +00:00
|
|
|
goto out;
|
2009-06-18 15:23:43 +00:00
|
|
|
|
|
|
|
/* validate settings */
|
|
|
|
err = 0;
|
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
if (!rdev->ops->add_station) {
|
2007-12-19 01:03:34 +00:00
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-03-20 19:21:18 +00:00
|
|
|
if (!netif_running(dev)) {
|
|
|
|
err = -ENETDOWN;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
err = rdev->ops->add_station(&rdev->wiphy, dev, mac_addr, ¶ms);
|
2007-12-19 01:03:34 +00:00
|
|
|
|
|
|
|
out:
|
|
|
|
if (params.vlan)
|
|
|
|
dev_put(params.vlan);
|
2009-07-07 01:56:12 +00:00
|
|
|
cfg80211_unlock_rdev(rdev);
|
2007-12-19 01:03:34 +00:00
|
|
|
dev_put(dev);
|
2009-03-12 08:55:09 +00:00
|
|
|
out_rtnl:
|
|
|
|
rtnl_unlock();
|
|
|
|
|
2007-12-19 01:03:34 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2009-07-07 01:56:12 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
2007-12-19 01:03:34 +00:00
|
|
|
int err;
|
|
|
|
struct net_device *dev;
|
|
|
|
u8 *mac_addr = NULL;
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_MAC])
|
|
|
|
mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
|
|
|
|
2009-03-12 08:55:09 +00:00
|
|
|
rtnl_lock();
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
2007-12-19 01:03:34 +00:00
|
|
|
if (err)
|
2009-03-12 08:55:09 +00:00
|
|
|
goto out_rtnl;
|
2007-12-19 01:03:34 +00:00
|
|
|
|
2009-05-11 12:43:13 +00:00
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
|
2010-03-30 08:00:16 +00:00
|
|
|
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
|
|
|
|
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
|
2009-05-11 12:43:13 +00:00
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
if (!rdev->ops->del_station) {
|
2007-12-19 01:03:34 +00:00
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
err = rdev->ops->del_station(&rdev->wiphy, dev, mac_addr);
|
2007-12-19 01:03:34 +00:00
|
|
|
|
|
|
|
out:
|
2009-07-07 01:56:12 +00:00
|
|
|
cfg80211_unlock_rdev(rdev);
|
2007-12-19 01:03:34 +00:00
|
|
|
dev_put(dev);
|
2009-03-12 08:55:09 +00:00
|
|
|
out_rtnl:
|
|
|
|
rtnl_unlock();
|
|
|
|
|
2007-12-19 01:03:34 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2008-02-23 14:17:06 +00:00
|
|
|
static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq,
|
|
|
|
int flags, struct net_device *dev,
|
|
|
|
u8 *dst, u8 *next_hop,
|
|
|
|
struct mpath_info *pinfo)
|
|
|
|
{
|
|
|
|
void *hdr;
|
|
|
|
struct nlattr *pinfoattr;
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
|
|
|
|
if (!hdr)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
|
|
|
|
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst);
|
|
|
|
NLA_PUT(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop);
|
|
|
|
|
2009-08-07 14:17:38 +00:00
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, pinfo->generation);
|
|
|
|
|
2008-02-23 14:17:06 +00:00
|
|
|
pinfoattr = nla_nest_start(msg, NL80211_ATTR_MPATH_INFO);
|
|
|
|
if (!pinfoattr)
|
|
|
|
goto nla_put_failure;
|
|
|
|
if (pinfo->filled & MPATH_INFO_FRAME_QLEN)
|
|
|
|
NLA_PUT_U32(msg, NL80211_MPATH_INFO_FRAME_QLEN,
|
|
|
|
pinfo->frame_qlen);
|
2009-11-09 23:46:55 +00:00
|
|
|
if (pinfo->filled & MPATH_INFO_SN)
|
|
|
|
NLA_PUT_U32(msg, NL80211_MPATH_INFO_SN,
|
|
|
|
pinfo->sn);
|
2008-02-23 14:17:06 +00:00
|
|
|
if (pinfo->filled & MPATH_INFO_METRIC)
|
|
|
|
NLA_PUT_U32(msg, NL80211_MPATH_INFO_METRIC,
|
|
|
|
pinfo->metric);
|
|
|
|
if (pinfo->filled & MPATH_INFO_EXPTIME)
|
|
|
|
NLA_PUT_U32(msg, NL80211_MPATH_INFO_EXPTIME,
|
|
|
|
pinfo->exptime);
|
|
|
|
if (pinfo->filled & MPATH_INFO_FLAGS)
|
|
|
|
NLA_PUT_U8(msg, NL80211_MPATH_INFO_FLAGS,
|
|
|
|
pinfo->flags);
|
|
|
|
if (pinfo->filled & MPATH_INFO_DISCOVERY_TIMEOUT)
|
|
|
|
NLA_PUT_U32(msg, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
|
|
|
|
pinfo->discovery_timeout);
|
|
|
|
if (pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES)
|
|
|
|
NLA_PUT_U8(msg, NL80211_MPATH_INFO_DISCOVERY_RETRIES,
|
|
|
|
pinfo->discovery_retries);
|
|
|
|
|
|
|
|
nla_nest_end(msg, pinfoattr);
|
|
|
|
|
|
|
|
return genlmsg_end(msg, hdr);
|
|
|
|
|
|
|
|
nla_put_failure:
|
2008-06-03 23:36:54 +00:00
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
return -EMSGSIZE;
|
2008-02-23 14:17:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_dump_mpath(struct sk_buff *skb,
|
2008-07-29 11:22:51 +00:00
|
|
|
struct netlink_callback *cb)
|
2008-02-23 14:17:06 +00:00
|
|
|
{
|
|
|
|
struct mpath_info pinfo;
|
|
|
|
struct cfg80211_registered_device *dev;
|
2008-07-29 11:22:51 +00:00
|
|
|
struct net_device *netdev;
|
2008-02-23 14:17:06 +00:00
|
|
|
u8 dst[ETH_ALEN];
|
|
|
|
u8 next_hop[ETH_ALEN];
|
2008-07-29 11:22:51 +00:00
|
|
|
int ifidx = cb->args[0];
|
|
|
|
int path_idx = cb->args[1];
|
2008-02-23 14:17:06 +00:00
|
|
|
int err;
|
|
|
|
|
2009-11-11 10:30:02 +00:00
|
|
|
if (!ifidx)
|
|
|
|
ifidx = nl80211_get_ifidx(cb);
|
|
|
|
if (ifidx < 0)
|
|
|
|
return ifidx;
|
2008-07-29 11:22:51 +00:00
|
|
|
|
2009-03-12 08:55:09 +00:00
|
|
|
rtnl_lock();
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
netdev = __dev_get_by_index(sock_net(skb->sk), ifidx);
|
2009-03-12 08:55:09 +00:00
|
|
|
if (!netdev) {
|
|
|
|
err = -ENODEV;
|
|
|
|
goto out_rtnl;
|
|
|
|
}
|
2008-07-29 11:22:51 +00:00
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
dev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
|
2008-07-29 11:22:51 +00:00
|
|
|
if (IS_ERR(dev)) {
|
|
|
|
err = PTR_ERR(dev);
|
2009-03-12 08:55:09 +00:00
|
|
|
goto out_rtnl;
|
2008-07-29 11:22:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!dev->ops->dump_mpath) {
|
2009-03-20 19:21:19 +00:00
|
|
|
err = -EOPNOTSUPP;
|
2008-07-29 11:22:51 +00:00
|
|
|
goto out_err;
|
|
|
|
}
|
|
|
|
|
2009-03-20 19:21:19 +00:00
|
|
|
if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
|
|
|
|
err = -EOPNOTSUPP;
|
2009-08-22 19:15:49 +00:00
|
|
|
goto out_err;
|
2009-03-20 19:21:19 +00:00
|
|
|
}
|
|
|
|
|
2008-07-29 11:22:51 +00:00
|
|
|
while (1) {
|
|
|
|
err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx,
|
|
|
|
dst, next_hop, &pinfo);
|
|
|
|
if (err == -ENOENT)
|
2008-02-23 14:17:06 +00:00
|
|
|
break;
|
2008-07-29 11:22:51 +00:00
|
|
|
if (err)
|
2009-03-12 08:55:09 +00:00
|
|
|
goto out_err;
|
2008-02-23 14:17:06 +00:00
|
|
|
|
2008-07-29 11:22:51 +00:00
|
|
|
if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).pid,
|
|
|
|
cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
|
|
|
netdev, dst, next_hop,
|
|
|
|
&pinfo) < 0)
|
|
|
|
goto out;
|
2008-02-23 14:17:06 +00:00
|
|
|
|
2008-07-29 11:22:51 +00:00
|
|
|
path_idx++;
|
2008-02-23 14:17:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-29 11:22:51 +00:00
|
|
|
out:
|
|
|
|
cb->args[1] = path_idx;
|
|
|
|
err = skb->len;
|
|
|
|
out_err:
|
2009-07-07 01:56:09 +00:00
|
|
|
cfg80211_unlock_rdev(dev);
|
2009-03-12 08:55:09 +00:00
|
|
|
out_rtnl:
|
|
|
|
rtnl_unlock();
|
2008-07-29 11:22:51 +00:00
|
|
|
|
|
|
|
return err;
|
2008-02-23 14:17:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2009-07-07 01:56:12 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
2008-02-23 14:17:06 +00:00
|
|
|
int err;
|
|
|
|
struct net_device *dev;
|
|
|
|
struct mpath_info pinfo;
|
|
|
|
struct sk_buff *msg;
|
|
|
|
u8 *dst = NULL;
|
|
|
|
u8 next_hop[ETH_ALEN];
|
|
|
|
|
|
|
|
memset(&pinfo, 0, sizeof(pinfo));
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_MAC])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
|
|
|
|
2009-03-12 08:55:09 +00:00
|
|
|
rtnl_lock();
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
2008-02-23 14:17:06 +00:00
|
|
|
if (err)
|
2009-03-12 08:55:09 +00:00
|
|
|
goto out_rtnl;
|
2008-02-23 14:17:06 +00:00
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
if (!rdev->ops->get_mpath) {
|
2008-02-23 14:17:06 +00:00
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-03-20 19:21:19 +00:00
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
err = rdev->ops->get_mpath(&rdev->wiphy, dev, dst, next_hop, &pinfo);
|
2008-02-23 14:17:06 +00:00
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
2009-05-19 22:27:55 +00:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
2008-02-23 14:17:06 +00:00
|
|
|
if (!msg)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (nl80211_send_mpath(msg, info->snd_pid, info->snd_seq, 0,
|
|
|
|
dev, dst, next_hop, &pinfo) < 0)
|
|
|
|
goto out_free;
|
|
|
|
|
2009-07-10 09:51:34 +00:00
|
|
|
err = genlmsg_reply(msg, info);
|
2008-02-23 14:17:06 +00:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
out_free:
|
|
|
|
nlmsg_free(msg);
|
|
|
|
out:
|
2009-07-07 01:56:12 +00:00
|
|
|
cfg80211_unlock_rdev(rdev);
|
2008-02-23 14:17:06 +00:00
|
|
|
dev_put(dev);
|
2009-03-12 08:55:09 +00:00
|
|
|
out_rtnl:
|
|
|
|
rtnl_unlock();
|
|
|
|
|
2008-02-23 14:17:06 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2009-07-07 01:56:12 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
2008-02-23 14:17:06 +00:00
|
|
|
int err;
|
|
|
|
struct net_device *dev;
|
|
|
|
u8 *dst = NULL;
|
|
|
|
u8 *next_hop = NULL;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_MAC])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
|
|
|
next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
|
|
|
|
|
2009-03-12 08:55:09 +00:00
|
|
|
rtnl_lock();
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
2008-02-23 14:17:06 +00:00
|
|
|
if (err)
|
2009-03-12 08:55:09 +00:00
|
|
|
goto out_rtnl;
|
2008-02-23 14:17:06 +00:00
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
if (!rdev->ops->change_mpath) {
|
2008-02-23 14:17:06 +00:00
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-03-20 19:21:19 +00:00
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-03-20 19:21:18 +00:00
|
|
|
if (!netif_running(dev)) {
|
|
|
|
err = -ENETDOWN;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
err = rdev->ops->change_mpath(&rdev->wiphy, dev, dst, next_hop);
|
2008-02-23 14:17:06 +00:00
|
|
|
|
|
|
|
out:
|
2009-07-07 01:56:12 +00:00
|
|
|
cfg80211_unlock_rdev(rdev);
|
2008-02-23 14:17:06 +00:00
|
|
|
dev_put(dev);
|
2009-03-12 08:55:09 +00:00
|
|
|
out_rtnl:
|
|
|
|
rtnl_unlock();
|
|
|
|
|
2008-02-23 14:17:06 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2009-07-07 01:56:12 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
2008-02-23 14:17:06 +00:00
|
|
|
int err;
|
|
|
|
struct net_device *dev;
|
|
|
|
u8 *dst = NULL;
|
|
|
|
u8 *next_hop = NULL;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_MAC])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
|
|
|
next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
|
|
|
|
|
2009-03-12 08:55:09 +00:00
|
|
|
rtnl_lock();
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
2008-02-23 14:17:06 +00:00
|
|
|
if (err)
|
2009-03-12 08:55:09 +00:00
|
|
|
goto out_rtnl;
|
2008-02-23 14:17:06 +00:00
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
if (!rdev->ops->add_mpath) {
|
2008-02-23 14:17:06 +00:00
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-03-20 19:21:19 +00:00
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-03-20 19:21:18 +00:00
|
|
|
if (!netif_running(dev)) {
|
|
|
|
err = -ENETDOWN;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
err = rdev->ops->add_mpath(&rdev->wiphy, dev, dst, next_hop);
|
2008-02-23 14:17:06 +00:00
|
|
|
|
|
|
|
out:
|
2009-07-07 01:56:12 +00:00
|
|
|
cfg80211_unlock_rdev(rdev);
|
2008-02-23 14:17:06 +00:00
|
|
|
dev_put(dev);
|
2009-03-12 08:55:09 +00:00
|
|
|
out_rtnl:
|
|
|
|
rtnl_unlock();
|
|
|
|
|
2008-02-23 14:17:06 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2009-07-07 01:56:12 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
2008-02-23 14:17:06 +00:00
|
|
|
int err;
|
|
|
|
struct net_device *dev;
|
|
|
|
u8 *dst = NULL;
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_MAC])
|
|
|
|
dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
|
|
|
|
2009-03-12 08:55:09 +00:00
|
|
|
rtnl_lock();
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
2008-02-23 14:17:06 +00:00
|
|
|
if (err)
|
2009-03-12 08:55:09 +00:00
|
|
|
goto out_rtnl;
|
2008-02-23 14:17:06 +00:00
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
if (!rdev->ops->del_mpath) {
|
2008-02-23 14:17:06 +00:00
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
err = rdev->ops->del_mpath(&rdev->wiphy, dev, dst);
|
2008-02-23 14:17:06 +00:00
|
|
|
|
|
|
|
out:
|
2009-07-07 01:56:12 +00:00
|
|
|
cfg80211_unlock_rdev(rdev);
|
2008-02-23 14:17:06 +00:00
|
|
|
dev_put(dev);
|
2009-03-12 08:55:09 +00:00
|
|
|
out_rtnl:
|
|
|
|
rtnl_unlock();
|
|
|
|
|
2008-02-23 14:17:06 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2008-08-07 17:07:01 +00:00
|
|
|
static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2009-07-07 01:56:12 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
2008-08-07 17:07:01 +00:00
|
|
|
int err;
|
|
|
|
struct net_device *dev;
|
|
|
|
struct bss_parameters params;
|
|
|
|
|
|
|
|
memset(¶ms, 0, sizeof(params));
|
|
|
|
/* default to not changing parameters */
|
|
|
|
params.use_cts_prot = -1;
|
|
|
|
params.use_short_preamble = -1;
|
|
|
|
params.use_short_slot_time = -1;
|
2010-04-26 23:23:35 +00:00
|
|
|
params.ap_isolate = -1;
|
2008-08-07 17:07:01 +00:00
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_BSS_CTS_PROT])
|
|
|
|
params.use_cts_prot =
|
|
|
|
nla_get_u8(info->attrs[NL80211_ATTR_BSS_CTS_PROT]);
|
|
|
|
if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE])
|
|
|
|
params.use_short_preamble =
|
|
|
|
nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]);
|
|
|
|
if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME])
|
|
|
|
params.use_short_slot_time =
|
|
|
|
nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]);
|
2008-10-30 14:59:22 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
|
|
|
|
params.basic_rates =
|
|
|
|
nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
|
|
|
|
params.basic_rates_len =
|
|
|
|
nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
|
|
|
|
}
|
2010-04-26 23:23:35 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_AP_ISOLATE])
|
|
|
|
params.ap_isolate = !!nla_get_u8(info->attrs[NL80211_ATTR_AP_ISOLATE]);
|
2008-08-07 17:07:01 +00:00
|
|
|
|
2009-03-12 08:55:09 +00:00
|
|
|
rtnl_lock();
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
2008-08-07 17:07:01 +00:00
|
|
|
if (err)
|
2009-03-12 08:55:09 +00:00
|
|
|
goto out_rtnl;
|
2008-08-07 17:07:01 +00:00
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
if (!rdev->ops->change_bss) {
|
2008-08-07 17:07:01 +00:00
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-03-20 19:21:19 +00:00
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
err = rdev->ops->change_bss(&rdev->wiphy, dev, ¶ms);
|
2008-08-07 17:07:01 +00:00
|
|
|
|
|
|
|
out:
|
2009-07-07 01:56:12 +00:00
|
|
|
cfg80211_unlock_rdev(rdev);
|
2008-08-07 17:07:01 +00:00
|
|
|
dev_put(dev);
|
2009-03-12 08:55:09 +00:00
|
|
|
out_rtnl:
|
|
|
|
rtnl_unlock();
|
|
|
|
|
2008-08-07 17:07:01 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2010-02-18 08:14:31 +00:00
|
|
|
static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = {
|
2008-09-10 06:19:48 +00:00
|
|
|
[NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 },
|
|
|
|
[NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 },
|
|
|
|
[NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 },
|
|
|
|
[NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
|
|
|
|
[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
|
|
|
|
[NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
|
|
|
|
};
|
|
|
|
|
|
|
|
static int parse_reg_rule(struct nlattr *tb[],
|
|
|
|
struct ieee80211_reg_rule *reg_rule)
|
|
|
|
{
|
|
|
|
struct ieee80211_freq_range *freq_range = ®_rule->freq_range;
|
|
|
|
struct ieee80211_power_rule *power_rule = ®_rule->power_rule;
|
|
|
|
|
|
|
|
if (!tb[NL80211_ATTR_REG_RULE_FLAGS])
|
|
|
|
return -EINVAL;
|
|
|
|
if (!tb[NL80211_ATTR_FREQ_RANGE_START])
|
|
|
|
return -EINVAL;
|
|
|
|
if (!tb[NL80211_ATTR_FREQ_RANGE_END])
|
|
|
|
return -EINVAL;
|
|
|
|
if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
|
|
|
|
return -EINVAL;
|
|
|
|
if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
reg_rule->flags = nla_get_u32(tb[NL80211_ATTR_REG_RULE_FLAGS]);
|
|
|
|
|
|
|
|
freq_range->start_freq_khz =
|
|
|
|
nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]);
|
|
|
|
freq_range->end_freq_khz =
|
|
|
|
nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]);
|
|
|
|
freq_range->max_bandwidth_khz =
|
|
|
|
nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
|
|
|
|
|
|
|
|
power_rule->max_eirp =
|
|
|
|
nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]);
|
|
|
|
|
|
|
|
if (tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN])
|
|
|
|
power_rule->max_antenna_gain =
|
|
|
|
nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
int r;
|
|
|
|
char *data = NULL;
|
|
|
|
|
2009-02-21 05:04:22 +00:00
|
|
|
/*
|
|
|
|
* 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)) {
|
2009-02-21 05:04:30 +00:00
|
|
|
mutex_unlock(&cfg80211_mutex);
|
|
|
|
return -EINPROGRESS;
|
2009-02-21 05:04:22 +00:00
|
|
|
}
|
2009-02-21 05:04:30 +00:00
|
|
|
mutex_unlock(&cfg80211_mutex);
|
2009-02-21 05:04:22 +00:00
|
|
|
|
2009-02-21 05:04:30 +00:00
|
|
|
if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
|
|
|
|
return -EINVAL;
|
2008-09-10 06:19:48 +00:00
|
|
|
|
|
|
|
data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
|
|
|
|
|
2009-02-21 05:04:30 +00:00
|
|
|
r = regulatory_hint_user(data);
|
|
|
|
|
2008-09-10 06:19:48 +00:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2008-10-21 19:03:48 +00:00
|
|
|
static int nl80211_get_mesh_params(struct sk_buff *skb,
|
|
|
|
struct genl_info *info)
|
|
|
|
{
|
2009-07-07 01:56:12 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
2008-10-21 19:03:48 +00:00
|
|
|
struct mesh_config cur_params;
|
|
|
|
int err;
|
|
|
|
struct net_device *dev;
|
|
|
|
void *hdr;
|
|
|
|
struct nlattr *pinfoattr;
|
|
|
|
struct sk_buff *msg;
|
|
|
|
|
2009-03-12 08:55:09 +00:00
|
|
|
rtnl_lock();
|
|
|
|
|
2008-10-21 19:03:48 +00:00
|
|
|
/* Look up our device */
|
2009-07-13 22:33:35 +00:00
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
2008-10-21 19:03:48 +00:00
|
|
|
if (err)
|
2009-03-12 08:55:09 +00:00
|
|
|
goto out_rtnl;
|
2008-10-21 19:03:48 +00:00
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
if (!rdev->ops->get_mesh_params) {
|
2009-03-20 15:57:36 +00:00
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2008-10-21 19:03:48 +00:00
|
|
|
/* Get the mesh params */
|
2009-07-07 01:56:12 +00:00
|
|
|
err = rdev->ops->get_mesh_params(&rdev->wiphy, dev, &cur_params);
|
2008-10-21 19:03:48 +00:00
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* Draw up a netlink message to send back */
|
2009-05-19 22:27:55 +00:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
2008-10-21 19:03:48 +00:00
|
|
|
if (!msg) {
|
|
|
|
err = -ENOBUFS;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
|
|
|
|
NL80211_CMD_GET_MESH_PARAMS);
|
|
|
|
if (!hdr)
|
|
|
|
goto nla_put_failure;
|
|
|
|
pinfoattr = nla_nest_start(msg, NL80211_ATTR_MESH_PARAMS);
|
|
|
|
if (!pinfoattr)
|
|
|
|
goto nla_put_failure;
|
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
|
|
|
|
NLA_PUT_U16(msg, NL80211_MESHCONF_RETRY_TIMEOUT,
|
|
|
|
cur_params.dot11MeshRetryTimeout);
|
|
|
|
NLA_PUT_U16(msg, NL80211_MESHCONF_CONFIRM_TIMEOUT,
|
|
|
|
cur_params.dot11MeshConfirmTimeout);
|
|
|
|
NLA_PUT_U16(msg, NL80211_MESHCONF_HOLDING_TIMEOUT,
|
|
|
|
cur_params.dot11MeshHoldingTimeout);
|
|
|
|
NLA_PUT_U16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
|
|
|
|
cur_params.dot11MeshMaxPeerLinks);
|
|
|
|
NLA_PUT_U8(msg, NL80211_MESHCONF_MAX_RETRIES,
|
|
|
|
cur_params.dot11MeshMaxRetries);
|
|
|
|
NLA_PUT_U8(msg, NL80211_MESHCONF_TTL,
|
|
|
|
cur_params.dot11MeshTTL);
|
|
|
|
NLA_PUT_U8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
|
|
|
|
cur_params.auto_open_plinks);
|
|
|
|
NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
|
|
|
|
cur_params.dot11MeshHWMPmaxPREQretries);
|
|
|
|
NLA_PUT_U32(msg, NL80211_MESHCONF_PATH_REFRESH_TIME,
|
|
|
|
cur_params.path_refresh_time);
|
|
|
|
NLA_PUT_U16(msg, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
|
|
|
|
cur_params.min_discovery_timeout);
|
|
|
|
NLA_PUT_U32(msg, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
|
|
|
|
cur_params.dot11MeshHWMPactivePathTimeout);
|
|
|
|
NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
|
|
|
|
cur_params.dot11MeshHWMPpreqMinInterval);
|
|
|
|
NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
|
|
|
|
cur_params.dot11MeshHWMPnetDiameterTraversalTime);
|
2009-11-09 23:46:57 +00:00
|
|
|
NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_ROOTMODE,
|
|
|
|
cur_params.dot11MeshHWMPRootMode);
|
2008-10-21 19:03:48 +00:00
|
|
|
nla_nest_end(msg, pinfoattr);
|
|
|
|
genlmsg_end(msg, hdr);
|
2009-07-10 09:51:34 +00:00
|
|
|
err = genlmsg_reply(msg, info);
|
2008-10-21 19:03:48 +00:00
|
|
|
goto out;
|
|
|
|
|
2009-03-12 08:55:09 +00:00
|
|
|
nla_put_failure:
|
2008-10-21 19:03:48 +00:00
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
err = -EMSGSIZE;
|
2009-03-12 08:55:09 +00:00
|
|
|
out:
|
2008-10-21 19:03:48 +00:00
|
|
|
/* Cleanup */
|
2009-07-07 01:56:12 +00:00
|
|
|
cfg80211_unlock_rdev(rdev);
|
2008-10-21 19:03:48 +00:00
|
|
|
dev_put(dev);
|
2009-03-12 08:55:09 +00:00
|
|
|
out_rtnl:
|
|
|
|
rtnl_unlock();
|
|
|
|
|
2008-10-21 19:03:48 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \
|
|
|
|
do {\
|
|
|
|
if (table[attr_num]) {\
|
|
|
|
cfg.param = nla_fn(table[attr_num]); \
|
|
|
|
mask |= (1 << (attr_num - 1)); \
|
|
|
|
} \
|
|
|
|
} while (0);\
|
|
|
|
|
2010-02-18 08:14:31 +00:00
|
|
|
static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] = {
|
2008-10-21 19:03:48 +00:00
|
|
|
[NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 },
|
|
|
|
[NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 },
|
|
|
|
[NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 },
|
|
|
|
[NL80211_MESHCONF_MAX_PEER_LINKS] = { .type = NLA_U16 },
|
|
|
|
[NL80211_MESHCONF_MAX_RETRIES] = { .type = NLA_U8 },
|
|
|
|
[NL80211_MESHCONF_TTL] = { .type = NLA_U8 },
|
|
|
|
[NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 },
|
|
|
|
|
|
|
|
[NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 },
|
|
|
|
[NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 },
|
|
|
|
[NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = { .type = NLA_U16 },
|
|
|
|
[NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 },
|
|
|
|
[NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 },
|
|
|
|
[NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 },
|
|
|
|
};
|
|
|
|
|
|
|
|
static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
u32 mask;
|
2009-07-07 01:56:12 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
2008-10-21 19:03:48 +00:00
|
|
|
struct net_device *dev;
|
|
|
|
struct mesh_config cfg;
|
|
|
|
struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1];
|
|
|
|
struct nlattr *parent_attr;
|
|
|
|
|
|
|
|
parent_attr = info->attrs[NL80211_ATTR_MESH_PARAMS];
|
|
|
|
if (!parent_attr)
|
|
|
|
return -EINVAL;
|
|
|
|
if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX,
|
|
|
|
parent_attr, nl80211_meshconf_params_policy))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2009-03-12 08:55:09 +00:00
|
|
|
rtnl_lock();
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
2008-10-21 19:03:48 +00:00
|
|
|
if (err)
|
2009-03-12 08:55:09 +00:00
|
|
|
goto out_rtnl;
|
2008-10-21 19:03:48 +00:00
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
if (!rdev->ops->set_mesh_params) {
|
2009-03-20 15:57:36 +00:00
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2008-10-21 19:03:48 +00:00
|
|
|
/* This makes sure that there aren't more than 32 mesh config
|
|
|
|
* parameters (otherwise our bitfield scheme would not work.) */
|
|
|
|
BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32);
|
|
|
|
|
|
|
|
/* Fill in the params struct */
|
|
|
|
mask = 0;
|
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout,
|
|
|
|
mask, NL80211_MESHCONF_RETRY_TIMEOUT, nla_get_u16);
|
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout,
|
|
|
|
mask, NL80211_MESHCONF_CONFIRM_TIMEOUT, nla_get_u16);
|
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout,
|
|
|
|
mask, NL80211_MESHCONF_HOLDING_TIMEOUT, nla_get_u16);
|
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks,
|
|
|
|
mask, NL80211_MESHCONF_MAX_PEER_LINKS, nla_get_u16);
|
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries,
|
|
|
|
mask, NL80211_MESHCONF_MAX_RETRIES, nla_get_u8);
|
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL,
|
|
|
|
mask, NL80211_MESHCONF_TTL, nla_get_u8);
|
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks,
|
|
|
|
mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, nla_get_u8);
|
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries,
|
|
|
|
mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
|
|
|
|
nla_get_u8);
|
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time,
|
|
|
|
mask, NL80211_MESHCONF_PATH_REFRESH_TIME, nla_get_u32);
|
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout,
|
|
|
|
mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
|
|
|
|
nla_get_u16);
|
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout,
|
|
|
|
mask, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
|
|
|
|
nla_get_u32);
|
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval,
|
|
|
|
mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
|
|
|
|
nla_get_u16);
|
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
|
|
|
|
dot11MeshHWMPnetDiameterTraversalTime,
|
|
|
|
mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
|
|
|
|
nla_get_u16);
|
2009-11-09 23:46:57 +00:00
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
|
|
|
|
dot11MeshHWMPRootMode, mask,
|
|
|
|
NL80211_MESHCONF_HWMP_ROOTMODE,
|
|
|
|
nla_get_u8);
|
2008-10-21 19:03:48 +00:00
|
|
|
|
|
|
|
/* Apply changes */
|
2009-07-07 01:56:12 +00:00
|
|
|
err = rdev->ops->set_mesh_params(&rdev->wiphy, dev, &cfg, mask);
|
2008-10-21 19:03:48 +00:00
|
|
|
|
2009-03-20 15:57:36 +00:00
|
|
|
out:
|
2008-10-21 19:03:48 +00:00
|
|
|
/* cleanup */
|
2009-07-07 01:56:12 +00:00
|
|
|
cfg80211_unlock_rdev(rdev);
|
2008-10-21 19:03:48 +00:00
|
|
|
dev_put(dev);
|
2009-03-12 08:55:09 +00:00
|
|
|
out_rtnl:
|
|
|
|
rtnl_unlock();
|
|
|
|
|
2008-10-21 19:03:48 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef FILL_IN_MESH_PARAM_IF_SET
|
|
|
|
|
2009-01-30 17:26:42 +00:00
|
|
|
static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct sk_buff *msg;
|
|
|
|
void *hdr = NULL;
|
|
|
|
struct nlattr *nl_reg_rules;
|
|
|
|
unsigned int i;
|
|
|
|
int err = -EINVAL;
|
|
|
|
|
2009-02-21 05:04:21 +00:00
|
|
|
mutex_lock(&cfg80211_mutex);
|
2009-01-30 17:26:42 +00:00
|
|
|
|
|
|
|
if (!cfg80211_regdomain)
|
|
|
|
goto out;
|
|
|
|
|
2009-05-19 22:27:55 +00:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
2009-01-30 17:26:42 +00:00
|
|
|
if (!msg) {
|
|
|
|
err = -ENOBUFS;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
|
|
|
|
NL80211_CMD_GET_REG);
|
|
|
|
if (!hdr)
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2,
|
|
|
|
cfg80211_regdomain->alpha2);
|
|
|
|
|
|
|
|
nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
|
|
|
|
if (!nl_reg_rules)
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) {
|
|
|
|
struct nlattr *nl_reg_rule;
|
|
|
|
const struct ieee80211_reg_rule *reg_rule;
|
|
|
|
const struct ieee80211_freq_range *freq_range;
|
|
|
|
const struct ieee80211_power_rule *power_rule;
|
|
|
|
|
|
|
|
reg_rule = &cfg80211_regdomain->reg_rules[i];
|
|
|
|
freq_range = ®_rule->freq_range;
|
|
|
|
power_rule = ®_rule->power_rule;
|
|
|
|
|
|
|
|
nl_reg_rule = nla_nest_start(msg, i);
|
|
|
|
if (!nl_reg_rule)
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_REG_RULE_FLAGS,
|
|
|
|
reg_rule->flags);
|
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_START,
|
|
|
|
freq_range->start_freq_khz);
|
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_END,
|
|
|
|
freq_range->end_freq_khz);
|
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_MAX_BW,
|
|
|
|
freq_range->max_bandwidth_khz);
|
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
|
|
|
|
power_rule->max_antenna_gain);
|
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP,
|
|
|
|
power_rule->max_eirp);
|
|
|
|
|
|
|
|
nla_nest_end(msg, nl_reg_rule);
|
|
|
|
}
|
|
|
|
|
|
|
|
nla_nest_end(msg, nl_reg_rules);
|
|
|
|
|
|
|
|
genlmsg_end(msg, hdr);
|
2009-07-10 09:51:34 +00:00
|
|
|
err = genlmsg_reply(msg, info);
|
2009-01-30 17:26:42 +00:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
err = -EMSGSIZE;
|
|
|
|
out:
|
2009-02-21 05:04:21 +00:00
|
|
|
mutex_unlock(&cfg80211_mutex);
|
2009-01-30 17:26:42 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2008-09-10 06:19:48 +00:00
|
|
|
static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
|
|
|
|
struct nlattr *nl_reg_rule;
|
|
|
|
char *alpha2 = NULL;
|
|
|
|
int rem_reg_rules = 0, r = 0;
|
|
|
|
u32 num_rules = 0, rule_idx = 0, size_of_regd;
|
|
|
|
struct ieee80211_regdomain *rd = NULL;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_REG_RULES])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
alpha2 = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
|
|
|
|
|
|
|
|
nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
|
|
|
|
rem_reg_rules) {
|
|
|
|
num_rules++;
|
|
|
|
if (num_rules > NL80211_MAX_SUPP_REG_RULES)
|
2009-05-13 21:04:39 +00:00
|
|
|
return -EINVAL;
|
2008-09-10 06:19:48 +00:00
|
|
|
}
|
|
|
|
|
2009-05-13 21:04:41 +00:00
|
|
|
mutex_lock(&cfg80211_mutex);
|
|
|
|
|
2009-05-13 21:04:40 +00:00
|
|
|
if (!reg_is_valid_request(alpha2)) {
|
|
|
|
r = -EINVAL;
|
|
|
|
goto bad_reg;
|
|
|
|
}
|
2008-09-10 06:19:48 +00:00
|
|
|
|
|
|
|
size_of_regd = sizeof(struct ieee80211_regdomain) +
|
|
|
|
(num_rules * sizeof(struct ieee80211_reg_rule));
|
|
|
|
|
|
|
|
rd = kzalloc(size_of_regd, GFP_KERNEL);
|
2009-05-13 21:04:40 +00:00
|
|
|
if (!rd) {
|
|
|
|
r = -ENOMEM;
|
|
|
|
goto bad_reg;
|
|
|
|
}
|
2008-09-10 06:19:48 +00:00
|
|
|
|
|
|
|
rd->n_reg_rules = num_rules;
|
|
|
|
rd->alpha2[0] = alpha2[0];
|
|
|
|
rd->alpha2[1] = alpha2[1];
|
|
|
|
|
|
|
|
nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
|
|
|
|
rem_reg_rules) {
|
|
|
|
nla_parse(tb, NL80211_REG_RULE_ATTR_MAX,
|
|
|
|
nla_data(nl_reg_rule), nla_len(nl_reg_rule),
|
|
|
|
reg_rule_policy);
|
|
|
|
r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]);
|
|
|
|
if (r)
|
|
|
|
goto bad_reg;
|
|
|
|
|
|
|
|
rule_idx++;
|
|
|
|
|
2009-05-13 21:04:40 +00:00
|
|
|
if (rule_idx > NL80211_MAX_SUPP_REG_RULES) {
|
|
|
|
r = -EINVAL;
|
2008-09-10 06:19:48 +00:00
|
|
|
goto bad_reg;
|
2009-05-13 21:04:40 +00:00
|
|
|
}
|
2008-09-10 06:19:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
BUG_ON(rule_idx != num_rules);
|
|
|
|
|
|
|
|
r = set_regdom(rd);
|
2009-05-13 21:04:41 +00:00
|
|
|
|
2009-02-21 05:04:21 +00:00
|
|
|
mutex_unlock(&cfg80211_mutex);
|
2009-05-13 21:04:40 +00:00
|
|
|
|
2008-09-10 06:19:48 +00:00
|
|
|
return r;
|
|
|
|
|
2008-10-24 18:32:20 +00:00
|
|
|
bad_reg:
|
2009-05-13 21:04:41 +00:00
|
|
|
mutex_unlock(&cfg80211_mutex);
|
2008-09-10 06:19:48 +00:00
|
|
|
kfree(rd);
|
2009-05-13 21:04:40 +00:00
|
|
|
return r;
|
2008-09-10 06:19:48 +00:00
|
|
|
}
|
|
|
|
|
2009-06-17 15:41:49 +00:00
|
|
|
static int validate_scan_freqs(struct nlattr *freqs)
|
|
|
|
{
|
|
|
|
struct nlattr *attr1, *attr2;
|
|
|
|
int n_channels = 0, tmp1, tmp2;
|
|
|
|
|
|
|
|
nla_for_each_nested(attr1, freqs, tmp1) {
|
|
|
|
n_channels++;
|
|
|
|
/*
|
|
|
|
* Some hardware has a limited channel list for
|
|
|
|
* scanning, and it is pretty much nonsensical
|
|
|
|
* to scan for a channel twice, so disallow that
|
|
|
|
* and don't require drivers to check that the
|
|
|
|
* channel list they get isn't longer than what
|
|
|
|
* they can scan, as long as they can scan all
|
|
|
|
* the channels they registered at once.
|
|
|
|
*/
|
|
|
|
nla_for_each_nested(attr2, freqs, tmp2)
|
|
|
|
if (attr1 != attr2 &&
|
|
|
|
nla_get_u32(attr1) == nla_get_u32(attr2))
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return n_channels;
|
|
|
|
}
|
|
|
|
|
2009-02-10 20:25:55 +00:00
|
|
|
static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2009-07-07 01:56:12 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
2009-02-10 20:25:55 +00:00
|
|
|
struct net_device *dev;
|
|
|
|
struct cfg80211_scan_request *request;
|
|
|
|
struct cfg80211_ssid *ssid;
|
|
|
|
struct ieee80211_channel *channel;
|
|
|
|
struct nlattr *attr;
|
|
|
|
struct wiphy *wiphy;
|
2009-06-17 15:41:49 +00:00
|
|
|
int err, tmp, n_ssids = 0, n_channels, i;
|
2009-02-10 20:25:55 +00:00
|
|
|
enum ieee80211_band band;
|
2009-02-16 17:39:13 +00:00
|
|
|
size_t ie_len;
|
2009-02-10 20:25:55 +00:00
|
|
|
|
2009-03-27 11:40:28 +00:00
|
|
|
if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2009-03-12 08:55:09 +00:00
|
|
|
rtnl_lock();
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
2009-02-10 20:25:55 +00:00
|
|
|
if (err)
|
2009-03-12 08:55:09 +00:00
|
|
|
goto out_rtnl;
|
2009-02-10 20:25:55 +00:00
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
wiphy = &rdev->wiphy;
|
2009-02-10 20:25:55 +00:00
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
if (!rdev->ops->scan) {
|
2009-02-10 20:25:55 +00:00
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-03-20 19:21:18 +00:00
|
|
|
if (!netif_running(dev)) {
|
|
|
|
err = -ENETDOWN;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
if (rdev->scan_req) {
|
2009-02-10 20:25:55 +00:00
|
|
|
err = -EBUSY;
|
2009-03-12 08:55:09 +00:00
|
|
|
goto out;
|
2009-02-10 20:25:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
|
2009-06-17 15:41:49 +00:00
|
|
|
n_channels = validate_scan_freqs(
|
|
|
|
info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
|
2009-02-10 20:25:55 +00:00
|
|
|
if (!n_channels) {
|
|
|
|
err = -EINVAL;
|
2009-03-12 08:55:09 +00:00
|
|
|
goto out;
|
2009-02-10 20:25:55 +00:00
|
|
|
}
|
|
|
|
} else {
|
2009-06-17 15:41:49 +00:00
|
|
|
n_channels = 0;
|
|
|
|
|
2009-02-10 20:25:55 +00:00
|
|
|
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;
|
2009-03-12 08:55:09 +00:00
|
|
|
goto out;
|
2009-02-10 20:25:55 +00:00
|
|
|
}
|
|
|
|
|
2009-02-16 17:39:13 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_IE])
|
|
|
|
ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
|
|
|
|
else
|
|
|
|
ie_len = 0;
|
|
|
|
|
2009-03-31 10:12:05 +00:00
|
|
|
if (ie_len > wiphy->max_scan_ie_len) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-02-10 20:25:55 +00:00
|
|
|
request = kzalloc(sizeof(*request)
|
|
|
|
+ sizeof(*ssid) * n_ssids
|
2009-02-16 17:39:13 +00:00
|
|
|
+ sizeof(channel) * n_channels
|
|
|
|
+ ie_len, GFP_KERNEL);
|
2009-02-10 20:25:55 +00:00
|
|
|
if (!request) {
|
|
|
|
err = -ENOMEM;
|
2009-03-12 08:55:09 +00:00
|
|
|
goto out;
|
2009-02-10 20:25:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (n_ssids)
|
2009-08-07 15:54:07 +00:00
|
|
|
request->ssids = (void *)&request->channels[n_channels];
|
2009-02-10 20:25:55 +00:00
|
|
|
request->n_ssids = n_ssids;
|
2009-02-16 17:39:13 +00:00
|
|
|
if (ie_len) {
|
|
|
|
if (request->ssids)
|
|
|
|
request->ie = (void *)(request->ssids + n_ssids);
|
|
|
|
else
|
|
|
|
request->ie = (void *)(request->channels + n_channels);
|
|
|
|
}
|
2009-02-10 20:25:55 +00:00
|
|
|
|
2009-11-02 12:32:03 +00:00
|
|
|
i = 0;
|
2009-02-10 20:25:55 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
|
|
|
|
/* user specified, bail out if channel not found */
|
|
|
|
nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) {
|
2009-11-02 12:32:03 +00:00
|
|
|
struct ieee80211_channel *chan;
|
|
|
|
|
|
|
|
chan = ieee80211_get_channel(wiphy, nla_get_u32(attr));
|
|
|
|
|
|
|
|
if (!chan) {
|
2009-02-10 20:25:55 +00:00
|
|
|
err = -EINVAL;
|
|
|
|
goto out_free;
|
|
|
|
}
|
2009-11-02 12:32:03 +00:00
|
|
|
|
|
|
|
/* ignore disabled channels */
|
|
|
|
if (chan->flags & IEEE80211_CHAN_DISABLED)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
request->channels[i] = chan;
|
2009-02-10 20:25:55 +00:00
|
|
|
i++;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* all channels */
|
|
|
|
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++) {
|
2009-11-02 12:32:03 +00:00
|
|
|
struct ieee80211_channel *chan;
|
|
|
|
|
|
|
|
chan = &wiphy->bands[band]->channels[j];
|
|
|
|
|
|
|
|
if (chan->flags & IEEE80211_CHAN_DISABLED)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
request->channels[i] = chan;
|
2009-02-10 20:25:55 +00:00
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-11-02 12:32:03 +00:00
|
|
|
if (!i) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out_free;
|
|
|
|
}
|
|
|
|
|
|
|
|
request->n_channels = i;
|
|
|
|
|
2009-02-10 20:25:55 +00:00
|
|
|
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++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-02-16 17:39:13 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_IE]) {
|
|
|
|
request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
|
2009-04-01 09:58:36 +00:00
|
|
|
memcpy((void *)request->ie,
|
|
|
|
nla_data(info->attrs[NL80211_ATTR_IE]),
|
2009-02-16 17:39:13 +00:00
|
|
|
request->ie_len);
|
|
|
|
}
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
request->dev = dev;
|
2009-07-07 01:56:12 +00:00
|
|
|
request->wiphy = &rdev->wiphy;
|
2009-02-10 20:25:55 +00:00
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
rdev->scan_req = request;
|
|
|
|
err = rdev->ops->scan(&rdev->wiphy, dev, request);
|
2009-02-10 20:25:55 +00:00
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
if (!err) {
|
2009-07-07 01:56:12 +00:00
|
|
|
nl80211_send_scan_start(rdev, dev);
|
2009-07-13 22:33:35 +00:00
|
|
|
dev_hold(dev);
|
|
|
|
}
|
2009-06-16 17:56:42 +00:00
|
|
|
|
2009-02-10 20:25:55 +00:00
|
|
|
out_free:
|
|
|
|
if (err) {
|
2009-07-07 01:56:12 +00:00
|
|
|
rdev->scan_req = NULL;
|
2009-02-10 20:25:55 +00:00
|
|
|
kfree(request);
|
|
|
|
}
|
|
|
|
out:
|
2009-07-07 01:56:12 +00:00
|
|
|
cfg80211_unlock_rdev(rdev);
|
2009-02-10 20:25:55 +00:00
|
|
|
dev_put(dev);
|
2009-03-12 08:55:09 +00:00
|
|
|
out_rtnl:
|
|
|
|
rtnl_unlock();
|
|
|
|
|
2009-02-10 20:25:55 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
|
|
|
|
struct cfg80211_registered_device *rdev,
|
2009-07-10 16:42:31 +00:00
|
|
|
struct wireless_dev *wdev,
|
|
|
|
struct cfg80211_internal_bss *intbss)
|
2009-02-10 20:25:55 +00:00
|
|
|
{
|
2009-07-10 16:42:31 +00:00
|
|
|
struct cfg80211_bss *res = &intbss->pub;
|
2009-02-10 20:25:55 +00:00
|
|
|
void *hdr;
|
|
|
|
struct nlattr *bss;
|
2009-07-10 16:42:31 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
ASSERT_WDEV_LOCK(wdev);
|
2009-02-10 20:25:55 +00:00
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, pid, seq, flags,
|
|
|
|
NL80211_CMD_NEW_SCAN_RESULTS);
|
|
|
|
if (!hdr)
|
|
|
|
return -1;
|
|
|
|
|
2009-08-07 14:17:38 +00:00
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, rdev->bss_generation);
|
2009-07-10 16:42:31 +00:00
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex);
|
2009-02-10 20:25:55 +00:00
|
|
|
|
|
|
|
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);
|
2010-01-06 14:19:24 +00:00
|
|
|
if (res->beacon_ies && res->len_beacon_ies &&
|
|
|
|
res->beacon_ies != res->information_elements)
|
|
|
|
NLA_PUT(msg, NL80211_BSS_BEACON_IES,
|
|
|
|
res->len_beacon_ies, res->beacon_ies);
|
2009-02-10 20:25:55 +00:00
|
|
|
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);
|
2009-09-24 10:21:01 +00:00
|
|
|
NLA_PUT_U32(msg, NL80211_BSS_SEEN_MS_AGO,
|
|
|
|
jiffies_to_msecs(jiffies - intbss->ts));
|
2009-02-10 20:25:55 +00:00
|
|
|
|
2009-02-18 17:45:06 +00:00
|
|
|
switch (rdev->wiphy.signal_type) {
|
2009-02-10 20:25:55 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2009-07-10 16:42:31 +00:00
|
|
|
switch (wdev->iftype) {
|
|
|
|
case NL80211_IFTYPE_STATION:
|
|
|
|
if (intbss == wdev->current_bss)
|
|
|
|
NLA_PUT_U32(msg, NL80211_BSS_STATUS,
|
|
|
|
NL80211_BSS_STATUS_ASSOCIATED);
|
|
|
|
else for (i = 0; i < MAX_AUTH_BSSES; i++) {
|
|
|
|
if (intbss != wdev->auth_bsses[i])
|
|
|
|
continue;
|
|
|
|
NLA_PUT_U32(msg, NL80211_BSS_STATUS,
|
|
|
|
NL80211_BSS_STATUS_AUTHENTICATED);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case NL80211_IFTYPE_ADHOC:
|
|
|
|
if (intbss == wdev->current_bss)
|
|
|
|
NLA_PUT_U32(msg, NL80211_BSS_STATUS,
|
|
|
|
NL80211_BSS_STATUS_IBSS_JOINED);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2009-02-10 20:25:55 +00:00
|
|
|
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)
|
|
|
|
{
|
2009-07-10 16:42:31 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
|
|
|
struct net_device *dev;
|
2009-02-10 20:25:55 +00:00
|
|
|
struct cfg80211_internal_bss *scan;
|
2009-07-10 16:42:31 +00:00
|
|
|
struct wireless_dev *wdev;
|
2009-02-10 20:25:55 +00:00
|
|
|
int ifidx = cb->args[0];
|
|
|
|
int start = cb->args[1], idx = 0;
|
|
|
|
int err;
|
|
|
|
|
2009-11-11 10:30:02 +00:00
|
|
|
if (!ifidx)
|
|
|
|
ifidx = nl80211_get_ifidx(cb);
|
|
|
|
if (ifidx < 0)
|
|
|
|
return ifidx;
|
|
|
|
cb->args[0] = ifidx;
|
2009-02-10 20:25:55 +00:00
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
dev = dev_get_by_index(sock_net(skb->sk), ifidx);
|
2009-07-10 16:42:31 +00:00
|
|
|
if (!dev)
|
2009-02-10 20:25:55 +00:00
|
|
|
return -ENODEV;
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
rdev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
|
2009-07-10 16:42:31 +00:00
|
|
|
if (IS_ERR(rdev)) {
|
|
|
|
err = PTR_ERR(rdev);
|
2009-02-10 20:25:55 +00:00
|
|
|
goto out_put_netdev;
|
|
|
|
}
|
|
|
|
|
2009-07-10 16:42:31 +00:00
|
|
|
wdev = dev->ieee80211_ptr;
|
2009-02-10 20:25:55 +00:00
|
|
|
|
2009-07-10 16:42:31 +00:00
|
|
|
wdev_lock(wdev);
|
|
|
|
spin_lock_bh(&rdev->bss_lock);
|
|
|
|
cfg80211_bss_expire(rdev);
|
|
|
|
|
|
|
|
list_for_each_entry(scan, &rdev->bss_list, list) {
|
2009-02-10 20:25:55 +00:00
|
|
|
if (++idx <= start)
|
|
|
|
continue;
|
|
|
|
if (nl80211_send_bss(skb,
|
|
|
|
NETLINK_CB(cb->skb).pid,
|
|
|
|
cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
2009-07-10 16:42:31 +00:00
|
|
|
rdev, wdev, scan) < 0) {
|
2009-02-10 20:25:55 +00:00
|
|
|
idx--;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
2009-07-10 16:42:31 +00:00
|
|
|
spin_unlock_bh(&rdev->bss_lock);
|
|
|
|
wdev_unlock(wdev);
|
2009-02-10 20:25:55 +00:00
|
|
|
|
|
|
|
cb->args[1] = idx;
|
|
|
|
err = skb->len;
|
2009-07-10 16:42:31 +00:00
|
|
|
cfg80211_unlock_rdev(rdev);
|
2009-02-10 20:25:55 +00:00
|
|
|
out_put_netdev:
|
2009-07-10 16:42:31 +00:00
|
|
|
dev_put(dev);
|
2009-02-10 20:25:55 +00:00
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2009-11-11 11:25:40 +00:00
|
|
|
static int nl80211_send_survey(struct sk_buff *msg, u32 pid, u32 seq,
|
|
|
|
int flags, struct net_device *dev,
|
|
|
|
struct survey_info *survey)
|
|
|
|
{
|
|
|
|
void *hdr;
|
|
|
|
struct nlattr *infoattr;
|
|
|
|
|
|
|
|
/* Survey without a channel doesn't make sense */
|
|
|
|
if (!survey->channel)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, pid, seq, flags,
|
|
|
|
NL80211_CMD_NEW_SURVEY_RESULTS);
|
|
|
|
if (!hdr)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
|
|
|
|
|
|
|
|
infoattr = nla_nest_start(msg, NL80211_ATTR_SURVEY_INFO);
|
|
|
|
if (!infoattr)
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
NLA_PUT_U32(msg, NL80211_SURVEY_INFO_FREQUENCY,
|
|
|
|
survey->channel->center_freq);
|
|
|
|
if (survey->filled & SURVEY_INFO_NOISE_DBM)
|
|
|
|
NLA_PUT_U8(msg, NL80211_SURVEY_INFO_NOISE,
|
|
|
|
survey->noise);
|
|
|
|
|
|
|
|
nla_nest_end(msg, infoattr);
|
|
|
|
|
|
|
|
return genlmsg_end(msg, hdr);
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
return -EMSGSIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_dump_survey(struct sk_buff *skb,
|
|
|
|
struct netlink_callback *cb)
|
|
|
|
{
|
|
|
|
struct survey_info survey;
|
|
|
|
struct cfg80211_registered_device *dev;
|
|
|
|
struct net_device *netdev;
|
|
|
|
int ifidx = cb->args[0];
|
|
|
|
int survey_idx = cb->args[1];
|
|
|
|
int res;
|
|
|
|
|
|
|
|
if (!ifidx)
|
|
|
|
ifidx = nl80211_get_ifidx(cb);
|
|
|
|
if (ifidx < 0)
|
|
|
|
return ifidx;
|
|
|
|
cb->args[0] = ifidx;
|
|
|
|
|
|
|
|
rtnl_lock();
|
|
|
|
|
|
|
|
netdev = __dev_get_by_index(sock_net(skb->sk), ifidx);
|
|
|
|
if (!netdev) {
|
|
|
|
res = -ENODEV;
|
|
|
|
goto out_rtnl;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
|
|
|
|
if (IS_ERR(dev)) {
|
|
|
|
res = PTR_ERR(dev);
|
|
|
|
goto out_rtnl;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!dev->ops->dump_survey) {
|
|
|
|
res = -EOPNOTSUPP;
|
|
|
|
goto out_err;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
res = dev->ops->dump_survey(&dev->wiphy, netdev, survey_idx,
|
|
|
|
&survey);
|
|
|
|
if (res == -ENOENT)
|
|
|
|
break;
|
|
|
|
if (res)
|
|
|
|
goto out_err;
|
|
|
|
|
|
|
|
if (nl80211_send_survey(skb,
|
|
|
|
NETLINK_CB(cb->skb).pid,
|
|
|
|
cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
|
|
|
netdev,
|
|
|
|
&survey) < 0)
|
|
|
|
goto out;
|
|
|
|
survey_idx++;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
cb->args[1] = survey_idx;
|
|
|
|
res = skb->len;
|
|
|
|
out_err:
|
|
|
|
cfg80211_unlock_rdev(dev);
|
|
|
|
out_rtnl:
|
|
|
|
rtnl_unlock();
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2009-03-20 19:21:17 +00:00
|
|
|
static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type)
|
|
|
|
{
|
2009-07-01 19:26:54 +00:00
|
|
|
return auth_type <= NL80211_AUTHTYPE_MAX;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool nl80211_valid_wpa_versions(u32 wpa_versions)
|
|
|
|
{
|
|
|
|
return !(wpa_versions & ~(NL80211_WPA_VERSION_1 |
|
|
|
|
NL80211_WPA_VERSION_2));
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool nl80211_valid_akm_suite(u32 akm)
|
|
|
|
{
|
|
|
|
return akm == WLAN_AKM_SUITE_8021X ||
|
|
|
|
akm == WLAN_AKM_SUITE_PSK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool nl80211_valid_cipher_suite(u32 cipher)
|
|
|
|
{
|
|
|
|
return cipher == WLAN_CIPHER_SUITE_WEP40 ||
|
|
|
|
cipher == WLAN_CIPHER_SUITE_WEP104 ||
|
|
|
|
cipher == WLAN_CIPHER_SUITE_TKIP ||
|
|
|
|
cipher == WLAN_CIPHER_SUITE_CCMP ||
|
|
|
|
cipher == WLAN_CIPHER_SUITE_AES_CMAC;
|
2009-03-20 19:21:17 +00:00
|
|
|
}
|
|
|
|
|
2009-07-01 19:26:54 +00:00
|
|
|
|
2009-03-19 11:39:22 +00:00
|
|
|
static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2009-07-07 01:56:12 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
2009-03-19 11:39:22 +00:00
|
|
|
struct net_device *dev;
|
2009-07-02 15:20:43 +00:00
|
|
|
struct ieee80211_channel *chan;
|
|
|
|
const u8 *bssid, *ssid, *ie = NULL;
|
|
|
|
int err, ssid_len, ie_len = 0;
|
|
|
|
enum nl80211_auth_type auth_type;
|
cfg80211: rework key operation
This reworks the key operation in cfg80211, and now only
allows, from userspace, configuring keys (via nl80211)
after the connection has been established (in managed
mode), the IBSS been joined (in IBSS mode), at any time
(in AP[_VLAN] modes) or never for all the other modes.
In order to do shared key authentication correctly, it
is now possible to give a WEP key to the AUTH command.
To configure static WEP keys, these are given to the
CONNECT or IBSS_JOIN command directly, for a userspace
SME it is assumed it will configure it properly after
the connection has been established.
Since mac80211 used to check the default key in IBSS
mode to see whether or not the network is protected,
it needs an update in that area, as well as an update
to make use of the WEP key passed to auth() for shared
key authentication.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-07-08 12:22:54 +00:00
|
|
|
struct key_parse key;
|
2010-04-04 06:37:19 +00:00
|
|
|
bool local_state_change;
|
2009-03-19 11:39:22 +00:00
|
|
|
|
2009-03-27 11:40:28 +00:00
|
|
|
if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_MAC])
|
|
|
|
return -EINVAL;
|
|
|
|
|
2009-03-27 18:52:47 +00:00
|
|
|
if (!info->attrs[NL80211_ATTR_AUTH_TYPE])
|
|
|
|
return -EINVAL;
|
|
|
|
|
2009-07-02 15:20:43 +00:00
|
|
|
if (!info->attrs[NL80211_ATTR_SSID])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
|
|
|
|
return -EINVAL;
|
|
|
|
|
cfg80211: rework key operation
This reworks the key operation in cfg80211, and now only
allows, from userspace, configuring keys (via nl80211)
after the connection has been established (in managed
mode), the IBSS been joined (in IBSS mode), at any time
(in AP[_VLAN] modes) or never for all the other modes.
In order to do shared key authentication correctly, it
is now possible to give a WEP key to the AUTH command.
To configure static WEP keys, these are given to the
CONNECT or IBSS_JOIN command directly, for a userspace
SME it is assumed it will configure it properly after
the connection has been established.
Since mac80211 used to check the default key in IBSS
mode to see whether or not the network is protected,
it needs an update in that area, as well as an update
to make use of the WEP key passed to auth() for shared
key authentication.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-07-08 12:22:54 +00:00
|
|
|
err = nl80211_parse_key(info, &key);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (key.idx >= 0) {
|
|
|
|
if (!key.p.key || !key.p.key_len)
|
|
|
|
return -EINVAL;
|
|
|
|
if ((key.p.cipher != WLAN_CIPHER_SUITE_WEP40 ||
|
|
|
|
key.p.key_len != WLAN_KEY_LEN_WEP40) &&
|
|
|
|
(key.p.cipher != WLAN_CIPHER_SUITE_WEP104 ||
|
|
|
|
key.p.key_len != WLAN_KEY_LEN_WEP104))
|
|
|
|
return -EINVAL;
|
|
|
|
if (key.idx > 4)
|
|
|
|
return -EINVAL;
|
|
|
|
} else {
|
|
|
|
key.p.key_len = 0;
|
|
|
|
key.p.key = NULL;
|
|
|
|
}
|
|
|
|
|
2009-03-19 11:39:22 +00:00
|
|
|
rtnl_lock();
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
2009-03-19 11:39:22 +00:00
|
|
|
if (err)
|
|
|
|
goto unlock_rtnl;
|
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
if (!rdev->ops->auth) {
|
2009-03-19 11:39:22 +00:00
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-03-20 19:21:19 +00:00
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-03-20 19:21:18 +00:00
|
|
|
if (!netif_running(dev)) {
|
|
|
|
err = -ENETDOWN;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-07-02 15:20:43 +00:00
|
|
|
bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
2009-07-07 01:56:12 +00:00
|
|
|
chan = ieee80211_get_channel(&rdev->wiphy,
|
2009-07-02 15:20:43 +00:00
|
|
|
nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
|
|
|
|
if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED)) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
2009-03-19 11:39:22 +00:00
|
|
|
}
|
|
|
|
|
2009-07-02 15:20:43 +00:00
|
|
|
ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
|
|
|
|
ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
|
2009-03-19 11:39:22 +00:00
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_IE]) {
|
2009-07-02 15:20:43 +00:00
|
|
|
ie = nla_data(info->attrs[NL80211_ATTR_IE]);
|
|
|
|
ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
|
2009-03-19 11:39:22 +00:00
|
|
|
}
|
|
|
|
|
2009-07-02 15:20:43 +00:00
|
|
|
auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
|
|
|
|
if (!nl80211_valid_auth_type(auth_type)) {
|
2009-03-27 18:52:47 +00:00
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
2009-03-19 11:39:22 +00:00
|
|
|
}
|
|
|
|
|
2010-04-04 06:37:19 +00:00
|
|
|
local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
|
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
err = cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
|
cfg80211: rework key operation
This reworks the key operation in cfg80211, and now only
allows, from userspace, configuring keys (via nl80211)
after the connection has been established (in managed
mode), the IBSS been joined (in IBSS mode), at any time
(in AP[_VLAN] modes) or never for all the other modes.
In order to do shared key authentication correctly, it
is now possible to give a WEP key to the AUTH command.
To configure static WEP keys, these are given to the
CONNECT or IBSS_JOIN command directly, for a userspace
SME it is assumed it will configure it properly after
the connection has been established.
Since mac80211 used to check the default key in IBSS
mode to see whether or not the network is protected,
it needs an update in that area, as well as an update
to make use of the WEP key passed to auth() for shared
key authentication.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-07-08 12:22:54 +00:00
|
|
|
ssid, ssid_len, ie, ie_len,
|
2010-04-04 06:37:19 +00:00
|
|
|
key.p.key, key.p.key_len, key.idx,
|
|
|
|
local_state_change);
|
2009-03-19 11:39:22 +00:00
|
|
|
|
|
|
|
out:
|
2009-07-07 01:56:12 +00:00
|
|
|
cfg80211_unlock_rdev(rdev);
|
2009-03-19 11:39:22 +00:00
|
|
|
dev_put(dev);
|
|
|
|
unlock_rtnl:
|
|
|
|
rtnl_unlock();
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2009-07-01 19:26:54 +00:00
|
|
|
static int nl80211_crypto_settings(struct genl_info *info,
|
2009-07-02 19:36:37 +00:00
|
|
|
struct cfg80211_crypto_settings *settings,
|
|
|
|
int cipher_limit)
|
2009-07-01 19:26:54 +00:00
|
|
|
{
|
2009-07-25 14:54:36 +00:00
|
|
|
memset(settings, 0, sizeof(*settings));
|
|
|
|
|
2009-07-01 19:26:54 +00:00
|
|
|
settings->control_port = info->attrs[NL80211_ATTR_CONTROL_PORT];
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]) {
|
|
|
|
void *data;
|
|
|
|
int len, i;
|
|
|
|
|
|
|
|
data = nla_data(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
|
|
|
|
len = nla_len(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
|
|
|
|
settings->n_ciphers_pairwise = len / sizeof(u32);
|
|
|
|
|
|
|
|
if (len % sizeof(u32))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2009-07-02 19:36:37 +00:00
|
|
|
if (settings->n_ciphers_pairwise > cipher_limit)
|
2009-07-01 19:26:54 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
memcpy(settings->ciphers_pairwise, data, len);
|
|
|
|
|
|
|
|
for (i = 0; i < settings->n_ciphers_pairwise; i++)
|
|
|
|
if (!nl80211_valid_cipher_suite(
|
|
|
|
settings->ciphers_pairwise[i]))
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]) {
|
|
|
|
settings->cipher_group =
|
|
|
|
nla_get_u32(info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]);
|
|
|
|
if (!nl80211_valid_cipher_suite(settings->cipher_group))
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_WPA_VERSIONS]) {
|
|
|
|
settings->wpa_versions =
|
|
|
|
nla_get_u32(info->attrs[NL80211_ATTR_WPA_VERSIONS]);
|
|
|
|
if (!nl80211_valid_wpa_versions(settings->wpa_versions))
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_AKM_SUITES]) {
|
|
|
|
void *data;
|
|
|
|
int len, i;
|
|
|
|
|
|
|
|
data = nla_data(info->attrs[NL80211_ATTR_AKM_SUITES]);
|
|
|
|
len = nla_len(info->attrs[NL80211_ATTR_AKM_SUITES]);
|
|
|
|
settings->n_akm_suites = len / sizeof(u32);
|
|
|
|
|
|
|
|
if (len % sizeof(u32))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
memcpy(settings->akm_suites, data, len);
|
|
|
|
|
|
|
|
for (i = 0; i < settings->n_ciphers_pairwise; i++)
|
|
|
|
if (!nl80211_valid_akm_suite(settings->akm_suites[i]))
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-03-19 11:39:22 +00:00
|
|
|
static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2009-07-02 15:20:43 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
2009-03-19 11:39:22 +00:00
|
|
|
struct net_device *dev;
|
2009-07-02 15:20:43 +00:00
|
|
|
struct cfg80211_crypto_settings crypto;
|
cfg80211/mac80211: better channel handling
Currently (all tested with hwsim) you can do stupid
things like setting up an AP on a certain channel,
then adding another virtual interface and making
that associate on another channel -- this will make
the beaconing to move channel but obviously without
the necessary IEs data update.
In order to improve this situation, first make the
configuration APIs (cfg80211 and nl80211) aware of
multi-channel operation -- we'll eventually need
that in the future anyway. There's one userland API
change and one API addition. The API change is that
now SET_WIPHY must be called with virtual interface
index rather than only wiphy index in order to take
effect for that interface -- luckily all current
users (hostapd) do that. For monitor interfaces, the
old setting is preserved, but monitors are always
slaved to other devices anyway so no guarantees.
The second userland API change is the introduction
of a per virtual interface SET_CHANNEL command, that
hostapd should use going forward to make it easier
to understand what's going on (it can automatically
detect a kernel with this command).
Other than mac80211, no existing cfg80211 drivers
are affected by this change because they only allow
a single virtual interface.
mac80211, however, now needs to be aware that the
channel settings are per interface now, and needs
to disallow (for now) real multi-channel operation,
which is another important part of this patch.
One of the immediate benefits is that you can now
start hostapd to operate on a hardware that already
has a connection on another virtual interface, as
long as you specify the same channel.
Note that two things are left unhandled (this is an
improvement -- not a complete fix):
* different HT/no-HT modes
currently you could start an HT AP and then
connect to a non-HT network on the same channel
which would configure the hardware for no HT;
that can be fixed fairly easily
* CSA
An AP we're connected to on a virtual interface
might indicate switching channels, and in that
case we would follow it, regardless of how many
other interfaces are operating; this requires
more effort to fix but is pretty rare after all
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2010-05-05 13:25:02 +00:00
|
|
|
struct ieee80211_channel *chan;
|
2009-07-07 12:37:26 +00:00
|
|
|
const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL;
|
2009-07-02 15:20:43 +00:00
|
|
|
int err, ssid_len, ie_len = 0;
|
|
|
|
bool use_mfp = false;
|
2009-03-19 11:39:22 +00:00
|
|
|
|
2009-03-27 11:40:28 +00:00
|
|
|
if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_MAC] ||
|
2009-07-02 15:20:43 +00:00
|
|
|
!info->attrs[NL80211_ATTR_SSID] ||
|
|
|
|
!info->attrs[NL80211_ATTR_WIPHY_FREQ])
|
2009-03-27 11:40:28 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2009-03-19 11:39:22 +00:00
|
|
|
rtnl_lock();
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
2009-03-19 11:39:22 +00:00
|
|
|
if (err)
|
|
|
|
goto unlock_rtnl;
|
|
|
|
|
2009-07-02 15:20:43 +00:00
|
|
|
if (!rdev->ops->assoc) {
|
2009-03-19 11:39:22 +00:00
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-03-20 19:21:19 +00:00
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-03-20 19:21:18 +00:00
|
|
|
if (!netif_running(dev)) {
|
|
|
|
err = -ENETDOWN;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-07-02 15:20:43 +00:00
|
|
|
bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
2009-03-19 11:39:22 +00:00
|
|
|
|
2009-07-02 15:20:43 +00:00
|
|
|
chan = ieee80211_get_channel(&rdev->wiphy,
|
|
|
|
nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
|
|
|
|
if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED)) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
2009-03-19 11:39:22 +00:00
|
|
|
}
|
|
|
|
|
2009-07-02 15:20:43 +00:00
|
|
|
ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
|
|
|
|
ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
|
2009-03-19 11:39:22 +00:00
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_IE]) {
|
2009-07-02 15:20:43 +00:00
|
|
|
ie = nla_data(info->attrs[NL80211_ATTR_IE]);
|
|
|
|
ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
|
2009-03-19 11:39:22 +00:00
|
|
|
}
|
|
|
|
|
2009-05-06 19:09:37 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_USE_MFP]) {
|
2009-07-07 01:56:10 +00:00
|
|
|
enum nl80211_mfp mfp =
|
2009-05-06 19:09:37 +00:00
|
|
|
nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
|
2009-07-07 01:56:10 +00:00
|
|
|
if (mfp == NL80211_MFP_REQUIRED)
|
2009-07-02 15:20:43 +00:00
|
|
|
use_mfp = true;
|
2009-07-07 01:56:10 +00:00
|
|
|
else if (mfp != NL80211_MFP_NO) {
|
2009-05-06 19:09:37 +00:00
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-07-07 12:37:26 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_PREV_BSSID])
|
|
|
|
prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
|
|
|
|
|
2009-07-02 19:36:37 +00:00
|
|
|
err = nl80211_crypto_settings(info, &crypto, 1);
|
2009-07-01 19:26:54 +00:00
|
|
|
if (!err)
|
2009-07-07 12:37:26 +00:00
|
|
|
err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
|
|
|
|
ssid, ssid_len, ie, ie_len, use_mfp,
|
2009-07-02 15:20:43 +00:00
|
|
|
&crypto);
|
2009-03-19 11:39:22 +00:00
|
|
|
|
|
|
|
out:
|
2009-07-07 01:56:09 +00:00
|
|
|
cfg80211_unlock_rdev(rdev);
|
2009-03-19 11:39:22 +00:00
|
|
|
dev_put(dev);
|
|
|
|
unlock_rtnl:
|
|
|
|
rtnl_unlock();
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2009-07-07 01:56:12 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
2009-03-19 11:39:22 +00:00
|
|
|
struct net_device *dev;
|
2009-07-02 15:20:43 +00:00
|
|
|
const u8 *ie = NULL, *bssid;
|
|
|
|
int err, ie_len = 0;
|
|
|
|
u16 reason_code;
|
2010-04-04 06:37:19 +00:00
|
|
|
bool local_state_change;
|
2009-03-19 11:39:22 +00:00
|
|
|
|
2009-03-27 11:40:28 +00:00
|
|
|
if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_MAC])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_REASON_CODE])
|
|
|
|
return -EINVAL;
|
|
|
|
|
2009-03-19 11:39:22 +00:00
|
|
|
rtnl_lock();
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
2009-03-19 11:39:22 +00:00
|
|
|
if (err)
|
|
|
|
goto unlock_rtnl;
|
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
if (!rdev->ops->deauth) {
|
2009-03-19 11:39:22 +00:00
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-03-20 19:21:19 +00:00
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-03-20 19:21:18 +00:00
|
|
|
if (!netif_running(dev)) {
|
|
|
|
err = -ENETDOWN;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-07-02 15:20:43 +00:00
|
|
|
bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
2009-03-19 11:39:22 +00:00
|
|
|
|
2009-07-02 15:20:43 +00:00
|
|
|
reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
|
|
|
|
if (reason_code == 0) {
|
2009-03-27 11:40:28 +00:00
|
|
|
/* Reason Code 0 is reserved */
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
2009-03-20 19:21:17 +00:00
|
|
|
}
|
2009-03-19 11:39:22 +00:00
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_IE]) {
|
2009-07-02 15:20:43 +00:00
|
|
|
ie = nla_data(info->attrs[NL80211_ATTR_IE]);
|
|
|
|
ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
|
2009-03-19 11:39:22 +00:00
|
|
|
}
|
|
|
|
|
2010-04-04 06:37:19 +00:00
|
|
|
local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
|
|
|
|
|
|
|
|
err = cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code,
|
|
|
|
local_state_change);
|
2009-03-19 11:39:22 +00:00
|
|
|
|
|
|
|
out:
|
2009-07-07 01:56:12 +00:00
|
|
|
cfg80211_unlock_rdev(rdev);
|
2009-03-19 11:39:22 +00:00
|
|
|
dev_put(dev);
|
|
|
|
unlock_rtnl:
|
|
|
|
rtnl_unlock();
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2009-07-07 01:56:12 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
2009-03-19 11:39:22 +00:00
|
|
|
struct net_device *dev;
|
2009-07-02 15:20:43 +00:00
|
|
|
const u8 *ie = NULL, *bssid;
|
|
|
|
int err, ie_len = 0;
|
|
|
|
u16 reason_code;
|
2010-04-04 06:37:19 +00:00
|
|
|
bool local_state_change;
|
2009-03-19 11:39:22 +00:00
|
|
|
|
2009-03-27 11:40:28 +00:00
|
|
|
if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_MAC])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_REASON_CODE])
|
|
|
|
return -EINVAL;
|
|
|
|
|
2009-03-19 11:39:22 +00:00
|
|
|
rtnl_lock();
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
2009-03-19 11:39:22 +00:00
|
|
|
if (err)
|
|
|
|
goto unlock_rtnl;
|
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
if (!rdev->ops->disassoc) {
|
2009-03-19 11:39:22 +00:00
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-03-20 19:21:19 +00:00
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-03-20 19:21:18 +00:00
|
|
|
if (!netif_running(dev)) {
|
|
|
|
err = -ENETDOWN;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-07-02 15:20:43 +00:00
|
|
|
bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
2009-03-19 11:39:22 +00:00
|
|
|
|
2009-07-02 15:20:43 +00:00
|
|
|
reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
|
|
|
|
if (reason_code == 0) {
|
2009-03-27 11:40:28 +00:00
|
|
|
/* Reason Code 0 is reserved */
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
2009-03-20 19:21:17 +00:00
|
|
|
}
|
2009-03-19 11:39:22 +00:00
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_IE]) {
|
2009-07-02 15:20:43 +00:00
|
|
|
ie = nla_data(info->attrs[NL80211_ATTR_IE]);
|
|
|
|
ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
|
2009-03-19 11:39:22 +00:00
|
|
|
}
|
|
|
|
|
2010-04-04 06:37:19 +00:00
|
|
|
local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
|
|
|
|
|
|
|
|
err = cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code,
|
|
|
|
local_state_change);
|
2009-03-19 11:39:22 +00:00
|
|
|
|
|
|
|
out:
|
2009-07-07 01:56:12 +00:00
|
|
|
cfg80211_unlock_rdev(rdev);
|
2009-03-19 11:39:22 +00:00
|
|
|
dev_put(dev);
|
|
|
|
unlock_rtnl:
|
|
|
|
rtnl_unlock();
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2009-04-19 19:24:32 +00:00
|
|
|
static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2009-07-07 01:56:12 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
2009-04-19 19:24:32 +00:00
|
|
|
struct net_device *dev;
|
|
|
|
struct cfg80211_ibss_params ibss;
|
|
|
|
struct wiphy *wiphy;
|
cfg80211: rework key operation
This reworks the key operation in cfg80211, and now only
allows, from userspace, configuring keys (via nl80211)
after the connection has been established (in managed
mode), the IBSS been joined (in IBSS mode), at any time
(in AP[_VLAN] modes) or never for all the other modes.
In order to do shared key authentication correctly, it
is now possible to give a WEP key to the AUTH command.
To configure static WEP keys, these are given to the
CONNECT or IBSS_JOIN command directly, for a userspace
SME it is assumed it will configure it properly after
the connection has been established.
Since mac80211 used to check the default key in IBSS
mode to see whether or not the network is protected,
it needs an update in that area, as well as an update
to make use of the WEP key passed to auth() for shared
key authentication.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-07-08 12:22:54 +00:00
|
|
|
struct cfg80211_cached_keys *connkeys = NULL;
|
2009-04-19 19:24:32 +00:00
|
|
|
int err;
|
|
|
|
|
2009-04-22 15:45:38 +00:00
|
|
|
memset(&ibss, 0, sizeof(ibss));
|
|
|
|
|
2009-04-19 19:24:32 +00:00
|
|
|
if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
|
|
|
|
!info->attrs[NL80211_ATTR_SSID] ||
|
|
|
|
!nla_len(info->attrs[NL80211_ATTR_SSID]))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2009-04-22 15:45:38 +00:00
|
|
|
ibss.beacon_interval = 100;
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
|
|
|
|
ibss.beacon_interval =
|
|
|
|
nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
|
|
|
|
if (ibss.beacon_interval < 1 || ibss.beacon_interval > 10000)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2009-04-19 19:24:32 +00:00
|
|
|
rtnl_lock();
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
2009-04-19 19:24:32 +00:00
|
|
|
if (err)
|
|
|
|
goto unlock_rtnl;
|
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
if (!rdev->ops->join_ibss) {
|
2009-04-19 19:24:32 +00:00
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!netif_running(dev)) {
|
|
|
|
err = -ENETDOWN;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
wiphy = &rdev->wiphy;
|
2009-04-19 19:24:32 +00:00
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_MAC])
|
|
|
|
ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
|
|
|
ibss.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
|
|
|
|
ibss.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_IE]) {
|
|
|
|
ibss.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
|
|
|
|
ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
|
|
|
|
}
|
|
|
|
|
|
|
|
ibss.channel = ieee80211_get_channel(wiphy,
|
|
|
|
nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
|
|
|
|
if (!ibss.channel ||
|
|
|
|
ibss.channel->flags & IEEE80211_CHAN_NO_IBSS ||
|
|
|
|
ibss.channel->flags & IEEE80211_CHAN_DISABLED) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
|
cfg80211: rework key operation
This reworks the key operation in cfg80211, and now only
allows, from userspace, configuring keys (via nl80211)
after the connection has been established (in managed
mode), the IBSS been joined (in IBSS mode), at any time
(in AP[_VLAN] modes) or never for all the other modes.
In order to do shared key authentication correctly, it
is now possible to give a WEP key to the AUTH command.
To configure static WEP keys, these are given to the
CONNECT or IBSS_JOIN command directly, for a userspace
SME it is assumed it will configure it properly after
the connection has been established.
Since mac80211 used to check the default key in IBSS
mode to see whether or not the network is protected,
it needs an update in that area, as well as an update
to make use of the WEP key passed to auth() for shared
key authentication.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-07-08 12:22:54 +00:00
|
|
|
ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
|
|
|
|
|
|
|
|
if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) {
|
|
|
|
connkeys = nl80211_parse_connkeys(rdev,
|
|
|
|
info->attrs[NL80211_ATTR_KEYS]);
|
|
|
|
if (IS_ERR(connkeys)) {
|
|
|
|
err = PTR_ERR(connkeys);
|
|
|
|
connkeys = NULL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
2009-04-19 19:24:32 +00:00
|
|
|
|
2010-06-14 09:55:31 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
|
|
|
|
u8 *rates =
|
|
|
|
nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
|
|
|
|
int n_rates =
|
|
|
|
nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
|
|
|
|
struct ieee80211_supported_band *sband =
|
|
|
|
wiphy->bands[ibss.channel->band];
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
if (n_rates == 0) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < n_rates; i++) {
|
|
|
|
int rate = (rates[i] & 0x7f) * 5;
|
|
|
|
bool found = false;
|
|
|
|
|
|
|
|
for (j = 0; j < sband->n_bitrates; j++) {
|
|
|
|
if (sband->bitrates[j].bitrate == rate) {
|
|
|
|
found = true;
|
|
|
|
ibss.basic_rates |= BIT(j);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!found) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* If no rates were explicitly configured,
|
|
|
|
* use the mandatory rate set for 11b or
|
|
|
|
* 11a for maximum compatibility.
|
|
|
|
*/
|
|
|
|
struct ieee80211_supported_band *sband =
|
|
|
|
wiphy->bands[ibss.channel->band];
|
|
|
|
int j;
|
|
|
|
u32 flag = ibss.channel->band == IEEE80211_BAND_5GHZ ?
|
|
|
|
IEEE80211_RATE_MANDATORY_A :
|
|
|
|
IEEE80211_RATE_MANDATORY_B;
|
|
|
|
|
|
|
|
for (j = 0; j < sband->n_bitrates; j++) {
|
|
|
|
if (sband->bitrates[j].flags & flag)
|
|
|
|
ibss.basic_rates |= BIT(j);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
cfg80211: rework key operation
This reworks the key operation in cfg80211, and now only
allows, from userspace, configuring keys (via nl80211)
after the connection has been established (in managed
mode), the IBSS been joined (in IBSS mode), at any time
(in AP[_VLAN] modes) or never for all the other modes.
In order to do shared key authentication correctly, it
is now possible to give a WEP key to the AUTH command.
To configure static WEP keys, these are given to the
CONNECT or IBSS_JOIN command directly, for a userspace
SME it is assumed it will configure it properly after
the connection has been established.
Since mac80211 used to check the default key in IBSS
mode to see whether or not the network is protected,
it needs an update in that area, as well as an update
to make use of the WEP key passed to auth() for shared
key authentication.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-07-08 12:22:54 +00:00
|
|
|
err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
|
2009-04-19 19:24:32 +00:00
|
|
|
|
|
|
|
out:
|
2009-07-07 01:56:12 +00:00
|
|
|
cfg80211_unlock_rdev(rdev);
|
2009-04-19 19:24:32 +00:00
|
|
|
dev_put(dev);
|
|
|
|
unlock_rtnl:
|
cfg80211: rework key operation
This reworks the key operation in cfg80211, and now only
allows, from userspace, configuring keys (via nl80211)
after the connection has been established (in managed
mode), the IBSS been joined (in IBSS mode), at any time
(in AP[_VLAN] modes) or never for all the other modes.
In order to do shared key authentication correctly, it
is now possible to give a WEP key to the AUTH command.
To configure static WEP keys, these are given to the
CONNECT or IBSS_JOIN command directly, for a userspace
SME it is assumed it will configure it properly after
the connection has been established.
Since mac80211 used to check the default key in IBSS
mode to see whether or not the network is protected,
it needs an update in that area, as well as an update
to make use of the WEP key passed to auth() for shared
key authentication.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-07-08 12:22:54 +00:00
|
|
|
if (err)
|
|
|
|
kfree(connkeys);
|
2009-04-19 19:24:32 +00:00
|
|
|
rtnl_unlock();
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2009-07-07 01:56:12 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
2009-04-19 19:24:32 +00:00
|
|
|
struct net_device *dev;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
rtnl_lock();
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
2009-04-19 19:24:32 +00:00
|
|
|
if (err)
|
|
|
|
goto unlock_rtnl;
|
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
if (!rdev->ops->leave_ibss) {
|
2009-04-19 19:24:32 +00:00
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!netif_running(dev)) {
|
|
|
|
err = -ENETDOWN;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
err = cfg80211_leave_ibss(rdev, dev, false);
|
2009-04-19 19:24:32 +00:00
|
|
|
|
|
|
|
out:
|
2009-07-07 01:56:12 +00:00
|
|
|
cfg80211_unlock_rdev(rdev);
|
2009-04-19 19:24:32 +00:00
|
|
|
dev_put(dev);
|
|
|
|
unlock_rtnl:
|
|
|
|
rtnl_unlock();
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2009-07-01 19:26:51 +00:00
|
|
|
#ifdef CONFIG_NL80211_TESTMODE
|
|
|
|
static struct genl_multicast_group nl80211_testmode_mcgrp = {
|
|
|
|
.name = "testmode",
|
|
|
|
};
|
|
|
|
|
|
|
|
static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_TESTDATA])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
rtnl_lock();
|
|
|
|
|
|
|
|
rdev = cfg80211_get_dev_from_info(info);
|
|
|
|
if (IS_ERR(rdev)) {
|
|
|
|
err = PTR_ERR(rdev);
|
|
|
|
goto unlock_rtnl;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
if (rdev->ops->testmode_cmd) {
|
|
|
|
rdev->testmode_info = info;
|
|
|
|
err = rdev->ops->testmode_cmd(&rdev->wiphy,
|
|
|
|
nla_data(info->attrs[NL80211_ATTR_TESTDATA]),
|
|
|
|
nla_len(info->attrs[NL80211_ATTR_TESTDATA]));
|
|
|
|
rdev->testmode_info = NULL;
|
|
|
|
}
|
|
|
|
|
2009-07-07 01:56:09 +00:00
|
|
|
cfg80211_unlock_rdev(rdev);
|
2009-07-01 19:26:51 +00:00
|
|
|
|
|
|
|
unlock_rtnl:
|
|
|
|
rtnl_unlock();
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct sk_buff *
|
|
|
|
__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev,
|
|
|
|
int approxlen, u32 pid, u32 seq, gfp_t gfp)
|
|
|
|
{
|
|
|
|
struct sk_buff *skb;
|
|
|
|
void *hdr;
|
|
|
|
struct nlattr *data;
|
|
|
|
|
|
|
|
skb = nlmsg_new(approxlen + 100, gfp);
|
|
|
|
if (!skb)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(skb, pid, seq, 0, NL80211_CMD_TESTMODE);
|
|
|
|
if (!hdr) {
|
|
|
|
kfree_skb(skb);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
NLA_PUT_U32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
|
|
|
|
data = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
|
|
|
|
|
|
|
|
((void **)skb->cb)[0] = rdev;
|
|
|
|
((void **)skb->cb)[1] = hdr;
|
|
|
|
((void **)skb->cb)[2] = data;
|
|
|
|
|
|
|
|
return skb;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
kfree_skb(skb);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy,
|
|
|
|
int approxlen)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
|
|
|
|
|
|
|
|
if (WARN_ON(!rdev->testmode_info))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return __cfg80211_testmode_alloc_skb(rdev, approxlen,
|
|
|
|
rdev->testmode_info->snd_pid,
|
|
|
|
rdev->testmode_info->snd_seq,
|
|
|
|
GFP_KERNEL);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(cfg80211_testmode_alloc_reply_skb);
|
|
|
|
|
|
|
|
int cfg80211_testmode_reply(struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
|
|
|
|
void *hdr = ((void **)skb->cb)[1];
|
|
|
|
struct nlattr *data = ((void **)skb->cb)[2];
|
|
|
|
|
|
|
|
if (WARN_ON(!rdev->testmode_info)) {
|
|
|
|
kfree_skb(skb);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
nla_nest_end(skb, data);
|
|
|
|
genlmsg_end(skb, hdr);
|
|
|
|
return genlmsg_reply(skb, rdev->testmode_info);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(cfg80211_testmode_reply);
|
|
|
|
|
|
|
|
struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy,
|
|
|
|
int approxlen, gfp_t gfp)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
|
|
|
|
|
|
|
|
return __cfg80211_testmode_alloc_skb(rdev, approxlen, 0, 0, gfp);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb);
|
|
|
|
|
|
|
|
void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
|
|
|
|
{
|
|
|
|
void *hdr = ((void **)skb->cb)[1];
|
|
|
|
struct nlattr *data = ((void **)skb->cb)[2];
|
|
|
|
|
|
|
|
nla_nest_end(skb, data);
|
|
|
|
genlmsg_end(skb, hdr);
|
|
|
|
genlmsg_multicast(skb, 0, nl80211_testmode_mcgrp.id, gfp);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(cfg80211_testmode_event);
|
|
|
|
#endif
|
|
|
|
|
2009-07-01 19:26:54 +00:00
|
|
|
static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2009-07-07 01:56:12 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
2009-07-01 19:26:54 +00:00
|
|
|
struct net_device *dev;
|
|
|
|
struct cfg80211_connect_params connect;
|
|
|
|
struct wiphy *wiphy;
|
cfg80211: rework key operation
This reworks the key operation in cfg80211, and now only
allows, from userspace, configuring keys (via nl80211)
after the connection has been established (in managed
mode), the IBSS been joined (in IBSS mode), at any time
(in AP[_VLAN] modes) or never for all the other modes.
In order to do shared key authentication correctly, it
is now possible to give a WEP key to the AUTH command.
To configure static WEP keys, these are given to the
CONNECT or IBSS_JOIN command directly, for a userspace
SME it is assumed it will configure it properly after
the connection has been established.
Since mac80211 used to check the default key in IBSS
mode to see whether or not the network is protected,
it needs an update in that area, as well as an update
to make use of the WEP key passed to auth() for shared
key authentication.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-07-08 12:22:54 +00:00
|
|
|
struct cfg80211_cached_keys *connkeys = NULL;
|
2009-07-01 19:26:54 +00:00
|
|
|
int err;
|
|
|
|
|
|
|
|
memset(&connect, 0, sizeof(connect));
|
|
|
|
|
|
|
|
if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_SSID] ||
|
|
|
|
!nla_len(info->attrs[NL80211_ATTR_SSID]))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
|
|
|
|
connect.auth_type =
|
|
|
|
nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
|
|
|
|
if (!nl80211_valid_auth_type(connect.auth_type))
|
|
|
|
return -EINVAL;
|
|
|
|
} else
|
|
|
|
connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
|
|
|
|
|
|
|
|
connect.privacy = info->attrs[NL80211_ATTR_PRIVACY];
|
|
|
|
|
2009-07-02 19:36:37 +00:00
|
|
|
err = nl80211_crypto_settings(info, &connect.crypto,
|
|
|
|
NL80211_MAX_NR_CIPHER_SUITES);
|
2009-07-01 19:26:54 +00:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
rtnl_lock();
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
2009-07-01 19:26:54 +00:00
|
|
|
if (err)
|
|
|
|
goto unlock_rtnl;
|
|
|
|
|
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!netif_running(dev)) {
|
|
|
|
err = -ENETDOWN;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
wiphy = &rdev->wiphy;
|
2009-07-01 19:26:54 +00:00
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_MAC])
|
|
|
|
connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
|
|
|
connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
|
|
|
|
connect.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_IE]) {
|
|
|
|
connect.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
|
|
|
|
connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
|
|
|
|
connect.channel =
|
|
|
|
ieee80211_get_channel(wiphy,
|
|
|
|
nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
|
|
|
|
if (!connect.channel ||
|
|
|
|
connect.channel->flags & IEEE80211_CHAN_DISABLED) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
cfg80211: rework key operation
This reworks the key operation in cfg80211, and now only
allows, from userspace, configuring keys (via nl80211)
after the connection has been established (in managed
mode), the IBSS been joined (in IBSS mode), at any time
(in AP[_VLAN] modes) or never for all the other modes.
In order to do shared key authentication correctly, it
is now possible to give a WEP key to the AUTH command.
To configure static WEP keys, these are given to the
CONNECT or IBSS_JOIN command directly, for a userspace
SME it is assumed it will configure it properly after
the connection has been established.
Since mac80211 used to check the default key in IBSS
mode to see whether or not the network is protected,
it needs an update in that area, as well as an update
to make use of the WEP key passed to auth() for shared
key authentication.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-07-08 12:22:54 +00:00
|
|
|
if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) {
|
|
|
|
connkeys = nl80211_parse_connkeys(rdev,
|
|
|
|
info->attrs[NL80211_ATTR_KEYS]);
|
|
|
|
if (IS_ERR(connkeys)) {
|
|
|
|
err = PTR_ERR(connkeys);
|
|
|
|
connkeys = NULL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
err = cfg80211_connect(rdev, dev, &connect, connkeys);
|
2009-07-01 19:26:54 +00:00
|
|
|
|
|
|
|
out:
|
2009-07-07 01:56:12 +00:00
|
|
|
cfg80211_unlock_rdev(rdev);
|
2009-07-01 19:26:54 +00:00
|
|
|
dev_put(dev);
|
|
|
|
unlock_rtnl:
|
cfg80211: rework key operation
This reworks the key operation in cfg80211, and now only
allows, from userspace, configuring keys (via nl80211)
after the connection has been established (in managed
mode), the IBSS been joined (in IBSS mode), at any time
(in AP[_VLAN] modes) or never for all the other modes.
In order to do shared key authentication correctly, it
is now possible to give a WEP key to the AUTH command.
To configure static WEP keys, these are given to the
CONNECT or IBSS_JOIN command directly, for a userspace
SME it is assumed it will configure it properly after
the connection has been established.
Since mac80211 used to check the default key in IBSS
mode to see whether or not the network is protected,
it needs an update in that area, as well as an update
to make use of the WEP key passed to auth() for shared
key authentication.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-07-08 12:22:54 +00:00
|
|
|
if (err)
|
|
|
|
kfree(connkeys);
|
2009-07-01 19:26:54 +00:00
|
|
|
rtnl_unlock();
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2009-07-07 01:56:12 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
2009-07-01 19:26:54 +00:00
|
|
|
struct net_device *dev;
|
|
|
|
int err;
|
|
|
|
u16 reason;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_REASON_CODE])
|
|
|
|
reason = WLAN_REASON_DEAUTH_LEAVING;
|
|
|
|
else
|
|
|
|
reason = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
|
|
|
|
|
|
|
|
if (reason == 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
rtnl_lock();
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
2009-07-01 19:26:54 +00:00
|
|
|
if (err)
|
|
|
|
goto unlock_rtnl;
|
|
|
|
|
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!netif_running(dev)) {
|
|
|
|
err = -ENETDOWN;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
err = cfg80211_disconnect(rdev, dev, reason, true);
|
2009-07-01 19:26:54 +00:00
|
|
|
|
|
|
|
out:
|
2009-07-07 01:56:12 +00:00
|
|
|
cfg80211_unlock_rdev(rdev);
|
2009-07-01 19:26:54 +00:00
|
|
|
dev_put(dev);
|
|
|
|
unlock_rtnl:
|
|
|
|
rtnl_unlock();
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev;
|
|
|
|
struct net *net;
|
|
|
|
int err;
|
|
|
|
u32 pid;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_PID])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
pid = nla_get_u32(info->attrs[NL80211_ATTR_PID]);
|
|
|
|
|
|
|
|
rtnl_lock();
|
|
|
|
|
|
|
|
rdev = cfg80211_get_dev_from_info(info);
|
|
|
|
if (IS_ERR(rdev)) {
|
|
|
|
err = PTR_ERR(rdev);
|
2009-10-08 19:02:02 +00:00
|
|
|
goto out_rtnl;
|
2009-07-13 22:33:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
net = get_net_ns_by_pid(pid);
|
|
|
|
if (IS_ERR(net)) {
|
|
|
|
err = PTR_ERR(net);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
/* check if anything to do */
|
|
|
|
if (net_eq(wiphy_net(&rdev->wiphy), net))
|
|
|
|
goto out_put_net;
|
|
|
|
|
|
|
|
err = cfg80211_switch_netns(rdev, net);
|
|
|
|
out_put_net:
|
|
|
|
put_net(net);
|
|
|
|
out:
|
|
|
|
cfg80211_unlock_rdev(rdev);
|
2009-10-08 19:02:02 +00:00
|
|
|
out_rtnl:
|
2009-07-13 22:33:35 +00:00
|
|
|
rtnl_unlock();
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2009-11-24 22:59:15 +00:00
|
|
|
static int nl80211_setdel_pmksa(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev;
|
|
|
|
int (*rdev_ops)(struct wiphy *wiphy, struct net_device *dev,
|
|
|
|
struct cfg80211_pmksa *pmksa) = NULL;
|
|
|
|
int err;
|
|
|
|
struct net_device *dev;
|
|
|
|
struct cfg80211_pmksa pmksa;
|
|
|
|
|
|
|
|
memset(&pmksa, 0, sizeof(struct cfg80211_pmksa));
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_MAC])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_PMKID])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
rtnl_lock();
|
|
|
|
|
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
|
|
|
if (err)
|
|
|
|
goto out_rtnl;
|
|
|
|
|
|
|
|
pmksa.pmkid = nla_data(info->attrs[NL80211_ATTR_PMKID]);
|
|
|
|
pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
|
|
|
|
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (info->genlhdr->cmd) {
|
|
|
|
case NL80211_CMD_SET_PMKSA:
|
|
|
|
rdev_ops = rdev->ops->set_pmksa;
|
|
|
|
break;
|
|
|
|
case NL80211_CMD_DEL_PMKSA:
|
|
|
|
rdev_ops = rdev->ops->del_pmksa;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
WARN_ON(1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!rdev_ops) {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = rdev_ops(&rdev->wiphy, dev, &pmksa);
|
|
|
|
|
|
|
|
out:
|
|
|
|
cfg80211_unlock_rdev(rdev);
|
|
|
|
dev_put(dev);
|
|
|
|
out_rtnl:
|
|
|
|
rtnl_unlock();
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev;
|
|
|
|
int err;
|
|
|
|
struct net_device *dev;
|
|
|
|
|
|
|
|
rtnl_lock();
|
|
|
|
|
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
|
|
|
if (err)
|
|
|
|
goto out_rtnl;
|
|
|
|
|
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!rdev->ops->flush_pmksa) {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = rdev->ops->flush_pmksa(&rdev->wiphy, dev);
|
|
|
|
|
|
|
|
out:
|
|
|
|
cfg80211_unlock_rdev(rdev);
|
|
|
|
dev_put(dev);
|
|
|
|
out_rtnl:
|
|
|
|
rtnl_unlock();
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2009-12-23 12:15:41 +00:00
|
|
|
static int nl80211_remain_on_channel(struct sk_buff *skb,
|
|
|
|
struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev;
|
|
|
|
struct net_device *dev;
|
|
|
|
struct ieee80211_channel *chan;
|
|
|
|
struct sk_buff *msg;
|
|
|
|
void *hdr;
|
|
|
|
u64 cookie;
|
|
|
|
enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
|
|
|
|
u32 freq, duration;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
|
|
|
|
!info->attrs[NL80211_ATTR_DURATION])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
duration = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We should be on that channel for at least one jiffie,
|
|
|
|
* and more than 5 seconds seems excessive.
|
|
|
|
*/
|
|
|
|
if (!duration || !msecs_to_jiffies(duration) || duration > 5000)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
rtnl_lock();
|
|
|
|
|
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
|
|
|
if (err)
|
|
|
|
goto unlock_rtnl;
|
|
|
|
|
|
|
|
if (!rdev->ops->remain_on_channel) {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!netif_running(dev)) {
|
|
|
|
err = -ENETDOWN;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
|
|
|
|
channel_type = nla_get_u32(
|
|
|
|
info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
|
|
|
|
if (channel_type != NL80211_CHAN_NO_HT &&
|
|
|
|
channel_type != NL80211_CHAN_HT20 &&
|
|
|
|
channel_type != NL80211_CHAN_HT40PLUS &&
|
2010-05-18 12:36:34 +00:00
|
|
|
channel_type != NL80211_CHAN_HT40MINUS) {
|
2009-12-23 12:15:41 +00:00
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
2010-05-18 12:36:34 +00:00
|
|
|
}
|
2009-12-23 12:15:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
|
|
|
|
chan = rdev_freq_to_chan(rdev, freq, channel_type);
|
|
|
|
if (chan == NULL) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
|
|
if (!msg) {
|
|
|
|
err = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
|
|
|
|
NL80211_CMD_REMAIN_ON_CHANNEL);
|
|
|
|
|
|
|
|
if (IS_ERR(hdr)) {
|
|
|
|
err = PTR_ERR(hdr);
|
|
|
|
goto free_msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = rdev->ops->remain_on_channel(&rdev->wiphy, dev, chan,
|
|
|
|
channel_type, duration, &cookie);
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
goto free_msg;
|
|
|
|
|
|
|
|
NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
|
|
|
|
|
|
|
|
genlmsg_end(msg, hdr);
|
|
|
|
err = genlmsg_reply(msg, info);
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
err = -ENOBUFS;
|
|
|
|
free_msg:
|
|
|
|
nlmsg_free(msg);
|
|
|
|
out:
|
|
|
|
cfg80211_unlock_rdev(rdev);
|
|
|
|
dev_put(dev);
|
|
|
|
unlock_rtnl:
|
|
|
|
rtnl_unlock();
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_cancel_remain_on_channel(struct sk_buff *skb,
|
|
|
|
struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev;
|
|
|
|
struct net_device *dev;
|
|
|
|
u64 cookie;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_COOKIE])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
rtnl_lock();
|
|
|
|
|
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
|
|
|
if (err)
|
|
|
|
goto unlock_rtnl;
|
|
|
|
|
|
|
|
if (!rdev->ops->cancel_remain_on_channel) {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!netif_running(dev)) {
|
|
|
|
err = -ENETDOWN;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
|
|
|
|
|
|
|
|
err = rdev->ops->cancel_remain_on_channel(&rdev->wiphy, dev, cookie);
|
|
|
|
|
|
|
|
out:
|
|
|
|
cfg80211_unlock_rdev(rdev);
|
|
|
|
dev_put(dev);
|
|
|
|
unlock_rtnl:
|
|
|
|
rtnl_unlock();
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2009-12-29 10:59:45 +00:00
|
|
|
static u32 rateset_to_mask(struct ieee80211_supported_band *sband,
|
|
|
|
u8 *rates, u8 rates_len)
|
|
|
|
{
|
|
|
|
u8 i;
|
|
|
|
u32 mask = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < rates_len; i++) {
|
|
|
|
int rate = (rates[i] & 0x7f) * 5;
|
|
|
|
int ridx;
|
|
|
|
for (ridx = 0; ridx < sband->n_bitrates; ridx++) {
|
|
|
|
struct ieee80211_rate *srate =
|
|
|
|
&sband->bitrates[ridx];
|
|
|
|
if (rate == srate->bitrate) {
|
|
|
|
mask |= 1 << ridx;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ridx == sband->n_bitrates)
|
|
|
|
return 0; /* rate not found */
|
|
|
|
}
|
|
|
|
|
|
|
|
return mask;
|
|
|
|
}
|
|
|
|
|
2010-02-18 08:14:31 +00:00
|
|
|
static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = {
|
2009-12-29 10:59:45 +00:00
|
|
|
[NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY,
|
|
|
|
.len = NL80211_MAX_SUPP_RATES },
|
|
|
|
};
|
|
|
|
|
|
|
|
static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
|
|
|
|
struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct nlattr *tb[NL80211_TXRATE_MAX + 1];
|
|
|
|
struct cfg80211_registered_device *rdev;
|
|
|
|
struct cfg80211_bitrate_mask mask;
|
|
|
|
int err, rem, i;
|
|
|
|
struct net_device *dev;
|
|
|
|
struct nlattr *tx_rates;
|
|
|
|
struct ieee80211_supported_band *sband;
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_TX_RATES] == NULL)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
rtnl_lock();
|
|
|
|
|
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
|
|
|
if (err)
|
|
|
|
goto unlock_rtnl;
|
|
|
|
|
|
|
|
if (!rdev->ops->set_bitrate_mask) {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(&mask, 0, sizeof(mask));
|
|
|
|
/* Default to all rates enabled */
|
|
|
|
for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
|
|
|
|
sband = rdev->wiphy.bands[i];
|
|
|
|
mask.control[i].legacy =
|
|
|
|
sband ? (1 << sband->n_bitrates) - 1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The nested attribute uses enum nl80211_band as the index. This maps
|
|
|
|
* directly to the enum ieee80211_band values used in cfg80211.
|
|
|
|
*/
|
|
|
|
nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem)
|
|
|
|
{
|
|
|
|
enum ieee80211_band band = nla_type(tx_rates);
|
|
|
|
if (band < 0 || band >= IEEE80211_NUM_BANDS) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
sband = rdev->wiphy.bands[band];
|
|
|
|
if (sband == NULL) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates),
|
|
|
|
nla_len(tx_rates), nl80211_txattr_policy);
|
|
|
|
if (tb[NL80211_TXRATE_LEGACY]) {
|
|
|
|
mask.control[band].legacy = rateset_to_mask(
|
|
|
|
sband,
|
|
|
|
nla_data(tb[NL80211_TXRATE_LEGACY]),
|
|
|
|
nla_len(tb[NL80211_TXRATE_LEGACY]));
|
|
|
|
if (mask.control[band].legacy == 0) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
err = rdev->ops->set_bitrate_mask(&rdev->wiphy, dev, NULL, &mask);
|
|
|
|
|
|
|
|
unlock:
|
|
|
|
dev_put(dev);
|
|
|
|
cfg80211_unlock_rdev(rdev);
|
|
|
|
unlock_rtnl:
|
|
|
|
rtnl_unlock();
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2010-02-15 10:53:10 +00:00
|
|
|
static int nl80211_register_action(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev;
|
|
|
|
struct net_device *dev;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_FRAME_MATCH])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH]) < 1)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
rtnl_lock();
|
|
|
|
|
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
|
|
|
if (err)
|
|
|
|
goto unlock_rtnl;
|
|
|
|
|
2010-06-09 15:20:33 +00:00
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
|
|
|
|
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) {
|
2010-02-15 10:53:10 +00:00
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* not much point in registering if we can't reply */
|
|
|
|
if (!rdev->ops->action) {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = cfg80211_mlme_register_action(dev->ieee80211_ptr, info->snd_pid,
|
|
|
|
nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]),
|
|
|
|
nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH]));
|
|
|
|
out:
|
|
|
|
cfg80211_unlock_rdev(rdev);
|
|
|
|
dev_put(dev);
|
|
|
|
unlock_rtnl:
|
|
|
|
rtnl_unlock();
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_action(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev;
|
|
|
|
struct net_device *dev;
|
|
|
|
struct ieee80211_channel *chan;
|
|
|
|
enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
|
2010-05-19 10:17:12 +00:00
|
|
|
bool channel_type_valid = false;
|
2010-02-15 10:53:10 +00:00
|
|
|
u32 freq;
|
|
|
|
int err;
|
|
|
|
void *hdr;
|
|
|
|
u64 cookie;
|
|
|
|
struct sk_buff *msg;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_FRAME] ||
|
|
|
|
!info->attrs[NL80211_ATTR_WIPHY_FREQ])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
rtnl_lock();
|
|
|
|
|
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
|
|
|
if (err)
|
|
|
|
goto unlock_rtnl;
|
|
|
|
|
|
|
|
if (!rdev->ops->action) {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-06-09 15:20:33 +00:00
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
|
|
|
|
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) {
|
2010-02-15 10:53:10 +00:00
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!netif_running(dev)) {
|
|
|
|
err = -ENETDOWN;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
|
|
|
|
channel_type = nla_get_u32(
|
|
|
|
info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
|
|
|
|
if (channel_type != NL80211_CHAN_NO_HT &&
|
|
|
|
channel_type != NL80211_CHAN_HT20 &&
|
|
|
|
channel_type != NL80211_CHAN_HT40PLUS &&
|
2010-05-18 12:36:34 +00:00
|
|
|
channel_type != NL80211_CHAN_HT40MINUS) {
|
2010-02-15 10:53:10 +00:00
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
2010-05-18 12:36:34 +00:00
|
|
|
}
|
2010-05-19 10:17:12 +00:00
|
|
|
channel_type_valid = true;
|
2010-02-15 10:53:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
|
|
|
|
chan = rdev_freq_to_chan(rdev, freq, channel_type);
|
|
|
|
if (chan == NULL) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
|
|
if (!msg) {
|
|
|
|
err = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
|
|
|
|
NL80211_CMD_ACTION);
|
|
|
|
|
|
|
|
if (IS_ERR(hdr)) {
|
|
|
|
err = PTR_ERR(hdr);
|
|
|
|
goto free_msg;
|
|
|
|
}
|
|
|
|
err = cfg80211_mlme_action(rdev, dev, chan, channel_type,
|
2010-05-19 10:17:12 +00:00
|
|
|
channel_type_valid,
|
2010-02-15 10:53:10 +00:00
|
|
|
nla_data(info->attrs[NL80211_ATTR_FRAME]),
|
|
|
|
nla_len(info->attrs[NL80211_ATTR_FRAME]),
|
|
|
|
&cookie);
|
|
|
|
if (err)
|
|
|
|
goto free_msg;
|
|
|
|
|
|
|
|
NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
|
|
|
|
|
|
|
|
genlmsg_end(msg, hdr);
|
|
|
|
err = genlmsg_reply(msg, info);
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
err = -ENOBUFS;
|
|
|
|
free_msg:
|
|
|
|
nlmsg_free(msg);
|
|
|
|
out:
|
|
|
|
cfg80211_unlock_rdev(rdev);
|
|
|
|
dev_put(dev);
|
|
|
|
unlock_rtnl:
|
|
|
|
rtnl_unlock();
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
nl80211: add power save commands
The most needed command from nl80211, which Wireless Extensions had,
is support for power save mode. Add a simple command to make it possible
to enable and disable power save via nl80211.
I was also planning about extending the interface, for example adding the
timeout value, but after thinking more about this I decided not to do it.
Basically there were three reasons:
Firstly, the parameters for power save are very much hardware dependent.
Trying to find a unified interface which would work with all hardware, and
still make sense to users, will be very difficult.
Secondly, IEEE 802.11 power save implementation in Linux is still in state
of flux. We have a long way to still to go and there is no way to predict
what kind of implementation we will have after few years. And because we
need to support nl80211 interface a long time, practically forever, adding
now parameters to nl80211 might create maintenance problems later on.
Third issue are the users. Power save parameters are mostly used for
debugging, so debugfs is better, more flexible, interface for this.
For example, wpa_supplicant currently doesn't configure anything related
to power save mode. It's better to strive that kernel can automatically
optimise the power save parameters, like with help of pm qos network
and other traffic parameters.
Later on, when we have better understanding of power save, we can extend
this command with more features, if there's a need for that.
Signed-off-by: Kalle Valo <kalle.valo@nokia.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2010-02-17 15:58:10 +00:00
|
|
|
static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev;
|
|
|
|
struct wireless_dev *wdev;
|
|
|
|
struct net_device *dev;
|
|
|
|
u8 ps_state;
|
|
|
|
bool state;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_PS_STATE]) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ps_state = nla_get_u32(info->attrs[NL80211_ATTR_PS_STATE]);
|
|
|
|
|
|
|
|
if (ps_state != NL80211_PS_DISABLED && ps_state != NL80211_PS_ENABLED) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
rtnl_lock();
|
|
|
|
|
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
|
|
|
if (err)
|
|
|
|
goto unlock_rdev;
|
|
|
|
|
|
|
|
wdev = dev->ieee80211_ptr;
|
|
|
|
|
|
|
|
if (!rdev->ops->set_power_mgmt) {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto unlock_rdev;
|
|
|
|
}
|
|
|
|
|
|
|
|
state = (ps_state == NL80211_PS_ENABLED) ? true : false;
|
|
|
|
|
|
|
|
if (state == wdev->ps)
|
|
|
|
goto unlock_rdev;
|
|
|
|
|
|
|
|
wdev->ps = state;
|
|
|
|
|
|
|
|
if (rdev->ops->set_power_mgmt(wdev->wiphy, dev, wdev->ps,
|
|
|
|
wdev->ps_timeout))
|
|
|
|
/* assume this means it's off */
|
|
|
|
wdev->ps = false;
|
|
|
|
|
|
|
|
unlock_rdev:
|
|
|
|
cfg80211_unlock_rdev(rdev);
|
|
|
|
dev_put(dev);
|
|
|
|
rtnl_unlock();
|
|
|
|
|
|
|
|
out:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev;
|
|
|
|
enum nl80211_ps_state ps_state;
|
|
|
|
struct wireless_dev *wdev;
|
|
|
|
struct net_device *dev;
|
|
|
|
struct sk_buff *msg;
|
|
|
|
void *hdr;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
rtnl_lock();
|
|
|
|
|
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
|
|
|
if (err)
|
|
|
|
goto unlock_rtnl;
|
|
|
|
|
|
|
|
wdev = dev->ieee80211_ptr;
|
|
|
|
|
|
|
|
if (!rdev->ops->set_power_mgmt) {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
|
|
if (!msg) {
|
|
|
|
err = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
|
|
|
|
NL80211_CMD_GET_POWER_SAVE);
|
|
|
|
if (!hdr) {
|
|
|
|
err = -ENOMEM;
|
|
|
|
goto free_msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wdev->ps)
|
|
|
|
ps_state = NL80211_PS_ENABLED;
|
|
|
|
else
|
|
|
|
ps_state = NL80211_PS_DISABLED;
|
|
|
|
|
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_PS_STATE, ps_state);
|
|
|
|
|
|
|
|
genlmsg_end(msg, hdr);
|
|
|
|
err = genlmsg_reply(msg, info);
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
err = -ENOBUFS;
|
|
|
|
|
|
|
|
free_msg:
|
|
|
|
nlmsg_free(msg);
|
|
|
|
|
|
|
|
out:
|
|
|
|
cfg80211_unlock_rdev(rdev);
|
|
|
|
dev_put(dev);
|
|
|
|
|
|
|
|
unlock_rtnl:
|
|
|
|
rtnl_unlock();
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2010-03-23 07:02:33 +00:00
|
|
|
static struct nla_policy
|
|
|
|
nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = {
|
|
|
|
[NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
|
|
|
|
[NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 },
|
|
|
|
[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
|
|
|
|
};
|
|
|
|
|
|
|
|
static int nl80211_set_cqm_rssi(struct genl_info *info,
|
|
|
|
s32 threshold, u32 hysteresis)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev;
|
|
|
|
struct wireless_dev *wdev;
|
|
|
|
struct net_device *dev;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (threshold > 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
rtnl_lock();
|
|
|
|
|
|
|
|
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
|
|
|
if (err)
|
|
|
|
goto unlock_rdev;
|
|
|
|
|
|
|
|
wdev = dev->ieee80211_ptr;
|
|
|
|
|
|
|
|
if (!rdev->ops->set_cqm_rssi_config) {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto unlock_rdev;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wdev->iftype != NL80211_IFTYPE_STATION) {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto unlock_rdev;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = rdev->ops->set_cqm_rssi_config(wdev->wiphy, dev,
|
|
|
|
threshold, hysteresis);
|
|
|
|
|
|
|
|
unlock_rdev:
|
|
|
|
cfg80211_unlock_rdev(rdev);
|
|
|
|
dev_put(dev);
|
|
|
|
rtnl_unlock();
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct nlattr *attrs[NL80211_ATTR_CQM_MAX + 1];
|
|
|
|
struct nlattr *cqm;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
cqm = info->attrs[NL80211_ATTR_CQM];
|
|
|
|
if (!cqm) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm,
|
|
|
|
nl80211_attr_cqm_policy);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] &&
|
|
|
|
attrs[NL80211_ATTR_CQM_RSSI_HYST]) {
|
|
|
|
s32 threshold;
|
|
|
|
u32 hysteresis;
|
|
|
|
threshold = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
|
|
|
|
hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]);
|
|
|
|
err = nl80211_set_cqm_rssi(info, threshold, hysteresis);
|
|
|
|
} else
|
|
|
|
err = -EINVAL;
|
|
|
|
|
|
|
|
out:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2007-09-20 17:09:35 +00:00
|
|
|
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,
|
2007-12-19 01:03:29 +00:00
|
|
|
.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,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_DEL_KEY,
|
|
|
|
.doit = nl80211_del_key,
|
|
|
|
.policy = nl80211_policy,
|
2007-09-20 17:09:35 +00:00
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
2007-12-19 01:03:32 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_SET_BEACON,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
.doit = nl80211_addset_beacon,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_NEW_BEACON,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
.doit = nl80211_addset_beacon,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_DEL_BEACON,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
.doit = nl80211_del_beacon,
|
|
|
|
},
|
2007-12-19 01:03:34 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_GET_STATION,
|
|
|
|
.doit = nl80211_get_station,
|
2008-02-23 14:17:06 +00:00
|
|
|
.dumpit = nl80211_dump_station,
|
2007-12-19 01:03:34 +00:00
|
|
|
.policy = nl80211_policy,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_SET_STATION,
|
|
|
|
.doit = nl80211_set_station,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_NEW_STATION,
|
|
|
|
.doit = nl80211_new_station,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_DEL_STATION,
|
|
|
|
.doit = nl80211_del_station,
|
|
|
|
.policy = nl80211_policy,
|
2008-02-23 14:17:06 +00:00
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_GET_MPATH,
|
|
|
|
.doit = nl80211_get_mpath,
|
|
|
|
.dumpit = nl80211_dump_mpath,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_SET_MPATH,
|
|
|
|
.doit = nl80211_set_mpath,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_NEW_MPATH,
|
|
|
|
.doit = nl80211_new_mpath,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_DEL_MPATH,
|
|
|
|
.doit = nl80211_del_mpath,
|
|
|
|
.policy = nl80211_policy,
|
2008-08-07 17:07:01 +00:00
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_SET_BSS,
|
|
|
|
.doit = nl80211_set_bss,
|
|
|
|
.policy = nl80211_policy,
|
2008-09-10 06:19:48 +00:00
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
2009-01-30 17:26:42 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_GET_REG,
|
|
|
|
.doit = nl80211_get_reg,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
/* can be retrieved by unprivileged users */
|
|
|
|
},
|
2008-09-10 06:19:48 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_SET_REG,
|
|
|
|
.doit = nl80211_set_reg,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_REQ_SET_REG,
|
|
|
|
.doit = nl80211_req_set_reg,
|
|
|
|
.policy = nl80211_policy,
|
2008-10-21 19:03:48 +00:00
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_GET_MESH_PARAMS,
|
|
|
|
.doit = nl80211_get_mesh_params,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
/* can be retrieved by unprivileged users */
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_SET_MESH_PARAMS,
|
|
|
|
.doit = nl80211_set_mesh_params,
|
|
|
|
.policy = nl80211_policy,
|
2009-01-13 14:03:29 +00:00
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
2009-02-10 20:25:55 +00:00
|
|
|
{
|
|
|
|
.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,
|
|
|
|
},
|
2009-03-19 11:39:22 +00:00
|
|
|
{
|
|
|
|
.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,
|
|
|
|
},
|
2009-04-19 19:24:32 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_JOIN_IBSS,
|
|
|
|
.doit = nl80211_join_ibss,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_LEAVE_IBSS,
|
|
|
|
.doit = nl80211_leave_ibss,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
2009-07-01 19:26:51 +00:00
|
|
|
#ifdef CONFIG_NL80211_TESTMODE
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_TESTMODE,
|
|
|
|
.doit = nl80211_testmode_do,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
|
|
|
#endif
|
2009-07-01 19:26:54 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_CONNECT,
|
|
|
|
.doit = nl80211_connect,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_DISCONNECT,
|
|
|
|
.doit = nl80211_disconnect,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
2009-07-13 22:33:35 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_SET_WIPHY_NETNS,
|
|
|
|
.doit = nl80211_wiphy_netns,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
2009-11-11 11:25:40 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_GET_SURVEY,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.dumpit = nl80211_dump_survey,
|
|
|
|
},
|
2009-11-24 22:59:15 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_SET_PMKSA,
|
|
|
|
.doit = nl80211_setdel_pmksa,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_DEL_PMKSA,
|
|
|
|
.doit = nl80211_setdel_pmksa,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_FLUSH_PMKSA,
|
|
|
|
.doit = nl80211_flush_pmksa,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
2009-12-23 12:15:41 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_REMAIN_ON_CHANNEL,
|
|
|
|
.doit = nl80211_remain_on_channel,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
|
|
|
|
.doit = nl80211_cancel_remain_on_channel,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
2009-12-29 10:59:45 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_SET_TX_BITRATE_MASK,
|
|
|
|
.doit = nl80211_set_tx_bitrate_mask,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
2010-02-15 10:53:10 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_REGISTER_ACTION,
|
|
|
|
.doit = nl80211_register_action,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_ACTION,
|
|
|
|
.doit = nl80211_action,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
nl80211: add power save commands
The most needed command from nl80211, which Wireless Extensions had,
is support for power save mode. Add a simple command to make it possible
to enable and disable power save via nl80211.
I was also planning about extending the interface, for example adding the
timeout value, but after thinking more about this I decided not to do it.
Basically there were three reasons:
Firstly, the parameters for power save are very much hardware dependent.
Trying to find a unified interface which would work with all hardware, and
still make sense to users, will be very difficult.
Secondly, IEEE 802.11 power save implementation in Linux is still in state
of flux. We have a long way to still to go and there is no way to predict
what kind of implementation we will have after few years. And because we
need to support nl80211 interface a long time, practically forever, adding
now parameters to nl80211 might create maintenance problems later on.
Third issue are the users. Power save parameters are mostly used for
debugging, so debugfs is better, more flexible, interface for this.
For example, wpa_supplicant currently doesn't configure anything related
to power save mode. It's better to strive that kernel can automatically
optimise the power save parameters, like with help of pm qos network
and other traffic parameters.
Later on, when we have better understanding of power save, we can extend
this command with more features, if there's a need for that.
Signed-off-by: Kalle Valo <kalle.valo@nokia.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2010-02-17 15:58:10 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_SET_POWER_SAVE,
|
|
|
|
.doit = nl80211_set_power_save,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_GET_POWER_SAVE,
|
|
|
|
.doit = nl80211_get_power_save,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
/* can be retrieved by unprivileged users */
|
|
|
|
},
|
2010-03-23 07:02:33 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_SET_CQM,
|
|
|
|
.doit = nl80211_set_cqm,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
cfg80211/mac80211: better channel handling
Currently (all tested with hwsim) you can do stupid
things like setting up an AP on a certain channel,
then adding another virtual interface and making
that associate on another channel -- this will make
the beaconing to move channel but obviously without
the necessary IEs data update.
In order to improve this situation, first make the
configuration APIs (cfg80211 and nl80211) aware of
multi-channel operation -- we'll eventually need
that in the future anyway. There's one userland API
change and one API addition. The API change is that
now SET_WIPHY must be called with virtual interface
index rather than only wiphy index in order to take
effect for that interface -- luckily all current
users (hostapd) do that. For monitor interfaces, the
old setting is preserved, but monitors are always
slaved to other devices anyway so no guarantees.
The second userland API change is the introduction
of a per virtual interface SET_CHANNEL command, that
hostapd should use going forward to make it easier
to understand what's going on (it can automatically
detect a kernel with this command).
Other than mac80211, no existing cfg80211 drivers
are affected by this change because they only allow
a single virtual interface.
mac80211, however, now needs to be aware that the
channel settings are per interface now, and needs
to disallow (for now) real multi-channel operation,
which is another important part of this patch.
One of the immediate benefits is that you can now
start hostapd to operate on a hardware that already
has a connection on another virtual interface, as
long as you specify the same channel.
Note that two things are left unhandled (this is an
improvement -- not a complete fix):
* different HT/no-HT modes
currently you could start an HT AP and then
connect to a non-HT network on the same channel
which would configure the hardware for no HT;
that can be fixed fairly easily
* CSA
An AP we're connected to on a virtual interface
might indicate switching channels, and in that
case we would follow it, regardless of how many
other interfaces are operating; this requires
more effort to fix but is pretty rare after all
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2010-05-05 13:25:02 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_SET_CHANNEL,
|
|
|
|
.doit = nl80211_set_channel,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
},
|
2007-09-20 17:09:35 +00:00
|
|
|
};
|
2009-12-23 12:15:41 +00:00
|
|
|
|
nl80211: Event notifications for MLME events
Add new nl80211 event notifications (and a new multicast group, "mlme")
for informing user space about received and processed Authentication,
(Re)Association Response, Deauthentication, and Disassociation frames in
station and IBSS modes (i.e., MLME SAP interface primitives
MLME-AUTHENTICATE.confirm, MLME-ASSOCIATE.confirm,
MLME-REASSOCIATE.confirm, MLME-DEAUTHENTICATE.indicate, and
MLME-DISASSOCIATE.indication). The event data is encapsulated as the 802.11
management frame since we already have the frame in that format and it
includes all the needed information.
This is the initial step in providing MLME SAP interface for
authentication and association with nl80211. In other words, kernel code
will act as the MLME and a user space application can control it as the
SME.
Signed-off-by: Jouni Malinen <j@w1.fi>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-03-19 11:39:21 +00:00
|
|
|
static struct genl_multicast_group nl80211_mlme_mcgrp = {
|
|
|
|
.name = "mlme",
|
|
|
|
};
|
2007-09-20 17:09:35 +00:00
|
|
|
|
|
|
|
/* multicast groups */
|
|
|
|
static struct genl_multicast_group nl80211_config_mcgrp = {
|
|
|
|
.name = "config",
|
|
|
|
};
|
2009-02-10 20:25:55 +00:00
|
|
|
static struct genl_multicast_group nl80211_scan_mcgrp = {
|
|
|
|
.name = "scan",
|
|
|
|
};
|
2009-03-10 02:07:42 +00:00
|
|
|
static struct genl_multicast_group nl80211_regulatory_mcgrp = {
|
|
|
|
.name = "regulatory",
|
|
|
|
};
|
2007-09-20 17:09:35 +00:00
|
|
|
|
|
|
|
/* notification functions */
|
|
|
|
|
|
|
|
void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
|
|
|
|
{
|
|
|
|
struct sk_buff *msg;
|
|
|
|
|
2009-05-19 22:27:55 +00:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
2007-09-20 17:09:35 +00:00
|
|
|
if (!msg)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
|
|
|
|
nl80211_config_mcgrp.id, GFP_KERNEL);
|
2007-09-20 17:09:35 +00:00
|
|
|
}
|
|
|
|
|
2009-05-24 14:43:15 +00:00
|
|
|
static int nl80211_add_scan_req(struct sk_buff *msg,
|
|
|
|
struct cfg80211_registered_device *rdev)
|
|
|
|
{
|
|
|
|
struct cfg80211_scan_request *req = rdev->scan_req;
|
|
|
|
struct nlattr *nest;
|
|
|
|
int i;
|
|
|
|
|
2009-07-07 01:56:11 +00:00
|
|
|
ASSERT_RDEV_LOCK(rdev);
|
|
|
|
|
2009-05-24 14:43:15 +00:00
|
|
|
if (WARN_ON(!req))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
nest = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS);
|
|
|
|
if (!nest)
|
|
|
|
goto nla_put_failure;
|
|
|
|
for (i = 0; i < req->n_ssids; i++)
|
|
|
|
NLA_PUT(msg, i, req->ssids[i].ssid_len, req->ssids[i].ssid);
|
|
|
|
nla_nest_end(msg, nest);
|
|
|
|
|
|
|
|
nest = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
|
|
|
|
if (!nest)
|
|
|
|
goto nla_put_failure;
|
|
|
|
for (i = 0; i < req->n_channels; i++)
|
|
|
|
NLA_PUT_U32(msg, i, req->channels[i]->center_freq);
|
|
|
|
nla_nest_end(msg, nest);
|
|
|
|
|
|
|
|
if (req->ie)
|
|
|
|
NLA_PUT(msg, NL80211_ATTR_IE, req->ie_len, req->ie);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
nla_put_failure:
|
|
|
|
return -ENOBUFS;
|
|
|
|
}
|
|
|
|
|
2009-06-16 17:56:42 +00:00
|
|
|
static int nl80211_send_scan_msg(struct sk_buff *msg,
|
|
|
|
struct cfg80211_registered_device *rdev,
|
|
|
|
struct net_device *netdev,
|
|
|
|
u32 pid, u32 seq, int flags,
|
|
|
|
u32 cmd)
|
2009-02-10 20:25:55 +00:00
|
|
|
{
|
|
|
|
void *hdr;
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
|
|
|
|
if (!hdr)
|
|
|
|
return -1;
|
|
|
|
|
2009-02-21 05:04:19 +00:00
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
|
2009-02-10 20:25:55 +00:00
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
|
|
|
|
|
2009-05-24 14:43:15 +00:00
|
|
|
/* ignore errors and send incomplete event anyway */
|
|
|
|
nl80211_add_scan_req(msg, rdev);
|
2009-02-10 20:25:55 +00:00
|
|
|
|
|
|
|
return genlmsg_end(msg, hdr);
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
return -EMSGSIZE;
|
|
|
|
}
|
|
|
|
|
2009-06-16 17:56:42 +00:00
|
|
|
void nl80211_send_scan_start(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_msg(msg, rdev, netdev, 0, 0, 0,
|
|
|
|
NL80211_CMD_TRIGGER_SCAN) < 0) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
|
|
|
|
nl80211_scan_mcgrp.id, GFP_KERNEL);
|
2009-06-16 17:56:42 +00:00
|
|
|
}
|
|
|
|
|
2009-02-10 20:25:55 +00:00
|
|
|
void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
|
|
|
|
struct net_device *netdev)
|
|
|
|
{
|
|
|
|
struct sk_buff *msg;
|
|
|
|
|
2009-05-19 22:27:55 +00:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
2009-02-10 20:25:55 +00:00
|
|
|
if (!msg)
|
|
|
|
return;
|
|
|
|
|
2009-06-16 17:56:42 +00:00
|
|
|
if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
|
|
|
|
NL80211_CMD_NEW_SCAN_RESULTS) < 0) {
|
2009-02-10 20:25:55 +00:00
|
|
|
nlmsg_free(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
|
|
|
|
nl80211_scan_mcgrp.id, GFP_KERNEL);
|
2009-02-10 20:25:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
|
|
|
|
struct net_device *netdev)
|
|
|
|
{
|
|
|
|
struct sk_buff *msg;
|
|
|
|
|
2009-05-19 22:27:55 +00:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
2009-02-10 20:25:55 +00:00
|
|
|
if (!msg)
|
|
|
|
return;
|
|
|
|
|
2009-06-16 17:56:42 +00:00
|
|
|
if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
|
|
|
|
NL80211_CMD_SCAN_ABORTED) < 0) {
|
2009-02-10 20:25:55 +00:00
|
|
|
nlmsg_free(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
|
|
|
|
nl80211_scan_mcgrp.id, GFP_KERNEL);
|
2009-02-10 20:25:55 +00:00
|
|
|
}
|
|
|
|
|
2009-03-10 02:07:42 +00:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
2009-05-19 22:27:55 +00:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
2009-03-10 02:07:42 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2009-07-25 08:54:13 +00:00
|
|
|
rcu_read_lock();
|
2009-07-13 22:33:35 +00:00
|
|
|
genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
|
2009-07-25 08:54:13 +00:00
|
|
|
GFP_ATOMIC);
|
|
|
|
rcu_read_unlock();
|
2009-03-10 02:07:42 +00:00
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
nlmsg_free(msg);
|
|
|
|
}
|
|
|
|
|
nl80211: Event notifications for MLME events
Add new nl80211 event notifications (and a new multicast group, "mlme")
for informing user space about received and processed Authentication,
(Re)Association Response, Deauthentication, and Disassociation frames in
station and IBSS modes (i.e., MLME SAP interface primitives
MLME-AUTHENTICATE.confirm, MLME-ASSOCIATE.confirm,
MLME-REASSOCIATE.confirm, MLME-DEAUTHENTICATE.indicate, and
MLME-DISASSOCIATE.indication). The event data is encapsulated as the 802.11
management frame since we already have the frame in that format and it
includes all the needed information.
This is the initial step in providing MLME SAP interface for
authentication and association with nl80211. In other words, kernel code
will act as the MLME and a user space application can control it as the
SME.
Signed-off-by: Jouni Malinen <j@w1.fi>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-03-19 11:39:21 +00:00
|
|
|
static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
|
|
|
|
struct net_device *netdev,
|
|
|
|
const u8 *buf, size_t len,
|
2009-07-01 19:26:47 +00:00
|
|
|
enum nl80211_commands cmd, gfp_t gfp)
|
nl80211: Event notifications for MLME events
Add new nl80211 event notifications (and a new multicast group, "mlme")
for informing user space about received and processed Authentication,
(Re)Association Response, Deauthentication, and Disassociation frames in
station and IBSS modes (i.e., MLME SAP interface primitives
MLME-AUTHENTICATE.confirm, MLME-ASSOCIATE.confirm,
MLME-REASSOCIATE.confirm, MLME-DEAUTHENTICATE.indicate, and
MLME-DISASSOCIATE.indication). The event data is encapsulated as the 802.11
management frame since we already have the frame in that format and it
includes all the needed information.
This is the initial step in providing MLME SAP interface for
authentication and association with nl80211. In other words, kernel code
will act as the MLME and a user space application can control it as the
SME.
Signed-off-by: Jouni Malinen <j@w1.fi>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-03-19 11:39:21 +00:00
|
|
|
{
|
|
|
|
struct sk_buff *msg;
|
|
|
|
void *hdr;
|
|
|
|
|
2009-07-01 19:26:47 +00:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
nl80211: Event notifications for MLME events
Add new nl80211 event notifications (and a new multicast group, "mlme")
for informing user space about received and processed Authentication,
(Re)Association Response, Deauthentication, and Disassociation frames in
station and IBSS modes (i.e., MLME SAP interface primitives
MLME-AUTHENTICATE.confirm, MLME-ASSOCIATE.confirm,
MLME-REASSOCIATE.confirm, MLME-DEAUTHENTICATE.indicate, and
MLME-DISASSOCIATE.indication). The event data is encapsulated as the 802.11
management frame since we already have the frame in that format and it
includes all the needed information.
This is the initial step in providing MLME SAP interface for
authentication and association with nl80211. In other words, kernel code
will act as the MLME and a user space application can control it as the
SME.
Signed-off-by: Jouni Malinen <j@w1.fi>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-03-19 11:39:21 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
|
|
|
|
nl80211_mlme_mcgrp.id, gfp);
|
nl80211: Event notifications for MLME events
Add new nl80211 event notifications (and a new multicast group, "mlme")
for informing user space about received and processed Authentication,
(Re)Association Response, Deauthentication, and Disassociation frames in
station and IBSS modes (i.e., MLME SAP interface primitives
MLME-AUTHENTICATE.confirm, MLME-ASSOCIATE.confirm,
MLME-REASSOCIATE.confirm, MLME-DEAUTHENTICATE.indicate, and
MLME-DISASSOCIATE.indication). The event data is encapsulated as the 802.11
management frame since we already have the frame in that format and it
includes all the needed information.
This is the initial step in providing MLME SAP interface for
authentication and association with nl80211. In other words, kernel code
will act as the MLME and a user space application can control it as the
SME.
Signed-off-by: Jouni Malinen <j@w1.fi>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-03-19 11:39:21 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
nlmsg_free(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
|
2009-07-01 19:26:47 +00:00
|
|
|
struct net_device *netdev, const u8 *buf,
|
|
|
|
size_t len, gfp_t gfp)
|
nl80211: Event notifications for MLME events
Add new nl80211 event notifications (and a new multicast group, "mlme")
for informing user space about received and processed Authentication,
(Re)Association Response, Deauthentication, and Disassociation frames in
station and IBSS modes (i.e., MLME SAP interface primitives
MLME-AUTHENTICATE.confirm, MLME-ASSOCIATE.confirm,
MLME-REASSOCIATE.confirm, MLME-DEAUTHENTICATE.indicate, and
MLME-DISASSOCIATE.indication). The event data is encapsulated as the 802.11
management frame since we already have the frame in that format and it
includes all the needed information.
This is the initial step in providing MLME SAP interface for
authentication and association with nl80211. In other words, kernel code
will act as the MLME and a user space application can control it as the
SME.
Signed-off-by: Jouni Malinen <j@w1.fi>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-03-19 11:39:21 +00:00
|
|
|
{
|
|
|
|
nl80211_send_mlme_event(rdev, netdev, buf, len,
|
2009-07-01 19:26:47 +00:00
|
|
|
NL80211_CMD_AUTHENTICATE, gfp);
|
nl80211: Event notifications for MLME events
Add new nl80211 event notifications (and a new multicast group, "mlme")
for informing user space about received and processed Authentication,
(Re)Association Response, Deauthentication, and Disassociation frames in
station and IBSS modes (i.e., MLME SAP interface primitives
MLME-AUTHENTICATE.confirm, MLME-ASSOCIATE.confirm,
MLME-REASSOCIATE.confirm, MLME-DEAUTHENTICATE.indicate, and
MLME-DISASSOCIATE.indication). The event data is encapsulated as the 802.11
management frame since we already have the frame in that format and it
includes all the needed information.
This is the initial step in providing MLME SAP interface for
authentication and association with nl80211. In other words, kernel code
will act as the MLME and a user space application can control it as the
SME.
Signed-off-by: Jouni Malinen <j@w1.fi>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-03-19 11:39:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
|
|
|
|
struct net_device *netdev, const u8 *buf,
|
2009-07-01 19:26:47 +00:00
|
|
|
size_t len, gfp_t gfp)
|
nl80211: Event notifications for MLME events
Add new nl80211 event notifications (and a new multicast group, "mlme")
for informing user space about received and processed Authentication,
(Re)Association Response, Deauthentication, and Disassociation frames in
station and IBSS modes (i.e., MLME SAP interface primitives
MLME-AUTHENTICATE.confirm, MLME-ASSOCIATE.confirm,
MLME-REASSOCIATE.confirm, MLME-DEAUTHENTICATE.indicate, and
MLME-DISASSOCIATE.indication). The event data is encapsulated as the 802.11
management frame since we already have the frame in that format and it
includes all the needed information.
This is the initial step in providing MLME SAP interface for
authentication and association with nl80211. In other words, kernel code
will act as the MLME and a user space application can control it as the
SME.
Signed-off-by: Jouni Malinen <j@w1.fi>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-03-19 11:39:21 +00:00
|
|
|
{
|
2009-07-01 19:26:47 +00:00
|
|
|
nl80211_send_mlme_event(rdev, netdev, buf, len,
|
|
|
|
NL80211_CMD_ASSOCIATE, gfp);
|
nl80211: Event notifications for MLME events
Add new nl80211 event notifications (and a new multicast group, "mlme")
for informing user space about received and processed Authentication,
(Re)Association Response, Deauthentication, and Disassociation frames in
station and IBSS modes (i.e., MLME SAP interface primitives
MLME-AUTHENTICATE.confirm, MLME-ASSOCIATE.confirm,
MLME-REASSOCIATE.confirm, MLME-DEAUTHENTICATE.indicate, and
MLME-DISASSOCIATE.indication). The event data is encapsulated as the 802.11
management frame since we already have the frame in that format and it
includes all the needed information.
This is the initial step in providing MLME SAP interface for
authentication and association with nl80211. In other words, kernel code
will act as the MLME and a user space application can control it as the
SME.
Signed-off-by: Jouni Malinen <j@w1.fi>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-03-19 11:39:21 +00:00
|
|
|
}
|
|
|
|
|
2009-03-27 18:53:56 +00:00
|
|
|
void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
|
2009-07-01 19:26:47 +00:00
|
|
|
struct net_device *netdev, const u8 *buf,
|
|
|
|
size_t len, gfp_t gfp)
|
nl80211: Event notifications for MLME events
Add new nl80211 event notifications (and a new multicast group, "mlme")
for informing user space about received and processed Authentication,
(Re)Association Response, Deauthentication, and Disassociation frames in
station and IBSS modes (i.e., MLME SAP interface primitives
MLME-AUTHENTICATE.confirm, MLME-ASSOCIATE.confirm,
MLME-REASSOCIATE.confirm, MLME-DEAUTHENTICATE.indicate, and
MLME-DISASSOCIATE.indication). The event data is encapsulated as the 802.11
management frame since we already have the frame in that format and it
includes all the needed information.
This is the initial step in providing MLME SAP interface for
authentication and association with nl80211. In other words, kernel code
will act as the MLME and a user space application can control it as the
SME.
Signed-off-by: Jouni Malinen <j@w1.fi>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-03-19 11:39:21 +00:00
|
|
|
{
|
|
|
|
nl80211_send_mlme_event(rdev, netdev, buf, len,
|
2009-07-01 19:26:47 +00:00
|
|
|
NL80211_CMD_DEAUTHENTICATE, gfp);
|
nl80211: Event notifications for MLME events
Add new nl80211 event notifications (and a new multicast group, "mlme")
for informing user space about received and processed Authentication,
(Re)Association Response, Deauthentication, and Disassociation frames in
station and IBSS modes (i.e., MLME SAP interface primitives
MLME-AUTHENTICATE.confirm, MLME-ASSOCIATE.confirm,
MLME-REASSOCIATE.confirm, MLME-DEAUTHENTICATE.indicate, and
MLME-DISASSOCIATE.indication). The event data is encapsulated as the 802.11
management frame since we already have the frame in that format and it
includes all the needed information.
This is the initial step in providing MLME SAP interface for
authentication and association with nl80211. In other words, kernel code
will act as the MLME and a user space application can control it as the
SME.
Signed-off-by: Jouni Malinen <j@w1.fi>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-03-19 11:39:21 +00:00
|
|
|
}
|
|
|
|
|
2009-03-27 18:53:56 +00:00
|
|
|
void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
|
|
|
|
struct net_device *netdev, const u8 *buf,
|
2009-07-01 19:26:47 +00:00
|
|
|
size_t len, gfp_t gfp)
|
nl80211: Event notifications for MLME events
Add new nl80211 event notifications (and a new multicast group, "mlme")
for informing user space about received and processed Authentication,
(Re)Association Response, Deauthentication, and Disassociation frames in
station and IBSS modes (i.e., MLME SAP interface primitives
MLME-AUTHENTICATE.confirm, MLME-ASSOCIATE.confirm,
MLME-REASSOCIATE.confirm, MLME-DEAUTHENTICATE.indicate, and
MLME-DISASSOCIATE.indication). The event data is encapsulated as the 802.11
management frame since we already have the frame in that format and it
includes all the needed information.
This is the initial step in providing MLME SAP interface for
authentication and association with nl80211. In other words, kernel code
will act as the MLME and a user space application can control it as the
SME.
Signed-off-by: Jouni Malinen <j@w1.fi>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-03-19 11:39:21 +00:00
|
|
|
{
|
|
|
|
nl80211_send_mlme_event(rdev, netdev, buf, len,
|
2009-07-01 19:26:47 +00:00
|
|
|
NL80211_CMD_DISASSOCIATE, gfp);
|
nl80211: Event notifications for MLME events
Add new nl80211 event notifications (and a new multicast group, "mlme")
for informing user space about received and processed Authentication,
(Re)Association Response, Deauthentication, and Disassociation frames in
station and IBSS modes (i.e., MLME SAP interface primitives
MLME-AUTHENTICATE.confirm, MLME-ASSOCIATE.confirm,
MLME-REASSOCIATE.confirm, MLME-DEAUTHENTICATE.indicate, and
MLME-DISASSOCIATE.indication). The event data is encapsulated as the 802.11
management frame since we already have the frame in that format and it
includes all the needed information.
This is the initial step in providing MLME SAP interface for
authentication and association with nl80211. In other words, kernel code
will act as the MLME and a user space application can control it as the
SME.
Signed-off-by: Jouni Malinen <j@w1.fi>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-03-19 11:39:21 +00:00
|
|
|
}
|
|
|
|
|
2009-05-02 04:34:48 +00:00
|
|
|
static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
|
|
|
|
struct net_device *netdev, int cmd,
|
2009-07-01 19:26:47 +00:00
|
|
|
const u8 *addr, gfp_t gfp)
|
2009-04-22 18:38:25 +00:00
|
|
|
{
|
|
|
|
struct sk_buff *msg;
|
|
|
|
void *hdr;
|
|
|
|
|
2009-07-01 19:26:47 +00:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
2009-04-22 18:38:25 +00:00
|
|
|
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_FLAG(msg, NL80211_ATTR_TIMED_OUT);
|
|
|
|
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
|
|
|
|
|
|
|
|
if (genlmsg_end(msg, hdr) < 0) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
|
|
|
|
nl80211_mlme_mcgrp.id, gfp);
|
2009-04-22 18:38:25 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
nlmsg_free(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev,
|
2009-07-01 19:26:47 +00:00
|
|
|
struct net_device *netdev, const u8 *addr,
|
|
|
|
gfp_t gfp)
|
2009-04-22 18:38:25 +00:00
|
|
|
{
|
|
|
|
nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_AUTHENTICATE,
|
2009-07-01 19:26:47 +00:00
|
|
|
addr, gfp);
|
2009-04-22 18:38:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev,
|
2009-07-01 19:26:47 +00:00
|
|
|
struct net_device *netdev, const u8 *addr,
|
|
|
|
gfp_t gfp)
|
2009-04-22 18:38:25 +00:00
|
|
|
{
|
2009-07-01 19:26:47 +00:00
|
|
|
nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_ASSOCIATE,
|
|
|
|
addr, gfp);
|
2009-04-22 18:38:25 +00:00
|
|
|
}
|
|
|
|
|
2009-07-01 19:26:54 +00:00
|
|
|
void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
|
|
|
|
struct net_device *netdev, const u8 *bssid,
|
|
|
|
const u8 *req_ie, size_t req_ie_len,
|
|
|
|
const u8 *resp_ie, size_t resp_ie_len,
|
|
|
|
u16 status, gfp_t gfp)
|
|
|
|
{
|
|
|
|
struct sk_buff *msg;
|
|
|
|
void *hdr;
|
|
|
|
|
|
|
|
msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
|
|
|
|
if (!msg)
|
|
|
|
return;
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONNECT);
|
|
|
|
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);
|
|
|
|
if (bssid)
|
|
|
|
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
|
|
|
|
NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status);
|
|
|
|
if (req_ie)
|
|
|
|
NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie);
|
|
|
|
if (resp_ie)
|
|
|
|
NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie);
|
|
|
|
|
|
|
|
if (genlmsg_end(msg, hdr) < 0) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
|
|
|
|
nl80211_mlme_mcgrp.id, gfp);
|
2009-07-01 19:26:54 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
nlmsg_free(msg);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
|
|
|
|
struct net_device *netdev, const u8 *bssid,
|
|
|
|
const u8 *req_ie, size_t req_ie_len,
|
|
|
|
const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp)
|
|
|
|
{
|
|
|
|
struct sk_buff *msg;
|
|
|
|
void *hdr;
|
|
|
|
|
|
|
|
msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
|
|
|
|
if (!msg)
|
|
|
|
return;
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ROAM);
|
|
|
|
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_MAC, ETH_ALEN, bssid);
|
|
|
|
if (req_ie)
|
|
|
|
NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie);
|
|
|
|
if (resp_ie)
|
|
|
|
NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie);
|
|
|
|
|
|
|
|
if (genlmsg_end(msg, hdr) < 0) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
|
|
|
|
nl80211_mlme_mcgrp.id, gfp);
|
2009-07-01 19:26:54 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
nlmsg_free(msg);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
|
|
|
|
struct net_device *netdev, u16 reason,
|
2009-07-07 01:56:11 +00:00
|
|
|
const u8 *ie, size_t ie_len, bool from_ap)
|
2009-07-01 19:26:54 +00:00
|
|
|
{
|
|
|
|
struct sk_buff *msg;
|
|
|
|
void *hdr;
|
|
|
|
|
2009-07-07 01:56:11 +00:00
|
|
|
msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
|
2009-07-01 19:26:54 +00:00
|
|
|
if (!msg)
|
|
|
|
return;
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DISCONNECT);
|
|
|
|
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);
|
|
|
|
if (from_ap && reason)
|
|
|
|
NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason);
|
|
|
|
if (from_ap)
|
|
|
|
NLA_PUT_FLAG(msg, NL80211_ATTR_DISCONNECTED_BY_AP);
|
|
|
|
if (ie)
|
|
|
|
NLA_PUT(msg, NL80211_ATTR_IE, ie_len, ie);
|
|
|
|
|
|
|
|
if (genlmsg_end(msg, hdr) < 0) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
|
|
|
|
nl80211_mlme_mcgrp.id, GFP_KERNEL);
|
2009-07-01 19:26:54 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
nlmsg_free(msg);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2009-04-19 19:24:32 +00:00
|
|
|
void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
|
|
|
|
struct net_device *netdev, const u8 *bssid,
|
|
|
|
gfp_t gfp)
|
|
|
|
{
|
|
|
|
struct sk_buff *msg;
|
|
|
|
void *hdr;
|
|
|
|
|
2009-05-19 22:27:55 +00:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
2009-04-19 19:24:32 +00:00
|
|
|
if (!msg)
|
|
|
|
return;
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_JOIN_IBSS);
|
|
|
|
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_MAC, ETH_ALEN, bssid);
|
|
|
|
|
|
|
|
if (genlmsg_end(msg, hdr) < 0) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
|
|
|
|
nl80211_mlme_mcgrp.id, gfp);
|
2009-04-19 19:24:32 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
nlmsg_free(msg);
|
|
|
|
}
|
|
|
|
|
2009-03-27 19:59:49 +00:00
|
|
|
void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
|
|
|
|
struct net_device *netdev, const u8 *addr,
|
|
|
|
enum nl80211_key_type key_type, int key_id,
|
2009-07-01 19:26:47 +00:00
|
|
|
const u8 *tsc, gfp_t gfp)
|
2009-03-27 19:59:49 +00:00
|
|
|
{
|
|
|
|
struct sk_buff *msg;
|
|
|
|
void *hdr;
|
|
|
|
|
2009-07-01 19:26:47 +00:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
2009-03-27 19:59:49 +00:00
|
|
|
if (!msg)
|
|
|
|
return;
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_MICHAEL_MIC_FAILURE);
|
|
|
|
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);
|
|
|
|
if (addr)
|
|
|
|
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
|
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE, key_type);
|
|
|
|
NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_id);
|
|
|
|
if (tsc)
|
|
|
|
NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, 6, tsc);
|
|
|
|
|
|
|
|
if (genlmsg_end(msg, hdr) < 0) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
|
|
|
|
nl80211_mlme_mcgrp.id, gfp);
|
2009-03-27 19:59:49 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
nlmsg_free(msg);
|
|
|
|
}
|
|
|
|
|
2009-04-02 18:08:09 +00:00
|
|
|
void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
|
|
|
|
struct ieee80211_channel *channel_before,
|
|
|
|
struct ieee80211_channel *channel_after)
|
|
|
|
{
|
|
|
|
struct sk_buff *msg;
|
|
|
|
void *hdr;
|
|
|
|
struct nlattr *nl_freq;
|
|
|
|
|
2009-05-19 22:27:55 +00:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
|
2009-04-02 18:08:09 +00:00
|
|
|
if (!msg)
|
|
|
|
return;
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_BEACON_HINT);
|
|
|
|
if (!hdr) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Since we are applying the beacon hint to a wiphy we know its
|
|
|
|
* wiphy_idx is valid
|
|
|
|
*/
|
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy));
|
|
|
|
|
|
|
|
/* Before */
|
|
|
|
nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE);
|
|
|
|
if (!nl_freq)
|
|
|
|
goto nla_put_failure;
|
|
|
|
if (nl80211_msg_put_channel(msg, channel_before))
|
|
|
|
goto nla_put_failure;
|
|
|
|
nla_nest_end(msg, nl_freq);
|
|
|
|
|
|
|
|
/* After */
|
|
|
|
nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER);
|
|
|
|
if (!nl_freq)
|
|
|
|
goto nla_put_failure;
|
|
|
|
if (nl80211_msg_put_channel(msg, channel_after))
|
|
|
|
goto nla_put_failure;
|
|
|
|
nla_nest_end(msg, nl_freq);
|
|
|
|
|
|
|
|
if (genlmsg_end(msg, hdr) < 0) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
rcu_read_lock();
|
|
|
|
genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
|
|
|
|
GFP_ATOMIC);
|
|
|
|
rcu_read_unlock();
|
2009-04-02 18:08:09 +00:00
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
nlmsg_free(msg);
|
|
|
|
}
|
|
|
|
|
2009-12-23 12:15:41 +00:00
|
|
|
static void nl80211_send_remain_on_chan_event(
|
|
|
|
int cmd, struct cfg80211_registered_device *rdev,
|
|
|
|
struct net_device *netdev, u64 cookie,
|
|
|
|
struct ieee80211_channel *chan,
|
|
|
|
enum nl80211_channel_type channel_type,
|
|
|
|
unsigned int duration, gfp_t gfp)
|
|
|
|
{
|
|
|
|
struct sk_buff *msg;
|
|
|
|
void *hdr;
|
|
|
|
|
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
|
|
|
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_U32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq);
|
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type);
|
|
|
|
NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
|
|
|
|
|
|
|
|
if (cmd == NL80211_CMD_REMAIN_ON_CHANNEL)
|
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration);
|
|
|
|
|
|
|
|
if (genlmsg_end(msg, hdr) < 0) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
|
|
|
|
nl80211_mlme_mcgrp.id, gfp);
|
|
|
|
return;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
nlmsg_free(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev,
|
|
|
|
struct net_device *netdev, u64 cookie,
|
|
|
|
struct ieee80211_channel *chan,
|
|
|
|
enum nl80211_channel_type channel_type,
|
|
|
|
unsigned int duration, gfp_t gfp)
|
|
|
|
{
|
|
|
|
nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL,
|
|
|
|
rdev, netdev, cookie, chan,
|
|
|
|
channel_type, duration, gfp);
|
|
|
|
}
|
|
|
|
|
|
|
|
void nl80211_send_remain_on_channel_cancel(
|
|
|
|
struct cfg80211_registered_device *rdev, struct net_device *netdev,
|
|
|
|
u64 cookie, struct ieee80211_channel *chan,
|
|
|
|
enum nl80211_channel_type channel_type, gfp_t gfp)
|
|
|
|
{
|
|
|
|
nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
|
|
|
|
rdev, netdev, cookie, chan,
|
|
|
|
channel_type, 0, gfp);
|
|
|
|
}
|
|
|
|
|
2009-12-23 12:15:44 +00:00
|
|
|
void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
|
|
|
|
struct net_device *dev, const u8 *mac_addr,
|
|
|
|
struct station_info *sinfo, gfp_t gfp)
|
|
|
|
{
|
|
|
|
struct sk_buff *msg;
|
|
|
|
|
|
|
|
msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
|
|
|
|
if (!msg)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (nl80211_send_station(msg, 0, 0, 0, dev, mac_addr, sinfo) < 0) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
|
|
|
|
nl80211_mlme_mcgrp.id, gfp);
|
|
|
|
}
|
|
|
|
|
2010-02-15 10:53:10 +00:00
|
|
|
int nl80211_send_action(struct cfg80211_registered_device *rdev,
|
|
|
|
struct net_device *netdev, u32 nlpid,
|
|
|
|
int freq, const u8 *buf, size_t len, gfp_t gfp)
|
|
|
|
{
|
|
|
|
struct sk_buff *msg;
|
|
|
|
void *hdr;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
|
|
|
if (!msg)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ACTION);
|
|
|
|
if (!hdr) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
|
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
|
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
|
|
|
|
NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
|
|
|
|
|
|
|
|
err = genlmsg_end(msg, hdr);
|
|
|
|
if (err < 0) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return -ENOBUFS;
|
|
|
|
}
|
|
|
|
|
|
|
|
void nl80211_send_action_tx_status(struct cfg80211_registered_device *rdev,
|
|
|
|
struct net_device *netdev, u64 cookie,
|
|
|
|
const u8 *buf, size_t len, bool ack,
|
|
|
|
gfp_t gfp)
|
|
|
|
{
|
|
|
|
struct sk_buff *msg;
|
|
|
|
void *hdr;
|
|
|
|
|
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
|
|
|
if (!msg)
|
|
|
|
return;
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ACTION_TX_STATUS);
|
|
|
|
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);
|
|
|
|
NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
|
|
|
|
if (ack)
|
|
|
|
NLA_PUT_FLAG(msg, NL80211_ATTR_ACK);
|
|
|
|
|
|
|
|
if (genlmsg_end(msg, hdr) < 0) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
|
|
|
|
return;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
nlmsg_free(msg);
|
|
|
|
}
|
|
|
|
|
2010-03-23 07:02:33 +00:00
|
|
|
void
|
|
|
|
nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
|
|
|
|
struct net_device *netdev,
|
|
|
|
enum nl80211_cqm_rssi_threshold_event rssi_event,
|
|
|
|
gfp_t gfp)
|
|
|
|
{
|
|
|
|
struct sk_buff *msg;
|
|
|
|
struct nlattr *pinfoattr;
|
|
|
|
void *hdr;
|
|
|
|
|
|
|
|
msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
|
|
|
|
if (!msg)
|
|
|
|
return;
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
|
|
|
|
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);
|
|
|
|
|
|
|
|
pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
|
|
|
|
if (!pinfoattr)
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
NLA_PUT_U32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
|
|
|
|
rssi_event);
|
|
|
|
|
|
|
|
nla_nest_end(msg, pinfoattr);
|
|
|
|
|
|
|
|
if (genlmsg_end(msg, hdr) < 0) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
|
|
|
|
nl80211_mlme_mcgrp.id, gfp);
|
|
|
|
return;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
nlmsg_free(msg);
|
|
|
|
}
|
|
|
|
|
2010-02-15 10:53:10 +00:00
|
|
|
static int nl80211_netlink_notify(struct notifier_block * nb,
|
|
|
|
unsigned long state,
|
|
|
|
void *_notify)
|
|
|
|
{
|
|
|
|
struct netlink_notify *notify = _notify;
|
|
|
|
struct cfg80211_registered_device *rdev;
|
|
|
|
struct wireless_dev *wdev;
|
|
|
|
|
|
|
|
if (state != NETLINK_URELEASE)
|
|
|
|
return NOTIFY_DONE;
|
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
|
|
|
|
list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list)
|
|
|
|
list_for_each_entry_rcu(wdev, &rdev->netdev_list, list)
|
|
|
|
cfg80211_mlme_unregister_actions(wdev, notify->pid);
|
|
|
|
|
|
|
|
rcu_read_unlock();
|
|
|
|
|
|
|
|
return NOTIFY_DONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct notifier_block nl80211_netlink_notifier = {
|
|
|
|
.notifier_call = nl80211_netlink_notify,
|
|
|
|
};
|
|
|
|
|
2007-09-20 17:09:35 +00:00
|
|
|
/* initialisation/exit functions */
|
|
|
|
|
|
|
|
int nl80211_init(void)
|
|
|
|
{
|
2009-05-21 10:34:06 +00:00
|
|
|
int err;
|
2007-09-20 17:09:35 +00:00
|
|
|
|
2009-05-21 10:34:06 +00:00
|
|
|
err = genl_register_family_with_ops(&nl80211_fam,
|
|
|
|
nl80211_ops, ARRAY_SIZE(nl80211_ops));
|
2007-09-20 17:09:35 +00:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp);
|
|
|
|
if (err)
|
|
|
|
goto err_out;
|
|
|
|
|
2009-02-10 20:25:55 +00:00
|
|
|
err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp);
|
|
|
|
if (err)
|
|
|
|
goto err_out;
|
|
|
|
|
2009-03-10 02:07:42 +00:00
|
|
|
err = genl_register_mc_group(&nl80211_fam, &nl80211_regulatory_mcgrp);
|
|
|
|
if (err)
|
|
|
|
goto err_out;
|
|
|
|
|
nl80211: Event notifications for MLME events
Add new nl80211 event notifications (and a new multicast group, "mlme")
for informing user space about received and processed Authentication,
(Re)Association Response, Deauthentication, and Disassociation frames in
station and IBSS modes (i.e., MLME SAP interface primitives
MLME-AUTHENTICATE.confirm, MLME-ASSOCIATE.confirm,
MLME-REASSOCIATE.confirm, MLME-DEAUTHENTICATE.indicate, and
MLME-DISASSOCIATE.indication). The event data is encapsulated as the 802.11
management frame since we already have the frame in that format and it
includes all the needed information.
This is the initial step in providing MLME SAP interface for
authentication and association with nl80211. In other words, kernel code
will act as the MLME and a user space application can control it as the
SME.
Signed-off-by: Jouni Malinen <j@w1.fi>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-03-19 11:39:21 +00:00
|
|
|
err = genl_register_mc_group(&nl80211_fam, &nl80211_mlme_mcgrp);
|
|
|
|
if (err)
|
|
|
|
goto err_out;
|
|
|
|
|
2009-07-01 19:26:51 +00:00
|
|
|
#ifdef CONFIG_NL80211_TESTMODE
|
|
|
|
err = genl_register_mc_group(&nl80211_fam, &nl80211_testmode_mcgrp);
|
|
|
|
if (err)
|
|
|
|
goto err_out;
|
|
|
|
#endif
|
|
|
|
|
2010-02-15 10:53:10 +00:00
|
|
|
err = netlink_register_notifier(&nl80211_netlink_notifier);
|
|
|
|
if (err)
|
|
|
|
goto err_out;
|
|
|
|
|
2007-09-20 17:09:35 +00:00
|
|
|
return 0;
|
|
|
|
err_out:
|
|
|
|
genl_unregister_family(&nl80211_fam);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
void nl80211_exit(void)
|
|
|
|
{
|
2010-02-15 10:53:10 +00:00
|
|
|
netlink_unregister_notifier(&nl80211_netlink_notifier);
|
2007-09-20 17:09:35 +00:00
|
|
|
genl_unregister_family(&nl80211_fam);
|
|
|
|
}
|