2019-06-04 16:11:33 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2007-05-06 02:45:53 +08:00
|
|
|
/*
|
|
|
|
* BSS client mode implementation
|
2009-01-08 19:31:59 +08:00
|
|
|
* Copyright 2003-2008, Jouni Malinen <j@w1.fi>
|
2007-05-06 02:45:53 +08: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 20:24:57 +08:00
|
|
|
* Copyright 2013-2014 Intel Mobile Communications GmbH
|
2017-04-26 15:58:47 +08:00
|
|
|
* Copyright (C) 2015 - 2017 Intel Deutschland GmbH
|
2023-01-27 19:39:31 +08:00
|
|
|
* Copyright (C) 2018 - 2023 Intel Corporation
|
2007-05-06 02:45:53 +08:00
|
|
|
*/
|
|
|
|
|
2007-05-09 09:40:27 +08:00
|
|
|
#include <linux/delay.h>
|
2019-06-13 00:19:54 +08:00
|
|
|
#include <linux/fips.h>
|
2007-05-06 02:45:53 +08:00
|
|
|
#include <linux/if_ether.h>
|
|
|
|
#include <linux/skbuff.h>
|
|
|
|
#include <linux/if_arp.h>
|
|
|
|
#include <linux/etherdevice.h>
|
2011-09-19 01:21:27 +08:00
|
|
|
#include <linux/moduleparam.h>
|
2008-02-25 23:27:46 +08:00
|
|
|
#include <linux/rtnetlink.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 19:17:26 +08: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 16:04:11 +08:00
|
|
|
#include <linux/slab.h>
|
2011-07-15 23:47:34 +08:00
|
|
|
#include <linux/export.h>
|
2007-05-06 02:45:53 +08:00
|
|
|
#include <net/mac80211.h>
|
2008-09-11 06:01:49 +08:00
|
|
|
#include <asm/unaligned.h>
|
2008-09-08 23:44:22 +08:00
|
|
|
|
2007-05-06 02:45:53 +08:00
|
|
|
#include "ieee80211_i.h"
|
2009-04-24 00:52:52 +08:00
|
|
|
#include "driver-ops.h"
|
2008-04-09 03:14:40 +08:00
|
|
|
#include "rate.h"
|
|
|
|
#include "led.h"
|
2016-10-27 05:42:05 +08:00
|
|
|
#include "fils_aead.h"
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2013-01-29 22:02:27 +08:00
|
|
|
#define IEEE80211_AUTH_TIMEOUT (HZ / 5)
|
2013-07-30 05:07:43 +08:00
|
|
|
#define IEEE80211_AUTH_TIMEOUT_LONG (HZ / 2)
|
2013-01-29 22:02:27 +08:00
|
|
|
#define IEEE80211_AUTH_TIMEOUT_SHORT (HZ / 10)
|
2018-04-20 18:49:20 +08:00
|
|
|
#define IEEE80211_AUTH_TIMEOUT_SAE (HZ * 2)
|
2013-01-29 22:02:27 +08:00
|
|
|
#define IEEE80211_AUTH_MAX_TRIES 3
|
|
|
|
#define IEEE80211_AUTH_WAIT_ASSOC (HZ * 5)
|
2022-02-24 17:39:34 +08:00
|
|
|
#define IEEE80211_AUTH_WAIT_SAE_RETRY (HZ * 2)
|
2013-01-29 22:02:27 +08:00
|
|
|
#define IEEE80211_ASSOC_TIMEOUT (HZ / 5)
|
2013-07-30 05:07:43 +08:00
|
|
|
#define IEEE80211_ASSOC_TIMEOUT_LONG (HZ / 2)
|
2013-01-29 22:02:27 +08:00
|
|
|
#define IEEE80211_ASSOC_TIMEOUT_SHORT (HZ / 10)
|
|
|
|
#define IEEE80211_ASSOC_MAX_TRIES 3
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2011-02-05 07:30:24 +08: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 21:29:03 +08:00
|
|
|
|
|
|
|
/*
|
2010-11-20 05:55:38 +08: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 21:29:03 +08:00
|
|
|
*/
|
2013-03-20 05:19:56 +08: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-20 05:55:38 +08:00
|
|
|
|
2009-07-10 21:29:03 +08:00
|
|
|
/*
|
|
|
|
* Time the connection can be idle before we probe
|
|
|
|
* it to see if we can still talk to the AP.
|
|
|
|
*/
|
2009-07-31 23:54:23 +08:00
|
|
|
#define IEEE80211_CONNECTION_IDLE_TIME (30 * HZ)
|
2009-07-10 21:29:03 +08: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 07:30:24 +08: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-06 02:45:53 +08:00
|
|
|
|
2010-08-28 03:22:00 +08: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
|
|
|
|
|
2023-01-27 19:39:31 +08:00
|
|
|
/*
|
|
|
|
* Extract from the given disabled subchannel bitmap (raw format
|
|
|
|
* from the EHT Operation Element) the bits for the subchannel
|
|
|
|
* we're using right now.
|
|
|
|
*/
|
|
|
|
static u16
|
|
|
|
ieee80211_extract_dis_subch_bmap(const struct ieee80211_eht_operation *eht_oper,
|
|
|
|
struct cfg80211_chan_def *chandef, u16 bitmap)
|
|
|
|
{
|
|
|
|
struct ieee80211_eht_operation_info *info = (void *)eht_oper->optional;
|
|
|
|
struct cfg80211_chan_def ap_chandef = *chandef;
|
|
|
|
u32 ap_center_freq, local_center_freq;
|
|
|
|
u32 ap_bw, local_bw;
|
|
|
|
int ap_start_freq, local_start_freq;
|
|
|
|
u16 shift, mask;
|
|
|
|
|
|
|
|
if (!(eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT) ||
|
|
|
|
!(eht_oper->params &
|
|
|
|
IEEE80211_EHT_OPER_DISABLED_SUBCHANNEL_BITMAP_PRESENT))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* set 160/320 supported to get the full AP definition */
|
|
|
|
ieee80211_chandef_eht_oper(eht_oper, true, true, &ap_chandef);
|
|
|
|
ap_center_freq = ap_chandef.center_freq1;
|
|
|
|
ap_bw = 20 * BIT(u8_get_bits(info->control,
|
|
|
|
IEEE80211_EHT_OPER_CHAN_WIDTH));
|
|
|
|
ap_start_freq = ap_center_freq - ap_bw / 2;
|
|
|
|
local_center_freq = chandef->center_freq1;
|
|
|
|
local_bw = 20 * BIT(ieee80211_chan_width_to_rx_bw(chandef->width));
|
|
|
|
local_start_freq = local_center_freq - local_bw / 2;
|
|
|
|
shift = (local_start_freq - ap_start_freq) / 20;
|
|
|
|
mask = BIT(local_bw / 20) - 1;
|
|
|
|
|
|
|
|
return (bitmap >> shift) & mask;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handle the puncturing bitmap, possibly downgrading bandwidth to get a
|
|
|
|
* valid bitmap.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
ieee80211_handle_puncturing_bitmap(struct ieee80211_link_data *link,
|
|
|
|
const struct ieee80211_eht_operation *eht_oper,
|
|
|
|
u16 bitmap, u64 *changed)
|
|
|
|
{
|
|
|
|
struct cfg80211_chan_def *chandef = &link->conf->chandef;
|
|
|
|
u16 extracted;
|
|
|
|
u64 _changed = 0;
|
|
|
|
|
|
|
|
if (!changed)
|
|
|
|
changed = &_changed;
|
|
|
|
|
|
|
|
while (chandef->width > NL80211_CHAN_WIDTH_40) {
|
|
|
|
extracted =
|
|
|
|
ieee80211_extract_dis_subch_bmap(eht_oper, chandef,
|
|
|
|
bitmap);
|
|
|
|
|
2023-01-31 08:12:24 +08:00
|
|
|
if (cfg80211_valid_disable_subchannel_bitmap(&bitmap,
|
|
|
|
chandef))
|
2023-01-27 19:39:31 +08:00
|
|
|
break;
|
|
|
|
link->u.mgd.conn_flags |=
|
|
|
|
ieee80211_chandef_downgrade(chandef);
|
|
|
|
*changed |= BSS_CHANGED_BANDWIDTH;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (chandef->width <= NL80211_CHAN_WIDTH_40)
|
|
|
|
extracted = 0;
|
|
|
|
|
|
|
|
if (link->conf->eht_puncturing != extracted) {
|
|
|
|
link->conf->eht_puncturing = extracted;
|
|
|
|
*changed |= BSS_CHANGED_EHT_PUNCTURING;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-07-10 08:39:48 +08: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 18:32:47 +08:00
|
|
|
static void run_again(struct ieee80211_sub_if_data *sdata,
|
|
|
|
unsigned long timeout)
|
2009-07-10 08:39:48 +08:00
|
|
|
{
|
2013-05-10 18:32:47 +08:00
|
|
|
sdata_assert_lock(sdata);
|
2009-07-10 08:39:48 +08:00
|
|
|
|
2013-05-10 18:32:47 +08: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 08:39:48 +08:00
|
|
|
}
|
|
|
|
|
2010-09-17 03:12:32 +08:00
|
|
|
void ieee80211_sta_reset_beacon_monitor(struct ieee80211_sub_if_data *sdata)
|
2009-07-10 21:29:03 +08:00
|
|
|
{
|
2012-01-19 16:29:57 +08:00
|
|
|
if (sdata->vif.driver_flags & IEEE80211_VIF_BEACON_FILTER)
|
2009-07-10 21:29:03 +08:00
|
|
|
return;
|
|
|
|
|
2015-06-03 03:39:54 +08:00
|
|
|
if (ieee80211_hw_check(&sdata->local->hw, CONNECTION_MONITOR))
|
2012-07-18 16:27:27 +08:00
|
|
|
return;
|
|
|
|
|
2009-07-10 21:29:03 +08:00
|
|
|
mod_timer(&sdata->u.mgd.bcn_mon_timer,
|
2010-11-20 05:55:38 +08:00
|
|
|
round_jiffies_up(jiffies + sdata->u.mgd.beacon_timeout));
|
2009-07-10 21:29:03 +08:00
|
|
|
}
|
|
|
|
|
2010-09-17 03:12:29 +08:00
|
|
|
void ieee80211_sta_reset_conn_monitor(struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
2010-09-17 03:12:30 +08:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
|
2016-03-17 21:41:37 +08:00
|
|
|
if (unlikely(!ifmgd->associated))
|
2011-02-25 21:46:02 +08:00
|
|
|
return;
|
|
|
|
|
2016-03-17 21:41:37 +08:00
|
|
|
if (ifmgd->probe_send_count)
|
|
|
|
ifmgd->probe_send_count = 0;
|
2014-02-11 18:30:18 +08:00
|
|
|
|
2015-06-03 03:39:54 +08:00
|
|
|
if (ieee80211_hw_check(&sdata->local->hw, CONNECTION_MONITOR))
|
2010-09-17 03:12:29 +08:00
|
|
|
return;
|
|
|
|
|
2016-03-17 21:41:37 +08:00
|
|
|
mod_timer(&ifmgd->conn_mon_timer,
|
2010-09-17 03:12:29 +08:00
|
|
|
round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME));
|
|
|
|
}
|
|
|
|
|
2008-09-08 23:44:27 +08:00
|
|
|
static int ecw2cw(int ecw)
|
2008-09-08 23:44:22 +08:00
|
|
|
{
|
2008-09-08 23:44:27 +08:00
|
|
|
return (1 << ecw) - 1;
|
2008-09-08 23:44:22 +08:00
|
|
|
}
|
|
|
|
|
2022-06-23 18:04:55 +08:00
|
|
|
static ieee80211_conn_flags_t
|
2022-07-12 17:33:57 +08:00
|
|
|
ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct ieee80211_link_data *link,
|
|
|
|
ieee80211_conn_flags_t conn_flags,
|
2013-02-08 21:52:32 +08:00
|
|
|
struct ieee80211_supported_band *sband,
|
|
|
|
struct ieee80211_channel *channel,
|
2020-05-29 03:34:35 +08:00
|
|
|
u32 vht_cap_info,
|
2013-02-08 21:52:32 +08:00
|
|
|
const struct ieee80211_ht_operation *ht_oper,
|
|
|
|
const struct ieee80211_vht_operation *vht_oper,
|
2018-06-09 14:14:44 +08:00
|
|
|
const struct ieee80211_he_operation *he_oper,
|
2022-02-15 00:30:00 +08:00
|
|
|
const struct ieee80211_eht_operation *eht_oper,
|
2020-09-22 10:28:15 +08:00
|
|
|
const struct ieee80211_s1g_oper_ie *s1g_oper,
|
2013-07-31 17:23:06 +08:00
|
|
|
struct cfg80211_chan_def *chandef, bool tracking)
|
2013-02-08 21:52:32 +08:00
|
|
|
{
|
|
|
|
struct cfg80211_chan_def vht_chandef;
|
2014-12-14 16:38:59 +08:00
|
|
|
struct ieee80211_sta_ht_cap sta_ht_cap;
|
2022-06-23 18:04:55 +08:00
|
|
|
ieee80211_conn_flags_t ret;
|
|
|
|
u32 ht_cfreq;
|
2013-02-08 21:52:32 +08:00
|
|
|
|
2019-08-18 22:35:17 +08:00
|
|
|
memset(chandef, 0, sizeof(struct cfg80211_chan_def));
|
2013-02-08 21:52:32 +08:00
|
|
|
chandef->chan = channel;
|
|
|
|
chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
|
|
|
|
chandef->center_freq1 = channel->center_freq;
|
2020-04-02 09:18:04 +08:00
|
|
|
chandef->freq1_offset = channel->freq_offset;
|
2013-02-08 21:52:32 +08:00
|
|
|
|
2020-05-29 03:34:36 +08:00
|
|
|
if (channel->band == NL80211_BAND_6GHZ) {
|
2022-02-15 00:30:00 +08:00
|
|
|
if (!ieee80211_chandef_he_6ghz_oper(sdata, he_oper, eht_oper,
|
|
|
|
chandef)) {
|
2021-11-30 19:16:59 +08:00
|
|
|
mlme_dbg(sdata,
|
2022-02-15 00:30:00 +08:00
|
|
|
"bad 6 GHz operation, disabling HT/VHT/HE/EHT\n");
|
2022-06-23 18:04:55 +08:00
|
|
|
ret = IEEE80211_CONN_DISABLE_HT |
|
|
|
|
IEEE80211_CONN_DISABLE_VHT |
|
|
|
|
IEEE80211_CONN_DISABLE_HE |
|
|
|
|
IEEE80211_CONN_DISABLE_EHT;
|
2021-11-30 19:16:59 +08:00
|
|
|
} else {
|
2020-06-03 17:15:03 +08:00
|
|
|
ret = 0;
|
2021-11-30 19:16:59 +08:00
|
|
|
}
|
2020-05-29 03:34:36 +08:00
|
|
|
vht_chandef = *chandef;
|
|
|
|
goto out;
|
2020-10-02 01:47:48 +08:00
|
|
|
} else if (sband->band == NL80211_BAND_S1GHZ) {
|
|
|
|
if (!ieee80211_chandef_s1g_oper(s1g_oper, chandef)) {
|
|
|
|
sdata_info(sdata,
|
|
|
|
"Missing S1G Operation Element? Trying operating == primary\n");
|
|
|
|
chandef->width = ieee80211_s1g_channel_width(channel);
|
|
|
|
}
|
2020-05-29 03:34:36 +08:00
|
|
|
|
2022-06-23 18:04:55 +08:00
|
|
|
ret = IEEE80211_CONN_DISABLE_HT | IEEE80211_CONN_DISABLE_40MHZ |
|
|
|
|
IEEE80211_CONN_DISABLE_VHT |
|
|
|
|
IEEE80211_CONN_DISABLE_80P80MHZ |
|
|
|
|
IEEE80211_CONN_DISABLE_160MHZ;
|
2020-09-22 10:28:15 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2020-10-02 01:47:48 +08:00
|
|
|
memcpy(&sta_ht_cap, &sband->ht_cap, sizeof(sta_ht_cap));
|
|
|
|
ieee80211_apply_htcap_overrides(sdata, &sta_ht_cap);
|
|
|
|
|
2017-10-13 20:26:01 +08:00
|
|
|
if (!ht_oper || !sta_ht_cap.ht_supported) {
|
2021-11-30 19:16:59 +08:00
|
|
|
mlme_dbg(sdata, "HT operation missing / HT not supported\n");
|
2022-06-23 18:04:55 +08:00
|
|
|
ret = IEEE80211_CONN_DISABLE_HT |
|
|
|
|
IEEE80211_CONN_DISABLE_VHT |
|
|
|
|
IEEE80211_CONN_DISABLE_HE |
|
|
|
|
IEEE80211_CONN_DISABLE_EHT;
|
2013-02-08 21:52:32 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
chandef->width = NL80211_CHAN_WIDTH_20;
|
|
|
|
|
|
|
|
ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan,
|
|
|
|
channel->band);
|
|
|
|
/* check that channel matches the right operating channel */
|
2013-07-31 17:23:06 +08:00
|
|
|
if (!tracking && channel->center_freq != ht_cfreq) {
|
2013-02-08 21:52:32 +08: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 17:23:06 +08: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);
|
2022-06-23 18:04:55 +08:00
|
|
|
ret = IEEE80211_CONN_DISABLE_HT |
|
|
|
|
IEEE80211_CONN_DISABLE_VHT |
|
|
|
|
IEEE80211_CONN_DISABLE_HE |
|
|
|
|
IEEE80211_CONN_DISABLE_EHT;
|
2013-02-08 21:52:32 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check 40 MHz support, if we have it */
|
2014-12-14 16:38:59 +08:00
|
|
|
if (sta_ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) {
|
2015-12-18 22:08:34 +08:00
|
|
|
ieee80211_chandef_ht_oper(ht_oper, chandef);
|
2013-02-08 21:52:32 +08:00
|
|
|
} else {
|
2021-11-30 19:16:59 +08:00
|
|
|
mlme_dbg(sdata, "40 MHz not supported\n");
|
2013-02-08 21:52:32 +08:00
|
|
|
/* 40 MHz (and 80 MHz) must be supported for VHT */
|
2022-06-23 18:04:55 +08:00
|
|
|
ret = IEEE80211_CONN_DISABLE_VHT;
|
2013-03-26 01:29:27 +08:00
|
|
|
/* also mark 40 MHz disabled */
|
2022-06-23 18:04:55 +08:00
|
|
|
ret |= IEEE80211_CONN_DISABLE_40MHZ;
|
2013-02-08 21:52:32 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!vht_oper || !sband->vht_cap.vht_supported) {
|
2021-11-30 19:16:59 +08:00
|
|
|
mlme_dbg(sdata, "VHT operation missing / VHT not supported\n");
|
2022-06-23 18:04:55 +08:00
|
|
|
ret = IEEE80211_CONN_DISABLE_VHT;
|
2013-02-08 21:52:32 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2015-12-18 22:08:34 +08:00
|
|
|
vht_chandef = *chandef;
|
2022-07-12 17:33:57 +08:00
|
|
|
if (!(conn_flags & IEEE80211_CONN_DISABLE_HE) &&
|
2022-06-23 18:04:55 +08:00
|
|
|
he_oper &&
|
2018-06-09 14:14:44 +08:00
|
|
|
(le32_to_cpu(he_oper->he_oper_params) &
|
|
|
|
IEEE80211_HE_OPERATION_VHT_OPER_INFO)) {
|
|
|
|
struct ieee80211_vht_operation he_oper_vht_cap;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set only first 3 bytes (other 2 aren't used in
|
|
|
|
* ieee80211_chandef_vht_oper() anyway)
|
|
|
|
*/
|
|
|
|
memcpy(&he_oper_vht_cap, he_oper->optional, 3);
|
|
|
|
he_oper_vht_cap.basic_mcs_set = cpu_to_le16(0);
|
|
|
|
|
2020-05-29 03:34:35 +08:00
|
|
|
if (!ieee80211_chandef_vht_oper(&sdata->local->hw, vht_cap_info,
|
2018-08-31 16:31:18 +08:00
|
|
|
&he_oper_vht_cap, ht_oper,
|
2018-06-09 14:14:44 +08:00
|
|
|
&vht_chandef)) {
|
2022-07-12 17:33:57 +08:00
|
|
|
if (!(conn_flags & IEEE80211_CONN_DISABLE_HE))
|
2018-06-09 14:14:44 +08:00
|
|
|
sdata_info(sdata,
|
2021-11-30 19:16:59 +08:00
|
|
|
"HE AP VHT information is invalid, disabling HE\n");
|
2022-06-23 18:04:55 +08:00
|
|
|
ret = IEEE80211_CONN_DISABLE_HE | IEEE80211_CONN_DISABLE_EHT;
|
2018-06-09 14:14:44 +08:00
|
|
|
goto out;
|
|
|
|
}
|
2020-05-29 03:34:35 +08:00
|
|
|
} else if (!ieee80211_chandef_vht_oper(&sdata->local->hw,
|
|
|
|
vht_cap_info,
|
|
|
|
vht_oper, ht_oper,
|
|
|
|
&vht_chandef)) {
|
2022-07-12 17:33:57 +08:00
|
|
|
if (!(conn_flags & IEEE80211_CONN_DISABLE_VHT))
|
2013-02-08 22:12:14 +08:00
|
|
|
sdata_info(sdata,
|
2021-11-30 19:16:59 +08:00
|
|
|
"AP VHT information is invalid, disabling VHT\n");
|
2022-06-23 18:04:55 +08:00
|
|
|
ret = IEEE80211_CONN_DISABLE_VHT;
|
2013-02-08 21:52:32 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cfg80211_chandef_valid(&vht_chandef)) {
|
2022-07-12 17:33:57 +08:00
|
|
|
if (!(conn_flags & IEEE80211_CONN_DISABLE_VHT))
|
2013-02-08 22:12:14 +08:00
|
|
|
sdata_info(sdata,
|
2021-11-30 19:16:59 +08:00
|
|
|
"AP VHT information is invalid, disabling VHT\n");
|
2022-06-23 18:04:55 +08:00
|
|
|
ret = IEEE80211_CONN_DISABLE_VHT;
|
2013-02-08 21:52:32 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cfg80211_chandef_identical(chandef, &vht_chandef)) {
|
|
|
|
ret = 0;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cfg80211_chandef_compatible(chandef, &vht_chandef)) {
|
2022-07-12 17:33:57 +08:00
|
|
|
if (!(conn_flags & IEEE80211_CONN_DISABLE_VHT))
|
2013-02-08 22:12:14 +08:00
|
|
|
sdata_info(sdata,
|
2021-11-30 19:16:59 +08:00
|
|
|
"AP VHT information doesn't match HT, disabling VHT\n");
|
2022-06-23 18:04:55 +08:00
|
|
|
ret = IEEE80211_CONN_DISABLE_VHT;
|
2013-02-08 21:52:32 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
*chandef = vht_chandef;
|
|
|
|
|
2022-04-27 23:02:10 +08:00
|
|
|
/*
|
|
|
|
* handle the case that the EHT operation indicates that it holds EHT
|
|
|
|
* operation information (in case that the channel width differs from
|
|
|
|
* the channel width reported in HT/VHT/HE).
|
|
|
|
*/
|
|
|
|
if (eht_oper && (eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT)) {
|
|
|
|
struct cfg80211_chan_def eht_chandef = *chandef;
|
|
|
|
|
2022-09-02 22:12:34 +08:00
|
|
|
ieee80211_chandef_eht_oper(eht_oper,
|
2022-04-27 23:02:10 +08:00
|
|
|
eht_chandef.width ==
|
|
|
|
NL80211_CHAN_WIDTH_160,
|
|
|
|
false, &eht_chandef);
|
|
|
|
|
|
|
|
if (!cfg80211_chandef_valid(&eht_chandef)) {
|
2022-07-12 17:33:57 +08:00
|
|
|
if (!(conn_flags & IEEE80211_CONN_DISABLE_EHT))
|
2022-04-27 23:02:10 +08:00
|
|
|
sdata_info(sdata,
|
|
|
|
"AP EHT information is invalid, disabling EHT\n");
|
2022-06-23 18:04:55 +08:00
|
|
|
ret = IEEE80211_CONN_DISABLE_EHT;
|
2022-04-27 23:02:10 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!cfg80211_chandef_compatible(chandef, &eht_chandef)) {
|
2022-07-12 17:33:57 +08:00
|
|
|
if (!(conn_flags & IEEE80211_CONN_DISABLE_EHT))
|
2022-04-27 23:02:10 +08:00
|
|
|
sdata_info(sdata,
|
|
|
|
"AP EHT information is incompatible, disabling EHT\n");
|
2022-06-23 18:04:55 +08:00
|
|
|
ret = IEEE80211_CONN_DISABLE_EHT;
|
2022-04-27 23:02:10 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
*chandef = eht_chandef;
|
|
|
|
}
|
|
|
|
|
2013-02-08 21:52:32 +08:00
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
out:
|
2014-02-22 03:34:34 +08: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 &&
|
2022-06-24 22:18:56 +08:00
|
|
|
cfg80211_chandef_identical(chandef, &link->conf->chandef))
|
2014-02-22 03:34:34 +08:00
|
|
|
return ret;
|
|
|
|
|
2013-02-14 19:13:53 +08:00
|
|
|
/* don't print the message below for VHT mismatch if VHT is disabled */
|
2022-06-23 18:04:55 +08:00
|
|
|
if (ret & IEEE80211_CONN_DISABLE_VHT)
|
2013-02-14 19:13:53 +08:00
|
|
|
vht_chandef = *chandef;
|
|
|
|
|
2013-08-01 02:52:03 +08: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 21:52:32 +08:00
|
|
|
while (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
|
2013-08-01 02:52:03 +08:00
|
|
|
tracking ? 0 :
|
|
|
|
IEEE80211_CHAN_DISABLED)) {
|
2013-02-08 21:52:32 +08:00
|
|
|
if (WARN_ON(chandef->width == NL80211_CHAN_WIDTH_20_NOHT)) {
|
2022-06-23 18:04:55 +08:00
|
|
|
ret = IEEE80211_CONN_DISABLE_HT |
|
|
|
|
IEEE80211_CONN_DISABLE_VHT |
|
|
|
|
IEEE80211_CONN_DISABLE_HE |
|
|
|
|
IEEE80211_CONN_DISABLE_EHT;
|
2013-08-01 03:12:24 +08:00
|
|
|
break;
|
2013-02-08 21:52:32 +08:00
|
|
|
}
|
|
|
|
|
2013-08-28 19:41:29 +08:00
|
|
|
ret |= ieee80211_chandef_downgrade(chandef);
|
2013-02-08 21:52:32 +08:00
|
|
|
}
|
|
|
|
|
2020-01-31 19:12:52 +08:00
|
|
|
if (!he_oper || !cfg80211_chandef_usable(sdata->wdev.wiphy, chandef,
|
|
|
|
IEEE80211_CHAN_NO_HE))
|
2022-06-23 18:04:55 +08:00
|
|
|
ret |= IEEE80211_CONN_DISABLE_HE | IEEE80211_CONN_DISABLE_EHT;
|
2022-02-15 00:30:00 +08:00
|
|
|
|
|
|
|
if (!eht_oper || !cfg80211_chandef_usable(sdata->wdev.wiphy, chandef,
|
|
|
|
IEEE80211_CHAN_NO_EHT))
|
2022-06-23 18:04:55 +08:00
|
|
|
ret |= IEEE80211_CONN_DISABLE_EHT;
|
2020-01-31 19:12:44 +08:00
|
|
|
|
2013-07-31 17:23:06 +08:00
|
|
|
if (chandef->width != vht_chandef.width && !tracking)
|
2013-02-08 21:52:32 +08:00
|
|
|
sdata_info(sdata,
|
|
|
|
"capabilities/regulatory prevented using AP HT/VHT configuration, downgraded\n");
|
|
|
|
|
|
|
|
WARN_ON_ONCE(!cfg80211_chandef_valid(chandef));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-06-24 22:18:56 +08:00
|
|
|
static int ieee80211_config_bw(struct ieee80211_link_data *link,
|
2014-07-24 16:20:05 +08:00
|
|
|
const struct ieee80211_ht_cap *ht_cap,
|
2020-05-29 03:34:35 +08:00
|
|
|
const struct ieee80211_vht_cap *vht_cap,
|
2013-02-08 22:12:14 +08:00
|
|
|
const struct ieee80211_ht_operation *ht_oper,
|
|
|
|
const struct ieee80211_vht_operation *vht_oper,
|
2018-06-09 14:14:44 +08:00
|
|
|
const struct ieee80211_he_operation *he_oper,
|
2022-02-15 00:30:00 +08:00
|
|
|
const struct ieee80211_eht_operation *eht_oper,
|
2020-09-22 10:28:15 +08:00
|
|
|
const struct ieee80211_s1g_oper_ie *s1g_oper,
|
2023-01-27 19:39:31 +08:00
|
|
|
const u8 *bssid, u64 *changed)
|
2009-03-30 19:23:35 +08:00
|
|
|
{
|
2022-06-24 22:18:56 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = link->sdata;
|
2009-03-30 19:23:35 +08:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
2013-02-08 22:12:14 +08:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2022-06-24 22:18:56 +08:00
|
|
|
struct ieee80211_channel *chan = link->conf->chandef.chan;
|
2018-06-09 14:14:44 +08:00
|
|
|
struct ieee80211_supported_band *sband =
|
|
|
|
local->hw.wiphy->bands[chan->band];
|
2013-02-08 22:12:14 +08:00
|
|
|
struct cfg80211_chan_def chandef;
|
2009-05-09 02:47:39 +08:00
|
|
|
u16 ht_opmode;
|
2022-06-23 18:04:55 +08:00
|
|
|
ieee80211_conn_flags_t flags;
|
2020-05-29 03:34:35 +08:00
|
|
|
u32 vht_cap_info = 0;
|
2013-02-08 22:12:14 +08:00
|
|
|
int ret;
|
2009-03-30 19:23:35 +08:00
|
|
|
|
2013-02-08 22:12:14 +08:00
|
|
|
/* if HT was/is disabled, don't track any bandwidth changes */
|
2022-06-24 22:18:56 +08:00
|
|
|
if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT || !ht_oper)
|
2013-02-08 00:36:12 +08:00
|
|
|
return 0;
|
|
|
|
|
2013-02-08 22:12:14 +08:00
|
|
|
/* don't check VHT if we associated as non-VHT station */
|
2022-06-24 22:18:56 +08:00
|
|
|
if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT)
|
2013-02-08 22:12:14 +08:00
|
|
|
vht_oper = NULL;
|
|
|
|
|
2018-06-09 14:14:44 +08:00
|
|
|
/* don't check HE if we associated as non-HE station */
|
2022-06-24 22:18:56 +08:00
|
|
|
if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE ||
|
2021-06-18 18:41:50 +08:00
|
|
|
!ieee80211_get_he_iftype_cap(sband,
|
2022-02-15 00:30:00 +08:00
|
|
|
ieee80211_vif_type_p2p(&sdata->vif))) {
|
2018-06-09 14:14:44 +08:00
|
|
|
he_oper = NULL;
|
2022-02-15 00:30:00 +08:00
|
|
|
eht_oper = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* don't check EHT if we associated as non-EHT station */
|
2022-06-24 22:18:56 +08:00
|
|
|
if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_EHT ||
|
2022-02-15 00:30:00 +08:00
|
|
|
!ieee80211_get_eht_iftype_cap(sband,
|
|
|
|
ieee80211_vif_type_p2p(&sdata->vif)))
|
|
|
|
eht_oper = NULL;
|
2018-06-09 14:14:44 +08:00
|
|
|
|
2013-11-19 01:06:48 +08: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);
|
2022-06-24 22:18:56 +08:00
|
|
|
if (link->conf->ht_operation_mode != ht_opmode) {
|
2013-11-19 01:06:48 +08:00
|
|
|
*changed |= BSS_CHANGED_HT;
|
2022-06-24 22:18:56 +08:00
|
|
|
link->conf->ht_operation_mode = ht_opmode;
|
2013-11-19 01:06:48 +08:00
|
|
|
}
|
|
|
|
|
2020-05-29 03:34:35 +08:00
|
|
|
if (vht_cap)
|
|
|
|
vht_cap_info = le32_to_cpu(vht_cap->vht_cap_info);
|
|
|
|
|
2018-06-09 14:14:44 +08:00
|
|
|
/* calculate new channel (type) based on HT/VHT/HE operation IEs */
|
2022-07-12 17:33:57 +08:00
|
|
|
flags = ieee80211_determine_chantype(sdata, link,
|
|
|
|
link->u.mgd.conn_flags,
|
|
|
|
sband, chan, vht_cap_info,
|
2022-02-15 00:30:00 +08:00
|
|
|
ht_oper, vht_oper,
|
|
|
|
he_oper, eht_oper,
|
2020-09-22 10:28:15 +08:00
|
|
|
s1g_oper, &chandef, true);
|
2013-02-08 22:12:14 +08: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.
|
|
|
|
*/
|
2022-06-24 22:18:56 +08:00
|
|
|
if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_80P80MHZ &&
|
2013-02-08 22:12:14 +08:00
|
|
|
chandef.width == NL80211_CHAN_WIDTH_80P80)
|
2013-08-28 19:41:29 +08:00
|
|
|
flags |= ieee80211_chandef_downgrade(&chandef);
|
2022-06-24 22:18:56 +08:00
|
|
|
if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_160MHZ &&
|
2013-02-08 22:12:14 +08:00
|
|
|
chandef.width == NL80211_CHAN_WIDTH_160)
|
2013-08-28 19:41:29 +08:00
|
|
|
flags |= ieee80211_chandef_downgrade(&chandef);
|
2022-06-24 22:18:56 +08:00
|
|
|
if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_40MHZ &&
|
2013-02-08 22:12:14 +08:00
|
|
|
chandef.width > NL80211_CHAN_WIDTH_20)
|
2013-08-28 19:41:29 +08:00
|
|
|
flags |= ieee80211_chandef_downgrade(&chandef);
|
2013-02-08 22:12:14 +08:00
|
|
|
|
2022-06-24 22:18:56 +08:00
|
|
|
if (cfg80211_chandef_identical(&chandef, &link->conf->chandef))
|
2013-02-08 22:12:14 +08:00
|
|
|
return 0;
|
|
|
|
|
2022-06-24 22:18:56 +08:00
|
|
|
link_info(link,
|
|
|
|
"AP %pM changed bandwidth, new config is %d.%03d MHz, width %d (%d.%03d/%d MHz)\n",
|
|
|
|
link->u.mgd.bssid, chandef.chan->center_freq,
|
|
|
|
chandef.chan->freq_offset, chandef.width,
|
|
|
|
chandef.center_freq1, chandef.freq1_offset,
|
|
|
|
chandef.center_freq2);
|
|
|
|
|
|
|
|
if (flags != (link->u.mgd.conn_flags &
|
2022-06-23 18:04:55 +08:00
|
|
|
(IEEE80211_CONN_DISABLE_HT |
|
|
|
|
IEEE80211_CONN_DISABLE_VHT |
|
|
|
|
IEEE80211_CONN_DISABLE_HE |
|
|
|
|
IEEE80211_CONN_DISABLE_EHT |
|
|
|
|
IEEE80211_CONN_DISABLE_40MHZ |
|
|
|
|
IEEE80211_CONN_DISABLE_80P80MHZ |
|
|
|
|
IEEE80211_CONN_DISABLE_160MHZ |
|
|
|
|
IEEE80211_CONN_DISABLE_320MHZ)) ||
|
2013-02-08 22:12:14 +08:00
|
|
|
!cfg80211_chandef_valid(&chandef)) {
|
|
|
|
sdata_info(sdata,
|
2021-06-18 18:41:47 +08:00
|
|
|
"AP %pM changed caps/bw in a way we can't support (0x%x/0x%x) - disconnect\n",
|
2022-06-24 22:18:56 +08:00
|
|
|
link->u.mgd.bssid, flags, ifmgd->flags);
|
2013-02-08 22:12:14 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2022-06-24 22:18:56 +08:00
|
|
|
ret = ieee80211_link_change_bandwidth(link, &chandef, changed);
|
2021-06-18 18:41:30 +08:00
|
|
|
|
2013-02-08 22:12:14 +08:00
|
|
|
if (ret) {
|
|
|
|
sdata_info(sdata,
|
|
|
|
"AP %pM changed bandwidth to incompatible one - disconnect\n",
|
2022-06-24 22:18:56 +08:00
|
|
|
link->u.mgd.bssid);
|
2013-02-08 22:12:14 +08:00
|
|
|
return ret;
|
|
|
|
}
|
2011-09-16 18:02:34 +08:00
|
|
|
|
2013-02-08 22:12:14 +08:00
|
|
|
return 0;
|
2009-03-30 19:23:35 +08:00
|
|
|
}
|
|
|
|
|
2008-09-11 06:01:52 +08:00
|
|
|
/* frame sending functions */
|
|
|
|
|
2022-07-01 22:14:33 +08:00
|
|
|
static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata,
|
2012-05-17 05:43:19 +08:00
|
|
|
struct sk_buff *skb, u8 ap_ht_param,
|
2012-01-20 20:55:27 +08:00
|
|
|
struct ieee80211_supported_band *sband,
|
|
|
|
struct ieee80211_channel *channel,
|
2022-07-01 22:14:33 +08:00
|
|
|
enum ieee80211_smps_mode smps,
|
|
|
|
ieee80211_conn_flags_t conn_flags)
|
2012-01-20 20:55:27 +08:00
|
|
|
{
|
|
|
|
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-17 05:43:19 +08:00
|
|
|
switch (ap_ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
|
2012-01-20 20:55:27 +08: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 16:58:36 +08: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.
|
|
|
|
*/
|
2022-07-01 22:14:33 +08:00
|
|
|
if (conn_flags & IEEE80211_CONN_DISABLE_40MHZ) {
|
2012-03-28 16:58:36 +08:00
|
|
|
cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
|
|
|
|
cap &= ~IEEE80211_HT_CAP_SGI_40;
|
|
|
|
}
|
|
|
|
|
2012-01-20 20:55:27 +08: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);
|
2020-07-08 04:45:48 +08:00
|
|
|
fallthrough;
|
2012-01-20 20:55:27 +08:00
|
|
|
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 20:41:43 +08:00
|
|
|
/* This function determines vht capability flags for the association
|
|
|
|
* and builds the IE.
|
2022-07-01 22:14:33 +08:00
|
|
|
* Note - the function returns true to own the MU-MIMO capability
|
2015-07-08 20:41:43 +08:00
|
|
|
*/
|
2022-07-01 22:14:33 +08:00
|
|
|
static bool ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
|
2012-07-24 11:33:10 +08:00
|
|
|
struct sk_buff *skb,
|
2012-12-07 20:06:48 +08:00
|
|
|
struct ieee80211_supported_band *sband,
|
2022-07-01 22:14:33 +08:00
|
|
|
struct ieee80211_vht_cap *ap_vht_cap,
|
|
|
|
ieee80211_conn_flags_t conn_flags)
|
2012-07-24 11:33:10 +08:00
|
|
|
{
|
2015-07-08 20:41:43 +08:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
2012-07-24 11:33:10 +08:00
|
|
|
u8 *pos;
|
|
|
|
u32 cap;
|
|
|
|
struct ieee80211_sta_vht_cap vht_cap;
|
2014-01-08 21:49:08 +08:00
|
|
|
u32 mask, ap_bf_sts, our_bf_sts;
|
2022-07-01 22:14:33 +08:00
|
|
|
bool mu_mimo_owner = false;
|
2012-07-24 11:33:10 +08:00
|
|
|
|
|
|
|
BUILD_BUG_ON(sizeof(vht_cap) != sizeof(sband->vht_cap));
|
|
|
|
|
|
|
|
memcpy(&vht_cap, &sband->vht_cap, sizeof(vht_cap));
|
2013-02-22 00:40:19 +08:00
|
|
|
ieee80211_apply_vhtcap_overrides(sdata, &vht_cap);
|
2012-07-24 11:33:10 +08:00
|
|
|
|
|
|
|
/* determine capability flags */
|
|
|
|
cap = vht_cap.cap;
|
|
|
|
|
2022-07-01 22:14:33 +08:00
|
|
|
if (conn_flags & IEEE80211_CONN_DISABLE_80P80MHZ) {
|
2014-11-24 23:51:33 +08: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 21:11:39 +08:00
|
|
|
}
|
|
|
|
|
2022-07-01 22:14:33 +08:00
|
|
|
if (conn_flags & IEEE80211_CONN_DISABLE_160MHZ) {
|
2012-11-22 21:11:39 +08:00
|
|
|
cap &= ~IEEE80211_VHT_CAP_SHORT_GI_160;
|
2014-11-24 23:51:33 +08:00
|
|
|
cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
|
2012-11-22 21:11:39 +08:00
|
|
|
}
|
|
|
|
|
2012-12-07 20:06:48 +08: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 20:41:43 +08: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;
|
|
|
|
|
|
|
|
/*
|
2021-06-07 23:00:47 +08:00
|
|
|
* If some other vif is using the MU-MIMO capability we cannot associate
|
2015-07-08 20:41:43 +08:00
|
|
|
* 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) {
|
wifi: mac80211: move some future per-link data to bss_conf
To add MLD, reuse the bss_conf structure later for per-link
information, so move some things into it that are per link.
Most transformations were done with the following spatch:
@@
expression sdata;
identifier var = { chanctx_conf, mu_mimo_owner, csa_active, color_change_active, color_change_color };
@@
-sdata->vif.var
+sdata->vif.bss_conf.var
@@
struct ieee80211_vif *vif;
identifier var = { chanctx_conf, mu_mimo_owner, csa_active, color_change_active, color_change_color };
@@
-vif->var
+vif->bss_conf.var
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2022-05-10 19:26:44 +08:00
|
|
|
if (other->vif.bss_conf.mu_mimo_owner) {
|
2015-07-08 20:41:43 +08:00
|
|
|
disable_mu_mimo = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (disable_mu_mimo)
|
|
|
|
cap &= ~IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
|
|
|
|
else
|
2022-07-01 22:14:33 +08:00
|
|
|
mu_mimo_owner = true;
|
2015-07-08 20:41:43 +08:00
|
|
|
}
|
2012-12-07 20:06:48 +08:00
|
|
|
|
2014-01-08 21:49:08 +08: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 11:33:10 +08:00
|
|
|
/* reserve and fill IE */
|
2012-10-10 19:25:40 +08:00
|
|
|
pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
|
2012-07-24 11:33:10 +08:00
|
|
|
ieee80211_ie_build_vht_cap(pos, &vht_cap, cap);
|
2022-07-01 22:14:33 +08:00
|
|
|
|
|
|
|
return mu_mimo_owner;
|
2012-07-24 11:33:10 +08:00
|
|
|
}
|
|
|
|
|
2018-06-09 14:14:44 +08:00
|
|
|
/* This function determines HE capability flags for the association
|
|
|
|
* and builds the IE.
|
|
|
|
*/
|
2022-07-01 22:14:33 +08:00
|
|
|
static void ieee80211_add_he_ie(struct ieee80211_sub_if_data *sdata,
|
2018-06-09 14:14:44 +08:00
|
|
|
struct sk_buff *skb,
|
2022-07-01 22:14:33 +08:00
|
|
|
struct ieee80211_supported_band *sband,
|
2022-08-26 02:57:32 +08:00
|
|
|
enum ieee80211_smps_mode smps_mode,
|
2022-07-01 22:14:33 +08:00
|
|
|
ieee80211_conn_flags_t conn_flags)
|
2018-06-09 14:14:44 +08:00
|
|
|
{
|
2022-02-02 16:49:34 +08:00
|
|
|
u8 *pos, *pre_he_pos;
|
2022-07-01 22:14:33 +08:00
|
|
|
const struct ieee80211_sta_he_cap *he_cap;
|
2018-06-09 14:14:44 +08:00
|
|
|
u8 he_cap_size;
|
|
|
|
|
2021-06-18 18:41:50 +08:00
|
|
|
he_cap = ieee80211_get_he_iftype_cap(sband,
|
|
|
|
ieee80211_vif_type_p2p(&sdata->vif));
|
2022-07-01 22:14:33 +08:00
|
|
|
if (WARN_ON(!he_cap))
|
2018-06-09 14:14:44 +08:00
|
|
|
return;
|
|
|
|
|
2022-02-02 16:49:34 +08:00
|
|
|
/* get a max size estimate */
|
2018-06-09 14:14:44 +08:00
|
|
|
he_cap_size =
|
|
|
|
2 + 1 + sizeof(he_cap->he_cap_elem) +
|
|
|
|
ieee80211_he_mcs_nss_size(&he_cap->he_cap_elem) +
|
|
|
|
ieee80211_he_ppe_size(he_cap->ppe_thres[0],
|
|
|
|
he_cap->he_cap_elem.phy_cap_info);
|
|
|
|
pos = skb_put(skb, he_cap_size);
|
2022-02-02 16:49:34 +08:00
|
|
|
pre_he_pos = pos;
|
2022-07-01 22:14:33 +08:00
|
|
|
pos = ieee80211_ie_build_he_cap(conn_flags,
|
2022-02-02 16:49:34 +08:00
|
|
|
pos, he_cap, pos + he_cap_size);
|
|
|
|
/* trim excess if any */
|
|
|
|
skb_trim(skb, skb->len - (pre_he_pos + he_cap_size - pos));
|
2020-05-29 03:34:32 +08:00
|
|
|
|
2022-08-26 02:57:32 +08:00
|
|
|
ieee80211_ie_build_he_6ghz_cap(sdata, smps_mode, skb);
|
2018-06-09 14:14:44 +08:00
|
|
|
}
|
|
|
|
|
2022-07-01 22:14:33 +08:00
|
|
|
static void ieee80211_add_eht_ie(struct ieee80211_sub_if_data *sdata,
|
2022-02-15 00:30:01 +08:00
|
|
|
struct sk_buff *skb,
|
|
|
|
struct ieee80211_supported_band *sband)
|
|
|
|
{
|
|
|
|
u8 *pos;
|
|
|
|
const struct ieee80211_sta_he_cap *he_cap;
|
|
|
|
const struct ieee80211_sta_eht_cap *eht_cap;
|
|
|
|
u8 eht_cap_size;
|
|
|
|
|
|
|
|
he_cap = ieee80211_get_he_iftype_cap(sband,
|
|
|
|
ieee80211_vif_type_p2p(&sdata->vif));
|
|
|
|
eht_cap = ieee80211_get_eht_iftype_cap(sband,
|
|
|
|
ieee80211_vif_type_p2p(&sdata->vif));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* EHT capabilities element is only added if the HE capabilities element
|
|
|
|
* was added so assume that 'he_cap' is valid and don't check it.
|
|
|
|
*/
|
2022-07-01 22:14:33 +08:00
|
|
|
if (WARN_ON(!he_cap || !eht_cap))
|
2022-02-15 00:30:01 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
eht_cap_size =
|
|
|
|
2 + 1 + sizeof(eht_cap->eht_cap_elem) +
|
|
|
|
ieee80211_eht_mcs_nss_size(&he_cap->he_cap_elem,
|
2022-08-16 17:26:23 +08:00
|
|
|
&eht_cap->eht_cap_elem,
|
|
|
|
false) +
|
2022-02-15 00:30:01 +08:00
|
|
|
ieee80211_eht_ppe_size(eht_cap->eht_ppe_thres[0],
|
|
|
|
eht_cap->eht_cap_elem.phy_cap_info);
|
|
|
|
pos = skb_put(skb, eht_cap_size);
|
2022-08-16 17:26:23 +08:00
|
|
|
ieee80211_ie_build_eht_cap(pos, he_cap, eht_cap, pos + eht_cap_size,
|
|
|
|
false);
|
2022-02-15 00:30:01 +08:00
|
|
|
}
|
|
|
|
|
2022-07-01 20:08:25 +08:00
|
|
|
static void ieee80211_assoc_add_rates(struct sk_buff *skb,
|
|
|
|
enum nl80211_chan_width width,
|
|
|
|
struct ieee80211_supported_band *sband,
|
|
|
|
struct ieee80211_mgd_assoc_data *assoc_data)
|
|
|
|
{
|
|
|
|
unsigned int shift = ieee80211_chanwidth_get_shift(width);
|
|
|
|
unsigned int rates_len, supp_rates_len;
|
|
|
|
u32 rates = 0;
|
|
|
|
int i, count;
|
|
|
|
u8 *pos;
|
|
|
|
|
|
|
|
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)...
|
|
|
|
*/
|
|
|
|
rates_len = ieee80211_parse_bitrates(width, sband,
|
|
|
|
assoc_data->supp_rates,
|
|
|
|
assoc_data->supp_rates_len,
|
|
|
|
&rates);
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* In case AP not provide any supported rates information
|
|
|
|
* before association, we send information element(s) with
|
|
|
|
* all rates that we support.
|
|
|
|
*/
|
|
|
|
rates_len = sband->n_bitrates;
|
|
|
|
for (i = 0; i < sband->n_bitrates; i++)
|
|
|
|
rates |= BIT(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
|
|
|
int rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
|
|
|
|
5 * (1 << shift));
|
|
|
|
*pos++ = (u8)rate;
|
|
|
|
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) {
|
|
|
|
int rate;
|
|
|
|
|
|
|
|
rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
|
|
|
|
5 * (1 << shift));
|
|
|
|
*pos++ = (u8)rate;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-01 21:38:57 +08:00
|
|
|
static size_t ieee80211_add_before_ht_elems(struct sk_buff *skb,
|
|
|
|
const u8 *elems,
|
|
|
|
size_t elems_len,
|
|
|
|
size_t offset)
|
|
|
|
{
|
|
|
|
size_t noffset;
|
|
|
|
|
|
|
|
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,
|
|
|
|
WLAN_EID_FAST_BSS_TRANSITION, /* reassoc only */
|
|
|
|
WLAN_EID_RIC_DATA, /* reassoc only */
|
|
|
|
WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
|
|
|
|
};
|
|
|
|
static const u8 after_ric[] = {
|
|
|
|
WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
|
|
|
|
WLAN_EID_HT_CAPABILITY,
|
|
|
|
WLAN_EID_BSS_COEX_2040,
|
|
|
|
/* luckily this is almost always there */
|
|
|
|
WLAN_EID_EXT_CAPABILITY,
|
|
|
|
WLAN_EID_QOS_TRAFFIC_CAPA,
|
|
|
|
WLAN_EID_TIM_BCAST_REQ,
|
|
|
|
WLAN_EID_INTERWORKING,
|
|
|
|
/* 60 GHz (Multi-band, DMG, MMS) can't happen */
|
|
|
|
WLAN_EID_VHT_CAPABILITY,
|
|
|
|
WLAN_EID_OPMODE_NOTIF,
|
|
|
|
};
|
|
|
|
|
|
|
|
if (!elems_len)
|
|
|
|
return offset;
|
|
|
|
|
|
|
|
noffset = ieee80211_ie_split_ric(elems, elems_len,
|
|
|
|
before_ht,
|
|
|
|
ARRAY_SIZE(before_ht),
|
|
|
|
after_ric,
|
|
|
|
ARRAY_SIZE(after_ric),
|
|
|
|
offset);
|
|
|
|
skb_put_data(skb, elems + offset, noffset - offset);
|
|
|
|
|
|
|
|
return noffset;
|
|
|
|
}
|
|
|
|
|
|
|
|
static size_t ieee80211_add_before_vht_elems(struct sk_buff *skb,
|
|
|
|
const u8 *elems,
|
|
|
|
size_t elems_len,
|
|
|
|
size_t offset)
|
|
|
|
{
|
|
|
|
static const u8 before_vht[] = {
|
|
|
|
/*
|
|
|
|
* no need to list the ones split off before HT
|
|
|
|
* or generated here
|
|
|
|
*/
|
|
|
|
WLAN_EID_BSS_COEX_2040,
|
|
|
|
WLAN_EID_EXT_CAPABILITY,
|
|
|
|
WLAN_EID_QOS_TRAFFIC_CAPA,
|
|
|
|
WLAN_EID_TIM_BCAST_REQ,
|
|
|
|
WLAN_EID_INTERWORKING,
|
|
|
|
/* 60 GHz (Multi-band, DMG, MMS) can't happen */
|
|
|
|
};
|
|
|
|
size_t noffset;
|
|
|
|
|
|
|
|
if (!elems_len)
|
|
|
|
return offset;
|
|
|
|
|
|
|
|
/* RIC already taken care of in ieee80211_add_before_ht_elems() */
|
|
|
|
noffset = ieee80211_ie_split(elems, elems_len,
|
|
|
|
before_vht, ARRAY_SIZE(before_vht),
|
|
|
|
offset);
|
|
|
|
skb_put_data(skb, elems + offset, noffset - offset);
|
|
|
|
|
|
|
|
return noffset;
|
|
|
|
}
|
|
|
|
|
|
|
|
static size_t ieee80211_add_before_he_elems(struct sk_buff *skb,
|
|
|
|
const u8 *elems,
|
|
|
|
size_t elems_len,
|
|
|
|
size_t offset)
|
|
|
|
{
|
|
|
|
static const u8 before_he[] = {
|
|
|
|
/*
|
|
|
|
* no need to list the ones split off before VHT
|
|
|
|
* or generated here
|
|
|
|
*/
|
|
|
|
WLAN_EID_OPMODE_NOTIF,
|
|
|
|
WLAN_EID_EXTENSION, WLAN_EID_EXT_FUTURE_CHAN_GUIDANCE,
|
|
|
|
/* 11ai elements */
|
|
|
|
WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_SESSION,
|
|
|
|
WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_PUBLIC_KEY,
|
|
|
|
WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_KEY_CONFIRM,
|
|
|
|
WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_HLP_CONTAINER,
|
|
|
|
WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_IP_ADDR_ASSIGN,
|
|
|
|
/* TODO: add 11ah/11aj/11ak elements */
|
|
|
|
};
|
|
|
|
size_t noffset;
|
|
|
|
|
|
|
|
if (!elems_len)
|
|
|
|
return offset;
|
|
|
|
|
|
|
|
/* RIC already taken care of in ieee80211_add_before_ht_elems() */
|
|
|
|
noffset = ieee80211_ie_split(elems, elems_len,
|
|
|
|
before_he, ARRAY_SIZE(before_he),
|
|
|
|
offset);
|
|
|
|
skb_put_data(skb, elems + offset, noffset - offset);
|
|
|
|
|
|
|
|
return noffset;
|
|
|
|
}
|
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
#define PRESENT_ELEMS_MAX 8
|
|
|
|
#define PRESENT_ELEM_EXT_OFFS 0x100
|
|
|
|
|
|
|
|
static void ieee80211_assoc_add_ml_elem(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct sk_buff *skb, u16 capab,
|
|
|
|
const struct element *ext_capa,
|
|
|
|
const u16 *present_elems);
|
|
|
|
|
2022-07-12 20:38:02 +08:00
|
|
|
static size_t ieee80211_assoc_link_elems(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct sk_buff *skb, u16 *capab,
|
|
|
|
const struct element *ext_capa,
|
|
|
|
const u8 *extra_elems,
|
|
|
|
size_t extra_elems_len,
|
2022-06-02 03:17:34 +08:00
|
|
|
unsigned int link_id,
|
|
|
|
struct ieee80211_link_data *link,
|
|
|
|
u16 *present_elems)
|
2022-07-12 20:38:02 +08:00
|
|
|
{
|
|
|
|
enum nl80211_iftype iftype = ieee80211_vif_type_p2p(&sdata->vif);
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data;
|
2022-06-02 03:17:34 +08:00
|
|
|
struct cfg80211_bss *cbss = assoc_data->link[link_id].bss;
|
2022-07-12 20:38:02 +08:00
|
|
|
struct ieee80211_channel *chan = cbss->channel;
|
|
|
|
const struct ieee80211_sband_iftype_data *iftd;
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
struct ieee80211_supported_band *sband;
|
|
|
|
enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20;
|
|
|
|
struct ieee80211_chanctx_conf *chanctx_conf;
|
2022-06-02 03:17:34 +08:00
|
|
|
enum ieee80211_smps_mode smps_mode;
|
|
|
|
u16 orig_capab = *capab;
|
2022-07-12 20:38:02 +08:00
|
|
|
size_t offset = 0;
|
2022-06-02 03:17:34 +08:00
|
|
|
int present_elems_len = 0;
|
2022-07-12 20:38:02 +08:00
|
|
|
u8 *pos;
|
|
|
|
int i;
|
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
#define ADD_PRESENT_ELEM(id) do { \
|
|
|
|
/* need a last for termination - we use 0 == SSID */ \
|
|
|
|
if (!WARN_ON(present_elems_len >= PRESENT_ELEMS_MAX - 1)) \
|
|
|
|
present_elems[present_elems_len++] = (id); \
|
|
|
|
} while (0)
|
|
|
|
#define ADD_PRESENT_EXT_ELEM(id) ADD_PRESENT_ELEM(PRESENT_ELEM_EXT_OFFS | (id))
|
|
|
|
|
|
|
|
if (link)
|
|
|
|
smps_mode = link->smps_mode;
|
|
|
|
else if (sdata->u.mgd.powersave)
|
|
|
|
smps_mode = IEEE80211_SMPS_DYNAMIC;
|
|
|
|
else
|
|
|
|
smps_mode = IEEE80211_SMPS_OFF;
|
|
|
|
|
|
|
|
if (link) {
|
|
|
|
/*
|
|
|
|
* 5/10 MHz scenarios are only viable without MLO, in which
|
|
|
|
* case this pointer should be used ... All of this is a bit
|
|
|
|
* unclear though, not sure this even works at all.
|
|
|
|
*/
|
|
|
|
rcu_read_lock();
|
|
|
|
chanctx_conf = rcu_dereference(link->conf->chanctx_conf);
|
|
|
|
if (chanctx_conf)
|
|
|
|
width = chanctx_conf->def.width;
|
|
|
|
rcu_read_unlock();
|
|
|
|
}
|
2022-07-12 20:38:02 +08:00
|
|
|
|
|
|
|
sband = local->hw.wiphy->bands[chan->band];
|
|
|
|
iftd = ieee80211_get_sband_iftype_data(sband, iftype);
|
|
|
|
|
|
|
|
if (sband->band == NL80211_BAND_2GHZ) {
|
|
|
|
*capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
|
|
|
|
*capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((cbss->capability & WLAN_CAPABILITY_SPECTRUM_MGMT) &&
|
|
|
|
ieee80211_hw_check(&local->hw, SPECTRUM_MGMT))
|
|
|
|
*capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
|
|
|
|
|
|
|
|
if (sband->band != NL80211_BAND_S1GHZ)
|
|
|
|
ieee80211_assoc_add_rates(skb, width, sband, assoc_data);
|
|
|
|
|
|
|
|
if (*capab & WLAN_CAPABILITY_SPECTRUM_MGMT ||
|
|
|
|
*capab & WLAN_CAPABILITY_RADIO_MEASURE) {
|
|
|
|
struct cfg80211_chan_def chandef = {
|
|
|
|
.width = width,
|
|
|
|
.chan = chan,
|
|
|
|
};
|
|
|
|
|
|
|
|
pos = skb_put(skb, 4);
|
|
|
|
*pos++ = WLAN_EID_PWR_CAPABILITY;
|
|
|
|
*pos++ = 2;
|
|
|
|
*pos++ = 0; /* min tx power */
|
|
|
|
/* max tx power */
|
|
|
|
*pos++ = ieee80211_chandef_max_power(&chandef);
|
2022-06-02 03:17:34 +08:00
|
|
|
ADD_PRESENT_ELEM(WLAN_EID_PWR_CAPABILITY);
|
2022-07-12 20:38:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Per spec, we shouldn't include the list of channels if we advertise
|
|
|
|
* support for extended channel switching, but we've always done that;
|
|
|
|
* (for now?) apply this restriction only on the (new) 6 GHz band.
|
|
|
|
*/
|
|
|
|
if (*capab & WLAN_CAPABILITY_SPECTRUM_MGMT &&
|
|
|
|
(sband->band != NL80211_BAND_6GHZ ||
|
|
|
|
!ext_capa || ext_capa->datalen < 1 ||
|
|
|
|
!(ext_capa->data[0] & WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING))) {
|
|
|
|
/* 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++) {
|
|
|
|
int cf = sband->channels[i].center_freq;
|
|
|
|
|
|
|
|
*pos++ = ieee80211_frequency_to_channel(cf);
|
|
|
|
*pos++ = 1; /* one channel in the subband*/
|
|
|
|
}
|
2022-06-02 03:17:34 +08:00
|
|
|
ADD_PRESENT_ELEM(WLAN_EID_SUPPORTED_CHANNELS);
|
2022-07-12 20:38:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* if present, add any custom IEs that go before HT */
|
|
|
|
offset = ieee80211_add_before_ht_elems(skb, extra_elems,
|
|
|
|
extra_elems_len,
|
|
|
|
offset);
|
|
|
|
|
|
|
|
if (sband->band != NL80211_BAND_6GHZ &&
|
2022-06-02 03:17:34 +08:00
|
|
|
!(assoc_data->link[link_id].conn_flags & IEEE80211_CONN_DISABLE_HT)) {
|
2022-07-12 20:38:02 +08:00
|
|
|
ieee80211_add_ht_ie(sdata, skb,
|
2022-06-02 03:17:34 +08:00
|
|
|
assoc_data->link[link_id].ap_ht_param,
|
|
|
|
sband, chan, smps_mode,
|
|
|
|
assoc_data->link[link_id].conn_flags);
|
|
|
|
ADD_PRESENT_ELEM(WLAN_EID_HT_CAPABILITY);
|
|
|
|
}
|
2022-07-12 20:38:02 +08:00
|
|
|
|
|
|
|
/* if present, add any custom IEs that go before VHT */
|
|
|
|
offset = ieee80211_add_before_vht_elems(skb, extra_elems,
|
|
|
|
extra_elems_len,
|
|
|
|
offset);
|
|
|
|
|
|
|
|
if (sband->band != NL80211_BAND_6GHZ &&
|
2022-06-02 03:17:34 +08:00
|
|
|
!(assoc_data->link[link_id].conn_flags & IEEE80211_CONN_DISABLE_VHT)) {
|
|
|
|
bool mu_mimo_owner =
|
2022-07-12 20:38:02 +08:00
|
|
|
ieee80211_add_vht_ie(sdata, skb, sband,
|
2022-06-02 03:17:34 +08:00
|
|
|
&assoc_data->link[link_id].ap_vht_cap,
|
|
|
|
assoc_data->link[link_id].conn_flags);
|
|
|
|
|
|
|
|
if (link)
|
|
|
|
link->conf->mu_mimo_owner = mu_mimo_owner;
|
|
|
|
ADD_PRESENT_ELEM(WLAN_EID_VHT_CAPABILITY);
|
|
|
|
}
|
2022-07-12 20:38:02 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If AP doesn't support HT, mark HE and EHT as disabled.
|
|
|
|
* If on the 5GHz band, make sure it supports VHT.
|
|
|
|
*/
|
2022-06-02 03:17:34 +08:00
|
|
|
if (assoc_data->link[link_id].conn_flags & IEEE80211_CONN_DISABLE_HT ||
|
2022-07-12 20:38:02 +08:00
|
|
|
(sband->band == NL80211_BAND_5GHZ &&
|
2022-06-02 03:17:34 +08:00
|
|
|
assoc_data->link[link_id].conn_flags & IEEE80211_CONN_DISABLE_VHT))
|
|
|
|
assoc_data->link[link_id].conn_flags |=
|
2022-07-12 20:38:02 +08:00
|
|
|
IEEE80211_CONN_DISABLE_HE |
|
|
|
|
IEEE80211_CONN_DISABLE_EHT;
|
|
|
|
|
|
|
|
/* if present, add any custom IEs that go before HE */
|
|
|
|
offset = ieee80211_add_before_he_elems(skb, extra_elems,
|
|
|
|
extra_elems_len,
|
|
|
|
offset);
|
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
if (!(assoc_data->link[link_id].conn_flags & IEEE80211_CONN_DISABLE_HE)) {
|
2022-08-26 02:57:32 +08:00
|
|
|
ieee80211_add_he_ie(sdata, skb, sband, smps_mode,
|
2022-06-02 03:17:34 +08:00
|
|
|
assoc_data->link[link_id].conn_flags);
|
|
|
|
ADD_PRESENT_EXT_ELEM(WLAN_EID_EXT_HE_CAPABILITY);
|
|
|
|
}
|
2022-07-12 20:38:02 +08:00
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
/*
|
|
|
|
* careful - need to know about all the present elems before
|
|
|
|
* calling ieee80211_assoc_add_ml_elem(), so add this one if
|
|
|
|
* we're going to put it after the ML element
|
|
|
|
*/
|
|
|
|
if (!(assoc_data->link[link_id].conn_flags & IEEE80211_CONN_DISABLE_EHT))
|
|
|
|
ADD_PRESENT_EXT_ELEM(WLAN_EID_EXT_EHT_CAPABILITY);
|
|
|
|
|
|
|
|
if (link_id == assoc_data->assoc_link_id)
|
|
|
|
ieee80211_assoc_add_ml_elem(sdata, skb, orig_capab, ext_capa,
|
|
|
|
present_elems);
|
|
|
|
|
|
|
|
/* crash if somebody gets it wrong */
|
|
|
|
present_elems = NULL;
|
|
|
|
|
|
|
|
if (!(assoc_data->link[link_id].conn_flags & IEEE80211_CONN_DISABLE_EHT))
|
2022-07-12 20:38:02 +08:00
|
|
|
ieee80211_add_eht_ie(sdata, skb, sband);
|
|
|
|
|
|
|
|
if (sband->band == NL80211_BAND_S1GHZ) {
|
|
|
|
ieee80211_add_aid_request_ie(sdata, skb);
|
|
|
|
ieee80211_add_s1g_capab_ie(sdata, &sband->s1g_cap, skb);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (iftd && iftd->vendor_elems.data && iftd->vendor_elems.len)
|
|
|
|
skb_put_data(skb, iftd->vendor_elems.data, iftd->vendor_elems.len);
|
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
if (link)
|
|
|
|
link->u.mgd.conn_flags = assoc_data->link[link_id].conn_flags;
|
|
|
|
|
2022-07-12 20:38:02 +08:00
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
static void ieee80211_add_non_inheritance_elem(struct sk_buff *skb,
|
|
|
|
const u16 *outer,
|
|
|
|
const u16 *inner)
|
|
|
|
{
|
|
|
|
unsigned int skb_len = skb->len;
|
|
|
|
bool added = false;
|
|
|
|
int i, j;
|
|
|
|
u8 *len, *list_len = NULL;
|
|
|
|
|
|
|
|
skb_put_u8(skb, WLAN_EID_EXTENSION);
|
|
|
|
len = skb_put(skb, 1);
|
|
|
|
skb_put_u8(skb, WLAN_EID_EXT_NON_INHERITANCE);
|
|
|
|
|
|
|
|
for (i = 0; i < PRESENT_ELEMS_MAX && outer[i]; i++) {
|
|
|
|
u16 elem = outer[i];
|
|
|
|
bool have_inner = false;
|
|
|
|
bool at_extension = false;
|
|
|
|
|
|
|
|
/* should at least be sorted in the sense of normal -> ext */
|
|
|
|
WARN_ON(at_extension && elem < PRESENT_ELEM_EXT_OFFS);
|
|
|
|
|
|
|
|
/* switch to extension list */
|
|
|
|
if (!at_extension && elem >= PRESENT_ELEM_EXT_OFFS) {
|
|
|
|
at_extension = true;
|
|
|
|
if (!list_len)
|
|
|
|
skb_put_u8(skb, 0);
|
|
|
|
list_len = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (j = 0; j < PRESENT_ELEMS_MAX && inner[j]; j++) {
|
|
|
|
if (elem == inner[j]) {
|
|
|
|
have_inner = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (have_inner)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!list_len) {
|
|
|
|
list_len = skb_put(skb, 1);
|
|
|
|
*list_len = 0;
|
|
|
|
}
|
|
|
|
*list_len += 1;
|
|
|
|
skb_put_u8(skb, (u8)elem);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!added)
|
|
|
|
skb_trim(skb, skb_len);
|
|
|
|
else
|
|
|
|
*len = skb->len - skb_len - 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ieee80211_assoc_add_ml_elem(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct sk_buff *skb, u16 capab,
|
|
|
|
const struct element *ext_capa,
|
|
|
|
const u16 *outer_present_elems)
|
|
|
|
{
|
|
|
|
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 ieee80211_multi_link_elem *ml_elem;
|
|
|
|
struct ieee80211_mle_basic_common_info *common;
|
|
|
|
const struct wiphy_iftype_ext_capab *ift_ext_capa;
|
|
|
|
__le16 eml_capa = 0, mld_capa_ops = 0;
|
|
|
|
unsigned int link_id;
|
|
|
|
u8 *ml_elem_len;
|
|
|
|
void *capab_pos;
|
|
|
|
|
|
|
|
if (!sdata->vif.valid_links)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ift_ext_capa = cfg80211_get_iftype_ext_capa(local->hw.wiphy,
|
|
|
|
ieee80211_vif_type_p2p(&sdata->vif));
|
|
|
|
if (ift_ext_capa) {
|
|
|
|
eml_capa = cpu_to_le16(ift_ext_capa->eml_capabilities);
|
|
|
|
mld_capa_ops = cpu_to_le16(ift_ext_capa->mld_capa_and_ops);
|
|
|
|
}
|
|
|
|
|
|
|
|
skb_put_u8(skb, WLAN_EID_EXTENSION);
|
|
|
|
ml_elem_len = skb_put(skb, 1);
|
|
|
|
skb_put_u8(skb, WLAN_EID_EXT_EHT_MULTI_LINK);
|
|
|
|
ml_elem = skb_put(skb, sizeof(*ml_elem));
|
|
|
|
ml_elem->control =
|
|
|
|
cpu_to_le16(IEEE80211_ML_CONTROL_TYPE_BASIC |
|
|
|
|
IEEE80211_MLC_BASIC_PRES_MLD_CAPA_OP);
|
|
|
|
common = skb_put(skb, sizeof(*common));
|
|
|
|
common->len = sizeof(*common) +
|
|
|
|
2; /* MLD capa/ops */
|
|
|
|
memcpy(common->mld_mac_addr, sdata->vif.addr, ETH_ALEN);
|
2022-07-30 08:51:08 +08:00
|
|
|
|
|
|
|
/* add EML_CAPA only if needed, see Draft P802.11be_D2.1, 35.3.17 */
|
|
|
|
if (eml_capa &
|
|
|
|
cpu_to_le16((IEEE80211_EML_CAP_EMLSR_SUPP |
|
|
|
|
IEEE80211_EML_CAP_EMLMR_SUPPORT))) {
|
|
|
|
common->len += 2; /* EML capabilities */
|
|
|
|
ml_elem->control |=
|
|
|
|
cpu_to_le16(IEEE80211_MLC_BASIC_PRES_EML_CAPA);
|
|
|
|
skb_put_data(skb, &eml_capa, sizeof(eml_capa));
|
|
|
|
}
|
2022-06-02 03:17:34 +08:00
|
|
|
/* need indication from userspace to support this */
|
|
|
|
mld_capa_ops &= ~cpu_to_le16(IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP);
|
|
|
|
skb_put_data(skb, &mld_capa_ops, sizeof(mld_capa_ops));
|
|
|
|
|
|
|
|
for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {
|
|
|
|
u16 link_present_elems[PRESENT_ELEMS_MAX] = {};
|
|
|
|
const u8 *extra_elems;
|
|
|
|
size_t extra_elems_len;
|
|
|
|
size_t extra_used;
|
|
|
|
u8 *subelem_len = NULL;
|
|
|
|
__le16 ctrl;
|
|
|
|
|
|
|
|
if (!assoc_data->link[link_id].bss ||
|
|
|
|
link_id == assoc_data->assoc_link_id)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
extra_elems = assoc_data->link[link_id].elems;
|
|
|
|
extra_elems_len = assoc_data->link[link_id].elems_len;
|
|
|
|
|
|
|
|
skb_put_u8(skb, IEEE80211_MLE_SUBELEM_PER_STA_PROFILE);
|
|
|
|
subelem_len = skb_put(skb, 1);
|
|
|
|
|
|
|
|
ctrl = cpu_to_le16(link_id |
|
|
|
|
IEEE80211_MLE_STA_CONTROL_COMPLETE_PROFILE |
|
|
|
|
IEEE80211_MLE_STA_CONTROL_STA_MAC_ADDR_PRESENT);
|
|
|
|
skb_put_data(skb, &ctrl, sizeof(ctrl));
|
|
|
|
skb_put_u8(skb, 1 + ETH_ALEN); /* STA Info Length */
|
|
|
|
skb_put_data(skb, assoc_data->link[link_id].addr,
|
|
|
|
ETH_ALEN);
|
|
|
|
/*
|
|
|
|
* Now add the contents of the (re)association request,
|
|
|
|
* but the "listen interval" and "current AP address"
|
|
|
|
* (if applicable) are skipped. So we only have
|
|
|
|
* the capability field (remember the position and fill
|
|
|
|
* later), followed by the elements added below by
|
|
|
|
* calling ieee80211_assoc_link_elems().
|
|
|
|
*/
|
|
|
|
capab_pos = skb_put(skb, 2);
|
|
|
|
|
|
|
|
extra_used = ieee80211_assoc_link_elems(sdata, skb, &capab,
|
|
|
|
ext_capa,
|
|
|
|
extra_elems,
|
|
|
|
extra_elems_len,
|
|
|
|
link_id, NULL,
|
|
|
|
link_present_elems);
|
|
|
|
if (extra_elems)
|
|
|
|
skb_put_data(skb, extra_elems + extra_used,
|
|
|
|
extra_elems_len - extra_used);
|
|
|
|
|
|
|
|
put_unaligned_le16(capab, capab_pos);
|
|
|
|
|
|
|
|
ieee80211_add_non_inheritance_elem(skb, outer_present_elems,
|
|
|
|
link_present_elems);
|
|
|
|
|
|
|
|
ieee80211_fragment_element(skb, subelem_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
ieee80211_fragment_element(skb, ml_elem_len);
|
|
|
|
}
|
|
|
|
|
2022-01-05 16:15:59 +08:00
|
|
|
static int ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
|
2012-01-20 20:55:27 +08:00
|
|
|
{
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data;
|
2022-06-02 03:17:34 +08:00
|
|
|
struct ieee80211_link_data *link;
|
2012-01-20 20:55:27 +08:00
|
|
|
struct sk_buff *skb;
|
|
|
|
struct ieee80211_mgmt *mgmt;
|
2019-02-15 08:14:33 +08:00
|
|
|
u8 *pos, qos_info, *ie_start;
|
2022-07-12 20:38:02 +08:00
|
|
|
size_t offset, noffset;
|
2022-06-02 03:17:34 +08:00
|
|
|
u16 capab = WLAN_CAPABILITY_ESS, link_capab;
|
2020-09-22 10:28:10 +08:00
|
|
|
__le16 listen_int;
|
2020-01-31 19:31:11 +08:00
|
|
|
struct element *ext_capa = NULL;
|
2021-06-18 18:41:53 +08:00
|
|
|
enum nl80211_iftype iftype = ieee80211_vif_type_p2p(&sdata->vif);
|
2021-06-18 18:41:55 +08:00
|
|
|
struct ieee80211_prep_tx_info info = {};
|
2022-06-02 03:17:34 +08:00
|
|
|
unsigned int link_id, n_links = 0;
|
|
|
|
u16 present_elems[PRESENT_ELEMS_MAX] = {};
|
2022-07-12 20:38:02 +08:00
|
|
|
void *capab_pos;
|
2022-06-02 03:17:34 +08:00
|
|
|
size_t size;
|
2022-01-05 16:15:59 +08:00
|
|
|
int ret;
|
2020-01-31 19:31:11 +08:00
|
|
|
|
|
|
|
/* we know it's writable, cast away the const */
|
|
|
|
if (assoc_data->ie_len)
|
|
|
|
ext_capa = (void *)cfg80211_find_elem(WLAN_EID_EXT_CAPABILITY,
|
|
|
|
assoc_data->ie,
|
|
|
|
assoc_data->ie_len);
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2013-05-10 18:32:47 +08:00
|
|
|
sdata_assert_lock(sdata);
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
size = local->hw.extra_tx_headroom +
|
|
|
|
sizeof(*mgmt) + /* bit too much but doesn't matter */
|
|
|
|
2 + assoc_data->ssid_len + /* SSID */
|
|
|
|
assoc_data->ie_len + /* extra IEs */
|
|
|
|
(assoc_data->fils_kek_len ? 16 /* AES-SIV */ : 0) +
|
|
|
|
9; /* WMM */
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {
|
|
|
|
struct cfg80211_bss *cbss = assoc_data->link[link_id].bss;
|
|
|
|
const struct ieee80211_sband_iftype_data *iftd;
|
|
|
|
struct ieee80211_supported_band *sband;
|
|
|
|
|
|
|
|
if (!cbss)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
sband = local->hw.wiphy->bands[cbss->channel->band];
|
|
|
|
|
|
|
|
n_links++;
|
|
|
|
/* add STA profile elements length */
|
|
|
|
size += assoc_data->link[link_id].elems_len;
|
|
|
|
/* and supported rates length */
|
|
|
|
size += 4 + sband->n_bitrates;
|
|
|
|
/* supported channels */
|
|
|
|
size += 2 + 2 * sband->n_channels;
|
|
|
|
|
|
|
|
iftd = ieee80211_get_sband_iftype_data(sband, iftype);
|
|
|
|
if (iftd)
|
|
|
|
size += iftd->vendor_elems.len;
|
|
|
|
|
|
|
|
/* power capability */
|
|
|
|
size += 4;
|
|
|
|
|
|
|
|
/* HT, VHT, HE, EHT */
|
|
|
|
size += 2 + sizeof(struct ieee80211_ht_cap);
|
|
|
|
size += 2 + sizeof(struct ieee80211_vht_cap);
|
|
|
|
size += 2 + 1 + sizeof(struct ieee80211_he_cap_elem) +
|
|
|
|
sizeof(struct ieee80211_he_mcs_nss_supp) +
|
|
|
|
IEEE80211_HE_PPE_THRES_MAX_LEN;
|
2021-06-18 18:41:53 +08:00
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
if (sband->band == NL80211_BAND_6GHZ)
|
|
|
|
size += 2 + 1 + sizeof(struct ieee80211_he_6ghz_capa);
|
|
|
|
|
|
|
|
size += 2 + 1 + sizeof(struct ieee80211_eht_cap_elem) +
|
2022-07-01 22:22:29 +08:00
|
|
|
sizeof(struct ieee80211_eht_mcs_nss_supp) +
|
2022-06-02 03:17:34 +08:00
|
|
|
IEEE80211_EHT_PPE_THRES_MAX_LEN;
|
|
|
|
|
|
|
|
/* non-inheritance element */
|
|
|
|
size += 2 + 2 + PRESENT_ELEMS_MAX;
|
|
|
|
|
|
|
|
/* should be the same across all BSSes */
|
|
|
|
if (cbss->capability & WLAN_CAPABILITY_PRIVACY)
|
|
|
|
capab |= WLAN_CAPABILITY_PRIVACY;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sdata->vif.valid_links) {
|
|
|
|
/* consider the multi-link element with STA profile */
|
|
|
|
size += sizeof(struct ieee80211_multi_link_elem);
|
|
|
|
/* max common info field in basic multi-link element */
|
|
|
|
size += sizeof(struct ieee80211_mle_basic_common_info) +
|
|
|
|
2 + /* capa & op */
|
|
|
|
2; /* EML capa */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The capability elements were already considered above;
|
|
|
|
* note this over-estimates a bit because there's no
|
|
|
|
* STA profile for the assoc link.
|
|
|
|
*/
|
|
|
|
size += (n_links - 1) *
|
|
|
|
(1 + 1 + /* subelement ID/length */
|
|
|
|
2 + /* STA control */
|
|
|
|
1 + ETH_ALEN + 2 /* STA Info field */);
|
|
|
|
}
|
|
|
|
|
|
|
|
link = sdata_dereference(sdata->link[assoc_data->assoc_link_id], sdata);
|
|
|
|
if (WARN_ON(!link))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (WARN_ON(!assoc_data->link[assoc_data->assoc_link_id].bss))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
skb = alloc_skb(size, GFP_KERNEL);
|
2012-01-20 20:55:27 +08:00
|
|
|
if (!skb)
|
2022-01-05 16:15:59 +08:00
|
|
|
return -ENOMEM;
|
2012-01-20 20:55:27 +08:00
|
|
|
|
|
|
|
skb_reserve(skb, local->hw.extra_tx_headroom);
|
|
|
|
|
2014-09-03 20:25:02 +08:00
|
|
|
if (ifmgd->flags & IEEE80211_STA_ENABLE_RRM)
|
|
|
|
capab |= WLAN_CAPABILITY_RADIO_MEASURE;
|
|
|
|
|
2022-07-12 20:38:02 +08:00
|
|
|
/* Set MBSSID support for HE AP if needed */
|
|
|
|
if (ieee80211_hw_check(&local->hw, SUPPORTS_ONLY_HE_MULTI_BSSID) &&
|
|
|
|
!(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE) &&
|
|
|
|
ext_capa && ext_capa->datalen >= 3)
|
|
|
|
ext_capa->data[2] |= WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT;
|
|
|
|
|
networking: convert many more places to skb_put_zero()
There were many places that my previous spatch didn't find,
as pointed out by yuan linyu in various patches.
The following spatch found many more and also removes the
now unnecessary casts:
@@
identifier p, p2;
expression len;
expression skb;
type t, t2;
@@
(
-p = skb_put(skb, len);
+p = skb_put_zero(skb, len);
|
-p = (t)skb_put(skb, len);
+p = skb_put_zero(skb, len);
)
... when != p
(
p2 = (t2)p;
-memset(p2, 0, len);
|
-memset(p, 0, len);
)
@@
type t, t2;
identifier p, p2;
expression skb;
@@
t *p;
...
(
-p = skb_put(skb, sizeof(t));
+p = skb_put_zero(skb, sizeof(t));
|
-p = (t *)skb_put(skb, sizeof(t));
+p = skb_put_zero(skb, sizeof(t));
)
... when != p
(
p2 = (t2)p;
-memset(p2, 0, sizeof(*p));
|
-memset(p, 0, sizeof(*p));
)
@@
expression skb, len;
@@
-memset(skb_put(skb, len), 0, len);
+skb_put_zero(skb, len);
Apply it to the tree (with one manual fixup to keep the
comment in vxlan.c, which spatch removed.)
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 20:29:19 +08:00
|
|
|
mgmt = skb_put_zero(skb, 24);
|
2022-07-22 05:35:21 +08:00
|
|
|
memcpy(mgmt->da, sdata->vif.cfg.ap_addr, ETH_ALEN);
|
|
|
|
memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
|
|
|
|
memcpy(mgmt->bssid, sdata->vif.cfg.ap_addr, ETH_ALEN);
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
listen_int = cpu_to_le16(assoc_data->s1g ?
|
2020-09-22 10:28:10 +08:00
|
|
|
ieee80211_encode_usf(local->hw.conf.listen_interval) :
|
|
|
|
local->hw.conf.listen_interval);
|
2022-06-02 03:17:34 +08:00
|
|
|
if (!is_zero_ether_addr(assoc_data->prev_ap_addr)) {
|
2012-01-20 20:55:27 +08:00
|
|
|
skb_put(skb, 10);
|
|
|
|
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
|
|
|
|
IEEE80211_STYPE_REASSOC_REQ);
|
2022-07-12 20:38:02 +08:00
|
|
|
capab_pos = &mgmt->u.reassoc_req.capab_info;
|
2020-09-22 10:28:10 +08:00
|
|
|
mgmt->u.reassoc_req.listen_interval = listen_int;
|
2022-06-02 03:17:34 +08:00
|
|
|
memcpy(mgmt->u.reassoc_req.current_ap,
|
|
|
|
assoc_data->prev_ap_addr, ETH_ALEN);
|
2021-06-18 18:41:55 +08:00
|
|
|
info.subtype = IEEE80211_STYPE_REASSOC_REQ;
|
2012-01-20 20:55:27 +08:00
|
|
|
} else {
|
|
|
|
skb_put(skb, 4);
|
|
|
|
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
|
|
|
|
IEEE80211_STYPE_ASSOC_REQ);
|
2022-07-12 20:38:02 +08:00
|
|
|
capab_pos = &mgmt->u.assoc_req.capab_info;
|
2020-09-22 10:28:10 +08:00
|
|
|
mgmt->u.assoc_req.listen_interval = listen_int;
|
2021-06-18 18:41:55 +08:00
|
|
|
info.subtype = IEEE80211_STYPE_ASSOC_REQ;
|
2012-01-20 20:55:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* SSID */
|
|
|
|
pos = skb_put(skb, 2 + assoc_data->ssid_len);
|
2019-02-15 08:14:33 +08:00
|
|
|
ie_start = pos;
|
2012-01-20 20:55:27 +08:00
|
|
|
*pos++ = WLAN_EID_SSID;
|
|
|
|
*pos++ = assoc_data->ssid_len;
|
|
|
|
memcpy(pos, assoc_data->ssid, assoc_data->ssid_len);
|
|
|
|
|
2022-07-12 20:38:02 +08:00
|
|
|
/* add the elements for the assoc (main) link */
|
2022-06-02 03:17:34 +08:00
|
|
|
link_capab = capab;
|
|
|
|
offset = ieee80211_assoc_link_elems(sdata, skb, &link_capab,
|
2022-07-12 20:38:02 +08:00
|
|
|
ext_capa,
|
|
|
|
assoc_data->ie,
|
|
|
|
assoc_data->ie_len,
|
2022-06-02 03:17:34 +08:00
|
|
|
assoc_data->assoc_link_id, link,
|
|
|
|
present_elems);
|
|
|
|
put_unaligned_le16(link_capab, capab_pos);
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2022-07-12 20:38:02 +08:00
|
|
|
/* if present, add any custom non-vendor IEs */
|
2013-10-25 17:31:42 +08:00
|
|
|
if (assoc_data->ie_len) {
|
2012-01-20 20:55:27 +08:00
|
|
|
noffset = ieee80211_ie_split_vendor(assoc_data->ie,
|
|
|
|
assoc_data->ie_len,
|
|
|
|
offset);
|
2017-06-18 22:52:04 +08:00
|
|
|
skb_put_data(skb, assoc_data->ie + offset, noffset - offset);
|
2012-01-20 20:55:27 +08:00
|
|
|
offset = noffset;
|
|
|
|
}
|
|
|
|
|
2012-03-08 22:02:05 +08:00
|
|
|
if (assoc_data->wmm) {
|
|
|
|
if (assoc_data->uapsd) {
|
2012-03-14 22:15:03 +08:00
|
|
|
qos_info = ifmgd->uapsd_queues;
|
|
|
|
qos_info |= (ifmgd->uapsd_max_sp_len <<
|
2012-01-20 20:55:27 +08:00
|
|
|
IEEE80211_WMM_IE_STA_QOSINFO_SP_SHIFT);
|
|
|
|
} else {
|
|
|
|
qos_info = 0;
|
|
|
|
}
|
|
|
|
|
2014-07-17 22:14:23 +08:00
|
|
|
pos = ieee80211_add_wmm_info_ie(skb_put(skb, 9), qos_info);
|
2012-01-20 20:55:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* add any remaining custom (i.e. vendor specific here) IEs */
|
2013-10-25 17:31:42 +08:00
|
|
|
if (assoc_data->ie_len) {
|
2012-01-20 20:55:27 +08:00
|
|
|
noffset = assoc_data->ie_len;
|
2017-06-18 22:52:04 +08:00
|
|
|
skb_put_data(skb, assoc_data->ie + offset, noffset - offset);
|
2012-01-20 20:55:27 +08:00
|
|
|
}
|
|
|
|
|
2022-01-05 16:15:59 +08:00
|
|
|
if (assoc_data->fils_kek_len) {
|
|
|
|
ret = fils_encrypt_assoc_req(skb, assoc_data);
|
|
|
|
if (ret < 0) {
|
|
|
|
dev_kfree_skb(skb);
|
|
|
|
return ret;
|
|
|
|
}
|
2016-10-27 05:42:05 +08:00
|
|
|
}
|
|
|
|
|
2019-02-15 08:14:33 +08:00
|
|
|
pos = skb_tail_pointer(skb);
|
|
|
|
kfree(ifmgd->assoc_req_ies);
|
|
|
|
ifmgd->assoc_req_ies = kmemdup(ie_start, pos - ie_start, GFP_ATOMIC);
|
2022-01-05 16:15:59 +08:00
|
|
|
if (!ifmgd->assoc_req_ies) {
|
|
|
|
dev_kfree_skb(skb);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2019-02-15 08:14:33 +08:00
|
|
|
ifmgd->assoc_req_ies_len = pos - ie_start;
|
|
|
|
|
2021-06-18 18:41:55 +08:00
|
|
|
drv_mgd_prepare_tx(local, sdata, &info);
|
2012-06-27 19:18:36 +08:00
|
|
|
|
2012-01-20 20:55:27 +08:00
|
|
|
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
|
2015-06-03 03:39:54 +08:00
|
|
|
if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS))
|
2013-01-29 22:02:27 +08:00
|
|
|
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS |
|
|
|
|
IEEE80211_TX_INTFL_MLME_CONN_TX;
|
2012-01-20 20:55:27 +08:00
|
|
|
ieee80211_tx_skb(sdata, skb);
|
2022-01-05 16:15:59 +08:00
|
|
|
|
|
|
|
return 0;
|
2012-01-20 20:55:27 +08:00
|
|
|
}
|
|
|
|
|
2009-02-10 23:09:31 +08: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-06 02:16:26 +08:00
|
|
|
skb = ieee80211_pspoll_get(&local->hw, &sdata->vif);
|
|
|
|
if (!skb)
|
2009-02-10 23:09:31 +08:00
|
|
|
return;
|
|
|
|
|
2010-01-06 02:16:26 +08:00
|
|
|
pspoll = (struct ieee80211_pspoll *) skb->data;
|
|
|
|
pspoll->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
|
2009-02-10 23:09:31 +08:00
|
|
|
|
2009-11-19 01:42:05 +08:00
|
|
|
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
|
|
|
|
ieee80211_tx_skb(sdata, skb);
|
2009-02-10 23:09:31 +08:00
|
|
|
}
|
|
|
|
|
2009-04-16 19:17:24 +08:00
|
|
|
void ieee80211_send_nullfunc(struct ieee80211_local *local,
|
|
|
|
struct ieee80211_sub_if_data *sdata,
|
2015-09-24 22:14:55 +08:00
|
|
|
bool powersave)
|
2009-04-16 19:17:24 +08:00
|
|
|
{
|
|
|
|
struct sk_buff *skb;
|
2010-01-06 02:16:26 +08:00
|
|
|
struct ieee80211_hdr_3addr *nullfunc;
|
2011-09-29 23:04:04 +08:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2009-04-16 19:17:24 +08:00
|
|
|
|
2022-09-02 22:12:47 +08:00
|
|
|
skb = ieee80211_nullfunc_get(&local->hw, &sdata->vif, -1,
|
|
|
|
!ieee80211_hw_check(&local->hw,
|
|
|
|
DOESNT_SUPPORT_QOS_NDP));
|
2010-01-06 02:16:26 +08:00
|
|
|
if (!skb)
|
2009-04-16 19:17:24 +08:00
|
|
|
return;
|
|
|
|
|
2010-01-06 02:16:26 +08:00
|
|
|
nullfunc = (struct ieee80211_hdr_3addr *) skb->data;
|
2009-04-16 19:17:24 +08:00
|
|
|
if (powersave)
|
2010-01-06 02:16:26 +08:00
|
|
|
nullfunc->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
|
2009-04-16 19:17:24 +08:00
|
|
|
|
2013-02-12 01:21:07 +08:00
|
|
|
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
|
|
|
|
IEEE80211_TX_INTFL_OFFCHAN_TX_OK;
|
2013-06-04 18:44:52 +08:00
|
|
|
|
2015-06-03 03:39:54 +08:00
|
|
|
if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS))
|
2013-06-04 18:44:52 +08:00
|
|
|
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
|
|
|
|
|
2013-08-27 17:36:35 +08:00
|
|
|
if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL)
|
2011-09-29 23:04:04 +08:00
|
|
|
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_USE_MINRATE;
|
|
|
|
|
2009-11-19 01:42:05 +08:00
|
|
|
ieee80211_tx_skb(sdata, skb);
|
2009-04-16 19:17:24 +08:00
|
|
|
}
|
|
|
|
|
2021-07-02 13:01:11 +08:00
|
|
|
void ieee80211_send_4addr_nullfunc(struct ieee80211_local *local,
|
|
|
|
struct ieee80211_sub_if_data *sdata)
|
2010-01-09 01:06:26 +08:00
|
|
|
{
|
|
|
|
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-30 05:17:31 +08:00
|
|
|
if (!skb)
|
2010-01-09 01:06:26 +08:00
|
|
|
return;
|
2011-08-30 05:17:31 +08:00
|
|
|
|
2010-01-09 01:06:26 +08:00
|
|
|
skb_reserve(skb, local->hw.extra_tx_headroom);
|
|
|
|
|
networking: convert many more places to skb_put_zero()
There were many places that my previous spatch didn't find,
as pointed out by yuan linyu in various patches.
The following spatch found many more and also removes the
now unnecessary casts:
@@
identifier p, p2;
expression len;
expression skb;
type t, t2;
@@
(
-p = skb_put(skb, len);
+p = skb_put_zero(skb, len);
|
-p = (t)skb_put(skb, len);
+p = skb_put_zero(skb, len);
)
... when != p
(
p2 = (t2)p;
-memset(p2, 0, len);
|
-memset(p, 0, len);
)
@@
type t, t2;
identifier p, p2;
expression skb;
@@
t *p;
...
(
-p = skb_put(skb, sizeof(t));
+p = skb_put_zero(skb, sizeof(t));
|
-p = (t *)skb_put(skb, sizeof(t));
+p = skb_put_zero(skb, sizeof(t));
)
... when != p
(
p2 = (t2)p;
-memset(p2, 0, sizeof(*p));
|
-memset(p, 0, sizeof(*p));
)
@@
expression skb, len;
@@
-memset(skb_put(skb, len), 0, len);
+skb_put_zero(skb, len);
Apply it to the tree (with one manual fixup to keep the
comment in vxlan.c, which spatch removed.)
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 20:29:19 +08:00
|
|
|
nullfunc = skb_put_zero(skb, 30);
|
2010-01-09 01:06:26 +08:00
|
|
|
fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC |
|
|
|
|
IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
|
|
|
|
nullfunc->frame_control = fc;
|
2022-05-16 21:00:15 +08:00
|
|
|
memcpy(nullfunc->addr1, sdata->deflink.u.mgd.bssid, ETH_ALEN);
|
2010-01-09 01:06:26 +08:00
|
|
|
memcpy(nullfunc->addr2, sdata->vif.addr, ETH_ALEN);
|
2022-05-16 21:00:15 +08:00
|
|
|
memcpy(nullfunc->addr3, sdata->deflink.u.mgd.bssid, ETH_ALEN);
|
2010-01-09 01:06:26 +08:00
|
|
|
memcpy(nullfunc->addr4, sdata->vif.addr, ETH_ALEN);
|
|
|
|
|
|
|
|
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
|
2022-07-14 17:16:36 +08:00
|
|
|
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_USE_MINRATE;
|
2010-01-09 01:06:26 +08:00
|
|
|
ieee80211_tx_skb(sdata, skb);
|
|
|
|
}
|
|
|
|
|
2009-05-15 17:52:31 +08:00
|
|
|
/* spectrum management related things */
|
|
|
|
static void ieee80211_chswitch_work(struct work_struct *work)
|
|
|
|
{
|
2022-06-24 22:18:56 +08:00
|
|
|
struct ieee80211_link_data *link =
|
|
|
|
container_of(work, struct ieee80211_link_data, u.mgd.chswitch_work);
|
|
|
|
struct ieee80211_sub_if_data *sdata = link->sdata;
|
2013-03-25 23:26:57 +08:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
2009-05-15 17:52:31 +08:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2013-09-01 22:15:51 +08:00
|
|
|
int ret;
|
2009-05-15 17:52:31 +08:00
|
|
|
|
2009-12-23 20:15:31 +08:00
|
|
|
if (!ieee80211_sdata_running(sdata))
|
2009-05-15 17:52:31 +08:00
|
|
|
return;
|
|
|
|
|
2013-05-10 18:32:47 +08:00
|
|
|
sdata_lock(sdata);
|
2014-06-25 18:35:09 +08:00
|
|
|
mutex_lock(&local->mtx);
|
|
|
|
mutex_lock(&local->chanctx_mtx);
|
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
if (!ifmgd->associated)
|
|
|
|
goto out;
|
2009-05-15 17:52:31 +08:00
|
|
|
|
2022-06-24 22:18:56 +08:00
|
|
|
if (!link->conf->csa_active)
|
2014-06-25 18:35:09 +08:00
|
|
|
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
|
|
|
|
*/
|
|
|
|
|
2022-06-24 22:18:56 +08:00
|
|
|
if (link->reserved_chanctx) {
|
2014-06-25 18:35:09 +08:00
|
|
|
/*
|
|
|
|
* with multi-vif csa driver may call ieee80211_csa_finish()
|
|
|
|
* many times while waiting for other interfaces to use their
|
|
|
|
* reservations
|
|
|
|
*/
|
2022-06-24 22:18:56 +08:00
|
|
|
if (link->reserved_ready)
|
2014-06-25 18:35:09 +08:00
|
|
|
goto out;
|
|
|
|
|
2022-06-24 22:18:56 +08:00
|
|
|
ret = ieee80211_link_use_reserved_context(link);
|
2014-06-25 18:35:09 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2022-06-24 22:18:56 +08:00
|
|
|
if (!cfg80211_chandef_identical(&link->conf->chandef,
|
|
|
|
&link->csa_chandef)) {
|
2013-09-01 22:15:51 +08:00
|
|
|
sdata_info(sdata,
|
2014-06-25 18:35:09 +08:00
|
|
|
"failed to finalize channel switch, disconnecting\n");
|
2013-09-01 22:15:51 +08:00
|
|
|
ieee80211_queue_work(&sdata->local->hw,
|
|
|
|
&ifmgd->csa_connection_drop_work);
|
|
|
|
goto out;
|
|
|
|
}
|
2013-03-25 23:26:57 +08:00
|
|
|
|
2022-06-24 22:18:56 +08:00
|
|
|
link->u.mgd.csa_waiting_bcn = true;
|
2014-10-08 14:48:39 +08:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2022-06-24 22:18:56 +08:00
|
|
|
static void ieee80211_chswitch_post_beacon(struct ieee80211_link_data *link)
|
2014-10-08 14:48:39 +08:00
|
|
|
{
|
2022-06-24 22:18:56 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = link->sdata;
|
2014-10-08 14:48:39 +08:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
sdata_assert_lock(sdata);
|
|
|
|
|
2022-06-24 22:18:56 +08:00
|
|
|
WARN_ON(!link->conf->csa_active);
|
2014-06-25 18:35:09 +08:00
|
|
|
|
2022-06-24 22:18:56 +08:00
|
|
|
if (link->csa_block_tx) {
|
2014-06-13 21:30:07 +08:00
|
|
|
ieee80211_wake_vif_queues(local, sdata,
|
|
|
|
IEEE80211_QUEUE_STOP_REASON_CSA);
|
2022-06-24 22:18:56 +08:00
|
|
|
link->csa_block_tx = false;
|
2014-06-13 21:30:07 +08:00
|
|
|
}
|
2013-09-01 22:15:51 +08:00
|
|
|
|
2022-06-24 22:18:56 +08:00
|
|
|
link->conf->csa_active = false;
|
|
|
|
link->u.mgd.csa_waiting_bcn = false;
|
2021-04-08 20:31:25 +08:00
|
|
|
/*
|
|
|
|
* If the CSA IE is still present on the beacon after the switch,
|
|
|
|
* we need to consider it as a new CSA (possibly to self).
|
|
|
|
*/
|
2022-06-24 22:18:56 +08:00
|
|
|
link->u.mgd.beacon_crc_valid = false;
|
2014-10-08 14:48:39 +08:00
|
|
|
|
2014-10-08 14:48:38 +08: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 14:48:39 +08:00
|
|
|
return;
|
2014-10-08 14:48:38 +08:00
|
|
|
}
|
2014-12-16 22:01:39 +08:00
|
|
|
|
2023-01-31 08:12:26 +08:00
|
|
|
cfg80211_ch_switch_notify(sdata->dev, &link->reserved_chandef, 0, 0);
|
2009-05-15 17:52:31 +08:00
|
|
|
}
|
|
|
|
|
2010-05-11 22:20:57 +08:00
|
|
|
void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success)
|
|
|
|
{
|
2012-07-26 23:24:39 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2010-05-11 22:20:57 +08:00
|
|
|
|
2022-06-24 22:18:56 +08:00
|
|
|
if (WARN_ON(sdata->vif.valid_links))
|
|
|
|
success = false;
|
|
|
|
|
2010-05-11 22:20:57 +08:00
|
|
|
trace_api_chswitch_done(sdata, success);
|
|
|
|
if (!success) {
|
2012-08-02 04:32:45 +08:00
|
|
|
sdata_info(sdata,
|
|
|
|
"driver channel switch failed, disconnecting\n");
|
|
|
|
ieee80211_queue_work(&sdata->local->hw,
|
|
|
|
&ifmgd->csa_connection_drop_work);
|
|
|
|
} else {
|
2022-06-24 22:18:56 +08:00
|
|
|
ieee80211_queue_work(&sdata->local->hw,
|
|
|
|
&sdata->deflink.u.mgd.chswitch_work);
|
2010-05-11 22:20:57 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ieee80211_chswitch_done);
|
|
|
|
|
2017-10-17 07:35:49 +08:00
|
|
|
static void ieee80211_chswitch_timer(struct timer_list *t)
|
2009-05-15 17:52:31 +08:00
|
|
|
{
|
2022-06-24 22:18:56 +08:00
|
|
|
struct ieee80211_link_data *link =
|
|
|
|
from_timer(link, t, u.mgd.chswitch_timer);
|
2009-05-17 17:40:42 +08:00
|
|
|
|
2022-06-24 22:18:56 +08:00
|
|
|
ieee80211_queue_work(&link->sdata->local->hw,
|
|
|
|
&link->u.mgd.chswitch_work);
|
2009-05-15 17:52:31 +08:00
|
|
|
}
|
|
|
|
|
2019-02-06 19:17:10 +08:00
|
|
|
static void
|
2022-06-24 22:18:56 +08:00
|
|
|
ieee80211_sta_abort_chanswitch(struct ieee80211_link_data *link)
|
2019-02-06 19:17:10 +08:00
|
|
|
{
|
2022-06-24 22:18:56 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = link->sdata;
|
2019-02-06 19:17:10 +08:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
|
|
|
|
if (!local->ops->abort_channel_switch)
|
|
|
|
return;
|
|
|
|
|
|
|
|
mutex_lock(&local->mtx);
|
|
|
|
|
|
|
|
mutex_lock(&local->chanctx_mtx);
|
2022-06-24 22:18:56 +08:00
|
|
|
ieee80211_link_unreserve_chanctx(link);
|
2019-02-06 19:17:10 +08:00
|
|
|
mutex_unlock(&local->chanctx_mtx);
|
|
|
|
|
2022-06-24 22:18:56 +08:00
|
|
|
if (link->csa_block_tx)
|
2019-02-06 19:17:10 +08:00
|
|
|
ieee80211_wake_vif_queues(local, sdata,
|
|
|
|
IEEE80211_QUEUE_STOP_REASON_CSA);
|
|
|
|
|
2022-06-24 22:18:56 +08:00
|
|
|
link->csa_block_tx = false;
|
|
|
|
link->conf->csa_active = false;
|
2019-02-06 19:17:10 +08:00
|
|
|
|
|
|
|
mutex_unlock(&local->mtx);
|
|
|
|
|
|
|
|
drv_abort_channel_switch(sdata);
|
|
|
|
}
|
|
|
|
|
2013-03-26 21:02:26 +08:00
|
|
|
static void
|
2022-06-24 22:18:56 +08:00
|
|
|
ieee80211_sta_process_chanswitch(struct ieee80211_link_data *link,
|
2014-10-08 14:48:35 +08:00
|
|
|
u64 timestamp, u32 device_timestamp,
|
|
|
|
struct ieee802_11_elems *elems,
|
2013-05-03 15:35:35 +08:00
|
|
|
bool beacon)
|
2009-05-15 17:52:31 +08:00
|
|
|
{
|
2022-06-24 22:18:56 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = link->sdata;
|
2013-03-26 21:13:58 +08:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
2009-05-15 17:52:31 +08:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2022-06-24 22:18:56 +08:00
|
|
|
struct cfg80211_bss *cbss = link->u.mgd.bss;
|
2014-06-25 18:35:09 +08:00
|
|
|
struct ieee80211_chanctx_conf *conf;
|
2012-07-26 23:24:39 +08:00
|
|
|
struct ieee80211_chanctx *chanctx;
|
2016-04-12 21:56:15 +08:00
|
|
|
enum nl80211_band current_band;
|
2013-10-15 10:08:29 +08:00
|
|
|
struct ieee80211_csa_ie csa_ie;
|
2014-10-08 14:48:37 +08:00
|
|
|
struct ieee80211_channel_switch ch_switch;
|
2020-05-29 03:34:35 +08:00
|
|
|
struct ieee80211_bss *bss;
|
2013-08-28 19:41:29 +08:00
|
|
|
int res;
|
2009-05-15 17:52:31 +08:00
|
|
|
|
2013-05-10 18:32:47 +08:00
|
|
|
sdata_assert_lock(sdata);
|
2009-07-07 09:45:17 +08:00
|
|
|
|
2013-03-26 21:02:26 +08:00
|
|
|
if (!cbss)
|
2009-05-15 17:52:31 +08:00
|
|
|
return;
|
|
|
|
|
2013-03-26 21:13:58 +08:00
|
|
|
if (local->scanning)
|
2009-05-15 17:52:31 +08:00
|
|
|
return;
|
|
|
|
|
2013-08-28 19:41:29 +08:00
|
|
|
current_band = cbss->channel->band;
|
2020-05-29 03:34:35 +08:00
|
|
|
bss = (void *)cbss->priv;
|
2014-10-28 19:33:04 +08:00
|
|
|
res = ieee80211_parse_ch_switch_ie(sdata, elems, current_band,
|
2020-05-29 03:34:35 +08:00
|
|
|
bss->vht_cap_info,
|
2022-06-24 22:18:56 +08:00
|
|
|
link->u.mgd.conn_flags,
|
|
|
|
link->u.mgd.bssid, &csa_ie);
|
2019-02-06 19:17:15 +08:00
|
|
|
|
|
|
|
if (!res) {
|
|
|
|
ch_switch.timestamp = timestamp;
|
|
|
|
ch_switch.device_timestamp = device_timestamp;
|
2020-01-31 19:12:51 +08:00
|
|
|
ch_switch.block_tx = csa_ie.mode;
|
2019-02-06 19:17:15 +08:00
|
|
|
ch_switch.chandef = csa_ie.chandef;
|
|
|
|
ch_switch.count = csa_ie.count;
|
|
|
|
ch_switch.delay = csa_ie.max_switch_time;
|
|
|
|
}
|
|
|
|
|
2021-04-09 17:40:16 +08:00
|
|
|
if (res < 0)
|
|
|
|
goto lock_and_drop_connection;
|
2019-02-06 19:17:10 +08:00
|
|
|
|
2022-06-24 22:18:56 +08:00
|
|
|
if (beacon && link->conf->csa_active &&
|
|
|
|
!link->u.mgd.csa_waiting_bcn) {
|
2019-02-06 19:17:15 +08:00
|
|
|
if (res)
|
2022-06-24 22:18:56 +08:00
|
|
|
ieee80211_sta_abort_chanswitch(link);
|
2019-02-06 19:17:15 +08:00
|
|
|
else
|
|
|
|
drv_channel_switch_rx_beacon(sdata, &ch_switch);
|
2019-02-06 19:17:10 +08:00
|
|
|
return;
|
2022-06-24 22:18:56 +08:00
|
|
|
} else if (link->conf->csa_active || res) {
|
2019-02-06 19:17:10 +08:00
|
|
|
/* disregard subsequent announcements if already processing */
|
|
|
|
return;
|
|
|
|
}
|
2013-03-28 17:44:18 +08:00
|
|
|
|
2022-06-24 22:18:56 +08:00
|
|
|
if (link->conf->chandef.chan->band !=
|
2020-11-29 23:30:54 +08:00
|
|
|
csa_ie.chandef.chan->band) {
|
|
|
|
sdata_info(sdata,
|
|
|
|
"AP %pM switches to different band (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n",
|
2022-06-24 22:18:56 +08:00
|
|
|
link->u.mgd.bssid,
|
2020-11-29 23:30:54 +08:00
|
|
|
csa_ie.chandef.chan->center_freq,
|
|
|
|
csa_ie.chandef.width, csa_ie.chandef.center_freq1,
|
|
|
|
csa_ie.chandef.center_freq2);
|
|
|
|
goto lock_and_drop_connection;
|
|
|
|
}
|
|
|
|
|
2013-10-15 10:08:29 +08:00
|
|
|
if (!cfg80211_chandef_usable(local->hw.wiphy, &csa_ie.chandef,
|
2013-03-26 01:29:27 +08:00
|
|
|
IEEE80211_CHAN_DISABLED)) {
|
|
|
|
sdata_info(sdata,
|
2020-04-02 09:18:04 +08:00
|
|
|
"AP %pM switches to unsupported channel "
|
|
|
|
"(%d.%03d MHz, width:%d, CF1/2: %d.%03d/%d MHz), "
|
|
|
|
"disconnecting\n",
|
2022-06-24 22:18:56 +08:00
|
|
|
link->u.mgd.bssid,
|
2013-10-15 10:08:29 +08:00
|
|
|
csa_ie.chandef.chan->center_freq,
|
2020-04-02 09:18:04 +08:00
|
|
|
csa_ie.chandef.chan->freq_offset,
|
2013-10-15 10:08:29 +08:00
|
|
|
csa_ie.chandef.width, csa_ie.chandef.center_freq1,
|
2020-04-02 09:18:04 +08:00
|
|
|
csa_ie.chandef.freq1_offset,
|
2013-10-15 10:08:29 +08:00
|
|
|
csa_ie.chandef.center_freq2);
|
2020-11-29 23:30:54 +08:00
|
|
|
goto lock_and_drop_connection;
|
2013-03-26 01:29:27 +08:00
|
|
|
}
|
|
|
|
|
2015-03-12 14:53:26 +08:00
|
|
|
if (cfg80211_chandef_identical(&csa_ie.chandef,
|
2022-06-24 22:18:56 +08:00
|
|
|
&link->conf->chandef) &&
|
2019-02-06 19:17:16 +08:00
|
|
|
(!csa_ie.mode || !beacon)) {
|
2022-06-24 22:18:56 +08:00
|
|
|
if (link->u.mgd.csa_ignored_same_chan)
|
2015-03-12 14:53:26 +08:00
|
|
|
return;
|
|
|
|
sdata_info(sdata,
|
|
|
|
"AP %pM tries to chanswitch to same channel, ignore\n",
|
2022-06-24 22:18:56 +08:00
|
|
|
link->u.mgd.bssid);
|
|
|
|
link->u.mgd.csa_ignored_same_chan = true;
|
2015-03-12 14:53:26 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-05-19 19:36:48 +08: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 18:35:09 +08:00
|
|
|
mutex_lock(&local->mtx);
|
2013-09-01 22:15:51 +08:00
|
|
|
mutex_lock(&local->chanctx_mtx);
|
2022-06-24 22:18:56 +08:00
|
|
|
conf = rcu_dereference_protected(link->conf->chanctx_conf,
|
2014-06-25 18:35:09 +08:00
|
|
|
lockdep_is_held(&local->chanctx_mtx));
|
|
|
|
if (!conf) {
|
|
|
|
sdata_info(sdata,
|
|
|
|
"no channel context assigned to vif?, disconnecting\n");
|
2015-03-12 14:53:25 +08:00
|
|
|
goto drop_connection;
|
2014-06-25 18:35:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
chanctx = container_of(conf, struct ieee80211_chanctx, conf);
|
|
|
|
|
2014-10-08 14:48:40 +08:00
|
|
|
if (local->use_chanctx &&
|
2015-06-03 03:39:54 +08:00
|
|
|
!ieee80211_hw_check(&local->hw, CHANCTX_STA_CSA)) {
|
2014-10-08 14:48:40 +08:00
|
|
|
sdata_info(sdata,
|
|
|
|
"driver doesn't support chan-switch with channel contexts\n");
|
2015-03-12 14:53:25 +08:00
|
|
|
goto drop_connection;
|
2012-07-26 23:24:39 +08:00
|
|
|
}
|
|
|
|
|
2014-10-08 14:48:37 +08:00
|
|
|
if (drv_pre_channel_switch(sdata, &ch_switch)) {
|
|
|
|
sdata_info(sdata,
|
|
|
|
"preparing for channel switch failed, disconnecting\n");
|
2015-03-12 14:53:25 +08:00
|
|
|
goto drop_connection;
|
2014-10-08 14:48:37 +08:00
|
|
|
}
|
|
|
|
|
2022-06-24 22:18:56 +08:00
|
|
|
res = ieee80211_link_reserve_chanctx(link, &csa_ie.chandef,
|
2022-05-31 00:35:23 +08:00
|
|
|
chanctx->mode, false);
|
2014-06-25 18:35:09 +08:00
|
|
|
if (res) {
|
2012-07-26 23:24:39 +08:00
|
|
|
sdata_info(sdata,
|
2014-06-25 18:35:09 +08:00
|
|
|
"failed to reserve channel context for channel switch, disconnecting (err=%d)\n",
|
|
|
|
res);
|
2015-03-12 14:53:25 +08:00
|
|
|
goto drop_connection;
|
2012-07-26 23:24:39 +08:00
|
|
|
}
|
2013-03-26 21:13:58 +08:00
|
|
|
mutex_unlock(&local->chanctx_mtx);
|
2012-07-26 23:24:39 +08:00
|
|
|
|
2022-06-24 22:18:56 +08:00
|
|
|
link->conf->csa_active = true;
|
|
|
|
link->csa_chandef = csa_ie.chandef;
|
|
|
|
link->csa_block_tx = csa_ie.mode;
|
|
|
|
link->u.mgd.csa_ignored_same_chan = false;
|
|
|
|
link->u.mgd.beacon_crc_valid = false;
|
2012-07-26 23:24:39 +08:00
|
|
|
|
2022-06-24 22:18:56 +08:00
|
|
|
if (link->csa_block_tx)
|
2014-06-13 21:30:07 +08:00
|
|
|
ieee80211_stop_vif_queues(local, sdata,
|
|
|
|
IEEE80211_QUEUE_STOP_REASON_CSA);
|
2014-04-09 21:10:59 +08:00
|
|
|
mutex_unlock(&local->mtx);
|
2012-08-01 21:50:46 +08:00
|
|
|
|
2022-07-22 21:11:42 +08:00
|
|
|
cfg80211_ch_switch_started_notify(sdata->dev, &csa_ie.chandef, 0,
|
2023-01-31 08:12:26 +08:00
|
|
|
csa_ie.count, csa_ie.mode, 0);
|
2014-11-07 20:31:36 +08:00
|
|
|
|
2013-03-26 21:13:58 +08:00
|
|
|
if (local->ops->channel_switch) {
|
2010-05-11 22:20:57 +08:00
|
|
|
/* use driver's channel switch callback */
|
2014-10-08 14:48:40 +08:00
|
|
|
drv_channel_switch(local, sdata, &ch_switch);
|
2010-05-11 22:20:57 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* channel switch handled in software */
|
2013-10-15 10:08:29 +08:00
|
|
|
if (csa_ie.count <= 1)
|
2022-06-24 22:18:56 +08:00
|
|
|
ieee80211_queue_work(&local->hw, &link->u.mgd.chswitch_work);
|
2012-08-01 21:50:46 +08:00
|
|
|
else
|
2022-06-24 22:18:56 +08:00
|
|
|
mod_timer(&link->u.mgd.chswitch_timer,
|
2014-10-28 19:33:05 +08:00
|
|
|
TU_TO_EXP_TIME((csa_ie.count - 1) *
|
|
|
|
cbss->beacon_interval));
|
2015-03-12 14:53:25 +08:00
|
|
|
return;
|
2020-11-29 23:30:54 +08:00
|
|
|
lock_and_drop_connection:
|
|
|
|
mutex_lock(&local->mtx);
|
|
|
|
mutex_lock(&local->chanctx_mtx);
|
2015-03-12 14:53:25 +08:00
|
|
|
drop_connection:
|
2018-08-31 16:31:12 +08:00
|
|
|
/*
|
|
|
|
* This is just so that the disconnect flow will know that
|
|
|
|
* we were trying to switch channel and failed. In case the
|
|
|
|
* mode is 1 (we are not allowed to Tx), we will know not to
|
|
|
|
* send a deauthentication frame. Those two fields will be
|
|
|
|
* reset when the disconnection worker runs.
|
|
|
|
*/
|
2022-06-24 22:18:56 +08:00
|
|
|
link->conf->csa_active = true;
|
|
|
|
link->csa_block_tx = csa_ie.mode;
|
2018-08-31 16:31:12 +08:00
|
|
|
|
2015-03-12 14:53:25 +08:00
|
|
|
ieee80211_queue_work(&local->hw, &ifmgd->csa_connection_drop_work);
|
|
|
|
mutex_unlock(&local->chanctx_mtx);
|
|
|
|
mutex_unlock(&local->mtx);
|
2009-05-15 17:52:31 +08:00
|
|
|
}
|
|
|
|
|
2014-09-03 21:22:10 +08: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 17:52:31 +08:00
|
|
|
{
|
2012-09-05 19:41:37 +08:00
|
|
|
struct ieee80211_country_ie_triplet *triplet;
|
|
|
|
int chan = ieee80211_frequency_to_channel(channel->center_freq);
|
2014-09-03 21:22:10 +08:00
|
|
|
int i, chan_increment;
|
2012-09-05 19:41:37 +08:00
|
|
|
bool have_chan_pwr = false;
|
2009-05-15 17:52:31 +08:00
|
|
|
|
2012-09-05 19:41:37 +08:00
|
|
|
/* Invalid IE */
|
|
|
|
if (country_ie_len % 2 || country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN)
|
2014-09-03 21:22:10 +08:00
|
|
|
return false;
|
2009-05-15 17:52:31 +08:00
|
|
|
|
2012-09-05 19:41:37 +08:00
|
|
|
triplet = (void *)(country_ie + 3);
|
|
|
|
country_ie_len -= 3;
|
|
|
|
|
|
|
|
switch (channel->band) {
|
|
|
|
default:
|
|
|
|
WARN_ON_ONCE(1);
|
2020-07-08 04:45:48 +08:00
|
|
|
fallthrough;
|
2016-04-12 21:56:15 +08:00
|
|
|
case NL80211_BAND_2GHZ:
|
|
|
|
case NL80211_BAND_60GHZ:
|
2021-10-18 18:00:54 +08:00
|
|
|
case NL80211_BAND_LC:
|
2012-09-05 19:41:37 +08:00
|
|
|
chan_increment = 1;
|
|
|
|
break;
|
2016-04-12 21:56:15 +08:00
|
|
|
case NL80211_BAND_5GHZ:
|
2012-09-05 19:41:37 +08:00
|
|
|
chan_increment = 4;
|
|
|
|
break;
|
2020-12-06 20:54:47 +08:00
|
|
|
case NL80211_BAND_6GHZ:
|
|
|
|
/*
|
|
|
|
* In the 6 GHz band, the "maximum transmit power level"
|
|
|
|
* field in the triplets is reserved, and thus will be
|
|
|
|
* zero and we shouldn't use it to control TX power.
|
|
|
|
* The actual TX power will be given in the transmit
|
|
|
|
* power envelope element instead.
|
|
|
|
*/
|
|
|
|
return false;
|
2009-05-15 17:52:31 +08:00
|
|
|
}
|
2012-09-05 19:41:37 +08: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 21:22:10 +08:00
|
|
|
*chan_pwr = triplet->chans.max_power;
|
2012-09-05 19:41:37 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (have_chan_pwr)
|
|
|
|
break;
|
|
|
|
|
|
|
|
next:
|
|
|
|
triplet++;
|
|
|
|
country_ie_len -= 3;
|
|
|
|
}
|
|
|
|
|
2014-12-14 17:05:50 +08:00
|
|
|
if (have_chan_pwr && pwr_constr_elem)
|
2014-09-03 21:22:10 +08:00
|
|
|
*pwr_reduction = *pwr_constr_elem;
|
2014-12-14 17:05:50 +08:00
|
|
|
else
|
|
|
|
*pwr_reduction = 0;
|
|
|
|
|
2014-09-03 21:22:10 +08: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 21:48:37 +08: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];
|
|
|
|
}
|
|
|
|
|
2022-06-24 22:18:56 +08:00
|
|
|
static u32 ieee80211_handle_pwr_constr(struct ieee80211_link_data *link,
|
2014-09-03 21:22:10 +08:00
|
|
|
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 21:48:37 +08:00
|
|
|
const u8 *pwr_constr_ie,
|
|
|
|
const u8 *cisco_dtpc_ie)
|
2014-09-03 21:22:10 +08:00
|
|
|
{
|
2022-06-24 22:18:56 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = link->sdata;
|
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 21:48:37 +08: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 21:22:10 +08:00
|
|
|
int new_ap_level;
|
2014-12-14 17:05:50 +08:00
|
|
|
__le16 capab = mgmt->u.probe_resp.capab_info;
|
2012-09-05 19:41:37 +08:00
|
|
|
|
2020-09-22 10:28:14 +08:00
|
|
|
if (ieee80211_is_s1g_beacon(mgmt->frame_control))
|
|
|
|
return 0; /* TODO */
|
|
|
|
|
2014-12-14 17:05:50 +08:00
|
|
|
if (country_ie &&
|
|
|
|
(capab & cpu_to_le16(WLAN_CAPABILITY_SPECTRUM_MGMT) ||
|
|
|
|
capab & cpu_to_le16(WLAN_CAPABILITY_RADIO_MEASURE))) {
|
2014-09-03 21:22:10 +08: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 21:48:37 +08: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 21:22:10 +08:00
|
|
|
}
|
2012-09-05 19:41:37 +08: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 21:48:37 +08:00
|
|
|
if (!has_80211h_pwr && !has_cisco_pwr)
|
2012-10-24 16:59:25 +08:00
|
|
|
return 0;
|
2012-09-05 19:41:37 +08: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 21:48:37 +08: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-12-08 22:04:37 +08:00
|
|
|
new_ap_level = pwr_level_80211h;
|
|
|
|
|
2022-06-24 22:18:56 +08:00
|
|
|
if (link->ap_power_level == new_ap_level)
|
2015-12-08 22:04:37 +08:00
|
|
|
return 0;
|
|
|
|
|
2015-03-31 22:49:14 +08: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,
|
2022-06-24 22:18:56 +08:00
|
|
|
link->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 21:48:37 +08:00
|
|
|
} else { /* has_cisco_pwr is always true here. */
|
2015-12-08 22:04:37 +08:00
|
|
|
new_ap_level = pwr_level_cisco;
|
|
|
|
|
2022-06-24 22:18:56 +08:00
|
|
|
if (link->ap_power_level == new_ap_level)
|
2015-12-08 22:04:37 +08:00
|
|
|
return 0;
|
|
|
|
|
2015-03-31 22:49:14 +08:00
|
|
|
sdata_dbg(sdata,
|
|
|
|
"Limiting TX power to %d dBm as advertised by %pM\n",
|
2022-06-24 22:18:56 +08:00
|
|
|
pwr_level_cisco, link->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 21:48:37 +08:00
|
|
|
}
|
2012-09-05 19:41:37 +08:00
|
|
|
|
2022-06-24 22:18:56 +08:00
|
|
|
link->ap_power_level = new_ap_level;
|
2012-10-24 16:59:25 +08:00
|
|
|
if (__ieee80211_recalc_txpower(sdata))
|
|
|
|
return BSS_CHANGED_TXPOWER;
|
|
|
|
return 0;
|
2009-05-15 17:52:31 +08:00
|
|
|
}
|
|
|
|
|
2009-04-16 19:17:24 +08: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-23 05:02:51 +08:00
|
|
|
/*
|
|
|
|
* If we are scanning right now then the parameters will
|
|
|
|
* take effect when scan finishes.
|
|
|
|
*/
|
2009-07-23 18:14:04 +08:00
|
|
|
if (local->scanning)
|
2009-04-23 05:02:51 +08:00
|
|
|
return;
|
|
|
|
|
2009-04-16 19:17:24 +08:00
|
|
|
if (conf->dynamic_ps_timeout > 0 &&
|
2015-06-03 03:39:54 +08:00
|
|
|
!ieee80211_hw_check(&local->hw, SUPPORTS_DYNAMIC_PS)) {
|
2009-04-16 19:17:24 +08:00
|
|
|
mod_timer(&local->dynamic_ps_timer, jiffies +
|
|
|
|
msecs_to_jiffies(conf->dynamic_ps_timeout));
|
|
|
|
} else {
|
2015-06-03 03:39:54 +08:00
|
|
|
if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK))
|
2015-09-24 22:14:55 +08:00
|
|
|
ieee80211_send_nullfunc(local, sdata, true);
|
2010-02-09 17:20:28 +08:00
|
|
|
|
2015-06-03 03:39:54 +08:00
|
|
|
if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK) &&
|
|
|
|
ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS))
|
2010-03-09 20:25:02 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
conf->flags |= IEEE80211_CONF_PS;
|
|
|
|
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
|
2009-04-16 19:17:24 +08: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 08:43:19 +08:00
|
|
|
static bool ieee80211_powersave_allowed(struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
2022-06-24 21:14:26 +08:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
2011-03-11 08:43:19 +08:00
|
|
|
struct ieee80211_if_managed *mgd = &sdata->u.mgd;
|
|
|
|
struct sta_info *sta = NULL;
|
2011-09-29 22:04:36 +08:00
|
|
|
bool authorized = false;
|
2011-03-11 08:43:19 +08:00
|
|
|
|
|
|
|
if (!mgd->powersave)
|
|
|
|
return false;
|
|
|
|
|
2011-10-28 17:59:47 +08:00
|
|
|
if (mgd->broken_ap)
|
|
|
|
return false;
|
|
|
|
|
2011-03-11 08:43:19 +08:00
|
|
|
if (!mgd->associated)
|
|
|
|
return false;
|
|
|
|
|
2013-08-27 17:36:35 +08:00
|
|
|
if (mgd->flags & IEEE80211_STA_CONNECTION_POLL)
|
2011-03-11 08:43:19 +08:00
|
|
|
return false;
|
|
|
|
|
2022-06-24 21:14:26 +08:00
|
|
|
if (!(local->hw.wiphy->flags & WIPHY_FLAG_SUPPORTS_MLO) &&
|
|
|
|
!sdata->deflink.u.mgd.have_beacon)
|
2013-05-06 22:17:04 +08:00
|
|
|
return false;
|
|
|
|
|
2011-03-11 08:43:19 +08:00
|
|
|
rcu_read_lock();
|
2022-06-24 22:08:39 +08:00
|
|
|
sta = sta_info_get(sdata, sdata->vif.cfg.ap_addr);
|
2011-03-11 08:43:19 +08:00
|
|
|
if (sta)
|
2011-09-29 22:04:36 +08:00
|
|
|
authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
|
2011-03-11 08:43:19 +08:00
|
|
|
rcu_read_unlock();
|
|
|
|
|
2011-09-29 22:04:36 +08:00
|
|
|
return authorized;
|
2011-03-11 08:43:19 +08:00
|
|
|
}
|
|
|
|
|
2009-04-16 19:17:24 +08:00
|
|
|
/* need to hold RTNL or interface lock */
|
2015-10-15 00:02:43 +08:00
|
|
|
void ieee80211_recalc_ps(struct ieee80211_local *local)
|
2009-04-16 19:17:24 +08:00
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata, *found = NULL;
|
|
|
|
int count = 0;
|
2010-04-27 17:47:40 +08:00
|
|
|
int timeout;
|
2009-04-16 19:17:24 +08:00
|
|
|
|
2022-06-23 15:48:00 +08:00
|
|
|
if (!ieee80211_hw_check(&local->hw, SUPPORTS_PS) ||
|
|
|
|
ieee80211_hw_check(&local->hw, SUPPORTS_DYNAMIC_PS)) {
|
2009-04-16 19:17:24 +08:00
|
|
|
local->ps_sdata = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
list_for_each_entry(sdata, &local->interfaces, list) {
|
2009-12-23 20:15:31 +08:00
|
|
|
if (!ieee80211_sdata_running(sdata))
|
2009-04-16 19:17:24 +08:00
|
|
|
continue;
|
2011-02-01 02:58:59 +08: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 19:17:24 +08:00
|
|
|
if (sdata->vif.type != NL80211_IFTYPE_STATION)
|
|
|
|
continue;
|
|
|
|
found = sdata;
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
|
2011-03-11 08:43:19 +08:00
|
|
|
if (count == 1 && ieee80211_powersave_allowed(found)) {
|
2022-05-16 21:00:15 +08:00
|
|
|
u8 dtimper = found->deflink.u.mgd.dtim_period;
|
2009-04-16 19:17:25 +08:00
|
|
|
|
2010-06-09 14:51:52 +08:00
|
|
|
timeout = local->dynamic_ps_forced_timeout;
|
2015-10-15 00:02:43 +08:00
|
|
|
if (timeout < 0)
|
|
|
|
timeout = 100;
|
2013-02-07 06:07:41 +08:00
|
|
|
local->hw.conf.dynamic_ps_timeout = timeout;
|
2010-04-27 17:47:40 +08:00
|
|
|
|
2015-10-15 00:02:43 +08:00
|
|
|
/* If the TIM IE is invalid, pretend the value is 1 */
|
|
|
|
if (!dtimper)
|
|
|
|
dtimper = 1;
|
|
|
|
|
|
|
|
local->hw.conf.ps_dtim_period = dtimper;
|
|
|
|
local->ps_sdata = found;
|
2009-04-16 19:17:25 +08:00
|
|
|
} else {
|
2009-04-16 19:17:24 +08:00
|
|
|
local->ps_sdata = NULL;
|
2009-04-16 19:17:25 +08:00
|
|
|
}
|
2009-04-16 19:17:24 +08:00
|
|
|
|
|
|
|
ieee80211_change_ps(local);
|
|
|
|
}
|
|
|
|
|
2012-07-27 17:33:22 +08:00
|
|
|
void ieee80211_recalc_ps_vif(struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
|
|
|
bool ps_allowed = ieee80211_powersave_allowed(sdata);
|
|
|
|
|
2022-06-24 21:02:16 +08:00
|
|
|
if (sdata->vif.cfg.ps != ps_allowed) {
|
|
|
|
sdata->vif.cfg.ps = ps_allowed;
|
|
|
|
ieee80211_vif_cfg_change_notify(sdata, BSS_CHANGED_PS);
|
2012-07-27 17:33:22 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-16 19:17:24 +08: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 19:25:28 +08:00
|
|
|
IEEE80211_MAX_QUEUE_MAP,
|
2014-06-13 21:30:05 +08:00
|
|
|
IEEE80211_QUEUE_STOP_REASON_PS,
|
|
|
|
false);
|
2009-04-16 19:17:24 +08: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-07-01 03:08:43 +08:00
|
|
|
struct ieee80211_if_managed *ifmgd;
|
2011-05-03 19:33:59 +08:00
|
|
|
unsigned long flags;
|
|
|
|
int q;
|
2009-04-16 19:17:24 +08:00
|
|
|
|
|
|
|
/* can only happen when PS was just disabled anyway */
|
|
|
|
if (!sdata)
|
|
|
|
return;
|
|
|
|
|
2011-07-01 03:08:43 +08:00
|
|
|
ifmgd = &sdata->u.mgd;
|
|
|
|
|
2009-04-16 19:17:24 +08:00
|
|
|
if (local->hw.conf.flags & IEEE80211_CONF_PS)
|
|
|
|
return;
|
|
|
|
|
2013-02-07 06:07:41 +08:00
|
|
|
if (local->hw.conf.dynamic_ps_timeout > 0) {
|
2011-06-26 17:06:54 +08:00
|
|
|
/* don't enter PS if TX frames are pending */
|
|
|
|
if (drv_tx_frames_pending(local)) {
|
2011-05-03 19:33:59 +08:00
|
|
|
mod_timer(&local->dynamic_ps_timer, jiffies +
|
|
|
|
msecs_to_jiffies(
|
|
|
|
local->hw.conf.dynamic_ps_timeout));
|
|
|
|
return;
|
|
|
|
}
|
2011-06-26 17:06:54 +08: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 19:33:59 +08:00
|
|
|
}
|
|
|
|
|
2015-06-03 03:39:54 +08:00
|
|
|
if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK) &&
|
2011-12-14 22:16:07 +08:00
|
|
|
!(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)) {
|
2013-03-27 05:02:42 +08:00
|
|
|
if (drv_tx_frames_pending(local)) {
|
2011-04-06 14:11:10 +08:00
|
|
|
mod_timer(&local->dynamic_ps_timer, jiffies +
|
|
|
|
msecs_to_jiffies(
|
|
|
|
local->hw.conf.dynamic_ps_timeout));
|
2013-03-27 05:02:42 +08:00
|
|
|
} else {
|
2015-09-24 22:14:55 +08:00
|
|
|
ieee80211_send_nullfunc(local, sdata, true);
|
2011-04-06 14:11:10 +08:00
|
|
|
/* Flush to get the tx status of nullfunc frame */
|
2015-01-07 21:42:39 +08:00
|
|
|
ieee80211_flush_queues(local, sdata, false);
|
2011-04-06 14:11:10 +08:00
|
|
|
}
|
2011-02-23 15:34:32 +08:00
|
|
|
}
|
|
|
|
|
2015-06-03 03:39:54 +08:00
|
|
|
if (!(ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS) &&
|
|
|
|
ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK)) ||
|
2010-02-09 17:20:28 +08: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 19:17:24 +08:00
|
|
|
}
|
|
|
|
|
2017-10-17 07:35:49 +08:00
|
|
|
void ieee80211_dynamic_ps_timer(struct timer_list *t)
|
2009-04-16 19:17:24 +08:00
|
|
|
{
|
2017-10-17 07:35:49 +08:00
|
|
|
struct ieee80211_local *local = from_timer(local, t, dynamic_ps_timer);
|
2009-04-16 19:17:24 +08:00
|
|
|
|
2009-07-30 08:08:07 +08:00
|
|
|
ieee80211_queue_work(&local->hw, &local->dynamic_ps_enable_work);
|
2009-04-16 19:17:24 +08:00
|
|
|
}
|
|
|
|
|
2013-02-09 01:16:20 +08:00
|
|
|
void ieee80211_dfs_cac_timer_work(struct work_struct *work)
|
|
|
|
{
|
2016-01-01 23:48:52 +08:00
|
|
|
struct delayed_work *delayed_work = to_delayed_work(work);
|
2022-06-24 22:18:56 +08:00
|
|
|
struct ieee80211_link_data *link =
|
|
|
|
container_of(delayed_work, struct ieee80211_link_data,
|
|
|
|
dfs_cac_timer_work);
|
|
|
|
struct cfg80211_chan_def chandef = link->conf->chandef;
|
|
|
|
struct ieee80211_sub_if_data *sdata = link->sdata;
|
2013-02-09 01:16:20 +08: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 16:43:33 +08:00
|
|
|
mutex_lock(&sdata->local->mtx);
|
|
|
|
if (sdata->wdev.cac_started) {
|
2022-06-24 22:18:56 +08:00
|
|
|
ieee80211_link_release_channel(link);
|
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 16:43:33 +08:00
|
|
|
cfg80211_cac_event(sdata->dev, &chandef,
|
|
|
|
NL80211_RADAR_CAC_FINISHED,
|
|
|
|
GFP_KERNEL);
|
|
|
|
}
|
|
|
|
mutex_unlock(&sdata->local->mtx);
|
2013-02-09 01:16:20 +08:00
|
|
|
}
|
|
|
|
|
2014-10-07 15:38:50 +08: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-07 03:39:33 +08:00
|
|
|
bool ret = false;
|
2014-10-07 15:38:50 +08: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 */
|
2022-06-24 21:40:11 +08:00
|
|
|
if (drv_conf_tx(local, &sdata->deflink, ac,
|
|
|
|
&sdata->deflink.tx_conf[ac]))
|
|
|
|
link_err(&sdata->deflink,
|
|
|
|
"failed to set TX queue parameters for queue %d\n",
|
|
|
|
ac);
|
2014-10-07 15:38:50 +08:00
|
|
|
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;
|
2016-09-14 15:23:51 +08:00
|
|
|
/* Usually the loop will result in using BK even if it
|
|
|
|
* requires admission control, but such a configuration
|
|
|
|
* makes no sense and we have to transmit somehow - the
|
|
|
|
* AC selection does the same thing.
|
|
|
|
* If we started out trying to downgrade from BK, then
|
|
|
|
* the extra condition here might be needed.
|
2014-10-07 15:38:50 +08:00
|
|
|
*/
|
2016-09-14 15:23:51 +08:00
|
|
|
if (non_acm_ac >= IEEE80211_NUM_ACS)
|
|
|
|
non_acm_ac = IEEE80211_AC_BK;
|
2022-06-24 21:40:11 +08:00
|
|
|
if (drv_conf_tx(local, &sdata->deflink, ac,
|
|
|
|
&sdata->deflink.tx_conf[non_acm_ac]))
|
|
|
|
link_err(&sdata->deflink,
|
|
|
|
"failed to set TX queue parameters for queue %d\n",
|
|
|
|
ac);
|
2014-10-07 15:38:50 +08:00
|
|
|
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))
|
2022-06-18 04:36:37 +08:00
|
|
|
ieee80211_link_info_change_notify(sdata, &sdata->deflink,
|
|
|
|
BSS_CHANGED_QOS);
|
2014-10-07 15:38:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2022-09-02 22:12:46 +08:00
|
|
|
void ieee80211_mgd_set_link_qos_params(struct ieee80211_link_data *link)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata = link->sdata;
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
struct ieee80211_tx_queue_params *params = link->tx_conf;
|
|
|
|
u8 ac;
|
|
|
|
|
|
|
|
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
|
|
|
|
mlme_dbg(sdata,
|
|
|
|
"WMM AC=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d, downgraded=%d\n",
|
|
|
|
ac, params[ac].acm,
|
|
|
|
params[ac].aifs, params[ac].cw_min, params[ac].cw_max,
|
|
|
|
params[ac].txop, params[ac].uapsd,
|
|
|
|
ifmgd->tx_tspec[ac].downgraded);
|
|
|
|
if (!ifmgd->tx_tspec[ac].downgraded &&
|
|
|
|
drv_conf_tx(local, link, ac, ¶ms[ac]))
|
|
|
|
link_err(link,
|
|
|
|
"failed to set TX queue parameters for AC %d\n",
|
|
|
|
ac);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-09-08 23:44:22 +08:00
|
|
|
/* MLME */
|
2018-06-09 14:14:44 +08:00
|
|
|
static bool
|
|
|
|
ieee80211_sta_wmm_params(struct ieee80211_local *local,
|
2022-06-24 21:40:11 +08:00
|
|
|
struct ieee80211_link_data *link,
|
2018-06-09 14:14:44 +08:00
|
|
|
const u8 *wmm_param, size_t wmm_param_len,
|
|
|
|
const struct ieee80211_mu_edca_param_set *mu_edca)
|
2007-05-06 02:45:53 +08:00
|
|
|
{
|
2022-06-24 21:40:11 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = link->sdata;
|
2015-10-22 23:46:06 +08:00
|
|
|
struct ieee80211_tx_queue_params params[IEEE80211_NUM_ACS];
|
2010-07-19 22:39:04 +08:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2007-05-06 02:45:53 +08:00
|
|
|
size_t left;
|
2018-12-15 17:03:15 +08:00
|
|
|
int count, mu_edca_count, ac;
|
2013-02-12 23:43:19 +08:00
|
|
|
const u8 *pos;
|
|
|
|
u8 uapsd_queues = 0;
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2010-03-29 18:18:34 +08:00
|
|
|
if (!local->ops->conf_tx)
|
2012-07-06 23:37:43 +08:00
|
|
|
return false;
|
2010-03-29 18:18:34 +08:00
|
|
|
|
2012-03-28 17:04:29 +08:00
|
|
|
if (local->hw.queues < IEEE80211_NUM_ACS)
|
2012-07-06 23:37:43 +08:00
|
|
|
return false;
|
2008-05-03 06:59:37 +08:00
|
|
|
|
|
|
|
if (!wmm_param)
|
2012-07-06 23:37:43 +08:00
|
|
|
return false;
|
2008-05-03 06:59:37 +08:00
|
|
|
|
2007-05-06 02:45:53 +08:00
|
|
|
if (wmm_param_len < 8 || wmm_param[5] /* version */ != 1)
|
2012-07-06 23:37:43 +08:00
|
|
|
return false;
|
2010-01-12 16:42:31 +08:00
|
|
|
|
|
|
|
if (ifmgd->flags & IEEE80211_STA_UAPSD_ENABLED)
|
2012-03-14 22:15:03 +08:00
|
|
|
uapsd_queues = ifmgd->uapsd_queues;
|
2010-01-12 16:42:31 +08:00
|
|
|
|
2007-05-06 02:45:53 +08:00
|
|
|
count = wmm_param[6] & 0x0f;
|
2018-12-15 17:03:15 +08:00
|
|
|
/* -1 is the initial value of ifmgd->mu_edca_last_param_set.
|
|
|
|
* if mu_edca was preset before and now it disappeared tell
|
|
|
|
* the driver about it.
|
|
|
|
*/
|
|
|
|
mu_edca_count = mu_edca ? mu_edca->mu_qos_info & 0x0f : -1;
|
2022-06-24 21:40:11 +08:00
|
|
|
if (count == link->u.mgd.wmm_last_param_set &&
|
|
|
|
mu_edca_count == link->u.mgd.mu_edca_last_param_set)
|
2012-07-06 23:37:43 +08:00
|
|
|
return false;
|
2022-06-24 21:40:11 +08:00
|
|
|
link->u.mgd.wmm_last_param_set = count;
|
|
|
|
link->u.mgd.mu_edca_last_param_set = mu_edca_count;
|
2007-05-06 02:45:53 +08:00
|
|
|
|
|
|
|
pos = wmm_param + 8;
|
|
|
|
left = wmm_param_len - 8;
|
|
|
|
|
|
|
|
memset(¶ms, 0, sizeof(params));
|
|
|
|
|
2012-06-20 20:39:13 +08:00
|
|
|
sdata->wmm_acm = 0;
|
2007-05-06 02:45:53 +08:00
|
|
|
for (; left >= 4; left -= 4, pos += 4) {
|
|
|
|
int aci = (pos[0] >> 5) & 0x03;
|
|
|
|
int acm = (pos[0] >> 4) & 0x01;
|
2010-01-12 16:42:31 +08:00
|
|
|
bool uapsd = false;
|
2007-05-06 02:45:53 +08:00
|
|
|
|
|
|
|
switch (aci) {
|
2009-03-05 23:23:46 +08:00
|
|
|
case 1: /* AC_BK */
|
2015-10-22 23:46:06 +08:00
|
|
|
ac = IEEE80211_AC_BK;
|
2008-04-18 01:21:22 +08:00
|
|
|
if (acm)
|
2012-06-20 20:39:13 +08:00
|
|
|
sdata->wmm_acm |= BIT(1) | BIT(2); /* BK/- */
|
2010-01-12 16:42:31 +08:00
|
|
|
if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK)
|
|
|
|
uapsd = true;
|
2018-06-09 14:14:44 +08:00
|
|
|
params[ac].mu_edca = !!mu_edca;
|
|
|
|
if (mu_edca)
|
|
|
|
params[ac].mu_edca_param_rec = mu_edca->ac_bk;
|
2007-05-06 02:45:53 +08:00
|
|
|
break;
|
2009-03-05 23:23:46 +08:00
|
|
|
case 2: /* AC_VI */
|
2015-10-22 23:46:06 +08:00
|
|
|
ac = IEEE80211_AC_VI;
|
2008-04-18 01:21:22 +08:00
|
|
|
if (acm)
|
2012-06-20 20:39:13 +08:00
|
|
|
sdata->wmm_acm |= BIT(4) | BIT(5); /* CL/VI */
|
2010-01-12 16:42:31 +08:00
|
|
|
if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI)
|
|
|
|
uapsd = true;
|
2018-06-09 14:14:44 +08:00
|
|
|
params[ac].mu_edca = !!mu_edca;
|
|
|
|
if (mu_edca)
|
|
|
|
params[ac].mu_edca_param_rec = mu_edca->ac_vi;
|
2007-05-06 02:45:53 +08:00
|
|
|
break;
|
2009-03-05 23:23:46 +08:00
|
|
|
case 3: /* AC_VO */
|
2015-10-22 23:46:06 +08:00
|
|
|
ac = IEEE80211_AC_VO;
|
2008-04-18 01:21:22 +08:00
|
|
|
if (acm)
|
2012-06-20 20:39:13 +08:00
|
|
|
sdata->wmm_acm |= BIT(6) | BIT(7); /* VO/NC */
|
2010-01-12 16:42:31 +08:00
|
|
|
if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
|
|
|
|
uapsd = true;
|
2018-06-09 14:14:44 +08:00
|
|
|
params[ac].mu_edca = !!mu_edca;
|
|
|
|
if (mu_edca)
|
|
|
|
params[ac].mu_edca_param_rec = mu_edca->ac_vo;
|
2007-05-06 02:45:53 +08:00
|
|
|
break;
|
2009-03-05 23:23:46 +08:00
|
|
|
case 0: /* AC_BE */
|
2007-05-06 02:45:53 +08:00
|
|
|
default:
|
2015-10-22 23:46:06 +08:00
|
|
|
ac = IEEE80211_AC_BE;
|
2008-04-18 01:21:22 +08:00
|
|
|
if (acm)
|
2012-06-20 20:39:13 +08:00
|
|
|
sdata->wmm_acm |= BIT(0) | BIT(3); /* BE/EE */
|
2010-01-12 16:42:31 +08:00
|
|
|
if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE)
|
|
|
|
uapsd = true;
|
2018-06-09 14:14:44 +08:00
|
|
|
params[ac].mu_edca = !!mu_edca;
|
|
|
|
if (mu_edca)
|
|
|
|
params[ac].mu_edca_param_rec = mu_edca->ac_be;
|
2007-05-06 02:45:53 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-10-22 23:46:06 +08:00
|
|
|
params[ac].aifs = pos[0] & 0x0f;
|
2015-10-22 23:46:05 +08:00
|
|
|
|
2015-10-22 23:46:06 +08:00
|
|
|
if (params[ac].aifs < 2) {
|
2015-10-22 23:46:05 +08:00
|
|
|
sdata_info(sdata,
|
|
|
|
"AP has invalid WMM params (AIFSN=%d for ACI %d), will use 2\n",
|
2015-10-22 23:46:06 +08:00
|
|
|
params[ac].aifs, aci);
|
|
|
|
params[ac].aifs = 2;
|
2015-10-22 23:46:05 +08:00
|
|
|
}
|
2015-10-22 23:46:06 +08:00
|
|
|
params[ac].cw_max = ecw2cw((pos[1] & 0xf0) >> 4);
|
|
|
|
params[ac].cw_min = ecw2cw(pos[1] & 0x0f);
|
|
|
|
params[ac].txop = get_unaligned_le16(pos + 2);
|
|
|
|
params[ac].acm = acm;
|
|
|
|
params[ac].uapsd = uapsd;
|
2010-01-12 16:42:31 +08:00
|
|
|
|
2018-04-03 16:35:22 +08:00
|
|
|
if (params[ac].cw_min == 0 ||
|
2018-03-26 21:21:04 +08:00
|
|
|
params[ac].cw_min > params[ac].cw_max) {
|
2015-10-22 23:46:06 +08:00
|
|
|
sdata_info(sdata,
|
|
|
|
"AP has invalid WMM params (CWmin/max=%d/%d for ACI %d), using defaults\n",
|
|
|
|
params[ac].cw_min, params[ac].cw_max, aci);
|
|
|
|
return false;
|
|
|
|
}
|
2018-03-28 18:24:10 +08:00
|
|
|
ieee80211_regulatory_limit_wmm_params(sdata, ¶ms[ac], ac);
|
2015-10-22 23:46:06 +08:00
|
|
|
}
|
|
|
|
|
2019-07-27 06:47:58 +08:00
|
|
|
/* WMM specification requires all 4 ACIs. */
|
|
|
|
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
|
|
|
|
if (params[ac].cw_min == 0) {
|
|
|
|
sdata_info(sdata,
|
|
|
|
"AP has invalid WMM params (missing AC %d), using defaults\n",
|
|
|
|
ac);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-02 22:12:46 +08:00
|
|
|
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
|
2022-06-24 21:40:11 +08:00
|
|
|
link->tx_conf[ac] = params[ac];
|
2022-09-02 22:12:46 +08:00
|
|
|
|
|
|
|
ieee80211_mgd_set_link_qos_params(link);
|
2010-03-29 18:18:34 +08:00
|
|
|
|
|
|
|
/* enable WMM or activate new settings */
|
2022-06-24 21:40:11 +08:00
|
|
|
link->conf->qos = true;
|
2012-07-06 23:37:43 +08:00
|
|
|
return true;
|
2007-05-06 02:45:53 +08:00
|
|
|
}
|
|
|
|
|
2012-05-16 21:27:20 +08:00
|
|
|
static void __ieee80211_stop_poll(struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
|
|
|
lockdep_assert_held(&sdata->local->mtx);
|
|
|
|
|
2013-08-27 17:36:35 +08:00
|
|
|
sdata->u.mgd.flags &= ~IEEE80211_STA_CONNECTION_POLL;
|
2012-05-16 21:27:20 +08: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);
|
|
|
|
}
|
|
|
|
|
2022-06-27 20:10:18 +08:00
|
|
|
static u32 ieee80211_handle_bss_capability(struct ieee80211_link_data *link,
|
2008-10-08 16:59:33 +08:00
|
|
|
u16 capab, bool erp_valid, u8 erp)
|
2007-07-11 01:32:10 +08:00
|
|
|
{
|
2022-06-27 20:10:18 +08:00
|
|
|
struct ieee80211_bss_conf *bss_conf = link->conf;
|
2017-04-27 15:15:38 +08:00
|
|
|
struct ieee80211_supported_band *sband;
|
2007-12-28 21:32:58 +08:00
|
|
|
u32 changed = 0;
|
2008-10-08 16:59:33 +08:00
|
|
|
bool use_protection;
|
|
|
|
bool use_short_preamble;
|
|
|
|
bool use_short_slot;
|
|
|
|
|
2022-06-27 20:10:18 +08:00
|
|
|
sband = ieee80211_get_link_sband(link);
|
2017-04-27 15:15:38 +08:00
|
|
|
if (!sband)
|
|
|
|
return changed;
|
|
|
|
|
2008-10-08 16:59:33 +08:00
|
|
|
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);
|
2020-05-29 03:34:46 +08:00
|
|
|
if (sband->band == NL80211_BAND_5GHZ ||
|
|
|
|
sband->band == NL80211_BAND_6GHZ)
|
2010-01-15 10:00:48 +08:00
|
|
|
use_short_slot = true;
|
2007-07-11 01:32:10 +08:00
|
|
|
|
2007-12-28 21:32:58 +08:00
|
|
|
if (use_protection != bss_conf->use_cts_prot) {
|
|
|
|
bss_conf->use_cts_prot = use_protection;
|
|
|
|
changed |= BSS_CHANGED_ERP_CTS_PROT;
|
2007-07-11 01:32:10 +08:00
|
|
|
}
|
2007-07-27 21:43:24 +08:00
|
|
|
|
2008-03-31 23:05:03 +08:00
|
|
|
if (use_short_preamble != bss_conf->use_short_preamble) {
|
|
|
|
bss_conf->use_short_preamble = use_short_preamble;
|
2007-12-28 21:32:58 +08:00
|
|
|
changed |= BSS_CHANGED_ERP_PREAMBLE;
|
2007-07-27 21:43:24 +08:00
|
|
|
}
|
2007-07-27 21:43:24 +08:00
|
|
|
|
2008-10-08 16:59:33 +08: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-16 02:09:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return changed;
|
|
|
|
}
|
|
|
|
|
2022-07-13 04:18:24 +08:00
|
|
|
static u32 ieee80211_link_set_associated(struct ieee80211_link_data *link,
|
|
|
|
struct cfg80211_bss *cbss)
|
2007-05-06 02:45:53 +08:00
|
|
|
{
|
2022-07-13 04:18:24 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = link->sdata;
|
2022-06-24 22:18:56 +08:00
|
|
|
struct ieee80211_bss_conf *bss_conf = link->conf;
|
2022-07-13 04:18:24 +08:00
|
|
|
struct ieee80211_bss *bss = (void *)cbss->priv;
|
2022-06-02 03:17:34 +08:00
|
|
|
u32 changed = BSS_CHANGED_QOS;
|
2007-08-29 05:01:54 +08:00
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
/* not really used in MLO */
|
2022-07-13 04:18:24 +08:00
|
|
|
sdata->u.mgd.beacon_timeout =
|
|
|
|
usecs_to_jiffies(ieee80211_tu_to_usec(beacon_loss_count *
|
|
|
|
bss_conf->beacon_int));
|
2008-03-29 07:33:34 +08:00
|
|
|
|
2022-07-13 04:18:24 +08:00
|
|
|
changed |= ieee80211_handle_bss_capability(link,
|
|
|
|
bss_conf->assoc_capability,
|
|
|
|
bss->has_erp_value,
|
|
|
|
bss->erp_value);
|
|
|
|
|
|
|
|
ieee80211_check_rate_mask(link);
|
2010-11-20 05:55:38 +08:00
|
|
|
|
2022-06-24 22:18:56 +08:00
|
|
|
link->u.mgd.bss = cbss;
|
|
|
|
memcpy(link->u.mgd.bssid, cbss->bssid, ETH_ALEN);
|
mac80211: reject/clear user rate mask if not usable
If the user rate mask results in no (basic) rates being usable,
clear it. Also, if we're already operating when it's set, reject
it instead.
Technically, selecting basic rates as the criterion is a bit too
restrictive, but calculating the usable rates over all stations
(e.g. in AP mode) is harder, and all stations must support the
basic rates. Similarly, in client mode, the basic rates will be
used anyway for control frames.
This fixes the "no supported rates (...) in rate_mask ..." warning
that occurs on TX when you've selected a rate mask that's not
compatible with the connection (e.g. an AP that enables only the
rates 36, 48, 54 and you've selected only 6, 9, 12.)
Reported-by: Kirtika Ruchandani <kirtika@google.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2017-03-08 18:12:10 +08:00
|
|
|
|
2015-10-27 15:38:40 +08:00
|
|
|
if (sdata->vif.p2p ||
|
|
|
|
sdata->vif.driver_flags & IEEE80211_VIF_GET_NOA_UPDATE) {
|
2012-11-29 08:25:20 +08:00
|
|
|
const struct cfg80211_bss_ies *ies;
|
2012-10-30 03:08:01 +08:00
|
|
|
|
2012-11-29 08:25:20 +08: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 22:47:56 +08:00
|
|
|
(u8 *) &bss_conf->p2p_noa_attr,
|
|
|
|
sizeof(bss_conf->p2p_noa_attr));
|
2012-11-29 08:25:20 +08:00
|
|
|
if (ret >= 2) {
|
2022-06-24 22:18:56 +08:00
|
|
|
link->u.mgd.p2p_noa_index =
|
2013-03-21 22:47:56 +08:00
|
|
|
bss_conf->p2p_noa_attr.index;
|
2022-07-13 04:18:24 +08:00
|
|
|
changed |= BSS_CHANGED_P2P_PS;
|
2012-11-29 08:25:20 +08:00
|
|
|
}
|
2012-10-30 03:08:01 +08:00
|
|
|
}
|
2012-11-29 08:25:20 +08:00
|
|
|
rcu_read_unlock();
|
2012-10-30 03:08:01 +08:00
|
|
|
}
|
|
|
|
|
2022-06-24 22:18:56 +08:00
|
|
|
if (link->u.mgd.have_beacon) {
|
2013-05-19 19:23:57 +08:00
|
|
|
bss_conf->beacon_rate = bss->beacon_rate;
|
2022-07-13 04:18:24 +08:00
|
|
|
changed |= BSS_CHANGED_BEACON_INFO;
|
2012-12-10 22:38:14 +08:00
|
|
|
} else {
|
2013-05-19 19:23:57 +08:00
|
|
|
bss_conf->beacon_rate = NULL;
|
2012-12-10 22:38:14 +08:00
|
|
|
}
|
2010-07-29 22:08:55 +08:00
|
|
|
|
2010-03-23 15:02:34 +08:00
|
|
|
/* Tell the driver to monitor connection quality (if supported) */
|
2012-01-19 16:29:58 +08:00
|
|
|
if (sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI &&
|
2010-06-09 18:43:26 +08:00
|
|
|
bss_conf->cqm_rssi_thold)
|
2022-07-13 04:18:24 +08:00
|
|
|
changed |= BSS_CHANGED_CQM;
|
|
|
|
|
|
|
|
return changed;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
|
2022-06-02 03:17:34 +08:00
|
|
|
struct ieee80211_mgd_assoc_data *assoc_data,
|
|
|
|
u64 changed[IEEE80211_MLD_MAX_NUM_LINKS])
|
2022-07-13 04:18:24 +08:00
|
|
|
{
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
struct ieee80211_vif_cfg *vif_cfg = &sdata->vif.cfg;
|
2022-06-02 03:17:34 +08:00
|
|
|
u64 vif_changed = BSS_CHANGED_ASSOC;
|
|
|
|
unsigned int link_id;
|
2022-07-13 04:18:24 +08:00
|
|
|
|
|
|
|
sdata->u.mgd.associated = true;
|
2022-06-02 03:17:34 +08:00
|
|
|
|
|
|
|
for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {
|
|
|
|
struct cfg80211_bss *cbss = assoc_data->link[link_id].bss;
|
|
|
|
struct ieee80211_link_data *link;
|
|
|
|
|
2022-09-08 16:31:10 +08:00
|
|
|
if (!cbss ||
|
|
|
|
assoc_data->link[link_id].status != WLAN_STATUS_SUCCESS)
|
2022-06-02 03:17:34 +08:00
|
|
|
continue;
|
|
|
|
|
|
|
|
link = sdata_dereference(sdata->link[link_id], sdata);
|
|
|
|
if (WARN_ON(!link))
|
|
|
|
return;
|
|
|
|
|
|
|
|
changed[link_id] |= ieee80211_link_set_associated(link, cbss);
|
|
|
|
}
|
|
|
|
|
2022-07-13 04:18:24 +08:00
|
|
|
/* just to be sure */
|
|
|
|
ieee80211_stop_poll(sdata);
|
|
|
|
|
|
|
|
ieee80211_led_assoc(local, 1);
|
|
|
|
|
|
|
|
vif_cfg->assoc = 1;
|
2010-03-23 15:02:34 +08:00
|
|
|
|
2010-06-09 18:43:26 +08:00
|
|
|
/* Enable ARP filtering */
|
wifi: mac80211: move interface config to new struct
We'll use bss_conf for per-link configuration later, so
move out all the non-link-specific data out into a new
struct ieee80211_vif_cfg used in the vif.
Some adjustments were done with the following spatch:
@@
expression sdata;
struct ieee80211_vif *vifp;
identifier var = { assoc, ibss_joined, aid, arp_addr_list, arp_addr_cnt, ssid, ssid_len, s1g, ibss_creator };
@@
(
-sdata->vif.bss_conf.var
+sdata->vif.cfg.var
|
-vifp->bss_conf.var
+vifp->cfg.var
)
@bss_conf@
struct ieee80211_bss_conf *bss_conf;
identifier var = { assoc, ibss_joined, aid, arp_addr_list, arp_addr_cnt, ssid, ssid_len, s1g, ibss_creator };
@@
-bss_conf->var
+vif_cfg->var
(though more manual fixups were needed, e.g. replacing
"vif_cfg->" by "vif->cfg." in many files.)
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2022-05-10 23:05:04 +08:00
|
|
|
if (vif_cfg->arp_addr_cnt)
|
2022-06-02 03:17:34 +08:00
|
|
|
vif_changed |= BSS_CHANGED_ARP_FILTER;
|
|
|
|
|
|
|
|
if (sdata->vif.valid_links) {
|
|
|
|
for (link_id = 0;
|
|
|
|
link_id < IEEE80211_MLD_MAX_NUM_LINKS;
|
|
|
|
link_id++) {
|
|
|
|
struct ieee80211_link_data *link;
|
|
|
|
struct cfg80211_bss *cbss = assoc_data->link[link_id].bss;
|
2010-06-09 18:43:26 +08:00
|
|
|
|
2022-09-08 16:31:10 +08:00
|
|
|
if (!cbss ||
|
|
|
|
assoc_data->link[link_id].status != WLAN_STATUS_SUCCESS)
|
2022-06-02 03:17:34 +08:00
|
|
|
continue;
|
|
|
|
|
|
|
|
link = sdata_dereference(sdata->link[link_id], sdata);
|
|
|
|
if (WARN_ON(!link))
|
|
|
|
return;
|
|
|
|
|
|
|
|
ieee80211_link_info_change_notify(sdata, link,
|
|
|
|
changed[link_id]);
|
|
|
|
|
|
|
|
ieee80211_recalc_smps(sdata, link);
|
|
|
|
}
|
|
|
|
|
|
|
|
ieee80211_vif_cfg_change_notify(sdata, vif_changed);
|
|
|
|
} else {
|
|
|
|
ieee80211_bss_info_change_notify(sdata,
|
|
|
|
vif_changed | changed[0]);
|
|
|
|
}
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2009-07-31 03:43:55 +08:00
|
|
|
mutex_lock(&local->iflist_mtx);
|
2015-10-15 00:02:43 +08:00
|
|
|
ieee80211_recalc_ps(local);
|
2009-07-31 03:43:55 +08:00
|
|
|
mutex_unlock(&local->iflist_mtx);
|
2008-12-19 05:35:13 +08:00
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
/* leave this here to not change ordering in non-MLO cases */
|
|
|
|
if (!sdata->vif.valid_links)
|
|
|
|
ieee80211_recalc_smps(sdata, &sdata->deflink);
|
2012-07-27 17:33:22 +08:00
|
|
|
ieee80211_recalc_ps_vif(sdata);
|
|
|
|
|
2008-09-09 16:57:09 +08:00
|
|
|
netif_carrier_on(sdata->dev);
|
2007-05-06 02:45:53 +08:00
|
|
|
}
|
|
|
|
|
2010-03-30 14:29:31 +08:00
|
|
|
static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
|
2012-02-24 20:50:54 +08:00
|
|
|
u16 stype, u16 reason, bool tx,
|
|
|
|
u8 *frame_buf)
|
2008-09-09 05:32:12 +08:00
|
|
|
{
|
2009-02-15 19:44:28 +08:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2008-09-09 05:32:12 +08:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
2022-06-02 03:17:34 +08:00
|
|
|
unsigned int link_id;
|
2012-03-09 20:12:35 +08:00
|
|
|
u32 changed = 0;
|
2021-06-18 18:41:55 +08:00
|
|
|
struct ieee80211_prep_tx_info info = {
|
|
|
|
.subtype = stype,
|
|
|
|
};
|
2008-09-09 05:32:12 +08:00
|
|
|
|
2013-05-10 18:32:47 +08:00
|
|
|
sdata_assert_lock(sdata);
|
2009-07-07 09:45:17 +08:00
|
|
|
|
2012-02-24 20:50:54 +08:00
|
|
|
if (WARN_ON_ONCE(tx && !frame_buf))
|
|
|
|
return;
|
|
|
|
|
2009-07-10 21:29:03 +08:00
|
|
|
if (WARN_ON(!ifmgd->associated))
|
|
|
|
return;
|
|
|
|
|
2012-06-12 14:59:45 +08:00
|
|
|
ieee80211_stop_poll(sdata);
|
|
|
|
|
2022-05-14 00:03:14 +08:00
|
|
|
ifmgd->associated = false;
|
2022-07-12 19:46:19 +08:00
|
|
|
|
|
|
|
/* other links will be destroyed */
|
|
|
|
sdata->deflink.u.mgd.bss = NULL;
|
|
|
|
|
2008-09-09 05:32:12 +08:00
|
|
|
netif_carrier_off(sdata->dev);
|
|
|
|
|
2012-07-12 22:35:33 +08: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 17:33:22 +08:00
|
|
|
/* disable per-vif ps */
|
|
|
|
ieee80211_recalc_ps_vif(sdata);
|
|
|
|
|
2015-01-23 05:30:19 +08:00
|
|
|
/* make sure ongoing transmission finishes */
|
|
|
|
synchronize_net();
|
|
|
|
|
2015-01-07 21:42:39 +08: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 19:18:22 +08:00
|
|
|
if (tx)
|
2015-01-07 21:42:39 +08:00
|
|
|
ieee80211_flush_queues(local, sdata, true);
|
2012-06-27 19:18:22 +08:00
|
|
|
|
2012-02-24 20:50:54 +08:00
|
|
|
/* deauthenticate/disassociate now */
|
2018-02-19 20:48:41 +08:00
|
|
|
if (tx || frame_buf) {
|
|
|
|
/*
|
|
|
|
* In multi channel scenarios guarantee that the virtual
|
|
|
|
* interface is granted immediate airtime to transmit the
|
|
|
|
* deauthentication frame by calling mgd_prepare_tx, if the
|
|
|
|
* driver requested so.
|
|
|
|
*/
|
|
|
|
if (ieee80211_hw_check(&local->hw, DEAUTH_NEED_MGD_TX_PREP) &&
|
2022-07-12 19:46:19 +08:00
|
|
|
!sdata->deflink.u.mgd.have_beacon) {
|
2021-06-18 18:41:55 +08:00
|
|
|
drv_mgd_prepare_tx(sdata->local, sdata, &info);
|
|
|
|
}
|
2018-02-19 20:48:41 +08:00
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
ieee80211_send_deauth_disassoc(sdata, sdata->vif.cfg.ap_addr,
|
|
|
|
sdata->vif.cfg.ap_addr, stype,
|
|
|
|
reason, tx, frame_buf);
|
2018-02-19 20:48:41 +08:00
|
|
|
}
|
2012-02-24 20:50:54 +08:00
|
|
|
|
2015-01-07 21:42:39 +08:00
|
|
|
/* flush out frame - make sure the deauth was actually sent */
|
2012-02-24 20:50:54 +08:00
|
|
|
if (tx)
|
2015-01-07 21:42:39 +08:00
|
|
|
ieee80211_flush_queues(local, sdata, false);
|
2012-02-24 20:50:54 +08:00
|
|
|
|
2021-06-18 18:41:55 +08:00
|
|
|
drv_mgd_complete_tx(sdata->local, sdata, &info);
|
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
/* clear AP addr only after building the needed mgmt frames */
|
|
|
|
eth_zero_addr(sdata->deflink.u.mgd.bssid);
|
2022-06-24 22:08:39 +08:00
|
|
|
eth_zero_addr(sdata->vif.cfg.ap_addr);
|
2022-06-02 03:17:34 +08:00
|
|
|
|
wifi: mac80211: move interface config to new struct
We'll use bss_conf for per-link configuration later, so
move out all the non-link-specific data out into a new
struct ieee80211_vif_cfg used in the vif.
Some adjustments were done with the following spatch:
@@
expression sdata;
struct ieee80211_vif *vifp;
identifier var = { assoc, ibss_joined, aid, arp_addr_list, arp_addr_cnt, ssid, ssid_len, s1g, ibss_creator };
@@
(
-sdata->vif.bss_conf.var
+sdata->vif.cfg.var
|
-vifp->bss_conf.var
+vifp->cfg.var
)
@bss_conf@
struct ieee80211_bss_conf *bss_conf;
identifier var = { assoc, ibss_joined, aid, arp_addr_list, arp_addr_cnt, ssid, ssid_len, s1g, ibss_creator };
@@
-bss_conf->var
+vif_cfg->var
(though more manual fixups were needed, e.g. replacing
"vif_cfg->" by "vif->cfg." in many files.)
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2022-05-10 23:05:04 +08:00
|
|
|
sdata->vif.cfg.ssid_len = 0;
|
2020-12-07 11:36:34 +08:00
|
|
|
|
2012-02-24 20:50:54 +08:00
|
|
|
/* remove AP and TDLS peers */
|
2013-12-05 05:46:11 +08:00
|
|
|
sta_info_flush(sdata);
|
2012-02-24 20:50:54 +08:00
|
|
|
|
|
|
|
/* finally reset all BSS / config parameters */
|
2022-07-22 17:01:18 +08:00
|
|
|
if (!sdata->vif.valid_links)
|
|
|
|
changed |= ieee80211_reset_erp_info(sdata);
|
2008-09-08 23:33:39 +08:00
|
|
|
|
|
|
|
ieee80211_led_assoc(local, 0);
|
2008-10-14 22:58:37 +08:00
|
|
|
changed |= BSS_CHANGED_ASSOC;
|
wifi: mac80211: move interface config to new struct
We'll use bss_conf for per-link configuration later, so
move out all the non-link-specific data out into a new
struct ieee80211_vif_cfg used in the vif.
Some adjustments were done with the following spatch:
@@
expression sdata;
struct ieee80211_vif *vifp;
identifier var = { assoc, ibss_joined, aid, arp_addr_list, arp_addr_cnt, ssid, ssid_len, s1g, ibss_creator };
@@
(
-sdata->vif.bss_conf.var
+sdata->vif.cfg.var
|
-vifp->bss_conf.var
+vifp->cfg.var
)
@bss_conf@
struct ieee80211_bss_conf *bss_conf;
identifier var = { assoc, ibss_joined, aid, arp_addr_list, arp_addr_cnt, ssid, ssid_len, s1g, ibss_creator };
@@
-bss_conf->var
+vif_cfg->var
(though more manual fixups were needed, e.g. replacing
"vif_cfg->" by "vif->cfg." in many files.)
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2022-05-10 23:05:04 +08:00
|
|
|
sdata->vif.cfg.assoc = false;
|
2008-09-08 23:33:39 +08:00
|
|
|
|
2022-07-12 19:46:19 +08:00
|
|
|
sdata->deflink.u.mgd.p2p_noa_index = -1;
|
|
|
|
memset(&sdata->vif.bss_conf.p2p_noa_attr, 0,
|
|
|
|
sizeof(sdata->vif.bss_conf.p2p_noa_attr));
|
2012-10-30 03:08:01 +08:00
|
|
|
|
2013-02-22 00:40:19 +08:00
|
|
|
/* on the next assoc, re-program HT/VHT parameters */
|
2011-11-19 03:32:00 +08:00
|
|
|
memset(&ifmgd->ht_capa, 0, sizeof(ifmgd->ht_capa));
|
|
|
|
memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask));
|
2013-02-22 00:40:19 +08:00
|
|
|
memset(&ifmgd->vht_capa, 0, sizeof(ifmgd->vht_capa));
|
|
|
|
memset(&ifmgd->vht_capa_mask, 0, sizeof(ifmgd->vht_capa_mask));
|
2015-12-08 22:04:31 +08:00
|
|
|
|
2022-07-12 19:46:19 +08:00
|
|
|
/*
|
|
|
|
* reset MU-MIMO ownership and group data in default link,
|
|
|
|
* if used, other links are destroyed
|
|
|
|
*/
|
|
|
|
memset(sdata->vif.bss_conf.mu_group.membership, 0,
|
|
|
|
sizeof(sdata->vif.bss_conf.mu_group.membership));
|
|
|
|
memset(sdata->vif.bss_conf.mu_group.position, 0,
|
|
|
|
sizeof(sdata->vif.bss_conf.mu_group.position));
|
|
|
|
if (!sdata->vif.valid_links)
|
|
|
|
changed |= BSS_CHANGED_MU_GROUPS;
|
|
|
|
sdata->vif.bss_conf.mu_mimo_owner = false;
|
2009-05-09 03:21:06 +08:00
|
|
|
|
2022-07-12 19:46:19 +08:00
|
|
|
sdata->deflink.ap_power_level = IEEE80211_UNSET_POWER_LEVEL;
|
2009-01-09 20:44:15 +08:00
|
|
|
|
2008-12-19 05:35:27 +08:00
|
|
|
del_timer_sync(&local->dynamic_ps_timer);
|
|
|
|
cancel_work_sync(&local->dynamic_ps_enable_work);
|
|
|
|
|
2010-06-09 18:43:26 +08:00
|
|
|
/* Disable ARP filtering */
|
wifi: mac80211: move interface config to new struct
We'll use bss_conf for per-link configuration later, so
move out all the non-link-specific data out into a new
struct ieee80211_vif_cfg used in the vif.
Some adjustments were done with the following spatch:
@@
expression sdata;
struct ieee80211_vif *vifp;
identifier var = { assoc, ibss_joined, aid, arp_addr_list, arp_addr_cnt, ssid, ssid_len, s1g, ibss_creator };
@@
(
-sdata->vif.bss_conf.var
+sdata->vif.cfg.var
|
-vifp->bss_conf.var
+vifp->cfg.var
)
@bss_conf@
struct ieee80211_bss_conf *bss_conf;
identifier var = { assoc, ibss_joined, aid, arp_addr_list, arp_addr_cnt, ssid, ssid_len, s1g, ibss_creator };
@@
-bss_conf->var
+vif_cfg->var
(though more manual fixups were needed, e.g. replacing
"vif_cfg->" by "vif->cfg." in many files.)
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2022-05-10 23:05:04 +08:00
|
|
|
if (sdata->vif.cfg.arp_addr_cnt)
|
2010-06-09 18:43:26 +08:00
|
|
|
changed |= BSS_CHANGED_ARP_FILTER;
|
|
|
|
|
2022-07-12 19:46:19 +08:00
|
|
|
sdata->vif.bss_conf.qos = false;
|
2022-06-02 03:17:34 +08:00
|
|
|
if (!sdata->vif.valid_links) {
|
|
|
|
changed |= BSS_CHANGED_QOS;
|
|
|
|
/* The BSSID (not really interesting) and HT changed */
|
|
|
|
changed |= BSS_CHANGED_BSSID | BSS_CHANGED_HT;
|
2022-07-22 17:01:18 +08:00
|
|
|
ieee80211_bss_info_change_notify(sdata, changed);
|
|
|
|
} else {
|
|
|
|
ieee80211_vif_cfg_change_notify(sdata, changed);
|
2022-06-02 03:17:34 +08:00
|
|
|
}
|
2012-03-02 22:56:59 +08:00
|
|
|
|
|
|
|
/* disassociated - set to defaults now */
|
2022-06-24 21:40:11 +08:00
|
|
|
ieee80211_set_wmm_default(&sdata->deflink, false, false);
|
2012-03-02 22:56:59 +08:00
|
|
|
|
2010-08-27 18:35:54 +08: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);
|
2022-07-12 19:46:19 +08:00
|
|
|
del_timer_sync(&sdata->deflink.u.mgd.chswitch_timer);
|
2012-08-02 02:54:52 +08:00
|
|
|
|
2022-07-12 19:46:19 +08:00
|
|
|
sdata->vif.bss_conf.dtim_period = 0;
|
|
|
|
sdata->vif.bss_conf.beacon_rate = NULL;
|
2013-05-19 19:23:57 +08:00
|
|
|
|
2022-07-12 19:46:19 +08:00
|
|
|
sdata->deflink.u.mgd.have_beacon = false;
|
|
|
|
sdata->deflink.u.mgd.tracking_signal_avg = false;
|
|
|
|
sdata->deflink.u.mgd.disable_wmm_tracking = false;
|
2012-12-10 22:38:14 +08:00
|
|
|
|
2012-11-26 18:57:41 +08:00
|
|
|
ifmgd->flags = 0;
|
2022-07-12 19:46:19 +08:00
|
|
|
sdata->deflink.u.mgd.conn_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 16:43:33 +08:00
|
|
|
mutex_lock(&local->mtx);
|
2022-06-02 03:17:34 +08:00
|
|
|
|
|
|
|
for (link_id = 0; link_id < ARRAY_SIZE(sdata->link); link_id++) {
|
|
|
|
struct ieee80211_link_data *link;
|
|
|
|
|
|
|
|
link = sdata_dereference(sdata->link[link_id], sdata);
|
|
|
|
if (!link)
|
|
|
|
continue;
|
|
|
|
ieee80211_link_release_channel(link);
|
|
|
|
}
|
2014-04-09 21:10:59 +08:00
|
|
|
|
2022-07-12 19:46:19 +08:00
|
|
|
sdata->vif.bss_conf.csa_active = false;
|
|
|
|
sdata->deflink.u.mgd.csa_waiting_bcn = false;
|
|
|
|
sdata->deflink.u.mgd.csa_ignored_same_chan = false;
|
|
|
|
if (sdata->deflink.csa_block_tx) {
|
2014-06-13 21:30:07 +08:00
|
|
|
ieee80211_wake_vif_queues(local, sdata,
|
|
|
|
IEEE80211_QUEUE_STOP_REASON_CSA);
|
2022-07-12 19:46:19 +08:00
|
|
|
sdata->deflink.csa_block_tx = false;
|
2014-06-13 21:30:07 +08: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 16:43:33 +08:00
|
|
|
mutex_unlock(&local->mtx);
|
2013-03-24 20:23:27 +08:00
|
|
|
|
2014-10-07 15:38:50 +08: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);
|
|
|
|
|
2022-07-12 19:46:19 +08:00
|
|
|
sdata->vif.bss_conf.pwr_reduction = 0;
|
|
|
|
sdata->vif.bss_conf.tx_pwr_env_num = 0;
|
|
|
|
memset(sdata->vif.bss_conf.tx_pwr_env, 0,
|
|
|
|
sizeof(sdata->vif.bss_conf.tx_pwr_env));
|
2022-06-02 03:17:34 +08:00
|
|
|
|
|
|
|
ieee80211_vif_set_links(sdata, 0);
|
2008-09-09 05:32:12 +08:00
|
|
|
}
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2010-11-23 10:10:31 +08:00
|
|
|
static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2012-03-28 22:01:19 +08:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
2010-11-23 10:10:31 +08:00
|
|
|
|
2012-03-28 22:01:19 +08:00
|
|
|
mutex_lock(&local->mtx);
|
2013-08-27 17:36:35 +08:00
|
|
|
if (!(ifmgd->flags & IEEE80211_STA_CONNECTION_POLL))
|
|
|
|
goto out;
|
2010-11-23 10:10:31 +08:00
|
|
|
|
2012-05-16 21:27:20 +08:00
|
|
|
__ieee80211_stop_poll(sdata);
|
2012-03-28 22:01:19 +08:00
|
|
|
|
|
|
|
mutex_lock(&local->iflist_mtx);
|
2015-10-15 00:02:43 +08:00
|
|
|
ieee80211_recalc_ps(local);
|
2012-03-28 22:01:19 +08:00
|
|
|
mutex_unlock(&local->iflist_mtx);
|
2010-11-23 10:10:31 +08:00
|
|
|
|
2015-06-03 03:39:54 +08:00
|
|
|
if (ieee80211_hw_check(&sdata->local->hw, CONNECTION_MONITOR))
|
2012-03-28 22:01:19 +08:00
|
|
|
goto out;
|
2010-11-23 10:10:31 +08: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 22:01:19 +08:00
|
|
|
out:
|
|
|
|
mutex_unlock(&local->mtx);
|
2010-11-23 10:10:31 +08:00
|
|
|
}
|
|
|
|
|
2014-10-07 15:38:50 +08: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;
|
2021-11-22 19:47:40 +08:00
|
|
|
u16 tid;
|
|
|
|
int ac;
|
|
|
|
struct ieee80211_sta_tx_tspec *tx_tspec;
|
2014-10-07 15:38:50 +08:00
|
|
|
unsigned long now = jiffies;
|
|
|
|
|
2021-11-22 19:47:40 +08:00
|
|
|
if (!ieee80211_is_data_qos(hdr->frame_control))
|
|
|
|
return;
|
|
|
|
|
|
|
|
tid = ieee80211_get_tid(hdr);
|
|
|
|
ac = ieee80211_ac_from_tid(tid);
|
|
|
|
tx_tspec = &ifmgd->tx_tspec[ac];
|
|
|
|
|
2014-10-07 15:38:50 +08:00
|
|
|
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 10:10:31 +08:00
|
|
|
void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata,
|
2014-10-07 15:38:50 +08:00
|
|
|
struct ieee80211_hdr *hdr, bool ack, u16 tx_time)
|
2010-11-23 10:10:31 +08:00
|
|
|
{
|
2014-10-07 15:38:50 +08:00
|
|
|
ieee80211_sta_tx_wmm_ac_notify(sdata, hdr, tx_time);
|
|
|
|
|
2020-09-08 20:36:56 +08:00
|
|
|
if (!ieee80211_is_any_nullfunc(hdr->frame_control) ||
|
|
|
|
!sdata->u.mgd.probe_send_count)
|
2013-02-14 21:08:37 +08:00
|
|
|
return;
|
|
|
|
|
2020-09-27 18:56:05 +08:00
|
|
|
if (ack)
|
|
|
|
sdata->u.mgd.probe_send_count = 0;
|
|
|
|
else
|
2020-09-08 20:36:56 +08:00
|
|
|
sdata->u.mgd.nullfunc_failed = true;
|
|
|
|
ieee80211_queue_work(&sdata->local->hw, &sdata->work);
|
2010-11-23 10:10:31 +08:00
|
|
|
}
|
|
|
|
|
2018-05-28 21:47:39 +08:00
|
|
|
static void ieee80211_mlme_send_probe_req(struct ieee80211_sub_if_data *sdata,
|
|
|
|
const u8 *src, const u8 *dst,
|
|
|
|
const u8 *ssid, size_t ssid_len,
|
|
|
|
struct ieee80211_channel *channel)
|
|
|
|
{
|
|
|
|
struct sk_buff *skb;
|
|
|
|
|
|
|
|
skb = ieee80211_build_probe_req(sdata, src, dst, (u32)-1, channel,
|
|
|
|
ssid, ssid_len, NULL, 0,
|
|
|
|
IEEE80211_PROBE_FLAG_DIRECTED);
|
|
|
|
if (skb)
|
|
|
|
ieee80211_tx_skb(sdata, skb);
|
|
|
|
}
|
|
|
|
|
2009-07-31 23:54:12 +08:00
|
|
|
static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2022-06-24 22:08:39 +08:00
|
|
|
u8 *dst = sdata->vif.cfg.ap_addr;
|
2011-02-05 07:30:24 +08:00
|
|
|
u8 unicast_limit = max(1, max_probe_tries - 3);
|
2016-04-01 01:02:10 +08:00
|
|
|
struct sta_info *sta;
|
2010-09-17 03:12:34 +08:00
|
|
|
|
2022-06-24 22:18:56 +08:00
|
|
|
if (WARN_ON(sdata->vif.valid_links))
|
|
|
|
return;
|
|
|
|
|
2010-09-17 03:12:34 +08: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 23:54:12 +08:00
|
|
|
|
2010-11-23 10:10:31 +08: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 18:01:13 +08:00
|
|
|
ifmgd->probe_send_count++;
|
|
|
|
|
2016-04-01 01:02:10 +08:00
|
|
|
if (dst) {
|
|
|
|
mutex_lock(&sdata->local->sta_mtx);
|
|
|
|
sta = sta_info_get(sdata, dst);
|
|
|
|
if (!WARN_ON(!sta))
|
|
|
|
ieee80211_check_fast_rx(sta);
|
|
|
|
mutex_unlock(&sdata->local->sta_mtx);
|
|
|
|
}
|
|
|
|
|
2015-06-03 03:39:54 +08:00
|
|
|
if (ieee80211_hw_check(&sdata->local->hw, REPORTS_TX_ACK_STATUS)) {
|
2010-12-03 04:01:08 +08:00
|
|
|
ifmgd->nullfunc_failed = false;
|
2021-06-23 21:48:26 +08:00
|
|
|
ieee80211_send_nullfunc(sdata->local, sdata, false);
|
2010-12-03 04:01:08 +08:00
|
|
|
} else {
|
2018-05-28 21:47:39 +08:00
|
|
|
ieee80211_mlme_send_probe_req(sdata, sdata->vif.addr, dst,
|
wifi: mac80211: move interface config to new struct
We'll use bss_conf for per-link configuration later, so
move out all the non-link-specific data out into a new
struct ieee80211_vif_cfg used in the vif.
Some adjustments were done with the following spatch:
@@
expression sdata;
struct ieee80211_vif *vifp;
identifier var = { assoc, ibss_joined, aid, arp_addr_list, arp_addr_cnt, ssid, ssid_len, s1g, ibss_creator };
@@
(
-sdata->vif.bss_conf.var
+sdata->vif.cfg.var
|
-vifp->bss_conf.var
+vifp->cfg.var
)
@bss_conf@
struct ieee80211_bss_conf *bss_conf;
identifier var = { assoc, ibss_joined, aid, arp_addr_list, arp_addr_cnt, ssid, ssid_len, s1g, ibss_creator };
@@
-bss_conf->var
+vif_cfg->var
(though more manual fixups were needed, e.g. replacing
"vif_cfg->" by "vif->cfg." in many files.)
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2022-05-10 23:05:04 +08:00
|
|
|
sdata->vif.cfg.ssid,
|
|
|
|
sdata->vif.cfg.ssid_len,
|
2022-05-16 21:00:15 +08:00
|
|
|
sdata->deflink.u.mgd.bss->channel);
|
2010-11-23 10:10:31 +08:00
|
|
|
}
|
2009-07-31 23:54:12 +08:00
|
|
|
|
2011-02-05 07:30:24 +08:00
|
|
|
ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms);
|
2013-05-10 18:32:47 +08:00
|
|
|
run_again(sdata, ifmgd->probe_timeout);
|
2009-07-31 23:54:12 +08:00
|
|
|
}
|
|
|
|
|
2009-07-10 21:29:03 +08:00
|
|
|
static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
|
|
|
|
bool beacon)
|
2009-03-23 03:57:35 +08:00
|
|
|
{
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2009-07-10 21:29:03 +08:00
|
|
|
bool already = false;
|
2009-05-13 01:58:12 +08:00
|
|
|
|
2022-06-24 22:18:56 +08:00
|
|
|
if (WARN_ON(sdata->vif.valid_links))
|
|
|
|
return;
|
|
|
|
|
2009-12-23 20:15:31 +08:00
|
|
|
if (!ieee80211_sdata_running(sdata))
|
2009-07-13 19:23:39 +08:00
|
|
|
return;
|
|
|
|
|
2013-05-10 18:32:47 +08:00
|
|
|
sdata_lock(sdata);
|
2009-07-07 09:45:17 +08:00
|
|
|
|
|
|
|
if (!ifmgd->associated)
|
|
|
|
goto out;
|
|
|
|
|
2012-03-28 22:01:19 +08:00
|
|
|
mutex_lock(&sdata->local->mtx);
|
|
|
|
|
|
|
|
if (sdata->local->tmp_channel || sdata->local->scanning) {
|
|
|
|
mutex_unlock(&sdata->local->mtx);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2021-10-21 16:45:27 +08:00
|
|
|
if (sdata->local->suspending) {
|
|
|
|
/* reschedule after resume */
|
|
|
|
mutex_unlock(&sdata->local->mtx);
|
|
|
|
ieee80211_reset_ap_probe(sdata);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2013-03-26 02:19:35 +08:00
|
|
|
if (beacon) {
|
2012-06-22 17:29:50 +08:00
|
|
|
mlme_dbg_ratelimited(sdata,
|
2013-03-20 05:19:56 +08:00
|
|
|
"detected beacon loss from AP (missed %d beacons) - probing\n",
|
|
|
|
beacon_loss_count);
|
2012-06-22 17:29:50 +08:00
|
|
|
|
2014-11-26 19:42:02 +08:00
|
|
|
ieee80211_cqm_beacon_loss_notify(&sdata->vif, GFP_KERNEL);
|
2013-03-26 02:19:35 +08:00
|
|
|
}
|
2009-03-23 03:57:35 +08:00
|
|
|
|
2009-07-10 21:29:03 +08: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 17:36:35 +08:00
|
|
|
if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL)
|
2009-07-10 21:29:03 +08:00
|
|
|
already = true;
|
|
|
|
|
2013-11-19 01:06:46 +08:00
|
|
|
ifmgd->flags |= IEEE80211_STA_CONNECTION_POLL;
|
|
|
|
|
2012-03-28 22:01:19 +08:00
|
|
|
mutex_unlock(&sdata->local->mtx);
|
|
|
|
|
2009-07-10 21:29:03 +08:00
|
|
|
if (already)
|
|
|
|
goto out;
|
|
|
|
|
2009-06-10 21:16:52 +08:00
|
|
|
mutex_lock(&sdata->local->iflist_mtx);
|
2015-10-15 00:02:43 +08:00
|
|
|
ieee80211_recalc_ps(sdata->local);
|
2009-06-10 21:16:52 +08:00
|
|
|
mutex_unlock(&sdata->local->iflist_mtx);
|
|
|
|
|
2009-07-31 23:54:12 +08:00
|
|
|
ifmgd->probe_send_count = 0;
|
|
|
|
ieee80211_mgd_probe_ap_send(sdata);
|
2009-07-07 09:45:17 +08:00
|
|
|
out:
|
2013-05-10 18:32:47 +08:00
|
|
|
sdata_unlock(sdata);
|
2009-03-23 03:57:35 +08:00
|
|
|
}
|
|
|
|
|
2010-11-11 14:50:18 +08: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 20:03:13 +08:00
|
|
|
struct cfg80211_bss *cbss;
|
2010-11-11 14:50:18 +08:00
|
|
|
struct sk_buff *skb;
|
2021-09-30 19:11:31 +08:00
|
|
|
const struct element *ssid;
|
2012-03-29 22:30:41 +08:00
|
|
|
int ssid_len;
|
2010-11-11 14:50:18 +08:00
|
|
|
|
2022-06-24 22:18:56 +08:00
|
|
|
if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION ||
|
|
|
|
sdata->vif.valid_links))
|
2010-11-11 14:50:18 +08:00
|
|
|
return NULL;
|
|
|
|
|
2013-05-10 18:32:47 +08:00
|
|
|
sdata_assert_lock(sdata);
|
2010-11-11 14:50:18 +08:00
|
|
|
|
2012-06-28 20:03:13 +08:00
|
|
|
if (ifmgd->associated)
|
2022-05-16 21:00:15 +08:00
|
|
|
cbss = sdata->deflink.u.mgd.bss;
|
2012-06-28 20:03:13 +08:00
|
|
|
else if (ifmgd->auth_data)
|
|
|
|
cbss = ifmgd->auth_data->bss;
|
2022-06-02 03:17:34 +08:00
|
|
|
else if (ifmgd->assoc_data && ifmgd->assoc_data->link[0].bss)
|
|
|
|
cbss = ifmgd->assoc_data->link[0].bss;
|
2012-06-28 20:03:13 +08:00
|
|
|
else
|
2010-11-11 14:50:18 +08:00
|
|
|
return NULL;
|
|
|
|
|
2012-11-29 08:25:20 +08:00
|
|
|
rcu_read_lock();
|
2021-09-30 19:11:31 +08:00
|
|
|
ssid = ieee80211_bss_get_elem(cbss, WLAN_EID_SSID);
|
|
|
|
if (WARN_ONCE(!ssid || ssid->datalen > IEEE80211_MAX_SSID_LEN,
|
|
|
|
"invalid SSID element (len=%d)",
|
|
|
|
ssid ? ssid->datalen : -1))
|
2012-03-29 22:30:41 +08:00
|
|
|
ssid_len = 0;
|
|
|
|
else
|
2021-09-30 19:11:31 +08:00
|
|
|
ssid_len = ssid->datalen;
|
2012-03-29 22:30:41 +08:00
|
|
|
|
2014-06-13 04:24:31 +08:00
|
|
|
skb = ieee80211_build_probe_req(sdata, sdata->vif.addr, cbss->bssid,
|
2012-07-26 23:24:39 +08:00
|
|
|
(u32) -1, cbss->channel,
|
2021-09-30 19:11:31 +08:00
|
|
|
ssid->data, ssid_len,
|
2018-05-28 21:47:38 +08:00
|
|
|
NULL, 0, IEEE80211_PROBE_FLAG_DIRECTED);
|
2012-11-29 08:25:20 +08:00
|
|
|
rcu_read_unlock();
|
2010-11-11 14:50:18 +08:00
|
|
|
|
|
|
|
return skb;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ieee80211_ap_probereq_get);
|
|
|
|
|
2015-03-17 05:23:37 +08:00
|
|
|
static void ieee80211_report_disconnect(struct ieee80211_sub_if_data *sdata,
|
|
|
|
const u8 *buf, size_t len, bool tx,
|
2020-12-06 20:54:43 +08:00
|
|
|
u16 reason, bool reconnect)
|
2015-03-17 05:23:37 +08:00
|
|
|
{
|
|
|
|
struct ieee80211_event event = {
|
|
|
|
.type = MLME_EVENT,
|
|
|
|
.u.mlme.data = tx ? DEAUTH_TX_EVENT : DEAUTH_RX_EVENT,
|
|
|
|
.u.mlme.reason = reason,
|
|
|
|
};
|
|
|
|
|
|
|
|
if (tx)
|
2020-12-06 20:54:43 +08:00
|
|
|
cfg80211_tx_mlme_mgmt(sdata->dev, buf, len, reconnect);
|
2015-03-17 05:23:37 +08:00
|
|
|
else
|
|
|
|
cfg80211_rx_mlme_mgmt(sdata->dev, buf, len);
|
|
|
|
|
|
|
|
drv_event_callback(sdata->local, sdata, &event);
|
|
|
|
}
|
|
|
|
|
2013-01-29 20:09:34 +08:00
|
|
|
static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
|
2010-03-19 13:14:53 +08:00
|
|
|
{
|
2014-04-09 21:10:59 +08:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
2010-03-19 13:14:53 +08:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2012-09-07 19:28:52 +08:00
|
|
|
u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
|
2018-08-31 16:31:12 +08:00
|
|
|
bool tx;
|
2010-03-19 13:14:53 +08:00
|
|
|
|
2013-05-10 18:32:47 +08:00
|
|
|
sdata_lock(sdata);
|
2010-03-19 13:14:53 +08:00
|
|
|
if (!ifmgd->associated) {
|
2013-05-10 18:32:47 +08:00
|
|
|
sdata_unlock(sdata);
|
2010-03-19 13:14:53 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
/* in MLO assume we have a link where we can TX the frame */
|
|
|
|
tx = sdata->vif.valid_links || !sdata->deflink.csa_block_tx;
|
2018-08-31 16:31:12 +08:00
|
|
|
|
2020-12-06 20:54:43 +08:00
|
|
|
if (!ifmgd->driver_disconnect) {
|
2022-06-02 03:17:34 +08:00
|
|
|
unsigned int link_id;
|
|
|
|
|
2020-12-06 20:54:43 +08:00
|
|
|
/*
|
|
|
|
* AP is probably out of range (or not reachable for another
|
2022-06-02 03:17:34 +08:00
|
|
|
* reason) so remove the bss structs for that AP. In the case
|
|
|
|
* of multi-link, it's not clear that all of them really are
|
|
|
|
* out of range, but if they weren't the driver likely would
|
|
|
|
* have switched to just have a single link active?
|
2020-12-06 20:54:43 +08:00
|
|
|
*/
|
2022-06-02 03:17:34 +08:00
|
|
|
for (link_id = 0;
|
|
|
|
link_id < ARRAY_SIZE(sdata->link);
|
|
|
|
link_id++) {
|
|
|
|
struct ieee80211_link_data *link;
|
|
|
|
|
|
|
|
link = sdata_dereference(sdata->link[link_id], sdata);
|
|
|
|
if (!link)
|
|
|
|
continue;
|
|
|
|
cfg80211_unlink_bss(local->hw.wiphy, link->u.mgd.bss);
|
|
|
|
link->u.mgd.bss = NULL;
|
|
|
|
}
|
2020-12-06 20:54:43 +08:00
|
|
|
}
|
2016-05-03 21:05:02 +08:00
|
|
|
|
2012-02-24 20:50:54 +08:00
|
|
|
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
|
2020-12-06 20:54:43 +08:00
|
|
|
ifmgd->driver_disconnect ?
|
|
|
|
WLAN_REASON_DEAUTH_LEAVING :
|
|
|
|
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
|
2018-08-31 16:31:12 +08:00
|
|
|
tx, frame_buf);
|
2014-04-09 21:10:59 +08:00
|
|
|
mutex_lock(&local->mtx);
|
2022-07-12 19:46:19 +08:00
|
|
|
/* the other links will be destroyed */
|
wifi: mac80211: move some future per-link data to bss_conf
To add MLD, reuse the bss_conf structure later for per-link
information, so move some things into it that are per link.
Most transformations were done with the following spatch:
@@
expression sdata;
identifier var = { chanctx_conf, mu_mimo_owner, csa_active, color_change_active, color_change_color };
@@
-sdata->vif.var
+sdata->vif.bss_conf.var
@@
struct ieee80211_vif *vif;
identifier var = { chanctx_conf, mu_mimo_owner, csa_active, color_change_active, color_change_color };
@@
-vif->var
+vif->bss_conf.var
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2022-05-10 19:26:44 +08:00
|
|
|
sdata->vif.bss_conf.csa_active = false;
|
2022-05-16 21:00:15 +08:00
|
|
|
sdata->deflink.u.mgd.csa_waiting_bcn = false;
|
|
|
|
if (sdata->deflink.csa_block_tx) {
|
2014-06-13 21:30:07 +08:00
|
|
|
ieee80211_wake_vif_queues(local, sdata,
|
|
|
|
IEEE80211_QUEUE_STOP_REASON_CSA);
|
2022-05-16 21:00:15 +08:00
|
|
|
sdata->deflink.csa_block_tx = false;
|
2014-06-13 21:30:07 +08:00
|
|
|
}
|
2014-04-09 21:10:59 +08:00
|
|
|
mutex_unlock(&local->mtx);
|
2010-08-05 23:02:38 +08:00
|
|
|
|
2018-08-31 16:31:12 +08:00
|
|
|
ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), tx,
|
2020-12-06 20:54:43 +08:00
|
|
|
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
|
|
|
|
ifmgd->reconnect);
|
|
|
|
ifmgd->reconnect = false;
|
2015-03-17 05:23:37 +08:00
|
|
|
|
2013-05-10 18:32:47 +08:00
|
|
|
sdata_unlock(sdata);
|
2010-03-19 13:14:53 +08:00
|
|
|
}
|
|
|
|
|
2012-08-01 22:49:34 +08:00
|
|
|
static void ieee80211_beacon_connection_loss_work(struct work_struct *work)
|
2009-07-10 21:29:03 +08:00
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata =
|
|
|
|
container_of(work, struct ieee80211_sub_if_data,
|
2010-03-19 13:14:53 +08:00
|
|
|
u.mgd.beacon_connection_loss_work);
|
2011-12-10 03:01:49 +08:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
|
2013-01-29 20:13:50 +08:00
|
|
|
if (ifmgd->connection_loss) {
|
2012-08-02 04:32:45 +08:00
|
|
|
sdata_info(sdata, "Connection to AP %pM lost\n",
|
2022-06-24 22:08:39 +08:00
|
|
|
sdata->vif.cfg.ap_addr);
|
2013-01-29 20:09:34 +08:00
|
|
|
__ieee80211_disconnect(sdata);
|
2020-12-06 20:54:43 +08:00
|
|
|
ifmgd->connection_loss = false;
|
|
|
|
} else if (ifmgd->driver_disconnect) {
|
|
|
|
sdata_info(sdata,
|
|
|
|
"Driver requested disconnection from AP %pM\n",
|
2022-06-24 22:08:39 +08:00
|
|
|
sdata->vif.cfg.ap_addr);
|
2020-12-06 20:54:43 +08:00
|
|
|
__ieee80211_disconnect(sdata);
|
|
|
|
ifmgd->driver_disconnect = false;
|
2012-08-02 04:32:45 +08:00
|
|
|
} else {
|
2022-06-24 22:18:56 +08:00
|
|
|
if (ifmgd->associated)
|
|
|
|
sdata->deflink.u.mgd.beacon_loss_count++;
|
2010-03-19 13:14:53 +08:00
|
|
|
ieee80211_mgd_probe_ap(sdata, true);
|
2012-08-02 04:32:45 +08: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 20:09:34 +08:00
|
|
|
__ieee80211_disconnect(sdata);
|
2009-07-10 21:29:03 +08:00
|
|
|
}
|
|
|
|
|
2009-03-23 03:57:35 +08:00
|
|
|
void ieee80211_beacon_loss(struct ieee80211_vif *vif)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
2010-03-19 13:14:53 +08:00
|
|
|
struct ieee80211_hw *hw = &sdata->local->hw;
|
2009-03-23 03:57:35 +08:00
|
|
|
|
2010-04-07 22:48:40 +08:00
|
|
|
trace_api_beacon_loss(sdata);
|
|
|
|
|
2013-01-29 20:13:50 +08:00
|
|
|
sdata->u.mgd.connection_loss = false;
|
2010-03-19 13:14:53 +08:00
|
|
|
ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work);
|
2009-03-23 03:57:35 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ieee80211_beacon_loss);
|
|
|
|
|
2010-03-19 13:14:53 +08: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 22:48:40 +08:00
|
|
|
trace_api_connection_loss(sdata);
|
|
|
|
|
2013-01-29 20:13:50 +08:00
|
|
|
sdata->u.mgd.connection_loss = true;
|
2010-03-19 13:14:53 +08:00
|
|
|
ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ieee80211_connection_loss);
|
|
|
|
|
2020-12-06 20:54:43 +08:00
|
|
|
void ieee80211_disconnect(struct ieee80211_vif *vif, bool reconnect)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
|
|
|
struct ieee80211_hw *hw = &sdata->local->hw;
|
|
|
|
|
|
|
|
trace_api_disconnect(sdata, reconnect);
|
|
|
|
|
|
|
|
if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
|
|
|
|
return;
|
|
|
|
|
|
|
|
sdata->u.mgd.driver_disconnect = true;
|
|
|
|
sdata->u.mgd.reconnect = reconnect;
|
|
|
|
ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ieee80211_disconnect);
|
2010-03-19 13:14:53 +08:00
|
|
|
|
2012-01-20 20:55:27 +08: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 18:32:47 +08:00
|
|
|
sdata_assert_lock(sdata);
|
2012-01-20 20:55:27 +08:00
|
|
|
|
|
|
|
if (!assoc) {
|
2015-01-19 22:53:06 +08: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);
|
2022-06-02 03:17:34 +08:00
|
|
|
sta_info_destroy_addr(sdata, auth_data->ap_addr);
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
/* other links are destroyed */
|
2022-06-24 22:18:56 +08:00
|
|
|
sdata->deflink.u.mgd.conn_flags = 0;
|
2022-05-16 21:00:15 +08:00
|
|
|
eth_zero_addr(sdata->deflink.u.mgd.bssid);
|
2022-06-18 04:36:37 +08:00
|
|
|
ieee80211_link_info_change_notify(sdata, &sdata->deflink,
|
|
|
|
BSS_CHANGED_BSSID);
|
2012-11-26 18:57:41 +08:00
|
|
|
sdata->u.mgd.flags = 0;
|
2022-09-02 22:11:14 +08: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 16:43:33 +08:00
|
|
|
mutex_lock(&sdata->local->mtx);
|
2022-05-31 00:35:23 +08:00
|
|
|
ieee80211_link_release_channel(&sdata->deflink);
|
2022-06-02 03:17:34 +08:00
|
|
|
ieee80211_vif_set_links(sdata, 0);
|
2022-09-02 22:11:14 +08:00
|
|
|
mutex_unlock(&sdata->local->mtx);
|
2012-01-20 20:55:27 +08:00
|
|
|
}
|
|
|
|
|
2013-02-01 08:49:58 +08:00
|
|
|
cfg80211_put_bss(sdata->local->hw.wiphy, auth_data->bss);
|
2012-01-20 20:55:27 +08:00
|
|
|
kfree(auth_data);
|
|
|
|
sdata->u.mgd.auth_data = NULL;
|
|
|
|
}
|
|
|
|
|
2022-06-28 04:23:42 +08:00
|
|
|
enum assoc_status {
|
|
|
|
ASSOC_SUCCESS,
|
|
|
|
ASSOC_REJECTED,
|
|
|
|
ASSOC_TIMEOUT,
|
|
|
|
ASSOC_ABANDON,
|
|
|
|
};
|
|
|
|
|
2015-06-01 20:10:09 +08:00
|
|
|
static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata,
|
2022-06-28 04:23:42 +08:00
|
|
|
enum assoc_status status)
|
2015-06-01 20:10:09 +08:00
|
|
|
{
|
|
|
|
struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data;
|
|
|
|
|
|
|
|
sdata_assert_lock(sdata);
|
|
|
|
|
2022-06-28 04:23:42 +08:00
|
|
|
if (status != ASSOC_SUCCESS) {
|
2015-06-01 20:10:09 +08:00
|
|
|
/*
|
|
|
|
* 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);
|
2022-06-02 03:17:34 +08:00
|
|
|
sta_info_destroy_addr(sdata, assoc_data->ap_addr);
|
2015-06-01 20:10:09 +08:00
|
|
|
|
2022-06-24 22:18:56 +08:00
|
|
|
sdata->deflink.u.mgd.conn_flags = 0;
|
2022-05-16 21:00:15 +08:00
|
|
|
eth_zero_addr(sdata->deflink.u.mgd.bssid);
|
2022-06-18 04:36:37 +08:00
|
|
|
ieee80211_link_info_change_notify(sdata, &sdata->deflink,
|
|
|
|
BSS_CHANGED_BSSID);
|
2015-06-01 20:10:09 +08:00
|
|
|
sdata->u.mgd.flags = 0;
|
wifi: mac80211: move some future per-link data to bss_conf
To add MLD, reuse the bss_conf structure later for per-link
information, so move some things into it that are per link.
Most transformations were done with the following spatch:
@@
expression sdata;
identifier var = { chanctx_conf, mu_mimo_owner, csa_active, color_change_active, color_change_color };
@@
-sdata->vif.var
+sdata->vif.bss_conf.var
@@
struct ieee80211_vif *vif;
identifier var = { chanctx_conf, mu_mimo_owner, csa_active, color_change_active, color_change_color };
@@
-vif->var
+vif->bss_conf.var
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2022-05-10 19:26:44 +08:00
|
|
|
sdata->vif.bss_conf.mu_mimo_owner = false;
|
2016-02-16 18:48:18 +08:00
|
|
|
|
2022-06-28 04:23:42 +08:00
|
|
|
if (status != ASSOC_REJECTED) {
|
2022-06-28 04:09:50 +08:00
|
|
|
struct cfg80211_assoc_failure data = {
|
2022-06-28 04:23:42 +08:00
|
|
|
.timeout = status == ASSOC_TIMEOUT,
|
2022-06-28 04:09:50 +08:00
|
|
|
};
|
2022-06-02 03:17:34 +08:00
|
|
|
int i;
|
|
|
|
|
|
|
|
BUILD_BUG_ON(ARRAY_SIZE(data.bss) !=
|
|
|
|
ARRAY_SIZE(assoc_data->link));
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(data.bss); i++)
|
|
|
|
data.bss[i] = assoc_data->link[i].bss;
|
|
|
|
|
|
|
|
if (sdata->vif.valid_links)
|
|
|
|
data.ap_mld_addr = assoc_data->ap_addr;
|
2022-06-28 04:09:50 +08:00
|
|
|
|
|
|
|
cfg80211_assoc_failure(sdata->dev, &data);
|
|
|
|
}
|
2022-06-02 03:17:34 +08:00
|
|
|
|
2022-09-02 22:11:14 +08:00
|
|
|
mutex_lock(&sdata->local->mtx);
|
|
|
|
ieee80211_link_release_channel(&sdata->deflink);
|
2022-06-02 03:17:34 +08:00
|
|
|
ieee80211_vif_set_links(sdata, 0);
|
2022-09-02 22:11:14 +08:00
|
|
|
mutex_unlock(&sdata->local->mtx);
|
2015-06-01 20:10:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
kfree(assoc_data);
|
|
|
|
sdata->u.mgd.assoc_data = NULL;
|
|
|
|
}
|
|
|
|
|
2012-01-20 20:55:27 +08:00
|
|
|
static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct ieee80211_mgmt *mgmt, size_t len)
|
|
|
|
{
|
2013-01-29 22:02:27 +08:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
2012-01-20 20:55:27 +08:00
|
|
|
struct ieee80211_mgd_auth_data *auth_data = sdata->u.mgd.auth_data;
|
2021-09-20 21:40:09 +08:00
|
|
|
const struct element *challenge;
|
2012-01-20 20:55:27 +08:00
|
|
|
u8 *pos;
|
2013-01-29 22:02:27 +08:00
|
|
|
u32 tx_flags = 0;
|
2021-06-18 18:41:55 +08:00
|
|
|
struct ieee80211_prep_tx_info info = {
|
|
|
|
.subtype = IEEE80211_STYPE_AUTH,
|
|
|
|
};
|
2012-01-20 20:55:27 +08:00
|
|
|
|
|
|
|
pos = mgmt->u.auth.variable;
|
2021-09-20 21:40:09 +08:00
|
|
|
challenge = cfg80211_find_elem(WLAN_EID_CHALLENGE, pos,
|
|
|
|
len - (pos - (u8 *)mgmt));
|
|
|
|
if (!challenge)
|
2012-01-20 20:55:27 +08:00
|
|
|
return;
|
|
|
|
auth_data->expected_transaction = 4;
|
2021-06-18 18:41:55 +08:00
|
|
|
drv_mgd_prepare_tx(sdata->local, sdata, &info);
|
2015-06-03 03:39:54 +08:00
|
|
|
if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS))
|
2013-01-29 22:02:27 +08:00
|
|
|
tx_flags = IEEE80211_TX_CTL_REQ_TX_STATUS |
|
|
|
|
IEEE80211_TX_INTFL_MLME_CONN_TX;
|
2012-10-01 00:29:37 +08:00
|
|
|
ieee80211_send_auth(sdata, 3, auth_data->algorithm, 0,
|
2021-09-20 21:40:09 +08:00
|
|
|
(void *)challenge,
|
|
|
|
challenge->datalen + sizeof(*challenge),
|
2022-06-02 03:17:34 +08:00
|
|
|
auth_data->ap_addr, auth_data->ap_addr,
|
2012-01-20 20:55:27 +08:00
|
|
|
auth_data->key, auth_data->key_len,
|
2013-01-29 22:02:27 +08:00
|
|
|
auth_data->key_idx, tx_flags);
|
2012-01-20 20:55:27 +08:00
|
|
|
}
|
|
|
|
|
2022-07-12 21:13:23 +08:00
|
|
|
static bool ieee80211_mark_sta_auth(struct ieee80211_sub_if_data *sdata)
|
2018-10-11 05:21:19 +08:00
|
|
|
{
|
2018-10-11 05:21:21 +08:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2022-06-02 03:17:34 +08:00
|
|
|
const u8 *ap_addr = ifmgd->auth_data->ap_addr;
|
2018-10-11 05:21:19 +08:00
|
|
|
struct sta_info *sta;
|
2018-10-16 10:35:30 +08:00
|
|
|
bool result = true;
|
2018-10-11 05:21:19 +08:00
|
|
|
|
2018-10-11 05:21:21 +08:00
|
|
|
sdata_info(sdata, "authenticated\n");
|
|
|
|
ifmgd->auth_data->done = true;
|
|
|
|
ifmgd->auth_data->timeout = jiffies + IEEE80211_AUTH_WAIT_ASSOC;
|
|
|
|
ifmgd->auth_data->timeout_started = true;
|
|
|
|
run_again(sdata, ifmgd->auth_data->timeout);
|
|
|
|
|
2018-10-11 05:21:19 +08:00
|
|
|
/* move station state to auth */
|
|
|
|
mutex_lock(&sdata->local->sta_mtx);
|
2022-06-24 22:08:39 +08:00
|
|
|
sta = sta_info_get(sdata, ap_addr);
|
2018-10-11 05:21:19 +08:00
|
|
|
if (!sta) {
|
2022-06-24 22:08:39 +08:00
|
|
|
WARN_ONCE(1, "%s: STA %pM not found", sdata->name, ap_addr);
|
2018-10-16 10:35:30 +08:00
|
|
|
result = false;
|
|
|
|
goto out;
|
2018-10-11 05:21:19 +08:00
|
|
|
}
|
|
|
|
if (sta_info_move_state(sta, IEEE80211_STA_AUTH)) {
|
2022-06-24 22:08:39 +08:00
|
|
|
sdata_info(sdata, "failed moving %pM to auth\n", ap_addr);
|
2018-10-16 10:35:30 +08:00
|
|
|
result = false;
|
|
|
|
goto out;
|
2018-10-11 05:21:19 +08:00
|
|
|
}
|
|
|
|
|
2018-10-16 10:35:30 +08:00
|
|
|
out:
|
|
|
|
mutex_unlock(&sdata->local->sta_mtx);
|
|
|
|
return result;
|
2018-10-11 05:21:19 +08:00
|
|
|
}
|
|
|
|
|
2013-05-10 18:32:47 +08:00
|
|
|
static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct ieee80211_mgmt *mgmt, size_t len)
|
2012-01-20 20:55:27 +08:00
|
|
|
{
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
u16 auth_alg, auth_transaction, status_code;
|
2015-03-17 05:23:35 +08:00
|
|
|
struct ieee80211_event event = {
|
|
|
|
.type = MLME_EVENT,
|
|
|
|
.u.mlme.data = AUTH_EVENT,
|
|
|
|
};
|
2021-06-18 18:41:55 +08:00
|
|
|
struct ieee80211_prep_tx_info info = {
|
|
|
|
.subtype = IEEE80211_STYPE_AUTH,
|
|
|
|
};
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2013-05-10 18:32:47 +08:00
|
|
|
sdata_assert_lock(sdata);
|
2012-01-20 20:55:27 +08:00
|
|
|
|
|
|
|
if (len < 24 + 6)
|
2013-05-10 18:32:47 +08:00
|
|
|
return;
|
2012-01-20 20:55:27 +08:00
|
|
|
|
|
|
|
if (!ifmgd->auth_data || ifmgd->auth_data->done)
|
2013-05-10 18:32:47 +08:00
|
|
|
return;
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
if (!ether_addr_equal(ifmgd->auth_data->ap_addr, mgmt->bssid))
|
2013-05-10 18:32:47 +08:00
|
|
|
return;
|
2012-01-20 20:55:27 +08: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 ||
|
2018-10-11 05:21:21 +08:00
|
|
|
(auth_alg != WLAN_AUTH_SAE &&
|
|
|
|
auth_transaction != ifmgd->auth_data->expected_transaction) ||
|
|
|
|
(auth_alg == WLAN_AUTH_SAE &&
|
|
|
|
(auth_transaction < ifmgd->auth_data->expected_transaction ||
|
|
|
|
auth_transaction > 2))) {
|
2012-10-01 00:29:38 +08:00
|
|
|
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);
|
2021-06-18 18:41:55 +08:00
|
|
|
goto notify_driver;
|
2012-10-01 00:29:38 +08:00
|
|
|
}
|
2012-01-20 20:55:27 +08:00
|
|
|
|
|
|
|
if (status_code != WLAN_STATUS_SUCCESS) {
|
2020-03-26 21:09:34 +08:00
|
|
|
cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
|
|
|
|
|
|
|
|
if (auth_alg == WLAN_AUTH_SAE &&
|
2020-08-01 02:38:30 +08:00
|
|
|
(status_code == WLAN_STATUS_ANTI_CLOG_REQUIRED ||
|
|
|
|
(auth_transaction == 1 &&
|
|
|
|
(status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
|
2022-02-24 17:39:34 +08:00
|
|
|
status_code == WLAN_STATUS_SAE_PK)))) {
|
|
|
|
/* waiting for userspace now */
|
|
|
|
ifmgd->auth_data->waiting = true;
|
|
|
|
ifmgd->auth_data->timeout =
|
|
|
|
jiffies + IEEE80211_AUTH_WAIT_SAE_RETRY;
|
|
|
|
ifmgd->auth_data->timeout_started = true;
|
|
|
|
run_again(sdata, ifmgd->auth_data->timeout);
|
2021-06-18 18:41:55 +08:00
|
|
|
goto notify_driver;
|
2022-02-24 17:39:34 +08:00
|
|
|
}
|
2020-03-26 21:09:34 +08:00
|
|
|
|
2012-06-22 17:29:50 +08:00
|
|
|
sdata_info(sdata, "%pM denied authentication (status %d)\n",
|
|
|
|
mgmt->sa, status_code);
|
2012-05-13 23:07:04 +08:00
|
|
|
ieee80211_destroy_auth_data(sdata, false);
|
2015-03-17 05:23:35 +08:00
|
|
|
event.u.mlme.status = MLME_DENIED;
|
|
|
|
event.u.mlme.reason = status_code;
|
|
|
|
drv_event_callback(sdata->local, sdata, &event);
|
2021-06-18 18:41:55 +08:00
|
|
|
goto notify_driver;
|
2012-01-20 20:55:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (ifmgd->auth_data->algorithm) {
|
|
|
|
case WLAN_AUTH_OPEN:
|
|
|
|
case WLAN_AUTH_LEAP:
|
|
|
|
case WLAN_AUTH_FT:
|
2012-10-01 00:29:40 +08:00
|
|
|
case WLAN_AUTH_SAE:
|
2016-10-27 05:42:04 +08:00
|
|
|
case WLAN_AUTH_FILS_SK:
|
|
|
|
case WLAN_AUTH_FILS_SK_PFS:
|
|
|
|
case WLAN_AUTH_FILS_PK:
|
2012-01-20 20:55:27 +08: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 18:32:47 +08:00
|
|
|
return;
|
2012-01-20 20:55:27 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
WARN_ONCE(1, "invalid auth alg %d",
|
|
|
|
ifmgd->auth_data->algorithm);
|
2021-06-18 18:41:55 +08:00
|
|
|
goto notify_driver;
|
2012-01-20 20:55:27 +08:00
|
|
|
}
|
|
|
|
|
2015-03-17 05:23:35 +08:00
|
|
|
event.u.mlme.status = MLME_SUCCESS;
|
2021-06-18 18:41:55 +08:00
|
|
|
info.success = 1;
|
2015-03-17 05:23:35 +08:00
|
|
|
drv_event_callback(sdata->local, sdata, &event);
|
2018-10-11 05:21:21 +08:00
|
|
|
if (ifmgd->auth_data->algorithm != WLAN_AUTH_SAE ||
|
|
|
|
(auth_transaction == 2 &&
|
|
|
|
ifmgd->auth_data->expected_transaction == 2)) {
|
2022-07-12 21:13:23 +08:00
|
|
|
if (!ieee80211_mark_sta_auth(sdata))
|
2020-02-21 17:47:20 +08:00
|
|
|
return; /* ignore frame -- wait for timeout */
|
2018-10-11 05:21:21 +08:00
|
|
|
} else if (ifmgd->auth_data->algorithm == WLAN_AUTH_SAE &&
|
|
|
|
auth_transaction == 2) {
|
|
|
|
sdata_info(sdata, "SAE peer confirmed\n");
|
|
|
|
ifmgd->auth_data->peer_confirmed = true;
|
2012-10-01 00:29:40 +08:00
|
|
|
}
|
|
|
|
|
2013-05-16 06:55:00 +08:00
|
|
|
cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
|
2021-06-18 18:41:55 +08:00
|
|
|
notify_driver:
|
|
|
|
drv_mgd_complete_tx(sdata->local, sdata, &info);
|
2012-01-20 20:55:27 +08:00
|
|
|
}
|
|
|
|
|
2014-02-12 02:36:24 +08:00
|
|
|
#define case_WLAN(type) \
|
|
|
|
case WLAN_REASON_##type: return #type
|
|
|
|
|
mac80211: handle deauthentication/disassociation from TDLS peer
When receiving a deauthentication/disassociation frame from a TDLS
peer, a station should not disconnect the current AP, but only
disable the current TDLS link if it's enabled.
Without this change, a TDLS issue can be reproduced by following the
steps as below:
1. STA-1 and STA-2 are connected to AP, bidirection traffic is running
between STA-1 and STA-2.
2. Set up TDLS link between STA-1 and STA-2, stay for a while, then
teardown TDLS link.
3. Repeat step #2 and monitor the connection between STA and AP.
During the test, one STA may send a deauthentication/disassociation
frame to another, after TDLS teardown, with reason code 6/7, which
means: Class 2/3 frame received from nonassociated STA.
On receive this frame, the receiver STA will disconnect the current
AP and then reconnect. It's not a expected behavior, purpose of this
frame should be disabling the TDLS link, not the link with AP.
Cc: stable@vger.kernel.org
Signed-off-by: Yu Wang <yyuwang@codeaurora.org>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2019-05-10 17:04:52 +08:00
|
|
|
const char *ieee80211_get_reason_code_string(u16 reason_code)
|
2014-02-12 02:36:24 +08:00
|
|
|
{
|
|
|
|
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 20:55:27 +08:00
|
|
|
|
2013-05-10 18:32:47 +08:00
|
|
|
static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct ieee80211_mgmt *mgmt, size_t len)
|
2007-05-06 02:45:53 +08:00
|
|
|
{
|
2009-02-15 19:44:28 +08:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2015-06-01 20:10:09 +08:00
|
|
|
u16 reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2013-05-10 18:32:47 +08:00
|
|
|
sdata_assert_lock(sdata);
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2008-06-30 21:10:46 +08:00
|
|
|
if (len < 24 + 2)
|
2013-05-10 18:32:47 +08:00
|
|
|
return;
|
2007-05-06 02:45:53 +08:00
|
|
|
|
mac80211: handle deauthentication/disassociation from TDLS peer
When receiving a deauthentication/disassociation frame from a TDLS
peer, a station should not disconnect the current AP, but only
disable the current TDLS link if it's enabled.
Without this change, a TDLS issue can be reproduced by following the
steps as below:
1. STA-1 and STA-2 are connected to AP, bidirection traffic is running
between STA-1 and STA-2.
2. Set up TDLS link between STA-1 and STA-2, stay for a while, then
teardown TDLS link.
3. Repeat step #2 and monitor the connection between STA and AP.
During the test, one STA may send a deauthentication/disassociation
frame to another, after TDLS teardown, with reason code 6/7, which
means: Class 2/3 frame received from nonassociated STA.
On receive this frame, the receiver STA will disconnect the current
AP and then reconnect. It's not a expected behavior, purpose of this
frame should be disabling the TDLS link, not the link with AP.
Cc: stable@vger.kernel.org
Signed-off-by: Yu Wang <yyuwang@codeaurora.org>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2019-05-10 17:04:52 +08:00
|
|
|
if (!ether_addr_equal(mgmt->bssid, mgmt->sa)) {
|
|
|
|
ieee80211_tdls_handle_disconnect(sdata, mgmt->sa, reason_code);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-06-01 20:10:09 +08:00
|
|
|
if (ifmgd->associated &&
|
2022-06-02 03:17:34 +08:00
|
|
|
ether_addr_equal(mgmt->bssid, sdata->vif.cfg.ap_addr)) {
|
2015-06-01 20:10:09 +08:00
|
|
|
sdata_info(sdata, "deauthenticated from %pM (Reason: %u=%s)\n",
|
2022-06-02 03:17:34 +08:00
|
|
|
sdata->vif.cfg.ap_addr, reason_code,
|
2015-06-01 20:10:09 +08:00
|
|
|
ieee80211_get_reason_code_string(reason_code));
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2015-06-01 20:10:09 +08:00
|
|
|
ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2015-06-01 20:10:09 +08:00
|
|
|
ieee80211_report_disconnect(sdata, (u8 *)mgmt, len, false,
|
2020-12-06 20:54:43 +08:00
|
|
|
reason_code, false);
|
2015-06-01 20:10:09 +08:00
|
|
|
return;
|
|
|
|
}
|
2009-07-07 09:45:17 +08:00
|
|
|
|
2015-06-01 20:10:09 +08:00
|
|
|
if (ifmgd->assoc_data &&
|
2022-06-02 03:17:34 +08:00
|
|
|
ether_addr_equal(mgmt->bssid, ifmgd->assoc_data->ap_addr)) {
|
2015-06-01 20:10:09 +08:00
|
|
|
sdata_info(sdata,
|
|
|
|
"deauthenticated from %pM while associating (Reason: %u=%s)\n",
|
2022-06-02 03:17:34 +08:00
|
|
|
ifmgd->assoc_data->ap_addr, reason_code,
|
2015-06-01 20:10:09 +08:00
|
|
|
ieee80211_get_reason_code_string(reason_code));
|
|
|
|
|
2022-06-28 04:23:42 +08:00
|
|
|
ieee80211_destroy_assoc_data(sdata, ASSOC_ABANDON);
|
2015-06-01 20:10:09 +08:00
|
|
|
|
|
|
|
cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len);
|
|
|
|
return;
|
|
|
|
}
|
2007-05-06 02:45:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-05-10 18:32:47 +08:00
|
|
|
static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct ieee80211_mgmt *mgmt, size_t len)
|
2007-05-06 02:45:53 +08:00
|
|
|
{
|
2009-02-15 19:44:28 +08:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2007-05-06 02:45:53 +08:00
|
|
|
u16 reason_code;
|
|
|
|
|
2013-05-10 18:32:47 +08:00
|
|
|
sdata_assert_lock(sdata);
|
2009-07-07 09:45:17 +08:00
|
|
|
|
2012-01-20 20:55:27 +08:00
|
|
|
if (len < 24 + 2)
|
2013-05-10 18:32:47 +08:00
|
|
|
return;
|
2009-07-07 09:45:17 +08:00
|
|
|
|
2012-01-20 20:55:27 +08:00
|
|
|
if (!ifmgd->associated ||
|
2022-06-02 03:17:34 +08:00
|
|
|
!ether_addr_equal(mgmt->bssid, sdata->vif.cfg.ap_addr))
|
2013-05-10 18:32:47 +08:00
|
|
|
return;
|
2007-05-06 02:45:53 +08:00
|
|
|
|
|
|
|
reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
|
|
|
|
|
mac80211: handle deauthentication/disassociation from TDLS peer
When receiving a deauthentication/disassociation frame from a TDLS
peer, a station should not disconnect the current AP, but only
disable the current TDLS link if it's enabled.
Without this change, a TDLS issue can be reproduced by following the
steps as below:
1. STA-1 and STA-2 are connected to AP, bidirection traffic is running
between STA-1 and STA-2.
2. Set up TDLS link between STA-1 and STA-2, stay for a while, then
teardown TDLS link.
3. Repeat step #2 and monitor the connection between STA and AP.
During the test, one STA may send a deauthentication/disassociation
frame to another, after TDLS teardown, with reason code 6/7, which
means: Class 2/3 frame received from nonassociated STA.
On receive this frame, the receiver STA will disconnect the current
AP and then reconnect. It's not a expected behavior, purpose of this
frame should be disabling the TDLS link, not the link with AP.
Cc: stable@vger.kernel.org
Signed-off-by: Yu Wang <yyuwang@codeaurora.org>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2019-05-10 17:04:52 +08:00
|
|
|
if (!ether_addr_equal(mgmt->bssid, mgmt->sa)) {
|
|
|
|
ieee80211_tdls_handle_disconnect(sdata, mgmt->sa, reason_code);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-02-15 21:21:27 +08:00
|
|
|
sdata_info(sdata, "disassociated from %pM (Reason: %u=%s)\n",
|
2022-06-02 03:17:34 +08:00
|
|
|
sdata->vif.cfg.ap_addr, reason_code,
|
2017-02-15 21:21:27 +08:00
|
|
|
ieee80211_get_reason_code_string(reason_code));
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2012-02-24 20:50:54 +08:00
|
|
|
ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
|
|
|
|
|
2020-12-06 20:54:43 +08:00
|
|
|
ieee80211_report_disconnect(sdata, (u8 *)mgmt, len, false, reason_code,
|
|
|
|
false);
|
2007-05-06 02:45:53 +08:00
|
|
|
}
|
|
|
|
|
2011-10-15 06:14:49 +08: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 22:55:53 +08:00
|
|
|
int *min_rate, int *min_rate_index,
|
2017-06-10 18:52:44 +08:00
|
|
|
int shift)
|
2011-10-15 06:14:49 +08:00
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
for (i = 0; i < supp_rates_len; i++) {
|
2013-07-08 22:55:53 +08:00
|
|
|
int rate = supp_rates[i] & 0x7f;
|
2011-10-15 06:14:49 +08:00
|
|
|
bool is_basic = !!(supp_rates[i] & 0x80);
|
|
|
|
|
2013-07-08 22:55:53 +08:00
|
|
|
if ((rate * 5 * (1 << shift)) > 110)
|
2011-10-15 06:14:49 +08:00
|
|
|
*have_higher_than_11mbit = true;
|
|
|
|
|
|
|
|
/*
|
2020-11-29 23:30:47 +08:00
|
|
|
* Skip HT, VHT, HE and SAE H2E only BSS membership selectors
|
|
|
|
* since they're not rates.
|
2011-10-15 06:14:49 +08:00
|
|
|
*
|
2017-03-07 05:59:04 +08:00
|
|
|
* Note: Even though the membership selector and the basic
|
2011-10-15 06:14:49 +08:00
|
|
|
* rate flag share the same bit, they are not exactly
|
|
|
|
* the same.
|
|
|
|
*/
|
2017-03-07 05:59:04 +08:00
|
|
|
if (supp_rates[i] == (0x80 | BSS_MEMBERSHIP_SELECTOR_HT_PHY) ||
|
2020-03-26 21:09:36 +08:00
|
|
|
supp_rates[i] == (0x80 | BSS_MEMBERSHIP_SELECTOR_VHT_PHY) ||
|
2020-11-29 23:30:47 +08:00
|
|
|
supp_rates[i] == (0x80 | BSS_MEMBERSHIP_SELECTOR_HE_PHY) ||
|
|
|
|
supp_rates[i] == (0x80 | BSS_MEMBERSHIP_SELECTOR_SAE_H2E))
|
2011-10-15 06:14:49 +08:00
|
|
|
continue;
|
|
|
|
|
|
|
|
for (j = 0; j < sband->n_bitrates; j++) {
|
2013-07-08 22:55:53 +08:00
|
|
|
struct ieee80211_rate *br;
|
|
|
|
int brate;
|
|
|
|
|
|
|
|
br = &sband->bitrates[j];
|
|
|
|
|
|
|
|
brate = DIV_ROUND_UP(br->bitrate, (1 << shift) * 5);
|
|
|
|
if (brate == rate) {
|
2011-10-15 06:14:49 +08:00
|
|
|
*rates |= BIT(j);
|
|
|
|
if (is_basic)
|
|
|
|
*basic_rates |= BIT(j);
|
2013-07-08 22:55:53 +08:00
|
|
|
if ((rate * 5) < *min_rate) {
|
|
|
|
*min_rate = rate * 5;
|
2011-10-15 06:14:49 +08:00
|
|
|
*min_rate_index = j;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2022-09-12 03:01:42 +08:00
|
|
|
static bool ieee80211_twt_req_supported(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct ieee80211_supported_band *sband,
|
|
|
|
const struct link_sta_info *link_sta,
|
2018-12-15 17:03:04 +08:00
|
|
|
const struct ieee802_11_elems *elems)
|
|
|
|
{
|
2022-09-12 03:01:42 +08:00
|
|
|
const struct ieee80211_sta_he_cap *own_he_cap =
|
|
|
|
ieee80211_get_he_iftype_cap(sband,
|
|
|
|
ieee80211_vif_type_p2p(&sdata->vif));
|
|
|
|
|
2018-12-15 17:03:04 +08:00
|
|
|
if (elems->ext_capab_len < 10)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!(elems->ext_capab[9] & WLAN_EXT_CAPA10_TWT_RESPONDER_SUPPORT))
|
|
|
|
return false;
|
|
|
|
|
2022-06-27 20:17:28 +08:00
|
|
|
return link_sta->pub->he_cap.he_cap_elem.mac_cap_info[0] &
|
2022-09-12 03:01:42 +08:00
|
|
|
IEEE80211_HE_MAC_CAP0_TWT_RES &&
|
|
|
|
own_he_cap &&
|
|
|
|
(own_he_cap->he_cap_elem.mac_cap_info[0] &
|
|
|
|
IEEE80211_HE_MAC_CAP0_TWT_REQ);
|
2018-12-15 17:03:04 +08:00
|
|
|
}
|
|
|
|
|
2022-09-12 03:01:42 +08:00
|
|
|
static int ieee80211_recalc_twt_req(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct ieee80211_supported_band *sband,
|
|
|
|
struct ieee80211_link_data *link,
|
2022-06-27 20:17:28 +08:00
|
|
|
struct link_sta_info *link_sta,
|
2019-05-28 19:49:47 +08:00
|
|
|
struct ieee802_11_elems *elems)
|
|
|
|
{
|
2022-09-12 03:01:42 +08:00
|
|
|
bool twt = ieee80211_twt_req_supported(sdata, sband, link_sta, elems);
|
2019-05-28 19:49:47 +08:00
|
|
|
|
2022-06-24 22:18:56 +08:00
|
|
|
if (link->conf->twt_requester != twt) {
|
|
|
|
link->conf->twt_requester = twt;
|
2019-05-28 19:49:47 +08:00
|
|
|
return BSS_CHANGED_TWT;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-06-18 18:41:50 +08:00
|
|
|
static bool ieee80211_twt_bcast_support(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct ieee80211_bss_conf *bss_conf,
|
2021-06-18 18:41:35 +08:00
|
|
|
struct ieee80211_supported_band *sband,
|
2022-06-27 20:17:28 +08:00
|
|
|
struct link_sta_info *link_sta)
|
2021-06-18 18:41:35 +08:00
|
|
|
{
|
|
|
|
const struct ieee80211_sta_he_cap *own_he_cap =
|
2021-06-18 18:41:50 +08:00
|
|
|
ieee80211_get_he_iftype_cap(sband,
|
|
|
|
ieee80211_vif_type_p2p(&sdata->vif));
|
2021-06-18 18:41:35 +08:00
|
|
|
|
|
|
|
return bss_conf->he_support &&
|
2022-06-27 20:17:28 +08:00
|
|
|
(link_sta->pub->he_cap.he_cap_elem.mac_cap_info[2] &
|
2021-06-18 18:41:35 +08:00
|
|
|
IEEE80211_HE_MAC_CAP2_BCAST_TWT) &&
|
|
|
|
own_he_cap &&
|
|
|
|
(own_he_cap->he_cap_elem.mac_cap_info[2] &
|
|
|
|
IEEE80211_HE_MAC_CAP2_BCAST_TWT);
|
|
|
|
}
|
|
|
|
|
2022-07-12 20:59:22 +08:00
|
|
|
static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link,
|
|
|
|
struct link_sta_info *link_sta,
|
|
|
|
struct cfg80211_bss *cbss,
|
|
|
|
struct ieee80211_mgmt *mgmt,
|
|
|
|
const u8 *elem_start,
|
|
|
|
unsigned int elem_len,
|
|
|
|
u64 *changed)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata = link->sdata;
|
|
|
|
struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data;
|
|
|
|
struct ieee80211_bss_conf *bss_conf = link->conf;
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
2022-09-06 16:48:56 +08:00
|
|
|
unsigned int link_id = link->link_id;
|
2022-07-12 20:59:22 +08:00
|
|
|
struct ieee80211_elems_parse_params parse_params = {
|
|
|
|
.start = elem_start,
|
|
|
|
.len = elem_len,
|
2022-09-06 16:48:56 +08:00
|
|
|
.link_id = link_id == assoc_data->assoc_link_id ? -1 : link_id,
|
2022-08-16 17:26:23 +08:00
|
|
|
.from_ap = true,
|
2022-07-12 20:59:22 +08:00
|
|
|
};
|
|
|
|
bool is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ;
|
|
|
|
bool is_s1g = cbss->channel->band == NL80211_BAND_S1GHZ;
|
|
|
|
const struct cfg80211_bss_ies *bss_ies = NULL;
|
|
|
|
struct ieee80211_supported_band *sband;
|
|
|
|
struct ieee802_11_elems *elems;
|
|
|
|
u16 capab_info;
|
|
|
|
bool ret;
|
|
|
|
|
|
|
|
elems = ieee802_11_parse_elems_full(&parse_params);
|
|
|
|
if (!elems)
|
|
|
|
return false;
|
|
|
|
|
2022-09-06 16:48:56 +08:00
|
|
|
if (link_id == assoc_data->assoc_link_id) {
|
|
|
|
capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
|
2022-09-08 16:31:10 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* we should not get to this flow unless the association was
|
|
|
|
* successful, so set the status directly to success
|
|
|
|
*/
|
|
|
|
assoc_data->link[link_id].status = WLAN_STATUS_SUCCESS;
|
2022-09-06 16:48:56 +08:00
|
|
|
} else if (!elems->prof) {
|
|
|
|
ret = false;
|
|
|
|
goto out;
|
|
|
|
} else {
|
|
|
|
const u8 *ptr = elems->prof->variable +
|
|
|
|
elems->prof->sta_info_len - 1;
|
|
|
|
|
2022-09-08 16:31:10 +08:00
|
|
|
/*
|
|
|
|
* During parsing, we validated that these fields exist,
|
|
|
|
* otherwise elems->prof would have been set to NULL.
|
|
|
|
*/
|
2022-09-06 16:48:56 +08:00
|
|
|
capab_info = get_unaligned_le16(ptr);
|
2022-09-08 16:31:10 +08:00
|
|
|
assoc_data->link[link_id].status = get_unaligned_le16(ptr + 2);
|
|
|
|
|
|
|
|
if (assoc_data->link[link_id].status != WLAN_STATUS_SUCCESS) {
|
|
|
|
link_info(link, "association response status code=%u\n",
|
|
|
|
assoc_data->link[link_id].status);
|
|
|
|
ret = true;
|
|
|
|
goto out;
|
|
|
|
}
|
2022-09-06 16:48:56 +08:00
|
|
|
}
|
2022-07-12 20:59:22 +08:00
|
|
|
|
|
|
|
if (!is_s1g && !elems->supp_rates) {
|
|
|
|
sdata_info(sdata, "no SuppRates element in AssocResp\n");
|
|
|
|
ret = false;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
link->u.mgd.tdls_chan_switch_prohibited =
|
|
|
|
elems->ext_capab && elems->ext_capab_len >= 5 &&
|
|
|
|
(elems->ext_capab[4] & WLAN_EXT_CAPA5_TDLS_CH_SW_PROHIBITED);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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 (!is_6ghz &&
|
|
|
|
((assoc_data->wmm && !elems->wmm_param) ||
|
|
|
|
(!(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT) &&
|
|
|
|
(!elems->ht_cap_elem || !elems->ht_operation)) ||
|
|
|
|
(!(link->u.mgd.conn_flags & IEEE80211_CONN_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) {
|
|
|
|
ret = false;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
parse_params.start = bss_ies->data;
|
|
|
|
parse_params.len = bss_ies->len;
|
2022-10-15 00:56:11 +08:00
|
|
|
parse_params.bss = cbss;
|
2022-07-12 20:59:22 +08:00
|
|
|
bss_elems = ieee802_11_parse_elems_full(&parse_params);
|
|
|
|
if (!bss_elems) {
|
|
|
|
ret = false;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 &&
|
|
|
|
!(link->u.mgd.conn_flags & IEEE80211_CONN_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 &&
|
|
|
|
!(link->u.mgd.conn_flags & IEEE80211_CONN_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 &&
|
|
|
|
!(link->u.mgd.conn_flags & IEEE80211_CONN_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 &&
|
|
|
|
!(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT)) {
|
|
|
|
elems->vht_operation = bss_elems->vht_operation;
|
|
|
|
sdata_info(sdata,
|
|
|
|
"AP bug: VHT operation missing from AssocResp\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
kfree(bss_elems);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We previously checked these in the beacon/probe response, so
|
|
|
|
* they should be present here. This is just a safety net.
|
|
|
|
*/
|
|
|
|
if (!is_6ghz && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT) &&
|
|
|
|
(!elems->wmm_param || !elems->ht_cap_elem || !elems->ht_operation)) {
|
|
|
|
sdata_info(sdata,
|
|
|
|
"HT AP is missing WMM params or HT capability/operation\n");
|
|
|
|
ret = false;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!is_6ghz && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT) &&
|
|
|
|
(!elems->vht_cap_elem || !elems->vht_operation)) {
|
|
|
|
sdata_info(sdata,
|
|
|
|
"VHT AP is missing VHT capability/operation\n");
|
|
|
|
ret = false;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_6ghz && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE) &&
|
|
|
|
!elems->he_6ghz_capa) {
|
|
|
|
sdata_info(sdata,
|
|
|
|
"HE 6 GHz AP is missing HE 6 GHz band capability\n");
|
|
|
|
ret = false;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2022-09-02 22:12:42 +08:00
|
|
|
if (WARN_ON(!link->conf->chandef.chan)) {
|
2022-07-12 20:59:22 +08:00
|
|
|
ret = false;
|
|
|
|
goto out;
|
|
|
|
}
|
2022-09-02 22:12:42 +08:00
|
|
|
sband = local->hw.wiphy->bands[link->conf->chandef.chan->band];
|
2022-07-12 20:59:22 +08:00
|
|
|
|
|
|
|
if (!(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE) &&
|
|
|
|
(!elems->he_cap || !elems->he_operation)) {
|
|
|
|
sdata_info(sdata,
|
|
|
|
"HE AP is missing HE capability/operation\n");
|
|
|
|
ret = false;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set up internal HT/VHT capabilities */
|
|
|
|
if (elems->ht_cap_elem && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT))
|
|
|
|
ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
|
|
|
|
elems->ht_cap_elem,
|
|
|
|
link_sta);
|
|
|
|
|
|
|
|
if (elems->vht_cap_elem && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT))
|
|
|
|
ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
|
|
|
|
elems->vht_cap_elem,
|
|
|
|
link_sta);
|
|
|
|
|
|
|
|
if (elems->he_operation && !(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE) &&
|
|
|
|
elems->he_cap) {
|
|
|
|
ieee80211_he_cap_ie_to_sta_he_cap(sdata, sband,
|
|
|
|
elems->he_cap,
|
|
|
|
elems->he_cap_len,
|
|
|
|
elems->he_6ghz_capa,
|
|
|
|
link_sta);
|
|
|
|
|
|
|
|
bss_conf->he_support = link_sta->pub->he_cap.has_he;
|
|
|
|
if (elems->rsnx && elems->rsnx_len &&
|
|
|
|
(elems->rsnx[0] & WLAN_RSNX_CAPA_PROTECTED_TWT) &&
|
|
|
|
wiphy_ext_feature_isset(local->hw.wiphy,
|
|
|
|
NL80211_EXT_FEATURE_PROTECTED_TWT))
|
|
|
|
bss_conf->twt_protected = true;
|
|
|
|
else
|
|
|
|
bss_conf->twt_protected = false;
|
|
|
|
|
2022-09-12 03:01:42 +08:00
|
|
|
*changed |= ieee80211_recalc_twt_req(sdata, sband, link,
|
|
|
|
link_sta, elems);
|
2022-07-12 20:59:22 +08:00
|
|
|
|
|
|
|
if (elems->eht_operation && elems->eht_cap &&
|
|
|
|
!(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_EHT)) {
|
|
|
|
ieee80211_eht_cap_ie_to_sta_eht_cap(sdata, sband,
|
|
|
|
elems->he_cap,
|
|
|
|
elems->he_cap_len,
|
|
|
|
elems->eht_cap,
|
|
|
|
elems->eht_cap_len,
|
|
|
|
link_sta);
|
|
|
|
|
|
|
|
bss_conf->eht_support = link_sta->pub->eht_cap.has_eht;
|
2023-01-27 19:39:31 +08:00
|
|
|
*changed |= BSS_CHANGED_EHT_PUNCTURING;
|
2022-07-12 20:59:22 +08:00
|
|
|
} else {
|
|
|
|
bss_conf->eht_support = false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
bss_conf->he_support = false;
|
|
|
|
bss_conf->twt_requester = false;
|
|
|
|
bss_conf->twt_protected = false;
|
|
|
|
bss_conf->eht_support = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bss_conf->twt_broadcast =
|
|
|
|
ieee80211_twt_bcast_support(sdata, bss_conf, sband, link_sta);
|
|
|
|
|
|
|
|
if (bss_conf->he_support) {
|
|
|
|
bss_conf->he_bss_color.color =
|
|
|
|
le32_get_bits(elems->he_operation->he_oper_params,
|
|
|
|
IEEE80211_HE_OPERATION_BSS_COLOR_MASK);
|
|
|
|
bss_conf->he_bss_color.partial =
|
|
|
|
le32_get_bits(elems->he_operation->he_oper_params,
|
|
|
|
IEEE80211_HE_OPERATION_PARTIAL_BSS_COLOR);
|
|
|
|
bss_conf->he_bss_color.enabled =
|
|
|
|
!le32_get_bits(elems->he_operation->he_oper_params,
|
|
|
|
IEEE80211_HE_OPERATION_BSS_COLOR_DISABLED);
|
|
|
|
|
|
|
|
if (bss_conf->he_bss_color.enabled)
|
|
|
|
*changed |= BSS_CHANGED_HE_BSS_COLOR;
|
|
|
|
|
|
|
|
bss_conf->htc_trig_based_pkt_ext =
|
|
|
|
le32_get_bits(elems->he_operation->he_oper_params,
|
|
|
|
IEEE80211_HE_OPERATION_DFLT_PE_DURATION_MASK);
|
|
|
|
bss_conf->frame_time_rts_th =
|
|
|
|
le32_get_bits(elems->he_operation->he_oper_params,
|
|
|
|
IEEE80211_HE_OPERATION_RTS_THRESHOLD_MASK);
|
|
|
|
|
|
|
|
bss_conf->uora_exists = !!elems->uora_element;
|
|
|
|
if (elems->uora_element)
|
|
|
|
bss_conf->uora_ocw_range = elems->uora_element[0];
|
|
|
|
|
|
|
|
ieee80211_he_op_ie_to_bss_conf(&sdata->vif, elems->he_operation);
|
|
|
|
ieee80211_he_spr_ie_to_bss_conf(&sdata->vif, elems->he_spr);
|
|
|
|
/* TODO: OPEN: what happens if BSS color disable is set? */
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cbss->transmitted_bss) {
|
|
|
|
bss_conf->nontransmitted = true;
|
|
|
|
ether_addr_copy(bss_conf->transmitter_bssid,
|
|
|
|
cbss->transmitted_bss->bssid);
|
|
|
|
bss_conf->bssid_indicator = cbss->max_bssid_indicator;
|
|
|
|
bss_conf->bssid_index = cbss->bssid_index;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
link_sta->pub->rx_nss = nss;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
link->u.mgd.wmm_last_param_set = -1;
|
|
|
|
link->u.mgd.mu_edca_last_param_set = -1;
|
|
|
|
|
|
|
|
if (link->u.mgd.disable_wmm_tracking) {
|
|
|
|
ieee80211_set_wmm_default(link, false, false);
|
|
|
|
} else if (!ieee80211_sta_wmm_params(local, link, elems->wmm_param,
|
|
|
|
elems->wmm_param_len,
|
|
|
|
elems->mu_edca_param_set)) {
|
|
|
|
/* still enable QoS since we might have HT/VHT */
|
|
|
|
ieee80211_set_wmm_default(link, false, true);
|
|
|
|
/* disable WMM tracking in this case to disable
|
|
|
|
* tracking WMM parameter changes in the beacon if
|
|
|
|
* the parameters weren't actually valid. Doing so
|
|
|
|
* avoids changing parameters very strangely when
|
|
|
|
* the AP is going back and forth between valid and
|
|
|
|
* invalid parameters.
|
|
|
|
*/
|
|
|
|
link->u.mgd.disable_wmm_tracking = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (elems->max_idle_period_ie) {
|
|
|
|
bss_conf->max_idle_period =
|
|
|
|
le16_to_cpu(elems->max_idle_period_ie->max_idle_period);
|
|
|
|
bss_conf->protected_keep_alive =
|
|
|
|
!!(elems->max_idle_period_ie->idle_options &
|
|
|
|
WLAN_IDLE_OPTIONS_PROTECTED_KEEP_ALIVE);
|
|
|
|
*changed |= BSS_CHANGED_KEEP_ALIVE;
|
|
|
|
} else {
|
|
|
|
bss_conf->max_idle_period = 0;
|
|
|
|
bss_conf->protected_keep_alive = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set assoc capability (AID was already set earlier),
|
|
|
|
* ieee80211_set_associated() will tell the driver */
|
|
|
|
bss_conf->assoc_capability = capab_info;
|
|
|
|
|
|
|
|
ret = true;
|
|
|
|
out:
|
|
|
|
kfree(elems);
|
|
|
|
kfree(bss_ies);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-07-12 17:06:33 +08:00
|
|
|
static int ieee80211_mgd_setup_link_sta(struct ieee80211_link_data *link,
|
|
|
|
struct sta_info *sta,
|
2022-07-19 18:06:17 +08:00
|
|
|
struct link_sta_info *link_sta,
|
2022-07-12 17:06:33 +08:00
|
|
|
struct cfg80211_bss *cbss)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata = link->sdata;
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
struct ieee80211_bss *bss = (void *)cbss->priv;
|
|
|
|
u32 rates = 0, basic_rates = 0;
|
|
|
|
bool have_higher_than_11mbit = false;
|
|
|
|
int min_rate = INT_MAX, min_rate_index = -1;
|
|
|
|
/* this is clearly wrong for MLO but we'll just remove it later */
|
|
|
|
int shift = ieee80211_vif_get_shift(&sdata->vif);
|
|
|
|
struct ieee80211_supported_band *sband;
|
|
|
|
|
|
|
|
memcpy(link_sta->addr, cbss->bssid, ETH_ALEN);
|
2022-07-19 18:06:17 +08:00
|
|
|
memcpy(link_sta->pub->addr, cbss->bssid, ETH_ALEN);
|
2022-07-12 17:06:33 +08:00
|
|
|
|
|
|
|
/* TODO: S1G Basic Rate Set is expressed elsewhere */
|
|
|
|
if (cbss->channel->band == NL80211_BAND_S1GHZ) {
|
|
|
|
ieee80211_s1g_sta_rate_init(sta);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
sband = local->hw.wiphy->bands[cbss->channel->band];
|
|
|
|
|
|
|
|
ieee80211_get_rates(sband, bss->supp_rates, bss->supp_rates_len,
|
|
|
|
&rates, &basic_rates, &have_higher_than_11mbit,
|
|
|
|
&min_rate, &min_rate_index, shift);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
* Allow this workaround only in case the AP provided at least
|
|
|
|
* one rate.
|
|
|
|
*/
|
|
|
|
if (min_rate_index < 0) {
|
|
|
|
link_info(link, "No legacy rates in association response\n");
|
|
|
|
return -EINVAL;
|
|
|
|
} else if (!basic_rates) {
|
|
|
|
link_info(link, "No basic rates, using min rate instead\n");
|
|
|
|
basic_rates = BIT(min_rate_index);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rates)
|
2022-07-19 18:06:17 +08:00
|
|
|
link_sta->pub->supp_rates[cbss->channel->band] = rates;
|
2022-07-12 17:06:33 +08:00
|
|
|
else
|
|
|
|
link_info(link, "No rates found, keeping mandatory only\n");
|
|
|
|
|
|
|
|
link->conf->basic_rates = basic_rates;
|
|
|
|
|
|
|
|
/* cf. IEEE 802.11 9.2.12 */
|
|
|
|
link->operating_11g_mode = sband->band == NL80211_BAND_2GHZ &&
|
|
|
|
have_higher_than_11mbit;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
static u8 ieee80211_max_rx_chains(struct ieee80211_link_data *link,
|
|
|
|
struct cfg80211_bss *cbss)
|
2007-05-06 02:45:53 +08:00
|
|
|
{
|
2022-07-12 17:13:56 +08:00
|
|
|
struct ieee80211_he_mcs_nss_supp *he_mcs_nss_supp;
|
|
|
|
const struct element *ht_cap_elem, *vht_cap_elem;
|
|
|
|
const struct cfg80211_bss_ies *ies;
|
|
|
|
const struct ieee80211_ht_cap *ht_cap;
|
|
|
|
const struct ieee80211_vht_cap *vht_cap;
|
|
|
|
const struct ieee80211_he_cap_elem *he_cap;
|
|
|
|
const struct element *he_cap_elem;
|
|
|
|
u16 mcs_80_map, mcs_160_map;
|
|
|
|
int i, mcs_nss_size;
|
|
|
|
bool support_160;
|
|
|
|
u8 chains = 1;
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HT)
|
|
|
|
return chains;
|
2020-09-22 10:28:15 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
ht_cap_elem = ieee80211_bss_get_elem(cbss, WLAN_EID_HT_CAPABILITY);
|
|
|
|
if (ht_cap_elem && ht_cap_elem->datalen >= sizeof(*ht_cap)) {
|
|
|
|
ht_cap = (void *)ht_cap_elem->data;
|
|
|
|
chains = ieee80211_mcs_to_chains(&ht_cap->mcs);
|
|
|
|
/*
|
|
|
|
* TODO: use "Tx Maximum Number Spatial Streams Supported" and
|
|
|
|
* "Tx Unequal Modulation Supported" fields.
|
|
|
|
*/
|
|
|
|
}
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_VHT)
|
|
|
|
return chains;
|
2007-10-10 18:03:41 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
vht_cap_elem = ieee80211_bss_get_elem(cbss, WLAN_EID_VHT_CAPABILITY);
|
|
|
|
if (vht_cap_elem && vht_cap_elem->datalen >= sizeof(*vht_cap)) {
|
|
|
|
u8 nss;
|
|
|
|
u16 tx_mcs_map;
|
2011-10-28 17:59:47 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
vht_cap = (void *)vht_cap_elem->data;
|
|
|
|
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);
|
2011-10-28 17:59:47 +08:00
|
|
|
}
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_HE)
|
|
|
|
return chains;
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
ies = rcu_dereference(cbss->ies);
|
|
|
|
he_cap_elem = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_CAPABILITY,
|
|
|
|
ies->data, ies->len);
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (!he_cap_elem || he_cap_elem->datalen < sizeof(*he_cap))
|
|
|
|
return chains;
|
2013-05-28 16:54:03 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
/* skip one byte ext_tag_id */
|
|
|
|
he_cap = (void *)(he_cap_elem->data + 1);
|
|
|
|
mcs_nss_size = ieee80211_he_mcs_nss_size(he_cap);
|
2013-05-28 16:54:03 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
/* invalid HE IE */
|
|
|
|
if (he_cap_elem->datalen < 1 + mcs_nss_size + sizeof(*he_cap))
|
|
|
|
return chains;
|
mac80211: always allocate struct ieee802_11_elems
As the 802.11 spec evolves, we need to parse more and more
elements. This is causing the struct to grow, and we can no
longer get away with putting it on the stack.
Change the API to always dynamically allocate and return an
allocated pointer that must be kfree()d later.
As an alternative, I contemplated a scheme whereby we'd say
in the code which elements we needed, e.g.
DECLARE_ELEMENT_PARSER(elems,
SUPPORTED_CHANNELS,
CHANNEL_SWITCH,
EXT(KEY_DELIVERY));
ieee802_11_parse_elems(..., &elems, ...);
and while I think this is possible and will save us a lot
since most individual places only care about a small subset
of the elements, it ended up being a bit more work since a
lot of places do the parsing and then pass the struct to
other functions, sometimes with multiple levels.
Link: https://lore.kernel.org/r/20210920154009.26caff6b5998.I05ae58768e990e611aee8eca8abefd9d7bc15e05@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2021-09-20 21:40:10 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
/* mcs_nss is right after he_cap info */
|
|
|
|
he_mcs_nss_supp = (void *)(he_cap + 1);
|
2013-05-28 16:54:03 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
mcs_80_map = le16_to_cpu(he_mcs_nss_supp->tx_mcs_80);
|
mac80211: always allocate struct ieee802_11_elems
As the 802.11 spec evolves, we need to parse more and more
elements. This is causing the struct to grow, and we can no
longer get away with putting it on the stack.
Change the API to always dynamically allocate and return an
allocated pointer that must be kfree()d later.
As an alternative, I contemplated a scheme whereby we'd say
in the code which elements we needed, e.g.
DECLARE_ELEMENT_PARSER(elems,
SUPPORTED_CHANNELS,
CHANNEL_SWITCH,
EXT(KEY_DELIVERY));
ieee802_11_parse_elems(..., &elems, ...);
and while I think this is possible and will save us a lot
since most individual places only care about a small subset
of the elements, it ended up being a bit more work since a
lot of places do the parsing and then pass the struct to
other functions, sometimes with multiple levels.
Link: https://lore.kernel.org/r/20210920154009.26caff6b5998.I05ae58768e990e611aee8eca8abefd9d7bc15e05@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2021-09-20 21:40:10 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
for (i = 7; i >= 0; i--) {
|
|
|
|
u8 mcs_80 = mcs_80_map >> (2 * i) & 3;
|
|
|
|
|
|
|
|
if (mcs_80 != IEEE80211_VHT_MCS_NOT_SUPPORTED) {
|
|
|
|
chains = max_t(u8, chains, i + 1);
|
|
|
|
break;
|
|
|
|
}
|
2013-05-28 16:54:03 +08:00
|
|
|
}
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
support_160 = he_cap->phy_cap_info[0] &
|
|
|
|
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
|
|
|
|
|
|
|
|
if (!support_160)
|
|
|
|
return chains;
|
|
|
|
|
|
|
|
mcs_160_map = le16_to_cpu(he_mcs_nss_supp->tx_mcs_160);
|
|
|
|
for (i = 7; i >= 0; i--) {
|
|
|
|
u8 mcs_160 = mcs_160_map >> (2 * i) & 3;
|
|
|
|
|
|
|
|
if (mcs_160 != IEEE80211_VHT_MCS_NOT_SUPPORTED) {
|
|
|
|
chains = max_t(u8, chains, i + 1);
|
|
|
|
break;
|
|
|
|
}
|
2013-02-08 22:12:14 +08:00
|
|
|
}
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
return chains;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
ieee80211_verify_peer_he_mcs_support(struct ieee80211_sub_if_data *sdata,
|
|
|
|
const struct cfg80211_bss_ies *ies,
|
|
|
|
const struct ieee80211_he_operation *he_op)
|
|
|
|
{
|
|
|
|
const struct element *he_cap_elem;
|
|
|
|
const struct ieee80211_he_cap_elem *he_cap;
|
|
|
|
struct ieee80211_he_mcs_nss_supp *he_mcs_nss_supp;
|
|
|
|
u16 mcs_80_map_tx, mcs_80_map_rx;
|
|
|
|
u16 ap_min_req_set;
|
|
|
|
int mcs_nss_size;
|
|
|
|
int nss;
|
|
|
|
|
|
|
|
he_cap_elem = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_CAPABILITY,
|
|
|
|
ies->data, ies->len);
|
|
|
|
|
2022-09-16 03:55:53 +08:00
|
|
|
if (!he_cap_elem)
|
|
|
|
return false;
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
/* invalid HE IE */
|
2022-09-16 03:55:53 +08:00
|
|
|
if (he_cap_elem->datalen < 1 + sizeof(*he_cap)) {
|
2013-02-08 22:12:14 +08:00
|
|
|
sdata_info(sdata,
|
2022-07-12 17:13:56 +08:00
|
|
|
"Invalid HE elem, Disable HE\n");
|
|
|
|
return false;
|
2013-02-08 22:12:14 +08:00
|
|
|
}
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
/* skip one byte ext_tag_id */
|
|
|
|
he_cap = (void *)(he_cap_elem->data + 1);
|
|
|
|
mcs_nss_size = ieee80211_he_mcs_nss_size(he_cap);
|
|
|
|
|
|
|
|
/* invalid HE IE */
|
|
|
|
if (he_cap_elem->datalen < 1 + sizeof(*he_cap) + mcs_nss_size) {
|
2020-05-29 03:34:36 +08:00
|
|
|
sdata_info(sdata,
|
2022-07-12 17:13:56 +08:00
|
|
|
"Invalid HE elem with nss size, Disable HE\n");
|
|
|
|
return false;
|
2020-05-29 03:34:36 +08:00
|
|
|
}
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
/* mcs_nss is right after he_cap info */
|
|
|
|
he_mcs_nss_supp = (void *)(he_cap + 1);
|
2008-04-01 07:00:13 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
mcs_80_map_tx = le16_to_cpu(he_mcs_nss_supp->tx_mcs_80);
|
|
|
|
mcs_80_map_rx = le16_to_cpu(he_mcs_nss_supp->rx_mcs_80);
|
2008-02-23 22:17:12 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
/* P802.11-REVme/D0.3
|
|
|
|
* 27.1.1 Introduction to the HE PHY
|
|
|
|
* ...
|
|
|
|
* An HE STA shall support the following features:
|
|
|
|
* ...
|
|
|
|
* Single spatial stream HE-MCSs 0 to 7 (transmit and receive) in all
|
|
|
|
* supported channel widths for HE SU PPDUs
|
|
|
|
*/
|
|
|
|
if ((mcs_80_map_tx & 0x3) == IEEE80211_HE_MCS_NOT_SUPPORTED ||
|
|
|
|
(mcs_80_map_rx & 0x3) == IEEE80211_HE_MCS_NOT_SUPPORTED) {
|
2018-06-09 14:14:44 +08:00
|
|
|
sdata_info(sdata,
|
2022-07-12 17:13:56 +08:00
|
|
|
"Missing mandatory rates for 1 Nss, rx 0x%x, tx 0x%x, disable HE\n",
|
|
|
|
mcs_80_map_tx, mcs_80_map_rx);
|
|
|
|
return false;
|
2018-06-09 14:14:44 +08:00
|
|
|
}
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (!he_op)
|
|
|
|
return true;
|
2012-03-28 16:58:37 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
ap_min_req_set = le16_to_cpu(he_op->he_mcs_nss_set);
|
2012-10-10 19:33:04 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
/*
|
|
|
|
* Apparently iPhone 13 (at least iOS version 15.3.1) sets this to all
|
|
|
|
* zeroes, which is nonsense, and completely inconsistent with itself
|
|
|
|
* (it doesn't have 8 streams). Accept the settings in this case anyway.
|
|
|
|
*/
|
|
|
|
if (!ap_min_req_set)
|
|
|
|
return true;
|
2018-06-09 14:14:44 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
/* make sure the AP is consistent with itself
|
|
|
|
*
|
|
|
|
* P802.11-REVme/D0.3
|
|
|
|
* 26.17.1 Basic HE BSS operation
|
|
|
|
*
|
|
|
|
* A STA that is operating in an HE BSS shall be able to receive and
|
|
|
|
* transmit at each of the <HE-MCS, NSS> tuple values indicated by the
|
|
|
|
* Basic HE-MCS And NSS Set field of the HE Operation parameter of the
|
|
|
|
* MLME-START.request primitive and shall be able to receive at each of
|
|
|
|
* the <HE-MCS, NSS> tuple values indicated by the Supported HE-MCS and
|
|
|
|
* NSS Set field in the HE Capabilities parameter of the MLMESTART.request
|
|
|
|
* primitive
|
|
|
|
*/
|
|
|
|
for (nss = 8; nss > 0; nss--) {
|
|
|
|
u8 ap_op_val = (ap_min_req_set >> (2 * (nss - 1))) & 3;
|
|
|
|
u8 ap_rx_val;
|
|
|
|
u8 ap_tx_val;
|
2020-03-26 21:09:33 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (ap_op_val == IEEE80211_HE_MCS_NOT_SUPPORTED)
|
|
|
|
continue;
|
2022-02-15 00:30:02 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
ap_rx_val = (mcs_80_map_rx >> (2 * (nss - 1))) & 3;
|
|
|
|
ap_tx_val = (mcs_80_map_tx >> (2 * (nss - 1))) & 3;
|
2022-02-15 00:30:02 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (ap_rx_val == IEEE80211_HE_MCS_NOT_SUPPORTED ||
|
|
|
|
ap_tx_val == IEEE80211_HE_MCS_NOT_SUPPORTED ||
|
|
|
|
ap_rx_val < ap_op_val || ap_tx_val < ap_op_val) {
|
|
|
|
sdata_info(sdata,
|
|
|
|
"Invalid rates for %d Nss, rx %d, tx %d oper %d, disable HE\n",
|
|
|
|
nss, ap_rx_val, ap_rx_val, ap_op_val);
|
|
|
|
return false;
|
2022-02-15 00:30:02 +08:00
|
|
|
}
|
2018-06-09 14:14:44 +08:00
|
|
|
}
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
return true;
|
|
|
|
}
|
2018-06-09 14:14:44 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
static bool
|
|
|
|
ieee80211_verify_sta_he_mcs_support(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct ieee80211_supported_band *sband,
|
|
|
|
const struct ieee80211_he_operation *he_op)
|
|
|
|
{
|
|
|
|
const struct ieee80211_sta_he_cap *sta_he_cap =
|
|
|
|
ieee80211_get_he_iftype_cap(sband,
|
|
|
|
ieee80211_vif_type_p2p(&sdata->vif));
|
|
|
|
u16 ap_min_req_set;
|
|
|
|
int i;
|
2018-06-09 14:14:44 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (!sta_he_cap || !he_op)
|
|
|
|
return false;
|
2018-06-09 14:14:44 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
ap_min_req_set = le16_to_cpu(he_op->he_mcs_nss_set);
|
2019-01-17 00:22:56 +08:00
|
|
|
|
2013-02-08 22:12:14 +08:00
|
|
|
/*
|
2022-07-12 17:13:56 +08:00
|
|
|
* Apparently iPhone 13 (at least iOS version 15.3.1) sets this to all
|
|
|
|
* zeroes, which is nonsense, and completely inconsistent with itself
|
|
|
|
* (it doesn't have 8 streams). Accept the settings in this case anyway.
|
2013-02-08 22:12:14 +08:00
|
|
|
*/
|
2022-07-12 17:13:56 +08:00
|
|
|
if (!ap_min_req_set)
|
|
|
|
return true;
|
2013-02-08 00:36:12 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
/* Need to go over for 80MHz, 160MHz and for 80+80 */
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
|
|
const struct ieee80211_he_mcs_nss_supp *sta_mcs_nss_supp =
|
|
|
|
&sta_he_cap->he_mcs_nss_supp;
|
|
|
|
u16 sta_mcs_map_rx =
|
|
|
|
le16_to_cpu(((__le16 *)sta_mcs_nss_supp)[2 * i]);
|
|
|
|
u16 sta_mcs_map_tx =
|
|
|
|
le16_to_cpu(((__le16 *)sta_mcs_nss_supp)[2 * i + 1]);
|
2013-02-08 05:24:55 +08:00
|
|
|
u8 nss;
|
2022-07-12 17:13:56 +08:00
|
|
|
bool verified = true;
|
2013-02-08 05:24:55 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
/*
|
|
|
|
* For each band there is a maximum of 8 spatial streams
|
|
|
|
* possible. Each of the sta_mcs_map_* is a 16-bit struct built
|
|
|
|
* of 2 bits per NSS (1-8), with the values defined in enum
|
|
|
|
* ieee80211_he_mcs_support. Need to make sure STA TX and RX
|
|
|
|
* capabilities aren't less than the AP's minimum requirements
|
|
|
|
* for this HE BSS per SS.
|
|
|
|
* It is enough to find one such band that meets the reqs.
|
|
|
|
*/
|
|
|
|
for (nss = 8; nss > 0; nss--) {
|
|
|
|
u8 sta_rx_val = (sta_mcs_map_rx >> (2 * (nss - 1))) & 3;
|
|
|
|
u8 sta_tx_val = (sta_mcs_map_tx >> (2 * (nss - 1))) & 3;
|
|
|
|
u8 ap_val = (ap_min_req_set >> (2 * (nss - 1))) & 3;
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (ap_val == IEEE80211_HE_MCS_NOT_SUPPORTED)
|
|
|
|
continue;
|
2009-01-08 19:31:59 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
/*
|
|
|
|
* Make sure the HE AP doesn't require MCSs that aren't
|
|
|
|
* supported by the client as required by spec
|
|
|
|
*
|
|
|
|
* P802.11-REVme/D0.3
|
|
|
|
* 26.17.1 Basic HE BSS operation
|
|
|
|
*
|
|
|
|
* An HE STA shall not attempt to join * (MLME-JOIN.request primitive)
|
|
|
|
* a BSS, unless it supports (i.e., is able to both transmit and
|
|
|
|
* receive using) all of the <HE-MCS, NSS> tuples in the basic
|
|
|
|
* HE-MCS and NSS set.
|
|
|
|
*/
|
|
|
|
if (sta_rx_val == IEEE80211_HE_MCS_NOT_SUPPORTED ||
|
|
|
|
sta_tx_val == IEEE80211_HE_MCS_NOT_SUPPORTED ||
|
|
|
|
(ap_val > sta_rx_val) || (ap_val > sta_tx_val)) {
|
|
|
|
verified = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2008-10-22 17:41:38 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (verified)
|
|
|
|
return true;
|
2012-01-20 20:55:17 +08:00
|
|
|
}
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
/* If here, STA doesn't meet AP's HE min requirements */
|
|
|
|
return false;
|
|
|
|
}
|
2008-10-22 17:41:38 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct ieee80211_link_data *link,
|
2022-07-12 20:48:55 +08:00
|
|
|
struct cfg80211_bss *cbss,
|
|
|
|
ieee80211_conn_flags_t *conn_flags)
|
2022-07-12 17:13:56 +08:00
|
|
|
{
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
const struct ieee80211_ht_cap *ht_cap = NULL;
|
|
|
|
const struct ieee80211_ht_operation *ht_oper = NULL;
|
|
|
|
const struct ieee80211_vht_operation *vht_oper = NULL;
|
|
|
|
const struct ieee80211_he_operation *he_oper = NULL;
|
|
|
|
const struct ieee80211_eht_operation *eht_oper = NULL;
|
|
|
|
const struct ieee80211_s1g_oper_ie *s1g_oper = NULL;
|
|
|
|
struct ieee80211_supported_band *sband;
|
|
|
|
struct cfg80211_chan_def chandef;
|
|
|
|
bool is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ;
|
|
|
|
bool is_5ghz = cbss->channel->band == NL80211_BAND_5GHZ;
|
|
|
|
struct ieee80211_bss *bss = (void *)cbss->priv;
|
2022-08-16 17:26:23 +08:00
|
|
|
struct ieee80211_elems_parse_params parse_params = {
|
|
|
|
.bss = cbss,
|
|
|
|
.link_id = -1,
|
|
|
|
.from_ap = true,
|
|
|
|
};
|
2022-07-12 17:13:56 +08:00
|
|
|
struct ieee802_11_elems *elems;
|
|
|
|
const struct cfg80211_bss_ies *ies;
|
|
|
|
int ret;
|
|
|
|
u32 i;
|
|
|
|
bool have_80mhz;
|
2010-09-28 19:39:32 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
rcu_read_lock();
|
|
|
|
|
|
|
|
ies = rcu_dereference(cbss->ies);
|
2022-08-16 17:26:23 +08:00
|
|
|
parse_params.start = ies->data;
|
|
|
|
parse_params.len = ies->len;
|
|
|
|
elems = ieee802_11_parse_elems_full(&parse_params);
|
2022-07-12 17:13:56 +08:00
|
|
|
if (!elems) {
|
|
|
|
rcu_read_unlock();
|
|
|
|
return -ENOMEM;
|
2015-10-22 23:46:06 +08:00
|
|
|
}
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
sband = local->hw.wiphy->bands[cbss->channel->band];
|
|
|
|
|
2022-07-12 20:48:55 +08:00
|
|
|
*conn_flags &= ~(IEEE80211_CONN_DISABLE_40MHZ |
|
|
|
|
IEEE80211_CONN_DISABLE_80P80MHZ |
|
|
|
|
IEEE80211_CONN_DISABLE_160MHZ);
|
2022-07-12 17:13:56 +08:00
|
|
|
|
|
|
|
/* disable HT/VHT/HE if we don't support them */
|
|
|
|
if (!sband->ht_cap.ht_supported && !is_6ghz) {
|
|
|
|
mlme_dbg(sdata, "HT not supported, disabling HT/VHT/HE/EHT\n");
|
2022-07-12 20:48:55 +08:00
|
|
|
*conn_flags |= IEEE80211_CONN_DISABLE_HT;
|
|
|
|
*conn_flags |= IEEE80211_CONN_DISABLE_VHT;
|
|
|
|
*conn_flags |= IEEE80211_CONN_DISABLE_HE;
|
|
|
|
*conn_flags |= IEEE80211_CONN_DISABLE_EHT;
|
2017-04-26 15:58:47 +08:00
|
|
|
}
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (!sband->vht_cap.vht_supported && is_5ghz) {
|
|
|
|
mlme_dbg(sdata, "VHT not supported, disabling VHT/HE/EHT\n");
|
2022-07-12 20:48:55 +08:00
|
|
|
*conn_flags |= IEEE80211_CONN_DISABLE_VHT;
|
|
|
|
*conn_flags |= IEEE80211_CONN_DISABLE_HE;
|
|
|
|
*conn_flags |= IEEE80211_CONN_DISABLE_EHT;
|
2022-07-12 17:13:56 +08:00
|
|
|
}
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (!ieee80211_get_he_iftype_cap(sband,
|
|
|
|
ieee80211_vif_type_p2p(&sdata->vif))) {
|
|
|
|
mlme_dbg(sdata, "HE not supported, disabling HE and EHT\n");
|
2022-07-12 20:48:55 +08:00
|
|
|
*conn_flags |= IEEE80211_CONN_DISABLE_HE;
|
|
|
|
*conn_flags |= IEEE80211_CONN_DISABLE_EHT;
|
2022-07-12 17:13:56 +08:00
|
|
|
}
|
2010-01-09 01:06:26 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (!ieee80211_get_eht_iftype_cap(sband,
|
|
|
|
ieee80211_vif_type_p2p(&sdata->vif))) {
|
|
|
|
mlme_dbg(sdata, "EHT not supported, disabling EHT\n");
|
2022-07-12 20:48:55 +08:00
|
|
|
*conn_flags |= IEEE80211_CONN_DISABLE_EHT;
|
2022-07-12 17:13:56 +08:00
|
|
|
}
|
2009-03-23 03:57:14 +08:00
|
|
|
|
2022-07-12 20:48:55 +08:00
|
|
|
if (!(*conn_flags & IEEE80211_CONN_DISABLE_HT) && !is_6ghz) {
|
2022-07-12 17:13:56 +08:00
|
|
|
ht_oper = elems->ht_operation;
|
|
|
|
ht_cap = elems->ht_cap_elem;
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (!ht_cap) {
|
2022-07-12 20:48:55 +08:00
|
|
|
*conn_flags |= IEEE80211_CONN_DISABLE_HT;
|
2022-07-12 17:13:56 +08:00
|
|
|
ht_oper = NULL;
|
|
|
|
}
|
|
|
|
}
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2022-07-12 20:48:55 +08:00
|
|
|
if (!(*conn_flags & IEEE80211_CONN_DISABLE_VHT) && !is_6ghz) {
|
2022-07-12 17:13:56 +08:00
|
|
|
vht_oper = elems->vht_operation;
|
|
|
|
if (vht_oper && !ht_oper) {
|
|
|
|
vht_oper = NULL;
|
|
|
|
sdata_info(sdata,
|
|
|
|
"AP advertised VHT without HT, disabling HT/VHT/HE\n");
|
2022-07-12 20:48:55 +08:00
|
|
|
*conn_flags |= IEEE80211_CONN_DISABLE_HT;
|
|
|
|
*conn_flags |= IEEE80211_CONN_DISABLE_VHT;
|
|
|
|
*conn_flags |= IEEE80211_CONN_DISABLE_HE;
|
|
|
|
*conn_flags |= IEEE80211_CONN_DISABLE_EHT;
|
2022-07-12 17:13:56 +08:00
|
|
|
}
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (!elems->vht_cap_elem) {
|
2022-07-12 20:48:55 +08:00
|
|
|
*conn_flags |= IEEE80211_CONN_DISABLE_VHT;
|
2022-07-12 17:13:56 +08:00
|
|
|
vht_oper = NULL;
|
|
|
|
}
|
|
|
|
}
|
2020-09-22 10:28:15 +08:00
|
|
|
|
2022-07-12 20:48:55 +08:00
|
|
|
if (!(*conn_flags & IEEE80211_CONN_DISABLE_HE)) {
|
2022-07-12 17:13:56 +08:00
|
|
|
he_oper = elems->he_operation;
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2022-07-12 20:48:55 +08:00
|
|
|
if (link && is_6ghz) {
|
2022-07-12 17:13:56 +08:00
|
|
|
struct ieee80211_bss_conf *bss_conf;
|
|
|
|
u8 j = 0;
|
2020-09-22 10:28:15 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
bss_conf = link->conf;
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (elems->pwr_constr_elem)
|
|
|
|
bss_conf->pwr_reduction = *elems->pwr_constr_elem;
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
BUILD_BUG_ON(ARRAY_SIZE(bss_conf->tx_pwr_env) !=
|
|
|
|
ARRAY_SIZE(elems->tx_pwr_env));
|
|
|
|
|
|
|
|
for (i = 0; i < elems->tx_pwr_env_num; i++) {
|
|
|
|
if (elems->tx_pwr_env_len[i] >
|
|
|
|
sizeof(bss_conf->tx_pwr_env[j]))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
bss_conf->tx_pwr_env_num++;
|
|
|
|
memcpy(&bss_conf->tx_pwr_env[j], elems->tx_pwr_env[i],
|
|
|
|
elems->tx_pwr_env_len[i]);
|
|
|
|
j++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ieee80211_verify_peer_he_mcs_support(sdata, ies, he_oper) ||
|
|
|
|
!ieee80211_verify_sta_he_mcs_support(sdata, sband, he_oper))
|
2022-07-12 20:48:55 +08:00
|
|
|
*conn_flags |= IEEE80211_CONN_DISABLE_HE |
|
|
|
|
IEEE80211_CONN_DISABLE_EHT;
|
2020-09-22 10:28:15 +08:00
|
|
|
}
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2021-06-18 18:41:55 +08:00
|
|
|
/*
|
2022-07-12 17:13:56 +08:00
|
|
|
* EHT requires HE to be supported as well. Specifically for 6 GHz
|
|
|
|
* channels, the operation channel information can only be deduced from
|
|
|
|
* both the 6 GHz operation information (from the HE operation IE) and
|
|
|
|
* EHT operation.
|
2021-06-18 18:41:55 +08:00
|
|
|
*/
|
2022-07-12 20:48:55 +08:00
|
|
|
if (!(*conn_flags &
|
2022-07-12 17:13:56 +08:00
|
|
|
(IEEE80211_CONN_DISABLE_HE |
|
|
|
|
IEEE80211_CONN_DISABLE_EHT)) &&
|
|
|
|
he_oper) {
|
|
|
|
const struct cfg80211_bss_ies *cbss_ies;
|
|
|
|
const u8 *eht_oper_ie;
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
cbss_ies = rcu_dereference(cbss->ies);
|
|
|
|
eht_oper_ie = cfg80211_find_ext_ie(WLAN_EID_EXT_EHT_OPERATION,
|
|
|
|
cbss_ies->data, cbss_ies->len);
|
|
|
|
if (eht_oper_ie && eht_oper_ie[1] >=
|
|
|
|
1 + sizeof(struct ieee80211_eht_operation))
|
|
|
|
eht_oper = (void *)(eht_oper_ie + 3);
|
|
|
|
else
|
|
|
|
eht_oper = NULL;
|
|
|
|
}
|
2016-10-27 05:42:05 +08:00
|
|
|
|
2022-07-12 17:13:56 +08: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;
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2022-07-12 20:59:22 +08:00
|
|
|
have_80mhz = true;
|
|
|
|
break;
|
2022-07-12 17:13:56 +08:00
|
|
|
}
|
2019-01-17 00:22:56 +08:00
|
|
|
|
2022-07-12 20:59:22 +08:00
|
|
|
if (!have_80mhz) {
|
|
|
|
sdata_info(sdata, "80 MHz not supported, disabling VHT\n");
|
|
|
|
*conn_flags |= IEEE80211_CONN_DISABLE_VHT;
|
2018-03-15 00:58:34 +08:00
|
|
|
}
|
|
|
|
|
2022-07-12 20:59:22 +08:00
|
|
|
if (sband->band == NL80211_BAND_S1GHZ) {
|
|
|
|
s1g_oper = elems->s1g_oper;
|
|
|
|
if (!s1g_oper)
|
|
|
|
sdata_info(sdata,
|
|
|
|
"AP missing S1G operation element?\n");
|
|
|
|
}
|
2009-05-15 02:15:36 +08:00
|
|
|
|
2022-07-12 20:59:22 +08:00
|
|
|
*conn_flags |=
|
|
|
|
ieee80211_determine_chantype(sdata, link, *conn_flags,
|
|
|
|
sband,
|
|
|
|
cbss->channel,
|
|
|
|
bss->vht_cap_info,
|
|
|
|
ht_oper, vht_oper,
|
|
|
|
he_oper, eht_oper,
|
|
|
|
s1g_oper,
|
|
|
|
&chandef, false);
|
2009-07-10 21:29:03 +08:00
|
|
|
|
2022-07-12 20:59:22 +08:00
|
|
|
if (link)
|
|
|
|
link->needed_rx_chains =
|
|
|
|
min(ieee80211_max_rx_chains(link, cbss),
|
|
|
|
local->rx_chains);
|
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 19:17:26 +08:00
|
|
|
|
2022-07-12 20:59:22 +08:00
|
|
|
rcu_read_unlock();
|
|
|
|
/* the element data was RCU protected so no longer valid anyway */
|
|
|
|
kfree(elems);
|
|
|
|
elems = NULL;
|
2015-09-24 22:13:07 +08:00
|
|
|
|
2022-07-12 20:59:22 +08:00
|
|
|
if (*conn_flags & IEEE80211_CONN_DISABLE_HE && is_6ghz) {
|
|
|
|
sdata_info(sdata, "Rejecting non-HE 6/7 GHz connection");
|
|
|
|
return -EINVAL;
|
2008-12-24 10:39:02 +08:00
|
|
|
}
|
2008-10-08 16:59:33 +08:00
|
|
|
|
2022-07-12 20:59:22 +08:00
|
|
|
if (!link)
|
|
|
|
return 0;
|
2014-10-08 14:48:39 +08:00
|
|
|
|
2022-07-12 20:59:22 +08:00
|
|
|
/* will change later if needed */
|
|
|
|
link->smps_mode = IEEE80211_SMPS_OFF;
|
2015-03-01 15:10:00 +08:00
|
|
|
|
2022-07-12 20:59:22 +08:00
|
|
|
mutex_lock(&local->mtx);
|
|
|
|
/*
|
|
|
|
* 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_link_use_channel(link, &chandef,
|
|
|
|
IEEE80211_CHANCTX_SHARED);
|
2009-05-19 22:01:43 +08:00
|
|
|
|
2022-07-12 20:59:22 +08:00
|
|
|
/* don't downgrade for 5 and 10 MHz channels, though. */
|
|
|
|
if (chandef.width == NL80211_CHAN_WIDTH_5 ||
|
|
|
|
chandef.width == NL80211_CHAN_WIDTH_10)
|
|
|
|
goto out;
|
2012-07-06 23:37:43 +08:00
|
|
|
|
2022-07-12 20:59:22 +08:00
|
|
|
while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT) {
|
|
|
|
*conn_flags |=
|
|
|
|
ieee80211_chandef_downgrade(&chandef);
|
|
|
|
ret = ieee80211_link_use_channel(link, &chandef,
|
|
|
|
IEEE80211_CHANCTX_SHARED);
|
2022-07-12 17:13:56 +08:00
|
|
|
}
|
2022-07-12 20:59:22 +08:00
|
|
|
out:
|
|
|
|
mutex_unlock(&local->mtx);
|
|
|
|
return ret;
|
|
|
|
}
|
2013-08-23 21:48:48 +08:00
|
|
|
|
2022-09-02 22:12:53 +08:00
|
|
|
static bool ieee80211_get_dtim(const struct cfg80211_bss_ies *ies,
|
|
|
|
u8 *dtim_count, u8 *dtim_period)
|
|
|
|
{
|
|
|
|
const u8 *tim_ie = cfg80211_find_ie(WLAN_EID_TIM, ies->data, ies->len);
|
|
|
|
const u8 *idx_ie = cfg80211_find_ie(WLAN_EID_MULTI_BSSID_IDX, ies->data,
|
|
|
|
ies->len);
|
|
|
|
const struct ieee80211_tim_ie *tim = NULL;
|
|
|
|
const struct ieee80211_bssid_index *idx;
|
|
|
|
bool valid = tim_ie && tim_ie[1] >= 2;
|
|
|
|
|
|
|
|
if (valid)
|
|
|
|
tim = (void *)(tim_ie + 2);
|
|
|
|
|
|
|
|
if (dtim_count)
|
|
|
|
*dtim_count = valid ? tim->dtim_count : 0;
|
|
|
|
|
|
|
|
if (dtim_period)
|
|
|
|
*dtim_period = valid ? tim->dtim_period : 0;
|
|
|
|
|
|
|
|
/* Check if value is overridden by non-transmitted profile */
|
|
|
|
if (!idx_ie || idx_ie[1] < 3)
|
|
|
|
return valid;
|
|
|
|
|
|
|
|
idx = (void *)(idx_ie + 2);
|
|
|
|
|
|
|
|
if (dtim_count)
|
|
|
|
*dtim_count = idx->dtim_count;
|
|
|
|
|
|
|
|
if (dtim_period)
|
|
|
|
*dtim_period = idx->dtim_period;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-07-12 20:59:22 +08:00
|
|
|
static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct ieee80211_mgmt *mgmt,
|
|
|
|
struct ieee802_11_elems *elems,
|
|
|
|
const u8 *elem_start, unsigned int elem_len)
|
|
|
|
{
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2022-06-02 03:17:34 +08:00
|
|
|
struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data;
|
2022-07-12 20:59:22 +08:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
2022-06-02 03:17:34 +08:00
|
|
|
unsigned int link_id;
|
2022-07-12 20:59:22 +08:00
|
|
|
struct sta_info *sta;
|
2022-06-02 03:17:34 +08:00
|
|
|
u64 changed[IEEE80211_MLD_MAX_NUM_LINKS] = {};
|
2022-09-08 16:31:10 +08:00
|
|
|
u16 valid_links = 0;
|
2022-07-12 20:59:22 +08:00
|
|
|
int err;
|
2012-07-06 23:37:43 +08:00
|
|
|
|
2022-07-12 20:59:22 +08:00
|
|
|
mutex_lock(&sdata->local->sta_mtx);
|
2012-12-12 16:12:24 +08:00
|
|
|
/*
|
2022-07-12 20:59:22 +08:00
|
|
|
* station info was already allocated and inserted before
|
|
|
|
* the association and should be available to us
|
2012-12-12 16:12:24 +08:00
|
|
|
*/
|
2022-06-02 03:17:34 +08:00
|
|
|
sta = sta_info_get(sdata, assoc_data->ap_addr);
|
2022-07-12 20:59:22 +08:00
|
|
|
if (WARN_ON(!sta))
|
|
|
|
goto out_err;
|
2013-02-06 00:48:40 +08:00
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
if (sdata->vif.valid_links) {
|
|
|
|
for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {
|
|
|
|
if (!assoc_data->link[link_id].bss)
|
|
|
|
continue;
|
|
|
|
valid_links |= BIT(link_id);
|
|
|
|
|
|
|
|
if (link_id != assoc_data->assoc_link_id) {
|
|
|
|
err = ieee80211_sta_allocate_link(sta, link_id);
|
|
|
|
if (err)
|
|
|
|
goto out_err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ieee80211_vif_set_links(sdata, valid_links);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {
|
2022-09-15 20:57:47 +08:00
|
|
|
struct cfg80211_bss *cbss = assoc_data->link[link_id].bss;
|
2022-06-02 03:17:34 +08:00
|
|
|
struct ieee80211_link_data *link;
|
|
|
|
struct link_sta_info *link_sta;
|
|
|
|
|
2022-09-15 20:57:47 +08:00
|
|
|
if (!cbss)
|
2022-06-02 03:17:34 +08:00
|
|
|
continue;
|
|
|
|
|
|
|
|
link = sdata_dereference(sdata->link[link_id], sdata);
|
|
|
|
if (WARN_ON(!link))
|
|
|
|
goto out_err;
|
|
|
|
|
|
|
|
if (sdata->vif.valid_links)
|
|
|
|
link_info(link,
|
2022-09-21 04:43:53 +08:00
|
|
|
"local address %pM, AP link address %pM%s\n",
|
2022-06-02 03:17:34 +08:00
|
|
|
link->conf->addr,
|
2022-09-21 04:43:53 +08:00
|
|
|
assoc_data->link[link_id].bss->bssid,
|
|
|
|
link_id == assoc_data->assoc_link_id ?
|
|
|
|
" (assoc)" : "");
|
2022-06-02 03:17:34 +08:00
|
|
|
|
|
|
|
link_sta = rcu_dereference_protected(sta->link[link_id],
|
|
|
|
lockdep_is_held(&local->sta_mtx));
|
|
|
|
if (WARN_ON(!link_sta))
|
|
|
|
goto out_err;
|
|
|
|
|
2022-09-15 20:57:47 +08:00
|
|
|
if (!link->u.mgd.have_beacon) {
|
2022-09-02 22:12:53 +08:00
|
|
|
const struct cfg80211_bss_ies *ies;
|
|
|
|
|
|
|
|
rcu_read_lock();
|
2022-09-15 20:57:47 +08:00
|
|
|
ies = rcu_dereference(cbss->beacon_ies);
|
|
|
|
if (ies)
|
|
|
|
link->u.mgd.have_beacon = true;
|
|
|
|
else
|
|
|
|
ies = rcu_dereference(cbss->ies);
|
2022-09-02 22:12:53 +08:00
|
|
|
ieee80211_get_dtim(ies,
|
|
|
|
&link->conf->sync_dtim_count,
|
|
|
|
&link->u.mgd.dtim_period);
|
|
|
|
link->conf->beacon_int = cbss->beacon_interval;
|
|
|
|
rcu_read_unlock();
|
2022-09-15 20:57:47 +08:00
|
|
|
}
|
2022-09-02 22:12:53 +08:00
|
|
|
|
2022-09-15 20:57:47 +08:00
|
|
|
link->conf->dtim_period = link->u.mgd.dtim_period ?: 1;
|
|
|
|
|
|
|
|
if (link_id != assoc_data->assoc_link_id) {
|
2022-09-02 22:12:53 +08:00
|
|
|
err = ieee80211_prep_channel(sdata, link, cbss,
|
2022-06-02 03:17:34 +08:00
|
|
|
&link->u.mgd.conn_flags);
|
2022-09-02 22:12:42 +08:00
|
|
|
if (err) {
|
|
|
|
link_info(link, "prep_channel failed\n");
|
2022-06-02 03:17:34 +08:00
|
|
|
goto out_err;
|
2022-09-02 22:12:42 +08:00
|
|
|
}
|
2022-06-02 03:17:34 +08:00
|
|
|
}
|
|
|
|
|
2022-07-19 18:06:17 +08:00
|
|
|
err = ieee80211_mgd_setup_link_sta(link, sta, link_sta,
|
2022-06-02 03:17:34 +08:00
|
|
|
assoc_data->link[link_id].bss);
|
|
|
|
if (err)
|
|
|
|
goto out_err;
|
|
|
|
|
|
|
|
if (!ieee80211_assoc_config_link(link, link_sta,
|
|
|
|
assoc_data->link[link_id].bss,
|
|
|
|
mgmt, elem_start, elem_len,
|
|
|
|
&changed[link_id]))
|
|
|
|
goto out_err;
|
|
|
|
|
2022-09-08 16:31:10 +08:00
|
|
|
if (assoc_data->link[link_id].status != WLAN_STATUS_SUCCESS) {
|
|
|
|
valid_links &= ~BIT(link_id);
|
|
|
|
ieee80211_sta_remove_link(sta, link_id);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
if (link_id != assoc_data->assoc_link_id) {
|
|
|
|
err = ieee80211_sta_activate_link(sta, link_id);
|
|
|
|
if (err)
|
|
|
|
goto out_err;
|
|
|
|
}
|
|
|
|
}
|
2013-06-03 22:29:33 +08:00
|
|
|
|
2022-09-08 16:31:10 +08:00
|
|
|
/* links might have changed due to rejected ones, set them again */
|
|
|
|
ieee80211_vif_set_links(sdata, valid_links);
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
rate_control_rate_init(sta);
|
2013-06-03 22:29:33 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED) {
|
|
|
|
set_sta_flag(sta, WLAN_STA_MFP);
|
|
|
|
sta->sta.mfp = true;
|
|
|
|
} else {
|
|
|
|
sta->sta.mfp = false;
|
2012-12-12 16:12:24 +08:00
|
|
|
}
|
|
|
|
|
2022-07-14 00:08:03 +08:00
|
|
|
ieee80211_sta_set_max_amsdu_subframes(sta, elems->ext_capab,
|
|
|
|
elems->ext_capab_len);
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
sta->sta.wme = (elems->wmm_param || elems->s1g_capab) &&
|
|
|
|
local->hw.queues >= IEEE80211_NUM_ACS;
|
|
|
|
|
|
|
|
err = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
|
|
|
|
if (!err && !(ifmgd->flags & IEEE80211_STA_CONTROL_PORT))
|
|
|
|
err = sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
|
|
|
|
if (err) {
|
|
|
|
sdata_info(sdata,
|
|
|
|
"failed to move station %pM to desired state\n",
|
|
|
|
sta->sta.addr);
|
|
|
|
WARN_ON(__sta_info_destroy(sta));
|
2022-07-12 20:59:22 +08:00
|
|
|
goto out_err;
|
2008-04-16 02:09:27 +08:00
|
|
|
}
|
2020-09-22 10:28:14 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (sdata->wdev.use_4addr)
|
|
|
|
drv_sta_set_4addr(local, sdata, &sta->sta, true);
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
mutex_unlock(&sdata->local->sta_mtx);
|
2013-02-08 05:24:55 +08:00
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
ieee80211_set_associated(sdata, assoc_data, changed);
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2022-07-12 17:13:56 +08: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);
|
2020-09-22 10:28:14 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
/*
|
|
|
|
* Start timer to probe the connection to the AP now.
|
|
|
|
* Also start the timer that will detect beacon loss.
|
|
|
|
*/
|
|
|
|
ieee80211_sta_reset_beacon_monitor(sdata);
|
|
|
|
ieee80211_sta_reset_conn_monitor(sdata);
|
2020-09-22 10:28:14 +08:00
|
|
|
|
2022-07-12 20:59:22 +08:00
|
|
|
return true;
|
|
|
|
out_err:
|
2022-07-15 21:37:23 +08:00
|
|
|
eth_zero_addr(sdata->vif.cfg.ap_addr);
|
2022-07-12 20:59:22 +08:00
|
|
|
mutex_unlock(&sdata->local->sta_mtx);
|
|
|
|
return false;
|
2020-09-22 10:28:14 +08:00
|
|
|
}
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct ieee80211_mgmt *mgmt,
|
|
|
|
size_t len)
|
2007-05-06 02:45:53 +08:00
|
|
|
{
|
2022-07-12 17:13:56 +08: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;
|
2022-08-16 17:26:23 +08:00
|
|
|
struct ieee80211_elems_parse_params parse_params = {
|
|
|
|
.bss = NULL,
|
|
|
|
.link_id = -1,
|
|
|
|
.from_ap = true,
|
|
|
|
};
|
2022-07-12 17:13:56 +08:00
|
|
|
struct ieee802_11_elems *elems;
|
|
|
|
int ac;
|
2022-07-12 20:59:22 +08:00
|
|
|
const u8 *elem_start;
|
|
|
|
unsigned int elem_len;
|
2022-07-12 17:13:56 +08:00
|
|
|
bool reassoc;
|
|
|
|
struct ieee80211_event event = {
|
|
|
|
.type = MLME_EVENT,
|
|
|
|
.u.mlme.data = ASSOC_EVENT,
|
|
|
|
};
|
|
|
|
struct ieee80211_prep_tx_info info = {};
|
|
|
|
struct cfg80211_rx_assoc_resp resp = {
|
|
|
|
.uapsd_queues = -1,
|
|
|
|
};
|
2022-09-21 08:44:58 +08:00
|
|
|
u8 ap_mld_addr[ETH_ALEN] __aligned(2);
|
2022-06-02 03:17:34 +08:00
|
|
|
unsigned int link_id;
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
sdata_assert_lock(sdata);
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (!assoc_data)
|
|
|
|
return;
|
2009-07-07 09:45:17 +08:00
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
if (!ether_addr_equal(assoc_data->ap_addr, mgmt->bssid) ||
|
|
|
|
!ether_addr_equal(assoc_data->ap_addr, mgmt->sa))
|
2022-07-12 17:13:56 +08:00
|
|
|
return;
|
mac80211: always allocate struct ieee802_11_elems
As the 802.11 spec evolves, we need to parse more and more
elements. This is causing the struct to grow, and we can no
longer get away with putting it on the stack.
Change the API to always dynamically allocate and return an
allocated pointer that must be kfree()d later.
As an alternative, I contemplated a scheme whereby we'd say
in the code which elements we needed, e.g.
DECLARE_ELEMENT_PARSER(elems,
SUPPORTED_CHANNELS,
CHANNEL_SWITCH,
EXT(KEY_DELIVERY));
ieee802_11_parse_elems(..., &elems, ...);
and while I think this is possible and will save us a lot
since most individual places only care about a small subset
of the elements, it ended up being a bit more work since a
lot of places do the parsing and then pass the struct to
other functions, sometimes with multiple levels.
Link: https://lore.kernel.org/r/20210920154009.26caff6b5998.I05ae58768e990e611aee8eca8abefd9d7bc15e05@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2021-09-20 21:40:10 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
/*
|
|
|
|
* AssocResp and ReassocResp have identical structure, so process both
|
|
|
|
* of them in this function.
|
|
|
|
*/
|
2013-03-26 21:02:26 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (len < 24 + 6)
|
|
|
|
return;
|
2013-03-26 21:02:26 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
reassoc = ieee80211_is_reassoc_resp(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);
|
2022-06-02 03:17:34 +08:00
|
|
|
if (assoc_data->s1g)
|
2022-07-12 20:59:22 +08:00
|
|
|
elem_start = mgmt->u.s1g_assoc_resp.variable;
|
|
|
|
else
|
|
|
|
elem_start = mgmt->u.assoc_resp.variable;
|
mac80211: always allocate struct ieee802_11_elems
As the 802.11 spec evolves, we need to parse more and more
elements. This is causing the struct to grow, and we can no
longer get away with putting it on the stack.
Change the API to always dynamically allocate and return an
allocated pointer that must be kfree()d later.
As an alternative, I contemplated a scheme whereby we'd say
in the code which elements we needed, e.g.
DECLARE_ELEMENT_PARSER(elems,
SUPPORTED_CHANNELS,
CHANNEL_SWITCH,
EXT(KEY_DELIVERY));
ieee802_11_parse_elems(..., &elems, ...);
and while I think this is possible and will save us a lot
since most individual places only care about a small subset
of the elements, it ended up being a bit more work since a
lot of places do the parsing and then pass the struct to
other functions, sometimes with multiple levels.
Link: https://lore.kernel.org/r/20210920154009.26caff6b5998.I05ae58768e990e611aee8eca8abefd9d7bc15e05@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2021-09-20 21:40:10 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
/*
|
|
|
|
* Note: this may not be perfect, AP might misbehave - if
|
|
|
|
* anyone needs to rely on perfect complete notification
|
|
|
|
* with the exact right subtype, then we need to track what
|
|
|
|
* we actually transmitted.
|
|
|
|
*/
|
|
|
|
info.subtype = reassoc ? IEEE80211_STYPE_REASSOC_REQ :
|
|
|
|
IEEE80211_STYPE_ASSOC_REQ;
|
2013-03-26 22:17:18 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (assoc_data->fils_kek_len &&
|
|
|
|
fils_decrypt_assoc_resp(sdata, (u8 *)mgmt, &len, assoc_data) < 0)
|
|
|
|
return;
|
2013-03-26 22:17:18 +08:00
|
|
|
|
2022-07-12 20:59:22 +08:00
|
|
|
elem_len = len - (elem_start - (u8 *)mgmt);
|
2022-08-16 17:26:23 +08:00
|
|
|
parse_params.start = elem_start;
|
|
|
|
parse_params.len = elem_len;
|
|
|
|
elems = ieee802_11_parse_elems_full(&parse_params);
|
2022-07-12 17:13:56 +08:00
|
|
|
if (!elems)
|
|
|
|
goto notify_driver;
|
2013-03-26 22:17:18 +08:00
|
|
|
|
2022-07-12 20:59:22 +08:00
|
|
|
if (elems->aid_resp)
|
|
|
|
aid = le16_to_cpu(elems->aid_resp->aid);
|
2022-06-02 03:17:34 +08:00
|
|
|
else if (assoc_data->s1g)
|
2022-07-12 20:59:22 +08:00
|
|
|
aid = 0; /* TODO */
|
|
|
|
else
|
|
|
|
aid = le16_to_cpu(mgmt->u.assoc_resp.aid);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The 5 MSB of the AID field are reserved
|
|
|
|
* (802.11-2016 9.4.1.8 AID field)
|
|
|
|
*/
|
|
|
|
aid &= 0x7ff;
|
|
|
|
|
|
|
|
sdata_info(sdata,
|
|
|
|
"RX %sssocResp from %pM (capab=0x%x status=%d aid=%d)\n",
|
2022-06-02 03:17:34 +08:00
|
|
|
reassoc ? "Rea" : "A", assoc_data->ap_addr,
|
2022-07-12 20:59:22 +08:00
|
|
|
capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14))));
|
|
|
|
|
|
|
|
ifmgd->broken_ap = false;
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY &&
|
|
|
|
elems->timeout_int &&
|
|
|
|
elems->timeout_int->type == WLAN_TIMEOUT_ASSOC_COMEBACK) {
|
|
|
|
u32 tu, ms;
|
2013-03-26 22:17:18 +08:00
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
cfg80211_assoc_comeback(sdata->dev, assoc_data->ap_addr,
|
2022-07-12 17:13:56 +08:00
|
|
|
le32_to_cpu(elems->timeout_int->value));
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
tu = le32_to_cpu(elems->timeout_int->value);
|
|
|
|
ms = tu * 1024 / 1000;
|
|
|
|
sdata_info(sdata,
|
|
|
|
"%pM rejected association temporarily; comeback duration %u TU (%u ms)\n",
|
2022-06-02 03:17:34 +08:00
|
|
|
assoc_data->ap_addr, tu, ms);
|
2022-07-12 17:13:56 +08:00
|
|
|
assoc_data->timeout = jiffies + msecs_to_jiffies(ms);
|
|
|
|
assoc_data->timeout_started = true;
|
|
|
|
if (ms > IEEE80211_ASSOC_TIMEOUT)
|
|
|
|
run_again(sdata, assoc_data->timeout);
|
|
|
|
goto notify_driver;
|
|
|
|
}
|
2009-05-17 17:40:42 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (status_code != WLAN_STATUS_SUCCESS) {
|
|
|
|
sdata_info(sdata, "%pM denied association (code=%d)\n",
|
2022-06-02 03:17:34 +08:00
|
|
|
assoc_data->ap_addr, status_code);
|
2022-07-12 17:13:56 +08:00
|
|
|
event.u.mlme.status = MLME_DENIED;
|
|
|
|
event.u.mlme.reason = status_code;
|
|
|
|
drv_event_callback(sdata->local, sdata, &event);
|
|
|
|
} else {
|
2022-07-12 20:59:22 +08:00
|
|
|
if (aid == 0 || aid > IEEE80211_MAX_AID) {
|
|
|
|
sdata_info(sdata,
|
|
|
|
"invalid AID value %d (out of range), turn off PS\n",
|
|
|
|
aid);
|
|
|
|
aid = 0;
|
|
|
|
ifmgd->broken_ap = true;
|
|
|
|
}
|
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
if (sdata->vif.valid_links) {
|
|
|
|
if (!elems->multi_link) {
|
|
|
|
sdata_info(sdata,
|
|
|
|
"MLO association with %pM but no multi-link element in response!\n",
|
|
|
|
assoc_data->ap_addr);
|
|
|
|
goto abandon_assoc;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (le16_get_bits(elems->multi_link->control,
|
|
|
|
IEEE80211_ML_CONTROL_TYPE) !=
|
|
|
|
IEEE80211_ML_CONTROL_TYPE_BASIC) {
|
|
|
|
sdata_info(sdata,
|
|
|
|
"bad multi-link element (control=0x%x)\n",
|
|
|
|
le16_to_cpu(elems->multi_link->control));
|
|
|
|
goto abandon_assoc;
|
|
|
|
} else {
|
|
|
|
struct ieee80211_mle_basic_common_info *common;
|
|
|
|
|
|
|
|
common = (void *)elems->multi_link->variable;
|
|
|
|
|
|
|
|
if (memcmp(assoc_data->ap_addr,
|
|
|
|
common->mld_mac_addr, ETH_ALEN)) {
|
|
|
|
sdata_info(sdata,
|
|
|
|
"AP MLD MAC address mismatch: got %pM expected %pM\n",
|
|
|
|
common->mld_mac_addr,
|
|
|
|
assoc_data->ap_addr);
|
|
|
|
goto abandon_assoc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-12 20:59:22 +08:00
|
|
|
sdata->vif.cfg.aid = aid;
|
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
if (!ieee80211_assoc_success(sdata, mgmt, elems,
|
2022-07-12 20:59:22 +08:00
|
|
|
elem_start, elem_len)) {
|
2022-07-12 17:13:56 +08:00
|
|
|
/* oops -- internal error -- send timeout for now */
|
|
|
|
ieee80211_destroy_assoc_data(sdata, ASSOC_TIMEOUT);
|
|
|
|
goto notify_driver;
|
|
|
|
}
|
|
|
|
event.u.mlme.status = MLME_SUCCESS;
|
|
|
|
drv_event_callback(sdata->local, sdata, &event);
|
|
|
|
sdata_info(sdata, "associated\n");
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
info.success = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) {
|
|
|
|
struct ieee80211_link_data *link;
|
|
|
|
|
|
|
|
link = sdata_dereference(sdata->link[link_id], sdata);
|
|
|
|
if (!link)
|
|
|
|
continue;
|
2022-09-08 16:31:10 +08:00
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
if (!assoc_data->link[link_id].bss)
|
|
|
|
continue;
|
2022-09-08 16:31:10 +08:00
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
resp.links[link_id].bss = assoc_data->link[link_id].bss;
|
|
|
|
resp.links[link_id].addr = link->conf->addr;
|
2022-09-08 16:31:10 +08:00
|
|
|
resp.links[link_id].status = assoc_data->link[link_id].status;
|
2010-12-03 04:01:08 +08:00
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
/* get uapsd queues configuration - same for all links */
|
2022-07-12 17:13:56 +08:00
|
|
|
resp.uapsd_queues = 0;
|
|
|
|
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
|
2022-06-02 03:17:34 +08:00
|
|
|
if (link->tx_conf[ac].uapsd)
|
2022-07-12 17:13:56 +08:00
|
|
|
resp.uapsd_queues |= ieee80211_ac_to_qos_mask[ac];
|
|
|
|
}
|
|
|
|
|
2022-09-21 08:44:58 +08:00
|
|
|
if (sdata->vif.valid_links) {
|
|
|
|
ether_addr_copy(ap_mld_addr, sdata->vif.cfg.ap_addr);
|
|
|
|
resp.ap_mld_addr = ap_mld_addr;
|
|
|
|
}
|
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
ieee80211_destroy_assoc_data(sdata,
|
|
|
|
status_code == WLAN_STATUS_SUCCESS ?
|
|
|
|
ASSOC_SUCCESS :
|
|
|
|
ASSOC_REJECTED);
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
resp.buf = (u8 *)mgmt;
|
|
|
|
resp.len = len;
|
|
|
|
resp.req_ies = ifmgd->assoc_req_ies;
|
|
|
|
resp.req_ies_len = ifmgd->assoc_req_ies_len;
|
|
|
|
cfg80211_rx_assoc_resp(sdata->dev, &resp);
|
|
|
|
notify_driver:
|
|
|
|
drv_mgd_complete_tx(sdata->local, sdata, &info);
|
|
|
|
kfree(elems);
|
2022-06-02 03:17:34 +08:00
|
|
|
return;
|
|
|
|
abandon_assoc:
|
|
|
|
ieee80211_destroy_assoc_data(sdata, ASSOC_ABANDON);
|
|
|
|
goto notify_driver;
|
2010-12-03 04:01:08 +08:00
|
|
|
}
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
static void ieee80211_rx_bss_info(struct ieee80211_link_data *link,
|
|
|
|
struct ieee80211_mgmt *mgmt, size_t len,
|
|
|
|
struct ieee80211_rx_status *rx_status)
|
2012-01-20 20:55:27 +08:00
|
|
|
{
|
2022-07-12 17:13:56 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = link->sdata;
|
2012-01-20 20:55:27 +08:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
2022-07-12 17:13:56 +08:00
|
|
|
struct ieee80211_bss *bss;
|
|
|
|
struct ieee80211_channel *channel;
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2013-05-10 18:32:47 +08:00
|
|
|
sdata_assert_lock(sdata);
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
channel = ieee80211_get_channel_khz(local->hw.wiphy,
|
|
|
|
ieee80211_rx_status_to_khz(rx_status));
|
|
|
|
if (!channel)
|
|
|
|
return;
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, channel);
|
|
|
|
if (bss) {
|
|
|
|
link->conf->beacon_rate = bss->beacon_rate;
|
|
|
|
ieee80211_rx_bss_put(local, bss);
|
2012-01-20 20:55:27 +08:00
|
|
|
}
|
2022-07-12 17:13:56 +08:00
|
|
|
}
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2012-06-27 19:18:36 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_link_data *link,
|
|
|
|
struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata = link->sdata;
|
|
|
|
struct ieee80211_mgmt *mgmt = (void *)skb->data;
|
|
|
|
struct ieee80211_if_managed *ifmgd;
|
|
|
|
struct ieee80211_rx_status *rx_status = (void *) skb->cb;
|
|
|
|
struct ieee80211_channel *channel;
|
|
|
|
size_t baselen, len = skb->len;
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
ifmgd = &sdata->u.mgd;
|
2012-10-01 00:29:40 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
sdata_assert_lock(sdata);
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
/*
|
|
|
|
* According to Draft P802.11ax D6.0 clause 26.17.2.3.2:
|
|
|
|
* "If a 6 GHz AP receives a Probe Request frame and responds with
|
|
|
|
* a Probe Response frame [..], the Address 1 field of the Probe
|
|
|
|
* Response frame shall be set to the broadcast address [..]"
|
|
|
|
* So, on 6GHz band we should also accept broadcast responses.
|
|
|
|
*/
|
|
|
|
channel = ieee80211_get_channel(sdata->local->hw.wiphy,
|
|
|
|
rx_status->freq);
|
|
|
|
if (!channel)
|
|
|
|
return;
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (!ether_addr_equal(mgmt->da, sdata->vif.addr) &&
|
|
|
|
(channel->band != NL80211_BAND_6GHZ ||
|
|
|
|
!is_broadcast_ether_addr(mgmt->da)))
|
|
|
|
return; /* ignore ProbeResp to foreign address */
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
baselen = (u8 *) mgmt->u.probe_resp.variable - (u8 *) mgmt;
|
|
|
|
if (baselen > len)
|
|
|
|
return;
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
ieee80211_rx_bss_info(link, mgmt, len, rx_status);
|
2018-04-20 18:49:20 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (ifmgd->associated &&
|
|
|
|
ether_addr_equal(mgmt->bssid, link->u.mgd.bssid))
|
|
|
|
ieee80211_reset_ap_probe(sdata);
|
2012-01-20 20:55:27 +08:00
|
|
|
}
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
/*
|
|
|
|
* This is the canonical list of information elements we care about,
|
|
|
|
* the filter code also gives us all changes to the Microsoft OUI
|
|
|
|
* (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.
|
|
|
|
*
|
|
|
|
* 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 =
|
|
|
|
(1ULL << WLAN_EID_COUNTRY) |
|
|
|
|
(1ULL << WLAN_EID_ERP_INFO) |
|
|
|
|
(1ULL << WLAN_EID_CHANNEL_SWITCH) |
|
|
|
|
(1ULL << WLAN_EID_PWR_CONSTRAINT) |
|
|
|
|
(1ULL << WLAN_EID_HT_CAPABILITY) |
|
|
|
|
(1ULL << WLAN_EID_HT_OPERATION) |
|
|
|
|
(1ULL << WLAN_EID_EXT_CHANSWITCH_ANN);
|
|
|
|
|
|
|
|
static void ieee80211_handle_beacon_sig(struct ieee80211_link_data *link,
|
|
|
|
struct ieee80211_if_managed *ifmgd,
|
|
|
|
struct ieee80211_bss_conf *bss_conf,
|
|
|
|
struct ieee80211_local *local,
|
|
|
|
struct ieee80211_rx_status *rx_status)
|
2012-01-20 20:55:27 +08:00
|
|
|
{
|
2022-07-12 17:13:56 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = link->sdata;
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
/* Track average RSSI from the Beacon frames of the current AP */
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (!link->u.mgd.tracking_signal_avg) {
|
|
|
|
link->u.mgd.tracking_signal_avg = true;
|
|
|
|
ewma_beacon_signal_init(&link->u.mgd.ave_beacon_signal);
|
|
|
|
link->u.mgd.last_cqm_event_signal = 0;
|
|
|
|
link->u.mgd.count_beacon_signal = 1;
|
|
|
|
link->u.mgd.last_ave_beacon_signal = 0;
|
|
|
|
} else {
|
|
|
|
link->u.mgd.count_beacon_signal++;
|
|
|
|
}
|
|
|
|
|
|
|
|
ewma_beacon_signal_add(&link->u.mgd.ave_beacon_signal,
|
|
|
|
-rx_status->signal);
|
|
|
|
|
|
|
|
if (ifmgd->rssi_min_thold != ifmgd->rssi_max_thold &&
|
|
|
|
link->u.mgd.count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT) {
|
|
|
|
int sig = -ewma_beacon_signal_read(&link->u.mgd.ave_beacon_signal);
|
|
|
|
int last_sig = link->u.mgd.last_ave_beacon_signal;
|
|
|
|
struct ieee80211_event event = {
|
|
|
|
.type = RSSI_EVENT,
|
|
|
|
};
|
2012-01-20 20:55:27 +08:00
|
|
|
|
|
|
|
/*
|
2022-07-12 17:13:56 +08:00
|
|
|
* if signal crosses either of the boundaries, invoke callback
|
|
|
|
* with appropriate parameters
|
2012-01-20 20:55:27 +08:00
|
|
|
*/
|
2022-07-12 17:13:56 +08:00
|
|
|
if (sig > ifmgd->rssi_max_thold &&
|
|
|
|
(last_sig <= ifmgd->rssi_min_thold || last_sig == 0)) {
|
|
|
|
link->u.mgd.last_ave_beacon_signal = sig;
|
|
|
|
event.u.rssi.data = RSSI_EVENT_HIGH;
|
|
|
|
drv_event_callback(local, sdata, &event);
|
|
|
|
} else if (sig < ifmgd->rssi_min_thold &&
|
|
|
|
(last_sig >= ifmgd->rssi_max_thold ||
|
|
|
|
last_sig == 0)) {
|
|
|
|
link->u.mgd.last_ave_beacon_signal = sig;
|
|
|
|
event.u.rssi.data = RSSI_EVENT_LOW;
|
|
|
|
drv_event_callback(local, sdata, &event);
|
|
|
|
}
|
|
|
|
}
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (bss_conf->cqm_rssi_thold &&
|
|
|
|
link->u.mgd.count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT &&
|
|
|
|
!(sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI)) {
|
|
|
|
int sig = -ewma_beacon_signal_read(&link->u.mgd.ave_beacon_signal);
|
|
|
|
int last_event = link->u.mgd.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)) {
|
|
|
|
link->u.mgd.last_cqm_event_signal = sig;
|
|
|
|
ieee80211_cqm_rssi_notify(
|
|
|
|
&sdata->vif,
|
|
|
|
NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
|
|
|
|
sig, GFP_KERNEL);
|
|
|
|
} else if (sig > thold &&
|
|
|
|
(last_event == 0 || sig > last_event + hyst)) {
|
|
|
|
link->u.mgd.last_cqm_event_signal = sig;
|
|
|
|
ieee80211_cqm_rssi_notify(
|
|
|
|
&sdata->vif,
|
|
|
|
NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
|
|
|
|
sig, GFP_KERNEL);
|
|
|
|
}
|
2012-01-20 20:55:27 +08:00
|
|
|
}
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (bss_conf->cqm_rssi_low &&
|
|
|
|
link->u.mgd.count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT) {
|
|
|
|
int sig = -ewma_beacon_signal_read(&link->u.mgd.ave_beacon_signal);
|
|
|
|
int last_event = link->u.mgd.last_cqm_event_signal;
|
|
|
|
int low = bss_conf->cqm_rssi_low;
|
|
|
|
int high = bss_conf->cqm_rssi_high;
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (sig < low &&
|
|
|
|
(last_event == 0 || last_event >= low)) {
|
|
|
|
link->u.mgd.last_cqm_event_signal = sig;
|
|
|
|
ieee80211_cqm_rssi_notify(
|
|
|
|
&sdata->vif,
|
|
|
|
NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
|
|
|
|
sig, GFP_KERNEL);
|
|
|
|
} else if (sig > high &&
|
|
|
|
(last_event == 0 || last_event <= high)) {
|
|
|
|
link->u.mgd.last_cqm_event_signal = sig;
|
|
|
|
ieee80211_cqm_rssi_notify(
|
|
|
|
&sdata->vif,
|
|
|
|
NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
|
|
|
|
sig, GFP_KERNEL);
|
|
|
|
}
|
2013-01-29 22:02:27 +08:00
|
|
|
}
|
2012-01-20 20:55:27 +08:00
|
|
|
}
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
static bool ieee80211_rx_our_beacon(const u8 *tx_bssid,
|
|
|
|
struct cfg80211_bss *bss)
|
2013-01-29 22:02:27 +08:00
|
|
|
{
|
2022-07-12 17:13:56 +08:00
|
|
|
if (ether_addr_equal(tx_bssid, bss->bssid))
|
|
|
|
return true;
|
|
|
|
if (!bss->transmitted_bss)
|
|
|
|
return false;
|
|
|
|
return ether_addr_equal(tx_bssid, bss->transmitted_bss->bssid);
|
2013-01-29 22:02:27 +08:00
|
|
|
}
|
|
|
|
|
2023-01-27 19:39:31 +08:00
|
|
|
static bool ieee80211_config_puncturing(struct ieee80211_link_data *link,
|
|
|
|
const struct ieee80211_eht_operation *eht_oper,
|
|
|
|
u64 *changed)
|
|
|
|
{
|
|
|
|
u16 bitmap = 0, extracted;
|
|
|
|
|
|
|
|
if ((eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT) &&
|
|
|
|
(eht_oper->params &
|
|
|
|
IEEE80211_EHT_OPER_DISABLED_SUBCHANNEL_BITMAP_PRESENT)) {
|
|
|
|
const struct ieee80211_eht_operation_info *info =
|
|
|
|
(void *)eht_oper->optional;
|
|
|
|
const u8 *disable_subchannel_bitmap = info->optional;
|
|
|
|
|
|
|
|
bitmap = get_unaligned_le16(disable_subchannel_bitmap);
|
|
|
|
}
|
|
|
|
|
|
|
|
extracted = ieee80211_extract_dis_subch_bmap(eht_oper,
|
|
|
|
&link->conf->chandef,
|
|
|
|
bitmap);
|
|
|
|
|
|
|
|
/* accept if there are no changes */
|
|
|
|
if (!(*changed & BSS_CHANGED_BANDWIDTH) &&
|
|
|
|
extracted == link->conf->eht_puncturing)
|
|
|
|
return true;
|
|
|
|
|
2023-01-31 08:12:24 +08:00
|
|
|
if (!cfg80211_valid_disable_subchannel_bitmap(&bitmap,
|
|
|
|
&link->conf->chandef)) {
|
2023-01-27 19:39:31 +08:00
|
|
|
link_info(link,
|
|
|
|
"Got an invalid disable subchannel bitmap from AP %pM: bitmap = 0x%x, bw = 0x%x. disconnect\n",
|
|
|
|
link->u.mgd.bssid,
|
|
|
|
bitmap,
|
|
|
|
link->conf->chandef.width);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
ieee80211_handle_puncturing_bitmap(link, eht_oper, bitmap, changed);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
static void ieee80211_rx_mgmt_beacon(struct ieee80211_link_data *link,
|
|
|
|
struct ieee80211_hdr *hdr, size_t len,
|
|
|
|
struct ieee80211_rx_status *rx_status)
|
2008-09-11 06:01:52 +08:00
|
|
|
{
|
2022-07-12 17:13:56 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = link->sdata;
|
2010-06-10 16:21:32 +08:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2022-07-12 17:13:56 +08:00
|
|
|
struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
|
|
|
|
struct ieee80211_vif_cfg *vif_cfg = &sdata->vif.cfg;
|
|
|
|
struct ieee80211_mgmt *mgmt = (void *) hdr;
|
|
|
|
size_t baselen;
|
|
|
|
struct ieee802_11_elems *elems;
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
struct ieee80211_chanctx_conf *chanctx_conf;
|
2022-09-12 03:01:42 +08:00
|
|
|
struct ieee80211_supported_band *sband;
|
2022-07-12 17:13:56 +08:00
|
|
|
struct ieee80211_channel *chan;
|
|
|
|
struct link_sta_info *link_sta;
|
|
|
|
struct sta_info *sta;
|
2023-01-27 19:39:31 +08:00
|
|
|
u64 changed = 0;
|
2022-07-12 17:13:56 +08:00
|
|
|
bool erp_valid;
|
|
|
|
u8 erp_value = 0;
|
|
|
|
u32 ncrc = 0;
|
|
|
|
u8 *bssid, *variable = mgmt->u.beacon.variable;
|
|
|
|
u8 deauth_buf[IEEE80211_DEAUTH_FRAME_LEN];
|
2022-08-16 17:26:23 +08:00
|
|
|
struct ieee80211_elems_parse_params parse_params = {
|
|
|
|
.link_id = -1,
|
|
|
|
.from_ap = true,
|
|
|
|
};
|
2008-09-11 06:01:52 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
sdata_assert_lock(sdata);
|
2009-07-07 09:45:17 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
/* Process beacon from the current BSS */
|
|
|
|
bssid = ieee80211_get_bssid(hdr, len, sdata->vif.type);
|
|
|
|
if (ieee80211_is_s1g_beacon(mgmt->frame_control)) {
|
|
|
|
struct ieee80211_ext *ext = (void *) mgmt;
|
2013-01-29 22:02:27 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (ieee80211_is_s1g_short_beacon(ext->frame_control))
|
|
|
|
variable = ext->u.s1g_short_beacon.variable;
|
|
|
|
else
|
|
|
|
variable = ext->u.s1g_beacon.variable;
|
2013-01-29 22:02:27 +08:00
|
|
|
}
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
baselen = (u8 *) variable - (u8 *) mgmt;
|
|
|
|
if (baselen > len)
|
|
|
|
return;
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2022-08-16 17:26:23 +08:00
|
|
|
parse_params.start = variable;
|
|
|
|
parse_params.len = len - baselen;
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
rcu_read_lock();
|
|
|
|
chanctx_conf = rcu_dereference(link->conf->chanctx_conf);
|
|
|
|
if (!chanctx_conf) {
|
|
|
|
rcu_read_unlock();
|
|
|
|
return;
|
|
|
|
}
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (ieee80211_rx_status_to_khz(rx_status) !=
|
|
|
|
ieee80211_channel_to_khz(chanctx_conf->def.chan)) {
|
|
|
|
rcu_read_unlock();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
chan = chanctx_conf->def.chan;
|
|
|
|
rcu_read_unlock();
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (ifmgd->assoc_data && ifmgd->assoc_data->need_beacon &&
|
2022-06-02 03:17:34 +08:00
|
|
|
!WARN_ON(sdata->vif.valid_links) &&
|
|
|
|
ieee80211_rx_our_beacon(bssid, ifmgd->assoc_data->link[0].bss)) {
|
2022-08-16 17:26:23 +08:00
|
|
|
parse_params.bss = ifmgd->assoc_data->link[0].bss;
|
|
|
|
elems = ieee802_11_parse_elems_full(&parse_params);
|
2022-07-12 17:13:56 +08:00
|
|
|
if (!elems)
|
|
|
|
return;
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
ieee80211_rx_bss_info(link, mgmt, len, rx_status);
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (elems->dtim_period)
|
|
|
|
link->u.mgd.dtim_period = elems->dtim_period;
|
|
|
|
link->u.mgd.have_beacon = true;
|
|
|
|
ifmgd->assoc_data->need_beacon = false;
|
|
|
|
if (ieee80211_hw_check(&local->hw, TIMING_BEACON_ONLY)) {
|
|
|
|
link->conf->sync_tsf =
|
|
|
|
le64_to_cpu(mgmt->u.beacon.timestamp);
|
|
|
|
link->conf->sync_device_ts =
|
|
|
|
rx_status->device_timestamp;
|
|
|
|
link->conf->sync_dtim_count = elems->dtim_count;
|
2012-01-20 20:55:27 +08:00
|
|
|
}
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (elems->mbssid_config_ie)
|
|
|
|
bss_conf->profile_periodicity =
|
|
|
|
elems->mbssid_config_ie->profile_periodicity;
|
|
|
|
else
|
|
|
|
bss_conf->profile_periodicity = 0;
|
2009-07-31 23:54:12 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (elems->ext_capab_len >= 11 &&
|
|
|
|
(elems->ext_capab[10] & WLAN_EXT_CAPA11_EMA_SUPPORT))
|
|
|
|
bss_conf->ema_ap = true;
|
2010-11-23 10:10:32 +08:00
|
|
|
else
|
2022-07-12 17:13:56 +08:00
|
|
|
bss_conf->ema_ap = false;
|
|
|
|
|
|
|
|
/* continue assoc process */
|
|
|
|
ifmgd->assoc_data->timeout = jiffies;
|
|
|
|
ifmgd->assoc_data->timeout_started = true;
|
|
|
|
run_again(sdata, ifmgd->assoc_data->timeout);
|
|
|
|
kfree(elems);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ifmgd->associated ||
|
|
|
|
!ieee80211_rx_our_beacon(bssid, link->u.mgd.bss))
|
|
|
|
return;
|
|
|
|
bssid = link->u.mgd.bssid;
|
|
|
|
|
|
|
|
if (!(rx_status->flag & RX_FLAG_NO_SIGNAL_VAL))
|
|
|
|
ieee80211_handle_beacon_sig(link, ifmgd, bss_conf,
|
|
|
|
local, rx_status);
|
|
|
|
|
|
|
|
if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL) {
|
|
|
|
mlme_dbg_ratelimited(sdata,
|
|
|
|
"cancelling AP probe due to a received beacon\n");
|
|
|
|
ieee80211_reset_ap_probe(sdata);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Push the beacon loss detection into the future since
|
|
|
|
* we are processing a beacon from the AP just now.
|
|
|
|
*/
|
|
|
|
ieee80211_sta_reset_beacon_monitor(sdata);
|
|
|
|
|
|
|
|
/* TODO: CRC urrently not calculated on S1G Beacon Compatibility
|
|
|
|
* element (which carries the beacon interval). Don't forget to add a
|
|
|
|
* bit to care_about_ies[] above if mac80211 is interested in a
|
|
|
|
* changing S1G element.
|
|
|
|
*/
|
|
|
|
if (!ieee80211_is_s1g_beacon(hdr->frame_control))
|
|
|
|
ncrc = crc32_be(0, (void *)&mgmt->u.beacon.beacon_int, 4);
|
2022-08-16 17:26:23 +08:00
|
|
|
parse_params.bss = link->u.mgd.bss;
|
|
|
|
parse_params.filter = care_about_ies;
|
|
|
|
parse_params.crc = ncrc;
|
|
|
|
elems = ieee802_11_parse_elems_full(&parse_params);
|
2022-07-12 17:13:56 +08:00
|
|
|
if (!elems)
|
|
|
|
return;
|
|
|
|
ncrc = elems->crc;
|
|
|
|
|
|
|
|
if (ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK) &&
|
|
|
|
ieee80211_check_tim(elems->tim, elems->tim_len, vif_cfg->aid)) {
|
|
|
|
if (local->hw.conf.dynamic_ps_timeout > 0) {
|
|
|
|
if (local->hw.conf.flags & IEEE80211_CONF_PS) {
|
|
|
|
local->hw.conf.flags &= ~IEEE80211_CONF_PS;
|
|
|
|
ieee80211_hw_config(local,
|
|
|
|
IEEE80211_CONF_CHANGE_PS);
|
|
|
|
}
|
|
|
|
ieee80211_send_nullfunc(local, sdata, false);
|
|
|
|
} else if (!local->pspolling && sdata->u.mgd.powersave) {
|
|
|
|
local->pspolling = true;
|
2010-11-23 10:10:32 +08:00
|
|
|
|
2009-07-10 21:29:03 +08:00
|
|
|
/*
|
2022-07-12 17:13:56 +08:00
|
|
|
* 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.
|
2009-07-10 21:29:03 +08:00
|
|
|
*/
|
2022-07-12 17:13:56 +08:00
|
|
|
ieee80211_send_pspoll(local, sdata);
|
2009-07-10 21:29:03 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (sdata->vif.p2p ||
|
|
|
|
sdata->vif.driver_flags & IEEE80211_VIF_GET_NOA_UPDATE) {
|
|
|
|
struct ieee80211_p2p_noa_attr noa = {};
|
|
|
|
int ret;
|
2014-04-09 21:45:36 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
ret = cfg80211_get_p2p_attr(variable,
|
|
|
|
len - baselen,
|
|
|
|
IEEE80211_P2P_ATTR_ABSENCE_NOTICE,
|
|
|
|
(u8 *) &noa, sizeof(noa));
|
|
|
|
if (ret >= 2) {
|
|
|
|
if (link->u.mgd.p2p_noa_index != noa.index) {
|
|
|
|
/* valid noa_attr and index changed */
|
|
|
|
link->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.
|
|
|
|
*/
|
|
|
|
link->u.mgd.beacon_crc_valid = false;
|
|
|
|
}
|
|
|
|
} else if (link->u.mgd.p2p_noa_index != -1) {
|
|
|
|
/* noa_attr not found and we had valid noa_attr before */
|
|
|
|
link->u.mgd.p2p_noa_index = -1;
|
|
|
|
memset(&bss_conf->p2p_noa_attr, 0, sizeof(bss_conf->p2p_noa_attr));
|
|
|
|
changed |= BSS_CHANGED_P2P_PS;
|
|
|
|
link->u.mgd.beacon_crc_valid = false;
|
|
|
|
}
|
|
|
|
}
|
2020-06-18 17:17:42 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (link->u.mgd.csa_waiting_bcn)
|
|
|
|
ieee80211_chswitch_post_beacon(link);
|
2009-07-10 21:29:03 +08:00
|
|
|
|
2022-07-12 17:13:56 +08: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.
|
|
|
|
*/
|
|
|
|
if (ieee80211_hw_check(&local->hw, TIMING_BEACON_ONLY) &&
|
|
|
|
!ieee80211_is_s1g_beacon(hdr->frame_control)) {
|
|
|
|
link->conf->sync_tsf =
|
|
|
|
le64_to_cpu(mgmt->u.beacon.timestamp);
|
|
|
|
link->conf->sync_device_ts =
|
|
|
|
rx_status->device_timestamp;
|
|
|
|
link->conf->sync_dtim_count = elems->dtim_count;
|
|
|
|
}
|
2009-07-10 21:29:03 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if ((ncrc == link->u.mgd.beacon_crc && link->u.mgd.beacon_crc_valid) ||
|
|
|
|
ieee80211_is_s1g_short_beacon(mgmt->frame_control))
|
|
|
|
goto free;
|
|
|
|
link->u.mgd.beacon_crc = ncrc;
|
|
|
|
link->u.mgd.beacon_crc_valid = true;
|
2022-06-24 22:18:56 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
ieee80211_rx_bss_info(link, mgmt, len, rx_status);
|
2014-04-09 21:45:36 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
ieee80211_sta_process_chanswitch(link, rx_status->mactime,
|
|
|
|
rx_status->device_timestamp,
|
|
|
|
elems, true);
|
2020-09-08 20:36:56 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (!link->u.mgd.disable_wmm_tracking &&
|
|
|
|
ieee80211_sta_wmm_params(local, link, elems->wmm_param,
|
|
|
|
elems->wmm_param_len,
|
|
|
|
elems->mu_edca_param_set))
|
|
|
|
changed |= BSS_CHANGED_QOS;
|
2020-09-08 20:36:56 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
/*
|
|
|
|
* If we haven't had a beacon before, tell the driver about the
|
|
|
|
* DTIM period (and beacon timing if desired) now.
|
2021-03-31 07:07:49 +08:00
|
|
|
*/
|
2022-07-12 17:13:56 +08:00
|
|
|
if (!link->u.mgd.have_beacon) {
|
|
|
|
/* a few bogus AP send dtim_period = 0 or no TIM IE */
|
|
|
|
bss_conf->dtim_period = elems->dtim_period ?: 1;
|
2009-07-10 21:29:03 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
changed |= BSS_CHANGED_BEACON_INFO;
|
|
|
|
link->u.mgd.have_beacon = true;
|
2009-07-10 21:29:03 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
mutex_lock(&local->iflist_mtx);
|
|
|
|
ieee80211_recalc_ps(local);
|
|
|
|
mutex_unlock(&local->iflist_mtx);
|
2009-07-10 21:29:03 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
ieee80211_recalc_ps_vif(sdata);
|
|
|
|
}
|
2009-04-19 13:47:19 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (elems->erp_info) {
|
|
|
|
erp_valid = true;
|
|
|
|
erp_value = elems->erp_info[0];
|
|
|
|
} else {
|
|
|
|
erp_valid = false;
|
2009-04-19 13:47:19 +08:00
|
|
|
}
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (!ieee80211_is_s1g_beacon(hdr->frame_control))
|
|
|
|
changed |= ieee80211_handle_bss_capability(link,
|
|
|
|
le16_to_cpu(mgmt->u.beacon.capab_info),
|
|
|
|
erp_valid, erp_value);
|
2014-03-19 16:55:55 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
mutex_lock(&local->sta_mtx);
|
|
|
|
sta = sta_info_get(sdata, sdata->vif.cfg.ap_addr);
|
2022-09-25 02:40:41 +08:00
|
|
|
if (WARN_ON(!sta)) {
|
|
|
|
mutex_unlock(&local->sta_mtx);
|
2022-07-12 17:13:56 +08:00
|
|
|
goto free;
|
2022-09-25 02:40:41 +08:00
|
|
|
}
|
2022-07-12 17:13:56 +08:00
|
|
|
link_sta = rcu_dereference_protected(sta->link[link->link_id],
|
|
|
|
lockdep_is_held(&local->sta_mtx));
|
2022-09-25 02:40:41 +08:00
|
|
|
if (WARN_ON(!link_sta)) {
|
|
|
|
mutex_unlock(&local->sta_mtx);
|
2022-07-12 17:13:56 +08:00
|
|
|
goto free;
|
2022-09-25 02:40:41 +08:00
|
|
|
}
|
2014-03-19 16:55:55 +08:00
|
|
|
|
2022-09-12 03:01:42 +08:00
|
|
|
if (WARN_ON(!link->conf->chandef.chan))
|
|
|
|
goto free;
|
|
|
|
|
|
|
|
sband = local->hw.wiphy->bands[link->conf->chandef.chan->band];
|
|
|
|
|
|
|
|
changed |= ieee80211_recalc_twt_req(sdata, sband, link, link_sta, elems);
|
2014-05-13 17:54:09 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (ieee80211_config_bw(link, elems->ht_cap_elem,
|
|
|
|
elems->vht_cap_elem, elems->ht_operation,
|
|
|
|
elems->vht_operation, elems->he_operation,
|
|
|
|
elems->eht_operation,
|
|
|
|
elems->s1g_oper, bssid, &changed)) {
|
|
|
|
mutex_unlock(&local->sta_mtx);
|
|
|
|
sdata_info(sdata,
|
|
|
|
"failed to follow AP %pM bandwidth change, disconnect\n",
|
|
|
|
bssid);
|
|
|
|
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
|
|
|
|
WLAN_REASON_DEAUTH_LEAVING,
|
|
|
|
true, deauth_buf);
|
|
|
|
ieee80211_report_disconnect(sdata, deauth_buf,
|
|
|
|
sizeof(deauth_buf), true,
|
|
|
|
WLAN_REASON_DEAUTH_LEAVING,
|
|
|
|
false);
|
|
|
|
goto free;
|
2014-03-19 16:55:55 +08:00
|
|
|
}
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (sta && elems->opmode_notif)
|
|
|
|
ieee80211_vht_handle_opmode(sdata, link_sta,
|
|
|
|
*elems->opmode_notif,
|
|
|
|
rx_status->band);
|
|
|
|
mutex_unlock(&local->sta_mtx);
|
2015-03-01 15:10:03 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
changed |= ieee80211_handle_pwr_constr(link, chan, mgmt,
|
|
|
|
elems->country_elem,
|
|
|
|
elems->country_elem_len,
|
|
|
|
elems->pwr_constr_elem,
|
|
|
|
elems->cisco_dtpc_elem);
|
2015-03-01 15:10:03 +08:00
|
|
|
|
2023-01-27 19:39:31 +08:00
|
|
|
if (elems->eht_operation &&
|
|
|
|
!(link->u.mgd.conn_flags & IEEE80211_CONN_DISABLE_EHT)) {
|
|
|
|
if (!ieee80211_config_puncturing(link, elems->eht_operation,
|
|
|
|
&changed)) {
|
|
|
|
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
|
|
|
|
WLAN_REASON_DEAUTH_LEAVING,
|
|
|
|
true, deauth_buf);
|
|
|
|
ieee80211_report_disconnect(sdata, deauth_buf,
|
|
|
|
sizeof(deauth_buf), true,
|
|
|
|
WLAN_REASON_DEAUTH_LEAVING,
|
|
|
|
false);
|
|
|
|
goto free;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
ieee80211_link_info_change_notify(sdata, link, changed);
|
|
|
|
free:
|
|
|
|
kfree(elems);
|
2014-03-19 16:55:55 +08:00
|
|
|
}
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
void ieee80211_sta_rx_queued_ext(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct sk_buff *skb)
|
2013-04-29 20:57:44 +08:00
|
|
|
{
|
2022-07-12 17:13:56 +08:00
|
|
|
struct ieee80211_link_data *link = &sdata->deflink;
|
|
|
|
struct ieee80211_rx_status *rx_status;
|
|
|
|
struct ieee80211_hdr *hdr;
|
|
|
|
u16 fc;
|
2013-04-29 20:57:44 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
rx_status = (struct ieee80211_rx_status *) skb->cb;
|
|
|
|
hdr = (struct ieee80211_hdr *) skb->data;
|
|
|
|
fc = le16_to_cpu(hdr->frame_control);
|
2022-03-08 19:53:24 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
sdata_lock(sdata);
|
|
|
|
switch (fc & IEEE80211_FCTL_STYPE) {
|
|
|
|
case IEEE80211_STYPE_S1G_BEACON:
|
|
|
|
ieee80211_rx_mgmt_beacon(link, hdr, skb->len, rx_status);
|
|
|
|
break;
|
2022-03-08 19:53:24 +08:00
|
|
|
}
|
2013-05-10 18:32:47 +08:00
|
|
|
sdata_unlock(sdata);
|
2013-04-29 20:57:44 +08:00
|
|
|
}
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct sk_buff *skb)
|
2022-06-24 17:15:52 +08:00
|
|
|
{
|
2022-07-12 17:13:56 +08:00
|
|
|
struct ieee80211_link_data *link = &sdata->deflink;
|
|
|
|
struct ieee80211_rx_status *rx_status;
|
|
|
|
struct ieee80211_mgmt *mgmt;
|
|
|
|
u16 fc;
|
|
|
|
int ies_len;
|
2022-06-24 17:15:52 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
rx_status = (struct ieee80211_rx_status *) skb->cb;
|
|
|
|
mgmt = (struct ieee80211_mgmt *) skb->data;
|
|
|
|
fc = le16_to_cpu(mgmt->frame_control);
|
2022-06-24 17:15:52 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
sdata_lock(sdata);
|
2008-04-18 01:21:22 +08:00
|
|
|
|
2022-08-18 03:57:19 +08:00
|
|
|
if (rx_status->link_valid) {
|
|
|
|
link = sdata_dereference(sdata->link[rx_status->link_id],
|
|
|
|
sdata);
|
|
|
|
if (!link)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
switch (fc & IEEE80211_FCTL_STYPE) {
|
|
|
|
case IEEE80211_STYPE_BEACON:
|
|
|
|
ieee80211_rx_mgmt_beacon(link, (void *)mgmt,
|
|
|
|
skb->len, rx_status);
|
|
|
|
break;
|
|
|
|
case IEEE80211_STYPE_PROBE_RESP:
|
|
|
|
ieee80211_rx_mgmt_probe_resp(link, skb);
|
|
|
|
break;
|
|
|
|
case IEEE80211_STYPE_AUTH:
|
|
|
|
ieee80211_rx_mgmt_auth(sdata, mgmt, skb->len);
|
|
|
|
break;
|
|
|
|
case IEEE80211_STYPE_DEAUTH:
|
|
|
|
ieee80211_rx_mgmt_deauth(sdata, mgmt, skb->len);
|
|
|
|
break;
|
|
|
|
case IEEE80211_STYPE_DISASSOC:
|
|
|
|
ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len);
|
|
|
|
break;
|
|
|
|
case IEEE80211_STYPE_ASSOC_RESP:
|
|
|
|
case IEEE80211_STYPE_REASSOC_RESP:
|
|
|
|
ieee80211_rx_mgmt_assoc_resp(sdata, mgmt, skb->len);
|
|
|
|
break;
|
|
|
|
case IEEE80211_STYPE_ACTION:
|
|
|
|
if (mgmt->u.action.category == WLAN_CATEGORY_SPECTRUM_MGMT) {
|
|
|
|
struct ieee802_11_elems *elems;
|
2008-09-11 06:01:52 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
ies_len = skb->len -
|
|
|
|
offsetof(struct ieee80211_mgmt,
|
|
|
|
u.action.u.chan_switch.variable);
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (ies_len < 0)
|
|
|
|
break;
|
2022-06-24 16:57:41 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
/* CSA IE cannot be overridden, no need for BSSID */
|
|
|
|
elems = ieee802_11_parse_elems(
|
|
|
|
mgmt->u.action.u.chan_switch.variable,
|
|
|
|
ies_len, true, NULL);
|
2022-06-24 16:57:41 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (elems && !elems->parse_error)
|
|
|
|
ieee80211_sta_process_chanswitch(link,
|
|
|
|
rx_status->mactime,
|
|
|
|
rx_status->device_timestamp,
|
|
|
|
elems, false);
|
|
|
|
kfree(elems);
|
|
|
|
} else if (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) {
|
|
|
|
struct ieee802_11_elems *elems;
|
2022-06-24 22:18:56 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
ies_len = skb->len -
|
|
|
|
offsetof(struct ieee80211_mgmt,
|
|
|
|
u.action.u.ext_chan_switch.variable);
|
2022-06-24 16:57:41 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (ies_len < 0)
|
|
|
|
break;
|
2008-09-08 23:44:22 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
/*
|
|
|
|
* extended CSA IE can't be overridden, no need for
|
|
|
|
* BSSID
|
|
|
|
*/
|
|
|
|
elems = ieee802_11_parse_elems(
|
|
|
|
mgmt->u.action.u.ext_chan_switch.variable,
|
|
|
|
ies_len, true, NULL);
|
|
|
|
|
|
|
|
if (elems && !elems->parse_error) {
|
|
|
|
/* for the handling code pretend it was an IE */
|
|
|
|
elems->ext_chansw_ie =
|
|
|
|
&mgmt->u.action.u.ext_chan_switch.data;
|
|
|
|
|
|
|
|
ieee80211_sta_process_chanswitch(link,
|
|
|
|
rx_status->mactime,
|
|
|
|
rx_status->device_timestamp,
|
|
|
|
elems, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
kfree(elems);
|
|
|
|
}
|
|
|
|
break;
|
2013-03-20 08:50:50 +08:00
|
|
|
}
|
2022-08-18 03:57:19 +08:00
|
|
|
out:
|
2022-07-12 17:13:56 +08:00
|
|
|
sdata_unlock(sdata);
|
2009-07-07 09:45:17 +08:00
|
|
|
}
|
2008-09-08 23:44:22 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
static void ieee80211_sta_timer(struct timer_list *t)
|
2012-11-22 21:11:39 +08:00
|
|
|
{
|
2022-07-12 17:13:56 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata =
|
|
|
|
from_timer(sdata, t, u.mgd.timer);
|
2012-11-22 21:11:39 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
ieee80211_queue_work(&sdata->local->hw, &sdata->work);
|
|
|
|
}
|
2012-11-22 21:11:39 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
|
|
|
|
u8 reason, bool tx)
|
|
|
|
{
|
|
|
|
u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
|
2012-11-22 21:11:39 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, reason,
|
|
|
|
tx, frame_buf);
|
2012-11-22 21:11:39 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true,
|
|
|
|
reason, false);
|
|
|
|
}
|
2012-11-22 21:11:39 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
static int ieee80211_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;
|
|
|
|
u32 tx_flags = 0;
|
|
|
|
u16 trans = 1;
|
|
|
|
u16 status = 0;
|
|
|
|
struct ieee80211_prep_tx_info info = {
|
|
|
|
.subtype = IEEE80211_STYPE_AUTH,
|
|
|
|
};
|
|
|
|
|
|
|
|
sdata_assert_lock(sdata);
|
2012-11-22 21:11:39 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (WARN_ON_ONCE(!auth_data))
|
|
|
|
return -EINVAL;
|
2022-02-02 16:49:35 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
auth_data->tries++;
|
2022-02-02 16:49:35 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (auth_data->tries > IEEE80211_AUTH_MAX_TRIES) {
|
|
|
|
sdata_info(sdata, "authentication with %pM timed out\n",
|
2022-06-02 03:17:34 +08:00
|
|
|
auth_data->ap_addr);
|
2022-02-02 16:49:35 +08:00
|
|
|
|
2022-07-12 17:13:56 +08: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);
|
2022-02-02 16:49:35 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
return -ETIMEDOUT;
|
|
|
|
}
|
2022-02-02 16:49:35 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (auth_data->algorithm == WLAN_AUTH_SAE)
|
|
|
|
info.duration = jiffies_to_msecs(IEEE80211_AUTH_TIMEOUT_SAE);
|
2022-02-02 16:49:35 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
drv_mgd_prepare_tx(local, sdata, &info);
|
2022-02-02 16:49:35 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
sdata_info(sdata, "send auth to %pM (try %d/%d)\n",
|
2022-06-02 03:17:34 +08:00
|
|
|
auth_data->ap_addr, auth_data->tries,
|
2022-07-12 17:13:56 +08:00
|
|
|
IEEE80211_AUTH_MAX_TRIES);
|
2022-02-02 16:49:35 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
auth_data->expected_transaction = 2;
|
2022-02-02 16:49:35 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (auth_data->algorithm == WLAN_AUTH_SAE) {
|
|
|
|
trans = auth_data->sae_trans;
|
|
|
|
status = auth_data->sae_status;
|
|
|
|
auth_data->expected_transaction = trans;
|
|
|
|
}
|
2022-02-02 16:49:35 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS))
|
|
|
|
tx_flags = IEEE80211_TX_CTL_REQ_TX_STATUS |
|
|
|
|
IEEE80211_TX_INTFL_MLME_CONN_TX;
|
2022-02-02 16:49:35 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
ieee80211_send_auth(sdata, trans, auth_data->algorithm, status,
|
|
|
|
auth_data->data, auth_data->data_len,
|
2022-06-02 03:17:34 +08:00
|
|
|
auth_data->ap_addr, auth_data->ap_addr,
|
|
|
|
NULL, 0, 0, tx_flags);
|
2022-02-02 16:49:35 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (tx_flags == 0) {
|
|
|
|
if (auth_data->algorithm == WLAN_AUTH_SAE)
|
|
|
|
auth_data->timeout = jiffies +
|
|
|
|
IEEE80211_AUTH_TIMEOUT_SAE;
|
|
|
|
else
|
|
|
|
auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
|
|
|
|
} else {
|
|
|
|
auth_data->timeout =
|
|
|
|
round_jiffies_up(jiffies + IEEE80211_AUTH_TIMEOUT_LONG);
|
2022-02-02 16:49:35 +08:00
|
|
|
}
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
auth_data->timeout_started = true;
|
|
|
|
run_again(sdata, auth_data->timeout);
|
|
|
|
|
|
|
|
return 0;
|
2012-11-22 21:11:39 +08:00
|
|
|
}
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
static int ieee80211_do_assoc(struct ieee80211_sub_if_data *sdata)
|
2022-02-02 16:49:46 +08:00
|
|
|
{
|
2022-07-12 17:13:56 +08:00
|
|
|
struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data;
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
int ret;
|
2022-02-02 16:49:46 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
sdata_assert_lock(sdata);
|
2022-02-02 16:49:46 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
assoc_data->tries++;
|
|
|
|
if (assoc_data->tries > IEEE80211_ASSOC_MAX_TRIES) {
|
|
|
|
sdata_info(sdata, "association with %pM timed out\n",
|
2022-06-02 03:17:34 +08:00
|
|
|
assoc_data->ap_addr);
|
2022-02-02 16:49:46 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
/*
|
|
|
|
* Most likely AP is not in the range so remove the
|
|
|
|
* bss struct for that AP.
|
|
|
|
*/
|
2022-06-02 03:17:34 +08:00
|
|
|
cfg80211_unlink_bss(local->hw.wiphy,
|
|
|
|
assoc_data->link[assoc_data->assoc_link_id].bss);
|
2022-02-02 16:49:46 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
return -ETIMEDOUT;
|
2022-02-02 16:49:46 +08:00
|
|
|
}
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
sdata_info(sdata, "associate with %pM (try %d/%d)\n",
|
2022-06-02 03:17:34 +08:00
|
|
|
assoc_data->ap_addr, assoc_data->tries,
|
2022-07-12 17:13:56 +08:00
|
|
|
IEEE80211_ASSOC_MAX_TRIES);
|
|
|
|
ret = ieee80211_send_assoc(sdata);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2022-02-02 16:49:46 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (!ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) {
|
|
|
|
assoc_data->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT;
|
|
|
|
assoc_data->timeout_started = true;
|
|
|
|
run_again(sdata, assoc_data->timeout);
|
|
|
|
} else {
|
|
|
|
assoc_data->timeout =
|
|
|
|
round_jiffies_up(jiffies +
|
|
|
|
IEEE80211_ASSOC_TIMEOUT_LONG);
|
|
|
|
assoc_data->timeout_started = true;
|
|
|
|
run_again(sdata, assoc_data->timeout);
|
2022-02-02 16:49:46 +08:00
|
|
|
}
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2022-02-02 16:49:46 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
void ieee80211_mgd_conn_tx_status(struct ieee80211_sub_if_data *sdata,
|
|
|
|
__le16 fc, bool acked)
|
|
|
|
{
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
2022-02-02 16:49:46 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
sdata->u.mgd.status_fc = fc;
|
|
|
|
sdata->u.mgd.status_acked = acked;
|
|
|
|
sdata->u.mgd.status_received = true;
|
2022-02-02 16:49:46 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
ieee80211_queue_work(&local->hw, &sdata->work);
|
|
|
|
}
|
2022-02-02 16:49:46 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2022-02-02 16:49:46 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
sdata_lock(sdata);
|
|
|
|
|
|
|
|
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_auth(fc)) {
|
|
|
|
if (status_acked) {
|
|
|
|
if (ifmgd->auth_data->algorithm ==
|
|
|
|
WLAN_AUTH_SAE)
|
|
|
|
ifmgd->auth_data->timeout =
|
|
|
|
jiffies +
|
|
|
|
IEEE80211_AUTH_TIMEOUT_SAE;
|
|
|
|
else
|
|
|
|
ifmgd->auth_data->timeout =
|
|
|
|
jiffies +
|
|
|
|
IEEE80211_AUTH_TIMEOUT_SHORT;
|
|
|
|
run_again(sdata, ifmgd->auth_data->timeout);
|
|
|
|
} else {
|
|
|
|
ifmgd->auth_data->timeout = jiffies - 1;
|
|
|
|
}
|
|
|
|
ifmgd->auth_data->timeout_started = true;
|
|
|
|
} 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;
|
|
|
|
run_again(sdata, ifmgd->assoc_data->timeout);
|
|
|
|
} else {
|
|
|
|
ifmgd->assoc_data->timeout = jiffies - 1;
|
|
|
|
}
|
|
|
|
ifmgd->assoc_data->timeout_started = true;
|
2022-02-02 16:49:46 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (ifmgd->auth_data && ifmgd->auth_data->timeout_started &&
|
|
|
|
time_after(jiffies, ifmgd->auth_data->timeout)) {
|
|
|
|
if (ifmgd->auth_data->done || ifmgd->auth_data->waiting) {
|
|
|
|
/*
|
|
|
|
* ok ... we waited for assoc or continuation but
|
|
|
|
* userspace didn't do it, so kill the auth data
|
|
|
|
*/
|
|
|
|
ieee80211_destroy_auth_data(sdata, false);
|
|
|
|
} else if (ieee80211_auth(sdata)) {
|
2022-06-02 03:17:34 +08:00
|
|
|
u8 ap_addr[ETH_ALEN];
|
2022-07-12 17:13:56 +08:00
|
|
|
struct ieee80211_event event = {
|
|
|
|
.type = MLME_EVENT,
|
|
|
|
.u.mlme.data = AUTH_EVENT,
|
|
|
|
.u.mlme.status = MLME_TIMEOUT,
|
|
|
|
};
|
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
memcpy(ap_addr, ifmgd->auth_data->ap_addr, ETH_ALEN);
|
2022-02-02 16:49:46 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
ieee80211_destroy_auth_data(sdata, false);
|
2018-06-09 14:14:44 +08:00
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
cfg80211_auth_timeout(sdata->dev, ap_addr);
|
2022-07-12 17:13:56 +08:00
|
|
|
drv_event_callback(sdata->local, sdata, &event);
|
|
|
|
}
|
|
|
|
} else if (ifmgd->auth_data && ifmgd->auth_data->timeout_started)
|
|
|
|
run_again(sdata, ifmgd->auth_data->timeout);
|
2018-06-09 14:14:44 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started &&
|
|
|
|
time_after(jiffies, ifmgd->assoc_data->timeout)) {
|
|
|
|
if ((ifmgd->assoc_data->need_beacon &&
|
|
|
|
!sdata->deflink.u.mgd.have_beacon) ||
|
|
|
|
ieee80211_do_assoc(sdata)) {
|
|
|
|
struct ieee80211_event event = {
|
|
|
|
.type = MLME_EVENT,
|
|
|
|
.u.mlme.data = ASSOC_EVENT,
|
|
|
|
.u.mlme.status = MLME_TIMEOUT,
|
|
|
|
};
|
2018-06-18 20:41:34 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
ieee80211_destroy_assoc_data(sdata, ASSOC_TIMEOUT);
|
|
|
|
drv_event_callback(sdata->local, sdata, &event);
|
|
|
|
}
|
|
|
|
} else if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started)
|
|
|
|
run_again(sdata, ifmgd->assoc_data->timeout);
|
2018-06-09 14:14:44 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL &&
|
|
|
|
ifmgd->associated) {
|
|
|
|
u8 *bssid = sdata->deflink.u.mgd.bssid;
|
|
|
|
int max_tries;
|
2018-06-09 14:14:44 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS))
|
|
|
|
max_tries = max_nullfunc_tries;
|
|
|
|
else
|
|
|
|
max_tries = max_probe_tries;
|
2018-06-09 14:14:44 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
/* ACK received for nullfunc probing frame */
|
|
|
|
if (!ifmgd->probe_send_count)
|
|
|
|
ieee80211_reset_ap_probe(sdata);
|
|
|
|
else if (ifmgd->nullfunc_failed) {
|
|
|
|
if (ifmgd->probe_send_count < max_tries) {
|
|
|
|
mlme_dbg(sdata,
|
|
|
|
"No ack for nullfunc frame to AP %pM, try %d/%i\n",
|
|
|
|
bssid, ifmgd->probe_send_count,
|
|
|
|
max_tries);
|
|
|
|
ieee80211_mgd_probe_ap_send(sdata);
|
|
|
|
} else {
|
|
|
|
mlme_dbg(sdata,
|
|
|
|
"No ack for nullfunc frame to AP %pM, disconnecting.\n",
|
|
|
|
bssid);
|
|
|
|
ieee80211_sta_connection_lost(sdata,
|
|
|
|
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
|
|
|
|
false);
|
|
|
|
}
|
|
|
|
} else if (time_is_after_jiffies(ifmgd->probe_timeout))
|
|
|
|
run_again(sdata, ifmgd->probe_timeout);
|
|
|
|
else if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) {
|
|
|
|
mlme_dbg(sdata,
|
|
|
|
"Failed to send nullfunc to AP %pM after %dms, disconnecting\n",
|
|
|
|
bssid, probe_wait_ms);
|
|
|
|
ieee80211_sta_connection_lost(sdata,
|
|
|
|
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, false);
|
|
|
|
} else if (ifmgd->probe_send_count < max_tries) {
|
|
|
|
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);
|
|
|
|
ieee80211_mgd_probe_ap_send(sdata);
|
|
|
|
} else {
|
2018-06-09 14:14:44 +08:00
|
|
|
/*
|
2022-07-12 17:13:56 +08:00
|
|
|
* We actually lost the connection ... or did we?
|
|
|
|
* Let's make sure!
|
2018-06-09 14:14:44 +08:00
|
|
|
*/
|
2022-07-12 17:13:56 +08:00
|
|
|
mlme_dbg(sdata,
|
|
|
|
"No probe response from AP %pM after %dms, disconnecting.\n",
|
|
|
|
bssid, probe_wait_ms);
|
2018-06-09 14:14:44 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
ieee80211_sta_connection_lost(sdata,
|
|
|
|
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, false);
|
|
|
|
}
|
2018-06-09 14:14:44 +08:00
|
|
|
}
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
sdata_unlock(sdata);
|
2018-06-09 14:14:44 +08:00
|
|
|
}
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
static void ieee80211_sta_bcn_mon_timer(struct timer_list *t)
|
2012-03-08 22:02:07 +08:00
|
|
|
{
|
2022-07-12 17:13:56 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata =
|
|
|
|
from_timer(sdata, t, u.mgd.bcn_mon_timer);
|
2021-09-24 18:00:51 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (WARN_ON(sdata->vif.valid_links))
|
|
|
|
return;
|
2012-03-28 16:58:36 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (sdata->vif.bss_conf.csa_active &&
|
|
|
|
!sdata->deflink.u.mgd.csa_waiting_bcn)
|
|
|
|
return;
|
2012-11-22 21:11:39 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (sdata->vif.driver_flags & IEEE80211_VIF_BEACON_FILTER)
|
|
|
|
return;
|
2020-01-31 19:12:39 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
sdata->u.mgd.connection_loss = false;
|
|
|
|
ieee80211_queue_work(&sdata->local->hw,
|
|
|
|
&sdata->u.mgd.beacon_connection_loss_work);
|
|
|
|
}
|
2020-01-31 19:12:39 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
static void ieee80211_sta_conn_mon_timer(struct timer_list *t)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata =
|
|
|
|
from_timer(sdata, t, u.mgd.conn_mon_timer);
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
struct sta_info *sta;
|
|
|
|
unsigned long timeout;
|
2022-02-15 00:30:00 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (WARN_ON(sdata->vif.valid_links))
|
|
|
|
return;
|
2020-01-31 19:12:39 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (sdata->vif.bss_conf.csa_active &&
|
|
|
|
!sdata->deflink.u.mgd.csa_waiting_bcn)
|
|
|
|
return;
|
2014-07-24 16:20:05 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
sta = sta_info_get(sdata, sdata->vif.cfg.ap_addr);
|
|
|
|
if (!sta)
|
|
|
|
return;
|
2012-03-28 16:58:36 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
timeout = sta->deflink.status_stats.last_ack;
|
|
|
|
if (time_before(sta->deflink.status_stats.last_ack, sta->deflink.rx_stats.last_rx))
|
|
|
|
timeout = sta->deflink.rx_stats.last_rx;
|
|
|
|
timeout += IEEE80211_CONNECTION_IDLE_TIME;
|
2013-02-08 06:33:32 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
/* If timeout is after now, then update timer to fire at
|
|
|
|
* the later date, but do not actually probe at this time.
|
|
|
|
*/
|
|
|
|
if (time_is_after_jiffies(timeout)) {
|
|
|
|
mod_timer(&ifmgd->conn_mon_timer, round_jiffies_up(timeout));
|
|
|
|
return;
|
2012-03-28 16:58:36 +08:00
|
|
|
}
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
ieee80211_queue_work(&local->hw, &ifmgd->monitor_work);
|
|
|
|
}
|
2018-06-09 14:14:44 +08:00
|
|
|
|
2022-07-12 17:13:56 +08: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);
|
2021-09-24 18:00:52 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
ieee80211_mgd_probe_ap(sdata, false);
|
|
|
|
}
|
2021-09-24 18:00:52 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
|
|
|
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
|
|
|
|
__ieee80211_stop_poll(sdata);
|
2021-09-24 18:00:52 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
/* let's probe the connection once */
|
|
|
|
if (!ieee80211_hw_check(&sdata->local->hw, CONNECTION_MONITOR))
|
|
|
|
ieee80211_queue_work(&sdata->local->hw,
|
|
|
|
&sdata->u.mgd.monitor_work);
|
|
|
|
}
|
|
|
|
}
|
2021-09-24 18:00:52 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
#ifdef CONFIG_PM
|
|
|
|
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];
|
2021-09-24 18:00:52 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
sdata_lock(sdata);
|
2021-09-24 18:00:52 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (ifmgd->auth_data || ifmgd->assoc_data) {
|
2022-06-02 03:17:34 +08:00
|
|
|
const u8 *ap_addr = ifmgd->auth_data ?
|
|
|
|
ifmgd->auth_data->ap_addr :
|
|
|
|
ifmgd->assoc_data->ap_addr;
|
2022-07-12 17:13:56 +08: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.
|
|
|
|
*/
|
2022-06-02 03:17:34 +08:00
|
|
|
ieee80211_send_deauth_disassoc(sdata, ap_addr, ap_addr,
|
2022-07-12 17:13:56 +08:00
|
|
|
IEEE80211_STYPE_DEAUTH,
|
|
|
|
WLAN_REASON_DEAUTH_LEAVING,
|
|
|
|
false, frame_buf);
|
|
|
|
if (ifmgd->assoc_data)
|
|
|
|
ieee80211_destroy_assoc_data(sdata, ASSOC_ABANDON);
|
|
|
|
if (ifmgd->auth_data)
|
|
|
|
ieee80211_destroy_auth_data(sdata, false);
|
|
|
|
cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf,
|
|
|
|
IEEE80211_DEAUTH_FRAME_LEN,
|
|
|
|
false);
|
2022-02-15 00:30:00 +08:00
|
|
|
}
|
|
|
|
|
2022-07-12 17:13:56 +08: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.
|
2022-02-15 00:30:00 +08:00
|
|
|
*/
|
2022-07-12 17:13:56 +08:00
|
|
|
if (ifmgd->associated && !sdata->local->wowlan) {
|
|
|
|
u8 bssid[ETH_ALEN];
|
|
|
|
struct cfg80211_deauth_request req = {
|
|
|
|
.reason_code = WLAN_REASON_DEAUTH_LEAVING,
|
|
|
|
.bssid = bssid,
|
|
|
|
};
|
2022-02-15 00:30:00 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
memcpy(bssid, sdata->vif.cfg.ap_addr, ETH_ALEN);
|
|
|
|
ieee80211_mgd_deauth(sdata, &req);
|
2018-06-09 14:14:44 +08:00
|
|
|
}
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
sdata_unlock(sdata);
|
|
|
|
}
|
|
|
|
#endif
|
2015-08-16 03:39:53 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
|
|
|
|
sdata_lock(sdata);
|
|
|
|
if (!ifmgd->associated) {
|
|
|
|
sdata_unlock(sdata);
|
|
|
|
return;
|
2015-08-16 03:39:53 +08:00
|
|
|
}
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
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,
|
|
|
|
WLAN_REASON_UNSPECIFIED,
|
|
|
|
true);
|
|
|
|
sdata_unlock(sdata);
|
|
|
|
return;
|
2021-11-30 19:16:59 +08:00
|
|
|
}
|
2015-08-16 03:39:53 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
if (sdata->flags & IEEE80211_SDATA_DISCONNECT_HW_RESTART) {
|
|
|
|
sdata->flags &= ~IEEE80211_SDATA_DISCONNECT_HW_RESTART;
|
|
|
|
mlme_dbg(sdata, "driver requested disconnect after hardware restart\n");
|
|
|
|
ieee80211_sta_connection_lost(sdata,
|
|
|
|
WLAN_REASON_UNSPECIFIED,
|
|
|
|
true);
|
|
|
|
sdata_unlock(sdata);
|
|
|
|
return;
|
2020-09-22 10:28:15 +08:00
|
|
|
}
|
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
sdata_unlock(sdata);
|
|
|
|
}
|
2012-09-11 20:34:12 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
static void ieee80211_request_smps_mgd_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct ieee80211_link_data *link =
|
|
|
|
container_of(work, struct ieee80211_link_data,
|
|
|
|
u.mgd.request_smps_work);
|
2012-03-28 16:58:36 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
sdata_lock(link->sdata);
|
|
|
|
__ieee80211_request_smps_mgd(link->sdata, link,
|
|
|
|
link->u.mgd.driver_smps_mode);
|
|
|
|
sdata_unlock(link->sdata);
|
|
|
|
}
|
2012-11-29 08:25:20 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
/* interface setup */
|
|
|
|
void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
|
|
|
|
{
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2020-05-29 03:34:36 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
INIT_WORK(&ifmgd->monitor_work, ieee80211_sta_monitor_work);
|
|
|
|
INIT_WORK(&ifmgd->beacon_connection_loss_work,
|
|
|
|
ieee80211_beacon_connection_loss_work);
|
|
|
|
INIT_WORK(&ifmgd->csa_connection_drop_work,
|
|
|
|
ieee80211_csa_connection_drop_work);
|
|
|
|
INIT_DELAYED_WORK(&ifmgd->tdls_peer_del_work,
|
|
|
|
ieee80211_tdls_peer_del_work);
|
|
|
|
timer_setup(&ifmgd->timer, ieee80211_sta_timer, 0);
|
|
|
|
timer_setup(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer, 0);
|
|
|
|
timer_setup(&ifmgd->conn_mon_timer, ieee80211_sta_conn_mon_timer, 0);
|
|
|
|
INIT_DELAYED_WORK(&ifmgd->tx_tspec_wk,
|
|
|
|
ieee80211_sta_handle_tspec_ac_params_wk);
|
2012-09-11 20:34:12 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
ifmgd->flags = 0;
|
|
|
|
ifmgd->powersave = sdata->wdev.ps;
|
|
|
|
ifmgd->uapsd_queues = sdata->local->hw.uapsd_queues;
|
|
|
|
ifmgd->uapsd_max_sp_len = sdata->local->hw.uapsd_max_sp_len;
|
|
|
|
/* Setup TDLS data */
|
|
|
|
spin_lock_init(&ifmgd->teardown_lock);
|
|
|
|
ifmgd->teardown_skb = NULL;
|
|
|
|
ifmgd->orig_teardown_skb = NULL;
|
|
|
|
}
|
2013-05-16 19:00:31 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
void ieee80211_mgd_setup_link(struct ieee80211_link_data *link)
|
|
|
|
{
|
2022-06-02 03:17:34 +08:00
|
|
|
struct ieee80211_sub_if_data *sdata = link->sdata;
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
unsigned int link_id = link->link_id;
|
2013-05-16 19:00:31 +08:00
|
|
|
|
2022-07-12 17:13:56 +08:00
|
|
|
link->u.mgd.p2p_noa_index = -1;
|
|
|
|
link->u.mgd.conn_flags = 0;
|
|
|
|
link->conf->bssid = link->u.mgd.bssid;
|
|
|
|
|
|
|
|
INIT_WORK(&link->u.mgd.request_smps_work,
|
|
|
|
ieee80211_request_smps_mgd_work);
|
|
|
|
if (local->hw.wiphy->features & NL80211_FEATURE_DYNAMIC_SMPS)
|
|
|
|
link->u.mgd.req_smps = IEEE80211_SMPS_AUTOMATIC;
|
|
|
|
else
|
|
|
|
link->u.mgd.req_smps = IEEE80211_SMPS_OFF;
|
|
|
|
|
|
|
|
INIT_WORK(&link->u.mgd.chswitch_work, ieee80211_chswitch_work);
|
|
|
|
timer_setup(&link->u.mgd.chswitch_timer, ieee80211_chswitch_timer, 0);
|
2022-06-02 03:17:34 +08:00
|
|
|
|
|
|
|
if (sdata->u.mgd.assoc_data)
|
|
|
|
ether_addr_copy(link->conf->addr,
|
|
|
|
sdata->u.mgd.assoc_data->link[link_id].addr);
|
2022-09-02 22:12:38 +08:00
|
|
|
else if (!is_valid_ether_addr(link->conf->addr))
|
|
|
|
eth_random_addr(link->conf->addr);
|
2022-07-12 17:13:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* scan finished notification */
|
|
|
|
void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata;
|
|
|
|
|
|
|
|
/* Restart STA timers */
|
|
|
|
rcu_read_lock();
|
|
|
|
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
|
|
|
|
if (ieee80211_sdata_running(sdata))
|
|
|
|
ieee80211_restart_sta_timer(sdata);
|
2013-02-08 03:54:51 +08:00
|
|
|
}
|
2022-07-12 17:13:56 +08:00
|
|
|
rcu_read_unlock();
|
2012-07-27 17:41:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
|
2022-06-02 03:17:34 +08:00
|
|
|
struct cfg80211_bss *cbss, s8 link_id,
|
|
|
|
const u8 *ap_mld_addr, bool assoc,
|
2015-04-21 03:51:46 +08:00
|
|
|
bool override)
|
2012-07-27 17:41:27 +08: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;
|
2022-06-02 03:17:34 +08:00
|
|
|
struct ieee80211_link_data *link;
|
2015-04-21 03:51:46 +08:00
|
|
|
bool have_sta = false;
|
2022-06-02 03:17:34 +08:00
|
|
|
bool mlo;
|
2012-07-27 17:41:27 +08:00
|
|
|
int err;
|
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
if (link_id >= 0) {
|
|
|
|
mlo = true;
|
|
|
|
if (WARN_ON(!ap_mld_addr))
|
|
|
|
return -EINVAL;
|
|
|
|
err = ieee80211_vif_set_links(sdata, BIT(link_id));
|
|
|
|
} else {
|
|
|
|
if (WARN_ON(ap_mld_addr))
|
|
|
|
return -EINVAL;
|
|
|
|
ap_mld_addr = cbss->bssid;
|
|
|
|
err = ieee80211_vif_set_links(sdata, 0);
|
|
|
|
link_id = 0;
|
|
|
|
mlo = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
link = sdata_dereference(sdata->link[link_id], sdata);
|
|
|
|
if (WARN_ON(!link)) {
|
|
|
|
err = -ENOLINK;
|
|
|
|
goto out_err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (WARN_ON(!ifmgd->auth_data && !ifmgd->assoc_data)) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out_err;
|
|
|
|
}
|
2012-07-27 17:41:27 +08:00
|
|
|
|
2017-05-02 22:56:21 +08:00
|
|
|
/* If a reconfig is happening, bail out */
|
2022-06-02 03:17:34 +08:00
|
|
|
if (local->in_reconfig) {
|
|
|
|
err = -EBUSY;
|
|
|
|
goto out_err;
|
|
|
|
}
|
2017-05-02 22:56:21 +08:00
|
|
|
|
2012-07-27 17:41:27 +08:00
|
|
|
if (assoc) {
|
|
|
|
rcu_read_lock();
|
2022-06-02 03:17:34 +08:00
|
|
|
have_sta = sta_info_get(sdata, ap_mld_addr);
|
2012-07-27 17:41:27 +08:00
|
|
|
rcu_read_unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!have_sta) {
|
2022-07-18 22:04:05 +08:00
|
|
|
if (mlo)
|
2022-06-02 03:17:34 +08:00
|
|
|
new_sta = sta_info_alloc_with_link(sdata, ap_mld_addr,
|
|
|
|
link_id, cbss->bssid,
|
|
|
|
GFP_KERNEL);
|
|
|
|
else
|
|
|
|
new_sta = sta_info_alloc(sdata, ap_mld_addr, GFP_KERNEL);
|
|
|
|
|
|
|
|
if (!new_sta) {
|
|
|
|
err = -ENOMEM;
|
|
|
|
goto out_err;
|
|
|
|
}
|
2022-07-15 21:11:50 +08:00
|
|
|
|
2022-07-18 22:04:05 +08:00
|
|
|
new_sta->sta.mlo = mlo;
|
2012-07-27 17:41:27 +08:00
|
|
|
}
|
2014-12-14 16:38:59 +08:00
|
|
|
|
2017-06-10 18:52:43 +08:00
|
|
|
/*
|
|
|
|
* Set up the information for the new channel before setting the
|
|
|
|
* new channel. We can't - completely race-free - change the basic
|
|
|
|
* rates bitmap and the channel (sband) that it refers to, but if
|
|
|
|
* we set it up before we at least avoid calling into the driver's
|
|
|
|
* bss_info_changed() method with invalid information (since we do
|
|
|
|
* call that from changing the channel - only for IDLE and perhaps
|
|
|
|
* some others, but ...).
|
|
|
|
*
|
|
|
|
* So to avoid that, just set up all the new information before the
|
|
|
|
* channel, but tell the driver to apply it only afterwards, since
|
|
|
|
* it might need the new channel for that.
|
|
|
|
*/
|
2012-07-27 16:43:16 +08:00
|
|
|
if (new_sta) {
|
2013-02-05 23:54:31 +08:00
|
|
|
const struct cfg80211_bss_ies *ies;
|
2022-07-19 18:06:17 +08:00
|
|
|
struct link_sta_info *link_sta;
|
2022-06-02 03:17:34 +08:00
|
|
|
|
|
|
|
rcu_read_lock();
|
2022-07-19 18:06:17 +08:00
|
|
|
link_sta = rcu_dereference(new_sta->link[link_id]);
|
2022-06-02 03:17:34 +08:00
|
|
|
if (WARN_ON(!link_sta)) {
|
|
|
|
rcu_read_unlock();
|
|
|
|
sta_info_free(local, new_sta);
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out_err;
|
|
|
|
}
|
2022-06-24 22:18:56 +08:00
|
|
|
|
2022-07-12 17:06:33 +08:00
|
|
|
err = ieee80211_mgd_setup_link_sta(link, new_sta,
|
|
|
|
link_sta, cbss);
|
|
|
|
if (err) {
|
2022-06-02 03:17:34 +08:00
|
|
|
rcu_read_unlock();
|
2020-03-26 21:09:39 +08:00
|
|
|
sta_info_free(local, new_sta);
|
2022-06-02 03:17:34 +08:00
|
|
|
goto out_err;
|
2012-03-08 22:02:08 +08:00
|
|
|
}
|
|
|
|
|
2022-06-24 22:18:56 +08:00
|
|
|
memcpy(link->u.mgd.bssid, cbss->bssid, ETH_ALEN);
|
2012-03-08 22:02:08 +08:00
|
|
|
|
2012-05-23 04:13:05 +08:00
|
|
|
/* set timing information */
|
2022-06-24 22:18:56 +08:00
|
|
|
link->conf->beacon_int = cbss->beacon_interval;
|
2013-02-06 00:48:40 +08:00
|
|
|
ies = rcu_dereference(cbss->beacon_ies);
|
|
|
|
if (ies) {
|
2022-06-24 22:18:56 +08:00
|
|
|
link->conf->sync_tsf = ies->tsf;
|
|
|
|
link->conf->sync_device_ts =
|
2013-02-06 00:48:40 +08:00
|
|
|
bss->device_ts_beacon;
|
2019-01-17 00:22:56 +08:00
|
|
|
|
|
|
|
ieee80211_get_dtim(ies,
|
2022-06-24 22:18:56 +08:00
|
|
|
&link->conf->sync_dtim_count,
|
2019-01-17 00:22:56 +08:00
|
|
|
NULL);
|
2015-06-03 03:39:54 +08:00
|
|
|
} else if (!ieee80211_hw_check(&sdata->local->hw,
|
|
|
|
TIMING_BEACON_ONLY)) {
|
2013-02-06 00:48:40 +08:00
|
|
|
ies = rcu_dereference(cbss->proberesp_ies);
|
|
|
|
/* must be non-NULL since beacon IEs were NULL */
|
2022-06-24 22:18:56 +08:00
|
|
|
link->conf->sync_tsf = ies->tsf;
|
|
|
|
link->conf->sync_device_ts =
|
2013-02-06 00:48:40 +08:00
|
|
|
bss->device_ts_presp;
|
2022-06-24 22:18:56 +08:00
|
|
|
link->conf->sync_dtim_count = 0;
|
2013-02-06 00:48:40 +08:00
|
|
|
} else {
|
2022-06-24 22:18:56 +08:00
|
|
|
link->conf->sync_tsf = 0;
|
|
|
|
link->conf->sync_device_ts = 0;
|
|
|
|
link->conf->sync_dtim_count = 0;
|
2013-02-06 00:48:40 +08:00
|
|
|
}
|
2013-02-05 23:54:31 +08:00
|
|
|
rcu_read_unlock();
|
2017-06-10 18:52:43 +08:00
|
|
|
}
|
2012-05-23 04:13:05 +08:00
|
|
|
|
2017-06-10 18:52:43 +08:00
|
|
|
if (new_sta || override) {
|
2022-07-12 20:48:55 +08:00
|
|
|
err = ieee80211_prep_channel(sdata, link, cbss,
|
|
|
|
&link->u.mgd.conn_flags);
|
2017-06-10 18:52:43 +08:00
|
|
|
if (err) {
|
|
|
|
if (new_sta)
|
|
|
|
sta_info_free(local, new_sta);
|
2022-06-02 03:17:34 +08:00
|
|
|
goto out_err;
|
2017-06-10 18:52:43 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (new_sta) {
|
|
|
|
/*
|
|
|
|
* tell driver about BSSID, basic rates and timing
|
|
|
|
* this was set up above, before setting the channel
|
|
|
|
*/
|
2022-06-24 22:18:56 +08:00
|
|
|
ieee80211_link_info_change_notify(sdata, link,
|
2022-06-18 04:36:37 +08:00
|
|
|
BSS_CHANGED_BSSID |
|
|
|
|
BSS_CHANGED_BASIC_RATES |
|
|
|
|
BSS_CHANGED_BEACON_INT);
|
2012-03-08 22:02:07 +08:00
|
|
|
|
|
|
|
if (assoc)
|
2012-07-27 16:43:16 +08:00
|
|
|
sta_info_pre_move_state(new_sta, IEEE80211_STA_AUTH);
|
2012-03-08 22:02:07 +08:00
|
|
|
|
2012-07-27 16:43:16 +08:00
|
|
|
err = sta_info_insert(new_sta);
|
|
|
|
new_sta = NULL;
|
2012-03-08 22:02:07 +08:00
|
|
|
if (err) {
|
2012-06-22 17:29:50 +08:00
|
|
|
sdata_info(sdata,
|
|
|
|
"failed to insert STA entry for the AP (error %d)\n",
|
|
|
|
err);
|
2022-06-02 03:17:34 +08:00
|
|
|
goto out_err;
|
2012-03-08 22:02:07 +08:00
|
|
|
}
|
|
|
|
} else
|
2022-06-24 22:18:56 +08:00
|
|
|
WARN_ON_ONCE(!ether_addr_equal(link->u.mgd.bssid, cbss->bssid));
|
2012-03-08 22:02:07 +08:00
|
|
|
|
2015-03-18 01:58:38 +08:00
|
|
|
/* Cancel scan to ensure that nothing interferes with connection */
|
|
|
|
if (local->scanning)
|
|
|
|
ieee80211_scan_cancel(local);
|
|
|
|
|
2012-03-08 22:02:07 +08:00
|
|
|
return 0;
|
2022-06-02 03:17:34 +08:00
|
|
|
|
|
|
|
out_err:
|
2022-09-02 22:11:15 +08:00
|
|
|
ieee80211_link_release_channel(&sdata->deflink);
|
2022-06-02 03:17:34 +08:00
|
|
|
ieee80211_vif_set_links(sdata, 0);
|
|
|
|
return err;
|
2012-03-08 22:02:07 +08:00
|
|
|
}
|
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
/* config hooks */
|
|
|
|
int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct cfg80211_auth_request *req)
|
2008-09-11 06:01:52 +08:00
|
|
|
{
|
2012-01-20 20:55:27 +08: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 09:45:17 +08:00
|
|
|
u16 auth_alg;
|
2012-01-20 20:55:27 +08:00
|
|
|
int err;
|
2018-10-11 05:21:21 +08:00
|
|
|
bool cont_auth;
|
2012-01-20 20:55:27 +08:00
|
|
|
|
|
|
|
/* prepare auth data structure */
|
2008-09-11 06:01:52 +08:00
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
switch (req->auth_type) {
|
|
|
|
case NL80211_AUTHTYPE_OPEN_SYSTEM:
|
|
|
|
auth_alg = WLAN_AUTH_OPEN;
|
|
|
|
break;
|
|
|
|
case NL80211_AUTHTYPE_SHARED_KEY:
|
2019-06-13 00:19:54 +08:00
|
|
|
if (fips_enabled)
|
2010-07-21 16:09:25 +08:00
|
|
|
return -EOPNOTSUPP;
|
2009-07-07 09:45:17 +08: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-10-01 00:29:40 +08:00
|
|
|
case NL80211_AUTHTYPE_SAE:
|
|
|
|
auth_alg = WLAN_AUTH_SAE;
|
|
|
|
break;
|
2016-10-27 05:42:04 +08:00
|
|
|
case NL80211_AUTHTYPE_FILS_SK:
|
|
|
|
auth_alg = WLAN_AUTH_FILS_SK;
|
|
|
|
break;
|
|
|
|
case NL80211_AUTHTYPE_FILS_SK_PFS:
|
|
|
|
auth_alg = WLAN_AUTH_FILS_SK_PFS;
|
|
|
|
break;
|
|
|
|
case NL80211_AUTHTYPE_FILS_PK:
|
|
|
|
auth_alg = WLAN_AUTH_FILS_PK;
|
|
|
|
break;
|
2009-07-07 09:45:17 +08:00
|
|
|
default:
|
|
|
|
return -EOPNOTSUPP;
|
2008-09-08 23:44:22 +08:00
|
|
|
}
|
2009-07-07 09:45:17 +08:00
|
|
|
|
2018-10-11 05:21:21 +08:00
|
|
|
if (ifmgd->assoc_data)
|
2018-10-11 05:21:20 +08:00
|
|
|
return -EBUSY;
|
|
|
|
|
2016-10-27 05:41:58 +08:00
|
|
|
auth_data = kzalloc(sizeof(*auth_data) + req->auth_data_len +
|
2012-10-01 00:29:40 +08:00
|
|
|
req->ie_len, GFP_KERNEL);
|
2012-01-20 20:55:27 +08:00
|
|
|
if (!auth_data)
|
2008-09-11 06:01:52 +08:00
|
|
|
return -ENOMEM;
|
2009-07-07 09:45:17 +08:00
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
memcpy(auth_data->ap_addr,
|
|
|
|
req->ap_mld_addr ?: req->bss->bssid,
|
|
|
|
ETH_ALEN);
|
2012-01-20 20:55:27 +08:00
|
|
|
auth_data->bss = req->bss;
|
2022-09-22 05:01:46 +08:00
|
|
|
auth_data->link_id = req->link_id;
|
2009-07-07 09:45:17 +08:00
|
|
|
|
2016-10-27 05:41:58 +08:00
|
|
|
if (req->auth_data_len >= 4) {
|
2016-10-27 05:41:59 +08:00
|
|
|
if (req->auth_type == NL80211_AUTHTYPE_SAE) {
|
|
|
|
__le16 *pos = (__le16 *) req->auth_data;
|
|
|
|
|
|
|
|
auth_data->sae_trans = le16_to_cpu(pos[0]);
|
|
|
|
auth_data->sae_status = le16_to_cpu(pos[1]);
|
|
|
|
}
|
2016-10-27 05:41:58 +08:00
|
|
|
memcpy(auth_data->data, req->auth_data + 4,
|
|
|
|
req->auth_data_len - 4);
|
|
|
|
auth_data->data_len += req->auth_data_len - 4;
|
2012-10-01 00:29:40 +08:00
|
|
|
}
|
|
|
|
|
2018-10-11 05:21:21 +08:00
|
|
|
/* Check if continuing authentication or trying to authenticate with the
|
|
|
|
* same BSS that we were in the process of authenticating with and avoid
|
|
|
|
* removal and re-addition of the STA entry in
|
|
|
|
* ieee80211_prep_connection().
|
|
|
|
*/
|
2022-09-22 05:01:46 +08:00
|
|
|
cont_auth = ifmgd->auth_data && req->bss == ifmgd->auth_data->bss &&
|
|
|
|
ifmgd->auth_data->link_id == req->link_id;
|
2018-10-11 05:21:21 +08:00
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
if (req->ie && req->ie_len) {
|
2012-10-01 00:29:40 +08:00
|
|
|
memcpy(&auth_data->data[auth_data->data_len],
|
|
|
|
req->ie, req->ie_len);
|
|
|
|
auth_data->data_len += req->ie_len;
|
2008-09-11 06:01:52 +08:00
|
|
|
}
|
2009-07-07 09:45:17 +08: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 20:22:54 +08:00
|
|
|
if (req->key && req->key_len) {
|
2012-01-20 20:55:27 +08: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 20:22:54 +08:00
|
|
|
}
|
|
|
|
|
2012-01-20 20:55:27 +08:00
|
|
|
auth_data->algorithm = auth_alg;
|
2008-09-08 23:44:22 +08:00
|
|
|
|
2012-01-20 20:55:27 +08:00
|
|
|
/* try to authenticate/probe */
|
2011-08-17 20:18:15 +08:00
|
|
|
|
2018-10-11 05:21:21 +08:00
|
|
|
if (ifmgd->auth_data) {
|
|
|
|
if (cont_auth && req->auth_type == NL80211_AUTHTYPE_SAE) {
|
|
|
|
auth_data->peer_confirmed =
|
|
|
|
ifmgd->auth_data->peer_confirmed;
|
|
|
|
}
|
|
|
|
ieee80211_destroy_auth_data(sdata, cont_auth);
|
|
|
|
}
|
2011-08-17 20:18:15 +08:00
|
|
|
|
2012-01-20 20:55:27 +08:00
|
|
|
/* prep auth_data so we don't go into idle on disassoc */
|
|
|
|
ifmgd->auth_data = auth_data;
|
2009-12-23 20:15:35 +08:00
|
|
|
|
2018-10-11 05:21:21 +08:00
|
|
|
/* If this is continuation of an ongoing SAE authentication exchange
|
|
|
|
* (i.e., request to send SAE Confirm) and the peer has already
|
|
|
|
* confirmed, mark authentication completed since we are about to send
|
|
|
|
* out SAE Confirm.
|
|
|
|
*/
|
|
|
|
if (cont_auth && req->auth_type == NL80211_AUTHTYPE_SAE &&
|
|
|
|
auth_data->peer_confirmed && auth_data->sae_trans == 2)
|
2022-07-12 21:13:23 +08:00
|
|
|
ieee80211_mark_sta_auth(sdata);
|
2018-10-11 05:21:21 +08: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-11 03:38:36 +08:00
|
|
|
if (ifmgd->associated) {
|
|
|
|
u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
|
|
|
|
|
2016-01-25 21:46:36 +08:00
|
|
|
sdata_info(sdata,
|
|
|
|
"disconnect from AP %pM for new auth to %pM\n",
|
2022-06-02 03:17:34 +08:00
|
|
|
sdata->vif.cfg.ap_addr, auth_data->ap_addr);
|
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-11 03:38:36 +08:00
|
|
|
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
|
|
|
|
WLAN_REASON_UNSPECIFIED,
|
|
|
|
false, frame_buf);
|
|
|
|
|
2015-03-17 05:23:37 +08:00
|
|
|
ieee80211_report_disconnect(sdata, frame_buf,
|
|
|
|
sizeof(frame_buf), true,
|
2020-12-06 20:54:43 +08:00
|
|
|
WLAN_REASON_UNSPECIFIED,
|
|
|
|
false);
|
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-11 03:38:36 +08:00
|
|
|
}
|
2009-12-23 20:15:35 +08:00
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
sdata_info(sdata, "authenticate with %pM\n", auth_data->ap_addr);
|
2010-07-29 22:08:55 +08:00
|
|
|
|
2022-07-22 05:35:21 +08:00
|
|
|
/* needed for transmitting the auth frame(s) properly */
|
|
|
|
memcpy(sdata->vif.cfg.ap_addr, auth_data->ap_addr, ETH_ALEN);
|
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
err = ieee80211_prep_connection(sdata, req->bss, req->link_id,
|
|
|
|
req->ap_mld_addr, cont_auth, false);
|
2012-03-08 22:02:07 +08:00
|
|
|
if (err)
|
2012-01-20 20:55:27 +08:00
|
|
|
goto err_clear;
|
2009-12-23 20:15:35 +08:00
|
|
|
|
2015-08-16 03:39:54 +08:00
|
|
|
err = ieee80211_auth(sdata);
|
2012-01-20 20:55:27 +08:00
|
|
|
if (err) {
|
2022-06-02 03:17:34 +08:00
|
|
|
sta_info_destroy_addr(sdata, auth_data->ap_addr);
|
2012-01-20 20:55:27 +08:00
|
|
|
goto err_clear;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* hold our own reference */
|
2013-02-01 08:49:58 +08:00
|
|
|
cfg80211_ref_bss(local->hw.wiphy, auth_data->bss);
|
2013-05-10 18:32:47 +08:00
|
|
|
return 0;
|
2012-01-20 20:55:27 +08:00
|
|
|
|
|
|
|
err_clear:
|
2022-06-02 03:17:34 +08:00
|
|
|
if (!sdata->vif.valid_links) {
|
|
|
|
eth_zero_addr(sdata->deflink.u.mgd.bssid);
|
|
|
|
ieee80211_link_info_change_notify(sdata, &sdata->deflink,
|
|
|
|
BSS_CHANGED_BSSID);
|
|
|
|
mutex_lock(&sdata->local->mtx);
|
|
|
|
ieee80211_link_release_channel(&sdata->deflink);
|
|
|
|
mutex_unlock(&sdata->local->mtx);
|
|
|
|
}
|
2012-01-20 20:55:27 +08:00
|
|
|
ifmgd->auth_data = NULL;
|
|
|
|
kfree(auth_data);
|
|
|
|
return err;
|
2009-12-23 20:15:35 +08:00
|
|
|
}
|
|
|
|
|
2022-07-12 21:21:23 +08:00
|
|
|
static ieee80211_conn_flags_t
|
|
|
|
ieee80211_setup_assoc_link(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct ieee80211_mgd_assoc_data *assoc_data,
|
|
|
|
struct cfg80211_assoc_request *req,
|
2022-06-02 03:17:34 +08:00
|
|
|
ieee80211_conn_flags_t conn_flags,
|
|
|
|
unsigned int link_id)
|
2022-07-12 21:21:23 +08:00
|
|
|
{
|
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
|
const struct cfg80211_bss_ies *beacon_ies;
|
|
|
|
struct ieee80211_supported_band *sband;
|
|
|
|
const struct element *ht_elem, *vht_elem;
|
2022-06-02 03:17:34 +08:00
|
|
|
struct ieee80211_link_data *link;
|
|
|
|
struct cfg80211_bss *cbss;
|
|
|
|
struct ieee80211_bss *bss;
|
2022-07-12 21:21:23 +08:00
|
|
|
bool is_5ghz, is_6ghz;
|
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
cbss = assoc_data->link[link_id].bss;
|
|
|
|
if (WARN_ON(!cbss))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
bss = (void *)cbss->priv;
|
|
|
|
|
2022-07-12 21:21:23 +08:00
|
|
|
sband = local->hw.wiphy->bands[cbss->channel->band];
|
|
|
|
if (WARN_ON(!sband))
|
2022-06-02 03:17:34 +08:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
link = sdata_dereference(sdata->link[link_id], sdata);
|
|
|
|
if (WARN_ON(!link))
|
|
|
|
return 0;
|
2022-07-12 21:21:23 +08:00
|
|
|
|
|
|
|
is_5ghz = cbss->channel->band == NL80211_BAND_5GHZ;
|
|
|
|
is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ;
|
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
/* for MLO connections assume advertising all rates is OK */
|
|
|
|
if (!req->ap_mld_addr) {
|
|
|
|
assoc_data->supp_rates = bss->supp_rates;
|
|
|
|
assoc_data->supp_rates_len = bss->supp_rates_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* copy and link elems for the STA profile */
|
|
|
|
if (req->links[link_id].elems_len) {
|
|
|
|
memcpy(assoc_data->ie_pos, req->links[link_id].elems,
|
|
|
|
req->links[link_id].elems_len);
|
|
|
|
assoc_data->link[link_id].elems = assoc_data->ie_pos;
|
|
|
|
assoc_data->link[link_id].elems_len = req->links[link_id].elems_len;
|
|
|
|
assoc_data->ie_pos += req->links[link_id].elems_len;
|
|
|
|
}
|
2022-07-12 21:21:23 +08:00
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
ht_elem = ieee80211_bss_get_elem(cbss, WLAN_EID_HT_OPERATION);
|
|
|
|
if (ht_elem && ht_elem->datalen >= sizeof(struct ieee80211_ht_operation))
|
2022-06-02 03:17:34 +08:00
|
|
|
assoc_data->link[link_id].ap_ht_param =
|
2022-07-12 21:21:23 +08:00
|
|
|
((struct ieee80211_ht_operation *)(ht_elem->data))->ht_param;
|
|
|
|
else if (!is_6ghz)
|
|
|
|
conn_flags |= IEEE80211_CONN_DISABLE_HT;
|
|
|
|
vht_elem = ieee80211_bss_get_elem(cbss, WLAN_EID_VHT_CAPABILITY);
|
|
|
|
if (vht_elem && vht_elem->datalen >= sizeof(struct ieee80211_vht_cap)) {
|
2022-06-02 03:17:34 +08:00
|
|
|
memcpy(&assoc_data->link[link_id].ap_vht_cap, vht_elem->data,
|
2022-07-12 21:21:23 +08:00
|
|
|
sizeof(struct ieee80211_vht_cap));
|
|
|
|
} else if (is_5ghz) {
|
|
|
|
link_info(link,
|
|
|
|
"VHT capa missing/short, disabling VHT/HE/EHT\n");
|
|
|
|
conn_flags |= IEEE80211_CONN_DISABLE_VHT |
|
|
|
|
IEEE80211_CONN_DISABLE_HE |
|
|
|
|
IEEE80211_CONN_DISABLE_EHT;
|
|
|
|
}
|
|
|
|
rcu_read_unlock();
|
|
|
|
|
|
|
|
link->u.mgd.beacon_crc_valid = false;
|
|
|
|
link->u.mgd.dtim_period = 0;
|
|
|
|
link->u.mgd.have_beacon = false;
|
|
|
|
|
|
|
|
/* override HT/VHT configuration only if the AP and we support it */
|
|
|
|
if (!(conn_flags & IEEE80211_CONN_DISABLE_HT)) {
|
|
|
|
struct ieee80211_sta_ht_cap sta_ht_cap;
|
|
|
|
|
|
|
|
memcpy(&sta_ht_cap, &sband->ht_cap, sizeof(sta_ht_cap));
|
|
|
|
ieee80211_apply_htcap_overrides(sdata, &sta_ht_cap);
|
|
|
|
}
|
|
|
|
|
2023-01-27 19:39:31 +08:00
|
|
|
link->conf->eht_puncturing = 0;
|
|
|
|
|
2022-07-12 21:21:23 +08:00
|
|
|
rcu_read_lock();
|
|
|
|
beacon_ies = rcu_dereference(cbss->beacon_ies);
|
|
|
|
if (beacon_ies) {
|
2023-01-27 19:39:31 +08:00
|
|
|
const struct ieee80211_eht_operation *eht_oper;
|
2022-07-12 21:21:23 +08:00
|
|
|
const struct element *elem;
|
|
|
|
u8 dtim_count = 0;
|
|
|
|
|
|
|
|
ieee80211_get_dtim(beacon_ies, &dtim_count,
|
|
|
|
&link->u.mgd.dtim_period);
|
|
|
|
|
|
|
|
sdata->deflink.u.mgd.have_beacon = true;
|
|
|
|
|
|
|
|
if (ieee80211_hw_check(&local->hw, TIMING_BEACON_ONLY)) {
|
|
|
|
link->conf->sync_tsf = beacon_ies->tsf;
|
|
|
|
link->conf->sync_device_ts = bss->device_ts_beacon;
|
|
|
|
link->conf->sync_dtim_count = dtim_count;
|
|
|
|
}
|
|
|
|
|
|
|
|
elem = cfg80211_find_ext_elem(WLAN_EID_EXT_MULTIPLE_BSSID_CONFIGURATION,
|
|
|
|
beacon_ies->data, beacon_ies->len);
|
|
|
|
if (elem && elem->datalen >= 3)
|
|
|
|
link->conf->profile_periodicity = elem->data[2];
|
|
|
|
else
|
|
|
|
link->conf->profile_periodicity = 0;
|
|
|
|
|
|
|
|
elem = cfg80211_find_elem(WLAN_EID_EXT_CAPABILITY,
|
|
|
|
beacon_ies->data, beacon_ies->len);
|
|
|
|
if (elem && elem->datalen >= 11 &&
|
|
|
|
(elem->data[10] & WLAN_EXT_CAPA11_EMA_SUPPORT))
|
|
|
|
link->conf->ema_ap = true;
|
|
|
|
else
|
|
|
|
link->conf->ema_ap = false;
|
2023-01-27 19:39:31 +08:00
|
|
|
|
|
|
|
elem = cfg80211_find_ext_elem(WLAN_EID_EXT_EHT_OPERATION,
|
|
|
|
beacon_ies->data, beacon_ies->len);
|
|
|
|
eht_oper = (const void *)(elem->data + 1);
|
|
|
|
|
|
|
|
if (elem &&
|
|
|
|
ieee80211_eht_oper_size_ok((const void *)(elem->data + 1),
|
|
|
|
elem->datalen - 1) &&
|
|
|
|
(eht_oper->params & IEEE80211_EHT_OPER_INFO_PRESENT) &&
|
|
|
|
(eht_oper->params & IEEE80211_EHT_OPER_DISABLED_SUBCHANNEL_BITMAP_PRESENT)) {
|
|
|
|
const struct ieee80211_eht_operation_info *info =
|
|
|
|
(void *)eht_oper->optional;
|
|
|
|
const u8 *disable_subchannel_bitmap = info->optional;
|
|
|
|
u16 bitmap;
|
|
|
|
|
|
|
|
bitmap = get_unaligned_le16(disable_subchannel_bitmap);
|
2023-01-31 08:12:24 +08:00
|
|
|
if (cfg80211_valid_disable_subchannel_bitmap(&bitmap,
|
|
|
|
&link->conf->chandef))
|
2023-01-27 19:39:31 +08:00
|
|
|
ieee80211_handle_puncturing_bitmap(link,
|
|
|
|
eht_oper,
|
|
|
|
bitmap,
|
|
|
|
NULL);
|
|
|
|
else
|
|
|
|
conn_flags |= IEEE80211_CONN_DISABLE_EHT;
|
|
|
|
}
|
2022-07-12 21:21:23 +08:00
|
|
|
}
|
|
|
|
rcu_read_unlock();
|
|
|
|
|
|
|
|
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";
|
|
|
|
}
|
|
|
|
sdata_info(sdata, "associating to AP %pM with corrupt %s\n",
|
|
|
|
cbss->bssid, corrupt_type);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (link->u.mgd.req_smps == IEEE80211_SMPS_AUTOMATIC) {
|
|
|
|
if (sdata->u.mgd.powersave)
|
|
|
|
link->smps_mode = IEEE80211_SMPS_DYNAMIC;
|
|
|
|
else
|
|
|
|
link->smps_mode = IEEE80211_SMPS_OFF;
|
|
|
|
} else {
|
|
|
|
link->smps_mode = link->u.mgd.req_smps;
|
|
|
|
}
|
|
|
|
|
|
|
|
return conn_flags;
|
|
|
|
}
|
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
|
|
|
|
struct cfg80211_assoc_request *req)
|
2007-05-06 02:45:53 +08:00
|
|
|
{
|
2022-06-02 03:17:34 +08:00
|
|
|
unsigned int assoc_link_id = req->link_id < 0 ? 0 : req->link_id;
|
2012-01-20 20:55:27 +08:00
|
|
|
struct ieee80211_local *local = sdata->local;
|
2009-07-07 09:45:17 +08:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2012-01-20 20:55:27 +08:00
|
|
|
struct ieee80211_mgd_assoc_data *assoc_data;
|
2022-07-12 21:21:23 +08:00
|
|
|
const struct element *ssid_elem;
|
2022-06-02 03:17:34 +08:00
|
|
|
struct ieee80211_vif_cfg *vif_cfg = &sdata->vif.cfg;
|
2022-07-12 21:21:23 +08:00
|
|
|
ieee80211_conn_flags_t conn_flags = 0;
|
2022-06-02 03:17:34 +08:00
|
|
|
struct ieee80211_link_data *link;
|
|
|
|
struct cfg80211_bss *cbss;
|
|
|
|
struct ieee80211_bss *bss;
|
2022-07-12 21:21:23 +08:00
|
|
|
bool override;
|
2022-06-02 03:17:34 +08:00
|
|
|
int i, err;
|
|
|
|
size_t size = sizeof(*assoc_data) + req->ie_len;
|
|
|
|
|
|
|
|
for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++)
|
|
|
|
size += req->links[i].elems_len;
|
|
|
|
|
2022-09-02 22:12:58 +08:00
|
|
|
/* FIXME: no support for 4-addr MLO yet */
|
|
|
|
if (sdata->u.mgd.use_4addr && req->link_id >= 0)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
assoc_data = kzalloc(size, GFP_KERNEL);
|
2012-01-20 20:55:27 +08:00
|
|
|
if (!assoc_data)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
cbss = req->link_id < 0 ? req->bss : req->links[req->link_id].bss;
|
|
|
|
|
2012-11-29 08:25:20 +08:00
|
|
|
rcu_read_lock();
|
2022-06-02 03:17:34 +08:00
|
|
|
ssid_elem = ieee80211_bss_get_elem(cbss, WLAN_EID_SSID);
|
2021-09-30 19:11:31 +08:00
|
|
|
if (!ssid_elem || ssid_elem->datalen > sizeof(assoc_data->ssid)) {
|
2012-11-29 08:25:20 +08:00
|
|
|
rcu_read_unlock();
|
|
|
|
kfree(assoc_data);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2021-09-30 19:11:31 +08:00
|
|
|
memcpy(assoc_data->ssid, ssid_elem->data, ssid_elem->datalen);
|
|
|
|
assoc_data->ssid_len = ssid_elem->datalen;
|
wifi: mac80211: move interface config to new struct
We'll use bss_conf for per-link configuration later, so
move out all the non-link-specific data out into a new
struct ieee80211_vif_cfg used in the vif.
Some adjustments were done with the following spatch:
@@
expression sdata;
struct ieee80211_vif *vifp;
identifier var = { assoc, ibss_joined, aid, arp_addr_list, arp_addr_cnt, ssid, ssid_len, s1g, ibss_creator };
@@
(
-sdata->vif.bss_conf.var
+sdata->vif.cfg.var
|
-vifp->bss_conf.var
+vifp->cfg.var
)
@bss_conf@
struct ieee80211_bss_conf *bss_conf;
identifier var = { assoc, ibss_joined, aid, arp_addr_list, arp_addr_cnt, ssid, ssid_len, s1g, ibss_creator };
@@
-bss_conf->var
+vif_cfg->var
(though more manual fixups were needed, e.g. replacing
"vif_cfg->" by "vif->cfg." in many files.)
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
2022-05-10 23:05:04 +08:00
|
|
|
memcpy(vif_cfg->ssid, assoc_data->ssid, assoc_data->ssid_len);
|
|
|
|
vif_cfg->ssid_len = assoc_data->ssid_len;
|
2012-11-29 08:25:20 +08:00
|
|
|
rcu_read_unlock();
|
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
if (req->ap_mld_addr) {
|
|
|
|
for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) {
|
|
|
|
if (!req->links[i].bss)
|
|
|
|
continue;
|
|
|
|
link = sdata_dereference(sdata->link[i], sdata);
|
|
|
|
if (link)
|
|
|
|
ether_addr_copy(assoc_data->link[i].addr,
|
|
|
|
link->conf->addr);
|
|
|
|
else
|
|
|
|
eth_random_addr(assoc_data->link[i].addr);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
memcpy(assoc_data->link[0].addr, sdata->vif.addr, ETH_ALEN);
|
|
|
|
}
|
|
|
|
|
|
|
|
assoc_data->s1g = cbss->channel->band == NL80211_BAND_S1GHZ;
|
|
|
|
|
|
|
|
memcpy(assoc_data->ap_addr,
|
|
|
|
req->ap_mld_addr ?: req->bss->bssid,
|
|
|
|
ETH_ALEN);
|
|
|
|
|
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-11 03:38:36 +08:00
|
|
|
if (ifmgd->associated) {
|
|
|
|
u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
|
|
|
|
|
2016-01-25 21:46:36 +08:00
|
|
|
sdata_info(sdata,
|
|
|
|
"disconnect from AP %pM for new assoc to %pM\n",
|
2022-06-02 03:17:34 +08:00
|
|
|
sdata->vif.cfg.ap_addr, assoc_data->ap_addr);
|
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-11 03:38:36 +08:00
|
|
|
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
|
|
|
|
WLAN_REASON_UNSPECIFIED,
|
|
|
|
false, frame_buf);
|
|
|
|
|
2015-03-17 05:23:37 +08:00
|
|
|
ieee80211_report_disconnect(sdata, frame_buf,
|
|
|
|
sizeof(frame_buf), true,
|
2020-12-06 20:54:43 +08:00
|
|
|
WLAN_REASON_UNSPECIFIED,
|
|
|
|
false);
|
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-11 03:38:36 +08:00
|
|
|
}
|
2012-01-20 20:55:27 +08:00
|
|
|
|
|
|
|
if (ifmgd->auth_data && !ifmgd->auth_data->done) {
|
|
|
|
err = -EBUSY;
|
|
|
|
goto err_free;
|
2009-12-23 20:15:35 +08:00
|
|
|
}
|
2009-07-07 09:45:17 +08:00
|
|
|
|
2012-01-20 20:55:27 +08:00
|
|
|
if (ifmgd->assoc_data) {
|
|
|
|
err = -EBUSY;
|
|
|
|
goto err_free;
|
|
|
|
}
|
2009-07-07 09:45:17 +08:00
|
|
|
|
2012-01-20 20:55:27 +08:00
|
|
|
if (ifmgd->auth_data) {
|
|
|
|
bool match;
|
|
|
|
|
|
|
|
/* keep sta info, bssid if matching */
|
2022-06-02 03:17:34 +08:00
|
|
|
match = ether_addr_equal(ifmgd->auth_data->ap_addr,
|
2022-09-22 05:01:46 +08:00
|
|
|
assoc_data->ap_addr) &&
|
|
|
|
ifmgd->auth_data->link_id == req->link_id;
|
2012-01-20 20:55:27 +08:00
|
|
|
ieee80211_destroy_auth_data(sdata, match);
|
2011-08-17 20:18:15 +08:00
|
|
|
}
|
|
|
|
|
2012-01-20 20:55:27 +08:00
|
|
|
/* prepare assoc data */
|
2013-10-15 18:25:07 +08:00
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
bss = (void *)cbss->priv;
|
2013-10-15 18:25:07 +08:00
|
|
|
assoc_data->wmm = bss->wmm_used &&
|
|
|
|
(local->hw.queues >= IEEE80211_NUM_ACS);
|
|
|
|
|
2012-03-08 22:02:03 +08: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 21:57:00 +08:00
|
|
|
for (i = 0; i < req->crypto.n_ciphers_pairwise; i++) {
|
2009-07-07 09:45:17 +08:00
|
|
|
if (req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP40 ||
|
|
|
|
req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_TKIP ||
|
2012-05-30 21:57:00 +08:00
|
|
|
req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104) {
|
2022-07-12 21:21:23 +08:00
|
|
|
conn_flags |= IEEE80211_CONN_DISABLE_HT;
|
|
|
|
conn_flags |= IEEE80211_CONN_DISABLE_VHT;
|
|
|
|
conn_flags |= IEEE80211_CONN_DISABLE_HE;
|
|
|
|
conn_flags |= IEEE80211_CONN_DISABLE_EHT;
|
2012-05-30 21:57:00 +08:00
|
|
|
netdev_info(sdata->dev,
|
2019-08-30 19:24:46 +08:00
|
|
|
"disabling HT/VHT/HE due to WEP/TKIP use\n");
|
2012-05-30 21:57:00 +08:00
|
|
|
}
|
|
|
|
}
|
2009-07-07 09:45:17 +08:00
|
|
|
|
2022-02-15 00:30:00 +08:00
|
|
|
/* also disable HT/VHT/HE/EHT if the AP doesn't use WMM */
|
2020-01-31 19:12:39 +08:00
|
|
|
if (!bss->wmm_used) {
|
2022-07-12 21:21:23 +08:00
|
|
|
conn_flags |= IEEE80211_CONN_DISABLE_HT;
|
|
|
|
conn_flags |= IEEE80211_CONN_DISABLE_VHT;
|
|
|
|
conn_flags |= IEEE80211_CONN_DISABLE_HE;
|
|
|
|
conn_flags |= IEEE80211_CONN_DISABLE_EHT;
|
2020-01-31 19:12:39 +08:00
|
|
|
netdev_info(sdata->dev,
|
|
|
|
"disabling HT/VHT/HE as WMM/QoS is not supported by the AP\n");
|
2012-07-24 11:33:10 +08:00
|
|
|
}
|
|
|
|
|
2022-07-12 21:21:23 +08:00
|
|
|
if (req->flags & ASSOC_REQ_DISABLE_HT) {
|
|
|
|
mlme_dbg(sdata, "HT disabled by flag, disabling HT/VHT/HE\n");
|
|
|
|
conn_flags |= IEEE80211_CONN_DISABLE_HT;
|
|
|
|
conn_flags |= IEEE80211_CONN_DISABLE_VHT;
|
|
|
|
conn_flags |= IEEE80211_CONN_DISABLE_HE;
|
|
|
|
conn_flags |= IEEE80211_CONN_DISABLE_EHT;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (req->flags & ASSOC_REQ_DISABLE_VHT) {
|
|
|
|
mlme_dbg(sdata, "VHT disabled by flag, disabling VHT\n");
|
|
|
|
conn_flags |= IEEE80211_CONN_DISABLE_VHT;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (req->flags & ASSOC_REQ_DISABLE_HE) {
|
|
|
|
mlme_dbg(sdata, "HE disabled by flag, disabling HE/EHT\n");
|
|
|
|
conn_flags |= IEEE80211_CONN_DISABLE_HE;
|
|
|
|
conn_flags |= IEEE80211_CONN_DISABLE_EHT;
|
|
|
|
}
|
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
if (req->flags & ASSOC_REQ_DISABLE_EHT)
|
|
|
|
conn_flags |= IEEE80211_CONN_DISABLE_EHT;
|
|
|
|
|
2011-11-19 03:32:00 +08: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-22 00:40:19 +08: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));
|
|
|
|
|
2020-09-22 10:28:05 +08:00
|
|
|
memcpy(&ifmgd->s1g_capa, &req->s1g_capa, sizeof(ifmgd->s1g_capa));
|
|
|
|
memcpy(&ifmgd->s1g_capa_mask, &req->s1g_capa_mask,
|
|
|
|
sizeof(ifmgd->s1g_capa_mask));
|
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
if (req->ie && req->ie_len) {
|
2012-01-20 20:55:27 +08:00
|
|
|
memcpy(assoc_data->ie, req->ie, req->ie_len);
|
|
|
|
assoc_data->ie_len = req->ie_len;
|
2022-06-02 03:17:34 +08:00
|
|
|
assoc_data->ie_pos = assoc_data->ie + assoc_data->ie_len;
|
|
|
|
} else {
|
|
|
|
assoc_data->ie_pos = assoc_data->ie;
|
2012-01-20 20:55:27 +08:00
|
|
|
}
|
2009-12-23 20:15:34 +08:00
|
|
|
|
2016-10-27 05:42:05 +08:00
|
|
|
if (req->fils_kek) {
|
|
|
|
/* should already be checked in cfg80211 - so warn */
|
|
|
|
if (WARN_ON(req->fils_kek_len > FILS_MAX_KEK_LEN)) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto err_free;
|
|
|
|
}
|
|
|
|
memcpy(assoc_data->fils_kek, req->fils_kek,
|
|
|
|
req->fils_kek_len);
|
|
|
|
assoc_data->fils_kek_len = req->fils_kek_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (req->fils_nonces)
|
|
|
|
memcpy(assoc_data->fils_nonces, req->fils_nonces,
|
|
|
|
2 * FILS_NONCE_LEN);
|
|
|
|
|
2022-07-12 21:21:23 +08:00
|
|
|
/* default timeout */
|
|
|
|
assoc_data->timeout = jiffies;
|
|
|
|
assoc_data->timeout_started = true;
|
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
assoc_data->assoc_link_id = assoc_link_id;
|
|
|
|
|
|
|
|
if (req->ap_mld_addr) {
|
|
|
|
for (i = 0; i < ARRAY_SIZE(assoc_data->link); i++) {
|
|
|
|
assoc_data->link[i].conn_flags = conn_flags;
|
|
|
|
assoc_data->link[i].bss = req->links[i].bss;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if there was no authentication, set up the link */
|
|
|
|
err = ieee80211_vif_set_links(sdata, BIT(assoc_link_id));
|
|
|
|
if (err)
|
|
|
|
goto err_clear;
|
|
|
|
} else {
|
|
|
|
assoc_data->link[0].conn_flags = conn_flags;
|
|
|
|
assoc_data->link[0].bss = cbss;
|
|
|
|
}
|
|
|
|
|
|
|
|
link = sdata_dereference(sdata->link[assoc_link_id], sdata);
|
|
|
|
if (WARN_ON(!link)) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto err_clear;
|
|
|
|
}
|
|
|
|
|
2022-07-18 00:16:51 +08:00
|
|
|
/* keep old conn_flags from ieee80211_prep_channel() from auth */
|
|
|
|
conn_flags |= link->u.mgd.conn_flags;
|
2022-07-12 21:21:23 +08:00
|
|
|
conn_flags |= ieee80211_setup_assoc_link(sdata, assoc_data, req,
|
2022-06-02 03:17:34 +08:00
|
|
|
conn_flags, assoc_link_id);
|
2022-07-12 21:21:23 +08:00
|
|
|
override = link->u.mgd.conn_flags != conn_flags;
|
|
|
|
link->u.mgd.conn_flags |= conn_flags;
|
2009-12-23 20:15:33 +08:00
|
|
|
|
2014-11-11 19:48:42 +08:00
|
|
|
if (WARN((sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_UAPSD) &&
|
2015-06-03 03:39:54 +08:00
|
|
|
ieee80211_hw_check(&local->hw, PS_NULLFUNC_STACK),
|
2014-11-11 19:48:42 +08:00
|
|
|
"U-APSD not supported with HW_PS_NULLFUNC_STACK\n"))
|
|
|
|
sdata->vif.driver_flags &= ~IEEE80211_VIF_SUPPORTS_UAPSD;
|
|
|
|
|
2010-01-12 16:42:31 +08:00
|
|
|
if (bss->wmm_used && bss->uapsd_supported &&
|
2014-11-11 19:48:42 +08:00
|
|
|
(sdata->vif.driver_flags & IEEE80211_VIF_SUPPORTS_UAPSD)) {
|
2012-03-08 22:02:05 +08:00
|
|
|
assoc_data->uapsd = true;
|
2010-01-12 16:42:31 +08:00
|
|
|
ifmgd->flags |= IEEE80211_STA_UAPSD_ENABLED;
|
|
|
|
} else {
|
2012-03-08 22:02:05 +08:00
|
|
|
assoc_data->uapsd = false;
|
2010-01-12 16:42:31 +08:00
|
|
|
ifmgd->flags &= ~IEEE80211_STA_UAPSD_ENABLED;
|
|
|
|
}
|
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
if (req->prev_bssid)
|
2022-06-02 03:17:34 +08:00
|
|
|
memcpy(assoc_data->prev_ap_addr, req->prev_bssid, ETH_ALEN);
|
2009-07-07 09:45:17 +08: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 20:25:02 +08:00
|
|
|
if (req->flags & ASSOC_REQ_USE_RRM)
|
|
|
|
ifmgd->flags |= IEEE80211_STA_ENABLE_RRM;
|
|
|
|
else
|
|
|
|
ifmgd->flags &= ~IEEE80211_STA_ENABLE_RRM;
|
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
if (req->crypto.control_port)
|
|
|
|
ifmgd->flags |= IEEE80211_STA_CONTROL_PORT;
|
|
|
|
else
|
|
|
|
ifmgd->flags &= ~IEEE80211_STA_CONTROL_PORT;
|
|
|
|
|
2010-08-27 19:26:54 +08:00
|
|
|
sdata->control_port_protocol = req->crypto.control_port_ethertype;
|
|
|
|
sdata->control_port_no_encrypt = req->crypto.control_port_no_encrypt;
|
2018-03-27 01:52:51 +08:00
|
|
|
sdata->control_port_over_nl80211 =
|
|
|
|
req->crypto.control_port_over_nl80211;
|
2020-03-12 17:10:54 +08:00
|
|
|
sdata->control_port_no_preauth = req->crypto.control_port_no_preauth;
|
2010-08-27 19:26:54 +08:00
|
|
|
|
2012-01-20 20:55:27 +08:00
|
|
|
/* kick off associate process */
|
|
|
|
ifmgd->assoc_data = assoc_data;
|
2021-02-04 22:46:10 +08:00
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
for (i = 0; i < ARRAY_SIZE(assoc_data->link); i++) {
|
|
|
|
if (!assoc_data->link[i].bss)
|
|
|
|
continue;
|
|
|
|
if (i == assoc_data->assoc_link_id)
|
|
|
|
continue;
|
|
|
|
/* only calculate the flags, hence link == NULL */
|
|
|
|
err = ieee80211_prep_channel(sdata, NULL, assoc_data->link[i].bss,
|
|
|
|
&assoc_data->link[i].conn_flags);
|
|
|
|
if (err)
|
|
|
|
goto err_clear;
|
|
|
|
}
|
2022-03-24 06:46:36 +08:00
|
|
|
|
2022-07-22 05:35:21 +08:00
|
|
|
/* needed for transmitting the assoc frames properly */
|
|
|
|
memcpy(sdata->vif.cfg.ap_addr, assoc_data->ap_addr, ETH_ALEN);
|
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
err = ieee80211_prep_connection(sdata, cbss, req->link_id,
|
|
|
|
req->ap_mld_addr, true, override);
|
2012-03-08 22:02:07 +08:00
|
|
|
if (err)
|
|
|
|
goto err_clear;
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2022-06-02 03:17:34 +08:00
|
|
|
assoc_data->link[assoc_data->assoc_link_id].conn_flags =
|
|
|
|
link->u.mgd.conn_flags;
|
2021-06-18 18:41:34 +08:00
|
|
|
|
2022-07-12 23:21:22 +08:00
|
|
|
if (ieee80211_hw_check(&sdata->local->hw, NEED_DTIM_BEFORE_ASSOC)) {
|
|
|
|
const struct cfg80211_bss_ies *beacon_ies;
|
2012-12-10 22:38:14 +08:00
|
|
|
|
2022-07-12 23:21:22 +08:00
|
|
|
rcu_read_lock();
|
|
|
|
beacon_ies = rcu_dereference(req->bss->beacon_ies);
|
|
|
|
|
|
|
|
if (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",
|
|
|
|
link->u.mgd.bssid);
|
|
|
|
assoc_data->timeout = TU_TO_EXP_TIME(req->bss->beacon_interval);
|
|
|
|
assoc_data->timeout_started = true;
|
|
|
|
assoc_data->need_beacon = true;
|
|
|
|
}
|
|
|
|
rcu_read_unlock();
|
2012-01-20 20:55:27 +08:00
|
|
|
}
|
2012-12-12 16:12:24 +08:00
|
|
|
|
2013-05-10 18:32:47 +08:00
|
|
|
run_again(sdata, assoc_data->timeout);
|
2012-01-20 20:55:27 +08:00
|
|
|
|
2013-05-10 18:32:47 +08:00
|
|
|
return 0;
|
2012-01-20 20:55:27 +08:00
|
|
|
err_clear:
|
2022-07-12 19:46:19 +08:00
|
|
|
eth_zero_addr(sdata->deflink.u.mgd.bssid);
|
2022-06-18 04:36:37 +08:00
|
|
|
ieee80211_link_info_change_notify(sdata, &sdata->deflink,
|
|
|
|
BSS_CHANGED_BSSID);
|
2012-01-20 20:55:27 +08:00
|
|
|
ifmgd->assoc_data = NULL;
|
|
|
|
err_free:
|
|
|
|
kfree(assoc_data);
|
|
|
|
return err;
|
2007-05-06 02:45:53 +08:00
|
|
|
}
|
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
|
2012-02-24 20:50:51 +08:00
|
|
|
struct cfg80211_deauth_request *req)
|
2007-05-06 02:45:53 +08:00
|
|
|
{
|
2009-02-15 19:44:28 +08:00
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
2012-09-07 19:28:52 +08:00
|
|
|
u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
|
2012-10-15 20:52:41 +08:00
|
|
|
bool tx = !req->local_state_change;
|
2021-06-18 18:41:55 +08:00
|
|
|
struct ieee80211_prep_tx_info info = {
|
|
|
|
.subtype = IEEE80211_STYPE_DEAUTH,
|
|
|
|
};
|
2007-05-06 02:45:53 +08:00
|
|
|
|
2014-03-19 16:11:19 +08:00
|
|
|
if (ifmgd->auth_data &&
|
2022-06-02 03:17:34 +08:00
|
|
|
ether_addr_equal(ifmgd->auth_data->ap_addr, req->bssid)) {
|
2014-03-19 16:11:19 +08:00
|
|
|
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 20:45:41 +08:00
|
|
|
|
2021-06-18 18:41:55 +08:00
|
|
|
drv_mgd_prepare_tx(sdata->local, sdata, &info);
|
2019-08-30 19:24:51 +08:00
|
|
|
ieee80211_send_deauth_disassoc(sdata, req->bssid, req->bssid,
|
2012-02-24 20:50:54 +08:00
|
|
|
IEEE80211_STYPE_DEAUTH,
|
2012-10-15 20:52:41 +08:00
|
|
|
req->reason_code, tx,
|
2012-02-24 20:50:54 +08:00
|
|
|
frame_buf);
|
2012-10-29 16:46:31 +08:00
|
|
|
ieee80211_destroy_auth_data(sdata, false);
|
2015-03-17 05:23:37 +08:00
|
|
|
ieee80211_report_disconnect(sdata, frame_buf,
|
|
|
|
sizeof(frame_buf), true,
|
2020-12-06 20:54:43 +08:00
|
|
|
req->reason_code, false);
|
2021-06-18 18:41:55 +08:00
|
|
|
drv_mgd_complete_tx(sdata->local, sdata, &info);
|
2014-03-19 16:11:19 +08:00
|
|
|
return 0;
|
2012-07-25 06:42:36 +08:00
|
|
|
}
|
|
|
|
|
2015-10-25 16:59:38 +08:00
|
|
|
if (ifmgd->assoc_data &&
|
2022-06-02 03:17:34 +08:00
|
|
|
ether_addr_equal(ifmgd->assoc_data->ap_addr, req->bssid)) {
|
2015-10-25 16:59:38 +08:00
|
|
|
sdata_info(sdata,
|
|
|
|
"aborting association with %pM by local choice (Reason: %u=%s)\n",
|
|
|
|
req->bssid, req->reason_code,
|
|
|
|
ieee80211_get_reason_code_string(req->reason_code));
|
|
|
|
|
2021-06-18 18:41:55 +08:00
|
|
|
drv_mgd_prepare_tx(sdata->local, sdata, &info);
|
2019-08-30 19:24:51 +08:00
|
|
|
ieee80211_send_deauth_disassoc(sdata, req->bssid, req->bssid,
|
2015-10-25 16:59:38 +08:00
|
|
|
IEEE80211_STYPE_DEAUTH,
|
|
|
|
req->reason_code, tx,
|
|
|
|
frame_buf);
|
2022-06-28 04:23:42 +08:00
|
|
|
ieee80211_destroy_assoc_data(sdata, ASSOC_ABANDON);
|
2015-10-25 16:59:38 +08:00
|
|
|
ieee80211_report_disconnect(sdata, frame_buf,
|
|
|
|
sizeof(frame_buf), true,
|
2020-12-06 20:54:43 +08:00
|
|
|
req->reason_code, false);
|
2015-10-25 16:59:38 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-10-29 16:46:31 +08:00
|
|
|
if (ifmgd->associated &&
|
2022-06-24 22:08:39 +08:00
|
|
|
ether_addr_equal(sdata->vif.cfg.ap_addr, req->bssid)) {
|
2014-03-19 16:11:19 +08: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 16:46:31 +08:00
|
|
|
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
|
|
|
|
req->reason_code, tx, frame_buf);
|
2015-03-17 05:23:37 +08:00
|
|
|
ieee80211_report_disconnect(sdata, frame_buf,
|
|
|
|
sizeof(frame_buf), true,
|
2020-12-06 20:54:43 +08:00
|
|
|
req->reason_code, false);
|
2021-06-18 18:41:55 +08:00
|
|
|
drv_mgd_complete_tx(sdata->local, sdata, &info);
|
2014-03-19 16:11:19 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2012-10-29 16:46:31 +08:00
|
|
|
|
2014-03-19 16:11:19 +08:00
|
|
|
return -ENOTCONN;
|
2007-05-06 02:45:53 +08:00
|
|
|
}
|
2008-04-05 07:59:58 +08:00
|
|
|
|
2009-07-07 09:45:17 +08:00
|
|
|
int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
|
2012-02-24 20:50:51 +08:00
|
|
|
struct cfg80211_disassoc_request *req)
|
2008-09-11 06:01:52 +08:00
|
|
|
{
|
2012-09-07 19:28:52 +08:00
|
|
|
u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
|
2008-09-11 06:01:52 +08:00
|
|
|
|
2022-06-27 22:19:18 +08:00
|
|
|
if (!sdata->u.mgd.associated ||
|
|
|
|
memcmp(sdata->vif.cfg.ap_addr, req->ap_addr, ETH_ALEN))
|
|
|
|
return -ENOTCONN;
|
2009-07-07 09:45:17 +08:00
|
|
|
|
2012-06-22 17:29:50 +08:00
|
|
|
sdata_info(sdata,
|
2014-02-12 02:36:24 +08:00
|
|
|
"disassociating from %pM by local choice (Reason: %u=%s)\n",
|
2022-06-27 22:19:18 +08:00
|
|
|
req->ap_addr, req->reason_code,
|
|
|
|
ieee80211_get_reason_code_string(req->reason_code));
|
2009-09-26 20:45:41 +08:00
|
|
|
|
2012-02-24 20:50:54 +08:00
|
|
|
ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DISASSOC,
|
|
|
|
req->reason_code, !req->local_state_change,
|
|
|
|
frame_buf);
|
2009-04-16 19:17:25 +08:00
|
|
|
|
2015-03-17 05:23:37 +08:00
|
|
|
ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true,
|
2020-12-06 20:54:43 +08:00
|
|
|
req->reason_code, false);
|
2009-11-29 19:19:06 +08:00
|
|
|
|
2009-04-16 19:17:25 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2010-02-15 18:53:10 +08:00
|
|
|
|
2022-06-24 16:57:41 +08:00
|
|
|
void ieee80211_mgd_stop_link(struct ieee80211_link_data *link)
|
|
|
|
{
|
|
|
|
cancel_work_sync(&link->u.mgd.request_smps_work);
|
2022-06-24 22:18:56 +08:00
|
|
|
cancel_work_sync(&link->u.mgd.chswitch_work);
|
2022-06-24 16:57:41 +08:00
|
|
|
}
|
|
|
|
|
2012-04-23 19:45:15 +08:00
|
|
|
void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata)
|
2012-02-26 04:48:08 +08:00
|
|
|
{
|
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
|
2013-02-21 01:41:09 +08: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->csa_connection_drop_work);
|
2014-07-17 22:14:25 +08:00
|
|
|
cancel_delayed_work_sync(&ifmgd->tdls_peer_del_work);
|
2013-02-21 01:41:09 +08:00
|
|
|
|
2013-05-10 18:32:47 +08:00
|
|
|
sdata_lock(sdata);
|
2022-06-28 04:23:42 +08:00
|
|
|
if (ifmgd->assoc_data)
|
|
|
|
ieee80211_destroy_assoc_data(sdata, ASSOC_TIMEOUT);
|
2012-02-26 04:48:08 +08:00
|
|
|
if (ifmgd->auth_data)
|
|
|
|
ieee80211_destroy_auth_data(sdata, false);
|
2014-11-10 00:50:08 +08: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;
|
|
|
|
}
|
2019-02-15 08:14:33 +08:00
|
|
|
kfree(ifmgd->assoc_req_ies);
|
|
|
|
ifmgd->assoc_req_ies = NULL;
|
|
|
|
ifmgd->assoc_req_ies_len = 0;
|
2014-11-10 00:50:08 +08:00
|
|
|
spin_unlock_bh(&ifmgd->teardown_lock);
|
2012-02-26 04:48:08 +08:00
|
|
|
del_timer_sync(&ifmgd->timer);
|
2013-05-10 18:32:47 +08:00
|
|
|
sdata_unlock(sdata);
|
2012-02-26 04:48:08 +08:00
|
|
|
}
|
|
|
|
|
2010-03-23 15:02:34 +08:00
|
|
|
void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif,
|
|
|
|
enum nl80211_cqm_rssi_threshold_event rssi_event,
|
2017-01-25 19:43:40 +08:00
|
|
|
s32 rssi_level,
|
2010-03-23 15:02:34 +08:00
|
|
|
gfp_t gfp)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
|
|
|
|
2017-01-25 19:43:40 +08:00
|
|
|
trace_api_cqm_rssi_notify(sdata, rssi_event, rssi_level);
|
2010-04-07 22:48:40 +08:00
|
|
|
|
2017-01-25 19:43:41 +08:00
|
|
|
cfg80211_cqm_rssi_notify(sdata->dev, rssi_event, rssi_level, gfp);
|
2010-03-23 15:02:34 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ieee80211_cqm_rssi_notify);
|
2014-11-26 19:42:02 +08: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);
|
2022-05-13 21:48:04 +08:00
|
|
|
|
|
|
|
static void _ieee80211_enable_rssi_reports(struct ieee80211_sub_if_data *sdata,
|
|
|
|
int rssi_min_thold,
|
|
|
|
int rssi_max_thold)
|
|
|
|
{
|
|
|
|
trace_api_enable_rssi_reports(sdata, rssi_min_thold, rssi_max_thold);
|
|
|
|
|
|
|
|
if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Scale up threshold values before storing it, as the RSSI averaging
|
|
|
|
* algorithm uses a scaled up value as well. Change this scaling
|
|
|
|
* factor if the RSSI averaging algorithm changes.
|
|
|
|
*/
|
|
|
|
sdata->u.mgd.rssi_min_thold = rssi_min_thold*16;
|
|
|
|
sdata->u.mgd.rssi_max_thold = rssi_max_thold*16;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ieee80211_enable_rssi_reports(struct ieee80211_vif *vif,
|
|
|
|
int rssi_min_thold,
|
|
|
|
int rssi_max_thold)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
|
|
|
|
|
|
|
WARN_ON(rssi_min_thold == rssi_max_thold ||
|
|
|
|
rssi_min_thold > rssi_max_thold);
|
|
|
|
|
|
|
|
_ieee80211_enable_rssi_reports(sdata, rssi_min_thold,
|
|
|
|
rssi_max_thold);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ieee80211_enable_rssi_reports);
|
|
|
|
|
|
|
|
void ieee80211_disable_rssi_reports(struct ieee80211_vif *vif)
|
|
|
|
{
|
|
|
|
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
|
|
|
|
|
|
|
_ieee80211_enable_rssi_reports(sdata, 0, 0);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(ieee80211_disable_rssi_reports);
|