3
0
mirror of https://git.kernel.org/pub/scm/network/wireless/iwd.git synced 2024-11-24 16:49:25 +01:00
iwd/src/wiphy.c

2875 lines
66 KiB
C
Raw Normal View History

2014-07-29 21:25:01 +02:00
/*
*
* Wireless daemon for Linux
*
* Copyright (C) 2013-2019 Intel Corporation. All rights reserved.
2014-07-29 21:25:01 +02: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
#define _GNU_SOURCE
#include <stdlib.h>
2014-10-23 21:32:12 +02:00
#include <stdio.h>
#include <errno.h>
#include <linux/if_ether.h>
2017-03-16 22:45:10 +01:00
#include <fnmatch.h>
#include <unistd.h>
#include <string.h>
wiphy: introduce new radio management APIs These APIs will handle fairness and order in any operations which radios can only do sequentially (offchannel, scanning, connection etc.). Both scan and frame-xchg are complex modules (especially scanning) which is why the radio management APIs were implemented generic enough where the changes to both modules will be minimal. Any module that requires this kind of work can push a work item into the radio management work queue (wiphy_radio_work_insert) and when the work is ready to be started radio management will call back into the module. Once the work is completed (and this may be some time later e.g. in scan results or a frame watch) the module can signal back that the work is finished (wiphy_radio_work_done). Wiphy will then pop the queue and continue with the next work item. A concept of priority was added in order to allow important offchannel operations (e.g. ANQP) to take priority over other work items. The priority is an integer, where lower values are of a higher priority. The concept of priority cleanly solves a lot of the complexity that was added in order to support ANQP queries (suspending scanning and waiting for ANQP to finish before connecting). Instead ANQP queries can be queued at a higher priority than scanning which removes the need for suspending scans. In addition we can treat connections as radio management work and insert them at a lower priority than ANQP, but higher than scanning. This forces the connection to wait for ANQP without having to track any state.
2020-07-09 02:04:32 +02:00
#include <limits.h>
2014-07-29 21:25:01 +02:00
#include <ell/ell.h>
#include "linux/nl80211.h"
2016-06-16 22:40:38 +02:00
#include "ell/useful.h"
2020-02-03 18:54:28 +01:00
#include "src/missing.h"
2014-10-30 06:26:49 +01:00
#include "src/iwd.h"
#include "src/module.h"
#include "src/ie.h"
2016-06-16 22:40:38 +02:00
#include "src/crypto.h"
#include "src/scan.h"
#include "src/netdev.h"
#include "src/dbus.h"
#include "src/rfkill.h"
2016-06-16 22:40:38 +02:00
#include "src/wiphy.h"
#include "src/storage.h"
#include "src/util.h"
#include "src/common.h"
#include "src/watchlist.h"
#include "src/nl80211util.h"
#include "src/nl80211cmd.h"
#include "src/band.h"
2014-10-23 21:32:12 +02:00
#define EXT_CAP_LEN 10
2014-07-29 21:25:01 +02:00
static struct l_genl_family *nl80211 = NULL;
static struct l_hwdb *hwdb;
2017-03-16 22:45:10 +01:00
static char **whitelist_filter;
static char **blacklist_filter;
static int mac_randomize_bytes = 6;
static char regdom_country[2];
wiphy: introduce new radio management APIs These APIs will handle fairness and order in any operations which radios can only do sequentially (offchannel, scanning, connection etc.). Both scan and frame-xchg are complex modules (especially scanning) which is why the radio management APIs were implemented generic enough where the changes to both modules will be minimal. Any module that requires this kind of work can push a work item into the radio management work queue (wiphy_radio_work_insert) and when the work is ready to be started radio management will call back into the module. Once the work is completed (and this may be some time later e.g. in scan results or a frame watch) the module can signal back that the work is finished (wiphy_radio_work_done). Wiphy will then pop the queue and continue with the next work item. A concept of priority was added in order to allow important offchannel operations (e.g. ANQP) to take priority over other work items. The priority is an integer, where lower values are of a higher priority. The concept of priority cleanly solves a lot of the complexity that was added in order to support ANQP queries (suspending scanning and waiting for ANQP to finish before connecting). Instead ANQP queries can be queued at a higher priority than scanning which removes the need for suspending scans. In addition we can treat connections as radio management work and insert them at a lower priority than ANQP, but higher than scanning. This forces the connection to wait for ANQP without having to track any state.
2020-07-09 02:04:32 +02:00
static uint32_t work_ids;
static unsigned int wiphy_dump_id;
2014-07-29 21:25:01 +02:00
enum driver_flag {
DEFAULT_IF = 0x1,
FORCE_PAE = 0x2,
POWER_SAVE_DISABLE = 0x4,
OWE_DISABLE = 0x8,
};
struct driver_flag_name {
const char *name;
enum driver_flag flag;
};
struct driver_info {
const char *prefix;
unsigned int flags;
};
/*
* The out-of-tree rtl88x2bu crashes the kernel hard if default interface is
* destroyed. It seems many other drivers are built from the same source code
* so we set the DEFAULT_IF flag for all of them. Unfortunately there are
* in-tree drivers that also match these names and may be fine.
*/
static const struct driver_info driver_infos[] = {
{ "rtl81*", DEFAULT_IF },
{ "rtl87*", DEFAULT_IF },
{ "rtl88*", DEFAULT_IF },
{ "rtw_*", DEFAULT_IF },
{ "brcmfmac", DEFAULT_IF },
{ "bcmsdh_sdmmc", DEFAULT_IF },
};
static const struct driver_flag_name driver_flag_names[] = {
{ "DefaultInterface", DEFAULT_IF },
{ "ForcePae", FORCE_PAE },
{ "PowerSaveDisable", POWER_SAVE_DISABLE },
{ "OweDisable", OWE_DISABLE },
};
struct wiphy {
uint32_t id;
char name[20];
uint8_t permanent_addr[ETH_ALEN];
uint32_t feature_flags;
2018-05-02 03:45:32 +02:00
uint8_t ext_features[(NUM_NL80211_EXT_FEATURES + 7) / 8];
uint8_t max_num_ssids_per_scan;
uint32_t max_roc_duration;
uint16_t max_scan_ie_len;
uint16_t supported_iftypes;
uint16_t supported_ciphers;
struct scan_freq_set *supported_freqs;
struct band *band_2g;
struct band *band_5g;
2022-02-25 18:58:05 +01:00
struct band *band_6g;
char *model_str;
char *vendor_str;
char *driver_str;
uint32_t driver_flags;
struct watchlist state_watches;
uint8_t extended_capabilities[EXT_CAP_LEN + 2]; /* max bitmap size + IE header */
uint8_t *iftype_extended_capabilities[NUM_NL80211_IFTYPES];
uint8_t rm_enabled_capabilities[7]; /* 5 size max + header */
struct l_genl_family *nl80211;
char regdom_country[2];
wiphy: introduce new radio management APIs These APIs will handle fairness and order in any operations which radios can only do sequentially (offchannel, scanning, connection etc.). Both scan and frame-xchg are complex modules (especially scanning) which is why the radio management APIs were implemented generic enough where the changes to both modules will be minimal. Any module that requires this kind of work can push a work item into the radio management work queue (wiphy_radio_work_insert) and when the work is ready to be started radio management will call back into the module. Once the work is completed (and this may be some time later e.g. in scan results or a frame watch) the module can signal back that the work is finished (wiphy_radio_work_done). Wiphy will then pop the queue and continue with the next work item. A concept of priority was added in order to allow important offchannel operations (e.g. ANQP) to take priority over other work items. The priority is an integer, where lower values are of a higher priority. The concept of priority cleanly solves a lot of the complexity that was added in order to support ANQP queries (suspending scanning and waiting for ANQP to finish before connecting). Instead ANQP queries can be queued at a higher priority than scanning which removes the need for suspending scans. In addition we can treat connections as radio management work and insert them at a lower priority than ANQP, but higher than scanning. This forces the connection to wait for ANQP without having to track any state.
2020-07-09 02:04:32 +02:00
/* Work queue for this radio */
struct l_queue *work;
bool work_in_callback;
unsigned int get_reg_id;
unsigned int dump_id;
2018-11-29 18:22:07 +01:00
bool support_scheduled_scan:1;
bool support_rekey_offload:1;
bool support_qos_set_map:1;
bool support_cmds_auth_assoc:1;
bool support_fw_roam:1;
bool soft_rfkill : 1;
bool hard_rfkill : 1;
bool offchannel_tx_ok : 1;
bool blacklisted : 1;
bool registered : 1;
bool self_managed : 1;
bool ap_probe_resp_offload : 1;
2022-12-20 22:43:15 +01:00
bool supports_uapsd : 1;
bool supports_cmd_offchannel : 1;
};
static struct l_queue *wiphy_list = NULL;
2016-05-16 19:21:26 +02:00
enum ie_rsn_cipher_suite wiphy_select_cipher(struct wiphy *wiphy, uint16_t mask)
{
if (mask == IE_RSN_CIPHER_SUITE_NO_GROUP_TRAFFIC)
return IE_RSN_CIPHER_SUITE_NO_GROUP_TRAFFIC;
mask &= wiphy->supported_ciphers;
if (mask & IE_RSN_CIPHER_SUITE_GCMP_256)
return IE_RSN_CIPHER_SUITE_GCMP_256;
if (mask & IE_RSN_CIPHER_SUITE_CCMP_256)
return IE_RSN_CIPHER_SUITE_CCMP_256;
2022-10-20 21:01:39 +02:00
if (mask & IE_RSN_CIPHER_SUITE_GCMP)
return IE_RSN_CIPHER_SUITE_GCMP;
if (mask & IE_RSN_CIPHER_SUITE_CCMP)
return IE_RSN_CIPHER_SUITE_CCMP;
if (mask & IE_RSN_CIPHER_SUITE_TKIP)
return IE_RSN_CIPHER_SUITE_TKIP;
if (mask & IE_RSN_CIPHER_SUITE_BIP_GMAC_256)
return IE_RSN_CIPHER_SUITE_BIP_GMAC_256;
if (mask & IE_RSN_CIPHER_SUITE_BIP_CMAC_256)
return IE_RSN_CIPHER_SUITE_BIP_CMAC_256;
if (mask & IE_RSN_CIPHER_SUITE_BIP_GMAC)
return IE_RSN_CIPHER_SUITE_BIP_GMAC;
2022-10-19 23:26:46 +02:00
if (mask & IE_RSN_CIPHER_SUITE_BIP_CMAC)
return IE_RSN_CIPHER_SUITE_BIP_CMAC;
return 0;
}
2018-09-19 00:06:07 +02:00
uint16_t wiphy_get_supported_ciphers(struct wiphy *wiphy, uint16_t mask)
{
return wiphy->supported_ciphers & mask;
}
static bool wiphy_can_connect_sae(struct wiphy *wiphy)
{
/*
* WPA3 Specification version 3, Section 2.2:
* A STA shall not enable WEP and TKIP
*/
if (!(wiphy->supported_ciphers & IE_RSN_CIPHER_SUITE_CCMP)) {
l_debug("HW not CCMP capable, can't use SAE");
return false;
}
/*
* WPA3 Specification version 3, Section 2.3:
* A STA shall negotiate PMF when associating to an AP using SAE
*/
2022-10-19 23:26:46 +02:00
if (!(wiphy->supported_ciphers & IE_RSN_CIPHER_SUITE_BIP_CMAC)) {
l_debug("HW not MFP capable, can't use SAE");
return false;
}
/*
* SAE support in the kernel is a complete mess in that there are 3
* different ways the hardware can support SAE:
*
* 1. Cards which allow SAE in userspace, meaning they support both
* CMD_AUTHENTICATE and CMD_ASSOCIATE as well as advertise support
* for FEATURE_SAE (SoftMAC).
*
* 2. Cards which allow SAE to be offloaded to hardware. These cards
* do not support AUTH/ASSOC commands, do not advertise FEATURE_SAE,
* but advertise support for EXT_FEATURE_SAE_OFFLOAD. With these
* cards the entire SAE protocol as well as the subsequent 4-way
* handshake are all done in the driver/firmware (fullMAC).
*
* 3. Cards which allow SAE in userspace via CMD_EXTERNAL_AUTH.
* These cards do not support AUTH/ASSOC commands but do implement
* CMD_EXTERNAL_AUTH which is supposed to allow userspace to
* generate Authenticate frames as it would for case (1).
*/
if (wiphy_has_feature(wiphy, NL80211_FEATURE_SAE)) {
/* Case (1) */
if (wiphy->support_cmds_auth_assoc)
return true;
/* Case 3 */
iwd_notice(IWD_NOTICE_CONNECT_INFO,
"FullMAC driver: %s using SAE. Expect EXTERNAL_AUTH",
wiphy->driver_str);
return true;
}
/* Case (2) */
if (wiphy_has_ext_feature(wiphy,
NL80211_EXT_FEATURE_SAE_OFFLOAD))
return true;
return false;
}
enum ie_rsn_akm_suite wiphy_select_akm(struct wiphy *wiphy,
const struct scan_bss *bss,
enum security security,
const struct ie_rsn_info *info,
bool fils_capable_hint)
{
bool psk_offload = wiphy_has_ext_feature(wiphy,
NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK);
/*
* If FT is available, use FT authentication to keep the door open
* for fast transitions. Otherwise use SHA256 version if present.
*/
if (security == SECURITY_8021X) {
if (wiphy_has_ext_feature(wiphy, NL80211_EXT_FEATURE_FILS_STA) &&
wiphy->support_cmds_auth_assoc &&
fils_capable_hint) {
if ((info->akm_suites &
IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA384) &&
bss->rsne && bss->mde_present)
return IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA384;
if ((info->akm_suites &
IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA256) &&
bss->rsne && bss->mde_present)
return IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA256;
if (info->akm_suites & IE_RSN_AKM_SUITE_FILS_SHA384)
return IE_RSN_AKM_SUITE_FILS_SHA384;
if (info->akm_suites & IE_RSN_AKM_SUITE_FILS_SHA256)
return IE_RSN_AKM_SUITE_FILS_SHA256;
}
if ((info->akm_suites & IE_RSN_AKM_SUITE_FT_OVER_8021X) &&
bss->rsne && bss->mde_present &&
wiphy->support_cmds_auth_assoc)
return IE_RSN_AKM_SUITE_FT_OVER_8021X;
if (info->akm_suites & IE_RSN_AKM_SUITE_8021X_SHA256)
return IE_RSN_AKM_SUITE_8021X_SHA256;
if (info->akm_suites & IE_RSN_AKM_SUITE_8021X)
return IE_RSN_AKM_SUITE_8021X;
} else if (security == SECURITY_PSK) {
/*
* Prefer connecting to SAE/WPA3 network, but only if SAE is
* supported, we are MFP capable, and the AP has set the
* MFPR/MFPC bits correctly. If any of these conditions are not
* met, we can fallback to WPA2 (if the AKM is present).
*/
if (ie_rsne_is_wpa3_personal(info)) {
l_debug("Network is WPA3-Personal...");
if (!wiphy_can_connect_sae(wiphy)) {
l_debug("Can't use SAE, trying WPA2");
goto wpa2_personal;
}
if (info->akm_suites &
IE_RSN_AKM_SUITE_FT_OVER_SAE_SHA256)
return IE_RSN_AKM_SUITE_FT_OVER_SAE_SHA256;
if (info->akm_suites & IE_RSN_AKM_SUITE_SAE_SHA256)
return IE_RSN_AKM_SUITE_SAE_SHA256;
}
wpa2_personal:
/*
* Allow FT if either Auth/Assoc is supported OR if the card
* supports PSK offload. Without Auth/Assoc, PSK offload is the
* only mechanism to allow FT on these cards.
*/
if ((info->akm_suites & IE_RSN_AKM_SUITE_FT_USING_PSK) &&
bss->rsne && bss->mde_present) {
if (wiphy->support_cmds_auth_assoc ||
(psk_offload && wiphy->support_fw_roam))
return IE_RSN_AKM_SUITE_FT_USING_PSK;
}
if (info->akm_suites & IE_RSN_AKM_SUITE_PSK_SHA256)
return IE_RSN_AKM_SUITE_PSK_SHA256;
if (info->akm_suites & IE_RSN_AKM_SUITE_PSK)
return IE_RSN_AKM_SUITE_PSK;
} else if (security == SECURITY_NONE) {
if (info->akm_suites & IE_RSN_AKM_SUITE_OWE &&
!wiphy_owe_disabled(wiphy))
return IE_RSN_AKM_SUITE_OWE;
}
return 0;
}
2018-11-29 18:22:07 +01:00
static struct wiphy *wiphy_new(uint32_t id)
{
struct wiphy *wiphy = l_new(struct wiphy, 1);
wiphy->id = id;
wiphy->supported_freqs = scan_freq_set_new();
watchlist_init(&wiphy->state_watches, NULL);
wiphy->extended_capabilities[0] = IE_TYPE_EXTENDED_CAPABILITIES;
wiphy->extended_capabilities[1] = EXT_CAP_LEN;
2018-11-29 18:22:07 +01:00
return wiphy;
}
wiphy: introduce new radio management APIs These APIs will handle fairness and order in any operations which radios can only do sequentially (offchannel, scanning, connection etc.). Both scan and frame-xchg are complex modules (especially scanning) which is why the radio management APIs were implemented generic enough where the changes to both modules will be minimal. Any module that requires this kind of work can push a work item into the radio management work queue (wiphy_radio_work_insert) and when the work is ready to be started radio management will call back into the module. Once the work is completed (and this may be some time later e.g. in scan results or a frame watch) the module can signal back that the work is finished (wiphy_radio_work_done). Wiphy will then pop the queue and continue with the next work item. A concept of priority was added in order to allow important offchannel operations (e.g. ANQP) to take priority over other work items. The priority is an integer, where lower values are of a higher priority. The concept of priority cleanly solves a lot of the complexity that was added in order to support ANQP queries (suspending scanning and waiting for ANQP to finish before connecting). Instead ANQP queries can be queued at a higher priority than scanning which removes the need for suspending scans. In addition we can treat connections as radio management work and insert them at a lower priority than ANQP, but higher than scanning. This forces the connection to wait for ANQP without having to track any state.
2020-07-09 02:04:32 +02:00
static void destroy_work(void *user_data)
{
struct wiphy_radio_work_item *work = user_data;
if (work->ops && work->ops->destroy)
work->ops->destroy(work);
}
static void wiphy_free(void *data)
{
struct wiphy *wiphy = data;
uint32_t i;
2016-06-25 06:31:02 +02:00
l_debug("Freeing wiphy %s[%u]", wiphy->name, wiphy->id);
if (wiphy->dump_id)
l_genl_family_cancel(nl80211, wiphy->dump_id);
if (wiphy->get_reg_id)
l_genl_family_cancel(nl80211, wiphy->get_reg_id);
for (i = 0; i < NUM_NL80211_IFTYPES; i++)
l_free(wiphy->iftype_extended_capabilities[i]);
if (wiphy->band_2g) {
band_free(wiphy->band_2g);
wiphy->band_2g = NULL;
}
if (wiphy->band_5g) {
band_free(wiphy->band_5g);
wiphy->band_5g = NULL;
}
2022-02-25 18:58:05 +01:00
if (wiphy->band_6g) {
band_free(wiphy->band_6g);
wiphy->band_6g = NULL;
}
scan_freq_set_free(wiphy->supported_freqs);
watchlist_destroy(&wiphy->state_watches);
l_free(wiphy->model_str);
l_free(wiphy->vendor_str);
l_free(wiphy->driver_str);
l_genl_family_free(wiphy->nl80211);
wiphy: introduce new radio management APIs These APIs will handle fairness and order in any operations which radios can only do sequentially (offchannel, scanning, connection etc.). Both scan and frame-xchg are complex modules (especially scanning) which is why the radio management APIs were implemented generic enough where the changes to both modules will be minimal. Any module that requires this kind of work can push a work item into the radio management work queue (wiphy_radio_work_insert) and when the work is ready to be started radio management will call back into the module. Once the work is completed (and this may be some time later e.g. in scan results or a frame watch) the module can signal back that the work is finished (wiphy_radio_work_done). Wiphy will then pop the queue and continue with the next work item. A concept of priority was added in order to allow important offchannel operations (e.g. ANQP) to take priority over other work items. The priority is an integer, where lower values are of a higher priority. The concept of priority cleanly solves a lot of the complexity that was added in order to support ANQP queries (suspending scanning and waiting for ANQP to finish before connecting). Instead ANQP queries can be queued at a higher priority than scanning which removes the need for suspending scans. In addition we can treat connections as radio management work and insert them at a lower priority than ANQP, but higher than scanning. This forces the connection to wait for ANQP without having to track any state.
2020-07-09 02:04:32 +02:00
l_queue_destroy(wiphy->work, destroy_work);
l_free(wiphy);
}
static bool wiphy_match(const void *a, const void *b)
{
const struct wiphy *wiphy = a;
uint32_t id = L_PTR_TO_UINT(b);
return (wiphy->id == id);
}
2016-06-01 22:20:33 +02:00
struct wiphy *wiphy_find(int wiphy_id)
{
return l_queue_find(wiphy_list, wiphy_match, L_UINT_TO_PTR(wiphy_id));
}
bool wiphy_is_blacklisted(const struct wiphy *wiphy)
{
return wiphy->blacklisted;
}
2017-03-16 22:45:10 +01:00
static bool wiphy_is_managed(const char *phy)
{
char *pattern;
unsigned int i;
if (!whitelist_filter)
goto check_blacklist;
for (i = 0; (pattern = whitelist_filter[i]); i++) {
if (fnmatch(pattern, phy, 0) != 0)
continue;
goto check_blacklist;
}
l_debug("whitelist filtered phy: %s", phy);
return false;
check_blacklist:
if (!blacklist_filter)
return true;
for (i = 0; (pattern = blacklist_filter[i]); i++) {
if (fnmatch(pattern, phy, 0) == 0) {
l_debug("blacklist filtered ifname: %s", phy);
return false;
}
}
return true;
}
const char *wiphy_get_path(struct wiphy *wiphy)
{
static char path[256];
L_WARN_ON(snprintf(path, sizeof(path), "%s/%d", IWD_BASE_PATH,
wiphy->id) >= (int) sizeof(path));
path[sizeof(path) - 1] = '\0';
return path;
}
2020-02-04 00:37:08 +01:00
uint32_t wiphy_get_id(struct wiphy *wiphy)
{
return wiphy->id;
}
2016-09-13 21:36:46 +02:00
uint32_t wiphy_get_supported_bands(struct wiphy *wiphy)
{
uint32_t bands = 0;
if (wiphy->band_2g)
bands |= BAND_FREQ_2_4_GHZ;
2016-09-13 21:36:46 +02:00
if (wiphy->band_5g)
bands |= BAND_FREQ_5_GHZ;
2022-02-25 18:58:05 +01:00
if (wiphy->band_6g)
bands |= BAND_FREQ_6_GHZ;
return bands;
2016-09-13 21:36:46 +02:00
}
const struct scan_freq_set *wiphy_get_supported_freqs(
const struct wiphy *wiphy)
{
return wiphy->supported_freqs;
}
static struct band *wiphy_get_band(const struct wiphy *wiphy, enum band_freq band)
{
switch (band) {
case BAND_FREQ_2_4_GHZ:
return wiphy->band_2g;
case BAND_FREQ_5_GHZ:
return wiphy->band_5g;
case BAND_FREQ_6_GHZ:
return wiphy->band_6g;
default:
return NULL;
}
}
const struct band_freq_attrs *wiphy_get_frequency_info(
const struct wiphy *wiphy,
uint32_t freq)
{
struct band_freq_attrs *attr;
enum band_freq band;
uint8_t channel;
struct band *bandp;
channel = band_freq_to_channel(freq, &band);
if (!channel)
return NULL;
bandp = wiphy_get_band(wiphy, band);
if (!bandp)
return NULL;
attr = &bandp->freq_attrs[channel];
if (!attr->supported)
return NULL;
return attr;
}
const struct band_freq_attrs *wiphy_get_frequency_info_list(
const struct wiphy *wiphy,
enum band_freq band,
size_t *size)
{
struct band *bandp;
bandp = wiphy_get_band(wiphy, band);
if (!bandp)
return NULL;
if (size)
*size = bandp->freqs_len;
return bandp->freq_attrs;
}
int wiphy_band_is_disabled(const struct wiphy *wiphy, enum band_freq band)
{
struct band_freq_attrs attr;
unsigned int i;
struct band *bandp;
bandp = wiphy_get_band(wiphy, band);
if (!bandp)
return -ENOTSUP;
for (i = 0; i < bandp->freqs_len; i++) {
attr = bandp->freq_attrs[i];
if (!attr.supported)
continue;
if (!attr.disabled)
return 0;
}
return 1;
}
bool wiphy_supports_probe_resp_offload(struct wiphy *wiphy)
{
return wiphy->ap_probe_resp_offload;
}
bool wiphy_can_transition_disable(struct wiphy *wiphy)
{
/*
* WPA3 Specification version 3, Section 2.2:
* A STA shall not enable WEP and TKIP
*/
if (!(wiphy->supported_ciphers & IE_RSN_CIPHER_SUITE_CCMP))
return false;
2022-10-19 23:26:46 +02:00
if (!(wiphy->supported_ciphers & IE_RSN_CIPHER_SUITE_BIP_CMAC))
return false;
return true;
}
/* Catch all for the offload features */
bool wiphy_can_offload(struct wiphy *wiphy)
{
return wiphy_has_ext_feature(wiphy,
NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK) ||
wiphy_has_ext_feature(wiphy,
NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X) ||
wiphy_has_ext_feature(wiphy, NL80211_EXT_FEATURE_SAE_OFFLOAD);
}
2021-10-04 18:48:52 +02:00
bool wiphy_supports_ext_key_id(struct wiphy *wiphy)
{
return wiphy_has_ext_feature(wiphy, NL80211_EXT_FEATURE_EXT_KEY_ID);
}
bool wiphy_supports_cmds_auth_assoc(struct wiphy *wiphy)
{
return wiphy->support_cmds_auth_assoc;
}
bool wiphy_has_feature(struct wiphy *wiphy, uint32_t feature)
{
return wiphy->feature_flags & feature;
}
bool wiphy_can_randomize_mac_addr(struct wiphy *wiphy)
{
return wiphy_has_feature(wiphy, NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR);
}
2019-07-10 23:23:13 +02:00
bool wiphy_rrm_capable(struct wiphy *wiphy)
{
if (wiphy_has_feature(wiphy,
NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES) &&
wiphy_has_feature(wiphy, NL80211_FEATURE_QUIET))
return true;
if (wiphy_has_ext_feature(wiphy, NL80211_EXT_FEATURE_RRM))
return true;
return false;
}
bool wiphy_has_ext_feature(struct wiphy *wiphy, uint32_t feature)
{
return feature < sizeof(wiphy->ext_features) * 8 &&
2021-03-12 20:49:23 +01:00
test_bit(wiphy->ext_features, feature);
}
uint8_t wiphy_get_max_num_ssids_per_scan(struct wiphy *wiphy)
{
return wiphy->max_num_ssids_per_scan;
}
uint16_t wiphy_get_max_scan_ie_len(struct wiphy *wiphy)
{
return wiphy->max_scan_ie_len;
}
uint32_t wiphy_get_max_roc_duration(struct wiphy *wiphy)
{
return wiphy->max_roc_duration;
}
bool wiphy_supports_qos_set_map(struct wiphy *wiphy)
{
return wiphy->support_qos_set_map;
}
bool wiphy_supports_firmware_roam(struct wiphy *wiphy)
{
return wiphy->support_fw_roam;
}
const char *wiphy_get_driver(struct wiphy *wiphy)
{
return wiphy->driver_str;
}
2019-07-02 01:28:07 +02:00
const char *wiphy_get_name(struct wiphy *wiphy)
{
return wiphy->name;
}
bool wiphy_uses_default_if(struct wiphy *wiphy)
{
if (!wiphy_get_driver(wiphy))
return true;
if (wiphy->driver_flags & DEFAULT_IF)
return true;
return false;
}
bool wiphy_control_port_enabled(struct wiphy *wiphy)
{
const struct l_settings *settings = iwd_get_config();
bool enabled;
if (wiphy->driver_flags & FORCE_PAE)
return false;
if (!wiphy_has_ext_feature(wiphy,
NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211))
return false;
if (!l_settings_get_bool(settings, "General",
"ControlPortOverNL80211", &enabled))
enabled = true;
return enabled;
}
bool wiphy_power_save_disabled(struct wiphy *wiphy)
{
if (wiphy->driver_flags & POWER_SAVE_DISABLE)
return true;
return false;
}
bool wiphy_owe_disabled(struct wiphy *wiphy)
{
if (wiphy->driver_flags & OWE_DISABLE)
return true;
return false;
}
const uint8_t *wiphy_get_extended_capabilities(struct wiphy *wiphy,
uint32_t iftype)
{
if (wiphy->iftype_extended_capabilities[iftype])
return wiphy->iftype_extended_capabilities[iftype];
return wiphy->extended_capabilities;
}
const uint8_t *wiphy_get_rm_enabled_capabilities(struct wiphy *wiphy)
{
if (!wiphy_rrm_capable(wiphy))
return NULL;
return wiphy->rm_enabled_capabilities;
}
bool wiphy_get_rsnxe(const struct wiphy *wiphy, uint8_t *buf, size_t len)
{
if (len < 3)
return false;
buf[0] = IE_TYPE_RSNX;
buf[1] = 1;
/*
* Lower 4 bits of the first octet:
* The length of the Extended RSN Capabilities field, in octets,
* minus 1, i.e., n - 1.
*/
buf[2] = 0;
/* No other bits set for now */
return true;
}
static void wiphy_address_constrain(struct wiphy *wiphy, uint8_t addr[static 6])
{
switch (mac_randomize_bytes) {
case 6:
/* Set the locally administered bit */
addr[0] |= 0x2;
/* Reset multicast bit */
addr[0] &= 0xfe;
break;
case 3:
memcpy(addr, wiphy->permanent_addr, 3);
break;
}
/*
* Constrain the last NIC byte to 0x00 .. 0xfe, otherwise we might be
* able to generate an address of 0xff 0xff 0xff which might be
* interpreted as a vendor broadcast. Similarly, 0x00 0x00 0x00 is
* also not valid
*/
addr[5] &= 0xfe;
if (l_memeqzero(addr + 3, 3))
addr[5] = 0x01;
}
void wiphy_generate_random_address(struct wiphy *wiphy, uint8_t addr[static 6])
{
switch (mac_randomize_bytes) {
case 6:
l_getrandom(addr, 6);
break;
case 3:
l_getrandom(addr + 3, 3);
break;
}
wiphy_address_constrain(wiphy, addr);
}
void wiphy_generate_address_from_ssid(struct wiphy *wiphy,
const uint8_t *ssid, size_t ssid_len,
uint8_t addr[static 6])
{
struct l_checksum *sha = l_checksum_new(L_CHECKSUM_SHA256);
l_checksum_update(sha, ssid, ssid_len);
l_checksum_update(sha, wiphy->permanent_addr,
sizeof(wiphy->permanent_addr));
l_checksum_get_digest(sha, addr, mac_randomize_bytes);
l_checksum_free(sha);
wiphy_address_constrain(wiphy, addr);
}
2019-04-11 21:14:27 +02:00
bool wiphy_constrain_freq_set(const struct wiphy *wiphy,
struct scan_freq_set *set)
{
struct band *bands[3] = { wiphy->band_2g,
wiphy->band_5g, wiphy->band_6g };
unsigned int b;
unsigned int i;
2019-04-11 21:14:27 +02:00
scan_freq_set_constrain(set, wiphy->supported_freqs);
for (b = 0; b < L_ARRAY_SIZE(bands); b++) {
struct band *band = bands[b];
if (!band)
continue;
for (i = 1; i <= band->freqs_len; i++) {
uint32_t freq;
if (!band->freq_attrs[i].supported)
continue;
if (!band->freq_attrs[i].disabled)
continue;
freq = band_channel_to_freq(i, band->freq);
if (!freq)
continue;
scan_freq_set_remove(set, freq);
}
}
2019-04-11 21:14:27 +02:00
if (!scan_freq_set_get_bands(set))
/* The set is empty. */
return false;
return true;
}
static char **wiphy_iftype_mask_to_str(uint16_t mask)
{
char **ret = l_new(char *, __builtin_popcount(mask) + 1);
unsigned int i;
unsigned int j;
for (j = 0, i = 0; i < sizeof(mask) * 8; i++) {
const char *str;
if (!(mask & (1 << i)))
continue;
str = netdev_iftype_to_string(i + 1);
if (str)
ret[j++] = l_strdup(str);
}
return ret;
}
static char **wiphy_get_supported_iftypes(struct wiphy *wiphy, uint16_t mask)
{
return wiphy_iftype_mask_to_str(wiphy->supported_iftypes & mask);
}
bool wiphy_supports_iftype(struct wiphy *wiphy, uint32_t iftype)
{
if (iftype > sizeof(wiphy->supported_iftypes) * 8)
return false;
return wiphy->supported_iftypes & (1 << (iftype - 1));
}
const uint8_t *wiphy_get_supported_rates(struct wiphy *wiphy,
enum band_freq band,
unsigned int *out_num)
{
struct band *bandp = wiphy_get_band(wiphy, band);
if (!bandp)
return NULL;
if (out_num)
*out_num = bandp->supported_rates_len;
return bandp->supported_rates;
}
void wiphy_get_reg_domain_country(struct wiphy *wiphy, char *out)
{
char *country = wiphy->regdom_country;
if (!country[0])
/* Wiphy uses the global regulatory domain */
country = regdom_country;
out[0] = country[0];
out[1] = country[1];
}
bool wiphy_country_is_unknown(struct wiphy *wiphy)
{
char cc[2];
wiphy_get_reg_domain_country(wiphy, cc);
/*
* Treat OO and XX as an unknown country. Additional codes could be
* added here if needed. The purpose of this is to know if we can
* expect the disabled frequency list to be updated once a country is
* known.
*/
return ((cc[0] == 'O' && cc[1] == 'O') ||
(cc[0] == 'X' && cc[1] == 'X'));
}
2022-12-20 22:43:15 +01:00
bool wiphy_supports_uapsd(const struct wiphy *wiphy)
{
return wiphy->supports_uapsd;
}
bool wiphy_supports_cmd_offchannel(const struct wiphy *wiphy)
{
return wiphy->supports_cmd_offchannel;
}
const uint8_t *wiphy_get_ht_capabilities(const struct wiphy *wiphy,
enum band_freq band,
size_t *size)
{
static uint8_t ht_capa[26];
const struct band *bandp = wiphy_get_band(wiphy, band);
if (!bandp)
return NULL;
if (!bandp->ht_supported)
return NULL;
memset(ht_capa, 0, sizeof(ht_capa));
/*
* The kernel segments the HT capabilities element into multiple
* attributes. For convenience on the caller just combine them and
* return the full IE rather than adding 3 separate getters. This also
* provides a way to check if HT is supported.
*/
memcpy(ht_capa, bandp->ht_capabilities, 2);
ht_capa[2] = bandp->ht_ampdu_params;
memcpy(ht_capa + 3, bandp->ht_mcs_set, 16);
/*
* TODO: HT Extended capabilities, beamforming, and ASEL capabilities
* are not available to get from the kernel, leave as zero.
*/
if (size)
*size = sizeof(ht_capa);
return ht_capa;
}
int wiphy_estimate_data_rate(struct wiphy *wiphy,
const void *ies, uint16_t ies_len,
const struct scan_bss *bss,
uint64_t *out_data_rate)
{
struct ie_tlv_iter iter;
const void *supported_rates = NULL;
const void *ext_supported_rates = NULL;
const void *vht_capabilities = NULL;
const void *vht_operation = NULL;
const void *ht_capabilities = NULL;
const void *ht_operation = NULL;
const void *he_capabilities = NULL;
const struct band *bandp;
enum band_freq band;
int ret;
if (band_freq_to_channel(bss->frequency, &band) == 0)
return -ENOTSUP;
bandp = wiphy_get_band(wiphy, band);
if (!bandp)
return -ENOTSUP;
ie_tlv_iter_init(&iter, ies, ies_len);
while (ie_tlv_iter_next(&iter)) {
uint16_t tag = ie_tlv_iter_get_tag(&iter);
switch (tag) {
case IE_TYPE_SUPPORTED_RATES:
if (iter.len > 8)
continue;
supported_rates = iter.data - 2;
break;
case IE_TYPE_EXTENDED_SUPPORTED_RATES:
ext_supported_rates = iter.data - 2;
break;
case IE_TYPE_HT_CAPABILITIES:
if (iter.len != 26)
continue;
ht_capabilities = iter.data - 2;
break;
case IE_TYPE_HT_OPERATION:
if (iter.len != 22)
continue;
ht_operation = iter.data - 2;
break;
case IE_TYPE_VHT_CAPABILITIES:
if (iter.len != 12)
continue;
vht_capabilities = iter.data - 2;
break;
case IE_TYPE_VHT_OPERATION:
if (iter.len != 5)
continue;
vht_operation = iter.data - 2;
break;
case IE_TYPE_HE_CAPABILITIES:
if (!ie_validate_he_capabilities(iter.data, iter.len)) {
l_warn("invalid HE capabilities for "MAC,
MAC_STR(bss->addr));
continue;
}
he_capabilities = iter.data;
break;
default:
break;
}
}
ret = band_estimate_he_rx_rate(bandp, he_capabilities,
bss->signal_strength / 100,
out_data_rate);
if (!ret)
return 0;
else if (ret != -ENOTSUP && ret != -ENETUNREACH)
l_warn("error parsing HE capabilities");
ret = band_estimate_vht_rx_rate(bandp, vht_capabilities, vht_operation,
ht_capabilities, ht_operation,
bss->signal_strength / 100,
out_data_rate);
if (!ret)
return 0;
else if (ret != -ENOTSUP && ret != -ENETUNREACH)
l_warn("error parsing VHT capabilities");
ret = band_estimate_ht_rx_rate(bandp, ht_capabilities, ht_operation,
bss->signal_strength / 100,
out_data_rate);
if (!ret)
return 0;
else if (ret != -ENOTSUP && ret != -ENETUNREACH)
l_warn("error parsing HT capabilities");
ret = band_estimate_nonht_rate(bandp, supported_rates,
ext_supported_rates,
bss->signal_strength / 100,
out_data_rate);
if (ret != 0 && ret != -ENOTSUP && ret != -ENETUNREACH)
l_warn("error parsing non-HT rates");
return ret;
}
bool wiphy_regdom_is_updating(struct wiphy *wiphy)
{
return wiphy->dump_id || (!wiphy->self_managed && wiphy_dump_id);
}
uint32_t wiphy_state_watch_add(struct wiphy *wiphy,
wiphy_state_watch_func_t func,
void *user_data, wiphy_destroy_func_t destroy)
{
return watchlist_add(&wiphy->state_watches, func, user_data, destroy);
}
bool wiphy_state_watch_remove(struct wiphy *wiphy, uint32_t id)
{
return watchlist_remove(&wiphy->state_watches, id);
}
2021-05-29 05:14:01 +02:00
static void wiphy_print_mcs_indexes(const uint8_t *mcs)
{
2021-05-29 05:14:01 +02:00
int i;
2021-05-29 05:14:01 +02:00
for (i = 0; i < 77; i++) {
int start;
2021-05-29 05:14:01 +02:00
if (!test_bit(mcs, i))
continue;
2021-05-29 05:14:01 +02:00
start = i;
2021-05-29 05:14:01 +02:00
while (i < 76 && test_bit(mcs, i + 1))
i += 1;
2021-05-29 05:14:01 +02:00
if (start != i)
l_info("\t\t\t%d-%d", start, i);
else
l_info("\t\t\t%d", start);
}
}
static void wiphy_print_mcs_info(const uint8_t *mcs_map,
const char *prefix,
uint8_t value0,
uint8_t value1,
uint8_t value2)
{
int i;
for (i = 14; i >= 0; i -= 2) {
uint8_t value;
int mcs = bit_field(mcs_map[i / 8], i % 8, 2);
if (mcs == 0x3)
continue;
switch (mcs) {
case 0:
value = value0;
break;
case 1:
value = value1;
break;
case 2:
value = value2;
break;
}
l_info("\t\t\tMax %s MCS: 0-%d for NSS: %d", prefix,
value, i / 2 + 1);
return;
}
}
static void wiphy_print_he_capabilities(struct band *band,
const struct band_he_capabilities *he_cap)
{
_auto_(l_strv_free) char **iftypes = NULL;
_auto_(l_free) char *joined = NULL;
uint8_t width_set = bit_field(he_cap->he_phy_capa[0], 1, 7);
iftypes = wiphy_iftype_mask_to_str(he_cap->iftypes);
joined = l_strjoinv(iftypes, ' ');
l_info("\t\t\tInterface Types: %s", joined);
switch (band->freq) {
case BAND_FREQ_2_4_GHZ:
wiphy_print_mcs_info(he_cap->he_mcs_set,
"HE RX <= 80MHz", 7, 9, 11);
wiphy_print_mcs_info(he_cap->he_mcs_set + 2,
"HE TX <= 80MHz", 7, 9, 11);
break;
case BAND_FREQ_5_GHZ:
case BAND_FREQ_6_GHZ:
wiphy_print_mcs_info(he_cap->he_mcs_set,
"HE RX <= 80MHz", 7, 9, 11);
wiphy_print_mcs_info(he_cap->he_mcs_set + 2,
"HE TX <= 80MHz", 7, 9, 11);
if (test_bit(&width_set, 2)) {
wiphy_print_mcs_info(he_cap->he_mcs_set + 4,
"HE RX <= 160MHz", 7, 9, 11);
wiphy_print_mcs_info(he_cap->he_mcs_set + 6,
"HE TX <= 160MHz", 7, 9, 11);
}
if (test_bit(&width_set, 3)) {
wiphy_print_mcs_info(he_cap->he_mcs_set + 8,
"HE RX <= 80+80MHz", 7, 9, 11);
wiphy_print_mcs_info(he_cap->he_mcs_set + 10,
"HE TX <= 80+80MHz", 7, 9, 11);
}
break;
}
}
2021-05-29 05:14:01 +02:00
static void wiphy_print_band_info(struct band *band, const char *name)
{
int i;
2021-05-29 05:14:01 +02:00
l_info("\t%s:", name);
l_info("\t\tBitrates (non-HT):");
for (i = 0; i < band->supported_rates_len; i++)
l_info("\t\t\t%2d.%d Mbps", band->supported_rates[i] / 2,
band->supported_rates[i] % 2 * 5);
if (band->ht_supported) {
uint8_t max_nss = bit_field(band->ht_mcs_set[12], 2, 2) + 1;
2021-05-29 05:14:01 +02:00
l_info("\t\tHT Capabilities:");
if (test_bit(band->ht_capabilities, 1))
l_info("\t\t\tHT40");
else
l_info("\t\t\tHT20");
if (test_bit(band->ht_capabilities, 5))
l_info("\t\t\tShort GI for 20Mhz");
if (test_bit(band->ht_capabilities, 6))
l_info("\t\t\tShort GI for 40Mhz");
l_info("\t\tHT RX MCS indexes:");
wiphy_print_mcs_indexes(band->ht_mcs_set);
if (test_bit(band->ht_mcs_set, 96)) {
if (test_bit(band->ht_mcs_set, 97))
l_info("\t\tHT TX MCS differ, max NSS: %d",
max_nss);
} else
l_info("\t\tHT TX MCS set undefined");
}
if (band->vht_supported) {
l_info("\t\tVHT Capabilities:");
switch (bit_field(band->vht_capabilities[0], 2, 2)) {
case 1:
l_info("\t\t\t160 Mhz operation");
break;
case 2:
l_info("\t\t\t160 Mhz, 80+80 Mhz operation");
break;
}
if (test_bit(band->vht_capabilities, 5))
l_info("\t\t\tShort GI for 80Mhz");
if (test_bit(band->vht_capabilities, 6))
l_info("\t\t\tShort GI for 160 and 80 + 80 Mhz");
wiphy_print_mcs_info(band->vht_mcs_set, "RX", 7, 8, 9);
wiphy_print_mcs_info(band->vht_mcs_set + 4, "TX", 7, 8, 9);
}
if (band->he_capabilities) {
const struct l_queue_entry *entry;
l_info("\t\tHE Capabilities");
for (entry = l_queue_get_entries(band->he_capabilities);
entry; entry = entry->next) {
const struct band_he_capabilities *he_cap = entry->data;
wiphy_print_he_capabilities(band, he_cap);
}
}
2021-05-29 05:14:01 +02:00
}
static void wiphy_print_basic_info(struct wiphy *wiphy)
{
char buf[2048];
2021-05-29 05:14:01 +02:00
l_info("Wiphy: %d, Name: %s", wiphy->id, wiphy->name);
l_info("\tPermanent Address: "MAC, MAC_STR(wiphy->permanent_addr));
if (wiphy->band_2g)
wiphy_print_band_info(wiphy->band_2g, "2.4GHz Band");
2021-05-29 05:14:01 +02:00
if (wiphy->band_5g)
wiphy_print_band_info(wiphy->band_5g, "5GHz Band");
2022-02-25 18:58:05 +01:00
if (wiphy->band_6g)
wiphy_print_band_info(wiphy->band_6g, "6GHz Band");
if (wiphy->supported_ciphers) {
int n = 0;
size_t len = 0;
int i = sizeof(wiphy->supported_ciphers) * 8 - 1;
len += snprintf(buf, sizeof(buf), "\tCiphers:");
for (; i >= 0 && len < sizeof(buf); i--) {
typeof(wiphy->supported_ciphers) cipher = 1 << i;
const char *str;
if (cipher == IE_RSN_CIPHER_SUITE_WEP40 ||
cipher == IE_RSN_CIPHER_SUITE_WEP104)
continue;
if (!(wiphy->supported_ciphers & cipher))
continue;
str = ie_rsn_cipher_suite_to_string(cipher);
if (!str)
continue;
len += snprintf(buf + len, sizeof(buf) - len, "%s%s",
!n || (n % 4) ? " " : "\n\t\t ",
str);
n += 1;
}
l_info("%s", buf);
}
if (wiphy->supported_iftypes) {
char **iftypes = wiphy_get_supported_iftypes(wiphy, ~0);
char *joined = l_strjoinv(iftypes, ' ');
l_info("\tSupported iftypes: %s", joined);
l_free(joined);
l_strfreev(iftypes);
}
if (wiphy->driver_flags) {
char **flags = l_strv_new();
char *joined;
if (wiphy->driver_flags & DEFAULT_IF)
flags = l_strv_append(flags, "DefaultInterface");
if (wiphy->driver_flags & FORCE_PAE)
flags = l_strv_append(flags, "ForcePae");
if (wiphy->driver_flags & POWER_SAVE_DISABLE)
flags = l_strv_append(flags, "PowerSaveDisable");
if (wiphy->driver_flags & OWE_DISABLE)
flags = l_strv_append(flags, "OweDisable");
joined = l_strjoinv(flags, ' ');
l_info("\tDriver Flags: %s", joined);
l_free(joined);
l_strfreev(flags);
}
}
static void parse_supported_commands(struct wiphy *wiphy,
struct l_genl_attr *attr)
{
uint16_t type, len;
const void *data;
bool auth = false;
bool assoc = false;
while (l_genl_attr_next(attr, &type, &len, &data)) {
uint32_t cmd = *(uint32_t *)data;
switch (cmd) {
case NL80211_CMD_START_SCHED_SCAN:
wiphy->support_scheduled_scan = true;
break;
case NL80211_CMD_SET_REKEY_OFFLOAD:
wiphy->support_rekey_offload = true;
break;
case NL80211_CMD_SET_QOS_MAP:
wiphy->support_qos_set_map = true;
break;
case NL80211_CMD_AUTHENTICATE:
auth = true;
break;
case NL80211_CMD_ASSOCIATE:
assoc = true;
break;
case NL80211_CMD_REMAIN_ON_CHANNEL:
wiphy->supports_cmd_offchannel = true;
break;
}
}
if (auth && assoc)
wiphy->support_cmds_auth_assoc = true;
}
static void parse_supported_ciphers(struct wiphy *wiphy, const void *data,
uint16_t len)
{
while (len >= 4) {
uint32_t cipher = *(uint32_t *)data;
switch (cipher) {
case CRYPTO_CIPHER_CCMP:
wiphy->supported_ciphers |= IE_RSN_CIPHER_SUITE_CCMP;
break;
case CRYPTO_CIPHER_TKIP:
wiphy->supported_ciphers |= IE_RSN_CIPHER_SUITE_TKIP;
break;
case CRYPTO_CIPHER_WEP40:
wiphy->supported_ciphers |= IE_RSN_CIPHER_SUITE_WEP40;
break;
case CRYPTO_CIPHER_WEP104:
wiphy->supported_ciphers |= IE_RSN_CIPHER_SUITE_WEP104;
break;
case CRYPTO_CIPHER_BIP_CMAC:
2022-10-19 23:26:46 +02:00
wiphy->supported_ciphers |=
IE_RSN_CIPHER_SUITE_BIP_CMAC;
break;
2022-10-20 21:01:39 +02:00
case CRYPTO_CIPHER_GCMP:
wiphy->supported_ciphers |= IE_RSN_CIPHER_SUITE_GCMP;
break;
case CRYPTO_CIPHER_GCMP_256:
wiphy->supported_ciphers |=
IE_RSN_CIPHER_SUITE_GCMP_256;
break;
case CRYPTO_CIPHER_CCMP_256:
wiphy->supported_ciphers |=
IE_RSN_CIPHER_SUITE_CCMP_256;
break;
case CRYPTO_CIPHER_BIP_GMAC:
wiphy->supported_ciphers |=
IE_RSN_CIPHER_SUITE_BIP_GMAC;
break;
case CRYPTO_CIPHER_BIP_GMAC_256:
wiphy->supported_ciphers |=
IE_RSN_CIPHER_SUITE_BIP_GMAC_256;
break;
case CRYPTO_CIPHER_BIP_CMAC_256:
wiphy->supported_ciphers |=
IE_RSN_CIPHER_SUITE_BIP_CMAC_256;
break;
default: /* TODO: Support other ciphers */
break;
}
len -= 4;
data += 4;
}
}
static int parse_supported_rates(struct l_genl_attr *attr, struct band *band)
{
uint16_t type;
uint16_t len;
const void *data;
struct l_genl_attr nested;
int count = 0;
if (!l_genl_attr_recurse(attr, &nested))
return -EBADMSG;
while (l_genl_attr_next(&nested, NULL, NULL, NULL)) {
struct l_genl_attr nested2;
if (!l_genl_attr_recurse(&nested, &nested2))
return -EBADMSG;
while (l_genl_attr_next(&nested2, &type, &len, &data)) {
uint32_t rate;
if (type != NL80211_BITRATE_ATTR_RATE || len != 4)
continue;
rate = l_get_u32(data);
if (rate % 5)
continue;
/*
* Convert from the 100kb/s units reported by the
* kernel to the 500kb/s used in 802.11 IEs.
*/
rate /= 5;
/*
* Rates past 120 seem to be used for other purposes,
* BSS Membership Selector (HT/VHT), etc
*/
if (rate > 120)
continue;
band->supported_rates[count++] = rate;
}
}
band->supported_rates_len = count;
return 0;
}
static struct band *band_new_from_message(struct l_genl_attr *band)
{
uint16_t type;
struct l_genl_attr nested;
uint16_t count = 0;
struct band *ret;
size_t toalloc;
/* First find the number of supported rates */
while (l_genl_attr_next(band, &type, NULL, NULL)) {
switch (type) {
case NL80211_BAND_ATTR_RATES:
if (!l_genl_attr_recurse(band, &nested))
return NULL;
while (l_genl_attr_next(&nested, NULL, NULL, NULL))
count++;
}
}
toalloc = sizeof(struct band) + count * sizeof(uint8_t);
ret = l_malloc(toalloc);
memset(ret, 0, toalloc);
#if __GNUC__ == 11 && __GNUC_MINOR__ == 2
_Pragma("GCC diagnostic push")
_Pragma("GCC diagnostic ignored \"-Warray-bounds\"")
#endif
memset(ret->vht_mcs_set, 0xff, sizeof(ret->vht_mcs_set));
#if __GNUC__ == 11 && __GNUC_MINOR__ == 2
_Pragma("GCC diagnostic pop")
#endif
return ret;
}
static uint32_t get_iftypes(struct l_genl_attr *iftypes)
{
uint16_t type;
uint16_t len;
uint32_t types = 0;
while (l_genl_attr_next(iftypes, &type, &len, NULL)) {
if (len != 0)
continue;
types |= (1 << (type - 1));
}
return types;
}
static void parse_iftype_attrs(struct band *band, struct l_genl_attr *types)
{
uint16_t type;
uint16_t len;
const void *data;
unsigned int count = 0;
struct band_he_capabilities *he_cap =
l_new(struct band_he_capabilities, 1);
while (l_genl_attr_next(types, &type, &len, &data)) {
struct l_genl_attr iftypes;
switch (type) {
case NL80211_BAND_IFTYPE_ATTR_IFTYPES:
if (!l_genl_attr_recurse(types, &iftypes))
goto parse_error;
he_cap->iftypes = get_iftypes(&iftypes);
break;
case NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY:
if (len > sizeof(he_cap->he_phy_capa))
continue;
memcpy(he_cap->he_phy_capa, data, len);
count++;
break;
case NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET:
if (len > sizeof(he_cap->he_mcs_set))
continue;
memcpy(he_cap->he_mcs_set, data, len);
count++;
break;
default:
break;
}
}
/*
* Since the capabilities element indicates what values are present in
* the MCS set ensure both values are parsed
*/
if (count != 2 || !he_cap->iftypes)
goto parse_error;
if (!band->he_capabilities)
band->he_capabilities = l_queue_new();
l_queue_push_head(band->he_capabilities, he_cap);
return;
parse_error:
l_free(he_cap);
}
static void parse_band_iftype_data(struct band *band, struct l_genl_attr *ifdata)
{
while (l_genl_attr_next(ifdata, NULL, NULL, NULL)) {
struct l_genl_attr types;
if (!l_genl_attr_recurse(ifdata, &types))
continue;
parse_iftype_attrs(band, &types);
}
}
static void parse_supported_bands(struct wiphy *wiphy,
struct l_genl_attr *bands)
{
uint16_t type;
uint16_t len;
const void *data;
struct l_genl_attr attr;
while (l_genl_attr_next(bands, &type, NULL, NULL)) {
struct band **bandp;
struct band *band;
enum band_freq freq;
size_t num_channels;
switch (type) {
case NL80211_BAND_2GHZ:
bandp = &wiphy->band_2g;
freq = BAND_FREQ_2_4_GHZ;
num_channels = 14;
break;
case NL80211_BAND_5GHZ:
bandp = &wiphy->band_5g;
freq = BAND_FREQ_5_GHZ;
num_channels = 196;
break;
2022-02-25 18:58:05 +01:00
case NL80211_BAND_6GHZ:
bandp = &wiphy->band_6g;
freq = BAND_FREQ_6_GHZ;
num_channels = 233;
2022-02-25 18:58:05 +01:00
break;
default:
continue;
}
if (!l_genl_attr_recurse(bands, &attr))
continue;
if (*bandp == NULL) {
band = band_new_from_message(&attr);
if (!band)
continue;
band->freq = freq;
wiphy: Fix buffer overflow due to off-by-one error Since channels numbers are used as indexes into the array, and given that channel numbers start at '1' instead of 0, make sure to allocate a buffer large enough to not overflow when the max channel number for a given band is accessed. src/manager.c:manager_wiphy_dump_callback() New wiphy phy1 added (1) ==22290== Invalid write of size 2 ==22290== at 0x4624B2: nl80211_parse_supported_frequencies (nl80211util.c:570) ==22290== by 0x417CA5: parse_supported_bands (wiphy.c:1636) ==22290== by 0x418594: wiphy_parse_attributes (wiphy.c:1805) ==22290== by 0x418E20: wiphy_update_from_genl (wiphy.c:1991) ==22290== by 0x464589: manager_wiphy_dump_callback (manager.c:564) ==22290== by 0x4CBDDA: process_unicast (genl.c:944) ==22290== by 0x4CC19C: received_data (genl.c:1056) ==22290== by 0x4C7140: io_callback (io.c:120) ==22290== by 0x4C5A97: l_main_iterate (main.c:476) ==22290== by 0x4C5BDC: l_main_run (main.c:523) ==22290== by 0x4C5F0F: l_main_run_with_signal (main.c:645) ==22290== by 0x40503B: main (main.c:600) ==22290== Address 0x4aa76ec is 0 bytes after a block of size 28 alloc'd ==22290== at 0x48417B5: malloc (vg_replace_malloc.c:393) ==22290== by 0x4BC4D1: l_malloc (util.c:62) ==22290== by 0x417BE4: parse_supported_bands (wiphy.c:1619) ==22290== by 0x418594: wiphy_parse_attributes (wiphy.c:1805) ==22290== by 0x418E20: wiphy_update_from_genl (wiphy.c:1991) ==22290== by 0x464589: manager_wiphy_dump_callback (manager.c:564) ==22290== by 0x4CBDDA: process_unicast (genl.c:944) ==22290== by 0x4CC19C: received_data (genl.c:1056) ==22290== by 0x4C7140: io_callback (io.c:120) ==22290== by 0x4C5A97: l_main_iterate (main.c:476) ==22290== by 0x4C5BDC: l_main_run (main.c:523) ==22290== by 0x4C5F0F: l_main_run_with_signal (main.c:645) ==22290==
2023-01-26 16:59:56 +01:00
/*
* Since channels start at 1, allocate one extra in
* order to use channel indexes without arithmetic
*/
band->freq_attrs = l_new(struct band_freq_attrs,
wiphy: Fix buffer overflow due to off-by-one error Since channels numbers are used as indexes into the array, and given that channel numbers start at '1' instead of 0, make sure to allocate a buffer large enough to not overflow when the max channel number for a given band is accessed. src/manager.c:manager_wiphy_dump_callback() New wiphy phy1 added (1) ==22290== Invalid write of size 2 ==22290== at 0x4624B2: nl80211_parse_supported_frequencies (nl80211util.c:570) ==22290== by 0x417CA5: parse_supported_bands (wiphy.c:1636) ==22290== by 0x418594: wiphy_parse_attributes (wiphy.c:1805) ==22290== by 0x418E20: wiphy_update_from_genl (wiphy.c:1991) ==22290== by 0x464589: manager_wiphy_dump_callback (manager.c:564) ==22290== by 0x4CBDDA: process_unicast (genl.c:944) ==22290== by 0x4CC19C: received_data (genl.c:1056) ==22290== by 0x4C7140: io_callback (io.c:120) ==22290== by 0x4C5A97: l_main_iterate (main.c:476) ==22290== by 0x4C5BDC: l_main_run (main.c:523) ==22290== by 0x4C5F0F: l_main_run_with_signal (main.c:645) ==22290== by 0x40503B: main (main.c:600) ==22290== Address 0x4aa76ec is 0 bytes after a block of size 28 alloc'd ==22290== at 0x48417B5: malloc (vg_replace_malloc.c:393) ==22290== by 0x4BC4D1: l_malloc (util.c:62) ==22290== by 0x417BE4: parse_supported_bands (wiphy.c:1619) ==22290== by 0x418594: wiphy_parse_attributes (wiphy.c:1805) ==22290== by 0x418E20: wiphy_update_from_genl (wiphy.c:1991) ==22290== by 0x464589: manager_wiphy_dump_callback (manager.c:564) ==22290== by 0x4CBDDA: process_unicast (genl.c:944) ==22290== by 0x4CC19C: received_data (genl.c:1056) ==22290== by 0x4C7140: io_callback (io.c:120) ==22290== by 0x4C5A97: l_main_iterate (main.c:476) ==22290== by 0x4C5BDC: l_main_run (main.c:523) ==22290== by 0x4C5F0F: l_main_run_with_signal (main.c:645) ==22290==
2023-01-26 16:59:56 +01:00
num_channels + 1);
band->freqs_len = num_channels;
/* Reset iter to beginning */
if (!l_genl_attr_recurse(bands, &attr)) {
band_free(band);
continue;
}
} else
band = *bandp;
while (l_genl_attr_next(&attr, &type, &len, &data)) {
struct l_genl_attr nested;
switch (type) {
case NL80211_BAND_ATTR_FREQS:
nl80211_parse_supported_frequencies(&attr,
wiphy->supported_freqs,
band);
break;
case NL80211_BAND_ATTR_RATES:
if (parse_supported_rates(&attr, band) < 0) {
band_free(band);
continue;
}
break;
case NL80211_BAND_ATTR_VHT_MCS_SET:
if (L_WARN_ON(len != sizeof(band->vht_mcs_set)))
continue;
memcpy(band->vht_mcs_set, data, len);
band->vht_supported = true;
break;
case NL80211_BAND_ATTR_VHT_CAPA:
if (L_WARN_ON(len !=
sizeof(band->vht_capabilities)))
continue;
memcpy(band->vht_capabilities, data, len);
band->vht_supported = true;
break;
2021-05-28 21:19:57 +02:00
case NL80211_BAND_ATTR_HT_MCS_SET:
if (L_WARN_ON(len != sizeof(band->ht_mcs_set)))
continue;
memcpy(band->ht_mcs_set, data, len);
band->ht_supported = true;
break;
case NL80211_BAND_ATTR_HT_CAPA:
if (L_WARN_ON(len !=
sizeof(band->ht_capabilities)))
continue;
memcpy(band->ht_capabilities, data, len);
band->ht_supported = true;
break;
/*
* AMPDU factor/density are part of A-MPDU Parameters,
* 802.11-2020 Section 9.4.2.55.3.
*/
case NL80211_BAND_ATTR_HT_AMPDU_FACTOR:
if (L_WARN_ON(len != 1))
continue;
band->ht_ampdu_params |= l_get_u8(data) & 0x3;
break;
case NL80211_BAND_ATTR_HT_AMPDU_DENSITY:
if (L_WARN_ON(len != 1))
continue;
band->ht_ampdu_params |=
(l_get_u8(data) & 0x7) << 2;
break;
case NL80211_BAND_ATTR_IFTYPE_DATA:
if (!l_genl_attr_recurse(&attr, &nested))
continue;
parse_band_iftype_data(band, &nested);
break;
}
}
if (*bandp == NULL)
*bandp = band;
}
}
static void parse_supported_iftypes(struct wiphy *wiphy,
struct l_genl_attr *attr)
{
uint16_t type, len;
const void *data;
while (l_genl_attr_next(attr, &type, &len, &data)) {
/*
* NL80211_IFTYPE_UNSPECIFIED can be ignored, so we start
* at the first bit
*/
if (type > sizeof(wiphy->supported_iftypes) * 8) {
l_warn("unsupported iftype: %u", type);
continue;
}
wiphy->supported_iftypes |= 1 << (type - 1);
}
}
static void parse_iftype_extended_capabilities(struct wiphy *wiphy,
struct l_genl_attr *attr)
{
uint16_t type;
uint16_t len;
const void *data;
struct l_genl_attr nested;
while (l_genl_attr_next(attr, &type, &len, &data)) {
uint32_t iftype;
if (!l_genl_attr_recurse(attr, &nested))
continue;
if (!l_genl_attr_next(&nested, &type, &len, &data))
continue;
if (type != NL80211_ATTR_IFTYPE)
continue;
iftype = l_get_u32(data);
if (!l_genl_attr_next(&nested, &type, &len, &data))
continue;
if (type != NL80211_ATTR_EXT_CAPA)
continue;
wiphy->iftype_extended_capabilities[iftype] =
l_new(uint8_t, EXT_CAP_LEN + 2);
wiphy->iftype_extended_capabilities[iftype][0] =
IE_TYPE_EXTENDED_CAPABILITIES;
wiphy->iftype_extended_capabilities[iftype][1] =
EXT_CAP_LEN;
memcpy(wiphy->iftype_extended_capabilities[iftype] + 2,
data, minsize(len, EXT_CAP_LEN));
}
}
2016-06-25 02:20:35 +02:00
static void wiphy_parse_attributes(struct wiphy *wiphy,
struct l_genl_msg *msg)
{
struct l_genl_attr attr;
2016-06-25 02:20:35 +02:00
struct l_genl_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_FEATURE_FLAGS:
2016-06-25 02:20:35 +02:00
if (len != sizeof(uint32_t))
l_warn("Invalid feature flags attribute");
2016-06-25 02:20:35 +02:00
else
wiphy->feature_flags = *((uint32_t *) data);
break;
case NL80211_ATTR_EXT_FEATURES:
if (len > sizeof(wiphy->ext_features))
len = sizeof(wiphy->ext_features);
memcpy(wiphy->ext_features, data, len);
break;
case NL80211_ATTR_SUPPORTED_COMMANDS:
if (l_genl_attr_recurse(&attr, &nested))
2016-06-25 02:20:35 +02:00
parse_supported_commands(wiphy, &nested);
break;
case NL80211_ATTR_CIPHER_SUITES:
parse_supported_ciphers(wiphy, data, len);
break;
case NL80211_ATTR_WIPHY_BANDS:
if (l_genl_attr_recurse(&attr, &nested))
2016-06-25 02:20:35 +02:00
parse_supported_bands(wiphy, &nested);
break;
case NL80211_ATTR_MAX_NUM_SCAN_SSIDS:
if (len != sizeof(uint8_t))
l_warn("Invalid MAX_NUM_SCAN_SSIDS attribute");
else
wiphy->max_num_ssids_per_scan =
*((uint8_t *) data);
break;
case NL80211_ATTR_MAX_SCAN_IE_LEN:
if (len != sizeof(uint16_t))
l_warn("Invalid MAX_SCAN_IE_LEN attribute");
else
wiphy->max_scan_ie_len = *((uint16_t *) data);
break;
case NL80211_ATTR_SUPPORTED_IFTYPES:
if (l_genl_attr_recurse(&attr, &nested))
parse_supported_iftypes(wiphy, &nested);
break;
case NL80211_ATTR_OFFCHANNEL_TX_OK:
wiphy->offchannel_tx_ok = true;
break;
case NL80211_ATTR_EXT_CAPA:
memcpy(wiphy->extended_capabilities + 2,
data, minsize(EXT_CAP_LEN, len));
break;
case NL80211_ATTR_IFTYPE_EXT_CAPA:
if (!l_genl_attr_recurse(&attr, &nested))
break;
parse_iftype_extended_capabilities(wiphy, &nested);
break;
case NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION:
if (len != 4)
l_warn("Invalid MAX_ROC_DURATION attribute");
else
wiphy->max_roc_duration = *((uint32_t *) data);
break;
case NL80211_ATTR_ROAM_SUPPORT:
wiphy->support_fw_roam = true;
break;
case NL80211_ATTR_WIPHY_SELF_MANAGED_REG:
wiphy->self_managed = true;
break;
case NL80211_ATTR_PROBE_RESP_OFFLOAD:
wiphy->ap_probe_resp_offload = true;
break;
2022-12-20 22:43:15 +01:00
case NL80211_ATTR_SUPPORT_AP_UAPSD:
wiphy->supports_uapsd = true;
break;
}
}
2016-06-25 02:20:35 +02:00
}
static bool wiphy_get_driver_name(struct wiphy *wiphy)
{
L_AUTO_FREE_VAR(char *, driver_link) = NULL;
char driver_path[256];
ssize_t len;
unsigned int i;
unsigned int j;
const struct l_settings *config = iwd_get_config();
char **flag_list;
char *driver;
driver_link = l_strdup_printf("/sys/class/ieee80211/%s/device/driver",
wiphy->name);
len = readlink(driver_link, driver_path, sizeof(driver_path) - 1);
if (len == -1) {
l_error("Can't read %s: %s", driver_link, strerror(errno));
return false;
}
driver_path[len] = '\0';
driver = memrchr(driver_path, '/', len);
wiphy->driver_str = l_strdup(driver ? driver + 1 : driver_path);
for (i = 0; i < L_ARRAY_SIZE(driver_infos); i++)
if (!fnmatch(driver_infos[i].prefix, wiphy->driver_str, 0))
wiphy->driver_flags |= driver_infos[i].flags;
/* Check for any user-defined driver flags */
if (!l_settings_has_group(config, "DriverQuirks"))
return true;
for (i = 0; i < L_ARRAY_SIZE(driver_flag_names); i++) {
flag_list = l_settings_get_string_list(config, "DriverQuirks",
driver_flag_names[i].name, ',');
if (!flag_list)
continue;
for (j = 0; flag_list[j]; j++)
if (!fnmatch(flag_list[j], wiphy->driver_str, 0))
wiphy->driver_flags |=
driver_flag_names[i].flag;
l_strv_free(flag_list);
}
return true;
}
static int wiphy_get_permanent_addr_from_sysfs(struct wiphy *wiphy)
{
char addr[32];
ssize_t len;
len = read_file(addr, sizeof(addr),
"/sys/class/ieee80211/%s/macaddress",
wiphy->name);
if (len != 18) {
if (len < 0)
return -errno;
return -EINVAL;
}
/* Sysfs appends a \n at the end, strip it */
addr[17] = '\0';
if (!util_string_to_address(addr, wiphy->permanent_addr))
return -EINVAL;
return 0;
}
static void wiphy_register(struct wiphy *wiphy)
{
struct l_dbus *dbus = dbus_get_bus();
wiphy->soft_rfkill = rfkill_get_soft_state(wiphy->id);
wiphy->hard_rfkill = rfkill_get_hard_state(wiphy->id);
if (hwdb) {
char modalias[128];
ssize_t len;
struct l_hwdb_entry *entries = NULL, *kv;
len = read_file(modalias, sizeof(modalias) - 1,
"/sys/class/ieee80211/%s/device/modalias",
wiphy->name);
if (len > 0) {
modalias[len] = '\0';
entries = l_hwdb_lookup(hwdb, "%s", modalias);
}
for (kv = entries; kv; kv = kv->next) {
if (!strcmp(kv->key, "ID_MODEL_FROM_DATABASE")) {
if (wiphy->model_str)
continue;
wiphy->model_str = l_strdup(kv->value);
}
if (!strcmp(kv->key, "ID_VENDOR_FROM_DATABASE")) {
if (wiphy->vendor_str)
continue;
wiphy->vendor_str = l_strdup(kv->value);
}
}
l_hwdb_lookup_free(entries);
}
wiphy_get_driver_name(wiphy);
if (!l_dbus_object_add_interface(dbus, wiphy_get_path(wiphy),
IWD_WIPHY_INTERFACE, wiphy))
l_info("Unable to add the %s interface to %s",
IWD_WIPHY_INTERFACE, wiphy_get_path(wiphy));
if (!l_dbus_object_add_interface(dbus, wiphy_get_path(wiphy),
L_DBUS_INTERFACE_PROPERTIES, NULL))
l_info("Unable to add the %s interface to %s",
L_DBUS_INTERFACE_PROPERTIES,
wiphy_get_path(wiphy));
wiphy->registered = true;
}
struct wiphy *wiphy_create(uint32_t wiphy_id, const char *name)
{
struct wiphy *wiphy;
struct l_genl *genl = iwd_get_genl();
wiphy = wiphy_new(wiphy_id);
l_strlcpy(wiphy->name, name, sizeof(wiphy->name));
wiphy->nl80211 = l_genl_family_new(genl, NL80211_GENL_NAME);
l_queue_push_head(wiphy_list, wiphy);
if (!wiphy_is_managed(name))
wiphy->blacklisted = true;
wiphy: introduce new radio management APIs These APIs will handle fairness and order in any operations which radios can only do sequentially (offchannel, scanning, connection etc.). Both scan and frame-xchg are complex modules (especially scanning) which is why the radio management APIs were implemented generic enough where the changes to both modules will be minimal. Any module that requires this kind of work can push a work item into the radio management work queue (wiphy_radio_work_insert) and when the work is ready to be started radio management will call back into the module. Once the work is completed (and this may be some time later e.g. in scan results or a frame watch) the module can signal back that the work is finished (wiphy_radio_work_done). Wiphy will then pop the queue and continue with the next work item. A concept of priority was added in order to allow important offchannel operations (e.g. ANQP) to take priority over other work items. The priority is an integer, where lower values are of a higher priority. The concept of priority cleanly solves a lot of the complexity that was added in order to support ANQP queries (suspending scanning and waiting for ANQP to finish before connecting). Instead ANQP queries can be queued at a higher priority than scanning which removes the need for suspending scans. In addition we can treat connections as radio management work and insert them at a lower priority than ANQP, but higher than scanning. This forces the connection to wait for ANQP without having to track any state.
2020-07-09 02:04:32 +02:00
wiphy->work = l_queue_new();
return wiphy;
}
void wiphy_update_from_genl(struct wiphy *wiphy, struct l_genl_msg *msg)
{
if (wiphy->blacklisted)
return;
wiphy_parse_attributes(wiphy, msg);
}
void wiphy_update_name(struct wiphy *wiphy, const char *name)
{
bool updated = false;
if (strncmp(wiphy->name, name, sizeof(wiphy->name))) {
l_strlcpy(wiphy->name, name, sizeof(wiphy->name));
updated = true;
}
if (updated && wiphy->registered) {
struct l_dbus *dbus = dbus_get_bus();
l_dbus_property_changed(dbus, wiphy_get_path(wiphy),
IWD_WIPHY_INTERFACE, "Name");
}
}
static void wiphy_set_station_capability_bits(struct wiphy *wiphy)
{
uint8_t *ext_capa;
bool anqp_disabled;
/* No per-type capabilities exist for station, just copy the global */
if (!wiphy->iftype_extended_capabilities[NL80211_IFTYPE_STATION]) {
wiphy->iftype_extended_capabilities[NL80211_IFTYPE_STATION] =
l_new(uint8_t, EXT_CAP_LEN + 2);
memcpy(wiphy->iftype_extended_capabilities[
NL80211_IFTYPE_STATION],
wiphy->extended_capabilities,
EXT_CAP_LEN + 2);
}
ext_capa = wiphy->iftype_extended_capabilities[NL80211_IFTYPE_STATION];
if (!l_settings_get_bool(iwd_get_config(), "General", "DisableANQP",
&anqp_disabled))
anqp_disabled = true;
/* Set BSS Transition Management */
2021-03-12 05:23:52 +01:00
set_bit(ext_capa + 2, 19);
/* Set Interworking */
if (!anqp_disabled)
2021-03-12 05:23:52 +01:00
set_bit(ext_capa + 2, 31);
/* Set QoS Map */
if (wiphy->support_qos_set_map)
2021-03-12 05:23:52 +01:00
set_bit(ext_capa + 2, 32);
/* Set FILS */
2021-03-12 05:23:52 +01:00
set_bit(ext_capa + 2, 72);
}
static void wiphy_setup_rm_enabled_capabilities(struct wiphy *wiphy)
{
/* Nothing to do */
if (!wiphy_rrm_capable(wiphy))
return;
wiphy->rm_enabled_capabilities[0] = IE_TYPE_RM_ENABLED_CAPABILITIES;
wiphy->rm_enabled_capabilities[1] = 5;
/* Bits: Passive (4), Active (5), and Beacon Table (6) capabilities */
wiphy->rm_enabled_capabilities[2] = 0x70;
/*
* TODO: Support at least Link Measurement if TX_POWER_INSERTION is
* available
*/
}
static void wiphy_dump_done(void *user_data)
{
struct wiphy *wiphy = user_data;
const struct l_queue_entry *e;
/* This dump was canceled due to another dump */
if ((wiphy && !wiphy->dump_id) || (!wiphy && !wiphy_dump_id))
return;
if (wiphy) {
wiphy->dump_id = 0;
WATCHLIST_NOTIFY(&wiphy->state_watches,
wiphy_state_watch_func_t, wiphy,
WIPHY_STATE_WATCH_EVENT_REGDOM_DONE);
return;
}
wiphy_dump_id = 0;
for (e = l_queue_get_entries(wiphy_list); e; e = e->next) {
wiphy = e->data;
if (wiphy->self_managed)
continue;
WATCHLIST_NOTIFY(&wiphy->state_watches,
wiphy_state_watch_func_t, wiphy,
WIPHY_STATE_WATCH_EVENT_REGDOM_DONE);
}
}
/* We are dumping wiphy(s) due to a regulatory change */
static void wiphy_dump_callback(struct l_genl_msg *msg,
void *user_data)
{
struct wiphy *wiphy;
uint32_t id;
struct l_genl_attr bands;
struct l_genl_attr attr;
uint16_t type;
struct band *band;
if (nl80211_parse_attrs(msg, NL80211_ATTR_WIPHY, &id,
NL80211_ATTR_WIPHY_BANDS, &bands,
NL80211_ATTR_UNSPEC) < 0)
return;
wiphy = wiphy_find(id);
if (L_WARN_ON(!wiphy))
return;
/* Unregistered means the wiphy is blacklisted, don't bother parsing */
if (!wiphy->registered)
return;
while (l_genl_attr_next(&bands, &type, NULL, NULL)) {
switch (type) {
case NL80211_BAND_2GHZ:
band = wiphy->band_2g;
break;
case NL80211_BAND_5GHZ:
band = wiphy->band_5g;
break;
case NL80211_BAND_6GHZ:
band = wiphy->band_6g;
break;
default:
continue;
}
if (L_WARN_ON(!band))
continue;
if (!l_genl_attr_recurse(&bands, &attr))
return;
while (l_genl_attr_next(&attr, &type, NULL, NULL)) {
if (type != NL80211_BAND_ATTR_FREQS)
continue;
/*
* Just write over the old list for each frequency. In
* theory no new frequencies should be added so there
* should never be any stale values.
*/
nl80211_parse_supported_frequencies(&attr, NULL, band);
}
}
}
static bool wiphy_cancel_last_dump(struct wiphy *wiphy)
{
unsigned int id = 0;
/*
* Zero command ID to signal that wiphy_dump_done doesn't need to do
* anything.
*/
if (wiphy && wiphy->dump_id) {
id = wiphy->dump_id;
wiphy->dump_id = 0;
} else if (!wiphy && wiphy_dump_id) {
id = wiphy_dump_id;
wiphy_dump_id = 0;
}
if (id) {
l_debug("Canceling pending regdom wiphy dump (%s)",
wiphy ? wiphy->name : "global");
l_genl_family_cancel(nl80211, id);
}
return id != 0;
}
static void wiphy_dump_after_regdom(struct wiphy *wiphy)
{
const struct l_queue_entry *e;
struct l_genl_msg *msg;
unsigned int id;
bool no_start_event;
msg = l_genl_msg_new_sized(NL80211_CMD_GET_WIPHY, 128);
if (wiphy)
l_genl_msg_append_attr(msg, NL80211_ATTR_WIPHY, 4, &wiphy->id);
l_genl_msg_append_attr(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP, 0, NULL);
id = l_genl_family_dump(nl80211, msg, wiphy_dump_callback,
wiphy, wiphy_dump_done);
if (!id) {
l_error("Wiphy information dump failed");
l_genl_msg_unref(msg);
return;
}
/*
2024-11-07 14:26:12 +01:00
* Another update while dumping wiphy. This next dump should supersede
* the first and not result in a DONE event until this new dump is
* finished. This is because the disabled frequencies are in an unknown
* state and could cause incorrect behavior by any watchers.
*/
no_start_event = wiphy_cancel_last_dump(wiphy);
/* Limited dump so just emit the event for this wiphy */
if (wiphy) {
wiphy->dump_id = id;
if (no_start_event)
return;
WATCHLIST_NOTIFY(&wiphy->state_watches,
wiphy_state_watch_func_t, wiphy,
WIPHY_STATE_WATCH_EVENT_REGDOM_STARTED);
return;
}
wiphy_dump_id = id;
/* Otherwise for a global regdom change notify for all wiphy's */
for (e = l_queue_get_entries(wiphy_list); e; e = e->next) {
struct wiphy *w = e->data;
if (w->self_managed)
continue;
if (no_start_event)
continue;
WATCHLIST_NOTIFY(&w->state_watches, wiphy_state_watch_func_t,
w, WIPHY_STATE_WATCH_EVENT_REGDOM_STARTED);
}
}
static bool wiphy_update_reg_domain(struct wiphy *wiphy, bool global,
struct l_genl_msg *msg)
{
char out_country[2];
char *orig;
/*
* Write the new country code or XX if the reg domain is not a
* country domain.
*/
if (nl80211_parse_attrs(msg, NL80211_ATTR_REG_ALPHA2, out_country,
NL80211_ATTR_UNSPEC) < 0)
out_country[0] = out_country[1] = 'X';
if (global)
/*
* Leave @wiphy->regdom_country as all zeros to mean that it
* uses the global @regdom_country, i.e. is not self-managed.
*
* Even if we're called because we queried a new wiphy's
* reg domain, use the value we received here to update our
* global @regdom_country in case this is the first opportunity
* we have to update it -- possibly because this is the first
* wiphy created (that is not self-managed anyway) and we
* haven't received any REG_CHANGE events yet.
*/
orig = regdom_country;
else
orig = wiphy->regdom_country;
/*
* The kernel seems to send regdom updates even if the country didn't
* change. Skip these as there is no reason to re-dump.
*/
if (orig[0] == out_country[0] && orig[1] == out_country[1])
return false;
l_debug("New reg domain country code for %s is %c%c",
global ? "(global)" : wiphy->name,
out_country[0], out_country[1]);
orig[0] = out_country[0];
orig[1] = out_country[1];
return true;
}
static void wiphy_get_reg_cb(struct l_genl_msg *msg, void *user_data)
{
struct wiphy *wiphy = user_data;
uint32_t tmp;
bool global;
wiphy->get_reg_id = 0;
/*
* NL80211_CMD_GET_REG contains an NL80211_ATTR_WIPHY iff the wiphy
* uses a self-managed regulatory domain.
*/
global = nl80211_parse_attrs(msg, NL80211_ATTR_WIPHY, &tmp,
NL80211_ATTR_UNSPEC) < 0;
wiphy_update_reg_domain(wiphy, global, msg);
}
static void wiphy_get_reg_domain(struct wiphy *wiphy)
{
struct l_genl_msg *msg;
msg = l_genl_msg_new(NL80211_CMD_GET_REG);
l_genl_msg_append_attr(msg, NL80211_ATTR_WIPHY, 4, &wiphy->id);
wiphy->get_reg_id = l_genl_family_send(wiphy->nl80211, msg,
wiphy_get_reg_cb, wiphy, NULL);
if (!wiphy->get_reg_id) {
l_error("Error sending NL80211_CMD_GET_REG for %s", wiphy->name);
l_genl_msg_unref(msg);
}
}
void wiphy_create_complete(struct wiphy *wiphy)
{
/*
* With really bad timing two wiphy dumps can occur (initial and a
* NEW_WIPHY event) and actually register twice. Ignoring/preventing the
* second dump is problematic since it _could_ be a legitimate event so
* instead just prevent it from registering twice.
*/
if (wiphy->registered)
return;
wiphy_register(wiphy);
if (l_memeqzero(wiphy->permanent_addr, 6)) {
int err = wiphy_get_permanent_addr_from_sysfs(wiphy);
if (err < 0)
l_error("Can't read sysfs maccaddr for %s: %s",
wiphy->name, strerror(-err));
}
wiphy_set_station_capability_bits(wiphy);
wiphy_setup_rm_enabled_capabilities(wiphy);
wiphy_get_reg_domain(wiphy);
wiphy_print_basic_info(wiphy);
}
bool wiphy_destroy(struct wiphy *wiphy)
{
l_debug("");
if (!l_queue_remove(wiphy_list, wiphy))
return false;
if (wiphy->registered)
l_dbus_unregister_object(dbus_get_bus(), wiphy_get_path(wiphy));
wiphy_free(wiphy);
return true;
}
static void wiphy_rfkill_cb(unsigned int wiphy_id, bool soft, bool hard,
void *user_data)
{
struct wiphy *wiphy = wiphy_find(wiphy_id);
struct l_dbus *dbus = dbus_get_bus();
bool old_powered, new_powered;
enum wiphy_state_watch_event event;
if (!wiphy)
return;
old_powered = !wiphy->soft_rfkill && !wiphy->hard_rfkill;
wiphy->soft_rfkill = soft;
wiphy->hard_rfkill = hard;
new_powered = !wiphy->soft_rfkill && !wiphy->hard_rfkill;
if (old_powered == new_powered)
return;
event = new_powered ? WIPHY_STATE_WATCH_EVENT_POWERED :
WIPHY_STATE_WATCH_EVENT_RFKILLED;
WATCHLIST_NOTIFY(&wiphy->state_watches, wiphy_state_watch_func_t,
wiphy, event);
l_dbus_property_changed(dbus, wiphy_get_path(wiphy),
IWD_WIPHY_INTERFACE, "Powered");
}
static bool wiphy_property_get_powered(struct l_dbus *dbus,
struct l_dbus_message *message,
struct l_dbus_message_builder *builder,
void *user_data)
{
struct wiphy *wiphy = user_data;
bool value = !wiphy->soft_rfkill && !wiphy->hard_rfkill;
l_dbus_message_builder_append_basic(builder, 'b', &value);
return true;
}
2016-07-13 04:29:50 +02:00
static struct l_dbus_message *wiphy_property_set_powered(struct l_dbus *dbus,
struct l_dbus_message *message,
struct l_dbus_message_iter *new_value,
l_dbus_property_complete_cb_t complete,
void *user_data)
{
struct wiphy *wiphy = user_data;
bool old_powered, new_powered;
if (!l_dbus_message_iter_get_variant(new_value, "b", &new_powered))
return dbus_error_invalid_args(message);
old_powered = !wiphy->soft_rfkill && !wiphy->hard_rfkill;
if (old_powered == new_powered)
goto done;
if (wiphy->hard_rfkill)
return dbus_error_not_available(message);
if (!rfkill_set_soft_state(wiphy->id, !new_powered))
return dbus_error_failed(message);
done:
complete(dbus, message, NULL);
return NULL;
}
static bool wiphy_property_get_model(struct l_dbus *dbus,
struct l_dbus_message *message,
struct l_dbus_message_builder *builder,
void *user_data)
{
struct wiphy *wiphy = user_data;
if (!wiphy->model_str)
return false;
l_dbus_message_builder_append_basic(builder, 's', wiphy->model_str);
return true;
}
static bool wiphy_property_get_vendor(struct l_dbus *dbus,
struct l_dbus_message *message,
struct l_dbus_message_builder *builder,
void *user_data)
{
struct wiphy *wiphy = user_data;
if (!wiphy->vendor_str)
return false;
l_dbus_message_builder_append_basic(builder, 's', wiphy->vendor_str);
return true;
}
static bool wiphy_property_get_name(struct l_dbus *dbus,
struct l_dbus_message *message,
struct l_dbus_message_builder *builder,
void *user_data)
{
struct wiphy *wiphy = user_data;
char buf[20];
if (l_utf8_validate(wiphy->name, strlen(wiphy->name), NULL)) {
l_dbus_message_builder_append_basic(builder, 's', wiphy->name);
return true;
}
/*
* In the highly unlikely scenario that the wiphy name is not utf8,
* we simply use the canonical name phy<index>. The kernel guarantees
* that this name cannot be taken by any other wiphy, so this should
* be safe enough.
*/
sprintf(buf, "phy%d", wiphy->id);
l_dbus_message_builder_append_basic(builder, 's', buf);
return true;
}
#define WIPHY_MODE_MASK ( \
(1 << (NL80211_IFTYPE_STATION - 1)) | \
(1 << (NL80211_IFTYPE_AP - 1)) | \
(1 << (NL80211_IFTYPE_ADHOC - 1)))
static bool wiphy_property_get_supported_modes(struct l_dbus *dbus,
struct l_dbus_message *message,
struct l_dbus_message_builder *builder,
void *user_data)
{
struct wiphy *wiphy = user_data;
unsigned int j = 0;
char **iftypes = wiphy_get_supported_iftypes(wiphy, WIPHY_MODE_MASK);
l_dbus_message_builder_enter_array(builder, "s");
while (iftypes[j])
l_dbus_message_builder_append_basic(builder, 's', iftypes[j++]);
l_dbus_message_builder_leave_array(builder);
l_strfreev(iftypes);
return true;
}
static void setup_wiphy_interface(struct l_dbus_interface *interface)
{
l_dbus_interface_property(interface, "Powered", 0, "b",
2016-07-13 04:29:50 +02:00
wiphy_property_get_powered,
wiphy_property_set_powered);
l_dbus_interface_property(interface, "Model", 0, "s",
wiphy_property_get_model, NULL);
l_dbus_interface_property(interface, "Vendor", 0, "s",
wiphy_property_get_vendor, NULL);
l_dbus_interface_property(interface, "Name", 0, "s",
wiphy_property_get_name, NULL);
l_dbus_interface_property(interface, "SupportedModes", 0, "as",
wiphy_property_get_supported_modes,
NULL);
}
static void wiphy_reg_notify(struct l_genl_msg *msg, void *user_data)
{
uint8_t cmd = l_genl_msg_get_command(msg);
struct wiphy *wiphy = NULL;
uint32_t wiphy_id;
l_debug("Notification of command %s(%u)",
nl80211cmd_to_string(cmd), cmd);
switch (cmd) {
case NL80211_CMD_REG_CHANGE:
if (!wiphy_update_reg_domain(NULL, true, msg))
return;
break;
case NL80211_CMD_WIPHY_REG_CHANGE:
if (nl80211_parse_attrs(msg, NL80211_ATTR_WIPHY, &wiphy_id,
NL80211_ATTR_UNSPEC) < 0)
return;
wiphy = wiphy_find(wiphy_id);
if (!wiphy)
return;
if (!wiphy_update_reg_domain(wiphy, false, msg))
return;
break;
default:
return;
}
wiphy_dump_after_regdom(wiphy);
}
wiphy: introduce new radio management APIs These APIs will handle fairness and order in any operations which radios can only do sequentially (offchannel, scanning, connection etc.). Both scan and frame-xchg are complex modules (especially scanning) which is why the radio management APIs were implemented generic enough where the changes to both modules will be minimal. Any module that requires this kind of work can push a work item into the radio management work queue (wiphy_radio_work_insert) and when the work is ready to be started radio management will call back into the module. Once the work is completed (and this may be some time later e.g. in scan results or a frame watch) the module can signal back that the work is finished (wiphy_radio_work_done). Wiphy will then pop the queue and continue with the next work item. A concept of priority was added in order to allow important offchannel operations (e.g. ANQP) to take priority over other work items. The priority is an integer, where lower values are of a higher priority. The concept of priority cleanly solves a lot of the complexity that was added in order to support ANQP queries (suspending scanning and waiting for ANQP to finish before connecting). Instead ANQP queries can be queued at a higher priority than scanning which removes the need for suspending scans. In addition we can treat connections as radio management work and insert them at a lower priority than ANQP, but higher than scanning. This forces the connection to wait for ANQP without having to track any state.
2020-07-09 02:04:32 +02:00
static void wiphy_radio_work_next(struct wiphy *wiphy)
{
struct wiphy_radio_work_item *work;
bool done;
uint32_t id;
wiphy: introduce new radio management APIs These APIs will handle fairness and order in any operations which radios can only do sequentially (offchannel, scanning, connection etc.). Both scan and frame-xchg are complex modules (especially scanning) which is why the radio management APIs were implemented generic enough where the changes to both modules will be minimal. Any module that requires this kind of work can push a work item into the radio management work queue (wiphy_radio_work_insert) and when the work is ready to be started radio management will call back into the module. Once the work is completed (and this may be some time later e.g. in scan results or a frame watch) the module can signal back that the work is finished (wiphy_radio_work_done). Wiphy will then pop the queue and continue with the next work item. A concept of priority was added in order to allow important offchannel operations (e.g. ANQP) to take priority over other work items. The priority is an integer, where lower values are of a higher priority. The concept of priority cleanly solves a lot of the complexity that was added in order to support ANQP queries (suspending scanning and waiting for ANQP to finish before connecting). Instead ANQP queries can be queued at a higher priority than scanning which removes the need for suspending scans. In addition we can treat connections as radio management work and insert them at a lower priority than ANQP, but higher than scanning. This forces the connection to wait for ANQP without having to track any state.
2020-07-09 02:04:32 +02:00
work = l_queue_pop_head(wiphy->work);
wiphy: introduce new radio management APIs These APIs will handle fairness and order in any operations which radios can only do sequentially (offchannel, scanning, connection etc.). Both scan and frame-xchg are complex modules (especially scanning) which is why the radio management APIs were implemented generic enough where the changes to both modules will be minimal. Any module that requires this kind of work can push a work item into the radio management work queue (wiphy_radio_work_insert) and when the work is ready to be started radio management will call back into the module. Once the work is completed (and this may be some time later e.g. in scan results or a frame watch) the module can signal back that the work is finished (wiphy_radio_work_done). Wiphy will then pop the queue and continue with the next work item. A concept of priority was added in order to allow important offchannel operations (e.g. ANQP) to take priority over other work items. The priority is an integer, where lower values are of a higher priority. The concept of priority cleanly solves a lot of the complexity that was added in order to support ANQP queries (suspending scanning and waiting for ANQP to finish before connecting). Instead ANQP queries can be queued at a higher priority than scanning which removes the need for suspending scans. In addition we can treat connections as radio management work and insert them at a lower priority than ANQP, but higher than scanning. This forces the connection to wait for ANQP without having to track any state.
2020-07-09 02:04:32 +02:00
if (!work)
return;
id = work->id;
wiphy: introduce new radio management APIs These APIs will handle fairness and order in any operations which radios can only do sequentially (offchannel, scanning, connection etc.). Both scan and frame-xchg are complex modules (especially scanning) which is why the radio management APIs were implemented generic enough where the changes to both modules will be minimal. Any module that requires this kind of work can push a work item into the radio management work queue (wiphy_radio_work_insert) and when the work is ready to be started radio management will call back into the module. Once the work is completed (and this may be some time later e.g. in scan results or a frame watch) the module can signal back that the work is finished (wiphy_radio_work_done). Wiphy will then pop the queue and continue with the next work item. A concept of priority was added in order to allow important offchannel operations (e.g. ANQP) to take priority over other work items. The priority is an integer, where lower values are of a higher priority. The concept of priority cleanly solves a lot of the complexity that was added in order to support ANQP queries (suspending scanning and waiting for ANQP to finish before connecting). Instead ANQP queries can be queued at a higher priority than scanning which removes the need for suspending scans. In addition we can treat connections as radio management work and insert them at a lower priority than ANQP, but higher than scanning. This forces the connection to wait for ANQP without having to track any state.
2020-07-09 02:04:32 +02:00
l_debug("Starting work item %u", work->id);
wiphy->work_in_callback = true;
wiphy: introduce new radio management APIs These APIs will handle fairness and order in any operations which radios can only do sequentially (offchannel, scanning, connection etc.). Both scan and frame-xchg are complex modules (especially scanning) which is why the radio management APIs were implemented generic enough where the changes to both modules will be minimal. Any module that requires this kind of work can push a work item into the radio management work queue (wiphy_radio_work_insert) and when the work is ready to be started radio management will call back into the module. Once the work is completed (and this may be some time later e.g. in scan results or a frame watch) the module can signal back that the work is finished (wiphy_radio_work_done). Wiphy will then pop the queue and continue with the next work item. A concept of priority was added in order to allow important offchannel operations (e.g. ANQP) to take priority over other work items. The priority is an integer, where lower values are of a higher priority. The concept of priority cleanly solves a lot of the complexity that was added in order to support ANQP queries (suspending scanning and waiting for ANQP to finish before connecting). Instead ANQP queries can be queued at a higher priority than scanning which removes the need for suspending scans. In addition we can treat connections as radio management work and insert them at a lower priority than ANQP, but higher than scanning. This forces the connection to wait for ANQP without having to track any state.
2020-07-09 02:04:32 +02:00
done = work->ops->do_work(work);
wiphy->work_in_callback = false;
wiphy: introduce new radio management APIs These APIs will handle fairness and order in any operations which radios can only do sequentially (offchannel, scanning, connection etc.). Both scan and frame-xchg are complex modules (especially scanning) which is why the radio management APIs were implemented generic enough where the changes to both modules will be minimal. Any module that requires this kind of work can push a work item into the radio management work queue (wiphy_radio_work_insert) and when the work is ready to be started radio management will call back into the module. Once the work is completed (and this may be some time later e.g. in scan results or a frame watch) the module can signal back that the work is finished (wiphy_radio_work_done). Wiphy will then pop the queue and continue with the next work item. A concept of priority was added in order to allow important offchannel operations (e.g. ANQP) to take priority over other work items. The priority is an integer, where lower values are of a higher priority. The concept of priority cleanly solves a lot of the complexity that was added in order to support ANQP queries (suspending scanning and waiting for ANQP to finish before connecting). Instead ANQP queries can be queued at a higher priority than scanning which removes the need for suspending scans. In addition we can treat connections as radio management work and insert them at a lower priority than ANQP, but higher than scanning. This forces the connection to wait for ANQP without having to track any state.
2020-07-09 02:04:32 +02:00
if (done) {
/* Item was rescheduled, don't destroy */
if (work->id != id)
goto next;
wiphy: introduce new radio management APIs These APIs will handle fairness and order in any operations which radios can only do sequentially (offchannel, scanning, connection etc.). Both scan and frame-xchg are complex modules (especially scanning) which is why the radio management APIs were implemented generic enough where the changes to both modules will be minimal. Any module that requires this kind of work can push a work item into the radio management work queue (wiphy_radio_work_insert) and when the work is ready to be started radio management will call back into the module. Once the work is completed (and this may be some time later e.g. in scan results or a frame watch) the module can signal back that the work is finished (wiphy_radio_work_done). Wiphy will then pop the queue and continue with the next work item. A concept of priority was added in order to allow important offchannel operations (e.g. ANQP) to take priority over other work items. The priority is an integer, where lower values are of a higher priority. The concept of priority cleanly solves a lot of the complexity that was added in order to support ANQP queries (suspending scanning and waiting for ANQP to finish before connecting). Instead ANQP queries can be queued at a higher priority than scanning which removes the need for suspending scans. In addition we can treat connections as radio management work and insert them at a lower priority than ANQP, but higher than scanning. This forces the connection to wait for ANQP without having to track any state.
2020-07-09 02:04:32 +02:00
work->id = 0;
wiphy: introduce new radio management APIs These APIs will handle fairness and order in any operations which radios can only do sequentially (offchannel, scanning, connection etc.). Both scan and frame-xchg are complex modules (especially scanning) which is why the radio management APIs were implemented generic enough where the changes to both modules will be minimal. Any module that requires this kind of work can push a work item into the radio management work queue (wiphy_radio_work_insert) and when the work is ready to be started radio management will call back into the module. Once the work is completed (and this may be some time later e.g. in scan results or a frame watch) the module can signal back that the work is finished (wiphy_radio_work_done). Wiphy will then pop the queue and continue with the next work item. A concept of priority was added in order to allow important offchannel operations (e.g. ANQP) to take priority over other work items. The priority is an integer, where lower values are of a higher priority. The concept of priority cleanly solves a lot of the complexity that was added in order to support ANQP queries (suspending scanning and waiting for ANQP to finish before connecting). Instead ANQP queries can be queued at a higher priority than scanning which removes the need for suspending scans. In addition we can treat connections as radio management work and insert them at a lower priority than ANQP, but higher than scanning. This forces the connection to wait for ANQP without having to track any state.
2020-07-09 02:04:32 +02:00
wiphy->work_in_callback = true;
wiphy: introduce new radio management APIs These APIs will handle fairness and order in any operations which radios can only do sequentially (offchannel, scanning, connection etc.). Both scan and frame-xchg are complex modules (especially scanning) which is why the radio management APIs were implemented generic enough where the changes to both modules will be minimal. Any module that requires this kind of work can push a work item into the radio management work queue (wiphy_radio_work_insert) and when the work is ready to be started radio management will call back into the module. Once the work is completed (and this may be some time later e.g. in scan results or a frame watch) the module can signal back that the work is finished (wiphy_radio_work_done). Wiphy will then pop the queue and continue with the next work item. A concept of priority was added in order to allow important offchannel operations (e.g. ANQP) to take priority over other work items. The priority is an integer, where lower values are of a higher priority. The concept of priority cleanly solves a lot of the complexity that was added in order to support ANQP queries (suspending scanning and waiting for ANQP to finish before connecting). Instead ANQP queries can be queued at a higher priority than scanning which removes the need for suspending scans. In addition we can treat connections as radio management work and insert them at a lower priority than ANQP, but higher than scanning. This forces the connection to wait for ANQP without having to track any state.
2020-07-09 02:04:32 +02:00
destroy_work(work);
wiphy->work_in_callback = false;
wiphy: introduce new radio management APIs These APIs will handle fairness and order in any operations which radios can only do sequentially (offchannel, scanning, connection etc.). Both scan and frame-xchg are complex modules (especially scanning) which is why the radio management APIs were implemented generic enough where the changes to both modules will be minimal. Any module that requires this kind of work can push a work item into the radio management work queue (wiphy_radio_work_insert) and when the work is ready to be started radio management will call back into the module. Once the work is completed (and this may be some time later e.g. in scan results or a frame watch) the module can signal back that the work is finished (wiphy_radio_work_done). Wiphy will then pop the queue and continue with the next work item. A concept of priority was added in order to allow important offchannel operations (e.g. ANQP) to take priority over other work items. The priority is an integer, where lower values are of a higher priority. The concept of priority cleanly solves a lot of the complexity that was added in order to support ANQP queries (suspending scanning and waiting for ANQP to finish before connecting). Instead ANQP queries can be queued at a higher priority than scanning which removes the need for suspending scans. In addition we can treat connections as radio management work and insert them at a lower priority than ANQP, but higher than scanning. This forces the connection to wait for ANQP without having to track any state.
2020-07-09 02:04:32 +02:00
next:
wiphy: introduce new radio management APIs These APIs will handle fairness and order in any operations which radios can only do sequentially (offchannel, scanning, connection etc.). Both scan and frame-xchg are complex modules (especially scanning) which is why the radio management APIs were implemented generic enough where the changes to both modules will be minimal. Any module that requires this kind of work can push a work item into the radio management work queue (wiphy_radio_work_insert) and when the work is ready to be started radio management will call back into the module. Once the work is completed (and this may be some time later e.g. in scan results or a frame watch) the module can signal back that the work is finished (wiphy_radio_work_done). Wiphy will then pop the queue and continue with the next work item. A concept of priority was added in order to allow important offchannel operations (e.g. ANQP) to take priority over other work items. The priority is an integer, where lower values are of a higher priority. The concept of priority cleanly solves a lot of the complexity that was added in order to support ANQP queries (suspending scanning and waiting for ANQP to finish before connecting). Instead ANQP queries can be queued at a higher priority than scanning which removes the need for suspending scans. In addition we can treat connections as radio management work and insert them at a lower priority than ANQP, but higher than scanning. This forces the connection to wait for ANQP without having to track any state.
2020-07-09 02:04:32 +02:00
wiphy_radio_work_next(wiphy);
} else {
/*
* Ensures no other work item will get inserted before this one
* while the work is being done.
*/
work->priority = INT_MIN;
l_queue_push_head(wiphy->work, work);
wiphy: introduce new radio management APIs These APIs will handle fairness and order in any operations which radios can only do sequentially (offchannel, scanning, connection etc.). Both scan and frame-xchg are complex modules (especially scanning) which is why the radio management APIs were implemented generic enough where the changes to both modules will be minimal. Any module that requires this kind of work can push a work item into the radio management work queue (wiphy_radio_work_insert) and when the work is ready to be started radio management will call back into the module. Once the work is completed (and this may be some time later e.g. in scan results or a frame watch) the module can signal back that the work is finished (wiphy_radio_work_done). Wiphy will then pop the queue and continue with the next work item. A concept of priority was added in order to allow important offchannel operations (e.g. ANQP) to take priority over other work items. The priority is an integer, where lower values are of a higher priority. The concept of priority cleanly solves a lot of the complexity that was added in order to support ANQP queries (suspending scanning and waiting for ANQP to finish before connecting). Instead ANQP queries can be queued at a higher priority than scanning which removes the need for suspending scans. In addition we can treat connections as radio management work and insert them at a lower priority than ANQP, but higher than scanning. This forces the connection to wait for ANQP without having to track any state.
2020-07-09 02:04:32 +02:00
}
}
static int insert_by_priority(const void *a, const void *b, void *user_data)
{
const struct wiphy_radio_work_item *new = a;
const struct wiphy_radio_work_item *work = b;
if (work->priority <= new->priority)
return 1;
return -1;
}
uint32_t wiphy_radio_work_insert(struct wiphy *wiphy,
struct wiphy_radio_work_item *item,
int priority,
const struct wiphy_radio_work_item_ops *ops)
{
item->priority = priority;
item->ops = ops;
item->id = ++work_ids;
l_debug("Inserting work item %u", item->id);
l_queue_insert(wiphy->work, item, insert_by_priority, NULL);
if (l_queue_length(wiphy->work) == 1 && !wiphy->work_in_callback)
wiphy: introduce new radio management APIs These APIs will handle fairness and order in any operations which radios can only do sequentially (offchannel, scanning, connection etc.). Both scan and frame-xchg are complex modules (especially scanning) which is why the radio management APIs were implemented generic enough where the changes to both modules will be minimal. Any module that requires this kind of work can push a work item into the radio management work queue (wiphy_radio_work_insert) and when the work is ready to be started radio management will call back into the module. Once the work is completed (and this may be some time later e.g. in scan results or a frame watch) the module can signal back that the work is finished (wiphy_radio_work_done). Wiphy will then pop the queue and continue with the next work item. A concept of priority was added in order to allow important offchannel operations (e.g. ANQP) to take priority over other work items. The priority is an integer, where lower values are of a higher priority. The concept of priority cleanly solves a lot of the complexity that was added in order to support ANQP queries (suspending scanning and waiting for ANQP to finish before connecting). Instead ANQP queries can be queued at a higher priority than scanning which removes the need for suspending scans. In addition we can treat connections as radio management work and insert them at a lower priority than ANQP, but higher than scanning. This forces the connection to wait for ANQP without having to track any state.
2020-07-09 02:04:32 +02:00
wiphy_radio_work_next(wiphy);
return item->id;
}
static bool match_id(const void *a, const void *b)
{
const struct wiphy_radio_work_item *item = a;
if (item->id == L_PTR_TO_UINT(b))
return true;
return false;
}
void wiphy_radio_work_done(struct wiphy *wiphy, uint32_t id)
{
struct wiphy_radio_work_item *item;
bool next = false;
item = l_queue_peek_head(wiphy->work);
if (!item)
return;
if (item->id == id) {
next = true;
l_queue_pop_head(wiphy->work);
} else
item = l_queue_remove_if(wiphy->work, match_id,
L_UINT_TO_PTR(id));
if (!item)
return;
l_debug("Work item %u done", id);
item->id = 0;
wiphy->work_in_callback = true;
wiphy: introduce new radio management APIs These APIs will handle fairness and order in any operations which radios can only do sequentially (offchannel, scanning, connection etc.). Both scan and frame-xchg are complex modules (especially scanning) which is why the radio management APIs were implemented generic enough where the changes to both modules will be minimal. Any module that requires this kind of work can push a work item into the radio management work queue (wiphy_radio_work_insert) and when the work is ready to be started radio management will call back into the module. Once the work is completed (and this may be some time later e.g. in scan results or a frame watch) the module can signal back that the work is finished (wiphy_radio_work_done). Wiphy will then pop the queue and continue with the next work item. A concept of priority was added in order to allow important offchannel operations (e.g. ANQP) to take priority over other work items. The priority is an integer, where lower values are of a higher priority. The concept of priority cleanly solves a lot of the complexity that was added in order to support ANQP queries (suspending scanning and waiting for ANQP to finish before connecting). Instead ANQP queries can be queued at a higher priority than scanning which removes the need for suspending scans. In addition we can treat connections as radio management work and insert them at a lower priority than ANQP, but higher than scanning. This forces the connection to wait for ANQP without having to track any state.
2020-07-09 02:04:32 +02:00
destroy_work(item);
wiphy->work_in_callback = false;
wiphy: introduce new radio management APIs These APIs will handle fairness and order in any operations which radios can only do sequentially (offchannel, scanning, connection etc.). Both scan and frame-xchg are complex modules (especially scanning) which is why the radio management APIs were implemented generic enough where the changes to both modules will be minimal. Any module that requires this kind of work can push a work item into the radio management work queue (wiphy_radio_work_insert) and when the work is ready to be started radio management will call back into the module. Once the work is completed (and this may be some time later e.g. in scan results or a frame watch) the module can signal back that the work is finished (wiphy_radio_work_done). Wiphy will then pop the queue and continue with the next work item. A concept of priority was added in order to allow important offchannel operations (e.g. ANQP) to take priority over other work items. The priority is an integer, where lower values are of a higher priority. The concept of priority cleanly solves a lot of the complexity that was added in order to support ANQP queries (suspending scanning and waiting for ANQP to finish before connecting). Instead ANQP queries can be queued at a higher priority than scanning which removes the need for suspending scans. In addition we can treat connections as radio management work and insert them at a lower priority than ANQP, but higher than scanning. This forces the connection to wait for ANQP without having to track any state.
2020-07-09 02:04:32 +02:00
if (next)
wiphy_radio_work_next(wiphy);
}
int wiphy_radio_work_is_running(struct wiphy *wiphy, uint32_t id)
{
struct wiphy_radio_work_item *item = l_queue_find(wiphy->work, match_id,
L_UINT_TO_PTR(id));
if (!item)
return -ENOENT;
return item == l_queue_peek_head(wiphy->work) ? 1 : 0;
}
uint32_t wiphy_radio_work_reschedule(struct wiphy *wiphy,
struct wiphy_radio_work_item *item)
{
/*
* This should only be called from within the do_work callback, meaning
* the item should not be in the queue. Any re-insertion on a running
* item after do_work is not allowed.
*/
if (L_WARN_ON(wiphy_radio_work_is_running(wiphy, item->id) != -ENOENT))
return 0;
work_ids++;
l_debug("Rescheduling work item %u, new id %u", item->id, work_ids);
item->id = work_ids;
l_queue_insert(wiphy->work, item, insert_by_priority, NULL);
return item->id;
}
static int wiphy_init(void)
2014-07-29 21:25:01 +02:00
{
struct l_genl *genl = iwd_get_genl();
const struct l_settings *config = iwd_get_config();
const char *whitelist = iwd_get_phy_whitelist();
const char *blacklist = iwd_get_phy_blacklist();
const char *s;
nl80211 = l_genl_family_new(genl, NL80211_GENL_NAME);
/*
* This is an extra sanity check so that no memory is leaked
* in case the generic netlink handling gets confused.
*/
if (wiphy_list) {
l_warn("Destroying existing list of wiphy devices");
l_queue_destroy(wiphy_list, NULL);
}
wiphy_list = l_queue_new();
rfkill_watch_add(wiphy_rfkill_cb, NULL);
if (!l_dbus_register_interface(dbus_get_bus(),
IWD_WIPHY_INTERFACE,
setup_wiphy_interface,
NULL, false))
l_error("Unable to register the %s interface",
IWD_WIPHY_INTERFACE);
hwdb = l_hwdb_new_default();
2017-03-16 22:45:10 +01:00
if (whitelist)
whitelist_filter = l_strsplit(whitelist, ',');
if (blacklist)
blacklist_filter = l_strsplit(blacklist, ',');
s = l_settings_get_value(config, "General",
"AddressRandomizationRange");
if (s) {
if (!strcmp(s, "nic"))
mac_randomize_bytes = 3;
else if (!strcmp(s, "full"))
mac_randomize_bytes = 6;
else
l_warn("Invalid [General].AddressRandomizationRange"
" value: %s", s);
}
if (!l_genl_family_register(nl80211, NL80211_MULTICAST_GROUP_REG,
wiphy_reg_notify, NULL, NULL))
l_error("Registering for regulatory notifications failed");
return 0;
2014-07-29 21:25:01 +02:00
}
static void wiphy_exit(void)
2014-07-29 21:25:01 +02:00
{
2017-03-16 22:45:10 +01:00
l_strfreev(whitelist_filter);
l_strfreev(blacklist_filter);
if (wiphy_dump_id) {
l_genl_family_cancel(nl80211, wiphy_dump_id);
wiphy_dump_id = 0;
}
2015-09-29 02:51:40 +02:00
l_queue_destroy(wiphy_list, wiphy_free);
wiphy_list = NULL;
2014-07-29 21:25:01 +02:00
l_genl_family_free(nl80211);
2015-09-29 02:51:40 +02:00
nl80211 = NULL;
mac_randomize_bytes = 6;
l_dbus_unregister_interface(dbus_get_bus(), IWD_WIPHY_INTERFACE);
l_hwdb_unref(hwdb);
2014-07-29 21:25:01 +02:00
}
IWD_MODULE(wiphy, wiphy_init, wiphy_exit);
IWD_MODULE_DEPENDS(wiphy, rfkill);