mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 01:08:03 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
220
net/wireless/Kconfig
Normal file
220
net/wireless/Kconfig
Normal file
|
@ -0,0 +1,220 @@
|
|||
config WIRELESS_EXT
|
||||
bool
|
||||
|
||||
config WEXT_CORE
|
||||
def_bool y
|
||||
depends on CFG80211_WEXT || WIRELESS_EXT
|
||||
|
||||
config WEXT_PROC
|
||||
def_bool y
|
||||
depends on PROC_FS
|
||||
depends on WEXT_CORE
|
||||
|
||||
config WEXT_SPY
|
||||
bool
|
||||
|
||||
config WEXT_PRIV
|
||||
bool
|
||||
|
||||
config CFG80211
|
||||
tristate "cfg80211 - wireless configuration API"
|
||||
depends on RFKILL || !RFKILL
|
||||
---help---
|
||||
cfg80211 is the Linux wireless LAN (802.11) configuration API.
|
||||
Enable this if you have a wireless device.
|
||||
|
||||
For more information refer to documentation on the wireless wiki:
|
||||
|
||||
http://wireless.kernel.org/en/developers/Documentation/cfg80211
|
||||
|
||||
When built as a module it will be called cfg80211.
|
||||
|
||||
config NL80211_TESTMODE
|
||||
bool "nl80211 testmode command"
|
||||
depends on CFG80211
|
||||
help
|
||||
The nl80211 testmode command helps implementing things like
|
||||
factory calibration or validation tools for wireless chips.
|
||||
|
||||
Select this option ONLY for kernels that are specifically
|
||||
built for such purposes.
|
||||
|
||||
Debugging tools that are supposed to end up in the hands of
|
||||
users should better be implemented with debugfs.
|
||||
|
||||
Say N.
|
||||
|
||||
config CFG80211_DEVELOPER_WARNINGS
|
||||
bool "enable developer warnings"
|
||||
depends on CFG80211
|
||||
default n
|
||||
help
|
||||
This option enables some additional warnings that help
|
||||
cfg80211 developers and driver developers, but that can
|
||||
trigger due to races with userspace.
|
||||
|
||||
For example, when a driver reports that it was disconnected
|
||||
from the AP, but the user disconnects manually at the same
|
||||
time, the warning might trigger spuriously due to races.
|
||||
|
||||
Say Y only if you are developing cfg80211 or a driver based
|
||||
on it (or mac80211).
|
||||
|
||||
|
||||
config CFG80211_REG_DEBUG
|
||||
bool "cfg80211 regulatory debugging"
|
||||
depends on CFG80211
|
||||
default n
|
||||
---help---
|
||||
You can enable this if you want to debug regulatory changes.
|
||||
For more information on cfg80211 regulatory refer to the wireless
|
||||
wiki:
|
||||
|
||||
http://wireless.kernel.org/en/developers/Regulatory
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config CFG80211_CERTIFICATION_ONUS
|
||||
bool "cfg80211 certification onus"
|
||||
depends on CFG80211 && EXPERT
|
||||
default n
|
||||
---help---
|
||||
You should disable this option unless you are both capable
|
||||
and willing to ensure your system will remain regulatory
|
||||
compliant with the features available under this option.
|
||||
Some options may still be under heavy development and
|
||||
for whatever reason regulatory compliance has not or
|
||||
cannot yet be verified. Regulatory verification may at
|
||||
times only be possible until you have the final system
|
||||
in place.
|
||||
|
||||
This option should only be enabled by system integrators
|
||||
or distributions that have done work necessary to ensure
|
||||
regulatory certification on the system with the enabled
|
||||
features. Alternatively you can enable this option if
|
||||
you are a wireless researcher and are working in a controlled
|
||||
and approved environment by your local regulatory agency.
|
||||
|
||||
config CFG80211_REG_CELLULAR_HINTS
|
||||
bool "cfg80211 regulatory support for cellular base station hints"
|
||||
depends on CFG80211_CERTIFICATION_ONUS
|
||||
---help---
|
||||
This option enables support for parsing regulatory hints
|
||||
from cellular base stations. If enabled and at least one driver
|
||||
claims support for parsing cellular base station hints the
|
||||
regulatory core will allow and parse these regulatory hints.
|
||||
The regulatory core will only apply these regulatory hints on
|
||||
drivers that support this feature. You should only enable this
|
||||
feature if you have tested and validated this feature on your
|
||||
systems.
|
||||
|
||||
config CFG80211_REG_RELAX_NO_IR
|
||||
bool "cfg80211 support for NO_IR relaxation"
|
||||
depends on CFG80211_CERTIFICATION_ONUS
|
||||
---help---
|
||||
This option enables support for relaxation of the NO_IR flag for
|
||||
situations that certain regulatory bodies have provided clarifications
|
||||
on how relaxation can occur. This feature has an inherent dependency on
|
||||
userspace features which must have been properly tested and as such is
|
||||
not enabled by default.
|
||||
|
||||
A relaxation feature example is allowing the operation of a P2P group
|
||||
owner (GO) on channels marked with NO_IR if there is an additional BSS
|
||||
interface which associated to an AP which userspace assumes or confirms
|
||||
to be an authorized master, i.e., with radar detection support and DFS
|
||||
capabilities. However, note that in order to not create daisy chain
|
||||
scenarios, this relaxation is not allowed in cases that the BSS client
|
||||
is associated to P2P GO and in addition the P2P GO instantiated on
|
||||
a channel due to this relaxation should not allow connection from
|
||||
non P2P clients.
|
||||
|
||||
The regulatory core will apply these relaxations only for drivers that
|
||||
support this feature by declaring the appropriate channel flags and
|
||||
capabilities in their registration flow.
|
||||
|
||||
config CFG80211_DEFAULT_PS
|
||||
bool "enable powersave by default"
|
||||
depends on CFG80211
|
||||
default y
|
||||
help
|
||||
This option enables powersave mode by default.
|
||||
|
||||
If this causes your applications to misbehave you should fix your
|
||||
applications instead -- they need to register their network
|
||||
latency requirement, see Documentation/power/pm_qos_interface.txt.
|
||||
|
||||
config CFG80211_DEBUGFS
|
||||
bool "cfg80211 DebugFS entries"
|
||||
depends on CFG80211
|
||||
depends on DEBUG_FS
|
||||
---help---
|
||||
You can enable this if you want to debugfs entries for cfg80211.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config CFG80211_INTERNAL_REGDB
|
||||
bool "use statically compiled regulatory rules database" if EXPERT
|
||||
default n
|
||||
depends on CFG80211
|
||||
---help---
|
||||
This option generates an internal data structure representing
|
||||
the wireless regulatory rules described in net/wireless/db.txt
|
||||
and includes code to query that database. This is an alternative
|
||||
to using CRDA for defining regulatory rules for the kernel.
|
||||
|
||||
Using this option requires some parsing of the db.txt at build time,
|
||||
the parser will be upkept with the latest wireless-regdb updates but
|
||||
older wireless-regdb formats will be ignored. The parser may later
|
||||
be replaced to avoid issues with conflicts on versions of
|
||||
wireless-regdb.
|
||||
|
||||
For details see:
|
||||
|
||||
http://wireless.kernel.org/en/developers/Regulatory
|
||||
|
||||
Most distributions have a CRDA package. So if unsure, say N.
|
||||
|
||||
config CFG80211_WEXT
|
||||
bool "cfg80211 wireless extensions compatibility"
|
||||
depends on CFG80211
|
||||
select WEXT_CORE
|
||||
help
|
||||
Enable this option if you need old userspace for wireless
|
||||
extensions with cfg80211-based drivers.
|
||||
|
||||
config CFG80211_REG_NOT_UPDATED
|
||||
bool "Do not update Regulatory configuration"
|
||||
depends on CFG80211
|
||||
default n
|
||||
help
|
||||
Regulation should not updated even if device found other country
|
||||
Access Point Beacon once since device should find around other
|
||||
Access Points.
|
||||
|
||||
config LIB80211
|
||||
tristate
|
||||
default n
|
||||
help
|
||||
This options enables a library of common routines used
|
||||
by IEEE802.11 wireless LAN drivers.
|
||||
|
||||
Drivers should select this themselves if needed.
|
||||
|
||||
config LIB80211_CRYPT_WEP
|
||||
tristate
|
||||
|
||||
config LIB80211_CRYPT_CCMP
|
||||
tristate
|
||||
|
||||
config LIB80211_CRYPT_TKIP
|
||||
tristate
|
||||
|
||||
config LIB80211_DEBUG
|
||||
bool "lib80211 debugging messages"
|
||||
depends on LIB80211
|
||||
default n
|
||||
---help---
|
||||
You can enable this if you want verbose debugging messages
|
||||
from lib80211.
|
||||
|
||||
If unsure, say N.
|
25
net/wireless/Makefile
Normal file
25
net/wireless/Makefile
Normal file
|
@ -0,0 +1,25 @@
|
|||
obj-$(CONFIG_CFG80211) += cfg80211.o
|
||||
obj-$(CONFIG_LIB80211) += lib80211.o
|
||||
obj-$(CONFIG_LIB80211_CRYPT_WEP) += lib80211_crypt_wep.o
|
||||
obj-$(CONFIG_LIB80211_CRYPT_CCMP) += lib80211_crypt_ccmp.o
|
||||
obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o
|
||||
|
||||
obj-$(CONFIG_WEXT_CORE) += wext-core.o
|
||||
obj-$(CONFIG_WEXT_PROC) += wext-proc.o
|
||||
obj-$(CONFIG_WEXT_SPY) += wext-spy.o
|
||||
obj-$(CONFIG_WEXT_PRIV) += wext-priv.o
|
||||
|
||||
cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o
|
||||
cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o trace.o
|
||||
cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o
|
||||
cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o
|
||||
cfg80211-$(CONFIG_CFG80211_INTERNAL_REGDB) += regdb.o
|
||||
|
||||
CFLAGS_trace.o := -I$(src)
|
||||
|
||||
ccflags-y += -D__CHECK_ENDIAN__
|
||||
|
||||
$(obj)/regdb.c: $(src)/db.txt $(src)/genregdb.awk
|
||||
@$(AWK) -f $(srctree)/$(src)/genregdb.awk < $< > $@
|
||||
|
||||
clean-files := regdb.c
|
51
net/wireless/ap.c
Normal file
51
net/wireless/ap.c
Normal file
|
@ -0,0 +1,51 @@
|
|||
#include <linux/ieee80211.h>
|
||||
#include <linux/export.h>
|
||||
#include <net/cfg80211.h>
|
||||
#include "nl80211.h"
|
||||
#include "core.h"
|
||||
#include "rdev-ops.h"
|
||||
|
||||
|
||||
int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, bool notify)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
int err;
|
||||
|
||||
ASSERT_WDEV_LOCK(wdev);
|
||||
|
||||
if (!rdev->ops->stop_ap)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
|
||||
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!wdev->beacon_interval)
|
||||
return -ENOENT;
|
||||
|
||||
err = rdev_stop_ap(rdev, dev);
|
||||
if (!err) {
|
||||
wdev->beacon_interval = 0;
|
||||
memset(&wdev->chandef, 0, sizeof(wdev->chandef));
|
||||
wdev->ssid_len = 0;
|
||||
rdev_set_qos_map(rdev, dev, NULL);
|
||||
if (notify)
|
||||
nl80211_send_ap_stopped(wdev);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, bool notify)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
int err;
|
||||
|
||||
wdev_lock(wdev);
|
||||
err = __cfg80211_stop_ap(rdev, dev, notify);
|
||||
wdev_unlock(wdev);
|
||||
|
||||
return err;
|
||||
}
|
908
net/wireless/chan.c
Normal file
908
net/wireless/chan.c
Normal file
|
@ -0,0 +1,908 @@
|
|||
/*
|
||||
* This file contains helper code to handle channel
|
||||
* settings and keeping track of what is possible at
|
||||
* any point in time.
|
||||
*
|
||||
* Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
|
||||
* Copyright 2013-2014 Intel Mobile Communications GmbH
|
||||
*/
|
||||
|
||||
#include <linux/export.h>
|
||||
#include <net/cfg80211.h>
|
||||
#include "core.h"
|
||||
#include "rdev-ops.h"
|
||||
|
||||
void cfg80211_chandef_create(struct cfg80211_chan_def *chandef,
|
||||
struct ieee80211_channel *chan,
|
||||
enum nl80211_channel_type chan_type)
|
||||
{
|
||||
if (WARN_ON(!chan))
|
||||
return;
|
||||
|
||||
chandef->chan = chan;
|
||||
chandef->center_freq2 = 0;
|
||||
|
||||
switch (chan_type) {
|
||||
case NL80211_CHAN_NO_HT:
|
||||
chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
|
||||
chandef->center_freq1 = chan->center_freq;
|
||||
break;
|
||||
case NL80211_CHAN_HT20:
|
||||
chandef->width = NL80211_CHAN_WIDTH_20;
|
||||
chandef->center_freq1 = chan->center_freq;
|
||||
break;
|
||||
case NL80211_CHAN_HT40PLUS:
|
||||
chandef->width = NL80211_CHAN_WIDTH_40;
|
||||
chandef->center_freq1 = chan->center_freq + 10;
|
||||
break;
|
||||
case NL80211_CHAN_HT40MINUS:
|
||||
chandef->width = NL80211_CHAN_WIDTH_40;
|
||||
chandef->center_freq1 = chan->center_freq - 10;
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_chandef_create);
|
||||
|
||||
bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
u32 control_freq;
|
||||
|
||||
if (!chandef->chan)
|
||||
return false;
|
||||
|
||||
control_freq = chandef->chan->center_freq;
|
||||
|
||||
switch (chandef->width) {
|
||||
case NL80211_CHAN_WIDTH_5:
|
||||
case NL80211_CHAN_WIDTH_10:
|
||||
case NL80211_CHAN_WIDTH_20:
|
||||
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||
if (chandef->center_freq1 != control_freq)
|
||||
return false;
|
||||
if (chandef->center_freq2)
|
||||
return false;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
if (chandef->center_freq1 != control_freq + 10 &&
|
||||
chandef->center_freq1 != control_freq - 10)
|
||||
return false;
|
||||
if (chandef->center_freq2)
|
||||
return false;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_80P80:
|
||||
if (chandef->center_freq1 != control_freq + 30 &&
|
||||
chandef->center_freq1 != control_freq + 10 &&
|
||||
chandef->center_freq1 != control_freq - 10 &&
|
||||
chandef->center_freq1 != control_freq - 30)
|
||||
return false;
|
||||
if (!chandef->center_freq2)
|
||||
return false;
|
||||
/* adjacent is not allowed -- that's a 160 MHz channel */
|
||||
if (chandef->center_freq1 - chandef->center_freq2 == 80 ||
|
||||
chandef->center_freq2 - chandef->center_freq1 == 80)
|
||||
return false;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_80:
|
||||
if (chandef->center_freq1 != control_freq + 30 &&
|
||||
chandef->center_freq1 != control_freq + 10 &&
|
||||
chandef->center_freq1 != control_freq - 10 &&
|
||||
chandef->center_freq1 != control_freq - 30)
|
||||
return false;
|
||||
if (chandef->center_freq2)
|
||||
return false;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_160:
|
||||
if (chandef->center_freq1 != control_freq + 70 &&
|
||||
chandef->center_freq1 != control_freq + 50 &&
|
||||
chandef->center_freq1 != control_freq + 30 &&
|
||||
chandef->center_freq1 != control_freq + 10 &&
|
||||
chandef->center_freq1 != control_freq - 10 &&
|
||||
chandef->center_freq1 != control_freq - 30 &&
|
||||
chandef->center_freq1 != control_freq - 50 &&
|
||||
chandef->center_freq1 != control_freq - 70)
|
||||
return false;
|
||||
if (chandef->center_freq2)
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_chandef_valid);
|
||||
|
||||
static void chandef_primary_freqs(const struct cfg80211_chan_def *c,
|
||||
int *pri40, int *pri80)
|
||||
{
|
||||
int tmp;
|
||||
|
||||
switch (c->width) {
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
*pri40 = c->center_freq1;
|
||||
*pri80 = 0;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_80:
|
||||
case NL80211_CHAN_WIDTH_80P80:
|
||||
*pri80 = c->center_freq1;
|
||||
/* n_P20 */
|
||||
tmp = (30 + c->chan->center_freq - c->center_freq1)/20;
|
||||
/* n_P40 */
|
||||
tmp /= 2;
|
||||
/* freq_P40 */
|
||||
*pri40 = c->center_freq1 - 20 + 40 * tmp;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_160:
|
||||
/* n_P20 */
|
||||
tmp = (70 + c->chan->center_freq - c->center_freq1)/20;
|
||||
/* n_P40 */
|
||||
tmp /= 2;
|
||||
/* freq_P40 */
|
||||
*pri40 = c->center_freq1 - 60 + 40 * tmp;
|
||||
/* n_P80 */
|
||||
tmp /= 2;
|
||||
*pri80 = c->center_freq1 - 40 + 80 * tmp;
|
||||
break;
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
}
|
||||
}
|
||||
|
||||
static int cfg80211_chandef_get_width(const struct cfg80211_chan_def *c)
|
||||
{
|
||||
int width;
|
||||
|
||||
switch (c->width) {
|
||||
case NL80211_CHAN_WIDTH_5:
|
||||
width = 5;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_10:
|
||||
width = 10;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_20:
|
||||
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||
width = 20;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
width = 40;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_80P80:
|
||||
case NL80211_CHAN_WIDTH_80:
|
||||
width = 80;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_160:
|
||||
width = 160;
|
||||
break;
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
return -1;
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
const struct cfg80211_chan_def *
|
||||
cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1,
|
||||
const struct cfg80211_chan_def *c2)
|
||||
{
|
||||
u32 c1_pri40, c1_pri80, c2_pri40, c2_pri80;
|
||||
|
||||
/* If they are identical, return */
|
||||
if (cfg80211_chandef_identical(c1, c2))
|
||||
return c1;
|
||||
|
||||
/* otherwise, must have same control channel */
|
||||
if (c1->chan != c2->chan)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* If they have the same width, but aren't identical,
|
||||
* then they can't be compatible.
|
||||
*/
|
||||
if (c1->width == c2->width)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* can't be compatible if one of them is 5 or 10 MHz,
|
||||
* but they don't have the same width.
|
||||
*/
|
||||
if (c1->width == NL80211_CHAN_WIDTH_5 ||
|
||||
c1->width == NL80211_CHAN_WIDTH_10 ||
|
||||
c2->width == NL80211_CHAN_WIDTH_5 ||
|
||||
c2->width == NL80211_CHAN_WIDTH_10)
|
||||
return NULL;
|
||||
|
||||
if (c1->width == NL80211_CHAN_WIDTH_20_NOHT ||
|
||||
c1->width == NL80211_CHAN_WIDTH_20)
|
||||
return c2;
|
||||
|
||||
if (c2->width == NL80211_CHAN_WIDTH_20_NOHT ||
|
||||
c2->width == NL80211_CHAN_WIDTH_20)
|
||||
return c1;
|
||||
|
||||
chandef_primary_freqs(c1, &c1_pri40, &c1_pri80);
|
||||
chandef_primary_freqs(c2, &c2_pri40, &c2_pri80);
|
||||
|
||||
if (c1_pri40 != c2_pri40)
|
||||
return NULL;
|
||||
|
||||
WARN_ON(!c1_pri80 && !c2_pri80);
|
||||
if (c1_pri80 && c2_pri80 && c1_pri80 != c2_pri80)
|
||||
return NULL;
|
||||
|
||||
if (c1->width > c2->width)
|
||||
return c1;
|
||||
return c2;
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_chandef_compatible);
|
||||
|
||||
static void cfg80211_set_chans_dfs_state(struct wiphy *wiphy, u32 center_freq,
|
||||
u32 bandwidth,
|
||||
enum nl80211_dfs_state dfs_state)
|
||||
{
|
||||
struct ieee80211_channel *c;
|
||||
u32 freq;
|
||||
|
||||
for (freq = center_freq - bandwidth/2 + 10;
|
||||
freq <= center_freq + bandwidth/2 - 10;
|
||||
freq += 20) {
|
||||
c = ieee80211_get_channel(wiphy, freq);
|
||||
if (!c || !(c->flags & IEEE80211_CHAN_RADAR))
|
||||
continue;
|
||||
|
||||
c->dfs_state = dfs_state;
|
||||
c->dfs_state_entered = jiffies;
|
||||
}
|
||||
}
|
||||
|
||||
void cfg80211_set_dfs_state(struct wiphy *wiphy,
|
||||
const struct cfg80211_chan_def *chandef,
|
||||
enum nl80211_dfs_state dfs_state)
|
||||
{
|
||||
int width;
|
||||
|
||||
if (WARN_ON(!cfg80211_chandef_valid(chandef)))
|
||||
return;
|
||||
|
||||
width = cfg80211_chandef_get_width(chandef);
|
||||
if (width < 0)
|
||||
return;
|
||||
|
||||
cfg80211_set_chans_dfs_state(wiphy, chandef->center_freq1,
|
||||
width, dfs_state);
|
||||
|
||||
if (!chandef->center_freq2)
|
||||
return;
|
||||
cfg80211_set_chans_dfs_state(wiphy, chandef->center_freq2,
|
||||
width, dfs_state);
|
||||
}
|
||||
|
||||
static u32 cfg80211_get_start_freq(u32 center_freq,
|
||||
u32 bandwidth)
|
||||
{
|
||||
u32 start_freq;
|
||||
|
||||
if (bandwidth <= 20)
|
||||
start_freq = center_freq;
|
||||
else
|
||||
start_freq = center_freq - bandwidth/2 + 10;
|
||||
|
||||
return start_freq;
|
||||
}
|
||||
|
||||
static u32 cfg80211_get_end_freq(u32 center_freq,
|
||||
u32 bandwidth)
|
||||
{
|
||||
u32 end_freq;
|
||||
|
||||
if (bandwidth <= 20)
|
||||
end_freq = center_freq;
|
||||
else
|
||||
end_freq = center_freq + bandwidth/2 - 10;
|
||||
|
||||
return end_freq;
|
||||
}
|
||||
|
||||
static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy,
|
||||
u32 center_freq,
|
||||
u32 bandwidth)
|
||||
{
|
||||
struct ieee80211_channel *c;
|
||||
u32 freq, start_freq, end_freq;
|
||||
|
||||
start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
|
||||
end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
|
||||
|
||||
for (freq = start_freq; freq <= end_freq; freq += 20) {
|
||||
c = ieee80211_get_channel(wiphy, freq);
|
||||
if (!c)
|
||||
return -EINVAL;
|
||||
|
||||
if (c->flags & IEEE80211_CHAN_RADAR)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
|
||||
const struct cfg80211_chan_def *chandef,
|
||||
enum nl80211_iftype iftype)
|
||||
{
|
||||
int width;
|
||||
int ret;
|
||||
|
||||
if (WARN_ON(!cfg80211_chandef_valid(chandef)))
|
||||
return -EINVAL;
|
||||
|
||||
switch (iftype) {
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
case NL80211_IFTYPE_AP:
|
||||
case NL80211_IFTYPE_P2P_GO:
|
||||
case NL80211_IFTYPE_MESH_POINT:
|
||||
width = cfg80211_chandef_get_width(chandef);
|
||||
if (width < 0)
|
||||
return -EINVAL;
|
||||
|
||||
ret = cfg80211_get_chans_dfs_required(wiphy,
|
||||
chandef->center_freq1,
|
||||
width);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else if (ret > 0)
|
||||
return BIT(chandef->width);
|
||||
|
||||
if (!chandef->center_freq2)
|
||||
return 0;
|
||||
|
||||
ret = cfg80211_get_chans_dfs_required(wiphy,
|
||||
chandef->center_freq2,
|
||||
width);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else if (ret > 0)
|
||||
return BIT(chandef->width);
|
||||
|
||||
break;
|
||||
case NL80211_IFTYPE_STATION:
|
||||
case NL80211_IFTYPE_P2P_CLIENT:
|
||||
case NL80211_IFTYPE_MONITOR:
|
||||
case NL80211_IFTYPE_AP_VLAN:
|
||||
case NL80211_IFTYPE_WDS:
|
||||
case NL80211_IFTYPE_P2P_DEVICE:
|
||||
break;
|
||||
case NL80211_IFTYPE_UNSPECIFIED:
|
||||
case NUM_NL80211_IFTYPES:
|
||||
WARN_ON(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_chandef_dfs_required);
|
||||
|
||||
static int cfg80211_get_chans_dfs_usable(struct wiphy *wiphy,
|
||||
u32 center_freq,
|
||||
u32 bandwidth)
|
||||
{
|
||||
struct ieee80211_channel *c;
|
||||
u32 freq, start_freq, end_freq;
|
||||
int count = 0;
|
||||
|
||||
start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
|
||||
end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
|
||||
|
||||
/*
|
||||
* Check entire range of channels for the bandwidth.
|
||||
* Check all channels are DFS channels (DFS_USABLE or
|
||||
* DFS_AVAILABLE). Return number of usable channels
|
||||
* (require CAC). Allow DFS and non-DFS channel mix.
|
||||
*/
|
||||
for (freq = start_freq; freq <= end_freq; freq += 20) {
|
||||
c = ieee80211_get_channel(wiphy, freq);
|
||||
if (!c)
|
||||
return -EINVAL;
|
||||
|
||||
if (c->flags & IEEE80211_CHAN_DISABLED)
|
||||
return -EINVAL;
|
||||
|
||||
if (c->flags & IEEE80211_CHAN_RADAR) {
|
||||
if (c->dfs_state == NL80211_DFS_UNAVAILABLE)
|
||||
return -EINVAL;
|
||||
|
||||
if (c->dfs_state == NL80211_DFS_USABLE)
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
bool cfg80211_chandef_dfs_usable(struct wiphy *wiphy,
|
||||
const struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
int width;
|
||||
int r1, r2 = 0;
|
||||
|
||||
if (WARN_ON(!cfg80211_chandef_valid(chandef)))
|
||||
return false;
|
||||
|
||||
width = cfg80211_chandef_get_width(chandef);
|
||||
if (width < 0)
|
||||
return false;
|
||||
|
||||
r1 = cfg80211_get_chans_dfs_usable(wiphy, chandef->center_freq1,
|
||||
width);
|
||||
|
||||
if (r1 < 0)
|
||||
return false;
|
||||
|
||||
switch (chandef->width) {
|
||||
case NL80211_CHAN_WIDTH_80P80:
|
||||
WARN_ON(!chandef->center_freq2);
|
||||
r2 = cfg80211_get_chans_dfs_usable(wiphy,
|
||||
chandef->center_freq2,
|
||||
width);
|
||||
if (r2 < 0)
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
WARN_ON(chandef->center_freq2);
|
||||
break;
|
||||
}
|
||||
|
||||
return (r1 + r2 > 0);
|
||||
}
|
||||
|
||||
|
||||
static bool cfg80211_get_chans_dfs_available(struct wiphy *wiphy,
|
||||
u32 center_freq,
|
||||
u32 bandwidth)
|
||||
{
|
||||
struct ieee80211_channel *c;
|
||||
u32 freq, start_freq, end_freq;
|
||||
|
||||
start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
|
||||
end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
|
||||
|
||||
/*
|
||||
* Check entire range of channels for the bandwidth.
|
||||
* If any channel in between is disabled or has not
|
||||
* had gone through CAC return false
|
||||
*/
|
||||
for (freq = start_freq; freq <= end_freq; freq += 20) {
|
||||
c = ieee80211_get_channel(wiphy, freq);
|
||||
if (!c)
|
||||
return false;
|
||||
|
||||
if (c->flags & IEEE80211_CHAN_DISABLED)
|
||||
return false;
|
||||
|
||||
if ((c->flags & IEEE80211_CHAN_RADAR) &&
|
||||
(c->dfs_state != NL80211_DFS_AVAILABLE))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool cfg80211_chandef_dfs_available(struct wiphy *wiphy,
|
||||
const struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
int width;
|
||||
int r;
|
||||
|
||||
if (WARN_ON(!cfg80211_chandef_valid(chandef)))
|
||||
return false;
|
||||
|
||||
width = cfg80211_chandef_get_width(chandef);
|
||||
if (width < 0)
|
||||
return false;
|
||||
|
||||
r = cfg80211_get_chans_dfs_available(wiphy, chandef->center_freq1,
|
||||
width);
|
||||
|
||||
/* If any of channels unavailable for cf1 just return */
|
||||
if (!r)
|
||||
return r;
|
||||
|
||||
switch (chandef->width) {
|
||||
case NL80211_CHAN_WIDTH_80P80:
|
||||
WARN_ON(!chandef->center_freq2);
|
||||
r = cfg80211_get_chans_dfs_available(wiphy,
|
||||
chandef->center_freq2,
|
||||
width);
|
||||
default:
|
||||
WARN_ON(chandef->center_freq2);
|
||||
break;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static unsigned int cfg80211_get_chans_dfs_cac_time(struct wiphy *wiphy,
|
||||
u32 center_freq,
|
||||
u32 bandwidth)
|
||||
{
|
||||
struct ieee80211_channel *c;
|
||||
u32 start_freq, end_freq, freq;
|
||||
unsigned int dfs_cac_ms = 0;
|
||||
|
||||
start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
|
||||
end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
|
||||
|
||||
for (freq = start_freq; freq <= end_freq; freq += 20) {
|
||||
c = ieee80211_get_channel(wiphy, freq);
|
||||
if (!c)
|
||||
return 0;
|
||||
|
||||
if (c->flags & IEEE80211_CHAN_DISABLED)
|
||||
return 0;
|
||||
|
||||
if (!(c->flags & IEEE80211_CHAN_RADAR))
|
||||
continue;
|
||||
|
||||
if (c->dfs_cac_ms > dfs_cac_ms)
|
||||
dfs_cac_ms = c->dfs_cac_ms;
|
||||
}
|
||||
|
||||
return dfs_cac_ms;
|
||||
}
|
||||
|
||||
unsigned int
|
||||
cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy,
|
||||
const struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
int width;
|
||||
unsigned int t1 = 0, t2 = 0;
|
||||
|
||||
if (WARN_ON(!cfg80211_chandef_valid(chandef)))
|
||||
return 0;
|
||||
|
||||
width = cfg80211_chandef_get_width(chandef);
|
||||
if (width < 0)
|
||||
return 0;
|
||||
|
||||
t1 = cfg80211_get_chans_dfs_cac_time(wiphy,
|
||||
chandef->center_freq1,
|
||||
width);
|
||||
|
||||
if (!chandef->center_freq2)
|
||||
return t1;
|
||||
|
||||
t2 = cfg80211_get_chans_dfs_cac_time(wiphy,
|
||||
chandef->center_freq2,
|
||||
width);
|
||||
|
||||
return max(t1, t2);
|
||||
}
|
||||
|
||||
static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
|
||||
u32 center_freq, u32 bandwidth,
|
||||
u32 prohibited_flags)
|
||||
{
|
||||
struct ieee80211_channel *c;
|
||||
u32 freq, start_freq, end_freq;
|
||||
|
||||
start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
|
||||
end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
|
||||
|
||||
for (freq = start_freq; freq <= end_freq; freq += 20) {
|
||||
c = ieee80211_get_channel(wiphy, freq);
|
||||
if (!c || c->flags & prohibited_flags)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cfg80211_chandef_usable(struct wiphy *wiphy,
|
||||
const struct cfg80211_chan_def *chandef,
|
||||
u32 prohibited_flags)
|
||||
{
|
||||
struct ieee80211_sta_ht_cap *ht_cap;
|
||||
struct ieee80211_sta_vht_cap *vht_cap;
|
||||
u32 width, control_freq, cap;
|
||||
|
||||
if (WARN_ON(!cfg80211_chandef_valid(chandef)))
|
||||
return false;
|
||||
|
||||
ht_cap = &wiphy->bands[chandef->chan->band]->ht_cap;
|
||||
vht_cap = &wiphy->bands[chandef->chan->band]->vht_cap;
|
||||
|
||||
control_freq = chandef->chan->center_freq;
|
||||
|
||||
switch (chandef->width) {
|
||||
case NL80211_CHAN_WIDTH_5:
|
||||
width = 5;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_10:
|
||||
prohibited_flags |= IEEE80211_CHAN_NO_10MHZ;
|
||||
width = 10;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_20:
|
||||
if (!ht_cap->ht_supported)
|
||||
return false;
|
||||
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||
prohibited_flags |= IEEE80211_CHAN_NO_20MHZ;
|
||||
width = 20;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
width = 40;
|
||||
if (!ht_cap->ht_supported)
|
||||
return false;
|
||||
if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
|
||||
ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)
|
||||
return false;
|
||||
if (chandef->center_freq1 < control_freq &&
|
||||
chandef->chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
|
||||
return false;
|
||||
if (chandef->center_freq1 > control_freq &&
|
||||
chandef->chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
|
||||
return false;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_80P80:
|
||||
cap = vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
|
||||
if (cap != IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)
|
||||
return false;
|
||||
case NL80211_CHAN_WIDTH_80:
|
||||
if (!vht_cap->vht_supported)
|
||||
return false;
|
||||
prohibited_flags |= IEEE80211_CHAN_NO_80MHZ;
|
||||
width = 80;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_160:
|
||||
if (!vht_cap->vht_supported)
|
||||
return false;
|
||||
cap = vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
|
||||
if (cap != IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ &&
|
||||
cap != IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)
|
||||
return false;
|
||||
prohibited_flags |= IEEE80211_CHAN_NO_160MHZ;
|
||||
width = 160;
|
||||
break;
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: What if there are only certain 80/160/80+80 MHz channels
|
||||
* allowed by the driver, or only certain combinations?
|
||||
* For 40 MHz the driver can set the NO_HT40 flags, but for
|
||||
* 80/160 MHz and in particular 80+80 MHz this isn't really
|
||||
* feasible and we only have NO_80MHZ/NO_160MHZ so far but
|
||||
* no way to cover 80+80 MHz or more complex restrictions.
|
||||
* Note that such restrictions also need to be advertised to
|
||||
* userspace, for example for P2P channel selection.
|
||||
*/
|
||||
|
||||
if (width > 20)
|
||||
prohibited_flags |= IEEE80211_CHAN_NO_OFDM;
|
||||
|
||||
/* 5 and 10 MHz are only defined for the OFDM PHY */
|
||||
if (width < 20)
|
||||
prohibited_flags |= IEEE80211_CHAN_NO_OFDM;
|
||||
|
||||
|
||||
if (!cfg80211_secondary_chans_ok(wiphy, chandef->center_freq1,
|
||||
width, prohibited_flags))
|
||||
return false;
|
||||
|
||||
if (!chandef->center_freq2)
|
||||
return true;
|
||||
return cfg80211_secondary_chans_ok(wiphy, chandef->center_freq2,
|
||||
width, prohibited_flags);
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_chandef_usable);
|
||||
|
||||
/*
|
||||
* For GO only, check if the channel can be used under permissive conditions
|
||||
* mandated by the some regulatory bodies, i.e., the channel is marked with
|
||||
* IEEE80211_CHAN_GO_CONCURRENT and there is an additional station interface
|
||||
* associated to an AP on the same channel or on the same UNII band
|
||||
* (assuming that the AP is an authorized master).
|
||||
* In addition allow the GO to operate on a channel on which indoor operation is
|
||||
* allowed, iff we are currently operating in an indoor environment.
|
||||
*/
|
||||
static bool cfg80211_go_permissive_chan(struct cfg80211_registered_device *rdev,
|
||||
struct ieee80211_channel *chan)
|
||||
{
|
||||
struct wireless_dev *wdev_iter;
|
||||
struct wiphy *wiphy = wiphy_idx_to_wiphy(rdev->wiphy_idx);
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
if (!config_enabled(CONFIG_CFG80211_REG_RELAX_NO_IR) ||
|
||||
!(wiphy->regulatory_flags & REGULATORY_ENABLE_RELAX_NO_IR))
|
||||
return false;
|
||||
|
||||
if (regulatory_indoor_allowed() &&
|
||||
(chan->flags & IEEE80211_CHAN_INDOOR_ONLY))
|
||||
return true;
|
||||
|
||||
if (!(chan->flags & IEEE80211_CHAN_GO_CONCURRENT))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Generally, it is possible to rely on another device/driver to allow
|
||||
* the GO concurrent relaxation, however, since the device can further
|
||||
* enforce the relaxation (by doing a similar verifications as this),
|
||||
* and thus fail the GO instantiation, consider only the interfaces of
|
||||
* the current registered device.
|
||||
*/
|
||||
list_for_each_entry(wdev_iter, &rdev->wdev_list, list) {
|
||||
struct ieee80211_channel *other_chan = NULL;
|
||||
int r1, r2;
|
||||
|
||||
if (wdev_iter->iftype != NL80211_IFTYPE_STATION ||
|
||||
!netif_running(wdev_iter->netdev))
|
||||
continue;
|
||||
|
||||
wdev_lock(wdev_iter);
|
||||
if (wdev_iter->current_bss)
|
||||
other_chan = wdev_iter->current_bss->pub.channel;
|
||||
wdev_unlock(wdev_iter);
|
||||
|
||||
if (!other_chan)
|
||||
continue;
|
||||
|
||||
if (chan == other_chan)
|
||||
return true;
|
||||
|
||||
if (chan->band != IEEE80211_BAND_5GHZ)
|
||||
continue;
|
||||
|
||||
r1 = cfg80211_get_unii(chan->center_freq);
|
||||
r2 = cfg80211_get_unii(other_chan->center_freq);
|
||||
|
||||
if (r1 != -EINVAL && r1 == r2) {
|
||||
/*
|
||||
* At some locations channels 149-165 are considered a
|
||||
* bundle, but at other locations, e.g., Indonesia,
|
||||
* channels 149-161 are considered a bundle while
|
||||
* channel 165 is left out and considered to be in a
|
||||
* different bundle. Thus, in case that there is a
|
||||
* station interface connected to an AP on channel 165,
|
||||
* it is assumed that channels 149-161 are allowed for
|
||||
* GO operations. However, having a station interface
|
||||
* connected to an AP on channels 149-161, does not
|
||||
* allow GO operation on channel 165.
|
||||
*/
|
||||
if (chan->center_freq == 5825 &&
|
||||
other_chan->center_freq != 5825)
|
||||
continue;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cfg80211_reg_can_beacon(struct wiphy *wiphy,
|
||||
struct cfg80211_chan_def *chandef,
|
||||
enum nl80211_iftype iftype)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
||||
bool res;
|
||||
u32 prohibited_flags = IEEE80211_CHAN_DISABLED |
|
||||
IEEE80211_CHAN_RADAR;
|
||||
|
||||
trace_cfg80211_reg_can_beacon(wiphy, chandef, iftype);
|
||||
|
||||
/*
|
||||
* Under certain conditions suggested by the some regulatory bodies
|
||||
* a GO can operate on channels marked with IEEE80211_NO_IR
|
||||
* so set this flag only if such relaxations are not enabled and
|
||||
* the conditions are not met.
|
||||
*/
|
||||
if (iftype != NL80211_IFTYPE_P2P_GO ||
|
||||
!cfg80211_go_permissive_chan(rdev, chandef->chan))
|
||||
prohibited_flags |= IEEE80211_CHAN_NO_IR;
|
||||
|
||||
if (cfg80211_chandef_dfs_required(wiphy, chandef, iftype) > 0 &&
|
||||
cfg80211_chandef_dfs_available(wiphy, chandef)) {
|
||||
/* We can skip IEEE80211_CHAN_NO_IR if chandef dfs available */
|
||||
prohibited_flags = IEEE80211_CHAN_DISABLED;
|
||||
}
|
||||
|
||||
res = cfg80211_chandef_usable(wiphy, chandef, prohibited_flags);
|
||||
|
||||
trace_cfg80211_return_bool(res);
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_reg_can_beacon);
|
||||
|
||||
int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
if (!rdev->ops->set_monitor_channel)
|
||||
return -EOPNOTSUPP;
|
||||
if (!cfg80211_has_monitors_only(rdev))
|
||||
return -EBUSY;
|
||||
|
||||
return rdev_set_monitor_channel(rdev, chandef);
|
||||
}
|
||||
|
||||
void
|
||||
cfg80211_get_chan_state(struct wireless_dev *wdev,
|
||||
struct ieee80211_channel **chan,
|
||||
enum cfg80211_chan_mode *chanmode,
|
||||
u8 *radar_detect)
|
||||
{
|
||||
int ret;
|
||||
|
||||
*chan = NULL;
|
||||
*chanmode = CHAN_MODE_UNDEFINED;
|
||||
|
||||
ASSERT_WDEV_LOCK(wdev);
|
||||
|
||||
if (wdev->netdev && !netif_running(wdev->netdev))
|
||||
return;
|
||||
|
||||
switch (wdev->iftype) {
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
if (wdev->current_bss) {
|
||||
*chan = wdev->current_bss->pub.channel;
|
||||
*chanmode = (wdev->ibss_fixed &&
|
||||
!wdev->ibss_dfs_possible)
|
||||
? CHAN_MODE_SHARED
|
||||
: CHAN_MODE_EXCLUSIVE;
|
||||
|
||||
/* consider worst-case - IBSS can try to return to the
|
||||
* original user-specified channel as creator */
|
||||
if (wdev->ibss_dfs_possible)
|
||||
*radar_detect |= BIT(wdev->chandef.width);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case NL80211_IFTYPE_STATION:
|
||||
case NL80211_IFTYPE_P2P_CLIENT:
|
||||
if (wdev->current_bss) {
|
||||
*chan = wdev->current_bss->pub.channel;
|
||||
*chanmode = CHAN_MODE_SHARED;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case NL80211_IFTYPE_AP:
|
||||
case NL80211_IFTYPE_P2P_GO:
|
||||
if (wdev->cac_started) {
|
||||
*chan = wdev->chandef.chan;
|
||||
*chanmode = CHAN_MODE_SHARED;
|
||||
*radar_detect |= BIT(wdev->chandef.width);
|
||||
} else if (wdev->beacon_interval) {
|
||||
*chan = wdev->chandef.chan;
|
||||
*chanmode = CHAN_MODE_SHARED;
|
||||
|
||||
ret = cfg80211_chandef_dfs_required(wdev->wiphy,
|
||||
&wdev->chandef,
|
||||
wdev->iftype);
|
||||
WARN_ON(ret < 0);
|
||||
if (ret > 0)
|
||||
*radar_detect |= BIT(wdev->chandef.width);
|
||||
}
|
||||
return;
|
||||
case NL80211_IFTYPE_MESH_POINT:
|
||||
if (wdev->mesh_id_len) {
|
||||
*chan = wdev->chandef.chan;
|
||||
*chanmode = CHAN_MODE_SHARED;
|
||||
|
||||
ret = cfg80211_chandef_dfs_required(wdev->wiphy,
|
||||
&wdev->chandef,
|
||||
wdev->iftype);
|
||||
WARN_ON(ret < 0);
|
||||
if (ret > 0)
|
||||
*radar_detect |= BIT(wdev->chandef.width);
|
||||
}
|
||||
return;
|
||||
case NL80211_IFTYPE_MONITOR:
|
||||
case NL80211_IFTYPE_AP_VLAN:
|
||||
case NL80211_IFTYPE_WDS:
|
||||
case NL80211_IFTYPE_P2P_DEVICE:
|
||||
/* these interface types don't really have a channel */
|
||||
return;
|
||||
case NL80211_IFTYPE_UNSPECIFIED:
|
||||
case NUM_NL80211_IFTYPES:
|
||||
WARN_ON(1);
|
||||
}
|
||||
}
|
1124
net/wireless/core.c
Normal file
1124
net/wireless/core.c
Normal file
File diff suppressed because it is too large
Load diff
470
net/wireless/core.h
Normal file
470
net/wireless/core.h
Normal file
|
@ -0,0 +1,470 @@
|
|||
/*
|
||||
* Wireless configuration interface internals.
|
||||
*
|
||||
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
|
||||
*/
|
||||
#ifndef __NET_WIRELESS_CORE_H
|
||||
#define __NET_WIRELESS_CORE_H
|
||||
#include <linux/list.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/rfkill.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <net/genetlink.h>
|
||||
#include <net/cfg80211.h>
|
||||
#include "reg.h"
|
||||
|
||||
|
||||
#define WIPHY_IDX_INVALID -1
|
||||
|
||||
struct cfg80211_registered_device {
|
||||
const struct cfg80211_ops *ops;
|
||||
struct list_head list;
|
||||
|
||||
/* rfkill support */
|
||||
struct rfkill_ops rfkill_ops;
|
||||
struct rfkill *rfkill;
|
||||
struct work_struct rfkill_sync;
|
||||
|
||||
/* ISO / IEC 3166 alpha2 for which this device is receiving
|
||||
* country IEs on, this can help disregard country IEs from APs
|
||||
* on the same alpha2 quickly. The alpha2 may differ from
|
||||
* cfg80211_regdomain's alpha2 when an intersection has occurred.
|
||||
* If the AP is reconfigured this can also be used to tell us if
|
||||
* the country on the country IE changed. */
|
||||
char country_ie_alpha2[2];
|
||||
|
||||
/* If a Country IE has been received this tells us the environment
|
||||
* which its telling us its in. This defaults to ENVIRON_ANY */
|
||||
enum environment_cap env;
|
||||
|
||||
/* wiphy index, internal only */
|
||||
int wiphy_idx;
|
||||
|
||||
/* associated wireless interfaces, protected by rtnl or RCU */
|
||||
struct list_head wdev_list;
|
||||
int devlist_generation, wdev_id;
|
||||
int opencount; /* also protected by devlist_mtx */
|
||||
wait_queue_head_t dev_wait;
|
||||
|
||||
struct list_head beacon_registrations;
|
||||
spinlock_t beacon_registrations_lock;
|
||||
|
||||
/* protected by RTNL only */
|
||||
int num_running_ifaces;
|
||||
int num_running_monitor_ifaces;
|
||||
|
||||
/* BSSes/scanning */
|
||||
spinlock_t bss_lock;
|
||||
struct list_head bss_list;
|
||||
struct rb_root bss_tree;
|
||||
u32 bss_generation;
|
||||
struct cfg80211_scan_request *scan_req; /* protected by RTNL */
|
||||
struct sk_buff *scan_msg;
|
||||
struct cfg80211_sched_scan_request *sched_scan_req;
|
||||
unsigned long suspend_at;
|
||||
struct work_struct scan_done_wk;
|
||||
struct work_struct sched_scan_results_wk;
|
||||
|
||||
struct genl_info *cur_cmd_info;
|
||||
|
||||
struct work_struct conn_work;
|
||||
struct work_struct event_work;
|
||||
|
||||
struct delayed_work dfs_update_channels_wk;
|
||||
|
||||
/* netlink port which started critical protocol (0 means not started) */
|
||||
u32 crit_proto_nlportid;
|
||||
|
||||
struct cfg80211_coalesce *coalesce;
|
||||
|
||||
spinlock_t destroy_list_lock;
|
||||
struct list_head destroy_list;
|
||||
struct work_struct destroy_work;
|
||||
|
||||
/* must be last because of the way we do wiphy_priv(),
|
||||
* and it should at least be aligned to NETDEV_ALIGN */
|
||||
struct wiphy wiphy __aligned(NETDEV_ALIGN);
|
||||
};
|
||||
|
||||
static inline
|
||||
struct cfg80211_registered_device *wiphy_to_rdev(struct wiphy *wiphy)
|
||||
{
|
||||
BUG_ON(!wiphy);
|
||||
return container_of(wiphy, struct cfg80211_registered_device, wiphy);
|
||||
}
|
||||
|
||||
static inline void
|
||||
cfg80211_rdev_free_wowlan(struct cfg80211_registered_device *rdev)
|
||||
{
|
||||
#ifdef CONFIG_PM
|
||||
int i;
|
||||
|
||||
if (!rdev->wiphy.wowlan_config)
|
||||
return;
|
||||
for (i = 0; i < rdev->wiphy.wowlan_config->n_patterns; i++)
|
||||
kfree(rdev->wiphy.wowlan_config->patterns[i].mask);
|
||||
kfree(rdev->wiphy.wowlan_config->patterns);
|
||||
if (rdev->wiphy.wowlan_config->tcp &&
|
||||
rdev->wiphy.wowlan_config->tcp->sock)
|
||||
sock_release(rdev->wiphy.wowlan_config->tcp->sock);
|
||||
kfree(rdev->wiphy.wowlan_config->tcp);
|
||||
kfree(rdev->wiphy.wowlan_config);
|
||||
#endif
|
||||
}
|
||||
|
||||
extern struct workqueue_struct *cfg80211_wq;
|
||||
extern struct list_head cfg80211_rdev_list;
|
||||
extern int cfg80211_rdev_list_generation;
|
||||
|
||||
struct cfg80211_internal_bss {
|
||||
struct list_head list;
|
||||
struct list_head hidden_list;
|
||||
struct rb_node rbn;
|
||||
unsigned long ts;
|
||||
unsigned long refcount;
|
||||
atomic_t hold;
|
||||
|
||||
/* must be last because of priv member */
|
||||
struct cfg80211_bss pub;
|
||||
};
|
||||
|
||||
static inline struct cfg80211_internal_bss *bss_from_pub(struct cfg80211_bss *pub)
|
||||
{
|
||||
return container_of(pub, struct cfg80211_internal_bss, pub);
|
||||
}
|
||||
|
||||
static inline void cfg80211_hold_bss(struct cfg80211_internal_bss *bss)
|
||||
{
|
||||
atomic_inc(&bss->hold);
|
||||
}
|
||||
|
||||
static inline void cfg80211_unhold_bss(struct cfg80211_internal_bss *bss)
|
||||
{
|
||||
int r = atomic_dec_return(&bss->hold);
|
||||
WARN_ON(r < 0);
|
||||
}
|
||||
|
||||
|
||||
struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx);
|
||||
int get_wiphy_idx(struct wiphy *wiphy);
|
||||
|
||||
struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx);
|
||||
|
||||
int cfg80211_switch_netns(struct cfg80211_registered_device *rdev,
|
||||
struct net *net);
|
||||
|
||||
static inline void wdev_lock(struct wireless_dev *wdev)
|
||||
__acquires(wdev)
|
||||
{
|
||||
mutex_lock(&wdev->mtx);
|
||||
__acquire(wdev->mtx);
|
||||
}
|
||||
|
||||
static inline void wdev_unlock(struct wireless_dev *wdev)
|
||||
__releases(wdev)
|
||||
{
|
||||
__release(wdev->mtx);
|
||||
mutex_unlock(&wdev->mtx);
|
||||
}
|
||||
|
||||
#define ASSERT_WDEV_LOCK(wdev) lockdep_assert_held(&(wdev)->mtx)
|
||||
|
||||
static inline bool cfg80211_has_monitors_only(struct cfg80211_registered_device *rdev)
|
||||
{
|
||||
ASSERT_RTNL();
|
||||
|
||||
return rdev->num_running_ifaces == rdev->num_running_monitor_ifaces &&
|
||||
rdev->num_running_ifaces > 0;
|
||||
}
|
||||
|
||||
enum cfg80211_event_type {
|
||||
EVENT_CONNECT_RESULT,
|
||||
EVENT_ROAMED,
|
||||
EVENT_DISCONNECTED,
|
||||
EVENT_IBSS_JOINED,
|
||||
EVENT_STOPPED,
|
||||
};
|
||||
|
||||
struct cfg80211_event {
|
||||
struct list_head list;
|
||||
enum cfg80211_event_type type;
|
||||
|
||||
union {
|
||||
struct {
|
||||
u8 bssid[ETH_ALEN];
|
||||
const u8 *req_ie;
|
||||
const u8 *resp_ie;
|
||||
size_t req_ie_len;
|
||||
size_t resp_ie_len;
|
||||
u16 status;
|
||||
} cr;
|
||||
struct {
|
||||
const u8 *req_ie;
|
||||
const u8 *resp_ie;
|
||||
size_t req_ie_len;
|
||||
size_t resp_ie_len;
|
||||
struct cfg80211_bss *bss;
|
||||
} rm;
|
||||
struct {
|
||||
const u8 *ie;
|
||||
size_t ie_len;
|
||||
u16 reason;
|
||||
} dc;
|
||||
struct {
|
||||
u8 bssid[ETH_ALEN];
|
||||
struct ieee80211_channel *channel;
|
||||
} ij;
|
||||
};
|
||||
};
|
||||
|
||||
struct cfg80211_cached_keys {
|
||||
struct key_params params[6];
|
||||
u8 data[6][WLAN_MAX_KEY_LEN];
|
||||
int def, defmgmt;
|
||||
};
|
||||
|
||||
enum cfg80211_chan_mode {
|
||||
CHAN_MODE_UNDEFINED,
|
||||
CHAN_MODE_SHARED,
|
||||
CHAN_MODE_EXCLUSIVE,
|
||||
};
|
||||
|
||||
struct cfg80211_beacon_registration {
|
||||
struct list_head list;
|
||||
u32 nlportid;
|
||||
};
|
||||
|
||||
struct cfg80211_iface_destroy {
|
||||
struct list_head list;
|
||||
u32 nlportid;
|
||||
};
|
||||
|
||||
void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev);
|
||||
|
||||
/* free object */
|
||||
void cfg80211_dev_free(struct cfg80211_registered_device *rdev);
|
||||
|
||||
int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
|
||||
char *newname);
|
||||
|
||||
void ieee80211_set_bitrate_flags(struct wiphy *wiphy);
|
||||
|
||||
void cfg80211_bss_expire(struct cfg80211_registered_device *rdev);
|
||||
void cfg80211_bss_age(struct cfg80211_registered_device *rdev,
|
||||
unsigned long age_secs);
|
||||
|
||||
/* IBSS */
|
||||
int cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct cfg80211_ibss_params *params,
|
||||
struct cfg80211_cached_keys *connkeys);
|
||||
void cfg80211_clear_ibss(struct net_device *dev, bool nowext);
|
||||
int __cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, bool nowext);
|
||||
int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, bool nowext);
|
||||
void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
|
||||
struct ieee80211_channel *channel);
|
||||
int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev);
|
||||
|
||||
/* mesh */
|
||||
extern const struct mesh_config default_mesh_config;
|
||||
extern const struct mesh_setup default_mesh_setup;
|
||||
int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct mesh_setup *setup,
|
||||
const struct mesh_config *conf);
|
||||
int cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct mesh_setup *setup,
|
||||
const struct mesh_config *conf);
|
||||
int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev);
|
||||
int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev);
|
||||
int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev,
|
||||
struct cfg80211_chan_def *chandef);
|
||||
|
||||
/* AP */
|
||||
int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, bool notify);
|
||||
int cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, bool notify);
|
||||
|
||||
/* MLME */
|
||||
int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct ieee80211_channel *chan,
|
||||
enum nl80211_auth_type auth_type,
|
||||
const u8 *bssid,
|
||||
const u8 *ssid, int ssid_len,
|
||||
const u8 *ie, int ie_len,
|
||||
const u8 *key, int key_len, int key_idx,
|
||||
const u8 *sae_data, int sae_data_len);
|
||||
int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct ieee80211_channel *chan,
|
||||
const u8 *bssid,
|
||||
const u8 *ssid, int ssid_len,
|
||||
struct cfg80211_assoc_request *req);
|
||||
int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, const u8 *bssid,
|
||||
const u8 *ie, int ie_len, u16 reason,
|
||||
bool local_state_change);
|
||||
int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, const u8 *bssid,
|
||||
const u8 *ie, int ie_len, u16 reason,
|
||||
bool local_state_change);
|
||||
void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev);
|
||||
int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_pid,
|
||||
u16 frame_type, const u8 *match_data,
|
||||
int match_len);
|
||||
void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid);
|
||||
void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev);
|
||||
int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev,
|
||||
struct cfg80211_mgmt_tx_params *params,
|
||||
u64 *cookie);
|
||||
void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa,
|
||||
const struct ieee80211_ht_cap *ht_capa_mask);
|
||||
void cfg80211_oper_and_vht_capa(struct ieee80211_vht_cap *vht_capa,
|
||||
const struct ieee80211_vht_cap *vht_capa_mask);
|
||||
|
||||
/* SME events */
|
||||
int cfg80211_connect(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct cfg80211_connect_params *connect,
|
||||
struct cfg80211_cached_keys *connkeys,
|
||||
const u8 *prev_bssid);
|
||||
void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
|
||||
const u8 *req_ie, size_t req_ie_len,
|
||||
const u8 *resp_ie, size_t resp_ie_len,
|
||||
u16 status, bool wextev,
|
||||
struct cfg80211_bss *bss);
|
||||
void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
|
||||
size_t ie_len, u16 reason, bool from_ap);
|
||||
int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, u16 reason,
|
||||
bool wextev);
|
||||
void __cfg80211_roamed(struct wireless_dev *wdev,
|
||||
struct cfg80211_bss *bss,
|
||||
const u8 *req_ie, size_t req_ie_len,
|
||||
const u8 *resp_ie, size_t resp_ie_len);
|
||||
int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev);
|
||||
|
||||
/* SME implementation */
|
||||
void cfg80211_conn_work(struct work_struct *work);
|
||||
void cfg80211_sme_scan_done(struct net_device *dev);
|
||||
bool cfg80211_sme_rx_assoc_resp(struct wireless_dev *wdev, u16 status);
|
||||
void cfg80211_sme_rx_auth(struct wireless_dev *wdev, const u8 *buf, size_t len);
|
||||
void cfg80211_sme_disassoc(struct wireless_dev *wdev);
|
||||
void cfg80211_sme_deauth(struct wireless_dev *wdev);
|
||||
void cfg80211_sme_auth_timeout(struct wireless_dev *wdev);
|
||||
void cfg80211_sme_assoc_timeout(struct wireless_dev *wdev);
|
||||
|
||||
/* internal helpers */
|
||||
bool cfg80211_supported_cipher_suite(struct wiphy *wiphy, u32 cipher);
|
||||
int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,
|
||||
struct key_params *params, int key_idx,
|
||||
bool pairwise, const u8 *mac_addr);
|
||||
void __cfg80211_scan_done(struct work_struct *wk);
|
||||
void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev,
|
||||
bool send_message);
|
||||
void __cfg80211_sched_scan_results(struct work_struct *wk);
|
||||
int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
|
||||
bool driver_initiated);
|
||||
void cfg80211_upload_connect_keys(struct wireless_dev *wdev);
|
||||
int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, enum nl80211_iftype ntype,
|
||||
u32 *flags, struct vif_params *params);
|
||||
void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev);
|
||||
void cfg80211_process_wdev_events(struct wireless_dev *wdev);
|
||||
|
||||
int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev,
|
||||
enum nl80211_iftype iftype,
|
||||
struct ieee80211_channel *chan,
|
||||
enum cfg80211_chan_mode chanmode,
|
||||
u8 radar_detect);
|
||||
|
||||
/**
|
||||
* cfg80211_chandef_dfs_usable - checks if chandef is DFS usable
|
||||
* @wiphy: the wiphy to validate against
|
||||
* @chandef: the channel definition to check
|
||||
*
|
||||
* Checks if chandef is usable and we can/need start CAC on such channel.
|
||||
*
|
||||
* Return: Return true if all channels available and at least
|
||||
* one channel require CAC (NL80211_DFS_USABLE)
|
||||
*/
|
||||
bool cfg80211_chandef_dfs_usable(struct wiphy *wiphy,
|
||||
const struct cfg80211_chan_def *chandef);
|
||||
|
||||
void cfg80211_set_dfs_state(struct wiphy *wiphy,
|
||||
const struct cfg80211_chan_def *chandef,
|
||||
enum nl80211_dfs_state dfs_state);
|
||||
|
||||
void cfg80211_dfs_channels_update_work(struct work_struct *work);
|
||||
|
||||
unsigned int
|
||||
cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy,
|
||||
const struct cfg80211_chan_def *chandef);
|
||||
|
||||
static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
|
||||
{
|
||||
unsigned long end = jiffies;
|
||||
|
||||
if (end >= start)
|
||||
return jiffies_to_msecs(end - start);
|
||||
|
||||
return jiffies_to_msecs(end + (ULONG_MAX - start) + 1);
|
||||
}
|
||||
|
||||
void
|
||||
cfg80211_get_chan_state(struct wireless_dev *wdev,
|
||||
struct ieee80211_channel **chan,
|
||||
enum cfg80211_chan_mode *chanmode,
|
||||
u8 *radar_detect);
|
||||
|
||||
int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,
|
||||
struct cfg80211_chan_def *chandef);
|
||||
|
||||
int ieee80211_get_ratemask(struct ieee80211_supported_band *sband,
|
||||
const u8 *rates, unsigned int n_rates,
|
||||
u32 *mask);
|
||||
|
||||
int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
|
||||
u32 beacon_int);
|
||||
|
||||
void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,
|
||||
enum nl80211_iftype iftype, int num);
|
||||
|
||||
void __cfg80211_leave(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev);
|
||||
void cfg80211_leave(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev);
|
||||
|
||||
void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev);
|
||||
|
||||
#define CFG80211_MAX_NUM_DIFFERENT_CHANNELS 10
|
||||
|
||||
#ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS
|
||||
#define CFG80211_DEV_WARN_ON(cond) WARN_ON(cond)
|
||||
#else
|
||||
/*
|
||||
* Trick to enable using it as a condition,
|
||||
* and also not give a warning when it's
|
||||
* not used that way.
|
||||
*/
|
||||
#define CFG80211_DEV_WARN_ON(cond) ({bool __r = (cond); __r; })
|
||||
#endif
|
||||
|
||||
#endif /* __NET_WIRELESS_CORE_H */
|
17
net/wireless/db.txt
Normal file
17
net/wireless/db.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
#
|
||||
# This file is a placeholder to prevent accidental build breakage if someone
|
||||
# enables CONFIG_CFG80211_INTERNAL_REGDB. Almost no one actually needs to
|
||||
# enable that build option.
|
||||
#
|
||||
# You should be using CRDA instead. It is even better if you use the CRDA
|
||||
# package provided by your distribution, since they will probably keep it
|
||||
# up-to-date on your behalf.
|
||||
#
|
||||
# If you _really_ intend to use CONFIG_CFG80211_INTERNAL_REGDB then you will
|
||||
# need to replace this file with one containing appropriately formatted
|
||||
# regulatory rules that cover the regulatory domains you will be using. Your
|
||||
# best option is to extract the db.txt file from the wireless-regdb git
|
||||
# repository:
|
||||
#
|
||||
# git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-regdb.git
|
||||
#
|
117
net/wireless/debugfs.c
Normal file
117
net/wireless/debugfs.c
Normal file
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* cfg80211 debugfs
|
||||
*
|
||||
* Copyright 2009 Luis R. Rodriguez <lrodriguez@atheros.com>
|
||||
* Copyright 2007 Johannes Berg <johannes@sipsolutions.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include "core.h"
|
||||
#include "debugfs.h"
|
||||
|
||||
#define DEBUGFS_READONLY_FILE(name, buflen, fmt, value...) \
|
||||
static ssize_t name## _read(struct file *file, char __user *userbuf, \
|
||||
size_t count, loff_t *ppos) \
|
||||
{ \
|
||||
struct wiphy *wiphy= file->private_data; \
|
||||
char buf[buflen]; \
|
||||
int res; \
|
||||
\
|
||||
res = scnprintf(buf, buflen, fmt "\n", ##value); \
|
||||
return simple_read_from_buffer(userbuf, count, ppos, buf, res); \
|
||||
} \
|
||||
\
|
||||
static const struct file_operations name## _ops = { \
|
||||
.read = name## _read, \
|
||||
.open = simple_open, \
|
||||
.llseek = generic_file_llseek, \
|
||||
};
|
||||
|
||||
DEBUGFS_READONLY_FILE(rts_threshold, 20, "%d",
|
||||
wiphy->rts_threshold)
|
||||
DEBUGFS_READONLY_FILE(fragmentation_threshold, 20, "%d",
|
||||
wiphy->frag_threshold);
|
||||
DEBUGFS_READONLY_FILE(short_retry_limit, 20, "%d",
|
||||
wiphy->retry_short)
|
||||
DEBUGFS_READONLY_FILE(long_retry_limit, 20, "%d",
|
||||
wiphy->retry_long);
|
||||
|
||||
static int ht_print_chan(struct ieee80211_channel *chan,
|
||||
char *buf, int buf_size, int offset)
|
||||
{
|
||||
if (WARN_ON(offset > buf_size))
|
||||
return 0;
|
||||
|
||||
if (chan->flags & IEEE80211_CHAN_DISABLED)
|
||||
return scnprintf(buf + offset,
|
||||
buf_size - offset,
|
||||
"%d Disabled\n",
|
||||
chan->center_freq);
|
||||
|
||||
return scnprintf(buf + offset,
|
||||
buf_size - offset,
|
||||
"%d HT40 %c%c\n",
|
||||
chan->center_freq,
|
||||
(chan->flags & IEEE80211_CHAN_NO_HT40MINUS) ?
|
||||
' ' : '-',
|
||||
(chan->flags & IEEE80211_CHAN_NO_HT40PLUS) ?
|
||||
' ' : '+');
|
||||
}
|
||||
|
||||
static ssize_t ht40allow_map_read(struct file *file,
|
||||
char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct wiphy *wiphy = file->private_data;
|
||||
char *buf;
|
||||
unsigned int offset = 0, buf_size = PAGE_SIZE, i, r;
|
||||
enum ieee80211_band band;
|
||||
struct ieee80211_supported_band *sband;
|
||||
|
||||
buf = kzalloc(buf_size, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
rtnl_lock();
|
||||
|
||||
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
|
||||
sband = wiphy->bands[band];
|
||||
if (!sband)
|
||||
continue;
|
||||
for (i = 0; i < sband->n_channels; i++)
|
||||
offset += ht_print_chan(&sband->channels[i],
|
||||
buf, buf_size, offset);
|
||||
}
|
||||
|
||||
rtnl_unlock();
|
||||
|
||||
r = simple_read_from_buffer(user_buf, count, ppos, buf, offset);
|
||||
|
||||
kfree(buf);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static const struct file_operations ht40allow_map_ops = {
|
||||
.read = ht40allow_map_read,
|
||||
.open = simple_open,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
#define DEBUGFS_ADD(name) \
|
||||
debugfs_create_file(#name, S_IRUGO, phyd, &rdev->wiphy, &name## _ops);
|
||||
|
||||
void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev)
|
||||
{
|
||||
struct dentry *phyd = rdev->wiphy.debugfsdir;
|
||||
|
||||
DEBUGFS_ADD(rts_threshold);
|
||||
DEBUGFS_ADD(fragmentation_threshold);
|
||||
DEBUGFS_ADD(short_retry_limit);
|
||||
DEBUGFS_ADD(long_retry_limit);
|
||||
DEBUGFS_ADD(ht40allow_map);
|
||||
}
|
11
net/wireless/debugfs.h
Normal file
11
net/wireless/debugfs.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
#ifndef __CFG80211_DEBUGFS_H
|
||||
#define __CFG80211_DEBUGFS_H
|
||||
|
||||
#ifdef CONFIG_CFG80211_DEBUGFS
|
||||
void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev);
|
||||
#else
|
||||
static inline
|
||||
void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev) {}
|
||||
#endif
|
||||
|
||||
#endif /* __CFG80211_DEBUGFS_H */
|
24
net/wireless/ethtool.c
Normal file
24
net/wireless/ethtool.c
Normal file
|
@ -0,0 +1,24 @@
|
|||
#include <linux/utsname.h>
|
||||
#include <net/cfg80211.h>
|
||||
#include "core.h"
|
||||
#include "rdev-ops.h"
|
||||
|
||||
void cfg80211_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
|
||||
strlcpy(info->driver, wiphy_dev(wdev->wiphy)->driver->name,
|
||||
sizeof(info->driver));
|
||||
|
||||
strlcpy(info->version, init_utsname()->release, sizeof(info->version));
|
||||
|
||||
if (wdev->wiphy->fw_version[0])
|
||||
strlcpy(info->fw_version, wdev->wiphy->fw_version,
|
||||
sizeof(info->fw_version));
|
||||
else
|
||||
strlcpy(info->fw_version, "N/A", sizeof(info->fw_version));
|
||||
|
||||
strlcpy(info->bus_info, dev_name(wiphy_dev(wdev->wiphy)),
|
||||
sizeof(info->bus_info));
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_get_drvinfo);
|
158
net/wireless/genregdb.awk
Normal file
158
net/wireless/genregdb.awk
Normal file
|
@ -0,0 +1,158 @@
|
|||
#!/usr/bin/awk -f
|
||||
#
|
||||
# genregdb.awk -- generate regdb.c from db.txt
|
||||
#
|
||||
# Actually, it reads from stdin (presumed to be db.txt) and writes
|
||||
# to stdout (presumed to be regdb.c), but close enough...
|
||||
#
|
||||
# Copyright 2009 John W. Linville <linville@tuxdriver.com>
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for any
|
||||
# purpose with or without fee is hereby granted, provided that the above
|
||||
# copyright notice and this permission notice appear in all copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
BEGIN {
|
||||
active = 0
|
||||
rules = 0;
|
||||
print "/*"
|
||||
print " * DO NOT EDIT -- file generated from data in db.txt"
|
||||
print " */"
|
||||
print ""
|
||||
print "#include <linux/nl80211.h>"
|
||||
print "#include <net/cfg80211.h>"
|
||||
print "#include \"regdb.h\""
|
||||
print ""
|
||||
regdb = "const struct ieee80211_regdomain *reg_regdb[] = {\n"
|
||||
}
|
||||
|
||||
function parse_country_head() {
|
||||
country=$2
|
||||
sub(/:/, "", country)
|
||||
printf "static const struct ieee80211_regdomain regdom_%s = {\n", country
|
||||
printf "\t.alpha2 = \"%s\",\n", country
|
||||
if ($NF ~ /DFS-ETSI/)
|
||||
printf "\t.dfs_region = NL80211_DFS_ETSI,\n"
|
||||
else if ($NF ~ /DFS-FCC/)
|
||||
printf "\t.dfs_region = NL80211_DFS_FCC,\n"
|
||||
else if ($NF ~ /DFS-JP/)
|
||||
printf "\t.dfs_region = NL80211_DFS_JP,\n"
|
||||
printf "\t.reg_rules = {\n"
|
||||
active = 1
|
||||
regdb = regdb "\t®dom_" country ",\n"
|
||||
}
|
||||
|
||||
function parse_reg_rule()
|
||||
{
|
||||
flag_starts_at = 7
|
||||
|
||||
start = $1
|
||||
sub(/\(/, "", start)
|
||||
end = $3
|
||||
bw = $5
|
||||
sub(/\),/, "", bw)
|
||||
gain = 0
|
||||
power = $6
|
||||
# power might be in mW...
|
||||
units = $7
|
||||
dfs_cac = 0
|
||||
|
||||
sub(/\(/, "", power)
|
||||
sub(/\),/, "", power)
|
||||
sub(/\),/, "", units)
|
||||
sub(/\)/, "", units)
|
||||
|
||||
if (units == "mW") {
|
||||
flag_starts_at = 8
|
||||
power = 10 * log(power)/log(10)
|
||||
if ($8 ~ /[[:digit:]]/) {
|
||||
flag_starts_at = 9
|
||||
dfs_cac = $8
|
||||
}
|
||||
} else {
|
||||
if ($7 ~ /[[:digit:]]/) {
|
||||
flag_starts_at = 8
|
||||
dfs_cac = $7
|
||||
}
|
||||
}
|
||||
sub(/\(/, "", dfs_cac)
|
||||
sub(/\),/, "", dfs_cac)
|
||||
flagstr = ""
|
||||
for (i=flag_starts_at; i<=NF; i++)
|
||||
flagstr = flagstr $i
|
||||
split(flagstr, flagarray, ",")
|
||||
flags = ""
|
||||
for (arg in flagarray) {
|
||||
if (flagarray[arg] == "NO-OFDM") {
|
||||
flags = flags "\n\t\t\tNL80211_RRF_NO_OFDM | "
|
||||
} else if (flagarray[arg] == "NO-CCK") {
|
||||
flags = flags "\n\t\t\tNL80211_RRF_NO_CCK | "
|
||||
} else if (flagarray[arg] == "NO-INDOOR") {
|
||||
flags = flags "\n\t\t\tNL80211_RRF_NO_INDOOR | "
|
||||
} else if (flagarray[arg] == "NO-OUTDOOR") {
|
||||
flags = flags "\n\t\t\tNL80211_RRF_NO_OUTDOOR | "
|
||||
} else if (flagarray[arg] == "DFS") {
|
||||
flags = flags "\n\t\t\tNL80211_RRF_DFS | "
|
||||
} else if (flagarray[arg] == "PTP-ONLY") {
|
||||
flags = flags "\n\t\t\tNL80211_RRF_PTP_ONLY | "
|
||||
} else if (flagarray[arg] == "PTMP-ONLY") {
|
||||
flags = flags "\n\t\t\tNL80211_RRF_PTMP_ONLY | "
|
||||
} else if (flagarray[arg] == "PASSIVE-SCAN") {
|
||||
flags = flags "\n\t\t\tNL80211_RRF_NO_IR | "
|
||||
} else if (flagarray[arg] == "NO-IBSS") {
|
||||
flags = flags "\n\t\t\tNL80211_RRF_NO_IR | "
|
||||
} else if (flagarray[arg] == "NO-IR") {
|
||||
flags = flags "\n\t\t\tNL80211_RRF_NO_IR | "
|
||||
} else if (flagarray[arg] == "AUTO-BW") {
|
||||
flags = flags "\n\t\t\tNL80211_RRF_AUTO_BW | "
|
||||
}
|
||||
|
||||
}
|
||||
flags = flags "0"
|
||||
printf "\t\tREG_RULE_EXT(%d, %d, %d, %d, %.0f, %d, %s),\n", start, end, bw, gain, power, dfs_cac, flags
|
||||
rules++
|
||||
}
|
||||
|
||||
function print_tail_country()
|
||||
{
|
||||
active = 0
|
||||
printf "\t},\n"
|
||||
printf "\t.n_reg_rules = %d\n", rules
|
||||
printf "};\n\n"
|
||||
rules = 0;
|
||||
}
|
||||
|
||||
/^[ \t]*#/ {
|
||||
# Ignore
|
||||
}
|
||||
|
||||
!active && /^[ \t]*$/ {
|
||||
# Ignore
|
||||
}
|
||||
|
||||
!active && /country/ {
|
||||
parse_country_head()
|
||||
}
|
||||
|
||||
active && /^[ \t]*\(/ {
|
||||
parse_reg_rule()
|
||||
}
|
||||
|
||||
active && /^[ \t]*$/ {
|
||||
print_tail_country()
|
||||
}
|
||||
|
||||
END {
|
||||
if (active)
|
||||
print_tail_country()
|
||||
print regdb "};"
|
||||
print ""
|
||||
print "int reg_regdb_size = ARRAY_SIZE(reg_regdb);"
|
||||
}
|
542
net/wireless/ibss.c
Normal file
542
net/wireless/ibss.c
Normal file
|
@ -0,0 +1,542 @@
|
|||
/*
|
||||
* Some IBSS support code for cfg80211.
|
||||
*
|
||||
* Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
|
||||
*/
|
||||
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/export.h>
|
||||
#include <net/cfg80211.h>
|
||||
#include "wext-compat.h"
|
||||
#include "nl80211.h"
|
||||
#include "rdev-ops.h"
|
||||
|
||||
|
||||
void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
|
||||
struct ieee80211_channel *channel)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct cfg80211_bss *bss;
|
||||
#ifdef CONFIG_CFG80211_WEXT
|
||||
union iwreq_data wrqu;
|
||||
#endif
|
||||
|
||||
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
|
||||
return;
|
||||
|
||||
if (!wdev->ssid_len)
|
||||
return;
|
||||
|
||||
bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, NULL, 0,
|
||||
WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS);
|
||||
|
||||
if (WARN_ON(!bss))
|
||||
return;
|
||||
|
||||
if (wdev->current_bss) {
|
||||
cfg80211_unhold_bss(wdev->current_bss);
|
||||
cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
|
||||
}
|
||||
|
||||
cfg80211_hold_bss(bss_from_pub(bss));
|
||||
wdev->current_bss = bss_from_pub(bss);
|
||||
|
||||
cfg80211_upload_connect_keys(wdev);
|
||||
|
||||
nl80211_send_ibss_bssid(wiphy_to_rdev(wdev->wiphy), dev, bssid,
|
||||
GFP_KERNEL);
|
||||
#ifdef CONFIG_CFG80211_WEXT
|
||||
memset(&wrqu, 0, sizeof(wrqu));
|
||||
memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
|
||||
wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
|
||||
struct ieee80211_channel *channel, gfp_t gfp)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||
struct cfg80211_event *ev;
|
||||
unsigned long flags;
|
||||
|
||||
trace_cfg80211_ibss_joined(dev, bssid, channel);
|
||||
|
||||
if (WARN_ON(!channel))
|
||||
return;
|
||||
|
||||
ev = kzalloc(sizeof(*ev), gfp);
|
||||
if (!ev)
|
||||
return;
|
||||
|
||||
ev->type = EVENT_IBSS_JOINED;
|
||||
memcpy(ev->ij.bssid, bssid, ETH_ALEN);
|
||||
ev->ij.channel = channel;
|
||||
|
||||
spin_lock_irqsave(&wdev->event_lock, flags);
|
||||
list_add_tail(&ev->list, &wdev->event_list);
|
||||
spin_unlock_irqrestore(&wdev->event_lock, flags);
|
||||
queue_work(cfg80211_wq, &rdev->event_work);
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_ibss_joined);
|
||||
|
||||
static int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct cfg80211_ibss_params *params,
|
||||
struct cfg80211_cached_keys *connkeys)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
int err;
|
||||
|
||||
ASSERT_WDEV_LOCK(wdev);
|
||||
|
||||
if (wdev->ssid_len)
|
||||
return -EALREADY;
|
||||
|
||||
if (!params->basic_rates) {
|
||||
/*
|
||||
* If no rates were explicitly configured,
|
||||
* use the mandatory rate set for 11b or
|
||||
* 11a for maximum compatibility.
|
||||
*/
|
||||
struct ieee80211_supported_band *sband =
|
||||
rdev->wiphy.bands[params->chandef.chan->band];
|
||||
int j;
|
||||
u32 flag = params->chandef.chan->band == IEEE80211_BAND_5GHZ ?
|
||||
IEEE80211_RATE_MANDATORY_A :
|
||||
IEEE80211_RATE_MANDATORY_B;
|
||||
|
||||
for (j = 0; j < sband->n_bitrates; j++) {
|
||||
if (sband->bitrates[j].flags & flag)
|
||||
params->basic_rates |= BIT(j);
|
||||
}
|
||||
}
|
||||
|
||||
if (WARN_ON(wdev->connect_keys))
|
||||
kzfree(wdev->connect_keys);
|
||||
wdev->connect_keys = connkeys;
|
||||
|
||||
wdev->ibss_fixed = params->channel_fixed;
|
||||
wdev->ibss_dfs_possible = params->userspace_handles_dfs;
|
||||
wdev->chandef = params->chandef;
|
||||
#ifdef CONFIG_CFG80211_WEXT
|
||||
wdev->wext.ibss.chandef = params->chandef;
|
||||
#endif
|
||||
err = rdev_join_ibss(rdev, dev, params);
|
||||
if (err) {
|
||||
wdev->connect_keys = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
memcpy(wdev->ssid, params->ssid, params->ssid_len);
|
||||
wdev->ssid_len = params->ssid_len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct cfg80211_ibss_params *params,
|
||||
struct cfg80211_cached_keys *connkeys)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
int err;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
wdev_lock(wdev);
|
||||
err = __cfg80211_join_ibss(rdev, dev, params, connkeys);
|
||||
wdev_unlock(wdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||
int i;
|
||||
|
||||
ASSERT_WDEV_LOCK(wdev);
|
||||
|
||||
kzfree(wdev->connect_keys);
|
||||
wdev->connect_keys = NULL;
|
||||
|
||||
rdev_set_qos_map(rdev, dev, NULL);
|
||||
|
||||
/*
|
||||
* Delete all the keys ... pairwise keys can't really
|
||||
* exist any more anyway, but default keys might.
|
||||
*/
|
||||
if (rdev->ops->del_key)
|
||||
for (i = 0; i < 6; i++)
|
||||
rdev_del_key(rdev, dev, i, false, NULL);
|
||||
|
||||
if (wdev->current_bss) {
|
||||
cfg80211_unhold_bss(wdev->current_bss);
|
||||
cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
|
||||
}
|
||||
|
||||
wdev->current_bss = NULL;
|
||||
wdev->ssid_len = 0;
|
||||
memset(&wdev->chandef, 0, sizeof(wdev->chandef));
|
||||
#ifdef CONFIG_CFG80211_WEXT
|
||||
if (!nowext)
|
||||
wdev->wext.ibss.ssid_len = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void cfg80211_clear_ibss(struct net_device *dev, bool nowext)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
|
||||
wdev_lock(wdev);
|
||||
__cfg80211_clear_ibss(dev, nowext);
|
||||
wdev_unlock(wdev);
|
||||
}
|
||||
|
||||
int __cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, bool nowext)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
int err;
|
||||
|
||||
ASSERT_WDEV_LOCK(wdev);
|
||||
|
||||
if (!wdev->ssid_len)
|
||||
return -ENOLINK;
|
||||
|
||||
err = rdev_leave_ibss(rdev, dev);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
__cfg80211_clear_ibss(dev, nowext);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, bool nowext)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
int err;
|
||||
|
||||
wdev_lock(wdev);
|
||||
err = __cfg80211_leave_ibss(rdev, dev, nowext);
|
||||
wdev_unlock(wdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CFG80211_WEXT
|
||||
int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev)
|
||||
{
|
||||
struct cfg80211_cached_keys *ck = NULL;
|
||||
enum ieee80211_band band;
|
||||
int i, err;
|
||||
|
||||
ASSERT_WDEV_LOCK(wdev);
|
||||
|
||||
if (!wdev->wext.ibss.beacon_interval)
|
||||
wdev->wext.ibss.beacon_interval = 100;
|
||||
|
||||
/* try to find an IBSS channel if none requested ... */
|
||||
if (!wdev->wext.ibss.chandef.chan) {
|
||||
struct ieee80211_channel *new_chan = NULL;
|
||||
|
||||
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
|
||||
struct ieee80211_supported_band *sband;
|
||||
struct ieee80211_channel *chan;
|
||||
|
||||
sband = rdev->wiphy.bands[band];
|
||||
if (!sband)
|
||||
continue;
|
||||
|
||||
for (i = 0; i < sband->n_channels; i++) {
|
||||
chan = &sband->channels[i];
|
||||
if (chan->flags & IEEE80211_CHAN_NO_IR)
|
||||
continue;
|
||||
if (chan->flags & IEEE80211_CHAN_DISABLED)
|
||||
continue;
|
||||
new_chan = chan;
|
||||
break;
|
||||
}
|
||||
|
||||
if (new_chan)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!new_chan)
|
||||
return -EINVAL;
|
||||
|
||||
cfg80211_chandef_create(&wdev->wext.ibss.chandef, new_chan,
|
||||
NL80211_CHAN_NO_HT);
|
||||
}
|
||||
|
||||
/* don't join -- SSID is not there */
|
||||
if (!wdev->wext.ibss.ssid_len)
|
||||
return 0;
|
||||
|
||||
if (!netif_running(wdev->netdev))
|
||||
return 0;
|
||||
|
||||
if (wdev->wext.keys) {
|
||||
wdev->wext.keys->def = wdev->wext.default_key;
|
||||
wdev->wext.keys->defmgmt = wdev->wext.default_mgmt_key;
|
||||
}
|
||||
|
||||
wdev->wext.ibss.privacy = wdev->wext.default_key != -1;
|
||||
|
||||
if (wdev->wext.keys) {
|
||||
ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL);
|
||||
if (!ck)
|
||||
return -ENOMEM;
|
||||
for (i = 0; i < 6; i++)
|
||||
ck->params[i].key = ck->data[i];
|
||||
}
|
||||
err = __cfg80211_join_ibss(rdev, wdev->netdev,
|
||||
&wdev->wext.ibss, ck);
|
||||
if (err)
|
||||
kfree(ck);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_freq *wextfreq, char *extra)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||
struct ieee80211_channel *chan = NULL;
|
||||
int err, freq;
|
||||
|
||||
/* call only for ibss! */
|
||||
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
|
||||
return -EINVAL;
|
||||
|
||||
if (!rdev->ops->join_ibss)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
freq = cfg80211_wext_freq(wextfreq);
|
||||
if (freq < 0)
|
||||
return freq;
|
||||
|
||||
if (freq) {
|
||||
chan = ieee80211_get_channel(wdev->wiphy, freq);
|
||||
if (!chan)
|
||||
return -EINVAL;
|
||||
if (chan->flags & IEEE80211_CHAN_NO_IR ||
|
||||
chan->flags & IEEE80211_CHAN_DISABLED)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (wdev->wext.ibss.chandef.chan == chan)
|
||||
return 0;
|
||||
|
||||
wdev_lock(wdev);
|
||||
err = 0;
|
||||
if (wdev->ssid_len)
|
||||
err = __cfg80211_leave_ibss(rdev, dev, true);
|
||||
wdev_unlock(wdev);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (chan) {
|
||||
cfg80211_chandef_create(&wdev->wext.ibss.chandef, chan,
|
||||
NL80211_CHAN_NO_HT);
|
||||
wdev->wext.ibss.channel_fixed = true;
|
||||
} else {
|
||||
/* cfg80211_ibss_wext_join will pick one if needed */
|
||||
wdev->wext.ibss.channel_fixed = false;
|
||||
}
|
||||
|
||||
wdev_lock(wdev);
|
||||
err = cfg80211_ibss_wext_join(rdev, wdev);
|
||||
wdev_unlock(wdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int cfg80211_ibss_wext_giwfreq(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_freq *freq, char *extra)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct ieee80211_channel *chan = NULL;
|
||||
|
||||
/* call only for ibss! */
|
||||
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
|
||||
return -EINVAL;
|
||||
|
||||
wdev_lock(wdev);
|
||||
if (wdev->current_bss)
|
||||
chan = wdev->current_bss->pub.channel;
|
||||
else if (wdev->wext.ibss.chandef.chan)
|
||||
chan = wdev->wext.ibss.chandef.chan;
|
||||
wdev_unlock(wdev);
|
||||
|
||||
if (chan) {
|
||||
freq->m = chan->center_freq;
|
||||
freq->e = 6;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* no channel if not joining */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int cfg80211_ibss_wext_siwessid(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_point *data, char *ssid)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||
size_t len = data->length;
|
||||
int err;
|
||||
|
||||
/* call only for ibss! */
|
||||
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
|
||||
return -EINVAL;
|
||||
|
||||
if (!rdev->ops->join_ibss)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
wdev_lock(wdev);
|
||||
err = 0;
|
||||
if (wdev->ssid_len)
|
||||
err = __cfg80211_leave_ibss(rdev, dev, true);
|
||||
wdev_unlock(wdev);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* iwconfig uses nul termination in SSID.. */
|
||||
if (len > 0 && ssid[len - 1] == '\0')
|
||||
len--;
|
||||
|
||||
memcpy(wdev->ssid, ssid, len);
|
||||
wdev->wext.ibss.ssid = wdev->ssid;
|
||||
wdev->wext.ibss.ssid_len = len;
|
||||
|
||||
wdev_lock(wdev);
|
||||
err = cfg80211_ibss_wext_join(rdev, wdev);
|
||||
wdev_unlock(wdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int cfg80211_ibss_wext_giwessid(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_point *data, char *ssid)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
|
||||
/* call only for ibss! */
|
||||
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
|
||||
return -EINVAL;
|
||||
|
||||
data->flags = 0;
|
||||
|
||||
wdev_lock(wdev);
|
||||
if (wdev->ssid_len) {
|
||||
data->flags = 1;
|
||||
data->length = wdev->ssid_len;
|
||||
memcpy(ssid, wdev->ssid, data->length);
|
||||
} else if (wdev->wext.ibss.ssid && wdev->wext.ibss.ssid_len) {
|
||||
data->flags = 1;
|
||||
data->length = wdev->wext.ibss.ssid_len;
|
||||
memcpy(ssid, wdev->wext.ibss.ssid, data->length);
|
||||
}
|
||||
wdev_unlock(wdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cfg80211_ibss_wext_siwap(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct sockaddr *ap_addr, char *extra)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||
u8 *bssid = ap_addr->sa_data;
|
||||
int err;
|
||||
|
||||
/* call only for ibss! */
|
||||
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
|
||||
return -EINVAL;
|
||||
|
||||
if (!rdev->ops->join_ibss)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (ap_addr->sa_family != ARPHRD_ETHER)
|
||||
return -EINVAL;
|
||||
|
||||
/* automatic mode */
|
||||
if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid))
|
||||
bssid = NULL;
|
||||
|
||||
if (bssid && !is_valid_ether_addr(bssid))
|
||||
return -EINVAL;
|
||||
|
||||
/* both automatic */
|
||||
if (!bssid && !wdev->wext.ibss.bssid)
|
||||
return 0;
|
||||
|
||||
/* fixed already - and no change */
|
||||
if (wdev->wext.ibss.bssid && bssid &&
|
||||
ether_addr_equal(bssid, wdev->wext.ibss.bssid))
|
||||
return 0;
|
||||
|
||||
wdev_lock(wdev);
|
||||
err = 0;
|
||||
if (wdev->ssid_len)
|
||||
err = __cfg80211_leave_ibss(rdev, dev, true);
|
||||
wdev_unlock(wdev);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (bssid) {
|
||||
memcpy(wdev->wext.bssid, bssid, ETH_ALEN);
|
||||
wdev->wext.ibss.bssid = wdev->wext.bssid;
|
||||
} else
|
||||
wdev->wext.ibss.bssid = NULL;
|
||||
|
||||
wdev_lock(wdev);
|
||||
err = cfg80211_ibss_wext_join(rdev, wdev);
|
||||
wdev_unlock(wdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int cfg80211_ibss_wext_giwap(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct sockaddr *ap_addr, char *extra)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
|
||||
/* call only for ibss! */
|
||||
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
|
||||
return -EINVAL;
|
||||
|
||||
ap_addr->sa_family = ARPHRD_ETHER;
|
||||
|
||||
wdev_lock(wdev);
|
||||
if (wdev->current_bss)
|
||||
memcpy(ap_addr->sa_data, wdev->current_bss->pub.bssid, ETH_ALEN);
|
||||
else if (wdev->wext.ibss.bssid)
|
||||
memcpy(ap_addr->sa_data, wdev->wext.ibss.bssid, ETH_ALEN);
|
||||
else
|
||||
memset(ap_addr->sa_data, 0, ETH_ALEN);
|
||||
|
||||
wdev_unlock(wdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
257
net/wireless/lib80211.c
Normal file
257
net/wireless/lib80211.c
Normal file
|
@ -0,0 +1,257 @@
|
|||
/*
|
||||
* lib80211 -- common bits for IEEE802.11 drivers
|
||||
*
|
||||
* Copyright(c) 2008 John W. Linville <linville@tuxdriver.com>
|
||||
*
|
||||
* Portions copied from old ieee80211 component, w/ original copyright
|
||||
* notices below:
|
||||
*
|
||||
* Host AP crypto routines
|
||||
*
|
||||
* Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
|
||||
* Portions Copyright (C) 2004, Intel Corporation <jketreno@linux.intel.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/ieee80211.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include <net/lib80211.h>
|
||||
|
||||
#define DRV_NAME "lib80211"
|
||||
|
||||
#define DRV_DESCRIPTION "common routines for IEEE802.11 drivers"
|
||||
|
||||
MODULE_DESCRIPTION(DRV_DESCRIPTION);
|
||||
MODULE_AUTHOR("John W. Linville <linville@tuxdriver.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
struct lib80211_crypto_alg {
|
||||
struct list_head list;
|
||||
struct lib80211_crypto_ops *ops;
|
||||
};
|
||||
|
||||
static LIST_HEAD(lib80211_crypto_algs);
|
||||
static DEFINE_SPINLOCK(lib80211_crypto_lock);
|
||||
|
||||
static void lib80211_crypt_deinit_entries(struct lib80211_crypt_info *info,
|
||||
int force);
|
||||
static void lib80211_crypt_quiescing(struct lib80211_crypt_info *info);
|
||||
static void lib80211_crypt_deinit_handler(unsigned long data);
|
||||
|
||||
int lib80211_crypt_info_init(struct lib80211_crypt_info *info, char *name,
|
||||
spinlock_t *lock)
|
||||
{
|
||||
memset(info, 0, sizeof(*info));
|
||||
|
||||
info->name = name;
|
||||
info->lock = lock;
|
||||
|
||||
INIT_LIST_HEAD(&info->crypt_deinit_list);
|
||||
setup_timer(&info->crypt_deinit_timer, lib80211_crypt_deinit_handler,
|
||||
(unsigned long)info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(lib80211_crypt_info_init);
|
||||
|
||||
void lib80211_crypt_info_free(struct lib80211_crypt_info *info)
|
||||
{
|
||||
int i;
|
||||
|
||||
lib80211_crypt_quiescing(info);
|
||||
del_timer_sync(&info->crypt_deinit_timer);
|
||||
lib80211_crypt_deinit_entries(info, 1);
|
||||
|
||||
for (i = 0; i < NUM_WEP_KEYS; i++) {
|
||||
struct lib80211_crypt_data *crypt = info->crypt[i];
|
||||
if (crypt) {
|
||||
if (crypt->ops) {
|
||||
crypt->ops->deinit(crypt->priv);
|
||||
module_put(crypt->ops->owner);
|
||||
}
|
||||
kfree(crypt);
|
||||
info->crypt[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(lib80211_crypt_info_free);
|
||||
|
||||
static void lib80211_crypt_deinit_entries(struct lib80211_crypt_info *info,
|
||||
int force)
|
||||
{
|
||||
struct lib80211_crypt_data *entry, *next;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(info->lock, flags);
|
||||
list_for_each_entry_safe(entry, next, &info->crypt_deinit_list, list) {
|
||||
if (atomic_read(&entry->refcnt) != 0 && !force)
|
||||
continue;
|
||||
|
||||
list_del(&entry->list);
|
||||
|
||||
if (entry->ops) {
|
||||
entry->ops->deinit(entry->priv);
|
||||
module_put(entry->ops->owner);
|
||||
}
|
||||
kfree(entry);
|
||||
}
|
||||
spin_unlock_irqrestore(info->lock, flags);
|
||||
}
|
||||
|
||||
/* After this, crypt_deinit_list won't accept new members */
|
||||
static void lib80211_crypt_quiescing(struct lib80211_crypt_info *info)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(info->lock, flags);
|
||||
info->crypt_quiesced = 1;
|
||||
spin_unlock_irqrestore(info->lock, flags);
|
||||
}
|
||||
|
||||
static void lib80211_crypt_deinit_handler(unsigned long data)
|
||||
{
|
||||
struct lib80211_crypt_info *info = (struct lib80211_crypt_info *)data;
|
||||
unsigned long flags;
|
||||
|
||||
lib80211_crypt_deinit_entries(info, 0);
|
||||
|
||||
spin_lock_irqsave(info->lock, flags);
|
||||
if (!list_empty(&info->crypt_deinit_list) && !info->crypt_quiesced) {
|
||||
printk(KERN_DEBUG "%s: entries remaining in delayed crypt "
|
||||
"deletion list\n", info->name);
|
||||
info->crypt_deinit_timer.expires = jiffies + HZ;
|
||||
add_timer(&info->crypt_deinit_timer);
|
||||
}
|
||||
spin_unlock_irqrestore(info->lock, flags);
|
||||
}
|
||||
|
||||
void lib80211_crypt_delayed_deinit(struct lib80211_crypt_info *info,
|
||||
struct lib80211_crypt_data **crypt)
|
||||
{
|
||||
struct lib80211_crypt_data *tmp;
|
||||
unsigned long flags;
|
||||
|
||||
if (*crypt == NULL)
|
||||
return;
|
||||
|
||||
tmp = *crypt;
|
||||
*crypt = NULL;
|
||||
|
||||
/* must not run ops->deinit() while there may be pending encrypt or
|
||||
* decrypt operations. Use a list of delayed deinits to avoid needing
|
||||
* locking. */
|
||||
|
||||
spin_lock_irqsave(info->lock, flags);
|
||||
if (!info->crypt_quiesced) {
|
||||
list_add(&tmp->list, &info->crypt_deinit_list);
|
||||
if (!timer_pending(&info->crypt_deinit_timer)) {
|
||||
info->crypt_deinit_timer.expires = jiffies + HZ;
|
||||
add_timer(&info->crypt_deinit_timer);
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(info->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(lib80211_crypt_delayed_deinit);
|
||||
|
||||
int lib80211_register_crypto_ops(struct lib80211_crypto_ops *ops)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct lib80211_crypto_alg *alg;
|
||||
|
||||
alg = kzalloc(sizeof(*alg), GFP_KERNEL);
|
||||
if (alg == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
alg->ops = ops;
|
||||
|
||||
spin_lock_irqsave(&lib80211_crypto_lock, flags);
|
||||
list_add(&alg->list, &lib80211_crypto_algs);
|
||||
spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
|
||||
|
||||
printk(KERN_DEBUG "lib80211_crypt: registered algorithm '%s'\n",
|
||||
ops->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(lib80211_register_crypto_ops);
|
||||
|
||||
int lib80211_unregister_crypto_ops(struct lib80211_crypto_ops *ops)
|
||||
{
|
||||
struct lib80211_crypto_alg *alg;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&lib80211_crypto_lock, flags);
|
||||
list_for_each_entry(alg, &lib80211_crypto_algs, list) {
|
||||
if (alg->ops == ops)
|
||||
goto found;
|
||||
}
|
||||
spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
|
||||
return -EINVAL;
|
||||
|
||||
found:
|
||||
printk(KERN_DEBUG "lib80211_crypt: unregistered algorithm '%s'\n",
|
||||
ops->name);
|
||||
list_del(&alg->list);
|
||||
spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
|
||||
kfree(alg);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(lib80211_unregister_crypto_ops);
|
||||
|
||||
struct lib80211_crypto_ops *lib80211_get_crypto_ops(const char *name)
|
||||
{
|
||||
struct lib80211_crypto_alg *alg;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&lib80211_crypto_lock, flags);
|
||||
list_for_each_entry(alg, &lib80211_crypto_algs, list) {
|
||||
if (strcmp(alg->ops->name, name) == 0)
|
||||
goto found;
|
||||
}
|
||||
spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
|
||||
return NULL;
|
||||
|
||||
found:
|
||||
spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
|
||||
return alg->ops;
|
||||
}
|
||||
EXPORT_SYMBOL(lib80211_get_crypto_ops);
|
||||
|
||||
static void *lib80211_crypt_null_init(int keyidx)
|
||||
{
|
||||
return (void *)1;
|
||||
}
|
||||
|
||||
static void lib80211_crypt_null_deinit(void *priv)
|
||||
{
|
||||
}
|
||||
|
||||
static struct lib80211_crypto_ops lib80211_crypt_null = {
|
||||
.name = "NULL",
|
||||
.init = lib80211_crypt_null_init,
|
||||
.deinit = lib80211_crypt_null_deinit,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init lib80211_init(void)
|
||||
{
|
||||
pr_info(DRV_DESCRIPTION "\n");
|
||||
return lib80211_register_crypto_ops(&lib80211_crypt_null);
|
||||
}
|
||||
|
||||
static void __exit lib80211_exit(void)
|
||||
{
|
||||
lib80211_unregister_crypto_ops(&lib80211_crypt_null);
|
||||
BUG_ON(!list_empty(&lib80211_crypto_algs));
|
||||
}
|
||||
|
||||
module_init(lib80211_init);
|
||||
module_exit(lib80211_exit);
|
479
net/wireless/lib80211_crypt_ccmp.c
Normal file
479
net/wireless/lib80211_crypt_ccmp.c
Normal file
|
@ -0,0 +1,479 @@
|
|||
/*
|
||||
* lib80211 crypt: host-based CCMP encryption implementation for lib80211
|
||||
*
|
||||
* Copyright (c) 2003-2004, Jouni Malinen <j@w1.fi>
|
||||
* Copyright (c) 2008, John W. Linville <linville@tuxdriver.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation. See README and COPYING for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <asm/string.h>
|
||||
#include <linux/wireless.h>
|
||||
|
||||
#include <linux/ieee80211.h>
|
||||
|
||||
#include <linux/crypto.h>
|
||||
|
||||
#include <net/lib80211.h>
|
||||
|
||||
MODULE_AUTHOR("Jouni Malinen");
|
||||
MODULE_DESCRIPTION("Host AP crypt: CCMP");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define AES_BLOCK_LEN 16
|
||||
#define CCMP_HDR_LEN 8
|
||||
#define CCMP_MIC_LEN 8
|
||||
#define CCMP_TK_LEN 16
|
||||
#define CCMP_PN_LEN 6
|
||||
|
||||
struct lib80211_ccmp_data {
|
||||
u8 key[CCMP_TK_LEN];
|
||||
int key_set;
|
||||
|
||||
u8 tx_pn[CCMP_PN_LEN];
|
||||
u8 rx_pn[CCMP_PN_LEN];
|
||||
|
||||
u32 dot11RSNAStatsCCMPFormatErrors;
|
||||
u32 dot11RSNAStatsCCMPReplays;
|
||||
u32 dot11RSNAStatsCCMPDecryptErrors;
|
||||
|
||||
int key_idx;
|
||||
|
||||
struct crypto_cipher *tfm;
|
||||
|
||||
/* scratch buffers for virt_to_page() (crypto API) */
|
||||
u8 tx_b0[AES_BLOCK_LEN], tx_b[AES_BLOCK_LEN],
|
||||
tx_e[AES_BLOCK_LEN], tx_s0[AES_BLOCK_LEN];
|
||||
u8 rx_b0[AES_BLOCK_LEN], rx_b[AES_BLOCK_LEN], rx_a[AES_BLOCK_LEN];
|
||||
};
|
||||
|
||||
static inline void lib80211_ccmp_aes_encrypt(struct crypto_cipher *tfm,
|
||||
const u8 pt[16], u8 ct[16])
|
||||
{
|
||||
crypto_cipher_encrypt_one(tfm, ct, pt);
|
||||
}
|
||||
|
||||
static void *lib80211_ccmp_init(int key_idx)
|
||||
{
|
||||
struct lib80211_ccmp_data *priv;
|
||||
|
||||
priv = kzalloc(sizeof(*priv), GFP_ATOMIC);
|
||||
if (priv == NULL)
|
||||
goto fail;
|
||||
priv->key_idx = key_idx;
|
||||
|
||||
priv->tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(priv->tfm)) {
|
||||
priv->tfm = NULL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return priv;
|
||||
|
||||
fail:
|
||||
if (priv) {
|
||||
if (priv->tfm)
|
||||
crypto_free_cipher(priv->tfm);
|
||||
kfree(priv);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void lib80211_ccmp_deinit(void *priv)
|
||||
{
|
||||
struct lib80211_ccmp_data *_priv = priv;
|
||||
if (_priv && _priv->tfm)
|
||||
crypto_free_cipher(_priv->tfm);
|
||||
kfree(priv);
|
||||
}
|
||||
|
||||
static inline void xor_block(u8 * b, u8 * a, size_t len)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < len; i++)
|
||||
b[i] ^= a[i];
|
||||
}
|
||||
|
||||
static void ccmp_init_blocks(struct crypto_cipher *tfm,
|
||||
struct ieee80211_hdr *hdr,
|
||||
u8 * pn, size_t dlen, u8 * b0, u8 * auth, u8 * s0)
|
||||
{
|
||||
u8 *pos, qc = 0;
|
||||
size_t aad_len;
|
||||
int a4_included, qc_included;
|
||||
u8 aad[2 * AES_BLOCK_LEN];
|
||||
|
||||
a4_included = ieee80211_has_a4(hdr->frame_control);
|
||||
qc_included = ieee80211_is_data_qos(hdr->frame_control);
|
||||
|
||||
aad_len = 22;
|
||||
if (a4_included)
|
||||
aad_len += 6;
|
||||
if (qc_included) {
|
||||
pos = (u8 *) & hdr->addr4;
|
||||
if (a4_included)
|
||||
pos += 6;
|
||||
qc = *pos & 0x0f;
|
||||
aad_len += 2;
|
||||
}
|
||||
|
||||
/* CCM Initial Block:
|
||||
* Flag (Include authentication header, M=3 (8-octet MIC),
|
||||
* L=1 (2-octet Dlen))
|
||||
* Nonce: 0x00 | A2 | PN
|
||||
* Dlen */
|
||||
b0[0] = 0x59;
|
||||
b0[1] = qc;
|
||||
memcpy(b0 + 2, hdr->addr2, ETH_ALEN);
|
||||
memcpy(b0 + 8, pn, CCMP_PN_LEN);
|
||||
b0[14] = (dlen >> 8) & 0xff;
|
||||
b0[15] = dlen & 0xff;
|
||||
|
||||
/* AAD:
|
||||
* FC with bits 4..6 and 11..13 masked to zero; 14 is always one
|
||||
* A1 | A2 | A3
|
||||
* SC with bits 4..15 (seq#) masked to zero
|
||||
* A4 (if present)
|
||||
* QC (if present)
|
||||
*/
|
||||
pos = (u8 *) hdr;
|
||||
aad[0] = 0; /* aad_len >> 8 */
|
||||
aad[1] = aad_len & 0xff;
|
||||
aad[2] = pos[0] & 0x8f;
|
||||
aad[3] = pos[1] & 0xc7;
|
||||
memcpy(aad + 4, hdr->addr1, 3 * ETH_ALEN);
|
||||
pos = (u8 *) & hdr->seq_ctrl;
|
||||
aad[22] = pos[0] & 0x0f;
|
||||
aad[23] = 0; /* all bits masked */
|
||||
memset(aad + 24, 0, 8);
|
||||
if (a4_included)
|
||||
memcpy(aad + 24, hdr->addr4, ETH_ALEN);
|
||||
if (qc_included) {
|
||||
aad[a4_included ? 30 : 24] = qc;
|
||||
/* rest of QC masked */
|
||||
}
|
||||
|
||||
/* Start with the first block and AAD */
|
||||
lib80211_ccmp_aes_encrypt(tfm, b0, auth);
|
||||
xor_block(auth, aad, AES_BLOCK_LEN);
|
||||
lib80211_ccmp_aes_encrypt(tfm, auth, auth);
|
||||
xor_block(auth, &aad[AES_BLOCK_LEN], AES_BLOCK_LEN);
|
||||
lib80211_ccmp_aes_encrypt(tfm, auth, auth);
|
||||
b0[0] &= 0x07;
|
||||
b0[14] = b0[15] = 0;
|
||||
lib80211_ccmp_aes_encrypt(tfm, b0, s0);
|
||||
}
|
||||
|
||||
static int lib80211_ccmp_hdr(struct sk_buff *skb, int hdr_len,
|
||||
u8 *aeskey, int keylen, void *priv)
|
||||
{
|
||||
struct lib80211_ccmp_data *key = priv;
|
||||
int i;
|
||||
u8 *pos;
|
||||
|
||||
if (skb_headroom(skb) < CCMP_HDR_LEN || skb->len < hdr_len)
|
||||
return -1;
|
||||
|
||||
if (aeskey != NULL && keylen >= CCMP_TK_LEN)
|
||||
memcpy(aeskey, key->key, CCMP_TK_LEN);
|
||||
|
||||
pos = skb_push(skb, CCMP_HDR_LEN);
|
||||
memmove(pos, pos + CCMP_HDR_LEN, hdr_len);
|
||||
pos += hdr_len;
|
||||
|
||||
i = CCMP_PN_LEN - 1;
|
||||
while (i >= 0) {
|
||||
key->tx_pn[i]++;
|
||||
if (key->tx_pn[i] != 0)
|
||||
break;
|
||||
i--;
|
||||
}
|
||||
|
||||
*pos++ = key->tx_pn[5];
|
||||
*pos++ = key->tx_pn[4];
|
||||
*pos++ = 0;
|
||||
*pos++ = (key->key_idx << 6) | (1 << 5) /* Ext IV included */ ;
|
||||
*pos++ = key->tx_pn[3];
|
||||
*pos++ = key->tx_pn[2];
|
||||
*pos++ = key->tx_pn[1];
|
||||
*pos++ = key->tx_pn[0];
|
||||
|
||||
return CCMP_HDR_LEN;
|
||||
}
|
||||
|
||||
static int lib80211_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
|
||||
{
|
||||
struct lib80211_ccmp_data *key = priv;
|
||||
int data_len, i, blocks, last, len;
|
||||
u8 *pos, *mic;
|
||||
struct ieee80211_hdr *hdr;
|
||||
u8 *b0 = key->tx_b0;
|
||||
u8 *b = key->tx_b;
|
||||
u8 *e = key->tx_e;
|
||||
u8 *s0 = key->tx_s0;
|
||||
|
||||
if (skb_tailroom(skb) < CCMP_MIC_LEN || skb->len < hdr_len)
|
||||
return -1;
|
||||
|
||||
data_len = skb->len - hdr_len;
|
||||
len = lib80211_ccmp_hdr(skb, hdr_len, NULL, 0, priv);
|
||||
if (len < 0)
|
||||
return -1;
|
||||
|
||||
pos = skb->data + hdr_len + CCMP_HDR_LEN;
|
||||
hdr = (struct ieee80211_hdr *)skb->data;
|
||||
ccmp_init_blocks(key->tfm, hdr, key->tx_pn, data_len, b0, b, s0);
|
||||
|
||||
blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN);
|
||||
last = data_len % AES_BLOCK_LEN;
|
||||
|
||||
for (i = 1; i <= blocks; i++) {
|
||||
len = (i == blocks && last) ? last : AES_BLOCK_LEN;
|
||||
/* Authentication */
|
||||
xor_block(b, pos, len);
|
||||
lib80211_ccmp_aes_encrypt(key->tfm, b, b);
|
||||
/* Encryption, with counter */
|
||||
b0[14] = (i >> 8) & 0xff;
|
||||
b0[15] = i & 0xff;
|
||||
lib80211_ccmp_aes_encrypt(key->tfm, b0, e);
|
||||
xor_block(pos, e, len);
|
||||
pos += len;
|
||||
}
|
||||
|
||||
mic = skb_put(skb, CCMP_MIC_LEN);
|
||||
for (i = 0; i < CCMP_MIC_LEN; i++)
|
||||
mic[i] = b[i] ^ s0[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* deal with seq counter wrapping correctly.
|
||||
* refer to timer_after() for jiffies wrapping handling
|
||||
*/
|
||||
static inline int ccmp_replay_check(u8 *pn_n, u8 *pn_o)
|
||||
{
|
||||
u32 iv32_n, iv16_n;
|
||||
u32 iv32_o, iv16_o;
|
||||
|
||||
iv32_n = (pn_n[0] << 24) | (pn_n[1] << 16) | (pn_n[2] << 8) | pn_n[3];
|
||||
iv16_n = (pn_n[4] << 8) | pn_n[5];
|
||||
|
||||
iv32_o = (pn_o[0] << 24) | (pn_o[1] << 16) | (pn_o[2] << 8) | pn_o[3];
|
||||
iv16_o = (pn_o[4] << 8) | pn_o[5];
|
||||
|
||||
if ((s32)iv32_n - (s32)iv32_o < 0 ||
|
||||
(iv32_n == iv32_o && iv16_n <= iv16_o))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lib80211_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
|
||||
{
|
||||
struct lib80211_ccmp_data *key = priv;
|
||||
u8 keyidx, *pos;
|
||||
struct ieee80211_hdr *hdr;
|
||||
u8 *b0 = key->rx_b0;
|
||||
u8 *b = key->rx_b;
|
||||
u8 *a = key->rx_a;
|
||||
u8 pn[6];
|
||||
int i, blocks, last, len;
|
||||
size_t data_len = skb->len - hdr_len - CCMP_HDR_LEN - CCMP_MIC_LEN;
|
||||
u8 *mic = skb->data + skb->len - CCMP_MIC_LEN;
|
||||
|
||||
if (skb->len < hdr_len + CCMP_HDR_LEN + CCMP_MIC_LEN) {
|
||||
key->dot11RSNAStatsCCMPFormatErrors++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
hdr = (struct ieee80211_hdr *)skb->data;
|
||||
pos = skb->data + hdr_len;
|
||||
keyidx = pos[3];
|
||||
if (!(keyidx & (1 << 5))) {
|
||||
net_dbg_ratelimited("CCMP: received packet without ExtIV flag from %pM\n",
|
||||
hdr->addr2);
|
||||
key->dot11RSNAStatsCCMPFormatErrors++;
|
||||
return -2;
|
||||
}
|
||||
keyidx >>= 6;
|
||||
if (key->key_idx != keyidx) {
|
||||
printk(KERN_DEBUG "CCMP: RX tkey->key_idx=%d frame "
|
||||
"keyidx=%d priv=%p\n", key->key_idx, keyidx, priv);
|
||||
return -6;
|
||||
}
|
||||
if (!key->key_set) {
|
||||
net_dbg_ratelimited("CCMP: received packet from %pM with keyid=%d that does not have a configured key\n",
|
||||
hdr->addr2, keyidx);
|
||||
return -3;
|
||||
}
|
||||
|
||||
pn[0] = pos[7];
|
||||
pn[1] = pos[6];
|
||||
pn[2] = pos[5];
|
||||
pn[3] = pos[4];
|
||||
pn[4] = pos[1];
|
||||
pn[5] = pos[0];
|
||||
pos += 8;
|
||||
|
||||
if (ccmp_replay_check(pn, key->rx_pn)) {
|
||||
#ifdef CONFIG_LIB80211_DEBUG
|
||||
net_dbg_ratelimited("CCMP: replay detected: STA=%pM previous PN %02x%02x%02x%02x%02x%02x received PN %02x%02x%02x%02x%02x%02x\n",
|
||||
hdr->addr2,
|
||||
key->rx_pn[0], key->rx_pn[1], key->rx_pn[2],
|
||||
key->rx_pn[3], key->rx_pn[4], key->rx_pn[5],
|
||||
pn[0], pn[1], pn[2], pn[3], pn[4], pn[5]);
|
||||
#endif
|
||||
key->dot11RSNAStatsCCMPReplays++;
|
||||
return -4;
|
||||
}
|
||||
|
||||
ccmp_init_blocks(key->tfm, hdr, pn, data_len, b0, a, b);
|
||||
xor_block(mic, b, CCMP_MIC_LEN);
|
||||
|
||||
blocks = DIV_ROUND_UP(data_len, AES_BLOCK_LEN);
|
||||
last = data_len % AES_BLOCK_LEN;
|
||||
|
||||
for (i = 1; i <= blocks; i++) {
|
||||
len = (i == blocks && last) ? last : AES_BLOCK_LEN;
|
||||
/* Decrypt, with counter */
|
||||
b0[14] = (i >> 8) & 0xff;
|
||||
b0[15] = i & 0xff;
|
||||
lib80211_ccmp_aes_encrypt(key->tfm, b0, b);
|
||||
xor_block(pos, b, len);
|
||||
/* Authentication */
|
||||
xor_block(a, pos, len);
|
||||
lib80211_ccmp_aes_encrypt(key->tfm, a, a);
|
||||
pos += len;
|
||||
}
|
||||
|
||||
if (memcmp(mic, a, CCMP_MIC_LEN) != 0) {
|
||||
net_dbg_ratelimited("CCMP: decrypt failed: STA=%pM\n",
|
||||
hdr->addr2);
|
||||
key->dot11RSNAStatsCCMPDecryptErrors++;
|
||||
return -5;
|
||||
}
|
||||
|
||||
memcpy(key->rx_pn, pn, CCMP_PN_LEN);
|
||||
|
||||
/* Remove hdr and MIC */
|
||||
memmove(skb->data + CCMP_HDR_LEN, skb->data, hdr_len);
|
||||
skb_pull(skb, CCMP_HDR_LEN);
|
||||
skb_trim(skb, skb->len - CCMP_MIC_LEN);
|
||||
|
||||
return keyidx;
|
||||
}
|
||||
|
||||
static int lib80211_ccmp_set_key(void *key, int len, u8 * seq, void *priv)
|
||||
{
|
||||
struct lib80211_ccmp_data *data = priv;
|
||||
int keyidx;
|
||||
struct crypto_cipher *tfm = data->tfm;
|
||||
|
||||
keyidx = data->key_idx;
|
||||
memset(data, 0, sizeof(*data));
|
||||
data->key_idx = keyidx;
|
||||
data->tfm = tfm;
|
||||
if (len == CCMP_TK_LEN) {
|
||||
memcpy(data->key, key, CCMP_TK_LEN);
|
||||
data->key_set = 1;
|
||||
if (seq) {
|
||||
data->rx_pn[0] = seq[5];
|
||||
data->rx_pn[1] = seq[4];
|
||||
data->rx_pn[2] = seq[3];
|
||||
data->rx_pn[3] = seq[2];
|
||||
data->rx_pn[4] = seq[1];
|
||||
data->rx_pn[5] = seq[0];
|
||||
}
|
||||
crypto_cipher_setkey(data->tfm, data->key, CCMP_TK_LEN);
|
||||
} else if (len == 0)
|
||||
data->key_set = 0;
|
||||
else
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lib80211_ccmp_get_key(void *key, int len, u8 * seq, void *priv)
|
||||
{
|
||||
struct lib80211_ccmp_data *data = priv;
|
||||
|
||||
if (len < CCMP_TK_LEN)
|
||||
return -1;
|
||||
|
||||
if (!data->key_set)
|
||||
return 0;
|
||||
memcpy(key, data->key, CCMP_TK_LEN);
|
||||
|
||||
if (seq) {
|
||||
seq[0] = data->tx_pn[5];
|
||||
seq[1] = data->tx_pn[4];
|
||||
seq[2] = data->tx_pn[3];
|
||||
seq[3] = data->tx_pn[2];
|
||||
seq[4] = data->tx_pn[1];
|
||||
seq[5] = data->tx_pn[0];
|
||||
}
|
||||
|
||||
return CCMP_TK_LEN;
|
||||
}
|
||||
|
||||
static void lib80211_ccmp_print_stats(struct seq_file *m, void *priv)
|
||||
{
|
||||
struct lib80211_ccmp_data *ccmp = priv;
|
||||
|
||||
seq_printf(m,
|
||||
"key[%d] alg=CCMP key_set=%d "
|
||||
"tx_pn=%02x%02x%02x%02x%02x%02x "
|
||||
"rx_pn=%02x%02x%02x%02x%02x%02x "
|
||||
"format_errors=%d replays=%d decrypt_errors=%d\n",
|
||||
ccmp->key_idx, ccmp->key_set,
|
||||
ccmp->tx_pn[0], ccmp->tx_pn[1], ccmp->tx_pn[2],
|
||||
ccmp->tx_pn[3], ccmp->tx_pn[4], ccmp->tx_pn[5],
|
||||
ccmp->rx_pn[0], ccmp->rx_pn[1], ccmp->rx_pn[2],
|
||||
ccmp->rx_pn[3], ccmp->rx_pn[4], ccmp->rx_pn[5],
|
||||
ccmp->dot11RSNAStatsCCMPFormatErrors,
|
||||
ccmp->dot11RSNAStatsCCMPReplays,
|
||||
ccmp->dot11RSNAStatsCCMPDecryptErrors);
|
||||
}
|
||||
|
||||
static struct lib80211_crypto_ops lib80211_crypt_ccmp = {
|
||||
.name = "CCMP",
|
||||
.init = lib80211_ccmp_init,
|
||||
.deinit = lib80211_ccmp_deinit,
|
||||
.encrypt_mpdu = lib80211_ccmp_encrypt,
|
||||
.decrypt_mpdu = lib80211_ccmp_decrypt,
|
||||
.encrypt_msdu = NULL,
|
||||
.decrypt_msdu = NULL,
|
||||
.set_key = lib80211_ccmp_set_key,
|
||||
.get_key = lib80211_ccmp_get_key,
|
||||
.print_stats = lib80211_ccmp_print_stats,
|
||||
.extra_mpdu_prefix_len = CCMP_HDR_LEN,
|
||||
.extra_mpdu_postfix_len = CCMP_MIC_LEN,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init lib80211_crypto_ccmp_init(void)
|
||||
{
|
||||
return lib80211_register_crypto_ops(&lib80211_crypt_ccmp);
|
||||
}
|
||||
|
||||
static void __exit lib80211_crypto_ccmp_exit(void)
|
||||
{
|
||||
lib80211_unregister_crypto_ops(&lib80211_crypt_ccmp);
|
||||
}
|
||||
|
||||
module_init(lib80211_crypto_ccmp_init);
|
||||
module_exit(lib80211_crypto_ccmp_exit);
|
762
net/wireless/lib80211_crypt_tkip.c
Normal file
762
net/wireless/lib80211_crypt_tkip.c
Normal file
|
@ -0,0 +1,762 @@
|
|||
/*
|
||||
* lib80211 crypt: host-based TKIP encryption implementation for lib80211
|
||||
*
|
||||
* Copyright (c) 2003-2004, Jouni Malinen <j@w1.fi>
|
||||
* Copyright (c) 2008, John W. Linville <linville@tuxdriver.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation. See README and COPYING for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <asm/string.h>
|
||||
|
||||
#include <linux/wireless.h>
|
||||
#include <linux/ieee80211.h>
|
||||
#include <net/iw_handler.h>
|
||||
|
||||
#include <linux/crypto.h>
|
||||
#include <linux/crc32.h>
|
||||
|
||||
#include <net/lib80211.h>
|
||||
|
||||
MODULE_AUTHOR("Jouni Malinen");
|
||||
MODULE_DESCRIPTION("lib80211 crypt: TKIP");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define TKIP_HDR_LEN 8
|
||||
|
||||
struct lib80211_tkip_data {
|
||||
#define TKIP_KEY_LEN 32
|
||||
u8 key[TKIP_KEY_LEN];
|
||||
int key_set;
|
||||
|
||||
u32 tx_iv32;
|
||||
u16 tx_iv16;
|
||||
u16 tx_ttak[5];
|
||||
int tx_phase1_done;
|
||||
|
||||
u32 rx_iv32;
|
||||
u16 rx_iv16;
|
||||
u16 rx_ttak[5];
|
||||
int rx_phase1_done;
|
||||
u32 rx_iv32_new;
|
||||
u16 rx_iv16_new;
|
||||
|
||||
u32 dot11RSNAStatsTKIPReplays;
|
||||
u32 dot11RSNAStatsTKIPICVErrors;
|
||||
u32 dot11RSNAStatsTKIPLocalMICFailures;
|
||||
|
||||
int key_idx;
|
||||
|
||||
struct crypto_blkcipher *rx_tfm_arc4;
|
||||
struct crypto_hash *rx_tfm_michael;
|
||||
struct crypto_blkcipher *tx_tfm_arc4;
|
||||
struct crypto_hash *tx_tfm_michael;
|
||||
|
||||
/* scratch buffers for virt_to_page() (crypto API) */
|
||||
u8 rx_hdr[16], tx_hdr[16];
|
||||
|
||||
unsigned long flags;
|
||||
};
|
||||
|
||||
static unsigned long lib80211_tkip_set_flags(unsigned long flags, void *priv)
|
||||
{
|
||||
struct lib80211_tkip_data *_priv = priv;
|
||||
unsigned long old_flags = _priv->flags;
|
||||
_priv->flags = flags;
|
||||
return old_flags;
|
||||
}
|
||||
|
||||
static unsigned long lib80211_tkip_get_flags(void *priv)
|
||||
{
|
||||
struct lib80211_tkip_data *_priv = priv;
|
||||
return _priv->flags;
|
||||
}
|
||||
|
||||
static void *lib80211_tkip_init(int key_idx)
|
||||
{
|
||||
struct lib80211_tkip_data *priv;
|
||||
|
||||
priv = kzalloc(sizeof(*priv), GFP_ATOMIC);
|
||||
if (priv == NULL)
|
||||
goto fail;
|
||||
|
||||
priv->key_idx = key_idx;
|
||||
|
||||
priv->tx_tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0,
|
||||
CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(priv->tx_tfm_arc4)) {
|
||||
priv->tx_tfm_arc4 = NULL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
priv->tx_tfm_michael = crypto_alloc_hash("michael_mic", 0,
|
||||
CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(priv->tx_tfm_michael)) {
|
||||
priv->tx_tfm_michael = NULL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
priv->rx_tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0,
|
||||
CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(priv->rx_tfm_arc4)) {
|
||||
priv->rx_tfm_arc4 = NULL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
priv->rx_tfm_michael = crypto_alloc_hash("michael_mic", 0,
|
||||
CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(priv->rx_tfm_michael)) {
|
||||
priv->rx_tfm_michael = NULL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return priv;
|
||||
|
||||
fail:
|
||||
if (priv) {
|
||||
if (priv->tx_tfm_michael)
|
||||
crypto_free_hash(priv->tx_tfm_michael);
|
||||
if (priv->tx_tfm_arc4)
|
||||
crypto_free_blkcipher(priv->tx_tfm_arc4);
|
||||
if (priv->rx_tfm_michael)
|
||||
crypto_free_hash(priv->rx_tfm_michael);
|
||||
if (priv->rx_tfm_arc4)
|
||||
crypto_free_blkcipher(priv->rx_tfm_arc4);
|
||||
kfree(priv);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void lib80211_tkip_deinit(void *priv)
|
||||
{
|
||||
struct lib80211_tkip_data *_priv = priv;
|
||||
if (_priv) {
|
||||
if (_priv->tx_tfm_michael)
|
||||
crypto_free_hash(_priv->tx_tfm_michael);
|
||||
if (_priv->tx_tfm_arc4)
|
||||
crypto_free_blkcipher(_priv->tx_tfm_arc4);
|
||||
if (_priv->rx_tfm_michael)
|
||||
crypto_free_hash(_priv->rx_tfm_michael);
|
||||
if (_priv->rx_tfm_arc4)
|
||||
crypto_free_blkcipher(_priv->rx_tfm_arc4);
|
||||
}
|
||||
kfree(priv);
|
||||
}
|
||||
|
||||
static inline u16 RotR1(u16 val)
|
||||
{
|
||||
return (val >> 1) | (val << 15);
|
||||
}
|
||||
|
||||
static inline u8 Lo8(u16 val)
|
||||
{
|
||||
return val & 0xff;
|
||||
}
|
||||
|
||||
static inline u8 Hi8(u16 val)
|
||||
{
|
||||
return val >> 8;
|
||||
}
|
||||
|
||||
static inline u16 Lo16(u32 val)
|
||||
{
|
||||
return val & 0xffff;
|
||||
}
|
||||
|
||||
static inline u16 Hi16(u32 val)
|
||||
{
|
||||
return val >> 16;
|
||||
}
|
||||
|
||||
static inline u16 Mk16(u8 hi, u8 lo)
|
||||
{
|
||||
return lo | (((u16) hi) << 8);
|
||||
}
|
||||
|
||||
static inline u16 Mk16_le(__le16 * v)
|
||||
{
|
||||
return le16_to_cpu(*v);
|
||||
}
|
||||
|
||||
static const u16 Sbox[256] = {
|
||||
0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154,
|
||||
0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A,
|
||||
0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B,
|
||||
0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B,
|
||||
0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F,
|
||||
0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F,
|
||||
0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5,
|
||||
0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F,
|
||||
0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB,
|
||||
0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397,
|
||||
0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED,
|
||||
0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A,
|
||||
0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194,
|
||||
0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3,
|
||||
0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104,
|
||||
0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D,
|
||||
0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39,
|
||||
0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695,
|
||||
0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83,
|
||||
0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76,
|
||||
0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4,
|
||||
0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B,
|
||||
0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0,
|
||||
0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018,
|
||||
0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751,
|
||||
0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85,
|
||||
0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12,
|
||||
0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9,
|
||||
0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7,
|
||||
0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A,
|
||||
0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8,
|
||||
0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A,
|
||||
};
|
||||
|
||||
static inline u16 _S_(u16 v)
|
||||
{
|
||||
u16 t = Sbox[Hi8(v)];
|
||||
return Sbox[Lo8(v)] ^ ((t << 8) | (t >> 8));
|
||||
}
|
||||
|
||||
#define PHASE1_LOOP_COUNT 8
|
||||
|
||||
static void tkip_mixing_phase1(u16 * TTAK, const u8 * TK, const u8 * TA,
|
||||
u32 IV32)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
/* Initialize the 80-bit TTAK from TSC (IV32) and TA[0..5] */
|
||||
TTAK[0] = Lo16(IV32);
|
||||
TTAK[1] = Hi16(IV32);
|
||||
TTAK[2] = Mk16(TA[1], TA[0]);
|
||||
TTAK[3] = Mk16(TA[3], TA[2]);
|
||||
TTAK[4] = Mk16(TA[5], TA[4]);
|
||||
|
||||
for (i = 0; i < PHASE1_LOOP_COUNT; i++) {
|
||||
j = 2 * (i & 1);
|
||||
TTAK[0] += _S_(TTAK[4] ^ Mk16(TK[1 + j], TK[0 + j]));
|
||||
TTAK[1] += _S_(TTAK[0] ^ Mk16(TK[5 + j], TK[4 + j]));
|
||||
TTAK[2] += _S_(TTAK[1] ^ Mk16(TK[9 + j], TK[8 + j]));
|
||||
TTAK[3] += _S_(TTAK[2] ^ Mk16(TK[13 + j], TK[12 + j]));
|
||||
TTAK[4] += _S_(TTAK[3] ^ Mk16(TK[1 + j], TK[0 + j])) + i;
|
||||
}
|
||||
}
|
||||
|
||||
static void tkip_mixing_phase2(u8 * WEPSeed, const u8 * TK, const u16 * TTAK,
|
||||
u16 IV16)
|
||||
{
|
||||
/* Make temporary area overlap WEP seed so that the final copy can be
|
||||
* avoided on little endian hosts. */
|
||||
u16 *PPK = (u16 *) & WEPSeed[4];
|
||||
|
||||
/* Step 1 - make copy of TTAK and bring in TSC */
|
||||
PPK[0] = TTAK[0];
|
||||
PPK[1] = TTAK[1];
|
||||
PPK[2] = TTAK[2];
|
||||
PPK[3] = TTAK[3];
|
||||
PPK[4] = TTAK[4];
|
||||
PPK[5] = TTAK[4] + IV16;
|
||||
|
||||
/* Step 2 - 96-bit bijective mixing using S-box */
|
||||
PPK[0] += _S_(PPK[5] ^ Mk16_le((__le16 *) & TK[0]));
|
||||
PPK[1] += _S_(PPK[0] ^ Mk16_le((__le16 *) & TK[2]));
|
||||
PPK[2] += _S_(PPK[1] ^ Mk16_le((__le16 *) & TK[4]));
|
||||
PPK[3] += _S_(PPK[2] ^ Mk16_le((__le16 *) & TK[6]));
|
||||
PPK[4] += _S_(PPK[3] ^ Mk16_le((__le16 *) & TK[8]));
|
||||
PPK[5] += _S_(PPK[4] ^ Mk16_le((__le16 *) & TK[10]));
|
||||
|
||||
PPK[0] += RotR1(PPK[5] ^ Mk16_le((__le16 *) & TK[12]));
|
||||
PPK[1] += RotR1(PPK[0] ^ Mk16_le((__le16 *) & TK[14]));
|
||||
PPK[2] += RotR1(PPK[1]);
|
||||
PPK[3] += RotR1(PPK[2]);
|
||||
PPK[4] += RotR1(PPK[3]);
|
||||
PPK[5] += RotR1(PPK[4]);
|
||||
|
||||
/* Step 3 - bring in last of TK bits, assign 24-bit WEP IV value
|
||||
* WEPSeed[0..2] is transmitted as WEP IV */
|
||||
WEPSeed[0] = Hi8(IV16);
|
||||
WEPSeed[1] = (Hi8(IV16) | 0x20) & 0x7F;
|
||||
WEPSeed[2] = Lo8(IV16);
|
||||
WEPSeed[3] = Lo8((PPK[5] ^ Mk16_le((__le16 *) & TK[0])) >> 1);
|
||||
|
||||
#ifdef __BIG_ENDIAN
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 6; i++)
|
||||
PPK[i] = (PPK[i] << 8) | (PPK[i] >> 8);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static int lib80211_tkip_hdr(struct sk_buff *skb, int hdr_len,
|
||||
u8 * rc4key, int keylen, void *priv)
|
||||
{
|
||||
struct lib80211_tkip_data *tkey = priv;
|
||||
u8 *pos;
|
||||
struct ieee80211_hdr *hdr;
|
||||
|
||||
hdr = (struct ieee80211_hdr *)skb->data;
|
||||
|
||||
if (skb_headroom(skb) < TKIP_HDR_LEN || skb->len < hdr_len)
|
||||
return -1;
|
||||
|
||||
if (rc4key == NULL || keylen < 16)
|
||||
return -1;
|
||||
|
||||
if (!tkey->tx_phase1_done) {
|
||||
tkip_mixing_phase1(tkey->tx_ttak, tkey->key, hdr->addr2,
|
||||
tkey->tx_iv32);
|
||||
tkey->tx_phase1_done = 1;
|
||||
}
|
||||
tkip_mixing_phase2(rc4key, tkey->key, tkey->tx_ttak, tkey->tx_iv16);
|
||||
|
||||
pos = skb_push(skb, TKIP_HDR_LEN);
|
||||
memmove(pos, pos + TKIP_HDR_LEN, hdr_len);
|
||||
pos += hdr_len;
|
||||
|
||||
*pos++ = *rc4key;
|
||||
*pos++ = *(rc4key + 1);
|
||||
*pos++ = *(rc4key + 2);
|
||||
*pos++ = (tkey->key_idx << 6) | (1 << 5) /* Ext IV included */ ;
|
||||
*pos++ = tkey->tx_iv32 & 0xff;
|
||||
*pos++ = (tkey->tx_iv32 >> 8) & 0xff;
|
||||
*pos++ = (tkey->tx_iv32 >> 16) & 0xff;
|
||||
*pos++ = (tkey->tx_iv32 >> 24) & 0xff;
|
||||
|
||||
tkey->tx_iv16++;
|
||||
if (tkey->tx_iv16 == 0) {
|
||||
tkey->tx_phase1_done = 0;
|
||||
tkey->tx_iv32++;
|
||||
}
|
||||
|
||||
return TKIP_HDR_LEN;
|
||||
}
|
||||
|
||||
static int lib80211_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
|
||||
{
|
||||
struct lib80211_tkip_data *tkey = priv;
|
||||
struct blkcipher_desc desc = { .tfm = tkey->tx_tfm_arc4 };
|
||||
int len;
|
||||
u8 rc4key[16], *pos, *icv;
|
||||
u32 crc;
|
||||
struct scatterlist sg;
|
||||
|
||||
if (tkey->flags & IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) {
|
||||
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
|
||||
net_dbg_ratelimited("TKIP countermeasures: dropped TX packet to %pM\n",
|
||||
hdr->addr1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (skb_tailroom(skb) < 4 || skb->len < hdr_len)
|
||||
return -1;
|
||||
|
||||
len = skb->len - hdr_len;
|
||||
pos = skb->data + hdr_len;
|
||||
|
||||
if ((lib80211_tkip_hdr(skb, hdr_len, rc4key, 16, priv)) < 0)
|
||||
return -1;
|
||||
|
||||
crc = ~crc32_le(~0, pos, len);
|
||||
icv = skb_put(skb, 4);
|
||||
icv[0] = crc;
|
||||
icv[1] = crc >> 8;
|
||||
icv[2] = crc >> 16;
|
||||
icv[3] = crc >> 24;
|
||||
|
||||
crypto_blkcipher_setkey(tkey->tx_tfm_arc4, rc4key, 16);
|
||||
sg_init_one(&sg, pos, len + 4);
|
||||
return crypto_blkcipher_encrypt(&desc, &sg, &sg, len + 4);
|
||||
}
|
||||
|
||||
/*
|
||||
* deal with seq counter wrapping correctly.
|
||||
* refer to timer_after() for jiffies wrapping handling
|
||||
*/
|
||||
static inline int tkip_replay_check(u32 iv32_n, u16 iv16_n,
|
||||
u32 iv32_o, u16 iv16_o)
|
||||
{
|
||||
if ((s32)iv32_n - (s32)iv32_o < 0 ||
|
||||
(iv32_n == iv32_o && iv16_n <= iv16_o))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lib80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
|
||||
{
|
||||
struct lib80211_tkip_data *tkey = priv;
|
||||
struct blkcipher_desc desc = { .tfm = tkey->rx_tfm_arc4 };
|
||||
u8 rc4key[16];
|
||||
u8 keyidx, *pos;
|
||||
u32 iv32;
|
||||
u16 iv16;
|
||||
struct ieee80211_hdr *hdr;
|
||||
u8 icv[4];
|
||||
u32 crc;
|
||||
struct scatterlist sg;
|
||||
int plen;
|
||||
|
||||
hdr = (struct ieee80211_hdr *)skb->data;
|
||||
|
||||
if (tkey->flags & IEEE80211_CRYPTO_TKIP_COUNTERMEASURES) {
|
||||
net_dbg_ratelimited("TKIP countermeasures: dropped received packet from %pM\n",
|
||||
hdr->addr2);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (skb->len < hdr_len + TKIP_HDR_LEN + 4)
|
||||
return -1;
|
||||
|
||||
pos = skb->data + hdr_len;
|
||||
keyidx = pos[3];
|
||||
if (!(keyidx & (1 << 5))) {
|
||||
net_dbg_ratelimited("TKIP: received packet without ExtIV flag from %pM\n",
|
||||
hdr->addr2);
|
||||
return -2;
|
||||
}
|
||||
keyidx >>= 6;
|
||||
if (tkey->key_idx != keyidx) {
|
||||
printk(KERN_DEBUG "TKIP: RX tkey->key_idx=%d frame "
|
||||
"keyidx=%d priv=%p\n", tkey->key_idx, keyidx, priv);
|
||||
return -6;
|
||||
}
|
||||
if (!tkey->key_set) {
|
||||
net_dbg_ratelimited("TKIP: received packet from %pM with keyid=%d that does not have a configured key\n",
|
||||
hdr->addr2, keyidx);
|
||||
return -3;
|
||||
}
|
||||
iv16 = (pos[0] << 8) | pos[2];
|
||||
iv32 = pos[4] | (pos[5] << 8) | (pos[6] << 16) | (pos[7] << 24);
|
||||
pos += TKIP_HDR_LEN;
|
||||
|
||||
if (tkip_replay_check(iv32, iv16, tkey->rx_iv32, tkey->rx_iv16)) {
|
||||
#ifdef CONFIG_LIB80211_DEBUG
|
||||
net_dbg_ratelimited("TKIP: replay detected: STA=%pM previous TSC %08x%04x received TSC %08x%04x\n",
|
||||
hdr->addr2, tkey->rx_iv32, tkey->rx_iv16,
|
||||
iv32, iv16);
|
||||
#endif
|
||||
tkey->dot11RSNAStatsTKIPReplays++;
|
||||
return -4;
|
||||
}
|
||||
|
||||
if (iv32 != tkey->rx_iv32 || !tkey->rx_phase1_done) {
|
||||
tkip_mixing_phase1(tkey->rx_ttak, tkey->key, hdr->addr2, iv32);
|
||||
tkey->rx_phase1_done = 1;
|
||||
}
|
||||
tkip_mixing_phase2(rc4key, tkey->key, tkey->rx_ttak, iv16);
|
||||
|
||||
plen = skb->len - hdr_len - 12;
|
||||
|
||||
crypto_blkcipher_setkey(tkey->rx_tfm_arc4, rc4key, 16);
|
||||
sg_init_one(&sg, pos, plen + 4);
|
||||
if (crypto_blkcipher_decrypt(&desc, &sg, &sg, plen + 4)) {
|
||||
net_dbg_ratelimited("TKIP: failed to decrypt received packet from %pM\n",
|
||||
hdr->addr2);
|
||||
return -7;
|
||||
}
|
||||
|
||||
crc = ~crc32_le(~0, pos, plen);
|
||||
icv[0] = crc;
|
||||
icv[1] = crc >> 8;
|
||||
icv[2] = crc >> 16;
|
||||
icv[3] = crc >> 24;
|
||||
if (memcmp(icv, pos + plen, 4) != 0) {
|
||||
if (iv32 != tkey->rx_iv32) {
|
||||
/* Previously cached Phase1 result was already lost, so
|
||||
* it needs to be recalculated for the next packet. */
|
||||
tkey->rx_phase1_done = 0;
|
||||
}
|
||||
#ifdef CONFIG_LIB80211_DEBUG
|
||||
net_dbg_ratelimited("TKIP: ICV error detected: STA=%pM\n",
|
||||
hdr->addr2);
|
||||
#endif
|
||||
tkey->dot11RSNAStatsTKIPICVErrors++;
|
||||
return -5;
|
||||
}
|
||||
|
||||
/* Update real counters only after Michael MIC verification has
|
||||
* completed */
|
||||
tkey->rx_iv32_new = iv32;
|
||||
tkey->rx_iv16_new = iv16;
|
||||
|
||||
/* Remove IV and ICV */
|
||||
memmove(skb->data + TKIP_HDR_LEN, skb->data, hdr_len);
|
||||
skb_pull(skb, TKIP_HDR_LEN);
|
||||
skb_trim(skb, skb->len - 4);
|
||||
|
||||
return keyidx;
|
||||
}
|
||||
|
||||
static int michael_mic(struct crypto_hash *tfm_michael, u8 * key, u8 * hdr,
|
||||
u8 * data, size_t data_len, u8 * mic)
|
||||
{
|
||||
struct hash_desc desc;
|
||||
struct scatterlist sg[2];
|
||||
|
||||
if (tfm_michael == NULL) {
|
||||
pr_warn("%s(): tfm_michael == NULL\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
sg_init_table(sg, 2);
|
||||
sg_set_buf(&sg[0], hdr, 16);
|
||||
sg_set_buf(&sg[1], data, data_len);
|
||||
|
||||
if (crypto_hash_setkey(tfm_michael, key, 8))
|
||||
return -1;
|
||||
|
||||
desc.tfm = tfm_michael;
|
||||
desc.flags = 0;
|
||||
return crypto_hash_digest(&desc, sg, data_len + 16, mic);
|
||||
}
|
||||
|
||||
static void michael_mic_hdr(struct sk_buff *skb, u8 * hdr)
|
||||
{
|
||||
struct ieee80211_hdr *hdr11;
|
||||
|
||||
hdr11 = (struct ieee80211_hdr *)skb->data;
|
||||
|
||||
switch (le16_to_cpu(hdr11->frame_control) &
|
||||
(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) {
|
||||
case IEEE80211_FCTL_TODS:
|
||||
memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */
|
||||
memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */
|
||||
break;
|
||||
case IEEE80211_FCTL_FROMDS:
|
||||
memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */
|
||||
memcpy(hdr + ETH_ALEN, hdr11->addr3, ETH_ALEN); /* SA */
|
||||
break;
|
||||
case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS:
|
||||
memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */
|
||||
memcpy(hdr + ETH_ALEN, hdr11->addr4, ETH_ALEN); /* SA */
|
||||
break;
|
||||
case 0:
|
||||
memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */
|
||||
memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */
|
||||
break;
|
||||
}
|
||||
|
||||
if (ieee80211_is_data_qos(hdr11->frame_control)) {
|
||||
hdr[12] = le16_to_cpu(*((__le16 *)ieee80211_get_qos_ctl(hdr11)))
|
||||
& IEEE80211_QOS_CTL_TID_MASK;
|
||||
} else
|
||||
hdr[12] = 0; /* priority */
|
||||
|
||||
hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */
|
||||
}
|
||||
|
||||
static int lib80211_michael_mic_add(struct sk_buff *skb, int hdr_len,
|
||||
void *priv)
|
||||
{
|
||||
struct lib80211_tkip_data *tkey = priv;
|
||||
u8 *pos;
|
||||
|
||||
if (skb_tailroom(skb) < 8 || skb->len < hdr_len) {
|
||||
printk(KERN_DEBUG "Invalid packet for Michael MIC add "
|
||||
"(tailroom=%d hdr_len=%d skb->len=%d)\n",
|
||||
skb_tailroom(skb), hdr_len, skb->len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
michael_mic_hdr(skb, tkey->tx_hdr);
|
||||
pos = skb_put(skb, 8);
|
||||
if (michael_mic(tkey->tx_tfm_michael, &tkey->key[16], tkey->tx_hdr,
|
||||
skb->data + hdr_len, skb->len - 8 - hdr_len, pos))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lib80211_michael_mic_failure(struct net_device *dev,
|
||||
struct ieee80211_hdr *hdr,
|
||||
int keyidx)
|
||||
{
|
||||
union iwreq_data wrqu;
|
||||
struct iw_michaelmicfailure ev;
|
||||
|
||||
/* TODO: needed parameters: count, keyid, key type, TSC */
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.flags = keyidx & IW_MICFAILURE_KEY_ID;
|
||||
if (hdr->addr1[0] & 0x01)
|
||||
ev.flags |= IW_MICFAILURE_GROUP;
|
||||
else
|
||||
ev.flags |= IW_MICFAILURE_PAIRWISE;
|
||||
ev.src_addr.sa_family = ARPHRD_ETHER;
|
||||
memcpy(ev.src_addr.sa_data, hdr->addr2, ETH_ALEN);
|
||||
memset(&wrqu, 0, sizeof(wrqu));
|
||||
wrqu.data.length = sizeof(ev);
|
||||
wireless_send_event(dev, IWEVMICHAELMICFAILURE, &wrqu, (char *)&ev);
|
||||
}
|
||||
|
||||
static int lib80211_michael_mic_verify(struct sk_buff *skb, int keyidx,
|
||||
int hdr_len, void *priv)
|
||||
{
|
||||
struct lib80211_tkip_data *tkey = priv;
|
||||
u8 mic[8];
|
||||
|
||||
if (!tkey->key_set)
|
||||
return -1;
|
||||
|
||||
michael_mic_hdr(skb, tkey->rx_hdr);
|
||||
if (michael_mic(tkey->rx_tfm_michael, &tkey->key[24], tkey->rx_hdr,
|
||||
skb->data + hdr_len, skb->len - 8 - hdr_len, mic))
|
||||
return -1;
|
||||
if (memcmp(mic, skb->data + skb->len - 8, 8) != 0) {
|
||||
struct ieee80211_hdr *hdr;
|
||||
hdr = (struct ieee80211_hdr *)skb->data;
|
||||
printk(KERN_DEBUG "%s: Michael MIC verification failed for "
|
||||
"MSDU from %pM keyidx=%d\n",
|
||||
skb->dev ? skb->dev->name : "N/A", hdr->addr2,
|
||||
keyidx);
|
||||
if (skb->dev)
|
||||
lib80211_michael_mic_failure(skb->dev, hdr, keyidx);
|
||||
tkey->dot11RSNAStatsTKIPLocalMICFailures++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Update TSC counters for RX now that the packet verification has
|
||||
* completed. */
|
||||
tkey->rx_iv32 = tkey->rx_iv32_new;
|
||||
tkey->rx_iv16 = tkey->rx_iv16_new;
|
||||
|
||||
skb_trim(skb, skb->len - 8);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lib80211_tkip_set_key(void *key, int len, u8 * seq, void *priv)
|
||||
{
|
||||
struct lib80211_tkip_data *tkey = priv;
|
||||
int keyidx;
|
||||
struct crypto_hash *tfm = tkey->tx_tfm_michael;
|
||||
struct crypto_blkcipher *tfm2 = tkey->tx_tfm_arc4;
|
||||
struct crypto_hash *tfm3 = tkey->rx_tfm_michael;
|
||||
struct crypto_blkcipher *tfm4 = tkey->rx_tfm_arc4;
|
||||
|
||||
keyidx = tkey->key_idx;
|
||||
memset(tkey, 0, sizeof(*tkey));
|
||||
tkey->key_idx = keyidx;
|
||||
tkey->tx_tfm_michael = tfm;
|
||||
tkey->tx_tfm_arc4 = tfm2;
|
||||
tkey->rx_tfm_michael = tfm3;
|
||||
tkey->rx_tfm_arc4 = tfm4;
|
||||
if (len == TKIP_KEY_LEN) {
|
||||
memcpy(tkey->key, key, TKIP_KEY_LEN);
|
||||
tkey->key_set = 1;
|
||||
tkey->tx_iv16 = 1; /* TSC is initialized to 1 */
|
||||
if (seq) {
|
||||
tkey->rx_iv32 = (seq[5] << 24) | (seq[4] << 16) |
|
||||
(seq[3] << 8) | seq[2];
|
||||
tkey->rx_iv16 = (seq[1] << 8) | seq[0];
|
||||
}
|
||||
} else if (len == 0)
|
||||
tkey->key_set = 0;
|
||||
else
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lib80211_tkip_get_key(void *key, int len, u8 * seq, void *priv)
|
||||
{
|
||||
struct lib80211_tkip_data *tkey = priv;
|
||||
|
||||
if (len < TKIP_KEY_LEN)
|
||||
return -1;
|
||||
|
||||
if (!tkey->key_set)
|
||||
return 0;
|
||||
memcpy(key, tkey->key, TKIP_KEY_LEN);
|
||||
|
||||
if (seq) {
|
||||
/* Return the sequence number of the last transmitted frame. */
|
||||
u16 iv16 = tkey->tx_iv16;
|
||||
u32 iv32 = tkey->tx_iv32;
|
||||
if (iv16 == 0)
|
||||
iv32--;
|
||||
iv16--;
|
||||
seq[0] = tkey->tx_iv16;
|
||||
seq[1] = tkey->tx_iv16 >> 8;
|
||||
seq[2] = tkey->tx_iv32;
|
||||
seq[3] = tkey->tx_iv32 >> 8;
|
||||
seq[4] = tkey->tx_iv32 >> 16;
|
||||
seq[5] = tkey->tx_iv32 >> 24;
|
||||
}
|
||||
|
||||
return TKIP_KEY_LEN;
|
||||
}
|
||||
|
||||
static void lib80211_tkip_print_stats(struct seq_file *m, void *priv)
|
||||
{
|
||||
struct lib80211_tkip_data *tkip = priv;
|
||||
seq_printf(m,
|
||||
"key[%d] alg=TKIP key_set=%d "
|
||||
"tx_pn=%02x%02x%02x%02x%02x%02x "
|
||||
"rx_pn=%02x%02x%02x%02x%02x%02x "
|
||||
"replays=%d icv_errors=%d local_mic_failures=%d\n",
|
||||
tkip->key_idx, tkip->key_set,
|
||||
(tkip->tx_iv32 >> 24) & 0xff,
|
||||
(tkip->tx_iv32 >> 16) & 0xff,
|
||||
(tkip->tx_iv32 >> 8) & 0xff,
|
||||
tkip->tx_iv32 & 0xff,
|
||||
(tkip->tx_iv16 >> 8) & 0xff,
|
||||
tkip->tx_iv16 & 0xff,
|
||||
(tkip->rx_iv32 >> 24) & 0xff,
|
||||
(tkip->rx_iv32 >> 16) & 0xff,
|
||||
(tkip->rx_iv32 >> 8) & 0xff,
|
||||
tkip->rx_iv32 & 0xff,
|
||||
(tkip->rx_iv16 >> 8) & 0xff,
|
||||
tkip->rx_iv16 & 0xff,
|
||||
tkip->dot11RSNAStatsTKIPReplays,
|
||||
tkip->dot11RSNAStatsTKIPICVErrors,
|
||||
tkip->dot11RSNAStatsTKIPLocalMICFailures);
|
||||
}
|
||||
|
||||
static struct lib80211_crypto_ops lib80211_crypt_tkip = {
|
||||
.name = "TKIP",
|
||||
.init = lib80211_tkip_init,
|
||||
.deinit = lib80211_tkip_deinit,
|
||||
.encrypt_mpdu = lib80211_tkip_encrypt,
|
||||
.decrypt_mpdu = lib80211_tkip_decrypt,
|
||||
.encrypt_msdu = lib80211_michael_mic_add,
|
||||
.decrypt_msdu = lib80211_michael_mic_verify,
|
||||
.set_key = lib80211_tkip_set_key,
|
||||
.get_key = lib80211_tkip_get_key,
|
||||
.print_stats = lib80211_tkip_print_stats,
|
||||
.extra_mpdu_prefix_len = 4 + 4, /* IV + ExtIV */
|
||||
.extra_mpdu_postfix_len = 4, /* ICV */
|
||||
.extra_msdu_postfix_len = 8, /* MIC */
|
||||
.get_flags = lib80211_tkip_get_flags,
|
||||
.set_flags = lib80211_tkip_set_flags,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init lib80211_crypto_tkip_init(void)
|
||||
{
|
||||
return lib80211_register_crypto_ops(&lib80211_crypt_tkip);
|
||||
}
|
||||
|
||||
static void __exit lib80211_crypto_tkip_exit(void)
|
||||
{
|
||||
lib80211_unregister_crypto_ops(&lib80211_crypt_tkip);
|
||||
}
|
||||
|
||||
module_init(lib80211_crypto_tkip_init);
|
||||
module_exit(lib80211_crypto_tkip_exit);
|
289
net/wireless/lib80211_crypt_wep.c
Normal file
289
net/wireless/lib80211_crypt_wep.c
Normal file
|
@ -0,0 +1,289 @@
|
|||
/*
|
||||
* lib80211 crypt: host-based WEP encryption implementation for lib80211
|
||||
*
|
||||
* Copyright (c) 2002-2004, Jouni Malinen <j@w1.fi>
|
||||
* Copyright (c) 2008, John W. Linville <linville@tuxdriver.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation. See README and COPYING for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/mm.h>
|
||||
#include <asm/string.h>
|
||||
|
||||
#include <net/lib80211.h>
|
||||
|
||||
#include <linux/crypto.h>
|
||||
#include <linux/crc32.h>
|
||||
|
||||
MODULE_AUTHOR("Jouni Malinen");
|
||||
MODULE_DESCRIPTION("lib80211 crypt: WEP");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
struct lib80211_wep_data {
|
||||
u32 iv;
|
||||
#define WEP_KEY_LEN 13
|
||||
u8 key[WEP_KEY_LEN + 1];
|
||||
u8 key_len;
|
||||
u8 key_idx;
|
||||
struct crypto_blkcipher *tx_tfm;
|
||||
struct crypto_blkcipher *rx_tfm;
|
||||
};
|
||||
|
||||
static void *lib80211_wep_init(int keyidx)
|
||||
{
|
||||
struct lib80211_wep_data *priv;
|
||||
|
||||
priv = kzalloc(sizeof(*priv), GFP_ATOMIC);
|
||||
if (priv == NULL)
|
||||
goto fail;
|
||||
priv->key_idx = keyidx;
|
||||
|
||||
priv->tx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(priv->tx_tfm)) {
|
||||
priv->tx_tfm = NULL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
priv->rx_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(priv->rx_tfm)) {
|
||||
priv->rx_tfm = NULL;
|
||||
goto fail;
|
||||
}
|
||||
/* start WEP IV from a random value */
|
||||
get_random_bytes(&priv->iv, 4);
|
||||
|
||||
return priv;
|
||||
|
||||
fail:
|
||||
if (priv) {
|
||||
if (priv->tx_tfm)
|
||||
crypto_free_blkcipher(priv->tx_tfm);
|
||||
if (priv->rx_tfm)
|
||||
crypto_free_blkcipher(priv->rx_tfm);
|
||||
kfree(priv);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void lib80211_wep_deinit(void *priv)
|
||||
{
|
||||
struct lib80211_wep_data *_priv = priv;
|
||||
if (_priv) {
|
||||
if (_priv->tx_tfm)
|
||||
crypto_free_blkcipher(_priv->tx_tfm);
|
||||
if (_priv->rx_tfm)
|
||||
crypto_free_blkcipher(_priv->rx_tfm);
|
||||
}
|
||||
kfree(priv);
|
||||
}
|
||||
|
||||
/* Add WEP IV/key info to a frame that has at least 4 bytes of headroom */
|
||||
static int lib80211_wep_build_iv(struct sk_buff *skb, int hdr_len,
|
||||
u8 *key, int keylen, void *priv)
|
||||
{
|
||||
struct lib80211_wep_data *wep = priv;
|
||||
u32 klen;
|
||||
u8 *pos;
|
||||
|
||||
if (skb_headroom(skb) < 4 || skb->len < hdr_len)
|
||||
return -1;
|
||||
|
||||
pos = skb_push(skb, 4);
|
||||
memmove(pos, pos + 4, hdr_len);
|
||||
pos += hdr_len;
|
||||
|
||||
klen = 3 + wep->key_len;
|
||||
|
||||
wep->iv++;
|
||||
|
||||
/* Fluhrer, Mantin, and Shamir have reported weaknesses in the key
|
||||
* scheduling algorithm of RC4. At least IVs (KeyByte + 3, 0xff, N)
|
||||
* can be used to speedup attacks, so avoid using them. */
|
||||
if ((wep->iv & 0xff00) == 0xff00) {
|
||||
u8 B = (wep->iv >> 16) & 0xff;
|
||||
if (B >= 3 && B < klen)
|
||||
wep->iv += 0x0100;
|
||||
}
|
||||
|
||||
/* Prepend 24-bit IV to RC4 key and TX frame */
|
||||
*pos++ = (wep->iv >> 16) & 0xff;
|
||||
*pos++ = (wep->iv >> 8) & 0xff;
|
||||
*pos++ = wep->iv & 0xff;
|
||||
*pos++ = wep->key_idx << 6;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Perform WEP encryption on given skb that has at least 4 bytes of headroom
|
||||
* for IV and 4 bytes of tailroom for ICV. Both IV and ICV will be transmitted,
|
||||
* so the payload length increases with 8 bytes.
|
||||
*
|
||||
* WEP frame payload: IV + TX key idx, RC4(data), ICV = RC4(CRC32(data))
|
||||
*/
|
||||
static int lib80211_wep_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
|
||||
{
|
||||
struct lib80211_wep_data *wep = priv;
|
||||
struct blkcipher_desc desc = { .tfm = wep->tx_tfm };
|
||||
u32 crc, klen, len;
|
||||
u8 *pos, *icv;
|
||||
struct scatterlist sg;
|
||||
u8 key[WEP_KEY_LEN + 3];
|
||||
|
||||
/* other checks are in lib80211_wep_build_iv */
|
||||
if (skb_tailroom(skb) < 4)
|
||||
return -1;
|
||||
|
||||
/* add the IV to the frame */
|
||||
if (lib80211_wep_build_iv(skb, hdr_len, NULL, 0, priv))
|
||||
return -1;
|
||||
|
||||
/* Copy the IV into the first 3 bytes of the key */
|
||||
skb_copy_from_linear_data_offset(skb, hdr_len, key, 3);
|
||||
|
||||
/* Copy rest of the WEP key (the secret part) */
|
||||
memcpy(key + 3, wep->key, wep->key_len);
|
||||
|
||||
len = skb->len - hdr_len - 4;
|
||||
pos = skb->data + hdr_len + 4;
|
||||
klen = 3 + wep->key_len;
|
||||
|
||||
/* Append little-endian CRC32 over only the data and encrypt it to produce ICV */
|
||||
crc = ~crc32_le(~0, pos, len);
|
||||
icv = skb_put(skb, 4);
|
||||
icv[0] = crc;
|
||||
icv[1] = crc >> 8;
|
||||
icv[2] = crc >> 16;
|
||||
icv[3] = crc >> 24;
|
||||
|
||||
crypto_blkcipher_setkey(wep->tx_tfm, key, klen);
|
||||
sg_init_one(&sg, pos, len + 4);
|
||||
return crypto_blkcipher_encrypt(&desc, &sg, &sg, len + 4);
|
||||
}
|
||||
|
||||
/* Perform WEP decryption on given buffer. Buffer includes whole WEP part of
|
||||
* the frame: IV (4 bytes), encrypted payload (including SNAP header),
|
||||
* ICV (4 bytes). len includes both IV and ICV.
|
||||
*
|
||||
* Returns 0 if frame was decrypted successfully and ICV was correct and -1 on
|
||||
* failure. If frame is OK, IV and ICV will be removed.
|
||||
*/
|
||||
static int lib80211_wep_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
|
||||
{
|
||||
struct lib80211_wep_data *wep = priv;
|
||||
struct blkcipher_desc desc = { .tfm = wep->rx_tfm };
|
||||
u32 crc, klen, plen;
|
||||
u8 key[WEP_KEY_LEN + 3];
|
||||
u8 keyidx, *pos, icv[4];
|
||||
struct scatterlist sg;
|
||||
|
||||
if (skb->len < hdr_len + 8)
|
||||
return -1;
|
||||
|
||||
pos = skb->data + hdr_len;
|
||||
key[0] = *pos++;
|
||||
key[1] = *pos++;
|
||||
key[2] = *pos++;
|
||||
keyidx = *pos++ >> 6;
|
||||
if (keyidx != wep->key_idx)
|
||||
return -1;
|
||||
|
||||
klen = 3 + wep->key_len;
|
||||
|
||||
/* Copy rest of the WEP key (the secret part) */
|
||||
memcpy(key + 3, wep->key, wep->key_len);
|
||||
|
||||
/* Apply RC4 to data and compute CRC32 over decrypted data */
|
||||
plen = skb->len - hdr_len - 8;
|
||||
|
||||
crypto_blkcipher_setkey(wep->rx_tfm, key, klen);
|
||||
sg_init_one(&sg, pos, plen + 4);
|
||||
if (crypto_blkcipher_decrypt(&desc, &sg, &sg, plen + 4))
|
||||
return -7;
|
||||
|
||||
crc = ~crc32_le(~0, pos, plen);
|
||||
icv[0] = crc;
|
||||
icv[1] = crc >> 8;
|
||||
icv[2] = crc >> 16;
|
||||
icv[3] = crc >> 24;
|
||||
if (memcmp(icv, pos + plen, 4) != 0) {
|
||||
/* ICV mismatch - drop frame */
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* Remove IV and ICV */
|
||||
memmove(skb->data + 4, skb->data, hdr_len);
|
||||
skb_pull(skb, 4);
|
||||
skb_trim(skb, skb->len - 4);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lib80211_wep_set_key(void *key, int len, u8 * seq, void *priv)
|
||||
{
|
||||
struct lib80211_wep_data *wep = priv;
|
||||
|
||||
if (len < 0 || len > WEP_KEY_LEN)
|
||||
return -1;
|
||||
|
||||
memcpy(wep->key, key, len);
|
||||
wep->key_len = len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lib80211_wep_get_key(void *key, int len, u8 * seq, void *priv)
|
||||
{
|
||||
struct lib80211_wep_data *wep = priv;
|
||||
|
||||
if (len < wep->key_len)
|
||||
return -1;
|
||||
|
||||
memcpy(key, wep->key, wep->key_len);
|
||||
|
||||
return wep->key_len;
|
||||
}
|
||||
|
||||
static void lib80211_wep_print_stats(struct seq_file *m, void *priv)
|
||||
{
|
||||
struct lib80211_wep_data *wep = priv;
|
||||
seq_printf(m, "key[%d] alg=WEP len=%d\n", wep->key_idx, wep->key_len);
|
||||
}
|
||||
|
||||
static struct lib80211_crypto_ops lib80211_crypt_wep = {
|
||||
.name = "WEP",
|
||||
.init = lib80211_wep_init,
|
||||
.deinit = lib80211_wep_deinit,
|
||||
.encrypt_mpdu = lib80211_wep_encrypt,
|
||||
.decrypt_mpdu = lib80211_wep_decrypt,
|
||||
.encrypt_msdu = NULL,
|
||||
.decrypt_msdu = NULL,
|
||||
.set_key = lib80211_wep_set_key,
|
||||
.get_key = lib80211_wep_get_key,
|
||||
.print_stats = lib80211_wep_print_stats,
|
||||
.extra_mpdu_prefix_len = 4, /* IV */
|
||||
.extra_mpdu_postfix_len = 4, /* ICV */
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init lib80211_crypto_wep_init(void)
|
||||
{
|
||||
return lib80211_register_crypto_ops(&lib80211_crypt_wep);
|
||||
}
|
||||
|
||||
static void __exit lib80211_crypto_wep_exit(void)
|
||||
{
|
||||
lib80211_unregister_crypto_ops(&lib80211_crypt_wep);
|
||||
}
|
||||
|
||||
module_init(lib80211_crypto_wep_init);
|
||||
module_exit(lib80211_crypto_wep_exit);
|
279
net/wireless/mesh.c
Normal file
279
net/wireless/mesh.c
Normal file
|
@ -0,0 +1,279 @@
|
|||
#include <linux/ieee80211.h>
|
||||
#include <linux/export.h>
|
||||
#include <net/cfg80211.h>
|
||||
#include "nl80211.h"
|
||||
#include "core.h"
|
||||
#include "rdev-ops.h"
|
||||
|
||||
/* Default values, timeouts in ms */
|
||||
#define MESH_TTL 31
|
||||
#define MESH_DEFAULT_ELEMENT_TTL 31
|
||||
#define MESH_MAX_RETR 3
|
||||
#define MESH_RET_T 100
|
||||
#define MESH_CONF_T 100
|
||||
#define MESH_HOLD_T 100
|
||||
|
||||
#define MESH_PATH_TIMEOUT 5000
|
||||
#define MESH_RANN_INTERVAL 5000
|
||||
#define MESH_PATH_TO_ROOT_TIMEOUT 6000
|
||||
#define MESH_ROOT_INTERVAL 5000
|
||||
#define MESH_ROOT_CONFIRMATION_INTERVAL 2000
|
||||
#define MESH_DEFAULT_PLINK_TIMEOUT 1800 /* timeout in seconds */
|
||||
|
||||
/*
|
||||
* Minimum interval between two consecutive PREQs originated by the same
|
||||
* interface
|
||||
*/
|
||||
#define MESH_PREQ_MIN_INT 10
|
||||
#define MESH_PERR_MIN_INT 100
|
||||
#define MESH_DIAM_TRAVERSAL_TIME 50
|
||||
|
||||
#define MESH_RSSI_THRESHOLD 0
|
||||
|
||||
/*
|
||||
* A path will be refreshed if it is used PATH_REFRESH_TIME milliseconds
|
||||
* before timing out. This way it will remain ACTIVE and no data frames
|
||||
* will be unnecessarily held in the pending queue.
|
||||
*/
|
||||
#define MESH_PATH_REFRESH_TIME 1000
|
||||
#define MESH_MIN_DISCOVERY_TIMEOUT (2 * MESH_DIAM_TRAVERSAL_TIME)
|
||||
|
||||
/* Default maximum number of established plinks per interface */
|
||||
#define MESH_MAX_ESTAB_PLINKS 32
|
||||
|
||||
#define MESH_MAX_PREQ_RETRIES 4
|
||||
|
||||
#define MESH_SYNC_NEIGHBOR_OFFSET_MAX 50
|
||||
|
||||
#define MESH_DEFAULT_BEACON_INTERVAL 1000 /* in 1024 us units (=TUs) */
|
||||
#define MESH_DEFAULT_DTIM_PERIOD 2
|
||||
#define MESH_DEFAULT_AWAKE_WINDOW 10 /* in 1024 us units (=TUs) */
|
||||
|
||||
const struct mesh_config default_mesh_config = {
|
||||
.dot11MeshRetryTimeout = MESH_RET_T,
|
||||
.dot11MeshConfirmTimeout = MESH_CONF_T,
|
||||
.dot11MeshHoldingTimeout = MESH_HOLD_T,
|
||||
.dot11MeshMaxRetries = MESH_MAX_RETR,
|
||||
.dot11MeshTTL = MESH_TTL,
|
||||
.element_ttl = MESH_DEFAULT_ELEMENT_TTL,
|
||||
.auto_open_plinks = true,
|
||||
.dot11MeshMaxPeerLinks = MESH_MAX_ESTAB_PLINKS,
|
||||
.dot11MeshNbrOffsetMaxNeighbor = MESH_SYNC_NEIGHBOR_OFFSET_MAX,
|
||||
.dot11MeshHWMPactivePathTimeout = MESH_PATH_TIMEOUT,
|
||||
.dot11MeshHWMPpreqMinInterval = MESH_PREQ_MIN_INT,
|
||||
.dot11MeshHWMPperrMinInterval = MESH_PERR_MIN_INT,
|
||||
.dot11MeshHWMPnetDiameterTraversalTime = MESH_DIAM_TRAVERSAL_TIME,
|
||||
.dot11MeshHWMPmaxPREQretries = MESH_MAX_PREQ_RETRIES,
|
||||
.path_refresh_time = MESH_PATH_REFRESH_TIME,
|
||||
.min_discovery_timeout = MESH_MIN_DISCOVERY_TIMEOUT,
|
||||
.dot11MeshHWMPRannInterval = MESH_RANN_INTERVAL,
|
||||
.dot11MeshGateAnnouncementProtocol = false,
|
||||
.dot11MeshForwarding = true,
|
||||
.rssi_threshold = MESH_RSSI_THRESHOLD,
|
||||
.ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED,
|
||||
.dot11MeshHWMPactivePathToRootTimeout = MESH_PATH_TO_ROOT_TIMEOUT,
|
||||
.dot11MeshHWMProotInterval = MESH_ROOT_INTERVAL,
|
||||
.dot11MeshHWMPconfirmationInterval = MESH_ROOT_CONFIRMATION_INTERVAL,
|
||||
.power_mode = NL80211_MESH_POWER_ACTIVE,
|
||||
.dot11MeshAwakeWindowDuration = MESH_DEFAULT_AWAKE_WINDOW,
|
||||
.plink_timeout = MESH_DEFAULT_PLINK_TIMEOUT,
|
||||
};
|
||||
|
||||
const struct mesh_setup default_mesh_setup = {
|
||||
/* cfg80211_join_mesh() will pick a channel if needed */
|
||||
.sync_method = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET,
|
||||
.path_sel_proto = IEEE80211_PATH_PROTOCOL_HWMP,
|
||||
.path_metric = IEEE80211_PATH_METRIC_AIRTIME,
|
||||
.auth_id = 0, /* open */
|
||||
.ie = NULL,
|
||||
.ie_len = 0,
|
||||
.is_secure = false,
|
||||
.user_mpm = false,
|
||||
.beacon_interval = MESH_DEFAULT_BEACON_INTERVAL,
|
||||
.dtim_period = MESH_DEFAULT_DTIM_PERIOD,
|
||||
};
|
||||
|
||||
int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct mesh_setup *setup,
|
||||
const struct mesh_config *conf)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
int err;
|
||||
|
||||
BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN != IEEE80211_MAX_MESH_ID_LEN);
|
||||
|
||||
ASSERT_WDEV_LOCK(wdev);
|
||||
|
||||
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!(rdev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) &&
|
||||
setup->is_secure)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (wdev->mesh_id_len)
|
||||
return -EALREADY;
|
||||
|
||||
if (!setup->mesh_id_len)
|
||||
return -EINVAL;
|
||||
|
||||
if (!rdev->ops->join_mesh)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!setup->chandef.chan) {
|
||||
/* if no channel explicitly given, use preset channel */
|
||||
setup->chandef = wdev->preset_chandef;
|
||||
}
|
||||
|
||||
if (!setup->chandef.chan) {
|
||||
/* if we don't have that either, use the first usable channel */
|
||||
enum ieee80211_band band;
|
||||
|
||||
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
|
||||
struct ieee80211_supported_band *sband;
|
||||
struct ieee80211_channel *chan;
|
||||
int i;
|
||||
|
||||
sband = rdev->wiphy.bands[band];
|
||||
if (!sband)
|
||||
continue;
|
||||
|
||||
for (i = 0; i < sband->n_channels; i++) {
|
||||
chan = &sband->channels[i];
|
||||
if (chan->flags & (IEEE80211_CHAN_NO_IR |
|
||||
IEEE80211_CHAN_DISABLED |
|
||||
IEEE80211_CHAN_RADAR))
|
||||
continue;
|
||||
setup->chandef.chan = chan;
|
||||
break;
|
||||
}
|
||||
|
||||
if (setup->chandef.chan)
|
||||
break;
|
||||
}
|
||||
|
||||
/* no usable channel ... */
|
||||
if (!setup->chandef.chan)
|
||||
return -EINVAL;
|
||||
|
||||
setup->chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
|
||||
setup->chandef.center_freq1 = setup->chandef.chan->center_freq;
|
||||
}
|
||||
|
||||
/*
|
||||
* check if basic rates are available otherwise use mandatory rates as
|
||||
* basic rates
|
||||
*/
|
||||
if (!setup->basic_rates) {
|
||||
enum nl80211_bss_scan_width scan_width;
|
||||
struct ieee80211_supported_band *sband =
|
||||
rdev->wiphy.bands[setup->chandef.chan->band];
|
||||
scan_width = cfg80211_chandef_to_scan_width(&setup->chandef);
|
||||
setup->basic_rates = ieee80211_mandatory_rates(sband,
|
||||
scan_width);
|
||||
}
|
||||
|
||||
if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef,
|
||||
NL80211_IFTYPE_MESH_POINT))
|
||||
return -EINVAL;
|
||||
|
||||
err = rdev_join_mesh(rdev, dev, conf, setup);
|
||||
if (!err) {
|
||||
memcpy(wdev->ssid, setup->mesh_id, setup->mesh_id_len);
|
||||
wdev->mesh_id_len = setup->mesh_id_len;
|
||||
wdev->chandef = setup->chandef;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct mesh_setup *setup,
|
||||
const struct mesh_config *conf)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
int err;
|
||||
|
||||
wdev_lock(wdev);
|
||||
err = __cfg80211_join_mesh(rdev, dev, setup, conf);
|
||||
wdev_unlock(wdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
int err;
|
||||
|
||||
/*
|
||||
* Workaround for libertas (only!), it puts the interface
|
||||
* into mesh mode but doesn't implement join_mesh. Instead,
|
||||
* it is configured via sysfs and then joins the mesh when
|
||||
* you set the channel. Note that the libertas mesh isn't
|
||||
* compatible with 802.11 mesh.
|
||||
*/
|
||||
if (rdev->ops->libertas_set_mesh_channel) {
|
||||
if (chandef->width != NL80211_CHAN_WIDTH_20_NOHT)
|
||||
return -EINVAL;
|
||||
|
||||
if (!netif_running(wdev->netdev))
|
||||
return -ENETDOWN;
|
||||
|
||||
err = rdev_libertas_set_mesh_channel(rdev, wdev->netdev,
|
||||
chandef->chan);
|
||||
if (!err)
|
||||
wdev->chandef = *chandef;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
if (wdev->mesh_id_len)
|
||||
return -EBUSY;
|
||||
|
||||
wdev->preset_chandef = *chandef;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
int err;
|
||||
|
||||
ASSERT_WDEV_LOCK(wdev);
|
||||
|
||||
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!rdev->ops->leave_mesh)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!wdev->mesh_id_len)
|
||||
return -ENOTCONN;
|
||||
|
||||
err = rdev_leave_mesh(rdev, dev);
|
||||
if (!err) {
|
||||
wdev->mesh_id_len = 0;
|
||||
memset(&wdev->chandef, 0, sizeof(wdev->chandef));
|
||||
rdev_set_qos_map(rdev, dev, NULL);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
int err;
|
||||
|
||||
wdev_lock(wdev);
|
||||
err = __cfg80211_leave_mesh(rdev, dev);
|
||||
wdev_unlock(wdev);
|
||||
|
||||
return err;
|
||||
}
|
783
net/wireless/mlme.c
Normal file
783
net/wireless/mlme.c
Normal file
|
@ -0,0 +1,783 @@
|
|||
/*
|
||||
* cfg80211 MLME SAP interface
|
||||
*
|
||||
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/nl80211.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/wireless.h>
|
||||
#include <net/cfg80211.h>
|
||||
#include <net/iw_handler.h>
|
||||
#include "core.h"
|
||||
#include "nl80211.h"
|
||||
#include "rdev-ops.h"
|
||||
|
||||
|
||||
void cfg80211_rx_assoc_resp(struct net_device *dev, struct cfg80211_bss *bss,
|
||||
const u8 *buf, size_t len, int uapsd_queues)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct wiphy *wiphy = wdev->wiphy;
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
||||
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
|
||||
u8 *ie = mgmt->u.assoc_resp.variable;
|
||||
int ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable);
|
||||
u16 status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
|
||||
|
||||
trace_cfg80211_send_rx_assoc(dev, bss);
|
||||
|
||||
/*
|
||||
* This is a bit of a hack, we don't notify userspace of
|
||||
* a (re-)association reply if we tried to send a reassoc
|
||||
* and got a reject -- we only try again with an assoc
|
||||
* frame instead of reassoc.
|
||||
*/
|
||||
if (cfg80211_sme_rx_assoc_resp(wdev, status_code)) {
|
||||
cfg80211_unhold_bss(bss_from_pub(bss));
|
||||
cfg80211_put_bss(wiphy, bss);
|
||||
return;
|
||||
}
|
||||
|
||||
nl80211_send_rx_assoc(rdev, dev, buf, len, GFP_KERNEL, uapsd_queues);
|
||||
/* update current_bss etc., consumes the bss reference */
|
||||
__cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs,
|
||||
status_code,
|
||||
status_code == WLAN_STATUS_SUCCESS, bss);
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_rx_assoc_resp);
|
||||
|
||||
static void cfg80211_process_auth(struct wireless_dev *wdev,
|
||||
const u8 *buf, size_t len)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||
|
||||
nl80211_send_rx_auth(rdev, wdev->netdev, buf, len, GFP_KERNEL);
|
||||
cfg80211_sme_rx_auth(wdev, buf, len);
|
||||
}
|
||||
|
||||
static void cfg80211_process_deauth(struct wireless_dev *wdev,
|
||||
const u8 *buf, size_t len)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
|
||||
const u8 *bssid = mgmt->bssid;
|
||||
u16 reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
|
||||
bool from_ap = !ether_addr_equal(mgmt->sa, wdev->netdev->dev_addr);
|
||||
|
||||
nl80211_send_deauth(rdev, wdev->netdev, buf, len, GFP_KERNEL);
|
||||
|
||||
if (!wdev->current_bss ||
|
||||
!ether_addr_equal(wdev->current_bss->pub.bssid, bssid))
|
||||
return;
|
||||
|
||||
__cfg80211_disconnected(wdev->netdev, NULL, 0, reason_code, from_ap);
|
||||
cfg80211_sme_deauth(wdev);
|
||||
}
|
||||
|
||||
static void cfg80211_process_disassoc(struct wireless_dev *wdev,
|
||||
const u8 *buf, size_t len)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
|
||||
const u8 *bssid = mgmt->bssid;
|
||||
u16 reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
|
||||
bool from_ap = !ether_addr_equal(mgmt->sa, wdev->netdev->dev_addr);
|
||||
|
||||
nl80211_send_disassoc(rdev, wdev->netdev, buf, len, GFP_KERNEL);
|
||||
|
||||
if (WARN_ON(!wdev->current_bss ||
|
||||
!ether_addr_equal(wdev->current_bss->pub.bssid, bssid)))
|
||||
return;
|
||||
|
||||
__cfg80211_disconnected(wdev->netdev, NULL, 0, reason_code, from_ap);
|
||||
cfg80211_sme_disassoc(wdev);
|
||||
}
|
||||
|
||||
void cfg80211_rx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct ieee80211_mgmt *mgmt = (void *)buf;
|
||||
|
||||
ASSERT_WDEV_LOCK(wdev);
|
||||
|
||||
trace_cfg80211_rx_mlme_mgmt(dev, buf, len);
|
||||
|
||||
if (WARN_ON(len < 2))
|
||||
return;
|
||||
|
||||
if (ieee80211_is_auth(mgmt->frame_control))
|
||||
cfg80211_process_auth(wdev, buf, len);
|
||||
else if (ieee80211_is_deauth(mgmt->frame_control))
|
||||
cfg80211_process_deauth(wdev, buf, len);
|
||||
else if (ieee80211_is_disassoc(mgmt->frame_control))
|
||||
cfg80211_process_disassoc(wdev, buf, len);
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_rx_mlme_mgmt);
|
||||
|
||||
void cfg80211_auth_timeout(struct net_device *dev, const u8 *addr)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct wiphy *wiphy = wdev->wiphy;
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
||||
|
||||
trace_cfg80211_send_auth_timeout(dev, addr);
|
||||
|
||||
nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL);
|
||||
cfg80211_sme_auth_timeout(wdev);
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_auth_timeout);
|
||||
|
||||
void cfg80211_assoc_timeout(struct net_device *dev, struct cfg80211_bss *bss)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct wiphy *wiphy = wdev->wiphy;
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
||||
|
||||
trace_cfg80211_send_assoc_timeout(dev, bss->bssid);
|
||||
|
||||
nl80211_send_assoc_timeout(rdev, dev, bss->bssid, GFP_KERNEL);
|
||||
cfg80211_sme_assoc_timeout(wdev);
|
||||
|
||||
cfg80211_unhold_bss(bss_from_pub(bss));
|
||||
cfg80211_put_bss(wiphy, bss);
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_assoc_timeout);
|
||||
|
||||
void cfg80211_tx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct ieee80211_mgmt *mgmt = (void *)buf;
|
||||
|
||||
ASSERT_WDEV_LOCK(wdev);
|
||||
|
||||
trace_cfg80211_tx_mlme_mgmt(dev, buf, len);
|
||||
|
||||
if (WARN_ON(len < 2))
|
||||
return;
|
||||
|
||||
if (ieee80211_is_deauth(mgmt->frame_control))
|
||||
cfg80211_process_deauth(wdev, buf, len);
|
||||
else
|
||||
cfg80211_process_disassoc(wdev, buf, len);
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_tx_mlme_mgmt);
|
||||
|
||||
void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr,
|
||||
enum nl80211_key_type key_type, int key_id,
|
||||
const u8 *tsc, gfp_t gfp)
|
||||
{
|
||||
struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
||||
#ifdef CONFIG_CFG80211_WEXT
|
||||
union iwreq_data wrqu;
|
||||
char *buf = kmalloc(128, gfp);
|
||||
|
||||
if (buf) {
|
||||
sprintf(buf, "MLME-MICHAELMICFAILURE.indication("
|
||||
"keyid=%d %scast addr=%pM)", key_id,
|
||||
key_type == NL80211_KEYTYPE_GROUP ? "broad" : "uni",
|
||||
addr);
|
||||
memset(&wrqu, 0, sizeof(wrqu));
|
||||
wrqu.data.length = strlen(buf);
|
||||
wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf);
|
||||
kfree(buf);
|
||||
}
|
||||
#endif
|
||||
|
||||
trace_cfg80211_michael_mic_failure(dev, addr, key_type, key_id, tsc);
|
||||
nl80211_michael_mic_failure(rdev, dev, addr, key_type, key_id, tsc, gfp);
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_michael_mic_failure);
|
||||
|
||||
/* some MLME handling for userspace SME */
|
||||
int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct ieee80211_channel *chan,
|
||||
enum nl80211_auth_type auth_type,
|
||||
const u8 *bssid,
|
||||
const u8 *ssid, int ssid_len,
|
||||
const u8 *ie, int ie_len,
|
||||
const u8 *key, int key_len, int key_idx,
|
||||
const u8 *sae_data, int sae_data_len)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct cfg80211_auth_request req = {
|
||||
.ie = ie,
|
||||
.ie_len = ie_len,
|
||||
.sae_data = sae_data,
|
||||
.sae_data_len = sae_data_len,
|
||||
.auth_type = auth_type,
|
||||
.key = key,
|
||||
.key_len = key_len,
|
||||
.key_idx = key_idx,
|
||||
};
|
||||
int err;
|
||||
|
||||
ASSERT_WDEV_LOCK(wdev);
|
||||
|
||||
if (auth_type == NL80211_AUTHTYPE_SHARED_KEY)
|
||||
if (!key || !key_len || key_idx < 0 || key_idx > 4)
|
||||
return -EINVAL;
|
||||
|
||||
if (wdev->current_bss &&
|
||||
ether_addr_equal(bssid, wdev->current_bss->pub.bssid))
|
||||
return -EALREADY;
|
||||
|
||||
req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
|
||||
WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
|
||||
if (!req.bss)
|
||||
return -ENOENT;
|
||||
|
||||
err = rdev_auth(rdev, dev, &req);
|
||||
|
||||
cfg80211_put_bss(&rdev->wiphy, req.bss);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Do a logical ht_capa &= ht_capa_mask. */
|
||||
void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa,
|
||||
const struct ieee80211_ht_cap *ht_capa_mask)
|
||||
{
|
||||
int i;
|
||||
u8 *p1, *p2;
|
||||
if (!ht_capa_mask) {
|
||||
memset(ht_capa, 0, sizeof(*ht_capa));
|
||||
return;
|
||||
}
|
||||
|
||||
p1 = (u8*)(ht_capa);
|
||||
p2 = (u8*)(ht_capa_mask);
|
||||
for (i = 0; i<sizeof(*ht_capa); i++)
|
||||
p1[i] &= p2[i];
|
||||
}
|
||||
|
||||
/* Do a logical ht_capa &= ht_capa_mask. */
|
||||
void cfg80211_oper_and_vht_capa(struct ieee80211_vht_cap *vht_capa,
|
||||
const struct ieee80211_vht_cap *vht_capa_mask)
|
||||
{
|
||||
int i;
|
||||
u8 *p1, *p2;
|
||||
if (!vht_capa_mask) {
|
||||
memset(vht_capa, 0, sizeof(*vht_capa));
|
||||
return;
|
||||
}
|
||||
|
||||
p1 = (u8*)(vht_capa);
|
||||
p2 = (u8*)(vht_capa_mask);
|
||||
for (i = 0; i < sizeof(*vht_capa); i++)
|
||||
p1[i] &= p2[i];
|
||||
}
|
||||
|
||||
int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct ieee80211_channel *chan,
|
||||
const u8 *bssid,
|
||||
const u8 *ssid, int ssid_len,
|
||||
struct cfg80211_assoc_request *req)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
int err;
|
||||
|
||||
ASSERT_WDEV_LOCK(wdev);
|
||||
|
||||
if (wdev->current_bss &&
|
||||
(!req->prev_bssid || !ether_addr_equal(wdev->current_bss->pub.bssid,
|
||||
req->prev_bssid)))
|
||||
return -EALREADY;
|
||||
|
||||
cfg80211_oper_and_ht_capa(&req->ht_capa_mask,
|
||||
rdev->wiphy.ht_capa_mod_mask);
|
||||
cfg80211_oper_and_vht_capa(&req->vht_capa_mask,
|
||||
rdev->wiphy.vht_capa_mod_mask);
|
||||
|
||||
req->bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
|
||||
WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
|
||||
if (!req->bss)
|
||||
return -ENOENT;
|
||||
|
||||
err = rdev_assoc(rdev, dev, req);
|
||||
if (!err)
|
||||
cfg80211_hold_bss(bss_from_pub(req->bss));
|
||||
else
|
||||
cfg80211_put_bss(&rdev->wiphy, req->bss);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, const u8 *bssid,
|
||||
const u8 *ie, int ie_len, u16 reason,
|
||||
bool local_state_change)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct cfg80211_deauth_request req = {
|
||||
.bssid = bssid,
|
||||
.reason_code = reason,
|
||||
.ie = ie,
|
||||
.ie_len = ie_len,
|
||||
.local_state_change = local_state_change,
|
||||
};
|
||||
|
||||
ASSERT_WDEV_LOCK(wdev);
|
||||
|
||||
if (local_state_change &&
|
||||
(!wdev->current_bss ||
|
||||
!ether_addr_equal(wdev->current_bss->pub.bssid, bssid)))
|
||||
return 0;
|
||||
|
||||
return rdev_deauth(rdev, dev, &req);
|
||||
}
|
||||
|
||||
int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, const u8 *bssid,
|
||||
const u8 *ie, int ie_len, u16 reason,
|
||||
bool local_state_change)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct cfg80211_disassoc_request req = {
|
||||
.reason_code = reason,
|
||||
.local_state_change = local_state_change,
|
||||
.ie = ie,
|
||||
.ie_len = ie_len,
|
||||
};
|
||||
int err;
|
||||
|
||||
ASSERT_WDEV_LOCK(wdev);
|
||||
|
||||
if (!wdev->current_bss)
|
||||
return -ENOTCONN;
|
||||
|
||||
if (ether_addr_equal(wdev->current_bss->pub.bssid, bssid))
|
||||
req.bss = &wdev->current_bss->pub;
|
||||
else
|
||||
return -ENOTCONN;
|
||||
|
||||
err = rdev_disassoc(rdev, dev, &req);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* driver should have reported the disassoc */
|
||||
WARN_ON(wdev->current_bss);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
u8 bssid[ETH_ALEN];
|
||||
|
||||
ASSERT_WDEV_LOCK(wdev);
|
||||
|
||||
if (!rdev->ops->deauth)
|
||||
return;
|
||||
|
||||
if (!wdev->current_bss)
|
||||
return;
|
||||
|
||||
memcpy(bssid, wdev->current_bss->pub.bssid, ETH_ALEN);
|
||||
cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0,
|
||||
WLAN_REASON_DEAUTH_LEAVING, false);
|
||||
}
|
||||
|
||||
struct cfg80211_mgmt_registration {
|
||||
struct list_head list;
|
||||
|
||||
u32 nlportid;
|
||||
|
||||
int match_len;
|
||||
|
||||
__le16 frame_type;
|
||||
|
||||
u8 match[];
|
||||
};
|
||||
|
||||
int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
|
||||
u16 frame_type, const u8 *match_data,
|
||||
int match_len)
|
||||
{
|
||||
struct wiphy *wiphy = wdev->wiphy;
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
||||
struct cfg80211_mgmt_registration *reg, *nreg;
|
||||
int err = 0;
|
||||
u16 mgmt_type;
|
||||
|
||||
if (!wdev->wiphy->mgmt_stypes)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if ((frame_type & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT)
|
||||
return -EINVAL;
|
||||
|
||||
if (frame_type & ~(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE))
|
||||
return -EINVAL;
|
||||
|
||||
mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4;
|
||||
if (!(wdev->wiphy->mgmt_stypes[wdev->iftype].rx & BIT(mgmt_type)))
|
||||
return -EINVAL;
|
||||
|
||||
nreg = kzalloc(sizeof(*reg) + match_len, GFP_KERNEL);
|
||||
if (!nreg)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_bh(&wdev->mgmt_registrations_lock);
|
||||
|
||||
list_for_each_entry(reg, &wdev->mgmt_registrations, list) {
|
||||
int mlen = min(match_len, reg->match_len);
|
||||
|
||||
if (frame_type != le16_to_cpu(reg->frame_type))
|
||||
continue;
|
||||
|
||||
if (memcmp(reg->match, match_data, mlen) == 0) {
|
||||
err = -EALREADY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (err) {
|
||||
kfree(nreg);
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(nreg->match, match_data, match_len);
|
||||
nreg->match_len = match_len;
|
||||
nreg->nlportid = snd_portid;
|
||||
nreg->frame_type = cpu_to_le16(frame_type);
|
||||
list_add(&nreg->list, &wdev->mgmt_registrations);
|
||||
|
||||
if (rdev->ops->mgmt_frame_register)
|
||||
rdev_mgmt_frame_register(rdev, wdev, frame_type, true);
|
||||
|
||||
out:
|
||||
spin_unlock_bh(&wdev->mgmt_registrations_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid)
|
||||
{
|
||||
struct wiphy *wiphy = wdev->wiphy;
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
||||
struct cfg80211_mgmt_registration *reg, *tmp;
|
||||
|
||||
spin_lock_bh(&wdev->mgmt_registrations_lock);
|
||||
|
||||
list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) {
|
||||
if (reg->nlportid != nlportid)
|
||||
continue;
|
||||
|
||||
if (rdev->ops->mgmt_frame_register) {
|
||||
u16 frame_type = le16_to_cpu(reg->frame_type);
|
||||
|
||||
rdev_mgmt_frame_register(rdev, wdev,
|
||||
frame_type, false);
|
||||
}
|
||||
|
||||
list_del(®->list);
|
||||
kfree(reg);
|
||||
}
|
||||
|
||||
spin_unlock_bh(&wdev->mgmt_registrations_lock);
|
||||
|
||||
if (nlportid && rdev->crit_proto_nlportid == nlportid) {
|
||||
rdev->crit_proto_nlportid = 0;
|
||||
rdev_crit_proto_stop(rdev, wdev);
|
||||
}
|
||||
|
||||
if (nlportid == wdev->ap_unexpected_nlportid)
|
||||
wdev->ap_unexpected_nlportid = 0;
|
||||
}
|
||||
|
||||
void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev)
|
||||
{
|
||||
struct cfg80211_mgmt_registration *reg, *tmp;
|
||||
|
||||
spin_lock_bh(&wdev->mgmt_registrations_lock);
|
||||
|
||||
list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) {
|
||||
list_del(®->list);
|
||||
kfree(reg);
|
||||
}
|
||||
|
||||
spin_unlock_bh(&wdev->mgmt_registrations_lock);
|
||||
}
|
||||
|
||||
int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev,
|
||||
struct cfg80211_mgmt_tx_params *params, u64 *cookie)
|
||||
{
|
||||
const struct ieee80211_mgmt *mgmt;
|
||||
u16 stype;
|
||||
|
||||
if (!wdev->wiphy->mgmt_stypes)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!rdev->ops->mgmt_tx)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (params->len < 24 + 1)
|
||||
return -EINVAL;
|
||||
|
||||
mgmt = (const struct ieee80211_mgmt *)params->buf;
|
||||
|
||||
if (!ieee80211_is_mgmt(mgmt->frame_control))
|
||||
return -EINVAL;
|
||||
|
||||
stype = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE;
|
||||
if (!(wdev->wiphy->mgmt_stypes[wdev->iftype].tx & BIT(stype >> 4)))
|
||||
return -EINVAL;
|
||||
|
||||
if (ieee80211_is_action(mgmt->frame_control) &&
|
||||
mgmt->u.action.category != WLAN_CATEGORY_PUBLIC) {
|
||||
int err = 0;
|
||||
|
||||
wdev_lock(wdev);
|
||||
|
||||
switch (wdev->iftype) {
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
case NL80211_IFTYPE_STATION:
|
||||
case NL80211_IFTYPE_P2P_CLIENT:
|
||||
if (!wdev->current_bss) {
|
||||
err = -ENOTCONN;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ether_addr_equal(wdev->current_bss->pub.bssid,
|
||||
mgmt->bssid)) {
|
||||
err = -ENOTCONN;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* check for IBSS DA must be done by driver as
|
||||
* cfg80211 doesn't track the stations
|
||||
*/
|
||||
if (wdev->iftype == NL80211_IFTYPE_ADHOC)
|
||||
break;
|
||||
|
||||
/* for station, check that DA is the AP */
|
||||
if (!ether_addr_equal(wdev->current_bss->pub.bssid,
|
||||
mgmt->da)) {
|
||||
err = -ENOTCONN;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case NL80211_IFTYPE_AP:
|
||||
case NL80211_IFTYPE_P2P_GO:
|
||||
case NL80211_IFTYPE_AP_VLAN:
|
||||
if (!ether_addr_equal(mgmt->bssid, wdev_address(wdev)))
|
||||
err = -EINVAL;
|
||||
break;
|
||||
case NL80211_IFTYPE_MESH_POINT:
|
||||
if (!ether_addr_equal(mgmt->sa, mgmt->bssid)) {
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* check for mesh DA must be done by driver as
|
||||
* cfg80211 doesn't track the stations
|
||||
*/
|
||||
break;
|
||||
case NL80211_IFTYPE_P2P_DEVICE:
|
||||
/*
|
||||
* fall through, P2P device only supports
|
||||
* public action frames
|
||||
*/
|
||||
default:
|
||||
err = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
wdev_unlock(wdev);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!ether_addr_equal(mgmt->sa, wdev_address(wdev)))
|
||||
return -EINVAL;
|
||||
|
||||
/* Transmit the Action frame as requested by user space */
|
||||
return rdev_mgmt_tx(rdev, wdev, params, cookie);
|
||||
}
|
||||
|
||||
bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm,
|
||||
const u8 *buf, size_t len, u32 flags)
|
||||
{
|
||||
struct wiphy *wiphy = wdev->wiphy;
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
||||
struct cfg80211_mgmt_registration *reg;
|
||||
const struct ieee80211_txrx_stypes *stypes =
|
||||
&wiphy->mgmt_stypes[wdev->iftype];
|
||||
struct ieee80211_mgmt *mgmt = (void *)buf;
|
||||
const u8 *data;
|
||||
int data_len;
|
||||
bool result = false;
|
||||
__le16 ftype = mgmt->frame_control &
|
||||
cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE);
|
||||
u16 stype;
|
||||
|
||||
trace_cfg80211_rx_mgmt(wdev, freq, sig_mbm);
|
||||
stype = (le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE) >> 4;
|
||||
|
||||
if (!(stypes->rx & BIT(stype))) {
|
||||
trace_cfg80211_return_bool(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
data = buf + ieee80211_hdrlen(mgmt->frame_control);
|
||||
data_len = len - ieee80211_hdrlen(mgmt->frame_control);
|
||||
|
||||
spin_lock_bh(&wdev->mgmt_registrations_lock);
|
||||
|
||||
list_for_each_entry(reg, &wdev->mgmt_registrations, list) {
|
||||
if (reg->frame_type != ftype)
|
||||
continue;
|
||||
|
||||
if (reg->match_len > data_len)
|
||||
continue;
|
||||
|
||||
if (memcmp(reg->match, data, reg->match_len))
|
||||
continue;
|
||||
|
||||
/* found match! */
|
||||
|
||||
/* Indicate the received Action frame to user space */
|
||||
if (nl80211_send_mgmt(rdev, wdev, reg->nlportid,
|
||||
freq, sig_mbm,
|
||||
buf, len, flags, GFP_ATOMIC))
|
||||
continue;
|
||||
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
|
||||
spin_unlock_bh(&wdev->mgmt_registrations_lock);
|
||||
|
||||
trace_cfg80211_return_bool(result);
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_rx_mgmt);
|
||||
|
||||
void cfg80211_dfs_channels_update_work(struct work_struct *work)
|
||||
{
|
||||
struct delayed_work *delayed_work;
|
||||
struct cfg80211_registered_device *rdev;
|
||||
struct cfg80211_chan_def chandef;
|
||||
struct ieee80211_supported_band *sband;
|
||||
struct ieee80211_channel *c;
|
||||
struct wiphy *wiphy;
|
||||
bool check_again = false;
|
||||
unsigned long timeout, next_time = 0;
|
||||
int bandid, i;
|
||||
|
||||
delayed_work = container_of(work, struct delayed_work, work);
|
||||
rdev = container_of(delayed_work, struct cfg80211_registered_device,
|
||||
dfs_update_channels_wk);
|
||||
wiphy = &rdev->wiphy;
|
||||
|
||||
rtnl_lock();
|
||||
for (bandid = 0; bandid < IEEE80211_NUM_BANDS; bandid++) {
|
||||
sband = wiphy->bands[bandid];
|
||||
if (!sband)
|
||||
continue;
|
||||
|
||||
for (i = 0; i < sband->n_channels; i++) {
|
||||
c = &sband->channels[i];
|
||||
|
||||
if (c->dfs_state != NL80211_DFS_UNAVAILABLE)
|
||||
continue;
|
||||
|
||||
timeout = c->dfs_state_entered + msecs_to_jiffies(
|
||||
IEEE80211_DFS_MIN_NOP_TIME_MS);
|
||||
|
||||
if (time_after_eq(jiffies, timeout)) {
|
||||
c->dfs_state = NL80211_DFS_USABLE;
|
||||
c->dfs_state_entered = jiffies;
|
||||
|
||||
cfg80211_chandef_create(&chandef, c,
|
||||
NL80211_CHAN_NO_HT);
|
||||
|
||||
nl80211_radar_notify(rdev, &chandef,
|
||||
NL80211_RADAR_NOP_FINISHED,
|
||||
NULL, GFP_ATOMIC);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!check_again)
|
||||
next_time = timeout - jiffies;
|
||||
else
|
||||
next_time = min(next_time, timeout - jiffies);
|
||||
check_again = true;
|
||||
}
|
||||
}
|
||||
rtnl_unlock();
|
||||
|
||||
/* reschedule if there are other channels waiting to be cleared again */
|
||||
if (check_again)
|
||||
queue_delayed_work(cfg80211_wq, &rdev->dfs_update_channels_wk,
|
||||
next_time);
|
||||
}
|
||||
|
||||
|
||||
void cfg80211_radar_event(struct wiphy *wiphy,
|
||||
struct cfg80211_chan_def *chandef,
|
||||
gfp_t gfp)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
||||
unsigned long timeout;
|
||||
|
||||
trace_cfg80211_radar_event(wiphy, chandef);
|
||||
|
||||
/* only set the chandef supplied channel to unavailable, in
|
||||
* case the radar is detected on only one of multiple channels
|
||||
* spanned by the chandef.
|
||||
*/
|
||||
cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_UNAVAILABLE);
|
||||
|
||||
timeout = msecs_to_jiffies(IEEE80211_DFS_MIN_NOP_TIME_MS);
|
||||
queue_delayed_work(cfg80211_wq, &rdev->dfs_update_channels_wk,
|
||||
timeout);
|
||||
|
||||
nl80211_radar_notify(rdev, chandef, NL80211_RADAR_DETECTED, NULL, gfp);
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_radar_event);
|
||||
|
||||
void cfg80211_cac_event(struct net_device *netdev,
|
||||
const struct cfg80211_chan_def *chandef,
|
||||
enum nl80211_radar_event event, gfp_t gfp)
|
||||
{
|
||||
struct wireless_dev *wdev = netdev->ieee80211_ptr;
|
||||
struct wiphy *wiphy = wdev->wiphy;
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
||||
unsigned long timeout;
|
||||
|
||||
trace_cfg80211_cac_event(netdev, event);
|
||||
|
||||
if (WARN_ON(!wdev->cac_started))
|
||||
return;
|
||||
|
||||
if (WARN_ON(!wdev->chandef.chan))
|
||||
return;
|
||||
|
||||
switch (event) {
|
||||
case NL80211_RADAR_CAC_FINISHED:
|
||||
timeout = wdev->cac_start_time +
|
||||
msecs_to_jiffies(wdev->cac_time_ms);
|
||||
WARN_ON(!time_after_eq(jiffies, timeout));
|
||||
cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE);
|
||||
break;
|
||||
case NL80211_RADAR_CAC_ABORTED:
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
return;
|
||||
}
|
||||
wdev->cac_started = false;
|
||||
|
||||
nl80211_radar_notify(rdev, chandef, event, netdev, gfp);
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_cac_event);
|
12140
net/wireless/nl80211.c
Normal file
12140
net/wireless/nl80211.c
Normal file
File diff suppressed because it is too large
Load diff
83
net/wireless/nl80211.h
Normal file
83
net/wireless/nl80211.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
#ifndef __NET_WIRELESS_NL80211_H
|
||||
#define __NET_WIRELESS_NL80211_H
|
||||
|
||||
#include "core.h"
|
||||
|
||||
int nl80211_init(void);
|
||||
void nl80211_exit(void);
|
||||
void nl80211_notify_wiphy(struct cfg80211_registered_device *rdev,
|
||||
enum nl80211_commands cmd);
|
||||
void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev);
|
||||
struct sk_buff *nl80211_build_scan_msg(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev, bool aborted);
|
||||
void nl80211_send_scan_result(struct cfg80211_registered_device *rdev,
|
||||
struct sk_buff *msg);
|
||||
void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *netdev, u32 cmd);
|
||||
void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *netdev);
|
||||
void nl80211_send_reg_change_event(struct regulatory_request *request);
|
||||
void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *netdev,
|
||||
const u8 *buf, size_t len, gfp_t gfp);
|
||||
void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *netdev,
|
||||
const u8 *buf, size_t len, gfp_t gfp,
|
||||
int uapsd_queues);
|
||||
void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *netdev,
|
||||
const u8 *buf, size_t len, gfp_t gfp);
|
||||
void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *netdev,
|
||||
const u8 *buf, size_t len, gfp_t gfp);
|
||||
void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *netdev,
|
||||
const u8 *addr, gfp_t gfp);
|
||||
void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *netdev,
|
||||
const u8 *addr, gfp_t gfp);
|
||||
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);
|
||||
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);
|
||||
void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *netdev, u16 reason,
|
||||
const u8 *ie, size_t ie_len, bool from_ap);
|
||||
|
||||
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, const u8 *tsc, gfp_t gfp);
|
||||
|
||||
void
|
||||
nl80211_send_beacon_hint_event(struct wiphy *wiphy,
|
||||
struct ieee80211_channel *channel_before,
|
||||
struct ieee80211_channel *channel_after);
|
||||
|
||||
void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *netdev, const u8 *bssid,
|
||||
gfp_t gfp);
|
||||
|
||||
int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev, u32 nlpid,
|
||||
int freq, int sig_dbm,
|
||||
const u8 *buf, size_t len, u32 flags, gfp_t gfp);
|
||||
|
||||
void
|
||||
nl80211_radar_notify(struct cfg80211_registered_device *rdev,
|
||||
const struct cfg80211_chan_def *chandef,
|
||||
enum nl80211_radar_event event,
|
||||
struct net_device *netdev, gfp_t gfp);
|
||||
|
||||
void nl80211_send_ap_stopped(struct wireless_dev *wdev);
|
||||
|
||||
void cfg80211_rdev_free_coalesce(struct cfg80211_registered_device *rdev);
|
||||
|
||||
#endif /* __NET_WIRELESS_NL80211_H */
|
369
net/wireless/radiotap.c
Normal file
369
net/wireless/radiotap.c
Normal file
|
@ -0,0 +1,369 @@
|
|||
/*
|
||||
* Radiotap parser
|
||||
*
|
||||
* Copyright 2007 Andy Green <andy@warmcat.com>
|
||||
* Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Alternatively, this software may be distributed under the terms of BSD
|
||||
* license.
|
||||
*
|
||||
* See COPYING for more details.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/export.h>
|
||||
#include <net/cfg80211.h>
|
||||
#include <net/ieee80211_radiotap.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
/* function prototypes and related defs are in include/net/cfg80211.h */
|
||||
|
||||
static const struct radiotap_align_size rtap_namespace_sizes[] = {
|
||||
[IEEE80211_RADIOTAP_TSFT] = { .align = 8, .size = 8, },
|
||||
[IEEE80211_RADIOTAP_FLAGS] = { .align = 1, .size = 1, },
|
||||
[IEEE80211_RADIOTAP_RATE] = { .align = 1, .size = 1, },
|
||||
[IEEE80211_RADIOTAP_CHANNEL] = { .align = 2, .size = 4, },
|
||||
[IEEE80211_RADIOTAP_FHSS] = { .align = 2, .size = 2, },
|
||||
[IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = { .align = 1, .size = 1, },
|
||||
[IEEE80211_RADIOTAP_DBM_ANTNOISE] = { .align = 1, .size = 1, },
|
||||
[IEEE80211_RADIOTAP_LOCK_QUALITY] = { .align = 2, .size = 2, },
|
||||
[IEEE80211_RADIOTAP_TX_ATTENUATION] = { .align = 2, .size = 2, },
|
||||
[IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = { .align = 2, .size = 2, },
|
||||
[IEEE80211_RADIOTAP_DBM_TX_POWER] = { .align = 1, .size = 1, },
|
||||
[IEEE80211_RADIOTAP_ANTENNA] = { .align = 1, .size = 1, },
|
||||
[IEEE80211_RADIOTAP_DB_ANTSIGNAL] = { .align = 1, .size = 1, },
|
||||
[IEEE80211_RADIOTAP_DB_ANTNOISE] = { .align = 1, .size = 1, },
|
||||
[IEEE80211_RADIOTAP_RX_FLAGS] = { .align = 2, .size = 2, },
|
||||
[IEEE80211_RADIOTAP_TX_FLAGS] = { .align = 2, .size = 2, },
|
||||
[IEEE80211_RADIOTAP_RTS_RETRIES] = { .align = 1, .size = 1, },
|
||||
[IEEE80211_RADIOTAP_DATA_RETRIES] = { .align = 1, .size = 1, },
|
||||
[IEEE80211_RADIOTAP_MCS] = { .align = 1, .size = 3, },
|
||||
[IEEE80211_RADIOTAP_AMPDU_STATUS] = { .align = 4, .size = 8, },
|
||||
/*
|
||||
* add more here as they are defined in radiotap.h
|
||||
*/
|
||||
};
|
||||
|
||||
static const struct ieee80211_radiotap_namespace radiotap_ns = {
|
||||
.n_bits = ARRAY_SIZE(rtap_namespace_sizes),
|
||||
.align_size = rtap_namespace_sizes,
|
||||
};
|
||||
|
||||
/**
|
||||
* ieee80211_radiotap_iterator_init - radiotap parser iterator initialization
|
||||
* @iterator: radiotap_iterator to initialize
|
||||
* @radiotap_header: radiotap header to parse
|
||||
* @max_length: total length we can parse into (eg, whole packet length)
|
||||
*
|
||||
* Returns: 0 or a negative error code if there is a problem.
|
||||
*
|
||||
* This function initializes an opaque iterator struct which can then
|
||||
* be passed to ieee80211_radiotap_iterator_next() to visit every radiotap
|
||||
* argument which is present in the header. It knows about extended
|
||||
* present headers and handles them.
|
||||
*
|
||||
* How to use:
|
||||
* call __ieee80211_radiotap_iterator_init() to init a semi-opaque iterator
|
||||
* struct ieee80211_radiotap_iterator (no need to init the struct beforehand)
|
||||
* checking for a good 0 return code. Then loop calling
|
||||
* __ieee80211_radiotap_iterator_next()... it returns either 0,
|
||||
* -ENOENT if there are no more args to parse, or -EINVAL if there is a problem.
|
||||
* The iterator's @this_arg member points to the start of the argument
|
||||
* associated with the current argument index that is present, which can be
|
||||
* found in the iterator's @this_arg_index member. This arg index corresponds
|
||||
* to the IEEE80211_RADIOTAP_... defines.
|
||||
*
|
||||
* Radiotap header length:
|
||||
* You can find the CPU-endian total radiotap header length in
|
||||
* iterator->max_length after executing ieee80211_radiotap_iterator_init()
|
||||
* successfully.
|
||||
*
|
||||
* Alignment Gotcha:
|
||||
* You must take care when dereferencing iterator.this_arg
|
||||
* for multibyte types... the pointer is not aligned. Use
|
||||
* get_unaligned((type *)iterator.this_arg) to dereference
|
||||
* iterator.this_arg for type "type" safely on all arches.
|
||||
*
|
||||
* Example code:
|
||||
* See Documentation/networking/radiotap-headers.txt
|
||||
*/
|
||||
|
||||
int ieee80211_radiotap_iterator_init(
|
||||
struct ieee80211_radiotap_iterator *iterator,
|
||||
struct ieee80211_radiotap_header *radiotap_header,
|
||||
int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns)
|
||||
{
|
||||
/* check the radiotap header can actually be present */
|
||||
if (max_length < sizeof(struct ieee80211_radiotap_header))
|
||||
return -EINVAL;
|
||||
|
||||
/* Linux only supports version 0 radiotap format */
|
||||
if (radiotap_header->it_version)
|
||||
return -EINVAL;
|
||||
|
||||
/* sanity check for allowed length and radiotap length field */
|
||||
if (max_length < get_unaligned_le16(&radiotap_header->it_len))
|
||||
return -EINVAL;
|
||||
|
||||
iterator->_rtheader = radiotap_header;
|
||||
iterator->_max_length = get_unaligned_le16(&radiotap_header->it_len);
|
||||
iterator->_arg_index = 0;
|
||||
iterator->_bitmap_shifter = get_unaligned_le32(&radiotap_header->it_present);
|
||||
iterator->_arg = (uint8_t *)radiotap_header + sizeof(*radiotap_header);
|
||||
iterator->_reset_on_ext = 0;
|
||||
iterator->_next_bitmap = &radiotap_header->it_present;
|
||||
iterator->_next_bitmap++;
|
||||
iterator->_vns = vns;
|
||||
iterator->current_namespace = &radiotap_ns;
|
||||
iterator->is_radiotap_ns = 1;
|
||||
|
||||
/* find payload start allowing for extended bitmap(s) */
|
||||
|
||||
if (iterator->_bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT)) {
|
||||
if ((unsigned long)iterator->_arg -
|
||||
(unsigned long)iterator->_rtheader + sizeof(uint32_t) >
|
||||
(unsigned long)iterator->_max_length)
|
||||
return -EINVAL;
|
||||
while (get_unaligned_le32(iterator->_arg) &
|
||||
(1 << IEEE80211_RADIOTAP_EXT)) {
|
||||
iterator->_arg += sizeof(uint32_t);
|
||||
|
||||
/*
|
||||
* check for insanity where the present bitmaps
|
||||
* keep claiming to extend up to or even beyond the
|
||||
* stated radiotap header length
|
||||
*/
|
||||
|
||||
if ((unsigned long)iterator->_arg -
|
||||
(unsigned long)iterator->_rtheader +
|
||||
sizeof(uint32_t) >
|
||||
(unsigned long)iterator->_max_length)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
iterator->_arg += sizeof(uint32_t);
|
||||
|
||||
/*
|
||||
* no need to check again for blowing past stated radiotap
|
||||
* header length, because ieee80211_radiotap_iterator_next
|
||||
* checks it before it is dereferenced
|
||||
*/
|
||||
}
|
||||
|
||||
iterator->this_arg = iterator->_arg;
|
||||
|
||||
/* we are all initialized happily */
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_radiotap_iterator_init);
|
||||
|
||||
static void find_ns(struct ieee80211_radiotap_iterator *iterator,
|
||||
uint32_t oui, uint8_t subns)
|
||||
{
|
||||
int i;
|
||||
|
||||
iterator->current_namespace = NULL;
|
||||
|
||||
if (!iterator->_vns)
|
||||
return;
|
||||
|
||||
for (i = 0; i < iterator->_vns->n_ns; i++) {
|
||||
if (iterator->_vns->ns[i].oui != oui)
|
||||
continue;
|
||||
if (iterator->_vns->ns[i].subns != subns)
|
||||
continue;
|
||||
|
||||
iterator->current_namespace = &iterator->_vns->ns[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* ieee80211_radiotap_iterator_next - return next radiotap parser iterator arg
|
||||
* @iterator: radiotap_iterator to move to next arg (if any)
|
||||
*
|
||||
* Returns: 0 if there is an argument to handle,
|
||||
* -ENOENT if there are no more args or -EINVAL
|
||||
* if there is something else wrong.
|
||||
*
|
||||
* This function provides the next radiotap arg index (IEEE80211_RADIOTAP_*)
|
||||
* in @this_arg_index and sets @this_arg to point to the
|
||||
* payload for the field. It takes care of alignment handling and extended
|
||||
* present fields. @this_arg can be changed by the caller (eg,
|
||||
* incremented to move inside a compound argument like
|
||||
* IEEE80211_RADIOTAP_CHANNEL). The args pointed to are in
|
||||
* little-endian format whatever the endianess of your CPU.
|
||||
*
|
||||
* Alignment Gotcha:
|
||||
* You must take care when dereferencing iterator.this_arg
|
||||
* for multibyte types... the pointer is not aligned. Use
|
||||
* get_unaligned((type *)iterator.this_arg) to dereference
|
||||
* iterator.this_arg for type "type" safely on all arches.
|
||||
*/
|
||||
|
||||
int ieee80211_radiotap_iterator_next(
|
||||
struct ieee80211_radiotap_iterator *iterator)
|
||||
{
|
||||
while (1) {
|
||||
int hit = 0;
|
||||
int pad, align, size, subns;
|
||||
uint32_t oui;
|
||||
|
||||
/* if no more EXT bits, that's it */
|
||||
if ((iterator->_arg_index % 32) == IEEE80211_RADIOTAP_EXT &&
|
||||
!(iterator->_bitmap_shifter & 1))
|
||||
return -ENOENT;
|
||||
|
||||
if (!(iterator->_bitmap_shifter & 1))
|
||||
goto next_entry; /* arg not present */
|
||||
|
||||
/* get alignment/size of data */
|
||||
switch (iterator->_arg_index % 32) {
|
||||
case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE:
|
||||
case IEEE80211_RADIOTAP_EXT:
|
||||
align = 1;
|
||||
size = 0;
|
||||
break;
|
||||
case IEEE80211_RADIOTAP_VENDOR_NAMESPACE:
|
||||
align = 2;
|
||||
size = 6;
|
||||
break;
|
||||
default:
|
||||
if (!iterator->current_namespace ||
|
||||
iterator->_arg_index >= iterator->current_namespace->n_bits) {
|
||||
if (iterator->current_namespace == &radiotap_ns)
|
||||
return -ENOENT;
|
||||
align = 0;
|
||||
} else {
|
||||
align = iterator->current_namespace->align_size[iterator->_arg_index].align;
|
||||
size = iterator->current_namespace->align_size[iterator->_arg_index].size;
|
||||
}
|
||||
if (!align) {
|
||||
/* skip all subsequent data */
|
||||
iterator->_arg = iterator->_next_ns_data;
|
||||
/* give up on this namespace */
|
||||
iterator->current_namespace = NULL;
|
||||
goto next_entry;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* arg is present, account for alignment padding
|
||||
*
|
||||
* Note that these alignments are relative to the start
|
||||
* of the radiotap header. There is no guarantee
|
||||
* that the radiotap header itself is aligned on any
|
||||
* kind of boundary.
|
||||
*
|
||||
* The above is why get_unaligned() is used to dereference
|
||||
* multibyte elements from the radiotap area.
|
||||
*/
|
||||
|
||||
pad = ((unsigned long)iterator->_arg -
|
||||
(unsigned long)iterator->_rtheader) & (align - 1);
|
||||
|
||||
if (pad)
|
||||
iterator->_arg += align - pad;
|
||||
|
||||
if (iterator->_arg_index % 32 == IEEE80211_RADIOTAP_VENDOR_NAMESPACE) {
|
||||
int vnslen;
|
||||
|
||||
if ((unsigned long)iterator->_arg + size -
|
||||
(unsigned long)iterator->_rtheader >
|
||||
(unsigned long)iterator->_max_length)
|
||||
return -EINVAL;
|
||||
|
||||
oui = (*iterator->_arg << 16) |
|
||||
(*(iterator->_arg + 1) << 8) |
|
||||
*(iterator->_arg + 2);
|
||||
subns = *(iterator->_arg + 3);
|
||||
|
||||
find_ns(iterator, oui, subns);
|
||||
|
||||
vnslen = get_unaligned_le16(iterator->_arg + 4);
|
||||
iterator->_next_ns_data = iterator->_arg + size + vnslen;
|
||||
if (!iterator->current_namespace)
|
||||
size += vnslen;
|
||||
}
|
||||
|
||||
/*
|
||||
* this is what we will return to user, but we need to
|
||||
* move on first so next call has something fresh to test
|
||||
*/
|
||||
iterator->this_arg_index = iterator->_arg_index;
|
||||
iterator->this_arg = iterator->_arg;
|
||||
iterator->this_arg_size = size;
|
||||
|
||||
/* internally move on the size of this arg */
|
||||
iterator->_arg += size;
|
||||
|
||||
/*
|
||||
* check for insanity where we are given a bitmap that
|
||||
* claims to have more arg content than the length of the
|
||||
* radiotap section. We will normally end up equalling this
|
||||
* max_length on the last arg, never exceeding it.
|
||||
*/
|
||||
|
||||
if ((unsigned long)iterator->_arg -
|
||||
(unsigned long)iterator->_rtheader >
|
||||
(unsigned long)iterator->_max_length)
|
||||
return -EINVAL;
|
||||
|
||||
/* these special ones are valid in each bitmap word */
|
||||
switch (iterator->_arg_index % 32) {
|
||||
case IEEE80211_RADIOTAP_VENDOR_NAMESPACE:
|
||||
iterator->_reset_on_ext = 1;
|
||||
|
||||
iterator->is_radiotap_ns = 0;
|
||||
/*
|
||||
* If parser didn't register this vendor
|
||||
* namespace with us, allow it to show it
|
||||
* as 'raw. Do do that, set argument index
|
||||
* to vendor namespace.
|
||||
*/
|
||||
iterator->this_arg_index =
|
||||
IEEE80211_RADIOTAP_VENDOR_NAMESPACE;
|
||||
if (!iterator->current_namespace)
|
||||
hit = 1;
|
||||
goto next_entry;
|
||||
case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE:
|
||||
iterator->_reset_on_ext = 1;
|
||||
iterator->current_namespace = &radiotap_ns;
|
||||
iterator->is_radiotap_ns = 1;
|
||||
goto next_entry;
|
||||
case IEEE80211_RADIOTAP_EXT:
|
||||
/*
|
||||
* bit 31 was set, there is more
|
||||
* -- move to next u32 bitmap
|
||||
*/
|
||||
iterator->_bitmap_shifter =
|
||||
get_unaligned_le32(iterator->_next_bitmap);
|
||||
iterator->_next_bitmap++;
|
||||
if (iterator->_reset_on_ext)
|
||||
iterator->_arg_index = 0;
|
||||
else
|
||||
iterator->_arg_index++;
|
||||
iterator->_reset_on_ext = 0;
|
||||
break;
|
||||
default:
|
||||
/* we've got a hit! */
|
||||
hit = 1;
|
||||
next_entry:
|
||||
iterator->_bitmap_shifter >>= 1;
|
||||
iterator->_arg_index++;
|
||||
}
|
||||
|
||||
/* if we found a valid arg earlier, return it now */
|
||||
if (hit)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_radiotap_iterator_next);
|
949
net/wireless/rdev-ops.h
Normal file
949
net/wireless/rdev-ops.h
Normal file
|
@ -0,0 +1,949 @@
|
|||
#ifndef __CFG80211_RDEV_OPS
|
||||
#define __CFG80211_RDEV_OPS
|
||||
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <net/cfg80211.h>
|
||||
#include "core.h"
|
||||
#include "trace.h"
|
||||
|
||||
static inline int rdev_suspend(struct cfg80211_registered_device *rdev,
|
||||
struct cfg80211_wowlan *wowlan)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_suspend(&rdev->wiphy, wowlan);
|
||||
ret = rdev->ops->suspend(&rdev->wiphy, wowlan);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_resume(struct cfg80211_registered_device *rdev)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_resume(&rdev->wiphy);
|
||||
ret = rdev->ops->resume(&rdev->wiphy);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void rdev_set_wakeup(struct cfg80211_registered_device *rdev,
|
||||
bool enabled)
|
||||
{
|
||||
trace_rdev_set_wakeup(&rdev->wiphy, enabled);
|
||||
rdev->ops->set_wakeup(&rdev->wiphy, enabled);
|
||||
trace_rdev_return_void(&rdev->wiphy);
|
||||
}
|
||||
|
||||
static inline struct wireless_dev
|
||||
*rdev_add_virtual_intf(struct cfg80211_registered_device *rdev, char *name,
|
||||
enum nl80211_iftype type, u32 *flags,
|
||||
struct vif_params *params)
|
||||
{
|
||||
struct wireless_dev *ret;
|
||||
trace_rdev_add_virtual_intf(&rdev->wiphy, name, type);
|
||||
ret = rdev->ops->add_virtual_intf(&rdev->wiphy, name, type, flags,
|
||||
params);
|
||||
trace_rdev_return_wdev(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_del_virtual_intf(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_del_virtual_intf(&rdev->wiphy, wdev);
|
||||
ret = rdev->ops->del_virtual_intf(&rdev->wiphy, wdev);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_change_virtual_intf(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, enum nl80211_iftype type,
|
||||
u32 *flags, struct vif_params *params)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_change_virtual_intf(&rdev->wiphy, dev, type);
|
||||
ret = rdev->ops->change_virtual_intf(&rdev->wiphy, dev, type, flags,
|
||||
params);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_add_key(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *netdev, u8 key_index,
|
||||
bool pairwise, const u8 *mac_addr,
|
||||
struct key_params *params)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_add_key(&rdev->wiphy, netdev, key_index, pairwise, mac_addr);
|
||||
ret = rdev->ops->add_key(&rdev->wiphy, netdev, key_index, pairwise,
|
||||
mac_addr, params);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_get_key(struct cfg80211_registered_device *rdev, struct net_device *netdev,
|
||||
u8 key_index, bool pairwise, const u8 *mac_addr, void *cookie,
|
||||
void (*callback)(void *cookie, struct key_params*))
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_get_key(&rdev->wiphy, netdev, key_index, pairwise, mac_addr);
|
||||
ret = rdev->ops->get_key(&rdev->wiphy, netdev, key_index, pairwise,
|
||||
mac_addr, cookie, callback);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_del_key(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *netdev, u8 key_index,
|
||||
bool pairwise, const u8 *mac_addr)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_del_key(&rdev->wiphy, netdev, key_index, pairwise, mac_addr);
|
||||
ret = rdev->ops->del_key(&rdev->wiphy, netdev, key_index, pairwise,
|
||||
mac_addr);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_set_default_key(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *netdev, u8 key_index, bool unicast,
|
||||
bool multicast)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_set_default_key(&rdev->wiphy, netdev, key_index,
|
||||
unicast, multicast);
|
||||
ret = rdev->ops->set_default_key(&rdev->wiphy, netdev, key_index,
|
||||
unicast, multicast);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_set_default_mgmt_key(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *netdev, u8 key_index)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_set_default_mgmt_key(&rdev->wiphy, netdev, key_index);
|
||||
ret = rdev->ops->set_default_mgmt_key(&rdev->wiphy, netdev,
|
||||
key_index);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_start_ap(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct cfg80211_ap_settings *settings)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_start_ap(&rdev->wiphy, dev, settings);
|
||||
ret = rdev->ops->start_ap(&rdev->wiphy, dev, settings);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_change_beacon(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct cfg80211_beacon_data *info)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_change_beacon(&rdev->wiphy, dev, info);
|
||||
ret = rdev->ops->change_beacon(&rdev->wiphy, dev, info);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_stop_ap(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_stop_ap(&rdev->wiphy, dev);
|
||||
ret = rdev->ops->stop_ap(&rdev->wiphy, dev);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_add_station(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, u8 *mac,
|
||||
struct station_parameters *params)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_add_station(&rdev->wiphy, dev, mac, params);
|
||||
ret = rdev->ops->add_station(&rdev->wiphy, dev, mac, params);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_del_station(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, u8 *mac)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_del_station(&rdev->wiphy, dev, mac);
|
||||
ret = rdev->ops->del_station(&rdev->wiphy, dev, mac);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_change_station(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, u8 *mac,
|
||||
struct station_parameters *params)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_change_station(&rdev->wiphy, dev, mac, params);
|
||||
ret = rdev->ops->change_station(&rdev->wiphy, dev, mac, params);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_get_station(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, const u8 *mac,
|
||||
struct station_info *sinfo)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_get_station(&rdev->wiphy, dev, mac);
|
||||
ret = rdev->ops->get_station(&rdev->wiphy, dev, mac, sinfo);
|
||||
trace_rdev_return_int_station_info(&rdev->wiphy, ret, sinfo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_dump_station(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, int idx, u8 *mac,
|
||||
struct station_info *sinfo)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_dump_station(&rdev->wiphy, dev, idx, mac);
|
||||
ret = rdev->ops->dump_station(&rdev->wiphy, dev, idx, mac, sinfo);
|
||||
trace_rdev_return_int_station_info(&rdev->wiphy, ret, sinfo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_add_mpath(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, u8 *dst, u8 *next_hop)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_add_mpath(&rdev->wiphy, dev, dst, next_hop);
|
||||
ret = rdev->ops->add_mpath(&rdev->wiphy, dev, dst, next_hop);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_del_mpath(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, u8 *dst)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_del_mpath(&rdev->wiphy, dev, dst);
|
||||
ret = rdev->ops->del_mpath(&rdev->wiphy, dev, dst);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_change_mpath(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, u8 *dst,
|
||||
u8 *next_hop)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_change_mpath(&rdev->wiphy, dev, dst, next_hop);
|
||||
ret = rdev->ops->change_mpath(&rdev->wiphy, dev, dst, next_hop);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_get_mpath(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, u8 *dst, u8 *next_hop,
|
||||
struct mpath_info *pinfo)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_get_mpath(&rdev->wiphy, dev, dst, next_hop);
|
||||
ret = rdev->ops->get_mpath(&rdev->wiphy, dev, dst, next_hop, pinfo);
|
||||
trace_rdev_return_int_mpath_info(&rdev->wiphy, ret, pinfo);
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static inline int rdev_dump_mpath(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, int idx, u8 *dst,
|
||||
u8 *next_hop, struct mpath_info *pinfo)
|
||||
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_dump_mpath(&rdev->wiphy, dev, idx, dst, next_hop);
|
||||
ret = rdev->ops->dump_mpath(&rdev->wiphy, dev, idx, dst, next_hop,
|
||||
pinfo);
|
||||
trace_rdev_return_int_mpath_info(&rdev->wiphy, ret, pinfo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_get_mesh_config(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, struct mesh_config *conf)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_get_mesh_config(&rdev->wiphy, dev);
|
||||
ret = rdev->ops->get_mesh_config(&rdev->wiphy, dev, conf);
|
||||
trace_rdev_return_int_mesh_config(&rdev->wiphy, ret, conf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_update_mesh_config(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, u32 mask,
|
||||
const struct mesh_config *nconf)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_update_mesh_config(&rdev->wiphy, dev, mask, nconf);
|
||||
ret = rdev->ops->update_mesh_config(&rdev->wiphy, dev, mask, nconf);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_join_mesh(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
const struct mesh_config *conf,
|
||||
const struct mesh_setup *setup)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_join_mesh(&rdev->wiphy, dev, conf, setup);
|
||||
ret = rdev->ops->join_mesh(&rdev->wiphy, dev, conf, setup);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static inline int rdev_leave_mesh(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_leave_mesh(&rdev->wiphy, dev);
|
||||
ret = rdev->ops->leave_mesh(&rdev->wiphy, dev);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_change_bss(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct bss_parameters *params)
|
||||
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_change_bss(&rdev->wiphy, dev, params);
|
||||
ret = rdev->ops->change_bss(&rdev->wiphy, dev, params);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_set_txq_params(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct ieee80211_txq_params *params)
|
||||
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_set_txq_params(&rdev->wiphy, dev, params);
|
||||
ret = rdev->ops->set_txq_params(&rdev->wiphy, dev, params);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_libertas_set_mesh_channel(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct ieee80211_channel *chan)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_libertas_set_mesh_channel(&rdev->wiphy, dev, chan);
|
||||
ret = rdev->ops->libertas_set_mesh_channel(&rdev->wiphy, dev, chan);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_set_monitor_channel(struct cfg80211_registered_device *rdev,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_set_monitor_channel(&rdev->wiphy, chandef);
|
||||
ret = rdev->ops->set_monitor_channel(&rdev->wiphy, chandef);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_scan(struct cfg80211_registered_device *rdev,
|
||||
struct cfg80211_scan_request *request)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_scan(&rdev->wiphy, request);
|
||||
ret = rdev->ops->scan(&rdev->wiphy, request);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_auth(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct cfg80211_auth_request *req)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_auth(&rdev->wiphy, dev, req);
|
||||
ret = rdev->ops->auth(&rdev->wiphy, dev, req);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_assoc(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct cfg80211_assoc_request *req)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_assoc(&rdev->wiphy, dev, req);
|
||||
ret = rdev->ops->assoc(&rdev->wiphy, dev, req);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_deauth(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct cfg80211_deauth_request *req)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_deauth(&rdev->wiphy, dev, req);
|
||||
ret = rdev->ops->deauth(&rdev->wiphy, dev, req);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_disassoc(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct cfg80211_disassoc_request *req)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_disassoc(&rdev->wiphy, dev, req);
|
||||
ret = rdev->ops->disassoc(&rdev->wiphy, dev, req);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_connect(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct cfg80211_connect_params *sme)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_connect(&rdev->wiphy, dev, sme);
|
||||
ret = rdev->ops->connect(&rdev->wiphy, dev, sme);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_disconnect(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, u16 reason_code)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_disconnect(&rdev->wiphy, dev, reason_code);
|
||||
ret = rdev->ops->disconnect(&rdev->wiphy, dev, reason_code);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_join_ibss(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct cfg80211_ibss_params *params)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_join_ibss(&rdev->wiphy, dev, params);
|
||||
ret = rdev->ops->join_ibss(&rdev->wiphy, dev, params);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_leave_ibss(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_leave_ibss(&rdev->wiphy, dev);
|
||||
ret = rdev->ops->leave_ibss(&rdev->wiphy, dev);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_set_wiphy_params(struct cfg80211_registered_device *rdev, u32 changed)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_set_wiphy_params(&rdev->wiphy, changed);
|
||||
ret = rdev->ops->set_wiphy_params(&rdev->wiphy, changed);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_set_tx_power(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev,
|
||||
enum nl80211_tx_power_setting type, int mbm)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_set_tx_power(&rdev->wiphy, wdev, type, mbm);
|
||||
ret = rdev->ops->set_tx_power(&rdev->wiphy, wdev, type, mbm);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_get_tx_power(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev, int *dbm)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_get_tx_power(&rdev->wiphy, wdev);
|
||||
ret = rdev->ops->get_tx_power(&rdev->wiphy, wdev, dbm);
|
||||
trace_rdev_return_int_int(&rdev->wiphy, ret, *dbm);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_set_wds_peer(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, const u8 *addr)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_set_wds_peer(&rdev->wiphy, dev, addr);
|
||||
ret = rdev->ops->set_wds_peer(&rdev->wiphy, dev, addr);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void rdev_rfkill_poll(struct cfg80211_registered_device *rdev)
|
||||
{
|
||||
trace_rdev_rfkill_poll(&rdev->wiphy);
|
||||
rdev->ops->rfkill_poll(&rdev->wiphy);
|
||||
trace_rdev_return_void(&rdev->wiphy);
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_NL80211_TESTMODE
|
||||
static inline int rdev_testmode_cmd(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev,
|
||||
void *data, int len)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_testmode_cmd(&rdev->wiphy, wdev);
|
||||
ret = rdev->ops->testmode_cmd(&rdev->wiphy, wdev, data, len);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_testmode_dump(struct cfg80211_registered_device *rdev,
|
||||
struct sk_buff *skb,
|
||||
struct netlink_callback *cb, void *data,
|
||||
int len)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_testmode_dump(&rdev->wiphy);
|
||||
ret = rdev->ops->testmode_dump(&rdev->wiphy, skb, cb, data, len);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline int
|
||||
rdev_set_bitrate_mask(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, const u8 *peer,
|
||||
const struct cfg80211_bitrate_mask *mask)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_set_bitrate_mask(&rdev->wiphy, dev, peer, mask);
|
||||
ret = rdev->ops->set_bitrate_mask(&rdev->wiphy, dev, peer, mask);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_dump_survey(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *netdev, int idx,
|
||||
struct survey_info *info)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_dump_survey(&rdev->wiphy, netdev, idx);
|
||||
ret = rdev->ops->dump_survey(&rdev->wiphy, netdev, idx, info);
|
||||
if (ret < 0)
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
else
|
||||
trace_rdev_return_int_survey_info(&rdev->wiphy, ret, info);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_set_pmksa(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *netdev,
|
||||
struct cfg80211_pmksa *pmksa)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_set_pmksa(&rdev->wiphy, netdev, pmksa);
|
||||
ret = rdev->ops->set_pmksa(&rdev->wiphy, netdev, pmksa);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_del_pmksa(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *netdev,
|
||||
struct cfg80211_pmksa *pmksa)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_del_pmksa(&rdev->wiphy, netdev, pmksa);
|
||||
ret = rdev->ops->del_pmksa(&rdev->wiphy, netdev, pmksa);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_flush_pmksa(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *netdev)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_flush_pmksa(&rdev->wiphy, netdev);
|
||||
ret = rdev->ops->flush_pmksa(&rdev->wiphy, netdev);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_remain_on_channel(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev,
|
||||
struct ieee80211_channel *chan,
|
||||
unsigned int duration, u64 *cookie)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_remain_on_channel(&rdev->wiphy, wdev, chan, duration);
|
||||
ret = rdev->ops->remain_on_channel(&rdev->wiphy, wdev, chan,
|
||||
duration, cookie);
|
||||
trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_cancel_remain_on_channel(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev, u64 cookie)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_cancel_remain_on_channel(&rdev->wiphy, wdev, cookie);
|
||||
ret = rdev->ops->cancel_remain_on_channel(&rdev->wiphy, wdev, cookie);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_mgmt_tx(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev,
|
||||
struct cfg80211_mgmt_tx_params *params,
|
||||
u64 *cookie)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_mgmt_tx(&rdev->wiphy, wdev, params);
|
||||
ret = rdev->ops->mgmt_tx(&rdev->wiphy, wdev, params, cookie);
|
||||
trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_mgmt_tx_cancel_wait(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev, u64 cookie)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_mgmt_tx_cancel_wait(&rdev->wiphy, wdev, cookie);
|
||||
ret = rdev->ops->mgmt_tx_cancel_wait(&rdev->wiphy, wdev, cookie);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_set_power_mgmt(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, bool enabled,
|
||||
int timeout)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_set_power_mgmt(&rdev->wiphy, dev, enabled, timeout);
|
||||
ret = rdev->ops->set_power_mgmt(&rdev->wiphy, dev, enabled, timeout);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_set_cqm_rssi_config(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, s32 rssi_thold, u32 rssi_hyst)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_set_cqm_rssi_config(&rdev->wiphy, dev, rssi_thold,
|
||||
rssi_hyst);
|
||||
ret = rdev->ops->set_cqm_rssi_config(&rdev->wiphy, dev, rssi_thold,
|
||||
rssi_hyst);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_set_cqm_txe_config(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, u32 rate, u32 pkts, u32 intvl)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_set_cqm_txe_config(&rdev->wiphy, dev, rate, pkts, intvl);
|
||||
ret = rdev->ops->set_cqm_txe_config(&rdev->wiphy, dev, rate, pkts,
|
||||
intvl);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void
|
||||
rdev_mgmt_frame_register(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev, u16 frame_type, bool reg)
|
||||
{
|
||||
trace_rdev_mgmt_frame_register(&rdev->wiphy, wdev , frame_type, reg);
|
||||
rdev->ops->mgmt_frame_register(&rdev->wiphy, wdev , frame_type, reg);
|
||||
trace_rdev_return_void(&rdev->wiphy);
|
||||
}
|
||||
|
||||
static inline int rdev_set_antenna(struct cfg80211_registered_device *rdev,
|
||||
u32 tx_ant, u32 rx_ant)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_set_antenna(&rdev->wiphy, tx_ant, rx_ant);
|
||||
ret = rdev->ops->set_antenna(&rdev->wiphy, tx_ant, rx_ant);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_get_antenna(struct cfg80211_registered_device *rdev,
|
||||
u32 *tx_ant, u32 *rx_ant)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_get_antenna(&rdev->wiphy);
|
||||
ret = rdev->ops->get_antenna(&rdev->wiphy, tx_ant, rx_ant);
|
||||
if (ret)
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
else
|
||||
trace_rdev_return_int_tx_rx(&rdev->wiphy, ret, *tx_ant,
|
||||
*rx_ant);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_sched_scan_start(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct cfg80211_sched_scan_request *request)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_sched_scan_start(&rdev->wiphy, dev, request);
|
||||
ret = rdev->ops->sched_scan_start(&rdev->wiphy, dev, request);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_sched_scan_stop(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_sched_scan_stop(&rdev->wiphy, dev);
|
||||
ret = rdev->ops->sched_scan_stop(&rdev->wiphy, dev);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_set_rekey_data(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct cfg80211_gtk_rekey_data *data)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_set_rekey_data(&rdev->wiphy, dev);
|
||||
ret = rdev->ops->set_rekey_data(&rdev->wiphy, dev, data);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_tdls_mgmt(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, u8 *peer,
|
||||
u8 action_code, u8 dialog_token,
|
||||
u16 status_code, u32 peer_capability,
|
||||
bool initiator, const u8 *buf, size_t len)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_tdls_mgmt(&rdev->wiphy, dev, peer, action_code,
|
||||
dialog_token, status_code, peer_capability,
|
||||
initiator, buf, len);
|
||||
ret = rdev->ops->tdls_mgmt(&rdev->wiphy, dev, peer, action_code,
|
||||
dialog_token, status_code, peer_capability,
|
||||
initiator, buf, len);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_tdls_oper(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, u8 *peer,
|
||||
enum nl80211_tdls_operation oper)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_tdls_oper(&rdev->wiphy, dev, peer, oper);
|
||||
ret = rdev->ops->tdls_oper(&rdev->wiphy, dev, peer, oper);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_probe_client(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, const u8 *peer,
|
||||
u64 *cookie)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_probe_client(&rdev->wiphy, dev, peer);
|
||||
ret = rdev->ops->probe_client(&rdev->wiphy, dev, peer, cookie);
|
||||
trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_set_noack_map(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, u16 noack_map)
|
||||
{
|
||||
int ret;
|
||||
trace_rdev_set_noack_map(&rdev->wiphy, dev, noack_map);
|
||||
ret = rdev->ops->set_noack_map(&rdev->wiphy, dev, noack_map);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_get_channel(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
int ret;
|
||||
|
||||
trace_rdev_get_channel(&rdev->wiphy, wdev);
|
||||
ret = rdev->ops->get_channel(&rdev->wiphy, wdev, chandef);
|
||||
trace_rdev_return_chandef(&rdev->wiphy, ret, chandef);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_start_p2p_device(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
trace_rdev_start_p2p_device(&rdev->wiphy, wdev);
|
||||
ret = rdev->ops->start_p2p_device(&rdev->wiphy, wdev);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void rdev_stop_p2p_device(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev)
|
||||
{
|
||||
trace_rdev_stop_p2p_device(&rdev->wiphy, wdev);
|
||||
rdev->ops->stop_p2p_device(&rdev->wiphy, wdev);
|
||||
trace_rdev_return_void(&rdev->wiphy);
|
||||
}
|
||||
|
||||
static inline int rdev_set_mac_acl(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct cfg80211_acl_data *params)
|
||||
{
|
||||
int ret;
|
||||
|
||||
trace_rdev_set_mac_acl(&rdev->wiphy, dev, params);
|
||||
ret = rdev->ops->set_mac_acl(&rdev->wiphy, dev, params);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_update_ft_ies(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct cfg80211_update_ft_ies_params *ftie)
|
||||
{
|
||||
int ret;
|
||||
|
||||
trace_rdev_update_ft_ies(&rdev->wiphy, dev, ftie);
|
||||
ret = rdev->ops->update_ft_ies(&rdev->wiphy, dev, ftie);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_crit_proto_start(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev,
|
||||
enum nl80211_crit_proto_id protocol,
|
||||
u16 duration)
|
||||
{
|
||||
int ret;
|
||||
|
||||
trace_rdev_crit_proto_start(&rdev->wiphy, wdev, protocol, duration);
|
||||
ret = rdev->ops->crit_proto_start(&rdev->wiphy, wdev,
|
||||
protocol, duration);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void rdev_crit_proto_stop(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev)
|
||||
{
|
||||
trace_rdev_crit_proto_stop(&rdev->wiphy, wdev);
|
||||
rdev->ops->crit_proto_stop(&rdev->wiphy, wdev);
|
||||
trace_rdev_return_void(&rdev->wiphy);
|
||||
}
|
||||
|
||||
static inline int rdev_channel_switch(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct cfg80211_csa_settings *params)
|
||||
{
|
||||
int ret;
|
||||
|
||||
trace_rdev_channel_switch(&rdev->wiphy, dev, params);
|
||||
ret = rdev->ops->channel_switch(&rdev->wiphy, dev, params);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int rdev_set_qos_map(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct cfg80211_qos_map *qos_map)
|
||||
{
|
||||
int ret = -EOPNOTSUPP;
|
||||
|
||||
if (rdev->ops->set_qos_map) {
|
||||
trace_rdev_set_qos_map(&rdev->wiphy, dev, qos_map);
|
||||
ret = rdev->ops->set_qos_map(&rdev->wiphy, dev, qos_map);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_set_ap_chanwidth(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
int ret;
|
||||
|
||||
trace_rdev_set_ap_chanwidth(&rdev->wiphy, dev, chandef);
|
||||
ret = rdev->ops->set_ap_chanwidth(&rdev->wiphy, dev, chandef);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_add_tx_ts(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, u8 tsid, const u8 *peer,
|
||||
u8 user_prio, u16 admitted_time)
|
||||
{
|
||||
int ret = -EOPNOTSUPP;
|
||||
|
||||
trace_rdev_add_tx_ts(&rdev->wiphy, dev, tsid, peer,
|
||||
user_prio, admitted_time);
|
||||
if (rdev->ops->add_tx_ts)
|
||||
ret = rdev->ops->add_tx_ts(&rdev->wiphy, dev, tsid, peer,
|
||||
user_prio, admitted_time);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_del_tx_ts(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, u8 tsid, const u8 *peer)
|
||||
{
|
||||
int ret = -EOPNOTSUPP;
|
||||
|
||||
trace_rdev_del_tx_ts(&rdev->wiphy, dev, tsid, peer);
|
||||
if (rdev->ops->del_tx_ts)
|
||||
ret = rdev->ops->del_tx_ts(&rdev->wiphy, dev, tsid, peer);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* __CFG80211_RDEV_OPS */
|
2893
net/wireless/reg.c
Normal file
2893
net/wireless/reg.c
Normal file
File diff suppressed because it is too large
Load diff
125
net/wireless/reg.h
Normal file
125
net/wireless/reg.h
Normal file
|
@ -0,0 +1,125 @@
|
|||
#ifndef __NET_WIRELESS_REG_H
|
||||
#define __NET_WIRELESS_REG_H
|
||||
/*
|
||||
* Copyright 2008-2011 Luis R. Rodriguez <mcgrof@qca.qualcomm.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
extern const struct ieee80211_regdomain __rcu *cfg80211_regdomain;
|
||||
|
||||
bool reg_is_valid_request(const char *alpha2);
|
||||
bool is_world_regdom(const char *alpha2);
|
||||
bool reg_supported_dfs_region(enum nl80211_dfs_regions dfs_region);
|
||||
enum nl80211_dfs_regions reg_get_dfs_region(struct wiphy *wiphy);
|
||||
|
||||
int regulatory_hint_user(const char *alpha2,
|
||||
enum nl80211_user_reg_hint_type user_reg_hint_type);
|
||||
int regulatory_hint_indoor_user(void);
|
||||
|
||||
void wiphy_regulatory_register(struct wiphy *wiphy);
|
||||
void wiphy_regulatory_deregister(struct wiphy *wiphy);
|
||||
|
||||
int __init regulatory_init(void);
|
||||
void regulatory_exit(void);
|
||||
|
||||
int set_regdom(const struct ieee80211_regdomain *rd);
|
||||
unsigned int reg_get_max_bandwidth(const struct ieee80211_regdomain *rd,
|
||||
const struct ieee80211_reg_rule *rule);
|
||||
|
||||
bool reg_last_request_cell_base(void);
|
||||
|
||||
/**
|
||||
* regulatory_hint_found_beacon - hints a beacon was found on a channel
|
||||
* @wiphy: the wireless device where the beacon was found on
|
||||
* @beacon_chan: the channel on which the beacon was found on
|
||||
* @gfp: context flags
|
||||
*
|
||||
* This informs the wireless core that a beacon from an AP was found on
|
||||
* the channel provided. This allows the wireless core to make educated
|
||||
* guesses on regulatory to help with world roaming. This is only used for
|
||||
* world roaming -- when we do not know our current location. This is
|
||||
* only useful on channels 12, 13 and 14 on the 2 GHz band as channels
|
||||
* 1-11 are already enabled by the world regulatory domain; and on
|
||||
* non-radar 5 GHz channels.
|
||||
*
|
||||
* Drivers do not need to call this, cfg80211 will do it for after a scan
|
||||
* on a newly found BSS. If you cannot make use of this feature you can
|
||||
* set the wiphy->disable_beacon_hints to true.
|
||||
*/
|
||||
int regulatory_hint_found_beacon(struct wiphy *wiphy,
|
||||
struct ieee80211_channel *beacon_chan,
|
||||
gfp_t gfp);
|
||||
|
||||
/**
|
||||
* regulatory_hint_country_ie - hints a country IE as a regulatory domain
|
||||
* @wiphy: the wireless device giving the hint (used only for reporting
|
||||
* conflicts)
|
||||
* @band: the band on which the country IE was received on. This determines
|
||||
* the band we'll process the country IE channel triplets for.
|
||||
* @country_ie: pointer to the country IE
|
||||
* @country_ie_len: length of the country IE
|
||||
*
|
||||
* We will intersect the rd with the what CRDA tells us should apply
|
||||
* for the alpha2 this country IE belongs to, this prevents APs from
|
||||
* sending us incorrect or outdated information against a country.
|
||||
*
|
||||
* The AP is expected to provide Country IE channel triplets for the
|
||||
* band it is on. It is technically possible for APs to send channel
|
||||
* country IE triplets even for channels outside of the band they are
|
||||
* in but for that they would have to use the regulatory extension
|
||||
* in combination with a triplet but this behaviour is currently
|
||||
* not observed. For this reason if a triplet is seen with channel
|
||||
* information for a band the BSS is not present in it will be ignored.
|
||||
*/
|
||||
void regulatory_hint_country_ie(struct wiphy *wiphy,
|
||||
enum ieee80211_band band,
|
||||
const u8 *country_ie,
|
||||
u8 country_ie_len);
|
||||
|
||||
/**
|
||||
* regulatory_hint_disconnect - informs all devices have been disconneted
|
||||
*
|
||||
* Regulotory rules can be enhanced further upon scanning and upon
|
||||
* connection to an AP. These rules become stale if we disconnect
|
||||
* and go to another country, whether or not we suspend and resume.
|
||||
* If we suspend, go to another country and resume we'll automatically
|
||||
* get disconnected shortly after resuming and things will be reset as well.
|
||||
* This routine is a helper to restore regulatory settings to how they were
|
||||
* prior to our first connect attempt. This includes ignoring country IE and
|
||||
* beacon regulatory hints. The ieee80211_regdom module parameter will always
|
||||
* be respected but if a user had set the regulatory domain that will take
|
||||
* precedence.
|
||||
*
|
||||
* Must be called from process context.
|
||||
*/
|
||||
void regulatory_hint_disconnect(void);
|
||||
|
||||
/**
|
||||
* cfg80211_get_unii - get the U-NII band for the frequency
|
||||
* @freq: the frequency for which we want to get the UNII band.
|
||||
|
||||
* Get a value specifying the U-NII band frequency belongs to.
|
||||
* U-NII bands are defined by the FCC in C.F.R 47 part 15.
|
||||
*
|
||||
* Returns -EINVAL if freq is invalid, 0 for UNII-1, 1 for UNII-2A,
|
||||
* 2 for UNII-2B, 3 for UNII-2C and 4 for UNII-3.
|
||||
*/
|
||||
int cfg80211_get_unii(int freq);
|
||||
|
||||
/**
|
||||
* regulatory_indoor_allowed - is indoor operation allowed
|
||||
*/
|
||||
bool regulatory_indoor_allowed(void);
|
||||
|
||||
#endif /* __NET_WIRELESS_REG_H */
|
23
net/wireless/regdb.h
Normal file
23
net/wireless/regdb.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#ifndef __REGDB_H__
|
||||
#define __REGDB_H__
|
||||
|
||||
/*
|
||||
* Copyright 2009 John W. Linville <linville@tuxdriver.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
extern const struct ieee80211_regdomain *reg_regdb[];
|
||||
extern int reg_regdb_size;
|
||||
|
||||
#endif /* __REGDB_H__ */
|
1546
net/wireless/scan.c
Normal file
1546
net/wireless/scan.c
Normal file
File diff suppressed because it is too large
Load diff
992
net/wireless/sme.c
Normal file
992
net/wireless/sme.c
Normal file
|
@ -0,0 +1,992 @@
|
|||
/*
|
||||
* SME code for cfg80211
|
||||
* both driver SME event handling and the SME implementation
|
||||
* (for nl80211's connect() and wext)
|
||||
*
|
||||
* Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
|
||||
* Copyright (C) 2009 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/wireless.h>
|
||||
#include <linux/export.h>
|
||||
#include <net/iw_handler.h>
|
||||
#include <net/cfg80211.h>
|
||||
#include <net/rtnetlink.h>
|
||||
#include "nl80211.h"
|
||||
#include "reg.h"
|
||||
#include "rdev-ops.h"
|
||||
|
||||
/*
|
||||
* Software SME in cfg80211, using auth/assoc/deauth calls to the
|
||||
* driver. This is is for implementing nl80211's connect/disconnect
|
||||
* and wireless extensions (if configured.)
|
||||
*/
|
||||
|
||||
struct cfg80211_conn {
|
||||
struct cfg80211_connect_params params;
|
||||
/* these are sub-states of the _CONNECTING sme_state */
|
||||
enum {
|
||||
CFG80211_CONN_SCANNING,
|
||||
CFG80211_CONN_SCAN_AGAIN,
|
||||
CFG80211_CONN_AUTHENTICATE_NEXT,
|
||||
CFG80211_CONN_AUTHENTICATING,
|
||||
CFG80211_CONN_AUTH_FAILED,
|
||||
CFG80211_CONN_ASSOCIATE_NEXT,
|
||||
CFG80211_CONN_ASSOCIATING,
|
||||
CFG80211_CONN_ASSOC_FAILED,
|
||||
CFG80211_CONN_DEAUTH,
|
||||
CFG80211_CONN_CONNECTED,
|
||||
} state;
|
||||
u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
|
||||
u8 *ie;
|
||||
size_t ie_len;
|
||||
bool auto_auth, prev_bssid_valid;
|
||||
};
|
||||
|
||||
static void cfg80211_sme_free(struct wireless_dev *wdev)
|
||||
{
|
||||
if (!wdev->conn)
|
||||
return;
|
||||
|
||||
kfree(wdev->conn->ie);
|
||||
kfree(wdev->conn);
|
||||
wdev->conn = NULL;
|
||||
}
|
||||
|
||||
static int cfg80211_conn_scan(struct wireless_dev *wdev)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||
struct cfg80211_scan_request *request;
|
||||
int n_channels, err;
|
||||
|
||||
ASSERT_RTNL();
|
||||
ASSERT_WDEV_LOCK(wdev);
|
||||
|
||||
if (rdev->scan_req || rdev->scan_msg)
|
||||
return -EBUSY;
|
||||
|
||||
if (wdev->conn->params.channel)
|
||||
n_channels = 1;
|
||||
else
|
||||
n_channels = ieee80211_get_num_supported_channels(wdev->wiphy);
|
||||
|
||||
request = kzalloc(sizeof(*request) + sizeof(request->ssids[0]) +
|
||||
sizeof(request->channels[0]) * n_channels,
|
||||
GFP_KERNEL);
|
||||
if (!request)
|
||||
return -ENOMEM;
|
||||
|
||||
if (wdev->conn->params.channel)
|
||||
request->channels[0] = wdev->conn->params.channel;
|
||||
else {
|
||||
int i = 0, j;
|
||||
enum ieee80211_band band;
|
||||
struct ieee80211_supported_band *bands;
|
||||
struct ieee80211_channel *channel;
|
||||
|
||||
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
|
||||
bands = wdev->wiphy->bands[band];
|
||||
if (!bands)
|
||||
continue;
|
||||
for (j = 0; j < bands->n_channels; j++) {
|
||||
channel = &bands->channels[j];
|
||||
if (channel->flags & IEEE80211_CHAN_DISABLED)
|
||||
continue;
|
||||
request->channels[i++] = channel;
|
||||
}
|
||||
request->rates[band] = (1 << bands->n_bitrates) - 1;
|
||||
}
|
||||
n_channels = i;
|
||||
}
|
||||
request->n_channels = n_channels;
|
||||
request->ssids = (void *)&request->channels[n_channels];
|
||||
request->n_ssids = 1;
|
||||
|
||||
memcpy(request->ssids[0].ssid, wdev->conn->params.ssid,
|
||||
wdev->conn->params.ssid_len);
|
||||
request->ssids[0].ssid_len = wdev->conn->params.ssid_len;
|
||||
|
||||
request->wdev = wdev;
|
||||
request->wiphy = &rdev->wiphy;
|
||||
request->scan_start = jiffies;
|
||||
|
||||
rdev->scan_req = request;
|
||||
|
||||
err = rdev_scan(rdev, request);
|
||||
if (!err) {
|
||||
wdev->conn->state = CFG80211_CONN_SCANNING;
|
||||
nl80211_send_scan_start(rdev, wdev);
|
||||
dev_hold(wdev->netdev);
|
||||
} else {
|
||||
rdev->scan_req = NULL;
|
||||
kfree(request);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cfg80211_conn_do_work(struct wireless_dev *wdev)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||
struct cfg80211_connect_params *params;
|
||||
struct cfg80211_assoc_request req = {};
|
||||
int err;
|
||||
|
||||
ASSERT_WDEV_LOCK(wdev);
|
||||
|
||||
if (!wdev->conn)
|
||||
return 0;
|
||||
|
||||
params = &wdev->conn->params;
|
||||
|
||||
switch (wdev->conn->state) {
|
||||
case CFG80211_CONN_SCANNING:
|
||||
/* didn't find it during scan ... */
|
||||
return -ENOENT;
|
||||
case CFG80211_CONN_SCAN_AGAIN:
|
||||
return cfg80211_conn_scan(wdev);
|
||||
case CFG80211_CONN_AUTHENTICATE_NEXT:
|
||||
if (WARN_ON(!rdev->ops->auth))
|
||||
return -EOPNOTSUPP;
|
||||
wdev->conn->state = CFG80211_CONN_AUTHENTICATING;
|
||||
return cfg80211_mlme_auth(rdev, wdev->netdev,
|
||||
params->channel, params->auth_type,
|
||||
params->bssid,
|
||||
params->ssid, params->ssid_len,
|
||||
NULL, 0,
|
||||
params->key, params->key_len,
|
||||
params->key_idx, NULL, 0);
|
||||
case CFG80211_CONN_AUTH_FAILED:
|
||||
return -ENOTCONN;
|
||||
case CFG80211_CONN_ASSOCIATE_NEXT:
|
||||
if (WARN_ON(!rdev->ops->assoc))
|
||||
return -EOPNOTSUPP;
|
||||
wdev->conn->state = CFG80211_CONN_ASSOCIATING;
|
||||
if (wdev->conn->prev_bssid_valid)
|
||||
req.prev_bssid = wdev->conn->prev_bssid;
|
||||
req.ie = params->ie;
|
||||
req.ie_len = params->ie_len;
|
||||
req.use_mfp = params->mfp != NL80211_MFP_NO;
|
||||
req.crypto = params->crypto;
|
||||
req.flags = params->flags;
|
||||
req.ht_capa = params->ht_capa;
|
||||
req.ht_capa_mask = params->ht_capa_mask;
|
||||
req.vht_capa = params->vht_capa;
|
||||
req.vht_capa_mask = params->vht_capa_mask;
|
||||
|
||||
err = cfg80211_mlme_assoc(rdev, wdev->netdev, params->channel,
|
||||
params->bssid, params->ssid,
|
||||
params->ssid_len, &req);
|
||||
if (err)
|
||||
cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
|
||||
NULL, 0,
|
||||
WLAN_REASON_DEAUTH_LEAVING,
|
||||
false);
|
||||
return err;
|
||||
case CFG80211_CONN_ASSOC_FAILED:
|
||||
cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
|
||||
NULL, 0,
|
||||
WLAN_REASON_DEAUTH_LEAVING, false);
|
||||
return -ENOTCONN;
|
||||
case CFG80211_CONN_DEAUTH:
|
||||
cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
|
||||
NULL, 0,
|
||||
WLAN_REASON_DEAUTH_LEAVING, false);
|
||||
/* free directly, disconnected event already sent */
|
||||
cfg80211_sme_free(wdev);
|
||||
return 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void cfg80211_conn_work(struct work_struct *work)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev =
|
||||
container_of(work, struct cfg80211_registered_device, conn_work);
|
||||
struct wireless_dev *wdev;
|
||||
u8 bssid_buf[ETH_ALEN], *bssid = NULL;
|
||||
|
||||
rtnl_lock();
|
||||
|
||||
list_for_each_entry(wdev, &rdev->wdev_list, list) {
|
||||
if (!wdev->netdev)
|
||||
continue;
|
||||
|
||||
wdev_lock(wdev);
|
||||
if (!netif_running(wdev->netdev)) {
|
||||
wdev_unlock(wdev);
|
||||
continue;
|
||||
}
|
||||
if (!wdev->conn ||
|
||||
wdev->conn->state == CFG80211_CONN_CONNECTED) {
|
||||
wdev_unlock(wdev);
|
||||
continue;
|
||||
}
|
||||
if (wdev->conn->params.bssid) {
|
||||
memcpy(bssid_buf, wdev->conn->params.bssid, ETH_ALEN);
|
||||
bssid = bssid_buf;
|
||||
}
|
||||
if (cfg80211_conn_do_work(wdev)) {
|
||||
__cfg80211_connect_result(
|
||||
wdev->netdev, bssid,
|
||||
NULL, 0, NULL, 0,
|
||||
WLAN_STATUS_UNSPECIFIED_FAILURE,
|
||||
false, NULL);
|
||||
}
|
||||
wdev_unlock(wdev);
|
||||
}
|
||||
|
||||
rtnl_unlock();
|
||||
}
|
||||
|
||||
/* Returned bss is reference counted and must be cleaned up appropriately. */
|
||||
static struct cfg80211_bss *cfg80211_get_conn_bss(struct wireless_dev *wdev)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||
struct cfg80211_bss *bss;
|
||||
u16 capa = WLAN_CAPABILITY_ESS;
|
||||
|
||||
ASSERT_WDEV_LOCK(wdev);
|
||||
|
||||
if (wdev->conn->params.privacy)
|
||||
capa |= WLAN_CAPABILITY_PRIVACY;
|
||||
|
||||
bss = cfg80211_get_bss(wdev->wiphy, wdev->conn->params.channel,
|
||||
wdev->conn->params.bssid,
|
||||
wdev->conn->params.ssid,
|
||||
wdev->conn->params.ssid_len,
|
||||
WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY,
|
||||
capa);
|
||||
if (!bss)
|
||||
return NULL;
|
||||
|
||||
memcpy(wdev->conn->bssid, bss->bssid, ETH_ALEN);
|
||||
wdev->conn->params.bssid = wdev->conn->bssid;
|
||||
wdev->conn->params.channel = bss->channel;
|
||||
wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
|
||||
schedule_work(&rdev->conn_work);
|
||||
|
||||
return bss;
|
||||
}
|
||||
|
||||
static void __cfg80211_sme_scan_done(struct net_device *dev)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||
struct cfg80211_bss *bss;
|
||||
|
||||
ASSERT_WDEV_LOCK(wdev);
|
||||
|
||||
if (!wdev->conn)
|
||||
return;
|
||||
|
||||
if (wdev->conn->state != CFG80211_CONN_SCANNING &&
|
||||
wdev->conn->state != CFG80211_CONN_SCAN_AGAIN)
|
||||
return;
|
||||
|
||||
bss = cfg80211_get_conn_bss(wdev);
|
||||
if (bss)
|
||||
cfg80211_put_bss(&rdev->wiphy, bss);
|
||||
else
|
||||
schedule_work(&rdev->conn_work);
|
||||
}
|
||||
|
||||
void cfg80211_sme_scan_done(struct net_device *dev)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
|
||||
wdev_lock(wdev);
|
||||
__cfg80211_sme_scan_done(dev);
|
||||
wdev_unlock(wdev);
|
||||
}
|
||||
|
||||
void cfg80211_sme_rx_auth(struct wireless_dev *wdev, const u8 *buf, size_t len)
|
||||
{
|
||||
struct wiphy *wiphy = wdev->wiphy;
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
||||
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
|
||||
u16 status_code = le16_to_cpu(mgmt->u.auth.status_code);
|
||||
|
||||
ASSERT_WDEV_LOCK(wdev);
|
||||
|
||||
if (!wdev->conn || wdev->conn->state == CFG80211_CONN_CONNECTED)
|
||||
return;
|
||||
|
||||
if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG &&
|
||||
wdev->conn->auto_auth &&
|
||||
wdev->conn->params.auth_type != NL80211_AUTHTYPE_NETWORK_EAP) {
|
||||
/* select automatically between only open, shared, leap */
|
||||
switch (wdev->conn->params.auth_type) {
|
||||
case NL80211_AUTHTYPE_OPEN_SYSTEM:
|
||||
if (wdev->connect_keys)
|
||||
wdev->conn->params.auth_type =
|
||||
NL80211_AUTHTYPE_SHARED_KEY;
|
||||
else
|
||||
wdev->conn->params.auth_type =
|
||||
NL80211_AUTHTYPE_NETWORK_EAP;
|
||||
break;
|
||||
case NL80211_AUTHTYPE_SHARED_KEY:
|
||||
wdev->conn->params.auth_type =
|
||||
NL80211_AUTHTYPE_NETWORK_EAP;
|
||||
break;
|
||||
default:
|
||||
/* huh? */
|
||||
wdev->conn->params.auth_type =
|
||||
NL80211_AUTHTYPE_OPEN_SYSTEM;
|
||||
break;
|
||||
}
|
||||
wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
|
||||
schedule_work(&rdev->conn_work);
|
||||
} else if (status_code != WLAN_STATUS_SUCCESS) {
|
||||
__cfg80211_connect_result(wdev->netdev, mgmt->bssid,
|
||||
NULL, 0, NULL, 0,
|
||||
status_code, false, NULL);
|
||||
} else if (wdev->conn->state == CFG80211_CONN_AUTHENTICATING) {
|
||||
wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT;
|
||||
schedule_work(&rdev->conn_work);
|
||||
}
|
||||
}
|
||||
|
||||
bool cfg80211_sme_rx_assoc_resp(struct wireless_dev *wdev, u16 status)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||
|
||||
if (!wdev->conn)
|
||||
return false;
|
||||
|
||||
if (status == WLAN_STATUS_SUCCESS) {
|
||||
wdev->conn->state = CFG80211_CONN_CONNECTED;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (wdev->conn->prev_bssid_valid) {
|
||||
/*
|
||||
* Some stupid APs don't accept reassoc, so we
|
||||
* need to fall back to trying regular assoc;
|
||||
* return true so no event is sent to userspace.
|
||||
*/
|
||||
wdev->conn->prev_bssid_valid = false;
|
||||
wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT;
|
||||
schedule_work(&rdev->conn_work);
|
||||
return true;
|
||||
}
|
||||
|
||||
wdev->conn->state = CFG80211_CONN_ASSOC_FAILED;
|
||||
schedule_work(&rdev->conn_work);
|
||||
return false;
|
||||
}
|
||||
|
||||
void cfg80211_sme_deauth(struct wireless_dev *wdev)
|
||||
{
|
||||
cfg80211_sme_free(wdev);
|
||||
}
|
||||
|
||||
void cfg80211_sme_auth_timeout(struct wireless_dev *wdev)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||
|
||||
if (!wdev->conn)
|
||||
return;
|
||||
|
||||
wdev->conn->state = CFG80211_CONN_AUTH_FAILED;
|
||||
schedule_work(&rdev->conn_work);
|
||||
}
|
||||
|
||||
void cfg80211_sme_disassoc(struct wireless_dev *wdev)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||
|
||||
if (!wdev->conn)
|
||||
return;
|
||||
|
||||
wdev->conn->state = CFG80211_CONN_DEAUTH;
|
||||
schedule_work(&rdev->conn_work);
|
||||
}
|
||||
|
||||
void cfg80211_sme_assoc_timeout(struct wireless_dev *wdev)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||
|
||||
if (!wdev->conn)
|
||||
return;
|
||||
|
||||
wdev->conn->state = CFG80211_CONN_ASSOC_FAILED;
|
||||
schedule_work(&rdev->conn_work);
|
||||
}
|
||||
|
||||
static int cfg80211_sme_connect(struct wireless_dev *wdev,
|
||||
struct cfg80211_connect_params *connect,
|
||||
const u8 *prev_bssid)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||
struct cfg80211_bss *bss;
|
||||
int err;
|
||||
|
||||
if (!rdev->ops->auth || !rdev->ops->assoc)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (wdev->current_bss)
|
||||
return -EALREADY;
|
||||
|
||||
if (WARN_ON(wdev->conn))
|
||||
return -EINPROGRESS;
|
||||
|
||||
wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL);
|
||||
if (!wdev->conn)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Copy all parameters, and treat explicitly IEs, BSSID, SSID.
|
||||
*/
|
||||
memcpy(&wdev->conn->params, connect, sizeof(*connect));
|
||||
if (connect->bssid) {
|
||||
wdev->conn->params.bssid = wdev->conn->bssid;
|
||||
memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN);
|
||||
}
|
||||
|
||||
if (connect->ie) {
|
||||
wdev->conn->ie = kmemdup(connect->ie, connect->ie_len,
|
||||
GFP_KERNEL);
|
||||
wdev->conn->params.ie = wdev->conn->ie;
|
||||
if (!wdev->conn->ie) {
|
||||
kfree(wdev->conn);
|
||||
wdev->conn = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) {
|
||||
wdev->conn->auto_auth = true;
|
||||
/* start with open system ... should mostly work */
|
||||
wdev->conn->params.auth_type =
|
||||
NL80211_AUTHTYPE_OPEN_SYSTEM;
|
||||
} else {
|
||||
wdev->conn->auto_auth = false;
|
||||
}
|
||||
|
||||
wdev->conn->params.ssid = wdev->ssid;
|
||||
wdev->conn->params.ssid_len = wdev->ssid_len;
|
||||
|
||||
/* see if we have the bss already */
|
||||
bss = cfg80211_get_conn_bss(wdev);
|
||||
|
||||
if (prev_bssid) {
|
||||
memcpy(wdev->conn->prev_bssid, prev_bssid, ETH_ALEN);
|
||||
wdev->conn->prev_bssid_valid = true;
|
||||
}
|
||||
|
||||
/* we're good if we have a matching bss struct */
|
||||
if (bss) {
|
||||
err = cfg80211_conn_do_work(wdev);
|
||||
cfg80211_put_bss(wdev->wiphy, bss);
|
||||
} else {
|
||||
/* otherwise we'll need to scan for the AP first */
|
||||
err = cfg80211_conn_scan(wdev);
|
||||
|
||||
/*
|
||||
* If we can't scan right now, then we need to scan again
|
||||
* after the current scan finished, since the parameters
|
||||
* changed (unless we find a good AP anyway).
|
||||
*/
|
||||
if (err == -EBUSY) {
|
||||
err = 0;
|
||||
wdev->conn->state = CFG80211_CONN_SCAN_AGAIN;
|
||||
}
|
||||
}
|
||||
|
||||
if (err)
|
||||
cfg80211_sme_free(wdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cfg80211_sme_disconnect(struct wireless_dev *wdev, u16 reason)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||
int err;
|
||||
|
||||
if (!wdev->conn)
|
||||
return 0;
|
||||
|
||||
if (!rdev->ops->deauth)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (wdev->conn->state == CFG80211_CONN_SCANNING ||
|
||||
wdev->conn->state == CFG80211_CONN_SCAN_AGAIN) {
|
||||
err = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* wdev->conn->params.bssid must be set if > SCANNING */
|
||||
err = cfg80211_mlme_deauth(rdev, wdev->netdev,
|
||||
wdev->conn->params.bssid,
|
||||
NULL, 0, reason, false);
|
||||
out:
|
||||
cfg80211_sme_free(wdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* code shared for in-device and software SME
|
||||
*/
|
||||
|
||||
static bool cfg80211_is_all_idle(void)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev;
|
||||
struct wireless_dev *wdev;
|
||||
bool is_all_idle = true;
|
||||
|
||||
/*
|
||||
* All devices must be idle as otherwise if you are actively
|
||||
* scanning some new beacon hints could be learned and would
|
||||
* count as new regulatory hints.
|
||||
*/
|
||||
list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
|
||||
list_for_each_entry(wdev, &rdev->wdev_list, list) {
|
||||
wdev_lock(wdev);
|
||||
if (wdev->conn || wdev->current_bss)
|
||||
is_all_idle = false;
|
||||
wdev_unlock(wdev);
|
||||
}
|
||||
}
|
||||
|
||||
return is_all_idle;
|
||||
}
|
||||
|
||||
static void disconnect_work(struct work_struct *work)
|
||||
{
|
||||
rtnl_lock();
|
||||
if (cfg80211_is_all_idle())
|
||||
regulatory_hint_disconnect();
|
||||
rtnl_unlock();
|
||||
}
|
||||
|
||||
static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work);
|
||||
|
||||
|
||||
/*
|
||||
* API calls for drivers implementing connect/disconnect and
|
||||
* SME event handling
|
||||
*/
|
||||
|
||||
/* This method must consume bss one way or another */
|
||||
void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
|
||||
const u8 *req_ie, size_t req_ie_len,
|
||||
const u8 *resp_ie, size_t resp_ie_len,
|
||||
u16 status, bool wextev,
|
||||
struct cfg80211_bss *bss)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
const u8 *country_ie;
|
||||
#ifdef CONFIG_CFG80211_WEXT
|
||||
union iwreq_data wrqu;
|
||||
#endif
|
||||
|
||||
ASSERT_WDEV_LOCK(wdev);
|
||||
|
||||
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION &&
|
||||
wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) {
|
||||
cfg80211_put_bss(wdev->wiphy, bss);
|
||||
return;
|
||||
}
|
||||
|
||||
nl80211_send_connect_result(wiphy_to_rdev(wdev->wiphy), dev,
|
||||
bssid, req_ie, req_ie_len,
|
||||
resp_ie, resp_ie_len,
|
||||
status, GFP_KERNEL);
|
||||
|
||||
#ifdef CONFIG_CFG80211_WEXT
|
||||
if (wextev) {
|
||||
if (req_ie && status == WLAN_STATUS_SUCCESS) {
|
||||
memset(&wrqu, 0, sizeof(wrqu));
|
||||
wrqu.data.length = req_ie_len;
|
||||
wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, req_ie);
|
||||
}
|
||||
|
||||
if (resp_ie && status == WLAN_STATUS_SUCCESS) {
|
||||
memset(&wrqu, 0, sizeof(wrqu));
|
||||
wrqu.data.length = resp_ie_len;
|
||||
wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie);
|
||||
}
|
||||
|
||||
memset(&wrqu, 0, sizeof(wrqu));
|
||||
wrqu.ap_addr.sa_family = ARPHRD_ETHER;
|
||||
if (bssid && status == WLAN_STATUS_SUCCESS) {
|
||||
memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
|
||||
memcpy(wdev->wext.prev_bssid, bssid, ETH_ALEN);
|
||||
wdev->wext.prev_bssid_valid = true;
|
||||
}
|
||||
wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!bss && (status == WLAN_STATUS_SUCCESS)) {
|
||||
WARN_ON_ONCE(!wiphy_to_rdev(wdev->wiphy)->ops->connect);
|
||||
bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
|
||||
wdev->ssid, wdev->ssid_len,
|
||||
WLAN_CAPABILITY_ESS,
|
||||
WLAN_CAPABILITY_ESS);
|
||||
if (bss)
|
||||
cfg80211_hold_bss(bss_from_pub(bss));
|
||||
}
|
||||
|
||||
if (wdev->current_bss) {
|
||||
cfg80211_unhold_bss(wdev->current_bss);
|
||||
cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
|
||||
wdev->current_bss = NULL;
|
||||
}
|
||||
|
||||
if (status != WLAN_STATUS_SUCCESS) {
|
||||
kzfree(wdev->connect_keys);
|
||||
wdev->connect_keys = NULL;
|
||||
wdev->ssid_len = 0;
|
||||
if (bss) {
|
||||
cfg80211_unhold_bss(bss_from_pub(bss));
|
||||
cfg80211_put_bss(wdev->wiphy, bss);
|
||||
}
|
||||
cfg80211_sme_free(wdev);
|
||||
return;
|
||||
}
|
||||
|
||||
if (WARN_ON(!bss))
|
||||
return;
|
||||
|
||||
wdev->current_bss = bss_from_pub(bss);
|
||||
|
||||
cfg80211_upload_connect_keys(wdev);
|
||||
|
||||
rcu_read_lock();
|
||||
country_ie = ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY);
|
||||
if (!country_ie) {
|
||||
rcu_read_unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
country_ie = kmemdup(country_ie, 2 + country_ie[1], GFP_ATOMIC);
|
||||
rcu_read_unlock();
|
||||
|
||||
if (!country_ie)
|
||||
return;
|
||||
|
||||
/*
|
||||
* ieee80211_bss_get_ie() ensures we can access:
|
||||
* - country_ie + 2, the start of the country ie data, and
|
||||
* - and country_ie[1] which is the IE length
|
||||
*/
|
||||
regulatory_hint_country_ie(wdev->wiphy, bss->channel->band,
|
||||
country_ie + 2, country_ie[1]);
|
||||
kfree(country_ie);
|
||||
}
|
||||
|
||||
void cfg80211_connect_result(struct net_device *dev, 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 wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||
struct cfg80211_event *ev;
|
||||
unsigned long flags;
|
||||
|
||||
ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp);
|
||||
if (!ev)
|
||||
return;
|
||||
|
||||
ev->type = EVENT_CONNECT_RESULT;
|
||||
if (bssid)
|
||||
memcpy(ev->cr.bssid, bssid, ETH_ALEN);
|
||||
if (req_ie_len) {
|
||||
ev->cr.req_ie = ((u8 *)ev) + sizeof(*ev);
|
||||
ev->cr.req_ie_len = req_ie_len;
|
||||
memcpy((void *)ev->cr.req_ie, req_ie, req_ie_len);
|
||||
}
|
||||
if (resp_ie_len) {
|
||||
ev->cr.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len;
|
||||
ev->cr.resp_ie_len = resp_ie_len;
|
||||
memcpy((void *)ev->cr.resp_ie, resp_ie, resp_ie_len);
|
||||
}
|
||||
ev->cr.status = status;
|
||||
|
||||
spin_lock_irqsave(&wdev->event_lock, flags);
|
||||
list_add_tail(&ev->list, &wdev->event_list);
|
||||
spin_unlock_irqrestore(&wdev->event_lock, flags);
|
||||
queue_work(cfg80211_wq, &rdev->event_work);
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_connect_result);
|
||||
|
||||
/* Consumes bss object one way or another */
|
||||
void __cfg80211_roamed(struct wireless_dev *wdev,
|
||||
struct cfg80211_bss *bss,
|
||||
const u8 *req_ie, size_t req_ie_len,
|
||||
const u8 *resp_ie, size_t resp_ie_len)
|
||||
{
|
||||
#ifdef CONFIG_CFG80211_WEXT
|
||||
union iwreq_data wrqu;
|
||||
#endif
|
||||
ASSERT_WDEV_LOCK(wdev);
|
||||
|
||||
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION &&
|
||||
wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
|
||||
goto out;
|
||||
|
||||
if (WARN_ON(!wdev->current_bss))
|
||||
goto out;
|
||||
|
||||
cfg80211_unhold_bss(wdev->current_bss);
|
||||
cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
|
||||
wdev->current_bss = NULL;
|
||||
|
||||
cfg80211_hold_bss(bss_from_pub(bss));
|
||||
wdev->current_bss = bss_from_pub(bss);
|
||||
|
||||
nl80211_send_roamed(wiphy_to_rdev(wdev->wiphy),
|
||||
wdev->netdev, bss->bssid,
|
||||
req_ie, req_ie_len, resp_ie, resp_ie_len,
|
||||
GFP_KERNEL);
|
||||
|
||||
#ifdef CONFIG_CFG80211_WEXT
|
||||
if (req_ie) {
|
||||
memset(&wrqu, 0, sizeof(wrqu));
|
||||
wrqu.data.length = req_ie_len;
|
||||
wireless_send_event(wdev->netdev, IWEVASSOCREQIE,
|
||||
&wrqu, req_ie);
|
||||
}
|
||||
|
||||
if (resp_ie) {
|
||||
memset(&wrqu, 0, sizeof(wrqu));
|
||||
wrqu.data.length = resp_ie_len;
|
||||
wireless_send_event(wdev->netdev, IWEVASSOCRESPIE,
|
||||
&wrqu, resp_ie);
|
||||
}
|
||||
|
||||
memset(&wrqu, 0, sizeof(wrqu));
|
||||
wrqu.ap_addr.sa_family = ARPHRD_ETHER;
|
||||
memcpy(wrqu.ap_addr.sa_data, bss->bssid, ETH_ALEN);
|
||||
memcpy(wdev->wext.prev_bssid, bss->bssid, ETH_ALEN);
|
||||
wdev->wext.prev_bssid_valid = true;
|
||||
wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL);
|
||||
#endif
|
||||
|
||||
return;
|
||||
out:
|
||||
cfg80211_put_bss(wdev->wiphy, bss);
|
||||
}
|
||||
|
||||
void cfg80211_roamed(struct net_device *dev,
|
||||
struct ieee80211_channel *channel,
|
||||
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 wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct cfg80211_bss *bss;
|
||||
|
||||
bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, wdev->ssid,
|
||||
wdev->ssid_len, WLAN_CAPABILITY_ESS,
|
||||
WLAN_CAPABILITY_ESS);
|
||||
if (WARN_ON(!bss))
|
||||
return;
|
||||
|
||||
cfg80211_roamed_bss(dev, bss, req_ie, req_ie_len, resp_ie,
|
||||
resp_ie_len, gfp);
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_roamed);
|
||||
|
||||
/* Consumes bss object one way or another */
|
||||
void cfg80211_roamed_bss(struct net_device *dev,
|
||||
struct cfg80211_bss *bss, const u8 *req_ie,
|
||||
size_t req_ie_len, const u8 *resp_ie,
|
||||
size_t resp_ie_len, gfp_t gfp)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||
struct cfg80211_event *ev;
|
||||
unsigned long flags;
|
||||
|
||||
if (WARN_ON(!bss))
|
||||
return;
|
||||
|
||||
ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp);
|
||||
if (!ev) {
|
||||
cfg80211_put_bss(wdev->wiphy, bss);
|
||||
return;
|
||||
}
|
||||
|
||||
ev->type = EVENT_ROAMED;
|
||||
ev->rm.req_ie = ((u8 *)ev) + sizeof(*ev);
|
||||
ev->rm.req_ie_len = req_ie_len;
|
||||
memcpy((void *)ev->rm.req_ie, req_ie, req_ie_len);
|
||||
ev->rm.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len;
|
||||
ev->rm.resp_ie_len = resp_ie_len;
|
||||
memcpy((void *)ev->rm.resp_ie, resp_ie, resp_ie_len);
|
||||
ev->rm.bss = bss;
|
||||
|
||||
spin_lock_irqsave(&wdev->event_lock, flags);
|
||||
list_add_tail(&ev->list, &wdev->event_list);
|
||||
spin_unlock_irqrestore(&wdev->event_lock, flags);
|
||||
queue_work(cfg80211_wq, &rdev->event_work);
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_roamed_bss);
|
||||
|
||||
void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
|
||||
size_t ie_len, u16 reason, bool from_ap)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||
int i;
|
||||
#ifdef CONFIG_CFG80211_WEXT
|
||||
union iwreq_data wrqu;
|
||||
#endif
|
||||
|
||||
ASSERT_WDEV_LOCK(wdev);
|
||||
|
||||
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION &&
|
||||
wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
|
||||
return;
|
||||
|
||||
if (wdev->current_bss) {
|
||||
cfg80211_unhold_bss(wdev->current_bss);
|
||||
cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
|
||||
}
|
||||
|
||||
wdev->current_bss = NULL;
|
||||
wdev->ssid_len = 0;
|
||||
|
||||
nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap);
|
||||
|
||||
/*
|
||||
* Delete all the keys ... pairwise keys can't really
|
||||
* exist any more anyway, but default keys might.
|
||||
*/
|
||||
if (rdev->ops->del_key)
|
||||
for (i = 0; i < 6; i++)
|
||||
rdev_del_key(rdev, dev, i, false, NULL);
|
||||
|
||||
rdev_set_qos_map(rdev, dev, NULL);
|
||||
|
||||
#ifdef CONFIG_CFG80211_WEXT
|
||||
memset(&wrqu, 0, sizeof(wrqu));
|
||||
wrqu.ap_addr.sa_family = ARPHRD_ETHER;
|
||||
wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
|
||||
wdev->wext.connect.ssid_len = 0;
|
||||
#endif
|
||||
|
||||
schedule_work(&cfg80211_disconnect_work);
|
||||
}
|
||||
|
||||
void cfg80211_disconnected(struct net_device *dev, u16 reason,
|
||||
const u8 *ie, size_t ie_len, gfp_t gfp)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||
struct cfg80211_event *ev;
|
||||
unsigned long flags;
|
||||
|
||||
ev = kzalloc(sizeof(*ev) + ie_len, gfp);
|
||||
if (!ev)
|
||||
return;
|
||||
|
||||
ev->type = EVENT_DISCONNECTED;
|
||||
ev->dc.ie = ((u8 *)ev) + sizeof(*ev);
|
||||
ev->dc.ie_len = ie_len;
|
||||
memcpy((void *)ev->dc.ie, ie, ie_len);
|
||||
ev->dc.reason = reason;
|
||||
|
||||
spin_lock_irqsave(&wdev->event_lock, flags);
|
||||
list_add_tail(&ev->list, &wdev->event_list);
|
||||
spin_unlock_irqrestore(&wdev->event_lock, flags);
|
||||
queue_work(cfg80211_wq, &rdev->event_work);
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_disconnected);
|
||||
|
||||
/*
|
||||
* API calls for nl80211/wext compatibility code
|
||||
*/
|
||||
int cfg80211_connect(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev,
|
||||
struct cfg80211_connect_params *connect,
|
||||
struct cfg80211_cached_keys *connkeys,
|
||||
const u8 *prev_bssid)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
int err;
|
||||
|
||||
ASSERT_WDEV_LOCK(wdev);
|
||||
|
||||
if (WARN_ON(wdev->connect_keys)) {
|
||||
kzfree(wdev->connect_keys);
|
||||
wdev->connect_keys = NULL;
|
||||
}
|
||||
|
||||
cfg80211_oper_and_ht_capa(&connect->ht_capa_mask,
|
||||
rdev->wiphy.ht_capa_mod_mask);
|
||||
|
||||
if (connkeys && connkeys->def >= 0) {
|
||||
int idx;
|
||||
u32 cipher;
|
||||
|
||||
idx = connkeys->def;
|
||||
cipher = connkeys->params[idx].cipher;
|
||||
/* If given a WEP key we may need it for shared key auth */
|
||||
if (cipher == WLAN_CIPHER_SUITE_WEP40 ||
|
||||
cipher == WLAN_CIPHER_SUITE_WEP104) {
|
||||
connect->key_idx = idx;
|
||||
connect->key = connkeys->params[idx].key;
|
||||
connect->key_len = connkeys->params[idx].key_len;
|
||||
|
||||
/*
|
||||
* If ciphers are not set (e.g. when going through
|
||||
* iwconfig), we have to set them appropriately here.
|
||||
*/
|
||||
if (connect->crypto.cipher_group == 0)
|
||||
connect->crypto.cipher_group = cipher;
|
||||
|
||||
if (connect->crypto.n_ciphers_pairwise == 0) {
|
||||
connect->crypto.n_ciphers_pairwise = 1;
|
||||
connect->crypto.ciphers_pairwise[0] = cipher;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wdev->connect_keys = connkeys;
|
||||
memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
|
||||
wdev->ssid_len = connect->ssid_len;
|
||||
|
||||
if (!rdev->ops->connect)
|
||||
err = cfg80211_sme_connect(wdev, connect, prev_bssid);
|
||||
else
|
||||
err = rdev_connect(rdev, dev, connect);
|
||||
|
||||
if (err) {
|
||||
wdev->connect_keys = NULL;
|
||||
wdev->ssid_len = 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, u16 reason, bool wextev)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
int err = 0;
|
||||
|
||||
ASSERT_WDEV_LOCK(wdev);
|
||||
|
||||
kzfree(wdev->connect_keys);
|
||||
wdev->connect_keys = NULL;
|
||||
|
||||
if (wdev->conn)
|
||||
err = cfg80211_sme_disconnect(wdev, reason);
|
||||
else if (!rdev->ops->disconnect)
|
||||
cfg80211_mlme_down(rdev, dev);
|
||||
else if (wdev->current_bss)
|
||||
err = rdev_disconnect(rdev, dev, reason);
|
||||
|
||||
return err;
|
||||
}
|
170
net/wireless/sysfs.c
Normal file
170
net/wireless/sysfs.c
Normal file
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* This file provides /sys/class/ieee80211/<wiphy name>/
|
||||
* and some default attributes.
|
||||
*
|
||||
* Copyright 2005-2006 Jiri Benc <jbenc@suse.cz>
|
||||
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
|
||||
*
|
||||
* This file is GPLv2 as found in COPYING.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/nl80211.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <net/cfg80211.h>
|
||||
#include "sysfs.h"
|
||||
#include "core.h"
|
||||
#include "rdev-ops.h"
|
||||
|
||||
static inline struct cfg80211_registered_device *dev_to_rdev(
|
||||
struct device *dev)
|
||||
{
|
||||
return container_of(dev, struct cfg80211_registered_device, wiphy.dev);
|
||||
}
|
||||
|
||||
#define SHOW_FMT(name, fmt, member) \
|
||||
static ssize_t name ## _show(struct device *dev, \
|
||||
struct device_attribute *attr, \
|
||||
char *buf) \
|
||||
{ \
|
||||
return sprintf(buf, fmt "\n", dev_to_rdev(dev)->member); \
|
||||
} \
|
||||
static DEVICE_ATTR_RO(name)
|
||||
|
||||
SHOW_FMT(index, "%d", wiphy_idx);
|
||||
SHOW_FMT(macaddress, "%pM", wiphy.perm_addr);
|
||||
SHOW_FMT(address_mask, "%pM", wiphy.addr_mask);
|
||||
|
||||
static ssize_t name_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf) {
|
||||
struct wiphy *wiphy = &dev_to_rdev(dev)->wiphy;
|
||||
return sprintf(buf, "%s\n", dev_name(&wiphy->dev));
|
||||
}
|
||||
static DEVICE_ATTR_RO(name);
|
||||
|
||||
static ssize_t addresses_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct wiphy *wiphy = &dev_to_rdev(dev)->wiphy;
|
||||
char *start = buf;
|
||||
int i;
|
||||
|
||||
if (!wiphy->addresses)
|
||||
return sprintf(buf, "%pM\n", wiphy->perm_addr);
|
||||
|
||||
for (i = 0; i < wiphy->n_addresses; i++)
|
||||
buf += sprintf(buf, "%pM\n", &wiphy->addresses[i].addr);
|
||||
|
||||
return buf - start;
|
||||
}
|
||||
static DEVICE_ATTR_RO(addresses);
|
||||
|
||||
static struct attribute *ieee80211_attrs[] = {
|
||||
&dev_attr_index.attr,
|
||||
&dev_attr_macaddress.attr,
|
||||
&dev_attr_address_mask.attr,
|
||||
&dev_attr_addresses.attr,
|
||||
&dev_attr_name.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(ieee80211);
|
||||
|
||||
static void wiphy_dev_release(struct device *dev)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = dev_to_rdev(dev);
|
||||
|
||||
cfg80211_dev_free(rdev);
|
||||
}
|
||||
|
||||
static int wiphy_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||
{
|
||||
/* TODO, we probably need stuff here */
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static void cfg80211_leave_all(struct cfg80211_registered_device *rdev)
|
||||
{
|
||||
struct wireless_dev *wdev;
|
||||
|
||||
list_for_each_entry(wdev, &rdev->wdev_list, list)
|
||||
cfg80211_leave(rdev, wdev);
|
||||
}
|
||||
|
||||
static int wiphy_suspend(struct device *dev, pm_message_t state)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = dev_to_rdev(dev);
|
||||
int ret = 0;
|
||||
|
||||
rdev->suspend_at = get_seconds();
|
||||
|
||||
rtnl_lock();
|
||||
if (rdev->wiphy.registered) {
|
||||
if (!rdev->wiphy.wowlan_config)
|
||||
cfg80211_leave_all(rdev);
|
||||
if (rdev->ops->suspend)
|
||||
ret = rdev_suspend(rdev, rdev->wiphy.wowlan_config);
|
||||
if (ret == 1) {
|
||||
/* Driver refuse to configure wowlan */
|
||||
cfg80211_leave_all(rdev);
|
||||
ret = rdev_suspend(rdev, NULL);
|
||||
}
|
||||
}
|
||||
rtnl_unlock();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wiphy_resume(struct device *dev)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = dev_to_rdev(dev);
|
||||
int ret = 0;
|
||||
|
||||
/* Age scan results with time spent in suspend */
|
||||
cfg80211_bss_age(rdev, get_seconds() - rdev->suspend_at);
|
||||
|
||||
if (rdev->ops->resume) {
|
||||
rtnl_lock();
|
||||
if (rdev->wiphy.registered)
|
||||
ret = rdev_resume(rdev);
|
||||
rtnl_unlock();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const void *wiphy_namespace(struct device *d)
|
||||
{
|
||||
struct wiphy *wiphy = container_of(d, struct wiphy, dev);
|
||||
|
||||
return wiphy_net(wiphy);
|
||||
}
|
||||
|
||||
struct class ieee80211_class = {
|
||||
.name = "ieee80211",
|
||||
.owner = THIS_MODULE,
|
||||
.dev_release = wiphy_dev_release,
|
||||
.dev_groups = ieee80211_groups,
|
||||
.dev_uevent = wiphy_uevent,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = wiphy_suspend,
|
||||
.resume = wiphy_resume,
|
||||
#endif
|
||||
.ns_type = &net_ns_type_operations,
|
||||
.namespace = wiphy_namespace,
|
||||
};
|
||||
|
||||
int wiphy_sysfs_init(void)
|
||||
{
|
||||
return class_register(&ieee80211_class);
|
||||
}
|
||||
|
||||
void wiphy_sysfs_exit(void)
|
||||
{
|
||||
class_unregister(&ieee80211_class);
|
||||
}
|
9
net/wireless/sysfs.h
Normal file
9
net/wireless/sysfs.h
Normal file
|
@ -0,0 +1,9 @@
|
|||
#ifndef __WIRELESS_SYSFS_H
|
||||
#define __WIRELESS_SYSFS_H
|
||||
|
||||
int wiphy_sysfs_init(void);
|
||||
void wiphy_sysfs_exit(void);
|
||||
|
||||
extern struct class ieee80211_class;
|
||||
|
||||
#endif /* __WIRELESS_SYSFS_H */
|
7
net/wireless/trace.c
Normal file
7
net/wireless/trace.c
Normal file
|
@ -0,0 +1,7 @@
|
|||
#include <linux/module.h>
|
||||
|
||||
#ifndef __CHECKER__
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include "trace.h"
|
||||
|
||||
#endif
|
2665
net/wireless/trace.h
Normal file
2665
net/wireless/trace.h
Normal file
File diff suppressed because it is too large
Load diff
1577
net/wireless/util.c
Normal file
1577
net/wireless/util.c
Normal file
File diff suppressed because it is too large
Load diff
1529
net/wireless/wext-compat.c
Normal file
1529
net/wireless/wext-compat.c
Normal file
File diff suppressed because it is too large
Load diff
57
net/wireless/wext-compat.h
Normal file
57
net/wireless/wext-compat.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
#ifndef __WEXT_COMPAT
|
||||
#define __WEXT_COMPAT
|
||||
|
||||
#include <net/iw_handler.h>
|
||||
#include <linux/wireless.h>
|
||||
|
||||
int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_freq *freq, char *extra);
|
||||
int cfg80211_ibss_wext_giwfreq(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_freq *freq, char *extra);
|
||||
int cfg80211_ibss_wext_siwap(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct sockaddr *ap_addr, char *extra);
|
||||
int cfg80211_ibss_wext_giwap(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct sockaddr *ap_addr, char *extra);
|
||||
int cfg80211_ibss_wext_siwessid(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_point *data, char *ssid);
|
||||
int cfg80211_ibss_wext_giwessid(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_point *data, char *ssid);
|
||||
|
||||
int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_freq *freq, char *extra);
|
||||
int cfg80211_mgd_wext_giwfreq(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_freq *freq, char *extra);
|
||||
int cfg80211_mgd_wext_siwap(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct sockaddr *ap_addr, char *extra);
|
||||
int cfg80211_mgd_wext_giwap(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct sockaddr *ap_addr, char *extra);
|
||||
int cfg80211_mgd_wext_siwessid(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_point *data, char *ssid);
|
||||
int cfg80211_mgd_wext_giwessid(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_point *data, char *ssid);
|
||||
|
||||
int cfg80211_wext_siwmlme(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_point *data, char *extra);
|
||||
int cfg80211_wext_siwgenie(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_point *data, char *extra);
|
||||
|
||||
|
||||
int cfg80211_wext_freq(struct iw_freq *freq);
|
||||
|
||||
|
||||
extern const struct iw_handler_def cfg80211_wext_handler;
|
||||
#endif /* __WEXT_COMPAT */
|
1088
net/wireless/wext-core.c
Normal file
1088
net/wireless/wext-core.c
Normal file
File diff suppressed because it is too large
Load diff
249
net/wireless/wext-priv.c
Normal file
249
net/wireless/wext-priv.c
Normal file
|
@ -0,0 +1,249 @@
|
|||
/*
|
||||
* This file implement the Wireless Extensions priv API.
|
||||
*
|
||||
* Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
|
||||
* Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
|
||||
* Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
|
||||
*
|
||||
* (As all part of the Linux kernel, this file is GPL)
|
||||
*/
|
||||
#include <linux/slab.h>
|
||||
#include <linux/wireless.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <net/iw_handler.h>
|
||||
#include <net/wext.h>
|
||||
|
||||
int iw_handler_get_private(struct net_device * dev,
|
||||
struct iw_request_info * info,
|
||||
union iwreq_data * wrqu,
|
||||
char * extra)
|
||||
{
|
||||
/* Check if the driver has something to export */
|
||||
if ((dev->wireless_handlers->num_private_args == 0) ||
|
||||
(dev->wireless_handlers->private_args == NULL))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* Check if there is enough buffer up there */
|
||||
if (wrqu->data.length < dev->wireless_handlers->num_private_args) {
|
||||
/* User space can't know in advance how large the buffer
|
||||
* needs to be. Give it a hint, so that we can support
|
||||
* any size buffer we want somewhat efficiently... */
|
||||
wrqu->data.length = dev->wireless_handlers->num_private_args;
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
/* Set the number of available ioctls. */
|
||||
wrqu->data.length = dev->wireless_handlers->num_private_args;
|
||||
|
||||
/* Copy structure to the user buffer. */
|
||||
memcpy(extra, dev->wireless_handlers->private_args,
|
||||
sizeof(struct iw_priv_args) * wrqu->data.length);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Size (in bytes) of the various private data types */
|
||||
static const char iw_priv_type_size[] = {
|
||||
0, /* IW_PRIV_TYPE_NONE */
|
||||
1, /* IW_PRIV_TYPE_BYTE */
|
||||
1, /* IW_PRIV_TYPE_CHAR */
|
||||
0, /* Not defined */
|
||||
sizeof(__u32), /* IW_PRIV_TYPE_INT */
|
||||
sizeof(struct iw_freq), /* IW_PRIV_TYPE_FLOAT */
|
||||
sizeof(struct sockaddr), /* IW_PRIV_TYPE_ADDR */
|
||||
0, /* Not defined */
|
||||
};
|
||||
|
||||
static int get_priv_size(__u16 args)
|
||||
{
|
||||
int num = args & IW_PRIV_SIZE_MASK;
|
||||
int type = (args & IW_PRIV_TYPE_MASK) >> 12;
|
||||
|
||||
return num * iw_priv_type_size[type];
|
||||
}
|
||||
|
||||
static int adjust_priv_size(__u16 args, struct iw_point *iwp)
|
||||
{
|
||||
int num = iwp->length;
|
||||
int max = args & IW_PRIV_SIZE_MASK;
|
||||
int type = (args & IW_PRIV_TYPE_MASK) >> 12;
|
||||
|
||||
/* Make sure the driver doesn't goof up */
|
||||
if (max < num)
|
||||
num = max;
|
||||
|
||||
return num * iw_priv_type_size[type];
|
||||
}
|
||||
|
||||
/*
|
||||
* Wrapper to call a private Wireless Extension handler.
|
||||
* We do various checks and also take care of moving data between
|
||||
* user space and kernel space.
|
||||
* It's not as nice and slimline as the standard wrapper. The cause
|
||||
* is struct iw_priv_args, which was not really designed for the
|
||||
* job we are going here.
|
||||
*
|
||||
* IMPORTANT : This function prevent to set and get data on the same
|
||||
* IOCTL and enforce the SET/GET convention. Not doing it would be
|
||||
* far too hairy...
|
||||
* If you need to set and get data at the same time, please don't use
|
||||
* a iw_handler but process it in your ioctl handler (i.e. use the
|
||||
* old driver API).
|
||||
*/
|
||||
static int get_priv_descr_and_size(struct net_device *dev, unsigned int cmd,
|
||||
const struct iw_priv_args **descrp)
|
||||
{
|
||||
const struct iw_priv_args *descr;
|
||||
int i, extra_size;
|
||||
|
||||
descr = NULL;
|
||||
for (i = 0; i < dev->wireless_handlers->num_private_args; i++) {
|
||||
if (cmd == dev->wireless_handlers->private_args[i].cmd) {
|
||||
descr = &dev->wireless_handlers->private_args[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
extra_size = 0;
|
||||
if (descr) {
|
||||
if (IW_IS_SET(cmd)) {
|
||||
int offset = 0; /* For sub-ioctls */
|
||||
/* Check for sub-ioctl handler */
|
||||
if (descr->name[0] == '\0')
|
||||
/* Reserve one int for sub-ioctl index */
|
||||
offset = sizeof(__u32);
|
||||
|
||||
/* Size of set arguments */
|
||||
extra_size = get_priv_size(descr->set_args);
|
||||
|
||||
/* Does it fits in iwr ? */
|
||||
if ((descr->set_args & IW_PRIV_SIZE_FIXED) &&
|
||||
((extra_size + offset) <= IFNAMSIZ))
|
||||
extra_size = 0;
|
||||
} else {
|
||||
/* Size of get arguments */
|
||||
extra_size = get_priv_size(descr->get_args);
|
||||
|
||||
/* Does it fits in iwr ? */
|
||||
if ((descr->get_args & IW_PRIV_SIZE_FIXED) &&
|
||||
(extra_size <= IFNAMSIZ))
|
||||
extra_size = 0;
|
||||
}
|
||||
}
|
||||
*descrp = descr;
|
||||
return extra_size;
|
||||
}
|
||||
|
||||
static int ioctl_private_iw_point(struct iw_point *iwp, unsigned int cmd,
|
||||
const struct iw_priv_args *descr,
|
||||
iw_handler handler, struct net_device *dev,
|
||||
struct iw_request_info *info, int extra_size)
|
||||
{
|
||||
char *extra;
|
||||
int err;
|
||||
|
||||
/* Check what user space is giving us */
|
||||
if (IW_IS_SET(cmd)) {
|
||||
if (!iwp->pointer && iwp->length != 0)
|
||||
return -EFAULT;
|
||||
|
||||
if (iwp->length > (descr->set_args & IW_PRIV_SIZE_MASK))
|
||||
return -E2BIG;
|
||||
} else if (!iwp->pointer)
|
||||
return -EFAULT;
|
||||
|
||||
extra = kzalloc(extra_size, GFP_KERNEL);
|
||||
if (!extra)
|
||||
return -ENOMEM;
|
||||
|
||||
/* If it is a SET, get all the extra data in here */
|
||||
if (IW_IS_SET(cmd) && (iwp->length != 0)) {
|
||||
if (copy_from_user(extra, iwp->pointer, extra_size)) {
|
||||
err = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Call the handler */
|
||||
err = handler(dev, info, (union iwreq_data *) iwp, extra);
|
||||
|
||||
/* If we have something to return to the user */
|
||||
if (!err && IW_IS_GET(cmd)) {
|
||||
/* Adjust for the actual length if it's variable,
|
||||
* avoid leaking kernel bits outside.
|
||||
*/
|
||||
if (!(descr->get_args & IW_PRIV_SIZE_FIXED))
|
||||
extra_size = adjust_priv_size(descr->get_args, iwp);
|
||||
|
||||
if (copy_to_user(iwp->pointer, extra, extra_size))
|
||||
err = -EFAULT;
|
||||
}
|
||||
|
||||
out:
|
||||
kfree(extra);
|
||||
return err;
|
||||
}
|
||||
|
||||
int ioctl_private_call(struct net_device *dev, struct iwreq *iwr,
|
||||
unsigned int cmd, struct iw_request_info *info,
|
||||
iw_handler handler)
|
||||
{
|
||||
int extra_size = 0, ret = -EINVAL;
|
||||
const struct iw_priv_args *descr;
|
||||
|
||||
extra_size = get_priv_descr_and_size(dev, cmd, &descr);
|
||||
|
||||
/* Check if we have a pointer to user space data or not. */
|
||||
if (extra_size == 0) {
|
||||
/* No extra arguments. Trivial to handle */
|
||||
ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u));
|
||||
} else {
|
||||
ret = ioctl_private_iw_point(&iwr->u.data, cmd, descr,
|
||||
handler, dev, info, extra_size);
|
||||
}
|
||||
|
||||
/* Call commit handler if needed and defined */
|
||||
if (ret == -EIWCOMMIT)
|
||||
ret = call_commit_handler(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
int compat_private_call(struct net_device *dev, struct iwreq *iwr,
|
||||
unsigned int cmd, struct iw_request_info *info,
|
||||
iw_handler handler)
|
||||
{
|
||||
const struct iw_priv_args *descr;
|
||||
int ret, extra_size;
|
||||
|
||||
extra_size = get_priv_descr_and_size(dev, cmd, &descr);
|
||||
|
||||
/* Check if we have a pointer to user space data or not. */
|
||||
if (extra_size == 0) {
|
||||
/* No extra arguments. Trivial to handle */
|
||||
ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u));
|
||||
} else {
|
||||
struct compat_iw_point *iwp_compat;
|
||||
struct iw_point iwp;
|
||||
|
||||
iwp_compat = (struct compat_iw_point *) &iwr->u.data;
|
||||
iwp.pointer = compat_ptr(iwp_compat->pointer);
|
||||
iwp.length = iwp_compat->length;
|
||||
iwp.flags = iwp_compat->flags;
|
||||
|
||||
ret = ioctl_private_iw_point(&iwp, cmd, descr,
|
||||
handler, dev, info, extra_size);
|
||||
|
||||
iwp_compat->pointer = ptr_to_compat(iwp.pointer);
|
||||
iwp_compat->length = iwp.length;
|
||||
iwp_compat->flags = iwp.flags;
|
||||
}
|
||||
|
||||
/* Call commit handler if needed and defined */
|
||||
if (ret == -EIWCOMMIT)
|
||||
ret = call_commit_handler(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
156
net/wireless/wext-proc.c
Normal file
156
net/wireless/wext-proc.c
Normal file
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* This file implement the Wireless Extensions proc API.
|
||||
*
|
||||
* Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
|
||||
* Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
|
||||
*
|
||||
* (As all part of the Linux kernel, this file is GPL)
|
||||
*/
|
||||
|
||||
/*
|
||||
* The /proc/net/wireless file is a human readable user-space interface
|
||||
* exporting various wireless specific statistics from the wireless devices.
|
||||
* This is the most popular part of the Wireless Extensions ;-)
|
||||
*
|
||||
* This interface is a pure clone of /proc/net/dev (in net/core/dev.c).
|
||||
* The content of the file is basically the content of "struct iw_statistics".
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/wireless.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <net/iw_handler.h>
|
||||
#include <net/wext.h>
|
||||
|
||||
|
||||
static void wireless_seq_printf_stats(struct seq_file *seq,
|
||||
struct net_device *dev)
|
||||
{
|
||||
/* Get stats from the driver */
|
||||
struct iw_statistics *stats = get_wireless_stats(dev);
|
||||
static struct iw_statistics nullstats = {};
|
||||
|
||||
/* show device if it's wireless regardless of current stats */
|
||||
if (!stats) {
|
||||
#ifdef CONFIG_WIRELESS_EXT
|
||||
if (dev->wireless_handlers)
|
||||
stats = &nullstats;
|
||||
#endif
|
||||
#ifdef CONFIG_CFG80211
|
||||
if (dev->ieee80211_ptr)
|
||||
stats = &nullstats;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (stats) {
|
||||
seq_printf(seq, "%6s: %04x %3d%c %3d%c %3d%c %6d %6d %6d "
|
||||
"%6d %6d %6d\n",
|
||||
dev->name, stats->status, stats->qual.qual,
|
||||
stats->qual.updated & IW_QUAL_QUAL_UPDATED
|
||||
? '.' : ' ',
|
||||
((__s32) stats->qual.level) -
|
||||
((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
|
||||
stats->qual.updated & IW_QUAL_LEVEL_UPDATED
|
||||
? '.' : ' ',
|
||||
((__s32) stats->qual.noise) -
|
||||
((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
|
||||
stats->qual.updated & IW_QUAL_NOISE_UPDATED
|
||||
? '.' : ' ',
|
||||
stats->discard.nwid, stats->discard.code,
|
||||
stats->discard.fragment, stats->discard.retries,
|
||||
stats->discard.misc, stats->miss.beacon);
|
||||
|
||||
if (stats != &nullstats)
|
||||
stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------- */
|
||||
/*
|
||||
* Print info for /proc/net/wireless (print all entries)
|
||||
*/
|
||||
static int wireless_dev_seq_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
might_sleep();
|
||||
|
||||
if (v == SEQ_START_TOKEN)
|
||||
seq_printf(seq, "Inter-| sta-| Quality | Discarded "
|
||||
"packets | Missed | WE\n"
|
||||
" face | tus | link level noise | nwid "
|
||||
"crypt frag retry misc | beacon | %d\n",
|
||||
WIRELESS_EXT);
|
||||
else
|
||||
wireless_seq_printf_stats(seq, v);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *wireless_dev_seq_start(struct seq_file *seq, loff_t *pos)
|
||||
{
|
||||
struct net *net = seq_file_net(seq);
|
||||
loff_t off;
|
||||
struct net_device *dev;
|
||||
|
||||
rtnl_lock();
|
||||
if (!*pos)
|
||||
return SEQ_START_TOKEN;
|
||||
|
||||
off = 1;
|
||||
for_each_netdev(net, dev)
|
||||
if (off++ == *pos)
|
||||
return dev;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void *wireless_dev_seq_next(struct seq_file *seq, void *v, loff_t *pos)
|
||||
{
|
||||
struct net *net = seq_file_net(seq);
|
||||
|
||||
++*pos;
|
||||
|
||||
return v == SEQ_START_TOKEN ?
|
||||
first_net_device(net) : next_net_device(v);
|
||||
}
|
||||
|
||||
static void wireless_dev_seq_stop(struct seq_file *seq, void *v)
|
||||
{
|
||||
rtnl_unlock();
|
||||
}
|
||||
|
||||
static const struct seq_operations wireless_seq_ops = {
|
||||
.start = wireless_dev_seq_start,
|
||||
.next = wireless_dev_seq_next,
|
||||
.stop = wireless_dev_seq_stop,
|
||||
.show = wireless_dev_seq_show,
|
||||
};
|
||||
|
||||
static int seq_open_wireless(struct inode *inode, struct file *file)
|
||||
{
|
||||
return seq_open_net(inode, file, &wireless_seq_ops,
|
||||
sizeof(struct seq_net_private));
|
||||
}
|
||||
|
||||
static const struct file_operations wireless_seq_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = seq_open_wireless,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release_net,
|
||||
};
|
||||
|
||||
int __net_init wext_proc_init(struct net *net)
|
||||
{
|
||||
/* Create /proc/net/wireless entry */
|
||||
if (!proc_create("wireless", S_IRUGO, net->proc_net,
|
||||
&wireless_seq_fops))
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __net_exit wext_proc_exit(struct net *net)
|
||||
{
|
||||
remove_proc_entry("wireless", net->proc_net);
|
||||
}
|
414
net/wireless/wext-sme.c
Normal file
414
net/wireless/wext-sme.c
Normal file
|
@ -0,0 +1,414 @@
|
|||
/*
|
||||
* cfg80211 wext compat for managed mode.
|
||||
*
|
||||
* Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
|
||||
* Copyright (C) 2009 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/export.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/slab.h>
|
||||
#include <net/cfg80211.h>
|
||||
#include <net/cfg80211-wext.h>
|
||||
#include "wext-compat.h"
|
||||
#include "nl80211.h"
|
||||
|
||||
int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev)
|
||||
{
|
||||
struct cfg80211_cached_keys *ck = NULL;
|
||||
const u8 *prev_bssid = NULL;
|
||||
int err, i;
|
||||
|
||||
ASSERT_RTNL();
|
||||
ASSERT_WDEV_LOCK(wdev);
|
||||
|
||||
if (!netif_running(wdev->netdev))
|
||||
return 0;
|
||||
|
||||
wdev->wext.connect.ie = wdev->wext.ie;
|
||||
wdev->wext.connect.ie_len = wdev->wext.ie_len;
|
||||
|
||||
/* Use default background scan period */
|
||||
wdev->wext.connect.bg_scan_period = -1;
|
||||
|
||||
if (wdev->wext.keys) {
|
||||
wdev->wext.keys->def = wdev->wext.default_key;
|
||||
wdev->wext.keys->defmgmt = wdev->wext.default_mgmt_key;
|
||||
if (wdev->wext.default_key != -1)
|
||||
wdev->wext.connect.privacy = true;
|
||||
}
|
||||
|
||||
if (!wdev->wext.connect.ssid_len)
|
||||
return 0;
|
||||
|
||||
if (wdev->wext.keys) {
|
||||
ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL);
|
||||
if (!ck)
|
||||
return -ENOMEM;
|
||||
for (i = 0; i < 6; i++)
|
||||
ck->params[i].key = ck->data[i];
|
||||
}
|
||||
|
||||
if (wdev->wext.prev_bssid_valid)
|
||||
prev_bssid = wdev->wext.prev_bssid;
|
||||
|
||||
err = cfg80211_connect(rdev, wdev->netdev,
|
||||
&wdev->wext.connect, ck, prev_bssid);
|
||||
if (err)
|
||||
kzfree(ck);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_freq *wextfreq, char *extra)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||
struct ieee80211_channel *chan = NULL;
|
||||
int err, freq;
|
||||
|
||||
/* call only for station! */
|
||||
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
|
||||
return -EINVAL;
|
||||
|
||||
freq = cfg80211_wext_freq(wextfreq);
|
||||
if (freq < 0)
|
||||
return freq;
|
||||
|
||||
if (freq) {
|
||||
chan = ieee80211_get_channel(wdev->wiphy, freq);
|
||||
if (!chan)
|
||||
return -EINVAL;
|
||||
if (chan->flags & IEEE80211_CHAN_DISABLED)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
wdev_lock(wdev);
|
||||
|
||||
if (wdev->conn) {
|
||||
bool event = true;
|
||||
|
||||
if (wdev->wext.connect.channel == chan) {
|
||||
err = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* if SSID set, we'll try right again, avoid event */
|
||||
if (wdev->wext.connect.ssid_len)
|
||||
event = false;
|
||||
err = cfg80211_disconnect(rdev, dev,
|
||||
WLAN_REASON_DEAUTH_LEAVING, event);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
wdev->wext.connect.channel = chan;
|
||||
|
||||
/*
|
||||
* SSID is not set, we just want to switch monitor channel,
|
||||
* this is really just backward compatibility, if the SSID
|
||||
* is set then we use the channel to select the BSS to use
|
||||
* to connect to instead. If we were connected on another
|
||||
* channel we disconnected above and reconnect below.
|
||||
*/
|
||||
if (chan && !wdev->wext.connect.ssid_len) {
|
||||
struct cfg80211_chan_def chandef = {
|
||||
.width = NL80211_CHAN_WIDTH_20_NOHT,
|
||||
.center_freq1 = freq,
|
||||
};
|
||||
|
||||
chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq);
|
||||
if (chandef.chan)
|
||||
err = cfg80211_set_monitor_channel(rdev, &chandef);
|
||||
else
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = cfg80211_mgd_wext_connect(rdev, wdev);
|
||||
out:
|
||||
wdev_unlock(wdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
int cfg80211_mgd_wext_giwfreq(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_freq *freq, char *extra)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct ieee80211_channel *chan = NULL;
|
||||
|
||||
/* call only for station! */
|
||||
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
|
||||
return -EINVAL;
|
||||
|
||||
wdev_lock(wdev);
|
||||
if (wdev->current_bss)
|
||||
chan = wdev->current_bss->pub.channel;
|
||||
else if (wdev->wext.connect.channel)
|
||||
chan = wdev->wext.connect.channel;
|
||||
wdev_unlock(wdev);
|
||||
|
||||
if (chan) {
|
||||
freq->m = chan->center_freq;
|
||||
freq->e = 6;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* no channel if not joining */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int cfg80211_mgd_wext_siwessid(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_point *data, char *ssid)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||
size_t len = data->length;
|
||||
int err;
|
||||
|
||||
/* call only for station! */
|
||||
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
|
||||
return -EINVAL;
|
||||
|
||||
if (!data->flags)
|
||||
len = 0;
|
||||
|
||||
/* iwconfig uses nul termination in SSID.. */
|
||||
if (len > 0 && ssid[len - 1] == '\0')
|
||||
len--;
|
||||
|
||||
wdev_lock(wdev);
|
||||
|
||||
err = 0;
|
||||
|
||||
if (wdev->conn) {
|
||||
bool event = true;
|
||||
|
||||
if (wdev->wext.connect.ssid && len &&
|
||||
len == wdev->wext.connect.ssid_len &&
|
||||
memcmp(wdev->wext.connect.ssid, ssid, len) == 0)
|
||||
goto out;
|
||||
|
||||
/* if SSID set now, we'll try to connect, avoid event */
|
||||
if (len)
|
||||
event = false;
|
||||
err = cfg80211_disconnect(rdev, dev,
|
||||
WLAN_REASON_DEAUTH_LEAVING, event);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
wdev->wext.prev_bssid_valid = false;
|
||||
wdev->wext.connect.ssid = wdev->wext.ssid;
|
||||
memcpy(wdev->wext.ssid, ssid, len);
|
||||
wdev->wext.connect.ssid_len = len;
|
||||
|
||||
wdev->wext.connect.crypto.control_port = false;
|
||||
wdev->wext.connect.crypto.control_port_ethertype =
|
||||
cpu_to_be16(ETH_P_PAE);
|
||||
|
||||
err = cfg80211_mgd_wext_connect(rdev, wdev);
|
||||
out:
|
||||
wdev_unlock(wdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
int cfg80211_mgd_wext_giwessid(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_point *data, char *ssid)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
|
||||
/* call only for station! */
|
||||
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
|
||||
return -EINVAL;
|
||||
|
||||
data->flags = 0;
|
||||
|
||||
wdev_lock(wdev);
|
||||
if (wdev->current_bss) {
|
||||
const u8 *ie;
|
||||
|
||||
rcu_read_lock();
|
||||
ie = ieee80211_bss_get_ie(&wdev->current_bss->pub,
|
||||
WLAN_EID_SSID);
|
||||
if (ie) {
|
||||
data->flags = 1;
|
||||
data->length = ie[1];
|
||||
memcpy(ssid, ie + 2, data->length);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
} else if (wdev->wext.connect.ssid && wdev->wext.connect.ssid_len) {
|
||||
data->flags = 1;
|
||||
data->length = wdev->wext.connect.ssid_len;
|
||||
memcpy(ssid, wdev->wext.connect.ssid, data->length);
|
||||
}
|
||||
wdev_unlock(wdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cfg80211_mgd_wext_siwap(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct sockaddr *ap_addr, char *extra)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||
u8 *bssid = ap_addr->sa_data;
|
||||
int err;
|
||||
|
||||
/* call only for station! */
|
||||
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
|
||||
return -EINVAL;
|
||||
|
||||
if (ap_addr->sa_family != ARPHRD_ETHER)
|
||||
return -EINVAL;
|
||||
|
||||
/* automatic mode */
|
||||
if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid))
|
||||
bssid = NULL;
|
||||
|
||||
wdev_lock(wdev);
|
||||
|
||||
if (wdev->conn) {
|
||||
err = 0;
|
||||
/* both automatic */
|
||||
if (!bssid && !wdev->wext.connect.bssid)
|
||||
goto out;
|
||||
|
||||
/* fixed already - and no change */
|
||||
if (wdev->wext.connect.bssid && bssid &&
|
||||
ether_addr_equal(bssid, wdev->wext.connect.bssid))
|
||||
goto out;
|
||||
|
||||
err = cfg80211_disconnect(rdev, dev,
|
||||
WLAN_REASON_DEAUTH_LEAVING, false);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (bssid) {
|
||||
memcpy(wdev->wext.bssid, bssid, ETH_ALEN);
|
||||
wdev->wext.connect.bssid = wdev->wext.bssid;
|
||||
} else
|
||||
wdev->wext.connect.bssid = NULL;
|
||||
|
||||
err = cfg80211_mgd_wext_connect(rdev, wdev);
|
||||
out:
|
||||
wdev_unlock(wdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
int cfg80211_mgd_wext_giwap(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct sockaddr *ap_addr, char *extra)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
|
||||
/* call only for station! */
|
||||
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
|
||||
return -EINVAL;
|
||||
|
||||
ap_addr->sa_family = ARPHRD_ETHER;
|
||||
|
||||
wdev_lock(wdev);
|
||||
if (wdev->current_bss)
|
||||
memcpy(ap_addr->sa_data, wdev->current_bss->pub.bssid, ETH_ALEN);
|
||||
else
|
||||
memset(ap_addr->sa_data, 0, ETH_ALEN);
|
||||
wdev_unlock(wdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cfg80211_wext_siwgenie(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_point *data, char *extra)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||
u8 *ie = extra;
|
||||
int ie_len = data->length, err;
|
||||
|
||||
if (wdev->iftype != NL80211_IFTYPE_STATION)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!ie_len)
|
||||
ie = NULL;
|
||||
|
||||
wdev_lock(wdev);
|
||||
|
||||
/* no change */
|
||||
err = 0;
|
||||
if (wdev->wext.ie_len == ie_len &&
|
||||
memcmp(wdev->wext.ie, ie, ie_len) == 0)
|
||||
goto out;
|
||||
|
||||
if (ie_len) {
|
||||
ie = kmemdup(extra, ie_len, GFP_KERNEL);
|
||||
if (!ie) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
} else
|
||||
ie = NULL;
|
||||
|
||||
kfree(wdev->wext.ie);
|
||||
wdev->wext.ie = ie;
|
||||
wdev->wext.ie_len = ie_len;
|
||||
|
||||
if (wdev->conn) {
|
||||
err = cfg80211_disconnect(rdev, dev,
|
||||
WLAN_REASON_DEAUTH_LEAVING, false);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* userspace better not think we'll reconnect */
|
||||
err = 0;
|
||||
out:
|
||||
wdev_unlock(wdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
int cfg80211_wext_siwmlme(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_point *data, char *extra)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct iw_mlme *mlme = (struct iw_mlme *)extra;
|
||||
struct cfg80211_registered_device *rdev;
|
||||
int err;
|
||||
|
||||
if (!wdev)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
rdev = wiphy_to_rdev(wdev->wiphy);
|
||||
|
||||
if (wdev->iftype != NL80211_IFTYPE_STATION)
|
||||
return -EINVAL;
|
||||
|
||||
if (mlme->addr.sa_family != ARPHRD_ETHER)
|
||||
return -EINVAL;
|
||||
|
||||
wdev_lock(wdev);
|
||||
switch (mlme->cmd) {
|
||||
case IW_MLME_DEAUTH:
|
||||
case IW_MLME_DISASSOC:
|
||||
err = cfg80211_disconnect(rdev, dev, mlme->reason_code, true);
|
||||
break;
|
||||
default:
|
||||
err = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
wdev_unlock(wdev);
|
||||
|
||||
return err;
|
||||
}
|
232
net/wireless/wext-spy.c
Normal file
232
net/wireless/wext-spy.c
Normal file
|
@ -0,0 +1,232 @@
|
|||
/*
|
||||
* This file implement the Wireless Extensions spy API.
|
||||
*
|
||||
* Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
|
||||
* Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
|
||||
*
|
||||
* (As all part of the Linux kernel, this file is GPL)
|
||||
*/
|
||||
|
||||
#include <linux/wireless.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/export.h>
|
||||
#include <net/iw_handler.h>
|
||||
#include <net/arp.h>
|
||||
#include <net/wext.h>
|
||||
|
||||
static inline struct iw_spy_data *get_spydata(struct net_device *dev)
|
||||
{
|
||||
/* This is the new way */
|
||||
if (dev->wireless_data)
|
||||
return dev->wireless_data->spy_data;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int iw_handler_set_spy(struct net_device * dev,
|
||||
struct iw_request_info * info,
|
||||
union iwreq_data * wrqu,
|
||||
char * extra)
|
||||
{
|
||||
struct iw_spy_data * spydata = get_spydata(dev);
|
||||
struct sockaddr * address = (struct sockaddr *) extra;
|
||||
|
||||
/* Make sure driver is not buggy or using the old API */
|
||||
if (!spydata)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* Disable spy collection while we copy the addresses.
|
||||
* While we copy addresses, any call to wireless_spy_update()
|
||||
* will NOP. This is OK, as anyway the addresses are changing. */
|
||||
spydata->spy_number = 0;
|
||||
|
||||
/* We want to operate without locking, because wireless_spy_update()
|
||||
* most likely will happen in the interrupt handler, and therefore
|
||||
* have its own locking constraints and needs performance.
|
||||
* The rtnl_lock() make sure we don't race with the other iw_handlers.
|
||||
* This make sure wireless_spy_update() "see" that the spy list
|
||||
* is temporarily disabled. */
|
||||
smp_wmb();
|
||||
|
||||
/* Are there are addresses to copy? */
|
||||
if (wrqu->data.length > 0) {
|
||||
int i;
|
||||
|
||||
/* Copy addresses */
|
||||
for (i = 0; i < wrqu->data.length; i++)
|
||||
memcpy(spydata->spy_address[i], address[i].sa_data,
|
||||
ETH_ALEN);
|
||||
/* Reset stats */
|
||||
memset(spydata->spy_stat, 0,
|
||||
sizeof(struct iw_quality) * IW_MAX_SPY);
|
||||
}
|
||||
|
||||
/* Make sure above is updated before re-enabling */
|
||||
smp_wmb();
|
||||
|
||||
/* Enable addresses */
|
||||
spydata->spy_number = wrqu->data.length;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(iw_handler_set_spy);
|
||||
|
||||
int iw_handler_get_spy(struct net_device * dev,
|
||||
struct iw_request_info * info,
|
||||
union iwreq_data * wrqu,
|
||||
char * extra)
|
||||
{
|
||||
struct iw_spy_data * spydata = get_spydata(dev);
|
||||
struct sockaddr * address = (struct sockaddr *) extra;
|
||||
int i;
|
||||
|
||||
/* Make sure driver is not buggy or using the old API */
|
||||
if (!spydata)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
wrqu->data.length = spydata->spy_number;
|
||||
|
||||
/* Copy addresses. */
|
||||
for (i = 0; i < spydata->spy_number; i++) {
|
||||
memcpy(address[i].sa_data, spydata->spy_address[i], ETH_ALEN);
|
||||
address[i].sa_family = AF_UNIX;
|
||||
}
|
||||
/* Copy stats to the user buffer (just after). */
|
||||
if (spydata->spy_number > 0)
|
||||
memcpy(extra + (sizeof(struct sockaddr) *spydata->spy_number),
|
||||
spydata->spy_stat,
|
||||
sizeof(struct iw_quality) * spydata->spy_number);
|
||||
/* Reset updated flags. */
|
||||
for (i = 0; i < spydata->spy_number; i++)
|
||||
spydata->spy_stat[i].updated &= ~IW_QUAL_ALL_UPDATED;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(iw_handler_get_spy);
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/*
|
||||
* Standard Wireless Handler : set spy threshold
|
||||
*/
|
||||
int iw_handler_set_thrspy(struct net_device * dev,
|
||||
struct iw_request_info *info,
|
||||
union iwreq_data * wrqu,
|
||||
char * extra)
|
||||
{
|
||||
struct iw_spy_data * spydata = get_spydata(dev);
|
||||
struct iw_thrspy * threshold = (struct iw_thrspy *) extra;
|
||||
|
||||
/* Make sure driver is not buggy or using the old API */
|
||||
if (!spydata)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* Just do it */
|
||||
memcpy(&(spydata->spy_thr_low), &(threshold->low),
|
||||
2 * sizeof(struct iw_quality));
|
||||
|
||||
/* Clear flag */
|
||||
memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under));
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(iw_handler_set_thrspy);
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/*
|
||||
* Standard Wireless Handler : get spy threshold
|
||||
*/
|
||||
int iw_handler_get_thrspy(struct net_device * dev,
|
||||
struct iw_request_info *info,
|
||||
union iwreq_data * wrqu,
|
||||
char * extra)
|
||||
{
|
||||
struct iw_spy_data * spydata = get_spydata(dev);
|
||||
struct iw_thrspy * threshold = (struct iw_thrspy *) extra;
|
||||
|
||||
/* Make sure driver is not buggy or using the old API */
|
||||
if (!spydata)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* Just do it */
|
||||
memcpy(&(threshold->low), &(spydata->spy_thr_low),
|
||||
2 * sizeof(struct iw_quality));
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(iw_handler_get_thrspy);
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/*
|
||||
* Prepare and send a Spy Threshold event
|
||||
*/
|
||||
static void iw_send_thrspy_event(struct net_device * dev,
|
||||
struct iw_spy_data * spydata,
|
||||
unsigned char * address,
|
||||
struct iw_quality * wstats)
|
||||
{
|
||||
union iwreq_data wrqu;
|
||||
struct iw_thrspy threshold;
|
||||
|
||||
/* Init */
|
||||
wrqu.data.length = 1;
|
||||
wrqu.data.flags = 0;
|
||||
/* Copy address */
|
||||
memcpy(threshold.addr.sa_data, address, ETH_ALEN);
|
||||
threshold.addr.sa_family = ARPHRD_ETHER;
|
||||
/* Copy stats */
|
||||
memcpy(&(threshold.qual), wstats, sizeof(struct iw_quality));
|
||||
/* Copy also thresholds */
|
||||
memcpy(&(threshold.low), &(spydata->spy_thr_low),
|
||||
2 * sizeof(struct iw_quality));
|
||||
|
||||
/* Send event to user space */
|
||||
wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------- */
|
||||
/*
|
||||
* Call for the driver to update the spy data.
|
||||
* For now, the spy data is a simple array. As the size of the array is
|
||||
* small, this is good enough. If we wanted to support larger number of
|
||||
* spy addresses, we should use something more efficient...
|
||||
*/
|
||||
void wireless_spy_update(struct net_device * dev,
|
||||
unsigned char * address,
|
||||
struct iw_quality * wstats)
|
||||
{
|
||||
struct iw_spy_data * spydata = get_spydata(dev);
|
||||
int i;
|
||||
int match = -1;
|
||||
|
||||
/* Make sure driver is not buggy or using the old API */
|
||||
if (!spydata)
|
||||
return;
|
||||
|
||||
/* Update all records that match */
|
||||
for (i = 0; i < spydata->spy_number; i++)
|
||||
if (ether_addr_equal(address, spydata->spy_address[i])) {
|
||||
memcpy(&(spydata->spy_stat[i]), wstats,
|
||||
sizeof(struct iw_quality));
|
||||
match = i;
|
||||
}
|
||||
|
||||
/* Generate an event if we cross the spy threshold.
|
||||
* To avoid event storms, we have a simple hysteresis : we generate
|
||||
* event only when we go under the low threshold or above the
|
||||
* high threshold. */
|
||||
if (match >= 0) {
|
||||
if (spydata->spy_thr_under[match]) {
|
||||
if (wstats->level > spydata->spy_thr_high.level) {
|
||||
spydata->spy_thr_under[match] = 0;
|
||||
iw_send_thrspy_event(dev, spydata,
|
||||
address, wstats);
|
||||
}
|
||||
} else {
|
||||
if (wstats->level < spydata->spy_thr_low.level) {
|
||||
spydata->spy_thr_under[match] = 1;
|
||||
iw_send_thrspy_event(dev, spydata,
|
||||
address, wstats);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(wireless_spy_update);
|
Loading…
Add table
Add a link
Reference in a new issue