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>
|
2014-09-03 12:24:58 +00:00
|
|
|
* Copyright 2013-2014 Intel Mobile Communications GmbH
|
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>
|
2013-01-23 21:57:40 +00:00
|
|
|
#include <net/inet_connection_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"
|
2012-06-27 14:19:42 +00:00
|
|
|
#include "rdev-ops.h"
|
2007-09-20 17:09:35 +00:00
|
|
|
|
2011-08-10 20:54:35 +00:00
|
|
|
static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
|
|
|
|
struct genl_info *info,
|
|
|
|
struct cfg80211_crypto_settings *settings,
|
|
|
|
int cipher_limit);
|
|
|
|
|
2013-11-14 16:14:45 +00:00
|
|
|
static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
|
2010-10-04 19:36:35 +00:00
|
|
|
struct genl_info *info);
|
2013-11-14 16:14:45 +00:00
|
|
|
static void nl80211_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
|
2010-10-04 19:36:35 +00:00
|
|
|
struct genl_info *info);
|
|
|
|
|
2007-09-20 17:09:35 +00:00
|
|
|
/* the netlink family */
|
|
|
|
static struct genl_family nl80211_fam = {
|
2013-04-28 23:22:06 +00:00
|
|
|
.id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */
|
|
|
|
.name = NL80211_GENL_NAME, /* have users key off the name instead */
|
|
|
|
.hdrsize = 0, /* no private header */
|
|
|
|
.version = 1, /* no particular meaning now */
|
2007-09-20 17:09:35 +00:00
|
|
|
.maxattr = NL80211_ATTR_MAX,
|
2009-07-13 22:33:35 +00:00
|
|
|
.netnsok = true,
|
2010-10-04 19:36:35 +00:00
|
|
|
.pre_doit = nl80211_pre_doit,
|
|
|
|
.post_doit = nl80211_post_doit,
|
2007-09-20 17:09:35 +00:00
|
|
|
};
|
|
|
|
|
2013-11-19 14:19:39 +00:00
|
|
|
/* multicast groups */
|
|
|
|
enum nl80211_multicast_groups {
|
|
|
|
NL80211_MCGRP_CONFIG,
|
|
|
|
NL80211_MCGRP_SCAN,
|
|
|
|
NL80211_MCGRP_REGULATORY,
|
|
|
|
NL80211_MCGRP_MLME,
|
2013-12-18 13:43:31 +00:00
|
|
|
NL80211_MCGRP_VENDOR,
|
2013-11-19 14:19:39 +00:00
|
|
|
NL80211_MCGRP_TESTMODE /* keep last - ifdef! */
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct genl_multicast_group nl80211_mcgrps[] = {
|
|
|
|
[NL80211_MCGRP_CONFIG] = { .name = "config", },
|
|
|
|
[NL80211_MCGRP_SCAN] = { .name = "scan", },
|
|
|
|
[NL80211_MCGRP_REGULATORY] = { .name = "regulatory", },
|
|
|
|
[NL80211_MCGRP_MLME] = { .name = "mlme", },
|
2013-12-18 13:43:31 +00:00
|
|
|
[NL80211_MCGRP_VENDOR] = { .name = "vendor", },
|
2013-11-19 14:19:39 +00:00
|
|
|
#ifdef CONFIG_NL80211_TESTMODE
|
|
|
|
[NL80211_MCGRP_TESTMODE] = { .name = "testmode", }
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
2012-06-15 12:33:17 +00:00
|
|
|
/* returns ERR_PTR values */
|
|
|
|
static struct wireless_dev *
|
|
|
|
__cfg80211_wdev_from_attrs(struct net *netns, struct nlattr **attrs)
|
2007-09-20 17:09:35 +00:00
|
|
|
{
|
2012-06-15 12:33:17 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
|
|
|
struct wireless_dev *result = NULL;
|
|
|
|
bool have_ifidx = attrs[NL80211_ATTR_IFINDEX];
|
|
|
|
bool have_wdev_id = attrs[NL80211_ATTR_WDEV];
|
|
|
|
u64 wdev_id;
|
|
|
|
int wiphy_idx = -1;
|
|
|
|
int ifidx = -1;
|
2007-09-20 17:09:35 +00:00
|
|
|
|
2013-05-08 19:45:15 +00:00
|
|
|
ASSERT_RTNL();
|
2007-09-20 17:09:35 +00:00
|
|
|
|
2012-06-15 12:33:17 +00:00
|
|
|
if (!have_ifidx && !have_wdev_id)
|
|
|
|
return ERR_PTR(-EINVAL);
|
2007-09-20 17:09:35 +00:00
|
|
|
|
2012-06-15 12:33:17 +00:00
|
|
|
if (have_ifidx)
|
|
|
|
ifidx = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]);
|
|
|
|
if (have_wdev_id) {
|
|
|
|
wdev_id = nla_get_u64(attrs[NL80211_ATTR_WDEV]);
|
|
|
|
wiphy_idx = wdev_id >> 32;
|
2007-09-20 17:09:35 +00:00
|
|
|
}
|
|
|
|
|
2012-06-15 12:33:17 +00:00
|
|
|
list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
|
|
|
|
struct wireless_dev *wdev;
|
|
|
|
|
|
|
|
if (wiphy_net(&rdev->wiphy) != netns)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (have_wdev_id && rdev->wiphy_idx != wiphy_idx)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
list_for_each_entry(wdev, &rdev->wdev_list, list) {
|
|
|
|
if (have_ifidx && wdev->netdev &&
|
|
|
|
wdev->netdev->ifindex == ifidx) {
|
|
|
|
result = wdev;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (have_wdev_id && wdev->identifier == (u32)wdev_id) {
|
|
|
|
result = wdev;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (result)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (result)
|
|
|
|
return result;
|
|
|
|
return ERR_PTR(-ENODEV);
|
2007-09-20 17:09:35 +00:00
|
|
|
}
|
|
|
|
|
2012-06-15 11:32:49 +00:00
|
|
|
static struct cfg80211_registered_device *
|
2012-06-15 12:18:32 +00:00
|
|
|
__cfg80211_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
|
2012-06-15 11:32:49 +00:00
|
|
|
{
|
2012-06-15 12:09:58 +00:00
|
|
|
struct cfg80211_registered_device *rdev = NULL, *tmp;
|
|
|
|
struct net_device *netdev;
|
2012-06-15 11:32:49 +00:00
|
|
|
|
2013-05-08 19:45:15 +00:00
|
|
|
ASSERT_RTNL();
|
2012-06-15 11:32:49 +00:00
|
|
|
|
2012-06-15 12:18:32 +00:00
|
|
|
if (!attrs[NL80211_ATTR_WIPHY] &&
|
2012-06-15 12:33:17 +00:00
|
|
|
!attrs[NL80211_ATTR_IFINDEX] &&
|
|
|
|
!attrs[NL80211_ATTR_WDEV])
|
2012-06-15 12:09:58 +00:00
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
|
2012-06-15 12:18:32 +00:00
|
|
|
if (attrs[NL80211_ATTR_WIPHY])
|
2012-06-15 12:09:58 +00:00
|
|
|
rdev = cfg80211_rdev_by_wiphy_idx(
|
2012-06-15 12:18:32 +00:00
|
|
|
nla_get_u32(attrs[NL80211_ATTR_WIPHY]));
|
2012-06-15 11:32:49 +00:00
|
|
|
|
2012-06-15 12:33:17 +00:00
|
|
|
if (attrs[NL80211_ATTR_WDEV]) {
|
|
|
|
u64 wdev_id = nla_get_u64(attrs[NL80211_ATTR_WDEV]);
|
|
|
|
struct wireless_dev *wdev;
|
|
|
|
bool found = false;
|
|
|
|
|
|
|
|
tmp = cfg80211_rdev_by_wiphy_idx(wdev_id >> 32);
|
|
|
|
if (tmp) {
|
|
|
|
/* make sure wdev exists */
|
|
|
|
list_for_each_entry(wdev, &tmp->wdev_list, list) {
|
|
|
|
if (wdev->identifier != (u32)wdev_id)
|
|
|
|
continue;
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found)
|
|
|
|
tmp = NULL;
|
|
|
|
|
|
|
|
if (rdev && tmp != rdev)
|
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
rdev = tmp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-15 12:18:32 +00:00
|
|
|
if (attrs[NL80211_ATTR_IFINDEX]) {
|
|
|
|
int ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]);
|
2014-01-15 02:23:45 +00:00
|
|
|
netdev = __dev_get_by_index(netns, ifindex);
|
2012-06-15 12:09:58 +00:00
|
|
|
if (netdev) {
|
|
|
|
if (netdev->ieee80211_ptr)
|
2014-04-21 04:53:03 +00:00
|
|
|
tmp = wiphy_to_rdev(
|
|
|
|
netdev->ieee80211_ptr->wiphy);
|
2012-06-15 12:09:58 +00:00
|
|
|
else
|
|
|
|
tmp = NULL;
|
|
|
|
|
|
|
|
/* not wireless device -- return error */
|
|
|
|
if (!tmp)
|
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
|
|
|
|
/* mismatch -- return error */
|
|
|
|
if (rdev && tmp != rdev)
|
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
|
|
|
|
rdev = tmp;
|
2012-06-15 11:32:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-15 12:14:22 +00:00
|
|
|
if (!rdev)
|
|
|
|
return ERR_PTR(-ENODEV);
|
2012-06-15 11:32:49 +00:00
|
|
|
|
2012-06-15 12:14:22 +00:00
|
|
|
if (netns != wiphy_net(&rdev->wiphy))
|
|
|
|
return ERR_PTR(-ENODEV);
|
|
|
|
|
|
|
|
return rdev;
|
2012-06-15 11:32:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This function returns a pointer to the driver
|
|
|
|
* that the genl_info item that is passed refers to.
|
|
|
|
*
|
|
|
|
* The result of this can be a PTR_ERR and hence must
|
|
|
|
* be checked with IS_ERR() for errors.
|
|
|
|
*/
|
|
|
|
static struct cfg80211_registered_device *
|
2012-06-15 12:14:22 +00:00
|
|
|
cfg80211_get_dev_from_info(struct net *netns, struct genl_info *info)
|
2012-06-15 11:32:49 +00:00
|
|
|
{
|
2013-05-08 19:45:15 +00:00
|
|
|
return __cfg80211_rdev_from_attrs(netns, info->attrs);
|
2012-06-15 11:32:49 +00:00
|
|
|
}
|
|
|
|
|
2007-09-20 17:09:35 +00:00
|
|
|
/* 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 },
|
2012-11-08 22:14:50 +00:00
|
|
|
|
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 },
|
2012-11-08 22:14:50 +00:00
|
|
|
[NL80211_ATTR_CHANNEL_WIDTH] = { .type = NLA_U32 },
|
|
|
|
[NL80211_ATTR_CENTER_FREQ1] = { .type = NLA_U32 },
|
|
|
|
[NL80211_ATTR_CENTER_FREQ2] = { .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 },
|
2014-09-04 21:57:40 +00:00
|
|
|
[NL80211_ATTR_WIPHY_DYN_ACK] = { .type = NLA_FLAG },
|
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
|
|
|
|
2011-11-24 16:13:56 +00:00
|
|
|
[NL80211_ATTR_MAC] = { .len = ETH_ALEN },
|
|
|
|
[NL80211_ATTR_PREV_BSSID] = { .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 },
|
2011-11-02 21:36:31 +00:00
|
|
|
[NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 },
|
2010-10-05 17:39:30 +00:00
|
|
|
[NL80211_ATTR_KEY_TYPE] = { .type = NLA_U32 },
|
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,
|
2012-06-11 03:59:36 +00:00
|
|
|
.len = IEEE80211_MAX_MESH_ID_LEN },
|
2008-02-23 14:17:06 +00:00
|
|
|
[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 },
|
2010-11-19 11:40:25 +00:00
|
|
|
[NL80211_ATTR_BSS_HT_OPMODE] = { .type = NLA_U16 },
|
2008-08-25 08:58:58 +00:00
|
|
|
|
2010-12-17 01:37:48 +00:00
|
|
|
[NL80211_ATTR_MESH_CONFIG] = { .type = NLA_NESTED },
|
2011-04-07 22:08:28 +00:00
|
|
|
[NL80211_ATTR_SUPPORT_MESH_AUTH] = { .type = NLA_FLAG },
|
2008-10-21 19:03:48 +00:00
|
|
|
|
2011-11-03 08:27:01 +00:00
|
|
|
[NL80211_ATTR_HT_CAPABILITY] = { .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 },
|
2010-08-27 11:26:53 +00:00
|
|
|
[NL80211_ATTR_CONTROL_PORT_ETHERTYPE] = { .type = NLA_U16 },
|
|
|
|
[NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT] = { .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 },
|
2010-08-12 13:38:38 +00:00
|
|
|
[NL80211_ATTR_FRAME_TYPE] = { .type = NLA_U16 },
|
cfg80211: Add nl80211 antenna configuration
Allow setting of TX and RX antennas configuration via nl80211.
The antenna configuration is defined as a bitmap of allowed antennas to use.
This API can be used to mask out antennas which are not attached or should not
be used for other reasons like regulatory concerns or special setups.
Separate bitmaps are used for RX and TX to allow configuring different antennas
for receiving and transmitting. Each bitmap is 32 bit long, each bit
representing one antenna, starting with antenna 1 at the first bit. If an
antenna bit is set, this means the driver is allowed to use this antenna for RX
or TX respectively; if the bit is not set the hardware is not allowed to use
this antenna.
Using bitmaps has the benefit of allowing for a flexible configuration
interface which can support many different configurations and which can be used
for 802.11n as well as non-802.11n devices. Instead of relying on some hardware
specific assumptions, drivers can use this information to know which antennas
are actually attached to the system and derive their capabilities based on
that.
802.11n devices should enable or disable chains, based on which antennas are
present (If all antennas belonging to a particular chain are disabled, the
entire chain should be disabled). HT capabilities (like STBC, TX Beamforming,
Antenna selection) should be calculated based on the available chains after
applying the antenna masks. Should a 802.11n device have diversity antennas
attached to one of their chains, diversity can be enabled or disabled based on
the antenna information.
Non-802.11n drivers can use the antenna masks to select RX and TX antennas and
to enable or disable antenna diversity.
While covering chainmasks for 802.11n and the standard "legacy" modes "fixed
antenna 1", "fixed antenna 2" and "diversity" this API also allows more rare,
but useful configurations as follows:
1) Send on antenna 1, receive on antenna 2 (or vice versa). This can be used to
have a low gain antenna for TX in order to keep within the regulatory
constraints and a high gain antenna for RX in order to receive weaker signals
("speak softly, but listen harder"). This can be useful for building long-shot
outdoor links. Another usage of this setup is having a low-noise pre-amplifier
on antenna 1 and a power amplifier on the other antenna. This way transmit
noise is mostly kept out of the low noise receive channel.
(This would be bitmaps: tx 1 rx 2).
2) Another similar setup is: Use RX diversity on both antennas, but always send
on antenna 1. Again that would allow us to benefit from a higher gain RX
antenna, while staying within the legal limits.
(This would be: tx 0 rx 3).
3) And finally there can be special experimental setups in research and
development even with pre 802.11n hardware where more than 2 antennas are
available. It's good to keep the API simple, yet flexible.
Signed-off-by: Bruno Randolf <br1@einfach.org>
--
v7: Made bitmasks 32 bit wide and rebased to latest wireless-testing.
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2010-11-10 03:50:50 +00:00
|
|
|
[NL80211_ATTR_WIPHY_ANTENNA_TX] = { .type = NLA_U32 },
|
|
|
|
[NL80211_ATTR_WIPHY_ANTENNA_RX] = { .type = NLA_U32 },
|
2010-11-11 14:07:22 +00:00
|
|
|
[NL80211_ATTR_MCAST_RATE] = { .type = NLA_U32 },
|
2010-11-25 09:02:29 +00:00
|
|
|
[NL80211_ATTR_OFFCHANNEL_TX_OK] = { .type = NLA_FLAG },
|
2010-12-09 18:58:59 +00:00
|
|
|
[NL80211_ATTR_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED },
|
2011-05-04 13:37:28 +00:00
|
|
|
[NL80211_ATTR_WOWLAN_TRIGGERS] = { .type = NLA_NESTED },
|
2011-05-03 23:57:11 +00:00
|
|
|
[NL80211_ATTR_STA_PLINK_STATE] = { .type = NLA_U8 },
|
2011-05-11 14:09:37 +00:00
|
|
|
[NL80211_ATTR_SCHED_SCAN_INTERVAL] = { .type = NLA_U32 },
|
2011-07-05 14:35:40 +00:00
|
|
|
[NL80211_ATTR_REKEY_DATA] = { .type = NLA_NESTED },
|
2011-07-18 16:08:35 +00:00
|
|
|
[NL80211_ATTR_SCAN_SUPP_RATES] = { .type = NLA_NESTED },
|
2011-08-10 20:53:31 +00:00
|
|
|
[NL80211_ATTR_HIDDEN_SSID] = { .type = NLA_U32 },
|
2011-08-10 20:55:56 +00:00
|
|
|
[NL80211_ATTR_IE_PROBE_RESP] = { .type = NLA_BINARY,
|
|
|
|
.len = IEEE80211_MAX_DATA_LEN },
|
|
|
|
[NL80211_ATTR_IE_ASSOC_RESP] = { .type = NLA_BINARY,
|
|
|
|
.len = IEEE80211_MAX_DATA_LEN },
|
2011-08-29 08:53:03 +00:00
|
|
|
[NL80211_ATTR_ROAM_SUPPORT] = { .type = NLA_FLAG },
|
2011-08-31 13:01:48 +00:00
|
|
|
[NL80211_ATTR_SCHED_SCAN_MATCH] = { .type = NLA_NESTED },
|
2011-09-25 09:23:30 +00:00
|
|
|
[NL80211_ATTR_TX_NO_CCK_RATE] = { .type = NLA_FLAG },
|
2011-09-28 11:12:50 +00:00
|
|
|
[NL80211_ATTR_TDLS_ACTION] = { .type = NLA_U8 },
|
|
|
|
[NL80211_ATTR_TDLS_DIALOG_TOKEN] = { .type = NLA_U8 },
|
|
|
|
[NL80211_ATTR_TDLS_OPERATION] = { .type = NLA_U8 },
|
|
|
|
[NL80211_ATTR_TDLS_SUPPORT] = { .type = NLA_FLAG },
|
|
|
|
[NL80211_ATTR_TDLS_EXTERNAL_SETUP] = { .type = NLA_FLAG },
|
2014-06-11 14:18:21 +00:00
|
|
|
[NL80211_ATTR_TDLS_INITIATOR] = { .type = NLA_FLAG },
|
2011-11-04 10:18:21 +00:00
|
|
|
[NL80211_ATTR_DONT_WAIT_FOR_ACK] = { .type = NLA_FLAG },
|
2011-11-10 09:28:56 +00:00
|
|
|
[NL80211_ATTR_PROBE_RESP] = { .type = NLA_BINARY,
|
|
|
|
.len = IEEE80211_MAX_DATA_LEN },
|
2011-10-11 17:59:02 +00:00
|
|
|
[NL80211_ATTR_DFS_REGION] = { .type = NLA_U8 },
|
2011-11-18 19:31:59 +00:00
|
|
|
[NL80211_ATTR_DISABLE_HT] = { .type = NLA_FLAG },
|
|
|
|
[NL80211_ATTR_HT_CAPABILITY_MASK] = {
|
|
|
|
.len = NL80211_HT_CAPABILITY_LEN
|
|
|
|
},
|
2011-11-18 13:20:43 +00:00
|
|
|
[NL80211_ATTR_NOACK_MAP] = { .type = NLA_U16 },
|
2012-03-02 10:20:02 +00:00
|
|
|
[NL80211_ATTR_INACTIVITY_TIMEOUT] = { .type = NLA_U16 },
|
2012-03-07 11:57:12 +00:00
|
|
|
[NL80211_ATTR_BG_SCAN_PERIOD] = { .type = NLA_U16 },
|
2012-06-15 12:33:17 +00:00
|
|
|
[NL80211_ATTR_WDEV] = { .type = NLA_U64 },
|
2012-07-12 18:49:18 +00:00
|
|
|
[NL80211_ATTR_USER_REG_HINT_TYPE] = { .type = NLA_U32 },
|
2012-09-30 16:29:39 +00:00
|
|
|
[NL80211_ATTR_SAE_DATA] = { .type = NLA_BINARY, },
|
2012-10-11 08:04:52 +00:00
|
|
|
[NL80211_ATTR_VHT_CAPABILITY] = { .len = NL80211_VHT_CAPABILITY_LEN },
|
2012-10-12 04:03:31 +00:00
|
|
|
[NL80211_ATTR_SCAN_FLAGS] = { .type = NLA_U32 },
|
2012-11-14 14:17:28 +00:00
|
|
|
[NL80211_ATTR_P2P_CTWINDOW] = { .type = NLA_U8 },
|
|
|
|
[NL80211_ATTR_P2P_OPPPS] = { .type = NLA_U8 },
|
2013-01-18 05:48:45 +00:00
|
|
|
[NL80211_ATTR_ACL_POLICY] = {. type = NLA_U32 },
|
|
|
|
[NL80211_ATTR_MAC_ADDRS] = { .type = NLA_NESTED },
|
2013-02-14 19:10:13 +00:00
|
|
|
[NL80211_ATTR_STA_CAPABILITY] = { .type = NLA_U16 },
|
|
|
|
[NL80211_ATTR_STA_EXT_CAPABILITY] = { .type = NLA_BINARY, },
|
2013-02-14 15:19:38 +00:00
|
|
|
[NL80211_ATTR_SPLIT_WIPHY_DUMP] = { .type = NLA_FLAG, },
|
2013-02-21 16:36:01 +00:00
|
|
|
[NL80211_ATTR_DISABLE_VHT] = { .type = NLA_FLAG },
|
|
|
|
[NL80211_ATTR_VHT_CAPABILITY_MASK] = {
|
|
|
|
.len = NL80211_VHT_CAPABILITY_LEN,
|
|
|
|
},
|
2013-02-27 15:14:27 +00:00
|
|
|
[NL80211_ATTR_MDID] = { .type = NLA_U16 },
|
|
|
|
[NL80211_ATTR_IE_RIC] = { .type = NLA_BINARY,
|
|
|
|
.len = IEEE80211_MAX_DATA_LEN },
|
2013-05-16 17:11:08 +00:00
|
|
|
[NL80211_ATTR_PEER_AID] = { .type = NLA_U16 },
|
2013-07-11 14:09:05 +00:00
|
|
|
[NL80211_ATTR_CH_SWITCH_COUNT] = { .type = NLA_U32 },
|
|
|
|
[NL80211_ATTR_CH_SWITCH_BLOCK_TX] = { .type = NLA_FLAG },
|
|
|
|
[NL80211_ATTR_CSA_IES] = { .type = NLA_NESTED },
|
2014-05-09 11:11:46 +00:00
|
|
|
[NL80211_ATTR_CSA_C_OFF_BEACON] = { .type = NLA_BINARY },
|
|
|
|
[NL80211_ATTR_CSA_C_OFF_PRESP] = { .type = NLA_BINARY },
|
2013-10-09 15:15:21 +00:00
|
|
|
[NL80211_ATTR_STA_SUPPORTED_CHANNELS] = { .type = NLA_BINARY },
|
|
|
|
[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES] = { .type = NLA_BINARY },
|
2013-10-07 16:41:05 +00:00
|
|
|
[NL80211_ATTR_HANDLE_DFS] = { .type = NLA_FLAG },
|
2013-12-03 09:04:59 +00:00
|
|
|
[NL80211_ATTR_OPMODE_NOTIF] = { .type = NLA_U8 },
|
2013-11-13 12:37:47 +00:00
|
|
|
[NL80211_ATTR_VENDOR_ID] = { .type = NLA_U32 },
|
|
|
|
[NL80211_ATTR_VENDOR_SUBCMD] = { .type = NLA_U32 },
|
|
|
|
[NL80211_ATTR_VENDOR_DATA] = { .type = NLA_BINARY },
|
2013-12-17 07:01:30 +00:00
|
|
|
[NL80211_ATTR_QOS_MAP] = { .type = NLA_BINARY,
|
|
|
|
.len = IEEE80211_QOS_MAP_LEN_MAX },
|
2014-01-14 22:00:47 +00:00
|
|
|
[NL80211_ATTR_MAC_HINT] = { .len = ETH_ALEN },
|
|
|
|
[NL80211_ATTR_WIPHY_FREQ_HINT] = { .type = NLA_U32 },
|
2014-02-20 10:52:09 +00:00
|
|
|
[NL80211_ATTR_TDLS_PEER_CAPABILITY] = { .type = NLA_U32 },
|
2014-03-24 16:57:27 +00:00
|
|
|
[NL80211_ATTR_IFACE_SOCKET_OWNER] = { .type = NLA_FLAG },
|
2014-05-09 11:11:44 +00:00
|
|
|
[NL80211_ATTR_CSA_C_OFFSETS_TX] = { .type = NLA_BINARY },
|
2014-09-03 12:25:01 +00:00
|
|
|
[NL80211_ATTR_USE_RRM] = { .type = NLA_FLAG },
|
2014-09-09 19:55:35 +00:00
|
|
|
[NL80211_ATTR_TSID] = { .type = NLA_U8 },
|
|
|
|
[NL80211_ATTR_USER_PRIO] = { .type = NLA_U8 },
|
|
|
|
[NL80211_ATTR_ADMITTED_TIME] = { .type = NLA_U16 },
|
2014-09-10 11:07:34 +00:00
|
|
|
[NL80211_ATTR_SMPS_MODE] = { .type = NLA_U8 },
|
2007-09-20 17:09:35 +00:00
|
|
|
};
|
|
|
|
|
2010-10-05 17:39:30 +00:00
|
|
|
/* policy for the key 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 },
|
2011-11-02 21:36:31 +00:00
|
|
|
[NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 },
|
2009-07-08 11:29:08 +00:00
|
|
|
[NL80211_KEY_DEFAULT] = { .type = NLA_FLAG },
|
|
|
|
[NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG },
|
2010-10-05 17:39:30 +00:00
|
|
|
[NL80211_KEY_TYPE] = { .type = NLA_U32 },
|
2010-12-09 18:58:59 +00:00
|
|
|
[NL80211_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED },
|
|
|
|
};
|
|
|
|
|
|
|
|
/* policy for the key default flags */
|
|
|
|
static const struct nla_policy
|
|
|
|
nl80211_key_default_policy[NUM_NL80211_KEY_DEFAULT_TYPES] = {
|
|
|
|
[NL80211_KEY_DEFAULT_TYPE_UNICAST] = { .type = NLA_FLAG },
|
|
|
|
[NL80211_KEY_DEFAULT_TYPE_MULTICAST] = { .type = NLA_FLAG },
|
2009-07-08 11:29:08 +00:00
|
|
|
};
|
|
|
|
|
2011-05-04 13:37:28 +00:00
|
|
|
/* policy for WoWLAN attributes */
|
|
|
|
static const struct nla_policy
|
|
|
|
nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = {
|
|
|
|
[NL80211_WOWLAN_TRIG_ANY] = { .type = NLA_FLAG },
|
|
|
|
[NL80211_WOWLAN_TRIG_DISCONNECT] = { .type = NLA_FLAG },
|
|
|
|
[NL80211_WOWLAN_TRIG_MAGIC_PKT] = { .type = NLA_FLAG },
|
|
|
|
[NL80211_WOWLAN_TRIG_PKT_PATTERN] = { .type = NLA_NESTED },
|
2011-07-13 08:48:55 +00:00
|
|
|
[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE] = { .type = NLA_FLAG },
|
|
|
|
[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST] = { .type = NLA_FLAG },
|
|
|
|
[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG },
|
|
|
|
[NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG },
|
2013-01-23 21:57:40 +00:00
|
|
|
[NL80211_WOWLAN_TRIG_TCP_CONNECTION] = { .type = NLA_NESTED },
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct nla_policy
|
|
|
|
nl80211_wowlan_tcp_policy[NUM_NL80211_WOWLAN_TCP] = {
|
|
|
|
[NL80211_WOWLAN_TCP_SRC_IPV4] = { .type = NLA_U32 },
|
|
|
|
[NL80211_WOWLAN_TCP_DST_IPV4] = { .type = NLA_U32 },
|
|
|
|
[NL80211_WOWLAN_TCP_DST_MAC] = { .len = ETH_ALEN },
|
|
|
|
[NL80211_WOWLAN_TCP_SRC_PORT] = { .type = NLA_U16 },
|
|
|
|
[NL80211_WOWLAN_TCP_DST_PORT] = { .type = NLA_U16 },
|
|
|
|
[NL80211_WOWLAN_TCP_DATA_PAYLOAD] = { .len = 1 },
|
|
|
|
[NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ] = {
|
|
|
|
.len = sizeof(struct nl80211_wowlan_tcp_data_seq)
|
|
|
|
},
|
|
|
|
[NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN] = {
|
|
|
|
.len = sizeof(struct nl80211_wowlan_tcp_data_token)
|
|
|
|
},
|
|
|
|
[NL80211_WOWLAN_TCP_DATA_INTERVAL] = { .type = NLA_U32 },
|
|
|
|
[NL80211_WOWLAN_TCP_WAKE_PAYLOAD] = { .len = 1 },
|
|
|
|
[NL80211_WOWLAN_TCP_WAKE_MASK] = { .len = 1 },
|
2011-05-04 13:37:28 +00:00
|
|
|
};
|
|
|
|
|
2013-06-28 18:51:26 +00:00
|
|
|
/* policy for coalesce rule attributes */
|
|
|
|
static const struct nla_policy
|
|
|
|
nl80211_coalesce_policy[NUM_NL80211_ATTR_COALESCE_RULE] = {
|
|
|
|
[NL80211_ATTR_COALESCE_RULE_DELAY] = { .type = NLA_U32 },
|
|
|
|
[NL80211_ATTR_COALESCE_RULE_CONDITION] = { .type = NLA_U32 },
|
|
|
|
[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN] = { .type = NLA_NESTED },
|
|
|
|
};
|
|
|
|
|
2011-07-05 14:35:40 +00:00
|
|
|
/* policy for GTK rekey offload attributes */
|
|
|
|
static const struct nla_policy
|
|
|
|
nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = {
|
|
|
|
[NL80211_REKEY_DATA_KEK] = { .len = NL80211_KEK_LEN },
|
|
|
|
[NL80211_REKEY_DATA_KCK] = { .len = NL80211_KCK_LEN },
|
|
|
|
[NL80211_REKEY_DATA_REPLAY_CTR] = { .len = NL80211_REPLAY_CTR_LEN },
|
|
|
|
};
|
|
|
|
|
2011-08-31 13:01:48 +00:00
|
|
|
static const struct nla_policy
|
|
|
|
nl80211_match_policy[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1] = {
|
2012-06-13 09:17:11 +00:00
|
|
|
[NL80211_SCHED_SCAN_MATCH_ATTR_SSID] = { .type = NLA_BINARY,
|
2011-08-31 13:01:48 +00:00
|
|
|
.len = IEEE80211_MAX_SSID_LEN },
|
2012-06-21 18:09:54 +00:00
|
|
|
[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI] = { .type = NLA_U32 },
|
2011-08-31 13:01:48 +00:00
|
|
|
};
|
|
|
|
|
2013-04-18 23:02:55 +00:00
|
|
|
static int nl80211_prepare_wdev_dump(struct sk_buff *skb,
|
|
|
|
struct netlink_callback *cb,
|
|
|
|
struct cfg80211_registered_device **rdev,
|
|
|
|
struct wireless_dev **wdev)
|
2009-11-11 10:30:02 +00:00
|
|
|
{
|
2013-04-18 23:02:55 +00:00
|
|
|
int err;
|
2009-11-11 10:30:02 +00:00
|
|
|
|
2013-04-18 23:02:55 +00:00
|
|
|
rtnl_lock();
|
2009-11-11 10:30:02 +00:00
|
|
|
|
2013-04-18 23:02:55 +00:00
|
|
|
if (!cb->args[0]) {
|
|
|
|
err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
|
|
|
|
nl80211_fam.attrbuf, nl80211_fam.maxattr,
|
|
|
|
nl80211_policy);
|
|
|
|
if (err)
|
|
|
|
goto out_unlock;
|
2010-10-04 19:14:06 +00:00
|
|
|
|
2013-04-18 23:02:55 +00:00
|
|
|
*wdev = __cfg80211_wdev_from_attrs(sock_net(skb->sk),
|
|
|
|
nl80211_fam.attrbuf);
|
|
|
|
if (IS_ERR(*wdev)) {
|
|
|
|
err = PTR_ERR(*wdev);
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
2014-04-21 04:53:03 +00:00
|
|
|
*rdev = wiphy_to_rdev((*wdev)->wiphy);
|
2013-07-30 20:34:28 +00:00
|
|
|
/* 0 is the first index - add 1 to parse only once */
|
|
|
|
cb->args[0] = (*rdev)->wiphy_idx + 1;
|
2013-04-18 23:02:55 +00:00
|
|
|
cb->args[1] = (*wdev)->identifier;
|
|
|
|
} else {
|
2013-07-30 20:34:28 +00:00
|
|
|
/* subtract the 1 again here */
|
|
|
|
struct wiphy *wiphy = wiphy_idx_to_wiphy(cb->args[0] - 1);
|
2013-04-18 23:02:55 +00:00
|
|
|
struct wireless_dev *tmp;
|
2010-10-04 19:14:06 +00:00
|
|
|
|
2013-04-18 23:02:55 +00:00
|
|
|
if (!wiphy) {
|
|
|
|
err = -ENODEV;
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
2014-04-21 04:53:03 +00:00
|
|
|
*rdev = wiphy_to_rdev(wiphy);
|
2013-04-18 23:02:55 +00:00
|
|
|
*wdev = NULL;
|
2010-10-04 19:14:06 +00:00
|
|
|
|
2013-04-18 23:02:55 +00:00
|
|
|
list_for_each_entry(tmp, &(*rdev)->wdev_list, list) {
|
|
|
|
if (tmp->identifier == cb->args[1]) {
|
|
|
|
*wdev = tmp;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2010-10-04 19:14:06 +00:00
|
|
|
|
2013-04-18 23:02:55 +00:00
|
|
|
if (!*wdev) {
|
|
|
|
err = -ENODEV;
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
2010-10-04 19:14:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2013-04-18 23:02:55 +00:00
|
|
|
out_unlock:
|
2010-10-04 19:14:06 +00:00
|
|
|
rtnl_unlock();
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2013-04-18 23:02:55 +00:00
|
|
|
static void nl80211_finish_wdev_dump(struct cfg80211_registered_device *rdev)
|
2010-10-04 19:14:06 +00:00
|
|
|
{
|
|
|
|
rtnl_unlock();
|
|
|
|
}
|
|
|
|
|
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 */
|
2012-09-07 20:12:54 +00:00
|
|
|
static inline void *nl80211hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
|
2007-09-20 17:09:35 +00:00
|
|
|
int flags, u8 cmd)
|
|
|
|
{
|
|
|
|
/* since there is no private header just add the generic one */
|
2012-09-07 20:12:54 +00:00
|
|
|
return genlmsg_put(skb, portid, seq, &nl80211_fam, flags, cmd);
|
2007-09-20 17:09:35 +00:00
|
|
|
}
|
|
|
|
|
2009-04-02 18:08:08 +00:00
|
|
|
static int nl80211_msg_put_channel(struct sk_buff *msg,
|
2013-02-18 22:54:36 +00:00
|
|
|
struct ieee80211_channel *chan,
|
|
|
|
bool large)
|
2009-04-02 18:08:08 +00:00
|
|
|
{
|
2014-04-15 12:37:55 +00:00
|
|
|
/* Some channels must be completely excluded from the
|
|
|
|
* list to protect old user-space tools from breaking
|
|
|
|
*/
|
|
|
|
if (!large && chan->flags &
|
|
|
|
(IEEE80211_CHAN_NO_10MHZ | IEEE80211_CHAN_NO_20MHZ))
|
|
|
|
return 0;
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_FREQ,
|
|
|
|
chan->center_freq))
|
|
|
|
goto nla_put_failure;
|
2009-04-02 18:08:08 +00:00
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if ((chan->flags & IEEE80211_CHAN_DISABLED) &&
|
|
|
|
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_DISABLED))
|
|
|
|
goto nla_put_failure;
|
2013-10-21 17:22:25 +00:00
|
|
|
if (chan->flags & IEEE80211_CHAN_NO_IR) {
|
|
|
|
if (nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_IR))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if (nla_put_flag(msg, __NL80211_FREQUENCY_ATTR_NO_IBSS))
|
|
|
|
goto nla_put_failure;
|
|
|
|
}
|
2013-02-18 22:54:36 +00:00
|
|
|
if (chan->flags & IEEE80211_CHAN_RADAR) {
|
|
|
|
if (nla_put_flag(msg, NL80211_FREQUENCY_ATTR_RADAR))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if (large) {
|
|
|
|
u32 time;
|
|
|
|
|
|
|
|
time = elapsed_jiffies_msecs(chan->dfs_state_entered);
|
|
|
|
|
|
|
|
if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_DFS_STATE,
|
|
|
|
chan->dfs_state))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_DFS_TIME,
|
|
|
|
time))
|
|
|
|
goto nla_put_failure;
|
2014-02-21 18:46:12 +00:00
|
|
|
if (nla_put_u32(msg,
|
|
|
|
NL80211_FREQUENCY_ATTR_DFS_CAC_TIME,
|
|
|
|
chan->dfs_cac_ms))
|
|
|
|
goto nla_put_failure;
|
2013-02-18 22:54:36 +00:00
|
|
|
}
|
|
|
|
}
|
2009-04-02 18:08:08 +00:00
|
|
|
|
2013-02-27 14:39:45 +00:00
|
|
|
if (large) {
|
|
|
|
if ((chan->flags & IEEE80211_CHAN_NO_HT40MINUS) &&
|
|
|
|
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_HT40_MINUS))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if ((chan->flags & IEEE80211_CHAN_NO_HT40PLUS) &&
|
|
|
|
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_HT40_PLUS))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if ((chan->flags & IEEE80211_CHAN_NO_80MHZ) &&
|
|
|
|
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_80MHZ))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if ((chan->flags & IEEE80211_CHAN_NO_160MHZ) &&
|
|
|
|
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_160MHZ))
|
|
|
|
goto nla_put_failure;
|
2014-02-23 07:12:59 +00:00
|
|
|
if ((chan->flags & IEEE80211_CHAN_INDOOR_ONLY) &&
|
|
|
|
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_INDOOR_ONLY))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if ((chan->flags & IEEE80211_CHAN_GO_CONCURRENT) &&
|
|
|
|
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_GO_CONCURRENT))
|
|
|
|
goto nla_put_failure;
|
2014-04-15 12:37:55 +00:00
|
|
|
if ((chan->flags & IEEE80211_CHAN_NO_20MHZ) &&
|
|
|
|
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_20MHZ))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if ((chan->flags & IEEE80211_CHAN_NO_10MHZ) &&
|
|
|
|
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_10MHZ))
|
|
|
|
goto nla_put_failure;
|
2013-02-27 14:39:45 +00:00
|
|
|
}
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
|
|
|
|
DBM_TO_MBM(chan->max_power)))
|
|
|
|
goto nla_put_failure;
|
2009-04-02 18:08:08 +00:00
|
|
|
|
|
|
|
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;
|
2010-10-05 17:39:30 +00:00
|
|
|
int type;
|
2009-07-08 11:29:08 +00:00
|
|
|
bool def, defmgmt;
|
2010-12-09 18:58:59 +00:00
|
|
|
bool def_uni, def_multi;
|
2009-07-08 11:29:08 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
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];
|
|
|
|
|
2010-12-09 18:58:59 +00:00
|
|
|
if (k->def) {
|
|
|
|
k->def_uni = true;
|
|
|
|
k->def_multi = true;
|
|
|
|
}
|
|
|
|
if (k->defmgmt)
|
|
|
|
k->def_multi = true;
|
|
|
|
|
2009-07-08 11:29:08 +00:00
|
|
|
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]);
|
|
|
|
|
2010-10-05 17:39:30 +00:00
|
|
|
if (tb[NL80211_KEY_TYPE]) {
|
|
|
|
k->type = nla_get_u32(tb[NL80211_KEY_TYPE]);
|
|
|
|
if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2010-12-09 18:58:59 +00:00
|
|
|
if (tb[NL80211_KEY_DEFAULT_TYPES]) {
|
|
|
|
struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES];
|
2012-01-20 12:52:37 +00:00
|
|
|
err = nla_parse_nested(kdt, NUM_NL80211_KEY_DEFAULT_TYPES - 1,
|
|
|
|
tb[NL80211_KEY_DEFAULT_TYPES],
|
|
|
|
nl80211_key_default_policy);
|
2010-12-09 18:58:59 +00:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST];
|
|
|
|
k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST];
|
|
|
|
}
|
|
|
|
|
2009-07-08 11:29:08 +00:00
|
|
|
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];
|
|
|
|
|
2010-12-09 18:58:59 +00:00
|
|
|
if (k->def) {
|
|
|
|
k->def_uni = true;
|
|
|
|
k->def_multi = true;
|
|
|
|
}
|
|
|
|
if (k->defmgmt)
|
|
|
|
k->def_multi = true;
|
|
|
|
|
2010-10-05 17:39:30 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_KEY_TYPE]) {
|
|
|
|
k->type = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]);
|
|
|
|
if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2010-12-09 18:58:59 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES]) {
|
|
|
|
struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES];
|
|
|
|
int err = nla_parse_nested(
|
|
|
|
kdt, NUM_NL80211_KEY_DEFAULT_TYPES - 1,
|
|
|
|
info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES],
|
|
|
|
nl80211_key_default_policy);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST];
|
|
|
|
k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST];
|
|
|
|
}
|
|
|
|
|
2009-07-08 11:29:08 +00:00
|
|
|
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;
|
2010-10-05 17:39:30 +00:00
|
|
|
k->type = -1;
|
2009-07-08 11:29:08 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2010-12-09 18:58:59 +00:00
|
|
|
if (k->defmgmt) {
|
|
|
|
if (k->def_uni || !k->def_multi)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2009-07-08 11:29:08 +00:00
|
|
|
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,
|
2012-10-18 04:49:28 +00:00
|
|
|
struct nlattr *keys, bool *no_ht)
|
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 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;
|
2010-12-09 18:58:59 +00:00
|
|
|
if (!parse.def_uni || !parse.def_multi)
|
|
|
|
goto error;
|
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
|
|
|
} else if (parse.defmgmt)
|
|
|
|
goto error;
|
|
|
|
err = cfg80211_validate_key_settings(rdev, &parse.p,
|
2010-10-05 17:39:30 +00:00
|
|
|
parse.idx, false, NULL);
|
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)
|
|
|
|
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);
|
2012-10-18 04:49:28 +00:00
|
|
|
|
|
|
|
if (parse.p.cipher == WLAN_CIPHER_SUITE_WEP40 ||
|
|
|
|
parse.p.cipher == WLAN_CIPHER_SUITE_WEP104) {
|
|
|
|
if (no_ht)
|
|
|
|
*no_ht = true;
|
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
error:
|
|
|
|
kfree(result);
|
|
|
|
return ERR_PTR(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_key_allowed(struct wireless_dev *wdev)
|
|
|
|
{
|
|
|
|
ASSERT_WDEV_LOCK(wdev);
|
|
|
|
|
|
|
|
switch (wdev->iftype) {
|
|
|
|
case NL80211_IFTYPE_AP:
|
|
|
|
case NL80211_IFTYPE_AP_VLAN:
|
2010-09-16 12:58:22 +00:00
|
|
|
case NL80211_IFTYPE_P2P_GO:
|
2011-05-03 23:57:12 +00:00
|
|
|
case NL80211_IFTYPE_MESH_POINT:
|
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
|
|
|
break;
|
|
|
|
case NL80211_IFTYPE_ADHOC:
|
|
|
|
case NL80211_IFTYPE_STATION:
|
2010-09-16 12:58:22 +00:00
|
|
|
case NL80211_IFTYPE_P2P_CLIENT:
|
2013-05-15 22:55:45 +00:00
|
|
|
if (!wdev->current_bss)
|
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
|
|
|
return -ENOLINK;
|
|
|
|
break;
|
2014-10-31 13:16:12 +00:00
|
|
|
case NL80211_IFTYPE_UNSPECIFIED:
|
2014-11-03 09:33:18 +00:00
|
|
|
case NL80211_IFTYPE_OCB:
|
2014-10-31 13:16:12 +00:00
|
|
|
case NL80211_IFTYPE_MONITOR:
|
|
|
|
case NL80211_IFTYPE_P2P_DEVICE:
|
|
|
|
case NL80211_IFTYPE_WDS:
|
|
|
|
case NUM_NL80211_IFTYPES:
|
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
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-01-14 22:01:44 +00:00
|
|
|
static struct ieee80211_channel *nl80211_get_valid_chan(struct wiphy *wiphy,
|
|
|
|
struct nlattr *tb)
|
|
|
|
{
|
|
|
|
struct ieee80211_channel *chan;
|
|
|
|
|
|
|
|
if (tb == NULL)
|
|
|
|
return NULL;
|
|
|
|
chan = ieee80211_get_channel(wiphy, nla_get_u32(tb));
|
|
|
|
if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
|
|
|
|
return NULL;
|
|
|
|
return chan;
|
|
|
|
}
|
|
|
|
|
2011-05-13 08:58:57 +00:00
|
|
|
static int nl80211_put_iftypes(struct sk_buff *msg, u32 attr, u16 ifmodes)
|
|
|
|
{
|
|
|
|
struct nlattr *nl_modes = nla_nest_start(msg, attr);
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!nl_modes)
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
while (ifmodes) {
|
2012-03-29 08:41:26 +00:00
|
|
|
if ((ifmodes & 1) && nla_put_flag(msg, i))
|
|
|
|
goto nla_put_failure;
|
2011-05-13 08:58:57 +00:00
|
|
|
ifmodes >>= 1;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
nla_nest_end(msg, nl_modes);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
return -ENOBUFS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_put_iface_combinations(struct wiphy *wiphy,
|
2013-02-18 22:54:36 +00:00
|
|
|
struct sk_buff *msg,
|
|
|
|
bool large)
|
2011-05-13 08:58:57 +00:00
|
|
|
{
|
|
|
|
struct nlattr *nl_combis;
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
nl_combis = nla_nest_start(msg,
|
|
|
|
NL80211_ATTR_INTERFACE_COMBINATIONS);
|
|
|
|
if (!nl_combis)
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
for (i = 0; i < wiphy->n_iface_combinations; i++) {
|
|
|
|
const struct ieee80211_iface_combination *c;
|
|
|
|
struct nlattr *nl_combi, *nl_limits;
|
|
|
|
|
|
|
|
c = &wiphy->iface_combinations[i];
|
|
|
|
|
|
|
|
nl_combi = nla_nest_start(msg, i + 1);
|
|
|
|
if (!nl_combi)
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
nl_limits = nla_nest_start(msg, NL80211_IFACE_COMB_LIMITS);
|
|
|
|
if (!nl_limits)
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
for (j = 0; j < c->n_limits; j++) {
|
|
|
|
struct nlattr *nl_limit;
|
|
|
|
|
|
|
|
nl_limit = nla_nest_start(msg, j + 1);
|
|
|
|
if (!nl_limit)
|
|
|
|
goto nla_put_failure;
|
2012-03-29 08:41:26 +00:00
|
|
|
if (nla_put_u32(msg, NL80211_IFACE_LIMIT_MAX,
|
|
|
|
c->limits[j].max))
|
|
|
|
goto nla_put_failure;
|
2011-05-13 08:58:57 +00:00
|
|
|
if (nl80211_put_iftypes(msg, NL80211_IFACE_LIMIT_TYPES,
|
|
|
|
c->limits[j].types))
|
|
|
|
goto nla_put_failure;
|
|
|
|
nla_nest_end(msg, nl_limit);
|
|
|
|
}
|
|
|
|
|
|
|
|
nla_nest_end(msg, nl_limits);
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (c->beacon_int_infra_match &&
|
|
|
|
nla_put_flag(msg, NL80211_IFACE_COMB_STA_AP_BI_MATCH))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if (nla_put_u32(msg, NL80211_IFACE_COMB_NUM_CHANNELS,
|
|
|
|
c->num_different_channels) ||
|
|
|
|
nla_put_u32(msg, NL80211_IFACE_COMB_MAXNUM,
|
|
|
|
c->max_interfaces))
|
|
|
|
goto nla_put_failure;
|
2013-02-18 22:54:36 +00:00
|
|
|
if (large &&
|
2014-05-05 09:48:40 +00:00
|
|
|
(nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS,
|
|
|
|
c->radar_detect_widths) ||
|
|
|
|
nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_REGIONS,
|
|
|
|
c->radar_detect_regions)))
|
2013-02-18 22:54:36 +00:00
|
|
|
goto nla_put_failure;
|
2011-05-13 08:58:57 +00:00
|
|
|
|
|
|
|
nla_nest_end(msg, nl_combi);
|
|
|
|
}
|
|
|
|
|
|
|
|
nla_nest_end(msg, nl_combis);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
nla_put_failure:
|
|
|
|
return -ENOBUFS;
|
|
|
|
}
|
|
|
|
|
2013-02-14 15:19:38 +00:00
|
|
|
#ifdef CONFIG_PM
|
2013-02-20 00:02:38 +00:00
|
|
|
static int nl80211_send_wowlan_tcp_caps(struct cfg80211_registered_device *rdev,
|
|
|
|
struct sk_buff *msg)
|
|
|
|
{
|
2013-06-03 15:25:34 +00:00
|
|
|
const struct wiphy_wowlan_tcp_support *tcp = rdev->wiphy.wowlan->tcp;
|
2013-02-20 00:02:38 +00:00
|
|
|
struct nlattr *nl_tcp;
|
|
|
|
|
|
|
|
if (!tcp)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
nl_tcp = nla_nest_start(msg, NL80211_WOWLAN_TRIG_TCP_CONNECTION);
|
|
|
|
if (!nl_tcp)
|
|
|
|
return -ENOBUFS;
|
|
|
|
|
|
|
|
if (nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD,
|
|
|
|
tcp->data_payload_max))
|
|
|
|
return -ENOBUFS;
|
|
|
|
|
|
|
|
if (nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD,
|
|
|
|
tcp->data_payload_max))
|
|
|
|
return -ENOBUFS;
|
|
|
|
|
|
|
|
if (tcp->seq && nla_put_flag(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ))
|
|
|
|
return -ENOBUFS;
|
|
|
|
|
|
|
|
if (tcp->tok && nla_put(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN,
|
|
|
|
sizeof(*tcp->tok), tcp->tok))
|
|
|
|
return -ENOBUFS;
|
|
|
|
|
|
|
|
if (nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_INTERVAL,
|
|
|
|
tcp->data_interval_max))
|
|
|
|
return -ENOBUFS;
|
|
|
|
|
|
|
|
if (nla_put_u32(msg, NL80211_WOWLAN_TCP_WAKE_PAYLOAD,
|
|
|
|
tcp->wake_payload_max))
|
|
|
|
return -ENOBUFS;
|
|
|
|
|
|
|
|
nla_nest_end(msg, nl_tcp);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-02-14 15:19:38 +00:00
|
|
|
static int nl80211_send_wowlan(struct sk_buff *msg,
|
2014-04-21 04:53:02 +00:00
|
|
|
struct cfg80211_registered_device *rdev,
|
2013-02-20 00:02:38 +00:00
|
|
|
bool large)
|
2007-09-20 17:09:35 +00:00
|
|
|
{
|
2013-02-14 15:19:38 +00:00
|
|
|
struct nlattr *nl_wowlan;
|
2007-09-20 17:09:35 +00:00
|
|
|
|
2014-04-21 04:53:02 +00:00
|
|
|
if (!rdev->wiphy.wowlan)
|
2013-02-14 15:19:38 +00:00
|
|
|
return 0;
|
2007-09-20 17:09:35 +00:00
|
|
|
|
2013-02-14 15:19:38 +00:00
|
|
|
nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED);
|
|
|
|
if (!nl_wowlan)
|
|
|
|
return -ENOBUFS;
|
2012-03-29 08:41:26 +00:00
|
|
|
|
2014-04-21 04:53:02 +00:00
|
|
|
if (((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_ANY) &&
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) ||
|
2014-04-21 04:53:02 +00:00
|
|
|
((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_DISCONNECT) &&
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) ||
|
2014-04-21 04:53:02 +00:00
|
|
|
((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT) &&
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) ||
|
2014-04-21 04:53:02 +00:00
|
|
|
((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) &&
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED)) ||
|
2014-04-21 04:53:02 +00:00
|
|
|
((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) &&
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) ||
|
2014-04-21 04:53:02 +00:00
|
|
|
((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) &&
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) ||
|
2014-04-21 04:53:02 +00:00
|
|
|
((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) &&
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) ||
|
2014-04-21 04:53:02 +00:00
|
|
|
((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE) &&
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE)))
|
|
|
|
return -ENOBUFS;
|
2012-03-29 08:41:26 +00:00
|
|
|
|
2014-04-21 04:53:02 +00:00
|
|
|
if (rdev->wiphy.wowlan->n_patterns) {
|
2013-06-26 02:03:56 +00:00
|
|
|
struct nl80211_pattern_support pat = {
|
2014-04-21 04:53:02 +00:00
|
|
|
.max_patterns = rdev->wiphy.wowlan->n_patterns,
|
|
|
|
.min_pattern_len = rdev->wiphy.wowlan->pattern_min_len,
|
|
|
|
.max_pattern_len = rdev->wiphy.wowlan->pattern_max_len,
|
|
|
|
.max_pkt_offset = rdev->wiphy.wowlan->max_pkt_offset,
|
2013-02-14 15:19:38 +00:00
|
|
|
};
|
2012-03-29 08:41:26 +00:00
|
|
|
|
2013-02-14 15:19:38 +00:00
|
|
|
if (nla_put(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
|
|
|
|
sizeof(pat), &pat))
|
|
|
|
return -ENOBUFS;
|
|
|
|
}
|
2012-03-29 08:41:26 +00:00
|
|
|
|
2014-04-21 04:53:02 +00:00
|
|
|
if (large && nl80211_send_wowlan_tcp_caps(rdev, msg))
|
2013-02-20 00:02:38 +00:00
|
|
|
return -ENOBUFS;
|
|
|
|
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_nest_end(msg, nl_wowlan);
|
2012-03-29 08:41:26 +00:00
|
|
|
|
2013-02-14 15:19:38 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
2012-03-29 08:41:26 +00:00
|
|
|
|
2013-06-28 18:51:26 +00:00
|
|
|
static int nl80211_send_coalesce(struct sk_buff *msg,
|
2014-04-21 04:53:02 +00:00
|
|
|
struct cfg80211_registered_device *rdev)
|
2013-06-28 18:51:26 +00:00
|
|
|
{
|
|
|
|
struct nl80211_coalesce_rule_support rule;
|
|
|
|
|
2014-04-21 04:53:02 +00:00
|
|
|
if (!rdev->wiphy.coalesce)
|
2013-06-28 18:51:26 +00:00
|
|
|
return 0;
|
|
|
|
|
2014-04-21 04:53:02 +00:00
|
|
|
rule.max_rules = rdev->wiphy.coalesce->n_rules;
|
|
|
|
rule.max_delay = rdev->wiphy.coalesce->max_delay;
|
|
|
|
rule.pat.max_patterns = rdev->wiphy.coalesce->n_patterns;
|
|
|
|
rule.pat.min_pattern_len = rdev->wiphy.coalesce->pattern_min_len;
|
|
|
|
rule.pat.max_pattern_len = rdev->wiphy.coalesce->pattern_max_len;
|
|
|
|
rule.pat.max_pkt_offset = rdev->wiphy.coalesce->max_pkt_offset;
|
2013-06-28 18:51:26 +00:00
|
|
|
|
|
|
|
if (nla_put(msg, NL80211_ATTR_COALESCE_RULE, sizeof(rule), &rule))
|
|
|
|
return -ENOBUFS;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-02-14 15:19:38 +00:00
|
|
|
static int nl80211_send_band_rateinfo(struct sk_buff *msg,
|
|
|
|
struct ieee80211_supported_band *sband)
|
|
|
|
{
|
|
|
|
struct nlattr *nl_rates, *nl_rate;
|
|
|
|
struct ieee80211_rate *rate;
|
|
|
|
int i;
|
2011-11-10 09:28:55 +00:00
|
|
|
|
2013-02-14 15:19:38 +00:00
|
|
|
/* add HT info */
|
|
|
|
if (sband->ht_cap.ht_supported &&
|
|
|
|
(nla_put(msg, NL80211_BAND_ATTR_HT_MCS_SET,
|
|
|
|
sizeof(sband->ht_cap.mcs),
|
|
|
|
&sband->ht_cap.mcs) ||
|
|
|
|
nla_put_u16(msg, NL80211_BAND_ATTR_HT_CAPA,
|
|
|
|
sband->ht_cap.cap) ||
|
|
|
|
nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
|
|
|
|
sband->ht_cap.ampdu_factor) ||
|
|
|
|
nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
|
|
|
|
sband->ht_cap.ampdu_density)))
|
|
|
|
return -ENOBUFS;
|
cfg80211: Add nl80211 antenna configuration
Allow setting of TX and RX antennas configuration via nl80211.
The antenna configuration is defined as a bitmap of allowed antennas to use.
This API can be used to mask out antennas which are not attached or should not
be used for other reasons like regulatory concerns or special setups.
Separate bitmaps are used for RX and TX to allow configuring different antennas
for receiving and transmitting. Each bitmap is 32 bit long, each bit
representing one antenna, starting with antenna 1 at the first bit. If an
antenna bit is set, this means the driver is allowed to use this antenna for RX
or TX respectively; if the bit is not set the hardware is not allowed to use
this antenna.
Using bitmaps has the benefit of allowing for a flexible configuration
interface which can support many different configurations and which can be used
for 802.11n as well as non-802.11n devices. Instead of relying on some hardware
specific assumptions, drivers can use this information to know which antennas
are actually attached to the system and derive their capabilities based on
that.
802.11n devices should enable or disable chains, based on which antennas are
present (If all antennas belonging to a particular chain are disabled, the
entire chain should be disabled). HT capabilities (like STBC, TX Beamforming,
Antenna selection) should be calculated based on the available chains after
applying the antenna masks. Should a 802.11n device have diversity antennas
attached to one of their chains, diversity can be enabled or disabled based on
the antenna information.
Non-802.11n drivers can use the antenna masks to select RX and TX antennas and
to enable or disable antenna diversity.
While covering chainmasks for 802.11n and the standard "legacy" modes "fixed
antenna 1", "fixed antenna 2" and "diversity" this API also allows more rare,
but useful configurations as follows:
1) Send on antenna 1, receive on antenna 2 (or vice versa). This can be used to
have a low gain antenna for TX in order to keep within the regulatory
constraints and a high gain antenna for RX in order to receive weaker signals
("speak softly, but listen harder"). This can be useful for building long-shot
outdoor links. Another usage of this setup is having a low-noise pre-amplifier
on antenna 1 and a power amplifier on the other antenna. This way transmit
noise is mostly kept out of the low noise receive channel.
(This would be bitmaps: tx 1 rx 2).
2) Another similar setup is: Use RX diversity on both antennas, but always send
on antenna 1. Again that would allow us to benefit from a higher gain RX
antenna, while staying within the legal limits.
(This would be: tx 0 rx 3).
3) And finally there can be special experimental setups in research and
development even with pre 802.11n hardware where more than 2 antennas are
available. It's good to keep the API simple, yet flexible.
Signed-off-by: Bruno Randolf <br1@einfach.org>
--
v7: Made bitmasks 32 bit wide and rebased to latest wireless-testing.
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2010-11-10 03:50:50 +00:00
|
|
|
|
2013-02-14 15:19:38 +00:00
|
|
|
/* add VHT info */
|
|
|
|
if (sband->vht_cap.vht_supported &&
|
|
|
|
(nla_put(msg, NL80211_BAND_ATTR_VHT_MCS_SET,
|
|
|
|
sizeof(sband->vht_cap.vht_mcs),
|
|
|
|
&sband->vht_cap.vht_mcs) ||
|
|
|
|
nla_put_u32(msg, NL80211_BAND_ATTR_VHT_CAPA,
|
|
|
|
sband->vht_cap.cap)))
|
|
|
|
return -ENOBUFS;
|
2008-08-29 23:26:43 +00:00
|
|
|
|
2013-02-14 15:19:38 +00:00
|
|
|
/* add bitrates */
|
|
|
|
nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
|
|
|
|
if (!nl_rates)
|
|
|
|
return -ENOBUFS;
|
2008-01-24 18:38:39 +00:00
|
|
|
|
2013-02-14 15:19:38 +00:00
|
|
|
for (i = 0; i < sband->n_bitrates; i++) {
|
|
|
|
nl_rate = nla_nest_start(msg, i);
|
|
|
|
if (!nl_rate)
|
|
|
|
return -ENOBUFS;
|
2008-01-24 18:38:39 +00:00
|
|
|
|
2013-02-14 15:19:38 +00:00
|
|
|
rate = &sband->bitrates[i];
|
|
|
|
if (nla_put_u32(msg, NL80211_BITRATE_ATTR_RATE,
|
|
|
|
rate->bitrate))
|
|
|
|
return -ENOBUFS;
|
|
|
|
if ((rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) &&
|
|
|
|
nla_put_flag(msg,
|
|
|
|
NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE))
|
|
|
|
return -ENOBUFS;
|
2008-01-24 18:38:39 +00:00
|
|
|
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_nest_end(msg, nl_rate);
|
|
|
|
}
|
2008-10-09 10:20:13 +00:00
|
|
|
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_nest_end(msg, nl_rates);
|
2012-06-22 07:27:46 +00:00
|
|
|
|
2013-02-14 15:19:38 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2008-01-24 18:38:39 +00:00
|
|
|
|
2013-02-14 15:19:38 +00:00
|
|
|
static int
|
|
|
|
nl80211_send_mgmt_stypes(struct sk_buff *msg,
|
|
|
|
const struct ieee80211_txrx_stypes *mgmt_stypes)
|
|
|
|
{
|
|
|
|
u16 stypes;
|
|
|
|
struct nlattr *nl_ftypes, *nl_ifs;
|
|
|
|
enum nl80211_iftype ift;
|
|
|
|
int i;
|
2008-01-24 18:38:39 +00:00
|
|
|
|
2013-02-14 15:19:38 +00:00
|
|
|
if (!mgmt_stypes)
|
|
|
|
return 0;
|
2009-04-02 18:08:08 +00:00
|
|
|
|
2013-02-14 15:19:38 +00:00
|
|
|
nl_ifs = nla_nest_start(msg, NL80211_ATTR_TX_FRAME_TYPES);
|
|
|
|
if (!nl_ifs)
|
|
|
|
return -ENOBUFS;
|
2008-11-21 17:01:30 +00:00
|
|
|
|
2013-02-14 15:19:38 +00:00
|
|
|
for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
|
|
|
|
nl_ftypes = nla_nest_start(msg, ift);
|
|
|
|
if (!nl_ftypes)
|
|
|
|
return -ENOBUFS;
|
|
|
|
i = 0;
|
|
|
|
stypes = mgmt_stypes[ift].tx;
|
|
|
|
while (stypes) {
|
|
|
|
if ((stypes & 1) &&
|
|
|
|
nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE,
|
|
|
|
(i << 4) | IEEE80211_FTYPE_MGMT))
|
|
|
|
return -ENOBUFS;
|
|
|
|
stypes >>= 1;
|
|
|
|
i++;
|
2008-01-24 18:38:39 +00:00
|
|
|
}
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_nest_end(msg, nl_ftypes);
|
|
|
|
}
|
2008-01-24 18:38:39 +00:00
|
|
|
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_nest_end(msg, nl_ifs);
|
2008-01-24 18:38:39 +00:00
|
|
|
|
2013-02-14 15:19:38 +00:00
|
|
|
nl_ifs = nla_nest_start(msg, NL80211_ATTR_RX_FRAME_TYPES);
|
|
|
|
if (!nl_ifs)
|
|
|
|
return -ENOBUFS;
|
2008-01-24 18:38:39 +00:00
|
|
|
|
2013-02-14 15:19:38 +00:00
|
|
|
for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
|
|
|
|
nl_ftypes = nla_nest_start(msg, ift);
|
|
|
|
if (!nl_ftypes)
|
|
|
|
return -ENOBUFS;
|
|
|
|
i = 0;
|
|
|
|
stypes = mgmt_stypes[ift].rx;
|
|
|
|
while (stypes) {
|
|
|
|
if ((stypes & 1) &&
|
|
|
|
nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE,
|
|
|
|
(i << 4) | IEEE80211_FTYPE_MGMT))
|
|
|
|
return -ENOBUFS;
|
|
|
|
stypes >>= 1;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
nla_nest_end(msg, nl_ftypes);
|
|
|
|
}
|
|
|
|
nla_nest_end(msg, nl_ifs);
|
2008-01-24 18:38:39 +00:00
|
|
|
|
2013-02-14 15:19:38 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2008-01-24 18:38:39 +00:00
|
|
|
|
2013-06-19 08:57:22 +00:00
|
|
|
struct nl80211_dump_wiphy_state {
|
|
|
|
s64 filter_wiphy;
|
|
|
|
long start;
|
|
|
|
long split_start, band_start, chan_start;
|
|
|
|
bool split;
|
|
|
|
};
|
|
|
|
|
2014-04-21 04:53:02 +00:00
|
|
|
static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
|
2014-05-26 11:52:25 +00:00
|
|
|
enum nl80211_commands cmd,
|
2013-02-14 15:19:38 +00:00
|
|
|
struct sk_buff *msg, u32 portid, u32 seq,
|
2013-06-19 08:57:22 +00:00
|
|
|
int flags, struct nl80211_dump_wiphy_state *state)
|
2013-02-14 15:19:38 +00:00
|
|
|
{
|
|
|
|
void *hdr;
|
|
|
|
struct nlattr *nl_bands, *nl_band;
|
|
|
|
struct nlattr *nl_freqs, *nl_freq;
|
|
|
|
struct nlattr *nl_cmds;
|
|
|
|
enum ieee80211_band band;
|
|
|
|
struct ieee80211_channel *chan;
|
|
|
|
int i;
|
|
|
|
const struct ieee80211_txrx_stypes *mgmt_stypes =
|
2014-04-21 04:53:02 +00:00
|
|
|
rdev->wiphy.mgmt_stypes;
|
2013-02-27 14:39:45 +00:00
|
|
|
u32 features;
|
2008-01-24 18:38:39 +00:00
|
|
|
|
2014-05-26 11:52:25 +00:00
|
|
|
hdr = nl80211hdr_put(msg, portid, seq, flags, cmd);
|
2013-02-14 15:19:38 +00:00
|
|
|
if (!hdr)
|
|
|
|
return -ENOBUFS;
|
2008-01-24 18:38:39 +00:00
|
|
|
|
2013-06-19 08:57:22 +00:00
|
|
|
if (WARN_ON(!state))
|
|
|
|
return -EINVAL;
|
2008-01-24 18:38:39 +00:00
|
|
|
|
2014-04-21 04:53:02 +00:00
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_put_string(msg, NL80211_ATTR_WIPHY_NAME,
|
2014-04-21 04:53:02 +00:00
|
|
|
wiphy_name(&rdev->wiphy)) ||
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_put_u32(msg, NL80211_ATTR_GENERATION,
|
|
|
|
cfg80211_rdev_list_generation))
|
2009-03-14 08:34:01 +00:00
|
|
|
goto nla_put_failure;
|
|
|
|
|
2014-05-26 11:52:25 +00:00
|
|
|
if (cmd != NL80211_CMD_NEW_WIPHY)
|
|
|
|
goto finish;
|
|
|
|
|
2013-06-19 08:57:22 +00:00
|
|
|
switch (state->split_start) {
|
2013-02-14 15:19:38 +00:00
|
|
|
case 0:
|
|
|
|
if (nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
|
2014-04-21 04:53:02 +00:00
|
|
|
rdev->wiphy.retry_short) ||
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
|
2014-04-21 04:53:02 +00:00
|
|
|
rdev->wiphy.retry_long) ||
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_put_u32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
|
2014-04-21 04:53:02 +00:00
|
|
|
rdev->wiphy.frag_threshold) ||
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_put_u32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD,
|
2014-04-21 04:53:02 +00:00
|
|
|
rdev->wiphy.rts_threshold) ||
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_put_u8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS,
|
2014-04-21 04:53:02 +00:00
|
|
|
rdev->wiphy.coverage_class) ||
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
|
2014-04-21 04:53:02 +00:00
|
|
|
rdev->wiphy.max_scan_ssids) ||
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS,
|
2014-04-21 04:53:02 +00:00
|
|
|
rdev->wiphy.max_sched_scan_ssids) ||
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_put_u16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
|
2014-04-21 04:53:02 +00:00
|
|
|
rdev->wiphy.max_scan_ie_len) ||
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_put_u16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN,
|
2014-04-21 04:53:02 +00:00
|
|
|
rdev->wiphy.max_sched_scan_ie_len) ||
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_put_u8(msg, NL80211_ATTR_MAX_MATCH_SETS,
|
2014-04-21 04:53:02 +00:00
|
|
|
rdev->wiphy.max_match_sets))
|
2012-03-29 08:41:26 +00:00
|
|
|
goto nla_put_failure;
|
2013-02-14 15:19:38 +00:00
|
|
|
|
2014-04-21 04:53:02 +00:00
|
|
|
if ((rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) &&
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_put_flag(msg, NL80211_ATTR_SUPPORT_IBSS_RSN))
|
2012-05-16 21:50:18 +00:00
|
|
|
goto nla_put_failure;
|
2014-04-21 04:53:02 +00:00
|
|
|
if ((rdev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) &&
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_put_flag(msg, NL80211_ATTR_SUPPORT_MESH_AUTH))
|
|
|
|
goto nla_put_failure;
|
2014-04-21 04:53:02 +00:00
|
|
|
if ((rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) &&
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_put_flag(msg, NL80211_ATTR_SUPPORT_AP_UAPSD))
|
|
|
|
goto nla_put_failure;
|
2014-04-21 04:53:02 +00:00
|
|
|
if ((rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) &&
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_put_flag(msg, NL80211_ATTR_ROAM_SUPPORT))
|
|
|
|
goto nla_put_failure;
|
2014-04-21 04:53:02 +00:00
|
|
|
if ((rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) &&
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_put_flag(msg, NL80211_ATTR_TDLS_SUPPORT))
|
|
|
|
goto nla_put_failure;
|
2014-04-21 04:53:02 +00:00
|
|
|
if ((rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) &&
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_put_flag(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP))
|
2012-03-29 08:41:26 +00:00
|
|
|
goto nla_put_failure;
|
2013-06-19 08:57:22 +00:00
|
|
|
state->split_start++;
|
|
|
|
if (state->split)
|
2013-02-14 15:19:38 +00:00
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
if (nla_put(msg, NL80211_ATTR_CIPHER_SUITES,
|
2014-04-21 04:53:02 +00:00
|
|
|
sizeof(u32) * rdev->wiphy.n_cipher_suites,
|
|
|
|
rdev->wiphy.cipher_suites))
|
2013-02-14 15:19:38 +00:00
|
|
|
goto nla_put_failure;
|
2011-11-17 17:06:10 +00:00
|
|
|
|
2013-02-14 15:19:38 +00:00
|
|
|
if (nla_put_u8(msg, NL80211_ATTR_MAX_NUM_PMKIDS,
|
2014-04-21 04:53:02 +00:00
|
|
|
rdev->wiphy.max_num_pmkids))
|
2013-02-14 15:19:38 +00:00
|
|
|
goto nla_put_failure;
|
2009-07-01 19:26:54 +00:00
|
|
|
|
2014-04-21 04:53:02 +00:00
|
|
|
if ((rdev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) &&
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE))
|
2012-03-29 08:41:26 +00:00
|
|
|
goto nla_put_failure;
|
2009-07-01 19:26:54 +00:00
|
|
|
|
2013-02-14 15:19:38 +00:00
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX,
|
2014-04-21 04:53:02 +00:00
|
|
|
rdev->wiphy.available_antennas_tx) ||
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX,
|
2014-04-21 04:53:02 +00:00
|
|
|
rdev->wiphy.available_antennas_rx))
|
2012-03-29 08:41:26 +00:00
|
|
|
goto nla_put_failure;
|
2009-07-01 19:26:54 +00:00
|
|
|
|
2014-04-21 04:53:02 +00:00
|
|
|
if ((rdev->wiphy.flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD) &&
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_put_u32(msg, NL80211_ATTR_PROBE_RESP_OFFLOAD,
|
2014-04-21 04:53:02 +00:00
|
|
|
rdev->wiphy.probe_resp_offload))
|
2013-02-14 15:19:38 +00:00
|
|
|
goto nla_put_failure;
|
2009-03-14 08:34:01 +00:00
|
|
|
|
2014-04-21 04:53:02 +00:00
|
|
|
if ((rdev->wiphy.available_antennas_tx ||
|
|
|
|
rdev->wiphy.available_antennas_rx) &&
|
|
|
|
rdev->ops->get_antenna) {
|
2013-02-14 15:19:38 +00:00
|
|
|
u32 tx_ant = 0, rx_ant = 0;
|
|
|
|
int res;
|
2014-04-21 04:53:02 +00:00
|
|
|
res = rdev_get_antenna(rdev, &tx_ant, &rx_ant);
|
2013-02-14 15:19:38 +00:00
|
|
|
if (!res) {
|
|
|
|
if (nla_put_u32(msg,
|
|
|
|
NL80211_ATTR_WIPHY_ANTENNA_TX,
|
|
|
|
tx_ant) ||
|
|
|
|
nla_put_u32(msg,
|
|
|
|
NL80211_ATTR_WIPHY_ANTENNA_RX,
|
|
|
|
rx_ant))
|
|
|
|
goto nla_put_failure;
|
|
|
|
}
|
|
|
|
}
|
2010-12-14 16:54:28 +00:00
|
|
|
|
2013-06-19 08:57:22 +00:00
|
|
|
state->split_start++;
|
|
|
|
if (state->split)
|
2013-02-14 15:19:38 +00:00
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES,
|
2014-04-21 04:53:02 +00:00
|
|
|
rdev->wiphy.interface_modes))
|
2013-02-14 15:19:38 +00:00
|
|
|
goto nla_put_failure;
|
2013-06-19 08:57:22 +00:00
|
|
|
state->split_start++;
|
|
|
|
if (state->split)
|
2013-02-14 15:19:38 +00:00
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
|
|
|
|
if (!nl_bands)
|
|
|
|
goto nla_put_failure;
|
2010-11-25 09:02:29 +00:00
|
|
|
|
2013-06-19 08:57:22 +00:00
|
|
|
for (band = state->band_start;
|
|
|
|
band < IEEE80211_NUM_BANDS; band++) {
|
2013-02-14 15:19:38 +00:00
|
|
|
struct ieee80211_supported_band *sband;
|
2010-08-12 13:38:38 +00:00
|
|
|
|
2014-04-21 04:53:02 +00:00
|
|
|
sband = rdev->wiphy.bands[band];
|
2010-08-12 13:38:38 +00:00
|
|
|
|
2013-02-14 15:19:38 +00:00
|
|
|
if (!sband)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
nl_band = nla_nest_start(msg, band);
|
|
|
|
if (!nl_band)
|
2010-08-12 13:38:38 +00:00
|
|
|
goto nla_put_failure;
|
2013-02-14 15:19:38 +00:00
|
|
|
|
2013-06-19 08:57:22 +00:00
|
|
|
switch (state->chan_start) {
|
2013-02-14 15:19:38 +00:00
|
|
|
case 0:
|
|
|
|
if (nl80211_send_band_rateinfo(msg, sband))
|
2012-03-29 08:41:26 +00:00
|
|
|
goto nla_put_failure;
|
2013-06-19 08:57:22 +00:00
|
|
|
state->chan_start++;
|
|
|
|
if (state->split)
|
2013-02-14 15:19:38 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* add frequencies */
|
|
|
|
nl_freqs = nla_nest_start(
|
|
|
|
msg, NL80211_BAND_ATTR_FREQS);
|
|
|
|
if (!nl_freqs)
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
2013-06-19 08:57:22 +00:00
|
|
|
for (i = state->chan_start - 1;
|
2013-02-14 15:19:38 +00:00
|
|
|
i < sband->n_channels;
|
|
|
|
i++) {
|
|
|
|
nl_freq = nla_nest_start(msg, i);
|
|
|
|
if (!nl_freq)
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
chan = &sband->channels[i];
|
|
|
|
|
2013-06-19 08:57:22 +00:00
|
|
|
if (nl80211_msg_put_channel(
|
|
|
|
msg, chan,
|
|
|
|
state->split))
|
2013-02-14 15:19:38 +00:00
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
nla_nest_end(msg, nl_freq);
|
2013-06-19 08:57:22 +00:00
|
|
|
if (state->split)
|
2013-02-14 15:19:38 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (i < sband->n_channels)
|
2013-06-19 08:57:22 +00:00
|
|
|
state->chan_start = i + 2;
|
2013-02-14 15:19:38 +00:00
|
|
|
else
|
2013-06-19 08:57:22 +00:00
|
|
|
state->chan_start = 0;
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_nest_end(msg, nl_freqs);
|
|
|
|
}
|
|
|
|
|
|
|
|
nla_nest_end(msg, nl_band);
|
|
|
|
|
2013-06-19 08:57:22 +00:00
|
|
|
if (state->split) {
|
2013-02-14 15:19:38 +00:00
|
|
|
/* start again here */
|
2013-06-19 08:57:22 +00:00
|
|
|
if (state->chan_start)
|
2013-02-14 15:19:38 +00:00
|
|
|
band--;
|
|
|
|
break;
|
2010-08-12 13:38:38 +00:00
|
|
|
}
|
|
|
|
}
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_nest_end(msg, nl_bands);
|
2010-08-12 13:38:38 +00:00
|
|
|
|
2013-02-14 15:19:38 +00:00
|
|
|
if (band < IEEE80211_NUM_BANDS)
|
2013-06-19 08:57:22 +00:00
|
|
|
state->band_start = band + 1;
|
2013-02-14 15:19:38 +00:00
|
|
|
else
|
2013-06-19 08:57:22 +00:00
|
|
|
state->band_start = 0;
|
2010-08-24 10:15:53 +00:00
|
|
|
|
2013-02-14 15:19:38 +00:00
|
|
|
/* if bands & channels are done, continue outside */
|
2013-06-19 08:57:22 +00:00
|
|
|
if (state->band_start == 0 && state->chan_start == 0)
|
|
|
|
state->split_start++;
|
|
|
|
if (state->split)
|
2013-02-14 15:19:38 +00:00
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
|
|
|
|
if (!nl_cmds)
|
2010-08-12 13:38:38 +00:00
|
|
|
goto nla_put_failure;
|
|
|
|
|
2013-02-14 15:19:38 +00:00
|
|
|
i = 0;
|
|
|
|
#define CMD(op, n) \
|
|
|
|
do { \
|
2014-04-21 04:53:02 +00:00
|
|
|
if (rdev->ops->op) { \
|
2013-02-14 15:19:38 +00:00
|
|
|
i++; \
|
|
|
|
if (nla_put_u32(msg, i, NL80211_CMD_ ## n)) \
|
|
|
|
goto nla_put_failure; \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
CMD(add_virtual_intf, NEW_INTERFACE);
|
|
|
|
CMD(change_virtual_intf, SET_INTERFACE);
|
|
|
|
CMD(add_key, NEW_KEY);
|
|
|
|
CMD(start_ap, START_AP);
|
|
|
|
CMD(add_station, NEW_STATION);
|
|
|
|
CMD(add_mpath, NEW_MPATH);
|
|
|
|
CMD(update_mesh_config, SET_MESH_CONFIG);
|
|
|
|
CMD(change_bss, SET_BSS);
|
|
|
|
CMD(auth, AUTHENTICATE);
|
|
|
|
CMD(assoc, ASSOCIATE);
|
|
|
|
CMD(deauth, DEAUTHENTICATE);
|
|
|
|
CMD(disassoc, DISASSOCIATE);
|
|
|
|
CMD(join_ibss, JOIN_IBSS);
|
|
|
|
CMD(join_mesh, JOIN_MESH);
|
|
|
|
CMD(set_pmksa, SET_PMKSA);
|
|
|
|
CMD(del_pmksa, DEL_PMKSA);
|
|
|
|
CMD(flush_pmksa, FLUSH_PMKSA);
|
2014-04-21 04:53:02 +00:00
|
|
|
if (rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL)
|
2013-02-14 15:19:38 +00:00
|
|
|
CMD(remain_on_channel, REMAIN_ON_CHANNEL);
|
|
|
|
CMD(set_bitrate_mask, SET_TX_BITRATE_MASK);
|
|
|
|
CMD(mgmt_tx, FRAME);
|
|
|
|
CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL);
|
2014-04-21 04:53:02 +00:00
|
|
|
if (rdev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
|
2013-02-14 15:19:38 +00:00
|
|
|
i++;
|
|
|
|
if (nla_put_u32(msg, i, NL80211_CMD_SET_WIPHY_NETNS))
|
2010-08-12 13:38:38 +00:00
|
|
|
goto nla_put_failure;
|
|
|
|
}
|
2014-04-21 04:53:02 +00:00
|
|
|
if (rdev->ops->set_monitor_channel || rdev->ops->start_ap ||
|
|
|
|
rdev->ops->join_mesh) {
|
2013-02-14 15:19:38 +00:00
|
|
|
i++;
|
|
|
|
if (nla_put_u32(msg, i, NL80211_CMD_SET_CHANNEL))
|
|
|
|
goto nla_put_failure;
|
|
|
|
}
|
|
|
|
CMD(set_wds_peer, SET_WDS_PEER);
|
2014-04-21 04:53:02 +00:00
|
|
|
if (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) {
|
2013-02-14 15:19:38 +00:00
|
|
|
CMD(tdls_mgmt, TDLS_MGMT);
|
|
|
|
CMD(tdls_oper, TDLS_OPER);
|
|
|
|
}
|
2014-04-21 04:53:02 +00:00
|
|
|
if (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
|
2013-02-14 15:19:38 +00:00
|
|
|
CMD(sched_scan_start, START_SCHED_SCAN);
|
|
|
|
CMD(probe_client, PROBE_CLIENT);
|
|
|
|
CMD(set_noack_map, SET_NOACK_MAP);
|
2014-04-21 04:53:02 +00:00
|
|
|
if (rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) {
|
2013-02-14 15:19:38 +00:00
|
|
|
i++;
|
|
|
|
if (nla_put_u32(msg, i, NL80211_CMD_REGISTER_BEACONS))
|
|
|
|
goto nla_put_failure;
|
|
|
|
}
|
|
|
|
CMD(start_p2p_device, START_P2P_DEVICE);
|
|
|
|
CMD(set_mcast_rate, SET_MCAST_RATE);
|
2014-06-10 12:06:25 +00:00
|
|
|
#ifdef CONFIG_NL80211_TESTMODE
|
|
|
|
CMD(testmode_cmd, TESTMODE);
|
|
|
|
#endif
|
2013-06-19 08:57:22 +00:00
|
|
|
if (state->split) {
|
2013-04-18 13:49:00 +00:00
|
|
|
CMD(crit_proto_start, CRIT_PROTOCOL_START);
|
|
|
|
CMD(crit_proto_stop, CRIT_PROTOCOL_STOP);
|
2014-04-21 04:53:02 +00:00
|
|
|
if (rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)
|
2013-07-11 14:09:05 +00:00
|
|
|
CMD(channel_switch, CHANNEL_SWITCH);
|
2014-06-10 12:06:25 +00:00
|
|
|
CMD(set_qos_map, SET_QOS_MAP);
|
2014-10-22 07:25:06 +00:00
|
|
|
if (rdev->wiphy.features &
|
|
|
|
NL80211_FEATURE_SUPPORTS_WMM_ADMISSION)
|
2014-09-09 19:55:35 +00:00
|
|
|
CMD(add_tx_ts, ADD_TX_TS);
|
2013-04-18 13:49:00 +00:00
|
|
|
}
|
2014-06-10 12:06:25 +00:00
|
|
|
/* add into the if now */
|
2013-02-14 15:19:38 +00:00
|
|
|
#undef CMD
|
2011-05-04 13:37:28 +00:00
|
|
|
|
2014-04-21 04:53:02 +00:00
|
|
|
if (rdev->ops->connect || rdev->ops->auth) {
|
2013-02-14 15:19:38 +00:00
|
|
|
i++;
|
|
|
|
if (nla_put_u32(msg, i, NL80211_CMD_CONNECT))
|
2012-03-29 08:41:26 +00:00
|
|
|
goto nla_put_failure;
|
2011-05-04 13:37:28 +00:00
|
|
|
}
|
|
|
|
|
2014-04-21 04:53:02 +00:00
|
|
|
if (rdev->ops->disconnect || rdev->ops->deauth) {
|
2013-02-14 15:19:38 +00:00
|
|
|
i++;
|
|
|
|
if (nla_put_u32(msg, i, NL80211_CMD_DISCONNECT))
|
|
|
|
goto nla_put_failure;
|
|
|
|
}
|
|
|
|
|
|
|
|
nla_nest_end(msg, nl_cmds);
|
2013-06-19 08:57:22 +00:00
|
|
|
state->split_start++;
|
|
|
|
if (state->split)
|
2013-02-14 15:19:38 +00:00
|
|
|
break;
|
|
|
|
case 5:
|
2014-04-21 04:53:02 +00:00
|
|
|
if (rdev->ops->remain_on_channel &&
|
|
|
|
(rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) &&
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_put_u32(msg,
|
|
|
|
NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION,
|
2014-04-21 04:53:02 +00:00
|
|
|
rdev->wiphy.max_remain_on_channel_duration))
|
2013-02-14 15:19:38 +00:00
|
|
|
goto nla_put_failure;
|
|
|
|
|
2014-04-21 04:53:02 +00:00
|
|
|
if ((rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX) &&
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_put_flag(msg, NL80211_ATTR_OFFCHANNEL_TX_OK))
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
if (nl80211_send_mgmt_stypes(msg, mgmt_stypes))
|
|
|
|
goto nla_put_failure;
|
2013-06-19 08:57:22 +00:00
|
|
|
state->split_start++;
|
|
|
|
if (state->split)
|
2013-02-14 15:19:38 +00:00
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
#ifdef CONFIG_PM
|
2014-04-21 04:53:02 +00:00
|
|
|
if (nl80211_send_wowlan(msg, rdev, state->split))
|
2013-02-14 15:19:38 +00:00
|
|
|
goto nla_put_failure;
|
2013-06-19 08:57:22 +00:00
|
|
|
state->split_start++;
|
|
|
|
if (state->split)
|
2013-02-14 15:19:38 +00:00
|
|
|
break;
|
|
|
|
#else
|
2013-06-19 08:57:22 +00:00
|
|
|
state->split_start++;
|
2012-06-27 07:23:48 +00:00
|
|
|
#endif
|
2013-02-14 15:19:38 +00:00
|
|
|
case 7:
|
|
|
|
if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES,
|
2014-04-21 04:53:02 +00:00
|
|
|
rdev->wiphy.software_iftypes))
|
2013-02-14 15:19:38 +00:00
|
|
|
goto nla_put_failure;
|
2011-05-04 13:37:28 +00:00
|
|
|
|
2014-04-21 04:53:02 +00:00
|
|
|
if (nl80211_put_iface_combinations(&rdev->wiphy, msg,
|
2013-06-19 08:57:22 +00:00
|
|
|
state->split))
|
2013-02-14 15:19:38 +00:00
|
|
|
goto nla_put_failure;
|
2011-05-13 08:58:57 +00:00
|
|
|
|
2013-06-19 08:57:22 +00:00
|
|
|
state->split_start++;
|
|
|
|
if (state->split)
|
2013-02-14 15:19:38 +00:00
|
|
|
break;
|
|
|
|
case 8:
|
2014-04-21 04:53:02 +00:00
|
|
|
if ((rdev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) &&
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_put_u32(msg, NL80211_ATTR_DEVICE_AP_SME,
|
2014-04-21 04:53:02 +00:00
|
|
|
rdev->wiphy.ap_sme_capa))
|
2013-02-14 15:19:38 +00:00
|
|
|
goto nla_put_failure;
|
2011-05-13 08:58:57 +00:00
|
|
|
|
2014-04-21 04:53:02 +00:00
|
|
|
features = rdev->wiphy.features;
|
2013-02-27 14:39:45 +00:00
|
|
|
/*
|
|
|
|
* We can only add the per-channel limit information if the
|
|
|
|
* dump is split, otherwise it makes it too big. Therefore
|
|
|
|
* only advertise it in that case.
|
|
|
|
*/
|
2013-06-19 08:57:22 +00:00
|
|
|
if (state->split)
|
2013-02-27 14:39:45 +00:00
|
|
|
features |= NL80211_FEATURE_ADVERTISE_CHAN_LIMITS;
|
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_FEATURE_FLAGS, features))
|
2013-02-14 15:19:38 +00:00
|
|
|
goto nla_put_failure;
|
2011-11-07 11:39:33 +00:00
|
|
|
|
2014-04-21 04:53:02 +00:00
|
|
|
if (rdev->wiphy.ht_capa_mod_mask &&
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_put(msg, NL80211_ATTR_HT_CAPABILITY_MASK,
|
2014-04-21 04:53:02 +00:00
|
|
|
sizeof(*rdev->wiphy.ht_capa_mod_mask),
|
|
|
|
rdev->wiphy.ht_capa_mod_mask))
|
2013-02-14 15:19:38 +00:00
|
|
|
goto nla_put_failure;
|
2011-11-06 13:13:33 +00:00
|
|
|
|
2014-04-21 04:53:02 +00:00
|
|
|
if (rdev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME &&
|
|
|
|
rdev->wiphy.max_acl_mac_addrs &&
|
2013-02-14 15:19:38 +00:00
|
|
|
nla_put_u32(msg, NL80211_ATTR_MAC_ACL_MAX,
|
2014-04-21 04:53:02 +00:00
|
|
|
rdev->wiphy.max_acl_mac_addrs))
|
2013-02-14 15:19:38 +00:00
|
|
|
goto nla_put_failure;
|
2011-11-18 19:31:59 +00:00
|
|
|
|
2013-02-14 15:19:38 +00:00
|
|
|
/*
|
|
|
|
* Any information below this point is only available to
|
|
|
|
* applications that can deal with it being split. This
|
|
|
|
* helps ensure that newly added capabilities don't break
|
|
|
|
* older tools by overrunning their buffers.
|
|
|
|
*
|
|
|
|
* We still increment split_start so that in the split
|
|
|
|
* case we'll continue with more data in the next round,
|
|
|
|
* but break unconditionally so unsplit data stops here.
|
|
|
|
*/
|
2013-06-19 08:57:22 +00:00
|
|
|
state->split_start++;
|
2013-02-14 15:19:38 +00:00
|
|
|
break;
|
|
|
|
case 9:
|
2014-04-21 04:53:02 +00:00
|
|
|
if (rdev->wiphy.extended_capabilities &&
|
2013-02-27 14:39:45 +00:00
|
|
|
(nla_put(msg, NL80211_ATTR_EXT_CAPA,
|
2014-04-21 04:53:02 +00:00
|
|
|
rdev->wiphy.extended_capabilities_len,
|
|
|
|
rdev->wiphy.extended_capabilities) ||
|
2013-02-27 14:39:45 +00:00
|
|
|
nla_put(msg, NL80211_ATTR_EXT_CAPA_MASK,
|
2014-04-21 04:53:02 +00:00
|
|
|
rdev->wiphy.extended_capabilities_len,
|
|
|
|
rdev->wiphy.extended_capabilities_mask)))
|
2013-02-27 14:39:45 +00:00
|
|
|
goto nla_put_failure;
|
2013-02-11 13:20:05 +00:00
|
|
|
|
2014-04-21 04:53:02 +00:00
|
|
|
if (rdev->wiphy.vht_capa_mod_mask &&
|
2013-02-21 16:36:01 +00:00
|
|
|
nla_put(msg, NL80211_ATTR_VHT_CAPABILITY_MASK,
|
2014-04-21 04:53:02 +00:00
|
|
|
sizeof(*rdev->wiphy.vht_capa_mod_mask),
|
|
|
|
rdev->wiphy.vht_capa_mod_mask))
|
2013-02-21 16:36:01 +00:00
|
|
|
goto nla_put_failure;
|
|
|
|
|
2013-06-28 18:51:26 +00:00
|
|
|
state->split_start++;
|
|
|
|
break;
|
|
|
|
case 10:
|
2014-04-21 04:53:02 +00:00
|
|
|
if (nl80211_send_coalesce(msg, rdev))
|
2013-06-28 18:51:26 +00:00
|
|
|
goto nla_put_failure;
|
|
|
|
|
2014-04-21 04:53:02 +00:00
|
|
|
if ((rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ) &&
|
2013-11-09 13:57:54 +00:00
|
|
|
(nla_put_flag(msg, NL80211_ATTR_SUPPORT_5_MHZ) ||
|
|
|
|
nla_put_flag(msg, NL80211_ATTR_SUPPORT_10_MHZ)))
|
|
|
|
goto nla_put_failure;
|
2014-01-14 22:01:08 +00:00
|
|
|
|
2014-04-21 04:53:02 +00:00
|
|
|
if (rdev->wiphy.max_ap_assoc_sta &&
|
2014-01-14 22:01:08 +00:00
|
|
|
nla_put_u32(msg, NL80211_ATTR_MAX_AP_ASSOC_STA,
|
2014-04-21 04:53:02 +00:00
|
|
|
rdev->wiphy.max_ap_assoc_sta))
|
2014-01-14 22:01:08 +00:00
|
|
|
goto nla_put_failure;
|
|
|
|
|
2013-11-13 12:37:47 +00:00
|
|
|
state->split_start++;
|
|
|
|
break;
|
|
|
|
case 11:
|
2014-04-21 04:53:02 +00:00
|
|
|
if (rdev->wiphy.n_vendor_commands) {
|
2013-12-18 13:43:31 +00:00
|
|
|
const struct nl80211_vendor_cmd_info *info;
|
|
|
|
struct nlattr *nested;
|
|
|
|
|
|
|
|
nested = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
|
|
|
|
if (!nested)
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
2014-04-21 04:53:02 +00:00
|
|
|
for (i = 0; i < rdev->wiphy.n_vendor_commands; i++) {
|
|
|
|
info = &rdev->wiphy.vendor_commands[i].info;
|
2013-12-18 13:43:31 +00:00
|
|
|
if (nla_put(msg, i + 1, sizeof(*info), info))
|
|
|
|
goto nla_put_failure;
|
|
|
|
}
|
|
|
|
nla_nest_end(msg, nested);
|
|
|
|
}
|
|
|
|
|
2014-04-21 04:53:02 +00:00
|
|
|
if (rdev->wiphy.n_vendor_events) {
|
2013-12-18 13:43:31 +00:00
|
|
|
const struct nl80211_vendor_cmd_info *info;
|
|
|
|
struct nlattr *nested;
|
2013-11-13 12:37:47 +00:00
|
|
|
|
2013-12-18 13:43:31 +00:00
|
|
|
nested = nla_nest_start(msg,
|
|
|
|
NL80211_ATTR_VENDOR_EVENTS);
|
|
|
|
if (!nested)
|
2013-11-13 12:37:47 +00:00
|
|
|
goto nla_put_failure;
|
2013-12-18 13:43:31 +00:00
|
|
|
|
2014-04-21 04:53:02 +00:00
|
|
|
for (i = 0; i < rdev->wiphy.n_vendor_events; i++) {
|
|
|
|
info = &rdev->wiphy.vendor_events[i];
|
2013-12-18 13:43:31 +00:00
|
|
|
if (nla_put(msg, i + 1, sizeof(*info), info))
|
|
|
|
goto nla_put_failure;
|
|
|
|
}
|
|
|
|
nla_nest_end(msg, nested);
|
|
|
|
}
|
2014-05-09 11:11:46 +00:00
|
|
|
state->split_start++;
|
|
|
|
break;
|
|
|
|
case 12:
|
|
|
|
if (rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH &&
|
|
|
|
nla_put_u8(msg, NL80211_ATTR_MAX_CSA_COUNTERS,
|
|
|
|
rdev->wiphy.max_num_csa_counters))
|
|
|
|
goto nla_put_failure;
|
2013-11-09 13:57:54 +00:00
|
|
|
|
2013-02-14 15:19:38 +00:00
|
|
|
/* done */
|
2013-06-19 08:57:22 +00:00
|
|
|
state->split_start = 0;
|
2013-02-14 15:19:38 +00:00
|
|
|
break;
|
|
|
|
}
|
2014-05-26 11:52:25 +00:00
|
|
|
finish:
|
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
|
|
|
}
|
|
|
|
|
2013-06-19 08:57:22 +00:00
|
|
|
static int nl80211_dump_wiphy_parse(struct sk_buff *skb,
|
|
|
|
struct netlink_callback *cb,
|
|
|
|
struct nl80211_dump_wiphy_state *state)
|
|
|
|
{
|
|
|
|
struct nlattr **tb = nl80211_fam.attrbuf;
|
|
|
|
int ret = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
|
|
|
|
tb, nl80211_fam.maxattr, nl80211_policy);
|
|
|
|
/* ignore parse errors for backward compatibility */
|
|
|
|
if (ret)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
state->split = tb[NL80211_ATTR_SPLIT_WIPHY_DUMP];
|
|
|
|
if (tb[NL80211_ATTR_WIPHY])
|
|
|
|
state->filter_wiphy = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
|
|
|
|
if (tb[NL80211_ATTR_WDEV])
|
|
|
|
state->filter_wiphy = nla_get_u64(tb[NL80211_ATTR_WDEV]) >> 32;
|
|
|
|
if (tb[NL80211_ATTR_IFINDEX]) {
|
|
|
|
struct net_device *netdev;
|
|
|
|
struct cfg80211_registered_device *rdev;
|
|
|
|
int ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
|
|
|
|
|
2014-01-15 02:23:45 +00:00
|
|
|
netdev = __dev_get_by_index(sock_net(skb->sk), ifidx);
|
2013-06-19 08:57:22 +00:00
|
|
|
if (!netdev)
|
|
|
|
return -ENODEV;
|
|
|
|
if (netdev->ieee80211_ptr) {
|
2014-04-21 04:53:03 +00:00
|
|
|
rdev = wiphy_to_rdev(
|
2013-06-19 08:57:22 +00:00
|
|
|
netdev->ieee80211_ptr->wiphy);
|
|
|
|
state->filter_wiphy = rdev->wiphy_idx;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-09-20 17:09:35 +00:00
|
|
|
static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
|
|
|
|
{
|
2013-03-01 13:03:49 +00:00
|
|
|
int idx = 0, ret;
|
2013-06-19 08:57:22 +00:00
|
|
|
struct nl80211_dump_wiphy_state *state = (void *)cb->args[0];
|
2014-04-21 04:53:02 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
2013-06-19 08:09:57 +00:00
|
|
|
|
2013-05-08 19:45:15 +00:00
|
|
|
rtnl_lock();
|
2013-06-19 08:57:22 +00:00
|
|
|
if (!state) {
|
|
|
|
state = kzalloc(sizeof(*state), GFP_KERNEL);
|
2013-06-28 17:18:21 +00:00
|
|
|
if (!state) {
|
|
|
|
rtnl_unlock();
|
2013-06-19 08:57:22 +00:00
|
|
|
return -ENOMEM;
|
2013-02-14 15:19:38 +00:00
|
|
|
}
|
2013-06-19 08:57:22 +00:00
|
|
|
state->filter_wiphy = -1;
|
|
|
|
ret = nl80211_dump_wiphy_parse(skb, cb, state);
|
|
|
|
if (ret) {
|
|
|
|
kfree(state);
|
|
|
|
rtnl_unlock();
|
|
|
|
return ret;
|
2013-02-14 15:19:38 +00:00
|
|
|
}
|
2013-06-19 08:57:22 +00:00
|
|
|
cb->args[0] = (long)state;
|
2013-02-14 15:19:38 +00:00
|
|
|
}
|
|
|
|
|
2014-04-21 04:53:02 +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;
|
2013-06-19 08:57:22 +00:00
|
|
|
if (++idx <= state->start)
|
2007-09-20 17:09:35 +00:00
|
|
|
continue;
|
2013-06-19 08:57:22 +00:00
|
|
|
if (state->filter_wiphy != -1 &&
|
2014-04-21 04:53:02 +00:00
|
|
|
state->filter_wiphy != rdev->wiphy_idx)
|
2013-02-14 15:19:38 +00:00
|
|
|
continue;
|
|
|
|
/* attempt to fit multiple wiphy data chunks into the skb */
|
|
|
|
do {
|
2014-05-26 11:52:25 +00:00
|
|
|
ret = nl80211_send_wiphy(rdev, NL80211_CMD_NEW_WIPHY,
|
|
|
|
skb,
|
2013-02-14 15:19:38 +00:00
|
|
|
NETLINK_CB(cb->skb).portid,
|
|
|
|
cb->nlh->nlmsg_seq,
|
2013-06-19 08:57:22 +00:00
|
|
|
NLM_F_MULTI, state);
|
2013-02-14 15:19:38 +00:00
|
|
|
if (ret < 0) {
|
|
|
|
/*
|
|
|
|
* If sending the wiphy data didn't fit (ENOBUFS
|
|
|
|
* or EMSGSIZE returned), this SKB is still
|
|
|
|
* empty (so it's not too big because another
|
|
|
|
* wiphy dataset is already in the skb) and
|
|
|
|
* we've not tried to adjust the dump allocation
|
|
|
|
* yet ... then adjust the alloc size to be
|
|
|
|
* bigger, and return 1 but with the empty skb.
|
|
|
|
* This results in an empty message being RX'ed
|
|
|
|
* in userspace, but that is ignored.
|
|
|
|
*
|
|
|
|
* We can then retry with the larger buffer.
|
|
|
|
*/
|
|
|
|
if ((ret == -ENOBUFS || ret == -EMSGSIZE) &&
|
2014-01-16 14:00:40 +00:00
|
|
|
!skb->len && !state->split &&
|
2013-02-14 15:19:38 +00:00
|
|
|
cb->min_dump_alloc < 4096) {
|
|
|
|
cb->min_dump_alloc = 4096;
|
2014-01-16 14:00:40 +00:00
|
|
|
state->split_start = 0;
|
2013-06-19 23:49:39 +00:00
|
|
|
rtnl_unlock();
|
2013-02-14 15:19:38 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
idx--;
|
|
|
|
break;
|
2013-03-01 13:03:49 +00:00
|
|
|
}
|
2013-06-19 08:57:22 +00:00
|
|
|
} while (state->split_start > 0);
|
2013-02-14 15:19:38 +00:00
|
|
|
break;
|
2007-09-20 17:09:35 +00:00
|
|
|
}
|
2013-05-08 19:45:15 +00:00
|
|
|
rtnl_unlock();
|
2007-09-20 17:09:35 +00:00
|
|
|
|
2013-06-19 08:57:22 +00:00
|
|
|
state->start = idx;
|
2007-09-20 17:09:35 +00:00
|
|
|
|
|
|
|
return skb->len;
|
|
|
|
}
|
|
|
|
|
2013-06-19 08:57:22 +00:00
|
|
|
static int nl80211_dump_wiphy_done(struct netlink_callback *cb)
|
|
|
|
{
|
|
|
|
kfree((void *)cb->args[0]);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-09-20 17:09:35 +00:00
|
|
|
static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct sk_buff *msg;
|
2014-04-21 04:53:02 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
2013-06-19 08:57:22 +00:00
|
|
|
struct nl80211_dump_wiphy_state state = {};
|
2007-09-20 17:09:35 +00:00
|
|
|
|
2013-03-01 13:03:49 +00:00
|
|
|
msg = nlmsg_new(4096, GFP_KERNEL);
|
2007-09-20 17:09:35 +00:00
|
|
|
if (!msg)
|
2010-10-04 19:36:35 +00:00
|
|
|
return -ENOMEM;
|
2007-09-20 17:09:35 +00:00
|
|
|
|
2014-05-26 11:52:25 +00:00
|
|
|
if (nl80211_send_wiphy(rdev, NL80211_CMD_NEW_WIPHY, msg,
|
|
|
|
info->snd_portid, info->snd_seq, 0,
|
2013-06-19 08:57:22 +00:00
|
|
|
&state) < 0) {
|
2010-10-04 19:36:35 +00:00
|
|
|
nlmsg_free(msg);
|
|
|
|
return -ENOBUFS;
|
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
{
|
2012-03-28 09:04:24 +00:00
|
|
|
if (!tb[NL80211_TXQ_ATTR_AC] || !tb[NL80211_TXQ_ATTR_TXOP] ||
|
2008-10-30 14:59:24 +00:00
|
|
|
!tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] ||
|
|
|
|
!tb[NL80211_TXQ_ATTR_AIFS])
|
|
|
|
return -EINVAL;
|
|
|
|
|
2012-03-28 09:04:24 +00:00
|
|
|
txq_params->ac = nla_get_u8(tb[NL80211_TXQ_ATTR_AC]);
|
2008-10-30 14:59:24 +00:00
|
|
|
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]);
|
|
|
|
|
2012-03-28 09:04:24 +00:00
|
|
|
if (txq_params->ac >= NL80211_NUM_ACS)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2008-10-30 14:59:24 +00:00
|
|
|
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)
|
|
|
|
{
|
|
|
|
/*
|
2012-05-16 21:50:20 +00:00
|
|
|
* You can only set the channel explicitly for WDS interfaces,
|
|
|
|
* all others have their channel managed via their respective
|
|
|
|
* "establish a connection" command (connect, join, ...)
|
|
|
|
*
|
|
|
|
* For AP/GO and mesh mode, the channel can be set with the
|
|
|
|
* channel userspace API, but is only stored and passed to the
|
|
|
|
* low-level driver when the AP starts or the mesh is joined.
|
|
|
|
* This is for backward compatibility, userspace can also give
|
|
|
|
* the channel in the start-ap or join-mesh commands instead.
|
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
|
|
|
*
|
|
|
|
* Monitors are special as they are normally slaved to
|
2012-06-06 06:18:22 +00:00
|
|
|
* whatever else is going on, so they have their own special
|
|
|
|
* operation to set the monitor channel if possible.
|
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
|
|
|
*/
|
|
|
|
return !wdev ||
|
|
|
|
wdev->iftype == NL80211_IFTYPE_AP ||
|
|
|
|
wdev->iftype == NL80211_IFTYPE_MESH_POINT ||
|
2010-09-16 12:58:22 +00:00
|
|
|
wdev->iftype == NL80211_IFTYPE_MONITOR ||
|
|
|
|
wdev->iftype == NL80211_IFTYPE_P2P_GO;
|
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
|
|
|
}
|
|
|
|
|
2012-11-08 20:25:48 +00:00
|
|
|
static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
|
|
|
|
struct genl_info *info,
|
|
|
|
struct cfg80211_chan_def *chandef)
|
|
|
|
{
|
2012-11-29 08:41:07 +00:00
|
|
|
u32 control_freq;
|
2012-11-08 20:25:48 +00:00
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
control_freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
|
|
|
|
|
|
|
|
chandef->chan = ieee80211_get_channel(&rdev->wiphy, control_freq);
|
2012-11-08 22:14:50 +00:00
|
|
|
chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
|
|
|
|
chandef->center_freq1 = control_freq;
|
|
|
|
chandef->center_freq2 = 0;
|
2012-11-08 20:25:48 +00:00
|
|
|
|
|
|
|
/* Primary channel not allowed */
|
|
|
|
if (!chandef->chan || chandef->chan->flags & IEEE80211_CHAN_DISABLED)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2012-11-08 22:14:50 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
|
|
|
|
enum nl80211_channel_type chantype;
|
|
|
|
|
|
|
|
chantype = nla_get_u32(
|
|
|
|
info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
|
|
|
|
|
|
|
|
switch (chantype) {
|
|
|
|
case NL80211_CHAN_NO_HT:
|
|
|
|
case NL80211_CHAN_HT20:
|
|
|
|
case NL80211_CHAN_HT40PLUS:
|
|
|
|
case NL80211_CHAN_HT40MINUS:
|
|
|
|
cfg80211_chandef_create(chandef, chandef->chan,
|
|
|
|
chantype);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
} else if (info->attrs[NL80211_ATTR_CHANNEL_WIDTH]) {
|
|
|
|
chandef->width =
|
|
|
|
nla_get_u32(info->attrs[NL80211_ATTR_CHANNEL_WIDTH]);
|
|
|
|
if (info->attrs[NL80211_ATTR_CENTER_FREQ1])
|
|
|
|
chandef->center_freq1 =
|
|
|
|
nla_get_u32(
|
|
|
|
info->attrs[NL80211_ATTR_CENTER_FREQ1]);
|
|
|
|
if (info->attrs[NL80211_ATTR_CENTER_FREQ2])
|
|
|
|
chandef->center_freq2 =
|
|
|
|
nla_get_u32(
|
|
|
|
info->attrs[NL80211_ATTR_CENTER_FREQ2]);
|
|
|
|
}
|
|
|
|
|
2012-11-22 15:59:45 +00:00
|
|
|
if (!cfg80211_chandef_valid(chandef))
|
2012-11-08 22:14:50 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2012-11-22 15:59:45 +00:00
|
|
|
if (!cfg80211_chandef_usable(&rdev->wiphy, chandef,
|
|
|
|
IEEE80211_CHAN_DISABLED))
|
2012-11-08 22:14:50 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2013-05-16 11:00:28 +00:00
|
|
|
if ((chandef->width == NL80211_CHAN_WIDTH_5 ||
|
|
|
|
chandef->width == NL80211_CHAN_WIDTH_10) &&
|
|
|
|
!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2012-11-08 20:25:48 +00:00
|
|
|
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 int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
|
2014-04-28 08:22:08 +00:00
|
|
|
struct net_device *dev,
|
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 genl_info *info)
|
|
|
|
{
|
2012-11-08 20:25:48 +00:00
|
|
|
struct cfg80211_chan_def chandef;
|
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
|
|
|
int result;
|
2012-06-06 06:18:22 +00:00
|
|
|
enum nl80211_iftype iftype = NL80211_IFTYPE_MONITOR;
|
2014-04-28 08:22:08 +00:00
|
|
|
struct wireless_dev *wdev = NULL;
|
2012-06-06 06:18:22 +00:00
|
|
|
|
2014-04-28 08:22:08 +00:00
|
|
|
if (dev)
|
|
|
|
wdev = dev->ieee80211_ptr;
|
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 (!nl80211_can_set_dev_channel(wdev))
|
|
|
|
return -EOPNOTSUPP;
|
2014-04-28 08:22:08 +00:00
|
|
|
if (wdev)
|
|
|
|
iftype = wdev->iftype;
|
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
|
|
|
|
2012-11-08 20:25:48 +00:00
|
|
|
result = nl80211_parse_chandef(rdev, info, &chandef);
|
|
|
|
if (result)
|
|
|
|
return result;
|
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
|
|
|
|
2012-06-06 06:18:22 +00:00
|
|
|
switch (iftype) {
|
2012-05-16 21:50:18 +00:00
|
|
|
case NL80211_IFTYPE_AP:
|
|
|
|
case NL80211_IFTYPE_P2P_GO:
|
2014-02-23 07:13:01 +00:00
|
|
|
if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef, iftype)) {
|
2012-05-16 21:50:18 +00:00
|
|
|
result = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
2014-04-28 08:22:08 +00:00
|
|
|
if (wdev->beacon_interval) {
|
|
|
|
if (!dev || !rdev->ops->set_ap_chanwidth ||
|
|
|
|
!(rdev->wiphy.features &
|
|
|
|
NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE)) {
|
|
|
|
result = -EBUSY;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Only allow dynamic channel width changes */
|
|
|
|
if (chandef.chan != wdev->preset_chandef.chan) {
|
|
|
|
result = -EBUSY;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
result = rdev_set_ap_chanwidth(rdev, dev, &chandef);
|
|
|
|
if (result)
|
|
|
|
break;
|
|
|
|
}
|
2012-11-08 20:25:48 +00:00
|
|
|
wdev->preset_chandef = chandef;
|
2012-05-16 21:50:18 +00:00
|
|
|
result = 0;
|
|
|
|
break;
|
2012-05-16 21:50:20 +00:00
|
|
|
case NL80211_IFTYPE_MESH_POINT:
|
2012-11-08 20:25:48 +00:00
|
|
|
result = cfg80211_set_mesh_channel(rdev, wdev, &chandef);
|
2012-05-16 21:50:20 +00:00
|
|
|
break;
|
2012-06-06 06:18:22 +00:00
|
|
|
case NL80211_IFTYPE_MONITOR:
|
2012-11-08 20:25:48 +00:00
|
|
|
result = cfg80211_set_monitor_channel(rdev, &chandef);
|
2012-06-06 06:18:22 +00:00
|
|
|
break;
|
2012-05-16 21:50:18 +00:00
|
|
|
default:
|
2012-06-06 06:18:22 +00:00
|
|
|
result = -EINVAL;
|
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
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *netdev = info->user_ptr[1];
|
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
|
|
|
|
2014-04-28 08:22:08 +00:00
|
|
|
return __nl80211_set_channel(rdev, netdev, info);
|
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
|
|
|
}
|
|
|
|
|
2010-10-01 17:54:28 +00:00
|
|
|
static int nl80211_set_wds_peer(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2010-10-07 11:10:30 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
|
|
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
2010-10-07 11:11:09 +00:00
|
|
|
const u8 *bssid;
|
2010-10-01 17:54:28 +00:00
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_MAC])
|
|
|
|
return -EINVAL;
|
|
|
|
|
2010-10-07 11:10:30 +00:00
|
|
|
if (netif_running(dev))
|
|
|
|
return -EBUSY;
|
2010-10-01 17:54:28 +00:00
|
|
|
|
2010-10-07 11:10:30 +00:00
|
|
|
if (!rdev->ops->set_wds_peer)
|
|
|
|
return -EOPNOTSUPP;
|
2010-10-01 17:54:28 +00:00
|
|
|
|
2010-10-07 11:10:30 +00:00
|
|
|
if (wdev->iftype != NL80211_IFTYPE_WDS)
|
|
|
|
return -EOPNOTSUPP;
|
2010-10-01 17:54:28 +00:00
|
|
|
|
|
|
|
bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
2012-06-27 14:19:42 +00:00
|
|
|
return rdev_set_wds_peer(rdev, dev, bssid);
|
2010-10-01 17:54:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
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;
|
2010-09-10 15:22:32 +00:00
|
|
|
int result = 0, 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
|
|
|
|
2013-05-08 19:45:15 +00:00
|
|
|
ASSERT_RTNL();
|
|
|
|
|
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
|
|
|
|
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]);
|
|
|
|
|
2014-01-15 02:23:45 +00:00
|
|
|
netdev = __dev_get_by_index(genl_info_net(info), ifindex);
|
2013-05-08 19:45:15 +00:00
|
|
|
if (netdev && netdev->ieee80211_ptr)
|
2014-04-21 04:53:03 +00:00
|
|
|
rdev = wiphy_to_rdev(netdev->ieee80211_ptr->wiphy);
|
2013-05-08 19:45:15 +00:00
|
|
|
else
|
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
|
|
|
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) {
|
2012-06-15 12:18:32 +00:00
|
|
|
rdev = __cfg80211_rdev_from_attrs(genl_info_net(info),
|
|
|
|
info->attrs);
|
2013-05-08 19:45:15 +00:00
|
|
|
if (IS_ERR(rdev))
|
2010-10-04 19:36:35 +00:00
|
|
|
return PTR_ERR(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
|
|
|
wdev = NULL;
|
|
|
|
netdev = NULL;
|
|
|
|
result = 0;
|
2012-10-24 08:04:58 +00:00
|
|
|
} else
|
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
|
|
|
wdev = netdev->ieee80211_ptr;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
|
|
|
if (result)
|
2014-01-15 02:23:45 +00:00
|
|
|
return result;
|
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];
|
|
|
|
|
2014-01-15 02:23:45 +00:00
|
|
|
if (!rdev->ops->set_txq_params)
|
|
|
|
return -EOPNOTSUPP;
|
2008-10-30 14:59:24 +00:00
|
|
|
|
2014-01-15 02:23:45 +00:00
|
|
|
if (!netdev)
|
|
|
|
return -EINVAL;
|
2011-09-25 17:06:53 +00:00
|
|
|
|
2011-11-03 13:50:13 +00:00
|
|
|
if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
|
2014-01-15 02:23:45 +00:00
|
|
|
netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
|
|
|
|
return -EINVAL;
|
2011-11-03 13:50:13 +00:00
|
|
|
|
2014-01-15 02:23:45 +00:00
|
|
|
if (!netif_running(netdev))
|
|
|
|
return -ENETDOWN;
|
2012-04-02 08:51:55 +00:00
|
|
|
|
2008-10-30 14:59:24 +00:00
|
|
|
nla_for_each_nested(nl_txq_params,
|
|
|
|
info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
|
|
|
|
rem_txq_params) {
|
2014-01-24 09:17:47 +00:00
|
|
|
result = nla_parse(tb, NL80211_TXQ_ATTR_MAX,
|
|
|
|
nla_data(nl_txq_params),
|
|
|
|
nla_len(nl_txq_params),
|
|
|
|
txq_params_policy);
|
|
|
|
if (result)
|
|
|
|
return result;
|
2008-10-30 14:59:24 +00:00
|
|
|
result = parse_txq_params(tb, &txq_params);
|
|
|
|
if (result)
|
2014-01-15 02:23:45 +00:00
|
|
|
return result;
|
2008-10-30 14:59:24 +00:00
|
|
|
|
2012-06-27 14:19:42 +00:00
|
|
|
result = rdev_set_txq_params(rdev, netdev,
|
|
|
|
&txq_params);
|
2008-10-30 14:59:24 +00:00
|
|
|
if (result)
|
2014-01-15 02:23:45 +00:00
|
|
|
return result;
|
2008-10-30 14:59:24 +00:00
|
|
|
}
|
|
|
|
}
|
2007-09-20 17:09:35 +00:00
|
|
|
|
2008-11-26 14:15:24 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
|
2014-04-28 08:22:08 +00:00
|
|
|
result = __nl80211_set_channel(
|
|
|
|
rdev,
|
|
|
|
nl80211_can_set_dev_channel(wdev) ? netdev : NULL,
|
|
|
|
info);
|
2008-11-26 14:15:24 +00:00
|
|
|
if (result)
|
2014-01-15 02:23:45 +00:00
|
|
|
return result;
|
2008-11-26 14:15:24 +00:00
|
|
|
}
|
|
|
|
|
2010-06-23 09:12:38 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_WIPHY_TX_POWER_SETTING]) {
|
2012-10-24 08:17:18 +00:00
|
|
|
struct wireless_dev *txp_wdev = wdev;
|
2010-06-23 09:12:38 +00:00
|
|
|
enum nl80211_tx_power_setting type;
|
|
|
|
int idx, mbm = 0;
|
|
|
|
|
2012-10-24 08:17:18 +00:00
|
|
|
if (!(rdev->wiphy.features & NL80211_FEATURE_VIF_TXPOWER))
|
|
|
|
txp_wdev = NULL;
|
|
|
|
|
2014-01-15 02:23:45 +00:00
|
|
|
if (!rdev->ops->set_tx_power)
|
|
|
|
return -EOPNOTSUPP;
|
2010-06-23 09:12:38 +00:00
|
|
|
|
|
|
|
idx = NL80211_ATTR_WIPHY_TX_POWER_SETTING;
|
|
|
|
type = nla_get_u32(info->attrs[idx]);
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_WIPHY_TX_POWER_LEVEL] &&
|
2014-01-15 02:23:45 +00:00
|
|
|
(type != NL80211_TX_POWER_AUTOMATIC))
|
|
|
|
return -EINVAL;
|
2010-06-23 09:12:38 +00:00
|
|
|
|
|
|
|
if (type != NL80211_TX_POWER_AUTOMATIC) {
|
|
|
|
idx = NL80211_ATTR_WIPHY_TX_POWER_LEVEL;
|
|
|
|
mbm = nla_get_u32(info->attrs[idx]);
|
|
|
|
}
|
|
|
|
|
2012-10-24 08:17:18 +00:00
|
|
|
result = rdev_set_tx_power(rdev, txp_wdev, type, mbm);
|
2010-06-23 09:12:38 +00:00
|
|
|
if (result)
|
2014-01-15 02:23:45 +00:00
|
|
|
return result;
|
2010-06-23 09:12:38 +00:00
|
|
|
}
|
|
|
|
|
cfg80211: Add nl80211 antenna configuration
Allow setting of TX and RX antennas configuration via nl80211.
The antenna configuration is defined as a bitmap of allowed antennas to use.
This API can be used to mask out antennas which are not attached or should not
be used for other reasons like regulatory concerns or special setups.
Separate bitmaps are used for RX and TX to allow configuring different antennas
for receiving and transmitting. Each bitmap is 32 bit long, each bit
representing one antenna, starting with antenna 1 at the first bit. If an
antenna bit is set, this means the driver is allowed to use this antenna for RX
or TX respectively; if the bit is not set the hardware is not allowed to use
this antenna.
Using bitmaps has the benefit of allowing for a flexible configuration
interface which can support many different configurations and which can be used
for 802.11n as well as non-802.11n devices. Instead of relying on some hardware
specific assumptions, drivers can use this information to know which antennas
are actually attached to the system and derive their capabilities based on
that.
802.11n devices should enable or disable chains, based on which antennas are
present (If all antennas belonging to a particular chain are disabled, the
entire chain should be disabled). HT capabilities (like STBC, TX Beamforming,
Antenna selection) should be calculated based on the available chains after
applying the antenna masks. Should a 802.11n device have diversity antennas
attached to one of their chains, diversity can be enabled or disabled based on
the antenna information.
Non-802.11n drivers can use the antenna masks to select RX and TX antennas and
to enable or disable antenna diversity.
While covering chainmasks for 802.11n and the standard "legacy" modes "fixed
antenna 1", "fixed antenna 2" and "diversity" this API also allows more rare,
but useful configurations as follows:
1) Send on antenna 1, receive on antenna 2 (or vice versa). This can be used to
have a low gain antenna for TX in order to keep within the regulatory
constraints and a high gain antenna for RX in order to receive weaker signals
("speak softly, but listen harder"). This can be useful for building long-shot
outdoor links. Another usage of this setup is having a low-noise pre-amplifier
on antenna 1 and a power amplifier on the other antenna. This way transmit
noise is mostly kept out of the low noise receive channel.
(This would be bitmaps: tx 1 rx 2).
2) Another similar setup is: Use RX diversity on both antennas, but always send
on antenna 1. Again that would allow us to benefit from a higher gain RX
antenna, while staying within the legal limits.
(This would be: tx 0 rx 3).
3) And finally there can be special experimental setups in research and
development even with pre 802.11n hardware where more than 2 antennas are
available. It's good to keep the API simple, yet flexible.
Signed-off-by: Bruno Randolf <br1@einfach.org>
--
v7: Made bitmasks 32 bit wide and rebased to latest wireless-testing.
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2010-11-10 03:50:50 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX] &&
|
|
|
|
info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]) {
|
|
|
|
u32 tx_ant, rx_ant;
|
2010-12-16 02:30:22 +00:00
|
|
|
if ((!rdev->wiphy.available_antennas_tx &&
|
|
|
|
!rdev->wiphy.available_antennas_rx) ||
|
2014-01-15 02:23:45 +00:00
|
|
|
!rdev->ops->set_antenna)
|
|
|
|
return -EOPNOTSUPP;
|
cfg80211: Add nl80211 antenna configuration
Allow setting of TX and RX antennas configuration via nl80211.
The antenna configuration is defined as a bitmap of allowed antennas to use.
This API can be used to mask out antennas which are not attached or should not
be used for other reasons like regulatory concerns or special setups.
Separate bitmaps are used for RX and TX to allow configuring different antennas
for receiving and transmitting. Each bitmap is 32 bit long, each bit
representing one antenna, starting with antenna 1 at the first bit. If an
antenna bit is set, this means the driver is allowed to use this antenna for RX
or TX respectively; if the bit is not set the hardware is not allowed to use
this antenna.
Using bitmaps has the benefit of allowing for a flexible configuration
interface which can support many different configurations and which can be used
for 802.11n as well as non-802.11n devices. Instead of relying on some hardware
specific assumptions, drivers can use this information to know which antennas
are actually attached to the system and derive their capabilities based on
that.
802.11n devices should enable or disable chains, based on which antennas are
present (If all antennas belonging to a particular chain are disabled, the
entire chain should be disabled). HT capabilities (like STBC, TX Beamforming,
Antenna selection) should be calculated based on the available chains after
applying the antenna masks. Should a 802.11n device have diversity antennas
attached to one of their chains, diversity can be enabled or disabled based on
the antenna information.
Non-802.11n drivers can use the antenna masks to select RX and TX antennas and
to enable or disable antenna diversity.
While covering chainmasks for 802.11n and the standard "legacy" modes "fixed
antenna 1", "fixed antenna 2" and "diversity" this API also allows more rare,
but useful configurations as follows:
1) Send on antenna 1, receive on antenna 2 (or vice versa). This can be used to
have a low gain antenna for TX in order to keep within the regulatory
constraints and a high gain antenna for RX in order to receive weaker signals
("speak softly, but listen harder"). This can be useful for building long-shot
outdoor links. Another usage of this setup is having a low-noise pre-amplifier
on antenna 1 and a power amplifier on the other antenna. This way transmit
noise is mostly kept out of the low noise receive channel.
(This would be bitmaps: tx 1 rx 2).
2) Another similar setup is: Use RX diversity on both antennas, but always send
on antenna 1. Again that would allow us to benefit from a higher gain RX
antenna, while staying within the legal limits.
(This would be: tx 0 rx 3).
3) And finally there can be special experimental setups in research and
development even with pre 802.11n hardware where more than 2 antennas are
available. It's good to keep the API simple, yet flexible.
Signed-off-by: Bruno Randolf <br1@einfach.org>
--
v7: Made bitmasks 32 bit wide and rebased to latest wireless-testing.
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2010-11-10 03:50:50 +00:00
|
|
|
|
|
|
|
tx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX]);
|
|
|
|
rx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]);
|
|
|
|
|
2010-12-08 04:59:24 +00:00
|
|
|
/* reject antenna configurations which don't match the
|
2010-12-16 02:30:22 +00:00
|
|
|
* available antenna masks, except for the "all" mask */
|
|
|
|
if ((~tx_ant && (tx_ant & ~rdev->wiphy.available_antennas_tx)) ||
|
2014-01-15 02:23:45 +00:00
|
|
|
(~rx_ant && (rx_ant & ~rdev->wiphy.available_antennas_rx)))
|
|
|
|
return -EINVAL;
|
2010-12-08 04:59:24 +00:00
|
|
|
|
2010-12-16 02:30:22 +00:00
|
|
|
tx_ant = tx_ant & rdev->wiphy.available_antennas_tx;
|
|
|
|
rx_ant = rx_ant & rdev->wiphy.available_antennas_rx;
|
2010-12-08 04:59:24 +00:00
|
|
|
|
2012-06-27 14:19:42 +00:00
|
|
|
result = rdev_set_antenna(rdev, tx_ant, rx_ant);
|
cfg80211: Add nl80211 antenna configuration
Allow setting of TX and RX antennas configuration via nl80211.
The antenna configuration is defined as a bitmap of allowed antennas to use.
This API can be used to mask out antennas which are not attached or should not
be used for other reasons like regulatory concerns or special setups.
Separate bitmaps are used for RX and TX to allow configuring different antennas
for receiving and transmitting. Each bitmap is 32 bit long, each bit
representing one antenna, starting with antenna 1 at the first bit. If an
antenna bit is set, this means the driver is allowed to use this antenna for RX
or TX respectively; if the bit is not set the hardware is not allowed to use
this antenna.
Using bitmaps has the benefit of allowing for a flexible configuration
interface which can support many different configurations and which can be used
for 802.11n as well as non-802.11n devices. Instead of relying on some hardware
specific assumptions, drivers can use this information to know which antennas
are actually attached to the system and derive their capabilities based on
that.
802.11n devices should enable or disable chains, based on which antennas are
present (If all antennas belonging to a particular chain are disabled, the
entire chain should be disabled). HT capabilities (like STBC, TX Beamforming,
Antenna selection) should be calculated based on the available chains after
applying the antenna masks. Should a 802.11n device have diversity antennas
attached to one of their chains, diversity can be enabled or disabled based on
the antenna information.
Non-802.11n drivers can use the antenna masks to select RX and TX antennas and
to enable or disable antenna diversity.
While covering chainmasks for 802.11n and the standard "legacy" modes "fixed
antenna 1", "fixed antenna 2" and "diversity" this API also allows more rare,
but useful configurations as follows:
1) Send on antenna 1, receive on antenna 2 (or vice versa). This can be used to
have a low gain antenna for TX in order to keep within the regulatory
constraints and a high gain antenna for RX in order to receive weaker signals
("speak softly, but listen harder"). This can be useful for building long-shot
outdoor links. Another usage of this setup is having a low-noise pre-amplifier
on antenna 1 and a power amplifier on the other antenna. This way transmit
noise is mostly kept out of the low noise receive channel.
(This would be bitmaps: tx 1 rx 2).
2) Another similar setup is: Use RX diversity on both antennas, but always send
on antenna 1. Again that would allow us to benefit from a higher gain RX
antenna, while staying within the legal limits.
(This would be: tx 0 rx 3).
3) And finally there can be special experimental setups in research and
development even with pre 802.11n hardware where more than 2 antennas are
available. It's good to keep the API simple, yet flexible.
Signed-off-by: Bruno Randolf <br1@einfach.org>
--
v7: Made bitmasks 32 bit wide and rebased to latest wireless-testing.
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2010-11-10 03:50:50 +00:00
|
|
|
if (result)
|
2014-01-15 02:23:45 +00:00
|
|
|
return result;
|
cfg80211: Add nl80211 antenna configuration
Allow setting of TX and RX antennas configuration via nl80211.
The antenna configuration is defined as a bitmap of allowed antennas to use.
This API can be used to mask out antennas which are not attached or should not
be used for other reasons like regulatory concerns or special setups.
Separate bitmaps are used for RX and TX to allow configuring different antennas
for receiving and transmitting. Each bitmap is 32 bit long, each bit
representing one antenna, starting with antenna 1 at the first bit. If an
antenna bit is set, this means the driver is allowed to use this antenna for RX
or TX respectively; if the bit is not set the hardware is not allowed to use
this antenna.
Using bitmaps has the benefit of allowing for a flexible configuration
interface which can support many different configurations and which can be used
for 802.11n as well as non-802.11n devices. Instead of relying on some hardware
specific assumptions, drivers can use this information to know which antennas
are actually attached to the system and derive their capabilities based on
that.
802.11n devices should enable or disable chains, based on which antennas are
present (If all antennas belonging to a particular chain are disabled, the
entire chain should be disabled). HT capabilities (like STBC, TX Beamforming,
Antenna selection) should be calculated based on the available chains after
applying the antenna masks. Should a 802.11n device have diversity antennas
attached to one of their chains, diversity can be enabled or disabled based on
the antenna information.
Non-802.11n drivers can use the antenna masks to select RX and TX antennas and
to enable or disable antenna diversity.
While covering chainmasks for 802.11n and the standard "legacy" modes "fixed
antenna 1", "fixed antenna 2" and "diversity" this API also allows more rare,
but useful configurations as follows:
1) Send on antenna 1, receive on antenna 2 (or vice versa). This can be used to
have a low gain antenna for TX in order to keep within the regulatory
constraints and a high gain antenna for RX in order to receive weaker signals
("speak softly, but listen harder"). This can be useful for building long-shot
outdoor links. Another usage of this setup is having a low-noise pre-amplifier
on antenna 1 and a power amplifier on the other antenna. This way transmit
noise is mostly kept out of the low noise receive channel.
(This would be bitmaps: tx 1 rx 2).
2) Another similar setup is: Use RX diversity on both antennas, but always send
on antenna 1. Again that would allow us to benefit from a higher gain RX
antenna, while staying within the legal limits.
(This would be: tx 0 rx 3).
3) And finally there can be special experimental setups in research and
development even with pre 802.11n hardware where more than 2 antennas are
available. It's good to keep the API simple, yet flexible.
Signed-off-by: Bruno Randolf <br1@einfach.org>
--
v7: Made bitmasks 32 bit wide and rebased to latest wireless-testing.
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2010-11-10 03:50:50 +00:00
|
|
|
}
|
|
|
|
|
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]);
|
2014-01-15 02:23:45 +00:00
|
|
|
if (retry_short == 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2009-04-20 16:39:05 +00:00
|
|
|
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]);
|
2014-01-15 02:23:45 +00:00
|
|
|
if (retry_long == 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2009-04-20 16:39:05 +00:00
|
|
|
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]);
|
2014-01-15 02:23:45 +00:00
|
|
|
if (frag_threshold < 256)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2009-04-20 16:39:05 +00:00
|
|
|
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]) {
|
2014-09-04 21:57:40 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_WIPHY_DYN_ACK])
|
|
|
|
return -EINVAL;
|
|
|
|
|
2009-12-21 21:50:47 +00:00
|
|
|
coverage_class = nla_get_u8(
|
|
|
|
info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]);
|
|
|
|
changed |= WIPHY_PARAM_COVERAGE_CLASS;
|
|
|
|
}
|
|
|
|
|
2014-09-04 21:57:40 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_WIPHY_DYN_ACK]) {
|
|
|
|
if (!(rdev->wiphy.features & NL80211_FEATURE_ACKTO_ESTIMATION))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
changed |= WIPHY_PARAM_DYN_ACK;
|
2009-12-21 21:50:47 +00:00
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2014-01-15 02:23:45 +00:00
|
|
|
if (!rdev->ops->set_wiphy_params)
|
|
|
|
return -EOPNOTSUPP;
|
2009-04-20 16:39:05 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
2012-06-27 14:19:42 +00:00
|
|
|
result = rdev_set_wiphy_params(rdev, changed);
|
2009-04-20 16:39:05 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
2014-01-15 02:23:45 +00:00
|
|
|
return 0;
|
2007-09-20 17:09:35 +00:00
|
|
|
}
|
|
|
|
|
2012-06-15 13:30:18 +00:00
|
|
|
static inline u64 wdev_id(struct wireless_dev *wdev)
|
|
|
|
{
|
|
|
|
return (u64)wdev->identifier |
|
2014-04-21 04:53:03 +00:00
|
|
|
((u64)wiphy_to_rdev(wdev->wiphy)->wiphy_idx << 32);
|
2012-06-15 13:30:18 +00:00
|
|
|
}
|
2007-09-20 17:09:35 +00:00
|
|
|
|
2012-11-08 20:25:48 +00:00
|
|
|
static int nl80211_send_chandef(struct sk_buff *msg,
|
2013-11-06 12:55:51 +00:00
|
|
|
const struct cfg80211_chan_def *chandef)
|
2012-11-08 20:25:48 +00:00
|
|
|
{
|
2012-11-22 15:59:45 +00:00
|
|
|
WARN_ON(!cfg80211_chandef_valid(chandef));
|
2012-11-08 22:14:50 +00:00
|
|
|
|
2012-11-08 20:25:48 +00:00
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
|
|
|
|
chandef->chan->center_freq))
|
|
|
|
return -ENOBUFS;
|
2012-11-08 22:14:50 +00:00
|
|
|
switch (chandef->width) {
|
|
|
|
case NL80211_CHAN_WIDTH_20_NOHT:
|
|
|
|
case NL80211_CHAN_WIDTH_20:
|
|
|
|
case NL80211_CHAN_WIDTH_40:
|
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
|
|
|
|
cfg80211_get_chandef_type(chandef)))
|
|
|
|
return -ENOBUFS;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_CHANNEL_WIDTH, chandef->width))
|
|
|
|
return -ENOBUFS;
|
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ1, chandef->center_freq1))
|
|
|
|
return -ENOBUFS;
|
|
|
|
if (chandef->center_freq2 &&
|
|
|
|
nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ2, chandef->center_freq2))
|
2012-11-08 20:25:48 +00:00
|
|
|
return -ENOBUFS;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-09-07 20:12:54 +00:00
|
|
|
static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags,
|
2009-04-19 14:23:20 +00:00
|
|
|
struct cfg80211_registered_device *rdev,
|
2012-06-15 15:52:47 +00:00
|
|
|
struct wireless_dev *wdev)
|
2007-09-20 17:09:35 +00:00
|
|
|
{
|
2012-06-15 15:52:47 +00:00
|
|
|
struct net_device *dev = wdev->netdev;
|
2007-09-20 17:09:35 +00:00
|
|
|
void *hdr;
|
|
|
|
|
2012-09-07 20:12:54 +00:00
|
|
|
hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_INTERFACE);
|
2007-09-20 17:09:35 +00:00
|
|
|
if (!hdr)
|
|
|
|
return -1;
|
|
|
|
|
2012-06-15 15:52:47 +00:00
|
|
|
if (dev &&
|
|
|
|
(nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
|
2012-06-15 22:19:54 +00:00
|
|
|
nla_put_string(msg, NL80211_ATTR_IFNAME, dev->name)))
|
2012-06-15 15:52:47 +00:00
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
|
|
|
|
nla_put_u32(msg, NL80211_ATTR_IFTYPE, wdev->iftype) ||
|
2012-06-15 13:30:18 +00:00
|
|
|
nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)) ||
|
2012-06-15 22:19:54 +00:00
|
|
|
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, wdev_address(wdev)) ||
|
2012-03-29 08:41:26 +00:00
|
|
|
nla_put_u32(msg, NL80211_ATTR_GENERATION,
|
|
|
|
rdev->devlist_generation ^
|
|
|
|
(cfg80211_rdev_list_generation << 2)))
|
|
|
|
goto nla_put_failure;
|
2009-08-07 14:17:38 +00:00
|
|
|
|
2012-07-12 17:45:08 +00:00
|
|
|
if (rdev->ops->get_channel) {
|
2012-11-08 20:25:48 +00:00
|
|
|
int ret;
|
|
|
|
struct cfg80211_chan_def chandef;
|
|
|
|
|
|
|
|
ret = rdev_get_channel(rdev, wdev, &chandef);
|
|
|
|
if (ret == 0) {
|
|
|
|
if (nl80211_send_chandef(msg, &chandef))
|
|
|
|
goto nla_put_failure;
|
|
|
|
}
|
2012-04-03 14:39:58 +00:00
|
|
|
}
|
|
|
|
|
2012-11-07 11:52:20 +00:00
|
|
|
if (wdev->ssid_len) {
|
|
|
|
if (nla_put(msg, NL80211_ATTR_SSID, wdev->ssid_len, wdev->ssid))
|
|
|
|
goto nla_put_failure;
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
2013-05-08 19:45:15 +00:00
|
|
|
rtnl_lock();
|
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;
|
|
|
|
|
2012-06-15 12:33:17 +00:00
|
|
|
list_for_each_entry(wdev, &rdev->wdev_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
|
|
|
}
|
2012-09-07 20:12:54 +00:00
|
|
|
if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).portid,
|
2007-09-20 17:09:35 +00:00
|
|
|
cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
2012-06-15 15:52:47 +00:00
|
|
|
rdev, wdev) < 0) {
|
2008-07-29 11:22:51 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if_idx++;
|
2007-09-20 17:09:35 +00:00
|
|
|
}
|
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:
|
2013-05-08 19:45:15 +00:00
|
|
|
rtnl_unlock();
|
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;
|
2014-04-21 04:53:02 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
2012-06-15 15:52:47 +00:00
|
|
|
struct wireless_dev *wdev = info->user_ptr[1];
|
2007-09-20 17:09:35 +00:00
|
|
|
|
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)
|
2010-10-04 19:36:35 +00:00
|
|
|
return -ENOMEM;
|
2007-09-20 17:09:35 +00:00
|
|
|
|
2012-09-07 20:12:54 +00:00
|
|
|
if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0,
|
2014-04-21 04:53:02 +00:00
|
|
|
rdev, wdev) < 0) {
|
2010-10-04 19:36:35 +00:00
|
|
|
nlmsg_free(msg);
|
|
|
|
return -ENOBUFS;
|
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
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 },
|
2013-05-28 11:01:52 +00:00
|
|
|
[NL80211_MNTR_FLAG_ACTIVE] = { .type = NLA_FLAG },
|
2008-01-31 18:48:22 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
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) {
|
2010-06-15 06:50:45 +00:00
|
|
|
if (netdev && (netdev->priv_flags & IFF_BRIDGE_PORT))
|
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
|
|
|
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)
|
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
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;
|
2010-10-04 19:36:35 +00:00
|
|
|
struct net_device *dev = info->user_ptr[1];
|
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-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;
|
2010-10-04 19:36:35 +00:00
|
|
|
if (ntype > NL80211_IFTYPE_MAX)
|
|
|
|
return -EINVAL;
|
2008-09-16 18:22:09 +00:00
|
|
|
}
|
|
|
|
|
2008-09-16 18:39:36 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_MESH_ID]) {
|
2010-12-03 08:20:44 +00:00
|
|
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (ntype != NL80211_IFTYPE_MESH_POINT)
|
|
|
|
return -EINVAL;
|
2010-12-03 08:20:44 +00:00
|
|
|
if (netif_running(dev))
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
wdev_lock(wdev);
|
|
|
|
BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN !=
|
|
|
|
IEEE80211_MAX_MESH_ID_LEN);
|
|
|
|
wdev->mesh_id_up_len =
|
|
|
|
nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
|
|
|
|
memcpy(wdev->ssid, nla_data(info->attrs[NL80211_ATTR_MESH_ID]),
|
|
|
|
wdev->mesh_id_up_len);
|
|
|
|
wdev_unlock(wdev);
|
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)
|
2010-10-04 19:36:35 +00:00
|
|
|
return err;
|
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]) {
|
2010-10-04 19:36:35 +00:00
|
|
|
if (ntype != NL80211_IFTYPE_MONITOR)
|
|
|
|
return -EINVAL;
|
2008-09-16 18:39:36 +00:00
|
|
|
err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS],
|
|
|
|
&_flags);
|
2009-03-21 16:07:59 +00:00
|
|
|
if (err)
|
2010-10-04 19:36:35 +00:00
|
|
|
return err;
|
2009-03-21 16:07:59 +00:00
|
|
|
|
|
|
|
flags = &_flags;
|
|
|
|
change = true;
|
2008-09-16 18:39:36 +00:00
|
|
|
}
|
2009-03-12 08:55:09 +00:00
|
|
|
|
2013-08-29 10:26:57 +00:00
|
|
|
if (flags && (*flags & MONITOR_FLAG_ACTIVE) &&
|
2013-05-28 11:01:52 +00:00
|
|
|
!(rdev->wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
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
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
2008-02-23 14:17:06 +00:00
|
|
|
struct vif_params params;
|
2012-06-15 22:00:26 +00:00
|
|
|
struct wireless_dev *wdev;
|
2012-06-15 22:05:37 +00:00
|
|
|
struct sk_buff *msg;
|
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
|
|
|
|
2014-03-24 16:57:27 +00:00
|
|
|
/* to avoid failing a new interface creation due to pending removal */
|
|
|
|
cfg80211_destroy_ifaces(rdev);
|
|
|
|
|
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-07-07 01:56:12 +00:00
|
|
|
if (!rdev->ops->add_virtual_intf ||
|
2010-10-04 19:36:35 +00:00
|
|
|
!(rdev->wiphy.interface_modes & (1 << type)))
|
|
|
|
return -EOPNOTSUPP;
|
2007-09-20 17:09:35 +00:00
|
|
|
|
2014-10-22 19:23:05 +00:00
|
|
|
if ((type == NL80211_IFTYPE_P2P_DEVICE ||
|
|
|
|
rdev->wiphy.features & NL80211_FEATURE_MAC_ON_CREATE) &&
|
|
|
|
info->attrs[NL80211_ATTR_MAC]) {
|
2013-01-08 09:17:27 +00:00
|
|
|
nla_memcpy(params.macaddr, info->attrs[NL80211_ATTR_MAC],
|
|
|
|
ETH_ALEN);
|
|
|
|
if (!is_valid_ether_addr(params.macaddr))
|
|
|
|
return -EADDRNOTAVAIL;
|
|
|
|
}
|
|
|
|
|
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)
|
2010-10-04 19:36:35 +00:00
|
|
|
return err;
|
2009-11-19 10:55:19 +00:00
|
|
|
}
|
2009-11-10 17:53:10 +00:00
|
|
|
|
2012-06-15 22:05:37 +00:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
|
|
if (!msg)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2008-01-31 18:48:22 +00:00
|
|
|
err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
|
|
|
|
info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
|
|
|
|
&flags);
|
2013-05-28 11:01:52 +00:00
|
|
|
|
2013-08-29 10:26:57 +00:00
|
|
|
if (!err && (flags & MONITOR_FLAG_ACTIVE) &&
|
2013-05-28 11:01:52 +00:00
|
|
|
!(rdev->wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2012-06-27 14:19:42 +00:00
|
|
|
wdev = rdev_add_virtual_intf(rdev,
|
|
|
|
nla_data(info->attrs[NL80211_ATTR_IFNAME]),
|
|
|
|
type, err ? NULL : &flags, ¶ms);
|
2012-06-15 22:05:37 +00:00
|
|
|
if (IS_ERR(wdev)) {
|
|
|
|
nlmsg_free(msg);
|
2012-06-15 22:00:26 +00:00
|
|
|
return PTR_ERR(wdev);
|
2012-06-15 22:05:37 +00:00
|
|
|
}
|
2008-02-23 14:17:06 +00:00
|
|
|
|
2014-03-24 16:57:27 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_IFACE_SOCKET_OWNER])
|
|
|
|
wdev->owner_nlportid = info->snd_portid;
|
|
|
|
|
2012-06-15 22:19:54 +00:00
|
|
|
switch (type) {
|
|
|
|
case NL80211_IFTYPE_MESH_POINT:
|
|
|
|
if (!info->attrs[NL80211_ATTR_MESH_ID])
|
|
|
|
break;
|
2010-12-03 08:20:44 +00:00
|
|
|
wdev_lock(wdev);
|
|
|
|
BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN !=
|
|
|
|
IEEE80211_MAX_MESH_ID_LEN);
|
|
|
|
wdev->mesh_id_up_len =
|
|
|
|
nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
|
|
|
|
memcpy(wdev->ssid, nla_data(info->attrs[NL80211_ATTR_MESH_ID]),
|
|
|
|
wdev->mesh_id_up_len);
|
|
|
|
wdev_unlock(wdev);
|
2012-06-15 22:19:54 +00:00
|
|
|
break;
|
|
|
|
case NL80211_IFTYPE_P2P_DEVICE:
|
|
|
|
/*
|
|
|
|
* P2P Device doesn't have a netdev, so doesn't go
|
|
|
|
* through the netdev notifier and must be added here
|
|
|
|
*/
|
|
|
|
mutex_init(&wdev->mtx);
|
|
|
|
INIT_LIST_HEAD(&wdev->event_list);
|
|
|
|
spin_lock_init(&wdev->event_lock);
|
|
|
|
INIT_LIST_HEAD(&wdev->mgmt_registrations);
|
|
|
|
spin_lock_init(&wdev->mgmt_registrations_lock);
|
|
|
|
|
|
|
|
wdev->identifier = ++rdev->wdev_id;
|
|
|
|
list_add_rcu(&wdev->list, &rdev->wdev_list);
|
|
|
|
rdev->devlist_generation++;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
2010-12-03 08:20:44 +00:00
|
|
|
}
|
|
|
|
|
2012-09-07 20:12:54 +00:00
|
|
|
if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0,
|
2012-06-15 22:05:37 +00:00
|
|
|
rdev, wdev) < 0) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return -ENOBUFS;
|
|
|
|
}
|
|
|
|
|
|
|
|
return genlmsg_reply(msg, info);
|
2007-09-20 17:09:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
2012-06-15 22:00:26 +00:00
|
|
|
struct wireless_dev *wdev = info->user_ptr[1];
|
2007-09-20 17:09:35 +00:00
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (!rdev->ops->del_virtual_intf)
|
|
|
|
return -EOPNOTSUPP;
|
2007-09-20 17:09:35 +00:00
|
|
|
|
2012-06-15 22:00:26 +00:00
|
|
|
/*
|
|
|
|
* If we remove a wireless device without a netdev then clear
|
|
|
|
* user_ptr[1] so that nl80211_post_doit won't dereference it
|
|
|
|
* to check if it needs to do dev_put(). Otherwise it crashes
|
|
|
|
* since the wdev has been freed, unlike with a netdev where
|
|
|
|
* we need the dev_put() for the netdev to really be freed.
|
|
|
|
*/
|
|
|
|
if (!wdev->netdev)
|
|
|
|
info->user_ptr[1] = NULL;
|
|
|
|
|
2012-06-27 14:19:42 +00:00
|
|
|
return rdev_del_virtual_intf(rdev, wdev);
|
2007-09-20 17:09:35 +00:00
|
|
|
}
|
|
|
|
|
2011-11-18 13:20:43 +00:00
|
|
|
static int nl80211_set_noack_map(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
|
|
|
u16 noack_map;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_NOACK_MAP])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!rdev->ops->set_noack_map)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
noack_map = nla_get_u16(info->attrs[NL80211_ATTR_NOACK_MAP]);
|
|
|
|
|
2012-06-27 14:19:42 +00:00
|
|
|
return rdev_set_noack_map(rdev, dev, noack_map);
|
2011-11-18 13:20:43 +00:00
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if ((params->key &&
|
|
|
|
nla_put(cookie->msg, NL80211_ATTR_KEY_DATA,
|
|
|
|
params->key_len, params->key)) ||
|
|
|
|
(params->seq &&
|
|
|
|
nla_put(cookie->msg, NL80211_ATTR_KEY_SEQ,
|
|
|
|
params->seq_len, params->seq)) ||
|
|
|
|
(params->cipher &&
|
|
|
|
nla_put_u32(cookie->msg, NL80211_ATTR_KEY_CIPHER,
|
|
|
|
params->cipher)))
|
|
|
|
goto nla_put_failure;
|
2007-12-19 01:03:29 +00:00
|
|
|
|
2009-07-08 11:29:08 +00:00
|
|
|
key = nla_nest_start(cookie->msg, NL80211_ATTR_KEY);
|
|
|
|
if (!key)
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if ((params->key &&
|
|
|
|
nla_put(cookie->msg, NL80211_KEY_DATA,
|
|
|
|
params->key_len, params->key)) ||
|
|
|
|
(params->seq &&
|
|
|
|
nla_put(cookie->msg, NL80211_KEY_SEQ,
|
|
|
|
params->seq_len, params->seq)) ||
|
|
|
|
(params->cipher &&
|
|
|
|
nla_put_u32(cookie->msg, NL80211_KEY_CIPHER,
|
|
|
|
params->cipher)))
|
|
|
|
goto nla_put_failure;
|
2009-07-08 11:29:08 +00:00
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (nla_put_u8(cookie->msg, NL80211_ATTR_KEY_IDX, cookie->idx))
|
|
|
|
goto nla_put_failure;
|
2009-07-08 11:29:08 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
2007-12-19 01:03:29 +00:00
|
|
|
int err;
|
2010-10-04 19:36:35 +00:00
|
|
|
struct net_device *dev = info->user_ptr[1];
|
2007-12-19 01:03:29 +00:00
|
|
|
u8 key_idx = 0;
|
2010-10-05 17:39:30 +00:00
|
|
|
const u8 *mac_addr = NULL;
|
|
|
|
bool pairwise;
|
2007-12-19 01:03:29 +00:00
|
|
|
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]);
|
|
|
|
|
2010-10-05 17:39:30 +00:00
|
|
|
pairwise = !!mac_addr;
|
|
|
|
if (info->attrs[NL80211_ATTR_KEY_TYPE]) {
|
|
|
|
u32 kt = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]);
|
|
|
|
if (kt >= NUM_NL80211_KEYTYPES)
|
|
|
|
return -EINVAL;
|
|
|
|
if (kt != NL80211_KEYTYPE_GROUP &&
|
|
|
|
kt != NL80211_KEYTYPE_PAIRWISE)
|
|
|
|
return -EINVAL;
|
|
|
|
pairwise = kt == NL80211_KEYTYPE_PAIRWISE;
|
|
|
|
}
|
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (!rdev->ops->get_key)
|
|
|
|
return -EOPNOTSUPP;
|
2007-12-19 01:03:29 +00:00
|
|
|
|
2009-05-19 22:27:55 +00:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
2010-10-04 19:36:35 +00:00
|
|
|
if (!msg)
|
|
|
|
return -ENOMEM;
|
2007-12-19 01:03:29 +00:00
|
|
|
|
2012-09-07 20:12:54 +00:00
|
|
|
hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
|
2007-12-19 01:03:29 +00:00
|
|
|
NL80211_CMD_NEW_KEY);
|
2013-08-14 11:50:01 +00:00
|
|
|
if (!hdr)
|
2013-10-25 09:15:12 +00:00
|
|
|
goto nla_put_failure;
|
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
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
|
|
|
|
nla_put_u8(msg, NL80211_ATTR_KEY_IDX, key_idx))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if (mac_addr &&
|
|
|
|
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr))
|
|
|
|
goto nla_put_failure;
|
2007-12-19 01:03:29 +00:00
|
|
|
|
2010-10-05 17:39:30 +00:00
|
|
|
if (pairwise && mac_addr &&
|
|
|
|
!(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
|
|
|
|
return -ENOENT;
|
|
|
|
|
2012-06-27 14:19:42 +00:00
|
|
|
err = rdev_get_key(rdev, dev, key_idx, pairwise, mac_addr, &cookie,
|
|
|
|
get_key_callback);
|
2007-12-19 01:03:29 +00:00
|
|
|
|
|
|
|
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);
|
2010-10-04 19:36:35 +00:00
|
|
|
return genlmsg_reply(msg, info);
|
2007-12-19 01:03:29 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
2009-07-08 11:29:08 +00:00
|
|
|
struct key_parse key;
|
2007-12-19 01:03:29 +00:00
|
|
|
int err;
|
2010-10-04 19:36:35 +00:00
|
|
|
struct net_device *dev = info->user_ptr[1];
|
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;
|
|
|
|
|
2010-12-09 18:58:59 +00:00
|
|
|
wdev_lock(dev->ieee80211_ptr);
|
2009-01-08 11:32:02 +00:00
|
|
|
|
2010-12-09 18:58:59 +00:00
|
|
|
if (key.def) {
|
|
|
|
if (!rdev->ops->set_default_key) {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
2007-12-19 01:03:29 +00:00
|
|
|
|
2010-12-09 18:58:59 +00:00
|
|
|
err = nl80211_key_allowed(dev->ieee80211_ptr);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
2012-06-27 14:19:42 +00:00
|
|
|
err = rdev_set_default_key(rdev, dev, key.idx,
|
2010-12-09 18:58:59 +00:00
|
|
|
key.def_uni, key.def_multi);
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
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
|
|
|
|
2009-09-29 21:27:28 +00:00
|
|
|
#ifdef CONFIG_CFG80211_WEXT
|
2010-12-09 18:58:59 +00:00
|
|
|
dev->ieee80211_ptr->wext.default_key = key.idx;
|
|
|
|
#endif
|
|
|
|
} else {
|
|
|
|
if (key.def_uni || !key.def_multi) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!rdev->ops->set_default_mgmt_key) {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = nl80211_key_allowed(dev->ieee80211_ptr);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
2012-06-27 14:19:42 +00:00
|
|
|
err = rdev_set_default_mgmt_key(rdev, dev, key.idx);
|
2010-12-09 18:58:59 +00:00
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
#ifdef CONFIG_CFG80211_WEXT
|
|
|
|
dev->ieee80211_ptr->wext.default_mgmt_key = key.idx;
|
2009-05-11 11:54:58 +00:00
|
|
|
#endif
|
2010-12-09 18:58:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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_unlock(dev->ieee80211_ptr);
|
2007-12-19 01:03:29 +00:00
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[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
|
|
|
int err;
|
2010-10-04 19:36:35 +00:00
|
|
|
struct net_device *dev = info->user_ptr[1];
|
2009-07-08 11:29:08 +00:00
|
|
|
struct key_parse key;
|
2010-10-05 17:39:30 +00:00
|
|
|
const u8 *mac_addr = NULL;
|
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.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]);
|
|
|
|
|
2010-10-05 17:39:30 +00:00
|
|
|
if (key.type == -1) {
|
|
|
|
if (mac_addr)
|
|
|
|
key.type = NL80211_KEYTYPE_PAIRWISE;
|
|
|
|
else
|
|
|
|
key.type = NL80211_KEYTYPE_GROUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* for now */
|
|
|
|
if (key.type != NL80211_KEYTYPE_PAIRWISE &&
|
|
|
|
key.type != NL80211_KEYTYPE_GROUP)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (!rdev->ops->add_key)
|
|
|
|
return -EOPNOTSUPP;
|
2009-04-02 18:14:06 +00:00
|
|
|
|
2010-10-05 17:39:30 +00:00
|
|
|
if (cfg80211_validate_key_settings(rdev, &key.p, key.idx,
|
|
|
|
key.type == NL80211_KEYTYPE_PAIRWISE,
|
|
|
|
mac_addr))
|
2010-10-04 19:36:35 +00:00
|
|
|
return -EINVAL;
|
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
|
|
|
wdev_lock(dev->ieee80211_ptr);
|
|
|
|
err = nl80211_key_allowed(dev->ieee80211_ptr);
|
|
|
|
if (!err)
|
2012-06-27 14:19:42 +00:00
|
|
|
err = rdev_add_key(rdev, dev, key.idx,
|
|
|
|
key.type == NL80211_KEYTYPE_PAIRWISE,
|
|
|
|
mac_addr, &key.p);
|
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
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
2007-12-19 01:03:29 +00:00
|
|
|
int err;
|
2010-10-04 19:36:35 +00:00
|
|
|
struct net_device *dev = info->user_ptr[1];
|
2007-12-19 01:03:29 +00:00
|
|
|
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]);
|
|
|
|
|
2010-10-05 17:39:30 +00:00
|
|
|
if (key.type == -1) {
|
|
|
|
if (mac_addr)
|
|
|
|
key.type = NL80211_KEYTYPE_PAIRWISE;
|
|
|
|
else
|
|
|
|
key.type = NL80211_KEYTYPE_GROUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* for now */
|
|
|
|
if (key.type != NL80211_KEYTYPE_PAIRWISE &&
|
|
|
|
key.type != NL80211_KEYTYPE_GROUP)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (!rdev->ops->del_key)
|
|
|
|
return -EOPNOTSUPP;
|
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
|
|
|
wdev_lock(dev->ieee80211_ptr);
|
|
|
|
err = nl80211_key_allowed(dev->ieee80211_ptr);
|
2010-10-05 17:39:30 +00:00
|
|
|
|
|
|
|
if (key.type == NL80211_KEYTYPE_PAIRWISE && mac_addr &&
|
|
|
|
!(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
|
|
|
|
err = -ENOENT;
|
|
|
|
|
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)
|
2012-06-27 14:19:42 +00:00
|
|
|
err = rdev_del_key(rdev, dev, key.idx,
|
|
|
|
key.type == NL80211_KEYTYPE_PAIRWISE,
|
|
|
|
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
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2013-01-18 05:48:45 +00:00
|
|
|
/* This function returns an error or the number of nested attributes */
|
|
|
|
static int validate_acl_mac_addrs(struct nlattr *nl_attr)
|
|
|
|
{
|
|
|
|
struct nlattr *attr;
|
|
|
|
int n_entries = 0, tmp;
|
|
|
|
|
|
|
|
nla_for_each_nested(attr, nl_attr, tmp) {
|
|
|
|
if (nla_len(attr) != ETH_ALEN)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
n_entries++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return n_entries;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This function parses ACL information and allocates memory for ACL data.
|
|
|
|
* On successful return, the calling function is responsible to free the
|
|
|
|
* ACL buffer returned by this function.
|
|
|
|
*/
|
|
|
|
static struct cfg80211_acl_data *parse_acl_data(struct wiphy *wiphy,
|
|
|
|
struct genl_info *info)
|
|
|
|
{
|
|
|
|
enum nl80211_acl_policy acl_policy;
|
|
|
|
struct nlattr *attr;
|
|
|
|
struct cfg80211_acl_data *acl;
|
|
|
|
int i = 0, n_entries, tmp;
|
|
|
|
|
|
|
|
if (!wiphy->max_acl_mac_addrs)
|
|
|
|
return ERR_PTR(-EOPNOTSUPP);
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_ACL_POLICY])
|
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
|
|
|
|
acl_policy = nla_get_u32(info->attrs[NL80211_ATTR_ACL_POLICY]);
|
|
|
|
if (acl_policy != NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED &&
|
|
|
|
acl_policy != NL80211_ACL_POLICY_DENY_UNLESS_LISTED)
|
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_MAC_ADDRS])
|
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
|
|
|
|
n_entries = validate_acl_mac_addrs(info->attrs[NL80211_ATTR_MAC_ADDRS]);
|
|
|
|
if (n_entries < 0)
|
|
|
|
return ERR_PTR(n_entries);
|
|
|
|
|
|
|
|
if (n_entries > wiphy->max_acl_mac_addrs)
|
|
|
|
return ERR_PTR(-ENOTSUPP);
|
|
|
|
|
|
|
|
acl = kzalloc(sizeof(*acl) + (sizeof(struct mac_address) * n_entries),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!acl)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
|
|
nla_for_each_nested(attr, info->attrs[NL80211_ATTR_MAC_ADDRS], tmp) {
|
|
|
|
memcpy(acl->mac_addrs[i].addr, nla_data(attr), ETH_ALEN);
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
acl->n_acl_entries = n_entries;
|
|
|
|
acl->acl_policy = acl_policy;
|
|
|
|
|
|
|
|
return acl;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_set_mac_acl(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
|
|
|
struct cfg80211_acl_data *acl;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
|
|
|
|
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (!dev->ieee80211_ptr->beacon_interval)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
acl = parse_acl_data(&rdev->wiphy, info);
|
|
|
|
if (IS_ERR(acl))
|
|
|
|
return PTR_ERR(acl);
|
|
|
|
|
|
|
|
err = rdev_set_mac_acl(rdev, dev, acl);
|
|
|
|
|
|
|
|
kfree(acl);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2013-06-14 12:15:19 +00:00
|
|
|
static int nl80211_parse_beacon(struct nlattr *attrs[],
|
2012-02-13 14:17:18 +00:00
|
|
|
struct cfg80211_beacon_data *bcn)
|
2007-12-19 01:03:32 +00:00
|
|
|
{
|
2012-02-13 14:17:18 +00:00
|
|
|
bool haveinfo = false;
|
2007-12-19 01:03:32 +00:00
|
|
|
|
2013-06-14 12:15:19 +00:00
|
|
|
if (!is_valid_ie_attr(attrs[NL80211_ATTR_BEACON_TAIL]) ||
|
|
|
|
!is_valid_ie_attr(attrs[NL80211_ATTR_IE]) ||
|
|
|
|
!is_valid_ie_attr(attrs[NL80211_ATTR_IE_PROBE_RESP]) ||
|
|
|
|
!is_valid_ie_attr(attrs[NL80211_ATTR_IE_ASSOC_RESP]))
|
2009-03-27 11:40:28 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2012-02-13 14:17:18 +00:00
|
|
|
memset(bcn, 0, sizeof(*bcn));
|
2007-12-19 01:03:32 +00:00
|
|
|
|
2013-06-14 12:15:19 +00:00
|
|
|
if (attrs[NL80211_ATTR_BEACON_HEAD]) {
|
|
|
|
bcn->head = nla_data(attrs[NL80211_ATTR_BEACON_HEAD]);
|
|
|
|
bcn->head_len = nla_len(attrs[NL80211_ATTR_BEACON_HEAD]);
|
2012-02-13 14:17:18 +00:00
|
|
|
if (!bcn->head_len)
|
|
|
|
return -EINVAL;
|
|
|
|
haveinfo = true;
|
2007-12-19 01:03:32 +00:00
|
|
|
}
|
|
|
|
|
2013-06-14 12:15:19 +00:00
|
|
|
if (attrs[NL80211_ATTR_BEACON_TAIL]) {
|
|
|
|
bcn->tail = nla_data(attrs[NL80211_ATTR_BEACON_TAIL]);
|
|
|
|
bcn->tail_len = nla_len(attrs[NL80211_ATTR_BEACON_TAIL]);
|
2012-02-13 14:17:18 +00:00
|
|
|
haveinfo = true;
|
2007-12-19 01:03:32 +00:00
|
|
|
}
|
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (!haveinfo)
|
|
|
|
return -EINVAL;
|
2009-03-12 08:55:09 +00:00
|
|
|
|
2013-06-14 12:15:19 +00:00
|
|
|
if (attrs[NL80211_ATTR_IE]) {
|
|
|
|
bcn->beacon_ies = nla_data(attrs[NL80211_ATTR_IE]);
|
|
|
|
bcn->beacon_ies_len = nla_len(attrs[NL80211_ATTR_IE]);
|
2011-08-10 20:55:56 +00:00
|
|
|
}
|
|
|
|
|
2013-06-14 12:15:19 +00:00
|
|
|
if (attrs[NL80211_ATTR_IE_PROBE_RESP]) {
|
2012-02-13 14:17:18 +00:00
|
|
|
bcn->proberesp_ies =
|
2013-06-14 12:15:19 +00:00
|
|
|
nla_data(attrs[NL80211_ATTR_IE_PROBE_RESP]);
|
2012-02-13 14:17:18 +00:00
|
|
|
bcn->proberesp_ies_len =
|
2013-06-14 12:15:19 +00:00
|
|
|
nla_len(attrs[NL80211_ATTR_IE_PROBE_RESP]);
|
2011-08-10 20:55:56 +00:00
|
|
|
}
|
|
|
|
|
2013-06-14 12:15:19 +00:00
|
|
|
if (attrs[NL80211_ATTR_IE_ASSOC_RESP]) {
|
2012-02-13 14:17:18 +00:00
|
|
|
bcn->assocresp_ies =
|
2013-06-14 12:15:19 +00:00
|
|
|
nla_data(attrs[NL80211_ATTR_IE_ASSOC_RESP]);
|
2012-02-13 14:17:18 +00:00
|
|
|
bcn->assocresp_ies_len =
|
2013-06-14 12:15:19 +00:00
|
|
|
nla_len(attrs[NL80211_ATTR_IE_ASSOC_RESP]);
|
2011-08-10 20:55:56 +00:00
|
|
|
}
|
|
|
|
|
2013-06-14 12:15:19 +00:00
|
|
|
if (attrs[NL80211_ATTR_PROBE_RESP]) {
|
|
|
|
bcn->probe_resp = nla_data(attrs[NL80211_ATTR_PROBE_RESP]);
|
|
|
|
bcn->probe_resp_len = nla_len(attrs[NL80211_ATTR_PROBE_RESP]);
|
2011-11-10 09:28:56 +00:00
|
|
|
}
|
|
|
|
|
2012-02-13 14:17:18 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-06-19 00:50:57 +00:00
|
|
|
static bool nl80211_get_ap_channel(struct cfg80211_registered_device *rdev,
|
|
|
|
struct cfg80211_ap_settings *params)
|
|
|
|
{
|
|
|
|
struct wireless_dev *wdev;
|
|
|
|
bool ret = false;
|
|
|
|
|
2012-06-15 12:33:17 +00:00
|
|
|
list_for_each_entry(wdev, &rdev->wdev_list, list) {
|
2012-06-19 00:50:57 +00:00
|
|
|
if (wdev->iftype != NL80211_IFTYPE_AP &&
|
|
|
|
wdev->iftype != NL80211_IFTYPE_P2P_GO)
|
|
|
|
continue;
|
|
|
|
|
2012-11-08 20:25:48 +00:00
|
|
|
if (!wdev->preset_chandef.chan)
|
2012-06-19 00:50:57 +00:00
|
|
|
continue;
|
|
|
|
|
2012-11-08 20:25:48 +00:00
|
|
|
params->chandef = wdev->preset_chandef;
|
2012-06-19 00:50:57 +00:00
|
|
|
ret = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-09-30 16:29:39 +00:00
|
|
|
static bool nl80211_valid_auth_type(struct cfg80211_registered_device *rdev,
|
|
|
|
enum nl80211_auth_type auth_type,
|
|
|
|
enum nl80211_commands cmd)
|
|
|
|
{
|
|
|
|
if (auth_type > NL80211_AUTHTYPE_MAX)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case NL80211_CMD_AUTHENTICATE:
|
|
|
|
if (!(rdev->wiphy.features & NL80211_FEATURE_SAE) &&
|
|
|
|
auth_type == NL80211_AUTHTYPE_SAE)
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
case NL80211_CMD_CONNECT:
|
|
|
|
case NL80211_CMD_START_AP:
|
|
|
|
/* SAE not supported yet */
|
|
|
|
if (auth_type == NL80211_AUTHTYPE_SAE)
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-13 14:17:18 +00:00
|
|
|
static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
|
|
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
|
struct cfg80211_ap_settings params;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
|
|
|
|
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (!rdev->ops->start_ap)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (wdev->beacon_interval)
|
|
|
|
return -EALREADY;
|
|
|
|
|
|
|
|
memset(¶ms, 0, sizeof(params));
|
|
|
|
|
|
|
|
/* these are required for START_AP */
|
|
|
|
if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] ||
|
|
|
|
!info->attrs[NL80211_ATTR_DTIM_PERIOD] ||
|
|
|
|
!info->attrs[NL80211_ATTR_BEACON_HEAD])
|
|
|
|
return -EINVAL;
|
|
|
|
|
2013-06-14 12:15:19 +00:00
|
|
|
err = nl80211_parse_beacon(info->attrs, ¶ms.beacon);
|
2012-02-13 14:17:18 +00:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
params.beacon_interval =
|
|
|
|
nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
|
|
|
|
params.dtim_period =
|
|
|
|
nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
|
|
|
|
|
|
|
|
err = cfg80211_validate_beacon_int(rdev, params.beacon_interval);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* In theory, some of these attributes should be required here
|
|
|
|
* but since they were not used when the command was originally
|
|
|
|
* added, keep them optional for old user space programs to let
|
|
|
|
* them continue to work with drivers that do not need the
|
|
|
|
* additional information -- drivers must check!
|
|
|
|
*/
|
|
|
|
if (info->attrs[NL80211_ATTR_SSID]) {
|
|
|
|
params.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
|
|
|
|
params.ssid_len =
|
|
|
|
nla_len(info->attrs[NL80211_ATTR_SSID]);
|
|
|
|
if (params.ssid_len == 0 ||
|
|
|
|
params.ssid_len > IEEE80211_MAX_SSID_LEN)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_HIDDEN_SSID]) {
|
|
|
|
params.hidden_ssid = nla_get_u32(
|
|
|
|
info->attrs[NL80211_ATTR_HIDDEN_SSID]);
|
|
|
|
if (params.hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE &&
|
|
|
|
params.hidden_ssid != NL80211_HIDDEN_SSID_ZERO_LEN &&
|
|
|
|
params.hidden_ssid != NL80211_HIDDEN_SSID_ZERO_CONTENTS)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
params.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
|
|
|
|
params.auth_type = nla_get_u32(
|
|
|
|
info->attrs[NL80211_ATTR_AUTH_TYPE]);
|
2012-09-30 16:29:39 +00:00
|
|
|
if (!nl80211_valid_auth_type(rdev, params.auth_type,
|
|
|
|
NL80211_CMD_START_AP))
|
2012-02-13 14:17:18 +00:00
|
|
|
return -EINVAL;
|
|
|
|
} else
|
|
|
|
params.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
|
|
|
|
|
|
|
|
err = nl80211_crypto_settings(rdev, info, ¶ms.crypto,
|
|
|
|
NL80211_MAX_NR_CIPHER_SUITES);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2012-03-02 10:20:02 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]) {
|
|
|
|
if (!(rdev->wiphy.features & NL80211_FEATURE_INACTIVITY_TIMER))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
params.inactivity_timeout = nla_get_u16(
|
|
|
|
info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]);
|
|
|
|
}
|
|
|
|
|
2012-11-14 14:17:28 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_P2P_CTWINDOW]) {
|
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
|
|
|
|
return -EINVAL;
|
|
|
|
params.p2p_ctwindow =
|
|
|
|
nla_get_u8(info->attrs[NL80211_ATTR_P2P_CTWINDOW]);
|
|
|
|
if (params.p2p_ctwindow > 127)
|
|
|
|
return -EINVAL;
|
|
|
|
if (params.p2p_ctwindow != 0 &&
|
|
|
|
!(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_CTWIN))
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_P2P_OPPPS]) {
|
|
|
|
u8 tmp;
|
|
|
|
|
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
|
|
|
|
return -EINVAL;
|
|
|
|
tmp = nla_get_u8(info->attrs[NL80211_ATTR_P2P_OPPPS]);
|
|
|
|
if (tmp > 1)
|
|
|
|
return -EINVAL;
|
|
|
|
params.p2p_opp_ps = tmp;
|
|
|
|
if (params.p2p_opp_ps != 0 &&
|
|
|
|
!(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_OPPPS))
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2012-05-16 21:50:18 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
|
2012-11-08 20:25:48 +00:00
|
|
|
err = nl80211_parse_chandef(rdev, info, ¶ms.chandef);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
} else if (wdev->preset_chandef.chan) {
|
|
|
|
params.chandef = wdev->preset_chandef;
|
2012-06-19 00:50:57 +00:00
|
|
|
} else if (!nl80211_get_ap_channel(rdev, ¶ms))
|
2012-05-16 21:50:18 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2014-02-23 07:13:01 +00:00
|
|
|
if (!cfg80211_reg_can_beacon(&rdev->wiphy, ¶ms.chandef,
|
|
|
|
wdev->iftype))
|
2012-05-16 21:50:18 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2013-01-18 05:48:45 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_ACL_POLICY]) {
|
|
|
|
params.acl = parse_acl_data(&rdev->wiphy, info);
|
|
|
|
if (IS_ERR(params.acl))
|
|
|
|
return PTR_ERR(params.acl);
|
|
|
|
}
|
|
|
|
|
2014-09-10 11:07:34 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_SMPS_MODE]) {
|
|
|
|
params.smps_mode =
|
|
|
|
nla_get_u8(info->attrs[NL80211_ATTR_SMPS_MODE]);
|
|
|
|
switch (params.smps_mode) {
|
|
|
|
case NL80211_SMPS_OFF:
|
|
|
|
break;
|
|
|
|
case NL80211_SMPS_STATIC:
|
|
|
|
if (!(rdev->wiphy.features &
|
|
|
|
NL80211_FEATURE_STATIC_SMPS))
|
|
|
|
return -EINVAL;
|
|
|
|
break;
|
|
|
|
case NL80211_SMPS_DYNAMIC:
|
|
|
|
if (!(rdev->wiphy.features &
|
|
|
|
NL80211_FEATURE_DYNAMIC_SMPS))
|
|
|
|
return -EINVAL;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
params.smps_mode = NL80211_SMPS_OFF;
|
|
|
|
}
|
|
|
|
|
2013-11-21 17:19:49 +00:00
|
|
|
wdev_lock(wdev);
|
2012-06-27 14:19:42 +00:00
|
|
|
err = rdev_start_ap(rdev, dev, ¶ms);
|
2012-06-19 00:50:57 +00:00
|
|
|
if (!err) {
|
2012-11-08 20:25:48 +00:00
|
|
|
wdev->preset_chandef = params.chandef;
|
2012-02-13 14:17:18 +00:00
|
|
|
wdev->beacon_interval = params.beacon_interval;
|
2014-01-29 13:22:27 +00:00
|
|
|
wdev->chandef = params.chandef;
|
2012-11-07 11:52:19 +00:00
|
|
|
wdev->ssid_len = params.ssid_len;
|
|
|
|
memcpy(wdev->ssid, params.ssid, wdev->ssid_len);
|
2012-06-19 00:50:57 +00:00
|
|
|
}
|
2013-11-21 17:19:49 +00:00
|
|
|
wdev_unlock(wdev);
|
2013-01-18 05:48:45 +00:00
|
|
|
|
|
|
|
kfree(params.acl);
|
|
|
|
|
2011-05-09 16:41:15 +00:00
|
|
|
return err;
|
2007-12-19 01:03:32 +00:00
|
|
|
}
|
|
|
|
|
2012-02-13 14:17:18 +00:00
|
|
|
static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
|
|
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
|
struct cfg80211_beacon_data params;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
|
|
|
|
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (!rdev->ops->change_beacon)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (!wdev->beacon_interval)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2013-06-14 12:15:19 +00:00
|
|
|
err = nl80211_parse_beacon(info->attrs, ¶ms);
|
2012-02-13 14:17:18 +00:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2013-11-21 17:19:49 +00:00
|
|
|
wdev_lock(wdev);
|
|
|
|
err = rdev_change_beacon(rdev, dev, ¶ms);
|
|
|
|
wdev_unlock(wdev);
|
|
|
|
|
|
|
|
return err;
|
2012-02-13 14:17:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_stop_ap(struct sk_buff *skb, struct genl_info *info)
|
2007-12-19 01:03:32 +00:00
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
2007-12-19 01:03:32 +00:00
|
|
|
|
2014-02-25 13:33:38 +00:00
|
|
|
return cfg80211_stop_ap(rdev, dev, false);
|
2007-12-19 01:03:32 +00:00
|
|
|
}
|
|
|
|
|
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 },
|
2011-04-07 22:08:30 +00:00
|
|
|
[NL80211_STA_FLAG_AUTHENTICATED] = { .type = NLA_FLAG },
|
2011-12-14 08:29:15 +00:00
|
|
|
[NL80211_STA_FLAG_TDLS_PEER] = { .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,
|
2012-01-02 12:30:03 +00:00
|
|
|
enum nl80211_iftype iftype,
|
2009-05-11 18:57:56 +00:00
|
|
|
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;
|
2013-02-14 23:48:33 +00:00
|
|
|
params->sta_flags_set &= params->sta_flags_mask;
|
2009-05-11 18:57:56 +00:00
|
|
|
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;
|
|
|
|
|
2012-01-02 12:30:03 +00:00
|
|
|
/*
|
|
|
|
* Only allow certain flags for interface types so that
|
|
|
|
* other attributes are silently ignored. Remember that
|
|
|
|
* this is backward compatibility code with old userspace
|
|
|
|
* and shouldn't be hit in other cases anyway.
|
|
|
|
*/
|
|
|
|
switch (iftype) {
|
|
|
|
case NL80211_IFTYPE_AP:
|
|
|
|
case NL80211_IFTYPE_AP_VLAN:
|
|
|
|
case NL80211_IFTYPE_P2P_GO:
|
|
|
|
params->sta_flags_mask = BIT(NL80211_STA_FLAG_AUTHORIZED) |
|
|
|
|
BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
|
|
|
|
BIT(NL80211_STA_FLAG_WME) |
|
|
|
|
BIT(NL80211_STA_FLAG_MFP);
|
|
|
|
break;
|
|
|
|
case NL80211_IFTYPE_P2P_CLIENT:
|
|
|
|
case NL80211_IFTYPE_STATION:
|
|
|
|
params->sta_flags_mask = BIT(NL80211_STA_FLAG_AUTHORIZED) |
|
|
|
|
BIT(NL80211_STA_FLAG_TDLS_PEER);
|
|
|
|
break;
|
|
|
|
case NL80211_IFTYPE_MESH_POINT:
|
|
|
|
params->sta_flags_mask = BIT(NL80211_STA_FLAG_AUTHENTICATED) |
|
|
|
|
BIT(NL80211_STA_FLAG_MFP) |
|
|
|
|
BIT(NL80211_STA_FLAG_AUTHORIZED);
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2007-12-19 01:03:34 +00:00
|
|
|
|
2012-05-10 18:14:43 +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
|
|
|
|
2012-05-10 18:14:43 +00:00
|
|
|
/* no longer support new API additions in old API */
|
|
|
|
if (flag > NL80211_STA_FLAG_MAX_OLD_API)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-12-19 01:03:34 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-02-27 21:08:00 +00:00
|
|
|
static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info,
|
|
|
|
int attr)
|
|
|
|
{
|
|
|
|
struct nlattr *rate;
|
2012-07-05 11:25:49 +00:00
|
|
|
u32 bitrate;
|
|
|
|
u16 bitrate_compat;
|
2011-02-27 21:08:00 +00:00
|
|
|
|
|
|
|
rate = nla_nest_start(msg, attr);
|
|
|
|
if (!rate)
|
2012-11-09 13:56:41 +00:00
|
|
|
return false;
|
2011-02-27 21:08:00 +00:00
|
|
|
|
|
|
|
/* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */
|
|
|
|
bitrate = cfg80211_calculate_bitrate(info);
|
2012-07-05 11:25:49 +00:00
|
|
|
/* report 16-bit bitrate only if we can */
|
|
|
|
bitrate_compat = bitrate < (1UL << 16) ? bitrate : 0;
|
2012-11-09 13:56:41 +00:00
|
|
|
if (bitrate > 0 &&
|
|
|
|
nla_put_u32(msg, NL80211_RATE_INFO_BITRATE32, bitrate))
|
|
|
|
return false;
|
|
|
|
if (bitrate_compat > 0 &&
|
|
|
|
nla_put_u16(msg, NL80211_RATE_INFO_BITRATE, bitrate_compat))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (info->flags & RATE_INFO_FLAGS_MCS) {
|
|
|
|
if (nla_put_u8(msg, NL80211_RATE_INFO_MCS, info->mcs))
|
|
|
|
return false;
|
|
|
|
if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH &&
|
|
|
|
nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH))
|
|
|
|
return false;
|
|
|
|
if (info->flags & RATE_INFO_FLAGS_SHORT_GI &&
|
|
|
|
nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI))
|
|
|
|
return false;
|
|
|
|
} else if (info->flags & RATE_INFO_FLAGS_VHT_MCS) {
|
|
|
|
if (nla_put_u8(msg, NL80211_RATE_INFO_VHT_MCS, info->mcs))
|
|
|
|
return false;
|
|
|
|
if (nla_put_u8(msg, NL80211_RATE_INFO_VHT_NSS, info->nss))
|
|
|
|
return false;
|
|
|
|
if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH &&
|
|
|
|
nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH))
|
|
|
|
return false;
|
|
|
|
if (info->flags & RATE_INFO_FLAGS_80_MHZ_WIDTH &&
|
|
|
|
nla_put_flag(msg, NL80211_RATE_INFO_80_MHZ_WIDTH))
|
|
|
|
return false;
|
|
|
|
if (info->flags & RATE_INFO_FLAGS_80P80_MHZ_WIDTH &&
|
|
|
|
nla_put_flag(msg, NL80211_RATE_INFO_80P80_MHZ_WIDTH))
|
|
|
|
return false;
|
|
|
|
if (info->flags & RATE_INFO_FLAGS_160_MHZ_WIDTH &&
|
|
|
|
nla_put_flag(msg, NL80211_RATE_INFO_160_MHZ_WIDTH))
|
|
|
|
return false;
|
|
|
|
if (info->flags & RATE_INFO_FLAGS_SHORT_GI &&
|
|
|
|
nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI))
|
|
|
|
return false;
|
|
|
|
}
|
2011-02-27 21:08:00 +00:00
|
|
|
|
|
|
|
nla_nest_end(msg, rate);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-04-22 14:29:30 +00:00
|
|
|
static bool nl80211_put_signal(struct sk_buff *msg, u8 mask, s8 *signal,
|
|
|
|
int id)
|
|
|
|
{
|
|
|
|
void *attr;
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
if (!mask)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
attr = nla_nest_start(msg, id);
|
|
|
|
if (!attr)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
for (i = 0; i < IEEE80211_MAX_CHAINS; i++) {
|
|
|
|
if (!(mask & BIT(i)))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (nla_put_u8(msg, i, signal[i]))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
nla_nest_end(msg, attr);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-09-07 20:12:54 +00:00
|
|
|
static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq,
|
2012-03-15 17:25:41 +00:00
|
|
|
int flags,
|
|
|
|
struct cfg80211_registered_device *rdev,
|
|
|
|
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;
|
2011-03-31 16:25:41 +00:00
|
|
|
struct nlattr *sinfoattr, *bss_param;
|
2007-12-19 01:03:36 +00:00
|
|
|
|
2012-09-07 20:12:54 +00:00
|
|
|
hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_STATION);
|
2007-12-19 01:03:36 +00:00
|
|
|
if (!hdr)
|
|
|
|
return -1;
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
|
|
|
|
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr) ||
|
|
|
|
nla_put_u32(msg, NL80211_ATTR_GENERATION, sinfo->generation))
|
|
|
|
goto nla_put_failure;
|
2009-08-07 14:17:38 +00:00
|
|
|
|
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;
|
2012-03-29 08:41:26 +00:00
|
|
|
if ((sinfo->filled & STATION_INFO_CONNECTED_TIME) &&
|
|
|
|
nla_put_u32(msg, NL80211_STA_INFO_CONNECTED_TIME,
|
|
|
|
sinfo->connected_time))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if ((sinfo->filled & STATION_INFO_INACTIVE_TIME) &&
|
|
|
|
nla_put_u32(msg, NL80211_STA_INFO_INACTIVE_TIME,
|
|
|
|
sinfo->inactive_time))
|
|
|
|
goto nla_put_failure;
|
2013-02-04 11:53:11 +00:00
|
|
|
if ((sinfo->filled & (STATION_INFO_RX_BYTES |
|
|
|
|
STATION_INFO_RX_BYTES64)) &&
|
2012-03-29 08:41:26 +00:00
|
|
|
nla_put_u32(msg, NL80211_STA_INFO_RX_BYTES,
|
2013-02-04 11:53:11 +00:00
|
|
|
(u32)sinfo->rx_bytes))
|
2012-03-29 08:41:26 +00:00
|
|
|
goto nla_put_failure;
|
2013-02-04 11:53:11 +00:00
|
|
|
if ((sinfo->filled & (STATION_INFO_TX_BYTES |
|
2013-05-23 13:05:59 +00:00
|
|
|
STATION_INFO_TX_BYTES64)) &&
|
2012-03-29 08:41:26 +00:00
|
|
|
nla_put_u32(msg, NL80211_STA_INFO_TX_BYTES,
|
2013-02-04 11:53:11 +00:00
|
|
|
(u32)sinfo->tx_bytes))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if ((sinfo->filled & STATION_INFO_RX_BYTES64) &&
|
|
|
|
nla_put_u64(msg, NL80211_STA_INFO_RX_BYTES64,
|
|
|
|
sinfo->rx_bytes))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if ((sinfo->filled & STATION_INFO_TX_BYTES64) &&
|
|
|
|
nla_put_u64(msg, NL80211_STA_INFO_TX_BYTES64,
|
2012-03-29 08:41:26 +00:00
|
|
|
sinfo->tx_bytes))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if ((sinfo->filled & STATION_INFO_LLID) &&
|
|
|
|
nla_put_u16(msg, NL80211_STA_INFO_LLID, sinfo->llid))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if ((sinfo->filled & STATION_INFO_PLID) &&
|
|
|
|
nla_put_u16(msg, NL80211_STA_INFO_PLID, sinfo->plid))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if ((sinfo->filled & STATION_INFO_PLINK_STATE) &&
|
|
|
|
nla_put_u8(msg, NL80211_STA_INFO_PLINK_STATE,
|
|
|
|
sinfo->plink_state))
|
|
|
|
goto nla_put_failure;
|
2012-03-15 17:25:41 +00:00
|
|
|
switch (rdev->wiphy.signal_type) {
|
|
|
|
case CFG80211_SIGNAL_TYPE_MBM:
|
2012-03-29 08:41:26 +00:00
|
|
|
if ((sinfo->filled & STATION_INFO_SIGNAL) &&
|
|
|
|
nla_put_u8(msg, NL80211_STA_INFO_SIGNAL,
|
|
|
|
sinfo->signal))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if ((sinfo->filled & STATION_INFO_SIGNAL_AVG) &&
|
|
|
|
nla_put_u8(msg, NL80211_STA_INFO_SIGNAL_AVG,
|
|
|
|
sinfo->signal_avg))
|
|
|
|
goto nla_put_failure;
|
2012-03-15 17:25:41 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2013-04-22 14:29:30 +00:00
|
|
|
if (sinfo->filled & STATION_INFO_CHAIN_SIGNAL) {
|
|
|
|
if (!nl80211_put_signal(msg, sinfo->chains,
|
|
|
|
sinfo->chain_signal,
|
|
|
|
NL80211_STA_INFO_CHAIN_SIGNAL))
|
|
|
|
goto nla_put_failure;
|
|
|
|
}
|
|
|
|
if (sinfo->filled & STATION_INFO_CHAIN_SIGNAL_AVG) {
|
|
|
|
if (!nl80211_put_signal(msg, sinfo->chains,
|
|
|
|
sinfo->chain_signal_avg,
|
|
|
|
NL80211_STA_INFO_CHAIN_SIGNAL_AVG))
|
|
|
|
goto nla_put_failure;
|
|
|
|
}
|
2008-12-11 21:04:19 +00:00
|
|
|
if (sinfo->filled & STATION_INFO_TX_BITRATE) {
|
2011-02-27 21:08:00 +00:00
|
|
|
if (!nl80211_put_sta_rate(msg, &sinfo->txrate,
|
|
|
|
NL80211_STA_INFO_TX_BITRATE))
|
|
|
|
goto nla_put_failure;
|
|
|
|
}
|
|
|
|
if (sinfo->filled & STATION_INFO_RX_BITRATE) {
|
|
|
|
if (!nl80211_put_sta_rate(msg, &sinfo->rxrate,
|
|
|
|
NL80211_STA_INFO_RX_BITRATE))
|
2008-12-11 21:04:19 +00:00
|
|
|
goto nla_put_failure;
|
|
|
|
}
|
2012-03-29 08:41:26 +00:00
|
|
|
if ((sinfo->filled & STATION_INFO_RX_PACKETS) &&
|
|
|
|
nla_put_u32(msg, NL80211_STA_INFO_RX_PACKETS,
|
|
|
|
sinfo->rx_packets))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if ((sinfo->filled & STATION_INFO_TX_PACKETS) &&
|
|
|
|
nla_put_u32(msg, NL80211_STA_INFO_TX_PACKETS,
|
|
|
|
sinfo->tx_packets))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if ((sinfo->filled & STATION_INFO_TX_RETRIES) &&
|
|
|
|
nla_put_u32(msg, NL80211_STA_INFO_TX_RETRIES,
|
|
|
|
sinfo->tx_retries))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if ((sinfo->filled & STATION_INFO_TX_FAILED) &&
|
|
|
|
nla_put_u32(msg, NL80211_STA_INFO_TX_FAILED,
|
|
|
|
sinfo->tx_failed))
|
|
|
|
goto nla_put_failure;
|
2014-05-19 19:53:19 +00:00
|
|
|
if ((sinfo->filled & STATION_INFO_EXPECTED_THROUGHPUT) &&
|
|
|
|
nla_put_u32(msg, NL80211_STA_INFO_EXPECTED_THROUGHPUT,
|
|
|
|
sinfo->expected_throughput))
|
|
|
|
goto nla_put_failure;
|
2012-03-29 08:41:26 +00:00
|
|
|
if ((sinfo->filled & STATION_INFO_BEACON_LOSS_COUNT) &&
|
|
|
|
nla_put_u32(msg, NL80211_STA_INFO_BEACON_LOSS,
|
|
|
|
sinfo->beacon_loss_count))
|
|
|
|
goto nla_put_failure;
|
2013-01-07 15:04:52 +00:00
|
|
|
if ((sinfo->filled & STATION_INFO_LOCAL_PM) &&
|
|
|
|
nla_put_u32(msg, NL80211_STA_INFO_LOCAL_PM,
|
|
|
|
sinfo->local_pm))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if ((sinfo->filled & STATION_INFO_PEER_PM) &&
|
|
|
|
nla_put_u32(msg, NL80211_STA_INFO_PEER_PM,
|
|
|
|
sinfo->peer_pm))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if ((sinfo->filled & STATION_INFO_NONPEER_PM) &&
|
|
|
|
nla_put_u32(msg, NL80211_STA_INFO_NONPEER_PM,
|
|
|
|
sinfo->nonpeer_pm))
|
|
|
|
goto nla_put_failure;
|
2011-03-31 16:25:41 +00:00
|
|
|
if (sinfo->filled & STATION_INFO_BSS_PARAM) {
|
|
|
|
bss_param = nla_nest_start(msg, NL80211_STA_INFO_BSS_PARAM);
|
|
|
|
if (!bss_param)
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (((sinfo->bss_param.flags & BSS_PARAM_FLAGS_CTS_PROT) &&
|
|
|
|
nla_put_flag(msg, NL80211_STA_BSS_PARAM_CTS_PROT)) ||
|
|
|
|
((sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_PREAMBLE) &&
|
|
|
|
nla_put_flag(msg, NL80211_STA_BSS_PARAM_SHORT_PREAMBLE)) ||
|
|
|
|
((sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_SLOT_TIME) &&
|
|
|
|
nla_put_flag(msg, NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME)) ||
|
|
|
|
nla_put_u8(msg, NL80211_STA_BSS_PARAM_DTIM_PERIOD,
|
|
|
|
sinfo->bss_param.dtim_period) ||
|
|
|
|
nla_put_u16(msg, NL80211_STA_BSS_PARAM_BEACON_INTERVAL,
|
|
|
|
sinfo->bss_param.beacon_interval))
|
|
|
|
goto nla_put_failure;
|
2011-03-31 16:25:41 +00:00
|
|
|
|
|
|
|
nla_nest_end(msg, bss_param);
|
|
|
|
}
|
2012-03-29 08:41:26 +00:00
|
|
|
if ((sinfo->filled & STATION_INFO_STA_FLAGS) &&
|
|
|
|
nla_put(msg, NL80211_STA_INFO_STA_FLAGS,
|
|
|
|
sizeof(struct nl80211_sta_flag_update),
|
|
|
|
&sinfo->sta_flags))
|
|
|
|
goto nla_put_failure;
|
2012-04-12 18:25:14 +00:00
|
|
|
if ((sinfo->filled & STATION_INFO_T_OFFSET) &&
|
|
|
|
nla_put_u64(msg, NL80211_STA_INFO_T_OFFSET,
|
|
|
|
sinfo->t_offset))
|
|
|
|
goto nla_put_failure;
|
2008-02-23 14:17:06 +00:00
|
|
|
nla_nest_end(msg, sinfoattr);
|
2007-12-19 01:03:36 +00:00
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if ((sinfo->filled & STATION_INFO_ASSOC_REQ_IES) &&
|
|
|
|
nla_put(msg, NL80211_ATTR_IE, sinfo->assoc_req_ies_len,
|
|
|
|
sinfo->assoc_req_ies))
|
|
|
|
goto nla_put_failure;
|
2011-08-08 09:11:52 +00:00
|
|
|
|
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;
|
2014-04-21 04:53:02 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
2013-04-18 23:02:55 +00:00
|
|
|
struct wireless_dev *wdev;
|
2008-02-23 14:17:06 +00:00
|
|
|
u8 mac_addr[ETH_ALEN];
|
2013-04-18 23:02:55 +00:00
|
|
|
int sta_idx = cb->args[2];
|
2008-02-23 14:17:06 +00:00
|
|
|
int err;
|
|
|
|
|
2014-04-21 04:53:02 +00:00
|
|
|
err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);
|
2010-10-04 19:14:06 +00:00
|
|
|
if (err)
|
|
|
|
return err;
|
2008-07-29 11:22:51 +00:00
|
|
|
|
2013-04-18 23:02:55 +00:00
|
|
|
if (!wdev->netdev) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out_err;
|
|
|
|
}
|
|
|
|
|
2014-04-21 04:53:02 +00:00
|
|
|
if (!rdev->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) {
|
2011-08-11 08:46:22 +00:00
|
|
|
memset(&sinfo, 0, sizeof(sinfo));
|
2014-04-21 04:53:02 +00:00
|
|
|
err = rdev_dump_station(rdev, wdev->netdev, sta_idx,
|
2012-06-27 14:19:42 +00:00
|
|
|
mac_addr, &sinfo);
|
2008-07-29 11:22:51 +00:00
|
|
|
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,
|
2012-09-07 20:12:54 +00:00
|
|
|
NETLINK_CB(cb->skb).portid,
|
2008-07-29 11:22:51 +00:00
|
|
|
cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
2014-04-21 04:53:02 +00:00
|
|
|
rdev, wdev->netdev, mac_addr,
|
2008-07-29 11:22:51 +00:00
|
|
|
&sinfo) < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
sta_idx++;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
out:
|
2013-04-18 23:02:55 +00:00
|
|
|
cb->args[2] = sta_idx;
|
2008-07-29 11:22:51 +00:00
|
|
|
err = skb->len;
|
|
|
|
out_err:
|
2014-04-21 04:53:02 +00:00
|
|
|
nl80211_finish_wdev_dump(rdev);
|
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)
|
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
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;
|
2010-10-04 19:36:35 +00:00
|
|
|
int err;
|
2007-12-19 01:03:36 +00:00
|
|
|
|
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]);
|
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (!rdev->ops->get_station)
|
|
|
|
return -EOPNOTSUPP;
|
2009-03-12 08:55:09 +00:00
|
|
|
|
2012-06-27 14:19:42 +00:00
|
|
|
err = rdev_get_station(rdev, dev, mac_addr, &sinfo);
|
2007-12-19 01:03:36 +00:00
|
|
|
if (err)
|
2010-10-04 19:36:35 +00:00
|
|
|
return err;
|
2008-02-23 14:17:06 +00:00
|
|
|
|
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)
|
2010-10-04 19:36:35 +00:00
|
|
|
return -ENOMEM;
|
2007-12-19 01:03:36 +00:00
|
|
|
|
2012-09-07 20:12:54 +00:00
|
|
|
if (nl80211_send_station(msg, info->snd_portid, info->snd_seq, 0,
|
2012-03-15 17:25:41 +00:00
|
|
|
rdev, dev, mac_addr, &sinfo) < 0) {
|
2010-10-04 19:36:35 +00:00
|
|
|
nlmsg_free(msg);
|
|
|
|
return -ENOBUFS;
|
|
|
|
}
|
2009-03-12 08:55:09 +00:00
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
return genlmsg_reply(msg, info);
|
2007-12-19 01:03:34 +00:00
|
|
|
}
|
|
|
|
|
2013-02-14 23:48:33 +00:00
|
|
|
int cfg80211_check_station_change(struct wiphy *wiphy,
|
|
|
|
struct station_parameters *params,
|
|
|
|
enum cfg80211_station_type statype)
|
|
|
|
{
|
|
|
|
if (params->listen_interval != -1)
|
|
|
|
return -EINVAL;
|
2014-07-17 14:14:29 +00:00
|
|
|
if (params->aid &&
|
|
|
|
!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
|
2013-02-14 23:48:33 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* When you run into this, adjust the code below for the new flag */
|
|
|
|
BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 7);
|
|
|
|
|
|
|
|
switch (statype) {
|
2013-03-04 21:06:11 +00:00
|
|
|
case CFG80211_STA_MESH_PEER_KERNEL:
|
|
|
|
case CFG80211_STA_MESH_PEER_USER:
|
2013-02-14 23:48:33 +00:00
|
|
|
/*
|
|
|
|
* No ignoring the TDLS flag here -- the userspace mesh
|
|
|
|
* code doesn't have the bug of including TDLS in the
|
|
|
|
* mask everywhere.
|
|
|
|
*/
|
|
|
|
if (params->sta_flags_mask &
|
|
|
|
~(BIT(NL80211_STA_FLAG_AUTHENTICATED) |
|
|
|
|
BIT(NL80211_STA_FLAG_MFP) |
|
|
|
|
BIT(NL80211_STA_FLAG_AUTHORIZED)))
|
|
|
|
return -EINVAL;
|
|
|
|
break;
|
|
|
|
case CFG80211_STA_TDLS_PEER_SETUP:
|
|
|
|
case CFG80211_STA_TDLS_PEER_ACTIVE:
|
|
|
|
if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
|
|
|
|
return -EINVAL;
|
|
|
|
/* ignore since it can't change */
|
|
|
|
params->sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* disallow mesh-specific things */
|
|
|
|
if (params->plink_action != NL80211_PLINK_ACTION_NO_ACTION)
|
|
|
|
return -EINVAL;
|
|
|
|
if (params->local_pm)
|
|
|
|
return -EINVAL;
|
|
|
|
if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (statype != CFG80211_STA_TDLS_PEER_SETUP &&
|
|
|
|
statype != CFG80211_STA_TDLS_PEER_ACTIVE) {
|
|
|
|
/* TDLS can't be set, ... */
|
|
|
|
if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
|
|
|
|
return -EINVAL;
|
|
|
|
/*
|
|
|
|
* ... but don't bother the driver with it. This works around
|
|
|
|
* a hostapd/wpa_supplicant issue -- it always includes the
|
|
|
|
* TLDS_PEER flag in the mask even for AP mode.
|
|
|
|
*/
|
|
|
|
params->sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (statype != CFG80211_STA_TDLS_PEER_SETUP) {
|
|
|
|
/* reject other things that can't change */
|
|
|
|
if (params->sta_modify_mask & STATION_PARAM_APPLY_UAPSD)
|
|
|
|
return -EINVAL;
|
|
|
|
if (params->sta_modify_mask & STATION_PARAM_APPLY_CAPABILITY)
|
|
|
|
return -EINVAL;
|
|
|
|
if (params->supported_rates)
|
|
|
|
return -EINVAL;
|
|
|
|
if (params->ext_capab || params->ht_capa || params->vht_capa)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (statype != CFG80211_STA_AP_CLIENT) {
|
|
|
|
if (params->vlan)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (statype) {
|
|
|
|
case CFG80211_STA_AP_MLME_CLIENT:
|
|
|
|
/* Use this only for authorizing/unauthorizing a station */
|
|
|
|
if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
break;
|
|
|
|
case CFG80211_STA_AP_CLIENT:
|
|
|
|
/* accept only the listed bits */
|
|
|
|
if (params->sta_flags_mask &
|
|
|
|
~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
|
|
|
|
BIT(NL80211_STA_FLAG_AUTHENTICATED) |
|
|
|
|
BIT(NL80211_STA_FLAG_ASSOCIATED) |
|
|
|
|
BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
|
|
|
|
BIT(NL80211_STA_FLAG_WME) |
|
|
|
|
BIT(NL80211_STA_FLAG_MFP)))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* but authenticated/associated only if driver handles it */
|
|
|
|
if (!(wiphy->features & NL80211_FEATURE_FULL_AP_CLIENT_STATE) &&
|
|
|
|
params->sta_flags_mask &
|
|
|
|
(BIT(NL80211_STA_FLAG_AUTHENTICATED) |
|
|
|
|
BIT(NL80211_STA_FLAG_ASSOCIATED)))
|
|
|
|
return -EINVAL;
|
|
|
|
break;
|
|
|
|
case CFG80211_STA_IBSS:
|
|
|
|
case CFG80211_STA_AP_STA:
|
|
|
|
/* reject any changes other than AUTHORIZED */
|
|
|
|
if (params->sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED))
|
|
|
|
return -EINVAL;
|
|
|
|
break;
|
|
|
|
case CFG80211_STA_TDLS_PEER_SETUP:
|
|
|
|
/* reject any changes other than AUTHORIZED or WME */
|
|
|
|
if (params->sta_flags_mask & ~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
|
|
|
|
BIT(NL80211_STA_FLAG_WME)))
|
|
|
|
return -EINVAL;
|
|
|
|
/* force (at least) rates when authorizing */
|
|
|
|
if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED) &&
|
|
|
|
!params->supported_rates)
|
|
|
|
return -EINVAL;
|
|
|
|
break;
|
|
|
|
case CFG80211_STA_TDLS_PEER_ACTIVE:
|
|
|
|
/* reject any changes */
|
|
|
|
return -EINVAL;
|
2013-03-04 21:06:11 +00:00
|
|
|
case CFG80211_STA_MESH_PEER_KERNEL:
|
2013-02-14 23:48:33 +00:00
|
|
|
if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE)
|
|
|
|
return -EINVAL;
|
|
|
|
break;
|
2013-03-04 21:06:11 +00:00
|
|
|
case CFG80211_STA_MESH_PEER_USER:
|
2013-02-14 23:48:33 +00:00
|
|
|
if (params->plink_action != NL80211_PLINK_ACTION_NO_ACTION)
|
|
|
|
return -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(cfg80211_check_station_change);
|
|
|
|
|
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
|
|
|
*/
|
2011-11-18 15:23:01 +00:00
|
|
|
static struct net_device *get_vlan(struct genl_info *info,
|
|
|
|
struct cfg80211_registered_device *rdev)
|
2007-12-19 01:03:34 +00:00
|
|
|
{
|
2009-07-13 22:33:35 +00:00
|
|
|
struct nlattr *vlanattr = info->attrs[NL80211_ATTR_STA_VLAN];
|
2011-11-18 15:23:01 +00:00
|
|
|
struct net_device *v;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!vlanattr)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
v = dev_get_by_index(genl_info_net(info), nla_get_u32(vlanattr));
|
|
|
|
if (!v)
|
|
|
|
return ERR_PTR(-ENODEV);
|
|
|
|
|
|
|
|
if (!v->ieee80211_ptr || v->ieee80211_ptr->wiphy != &rdev->wiphy) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto error;
|
2007-12-19 01:03:34 +00:00
|
|
|
}
|
2011-11-18 15:23:01 +00:00
|
|
|
|
2013-02-14 23:48:33 +00:00
|
|
|
if (v->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
|
|
|
|
v->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
|
|
|
|
v->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2011-11-18 15:23:01 +00:00
|
|
|
if (!netif_running(v)) {
|
|
|
|
ret = -ENETDOWN;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
return v;
|
|
|
|
error:
|
|
|
|
dev_put(v);
|
|
|
|
return ERR_PTR(ret);
|
2007-12-19 01:03:34 +00:00
|
|
|
}
|
|
|
|
|
2014-01-20 22:58:15 +00:00
|
|
|
static const struct nla_policy
|
|
|
|
nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] = {
|
2013-02-14 19:10:54 +00:00
|
|
|
[NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 },
|
|
|
|
[NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 },
|
|
|
|
};
|
|
|
|
|
2013-02-14 23:09:01 +00:00
|
|
|
static int nl80211_parse_sta_wme(struct genl_info *info,
|
|
|
|
struct station_parameters *params)
|
2013-02-14 19:10:54 +00:00
|
|
|
{
|
|
|
|
struct nlattr *tb[NL80211_STA_WME_MAX + 1];
|
|
|
|
struct nlattr *nla;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
/* parse WME attributes if present */
|
|
|
|
if (!info->attrs[NL80211_ATTR_STA_WME])
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
nla = info->attrs[NL80211_ATTR_STA_WME];
|
|
|
|
err = nla_parse_nested(tb, NL80211_STA_WME_MAX, nla,
|
|
|
|
nl80211_sta_wme_policy);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (tb[NL80211_STA_WME_UAPSD_QUEUES])
|
|
|
|
params->uapsd_queues = nla_get_u8(
|
|
|
|
tb[NL80211_STA_WME_UAPSD_QUEUES]);
|
|
|
|
if (params->uapsd_queues & ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (tb[NL80211_STA_WME_MAX_SP])
|
|
|
|
params->max_sp = nla_get_u8(tb[NL80211_STA_WME_MAX_SP]);
|
|
|
|
|
|
|
|
if (params->max_sp & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
params->sta_modify_mask |= STATION_PARAM_APPLY_UAPSD;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-10-09 15:15:21 +00:00
|
|
|
static int nl80211_parse_sta_channel_info(struct genl_info *info,
|
|
|
|
struct station_parameters *params)
|
|
|
|
{
|
|
|
|
if (info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]) {
|
|
|
|
params->supported_channels =
|
|
|
|
nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]);
|
|
|
|
params->supported_channels_len =
|
|
|
|
nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]);
|
|
|
|
/*
|
|
|
|
* Need to include at least one (first channel, number of
|
|
|
|
* channels) tuple for each subband, and must have proper
|
|
|
|
* tuples for the rest of the data as well.
|
|
|
|
*/
|
|
|
|
if (params->supported_channels_len < 2)
|
|
|
|
return -EINVAL;
|
|
|
|
if (params->supported_channels_len % 2)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]) {
|
|
|
|
params->supported_oper_classes =
|
|
|
|
nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]);
|
|
|
|
params->supported_oper_classes_len =
|
|
|
|
nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]);
|
|
|
|
/*
|
|
|
|
* The value of the Length field of the Supported Operating
|
|
|
|
* Classes element is between 2 and 253.
|
|
|
|
*/
|
|
|
|
if (params->supported_oper_classes_len < 2 ||
|
|
|
|
params->supported_oper_classes_len > 253)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-02-14 23:09:01 +00:00
|
|
|
static int nl80211_set_station_tdls(struct genl_info *info,
|
|
|
|
struct station_parameters *params)
|
|
|
|
{
|
2013-10-09 15:15:21 +00:00
|
|
|
int err;
|
2013-02-14 23:09:01 +00:00
|
|
|
/* Dummy STA entry gets updated once the peer capabilities are known */
|
2013-05-16 17:11:08 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_PEER_AID])
|
|
|
|
params->aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]);
|
2013-02-14 23:09:01 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
|
|
|
|
params->ht_capa =
|
|
|
|
nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
|
|
|
|
if (info->attrs[NL80211_ATTR_VHT_CAPABILITY])
|
|
|
|
params->vht_capa =
|
|
|
|
nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
|
|
|
|
|
2013-10-09 15:15:21 +00:00
|
|
|
err = nl80211_parse_sta_channel_info(info, params);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2013-02-14 23:09:01 +00:00
|
|
|
return nl80211_parse_sta_wme(info, params);
|
|
|
|
}
|
|
|
|
|
2007-12-19 01:03:34 +00:00
|
|
|
static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
2007-12-19 01:03:34 +00:00
|
|
|
struct station_parameters params;
|
2013-02-14 23:48:33 +00:00
|
|
|
u8 *mac_addr;
|
|
|
|
int err;
|
2007-12-19 01:03:34 +00:00
|
|
|
|
|
|
|
memset(¶ms, 0, sizeof(params));
|
|
|
|
|
|
|
|
params.listen_interval = -1;
|
|
|
|
|
2013-02-14 23:48:33 +00:00
|
|
|
if (!rdev->ops->change_station)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2007-12-19 01:03:34 +00:00
|
|
|
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]);
|
|
|
|
}
|
|
|
|
|
2013-02-14 19:10:13 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_STA_CAPABILITY]) {
|
|
|
|
params.capability =
|
|
|
|
nla_get_u16(info->attrs[NL80211_ATTR_STA_CAPABILITY]);
|
|
|
|
params.sta_modify_mask |= STATION_PARAM_APPLY_CAPABILITY;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]) {
|
|
|
|
params.ext_capab =
|
|
|
|
nla_data(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]);
|
|
|
|
params.ext_capab_len =
|
|
|
|
nla_len(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]);
|
|
|
|
}
|
|
|
|
|
2013-02-14 19:10:54 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
|
2012-12-27 16:32:09 +00:00
|
|
|
return -EINVAL;
|
2008-08-25 08:58:58 +00:00
|
|
|
|
2012-01-02 12:30:03 +00:00
|
|
|
if (parse_station_flags(info, dev->ieee80211_ptr->iftype, ¶ms))
|
2007-12-19 01:03:34 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2013-02-14 22:27:01 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) {
|
2008-02-23 14:17:06 +00:00
|
|
|
params.plink_action =
|
2013-02-14 22:27:01 +00:00
|
|
|
nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
|
|
|
|
if (params.plink_action >= NUM_NL80211_PLINK_ACTIONS)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2008-02-23 14:17:06 +00:00
|
|
|
|
2013-02-14 22:27:01 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_STA_PLINK_STATE]) {
|
2011-05-03 23:57:11 +00:00
|
|
|
params.plink_state =
|
2013-02-14 22:27:01 +00:00
|
|
|
nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]);
|
|
|
|
if (params.plink_state >= NUM_NL80211_PLINK_STATES)
|
|
|
|
return -EINVAL;
|
|
|
|
params.sta_modify_mask |= STATION_PARAM_APPLY_PLINK_STATE;
|
|
|
|
}
|
2011-05-03 23:57:11 +00:00
|
|
|
|
2013-01-07 15:04:52 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]) {
|
|
|
|
enum nl80211_mesh_power_mode pm = nla_get_u32(
|
|
|
|
info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]);
|
|
|
|
|
|
|
|
if (pm <= NL80211_MESH_POWER_UNKNOWN ||
|
|
|
|
pm > NL80211_MESH_POWER_MAX)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
params.local_pm = pm;
|
|
|
|
}
|
|
|
|
|
2013-02-14 23:48:33 +00:00
|
|
|
/* Include parameters for TDLS peer (will check later) */
|
|
|
|
err = nl80211_set_station_tdls(info, ¶ms);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
params.vlan = get_vlan(info, rdev);
|
|
|
|
if (IS_ERR(params.vlan))
|
|
|
|
return PTR_ERR(params.vlan);
|
|
|
|
|
2009-06-18 15:23:43 +00:00
|
|
|
switch (dev->ieee80211_ptr->iftype) {
|
|
|
|
case NL80211_IFTYPE_AP:
|
|
|
|
case NL80211_IFTYPE_AP_VLAN:
|
2010-09-16 12:58:22 +00:00
|
|
|
case NL80211_IFTYPE_P2P_GO:
|
|
|
|
case NL80211_IFTYPE_P2P_CLIENT:
|
2009-06-18 15:23:43 +00:00
|
|
|
case NL80211_IFTYPE_STATION:
|
2012-01-31 19:25:47 +00:00
|
|
|
case NL80211_IFTYPE_ADHOC:
|
2009-06-18 15:23:43 +00:00
|
|
|
case NL80211_IFTYPE_MESH_POINT:
|
|
|
|
break;
|
|
|
|
default:
|
2013-02-14 23:48:33 +00:00
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out_put_vlan;
|
2009-05-27 08:35:29 +00:00
|
|
|
}
|
|
|
|
|
2013-02-14 23:48:33 +00:00
|
|
|
/* driver will call cfg80211_check_station_change() */
|
2012-06-27 14:19:42 +00:00
|
|
|
err = rdev_change_station(rdev, dev, mac_addr, ¶ms);
|
2007-12-19 01:03:34 +00:00
|
|
|
|
2013-02-14 23:48:33 +00:00
|
|
|
out_put_vlan:
|
2007-12-19 01:03:34 +00:00
|
|
|
if (params.vlan)
|
|
|
|
dev_put(params.vlan);
|
2009-03-12 08:55:09 +00:00
|
|
|
|
2007-12-19 01:03:34 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
2007-12-19 01:03:34 +00:00
|
|
|
int err;
|
2010-10-04 19:36:35 +00:00
|
|
|
struct net_device *dev = info->user_ptr[1];
|
2007-12-19 01:03:34 +00:00
|
|
|
struct station_parameters params;
|
|
|
|
u8 *mac_addr = NULL;
|
|
|
|
|
|
|
|
memset(¶ms, 0, sizeof(params));
|
|
|
|
|
2013-02-14 22:43:25 +00:00
|
|
|
if (!rdev->ops->add_station)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2007-12-19 01:03:34 +00:00
|
|
|
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;
|
|
|
|
|
2013-05-16 17:11:08 +00:00
|
|
|
if (!info->attrs[NL80211_ATTR_STA_AID] &&
|
|
|
|
!info->attrs[NL80211_ATTR_PEER_AID])
|
2010-02-12 14:34:50 +00:00
|
|
|
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
|
|
|
|
2013-05-27 15:24:02 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_PEER_AID])
|
2013-05-16 17:11:08 +00:00
|
|
|
params.aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]);
|
2013-05-27 15:24:02 +00:00
|
|
|
else
|
|
|
|
params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
|
2010-02-12 14:34:50 +00:00
|
|
|
if (!params.aid || params.aid > IEEE80211_MAX_AID)
|
|
|
|
return -EINVAL;
|
2009-05-24 14:42:30 +00:00
|
|
|
|
2013-02-14 19:10:13 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_STA_CAPABILITY]) {
|
|
|
|
params.capability =
|
|
|
|
nla_get_u16(info->attrs[NL80211_ATTR_STA_CAPABILITY]);
|
|
|
|
params.sta_modify_mask |= STATION_PARAM_APPLY_CAPABILITY;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]) {
|
|
|
|
params.ext_capab =
|
|
|
|
nla_data(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]);
|
|
|
|
params.ext_capab_len =
|
|
|
|
nla_len(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]);
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2012-10-11 08:04:52 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_VHT_CAPABILITY])
|
|
|
|
params.vht_capa =
|
|
|
|
nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
|
|
|
|
|
2013-12-03 09:04:59 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_OPMODE_NOTIF]) {
|
|
|
|
params.opmode_notif_used = true;
|
|
|
|
params.opmode_notif =
|
|
|
|
nla_get_u8(info->attrs[NL80211_ATTR_OPMODE_NOTIF]);
|
|
|
|
}
|
|
|
|
|
2013-02-14 22:27:01 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) {
|
2011-04-07 22:08:33 +00:00
|
|
|
params.plink_action =
|
2013-02-14 22:27:01 +00:00
|
|
|
nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
|
|
|
|
if (params.plink_action >= NUM_NL80211_PLINK_ACTIONS)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2011-04-07 22:08:33 +00:00
|
|
|
|
2013-10-09 15:15:21 +00:00
|
|
|
err = nl80211_parse_sta_channel_info(info, ¶ms);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2013-02-14 23:09:01 +00:00
|
|
|
err = nl80211_parse_sta_wme(info, ¶ms);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2011-12-14 11:20:27 +00:00
|
|
|
|
2012-01-02 12:30:03 +00:00
|
|
|
if (parse_station_flags(info, dev->ieee80211_ptr->iftype, ¶ms))
|
2007-12-19 01:03:34 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2013-02-14 23:48:33 +00:00
|
|
|
/* When you run into this, adjust the code below for the new flag */
|
|
|
|
BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 7);
|
|
|
|
|
2011-12-14 11:20:27 +00:00
|
|
|
switch (dev->ieee80211_ptr->iftype) {
|
|
|
|
case NL80211_IFTYPE_AP:
|
|
|
|
case NL80211_IFTYPE_AP_VLAN:
|
|
|
|
case NL80211_IFTYPE_P2P_GO:
|
2013-02-14 22:43:25 +00:00
|
|
|
/* ignore WME attributes if iface/sta is not capable */
|
|
|
|
if (!(rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) ||
|
|
|
|
!(params.sta_flags_set & BIT(NL80211_STA_FLAG_WME)))
|
|
|
|
params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD;
|
2011-08-23 11:37:46 +00:00
|
|
|
|
2011-12-14 11:20:27 +00:00
|
|
|
/* TDLS peers cannot be added */
|
2013-05-27 15:24:02 +00:00
|
|
|
if ((params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) ||
|
|
|
|
info->attrs[NL80211_ATTR_PEER_AID])
|
2011-09-07 09:50:48 +00:00
|
|
|
return -EINVAL;
|
2011-12-14 11:20:27 +00:00
|
|
|
/* but don't bother the driver with it */
|
|
|
|
params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
|
2011-09-27 18:56:12 +00:00
|
|
|
|
nl80211/mac80211: support full station state in AP mode
Today, stations are added already associated. That is
inefficient if, for example, the driver has no room
for stations any more because then the station will
go through the entire auth/assoc handshake, only to
be kicked out afterwards.
To address this a bit better, at least with drivers
using the new station state callback, allow hostapd
to add stations in unauthenticated mode, just after
receiving the AUTH frame, before even replying. Thus
if there's no more space at that point, it can send
a negative auth frame back. It still needs to handle
later state transition errors though, of course.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2012-10-26 15:53:44 +00:00
|
|
|
/* allow authenticated/associated only if driver handles it */
|
|
|
|
if (!(rdev->wiphy.features &
|
|
|
|
NL80211_FEATURE_FULL_AP_CLIENT_STATE) &&
|
|
|
|
params.sta_flags_mask &
|
|
|
|
(BIT(NL80211_STA_FLAG_AUTHENTICATED) |
|
|
|
|
BIT(NL80211_STA_FLAG_ASSOCIATED)))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2011-12-14 11:20:27 +00:00
|
|
|
/* must be last in here for error handling */
|
|
|
|
params.vlan = get_vlan(info, rdev);
|
|
|
|
if (IS_ERR(params.vlan))
|
|
|
|
return PTR_ERR(params.vlan);
|
|
|
|
break;
|
|
|
|
case NL80211_IFTYPE_MESH_POINT:
|
2013-02-14 22:43:25 +00:00
|
|
|
/* ignore uAPSD data */
|
|
|
|
params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD;
|
|
|
|
|
nl80211/mac80211: support full station state in AP mode
Today, stations are added already associated. That is
inefficient if, for example, the driver has no room
for stations any more because then the station will
go through the entire auth/assoc handshake, only to
be kicked out afterwards.
To address this a bit better, at least with drivers
using the new station state callback, allow hostapd
to add stations in unauthenticated mode, just after
receiving the AUTH frame, before even replying. Thus
if there's no more space at that point, it can send
a negative auth frame back. It still needs to handle
later state transition errors though, of course.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2012-10-26 15:53:44 +00:00
|
|
|
/* associated is disallowed */
|
|
|
|
if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_ASSOCIATED))
|
|
|
|
return -EINVAL;
|
2011-12-14 11:20:27 +00:00
|
|
|
/* TDLS peers cannot be added */
|
2013-05-27 15:24:02 +00:00
|
|
|
if ((params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) ||
|
|
|
|
info->attrs[NL80211_ATTR_PEER_AID])
|
2011-12-14 11:20:27 +00:00
|
|
|
return -EINVAL;
|
|
|
|
break;
|
|
|
|
case NL80211_IFTYPE_STATION:
|
2013-03-04 08:29:46 +00:00
|
|
|
case NL80211_IFTYPE_P2P_CLIENT:
|
2013-02-14 22:43:25 +00:00
|
|
|
/* ignore uAPSD data */
|
|
|
|
params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD;
|
|
|
|
|
2013-02-14 23:48:33 +00:00
|
|
|
/* these are disallowed */
|
|
|
|
if (params.sta_flags_mask &
|
|
|
|
(BIT(NL80211_STA_FLAG_ASSOCIATED) |
|
|
|
|
BIT(NL80211_STA_FLAG_AUTHENTICATED)))
|
nl80211/mac80211: support full station state in AP mode
Today, stations are added already associated. That is
inefficient if, for example, the driver has no room
for stations any more because then the station will
go through the entire auth/assoc handshake, only to
be kicked out afterwards.
To address this a bit better, at least with drivers
using the new station state callback, allow hostapd
to add stations in unauthenticated mode, just after
receiving the AUTH frame, before even replying. Thus
if there's no more space at that point, it can send
a negative auth frame back. It still needs to handle
later state transition errors though, of course.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2012-10-26 15:53:44 +00:00
|
|
|
return -EINVAL;
|
2011-12-14 11:20:27 +00:00
|
|
|
/* Only TDLS peers can be added */
|
|
|
|
if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
|
|
|
|
return -EINVAL;
|
|
|
|
/* Can only add if TDLS ... */
|
|
|
|
if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
/* ... with external setup is supported */
|
|
|
|
if (!(rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP))
|
|
|
|
return -EOPNOTSUPP;
|
2013-02-14 23:48:33 +00:00
|
|
|
/*
|
|
|
|
* Older wpa_supplicant versions always mark the TDLS peer
|
|
|
|
* as authorized, but it shouldn't yet be.
|
|
|
|
*/
|
|
|
|
params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_AUTHORIZED);
|
2011-12-14 11:20:27 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EOPNOTSUPP;
|
2011-08-23 11:37:46 +00:00
|
|
|
}
|
|
|
|
|
2011-12-14 11:20:27 +00:00
|
|
|
/* be aware of params.vlan when changing code here */
|
2007-12-19 01:03:34 +00:00
|
|
|
|
2012-06-27 14:19:42 +00:00
|
|
|
err = rdev_add_station(rdev, dev, mac_addr, ¶ms);
|
2007-12-19 01:03:34 +00:00
|
|
|
|
|
|
|
if (params.vlan)
|
|
|
|
dev_put(params.vlan);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
2014-10-10 17:52:40 +00:00
|
|
|
struct station_del_parameters params;
|
|
|
|
|
|
|
|
memset(¶ms, 0, sizeof(params));
|
2007-12-19 01:03:34 +00:00
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_MAC])
|
2014-10-10 17:52:40 +00:00
|
|
|
params.mac = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
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 &&
|
2010-09-16 12:58:22 +00:00
|
|
|
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
|
2010-10-04 19:36:35 +00:00
|
|
|
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
|
|
|
|
return -EINVAL;
|
2007-12-19 01:03:34 +00:00
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (!rdev->ops->del_station)
|
|
|
|
return -EOPNOTSUPP;
|
2009-03-12 08:55:09 +00:00
|
|
|
|
2014-10-20 10:20:45 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_MGMT_SUBTYPE]) {
|
|
|
|
params.subtype =
|
|
|
|
nla_get_u8(info->attrs[NL80211_ATTR_MGMT_SUBTYPE]);
|
|
|
|
if (params.subtype != IEEE80211_STYPE_DISASSOC >> 4 &&
|
|
|
|
params.subtype != IEEE80211_STYPE_DEAUTH >> 4)
|
|
|
|
return -EINVAL;
|
|
|
|
} else {
|
|
|
|
/* Default to Deauthentication frame */
|
|
|
|
params.subtype = IEEE80211_STYPE_DEAUTH >> 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_REASON_CODE]) {
|
|
|
|
params.reason_code =
|
|
|
|
nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
|
|
|
|
if (params.reason_code == 0)
|
|
|
|
return -EINVAL; /* 0 is reserved */
|
|
|
|
} else {
|
|
|
|
/* Default to reason code 2 */
|
|
|
|
params.reason_code = WLAN_REASON_PREV_AUTH_NOT_VALID;
|
|
|
|
}
|
|
|
|
|
2014-10-10 17:52:40 +00:00
|
|
|
return rdev_del_station(rdev, dev, ¶ms);
|
2007-12-19 01:03:34 +00:00
|
|
|
}
|
|
|
|
|
2012-09-07 20:12:54 +00:00
|
|
|
static int nl80211_send_mpath(struct sk_buff *msg, u32 portid, u32 seq,
|
2008-02-23 14:17:06 +00:00
|
|
|
int flags, struct net_device *dev,
|
|
|
|
u8 *dst, u8 *next_hop,
|
|
|
|
struct mpath_info *pinfo)
|
|
|
|
{
|
|
|
|
void *hdr;
|
|
|
|
struct nlattr *pinfoattr;
|
|
|
|
|
2012-09-07 20:12:54 +00:00
|
|
|
hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_STATION);
|
2008-02-23 14:17:06 +00:00
|
|
|
if (!hdr)
|
|
|
|
return -1;
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (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) ||
|
|
|
|
nla_put_u32(msg, NL80211_ATTR_GENERATION, pinfo->generation))
|
|
|
|
goto nla_put_failure;
|
2009-08-07 14:17:38 +00:00
|
|
|
|
2008-02-23 14:17:06 +00:00
|
|
|
pinfoattr = nla_nest_start(msg, NL80211_ATTR_MPATH_INFO);
|
|
|
|
if (!pinfoattr)
|
|
|
|
goto nla_put_failure;
|
2012-03-29 08:41:26 +00:00
|
|
|
if ((pinfo->filled & MPATH_INFO_FRAME_QLEN) &&
|
|
|
|
nla_put_u32(msg, NL80211_MPATH_INFO_FRAME_QLEN,
|
|
|
|
pinfo->frame_qlen))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if (((pinfo->filled & MPATH_INFO_SN) &&
|
|
|
|
nla_put_u32(msg, NL80211_MPATH_INFO_SN, pinfo->sn)) ||
|
|
|
|
((pinfo->filled & MPATH_INFO_METRIC) &&
|
|
|
|
nla_put_u32(msg, NL80211_MPATH_INFO_METRIC,
|
|
|
|
pinfo->metric)) ||
|
|
|
|
((pinfo->filled & MPATH_INFO_EXPTIME) &&
|
|
|
|
nla_put_u32(msg, NL80211_MPATH_INFO_EXPTIME,
|
|
|
|
pinfo->exptime)) ||
|
|
|
|
((pinfo->filled & MPATH_INFO_FLAGS) &&
|
|
|
|
nla_put_u8(msg, NL80211_MPATH_INFO_FLAGS,
|
|
|
|
pinfo->flags)) ||
|
|
|
|
((pinfo->filled & MPATH_INFO_DISCOVERY_TIMEOUT) &&
|
|
|
|
nla_put_u32(msg, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
|
|
|
|
pinfo->discovery_timeout)) ||
|
|
|
|
((pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES) &&
|
|
|
|
nla_put_u8(msg, NL80211_MPATH_INFO_DISCOVERY_RETRIES,
|
|
|
|
pinfo->discovery_retries)))
|
|
|
|
goto nla_put_failure;
|
2008-02-23 14:17:06 +00:00
|
|
|
|
|
|
|
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;
|
2014-04-21 04:53:02 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
2013-04-18 23:02:55 +00:00
|
|
|
struct wireless_dev *wdev;
|
2008-02-23 14:17:06 +00:00
|
|
|
u8 dst[ETH_ALEN];
|
|
|
|
u8 next_hop[ETH_ALEN];
|
2013-04-18 23:02:55 +00:00
|
|
|
int path_idx = cb->args[2];
|
2008-02-23 14:17:06 +00:00
|
|
|
int err;
|
|
|
|
|
2014-04-21 04:53:02 +00:00
|
|
|
err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);
|
2010-10-04 19:14:06 +00:00
|
|
|
if (err)
|
|
|
|
return err;
|
2008-07-29 11:22:51 +00:00
|
|
|
|
2014-04-21 04:53:02 +00:00
|
|
|
if (!rdev->ops->dump_mpath) {
|
2009-03-20 19:21:19 +00:00
|
|
|
err = -EOPNOTSUPP;
|
2008-07-29 11:22:51 +00:00
|
|
|
goto out_err;
|
|
|
|
}
|
|
|
|
|
2013-04-18 23:02:55 +00:00
|
|
|
if (wdev->iftype != NL80211_IFTYPE_MESH_POINT) {
|
2009-03-20 19:21:19 +00:00
|
|
|
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) {
|
2014-04-21 04:53:02 +00:00
|
|
|
err = rdev_dump_mpath(rdev, wdev->netdev, path_idx, dst,
|
2013-04-18 23:02:55 +00:00
|
|
|
next_hop, &pinfo);
|
2008-07-29 11:22:51 +00:00
|
|
|
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
|
|
|
|
2012-09-07 20:12:54 +00:00
|
|
|
if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).portid,
|
2008-07-29 11:22:51 +00:00
|
|
|
cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
2013-04-18 23:02:55 +00:00
|
|
|
wdev->netdev, dst, next_hop,
|
2008-07-29 11:22:51 +00:00
|
|
|
&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:
|
2013-04-18 23:02:55 +00:00
|
|
|
cb->args[2] = path_idx;
|
2008-07-29 11:22:51 +00:00
|
|
|
err = skb->len;
|
|
|
|
out_err:
|
2014-04-21 04:53:02 +00:00
|
|
|
nl80211_finish_wdev_dump(rdev);
|
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)
|
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
2008-02-23 14:17:06 +00:00
|
|
|
int err;
|
2010-10-04 19:36:35 +00:00
|
|
|
struct net_device *dev = info->user_ptr[1];
|
2008-02-23 14:17:06 +00:00
|
|
|
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]);
|
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (!rdev->ops->get_mpath)
|
|
|
|
return -EOPNOTSUPP;
|
2008-02-23 14:17:06 +00:00
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
|
|
|
|
return -EOPNOTSUPP;
|
2009-03-20 19:21:19 +00:00
|
|
|
|
2012-06-27 14:19:42 +00:00
|
|
|
err = rdev_get_mpath(rdev, dev, dst, next_hop, &pinfo);
|
2008-02-23 14:17:06 +00:00
|
|
|
if (err)
|
2010-10-04 19:36:35 +00:00
|
|
|
return err;
|
2008-02-23 14:17:06 +00:00
|
|
|
|
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)
|
2010-10-04 19:36:35 +00:00
|
|
|
return -ENOMEM;
|
2008-02-23 14:17:06 +00:00
|
|
|
|
2012-09-07 20:12:54 +00:00
|
|
|
if (nl80211_send_mpath(msg, info->snd_portid, info->snd_seq, 0,
|
2010-10-04 19:36:35 +00:00
|
|
|
dev, dst, next_hop, &pinfo) < 0) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return -ENOBUFS;
|
|
|
|
}
|
2009-03-12 08:55:09 +00:00
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
return genlmsg_reply(msg, info);
|
2008-02-23 14:17:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
2008-02-23 14:17:06 +00:00
|
|
|
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]);
|
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (!rdev->ops->change_mpath)
|
|
|
|
return -EOPNOTSUPP;
|
2009-03-20 19:21:18 +00:00
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
|
|
|
|
return -EOPNOTSUPP;
|
2008-02-23 14:17:06 +00:00
|
|
|
|
2012-06-27 14:19:42 +00:00
|
|
|
return rdev_change_mpath(rdev, dev, dst, next_hop);
|
2008-02-23 14:17:06 +00:00
|
|
|
}
|
2010-10-04 19:36:35 +00:00
|
|
|
|
2008-02-23 14:17:06 +00:00
|
|
|
static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
2008-02-23 14:17:06 +00:00
|
|
|
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]);
|
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (!rdev->ops->add_mpath)
|
|
|
|
return -EOPNOTSUPP;
|
2009-03-20 19:21:18 +00:00
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
|
|
|
|
return -EOPNOTSUPP;
|
2008-02-23 14:17:06 +00:00
|
|
|
|
2012-06-27 14:19:42 +00:00
|
|
|
return rdev_add_mpath(rdev, dev, dst, next_hop);
|
2008-02-23 14:17:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
2008-02-23 14:17:06 +00:00
|
|
|
u8 *dst = NULL;
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_MAC])
|
|
|
|
dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (!rdev->ops->del_mpath)
|
|
|
|
return -EOPNOTSUPP;
|
2009-03-12 08:55:09 +00:00
|
|
|
|
2012-06-27 14:19:42 +00:00
|
|
|
return rdev_del_mpath(rdev, dev, dst);
|
2008-02-23 14:17:06 +00:00
|
|
|
}
|
|
|
|
|
2014-09-12 06:58:49 +00:00
|
|
|
static int nl80211_get_mpp(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
int err;
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
|
|
|
struct mpath_info pinfo;
|
|
|
|
struct sk_buff *msg;
|
|
|
|
u8 *dst = NULL;
|
|
|
|
u8 mpp[ETH_ALEN];
|
|
|
|
|
|
|
|
memset(&pinfo, 0, sizeof(pinfo));
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_MAC])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
|
|
|
|
|
|
|
if (!rdev->ops->get_mpp)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
err = rdev_get_mpp(rdev, dev, dst, mpp, &pinfo);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
|
|
if (!msg)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
if (nl80211_send_mpath(msg, info->snd_portid, info->snd_seq, 0,
|
|
|
|
dev, dst, mpp, &pinfo) < 0) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return -ENOBUFS;
|
|
|
|
}
|
|
|
|
|
|
|
|
return genlmsg_reply(msg, info);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_dump_mpp(struct sk_buff *skb,
|
|
|
|
struct netlink_callback *cb)
|
|
|
|
{
|
|
|
|
struct mpath_info pinfo;
|
|
|
|
struct cfg80211_registered_device *rdev;
|
|
|
|
struct wireless_dev *wdev;
|
|
|
|
u8 dst[ETH_ALEN];
|
|
|
|
u8 mpp[ETH_ALEN];
|
|
|
|
int path_idx = cb->args[2];
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (!rdev->ops->dump_mpp) {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out_err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wdev->iftype != NL80211_IFTYPE_MESH_POINT) {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out_err;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
err = rdev_dump_mpp(rdev, wdev->netdev, path_idx, dst,
|
|
|
|
mpp, &pinfo);
|
|
|
|
if (err == -ENOENT)
|
|
|
|
break;
|
|
|
|
if (err)
|
|
|
|
goto out_err;
|
|
|
|
|
|
|
|
if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).portid,
|
|
|
|
cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
|
|
|
wdev->netdev, dst, mpp,
|
|
|
|
&pinfo) < 0)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
path_idx++;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
cb->args[2] = path_idx;
|
|
|
|
err = skb->len;
|
|
|
|
out_err:
|
|
|
|
nl80211_finish_wdev_dump(rdev);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2008-08-07 17:07:01 +00:00
|
|
|
static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
2013-11-21 17:19:49 +00:00
|
|
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
2008-08-07 17:07:01 +00:00
|
|
|
struct bss_parameters params;
|
2013-11-21 17:19:49 +00:00
|
|
|
int err;
|
2008-08-07 17:07:01 +00:00
|
|
|
|
|
|
|
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;
|
2010-11-19 11:40:25 +00:00
|
|
|
params.ht_opmode = -1;
|
2012-11-14 14:17:28 +00:00
|
|
|
params.p2p_ctwindow = -1;
|
|
|
|
params.p2p_opp_ps = -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]);
|
2010-11-19 11:40:25 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_BSS_HT_OPMODE])
|
|
|
|
params.ht_opmode =
|
|
|
|
nla_get_u16(info->attrs[NL80211_ATTR_BSS_HT_OPMODE]);
|
2008-08-07 17:07:01 +00:00
|
|
|
|
2012-11-14 14:17:28 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_P2P_CTWINDOW]) {
|
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
|
|
|
|
return -EINVAL;
|
|
|
|
params.p2p_ctwindow =
|
|
|
|
nla_get_s8(info->attrs[NL80211_ATTR_P2P_CTWINDOW]);
|
|
|
|
if (params.p2p_ctwindow < 0)
|
|
|
|
return -EINVAL;
|
|
|
|
if (params.p2p_ctwindow != 0 &&
|
|
|
|
!(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_CTWIN))
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_P2P_OPPPS]) {
|
|
|
|
u8 tmp;
|
|
|
|
|
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
|
|
|
|
return -EINVAL;
|
|
|
|
tmp = nla_get_u8(info->attrs[NL80211_ATTR_P2P_OPPPS]);
|
|
|
|
if (tmp > 1)
|
|
|
|
return -EINVAL;
|
|
|
|
params.p2p_opp_ps = tmp;
|
|
|
|
if (params.p2p_opp_ps &&
|
|
|
|
!(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_OPPPS))
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (!rdev->ops->change_bss)
|
|
|
|
return -EOPNOTSUPP;
|
2008-08-07 17:07:01 +00:00
|
|
|
|
2010-09-16 12:58:22 +00:00
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
|
2010-10-04 19:36:35 +00:00
|
|
|
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
|
|
|
|
return -EOPNOTSUPP;
|
2009-03-12 08:55:09 +00:00
|
|
|
|
2013-11-21 17:19:49 +00:00
|
|
|
wdev_lock(wdev);
|
|
|
|
err = rdev_change_bss(rdev, dev, ¶ms);
|
|
|
|
wdev_unlock(wdev);
|
|
|
|
|
|
|
|
return err;
|
2008-08-07 17:07:01 +00:00
|
|
|
}
|
|
|
|
|
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 },
|
2014-02-21 18:46:12 +00:00
|
|
|
[NL80211_ATTR_DFS_CAC_TIME] = { .type = NLA_U32 },
|
2008-09-10 06:19:48 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
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;
|
2014-02-20 12:52:16 +00:00
|
|
|
if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
|
|
|
|
return -EINVAL;
|
2008-09-10 06:19:48 +00:00
|
|
|
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]);
|
2014-02-20 12:52:16 +00:00
|
|
|
freq_range->max_bandwidth_khz =
|
|
|
|
nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
|
2008-09-10 06:19:48 +00:00
|
|
|
|
|
|
|
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]);
|
|
|
|
|
2014-02-21 18:46:12 +00:00
|
|
|
if (tb[NL80211_ATTR_DFS_CAC_TIME])
|
|
|
|
reg_rule->dfs_cac_ms =
|
|
|
|
nla_get_u32(tb[NL80211_ATTR_DFS_CAC_TIME]);
|
|
|
|
|
2008-09-10 06:19:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
char *data = NULL;
|
2012-07-12 18:49:18 +00:00
|
|
|
enum nl80211_user_reg_hint_type user_reg_hint_type;
|
2008-09-10 06:19:48 +00:00
|
|
|
|
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.
|
|
|
|
*/
|
2012-12-06 14:47:38 +00:00
|
|
|
if (unlikely(!rcu_access_pointer(cfg80211_regdomain)))
|
2009-02-21 05:04:30 +00:00
|
|
|
return -EINPROGRESS;
|
2009-02-21 05:04:22 +00:00
|
|
|
|
2012-07-12 18:49:18 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_USER_REG_HINT_TYPE])
|
|
|
|
user_reg_hint_type =
|
|
|
|
nla_get_u32(info->attrs[NL80211_ATTR_USER_REG_HINT_TYPE]);
|
|
|
|
else
|
|
|
|
user_reg_hint_type = NL80211_USER_REG_HINT_USER;
|
|
|
|
|
|
|
|
switch (user_reg_hint_type) {
|
|
|
|
case NL80211_USER_REG_HINT_USER:
|
|
|
|
case NL80211_USER_REG_HINT_CELL_BASE:
|
2014-02-25 14:26:00 +00:00
|
|
|
if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
|
|
|
|
return regulatory_hint_user(data, user_reg_hint_type);
|
|
|
|
case NL80211_USER_REG_HINT_INDOOR:
|
|
|
|
return regulatory_hint_indoor_user();
|
2012-07-12 18:49:18 +00:00
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2008-09-10 06:19:48 +00:00
|
|
|
}
|
|
|
|
|
2010-12-17 01:37:48 +00:00
|
|
|
static int nl80211_get_mesh_config(struct sk_buff *skb,
|
2010-12-03 08:20:44 +00:00
|
|
|
struct genl_info *info)
|
2008-10-21 19:03:48 +00:00
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
2010-12-03 08:20:44 +00:00
|
|
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
|
struct mesh_config cur_params;
|
|
|
|
int err = 0;
|
2008-10-21 19:03:48 +00:00
|
|
|
void *hdr;
|
|
|
|
struct nlattr *pinfoattr;
|
|
|
|
struct sk_buff *msg;
|
|
|
|
|
2010-12-03 08:20:44 +00:00
|
|
|
if (wdev->iftype != NL80211_IFTYPE_MESH_POINT)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2010-12-17 01:37:48 +00:00
|
|
|
if (!rdev->ops->get_mesh_config)
|
2010-10-04 19:36:35 +00:00
|
|
|
return -EOPNOTSUPP;
|
2009-03-20 15:57:36 +00:00
|
|
|
|
2010-12-03 08:20:44 +00:00
|
|
|
wdev_lock(wdev);
|
|
|
|
/* If not connected, get default parameters */
|
|
|
|
if (!wdev->mesh_id_len)
|
|
|
|
memcpy(&cur_params, &default_mesh_config, sizeof(cur_params));
|
|
|
|
else
|
2012-06-27 14:19:42 +00:00
|
|
|
err = rdev_get_mesh_config(rdev, dev, &cur_params);
|
2010-12-03 08:20:44 +00:00
|
|
|
wdev_unlock(wdev);
|
|
|
|
|
2008-10-21 19:03:48 +00:00
|
|
|
if (err)
|
2010-10-04 19:36:35 +00:00
|
|
|
return err;
|
2008-10-21 19:03:48 +00:00
|
|
|
|
|
|
|
/* Draw up a netlink message to send back */
|
2009-05-19 22:27:55 +00:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
2010-10-04 19:36:35 +00:00
|
|
|
if (!msg)
|
|
|
|
return -ENOMEM;
|
2012-09-07 20:12:54 +00:00
|
|
|
hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
|
2010-12-17 01:37:48 +00:00
|
|
|
NL80211_CMD_GET_MESH_CONFIG);
|
2008-10-21 19:03:48 +00:00
|
|
|
if (!hdr)
|
2011-01-28 14:17:11 +00:00
|
|
|
goto out;
|
2010-12-17 01:37:48 +00:00
|
|
|
pinfoattr = nla_nest_start(msg, NL80211_ATTR_MESH_CONFIG);
|
2008-10-21 19:03:48 +00:00
|
|
|
if (!pinfoattr)
|
|
|
|
goto nla_put_failure;
|
2012-03-29 08:41:26 +00:00
|
|
|
if (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_ELEMENT_TTL,
|
|
|
|
cur_params.element_ttl) ||
|
|
|
|
nla_put_u8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
|
|
|
|
cur_params.auto_open_plinks) ||
|
2012-04-12 18:25:14 +00:00
|
|
|
nla_put_u32(msg, NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR,
|
|
|
|
cur_params.dot11MeshNbrOffsetMaxNeighbor) ||
|
2012-03-29 08:41:26 +00:00
|
|
|
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_PERR_MIN_INTERVAL,
|
|
|
|
cur_params.dot11MeshHWMPperrMinInterval) ||
|
|
|
|
nla_put_u16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
|
|
|
|
cur_params.dot11MeshHWMPnetDiameterTraversalTime) ||
|
|
|
|
nla_put_u8(msg, NL80211_MESHCONF_HWMP_ROOTMODE,
|
|
|
|
cur_params.dot11MeshHWMPRootMode) ||
|
|
|
|
nla_put_u16(msg, NL80211_MESHCONF_HWMP_RANN_INTERVAL,
|
|
|
|
cur_params.dot11MeshHWMPRannInterval) ||
|
|
|
|
nla_put_u8(msg, NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
|
|
|
|
cur_params.dot11MeshGateAnnouncementProtocol) ||
|
|
|
|
nla_put_u8(msg, NL80211_MESHCONF_FORWARDING,
|
|
|
|
cur_params.dot11MeshForwarding) ||
|
|
|
|
nla_put_u32(msg, NL80211_MESHCONF_RSSI_THRESHOLD,
|
2012-04-30 21:20:32 +00:00
|
|
|
cur_params.rssi_threshold) ||
|
|
|
|
nla_put_u32(msg, NL80211_MESHCONF_HT_OPMODE,
|
2012-06-13 18:06:06 +00:00
|
|
|
cur_params.ht_opmode) ||
|
|
|
|
nla_put_u32(msg, NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT,
|
|
|
|
cur_params.dot11MeshHWMPactivePathToRootTimeout) ||
|
|
|
|
nla_put_u16(msg, NL80211_MESHCONF_HWMP_ROOT_INTERVAL,
|
2012-06-13 18:06:10 +00:00
|
|
|
cur_params.dot11MeshHWMProotInterval) ||
|
|
|
|
nla_put_u16(msg, NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
|
2013-01-07 15:04:52 +00:00
|
|
|
cur_params.dot11MeshHWMPconfirmationInterval) ||
|
|
|
|
nla_put_u32(msg, NL80211_MESHCONF_POWER_MODE,
|
|
|
|
cur_params.power_mode) ||
|
|
|
|
nla_put_u16(msg, NL80211_MESHCONF_AWAKE_WINDOW,
|
2013-06-03 16:53:39 +00:00
|
|
|
cur_params.dot11MeshAwakeWindowDuration) ||
|
|
|
|
nla_put_u32(msg, NL80211_MESHCONF_PLINK_TIMEOUT,
|
|
|
|
cur_params.plink_timeout))
|
2012-03-29 08:41:26 +00:00
|
|
|
goto nla_put_failure;
|
2008-10-21 19:03:48 +00:00
|
|
|
nla_nest_end(msg, pinfoattr);
|
|
|
|
genlmsg_end(msg, hdr);
|
2010-10-04 19:36:35 +00:00
|
|
|
return genlmsg_reply(msg, info);
|
2008-10-21 19:03:48 +00:00
|
|
|
|
2009-03-12 08:55:09 +00:00
|
|
|
nla_put_failure:
|
2008-10-21 19:03:48 +00:00
|
|
|
genlmsg_cancel(msg, hdr);
|
2011-01-28 14:17:11 +00:00
|
|
|
out:
|
2010-06-29 11:08:07 +00:00
|
|
|
nlmsg_free(msg);
|
2010-10-04 19:36:35 +00:00
|
|
|
return -ENOBUFS;
|
2008-10-21 19:03:48 +00:00
|
|
|
}
|
|
|
|
|
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 },
|
2010-12-03 08:20:40 +00:00
|
|
|
[NL80211_MESHCONF_ELEMENT_TTL] = { .type = NLA_U8 },
|
2008-10-21 19:03:48 +00:00
|
|
|
[NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 },
|
2012-03-31 18:31:33 +00:00
|
|
|
[NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR] = { .type = NLA_U32 },
|
2008-10-21 19:03:48 +00:00
|
|
|
[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 },
|
2011-11-25 01:15:24 +00:00
|
|
|
[NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL] = { .type = NLA_U16 },
|
2008-10-21 19:03:48 +00:00
|
|
|
[NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 },
|
2011-08-09 23:45:09 +00:00
|
|
|
[NL80211_MESHCONF_HWMP_ROOTMODE] = { .type = NLA_U8 },
|
2011-08-09 23:45:10 +00:00
|
|
|
[NL80211_MESHCONF_HWMP_RANN_INTERVAL] = { .type = NLA_U16 },
|
2011-08-09 23:45:11 +00:00
|
|
|
[NL80211_MESHCONF_GATE_ANNOUNCEMENTS] = { .type = NLA_U8 },
|
2012-01-20 17:02:16 +00:00
|
|
|
[NL80211_MESHCONF_FORWARDING] = { .type = NLA_U8 },
|
2012-06-11 03:59:36 +00:00
|
|
|
[NL80211_MESHCONF_RSSI_THRESHOLD] = { .type = NLA_U32 },
|
|
|
|
[NL80211_MESHCONF_HT_OPMODE] = { .type = NLA_U16 },
|
2012-06-13 18:06:06 +00:00
|
|
|
[NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT] = { .type = NLA_U32 },
|
|
|
|
[NL80211_MESHCONF_HWMP_ROOT_INTERVAL] = { .type = NLA_U16 },
|
2012-06-13 18:06:10 +00:00
|
|
|
[NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL] = { .type = NLA_U16 },
|
2013-01-07 15:04:52 +00:00
|
|
|
[NL80211_MESHCONF_POWER_MODE] = { .type = NLA_U32 },
|
|
|
|
[NL80211_MESHCONF_AWAKE_WINDOW] = { .type = NLA_U16 },
|
2013-06-03 16:53:39 +00:00
|
|
|
[NL80211_MESHCONF_PLINK_TIMEOUT] = { .type = NLA_U32 },
|
2008-10-21 19:03:48 +00:00
|
|
|
};
|
|
|
|
|
2010-12-17 01:37:49 +00:00
|
|
|
static const struct nla_policy
|
|
|
|
nl80211_mesh_setup_params_policy[NL80211_MESH_SETUP_ATTR_MAX+1] = {
|
2012-03-31 18:31:33 +00:00
|
|
|
[NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC] = { .type = NLA_U8 },
|
2010-12-17 01:37:49 +00:00
|
|
|
[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 },
|
|
|
|
[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 },
|
2011-04-07 22:08:28 +00:00
|
|
|
[NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG },
|
2013-05-08 18:45:59 +00:00
|
|
|
[NL80211_MESH_SETUP_AUTH_PROTOCOL] = { .type = NLA_U8 },
|
2013-03-04 21:06:10 +00:00
|
|
|
[NL80211_MESH_SETUP_USERSPACE_MPM] = { .type = NLA_FLAG },
|
2011-04-07 22:08:27 +00:00
|
|
|
[NL80211_MESH_SETUP_IE] = { .type = NLA_BINARY,
|
2012-06-11 03:59:36 +00:00
|
|
|
.len = IEEE80211_MAX_DATA_LEN },
|
2011-05-03 23:57:07 +00:00
|
|
|
[NL80211_MESH_SETUP_USERSPACE_AMPE] = { .type = NLA_FLAG },
|
2010-12-17 01:37:49 +00:00
|
|
|
};
|
|
|
|
|
2010-12-17 01:37:48 +00:00
|
|
|
static int nl80211_parse_mesh_config(struct genl_info *info,
|
2010-12-03 08:20:43 +00:00
|
|
|
struct mesh_config *cfg,
|
|
|
|
u32 *mask_out)
|
2008-10-21 19:03:48 +00:00
|
|
|
{
|
|
|
|
struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1];
|
2010-12-03 08:20:43 +00:00
|
|
|
u32 mask = 0;
|
2008-10-21 19:03:48 +00:00
|
|
|
|
2013-01-07 15:04:48 +00:00
|
|
|
#define FILL_IN_MESH_PARAM_IF_SET(tb, cfg, param, min, max, mask, attr, fn) \
|
|
|
|
do { \
|
|
|
|
if (tb[attr]) { \
|
|
|
|
if (fn(tb[attr]) < min || fn(tb[attr]) > max) \
|
|
|
|
return -EINVAL; \
|
|
|
|
cfg->param = fn(tb[attr]); \
|
|
|
|
mask |= (1 << (attr - 1)); \
|
|
|
|
} \
|
|
|
|
} while (0)
|
2010-12-03 08:20:43 +00:00
|
|
|
|
|
|
|
|
2010-12-17 01:37:48 +00:00
|
|
|
if (!info->attrs[NL80211_ATTR_MESH_CONFIG])
|
2008-10-21 19:03:48 +00:00
|
|
|
return -EINVAL;
|
|
|
|
if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX,
|
2010-12-17 01:37:48 +00:00
|
|
|
info->attrs[NL80211_ATTR_MESH_CONFIG],
|
2010-12-03 08:20:43 +00:00
|
|
|
nl80211_meshconf_params_policy))
|
2008-10-21 19:03:48 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* 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 */
|
2013-01-07 15:04:48 +00:00
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout, 1, 255,
|
2012-06-11 03:59:36 +00:00
|
|
|
mask, NL80211_MESHCONF_RETRY_TIMEOUT,
|
|
|
|
nla_get_u16);
|
2013-01-07 15:04:48 +00:00
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout, 1, 255,
|
2012-06-11 03:59:36 +00:00
|
|
|
mask, NL80211_MESHCONF_CONFIRM_TIMEOUT,
|
|
|
|
nla_get_u16);
|
2013-01-07 15:04:48 +00:00
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout, 1, 255,
|
2012-06-11 03:59:36 +00:00
|
|
|
mask, NL80211_MESHCONF_HOLDING_TIMEOUT,
|
|
|
|
nla_get_u16);
|
2013-01-07 15:04:48 +00:00
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks, 0, 255,
|
2012-06-11 03:59:36 +00:00
|
|
|
mask, NL80211_MESHCONF_MAX_PEER_LINKS,
|
|
|
|
nla_get_u16);
|
2013-01-07 15:04:48 +00:00
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries, 0, 16,
|
2012-06-11 03:59:36 +00:00
|
|
|
mask, NL80211_MESHCONF_MAX_RETRIES,
|
|
|
|
nla_get_u8);
|
2013-01-07 15:04:48 +00:00
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL, 1, 255,
|
2012-06-11 03:59:36 +00:00
|
|
|
mask, NL80211_MESHCONF_TTL, nla_get_u8);
|
2013-01-07 15:04:48 +00:00
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, element_ttl, 1, 255,
|
2012-06-11 03:59:36 +00:00
|
|
|
mask, NL80211_MESHCONF_ELEMENT_TTL,
|
|
|
|
nla_get_u8);
|
2013-01-07 15:04:48 +00:00
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks, 0, 1,
|
2012-06-11 03:59:36 +00:00
|
|
|
mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
|
|
|
|
nla_get_u8);
|
2013-01-07 15:04:48 +00:00
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshNbrOffsetMaxNeighbor,
|
|
|
|
1, 255, mask,
|
2012-06-11 03:59:36 +00:00
|
|
|
NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR,
|
|
|
|
nla_get_u32);
|
2013-01-07 15:04:48 +00:00
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries, 0, 255,
|
2012-06-11 03:59:36 +00:00
|
|
|
mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
|
|
|
|
nla_get_u8);
|
2013-01-07 15:04:48 +00:00
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time, 1, 65535,
|
2012-06-11 03:59:36 +00:00
|
|
|
mask, NL80211_MESHCONF_PATH_REFRESH_TIME,
|
|
|
|
nla_get_u32);
|
2013-01-07 15:04:48 +00:00
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout, 1, 65535,
|
2012-06-11 03:59:36 +00:00
|
|
|
mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
|
|
|
|
nla_get_u16);
|
2013-01-07 15:04:48 +00:00
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout,
|
|
|
|
1, 65535, mask,
|
2012-06-11 03:59:36 +00:00
|
|
|
NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
|
|
|
|
nla_get_u32);
|
2008-10-21 19:03:48 +00:00
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval,
|
2013-01-07 15:04:48 +00:00
|
|
|
1, 65535, mask,
|
|
|
|
NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
|
2012-06-11 03:59:36 +00:00
|
|
|
nla_get_u16);
|
2011-11-25 01:15:24 +00:00
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPperrMinInterval,
|
2013-01-07 15:04:48 +00:00
|
|
|
1, 65535, mask,
|
|
|
|
NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL,
|
2012-06-11 03:59:36 +00:00
|
|
|
nla_get_u16);
|
2008-10-21 19:03:48 +00:00
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
|
2013-01-07 15:04:48 +00:00
|
|
|
dot11MeshHWMPnetDiameterTraversalTime,
|
|
|
|
1, 65535, mask,
|
2012-06-11 03:59:36 +00:00
|
|
|
NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
|
|
|
|
nla_get_u16);
|
2013-01-07 15:04:48 +00:00
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRootMode, 0, 4,
|
|
|
|
mask, NL80211_MESHCONF_HWMP_ROOTMODE,
|
|
|
|
nla_get_u8);
|
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRannInterval, 1, 65535,
|
|
|
|
mask, NL80211_MESHCONF_HWMP_RANN_INTERVAL,
|
2012-06-11 03:59:36 +00:00
|
|
|
nla_get_u16);
|
2009-11-09 23:46:57 +00:00
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
|
2013-01-07 15:04:48 +00:00
|
|
|
dot11MeshGateAnnouncementProtocol, 0, 1,
|
|
|
|
mask, NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
|
2012-06-11 03:59:36 +00:00
|
|
|
nla_get_u8);
|
2013-01-07 15:04:48 +00:00
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshForwarding, 0, 1,
|
2012-06-11 03:59:36 +00:00
|
|
|
mask, NL80211_MESHCONF_FORWARDING,
|
|
|
|
nla_get_u8);
|
2013-07-11 10:24:03 +00:00
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, rssi_threshold, -255, 0,
|
2012-06-11 03:59:36 +00:00
|
|
|
mask, NL80211_MESHCONF_RSSI_THRESHOLD,
|
2013-07-11 10:24:03 +00:00
|
|
|
nla_get_s32);
|
2013-01-07 15:04:48 +00:00
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, ht_opmode, 0, 16,
|
2012-06-11 03:59:36 +00:00
|
|
|
mask, NL80211_MESHCONF_HT_OPMODE,
|
2012-06-13 18:06:06 +00:00
|
|
|
nla_get_u16);
|
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathToRootTimeout,
|
2013-01-07 15:04:48 +00:00
|
|
|
1, 65535, mask,
|
2012-06-13 18:06:06 +00:00
|
|
|
NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT,
|
|
|
|
nla_get_u32);
|
2013-01-07 15:04:48 +00:00
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMProotInterval, 1, 65535,
|
2012-06-13 18:06:06 +00:00
|
|
|
mask, NL80211_MESHCONF_HWMP_ROOT_INTERVAL,
|
2012-06-13 18:06:10 +00:00
|
|
|
nla_get_u16);
|
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
|
2013-01-07 15:04:48 +00:00
|
|
|
dot11MeshHWMPconfirmationInterval,
|
|
|
|
1, 65535, mask,
|
2012-06-13 18:06:10 +00:00
|
|
|
NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
|
2012-06-11 03:59:36 +00:00
|
|
|
nla_get_u16);
|
2013-01-07 15:04:52 +00:00
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, power_mode,
|
|
|
|
NL80211_MESH_POWER_ACTIVE,
|
|
|
|
NL80211_MESH_POWER_MAX,
|
|
|
|
mask, NL80211_MESHCONF_POWER_MODE,
|
|
|
|
nla_get_u32);
|
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshAwakeWindowDuration,
|
|
|
|
0, 65535, mask,
|
|
|
|
NL80211_MESHCONF_AWAKE_WINDOW, nla_get_u16);
|
2013-06-03 16:53:39 +00:00
|
|
|
FILL_IN_MESH_PARAM_IF_SET(tb, cfg, plink_timeout, 1, 0xffffffff,
|
|
|
|
mask, NL80211_MESHCONF_PLINK_TIMEOUT,
|
|
|
|
nla_get_u32);
|
2010-12-03 08:20:43 +00:00
|
|
|
if (mask_out)
|
|
|
|
*mask_out = mask;
|
2010-12-17 01:37:49 +00:00
|
|
|
|
2010-12-03 08:20:43 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
#undef FILL_IN_MESH_PARAM_IF_SET
|
|
|
|
}
|
|
|
|
|
2010-12-17 01:37:49 +00:00
|
|
|
static int nl80211_parse_mesh_setup(struct genl_info *info,
|
|
|
|
struct mesh_setup *setup)
|
|
|
|
{
|
2013-03-04 21:06:10 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
2010-12-17 01:37:49 +00:00
|
|
|
struct nlattr *tb[NL80211_MESH_SETUP_ATTR_MAX + 1];
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_MESH_SETUP])
|
|
|
|
return -EINVAL;
|
|
|
|
if (nla_parse_nested(tb, NL80211_MESH_SETUP_ATTR_MAX,
|
|
|
|
info->attrs[NL80211_ATTR_MESH_SETUP],
|
|
|
|
nl80211_mesh_setup_params_policy))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2012-03-31 18:31:33 +00:00
|
|
|
if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC])
|
|
|
|
setup->sync_method =
|
|
|
|
(nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC])) ?
|
|
|
|
IEEE80211_SYNC_METHOD_VENDOR :
|
|
|
|
IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET;
|
|
|
|
|
2010-12-17 01:37:49 +00:00
|
|
|
if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])
|
|
|
|
setup->path_sel_proto =
|
|
|
|
(nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])) ?
|
|
|
|
IEEE80211_PATH_PROTOCOL_VENDOR :
|
|
|
|
IEEE80211_PATH_PROTOCOL_HWMP;
|
|
|
|
|
|
|
|
if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC])
|
|
|
|
setup->path_metric =
|
|
|
|
(nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC])) ?
|
|
|
|
IEEE80211_PATH_METRIC_VENDOR :
|
|
|
|
IEEE80211_PATH_METRIC_AIRTIME;
|
|
|
|
|
2011-04-07 22:08:27 +00:00
|
|
|
|
|
|
|
if (tb[NL80211_MESH_SETUP_IE]) {
|
2010-12-17 01:37:49 +00:00
|
|
|
struct nlattr *ieattr =
|
2011-04-07 22:08:27 +00:00
|
|
|
tb[NL80211_MESH_SETUP_IE];
|
2010-12-17 01:37:49 +00:00
|
|
|
if (!is_valid_ie_attr(ieattr))
|
|
|
|
return -EINVAL;
|
2011-04-07 22:08:27 +00:00
|
|
|
setup->ie = nla_data(ieattr);
|
|
|
|
setup->ie_len = nla_len(ieattr);
|
2010-12-17 01:37:49 +00:00
|
|
|
}
|
2013-03-04 21:06:10 +00:00
|
|
|
if (tb[NL80211_MESH_SETUP_USERSPACE_MPM] &&
|
|
|
|
!(rdev->wiphy.features & NL80211_FEATURE_USERSPACE_MPM))
|
|
|
|
return -EINVAL;
|
|
|
|
setup->user_mpm = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_MPM]);
|
2011-05-03 23:57:07 +00:00
|
|
|
setup->is_authenticated = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AUTH]);
|
|
|
|
setup->is_secure = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AMPE]);
|
2013-03-04 21:06:10 +00:00
|
|
|
if (setup->is_secure)
|
|
|
|
setup->user_mpm = true;
|
2010-12-17 01:37:49 +00:00
|
|
|
|
2013-05-08 18:45:59 +00:00
|
|
|
if (tb[NL80211_MESH_SETUP_AUTH_PROTOCOL]) {
|
|
|
|
if (!setup->user_mpm)
|
|
|
|
return -EINVAL;
|
|
|
|
setup->auth_id =
|
|
|
|
nla_get_u8(tb[NL80211_MESH_SETUP_AUTH_PROTOCOL]);
|
|
|
|
}
|
|
|
|
|
2010-12-17 01:37:49 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-12-17 01:37:48 +00:00
|
|
|
static int nl80211_update_mesh_config(struct sk_buff *skb,
|
2010-12-03 08:20:44 +00:00
|
|
|
struct genl_info *info)
|
2010-12-03 08:20:43 +00:00
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
2010-12-03 08:20:44 +00:00
|
|
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
2010-12-03 08:20:43 +00:00
|
|
|
struct mesh_config cfg;
|
|
|
|
u32 mask;
|
|
|
|
int err;
|
|
|
|
|
2010-12-03 08:20:44 +00:00
|
|
|
if (wdev->iftype != NL80211_IFTYPE_MESH_POINT)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2010-12-17 01:37:48 +00:00
|
|
|
if (!rdev->ops->update_mesh_config)
|
2010-12-03 08:20:43 +00:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2010-12-17 01:37:48 +00:00
|
|
|
err = nl80211_parse_mesh_config(info, &cfg, &mask);
|
2010-12-03 08:20:43 +00:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2010-12-03 08:20:44 +00:00
|
|
|
wdev_lock(wdev);
|
|
|
|
if (!wdev->mesh_id_len)
|
|
|
|
err = -ENOLINK;
|
|
|
|
|
|
|
|
if (!err)
|
2012-06-27 14:19:42 +00:00
|
|
|
err = rdev_update_mesh_config(rdev, dev, mask, &cfg);
|
2010-12-03 08:20:44 +00:00
|
|
|
|
|
|
|
wdev_unlock(wdev);
|
|
|
|
|
|
|
|
return err;
|
2008-10-21 19:03:48 +00:00
|
|
|
}
|
|
|
|
|
2009-01-30 17:26:42 +00:00
|
|
|
static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2012-12-06 14:47:38 +00:00
|
|
|
const struct ieee80211_regdomain *regdom;
|
2009-01-30 17:26:42 +00:00
|
|
|
struct sk_buff *msg;
|
|
|
|
void *hdr = NULL;
|
|
|
|
struct nlattr *nl_reg_rules;
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
if (!cfg80211_regdomain)
|
2013-05-08 19:45:15 +00:00
|
|
|
return -EINVAL;
|
2009-01-30 17:26:42 +00:00
|
|
|
|
2009-05-19 22:27:55 +00:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
2013-05-08 19:45:15 +00:00
|
|
|
if (!msg)
|
|
|
|
return -ENOBUFS;
|
2009-01-30 17:26:42 +00:00
|
|
|
|
2012-09-07 20:12:54 +00:00
|
|
|
hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
|
2009-01-30 17:26:42 +00:00
|
|
|
NL80211_CMD_GET_REG);
|
|
|
|
if (!hdr)
|
2011-01-28 14:17:11 +00:00
|
|
|
goto put_failure;
|
2009-01-30 17:26:42 +00:00
|
|
|
|
2012-07-12 18:49:18 +00:00
|
|
|
if (reg_last_request_cell_base() &&
|
|
|
|
nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE,
|
|
|
|
NL80211_USER_REG_HINT_CELL_BASE))
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
2012-12-06 14:47:38 +00:00
|
|
|
rcu_read_lock();
|
|
|
|
regdom = rcu_dereference(cfg80211_regdomain);
|
|
|
|
|
|
|
|
if (nla_put_string(msg, NL80211_ATTR_REG_ALPHA2, regdom->alpha2) ||
|
|
|
|
(regdom->dfs_region &&
|
|
|
|
nla_put_u8(msg, NL80211_ATTR_DFS_REGION, regdom->dfs_region)))
|
|
|
|
goto nla_put_failure_rcu;
|
|
|
|
|
2009-01-30 17:26:42 +00:00
|
|
|
nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
|
|
|
|
if (!nl_reg_rules)
|
2012-12-06 14:47:38 +00:00
|
|
|
goto nla_put_failure_rcu;
|
2009-01-30 17:26:42 +00:00
|
|
|
|
2012-12-06 14:47:38 +00:00
|
|
|
for (i = 0; i < regdom->n_reg_rules; i++) {
|
2009-01-30 17:26:42 +00:00
|
|
|
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;
|
cfg80211: regulatory introduce maximum bandwidth calculation
In case we will get regulatory request with rule
where max_bandwidth_khz is set to 0 handle this
case as a special one.
If max_bandwidth_khz == 0 we should calculate maximum
available bandwidth base on all frequency contiguous rules.
In case we need auto calculation we just have to set:
country PL: DFS-ETSI
(2402 - 2482 @ 40), (N/A, 20)
(5170 - 5250 @ AUTO), (N/A, 20)
(5250 - 5330 @ AUTO), (N/A, 20), DFS
(5490 - 5710 @ 80), (N/A, 27), DFS
This mean we will calculate maximum bw for rules where
AUTO (N/A) were set, 160MHz (5330 - 5170) in example above.
So we will get:
(5170 - 5250 @ 160), (N/A, 20)
(5250 - 5330 @ 160), (N/A, 20), DFS
In other case:
country FR: DFS-ETSI
(2402 - 2482 @ 40), (N/A, 20)
(5170 - 5250 @ AUTO), (N/A, 20)
(5250 - 5330 @ 80), (N/A, 20), DFS
(5490 - 5710 @ 80), (N/A, 27), DFS
We will get 80MHz (5250 - 5170):
(5170 - 5250 @ 80), (N/A, 20)
(5250 - 5330 @ 80), (N/A, 20), DFS
Base on this calculations we will set correct channel
bandwidth flags (eg. IEEE80211_CHAN_NO_80MHZ).
We don't need any changes in CRDA or internal regulatory.
Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com>
[extend nl80211 description a bit, fix typo]
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2014-01-30 08:52:20 +00:00
|
|
|
unsigned int max_bandwidth_khz;
|
2009-01-30 17:26:42 +00:00
|
|
|
|
2012-12-06 14:47:38 +00:00
|
|
|
reg_rule = ®dom->reg_rules[i];
|
2009-01-30 17:26:42 +00:00
|
|
|
freq_range = ®_rule->freq_range;
|
|
|
|
power_rule = ®_rule->power_rule;
|
|
|
|
|
|
|
|
nl_reg_rule = nla_nest_start(msg, i);
|
|
|
|
if (!nl_reg_rule)
|
2012-12-06 14:47:38 +00:00
|
|
|
goto nla_put_failure_rcu;
|
2009-01-30 17:26:42 +00:00
|
|
|
|
cfg80211: regulatory introduce maximum bandwidth calculation
In case we will get regulatory request with rule
where max_bandwidth_khz is set to 0 handle this
case as a special one.
If max_bandwidth_khz == 0 we should calculate maximum
available bandwidth base on all frequency contiguous rules.
In case we need auto calculation we just have to set:
country PL: DFS-ETSI
(2402 - 2482 @ 40), (N/A, 20)
(5170 - 5250 @ AUTO), (N/A, 20)
(5250 - 5330 @ AUTO), (N/A, 20), DFS
(5490 - 5710 @ 80), (N/A, 27), DFS
This mean we will calculate maximum bw for rules where
AUTO (N/A) were set, 160MHz (5330 - 5170) in example above.
So we will get:
(5170 - 5250 @ 160), (N/A, 20)
(5250 - 5330 @ 160), (N/A, 20), DFS
In other case:
country FR: DFS-ETSI
(2402 - 2482 @ 40), (N/A, 20)
(5170 - 5250 @ AUTO), (N/A, 20)
(5250 - 5330 @ 80), (N/A, 20), DFS
(5490 - 5710 @ 80), (N/A, 27), DFS
We will get 80MHz (5250 - 5170):
(5170 - 5250 @ 80), (N/A, 20)
(5250 - 5330 @ 80), (N/A, 20), DFS
Base on this calculations we will set correct channel
bandwidth flags (eg. IEEE80211_CHAN_NO_80MHZ).
We don't need any changes in CRDA or internal regulatory.
Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com>
[extend nl80211 description a bit, fix typo]
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2014-01-30 08:52:20 +00:00
|
|
|
max_bandwidth_khz = freq_range->max_bandwidth_khz;
|
|
|
|
if (!max_bandwidth_khz)
|
|
|
|
max_bandwidth_khz = reg_get_max_bandwidth(regdom,
|
|
|
|
reg_rule);
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (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,
|
cfg80211: regulatory introduce maximum bandwidth calculation
In case we will get regulatory request with rule
where max_bandwidth_khz is set to 0 handle this
case as a special one.
If max_bandwidth_khz == 0 we should calculate maximum
available bandwidth base on all frequency contiguous rules.
In case we need auto calculation we just have to set:
country PL: DFS-ETSI
(2402 - 2482 @ 40), (N/A, 20)
(5170 - 5250 @ AUTO), (N/A, 20)
(5250 - 5330 @ AUTO), (N/A, 20), DFS
(5490 - 5710 @ 80), (N/A, 27), DFS
This mean we will calculate maximum bw for rules where
AUTO (N/A) were set, 160MHz (5330 - 5170) in example above.
So we will get:
(5170 - 5250 @ 160), (N/A, 20)
(5250 - 5330 @ 160), (N/A, 20), DFS
In other case:
country FR: DFS-ETSI
(2402 - 2482 @ 40), (N/A, 20)
(5170 - 5250 @ AUTO), (N/A, 20)
(5250 - 5330 @ 80), (N/A, 20), DFS
(5490 - 5710 @ 80), (N/A, 27), DFS
We will get 80MHz (5250 - 5170):
(5170 - 5250 @ 80), (N/A, 20)
(5250 - 5330 @ 80), (N/A, 20), DFS
Base on this calculations we will set correct channel
bandwidth flags (eg. IEEE80211_CHAN_NO_80MHZ).
We don't need any changes in CRDA or internal regulatory.
Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com>
[extend nl80211 description a bit, fix typo]
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2014-01-30 08:52:20 +00:00
|
|
|
max_bandwidth_khz) ||
|
2012-03-29 08:41:26 +00:00
|
|
|
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,
|
2014-02-21 18:46:12 +00:00
|
|
|
power_rule->max_eirp) ||
|
|
|
|
nla_put_u32(msg, NL80211_ATTR_DFS_CAC_TIME,
|
|
|
|
reg_rule->dfs_cac_ms))
|
2012-12-06 14:47:38 +00:00
|
|
|
goto nla_put_failure_rcu;
|
2009-01-30 17:26:42 +00:00
|
|
|
|
|
|
|
nla_nest_end(msg, nl_reg_rule);
|
|
|
|
}
|
2012-12-06 14:47:38 +00:00
|
|
|
rcu_read_unlock();
|
2009-01-30 17:26:42 +00:00
|
|
|
|
|
|
|
nla_nest_end(msg, nl_reg_rules);
|
|
|
|
|
|
|
|
genlmsg_end(msg, hdr);
|
2013-05-08 19:45:15 +00:00
|
|
|
return genlmsg_reply(msg, info);
|
2009-01-30 17:26:42 +00:00
|
|
|
|
2012-12-06 14:47:38 +00:00
|
|
|
nla_put_failure_rcu:
|
|
|
|
rcu_read_unlock();
|
2009-01-30 17:26:42 +00:00
|
|
|
nla_put_failure:
|
|
|
|
genlmsg_cancel(msg, hdr);
|
2011-01-28 14:17:11 +00:00
|
|
|
put_failure:
|
2010-06-29 11:08:07 +00:00
|
|
|
nlmsg_free(msg);
|
2013-05-08 19:45:15 +00:00
|
|
|
return -EMSGSIZE;
|
2009-01-30 17:26:42 +00:00
|
|
|
}
|
|
|
|
|
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;
|
2013-11-13 17:54:02 +00:00
|
|
|
enum nl80211_dfs_regions dfs_region = NL80211_DFS_UNSET;
|
2008-09-10 06:19:48 +00:00
|
|
|
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]);
|
|
|
|
|
2011-10-11 17:59:02 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_DFS_REGION])
|
|
|
|
dfs_region = nla_get_u8(info->attrs[NL80211_ATTR_DFS_REGION]);
|
|
|
|
|
2008-09-10 06:19:48 +00:00
|
|
|
nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
|
2012-12-03 16:21:11 +00:00
|
|
|
rem_reg_rules) {
|
2008-09-10 06:19:48 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2013-11-05 17:18:01 +00:00
|
|
|
if (!reg_is_valid_request(alpha2))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2008-09-10 06:19:48 +00:00
|
|
|
size_of_regd = sizeof(struct ieee80211_regdomain) +
|
2012-12-03 16:21:11 +00:00
|
|
|
num_rules * sizeof(struct ieee80211_reg_rule);
|
2008-09-10 06:19:48 +00:00
|
|
|
|
|
|
|
rd = kzalloc(size_of_regd, GFP_KERNEL);
|
2012-12-03 23:48:59 +00:00
|
|
|
if (!rd)
|
|
|
|
return -ENOMEM;
|
2008-09-10 06:19:48 +00:00
|
|
|
|
|
|
|
rd->n_reg_rules = num_rules;
|
|
|
|
rd->alpha2[0] = alpha2[0];
|
|
|
|
rd->alpha2[1] = alpha2[1];
|
|
|
|
|
2011-10-11 17:59:02 +00:00
|
|
|
/*
|
|
|
|
* Disable DFS master mode if the DFS region was
|
|
|
|
* not supported or known on this kernel.
|
|
|
|
*/
|
|
|
|
if (reg_supported_dfs_region(dfs_region))
|
|
|
|
rd->dfs_region = dfs_region;
|
|
|
|
|
2008-09-10 06:19:48 +00:00
|
|
|
nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
|
2012-12-03 16:21:11 +00:00
|
|
|
rem_reg_rules) {
|
2014-01-24 09:17:47 +00:00
|
|
|
r = nla_parse(tb, NL80211_REG_RULE_ATTR_MAX,
|
|
|
|
nla_data(nl_reg_rule), nla_len(nl_reg_rule),
|
|
|
|
reg_rule_policy);
|
|
|
|
if (r)
|
|
|
|
goto bad_reg;
|
2008-09-10 06:19:48 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
r = set_regdom(rd);
|
2012-12-03 23:48:59 +00:00
|
|
|
/* set_regdom took ownership */
|
2012-12-03 16:21:11 +00:00
|
|
|
rd = NULL;
|
2008-09-10 06:19:48 +00:00
|
|
|
|
2008-10-24 18:32:20 +00:00
|
|
|
bad_reg:
|
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)
|
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
2012-06-18 17:17:03 +00:00
|
|
|
struct wireless_dev *wdev = info->user_ptr[1];
|
2009-02-10 20:25:55 +00:00
|
|
|
struct cfg80211_scan_request *request;
|
|
|
|
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-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-07-07 01:56:12 +00:00
|
|
|
wiphy = &rdev->wiphy;
|
2009-02-10 20:25:55 +00:00
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (!rdev->ops->scan)
|
|
|
|
return -EOPNOTSUPP;
|
2009-02-10 20:25:55 +00:00
|
|
|
|
cfg80211: send scan results from work queue
Due to the previous commit, when a scan finishes, it is in theory
possible to hit the following sequence:
1. interface starts being removed
2. scan is cancelled by driver and cfg80211 is notified
3. scan done work is scheduled
4. interface is removed completely, rdev->scan_req is freed,
event sent to userspace but scan done work remains pending
5. new scan is requested on another virtual interface
6. scan done work runs, freeing the still-running scan
To fix this situation, hang on to the scan done message and block
new scans while that is the case, and only send the message from
the work function, regardless of whether the scan_req is already
freed from interface removal. This makes step 5 above impossible
and changes step 6 to be
5. scan done work runs, sending the scan done message
As this can't work for wext, so we send the message immediately,
but this shouldn't be an issue since we still return -EBUSY.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2014-01-22 09:14:19 +00:00
|
|
|
if (rdev->scan_req || rdev->scan_msg) {
|
2013-03-19 14:04:07 +00:00
|
|
|
err = -EBUSY;
|
|
|
|
goto unlock;
|
|
|
|
}
|
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]);
|
2013-03-19 14:04:07 +00:00
|
|
|
if (!n_channels) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto unlock;
|
|
|
|
}
|
2009-02-10 20:25:55 +00:00
|
|
|
} else {
|
2014-01-09 09:37:23 +00:00
|
|
|
n_channels = ieee80211_get_num_supported_channels(wiphy);
|
2009-02-10 20:25:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
|
|
|
|
nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp)
|
|
|
|
n_ssids++;
|
|
|
|
|
2013-03-19 14:04:07 +00:00
|
|
|
if (n_ssids > wiphy->max_scan_ssids) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto unlock;
|
|
|
|
}
|
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;
|
|
|
|
|
2013-03-19 14:04:07 +00:00
|
|
|
if (ie_len > wiphy->max_scan_ie_len) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto unlock;
|
|
|
|
}
|
2009-03-31 10:12:05 +00:00
|
|
|
|
2009-02-10 20:25:55 +00:00
|
|
|
request = kzalloc(sizeof(*request)
|
2011-05-18 08:42:03 +00:00
|
|
|
+ sizeof(*request->ssids) * n_ssids
|
|
|
|
+ sizeof(*request->channels) * n_channels
|
2009-02-16 17:39:13 +00:00
|
|
|
+ ie_len, GFP_KERNEL);
|
2013-03-19 14:04:07 +00:00
|
|
|
if (!request) {
|
|
|
|
err = -ENOMEM;
|
|
|
|
goto unlock;
|
|
|
|
}
|
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 {
|
2011-07-18 16:08:35 +00:00
|
|
|
enum ieee80211_band band;
|
|
|
|
|
2009-02-10 20:25:55 +00:00
|
|
|
/* 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) {
|
2011-06-07 17:42:26 +00:00
|
|
|
if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
|
2009-02-10 20:25:55 +00:00
|
|
|
err = -EINVAL;
|
|
|
|
goto out_free;
|
|
|
|
}
|
2011-06-07 17:42:26 +00:00
|
|
|
request->ssids[i].ssid_len = nla_len(attr);
|
2009-02-10 20:25:55 +00:00
|
|
|
memcpy(request->ssids[i].ssid, nla_data(attr), 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);
|
|
|
|
}
|
|
|
|
|
2011-07-18 16:08:35 +00:00
|
|
|
for (i = 0; i < IEEE80211_NUM_BANDS; i++)
|
2011-07-19 22:52:16 +00:00
|
|
|
if (wiphy->bands[i])
|
|
|
|
request->rates[i] =
|
|
|
|
(1 << wiphy->bands[i]->n_bitrates) - 1;
|
2011-07-18 16:08:35 +00:00
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_SCAN_SUPP_RATES]) {
|
|
|
|
nla_for_each_nested(attr,
|
|
|
|
info->attrs[NL80211_ATTR_SCAN_SUPP_RATES],
|
|
|
|
tmp) {
|
|
|
|
enum ieee80211_band band = nla_type(attr);
|
|
|
|
|
2011-07-29 08:52:18 +00:00
|
|
|
if (band < 0 || band >= IEEE80211_NUM_BANDS) {
|
2011-07-18 16:08:35 +00:00
|
|
|
err = -EINVAL;
|
|
|
|
goto out_free;
|
|
|
|
}
|
2013-11-20 18:40:41 +00:00
|
|
|
|
|
|
|
if (!wiphy->bands[band])
|
|
|
|
continue;
|
|
|
|
|
2011-07-18 16:08:35 +00:00
|
|
|
err = ieee80211_get_ratemask(wiphy->bands[band],
|
|
|
|
nla_data(attr),
|
|
|
|
nla_len(attr),
|
|
|
|
&request->rates[band]);
|
|
|
|
if (err)
|
|
|
|
goto out_free;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-12 04:03:32 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) {
|
2012-10-12 04:03:31 +00:00
|
|
|
request->flags = nla_get_u32(
|
|
|
|
info->attrs[NL80211_ATTR_SCAN_FLAGS]);
|
2013-10-26 15:14:38 +00:00
|
|
|
if ((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
|
|
|
|
!(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) {
|
2012-10-12 04:03:32 +00:00
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out_free;
|
|
|
|
}
|
|
|
|
}
|
2012-10-12 04:03:31 +00:00
|
|
|
|
2011-09-25 09:23:30 +00:00
|
|
|
request->no_cck =
|
|
|
|
nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
|
|
|
|
|
2012-06-18 17:17:03 +00:00
|
|
|
request->wdev = wdev;
|
2009-07-07 01:56:12 +00:00
|
|
|
request->wiphy = &rdev->wiphy;
|
2012-10-12 04:03:34 +00:00
|
|
|
request->scan_start = jiffies;
|
2009-02-10 20:25:55 +00:00
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
rdev->scan_req = request;
|
2012-06-27 14:19:42 +00:00
|
|
|
err = rdev_scan(rdev, request);
|
2009-02-10 20:25:55 +00:00
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
if (!err) {
|
2012-06-18 17:17:03 +00:00
|
|
|
nl80211_send_scan_start(rdev, wdev);
|
|
|
|
if (wdev->netdev)
|
|
|
|
dev_hold(wdev->netdev);
|
2010-10-04 19:36:35 +00:00
|
|
|
} else {
|
2009-02-10 20:25:55 +00:00
|
|
|
out_free:
|
2009-07-07 01:56:12 +00:00
|
|
|
rdev->scan_req = NULL;
|
2009-02-10 20:25:55 +00:00
|
|
|
kfree(request);
|
|
|
|
}
|
2009-03-12 08:55:09 +00:00
|
|
|
|
2013-03-19 14:04:07 +00:00
|
|
|
unlock:
|
2009-02-10 20:25:55 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2011-05-11 14:09:35 +00:00
|
|
|
static int nl80211_start_sched_scan(struct sk_buff *skb,
|
|
|
|
struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_sched_scan_request *request;
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
|
|
|
struct nlattr *attr;
|
|
|
|
struct wiphy *wiphy;
|
2011-08-31 13:01:48 +00:00
|
|
|
int err, tmp, n_ssids = 0, n_match_sets = 0, n_channels, i;
|
2011-05-11 14:09:37 +00:00
|
|
|
u32 interval;
|
2011-05-11 14:09:35 +00:00
|
|
|
enum ieee80211_band band;
|
|
|
|
size_t ie_len;
|
2011-08-31 13:01:48 +00:00
|
|
|
struct nlattr *tb[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1];
|
nl80211: fix scheduled scan RSSI matchset attribute confusion
The scheduled scan matchsets were intended to be a list of filters,
with the found BSS having to pass at least one of them to be passed
to the host. When the RSSI attribute was added, however, this was
broken and currently wpa_supplicant adds that attribute in its own
matchset; however, it doesn't intend that to mean that anything
that passes the RSSI filter should be passed to the host, instead
it wants it to mean that everything needs to also have higher RSSI.
This is semantically problematic because we have a list of filters
like [ SSID1, SSID2, SSID3, RSSI ] with no real indication which
one should be OR'ed and which one AND'ed.
To fix this, move the RSSI filter attribute into each matchset. As
we need to stay backward compatible, treat a matchset with only the
RSSI attribute as a "default RSSI filter" for all other matchsets,
but only if there are other matchsets (an RSSI-only matchset by
itself is still desirable.)
To make driver implementation easier, keep a global min_rssi_thold
for the entire request as well. The only affected driver is ath6kl.
I found this when I looked into the code after Raja Mani submitted
a patch fixing the n_match_sets calculation to disregard the RSSI,
but that patch didn't address the semantic issue.
Reported-by: Raja Mani <rmani@qti.qualcomm.com>
Acked-by: Luciano Coelho <luciano.coelho@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2014-01-24 09:53:53 +00:00
|
|
|
s32 default_match_rssi = NL80211_SCAN_RSSI_THOLD_OFF;
|
2011-05-11 14:09:35 +00:00
|
|
|
|
|
|
|
if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
|
|
|
|
!rdev->ops->sched_scan_start)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2011-05-11 14:09:37 +00:00
|
|
|
if (!info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
interval = nla_get_u32(info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]);
|
|
|
|
if (interval == 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2011-05-11 14:09:35 +00:00
|
|
|
wiphy = &rdev->wiphy;
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
|
|
|
|
n_channels = validate_scan_freqs(
|
|
|
|
info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
|
|
|
|
if (!n_channels)
|
|
|
|
return -EINVAL;
|
|
|
|
} else {
|
2014-01-09 09:37:23 +00:00
|
|
|
n_channels = ieee80211_get_num_supported_channels(wiphy);
|
2011-05-11 14:09:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
|
|
|
|
nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
|
|
|
|
tmp)
|
|
|
|
n_ssids++;
|
|
|
|
|
2011-07-13 11:57:28 +00:00
|
|
|
if (n_ssids > wiphy->max_sched_scan_ssids)
|
2011-05-11 14:09:35 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
nl80211: fix scheduled scan RSSI matchset attribute confusion
The scheduled scan matchsets were intended to be a list of filters,
with the found BSS having to pass at least one of them to be passed
to the host. When the RSSI attribute was added, however, this was
broken and currently wpa_supplicant adds that attribute in its own
matchset; however, it doesn't intend that to mean that anything
that passes the RSSI filter should be passed to the host, instead
it wants it to mean that everything needs to also have higher RSSI.
This is semantically problematic because we have a list of filters
like [ SSID1, SSID2, SSID3, RSSI ] with no real indication which
one should be OR'ed and which one AND'ed.
To fix this, move the RSSI filter attribute into each matchset. As
we need to stay backward compatible, treat a matchset with only the
RSSI attribute as a "default RSSI filter" for all other matchsets,
but only if there are other matchsets (an RSSI-only matchset by
itself is still desirable.)
To make driver implementation easier, keep a global min_rssi_thold
for the entire request as well. The only affected driver is ath6kl.
I found this when I looked into the code after Raja Mani submitted
a patch fixing the n_match_sets calculation to disregard the RSSI,
but that patch didn't address the semantic issue.
Reported-by: Raja Mani <rmani@qti.qualcomm.com>
Acked-by: Luciano Coelho <luciano.coelho@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2014-01-24 09:53:53 +00:00
|
|
|
/*
|
|
|
|
* First, count the number of 'real' matchsets. Due to an issue with
|
|
|
|
* the old implementation, matchsets containing only the RSSI attribute
|
|
|
|
* (NL80211_SCHED_SCAN_MATCH_ATTR_RSSI) are considered as the 'default'
|
|
|
|
* RSSI for all matchsets, rather than their own matchset for reporting
|
|
|
|
* all APs with a strong RSSI. This is needed to be compatible with
|
|
|
|
* older userspace that treated a matchset with only the RSSI as the
|
|
|
|
* global RSSI for all other matchsets - if there are other matchsets.
|
|
|
|
*/
|
|
|
|
if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
|
2011-08-31 13:01:48 +00:00
|
|
|
nla_for_each_nested(attr,
|
|
|
|
info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
|
nl80211: fix scheduled scan RSSI matchset attribute confusion
The scheduled scan matchsets were intended to be a list of filters,
with the found BSS having to pass at least one of them to be passed
to the host. When the RSSI attribute was added, however, this was
broken and currently wpa_supplicant adds that attribute in its own
matchset; however, it doesn't intend that to mean that anything
that passes the RSSI filter should be passed to the host, instead
it wants it to mean that everything needs to also have higher RSSI.
This is semantically problematic because we have a list of filters
like [ SSID1, SSID2, SSID3, RSSI ] with no real indication which
one should be OR'ed and which one AND'ed.
To fix this, move the RSSI filter attribute into each matchset. As
we need to stay backward compatible, treat a matchset with only the
RSSI attribute as a "default RSSI filter" for all other matchsets,
but only if there are other matchsets (an RSSI-only matchset by
itself is still desirable.)
To make driver implementation easier, keep a global min_rssi_thold
for the entire request as well. The only affected driver is ath6kl.
I found this when I looked into the code after Raja Mani submitted
a patch fixing the n_match_sets calculation to disregard the RSSI,
but that patch didn't address the semantic issue.
Reported-by: Raja Mani <rmani@qti.qualcomm.com>
Acked-by: Luciano Coelho <luciano.coelho@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2014-01-24 09:53:53 +00:00
|
|
|
tmp) {
|
|
|
|
struct nlattr *rssi;
|
|
|
|
|
|
|
|
err = nla_parse(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX,
|
|
|
|
nla_data(attr), nla_len(attr),
|
|
|
|
nl80211_match_policy);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
/* add other standalone attributes here */
|
|
|
|
if (tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID]) {
|
|
|
|
n_match_sets++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
rssi = tb[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI];
|
|
|
|
if (rssi)
|
|
|
|
default_match_rssi = nla_get_s32(rssi);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* However, if there's no other matchset, add the RSSI one */
|
|
|
|
if (!n_match_sets && default_match_rssi != NL80211_SCAN_RSSI_THOLD_OFF)
|
|
|
|
n_match_sets = 1;
|
2011-08-31 13:01:48 +00:00
|
|
|
|
|
|
|
if (n_match_sets > wiphy->max_match_sets)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2011-05-11 14:09:35 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_IE])
|
|
|
|
ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
|
|
|
|
else
|
|
|
|
ie_len = 0;
|
|
|
|
|
2011-07-13 11:57:29 +00:00
|
|
|
if (ie_len > wiphy->max_sched_scan_ie_len)
|
2011-05-11 14:09:35 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2011-06-30 05:32:41 +00:00
|
|
|
if (rdev->sched_scan_req) {
|
|
|
|
err = -EINPROGRESS;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2011-05-11 14:09:35 +00:00
|
|
|
request = kzalloc(sizeof(*request)
|
2011-05-18 08:42:03 +00:00
|
|
|
+ sizeof(*request->ssids) * n_ssids
|
2011-08-31 13:01:48 +00:00
|
|
|
+ sizeof(*request->match_sets) * n_match_sets
|
2011-05-18 08:42:03 +00:00
|
|
|
+ sizeof(*request->channels) * n_channels
|
2011-05-11 14:09:35 +00:00
|
|
|
+ ie_len, GFP_KERNEL);
|
2011-06-30 05:32:41 +00:00
|
|
|
if (!request) {
|
|
|
|
err = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
2011-05-11 14:09:35 +00:00
|
|
|
|
|
|
|
if (n_ssids)
|
|
|
|
request->ssids = (void *)&request->channels[n_channels];
|
|
|
|
request->n_ssids = n_ssids;
|
|
|
|
if (ie_len) {
|
|
|
|
if (request->ssids)
|
|
|
|
request->ie = (void *)(request->ssids + n_ssids);
|
|
|
|
else
|
|
|
|
request->ie = (void *)(request->channels + n_channels);
|
|
|
|
}
|
|
|
|
|
2011-08-31 13:01:48 +00:00
|
|
|
if (n_match_sets) {
|
|
|
|
if (request->ie)
|
|
|
|
request->match_sets = (void *)(request->ie + ie_len);
|
|
|
|
else if (request->ssids)
|
|
|
|
request->match_sets =
|
|
|
|
(void *)(request->ssids + n_ssids);
|
|
|
|
else
|
|
|
|
request->match_sets =
|
|
|
|
(void *)(request->channels + n_channels);
|
|
|
|
}
|
|
|
|
request->n_match_sets = n_match_sets;
|
|
|
|
|
2011-05-11 14:09:35 +00:00
|
|
|
i = 0;
|
|
|
|
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) {
|
|
|
|
struct ieee80211_channel *chan;
|
|
|
|
|
|
|
|
chan = ieee80211_get_channel(wiphy, nla_get_u32(attr));
|
|
|
|
|
|
|
|
if (!chan) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out_free;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ignore disabled channels */
|
|
|
|
if (chan->flags & IEEE80211_CHAN_DISABLED)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
request->channels[i] = chan;
|
|
|
|
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++) {
|
|
|
|
struct ieee80211_channel *chan;
|
|
|
|
|
|
|
|
chan = &wiphy->bands[band]->channels[j];
|
|
|
|
|
|
|
|
if (chan->flags & IEEE80211_CHAN_DISABLED)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
request->channels[i] = chan;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!i) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out_free;
|
|
|
|
}
|
|
|
|
|
|
|
|
request->n_channels = i;
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
|
|
|
|
nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
|
|
|
|
tmp) {
|
2011-06-07 17:42:26 +00:00
|
|
|
if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
|
2011-05-11 14:09:35 +00:00
|
|
|
err = -EINVAL;
|
|
|
|
goto out_free;
|
|
|
|
}
|
2011-06-07 17:42:26 +00:00
|
|
|
request->ssids[i].ssid_len = nla_len(attr);
|
2011-05-11 14:09:35 +00:00
|
|
|
memcpy(request->ssids[i].ssid, nla_data(attr),
|
|
|
|
nla_len(attr));
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-31 13:01:48 +00:00
|
|
|
i = 0;
|
|
|
|
if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
|
|
|
|
nla_for_each_nested(attr,
|
|
|
|
info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
|
|
|
|
tmp) {
|
2012-06-21 18:09:54 +00:00
|
|
|
struct nlattr *ssid, *rssi;
|
2011-08-31 13:01:48 +00:00
|
|
|
|
2014-01-24 09:17:47 +00:00
|
|
|
err = nla_parse(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX,
|
|
|
|
nla_data(attr), nla_len(attr),
|
|
|
|
nl80211_match_policy);
|
|
|
|
if (err)
|
|
|
|
goto out_free;
|
2012-06-13 09:17:11 +00:00
|
|
|
ssid = tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID];
|
2011-08-31 13:01:48 +00:00
|
|
|
if (ssid) {
|
nl80211: fix scheduled scan RSSI matchset attribute confusion
The scheduled scan matchsets were intended to be a list of filters,
with the found BSS having to pass at least one of them to be passed
to the host. When the RSSI attribute was added, however, this was
broken and currently wpa_supplicant adds that attribute in its own
matchset; however, it doesn't intend that to mean that anything
that passes the RSSI filter should be passed to the host, instead
it wants it to mean that everything needs to also have higher RSSI.
This is semantically problematic because we have a list of filters
like [ SSID1, SSID2, SSID3, RSSI ] with no real indication which
one should be OR'ed and which one AND'ed.
To fix this, move the RSSI filter attribute into each matchset. As
we need to stay backward compatible, treat a matchset with only the
RSSI attribute as a "default RSSI filter" for all other matchsets,
but only if there are other matchsets (an RSSI-only matchset by
itself is still desirable.)
To make driver implementation easier, keep a global min_rssi_thold
for the entire request as well. The only affected driver is ath6kl.
I found this when I looked into the code after Raja Mani submitted
a patch fixing the n_match_sets calculation to disregard the RSSI,
but that patch didn't address the semantic issue.
Reported-by: Raja Mani <rmani@qti.qualcomm.com>
Acked-by: Luciano Coelho <luciano.coelho@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2014-01-24 09:53:53 +00:00
|
|
|
if (WARN_ON(i >= n_match_sets)) {
|
|
|
|
/* this indicates a programming error,
|
|
|
|
* the loop above should have verified
|
|
|
|
* things properly
|
|
|
|
*/
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out_free;
|
|
|
|
}
|
|
|
|
|
2011-08-31 13:01:48 +00:00
|
|
|
if (nla_len(ssid) > IEEE80211_MAX_SSID_LEN) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out_free;
|
|
|
|
}
|
|
|
|
memcpy(request->match_sets[i].ssid.ssid,
|
|
|
|
nla_data(ssid), nla_len(ssid));
|
|
|
|
request->match_sets[i].ssid.ssid_len =
|
|
|
|
nla_len(ssid);
|
nl80211: fix scheduled scan RSSI matchset attribute confusion
The scheduled scan matchsets were intended to be a list of filters,
with the found BSS having to pass at least one of them to be passed
to the host. When the RSSI attribute was added, however, this was
broken and currently wpa_supplicant adds that attribute in its own
matchset; however, it doesn't intend that to mean that anything
that passes the RSSI filter should be passed to the host, instead
it wants it to mean that everything needs to also have higher RSSI.
This is semantically problematic because we have a list of filters
like [ SSID1, SSID2, SSID3, RSSI ] with no real indication which
one should be OR'ed and which one AND'ed.
To fix this, move the RSSI filter attribute into each matchset. As
we need to stay backward compatible, treat a matchset with only the
RSSI attribute as a "default RSSI filter" for all other matchsets,
but only if there are other matchsets (an RSSI-only matchset by
itself is still desirable.)
To make driver implementation easier, keep a global min_rssi_thold
for the entire request as well. The only affected driver is ath6kl.
I found this when I looked into the code after Raja Mani submitted
a patch fixing the n_match_sets calculation to disregard the RSSI,
but that patch didn't address the semantic issue.
Reported-by: Raja Mani <rmani@qti.qualcomm.com>
Acked-by: Luciano Coelho <luciano.coelho@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2014-01-24 09:53:53 +00:00
|
|
|
/* special attribute - old implemenation w/a */
|
|
|
|
request->match_sets[i].rssi_thold =
|
|
|
|
default_match_rssi;
|
|
|
|
rssi = tb[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI];
|
|
|
|
if (rssi)
|
|
|
|
request->match_sets[i].rssi_thold =
|
|
|
|
nla_get_s32(rssi);
|
2011-08-31 13:01:48 +00:00
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
nl80211: fix scheduled scan RSSI matchset attribute confusion
The scheduled scan matchsets were intended to be a list of filters,
with the found BSS having to pass at least one of them to be passed
to the host. When the RSSI attribute was added, however, this was
broken and currently wpa_supplicant adds that attribute in its own
matchset; however, it doesn't intend that to mean that anything
that passes the RSSI filter should be passed to the host, instead
it wants it to mean that everything needs to also have higher RSSI.
This is semantically problematic because we have a list of filters
like [ SSID1, SSID2, SSID3, RSSI ] with no real indication which
one should be OR'ed and which one AND'ed.
To fix this, move the RSSI filter attribute into each matchset. As
we need to stay backward compatible, treat a matchset with only the
RSSI attribute as a "default RSSI filter" for all other matchsets,
but only if there are other matchsets (an RSSI-only matchset by
itself is still desirable.)
To make driver implementation easier, keep a global min_rssi_thold
for the entire request as well. The only affected driver is ath6kl.
I found this when I looked into the code after Raja Mani submitted
a patch fixing the n_match_sets calculation to disregard the RSSI,
but that patch didn't address the semantic issue.
Reported-by: Raja Mani <rmani@qti.qualcomm.com>
Acked-by: Luciano Coelho <luciano.coelho@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2014-01-24 09:53:53 +00:00
|
|
|
|
|
|
|
/* there was no other matchset, so the RSSI one is alone */
|
|
|
|
if (i == 0)
|
|
|
|
request->match_sets[0].rssi_thold = default_match_rssi;
|
|
|
|
|
|
|
|
request->min_rssi_thold = INT_MAX;
|
|
|
|
for (i = 0; i < n_match_sets; i++)
|
|
|
|
request->min_rssi_thold =
|
|
|
|
min(request->match_sets[i].rssi_thold,
|
|
|
|
request->min_rssi_thold);
|
|
|
|
} else {
|
|
|
|
request->min_rssi_thold = NL80211_SCAN_RSSI_THOLD_OFF;
|
2011-08-31 13:01:48 +00:00
|
|
|
}
|
|
|
|
|
2014-02-04 20:01:25 +00:00
|
|
|
if (ie_len) {
|
|
|
|
request->ie_len = ie_len;
|
2011-05-11 14:09:35 +00:00
|
|
|
memcpy((void *)request->ie,
|
|
|
|
nla_data(info->attrs[NL80211_ATTR_IE]),
|
|
|
|
request->ie_len);
|
|
|
|
}
|
|
|
|
|
2012-10-12 04:03:32 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) {
|
2012-10-12 04:03:31 +00:00
|
|
|
request->flags = nla_get_u32(
|
|
|
|
info->attrs[NL80211_ATTR_SCAN_FLAGS]);
|
2013-10-26 15:14:38 +00:00
|
|
|
if ((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
|
|
|
|
!(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) {
|
2012-10-12 04:03:32 +00:00
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out_free;
|
|
|
|
}
|
|
|
|
}
|
2012-10-12 04:03:31 +00:00
|
|
|
|
2011-05-11 14:09:35 +00:00
|
|
|
request->dev = dev;
|
|
|
|
request->wiphy = &rdev->wiphy;
|
2011-05-11 14:09:37 +00:00
|
|
|
request->interval = interval;
|
2012-10-12 04:03:34 +00:00
|
|
|
request->scan_start = jiffies;
|
2011-05-11 14:09:35 +00:00
|
|
|
|
2012-06-27 14:19:42 +00:00
|
|
|
err = rdev_sched_scan_start(rdev, dev, request);
|
2011-05-11 14:09:35 +00:00
|
|
|
if (!err) {
|
|
|
|
rdev->sched_scan_req = request;
|
|
|
|
nl80211_send_sched_scan(rdev, dev,
|
|
|
|
NL80211_CMD_START_SCHED_SCAN);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
out_free:
|
|
|
|
kfree(request);
|
|
|
|
out:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_stop_sched_scan(struct sk_buff *skb,
|
|
|
|
struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
|
|
|
|
if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
|
|
|
|
!rdev->ops->sched_scan_stop)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2013-05-08 19:45:15 +00:00
|
|
|
return __cfg80211_stop_sched_scan(rdev, false);
|
2011-05-11 14:09:35 +00:00
|
|
|
}
|
|
|
|
|
2013-02-08 17:16:19 +00:00
|
|
|
static int nl80211_start_radar_detection(struct sk_buff *skb,
|
|
|
|
struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
|
|
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
|
struct cfg80211_chan_def chandef;
|
2013-11-25 19:56:10 +00:00
|
|
|
enum nl80211_dfs_regions dfs_region;
|
2014-02-21 18:46:13 +00:00
|
|
|
unsigned int cac_time_ms;
|
2013-02-08 17:16:19 +00:00
|
|
|
int err;
|
|
|
|
|
2013-11-25 19:56:10 +00:00
|
|
|
dfs_region = reg_get_dfs_region(wdev->wiphy);
|
|
|
|
if (dfs_region == NL80211_DFS_UNSET)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2013-02-08 17:16:19 +00:00
|
|
|
err = nl80211_parse_chandef(rdev, info, &chandef);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2013-09-03 17:43:18 +00:00
|
|
|
if (netif_carrier_ok(dev))
|
|
|
|
return -EBUSY;
|
|
|
|
|
2013-02-08 17:16:19 +00:00
|
|
|
if (wdev->cac_started)
|
|
|
|
return -EBUSY;
|
|
|
|
|
2014-02-18 09:40:36 +00:00
|
|
|
err = cfg80211_chandef_dfs_required(wdev->wiphy, &chandef,
|
2014-05-15 10:05:39 +00:00
|
|
|
wdev->iftype);
|
2013-02-08 17:16:19 +00:00
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (err == 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2013-11-05 13:48:48 +00:00
|
|
|
if (!cfg80211_chandef_dfs_usable(wdev->wiphy, &chandef))
|
2013-02-08 17:16:19 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!rdev->ops->start_radar_detection)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2014-02-21 18:46:13 +00:00
|
|
|
cac_time_ms = cfg80211_chandef_dfs_cac_time(&rdev->wiphy, &chandef);
|
|
|
|
if (WARN_ON(!cac_time_ms))
|
|
|
|
cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
|
|
|
|
|
|
|
|
err = rdev->ops->start_radar_detection(&rdev->wiphy, dev, &chandef,
|
|
|
|
cac_time_ms);
|
2013-02-08 17:16:19 +00:00
|
|
|
if (!err) {
|
2014-01-29 13:22:27 +00:00
|
|
|
wdev->chandef = chandef;
|
2013-02-08 17:16:19 +00:00
|
|
|
wdev->cac_started = true;
|
|
|
|
wdev->cac_start_time = jiffies;
|
2014-02-21 18:46:13 +00:00
|
|
|
wdev->cac_time_ms = cac_time_ms;
|
2013-02-08 17:16:19 +00:00
|
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2013-07-11 14:09:05 +00:00
|
|
|
static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
|
|
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
|
struct cfg80211_csa_settings params;
|
|
|
|
/* csa_attrs is defined static to avoid waste of stack size - this
|
|
|
|
* function is called under RTNL lock, so this should not be a problem.
|
|
|
|
*/
|
|
|
|
static struct nlattr *csa_attrs[NL80211_ATTR_MAX+1];
|
|
|
|
int err;
|
2013-08-28 11:41:33 +00:00
|
|
|
bool need_new_beacon = false;
|
2014-05-09 11:11:46 +00:00
|
|
|
int len, i;
|
2014-10-08 06:48:34 +00:00
|
|
|
u32 cs_count;
|
2013-07-11 14:09:05 +00:00
|
|
|
|
|
|
|
if (!rdev->ops->channel_switch ||
|
|
|
|
!(rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2013-08-28 11:41:33 +00:00
|
|
|
switch (dev->ieee80211_ptr->iftype) {
|
|
|
|
case NL80211_IFTYPE_AP:
|
|
|
|
case NL80211_IFTYPE_P2P_GO:
|
|
|
|
need_new_beacon = true;
|
|
|
|
|
|
|
|
/* useless if AP is not running */
|
|
|
|
if (!wdev->beacon_interval)
|
2014-01-22 09:05:27 +00:00
|
|
|
return -ENOTCONN;
|
2013-08-28 11:41:33 +00:00
|
|
|
break;
|
|
|
|
case NL80211_IFTYPE_ADHOC:
|
2014-01-22 09:05:27 +00:00
|
|
|
if (!wdev->ssid_len)
|
|
|
|
return -ENOTCONN;
|
|
|
|
break;
|
2013-10-15 02:08:28 +00:00
|
|
|
case NL80211_IFTYPE_MESH_POINT:
|
2014-01-22 09:05:27 +00:00
|
|
|
if (!wdev->mesh_id_len)
|
|
|
|
return -ENOTCONN;
|
2013-08-28 11:41:33 +00:00
|
|
|
break;
|
|
|
|
default:
|
2013-07-11 14:09:05 +00:00
|
|
|
return -EOPNOTSUPP;
|
2013-08-28 11:41:33 +00:00
|
|
|
}
|
2013-07-11 14:09:05 +00:00
|
|
|
|
|
|
|
memset(¶ms, 0, sizeof(params));
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
|
|
|
|
!info->attrs[NL80211_ATTR_CH_SWITCH_COUNT])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* only important for AP, IBSS and mesh create IEs internally */
|
2013-10-17 08:52:17 +00:00
|
|
|
if (need_new_beacon && !info->attrs[NL80211_ATTR_CSA_IES])
|
2013-07-11 14:09:05 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2014-10-08 06:48:34 +00:00
|
|
|
/* Even though the attribute is u32, the specification says
|
|
|
|
* u8, so let's make sure we don't overflow.
|
|
|
|
*/
|
|
|
|
cs_count = nla_get_u32(info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]);
|
|
|
|
if (cs_count > 255)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
params.count = cs_count;
|
2013-07-11 14:09:05 +00:00
|
|
|
|
2013-08-28 11:41:33 +00:00
|
|
|
if (!need_new_beacon)
|
|
|
|
goto skip_beacons;
|
|
|
|
|
2013-07-11 14:09:05 +00:00
|
|
|
err = nl80211_parse_beacon(info->attrs, ¶ms.beacon_after);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
err = nla_parse_nested(csa_attrs, NL80211_ATTR_MAX,
|
|
|
|
info->attrs[NL80211_ATTR_CSA_IES],
|
|
|
|
nl80211_policy);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
err = nl80211_parse_beacon(csa_attrs, ¶ms.beacon_csa);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (!csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON])
|
|
|
|
return -EINVAL;
|
|
|
|
|
2014-05-09 11:11:46 +00:00
|
|
|
len = nla_len(csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]);
|
|
|
|
if (!len || (len % sizeof(u16)))
|
2013-07-11 14:09:05 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2014-05-09 11:11:46 +00:00
|
|
|
params.n_counter_offsets_beacon = len / sizeof(u16);
|
|
|
|
if (rdev->wiphy.max_num_csa_counters &&
|
|
|
|
(params.n_counter_offsets_beacon >
|
|
|
|
rdev->wiphy.max_num_csa_counters))
|
2013-07-11 14:09:05 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2014-05-09 11:11:46 +00:00
|
|
|
params.counter_offsets_beacon =
|
|
|
|
nla_data(csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]);
|
|
|
|
|
|
|
|
/* sanity checks - counters should fit and be the same */
|
|
|
|
for (i = 0; i < params.n_counter_offsets_beacon; i++) {
|
|
|
|
u16 offset = params.counter_offsets_beacon[i];
|
|
|
|
|
|
|
|
if (offset >= params.beacon_csa.tail_len)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (params.beacon_csa.tail[offset] != params.count)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2013-07-11 14:09:05 +00:00
|
|
|
if (csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]) {
|
2014-05-09 11:11:46 +00:00
|
|
|
len = nla_len(csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]);
|
|
|
|
if (!len || (len % sizeof(u16)))
|
2013-07-11 14:09:05 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2014-05-09 11:11:46 +00:00
|
|
|
params.n_counter_offsets_presp = len / sizeof(u16);
|
|
|
|
if (rdev->wiphy.max_num_csa_counters &&
|
|
|
|
(params.n_counter_offsets_beacon >
|
|
|
|
rdev->wiphy.max_num_csa_counters))
|
2013-07-11 14:09:05 +00:00
|
|
|
return -EINVAL;
|
2014-05-09 11:11:46 +00:00
|
|
|
|
|
|
|
params.counter_offsets_presp =
|
|
|
|
nla_data(csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]);
|
|
|
|
|
|
|
|
/* sanity checks - counters should fit and be the same */
|
|
|
|
for (i = 0; i < params.n_counter_offsets_presp; i++) {
|
|
|
|
u16 offset = params.counter_offsets_presp[i];
|
|
|
|
|
|
|
|
if (offset >= params.beacon_csa.probe_resp_len)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (params.beacon_csa.probe_resp[offset] !=
|
|
|
|
params.count)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2013-07-11 14:09:05 +00:00
|
|
|
}
|
|
|
|
|
2013-08-28 11:41:33 +00:00
|
|
|
skip_beacons:
|
2013-07-11 14:09:05 +00:00
|
|
|
err = nl80211_parse_chandef(rdev, info, ¶ms.chandef);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2014-02-23 07:13:01 +00:00
|
|
|
if (!cfg80211_reg_can_beacon(&rdev->wiphy, ¶ms.chandef,
|
|
|
|
wdev->iftype))
|
2013-07-11 14:09:05 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2014-02-18 09:40:36 +00:00
|
|
|
err = cfg80211_chandef_dfs_required(wdev->wiphy,
|
|
|
|
¶ms.chandef,
|
|
|
|
wdev->iftype);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
2014-10-25 15:57:35 +00:00
|
|
|
if (err > 0)
|
2014-02-18 09:40:36 +00:00
|
|
|
params.radar_required = true;
|
2013-07-11 14:09:05 +00:00
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_CH_SWITCH_BLOCK_TX])
|
|
|
|
params.block_tx = true;
|
|
|
|
|
2013-11-21 17:19:49 +00:00
|
|
|
wdev_lock(wdev);
|
|
|
|
err = rdev_channel_switch(rdev, dev, ¶ms);
|
|
|
|
wdev_unlock(wdev);
|
|
|
|
|
|
|
|
return err;
|
2013-07-11 14:09:05 +00:00
|
|
|
}
|
|
|
|
|
2011-06-21 07:45:33 +00:00
|
|
|
static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
|
|
|
|
u32 seq, int flags,
|
2009-02-10 20:25:55 +00:00
|
|
|
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;
|
2012-11-29 00:25:20 +00:00
|
|
|
const struct cfg80211_bss_ies *ies;
|
2009-02-10 20:25:55 +00:00
|
|
|
void *hdr;
|
|
|
|
struct nlattr *bss;
|
2009-07-10 16:42:31 +00:00
|
|
|
|
|
|
|
ASSERT_WDEV_LOCK(wdev);
|
2009-02-10 20:25:55 +00:00
|
|
|
|
2012-09-07 20:12:54 +00:00
|
|
|
hdr = nl80211hdr_put(msg, NETLINK_CB(cb->skb).portid, seq, flags,
|
2009-02-10 20:25:55 +00:00
|
|
|
NL80211_CMD_NEW_SCAN_RESULTS);
|
|
|
|
if (!hdr)
|
|
|
|
return -1;
|
|
|
|
|
2011-06-21 07:45:33 +00:00
|
|
|
genl_dump_check_consistent(cb, hdr, &nl80211_fam);
|
|
|
|
|
2013-04-18 23:02:55 +00:00
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_GENERATION, rdev->bss_generation))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if (wdev->netdev &&
|
2012-03-29 08:41:26 +00:00
|
|
|
nla_put_u32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex))
|
|
|
|
goto nla_put_failure;
|
2013-04-18 23:02:55 +00:00
|
|
|
if (nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)))
|
|
|
|
goto nla_put_failure;
|
2009-02-10 20:25:55 +00:00
|
|
|
|
|
|
|
bss = nla_nest_start(msg, NL80211_ATTR_BSS);
|
|
|
|
if (!bss)
|
|
|
|
goto nla_put_failure;
|
2012-03-29 08:41:26 +00:00
|
|
|
if ((!is_zero_ether_addr(res->bssid) &&
|
2012-11-29 00:25:20 +00:00
|
|
|
nla_put(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid)))
|
2012-03-29 08:41:26 +00:00
|
|
|
goto nla_put_failure;
|
2012-11-29 00:25:20 +00:00
|
|
|
|
|
|
|
rcu_read_lock();
|
2014-08-12 18:34:30 +00:00
|
|
|
/* indicate whether we have probe response data or not */
|
|
|
|
if (rcu_access_pointer(res->proberesp_ies) &&
|
|
|
|
nla_put_flag(msg, NL80211_BSS_PRESP_DATA))
|
|
|
|
goto fail_unlock_rcu;
|
|
|
|
|
|
|
|
/* this pointer prefers to be pointed to probe response data
|
|
|
|
* but is always valid
|
|
|
|
*/
|
2012-11-29 00:25:20 +00:00
|
|
|
ies = rcu_dereference(res->ies);
|
2013-02-05 15:54:31 +00:00
|
|
|
if (ies) {
|
|
|
|
if (nla_put_u64(msg, NL80211_BSS_TSF, ies->tsf))
|
|
|
|
goto fail_unlock_rcu;
|
|
|
|
if (ies->len && nla_put(msg, NL80211_BSS_INFORMATION_ELEMENTS,
|
|
|
|
ies->len, ies->data))
|
|
|
|
goto fail_unlock_rcu;
|
2012-11-29 00:25:20 +00:00
|
|
|
}
|
2014-08-12 18:34:30 +00:00
|
|
|
|
|
|
|
/* and this pointer is always (unless driver didn't know) beacon data */
|
2012-11-29 00:25:20 +00:00
|
|
|
ies = rcu_dereference(res->beacon_ies);
|
2014-08-12 18:34:30 +00:00
|
|
|
if (ies && ies->from_beacon) {
|
|
|
|
if (nla_put_u64(msg, NL80211_BSS_BEACON_TSF, ies->tsf))
|
2013-02-05 15:54:31 +00:00
|
|
|
goto fail_unlock_rcu;
|
|
|
|
if (ies->len && nla_put(msg, NL80211_BSS_BEACON_IES,
|
|
|
|
ies->len, ies->data))
|
|
|
|
goto fail_unlock_rcu;
|
2012-11-29 00:25:20 +00:00
|
|
|
}
|
|
|
|
rcu_read_unlock();
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (res->beacon_interval &&
|
|
|
|
nla_put_u16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if (nla_put_u16(msg, NL80211_BSS_CAPABILITY, res->capability) ||
|
|
|
|
nla_put_u32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq) ||
|
2013-07-08 14:55:49 +00:00
|
|
|
nla_put_u32(msg, NL80211_BSS_CHAN_WIDTH, res->scan_width) ||
|
2012-03-29 08:41:26 +00:00
|
|
|
nla_put_u32(msg, NL80211_BSS_SEEN_MS_AGO,
|
|
|
|
jiffies_to_msecs(jiffies - intbss->ts)))
|
|
|
|
goto nla_put_failure;
|
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:
|
2012-03-29 08:41:26 +00:00
|
|
|
if (nla_put_u32(msg, NL80211_BSS_SIGNAL_MBM, res->signal))
|
|
|
|
goto nla_put_failure;
|
2009-02-10 20:25:55 +00:00
|
|
|
break;
|
|
|
|
case CFG80211_SIGNAL_TYPE_UNSPEC:
|
2012-03-29 08:41:26 +00:00
|
|
|
if (nla_put_u8(msg, NL80211_BSS_SIGNAL_UNSPEC, res->signal))
|
|
|
|
goto nla_put_failure;
|
2009-02-10 20:25:55 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2009-07-10 16:42:31 +00:00
|
|
|
switch (wdev->iftype) {
|
2010-09-16 12:58:22 +00:00
|
|
|
case NL80211_IFTYPE_P2P_CLIENT:
|
2009-07-10 16:42:31 +00:00
|
|
|
case NL80211_IFTYPE_STATION:
|
2012-03-29 08:41:26 +00:00
|
|
|
if (intbss == wdev->current_bss &&
|
|
|
|
nla_put_u32(msg, NL80211_BSS_STATUS,
|
|
|
|
NL80211_BSS_STATUS_ASSOCIATED))
|
|
|
|
goto nla_put_failure;
|
2009-07-10 16:42:31 +00:00
|
|
|
break;
|
|
|
|
case NL80211_IFTYPE_ADHOC:
|
2012-03-29 08:41:26 +00:00
|
|
|
if (intbss == wdev->current_bss &&
|
|
|
|
nla_put_u32(msg, NL80211_BSS_STATUS,
|
|
|
|
NL80211_BSS_STATUS_IBSS_JOINED))
|
|
|
|
goto nla_put_failure;
|
2009-07-10 16:42:31 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2009-02-10 20:25:55 +00:00
|
|
|
nla_nest_end(msg, bss);
|
|
|
|
|
|
|
|
return genlmsg_end(msg, hdr);
|
|
|
|
|
2013-02-05 15:54:31 +00:00
|
|
|
fail_unlock_rcu:
|
|
|
|
rcu_read_unlock();
|
2009-02-10 20:25:55 +00:00
|
|
|
nla_put_failure:
|
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
return -EMSGSIZE;
|
|
|
|
}
|
|
|
|
|
2013-04-18 23:02:55 +00:00
|
|
|
static int nl80211_dump_scan(struct sk_buff *skb, struct netlink_callback *cb)
|
2009-02-10 20:25:55 +00:00
|
|
|
{
|
2009-07-10 16:42:31 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
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;
|
2013-04-18 23:02:55 +00:00
|
|
|
int start = cb->args[2], idx = 0;
|
2009-02-10 20:25:55 +00:00
|
|
|
int err;
|
|
|
|
|
2013-04-18 23:02:55 +00:00
|
|
|
err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);
|
2010-10-04 19:14:06 +00:00
|
|
|
if (err)
|
|
|
|
return err;
|
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);
|
|
|
|
|
2011-06-21 07:45:33 +00:00
|
|
|
cb->seq = rdev->bss_generation;
|
|
|
|
|
2009-07-10 16:42:31 +00:00
|
|
|
list_for_each_entry(scan, &rdev->bss_list, list) {
|
2009-02-10 20:25:55 +00:00
|
|
|
if (++idx <= start)
|
|
|
|
continue;
|
2011-06-21 07:45:33 +00:00
|
|
|
if (nl80211_send_bss(skb, cb,
|
2009-02-10 20:25:55 +00:00
|
|
|
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--;
|
2010-10-04 19:14:06 +00:00
|
|
|
break;
|
2009-02-10 20:25:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2013-04-18 23:02:55 +00:00
|
|
|
cb->args[2] = idx;
|
|
|
|
nl80211_finish_wdev_dump(rdev);
|
2009-02-10 20:25:55 +00:00
|
|
|
|
2010-10-04 19:14:06 +00:00
|
|
|
return skb->len;
|
2009-02-10 20:25:55 +00:00
|
|
|
}
|
|
|
|
|
2012-09-07 20:12:54 +00:00
|
|
|
static int nl80211_send_survey(struct sk_buff *msg, u32 portid, u32 seq,
|
2009-11-11 11:25:40 +00:00
|
|
|
int flags, struct net_device *dev,
|
|
|
|
struct survey_info *survey)
|
|
|
|
{
|
|
|
|
void *hdr;
|
|
|
|
struct nlattr *infoattr;
|
|
|
|
|
2012-09-07 20:12:54 +00:00
|
|
|
hdr = nl80211hdr_put(msg, portid, seq, flags,
|
2009-11-11 11:25:40 +00:00
|
|
|
NL80211_CMD_NEW_SURVEY_RESULTS);
|
|
|
|
if (!hdr)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex))
|
|
|
|
goto nla_put_failure;
|
2009-11-11 11:25:40 +00:00
|
|
|
|
|
|
|
infoattr = nla_nest_start(msg, NL80211_ATTR_SURVEY_INFO);
|
|
|
|
if (!infoattr)
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (nla_put_u32(msg, NL80211_SURVEY_INFO_FREQUENCY,
|
|
|
|
survey->channel->center_freq))
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
if ((survey->filled & SURVEY_INFO_NOISE_DBM) &&
|
|
|
|
nla_put_u8(msg, NL80211_SURVEY_INFO_NOISE, survey->noise))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if ((survey->filled & SURVEY_INFO_IN_USE) &&
|
|
|
|
nla_put_flag(msg, NL80211_SURVEY_INFO_IN_USE))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if ((survey->filled & SURVEY_INFO_CHANNEL_TIME) &&
|
|
|
|
nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME,
|
|
|
|
survey->channel_time))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_BUSY) &&
|
|
|
|
nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY,
|
|
|
|
survey->channel_time_busy))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY) &&
|
|
|
|
nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY,
|
|
|
|
survey->channel_time_ext_busy))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_RX) &&
|
|
|
|
nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_RX,
|
|
|
|
survey->channel_time_rx))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_TX) &&
|
|
|
|
nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_TX,
|
|
|
|
survey->channel_time_tx))
|
|
|
|
goto nla_put_failure;
|
2009-11-11 11:25:40 +00:00
|
|
|
|
|
|
|
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;
|
2014-04-21 04:53:02 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
2013-04-18 23:02:55 +00:00
|
|
|
struct wireless_dev *wdev;
|
|
|
|
int survey_idx = cb->args[2];
|
2009-11-11 11:25:40 +00:00
|
|
|
int res;
|
|
|
|
|
2014-04-21 04:53:02 +00:00
|
|
|
res = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);
|
2010-10-04 19:14:06 +00:00
|
|
|
if (res)
|
|
|
|
return res;
|
2009-11-11 11:25:40 +00:00
|
|
|
|
2013-04-18 23:02:55 +00:00
|
|
|
if (!wdev->netdev) {
|
|
|
|
res = -EINVAL;
|
|
|
|
goto out_err;
|
|
|
|
}
|
|
|
|
|
2014-04-21 04:53:02 +00:00
|
|
|
if (!rdev->ops->dump_survey) {
|
2009-11-11 11:25:40 +00:00
|
|
|
res = -EOPNOTSUPP;
|
|
|
|
goto out_err;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (1) {
|
2011-05-27 14:24:02 +00:00
|
|
|
struct ieee80211_channel *chan;
|
|
|
|
|
2014-04-21 04:53:02 +00:00
|
|
|
res = rdev_dump_survey(rdev, wdev->netdev, survey_idx, &survey);
|
2009-11-11 11:25:40 +00:00
|
|
|
if (res == -ENOENT)
|
|
|
|
break;
|
|
|
|
if (res)
|
|
|
|
goto out_err;
|
|
|
|
|
2011-05-27 14:24:02 +00:00
|
|
|
/* Survey without a channel doesn't make sense */
|
|
|
|
if (!survey.channel) {
|
|
|
|
res = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2014-04-21 04:53:02 +00:00
|
|
|
chan = ieee80211_get_channel(&rdev->wiphy,
|
2011-05-27 14:24:02 +00:00
|
|
|
survey.channel->center_freq);
|
|
|
|
if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) {
|
|
|
|
survey_idx++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2009-11-11 11:25:40 +00:00
|
|
|
if (nl80211_send_survey(skb,
|
2012-09-07 20:12:54 +00:00
|
|
|
NETLINK_CB(cb->skb).portid,
|
2009-11-11 11:25:40 +00:00
|
|
|
cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
2013-04-18 23:02:55 +00:00
|
|
|
wdev->netdev, &survey) < 0)
|
2009-11-11 11:25:40 +00:00
|
|
|
goto out;
|
|
|
|
survey_idx++;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
2013-04-18 23:02:55 +00:00
|
|
|
cb->args[2] = survey_idx;
|
2009-11-11 11:25:40 +00:00
|
|
|
res = skb->len;
|
|
|
|
out_err:
|
2014-04-21 04:53:02 +00:00
|
|
|
nl80211_finish_wdev_dump(rdev);
|
2009-11-11 11:25:40 +00:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2009-07-01 19:26:54 +00:00
|
|
|
static bool nl80211_valid_wpa_versions(u32 wpa_versions)
|
|
|
|
{
|
|
|
|
return !(wpa_versions & ~(NL80211_WPA_VERSION_1 |
|
|
|
|
NL80211_WPA_VERSION_2));
|
|
|
|
}
|
|
|
|
|
2009-03-19 11:39:22 +00:00
|
|
|
static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
2009-07-02 15:20:43 +00:00
|
|
|
struct ieee80211_channel *chan;
|
2012-09-30 16:29:39 +00:00
|
|
|
const u8 *bssid, *ssid, *ie = NULL, *sae_data = NULL;
|
|
|
|
int err, ssid_len, ie_len = 0, sae_data_len = 0;
|
2009-07-02 15:20:43 +00:00
|
|
|
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) {
|
2010-10-05 17:39:30 +00:00
|
|
|
if (key.type != -1 && key.type != NL80211_KEYTYPE_GROUP)
|
|
|
|
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
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2010-08-10 07:46:42 +00:00
|
|
|
if (key.idx >= 0) {
|
|
|
|
int i;
|
|
|
|
bool ok = false;
|
|
|
|
for (i = 0; i < rdev->wiphy.n_cipher_suites; i++) {
|
|
|
|
if (key.p.cipher == rdev->wiphy.cipher_suites[i]) {
|
|
|
|
ok = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2010-10-04 19:36:35 +00:00
|
|
|
if (!ok)
|
|
|
|
return -EINVAL;
|
2010-08-10 07:46:42 +00:00
|
|
|
}
|
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (!rdev->ops->auth)
|
|
|
|
return -EOPNOTSUPP;
|
2009-03-19 11:39:22 +00:00
|
|
|
|
2010-09-16 12:58:22 +00:00
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
|
2010-10-04 19:36:35 +00:00
|
|
|
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
|
|
|
|
return -EOPNOTSUPP;
|
2009-03-20 19:21:19 +00:00
|
|
|
|
2009-07-02 15:20:43 +00:00
|
|
|
bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
2014-01-14 22:01:44 +00:00
|
|
|
chan = nl80211_get_valid_chan(&rdev->wiphy,
|
|
|
|
info->attrs[NL80211_ATTR_WIPHY_FREQ]);
|
|
|
|
if (!chan)
|
2010-10-04 19:36:35 +00:00
|
|
|
return -EINVAL;
|
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]);
|
2012-09-30 16:29:39 +00:00
|
|
|
if (!nl80211_valid_auth_type(rdev, auth_type, NL80211_CMD_AUTHENTICATE))
|
2010-10-04 19:36:35 +00:00
|
|
|
return -EINVAL;
|
2009-03-19 11:39:22 +00:00
|
|
|
|
2012-09-30 16:29:39 +00:00
|
|
|
if (auth_type == NL80211_AUTHTYPE_SAE &&
|
|
|
|
!info->attrs[NL80211_ATTR_SAE_DATA])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_SAE_DATA]) {
|
|
|
|
if (auth_type != NL80211_AUTHTYPE_SAE)
|
|
|
|
return -EINVAL;
|
|
|
|
sae_data = nla_data(info->attrs[NL80211_ATTR_SAE_DATA]);
|
|
|
|
sae_data_len = nla_len(info->attrs[NL80211_ATTR_SAE_DATA]);
|
|
|
|
/* need to include at least Auth Transaction and Status Code */
|
|
|
|
if (sae_data_len < 4)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2010-04-04 06:37:19 +00:00
|
|
|
local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
|
|
|
|
|
2012-01-20 12:55:25 +00:00
|
|
|
/*
|
|
|
|
* Since we no longer track auth state, ignore
|
|
|
|
* requests to only change local state.
|
|
|
|
*/
|
|
|
|
if (local_state_change)
|
|
|
|
return 0;
|
|
|
|
|
2013-05-15 15:44:01 +00:00
|
|
|
wdev_lock(dev->ieee80211_ptr);
|
|
|
|
err = cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
|
|
|
|
ssid, ssid_len, ie, ie_len,
|
|
|
|
key.p.key, key.p.key_len, key.idx,
|
|
|
|
sae_data, sae_data_len);
|
|
|
|
wdev_unlock(dev->ieee80211_ptr);
|
|
|
|
return err;
|
2009-03-19 11:39:22 +00:00
|
|
|
}
|
|
|
|
|
2010-08-27 11:26:53 +00:00
|
|
|
static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
|
|
|
|
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];
|
|
|
|
|
2010-08-27 11:26:53 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]) {
|
|
|
|
u16 proto;
|
|
|
|
proto = nla_get_u16(
|
|
|
|
info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]);
|
|
|
|
settings->control_port_ethertype = cpu_to_be16(proto);
|
|
|
|
if (!(rdev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) &&
|
|
|
|
proto != ETH_P_PAE)
|
|
|
|
return -EINVAL;
|
|
|
|
if (info->attrs[NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT])
|
|
|
|
settings->control_port_no_encrypt = true;
|
|
|
|
} else
|
|
|
|
settings->control_port_ethertype = cpu_to_be16(ETH_P_PAE);
|
|
|
|
|
2009-07-01 19:26:54 +00:00
|
|
|
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++)
|
2011-09-21 15:14:56 +00:00
|
|
|
if (!cfg80211_supported_cipher_suite(
|
|
|
|
&rdev->wiphy,
|
2009-07-01 19:26:54 +00:00
|
|
|
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]);
|
2011-09-21 15:14:56 +00:00
|
|
|
if (!cfg80211_supported_cipher_suite(&rdev->wiphy,
|
|
|
|
settings->cipher_group))
|
2009-07-01 19:26:54 +00:00
|
|
|
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;
|
2011-09-21 15:11:33 +00:00
|
|
|
int len;
|
2009-07-01 19:26:54 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2011-09-21 13:13:07 +00:00
|
|
|
if (settings->n_akm_suites > NL80211_MAX_NR_AKM_SUITES)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2009-07-01 19:26:54 +00:00
|
|
|
memcpy(settings->akm_suites, data, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-03-19 11:39:22 +00:00
|
|
|
static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
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;
|
2013-02-21 19:09:09 +00:00
|
|
|
struct cfg80211_assoc_request req = {};
|
|
|
|
const u8 *bssid, *ssid;
|
|
|
|
int err, ssid_len = 0;
|
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;
|
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (!rdev->ops->assoc)
|
|
|
|
return -EOPNOTSUPP;
|
2009-03-19 11:39:22 +00:00
|
|
|
|
2010-09-16 12:58:22 +00:00
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
|
2010-10-04 19:36:35 +00:00
|
|
|
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
|
|
|
|
return -EOPNOTSUPP;
|
2009-03-20 19:21:19 +00:00
|
|
|
|
2009-07-02 15:20:43 +00:00
|
|
|
bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
2009-03-19 11:39:22 +00:00
|
|
|
|
2014-01-14 22:01:44 +00:00
|
|
|
chan = nl80211_get_valid_chan(&rdev->wiphy,
|
|
|
|
info->attrs[NL80211_ATTR_WIPHY_FREQ]);
|
|
|
|
if (!chan)
|
2010-10-04 19:36:35 +00:00
|
|
|
return -EINVAL;
|
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]) {
|
2013-02-21 19:09:09 +00:00
|
|
|
req.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
|
|
|
|
req.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)
|
2013-02-21 19:09:09 +00:00
|
|
|
req.use_mfp = true;
|
2010-10-04 19:36:35 +00:00
|
|
|
else if (mfp != NL80211_MFP_NO)
|
|
|
|
return -EINVAL;
|
2009-05-06 19:09:37 +00:00
|
|
|
}
|
|
|
|
|
2009-07-07 12:37:26 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_PREV_BSSID])
|
2013-02-21 19:09:09 +00:00
|
|
|
req.prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
|
2009-07-07 12:37:26 +00:00
|
|
|
|
2011-11-18 19:31:59 +00:00
|
|
|
if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_HT]))
|
2013-02-21 19:09:09 +00:00
|
|
|
req.flags |= ASSOC_REQ_DISABLE_HT;
|
2011-11-18 19:31:59 +00:00
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
|
2013-02-21 19:09:09 +00:00
|
|
|
memcpy(&req.ht_capa_mask,
|
|
|
|
nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]),
|
|
|
|
sizeof(req.ht_capa_mask));
|
2011-11-18 19:31:59 +00:00
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
|
2013-02-21 19:09:09 +00:00
|
|
|
if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
|
2011-11-18 19:31:59 +00:00
|
|
|
return -EINVAL;
|
2013-02-21 19:09:09 +00:00
|
|
|
memcpy(&req.ht_capa,
|
|
|
|
nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]),
|
|
|
|
sizeof(req.ht_capa));
|
2011-11-18 19:31:59 +00:00
|
|
|
}
|
|
|
|
|
2013-02-21 16:36:01 +00:00
|
|
|
if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_VHT]))
|
2013-02-21 19:09:09 +00:00
|
|
|
req.flags |= ASSOC_REQ_DISABLE_VHT;
|
2013-02-21 16:36:01 +00:00
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK])
|
2013-02-21 19:09:09 +00:00
|
|
|
memcpy(&req.vht_capa_mask,
|
|
|
|
nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]),
|
|
|
|
sizeof(req.vht_capa_mask));
|
2013-02-21 16:36:01 +00:00
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_VHT_CAPABILITY]) {
|
2013-02-21 19:09:09 +00:00
|
|
|
if (!info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK])
|
2013-02-21 16:36:01 +00:00
|
|
|
return -EINVAL;
|
2013-02-21 19:09:09 +00:00
|
|
|
memcpy(&req.vht_capa,
|
|
|
|
nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]),
|
|
|
|
sizeof(req.vht_capa));
|
2013-02-21 16:36:01 +00:00
|
|
|
}
|
|
|
|
|
2014-09-03 12:25:01 +00:00
|
|
|
if (nla_get_flag(info->attrs[NL80211_ATTR_USE_RRM])) {
|
|
|
|
if (!(rdev->wiphy.features &
|
|
|
|
NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES) ||
|
|
|
|
!(rdev->wiphy.features & NL80211_FEATURE_QUIET))
|
|
|
|
return -EINVAL;
|
|
|
|
req.flags |= ASSOC_REQ_USE_RRM;
|
|
|
|
}
|
|
|
|
|
2013-02-21 19:09:09 +00:00
|
|
|
err = nl80211_crypto_settings(rdev, info, &req.crypto, 1);
|
2013-05-15 15:44:01 +00:00
|
|
|
if (!err) {
|
|
|
|
wdev_lock(dev->ieee80211_ptr);
|
2013-02-21 19:09:09 +00:00
|
|
|
err = cfg80211_mlme_assoc(rdev, dev, chan, bssid,
|
|
|
|
ssid, ssid_len, &req);
|
2013-05-15 15:44:01 +00:00
|
|
|
wdev_unlock(dev->ieee80211_ptr);
|
|
|
|
}
|
2009-03-19 11:39:22 +00:00
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
2009-07-02 15:20:43 +00:00
|
|
|
const u8 *ie = NULL, *bssid;
|
2013-05-15 15:44:01 +00:00
|
|
|
int ie_len = 0, err;
|
2009-07-02 15:20:43 +00:00
|
|
|
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;
|
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (!rdev->ops->deauth)
|
|
|
|
return -EOPNOTSUPP;
|
2009-03-19 11:39:22 +00:00
|
|
|
|
2010-09-16 12:58:22 +00:00
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
|
2010-10-04 19:36:35 +00:00
|
|
|
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
|
|
|
|
return -EOPNOTSUPP;
|
2009-03-20 19:21:19 +00:00
|
|
|
|
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 */
|
2010-10-04 19:36:35 +00:00
|
|
|
return -EINVAL;
|
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];
|
|
|
|
|
2013-05-15 15:44:01 +00:00
|
|
|
wdev_lock(dev->ieee80211_ptr);
|
|
|
|
err = cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code,
|
|
|
|
local_state_change);
|
|
|
|
wdev_unlock(dev->ieee80211_ptr);
|
|
|
|
return err;
|
2009-03-19 11:39:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
2009-07-02 15:20:43 +00:00
|
|
|
const u8 *ie = NULL, *bssid;
|
2013-05-15 15:44:01 +00:00
|
|
|
int ie_len = 0, err;
|
2009-07-02 15:20:43 +00:00
|
|
|
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;
|
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (!rdev->ops->disassoc)
|
|
|
|
return -EOPNOTSUPP;
|
2009-03-19 11:39:22 +00:00
|
|
|
|
2010-09-16 12:58:22 +00:00
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
|
2010-10-04 19:36:35 +00:00
|
|
|
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
|
|
|
|
return -EOPNOTSUPP;
|
2009-03-20 19:21:19 +00:00
|
|
|
|
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 */
|
2010-10-04 19:36:35 +00:00
|
|
|
return -EINVAL;
|
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];
|
|
|
|
|
2013-05-15 15:44:01 +00:00
|
|
|
wdev_lock(dev->ieee80211_ptr);
|
|
|
|
err = cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code,
|
|
|
|
local_state_change);
|
|
|
|
wdev_unlock(dev->ieee80211_ptr);
|
|
|
|
return err;
|
2009-03-19 11:39:22 +00:00
|
|
|
}
|
|
|
|
|
2010-11-22 19:58:24 +00:00
|
|
|
static bool
|
|
|
|
nl80211_parse_mcast_rate(struct cfg80211_registered_device *rdev,
|
|
|
|
int mcast_rate[IEEE80211_NUM_BANDS],
|
|
|
|
int rateval)
|
|
|
|
{
|
|
|
|
struct wiphy *wiphy = &rdev->wiphy;
|
|
|
|
bool found = false;
|
|
|
|
int band, i;
|
|
|
|
|
|
|
|
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
|
|
|
|
struct ieee80211_supported_band *sband;
|
|
|
|
|
|
|
|
sband = wiphy->bands[band];
|
|
|
|
if (!sband)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
for (i = 0; i < sband->n_bitrates; i++) {
|
|
|
|
if (sband->bitrates[i].bitrate == rateval) {
|
|
|
|
mcast_rate[band] = i + 1;
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
2009-04-19 19:24:32 +00:00
|
|
|
static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
2009-04-19 19:24:32 +00:00
|
|
|
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;
|
|
|
|
|
2012-11-08 20:25:48 +00:00
|
|
|
if (!info->attrs[NL80211_ATTR_SSID] ||
|
2009-04-19 19:24:32 +00:00
|
|
|
!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;
|
|
|
|
}
|
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (!rdev->ops->join_ibss)
|
|
|
|
return -EOPNOTSUPP;
|
2009-04-19 19:24:32 +00:00
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC)
|
|
|
|
return -EOPNOTSUPP;
|
2009-04-19 19:24:32 +00:00
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
wiphy = &rdev->wiphy;
|
2009-04-19 19:24:32 +00:00
|
|
|
|
2011-09-16 11:45:25 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_MAC]) {
|
2009-04-19 19:24:32 +00:00
|
|
|
ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
2011-09-16 11:45:25 +00:00
|
|
|
|
|
|
|
if (!is_valid_ether_addr(ibss.bssid))
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2009-04-19 19:24:32 +00:00
|
|
|
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]);
|
|
|
|
}
|
|
|
|
|
2012-11-08 20:25:48 +00:00
|
|
|
err = nl80211_parse_chandef(rdev, info, &ibss.chandef);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2009-04-19 19:24:32 +00:00
|
|
|
|
2014-02-23 07:13:01 +00:00
|
|
|
if (!cfg80211_reg_can_beacon(&rdev->wiphy, &ibss.chandef,
|
|
|
|
NL80211_IFTYPE_ADHOC))
|
2011-11-30 15:56:32 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2013-05-16 11:00:28 +00:00
|
|
|
switch (ibss.chandef.width) {
|
2013-07-08 14:55:58 +00:00
|
|
|
case NL80211_CHAN_WIDTH_5:
|
|
|
|
case NL80211_CHAN_WIDTH_10:
|
2013-05-16 11:00:28 +00:00
|
|
|
case NL80211_CHAN_WIDTH_20_NOHT:
|
|
|
|
break;
|
|
|
|
case NL80211_CHAN_WIDTH_20:
|
|
|
|
case NL80211_CHAN_WIDTH_40:
|
|
|
|
if (rdev->wiphy.features & NL80211_FEATURE_HT_IBSS)
|
|
|
|
break;
|
|
|
|
default:
|
2012-11-29 17:37:22 +00:00
|
|
|
return -EINVAL;
|
2013-05-16 11:00:28 +00:00
|
|
|
}
|
2012-11-09 13:56:41 +00:00
|
|
|
|
2009-04-19 19:24:32 +00:00
|
|
|
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];
|
|
|
|
|
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 =
|
2012-11-08 20:25:48 +00:00
|
|
|
wiphy->bands[ibss.chandef.chan->band];
|
2010-06-14 09:55:31 +00:00
|
|
|
|
2011-07-18 16:08:35 +00:00
|
|
|
err = ieee80211_get_ratemask(sband, rates, n_rates,
|
|
|
|
&ibss.basic_rates);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2010-06-14 09:55:31 +00:00
|
|
|
}
|
2010-11-22 19:58:24 +00:00
|
|
|
|
2013-06-28 08:39:58 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
|
|
|
|
memcpy(&ibss.ht_capa_mask,
|
|
|
|
nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]),
|
|
|
|
sizeof(ibss.ht_capa_mask));
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
|
|
|
|
if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
|
|
|
|
return -EINVAL;
|
|
|
|
memcpy(&ibss.ht_capa,
|
|
|
|
nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]),
|
|
|
|
sizeof(ibss.ht_capa));
|
|
|
|
}
|
|
|
|
|
2010-11-22 19:58:24 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_MCAST_RATE] &&
|
|
|
|
!nl80211_parse_mcast_rate(rdev, ibss.mcast_rate,
|
|
|
|
nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE])))
|
|
|
|
return -EINVAL;
|
2010-06-14 09:55:31 +00:00
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) {
|
2012-10-18 04:49:28 +00:00
|
|
|
bool no_ht = false;
|
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
connkeys = nl80211_parse_connkeys(rdev,
|
2012-10-18 04:49:28 +00:00
|
|
|
info->attrs[NL80211_ATTR_KEYS],
|
|
|
|
&no_ht);
|
2010-10-04 19:36:35 +00:00
|
|
|
if (IS_ERR(connkeys))
|
|
|
|
return PTR_ERR(connkeys);
|
2012-10-18 04:49:28 +00:00
|
|
|
|
2012-11-08 22:14:50 +00:00
|
|
|
if ((ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) &&
|
|
|
|
no_ht) {
|
2012-10-18 04:49:28 +00:00
|
|
|
kfree(connkeys);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2010-10-04 19:36:35 +00:00
|
|
|
}
|
2009-04-19 19:24:32 +00:00
|
|
|
|
2012-01-31 19:25:47 +00:00
|
|
|
ibss.control_port =
|
|
|
|
nla_get_flag(info->attrs[NL80211_ATTR_CONTROL_PORT]);
|
|
|
|
|
2013-10-07 16:41:05 +00:00
|
|
|
ibss.userspace_handles_dfs =
|
|
|
|
nla_get_flag(info->attrs[NL80211_ATTR_HANDLE_DFS]);
|
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
|
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)
|
2014-09-10 10:39:54 +00:00
|
|
|
kzfree(connkeys);
|
2009-04-19 19:24:32 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
2009-04-19 19:24:32 +00:00
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (!rdev->ops->leave_ibss)
|
|
|
|
return -EOPNOTSUPP;
|
2009-04-19 19:24:32 +00:00
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC)
|
|
|
|
return -EOPNOTSUPP;
|
2009-04-19 19:24:32 +00:00
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
return cfg80211_leave_ibss(rdev, dev, false);
|
2009-04-19 19:24:32 +00:00
|
|
|
}
|
|
|
|
|
2012-11-02 12:27:48 +00:00
|
|
|
static int nl80211_set_mcast_rate(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
|
|
|
int mcast_rate[IEEE80211_NUM_BANDS];
|
|
|
|
u32 nla_rate;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
|
|
|
|
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (!rdev->ops->set_mcast_rate)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
memset(mcast_rate, 0, sizeof(mcast_rate));
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_MCAST_RATE])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
nla_rate = nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE]);
|
|
|
|
if (!nl80211_parse_mcast_rate(rdev, mcast_rate, nla_rate))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
err = rdev->ops->set_mcast_rate(&rdev->wiphy, dev, mcast_rate);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2013-11-13 12:37:47 +00:00
|
|
|
static struct sk_buff *
|
|
|
|
__cfg80211_alloc_vendor_skb(struct cfg80211_registered_device *rdev,
|
|
|
|
int approxlen, u32 portid, u32 seq,
|
|
|
|
enum nl80211_commands cmd,
|
2013-12-18 13:43:31 +00:00
|
|
|
enum nl80211_attrs attr,
|
|
|
|
const struct nl80211_vendor_cmd_info *info,
|
|
|
|
gfp_t gfp)
|
2013-11-13 12:37:47 +00:00
|
|
|
{
|
|
|
|
struct sk_buff *skb;
|
|
|
|
void *hdr;
|
|
|
|
struct nlattr *data;
|
|
|
|
|
|
|
|
skb = nlmsg_new(approxlen + 100, gfp);
|
|
|
|
if (!skb)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(skb, portid, seq, 0, cmd);
|
|
|
|
if (!hdr) {
|
|
|
|
kfree_skb(skb);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nla_put_u32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx))
|
|
|
|
goto nla_put_failure;
|
2013-12-18 13:43:31 +00:00
|
|
|
|
|
|
|
if (info) {
|
|
|
|
if (nla_put_u32(skb, NL80211_ATTR_VENDOR_ID,
|
|
|
|
info->vendor_id))
|
|
|
|
goto nla_put_failure;
|
|
|
|
if (nla_put_u32(skb, NL80211_ATTR_VENDOR_SUBCMD,
|
|
|
|
info->subcmd))
|
|
|
|
goto nla_put_failure;
|
|
|
|
}
|
|
|
|
|
2013-11-13 12:37:47 +00:00
|
|
|
data = nla_nest_start(skb, attr);
|
|
|
|
|
|
|
|
((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;
|
|
|
|
}
|
2012-11-02 12:27:48 +00:00
|
|
|
|
2014-01-01 16:22:30 +00:00
|
|
|
struct sk_buff *__cfg80211_alloc_event_skb(struct wiphy *wiphy,
|
|
|
|
enum nl80211_commands cmd,
|
|
|
|
enum nl80211_attrs attr,
|
|
|
|
int vendor_event_idx,
|
|
|
|
int approxlen, gfp_t gfp)
|
|
|
|
{
|
2014-04-21 04:53:03 +00:00
|
|
|
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
2014-01-01 16:22:30 +00:00
|
|
|
const struct nl80211_vendor_cmd_info *info;
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case NL80211_CMD_TESTMODE:
|
|
|
|
if (WARN_ON(vendor_event_idx != -1))
|
|
|
|
return NULL;
|
|
|
|
info = NULL;
|
|
|
|
break;
|
|
|
|
case NL80211_CMD_VENDOR:
|
|
|
|
if (WARN_ON(vendor_event_idx < 0 ||
|
|
|
|
vendor_event_idx >= wiphy->n_vendor_events))
|
|
|
|
return NULL;
|
|
|
|
info = &wiphy->vendor_events[vendor_event_idx];
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
WARN_ON(1);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return __cfg80211_alloc_vendor_skb(rdev, approxlen, 0, 0,
|
|
|
|
cmd, attr, info, gfp);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(__cfg80211_alloc_event_skb);
|
|
|
|
|
|
|
|
void __cfg80211_send_event_skb(struct sk_buff *skb, gfp_t gfp)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
|
|
|
|
void *hdr = ((void **)skb->cb)[1];
|
|
|
|
struct nlattr *data = ((void **)skb->cb)[2];
|
|
|
|
enum nl80211_multicast_groups mcgrp = NL80211_MCGRP_TESTMODE;
|
|
|
|
|
2014-07-30 12:55:26 +00:00
|
|
|
/* clear CB data for netlink core to own from now on */
|
|
|
|
memset(skb->cb, 0, sizeof(skb->cb));
|
|
|
|
|
2014-01-01 16:22:30 +00:00
|
|
|
nla_nest_end(skb, data);
|
|
|
|
genlmsg_end(skb, hdr);
|
|
|
|
|
|
|
|
if (data->nla_type == NL80211_ATTR_VENDOR_DATA)
|
|
|
|
mcgrp = NL80211_MCGRP_VENDOR;
|
|
|
|
|
|
|
|
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), skb, 0,
|
|
|
|
mcgrp, gfp);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(__cfg80211_send_event_skb);
|
|
|
|
|
2009-07-01 19:26:51 +00:00
|
|
|
#ifdef CONFIG_NL80211_TESTMODE
|
|
|
|
static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
2013-07-31 15:04:15 +00:00
|
|
|
struct wireless_dev *wdev =
|
|
|
|
__cfg80211_wdev_from_attrs(genl_info_net(info), info->attrs);
|
2009-07-01 19:26:51 +00:00
|
|
|
int err;
|
|
|
|
|
2013-07-31 15:04:15 +00:00
|
|
|
if (!rdev->ops->testmode_cmd)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (IS_ERR(wdev)) {
|
|
|
|
err = PTR_ERR(wdev);
|
|
|
|
if (err != -EINVAL)
|
|
|
|
return err;
|
|
|
|
wdev = NULL;
|
|
|
|
} else if (wdev->wiphy != &rdev->wiphy) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2009-07-01 19:26:51 +00:00
|
|
|
if (!info->attrs[NL80211_ATTR_TESTDATA])
|
|
|
|
return -EINVAL;
|
|
|
|
|
2013-11-13 12:37:47 +00:00
|
|
|
rdev->cur_cmd_info = info;
|
2013-07-31 15:04:15 +00:00
|
|
|
err = rdev_testmode_cmd(rdev, wdev,
|
2009-07-01 19:26:51 +00:00
|
|
|
nla_data(info->attrs[NL80211_ATTR_TESTDATA]),
|
|
|
|
nla_len(info->attrs[NL80211_ATTR_TESTDATA]));
|
2013-11-13 12:37:47 +00:00
|
|
|
rdev->cur_cmd_info = NULL;
|
2009-07-01 19:26:51 +00:00
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2011-05-20 16:05:54 +00:00
|
|
|
static int nl80211_testmode_dump(struct sk_buff *skb,
|
|
|
|
struct netlink_callback *cb)
|
|
|
|
{
|
2011-12-13 16:22:05 +00:00
|
|
|
struct cfg80211_registered_device *rdev;
|
2011-05-20 16:05:54 +00:00
|
|
|
int err;
|
|
|
|
long phy_idx;
|
|
|
|
void *data = NULL;
|
|
|
|
int data_len = 0;
|
|
|
|
|
2013-05-08 19:45:15 +00:00
|
|
|
rtnl_lock();
|
|
|
|
|
2011-05-20 16:05:54 +00:00
|
|
|
if (cb->args[0]) {
|
|
|
|
/*
|
|
|
|
* 0 is a valid index, but not valid for args[0],
|
|
|
|
* so we need to offset by 1.
|
|
|
|
*/
|
|
|
|
phy_idx = cb->args[0] - 1;
|
|
|
|
} else {
|
|
|
|
err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
|
|
|
|
nl80211_fam.attrbuf, nl80211_fam.maxattr,
|
|
|
|
nl80211_policy);
|
|
|
|
if (err)
|
2013-05-08 19:45:15 +00:00
|
|
|
goto out_err;
|
2011-12-13 16:22:05 +00:00
|
|
|
|
2012-06-15 12:23:16 +00:00
|
|
|
rdev = __cfg80211_rdev_from_attrs(sock_net(skb->sk),
|
|
|
|
nl80211_fam.attrbuf);
|
|
|
|
if (IS_ERR(rdev)) {
|
2013-05-08 19:45:15 +00:00
|
|
|
err = PTR_ERR(rdev);
|
|
|
|
goto out_err;
|
2011-12-13 16:22:05 +00:00
|
|
|
}
|
2012-06-15 12:23:16 +00:00
|
|
|
phy_idx = rdev->wiphy_idx;
|
|
|
|
rdev = NULL;
|
|
|
|
|
2011-05-20 16:05:54 +00:00
|
|
|
if (nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA])
|
|
|
|
cb->args[1] =
|
|
|
|
(long)nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cb->args[1]) {
|
|
|
|
data = nla_data((void *)cb->args[1]);
|
|
|
|
data_len = nla_len((void *)cb->args[1]);
|
|
|
|
}
|
|
|
|
|
2011-12-13 16:22:05 +00:00
|
|
|
rdev = cfg80211_rdev_by_wiphy_idx(phy_idx);
|
|
|
|
if (!rdev) {
|
2013-05-08 19:45:15 +00:00
|
|
|
err = -ENOENT;
|
|
|
|
goto out_err;
|
2011-05-20 16:05:54 +00:00
|
|
|
}
|
|
|
|
|
2011-12-13 16:22:05 +00:00
|
|
|
if (!rdev->ops->testmode_dump) {
|
2011-05-20 16:05:54 +00:00
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out_err;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (1) {
|
2012-09-07 20:12:54 +00:00
|
|
|
void *hdr = nl80211hdr_put(skb, NETLINK_CB(cb->skb).portid,
|
2011-05-20 16:05:54 +00:00
|
|
|
cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
|
|
|
NL80211_CMD_TESTMODE);
|
|
|
|
struct nlattr *tmdata;
|
|
|
|
|
2013-08-14 11:50:01 +00:00
|
|
|
if (!hdr)
|
|
|
|
break;
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (nla_put_u32(skb, NL80211_ATTR_WIPHY, phy_idx)) {
|
2011-05-20 16:05:54 +00:00
|
|
|
genlmsg_cancel(skb, hdr);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
tmdata = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
|
|
|
|
if (!tmdata) {
|
|
|
|
genlmsg_cancel(skb, hdr);
|
|
|
|
break;
|
|
|
|
}
|
2012-06-27 14:19:42 +00:00
|
|
|
err = rdev_testmode_dump(rdev, skb, cb, data, data_len);
|
2011-05-20 16:05:54 +00:00
|
|
|
nla_nest_end(skb, tmdata);
|
|
|
|
|
|
|
|
if (err == -ENOBUFS || err == -ENOENT) {
|
|
|
|
genlmsg_cancel(skb, hdr);
|
|
|
|
break;
|
|
|
|
} else if (err) {
|
|
|
|
genlmsg_cancel(skb, hdr);
|
|
|
|
goto out_err;
|
|
|
|
}
|
|
|
|
|
|
|
|
genlmsg_end(skb, hdr);
|
|
|
|
}
|
|
|
|
|
|
|
|
err = skb->len;
|
|
|
|
/* see above */
|
|
|
|
cb->args[0] = phy_idx + 1;
|
|
|
|
out_err:
|
2013-05-08 19:45:15 +00:00
|
|
|
rtnl_unlock();
|
2011-05-20 16:05:54 +00:00
|
|
|
return err;
|
|
|
|
}
|
2009-07-01 19:26:51 +00:00
|
|
|
#endif
|
|
|
|
|
2009-07-01 19:26:54 +00:00
|
|
|
static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
2009-07-01 19:26:54 +00:00
|
|
|
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]);
|
2012-09-30 16:29:39 +00:00
|
|
|
if (!nl80211_valid_auth_type(rdev, connect.auth_type,
|
|
|
|
NL80211_CMD_CONNECT))
|
2009-07-01 19:26:54 +00:00
|
|
|
return -EINVAL;
|
|
|
|
} else
|
|
|
|
connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
|
|
|
|
|
|
|
|
connect.privacy = info->attrs[NL80211_ATTR_PRIVACY];
|
|
|
|
|
2010-08-27 11:26:53 +00:00
|
|
|
err = nl80211_crypto_settings(rdev, info, &connect.crypto,
|
2009-07-02 19:36:37 +00:00
|
|
|
NL80211_MAX_NR_CIPHER_SUITES);
|
2009-07-01 19:26:54 +00:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2010-09-16 12:58:22 +00:00
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
|
2010-10-04 19:36:35 +00:00
|
|
|
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
|
|
|
|
return -EOPNOTSUPP;
|
2009-07-01 19:26:54 +00:00
|
|
|
|
2009-07-07 01:56:12 +00:00
|
|
|
wiphy = &rdev->wiphy;
|
2009-07-01 19:26:54 +00:00
|
|
|
|
2012-03-07 11:57:12 +00:00
|
|
|
connect.bg_scan_period = -1;
|
|
|
|
if (info->attrs[NL80211_ATTR_BG_SCAN_PERIOD] &&
|
|
|
|
(wiphy->flags & WIPHY_FLAG_SUPPORTS_FW_ROAM)) {
|
|
|
|
connect.bg_scan_period =
|
|
|
|
nla_get_u16(info->attrs[NL80211_ATTR_BG_SCAN_PERIOD]);
|
|
|
|
}
|
|
|
|
|
2009-07-01 19:26:54 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_MAC])
|
|
|
|
connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
2014-01-14 22:00:47 +00:00
|
|
|
else if (info->attrs[NL80211_ATTR_MAC_HINT])
|
|
|
|
connect.bssid_hint =
|
|
|
|
nla_data(info->attrs[NL80211_ATTR_MAC_HINT]);
|
2009-07-01 19:26:54 +00:00
|
|
|
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]);
|
|
|
|
}
|
|
|
|
|
2013-01-15 15:15:57 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_USE_MFP]) {
|
|
|
|
connect.mfp = nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
|
|
|
|
if (connect.mfp != NL80211_MFP_REQUIRED &&
|
|
|
|
connect.mfp != NL80211_MFP_NO)
|
|
|
|
return -EINVAL;
|
|
|
|
} else {
|
|
|
|
connect.mfp = NL80211_MFP_NO;
|
|
|
|
}
|
|
|
|
|
2009-07-01 19:26:54 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
|
2014-01-14 22:01:44 +00:00
|
|
|
connect.channel = nl80211_get_valid_chan(
|
|
|
|
wiphy, info->attrs[NL80211_ATTR_WIPHY_FREQ]);
|
|
|
|
if (!connect.channel)
|
2014-01-14 22:00:47 +00:00
|
|
|
return -EINVAL;
|
|
|
|
} else if (info->attrs[NL80211_ATTR_WIPHY_FREQ_HINT]) {
|
2014-01-14 22:01:44 +00:00
|
|
|
connect.channel_hint = nl80211_get_valid_chan(
|
|
|
|
wiphy, info->attrs[NL80211_ATTR_WIPHY_FREQ_HINT]);
|
|
|
|
if (!connect.channel_hint)
|
2010-10-04 19:36:35 +00:00
|
|
|
return -EINVAL;
|
2009-07-01 19:26:54 +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 (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) {
|
|
|
|
connkeys = nl80211_parse_connkeys(rdev,
|
2012-10-18 04:49:28 +00:00
|
|
|
info->attrs[NL80211_ATTR_KEYS], NULL);
|
2010-10-04 19:36:35 +00:00
|
|
|
if (IS_ERR(connkeys))
|
|
|
|
return PTR_ERR(connkeys);
|
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
|
|
|
}
|
|
|
|
|
2011-11-18 19:31:59 +00:00
|
|
|
if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_HT]))
|
|
|
|
connect.flags |= ASSOC_REQ_DISABLE_HT;
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
|
|
|
|
memcpy(&connect.ht_capa_mask,
|
|
|
|
nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]),
|
|
|
|
sizeof(connect.ht_capa_mask));
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
|
2012-09-02 13:41:04 +00:00
|
|
|
if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]) {
|
2014-09-10 10:39:54 +00:00
|
|
|
kzfree(connkeys);
|
2011-11-18 19:31:59 +00:00
|
|
|
return -EINVAL;
|
2012-09-02 13:41:04 +00:00
|
|
|
}
|
2011-11-18 19:31:59 +00:00
|
|
|
memcpy(&connect.ht_capa,
|
|
|
|
nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]),
|
|
|
|
sizeof(connect.ht_capa));
|
|
|
|
}
|
|
|
|
|
2013-02-21 16:36:01 +00:00
|
|
|
if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_VHT]))
|
|
|
|
connect.flags |= ASSOC_REQ_DISABLE_VHT;
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK])
|
|
|
|
memcpy(&connect.vht_capa_mask,
|
|
|
|
nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]),
|
|
|
|
sizeof(connect.vht_capa_mask));
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_VHT_CAPABILITY]) {
|
|
|
|
if (!info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]) {
|
2014-09-10 10:39:54 +00:00
|
|
|
kzfree(connkeys);
|
2013-02-21 16:36:01 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
memcpy(&connect.vht_capa,
|
|
|
|
nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]),
|
|
|
|
sizeof(connect.vht_capa));
|
|
|
|
}
|
|
|
|
|
2014-09-03 12:25:01 +00:00
|
|
|
if (nla_get_flag(info->attrs[NL80211_ATTR_USE_RRM])) {
|
|
|
|
if (!(rdev->wiphy.features &
|
|
|
|
NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES) ||
|
|
|
|
!(rdev->wiphy.features & NL80211_FEATURE_QUIET))
|
|
|
|
return -EINVAL;
|
|
|
|
connect.flags |= ASSOC_REQ_USE_RRM;
|
|
|
|
}
|
|
|
|
|
2013-05-15 15:44:01 +00:00
|
|
|
wdev_lock(dev->ieee80211_ptr);
|
|
|
|
err = cfg80211_connect(rdev, dev, &connect, connkeys, NULL);
|
|
|
|
wdev_unlock(dev->ieee80211_ptr);
|
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)
|
2014-09-10 10:39:54 +00:00
|
|
|
kzfree(connkeys);
|
2009-07-01 19:26:54 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
2009-07-01 19:26:54 +00:00
|
|
|
u16 reason;
|
2013-05-15 15:44:01 +00:00
|
|
|
int ret;
|
2009-07-01 19:26:54 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2010-09-16 12:58:22 +00:00
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
|
2010-10-04 19:36:35 +00:00
|
|
|
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
|
|
|
|
return -EOPNOTSUPP;
|
2009-07-01 19:26:54 +00:00
|
|
|
|
2013-05-15 15:44:01 +00:00
|
|
|
wdev_lock(dev->ieee80211_ptr);
|
|
|
|
ret = cfg80211_disconnect(rdev, dev, reason, true);
|
|
|
|
wdev_unlock(dev->ieee80211_ptr);
|
|
|
|
return ret;
|
2009-07-01 19:26:54 +00:00
|
|
|
}
|
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
2009-07-13 22:33:35 +00:00
|
|
|
struct net *net;
|
|
|
|
int err;
|
|
|
|
u32 pid;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_PID])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
pid = nla_get_u32(info->attrs[NL80211_ATTR_PID]);
|
|
|
|
|
|
|
|
net = get_net_ns_by_pid(pid);
|
2010-10-04 19:36:35 +00:00
|
|
|
if (IS_ERR(net))
|
|
|
|
return PTR_ERR(net);
|
2009-07-13 22:33:35 +00:00
|
|
|
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
/* check if anything to do */
|
2010-10-04 19:36:35 +00:00
|
|
|
if (!net_eq(wiphy_net(&rdev->wiphy), net))
|
|
|
|
err = cfg80211_switch_netns(rdev, net);
|
2009-07-13 22:33:35 +00:00
|
|
|
|
|
|
|
put_net(net);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2009-11-24 22:59:15 +00:00
|
|
|
static int nl80211_setdel_pmksa(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
2009-11-24 22:59:15 +00:00
|
|
|
int (*rdev_ops)(struct wiphy *wiphy, struct net_device *dev,
|
|
|
|
struct cfg80211_pmksa *pmksa) = NULL;
|
2010-10-04 19:36:35 +00:00
|
|
|
struct net_device *dev = info->user_ptr[1];
|
2009-11-24 22:59:15 +00:00
|
|
|
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;
|
|
|
|
|
|
|
|
pmksa.pmkid = nla_data(info->attrs[NL80211_ATTR_PMKID]);
|
|
|
|
pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
|
|
|
|
2010-09-16 12:58:22 +00:00
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
|
2010-10-04 19:36:35 +00:00
|
|
|
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
|
|
|
|
return -EOPNOTSUPP;
|
2009-11-24 22:59:15 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (!rdev_ops)
|
|
|
|
return -EOPNOTSUPP;
|
2009-11-24 22:59:15 +00:00
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
return rdev_ops(&rdev->wiphy, dev, &pmksa);
|
2009-11-24 22:59:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
2009-11-24 22:59:15 +00:00
|
|
|
|
2010-09-16 12:58:22 +00:00
|
|
|
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
|
2010-10-04 19:36:35 +00:00
|
|
|
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
|
|
|
|
return -EOPNOTSUPP;
|
2009-11-24 22:59:15 +00:00
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (!rdev->ops->flush_pmksa)
|
|
|
|
return -EOPNOTSUPP;
|
2009-11-24 22:59:15 +00:00
|
|
|
|
2012-06-27 14:19:42 +00:00
|
|
|
return rdev_flush_pmksa(rdev, dev);
|
2009-11-24 22:59:15 +00:00
|
|
|
}
|
|
|
|
|
2011-09-28 11:12:50 +00:00
|
|
|
static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
|
|
|
u8 action_code, dialog_token;
|
2014-02-20 10:52:09 +00:00
|
|
|
u32 peer_capability = 0;
|
2011-09-28 11:12:50 +00:00
|
|
|
u16 status_code;
|
|
|
|
u8 *peer;
|
2014-06-11 14:18:21 +00:00
|
|
|
bool initiator;
|
2011-09-28 11:12:50 +00:00
|
|
|
|
|
|
|
if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) ||
|
|
|
|
!rdev->ops->tdls_mgmt)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_TDLS_ACTION] ||
|
|
|
|
!info->attrs[NL80211_ATTR_STATUS_CODE] ||
|
|
|
|
!info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN] ||
|
|
|
|
!info->attrs[NL80211_ATTR_IE] ||
|
|
|
|
!info->attrs[NL80211_ATTR_MAC])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
peer = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
|
|
|
action_code = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_ACTION]);
|
|
|
|
status_code = nla_get_u16(info->attrs[NL80211_ATTR_STATUS_CODE]);
|
|
|
|
dialog_token = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN]);
|
2014-06-11 14:18:21 +00:00
|
|
|
initiator = nla_get_flag(info->attrs[NL80211_ATTR_TDLS_INITIATOR]);
|
2014-02-20 10:52:09 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_TDLS_PEER_CAPABILITY])
|
|
|
|
peer_capability =
|
|
|
|
nla_get_u32(info->attrs[NL80211_ATTR_TDLS_PEER_CAPABILITY]);
|
2011-09-28 11:12:50 +00:00
|
|
|
|
2012-06-27 14:19:42 +00:00
|
|
|
return rdev_tdls_mgmt(rdev, dev, peer, action_code,
|
2014-02-20 10:52:09 +00:00
|
|
|
dialog_token, status_code, peer_capability,
|
2014-06-11 14:18:21 +00:00
|
|
|
initiator,
|
2012-06-27 14:19:42 +00:00
|
|
|
nla_data(info->attrs[NL80211_ATTR_IE]),
|
|
|
|
nla_len(info->attrs[NL80211_ATTR_IE]));
|
2011-09-28 11:12:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_tdls_oper(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
|
|
|
enum nl80211_tdls_operation operation;
|
|
|
|
u8 *peer;
|
|
|
|
|
|
|
|
if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) ||
|
|
|
|
!rdev->ops->tdls_oper)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_TDLS_OPERATION] ||
|
|
|
|
!info->attrs[NL80211_ATTR_MAC])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
operation = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_OPERATION]);
|
|
|
|
peer = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
|
|
|
|
2012-06-27 14:19:42 +00:00
|
|
|
return rdev_tdls_oper(rdev, dev, peer, operation);
|
2011-09-28 11:12:50 +00:00
|
|
|
}
|
|
|
|
|
2009-12-23 12:15:41 +00:00
|
|
|
static int nl80211_remain_on_channel(struct sk_buff *skb,
|
|
|
|
struct genl_info *info)
|
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
2012-06-15 13:30:18 +00:00
|
|
|
struct wireless_dev *wdev = info->user_ptr[1];
|
2012-11-08 20:25:48 +00:00
|
|
|
struct cfg80211_chan_def chandef;
|
2009-12-23 12:15:41 +00:00
|
|
|
struct sk_buff *msg;
|
|
|
|
void *hdr;
|
|
|
|
u64 cookie;
|
2012-11-08 20:25:48 +00:00
|
|
|
u32 duration;
|
2009-12-23 12:15:41 +00:00
|
|
|
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]);
|
|
|
|
|
2012-06-01 10:50:54 +00:00
|
|
|
if (!rdev->ops->remain_on_channel ||
|
|
|
|
!(rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2009-12-23 12:15:41 +00:00
|
|
|
/*
|
2012-06-01 10:50:54 +00:00
|
|
|
* We should be on that channel for at least a minimum amount of
|
|
|
|
* time (10ms) but no longer than the driver supports.
|
2009-12-23 12:15:41 +00:00
|
|
|
*/
|
2012-06-01 10:50:54 +00:00
|
|
|
if (duration < NL80211_MIN_REMAIN_ON_CHANNEL_TIME ||
|
2010-12-14 16:54:28 +00:00
|
|
|
duration > rdev->wiphy.max_remain_on_channel_duration)
|
2009-12-23 12:15:41 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2012-11-08 20:25:48 +00:00
|
|
|
err = nl80211_parse_chandef(rdev, info, &chandef);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2009-12-23 12:15:41 +00:00
|
|
|
|
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
2010-10-04 19:36:35 +00:00
|
|
|
if (!msg)
|
|
|
|
return -ENOMEM;
|
2009-12-23 12:15:41 +00:00
|
|
|
|
2012-09-07 20:12:54 +00:00
|
|
|
hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
|
2009-12-23 12:15:41 +00:00
|
|
|
NL80211_CMD_REMAIN_ON_CHANNEL);
|
2013-08-14 11:50:01 +00:00
|
|
|
if (!hdr) {
|
|
|
|
err = -ENOBUFS;
|
2009-12-23 12:15:41 +00:00
|
|
|
goto free_msg;
|
|
|
|
}
|
|
|
|
|
2012-11-08 20:25:48 +00:00
|
|
|
err = rdev_remain_on_channel(rdev, wdev, chandef.chan,
|
|
|
|
duration, &cookie);
|
2009-12-23 12:15:41 +00:00
|
|
|
|
|
|
|
if (err)
|
|
|
|
goto free_msg;
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie))
|
|
|
|
goto nla_put_failure;
|
2009-12-23 12:15:41 +00:00
|
|
|
|
|
|
|
genlmsg_end(msg, hdr);
|
2010-10-04 19:36:35 +00:00
|
|
|
|
|
|
|
return genlmsg_reply(msg, info);
|
2009-12-23 12:15:41 +00:00
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
err = -ENOBUFS;
|
|
|
|
free_msg:
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_cancel_remain_on_channel(struct sk_buff *skb,
|
|
|
|
struct genl_info *info)
|
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
2012-06-15 13:30:18 +00:00
|
|
|
struct wireless_dev *wdev = info->user_ptr[1];
|
2009-12-23 12:15:41 +00:00
|
|
|
u64 cookie;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_COOKIE])
|
|
|
|
return -EINVAL;
|
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (!rdev->ops->cancel_remain_on_channel)
|
|
|
|
return -EOPNOTSUPP;
|
2009-12-23 12:15:41 +00:00
|
|
|
|
|
|
|
cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
|
|
|
|
|
2012-06-27 14:19:42 +00:00
|
|
|
return rdev_cancel_remain_on_channel(rdev, wdev, cookie);
|
2009-12-23 12:15:41 +00:00
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-01-28 16:25:32 +00:00
|
|
|
static bool ht_rateset_to_mask(struct ieee80211_supported_band *sband,
|
|
|
|
u8 *rates, u8 rates_len,
|
|
|
|
u8 mcs[IEEE80211_HT_MCS_MASK_LEN])
|
|
|
|
{
|
|
|
|
u8 i;
|
|
|
|
|
|
|
|
memset(mcs, 0, IEEE80211_HT_MCS_MASK_LEN);
|
|
|
|
|
|
|
|
for (i = 0; i < rates_len; i++) {
|
|
|
|
int ridx, rbit;
|
|
|
|
|
|
|
|
ridx = rates[i] / 8;
|
|
|
|
rbit = BIT(rates[i] % 8);
|
|
|
|
|
|
|
|
/* check validity */
|
2012-02-01 07:42:11 +00:00
|
|
|
if ((ridx < 0) || (ridx >= IEEE80211_HT_MCS_MASK_LEN))
|
2012-01-28 16:25:32 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
/* check availability */
|
|
|
|
if (sband->ht_cap.mcs.rx_mask[ridx] & rbit)
|
|
|
|
mcs[ridx] |= rbit;
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-12-05 19:42:58 +00:00
|
|
|
static u16 vht_mcs_map_to_mcs_mask(u8 vht_mcs_map)
|
|
|
|
{
|
|
|
|
u16 mcs_mask = 0;
|
|
|
|
|
|
|
|
switch (vht_mcs_map) {
|
|
|
|
case IEEE80211_VHT_MCS_NOT_SUPPORTED:
|
|
|
|
break;
|
|
|
|
case IEEE80211_VHT_MCS_SUPPORT_0_7:
|
|
|
|
mcs_mask = 0x00FF;
|
|
|
|
break;
|
|
|
|
case IEEE80211_VHT_MCS_SUPPORT_0_8:
|
|
|
|
mcs_mask = 0x01FF;
|
|
|
|
break;
|
|
|
|
case IEEE80211_VHT_MCS_SUPPORT_0_9:
|
|
|
|
mcs_mask = 0x03FF;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return mcs_mask;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void vht_build_mcs_mask(u16 vht_mcs_map,
|
|
|
|
u16 vht_mcs_mask[NL80211_VHT_NSS_MAX])
|
|
|
|
{
|
|
|
|
u8 nss;
|
|
|
|
|
|
|
|
for (nss = 0; nss < NL80211_VHT_NSS_MAX; nss++) {
|
|
|
|
vht_mcs_mask[nss] = vht_mcs_map_to_mcs_mask(vht_mcs_map & 0x03);
|
|
|
|
vht_mcs_map >>= 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool vht_set_mcs_mask(struct ieee80211_supported_band *sband,
|
|
|
|
struct nl80211_txrate_vht *txrate,
|
|
|
|
u16 mcs[NL80211_VHT_NSS_MAX])
|
|
|
|
{
|
|
|
|
u16 tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map);
|
|
|
|
u16 tx_mcs_mask[NL80211_VHT_NSS_MAX] = {};
|
|
|
|
u8 i;
|
|
|
|
|
|
|
|
if (!sband->vht_cap.vht_supported)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
memset(mcs, 0, sizeof(u16) * NL80211_VHT_NSS_MAX);
|
|
|
|
|
|
|
|
/* Build vht_mcs_mask from VHT capabilities */
|
|
|
|
vht_build_mcs_mask(tx_mcs_map, tx_mcs_mask);
|
|
|
|
|
|
|
|
for (i = 0; i < NL80211_VHT_NSS_MAX; i++) {
|
|
|
|
if ((tx_mcs_mask[i] & txrate->mcs[i]) == txrate->mcs[i])
|
|
|
|
mcs[i] = txrate->mcs[i];
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
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 },
|
2013-12-05 09:02:15 +00:00
|
|
|
[NL80211_TXRATE_HT] = { .type = NLA_BINARY,
|
|
|
|
.len = NL80211_MAX_SUPP_HT_RATES },
|
2013-12-05 19:42:58 +00:00
|
|
|
[NL80211_TXRATE_VHT] = { .len = sizeof(struct nl80211_txrate_vht)},
|
2014-01-08 07:46:02 +00:00
|
|
|
[NL80211_TXRATE_GI] = { .type = NLA_U8 },
|
2009-12-29 10:59:45 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
|
|
|
|
struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct nlattr *tb[NL80211_TXRATE_MAX + 1];
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
2009-12-29 10:59:45 +00:00
|
|
|
struct cfg80211_bitrate_mask mask;
|
2010-10-04 19:36:35 +00:00
|
|
|
int rem, i;
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
2009-12-29 10:59:45 +00:00
|
|
|
struct nlattr *tx_rates;
|
|
|
|
struct ieee80211_supported_band *sband;
|
2013-12-05 19:42:58 +00:00
|
|
|
u16 vht_tx_mcs_map;
|
2009-12-29 10:59:45 +00:00
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (!rdev->ops->set_bitrate_mask)
|
|
|
|
return -EOPNOTSUPP;
|
2009-12-29 10:59:45 +00:00
|
|
|
|
|
|
|
memset(&mask, 0, sizeof(mask));
|
|
|
|
/* Default to all rates enabled */
|
|
|
|
for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
|
|
|
|
sband = rdev->wiphy.bands[i];
|
2013-12-03 08:50:44 +00:00
|
|
|
|
|
|
|
if (!sband)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
mask.control[i].legacy = (1 << sband->n_bitrates) - 1;
|
2013-12-05 09:02:15 +00:00
|
|
|
memcpy(mask.control[i].ht_mcs,
|
2013-12-03 08:50:44 +00:00
|
|
|
sband->ht_cap.mcs.rx_mask,
|
2013-12-05 09:02:15 +00:00
|
|
|
sizeof(mask.control[i].ht_mcs));
|
2013-12-05 19:42:58 +00:00
|
|
|
|
|
|
|
if (!sband->vht_cap.vht_supported)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
vht_tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map);
|
|
|
|
vht_build_mcs_mask(vht_tx_mcs_map, mask.control[i].vht_mcs);
|
2009-12-29 10:59:45 +00:00
|
|
|
}
|
|
|
|
|
2013-12-05 09:02:14 +00:00
|
|
|
/* if no rates are given set it back to the defaults */
|
|
|
|
if (!info->attrs[NL80211_ATTR_TX_RATES])
|
|
|
|
goto out;
|
|
|
|
|
2009-12-29 10:59:45 +00:00
|
|
|
/*
|
|
|
|
* The nested attribute uses enum nl80211_band as the index. This maps
|
|
|
|
* directly to the enum ieee80211_band values used in cfg80211.
|
|
|
|
*/
|
2012-01-28 16:25:32 +00:00
|
|
|
BUILD_BUG_ON(NL80211_MAX_SUPP_HT_RATES > IEEE80211_HT_MCS_MASK_LEN * 8);
|
2014-01-24 09:17:47 +00:00
|
|
|
nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem) {
|
2009-12-29 10:59:45 +00:00
|
|
|
enum ieee80211_band band = nla_type(tx_rates);
|
2014-01-24 09:17:47 +00:00
|
|
|
int err;
|
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (band < 0 || band >= IEEE80211_NUM_BANDS)
|
|
|
|
return -EINVAL;
|
2009-12-29 10:59:45 +00:00
|
|
|
sband = rdev->wiphy.bands[band];
|
2010-10-04 19:36:35 +00:00
|
|
|
if (sband == NULL)
|
|
|
|
return -EINVAL;
|
2014-01-24 09:17:47 +00:00
|
|
|
err = nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates),
|
|
|
|
nla_len(tx_rates), nl80211_txattr_policy);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2009-12-29 10:59:45 +00:00
|
|
|
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]));
|
2012-04-20 13:42:58 +00:00
|
|
|
if ((mask.control[band].legacy == 0) &&
|
|
|
|
nla_len(tb[NL80211_TXRATE_LEGACY]))
|
|
|
|
return -EINVAL;
|
2012-01-28 16:25:32 +00:00
|
|
|
}
|
2013-12-05 09:02:15 +00:00
|
|
|
if (tb[NL80211_TXRATE_HT]) {
|
2012-01-28 16:25:32 +00:00
|
|
|
if (!ht_rateset_to_mask(
|
|
|
|
sband,
|
2013-12-05 09:02:15 +00:00
|
|
|
nla_data(tb[NL80211_TXRATE_HT]),
|
|
|
|
nla_len(tb[NL80211_TXRATE_HT]),
|
|
|
|
mask.control[band].ht_mcs))
|
2012-01-28 16:25:32 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2013-12-05 19:42:58 +00:00
|
|
|
if (tb[NL80211_TXRATE_VHT]) {
|
|
|
|
if (!vht_set_mcs_mask(
|
|
|
|
sband,
|
|
|
|
nla_data(tb[NL80211_TXRATE_VHT]),
|
|
|
|
mask.control[band].vht_mcs))
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2014-01-08 07:46:02 +00:00
|
|
|
if (tb[NL80211_TXRATE_GI]) {
|
|
|
|
mask.control[band].gi =
|
|
|
|
nla_get_u8(tb[NL80211_TXRATE_GI]);
|
|
|
|
if (mask.control[band].gi > NL80211_TXRATE_FORCE_LGI)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2012-01-28 16:25:32 +00:00
|
|
|
|
|
|
|
if (mask.control[band].legacy == 0) {
|
2013-12-05 19:42:58 +00:00
|
|
|
/* don't allow empty legacy rates if HT or VHT
|
|
|
|
* are not even supported.
|
|
|
|
*/
|
|
|
|
if (!(rdev->wiphy.bands[band]->ht_cap.ht_supported ||
|
|
|
|
rdev->wiphy.bands[band]->vht_cap.vht_supported))
|
2012-01-28 16:25:32 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
|
2013-12-05 09:02:15 +00:00
|
|
|
if (mask.control[band].ht_mcs[i])
|
2013-12-05 19:42:58 +00:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
for (i = 0; i < NL80211_VHT_NSS_MAX; i++)
|
|
|
|
if (mask.control[band].vht_mcs[i])
|
|
|
|
goto out;
|
2012-01-28 16:25:32 +00:00
|
|
|
|
|
|
|
/* legacy and mcs rates may not be both empty */
|
2013-12-05 19:42:58 +00:00
|
|
|
return -EINVAL;
|
2009-12-29 10:59:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-05 09:02:14 +00:00
|
|
|
out:
|
2012-06-27 14:19:42 +00:00
|
|
|
return rdev_set_bitrate_mask(rdev, dev, NULL, &mask);
|
2009-12-29 10:59:45 +00:00
|
|
|
}
|
|
|
|
|
2010-08-12 13:38:38 +00:00
|
|
|
static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info)
|
2010-02-15 10:53:10 +00:00
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
2012-06-15 13:30:18 +00:00
|
|
|
struct wireless_dev *wdev = info->user_ptr[1];
|
2010-08-12 13:38:38 +00:00
|
|
|
u16 frame_type = IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION;
|
2010-02-15 10:53:10 +00:00
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_FRAME_MATCH])
|
|
|
|
return -EINVAL;
|
|
|
|
|
2010-08-12 13:38:38 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_FRAME_TYPE])
|
|
|
|
frame_type = nla_get_u16(info->attrs[NL80211_ATTR_FRAME_TYPE]);
|
2010-02-15 10:53:10 +00:00
|
|
|
|
2012-06-15 13:30:18 +00:00
|
|
|
switch (wdev->iftype) {
|
|
|
|
case NL80211_IFTYPE_STATION:
|
|
|
|
case NL80211_IFTYPE_ADHOC:
|
|
|
|
case NL80211_IFTYPE_P2P_CLIENT:
|
|
|
|
case NL80211_IFTYPE_AP:
|
|
|
|
case NL80211_IFTYPE_AP_VLAN:
|
|
|
|
case NL80211_IFTYPE_MESH_POINT:
|
|
|
|
case NL80211_IFTYPE_P2P_GO:
|
2012-06-15 22:19:54 +00:00
|
|
|
case NL80211_IFTYPE_P2P_DEVICE:
|
2012-06-15 13:30:18 +00:00
|
|
|
break;
|
|
|
|
default:
|
2010-10-04 19:36:35 +00:00
|
|
|
return -EOPNOTSUPP;
|
2012-06-15 13:30:18 +00:00
|
|
|
}
|
2010-02-15 10:53:10 +00:00
|
|
|
|
|
|
|
/* not much point in registering if we can't reply */
|
2010-10-04 19:36:35 +00:00
|
|
|
if (!rdev->ops->mgmt_tx)
|
|
|
|
return -EOPNOTSUPP;
|
2010-02-15 10:53:10 +00:00
|
|
|
|
2012-09-07 20:12:54 +00:00
|
|
|
return cfg80211_mlme_register_mgmt(wdev, info->snd_portid, frame_type,
|
2010-02-15 10:53:10 +00:00
|
|
|
nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]),
|
|
|
|
nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH]));
|
|
|
|
}
|
|
|
|
|
2010-08-12 13:38:38 +00:00
|
|
|
static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
|
2010-02-15 10:53:10 +00:00
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
2012-06-15 13:30:18 +00:00
|
|
|
struct wireless_dev *wdev = info->user_ptr[1];
|
2012-11-08 20:25:48 +00:00
|
|
|
struct cfg80211_chan_def chandef;
|
2010-02-15 10:53:10 +00:00
|
|
|
int err;
|
2011-11-10 08:44:46 +00:00
|
|
|
void *hdr = NULL;
|
2010-02-15 10:53:10 +00:00
|
|
|
u64 cookie;
|
2011-11-04 10:18:21 +00:00
|
|
|
struct sk_buff *msg = NULL;
|
2013-11-18 17:06:49 +00:00
|
|
|
struct cfg80211_mgmt_tx_params params = {
|
|
|
|
.dont_wait_for_ack =
|
|
|
|
info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK],
|
|
|
|
};
|
2010-02-15 10:53:10 +00:00
|
|
|
|
2012-11-08 20:25:48 +00:00
|
|
|
if (!info->attrs[NL80211_ATTR_FRAME])
|
2010-02-15 10:53:10 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (!rdev->ops->mgmt_tx)
|
|
|
|
return -EOPNOTSUPP;
|
2010-02-15 10:53:10 +00:00
|
|
|
|
2012-06-15 13:30:18 +00:00
|
|
|
switch (wdev->iftype) {
|
2013-06-11 12:20:03 +00:00
|
|
|
case NL80211_IFTYPE_P2P_DEVICE:
|
|
|
|
if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
|
|
|
|
return -EINVAL;
|
2012-06-15 13:30:18 +00:00
|
|
|
case NL80211_IFTYPE_STATION:
|
|
|
|
case NL80211_IFTYPE_ADHOC:
|
|
|
|
case NL80211_IFTYPE_P2P_CLIENT:
|
|
|
|
case NL80211_IFTYPE_AP:
|
|
|
|
case NL80211_IFTYPE_AP_VLAN:
|
|
|
|
case NL80211_IFTYPE_MESH_POINT:
|
|
|
|
case NL80211_IFTYPE_P2P_GO:
|
|
|
|
break;
|
|
|
|
default:
|
2010-10-04 19:36:35 +00:00
|
|
|
return -EOPNOTSUPP;
|
2012-06-15 13:30:18 +00:00
|
|
|
}
|
2010-02-15 10:53:10 +00:00
|
|
|
|
2010-11-25 09:02:29 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_DURATION]) {
|
2011-11-18 14:33:48 +00:00
|
|
|
if (!(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX))
|
2010-11-25 09:02:29 +00:00
|
|
|
return -EINVAL;
|
2013-11-18 17:06:49 +00:00
|
|
|
params.wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
|
2012-06-01 10:50:54 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We should wait on the channel for at least a minimum amount
|
|
|
|
* of time (10ms) but no longer than the driver supports.
|
|
|
|
*/
|
2013-11-18 17:06:49 +00:00
|
|
|
if (params.wait < NL80211_MIN_REMAIN_ON_CHANNEL_TIME ||
|
|
|
|
params.wait > rdev->wiphy.max_remain_on_channel_duration)
|
2012-06-01 10:50:54 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2010-11-25 09:02:29 +00:00
|
|
|
}
|
|
|
|
|
2013-11-18 17:06:49 +00:00
|
|
|
params.offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK];
|
2010-11-25 09:02:29 +00:00
|
|
|
|
2013-11-18 17:06:49 +00:00
|
|
|
if (params.offchan && !(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX))
|
2011-11-18 14:33:48 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
2013-11-18 17:06:49 +00:00
|
|
|
params.no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
|
2011-09-25 09:23:30 +00:00
|
|
|
|
2013-06-11 12:20:03 +00:00
|
|
|
/* get the channel if any has been specified, otherwise pass NULL to
|
|
|
|
* the driver. The latter will use the current one
|
|
|
|
*/
|
|
|
|
chandef.chan = NULL;
|
|
|
|
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
|
|
|
|
err = nl80211_parse_chandef(rdev, info, &chandef);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2013-11-18 17:06:49 +00:00
|
|
|
if (!chandef.chan && params.offchan)
|
2013-06-11 12:20:03 +00:00
|
|
|
return -EINVAL;
|
2010-02-15 10:53:10 +00:00
|
|
|
|
2014-05-09 11:11:44 +00:00
|
|
|
params.buf = nla_data(info->attrs[NL80211_ATTR_FRAME]);
|
|
|
|
params.len = nla_len(info->attrs[NL80211_ATTR_FRAME]);
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_CSA_C_OFFSETS_TX]) {
|
|
|
|
int len = nla_len(info->attrs[NL80211_ATTR_CSA_C_OFFSETS_TX]);
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (len % sizeof(u16))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
params.n_csa_offsets = len / sizeof(u16);
|
|
|
|
params.csa_offsets =
|
|
|
|
nla_data(info->attrs[NL80211_ATTR_CSA_C_OFFSETS_TX]);
|
|
|
|
|
|
|
|
/* check that all the offsets fit the frame */
|
|
|
|
for (i = 0; i < params.n_csa_offsets; i++) {
|
|
|
|
if (params.csa_offsets[i] >= params.len)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-18 17:06:49 +00:00
|
|
|
if (!params.dont_wait_for_ack) {
|
2011-11-04 10:18:21 +00:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
|
|
if (!msg)
|
|
|
|
return -ENOMEM;
|
2010-02-15 10:53:10 +00:00
|
|
|
|
2012-09-07 20:12:54 +00:00
|
|
|
hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
|
2011-11-04 10:18:21 +00:00
|
|
|
NL80211_CMD_FRAME);
|
2013-08-14 11:50:01 +00:00
|
|
|
if (!hdr) {
|
|
|
|
err = -ENOBUFS;
|
2011-11-04 10:18:21 +00:00
|
|
|
goto free_msg;
|
|
|
|
}
|
2010-02-15 10:53:10 +00:00
|
|
|
}
|
2011-11-04 10:18:21 +00:00
|
|
|
|
2013-11-18 17:06:49 +00:00
|
|
|
params.chan = chandef.chan;
|
|
|
|
err = cfg80211_mlme_mgmt_tx(rdev, wdev, ¶ms, &cookie);
|
2010-02-15 10:53:10 +00:00
|
|
|
if (err)
|
|
|
|
goto free_msg;
|
|
|
|
|
2011-11-04 10:18:21 +00:00
|
|
|
if (msg) {
|
2012-03-29 08:41:26 +00:00
|
|
|
if (nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie))
|
|
|
|
goto nla_put_failure;
|
2010-02-15 10:53:10 +00:00
|
|
|
|
2011-11-04 10:18:21 +00:00
|
|
|
genlmsg_end(msg, hdr);
|
|
|
|
return genlmsg_reply(msg, info);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2010-02-15 10:53:10 +00:00
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
err = -ENOBUFS;
|
|
|
|
free_msg:
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2010-11-25 09:02:29 +00:00
|
|
|
static int nl80211_tx_mgmt_cancel_wait(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
2012-06-15 13:30:18 +00:00
|
|
|
struct wireless_dev *wdev = info->user_ptr[1];
|
2010-11-25 09:02:29 +00:00
|
|
|
u64 cookie;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_COOKIE])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!rdev->ops->mgmt_tx_cancel_wait)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2012-06-15 13:30:18 +00:00
|
|
|
switch (wdev->iftype) {
|
|
|
|
case NL80211_IFTYPE_STATION:
|
|
|
|
case NL80211_IFTYPE_ADHOC:
|
|
|
|
case NL80211_IFTYPE_P2P_CLIENT:
|
|
|
|
case NL80211_IFTYPE_AP:
|
|
|
|
case NL80211_IFTYPE_AP_VLAN:
|
|
|
|
case NL80211_IFTYPE_P2P_GO:
|
2012-06-15 22:19:54 +00:00
|
|
|
case NL80211_IFTYPE_P2P_DEVICE:
|
2012-06-15 13:30:18 +00:00
|
|
|
break;
|
|
|
|
default:
|
2010-11-25 09:02:29 +00:00
|
|
|
return -EOPNOTSUPP;
|
2012-06-15 13:30:18 +00:00
|
|
|
}
|
2010-11-25 09:02:29 +00:00
|
|
|
|
|
|
|
cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
|
|
|
|
|
2012-06-27 14:19:42 +00:00
|
|
|
return rdev_mgmt_tx_cancel_wait(rdev, wdev, cookie);
|
2010-11-25 09:02:29 +00:00
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
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
|
|
|
struct wireless_dev *wdev;
|
2010-10-04 19:36:35 +00:00
|
|
|
struct net_device *dev = info->user_ptr[1];
|
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
|
|
|
u8 ps_state;
|
|
|
|
bool state;
|
|
|
|
int err;
|
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (!info->attrs[NL80211_ATTR_PS_STATE])
|
|
|
|
return -EINVAL;
|
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
|
|
|
|
|
|
|
ps_state = nla_get_u32(info->attrs[NL80211_ATTR_PS_STATE]);
|
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (ps_state != NL80211_PS_DISABLED && ps_state != NL80211_PS_ENABLED)
|
|
|
|
return -EINVAL;
|
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
|
|
|
|
|
|
|
wdev = dev->ieee80211_ptr;
|
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (!rdev->ops->set_power_mgmt)
|
|
|
|
return -EOPNOTSUPP;
|
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
|
|
|
|
|
|
|
state = (ps_state == NL80211_PS_ENABLED) ? true : false;
|
|
|
|
|
|
|
|
if (state == wdev->ps)
|
2010-10-04 19:36:35 +00:00
|
|
|
return 0;
|
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
|
|
|
|
2012-06-27 14:19:42 +00:00
|
|
|
err = rdev_set_power_mgmt(rdev, dev, state, wdev->ps_timeout);
|
2010-10-04 19:36:35 +00:00
|
|
|
if (!err)
|
|
|
|
wdev->ps = state;
|
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
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
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
|
|
|
enum nl80211_ps_state ps_state;
|
|
|
|
struct wireless_dev *wdev;
|
2010-10-04 19:36:35 +00:00
|
|
|
struct net_device *dev = info->user_ptr[1];
|
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
|
|
|
struct sk_buff *msg;
|
|
|
|
void *hdr;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
wdev = dev->ieee80211_ptr;
|
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (!rdev->ops->set_power_mgmt)
|
|
|
|
return -EOPNOTSUPP;
|
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
|
|
|
|
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
2010-10-04 19:36:35 +00:00
|
|
|
if (!msg)
|
|
|
|
return -ENOMEM;
|
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
|
|
|
|
2012-09-07 20:12:54 +00:00
|
|
|
hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
|
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_CMD_GET_POWER_SAVE);
|
|
|
|
if (!hdr) {
|
2010-10-04 19:36:35 +00:00
|
|
|
err = -ENOBUFS;
|
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
|
|
|
goto free_msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wdev->ps)
|
|
|
|
ps_state = NL80211_PS_ENABLED;
|
|
|
|
else
|
|
|
|
ps_state = NL80211_PS_DISABLED;
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_PS_STATE, ps_state))
|
|
|
|
goto nla_put_failure;
|
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
|
|
|
|
|
|
|
genlmsg_end(msg, hdr);
|
2010-10-04 19:36:35 +00:00
|
|
|
return genlmsg_reply(msg, info);
|
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
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
nla_put_failure:
|
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
|
|
|
err = -ENOBUFS;
|
2010-10-04 19:36:35 +00:00
|
|
|
free_msg:
|
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
|
|
|
nlmsg_free(msg);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2014-01-20 22:58:15 +00:00
|
|
|
static const struct nla_policy
|
|
|
|
nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] = {
|
2010-03-23 07:02:33 +00:00
|
|
|
[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 },
|
2012-07-12 23:17:33 +00:00
|
|
|
[NL80211_ATTR_CQM_TXE_RATE] = { .type = NLA_U32 },
|
|
|
|
[NL80211_ATTR_CQM_TXE_PKTS] = { .type = NLA_U32 },
|
|
|
|
[NL80211_ATTR_CQM_TXE_INTVL] = { .type = NLA_U32 },
|
2010-03-23 07:02:33 +00:00
|
|
|
};
|
|
|
|
|
2012-07-12 23:17:33 +00:00
|
|
|
static int nl80211_set_cqm_txe(struct genl_info *info,
|
2012-11-26 11:51:52 +00:00
|
|
|
u32 rate, u32 pkts, u32 intvl)
|
2012-07-12 23:17:33 +00:00
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
2013-08-06 12:10:48 +00:00
|
|
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
2012-07-12 23:17:33 +00:00
|
|
|
|
2012-11-26 11:51:52 +00:00
|
|
|
if (rate > 100 || intvl > NL80211_CQM_TXE_MAX_INTVL)
|
2012-07-12 23:17:33 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!rdev->ops->set_cqm_txe_config)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (wdev->iftype != NL80211_IFTYPE_STATION &&
|
|
|
|
wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2012-06-27 14:19:42 +00:00
|
|
|
return rdev_set_cqm_txe_config(rdev, dev, rate, pkts, intvl);
|
2012-07-12 23:17:33 +00:00
|
|
|
}
|
|
|
|
|
2010-03-23 07:02:33 +00:00
|
|
|
static int nl80211_set_cqm_rssi(struct genl_info *info,
|
|
|
|
s32 threshold, u32 hysteresis)
|
|
|
|
{
|
2010-10-04 19:36:35 +00:00
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
2013-08-06 12:10:48 +00:00
|
|
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
2010-03-23 07:02:33 +00:00
|
|
|
|
|
|
|
if (threshold > 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2013-08-06 12:10:48 +00:00
|
|
|
/* disabling - hysteresis should also be zero then */
|
|
|
|
if (threshold == 0)
|
|
|
|
hysteresis = 0;
|
2010-03-23 07:02:33 +00:00
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (!rdev->ops->set_cqm_rssi_config)
|
|
|
|
return -EOPNOTSUPP;
|
2010-03-23 07:02:33 +00:00
|
|
|
|
2010-09-16 12:58:22 +00:00
|
|
|
if (wdev->iftype != NL80211_IFTYPE_STATION &&
|
2010-10-04 19:36:35 +00:00
|
|
|
wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
|
|
|
|
return -EOPNOTSUPP;
|
2010-03-23 07:02:33 +00:00
|
|
|
|
2012-06-27 14:19:42 +00:00
|
|
|
return rdev_set_cqm_rssi_config(rdev, dev, threshold, hysteresis);
|
2010-03-23 07:02:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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];
|
2013-08-06 12:10:48 +00:00
|
|
|
if (!cqm)
|
|
|
|
return -EINVAL;
|
2010-03-23 07:02:33 +00:00
|
|
|
|
|
|
|
err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm,
|
|
|
|
nl80211_attr_cqm_policy);
|
|
|
|
if (err)
|
2013-08-06 12:10:48 +00:00
|
|
|
return err;
|
2010-03-23 07:02:33 +00:00
|
|
|
|
|
|
|
if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] &&
|
|
|
|
attrs[NL80211_ATTR_CQM_RSSI_HYST]) {
|
2013-08-06 12:10:48 +00:00
|
|
|
s32 threshold = nla_get_s32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
|
|
|
|
u32 hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]);
|
2010-03-23 07:02:33 +00:00
|
|
|
|
2013-08-06 12:10:48 +00:00
|
|
|
return nl80211_set_cqm_rssi(info, threshold, hysteresis);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (attrs[NL80211_ATTR_CQM_TXE_RATE] &&
|
|
|
|
attrs[NL80211_ATTR_CQM_TXE_PKTS] &&
|
|
|
|
attrs[NL80211_ATTR_CQM_TXE_INTVL]) {
|
|
|
|
u32 rate = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_RATE]);
|
|
|
|
u32 pkts = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_PKTS]);
|
|
|
|
u32 intvl = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_INTVL]);
|
|
|
|
|
|
|
|
return nl80211_set_cqm_txe(info, rate, pkts, intvl);
|
|
|
|
}
|
|
|
|
|
|
|
|
return -EINVAL;
|
2010-03-23 07:02:33 +00:00
|
|
|
}
|
|
|
|
|
2014-11-03 09:33:18 +00:00
|
|
|
static int nl80211_join_ocb(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
|
|
|
struct ocb_setup setup = {};
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = nl80211_parse_chandef(rdev, info, &setup.chandef);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
return cfg80211_join_ocb(rdev, dev, &setup);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_leave_ocb(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
|
|
|
|
|
|
|
return cfg80211_leave_ocb(rdev, dev);
|
|
|
|
}
|
|
|
|
|
2010-12-03 08:20:44 +00:00
|
|
|
static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
|
|
|
struct mesh_config cfg;
|
2010-12-17 01:37:49 +00:00
|
|
|
struct mesh_setup setup;
|
2010-12-03 08:20:44 +00:00
|
|
|
int err;
|
|
|
|
|
|
|
|
/* start with default */
|
|
|
|
memcpy(&cfg, &default_mesh_config, sizeof(cfg));
|
2010-12-17 01:37:49 +00:00
|
|
|
memcpy(&setup, &default_mesh_setup, sizeof(setup));
|
2010-12-03 08:20:44 +00:00
|
|
|
|
2010-12-17 01:37:48 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_MESH_CONFIG]) {
|
2010-12-03 08:20:44 +00:00
|
|
|
/* and parse parameters if given */
|
2010-12-17 01:37:48 +00:00
|
|
|
err = nl80211_parse_mesh_config(info, &cfg, NULL);
|
2010-12-03 08:20:44 +00:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_MESH_ID] ||
|
|
|
|
!nla_len(info->attrs[NL80211_ATTR_MESH_ID]))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2010-12-17 01:37:49 +00:00
|
|
|
setup.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
|
|
|
|
setup.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
|
|
|
|
|
2011-11-25 01:15:20 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_MCAST_RATE] &&
|
|
|
|
!nl80211_parse_mcast_rate(rdev, setup.mcast_rate,
|
|
|
|
nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE])))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2013-01-07 15:04:51 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
|
|
|
|
setup.beacon_interval =
|
|
|
|
nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
|
|
|
|
if (setup.beacon_interval < 10 ||
|
|
|
|
setup.beacon_interval > 10000)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_DTIM_PERIOD]) {
|
|
|
|
setup.dtim_period =
|
|
|
|
nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
|
|
|
|
if (setup.dtim_period < 1 || setup.dtim_period > 100)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2010-12-17 01:37:49 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_MESH_SETUP]) {
|
|
|
|
/* parse additional setup parameters if given */
|
|
|
|
err = nl80211_parse_mesh_setup(info, &setup);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2013-03-04 21:06:13 +00:00
|
|
|
if (setup.user_mpm)
|
|
|
|
cfg.auto_open_plinks = false;
|
|
|
|
|
2012-05-16 21:50:20 +00:00
|
|
|
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
|
2012-11-08 20:25:48 +00:00
|
|
|
err = nl80211_parse_chandef(rdev, info, &setup.chandef);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2012-05-16 21:50:20 +00:00
|
|
|
} else {
|
|
|
|
/* cfg80211_join_mesh() will sort it out */
|
2012-11-08 20:25:48 +00:00
|
|
|
setup.chandef.chan = NULL;
|
2012-05-16 21:50:20 +00:00
|
|
|
}
|
|
|
|
|
2013-06-03 17:33:36 +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;
|
|
|
|
|
|
|
|
if (!setup.chandef.chan)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
sband = rdev->wiphy.bands[setup.chandef.chan->band];
|
|
|
|
|
|
|
|
err = ieee80211_get_ratemask(sband, rates, n_rates,
|
|
|
|
&setup.basic_rates);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2010-12-17 01:37:49 +00:00
|
|
|
return cfg80211_join_mesh(rdev, dev, &setup, &cfg);
|
2010-12-03 08:20:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_leave_mesh(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
|
|
|
|
|
|
|
return cfg80211_leave_mesh(rdev, dev);
|
|
|
|
}
|
|
|
|
|
2012-06-27 07:23:48 +00:00
|
|
|
#ifdef CONFIG_PM
|
2013-02-12 20:16:26 +00:00
|
|
|
static int nl80211_send_wowlan_patterns(struct sk_buff *msg,
|
|
|
|
struct cfg80211_registered_device *rdev)
|
|
|
|
{
|
2013-05-15 07:30:07 +00:00
|
|
|
struct cfg80211_wowlan *wowlan = rdev->wiphy.wowlan_config;
|
2013-02-12 20:16:26 +00:00
|
|
|
struct nlattr *nl_pats, *nl_pat;
|
|
|
|
int i, pat_len;
|
|
|
|
|
2013-05-15 07:30:07 +00:00
|
|
|
if (!wowlan->n_patterns)
|
2013-02-12 20:16:26 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
nl_pats = nla_nest_start(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN);
|
|
|
|
if (!nl_pats)
|
|
|
|
return -ENOBUFS;
|
|
|
|
|
2013-05-15 07:30:07 +00:00
|
|
|
for (i = 0; i < wowlan->n_patterns; i++) {
|
2013-02-12 20:16:26 +00:00
|
|
|
nl_pat = nla_nest_start(msg, i + 1);
|
|
|
|
if (!nl_pat)
|
|
|
|
return -ENOBUFS;
|
2013-05-15 07:30:07 +00:00
|
|
|
pat_len = wowlan->patterns[i].pattern_len;
|
2013-06-26 02:03:56 +00:00
|
|
|
if (nla_put(msg, NL80211_PKTPAT_MASK, DIV_ROUND_UP(pat_len, 8),
|
2013-05-15 07:30:07 +00:00
|
|
|
wowlan->patterns[i].mask) ||
|
2013-06-26 02:03:56 +00:00
|
|
|
nla_put(msg, NL80211_PKTPAT_PATTERN, pat_len,
|
|
|
|
wowlan->patterns[i].pattern) ||
|
|
|
|
nla_put_u32(msg, NL80211_PKTPAT_OFFSET,
|
2013-05-15 07:30:07 +00:00
|
|
|
wowlan->patterns[i].pkt_offset))
|
2013-02-12 20:16:26 +00:00
|
|
|
return -ENOBUFS;
|
|
|
|
nla_nest_end(msg, nl_pat);
|
|
|
|
}
|
|
|
|
nla_nest_end(msg, nl_pats);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-01-23 21:57:40 +00:00
|
|
|
static int nl80211_send_wowlan_tcp(struct sk_buff *msg,
|
|
|
|
struct cfg80211_wowlan_tcp *tcp)
|
|
|
|
{
|
|
|
|
struct nlattr *nl_tcp;
|
|
|
|
|
|
|
|
if (!tcp)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
nl_tcp = nla_nest_start(msg, NL80211_WOWLAN_TRIG_TCP_CONNECTION);
|
|
|
|
if (!nl_tcp)
|
|
|
|
return -ENOBUFS;
|
|
|
|
|
|
|
|
if (nla_put_be32(msg, NL80211_WOWLAN_TCP_SRC_IPV4, tcp->src) ||
|
|
|
|
nla_put_be32(msg, NL80211_WOWLAN_TCP_DST_IPV4, tcp->dst) ||
|
|
|
|
nla_put(msg, NL80211_WOWLAN_TCP_DST_MAC, ETH_ALEN, tcp->dst_mac) ||
|
|
|
|
nla_put_u16(msg, NL80211_WOWLAN_TCP_SRC_PORT, tcp->src_port) ||
|
|
|
|
nla_put_u16(msg, NL80211_WOWLAN_TCP_DST_PORT, tcp->dst_port) ||
|
|
|
|
nla_put(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD,
|
|
|
|
tcp->payload_len, tcp->payload) ||
|
|
|
|
nla_put_u32(msg, NL80211_WOWLAN_TCP_DATA_INTERVAL,
|
|
|
|
tcp->data_interval) ||
|
|
|
|
nla_put(msg, NL80211_WOWLAN_TCP_WAKE_PAYLOAD,
|
|
|
|
tcp->wake_len, tcp->wake_data) ||
|
|
|
|
nla_put(msg, NL80211_WOWLAN_TCP_WAKE_MASK,
|
|
|
|
DIV_ROUND_UP(tcp->wake_len, 8), tcp->wake_mask))
|
|
|
|
return -ENOBUFS;
|
|
|
|
|
|
|
|
if (tcp->payload_seq.len &&
|
|
|
|
nla_put(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ,
|
|
|
|
sizeof(tcp->payload_seq), &tcp->payload_seq))
|
|
|
|
return -ENOBUFS;
|
|
|
|
|
|
|
|
if (tcp->payload_tok.len &&
|
|
|
|
nla_put(msg, NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN,
|
|
|
|
sizeof(tcp->payload_tok) + tcp->tokens_size,
|
|
|
|
&tcp->payload_tok))
|
|
|
|
return -ENOBUFS;
|
|
|
|
|
2013-05-16 08:24:28 +00:00
|
|
|
nla_nest_end(msg, nl_tcp);
|
|
|
|
|
2013-01-23 21:57:40 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-05-04 13:37:28 +00:00
|
|
|
static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct sk_buff *msg;
|
|
|
|
void *hdr;
|
2013-01-23 21:57:40 +00:00
|
|
|
u32 size = NLMSG_DEFAULT_SIZE;
|
2011-05-04 13:37:28 +00:00
|
|
|
|
2013-06-03 15:25:34 +00:00
|
|
|
if (!rdev->wiphy.wowlan)
|
2011-05-04 13:37:28 +00:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2013-05-15 07:30:07 +00:00
|
|
|
if (rdev->wiphy.wowlan_config && rdev->wiphy.wowlan_config->tcp) {
|
2013-01-23 21:57:40 +00:00
|
|
|
/* adjust size to have room for all the data */
|
2013-05-15 07:30:07 +00:00
|
|
|
size += rdev->wiphy.wowlan_config->tcp->tokens_size +
|
|
|
|
rdev->wiphy.wowlan_config->tcp->payload_len +
|
|
|
|
rdev->wiphy.wowlan_config->tcp->wake_len +
|
|
|
|
rdev->wiphy.wowlan_config->tcp->wake_len / 8;
|
2013-01-23 21:57:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
msg = nlmsg_new(size, GFP_KERNEL);
|
2011-05-04 13:37:28 +00:00
|
|
|
if (!msg)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2012-09-07 20:12:54 +00:00
|
|
|
hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
|
2011-05-04 13:37:28 +00:00
|
|
|
NL80211_CMD_GET_WOWLAN);
|
|
|
|
if (!hdr)
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
2013-05-15 07:30:07 +00:00
|
|
|
if (rdev->wiphy.wowlan_config) {
|
2011-05-04 13:37:28 +00:00
|
|
|
struct nlattr *nl_wowlan;
|
|
|
|
|
|
|
|
nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS);
|
|
|
|
if (!nl_wowlan)
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
2013-05-15 07:30:07 +00:00
|
|
|
if ((rdev->wiphy.wowlan_config->any &&
|
2012-03-29 08:41:26 +00:00
|
|
|
nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) ||
|
2013-05-15 07:30:07 +00:00
|
|
|
(rdev->wiphy.wowlan_config->disconnect &&
|
2012-03-29 08:41:26 +00:00
|
|
|
nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) ||
|
2013-05-15 07:30:07 +00:00
|
|
|
(rdev->wiphy.wowlan_config->magic_pkt &&
|
2012-03-29 08:41:26 +00:00
|
|
|
nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) ||
|
2013-05-15 07:30:07 +00:00
|
|
|
(rdev->wiphy.wowlan_config->gtk_rekey_failure &&
|
2012-03-29 08:41:26 +00:00
|
|
|
nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) ||
|
2013-05-15 07:30:07 +00:00
|
|
|
(rdev->wiphy.wowlan_config->eap_identity_req &&
|
2012-03-29 08:41:26 +00:00
|
|
|
nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) ||
|
2013-05-15 07:30:07 +00:00
|
|
|
(rdev->wiphy.wowlan_config->four_way_handshake &&
|
2012-03-29 08:41:26 +00:00
|
|
|
nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) ||
|
2013-05-15 07:30:07 +00:00
|
|
|
(rdev->wiphy.wowlan_config->rfkill_release &&
|
2012-03-29 08:41:26 +00:00
|
|
|
nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE)))
|
|
|
|
goto nla_put_failure;
|
2013-01-23 21:57:40 +00:00
|
|
|
|
2013-02-12 20:16:26 +00:00
|
|
|
if (nl80211_send_wowlan_patterns(msg, rdev))
|
|
|
|
goto nla_put_failure;
|
2013-01-23 21:57:40 +00:00
|
|
|
|
2013-05-15 07:30:07 +00:00
|
|
|
if (nl80211_send_wowlan_tcp(msg,
|
|
|
|
rdev->wiphy.wowlan_config->tcp))
|
2013-01-23 21:57:40 +00:00
|
|
|
goto nla_put_failure;
|
|
|
|
|
2011-05-04 13:37:28 +00:00
|
|
|
nla_nest_end(msg, nl_wowlan);
|
|
|
|
}
|
|
|
|
|
|
|
|
genlmsg_end(msg, hdr);
|
|
|
|
return genlmsg_reply(msg, info);
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return -ENOBUFS;
|
|
|
|
}
|
|
|
|
|
2013-01-23 21:57:40 +00:00
|
|
|
static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev,
|
|
|
|
struct nlattr *attr,
|
|
|
|
struct cfg80211_wowlan *trig)
|
|
|
|
{
|
|
|
|
struct nlattr *tb[NUM_NL80211_WOWLAN_TCP];
|
|
|
|
struct cfg80211_wowlan_tcp *cfg;
|
|
|
|
struct nl80211_wowlan_tcp_data_token *tok = NULL;
|
|
|
|
struct nl80211_wowlan_tcp_data_seq *seq = NULL;
|
|
|
|
u32 size;
|
|
|
|
u32 data_size, wake_size, tokens_size = 0, wake_mask_size;
|
|
|
|
int err, port;
|
|
|
|
|
2013-06-03 15:25:34 +00:00
|
|
|
if (!rdev->wiphy.wowlan->tcp)
|
2013-01-23 21:57:40 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
err = nla_parse(tb, MAX_NL80211_WOWLAN_TCP,
|
|
|
|
nla_data(attr), nla_len(attr),
|
|
|
|
nl80211_wowlan_tcp_policy);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (!tb[NL80211_WOWLAN_TCP_SRC_IPV4] ||
|
|
|
|
!tb[NL80211_WOWLAN_TCP_DST_IPV4] ||
|
|
|
|
!tb[NL80211_WOWLAN_TCP_DST_MAC] ||
|
|
|
|
!tb[NL80211_WOWLAN_TCP_DST_PORT] ||
|
|
|
|
!tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD] ||
|
|
|
|
!tb[NL80211_WOWLAN_TCP_DATA_INTERVAL] ||
|
|
|
|
!tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD] ||
|
|
|
|
!tb[NL80211_WOWLAN_TCP_WAKE_MASK])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
data_size = nla_len(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD]);
|
2013-06-03 15:25:34 +00:00
|
|
|
if (data_size > rdev->wiphy.wowlan->tcp->data_payload_max)
|
2013-01-23 21:57:40 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) >
|
2013-06-03 15:25:34 +00:00
|
|
|
rdev->wiphy.wowlan->tcp->data_interval_max ||
|
2013-02-26 12:56:40 +00:00
|
|
|
nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) == 0)
|
2013-01-23 21:57:40 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
wake_size = nla_len(tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD]);
|
2013-06-03 15:25:34 +00:00
|
|
|
if (wake_size > rdev->wiphy.wowlan->tcp->wake_payload_max)
|
2013-01-23 21:57:40 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
wake_mask_size = nla_len(tb[NL80211_WOWLAN_TCP_WAKE_MASK]);
|
|
|
|
if (wake_mask_size != DIV_ROUND_UP(wake_size, 8))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN]) {
|
|
|
|
u32 tokln = nla_len(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN]);
|
|
|
|
|
|
|
|
tok = nla_data(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN]);
|
|
|
|
tokens_size = tokln - sizeof(*tok);
|
|
|
|
|
|
|
|
if (!tok->len || tokens_size % tok->len)
|
|
|
|
return -EINVAL;
|
2013-06-03 15:25:34 +00:00
|
|
|
if (!rdev->wiphy.wowlan->tcp->tok)
|
2013-01-23 21:57:40 +00:00
|
|
|
return -EINVAL;
|
2013-06-03 15:25:34 +00:00
|
|
|
if (tok->len > rdev->wiphy.wowlan->tcp->tok->max_len)
|
2013-01-23 21:57:40 +00:00
|
|
|
return -EINVAL;
|
2013-06-03 15:25:34 +00:00
|
|
|
if (tok->len < rdev->wiphy.wowlan->tcp->tok->min_len)
|
2013-01-23 21:57:40 +00:00
|
|
|
return -EINVAL;
|
2013-06-03 15:25:34 +00:00
|
|
|
if (tokens_size > rdev->wiphy.wowlan->tcp->tok->bufsize)
|
2013-01-23 21:57:40 +00:00
|
|
|
return -EINVAL;
|
|
|
|
if (tok->offset + tok->len > data_size)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ]) {
|
|
|
|
seq = nla_data(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ]);
|
2013-06-03 15:25:34 +00:00
|
|
|
if (!rdev->wiphy.wowlan->tcp->seq)
|
2013-01-23 21:57:40 +00:00
|
|
|
return -EINVAL;
|
|
|
|
if (seq->len == 0 || seq->len > 4)
|
|
|
|
return -EINVAL;
|
|
|
|
if (seq->len + seq->offset > data_size)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
size = sizeof(*cfg);
|
|
|
|
size += data_size;
|
|
|
|
size += wake_size + wake_mask_size;
|
|
|
|
size += tokens_size;
|
|
|
|
|
|
|
|
cfg = kzalloc(size, GFP_KERNEL);
|
|
|
|
if (!cfg)
|
|
|
|
return -ENOMEM;
|
|
|
|
cfg->src = nla_get_be32(tb[NL80211_WOWLAN_TCP_SRC_IPV4]);
|
|
|
|
cfg->dst = nla_get_be32(tb[NL80211_WOWLAN_TCP_DST_IPV4]);
|
|
|
|
memcpy(cfg->dst_mac, nla_data(tb[NL80211_WOWLAN_TCP_DST_MAC]),
|
|
|
|
ETH_ALEN);
|
|
|
|
if (tb[NL80211_WOWLAN_TCP_SRC_PORT])
|
|
|
|
port = nla_get_u16(tb[NL80211_WOWLAN_TCP_SRC_PORT]);
|
|
|
|
else
|
|
|
|
port = 0;
|
|
|
|
#ifdef CONFIG_INET
|
|
|
|
/* allocate a socket and port for it and use it */
|
|
|
|
err = __sock_create(wiphy_net(&rdev->wiphy), PF_INET, SOCK_STREAM,
|
|
|
|
IPPROTO_TCP, &cfg->sock, 1);
|
|
|
|
if (err) {
|
|
|
|
kfree(cfg);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
if (inet_csk_get_port(cfg->sock->sk, port)) {
|
|
|
|
sock_release(cfg->sock);
|
|
|
|
kfree(cfg);
|
|
|
|
return -EADDRINUSE;
|
|
|
|
}
|
|
|
|
cfg->src_port = inet_sk(cfg->sock->sk)->inet_num;
|
|
|
|
#else
|
|
|
|
if (!port) {
|
|
|
|
kfree(cfg);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
cfg->src_port = port;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
cfg->dst_port = nla_get_u16(tb[NL80211_WOWLAN_TCP_DST_PORT]);
|
|
|
|
cfg->payload_len = data_size;
|
|
|
|
cfg->payload = (u8 *)cfg + sizeof(*cfg) + tokens_size;
|
|
|
|
memcpy((void *)cfg->payload,
|
|
|
|
nla_data(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD]),
|
|
|
|
data_size);
|
|
|
|
if (seq)
|
|
|
|
cfg->payload_seq = *seq;
|
|
|
|
cfg->data_interval = nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]);
|
|
|
|
cfg->wake_len = wake_size;
|
|
|
|
cfg->wake_data = (u8 *)cfg + sizeof(*cfg) + tokens_size + data_size;
|
|
|
|
memcpy((void *)cfg->wake_data,
|
|
|
|
nla_data(tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD]),
|
|
|
|
wake_size);
|
|
|
|
cfg->wake_mask = (u8 *)cfg + sizeof(*cfg) + tokens_size +
|
|
|
|
data_size + wake_size;
|
|
|
|
memcpy((void *)cfg->wake_mask,
|
|
|
|
nla_data(tb[NL80211_WOWLAN_TCP_WAKE_MASK]),
|
|
|
|
wake_mask_size);
|
|
|
|
if (tok) {
|
|
|
|
cfg->tokens_size = tokens_size;
|
|
|
|
memcpy(&cfg->payload_tok, tok, sizeof(*tok) + tokens_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
trig->tcp = cfg;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-05-04 13:37:28 +00:00
|
|
|
static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG];
|
|
|
|
struct cfg80211_wowlan new_triggers = {};
|
2012-07-12 14:25:02 +00:00
|
|
|
struct cfg80211_wowlan *ntrig;
|
2013-06-03 15:25:34 +00:00
|
|
|
const struct wiphy_wowlan_support *wowlan = rdev->wiphy.wowlan;
|
2011-05-04 13:37:28 +00:00
|
|
|
int err, i;
|
2013-05-15 07:30:07 +00:00
|
|
|
bool prev_enabled = rdev->wiphy.wowlan_config;
|
2011-05-04 13:37:28 +00:00
|
|
|
|
2013-06-03 15:25:34 +00:00
|
|
|
if (!wowlan)
|
2011-05-04 13:37:28 +00:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2012-07-12 14:25:02 +00:00
|
|
|
if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) {
|
|
|
|
cfg80211_rdev_free_wowlan(rdev);
|
2013-05-15 07:30:07 +00:00
|
|
|
rdev->wiphy.wowlan_config = NULL;
|
2012-07-12 14:25:02 +00:00
|
|
|
goto set_wakeup;
|
|
|
|
}
|
2011-05-04 13:37:28 +00:00
|
|
|
|
|
|
|
err = nla_parse(tb, MAX_NL80211_WOWLAN_TRIG,
|
|
|
|
nla_data(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
|
|
|
|
nla_len(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
|
|
|
|
nl80211_wowlan_policy);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (tb[NL80211_WOWLAN_TRIG_ANY]) {
|
|
|
|
if (!(wowlan->flags & WIPHY_WOWLAN_ANY))
|
|
|
|
return -EINVAL;
|
|
|
|
new_triggers.any = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tb[NL80211_WOWLAN_TRIG_DISCONNECT]) {
|
|
|
|
if (!(wowlan->flags & WIPHY_WOWLAN_DISCONNECT))
|
|
|
|
return -EINVAL;
|
|
|
|
new_triggers.disconnect = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tb[NL80211_WOWLAN_TRIG_MAGIC_PKT]) {
|
|
|
|
if (!(wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT))
|
|
|
|
return -EINVAL;
|
|
|
|
new_triggers.magic_pkt = true;
|
|
|
|
}
|
|
|
|
|
2011-07-13 08:48:55 +00:00
|
|
|
if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE]) {
|
|
|
|
if (!(wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE))
|
|
|
|
return -EINVAL;
|
|
|
|
new_triggers.gtk_rekey_failure = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tb[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST]) {
|
|
|
|
if (!(wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ))
|
|
|
|
return -EINVAL;
|
|
|
|
new_triggers.eap_identity_req = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tb[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE]) {
|
|
|
|
if (!(wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE))
|
|
|
|
return -EINVAL;
|
|
|
|
new_triggers.four_way_handshake = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tb[NL80211_WOWLAN_TRIG_RFKILL_RELEASE]) {
|
|
|
|
if (!(wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE))
|
|
|
|
return -EINVAL;
|
|
|
|
new_triggers.rfkill_release = true;
|
|
|
|
}
|
|
|
|
|
2011-05-04 13:37:28 +00:00
|
|
|
if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) {
|
|
|
|
struct nlattr *pat;
|
|
|
|
int n_patterns = 0;
|
2013-02-12 20:16:26 +00:00
|
|
|
int rem, pat_len, mask_len, pkt_offset;
|
2013-06-26 02:03:56 +00:00
|
|
|
struct nlattr *pat_tb[NUM_NL80211_PKTPAT];
|
2011-05-04 13:37:28 +00:00
|
|
|
|
|
|
|
nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
|
|
|
|
rem)
|
|
|
|
n_patterns++;
|
|
|
|
if (n_patterns > wowlan->n_patterns)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
new_triggers.patterns = kcalloc(n_patterns,
|
|
|
|
sizeof(new_triggers.patterns[0]),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!new_triggers.patterns)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
new_triggers.n_patterns = n_patterns;
|
|
|
|
i = 0;
|
|
|
|
|
|
|
|
nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
|
|
|
|
rem) {
|
2014-05-19 15:59:50 +00:00
|
|
|
u8 *mask_pat;
|
|
|
|
|
2013-06-26 02:03:56 +00:00
|
|
|
nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat),
|
|
|
|
nla_len(pat), NULL);
|
2011-05-04 13:37:28 +00:00
|
|
|
err = -EINVAL;
|
2013-06-26 02:03:56 +00:00
|
|
|
if (!pat_tb[NL80211_PKTPAT_MASK] ||
|
|
|
|
!pat_tb[NL80211_PKTPAT_PATTERN])
|
2011-05-04 13:37:28 +00:00
|
|
|
goto error;
|
2013-06-26 02:03:56 +00:00
|
|
|
pat_len = nla_len(pat_tb[NL80211_PKTPAT_PATTERN]);
|
2011-05-04 13:37:28 +00:00
|
|
|
mask_len = DIV_ROUND_UP(pat_len, 8);
|
2013-06-26 02:03:56 +00:00
|
|
|
if (nla_len(pat_tb[NL80211_PKTPAT_MASK]) != mask_len)
|
2011-05-04 13:37:28 +00:00
|
|
|
goto error;
|
|
|
|
if (pat_len > wowlan->pattern_max_len ||
|
|
|
|
pat_len < wowlan->pattern_min_len)
|
|
|
|
goto error;
|
|
|
|
|
2013-06-26 02:03:56 +00:00
|
|
|
if (!pat_tb[NL80211_PKTPAT_OFFSET])
|
2013-02-12 20:16:26 +00:00
|
|
|
pkt_offset = 0;
|
|
|
|
else
|
|
|
|
pkt_offset = nla_get_u32(
|
2013-06-26 02:03:56 +00:00
|
|
|
pat_tb[NL80211_PKTPAT_OFFSET]);
|
2013-02-12 20:16:26 +00:00
|
|
|
if (pkt_offset > wowlan->max_pkt_offset)
|
|
|
|
goto error;
|
|
|
|
new_triggers.patterns[i].pkt_offset = pkt_offset;
|
|
|
|
|
2014-05-19 15:59:50 +00:00
|
|
|
mask_pat = kmalloc(mask_len + pat_len, GFP_KERNEL);
|
|
|
|
if (!mask_pat) {
|
2011-05-04 13:37:28 +00:00
|
|
|
err = -ENOMEM;
|
|
|
|
goto error;
|
|
|
|
}
|
2014-05-19 15:59:50 +00:00
|
|
|
new_triggers.patterns[i].mask = mask_pat;
|
|
|
|
memcpy(mask_pat, nla_data(pat_tb[NL80211_PKTPAT_MASK]),
|
2011-05-04 13:37:28 +00:00
|
|
|
mask_len);
|
2014-05-19 15:59:50 +00:00
|
|
|
mask_pat += mask_len;
|
|
|
|
new_triggers.patterns[i].pattern = mask_pat;
|
2011-05-04 13:37:28 +00:00
|
|
|
new_triggers.patterns[i].pattern_len = pat_len;
|
2014-05-19 15:59:50 +00:00
|
|
|
memcpy(mask_pat,
|
2013-06-26 02:03:56 +00:00
|
|
|
nla_data(pat_tb[NL80211_PKTPAT_PATTERN]),
|
2011-05-04 13:37:28 +00:00
|
|
|
pat_len);
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-23 21:57:40 +00:00
|
|
|
if (tb[NL80211_WOWLAN_TRIG_TCP_CONNECTION]) {
|
|
|
|
err = nl80211_parse_wowlan_tcp(
|
|
|
|
rdev, tb[NL80211_WOWLAN_TRIG_TCP_CONNECTION],
|
|
|
|
&new_triggers);
|
|
|
|
if (err)
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2012-07-12 14:25:02 +00:00
|
|
|
ntrig = kmemdup(&new_triggers, sizeof(new_triggers), GFP_KERNEL);
|
|
|
|
if (!ntrig) {
|
|
|
|
err = -ENOMEM;
|
|
|
|
goto error;
|
2011-05-04 13:37:28 +00:00
|
|
|
}
|
2012-07-12 14:25:02 +00:00
|
|
|
cfg80211_rdev_free_wowlan(rdev);
|
2013-05-15 07:30:07 +00:00
|
|
|
rdev->wiphy.wowlan_config = ntrig;
|
2011-05-04 13:37:28 +00:00
|
|
|
|
2012-07-12 14:25:02 +00:00
|
|
|
set_wakeup:
|
2013-05-15 07:30:07 +00:00
|
|
|
if (rdev->ops->set_wakeup &&
|
|
|
|
prev_enabled != !!rdev->wiphy.wowlan_config)
|
|
|
|
rdev_set_wakeup(rdev, rdev->wiphy.wowlan_config);
|
2012-04-04 13:05:25 +00:00
|
|
|
|
2011-05-04 13:37:28 +00:00
|
|
|
return 0;
|
|
|
|
error:
|
|
|
|
for (i = 0; i < new_triggers.n_patterns; i++)
|
|
|
|
kfree(new_triggers.patterns[i].mask);
|
|
|
|
kfree(new_triggers.patterns);
|
2013-01-23 21:57:40 +00:00
|
|
|
if (new_triggers.tcp && new_triggers.tcp->sock)
|
|
|
|
sock_release(new_triggers.tcp->sock);
|
|
|
|
kfree(new_triggers.tcp);
|
2011-05-04 13:37:28 +00:00
|
|
|
return err;
|
|
|
|
}
|
2012-06-27 07:23:48 +00:00
|
|
|
#endif
|
2011-05-04 13:37:28 +00:00
|
|
|
|
2013-06-28 18:51:26 +00:00
|
|
|
static int nl80211_send_coalesce_rules(struct sk_buff *msg,
|
|
|
|
struct cfg80211_registered_device *rdev)
|
|
|
|
{
|
|
|
|
struct nlattr *nl_pats, *nl_pat, *nl_rule, *nl_rules;
|
|
|
|
int i, j, pat_len;
|
|
|
|
struct cfg80211_coalesce_rules *rule;
|
|
|
|
|
|
|
|
if (!rdev->coalesce->n_rules)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
nl_rules = nla_nest_start(msg, NL80211_ATTR_COALESCE_RULE);
|
|
|
|
if (!nl_rules)
|
|
|
|
return -ENOBUFS;
|
|
|
|
|
|
|
|
for (i = 0; i < rdev->coalesce->n_rules; i++) {
|
|
|
|
nl_rule = nla_nest_start(msg, i + 1);
|
|
|
|
if (!nl_rule)
|
|
|
|
return -ENOBUFS;
|
|
|
|
|
|
|
|
rule = &rdev->coalesce->rules[i];
|
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_COALESCE_RULE_DELAY,
|
|
|
|
rule->delay))
|
|
|
|
return -ENOBUFS;
|
|
|
|
|
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_COALESCE_RULE_CONDITION,
|
|
|
|
rule->condition))
|
|
|
|
return -ENOBUFS;
|
|
|
|
|
|
|
|
nl_pats = nla_nest_start(msg,
|
|
|
|
NL80211_ATTR_COALESCE_RULE_PKT_PATTERN);
|
|
|
|
if (!nl_pats)
|
|
|
|
return -ENOBUFS;
|
|
|
|
|
|
|
|
for (j = 0; j < rule->n_patterns; j++) {
|
|
|
|
nl_pat = nla_nest_start(msg, j + 1);
|
|
|
|
if (!nl_pat)
|
|
|
|
return -ENOBUFS;
|
|
|
|
pat_len = rule->patterns[j].pattern_len;
|
|
|
|
if (nla_put(msg, NL80211_PKTPAT_MASK,
|
|
|
|
DIV_ROUND_UP(pat_len, 8),
|
|
|
|
rule->patterns[j].mask) ||
|
|
|
|
nla_put(msg, NL80211_PKTPAT_PATTERN, pat_len,
|
|
|
|
rule->patterns[j].pattern) ||
|
|
|
|
nla_put_u32(msg, NL80211_PKTPAT_OFFSET,
|
|
|
|
rule->patterns[j].pkt_offset))
|
|
|
|
return -ENOBUFS;
|
|
|
|
nla_nest_end(msg, nl_pat);
|
|
|
|
}
|
|
|
|
nla_nest_end(msg, nl_pats);
|
|
|
|
nla_nest_end(msg, nl_rule);
|
|
|
|
}
|
|
|
|
nla_nest_end(msg, nl_rules);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_get_coalesce(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct sk_buff *msg;
|
|
|
|
void *hdr;
|
|
|
|
|
|
|
|
if (!rdev->wiphy.coalesce)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
|
|
if (!msg)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
|
|
|
|
NL80211_CMD_GET_COALESCE);
|
|
|
|
if (!hdr)
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
if (rdev->coalesce && nl80211_send_coalesce_rules(msg, rdev))
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
genlmsg_end(msg, hdr);
|
|
|
|
return genlmsg_reply(msg, info);
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return -ENOBUFS;
|
|
|
|
}
|
|
|
|
|
|
|
|
void cfg80211_rdev_free_coalesce(struct cfg80211_registered_device *rdev)
|
|
|
|
{
|
|
|
|
struct cfg80211_coalesce *coalesce = rdev->coalesce;
|
|
|
|
int i, j;
|
|
|
|
struct cfg80211_coalesce_rules *rule;
|
|
|
|
|
|
|
|
if (!coalesce)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < coalesce->n_rules; i++) {
|
|
|
|
rule = &coalesce->rules[i];
|
|
|
|
for (j = 0; j < rule->n_patterns; j++)
|
|
|
|
kfree(rule->patterns[j].mask);
|
|
|
|
kfree(rule->patterns);
|
|
|
|
}
|
|
|
|
kfree(coalesce->rules);
|
|
|
|
kfree(coalesce);
|
|
|
|
rdev->coalesce = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_parse_coalesce_rule(struct cfg80211_registered_device *rdev,
|
|
|
|
struct nlattr *rule,
|
|
|
|
struct cfg80211_coalesce_rules *new_rule)
|
|
|
|
{
|
|
|
|
int err, i;
|
|
|
|
const struct wiphy_coalesce_support *coalesce = rdev->wiphy.coalesce;
|
|
|
|
struct nlattr *tb[NUM_NL80211_ATTR_COALESCE_RULE], *pat;
|
|
|
|
int rem, pat_len, mask_len, pkt_offset, n_patterns = 0;
|
|
|
|
struct nlattr *pat_tb[NUM_NL80211_PKTPAT];
|
|
|
|
|
|
|
|
err = nla_parse(tb, NL80211_ATTR_COALESCE_RULE_MAX, nla_data(rule),
|
|
|
|
nla_len(rule), nl80211_coalesce_policy);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (tb[NL80211_ATTR_COALESCE_RULE_DELAY])
|
|
|
|
new_rule->delay =
|
|
|
|
nla_get_u32(tb[NL80211_ATTR_COALESCE_RULE_DELAY]);
|
|
|
|
if (new_rule->delay > coalesce->max_delay)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (tb[NL80211_ATTR_COALESCE_RULE_CONDITION])
|
|
|
|
new_rule->condition =
|
|
|
|
nla_get_u32(tb[NL80211_ATTR_COALESCE_RULE_CONDITION]);
|
|
|
|
if (new_rule->condition != NL80211_COALESCE_CONDITION_MATCH &&
|
|
|
|
new_rule->condition != NL80211_COALESCE_CONDITION_NO_MATCH)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
nla_for_each_nested(pat, tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN],
|
|
|
|
rem)
|
|
|
|
n_patterns++;
|
|
|
|
if (n_patterns > coalesce->n_patterns)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
new_rule->patterns = kcalloc(n_patterns, sizeof(new_rule->patterns[0]),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!new_rule->patterns)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
new_rule->n_patterns = n_patterns;
|
|
|
|
i = 0;
|
|
|
|
|
|
|
|
nla_for_each_nested(pat, tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN],
|
|
|
|
rem) {
|
2014-05-19 15:59:50 +00:00
|
|
|
u8 *mask_pat;
|
|
|
|
|
2013-06-28 18:51:26 +00:00
|
|
|
nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat),
|
|
|
|
nla_len(pat), NULL);
|
|
|
|
if (!pat_tb[NL80211_PKTPAT_MASK] ||
|
|
|
|
!pat_tb[NL80211_PKTPAT_PATTERN])
|
|
|
|
return -EINVAL;
|
|
|
|
pat_len = nla_len(pat_tb[NL80211_PKTPAT_PATTERN]);
|
|
|
|
mask_len = DIV_ROUND_UP(pat_len, 8);
|
|
|
|
if (nla_len(pat_tb[NL80211_PKTPAT_MASK]) != mask_len)
|
|
|
|
return -EINVAL;
|
|
|
|
if (pat_len > coalesce->pattern_max_len ||
|
|
|
|
pat_len < coalesce->pattern_min_len)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!pat_tb[NL80211_PKTPAT_OFFSET])
|
|
|
|
pkt_offset = 0;
|
|
|
|
else
|
|
|
|
pkt_offset = nla_get_u32(pat_tb[NL80211_PKTPAT_OFFSET]);
|
|
|
|
if (pkt_offset > coalesce->max_pkt_offset)
|
|
|
|
return -EINVAL;
|
|
|
|
new_rule->patterns[i].pkt_offset = pkt_offset;
|
|
|
|
|
2014-05-19 15:59:50 +00:00
|
|
|
mask_pat = kmalloc(mask_len + pat_len, GFP_KERNEL);
|
|
|
|
if (!mask_pat)
|
2013-06-28 18:51:26 +00:00
|
|
|
return -ENOMEM;
|
2014-05-19 15:59:50 +00:00
|
|
|
|
|
|
|
new_rule->patterns[i].mask = mask_pat;
|
|
|
|
memcpy(mask_pat, nla_data(pat_tb[NL80211_PKTPAT_MASK]),
|
|
|
|
mask_len);
|
|
|
|
|
|
|
|
mask_pat += mask_len;
|
|
|
|
new_rule->patterns[i].pattern = mask_pat;
|
2013-06-28 18:51:26 +00:00
|
|
|
new_rule->patterns[i].pattern_len = pat_len;
|
2014-05-19 15:59:50 +00:00
|
|
|
memcpy(mask_pat, nla_data(pat_tb[NL80211_PKTPAT_PATTERN]),
|
|
|
|
pat_len);
|
2013-06-28 18:51:26 +00:00
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_set_coalesce(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
const struct wiphy_coalesce_support *coalesce = rdev->wiphy.coalesce;
|
|
|
|
struct cfg80211_coalesce new_coalesce = {};
|
|
|
|
struct cfg80211_coalesce *n_coalesce;
|
|
|
|
int err, rem_rule, n_rules = 0, i, j;
|
|
|
|
struct nlattr *rule;
|
|
|
|
struct cfg80211_coalesce_rules *tmp_rule;
|
|
|
|
|
|
|
|
if (!rdev->wiphy.coalesce || !rdev->ops->set_coalesce)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_COALESCE_RULE]) {
|
|
|
|
cfg80211_rdev_free_coalesce(rdev);
|
|
|
|
rdev->ops->set_coalesce(&rdev->wiphy, NULL);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
nla_for_each_nested(rule, info->attrs[NL80211_ATTR_COALESCE_RULE],
|
|
|
|
rem_rule)
|
|
|
|
n_rules++;
|
|
|
|
if (n_rules > coalesce->n_rules)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
new_coalesce.rules = kcalloc(n_rules, sizeof(new_coalesce.rules[0]),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!new_coalesce.rules)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
new_coalesce.n_rules = n_rules;
|
|
|
|
i = 0;
|
|
|
|
|
|
|
|
nla_for_each_nested(rule, info->attrs[NL80211_ATTR_COALESCE_RULE],
|
|
|
|
rem_rule) {
|
|
|
|
err = nl80211_parse_coalesce_rule(rdev, rule,
|
|
|
|
&new_coalesce.rules[i]);
|
|
|
|
if (err)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = rdev->ops->set_coalesce(&rdev->wiphy, &new_coalesce);
|
|
|
|
if (err)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
n_coalesce = kmemdup(&new_coalesce, sizeof(new_coalesce), GFP_KERNEL);
|
|
|
|
if (!n_coalesce) {
|
|
|
|
err = -ENOMEM;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
cfg80211_rdev_free_coalesce(rdev);
|
|
|
|
rdev->coalesce = n_coalesce;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
error:
|
|
|
|
for (i = 0; i < new_coalesce.n_rules; i++) {
|
|
|
|
tmp_rule = &new_coalesce.rules[i];
|
|
|
|
for (j = 0; j < tmp_rule->n_patterns; j++)
|
|
|
|
kfree(tmp_rule->patterns[j].mask);
|
|
|
|
kfree(tmp_rule->patterns);
|
|
|
|
}
|
|
|
|
kfree(new_coalesce.rules);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2011-07-05 14:35:40 +00:00
|
|
|
static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
|
|
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
|
struct nlattr *tb[NUM_NL80211_REKEY_DATA];
|
|
|
|
struct cfg80211_gtk_rekey_data rekey_data;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_REKEY_DATA])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
err = nla_parse(tb, MAX_NL80211_REKEY_DATA,
|
|
|
|
nla_data(info->attrs[NL80211_ATTR_REKEY_DATA]),
|
|
|
|
nla_len(info->attrs[NL80211_ATTR_REKEY_DATA]),
|
|
|
|
nl80211_rekey_policy);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (nla_len(tb[NL80211_REKEY_DATA_REPLAY_CTR]) != NL80211_REPLAY_CTR_LEN)
|
|
|
|
return -ERANGE;
|
|
|
|
if (nla_len(tb[NL80211_REKEY_DATA_KEK]) != NL80211_KEK_LEN)
|
|
|
|
return -ERANGE;
|
|
|
|
if (nla_len(tb[NL80211_REKEY_DATA_KCK]) != NL80211_KCK_LEN)
|
|
|
|
return -ERANGE;
|
|
|
|
|
2014-09-10 19:28:06 +00:00
|
|
|
rekey_data.kek = nla_data(tb[NL80211_REKEY_DATA_KEK]);
|
|
|
|
rekey_data.kck = nla_data(tb[NL80211_REKEY_DATA_KCK]);
|
|
|
|
rekey_data.replay_ctr = nla_data(tb[NL80211_REKEY_DATA_REPLAY_CTR]);
|
2011-07-05 14:35:40 +00:00
|
|
|
|
|
|
|
wdev_lock(wdev);
|
|
|
|
if (!wdev->current_bss) {
|
|
|
|
err = -ENOTCONN;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!rdev->ops->set_rekey_data) {
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2012-06-27 14:19:42 +00:00
|
|
|
err = rdev_set_rekey_data(rdev, dev, &rekey_data);
|
2011-07-05 14:35:40 +00:00
|
|
|
out:
|
|
|
|
wdev_unlock(wdev);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2011-11-04 10:18:12 +00:00
|
|
|
static int nl80211_register_unexpected_frame(struct sk_buff *skb,
|
|
|
|
struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
|
|
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
|
|
|
|
|
if (wdev->iftype != NL80211_IFTYPE_AP &&
|
|
|
|
wdev->iftype != NL80211_IFTYPE_P2P_GO)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2012-09-07 20:12:54 +00:00
|
|
|
if (wdev->ap_unexpected_nlportid)
|
2011-11-04 10:18:12 +00:00
|
|
|
return -EBUSY;
|
|
|
|
|
2012-09-07 20:12:54 +00:00
|
|
|
wdev->ap_unexpected_nlportid = info->snd_portid;
|
2011-11-04 10:18:12 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-11-04 10:18:15 +00:00
|
|
|
static int nl80211_probe_client(struct sk_buff *skb,
|
|
|
|
struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
|
|
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
|
struct sk_buff *msg;
|
|
|
|
void *hdr;
|
|
|
|
const u8 *addr;
|
|
|
|
u64 cookie;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (wdev->iftype != NL80211_IFTYPE_AP &&
|
|
|
|
wdev->iftype != NL80211_IFTYPE_P2P_GO)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_MAC])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (!rdev->ops->probe_client)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
|
|
if (!msg)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2012-09-07 20:12:54 +00:00
|
|
|
hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
|
2011-11-04 10:18:15 +00:00
|
|
|
NL80211_CMD_PROBE_CLIENT);
|
2013-08-14 11:50:01 +00:00
|
|
|
if (!hdr) {
|
|
|
|
err = -ENOBUFS;
|
2011-11-04 10:18:15 +00:00
|
|
|
goto free_msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
|
|
|
|
2012-06-27 14:19:42 +00:00
|
|
|
err = rdev_probe_client(rdev, dev, addr, &cookie);
|
2011-11-04 10:18:15 +00:00
|
|
|
if (err)
|
|
|
|
goto free_msg;
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie))
|
|
|
|
goto nla_put_failure;
|
2011-11-04 10:18:15 +00:00
|
|
|
|
|
|
|
genlmsg_end(msg, hdr);
|
|
|
|
|
|
|
|
return genlmsg_reply(msg, info);
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
err = -ENOBUFS;
|
|
|
|
free_msg:
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2011-11-04 10:18:17 +00:00
|
|
|
static int nl80211_register_beacons(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
2012-10-26 21:49:25 +00:00
|
|
|
struct cfg80211_beacon_registration *reg, *nreg;
|
|
|
|
int rv;
|
2011-11-04 10:18:17 +00:00
|
|
|
|
|
|
|
if (!(rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2012-10-26 21:49:25 +00:00
|
|
|
nreg = kzalloc(sizeof(*nreg), GFP_KERNEL);
|
|
|
|
if (!nreg)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
/* First, check if already registered. */
|
|
|
|
spin_lock_bh(&rdev->beacon_registrations_lock);
|
|
|
|
list_for_each_entry(reg, &rdev->beacon_registrations, list) {
|
|
|
|
if (reg->nlportid == info->snd_portid) {
|
|
|
|
rv = -EALREADY;
|
|
|
|
goto out_err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Add it to the list */
|
|
|
|
nreg->nlportid = info->snd_portid;
|
|
|
|
list_add(&nreg->list, &rdev->beacon_registrations);
|
2011-11-04 10:18:17 +00:00
|
|
|
|
2012-10-26 21:49:25 +00:00
|
|
|
spin_unlock_bh(&rdev->beacon_registrations_lock);
|
2011-11-04 10:18:17 +00:00
|
|
|
|
|
|
|
return 0;
|
2012-10-26 21:49:25 +00:00
|
|
|
out_err:
|
|
|
|
spin_unlock_bh(&rdev->beacon_registrations_lock);
|
|
|
|
kfree(nreg);
|
|
|
|
return rv;
|
2011-11-04 10:18:17 +00:00
|
|
|
}
|
|
|
|
|
2012-06-15 22:19:54 +00:00
|
|
|
static int nl80211_start_p2p_device(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct wireless_dev *wdev = info->user_ptr[1];
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (!rdev->ops->start_p2p_device)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (wdev->iftype != NL80211_IFTYPE_P2P_DEVICE)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (wdev->p2p_started)
|
|
|
|
return 0;
|
|
|
|
|
2014-02-27 09:07:21 +00:00
|
|
|
if (rfkill_blocked(rdev->rfkill))
|
|
|
|
return -ERFKILL;
|
2012-06-15 22:19:54 +00:00
|
|
|
|
2012-10-23 13:16:50 +00:00
|
|
|
err = rdev_start_p2p_device(rdev, wdev);
|
2012-06-15 22:19:54 +00:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
wdev->p2p_started = true;
|
|
|
|
rdev->opencount++;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_stop_p2p_device(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct wireless_dev *wdev = info->user_ptr[1];
|
|
|
|
|
|
|
|
if (wdev->iftype != NL80211_IFTYPE_P2P_DEVICE)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (!rdev->ops->stop_p2p_device)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2013-03-19 14:04:07 +00:00
|
|
|
cfg80211_stop_p2p_device(rdev, wdev);
|
2012-06-15 22:19:54 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-02-14 15:19:38 +00:00
|
|
|
static int nl80211_get_protocol_features(struct sk_buff *skb,
|
|
|
|
struct genl_info *info)
|
|
|
|
{
|
|
|
|
void *hdr;
|
|
|
|
struct sk_buff *msg;
|
|
|
|
|
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
|
|
if (!msg)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
|
|
|
|
NL80211_CMD_GET_PROTOCOL_FEATURES);
|
|
|
|
if (!hdr)
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_PROTOCOL_FEATURES,
|
|
|
|
NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP))
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
genlmsg_end(msg, hdr);
|
|
|
|
return genlmsg_reply(msg, info);
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
kfree_skb(msg);
|
|
|
|
return -ENOBUFS;
|
|
|
|
}
|
|
|
|
|
2013-02-27 15:14:27 +00:00
|
|
|
static int nl80211_update_ft_ies(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct cfg80211_update_ft_ies_params ft_params;
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
|
|
|
|
|
|
|
if (!rdev->ops->update_ft_ies)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_MDID] ||
|
|
|
|
!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
memset(&ft_params, 0, sizeof(ft_params));
|
|
|
|
ft_params.md = nla_get_u16(info->attrs[NL80211_ATTR_MDID]);
|
|
|
|
ft_params.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
|
|
|
|
ft_params.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
|
|
|
|
|
|
|
|
return rdev_update_ft_ies(rdev, dev, &ft_params);
|
|
|
|
}
|
|
|
|
|
2013-04-18 13:49:00 +00:00
|
|
|
static int nl80211_crit_protocol_start(struct sk_buff *skb,
|
|
|
|
struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct wireless_dev *wdev = info->user_ptr[1];
|
|
|
|
enum nl80211_crit_proto_id proto = NL80211_CRIT_PROTO_UNSPEC;
|
|
|
|
u16 duration;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!rdev->ops->crit_proto_start)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (WARN_ON(!rdev->ops->crit_proto_stop))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (rdev->crit_proto_nlportid)
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
/* determine protocol if provided */
|
|
|
|
if (info->attrs[NL80211_ATTR_CRIT_PROT_ID])
|
|
|
|
proto = nla_get_u16(info->attrs[NL80211_ATTR_CRIT_PROT_ID]);
|
|
|
|
|
|
|
|
if (proto >= NUM_NL80211_CRIT_PROTO)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* timeout must be provided */
|
|
|
|
if (!info->attrs[NL80211_ATTR_MAX_CRIT_PROT_DURATION])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
duration =
|
|
|
|
nla_get_u16(info->attrs[NL80211_ATTR_MAX_CRIT_PROT_DURATION]);
|
|
|
|
|
|
|
|
if (duration > NL80211_CRIT_PROTO_MAX_DURATION)
|
|
|
|
return -ERANGE;
|
|
|
|
|
|
|
|
ret = rdev_crit_proto_start(rdev, wdev, proto, duration);
|
|
|
|
if (!ret)
|
|
|
|
rdev->crit_proto_nlportid = info->snd_portid;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_crit_protocol_stop(struct sk_buff *skb,
|
|
|
|
struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct wireless_dev *wdev = info->user_ptr[1];
|
|
|
|
|
|
|
|
if (!rdev->ops->crit_proto_stop)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (rdev->crit_proto_nlportid) {
|
|
|
|
rdev->crit_proto_nlportid = 0;
|
|
|
|
rdev_crit_proto_stop(rdev, wdev);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-11-13 12:37:47 +00:00
|
|
|
static int nl80211_vendor_cmd(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct wireless_dev *wdev =
|
|
|
|
__cfg80211_wdev_from_attrs(genl_info_net(info), info->attrs);
|
|
|
|
int i, err;
|
|
|
|
u32 vid, subcmd;
|
|
|
|
|
|
|
|
if (!rdev->wiphy.vendor_commands)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (IS_ERR(wdev)) {
|
|
|
|
err = PTR_ERR(wdev);
|
|
|
|
if (err != -EINVAL)
|
|
|
|
return err;
|
|
|
|
wdev = NULL;
|
|
|
|
} else if (wdev->wiphy != &rdev->wiphy) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_VENDOR_ID] ||
|
|
|
|
!info->attrs[NL80211_ATTR_VENDOR_SUBCMD])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
vid = nla_get_u32(info->attrs[NL80211_ATTR_VENDOR_ID]);
|
|
|
|
subcmd = nla_get_u32(info->attrs[NL80211_ATTR_VENDOR_SUBCMD]);
|
|
|
|
for (i = 0; i < rdev->wiphy.n_vendor_commands; i++) {
|
|
|
|
const struct wiphy_vendor_command *vcmd;
|
|
|
|
void *data = NULL;
|
|
|
|
int len = 0;
|
|
|
|
|
|
|
|
vcmd = &rdev->wiphy.vendor_commands[i];
|
|
|
|
|
|
|
|
if (vcmd->info.vendor_id != vid || vcmd->info.subcmd != subcmd)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (vcmd->flags & (WIPHY_VENDOR_CMD_NEED_WDEV |
|
|
|
|
WIPHY_VENDOR_CMD_NEED_NETDEV)) {
|
|
|
|
if (!wdev)
|
|
|
|
return -EINVAL;
|
|
|
|
if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_NETDEV &&
|
|
|
|
!wdev->netdev)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_RUNNING) {
|
|
|
|
if (wdev->netdev &&
|
|
|
|
!netif_running(wdev->netdev))
|
|
|
|
return -ENETDOWN;
|
|
|
|
if (!wdev->netdev && !wdev->p2p_started)
|
|
|
|
return -ENETDOWN;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
wdev = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_VENDOR_DATA]) {
|
|
|
|
data = nla_data(info->attrs[NL80211_ATTR_VENDOR_DATA]);
|
|
|
|
len = nla_len(info->attrs[NL80211_ATTR_VENDOR_DATA]);
|
|
|
|
}
|
|
|
|
|
|
|
|
rdev->cur_cmd_info = info;
|
|
|
|
err = rdev->wiphy.vendor_commands[i].doit(&rdev->wiphy, wdev,
|
|
|
|
data, len);
|
|
|
|
rdev->cur_cmd_info = NULL;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct sk_buff *__cfg80211_alloc_reply_skb(struct wiphy *wiphy,
|
|
|
|
enum nl80211_commands cmd,
|
|
|
|
enum nl80211_attrs attr,
|
|
|
|
int approxlen)
|
|
|
|
{
|
2014-04-21 04:53:03 +00:00
|
|
|
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
2013-11-13 12:37:47 +00:00
|
|
|
|
|
|
|
if (WARN_ON(!rdev->cur_cmd_info))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return __cfg80211_alloc_vendor_skb(rdev, approxlen,
|
|
|
|
rdev->cur_cmd_info->snd_portid,
|
|
|
|
rdev->cur_cmd_info->snd_seq,
|
2013-12-18 13:43:31 +00:00
|
|
|
cmd, attr, NULL, GFP_KERNEL);
|
2013-11-13 12:37:47 +00:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(__cfg80211_alloc_reply_skb);
|
|
|
|
|
|
|
|
int cfg80211_vendor_cmd_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];
|
|
|
|
|
2014-07-30 12:55:26 +00:00
|
|
|
/* clear CB data for netlink core to own from now on */
|
|
|
|
memset(skb->cb, 0, sizeof(skb->cb));
|
|
|
|
|
2013-11-13 12:37:47 +00:00
|
|
|
if (WARN_ON(!rdev->cur_cmd_info)) {
|
|
|
|
kfree_skb(skb);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
nla_nest_end(skb, data);
|
|
|
|
genlmsg_end(skb, hdr);
|
|
|
|
return genlmsg_reply(skb, rdev->cur_cmd_info);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(cfg80211_vendor_cmd_reply);
|
|
|
|
|
|
|
|
|
2013-12-17 07:01:30 +00:00
|
|
|
static int nl80211_set_qos_map(struct sk_buff *skb,
|
|
|
|
struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct cfg80211_qos_map *qos_map = NULL;
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
|
|
|
u8 *pos, len, num_des, des_len, des;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!rdev->ops->set_qos_map)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_QOS_MAP]) {
|
|
|
|
pos = nla_data(info->attrs[NL80211_ATTR_QOS_MAP]);
|
|
|
|
len = nla_len(info->attrs[NL80211_ATTR_QOS_MAP]);
|
|
|
|
|
|
|
|
if (len % 2 || len < IEEE80211_QOS_MAP_LEN_MIN ||
|
|
|
|
len > IEEE80211_QOS_MAP_LEN_MAX)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
qos_map = kzalloc(sizeof(struct cfg80211_qos_map), GFP_KERNEL);
|
|
|
|
if (!qos_map)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
num_des = (len - IEEE80211_QOS_MAP_LEN_MIN) >> 1;
|
|
|
|
if (num_des) {
|
|
|
|
des_len = num_des *
|
|
|
|
sizeof(struct cfg80211_dscp_exception);
|
|
|
|
memcpy(qos_map->dscp_exception, pos, des_len);
|
|
|
|
qos_map->num_des = num_des;
|
|
|
|
for (des = 0; des < num_des; des++) {
|
|
|
|
if (qos_map->dscp_exception[des].up > 7) {
|
|
|
|
kfree(qos_map);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pos += des_len;
|
|
|
|
}
|
|
|
|
memcpy(qos_map->up, pos, IEEE80211_QOS_MAP_LEN_MIN);
|
|
|
|
}
|
|
|
|
|
|
|
|
wdev_lock(dev->ieee80211_ptr);
|
|
|
|
ret = nl80211_key_allowed(dev->ieee80211_ptr);
|
|
|
|
if (!ret)
|
|
|
|
ret = rdev_set_qos_map(rdev, dev, qos_map);
|
|
|
|
wdev_unlock(dev->ieee80211_ptr);
|
|
|
|
|
|
|
|
kfree(qos_map);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-09-09 19:55:35 +00:00
|
|
|
static int nl80211_add_tx_ts(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
|
|
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
|
const u8 *peer;
|
|
|
|
u8 tsid, up;
|
|
|
|
u16 admitted_time = 0;
|
|
|
|
int err;
|
|
|
|
|
2014-10-22 07:25:06 +00:00
|
|
|
if (!(rdev->wiphy.features & NL80211_FEATURE_SUPPORTS_WMM_ADMISSION))
|
2014-09-09 19:55:35 +00:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_TSID] || !info->attrs[NL80211_ATTR_MAC] ||
|
|
|
|
!info->attrs[NL80211_ATTR_USER_PRIO])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
tsid = nla_get_u8(info->attrs[NL80211_ATTR_TSID]);
|
|
|
|
if (tsid >= IEEE80211_NUM_TIDS)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
up = nla_get_u8(info->attrs[NL80211_ATTR_USER_PRIO]);
|
|
|
|
if (up >= IEEE80211_NUM_UPS)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* WMM uses TIDs 0-7 even for TSPEC */
|
2014-10-22 07:25:06 +00:00
|
|
|
if (tsid >= IEEE80211_FIRST_TSPEC_TSID) {
|
2014-09-09 19:55:35 +00:00
|
|
|
/* TODO: handle 802.11 TSPEC/admission control
|
2014-10-22 07:25:06 +00:00
|
|
|
* need more attributes for that (e.g. BA session requirement);
|
|
|
|
* change the WMM adminssion test above to allow both then
|
2014-09-09 19:55:35 +00:00
|
|
|
*/
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
peer = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
|
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_ADMITTED_TIME]) {
|
|
|
|
admitted_time =
|
|
|
|
nla_get_u16(info->attrs[NL80211_ATTR_ADMITTED_TIME]);
|
|
|
|
if (!admitted_time)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
wdev_lock(wdev);
|
|
|
|
switch (wdev->iftype) {
|
|
|
|
case NL80211_IFTYPE_STATION:
|
|
|
|
case NL80211_IFTYPE_P2P_CLIENT:
|
|
|
|
if (wdev->current_bss)
|
|
|
|
break;
|
|
|
|
err = -ENOTCONN;
|
|
|
|
goto out;
|
|
|
|
default:
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = rdev_add_tx_ts(rdev, dev, tsid, peer, up, admitted_time);
|
|
|
|
|
|
|
|
out:
|
|
|
|
wdev_unlock(wdev);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nl80211_del_tx_ts(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
|
|
struct net_device *dev = info->user_ptr[1];
|
|
|
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
|
const u8 *peer;
|
|
|
|
u8 tsid;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_TSID] || !info->attrs[NL80211_ATTR_MAC])
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
tsid = nla_get_u8(info->attrs[NL80211_ATTR_TSID]);
|
|
|
|
peer = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
|
|
|
|
|
|
|
wdev_lock(wdev);
|
|
|
|
err = rdev_del_tx_ts(rdev, dev, tsid, peer);
|
|
|
|
wdev_unlock(wdev);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
#define NL80211_FLAG_NEED_WIPHY 0x01
|
|
|
|
#define NL80211_FLAG_NEED_NETDEV 0x02
|
|
|
|
#define NL80211_FLAG_NEED_RTNL 0x04
|
2010-10-04 19:14:05 +00:00
|
|
|
#define NL80211_FLAG_CHECK_NETDEV_UP 0x08
|
|
|
|
#define NL80211_FLAG_NEED_NETDEV_UP (NL80211_FLAG_NEED_NETDEV |\
|
|
|
|
NL80211_FLAG_CHECK_NETDEV_UP)
|
2012-06-15 13:23:36 +00:00
|
|
|
#define NL80211_FLAG_NEED_WDEV 0x10
|
2012-06-15 22:19:54 +00:00
|
|
|
/* If a netdev is associated, it must be UP, P2P must be started */
|
2012-06-15 13:23:36 +00:00
|
|
|
#define NL80211_FLAG_NEED_WDEV_UP (NL80211_FLAG_NEED_WDEV |\
|
|
|
|
NL80211_FLAG_CHECK_NETDEV_UP)
|
2014-09-10 12:00:16 +00:00
|
|
|
#define NL80211_FLAG_CLEAR_SKB 0x20
|
2010-10-04 19:36:35 +00:00
|
|
|
|
2013-11-14 16:14:45 +00:00
|
|
|
static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
|
2010-10-04 19:36:35 +00:00
|
|
|
struct genl_info *info)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev;
|
2012-06-15 12:33:17 +00:00
|
|
|
struct wireless_dev *wdev;
|
2010-10-04 19:36:35 +00:00
|
|
|
struct net_device *dev;
|
|
|
|
bool rtnl = ops->internal_flags & NL80211_FLAG_NEED_RTNL;
|
|
|
|
|
|
|
|
if (rtnl)
|
|
|
|
rtnl_lock();
|
|
|
|
|
|
|
|
if (ops->internal_flags & NL80211_FLAG_NEED_WIPHY) {
|
2012-06-15 12:14:22 +00:00
|
|
|
rdev = cfg80211_get_dev_from_info(genl_info_net(info), info);
|
2010-10-04 19:36:35 +00:00
|
|
|
if (IS_ERR(rdev)) {
|
|
|
|
if (rtnl)
|
|
|
|
rtnl_unlock();
|
|
|
|
return PTR_ERR(rdev);
|
|
|
|
}
|
|
|
|
info->user_ptr[0] = rdev;
|
2012-06-15 13:23:36 +00:00
|
|
|
} else if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV ||
|
|
|
|
ops->internal_flags & NL80211_FLAG_NEED_WDEV) {
|
2013-05-08 19:45:15 +00:00
|
|
|
ASSERT_RTNL();
|
|
|
|
|
2012-06-15 12:33:17 +00:00
|
|
|
wdev = __cfg80211_wdev_from_attrs(genl_info_net(info),
|
|
|
|
info->attrs);
|
|
|
|
if (IS_ERR(wdev)) {
|
2010-10-04 19:36:35 +00:00
|
|
|
if (rtnl)
|
|
|
|
rtnl_unlock();
|
2012-06-15 12:33:17 +00:00
|
|
|
return PTR_ERR(wdev);
|
2010-10-04 19:36:35 +00:00
|
|
|
}
|
2012-06-15 12:33:17 +00:00
|
|
|
|
|
|
|
dev = wdev->netdev;
|
2014-04-21 04:53:03 +00:00
|
|
|
rdev = wiphy_to_rdev(wdev->wiphy);
|
2012-06-15 12:33:17 +00:00
|
|
|
|
2012-06-15 13:23:36 +00:00
|
|
|
if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV) {
|
|
|
|
if (!dev) {
|
|
|
|
if (rtnl)
|
|
|
|
rtnl_unlock();
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
info->user_ptr[1] = dev;
|
|
|
|
} else {
|
|
|
|
info->user_ptr[1] = wdev;
|
2010-10-04 19:14:05 +00:00
|
|
|
}
|
2012-06-15 13:23:36 +00:00
|
|
|
|
|
|
|
if (dev) {
|
|
|
|
if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP &&
|
|
|
|
!netif_running(dev)) {
|
|
|
|
if (rtnl)
|
|
|
|
rtnl_unlock();
|
|
|
|
return -ENETDOWN;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev_hold(dev);
|
2012-06-15 22:19:54 +00:00
|
|
|
} else if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP) {
|
|
|
|
if (!wdev->p2p_started) {
|
|
|
|
if (rtnl)
|
|
|
|
rtnl_unlock();
|
|
|
|
return -ENETDOWN;
|
|
|
|
}
|
2010-10-04 19:14:05 +00:00
|
|
|
}
|
2012-06-15 12:33:17 +00:00
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
info->user_ptr[0] = rdev;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-11-14 16:14:45 +00:00
|
|
|
static void nl80211_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
|
2010-10-04 19:36:35 +00:00
|
|
|
struct genl_info *info)
|
|
|
|
{
|
2012-06-15 13:23:36 +00:00
|
|
|
if (info->user_ptr[1]) {
|
|
|
|
if (ops->internal_flags & NL80211_FLAG_NEED_WDEV) {
|
|
|
|
struct wireless_dev *wdev = info->user_ptr[1];
|
|
|
|
|
|
|
|
if (wdev->netdev)
|
|
|
|
dev_put(wdev->netdev);
|
|
|
|
} else {
|
|
|
|
dev_put(info->user_ptr[1]);
|
|
|
|
}
|
|
|
|
}
|
2014-09-10 12:00:16 +00:00
|
|
|
|
2010-10-04 19:36:35 +00:00
|
|
|
if (ops->internal_flags & NL80211_FLAG_NEED_RTNL)
|
|
|
|
rtnl_unlock();
|
2014-09-10 12:00:16 +00:00
|
|
|
|
|
|
|
/* If needed, clear the netlink message payload from the SKB
|
|
|
|
* as it might contain key data that shouldn't stick around on
|
|
|
|
* the heap after the SKB is freed. The netlink message header
|
|
|
|
* is still needed for further processing, so leave it intact.
|
|
|
|
*/
|
|
|
|
if (ops->internal_flags & NL80211_FLAG_CLEAR_SKB) {
|
|
|
|
struct nlmsghdr *nlh = nlmsg_hdr(skb);
|
|
|
|
|
|
|
|
memset(nlmsg_data(nlh), 0, nlmsg_len(nlh));
|
|
|
|
}
|
2010-10-04 19:36:35 +00:00
|
|
|
}
|
|
|
|
|
2013-11-14 16:14:46 +00:00
|
|
|
static const struct genl_ops nl80211_ops[] = {
|
2007-09-20 17:09:35 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_GET_WIPHY,
|
|
|
|
.doit = nl80211_get_wiphy,
|
|
|
|
.dumpit = nl80211_dump_wiphy,
|
2013-06-19 08:57:22 +00:00
|
|
|
.done = nl80211_dump_wiphy_done,
|
2007-09-20 17:09:35 +00:00
|
|
|
.policy = nl80211_policy,
|
|
|
|
/* can be retrieved by unprivileged users */
|
2013-05-08 19:45:15 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_WIPHY |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2007-09-20 17:09:35 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_SET_WIPHY,
|
|
|
|
.doit = nl80211_set_wiphy,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
2010-10-04 19:36:35 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_RTNL,
|
2007-09-20 17:09:35 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_GET_INTERFACE,
|
|
|
|
.doit = nl80211_get_interface,
|
|
|
|
.dumpit = nl80211_dump_interface,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
/* can be retrieved by unprivileged users */
|
2013-05-08 19:45:15 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_WDEV |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2007-09-20 17:09:35 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_SET_INTERFACE,
|
|
|
|
.doit = nl80211_set_interface,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
2010-10-04 19:36:35 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2007-09-20 17:09:35 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_NEW_INTERFACE,
|
|
|
|
.doit = nl80211_new_interface,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
2010-10-04 19:36:35 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_WIPHY |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2007-09-20 17:09:35 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_DEL_INTERFACE,
|
|
|
|
.doit = nl80211_del_interface,
|
|
|
|
.policy = nl80211_policy,
|
2007-12-19 01:03:29 +00:00
|
|
|
.flags = GENL_ADMIN_PERM,
|
2012-06-15 22:00:26 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_WDEV |
|
2010-10-04 19:36:35 +00:00
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2007-12-19 01:03:29 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_GET_KEY,
|
|
|
|
.doit = nl80211_get_key,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
2012-04-02 08:51:55 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
2010-10-04 19:36:35 +00:00
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2007-12-19 01:03:29 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_SET_KEY,
|
|
|
|
.doit = nl80211_set_key,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
2010-10-04 19:14:05 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
2014-09-10 12:00:16 +00:00
|
|
|
NL80211_FLAG_NEED_RTNL |
|
|
|
|
NL80211_FLAG_CLEAR_SKB,
|
2007-12-19 01:03:29 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_NEW_KEY,
|
|
|
|
.doit = nl80211_new_key,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
2010-10-04 19:14:05 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
2014-09-10 12:00:16 +00:00
|
|
|
NL80211_FLAG_NEED_RTNL |
|
|
|
|
NL80211_FLAG_CLEAR_SKB,
|
2007-12-19 01:03:29 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_DEL_KEY,
|
|
|
|
.doit = nl80211_del_key,
|
|
|
|
.policy = nl80211_policy,
|
2007-09-20 17:09:35 +00:00
|
|
|
.flags = GENL_ADMIN_PERM,
|
2010-10-04 19:14:05 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
2010-10-04 19:36:35 +00:00
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2007-09-20 17:09:35 +00:00
|
|
|
},
|
2007-12-19 01:03:32 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_SET_BEACON,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
2012-02-13 14:17:18 +00:00
|
|
|
.doit = nl80211_set_beacon,
|
2012-04-02 08:51:55 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
2010-10-04 19:36:35 +00:00
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2007-12-19 01:03:32 +00:00
|
|
|
},
|
|
|
|
{
|
2012-02-13 14:17:18 +00:00
|
|
|
.cmd = NL80211_CMD_START_AP,
|
2007-12-19 01:03:32 +00:00
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
2012-02-13 14:17:18 +00:00
|
|
|
.doit = nl80211_start_ap,
|
2012-04-02 08:51:55 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
2010-10-04 19:36:35 +00:00
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2007-12-19 01:03:32 +00:00
|
|
|
},
|
|
|
|
{
|
2012-02-13 14:17:18 +00:00
|
|
|
.cmd = NL80211_CMD_STOP_AP,
|
2007-12-19 01:03:32 +00:00
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
2012-02-13 14:17:18 +00:00
|
|
|
.doit = nl80211_stop_ap,
|
2012-04-02 08:51:55 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
2010-10-04 19:36:35 +00:00
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2007-12-19 01:03:32 +00:00
|
|
|
},
|
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,
|
2010-10-04 19:36:35 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2007-12-19 01:03:34 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_SET_STATION,
|
|
|
|
.doit = nl80211_set_station,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
2012-04-02 08:51:55 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
2010-10-04 19:36:35 +00:00
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2007-12-19 01:03:34 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_NEW_STATION,
|
|
|
|
.doit = nl80211_new_station,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
2010-10-04 19:14:05 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
2010-10-04 19:36:35 +00:00
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2007-12-19 01:03:34 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_DEL_STATION,
|
|
|
|
.doit = nl80211_del_station,
|
|
|
|
.policy = nl80211_policy,
|
2008-02-23 14:17:06 +00:00
|
|
|
.flags = GENL_ADMIN_PERM,
|
2012-04-02 08:51:55 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
2010-10-04 19:36:35 +00:00
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2008-02-23 14:17:06 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_GET_MPATH,
|
|
|
|
.doit = nl80211_get_mpath,
|
|
|
|
.dumpit = nl80211_dump_mpath,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
2010-10-04 19:14:05 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
2010-10-04 19:36:35 +00:00
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2008-02-23 14:17:06 +00:00
|
|
|
},
|
2014-09-12 06:58:49 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_GET_MPP,
|
|
|
|
.doit = nl80211_get_mpp,
|
|
|
|
.dumpit = nl80211_dump_mpp,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
|
|
|
},
|
2008-02-23 14:17:06 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_SET_MPATH,
|
|
|
|
.doit = nl80211_set_mpath,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
2010-10-04 19:14:05 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
2010-10-04 19:36:35 +00:00
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2008-02-23 14:17:06 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_NEW_MPATH,
|
|
|
|
.doit = nl80211_new_mpath,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
2010-10-04 19:14:05 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
2010-10-04 19:36:35 +00:00
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2008-02-23 14:17:06 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_DEL_MPATH,
|
|
|
|
.doit = nl80211_del_mpath,
|
|
|
|
.policy = nl80211_policy,
|
2008-08-07 17:07:01 +00:00
|
|
|
.flags = GENL_ADMIN_PERM,
|
2012-04-02 08:51:55 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
2010-10-04 19:36:35 +00:00
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2008-08-07 17:07:01 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_SET_BSS,
|
|
|
|
.doit = nl80211_set_bss,
|
|
|
|
.policy = nl80211_policy,
|
2008-09-10 06:19:48 +00:00
|
|
|
.flags = GENL_ADMIN_PERM,
|
2012-04-02 08:51:55 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
2010-10-04 19:36:35 +00:00
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2008-09-10 06:19:48 +00:00
|
|
|
},
|
2009-01-30 17:26:42 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_GET_REG,
|
|
|
|
.doit = nl80211_get_reg,
|
|
|
|
.policy = nl80211_policy,
|
2013-05-08 19:45:15 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_RTNL,
|
2009-01-30 17:26:42 +00:00
|
|
|
/* 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,
|
2013-05-08 19:45:15 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_RTNL,
|
2008-09-10 06:19:48 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.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,
|
|
|
|
},
|
|
|
|
{
|
2010-12-17 01:37:48 +00:00
|
|
|
.cmd = NL80211_CMD_GET_MESH_CONFIG,
|
|
|
|
.doit = nl80211_get_mesh_config,
|
2008-10-21 19:03:48 +00:00
|
|
|
.policy = nl80211_policy,
|
|
|
|
/* can be retrieved by unprivileged users */
|
2012-04-02 08:51:55 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
2010-10-04 19:36:35 +00:00
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2008-10-21 19:03:48 +00:00
|
|
|
},
|
|
|
|
{
|
2010-12-17 01:37:48 +00:00
|
|
|
.cmd = NL80211_CMD_SET_MESH_CONFIG,
|
|
|
|
.doit = nl80211_update_mesh_config,
|
2008-10-21 19:03:48 +00:00
|
|
|
.policy = nl80211_policy,
|
2009-01-13 14:03:29 +00:00
|
|
|
.flags = GENL_ADMIN_PERM,
|
2010-12-03 08:20:44 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
2010-10-04 19:36:35 +00:00
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2009-01-13 14:03:29 +00:00
|
|
|
},
|
2009-02-10 20:25:55 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_TRIGGER_SCAN,
|
|
|
|
.doit = nl80211_trigger_scan,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
2012-06-18 17:17:03 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_WDEV_UP |
|
2010-10-04 19:36:35 +00:00
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2009-02-10 20:25:55 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_GET_SCAN,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.dumpit = nl80211_dump_scan,
|
|
|
|
},
|
2011-05-11 14:09:35 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_START_SCHED_SCAN,
|
|
|
|
.doit = nl80211_start_sched_scan,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_STOP_SCHED_SCAN,
|
|
|
|
.doit = nl80211_stop_sched_scan,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
|
|
|
},
|
2009-03-19 11:39:22 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_AUTHENTICATE,
|
|
|
|
.doit = nl80211_authenticate,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
2010-10-04 19:14:05 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
2014-09-10 12:00:16 +00:00
|
|
|
NL80211_FLAG_NEED_RTNL |
|
|
|
|
NL80211_FLAG_CLEAR_SKB,
|
2009-03-19 11:39:22 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_ASSOCIATE,
|
|
|
|
.doit = nl80211_associate,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
2010-10-04 19:14:05 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
2010-10-04 19:36:35 +00:00
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2009-03-19 11:39:22 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_DEAUTHENTICATE,
|
|
|
|
.doit = nl80211_deauthenticate,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
2010-10-04 19:14:05 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
2010-10-04 19:36:35 +00:00
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2009-03-19 11:39:22 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_DISASSOCIATE,
|
|
|
|
.doit = nl80211_disassociate,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
2010-10-04 19:14:05 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
2010-10-04 19:36:35 +00:00
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2009-03-19 11:39:22 +00:00
|
|
|
},
|
2009-04-19 19:24:32 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_JOIN_IBSS,
|
|
|
|
.doit = nl80211_join_ibss,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
2010-10-04 19:14:05 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
2010-10-04 19:36:35 +00:00
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2009-04-19 19:24:32 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_LEAVE_IBSS,
|
|
|
|
.doit = nl80211_leave_ibss,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
2010-10-04 19:14:05 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
2010-10-04 19:36:35 +00:00
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2009-04-19 19:24:32 +00:00
|
|
|
},
|
2009-07-01 19:26:51 +00:00
|
|
|
#ifdef CONFIG_NL80211_TESTMODE
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_TESTMODE,
|
|
|
|
.doit = nl80211_testmode_do,
|
2011-05-20 16:05:54 +00:00
|
|
|
.dumpit = nl80211_testmode_dump,
|
2009-07-01 19:26:51 +00:00
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
2010-10-04 19:36:35 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_WIPHY |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2009-07-01 19:26:51 +00:00
|
|
|
},
|
|
|
|
#endif
|
2009-07-01 19:26:54 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_CONNECT,
|
|
|
|
.doit = nl80211_connect,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
2010-10-04 19:14:05 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
2010-10-04 19:36:35 +00:00
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2009-07-01 19:26:54 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_DISCONNECT,
|
|
|
|
.doit = nl80211_disconnect,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
2010-10-04 19:14:05 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
2010-10-04 19:36:35 +00:00
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2009-07-01 19:26:54 +00:00
|
|
|
},
|
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,
|
2010-10-04 19:36:35 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_WIPHY |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2009-07-13 22:33:35 +00:00
|
|
|
},
|
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,
|
2012-04-02 08:51:55 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
2010-10-04 19:36:35 +00:00
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2009-11-24 22:59:15 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_DEL_PMKSA,
|
|
|
|
.doit = nl80211_setdel_pmksa,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
2012-04-02 08:51:55 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
2010-10-04 19:36:35 +00:00
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2009-11-24 22:59:15 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_FLUSH_PMKSA,
|
|
|
|
.doit = nl80211_flush_pmksa,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
2012-04-02 08:51:55 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
2010-10-04 19:36:35 +00:00
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2009-11-24 22:59:15 +00:00
|
|
|
},
|
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,
|
2012-06-15 13:30:18 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_WDEV_UP |
|
2010-10-04 19:36:35 +00:00
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2009-12-23 12:15:41 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
|
|
|
|
.doit = nl80211_cancel_remain_on_channel,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
2012-06-15 13:30:18 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_WDEV_UP |
|
2010-10-04 19:36:35 +00:00
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2009-12-23 12:15:41 +00:00
|
|
|
},
|
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-10-04 19:36:35 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2009-12-29 10:59:45 +00:00
|
|
|
},
|
2010-02-15 10:53:10 +00:00
|
|
|
{
|
2010-08-12 13:38:38 +00:00
|
|
|
.cmd = NL80211_CMD_REGISTER_FRAME,
|
|
|
|
.doit = nl80211_register_mgmt,
|
2010-02-15 10:53:10 +00:00
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
2012-06-15 13:30:18 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_WDEV |
|
2010-10-04 19:36:35 +00:00
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2010-02-15 10:53:10 +00:00
|
|
|
},
|
|
|
|
{
|
2010-08-12 13:38:38 +00:00
|
|
|
.cmd = NL80211_CMD_FRAME,
|
|
|
|
.doit = nl80211_tx_mgmt,
|
2010-02-15 10:53:10 +00:00
|
|
|
.policy = nl80211_policy,
|
2010-11-25 09:02:29 +00:00
|
|
|
.flags = GENL_ADMIN_PERM,
|
2012-06-15 13:30:18 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_WDEV_UP |
|
2010-11-25 09:02:29 +00:00
|
|
|
NL80211_FLAG_NEED_RTNL,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_FRAME_WAIT_CANCEL,
|
|
|
|
.doit = nl80211_tx_mgmt_cancel_wait,
|
|
|
|
.policy = nl80211_policy,
|
2010-02-15 10:53:10 +00:00
|
|
|
.flags = GENL_ADMIN_PERM,
|
2012-06-15 13:30:18 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_WDEV_UP |
|
2010-10-04 19:36:35 +00:00
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2010-02-15 10:53:10 +00:00
|
|
|
},
|
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,
|
2010-10-04 19:36:35 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
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_GET_POWER_SAVE,
|
|
|
|
.doit = nl80211_get_power_save,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
/* can be retrieved by unprivileged users */
|
2010-10-04 19:36:35 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
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
|
|
|
},
|
2010-03-23 07:02:33 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_SET_CQM,
|
|
|
|
.doit = nl80211_set_cqm,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
2010-10-04 19:36:35 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2010-03-23 07:02:33 +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
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_SET_CHANNEL,
|
|
|
|
.doit = nl80211_set_channel,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
2010-10-04 19:36:35 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
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
|
|
|
},
|
2010-10-01 17:54:28 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_SET_WDS_PEER,
|
|
|
|
.doit = nl80211_set_wds_peer,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
2010-10-07 11:10:30 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2010-10-01 17:54:28 +00:00
|
|
|
},
|
2010-12-03 08:20:44 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_JOIN_MESH,
|
|
|
|
.doit = nl80211_join_mesh,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_LEAVE_MESH,
|
|
|
|
.doit = nl80211_leave_mesh,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
|
|
|
},
|
2014-11-03 09:33:18 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_JOIN_OCB,
|
|
|
|
.doit = nl80211_join_ocb,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_LEAVE_OCB,
|
|
|
|
.doit = nl80211_leave_ocb,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
|
|
|
},
|
2012-06-27 07:23:48 +00:00
|
|
|
#ifdef CONFIG_PM
|
2011-05-04 13:37:28 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_GET_WOWLAN,
|
|
|
|
.doit = nl80211_get_wowlan,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
/* can be retrieved by unprivileged users */
|
|
|
|
.internal_flags = NL80211_FLAG_NEED_WIPHY |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_SET_WOWLAN,
|
|
|
|
.doit = nl80211_set_wowlan,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
.internal_flags = NL80211_FLAG_NEED_WIPHY |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
|
|
|
},
|
2012-06-27 07:23:48 +00:00
|
|
|
#endif
|
2011-07-05 14:35:40 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_SET_REKEY_OFFLOAD,
|
|
|
|
.doit = nl80211_set_rekey_data,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
2014-09-10 12:00:16 +00:00
|
|
|
NL80211_FLAG_NEED_RTNL |
|
|
|
|
NL80211_FLAG_CLEAR_SKB,
|
2011-07-05 14:35:40 +00:00
|
|
|
},
|
2011-09-28 11:12:50 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_TDLS_MGMT,
|
|
|
|
.doit = nl80211_tdls_mgmt,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_TDLS_OPER,
|
|
|
|
.doit = nl80211_tdls_oper,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
|
|
|
},
|
2011-11-04 10:18:12 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_UNEXPECTED_FRAME,
|
|
|
|
.doit = nl80211_register_unexpected_frame,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
|
|
|
},
|
2011-11-04 10:18:15 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_PROBE_CLIENT,
|
|
|
|
.doit = nl80211_probe_client,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
2012-04-02 08:51:55 +00:00
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
2011-11-04 10:18:15 +00:00
|
|
|
NL80211_FLAG_NEED_RTNL,
|
|
|
|
},
|
2011-11-04 10:18:17 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_REGISTER_BEACONS,
|
|
|
|
.doit = nl80211_register_beacons,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
.internal_flags = NL80211_FLAG_NEED_WIPHY |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
|
|
|
},
|
2011-11-18 13:20:43 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_SET_NOACK_MAP,
|
|
|
|
.doit = nl80211_set_noack_map,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
|
|
|
},
|
2012-06-15 22:19:54 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_START_P2P_DEVICE,
|
|
|
|
.doit = nl80211_start_p2p_device,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
.internal_flags = NL80211_FLAG_NEED_WDEV |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_STOP_P2P_DEVICE,
|
|
|
|
.doit = nl80211_stop_p2p_device,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
.internal_flags = NL80211_FLAG_NEED_WDEV_UP |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
|
|
|
},
|
2012-11-02 12:27:48 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_SET_MCAST_RATE,
|
|
|
|
.doit = nl80211_set_mcast_rate,
|
2013-01-18 05:48:45 +00:00
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_SET_MAC_ACL,
|
|
|
|
.doit = nl80211_set_mac_acl,
|
2012-11-02 12:27:48 +00:00
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
|
|
|
},
|
2013-02-08 17:16:19 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_RADAR_DETECT,
|
|
|
|
.doit = nl80211_start_radar_detection,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
|
|
|
},
|
2013-02-14 15:19:38 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_GET_PROTOCOL_FEATURES,
|
|
|
|
.doit = nl80211_get_protocol_features,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
},
|
2013-02-27 15:14:27 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_UPDATE_FT_IES,
|
|
|
|
.doit = nl80211_update_ft_ies,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
|
|
|
},
|
2013-04-18 13:49:00 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_CRIT_PROTOCOL_START,
|
|
|
|
.doit = nl80211_crit_protocol_start,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
.internal_flags = NL80211_FLAG_NEED_WDEV_UP |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_CRIT_PROTOCOL_STOP,
|
|
|
|
.doit = nl80211_crit_protocol_stop,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
.internal_flags = NL80211_FLAG_NEED_WDEV_UP |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2013-06-28 18:51:26 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_GET_COALESCE,
|
|
|
|
.doit = nl80211_get_coalesce,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.internal_flags = NL80211_FLAG_NEED_WIPHY |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_SET_COALESCE,
|
|
|
|
.doit = nl80211_set_coalesce,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
.internal_flags = NL80211_FLAG_NEED_WIPHY |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
2013-07-11 14:09:05 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_CHANNEL_SWITCH,
|
|
|
|
.doit = nl80211_channel_switch,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
|
|
|
},
|
2013-11-13 12:37:47 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_VENDOR,
|
|
|
|
.doit = nl80211_vendor_cmd,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
.internal_flags = NL80211_FLAG_NEED_WIPHY |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
|
|
|
},
|
2013-12-17 07:01:30 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_SET_QOS_MAP,
|
|
|
|
.doit = nl80211_set_qos_map,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
|
|
|
},
|
2014-09-09 19:55:35 +00:00
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_ADD_TX_TS,
|
|
|
|
.doit = nl80211_add_tx_ts,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.cmd = NL80211_CMD_DEL_TX_TS,
|
|
|
|
.doit = nl80211_del_tx_ts,
|
|
|
|
.policy = nl80211_policy,
|
|
|
|
.flags = GENL_ADMIN_PERM,
|
|
|
|
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
|
|
|
NL80211_FLAG_NEED_RTNL,
|
|
|
|
},
|
2007-09-20 17:09:35 +00:00
|
|
|
};
|
2009-12-23 12:15:41 +00:00
|
|
|
|
2007-09-20 17:09:35 +00:00
|
|
|
/* notification functions */
|
|
|
|
|
2014-05-26 11:52:25 +00:00
|
|
|
void nl80211_notify_wiphy(struct cfg80211_registered_device *rdev,
|
|
|
|
enum nl80211_commands cmd)
|
2007-09-20 17:09:35 +00:00
|
|
|
{
|
|
|
|
struct sk_buff *msg;
|
2013-06-19 08:57:22 +00:00
|
|
|
struct nl80211_dump_wiphy_state state = {};
|
2007-09-20 17:09:35 +00:00
|
|
|
|
2014-05-26 11:52:25 +00:00
|
|
|
WARN_ON(cmd != NL80211_CMD_NEW_WIPHY &&
|
|
|
|
cmd != NL80211_CMD_DEL_WIPHY);
|
|
|
|
|
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;
|
|
|
|
|
2014-05-26 11:52:25 +00:00
|
|
|
if (nl80211_send_wiphy(rdev, cmd, msg, 0, 0, 0, &state) < 0) {
|
2007-09-20 17:09:35 +00:00
|
|
|
nlmsg_free(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-11-19 14:19:38 +00:00
|
|
|
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
|
2013-11-19 14:19:39 +00:00
|
|
|
NL80211_MCGRP_CONFIG, 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;
|
|
|
|
|
|
|
|
if (WARN_ON(!req))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
nest = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS);
|
|
|
|
if (!nest)
|
|
|
|
goto nla_put_failure;
|
2012-03-29 08:41:26 +00:00
|
|
|
for (i = 0; i < req->n_ssids; i++) {
|
|
|
|
if (nla_put(msg, i, req->ssids[i].ssid_len, req->ssids[i].ssid))
|
|
|
|
goto nla_put_failure;
|
|
|
|
}
|
2009-05-24 14:43:15 +00:00
|
|
|
nla_nest_end(msg, nest);
|
|
|
|
|
|
|
|
nest = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
|
|
|
|
if (!nest)
|
|
|
|
goto nla_put_failure;
|
2012-03-29 08:41:26 +00:00
|
|
|
for (i = 0; i < req->n_channels; i++) {
|
|
|
|
if (nla_put_u32(msg, i, req->channels[i]->center_freq))
|
|
|
|
goto nla_put_failure;
|
|
|
|
}
|
2009-05-24 14:43:15 +00:00
|
|
|
nla_nest_end(msg, nest);
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (req->ie &&
|
|
|
|
nla_put(msg, NL80211_ATTR_IE, req->ie_len, req->ie))
|
|
|
|
goto nla_put_failure;
|
2009-05-24 14:43:15 +00:00
|
|
|
|
2013-10-25 09:05:22 +00:00
|
|
|
if (req->flags &&
|
|
|
|
nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, req->flags))
|
|
|
|
goto nla_put_failure;
|
2012-10-12 04:03:31 +00:00
|
|
|
|
2009-05-24 14:43:15 +00:00
|
|
|
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,
|
2012-06-18 17:17:03 +00:00
|
|
|
struct wireless_dev *wdev,
|
2012-09-07 20:12:54 +00:00
|
|
|
u32 portid, u32 seq, int flags,
|
2009-06-16 17:56:42 +00:00
|
|
|
u32 cmd)
|
2009-02-10 20:25:55 +00:00
|
|
|
{
|
|
|
|
void *hdr;
|
|
|
|
|
2012-09-07 20:12:54 +00:00
|
|
|
hdr = nl80211hdr_put(msg, portid, seq, flags, cmd);
|
2009-02-10 20:25:55 +00:00
|
|
|
if (!hdr)
|
|
|
|
return -1;
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
|
2012-06-18 17:17:03 +00:00
|
|
|
(wdev->netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX,
|
|
|
|
wdev->netdev->ifindex)) ||
|
|
|
|
nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)))
|
2012-03-29 08:41:26 +00:00
|
|
|
goto nla_put_failure;
|
2009-02-10 20:25:55 +00:00
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2011-05-11 14:09:35 +00:00
|
|
|
static int
|
|
|
|
nl80211_send_sched_scan_msg(struct sk_buff *msg,
|
|
|
|
struct cfg80211_registered_device *rdev,
|
|
|
|
struct net_device *netdev,
|
2012-09-07 20:12:54 +00:00
|
|
|
u32 portid, u32 seq, int flags, u32 cmd)
|
2011-05-11 14:09:35 +00:00
|
|
|
{
|
|
|
|
void *hdr;
|
|
|
|
|
2012-09-07 20:12:54 +00:00
|
|
|
hdr = nl80211hdr_put(msg, portid, seq, flags, cmd);
|
2011-05-11 14:09:35 +00:00
|
|
|
if (!hdr)
|
|
|
|
return -1;
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
|
|
|
|
nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex))
|
|
|
|
goto nla_put_failure;
|
2011-05-11 14:09:35 +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,
|
2012-06-18 17:17:03 +00:00
|
|
|
struct wireless_dev *wdev)
|
2009-06-16 17:56:42 +00:00
|
|
|
{
|
|
|
|
struct sk_buff *msg;
|
|
|
|
|
2012-06-28 03:57:45 +00:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
2009-06-16 17:56:42 +00:00
|
|
|
if (!msg)
|
|
|
|
return;
|
|
|
|
|
2012-06-18 17:17:03 +00:00
|
|
|
if (nl80211_send_scan_msg(msg, rdev, wdev, 0, 0, 0,
|
2009-06-16 17:56:42 +00:00
|
|
|
NL80211_CMD_TRIGGER_SCAN) < 0) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-11-19 14:19:38 +00:00
|
|
|
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
|
2013-11-19 14:19:39 +00:00
|
|
|
NL80211_MCGRP_SCAN, GFP_KERNEL);
|
2009-06-16 17:56:42 +00:00
|
|
|
}
|
|
|
|
|
cfg80211: send scan results from work queue
Due to the previous commit, when a scan finishes, it is in theory
possible to hit the following sequence:
1. interface starts being removed
2. scan is cancelled by driver and cfg80211 is notified
3. scan done work is scheduled
4. interface is removed completely, rdev->scan_req is freed,
event sent to userspace but scan done work remains pending
5. new scan is requested on another virtual interface
6. scan done work runs, freeing the still-running scan
To fix this situation, hang on to the scan done message and block
new scans while that is the case, and only send the message from
the work function, regardless of whether the scan_req is already
freed from interface removal. This makes step 5 above impossible
and changes step 6 to be
5. scan done work runs, sending the scan done message
As this can't work for wext, so we send the message immediately,
but this shouldn't be an issue since we still return -EBUSY.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2014-01-22 09:14:19 +00:00
|
|
|
struct sk_buff *nl80211_build_scan_msg(struct cfg80211_registered_device *rdev,
|
|
|
|
struct wireless_dev *wdev, bool aborted)
|
2009-02-10 20:25:55 +00:00
|
|
|
{
|
|
|
|
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)
|
cfg80211: send scan results from work queue
Due to the previous commit, when a scan finishes, it is in theory
possible to hit the following sequence:
1. interface starts being removed
2. scan is cancelled by driver and cfg80211 is notified
3. scan done work is scheduled
4. interface is removed completely, rdev->scan_req is freed,
event sent to userspace but scan done work remains pending
5. new scan is requested on another virtual interface
6. scan done work runs, freeing the still-running scan
To fix this situation, hang on to the scan done message and block
new scans while that is the case, and only send the message from
the work function, regardless of whether the scan_req is already
freed from interface removal. This makes step 5 above impossible
and changes step 6 to be
5. scan done work runs, sending the scan done message
As this can't work for wext, so we send the message immediately,
but this shouldn't be an issue since we still return -EBUSY.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2014-01-22 09:14:19 +00:00
|
|
|
return NULL;
|
2009-02-10 20:25:55 +00:00
|
|
|
|
2012-06-18 17:17:03 +00:00
|
|
|
if (nl80211_send_scan_msg(msg, rdev, wdev, 0, 0, 0,
|
cfg80211: send scan results from work queue
Due to the previous commit, when a scan finishes, it is in theory
possible to hit the following sequence:
1. interface starts being removed
2. scan is cancelled by driver and cfg80211 is notified
3. scan done work is scheduled
4. interface is removed completely, rdev->scan_req is freed,
event sent to userspace but scan done work remains pending
5. new scan is requested on another virtual interface
6. scan done work runs, freeing the still-running scan
To fix this situation, hang on to the scan done message and block
new scans while that is the case, and only send the message from
the work function, regardless of whether the scan_req is already
freed from interface removal. This makes step 5 above impossible
and changes step 6 to be
5. scan done work runs, sending the scan done message
As this can't work for wext, so we send the message immediately,
but this shouldn't be an issue since we still return -EBUSY.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2014-01-22 09:14:19 +00:00
|
|
|
aborted ? NL80211_CMD_SCAN_ABORTED :
|
|
|
|
NL80211_CMD_NEW_SCAN_RESULTS) < 0) {
|
2009-02-10 20:25:55 +00:00
|
|
|
nlmsg_free(msg);
|
cfg80211: send scan results from work queue
Due to the previous commit, when a scan finishes, it is in theory
possible to hit the following sequence:
1. interface starts being removed
2. scan is cancelled by driver and cfg80211 is notified
3. scan done work is scheduled
4. interface is removed completely, rdev->scan_req is freed,
event sent to userspace but scan done work remains pending
5. new scan is requested on another virtual interface
6. scan done work runs, freeing the still-running scan
To fix this situation, hang on to the scan done message and block
new scans while that is the case, and only send the message from
the work function, regardless of whether the scan_req is already
freed from interface removal. This makes step 5 above impossible
and changes step 6 to be
5. scan done work runs, sending the scan done message
As this can't work for wext, so we send the message immediately,
but this shouldn't be an issue since we still return -EBUSY.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2014-01-22 09:14:19 +00:00
|
|
|
return NULL;
|
2009-02-10 20:25:55 +00:00
|
|
|
}
|
|
|
|
|
cfg80211: send scan results from work queue
Due to the previous commit, when a scan finishes, it is in theory
possible to hit the following sequence:
1. interface starts being removed
2. scan is cancelled by driver and cfg80211 is notified
3. scan done work is scheduled
4. interface is removed completely, rdev->scan_req is freed,
event sent to userspace but scan done work remains pending
5. new scan is requested on another virtual interface
6. scan done work runs, freeing the still-running scan
To fix this situation, hang on to the scan done message and block
new scans while that is the case, and only send the message from
the work function, regardless of whether the scan_req is already
freed from interface removal. This makes step 5 above impossible
and changes step 6 to be
5. scan done work runs, sending the scan done message
As this can't work for wext, so we send the message immediately,
but this shouldn't be an issue since we still return -EBUSY.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2014-01-22 09:14:19 +00:00
|
|
|
return msg;
|
2009-02-10 20:25:55 +00:00
|
|
|
}
|
|
|
|
|
cfg80211: send scan results from work queue
Due to the previous commit, when a scan finishes, it is in theory
possible to hit the following sequence:
1. interface starts being removed
2. scan is cancelled by driver and cfg80211 is notified
3. scan done work is scheduled
4. interface is removed completely, rdev->scan_req is freed,
event sent to userspace but scan done work remains pending
5. new scan is requested on another virtual interface
6. scan done work runs, freeing the still-running scan
To fix this situation, hang on to the scan done message and block
new scans while that is the case, and only send the message from
the work function, regardless of whether the scan_req is already
freed from interface removal. This makes step 5 above impossible
and changes step 6 to be
5. scan done work runs, sending the scan done message
As this can't work for wext, so we send the message immediately,
but this shouldn't be an issue since we still return -EBUSY.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2014-01-22 09:14:19 +00:00
|
|
|
void nl80211_send_scan_result(struct cfg80211_registered_device *rdev,
|
|
|
|
struct sk_buff *msg)
|
2009-02-10 20:25:55 +00:00
|
|
|
{
|
|
|
|
if (!msg)
|
|
|
|
return;
|
|
|
|
|
2013-11-19 14:19:38 +00:00
|
|
|
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
|
2013-11-19 14:19:39 +00:00
|
|
|
NL80211_MCGRP_SCAN, GFP_KERNEL);
|
2009-02-10 20:25:55 +00:00
|
|
|
}
|
|
|
|
|
2011-05-11 14:09:35 +00:00
|
|
|
void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,
|
|
|
|
struct net_device *netdev)
|
|
|
|
{
|
|
|
|
struct sk_buff *msg;
|
|
|
|
|
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
|
|
if (!msg)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0,
|
|
|
|
NL80211_CMD_SCHED_SCAN_RESULTS) < 0) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-11-19 14:19:38 +00:00
|
|
|
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
|
2013-11-19 14:19:39 +00:00
|
|
|
NL80211_MCGRP_SCAN, GFP_KERNEL);
|
2011-05-11 14:09:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
|
|
|
|
struct net_device *netdev, u32 cmd)
|
|
|
|
{
|
|
|
|
struct sk_buff *msg;
|
|
|
|
|
2012-06-28 03:57:45 +00:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
2011-05-11 14:09:35 +00:00
|
|
|
if (!msg)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, cmd) < 0) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-11-19 14:19:38 +00:00
|
|
|
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
|
2013-11-19 14:19:39 +00:00
|
|
|
NL80211_MCGRP_SCAN, GFP_KERNEL);
|
2011-05-11 14:09:35 +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 */
|
2012-03-29 08:41:26 +00:00
|
|
|
if (nla_put_u8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator))
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
if (request->alpha2[0] == '0' && request->alpha2[1] == '0') {
|
|
|
|
if (nla_put_u8(msg, NL80211_ATTR_REG_TYPE,
|
|
|
|
NL80211_REGDOM_TYPE_WORLD))
|
|
|
|
goto nla_put_failure;
|
|
|
|
} else if (request->alpha2[0] == '9' && request->alpha2[1] == '9') {
|
|
|
|
if (nla_put_u8(msg, NL80211_ATTR_REG_TYPE,
|
|
|
|
NL80211_REGDOM_TYPE_CUSTOM_WORLD))
|
|
|
|
goto nla_put_failure;
|
|
|
|
} else if ((request->alpha2[0] == '9' && request->alpha2[1] == '8') ||
|
|
|
|
request->intersect) {
|
|
|
|
if (nla_put_u8(msg, NL80211_ATTR_REG_TYPE,
|
|
|
|
NL80211_REGDOM_TYPE_INTERSECTION))
|
|
|
|
goto nla_put_failure;
|
|
|
|
} else {
|
|
|
|
if (nla_put_u8(msg, NL80211_ATTR_REG_TYPE,
|
|
|
|
NL80211_REGDOM_TYPE_COUNTRY) ||
|
|
|
|
nla_put_string(msg, NL80211_ATTR_REG_ALPHA2,
|
|
|
|
request->alpha2))
|
|
|
|
goto nla_put_failure;
|
|
|
|
}
|
|
|
|
|
2012-12-03 17:23:37 +00:00
|
|
|
if (request->wiphy_idx != WIPHY_IDX_INVALID &&
|
2012-03-29 08:41:26 +00:00
|
|
|
nla_put_u32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx))
|
|
|
|
goto nla_put_failure;
|
2009-03-10 02:07:42 +00:00
|
|
|
|
2011-10-22 17:05:51 +00:00
|
|
|
genlmsg_end(msg, hdr);
|
2009-03-10 02:07:42 +00:00
|
|
|
|
2009-07-25 08:54:13 +00:00
|
|
|
rcu_read_lock();
|
2013-11-19 14:19:38 +00:00
|
|
|
genlmsg_multicast_allns(&nl80211_fam, msg, 0,
|
2013-11-19 14:19:39 +00:00
|
|
|
NL80211_MCGRP_REGULATORY, GFP_ATOMIC);
|
2009-07-25 08:54:13 +00:00
|
|
|
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,
|
2014-09-09 14:09:45 +00:00
|
|
|
enum nl80211_commands cmd, gfp_t gfp,
|
|
|
|
int uapsd_queues)
|
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;
|
|
|
|
}
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (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))
|
|
|
|
goto nla_put_failure;
|
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
|
|
|
|
2014-09-09 14:09:45 +00:00
|
|
|
if (uapsd_queues >= 0) {
|
|
|
|
struct nlattr *nla_wmm =
|
|
|
|
nla_nest_start(msg, NL80211_ATTR_STA_WME);
|
|
|
|
if (!nla_wmm)
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
if (nla_put_u8(msg, NL80211_STA_WME_UAPSD_QUEUES,
|
|
|
|
uapsd_queues))
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
nla_nest_end(msg, nla_wmm);
|
|
|
|
}
|
|
|
|
|
2011-10-22 17:05:51 +00:00
|
|
|
genlmsg_end(msg, hdr);
|
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
|
|
|
|
2013-11-19 14:19:38 +00:00
|
|
|
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
|
2013-11-19 14:19:39 +00:00
|
|
|
NL80211_MCGRP_MLME, 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,
|
2014-09-09 14:09:45 +00:00
|
|
|
NL80211_CMD_AUTHENTICATE, gfp, -1);
|
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,
|
2014-09-09 14:09:45 +00:00
|
|
|
size_t len, gfp_t gfp, int uapsd_queues)
|
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,
|
2014-09-09 14:09:45 +00:00
|
|
|
NL80211_CMD_ASSOCIATE, gfp, uapsd_queues);
|
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,
|
2014-09-09 14:09:45 +00:00
|
|
|
NL80211_CMD_DEAUTHENTICATE, gfp, -1);
|
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,
|
2014-09-09 14:09:45 +00:00
|
|
|
NL80211_CMD_DISASSOCIATE, gfp, -1);
|
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
|
|
|
}
|
|
|
|
|
2013-05-15 22:55:00 +00:00
|
|
|
void cfg80211_rx_unprot_mlme_mgmt(struct net_device *dev, const u8 *buf,
|
|
|
|
size_t len)
|
2010-12-15 22:52:40 +00:00
|
|
|
{
|
2013-02-22 21:05:20 +00:00
|
|
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
|
struct wiphy *wiphy = wdev->wiphy;
|
2014-04-21 04:53:03 +00:00
|
|
|
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
2013-05-15 22:55:00 +00:00
|
|
|
const struct ieee80211_mgmt *mgmt = (void *)buf;
|
|
|
|
u32 cmd;
|
2013-02-22 21:05:20 +00:00
|
|
|
|
2013-05-15 22:55:00 +00:00
|
|
|
if (WARN_ON(len < 2))
|
|
|
|
return;
|
2010-12-15 22:52:40 +00:00
|
|
|
|
2013-05-15 22:55:00 +00:00
|
|
|
if (ieee80211_is_deauth(mgmt->frame_control))
|
|
|
|
cmd = NL80211_CMD_UNPROT_DEAUTHENTICATE;
|
|
|
|
else
|
|
|
|
cmd = NL80211_CMD_UNPROT_DISASSOCIATE;
|
2013-02-22 21:05:20 +00:00
|
|
|
|
2013-05-15 22:55:00 +00:00
|
|
|
trace_cfg80211_rx_unprot_mlme_mgmt(dev, buf, len);
|
2014-09-09 14:09:45 +00:00
|
|
|
nl80211_send_mlme_event(rdev, dev, buf, len, cmd, GFP_ATOMIC, -1);
|
2010-12-15 22:52:40 +00:00
|
|
|
}
|
2013-05-15 22:55:00 +00:00
|
|
|
EXPORT_SYMBOL(cfg80211_rx_unprot_mlme_mgmt);
|
2010-12-15 22:52:40 +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;
|
|
|
|
}
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (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))
|
|
|
|
goto nla_put_failure;
|
2009-04-22 18:38:25 +00:00
|
|
|
|
2011-10-22 17:05:51 +00:00
|
|
|
genlmsg_end(msg, hdr);
|
2009-04-22 18:38:25 +00:00
|
|
|
|
2013-11-19 14:19:38 +00:00
|
|
|
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
|
2013-11-19 14:19:39 +00:00
|
|
|
NL80211_MCGRP_MLME, 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;
|
|
|
|
|
2012-06-28 03:57:45 +00:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
2009-07-01 19:26:54 +00:00
|
|
|
if (!msg)
|
|
|
|
return;
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONNECT);
|
|
|
|
if (!hdr) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
|
|
|
|
nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
|
|
|
|
(bssid && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid)) ||
|
|
|
|
nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, status) ||
|
|
|
|
(req_ie &&
|
|
|
|
nla_put(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie)) ||
|
|
|
|
(resp_ie &&
|
|
|
|
nla_put(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie)))
|
|
|
|
goto nla_put_failure;
|
2009-07-01 19:26:54 +00:00
|
|
|
|
2011-10-22 17:05:51 +00:00
|
|
|
genlmsg_end(msg, hdr);
|
2009-07-01 19:26:54 +00:00
|
|
|
|
2013-11-19 14:19:38 +00:00
|
|
|
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
|
2013-11-19 14:19:39 +00:00
|
|
|
NL80211_MCGRP_MLME, 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;
|
|
|
|
|
2012-06-28 03:57:45 +00:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
2009-07-01 19:26:54 +00:00
|
|
|
if (!msg)
|
|
|
|
return;
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ROAM);
|
|
|
|
if (!hdr) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (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) ||
|
|
|
|
(req_ie &&
|
|
|
|
nla_put(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie)) ||
|
|
|
|
(resp_ie &&
|
|
|
|
nla_put(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie)))
|
|
|
|
goto nla_put_failure;
|
2009-07-01 19:26:54 +00:00
|
|
|
|
2011-10-22 17:05:51 +00:00
|
|
|
genlmsg_end(msg, hdr);
|
2009-07-01 19:26:54 +00:00
|
|
|
|
2013-11-19 14:19:38 +00:00
|
|
|
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
|
2013-11-19 14:19:39 +00:00
|
|
|
NL80211_MCGRP_MLME, 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;
|
|
|
|
|
2012-06-28 03:57:45 +00:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, 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;
|
|
|
|
}
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
|
|
|
|
nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
|
|
|
|
(from_ap && reason &&
|
|
|
|
nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason)) ||
|
|
|
|
(from_ap &&
|
|
|
|
nla_put_flag(msg, NL80211_ATTR_DISCONNECTED_BY_AP)) ||
|
|
|
|
(ie && nla_put(msg, NL80211_ATTR_IE, ie_len, ie)))
|
|
|
|
goto nla_put_failure;
|
2009-07-01 19:26:54 +00:00
|
|
|
|
2011-10-22 17:05:51 +00:00
|
|
|
genlmsg_end(msg, hdr);
|
2009-07-01 19:26:54 +00:00
|
|
|
|
2013-11-19 14:19:38 +00:00
|
|
|
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
|
2013-11-19 14:19:39 +00:00
|
|
|
NL80211_MCGRP_MLME, 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;
|
|
|
|
}
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (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))
|
|
|
|
goto nla_put_failure;
|
2009-04-19 19:24:32 +00:00
|
|
|
|
2011-10-22 17:05:51 +00:00
|
|
|
genlmsg_end(msg, hdr);
|
2009-04-19 19:24:32 +00:00
|
|
|
|
2013-11-19 14:19:38 +00:00
|
|
|
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
|
2013-11-19 14:19:39 +00:00
|
|
|
NL80211_MCGRP_MLME, gfp);
|
2009-04-19 19:24:32 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
nlmsg_free(msg);
|
|
|
|
}
|
|
|
|
|
2013-02-22 21:05:20 +00:00
|
|
|
void cfg80211_notify_new_peer_candidate(struct net_device *dev, const u8 *addr,
|
|
|
|
const u8* ie, u8 ie_len, gfp_t gfp)
|
2011-04-07 22:08:34 +00:00
|
|
|
{
|
2013-02-22 21:05:20 +00:00
|
|
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
2014-04-21 04:53:03 +00:00
|
|
|
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
2011-04-07 22:08:34 +00:00
|
|
|
struct sk_buff *msg;
|
|
|
|
void *hdr;
|
|
|
|
|
2013-02-22 21:05:20 +00:00
|
|
|
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_MESH_POINT))
|
|
|
|
return;
|
|
|
|
|
|
|
|
trace_cfg80211_notify_new_peer_candidate(dev, addr);
|
|
|
|
|
2011-04-07 22:08:34 +00:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
|
|
|
if (!msg)
|
|
|
|
return;
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NEW_PEER_CANDIDATE);
|
|
|
|
if (!hdr) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
|
2013-02-22 21:05:20 +00:00
|
|
|
nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
|
|
|
|
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
|
2012-03-29 08:41:26 +00:00
|
|
|
(ie_len && ie &&
|
|
|
|
nla_put(msg, NL80211_ATTR_IE, ie_len , ie)))
|
|
|
|
goto nla_put_failure;
|
2011-04-07 22:08:34 +00:00
|
|
|
|
2011-10-22 17:05:51 +00:00
|
|
|
genlmsg_end(msg, hdr);
|
2011-04-07 22:08:34 +00:00
|
|
|
|
2013-11-19 14:19:38 +00:00
|
|
|
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
|
2013-11-19 14:19:39 +00:00
|
|
|
NL80211_MCGRP_MLME, gfp);
|
2011-04-07 22:08:34 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
nlmsg_free(msg);
|
|
|
|
}
|
2013-02-22 21:05:20 +00:00
|
|
|
EXPORT_SYMBOL(cfg80211_notify_new_peer_candidate);
|
2011-04-07 22:08:34 +00:00
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
|
|
|
|
nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
|
|
|
|
(addr && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) ||
|
|
|
|
nla_put_u32(msg, NL80211_ATTR_KEY_TYPE, key_type) ||
|
|
|
|
(key_id != -1 &&
|
|
|
|
nla_put_u8(msg, NL80211_ATTR_KEY_IDX, key_id)) ||
|
|
|
|
(tsc && nla_put(msg, NL80211_ATTR_KEY_SEQ, 6, tsc)))
|
|
|
|
goto nla_put_failure;
|
2009-03-27 19:59:49 +00:00
|
|
|
|
2011-10-22 17:05:51 +00:00
|
|
|
genlmsg_end(msg, hdr);
|
2009-03-27 19:59:49 +00:00
|
|
|
|
2013-11-19 14:19:38 +00:00
|
|
|
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
|
2013-11-19 14:19:39 +00:00
|
|
|
NL80211_MCGRP_MLME, 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
|
|
|
|
*/
|
2012-03-29 08:41:26 +00:00
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy)))
|
|
|
|
goto nla_put_failure;
|
2009-04-02 18:08:09 +00:00
|
|
|
|
|
|
|
/* Before */
|
|
|
|
nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE);
|
|
|
|
if (!nl_freq)
|
|
|
|
goto nla_put_failure;
|
2013-02-18 22:54:36 +00:00
|
|
|
if (nl80211_msg_put_channel(msg, channel_before, false))
|
2009-04-02 18:08:09 +00:00
|
|
|
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;
|
2013-02-18 22:54:36 +00:00
|
|
|
if (nl80211_msg_put_channel(msg, channel_after, false))
|
2009-04-02 18:08:09 +00:00
|
|
|
goto nla_put_failure;
|
|
|
|
nla_nest_end(msg, nl_freq);
|
|
|
|
|
2011-10-22 17:05:51 +00:00
|
|
|
genlmsg_end(msg, hdr);
|
2009-04-02 18:08:09 +00:00
|
|
|
|
2009-07-13 22:33:35 +00:00
|
|
|
rcu_read_lock();
|
2013-11-19 14:19:38 +00:00
|
|
|
genlmsg_multicast_allns(&nl80211_fam, msg, 0,
|
2013-11-19 14:19:39 +00:00
|
|
|
NL80211_MCGRP_REGULATORY, GFP_ATOMIC);
|
2009-07-13 22:33:35 +00:00
|
|
|
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,
|
2012-06-15 13:30:18 +00:00
|
|
|
struct wireless_dev *wdev, u64 cookie,
|
2009-12-23 12:15:41 +00:00
|
|
|
struct ieee80211_channel *chan,
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
|
2012-06-15 13:30:18 +00:00
|
|
|
(wdev->netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX,
|
|
|
|
wdev->netdev->ifindex)) ||
|
2012-07-17 09:53:12 +00:00
|
|
|
nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)) ||
|
2012-03-29 08:41:26 +00:00
|
|
|
nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq) ||
|
2012-11-08 17:31:02 +00:00
|
|
|
nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
|
|
|
|
NL80211_CHAN_NO_HT) ||
|
2012-03-29 08:41:26 +00:00
|
|
|
nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie))
|
|
|
|
goto nla_put_failure;
|
2009-12-23 12:15:41 +00:00
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (cmd == NL80211_CMD_REMAIN_ON_CHANNEL &&
|
|
|
|
nla_put_u32(msg, NL80211_ATTR_DURATION, duration))
|
|
|
|
goto nla_put_failure;
|
2009-12-23 12:15:41 +00:00
|
|
|
|
2011-10-22 17:05:51 +00:00
|
|
|
genlmsg_end(msg, hdr);
|
2009-12-23 12:15:41 +00:00
|
|
|
|
2013-11-19 14:19:38 +00:00
|
|
|
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
|
2013-11-19 14:19:39 +00:00
|
|
|
NL80211_MCGRP_MLME, gfp);
|
2009-12-23 12:15:41 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
nlmsg_free(msg);
|
|
|
|
}
|
|
|
|
|
2013-02-22 21:05:20 +00:00
|
|
|
void cfg80211_ready_on_channel(struct wireless_dev *wdev, u64 cookie,
|
|
|
|
struct ieee80211_channel *chan,
|
|
|
|
unsigned int duration, gfp_t gfp)
|
2009-12-23 12:15:41 +00:00
|
|
|
{
|
2013-02-22 21:05:20 +00:00
|
|
|
struct wiphy *wiphy = wdev->wiphy;
|
2014-04-21 04:53:03 +00:00
|
|
|
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
2013-02-22 21:05:20 +00:00
|
|
|
|
|
|
|
trace_cfg80211_ready_on_channel(wdev, cookie, chan, duration);
|
2009-12-23 12:15:41 +00:00
|
|
|
nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL,
|
2012-06-15 13:30:18 +00:00
|
|
|
rdev, wdev, cookie, chan,
|
2012-11-08 17:31:02 +00:00
|
|
|
duration, gfp);
|
2009-12-23 12:15:41 +00:00
|
|
|
}
|
2013-02-22 21:05:20 +00:00
|
|
|
EXPORT_SYMBOL(cfg80211_ready_on_channel);
|
2009-12-23 12:15:41 +00:00
|
|
|
|
2013-02-22 21:05:20 +00:00
|
|
|
void cfg80211_remain_on_channel_expired(struct wireless_dev *wdev, u64 cookie,
|
|
|
|
struct ieee80211_channel *chan,
|
|
|
|
gfp_t gfp)
|
2009-12-23 12:15:41 +00:00
|
|
|
{
|
2013-02-22 21:05:20 +00:00
|
|
|
struct wiphy *wiphy = wdev->wiphy;
|
2014-04-21 04:53:03 +00:00
|
|
|
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
2013-02-22 21:05:20 +00:00
|
|
|
|
|
|
|
trace_cfg80211_ready_on_channel_expired(wdev, cookie, chan);
|
2009-12-23 12:15:41 +00:00
|
|
|
nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
|
2012-11-08 17:31:02 +00:00
|
|
|
rdev, wdev, cookie, chan, 0, gfp);
|
2009-12-23 12:15:41 +00:00
|
|
|
}
|
2013-02-22 21:05:20 +00:00
|
|
|
EXPORT_SYMBOL(cfg80211_remain_on_channel_expired);
|
2009-12-23 12:15:41 +00:00
|
|
|
|
2013-02-22 21:05:20 +00:00
|
|
|
void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr,
|
|
|
|
struct station_info *sinfo, gfp_t gfp)
|
2009-12-23 12:15:44 +00:00
|
|
|
{
|
2013-02-22 21:05:20 +00:00
|
|
|
struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
|
2014-04-21 04:53:03 +00:00
|
|
|
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
2009-12-23 12:15:44 +00:00
|
|
|
struct sk_buff *msg;
|
|
|
|
|
2013-02-22 21:05:20 +00:00
|
|
|
trace_cfg80211_new_sta(dev, mac_addr, sinfo);
|
|
|
|
|
2012-06-28 03:57:45 +00:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
2009-12-23 12:15:44 +00:00
|
|
|
if (!msg)
|
|
|
|
return;
|
|
|
|
|
2012-03-15 17:25:41 +00:00
|
|
|
if (nl80211_send_station(msg, 0, 0, 0,
|
|
|
|
rdev, dev, mac_addr, sinfo) < 0) {
|
2009-12-23 12:15:44 +00:00
|
|
|
nlmsg_free(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-11-19 14:19:38 +00:00
|
|
|
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
|
2013-11-19 14:19:39 +00:00
|
|
|
NL80211_MCGRP_MLME, gfp);
|
2009-12-23 12:15:44 +00:00
|
|
|
}
|
2013-02-22 21:05:20 +00:00
|
|
|
EXPORT_SYMBOL(cfg80211_new_sta);
|
2009-12-23 12:15:44 +00:00
|
|
|
|
2013-02-22 21:05:20 +00:00
|
|
|
void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp)
|
2011-03-23 13:29:52 +00:00
|
|
|
{
|
2013-02-22 21:05:20 +00:00
|
|
|
struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
|
2014-04-21 04:53:03 +00:00
|
|
|
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
2011-03-23 13:29:52 +00:00
|
|
|
struct sk_buff *msg;
|
|
|
|
void *hdr;
|
|
|
|
|
2013-02-22 21:05:20 +00:00
|
|
|
trace_cfg80211_del_sta(dev, mac_addr);
|
|
|
|
|
2012-06-28 03:57:45 +00:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
2011-03-23 13:29:52 +00:00
|
|
|
if (!msg)
|
|
|
|
return;
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DEL_STATION);
|
|
|
|
if (!hdr) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
|
|
|
|
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr))
|
|
|
|
goto nla_put_failure;
|
2011-03-23 13:29:52 +00:00
|
|
|
|
2011-10-22 17:05:51 +00:00
|
|
|
genlmsg_end(msg, hdr);
|
2011-03-23 13:29:52 +00:00
|
|
|
|
2013-11-19 14:19:38 +00:00
|
|
|
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
|
2013-11-19 14:19:39 +00:00
|
|
|
NL80211_MCGRP_MLME, gfp);
|
2011-03-23 13:29:52 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
nlmsg_free(msg);
|
|
|
|
}
|
2013-02-22 21:05:20 +00:00
|
|
|
EXPORT_SYMBOL(cfg80211_del_sta);
|
2011-03-23 13:29:52 +00:00
|
|
|
|
2013-02-22 21:05:20 +00:00
|
|
|
void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr,
|
|
|
|
enum nl80211_connect_failed_reason reason,
|
|
|
|
gfp_t gfp)
|
2012-09-18 11:20:49 +00:00
|
|
|
{
|
2013-02-22 21:05:20 +00:00
|
|
|
struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
|
2014-04-21 04:53:03 +00:00
|
|
|
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
2012-09-18 11:20:49 +00:00
|
|
|
struct sk_buff *msg;
|
|
|
|
void *hdr;
|
|
|
|
|
|
|
|
msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
|
|
|
|
if (!msg)
|
|
|
|
return;
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONN_FAILED);
|
|
|
|
if (!hdr) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
|
|
|
|
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr) ||
|
|
|
|
nla_put_u32(msg, NL80211_ATTR_CONN_FAILED_REASON, reason))
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
genlmsg_end(msg, hdr);
|
|
|
|
|
2013-11-19 14:19:38 +00:00
|
|
|
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
|
2013-11-19 14:19:39 +00:00
|
|
|
NL80211_MCGRP_MLME, gfp);
|
2012-09-18 11:20:49 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
nlmsg_free(msg);
|
|
|
|
}
|
2013-02-22 21:05:20 +00:00
|
|
|
EXPORT_SYMBOL(cfg80211_conn_failed);
|
2012-09-18 11:20:49 +00:00
|
|
|
|
2011-11-04 10:18:19 +00:00
|
|
|
static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,
|
|
|
|
const u8 *addr, gfp_t gfp)
|
2011-11-04 10:18:12 +00:00
|
|
|
{
|
|
|
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
2014-04-21 04:53:03 +00:00
|
|
|
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
2011-11-04 10:18:12 +00:00
|
|
|
struct sk_buff *msg;
|
|
|
|
void *hdr;
|
2012-09-07 20:12:54 +00:00
|
|
|
u32 nlportid = ACCESS_ONCE(wdev->ap_unexpected_nlportid);
|
2011-11-04 10:18:12 +00:00
|
|
|
|
2012-09-07 20:12:54 +00:00
|
|
|
if (!nlportid)
|
2011-11-04 10:18:12 +00:00
|
|
|
return false;
|
|
|
|
|
|
|
|
msg = nlmsg_new(100, gfp);
|
|
|
|
if (!msg)
|
|
|
|
return true;
|
|
|
|
|
2011-11-04 10:18:19 +00:00
|
|
|
hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
|
2011-11-04 10:18:12 +00:00
|
|
|
if (!hdr) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
|
|
|
|
nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
|
|
|
|
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr))
|
|
|
|
goto nla_put_failure;
|
2011-11-04 10:18:12 +00:00
|
|
|
|
2013-06-04 10:46:03 +00:00
|
|
|
genlmsg_end(msg, hdr);
|
2012-09-07 20:12:54 +00:00
|
|
|
genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid);
|
2011-11-04 10:18:12 +00:00
|
|
|
return true;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-02-22 21:05:20 +00:00
|
|
|
bool cfg80211_rx_spurious_frame(struct net_device *dev,
|
|
|
|
const u8 *addr, gfp_t gfp)
|
2011-11-04 10:18:19 +00:00
|
|
|
{
|
2013-02-22 21:05:20 +00:00
|
|
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
|
bool ret;
|
|
|
|
|
|
|
|
trace_cfg80211_rx_spurious_frame(dev, addr);
|
|
|
|
|
|
|
|
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
|
|
|
|
wdev->iftype != NL80211_IFTYPE_P2P_GO)) {
|
|
|
|
trace_cfg80211_return_bool(false);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
ret = __nl80211_unexpected_frame(dev, NL80211_CMD_UNEXPECTED_FRAME,
|
|
|
|
addr, gfp);
|
|
|
|
trace_cfg80211_return_bool(ret);
|
|
|
|
return ret;
|
2011-11-04 10:18:19 +00:00
|
|
|
}
|
2013-02-22 21:05:20 +00:00
|
|
|
EXPORT_SYMBOL(cfg80211_rx_spurious_frame);
|
2011-11-04 10:18:19 +00:00
|
|
|
|
2013-02-22 21:05:20 +00:00
|
|
|
bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev,
|
|
|
|
const u8 *addr, gfp_t gfp)
|
2011-11-04 10:18:19 +00:00
|
|
|
{
|
2013-02-22 21:05:20 +00:00
|
|
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
|
bool ret;
|
|
|
|
|
|
|
|
trace_cfg80211_rx_unexpected_4addr_frame(dev, addr);
|
|
|
|
|
|
|
|
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
|
|
|
|
wdev->iftype != NL80211_IFTYPE_P2P_GO &&
|
|
|
|
wdev->iftype != NL80211_IFTYPE_AP_VLAN)) {
|
|
|
|
trace_cfg80211_return_bool(false);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
ret = __nl80211_unexpected_frame(dev,
|
|
|
|
NL80211_CMD_UNEXPECTED_4ADDR_FRAME,
|
|
|
|
addr, gfp);
|
|
|
|
trace_cfg80211_return_bool(ret);
|
|
|
|
return ret;
|
2011-11-04 10:18:19 +00:00
|
|
|
}
|
2013-02-22 21:05:20 +00:00
|
|
|
EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame);
|
2011-11-04 10:18:19 +00:00
|
|
|
|
2010-08-12 13:38:38 +00:00
|
|
|
int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
|
2012-09-07 20:12:54 +00:00
|
|
|
struct wireless_dev *wdev, u32 nlportid,
|
2012-03-05 21:18:41 +00:00
|
|
|
int freq, int sig_dbm,
|
2013-08-15 11:51:28 +00:00
|
|
|
const u8 *buf, size_t len, u32 flags, gfp_t gfp)
|
2010-02-15 10:53:10 +00:00
|
|
|
{
|
2012-06-15 13:30:18 +00:00
|
|
|
struct net_device *netdev = wdev->netdev;
|
2010-02-15 10:53:10 +00:00
|
|
|
struct sk_buff *msg;
|
|
|
|
void *hdr;
|
|
|
|
|
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
|
|
|
if (!msg)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2010-08-12 13:38:38 +00:00
|
|
|
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
|
2010-02-15 10:53:10 +00:00
|
|
|
if (!hdr) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
|
2012-06-15 13:30:18 +00:00
|
|
|
(netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX,
|
|
|
|
netdev->ifindex)) ||
|
2013-05-08 13:35:55 +00:00
|
|
|
nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)) ||
|
2012-03-29 08:41:26 +00:00
|
|
|
nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq) ||
|
|
|
|
(sig_dbm &&
|
|
|
|
nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)) ||
|
2013-08-15 11:51:28 +00:00
|
|
|
nla_put(msg, NL80211_ATTR_FRAME, len, buf) ||
|
|
|
|
(flags &&
|
|
|
|
nla_put_u32(msg, NL80211_ATTR_RXMGMT_FLAGS, flags)))
|
2012-03-29 08:41:26 +00:00
|
|
|
goto nla_put_failure;
|
2010-02-15 10:53:10 +00:00
|
|
|
|
2011-10-22 17:05:51 +00:00
|
|
|
genlmsg_end(msg, hdr);
|
2010-02-15 10:53:10 +00:00
|
|
|
|
2012-09-07 20:12:54 +00:00
|
|
|
return genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid);
|
2010-02-15 10:53:10 +00:00
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return -ENOBUFS;
|
|
|
|
}
|
|
|
|
|
2013-02-22 21:05:20 +00:00
|
|
|
void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie,
|
|
|
|
const u8 *buf, size_t len, bool ack, gfp_t gfp)
|
2010-02-15 10:53:10 +00:00
|
|
|
{
|
2013-02-22 21:05:20 +00:00
|
|
|
struct wiphy *wiphy = wdev->wiphy;
|
2014-04-21 04:53:03 +00:00
|
|
|
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
2012-06-15 13:30:18 +00:00
|
|
|
struct net_device *netdev = wdev->netdev;
|
2010-02-15 10:53:10 +00:00
|
|
|
struct sk_buff *msg;
|
|
|
|
void *hdr;
|
|
|
|
|
2013-02-22 21:05:20 +00:00
|
|
|
trace_cfg80211_mgmt_tx_status(wdev, cookie, ack);
|
|
|
|
|
2010-02-15 10:53:10 +00:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
|
|
|
if (!msg)
|
|
|
|
return;
|
|
|
|
|
2010-08-12 13:38:38 +00:00
|
|
|
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME_TX_STATUS);
|
2010-02-15 10:53:10 +00:00
|
|
|
if (!hdr) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
|
2012-06-15 13:30:18 +00:00
|
|
|
(netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX,
|
|
|
|
netdev->ifindex)) ||
|
2013-05-08 13:35:55 +00:00
|
|
|
nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)) ||
|
2012-03-29 08:41:26 +00:00
|
|
|
nla_put(msg, NL80211_ATTR_FRAME, len, buf) ||
|
|
|
|
nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie) ||
|
|
|
|
(ack && nla_put_flag(msg, NL80211_ATTR_ACK)))
|
|
|
|
goto nla_put_failure;
|
2010-02-15 10:53:10 +00:00
|
|
|
|
2011-10-22 17:05:51 +00:00
|
|
|
genlmsg_end(msg, hdr);
|
2010-02-15 10:53:10 +00:00
|
|
|
|
2013-11-19 14:19:38 +00:00
|
|
|
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
|
2013-11-19 14:19:39 +00:00
|
|
|
NL80211_MCGRP_MLME, gfp);
|
2010-02-15 10:53:10 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
nlmsg_free(msg);
|
|
|
|
}
|
2013-02-22 21:05:20 +00:00
|
|
|
EXPORT_SYMBOL(cfg80211_mgmt_tx_status);
|
2010-02-15 10:53:10 +00:00
|
|
|
|
2013-02-22 21:05:20 +00:00
|
|
|
void cfg80211_cqm_rssi_notify(struct net_device *dev,
|
|
|
|
enum nl80211_cqm_rssi_threshold_event rssi_event,
|
|
|
|
gfp_t gfp)
|
2010-03-23 07:02:33 +00:00
|
|
|
{
|
2013-02-22 21:05:20 +00:00
|
|
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
|
struct wiphy *wiphy = wdev->wiphy;
|
2014-04-21 04:53:03 +00:00
|
|
|
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
2010-03-23 07:02:33 +00:00
|
|
|
struct sk_buff *msg;
|
|
|
|
struct nlattr *pinfoattr;
|
|
|
|
void *hdr;
|
|
|
|
|
2013-02-22 21:05:20 +00:00
|
|
|
trace_cfg80211_cqm_rssi_notify(dev, rssi_event);
|
|
|
|
|
2012-06-28 03:57:45 +00:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
2010-03-23 07:02:33 +00:00
|
|
|
if (!msg)
|
|
|
|
return;
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
|
|
|
|
if (!hdr) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
|
2013-02-22 21:05:20 +00:00
|
|
|
nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex))
|
2012-03-29 08:41:26 +00:00
|
|
|
goto nla_put_failure;
|
2010-03-23 07:02:33 +00:00
|
|
|
|
|
|
|
pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
|
|
|
|
if (!pinfoattr)
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
|
|
|
|
rssi_event))
|
|
|
|
goto nla_put_failure;
|
2010-03-23 07:02:33 +00:00
|
|
|
|
|
|
|
nla_nest_end(msg, pinfoattr);
|
|
|
|
|
2011-10-22 17:05:51 +00:00
|
|
|
genlmsg_end(msg, hdr);
|
2010-03-23 07:02:33 +00:00
|
|
|
|
2013-11-19 14:19:38 +00:00
|
|
|
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
|
2013-11-19 14:19:39 +00:00
|
|
|
NL80211_MCGRP_MLME, gfp);
|
2010-03-23 07:02:33 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
nlmsg_free(msg);
|
|
|
|
}
|
2013-02-22 21:05:20 +00:00
|
|
|
EXPORT_SYMBOL(cfg80211_cqm_rssi_notify);
|
2010-03-23 07:02:33 +00:00
|
|
|
|
2013-02-22 21:05:20 +00:00
|
|
|
static void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
|
|
|
|
struct net_device *netdev, const u8 *bssid,
|
|
|
|
const u8 *replay_ctr, gfp_t gfp)
|
2011-07-05 14:35:40 +00:00
|
|
|
{
|
|
|
|
struct sk_buff *msg;
|
|
|
|
struct nlattr *rekey_attr;
|
|
|
|
void *hdr;
|
|
|
|
|
2012-06-28 03:57:45 +00:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
2011-07-05 14:35:40 +00:00
|
|
|
if (!msg)
|
|
|
|
return;
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_SET_REKEY_OFFLOAD);
|
|
|
|
if (!hdr) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (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))
|
|
|
|
goto nla_put_failure;
|
2011-07-05 14:35:40 +00:00
|
|
|
|
|
|
|
rekey_attr = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA);
|
|
|
|
if (!rekey_attr)
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (nla_put(msg, NL80211_REKEY_DATA_REPLAY_CTR,
|
|
|
|
NL80211_REPLAY_CTR_LEN, replay_ctr))
|
|
|
|
goto nla_put_failure;
|
2011-07-05 14:35:40 +00:00
|
|
|
|
|
|
|
nla_nest_end(msg, rekey_attr);
|
|
|
|
|
2011-10-22 17:05:51 +00:00
|
|
|
genlmsg_end(msg, hdr);
|
2011-07-05 14:35:40 +00:00
|
|
|
|
2013-11-19 14:19:38 +00:00
|
|
|
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
|
2013-11-19 14:19:39 +00:00
|
|
|
NL80211_MCGRP_MLME, gfp);
|
2011-07-05 14:35:40 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
nlmsg_free(msg);
|
|
|
|
}
|
|
|
|
|
2013-02-22 21:05:20 +00:00
|
|
|
void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid,
|
|
|
|
const u8 *replay_ctr, gfp_t gfp)
|
|
|
|
{
|
|
|
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
|
struct wiphy *wiphy = wdev->wiphy;
|
2014-04-21 04:53:03 +00:00
|
|
|
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
2013-02-22 21:05:20 +00:00
|
|
|
|
|
|
|
trace_cfg80211_gtk_rekey_notify(dev, bssid);
|
|
|
|
nl80211_gtk_rekey_notify(rdev, dev, bssid, replay_ctr, gfp);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(cfg80211_gtk_rekey_notify);
|
|
|
|
|
|
|
|
static void
|
|
|
|
nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
|
|
|
|
struct net_device *netdev, int index,
|
|
|
|
const u8 *bssid, bool preauth, gfp_t gfp)
|
2011-09-16 15:56:23 +00:00
|
|
|
{
|
|
|
|
struct sk_buff *msg;
|
|
|
|
struct nlattr *attr;
|
|
|
|
void *hdr;
|
|
|
|
|
2012-06-28 03:57:45 +00:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
2011-09-16 15:56:23 +00:00
|
|
|
if (!msg)
|
|
|
|
return;
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PMKSA_CANDIDATE);
|
|
|
|
if (!hdr) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
|
|
|
|
nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex))
|
|
|
|
goto nla_put_failure;
|
2011-09-16 15:56:23 +00:00
|
|
|
|
|
|
|
attr = nla_nest_start(msg, NL80211_ATTR_PMKSA_CANDIDATE);
|
|
|
|
if (!attr)
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (nla_put_u32(msg, NL80211_PMKSA_CANDIDATE_INDEX, index) ||
|
|
|
|
nla_put(msg, NL80211_PMKSA_CANDIDATE_BSSID, ETH_ALEN, bssid) ||
|
|
|
|
(preauth &&
|
|
|
|
nla_put_flag(msg, NL80211_PMKSA_CANDIDATE_PREAUTH)))
|
|
|
|
goto nla_put_failure;
|
2011-09-16 15:56:23 +00:00
|
|
|
|
|
|
|
nla_nest_end(msg, attr);
|
|
|
|
|
2011-10-22 17:05:51 +00:00
|
|
|
genlmsg_end(msg, hdr);
|
2011-09-16 15:56:23 +00:00
|
|
|
|
2013-11-19 14:19:38 +00:00
|
|
|
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
|
2013-11-19 14:19:39 +00:00
|
|
|
NL80211_MCGRP_MLME, gfp);
|
2011-09-16 15:56:23 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
nlmsg_free(msg);
|
|
|
|
}
|
|
|
|
|
2013-02-22 21:05:20 +00:00
|
|
|
void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index,
|
|
|
|
const u8 *bssid, bool preauth, gfp_t gfp)
|
|
|
|
{
|
|
|
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
|
struct wiphy *wiphy = wdev->wiphy;
|
2014-04-21 04:53:03 +00:00
|
|
|
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
2013-02-22 21:05:20 +00:00
|
|
|
|
|
|
|
trace_cfg80211_pmksa_candidate_notify(dev, index, bssid, preauth);
|
|
|
|
nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify);
|
|
|
|
|
|
|
|
static void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
|
|
|
|
struct net_device *netdev,
|
|
|
|
struct cfg80211_chan_def *chandef,
|
|
|
|
gfp_t gfp)
|
2012-04-06 20:35:47 +00:00
|
|
|
{
|
|
|
|
struct sk_buff *msg;
|
|
|
|
void *hdr;
|
|
|
|
|
2012-06-28 03:57:45 +00:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
2012-04-06 20:35:47 +00:00
|
|
|
if (!msg)
|
|
|
|
return;
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CH_SWITCH_NOTIFY);
|
|
|
|
if (!hdr) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-11-08 20:25:48 +00:00
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex))
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
if (nl80211_send_chandef(msg, chandef))
|
2012-04-12 18:25:14 +00:00
|
|
|
goto nla_put_failure;
|
2012-04-06 20:35:47 +00:00
|
|
|
|
|
|
|
genlmsg_end(msg, hdr);
|
|
|
|
|
2013-11-19 14:19:38 +00:00
|
|
|
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
|
2013-11-19 14:19:39 +00:00
|
|
|
NL80211_MCGRP_MLME, gfp);
|
2012-04-06 20:35:47 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
nlmsg_free(msg);
|
|
|
|
}
|
|
|
|
|
2013-02-22 21:05:20 +00:00
|
|
|
void cfg80211_ch_switch_notify(struct net_device *dev,
|
|
|
|
struct cfg80211_chan_def *chandef)
|
2012-07-12 23:17:33 +00:00
|
|
|
{
|
2013-02-22 21:05:20 +00:00
|
|
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
|
struct wiphy *wiphy = wdev->wiphy;
|
2014-04-21 04:53:03 +00:00
|
|
|
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
2013-02-22 21:05:20 +00:00
|
|
|
|
2013-11-21 17:19:51 +00:00
|
|
|
ASSERT_WDEV_LOCK(wdev);
|
2013-02-22 21:05:20 +00:00
|
|
|
|
2013-11-21 17:19:51 +00:00
|
|
|
trace_cfg80211_ch_switch_notify(dev, chandef);
|
2013-02-22 21:05:20 +00:00
|
|
|
|
|
|
|
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
|
2013-08-28 11:41:33 +00:00
|
|
|
wdev->iftype != NL80211_IFTYPE_P2P_GO &&
|
2013-10-17 22:55:02 +00:00
|
|
|
wdev->iftype != NL80211_IFTYPE_ADHOC &&
|
|
|
|
wdev->iftype != NL80211_IFTYPE_MESH_POINT))
|
2013-11-21 17:19:51 +00:00
|
|
|
return;
|
2013-02-22 21:05:20 +00:00
|
|
|
|
2014-01-29 13:22:27 +00:00
|
|
|
wdev->chandef = *chandef;
|
2014-01-24 13:29:21 +00:00
|
|
|
wdev->preset_chandef = *chandef;
|
2013-02-22 21:05:20 +00:00
|
|
|
nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(cfg80211_ch_switch_notify);
|
|
|
|
|
|
|
|
void cfg80211_cqm_txe_notify(struct net_device *dev,
|
|
|
|
const u8 *peer, u32 num_packets,
|
|
|
|
u32 rate, u32 intvl, gfp_t gfp)
|
|
|
|
{
|
|
|
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
|
struct wiphy *wiphy = wdev->wiphy;
|
2014-04-21 04:53:03 +00:00
|
|
|
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
2012-07-12 23:17:33 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
|
2013-02-22 21:05:20 +00:00
|
|
|
nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
|
2012-07-12 23:17:33 +00:00
|
|
|
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer))
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
|
|
|
|
if (!pinfoattr)
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_PKTS, num_packets))
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_RATE, rate))
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_INTVL, intvl))
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
nla_nest_end(msg, pinfoattr);
|
|
|
|
|
|
|
|
genlmsg_end(msg, hdr);
|
|
|
|
|
2013-11-19 14:19:38 +00:00
|
|
|
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
|
2013-11-19 14:19:39 +00:00
|
|
|
NL80211_MCGRP_MLME, gfp);
|
2012-07-12 23:17:33 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
nlmsg_free(msg);
|
|
|
|
}
|
2013-02-22 21:05:20 +00:00
|
|
|
EXPORT_SYMBOL(cfg80211_cqm_txe_notify);
|
2012-07-12 23:17:33 +00:00
|
|
|
|
2013-02-08 17:16:19 +00:00
|
|
|
void
|
|
|
|
nl80211_radar_notify(struct cfg80211_registered_device *rdev,
|
2013-11-06 12:55:51 +00:00
|
|
|
const struct cfg80211_chan_def *chandef,
|
2013-02-08 17:16:19 +00:00
|
|
|
enum nl80211_radar_event event,
|
|
|
|
struct net_device *netdev, 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_RADAR_DETECT);
|
|
|
|
if (!hdr) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx))
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
/* NOP and radar events don't need a netdev parameter */
|
|
|
|
if (netdev) {
|
|
|
|
struct wireless_dev *wdev = netdev->ieee80211_ptr;
|
|
|
|
|
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
|
|
|
|
nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)))
|
|
|
|
goto nla_put_failure;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_RADAR_EVENT, event))
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
if (nl80211_send_chandef(msg, chandef))
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
2013-06-04 10:46:03 +00:00
|
|
|
genlmsg_end(msg, hdr);
|
2013-02-08 17:16:19 +00:00
|
|
|
|
2013-11-19 14:19:38 +00:00
|
|
|
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
|
2013-11-19 14:19:39 +00:00
|
|
|
NL80211_MCGRP_MLME, gfp);
|
2013-02-08 17:16:19 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
nlmsg_free(msg);
|
|
|
|
}
|
|
|
|
|
2013-02-22 21:05:20 +00:00
|
|
|
void cfg80211_cqm_pktloss_notify(struct net_device *dev,
|
|
|
|
const u8 *peer, u32 num_packets, gfp_t gfp)
|
2010-11-24 07:10:05 +00:00
|
|
|
{
|
2013-02-22 21:05:20 +00:00
|
|
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
|
|
|
struct wiphy *wiphy = wdev->wiphy;
|
2014-04-21 04:53:03 +00:00
|
|
|
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
2010-11-24 07:10:05 +00:00
|
|
|
struct sk_buff *msg;
|
|
|
|
struct nlattr *pinfoattr;
|
|
|
|
void *hdr;
|
|
|
|
|
2013-02-22 21:05:20 +00:00
|
|
|
trace_cfg80211_cqm_pktloss_notify(dev, peer, num_packets);
|
|
|
|
|
2012-06-28 03:57:45 +00:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
2010-11-24 07:10:05 +00:00
|
|
|
if (!msg)
|
|
|
|
return;
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
|
|
|
|
if (!hdr) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
|
2013-02-22 21:05:20 +00:00
|
|
|
nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
|
2012-03-29 08:41:26 +00:00
|
|
|
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer))
|
|
|
|
goto nla_put_failure;
|
2010-11-24 07:10:05 +00:00
|
|
|
|
|
|
|
pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
|
|
|
|
if (!pinfoattr)
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_CQM_PKT_LOSS_EVENT, num_packets))
|
|
|
|
goto nla_put_failure;
|
2010-11-24 07:10:05 +00:00
|
|
|
|
|
|
|
nla_nest_end(msg, pinfoattr);
|
|
|
|
|
2011-10-22 17:05:51 +00:00
|
|
|
genlmsg_end(msg, hdr);
|
2010-11-24 07:10:05 +00:00
|
|
|
|
2013-11-19 14:19:38 +00:00
|
|
|
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
|
2013-11-19 14:19:39 +00:00
|
|
|
NL80211_MCGRP_MLME, gfp);
|
2010-11-24 07:10:05 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
nlmsg_free(msg);
|
|
|
|
}
|
2013-02-22 21:05:20 +00:00
|
|
|
EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify);
|
2010-11-24 07:10:05 +00:00
|
|
|
|
2011-11-04 10:18:15 +00:00
|
|
|
void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
|
|
|
|
u64 cookie, bool acked, gfp_t gfp)
|
|
|
|
{
|
|
|
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
2014-04-21 04:53:03 +00:00
|
|
|
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
2011-11-04 10:18:15 +00:00
|
|
|
struct sk_buff *msg;
|
|
|
|
void *hdr;
|
|
|
|
|
2012-08-27 09:49:39 +00:00
|
|
|
trace_cfg80211_probe_status(dev, addr, cookie, acked);
|
|
|
|
|
2012-06-28 03:57:45 +00:00
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
2012-08-27 09:49:39 +00:00
|
|
|
|
2011-11-04 10:18:15 +00:00
|
|
|
if (!msg)
|
|
|
|
return;
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PROBE_CLIENT);
|
|
|
|
if (!hdr) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-03-29 08:41:26 +00:00
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
|
|
|
|
nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
|
|
|
|
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
|
|
|
|
nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie) ||
|
|
|
|
(acked && nla_put_flag(msg, NL80211_ATTR_ACK)))
|
|
|
|
goto nla_put_failure;
|
2011-11-04 10:18:15 +00:00
|
|
|
|
2013-06-04 10:46:03 +00:00
|
|
|
genlmsg_end(msg, hdr);
|
2011-11-04 10:18:15 +00:00
|
|
|
|
2013-11-19 14:19:38 +00:00
|
|
|
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
|
2013-11-19 14:19:39 +00:00
|
|
|
NL80211_MCGRP_MLME, gfp);
|
2011-11-04 10:18:15 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
nlmsg_free(msg);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(cfg80211_probe_status);
|
|
|
|
|
2011-11-04 10:18:17 +00:00
|
|
|
void cfg80211_report_obss_beacon(struct wiphy *wiphy,
|
|
|
|
const u8 *frame, size_t len,
|
2012-10-26 21:49:25 +00:00
|
|
|
int freq, int sig_dbm)
|
2011-11-04 10:18:17 +00:00
|
|
|
{
|
2014-04-21 04:53:03 +00:00
|
|
|
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
2011-11-04 10:18:17 +00:00
|
|
|
struct sk_buff *msg;
|
|
|
|
void *hdr;
|
2012-10-26 21:49:25 +00:00
|
|
|
struct cfg80211_beacon_registration *reg;
|
2011-11-04 10:18:17 +00:00
|
|
|
|
2012-08-27 09:49:39 +00:00
|
|
|
trace_cfg80211_report_obss_beacon(wiphy, frame, len, freq, sig_dbm);
|
|
|
|
|
2012-10-26 21:49:25 +00:00
|
|
|
spin_lock_bh(&rdev->beacon_registrations_lock);
|
|
|
|
list_for_each_entry(reg, &rdev->beacon_registrations, list) {
|
|
|
|
msg = nlmsg_new(len + 100, GFP_ATOMIC);
|
|
|
|
if (!msg) {
|
|
|
|
spin_unlock_bh(&rdev->beacon_registrations_lock);
|
|
|
|
return;
|
|
|
|
}
|
2011-11-04 10:18:17 +00:00
|
|
|
|
2012-10-26 21:49:25 +00:00
|
|
|
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
|
|
|
|
if (!hdr)
|
|
|
|
goto nla_put_failure;
|
2011-11-04 10:18:17 +00:00
|
|
|
|
2012-10-26 21:49:25 +00:00
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
|
|
|
|
(freq &&
|
|
|
|
nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq)) ||
|
|
|
|
(sig_dbm &&
|
|
|
|
nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)) ||
|
|
|
|
nla_put(msg, NL80211_ATTR_FRAME, len, frame))
|
|
|
|
goto nla_put_failure;
|
2011-11-04 10:18:17 +00:00
|
|
|
|
2012-10-26 21:49:25 +00:00
|
|
|
genlmsg_end(msg, hdr);
|
2011-11-04 10:18:17 +00:00
|
|
|
|
2012-10-26 21:49:25 +00:00
|
|
|
genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, reg->nlportid);
|
|
|
|
}
|
|
|
|
spin_unlock_bh(&rdev->beacon_registrations_lock);
|
2011-11-04 10:18:17 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
nla_put_failure:
|
2012-10-26 21:49:25 +00:00
|
|
|
spin_unlock_bh(&rdev->beacon_registrations_lock);
|
|
|
|
if (hdr)
|
|
|
|
genlmsg_cancel(msg, hdr);
|
2011-11-04 10:18:17 +00:00
|
|
|
nlmsg_free(msg);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(cfg80211_report_obss_beacon);
|
|
|
|
|
2013-01-22 11:34:29 +00:00
|
|
|
#ifdef CONFIG_PM
|
|
|
|
void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,
|
|
|
|
struct cfg80211_wowlan_wakeup *wakeup,
|
|
|
|
gfp_t gfp)
|
|
|
|
{
|
2014-04-21 04:53:03 +00:00
|
|
|
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
2013-01-22 11:34:29 +00:00
|
|
|
struct sk_buff *msg;
|
|
|
|
void *hdr;
|
2013-06-04 10:46:03 +00:00
|
|
|
int size = 200;
|
2013-01-22 11:34:29 +00:00
|
|
|
|
|
|
|
trace_cfg80211_report_wowlan_wakeup(wdev->wiphy, wdev, wakeup);
|
|
|
|
|
|
|
|
if (wakeup)
|
|
|
|
size += wakeup->packet_present_len;
|
|
|
|
|
|
|
|
msg = nlmsg_new(size, gfp);
|
|
|
|
if (!msg)
|
|
|
|
return;
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_SET_WOWLAN);
|
|
|
|
if (!hdr)
|
|
|
|
goto free_msg;
|
|
|
|
|
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
|
|
|
|
nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)))
|
|
|
|
goto free_msg;
|
|
|
|
|
|
|
|
if (wdev->netdev && nla_put_u32(msg, NL80211_ATTR_IFINDEX,
|
|
|
|
wdev->netdev->ifindex))
|
|
|
|
goto free_msg;
|
|
|
|
|
|
|
|
if (wakeup) {
|
|
|
|
struct nlattr *reasons;
|
|
|
|
|
|
|
|
reasons = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS);
|
2013-10-25 09:16:58 +00:00
|
|
|
if (!reasons)
|
|
|
|
goto free_msg;
|
2013-01-22 11:34:29 +00:00
|
|
|
|
|
|
|
if (wakeup->disconnect &&
|
|
|
|
nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT))
|
|
|
|
goto free_msg;
|
|
|
|
if (wakeup->magic_pkt &&
|
|
|
|
nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT))
|
|
|
|
goto free_msg;
|
|
|
|
if (wakeup->gtk_rekey_failure &&
|
|
|
|
nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE))
|
|
|
|
goto free_msg;
|
|
|
|
if (wakeup->eap_identity_req &&
|
|
|
|
nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST))
|
|
|
|
goto free_msg;
|
|
|
|
if (wakeup->four_way_handshake &&
|
|
|
|
nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE))
|
|
|
|
goto free_msg;
|
|
|
|
if (wakeup->rfkill_release &&
|
|
|
|
nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE))
|
|
|
|
goto free_msg;
|
|
|
|
|
|
|
|
if (wakeup->pattern_idx >= 0 &&
|
|
|
|
nla_put_u32(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
|
|
|
|
wakeup->pattern_idx))
|
|
|
|
goto free_msg;
|
|
|
|
|
2013-10-25 09:05:22 +00:00
|
|
|
if (wakeup->tcp_match &&
|
|
|
|
nla_put_flag(msg, NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH))
|
|
|
|
goto free_msg;
|
2013-01-23 21:57:40 +00:00
|
|
|
|
2013-10-25 09:05:22 +00:00
|
|
|
if (wakeup->tcp_connlost &&
|
|
|
|
nla_put_flag(msg, NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST))
|
|
|
|
goto free_msg;
|
2013-01-23 21:57:40 +00:00
|
|
|
|
2013-10-25 09:05:22 +00:00
|
|
|
if (wakeup->tcp_nomoretokens &&
|
|
|
|
nla_put_flag(msg,
|
|
|
|
NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS))
|
|
|
|
goto free_msg;
|
2013-01-23 21:57:40 +00:00
|
|
|
|
2013-01-22 11:34:29 +00:00
|
|
|
if (wakeup->packet) {
|
|
|
|
u32 pkt_attr = NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211;
|
|
|
|
u32 len_attr = NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN;
|
|
|
|
|
|
|
|
if (!wakeup->packet_80211) {
|
|
|
|
pkt_attr =
|
|
|
|
NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023;
|
|
|
|
len_attr =
|
|
|
|
NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wakeup->packet_len &&
|
|
|
|
nla_put_u32(msg, len_attr, wakeup->packet_len))
|
|
|
|
goto free_msg;
|
|
|
|
|
|
|
|
if (nla_put(msg, pkt_attr, wakeup->packet_present_len,
|
|
|
|
wakeup->packet))
|
|
|
|
goto free_msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
nla_nest_end(msg, reasons);
|
|
|
|
}
|
|
|
|
|
2013-06-04 10:46:03 +00:00
|
|
|
genlmsg_end(msg, hdr);
|
2013-01-22 11:34:29 +00:00
|
|
|
|
2013-11-19 14:19:38 +00:00
|
|
|
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
|
2013-11-19 14:19:39 +00:00
|
|
|
NL80211_MCGRP_MLME, gfp);
|
2013-01-22 11:34:29 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
free_msg:
|
|
|
|
nlmsg_free(msg);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(cfg80211_report_wowlan_wakeup);
|
|
|
|
#endif
|
|
|
|
|
2012-11-16 20:49:57 +00:00
|
|
|
void cfg80211_tdls_oper_request(struct net_device *dev, const u8 *peer,
|
|
|
|
enum nl80211_tdls_operation oper,
|
|
|
|
u16 reason_code, gfp_t gfp)
|
|
|
|
{
|
|
|
|
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
2014-04-21 04:53:03 +00:00
|
|
|
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
2012-11-16 20:49:57 +00:00
|
|
|
struct sk_buff *msg;
|
|
|
|
void *hdr;
|
|
|
|
|
|
|
|
trace_cfg80211_tdls_oper_request(wdev->wiphy, dev, peer, oper,
|
|
|
|
reason_code);
|
|
|
|
|
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
|
|
|
if (!msg)
|
|
|
|
return;
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_TDLS_OPER);
|
|
|
|
if (!hdr) {
|
|
|
|
nlmsg_free(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
|
|
|
|
nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
|
|
|
|
nla_put_u8(msg, NL80211_ATTR_TDLS_OPERATION, oper) ||
|
|
|
|
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer) ||
|
|
|
|
(reason_code > 0 &&
|
|
|
|
nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason_code)))
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
2013-06-04 10:46:03 +00:00
|
|
|
genlmsg_end(msg, hdr);
|
2012-11-16 20:49:57 +00:00
|
|
|
|
2013-11-19 14:19:38 +00:00
|
|
|
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
|
2013-11-19 14:19:39 +00:00
|
|
|
NL80211_MCGRP_MLME, gfp);
|
2012-11-16 20:49:57 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
nlmsg_free(msg);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(cfg80211_tdls_oper_request);
|
|
|
|
|
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;
|
2012-10-26 21:49:25 +00:00
|
|
|
struct cfg80211_beacon_registration *reg, *tmp;
|
2010-02-15 10:53:10 +00:00
|
|
|
|
|
|
|
if (state != NETLINK_URELEASE)
|
|
|
|
return NOTIFY_DONE;
|
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
|
2011-11-04 10:18:17 +00:00
|
|
|
list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) {
|
2014-03-24 16:57:27 +00:00
|
|
|
bool schedule_destroy_work = false;
|
|
|
|
|
|
|
|
list_for_each_entry_rcu(wdev, &rdev->wdev_list, list) {
|
2012-09-07 20:12:54 +00:00
|
|
|
cfg80211_mlme_unregister_socket(wdev, notify->portid);
|
2012-10-26 21:49:25 +00:00
|
|
|
|
2014-03-24 16:57:27 +00:00
|
|
|
if (wdev->owner_nlportid == notify->portid)
|
|
|
|
schedule_destroy_work = true;
|
|
|
|
}
|
|
|
|
|
2012-10-26 21:49:25 +00:00
|
|
|
spin_lock_bh(&rdev->beacon_registrations_lock);
|
|
|
|
list_for_each_entry_safe(reg, tmp, &rdev->beacon_registrations,
|
|
|
|
list) {
|
|
|
|
if (reg->nlportid == notify->portid) {
|
|
|
|
list_del(®->list);
|
|
|
|
kfree(reg);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
spin_unlock_bh(&rdev->beacon_registrations_lock);
|
2014-03-24 16:57:27 +00:00
|
|
|
|
|
|
|
if (schedule_destroy_work) {
|
|
|
|
struct cfg80211_iface_destroy *destroy;
|
|
|
|
|
|
|
|
destroy = kzalloc(sizeof(*destroy), GFP_ATOMIC);
|
|
|
|
if (destroy) {
|
|
|
|
destroy->nlportid = notify->portid;
|
|
|
|
spin_lock(&rdev->destroy_list_lock);
|
|
|
|
list_add(&destroy->list, &rdev->destroy_list);
|
|
|
|
spin_unlock(&rdev->destroy_list_lock);
|
|
|
|
schedule_work(&rdev->destroy_work);
|
|
|
|
}
|
|
|
|
}
|
2011-11-04 10:18:17 +00:00
|
|
|
}
|
2010-02-15 10:53:10 +00:00
|
|
|
|
|
|
|
rcu_read_unlock();
|
|
|
|
|
2014-04-21 04:53:04 +00:00
|
|
|
return NOTIFY_OK;
|
2010-02-15 10:53:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct notifier_block nl80211_netlink_notifier = {
|
|
|
|
.notifier_call = nl80211_netlink_notify,
|
|
|
|
};
|
|
|
|
|
2013-02-27 15:14:27 +00:00
|
|
|
void cfg80211_ft_event(struct net_device *netdev,
|
|
|
|
struct cfg80211_ft_event_params *ft_event)
|
|
|
|
{
|
|
|
|
struct wiphy *wiphy = netdev->ieee80211_ptr->wiphy;
|
2014-04-21 04:53:03 +00:00
|
|
|
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
2013-02-27 15:14:27 +00:00
|
|
|
struct sk_buff *msg;
|
|
|
|
void *hdr;
|
|
|
|
|
|
|
|
trace_cfg80211_ft_event(wiphy, netdev, ft_event);
|
|
|
|
|
|
|
|
if (!ft_event->target_ap)
|
|
|
|
return;
|
|
|
|
|
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
|
|
if (!msg)
|
|
|
|
return;
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FT_EVENT);
|
2013-10-25 09:05:22 +00:00
|
|
|
if (!hdr)
|
|
|
|
goto out;
|
2013-02-27 15:14:27 +00:00
|
|
|
|
2013-10-25 09:05:22 +00:00
|
|
|
if (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, ft_event->target_ap))
|
|
|
|
goto out;
|
2013-02-27 15:14:27 +00:00
|
|
|
|
2013-10-25 09:05:22 +00:00
|
|
|
if (ft_event->ies &&
|
|
|
|
nla_put(msg, NL80211_ATTR_IE, ft_event->ies_len, ft_event->ies))
|
|
|
|
goto out;
|
|
|
|
if (ft_event->ric_ies &&
|
|
|
|
nla_put(msg, NL80211_ATTR_IE_RIC, ft_event->ric_ies_len,
|
|
|
|
ft_event->ric_ies))
|
|
|
|
goto out;
|
2013-02-27 15:14:27 +00:00
|
|
|
|
2013-06-04 10:46:03 +00:00
|
|
|
genlmsg_end(msg, hdr);
|
2013-02-27 15:14:27 +00:00
|
|
|
|
2013-11-19 14:19:38 +00:00
|
|
|
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
|
2013-11-19 14:19:39 +00:00
|
|
|
NL80211_MCGRP_MLME, GFP_KERNEL);
|
2013-10-25 09:05:22 +00:00
|
|
|
return;
|
|
|
|
out:
|
|
|
|
nlmsg_free(msg);
|
2013-02-27 15:14:27 +00:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(cfg80211_ft_event);
|
|
|
|
|
2013-04-18 13:49:00 +00:00
|
|
|
void cfg80211_crit_proto_stopped(struct wireless_dev *wdev, gfp_t gfp)
|
|
|
|
{
|
|
|
|
struct cfg80211_registered_device *rdev;
|
|
|
|
struct sk_buff *msg;
|
|
|
|
void *hdr;
|
|
|
|
u32 nlportid;
|
|
|
|
|
2014-04-21 04:53:03 +00:00
|
|
|
rdev = wiphy_to_rdev(wdev->wiphy);
|
2013-04-18 13:49:00 +00:00
|
|
|
if (!rdev->crit_proto_nlportid)
|
|
|
|
return;
|
|
|
|
|
|
|
|
nlportid = rdev->crit_proto_nlportid;
|
|
|
|
rdev->crit_proto_nlportid = 0;
|
|
|
|
|
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
|
|
|
|
if (!msg)
|
|
|
|
return;
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CRIT_PROTOCOL_STOP);
|
|
|
|
if (!hdr)
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
|
|
|
|
nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)))
|
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
|
|
genlmsg_end(msg, hdr);
|
|
|
|
|
|
|
|
genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid);
|
|
|
|
return;
|
|
|
|
|
|
|
|
nla_put_failure:
|
|
|
|
if (hdr)
|
|
|
|
genlmsg_cancel(msg, hdr);
|
|
|
|
nlmsg_free(msg);
|
|
|
|
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(cfg80211_crit_proto_stopped);
|
|
|
|
|
2014-01-24 13:06:29 +00:00
|
|
|
void nl80211_send_ap_stopped(struct wireless_dev *wdev)
|
|
|
|
{
|
|
|
|
struct wiphy *wiphy = wdev->wiphy;
|
2014-04-21 04:53:03 +00:00
|
|
|
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
2014-01-24 13:06:29 +00:00
|
|
|
struct sk_buff *msg;
|
|
|
|
void *hdr;
|
|
|
|
|
|
|
|
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
|
|
|
if (!msg)
|
|
|
|
return;
|
|
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_STOP_AP);
|
|
|
|
if (!hdr)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
|
|
|
|
nla_put_u32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex) ||
|
|
|
|
nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
genlmsg_end(msg, hdr);
|
|
|
|
|
|
|
|
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(wiphy), msg, 0,
|
|
|
|
NL80211_MCGRP_MLME, GFP_KERNEL);
|
|
|
|
return;
|
|
|
|
out:
|
|
|
|
nlmsg_free(msg);
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2013-11-19 14:19:39 +00:00
|
|
|
err = genl_register_family_with_ops_groups(&nl80211_fam, nl80211_ops,
|
|
|
|
nl80211_mcgrps);
|
2007-09-20 17:09:35 +00:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
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);
|
|
|
|
}
|