2007-05-05 20:45:53 +02:00
|
|
|
/*
|
|
|
|
* BSS client mode implementation
|
2009-01-08 12:31:59 +01:00
|
|
|
* Copyright 2003-2008, Jouni Malinen <j@w1.fi>
|
2007-05-05 20:45:53 +02:00
|
|
|
* Copyright 2004, Instant802 Networks, Inc.
|
|
|
|
* Copyright 2005, Devicescape Software, Inc.
|
|
|
|
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
|
|
|
|
* Copyright 2007, Michael Wu <flamingice@sourmilk.net>
|
2014-09-03 14:24:57 +02:00
|
|
|
* Copyright 2013-2014 Intel Mobile Communications GmbH
|
2015-07-08 14:41:43 +02:00
|
|
|
* Copyright (C) 2015 Intel Deutschland GmbH
|
2007-05-05 20:45:53 +02:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2007-05-09 03:40:27 +02:00
|
|
|
#include <linux/delay.h>
|
2007-05-05 20:45:53 +02:00
|
|
|
#include <linux/if_ether.h>
|
|
|
|
#include <linux/skbuff.h>
|
|
|
|
#include <linux/if_arp.h>
|
|
|
|
#include <linux/etherdevice.h>
|
2011-09-18 19:21:27 +02:00
|
|
|
#include <linux/moduleparam.h>
|
2008-02-25 16:27:46 +01:00
|
|
|
#include <linux/rtnetlink.h>
|
2011-08-25 15:35:03 +02:00
|
|
|
#include <linux/pm_qos.h>
|
mac80211: implement beacon filtering in software
Regardless of whether the hardware implements beacon filtering,
there's no need to process all beacons in software all the time
throughout the stack (mac80211 does a lot, then cfg80211, then
in the future possibly userspace).
This patch implements the "best possible" beacon filtering in
mac80211. "Best possible" means that it can look for changes in
all requested information elements, and distinguish vendor IEs
by their OUI.
In the future, we will add nl80211 API for userspace to request
information elements and vendor IE OUIs to watch -- drivers can
then implement the best they can do while software implements
it fully.
It is unclear whether or not this actually saves CPU time, but
the data is all in the cache already so it should be fairly
cheap. The additional _testing_, however, has great benefit;
Without this, and on hardware that doesn't implement beacon
filtering, wrong assumptions about, for example, scan result
updates could quickly creep into code.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-04-16 13:17:26 +02:00
|
|
|
#include <linux/crc32.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 09:04:11 +01:00
|
|
|
#include <linux/slab.h>
|
2011-07-15 17:47:34 +02:00
|
|
|
#include <linux/export.h>
|
2007-05-05 20:45:53 +02:00
|
|
|
#include <net/mac80211.h>
|
2008-09-11 00:01:49 +02:00
|
|
|
#include <asm/unaligned.h>
|
2008-09-08 17:44:22 +02:00
|
|
|
|
2007-05-05 20:45:53 +02:00
|
|
|
#include "ieee80211_i.h"
|
2009-04-23 18:52:52 +02:00
|
|
|
#include "driver-ops.h"
|
2008-04-08 21:14:40 +02:00
|
|
|
#include "rate.h"
|
|
|
|
#include "led.h"
|
2007-05-05 20:45:53 +02:00
|
|
|
|
2013-01-29 15:02:27 +01:00
|
|
|
#define IEEE80211_AUTH_TIMEOUT (HZ / 5)
|
2013-07-29 23:07:43 +02:00
|
|
|
#define IEEE80211_AUTH_TIMEOUT_LONG (HZ / 2)
|
2013-01-29 15:02:27 +01:00
|
|
|
#define IEEE80211_AUTH_TIMEOUT_SHORT (HZ / 10)
|
|
|
|
#define IEEE80211_AUTH_MAX_TRIES 3
|
|
|
|
#define IEEE80211_AUTH_WAIT_ASSOC (HZ * 5)
|
|
|
|
#define IEEE80211_ASSOC_TIMEOUT (HZ / 5)
|
2013-07-29 23:07:43 +02:00
|
|
|
#define IEEE80211_ASSOC_TIMEOUT_LONG (HZ / 2)
|
2013-01-29 15:02:27 +01:00
|
|
|
#define IEEE80211_ASSOC_TIMEOUT_SHORT (HZ / 10)
|
|
|
|
#define IEEE80211_ASSOC_MAX_TRIES 3
|
2012-01-20 13:55:27 +01:00
|
|
|
|
2011-02-05 00:30:24 +01:00
|
|
|
static int max_nullfunc_tries = 2;
|
|
|
|
module_param(max_nullfunc_tries, int, 0644);
|
|
|
|
MODULE_PARM_DESC(max_nullfunc_tries,
|
|
|
|
"Maximum nullfunc tx tries before disconnecting (reason 4).");
|
|
|
|
|
|
|
|
static int max_probe_tries = 5;
|
|
|
|
module_param(max_probe_tries, int, 0644);
|
|
|
|
MODULE_PARM_DESC(max_probe_tries,
|
|
|
|
"Maximum probe tries before disconnecting (reason 4).");
|
2009-07-10 15:29:03 +02:00
|
|
|
|
|
|
|
/*
|
2010-11-19 22:55:38 +01:00
|
|
|
* Beacon loss timeout is calculated as N frames times the
|
|
|
|
* advertised beacon interval. This may need to be somewhat
|
|
|
|
* higher than what hardware might detect to account for
|
|
|
|
* delays in the host processing frames. But since we also
|
|
|
|
* probe on beacon miss before declaring the connection lost
|
|
|
|
* default to what we want.
|
2009-07-10 15:29:03 +02:00
|
|
|
*/
|
2013-03-19 22:19:56 +01:00
|
|
|
static int beacon_loss_count = 7;
|
|
|
|
module_param(beacon_loss_count, int, 0644);
|
|
|
|
MODULE_PARM_DESC(beacon_loss_count,
|
|
|
|
"Number of beacon intervals before we decide beacon was lost.");
|
2010-11-19 22:55:38 +01:00
|
|
|
|
2009-07-10 15:29:03 +02:00
|
|
|
/*
|
|
|
|
* Time the connection can be idle before we probe
|
|
|
|
* it to see if we can still talk to the AP.
|
|
|
|
*/
|
2009-07-31 17:54:23 +02:00
|
|
|
#define IEEE80211_CONNECTION_IDLE_TIME (30 * HZ)
|
2009-07-10 15:29:03 +02:00
|
|
|
/*
|
|
|
|
* Time we wait for a probe response after sending
|
|
|
|
* a probe request because of beacon loss or for
|
|
|
|
* checking the connection still works.
|
|
|
|
*/
|
2011-02-05 00:30:24 +01:00
|
|
|
static int probe_wait_ms = 500;
|
|
|
|
module_param(probe_wait_ms, int, 0644);
|
|
|
|
MODULE_PARM_DESC(probe_wait_ms,
|
|
|
|
"Maximum time(ms) to wait for probe response"
|
|
|
|
" before disconnecting (reason 4).");
|
2007-05-05 20:45:53 +02:00
|
|
|
|
2010-03-30 08:28:30 +02:00
|
|
|
/*
|
|
|
|
* Weight given to the latest Beacon frame when calculating average signal
|
|
|
|
* strength for Beacon frames received in the current BSS. This must be
|
|
|
|
* between 1 and 15.
|
|
|
|
*/
|
|
|
|
#define IEEE80211_SIGNAL_AVE_WEIGHT 3
|
|
|
|
|
2010-08-27 21:22:00 +02:00
|
|
|
/*
|
|
|
|
* How many Beacon frames need to have been used in average signal strength
|
|
|
|
* before starting to indicate signal change events.
|
|
|
|
*/
|
|
|
|
#define IEEE80211_SIGNAL_AVE_MIN_COUNT 4
|
|
|
|
|
2009-07-10 02:39:48 +02:00
|
|
|
/*
|
|
|
|
* We can have multiple work items (and connection probing)
|
|
|
|
* scheduling this timer, but we need to take care to only
|
|
|
|
* reschedule it when it should fire _earlier_ than it was
|
|
|
|
* asked for before, or if it's not pending right now. This
|
|
|
|
* function ensures that. Note that it then is required to
|
|
|
|
* run this function for all timeouts after the first one
|
|
|
|
* has happened -- the work that runs from this timer will
|
|
|
|
* do that.
|
|
|
|
*/
|
2013-05-10 12:32:47 +02:00
|
|
|
static void run_again(struct ieee80211_sub_if_data *sdata,
|
|
|
|
unsigned long timeout)
|
2009-07-10 02:39:48 +02:00
|
|
|
{
|
2013-05-10 12:32:47 +02:00
|
|
|
sdata_assert_lock(sdata);
|
2009-07-10 02:39:48 +02:00
|
|
|
|
2013-05-10 12:32:47 +02:00
|
|
|
if (!timer_pending(&sdata->u.mgd.timer) ||
|
|
|
|
time_before(timeout, sdata->u.mgd.timer.expires))
|
|
|
|
mod_timer(&sdata->u.mgd.timer, timeout);
|
2009-07-10 02:39:48 +02:00
|
|
|
}
|
|
|
|
|
2010-09-16 21:12:32 +02:00
|
|
|
void ieee80211_sta_reset_beacon_monitor(struct ieee80211_sub_if_data *sdata)
|
2009-07-10 15:29:03 +02:00
|
|
|
{
|
2012-01-19 09:29:57 +01:00
|
|
|
if (sdata->vif.driver_flags & IEEE80211_VIF_BEACON_FILTER)
|
2009-07-10 15:29:03 +02:00
|
|
|
return;
|
|
|
|
|
2015-06-02 21:39:54 +02:00
|
|
|
if (ieee80211_hw_check(&sdata->local->hw, CONNECTION_MONITOR))
|
2012-07-18 10:27:27 +02:00
|
|
|
return;
|
|
|
|
|
2009-07-10 15:29:03 +02:00
|
|
|
mod_timer(&sdata->u.mgd.bcn_mon_timer,
|
2010-11-19 22:55:38 +01:00
|
|
|
round_jiffies_up(jiffies + sdata->u.mgd.beacon_timeout));
|
2009-07-10 15:29:03 +02:00
|
|
|
}
|
|
|
|
|
2010-09-16 21:12:29 +02:00
|
|
|
void ieee80211_sta_reset_conn_monitor(struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
2010-09-16 21:12:30 +02:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
|
2011-02-25 14:46:02 +01:00
|
|
|
if (unlikely(!sdata->u.mgd.associated))
|
|
|
|
return;
|
|
|
|
|
2014-02-11 11:30:18 +01:00
|
|
|
ifmgd->probe_send_count = 0;
|
|
|
|
|
2015-06-02 21:39:54 +02:00
|
|
|
if (ieee80211_hw_check(&sdata->local->hw, CONNECTION_MONITOR))
|
2010-09-16 21:12:29 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
mod_timer(&sdata->u.mgd.conn_mon_timer,
|
|
|
|
round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME));
|
|
|
|
}
|
|
|
|
|
2008-09-08 17:44:27 +02:00
|
|
|
static int ecw2cw(int ecw)
|
2008-09-08 17:44:22 +02:00
|
|
|
{
|
2008-09-08 17:44:27 +02:00
|
|
|
return (1 << ecw) - 1;
|
2008-09-08 17:44:22 +02:00
|
|
|
}
|
|
|
|
|
2013-02-08 14:52:32 +01:00
|
|
|
static u32
|
|
|
|
ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct ieee80211_supported_band *sband,
|
|
|
|
struct ieee80211_channel *channel,
|
2014-07-24 10:20:05 +02:00
|
|
|
const struct ieee80211_ht_cap *ht_cap,
|
2013-02-08 14:52:32 +01:00
|
|
|
const struct ieee80211_ht_operation *ht_oper,
|
|
|
|
const struct ieee80211_vht_operation *vht_oper,
|
2013-07-31 11:23:06 +02:00
|
|
|
struct cfg80211_chan_def *chandef, bool tracking)
|
2013-02-08 14:52:32 +01:00
|
|
|
{
|
2013-07-31 11:23:06 +02:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2013-02-08 14:52:32 +01:00
|
|
|
struct cfg80211_chan_def vht_chandef;
|
2014-12-14 09:38:59 +01:00
|
|
|
struct ieee80211_sta_ht_cap sta_ht_cap;
|
2013-02-08 14:52:32 +01:00
|
|
|
u32 ht_cfreq, ret;
|
|
|
|
|
2014-12-14 09:38:59 +01:00
|
|
|
memcpy(&sta_ht_cap, &sband->ht_cap, sizeof(sta_ht_cap));
|
|
|
|
ieee80211_apply_htcap_overrides(sdata, &sta_ht_cap);
|
|
|
|
|
2013-02-08 14:52:32 +01:00
|
|
|
chandef->chan = channel;
|
|
|
|
chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
|
|
|
|
chandef->center_freq1 = channel->center_freq;
|
|
|
|
chandef->center_freq2 = 0;
|
|
|
|
|
2014-12-14 09:38:59 +01:00
|
|
|
if (!ht_cap || !ht_oper || !sta_ht_cap.ht_supported) {
|
2013-02-08 14:52:32 +01:00
|
|
|
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
chandef->width = NL80211_CHAN_WIDTH_20;
|
|
|
|
|
2014-07-24 10:20:05 +02:00
|
|
|
if (!(ht_cap->cap_info &
|
|
|
|
cpu_to_le16(IEEE80211_HT_CAP_SUP_WIDTH_20_40))) {
|
2014-09-04 21:37:55 +02:00
|
|
|
ret = IEEE80211_STA_DISABLE_40MHZ;
|
2014-12-10 20:14:07 +01:00
|
|
|
vht_chandef = *chandef;
|
2014-07-24 10:20:05 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2013-02-08 14:52:32 +01:00
|
|
|
ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan,
|
|
|
|
channel->band);
|
|
|
|
/* check that channel matches the right operating channel */
|
2013-07-31 11:23:06 +02:00
|
|
|
if (!tracking && channel->center_freq != ht_cfreq) {
|
2013-02-08 14:52:32 +01:00
|
|
|
/*
|
|
|
|
* It's possible that some APs are confused here;
|
|
|
|
* Netgear WNDR3700 sometimes reports 4 higher than
|
|
|
|
* the actual channel in association responses, but
|
|
|
|
* since we look at probe response/beacon data here
|
|
|
|
* it should be OK.
|
|
|
|
*/
|
2013-07-31 11:23:06 +02:00
|
|
|
sdata_info(sdata,
|
|
|
|
"Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n",
|
|
|
|
channel->center_freq, ht_cfreq,
|
|
|
|
ht_oper->primary_chan, channel->band);
|
2013-02-08 14:52:32 +01:00
|
|
|
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check 40 MHz support, if we have it */
|
2014-12-14 09:38:59 +01:00
|
|
|
if (sta_ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) {
|
2013-02-08 14:52:32 +01:00
|
|
|
switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
|
|
|
|
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
|
|
|
|
chandef->width = NL80211_CHAN_WIDTH_40;
|
|
|
|
chandef->center_freq1 += 10;
|
|
|
|
break;
|
|
|
|
case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
|
|
|
|
chandef->width = NL80211_CHAN_WIDTH_40;
|
|
|
|
chandef->center_freq1 -= 10;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* 40 MHz (and 80 MHz) must be supported for VHT */
|
|
|
|
ret = IEEE80211_STA_DISABLE_VHT;
|
2013-03-25 18:29:27 +01:00
|
|
|
/* also mark 40 MHz disabled */
|
|
|
|
ret |= IEEE80211_STA_DISABLE_40MHZ;
|
2013-02-08 14:52:32 +01:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!vht_oper || !sband->vht_cap.vht_supported) {
|
|
|
|
ret = IEEE80211_STA_DISABLE_VHT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
vht_chandef.chan = channel;
|
|
|
|
vht_chandef.center_freq1 =
|
|
|
|
ieee80211_channel_to_frequency(vht_oper->center_freq_seg1_idx,
|
|
|
|
channel->band);
|
|
|
|
vht_chandef.center_freq2 = 0;
|
|
|
|
|
|
|
|
switch (vht_oper->chan_width) {
|
|
|
|
case IEEE80211_VHT_CHANWIDTH_USE_HT:
|
|
|
|
vht_chandef.width = chandef->width;
|
2014-02-27 20:47:53 +01:00
|
|
|
vht_chandef.center_freq1 = chandef->center_freq1;
|
2013-02-08 14:52:32 +01:00
|
|
|
break;
|
|
|
|
case IEEE80211_VHT_CHANWIDTH_80MHZ:
|
|
|
|
vht_chandef.width = NL80211_CHAN_WIDTH_80;
|
|
|
|
break;
|
|
|
|
case IEEE80211_VHT_CHANWIDTH_160MHZ:
|
|
|
|
vht_chandef.width = NL80211_CHAN_WIDTH_160;
|
|
|
|
break;
|
|
|
|
case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
|
|
|
|
vht_chandef.width = NL80211_CHAN_WIDTH_80P80;
|
2013-03-28 10:26:17 +01:00
|
|
|
vht_chandef.center_freq2 =
|
|
|
|
ieee80211_channel_to_frequency(
|
|
|
|
vht_oper->center_freq_seg2_idx,
|
|
|
|
channel->band);
|
2013-02-08 14:52:32 +01:00
|
|
|
break;
|
|
|
|
default:
|
2013-07-31 11:23:06 +02:00
|
|
|
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))
|
2013-02-08 15:12:14 +01:00
|
|
|
sdata_info(sdata,
|
|
|
|
"AP VHT operation IE has invalid channel width (%d), disable VHT\n",
|
|
|
|
vht_oper->chan_width);
|
2013-02-08 14:52:32 +01:00
|
|
|
ret = IEEE80211_STA_DISABLE_VHT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cfg80211_chandef_valid(&vht_chandef)) {
|
2013-07-31 11:23:06 +02:00
|
|
|
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))
|
2013-02-08 15:12:14 +01:00
|
|
|
sdata_info(sdata,
|
|
|
|
"AP VHT information is invalid, disable VHT\n");
|
2013-02-08 14:52:32 +01:00
|
|
|
ret = IEEE80211_STA_DISABLE_VHT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cfg80211_chandef_identical(chandef, &vht_chandef)) {
|
|
|
|
ret = 0;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cfg80211_chandef_compatible(chandef, &vht_chandef)) {
|
2013-07-31 11:23:06 +02:00
|
|
|
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))
|
2013-02-08 15:12:14 +01:00
|
|
|
sdata_info(sdata,
|
|
|
|
"AP VHT information doesn't match HT, disable VHT\n");
|
2013-02-08 14:52:32 +01:00
|
|
|
ret = IEEE80211_STA_DISABLE_VHT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
*chandef = vht_chandef;
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
out:
|
2014-02-21 20:34:34 +01:00
|
|
|
/*
|
|
|
|
* When tracking the current AP, don't do any further checks if the
|
|
|
|
* new chandef is identical to the one we're currently using for the
|
|
|
|
* connection. This keeps us from playing ping-pong with regulatory,
|
|
|
|
* without it the following can happen (for example):
|
|
|
|
* - connect to an AP with 80 MHz, world regdom allows 80 MHz
|
|
|
|
* - AP advertises regdom US
|
|
|
|
* - CRDA loads regdom US with 80 MHz prohibited (old database)
|
|
|
|
* - the code below detects an unsupported channel, downgrades, and
|
|
|
|
* we disconnect from the AP in the caller
|
|
|
|
* - disconnect causes CRDA to reload world regdomain and the game
|
|
|
|
* starts anew.
|
|
|
|
* (see https://bugzilla.kernel.org/show_bug.cgi?id=70881)
|
|
|
|
*
|
|
|
|
* It seems possible that there are still scenarios with CSA or real
|
|
|
|
* bandwidth changes where a this could happen, but those cases are
|
|
|
|
* less common and wouldn't completely prevent using the AP.
|
|
|
|
*/
|
|
|
|
if (tracking &&
|
|
|
|
cfg80211_chandef_identical(chandef, &sdata->vif.bss_conf.chandef))
|
|
|
|
return ret;
|
|
|
|
|
2013-02-14 12:13:53 +01:00
|
|
|
/* don't print the message below for VHT mismatch if VHT is disabled */
|
|
|
|
if (ret & IEEE80211_STA_DISABLE_VHT)
|
|
|
|
vht_chandef = *chandef;
|
|
|
|
|
2013-07-31 20:52:03 +02:00
|
|
|
/*
|
|
|
|
* Ignore the DISABLED flag when we're already connected and only
|
|
|
|
* tracking the APs beacon for bandwidth changes - otherwise we
|
|
|
|
* might get disconnected here if we connect to an AP, update our
|
|
|
|
* regulatory information based on the AP's country IE and the
|
|
|
|
* information we have is wrong/outdated and disables the channel
|
|
|
|
* that we're actually using for the connection to the AP.
|
|
|
|
*/
|
2013-02-08 14:52:32 +01:00
|
|
|
while (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
|
2013-07-31 20:52:03 +02:00
|
|
|
tracking ? 0 :
|
|
|
|
IEEE80211_CHAN_DISABLED)) {
|
2013-02-08 14:52:32 +01:00
|
|
|
if (WARN_ON(chandef->width == NL80211_CHAN_WIDTH_20_NOHT)) {
|
|
|
|
ret = IEEE80211_STA_DISABLE_HT |
|
|
|
|
IEEE80211_STA_DISABLE_VHT;
|
2013-07-31 21:12:24 +02:00
|
|
|
break;
|
2013-02-08 14:52:32 +01:00
|
|
|
}
|
|
|
|
|
2013-08-28 13:41:29 +02:00
|
|
|
ret |= ieee80211_chandef_downgrade(chandef);
|
2013-02-08 14:52:32 +01:00
|
|
|
}
|
|
|
|
|
2013-07-31 11:23:06 +02:00
|
|
|
if (chandef->width != vht_chandef.width && !tracking)
|
2013-02-08 14:52:32 +01:00
|
|
|
sdata_info(sdata,
|
|
|
|
"capabilities/regulatory prevented using AP HT/VHT configuration, downgraded\n");
|
|
|
|
|
|
|
|
WARN_ON_ONCE(!cfg80211_chandef_valid(chandef));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-02-08 15:12:14 +01:00
|
|
|
static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct sta_info *sta,
|
2014-07-24 10:20:05 +02:00
|
|
|
const struct ieee80211_ht_cap *ht_cap,
|
2013-02-08 15:12:14 +01:00
|
|
|
const struct ieee80211_ht_operation *ht_oper,
|
|
|
|
const struct ieee80211_vht_operation *vht_oper,
|
|
|
|
const u8 *bssid, u32 *changed)
|
2009-03-30 13:23:35 +02:00
|
|
|
{
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
2013-02-08 15:12:14 +01:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2009-03-30 13:23:35 +02:00
|
|
|
struct ieee80211_supported_band *sband;
|
2012-07-26 17:24:39 +02:00
|
|
|
struct ieee80211_channel *chan;
|
2013-02-08 15:12:14 +01:00
|
|
|
struct cfg80211_chan_def chandef;
|
2009-05-08 20:47:39 +02:00
|
|
|
u16 ht_opmode;
|
2013-02-08 15:12:14 +01:00
|
|
|
u32 flags;
|
|
|
|
enum ieee80211_sta_rx_bandwidth new_sta_bw;
|
|
|
|
int ret;
|
2009-03-30 13:23:35 +02:00
|
|
|
|
2013-02-08 15:12:14 +01:00
|
|
|
/* if HT was/is disabled, don't track any bandwidth changes */
|
|
|
|
if (ifmgd->flags & IEEE80211_STA_DISABLE_HT || !ht_oper)
|
2013-02-07 17:36:12 +01:00
|
|
|
return 0;
|
|
|
|
|
2013-02-08 15:12:14 +01:00
|
|
|
/* don't check VHT if we associated as non-VHT station */
|
|
|
|
if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT)
|
|
|
|
vht_oper = NULL;
|
|
|
|
|
|
|
|
if (WARN_ON_ONCE(!sta))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2013-11-18 18:06:48 +01:00
|
|
|
/*
|
|
|
|
* if bss configuration changed store the new one -
|
|
|
|
* this may be applicable even if channel is identical
|
|
|
|
*/
|
|
|
|
ht_opmode = le16_to_cpu(ht_oper->operation_mode);
|
|
|
|
if (sdata->vif.bss_conf.ht_operation_mode != ht_opmode) {
|
|
|
|
*changed |= BSS_CHANGED_HT;
|
|
|
|
sdata->vif.bss_conf.ht_operation_mode = ht_opmode;
|
|
|
|
}
|
|
|
|
|
2013-02-08 13:15:55 +01:00
|
|
|
chan = sdata->vif.bss_conf.chandef.chan;
|
2012-07-26 17:24:39 +02:00
|
|
|
sband = local->hw.wiphy->bands[chan->band];
|
2009-03-30 13:23:35 +02:00
|
|
|
|
2013-02-08 15:12:14 +01:00
|
|
|
/* calculate new channel (type) based on HT/VHT operation IEs */
|
2014-07-24 10:20:05 +02:00
|
|
|
flags = ieee80211_determine_chantype(sdata, sband, chan,
|
|
|
|
ht_cap, ht_oper, vht_oper,
|
|
|
|
&chandef, true);
|
2013-02-08 15:12:14 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Downgrade the new channel if we associated with restricted
|
|
|
|
* capabilities. For example, if we associated as a 20 MHz STA
|
|
|
|
* to a 40 MHz AP (due to regulatory, capabilities or config
|
|
|
|
* reasons) then switching to a 40 MHz channel now won't do us
|
|
|
|
* any good -- we couldn't use it with the AP.
|
|
|
|
*/
|
|
|
|
if (ifmgd->flags & IEEE80211_STA_DISABLE_80P80MHZ &&
|
|
|
|
chandef.width == NL80211_CHAN_WIDTH_80P80)
|
2013-08-28 13:41:29 +02:00
|
|
|
flags |= ieee80211_chandef_downgrade(&chandef);
|
2013-02-08 15:12:14 +01:00
|
|
|
if (ifmgd->flags & IEEE80211_STA_DISABLE_160MHZ &&
|
|
|
|
chandef.width == NL80211_CHAN_WIDTH_160)
|
2013-08-28 13:41:29 +02:00
|
|
|
flags |= ieee80211_chandef_downgrade(&chandef);
|
2013-02-08 15:12:14 +01:00
|
|
|
if (ifmgd->flags & IEEE80211_STA_DISABLE_40MHZ &&
|
|
|
|
chandef.width > NL80211_CHAN_WIDTH_20)
|
2013-08-28 13:41:29 +02:00
|
|
|
flags |= ieee80211_chandef_downgrade(&chandef);
|
2013-02-08 15:12:14 +01:00
|
|
|
|
|
|
|
if (cfg80211_chandef_identical(&chandef, &sdata->vif.bss_conf.chandef))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
sdata_info(sdata,
|
|
|
|
"AP %pM changed bandwidth, new config is %d MHz, width %d (%d/%d MHz)\n",
|
|
|
|
ifmgd->bssid, chandef.chan->center_freq, chandef.width,
|
|
|
|
chandef.center_freq1, chandef.center_freq2);
|
|
|
|
|
|
|
|
if (flags != (ifmgd->flags & (IEEE80211_STA_DISABLE_HT |
|
|
|
|
IEEE80211_STA_DISABLE_VHT |
|
|
|
|
IEEE80211_STA_DISABLE_40MHZ |
|
|
|
|
IEEE80211_STA_DISABLE_80P80MHZ |
|
|
|
|
IEEE80211_STA_DISABLE_160MHZ)) ||
|
|
|
|
!cfg80211_chandef_valid(&chandef)) {
|
|
|
|
sdata_info(sdata,
|
|
|
|
"AP %pM changed bandwidth in a way we can't support - disconnect\n",
|
|
|
|
ifmgd->bssid);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (chandef.width) {
|
|
|
|
case NL80211_CHAN_WIDTH_20_NOHT:
|
|
|
|
case NL80211_CHAN_WIDTH_20:
|
|
|
|
new_sta_bw = IEEE80211_STA_RX_BW_20;
|
|
|
|
break;
|
2012-11-09 11:39:59 +01:00
|
|
|
case NL80211_CHAN_WIDTH_40:
|
2013-02-08 15:12:14 +01:00
|
|
|
new_sta_bw = IEEE80211_STA_RX_BW_40;
|
2012-03-28 10:58:37 +02:00
|
|
|
break;
|
2013-02-08 15:12:14 +01:00
|
|
|
case NL80211_CHAN_WIDTH_80:
|
|
|
|
new_sta_bw = IEEE80211_STA_RX_BW_80;
|
|
|
|
break;
|
|
|
|
case NL80211_CHAN_WIDTH_80P80:
|
|
|
|
case NL80211_CHAN_WIDTH_160:
|
|
|
|
new_sta_bw = IEEE80211_STA_RX_BW_160;
|
2012-03-28 10:58:37 +02:00
|
|
|
break;
|
2013-02-08 15:12:14 +01:00
|
|
|
default:
|
|
|
|
return -EINVAL;
|
2012-03-28 10:58:37 +02:00
|
|
|
}
|
2009-03-30 13:23:35 +02:00
|
|
|
|
2013-02-08 15:12:14 +01:00
|
|
|
if (new_sta_bw > sta->cur_max_bandwidth)
|
|
|
|
new_sta_bw = sta->cur_max_bandwidth;
|
2012-03-28 10:58:37 +02:00
|
|
|
|
2013-02-08 15:12:14 +01:00
|
|
|
if (new_sta_bw < sta->sta.bandwidth) {
|
|
|
|
sta->sta.bandwidth = new_sta_bw;
|
|
|
|
rate_control_rate_update(local, sband, sta,
|
|
|
|
IEEE80211_RC_BW_CHANGED);
|
|
|
|
}
|
2012-03-28 10:58:37 +02:00
|
|
|
|
2013-02-08 15:12:14 +01:00
|
|
|
ret = ieee80211_vif_change_bandwidth(sdata, &chandef, changed);
|
|
|
|
if (ret) {
|
|
|
|
sdata_info(sdata,
|
|
|
|
"AP %pM changed bandwidth to incompatible one - disconnect\n",
|
|
|
|
ifmgd->bssid);
|
|
|
|
return ret;
|
|
|
|
}
|
2011-09-16 12:02:34 +02:00
|
|
|
|
2013-02-08 15:12:14 +01:00
|
|
|
if (new_sta_bw > sta->sta.bandwidth) {
|
|
|
|
sta->sta.bandwidth = new_sta_bw;
|
|
|
|
rate_control_rate_update(local, sband, sta,
|
|
|
|
IEEE80211_RC_BW_CHANGED);
|
2010-05-05 15:28:27 +02:00
|
|
|
}
|
2009-03-30 13:23:35 +02:00
|
|
|
|
2013-02-08 15:12:14 +01:00
|
|
|
return 0;
|
2009-03-30 13:23:35 +02:00
|
|
|
}
|
|
|
|
|
2008-09-11 00:01:52 +02:00
|
|
|
/* frame sending functions */
|
|
|
|
|
2012-01-20 13:55:27 +01:00
|
|
|
static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata,
|
2012-05-16 23:43:19 +02:00
|
|
|
struct sk_buff *skb, u8 ap_ht_param,
|
2012-01-20 13:55:27 +01:00
|
|
|
struct ieee80211_supported_band *sband,
|
|
|
|
struct ieee80211_channel *channel,
|
|
|
|
enum ieee80211_smps_mode smps)
|
|
|
|
{
|
|
|
|
u8 *pos;
|
|
|
|
u32 flags = channel->flags;
|
|
|
|
u16 cap;
|
|
|
|
struct ieee80211_sta_ht_cap ht_cap;
|
|
|
|
|
|
|
|
BUILD_BUG_ON(sizeof(ht_cap) != sizeof(sband->ht_cap));
|
|
|
|
|
|
|
|
memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
|
|
|
|
ieee80211_apply_htcap_overrides(sdata, &ht_cap);
|
|
|
|
|
|
|
|
/* determine capability flags */
|
|
|
|
cap = ht_cap.cap;
|
|
|
|
|
2012-05-16 23:43:19 +02:00
|
|
|
switch (ap_ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
|
2012-01-20 13:55:27 +01:00
|
|
|
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
|
|
|
|
if (flags & IEEE80211_CHAN_NO_HT40PLUS) {
|
|
|
|
cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
|
|
|
|
cap &= ~IEEE80211_HT_CAP_SGI_40;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
|
|
|
|
if (flags & IEEE80211_CHAN_NO_HT40MINUS) {
|
|
|
|
cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
|
|
|
|
cap &= ~IEEE80211_HT_CAP_SGI_40;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2012-03-28 10:58:36 +02:00
|
|
|
/*
|
|
|
|
* If 40 MHz was disabled associate as though we weren't
|
|
|
|
* capable of 40 MHz -- some broken APs will never fall
|
|
|
|
* back to trying to transmit in 20 MHz.
|
|
|
|
*/
|
|
|
|
if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_40MHZ) {
|
|
|
|
cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
|
|
|
|
cap &= ~IEEE80211_HT_CAP_SGI_40;
|
|
|
|
}
|
|
|
|
|
2012-01-20 13:55:27 +01:00
|
|
|
/* set SM PS mode properly */
|
|
|
|
cap &= ~IEEE80211_HT_CAP_SM_PS;
|
|
|
|
switch (smps) {
|
|
|
|
case IEEE80211_SMPS_AUTOMATIC:
|
|
|
|
case IEEE80211_SMPS_NUM_MODES:
|
|
|
|
WARN_ON(1);
|
|
|
|
case IEEE80211_SMPS_OFF:
|
|
|
|
cap |= WLAN_HT_CAP_SM_PS_DISABLED <<
|
|
|
|
IEEE80211_HT_CAP_SM_PS_SHIFT;
|
|
|
|
break;
|
|
|
|
case IEEE80211_SMPS_STATIC:
|
|
|
|
cap |= WLAN_HT_CAP_SM_PS_STATIC <<
|
|
|
|
IEEE80211_HT_CAP_SM_PS_SHIFT;
|
|
|
|
break;
|
|
|
|
case IEEE80211_SMPS_DYNAMIC:
|
|
|
|
cap |= WLAN_HT_CAP_SM_PS_DYNAMIC <<
|
|
|
|
IEEE80211_HT_CAP_SM_PS_SHIFT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* reserve and fill IE */
|
|
|
|
pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
|
|
|
|
ieee80211_ie_build_ht_cap(pos, &ht_cap, cap);
|
|
|
|
}
|
|
|
|
|
2015-07-08 14:41:43 +02:00
|
|
|
/* This function determines vht capability flags for the association
|
|
|
|
* and builds the IE.
|
|
|
|
* Note - the function may set the owner of the MU-MIMO capability
|
|
|
|
*/
|
2012-07-24 05:33:10 +02:00
|
|
|
static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct sk_buff *skb,
|
2012-12-07 13:06:48 +01:00
|
|
|
struct ieee80211_supported_band *sband,
|
|
|
|
struct ieee80211_vht_cap *ap_vht_cap)
|
2012-07-24 05:33:10 +02:00
|
|
|
{
|
2015-07-08 14:41:43 +02:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
2012-07-24 05:33:10 +02:00
|
|
|
u8 *pos;
|
|
|
|
u32 cap;
|
|
|
|
struct ieee80211_sta_vht_cap vht_cap;
|
2014-01-08 14:49:08 +01:00
|
|
|
u32 mask, ap_bf_sts, our_bf_sts;
|
2012-07-24 05:33:10 +02:00
|
|
|
|
|
|
|
BUILD_BUG_ON(sizeof(vht_cap) != sizeof(sband->vht_cap));
|
|
|
|
|
|
|
|
memcpy(&vht_cap, &sband->vht_cap, sizeof(vht_cap));
|
2013-02-21 17:40:19 +01:00
|
|
|
ieee80211_apply_vhtcap_overrides(sdata, &vht_cap);
|
2012-07-24 05:33:10 +02:00
|
|
|
|
|
|
|
/* determine capability flags */
|
|
|
|
cap = vht_cap.cap;
|
|
|
|
|
2012-11-22 14:11:39 +01:00
|
|
|
if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_80P80MHZ) {
|
2014-11-24 16:51:33 +01:00
|
|
|
u32 bw = cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
|
|
|
|
|
|
|
|
cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
|
|
|
|
if (bw == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ ||
|
|
|
|
bw == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)
|
|
|
|
cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
|
2012-11-22 14:11:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_160MHZ) {
|
|
|
|
cap &= ~IEEE80211_VHT_CAP_SHORT_GI_160;
|
2014-11-24 16:51:33 +01:00
|
|
|
cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
|
2012-11-22 14:11:39 +01:00
|
|
|
}
|
|
|
|
|
2012-12-07 13:06:48 +01:00
|
|
|
/*
|
|
|
|
* Some APs apparently get confused if our capabilities are better
|
|
|
|
* than theirs, so restrict what we advertise in the assoc request.
|
|
|
|
*/
|
|
|
|
if (!(ap_vht_cap->vht_cap_info &
|
|
|
|
cpu_to_le32(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)))
|
2015-07-08 14:41:43 +02:00
|
|
|
cap &= ~(IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
|
|
|
|
IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE);
|
|
|
|
else if (!(ap_vht_cap->vht_cap_info &
|
|
|
|
cpu_to_le32(IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)))
|
|
|
|
cap &= ~IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If some other vif is using the MU-MIMO capablity we cannot associate
|
|
|
|
* using MU-MIMO - this will lead to contradictions in the group-id
|
|
|
|
* mechanism.
|
|
|
|
* Ownership is defined since association request, in order to avoid
|
|
|
|
* simultaneous associations with MU-MIMO.
|
|
|
|
*/
|
|
|
|
if (cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE) {
|
|
|
|
bool disable_mu_mimo = false;
|
|
|
|
struct ieee80211_sub_if_data *other;
|
|
|
|
|
|
|
|
list_for_each_entry_rcu(other, &local->interfaces, list) {
|
|
|
|
if (other->flags & IEEE80211_SDATA_MU_MIMO_OWNER) {
|
|
|
|
disable_mu_mimo = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (disable_mu_mimo)
|
|
|
|
cap &= ~IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
|
|
|
|
else
|
|
|
|
sdata->flags |= IEEE80211_SDATA_MU_MIMO_OWNER;
|
|
|
|
}
|
2012-12-07 13:06:48 +01:00
|
|
|
|
2014-01-08 14:49:08 +01:00
|
|
|
mask = IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK;
|
|
|
|
|
|
|
|
ap_bf_sts = le32_to_cpu(ap_vht_cap->vht_cap_info) & mask;
|
|
|
|
our_bf_sts = cap & mask;
|
|
|
|
|
|
|
|
if (ap_bf_sts < our_bf_sts) {
|
|
|
|
cap &= ~mask;
|
|
|
|
cap |= ap_bf_sts;
|
|
|
|
}
|
|
|
|
|
2012-07-24 05:33:10 +02:00
|
|
|
/* reserve and fill IE */
|
2012-10-10 13:25:40 +02:00
|
|
|
pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
|
2012-07-24 05:33:10 +02:00
|
|
|
ieee80211_ie_build_vht_cap(pos, &vht_cap, cap);
|
|
|
|
}
|
|
|
|
|
2012-01-20 13:55:27 +01:00
|
|
|
static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data;
|
|
|
|
struct sk_buff *skb;
|
|
|
|
struct ieee80211_mgmt *mgmt;
|
|
|
|
u8 *pos, qos_info;
|
|
|
|
size_t offset = 0, noffset;
|
2013-07-08 16:55:53 +02:00
|
|
|
int i, count, rates_len, supp_rates_len, shift;
|
2012-01-20 13:55:27 +01:00
|
|
|
u16 capab;
|
|
|
|
struct ieee80211_supported_band *sband;
|
2012-07-26 17:24:39 +02:00
|
|
|
struct ieee80211_chanctx_conf *chanctx_conf;
|
|
|
|
struct ieee80211_channel *chan;
|
2013-07-08 16:55:53 +02:00
|
|
|
u32 rate_flags, rates = 0;
|
2012-01-20 13:55:27 +01:00
|
|
|
|
2013-05-10 12:32:47 +02:00
|
|
|
sdata_assert_lock(sdata);
|
2012-01-20 13:55:27 +01:00
|
|
|
|
2012-07-26 17:24:39 +02:00
|
|
|
rcu_read_lock();
|
|
|
|
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
|
|
|
|
if (WARN_ON(!chanctx_conf)) {
|
|
|
|
rcu_read_unlock();
|
|
|
|
return;
|
|
|
|
}
|
2012-11-09 11:39:59 +01:00
|
|
|
chan = chanctx_conf->def.chan;
|
2013-07-08 16:55:53 +02:00
|
|
|
rate_flags = ieee80211_chandef_rate_flags(&chanctx_conf->def);
|
2012-07-26 17:24:39 +02:00
|
|
|
rcu_read_unlock();
|
|
|
|
sband = local->hw.wiphy->bands[chan->band];
|
2013-07-08 16:55:53 +02:00
|
|
|
shift = ieee80211_vif_get_shift(&sdata->vif);
|
2012-01-20 13:55:27 +01:00
|
|
|
|
|
|
|
if (assoc_data->supp_rates_len) {
|
|
|
|
/*
|
|
|
|
* Get all rates supported by the device and the AP as
|
|
|
|
* some APs don't like getting a superset of their rates
|
|
|
|
* in the association request (e.g. D-Link DAP 1353 in
|
|
|
|
* b-only mode)...
|
|
|
|
*/
|
2013-07-08 16:55:53 +02:00
|
|
|
rates_len = ieee80211_parse_bitrates(&chanctx_conf->def, sband,
|
|
|
|
assoc_data->supp_rates,
|
|
|
|
assoc_data->supp_rates_len,
|
|
|
|
&rates);
|
2012-01-20 13:55:27 +01:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* In case AP not provide any supported rates information
|
|
|
|
* before association, we send information element(s) with
|
|
|
|
* all rates that we support.
|
|
|
|
*/
|
2013-07-08 16:55:53 +02:00
|
|
|
rates_len = 0;
|
|
|
|
for (i = 0; i < sband->n_bitrates; i++) {
|
|
|
|
if ((rate_flags & sband->bitrates[i].flags)
|
|
|
|
!= rate_flags)
|
|
|
|
continue;
|
|
|
|
rates |= BIT(i);
|
|
|
|
rates_len++;
|
|
|
|
}
|
2012-01-20 13:55:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
skb = alloc_skb(local->hw.extra_tx_headroom +
|
|
|
|
sizeof(*mgmt) + /* bit too much but doesn't matter */
|
|
|
|
2 + assoc_data->ssid_len + /* SSID */
|
|
|
|
4 + rates_len + /* (extended) rates */
|
|
|
|
4 + /* power capability */
|
|
|
|
2 + 2 * sband->n_channels + /* supported channels */
|
|
|
|
2 + sizeof(struct ieee80211_ht_cap) + /* HT */
|
2012-10-10 13:25:40 +02:00
|
|
|
2 + sizeof(struct ieee80211_vht_cap) + /* VHT */
|
2012-01-20 13:55:27 +01:00
|
|
|
assoc_data->ie_len + /* extra IEs */
|
|
|
|
9, /* WMM */
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!skb)
|
|
|
|
return;
|
|
|
|
|
|
|
|
skb_reserve(skb, local->hw.extra_tx_headroom);
|
|
|
|
|
|
|
|
capab = WLAN_CAPABILITY_ESS;
|
|
|
|
|
|
|
|
if (sband->band == IEEE80211_BAND_2GHZ) {
|
2015-06-02 20:15:49 +02:00
|
|
|
capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
|
|
|
|
capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
|
2012-01-20 13:55:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (assoc_data->capability & WLAN_CAPABILITY_PRIVACY)
|
|
|
|
capab |= WLAN_CAPABILITY_PRIVACY;
|
|
|
|
|
|
|
|
if ((assoc_data->capability & WLAN_CAPABILITY_SPECTRUM_MGMT) &&
|
2015-06-02 21:39:54 +02:00
|
|
|
ieee80211_hw_check(&local->hw, SPECTRUM_MGMT))
|
2012-01-20 13:55:27 +01:00
|
|
|
capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
|
|
|
|
|
2014-09-03 14:25:02 +02:00
|
|
|
if (ifmgd->flags & IEEE80211_STA_ENABLE_RRM)
|
|
|
|
capab |= WLAN_CAPABILITY_RADIO_MEASURE;
|
|
|
|
|
2012-01-20 13:55:27 +01:00
|
|
|
mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
|
|
|
|
memset(mgmt, 0, 24);
|
|
|
|
memcpy(mgmt->da, assoc_data->bss->bssid, ETH_ALEN);
|
|
|
|
memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
|
|
|
|
memcpy(mgmt->bssid, assoc_data->bss->bssid, ETH_ALEN);
|
|
|
|
|
|
|
|
if (!is_zero_ether_addr(assoc_data->prev_bssid)) {
|
|
|
|
skb_put(skb, 10);
|
|
|
|
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
|
|
|
|
IEEE80211_STYPE_REASSOC_REQ);
|
|
|
|
mgmt->u.reassoc_req.capab_info = cpu_to_le16(capab);
|
|
|
|
mgmt->u.reassoc_req.listen_interval =
|
|
|
|
cpu_to_le16(local->hw.conf.listen_interval);
|
|
|
|
memcpy(mgmt->u.reassoc_req.current_ap, assoc_data->prev_bssid,
|
|
|
|
ETH_ALEN);
|
|
|
|
} else {
|
|
|
|
skb_put(skb, 4);
|
|
|
|
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
|
|
|
|
IEEE80211_STYPE_ASSOC_REQ);
|
|
|
|
mgmt->u.assoc_req.capab_info = cpu_to_le16(capab);
|
|
|
|
mgmt->u.assoc_req.listen_interval =
|
|
|
|
cpu_to_le16(local->hw.conf.listen_interval);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* SSID */
|
|
|
|
pos = skb_put(skb, 2 + assoc_data->ssid_len);
|
|
|
|
*pos++ = WLAN_EID_SSID;
|
|
|
|
*pos++ = assoc_data->ssid_len;
|
|
|
|
memcpy(pos, assoc_data->ssid, assoc_data->ssid_len);
|
|
|
|
|
|
|
|
/* add all rates which were marked to be used above */
|
|
|
|
supp_rates_len = rates_len;
|
|
|
|
if (supp_rates_len > 8)
|
|
|
|
supp_rates_len = 8;
|
|
|
|
|
|
|
|
pos = skb_put(skb, supp_rates_len + 2);
|
|
|
|
*pos++ = WLAN_EID_SUPP_RATES;
|
|
|
|
*pos++ = supp_rates_len;
|
|
|
|
|
|
|
|
count = 0;
|
|
|
|
for (i = 0; i < sband->n_bitrates; i++) {
|
|
|
|
if (BIT(i) & rates) {
|
2013-07-08 16:55:53 +02:00
|
|
|
int rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
|
|
|
|
5 * (1 << shift));
|
|
|
|
*pos++ = (u8) rate;
|
2012-01-20 13:55:27 +01:00
|
|
|
if (++count == 8)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rates_len > count) {
|
|
|
|
pos = skb_put(skb, rates_len - count + 2);
|
|
|
|
*pos++ = WLAN_EID_EXT_SUPP_RATES;
|
|
|
|
*pos++ = rates_len - count;
|
|
|
|
|
|
|
|
for (i++; i < sband->n_bitrates; i++) {
|
|
|
|
if (BIT(i) & rates) {
|
2013-07-08 16:55:53 +02:00
|
|
|
int rate;
|
|
|
|
rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
|
|
|
|
5 * (1 << shift));
|
|
|
|
*pos++ = (u8) rate;
|
2012-01-20 13:55:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-03 14:25:02 +02:00
|
|
|
if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT ||
|
|
|
|
capab & WLAN_CAPABILITY_RADIO_MEASURE) {
|
2012-01-20 13:55:27 +01:00
|
|
|
pos = skb_put(skb, 4);
|
|
|
|
*pos++ = WLAN_EID_PWR_CAPABILITY;
|
|
|
|
*pos++ = 2;
|
|
|
|
*pos++ = 0; /* min tx power */
|
2013-07-08 16:55:55 +02:00
|
|
|
/* max tx power */
|
|
|
|
*pos++ = ieee80211_chandef_max_power(&chanctx_conf->def);
|
2014-09-03 14:25:02 +02:00
|
|
|
}
|
2012-01-20 13:55:27 +01:00
|
|
|
|
2014-09-03 14:25:02 +02:00
|
|
|
if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT) {
|
2012-01-20 13:55:27 +01:00
|
|
|
/* TODO: get this in reg domain format */
|
|
|
|
pos = skb_put(skb, 2 * sband->n_channels + 2);
|
|
|
|
*pos++ = WLAN_EID_SUPPORTED_CHANNELS;
|
|
|
|
*pos++ = 2 * sband->n_channels;
|
|
|
|
for (i = 0; i < sband->n_channels; i++) {
|
|
|
|
*pos++ = ieee80211_frequency_to_channel(
|
|
|
|
sband->channels[i].center_freq);
|
|
|
|
*pos++ = 1; /* one channel in the subband*/
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if present, add any custom IEs that go before HT */
|
2013-10-25 11:31:42 +02:00
|
|
|
if (assoc_data->ie_len) {
|
2012-01-20 13:55:27 +01:00
|
|
|
static const u8 before_ht[] = {
|
|
|
|
WLAN_EID_SSID,
|
|
|
|
WLAN_EID_SUPP_RATES,
|
|
|
|
WLAN_EID_EXT_SUPP_RATES,
|
|
|
|
WLAN_EID_PWR_CAPABILITY,
|
|
|
|
WLAN_EID_SUPPORTED_CHANNELS,
|
|
|
|
WLAN_EID_RSN,
|
|
|
|
WLAN_EID_QOS_CAPA,
|
|
|
|
WLAN_EID_RRM_ENABLED_CAPABILITIES,
|
|
|
|
WLAN_EID_MOBILITY_DOMAIN,
|
mac80211: handle RIC data element in reassociation request
When the RIC data element (RDE) is included in the IEs coming
from userspace for an association request, its handling is
currently broken as any IEs that are contained within it would
be split off from it and inserted again after all the IEs that
mac80211 generates (e.g. HT, VHT.)
To fix this, treat the RIC element specially, and stop after
it only when we find something that doesn't actually belong to
it. This assumes userspace is actually correctly building it,
directly after the fast BSS transition IE and before all the
others like extended capabilities.
This leaves as a potential problem the case where userspace is
building the following IEs:
[RDE] [vendor resource description] [vendor non-resource IE]
In this case, we'd erroneously consider all three IEs to be
part of the RIC data together, and not split them between the
two vendor IEs. Unfortunately, it isn't easily possible to
distinguish vendor IEs, so this isn't easy to fix. Luckily,
this case is rare as normally wpa_supplicant will include an
extended capabilities IE in the IEs, and that certainly will
break the two vendor IEs apart correctly.
Reviewed-by: Eliad Peller <eliad@wizery.com>
Reviewed-by: Beni Lev <beni.lev@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2014-10-27 12:03:19 +01:00
|
|
|
WLAN_EID_FAST_BSS_TRANSITION, /* reassoc only */
|
|
|
|
WLAN_EID_RIC_DATA, /* reassoc only */
|
2012-01-20 13:55:27 +01:00
|
|
|
WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
|
|
|
|
};
|
mac80211: handle RIC data element in reassociation request
When the RIC data element (RDE) is included in the IEs coming
from userspace for an association request, its handling is
currently broken as any IEs that are contained within it would
be split off from it and inserted again after all the IEs that
mac80211 generates (e.g. HT, VHT.)
To fix this, treat the RIC element specially, and stop after
it only when we find something that doesn't actually belong to
it. This assumes userspace is actually correctly building it,
directly after the fast BSS transition IE and before all the
others like extended capabilities.
This leaves as a potential problem the case where userspace is
building the following IEs:
[RDE] [vendor resource description] [vendor non-resource IE]
In this case, we'd erroneously consider all three IEs to be
part of the RIC data together, and not split them between the
two vendor IEs. Unfortunately, it isn't easily possible to
distinguish vendor IEs, so this isn't easy to fix. Luckily,
this case is rare as normally wpa_supplicant will include an
extended capabilities IE in the IEs, and that certainly will
break the two vendor IEs apart correctly.
Reviewed-by: Eliad Peller <eliad@wizery.com>
Reviewed-by: Beni Lev <beni.lev@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2014-10-27 12:03:19 +01:00
|
|
|
static const u8 after_ric[] = {
|
|
|
|
WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
|
|
|
|
WLAN_EID_HT_CAPABILITY,
|
|
|
|
WLAN_EID_BSS_COEX_2040,
|
|
|
|
WLAN_EID_EXT_CAPABILITY,
|
|
|
|
WLAN_EID_QOS_TRAFFIC_CAPA,
|
|
|
|
WLAN_EID_TIM_BCAST_REQ,
|
|
|
|
WLAN_EID_INTERWORKING,
|
|
|
|
/* 60GHz doesn't happen right now */
|
|
|
|
WLAN_EID_VHT_CAPABILITY,
|
|
|
|
WLAN_EID_OPMODE_NOTIF,
|
|
|
|
};
|
|
|
|
|
|
|
|
noffset = ieee80211_ie_split_ric(assoc_data->ie,
|
|
|
|
assoc_data->ie_len,
|
|
|
|
before_ht,
|
|
|
|
ARRAY_SIZE(before_ht),
|
|
|
|
after_ric,
|
|
|
|
ARRAY_SIZE(after_ric),
|
|
|
|
offset);
|
2012-01-20 13:55:27 +01:00
|
|
|
pos = skb_put(skb, noffset - offset);
|
|
|
|
memcpy(pos, assoc_data->ie + offset, noffset - offset);
|
|
|
|
offset = noffset;
|
|
|
|
}
|
|
|
|
|
2012-11-22 14:11:39 +01:00
|
|
|
if (WARN_ON_ONCE((ifmgd->flags & IEEE80211_STA_DISABLE_HT) &&
|
|
|
|
!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)))
|
|
|
|
ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
|
|
|
|
|
2012-11-22 14:32:09 +01:00
|
|
|
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT))
|
2012-05-16 23:43:19 +02:00
|
|
|
ieee80211_add_ht_ie(sdata, skb, assoc_data->ap_ht_param,
|
2012-09-11 14:34:12 +02:00
|
|
|
sband, chan, sdata->smps_mode);
|
2012-01-20 13:55:27 +01:00
|
|
|
|
2014-02-04 09:54:07 +01:00
|
|
|
/* if present, add any custom IEs that go before VHT */
|
|
|
|
if (assoc_data->ie_len) {
|
|
|
|
static const u8 before_vht[] = {
|
|
|
|
WLAN_EID_SSID,
|
|
|
|
WLAN_EID_SUPP_RATES,
|
|
|
|
WLAN_EID_EXT_SUPP_RATES,
|
|
|
|
WLAN_EID_PWR_CAPABILITY,
|
|
|
|
WLAN_EID_SUPPORTED_CHANNELS,
|
|
|
|
WLAN_EID_RSN,
|
|
|
|
WLAN_EID_QOS_CAPA,
|
|
|
|
WLAN_EID_RRM_ENABLED_CAPABILITIES,
|
|
|
|
WLAN_EID_MOBILITY_DOMAIN,
|
|
|
|
WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
|
|
|
|
WLAN_EID_HT_CAPABILITY,
|
|
|
|
WLAN_EID_BSS_COEX_2040,
|
|
|
|
WLAN_EID_EXT_CAPABILITY,
|
|
|
|
WLAN_EID_QOS_TRAFFIC_CAPA,
|
|
|
|
WLAN_EID_TIM_BCAST_REQ,
|
|
|
|
WLAN_EID_INTERWORKING,
|
|
|
|
};
|
mac80211: handle RIC data element in reassociation request
When the RIC data element (RDE) is included in the IEs coming
from userspace for an association request, its handling is
currently broken as any IEs that are contained within it would
be split off from it and inserted again after all the IEs that
mac80211 generates (e.g. HT, VHT.)
To fix this, treat the RIC element specially, and stop after
it only when we find something that doesn't actually belong to
it. This assumes userspace is actually correctly building it,
directly after the fast BSS transition IE and before all the
others like extended capabilities.
This leaves as a potential problem the case where userspace is
building the following IEs:
[RDE] [vendor resource description] [vendor non-resource IE]
In this case, we'd erroneously consider all three IEs to be
part of the RIC data together, and not split them between the
two vendor IEs. Unfortunately, it isn't easily possible to
distinguish vendor IEs, so this isn't easy to fix. Luckily,
this case is rare as normally wpa_supplicant will include an
extended capabilities IE in the IEs, and that certainly will
break the two vendor IEs apart correctly.
Reviewed-by: Eliad Peller <eliad@wizery.com>
Reviewed-by: Beni Lev <beni.lev@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2014-10-27 12:03:19 +01:00
|
|
|
|
|
|
|
/* RIC already taken above, so no need to handle here anymore */
|
2014-02-04 09:54:07 +01:00
|
|
|
noffset = ieee80211_ie_split(assoc_data->ie, assoc_data->ie_len,
|
|
|
|
before_vht, ARRAY_SIZE(before_vht),
|
|
|
|
offset);
|
|
|
|
pos = skb_put(skb, noffset - offset);
|
|
|
|
memcpy(pos, assoc_data->ie + offset, noffset - offset);
|
|
|
|
offset = noffset;
|
|
|
|
}
|
|
|
|
|
2012-07-24 05:33:10 +02:00
|
|
|
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))
|
2012-12-07 13:06:48 +01:00
|
|
|
ieee80211_add_vht_ie(sdata, skb, sband,
|
|
|
|
&assoc_data->ap_vht_cap);
|
2012-07-24 05:33:10 +02:00
|
|
|
|
2012-01-20 13:55:27 +01:00
|
|
|
/* if present, add any custom non-vendor IEs that go after HT */
|
2013-10-25 11:31:42 +02:00
|
|
|
if (assoc_data->ie_len) {
|
2012-01-20 13:55:27 +01:00
|
|
|
noffset = ieee80211_ie_split_vendor(assoc_data->ie,
|
|
|
|
assoc_data->ie_len,
|
|
|
|
offset);
|
|
|
|
pos = skb_put(skb, noffset - offset);
|
|
|
|
memcpy(pos, assoc_data->ie + offset, noffset - offset);
|
|
|
|
offset = noffset;
|
|
|
|
}
|
|
|
|
|
2012-03-08 15:02:05 +01:00
|
|
|
if (assoc_data->wmm) {
|
|
|
|
if (assoc_data->uapsd) {
|
2012-03-14 15:15:03 +01:00
|
|
|
qos_info = ifmgd->uapsd_queues;
|
|
|
|
qos_info |= (ifmgd->uapsd_max_sp_len <<
|
2012-01-20 13:55:27 +01:00
|
|
|
IEEE80211_WMM_IE_STA_QOSINFO_SP_SHIFT);
|
|
|
|
} else {
|
|
|
|
qos_info = 0;
|
|
|
|
}
|
|
|
|
|
2014-07-17 16:14:23 +02:00
|
|
|
pos = ieee80211_add_wmm_info_ie(skb_put(skb, 9), qos_info);
|
2012-01-20 13:55:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* add any remaining custom (i.e. vendor specific here) IEs */
|
2013-10-25 11:31:42 +02:00
|
|
|
if (assoc_data->ie_len) {
|
2012-01-20 13:55:27 +01:00
|
|
|
noffset = assoc_data->ie_len;
|
|
|
|
pos = skb_put(skb, noffset - offset);
|
|
|
|
memcpy(pos, assoc_data->ie + offset, noffset - offset);
|
|
|
|
}
|
|
|
|
|
2012-06-27 13:18:36 +02:00
|
|
|
drv_mgd_prepare_tx(local, sdata);
|
|
|
|
|
2012-01-20 13:55:27 +01:00
|
|
|
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
|
2015-06-02 21:39:54 +02:00
|
|
|
if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS))
|
2013-01-29 15:02:27 +01:00
|
|
|
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS |
|
|
|
|
IEEE80211_TX_INTFL_MLME_CONN_TX;
|
2012-01-20 13:55:27 +01:00
|
|
|
ieee80211_tx_skb(sdata, skb);
|
|
|
|
}
|
|
|
|
|
2009-02-10 16:09:31 +01:00
|
|
|
void ieee80211_send_pspoll(struct ieee80211_local *local,
|
|
|
|
struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
|
|
|
struct ieee80211_pspoll *pspoll;
|
|
|
|
struct sk_buff *skb;
|
|
|
|
|
2010-01-05 19:16:26 +01:00
|
|
|
skb = ieee80211_pspoll_get(&local->hw, &sdata->vif);
|
|
|
|
if (!skb)
|
2009-02-10 16:09:31 +01:00
|
|
|
return;
|
|
|
|
|
2010-01-05 19:16:26 +01:00
|
|
|
pspoll = (struct ieee80211_pspoll *) skb->data;
|
|
|
|
pspoll->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
|
2009-02-10 16:09:31 +01:00
|
|
|
|
2009-11-18 18:42:05 +01:00
|
|
|
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
|
|
|
|
ieee80211_tx_skb(sdata, skb);
|
2009-02-10 16:09:31 +01:00
|
|
|
}
|
|
|
|
|
2009-04-16 13:17:24 +02:00
|
|
|
void ieee80211_send_nullfunc(struct ieee80211_local *local,
|
|
|
|
struct ieee80211_sub_if_data *sdata,
|
|
|
|
int powersave)
|
|
|
|
{
|
|
|
|
struct sk_buff *skb;
|
2010-01-05 19:16:26 +01:00
|
|
|
struct ieee80211_hdr_3addr *nullfunc;
|
2011-09-29 17:04:04 +02:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2009-04-16 13:17:24 +02:00
|
|
|
|
2010-01-05 19:16:26 +01:00
|
|
|
skb = ieee80211_nullfunc_get(&local->hw, &sdata->vif);
|
|
|
|
if (!skb)
|
2009-04-16 13:17:24 +02:00
|
|
|
return;
|
|
|
|
|
2010-01-05 19:16:26 +01:00
|
|
|
nullfunc = (struct ieee80211_hdr_3addr *) skb->data;
|
2009-04-16 13:17:24 +02:00
|
|
|
if (powersave)
|
2010-01-05 19:16:26 +01:00
|
|
|
nullfunc->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
|
2009-04-16 13:17:24 +02:00
|
|
|
|
2013-02-11 18:21:07 +01:00
|
|
|
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
|
|
|
|
IEEE80211_TX_INTFL_OFFCHAN_TX_OK;
|
2013-06-04 12:44:52 +02:00
|
|
|
|
2015-06-02 21:39:54 +02:00
|
|
|
if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS))
|
2013-06-04 12:44:52 +02:00
|
|
|
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
|
|
|
|
|
2013-08-27 11:36:35 +02:00
|
|
|
if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL)
|
2011-09-29 17:04:04 +02:00
|
|
|
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_USE_MINRATE;
|
|
|
|
|
2009-11-18 18:42:05 +01:00
|
|
|
ieee80211_tx_skb(sdata, skb);
|
2009-04-16 13:17:24 +02:00
|
|
|
}
|
|
|
|
|
2010-01-08 18:06:26 +01:00
|
|
|
static void ieee80211_send_4addr_nullfunc(struct ieee80211_local *local,
|
|
|
|
struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
|
|
|
struct sk_buff *skb;
|
|
|
|
struct ieee80211_hdr *nullfunc;
|
|
|
|
__le16 fc;
|
|
|
|
|
|
|
|
if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
|
|
|
|
return;
|
|
|
|
|
|
|
|
skb = dev_alloc_skb(local->hw.extra_tx_headroom + 30);
|
2011-08-29 23:17:31 +02:00
|
|
|
if (!skb)
|
2010-01-08 18:06:26 +01:00
|
|
|
return;
|
2011-08-29 23:17:31 +02:00
|
|
|
|
2010-01-08 18:06:26 +01:00
|
|
|
skb_reserve(skb, local->hw.extra_tx_headroom);
|
|
|
|
|
|
|
|
nullfunc = (struct ieee80211_hdr *) skb_put(skb, 30);
|
|
|
|
memset(nullfunc, 0, 30);
|
|
|
|
fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC |
|
|
|
|
IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
|
|
|
|
nullfunc->frame_control = fc;
|
|
|
|
memcpy(nullfunc->addr1, sdata->u.mgd.bssid, ETH_ALEN);
|
|
|
|
memcpy(nullfunc->addr2, sdata->vif.addr, ETH_ALEN);
|
|
|
|
memcpy(nullfunc->addr3, sdata->u.mgd.bssid, ETH_ALEN);
|
|
|
|
memcpy(nullfunc->addr4, sdata->vif.addr, ETH_ALEN);
|
|
|
|
|
|
|
|
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
|
|
|
|
ieee80211_tx_skb(sdata, skb);
|
|
|
|
}
|
|
|
|
|
2009-05-15 11:52:31 +02:00
|
|
|
/* spectrum management related things */
|
|
|
|
static void ieee80211_chswitch_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata =
|
|
|
|
container_of(work, struct ieee80211_sub_if_data, u.mgd.chswitch_work);
|
2013-03-25 16:26:57 +01:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
2009-05-15 11:52:31 +02:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2013-09-01 16:15:51 +02:00
|
|
|
int ret;
|
2009-05-15 11:52:31 +02:00
|
|
|
|
2009-12-23 13:15:31 +01:00
|
|
|
if (!ieee80211_sdata_running(sdata))
|
2009-05-15 11:52:31 +02:00
|
|
|
return;
|
|
|
|
|
2013-05-10 12:32:47 +02:00
|
|
|
sdata_lock(sdata);
|
2014-06-25 12:35:09 +02:00
|
|
|
mutex_lock(&local->mtx);
|
|
|
|
mutex_lock(&local->chanctx_mtx);
|
|
|
|
|
2009-07-07 03:45:17 +02:00
|
|
|
if (!ifmgd->associated)
|
|
|
|
goto out;
|
2009-05-15 11:52:31 +02:00
|
|
|
|
2014-06-25 12:35:09 +02:00
|
|
|
if (!sdata->vif.csa_active)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* using reservation isn't immediate as it may be deferred until later
|
|
|
|
* with multi-vif. once reservation is complete it will re-schedule the
|
|
|
|
* work with no reserved_chanctx so verify chandef to check if it
|
|
|
|
* completed successfully
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (sdata->reserved_chanctx) {
|
|
|
|
/*
|
|
|
|
* with multi-vif csa driver may call ieee80211_csa_finish()
|
|
|
|
* many times while waiting for other interfaces to use their
|
|
|
|
* reservations
|
|
|
|
*/
|
|
|
|
if (sdata->reserved_ready)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
ret = ieee80211_vif_use_reserved_context(sdata);
|
|
|
|
if (ret) {
|
|
|
|
sdata_info(sdata,
|
|
|
|
"failed to use reserved channel context, disconnecting (err=%d)\n",
|
|
|
|
ret);
|
|
|
|
ieee80211_queue_work(&sdata->local->hw,
|
|
|
|
&ifmgd->csa_connection_drop_work);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cfg80211_chandef_identical(&sdata->vif.bss_conf.chandef,
|
|
|
|
&sdata->csa_chandef)) {
|
2013-09-01 16:15:51 +02:00
|
|
|
sdata_info(sdata,
|
2014-06-25 12:35:09 +02:00
|
|
|
"failed to finalize channel switch, disconnecting\n");
|
2013-09-01 16:15:51 +02:00
|
|
|
ieee80211_queue_work(&sdata->local->hw,
|
|
|
|
&ifmgd->csa_connection_drop_work);
|
|
|
|
goto out;
|
|
|
|
}
|
2013-03-25 16:26:57 +01:00
|
|
|
|
2009-05-15 11:52:31 +02:00
|
|
|
/* XXX: shouldn't really modify cfg80211-owned data! */
|
2013-11-11 19:34:54 +01:00
|
|
|
ifmgd->associated->channel = sdata->csa_chandef.chan;
|
2009-05-15 11:52:31 +02:00
|
|
|
|
2014-10-08 08:48:39 +02:00
|
|
|
ifmgd->csa_waiting_bcn = true;
|
|
|
|
|
|
|
|
ieee80211_sta_reset_beacon_monitor(sdata);
|
|
|
|
ieee80211_sta_reset_conn_monitor(sdata);
|
|
|
|
|
|
|
|
out:
|
|
|
|
mutex_unlock(&local->chanctx_mtx);
|
|
|
|
mutex_unlock(&local->mtx);
|
|
|
|
sdata_unlock(sdata);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ieee80211_chswitch_post_beacon(struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
sdata_assert_lock(sdata);
|
|
|
|
|
|
|
|
WARN_ON(!sdata->vif.csa_active);
|
2014-06-25 12:35:09 +02:00
|
|
|
|
2014-06-13 15:30:07 +02:00
|
|
|
if (sdata->csa_block_tx) {
|
|
|
|
ieee80211_wake_vif_queues(local, sdata,
|
|
|
|
IEEE80211_QUEUE_STOP_REASON_CSA);
|
|
|
|
sdata->csa_block_tx = false;
|
|
|
|
}
|
2013-09-01 16:15:51 +02:00
|
|
|
|
2014-10-08 08:48:39 +02:00
|
|
|
sdata->vif.csa_active = false;
|
|
|
|
ifmgd->csa_waiting_bcn = false;
|
|
|
|
|
2014-10-08 08:48:38 +02:00
|
|
|
ret = drv_post_channel_switch(sdata);
|
|
|
|
if (ret) {
|
|
|
|
sdata_info(sdata,
|
|
|
|
"driver post channel switch failed, disconnecting\n");
|
|
|
|
ieee80211_queue_work(&local->hw,
|
|
|
|
&ifmgd->csa_connection_drop_work);
|
2014-10-08 08:48:39 +02:00
|
|
|
return;
|
2014-10-08 08:48:38 +02:00
|
|
|
}
|
2014-12-16 15:01:39 +01:00
|
|
|
|
|
|
|
cfg80211_ch_switch_notify(sdata->dev, &sdata->reserved_chandef);
|
2009-05-15 11:52:31 +02:00
|
|
|
}
|
|
|
|
|
2010-05-11 16:20:57 +02:00
|
|
|
void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success)
|
|
|
|
{
|
2012-07-26 17:24:39 +02:00
|
|
|
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2010-05-11 16:20:57 +02:00
|
|
|
|
|
|
|
trace_api_chswitch_done(sdata, success);
|
|
|
|
if (!success) {
|
2012-08-01 22:32:45 +02:00
|
|
|
sdata_info(sdata,
|
|
|
|
"driver channel switch failed, disconnecting\n");
|
|
|
|
ieee80211_queue_work(&sdata->local->hw,
|
|
|
|
&ifmgd->csa_connection_drop_work);
|
|
|
|
} else {
|
|
|
|
ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
|
2010-05-11 16:20:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ieee80211_chswitch_done);
|
|
|
|
|
2009-05-15 11:52:31 +02:00
|
|
|
static void ieee80211_chswitch_timer(unsigned long data)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata =
|
|
|
|
(struct ieee80211_sub_if_data *) data;
|
2009-05-17 11:40:42 +02:00
|
|
|
|
2013-02-28 10:55:27 +01:00
|
|
|
ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.chswitch_work);
|
2009-05-15 11:52:31 +02:00
|
|
|
}
|
|
|
|
|
2013-03-26 14:02:26 +01:00
|
|
|
static void
|
2013-02-12 16:43:19 +01:00
|
|
|
ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
2014-10-08 08:48:35 +02:00
|
|
|
u64 timestamp, u32 device_timestamp,
|
|
|
|
struct ieee802_11_elems *elems,
|
2013-05-03 09:35:35 +02:00
|
|
|
bool beacon)
|
2009-05-15 11:52:31 +02:00
|
|
|
{
|
2013-03-26 14:13:58 +01:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
2009-05-15 11:52:31 +02:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2013-03-26 14:02:26 +01:00
|
|
|
struct cfg80211_bss *cbss = ifmgd->associated;
|
2014-06-25 12:35:09 +02:00
|
|
|
struct ieee80211_chanctx_conf *conf;
|
2012-07-26 17:24:39 +02:00
|
|
|
struct ieee80211_chanctx *chanctx;
|
2013-08-28 13:41:29 +02:00
|
|
|
enum ieee80211_band current_band;
|
2013-10-15 04:08:29 +02:00
|
|
|
struct ieee80211_csa_ie csa_ie;
|
2014-10-08 08:48:37 +02:00
|
|
|
struct ieee80211_channel_switch ch_switch;
|
2013-08-28 13:41:29 +02:00
|
|
|
int res;
|
2009-05-15 11:52:31 +02:00
|
|
|
|
2013-05-10 12:32:47 +02:00
|
|
|
sdata_assert_lock(sdata);
|
2009-07-07 03:45:17 +02:00
|
|
|
|
2013-03-26 14:02:26 +01:00
|
|
|
if (!cbss)
|
2009-05-15 11:52:31 +02:00
|
|
|
return;
|
|
|
|
|
2013-03-26 14:13:58 +01:00
|
|
|
if (local->scanning)
|
2009-05-15 11:52:31 +02:00
|
|
|
return;
|
|
|
|
|
2013-03-26 14:02:26 +01:00
|
|
|
/* disregard subsequent announcements if we are already processing */
|
2014-07-05 10:43:01 +02:00
|
|
|
if (sdata->vif.csa_active)
|
2009-05-15 11:52:31 +02:00
|
|
|
return;
|
|
|
|
|
2013-08-28 13:41:29 +02:00
|
|
|
current_band = cbss->channel->band;
|
2013-10-15 04:08:29 +02:00
|
|
|
memset(&csa_ie, 0, sizeof(csa_ie));
|
2014-10-28 12:33:04 +01:00
|
|
|
res = ieee80211_parse_ch_switch_ie(sdata, elems, current_band,
|
2013-08-28 13:41:29 +02:00
|
|
|
ifmgd->flags,
|
2013-10-15 04:08:29 +02:00
|
|
|
ifmgd->associated->bssid, &csa_ie);
|
2013-08-28 13:41:29 +02:00
|
|
|
if (res < 0)
|
2013-03-26 14:13:58 +01:00
|
|
|
ieee80211_queue_work(&local->hw,
|
2012-08-01 22:32:45 +02:00
|
|
|
&ifmgd->csa_connection_drop_work);
|
2013-08-28 13:41:29 +02:00
|
|
|
if (res)
|
2009-05-15 11:52:31 +02:00
|
|
|
return;
|
2013-03-28 10:44:18 +01:00
|
|
|
|
2013-10-15 04:08:29 +02:00
|
|
|
if (!cfg80211_chandef_usable(local->hw.wiphy, &csa_ie.chandef,
|
2013-03-25 18:29:27 +01:00
|
|
|
IEEE80211_CHAN_DISABLED)) {
|
|
|
|
sdata_info(sdata,
|
|
|
|
"AP %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n",
|
2013-08-28 13:41:29 +02:00
|
|
|
ifmgd->associated->bssid,
|
2013-10-15 04:08:29 +02:00
|
|
|
csa_ie.chandef.chan->center_freq,
|
|
|
|
csa_ie.chandef.width, csa_ie.chandef.center_freq1,
|
|
|
|
csa_ie.chandef.center_freq2);
|
2013-03-25 18:29:27 +01:00
|
|
|
ieee80211_queue_work(&local->hw,
|
|
|
|
&ifmgd->csa_connection_drop_work);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-03-12 07:53:26 +01:00
|
|
|
if (cfg80211_chandef_identical(&csa_ie.chandef,
|
|
|
|
&sdata->vif.bss_conf.chandef)) {
|
|
|
|
if (ifmgd->csa_ignored_same_chan)
|
|
|
|
return;
|
|
|
|
sdata_info(sdata,
|
|
|
|
"AP %pM tries to chanswitch to same channel, ignore\n",
|
|
|
|
ifmgd->associated->bssid);
|
|
|
|
ifmgd->csa_ignored_same_chan = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-05-19 13:36:48 +02:00
|
|
|
/*
|
|
|
|
* Drop all TDLS peers - either we disconnect or move to a different
|
|
|
|
* channel from this point on. There's no telling what our peer will do.
|
|
|
|
* The TDLS WIDER_BW scenario is also problematic, as peers might now
|
|
|
|
* have an incompatible wider chandef.
|
|
|
|
*/
|
|
|
|
ieee80211_teardown_tdls_peers(sdata);
|
|
|
|
|
2014-06-25 12:35:09 +02:00
|
|
|
mutex_lock(&local->mtx);
|
2013-09-01 16:15:51 +02:00
|
|
|
mutex_lock(&local->chanctx_mtx);
|
2014-06-25 12:35:09 +02:00
|
|
|
conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
|
|
|
|
lockdep_is_held(&local->chanctx_mtx));
|
|
|
|
if (!conf) {
|
|
|
|
sdata_info(sdata,
|
|
|
|
"no channel context assigned to vif?, disconnecting\n");
|
2015-03-12 07:53:25 +01:00
|
|
|
goto drop_connection;
|
2014-06-25 12:35:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
chanctx = container_of(conf, struct ieee80211_chanctx, conf);
|
|
|
|
|
2014-10-08 08:48:40 +02:00
|
|
|
if (local->use_chanctx &&
|
2015-06-02 21:39:54 +02:00
|
|
|
!ieee80211_hw_check(&local->hw, CHANCTX_STA_CSA)) {
|
2014-10-08 08:48:40 +02:00
|
|
|
sdata_info(sdata,
|
|
|
|
"driver doesn't support chan-switch with channel contexts\n");
|
2015-03-12 07:53:25 +01:00
|
|
|
goto drop_connection;
|
2012-07-26 17:24:39 +02:00
|
|
|
}
|
|
|
|
|
2014-10-08 08:48:37 +02:00
|
|
|
ch_switch.timestamp = timestamp;
|
|
|
|
ch_switch.device_timestamp = device_timestamp;
|
|
|
|
ch_switch.block_tx = csa_ie.mode;
|
|
|
|
ch_switch.chandef = csa_ie.chandef;
|
|
|
|
ch_switch.count = csa_ie.count;
|
|
|
|
|
|
|
|
if (drv_pre_channel_switch(sdata, &ch_switch)) {
|
|
|
|
sdata_info(sdata,
|
|
|
|
"preparing for channel switch failed, disconnecting\n");
|
2015-03-12 07:53:25 +01:00
|
|
|
goto drop_connection;
|
2014-10-08 08:48:37 +02:00
|
|
|
}
|
|
|
|
|
2014-06-25 12:35:09 +02:00
|
|
|
res = ieee80211_vif_reserve_chanctx(sdata, &csa_ie.chandef,
|
|
|
|
chanctx->mode, false);
|
|
|
|
if (res) {
|
2012-07-26 17:24:39 +02:00
|
|
|
sdata_info(sdata,
|
2014-06-25 12:35:09 +02:00
|
|
|
"failed to reserve channel context for channel switch, disconnecting (err=%d)\n",
|
|
|
|
res);
|
2015-03-12 07:53:25 +01:00
|
|
|
goto drop_connection;
|
2012-07-26 17:24:39 +02:00
|
|
|
}
|
2013-03-26 14:13:58 +01:00
|
|
|
mutex_unlock(&local->chanctx_mtx);
|
2012-07-26 17:24:39 +02:00
|
|
|
|
2014-01-29 07:56:19 +01:00
|
|
|
sdata->vif.csa_active = true;
|
2014-06-25 12:35:09 +02:00
|
|
|
sdata->csa_chandef = csa_ie.chandef;
|
2014-04-09 15:10:59 +02:00
|
|
|
sdata->csa_block_tx = csa_ie.mode;
|
2015-03-12 07:53:26 +01:00
|
|
|
ifmgd->csa_ignored_same_chan = false;
|
2012-07-26 17:24:39 +02:00
|
|
|
|
2014-04-09 15:10:59 +02:00
|
|
|
if (sdata->csa_block_tx)
|
2014-06-13 15:30:07 +02:00
|
|
|
ieee80211_stop_vif_queues(local, sdata,
|
|
|
|
IEEE80211_QUEUE_STOP_REASON_CSA);
|
2014-04-09 15:10:59 +02:00
|
|
|
mutex_unlock(&local->mtx);
|
2012-08-01 15:50:46 +02:00
|
|
|
|
2014-11-07 13:31:36 +01:00
|
|
|
cfg80211_ch_switch_started_notify(sdata->dev, &csa_ie.chandef,
|
|
|
|
csa_ie.count);
|
|
|
|
|
2013-03-26 14:13:58 +01:00
|
|
|
if (local->ops->channel_switch) {
|
2010-05-11 16:20:57 +02:00
|
|
|
/* use driver's channel switch callback */
|
2014-10-08 08:48:40 +02:00
|
|
|
drv_channel_switch(local, sdata, &ch_switch);
|
2010-05-11 16:20:57 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* channel switch handled in software */
|
2013-10-15 04:08:29 +02:00
|
|
|
if (csa_ie.count <= 1)
|
2013-03-26 14:13:58 +01:00
|
|
|
ieee80211_queue_work(&local->hw, &ifmgd->chswitch_work);
|
2012-08-01 15:50:46 +02:00
|
|
|
else
|
2009-05-15 11:52:31 +02:00
|
|
|
mod_timer(&ifmgd->chswitch_timer,
|
2014-10-28 12:33:05 +01:00
|
|
|
TU_TO_EXP_TIME((csa_ie.count - 1) *
|
|
|
|
cbss->beacon_interval));
|
2015-03-12 07:53:25 +01:00
|
|
|
return;
|
|
|
|
drop_connection:
|
|
|
|
ieee80211_queue_work(&local->hw, &ifmgd->csa_connection_drop_work);
|
|
|
|
mutex_unlock(&local->chanctx_mtx);
|
|
|
|
mutex_unlock(&local->mtx);
|
2009-05-15 11:52:31 +02:00
|
|
|
}
|
|
|
|
|
2014-09-03 15:22:10 +02:00
|
|
|
static bool
|
|
|
|
ieee80211_find_80211h_pwr_constr(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct ieee80211_channel *channel,
|
|
|
|
const u8 *country_ie, u8 country_ie_len,
|
|
|
|
const u8 *pwr_constr_elem,
|
|
|
|
int *chan_pwr, int *pwr_reduction)
|
2009-05-15 11:52:31 +02:00
|
|
|
{
|
2012-09-05 13:41:37 +02:00
|
|
|
struct ieee80211_country_ie_triplet *triplet;
|
|
|
|
int chan = ieee80211_frequency_to_channel(channel->center_freq);
|
2014-09-03 15:22:10 +02:00
|
|
|
int i, chan_increment;
|
2012-09-05 13:41:37 +02:00
|
|
|
bool have_chan_pwr = false;
|
2009-05-15 11:52:31 +02:00
|
|
|
|
2012-09-05 13:41:37 +02:00
|
|
|
/* Invalid IE */
|
|
|
|
if (country_ie_len % 2 || country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN)
|
2014-09-03 15:22:10 +02:00
|
|
|
return false;
|
2009-05-15 11:52:31 +02:00
|
|
|
|
2012-09-05 13:41:37 +02:00
|
|
|
triplet = (void *)(country_ie + 3);
|
|
|
|
country_ie_len -= 3;
|
|
|
|
|
|
|
|
switch (channel->band) {
|
|
|
|
default:
|
|
|
|
WARN_ON_ONCE(1);
|
|
|
|
/* fall through */
|
|
|
|
case IEEE80211_BAND_2GHZ:
|
|
|
|
case IEEE80211_BAND_60GHZ:
|
|
|
|
chan_increment = 1;
|
|
|
|
break;
|
|
|
|
case IEEE80211_BAND_5GHZ:
|
|
|
|
chan_increment = 4;
|
|
|
|
break;
|
2009-05-15 11:52:31 +02:00
|
|
|
}
|
2012-09-05 13:41:37 +02:00
|
|
|
|
|
|
|
/* find channel */
|
|
|
|
while (country_ie_len >= 3) {
|
|
|
|
u8 first_channel = triplet->chans.first_channel;
|
|
|
|
|
|
|
|
if (first_channel >= IEEE80211_COUNTRY_EXTENSION_ID)
|
|
|
|
goto next;
|
|
|
|
|
|
|
|
for (i = 0; i < triplet->chans.num_channels; i++) {
|
|
|
|
if (first_channel + i * chan_increment == chan) {
|
|
|
|
have_chan_pwr = true;
|
2014-09-03 15:22:10 +02:00
|
|
|
*chan_pwr = triplet->chans.max_power;
|
2012-09-05 13:41:37 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (have_chan_pwr)
|
|
|
|
break;
|
|
|
|
|
|
|
|
next:
|
|
|
|
triplet++;
|
|
|
|
country_ie_len -= 3;
|
|
|
|
}
|
|
|
|
|
2014-12-14 10:05:50 +01:00
|
|
|
if (have_chan_pwr && pwr_constr_elem)
|
2014-09-03 15:22:10 +02:00
|
|
|
*pwr_reduction = *pwr_constr_elem;
|
2014-12-14 10:05:50 +01:00
|
|
|
else
|
|
|
|
*pwr_reduction = 0;
|
|
|
|
|
2014-09-03 15:22:10 +02:00
|
|
|
return have_chan_pwr;
|
|
|
|
}
|
|
|
|
|
mac80211: support DTPC IE (from Cisco Client eXtensions)
Linux already supports 802.11h, where the access point can tell the
client to reduce its transmission power. However, 802.11h is only
defined for 5 GHz, where the need for this is much smaller than on
2.4 GHz.
Cisco has their own solution, called DTPC (Dynamic Transmit Power
Control). Cisco APs on a controller sometimes but not always send
802.11h; they always send DTPC, even on 2.4 GHz. This patch adds support
for parsing and honoring the DTPC IE in addition to the 802.11h
element (they do not always contain the same limits, so both must
be honored); the format is not documented, but very simple.
Tested (on top of wireless.git and on 3.16.1) against a Cisco Aironet
1142 joined to a Cisco 2504 WLC, by setting various transmit power
levels for the given access points and observing the results.
The Wireshark 802.11 dissector agrees with the interpretation of the
element, except for negative numbers, which seem to never happen
anyway.
Signed-off-by: Steinar H. Gunderson <sgunderson@bigfoot.com>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
2014-09-03 15:48:37 +02:00
|
|
|
static void ieee80211_find_cisco_dtpc(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct ieee80211_channel *channel,
|
|
|
|
const u8 *cisco_dtpc_ie,
|
|
|
|
int *pwr_level)
|
|
|
|
{
|
|
|
|
/* From practical testing, the first data byte of the DTPC element
|
|
|
|
* seems to contain the requested dBm level, and the CLI on Cisco
|
|
|
|
* APs clearly state the range is -127 to 127 dBm, which indicates
|
|
|
|
* a signed byte, although it seemingly never actually goes negative.
|
|
|
|
* The other byte seems to always be zero.
|
|
|
|
*/
|
|
|
|
*pwr_level = (__s8)cisco_dtpc_ie[4];
|
|
|
|
}
|
|
|
|
|
2014-09-03 15:22:10 +02:00
|
|
|
static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct ieee80211_channel *channel,
|
|
|
|
struct ieee80211_mgmt *mgmt,
|
|
|
|
const u8 *country_ie, u8 country_ie_len,
|
mac80211: support DTPC IE (from Cisco Client eXtensions)
Linux already supports 802.11h, where the access point can tell the
client to reduce its transmission power. However, 802.11h is only
defined for 5 GHz, where the need for this is much smaller than on
2.4 GHz.
Cisco has their own solution, called DTPC (Dynamic Transmit Power
Control). Cisco APs on a controller sometimes but not always send
802.11h; they always send DTPC, even on 2.4 GHz. This patch adds support
for parsing and honoring the DTPC IE in addition to the 802.11h
element (they do not always contain the same limits, so both must
be honored); the format is not documented, but very simple.
Tested (on top of wireless.git and on 3.16.1) against a Cisco Aironet
1142 joined to a Cisco 2504 WLC, by setting various transmit power
levels for the given access points and observing the results.
The Wireshark 802.11 dissector agrees with the interpretation of the
element, except for negative numbers, which seem to never happen
anyway.
Signed-off-by: Steinar H. Gunderson <sgunderson@bigfoot.com>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
2014-09-03 15:48:37 +02:00
|
|
|
const u8 *pwr_constr_ie,
|
|
|
|
const u8 *cisco_dtpc_ie)
|
2014-09-03 15:22:10 +02:00
|
|
|
{
|
mac80211: support DTPC IE (from Cisco Client eXtensions)
Linux already supports 802.11h, where the access point can tell the
client to reduce its transmission power. However, 802.11h is only
defined for 5 GHz, where the need for this is much smaller than on
2.4 GHz.
Cisco has their own solution, called DTPC (Dynamic Transmit Power
Control). Cisco APs on a controller sometimes but not always send
802.11h; they always send DTPC, even on 2.4 GHz. This patch adds support
for parsing and honoring the DTPC IE in addition to the 802.11h
element (they do not always contain the same limits, so both must
be honored); the format is not documented, but very simple.
Tested (on top of wireless.git and on 3.16.1) against a Cisco Aironet
1142 joined to a Cisco 2504 WLC, by setting various transmit power
levels for the given access points and observing the results.
The Wireshark 802.11 dissector agrees with the interpretation of the
element, except for negative numbers, which seem to never happen
anyway.
Signed-off-by: Steinar H. Gunderson <sgunderson@bigfoot.com>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
2014-09-03 15:48:37 +02:00
|
|
|
bool has_80211h_pwr = false, has_cisco_pwr = false;
|
|
|
|
int chan_pwr = 0, pwr_reduction_80211h = 0;
|
|
|
|
int pwr_level_cisco, pwr_level_80211h;
|
2014-09-03 15:22:10 +02:00
|
|
|
int new_ap_level;
|
2014-12-14 10:05:50 +01:00
|
|
|
__le16 capab = mgmt->u.probe_resp.capab_info;
|
2012-09-05 13:41:37 +02:00
|
|
|
|
2014-12-14 10:05:50 +01:00
|
|
|
if (country_ie &&
|
|
|
|
(capab & cpu_to_le16(WLAN_CAPABILITY_SPECTRUM_MGMT) ||
|
|
|
|
capab & cpu_to_le16(WLAN_CAPABILITY_RADIO_MEASURE))) {
|
2014-09-03 15:22:10 +02:00
|
|
|
has_80211h_pwr = ieee80211_find_80211h_pwr_constr(
|
|
|
|
sdata, channel, country_ie, country_ie_len,
|
|
|
|
pwr_constr_ie, &chan_pwr, &pwr_reduction_80211h);
|
mac80211: support DTPC IE (from Cisco Client eXtensions)
Linux already supports 802.11h, where the access point can tell the
client to reduce its transmission power. However, 802.11h is only
defined for 5 GHz, where the need for this is much smaller than on
2.4 GHz.
Cisco has their own solution, called DTPC (Dynamic Transmit Power
Control). Cisco APs on a controller sometimes but not always send
802.11h; they always send DTPC, even on 2.4 GHz. This patch adds support
for parsing and honoring the DTPC IE in addition to the 802.11h
element (they do not always contain the same limits, so both must
be honored); the format is not documented, but very simple.
Tested (on top of wireless.git and on 3.16.1) against a Cisco Aironet
1142 joined to a Cisco 2504 WLC, by setting various transmit power
levels for the given access points and observing the results.
The Wireshark 802.11 dissector agrees with the interpretation of the
element, except for negative numbers, which seem to never happen
anyway.
Signed-off-by: Steinar H. Gunderson <sgunderson@bigfoot.com>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
2014-09-03 15:48:37 +02:00
|
|
|
pwr_level_80211h =
|
|
|
|
max_t(int, 0, chan_pwr - pwr_reduction_80211h);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cisco_dtpc_ie) {
|
|
|
|
ieee80211_find_cisco_dtpc(
|
|
|
|
sdata, channel, cisco_dtpc_ie, &pwr_level_cisco);
|
|
|
|
has_cisco_pwr = true;
|
2014-09-03 15:22:10 +02:00
|
|
|
}
|
2012-09-05 13:41:37 +02:00
|
|
|
|
mac80211: support DTPC IE (from Cisco Client eXtensions)
Linux already supports 802.11h, where the access point can tell the
client to reduce its transmission power. However, 802.11h is only
defined for 5 GHz, where the need for this is much smaller than on
2.4 GHz.
Cisco has their own solution, called DTPC (Dynamic Transmit Power
Control). Cisco APs on a controller sometimes but not always send
802.11h; they always send DTPC, even on 2.4 GHz. This patch adds support
for parsing and honoring the DTPC IE in addition to the 802.11h
element (they do not always contain the same limits, so both must
be honored); the format is not documented, but very simple.
Tested (on top of wireless.git and on 3.16.1) against a Cisco Aironet
1142 joined to a Cisco 2504 WLC, by setting various transmit power
levels for the given access points and observing the results.
The Wireshark 802.11 dissector agrees with the interpretation of the
element, except for negative numbers, which seem to never happen
anyway.
Signed-off-by: Steinar H. Gunderson <sgunderson@bigfoot.com>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
2014-09-03 15:48:37 +02:00
|
|
|
if (!has_80211h_pwr && !has_cisco_pwr)
|
2012-10-24 10:59:25 +02:00
|
|
|
return 0;
|
2012-09-05 13:41:37 +02:00
|
|
|
|
mac80211: support DTPC IE (from Cisco Client eXtensions)
Linux already supports 802.11h, where the access point can tell the
client to reduce its transmission power. However, 802.11h is only
defined for 5 GHz, where the need for this is much smaller than on
2.4 GHz.
Cisco has their own solution, called DTPC (Dynamic Transmit Power
Control). Cisco APs on a controller sometimes but not always send
802.11h; they always send DTPC, even on 2.4 GHz. This patch adds support
for parsing and honoring the DTPC IE in addition to the 802.11h
element (they do not always contain the same limits, so both must
be honored); the format is not documented, but very simple.
Tested (on top of wireless.git and on 3.16.1) against a Cisco Aironet
1142 joined to a Cisco 2504 WLC, by setting various transmit power
levels for the given access points and observing the results.
The Wireshark 802.11 dissector agrees with the interpretation of the
element, except for negative numbers, which seem to never happen
anyway.
Signed-off-by: Steinar H. Gunderson <sgunderson@bigfoot.com>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
2014-09-03 15:48:37 +02:00
|
|
|
/* If we have both 802.11h and Cisco DTPC, apply both limits
|
|
|
|
* by picking the smallest of the two power levels advertised.
|
|
|
|
*/
|
|
|
|
if (has_80211h_pwr &&
|
|
|
|
(!has_cisco_pwr || pwr_level_80211h <= pwr_level_cisco)) {
|
2015-03-31 16:49:14 +02:00
|
|
|
sdata_dbg(sdata,
|
|
|
|
"Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n",
|
|
|
|
pwr_level_80211h, chan_pwr, pwr_reduction_80211h,
|
|
|
|
sdata->u.mgd.bssid);
|
mac80211: support DTPC IE (from Cisco Client eXtensions)
Linux already supports 802.11h, where the access point can tell the
client to reduce its transmission power. However, 802.11h is only
defined for 5 GHz, where the need for this is much smaller than on
2.4 GHz.
Cisco has their own solution, called DTPC (Dynamic Transmit Power
Control). Cisco APs on a controller sometimes but not always send
802.11h; they always send DTPC, even on 2.4 GHz. This patch adds support
for parsing and honoring the DTPC IE in addition to the 802.11h
element (they do not always contain the same limits, so both must
be honored); the format is not documented, but very simple.
Tested (on top of wireless.git and on 3.16.1) against a Cisco Aironet
1142 joined to a Cisco 2504 WLC, by setting various transmit power
levels for the given access points and observing the results.
The Wireshark 802.11 dissector agrees with the interpretation of the
element, except for negative numbers, which seem to never happen
anyway.
Signed-off-by: Steinar H. Gunderson <sgunderson@bigfoot.com>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
2014-09-03 15:48:37 +02:00
|
|
|
new_ap_level = pwr_level_80211h;
|
|
|
|
} else { /* has_cisco_pwr is always true here. */
|
2015-03-31 16:49:14 +02:00
|
|
|
sdata_dbg(sdata,
|
|
|
|
"Limiting TX power to %d dBm as advertised by %pM\n",
|
|
|
|
pwr_level_cisco, sdata->u.mgd.bssid);
|
mac80211: support DTPC IE (from Cisco Client eXtensions)
Linux already supports 802.11h, where the access point can tell the
client to reduce its transmission power. However, 802.11h is only
defined for 5 GHz, where the need for this is much smaller than on
2.4 GHz.
Cisco has their own solution, called DTPC (Dynamic Transmit Power
Control). Cisco APs on a controller sometimes but not always send
802.11h; they always send DTPC, even on 2.4 GHz. This patch adds support
for parsing and honoring the DTPC IE in addition to the 802.11h
element (they do not always contain the same limits, so both must
be honored); the format is not documented, but very simple.
Tested (on top of wireless.git and on 3.16.1) against a Cisco Aironet
1142 joined to a Cisco 2504 WLC, by setting various transmit power
levels for the given access points and observing the results.
The Wireshark 802.11 dissector agrees with the interpretation of the
element, except for negative numbers, which seem to never happen
anyway.
Signed-off-by: Steinar H. Gunderson <sgunderson@bigfoot.com>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
2014-09-03 15:48:37 +02:00
|
|
|
new_ap_level = pwr_level_cisco;
|
|
|
|
}
|
2012-09-05 13:41:37 +02:00
|
|
|
|
2012-10-24 10:59:25 +02:00
|
|
|
if (sdata->ap_power_level == new_ap_level)
|
|
|
|
return 0;
|
2012-09-05 13:41:37 +02:00
|
|
|
|
2012-10-24 10:59:25 +02:00
|
|
|
sdata->ap_power_level = new_ap_level;
|
|
|
|
if (__ieee80211_recalc_txpower(sdata))
|
|
|
|
return BSS_CHANGED_TXPOWER;
|
|
|
|
return 0;
|
2009-05-15 11:52:31 +02:00
|
|
|
}
|
|
|
|
|
2009-04-16 13:17:24 +02:00
|
|
|
/* powersave */
|
|
|
|
static void ieee80211_enable_ps(struct ieee80211_local *local,
|
|
|
|
struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
|
|
|
struct ieee80211_conf *conf = &local->hw.conf;
|
|
|
|
|
2009-04-22 23:02:51 +02:00
|
|
|
/*
|
|
|
|
* If we are scanning right now then the parameters will
|
|
|
|
* take effect when scan finishes.
|
|
|
|
*/
|
2009-07-23 12:14:04 +02:00
|
|
|
if (local->scanning)
|
2009-04-22 23:02:51 +02:00
|
|
|
return;
|
|
|
|
|
2009-04-16 13:17:24 +02:00
|
|
|
if (conf->dynamic_ps_timeout > 0 &&
|
2015-06-02 21:39:54 +02:00
|
|
|
!ieee80211_hw_check(&local->hw, SUPPORTS_DYNAMIC_PS)) {
|
2009-04-16 13:17:24 +02:00
|
|
|
mod_timer(&local->dynamic_ps_timer, jiffies +
|
|
|
|
msecs_to_jiffies(conf->dynamic_ps_timeout));
|
|
|
|
} else {
|
2015-06-02 21:39:54 +02:00
|
|
|
if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK))
|
2009-04-16 13:17:24 +02:00
|
|
|
ieee80211_send_nullfunc(local, sdata, 1);
|
2010-02-09 10:20:28 +01:00
|
|
|
|
2015-06-02 21:39:54 +02:00
|
|
|
if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK) &&
|
|
|
|
ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS))
|
2010-03-09 13:25:02 +01:00
|
|
|
return;
|
|
|
|
|
|
|
|
conf->flags |= IEEE80211_CONF_PS;
|
|
|
|
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
|
2009-04-16 13:17:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ieee80211_change_ps(struct ieee80211_local *local)
|
|
|
|
{
|
|
|
|
struct ieee80211_conf *conf = &local->hw.conf;
|
|
|
|
|
|
|
|
if (local->ps_sdata) {
|
|
|
|
ieee80211_enable_ps(local, local->ps_sdata);
|
|
|
|
} else if (conf->flags & IEEE80211_CONF_PS) {
|
|
|
|
conf->flags &= ~IEEE80211_CONF_PS;
|
|
|
|
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
|
|
|
|
del_timer_sync(&local->dynamic_ps_timer);
|
|
|
|
cancel_work_sync(&local->dynamic_ps_enable_work);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-03-11 01:43:19 +01:00
|
|
|
static bool ieee80211_powersave_allowed(struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
|
|
|
struct ieee80211_if_managed *mgd = &sdata->u.mgd;
|
|
|
|
struct sta_info *sta = NULL;
|
2011-09-29 16:04:36 +02:00
|
|
|
bool authorized = false;
|
2011-03-11 01:43:19 +01:00
|
|
|
|
|
|
|
if (!mgd->powersave)
|
|
|
|
return false;
|
|
|
|
|
2011-10-28 11:59:47 +02:00
|
|
|
if (mgd->broken_ap)
|
|
|
|
return false;
|
|
|
|
|
2011-03-11 01:43:19 +01:00
|
|
|
if (!mgd->associated)
|
|
|
|
return false;
|
|
|
|
|
2013-08-27 11:36:35 +02:00
|
|
|
if (mgd->flags & IEEE80211_STA_CONNECTION_POLL)
|
2011-03-11 01:43:19 +01:00
|
|
|
return false;
|
|
|
|
|
2013-05-16 16:34:17 +02:00
|
|
|
if (!mgd->have_beacon)
|
2013-05-06 16:17:04 +02:00
|
|
|
return false;
|
|
|
|
|
2011-03-11 01:43:19 +01:00
|
|
|
rcu_read_lock();
|
|
|
|
sta = sta_info_get(sdata, mgd->bssid);
|
|
|
|
if (sta)
|
2011-09-29 16:04:36 +02:00
|
|
|
authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
|
2011-03-11 01:43:19 +01:00
|
|
|
rcu_read_unlock();
|
|
|
|
|
2011-09-29 16:04:36 +02:00
|
|
|
return authorized;
|
2011-03-11 01:43:19 +01:00
|
|
|
}
|
|
|
|
|
2009-04-16 13:17:24 +02:00
|
|
|
/* need to hold RTNL or interface lock */
|
2009-04-16 13:17:25 +02:00
|
|
|
void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
|
2009-04-16 13:17:24 +02:00
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata, *found = NULL;
|
|
|
|
int count = 0;
|
2010-04-27 11:47:40 +02:00
|
|
|
int timeout;
|
2009-04-16 13:17:24 +02:00
|
|
|
|
2015-06-02 21:39:54 +02:00
|
|
|
if (!ieee80211_hw_check(&local->hw, SUPPORTS_PS)) {
|
2009-04-16 13:17:24 +02:00
|
|
|
local->ps_sdata = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
list_for_each_entry(sdata, &local->interfaces, list) {
|
2009-12-23 13:15:31 +01:00
|
|
|
if (!ieee80211_sdata_running(sdata))
|
2009-04-16 13:17:24 +02:00
|
|
|
continue;
|
2011-01-31 19:58:59 +01:00
|
|
|
if (sdata->vif.type == NL80211_IFTYPE_AP) {
|
|
|
|
/* If an AP vif is found, then disable PS
|
|
|
|
* by setting the count to zero thereby setting
|
|
|
|
* ps_sdata to NULL.
|
|
|
|
*/
|
|
|
|
count = 0;
|
|
|
|
break;
|
|
|
|
}
|
2009-04-16 13:17:24 +02:00
|
|
|
if (sdata->vif.type != NL80211_IFTYPE_STATION)
|
|
|
|
continue;
|
|
|
|
found = sdata;
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
|
2011-03-11 01:43:19 +01:00
|
|
|
if (count == 1 && ieee80211_powersave_allowed(found)) {
|
2009-04-16 13:17:25 +02:00
|
|
|
s32 beaconint_us;
|
|
|
|
|
|
|
|
if (latency < 0)
|
2010-05-06 01:59:26 +02:00
|
|
|
latency = pm_qos_request(PM_QOS_NETWORK_LATENCY);
|
2009-04-16 13:17:25 +02:00
|
|
|
|
|
|
|
beaconint_us = ieee80211_tu_to_usec(
|
|
|
|
found->vif.bss_conf.beacon_int);
|
|
|
|
|
2010-06-09 08:51:52 +02:00
|
|
|
timeout = local->dynamic_ps_forced_timeout;
|
2010-04-27 11:47:40 +02:00
|
|
|
if (timeout < 0) {
|
|
|
|
/*
|
2010-06-09 08:51:52 +02:00
|
|
|
* Go to full PSM if the user configures a very low
|
|
|
|
* latency requirement.
|
2010-12-03 01:16:23 +01:00
|
|
|
* The 2000 second value is there for compatibility
|
|
|
|
* until the PM_QOS_NETWORK_LATENCY is configured
|
|
|
|
* with real values.
|
2010-04-27 11:47:40 +02:00
|
|
|
*/
|
2010-12-03 01:16:23 +01:00
|
|
|
if (latency > (1900 * USEC_PER_MSEC) &&
|
|
|
|
latency != (2000 * USEC_PER_SEC))
|
2010-04-27 11:47:40 +02:00
|
|
|
timeout = 0;
|
2010-06-09 08:51:52 +02:00
|
|
|
else
|
|
|
|
timeout = 100;
|
2010-04-27 11:47:40 +02:00
|
|
|
}
|
2013-02-06 23:07:41 +01:00
|
|
|
local->hw.conf.dynamic_ps_timeout = timeout;
|
2010-04-27 11:47:40 +02:00
|
|
|
|
2009-04-22 18:44:37 +02:00
|
|
|
if (beaconint_us > latency) {
|
2009-04-16 13:17:25 +02:00
|
|
|
local->ps_sdata = NULL;
|
2009-04-22 18:44:37 +02:00
|
|
|
} else {
|
|
|
|
int maxslp = 1;
|
2012-12-10 15:38:14 +01:00
|
|
|
u8 dtimper = found->u.mgd.dtim_period;
|
2010-01-26 14:19:52 +01:00
|
|
|
|
|
|
|
/* If the TIM IE is invalid, pretend the value is 1 */
|
|
|
|
if (!dtimper)
|
|
|
|
dtimper = 1;
|
|
|
|
else if (dtimper > 1)
|
2009-04-22 18:44:37 +02:00
|
|
|
maxslp = min_t(int, dtimper,
|
|
|
|
latency / beaconint_us);
|
|
|
|
|
2009-04-23 10:32:36 +02:00
|
|
|
local->hw.conf.max_sleep_period = maxslp;
|
2010-01-26 14:19:52 +01:00
|
|
|
local->hw.conf.ps_dtim_period = dtimper;
|
2009-04-16 13:17:25 +02:00
|
|
|
local->ps_sdata = found;
|
2009-04-22 18:44:37 +02:00
|
|
|
}
|
2009-04-16 13:17:25 +02:00
|
|
|
} else {
|
2009-04-16 13:17:24 +02:00
|
|
|
local->ps_sdata = NULL;
|
2009-04-16 13:17:25 +02:00
|
|
|
}
|
2009-04-16 13:17:24 +02:00
|
|
|
|
|
|
|
ieee80211_change_ps(local);
|
|
|
|
}
|
|
|
|
|
2012-07-27 11:33:22 +02:00
|
|
|
void ieee80211_recalc_ps_vif(struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
|
|
|
bool ps_allowed = ieee80211_powersave_allowed(sdata);
|
|
|
|
|
|
|
|
if (sdata->vif.bss_conf.ps != ps_allowed) {
|
|
|
|
sdata->vif.bss_conf.ps = ps_allowed;
|
|
|
|
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_PS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-16 13:17:24 +02:00
|
|
|
void ieee80211_dynamic_ps_disable_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct ieee80211_local *local =
|
|
|
|
container_of(work, struct ieee80211_local,
|
|
|
|
dynamic_ps_disable_work);
|
|
|
|
|
|
|
|
if (local->hw.conf.flags & IEEE80211_CONF_PS) {
|
|
|
|
local->hw.conf.flags &= ~IEEE80211_CONF_PS;
|
|
|
|
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
|
|
|
|
}
|
|
|
|
|
|
|
|
ieee80211_wake_queues_by_reason(&local->hw,
|
2013-02-13 12:25:28 +01:00
|
|
|
IEEE80211_MAX_QUEUE_MAP,
|
2014-06-13 15:30:05 +02:00
|
|
|
IEEE80211_QUEUE_STOP_REASON_PS,
|
|
|
|
false);
|
2009-04-16 13:17:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct ieee80211_local *local =
|
|
|
|
container_of(work, struct ieee80211_local,
|
|
|
|
dynamic_ps_enable_work);
|
|
|
|
struct ieee80211_sub_if_data *sdata = local->ps_sdata;
|
2011-06-30 21:08:43 +02:00
|
|
|
struct ieee80211_if_managed *ifmgd;
|
2011-05-03 13:33:59 +02:00
|
|
|
unsigned long flags;
|
|
|
|
int q;
|
2009-04-16 13:17:24 +02:00
|
|
|
|
|
|
|
/* can only happen when PS was just disabled anyway */
|
|
|
|
if (!sdata)
|
|
|
|
return;
|
|
|
|
|
2011-06-30 21:08:43 +02:00
|
|
|
ifmgd = &sdata->u.mgd;
|
|
|
|
|
2009-04-16 13:17:24 +02:00
|
|
|
if (local->hw.conf.flags & IEEE80211_CONF_PS)
|
|
|
|
return;
|
|
|
|
|
2013-02-06 23:07:41 +01:00
|
|
|
if (local->hw.conf.dynamic_ps_timeout > 0) {
|
2011-06-26 11:06:54 +02:00
|
|
|
/* don't enter PS if TX frames are pending */
|
|
|
|
if (drv_tx_frames_pending(local)) {
|
2011-05-03 13:33:59 +02:00
|
|
|
mod_timer(&local->dynamic_ps_timer, jiffies +
|
|
|
|
msecs_to_jiffies(
|
|
|
|
local->hw.conf.dynamic_ps_timeout));
|
|
|
|
return;
|
|
|
|
}
|
2011-06-26 11:06:54 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* transmission can be stopped by others which leads to
|
|
|
|
* dynamic_ps_timer expiry. Postpone the ps timer if it
|
|
|
|
* is not the actual idle state.
|
|
|
|
*/
|
|
|
|
spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
|
|
|
|
for (q = 0; q < local->hw.queues; q++) {
|
|
|
|
if (local->queue_stop_reasons[q]) {
|
|
|
|
spin_unlock_irqrestore(&local->queue_stop_reason_lock,
|
|
|
|
flags);
|
|
|
|
mod_timer(&local->dynamic_ps_timer, jiffies +
|
|
|
|
msecs_to_jiffies(
|
|
|
|
local->hw.conf.dynamic_ps_timeout));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
|
2011-05-03 13:33:59 +02:00
|
|
|
}
|
|
|
|
|
2015-06-02 21:39:54 +02:00
|
|
|
if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK) &&
|
2011-12-14 15:16:07 +01:00
|
|
|
!(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)) {
|
2013-03-26 22:02:42 +01:00
|
|
|
if (drv_tx_frames_pending(local)) {
|
2011-04-06 08:11:10 +02:00
|
|
|
mod_timer(&local->dynamic_ps_timer, jiffies +
|
|
|
|
msecs_to_jiffies(
|
|
|
|
local->hw.conf.dynamic_ps_timeout));
|
2013-03-26 22:02:42 +01:00
|
|
|
} else {
|
2011-04-06 08:11:10 +02:00
|
|
|
ieee80211_send_nullfunc(local, sdata, 1);
|
|
|
|
/* Flush to get the tx status of nullfunc frame */
|
2015-01-07 14:42:39 +01:00
|
|
|
ieee80211_flush_queues(local, sdata, false);
|
2011-04-06 08:11:10 +02:00
|
|
|
}
|
2011-02-23 08:34:32 +01:00
|
|
|
}
|
|
|
|
|
2015-06-02 21:39:54 +02:00
|
|
|
if (!(ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS) &&
|
|
|
|
ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK)) ||
|
2010-02-09 10:20:28 +01:00
|
|
|
(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)) {
|
|
|
|
ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;
|
|
|
|
local->hw.conf.flags |= IEEE80211_CONF_PS;
|
|
|
|
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
|
|
|
|
}
|
2009-04-16 13:17:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void ieee80211_dynamic_ps_timer(unsigned long data)
|
|
|
|
{
|
|
|
|
struct ieee80211_local *local = (void *) data;
|
|
|
|
|
2009-07-30 02:08:07 +02:00
|
|
|
ieee80211_queue_work(&local->hw, &local->dynamic_ps_enable_work);
|
2009-04-16 13:17:24 +02:00
|
|
|
}
|
|
|
|
|
2013-02-08 18:16:20 +01:00
|
|
|
void ieee80211_dfs_cac_timer_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct delayed_work *delayed_work =
|
|
|
|
container_of(work, struct delayed_work, work);
|
|
|
|
struct ieee80211_sub_if_data *sdata =
|
|
|
|
container_of(delayed_work, struct ieee80211_sub_if_data,
|
|
|
|
dfs_cac_timer_work);
|
2013-11-06 13:55:51 +01:00
|
|
|
struct cfg80211_chan_def chandef = sdata->vif.bss_conf.chandef;
|
2013-02-08 18:16:20 +01:00
|
|
|
|
mac80211: fix iflist_mtx/mtx locking in radar detection
The scan code creates an iflist_mtx -> mtx locking dependency,
and a few other places, notably radar detection, were creating
the opposite dependency, causing lockdep to complain. As scan
and radar detection are mutually exclusive, the deadlock can't
really happen in practice, but it's still bad form.
A similar issue exists in the monitor mode code, but this is
only used by channel-context drivers right now and those have
to have hardware scan, so that also can't happen.
Still, fix these issues by making some of the channel context
code require the mtx to be held rather than acquiring it, thus
allowing the monitor/radar callers to keep the iflist_mtx->mtx
lock ordering.
While at it, also fix access to the local->scanning variable
in the radar code, and document that radar_detect_enabled is
now properly protected by the mtx.
All this would now introduce an ABBA deadlock between the DFS
work cancelling and local->mtx, so change the locking there a
bit to not need to use cancel_delayed_work_sync() but be able
to just use cancel_delayed_work(). The work is also safely
stopped/removed when the interface is stopped, so no extra
changes are needed.
Reported-by: Kalle Valo <kvalo@qca.qualcomm.com>
Tested-by: Simon Wunderlich <sw@simonwunderlich.de>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2013-12-18 09:43:33 +01:00
|
|
|
mutex_lock(&sdata->local->mtx);
|
|
|
|
if (sdata->wdev.cac_started) {
|
|
|
|
ieee80211_vif_release_channel(sdata);
|
|
|
|
cfg80211_cac_event(sdata->dev, &chandef,
|
|
|
|
NL80211_RADAR_CAC_FINISHED,
|
|
|
|
GFP_KERNEL);
|
|
|
|
}
|
|
|
|
mutex_unlock(&sdata->local->mtx);
|
2013-02-08 18:16:20 +01:00
|
|
|
}
|
|
|
|
|
2014-10-07 09:38:50 +02:00
|
|
|
static bool
|
|
|
|
__ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2015-01-06 20:39:33 +01:00
|
|
|
bool ret = false;
|
2014-10-07 09:38:50 +02:00
|
|
|
int ac;
|
|
|
|
|
|
|
|
if (local->hw.queues < IEEE80211_NUM_ACS)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
|
|
|
|
struct ieee80211_sta_tx_tspec *tx_tspec = &ifmgd->tx_tspec[ac];
|
|
|
|
int non_acm_ac;
|
|
|
|
unsigned long now = jiffies;
|
|
|
|
|
|
|
|
if (tx_tspec->action == TX_TSPEC_ACTION_NONE &&
|
|
|
|
tx_tspec->admitted_time &&
|
|
|
|
time_after(now, tx_tspec->time_slice_start + HZ)) {
|
|
|
|
tx_tspec->consumed_tx_time = 0;
|
|
|
|
tx_tspec->time_slice_start = now;
|
|
|
|
|
|
|
|
if (tx_tspec->downgraded)
|
|
|
|
tx_tspec->action =
|
|
|
|
TX_TSPEC_ACTION_STOP_DOWNGRADE;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (tx_tspec->action) {
|
|
|
|
case TX_TSPEC_ACTION_STOP_DOWNGRADE:
|
|
|
|
/* take the original parameters */
|
|
|
|
if (drv_conf_tx(local, sdata, ac, &sdata->tx_conf[ac]))
|
|
|
|
sdata_err(sdata,
|
|
|
|
"failed to set TX queue parameters for queue %d\n",
|
|
|
|
ac);
|
|
|
|
tx_tspec->action = TX_TSPEC_ACTION_NONE;
|
|
|
|
tx_tspec->downgraded = false;
|
|
|
|
ret = true;
|
|
|
|
break;
|
|
|
|
case TX_TSPEC_ACTION_DOWNGRADE:
|
|
|
|
if (time_after(now, tx_tspec->time_slice_start + HZ)) {
|
|
|
|
tx_tspec->action = TX_TSPEC_ACTION_NONE;
|
|
|
|
ret = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* downgrade next lower non-ACM AC */
|
|
|
|
for (non_acm_ac = ac + 1;
|
|
|
|
non_acm_ac < IEEE80211_NUM_ACS;
|
|
|
|
non_acm_ac++)
|
|
|
|
if (!(sdata->wmm_acm & BIT(7 - 2 * non_acm_ac)))
|
|
|
|
break;
|
|
|
|
/* The loop will result in using BK even if it requires
|
|
|
|
* admission control, such configuration makes no sense
|
|
|
|
* and we have to transmit somehow - the AC selection
|
|
|
|
* does the same thing.
|
|
|
|
*/
|
|
|
|
if (drv_conf_tx(local, sdata, ac,
|
|
|
|
&sdata->tx_conf[non_acm_ac]))
|
|
|
|
sdata_err(sdata,
|
|
|
|
"failed to set TX queue parameters for queue %d\n",
|
|
|
|
ac);
|
|
|
|
tx_tspec->action = TX_TSPEC_ACTION_NONE;
|
|
|
|
ret = true;
|
|
|
|
schedule_delayed_work(&ifmgd->tx_tspec_wk,
|
|
|
|
tx_tspec->time_slice_start + HZ - now + 1);
|
|
|
|
break;
|
|
|
|
case TX_TSPEC_ACTION_NONE:
|
|
|
|
/* nothing now */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
|
|
|
if (__ieee80211_sta_handle_tspec_ac_params(sdata))
|
|
|
|
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_QOS);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ieee80211_sta_handle_tspec_ac_params_wk(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata;
|
|
|
|
|
|
|
|
sdata = container_of(work, struct ieee80211_sub_if_data,
|
|
|
|
u.mgd.tx_tspec_wk.work);
|
|
|
|
ieee80211_sta_handle_tspec_ac_params(sdata);
|
|
|
|
}
|
|
|
|
|
2008-09-08 17:44:22 +02:00
|
|
|
/* MLME */
|
2012-07-06 17:37:43 +02:00
|
|
|
static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
|
2010-07-19 16:39:04 +02:00
|
|
|
struct ieee80211_sub_if_data *sdata,
|
2013-02-12 16:43:19 +01:00
|
|
|
const u8 *wmm_param, size_t wmm_param_len)
|
2007-05-05 20:45:53 +02:00
|
|
|
{
|
|
|
|
struct ieee80211_tx_queue_params params;
|
2010-07-19 16:39:04 +02:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2007-05-05 20:45:53 +02:00
|
|
|
size_t left;
|
|
|
|
int count;
|
2013-02-12 16:43:19 +01:00
|
|
|
const u8 *pos;
|
|
|
|
u8 uapsd_queues = 0;
|
2007-05-05 20:45:53 +02:00
|
|
|
|
2010-03-29 12:18:34 +02:00
|
|
|
if (!local->ops->conf_tx)
|
2012-07-06 17:37:43 +02:00
|
|
|
return false;
|
2010-03-29 12:18:34 +02:00
|
|
|
|
2012-03-28 11:04:29 +02:00
|
|
|
if (local->hw.queues < IEEE80211_NUM_ACS)
|
2012-07-06 17:37:43 +02:00
|
|
|
return false;
|
2008-05-03 00:59:37 +02:00
|
|
|
|
|
|
|
if (!wmm_param)
|
2012-07-06 17:37:43 +02:00
|
|
|
return false;
|
2008-05-03 00:59:37 +02:00
|
|
|
|
2007-05-05 20:45:53 +02:00
|
|
|
if (wmm_param_len < 8 || wmm_param[5] /* version */ != 1)
|
2012-07-06 17:37:43 +02:00
|
|
|
return false;
|
2010-01-12 09:42:31 +01:00
|
|
|
|
|
|
|
if (ifmgd->flags & IEEE80211_STA_UAPSD_ENABLED)
|
2012-03-14 15:15:03 +01:00
|
|
|
uapsd_queues = ifmgd->uapsd_queues;
|
2010-01-12 09:42:31 +01:00
|
|
|
|
2007-05-05 20:45:53 +02:00
|
|
|
count = wmm_param[6] & 0x0f;
|
2009-02-15 12:44:28 +01:00
|
|
|
if (count == ifmgd->wmm_last_param_set)
|
2012-07-06 17:37:43 +02:00
|
|
|
return false;
|
2009-02-15 12:44:28 +01:00
|
|
|
ifmgd->wmm_last_param_set = count;
|
2007-05-05 20:45:53 +02:00
|
|
|
|
|
|
|
pos = wmm_param + 8;
|
|
|
|
left = wmm_param_len - 8;
|
|
|
|
|
|
|
|
memset(¶ms, 0, sizeof(params));
|
|
|
|
|
2012-06-20 14:39:13 +02:00
|
|
|
sdata->wmm_acm = 0;
|
2007-05-05 20:45:53 +02:00
|
|
|
for (; left >= 4; left -= 4, pos += 4) {
|
|
|
|
int aci = (pos[0] >> 5) & 0x03;
|
|
|
|
int acm = (pos[0] >> 4) & 0x01;
|
2010-01-12 09:42:31 +01:00
|
|
|
bool uapsd = false;
|
2007-05-05 20:45:53 +02:00
|
|
|
int queue;
|
|
|
|
|
|
|
|
switch (aci) {
|
2009-03-05 16:23:46 +01:00
|
|
|
case 1: /* AC_BK */
|
2008-04-30 18:51:21 +02:00
|
|
|
queue = 3;
|
2008-04-17 19:21:22 +02:00
|
|
|
if (acm)
|
2012-06-20 14:39:13 +02:00
|
|
|
sdata->wmm_acm |= BIT(1) | BIT(2); /* BK/- */
|
2010-01-12 09:42:31 +01:00
|
|
|
if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK)
|
|
|
|
uapsd = true;
|
2007-05-05 20:45:53 +02:00
|
|
|
break;
|
2009-03-05 16:23:46 +01:00
|
|
|
case 2: /* AC_VI */
|
2008-04-30 18:51:21 +02:00
|
|
|
queue = 1;
|
2008-04-17 19:21:22 +02:00
|
|
|
if (acm)
|
2012-06-20 14:39:13 +02:00
|
|
|
sdata->wmm_acm |= BIT(4) | BIT(5); /* CL/VI */
|
2010-01-12 09:42:31 +01:00
|
|
|
if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI)
|
|
|
|
uapsd = true;
|
2007-05-05 20:45:53 +02:00
|
|
|
break;
|
2009-03-05 16:23:46 +01:00
|
|
|
case 3: /* AC_VO */
|
2008-04-30 18:51:21 +02:00
|
|
|
queue = 0;
|
2008-04-17 19:21:22 +02:00
|
|
|
if (acm)
|
2012-06-20 14:39:13 +02:00
|
|
|
sdata->wmm_acm |= BIT(6) | BIT(7); /* VO/NC */
|
2010-01-12 09:42:31 +01:00
|
|
|
if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
|
|
|
|
uapsd = true;
|
2007-05-05 20:45:53 +02:00
|
|
|
break;
|
2009-03-05 16:23:46 +01:00
|
|
|
case 0: /* AC_BE */
|
2007-05-05 20:45:53 +02:00
|
|
|
default:
|
2008-04-30 18:51:21 +02:00
|
|
|
queue = 2;
|
2008-04-17 19:21:22 +02:00
|
|
|
if (acm)
|
2012-06-20 14:39:13 +02:00
|
|
|
sdata->wmm_acm |= BIT(0) | BIT(3); /* BE/EE */
|
2010-01-12 09:42:31 +01:00
|
|
|
if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE)
|
|
|
|
uapsd = true;
|
2007-05-05 20:45:53 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
params.aifs = pos[0] & 0x0f;
|
|
|
|
params.cw_max = ecw2cw((pos[1] & 0xf0) >> 4);
|
|
|
|
params.cw_min = ecw2cw(pos[1] & 0x0f);
|
2008-07-10 11:22:31 +02:00
|
|
|
params.txop = get_unaligned_le16(pos + 2);
|
2013-04-07 08:53:30 +02:00
|
|
|
params.acm = acm;
|
2010-01-12 09:42:31 +01:00
|
|
|
params.uapsd = uapsd;
|
|
|
|
|
2012-06-22 11:29:50 +02:00
|
|
|
mlme_dbg(sdata,
|
2014-10-07 09:38:50 +02:00
|
|
|
"WMM queue=%d aci=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d, downgraded=%d\n",
|
2012-06-22 11:29:50 +02:00
|
|
|
queue, aci, acm,
|
|
|
|
params.aifs, params.cw_min, params.cw_max,
|
2014-10-07 09:38:50 +02:00
|
|
|
params.txop, params.uapsd,
|
|
|
|
ifmgd->tx_tspec[queue].downgraded);
|
2011-09-25 19:06:54 +02:00
|
|
|
sdata->tx_conf[queue] = params;
|
2014-10-07 09:38:50 +02:00
|
|
|
if (!ifmgd->tx_tspec[queue].downgraded &&
|
|
|
|
drv_conf_tx(local, sdata, queue, ¶ms))
|
2012-06-22 11:29:50 +02:00
|
|
|
sdata_err(sdata,
|
|
|
|
"failed to set TX queue parameters for queue %d\n",
|
|
|
|
queue);
|
2007-05-05 20:45:53 +02:00
|
|
|
}
|
2010-03-29 12:18:34 +02:00
|
|
|
|
|
|
|
/* enable WMM or activate new settings */
|
2010-07-19 16:39:04 +02:00
|
|
|
sdata->vif.bss_conf.qos = true;
|
2012-07-06 17:37:43 +02:00
|
|
|
return true;
|
2007-05-05 20:45:53 +02:00
|
|
|
}
|
|
|
|
|
2012-05-16 15:27:20 +02:00
|
|
|
static void __ieee80211_stop_poll(struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
|
|
|
lockdep_assert_held(&sdata->local->mtx);
|
|
|
|
|
2013-08-27 11:36:35 +02:00
|
|
|
sdata->u.mgd.flags &= ~IEEE80211_STA_CONNECTION_POLL;
|
2012-05-16 15:27:20 +02:00
|
|
|
ieee80211_run_deferred_scan(sdata->local);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ieee80211_stop_poll(struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
|
|
|
mutex_lock(&sdata->local->mtx);
|
|
|
|
__ieee80211_stop_poll(sdata);
|
|
|
|
mutex_unlock(&sdata->local->mtx);
|
|
|
|
}
|
|
|
|
|
2008-10-08 10:59:33 +02:00
|
|
|
static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata,
|
|
|
|
u16 capab, bool erp_valid, u8 erp)
|
2007-07-10 19:32:10 +02:00
|
|
|
{
|
2008-10-11 01:51:51 +02:00
|
|
|
struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
|
2007-12-28 14:32:58 +01:00
|
|
|
u32 changed = 0;
|
2008-10-08 10:59:33 +02:00
|
|
|
bool use_protection;
|
|
|
|
bool use_short_preamble;
|
|
|
|
bool use_short_slot;
|
|
|
|
|
|
|
|
if (erp_valid) {
|
|
|
|
use_protection = (erp & WLAN_ERP_USE_PROTECTION) != 0;
|
|
|
|
use_short_preamble = (erp & WLAN_ERP_BARKER_PREAMBLE) == 0;
|
|
|
|
} else {
|
|
|
|
use_protection = false;
|
|
|
|
use_short_preamble = !!(capab & WLAN_CAPABILITY_SHORT_PREAMBLE);
|
|
|
|
}
|
|
|
|
|
|
|
|
use_short_slot = !!(capab & WLAN_CAPABILITY_SHORT_SLOT_TIME);
|
2012-07-26 17:24:39 +02:00
|
|
|
if (ieee80211_get_sdata_band(sdata) == IEEE80211_BAND_5GHZ)
|
2010-01-15 03:00:48 +01:00
|
|
|
use_short_slot = true;
|
2007-07-10 19:32:10 +02:00
|
|
|
|
2007-12-28 14:32:58 +01:00
|
|
|
if (use_protection != bss_conf->use_cts_prot) {
|
|
|
|
bss_conf->use_cts_prot = use_protection;
|
|
|
|
changed |= BSS_CHANGED_ERP_CTS_PROT;
|
2007-07-10 19:32:10 +02:00
|
|
|
}
|
2007-07-27 15:43:24 +02:00
|
|
|
|
2008-03-31 17:05:03 +02:00
|
|
|
if (use_short_preamble != bss_conf->use_short_preamble) {
|
|
|
|
bss_conf->use_short_preamble = use_short_preamble;
|
2007-12-28 14:32:58 +01:00
|
|
|
changed |= BSS_CHANGED_ERP_PREAMBLE;
|
2007-07-27 15:43:24 +02:00
|
|
|
}
|
2007-07-27 15:43:24 +02:00
|
|
|
|
2008-10-08 10:59:33 +02:00
|
|
|
if (use_short_slot != bss_conf->use_short_slot) {
|
|
|
|
bss_conf->use_short_slot = use_short_slot;
|
|
|
|
changed |= BSS_CHANGED_ERP_SLOT;
|
2008-04-15 20:09:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return changed;
|
|
|
|
}
|
|
|
|
|
2008-08-03 02:04:37 +02:00
|
|
|
static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
|
2009-12-23 13:15:39 +01:00
|
|
|
struct cfg80211_bss *cbss,
|
2008-10-14 16:58:37 +02:00
|
|
|
u32 bss_info_changed)
|
2007-05-05 20:45:53 +02:00
|
|
|
{
|
2009-12-23 13:15:39 +01:00
|
|
|
struct ieee80211_bss *bss = (void *)cbss->priv;
|
2007-12-28 14:32:58 +01:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
2010-06-09 12:43:26 +02:00
|
|
|
struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
|
2007-08-28 23:01:54 +02:00
|
|
|
|
2008-10-14 16:58:37 +02:00
|
|
|
bss_info_changed |= BSS_CHANGED_ASSOC;
|
2009-07-07 03:45:17 +02:00
|
|
|
bss_info_changed |= ieee80211_handle_bss_capability(sdata,
|
2012-06-20 16:23:24 +02:00
|
|
|
bss_conf->assoc_capability, bss->has_erp_value, bss->erp_value);
|
2008-03-29 00:33:34 +01:00
|
|
|
|
2010-11-19 22:55:38 +01:00
|
|
|
sdata->u.mgd.beacon_timeout = usecs_to_jiffies(ieee80211_tu_to_usec(
|
2013-03-19 22:19:56 +01:00
|
|
|
beacon_loss_count * bss_conf->beacon_int));
|
2010-11-19 22:55:38 +01:00
|
|
|
|
2009-12-23 13:15:39 +01:00
|
|
|
sdata->u.mgd.associated = cbss;
|
|
|
|
memcpy(sdata->u.mgd.bssid, cbss->bssid, ETH_ALEN);
|
2007-05-05 20:45:53 +02:00
|
|
|
|
2010-03-30 08:28:30 +02:00
|
|
|
sdata->u.mgd.flags |= IEEE80211_STA_RESET_SIGNAL_AVE;
|
|
|
|
|
2012-10-29 20:08:01 +01:00
|
|
|
if (sdata->vif.p2p) {
|
2012-11-29 01:25:20 +01:00
|
|
|
const struct cfg80211_bss_ies *ies;
|
2012-10-29 20:08:01 +01:00
|
|
|
|
2012-11-29 01:25:20 +01:00
|
|
|
rcu_read_lock();
|
|
|
|
ies = rcu_dereference(cbss->ies);
|
|
|
|
if (ies) {
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = cfg80211_get_p2p_attr(
|
|
|
|
ies->data, ies->len,
|
|
|
|
IEEE80211_P2P_ATTR_ABSENCE_NOTICE,
|
2013-03-21 15:47:56 +01:00
|
|
|
(u8 *) &bss_conf->p2p_noa_attr,
|
|
|
|
sizeof(bss_conf->p2p_noa_attr));
|
2012-11-29 01:25:20 +01:00
|
|
|
if (ret >= 2) {
|
2013-03-21 15:47:56 +01:00
|
|
|
sdata->u.mgd.p2p_noa_index =
|
|
|
|
bss_conf->p2p_noa_attr.index;
|
2012-11-29 01:25:20 +01:00
|
|
|
bss_info_changed |= BSS_CHANGED_P2P_PS;
|
|
|
|
}
|
2012-10-29 20:08:01 +01:00
|
|
|
}
|
2012-11-29 01:25:20 +01:00
|
|
|
rcu_read_unlock();
|
2012-10-29 20:08:01 +01:00
|
|
|
}
|
|
|
|
|
2009-07-10 15:29:03 +02:00
|
|
|
/* just to be sure */
|
2012-05-16 15:27:20 +02:00
|
|
|
ieee80211_stop_poll(sdata);
|
2009-07-10 15:29:03 +02:00
|
|
|
|
2008-09-09 10:57:09 +02:00
|
|
|
ieee80211_led_assoc(local, 1);
|
2008-05-29 10:35:23 +02:00
|
|
|
|
2013-05-16 16:34:17 +02:00
|
|
|
if (sdata->u.mgd.have_beacon) {
|
2012-12-10 15:38:14 +01:00
|
|
|
/*
|
|
|
|
* If the AP is buggy we may get here with no DTIM period
|
|
|
|
* known, so assume it's 1 which is the only safe assumption
|
|
|
|
* in that case, although if the TIM IE is broken powersave
|
|
|
|
* probably just won't work at all.
|
|
|
|
*/
|
|
|
|
bss_conf->dtim_period = sdata->u.mgd.dtim_period ?: 1;
|
2013-05-19 13:23:57 +02:00
|
|
|
bss_conf->beacon_rate = bss->beacon_rate;
|
2013-05-16 16:34:17 +02:00
|
|
|
bss_info_changed |= BSS_CHANGED_BEACON_INFO;
|
2012-12-10 15:38:14 +01:00
|
|
|
} else {
|
2013-05-19 13:23:57 +02:00
|
|
|
bss_conf->beacon_rate = NULL;
|
2010-07-29 16:08:55 +02:00
|
|
|
bss_conf->dtim_period = 0;
|
2012-12-10 15:38:14 +01:00
|
|
|
}
|
2010-07-29 16:08:55 +02:00
|
|
|
|
2010-06-09 12:43:26 +02:00
|
|
|
bss_conf->assoc = 1;
|
2009-05-14 13:10:14 +02:00
|
|
|
|
2010-03-23 08:02:34 +01:00
|
|
|
/* Tell the driver to monitor connection quality (if supported) */
|
2012-01-19 09:29:58 +01:00
|
|
|
if (sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI &&
|
2010-06-09 12:43:26 +02:00
|
|
|
bss_conf->cqm_rssi_thold)
|
2010-03-23 08:02:34 +01:00
|
|
|
bss_info_changed |= BSS_CHANGED_CQM;
|
|
|
|
|
2010-06-09 12:43:26 +02:00
|
|
|
/* Enable ARP filtering */
|
2013-01-14 16:39:07 +01:00
|
|
|
if (bss_conf->arp_addr_cnt)
|
2010-06-09 12:43:26 +02:00
|
|
|
bss_info_changed |= BSS_CHANGED_ARP_FILTER;
|
|
|
|
|
2008-10-14 16:58:37 +02:00
|
|
|
ieee80211_bss_info_change_notify(sdata, bss_info_changed);
|
2007-05-05 20:45:53 +02:00
|
|
|
|
2009-07-30 21:43:55 +02:00
|
|
|
mutex_lock(&local->iflist_mtx);
|
|
|
|
ieee80211_recalc_ps(local, -1);
|
|
|
|
mutex_unlock(&local->iflist_mtx);
|
2008-12-18 22:35:13 +01:00
|
|
|
|
2012-09-11 14:34:12 +02:00
|
|
|
ieee80211_recalc_smps(sdata);
|
2012-07-27 11:33:22 +02:00
|
|
|
ieee80211_recalc_ps_vif(sdata);
|
|
|
|
|
2008-09-09 10:57:09 +02:00
|
|
|
netif_carrier_on(sdata->dev);
|
2007-05-05 20:45:53 +02:00
|
|
|
}
|
|
|
|
|
2010-03-30 08:29:31 +02:00
|
|
|
static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
|
2012-02-24 13:50:54 +01:00
|
|
|
u16 stype, u16 reason, bool tx,
|
|
|
|
u8 *frame_buf)
|
2008-09-08 23:32:12 +02:00
|
|
|
{
|
2009-02-15 12:44:28 +01:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2008-09-08 23:32:12 +02:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
2012-03-09 13:12:35 +01:00
|
|
|
u32 changed = 0;
|
2008-09-08 23:32:12 +02:00
|
|
|
|
2013-05-10 12:32:47 +02:00
|
|
|
sdata_assert_lock(sdata);
|
2009-07-07 03:45:17 +02:00
|
|
|
|
2012-02-24 13:50:54 +01:00
|
|
|
if (WARN_ON_ONCE(tx && !frame_buf))
|
|
|
|
return;
|
|
|
|
|
2009-07-10 15:29:03 +02:00
|
|
|
if (WARN_ON(!ifmgd->associated))
|
|
|
|
return;
|
|
|
|
|
2012-06-12 08:59:45 +02:00
|
|
|
ieee80211_stop_poll(sdata);
|
|
|
|
|
2009-07-07 03:45:17 +02:00
|
|
|
ifmgd->associated = NULL;
|
2008-09-08 23:32:12 +02:00
|
|
|
netif_carrier_off(sdata->dev);
|
|
|
|
|
2012-07-12 16:35:33 +02:00
|
|
|
/*
|
|
|
|
* if we want to get out of ps before disassoc (why?) we have
|
|
|
|
* to do it before sending disassoc, as otherwise the null-packet
|
|
|
|
* won't be valid.
|
|
|
|
*/
|
|
|
|
if (local->hw.conf.flags & IEEE80211_CONF_PS) {
|
|
|
|
local->hw.conf.flags &= ~IEEE80211_CONF_PS;
|
|
|
|
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
|
|
|
|
}
|
|
|
|
local->ps_sdata = NULL;
|
|
|
|
|
2012-07-27 11:33:22 +02:00
|
|
|
/* disable per-vif ps */
|
|
|
|
ieee80211_recalc_ps_vif(sdata);
|
|
|
|
|
2015-01-22 22:30:19 +01:00
|
|
|
/* make sure ongoing transmission finishes */
|
|
|
|
synchronize_net();
|
|
|
|
|
2015-01-07 14:42:39 +01:00
|
|
|
/*
|
|
|
|
* drop any frame before deauth/disassoc, this can be data or
|
|
|
|
* management frame. Since we are disconnecting, we should not
|
|
|
|
* insist sending these frames which can take time and delay
|
|
|
|
* the disconnection and possible the roaming.
|
|
|
|
*/
|
2012-06-27 13:18:22 +02:00
|
|
|
if (tx)
|
2015-01-07 14:42:39 +01:00
|
|
|
ieee80211_flush_queues(local, sdata, true);
|
2012-06-27 13:18:22 +02:00
|
|
|
|
2012-02-24 13:50:54 +01:00
|
|
|
/* deauthenticate/disassociate now */
|
|
|
|
if (tx || frame_buf)
|
2012-06-01 10:14:03 +02:00
|
|
|
ieee80211_send_deauth_disassoc(sdata, ifmgd->bssid, stype,
|
|
|
|
reason, tx, frame_buf);
|
2012-02-24 13:50:54 +01:00
|
|
|
|
2015-01-07 14:42:39 +01:00
|
|
|
/* flush out frame - make sure the deauth was actually sent */
|
2012-02-24 13:50:54 +01:00
|
|
|
if (tx)
|
2015-01-07 14:42:39 +01:00
|
|
|
ieee80211_flush_queues(local, sdata, false);
|
2012-02-24 13:50:54 +01:00
|
|
|
|
2012-06-01 10:14:03 +02:00
|
|
|
/* clear bssid only after building the needed mgmt frames */
|
2015-03-03 04:54:57 +01:00
|
|
|
eth_zero_addr(ifmgd->bssid);
|
2012-06-01 10:14:03 +02:00
|
|
|
|
2012-02-24 13:50:54 +01:00
|
|
|
/* remove AP and TDLS peers */
|
2013-12-04 22:46:11 +01:00
|
|
|
sta_info_flush(sdata);
|
2012-02-24 13:50:54 +01:00
|
|
|
|
|
|
|
/* finally reset all BSS / config parameters */
|
2008-09-08 17:33:39 +02:00
|
|
|
changed |= ieee80211_reset_erp_info(sdata);
|
|
|
|
|
|
|
|
ieee80211_led_assoc(local, 0);
|
2008-10-14 16:58:37 +02:00
|
|
|
changed |= BSS_CHANGED_ASSOC;
|
|
|
|
sdata->vif.bss_conf.assoc = false;
|
2008-09-08 17:33:39 +02:00
|
|
|
|
2013-03-21 15:47:56 +01:00
|
|
|
ifmgd->p2p_noa_index = -1;
|
|
|
|
memset(&sdata->vif.bss_conf.p2p_noa_attr, 0,
|
|
|
|
sizeof(sdata->vif.bss_conf.p2p_noa_attr));
|
2012-10-29 20:08:01 +01:00
|
|
|
|
2013-02-21 17:40:19 +01:00
|
|
|
/* on the next assoc, re-program HT/VHT parameters */
|
2011-11-18 20:32:00 +01:00
|
|
|
memset(&ifmgd->ht_capa, 0, sizeof(ifmgd->ht_capa));
|
|
|
|
memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask));
|
2013-02-21 17:40:19 +01:00
|
|
|
memset(&ifmgd->vht_capa, 0, sizeof(ifmgd->vht_capa));
|
|
|
|
memset(&ifmgd->vht_capa_mask, 0, sizeof(ifmgd->vht_capa_mask));
|
2015-07-08 14:41:43 +02:00
|
|
|
sdata->flags &= ~IEEE80211_SDATA_MU_MIMO_OWNER;
|
2009-05-08 21:21:06 +02:00
|
|
|
|
2012-10-24 10:59:25 +02:00
|
|
|
sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL;
|
2009-01-09 13:44:15 +01:00
|
|
|
|
2008-12-18 22:35:27 +01:00
|
|
|
del_timer_sync(&local->dynamic_ps_timer);
|
|
|
|
cancel_work_sync(&local->dynamic_ps_enable_work);
|
|
|
|
|
2010-06-09 12:43:26 +02:00
|
|
|
/* Disable ARP filtering */
|
2013-01-14 16:39:07 +01:00
|
|
|
if (sdata->vif.bss_conf.arp_addr_cnt)
|
2010-06-09 12:43:26 +02:00
|
|
|
changed |= BSS_CHANGED_ARP_FILTER;
|
|
|
|
|
2012-03-02 15:56:59 +01:00
|
|
|
sdata->vif.bss_conf.qos = false;
|
|
|
|
changed |= BSS_CHANGED_QOS;
|
|
|
|
|
2010-05-05 15:28:27 +02:00
|
|
|
/* The BSSID (not really interesting) and HT changed */
|
|
|
|
changed |= BSS_CHANGED_BSSID | BSS_CHANGED_HT;
|
2008-10-14 16:58:37 +02:00
|
|
|
ieee80211_bss_info_change_notify(sdata, changed);
|
2008-11-25 12:05:44 +01:00
|
|
|
|
2012-03-02 15:56:59 +01:00
|
|
|
/* disassociated - set to defaults now */
|
|
|
|
ieee80211_set_wmm_default(sdata, false);
|
|
|
|
|
2010-08-27 12:35:54 +02:00
|
|
|
del_timer_sync(&sdata->u.mgd.conn_mon_timer);
|
|
|
|
del_timer_sync(&sdata->u.mgd.bcn_mon_timer);
|
|
|
|
del_timer_sync(&sdata->u.mgd.timer);
|
|
|
|
del_timer_sync(&sdata->u.mgd.chswitch_timer);
|
2012-08-01 20:54:52 +02:00
|
|
|
|
2012-12-10 15:38:14 +01:00
|
|
|
sdata->vif.bss_conf.dtim_period = 0;
|
2013-05-19 13:23:57 +02:00
|
|
|
sdata->vif.bss_conf.beacon_rate = NULL;
|
|
|
|
|
2013-05-16 16:34:17 +02:00
|
|
|
ifmgd->have_beacon = false;
|
2012-12-10 15:38:14 +01:00
|
|
|
|
2012-11-26 11:57:41 +01:00
|
|
|
ifmgd->flags = 0;
|
mac80211: fix iflist_mtx/mtx locking in radar detection
The scan code creates an iflist_mtx -> mtx locking dependency,
and a few other places, notably radar detection, were creating
the opposite dependency, causing lockdep to complain. As scan
and radar detection are mutually exclusive, the deadlock can't
really happen in practice, but it's still bad form.
A similar issue exists in the monitor mode code, but this is
only used by channel-context drivers right now and those have
to have hardware scan, so that also can't happen.
Still, fix these issues by making some of the channel context
code require the mtx to be held rather than acquiring it, thus
allowing the monitor/radar callers to keep the iflist_mtx->mtx
lock ordering.
While at it, also fix access to the local->scanning variable
in the radar code, and document that radar_detect_enabled is
now properly protected by the mtx.
All this would now introduce an ABBA deadlock between the DFS
work cancelling and local->mtx, so change the locking there a
bit to not need to use cancel_delayed_work_sync() but be able
to just use cancel_delayed_work(). The work is also safely
stopped/removed when the interface is stopped, so no extra
changes are needed.
Reported-by: Kalle Valo <kvalo@qca.qualcomm.com>
Tested-by: Simon Wunderlich <sw@simonwunderlich.de>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2013-12-18 09:43:33 +01:00
|
|
|
mutex_lock(&local->mtx);
|
2012-11-26 11:57:41 +01:00
|
|
|
ieee80211_vif_release_channel(sdata);
|
2014-04-09 15:10:59 +02:00
|
|
|
|
|
|
|
sdata->vif.csa_active = false;
|
2014-10-08 08:48:39 +02:00
|
|
|
ifmgd->csa_waiting_bcn = false;
|
2015-03-12 07:53:26 +01:00
|
|
|
ifmgd->csa_ignored_same_chan = false;
|
2014-06-13 15:30:07 +02:00
|
|
|
if (sdata->csa_block_tx) {
|
|
|
|
ieee80211_wake_vif_queues(local, sdata,
|
|
|
|
IEEE80211_QUEUE_STOP_REASON_CSA);
|
|
|
|
sdata->csa_block_tx = false;
|
|
|
|
}
|
mac80211: fix iflist_mtx/mtx locking in radar detection
The scan code creates an iflist_mtx -> mtx locking dependency,
and a few other places, notably radar detection, were creating
the opposite dependency, causing lockdep to complain. As scan
and radar detection are mutually exclusive, the deadlock can't
really happen in practice, but it's still bad form.
A similar issue exists in the monitor mode code, but this is
only used by channel-context drivers right now and those have
to have hardware scan, so that also can't happen.
Still, fix these issues by making some of the channel context
code require the mtx to be held rather than acquiring it, thus
allowing the monitor/radar callers to keep the iflist_mtx->mtx
lock ordering.
While at it, also fix access to the local->scanning variable
in the radar code, and document that radar_detect_enabled is
now properly protected by the mtx.
All this would now introduce an ABBA deadlock between the DFS
work cancelling and local->mtx, so change the locking there a
bit to not need to use cancel_delayed_work_sync() but be able
to just use cancel_delayed_work(). The work is also safely
stopped/removed when the interface is stopped, so no extra
changes are needed.
Reported-by: Kalle Valo <kvalo@qca.qualcomm.com>
Tested-by: Simon Wunderlich <sw@simonwunderlich.de>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2013-12-18 09:43:33 +01:00
|
|
|
mutex_unlock(&local->mtx);
|
2013-03-24 13:23:27 +01:00
|
|
|
|
2014-10-07 09:38:50 +02:00
|
|
|
/* existing TX TSPEC sessions no longer exist */
|
|
|
|
memset(ifmgd->tx_tspec, 0, sizeof(ifmgd->tx_tspec));
|
|
|
|
cancel_delayed_work_sync(&ifmgd->tx_tspec_wk);
|
|
|
|
|
2013-03-24 13:23:27 +01:00
|
|
|
sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
|
2008-09-08 23:32:12 +02:00
|
|
|
}
|
2007-05-05 20:45:53 +02:00
|
|
|
|
2009-03-22 20:57:06 +01:00
|
|
|
void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct ieee80211_hdr *hdr)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* We can postpone the mgd.timer whenever receiving unicast frames
|
|
|
|
* from AP because we know that the connection is working both ways
|
|
|
|
* at that time. But multicast frames (and hence also beacons) must
|
|
|
|
* be ignored here, because we need to trigger the timer during
|
2009-07-10 15:29:03 +02:00
|
|
|
* data idle periods for sending the periodic probe request to the
|
|
|
|
* AP we're connected to.
|
2009-03-22 20:57:06 +01:00
|
|
|
*/
|
2009-07-10 15:29:03 +02:00
|
|
|
if (is_multicast_ether_addr(hdr->addr1))
|
|
|
|
return;
|
|
|
|
|
2010-09-16 21:12:29 +02:00
|
|
|
ieee80211_sta_reset_conn_monitor(sdata);
|
2009-03-22 20:57:06 +01:00
|
|
|
}
|
2007-05-05 20:45:53 +02:00
|
|
|
|
2010-11-23 03:10:31 +01:00
|
|
|
static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2012-03-28 16:01:19 +02:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
2010-11-23 03:10:31 +01:00
|
|
|
|
2012-03-28 16:01:19 +02:00
|
|
|
mutex_lock(&local->mtx);
|
2013-08-27 11:36:35 +02:00
|
|
|
if (!(ifmgd->flags & IEEE80211_STA_CONNECTION_POLL))
|
|
|
|
goto out;
|
2010-11-23 03:10:31 +01:00
|
|
|
|
2012-05-16 15:27:20 +02:00
|
|
|
__ieee80211_stop_poll(sdata);
|
2012-03-28 16:01:19 +02:00
|
|
|
|
|
|
|
mutex_lock(&local->iflist_mtx);
|
|
|
|
ieee80211_recalc_ps(local, -1);
|
|
|
|
mutex_unlock(&local->iflist_mtx);
|
2010-11-23 03:10:31 +01:00
|
|
|
|
2015-06-02 21:39:54 +02:00
|
|
|
if (ieee80211_hw_check(&sdata->local->hw, CONNECTION_MONITOR))
|
2012-03-28 16:01:19 +02:00
|
|
|
goto out;
|
2010-11-23 03:10:31 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We've received a probe response, but are not sure whether
|
|
|
|
* we have or will be receiving any beacons or data, so let's
|
|
|
|
* schedule the timers again, just in case.
|
|
|
|
*/
|
|
|
|
ieee80211_sta_reset_beacon_monitor(sdata);
|
|
|
|
|
|
|
|
mod_timer(&ifmgd->conn_mon_timer,
|
|
|
|
round_jiffies_up(jiffies +
|
|
|
|
IEEE80211_CONNECTION_IDLE_TIME));
|
2012-03-28 16:01:19 +02:00
|
|
|
out:
|
|
|
|
mutex_unlock(&local->mtx);
|
2010-11-23 03:10:31 +01:00
|
|
|
}
|
|
|
|
|
2014-10-07 09:38:50 +02:00
|
|
|
static void ieee80211_sta_tx_wmm_ac_notify(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct ieee80211_hdr *hdr,
|
|
|
|
u16 tx_time)
|
|
|
|
{
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
u16 tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
|
|
|
|
int ac = ieee80211_ac_from_tid(tid);
|
|
|
|
struct ieee80211_sta_tx_tspec *tx_tspec = &ifmgd->tx_tspec[ac];
|
|
|
|
unsigned long now = jiffies;
|
|
|
|
|
|
|
|
if (likely(!tx_tspec->admitted_time))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (time_after(now, tx_tspec->time_slice_start + HZ)) {
|
|
|
|
tx_tspec->consumed_tx_time = 0;
|
|
|
|
tx_tspec->time_slice_start = now;
|
|
|
|
|
|
|
|
if (tx_tspec->downgraded) {
|
|
|
|
tx_tspec->action = TX_TSPEC_ACTION_STOP_DOWNGRADE;
|
|
|
|
schedule_delayed_work(&ifmgd->tx_tspec_wk, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tx_tspec->downgraded)
|
|
|
|
return;
|
|
|
|
|
|
|
|
tx_tspec->consumed_tx_time += tx_time;
|
|
|
|
|
|
|
|
if (tx_tspec->consumed_tx_time >= tx_tspec->admitted_time) {
|
|
|
|
tx_tspec->downgraded = true;
|
|
|
|
tx_tspec->action = TX_TSPEC_ACTION_DOWNGRADE;
|
|
|
|
schedule_delayed_work(&ifmgd->tx_tspec_wk, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-11-23 03:10:31 +01:00
|
|
|
void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata,
|
2014-10-07 09:38:50 +02:00
|
|
|
struct ieee80211_hdr *hdr, bool ack, u16 tx_time)
|
2010-11-23 03:10:31 +01:00
|
|
|
{
|
2014-10-07 09:38:50 +02:00
|
|
|
ieee80211_sta_tx_wmm_ac_notify(sdata, hdr, tx_time);
|
|
|
|
|
2010-12-02 21:01:07 +01:00
|
|
|
if (!ieee80211_is_data(hdr->frame_control))
|
2010-11-23 03:10:31 +01:00
|
|
|
return;
|
|
|
|
|
|
|
|
if (ieee80211_is_nullfunc(hdr->frame_control) &&
|
|
|
|
sdata->u.mgd.probe_send_count > 0) {
|
2010-12-02 21:01:08 +01:00
|
|
|
if (ack)
|
2013-02-14 14:08:37 +01:00
|
|
|
ieee80211_sta_reset_conn_monitor(sdata);
|
2010-12-02 21:01:08 +01:00
|
|
|
else
|
|
|
|
sdata->u.mgd.nullfunc_failed = true;
|
2010-11-23 03:10:31 +01:00
|
|
|
ieee80211_queue_work(&sdata->local->hw, &sdata->work);
|
2013-02-14 14:08:37 +01:00
|
|
|
return;
|
2010-11-23 03:10:31 +01:00
|
|
|
}
|
2013-02-14 14:08:37 +01:00
|
|
|
|
|
|
|
if (ack)
|
|
|
|
ieee80211_sta_reset_conn_monitor(sdata);
|
2010-11-23 03:10:31 +01:00
|
|
|
}
|
|
|
|
|
2009-07-31 17:54:12 +02:00
|
|
|
static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
const u8 *ssid;
|
2010-09-16 21:12:34 +02:00
|
|
|
u8 *dst = ifmgd->associated->bssid;
|
2011-02-05 00:30:24 +01:00
|
|
|
u8 unicast_limit = max(1, max_probe_tries - 3);
|
2010-09-16 21:12:34 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Try sending broadcast probe requests for the last three
|
|
|
|
* probe requests after the first ones failed since some
|
|
|
|
* buggy APs only support broadcast probe requests.
|
|
|
|
*/
|
|
|
|
if (ifmgd->probe_send_count >= unicast_limit)
|
|
|
|
dst = NULL;
|
2009-07-31 17:54:12 +02:00
|
|
|
|
2010-11-23 03:10:31 +01:00
|
|
|
/*
|
|
|
|
* When the hardware reports an accurate Tx ACK status, it's
|
|
|
|
* better to send a nullfunc frame instead of a probe request,
|
|
|
|
* as it will kick us off the AP quickly if we aren't associated
|
|
|
|
* anymore. The timeout will be reset if the frame is ACKed by
|
|
|
|
* the AP.
|
|
|
|
*/
|
2012-05-20 12:01:13 +02:00
|
|
|
ifmgd->probe_send_count++;
|
|
|
|
|
2015-06-02 21:39:54 +02:00
|
|
|
if (ieee80211_hw_check(&sdata->local->hw, REPORTS_TX_ACK_STATUS)) {
|
2010-12-02 21:01:08 +01:00
|
|
|
ifmgd->nullfunc_failed = false;
|
2010-11-23 03:10:31 +01:00
|
|
|
ieee80211_send_nullfunc(sdata->local, sdata, 0);
|
2010-12-02 21:01:08 +01:00
|
|
|
} else {
|
2012-03-29 16:30:41 +02:00
|
|
|
int ssid_len;
|
|
|
|
|
2012-11-29 01:25:20 +01:00
|
|
|
rcu_read_lock();
|
2010-11-23 03:10:31 +01:00
|
|
|
ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID);
|
2012-03-29 16:30:41 +02:00
|
|
|
if (WARN_ON_ONCE(ssid == NULL))
|
|
|
|
ssid_len = 0;
|
|
|
|
else
|
|
|
|
ssid_len = ssid[1];
|
|
|
|
|
2015-03-21 07:41:04 +01:00
|
|
|
ieee80211_send_probe_req(sdata, sdata->vif.addr, dst,
|
2014-06-12 22:24:31 +02:00
|
|
|
ssid + 2, ssid_len, NULL,
|
2013-01-29 15:02:27 +01:00
|
|
|
0, (u32) -1, true, 0,
|
2012-07-26 17:24:39 +02:00
|
|
|
ifmgd->associated->channel, false);
|
2012-11-29 01:25:20 +01:00
|
|
|
rcu_read_unlock();
|
2010-11-23 03:10:31 +01:00
|
|
|
}
|
2009-07-31 17:54:12 +02:00
|
|
|
|
2011-02-05 00:30:24 +01:00
|
|
|
ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms);
|
2013-05-10 12:32:47 +02:00
|
|
|
run_again(sdata, ifmgd->probe_timeout);
|
2009-07-31 17:54:12 +02:00
|
|
|
}
|
|
|
|
|
2009-07-10 15:29:03 +02:00
|
|
|
static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
|
|
|
|
bool beacon)
|
2009-03-22 20:57:35 +01:00
|
|
|
{
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2009-07-10 15:29:03 +02:00
|
|
|
bool already = false;
|
2009-05-12 19:58:12 +02:00
|
|
|
|
2009-12-23 13:15:31 +01:00
|
|
|
if (!ieee80211_sdata_running(sdata))
|
2009-07-13 13:23:39 +02:00
|
|
|
return;
|
|
|
|
|
2013-05-10 12:32:47 +02:00
|
|
|
sdata_lock(sdata);
|
2009-07-07 03:45:17 +02:00
|
|
|
|
|
|
|
if (!ifmgd->associated)
|
|
|
|
goto out;
|
|
|
|
|
2012-03-28 16:01:19 +02:00
|
|
|
mutex_lock(&sdata->local->mtx);
|
|
|
|
|
|
|
|
if (sdata->local->tmp_channel || sdata->local->scanning) {
|
|
|
|
mutex_unlock(&sdata->local->mtx);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2013-03-25 19:19:35 +01:00
|
|
|
if (beacon) {
|
2012-06-22 11:29:50 +02:00
|
|
|
mlme_dbg_ratelimited(sdata,
|
2013-03-19 22:19:56 +01:00
|
|
|
"detected beacon loss from AP (missed %d beacons) - probing\n",
|
|
|
|
beacon_loss_count);
|
2012-06-22 11:29:50 +02:00
|
|
|
|
2014-11-26 12:42:02 +01:00
|
|
|
ieee80211_cqm_beacon_loss_notify(&sdata->vif, GFP_KERNEL);
|
2013-03-25 19:19:35 +01:00
|
|
|
}
|
2009-03-22 20:57:35 +01:00
|
|
|
|
2009-07-10 15:29:03 +02:00
|
|
|
/*
|
|
|
|
* The driver/our work has already reported this event or the
|
|
|
|
* connection monitoring has kicked in and we have already sent
|
|
|
|
* a probe request. Or maybe the AP died and the driver keeps
|
|
|
|
* reporting until we disassociate...
|
|
|
|
*
|
|
|
|
* In either case we have to ignore the current call to this
|
|
|
|
* function (except for setting the correct probe reason bit)
|
|
|
|
* because otherwise we would reset the timer every time and
|
|
|
|
* never check whether we received a probe response!
|
|
|
|
*/
|
2013-08-27 11:36:35 +02:00
|
|
|
if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL)
|
2009-07-10 15:29:03 +02:00
|
|
|
already = true;
|
|
|
|
|
2013-11-18 18:06:46 +01:00
|
|
|
ifmgd->flags |= IEEE80211_STA_CONNECTION_POLL;
|
|
|
|
|
2012-03-28 16:01:19 +02:00
|
|
|
mutex_unlock(&sdata->local->mtx);
|
|
|
|
|
2009-07-10 15:29:03 +02:00
|
|
|
if (already)
|
|
|
|
goto out;
|
|
|
|
|
2009-06-10 15:16:52 +02:00
|
|
|
mutex_lock(&sdata->local->iflist_mtx);
|
|
|
|
ieee80211_recalc_ps(sdata->local, -1);
|
|
|
|
mutex_unlock(&sdata->local->iflist_mtx);
|
|
|
|
|
2009-07-31 17:54:12 +02:00
|
|
|
ifmgd->probe_send_count = 0;
|
|
|
|
ieee80211_mgd_probe_ap_send(sdata);
|
2009-07-07 03:45:17 +02:00
|
|
|
out:
|
2013-05-10 12:32:47 +02:00
|
|
|
sdata_unlock(sdata);
|
2009-03-22 20:57:35 +01:00
|
|
|
}
|
|
|
|
|
2010-11-11 07:50:18 +01:00
|
|
|
struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
|
|
|
|
struct ieee80211_vif *vif)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2012-06-28 14:03:13 +02:00
|
|
|
struct cfg80211_bss *cbss;
|
2010-11-11 07:50:18 +01:00
|
|
|
struct sk_buff *skb;
|
|
|
|
const u8 *ssid;
|
2012-03-29 16:30:41 +02:00
|
|
|
int ssid_len;
|
2010-11-11 07:50:18 +01:00
|
|
|
|
|
|
|
if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
|
|
|
|
return NULL;
|
|
|
|
|
2013-05-10 12:32:47 +02:00
|
|
|
sdata_assert_lock(sdata);
|
2010-11-11 07:50:18 +01:00
|
|
|
|
2012-06-28 14:03:13 +02:00
|
|
|
if (ifmgd->associated)
|
|
|
|
cbss = ifmgd->associated;
|
|
|
|
else if (ifmgd->auth_data)
|
|
|
|
cbss = ifmgd->auth_data->bss;
|
|
|
|
else if (ifmgd->assoc_data)
|
|
|
|
cbss = ifmgd->assoc_data->bss;
|
|
|
|
else
|
2010-11-11 07:50:18 +01:00
|
|
|
return NULL;
|
|
|
|
|
2012-11-29 01:25:20 +01:00
|
|
|
rcu_read_lock();
|
2012-06-28 14:03:13 +02:00
|
|
|
ssid = ieee80211_bss_get_ie(cbss, WLAN_EID_SSID);
|
2012-03-29 16:30:41 +02:00
|
|
|
if (WARN_ON_ONCE(ssid == NULL))
|
|
|
|
ssid_len = 0;
|
|
|
|
else
|
|
|
|
ssid_len = ssid[1];
|
|
|
|
|
2014-06-12 22:24:31 +02:00
|
|
|
skb = ieee80211_build_probe_req(sdata, sdata->vif.addr, cbss->bssid,
|
2012-07-26 17:24:39 +02:00
|
|
|
(u32) -1, cbss->channel,
|
2012-07-23 14:53:27 +02:00
|
|
|
ssid + 2, ssid_len,
|
2011-07-18 18:08:36 +02:00
|
|
|
NULL, 0, true);
|
2012-11-29 01:25:20 +01:00
|
|
|
rcu_read_unlock();
|
2010-11-11 07:50:18 +01:00
|
|
|
|
|
|
|
return skb;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ieee80211_ap_probereq_get);
|
|
|
|
|
2015-03-16 22:23:37 +01:00
|
|
|
static void ieee80211_report_disconnect(struct ieee80211_sub_if_data *sdata,
|
|
|
|
const u8 *buf, size_t len, bool tx,
|
|
|
|
u16 reason)
|
|
|
|
{
|
|
|
|
struct ieee80211_event event = {
|
|
|
|
.type = MLME_EVENT,
|
|
|
|
.u.mlme.data = tx ? DEAUTH_TX_EVENT : DEAUTH_RX_EVENT,
|
|
|
|
.u.mlme.reason = reason,
|
|
|
|
};
|
|
|
|
|
|
|
|
if (tx)
|
|
|
|
cfg80211_tx_mlme_mgmt(sdata->dev, buf, len);
|
|
|
|
else
|
|
|
|
cfg80211_rx_mlme_mgmt(sdata->dev, buf, len);
|
|
|
|
|
|
|
|
drv_event_callback(sdata->local, sdata, &event);
|
|
|
|
}
|
|
|
|
|
2013-01-29 13:09:34 +01:00
|
|
|
static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
|
2010-03-19 06:14:53 +01:00
|
|
|
{
|
2014-04-09 15:10:59 +02:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
2010-03-19 06:14:53 +01:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2012-09-07 13:28:52 +02:00
|
|
|
u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
|
2010-03-19 06:14:53 +01:00
|
|
|
|
2013-05-10 12:32:47 +02:00
|
|
|
sdata_lock(sdata);
|
2010-03-19 06:14:53 +01:00
|
|
|
if (!ifmgd->associated) {
|
2013-05-10 12:32:47 +02:00
|
|
|
sdata_unlock(sdata);
|
2010-03-19 06:14:53 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-02-24 13:50:54 +01:00
|
|
|
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
|
|
|
|
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
|
2013-01-29 13:09:34 +01:00
|
|
|
true, frame_buf);
|
2014-04-09 15:10:59 +02:00
|
|
|
mutex_lock(&local->mtx);
|
2013-09-01 16:15:51 +02:00
|
|
|
sdata->vif.csa_active = false;
|
2014-10-08 08:48:39 +02:00
|
|
|
ifmgd->csa_waiting_bcn = false;
|
2014-06-13 15:30:07 +02:00
|
|
|
if (sdata->csa_block_tx) {
|
|
|
|
ieee80211_wake_vif_queues(local, sdata,
|
|
|
|
IEEE80211_QUEUE_STOP_REASON_CSA);
|
|
|
|
sdata->csa_block_tx = false;
|
|
|
|
}
|
2014-04-09 15:10:59 +02:00
|
|
|
mutex_unlock(&local->mtx);
|
2010-08-05 17:02:38 +02:00
|
|
|
|
2015-03-16 22:23:37 +01:00
|
|
|
ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true,
|
|
|
|
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
|
|
|
|
|
2013-05-10 12:32:47 +02:00
|
|
|
sdata_unlock(sdata);
|
2010-03-19 06:14:53 +01:00
|
|
|
}
|
|
|
|
|
2012-08-01 16:49:34 +02:00
|
|
|
static void ieee80211_beacon_connection_loss_work(struct work_struct *work)
|
2009-07-10 15:29:03 +02:00
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata =
|
|
|
|
container_of(work, struct ieee80211_sub_if_data,
|
2010-03-19 06:14:53 +01:00
|
|
|
u.mgd.beacon_connection_loss_work);
|
2011-12-09 20:01:49 +01:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
struct sta_info *sta;
|
|
|
|
|
|
|
|
if (ifmgd->associated) {
|
2011-12-27 14:24:07 +01:00
|
|
|
rcu_read_lock();
|
2011-12-09 20:01:49 +01:00
|
|
|
sta = sta_info_get(sdata, ifmgd->bssid);
|
|
|
|
if (sta)
|
|
|
|
sta->beacon_loss_count++;
|
2011-12-27 14:24:07 +01:00
|
|
|
rcu_read_unlock();
|
2011-12-09 20:01:49 +01:00
|
|
|
}
|
2009-07-10 15:29:03 +02:00
|
|
|
|
2013-01-29 13:13:50 +01:00
|
|
|
if (ifmgd->connection_loss) {
|
2012-08-01 22:32:45 +02:00
|
|
|
sdata_info(sdata, "Connection to AP %pM lost\n",
|
|
|
|
ifmgd->bssid);
|
2013-01-29 13:09:34 +01:00
|
|
|
__ieee80211_disconnect(sdata);
|
2012-08-01 22:32:45 +02:00
|
|
|
} else {
|
2010-03-19 06:14:53 +01:00
|
|
|
ieee80211_mgd_probe_ap(sdata, true);
|
2012-08-01 22:32:45 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ieee80211_csa_connection_drop_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata =
|
|
|
|
container_of(work, struct ieee80211_sub_if_data,
|
|
|
|
u.mgd.csa_connection_drop_work);
|
|
|
|
|
2013-01-29 13:09:34 +01:00
|
|
|
__ieee80211_disconnect(sdata);
|
2009-07-10 15:29:03 +02:00
|
|
|
}
|
|
|
|
|
2009-03-22 20:57:35 +01:00
|
|
|
void ieee80211_beacon_loss(struct ieee80211_vif *vif)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
2010-03-19 06:14:53 +01:00
|
|
|
struct ieee80211_hw *hw = &sdata->local->hw;
|
2009-03-22 20:57:35 +01:00
|
|
|
|
2010-04-07 16:48:40 +02:00
|
|
|
trace_api_beacon_loss(sdata);
|
|
|
|
|
2013-01-29 13:13:50 +01:00
|
|
|
sdata->u.mgd.connection_loss = false;
|
2010-03-19 06:14:53 +01:00
|
|
|
ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work);
|
2009-03-22 20:57:35 +01:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ieee80211_beacon_loss);
|
|
|
|
|
2010-03-19 06:14:53 +01:00
|
|
|
void ieee80211_connection_loss(struct ieee80211_vif *vif)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
|
|
|
struct ieee80211_hw *hw = &sdata->local->hw;
|
|
|
|
|
2010-04-07 16:48:40 +02:00
|
|
|
trace_api_connection_loss(sdata);
|
|
|
|
|
2013-01-29 13:13:50 +01:00
|
|
|
sdata->u.mgd.connection_loss = true;
|
2010-03-19 06:14:53 +01:00
|
|
|
ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ieee80211_connection_loss);
|
|
|
|
|
|
|
|
|
2012-01-20 13:55:27 +01:00
|
|
|
static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata,
|
|
|
|
bool assoc)
|
|
|
|
{
|
|
|
|
struct ieee80211_mgd_auth_data *auth_data = sdata->u.mgd.auth_data;
|
|
|
|
|
2013-05-10 12:32:47 +02:00
|
|
|
sdata_assert_lock(sdata);
|
2012-01-20 13:55:27 +01:00
|
|
|
|
|
|
|
if (!assoc) {
|
2015-01-19 15:53:06 +01:00
|
|
|
/*
|
|
|
|
* we are not authenticated yet, the only timer that could be
|
|
|
|
* running is the timeout for the authentication response which
|
|
|
|
* which is not relevant anymore.
|
|
|
|
*/
|
|
|
|
del_timer_sync(&sdata->u.mgd.timer);
|
2012-01-20 13:55:27 +01:00
|
|
|
sta_info_destroy_addr(sdata, auth_data->bss->bssid);
|
|
|
|
|
2015-03-03 04:54:57 +01:00
|
|
|
eth_zero_addr(sdata->u.mgd.bssid);
|
2012-01-20 13:55:27 +01:00
|
|
|
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
|
2012-11-26 11:57:41 +01:00
|
|
|
sdata->u.mgd.flags = 0;
|
mac80211: fix iflist_mtx/mtx locking in radar detection
The scan code creates an iflist_mtx -> mtx locking dependency,
and a few other places, notably radar detection, were creating
the opposite dependency, causing lockdep to complain. As scan
and radar detection are mutually exclusive, the deadlock can't
really happen in practice, but it's still bad form.
A similar issue exists in the monitor mode code, but this is
only used by channel-context drivers right now and those have
to have hardware scan, so that also can't happen.
Still, fix these issues by making some of the channel context
code require the mtx to be held rather than acquiring it, thus
allowing the monitor/radar callers to keep the iflist_mtx->mtx
lock ordering.
While at it, also fix access to the local->scanning variable
in the radar code, and document that radar_detect_enabled is
now properly protected by the mtx.
All this would now introduce an ABBA deadlock between the DFS
work cancelling and local->mtx, so change the locking there a
bit to not need to use cancel_delayed_work_sync() but be able
to just use cancel_delayed_work(). The work is also safely
stopped/removed when the interface is stopped, so no extra
changes are needed.
Reported-by: Kalle Valo <kvalo@qca.qualcomm.com>
Tested-by: Simon Wunderlich <sw@simonwunderlich.de>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2013-12-18 09:43:33 +01:00
|
|
|
mutex_lock(&sdata->local->mtx);
|
2012-07-26 17:24:39 +02:00
|
|
|
ieee80211_vif_release_channel(sdata);
|
mac80211: fix iflist_mtx/mtx locking in radar detection
The scan code creates an iflist_mtx -> mtx locking dependency,
and a few other places, notably radar detection, were creating
the opposite dependency, causing lockdep to complain. As scan
and radar detection are mutually exclusive, the deadlock can't
really happen in practice, but it's still bad form.
A similar issue exists in the monitor mode code, but this is
only used by channel-context drivers right now and those have
to have hardware scan, so that also can't happen.
Still, fix these issues by making some of the channel context
code require the mtx to be held rather than acquiring it, thus
allowing the monitor/radar callers to keep the iflist_mtx->mtx
lock ordering.
While at it, also fix access to the local->scanning variable
in the radar code, and document that radar_detect_enabled is
now properly protected by the mtx.
All this would now introduce an ABBA deadlock between the DFS
work cancelling and local->mtx, so change the locking there a
bit to not need to use cancel_delayed_work_sync() but be able
to just use cancel_delayed_work(). The work is also safely
stopped/removed when the interface is stopped, so no extra
changes are needed.
Reported-by: Kalle Valo <kvalo@qca.qualcomm.com>
Tested-by: Simon Wunderlich <sw@simonwunderlich.de>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2013-12-18 09:43:33 +01:00
|
|
|
mutex_unlock(&sdata->local->mtx);
|
2012-01-20 13:55:27 +01:00
|
|
|
}
|
|
|
|
|
2013-02-01 01:49:58 +01:00
|
|
|
cfg80211_put_bss(sdata->local->hw.wiphy, auth_data->bss);
|
2012-01-20 13:55:27 +01:00
|
|
|
kfree(auth_data);
|
|
|
|
sdata->u.mgd.auth_data = NULL;
|
|
|
|
}
|
|
|
|
|
2015-06-01 14:10:09 +02:00
|
|
|
static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata,
|
|
|
|
bool assoc)
|
|
|
|
{
|
|
|
|
struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data;
|
|
|
|
|
|
|
|
sdata_assert_lock(sdata);
|
|
|
|
|
|
|
|
if (!assoc) {
|
|
|
|
/*
|
|
|
|
* we are not associated yet, the only timer that could be
|
|
|
|
* running is the timeout for the association response which
|
|
|
|
* which is not relevant anymore.
|
|
|
|
*/
|
|
|
|
del_timer_sync(&sdata->u.mgd.timer);
|
|
|
|
sta_info_destroy_addr(sdata, assoc_data->bss->bssid);
|
|
|
|
|
|
|
|
eth_zero_addr(sdata->u.mgd.bssid);
|
|
|
|
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
|
|
|
|
sdata->u.mgd.flags = 0;
|
2015-07-08 14:41:43 +02:00
|
|
|
sdata->flags &= ~IEEE80211_SDATA_MU_MIMO_OWNER;
|
2015-06-01 14:10:09 +02:00
|
|
|
mutex_lock(&sdata->local->mtx);
|
|
|
|
ieee80211_vif_release_channel(sdata);
|
|
|
|
mutex_unlock(&sdata->local->mtx);
|
|
|
|
}
|
|
|
|
|
|
|
|
kfree(assoc_data);
|
|
|
|
sdata->u.mgd.assoc_data = NULL;
|
|
|
|
}
|
|
|
|
|
2012-01-20 13:55:27 +01:00
|
|
|
static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct ieee80211_mgmt *mgmt, size_t len)
|
|
|
|
{
|
2013-01-29 15:02:27 +01:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
2012-01-20 13:55:27 +01:00
|
|
|
struct ieee80211_mgd_auth_data *auth_data = sdata->u.mgd.auth_data;
|
|
|
|
u8 *pos;
|
|
|
|
struct ieee802_11_elems elems;
|
2013-01-29 15:02:27 +01:00
|
|
|
u32 tx_flags = 0;
|
2012-01-20 13:55:27 +01:00
|
|
|
|
|
|
|
pos = mgmt->u.auth.variable;
|
2013-03-26 14:54:16 +01:00
|
|
|
ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), false, &elems);
|
2012-01-20 13:55:27 +01:00
|
|
|
if (!elems.challenge)
|
|
|
|
return;
|
|
|
|
auth_data->expected_transaction = 4;
|
2012-06-27 13:18:36 +02:00
|
|
|
drv_mgd_prepare_tx(sdata->local, sdata);
|
2015-06-02 21:39:54 +02:00
|
|
|
if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS))
|
2013-01-29 15:02:27 +01:00
|
|
|
tx_flags = IEEE80211_TX_CTL_REQ_TX_STATUS |
|
|
|
|
IEEE80211_TX_INTFL_MLME_CONN_TX;
|
2012-09-30 18:29:37 +02:00
|
|
|
ieee80211_send_auth(sdata, 3, auth_data->algorithm, 0,
|
2012-01-20 13:55:27 +01:00
|
|
|
elems.challenge - 2, elems.challenge_len + 2,
|
|
|
|
auth_data->bss->bssid, auth_data->bss->bssid,
|
|
|
|
auth_data->key, auth_data->key_len,
|
2013-01-29 15:02:27 +01:00
|
|
|
auth_data->key_idx, tx_flags);
|
2012-01-20 13:55:27 +01:00
|
|
|
}
|
|
|
|
|
2013-05-10 12:32:47 +02:00
|
|
|
static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct ieee80211_mgmt *mgmt, size_t len)
|
2012-01-20 13:55:27 +01:00
|
|
|
{
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
u8 bssid[ETH_ALEN];
|
|
|
|
u16 auth_alg, auth_transaction, status_code;
|
|
|
|
struct sta_info *sta;
|
2015-03-16 22:23:35 +01:00
|
|
|
struct ieee80211_event event = {
|
|
|
|
.type = MLME_EVENT,
|
|
|
|
.u.mlme.data = AUTH_EVENT,
|
|
|
|
};
|
2012-01-20 13:55:27 +01:00
|
|
|
|
2013-05-10 12:32:47 +02:00
|
|
|
sdata_assert_lock(sdata);
|
2012-01-20 13:55:27 +01:00
|
|
|
|
|
|
|
if (len < 24 + 6)
|
2013-05-10 12:32:47 +02:00
|
|
|
return;
|
2012-01-20 13:55:27 +01:00
|
|
|
|
|
|
|
if (!ifmgd->auth_data || ifmgd->auth_data->done)
|
2013-05-10 12:32:47 +02:00
|
|
|
return;
|
2012-01-20 13:55:27 +01:00
|
|
|
|
|
|
|
memcpy(bssid, ifmgd->auth_data->bss->bssid, ETH_ALEN);
|
|
|
|
|
mac80211: Convert compare_ether_addr to ether_addr_equal
Use the new bool function ether_addr_equal to add
some clarity and reduce the likelihood for misuse
of compare_ether_addr for sorting.
Done via cocci script:
$ cat compare_ether_addr.cocci
@@
expression a,b;
@@
- !compare_ether_addr(a, b)
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- compare_ether_addr(a, b)
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) == 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) != 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) == 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) != 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !!ether_addr_equal(a, b)
+ ether_addr_equal(a, b)
Signed-off-by: Joe Perches <joe@perches.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-05-08 20:56:52 +02:00
|
|
|
if (!ether_addr_equal(bssid, mgmt->bssid))
|
2013-05-10 12:32:47 +02:00
|
|
|
return;
|
2012-01-20 13:55:27 +01:00
|
|
|
|
|
|
|
auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg);
|
|
|
|
auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction);
|
|
|
|
status_code = le16_to_cpu(mgmt->u.auth.status_code);
|
|
|
|
|
|
|
|
if (auth_alg != ifmgd->auth_data->algorithm ||
|
2012-09-30 18:29:38 +02:00
|
|
|
auth_transaction != ifmgd->auth_data->expected_transaction) {
|
|
|
|
sdata_info(sdata, "%pM unexpected authentication state: alg %d (expected %d) transact %d (expected %d)\n",
|
|
|
|
mgmt->sa, auth_alg, ifmgd->auth_data->algorithm,
|
|
|
|
auth_transaction,
|
|
|
|
ifmgd->auth_data->expected_transaction);
|
2013-05-10 12:32:47 +02:00
|
|
|
return;
|
2012-09-30 18:29:38 +02:00
|
|
|
}
|
2012-01-20 13:55:27 +01:00
|
|
|
|
|
|
|
if (status_code != WLAN_STATUS_SUCCESS) {
|
2012-06-22 11:29:50 +02:00
|
|
|
sdata_info(sdata, "%pM denied authentication (status %d)\n",
|
|
|
|
mgmt->sa, status_code);
|
2012-05-13 17:07:04 +02:00
|
|
|
ieee80211_destroy_auth_data(sdata, false);
|
2013-05-16 00:55:00 +02:00
|
|
|
cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
|
2015-03-16 22:23:35 +01:00
|
|
|
event.u.mlme.status = MLME_DENIED;
|
|
|
|
event.u.mlme.reason = status_code;
|
|
|
|
drv_event_callback(sdata->local, sdata, &event);
|
2013-05-10 12:32:47 +02:00
|
|
|
return;
|
2012-01-20 13:55:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (ifmgd->auth_data->algorithm) {
|
|
|
|
case WLAN_AUTH_OPEN:
|
|
|
|
case WLAN_AUTH_LEAP:
|
|
|
|
case WLAN_AUTH_FT:
|
2012-09-30 18:29:40 +02:00
|
|
|
case WLAN_AUTH_SAE:
|
2012-01-20 13:55:27 +01:00
|
|
|
break;
|
|
|
|
case WLAN_AUTH_SHARED_KEY:
|
|
|
|
if (ifmgd->auth_data->expected_transaction != 4) {
|
|
|
|
ieee80211_auth_challenge(sdata, mgmt, len);
|
|
|
|
/* need another frame */
|
2013-05-10 12:32:47 +02:00
|
|
|
return;
|
2012-01-20 13:55:27 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
WARN_ONCE(1, "invalid auth alg %d",
|
|
|
|
ifmgd->auth_data->algorithm);
|
2013-05-10 12:32:47 +02:00
|
|
|
return;
|
2012-01-20 13:55:27 +01:00
|
|
|
}
|
|
|
|
|
2015-03-16 22:23:35 +01:00
|
|
|
event.u.mlme.status = MLME_SUCCESS;
|
|
|
|
drv_event_callback(sdata->local, sdata, &event);
|
2012-06-22 11:29:50 +02:00
|
|
|
sdata_info(sdata, "authenticated\n");
|
2012-01-20 13:55:27 +01:00
|
|
|
ifmgd->auth_data->done = true;
|
|
|
|
ifmgd->auth_data->timeout = jiffies + IEEE80211_AUTH_WAIT_ASSOC;
|
2013-02-13 15:39:57 +01:00
|
|
|
ifmgd->auth_data->timeout_started = true;
|
2013-05-10 12:32:47 +02:00
|
|
|
run_again(sdata, ifmgd->auth_data->timeout);
|
2012-01-20 13:55:27 +01:00
|
|
|
|
2012-09-30 18:29:40 +02:00
|
|
|
if (ifmgd->auth_data->algorithm == WLAN_AUTH_SAE &&
|
|
|
|
ifmgd->auth_data->expected_transaction != 2) {
|
|
|
|
/*
|
|
|
|
* Report auth frame to user space for processing since another
|
|
|
|
* round of Authentication frames is still needed.
|
|
|
|
*/
|
2013-05-16 00:55:00 +02:00
|
|
|
cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
|
2013-05-10 12:32:47 +02:00
|
|
|
return;
|
2012-09-30 18:29:40 +02:00
|
|
|
}
|
|
|
|
|
2012-01-20 13:55:27 +01:00
|
|
|
/* move station state to auth */
|
|
|
|
mutex_lock(&sdata->local->sta_mtx);
|
|
|
|
sta = sta_info_get(sdata, bssid);
|
|
|
|
if (!sta) {
|
|
|
|
WARN_ONCE(1, "%s: STA %pM not found", sdata->name, bssid);
|
|
|
|
goto out_err;
|
|
|
|
}
|
|
|
|
if (sta_info_move_state(sta, IEEE80211_STA_AUTH)) {
|
2012-06-22 11:29:50 +02:00
|
|
|
sdata_info(sdata, "failed moving %pM to auth\n", bssid);
|
2012-01-20 13:55:27 +01:00
|
|
|
goto out_err;
|
|
|
|
}
|
|
|
|
mutex_unlock(&sdata->local->sta_mtx);
|
|
|
|
|
2013-05-16 00:55:00 +02:00
|
|
|
cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
|
2013-05-10 12:32:47 +02:00
|
|
|
return;
|
2012-01-20 13:55:27 +01:00
|
|
|
out_err:
|
|
|
|
mutex_unlock(&sdata->local->sta_mtx);
|
|
|
|
/* ignore frame -- wait for timeout */
|
|
|
|
}
|
|
|
|
|
2014-02-11 19:36:24 +01:00
|
|
|
#define case_WLAN(type) \
|
|
|
|
case WLAN_REASON_##type: return #type
|
|
|
|
|
|
|
|
static const char *ieee80211_get_reason_code_string(u16 reason_code)
|
|
|
|
{
|
|
|
|
switch (reason_code) {
|
|
|
|
case_WLAN(UNSPECIFIED);
|
|
|
|
case_WLAN(PREV_AUTH_NOT_VALID);
|
|
|
|
case_WLAN(DEAUTH_LEAVING);
|
|
|
|
case_WLAN(DISASSOC_DUE_TO_INACTIVITY);
|
|
|
|
case_WLAN(DISASSOC_AP_BUSY);
|
|
|
|
case_WLAN(CLASS2_FRAME_FROM_NONAUTH_STA);
|
|
|
|
case_WLAN(CLASS3_FRAME_FROM_NONASSOC_STA);
|
|
|
|
case_WLAN(DISASSOC_STA_HAS_LEFT);
|
|
|
|
case_WLAN(STA_REQ_ASSOC_WITHOUT_AUTH);
|
|
|
|
case_WLAN(DISASSOC_BAD_POWER);
|
|
|
|
case_WLAN(DISASSOC_BAD_SUPP_CHAN);
|
|
|
|
case_WLAN(INVALID_IE);
|
|
|
|
case_WLAN(MIC_FAILURE);
|
|
|
|
case_WLAN(4WAY_HANDSHAKE_TIMEOUT);
|
|
|
|
case_WLAN(GROUP_KEY_HANDSHAKE_TIMEOUT);
|
|
|
|
case_WLAN(IE_DIFFERENT);
|
|
|
|
case_WLAN(INVALID_GROUP_CIPHER);
|
|
|
|
case_WLAN(INVALID_PAIRWISE_CIPHER);
|
|
|
|
case_WLAN(INVALID_AKMP);
|
|
|
|
case_WLAN(UNSUPP_RSN_VERSION);
|
|
|
|
case_WLAN(INVALID_RSN_IE_CAP);
|
|
|
|
case_WLAN(IEEE8021X_FAILED);
|
|
|
|
case_WLAN(CIPHER_SUITE_REJECTED);
|
|
|
|
case_WLAN(DISASSOC_UNSPECIFIED_QOS);
|
|
|
|
case_WLAN(DISASSOC_QAP_NO_BANDWIDTH);
|
|
|
|
case_WLAN(DISASSOC_LOW_ACK);
|
|
|
|
case_WLAN(DISASSOC_QAP_EXCEED_TXOP);
|
|
|
|
case_WLAN(QSTA_LEAVE_QBSS);
|
|
|
|
case_WLAN(QSTA_NOT_USE);
|
|
|
|
case_WLAN(QSTA_REQUIRE_SETUP);
|
|
|
|
case_WLAN(QSTA_TIMEOUT);
|
|
|
|
case_WLAN(QSTA_CIPHER_NOT_SUPP);
|
|
|
|
case_WLAN(MESH_PEER_CANCELED);
|
|
|
|
case_WLAN(MESH_MAX_PEERS);
|
|
|
|
case_WLAN(MESH_CONFIG);
|
|
|
|
case_WLAN(MESH_CLOSE);
|
|
|
|
case_WLAN(MESH_MAX_RETRIES);
|
|
|
|
case_WLAN(MESH_CONFIRM_TIMEOUT);
|
|
|
|
case_WLAN(MESH_INVALID_GTK);
|
|
|
|
case_WLAN(MESH_INCONSISTENT_PARAM);
|
|
|
|
case_WLAN(MESH_INVALID_SECURITY);
|
|
|
|
case_WLAN(MESH_PATH_ERROR);
|
|
|
|
case_WLAN(MESH_PATH_NOFORWARD);
|
|
|
|
case_WLAN(MESH_PATH_DEST_UNREACHABLE);
|
|
|
|
case_WLAN(MAC_EXISTS_IN_MBSS);
|
|
|
|
case_WLAN(MESH_CHAN_REGULATORY);
|
|
|
|
case_WLAN(MESH_CHAN);
|
|
|
|
default: return "<unknown>";
|
|
|
|
}
|
|
|
|
}
|
2012-01-20 13:55:27 +01:00
|
|
|
|
2013-05-10 12:32:47 +02:00
|
|
|
static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct ieee80211_mgmt *mgmt, size_t len)
|
2007-05-05 20:45:53 +02:00
|
|
|
{
|
2009-02-15 12:44:28 +01:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2015-06-01 14:10:09 +02:00
|
|
|
u16 reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
|
2007-05-05 20:45:53 +02:00
|
|
|
|
2013-05-10 12:32:47 +02:00
|
|
|
sdata_assert_lock(sdata);
|
2012-01-20 13:55:27 +01:00
|
|
|
|
2008-06-30 15:10:46 +02:00
|
|
|
if (len < 24 + 2)
|
2013-05-10 12:32:47 +02:00
|
|
|
return;
|
2007-05-05 20:45:53 +02:00
|
|
|
|
2015-06-01 14:10:09 +02:00
|
|
|
if (ifmgd->associated &&
|
|
|
|
ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid)) {
|
|
|
|
const u8 *bssid = ifmgd->associated->bssid;
|
2009-07-07 03:45:17 +02:00
|
|
|
|
2015-06-01 14:10:09 +02:00
|
|
|
sdata_info(sdata, "deauthenticated from %pM (Reason: %u=%s)\n",
|
|
|
|
bssid, reason_code,
|
|
|
|
ieee80211_get_reason_code_string(reason_code));
|
2007-05-05 20:45:53 +02:00
|
|
|
|
2015-06-01 14:10:09 +02:00
|
|
|
ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
|
2007-05-05 20:45:53 +02:00
|
|
|
|
2015-06-01 14:10:09 +02:00
|
|
|
ieee80211_report_disconnect(sdata, (u8 *)mgmt, len, false,
|
|
|
|
reason_code);
|
|
|
|
return;
|
|
|
|
}
|
2009-07-07 03:45:17 +02:00
|
|
|
|
2015-06-01 14:10:09 +02:00
|
|
|
if (ifmgd->assoc_data &&
|
|
|
|
ether_addr_equal(mgmt->bssid, ifmgd->assoc_data->bss->bssid)) {
|
|
|
|
const u8 *bssid = ifmgd->assoc_data->bss->bssid;
|
2012-02-24 13:50:54 +01:00
|
|
|
|
2015-06-01 14:10:09 +02:00
|
|
|
sdata_info(sdata,
|
|
|
|
"deauthenticated from %pM while associating (Reason: %u=%s)\n",
|
|
|
|
bssid, reason_code,
|
|
|
|
ieee80211_get_reason_code_string(reason_code));
|
|
|
|
|
|
|
|
ieee80211_destroy_assoc_data(sdata, false);
|
|
|
|
|
|
|
|
cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
|
|
|
|
return;
|
|
|
|
}
|
2007-05-05 20:45:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-05-10 12:32:47 +02:00
|
|
|
static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct ieee80211_mgmt *mgmt, size_t len)
|
2007-05-05 20:45:53 +02:00
|
|
|
{
|
2009-02-15 12:44:28 +01:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2007-05-05 20:45:53 +02:00
|
|
|
u16 reason_code;
|
|
|
|
|
2013-05-10 12:32:47 +02:00
|
|
|
sdata_assert_lock(sdata);
|
2009-07-07 03:45:17 +02:00
|
|
|
|
2012-01-20 13:55:27 +01:00
|
|
|
if (len < 24 + 2)
|
2013-05-10 12:32:47 +02:00
|
|
|
return;
|
2009-07-07 03:45:17 +02:00
|
|
|
|
2012-01-20 13:55:27 +01:00
|
|
|
if (!ifmgd->associated ||
|
mac80211: Convert compare_ether_addr to ether_addr_equal
Use the new bool function ether_addr_equal to add
some clarity and reduce the likelihood for misuse
of compare_ether_addr for sorting.
Done via cocci script:
$ cat compare_ether_addr.cocci
@@
expression a,b;
@@
- !compare_ether_addr(a, b)
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- compare_ether_addr(a, b)
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) == 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) != 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) == 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) != 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !!ether_addr_equal(a, b)
+ ether_addr_equal(a, b)
Signed-off-by: Joe Perches <joe@perches.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-05-08 20:56:52 +02:00
|
|
|
!ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid))
|
2013-05-10 12:32:47 +02:00
|
|
|
return;
|
2007-05-05 20:45:53 +02:00
|
|
|
|
|
|
|
reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
|
|
|
|
|
2012-06-22 11:29:50 +02:00
|
|
|
sdata_info(sdata, "disassociated from %pM (Reason: %u)\n",
|
|
|
|
mgmt->sa, reason_code);
|
2007-05-05 20:45:53 +02:00
|
|
|
|
2012-02-24 13:50:54 +01:00
|
|
|
ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
|
|
|
|
|
2015-03-16 22:23:37 +01:00
|
|
|
ieee80211_report_disconnect(sdata, (u8 *)mgmt, len, false, reason_code);
|
2007-05-05 20:45:53 +02:00
|
|
|
}
|
|
|
|
|
2011-10-15 00:14:49 +02:00
|
|
|
static void ieee80211_get_rates(struct ieee80211_supported_band *sband,
|
|
|
|
u8 *supp_rates, unsigned int supp_rates_len,
|
|
|
|
u32 *rates, u32 *basic_rates,
|
|
|
|
bool *have_higher_than_11mbit,
|
2013-07-08 16:55:53 +02:00
|
|
|
int *min_rate, int *min_rate_index,
|
|
|
|
int shift, u32 rate_flags)
|
2011-10-15 00:14:49 +02:00
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
for (i = 0; i < supp_rates_len; i++) {
|
2013-07-08 16:55:53 +02:00
|
|
|
int rate = supp_rates[i] & 0x7f;
|
2011-10-15 00:14:49 +02:00
|
|
|
bool is_basic = !!(supp_rates[i] & 0x80);
|
|
|
|
|
2013-07-08 16:55:53 +02:00
|
|
|
if ((rate * 5 * (1 << shift)) > 110)
|
2011-10-15 00:14:49 +02:00
|
|
|
*have_higher_than_11mbit = true;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* BSS_MEMBERSHIP_SELECTOR_HT_PHY is defined in 802.11n-2009
|
|
|
|
* 7.3.2.2 as a magic value instead of a rate. Hence, skip it.
|
|
|
|
*
|
|
|
|
* Note: Even through the membership selector and the basic
|
|
|
|
* rate flag share the same bit, they are not exactly
|
|
|
|
* the same.
|
|
|
|
*/
|
|
|
|
if (!!(supp_rates[i] & 0x80) &&
|
|
|
|
(supp_rates[i] & 0x7f) == BSS_MEMBERSHIP_SELECTOR_HT_PHY)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
for (j = 0; j < sband->n_bitrates; j++) {
|
2013-07-08 16:55:53 +02:00
|
|
|
struct ieee80211_rate *br;
|
|
|
|
int brate;
|
|
|
|
|
|
|
|
br = &sband->bitrates[j];
|
|
|
|
if ((rate_flags & br->flags) != rate_flags)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
brate = DIV_ROUND_UP(br->bitrate, (1 << shift) * 5);
|
|
|
|
if (brate == rate) {
|
2011-10-15 00:14:49 +02:00
|
|
|
*rates |= BIT(j);
|
|
|
|
if (is_basic)
|
|
|
|
*basic_rates |= BIT(j);
|
2013-07-08 16:55:53 +02:00
|
|
|
if ((rate * 5) < *min_rate) {
|
|
|
|
*min_rate = rate * 5;
|
2011-10-15 00:14:49 +02:00
|
|
|
*min_rate_index = j;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2007-05-05 20:45:53 +02:00
|
|
|
|
2012-01-20 13:55:27 +01:00
|
|
|
static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct cfg80211_bss *cbss,
|
2009-12-23 13:15:35 +01:00
|
|
|
struct ieee80211_mgmt *mgmt, size_t len)
|
2007-05-05 20:45:53 +02:00
|
|
|
{
|
2009-02-15 12:44:28 +01:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2007-12-28 14:32:58 +01:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
2008-01-24 19:38:38 +01:00
|
|
|
struct ieee80211_supported_band *sband;
|
2007-05-05 20:45:53 +02:00
|
|
|
struct sta_info *sta;
|
2009-12-23 13:15:35 +01:00
|
|
|
u8 *pos;
|
|
|
|
u16 capab_info, aid;
|
2007-05-05 20:45:53 +02:00
|
|
|
struct ieee802_11_elems elems;
|
2008-10-11 01:51:51 +02:00
|
|
|
struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
|
2013-05-28 10:54:03 +02:00
|
|
|
const struct cfg80211_bss_ies *bss_ies = NULL;
|
|
|
|
struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data;
|
2008-10-14 16:58:37 +02:00
|
|
|
u32 changed = 0;
|
2011-10-15 00:14:49 +02:00
|
|
|
int err;
|
2013-05-28 10:54:03 +02:00
|
|
|
bool ret;
|
2007-05-05 20:45:53 +02:00
|
|
|
|
2009-12-23 13:15:35 +01:00
|
|
|
/* AssocResp and ReassocResp have identical structure */
|
2007-05-05 20:45:53 +02:00
|
|
|
|
|
|
|
aid = le16_to_cpu(mgmt->u.assoc_resp.aid);
|
2009-12-23 13:15:35 +01:00
|
|
|
capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
|
2007-05-05 20:45:53 +02:00
|
|
|
|
2007-10-10 12:03:41 +02:00
|
|
|
if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14)))
|
2012-06-22 11:29:50 +02:00
|
|
|
sdata_info(sdata, "invalid AID value 0x%x; bits 15:14 not set\n",
|
|
|
|
aid);
|
2007-10-10 12:03:41 +02:00
|
|
|
aid &= ~(BIT(15) | BIT(14));
|
|
|
|
|
2011-10-28 11:59:47 +02:00
|
|
|
ifmgd->broken_ap = false;
|
|
|
|
|
|
|
|
if (aid == 0 || aid > IEEE80211_MAX_AID) {
|
2012-06-22 11:29:50 +02:00
|
|
|
sdata_info(sdata, "invalid AID value %d (out of range), turn off PS\n",
|
|
|
|
aid);
|
2011-10-28 11:59:47 +02:00
|
|
|
aid = 0;
|
|
|
|
ifmgd->broken_ap = true;
|
|
|
|
}
|
|
|
|
|
2009-12-23 13:15:35 +01:00
|
|
|
pos = mgmt->u.assoc_resp.variable;
|
2013-03-26 14:54:16 +01:00
|
|
|
ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), false, &elems);
|
2009-12-23 13:15:35 +01:00
|
|
|
|
2007-05-05 20:45:53 +02:00
|
|
|
if (!elems.supp_rates) {
|
2012-06-22 11:29:50 +02:00
|
|
|
sdata_info(sdata, "no SuppRates element in AssocResp\n");
|
2009-12-23 13:15:35 +01:00
|
|
|
return false;
|
2007-05-05 20:45:53 +02:00
|
|
|
}
|
|
|
|
|
2009-02-15 12:44:28 +01:00
|
|
|
ifmgd->aid = aid;
|
2014-11-09 17:50:15 +01:00
|
|
|
ifmgd->tdls_chan_switch_prohibited =
|
|
|
|
elems.ext_capab && elems.ext_capab_len >= 5 &&
|
|
|
|
(elems.ext_capab[4] & WLAN_EXT_CAPA5_TDLS_CH_SW_PROHIBITED);
|
2007-05-05 20:45:53 +02:00
|
|
|
|
2013-05-28 10:54:03 +02:00
|
|
|
/*
|
|
|
|
* Some APs are erroneously not including some information in their
|
|
|
|
* (re)association response frames. Try to recover by using the data
|
|
|
|
* from the beacon or probe response. This seems to afflict mobile
|
|
|
|
* 2G/3G/4G wifi routers, reported models include the "Onda PN51T",
|
|
|
|
* "Vodafone PocketWiFi 2", "ZTE MF60" and a similar T-Mobile device.
|
|
|
|
*/
|
|
|
|
if ((assoc_data->wmm && !elems.wmm_param) ||
|
|
|
|
(!(ifmgd->flags & IEEE80211_STA_DISABLE_HT) &&
|
|
|
|
(!elems.ht_cap_elem || !elems.ht_operation)) ||
|
|
|
|
(!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT) &&
|
|
|
|
(!elems.vht_cap_elem || !elems.vht_operation))) {
|
|
|
|
const struct cfg80211_bss_ies *ies;
|
|
|
|
struct ieee802_11_elems bss_elems;
|
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
ies = rcu_dereference(cbss->ies);
|
|
|
|
if (ies)
|
|
|
|
bss_ies = kmemdup(ies, sizeof(*ies) + ies->len,
|
|
|
|
GFP_ATOMIC);
|
|
|
|
rcu_read_unlock();
|
|
|
|
if (!bss_ies)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
ieee802_11_parse_elems(bss_ies->data, bss_ies->len,
|
|
|
|
false, &bss_elems);
|
|
|
|
if (assoc_data->wmm &&
|
|
|
|
!elems.wmm_param && bss_elems.wmm_param) {
|
|
|
|
elems.wmm_param = bss_elems.wmm_param;
|
|
|
|
sdata_info(sdata,
|
|
|
|
"AP bug: WMM param missing from AssocResp\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Also check if we requested HT/VHT, otherwise the AP doesn't
|
|
|
|
* have to include the IEs in the (re)association response.
|
|
|
|
*/
|
|
|
|
if (!elems.ht_cap_elem && bss_elems.ht_cap_elem &&
|
|
|
|
!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) {
|
|
|
|
elems.ht_cap_elem = bss_elems.ht_cap_elem;
|
|
|
|
sdata_info(sdata,
|
|
|
|
"AP bug: HT capability missing from AssocResp\n");
|
|
|
|
}
|
|
|
|
if (!elems.ht_operation && bss_elems.ht_operation &&
|
|
|
|
!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) {
|
|
|
|
elems.ht_operation = bss_elems.ht_operation;
|
|
|
|
sdata_info(sdata,
|
|
|
|
"AP bug: HT operation missing from AssocResp\n");
|
|
|
|
}
|
|
|
|
if (!elems.vht_cap_elem && bss_elems.vht_cap_elem &&
|
|
|
|
!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) {
|
|
|
|
elems.vht_cap_elem = bss_elems.vht_cap_elem;
|
|
|
|
sdata_info(sdata,
|
|
|
|
"AP bug: VHT capa missing from AssocResp\n");
|
|
|
|
}
|
|
|
|
if (!elems.vht_operation && bss_elems.vht_operation &&
|
|
|
|
!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)) {
|
|
|
|
elems.vht_operation = bss_elems.vht_operation;
|
|
|
|
sdata_info(sdata,
|
|
|
|
"AP bug: VHT operation missing from AssocResp\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-08 15:12:14 +01:00
|
|
|
/*
|
|
|
|
* We previously checked these in the beacon/probe response, so
|
|
|
|
* they should be present here. This is just a safety net.
|
|
|
|
*/
|
|
|
|
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT) &&
|
|
|
|
(!elems.wmm_param || !elems.ht_cap_elem || !elems.ht_operation)) {
|
|
|
|
sdata_info(sdata,
|
2013-05-28 10:54:03 +02:00
|
|
|
"HT AP is missing WMM params or HT capability/operation\n");
|
|
|
|
ret = false;
|
|
|
|
goto out;
|
2013-02-08 15:12:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT) &&
|
|
|
|
(!elems.vht_cap_elem || !elems.vht_operation)) {
|
|
|
|
sdata_info(sdata,
|
2013-05-28 10:54:03 +02:00
|
|
|
"VHT AP is missing VHT capability/operation\n");
|
|
|
|
ret = false;
|
|
|
|
goto out;
|
2013-02-08 15:12:14 +01:00
|
|
|
}
|
|
|
|
|
2011-08-17 14:18:15 +02:00
|
|
|
mutex_lock(&sdata->local->sta_mtx);
|
|
|
|
/*
|
|
|
|
* station info was already allocated and inserted before
|
|
|
|
* the association and should be available to us
|
|
|
|
*/
|
2012-01-20 13:55:24 +01:00
|
|
|
sta = sta_info_get(sdata, cbss->bssid);
|
2011-08-17 14:18:15 +02:00
|
|
|
if (WARN_ON(!sta)) {
|
|
|
|
mutex_unlock(&sdata->local->sta_mtx);
|
2013-05-28 10:54:03 +02:00
|
|
|
ret = false;
|
|
|
|
goto out;
|
2009-07-07 03:45:17 +02:00
|
|
|
}
|
2008-04-01 01:00:13 +02:00
|
|
|
|
2012-07-26 17:24:39 +02:00
|
|
|
sband = local->hw.wiphy->bands[ieee80211_get_sdata_band(sdata)];
|
2008-02-23 15:17:12 +01:00
|
|
|
|
2013-02-08 15:12:14 +01:00
|
|
|
/* Set up internal HT/VHT capabilities */
|
2012-11-22 14:32:09 +01:00
|
|
|
if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_HT))
|
2011-11-18 20:32:00 +01:00
|
|
|
ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
|
2013-02-07 11:47:44 +01:00
|
|
|
elems.ht_cap_elem, sta);
|
2012-03-28 10:58:37 +02:00
|
|
|
|
2012-10-10 13:33:04 +02:00
|
|
|
if (elems.vht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))
|
|
|
|
ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
|
2013-02-07 11:58:58 +01:00
|
|
|
elems.vht_cap_elem, sta);
|
2012-10-10 13:33:04 +02:00
|
|
|
|
2013-02-08 15:12:14 +01:00
|
|
|
/*
|
|
|
|
* Some APs, e.g. Netgear WNDR3700, report invalid HT operation data
|
|
|
|
* in their association response, so ignore that data for our own
|
|
|
|
* configuration. If it changed since the last beacon, we'll get the
|
|
|
|
* next beacon and update then.
|
|
|
|
*/
|
2013-02-07 17:36:12 +01:00
|
|
|
|
2013-02-07 22:24:55 +01:00
|
|
|
/*
|
|
|
|
* If an operating mode notification IE is present, override the
|
|
|
|
* NSS calculation (that would be done in rate_control_rate_init())
|
|
|
|
* and use the # of streams from that element.
|
|
|
|
*/
|
|
|
|
if (elems.opmode_notif &&
|
|
|
|
!(*elems.opmode_notif & IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF)) {
|
|
|
|
u8 nss;
|
|
|
|
|
|
|
|
nss = *elems.opmode_notif & IEEE80211_OPMODE_NOTIF_RX_NSS_MASK;
|
|
|
|
nss >>= IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT;
|
|
|
|
nss += 1;
|
|
|
|
sta->sta.rx_nss = nss;
|
|
|
|
}
|
|
|
|
|
2008-09-18 18:14:18 +02:00
|
|
|
rate_control_rate_init(sta);
|
2007-05-05 20:45:53 +02:00
|
|
|
|
2015-06-17 09:58:06 +02:00
|
|
|
if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED)
|
2011-09-29 16:04:36 +02:00
|
|
|
set_sta_flag(sta, WLAN_STA_MFP);
|
2009-01-08 12:31:59 +01:00
|
|
|
|
2015-03-21 08:09:55 +01:00
|
|
|
sta->sta.wme = elems.wmm_param && local->hw.queues >= IEEE80211_NUM_ACS;
|
2008-10-22 11:41:38 +02:00
|
|
|
|
2013-02-07 17:19:08 +01:00
|
|
|
err = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
|
2012-01-20 13:55:17 +01:00
|
|
|
if (!err && !(ifmgd->flags & IEEE80211_STA_CONTROL_PORT))
|
|
|
|
err = sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
|
|
|
|
if (err) {
|
2012-06-22 11:29:50 +02:00
|
|
|
sdata_info(sdata,
|
|
|
|
"failed to move station %pM to desired state\n",
|
|
|
|
sta->sta.addr);
|
2012-01-20 13:55:17 +01:00
|
|
|
WARN_ON(__sta_info_destroy(sta));
|
|
|
|
mutex_unlock(&sdata->local->sta_mtx);
|
2013-05-28 10:54:03 +02:00
|
|
|
ret = false;
|
|
|
|
goto out;
|
2012-01-20 13:55:17 +01:00
|
|
|
}
|
|
|
|
|
2012-01-20 13:55:24 +01:00
|
|
|
mutex_unlock(&sdata->local->sta_mtx);
|
2008-10-22 11:41:38 +02:00
|
|
|
|
2010-09-28 13:39:32 +02:00
|
|
|
/*
|
|
|
|
* Always handle WMM once after association regardless
|
|
|
|
* of the first value the AP uses. Setting -1 here has
|
|
|
|
* that effect because the AP values is an unsigned
|
|
|
|
* 4-bit value.
|
|
|
|
*/
|
|
|
|
ifmgd->wmm_last_param_set = -1;
|
|
|
|
|
2013-10-15 12:25:07 +02:00
|
|
|
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM) && elems.wmm_param)
|
2010-07-19 16:39:04 +02:00
|
|
|
ieee80211_sta_wmm_params(local, sdata, elems.wmm_param,
|
2008-09-08 17:44:22 +02:00
|
|
|
elems.wmm_param_len);
|
2009-05-07 16:16:24 +02:00
|
|
|
else
|
2012-03-02 15:56:59 +01:00
|
|
|
ieee80211_set_wmm_default(sdata, false);
|
|
|
|
changed |= BSS_CHANGED_QOS;
|
2007-05-05 20:45:53 +02:00
|
|
|
|
2008-09-08 17:44:22 +02:00
|
|
|
/* set AID and assoc capability,
|
|
|
|
* ieee80211_set_associated() will tell the driver */
|
|
|
|
bss_conf->aid = aid;
|
|
|
|
bss_conf->assoc_capability = capab_info;
|
2009-12-23 13:15:39 +01:00
|
|
|
ieee80211_set_associated(sdata, cbss, changed);
|
2007-05-05 20:45:53 +02:00
|
|
|
|
2010-01-08 18:06:26 +01:00
|
|
|
/*
|
|
|
|
* If we're using 4-addr mode, let the AP know that we're
|
|
|
|
* doing so, so that it can create the STA VLAN on its side
|
|
|
|
*/
|
|
|
|
if (ifmgd->use_4addr)
|
|
|
|
ieee80211_send_4addr_nullfunc(local, sdata);
|
|
|
|
|
2009-03-22 20:57:14 +01:00
|
|
|
/*
|
2009-07-10 15:29:03 +02:00
|
|
|
* Start timer to probe the connection to the AP now.
|
|
|
|
* Also start the timer that will detect beacon loss.
|
2009-03-22 20:57:14 +01:00
|
|
|
*/
|
2009-07-10 15:29:03 +02:00
|
|
|
ieee80211_sta_rx_notify(sdata, (struct ieee80211_hdr *)mgmt);
|
2010-09-16 21:12:32 +02:00
|
|
|
ieee80211_sta_reset_beacon_monitor(sdata);
|
2009-03-22 20:57:14 +01:00
|
|
|
|
2013-05-28 10:54:03 +02:00
|
|
|
ret = true;
|
|
|
|
out:
|
|
|
|
kfree(bss_ies);
|
|
|
|
return ret;
|
2007-05-05 20:45:53 +02:00
|
|
|
}
|
|
|
|
|
2013-05-10 12:32:47 +02:00
|
|
|
static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct ieee80211_mgmt *mgmt,
|
|
|
|
size_t len)
|
2012-01-20 13:55:27 +01:00
|
|
|
{
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data;
|
|
|
|
u16 capab_info, status_code, aid;
|
|
|
|
struct ieee802_11_elems elems;
|
2014-09-09 16:09:45 +02:00
|
|
|
int ac, uapsd_queues = -1;
|
2012-01-20 13:55:27 +01:00
|
|
|
u8 *pos;
|
|
|
|
bool reassoc;
|
2013-05-10 12:32:47 +02:00
|
|
|
struct cfg80211_bss *bss;
|
2015-03-16 22:23:36 +01:00
|
|
|
struct ieee80211_event event = {
|
|
|
|
.type = MLME_EVENT,
|
|
|
|
.u.mlme.data = ASSOC_EVENT,
|
|
|
|
};
|
2012-01-20 13:55:27 +01:00
|
|
|
|
2013-05-10 12:32:47 +02:00
|
|
|
sdata_assert_lock(sdata);
|
2012-01-20 13:55:27 +01:00
|
|
|
|
|
|
|
if (!assoc_data)
|
2013-05-10 12:32:47 +02:00
|
|
|
return;
|
mac80211: Convert compare_ether_addr to ether_addr_equal
Use the new bool function ether_addr_equal to add
some clarity and reduce the likelihood for misuse
of compare_ether_addr for sorting.
Done via cocci script:
$ cat compare_ether_addr.cocci
@@
expression a,b;
@@
- !compare_ether_addr(a, b)
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- compare_ether_addr(a, b)
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) == 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) != 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) == 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) != 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !!ether_addr_equal(a, b)
+ ether_addr_equal(a, b)
Signed-off-by: Joe Perches <joe@perches.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-05-08 20:56:52 +02:00
|
|
|
if (!ether_addr_equal(assoc_data->bss->bssid, mgmt->bssid))
|
2013-05-10 12:32:47 +02:00
|
|
|
return;
|
2012-01-20 13:55:27 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* AssocResp and ReassocResp have identical structure, so process both
|
|
|
|
* of them in this function.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (len < 24 + 6)
|
2013-05-10 12:32:47 +02:00
|
|
|
return;
|
2012-01-20 13:55:27 +01:00
|
|
|
|
|
|
|
reassoc = ieee80211_is_reassoc_req(mgmt->frame_control);
|
|
|
|
capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
|
|
|
|
status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
|
|
|
|
aid = le16_to_cpu(mgmt->u.assoc_resp.aid);
|
|
|
|
|
2012-06-22 11:29:50 +02:00
|
|
|
sdata_info(sdata,
|
|
|
|
"RX %sssocResp from %pM (capab=0x%x status=%d aid=%d)\n",
|
|
|
|
reassoc ? "Rea" : "A", mgmt->sa,
|
|
|
|
capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14))));
|
2012-01-20 13:55:27 +01:00
|
|
|
|
|
|
|
pos = mgmt->u.assoc_resp.variable;
|
2013-03-26 14:54:16 +01:00
|
|
|
ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), false, &elems);
|
2012-01-20 13:55:27 +01:00
|
|
|
|
|
|
|
if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY &&
|
2013-03-27 14:38:07 +01:00
|
|
|
elems.timeout_int &&
|
|
|
|
elems.timeout_int->type == WLAN_TIMEOUT_ASSOC_COMEBACK) {
|
2012-01-20 13:55:27 +01:00
|
|
|
u32 tu, ms;
|
2013-03-27 14:38:07 +01:00
|
|
|
tu = le32_to_cpu(elems.timeout_int->value);
|
2012-01-20 13:55:27 +01:00
|
|
|
ms = tu * 1024 / 1000;
|
2012-06-22 11:29:50 +02:00
|
|
|
sdata_info(sdata,
|
|
|
|
"%pM rejected association temporarily; comeback duration %u TU (%u ms)\n",
|
|
|
|
mgmt->sa, tu, ms);
|
2012-01-20 13:55:27 +01:00
|
|
|
assoc_data->timeout = jiffies + msecs_to_jiffies(ms);
|
2013-02-13 15:39:57 +01:00
|
|
|
assoc_data->timeout_started = true;
|
2012-01-20 13:55:27 +01:00
|
|
|
if (ms > IEEE80211_ASSOC_TIMEOUT)
|
2013-05-10 12:32:47 +02:00
|
|
|
run_again(sdata, assoc_data->timeout);
|
|
|
|
return;
|
2012-01-20 13:55:27 +01:00
|
|
|
}
|
|
|
|
|
2013-05-10 12:32:47 +02:00
|
|
|
bss = assoc_data->bss;
|
2012-01-20 13:55:27 +01:00
|
|
|
|
|
|
|
if (status_code != WLAN_STATUS_SUCCESS) {
|
2012-06-22 11:29:50 +02:00
|
|
|
sdata_info(sdata, "%pM denied association (code=%d)\n",
|
|
|
|
mgmt->sa, status_code);
|
2012-01-20 13:55:27 +01:00
|
|
|
ieee80211_destroy_assoc_data(sdata, false);
|
2015-03-16 22:23:36 +01:00
|
|
|
event.u.mlme.status = MLME_DENIED;
|
|
|
|
event.u.mlme.reason = status_code;
|
|
|
|
drv_event_callback(sdata->local, sdata, &event);
|
2012-01-20 13:55:27 +01:00
|
|
|
} else {
|
2013-05-10 12:32:47 +02:00
|
|
|
if (!ieee80211_assoc_success(sdata, bss, mgmt, len)) {
|
2012-01-20 13:55:27 +01:00
|
|
|
/* oops -- internal error -- send timeout for now */
|
2012-07-02 13:42:03 +02:00
|
|
|
ieee80211_destroy_assoc_data(sdata, false);
|
2013-06-19 13:05:42 +02:00
|
|
|
cfg80211_assoc_timeout(sdata->dev, bss);
|
2013-05-10 12:32:47 +02:00
|
|
|
return;
|
2012-01-20 13:55:27 +01:00
|
|
|
}
|
2015-03-16 22:23:36 +01:00
|
|
|
event.u.mlme.status = MLME_SUCCESS;
|
|
|
|
drv_event_callback(sdata->local, sdata, &event);
|
2012-07-09 22:34:34 +02:00
|
|
|
sdata_info(sdata, "associated\n");
|
2012-02-20 14:19:58 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* destroy assoc_data afterwards, as otherwise an idle
|
|
|
|
* recalc after assoc_data is NULL but before associated
|
|
|
|
* is set can cause the interface to go idle
|
|
|
|
*/
|
|
|
|
ieee80211_destroy_assoc_data(sdata, true);
|
2014-09-09 16:09:45 +02:00
|
|
|
|
|
|
|
/* get uapsd queues configuration */
|
|
|
|
uapsd_queues = 0;
|
|
|
|
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
|
|
|
|
if (sdata->tx_conf[ac].uapsd)
|
|
|
|
uapsd_queues |= BIT(ac);
|
2012-01-20 13:55:27 +01:00
|
|
|
}
|
|
|
|
|
2014-09-09 16:09:45 +02:00
|
|
|
cfg80211_rx_assoc_resp(sdata->dev, bss, (u8 *)mgmt, len, uapsd_queues);
|
2012-01-20 13:55:27 +01:00
|
|
|
}
|
2012-12-10 13:44:19 +01:00
|
|
|
|
2008-09-08 17:44:26 +02:00
|
|
|
static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
|
2012-12-10 13:44:19 +01:00
|
|
|
struct ieee80211_mgmt *mgmt, size_t len,
|
2008-09-08 17:44:26 +02:00
|
|
|
struct ieee80211_rx_status *rx_status,
|
2012-12-10 15:19:13 +01:00
|
|
|
struct ieee802_11_elems *elems)
|
2008-09-08 17:44:26 +02:00
|
|
|
{
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
2008-09-11 00:01:55 +02:00
|
|
|
struct ieee80211_bss *bss;
|
2008-09-08 17:44:26 +02:00
|
|
|
struct ieee80211_channel *channel;
|
2010-01-26 14:19:52 +01:00
|
|
|
|
2013-05-10 12:32:47 +02:00
|
|
|
sdata_assert_lock(sdata);
|
2013-03-26 14:13:58 +01:00
|
|
|
|
2014-03-04 15:50:13 +01:00
|
|
|
channel = ieee80211_get_channel(local->hw.wiphy, rx_status->freq);
|
|
|
|
if (!channel)
|
2008-09-08 17:44:26 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems,
|
2012-12-10 15:19:13 +01:00
|
|
|
channel);
|
2013-05-19 13:23:57 +02:00
|
|
|
if (bss) {
|
|
|
|
sdata->vif.bss_conf.beacon_rate = bss->beacon_rate;
|
2014-03-04 11:43:28 +01:00
|
|
|
ieee80211_rx_bss_put(local, bss);
|
2013-05-19 13:23:57 +02:00
|
|
|
}
|
2007-05-05 20:45:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-08-03 02:04:37 +02:00
|
|
|
static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
|
2009-12-23 13:15:35 +01:00
|
|
|
struct sk_buff *skb)
|
2007-05-05 20:45:53 +02:00
|
|
|
{
|
2009-12-23 13:15:35 +01:00
|
|
|
struct ieee80211_mgmt *mgmt = (void *)skb->data;
|
2009-03-22 20:57:14 +01:00
|
|
|
struct ieee80211_if_managed *ifmgd;
|
2009-12-23 13:15:35 +01:00
|
|
|
struct ieee80211_rx_status *rx_status = (void *) skb->cb;
|
|
|
|
size_t baselen, len = skb->len;
|
2008-06-27 17:54:48 +02:00
|
|
|
struct ieee802_11_elems elems;
|
|
|
|
|
2009-03-22 20:57:14 +01:00
|
|
|
ifmgd = &sdata->u.mgd;
|
|
|
|
|
2013-05-10 12:32:47 +02:00
|
|
|
sdata_assert_lock(sdata);
|
2009-07-07 03:45:17 +02:00
|
|
|
|
mac80211: Convert compare_ether_addr to ether_addr_equal
Use the new bool function ether_addr_equal to add
some clarity and reduce the likelihood for misuse
of compare_ether_addr for sorting.
Done via cocci script:
$ cat compare_ether_addr.cocci
@@
expression a,b;
@@
- !compare_ether_addr(a, b)
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- compare_ether_addr(a, b)
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) == 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) != 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) == 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) != 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !!ether_addr_equal(a, b)
+ ether_addr_equal(a, b)
Signed-off-by: Joe Perches <joe@perches.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-05-08 20:56:52 +02:00
|
|
|
if (!ether_addr_equal(mgmt->da, sdata->vif.addr))
|
2008-08-03 13:32:01 +02:00
|
|
|
return; /* ignore ProbeResp to foreign address */
|
|
|
|
|
2008-06-27 17:54:48 +02:00
|
|
|
baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt;
|
|
|
|
if (baselen > len)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen,
|
2013-03-26 14:54:16 +01:00
|
|
|
false, &elems);
|
2008-06-27 17:54:48 +02:00
|
|
|
|
2012-12-10 15:19:13 +01:00
|
|
|
ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
|
2008-08-09 02:02:19 +02:00
|
|
|
|
2009-07-07 03:45:17 +02:00
|
|
|
if (ifmgd->associated &&
|
mac80211: Convert compare_ether_addr to ether_addr_equal
Use the new bool function ether_addr_equal to add
some clarity and reduce the likelihood for misuse
of compare_ether_addr for sorting.
Done via cocci script:
$ cat compare_ether_addr.cocci
@@
expression a,b;
@@
- !compare_ether_addr(a, b)
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- compare_ether_addr(a, b)
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) == 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) != 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) == 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) != 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !!ether_addr_equal(a, b)
+ ether_addr_equal(a, b)
Signed-off-by: Joe Perches <joe@perches.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-05-08 20:56:52 +02:00
|
|
|
ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid))
|
2010-11-23 03:10:31 +01:00
|
|
|
ieee80211_reset_ap_probe(sdata);
|
2012-01-20 13:55:27 +01:00
|
|
|
|
|
|
|
if (ifmgd->auth_data && !ifmgd->auth_data->bss->proberesp_ies &&
|
mac80211: Convert compare_ether_addr to ether_addr_equal
Use the new bool function ether_addr_equal to add
some clarity and reduce the likelihood for misuse
of compare_ether_addr for sorting.
Done via cocci script:
$ cat compare_ether_addr.cocci
@@
expression a,b;
@@
- !compare_ether_addr(a, b)
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- compare_ether_addr(a, b)
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) == 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) != 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) == 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) != 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !!ether_addr_equal(a, b)
+ ether_addr_equal(a, b)
Signed-off-by: Joe Perches <joe@perches.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-05-08 20:56:52 +02:00
|
|
|
ether_addr_equal(mgmt->bssid, ifmgd->auth_data->bss->bssid)) {
|
2012-01-20 13:55:27 +01:00
|
|
|
/* got probe response, continue with auth */
|
2012-06-22 11:29:50 +02:00
|
|
|
sdata_info(sdata, "direct probe responded\n");
|
2012-01-20 13:55:27 +01:00
|
|
|
ifmgd->auth_data->tries = 0;
|
|
|
|
ifmgd->auth_data->timeout = jiffies;
|
2013-02-13 15:39:57 +01:00
|
|
|
ifmgd->auth_data->timeout_started = true;
|
2013-05-10 12:32:47 +02:00
|
|
|
run_again(sdata, ifmgd->auth_data->timeout);
|
2012-01-20 13:55:27 +01:00
|
|
|
}
|
2007-05-05 20:45:53 +02:00
|
|
|
}
|
|
|
|
|
mac80211: implement beacon filtering in software
Regardless of whether the hardware implements beacon filtering,
there's no need to process all beacons in software all the time
throughout the stack (mac80211 does a lot, then cfg80211, then
in the future possibly userspace).
This patch implements the "best possible" beacon filtering in
mac80211. "Best possible" means that it can look for changes in
all requested information elements, and distinguish vendor IEs
by their OUI.
In the future, we will add nl80211 API for userspace to request
information elements and vendor IE OUIs to watch -- drivers can
then implement the best they can do while software implements
it fully.
It is unclear whether or not this actually saves CPU time, but
the data is all in the cache already so it should be fairly
cheap. The additional _testing_, however, has great benefit;
Without this, and on hardware that doesn't implement beacon
filtering, wrong assumptions about, for example, scan result
updates could quickly creep into code.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-04-16 13:17:26 +02:00
|
|
|
/*
|
|
|
|
* This is the canonical list of information elements we care about,
|
|
|
|
* the filter code also gives us all changes to the Microsoft OUI
|
mac80211: support DTPC IE (from Cisco Client eXtensions)
Linux already supports 802.11h, where the access point can tell the
client to reduce its transmission power. However, 802.11h is only
defined for 5 GHz, where the need for this is much smaller than on
2.4 GHz.
Cisco has their own solution, called DTPC (Dynamic Transmit Power
Control). Cisco APs on a controller sometimes but not always send
802.11h; they always send DTPC, even on 2.4 GHz. This patch adds support
for parsing and honoring the DTPC IE in addition to the 802.11h
element (they do not always contain the same limits, so both must
be honored); the format is not documented, but very simple.
Tested (on top of wireless.git and on 3.16.1) against a Cisco Aironet
1142 joined to a Cisco 2504 WLC, by setting various transmit power
levels for the given access points and observing the results.
The Wireshark 802.11 dissector agrees with the interpretation of the
element, except for negative numbers, which seem to never happen
anyway.
Signed-off-by: Steinar H. Gunderson <sgunderson@bigfoot.com>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
2014-09-03 15:48:37 +02:00
|
|
|
* (00:50:F2) vendor IE which is used for WMM which we need to track,
|
|
|
|
* as well as the DTPC IE (part of the Cisco OUI) used for signaling
|
|
|
|
* changes to requested client power.
|
mac80211: implement beacon filtering in software
Regardless of whether the hardware implements beacon filtering,
there's no need to process all beacons in software all the time
throughout the stack (mac80211 does a lot, then cfg80211, then
in the future possibly userspace).
This patch implements the "best possible" beacon filtering in
mac80211. "Best possible" means that it can look for changes in
all requested information elements, and distinguish vendor IEs
by their OUI.
In the future, we will add nl80211 API for userspace to request
information elements and vendor IE OUIs to watch -- drivers can
then implement the best they can do while software implements
it fully.
It is unclear whether or not this actually saves CPU time, but
the data is all in the cache already so it should be fairly
cheap. The additional _testing_, however, has great benefit;
Without this, and on hardware that doesn't implement beacon
filtering, wrong assumptions about, for example, scan result
updates could quickly creep into code.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-04-16 13:17:26 +02:00
|
|
|
*
|
|
|
|
* We implement beacon filtering in software since that means we can
|
|
|
|
* avoid processing the frame here and in cfg80211, and userspace
|
|
|
|
* will not be able to tell whether the hardware supports it or not.
|
|
|
|
*
|
|
|
|
* XXX: This list needs to be dynamic -- userspace needs to be able to
|
|
|
|
* add items it requires. It also needs to be able to tell us to
|
|
|
|
* look out for other vendor IEs.
|
|
|
|
*/
|
|
|
|
static const u64 care_about_ies =
|
2009-04-22 11:25:43 +02:00
|
|
|
(1ULL << WLAN_EID_COUNTRY) |
|
|
|
|
(1ULL << WLAN_EID_ERP_INFO) |
|
|
|
|
(1ULL << WLAN_EID_CHANNEL_SWITCH) |
|
|
|
|
(1ULL << WLAN_EID_PWR_CONSTRAINT) |
|
|
|
|
(1ULL << WLAN_EID_HT_CAPABILITY) |
|
2015-03-12 07:53:29 +01:00
|
|
|
(1ULL << WLAN_EID_HT_OPERATION) |
|
|
|
|
(1ULL << WLAN_EID_EXT_CHANSWITCH_ANN);
|
mac80211: implement beacon filtering in software
Regardless of whether the hardware implements beacon filtering,
there's no need to process all beacons in software all the time
throughout the stack (mac80211 does a lot, then cfg80211, then
in the future possibly userspace).
This patch implements the "best possible" beacon filtering in
mac80211. "Best possible" means that it can look for changes in
all requested information elements, and distinguish vendor IEs
by their OUI.
In the future, we will add nl80211 API for userspace to request
information elements and vendor IE OUIs to watch -- drivers can
then implement the best they can do while software implements
it fully.
It is unclear whether or not this actually saves CPU time, but
the data is all in the cache already so it should be fairly
cheap. The additional _testing_, however, has great benefit;
Without this, and on hardware that doesn't implement beacon
filtering, wrong assumptions about, for example, scan result
updates could quickly creep into code.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-04-16 13:17:26 +02:00
|
|
|
|
2013-05-10 12:32:47 +02:00
|
|
|
static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct ieee80211_mgmt *mgmt, size_t len,
|
|
|
|
struct ieee80211_rx_status *rx_status)
|
2007-05-05 20:45:53 +02:00
|
|
|
{
|
mac80211: implement beacon filtering in software
Regardless of whether the hardware implements beacon filtering,
there's no need to process all beacons in software all the time
throughout the stack (mac80211 does a lot, then cfg80211, then
in the future possibly userspace).
This patch implements the "best possible" beacon filtering in
mac80211. "Best possible" means that it can look for changes in
all requested information elements, and distinguish vendor IEs
by their OUI.
In the future, we will add nl80211 API for userspace to request
information elements and vendor IE OUIs to watch -- drivers can
then implement the best they can do while software implements
it fully.
It is unclear whether or not this actually saves CPU time, but
the data is all in the cache already so it should be fairly
cheap. The additional _testing_, however, has great benefit;
Without this, and on hardware that doesn't implement beacon
filtering, wrong assumptions about, for example, scan result
updates could quickly creep into code.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-04-16 13:17:26 +02:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2010-03-30 08:28:30 +02:00
|
|
|
struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
|
2007-05-05 20:45:53 +02:00
|
|
|
size_t baselen;
|
|
|
|
struct ieee802_11_elems elems;
|
2008-08-03 02:04:37 +02:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
2012-07-26 17:24:39 +02:00
|
|
|
struct ieee80211_chanctx_conf *chanctx_conf;
|
|
|
|
struct ieee80211_channel *chan;
|
2013-02-07 22:24:55 +01:00
|
|
|
struct sta_info *sta;
|
2007-12-28 14:32:58 +01:00
|
|
|
u32 changed = 0;
|
2012-10-25 10:16:23 +02:00
|
|
|
bool erp_valid;
|
2008-10-08 10:59:33 +02:00
|
|
|
u8 erp_value = 0;
|
mac80211: implement beacon filtering in software
Regardless of whether the hardware implements beacon filtering,
there's no need to process all beacons in software all the time
throughout the stack (mac80211 does a lot, then cfg80211, then
in the future possibly userspace).
This patch implements the "best possible" beacon filtering in
mac80211. "Best possible" means that it can look for changes in
all requested information elements, and distinguish vendor IEs
by their OUI.
In the future, we will add nl80211 API for userspace to request
information elements and vendor IE OUIs to watch -- drivers can
then implement the best they can do while software implements
it fully.
It is unclear whether or not this actually saves CPU time, but
the data is all in the cache already so it should be fairly
cheap. The additional _testing_, however, has great benefit;
Without this, and on hardware that doesn't implement beacon
filtering, wrong assumptions about, for example, scan result
updates could quickly creep into code.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-04-16 13:17:26 +02:00
|
|
|
u32 ncrc;
|
2009-07-07 03:45:17 +02:00
|
|
|
u8 *bssid;
|
2013-05-10 12:32:47 +02:00
|
|
|
u8 deauth_buf[IEEE80211_DEAUTH_FRAME_LEN];
|
2009-07-07 03:45:17 +02:00
|
|
|
|
2013-05-10 12:32:47 +02:00
|
|
|
sdata_assert_lock(sdata);
|
2007-05-05 20:45:53 +02:00
|
|
|
|
2008-06-27 17:54:48 +02:00
|
|
|
/* Process beacon from the current BSS */
|
|
|
|
baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;
|
|
|
|
if (baselen > len)
|
2013-05-10 12:32:47 +02:00
|
|
|
return;
|
2008-06-27 17:54:48 +02:00
|
|
|
|
2012-07-26 17:24:39 +02:00
|
|
|
rcu_read_lock();
|
|
|
|
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
|
|
|
|
if (!chanctx_conf) {
|
|
|
|
rcu_read_unlock();
|
2013-05-10 12:32:47 +02:00
|
|
|
return;
|
2012-07-26 17:24:39 +02:00
|
|
|
}
|
|
|
|
|
2012-11-09 11:39:59 +01:00
|
|
|
if (rx_status->freq != chanctx_conf->def.chan->center_freq) {
|
2012-07-26 17:24:39 +02:00
|
|
|
rcu_read_unlock();
|
2013-05-10 12:32:47 +02:00
|
|
|
return;
|
2012-07-26 17:24:39 +02:00
|
|
|
}
|
2012-11-09 11:39:59 +01:00
|
|
|
chan = chanctx_conf->def.chan;
|
2012-07-26 17:24:39 +02:00
|
|
|
rcu_read_unlock();
|
2007-05-05 20:45:53 +02:00
|
|
|
|
2012-12-12 09:12:24 +01:00
|
|
|
if (ifmgd->assoc_data && ifmgd->assoc_data->need_beacon &&
|
mac80211: Convert compare_ether_addr to ether_addr_equal
Use the new bool function ether_addr_equal to add
some clarity and reduce the likelihood for misuse
of compare_ether_addr for sorting.
Done via cocci script:
$ cat compare_ether_addr.cocci
@@
expression a,b;
@@
- !compare_ether_addr(a, b)
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- compare_ether_addr(a, b)
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) == 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) != 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) == 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) != 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !!ether_addr_equal(a, b)
+ ether_addr_equal(a, b)
Signed-off-by: Joe Perches <joe@perches.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-05-08 20:56:52 +02:00
|
|
|
ether_addr_equal(mgmt->bssid, ifmgd->assoc_data->bss->bssid)) {
|
2012-01-20 13:55:27 +01:00
|
|
|
ieee802_11_parse_elems(mgmt->u.beacon.variable,
|
2013-03-26 14:54:16 +01:00
|
|
|
len - baselen, false, &elems);
|
2009-07-07 03:45:17 +02:00
|
|
|
|
2012-12-10 15:19:13 +01:00
|
|
|
ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
|
2013-06-03 16:29:33 +02:00
|
|
|
if (elems.tim && !elems.parse_error) {
|
|
|
|
const struct ieee80211_tim_ie *tim_ie = elems.tim;
|
|
|
|
ifmgd->dtim_period = tim_ie->dtim_period;
|
|
|
|
}
|
2013-05-16 16:34:17 +02:00
|
|
|
ifmgd->have_beacon = true;
|
2012-12-12 09:12:24 +01:00
|
|
|
ifmgd->assoc_data->need_beacon = false;
|
2015-06-02 21:39:54 +02:00
|
|
|
if (ieee80211_hw_check(&local->hw, TIMING_BEACON_ONLY)) {
|
2013-02-05 17:48:40 +01:00
|
|
|
sdata->vif.bss_conf.sync_tsf =
|
|
|
|
le64_to_cpu(mgmt->u.beacon.timestamp);
|
|
|
|
sdata->vif.bss_conf.sync_device_ts =
|
|
|
|
rx_status->device_timestamp;
|
|
|
|
if (elems.tim)
|
|
|
|
sdata->vif.bss_conf.sync_dtim_count =
|
|
|
|
elems.tim->dtim_count;
|
|
|
|
else
|
|
|
|
sdata->vif.bss_conf.sync_dtim_count = 0;
|
|
|
|
}
|
2012-01-20 13:55:27 +01:00
|
|
|
/* continue assoc process */
|
|
|
|
ifmgd->assoc_data->timeout = jiffies;
|
2013-02-13 15:39:57 +01:00
|
|
|
ifmgd->assoc_data->timeout_started = true;
|
2013-05-10 12:32:47 +02:00
|
|
|
run_again(sdata, ifmgd->assoc_data->timeout);
|
|
|
|
return;
|
2012-01-20 13:55:27 +01:00
|
|
|
}
|
2009-07-07 03:45:17 +02:00
|
|
|
|
2012-01-20 13:55:27 +01:00
|
|
|
if (!ifmgd->associated ||
|
mac80211: Convert compare_ether_addr to ether_addr_equal
Use the new bool function ether_addr_equal to add
some clarity and reduce the likelihood for misuse
of compare_ether_addr for sorting.
Done via cocci script:
$ cat compare_ether_addr.cocci
@@
expression a,b;
@@
- !compare_ether_addr(a, b)
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- compare_ether_addr(a, b)
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) == 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) != 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) == 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) != 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !!ether_addr_equal(a, b)
+ ether_addr_equal(a, b)
Signed-off-by: Joe Perches <joe@perches.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-05-08 20:56:52 +02:00
|
|
|
!ether_addr_equal(mgmt->bssid, ifmgd->associated->bssid))
|
2013-05-10 12:32:47 +02:00
|
|
|
return;
|
2012-01-20 13:55:27 +01:00
|
|
|
bssid = ifmgd->associated->bssid;
|
2007-05-05 20:45:53 +02:00
|
|
|
|
2010-03-30 08:28:30 +02:00
|
|
|
/* Track average RSSI from the Beacon frames of the current AP */
|
|
|
|
ifmgd->last_beacon_signal = rx_status->signal;
|
|
|
|
if (ifmgd->flags & IEEE80211_STA_RESET_SIGNAL_AVE) {
|
|
|
|
ifmgd->flags &= ~IEEE80211_STA_RESET_SIGNAL_AVE;
|
2010-08-27 21:21:13 +02:00
|
|
|
ifmgd->ave_beacon_signal = rx_status->signal * 16;
|
2010-03-30 08:28:30 +02:00
|
|
|
ifmgd->last_cqm_event_signal = 0;
|
2010-08-27 21:22:00 +02:00
|
|
|
ifmgd->count_beacon_signal = 1;
|
2011-07-08 17:46:22 +02:00
|
|
|
ifmgd->last_ave_beacon_signal = 0;
|
2010-03-30 08:28:30 +02:00
|
|
|
} else {
|
|
|
|
ifmgd->ave_beacon_signal =
|
|
|
|
(IEEE80211_SIGNAL_AVE_WEIGHT * rx_status->signal * 16 +
|
|
|
|
(16 - IEEE80211_SIGNAL_AVE_WEIGHT) *
|
|
|
|
ifmgd->ave_beacon_signal) / 16;
|
2010-08-27 21:22:00 +02:00
|
|
|
ifmgd->count_beacon_signal++;
|
2010-03-30 08:28:30 +02:00
|
|
|
}
|
2011-07-08 17:46:22 +02:00
|
|
|
|
|
|
|
if (ifmgd->rssi_min_thold != ifmgd->rssi_max_thold &&
|
|
|
|
ifmgd->count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT) {
|
|
|
|
int sig = ifmgd->ave_beacon_signal;
|
|
|
|
int last_sig = ifmgd->last_ave_beacon_signal;
|
2015-03-16 22:23:34 +01:00
|
|
|
struct ieee80211_event event = {
|
|
|
|
.type = RSSI_EVENT,
|
|
|
|
};
|
2011-07-08 17:46:22 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* if signal crosses either of the boundaries, invoke callback
|
|
|
|
* with appropriate parameters
|
|
|
|
*/
|
|
|
|
if (sig > ifmgd->rssi_max_thold &&
|
|
|
|
(last_sig <= ifmgd->rssi_min_thold || last_sig == 0)) {
|
|
|
|
ifmgd->last_ave_beacon_signal = sig;
|
2015-03-16 22:23:34 +01:00
|
|
|
event.u.rssi.data = RSSI_EVENT_HIGH;
|
|
|
|
drv_event_callback(local, sdata, &event);
|
2011-07-08 17:46:22 +02:00
|
|
|
} else if (sig < ifmgd->rssi_min_thold &&
|
|
|
|
(last_sig >= ifmgd->rssi_max_thold ||
|
|
|
|
last_sig == 0)) {
|
|
|
|
ifmgd->last_ave_beacon_signal = sig;
|
2015-03-16 22:23:34 +01:00
|
|
|
event.u.rssi.data = RSSI_EVENT_LOW;
|
|
|
|
drv_event_callback(local, sdata, &event);
|
2011-07-08 17:46:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-03-30 08:28:30 +02:00
|
|
|
if (bss_conf->cqm_rssi_thold &&
|
2010-08-27 21:22:00 +02:00
|
|
|
ifmgd->count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT &&
|
2012-01-19 09:29:58 +01:00
|
|
|
!(sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI)) {
|
2010-03-30 08:28:30 +02:00
|
|
|
int sig = ifmgd->ave_beacon_signal / 16;
|
|
|
|
int last_event = ifmgd->last_cqm_event_signal;
|
|
|
|
int thold = bss_conf->cqm_rssi_thold;
|
|
|
|
int hyst = bss_conf->cqm_rssi_hyst;
|
|
|
|
if (sig < thold &&
|
|
|
|
(last_event == 0 || sig < last_event - hyst)) {
|
|
|
|
ifmgd->last_cqm_event_signal = sig;
|
|
|
|
ieee80211_cqm_rssi_notify(
|
|
|
|
&sdata->vif,
|
|
|
|
NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
|
|
|
|
GFP_KERNEL);
|
|
|
|
} else if (sig > thold &&
|
|
|
|
(last_event == 0 || sig > last_event + hyst)) {
|
|
|
|
ifmgd->last_cqm_event_signal = sig;
|
|
|
|
ieee80211_cqm_rssi_notify(
|
|
|
|
&sdata->vif,
|
|
|
|
NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
|
|
|
|
GFP_KERNEL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-27 11:36:35 +02:00
|
|
|
if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL) {
|
2012-06-22 11:29:50 +02:00
|
|
|
mlme_dbg_ratelimited(sdata,
|
2013-02-08 22:59:00 +01:00
|
|
|
"cancelling AP probe due to a received beacon\n");
|
2013-08-27 11:36:35 +02:00
|
|
|
ieee80211_reset_ap_probe(sdata);
|
2009-05-14 20:15:36 +02:00
|
|
|
}
|
|
|
|
|
2009-07-10 15:29:03 +02:00
|
|
|
/*
|
|
|
|
* Push the beacon loss detection into the future since
|
|
|
|
* we are processing a beacon from the AP just now.
|
|
|
|
*/
|
2010-09-16 21:12:32 +02:00
|
|
|
ieee80211_sta_reset_beacon_monitor(sdata);
|
2009-07-10 15:29:03 +02:00
|
|
|
|
mac80211: implement beacon filtering in software
Regardless of whether the hardware implements beacon filtering,
there's no need to process all beacons in software all the time
throughout the stack (mac80211 does a lot, then cfg80211, then
in the future possibly userspace).
This patch implements the "best possible" beacon filtering in
mac80211. "Best possible" means that it can look for changes in
all requested information elements, and distinguish vendor IEs
by their OUI.
In the future, we will add nl80211 API for userspace to request
information elements and vendor IE OUIs to watch -- drivers can
then implement the best they can do while software implements
it fully.
It is unclear whether or not this actually saves CPU time, but
the data is all in the cache already so it should be fairly
cheap. The additional _testing_, however, has great benefit;
Without this, and on hardware that doesn't implement beacon
filtering, wrong assumptions about, for example, scan result
updates could quickly creep into code.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-04-16 13:17:26 +02:00
|
|
|
ncrc = crc32_be(0, (void *)&mgmt->u.beacon.beacon_int, 4);
|
|
|
|
ncrc = ieee802_11_parse_elems_crc(mgmt->u.beacon.variable,
|
2013-03-26 14:54:16 +01:00
|
|
|
len - baselen, false, &elems,
|
mac80211: implement beacon filtering in software
Regardless of whether the hardware implements beacon filtering,
there's no need to process all beacons in software all the time
throughout the stack (mac80211 does a lot, then cfg80211, then
in the future possibly userspace).
This patch implements the "best possible" beacon filtering in
mac80211. "Best possible" means that it can look for changes in
all requested information elements, and distinguish vendor IEs
by their OUI.
In the future, we will add nl80211 API for userspace to request
information elements and vendor IE OUIs to watch -- drivers can
then implement the best they can do while software implements
it fully.
It is unclear whether or not this actually saves CPU time, but
the data is all in the cache already so it should be fairly
cheap. The additional _testing_, however, has great benefit;
Without this, and on hardware that doesn't implement beacon
filtering, wrong assumptions about, for example, scan result
updates could quickly creep into code.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-04-16 13:17:26 +02:00
|
|
|
care_about_ies, ncrc);
|
|
|
|
|
2015-06-02 21:39:54 +02:00
|
|
|
if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK)) {
|
2012-10-25 10:16:23 +02:00
|
|
|
bool directed_tim = ieee80211_check_tim(elems.tim,
|
|
|
|
elems.tim_len,
|
|
|
|
ifmgd->aid);
|
2009-02-10 16:09:24 +01:00
|
|
|
if (directed_tim) {
|
2009-02-10 16:09:31 +01:00
|
|
|
if (local->hw.conf.dynamic_ps_timeout > 0) {
|
2012-03-19 14:37:20 +01:00
|
|
|
if (local->hw.conf.flags & IEEE80211_CONF_PS) {
|
|
|
|
local->hw.conf.flags &= ~IEEE80211_CONF_PS;
|
|
|
|
ieee80211_hw_config(local,
|
|
|
|
IEEE80211_CONF_CHANGE_PS);
|
|
|
|
}
|
2009-02-10 16:09:31 +01:00
|
|
|
ieee80211_send_nullfunc(local, sdata, 0);
|
2012-03-15 01:20:36 +01:00
|
|
|
} else if (!local->pspolling && sdata->u.mgd.powersave) {
|
2009-02-10 16:09:31 +01:00
|
|
|
local->pspolling = true;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Here is assumed that the driver will be
|
|
|
|
* able to send ps-poll frame and receive a
|
|
|
|
* response even though power save mode is
|
|
|
|
* enabled, but some drivers might require
|
|
|
|
* to disable power save here. This needs
|
|
|
|
* to be investigated.
|
|
|
|
*/
|
|
|
|
ieee80211_send_pspoll(local, sdata);
|
|
|
|
}
|
2008-12-24 03:39:02 +01:00
|
|
|
}
|
|
|
|
}
|
2008-10-08 10:59:33 +02:00
|
|
|
|
2012-10-29 20:08:01 +01:00
|
|
|
if (sdata->vif.p2p) {
|
2013-03-21 15:47:56 +01:00
|
|
|
struct ieee80211_p2p_noa_attr noa = {};
|
2012-10-29 20:08:01 +01:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = cfg80211_get_p2p_attr(mgmt->u.beacon.variable,
|
|
|
|
len - baselen,
|
|
|
|
IEEE80211_P2P_ATTR_ABSENCE_NOTICE,
|
2013-03-21 15:47:55 +01:00
|
|
|
(u8 *) &noa, sizeof(noa));
|
2013-03-21 15:47:56 +01:00
|
|
|
if (ret >= 2) {
|
|
|
|
if (sdata->u.mgd.p2p_noa_index != noa.index) {
|
|
|
|
/* valid noa_attr and index changed */
|
|
|
|
sdata->u.mgd.p2p_noa_index = noa.index;
|
|
|
|
memcpy(&bss_conf->p2p_noa_attr, &noa, sizeof(noa));
|
|
|
|
changed |= BSS_CHANGED_P2P_PS;
|
|
|
|
/*
|
|
|
|
* make sure we update all information, the CRC
|
|
|
|
* mechanism doesn't look at P2P attributes.
|
|
|
|
*/
|
|
|
|
ifmgd->beacon_crc_valid = false;
|
|
|
|
}
|
|
|
|
} else if (sdata->u.mgd.p2p_noa_index != -1) {
|
|
|
|
/* noa_attr not found and we had valid noa_attr before */
|
|
|
|
sdata->u.mgd.p2p_noa_index = -1;
|
|
|
|
memset(&bss_conf->p2p_noa_attr, 0, sizeof(bss_conf->p2p_noa_attr));
|
2012-10-29 20:08:01 +01:00
|
|
|
changed |= BSS_CHANGED_P2P_PS;
|
|
|
|
ifmgd->beacon_crc_valid = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-08 08:48:39 +02:00
|
|
|
if (ifmgd->csa_waiting_bcn)
|
|
|
|
ieee80211_chswitch_post_beacon(sdata);
|
|
|
|
|
2015-03-01 08:10:00 +01:00
|
|
|
/*
|
|
|
|
* Update beacon timing and dtim count on every beacon appearance. This
|
|
|
|
* will allow the driver to use the most updated values. Do it before
|
|
|
|
* comparing this one with last received beacon.
|
|
|
|
* IMPORTANT: These parameters would possibly be out of sync by the time
|
|
|
|
* the driver will use them. The synchronized view is currently
|
|
|
|
* guaranteed only in certain callbacks.
|
|
|
|
*/
|
2015-06-02 21:39:54 +02:00
|
|
|
if (ieee80211_hw_check(&local->hw, TIMING_BEACON_ONLY)) {
|
2015-03-01 08:10:00 +01:00
|
|
|
sdata->vif.bss_conf.sync_tsf =
|
|
|
|
le64_to_cpu(mgmt->u.beacon.timestamp);
|
|
|
|
sdata->vif.bss_conf.sync_device_ts =
|
|
|
|
rx_status->device_timestamp;
|
|
|
|
if (elems.tim)
|
|
|
|
sdata->vif.bss_conf.sync_dtim_count =
|
|
|
|
elems.tim->dtim_count;
|
|
|
|
else
|
|
|
|
sdata->vif.bss_conf.sync_dtim_count = 0;
|
|
|
|
}
|
|
|
|
|
mac80211: Add validity check for beacon_crc value
On association to an AP, after receiving beacons, the beacon_crc value is set.
The beacon_crc value is not reset in disassociation, but the BSS data may be
expired at a later point. When associating again, it's possible that a
beacon for the AP is not received, resulting in the beacon_ies to remain NULL.
After association, further beacons will not update the beacon data, as the
crc value of the beacon has not changed, and the beacon_crc still holds a
value matching the beacon. The beacon_ies will remain forever null.
One of the results of this is that WLAN power save cannot be entered, the STA
will remain foreven in active mode.
Fix this by adding a validation flag for the beacon_crc, which is cleared on
association.
Signed-off-by: Juuso Oikarinen <juuso.oikarinen@nokia.com>
Acked-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2010-10-01 15:02:31 +02:00
|
|
|
if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid)
|
2013-05-10 12:32:47 +02:00
|
|
|
return;
|
2009-05-19 16:01:43 +02:00
|
|
|
ifmgd->beacon_crc = ncrc;
|
mac80211: Add validity check for beacon_crc value
On association to an AP, after receiving beacons, the beacon_crc value is set.
The beacon_crc value is not reset in disassociation, but the BSS data may be
expired at a later point. When associating again, it's possible that a
beacon for the AP is not received, resulting in the beacon_ies to remain NULL.
After association, further beacons will not update the beacon data, as the
crc value of the beacon has not changed, and the beacon_crc still holds a
value matching the beacon. The beacon_ies will remain forever null.
One of the results of this is that WLAN power save cannot be entered, the STA
will remain foreven in active mode.
Fix this by adding a validation flag for the beacon_crc, which is cleared on
association.
Signed-off-by: Juuso Oikarinen <juuso.oikarinen@nokia.com>
Acked-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2010-10-01 15:02:31 +02:00
|
|
|
ifmgd->beacon_crc_valid = true;
|
2009-05-19 16:01:43 +02:00
|
|
|
|
2012-12-10 15:19:13 +01:00
|
|
|
ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
|
2012-07-06 17:37:43 +02:00
|
|
|
|
2013-08-23 15:48:48 +02:00
|
|
|
ieee80211_sta_process_chanswitch(sdata, rx_status->mactime,
|
2014-10-08 08:48:35 +02:00
|
|
|
rx_status->device_timestamp,
|
2013-08-23 15:48:48 +02:00
|
|
|
&elems, true);
|
|
|
|
|
2013-10-15 12:25:07 +02:00
|
|
|
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM) &&
|
|
|
|
ieee80211_sta_wmm_params(local, sdata, elems.wmm_param,
|
2012-07-06 17:37:43 +02:00
|
|
|
elems.wmm_param_len))
|
|
|
|
changed |= BSS_CHANGED_QOS;
|
|
|
|
|
2012-12-12 09:12:24 +01:00
|
|
|
/*
|
|
|
|
* If we haven't had a beacon before, tell the driver about the
|
2013-02-05 17:48:40 +01:00
|
|
|
* DTIM period (and beacon timing if desired) now.
|
2012-12-12 09:12:24 +01:00
|
|
|
*/
|
2013-05-16 16:34:17 +02:00
|
|
|
if (!ifmgd->have_beacon) {
|
2012-12-12 09:12:24 +01:00
|
|
|
/* a few bogus AP send dtim_period = 0 or no TIM IE */
|
|
|
|
if (elems.tim)
|
|
|
|
bss_conf->dtim_period = elems.tim->dtim_period ?: 1;
|
|
|
|
else
|
|
|
|
bss_conf->dtim_period = 1;
|
2013-02-05 17:48:40 +01:00
|
|
|
|
2013-05-16 16:34:17 +02:00
|
|
|
changed |= BSS_CHANGED_BEACON_INFO;
|
|
|
|
ifmgd->have_beacon = true;
|
2013-06-03 16:29:33 +02:00
|
|
|
|
|
|
|
mutex_lock(&local->iflist_mtx);
|
|
|
|
ieee80211_recalc_ps(local, -1);
|
|
|
|
mutex_unlock(&local->iflist_mtx);
|
|
|
|
|
2013-05-06 16:17:04 +02:00
|
|
|
ieee80211_recalc_ps_vif(sdata);
|
2012-12-12 09:12:24 +01:00
|
|
|
}
|
|
|
|
|
2013-03-27 14:31:53 +01:00
|
|
|
if (elems.erp_info) {
|
2008-10-08 10:59:33 +02:00
|
|
|
erp_valid = true;
|
|
|
|
erp_value = elems.erp_info[0];
|
|
|
|
} else {
|
|
|
|
erp_valid = false;
|
2008-04-15 20:09:27 +02:00
|
|
|
}
|
2008-10-08 10:59:33 +02:00
|
|
|
changed |= ieee80211_handle_bss_capability(sdata,
|
|
|
|
le16_to_cpu(mgmt->u.beacon.capab_info),
|
|
|
|
erp_valid, erp_value);
|
2007-05-05 20:45:53 +02:00
|
|
|
|
2013-02-07 17:36:12 +01:00
|
|
|
mutex_lock(&local->sta_mtx);
|
2013-02-07 22:24:55 +01:00
|
|
|
sta = sta_info_get(sdata, bssid);
|
|
|
|
|
2014-07-24 10:20:05 +02:00
|
|
|
if (ieee80211_config_bw(sdata, sta,
|
|
|
|
elems.ht_cap_elem, elems.ht_operation,
|
2013-02-08 15:12:14 +01:00
|
|
|
elems.vht_operation, bssid, &changed)) {
|
|
|
|
mutex_unlock(&local->sta_mtx);
|
|
|
|
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
|
|
|
|
WLAN_REASON_DEAUTH_LEAVING,
|
|
|
|
true, deauth_buf);
|
2015-03-16 22:23:37 +01:00
|
|
|
ieee80211_report_disconnect(sdata, deauth_buf,
|
|
|
|
sizeof(deauth_buf), true,
|
|
|
|
WLAN_REASON_DEAUTH_LEAVING);
|
2013-05-10 12:32:47 +02:00
|
|
|
return;
|
2013-02-08 15:12:14 +01:00
|
|
|
}
|
2013-02-07 22:24:55 +01:00
|
|
|
|
|
|
|
if (sta && elems.opmode_notif)
|
|
|
|
ieee80211_vht_handle_opmode(sdata, sta, *elems.opmode_notif,
|
|
|
|
rx_status->band, true);
|
2013-02-07 17:36:12 +01:00
|
|
|
mutex_unlock(&local->sta_mtx);
|
2007-11-26 15:14:34 +01:00
|
|
|
|
2014-09-03 15:22:10 +02:00
|
|
|
changed |= ieee80211_handle_pwr_constr(sdata, chan, mgmt,
|
|
|
|
elems.country_elem,
|
|
|
|
elems.country_elem_len,
|
mac80211: support DTPC IE (from Cisco Client eXtensions)
Linux already supports 802.11h, where the access point can tell the
client to reduce its transmission power. However, 802.11h is only
defined for 5 GHz, where the need for this is much smaller than on
2.4 GHz.
Cisco has their own solution, called DTPC (Dynamic Transmit Power
Control). Cisco APs on a controller sometimes but not always send
802.11h; they always send DTPC, even on 2.4 GHz. This patch adds support
for parsing and honoring the DTPC IE in addition to the 802.11h
element (they do not always contain the same limits, so both must
be honored); the format is not documented, but very simple.
Tested (on top of wireless.git and on 3.16.1) against a Cisco Aironet
1142 joined to a Cisco 2504 WLC, by setting various transmit power
levels for the given access points and observing the results.
The Wireshark 802.11 dissector agrees with the interpretation of the
element, except for negative numbers, which seem to never happen
anyway.
Signed-off-by: Steinar H. Gunderson <sgunderson@bigfoot.com>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
2014-09-03 15:48:37 +02:00
|
|
|
elems.pwr_constr_elem,
|
|
|
|
elems.cisco_dtpc_elem);
|
2008-11-12 23:22:02 +01:00
|
|
|
|
2007-12-28 14:32:58 +01:00
|
|
|
ieee80211_bss_info_change_notify(sdata, changed);
|
2007-05-05 20:45:53 +02:00
|
|
|
}
|
|
|
|
|
2010-06-10 10:21:32 +02:00
|
|
|
void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct sk_buff *skb)
|
2007-05-05 20:45:53 +02:00
|
|
|
{
|
|
|
|
struct ieee80211_rx_status *rx_status;
|
|
|
|
struct ieee80211_mgmt *mgmt;
|
|
|
|
u16 fc;
|
2013-03-26 15:17:18 +01:00
|
|
|
struct ieee802_11_elems elems;
|
|
|
|
int ies_len;
|
2007-05-05 20:45:53 +02:00
|
|
|
|
|
|
|
rx_status = (struct ieee80211_rx_status *) skb->cb;
|
|
|
|
mgmt = (struct ieee80211_mgmt *) skb->data;
|
|
|
|
fc = le16_to_cpu(mgmt->frame_control);
|
|
|
|
|
2013-05-10 12:32:47 +02:00
|
|
|
sdata_lock(sdata);
|
2009-07-07 03:45:17 +02:00
|
|
|
|
2012-01-20 13:55:27 +01:00
|
|
|
switch (fc & IEEE80211_FCTL_STYPE) {
|
|
|
|
case IEEE80211_STYPE_BEACON:
|
2013-05-10 12:32:47 +02:00
|
|
|
ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len, rx_status);
|
2012-01-20 13:55:27 +01:00
|
|
|
break;
|
|
|
|
case IEEE80211_STYPE_PROBE_RESP:
|
|
|
|
ieee80211_rx_mgmt_probe_resp(sdata, skb);
|
|
|
|
break;
|
|
|
|
case IEEE80211_STYPE_AUTH:
|
2013-05-10 12:32:47 +02:00
|
|
|
ieee80211_rx_mgmt_auth(sdata, mgmt, skb->len);
|
2012-01-20 13:55:27 +01:00
|
|
|
break;
|
|
|
|
case IEEE80211_STYPE_DEAUTH:
|
2013-05-10 12:32:47 +02:00
|
|
|
ieee80211_rx_mgmt_deauth(sdata, mgmt, skb->len);
|
2012-01-20 13:55:27 +01:00
|
|
|
break;
|
|
|
|
case IEEE80211_STYPE_DISASSOC:
|
2013-05-10 12:32:47 +02:00
|
|
|
ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len);
|
2012-01-20 13:55:27 +01:00
|
|
|
break;
|
|
|
|
case IEEE80211_STYPE_ASSOC_RESP:
|
|
|
|
case IEEE80211_STYPE_REASSOC_RESP:
|
2013-05-10 12:32:47 +02:00
|
|
|
ieee80211_rx_mgmt_assoc_resp(sdata, mgmt, skb->len);
|
2012-01-20 13:55:27 +01:00
|
|
|
break;
|
|
|
|
case IEEE80211_STYPE_ACTION:
|
2013-03-26 14:02:26 +01:00
|
|
|
if (mgmt->u.action.category == WLAN_CATEGORY_SPECTRUM_MGMT) {
|
2013-03-26 15:17:18 +01:00
|
|
|
ies_len = skb->len -
|
|
|
|
offsetof(struct ieee80211_mgmt,
|
|
|
|
u.action.u.chan_switch.variable);
|
2013-03-26 14:02:26 +01:00
|
|
|
|
|
|
|
if (ies_len < 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
ieee802_11_parse_elems(
|
|
|
|
mgmt->u.action.u.chan_switch.variable,
|
2013-03-26 14:54:16 +01:00
|
|
|
ies_len, true, &elems);
|
2013-03-26 14:02:26 +01:00
|
|
|
|
|
|
|
if (elems.parse_error)
|
|
|
|
break;
|
|
|
|
|
2013-03-26 15:17:18 +01:00
|
|
|
ieee80211_sta_process_chanswitch(sdata,
|
2014-10-08 08:48:35 +02:00
|
|
|
rx_status->mactime,
|
|
|
|
rx_status->device_timestamp,
|
|
|
|
&elems, false);
|
2013-03-26 15:17:18 +01:00
|
|
|
} else if (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) {
|
|
|
|
ies_len = skb->len -
|
|
|
|
offsetof(struct ieee80211_mgmt,
|
|
|
|
u.action.u.ext_chan_switch.variable);
|
|
|
|
|
|
|
|
if (ies_len < 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
ieee802_11_parse_elems(
|
|
|
|
mgmt->u.action.u.ext_chan_switch.variable,
|
2013-03-26 14:54:16 +01:00
|
|
|
ies_len, true, &elems);
|
2013-03-26 15:17:18 +01:00
|
|
|
|
|
|
|
if (elems.parse_error)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* for the handling code pretend this was also an IE */
|
|
|
|
elems.ext_chansw_ie =
|
|
|
|
&mgmt->u.action.u.ext_chan_switch.data;
|
|
|
|
|
2012-01-20 13:55:27 +01:00
|
|
|
ieee80211_sta_process_chanswitch(sdata,
|
2014-10-08 08:48:35 +02:00
|
|
|
rx_status->mactime,
|
|
|
|
rx_status->device_timestamp,
|
|
|
|
&elems, false);
|
2009-07-07 03:45:17 +02:00
|
|
|
}
|
2013-03-26 14:02:26 +01:00
|
|
|
break;
|
2009-07-07 03:45:17 +02:00
|
|
|
}
|
2013-05-10 12:32:47 +02:00
|
|
|
sdata_unlock(sdata);
|
2007-05-05 20:45:53 +02:00
|
|
|
}
|
|
|
|
|
2008-09-11 00:01:52 +02:00
|
|
|
static void ieee80211_sta_timer(unsigned long data)
|
2007-05-05 20:45:53 +02:00
|
|
|
{
|
2008-09-08 17:44:22 +02:00
|
|
|
struct ieee80211_sub_if_data *sdata =
|
|
|
|
(struct ieee80211_sub_if_data *) data;
|
2009-05-17 11:40:42 +02:00
|
|
|
|
2013-02-28 10:55:27 +01:00
|
|
|
ieee80211_queue_work(&sdata->local->hw, &sdata->work);
|
2007-05-05 20:45:53 +02:00
|
|
|
}
|
|
|
|
|
2010-12-02 21:01:08 +01:00
|
|
|
static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
|
2013-01-29 11:35:29 +01:00
|
|
|
u8 *bssid, u8 reason, bool tx)
|
2010-12-02 21:01:08 +01:00
|
|
|
{
|
2012-09-07 13:28:52 +02:00
|
|
|
u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
|
2010-12-02 21:01:08 +01:00
|
|
|
|
2012-02-24 13:50:54 +01:00
|
|
|
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, reason,
|
2013-01-29 11:35:29 +01:00
|
|
|
tx, frame_buf);
|
2012-02-24 13:50:54 +01:00
|
|
|
|
2015-03-16 22:23:37 +01:00
|
|
|
ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true,
|
|
|
|
reason);
|
2010-12-02 21:01:08 +01:00
|
|
|
}
|
|
|
|
|
2012-01-20 13:55:27 +01:00
|
|
|
static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
struct ieee80211_mgd_auth_data *auth_data = ifmgd->auth_data;
|
2013-01-29 15:02:27 +01:00
|
|
|
u32 tx_flags = 0;
|
2012-01-20 13:55:27 +01:00
|
|
|
|
2013-05-10 12:32:47 +02:00
|
|
|
sdata_assert_lock(sdata);
|
2012-01-20 13:55:27 +01:00
|
|
|
|
|
|
|
if (WARN_ON_ONCE(!auth_data))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
auth_data->tries++;
|
|
|
|
|
|
|
|
if (auth_data->tries > IEEE80211_AUTH_MAX_TRIES) {
|
2012-06-22 11:29:50 +02:00
|
|
|
sdata_info(sdata, "authentication with %pM timed out\n",
|
|
|
|
auth_data->bss->bssid);
|
2012-01-20 13:55:27 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Most likely AP is not in the range so remove the
|
|
|
|
* bss struct for that AP.
|
|
|
|
*/
|
|
|
|
cfg80211_unlink_bss(local->hw.wiphy, auth_data->bss);
|
|
|
|
|
|
|
|
return -ETIMEDOUT;
|
|
|
|
}
|
|
|
|
|
2012-06-27 13:18:36 +02:00
|
|
|
drv_mgd_prepare_tx(local, sdata);
|
|
|
|
|
2012-01-20 13:55:27 +01:00
|
|
|
if (auth_data->bss->proberesp_ies) {
|
2012-09-30 18:29:40 +02:00
|
|
|
u16 trans = 1;
|
|
|
|
u16 status = 0;
|
|
|
|
|
2012-06-22 11:29:50 +02:00
|
|
|
sdata_info(sdata, "send auth to %pM (try %d/%d)\n",
|
|
|
|
auth_data->bss->bssid, auth_data->tries,
|
|
|
|
IEEE80211_AUTH_MAX_TRIES);
|
2012-01-20 13:55:27 +01:00
|
|
|
|
|
|
|
auth_data->expected_transaction = 2;
|
2012-09-30 18:29:40 +02:00
|
|
|
|
|
|
|
if (auth_data->algorithm == WLAN_AUTH_SAE) {
|
|
|
|
trans = auth_data->sae_trans;
|
|
|
|
status = auth_data->sae_status;
|
|
|
|
auth_data->expected_transaction = trans;
|
|
|
|
}
|
|
|
|
|
2015-06-02 21:39:54 +02:00
|
|
|
if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS))
|
2013-05-17 13:43:04 +02:00
|
|
|
tx_flags = IEEE80211_TX_CTL_REQ_TX_STATUS |
|
|
|
|
IEEE80211_TX_INTFL_MLME_CONN_TX;
|
|
|
|
|
2012-09-30 18:29:40 +02:00
|
|
|
ieee80211_send_auth(sdata, trans, auth_data->algorithm, status,
|
|
|
|
auth_data->data, auth_data->data_len,
|
2012-01-20 13:55:27 +01:00
|
|
|
auth_data->bss->bssid,
|
2013-01-29 15:02:27 +01:00
|
|
|
auth_data->bss->bssid, NULL, 0, 0,
|
|
|
|
tx_flags);
|
2012-01-20 13:55:27 +01:00
|
|
|
} else {
|
|
|
|
const u8 *ssidie;
|
|
|
|
|
2012-06-22 11:29:50 +02:00
|
|
|
sdata_info(sdata, "direct probe to %pM (try %d/%i)\n",
|
|
|
|
auth_data->bss->bssid, auth_data->tries,
|
|
|
|
IEEE80211_AUTH_MAX_TRIES);
|
2012-01-20 13:55:27 +01:00
|
|
|
|
2012-11-29 01:25:20 +01:00
|
|
|
rcu_read_lock();
|
2012-01-20 13:55:27 +01:00
|
|
|
ssidie = ieee80211_bss_get_ie(auth_data->bss, WLAN_EID_SSID);
|
2012-11-29 01:25:20 +01:00
|
|
|
if (!ssidie) {
|
|
|
|
rcu_read_unlock();
|
2012-01-20 13:55:27 +01:00
|
|
|
return -EINVAL;
|
2012-11-29 01:25:20 +01:00
|
|
|
}
|
2012-01-20 13:55:27 +01:00
|
|
|
/*
|
|
|
|
* Direct probe is sent to broadcast address as some APs
|
|
|
|
* will not answer to direct packet in unassociated state.
|
|
|
|
*/
|
2014-06-12 22:24:31 +02:00
|
|
|
ieee80211_send_probe_req(sdata, sdata->vif.addr, NULL,
|
|
|
|
ssidie + 2, ssidie[1],
|
2013-05-17 13:43:04 +02:00
|
|
|
NULL, 0, (u32) -1, true, 0,
|
2012-07-26 17:24:39 +02:00
|
|
|
auth_data->bss->channel, false);
|
2012-11-29 01:25:20 +01:00
|
|
|
rcu_read_unlock();
|
2012-01-20 13:55:27 +01:00
|
|
|
}
|
|
|
|
|
2013-05-17 13:43:04 +02:00
|
|
|
if (tx_flags == 0) {
|
2013-01-29 15:02:27 +01:00
|
|
|
auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
|
2013-07-29 23:07:43 +02:00
|
|
|
auth_data->timeout_started = true;
|
2013-05-10 12:32:47 +02:00
|
|
|
run_again(sdata, auth_data->timeout);
|
2013-02-13 15:39:57 +01:00
|
|
|
} else {
|
2013-07-29 23:07:43 +02:00
|
|
|
auth_data->timeout =
|
|
|
|
round_jiffies_up(jiffies + IEEE80211_AUTH_TIMEOUT_LONG);
|
|
|
|
auth_data->timeout_started = true;
|
|
|
|
run_again(sdata, auth_data->timeout);
|
2013-01-29 15:02:27 +01:00
|
|
|
}
|
2012-01-20 13:55:27 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ieee80211_do_assoc(struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
|
|
|
struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data;
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
|
2013-05-10 12:32:47 +02:00
|
|
|
sdata_assert_lock(sdata);
|
2012-01-20 13:55:27 +01:00
|
|
|
|
|
|
|
assoc_data->tries++;
|
|
|
|
if (assoc_data->tries > IEEE80211_ASSOC_MAX_TRIES) {
|
2012-06-22 11:29:50 +02:00
|
|
|
sdata_info(sdata, "association with %pM timed out\n",
|
|
|
|
assoc_data->bss->bssid);
|
2012-01-20 13:55:27 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Most likely AP is not in the range so remove the
|
|
|
|
* bss struct for that AP.
|
|
|
|
*/
|
|
|
|
cfg80211_unlink_bss(local->hw.wiphy, assoc_data->bss);
|
|
|
|
|
|
|
|
return -ETIMEDOUT;
|
|
|
|
}
|
|
|
|
|
2012-06-22 11:29:50 +02:00
|
|
|
sdata_info(sdata, "associate with %pM (try %d/%d)\n",
|
|
|
|
assoc_data->bss->bssid, assoc_data->tries,
|
|
|
|
IEEE80211_ASSOC_MAX_TRIES);
|
2012-01-20 13:55:27 +01:00
|
|
|
ieee80211_send_assoc(sdata);
|
|
|
|
|
2015-06-02 21:39:54 +02:00
|
|
|
if (!ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) {
|
2013-01-29 15:02:27 +01:00
|
|
|
assoc_data->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT;
|
2013-02-13 15:39:57 +01:00
|
|
|
assoc_data->timeout_started = true;
|
2013-05-10 12:32:47 +02:00
|
|
|
run_again(sdata, assoc_data->timeout);
|
2013-02-13 15:39:57 +01:00
|
|
|
} else {
|
2013-07-29 23:07:43 +02:00
|
|
|
assoc_data->timeout =
|
|
|
|
round_jiffies_up(jiffies +
|
|
|
|
IEEE80211_ASSOC_TIMEOUT_LONG);
|
|
|
|
assoc_data->timeout_started = true;
|
|
|
|
run_again(sdata, assoc_data->timeout);
|
2013-01-29 15:02:27 +01:00
|
|
|
}
|
2012-01-20 13:55:27 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-01-29 15:02:27 +01:00
|
|
|
void ieee80211_mgd_conn_tx_status(struct ieee80211_sub_if_data *sdata,
|
|
|
|
__le16 fc, bool acked)
|
|
|
|
{
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
|
|
|
|
sdata->u.mgd.status_fc = fc;
|
|
|
|
sdata->u.mgd.status_acked = acked;
|
|
|
|
sdata->u.mgd.status_received = true;
|
|
|
|
|
|
|
|
ieee80211_queue_work(&local->hw, &sdata->work);
|
|
|
|
}
|
|
|
|
|
2010-06-10 10:21:32 +02:00
|
|
|
void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
|
2008-09-11 00:01:52 +02:00
|
|
|
{
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
2010-06-10 10:21:32 +02:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2008-09-11 00:01:52 +02:00
|
|
|
|
2013-05-10 12:32:47 +02:00
|
|
|
sdata_lock(sdata);
|
2009-07-07 03:45:17 +02:00
|
|
|
|
2013-01-29 15:02:27 +01:00
|
|
|
if (ifmgd->status_received) {
|
|
|
|
__le16 fc = ifmgd->status_fc;
|
|
|
|
bool status_acked = ifmgd->status_acked;
|
|
|
|
|
|
|
|
ifmgd->status_received = false;
|
|
|
|
if (ifmgd->auth_data &&
|
|
|
|
(ieee80211_is_probe_req(fc) || ieee80211_is_auth(fc))) {
|
|
|
|
if (status_acked) {
|
|
|
|
ifmgd->auth_data->timeout =
|
|
|
|
jiffies + IEEE80211_AUTH_TIMEOUT_SHORT;
|
2013-05-10 12:32:47 +02:00
|
|
|
run_again(sdata, ifmgd->auth_data->timeout);
|
2013-01-29 15:02:27 +01:00
|
|
|
} else {
|
|
|
|
ifmgd->auth_data->timeout = jiffies - 1;
|
|
|
|
}
|
2013-02-13 15:39:57 +01:00
|
|
|
ifmgd->auth_data->timeout_started = true;
|
2013-01-29 15:02:27 +01:00
|
|
|
} else if (ifmgd->assoc_data &&
|
|
|
|
(ieee80211_is_assoc_req(fc) ||
|
|
|
|
ieee80211_is_reassoc_req(fc))) {
|
|
|
|
if (status_acked) {
|
|
|
|
ifmgd->assoc_data->timeout =
|
|
|
|
jiffies + IEEE80211_ASSOC_TIMEOUT_SHORT;
|
2013-05-10 12:32:47 +02:00
|
|
|
run_again(sdata, ifmgd->assoc_data->timeout);
|
2013-01-29 15:02:27 +01:00
|
|
|
} else {
|
|
|
|
ifmgd->assoc_data->timeout = jiffies - 1;
|
|
|
|
}
|
2013-02-13 15:39:57 +01:00
|
|
|
ifmgd->assoc_data->timeout_started = true;
|
2013-01-29 15:02:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-13 15:39:57 +01:00
|
|
|
if (ifmgd->auth_data && ifmgd->auth_data->timeout_started &&
|
2012-01-20 13:55:27 +01:00
|
|
|
time_after(jiffies, ifmgd->auth_data->timeout)) {
|
|
|
|
if (ifmgd->auth_data->done) {
|
|
|
|
/*
|
|
|
|
* ok ... we waited for assoc but userspace didn't,
|
|
|
|
* so let's just kill the auth data
|
|
|
|
*/
|
|
|
|
ieee80211_destroy_auth_data(sdata, false);
|
|
|
|
} else if (ieee80211_probe_auth(sdata)) {
|
|
|
|
u8 bssid[ETH_ALEN];
|
2015-03-16 22:23:35 +01:00
|
|
|
struct ieee80211_event event = {
|
|
|
|
.type = MLME_EVENT,
|
|
|
|
.u.mlme.data = AUTH_EVENT,
|
|
|
|
.u.mlme.status = MLME_TIMEOUT,
|
|
|
|
};
|
2012-01-20 13:55:27 +01:00
|
|
|
|
|
|
|
memcpy(bssid, ifmgd->auth_data->bss->bssid, ETH_ALEN);
|
|
|
|
|
|
|
|
ieee80211_destroy_auth_data(sdata, false);
|
|
|
|
|
2013-05-16 00:55:00 +02:00
|
|
|
cfg80211_auth_timeout(sdata->dev, bssid);
|
2015-03-16 22:23:35 +01:00
|
|
|
drv_event_callback(sdata->local, sdata, &event);
|
2012-01-20 13:55:27 +01:00
|
|
|
}
|
2013-02-13 15:39:57 +01:00
|
|
|
} else if (ifmgd->auth_data && ifmgd->auth_data->timeout_started)
|
2013-05-10 12:32:47 +02:00
|
|
|
run_again(sdata, ifmgd->auth_data->timeout);
|
2012-01-20 13:55:27 +01:00
|
|
|
|
2013-02-13 15:39:57 +01:00
|
|
|
if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started &&
|
2012-01-20 13:55:27 +01:00
|
|
|
time_after(jiffies, ifmgd->assoc_data->timeout)) {
|
2013-05-16 16:34:17 +02:00
|
|
|
if ((ifmgd->assoc_data->need_beacon && !ifmgd->have_beacon) ||
|
2012-01-20 13:55:27 +01:00
|
|
|
ieee80211_do_assoc(sdata)) {
|
2013-06-19 13:05:42 +02:00
|
|
|
struct cfg80211_bss *bss = ifmgd->assoc_data->bss;
|
2015-03-16 22:23:36 +01:00
|
|
|
struct ieee80211_event event = {
|
|
|
|
.type = MLME_EVENT,
|
|
|
|
.u.mlme.data = ASSOC_EVENT,
|
|
|
|
.u.mlme.status = MLME_TIMEOUT,
|
|
|
|
};
|
2012-01-20 13:55:27 +01:00
|
|
|
|
|
|
|
ieee80211_destroy_assoc_data(sdata, false);
|
2013-06-19 13:05:42 +02:00
|
|
|
cfg80211_assoc_timeout(sdata->dev, bss);
|
2015-03-16 22:23:36 +01:00
|
|
|
drv_event_callback(sdata->local, sdata, &event);
|
2012-01-20 13:55:27 +01:00
|
|
|
}
|
2013-02-13 15:39:57 +01:00
|
|
|
} else if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started)
|
2013-05-10 12:32:47 +02:00
|
|
|
run_again(sdata, ifmgd->assoc_data->timeout);
|
2012-01-20 13:55:27 +01:00
|
|
|
|
2013-08-27 11:36:35 +02:00
|
|
|
if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL &&
|
2009-07-10 15:29:03 +02:00
|
|
|
ifmgd->associated) {
|
2009-07-31 17:54:12 +02:00
|
|
|
u8 bssid[ETH_ALEN];
|
2010-11-23 03:10:32 +01:00
|
|
|
int max_tries;
|
2009-07-31 17:54:12 +02:00
|
|
|
|
2009-12-23 13:15:39 +01:00
|
|
|
memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN);
|
2010-11-23 03:10:31 +01:00
|
|
|
|
2015-06-02 21:39:54 +02:00
|
|
|
if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS))
|
2011-02-05 00:30:24 +01:00
|
|
|
max_tries = max_nullfunc_tries;
|
2010-11-23 03:10:32 +01:00
|
|
|
else
|
2011-02-05 00:30:24 +01:00
|
|
|
max_tries = max_probe_tries;
|
2010-11-23 03:10:32 +01:00
|
|
|
|
2010-11-23 03:10:31 +01:00
|
|
|
/* ACK received for nullfunc probing frame */
|
|
|
|
if (!ifmgd->probe_send_count)
|
|
|
|
ieee80211_reset_ap_probe(sdata);
|
2010-12-02 21:01:08 +01:00
|
|
|
else if (ifmgd->nullfunc_failed) {
|
|
|
|
if (ifmgd->probe_send_count < max_tries) {
|
2012-06-22 11:29:50 +02:00
|
|
|
mlme_dbg(sdata,
|
|
|
|
"No ack for nullfunc frame to AP %pM, try %d/%i\n",
|
|
|
|
bssid, ifmgd->probe_send_count,
|
|
|
|
max_tries);
|
2010-12-02 21:01:08 +01:00
|
|
|
ieee80211_mgd_probe_ap_send(sdata);
|
|
|
|
} else {
|
2012-06-22 11:29:50 +02:00
|
|
|
mlme_dbg(sdata,
|
|
|
|
"No ack for nullfunc frame to AP %pM, disconnecting.\n",
|
|
|
|
bssid);
|
2011-07-12 12:30:59 +02:00
|
|
|
ieee80211_sta_connection_lost(sdata, bssid,
|
2013-01-29 11:35:29 +01:00
|
|
|
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
|
|
|
|
false);
|
2010-12-02 21:01:08 +01:00
|
|
|
}
|
|
|
|
} else if (time_is_after_jiffies(ifmgd->probe_timeout))
|
2013-05-10 12:32:47 +02:00
|
|
|
run_again(sdata, ifmgd->probe_timeout);
|
2015-06-02 21:39:54 +02:00
|
|
|
else if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) {
|
2012-06-22 11:29:50 +02:00
|
|
|
mlme_dbg(sdata,
|
|
|
|
"Failed to send nullfunc to AP %pM after %dms, disconnecting\n",
|
|
|
|
bssid, probe_wait_ms);
|
2011-07-12 12:30:59 +02:00
|
|
|
ieee80211_sta_connection_lost(sdata, bssid,
|
2013-01-29 11:35:29 +01:00
|
|
|
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, false);
|
2010-12-02 21:01:08 +01:00
|
|
|
} else if (ifmgd->probe_send_count < max_tries) {
|
2012-06-22 11:29:50 +02:00
|
|
|
mlme_dbg(sdata,
|
|
|
|
"No probe response from AP %pM after %dms, try %d/%i\n",
|
|
|
|
bssid, probe_wait_ms,
|
|
|
|
ifmgd->probe_send_count, max_tries);
|
2009-07-31 17:54:12 +02:00
|
|
|
ieee80211_mgd_probe_ap_send(sdata);
|
|
|
|
} else {
|
2009-07-10 15:29:03 +02:00
|
|
|
/*
|
|
|
|
* We actually lost the connection ... or did we?
|
|
|
|
* Let's make sure!
|
|
|
|
*/
|
2010-10-08 01:12:06 +02:00
|
|
|
wiphy_debug(local->hw.wiphy,
|
|
|
|
"%s: No probe response from AP %pM"
|
|
|
|
" after %dms, disconnecting.\n",
|
|
|
|
sdata->name,
|
2011-02-05 00:30:24 +01:00
|
|
|
bssid, probe_wait_ms);
|
2010-12-02 21:01:08 +01:00
|
|
|
|
2011-07-12 12:30:59 +02:00
|
|
|
ieee80211_sta_connection_lost(sdata, bssid,
|
2013-01-29 11:35:29 +01:00
|
|
|
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, false);
|
2009-07-10 15:29:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-10 12:32:47 +02:00
|
|
|
sdata_unlock(sdata);
|
2007-05-05 20:45:53 +02:00
|
|
|
}
|
|
|
|
|
2009-07-10 15:29:03 +02:00
|
|
|
static void ieee80211_sta_bcn_mon_timer(unsigned long data)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata =
|
|
|
|
(struct ieee80211_sub_if_data *) data;
|
2014-10-08 08:48:39 +02:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2009-07-10 15:29:03 +02:00
|
|
|
|
2014-10-08 08:48:39 +02:00
|
|
|
if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn)
|
2014-04-09 15:45:36 +02:00
|
|
|
return;
|
|
|
|
|
2013-01-29 13:13:50 +01:00
|
|
|
sdata->u.mgd.connection_loss = false;
|
2010-03-19 06:14:53 +01:00
|
|
|
ieee80211_queue_work(&sdata->local->hw,
|
|
|
|
&sdata->u.mgd.beacon_connection_loss_work);
|
2009-07-10 15:29:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ieee80211_sta_conn_mon_timer(unsigned long data)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata =
|
|
|
|
(struct ieee80211_sub_if_data *) data;
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
|
2014-10-08 08:48:39 +02:00
|
|
|
if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn)
|
2014-04-09 15:45:36 +02:00
|
|
|
return;
|
|
|
|
|
2009-07-30 02:08:07 +02:00
|
|
|
ieee80211_queue_work(&local->hw, &ifmgd->monitor_work);
|
2009-07-10 15:29:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ieee80211_sta_monitor_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata =
|
|
|
|
container_of(work, struct ieee80211_sub_if_data,
|
|
|
|
u.mgd.monitor_work);
|
|
|
|
|
|
|
|
ieee80211_mgd_probe_ap(sdata, false);
|
|
|
|
}
|
|
|
|
|
2008-09-11 00:01:52 +02:00
|
|
|
static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
2009-04-19 07:47:19 +02:00
|
|
|
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
|
2012-05-16 15:27:20 +02:00
|
|
|
__ieee80211_stop_poll(sdata);
|
2009-04-19 07:47:19 +02:00
|
|
|
|
2009-07-10 15:29:03 +02:00
|
|
|
/* let's probe the connection once */
|
2015-06-02 21:39:54 +02:00
|
|
|
if (!ieee80211_hw_check(&sdata->local->hw, CONNECTION_MONITOR))
|
2012-02-19 14:26:09 +01:00
|
|
|
ieee80211_queue_work(&sdata->local->hw,
|
|
|
|
&sdata->u.mgd.monitor_work);
|
2009-07-10 15:29:03 +02:00
|
|
|
/* and do all the other regular work too */
|
2010-06-10 10:21:31 +02:00
|
|
|
ieee80211_queue_work(&sdata->local->hw, &sdata->work);
|
2009-04-19 07:47:19 +02:00
|
|
|
}
|
2008-09-11 00:01:52 +02:00
|
|
|
}
|
2007-05-05 20:45:53 +02:00
|
|
|
|
2013-04-29 14:57:44 +02:00
|
|
|
#ifdef CONFIG_PM
|
2014-03-19 09:55:55 +01:00
|
|
|
void ieee80211_mgd_quiesce(struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
|
|
|
|
|
|
|
|
sdata_lock(sdata);
|
|
|
|
|
2014-05-13 11:54:09 +02:00
|
|
|
if (ifmgd->auth_data || ifmgd->assoc_data) {
|
|
|
|
const u8 *bssid = ifmgd->auth_data ?
|
|
|
|
ifmgd->auth_data->bss->bssid :
|
|
|
|
ifmgd->assoc_data->bss->bssid;
|
|
|
|
|
2014-03-19 09:55:55 +01:00
|
|
|
/*
|
2014-05-13 11:54:09 +02:00
|
|
|
* If we are trying to authenticate / associate while suspending,
|
|
|
|
* cfg80211 won't know and won't actually abort those attempts,
|
|
|
|
* thus we need to do that ourselves.
|
2014-03-19 09:55:55 +01:00
|
|
|
*/
|
2014-05-13 11:54:09 +02:00
|
|
|
ieee80211_send_deauth_disassoc(sdata, bssid,
|
2014-03-19 09:55:55 +01:00
|
|
|
IEEE80211_STYPE_DEAUTH,
|
|
|
|
WLAN_REASON_DEAUTH_LEAVING,
|
|
|
|
false, frame_buf);
|
2014-05-13 11:54:09 +02:00
|
|
|
if (ifmgd->assoc_data)
|
|
|
|
ieee80211_destroy_assoc_data(sdata, false);
|
|
|
|
if (ifmgd->auth_data)
|
|
|
|
ieee80211_destroy_auth_data(sdata, false);
|
2014-03-19 09:55:55 +01:00
|
|
|
cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
|
|
|
|
IEEE80211_DEAUTH_FRAME_LEN);
|
|
|
|
}
|
|
|
|
|
2015-03-01 08:10:03 +01:00
|
|
|
/* This is a bit of a hack - we should find a better and more generic
|
|
|
|
* solution to this. Normally when suspending, cfg80211 will in fact
|
|
|
|
* deauthenticate. However, it doesn't (and cannot) stop an ongoing
|
|
|
|
* auth (not so important) or assoc (this is the problem) process.
|
|
|
|
*
|
|
|
|
* As a consequence, it can happen that we are in the process of both
|
|
|
|
* associating and suspending, and receive an association response
|
|
|
|
* after cfg80211 has checked if it needs to disconnect, but before
|
|
|
|
* we actually set the flag to drop incoming frames. This will then
|
|
|
|
* cause the workqueue flush to process the association response in
|
|
|
|
* the suspend, resulting in a successful association just before it
|
|
|
|
* tries to remove the interface from the driver, which now though
|
|
|
|
* has a channel context assigned ... this results in issues.
|
|
|
|
*
|
|
|
|
* To work around this (for now) simply deauth here again if we're
|
|
|
|
* now connected.
|
|
|
|
*/
|
|
|
|
if (ifmgd->associated && !sdata->local->wowlan) {
|
|
|
|
u8 bssid[ETH_ALEN];
|
|
|
|
struct cfg80211_deauth_request req = {
|
|
|
|
.reason_code = WLAN_REASON_DEAUTH_LEAVING,
|
|
|
|
.bssid = bssid,
|
|
|
|
};
|
|
|
|
|
|
|
|
memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN);
|
|
|
|
ieee80211_mgd_deauth(sdata, &req);
|
|
|
|
}
|
|
|
|
|
2014-03-19 09:55:55 +01:00
|
|
|
sdata_unlock(sdata);
|
|
|
|
}
|
|
|
|
|
2013-04-29 14:57:44 +02:00
|
|
|
void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
|
2013-05-10 12:32:47 +02:00
|
|
|
sdata_lock(sdata);
|
2013-04-29 14:57:44 +02:00
|
|
|
if (!ifmgd->associated) {
|
2013-05-10 12:32:47 +02:00
|
|
|
sdata_unlock(sdata);
|
2013-04-29 14:57:44 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sdata->flags & IEEE80211_SDATA_DISCONNECT_RESUME) {
|
|
|
|
sdata->flags &= ~IEEE80211_SDATA_DISCONNECT_RESUME;
|
|
|
|
mlme_dbg(sdata, "driver requested disconnect after resume\n");
|
|
|
|
ieee80211_sta_connection_lost(sdata,
|
|
|
|
ifmgd->associated->bssid,
|
|
|
|
WLAN_REASON_UNSPECIFIED,
|
|
|
|
true);
|
2013-05-10 12:32:47 +02:00
|
|
|
sdata_unlock(sdata);
|
2013-04-29 14:57:44 +02:00
|
|
|
return;
|
|
|
|
}
|
2013-05-10 12:32:47 +02:00
|
|
|
sdata_unlock(sdata);
|
2013-04-29 14:57:44 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-09-11 00:01:52 +02:00
|
|
|
/* interface setup */
|
|
|
|
void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
|
2007-05-05 20:45:53 +02:00
|
|
|
{
|
2009-02-15 12:44:28 +01:00
|
|
|
struct ieee80211_if_managed *ifmgd;
|
2008-04-17 19:21:22 +02:00
|
|
|
|
2009-02-15 12:44:28 +01:00
|
|
|
ifmgd = &sdata->u.mgd;
|
2009-07-10 15:29:03 +02:00
|
|
|
INIT_WORK(&ifmgd->monitor_work, ieee80211_sta_monitor_work);
|
2009-02-15 12:44:28 +01:00
|
|
|
INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work);
|
2010-03-19 06:14:53 +01:00
|
|
|
INIT_WORK(&ifmgd->beacon_connection_loss_work,
|
|
|
|
ieee80211_beacon_connection_loss_work);
|
2012-08-01 22:32:45 +02:00
|
|
|
INIT_WORK(&ifmgd->csa_connection_drop_work,
|
|
|
|
ieee80211_csa_connection_drop_work);
|
2013-10-01 15:45:43 +02:00
|
|
|
INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_mgd_work);
|
2014-07-17 16:14:25 +02:00
|
|
|
INIT_DELAYED_WORK(&ifmgd->tdls_peer_del_work,
|
|
|
|
ieee80211_tdls_peer_del_work);
|
2009-02-15 12:44:28 +01:00
|
|
|
setup_timer(&ifmgd->timer, ieee80211_sta_timer,
|
2009-01-06 04:58:37 +01:00
|
|
|
(unsigned long) sdata);
|
2009-07-10 15:29:03 +02:00
|
|
|
setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer,
|
|
|
|
(unsigned long) sdata);
|
|
|
|
setup_timer(&ifmgd->conn_mon_timer, ieee80211_sta_conn_mon_timer,
|
|
|
|
(unsigned long) sdata);
|
2009-02-15 12:44:28 +01:00
|
|
|
setup_timer(&ifmgd->chswitch_timer, ieee80211_chswitch_timer,
|
2008-09-11 00:01:52 +02:00
|
|
|
(unsigned long) sdata);
|
2014-10-07 09:38:50 +02:00
|
|
|
INIT_DELAYED_WORK(&ifmgd->tx_tspec_wk,
|
|
|
|
ieee80211_sta_handle_tspec_ac_params_wk);
|
2008-09-11 00:01:52 +02:00
|
|
|
|
2009-07-01 21:41:17 +02:00
|
|
|
ifmgd->flags = 0;
|
2011-12-14 15:16:08 +01:00
|
|
|
ifmgd->powersave = sdata->wdev.ps;
|
2013-01-22 15:52:23 +01:00
|
|
|
ifmgd->uapsd_queues = sdata->local->hw.uapsd_queues;
|
|
|
|
ifmgd->uapsd_max_sp_len = sdata->local->hw.uapsd_max_sp_len;
|
2013-03-21 15:47:56 +01:00
|
|
|
ifmgd->p2p_noa_index = -1;
|
2009-04-16 13:27:42 +02:00
|
|
|
|
2014-09-10 13:07:36 +02:00
|
|
|
if (sdata->local->hw.wiphy->features & NL80211_FEATURE_DYNAMIC_SMPS)
|
2009-12-01 13:37:02 +01:00
|
|
|
ifmgd->req_smps = IEEE80211_SMPS_AUTOMATIC;
|
|
|
|
else
|
|
|
|
ifmgd->req_smps = IEEE80211_SMPS_OFF;
|
2014-11-09 17:50:08 +01:00
|
|
|
|
|
|
|
/* Setup TDLS data */
|
|
|
|
spin_lock_init(&ifmgd->teardown_lock);
|
|
|
|
ifmgd->teardown_skb = NULL;
|
|
|
|
ifmgd->orig_teardown_skb = NULL;
|
2007-05-05 20:45:53 +02:00
|
|
|
}
|
|
|
|
|
2009-07-07 03:45:17 +02:00
|
|
|
/* scan finished notification */
|
|
|
|
void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local)
|
2008-09-08 17:44:22 +02:00
|
|
|
{
|
2012-07-06 21:18:24 +02:00
|
|
|
struct ieee80211_sub_if_data *sdata;
|
2008-09-08 17:44:22 +02:00
|
|
|
|
2009-07-07 03:45:17 +02:00
|
|
|
/* Restart STA timers */
|
|
|
|
rcu_read_lock();
|
2013-03-20 01:50:50 +01:00
|
|
|
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
|
|
|
|
if (ieee80211_sdata_running(sdata))
|
|
|
|
ieee80211_restart_sta_timer(sdata);
|
|
|
|
}
|
2009-07-07 03:45:17 +02:00
|
|
|
rcu_read_unlock();
|
|
|
|
}
|
2008-09-08 17:44:22 +02:00
|
|
|
|
2009-07-07 03:45:17 +02:00
|
|
|
int ieee80211_max_network_latency(struct notifier_block *nb,
|
|
|
|
unsigned long data, void *dummy)
|
|
|
|
{
|
|
|
|
s32 latency_usec = (s32) data;
|
|
|
|
struct ieee80211_local *local =
|
|
|
|
container_of(nb, struct ieee80211_local,
|
|
|
|
network_latency_notifier);
|
2009-06-15 18:13:58 +02:00
|
|
|
|
2009-07-07 03:45:17 +02:00
|
|
|
mutex_lock(&local->iflist_mtx);
|
|
|
|
ieee80211_recalc_ps(local, latency_usec);
|
|
|
|
mutex_unlock(&local->iflist_mtx);
|
2009-01-24 01:19:04 +01:00
|
|
|
|
2014-04-21 06:53:05 +02:00
|
|
|
return NOTIFY_OK;
|
2008-09-11 00:01:52 +02:00
|
|
|
}
|
|
|
|
|
2012-11-22 14:11:39 +01:00
|
|
|
static u8 ieee80211_ht_vht_rx_chains(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct cfg80211_bss *cbss)
|
|
|
|
{
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
const u8 *ht_cap_ie, *vht_cap_ie;
|
|
|
|
const struct ieee80211_ht_cap *ht_cap;
|
|
|
|
const struct ieee80211_vht_cap *vht_cap;
|
|
|
|
u8 chains = 1;
|
|
|
|
|
|
|
|
if (ifmgd->flags & IEEE80211_STA_DISABLE_HT)
|
|
|
|
return chains;
|
|
|
|
|
2012-11-29 01:25:20 +01:00
|
|
|
ht_cap_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_HT_CAPABILITY);
|
2012-11-22 14:11:39 +01:00
|
|
|
if (ht_cap_ie && ht_cap_ie[1] >= sizeof(*ht_cap)) {
|
|
|
|
ht_cap = (void *)(ht_cap_ie + 2);
|
|
|
|
chains = ieee80211_mcs_to_chains(&ht_cap->mcs);
|
|
|
|
/*
|
|
|
|
* TODO: use "Tx Maximum Number Spatial Streams Supported" and
|
|
|
|
* "Tx Unequal Modulation Supported" fields.
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT)
|
|
|
|
return chains;
|
|
|
|
|
2012-11-29 01:25:20 +01:00
|
|
|
vht_cap_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_VHT_CAPABILITY);
|
2012-11-22 14:11:39 +01:00
|
|
|
if (vht_cap_ie && vht_cap_ie[1] >= sizeof(*vht_cap)) {
|
|
|
|
u8 nss;
|
|
|
|
u16 tx_mcs_map;
|
|
|
|
|
|
|
|
vht_cap = (void *)(vht_cap_ie + 2);
|
|
|
|
tx_mcs_map = le16_to_cpu(vht_cap->supp_mcs.tx_mcs_map);
|
|
|
|
for (nss = 8; nss > 0; nss--) {
|
|
|
|
if (((tx_mcs_map >> (2 * (nss - 1))) & 3) !=
|
|
|
|
IEEE80211_VHT_MCS_NOT_SUPPORTED)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* TODO: use "Tx Highest Supported Long GI Data Rate" field? */
|
|
|
|
chains = max(chains, nss);
|
|
|
|
}
|
|
|
|
|
|
|
|
return chains;
|
|
|
|
}
|
|
|
|
|
2012-07-27 11:41:27 +02:00
|
|
|
static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct cfg80211_bss *cbss)
|
2012-03-08 15:02:07 +01:00
|
|
|
{
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2014-07-24 10:20:05 +02:00
|
|
|
const struct ieee80211_ht_cap *ht_cap = NULL;
|
2012-03-28 10:58:36 +02:00
|
|
|
const struct ieee80211_ht_operation *ht_oper = NULL;
|
2012-11-22 14:11:39 +01:00
|
|
|
const struct ieee80211_vht_operation *vht_oper = NULL;
|
2012-03-28 10:58:36 +02:00
|
|
|
struct ieee80211_supported_band *sband;
|
2012-11-09 11:39:59 +01:00
|
|
|
struct cfg80211_chan_def chandef;
|
2012-11-22 14:11:39 +01:00
|
|
|
int ret;
|
2015-08-15 21:39:53 +02:00
|
|
|
u32 i;
|
|
|
|
bool have_80mhz;
|
2012-03-08 15:02:07 +01:00
|
|
|
|
2012-03-28 10:58:36 +02:00
|
|
|
sband = local->hw.wiphy->bands[cbss->channel->band];
|
|
|
|
|
2012-11-22 14:11:39 +01:00
|
|
|
ifmgd->flags &= ~(IEEE80211_STA_DISABLE_40MHZ |
|
|
|
|
IEEE80211_STA_DISABLE_80P80MHZ |
|
|
|
|
IEEE80211_STA_DISABLE_160MHZ);
|
|
|
|
|
2012-11-29 01:25:20 +01:00
|
|
|
rcu_read_lock();
|
|
|
|
|
2012-11-22 14:11:39 +01:00
|
|
|
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT) &&
|
|
|
|
sband->ht_cap.ht_supported) {
|
2014-07-24 10:20:05 +02:00
|
|
|
const u8 *ht_oper_ie, *ht_cap_ie;
|
2012-03-28 10:58:36 +02:00
|
|
|
|
2012-11-29 01:25:20 +01:00
|
|
|
ht_oper_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_HT_OPERATION);
|
2012-03-28 10:58:36 +02:00
|
|
|
if (ht_oper_ie && ht_oper_ie[1] >= sizeof(*ht_oper))
|
|
|
|
ht_oper = (void *)(ht_oper_ie + 2);
|
2013-02-07 23:33:32 +01:00
|
|
|
|
2014-07-24 10:20:05 +02:00
|
|
|
ht_cap_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_HT_CAPABILITY);
|
|
|
|
if (ht_cap_ie && ht_cap_ie[1] >= sizeof(*ht_cap))
|
|
|
|
ht_cap = (void *)(ht_cap_ie + 2);
|
|
|
|
|
|
|
|
if (!ht_cap) {
|
2013-02-07 23:33:32 +01:00
|
|
|
ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
|
|
|
|
ht_oper = NULL;
|
|
|
|
}
|
2012-03-28 10:58:36 +02:00
|
|
|
}
|
|
|
|
|
2012-11-22 14:11:39 +01:00
|
|
|
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT) &&
|
|
|
|
sband->vht_cap.vht_supported) {
|
2013-02-07 23:33:32 +01:00
|
|
|
const u8 *vht_oper_ie, *vht_cap;
|
2012-11-22 14:11:39 +01:00
|
|
|
|
2012-11-29 01:25:20 +01:00
|
|
|
vht_oper_ie = ieee80211_bss_get_ie(cbss,
|
|
|
|
WLAN_EID_VHT_OPERATION);
|
2012-11-22 14:11:39 +01:00
|
|
|
if (vht_oper_ie && vht_oper_ie[1] >= sizeof(*vht_oper))
|
|
|
|
vht_oper = (void *)(vht_oper_ie + 2);
|
|
|
|
if (vht_oper && !ht_oper) {
|
|
|
|
vht_oper = NULL;
|
2012-06-22 11:29:50 +02:00
|
|
|
sdata_info(sdata,
|
2012-11-22 14:11:39 +01:00
|
|
|
"AP advertised VHT without HT, disabling both\n");
|
2013-02-07 20:41:50 +01:00
|
|
|
ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
|
|
|
|
ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
|
2012-03-28 10:58:36 +02:00
|
|
|
}
|
2013-02-07 23:33:32 +01:00
|
|
|
|
|
|
|
vht_cap = ieee80211_bss_get_ie(cbss, WLAN_EID_VHT_CAPABILITY);
|
|
|
|
if (!vht_cap || vht_cap[1] < sizeof(struct ieee80211_vht_cap)) {
|
|
|
|
ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
|
|
|
|
vht_oper = NULL;
|
|
|
|
}
|
2012-03-28 10:58:36 +02:00
|
|
|
}
|
|
|
|
|
2015-08-15 21:39:53 +02:00
|
|
|
/* Allow VHT if at least one channel on the sband supports 80 MHz */
|
|
|
|
have_80mhz = false;
|
|
|
|
for (i = 0; i < sband->n_channels; i++) {
|
|
|
|
if (sband->channels[i].flags & (IEEE80211_CHAN_DISABLED |
|
|
|
|
IEEE80211_CHAN_NO_80MHZ))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
have_80mhz = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!have_80mhz)
|
|
|
|
ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
|
|
|
|
|
2012-11-22 14:11:39 +01:00
|
|
|
ifmgd->flags |= ieee80211_determine_chantype(sdata, sband,
|
|
|
|
cbss->channel,
|
2014-07-24 10:20:05 +02:00
|
|
|
ht_cap, ht_oper, vht_oper,
|
2013-07-31 11:23:06 +02:00
|
|
|
&chandef, false);
|
2012-09-11 14:34:12 +02:00
|
|
|
|
2012-11-22 14:11:39 +01:00
|
|
|
sdata->needed_rx_chains = min(ieee80211_ht_vht_rx_chains(sdata, cbss),
|
|
|
|
local->rx_chains);
|
2012-03-28 10:58:36 +02:00
|
|
|
|
2012-11-29 01:25:20 +01:00
|
|
|
rcu_read_unlock();
|
|
|
|
|
2012-09-11 14:34:12 +02:00
|
|
|
/* will change later if needed */
|
|
|
|
sdata->smps_mode = IEEE80211_SMPS_OFF;
|
|
|
|
|
mac80211: fix iflist_mtx/mtx locking in radar detection
The scan code creates an iflist_mtx -> mtx locking dependency,
and a few other places, notably radar detection, were creating
the opposite dependency, causing lockdep to complain. As scan
and radar detection are mutually exclusive, the deadlock can't
really happen in practice, but it's still bad form.
A similar issue exists in the monitor mode code, but this is
only used by channel-context drivers right now and those have
to have hardware scan, so that also can't happen.
Still, fix these issues by making some of the channel context
code require the mtx to be held rather than acquiring it, thus
allowing the monitor/radar callers to keep the iflist_mtx->mtx
lock ordering.
While at it, also fix access to the local->scanning variable
in the radar code, and document that radar_detect_enabled is
now properly protected by the mtx.
All this would now introduce an ABBA deadlock between the DFS
work cancelling and local->mtx, so change the locking there a
bit to not need to use cancel_delayed_work_sync() but be able
to just use cancel_delayed_work(). The work is also safely
stopped/removed when the interface is stopped, so no extra
changes are needed.
Reported-by: Kalle Valo <kvalo@qca.qualcomm.com>
Tested-by: Simon Wunderlich <sw@simonwunderlich.de>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2013-12-18 09:43:33 +01:00
|
|
|
mutex_lock(&local->mtx);
|
2012-11-22 14:11:39 +01:00
|
|
|
/*
|
|
|
|
* If this fails (possibly due to channel context sharing
|
|
|
|
* on incompatible channels, e.g. 80+80 and 160 sharing the
|
|
|
|
* same control channel) try to use a smaller bandwidth.
|
|
|
|
*/
|
|
|
|
ret = ieee80211_vif_use_channel(sdata, &chandef,
|
|
|
|
IEEE80211_CHANCTX_SHARED);
|
2013-05-16 13:00:31 +02:00
|
|
|
|
|
|
|
/* don't downgrade for 5 and 10 MHz channels, though. */
|
|
|
|
if (chandef.width == NL80211_CHAN_WIDTH_5 ||
|
|
|
|
chandef.width == NL80211_CHAN_WIDTH_10)
|
mac80211: fix iflist_mtx/mtx locking in radar detection
The scan code creates an iflist_mtx -> mtx locking dependency,
and a few other places, notably radar detection, were creating
the opposite dependency, causing lockdep to complain. As scan
and radar detection are mutually exclusive, the deadlock can't
really happen in practice, but it's still bad form.
A similar issue exists in the monitor mode code, but this is
only used by channel-context drivers right now and those have
to have hardware scan, so that also can't happen.
Still, fix these issues by making some of the channel context
code require the mtx to be held rather than acquiring it, thus
allowing the monitor/radar callers to keep the iflist_mtx->mtx
lock ordering.
While at it, also fix access to the local->scanning variable
in the radar code, and document that radar_detect_enabled is
now properly protected by the mtx.
All this would now introduce an ABBA deadlock between the DFS
work cancelling and local->mtx, so change the locking there a
bit to not need to use cancel_delayed_work_sync() but be able
to just use cancel_delayed_work(). The work is also safely
stopped/removed when the interface is stopped, so no extra
changes are needed.
Reported-by: Kalle Valo <kvalo@qca.qualcomm.com>
Tested-by: Simon Wunderlich <sw@simonwunderlich.de>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2013-12-18 09:43:33 +01:00
|
|
|
goto out;
|
2013-05-16 13:00:31 +02:00
|
|
|
|
2013-02-07 20:54:51 +01:00
|
|
|
while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT) {
|
2013-08-28 13:41:29 +02:00
|
|
|
ifmgd->flags |= ieee80211_chandef_downgrade(&chandef);
|
2013-02-07 20:54:51 +01:00
|
|
|
ret = ieee80211_vif_use_channel(sdata, &chandef,
|
|
|
|
IEEE80211_CHANCTX_SHARED);
|
|
|
|
}
|
mac80211: fix iflist_mtx/mtx locking in radar detection
The scan code creates an iflist_mtx -> mtx locking dependency,
and a few other places, notably radar detection, were creating
the opposite dependency, causing lockdep to complain. As scan
and radar detection are mutually exclusive, the deadlock can't
really happen in practice, but it's still bad form.
A similar issue exists in the monitor mode code, but this is
only used by channel-context drivers right now and those have
to have hardware scan, so that also can't happen.
Still, fix these issues by making some of the channel context
code require the mtx to be held rather than acquiring it, thus
allowing the monitor/radar callers to keep the iflist_mtx->mtx
lock ordering.
While at it, also fix access to the local->scanning variable
in the radar code, and document that radar_detect_enabled is
now properly protected by the mtx.
All this would now introduce an ABBA deadlock between the DFS
work cancelling and local->mtx, so change the locking there a
bit to not need to use cancel_delayed_work_sync() but be able
to just use cancel_delayed_work(). The work is also safely
stopped/removed when the interface is stopped, so no extra
changes are needed.
Reported-by: Kalle Valo <kvalo@qca.qualcomm.com>
Tested-by: Simon Wunderlich <sw@simonwunderlich.de>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2013-12-18 09:43:33 +01:00
|
|
|
out:
|
|
|
|
mutex_unlock(&local->mtx);
|
2012-11-22 14:11:39 +01:00
|
|
|
return ret;
|
2012-07-27 11:41:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
|
2015-04-20 21:51:46 +02:00
|
|
|
struct cfg80211_bss *cbss, bool assoc,
|
|
|
|
bool override)
|
2012-07-27 11:41:27 +02:00
|
|
|
{
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
struct ieee80211_bss *bss = (void *)cbss->priv;
|
|
|
|
struct sta_info *new_sta = NULL;
|
2014-12-14 09:38:59 +01:00
|
|
|
struct ieee80211_supported_band *sband;
|
2015-04-20 21:51:46 +02:00
|
|
|
bool have_sta = false;
|
2012-07-27 11:41:27 +02:00
|
|
|
int err;
|
|
|
|
|
2014-12-14 09:38:59 +01:00
|
|
|
sband = local->hw.wiphy->bands[cbss->channel->band];
|
|
|
|
|
2012-07-27 11:41:27 +02:00
|
|
|
if (WARN_ON(!ifmgd->auth_data && !ifmgd->assoc_data))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (assoc) {
|
|
|
|
rcu_read_lock();
|
|
|
|
have_sta = sta_info_get(sdata, cbss->bssid);
|
|
|
|
rcu_read_unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!have_sta) {
|
|
|
|
new_sta = sta_info_alloc(sdata, cbss->bssid, GFP_KERNEL);
|
|
|
|
if (!new_sta)
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2014-12-14 09:38:59 +01:00
|
|
|
|
2015-04-20 21:51:46 +02:00
|
|
|
if (new_sta || override) {
|
2014-12-14 09:38:59 +01:00
|
|
|
err = ieee80211_prep_channel(sdata, cbss);
|
|
|
|
if (err) {
|
|
|
|
if (new_sta)
|
|
|
|
sta_info_free(local, new_sta);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-27 10:43:16 +02:00
|
|
|
if (new_sta) {
|
2012-03-08 15:02:08 +01:00
|
|
|
u32 rates = 0, basic_rates = 0;
|
|
|
|
bool have_higher_than_11mbit;
|
|
|
|
int min_rate = INT_MAX, min_rate_index = -1;
|
2013-07-08 16:55:53 +02:00
|
|
|
struct ieee80211_chanctx_conf *chanctx_conf;
|
2013-02-05 16:54:31 +01:00
|
|
|
const struct cfg80211_bss_ies *ies;
|
2014-12-14 09:38:59 +01:00
|
|
|
int shift = ieee80211_vif_get_shift(&sdata->vif);
|
2013-07-08 16:55:53 +02:00
|
|
|
u32 rate_flags;
|
2012-07-27 11:41:27 +02:00
|
|
|
|
2013-07-08 16:55:53 +02:00
|
|
|
rcu_read_lock();
|
|
|
|
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
|
|
|
|
if (WARN_ON(!chanctx_conf)) {
|
|
|
|
rcu_read_unlock();
|
2014-02-06 20:01:32 +01:00
|
|
|
sta_info_free(local, new_sta);
|
2013-07-08 16:55:53 +02:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
rate_flags = ieee80211_chandef_rate_flags(&chanctx_conf->def);
|
|
|
|
rcu_read_unlock();
|
2012-03-08 15:02:08 +01:00
|
|
|
|
|
|
|
ieee80211_get_rates(sband, bss->supp_rates,
|
|
|
|
bss->supp_rates_len,
|
|
|
|
&rates, &basic_rates,
|
|
|
|
&have_higher_than_11mbit,
|
2013-07-08 16:55:53 +02:00
|
|
|
&min_rate, &min_rate_index,
|
|
|
|
shift, rate_flags);
|
2012-03-08 15:02:08 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* This used to be a workaround for basic rates missing
|
|
|
|
* in the association response frame. Now that we no
|
|
|
|
* longer use the basic rates from there, it probably
|
|
|
|
* doesn't happen any more, but keep the workaround so
|
|
|
|
* in case some *other* APs are buggy in different ways
|
|
|
|
* we can connect -- with a warning.
|
|
|
|
*/
|
|
|
|
if (!basic_rates && min_rate_index >= 0) {
|
2012-06-22 11:29:50 +02:00
|
|
|
sdata_info(sdata,
|
|
|
|
"No basic rates, using min rate instead\n");
|
2012-03-08 15:02:08 +01:00
|
|
|
basic_rates = BIT(min_rate_index);
|
|
|
|
}
|
|
|
|
|
2012-07-27 10:43:16 +02:00
|
|
|
new_sta->sta.supp_rates[cbss->channel->band] = rates;
|
2012-03-08 15:02:08 +01:00
|
|
|
sdata->vif.bss_conf.basic_rates = basic_rates;
|
|
|
|
|
|
|
|
/* cf. IEEE 802.11 9.2.12 */
|
2012-07-26 17:24:39 +02:00
|
|
|
if (cbss->channel->band == IEEE80211_BAND_2GHZ &&
|
2012-03-08 15:02:08 +01:00
|
|
|
have_higher_than_11mbit)
|
|
|
|
sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
|
|
|
|
else
|
|
|
|
sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
|
|
|
|
|
|
|
|
memcpy(ifmgd->bssid, cbss->bssid, ETH_ALEN);
|
|
|
|
|
2012-05-22 22:13:05 +02:00
|
|
|
/* set timing information */
|
|
|
|
sdata->vif.bss_conf.beacon_int = cbss->beacon_interval;
|
2013-02-05 16:54:31 +01:00
|
|
|
rcu_read_lock();
|
2013-02-05 17:48:40 +01:00
|
|
|
ies = rcu_dereference(cbss->beacon_ies);
|
|
|
|
if (ies) {
|
|
|
|
const u8 *tim_ie;
|
|
|
|
|
|
|
|
sdata->vif.bss_conf.sync_tsf = ies->tsf;
|
|
|
|
sdata->vif.bss_conf.sync_device_ts =
|
|
|
|
bss->device_ts_beacon;
|
|
|
|
tim_ie = cfg80211_find_ie(WLAN_EID_TIM,
|
|
|
|
ies->data, ies->len);
|
|
|
|
if (tim_ie && tim_ie[1] >= 2)
|
|
|
|
sdata->vif.bss_conf.sync_dtim_count = tim_ie[2];
|
|
|
|
else
|
|
|
|
sdata->vif.bss_conf.sync_dtim_count = 0;
|
2015-06-02 21:39:54 +02:00
|
|
|
} else if (!ieee80211_hw_check(&sdata->local->hw,
|
|
|
|
TIMING_BEACON_ONLY)) {
|
2013-02-05 17:48:40 +01:00
|
|
|
ies = rcu_dereference(cbss->proberesp_ies);
|
|
|
|
/* must be non-NULL since beacon IEs were NULL */
|
|
|
|
sdata->vif.bss_conf.sync_tsf = ies->tsf;
|
|
|
|
sdata->vif.bss_conf.sync_device_ts =
|
|
|
|
bss->device_ts_presp;
|
|
|
|
sdata->vif.bss_conf.sync_dtim_count = 0;
|
|
|
|
} else {
|
|
|
|
sdata->vif.bss_conf.sync_tsf = 0;
|
|
|
|
sdata->vif.bss_conf.sync_device_ts = 0;
|
|
|
|
sdata->vif.bss_conf.sync_dtim_count = 0;
|
|
|
|
}
|
2013-02-05 16:54:31 +01:00
|
|
|
rcu_read_unlock();
|
2012-05-22 22:13:05 +02:00
|
|
|
|
|
|
|
/* tell driver about BSSID, basic rates and timing */
|
2012-03-08 15:02:08 +01:00
|
|
|
ieee80211_bss_info_change_notify(sdata,
|
2012-05-22 22:13:05 +02:00
|
|
|
BSS_CHANGED_BSSID | BSS_CHANGED_BASIC_RATES |
|
|
|
|
BSS_CHANGED_BEACON_INT);
|
2012-03-08 15:02:07 +01:00
|
|
|
|
|
|
|
if (assoc)
|
2012-07-27 10:43:16 +02:00
|
|
|
sta_info_pre_move_state(new_sta, IEEE80211_STA_AUTH);
|
2012-03-08 15:02:07 +01:00
|
|
|
|
2012-07-27 10:43:16 +02:00
|
|
|
err = sta_info_insert(new_sta);
|
|
|
|
new_sta = NULL;
|
2012-03-08 15:02:07 +01:00
|
|
|
if (err) {
|
2012-06-22 11:29:50 +02:00
|
|
|
sdata_info(sdata,
|
|
|
|
"failed to insert STA entry for the AP (error %d)\n",
|
|
|
|
err);
|
2012-03-08 15:02:07 +01:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
} else
|
mac80211: Convert compare_ether_addr to ether_addr_equal
Use the new bool function ether_addr_equal to add
some clarity and reduce the likelihood for misuse
of compare_ether_addr for sorting.
Done via cocci script:
$ cat compare_ether_addr.cocci
@@
expression a,b;
@@
- !compare_ether_addr(a, b)
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- compare_ether_addr(a, b)
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) == 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) != 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) == 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) != 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !!ether_addr_equal(a, b)
+ ether_addr_equal(a, b)
Signed-off-by: Joe Perches <joe@perches.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-05-08 20:56:52 +02:00
|
|
|
WARN_ON_ONCE(!ether_addr_equal(ifmgd->bssid, cbss->bssid));
|
2012-03-08 15:02:07 +01:00
|
|
|
|
2015-03-17 18:58:38 +01:00
|
|
|
/* Cancel scan to ensure that nothing interferes with connection */
|
|
|
|
if (local->scanning)
|
|
|
|
ieee80211_scan_cancel(local);
|
|
|
|
|
2012-03-08 15:02:07 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-07-07 03:45:17 +02:00
|
|
|
/* config hooks */
|
|
|
|
int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct cfg80211_auth_request *req)
|
2008-09-11 00:01:52 +02:00
|
|
|
{
|
2012-01-20 13:55:27 +01:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
struct ieee80211_mgd_auth_data *auth_data;
|
2009-07-07 03:45:17 +02:00
|
|
|
u16 auth_alg;
|
2012-01-20 13:55:27 +01:00
|
|
|
int err;
|
|
|
|
|
|
|
|
/* prepare auth data structure */
|
2008-09-11 00:01:52 +02:00
|
|
|
|
2009-07-07 03:45:17 +02:00
|
|
|
switch (req->auth_type) {
|
|
|
|
case NL80211_AUTHTYPE_OPEN_SYSTEM:
|
|
|
|
auth_alg = WLAN_AUTH_OPEN;
|
|
|
|
break;
|
|
|
|
case NL80211_AUTHTYPE_SHARED_KEY:
|
2012-01-20 13:55:27 +01:00
|
|
|
if (IS_ERR(local->wep_tx_tfm))
|
2010-07-21 10:09:25 +02:00
|
|
|
return -EOPNOTSUPP;
|
2009-07-07 03:45:17 +02:00
|
|
|
auth_alg = WLAN_AUTH_SHARED_KEY;
|
|
|
|
break;
|
|
|
|
case NL80211_AUTHTYPE_FT:
|
|
|
|
auth_alg = WLAN_AUTH_FT;
|
|
|
|
break;
|
|
|
|
case NL80211_AUTHTYPE_NETWORK_EAP:
|
|
|
|
auth_alg = WLAN_AUTH_LEAP;
|
|
|
|
break;
|
2012-09-30 18:29:40 +02:00
|
|
|
case NL80211_AUTHTYPE_SAE:
|
|
|
|
auth_alg = WLAN_AUTH_SAE;
|
|
|
|
break;
|
2009-07-07 03:45:17 +02:00
|
|
|
default:
|
|
|
|
return -EOPNOTSUPP;
|
2008-09-08 17:44:22 +02:00
|
|
|
}
|
2009-07-07 03:45:17 +02:00
|
|
|
|
2012-09-30 18:29:40 +02:00
|
|
|
auth_data = kzalloc(sizeof(*auth_data) + req->sae_data_len +
|
|
|
|
req->ie_len, GFP_KERNEL);
|
2012-01-20 13:55:27 +01:00
|
|
|
if (!auth_data)
|
2008-09-11 00:01:52 +02:00
|
|
|
return -ENOMEM;
|
2009-07-07 03:45:17 +02:00
|
|
|
|
2012-01-20 13:55:27 +01:00
|
|
|
auth_data->bss = req->bss;
|
2009-07-07 03:45:17 +02:00
|
|
|
|
2012-09-30 18:29:40 +02:00
|
|
|
if (req->sae_data_len >= 4) {
|
|
|
|
__le16 *pos = (__le16 *) req->sae_data;
|
|
|
|
auth_data->sae_trans = le16_to_cpu(pos[0]);
|
|
|
|
auth_data->sae_status = le16_to_cpu(pos[1]);
|
|
|
|
memcpy(auth_data->data, req->sae_data + 4,
|
|
|
|
req->sae_data_len - 4);
|
|
|
|
auth_data->data_len += req->sae_data_len - 4;
|
|
|
|
}
|
|
|
|
|
2009-07-07 03:45:17 +02:00
|
|
|
if (req->ie && req->ie_len) {
|
2012-09-30 18:29:40 +02:00
|
|
|
memcpy(&auth_data->data[auth_data->data_len],
|
|
|
|
req->ie, req->ie_len);
|
|
|
|
auth_data->data_len += req->ie_len;
|
2008-09-11 00:01:52 +02:00
|
|
|
}
|
2009-07-07 03:45:17 +02:00
|
|
|
|
cfg80211: rework key operation
This reworks the key operation in cfg80211, and now only
allows, from userspace, configuring keys (via nl80211)
after the connection has been established (in managed
mode), the IBSS been joined (in IBSS mode), at any time
(in AP[_VLAN] modes) or never for all the other modes.
In order to do shared key authentication correctly, it
is now possible to give a WEP key to the AUTH command.
To configure static WEP keys, these are given to the
CONNECT or IBSS_JOIN command directly, for a userspace
SME it is assumed it will configure it properly after
the connection has been established.
Since mac80211 used to check the default key in IBSS
mode to see whether or not the network is protected,
it needs an update in that area, as well as an update
to make use of the WEP key passed to auth() for shared
key authentication.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-07-08 14:22:54 +02:00
|
|
|
if (req->key && req->key_len) {
|
2012-01-20 13:55:27 +01:00
|
|
|
auth_data->key_len = req->key_len;
|
|
|
|
auth_data->key_idx = req->key_idx;
|
|
|
|
memcpy(auth_data->key, req->key, req->key_len);
|
cfg80211: rework key operation
This reworks the key operation in cfg80211, and now only
allows, from userspace, configuring keys (via nl80211)
after the connection has been established (in managed
mode), the IBSS been joined (in IBSS mode), at any time
(in AP[_VLAN] modes) or never for all the other modes.
In order to do shared key authentication correctly, it
is now possible to give a WEP key to the AUTH command.
To configure static WEP keys, these are given to the
CONNECT or IBSS_JOIN command directly, for a userspace
SME it is assumed it will configure it properly after
the connection has been established.
Since mac80211 used to check the default key in IBSS
mode to see whether or not the network is protected,
it needs an update in that area, as well as an update
to make use of the WEP key passed to auth() for shared
key authentication.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2009-07-08 14:22:54 +02:00
|
|
|
}
|
|
|
|
|
2012-01-20 13:55:27 +01:00
|
|
|
auth_data->algorithm = auth_alg;
|
2008-09-08 17:44:22 +02:00
|
|
|
|
2012-01-20 13:55:27 +01:00
|
|
|
/* try to authenticate/probe */
|
2011-08-17 14:18:15 +02:00
|
|
|
|
2012-01-20 13:55:27 +01:00
|
|
|
if ((ifmgd->auth_data && !ifmgd->auth_data->done) ||
|
|
|
|
ifmgd->assoc_data) {
|
|
|
|
err = -EBUSY;
|
|
|
|
goto err_free;
|
2011-08-17 14:18:15 +02:00
|
|
|
}
|
|
|
|
|
2012-01-20 13:55:27 +01:00
|
|
|
if (ifmgd->auth_data)
|
|
|
|
ieee80211_destroy_auth_data(sdata, false);
|
2011-08-17 14:18:15 +02:00
|
|
|
|
2012-01-20 13:55:27 +01:00
|
|
|
/* prep auth_data so we don't go into idle on disassoc */
|
|
|
|
ifmgd->auth_data = auth_data;
|
2009-12-23 13:15:35 +01:00
|
|
|
|
mac80211: fix cfg80211 interaction on auth/assoc request
If authentication (or association with FT) is requested by
userspace, mac80211 currently doesn't tell cfg80211 that it
disconnected from the AP. That leaves inconsistent state:
cfg80211 thinks it's connected while mac80211 thinks it's
not. Typically this won't last long, as soon as mac80211
reports the new association to cfg80211 the old one goes
away. If, however, the new authentication or association
doesn't succeed, then cfg80211 will forever think the old
one still exists and will refuse attempts to authenticate
or associate with the AP it thinks it's connected to.
Anders reported that this leads to it taking a very long
time to reconnect to a network, or never even succeeding.
I tested this with an AP hacked to never respond to auth
frames, and one that works, and with just those two the
system never recovers because one won't work and cfg80211
thinks it's connected to the other so refuses connections
to it.
To fix this, simply make mac80211 tell cfg80211 when it is
no longer connected to the old AP, while authenticating or
associating to a new one.
Cc: stable@vger.kernel.org
Reported-by: Anders Kaseorg <andersk@mit.edu>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2013-04-10 21:38:36 +02:00
|
|
|
if (ifmgd->associated) {
|
|
|
|
u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
|
|
|
|
|
|
|
|
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
|
|
|
|
WLAN_REASON_UNSPECIFIED,
|
|
|
|
false, frame_buf);
|
|
|
|
|
2015-03-16 22:23:37 +01:00
|
|
|
ieee80211_report_disconnect(sdata, frame_buf,
|
|
|
|
sizeof(frame_buf), true,
|
|
|
|
WLAN_REASON_UNSPECIFIED);
|
mac80211: fix cfg80211 interaction on auth/assoc request
If authentication (or association with FT) is requested by
userspace, mac80211 currently doesn't tell cfg80211 that it
disconnected from the AP. That leaves inconsistent state:
cfg80211 thinks it's connected while mac80211 thinks it's
not. Typically this won't last long, as soon as mac80211
reports the new association to cfg80211 the old one goes
away. If, however, the new authentication or association
doesn't succeed, then cfg80211 will forever think the old
one still exists and will refuse attempts to authenticate
or associate with the AP it thinks it's connected to.
Anders reported that this leads to it taking a very long
time to reconnect to a network, or never even succeeding.
I tested this with an AP hacked to never respond to auth
frames, and one that works, and with just those two the
system never recovers because one won't work and cfg80211
thinks it's connected to the other so refuses connections
to it.
To fix this, simply make mac80211 tell cfg80211 when it is
no longer connected to the old AP, while authenticating or
associating to a new one.
Cc: stable@vger.kernel.org
Reported-by: Anders Kaseorg <andersk@mit.edu>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2013-04-10 21:38:36 +02:00
|
|
|
}
|
2009-12-23 13:15:35 +01:00
|
|
|
|
2012-06-22 11:29:50 +02:00
|
|
|
sdata_info(sdata, "authenticate with %pM\n", req->bss->bssid);
|
2010-07-29 16:08:55 +02:00
|
|
|
|
2015-04-20 21:51:46 +02:00
|
|
|
err = ieee80211_prep_connection(sdata, req->bss, false, false);
|
2012-03-08 15:02:07 +01:00
|
|
|
if (err)
|
2012-01-20 13:55:27 +01:00
|
|
|
goto err_clear;
|
2009-12-23 13:15:35 +01:00
|
|
|
|
2012-01-20 13:55:27 +01:00
|
|
|
err = ieee80211_probe_auth(sdata);
|
|
|
|
if (err) {
|
|
|
|
sta_info_destroy_addr(sdata, req->bss->bssid);
|
|
|
|
goto err_clear;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* hold our own reference */
|
2013-02-01 01:49:58 +01:00
|
|
|
cfg80211_ref_bss(local->hw.wiphy, auth_data->bss);
|
2013-05-10 12:32:47 +02:00
|
|
|
return 0;
|
2012-01-20 13:55:27 +01:00
|
|
|
|
|
|
|
err_clear:
|
2015-03-03 04:54:57 +01:00
|
|
|
eth_zero_addr(ifmgd->bssid);
|
2012-09-04 16:44:45 +02:00
|
|
|
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
|
2012-01-20 13:55:27 +01:00
|
|
|
ifmgd->auth_data = NULL;
|
2015-06-03 08:36:13 +02:00
|
|
|
mutex_lock(&sdata->local->mtx);
|
|
|
|
ieee80211_vif_release_channel(sdata);
|
|
|
|
mutex_unlock(&sdata->local->mtx);
|
2012-01-20 13:55:27 +01:00
|
|
|
err_free:
|
|
|
|
kfree(auth_data);
|
|
|
|
return err;
|
2009-12-23 13:15:35 +01:00
|
|
|
}
|
|
|
|
|
2013-10-15 12:25:07 +02:00
|
|
|
static bool ieee80211_usable_wmm_params(struct ieee80211_sub_if_data *sdata,
|
|
|
|
const u8 *wmm_param, int len)
|
|
|
|
{
|
|
|
|
const u8 *pos;
|
|
|
|
size_t left;
|
|
|
|
|
|
|
|
if (len < 8)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (wmm_param[5] != 1 /* version */)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
pos = wmm_param + 8;
|
|
|
|
left = len - 8;
|
|
|
|
|
|
|
|
for (; left >= 4; left -= 4, pos += 4) {
|
|
|
|
u8 aifsn = pos[0] & 0x0f;
|
|
|
|
u8 ecwmin = pos[1] & 0x0f;
|
|
|
|
u8 ecwmax = (pos[1] & 0xf0) >> 4;
|
|
|
|
int aci = (pos[0] >> 5) & 0x03;
|
|
|
|
|
|
|
|
if (aifsn < 2) {
|
|
|
|
sdata_info(sdata,
|
|
|
|
"AP has invalid WMM params (AIFSN=%d for ACI %d), disabling WMM\n",
|
|
|
|
aifsn, aci);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (ecwmin > ecwmax) {
|
|
|
|
sdata_info(sdata,
|
|
|
|
"AP has invalid WMM params (ECWmin/max=%d/%d for ACI %d), disabling WMM\n",
|
|
|
|
ecwmin, ecwmax, aci);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2009-07-07 03:45:17 +02:00
|
|
|
int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct cfg80211_assoc_request *req)
|
2007-05-05 20:45:53 +02:00
|
|
|
{
|
2012-01-20 13:55:27 +01:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
2009-07-07 03:45:17 +02:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2009-12-23 13:15:39 +01:00
|
|
|
struct ieee80211_bss *bss = (void *)req->bss->priv;
|
2012-01-20 13:55:27 +01:00
|
|
|
struct ieee80211_mgd_assoc_data *assoc_data;
|
2012-12-12 09:12:24 +01:00
|
|
|
const struct cfg80211_bss_ies *beacon_ies;
|
2012-03-08 15:02:04 +01:00
|
|
|
struct ieee80211_supported_band *sband;
|
2012-12-07 13:06:48 +01:00
|
|
|
const u8 *ssidie, *ht_ie, *vht_ie;
|
2011-08-17 14:18:15 +02:00
|
|
|
int i, err;
|
2015-04-20 21:51:46 +02:00
|
|
|
bool override = false;
|
2007-05-05 20:45:53 +02:00
|
|
|
|
2012-01-20 13:55:27 +01:00
|
|
|
assoc_data = kzalloc(sizeof(*assoc_data) + req->ie_len, GFP_KERNEL);
|
|
|
|
if (!assoc_data)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2012-11-29 01:25:20 +01:00
|
|
|
rcu_read_lock();
|
|
|
|
ssidie = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID);
|
|
|
|
if (!ssidie) {
|
|
|
|
rcu_read_unlock();
|
|
|
|
kfree(assoc_data);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
memcpy(assoc_data->ssid, ssidie + 2, ssidie[1]);
|
|
|
|
assoc_data->ssid_len = ssidie[1];
|
|
|
|
rcu_read_unlock();
|
|
|
|
|
mac80211: fix cfg80211 interaction on auth/assoc request
If authentication (or association with FT) is requested by
userspace, mac80211 currently doesn't tell cfg80211 that it
disconnected from the AP. That leaves inconsistent state:
cfg80211 thinks it's connected while mac80211 thinks it's
not. Typically this won't last long, as soon as mac80211
reports the new association to cfg80211 the old one goes
away. If, however, the new authentication or association
doesn't succeed, then cfg80211 will forever think the old
one still exists and will refuse attempts to authenticate
or associate with the AP it thinks it's connected to.
Anders reported that this leads to it taking a very long
time to reconnect to a network, or never even succeeding.
I tested this with an AP hacked to never respond to auth
frames, and one that works, and with just those two the
system never recovers because one won't work and cfg80211
thinks it's connected to the other so refuses connections
to it.
To fix this, simply make mac80211 tell cfg80211 when it is
no longer connected to the old AP, while authenticating or
associating to a new one.
Cc: stable@vger.kernel.org
Reported-by: Anders Kaseorg <andersk@mit.edu>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2013-04-10 21:38:36 +02:00
|
|
|
if (ifmgd->associated) {
|
|
|
|
u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
|
|
|
|
|
|
|
|
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
|
|
|
|
WLAN_REASON_UNSPECIFIED,
|
|
|
|
false, frame_buf);
|
|
|
|
|
2015-03-16 22:23:37 +01:00
|
|
|
ieee80211_report_disconnect(sdata, frame_buf,
|
|
|
|
sizeof(frame_buf), true,
|
|
|
|
WLAN_REASON_UNSPECIFIED);
|
mac80211: fix cfg80211 interaction on auth/assoc request
If authentication (or association with FT) is requested by
userspace, mac80211 currently doesn't tell cfg80211 that it
disconnected from the AP. That leaves inconsistent state:
cfg80211 thinks it's connected while mac80211 thinks it's
not. Typically this won't last long, as soon as mac80211
reports the new association to cfg80211 the old one goes
away. If, however, the new authentication or association
doesn't succeed, then cfg80211 will forever think the old
one still exists and will refuse attempts to authenticate
or associate with the AP it thinks it's connected to.
Anders reported that this leads to it taking a very long
time to reconnect to a network, or never even succeeding.
I tested this with an AP hacked to never respond to auth
frames, and one that works, and with just those two the
system never recovers because one won't work and cfg80211
thinks it's connected to the other so refuses connections
to it.
To fix this, simply make mac80211 tell cfg80211 when it is
no longer connected to the old AP, while authenticating or
associating to a new one.
Cc: stable@vger.kernel.org
Reported-by: Anders Kaseorg <andersk@mit.edu>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2013-04-10 21:38:36 +02:00
|
|
|
}
|
2012-01-20 13:55:27 +01:00
|
|
|
|
|
|
|
if (ifmgd->auth_data && !ifmgd->auth_data->done) {
|
|
|
|
err = -EBUSY;
|
|
|
|
goto err_free;
|
2009-12-23 13:15:35 +01:00
|
|
|
}
|
2009-07-07 03:45:17 +02:00
|
|
|
|
2012-01-20 13:55:27 +01:00
|
|
|
if (ifmgd->assoc_data) {
|
|
|
|
err = -EBUSY;
|
|
|
|
goto err_free;
|
|
|
|
}
|
2009-07-07 03:45:17 +02:00
|
|
|
|
2012-01-20 13:55:27 +01:00
|
|
|
if (ifmgd->auth_data) {
|
|
|
|
bool match;
|
|
|
|
|
|
|
|
/* keep sta info, bssid if matching */
|
mac80211: Convert compare_ether_addr to ether_addr_equal
Use the new bool function ether_addr_equal to add
some clarity and reduce the likelihood for misuse
of compare_ether_addr for sorting.
Done via cocci script:
$ cat compare_ether_addr.cocci
@@
expression a,b;
@@
- !compare_ether_addr(a, b)
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- compare_ether_addr(a, b)
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) == 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !ether_addr_equal(a, b) != 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) == 0
+ !ether_addr_equal(a, b)
@@
expression a,b;
@@
- ether_addr_equal(a, b) != 0
+ ether_addr_equal(a, b)
@@
expression a,b;
@@
- !!ether_addr_equal(a, b)
+ ether_addr_equal(a, b)
Signed-off-by: Joe Perches <joe@perches.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2012-05-08 20:56:52 +02:00
|
|
|
match = ether_addr_equal(ifmgd->bssid, req->bss->bssid);
|
2012-01-20 13:55:27 +01:00
|
|
|
ieee80211_destroy_auth_data(sdata, match);
|
2011-08-17 14:18:15 +02:00
|
|
|
}
|
|
|
|
|
2012-01-20 13:55:27 +01:00
|
|
|
/* prepare assoc data */
|
2013-10-15 12:25:07 +02:00
|
|
|
|
mac80211: Add validity check for beacon_crc value
On association to an AP, after receiving beacons, the beacon_crc value is set.
The beacon_crc value is not reset in disassociation, but the BSS data may be
expired at a later point. When associating again, it's possible that a
beacon for the AP is not received, resulting in the beacon_ies to remain NULL.
After association, further beacons will not update the beacon data, as the
crc value of the beacon has not changed, and the beacon_crc still holds a
value matching the beacon. The beacon_ies will remain forever null.
One of the results of this is that WLAN power save cannot be entered, the STA
will remain foreven in active mode.
Fix this by adding a validation flag for the beacon_crc, which is cleared on
association.
Signed-off-by: Juuso Oikarinen <juuso.oikarinen@nokia.com>
Acked-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2010-10-01 15:02:31 +02:00
|
|
|
ifmgd->beacon_crc_valid = false;
|
|
|
|
|
2013-10-15 12:25:07 +02:00
|
|
|
assoc_data->wmm = bss->wmm_used &&
|
|
|
|
(local->hw.queues >= IEEE80211_NUM_ACS);
|
|
|
|
if (assoc_data->wmm) {
|
|
|
|
/* try to check validity of WMM params IE */
|
|
|
|
const struct cfg80211_bss_ies *ies;
|
|
|
|
const u8 *wp, *start, *end;
|
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
ies = rcu_dereference(req->bss->ies);
|
|
|
|
start = ies->data;
|
|
|
|
end = start + ies->len;
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
wp = cfg80211_find_vendor_ie(
|
|
|
|
WLAN_OUI_MICROSOFT,
|
|
|
|
WLAN_OUI_TYPE_MICROSOFT_WMM,
|
|
|
|
start, end - start);
|
|
|
|
if (!wp)
|
|
|
|
break;
|
|
|
|
start = wp + wp[1] + 2;
|
|
|
|
/* if this IE is too short, try the next */
|
|
|
|
if (wp[1] <= 4)
|
|
|
|
continue;
|
|
|
|
/* if this IE is WMM params, we found what we wanted */
|
|
|
|
if (wp[6] == 1)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!wp || !ieee80211_usable_wmm_params(sdata, wp + 2,
|
|
|
|
wp[1] - 2)) {
|
|
|
|
assoc_data->wmm = false;
|
|
|
|
ifmgd->flags |= IEEE80211_STA_DISABLE_WMM;
|
|
|
|
}
|
|
|
|
rcu_read_unlock();
|
|
|
|
}
|
|
|
|
|
2012-03-08 15:02:03 +01:00
|
|
|
/*
|
|
|
|
* IEEE802.11n does not allow TKIP/WEP as pairwise ciphers in HT mode.
|
|
|
|
* We still associate in non-HT mode (11a/b/g) if any one of these
|
|
|
|
* ciphers is configured as pairwise.
|
|
|
|
* We can set this to true for non-11n hardware, that'll be checked
|
|
|
|
* separately along with the peer capabilities.
|
|
|
|
*/
|
2012-05-30 15:57:00 +02:00
|
|
|
for (i = 0; i < req->crypto.n_ciphers_pairwise; i++) {
|
2009-07-07 03:45:17 +02:00
|
|
|
if (req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP40 ||
|
|
|
|
req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_TKIP ||
|
2012-05-30 15:57:00 +02:00
|
|
|
req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104) {
|
2012-11-22 14:32:09 +01:00
|
|
|
ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
|
2012-07-24 05:33:10 +02:00
|
|
|
ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
|
2012-05-30 15:57:00 +02:00
|
|
|
netdev_info(sdata->dev,
|
2012-07-24 05:33:10 +02:00
|
|
|
"disabling HT/VHT due to WEP/TKIP use\n");
|
2012-05-30 15:57:00 +02:00
|
|
|
}
|
|
|
|
}
|
2009-07-07 03:45:17 +02:00
|
|
|
|
2012-03-08 15:02:04 +01:00
|
|
|
/* Also disable HT if we don't support it or the AP doesn't use WMM */
|
|
|
|
sband = local->hw.wiphy->bands[req->bss->channel->band];
|
|
|
|
if (!sband->ht_cap.ht_supported ||
|
2013-10-15 12:25:07 +02:00
|
|
|
local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used ||
|
|
|
|
ifmgd->flags & IEEE80211_STA_DISABLE_WMM) {
|
2012-11-22 14:32:09 +01:00
|
|
|
ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
|
2013-10-15 12:25:07 +02:00
|
|
|
if (!bss->wmm_used &&
|
|
|
|
!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM))
|
2012-07-27 10:29:14 +02:00
|
|
|
netdev_info(sdata->dev,
|
|
|
|
"disabling HT as WMM/QoS is not supported by the AP\n");
|
2012-05-30 15:57:00 +02:00
|
|
|
}
|
2012-03-08 15:02:04 +01:00
|
|
|
|
2012-07-24 05:33:10 +02:00
|
|
|
/* disable VHT if we don't support it or the AP doesn't use WMM */
|
|
|
|
if (!sband->vht_cap.vht_supported ||
|
2013-10-15 12:25:07 +02:00
|
|
|
local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used ||
|
|
|
|
ifmgd->flags & IEEE80211_STA_DISABLE_WMM) {
|
2012-07-24 05:33:10 +02:00
|
|
|
ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
|
2013-10-15 12:25:07 +02:00
|
|
|
if (!bss->wmm_used &&
|
|
|
|
!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM))
|
2012-07-27 10:29:14 +02:00
|
|
|
netdev_info(sdata->dev,
|
|
|
|
"disabling VHT as WMM/QoS is not supported by the AP\n");
|
2012-07-24 05:33:10 +02:00
|
|
|
}
|
|
|
|
|
2011-11-18 20:32:00 +01:00
|
|
|
memcpy(&ifmgd->ht_capa, &req->ht_capa, sizeof(ifmgd->ht_capa));
|
|
|
|
memcpy(&ifmgd->ht_capa_mask, &req->ht_capa_mask,
|
|
|
|
sizeof(ifmgd->ht_capa_mask));
|
|
|
|
|
2013-02-21 17:40:19 +01:00
|
|
|
memcpy(&ifmgd->vht_capa, &req->vht_capa, sizeof(ifmgd->vht_capa));
|
|
|
|
memcpy(&ifmgd->vht_capa_mask, &req->vht_capa_mask,
|
|
|
|
sizeof(ifmgd->vht_capa_mask));
|
|
|
|
|
2009-07-07 03:45:17 +02:00
|
|
|
if (req->ie && req->ie_len) {
|
2012-01-20 13:55:27 +01:00
|
|
|
memcpy(assoc_data->ie, req->ie, req->ie_len);
|
|
|
|
assoc_data->ie_len = req->ie_len;
|
|
|
|
}
|
2009-12-23 13:15:34 +01:00
|
|
|
|
2012-01-20 13:55:27 +01:00
|
|
|
assoc_data->bss = req->bss;
|
2009-12-23 13:15:34 +01:00
|
|
|
|
2009-12-23 13:15:35 +01:00
|
|
|
if (ifmgd->req_smps == IEEE80211_SMPS_AUTOMATIC) {
|
|
|
|
if (ifmgd->powersave)
|
2012-09-11 14:34:12 +02:00
|
|
|
sdata->smps_mode = IEEE80211_SMPS_DYNAMIC;
|
2009-12-23 13:15:35 +01:00
|
|
|
else
|
2012-09-11 14:34:12 +02:00
|
|
|
sdata->smps_mode = IEEE80211_SMPS_OFF;
|
2009-12-23 13:15:35 +01:00
|
|
|
} else
|
2012-09-11 14:34:12 +02:00
|
|
|
sdata->smps_mode = ifmgd->req_smps;
|
2009-12-23 13:15:35 +01:00
|
|
|
|
2012-01-20 13:55:27 +01:00
|
|
|
assoc_data->capability = req->bss->capability;
|
|
|
|
assoc_data->supp_rates = bss->supp_rates;
|
|
|
|
assoc_data->supp_rates_len = bss->supp_rates_len;
|
2012-05-16 23:43:19 +02:00
|
|
|
|
2012-11-29 01:25:20 +01:00
|
|
|
rcu_read_lock();
|
2012-05-16 23:43:19 +02:00
|
|
|
ht_ie = ieee80211_bss_get_ie(req->bss, WLAN_EID_HT_OPERATION);
|
|
|
|
if (ht_ie && ht_ie[1] >= sizeof(struct ieee80211_ht_operation))
|
|
|
|
assoc_data->ap_ht_param =
|
|
|
|
((struct ieee80211_ht_operation *)(ht_ie + 2))->ht_param;
|
|
|
|
else
|
2012-11-22 14:32:09 +01:00
|
|
|
ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
|
2012-12-07 13:06:48 +01:00
|
|
|
vht_ie = ieee80211_bss_get_ie(req->bss, WLAN_EID_VHT_CAPABILITY);
|
|
|
|
if (vht_ie && vht_ie[1] >= sizeof(struct ieee80211_vht_cap))
|
|
|
|
memcpy(&assoc_data->ap_vht_cap, vht_ie + 2,
|
|
|
|
sizeof(struct ieee80211_vht_cap));
|
|
|
|
else
|
|
|
|
ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
|
2012-11-29 01:25:20 +01:00
|
|
|
rcu_read_unlock();
|
2009-12-23 13:15:33 +01:00
|
|
|
|
2014-11-11 12:48:42 +01:00
|
|
|
if (WARN((sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_UAPSD) &&
|
2015-06-02 21:39:54 +02:00
|
|
|
ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK),
|
2014-11-11 12:48:42 +01:00
|
|
|
"U-APSD not supported with HW_PS_NULLFUNC_STACK\n"))
|
|
|
|
sdata->vif.driver_flags &= ~IEEE80211_VIF_SUPPORTS_UAPSD;
|
|
|
|
|
2010-01-12 09:42:31 +01:00
|
|
|
if (bss->wmm_used && bss->uapsd_supported &&
|
2014-11-11 12:48:42 +01:00
|
|
|
(sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_UAPSD)) {
|
2012-03-08 15:02:05 +01:00
|
|
|
assoc_data->uapsd = true;
|
2010-01-12 09:42:31 +01:00
|
|
|
ifmgd->flags |= IEEE80211_STA_UAPSD_ENABLED;
|
|
|
|
} else {
|
2012-03-08 15:02:05 +01:00
|
|
|
assoc_data->uapsd = false;
|
2010-01-12 09:42:31 +01:00
|
|
|
ifmgd->flags &= ~IEEE80211_STA_UAPSD_ENABLED;
|
|
|
|
}
|
|
|
|
|
2009-07-07 03:45:17 +02:00
|
|
|
if (req->prev_bssid)
|
2012-01-20 13:55:27 +01:00
|
|
|
memcpy(assoc_data->prev_bssid, req->prev_bssid, ETH_ALEN);
|
2009-07-07 03:45:17 +02:00
|
|
|
|
|
|
|
if (req->use_mfp) {
|
|
|
|
ifmgd->mfp = IEEE80211_MFP_REQUIRED;
|
|
|
|
ifmgd->flags |= IEEE80211_STA_MFP_ENABLED;
|
|
|
|
} else {
|
|
|
|
ifmgd->mfp = IEEE80211_MFP_DISABLED;
|
|
|
|
ifmgd->flags &= ~IEEE80211_STA_MFP_ENABLED;
|
|
|
|
}
|
|
|
|
|
2014-09-03 14:25:02 +02:00
|
|
|
if (req->flags & ASSOC_REQ_USE_RRM)
|
|
|
|
ifmgd->flags |= IEEE80211_STA_ENABLE_RRM;
|
|
|
|
else
|
|
|
|
ifmgd->flags &= ~IEEE80211_STA_ENABLE_RRM;
|
|
|
|
|
2009-07-07 03:45:17 +02:00
|
|
|
if (req->crypto.control_port)
|
|
|
|
ifmgd->flags |= IEEE80211_STA_CONTROL_PORT;
|
|
|
|
else
|
|
|
|
ifmgd->flags &= ~IEEE80211_STA_CONTROL_PORT;
|
|
|
|
|
2010-08-27 13:26:54 +02:00
|
|
|
sdata->control_port_protocol = req->crypto.control_port_ethertype;
|
|
|
|
sdata->control_port_no_encrypt = req->crypto.control_port_no_encrypt;
|
2013-03-24 13:23:27 +01:00
|
|
|
sdata->encrypt_headroom = ieee80211_cs_headroom(local, &req->crypto,
|
|
|
|
sdata->vif.type);
|
2010-08-27 13:26:54 +02:00
|
|
|
|
2012-01-20 13:55:27 +01:00
|
|
|
/* kick off associate process */
|
|
|
|
|
|
|
|
ifmgd->assoc_data = assoc_data;
|
2012-12-10 15:38:14 +01:00
|
|
|
ifmgd->dtim_period = 0;
|
2013-05-16 16:34:17 +02:00
|
|
|
ifmgd->have_beacon = false;
|
2012-01-20 13:55:27 +01:00
|
|
|
|
2015-04-20 21:51:46 +02:00
|
|
|
/* override HT/VHT configuration only if the AP and we support it */
|
|
|
|
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) {
|
|
|
|
struct ieee80211_sta_ht_cap sta_ht_cap;
|
|
|
|
|
|
|
|
if (req->flags & ASSOC_REQ_DISABLE_HT)
|
|
|
|
override = true;
|
|
|
|
|
|
|
|
memcpy(&sta_ht_cap, &sband->ht_cap, sizeof(sta_ht_cap));
|
|
|
|
ieee80211_apply_htcap_overrides(sdata, &sta_ht_cap);
|
|
|
|
|
|
|
|
/* check for 40 MHz disable override */
|
|
|
|
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_40MHZ) &&
|
|
|
|
sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 &&
|
|
|
|
!(sta_ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40))
|
|
|
|
override = true;
|
|
|
|
|
|
|
|
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT) &&
|
|
|
|
req->flags & ASSOC_REQ_DISABLE_VHT)
|
|
|
|
override = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (req->flags & ASSOC_REQ_DISABLE_HT) {
|
|
|
|
ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
|
|
|
|
ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (req->flags & ASSOC_REQ_DISABLE_VHT)
|
|
|
|
ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
|
|
|
|
|
|
|
|
err = ieee80211_prep_connection(sdata, req->bss, true, override);
|
2012-03-08 15:02:07 +01:00
|
|
|
if (err)
|
|
|
|
goto err_clear;
|
2012-01-20 13:55:27 +01:00
|
|
|
|
2012-12-12 09:12:24 +01:00
|
|
|
rcu_read_lock();
|
|
|
|
beacon_ies = rcu_dereference(req->bss->beacon_ies);
|
2012-12-10 15:38:14 +01:00
|
|
|
|
2015-06-02 21:39:54 +02:00
|
|
|
if (ieee80211_hw_check(&sdata->local->hw, NEED_DTIM_BEFORE_ASSOC) &&
|
2012-12-12 09:12:24 +01:00
|
|
|
!beacon_ies) {
|
|
|
|
/*
|
|
|
|
* Wait up to one beacon interval ...
|
|
|
|
* should this be more if we miss one?
|
|
|
|
*/
|
|
|
|
sdata_info(sdata, "waiting for beacon from %pM\n",
|
|
|
|
ifmgd->bssid);
|
|
|
|
assoc_data->timeout = TU_TO_EXP_TIME(req->bss->beacon_interval);
|
2013-02-13 15:39:57 +01:00
|
|
|
assoc_data->timeout_started = true;
|
2012-12-12 09:12:24 +01:00
|
|
|
assoc_data->need_beacon = true;
|
|
|
|
} else if (beacon_ies) {
|
|
|
|
const u8 *tim_ie = cfg80211_find_ie(WLAN_EID_TIM,
|
|
|
|
beacon_ies->data,
|
|
|
|
beacon_ies->len);
|
2013-02-05 17:48:40 +01:00
|
|
|
u8 dtim_count = 0;
|
|
|
|
|
2012-12-12 09:12:24 +01:00
|
|
|
if (tim_ie && tim_ie[1] >= sizeof(struct ieee80211_tim_ie)) {
|
|
|
|
const struct ieee80211_tim_ie *tim;
|
|
|
|
tim = (void *)(tim_ie + 2);
|
|
|
|
ifmgd->dtim_period = tim->dtim_period;
|
2013-02-05 17:48:40 +01:00
|
|
|
dtim_count = tim->dtim_count;
|
2012-12-10 15:38:14 +01:00
|
|
|
}
|
2013-05-16 16:34:17 +02:00
|
|
|
ifmgd->have_beacon = true;
|
2012-01-20 13:55:27 +01:00
|
|
|
assoc_data->timeout = jiffies;
|
2013-02-13 15:39:57 +01:00
|
|
|
assoc_data->timeout_started = true;
|
2013-02-05 17:48:40 +01:00
|
|
|
|
2015-06-02 21:39:54 +02:00
|
|
|
if (ieee80211_hw_check(&local->hw, TIMING_BEACON_ONLY)) {
|
2013-02-05 17:48:40 +01:00
|
|
|
sdata->vif.bss_conf.sync_tsf = beacon_ies->tsf;
|
|
|
|
sdata->vif.bss_conf.sync_device_ts =
|
|
|
|
bss->device_ts_beacon;
|
|
|
|
sdata->vif.bss_conf.sync_dtim_count = dtim_count;
|
|
|
|
}
|
2012-12-12 09:12:24 +01:00
|
|
|
} else {
|
|
|
|
assoc_data->timeout = jiffies;
|
2013-02-13 15:39:57 +01:00
|
|
|
assoc_data->timeout_started = true;
|
2012-01-20 13:55:27 +01:00
|
|
|
}
|
2012-12-12 09:12:24 +01:00
|
|
|
rcu_read_unlock();
|
|
|
|
|
2013-05-10 12:32:47 +02:00
|
|
|
run_again(sdata, assoc_data->timeout);
|
2012-01-20 13:55:27 +01:00
|
|
|
|
mac80211: Filter duplicate IE ids
mac80211 is lenient with respect to reception of corrupted beacons.
Even if the frame is corrupted as a whole, the available IE elements
are still passed back and accepted, sometimes replacing legitimate
data. It is unknown to what extent this "feature" is made use of,
but it is clear that in some cases, this is detrimental. One such
case is reported in http://crosbug.com/26832 where an AP corrupts
its beacons but not its probe responses.
One approach would be to completely reject frames with invaid data
(for example, if the last tag extends beyond the end of the enclosing
PDU). The enclosed approach is much more conservative: we simply
prevent later IEs from overwriting the state from previous ones.
This approach hopes that there might be some salient data in the
IE stream before the corruption, and seeks to at least prevent that
data from being overwritten. This approach will fix the case above.
Further, we flag element structures that contain data we think might
be corrupted, so that as we fill the mac80211 BSS structure, we try
not to replace data from an un-corrupted probe response with that
of a corrupted beacon, for example.
Short of any statistics gathering in the various forms of AP breakage,
it's not possible to ascertain the side effects of more stringent
discarding of data.
Signed-off-by: Paul Stewart <pstew@chromium.org>
Cc: Sam Leffler <sleffler@chromium.org>
Cc: Eliad Peller <eliad@wizery.com>
Acked-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-02-24 02:59:53 +01:00
|
|
|
if (bss->corrupt_data) {
|
|
|
|
char *corrupt_type = "data";
|
|
|
|
if (bss->corrupt_data & IEEE80211_BSS_CORRUPT_BEACON) {
|
|
|
|
if (bss->corrupt_data &
|
|
|
|
IEEE80211_BSS_CORRUPT_PROBE_RESP)
|
|
|
|
corrupt_type = "beacon and probe response";
|
|
|
|
else
|
|
|
|
corrupt_type = "beacon";
|
|
|
|
} else if (bss->corrupt_data & IEEE80211_BSS_CORRUPT_PROBE_RESP)
|
|
|
|
corrupt_type = "probe response";
|
2012-06-22 11:29:50 +02:00
|
|
|
sdata_info(sdata, "associating with AP with corrupt %s\n",
|
|
|
|
corrupt_type);
|
mac80211: Filter duplicate IE ids
mac80211 is lenient with respect to reception of corrupted beacons.
Even if the frame is corrupted as a whole, the available IE elements
are still passed back and accepted, sometimes replacing legitimate
data. It is unknown to what extent this "feature" is made use of,
but it is clear that in some cases, this is detrimental. One such
case is reported in http://crosbug.com/26832 where an AP corrupts
its beacons but not its probe responses.
One approach would be to completely reject frames with invaid data
(for example, if the last tag extends beyond the end of the enclosing
PDU). The enclosed approach is much more conservative: we simply
prevent later IEs from overwriting the state from previous ones.
This approach hopes that there might be some salient data in the
IE stream before the corruption, and seeks to at least prevent that
data from being overwritten. This approach will fix the case above.
Further, we flag element structures that contain data we think might
be corrupted, so that as we fill the mac80211 BSS structure, we try
not to replace data from an un-corrupted probe response with that
of a corrupted beacon, for example.
Short of any statistics gathering in the various forms of AP breakage,
it's not possible to ascertain the side effects of more stringent
discarding of data.
Signed-off-by: Paul Stewart <pstew@chromium.org>
Cc: Sam Leffler <sleffler@chromium.org>
Cc: Eliad Peller <eliad@wizery.com>
Acked-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
2012-02-24 02:59:53 +01:00
|
|
|
}
|
|
|
|
|
2013-05-10 12:32:47 +02:00
|
|
|
return 0;
|
2012-01-20 13:55:27 +01:00
|
|
|
err_clear:
|
2015-03-03 04:54:57 +01:00
|
|
|
eth_zero_addr(ifmgd->bssid);
|
2012-09-04 16:44:45 +02:00
|
|
|
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
|
2012-01-20 13:55:27 +01:00
|
|
|
ifmgd->assoc_data = NULL;
|
|
|
|
err_free:
|
|
|
|
kfree(assoc_data);
|
|
|
|
return err;
|
2007-05-05 20:45:53 +02:00
|
|
|
}
|
|
|
|
|
2009-07-07 03:45:17 +02:00
|
|
|
int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
|
2012-02-24 13:50:51 +01:00
|
|
|
struct cfg80211_deauth_request *req)
|
2007-05-05 20:45:53 +02:00
|
|
|
{
|
2009-02-15 12:44:28 +01:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2012-09-07 13:28:52 +02:00
|
|
|
u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
|
2012-10-15 14:52:41 +02:00
|
|
|
bool tx = !req->local_state_change;
|
2007-05-05 20:45:53 +02:00
|
|
|
|
2014-03-19 09:11:19 +01:00
|
|
|
if (ifmgd->auth_data &&
|
|
|
|
ether_addr_equal(ifmgd->auth_data->bss->bssid, req->bssid)) {
|
|
|
|
sdata_info(sdata,
|
|
|
|
"aborting authentication with %pM by local choice (Reason: %u=%s)\n",
|
|
|
|
req->bssid, req->reason_code,
|
|
|
|
ieee80211_get_reason_code_string(req->reason_code));
|
2009-09-26 14:45:41 +02:00
|
|
|
|
2012-07-25 00:42:36 +02:00
|
|
|
drv_mgd_prepare_tx(sdata->local, sdata);
|
2012-02-24 13:50:54 +01:00
|
|
|
ieee80211_send_deauth_disassoc(sdata, req->bssid,
|
|
|
|
IEEE80211_STYPE_DEAUTH,
|
2012-10-15 14:52:41 +02:00
|
|
|
req->reason_code, tx,
|
2012-02-24 13:50:54 +01:00
|
|
|
frame_buf);
|
2012-10-29 09:46:31 +01:00
|
|
|
ieee80211_destroy_auth_data(sdata, false);
|
2015-03-16 22:23:37 +01:00
|
|
|
ieee80211_report_disconnect(sdata, frame_buf,
|
|
|
|
sizeof(frame_buf), true,
|
|
|
|
req->reason_code);
|
2012-10-29 09:46:31 +01:00
|
|
|
|
2014-03-19 09:11:19 +01:00
|
|
|
return 0;
|
2012-07-25 00:42:36 +02:00
|
|
|
}
|
|
|
|
|
2012-10-29 09:46:31 +01:00
|
|
|
if (ifmgd->associated &&
|
|
|
|
ether_addr_equal(ifmgd->associated->bssid, req->bssid)) {
|
2014-03-19 09:11:19 +01:00
|
|
|
sdata_info(sdata,
|
|
|
|
"deauthenticating from %pM by local choice (Reason: %u=%s)\n",
|
|
|
|
req->bssid, req->reason_code,
|
|
|
|
ieee80211_get_reason_code_string(req->reason_code));
|
|
|
|
|
2012-10-29 09:46:31 +01:00
|
|
|
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
|
|
|
|
req->reason_code, tx, frame_buf);
|
2015-03-16 22:23:37 +01:00
|
|
|
ieee80211_report_disconnect(sdata, frame_buf,
|
|
|
|
sizeof(frame_buf), true,
|
|
|
|
req->reason_code);
|
2014-03-19 09:11:19 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2012-10-29 09:46:31 +01:00
|
|
|
|
2014-03-19 09:11:19 +01:00
|
|
|
return -ENOTCONN;
|
2007-05-05 20:45:53 +02:00
|
|
|
}
|
2008-04-05 01:59:58 +02:00
|
|
|
|
2009-07-07 03:45:17 +02:00
|
|
|
int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
|
2012-02-24 13:50:51 +01:00
|
|
|
struct cfg80211_disassoc_request *req)
|
2008-09-11 00:01:52 +02:00
|
|
|
{
|
2009-07-07 03:45:17 +02:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2010-03-30 08:29:31 +02:00
|
|
|
u8 bssid[ETH_ALEN];
|
2012-09-07 13:28:52 +02:00
|
|
|
u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
|
2008-09-11 00:01:52 +02:00
|
|
|
|
2009-07-25 11:58:36 +02:00
|
|
|
/*
|
|
|
|
* cfg80211 should catch this ... but it's racy since
|
|
|
|
* we can receive a disassoc frame, process it, hand it
|
|
|
|
* to cfg80211 while that's in a locked section already
|
|
|
|
* trying to tell us that the user wants to disconnect.
|
|
|
|
*/
|
2013-05-10 12:32:47 +02:00
|
|
|
if (ifmgd->associated != req->bss)
|
2009-07-07 03:45:17 +02:00
|
|
|
return -ENOLINK;
|
|
|
|
|
2012-06-22 11:29:50 +02:00
|
|
|
sdata_info(sdata,
|
2014-02-11 19:36:24 +01:00
|
|
|
"disassociating from %pM by local choice (Reason: %u=%s)\n",
|
|
|
|
req->bss->bssid, req->reason_code, ieee80211_get_reason_code_string(req->reason_code));
|
2009-09-26 14:45:41 +02:00
|
|
|
|
2010-03-30 08:29:31 +02:00
|
|
|
memcpy(bssid, req->bss->bssid, ETH_ALEN);
|
2012-02-24 13:50:54 +01:00
|
|
|
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DISASSOC,
|
|
|
|
req->reason_code, !req->local_state_change,
|
|
|
|
frame_buf);
|
2009-04-16 13:17:25 +02:00
|
|
|
|
2015-03-16 22:23:37 +01:00
|
|
|
ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true,
|
|
|
|
req->reason_code);
|
2009-11-29 12:19:06 +01:00
|
|
|
|
2009-04-16 13:17:25 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2010-02-15 11:53:10 +01:00
|
|
|
|
2012-04-23 13:45:15 +02:00
|
|
|
void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata)
|
2012-02-25 21:48:08 +01:00
|
|
|
{
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
|
2013-02-20 18:41:09 +01:00
|
|
|
/*
|
|
|
|
* Make sure some work items will not run after this,
|
|
|
|
* they will not do anything but might not have been
|
|
|
|
* cancelled when disconnecting.
|
|
|
|
*/
|
|
|
|
cancel_work_sync(&ifmgd->monitor_work);
|
|
|
|
cancel_work_sync(&ifmgd->beacon_connection_loss_work);
|
|
|
|
cancel_work_sync(&ifmgd->request_smps_work);
|
|
|
|
cancel_work_sync(&ifmgd->csa_connection_drop_work);
|
|
|
|
cancel_work_sync(&ifmgd->chswitch_work);
|
2014-07-17 16:14:25 +02:00
|
|
|
cancel_delayed_work_sync(&ifmgd->tdls_peer_del_work);
|
2013-02-20 18:41:09 +01:00
|
|
|
|
2013-05-10 12:32:47 +02:00
|
|
|
sdata_lock(sdata);
|
2013-06-19 13:05:42 +02:00
|
|
|
if (ifmgd->assoc_data) {
|
|
|
|
struct cfg80211_bss *bss = ifmgd->assoc_data->bss;
|
2012-02-25 21:48:08 +01:00
|
|
|
ieee80211_destroy_assoc_data(sdata, false);
|
2013-06-19 13:05:42 +02:00
|
|
|
cfg80211_assoc_timeout(sdata->dev, bss);
|
|
|
|
}
|
2012-02-25 21:48:08 +01:00
|
|
|
if (ifmgd->auth_data)
|
|
|
|
ieee80211_destroy_auth_data(sdata, false);
|
2014-11-09 17:50:08 +01:00
|
|
|
spin_lock_bh(&ifmgd->teardown_lock);
|
|
|
|
if (ifmgd->teardown_skb) {
|
|
|
|
kfree_skb(ifmgd->teardown_skb);
|
|
|
|
ifmgd->teardown_skb = NULL;
|
|
|
|
ifmgd->orig_teardown_skb = NULL;
|
|
|
|
}
|
|
|
|
spin_unlock_bh(&ifmgd->teardown_lock);
|
2012-02-25 21:48:08 +01:00
|
|
|
del_timer_sync(&ifmgd->timer);
|
2013-05-10 12:32:47 +02:00
|
|
|
sdata_unlock(sdata);
|
2012-02-25 21:48:08 +01:00
|
|
|
}
|
|
|
|
|
2010-03-23 08:02:34 +01:00
|
|
|
void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif,
|
|
|
|
enum nl80211_cqm_rssi_threshold_event rssi_event,
|
|
|
|
gfp_t gfp)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
|
|
|
|
2010-04-07 16:48:40 +02:00
|
|
|
trace_api_cqm_rssi_notify(sdata, rssi_event);
|
|
|
|
|
2010-03-23 08:02:34 +01:00
|
|
|
cfg80211_cqm_rssi_notify(sdata->dev, rssi_event, gfp);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ieee80211_cqm_rssi_notify);
|
2014-11-26 12:42:02 +01:00
|
|
|
|
|
|
|
void ieee80211_cqm_beacon_loss_notify(struct ieee80211_vif *vif, gfp_t gfp)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
|
|
|
|
|
|
|
trace_api_cqm_beacon_loss_notify(sdata->local, sdata);
|
|
|
|
|
|
|
|
cfg80211_cqm_beacon_loss_notify(sdata->dev, gfp);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ieee80211_cqm_beacon_loss_notify);
|