2015-01-12 16:52:37 +01:00
|
|
|
/*
|
|
|
|
*
|
|
|
|
* Wireless daemon for Linux
|
|
|
|
*
|
2019-10-25 00:43:08 +02:00
|
|
|
* Copyright (C) 2015-2019 Intel Corporation. All rights reserved.
|
2015-01-12 16:52:37 +01:00
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include <config.h>
|
|
|
|
#endif
|
|
|
|
|
2018-11-01 22:37:11 +01:00
|
|
|
#define _GNU_SOURCE
|
2015-01-12 16:52:37 +01:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <errno.h>
|
2018-11-01 22:37:11 +01:00
|
|
|
#include <time.h>
|
2015-01-12 16:52:37 +01:00
|
|
|
#include <sys/socket.h>
|
2015-06-06 01:18:55 +02:00
|
|
|
#include <limits.h>
|
2015-01-12 16:52:37 +01:00
|
|
|
#include <linux/if.h>
|
|
|
|
#include <linux/if_ether.h>
|
|
|
|
#include <ell/ell.h>
|
|
|
|
|
2021-03-12 04:46:09 +01:00
|
|
|
#include "ell/useful.h"
|
2015-01-12 16:52:37 +01:00
|
|
|
#include "linux/nl80211.h"
|
|
|
|
#include "src/iwd.h"
|
2019-11-07 23:33:51 +01:00
|
|
|
#include "src/module.h"
|
2015-01-12 16:52:37 +01:00
|
|
|
#include "src/wiphy.h"
|
2015-01-28 13:14:53 +01:00
|
|
|
#include "src/ie.h"
|
2018-07-11 00:47:00 +02:00
|
|
|
#include "src/common.h"
|
|
|
|
#include "src/network.h"
|
2018-07-22 14:15:18 +02:00
|
|
|
#include "src/knownnetworks.h"
|
2019-07-15 21:04:43 +02:00
|
|
|
#include "src/nl80211cmd.h"
|
2019-10-16 23:33:40 +02:00
|
|
|
#include "src/nl80211util.h"
|
2018-07-11 00:47:01 +02:00
|
|
|
#include "src/util.h"
|
2019-11-21 21:15:56 +01:00
|
|
|
#include "src/p2putil.h"
|
2019-11-21 21:15:57 +01:00
|
|
|
#include "src/mpdu.h"
|
2021-09-22 21:34:16 +02:00
|
|
|
#include "src/band.h"
|
2015-01-12 16:52:37 +01:00
|
|
|
#include "src/scan.h"
|
|
|
|
|
2021-02-12 16:41:13 +01:00
|
|
|
/* User configurable options */
|
|
|
|
static double RANK_5G_FACTOR;
|
2022-11-13 15:07:35 +01:00
|
|
|
static double RANK_6G_FACTOR;
|
2021-02-12 16:41:13 +01:00
|
|
|
static uint32_t SCAN_MAX_INTERVAL;
|
2021-02-12 16:41:14 +01:00
|
|
|
static uint32_t SCAN_INIT_INTERVAL;
|
2021-02-12 16:41:13 +01:00
|
|
|
|
2019-05-01 17:32:30 +02:00
|
|
|
static struct l_queue *scan_contexts;
|
2015-06-05 05:31:39 +02:00
|
|
|
|
2019-05-01 17:32:30 +02:00
|
|
|
static struct l_genl_family *nl80211;
|
2020-07-10 19:44:43 +02:00
|
|
|
|
|
|
|
struct scan_context;
|
2015-06-01 16:28:05 +02:00
|
|
|
|
2015-09-20 21:58:23 +02:00
|
|
|
struct scan_periodic {
|
|
|
|
struct l_timeout *timeout;
|
|
|
|
uint16_t interval;
|
2016-06-25 20:42:23 +02:00
|
|
|
scan_trigger_func_t trigger;
|
2015-09-20 21:58:23 +02:00
|
|
|
scan_notify_func_t callback;
|
|
|
|
void *userdata;
|
2019-05-08 03:15:23 +02:00
|
|
|
uint32_t id;
|
2018-07-11 00:47:01 +02:00
|
|
|
bool needs_active_scan:1;
|
2015-09-22 21:05:50 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
struct scan_request {
|
2020-07-10 19:44:43 +02:00
|
|
|
struct scan_context *sc;
|
2015-09-22 21:05:50 +02:00
|
|
|
scan_trigger_func_t trigger;
|
|
|
|
scan_notify_func_t callback;
|
|
|
|
void *userdata;
|
|
|
|
scan_destroy_func_t destroy;
|
2022-01-19 23:00:41 +01:00
|
|
|
bool canceled : 1; /* Is scan_cancel being called on this request? */
|
2015-09-22 21:05:50 +02:00
|
|
|
bool passive:1; /* Active or Passive scan? */
|
2022-01-18 22:10:07 +01:00
|
|
|
bool started : 1; /* Has TRIGGER_SCAN succeeded at least once? */
|
2022-01-19 18:19:27 +01:00
|
|
|
/*
|
|
|
|
* Set to true if the TRIGGER_SCAN command at the head of the 'cmds'
|
|
|
|
* queue was acked by the kernel indicating that the scan request was
|
|
|
|
* successful. May be set and cleared multiple times during a
|
|
|
|
* the scan_request lifetime (as each command in the 'cmds' queue is
|
|
|
|
* issued to the kernel). Will be false if the current request
|
|
|
|
* was not started due to an -EBUSY error from the kernel. Also will
|
|
|
|
* be false when the scan is complete and GET_SCAN is pending.
|
|
|
|
*/
|
|
|
|
bool triggered : 1;
|
2022-01-19 21:39:27 +01:00
|
|
|
bool in_callback : 1; /* Scan request complete, re-entrancy guard */
|
2022-08-05 00:02:48 +02:00
|
|
|
/* The request was split anticipating 6GHz will become available */
|
|
|
|
bool split : 1;
|
2018-07-11 00:47:00 +02:00
|
|
|
struct l_queue *cmds;
|
2019-11-19 23:10:23 +01:00
|
|
|
/* The time the current scan was started. Reported in TRIGGER_SCAN */
|
|
|
|
uint64_t start_time_tsf;
|
2020-07-10 19:44:43 +02:00
|
|
|
struct wiphy_radio_work_item work;
|
2022-08-03 23:36:33 +02:00
|
|
|
/*
|
|
|
|
* List of frequencies scanned so far. Since the NEW_SCAN_RESULTS event
|
|
|
|
* contains frequencies of only the last CMD_TRIGGER we need to parse
|
|
|
|
* and save these since there may be additional scan commands to run.
|
|
|
|
*/
|
|
|
|
struct scan_freq_set *freqs_scanned;
|
2015-09-20 21:58:23 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
struct scan_context {
|
2019-07-08 16:02:59 +02:00
|
|
|
uint64_t wdev_id;
|
2022-08-05 19:45:17 +02:00
|
|
|
uint32_t wiphy_watch_id;
|
2019-05-08 03:15:23 +02:00
|
|
|
/*
|
|
|
|
* Tells us whether a scan, our own or external, is running.
|
|
|
|
* Set when scan gets triggered, cleared when scan done and
|
|
|
|
* before actual results are queried.
|
|
|
|
*/
|
2015-09-20 21:58:23 +02:00
|
|
|
enum scan_state state;
|
|
|
|
struct scan_periodic sp;
|
2015-09-22 21:05:50 +02:00
|
|
|
struct l_queue *requests;
|
2019-05-08 03:15:23 +02:00
|
|
|
/* Non-zero if SCAN_TRIGGER is still running */
|
scan: Cancel CMD_TRIGGER_SCAN when removing scan context
Save the ids of the netlink trigger scan commands that we send and
cancel them in scan_ifindex_remove to fix a race leading to a
segfault. The segfault would happen every time if scan_ifindex_remove
was called in the same main loop iteration in which we sent the
command, on shutdown:
^CTerminate
src/netdev.c:netdev_free() Freeing netdev wlan3[6]
src/device.c:device_disassociated() 6
src/device.c:device_enter_state() Old State: connected, new state:
disconnected
src/device.c:device_enter_state() Old State: disconnected, new state:
autoconnect
src/scan.c:scan_periodic_start() Starting periodic scan for ifindex: 6
src/device.c:device_free()
src/device.c:bss_free() Freeing BSS 02:00:00:00:00:00
src/device.c:bss_free() Freeing BSS 02:00:00:00:01:00
Removing scan context for ifindex: 6
src/scan.c:scan_context_free() sc: 0x5555557ca290
src/scan.c:scan_notify() Scan notification 33
src/netdev.c:netdev_operstate_down_cb() netdev: 6, success: 1
src/scan.c:scan_periodic_done()
src/scan.c:scan_periodic_done() Periodic scan triggered for ifindex:
1434209520
Program received signal SIGSEGV, Segmentation fault.
0x0000000000000064 in ?? ()
(gdb) bt
#0 0x0000000000000064 in ?? ()
#1 0x0000555555583560 in process_unicast (nlmsg=0x7fffffffc1a0,
genl=0x5555557c1d60) at ell/genl.c:390
#2 received_data (io=<optimized out>, user_data=0x5555557c1d60)
at ell/genl.c:506
#3 0x0000555555580d45 in io_callback (fd=<optimized out>,
events=1, user_data=0x5555557c1e60) at ell/io.c:120
#4 0x000055555558005f in l_main_run () at ell/main.c:381
#5 0x00005555555599c1 in main (argc=<optimized out>, argv=<optimized out>)
at src/main.c:259
2017-02-01 11:58:42 +01:00
|
|
|
unsigned int start_cmd_id;
|
2019-05-08 03:15:23 +02:00
|
|
|
/* Non-zero if GET_SCAN is still running */
|
|
|
|
unsigned int get_scan_cmd_id;
|
2021-03-15 18:29:28 +01:00
|
|
|
/*
|
|
|
|
* Special request used for getting scan results after the firmware
|
|
|
|
* roamed automatically.
|
|
|
|
*/
|
|
|
|
unsigned int get_fw_scan_cmd_id;
|
2018-05-24 19:12:12 +02:00
|
|
|
struct wiphy *wiphy;
|
2015-09-20 21:58:23 +02:00
|
|
|
};
|
2015-06-01 16:28:05 +02:00
|
|
|
|
|
|
|
struct scan_results {
|
2019-07-08 16:02:56 +02:00
|
|
|
struct scan_context *sc;
|
2015-06-01 16:28:05 +02:00
|
|
|
struct l_queue *bss_list;
|
2019-04-11 21:14:24 +02:00
|
|
|
uint64_t time_stamp;
|
2019-05-08 03:15:23 +02:00
|
|
|
struct scan_request *sr;
|
2022-08-05 17:03:03 +02:00
|
|
|
struct scan_freq_set *freqs;
|
2015-06-01 16:28:05 +02:00
|
|
|
};
|
|
|
|
|
2020-07-10 19:44:43 +02:00
|
|
|
static bool start_next_scan_request(struct wiphy_radio_work_item *item);
|
2017-03-14 16:11:33 +01:00
|
|
|
static void scan_periodic_rearm(struct scan_context *sc);
|
2015-09-22 21:05:50 +02:00
|
|
|
|
2015-09-20 21:58:23 +02:00
|
|
|
static bool scan_context_match(const void *a, const void *b)
|
|
|
|
{
|
|
|
|
const struct scan_context *sc = a;
|
2019-07-08 16:02:59 +02:00
|
|
|
const uint64_t *wdev_id = b;
|
2015-09-20 21:58:23 +02:00
|
|
|
|
2019-07-08 16:02:59 +02:00
|
|
|
return sc->wdev_id == *wdev_id;
|
2015-09-20 21:58:23 +02:00
|
|
|
}
|
|
|
|
|
2015-10-06 05:37:12 +02:00
|
|
|
static bool scan_request_match(const void *a, const void *b)
|
|
|
|
{
|
|
|
|
const struct scan_request *sr = a;
|
|
|
|
uint32_t id = L_PTR_TO_UINT(b);
|
|
|
|
|
2020-07-10 19:44:43 +02:00
|
|
|
return sr->work.id == id;
|
2015-10-06 05:37:12 +02:00
|
|
|
}
|
|
|
|
|
2020-07-10 19:44:43 +02:00
|
|
|
static void scan_request_free(struct wiphy_radio_work_item *item)
|
2015-09-22 21:05:50 +02:00
|
|
|
{
|
2020-07-10 19:44:43 +02:00
|
|
|
struct scan_request *sr = l_container_of(item, struct scan_request,
|
|
|
|
work);
|
2015-09-22 21:05:50 +02:00
|
|
|
|
2018-11-29 19:56:00 +01:00
|
|
|
if (sr->destroy)
|
|
|
|
sr->destroy(sr->userdata);
|
|
|
|
|
2018-07-11 23:20:29 +02:00
|
|
|
l_queue_destroy(sr->cmds, (l_queue_destroy_func_t) l_genl_msg_unref);
|
2018-07-11 00:47:00 +02:00
|
|
|
|
2022-08-03 23:36:33 +02:00
|
|
|
scan_freq_set_free(sr->freqs_scanned);
|
|
|
|
|
2018-07-11 00:47:00 +02:00
|
|
|
l_free(sr);
|
2015-09-22 21:05:50 +02:00
|
|
|
}
|
|
|
|
|
2018-12-04 03:44:34 +01:00
|
|
|
static void scan_request_failed(struct scan_context *sc,
|
|
|
|
struct scan_request *sr, int err)
|
2018-07-12 00:20:06 +02:00
|
|
|
{
|
2022-01-19 21:39:27 +01:00
|
|
|
sr->in_callback = true;
|
2018-12-04 03:44:34 +01:00
|
|
|
|
2018-07-12 00:20:06 +02:00
|
|
|
if (sr->trigger)
|
|
|
|
sr->trigger(err, sr->userdata);
|
2018-12-04 03:44:34 +01:00
|
|
|
else if (sr->callback)
|
2021-02-03 17:02:40 +01:00
|
|
|
sr->callback(err, NULL, NULL, sr->userdata);
|
2018-07-12 00:20:06 +02:00
|
|
|
|
2022-01-19 21:39:27 +01:00
|
|
|
sr->in_callback = false;
|
|
|
|
l_queue_remove(sc->requests, sr);
|
2020-07-10 19:44:43 +02:00
|
|
|
wiphy_radio_work_done(sc->wiphy, sr->work.id);
|
2018-07-12 00:20:06 +02:00
|
|
|
}
|
|
|
|
|
2020-07-10 19:44:43 +02:00
|
|
|
static void scan_request_cancel(void *data)
|
|
|
|
{
|
|
|
|
struct scan_request *sr = data;
|
|
|
|
|
|
|
|
wiphy_radio_work_done(sr->sc->wiphy, sr->work.id);
|
|
|
|
}
|
|
|
|
|
2015-09-20 21:58:23 +02:00
|
|
|
static void scan_context_free(struct scan_context *sc)
|
|
|
|
{
|
|
|
|
l_debug("sc: %p", sc);
|
|
|
|
|
2020-07-10 19:44:43 +02:00
|
|
|
l_queue_destroy(sc->requests, scan_request_cancel);
|
2015-09-22 21:05:50 +02:00
|
|
|
|
2015-09-20 21:58:23 +02:00
|
|
|
if (sc->sp.timeout)
|
|
|
|
l_timeout_remove(sc->sp.timeout);
|
|
|
|
|
2019-05-08 03:15:23 +02:00
|
|
|
if (sc->start_cmd_id && nl80211)
|
|
|
|
l_genl_family_cancel(nl80211, sc->start_cmd_id);
|
|
|
|
|
|
|
|
if (sc->get_scan_cmd_id && nl80211)
|
|
|
|
l_genl_family_cancel(nl80211, sc->get_scan_cmd_id);
|
|
|
|
|
2021-03-15 18:29:28 +01:00
|
|
|
if (sc->get_fw_scan_cmd_id && nl80211)
|
|
|
|
l_genl_family_cancel(nl80211, sc->get_fw_scan_cmd_id);
|
|
|
|
|
2022-08-05 19:45:17 +02:00
|
|
|
wiphy_state_watch_remove(sc->wiphy, sc->wiphy_watch_id);
|
|
|
|
|
2015-09-20 21:58:23 +02:00
|
|
|
l_free(sc);
|
|
|
|
}
|
|
|
|
|
2019-02-26 23:53:54 +01:00
|
|
|
static void scan_request_triggered(struct l_genl_msg *msg, void *userdata)
|
2015-09-22 21:05:50 +02:00
|
|
|
{
|
|
|
|
struct scan_context *sc = userdata;
|
2019-05-08 03:15:23 +02:00
|
|
|
struct scan_request *sr = l_queue_peek_head(sc->requests);
|
2015-09-22 21:05:50 +02:00
|
|
|
int err;
|
|
|
|
|
scan: Cancel CMD_TRIGGER_SCAN when removing scan context
Save the ids of the netlink trigger scan commands that we send and
cancel them in scan_ifindex_remove to fix a race leading to a
segfault. The segfault would happen every time if scan_ifindex_remove
was called in the same main loop iteration in which we sent the
command, on shutdown:
^CTerminate
src/netdev.c:netdev_free() Freeing netdev wlan3[6]
src/device.c:device_disassociated() 6
src/device.c:device_enter_state() Old State: connected, new state:
disconnected
src/device.c:device_enter_state() Old State: disconnected, new state:
autoconnect
src/scan.c:scan_periodic_start() Starting periodic scan for ifindex: 6
src/device.c:device_free()
src/device.c:bss_free() Freeing BSS 02:00:00:00:00:00
src/device.c:bss_free() Freeing BSS 02:00:00:00:01:00
Removing scan context for ifindex: 6
src/scan.c:scan_context_free() sc: 0x5555557ca290
src/scan.c:scan_notify() Scan notification 33
src/netdev.c:netdev_operstate_down_cb() netdev: 6, success: 1
src/scan.c:scan_periodic_done()
src/scan.c:scan_periodic_done() Periodic scan triggered for ifindex:
1434209520
Program received signal SIGSEGV, Segmentation fault.
0x0000000000000064 in ?? ()
(gdb) bt
#0 0x0000000000000064 in ?? ()
#1 0x0000555555583560 in process_unicast (nlmsg=0x7fffffffc1a0,
genl=0x5555557c1d60) at ell/genl.c:390
#2 received_data (io=<optimized out>, user_data=0x5555557c1d60)
at ell/genl.c:506
#3 0x0000555555580d45 in io_callback (fd=<optimized out>,
events=1, user_data=0x5555557c1e60) at ell/io.c:120
#4 0x000055555558005f in l_main_run () at ell/main.c:381
#5 0x00005555555599c1 in main (argc=<optimized out>, argv=<optimized out>)
at src/main.c:259
2017-02-01 11:58:42 +01:00
|
|
|
sc->start_cmd_id = 0;
|
|
|
|
|
2015-09-22 21:05:50 +02:00
|
|
|
err = l_genl_msg_get_error(msg);
|
|
|
|
if (err < 0) {
|
2019-05-08 03:15:23 +02:00
|
|
|
/* Scan in progress, assume another scan is running */
|
|
|
|
if (err == -EBUSY) {
|
|
|
|
sc->state = SCAN_STATE_PASSIVE;
|
2015-09-22 21:05:50 +02:00
|
|
|
return;
|
2019-05-08 03:15:23 +02:00
|
|
|
}
|
|
|
|
|
2018-12-04 03:44:34 +01:00
|
|
|
scan_request_failed(sc, sr, err);
|
2015-09-22 21:05:50 +02:00
|
|
|
|
2017-02-08 01:38:41 +01:00
|
|
|
l_error("Received error during CMD_TRIGGER_SCAN: %s (%d)",
|
|
|
|
strerror(-err), -err);
|
2015-09-22 21:05:50 +02:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sc->state = sr->passive ? SCAN_STATE_PASSIVE : SCAN_STATE_ACTIVE;
|
2019-07-08 16:02:59 +02:00
|
|
|
l_debug("%s scan triggered for wdev %" PRIx64,
|
|
|
|
sr->passive ? "Passive" : "Active", sc->wdev_id);
|
2019-02-27 00:28:58 +01:00
|
|
|
|
2022-01-19 18:19:27 +01:00
|
|
|
sr->triggered = true;
|
2022-01-18 22:10:07 +01:00
|
|
|
sr->started = true;
|
2019-02-27 00:28:58 +01:00
|
|
|
l_genl_msg_unref(l_queue_pop_head(sr->cmds));
|
2015-10-30 11:12:14 +01:00
|
|
|
|
2018-09-18 23:48:50 +02:00
|
|
|
if (sr->trigger) {
|
2015-10-30 11:12:14 +01:00
|
|
|
sr->trigger(0, sr->userdata);
|
2018-09-18 23:48:50 +02:00
|
|
|
|
2018-11-21 16:14:04 +01:00
|
|
|
/*
|
|
|
|
* Reset callback for the consequent scan triggerings of the
|
|
|
|
* multi-segmented scans.
|
|
|
|
*/
|
|
|
|
sr->trigger = NULL;
|
|
|
|
}
|
2015-09-22 21:05:50 +02:00
|
|
|
}
|
|
|
|
|
2016-12-23 11:37:59 +01:00
|
|
|
struct scan_freq_append_data {
|
|
|
|
struct l_genl_msg *msg;
|
|
|
|
int count;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void scan_freq_append(uint32_t freq, void *user_data)
|
|
|
|
{
|
|
|
|
struct scan_freq_append_data *data = user_data;
|
|
|
|
|
|
|
|
l_genl_msg_append_attr(data->msg, data->count++, 4, &freq);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void scan_build_attr_scan_frequencies(struct l_genl_msg *msg,
|
2022-07-26 19:09:15 +02:00
|
|
|
const struct scan_freq_set *freqs)
|
2016-12-23 11:37:59 +01:00
|
|
|
{
|
|
|
|
struct scan_freq_append_data append_data = { msg, 0 };
|
|
|
|
|
|
|
|
l_genl_msg_enter_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES);
|
|
|
|
|
|
|
|
scan_freq_set_foreach(freqs, scan_freq_append, &append_data);
|
|
|
|
|
|
|
|
l_genl_msg_leave_nested(msg);
|
|
|
|
}
|
|
|
|
|
2019-11-08 23:04:01 +01:00
|
|
|
static void scan_build_attr_ie(struct l_genl_msg *msg,
|
|
|
|
struct scan_context *sc,
|
2016-12-23 11:37:58 +01:00
|
|
|
const struct scan_parameters *params)
|
|
|
|
{
|
2019-08-21 22:20:04 +02:00
|
|
|
struct iovec iov[3];
|
2019-08-21 22:20:03 +02:00
|
|
|
unsigned int iov_elems = 0;
|
|
|
|
const uint8_t *ext_capa;
|
2019-08-21 22:20:04 +02:00
|
|
|
uint8_t interworking[3];
|
2016-12-23 11:37:59 +01:00
|
|
|
|
2019-08-21 22:20:03 +02:00
|
|
|
ext_capa = wiphy_get_extended_capabilities(sc->wiphy,
|
|
|
|
NL80211_IFTYPE_STATION);
|
|
|
|
/*
|
2019-11-08 23:03:59 +01:00
|
|
|
* If adding IE's here ensure that ordering is not broken for
|
|
|
|
* probe requests (IEEE Std 802.11-2016 Table 9-33).
|
2019-08-21 22:20:03 +02:00
|
|
|
*/
|
|
|
|
/* Order 9 - Extended Capabilities */
|
|
|
|
iov[iov_elems].iov_base = (void *) ext_capa;
|
|
|
|
iov[iov_elems].iov_len = ext_capa[1] + 2;
|
|
|
|
iov_elems++;
|
|
|
|
|
2021-03-12 20:49:23 +01:00
|
|
|
if (test_bit(&ext_capa[2 + 3], 7)) {
|
2019-08-22 00:32:31 +02:00
|
|
|
/* Order 12 - Interworking */
|
|
|
|
interworking[0] = IE_TYPE_INTERWORKING;
|
|
|
|
interworking[1] = 1;
|
|
|
|
/* Private network, INet=0,ASRA=0,ESR=0,UESA=0 */
|
|
|
|
interworking[2] = 0;
|
|
|
|
|
|
|
|
iov[iov_elems].iov_base = interworking;
|
|
|
|
iov[iov_elems].iov_len = 3;
|
|
|
|
iov_elems++;
|
|
|
|
}
|
2019-08-21 22:20:04 +02:00
|
|
|
|
2019-08-21 22:20:03 +02:00
|
|
|
/* Order Last (assuming WSC vendor specific) */
|
|
|
|
if (params->extra_ie && params->extra_ie_size) {
|
|
|
|
iov[iov_elems].iov_base = (void *) params->extra_ie;
|
|
|
|
iov[iov_elems].iov_len = params->extra_ie_size;
|
|
|
|
iov_elems++;
|
|
|
|
}
|
|
|
|
|
|
|
|
l_genl_msg_append_attrv(msg, NL80211_ATTR_IE, iov, iov_elems);
|
2019-11-08 23:04:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool scan_mac_address_randomization_is_disabled(void)
|
|
|
|
{
|
|
|
|
const struct l_settings *config = iwd_get_config();
|
|
|
|
bool disabled;
|
|
|
|
|
|
|
|
if (!l_settings_get_bool(config, "Scan",
|
|
|
|
"DisableMacAddressRandomization",
|
|
|
|
&disabled))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return disabled;
|
|
|
|
}
|
|
|
|
|
2022-08-05 00:02:48 +02:00
|
|
|
static struct scan_freq_set *scan_get_allowed_freqs(struct scan_context *sc)
|
|
|
|
{
|
|
|
|
struct scan_freq_set *allowed = scan_freq_set_new();
|
|
|
|
|
|
|
|
scan_freq_set_merge(allowed, wiphy_get_supported_freqs(sc->wiphy));
|
|
|
|
|
|
|
|
if (!wiphy_constrain_freq_set(sc->wiphy, allowed)) {
|
|
|
|
scan_freq_set_free(allowed);
|
|
|
|
allowed = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return allowed;
|
|
|
|
}
|
|
|
|
|
2019-11-08 23:04:01 +01:00
|
|
|
static struct l_genl_msg *scan_build_cmd(struct scan_context *sc,
|
|
|
|
bool ignore_flush_flag, bool is_passive,
|
2022-08-05 00:02:48 +02:00
|
|
|
const struct scan_parameters *params,
|
|
|
|
const struct scan_freq_set *freqs)
|
2019-11-08 23:04:01 +01:00
|
|
|
{
|
|
|
|
struct l_genl_msg *msg;
|
|
|
|
uint32_t flags = 0;
|
|
|
|
|
|
|
|
msg = l_genl_msg_new(NL80211_CMD_TRIGGER_SCAN);
|
|
|
|
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_WDEV, 8, &sc->wdev_id);
|
|
|
|
|
|
|
|
if (wiphy_get_max_scan_ie_len(sc->wiphy))
|
|
|
|
scan_build_attr_ie(msg, sc, params);
|
2016-12-23 11:37:58 +01:00
|
|
|
|
2022-08-05 00:02:48 +02:00
|
|
|
if (freqs)
|
|
|
|
scan_build_attr_scan_frequencies(msg, freqs);
|
2016-12-23 11:37:59 +01:00
|
|
|
|
2022-02-24 18:04:56 +01:00
|
|
|
if (params->flush && !ignore_flush_flag && wiphy_has_feature(sc->wiphy,
|
|
|
|
NL80211_FEATURE_SCAN_FLUSH))
|
2017-01-31 03:42:57 +01:00
|
|
|
flags |= NL80211_SCAN_FLAG_FLUSH;
|
|
|
|
|
2019-04-11 22:56:06 +02:00
|
|
|
if (!is_passive && params->randomize_mac_addr_hint &&
|
2019-01-16 04:14:55 +01:00
|
|
|
wiphy_can_randomize_mac_addr(sc->wiphy) &&
|
|
|
|
!scan_mac_address_randomization_is_disabled())
|
|
|
|
/*
|
|
|
|
* Randomizing 46 bits (locally administered 1 and multicast 0
|
|
|
|
* is assumed).
|
|
|
|
*/
|
|
|
|
flags |= NL80211_SCAN_FLAG_RANDOM_ADDR;
|
2018-07-11 00:47:02 +02:00
|
|
|
|
2020-09-09 01:49:18 +02:00
|
|
|
if (!is_passive && params->source_mac &&
|
|
|
|
wiphy_can_randomize_mac_addr(sc->wiphy)) {
|
|
|
|
static const uint8_t mask[6] = /* No random bits */
|
|
|
|
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
|
|
|
|
|
|
|
flags |= NL80211_SCAN_FLAG_RANDOM_ADDR;
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_MAC, 6,
|
|
|
|
params->source_mac);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_MAC_MASK, 6,
|
|
|
|
mask);
|
|
|
|
}
|
|
|
|
|
2019-04-11 22:56:07 +02:00
|
|
|
if (!is_passive && wiphy_has_ext_feature(sc->wiphy,
|
|
|
|
NL80211_EXT_FEATURE_SCAN_RANDOM_SN))
|
|
|
|
flags |= NL80211_SCAN_FLAG_RANDOM_SN;
|
|
|
|
|
2022-02-28 19:49:36 +01:00
|
|
|
if (params->ap_scan)
|
|
|
|
flags |= NL80211_SCAN_FLAG_AP;
|
|
|
|
|
2022-07-19 20:55:33 +02:00
|
|
|
flags |= NL80211_SCAN_FLAG_COLOCATED_6GHZ;
|
|
|
|
|
2017-01-31 03:42:57 +01:00
|
|
|
if (flags)
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_SCAN_FLAGS, 4, &flags);
|
|
|
|
|
2019-10-28 15:04:54 +01:00
|
|
|
if (params->no_cck_rates) {
|
|
|
|
static const uint8_t b_rates[] = { 2, 4, 11, 22 };
|
|
|
|
uint8_t *scan_rates;
|
|
|
|
const uint8_t *supported;
|
|
|
|
unsigned int num_supported;
|
|
|
|
unsigned int count;
|
|
|
|
unsigned int i;
|
|
|
|
|
2019-07-15 16:12:55 +02:00
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_TX_NO_CCK_RATE, 0,
|
|
|
|
NULL);
|
|
|
|
|
2019-10-28 15:04:54 +01:00
|
|
|
/*
|
|
|
|
* Assume if we're sending the probe requests at OFDM bit
|
|
|
|
* rates we don't want to advertise support for 802.11b rates.
|
|
|
|
*/
|
|
|
|
if (L_WARN_ON(!(supported = wiphy_get_supported_rates(sc->wiphy,
|
2022-12-09 20:39:27 +01:00
|
|
|
BAND_FREQ_2_4_GHZ,
|
2019-10-28 15:04:54 +01:00
|
|
|
&num_supported))))
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
scan_rates = l_malloc(num_supported);
|
|
|
|
|
|
|
|
for (count = 0, i = 0; i < num_supported; i++)
|
|
|
|
if (!memchr(b_rates, supported[i],
|
|
|
|
L_ARRAY_SIZE(b_rates)))
|
|
|
|
scan_rates[count++] = supported[i];
|
|
|
|
|
|
|
|
if (L_WARN_ON(!count)) {
|
|
|
|
l_free(scan_rates);
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
l_genl_msg_enter_nested(msg, NL80211_ATTR_SCAN_SUPP_RATES);
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_BAND_2GHZ,
|
|
|
|
count, scan_rates);
|
|
|
|
l_genl_msg_leave_nested(msg);
|
|
|
|
l_free(scan_rates);
|
|
|
|
}
|
|
|
|
|
2019-11-15 19:39:21 +01:00
|
|
|
if (wiphy_has_ext_feature(sc->wiphy,
|
|
|
|
NL80211_EXT_FEATURE_SET_SCAN_DWELL)) {
|
|
|
|
if (params->duration)
|
|
|
|
l_genl_msg_append_attr(msg,
|
|
|
|
NL80211_ATTR_MEASUREMENT_DURATION,
|
|
|
|
2, ¶ms->duration);
|
|
|
|
|
|
|
|
if (params->duration_mandatory)
|
|
|
|
l_genl_msg_append_attr(msg,
|
|
|
|
NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY,
|
|
|
|
0, NULL);
|
|
|
|
}
|
|
|
|
|
2019-10-28 15:04:54 +01:00
|
|
|
done:
|
2016-12-23 11:37:58 +01:00
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
2021-02-05 18:21:33 +01:00
|
|
|
struct l_genl_msg *scan_build_trigger_scan_bss(uint32_t ifindex,
|
|
|
|
struct wiphy *wiphy,
|
|
|
|
uint32_t frequency,
|
|
|
|
const uint8_t *ssid,
|
|
|
|
uint32_t ssid_len)
|
|
|
|
{
|
|
|
|
struct l_genl_msg *msg = l_genl_msg_new(NL80211_CMD_TRIGGER_SCAN);
|
|
|
|
uint32_t flags = 0;
|
|
|
|
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &ifindex);
|
|
|
|
|
|
|
|
l_genl_msg_enter_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES);
|
|
|
|
l_genl_msg_append_attr(msg, 0, 4, &frequency);
|
|
|
|
l_genl_msg_leave_nested(msg);
|
|
|
|
|
|
|
|
if (wiphy_has_ext_feature(wiphy, NL80211_EXT_FEATURE_SCAN_RANDOM_SN))
|
|
|
|
flags |= NL80211_SCAN_FLAG_RANDOM_SN;
|
|
|
|
|
|
|
|
if (flags)
|
|
|
|
l_genl_msg_append_attr(msg, NL80211_ATTR_SCAN_FLAGS, 4, &flags);
|
|
|
|
|
|
|
|
/* direct probe request scan */
|
|
|
|
l_genl_msg_enter_nested(msg, NL80211_ATTR_SCAN_SSIDS);
|
|
|
|
l_genl_msg_append_attr(msg, 0, ssid_len, ssid);
|
|
|
|
l_genl_msg_leave_nested(msg);
|
|
|
|
|
|
|
|
return msg;
|
|
|
|
}
|
|
|
|
|
2018-07-22 14:15:18 +02:00
|
|
|
struct scan_cmds_add_data {
|
|
|
|
struct scan_context *sc;
|
|
|
|
const struct scan_parameters *params;
|
|
|
|
struct l_queue *cmds;
|
|
|
|
struct l_genl_msg **cmd;
|
|
|
|
uint8_t max_ssids_per_scan;
|
|
|
|
uint8_t num_ssids_can_append;
|
|
|
|
};
|
|
|
|
|
|
|
|
static bool scan_cmds_add_hidden(const struct network_info *network,
|
|
|
|
void *user_data)
|
|
|
|
{
|
|
|
|
struct scan_cmds_add_data *data = user_data;
|
|
|
|
|
2021-07-16 18:11:36 +02:00
|
|
|
if (!network->config.is_hidden)
|
2018-07-22 14:15:18 +02:00
|
|
|
return true;
|
|
|
|
|
|
|
|
l_genl_msg_append_attr(*data->cmd, NL80211_ATTR_SSID,
|
|
|
|
strlen(network->ssid), network->ssid);
|
|
|
|
data->num_ssids_can_append--;
|
|
|
|
|
|
|
|
if (!data->num_ssids_can_append) {
|
|
|
|
l_genl_msg_leave_nested(*data->cmd);
|
|
|
|
l_queue_push_tail(data->cmds, *data->cmd);
|
|
|
|
|
|
|
|
data->num_ssids_can_append = data->max_ssids_per_scan;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create a consecutive scan trigger in the batch of scans.
|
|
|
|
* The 'flush' flag is ignored, this allows to get the results
|
|
|
|
* of all scans in the batch after the last scan is finished.
|
|
|
|
*/
|
2019-04-11 22:56:06 +02:00
|
|
|
*data->cmd = scan_build_cmd(data->sc, true, false,
|
2022-08-05 00:02:48 +02:00
|
|
|
data->params,
|
|
|
|
data->params->freqs);
|
2018-07-22 14:15:18 +02:00
|
|
|
l_genl_msg_enter_nested(*data->cmd, NL80211_ATTR_SCAN_SSIDS);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-08-05 00:02:48 +02:00
|
|
|
static void scan_build_next_cmd(struct l_queue *cmds, struct scan_context *sc,
|
2018-07-11 00:47:00 +02:00
|
|
|
bool passive,
|
2022-08-05 00:02:48 +02:00
|
|
|
const struct scan_parameters *params,
|
|
|
|
const struct scan_freq_set *freqs)
|
2018-07-11 00:47:00 +02:00
|
|
|
{
|
|
|
|
struct l_genl_msg *cmd;
|
2018-07-22 14:15:18 +02:00
|
|
|
struct scan_cmds_add_data data = {
|
|
|
|
sc,
|
|
|
|
params,
|
|
|
|
cmds,
|
|
|
|
&cmd,
|
|
|
|
wiphy_get_max_num_ssids_per_scan(sc->wiphy),
|
|
|
|
};
|
2018-07-11 00:47:00 +02:00
|
|
|
|
2022-08-05 00:02:48 +02:00
|
|
|
cmd = scan_build_cmd(sc, false, passive, params, freqs);
|
2018-07-11 00:47:00 +02:00
|
|
|
|
|
|
|
if (passive) {
|
|
|
|
/* passive scan */
|
|
|
|
l_queue_push_tail(cmds, cmd);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
l_genl_msg_enter_nested(cmd, NL80211_ATTR_SCAN_SSIDS);
|
|
|
|
|
|
|
|
if (params->ssid) {
|
|
|
|
/* direct probe request scan */
|
|
|
|
l_genl_msg_append_attr(cmd, NL80211_ATTR_SSID,
|
2021-09-16 18:30:41 +02:00
|
|
|
params->ssid_len, params->ssid);
|
2018-07-11 00:47:00 +02:00
|
|
|
l_genl_msg_leave_nested(cmd);
|
|
|
|
|
|
|
|
l_queue_push_tail(cmds, cmd);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-07-22 14:15:18 +02:00
|
|
|
data.num_ssids_can_append = data.max_ssids_per_scan;
|
|
|
|
known_networks_foreach(scan_cmds_add_hidden, &data);
|
2018-07-11 00:47:00 +02:00
|
|
|
|
|
|
|
l_genl_msg_append_attr(cmd, NL80211_ATTR_SSID, 0, NULL);
|
|
|
|
l_genl_msg_leave_nested(cmd);
|
|
|
|
l_queue_push_tail(cmds, cmd);
|
|
|
|
}
|
|
|
|
|
2022-08-05 00:02:48 +02:00
|
|
|
static void scan_cmds_add(struct scan_request *sr, struct scan_context *sc,
|
|
|
|
bool passive,
|
|
|
|
const struct scan_parameters *params)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
struct scan_freq_set *subsets[2] = { 0 };
|
|
|
|
struct scan_freq_set *allowed = scan_get_allowed_freqs(sc);
|
|
|
|
const struct scan_freq_set *supported =
|
|
|
|
wiphy_get_supported_freqs(sc->wiphy);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If 6GHz is not possible, or already allowed, or the frequencies are
|
|
|
|
* explicit don't break up the request.
|
|
|
|
*/
|
|
|
|
if (!(scan_freq_set_get_bands(supported) & BAND_FREQ_6_GHZ) ||
|
|
|
|
(scan_freq_set_get_bands(allowed) & BAND_FREQ_6_GHZ) ||
|
|
|
|
params->freqs) {
|
|
|
|
scan_freq_set_free(allowed);
|
|
|
|
scan_build_next_cmd(sr->cmds, sc, passive,
|
|
|
|
params, params->freqs);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Otherwise a full spectrum scan will likely open up the 6GHz
|
|
|
|
* band. The problem is the regdom update occurs after an
|
|
|
|
* individual scan request so a single request isn't going to
|
|
|
|
* include potential 6GHz results.
|
|
|
|
*
|
|
|
|
* Instead we can break this full scan up into individual bands
|
|
|
|
* and increase our chances of the regdom updating after one of
|
|
|
|
* the earlier requests. If it does update to allow 6GHz an
|
|
|
|
* extra 6GHz-only passive scan can be appended to this request
|
|
|
|
* at that time.
|
|
|
|
*/
|
|
|
|
subsets[0] = scan_freq_set_clone(allowed, BAND_FREQ_2_4_GHZ);
|
|
|
|
subsets[1] = scan_freq_set_clone(allowed, BAND_FREQ_5_GHZ);
|
|
|
|
|
|
|
|
scan_freq_set_free(allowed);
|
|
|
|
|
|
|
|
for(i = 0; i < L_ARRAY_SIZE(subsets); i++) {
|
|
|
|
if (!scan_freq_set_isempty(subsets[i]))
|
|
|
|
scan_build_next_cmd(sr->cmds, sc, passive, params,
|
|
|
|
subsets[i]);
|
|
|
|
|
|
|
|
scan_freq_set_free(subsets[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
sr->split = true;
|
|
|
|
}
|
|
|
|
|
2019-02-28 22:50:16 +01:00
|
|
|
static int scan_request_send_trigger(struct scan_context *sc,
|
2018-07-12 00:20:06 +02:00
|
|
|
struct scan_request *sr)
|
2018-07-11 00:47:00 +02:00
|
|
|
{
|
2019-02-27 00:28:58 +01:00
|
|
|
struct l_genl_msg *cmd = l_queue_peek_head(sr->cmds);
|
2022-01-11 17:51:11 +01:00
|
|
|
|
2018-07-11 00:47:00 +02:00
|
|
|
if (!cmd)
|
2018-07-12 00:20:06 +02:00
|
|
|
return -ENOMSG;
|
2018-07-11 00:47:00 +02:00
|
|
|
|
2019-02-28 22:50:15 +01:00
|
|
|
sc->start_cmd_id = l_genl_family_send(nl80211, cmd,
|
|
|
|
scan_request_triggered, sc,
|
|
|
|
NULL);
|
2018-07-13 19:30:25 +02:00
|
|
|
if (sc->start_cmd_id) {
|
2019-02-27 22:59:01 +01:00
|
|
|
l_genl_msg_ref(cmd);
|
|
|
|
|
2018-07-12 00:20:06 +02:00
|
|
|
return 0;
|
2018-07-13 19:30:25 +02:00
|
|
|
}
|
2018-07-11 00:47:00 +02:00
|
|
|
|
2019-02-28 22:50:15 +01:00
|
|
|
l_error("Scan request: failed to trigger scan.");
|
|
|
|
|
2018-07-12 00:20:06 +02:00
|
|
|
return -EIO;
|
2018-07-11 00:47:00 +02:00
|
|
|
}
|
|
|
|
|
2020-07-10 19:44:43 +02:00
|
|
|
static const struct wiphy_radio_work_item_ops work_ops = {
|
|
|
|
.do_work = start_next_scan_request,
|
|
|
|
.destroy = scan_request_free,
|
|
|
|
};
|
|
|
|
|
2021-09-18 00:58:36 +02:00
|
|
|
static struct scan_request *scan_request_new(struct scan_context *sc,
|
|
|
|
bool passive,
|
|
|
|
scan_trigger_func_t trigger,
|
|
|
|
scan_notify_func_t notify,
|
|
|
|
void *userdata,
|
|
|
|
scan_destroy_func_t destroy)
|
|
|
|
{
|
|
|
|
struct scan_request *sr;
|
|
|
|
|
|
|
|
sr = l_new(struct scan_request, 1);
|
|
|
|
sr->sc = sc;
|
|
|
|
sr->trigger = trigger;
|
|
|
|
sr->callback = notify;
|
|
|
|
sr->userdata = userdata;
|
|
|
|
sr->destroy = destroy;
|
|
|
|
sr->passive = passive;
|
|
|
|
sr->cmds = l_queue_new();
|
2022-08-03 23:36:33 +02:00
|
|
|
sr->freqs_scanned = scan_freq_set_new();
|
2021-09-18 00:58:36 +02:00
|
|
|
|
|
|
|
return sr;
|
|
|
|
}
|
|
|
|
|
2022-04-29 04:07:39 +02:00
|
|
|
static int insert_by_priority(const void *a, const void *b, void *user_data)
|
|
|
|
{
|
|
|
|
const struct scan_request *cur = b;
|
|
|
|
int priority = L_PTR_TO_INT(user_data);
|
|
|
|
|
|
|
|
if (cur->work.priority <= priority)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2019-07-08 16:02:59 +02:00
|
|
|
static uint32_t scan_common(uint64_t wdev_id, bool passive,
|
2016-12-23 11:37:58 +01:00
|
|
|
const struct scan_parameters *params,
|
2022-01-11 18:45:10 +01:00
|
|
|
int priority,
|
2015-09-30 18:32:02 +02:00
|
|
|
scan_trigger_func_t trigger,
|
|
|
|
scan_notify_func_t notify, void *userdata,
|
|
|
|
scan_destroy_func_t destroy)
|
2015-09-22 21:05:50 +02:00
|
|
|
{
|
|
|
|
struct scan_context *sc;
|
|
|
|
struct scan_request *sr;
|
|
|
|
|
2019-07-08 16:02:59 +02:00
|
|
|
sc = l_queue_find(scan_contexts, scan_context_match, &wdev_id);
|
2015-09-22 21:05:50 +02:00
|
|
|
|
|
|
|
if (!sc)
|
2015-10-06 05:37:12 +02:00
|
|
|
return 0;
|
2015-09-22 21:05:50 +02:00
|
|
|
|
2021-09-18 00:58:36 +02:00
|
|
|
sr = scan_request_new(sc, passive, trigger, notify, userdata, destroy);
|
2015-09-22 21:05:50 +02:00
|
|
|
|
2022-08-05 00:02:48 +02:00
|
|
|
scan_cmds_add(sr, sc, passive, params);
|
2016-12-23 11:37:58 +01:00
|
|
|
|
2022-04-29 04:07:39 +02:00
|
|
|
/*
|
|
|
|
* sr->work isn't initialized yet, it will be done by
|
|
|
|
* wiphy_radio_work_insert(). Pass the priority as user_data instead
|
|
|
|
*/
|
|
|
|
l_queue_insert(sc->requests, sr, insert_by_priority,
|
|
|
|
L_INT_TO_PTR(priority));
|
2015-09-22 21:05:50 +02:00
|
|
|
|
2022-01-11 18:45:10 +01:00
|
|
|
return wiphy_radio_work_insert(sc->wiphy, &sr->work,
|
|
|
|
priority, &work_ops);
|
2015-01-12 16:52:37 +01:00
|
|
|
}
|
2015-01-12 16:52:38 +01:00
|
|
|
|
2022-07-26 19:09:15 +02:00
|
|
|
uint32_t scan_passive(uint64_t wdev_id, const struct scan_freq_set *freqs,
|
2019-03-22 22:17:39 +01:00
|
|
|
scan_trigger_func_t trigger, scan_notify_func_t notify,
|
|
|
|
void *userdata, scan_destroy_func_t destroy)
|
2015-09-30 18:32:02 +02:00
|
|
|
{
|
2019-03-22 22:17:39 +01:00
|
|
|
struct scan_parameters params = { .freqs = freqs };
|
2016-12-23 11:37:58 +01:00
|
|
|
|
2022-01-11 18:45:10 +01:00
|
|
|
return scan_common(wdev_id, true, ¶ms, WIPHY_WORK_PRIORITY_SCAN,
|
|
|
|
trigger, notify, userdata, destroy);
|
2015-09-30 18:32:02 +02:00
|
|
|
}
|
|
|
|
|
2019-11-15 19:39:20 +01:00
|
|
|
uint32_t scan_passive_full(uint64_t wdev_id,
|
|
|
|
const struct scan_parameters *params,
|
|
|
|
scan_trigger_func_t trigger,
|
|
|
|
scan_notify_func_t notify, void *userdata,
|
|
|
|
scan_destroy_func_t destroy)
|
|
|
|
{
|
2022-01-11 18:45:10 +01:00
|
|
|
return scan_common(wdev_id, true, params, WIPHY_WORK_PRIORITY_SCAN,
|
|
|
|
trigger, notify, userdata, destroy);
|
2019-11-15 19:39:20 +01:00
|
|
|
}
|
|
|
|
|
2019-07-08 16:02:59 +02:00
|
|
|
uint32_t scan_active(uint64_t wdev_id, uint8_t *extra_ie, size_t extra_ie_size,
|
2015-09-30 18:32:02 +02:00
|
|
|
scan_trigger_func_t trigger,
|
|
|
|
scan_notify_func_t notify, void *userdata,
|
|
|
|
scan_destroy_func_t destroy)
|
|
|
|
{
|
2016-12-23 11:37:58 +01:00
|
|
|
struct scan_parameters params = {};
|
|
|
|
|
|
|
|
params.extra_ie = extra_ie;
|
|
|
|
params.extra_ie_size = extra_ie_size;
|
|
|
|
|
2022-01-11 18:45:10 +01:00
|
|
|
return scan_common(wdev_id, false, ¶ms, WIPHY_WORK_PRIORITY_SCAN,
|
2016-12-23 11:37:58 +01:00
|
|
|
trigger, notify, userdata, destroy);
|
|
|
|
}
|
|
|
|
|
2019-07-08 16:02:59 +02:00
|
|
|
uint32_t scan_active_full(uint64_t wdev_id,
|
2016-12-23 11:37:58 +01:00
|
|
|
const struct scan_parameters *params,
|
|
|
|
scan_trigger_func_t trigger, scan_notify_func_t notify,
|
|
|
|
void *userdata, scan_destroy_func_t destroy)
|
|
|
|
{
|
2022-01-11 18:45:10 +01:00
|
|
|
return scan_common(wdev_id, false, params, WIPHY_WORK_PRIORITY_SCAN,
|
2015-09-30 18:32:02 +02:00
|
|
|
trigger, notify, userdata, destroy);
|
|
|
|
}
|
|
|
|
|
2021-09-22 21:34:17 +02:00
|
|
|
static void scan_add_owe_freq(struct scan_freq_set *freqs,
|
|
|
|
const struct scan_bss *bss)
|
|
|
|
{
|
|
|
|
int freq;
|
|
|
|
|
|
|
|
if (bss->owe_trans->oper_class)
|
|
|
|
freq = oci_to_frequency(bss->owe_trans->oper_class,
|
|
|
|
bss->owe_trans->channel);
|
|
|
|
else
|
|
|
|
freq = bss->frequency;
|
|
|
|
|
|
|
|
L_WARN_ON(freq < 0);
|
|
|
|
|
|
|
|
scan_freq_set_add(freqs, freq);
|
|
|
|
}
|
|
|
|
|
2021-09-18 00:58:36 +02:00
|
|
|
static void add_owe_scan_cmd(struct scan_context *sc, struct scan_request *sr,
|
|
|
|
bool ignore_flush,
|
|
|
|
struct scan_freq_set *freqs,
|
|
|
|
const struct scan_bss *bss)
|
|
|
|
{
|
|
|
|
struct l_genl_msg *cmd;
|
|
|
|
struct scan_parameters params = {};
|
|
|
|
struct scan_freq_set *tmp;
|
|
|
|
|
|
|
|
if (!freqs) {
|
|
|
|
tmp = scan_freq_set_new();
|
2021-09-22 21:34:17 +02:00
|
|
|
|
|
|
|
scan_add_owe_freq(tmp, bss);
|
|
|
|
|
2021-09-18 00:58:36 +02:00
|
|
|
params.freqs = tmp;
|
|
|
|
} else
|
|
|
|
params.freqs = freqs;
|
|
|
|
|
2021-09-22 20:26:51 +02:00
|
|
|
params.ssid = bss->owe_trans->ssid;
|
|
|
|
params.ssid_len = bss->owe_trans->ssid_len;
|
2021-09-18 00:58:36 +02:00
|
|
|
params.flush = true;
|
|
|
|
|
2022-08-05 00:02:48 +02:00
|
|
|
cmd = scan_build_cmd(sc, ignore_flush, false, ¶ms, params.freqs);
|
2021-09-18 00:58:36 +02:00
|
|
|
|
|
|
|
l_genl_msg_enter_nested(cmd, NL80211_ATTR_SCAN_SSIDS);
|
|
|
|
l_genl_msg_append_attr(cmd, 0, params.ssid_len, params.ssid);
|
|
|
|
l_genl_msg_leave_nested(cmd);
|
|
|
|
|
|
|
|
l_queue_push_tail(sr->cmds, cmd);
|
|
|
|
|
|
|
|
if (!freqs)
|
|
|
|
scan_freq_set_free(tmp);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t scan_owe_hidden(uint64_t wdev_id, struct l_queue *list,
|
|
|
|
scan_trigger_func_t trigger, scan_notify_func_t notify,
|
|
|
|
void *userdata, scan_destroy_func_t destroy)
|
|
|
|
{
|
|
|
|
struct scan_context *sc;
|
|
|
|
struct scan_request *sr;
|
|
|
|
struct scan_freq_set *freqs;
|
|
|
|
const struct l_queue_entry *entry;
|
|
|
|
const uint8_t *ssid = NULL;
|
|
|
|
size_t ssid_len;
|
|
|
|
bool same_ssid = true;
|
|
|
|
struct scan_bss *bss;
|
|
|
|
bool ignore_flush = false;
|
|
|
|
|
|
|
|
sc = l_queue_find(scan_contexts, scan_context_match, &wdev_id);
|
|
|
|
|
|
|
|
if (!sc)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
sr = scan_request_new(sc, false, trigger, notify, userdata, destroy);
|
|
|
|
|
|
|
|
freqs = scan_freq_set_new();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Start building up a frequency list if all SSIDs are the same. This
|
|
|
|
* is hopefully the common case and will allow a single scan command.
|
|
|
|
*/
|
|
|
|
for (entry = l_queue_get_entries(list); entry; entry = entry->next) {
|
|
|
|
bss = entry->data;
|
|
|
|
|
2021-09-22 21:34:17 +02:00
|
|
|
scan_add_owe_freq(freqs, bss);
|
2021-09-18 00:58:36 +02:00
|
|
|
|
|
|
|
/* First */
|
|
|
|
if (!ssid) {
|
2021-09-22 20:26:51 +02:00
|
|
|
ssid = bss->owe_trans->ssid;
|
|
|
|
ssid_len = bss->owe_trans->ssid_len;
|
2021-09-18 00:58:36 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-09-22 20:26:51 +02:00
|
|
|
if (ssid_len == bss->owe_trans->ssid_len &&
|
|
|
|
!memcmp(ssid, bss->owe_trans->ssid,
|
|
|
|
bss->owe_trans->ssid_len))
|
2021-09-18 00:58:36 +02:00
|
|
|
continue;
|
|
|
|
|
|
|
|
same_ssid = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (same_ssid) {
|
|
|
|
bss = l_queue_peek_head(list);
|
|
|
|
|
|
|
|
add_owe_scan_cmd(sc, sr, ignore_flush, freqs, bss);
|
|
|
|
|
|
|
|
scan_freq_set_free(freqs);
|
|
|
|
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
scan_freq_set_free(freqs);
|
|
|
|
|
|
|
|
/* SSIDs differed, use separate scan commands. */
|
|
|
|
for (entry = l_queue_get_entries(list); entry; entry = entry->next) {
|
|
|
|
bss = entry->data;
|
|
|
|
|
|
|
|
add_owe_scan_cmd(sc, sr, ignore_flush, NULL, bss);
|
|
|
|
|
|
|
|
/* Ignore flush on all subsequent commands */
|
|
|
|
if (!ignore_flush)
|
|
|
|
ignore_flush = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
2022-04-29 04:07:39 +02:00
|
|
|
l_queue_insert(sc->requests, sr, insert_by_priority,
|
|
|
|
L_INT_TO_PTR(WIPHY_WORK_PRIORITY_SCAN));
|
2021-09-18 00:58:36 +02:00
|
|
|
|
2022-01-11 18:45:10 +01:00
|
|
|
return wiphy_radio_work_insert(sc->wiphy, &sr->work,
|
|
|
|
WIPHY_WORK_PRIORITY_SCAN, &work_ops);
|
2021-09-18 00:58:36 +02:00
|
|
|
}
|
|
|
|
|
2019-07-08 16:02:59 +02:00
|
|
|
bool scan_cancel(uint64_t wdev_id, uint32_t id)
|
2015-10-06 05:37:12 +02:00
|
|
|
{
|
|
|
|
struct scan_context *sc;
|
|
|
|
struct scan_request *sr;
|
|
|
|
|
2019-07-15 21:38:21 +02:00
|
|
|
l_debug("Trying to cancel scan id %u for wdev %" PRIx64, id, wdev_id);
|
|
|
|
|
2019-07-08 16:02:59 +02:00
|
|
|
sc = l_queue_find(scan_contexts, scan_context_match, &wdev_id);
|
2015-10-06 05:37:12 +02:00
|
|
|
if (!sc)
|
|
|
|
return false;
|
|
|
|
|
2019-05-08 03:15:23 +02:00
|
|
|
sr = l_queue_find(sc->requests, scan_request_match, L_UINT_TO_PTR(id));
|
|
|
|
if (!sr)
|
|
|
|
return false;
|
|
|
|
|
2022-01-19 21:39:27 +01:00
|
|
|
/* We're in the callback and about to be removed, invoke destroy now */
|
|
|
|
if (sr->in_callback)
|
|
|
|
goto call_destroy;
|
|
|
|
|
2018-12-04 03:44:34 +01:00
|
|
|
/* If already triggered, just zero out the callback */
|
2022-01-19 21:39:27 +01:00
|
|
|
if (sr->triggered) {
|
|
|
|
l_debug("Scan has been triggered, wait for it to complete");
|
2019-07-15 21:38:21 +02:00
|
|
|
|
2015-10-06 05:37:12 +02:00
|
|
|
sr->callback = NULL;
|
2022-01-19 21:39:27 +01:00
|
|
|
goto call_destroy;
|
2015-10-06 05:37:12 +02:00
|
|
|
}
|
|
|
|
|
2022-01-19 21:39:27 +01:00
|
|
|
/*
|
|
|
|
* Takes care of the following cases:
|
|
|
|
* 1. If TRIGGER_SCAN is in flight
|
|
|
|
* 2. TRIGGER_SCAN sent but bounced with -EBUSY
|
|
|
|
* 3. Scan request is done but GET_SCAN is still pending
|
|
|
|
*
|
|
|
|
* For case 3, we can easily cancel the command and proceed with the
|
|
|
|
* other pending requests. For case 1 & 2, the subsequent pending
|
|
|
|
* request might bounce off with an -EBUSY.
|
|
|
|
*/
|
|
|
|
if (wiphy_radio_work_is_running(sc->wiphy, sr->work.id)) {
|
|
|
|
l_debug("Scan is already started");
|
2019-07-15 21:38:21 +02:00
|
|
|
|
2022-01-19 23:00:41 +01:00
|
|
|
/* l_genl_family_cancel will trigger destroy callbacks */
|
|
|
|
sr->canceled = true;
|
|
|
|
|
2019-05-08 03:15:23 +02:00
|
|
|
if (sc->start_cmd_id)
|
|
|
|
l_genl_family_cancel(nl80211, sc->start_cmd_id);
|
|
|
|
|
|
|
|
if (sc->get_scan_cmd_id)
|
|
|
|
l_genl_family_cancel(nl80211, sc->get_scan_cmd_id);
|
|
|
|
|
2018-12-04 03:44:34 +01:00
|
|
|
sc->start_cmd_id = 0;
|
2022-01-19 21:39:27 +01:00
|
|
|
sc->get_scan_cmd_id = 0;
|
|
|
|
}
|
2017-02-08 01:38:42 +01:00
|
|
|
|
2022-01-19 21:39:27 +01:00
|
|
|
l_queue_remove(sc->requests, sr);
|
2020-07-10 19:44:43 +02:00
|
|
|
wiphy_radio_work_done(sc->wiphy, sr->work.id);
|
|
|
|
|
2022-01-19 21:39:27 +01:00
|
|
|
return true;
|
|
|
|
|
|
|
|
call_destroy:
|
|
|
|
if (sr->destroy) {
|
|
|
|
sr->destroy(sr->userdata);
|
|
|
|
sr->destroy = NULL;
|
|
|
|
}
|
|
|
|
|
2015-10-06 05:37:12 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-05-08 03:15:23 +02:00
|
|
|
static void scan_periodic_triggered(int err, void *user_data)
|
2015-06-05 05:31:39 +02:00
|
|
|
{
|
2015-09-20 21:58:23 +02:00
|
|
|
struct scan_context *sc = user_data;
|
2017-02-08 01:38:41 +01:00
|
|
|
|
2019-05-08 03:15:23 +02:00
|
|
|
if (err) {
|
|
|
|
scan_periodic_rearm(sc);
|
2015-06-05 05:31:39 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-07-08 16:02:59 +02:00
|
|
|
l_debug("Periodic scan triggered for wdev %" PRIx64, sc->wdev_id);
|
2017-03-14 16:11:34 +01:00
|
|
|
|
2016-06-25 20:42:23 +02:00
|
|
|
if (sc->sp.trigger)
|
|
|
|
sc->sp.trigger(0, sc->sp.userdata);
|
2015-06-05 05:31:39 +02:00
|
|
|
}
|
|
|
|
|
2019-05-08 03:15:23 +02:00
|
|
|
static bool scan_periodic_notify(int err, struct l_queue *bss_list,
|
2021-02-03 17:02:40 +01:00
|
|
|
const struct scan_freq_set *freqs,
|
2019-05-08 03:15:23 +02:00
|
|
|
void *user_data)
|
2016-12-23 11:37:58 +01:00
|
|
|
{
|
2019-05-08 03:15:23 +02:00
|
|
|
struct scan_context *sc = user_data;
|
2018-07-11 00:47:01 +02:00
|
|
|
|
2019-05-08 03:15:23 +02:00
|
|
|
scan_periodic_rearm(sc);
|
2018-07-11 00:47:01 +02:00
|
|
|
|
2019-05-08 03:15:23 +02:00
|
|
|
if (sc->sp.callback)
|
2021-02-03 17:02:40 +01:00
|
|
|
return sc->sp.callback(err, bss_list, freqs, sc->sp.userdata);
|
2018-07-11 00:47:01 +02:00
|
|
|
|
2019-05-08 03:15:23 +02:00
|
|
|
return false;
|
|
|
|
}
|
2016-12-23 11:37:58 +01:00
|
|
|
|
2022-01-11 18:45:11 +01:00
|
|
|
static void scan_periodic_destroy(void *user_data)
|
|
|
|
{
|
|
|
|
struct scan_context *sc = user_data;
|
|
|
|
|
|
|
|
sc->sp.id = 0;
|
|
|
|
}
|
|
|
|
|
2019-05-08 03:15:23 +02:00
|
|
|
static bool scan_periodic_queue(struct scan_context *sc)
|
|
|
|
{
|
2022-01-11 18:45:11 +01:00
|
|
|
struct scan_parameters params = {};
|
2016-12-23 11:37:58 +01:00
|
|
|
|
2019-05-08 03:15:23 +02:00
|
|
|
if (sc->sp.needs_active_scan && known_networks_has_hidden()) {
|
2022-01-11 18:45:11 +01:00
|
|
|
params.randomize_mac_addr_hint = true;
|
2019-05-08 03:15:23 +02:00
|
|
|
|
|
|
|
sc->sp.needs_active_scan = false;
|
2022-01-11 18:45:11 +01:00
|
|
|
sc->sp.id = scan_common(sc->wdev_id, false, ¶ms,
|
|
|
|
WIPHY_WORK_PRIORITY_PERIODIC_SCAN,
|
|
|
|
scan_periodic_triggered,
|
|
|
|
scan_periodic_notify, sc,
|
|
|
|
scan_periodic_destroy);
|
2019-05-08 03:15:23 +02:00
|
|
|
} else
|
2022-01-11 18:45:11 +01:00
|
|
|
sc->sp.id = scan_common(sc->wdev_id, true, ¶ms,
|
|
|
|
WIPHY_WORK_PRIORITY_PERIODIC_SCAN,
|
|
|
|
scan_periodic_triggered,
|
|
|
|
scan_periodic_notify, sc,
|
|
|
|
scan_periodic_destroy);
|
2019-05-08 03:15:23 +02:00
|
|
|
|
2022-12-12 22:10:50 +01:00
|
|
|
return sc->sp.id != 0;
|
2016-12-23 11:37:58 +01:00
|
|
|
}
|
|
|
|
|
2018-11-15 21:54:24 +01:00
|
|
|
static bool scan_periodic_is_disabled(void)
|
|
|
|
{
|
|
|
|
const struct l_settings *config = iwd_get_config();
|
|
|
|
bool disabled;
|
|
|
|
|
2019-10-24 22:08:44 +02:00
|
|
|
if (!l_settings_get_bool(config, "Scan", "DisablePeriodicScan",
|
2018-11-15 21:54:24 +01:00
|
|
|
&disabled))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return disabled;
|
|
|
|
}
|
|
|
|
|
2019-07-08 16:02:59 +02:00
|
|
|
void scan_periodic_start(uint64_t wdev_id, scan_trigger_func_t trigger,
|
2016-06-25 20:42:23 +02:00
|
|
|
scan_notify_func_t func, void *userdata)
|
2015-06-05 05:31:39 +02:00
|
|
|
{
|
2015-09-20 21:58:23 +02:00
|
|
|
struct scan_context *sc;
|
2015-06-05 05:31:39 +02:00
|
|
|
|
2018-11-15 21:54:24 +01:00
|
|
|
if (scan_periodic_is_disabled())
|
|
|
|
return;
|
|
|
|
|
2019-07-08 16:02:59 +02:00
|
|
|
sc = l_queue_find(scan_contexts, scan_context_match, &wdev_id);
|
2015-06-05 05:31:39 +02:00
|
|
|
|
2015-09-20 21:58:23 +02:00
|
|
|
if (!sc) {
|
2022-01-11 18:05:09 +01:00
|
|
|
l_error("%s called without scan_wdev_add", __func__);
|
2015-09-20 21:58:23 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sc->sp.interval)
|
2015-06-05 05:31:39 +02:00
|
|
|
return;
|
|
|
|
|
2019-07-08 16:02:59 +02:00
|
|
|
l_debug("Starting periodic scan for wdev %" PRIx64, wdev_id);
|
2015-06-05 05:31:39 +02:00
|
|
|
|
2015-09-20 21:58:23 +02:00
|
|
|
sc->sp.interval = SCAN_INIT_INTERVAL;
|
2016-06-25 20:42:23 +02:00
|
|
|
sc->sp.trigger = trigger;
|
2015-09-20 21:58:23 +02:00
|
|
|
sc->sp.callback = func;
|
|
|
|
sc->sp.userdata = userdata;
|
|
|
|
|
2019-05-08 03:15:23 +02:00
|
|
|
/* If nothing queued, start the first periodic scan */
|
|
|
|
scan_periodic_queue(sc);
|
2015-06-05 05:31:39 +02:00
|
|
|
}
|
|
|
|
|
2019-07-08 16:02:59 +02:00
|
|
|
bool scan_periodic_stop(uint64_t wdev_id)
|
2015-06-05 05:31:39 +02:00
|
|
|
{
|
2015-09-20 21:58:23 +02:00
|
|
|
struct scan_context *sc;
|
2015-06-05 05:31:39 +02:00
|
|
|
|
2019-07-08 16:02:59 +02:00
|
|
|
sc = l_queue_find(scan_contexts, scan_context_match, &wdev_id);
|
2015-06-05 05:31:39 +02:00
|
|
|
|
2015-09-20 21:58:23 +02:00
|
|
|
if (!sc)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!sc->sp.interval)
|
2015-06-05 05:31:39 +02:00
|
|
|
return false;
|
|
|
|
|
2019-07-08 16:02:59 +02:00
|
|
|
l_debug("Stopping periodic scan for wdev %" PRIx64, wdev_id);
|
2015-06-05 05:31:39 +02:00
|
|
|
|
2019-05-08 03:15:23 +02:00
|
|
|
if (sc->sp.timeout)
|
2016-04-13 23:03:33 +02:00
|
|
|
l_timeout_remove(sc->sp.timeout);
|
2019-05-08 03:15:23 +02:00
|
|
|
|
|
|
|
if (sc->sp.id) {
|
2019-07-08 16:02:59 +02:00
|
|
|
scan_cancel(wdev_id, sc->sp.id);
|
2019-05-08 03:15:23 +02:00
|
|
|
sc->sp.id = 0;
|
2016-04-13 23:03:33 +02:00
|
|
|
}
|
|
|
|
|
2015-09-20 21:58:23 +02:00
|
|
|
sc->sp.interval = 0;
|
2016-06-25 20:42:23 +02:00
|
|
|
sc->sp.trigger = NULL;
|
2015-09-20 21:58:23 +02:00
|
|
|
sc->sp.callback = NULL;
|
|
|
|
sc->sp.userdata = NULL;
|
2018-07-11 00:47:01 +02:00
|
|
|
sc->sp.needs_active_scan = false;
|
|
|
|
|
2015-06-05 05:31:39 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-11-19 23:10:23 +01:00
|
|
|
uint64_t scan_get_triggered_time(uint64_t wdev_id, uint32_t id)
|
|
|
|
{
|
|
|
|
struct scan_context *sc;
|
|
|
|
struct scan_request *sr;
|
|
|
|
|
|
|
|
sc = l_queue_find(scan_contexts, scan_context_match, &wdev_id);
|
|
|
|
if (!sc)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
sr = l_queue_find(sc->requests, scan_request_match, L_UINT_TO_PTR(id));
|
2022-01-19 18:19:27 +01:00
|
|
|
if (!sr || !sr->triggered)
|
2019-11-19 23:10:23 +01:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
return sr->start_time_tsf;
|
|
|
|
}
|
|
|
|
|
2015-06-05 05:31:39 +02:00
|
|
|
static void scan_periodic_timeout(struct l_timeout *timeout, void *user_data)
|
|
|
|
{
|
2015-09-20 21:58:23 +02:00
|
|
|
struct scan_context *sc = user_data;
|
2015-06-05 05:31:39 +02:00
|
|
|
|
2022-01-11 18:06:10 +01:00
|
|
|
l_debug("%" PRIx64, sc->wdev_id);
|
2015-06-05 05:31:39 +02:00
|
|
|
|
2022-01-11 18:45:11 +01:00
|
|
|
/*
|
|
|
|
* Timeout triggered before periodic scan could even start, just rearm
|
|
|
|
* with the same interval.
|
|
|
|
*/
|
|
|
|
if (sc->sp.id) {
|
|
|
|
l_debug("Periodic scan timer called before scan could start!");
|
|
|
|
scan_periodic_rearm(sc);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-09-20 21:58:23 +02:00
|
|
|
sc->sp.interval *= 2;
|
2021-02-09 16:28:58 +01:00
|
|
|
if (sc->sp.interval > SCAN_MAX_INTERVAL)
|
|
|
|
sc->sp.interval = SCAN_MAX_INTERVAL;
|
2016-12-23 11:37:58 +01:00
|
|
|
|
2019-05-08 03:15:23 +02:00
|
|
|
scan_periodic_queue(sc);
|
2015-06-05 05:31:39 +02:00
|
|
|
}
|
|
|
|
|
2019-05-01 17:32:32 +02:00
|
|
|
static void scan_periodic_timeout_destroy(void *user_data)
|
|
|
|
{
|
|
|
|
struct scan_context *sc = user_data;
|
|
|
|
|
|
|
|
sc->sp.timeout = NULL;
|
|
|
|
}
|
|
|
|
|
2015-09-20 21:58:23 +02:00
|
|
|
static void scan_periodic_rearm(struct scan_context *sc)
|
2015-06-05 05:31:39 +02:00
|
|
|
{
|
2015-09-20 21:58:23 +02:00
|
|
|
l_debug("Arming periodic scan timer: %u", sc->sp.interval);
|
2015-06-05 05:31:39 +02:00
|
|
|
|
2015-09-20 21:58:23 +02:00
|
|
|
if (sc->sp.timeout)
|
|
|
|
l_timeout_modify(sc->sp.timeout, sc->sp.interval);
|
2015-06-05 05:31:39 +02:00
|
|
|
else
|
2015-09-20 21:58:23 +02:00
|
|
|
sc->sp.timeout = l_timeout_create(sc->sp.interval,
|
2019-05-01 17:32:32 +02:00
|
|
|
scan_periodic_timeout, sc,
|
|
|
|
scan_periodic_timeout_destroy);
|
2015-06-05 05:31:39 +02:00
|
|
|
}
|
|
|
|
|
2020-07-10 19:44:43 +02:00
|
|
|
static bool start_next_scan_request(struct wiphy_radio_work_item *item)
|
2017-02-08 01:38:41 +01:00
|
|
|
{
|
2020-07-10 19:44:43 +02:00
|
|
|
struct scan_request *sr = l_container_of(item,
|
|
|
|
struct scan_request, work);
|
|
|
|
struct scan_context *sc = sr->sc;
|
2017-02-08 01:38:41 +01:00
|
|
|
|
2019-05-01 17:32:31 +02:00
|
|
|
if (sc->state != SCAN_STATE_NOT_RUNNING)
|
2020-07-10 19:44:43 +02:00
|
|
|
return false;
|
2020-06-08 10:42:07 +02:00
|
|
|
|
2020-07-10 19:44:43 +02:00
|
|
|
if (!scan_request_send_trigger(sc, sr))
|
|
|
|
return false;
|
2017-02-08 01:38:41 +01:00
|
|
|
|
2020-07-10 19:44:43 +02:00
|
|
|
scan_request_failed(sc, sr, -EIO);
|
2017-02-08 01:38:41 +01:00
|
|
|
|
2020-07-10 19:44:43 +02:00
|
|
|
return true;
|
2017-02-08 01:38:41 +01:00
|
|
|
}
|
|
|
|
|
2021-09-03 23:32:51 +02:00
|
|
|
static void scan_parse_vendor_specific(struct scan_bss *bss, const void *data,
|
2019-06-11 00:46:58 +02:00
|
|
|
uint16_t len)
|
|
|
|
{
|
2021-09-03 23:32:51 +02:00
|
|
|
uint16_t cost_level;
|
|
|
|
uint16_t cost_flags;
|
2021-11-04 18:57:56 +01:00
|
|
|
bool dgaf_disable;
|
2021-09-03 23:32:51 +02:00
|
|
|
|
|
|
|
if (!bss->wpa && is_ie_wpa_ie(data, len)) {
|
2019-06-11 00:46:58 +02:00
|
|
|
bss->wpa = l_memdup(data - 2, len + 2);
|
2021-09-03 23:32:51 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!bss->osen && is_ie_wfa_ie(data, len, IE_WFA_OI_OSEN)) {
|
2019-06-11 00:46:58 +02:00
|
|
|
bss->osen = l_memdup(data - 2, len + 2);
|
2021-09-03 23:32:51 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_ie_wfa_ie(data, len, IE_WFA_OI_HS20_INDICATION)) {
|
2019-07-24 22:09:46 +02:00
|
|
|
if (ie_parse_hs20_indication_from_data(data - 2, len + 2,
|
2021-11-04 18:57:55 +01:00
|
|
|
&bss->hs20_version, NULL, NULL,
|
2021-11-04 18:57:56 +01:00
|
|
|
&dgaf_disable) < 0)
|
2021-09-03 23:32:51 +02:00
|
|
|
return;
|
2019-07-24 22:09:46 +02:00
|
|
|
|
2021-11-04 18:57:56 +01:00
|
|
|
bss->hs20_dgaf_disable = dgaf_disable;
|
2019-07-24 22:09:46 +02:00
|
|
|
bss->hs20_capable = true;
|
2021-09-03 23:32:51 +02:00
|
|
|
return;
|
|
|
|
}
|
2019-06-11 00:46:58 +02:00
|
|
|
|
2021-09-16 18:30:42 +02:00
|
|
|
if (is_ie_wfa_ie(data, len, IE_WFA_OI_OWE_TRANSITION)) {
|
2021-09-22 21:34:16 +02:00
|
|
|
_auto_(l_free) struct ie_owe_transition_info *owe_trans =
|
|
|
|
l_new(struct ie_owe_transition_info, 1);
|
2021-09-22 20:26:51 +02:00
|
|
|
|
2021-09-22 21:34:16 +02:00
|
|
|
if (ie_parse_owe_transition(data - 2, len + 2, owe_trans) < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (owe_trans->oper_class &&
|
|
|
|
oci_to_frequency(owe_trans->oper_class,
|
|
|
|
owe_trans->channel) < 0)
|
|
|
|
return;
|
2021-09-22 20:26:51 +02:00
|
|
|
|
2021-09-22 21:34:16 +02:00
|
|
|
bss->owe_trans = l_steal_ptr(owe_trans);
|
2021-09-16 18:30:42 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-11-30 00:12:56 +01:00
|
|
|
if (is_ie_wfa_ie(data, len, IE_WFA_OI_CONFIGURATOR_CONNECTIVITY))
|
|
|
|
bss->dpp_configurator = true;
|
|
|
|
|
2021-09-03 23:32:51 +02:00
|
|
|
if (!ie_parse_network_cost(data, len, &cost_level, &cost_flags)) {
|
|
|
|
bss->cost_level = cost_level;
|
|
|
|
bss->cost_flags = cost_flags;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_ie_default_sae_group_oui(data, len))
|
|
|
|
bss->force_default_sae_group = true;
|
2019-06-11 00:46:58 +02:00
|
|
|
}
|
|
|
|
|
2019-06-14 22:12:07 +02:00
|
|
|
/*
|
|
|
|
* Fully parses the Advertisement Protocol Element. The only thing being looked
|
2020-01-21 07:21:38 +01:00
|
|
|
* for is the ANQP protocol ID, but this could be buried behind several other
|
2019-06-14 22:12:07 +02:00
|
|
|
* advertisement tuples so the entire IE may need to be parsed.
|
|
|
|
*/
|
|
|
|
static bool scan_parse_advertisement_protocol(struct scan_bss *bss,
|
|
|
|
const void *data, uint16_t len)
|
|
|
|
{
|
|
|
|
const uint8_t *ptr = data;
|
|
|
|
|
|
|
|
while (len) {
|
|
|
|
/*
|
|
|
|
* TODO: Store query info for GAS response length verification
|
|
|
|
*/
|
|
|
|
uint8_t id = ptr[1];
|
|
|
|
|
|
|
|
switch (id) {
|
|
|
|
/*
|
|
|
|
* IEEE 802.11-2016 Section 11.25.3.3.1
|
|
|
|
*
|
|
|
|
* "A non-AP STA shall not transmit an ANQP request to
|
|
|
|
* an AP for any ANQP-element unless the ANQP
|
|
|
|
* Advertisement Protocol ID is included..."
|
|
|
|
*/
|
|
|
|
case IE_ADVERTISEMENT_ANQP:
|
|
|
|
bss->anqp_capable = true;
|
|
|
|
return true;
|
|
|
|
case IE_ADVERTISEMENT_MIH_SERVICE:
|
|
|
|
case IE_ADVERTISEMENT_MIH_DISCOVERY:
|
|
|
|
case IE_ADVERTISEMENT_EAS:
|
|
|
|
case IE_ADVERTISEMENT_RLQP:
|
|
|
|
len -= 2;
|
|
|
|
ptr += 2;
|
|
|
|
break;
|
|
|
|
case IE_ADVERTISEMENT_VENDOR_SPECIFIC:
|
|
|
|
/* IEEE 802.11-2016 Section 9.4.2.26 */
|
|
|
|
len -= ptr[3];
|
|
|
|
ptr += ptr[3];
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-05-01 16:17:54 +02:00
|
|
|
static bool scan_parse_bss_information_elements(struct scan_bss *bss,
|
|
|
|
const void *data, uint16_t len)
|
|
|
|
{
|
|
|
|
struct ie_tlv_iter iter;
|
2015-06-01 16:28:05 +02:00
|
|
|
bool have_ssid = false;
|
2015-05-01 16:17:54 +02:00
|
|
|
|
|
|
|
ie_tlv_iter_init(&iter, data, len);
|
|
|
|
|
|
|
|
while (ie_tlv_iter_next(&iter)) {
|
|
|
|
uint8_t tag = ie_tlv_iter_get_tag(&iter);
|
|
|
|
|
|
|
|
switch (tag) {
|
|
|
|
case IE_TYPE_SSID:
|
|
|
|
if (iter.len > 32)
|
|
|
|
return false;
|
|
|
|
|
2015-06-01 16:28:05 +02:00
|
|
|
memcpy(bss->ssid, iter.data, iter.len);
|
|
|
|
bss->ssid_len = iter.len;
|
|
|
|
have_ssid = true;
|
2015-05-01 16:17:54 +02:00
|
|
|
break;
|
|
|
|
case IE_TYPE_RSN:
|
|
|
|
if (!bss->rsne)
|
|
|
|
bss->rsne = l_memdup(iter.data - 2,
|
|
|
|
iter.len + 2);
|
|
|
|
break;
|
2021-07-08 21:11:57 +02:00
|
|
|
case IE_TYPE_RSNX:
|
|
|
|
if (!bss->rsnxe)
|
|
|
|
bss->rsnxe = l_memdup(iter.data - 2,
|
|
|
|
iter.len + 2);
|
|
|
|
break;
|
2015-06-10 19:37:50 +02:00
|
|
|
case IE_TYPE_BSS_LOAD:
|
|
|
|
if (ie_parse_bss_load(&iter, NULL, &bss->utilization,
|
|
|
|
NULL) < 0)
|
2018-11-05 19:25:52 +01:00
|
|
|
l_warn("Unable to parse BSS Load IE for "
|
|
|
|
MAC, MAC_STR(bss->addr));
|
2015-06-10 19:37:50 +02:00
|
|
|
|
|
|
|
break;
|
2015-05-01 16:17:54 +02:00
|
|
|
case IE_TYPE_VENDOR_SPECIFIC:
|
2019-06-11 00:46:58 +02:00
|
|
|
scan_parse_vendor_specific(bss, iter.data, iter.len);
|
2015-05-01 16:17:54 +02:00
|
|
|
break;
|
2016-10-28 05:59:52 +02:00
|
|
|
case IE_TYPE_MOBILITY_DOMAIN:
|
|
|
|
if (!bss->mde_present && iter.len == 3) {
|
|
|
|
memcpy(bss->mde, iter.data, iter.len);
|
|
|
|
bss->mde_present = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
2016-10-28 05:59:53 +02:00
|
|
|
case IE_TYPE_RM_ENABLED_CAPABILITIES:
|
|
|
|
if (iter.len != 5)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Only interested in Neighbor Reports */
|
|
|
|
|
|
|
|
bss->cap_rm_neighbor_report =
|
|
|
|
(iter.data[0] & IE_RM_CAP_NEIGHBOR_REPORT) > 0;
|
|
|
|
break;
|
2016-10-28 05:59:54 +02:00
|
|
|
case IE_TYPE_COUNTRY:
|
|
|
|
if (bss->cc_present || iter.len < 6)
|
|
|
|
break;
|
|
|
|
|
|
|
|
bss->cc[0] = iter.data[0];
|
|
|
|
bss->cc[1] = iter.data[1];
|
|
|
|
bss->cc[2] = iter.data[2];
|
|
|
|
bss->cc_present = true;
|
|
|
|
|
2019-02-23 01:43:44 +01:00
|
|
|
break;
|
|
|
|
case IE_TYPE_HT_CAPABILITIES:
|
|
|
|
bss->ht_capable = true;
|
2019-02-25 21:29:32 +01:00
|
|
|
break;
|
|
|
|
case IE_TYPE_VHT_CAPABILITIES:
|
|
|
|
bss->vht_capable = true;
|
2016-10-28 05:59:54 +02:00
|
|
|
break;
|
2019-06-14 22:12:07 +02:00
|
|
|
case IE_TYPE_ADVERTISEMENT_PROTOCOL:
|
|
|
|
if (iter.len < 2)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
scan_parse_advertisement_protocol(bss, iter.data,
|
|
|
|
iter.len);
|
|
|
|
break;
|
2019-06-26 19:42:47 +02:00
|
|
|
case IE_TYPE_INTERWORKING:
|
|
|
|
/*
|
|
|
|
* No bits indicate if venue/HESSID is included, so the
|
|
|
|
* length is the only way to know.
|
|
|
|
* (IEEE 802.11-2016 - Figure 9-439)
|
|
|
|
*/
|
|
|
|
if (iter.len == 9)
|
|
|
|
memcpy(bss->hessid, iter.data + 3, 6);
|
|
|
|
else if (iter.len == 7)
|
|
|
|
memcpy(bss->hessid, iter.data + 1, 6);
|
2019-07-12 19:30:26 +02:00
|
|
|
break;
|
|
|
|
case IE_TYPE_ROAMING_CONSORTIUM:
|
|
|
|
if (iter.len < 2)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
bss->rc_ie = l_memdup(iter.data - 2, iter.len + 2);
|
|
|
|
|
|
|
|
break;
|
2021-11-04 18:57:54 +01:00
|
|
|
|
|
|
|
case IE_TYPE_EXTENDED_CAPABILITIES:
|
|
|
|
/* 802.11-2020 9.4.2.26
|
|
|
|
*
|
|
|
|
* "The length of the Extended Capabilities field is
|
|
|
|
* variable. If fewer bits are received in an Extended
|
|
|
|
* Capabilities field than shown in Table 9-153, the
|
|
|
|
* rest of the Extended Capabilities field bits are
|
|
|
|
* assumed to be zero"
|
|
|
|
*
|
|
|
|
* Currently only Proxy ARP bit (12) is checked, and if
|
|
|
|
* not found, this is not a fatal error.
|
|
|
|
*/
|
|
|
|
if (iter.len < 2)
|
|
|
|
break;
|
|
|
|
|
|
|
|
bss->proxy_arp = test_bit(iter.data, 12);
|
2015-05-01 16:17:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-21 21:15:56 +01:00
|
|
|
bss->wsc = ie_tlv_extract_wsc_payload(data, len, &bss->wsc_size);
|
|
|
|
|
|
|
|
switch (bss->source_frame) {
|
|
|
|
case SCAN_BSS_PROBE_RESP:
|
|
|
|
bss->p2p_probe_resp_info = l_new(struct p2p_probe_resp, 1);
|
|
|
|
|
|
|
|
if (p2p_parse_probe_resp(data, len, bss->p2p_probe_resp_info) ==
|
|
|
|
0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
l_free(bss->p2p_probe_resp_info);
|
|
|
|
bss->p2p_probe_resp_info = NULL;
|
|
|
|
break;
|
|
|
|
case SCAN_BSS_PROBE_REQ:
|
|
|
|
bss->p2p_probe_req_info = l_new(struct p2p_probe_req, 1);
|
|
|
|
|
|
|
|
if (p2p_parse_probe_req(data, len, bss->p2p_probe_req_info) ==
|
|
|
|
0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
l_free(bss->p2p_probe_req_info);
|
|
|
|
bss->p2p_probe_req_info = NULL;
|
|
|
|
break;
|
|
|
|
case SCAN_BSS_BEACON:
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Beacon and Probe Response P2P IE subelement formats are
|
|
|
|
* mutually incompatible and can help us distinguish one frame
|
|
|
|
* subtype from the other if the driver is not exposing enough
|
|
|
|
* information. As a result of trusting the frame contents on
|
|
|
|
* this, no critical code should depend on the
|
|
|
|
* bss->source_frame information being right.
|
|
|
|
*/
|
|
|
|
struct p2p_beacon info;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
r = p2p_parse_beacon(data, len, &info);
|
|
|
|
if (r == 0) {
|
|
|
|
bss->p2p_beacon_info = l_memdup(&info, sizeof(info));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (r == -ENOENT)
|
|
|
|
break;
|
|
|
|
|
|
|
|
bss->p2p_probe_resp_info = l_new(struct p2p_probe_resp, 1);
|
|
|
|
|
|
|
|
if (p2p_parse_probe_resp(data, len, bss->p2p_probe_resp_info) ==
|
|
|
|
0) {
|
|
|
|
bss->source_frame = SCAN_BSS_PROBE_RESP;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
l_free(bss->p2p_probe_resp_info);
|
|
|
|
bss->p2p_probe_resp_info = NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-11 03:00:43 +02:00
|
|
|
bss->wfd = ie_tlv_extract_wfd_payload(data, len, &bss->wfd_size);
|
|
|
|
|
2015-06-01 16:28:05 +02:00
|
|
|
return have_ssid;
|
2015-05-01 16:17:54 +02:00
|
|
|
}
|
|
|
|
|
2022-01-25 19:02:06 +01:00
|
|
|
/*
|
|
|
|
* Maps 0..100 values to -10000..0
|
|
|
|
*
|
|
|
|
* This isn't really mapping to mBm since the input is unit-less and we have no
|
|
|
|
* idea what the driver itself does to come up with this 'strength' value but
|
|
|
|
* this is really the best that can be done for these drivers (its only 4 in
|
|
|
|
* tree drivers after all).
|
|
|
|
*/
|
|
|
|
static int32_t signal_unspec_to_mbm(uint8_t strength)
|
|
|
|
{
|
|
|
|
if (L_WARN_ON(strength > 100))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return ((int32_t)strength * 100) - 10000;
|
|
|
|
}
|
|
|
|
|
2021-02-03 17:46:25 +01:00
|
|
|
static struct scan_bss *scan_parse_attr_bss(struct l_genl_attr *attr,
|
2021-05-28 23:32:30 +02:00
|
|
|
struct wiphy *wiphy,
|
2021-02-03 17:46:25 +01:00
|
|
|
uint32_t *out_seen_ms_ago)
|
2015-05-01 16:17:54 +02:00
|
|
|
{
|
|
|
|
uint16_t type, len;
|
|
|
|
const void *data;
|
|
|
|
struct scan_bss *bss;
|
2019-11-21 21:15:56 +01:00
|
|
|
const uint8_t *ies = NULL;
|
|
|
|
size_t ies_len;
|
|
|
|
const uint8_t *beacon_ies = NULL;
|
|
|
|
size_t beacon_ies_len;
|
2015-05-01 16:17:54 +02:00
|
|
|
|
|
|
|
bss = l_new(struct scan_bss, 1);
|
2015-06-10 19:37:50 +02:00
|
|
|
bss->utilization = 127;
|
2019-11-21 21:15:56 +01:00
|
|
|
bss->source_frame = SCAN_BSS_BEACON;
|
2015-05-01 16:17:54 +02:00
|
|
|
|
|
|
|
while (l_genl_attr_next(attr, &type, &len, &data)) {
|
|
|
|
switch (type) {
|
|
|
|
case NL80211_BSS_BSSID:
|
|
|
|
if (len != sizeof(bss->addr))
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
memcpy(bss->addr, data, len);
|
|
|
|
break;
|
|
|
|
case NL80211_BSS_CAPABILITY:
|
|
|
|
if (len != sizeof(uint16_t))
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
bss->capability = *((uint16_t *) data);
|
|
|
|
break;
|
|
|
|
case NL80211_BSS_FREQUENCY:
|
|
|
|
if (len != sizeof(uint32_t))
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
bss->frequency = *((uint32_t *) data);
|
|
|
|
break;
|
|
|
|
case NL80211_BSS_SIGNAL_MBM:
|
|
|
|
if (len != sizeof(int32_t))
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
bss->signal_strength = *((int32_t *) data);
|
|
|
|
break;
|
2022-01-25 19:02:06 +01:00
|
|
|
case NL80211_BSS_SIGNAL_UNSPEC:
|
|
|
|
if (len != 1)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
bss->signal_strength =
|
|
|
|
signal_unspec_to_mbm(l_get_u8(data));
|
|
|
|
break;
|
2015-05-01 16:17:54 +02:00
|
|
|
case NL80211_BSS_INFORMATION_ELEMENTS:
|
2019-11-21 21:15:56 +01:00
|
|
|
ies = data;
|
|
|
|
ies_len = len;
|
2015-05-01 16:17:54 +02:00
|
|
|
break;
|
2019-11-15 19:39:25 +01:00
|
|
|
case NL80211_BSS_PARENT_TSF:
|
|
|
|
if (len != sizeof(uint64_t))
|
|
|
|
goto fail;
|
|
|
|
|
2019-11-20 18:31:42 +01:00
|
|
|
bss->parent_tsf = l_get_u64(data);
|
2019-11-15 19:39:25 +01:00
|
|
|
break;
|
2019-11-21 21:15:56 +01:00
|
|
|
case NL80211_BSS_PRESP_DATA:
|
|
|
|
bss->source_frame = SCAN_BSS_PROBE_RESP;
|
|
|
|
break;
|
|
|
|
case NL80211_BSS_BEACON_IES:
|
|
|
|
beacon_ies = data;
|
|
|
|
beacon_ies_len = len;
|
|
|
|
break;
|
2021-02-03 17:46:25 +01:00
|
|
|
case NL80211_BSS_SEEN_MS_AGO:
|
|
|
|
if (L_WARN_ON(len != sizeof(uint32_t)))
|
|
|
|
break;
|
|
|
|
|
|
|
|
*out_seen_ms_ago = l_get_u32(data);
|
|
|
|
break;
|
|
|
|
case NL80211_BSS_LAST_SEEN_BOOTTIME:
|
|
|
|
if (L_WARN_ON(len != sizeof(uint64_t)))
|
|
|
|
break;
|
|
|
|
|
2021-05-22 14:06:25 +02:00
|
|
|
bss->time_stamp = l_get_u64(data) / L_NSEC_PER_USEC;
|
2021-02-03 17:46:25 +01:00
|
|
|
break;
|
2015-05-01 16:17:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-21 21:15:56 +01:00
|
|
|
/*
|
|
|
|
* Try our best at deciding whether the IEs come from a Probe
|
|
|
|
* Response based on the hints explained in nl80211.h
|
|
|
|
* (enum nl80211_bss).
|
|
|
|
*/
|
|
|
|
if (bss->source_frame == SCAN_BSS_BEACON && ies && (
|
|
|
|
!beacon_ies ||
|
|
|
|
ies_len != beacon_ies_len ||
|
|
|
|
memcmp(ies, beacon_ies, ies_len)))
|
|
|
|
bss->source_frame = SCAN_BSS_PROBE_RESP;
|
|
|
|
|
2021-05-28 23:32:30 +02:00
|
|
|
/* Set data rate to something low, just in case estimation fails */
|
|
|
|
bss->data_rate = 2000000;
|
|
|
|
|
|
|
|
if (ies) {
|
2021-07-28 16:53:21 +02:00
|
|
|
int ret;
|
|
|
|
|
2021-05-28 23:32:30 +02:00
|
|
|
if (!scan_parse_bss_information_elements(bss, ies, ies_len))
|
|
|
|
goto fail;
|
|
|
|
|
2021-07-28 16:53:21 +02:00
|
|
|
ret = wiphy_estimate_data_rate(wiphy, ies, ies_len, bss,
|
|
|
|
&bss->data_rate);
|
|
|
|
if (ret < 0 && ret != -ENETUNREACH)
|
|
|
|
l_warn("wiphy_estimate_data_rate() failed");
|
2021-05-28 23:32:30 +02:00
|
|
|
}
|
2019-11-21 21:15:56 +01:00
|
|
|
|
2015-05-01 16:17:54 +02:00
|
|
|
return bss;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
scan_bss_free(bss);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2022-08-03 23:36:33 +02:00
|
|
|
static void scan_parse_attr_scan_frequencies(struct l_genl_attr *attr,
|
|
|
|
struct scan_freq_set *set)
|
2021-02-03 16:30:38 +01:00
|
|
|
{
|
|
|
|
uint16_t type, len;
|
|
|
|
const void *data;
|
|
|
|
|
|
|
|
while (l_genl_attr_next(attr, &type, &len, &data)) {
|
|
|
|
uint32_t freq;
|
|
|
|
|
|
|
|
if (len != sizeof(uint32_t))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
freq = *((uint32_t *) data);
|
|
|
|
scan_freq_set_add(set, freq);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-01 16:28:05 +02:00
|
|
|
static struct scan_bss *scan_parse_result(struct l_genl_msg *msg,
|
2021-05-28 23:32:30 +02:00
|
|
|
struct wiphy *wiphy,
|
2021-02-03 17:46:25 +01:00
|
|
|
uint32_t *out_seen_ms_ago)
|
2015-05-01 16:17:54 +02:00
|
|
|
{
|
|
|
|
struct l_genl_attr attr, nested;
|
2021-05-28 22:50:25 +02:00
|
|
|
uint16_t type;
|
2015-05-01 16:17:54 +02:00
|
|
|
struct scan_bss *bss = NULL;
|
|
|
|
|
|
|
|
if (!l_genl_attr_init(&attr, msg))
|
|
|
|
return NULL;
|
|
|
|
|
2021-05-28 22:50:25 +02:00
|
|
|
while (l_genl_attr_next(&attr, &type, NULL, NULL)) {
|
2015-05-01 16:17:54 +02:00
|
|
|
switch (type) {
|
|
|
|
case NL80211_ATTR_BSS:
|
|
|
|
if (!l_genl_attr_recurse(&attr, &nested))
|
|
|
|
return NULL;
|
|
|
|
|
2021-05-28 23:32:30 +02:00
|
|
|
bss = scan_parse_attr_bss(&nested, wiphy,
|
|
|
|
out_seen_ms_ago);
|
2015-05-01 16:17:54 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return bss;
|
|
|
|
}
|
|
|
|
|
2015-06-22 21:12:09 +02:00
|
|
|
static void scan_bss_compute_rank(struct scan_bss *bss)
|
2015-06-06 01:18:55 +02:00
|
|
|
{
|
2015-06-10 19:37:50 +02:00
|
|
|
static const double RANK_HIGH_UTILIZATION_FACTOR = 0.8;
|
|
|
|
static const double RANK_LOW_UTILIZATION_FACTOR = 1.2;
|
2015-06-06 01:18:55 +02:00
|
|
|
double rank;
|
|
|
|
uint32_t irank;
|
|
|
|
/*
|
2022-07-19 20:55:44 +02:00
|
|
|
* Maximum rate is 9607.8Mbps (HE)
|
2015-06-06 01:18:55 +02:00
|
|
|
*/
|
2022-07-19 20:55:44 +02:00
|
|
|
double max_rate = 9607800000;
|
2015-06-06 01:18:55 +02:00
|
|
|
|
2021-05-28 23:32:30 +02:00
|
|
|
rank = (double)bss->data_rate / max_rate * USHRT_MAX;
|
2015-06-06 01:18:55 +02:00
|
|
|
|
2022-11-13 15:07:35 +01:00
|
|
|
/* Prefer 5G networks over 2.4G and 6G */
|
|
|
|
if (bss->frequency >= 4900 && bss->frequency < 5900)
|
2015-06-06 01:18:55 +02:00
|
|
|
rank *= RANK_5G_FACTOR;
|
|
|
|
|
2022-11-13 15:07:35 +01:00
|
|
|
/* Prefer 6G networks over 2.4G and 5G */
|
|
|
|
if (bss->frequency >= 5900 && bss->frequency < 7200)
|
|
|
|
rank *= RANK_6G_FACTOR;
|
|
|
|
|
2022-07-19 20:55:44 +02:00
|
|
|
/* Rank loaded APs lower and lightly loaded APs higher */
|
2015-06-10 19:37:50 +02:00
|
|
|
if (bss->utilization >= 192)
|
|
|
|
rank *= RANK_HIGH_UTILIZATION_FACTOR;
|
|
|
|
else if (bss->utilization <= 63)
|
|
|
|
rank *= RANK_LOW_UTILIZATION_FACTOR;
|
2015-06-06 01:18:55 +02:00
|
|
|
|
|
|
|
irank = rank;
|
|
|
|
|
|
|
|
if (irank > USHRT_MAX)
|
|
|
|
bss->rank = USHRT_MAX;
|
|
|
|
else
|
|
|
|
bss->rank = irank;
|
|
|
|
}
|
|
|
|
|
2019-11-21 21:15:57 +01:00
|
|
|
struct scan_bss *scan_bss_new_from_probe_req(const struct mmpdu_header *mpdu,
|
|
|
|
const uint8_t *body,
|
|
|
|
size_t body_len,
|
|
|
|
uint32_t frequency, int rssi)
|
|
|
|
|
|
|
|
{
|
|
|
|
struct scan_bss *bss;
|
|
|
|
|
|
|
|
bss = l_new(struct scan_bss, 1);
|
|
|
|
memcpy(bss->addr, mpdu->address_2, 6);
|
|
|
|
bss->utilization = 127;
|
|
|
|
bss->source_frame = SCAN_BSS_PROBE_REQ;
|
|
|
|
bss->frequency = frequency;
|
|
|
|
bss->signal_strength = rssi;
|
|
|
|
|
|
|
|
if (!scan_parse_bss_information_elements(bss, body, body_len))
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
return bss;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
scan_bss_free(bss);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-05-01 05:21:31 +02:00
|
|
|
void scan_bss_free(struct scan_bss *bss)
|
|
|
|
{
|
|
|
|
l_free(bss->rsne);
|
2021-07-08 21:11:57 +02:00
|
|
|
l_free(bss->rsnxe);
|
2015-05-01 05:21:31 +02:00
|
|
|
l_free(bss->wpa);
|
2015-09-23 15:44:46 +02:00
|
|
|
l_free(bss->wsc);
|
2019-06-14 23:56:16 +02:00
|
|
|
l_free(bss->osen);
|
2019-07-12 19:30:26 +02:00
|
|
|
l_free(bss->rc_ie);
|
2020-08-20 18:11:44 +02:00
|
|
|
l_free(bss->wfd);
|
2021-09-22 20:26:51 +02:00
|
|
|
l_free(bss->owe_trans);
|
2019-11-21 21:15:56 +01:00
|
|
|
|
|
|
|
switch (bss->source_frame) {
|
|
|
|
case SCAN_BSS_PROBE_RESP:
|
|
|
|
if (!bss->p2p_probe_resp_info)
|
|
|
|
break;
|
|
|
|
|
|
|
|
p2p_clear_probe_resp(bss->p2p_probe_resp_info);
|
|
|
|
l_free(bss->p2p_probe_resp_info);
|
|
|
|
break;
|
|
|
|
case SCAN_BSS_PROBE_REQ:
|
|
|
|
if (!bss->p2p_probe_req_info)
|
|
|
|
break;
|
|
|
|
|
|
|
|
p2p_clear_probe_req(bss->p2p_probe_req_info);
|
|
|
|
l_free(bss->p2p_probe_req_info);
|
|
|
|
break;
|
|
|
|
case SCAN_BSS_BEACON:
|
|
|
|
if (!bss->p2p_beacon_info)
|
|
|
|
break;
|
|
|
|
|
|
|
|
p2p_clear_beacon(bss->p2p_beacon_info);
|
|
|
|
l_free(bss->p2p_beacon_info);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-05-01 05:21:31 +02:00
|
|
|
l_free(bss);
|
|
|
|
}
|
2015-05-18 13:31:38 +02:00
|
|
|
|
2018-11-15 18:28:46 +01:00
|
|
|
int scan_bss_get_rsn_info(const struct scan_bss *bss, struct ie_rsn_info *info)
|
2015-05-18 13:31:38 +02:00
|
|
|
{
|
2016-10-25 05:12:25 +02:00
|
|
|
/*
|
|
|
|
* If both an RSN and a WPA elements are present currently
|
|
|
|
* RSN takes priority and the WPA IE is ignored.
|
|
|
|
*/
|
2015-05-18 13:31:38 +02:00
|
|
|
if (bss->rsne) {
|
|
|
|
int res = ie_parse_rsne_from_data(bss->rsne, bss->rsne[1] + 2,
|
2016-10-25 05:12:25 +02:00
|
|
|
info);
|
2015-05-18 13:31:38 +02:00
|
|
|
if (res < 0) {
|
|
|
|
l_debug("Cannot parse RSN field (%d, %s)",
|
|
|
|
res, strerror(-res));
|
2016-10-25 05:12:25 +02:00
|
|
|
return res;
|
2015-05-18 13:31:38 +02:00
|
|
|
}
|
|
|
|
} else if (bss->wpa) {
|
|
|
|
int res = ie_parse_wpa_from_data(bss->wpa, bss->wpa[1] + 2,
|
2016-10-25 05:12:25 +02:00
|
|
|
info);
|
2015-05-18 13:31:38 +02:00
|
|
|
if (res < 0) {
|
|
|
|
l_debug("Cannot parse WPA IE (%d, %s)",
|
|
|
|
res, strerror(-res));
|
2016-10-25 05:12:25 +02:00
|
|
|
return res;
|
2015-05-18 13:31:38 +02:00
|
|
|
}
|
2019-06-11 00:46:58 +02:00
|
|
|
} else if (bss->osen) {
|
|
|
|
int res = ie_parse_osen_from_data(bss->osen, bss->osen[1] + 2,
|
|
|
|
info);
|
|
|
|
if (res < 0) {
|
|
|
|
l_debug("Cannot parse OSEN IE (%d, %s)",
|
|
|
|
res, strerror(-res));
|
|
|
|
return res;
|
|
|
|
}
|
2015-05-18 13:31:38 +02:00
|
|
|
} else
|
2016-10-25 05:12:25 +02:00
|
|
|
return -ENOENT;
|
2015-05-18 13:31:38 +02:00
|
|
|
|
2016-10-25 05:12:25 +02:00
|
|
|
return 0;
|
2015-05-18 13:31:38 +02:00
|
|
|
}
|
2015-06-01 16:28:05 +02:00
|
|
|
|
2022-02-24 18:04:54 +01:00
|
|
|
int scan_bss_get_security(const struct scan_bss *bss, enum security *security)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct ie_rsn_info info;
|
|
|
|
|
|
|
|
ret = scan_bss_get_rsn_info(bss, &info);
|
|
|
|
if (ret < 0) {
|
|
|
|
if (ret != -ENOENT)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
*security = security_determine(bss->capability, NULL);
|
|
|
|
} else
|
|
|
|
*security = security_determine(bss->capability, &info);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-06-16 20:56:03 +02:00
|
|
|
int scan_bss_rank_compare(const void *a, const void *b, void *user_data)
|
|
|
|
{
|
|
|
|
const struct scan_bss *new_bss = a, *bss = b;
|
|
|
|
|
2021-11-08 20:39:29 +01:00
|
|
|
if (bss->rank == new_bss->rank)
|
|
|
|
return (bss->signal_strength >
|
|
|
|
new_bss->signal_strength) ? 1 : -1;
|
|
|
|
|
2020-08-14 15:40:33 +02:00
|
|
|
return (bss->rank > new_bss->rank) ? 1 : -1;
|
2015-06-16 20:56:03 +02:00
|
|
|
}
|
|
|
|
|
2015-06-01 16:28:05 +02:00
|
|
|
static void get_scan_callback(struct l_genl_msg *msg, void *user_data)
|
|
|
|
{
|
|
|
|
struct scan_results *results = user_data;
|
2019-07-08 16:02:56 +02:00
|
|
|
struct scan_context *sc = results->sc;
|
2015-06-01 16:28:05 +02:00
|
|
|
struct scan_bss *bss;
|
2019-07-08 16:02:59 +02:00
|
|
|
uint64_t wdev_id;
|
2021-02-03 17:46:25 +01:00
|
|
|
uint32_t seen_ms_ago = 0;
|
2015-06-01 16:28:05 +02:00
|
|
|
|
2021-05-28 22:50:25 +02:00
|
|
|
if (nl80211_parse_attrs(msg, NL80211_ATTR_WDEV, &wdev_id,
|
|
|
|
NL80211_ATTR_UNSPEC) < 0)
|
2015-06-01 16:28:05 +02:00
|
|
|
return;
|
|
|
|
|
2019-07-08 16:02:59 +02:00
|
|
|
if (wdev_id != sc->wdev_id) {
|
|
|
|
l_warn("wdev mismatch in get_scan_callback");
|
2015-06-01 16:28:05 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-05-28 23:32:30 +02:00
|
|
|
bss = scan_parse_result(msg, sc->wiphy, &seen_ms_ago);
|
2021-05-28 22:50:25 +02:00
|
|
|
if (!bss)
|
|
|
|
return;
|
|
|
|
|
2021-02-03 17:46:25 +01:00
|
|
|
if (!bss->time_stamp)
|
|
|
|
bss->time_stamp = results->time_stamp -
|
|
|
|
seen_ms_ago * L_USEC_PER_MSEC;
|
2019-04-11 21:14:24 +02:00
|
|
|
|
2015-06-06 01:18:55 +02:00
|
|
|
scan_bss_compute_rank(bss);
|
2015-06-16 20:56:03 +02:00
|
|
|
l_queue_insert(results->bss_list, bss, scan_bss_rank_compare, NULL);
|
2015-06-01 16:28:05 +02:00
|
|
|
}
|
|
|
|
|
2018-07-11 00:47:01 +02:00
|
|
|
static void discover_hidden_network_bsses(struct scan_context *sc,
|
|
|
|
struct l_queue *bss_list)
|
|
|
|
{
|
|
|
|
const struct l_queue_entry *bss_entry;
|
|
|
|
|
|
|
|
for (bss_entry = l_queue_get_entries(bss_list); bss_entry;
|
|
|
|
bss_entry = bss_entry->next) {
|
|
|
|
struct scan_bss *bss = bss_entry->data;
|
|
|
|
|
|
|
|
if (!util_ssid_is_hidden(bss->ssid_len, bss->ssid))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
sc->sp.needs_active_scan = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-08 16:02:56 +02:00
|
|
|
static void scan_finished(struct scan_context *sc,
|
2019-05-08 03:15:23 +02:00
|
|
|
int err, struct l_queue *bss_list,
|
2021-02-03 17:02:40 +01:00
|
|
|
const struct scan_freq_set *freqs,
|
2019-05-08 03:15:23 +02:00
|
|
|
struct scan_request *sr)
|
2015-06-01 16:28:05 +02:00
|
|
|
{
|
|
|
|
bool new_owner = false;
|
2022-01-19 21:20:48 +01:00
|
|
|
scan_notify_func_t callback = sr ? sr->callback : sc->sp.callback;
|
|
|
|
void *userdata = sr ? sr->userdata : sc->sp.userdata;
|
2015-06-01 16:28:05 +02:00
|
|
|
|
2019-05-08 03:15:23 +02:00
|
|
|
if (bss_list)
|
|
|
|
discover_hidden_network_bsses(sc, bss_list);
|
|
|
|
|
2022-01-19 21:20:48 +01:00
|
|
|
if (sr)
|
2022-01-19 21:39:27 +01:00
|
|
|
sr->in_callback = true;
|
2015-10-06 05:37:12 +02:00
|
|
|
|
2022-01-19 21:20:48 +01:00
|
|
|
if (callback)
|
|
|
|
new_owner = callback(err, bss_list, freqs, userdata);
|
2015-10-06 05:37:12 +02:00
|
|
|
|
2017-03-14 16:11:34 +01:00
|
|
|
if (bss_list && !new_owner)
|
|
|
|
l_queue_destroy(bss_list,
|
|
|
|
(l_queue_destroy_func_t) scan_bss_free);
|
2022-01-19 21:20:48 +01:00
|
|
|
|
|
|
|
if (!sr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Can start a new scan now that we've removed this one from the
|
|
|
|
* queue. If this were an external scan request (sr NULL) then the
|
|
|
|
* SCAN_FINISHED or SCAN_ABORTED handler would have taken care of
|
|
|
|
* sending the next command for a new or ongoing scan.
|
|
|
|
*/
|
2022-01-19 21:39:27 +01:00
|
|
|
sr->in_callback = false;
|
|
|
|
l_queue_remove(sc->requests, sr);
|
2022-01-19 21:20:48 +01:00
|
|
|
wiphy_radio_work_done(sc->wiphy, sr->work.id);
|
2017-03-14 16:11:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void get_scan_done(void *user)
|
|
|
|
{
|
|
|
|
struct scan_results *results = user;
|
2019-07-08 16:02:56 +02:00
|
|
|
struct scan_context *sc = results->sc;
|
2017-03-14 16:11:34 +01:00
|
|
|
|
2019-07-08 16:02:57 +02:00
|
|
|
sc->get_scan_cmd_id = 0;
|
|
|
|
|
2022-01-19 23:00:41 +01:00
|
|
|
if (!results->sr || !results->sr->canceled)
|
2021-02-03 17:02:40 +01:00
|
|
|
scan_finished(sc, 0, results->bss_list,
|
2022-08-05 17:03:03 +02:00
|
|
|
results->freqs, results->sr);
|
2017-03-14 16:11:34 +01:00
|
|
|
else
|
2015-06-01 16:28:05 +02:00
|
|
|
l_queue_destroy(results->bss_list,
|
|
|
|
(l_queue_destroy_func_t) scan_bss_free);
|
|
|
|
|
2022-08-05 17:03:03 +02:00
|
|
|
if (!results->sr)
|
|
|
|
scan_freq_set_free(results->freqs);
|
|
|
|
|
2015-06-01 16:28:05 +02:00
|
|
|
l_free(results);
|
|
|
|
}
|
|
|
|
|
2022-08-05 19:45:17 +02:00
|
|
|
static void scan_get_results(struct scan_context *sc, struct scan_request *sr,
|
|
|
|
struct scan_freq_set *freqs)
|
|
|
|
{
|
|
|
|
struct scan_results *results;
|
|
|
|
struct l_genl_msg *scan_msg;
|
|
|
|
|
|
|
|
results = l_new(struct scan_results, 1);
|
|
|
|
results->sc = sc;
|
|
|
|
results->time_stamp = l_time_now();
|
|
|
|
results->sr = sr;
|
|
|
|
results->bss_list = l_queue_new();
|
|
|
|
results->freqs = freqs;
|
|
|
|
|
|
|
|
scan_msg = l_genl_msg_new_sized(NL80211_CMD_GET_SCAN, 8);
|
|
|
|
|
|
|
|
l_genl_msg_append_attr(scan_msg, NL80211_ATTR_WDEV, 8,
|
|
|
|
&sc->wdev_id);
|
|
|
|
sc->get_scan_cmd_id = l_genl_family_dump(nl80211, scan_msg,
|
|
|
|
get_scan_callback,
|
|
|
|
results, get_scan_done);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void scan_wiphy_watch(struct wiphy *wiphy,
|
|
|
|
enum wiphy_state_watch_event event,
|
|
|
|
void *user_data)
|
|
|
|
{
|
|
|
|
struct scan_context *sc = user_data;
|
|
|
|
struct scan_request *sr = NULL;
|
|
|
|
struct l_genl_msg *msg = NULL;
|
|
|
|
struct scan_parameters params = { 0 };
|
|
|
|
struct scan_freq_set *freqs_6ghz;
|
|
|
|
struct scan_freq_set *allowed;
|
|
|
|
bool allow_6g;
|
|
|
|
|
2022-12-12 23:08:57 +01:00
|
|
|
/* Only care about completed regulatory dumps */
|
|
|
|
if (event != WIPHY_STATE_WATCH_EVENT_REGDOM_DONE)
|
2022-08-05 19:45:17 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
if (!sc->sp.id)
|
|
|
|
return;
|
|
|
|
|
|
|
|
sr = l_queue_find(sc->requests, scan_request_match,
|
|
|
|
L_UINT_TO_PTR(sc->sp.id));
|
|
|
|
if (!sr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
allowed = scan_get_allowed_freqs(sc);
|
|
|
|
allow_6g = scan_freq_set_get_bands(allowed) & BAND_FREQ_6_GHZ;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This update did not allow 6GHz, or the original request was
|
|
|
|
* not expecting 6GHz. The periodic scan should now be ended.
|
|
|
|
*/
|
|
|
|
if (!allow_6g || !sr->split) {
|
|
|
|
scan_get_results(sc, sr, sr->freqs_scanned);
|
|
|
|
goto free_allowed;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* At this point we know there is an ongoing periodic scan.
|
|
|
|
* Create a new 6GHz passive scan request and append to the
|
|
|
|
* command list
|
|
|
|
*/
|
|
|
|
freqs_6ghz = scan_freq_set_clone(allowed, BAND_FREQ_6_GHZ);
|
|
|
|
|
|
|
|
msg = scan_build_cmd(sc, false, true, ¶ms, freqs_6ghz);
|
|
|
|
l_queue_push_tail(sr->cmds, msg);
|
|
|
|
|
|
|
|
scan_freq_set_free(freqs_6ghz);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If this periodic scan is at the top of the queue, continue
|
|
|
|
* running it.
|
|
|
|
*/
|
|
|
|
if (l_queue_peek_head(sc->requests) == sr)
|
|
|
|
start_next_scan_request(&sr->work);
|
|
|
|
|
|
|
|
free_allowed:
|
|
|
|
scan_freq_set_free(allowed);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct scan_context *scan_context_new(uint64_t wdev_id)
|
|
|
|
{
|
|
|
|
struct wiphy *wiphy = wiphy_find_by_wdev(wdev_id);
|
|
|
|
struct scan_context *sc;
|
|
|
|
|
|
|
|
if (!wiphy)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
sc = l_new(struct scan_context, 1);
|
|
|
|
|
|
|
|
sc->wdev_id = wdev_id;
|
|
|
|
sc->wiphy = wiphy;
|
|
|
|
sc->state = SCAN_STATE_NOT_RUNNING;
|
|
|
|
sc->requests = l_queue_new();
|
|
|
|
sc->wiphy_watch_id = wiphy_state_watch_add(wiphy, scan_wiphy_watch,
|
|
|
|
sc, NULL);
|
|
|
|
|
|
|
|
return sc;
|
|
|
|
}
|
|
|
|
|
2019-05-11 01:18:55 +02:00
|
|
|
static bool scan_parse_flush_flag_from_msg(struct l_genl_msg *msg)
|
|
|
|
{
|
|
|
|
struct l_genl_attr attr;
|
|
|
|
uint16_t type, len;
|
|
|
|
const void *data;
|
|
|
|
|
|
|
|
if (!l_genl_attr_init(&attr, msg))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
while (l_genl_attr_next(&attr, &type, &len, &data))
|
|
|
|
if (type == NL80211_SCAN_FLAG_FLUSH)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-08-03 23:36:33 +02:00
|
|
|
static void scan_parse_result_frequencies(struct l_genl_msg *msg,
|
|
|
|
struct scan_freq_set *freqs)
|
2021-02-03 16:30:38 +01:00
|
|
|
{
|
|
|
|
struct l_genl_attr attr, nested;
|
|
|
|
uint16_t type, len;
|
|
|
|
const void *data;
|
|
|
|
|
|
|
|
if (!l_genl_attr_init(&attr, msg))
|
|
|
|
return;
|
|
|
|
|
|
|
|
while (l_genl_attr_next(&attr, &type, &len, &data)) {
|
|
|
|
switch (type) {
|
|
|
|
case NL80211_ATTR_SCAN_FREQUENCIES:
|
|
|
|
if (!l_genl_attr_recurse(&attr, &nested)) {
|
|
|
|
l_warn("Failed to parse ATTR_SCAN_FREQUENCIES");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2022-08-03 23:36:33 +02:00
|
|
|
scan_parse_attr_scan_frequencies(&nested, freqs);
|
2021-02-03 16:30:38 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
scan: retry scan based on scan done events per wiphy, not wdev
If a CMD_TRIGGER_SCAN request fails with -EBUSY, iwd currently assumes
that a scan is ongoing on the underlying wdev and will retry the same
command when that scan is complete. It gets notified of that completion
via the scan_notify() function, and kicks the scan logic to try again.
However, if there is another wdev on the same wiphy and that wdev has a
scan request in flight, the kernel will also return -EBUSY. In other
words, only one scan request per wiphy is permitted.
As an example, the brcmfmac driver can create an AP interface on the
same wiphy as the default station interface, and scans can be triggered
on that AP interface.
If -EBUSY is returned because another wdev is scanning, then iwd won't
know when it can retry the original trigger request because the relevant
netlink event will arrive on a different wdev. Indeed, if no scan
context exists for that other wdev, then scan_notify will return early
and the scan logic will stall indefinitely.
Instead, and in the event that no scan context matches, use it as a cue
to retry a pending scan request that happens to be destined for the same
wiphy.
2022-11-17 22:11:39 +01:00
|
|
|
static void scan_retry_pending(uint32_t wiphy_id)
|
|
|
|
{
|
|
|
|
const struct l_queue_entry *entry;
|
|
|
|
|
|
|
|
l_debug("");
|
|
|
|
|
|
|
|
for (entry = l_queue_get_entries(scan_contexts); entry;
|
|
|
|
entry = entry->next) {
|
|
|
|
struct scan_context *sc = entry->data;
|
|
|
|
struct scan_request *sr = l_queue_peek_head(sc->requests);
|
|
|
|
|
|
|
|
if (wiphy_get_id(sc->wiphy) != wiphy_id)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!sr)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!wiphy_radio_work_is_running(sc->wiphy, sr->work.id))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
sc->state = SCAN_STATE_NOT_RUNNING;
|
|
|
|
start_next_scan_request(&sr->work);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-01 16:28:05 +02:00
|
|
|
static void scan_notify(struct l_genl_msg *msg, void *user_data)
|
|
|
|
{
|
|
|
|
struct l_genl_attr attr;
|
|
|
|
uint16_t type, len;
|
|
|
|
const void *data;
|
|
|
|
uint8_t cmd;
|
2019-10-16 23:33:40 +02:00
|
|
|
uint64_t wdev_id;
|
|
|
|
uint32_t wiphy_id;
|
2015-09-20 21:58:23 +02:00
|
|
|
struct scan_context *sc;
|
|
|
|
bool active_scan = false;
|
2019-11-19 23:10:23 +01:00
|
|
|
uint64_t start_time_tsf = 0;
|
|
|
|
struct scan_request *sr;
|
2015-06-01 16:28:05 +02:00
|
|
|
|
|
|
|
cmd = l_genl_msg_get_command(msg);
|
|
|
|
|
2019-10-16 23:33:40 +02:00
|
|
|
if (nl80211_parse_attrs(msg, NL80211_ATTR_WDEV, &wdev_id,
|
|
|
|
NL80211_ATTR_WIPHY, &wiphy_id,
|
|
|
|
NL80211_ATTR_UNSPEC) < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
sc = l_queue_find(scan_contexts, scan_context_match, &wdev_id);
|
scan: retry scan based on scan done events per wiphy, not wdev
If a CMD_TRIGGER_SCAN request fails with -EBUSY, iwd currently assumes
that a scan is ongoing on the underlying wdev and will retry the same
command when that scan is complete. It gets notified of that completion
via the scan_notify() function, and kicks the scan logic to try again.
However, if there is another wdev on the same wiphy and that wdev has a
scan request in flight, the kernel will also return -EBUSY. In other
words, only one scan request per wiphy is permitted.
As an example, the brcmfmac driver can create an AP interface on the
same wiphy as the default station interface, and scans can be triggered
on that AP interface.
If -EBUSY is returned because another wdev is scanning, then iwd won't
know when it can retry the original trigger request because the relevant
netlink event will arrive on a different wdev. Indeed, if no scan
context exists for that other wdev, then scan_notify will return early
and the scan logic will stall indefinitely.
Instead, and in the event that no scan context matches, use it as a cue
to retry a pending scan request that happens to be destined for the same
wiphy.
2022-11-17 22:11:39 +01:00
|
|
|
if (!sc) {
|
|
|
|
/*
|
|
|
|
* If the event is for an unmanaged device, retry pending scan
|
|
|
|
* requests on the same wiphy.
|
|
|
|
*/
|
|
|
|
if (cmd == NL80211_CMD_NEW_SCAN_RESULTS ||
|
|
|
|
cmd == NL80211_CMD_SCAN_ABORTED)
|
|
|
|
scan_retry_pending(wiphy_id);
|
|
|
|
|
2019-10-16 23:33:40 +02:00
|
|
|
return;
|
scan: retry scan based on scan done events per wiphy, not wdev
If a CMD_TRIGGER_SCAN request fails with -EBUSY, iwd currently assumes
that a scan is ongoing on the underlying wdev and will retry the same
command when that scan is complete. It gets notified of that completion
via the scan_notify() function, and kicks the scan logic to try again.
However, if there is another wdev on the same wiphy and that wdev has a
scan request in flight, the kernel will also return -EBUSY. In other
words, only one scan request per wiphy is permitted.
As an example, the brcmfmac driver can create an AP interface on the
same wiphy as the default station interface, and scans can be triggered
on that AP interface.
If -EBUSY is returned because another wdev is scanning, then iwd won't
know when it can retry the original trigger request because the relevant
netlink event will arrive on a different wdev. Indeed, if no scan
context exists for that other wdev, then scan_notify will return early
and the scan logic will stall indefinitely.
Instead, and in the event that no scan context matches, use it as a cue
to retry a pending scan request that happens to be destined for the same
wiphy.
2022-11-17 22:11:39 +01:00
|
|
|
}
|
2019-10-16 23:33:40 +02:00
|
|
|
|
2022-01-12 19:08:41 +01:00
|
|
|
l_debug("Scan notification %s(%u)", nl80211cmd_to_string(cmd), cmd);
|
|
|
|
|
2015-06-01 16:28:05 +02:00
|
|
|
if (!l_genl_attr_init(&attr, msg))
|
|
|
|
return;
|
|
|
|
|
|
|
|
while (l_genl_attr_next(&attr, &type, &len, &data)) {
|
|
|
|
switch (type) {
|
2015-09-20 21:58:23 +02:00
|
|
|
case NL80211_ATTR_SCAN_SSIDS:
|
|
|
|
active_scan = true;
|
|
|
|
break;
|
2019-11-19 23:10:23 +01:00
|
|
|
case NL80211_ATTR_SCAN_START_TIME_TSF:
|
|
|
|
if (len != sizeof(uint64_t))
|
|
|
|
return;
|
|
|
|
|
|
|
|
start_time_tsf = l_get_u64(data);
|
|
|
|
break;
|
2015-06-01 16:28:05 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-19 23:10:23 +01:00
|
|
|
sr = l_queue_peek_head(sc->requests);
|
|
|
|
|
2015-06-01 16:28:05 +02:00
|
|
|
switch (cmd) {
|
|
|
|
case NL80211_CMD_NEW_SCAN_RESULTS:
|
|
|
|
{
|
2022-08-05 19:45:17 +02:00
|
|
|
struct scan_freq_set *freqs;
|
2019-05-08 03:15:23 +02:00
|
|
|
bool send_next = false;
|
2022-01-18 18:47:39 +01:00
|
|
|
bool retry = false;
|
2019-05-08 03:15:23 +02:00
|
|
|
bool get_results = false;
|
|
|
|
|
2019-03-11 15:43:14 +01:00
|
|
|
sc->state = SCAN_STATE_NOT_RUNNING;
|
|
|
|
|
2019-05-08 03:15:23 +02:00
|
|
|
/* Was this our own scan or an external scan */
|
2022-01-19 18:19:27 +01:00
|
|
|
if (sr && sr->triggered) {
|
|
|
|
sr->triggered = false;
|
2019-07-15 22:52:01 +02:00
|
|
|
|
2019-05-08 03:15:23 +02:00
|
|
|
if (!sr->callback) {
|
2021-02-03 17:02:40 +01:00
|
|
|
scan_finished(sc, -ECANCELED, NULL, NULL, sr);
|
2019-05-08 03:15:23 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2022-08-05 19:45:17 +02:00
|
|
|
/* Regdom changed during a periodic scan */
|
|
|
|
if (sc->sp.id == sr->work.id &&
|
|
|
|
wiphy_regdom_is_updating(sc->wiphy)) {
|
|
|
|
scan_parse_result_frequencies(msg,
|
|
|
|
sr->freqs_scanned);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-05-08 03:15:23 +02:00
|
|
|
/*
|
|
|
|
* If this was the last command for the current request
|
|
|
|
* avoid starting the next request until the GET_SCAN
|
|
|
|
* dump callback so that any current request is always
|
|
|
|
* at the top of the queue and handling is simpler.
|
|
|
|
*/
|
|
|
|
if (l_queue_isempty(sr->cmds))
|
|
|
|
get_results = true;
|
2022-08-03 23:36:33 +02:00
|
|
|
else {
|
|
|
|
scan_parse_result_frequencies(msg,
|
|
|
|
sr->freqs_scanned);
|
2019-05-08 03:15:23 +02:00
|
|
|
send_next = true;
|
2022-08-03 23:36:33 +02:00
|
|
|
}
|
2019-05-08 03:15:23 +02:00
|
|
|
} else {
|
|
|
|
if (sc->get_scan_cmd_id)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (sc->sp.callback)
|
|
|
|
get_results = true;
|
|
|
|
|
2022-01-18 18:47:39 +01:00
|
|
|
/*
|
|
|
|
* Drop the ongoing scan if an external scan flushed
|
|
|
|
* our results. Otherwise, try to retry the trigger
|
|
|
|
* request if it failed with an -EBUSY.
|
|
|
|
*/
|
2022-01-18 22:10:07 +01:00
|
|
|
if (sr && sr->started &&
|
|
|
|
scan_parse_flush_flag_from_msg(msg))
|
2021-02-03 17:02:40 +01:00
|
|
|
scan_finished(sc, -EAGAIN, NULL, NULL, sr);
|
2020-06-08 10:42:07 +02:00
|
|
|
else
|
2022-01-18 18:47:39 +01:00
|
|
|
retry = true;
|
2019-05-08 03:15:23 +02:00
|
|
|
|
|
|
|
sr = NULL;
|
|
|
|
}
|
|
|
|
|
2020-07-10 19:44:43 +02:00
|
|
|
/*
|
2022-01-18 18:47:39 +01:00
|
|
|
* Send the next command of an ongoing request, or continue
|
|
|
|
* with a previously busy scan attempt due to an external
|
|
|
|
* scan.
|
2020-07-10 19:44:43 +02:00
|
|
|
*/
|
2022-01-18 18:47:39 +01:00
|
|
|
if (send_next || retry) {
|
2020-07-10 19:44:43 +02:00
|
|
|
struct scan_request *next = l_queue_peek_head(
|
|
|
|
sc->requests);
|
|
|
|
|
2022-01-18 18:47:39 +01:00
|
|
|
if (next && wiphy_radio_work_is_running(sc->wiphy,
|
|
|
|
next->work.id))
|
2020-07-10 19:44:43 +02:00
|
|
|
start_next_scan_request(&next->work);
|
|
|
|
}
|
2019-05-08 03:15:23 +02:00
|
|
|
|
|
|
|
if (!get_results)
|
|
|
|
break;
|
2018-07-11 00:47:00 +02:00
|
|
|
|
2022-08-05 17:03:03 +02:00
|
|
|
/*
|
|
|
|
* In case this was an external scan, setup a new, temporary
|
|
|
|
* frequency set to report the results to the periodic callback
|
|
|
|
*/
|
2022-08-05 19:45:17 +02:00
|
|
|
if (!sr)
|
|
|
|
freqs = scan_freq_set_new();
|
2022-08-05 17:03:03 +02:00
|
|
|
else
|
2022-08-05 19:45:17 +02:00
|
|
|
freqs = sr->freqs_scanned;
|
2022-08-05 17:03:03 +02:00
|
|
|
|
2022-08-05 19:45:17 +02:00
|
|
|
scan_parse_result_frequencies(msg, freqs);
|
2021-02-03 16:30:38 +01:00
|
|
|
|
2022-08-05 19:45:17 +02:00
|
|
|
scan_get_results(sc, sr, freqs);
|
2015-06-05 05:31:39 +02:00
|
|
|
|
2015-09-20 21:58:23 +02:00
|
|
|
break;
|
2015-06-01 16:28:05 +02:00
|
|
|
}
|
2015-09-20 21:58:23 +02:00
|
|
|
|
|
|
|
case NL80211_CMD_TRIGGER_SCAN:
|
|
|
|
if (active_scan)
|
|
|
|
sc->state = SCAN_STATE_ACTIVE;
|
|
|
|
else
|
|
|
|
sc->state = SCAN_STATE_PASSIVE;
|
|
|
|
|
2019-12-02 17:53:55 +01:00
|
|
|
if (sr)
|
|
|
|
sr->start_time_tsf = start_time_tsf;
|
2019-11-19 23:10:23 +01:00
|
|
|
|
2015-09-20 21:58:23 +02:00
|
|
|
break;
|
2016-04-12 00:53:06 +02:00
|
|
|
|
|
|
|
case NL80211_CMD_SCAN_ABORTED:
|
2019-03-11 15:43:14 +01:00
|
|
|
sc->state = SCAN_STATE_NOT_RUNNING;
|
|
|
|
|
2022-01-19 04:25:47 +01:00
|
|
|
/*
|
|
|
|
* If there's nothing pending, then most likely an external
|
|
|
|
* scan got aborted. We don't care, ignore.
|
|
|
|
*/
|
|
|
|
if (!sr)
|
|
|
|
break;
|
|
|
|
|
2022-01-19 18:19:27 +01:00
|
|
|
if (sr->triggered) {
|
|
|
|
sr->triggered = false;
|
2022-12-12 22:10:50 +01:00
|
|
|
scan_finished(sc, -ECANCELED, NULL, NULL, sr);
|
2022-01-19 04:25:47 +01:00
|
|
|
} else if (wiphy_radio_work_is_running(sc->wiphy,
|
|
|
|
sr->work.id)) {
|
2019-05-08 03:15:23 +02:00
|
|
|
/*
|
|
|
|
* If this was an external scan that got aborted
|
|
|
|
* we may be able to now queue our own scan although
|
|
|
|
* the abort could also have been triggered by the
|
|
|
|
* hardware or the driver because of another activity
|
|
|
|
* starting in which case we should just get an EBUSY.
|
|
|
|
*/
|
2022-01-19 04:25:47 +01:00
|
|
|
start_next_scan_request(&sr->work);
|
2019-05-08 03:15:23 +02:00
|
|
|
}
|
2017-03-13 16:30:46 +01:00
|
|
|
|
2016-04-12 00:53:06 +02:00
|
|
|
break;
|
2015-06-01 16:28:05 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-15 18:29:28 +01:00
|
|
|
static void get_fw_scan_done(void *userdata)
|
|
|
|
{
|
|
|
|
struct scan_results *results = userdata;
|
|
|
|
struct scan_request *sr = results->sr;
|
|
|
|
struct scan_context *sc = results->sc;
|
|
|
|
int err = l_queue_length(results->bss_list) == 0 ? -ENOENT : 0;
|
|
|
|
bool new_owner = false;
|
|
|
|
|
|
|
|
sc->get_fw_scan_cmd_id = 0;
|
|
|
|
|
|
|
|
if (sr->callback)
|
|
|
|
new_owner = sr->callback(err, results->bss_list, NULL,
|
|
|
|
sr->userdata);
|
|
|
|
|
|
|
|
if (!new_owner)
|
|
|
|
l_queue_destroy(results->bss_list,
|
|
|
|
(l_queue_destroy_func_t) scan_bss_free);
|
|
|
|
|
|
|
|
if (sr->destroy)
|
|
|
|
sr->destroy(sr->userdata);
|
|
|
|
|
|
|
|
l_free(sr);
|
|
|
|
l_free(results);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool scan_get_firmware_scan(uint64_t wdev_id, scan_notify_func_t notify,
|
|
|
|
void *userdata, scan_destroy_func_t destroy)
|
|
|
|
{
|
|
|
|
struct l_genl_msg *scan_msg;
|
|
|
|
struct scan_results *results;
|
|
|
|
struct scan_request *sr;
|
|
|
|
struct scan_context *sc = l_queue_find(scan_contexts,
|
|
|
|
scan_context_match, &wdev_id);
|
|
|
|
|
|
|
|
if (!sc)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
sr = l_new(struct scan_request, 1);
|
|
|
|
sr->callback = notify;
|
|
|
|
sr->destroy = destroy;
|
|
|
|
sr->userdata = userdata;
|
|
|
|
|
|
|
|
results = l_new(struct scan_results, 1);
|
|
|
|
results->sc = sc;
|
|
|
|
results->time_stamp = l_time_now();
|
|
|
|
results->bss_list = l_queue_new();
|
|
|
|
results->sr = sr;
|
|
|
|
|
|
|
|
scan_msg = l_genl_msg_new_sized(NL80211_CMD_GET_SCAN, 8);
|
|
|
|
l_genl_msg_append_attr(scan_msg, NL80211_ATTR_WDEV, 8, &sc->wdev_id);
|
|
|
|
|
|
|
|
sc->get_fw_scan_cmd_id = l_genl_family_dump(nl80211, scan_msg,
|
|
|
|
get_scan_callback,
|
|
|
|
results,
|
|
|
|
get_fw_scan_done);
|
|
|
|
if (!sc->get_fw_scan_cmd_id) {
|
|
|
|
l_queue_destroy(results->bss_list,
|
|
|
|
(l_queue_destroy_func_t) scan_bss_free);
|
|
|
|
l_free(results);
|
|
|
|
l_free(sr);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-07-08 16:02:59 +02:00
|
|
|
bool scan_wdev_add(uint64_t wdev_id)
|
2015-06-01 16:28:05 +02:00
|
|
|
{
|
2019-05-28 21:12:25 +02:00
|
|
|
struct scan_context *sc;
|
2019-02-26 19:31:58 +01:00
|
|
|
|
2019-07-08 16:02:59 +02:00
|
|
|
if (l_queue_find(scan_contexts, scan_context_match, &wdev_id))
|
2015-06-01 16:28:05 +02:00
|
|
|
return false;
|
|
|
|
|
2019-07-08 16:02:59 +02:00
|
|
|
sc = scan_context_new(wdev_id);
|
2019-05-28 21:12:25 +02:00
|
|
|
if (!sc)
|
|
|
|
return false;
|
2015-06-01 16:28:05 +02:00
|
|
|
|
2019-05-28 21:12:25 +02:00
|
|
|
l_queue_push_head(scan_contexts, sc);
|
|
|
|
|
|
|
|
if (l_queue_length(scan_contexts) > 1)
|
|
|
|
goto done;
|
2019-02-26 19:31:58 +01:00
|
|
|
|
2019-05-28 21:12:25 +02:00
|
|
|
nl80211 = l_genl_family_new(iwd_get_genl(), NL80211_GENL_NAME);
|
|
|
|
l_genl_family_register(nl80211, "scan", scan_notify, NULL, NULL);
|
|
|
|
|
|
|
|
done:
|
2015-06-01 16:28:05 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-07-08 16:02:59 +02:00
|
|
|
bool scan_wdev_remove(uint64_t wdev_id)
|
2015-06-01 16:28:05 +02:00
|
|
|
{
|
2019-05-28 21:12:25 +02:00
|
|
|
struct scan_context *sc;
|
2015-06-05 05:31:39 +02:00
|
|
|
|
2019-07-08 16:02:59 +02:00
|
|
|
sc = l_queue_remove_if(scan_contexts, scan_context_match, &wdev_id);
|
2015-09-22 21:07:27 +02:00
|
|
|
|
2019-05-28 21:12:25 +02:00
|
|
|
if (!sc)
|
2015-06-01 16:28:05 +02:00
|
|
|
return false;
|
|
|
|
|
2019-07-08 16:02:59 +02:00
|
|
|
l_info("Removing scan context for wdev %" PRIx64, wdev_id);
|
2019-05-28 21:12:25 +02:00
|
|
|
scan_context_free(sc);
|
2015-06-05 05:31:39 +02:00
|
|
|
|
2019-05-28 21:12:25 +02:00
|
|
|
if (l_queue_isempty(scan_contexts)) {
|
|
|
|
l_genl_family_free(nl80211);
|
|
|
|
nl80211 = NULL;
|
|
|
|
}
|
2015-06-01 16:28:05 +02:00
|
|
|
|
2019-05-28 21:12:25 +02:00
|
|
|
return true;
|
|
|
|
}
|
2015-09-22 21:07:02 +02:00
|
|
|
|
2019-05-28 21:12:25 +02:00
|
|
|
static int scan_init(void)
|
|
|
|
{
|
|
|
|
const struct l_settings *config = iwd_get_config();
|
|
|
|
|
|
|
|
scan_contexts = l_queue_new();
|
|
|
|
|
2019-10-24 21:13:33 +02:00
|
|
|
if (!l_settings_get_double(config, "Rank", "BandModifier5Ghz",
|
2019-05-28 21:12:25 +02:00
|
|
|
&RANK_5G_FACTOR))
|
|
|
|
RANK_5G_FACTOR = 1.0;
|
|
|
|
|
2022-11-13 15:07:35 +01:00
|
|
|
if (!l_settings_get_double(config, "Rank", "BandModifier6Ghz",
|
|
|
|
&RANK_6G_FACTOR))
|
|
|
|
RANK_6G_FACTOR = 1.0;
|
|
|
|
|
2021-02-12 16:41:14 +01:00
|
|
|
if (!l_settings_get_uint(config, "Scan", "InitialPeriodicScanInterval",
|
|
|
|
&SCAN_INIT_INTERVAL))
|
|
|
|
SCAN_INIT_INTERVAL = 10;
|
|
|
|
|
|
|
|
if (SCAN_INIT_INTERVAL > UINT16_MAX)
|
|
|
|
SCAN_INIT_INTERVAL = UINT16_MAX;
|
|
|
|
|
2021-02-12 16:41:13 +01:00
|
|
|
if (!l_settings_get_uint(config, "Scan", "MaximumPeriodicScanInterval",
|
|
|
|
&SCAN_MAX_INTERVAL))
|
|
|
|
SCAN_MAX_INTERVAL = 300;
|
|
|
|
|
|
|
|
if (SCAN_MAX_INTERVAL > UINT16_MAX)
|
|
|
|
SCAN_MAX_INTERVAL = UINT16_MAX;
|
|
|
|
|
2019-05-28 21:12:25 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-01-11 17:51:11 +01:00
|
|
|
static void scan_exit(void)
|
2019-05-28 21:12:25 +02:00
|
|
|
{
|
|
|
|
l_queue_destroy(scan_contexts,
|
|
|
|
(l_queue_destroy_func_t) scan_context_free);
|
|
|
|
scan_contexts = NULL;
|
|
|
|
l_genl_family_free(nl80211);
|
|
|
|
nl80211 = NULL;
|
2015-06-01 16:28:05 +02:00
|
|
|
}
|
2019-05-28 21:12:25 +02:00
|
|
|
|
|
|
|
IWD_MODULE(scan, scan_init, scan_exit)
|
2022-07-15 06:38:18 +02:00
|
|
|
IWD_MODULE_DEPENDS(scan, wiphy)
|