Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
shipped-certs.c
extra-certs.c
+204
View File
@@ -0,0 +1,204 @@
# SPDX-License-Identifier: GPL-2.0-only
config WIRELESS_EXT
bool
config WEXT_CORE
def_bool y
depends on CFG80211_WEXT || WIRELESS_EXT
config WEXT_PROC
def_bool y
depends on PROC_FS
depends on WEXT_CORE
config WEXT_PRIV
bool
config CFG80211
tristate "cfg80211 - wireless configuration API"
depends on RFKILL || !RFKILL
select FW_LOADER
select CRC32
# may need to update this when certificates are changed and are
# using a different algorithm, though right now they shouldn't
# (this is here rather than below to allow it to be a module)
select CRYPTO_SHA256 if CFG80211_USE_KERNEL_REGDB_KEYS
help
cfg80211 is the Linux wireless LAN (802.11) configuration API.
Enable this if you have a wireless device.
For more information refer to documentation on the wireless wiki:
https://wireless.wiki.kernel.org/en/developers/Documentation/cfg80211
When built as a module it will be called cfg80211.
if CFG80211
config NL80211_TESTMODE
bool "nl80211 testmode command"
help
The nl80211 testmode command helps implementing things like
factory calibration or validation tools for wireless chips.
Select this option ONLY for kernels that are specifically
built for such purposes.
Debugging tools that are supposed to end up in the hands of
users should better be implemented with debugfs.
Say N.
config CFG80211_DEVELOPER_WARNINGS
bool "enable developer warnings"
default n
help
This option enables some additional warnings that help
cfg80211 developers and driver developers, but beware that
they can also trigger due to races with userspace.
For example, when a driver reports that it was disconnected
from the AP, but the user disconnects manually at the same
time, the warning might trigger spuriously due to races.
Say Y only if you are developing cfg80211 or a driver based
on it (or mac80211).
config CFG80211_CERTIFICATION_ONUS
bool "cfg80211 certification onus"
depends on EXPERT
default n
help
You should disable this option unless you are both capable
and willing to ensure your system will remain regulatory
compliant with the features available under this option.
Some options may still be under heavy development and
for whatever reason regulatory compliance has not or
cannot yet be verified. Regulatory verification may at
times only be possible until you have the final system
in place.
This option should only be enabled by system integrators
or distributions that have done work necessary to ensure
regulatory certification on the system with the enabled
features. Alternatively you can enable this option if
you are a wireless researcher and are working in a controlled
and approved environment by your local regulatory agency.
config CFG80211_REQUIRE_SIGNED_REGDB
bool "require regdb signature" if CFG80211_CERTIFICATION_ONUS
default y
select SYSTEM_DATA_VERIFICATION
help
Require that in addition to the "regulatory.db" file a
"regulatory.db.p7s" can be loaded with a valid PKCS#7
signature for the regulatory.db file made by one of the
keys in the certs/ directory.
config CFG80211_USE_KERNEL_REGDB_KEYS
bool "allow regdb keys shipped with the kernel" if CFG80211_CERTIFICATION_ONUS
default y
depends on CFG80211_REQUIRE_SIGNED_REGDB
help
Allow the regulatory database to be signed by one of the keys for
which certificates are part of the kernel sources
(in net/wireless/certs/).
This is currently only Seth Forshee's key, who is the regulatory
database maintainer.
config CFG80211_EXTRA_REGDB_KEYDIR
string "additional regdb key directory" if CFG80211_CERTIFICATION_ONUS
depends on CFG80211_REQUIRE_SIGNED_REGDB
help
If selected, point to a directory with DER-encoded X.509
certificates like in the kernel sources (net/wireless/certs/)
that shall be accepted for a signed regulatory database.
Note that you need to also select the correct CRYPTO_<hash> modules
for your certificates, and if cfg80211 is built-in they also must be.
config CFG80211_REG_CELLULAR_HINTS
bool "cfg80211 regulatory support for cellular base station hints"
depends on CFG80211_CERTIFICATION_ONUS
help
This option enables support for parsing regulatory hints
from cellular base stations. If enabled and at least one driver
claims support for parsing cellular base station hints the
regulatory core will allow and parse these regulatory hints.
The regulatory core will only apply these regulatory hints on
drivers that support this feature. You should only enable this
feature if you have tested and validated this feature on your
systems.
config CFG80211_REG_RELAX_NO_IR
bool "cfg80211 support for NO_IR relaxation"
depends on CFG80211_CERTIFICATION_ONUS
help
This option enables support for relaxation of the NO_IR flag for
situations that certain regulatory bodies have provided clarifications
on how relaxation can occur. This feature has an inherent dependency on
userspace features which must have been properly tested and as such is
not enabled by default.
A relaxation feature example is allowing the operation of a P2P group
owner (GO) on channels marked with NO_IR if there is an additional BSS
interface which associated to an AP which userspace assumes or confirms
to be an authorized master, i.e., with radar detection support and DFS
capabilities. However, note that in order to not create daisy chain
scenarios, this relaxation is not allowed in cases where the BSS client
is associated to P2P GO and in addition the P2P GO instantiated on
a channel due to this relaxation should not allow connection from
non P2P clients.
The regulatory core will apply these relaxations only for drivers that
support this feature by declaring the appropriate channel flags and
capabilities in their registration flow.
config CFG80211_DEFAULT_PS
bool "enable powersave by default"
default y
help
This option enables powersave mode by default.
If this causes your applications to misbehave you should fix your
applications instead -- they need to register their network
latency requirement, see Documentation/power/pm_qos_interface.rst.
config CFG80211_DEBUGFS
bool "cfg80211 DebugFS entries"
depends on DEBUG_FS
help
You can enable this if you want debugfs entries for cfg80211.
If unsure, say N.
config CFG80211_CRDA_SUPPORT
bool "support CRDA" if EXPERT
default y
help
You should enable this option unless you know for sure you have no
need for it, for example when using the regulatory database loaded as
a firmware file.
If unsure, say Y.
config CFG80211_WEXT
bool "cfg80211 wireless extensions compatibility"
select WEXT_CORE
help
Enable this option if you need old userspace for wireless
extensions with cfg80211-based drivers.
config CFG80211_KUNIT_TEST
tristate "KUnit tests for cfg80211" if !KUNIT_ALL_TESTS
depends on KUNIT
depends on CFG80211
default KUNIT_ALL_TESTS
help
Enable this option to test cfg80211 functions with kunit.
If unsure, say N.
endif # CFG80211
+56
View File
@@ -0,0 +1,56 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_CFG80211) += cfg80211.o
obj-y += tests/
obj-$(CONFIG_WEXT_CORE) += wext-core.o
obj-$(CONFIG_WEXT_PROC) += wext-proc.o
obj-$(CONFIG_WEXT_PRIV) += wext-priv.o
cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o
cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o trace.o ocb.o
cfg80211-y += michael-mic.o pmsr.o
cfg80211-$(CONFIG_OF) += of.o
cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o
cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o
CFLAGS_trace.o := -I$(src)
cfg80211-$(CONFIG_CFG80211_USE_KERNEL_REGDB_KEYS) += shipped-certs.o
ifneq ($(CONFIG_CFG80211_EXTRA_REGDB_KEYDIR),)
cfg80211-y += extra-certs.o
endif
$(obj)/shipped-certs.c: $(sort $(wildcard $(src)/certs/*.hex))
@$(kecho) " GEN $@"
$(Q)(echo '#include "reg.h"'; \
echo 'const u8 shipped_regdb_certs[] = {'; \
echo | cat - $^ ; \
echo '};'; \
echo 'unsigned int shipped_regdb_certs_len = sizeof(shipped_regdb_certs);'; \
) > $@
$(obj)/extra-certs.c: $(CONFIG_CFG80211_EXTRA_REGDB_KEYDIR) \
$(sort $(wildcard $(CONFIG_CFG80211_EXTRA_REGDB_KEYDIR)/*.x509))
@$(kecho) " GEN $@"
$(Q)(set -e; \
allf=""; \
for f in $^ ; do \
test -f $$f || continue;\
# similar to hexdump -v -e '1/1 "0x%.2x," "\n"' \
thisf=$$(od -An -v -tx1 < $$f | \
sed -e 's/ /\n/g' | \
sed -e 's/^[0-9a-f]\+$$/\0/;t;d' | \
sed -e 's/^/0x/;s/$$/,/'); \
# file should not be empty - maybe command substitution failed? \
test ! -z "$$thisf";\
allf=$$allf$$thisf;\
done; \
( \
echo '#include "reg.h"'; \
echo 'const u8 extra_regdb_certs[] = {'; \
echo "$$allf"; \
echo '};'; \
echo 'unsigned int extra_regdb_certs_len = sizeof(extra_regdb_certs);'; \
) > $@)
clean-files += shipped-certs.c extra-certs.c
+74
View File
@@ -0,0 +1,74 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Parts of this file are
* Copyright (C) 2022-2023 Intel Corporation
*/
#include <linux/ieee80211.h>
#include <linux/export.h>
#include <net/cfg80211.h>
#include "nl80211.h"
#include "core.h"
#include "rdev-ops.h"
static int ___cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
struct net_device *dev, unsigned int link_id,
bool notify)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
int err;
lockdep_assert_wiphy(wdev->wiphy);
if (!rdev->ops->stop_ap)
return -EOPNOTSUPP;
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
return -EOPNOTSUPP;
if (!wdev->links[link_id].ap.beacon_interval)
return -ENOENT;
err = rdev_stop_ap(rdev, dev, link_id);
if (!err) {
wdev->conn_owner_nlportid = 0;
wdev->links[link_id].ap.beacon_interval = 0;
memset(&wdev->links[link_id].ap.chandef, 0,
sizeof(wdev->links[link_id].ap.chandef));
wdev->u.ap.ssid_len = 0;
rdev_set_qos_map(rdev, dev, NULL);
if (notify)
nl80211_send_ap_stopped(wdev, link_id);
/* Should we apply the grace period during beaconing interface
* shutdown also?
*/
cfg80211_sched_dfs_chan_update(rdev);
}
schedule_work(&cfg80211_disconnect_work);
return err;
}
int cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
struct net_device *dev, int link_id,
bool notify)
{
unsigned int link;
int ret = 0;
if (link_id >= 0)
return ___cfg80211_stop_ap(rdev, dev, link_id, notify);
for_each_valid_link(dev->ieee80211_ptr, link) {
int ret1 = ___cfg80211_stop_ap(rdev, dev, link, notify);
if (ret1)
ret = ret1;
/* try the next one also if one errored */
}
return ret;
}
@@ -0,0 +1,86 @@
/* Seth Forshee's regdb certificate */
0x30, 0x82, 0x02, 0xa4, 0x30, 0x82, 0x01, 0x8c,
0x02, 0x09, 0x00, 0xb2, 0x8d, 0xdf, 0x47, 0xae,
0xf9, 0xce, 0xa7, 0x30, 0x0d, 0x06, 0x09, 0x2a,
0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b,
0x05, 0x00, 0x30, 0x13, 0x31, 0x11, 0x30, 0x0f,
0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x08, 0x73,
0x66, 0x6f, 0x72, 0x73, 0x68, 0x65, 0x65, 0x30,
0x20, 0x17, 0x0d, 0x31, 0x37, 0x31, 0x30, 0x30,
0x36, 0x31, 0x39, 0x34, 0x30, 0x33, 0x35, 0x5a,
0x18, 0x0f, 0x32, 0x31, 0x31, 0x37, 0x30, 0x39,
0x31, 0x32, 0x31, 0x39, 0x34, 0x30, 0x33, 0x35,
0x5a, 0x30, 0x13, 0x31, 0x11, 0x30, 0x0f, 0x06,
0x03, 0x55, 0x04, 0x03, 0x0c, 0x08, 0x73, 0x66,
0x6f, 0x72, 0x73, 0x68, 0x65, 0x65, 0x30, 0x82,
0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05,
0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82,
0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb5,
0x40, 0xe3, 0x9c, 0x28, 0x84, 0x39, 0x03, 0xf2,
0x39, 0xd7, 0x66, 0x2c, 0x41, 0x38, 0x15, 0xac,
0x7e, 0xa5, 0x83, 0x71, 0x25, 0x7e, 0x90, 0x7c,
0x68, 0xdd, 0x6f, 0x3f, 0xd9, 0xd7, 0x59, 0x38,
0x9f, 0x7c, 0x6a, 0x52, 0xc2, 0x03, 0x2a, 0x2d,
0x7e, 0x66, 0xf4, 0x1e, 0xb3, 0x12, 0x70, 0x20,
0x5b, 0xd4, 0x97, 0x32, 0x3d, 0x71, 0x8b, 0x3b,
0x1b, 0x08, 0x17, 0x14, 0x6b, 0x61, 0xc4, 0x57,
0x8b, 0x96, 0x16, 0x1c, 0xfd, 0x24, 0xd5, 0x0b,
0x09, 0xf9, 0x68, 0x11, 0x84, 0xfb, 0xca, 0x51,
0x0c, 0xd1, 0x45, 0x19, 0xda, 0x10, 0x44, 0x8a,
0xd9, 0xfe, 0x76, 0xa9, 0xfd, 0x60, 0x2d, 0x18,
0x0b, 0x28, 0x95, 0xb2, 0x2d, 0xea, 0x88, 0x98,
0xb8, 0xd1, 0x56, 0x21, 0xf0, 0x53, 0x1f, 0xf1,
0x02, 0x6f, 0xe9, 0x46, 0x9b, 0x93, 0x5f, 0x28,
0x90, 0x0f, 0xac, 0x36, 0xfa, 0x68, 0x23, 0x71,
0x57, 0x56, 0xf6, 0xcc, 0xd3, 0xdf, 0x7d, 0x2a,
0xd9, 0x1b, 0x73, 0x45, 0xeb, 0xba, 0x27, 0x85,
0xef, 0x7a, 0x7f, 0xa5, 0xcb, 0x80, 0xc7, 0x30,
0x36, 0xd2, 0x53, 0xee, 0xec, 0xac, 0x1e, 0xe7,
0x31, 0xf1, 0x36, 0xa2, 0x9c, 0x63, 0xc6, 0x65,
0x5b, 0x7f, 0x25, 0x75, 0x68, 0xa1, 0xea, 0xd3,
0x7e, 0x00, 0x5c, 0x9a, 0x5e, 0xd8, 0x20, 0x18,
0x32, 0x77, 0x07, 0x29, 0x12, 0x66, 0x1e, 0x36,
0x73, 0xe7, 0x97, 0x04, 0x41, 0x37, 0xb1, 0xb1,
0x72, 0x2b, 0xf4, 0xa1, 0x29, 0x20, 0x7c, 0x96,
0x79, 0x0b, 0x2b, 0xd0, 0xd8, 0xde, 0xc8, 0x6c,
0x3f, 0x93, 0xfb, 0xc5, 0xee, 0x78, 0x52, 0x11,
0x15, 0x1b, 0x7a, 0xf6, 0xe2, 0x68, 0x99, 0xe7,
0xfb, 0x46, 0x16, 0x84, 0xe3, 0xc7, 0xa1, 0xe6,
0xe0, 0xd2, 0x46, 0xd5, 0xe1, 0xc4, 0x5f, 0xa0,
0x66, 0xf4, 0xda, 0xc4, 0xff, 0x95, 0x1d, 0x02,
0x03, 0x01, 0x00, 0x01, 0x30, 0x0d, 0x06, 0x09,
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00,
0x87, 0x03, 0xda, 0xf2, 0x82, 0xc2, 0xdd, 0xaf,
0x7c, 0x44, 0x2f, 0x86, 0xd3, 0x5f, 0x4c, 0x93,
0x48, 0xb9, 0xfe, 0x07, 0x17, 0xbb, 0x21, 0xf7,
0x25, 0x23, 0x4e, 0xaa, 0x22, 0x0c, 0x16, 0xb9,
0x73, 0xae, 0x9d, 0x46, 0x7c, 0x75, 0xd9, 0xc3,
0x49, 0x57, 0x47, 0xbf, 0x33, 0xb7, 0x97, 0xec,
0xf5, 0x40, 0x75, 0xc0, 0x46, 0x22, 0xf0, 0xa0,
0x5d, 0x9c, 0x79, 0x13, 0xa1, 0xff, 0xb8, 0xa3,
0x2f, 0x7b, 0x8e, 0x06, 0x3f, 0xc8, 0xb6, 0xe4,
0x6a, 0x28, 0xf2, 0x34, 0x5c, 0x23, 0x3f, 0x32,
0xc0, 0xe6, 0xad, 0x0f, 0xac, 0xcf, 0x55, 0x74,
0x47, 0x73, 0xd3, 0x01, 0x85, 0xb7, 0x0b, 0x22,
0x56, 0x24, 0x7d, 0x9f, 0x09, 0xa9, 0x0e, 0x86,
0x9e, 0x37, 0x5b, 0x9c, 0x6d, 0x02, 0xd9, 0x8c,
0xc8, 0x50, 0x6a, 0xe2, 0x59, 0xf3, 0x16, 0x06,
0xea, 0xb2, 0x42, 0xb5, 0x58, 0xfe, 0xba, 0xd1,
0x81, 0x57, 0x1a, 0xef, 0xb2, 0x38, 0x88, 0x58,
0xf6, 0xaa, 0xc4, 0x2e, 0x8b, 0x5a, 0x27, 0xe4,
0xa5, 0xe8, 0xa4, 0xca, 0x67, 0x5c, 0xac, 0x72,
0x67, 0xc3, 0x6f, 0x13, 0xc3, 0x2d, 0x35, 0x79,
0xd7, 0x8a, 0xe7, 0xf5, 0xd4, 0x21, 0x30, 0x4a,
0xd5, 0xf6, 0xa3, 0xd9, 0x79, 0x56, 0xf2, 0x0f,
0x10, 0xf7, 0x7d, 0xd0, 0x51, 0x93, 0x2f, 0x47,
0xf8, 0x7d, 0x4b, 0x0a, 0x84, 0x55, 0x12, 0x0a,
0x7d, 0x4e, 0x3b, 0x1f, 0x2b, 0x2f, 0xfc, 0x28,
0xb3, 0x69, 0x34, 0xe1, 0x80, 0x80, 0xbb, 0xe2,
0xaf, 0xb9, 0xd6, 0x30, 0xf1, 0x1d, 0x54, 0x87,
0x23, 0x99, 0x9f, 0x51, 0x03, 0x4c, 0x45, 0x7d,
0x02, 0x65, 0x73, 0xab, 0xfd, 0xcf, 0x94, 0xcc,
0x0d, 0x3a, 0x60, 0xfd, 0x3c, 0x14, 0x2f, 0x16,
0x33, 0xa9, 0x21, 0x1f, 0xcb, 0x50, 0xb1, 0x8f,
0x03, 0xee, 0xa0, 0x66, 0xa9, 0x16, 0x79, 0x14,
+87
View File
@@ -0,0 +1,87 @@
/* Chen-Yu Tsai's regdb certificate */
0x30, 0x82, 0x02, 0xa7, 0x30, 0x82, 0x01, 0x8f,
0x02, 0x14, 0x61, 0xc0, 0x38, 0x65, 0x1a, 0xab,
0xdc, 0xf9, 0x4b, 0xd0, 0xac, 0x7f, 0xf0, 0x6c,
0x72, 0x48, 0xdb, 0x18, 0xc6, 0x00, 0x30, 0x0d,
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x0f, 0x31,
0x0d, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x04, 0x03,
0x0c, 0x04, 0x77, 0x65, 0x6e, 0x73, 0x30, 0x20,
0x17, 0x0d, 0x32, 0x33, 0x31, 0x32, 0x30, 0x31,
0x30, 0x37, 0x34, 0x31, 0x31, 0x34, 0x5a, 0x18,
0x0f, 0x32, 0x31, 0x32, 0x33, 0x31, 0x31, 0x30,
0x37, 0x30, 0x37, 0x34, 0x31, 0x31, 0x34, 0x5a,
0x30, 0x0f, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x03,
0x55, 0x04, 0x03, 0x0c, 0x04, 0x77, 0x65, 0x6e,
0x73, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06,
0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f,
0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01,
0x01, 0x00, 0xa9, 0x7a, 0x2c, 0x78, 0x4d, 0xa7,
0x19, 0x2d, 0x32, 0x52, 0xa0, 0x2e, 0x6c, 0xef,
0x88, 0x7f, 0x15, 0xc5, 0xb6, 0x69, 0x54, 0x16,
0x43, 0x14, 0x79, 0x53, 0xb7, 0xae, 0x88, 0xfe,
0xc0, 0xb7, 0x5d, 0x47, 0x8e, 0x1a, 0xe1, 0xef,
0xb3, 0x90, 0x86, 0xda, 0xd3, 0x64, 0x81, 0x1f,
0xce, 0x5d, 0x9e, 0x4b, 0x6e, 0x58, 0x02, 0x3e,
0xb2, 0x6f, 0x5e, 0x42, 0x47, 0x41, 0xf4, 0x2c,
0xb8, 0xa8, 0xd4, 0xaa, 0xc0, 0x0e, 0xe6, 0x48,
0xf0, 0xa8, 0xce, 0xcb, 0x08, 0xae, 0x37, 0xaf,
0xf6, 0x40, 0x39, 0xcb, 0x55, 0x6f, 0x5b, 0x4f,
0x85, 0x34, 0xe6, 0x69, 0x10, 0x50, 0x72, 0x5e,
0x4e, 0x9d, 0x4c, 0xba, 0x38, 0x36, 0x0d, 0xce,
0x73, 0x38, 0xd7, 0x27, 0x02, 0x2a, 0x79, 0x03,
0xe1, 0xac, 0xcf, 0xb0, 0x27, 0x85, 0x86, 0x93,
0x17, 0xab, 0xec, 0x42, 0x77, 0x37, 0x65, 0x8a,
0x44, 0xcb, 0xd6, 0x42, 0x93, 0x92, 0x13, 0xe3,
0x39, 0x45, 0xc5, 0x6e, 0x00, 0x4a, 0x7f, 0xcb,
0x42, 0x17, 0x2b, 0x25, 0x8c, 0xb8, 0x17, 0x3b,
0x15, 0x36, 0x59, 0xde, 0x42, 0xce, 0x21, 0xe6,
0xb6, 0xc7, 0x6e, 0x5e, 0x26, 0x1f, 0xf7, 0x8a,
0x57, 0x9e, 0xa5, 0x96, 0x72, 0xb7, 0x02, 0x32,
0xeb, 0x07, 0x2b, 0x73, 0xe2, 0x4f, 0x66, 0x58,
0x9a, 0xeb, 0x0f, 0x07, 0xb6, 0xab, 0x50, 0x8b,
0xc3, 0x8f, 0x17, 0xfa, 0x0a, 0x99, 0xc2, 0x16,
0x25, 0xbf, 0x2d, 0x6b, 0x1a, 0xaa, 0xe6, 0x3e,
0x5f, 0xeb, 0x6d, 0x9b, 0x5d, 0x4d, 0x42, 0x83,
0x2d, 0x39, 0xb8, 0xc9, 0xac, 0xdb, 0x3a, 0x91,
0x50, 0xdf, 0xbb, 0xb1, 0x76, 0x6d, 0x15, 0x73,
0xfd, 0xc6, 0xe6, 0x6b, 0x71, 0x9e, 0x67, 0x36,
0x22, 0x83, 0x79, 0xb1, 0xd6, 0xb8, 0x84, 0x52,
0xaf, 0x96, 0x5b, 0xc3, 0x63, 0x02, 0x4e, 0x78,
0x70, 0x57, 0x02, 0x03, 0x01, 0x00, 0x01, 0x30,
0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82,
0x01, 0x01, 0x00, 0x24, 0x28, 0xee, 0x22, 0x74,
0x7f, 0x7c, 0xfa, 0x6c, 0x1f, 0xb3, 0x18, 0xd1,
0xc2, 0x3d, 0x7d, 0x29, 0x42, 0x88, 0xad, 0x82,
0xa5, 0xb1, 0x8a, 0x05, 0xd0, 0xec, 0x5c, 0x91,
0x20, 0xf6, 0x82, 0xfd, 0xd5, 0x67, 0x60, 0x5f,
0x31, 0xf5, 0xbd, 0x88, 0x91, 0x70, 0xbd, 0xb8,
0xb9, 0x8c, 0x88, 0xfe, 0x53, 0xc9, 0x54, 0x9b,
0x43, 0xc4, 0x7a, 0x43, 0x74, 0x6b, 0xdd, 0xb0,
0xb1, 0x3b, 0x33, 0x45, 0x46, 0x78, 0xa3, 0x1c,
0xef, 0x54, 0x68, 0xf7, 0x85, 0x9c, 0xe4, 0x51,
0x6f, 0x06, 0xaf, 0x81, 0xdb, 0x2a, 0x7b, 0x7b,
0x6f, 0xa8, 0x9c, 0x67, 0xd8, 0xcb, 0xc9, 0x91,
0x40, 0x00, 0xae, 0xd9, 0xa1, 0x9f, 0xdd, 0xa6,
0x43, 0x0e, 0x28, 0x7b, 0xaa, 0x1b, 0xe9, 0x84,
0xdb, 0x76, 0x64, 0x42, 0x70, 0xc9, 0xc0, 0xeb,
0xae, 0x84, 0x11, 0x16, 0x68, 0x4e, 0x84, 0x9e,
0x7e, 0x92, 0x36, 0xee, 0x1c, 0x3b, 0x08, 0x63,
0xeb, 0x79, 0x84, 0x15, 0x08, 0x9d, 0xaf, 0xc8,
0x9a, 0xc7, 0x34, 0xd3, 0x94, 0x4b, 0xd1, 0x28,
0x97, 0xbe, 0xd1, 0x45, 0x75, 0xdc, 0x35, 0x62,
0xac, 0x1d, 0x1f, 0xb7, 0xb7, 0x15, 0x87, 0xc8,
0x98, 0xc0, 0x24, 0x31, 0x56, 0x8d, 0xed, 0xdb,
0x06, 0xc6, 0x46, 0xbf, 0x4b, 0x6d, 0xa6, 0xd5,
0xab, 0xcc, 0x60, 0xfc, 0xe5, 0x37, 0xb6, 0x53,
0x7d, 0x58, 0x95, 0xa9, 0x56, 0xc7, 0xf7, 0xee,
0xc3, 0xa0, 0x76, 0xf7, 0x65, 0x4d, 0x53, 0xfa,
0xff, 0x5f, 0x76, 0x33, 0x5a, 0x08, 0xfa, 0x86,
0x92, 0x5a, 0x13, 0xfa, 0x1a, 0xfc, 0xf2, 0x1b,
0x8c, 0x7f, 0x42, 0x6d, 0xb7, 0x7e, 0xb7, 0xb4,
0xf0, 0xc7, 0x83, 0xbb, 0xa2, 0x81, 0x03, 0x2d,
0xd4, 0x2a, 0x63, 0x3f, 0xf7, 0x31, 0x2e, 0x40,
0x33, 0x5c, 0x46, 0xbc, 0x9b, 0xc1, 0x05, 0xa5,
0x45, 0x4e, 0xc3,
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+656
View File
@@ -0,0 +1,656 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Wireless configuration interface internals.
*
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright (C) 2018-2026 Intel Corporation
*/
#ifndef __NET_WIRELESS_CORE_H
#define __NET_WIRELESS_CORE_H
#include <linux/list.h>
#include <linux/netdevice.h>
#include <linux/rbtree.h>
#include <linux/debugfs.h>
#include <linux/rfkill.h>
#include <linux/workqueue.h>
#include <linux/rtnetlink.h>
#include <net/genetlink.h>
#include <net/cfg80211.h>
#include "reg.h"
#define WIPHY_IDX_INVALID -1
struct cfg80211_scan_request_int {
struct cfg80211_scan_info info;
bool notified;
/* must be last - variable members */
struct cfg80211_scan_request req;
};
struct cfg80211_registered_device {
const struct cfg80211_ops *ops;
struct list_head list;
/* rfkill support */
struct rfkill_ops rfkill_ops;
struct work_struct rfkill_block;
/* ISO / IEC 3166 alpha2 for which this device is receiving
* country IEs on, this can help disregard country IEs from APs
* on the same alpha2 quickly. The alpha2 may differ from
* cfg80211_regdomain's alpha2 when an intersection has occurred.
* If the AP is reconfigured this can also be used to tell us if
* the country on the country IE changed. */
char country_ie_alpha2[2];
/*
* the driver requests the regulatory core to set this regulatory
* domain as the wiphy's. Only used for %REGULATORY_WIPHY_SELF_MANAGED
* devices using the regulatory_set_wiphy_regd() API
*/
const struct ieee80211_regdomain *requested_regd;
/* If a Country IE has been received this tells us the environment
* which its telling us its in. This defaults to ENVIRON_ANY */
enum environment_cap env;
/* wiphy index, internal only */
int wiphy_idx;
/* protected by RTNL */
int devlist_generation, wdev_id;
int opencount;
wait_queue_head_t dev_wait;
struct list_head beacon_registrations;
spinlock_t beacon_registrations_lock;
/* protected by RTNL only */
int num_running_ifaces;
int num_running_monitor_ifaces;
u64 cookie_counter;
/* BSSes/scanning */
spinlock_t bss_lock;
struct list_head bss_list;
struct rb_root bss_tree;
u32 bss_generation;
u32 bss_entries;
struct cfg80211_scan_request_int *scan_req; /* protected by RTNL */
struct cfg80211_scan_request_int *int_scan_req;
struct sk_buff *scan_msg;
struct list_head sched_scan_req_list;
time64_t suspend_at;
struct wiphy_work scan_done_wk;
struct genl_info *cur_cmd_info;
struct work_struct conn_work;
struct work_struct event_work;
struct delayed_work dfs_update_channels_wk;
struct wireless_dev *background_radar_wdev;
struct cfg80211_chan_def background_radar_chandef;
struct delayed_work background_cac_done_wk;
struct work_struct background_cac_abort_wk;
/* netlink port which started critical protocol (0 means not started) */
u32 crit_proto_nlportid;
struct cfg80211_coalesce *coalesce;
struct work_struct destroy_work;
struct wiphy_work sched_scan_stop_wk;
struct work_struct sched_scan_res_wk;
struct cfg80211_chan_def radar_chandef;
struct work_struct propagate_radar_detect_wk;
struct cfg80211_chan_def cac_done_chandef;
struct work_struct propagate_cac_done_wk;
struct work_struct mgmt_registrations_update_wk;
/* lock for all wdev lists */
spinlock_t mgmt_registrations_lock;
struct work_struct wiphy_work;
struct list_head wiphy_work_list;
/* protects the list above */
spinlock_t wiphy_work_lock;
bool suspended;
/* must be last because of the way we do wiphy_priv(),
* and it should at least be aligned to NETDEV_ALIGN */
struct wiphy wiphy __aligned(NETDEV_ALIGN);
};
static inline
struct cfg80211_registered_device *wiphy_to_rdev(struct wiphy *wiphy)
{
BUG_ON(!wiphy);
return container_of(wiphy, struct cfg80211_registered_device, wiphy);
}
static inline void
cfg80211_rdev_free_wowlan(struct cfg80211_registered_device *rdev)
{
#ifdef CONFIG_PM
int i;
if (!rdev->wiphy.wowlan_config)
return;
for (i = 0; i < rdev->wiphy.wowlan_config->n_patterns; i++)
kfree(rdev->wiphy.wowlan_config->patterns[i].mask);
kfree(rdev->wiphy.wowlan_config->patterns);
if (rdev->wiphy.wowlan_config->tcp &&
rdev->wiphy.wowlan_config->tcp->sock)
sock_release(rdev->wiphy.wowlan_config->tcp->sock);
kfree(rdev->wiphy.wowlan_config->tcp);
kfree(rdev->wiphy.wowlan_config->nd_config);
kfree(rdev->wiphy.wowlan_config);
#endif
}
static inline u64 cfg80211_assign_cookie(struct cfg80211_registered_device *rdev)
{
u64 r = ++rdev->cookie_counter;
if (WARN_ON(r == 0))
r = ++rdev->cookie_counter;
return r;
}
extern struct workqueue_struct *cfg80211_wq;
extern struct list_head cfg80211_rdev_list;
extern int cfg80211_rdev_list_generation;
/* This is constructed like this so it can be used in if/else */
static inline int for_each_rdev_check_rtnl(void)
{
ASSERT_RTNL();
return 0;
}
#define for_each_rdev(rdev) \
if (for_each_rdev_check_rtnl()) {} else \
list_for_each_entry(rdev, &cfg80211_rdev_list, list)
enum bss_source_type {
BSS_SOURCE_DIRECT = 0,
BSS_SOURCE_MBSSID,
BSS_SOURCE_STA_PROFILE,
};
struct cfg80211_internal_bss {
struct list_head list;
struct list_head hidden_list;
struct rb_node rbn;
unsigned long ts;
unsigned long refcount;
atomic_t hold;
/* time at the start of the reception of the first octet of the
* timestamp field of the last beacon/probe received for this BSS.
* The time is the TSF of the BSS specified by %parent_bssid.
*/
u64 parent_tsf;
/* the BSS according to which %parent_tsf is set. This is set to
* the BSS that the interface that requested the scan was connected to
* when the beacon/probe was received.
*/
u8 parent_bssid[ETH_ALEN] __aligned(2);
enum bss_source_type bss_source;
/* must be last because of priv member */
struct cfg80211_bss pub;
};
static inline struct cfg80211_internal_bss *bss_from_pub(struct cfg80211_bss *pub)
{
return container_of(pub, struct cfg80211_internal_bss, pub);
}
static inline void cfg80211_hold_bss(struct cfg80211_internal_bss *bss)
{
atomic_inc(&bss->hold);
if (bss->pub.transmitted_bss) {
bss = container_of(bss->pub.transmitted_bss,
struct cfg80211_internal_bss, pub);
atomic_inc(&bss->hold);
}
}
static inline void cfg80211_unhold_bss(struct cfg80211_internal_bss *bss)
{
int r = atomic_dec_return(&bss->hold);
WARN_ON(r < 0);
if (bss->pub.transmitted_bss) {
bss = container_of(bss->pub.transmitted_bss,
struct cfg80211_internal_bss, pub);
r = atomic_dec_return(&bss->hold);
WARN_ON(r < 0);
}
}
struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx);
int get_wiphy_idx(struct wiphy *wiphy);
struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx);
int cfg80211_switch_netns(struct cfg80211_registered_device *rdev,
struct net *net);
void cfg80211_init_wdev(struct wireless_dev *wdev);
void cfg80211_register_wdev(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev);
static inline bool cfg80211_has_monitors_only(struct cfg80211_registered_device *rdev)
{
lockdep_assert_held(&rdev->wiphy.mtx);
return rdev->num_running_ifaces == rdev->num_running_monitor_ifaces &&
rdev->num_running_ifaces > 0;
}
enum cfg80211_event_type {
EVENT_CONNECT_RESULT,
EVENT_ROAMED,
EVENT_DISCONNECTED,
EVENT_IBSS_JOINED,
EVENT_STOPPED,
EVENT_PORT_AUTHORIZED,
};
struct cfg80211_event {
struct list_head list;
enum cfg80211_event_type type;
union {
struct cfg80211_connect_resp_params cr;
struct cfg80211_roam_info rm;
struct {
const u8 *ie;
size_t ie_len;
u16 reason;
bool locally_generated;
} dc;
struct {
u8 bssid[ETH_ALEN];
struct ieee80211_channel *channel;
} ij;
struct {
u8 peer_addr[ETH_ALEN];
const u8 *td_bitmap;
u8 td_bitmap_len;
} pa;
};
int link_id;
};
struct cfg80211_cached_keys {
struct key_params params[4];
u8 data[4][WLAN_KEY_LEN_WEP104];
int def;
};
struct cfg80211_beacon_registration {
struct list_head list;
u32 nlportid;
};
struct cfg80211_cqm_config {
struct rcu_head rcu_head;
u32 rssi_hyst;
s32 last_rssi_event_value;
enum nl80211_cqm_rssi_threshold_event last_rssi_event_type;
bool use_range_api;
int n_rssi_thresholds;
s32 rssi_thresholds[] __counted_by(n_rssi_thresholds);
};
void cfg80211_cqm_rssi_notify_work(struct wiphy *wiphy,
struct wiphy_work *work);
void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev);
void cfg80211_close_dependents(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev);
/* free object */
void cfg80211_dev_free(struct cfg80211_registered_device *rdev);
int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
char *newname);
void ieee80211_set_bitrate_flags(struct wiphy *wiphy);
void cfg80211_bss_expire(struct cfg80211_registered_device *rdev);
void cfg80211_bss_age(struct cfg80211_registered_device *rdev,
unsigned long age_secs);
void cfg80211_update_assoc_bss_entry(struct wireless_dev *wdev,
unsigned int link,
struct ieee80211_channel *channel);
/* IBSS */
int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct cfg80211_ibss_params *params,
struct cfg80211_cached_keys *connkeys);
void cfg80211_clear_ibss(struct net_device *dev, bool nowext);
int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
struct net_device *dev, bool nowext);
void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
struct ieee80211_channel *channel);
int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev);
/* mesh */
extern const struct mesh_config default_mesh_config;
extern const struct mesh_setup default_mesh_setup;
int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct mesh_setup *setup,
const struct mesh_config *conf);
int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,
struct net_device *dev);
int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
struct cfg80211_chan_def *chandef);
/* OCB */
int cfg80211_join_ocb(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct ocb_setup *setup);
int cfg80211_leave_ocb(struct cfg80211_registered_device *rdev,
struct net_device *dev);
/* AP */
int cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
struct net_device *dev, int link,
bool notify);
/* MLME */
int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct cfg80211_auth_request *req);
int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct cfg80211_assoc_request *req,
struct netlink_ext_ack *extack);
int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
struct net_device *dev, const u8 *bssid,
const u8 *ie, int ie_len, u16 reason,
bool local_state_change);
int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
struct net_device *dev, const u8 *ap_addr,
const u8 *ie, int ie_len, u16 reason,
bool local_state_change);
void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
struct net_device *dev);
int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_pid,
u16 frame_type, const u8 *match_data,
int match_len, bool multicast_rx,
struct netlink_ext_ack *extack);
void cfg80211_mgmt_registrations_update_wk(struct work_struct *wk);
void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid);
void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev);
int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
struct cfg80211_mgmt_tx_params *params,
u64 *cookie);
void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa,
const struct ieee80211_ht_cap *ht_capa_mask);
void cfg80211_oper_and_vht_capa(struct ieee80211_vht_cap *vht_capa,
const struct ieee80211_vht_cap *vht_capa_mask);
/* SME events */
int cfg80211_connect(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct cfg80211_connect_params *connect,
struct cfg80211_cached_keys *connkeys,
const u8 *prev_bssid);
void __cfg80211_connect_result(struct net_device *dev,
struct cfg80211_connect_resp_params *params,
bool wextev);
void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
size_t ie_len, u16 reason, bool from_ap);
int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
struct net_device *dev, u16 reason,
bool wextev);
void __cfg80211_roamed(struct wireless_dev *wdev,
struct cfg80211_roam_info *info);
void __cfg80211_port_authorized(struct wireless_dev *wdev, const u8 *peer_addr,
const u8 *td_bitmap, u8 td_bitmap_len);
int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev);
void cfg80211_autodisconnect_wk(struct wiphy *wiphy, struct wiphy_work *work);
/* SME implementation */
void cfg80211_conn_work(struct work_struct *work);
void cfg80211_sme_scan_done(struct net_device *dev);
bool cfg80211_sme_rx_assoc_resp(struct wireless_dev *wdev, u16 status);
void cfg80211_sme_rx_auth(struct wireless_dev *wdev, const u8 *buf, size_t len);
void cfg80211_sme_disassoc(struct wireless_dev *wdev);
void cfg80211_sme_deauth(struct wireless_dev *wdev);
void cfg80211_sme_auth_timeout(struct wireless_dev *wdev);
void cfg80211_sme_assoc_timeout(struct wireless_dev *wdev);
void cfg80211_sme_abandon_assoc(struct wireless_dev *wdev);
/* internal helpers */
bool cfg80211_supported_cipher_suite(struct wiphy *wiphy, u32 cipher);
bool cfg80211_valid_key_idx(struct cfg80211_registered_device *rdev,
int key_idx, bool pairwise);
int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
struct key_params *params, int key_idx,
bool pairwise, const u8 *mac_addr);
void __cfg80211_scan_done(struct wiphy *wiphy, struct wiphy_work *wk);
void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev,
bool send_message);
void cfg80211_add_sched_scan_req(struct cfg80211_registered_device *rdev,
struct cfg80211_sched_scan_request *req);
int cfg80211_sched_scan_req_possible(struct cfg80211_registered_device *rdev,
bool want_multi);
void cfg80211_sched_scan_results_wk(struct work_struct *work);
int cfg80211_stop_sched_scan_req(struct cfg80211_registered_device *rdev,
struct cfg80211_sched_scan_request *req,
bool driver_initiated);
int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
u64 reqid, bool driver_initiated);
void cfg80211_upload_connect_keys(struct wireless_dev *wdev);
int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
struct net_device *dev, enum nl80211_iftype ntype,
struct vif_params *params);
void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev);
void cfg80211_process_wiphy_works(struct cfg80211_registered_device *rdev,
struct wiphy_work *end);
void cfg80211_process_wdev_events(struct wireless_dev *wdev);
bool cfg80211_does_bw_fit_range(const struct ieee80211_freq_range *freq_range,
u32 center_freq_khz, u32 bw_khz);
int cfg80211_scan(struct cfg80211_registered_device *rdev);
extern struct work_struct cfg80211_disconnect_work;
#define NL80211_BSS_USE_FOR_ALL (NL80211_BSS_USE_FOR_NORMAL | \
NL80211_BSS_USE_FOR_MLD_LINK)
void cfg80211_set_dfs_state(struct wiphy *wiphy,
const struct cfg80211_chan_def *chandef,
enum nl80211_dfs_state dfs_state);
void cfg80211_set_cac_state(struct wiphy *wiphy,
const struct cfg80211_chan_def *chandef,
bool cac_ongoing);
void cfg80211_dfs_channels_update_work(struct work_struct *work);
void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev);
int
cfg80211_start_background_radar_detection(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
struct cfg80211_chan_def *chandef);
void cfg80211_stop_radar_detection(struct wireless_dev *wdev);
void cfg80211_stop_background_radar_detection(struct wireless_dev *wdev);
void cfg80211_background_cac_done_wk(struct work_struct *work);
void cfg80211_background_cac_abort_wk(struct work_struct *work);
bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy,
struct ieee80211_channel *chan);
bool cfg80211_beaconing_iface_active(struct wireless_dev *wdev);
bool cfg80211_is_sub_chan(struct cfg80211_chan_def *chandef,
struct ieee80211_channel *chan,
bool primary_only);
bool cfg80211_wdev_on_sub_chan(struct wireless_dev *wdev,
struct ieee80211_channel *chan,
bool primary_only);
bool _cfg80211_chandef_usable(struct wiphy *wiphy,
const struct cfg80211_chan_def *chandef,
u32 prohibited_flags,
u32 permitting_flags);
static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
{
unsigned long end = jiffies;
if (end >= start)
return jiffies_to_msecs(end - start);
return jiffies_to_msecs(end + (ULONG_MAX - start) + 1);
}
int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct cfg80211_chan_def *chandef);
int ieee80211_get_ratemask(struct ieee80211_supported_band *sband,
const u8 *rates, unsigned int n_rates,
u32 *mask);
int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
enum nl80211_iftype iftype, u32 beacon_int);
void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,
enum nl80211_iftype iftype, int num);
void cfg80211_leave_locked(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev, int link_id);
void cfg80211_leave(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
int link_id);
void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev);
void cfg80211_stop_nan(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev);
void cfg80211_stop_pd(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev);
int cfg80211_nan_set_local_schedule(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
struct cfg80211_nan_local_sched *sched);
struct cfg80211_internal_bss *
cfg80211_bss_update(struct cfg80211_registered_device *rdev,
struct cfg80211_internal_bss *tmp,
bool signal_valid, unsigned long ts);
enum ieee80211_ap_reg_power
cfg80211_get_6ghz_power_type(const u8 *elems, size_t elems_len,
u32 client_flags);
#ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS
#define CFG80211_DEV_WARN_ON(cond) WARN_ON(cond)
#else
/*
* Trick to enable using it as a condition,
* and also not give a warning when it's
* not used that way.
*/
#define CFG80211_DEV_WARN_ON(cond) ({bool __r = (cond); __r; })
#endif
void cfg80211_release_pmsr(struct wireless_dev *wdev, u32 portid);
void cfg80211_pmsr_wdev_down(struct wireless_dev *wdev);
void cfg80211_pmsr_free_wk(struct wiphy *wiphy, struct wiphy_work *work);
void cfg80211_remove_link(struct wireless_dev *wdev, unsigned int link_id);
void cfg80211_remove_links(struct wireless_dev *wdev);
int cfg80211_remove_virtual_intf(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev);
void cfg80211_wdev_release_link_bsses(struct wireless_dev *wdev, u16 link_mask);
int cfg80211_assoc_ml_reconf(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct cfg80211_ml_reconf_req *req);
/**
* struct cfg80211_colocated_ap - colocated AP information
*
* @list: linked list to all colocated APs
* @bssid: BSSID of the reported AP
* @ssid: SSID of the reported AP
* @ssid_len: length of the ssid
* @center_freq: frequency the reported AP is on
* @unsolicited_probe: the reported AP is part of an ESS, where all the APs
* that operate in the same channel as the reported AP and that might be
* detected by a STA receiving this frame, are transmitting unsolicited
* Probe Response frames every 20 TUs
* @oct_recommended: OCT is recommended to exchange MMPDUs with the reported AP
* @same_ssid: the reported AP has the same SSID as the reporting AP
* @multi_bss: the reported AP is part of a multiple BSSID set
* @transmitted_bssid: the reported AP is the transmitting BSSID
* @colocated_ess: all the APs that share the same ESS as the reported AP are
* colocated and can be discovered via legacy bands.
* @short_ssid_valid: short_ssid is valid and can be used
* @short_ssid: the short SSID for this SSID
* @psd_20: The 20MHz PSD EIRP of the primary 20MHz channel for the reported AP
*/
struct cfg80211_colocated_ap {
struct list_head list;
u8 bssid[ETH_ALEN];
u8 ssid[IEEE80211_MAX_SSID_LEN];
size_t ssid_len;
u32 short_ssid;
u32 center_freq;
u8 unsolicited_probe:1,
oct_recommended:1,
same_ssid:1,
multi_bss:1,
transmitted_bssid:1,
colocated_ess:1,
short_ssid_valid:1;
s8 psd_20;
};
#if IS_ENABLED(CONFIG_CFG80211_KUNIT_TEST)
#define EXPORT_SYMBOL_IF_CFG80211_KUNIT(sym) EXPORT_SYMBOL_IF_KUNIT(sym)
#define VISIBLE_IF_CFG80211_KUNIT
void cfg80211_free_coloc_ap_list(struct list_head *coloc_ap_list);
int cfg80211_parse_colocated_ap(const struct cfg80211_bss_ies *ies,
struct list_head *list);
size_t cfg80211_gen_new_ie(const u8 *ie, size_t ielen,
const u8 *subie, size_t subie_len,
u8 *new_ie, size_t new_ie_len);
#else
#define EXPORT_SYMBOL_IF_CFG80211_KUNIT(sym)
#define VISIBLE_IF_CFG80211_KUNIT static
#endif /* IS_ENABLED(CONFIG_CFG80211_KUNIT_TEST) */
#endif /* __NET_WIRELESS_CORE_H */
+304
View File
@@ -0,0 +1,304 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* cfg80211 debugfs
*
* Copyright 2009 Luis R. Rodriguez <lrodriguez@atheros.com>
* Copyright 2007 Johannes Berg <johannes@sipsolutions.net>
* Copyright (C) 2023 Intel Corporation
*/
#include <linux/slab.h>
#include "core.h"
#include "debugfs.h"
#define DEBUGFS_READONLY_FILE(name, buflen, fmt, value...) \
static ssize_t name## _read(struct file *file, char __user *userbuf, \
size_t count, loff_t *ppos) \
{ \
struct wiphy *wiphy = file->private_data; \
char buf[buflen]; \
int res; \
\
res = scnprintf(buf, buflen, fmt "\n", ##value); \
return simple_read_from_buffer(userbuf, count, ppos, buf, res); \
} \
\
static const struct file_operations name## _ops = { \
.read = name## _read, \
.open = simple_open, \
.llseek = generic_file_llseek, \
}
#define DEBUGFS_RADIO_READONLY_FILE(name, buflen, fmt, value...) \
static ssize_t name## _read(struct file *file, char __user *userbuf, \
size_t count, loff_t *ppos) \
{ \
struct wiphy_radio_cfg *radio_cfg = file->private_data; \
char buf[buflen]; \
int res; \
\
res = scnprintf(buf, buflen, fmt "\n", ##value); \
return simple_read_from_buffer(userbuf, count, ppos, buf, res); \
} \
\
static const struct file_operations name## _ops = { \
.read = name## _read, \
.open = simple_open, \
.llseek = generic_file_llseek, \
}
DEBUGFS_READONLY_FILE(rts_threshold, 20, "%d",
wiphy->rts_threshold);
DEBUGFS_READONLY_FILE(fragmentation_threshold, 20, "%d",
wiphy->frag_threshold);
DEBUGFS_READONLY_FILE(short_retry_limit, 20, "%d",
wiphy->retry_short);
DEBUGFS_READONLY_FILE(long_retry_limit, 20, "%d",
wiphy->retry_long);
DEBUGFS_RADIO_READONLY_FILE(radio_rts_threshold, 20, "%d",
radio_cfg->rts_threshold);
static int ht_print_chan(struct ieee80211_channel *chan,
char *buf, int buf_size, int offset)
{
if (WARN_ON(offset > buf_size))
return 0;
if (chan->flags & IEEE80211_CHAN_DISABLED)
return scnprintf(buf + offset,
buf_size - offset,
"%d Disabled\n",
chan->center_freq);
return scnprintf(buf + offset,
buf_size - offset,
"%d HT40 %c%c\n",
chan->center_freq,
(chan->flags & IEEE80211_CHAN_NO_HT40MINUS) ?
' ' : '-',
(chan->flags & IEEE80211_CHAN_NO_HT40PLUS) ?
' ' : '+');
}
static ssize_t ht40allow_map_read(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
{
struct wiphy *wiphy = file->private_data;
char *buf;
unsigned int offset = 0, buf_size = PAGE_SIZE, i;
enum nl80211_band band;
struct ieee80211_supported_band *sband;
ssize_t r;
buf = kzalloc(buf_size, GFP_KERNEL);
if (!buf)
return -ENOMEM;
for (band = 0; band < NUM_NL80211_BANDS; band++) {
sband = wiphy->bands[band];
if (!sband)
continue;
for (i = 0; i < sband->n_channels; i++)
offset += ht_print_chan(&sband->channels[i],
buf, buf_size, offset);
}
r = simple_read_from_buffer(user_buf, count, ppos, buf, offset);
kfree(buf);
return r;
}
static const struct file_operations ht40allow_map_ops = {
.read = ht40allow_map_read,
.open = simple_open,
.llseek = default_llseek,
};
#define DEBUGFS_ADD(name) \
debugfs_create_file(#name, 0444, phyd, &rdev->wiphy, &name## _ops)
#define DEBUGFS_RADIO_ADD(name, radio_idx) \
debugfs_create_file(#name, 0444, radiod, \
&rdev->wiphy.radio_cfg[radio_idx], \
&name## _ops)
void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev)
{
struct dentry *phyd = rdev->wiphy.debugfsdir;
struct dentry *radiod;
u8 i;
DEBUGFS_ADD(rts_threshold);
DEBUGFS_ADD(fragmentation_threshold);
DEBUGFS_ADD(short_retry_limit);
DEBUGFS_ADD(long_retry_limit);
DEBUGFS_ADD(ht40allow_map);
for (i = 0; i < rdev->wiphy.n_radio; i++) {
radiod = rdev->wiphy.radio_cfg[i].radio_debugfsdir;
DEBUGFS_RADIO_ADD(radio_rts_threshold, i);
}
}
struct debugfs_read_work {
struct wiphy_work work;
ssize_t (*handler)(struct wiphy *wiphy,
struct file *file,
char *buf,
size_t count,
void *data);
struct wiphy *wiphy;
struct file *file;
char *buf;
size_t bufsize;
void *data;
ssize_t ret;
struct completion completion;
};
static void wiphy_locked_debugfs_read_work(struct wiphy *wiphy,
struct wiphy_work *work)
{
struct debugfs_read_work *w = container_of(work, typeof(*w), work);
w->ret = w->handler(w->wiphy, w->file, w->buf, w->bufsize, w->data);
complete(&w->completion);
}
static void wiphy_locked_debugfs_read_cancel(struct dentry *dentry,
void *data)
{
struct debugfs_read_work *w = data;
wiphy_work_cancel(w->wiphy, &w->work);
complete(&w->completion);
}
ssize_t wiphy_locked_debugfs_read(struct wiphy *wiphy, struct file *file,
char *buf, size_t bufsize,
char __user *userbuf, size_t count,
loff_t *ppos,
ssize_t (*handler)(struct wiphy *wiphy,
struct file *file,
char *buf,
size_t bufsize,
void *data),
void *data)
{
struct debugfs_read_work work = {
.handler = handler,
.wiphy = wiphy,
.file = file,
.buf = buf,
.bufsize = bufsize,
.data = data,
.ret = -ENODEV,
.completion = COMPLETION_INITIALIZER_ONSTACK(work.completion),
};
struct debugfs_cancellation cancellation = {
.cancel = wiphy_locked_debugfs_read_cancel,
.cancel_data = &work,
};
/* don't leak stack data or whatever */
memset(buf, 0, bufsize);
wiphy_work_init(&work.work, wiphy_locked_debugfs_read_work);
wiphy_work_queue(wiphy, &work.work);
debugfs_enter_cancellation(file, &cancellation);
wait_for_completion(&work.completion);
debugfs_leave_cancellation(file, &cancellation);
if (work.ret < 0)
return work.ret;
if (WARN_ON(work.ret > bufsize))
return -EINVAL;
return simple_read_from_buffer(userbuf, count, ppos, buf, work.ret);
}
EXPORT_SYMBOL_GPL(wiphy_locked_debugfs_read);
struct debugfs_write_work {
struct wiphy_work work;
ssize_t (*handler)(struct wiphy *wiphy,
struct file *file,
char *buf,
size_t count,
void *data);
struct wiphy *wiphy;
struct file *file;
char *buf;
size_t count;
void *data;
ssize_t ret;
struct completion completion;
};
static void wiphy_locked_debugfs_write_work(struct wiphy *wiphy,
struct wiphy_work *work)
{
struct debugfs_write_work *w = container_of(work, typeof(*w), work);
w->ret = w->handler(w->wiphy, w->file, w->buf, w->count, w->data);
complete(&w->completion);
}
static void wiphy_locked_debugfs_write_cancel(struct dentry *dentry,
void *data)
{
struct debugfs_write_work *w = data;
wiphy_work_cancel(w->wiphy, &w->work);
complete(&w->completion);
}
ssize_t wiphy_locked_debugfs_write(struct wiphy *wiphy,
struct file *file, char *buf, size_t bufsize,
const char __user *userbuf, size_t count,
ssize_t (*handler)(struct wiphy *wiphy,
struct file *file,
char *buf,
size_t count,
void *data),
void *data)
{
struct debugfs_write_work work = {
.handler = handler,
.wiphy = wiphy,
.file = file,
.buf = buf,
.count = count,
.data = data,
.ret = -ENODEV,
.completion = COMPLETION_INITIALIZER_ONSTACK(work.completion),
};
struct debugfs_cancellation cancellation = {
.cancel = wiphy_locked_debugfs_write_cancel,
.cancel_data = &work,
};
/* mostly used for strings so enforce NUL-termination for safety */
if (count >= bufsize)
return -EINVAL;
memset(buf, 0, bufsize);
if (copy_from_user(buf, userbuf, count))
return -EFAULT;
wiphy_work_init(&work.work, wiphy_locked_debugfs_write_work);
wiphy_work_queue(wiphy, &work.work);
debugfs_enter_cancellation(file, &cancellation);
wait_for_completion(&work.completion);
debugfs_leave_cancellation(file, &cancellation);
return work.ret;
}
EXPORT_SYMBOL_GPL(wiphy_locked_debugfs_write);
+12
View File
@@ -0,0 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __CFG80211_DEBUGFS_H
#define __CFG80211_DEBUGFS_H
#ifdef CONFIG_CFG80211_DEBUGFS
void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev);
#else
static inline
void cfg80211_debugfs_rdev_add(struct cfg80211_registered_device *rdev) {}
#endif
#endif /* __CFG80211_DEBUGFS_H */
+29
View File
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/utsname.h>
#include <net/cfg80211.h>
#include "core.h"
#include "rdev-ops.h"
void cfg80211_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct device *pdev = wiphy_dev(wdev->wiphy);
if (pdev->driver)
strscpy(info->driver, pdev->driver->name,
sizeof(info->driver));
else
strscpy(info->driver, "N/A", sizeof(info->driver));
strscpy(info->version, init_utsname()->release, sizeof(info->version));
if (wdev->wiphy->fw_version[0])
strscpy(info->fw_version, wdev->wiphy->fw_version,
sizeof(info->fw_version));
else
strscpy(info->fw_version, "N/A", sizeof(info->fw_version));
strscpy(info->bus_info, dev_name(pdev),
sizeof(info->bus_info));
}
EXPORT_SYMBOL(cfg80211_get_drvinfo);
+496
View File
@@ -0,0 +1,496 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Some IBSS support code for cfg80211.
*
* Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
* Copyright (C) 2020-2026 Intel Corporation
*/
#include <linux/etherdevice.h>
#include <linux/if_arp.h>
#include <linux/slab.h>
#include <linux/export.h>
#include <net/cfg80211.h>
#include "wext-compat.h"
#include "nl80211.h"
#include "rdev-ops.h"
void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
struct ieee80211_channel *channel)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_bss *bss;
#ifdef CONFIG_CFG80211_WEXT
union iwreq_data wrqu;
#endif
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
return;
if (!wdev->u.ibss.ssid_len)
return;
bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, NULL, 0,
IEEE80211_BSS_TYPE_IBSS, IEEE80211_PRIVACY_ANY);
if (WARN_ON(!bss))
return;
if (wdev->u.ibss.current_bss) {
cfg80211_unhold_bss(wdev->u.ibss.current_bss);
cfg80211_put_bss(wdev->wiphy, &wdev->u.ibss.current_bss->pub);
}
cfg80211_hold_bss(bss_from_pub(bss));
wdev->u.ibss.current_bss = bss_from_pub(bss);
cfg80211_upload_connect_keys(wdev);
nl80211_send_ibss_bssid(wiphy_to_rdev(wdev->wiphy), dev, bssid,
GFP_KERNEL);
#ifdef CONFIG_CFG80211_WEXT
memset(&wrqu, 0, sizeof(wrqu));
memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
#endif
}
void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid,
struct ieee80211_channel *channel, gfp_t gfp)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
struct cfg80211_event *ev;
unsigned long flags;
trace_cfg80211_ibss_joined(dev, bssid, channel);
if (WARN_ON(!channel))
return;
ev = kzalloc_obj(*ev, gfp);
if (!ev)
return;
ev->type = EVENT_IBSS_JOINED;
memcpy(ev->ij.bssid, bssid, ETH_ALEN);
ev->ij.channel = channel;
spin_lock_irqsave(&wdev->event_lock, flags);
list_add_tail(&ev->list, &wdev->event_list);
spin_unlock_irqrestore(&wdev->event_lock, flags);
queue_work(cfg80211_wq, &rdev->event_work);
}
EXPORT_SYMBOL(cfg80211_ibss_joined);
int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct cfg80211_ibss_params *params,
struct cfg80211_cached_keys *connkeys)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
int err;
lockdep_assert_held(&rdev->wiphy.mtx);
if (wdev->links[0].cac_started)
return -EBUSY;
if (wdev->u.ibss.ssid_len)
return -EALREADY;
if (!params->basic_rates) {
/*
* If no rates were explicitly configured,
* use the mandatory rate set for 11b or
* 11a for maximum compatibility.
*/
struct ieee80211_supported_band *sband;
enum nl80211_band band;
u32 flag;
int j;
band = params->chandef.chan->band;
if (band == NL80211_BAND_5GHZ ||
band == NL80211_BAND_6GHZ)
flag = IEEE80211_RATE_MANDATORY_A;
else
flag = IEEE80211_RATE_MANDATORY_B;
sband = rdev->wiphy.bands[band];
for (j = 0; j < sband->n_bitrates; j++) {
if (sband->bitrates[j].flags & flag)
params->basic_rates |= BIT(j);
}
}
if (WARN_ON(connkeys && connkeys->def < 0))
return -EINVAL;
if (WARN_ON(wdev->connect_keys))
kfree_sensitive(wdev->connect_keys);
wdev->connect_keys = connkeys;
wdev->u.ibss.chandef = params->chandef;
if (connkeys) {
params->wep_keys = connkeys->params;
params->wep_tx_key = connkeys->def;
}
#ifdef CONFIG_CFG80211_WEXT
wdev->wext.ibss.chandef = params->chandef;
#endif
err = rdev_join_ibss(rdev, dev, params);
if (err) {
wdev->connect_keys = NULL;
return err;
}
memcpy(wdev->u.ibss.ssid, params->ssid, params->ssid_len);
wdev->u.ibss.ssid_len = params->ssid_len;
return 0;
}
void cfg80211_clear_ibss(struct net_device *dev, bool nowext)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
int i;
lockdep_assert_wiphy(wdev->wiphy);
kfree_sensitive(wdev->connect_keys);
wdev->connect_keys = NULL;
rdev_set_qos_map(rdev, dev, NULL);
/*
* Delete all the keys ... pairwise keys can't really
* exist any more anyway, but default keys might.
*/
if (rdev->ops->del_key)
for (i = 0; i < 6; i++)
rdev_del_key(rdev, wdev, -1, i, false, NULL);
if (wdev->u.ibss.current_bss) {
cfg80211_unhold_bss(wdev->u.ibss.current_bss);
cfg80211_put_bss(wdev->wiphy, &wdev->u.ibss.current_bss->pub);
}
wdev->u.ibss.current_bss = NULL;
wdev->u.ibss.ssid_len = 0;
memset(&wdev->u.ibss.chandef, 0, sizeof(wdev->u.ibss.chandef));
#ifdef CONFIG_CFG80211_WEXT
if (!nowext)
wdev->wext.ibss.ssid_len = 0;
#endif
cfg80211_sched_dfs_chan_update(rdev);
}
int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
struct net_device *dev, bool nowext)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
int err;
lockdep_assert_wiphy(wdev->wiphy);
if (!wdev->u.ibss.ssid_len)
return -ENOLINK;
err = rdev_leave_ibss(rdev, dev);
if (err)
return err;
wdev->conn_owner_nlportid = 0;
cfg80211_clear_ibss(dev, nowext);
return 0;
}
#ifdef CONFIG_CFG80211_WEXT
int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev)
{
struct cfg80211_cached_keys *ck = NULL;
enum nl80211_band band;
int i, err;
lockdep_assert_wiphy(wdev->wiphy);
if (!wdev->wext.ibss.beacon_interval)
wdev->wext.ibss.beacon_interval = 100;
/* try to find an IBSS channel if none requested ... */
if (!wdev->wext.ibss.chandef.chan) {
struct ieee80211_channel *new_chan = NULL;
for (band = 0; band < NUM_NL80211_BANDS; band++) {
struct ieee80211_supported_band *sband;
struct ieee80211_channel *chan;
sband = rdev->wiphy.bands[band];
if (!sband)
continue;
for (i = 0; i < sband->n_channels; i++) {
chan = &sband->channels[i];
if (chan->flags & IEEE80211_CHAN_NO_IR)
continue;
if (chan->flags & IEEE80211_CHAN_DISABLED)
continue;
new_chan = chan;
break;
}
if (new_chan)
break;
}
if (!new_chan)
return -EINVAL;
cfg80211_chandef_create(&wdev->wext.ibss.chandef, new_chan,
NL80211_CHAN_NO_HT);
}
/* don't join -- SSID is not there */
if (!wdev->wext.ibss.ssid_len)
return 0;
if (!netif_running(wdev->netdev))
return 0;
if (wdev->wext.keys)
wdev->wext.keys->def = wdev->wext.default_key;
wdev->wext.ibss.privacy = wdev->wext.default_key != -1;
if (wdev->wext.keys && wdev->wext.keys->def != -1) {
ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL);
if (!ck)
return -ENOMEM;
for (i = 0; i < 4; i++)
ck->params[i].key = ck->data[i];
}
err = __cfg80211_join_ibss(rdev, wdev->netdev,
&wdev->wext.ibss, ck);
if (err)
kfree(ck);
return err;
}
int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
struct iw_request_info *info,
struct iw_freq *wextfreq, char *extra)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
struct ieee80211_channel *chan = NULL;
int err, freq;
/* call only for ibss! */
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
return -EINVAL;
if (!rdev->ops->join_ibss)
return -EOPNOTSUPP;
freq = cfg80211_wext_freq(wextfreq);
if (freq < 0)
return freq;
if (freq) {
chan = ieee80211_get_channel(wdev->wiphy, freq);
if (!chan)
return -EINVAL;
if (chan->flags & IEEE80211_CHAN_NO_IR ||
chan->flags & IEEE80211_CHAN_DISABLED)
return -EINVAL;
}
if (wdev->wext.ibss.chandef.chan == chan)
return 0;
err = 0;
if (wdev->u.ibss.ssid_len)
err = cfg80211_leave_ibss(rdev, dev, true);
if (err)
return err;
if (chan) {
cfg80211_chandef_create(&wdev->wext.ibss.chandef, chan,
NL80211_CHAN_NO_HT);
wdev->wext.ibss.channel_fixed = true;
} else {
/* cfg80211_ibss_wext_join will pick one if needed */
wdev->wext.ibss.channel_fixed = false;
}
return cfg80211_ibss_wext_join(rdev, wdev);
}
int cfg80211_ibss_wext_giwfreq(struct net_device *dev,
struct iw_request_info *info,
struct iw_freq *freq, char *extra)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct ieee80211_channel *chan = NULL;
/* call only for ibss! */
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
return -EINVAL;
if (wdev->u.ibss.current_bss)
chan = wdev->u.ibss.current_bss->pub.channel;
else if (wdev->wext.ibss.chandef.chan)
chan = wdev->wext.ibss.chandef.chan;
if (chan) {
freq->m = chan->center_freq;
freq->e = 6;
return 0;
}
/* no channel if not joining */
return -EINVAL;
}
int cfg80211_ibss_wext_siwessid(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *data, char *ssid)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
size_t len = data->length;
int err;
/* call only for ibss! */
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
return -EINVAL;
if (!rdev->ops->join_ibss)
return -EOPNOTSUPP;
err = 0;
if (wdev->u.ibss.ssid_len)
err = cfg80211_leave_ibss(rdev, dev, true);
if (err)
return err;
/* iwconfig uses nul termination in SSID.. */
if (len > 0 && ssid[len - 1] == '\0')
len--;
memcpy(wdev->u.ibss.ssid, ssid, len);
wdev->wext.ibss.ssid = wdev->u.ibss.ssid;
wdev->wext.ibss.ssid_len = len;
return cfg80211_ibss_wext_join(rdev, wdev);
}
int cfg80211_ibss_wext_giwessid(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *data, char *ssid)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
/* call only for ibss! */
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
return -EINVAL;
data->flags = 0;
if (wdev->u.ibss.ssid_len) {
data->flags = 1;
data->length = wdev->u.ibss.ssid_len;
memcpy(ssid, wdev->u.ibss.ssid, data->length);
} else if (wdev->wext.ibss.ssid && wdev->wext.ibss.ssid_len) {
data->flags = 1;
data->length = wdev->wext.ibss.ssid_len;
memcpy(ssid, wdev->wext.ibss.ssid, data->length);
}
return 0;
}
int cfg80211_ibss_wext_siwap(struct net_device *dev,
struct iw_request_info *info,
struct sockaddr *ap_addr, char *extra)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
u8 *bssid = ap_addr->sa_data;
int err;
/* call only for ibss! */
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
return -EINVAL;
if (!rdev->ops->join_ibss)
return -EOPNOTSUPP;
if (ap_addr->sa_family != ARPHRD_ETHER)
return -EINVAL;
/* automatic mode */
if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid))
bssid = NULL;
if (bssid && !is_valid_ether_addr(bssid))
return -EINVAL;
/* both automatic */
if (!bssid && !wdev->wext.ibss.bssid)
return 0;
/* fixed already - and no change */
if (wdev->wext.ibss.bssid && bssid &&
ether_addr_equal(bssid, wdev->wext.ibss.bssid))
return 0;
err = 0;
if (wdev->u.ibss.ssid_len)
err = cfg80211_leave_ibss(rdev, dev, true);
if (err)
return err;
if (bssid) {
memcpy(wdev->wext.bssid, bssid, ETH_ALEN);
wdev->wext.ibss.bssid = wdev->wext.bssid;
} else
wdev->wext.ibss.bssid = NULL;
return cfg80211_ibss_wext_join(rdev, wdev);
}
int cfg80211_ibss_wext_giwap(struct net_device *dev,
struct iw_request_info *info,
struct sockaddr *ap_addr, char *extra)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
/* call only for ibss! */
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC))
return -EINVAL;
ap_addr->sa_family = ARPHRD_ETHER;
if (wdev->u.ibss.current_bss)
memcpy(ap_addr->sa_data, wdev->u.ibss.current_bss->pub.bssid,
ETH_ALEN);
else if (wdev->wext.ibss.bssid)
memcpy(ap_addr->sa_data, wdev->wext.ibss.bssid, ETH_ALEN);
else
eth_zero_addr(ap_addr->sa_data);
return 0;
}
#endif
+289
View File
@@ -0,0 +1,289 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Portions
* Copyright (C) 2022-2024 Intel Corporation
*/
#include <linux/ieee80211.h>
#include <linux/export.h>
#include <net/cfg80211.h>
#include "nl80211.h"
#include "core.h"
#include "rdev-ops.h"
/* Default values, timeouts in ms */
#define MESH_TTL 31
#define MESH_DEFAULT_ELEMENT_TTL 31
#define MESH_MAX_RETR 3
#define MESH_RET_T 100
#define MESH_CONF_T 100
#define MESH_HOLD_T 100
#define MESH_PATH_TIMEOUT 5000
#define MESH_RANN_INTERVAL 5000
#define MESH_PATH_TO_ROOT_TIMEOUT 6000
#define MESH_ROOT_INTERVAL 5000
#define MESH_ROOT_CONFIRMATION_INTERVAL 2000
#define MESH_DEFAULT_PLINK_TIMEOUT 1800 /* timeout in seconds */
/*
* Minimum interval between two consecutive PREQs originated by the same
* interface
*/
#define MESH_PREQ_MIN_INT 10
#define MESH_PERR_MIN_INT 100
#define MESH_DIAM_TRAVERSAL_TIME 50
#define MESH_RSSI_THRESHOLD 0
/*
* A path will be refreshed if it is used PATH_REFRESH_TIME milliseconds
* before timing out. This way it will remain ACTIVE and no data frames
* will be unnecessarily held in the pending queue.
*/
#define MESH_PATH_REFRESH_TIME 1000
#define MESH_MIN_DISCOVERY_TIMEOUT (2 * MESH_DIAM_TRAVERSAL_TIME)
/* Default maximum number of established plinks per interface */
#define MESH_MAX_ESTAB_PLINKS 32
#define MESH_MAX_PREQ_RETRIES 4
#define MESH_SYNC_NEIGHBOR_OFFSET_MAX 50
#define MESH_DEFAULT_BEACON_INTERVAL 1000 /* in 1024 us units (=TUs) */
#define MESH_DEFAULT_DTIM_PERIOD 2
#define MESH_DEFAULT_AWAKE_WINDOW 10 /* in 1024 us units (=TUs) */
const struct mesh_config default_mesh_config = {
.dot11MeshRetryTimeout = MESH_RET_T,
.dot11MeshConfirmTimeout = MESH_CONF_T,
.dot11MeshHoldingTimeout = MESH_HOLD_T,
.dot11MeshMaxRetries = MESH_MAX_RETR,
.dot11MeshTTL = MESH_TTL,
.element_ttl = MESH_DEFAULT_ELEMENT_TTL,
.auto_open_plinks = true,
.dot11MeshMaxPeerLinks = MESH_MAX_ESTAB_PLINKS,
.dot11MeshNbrOffsetMaxNeighbor = MESH_SYNC_NEIGHBOR_OFFSET_MAX,
.dot11MeshHWMPactivePathTimeout = MESH_PATH_TIMEOUT,
.dot11MeshHWMPpreqMinInterval = MESH_PREQ_MIN_INT,
.dot11MeshHWMPperrMinInterval = MESH_PERR_MIN_INT,
.dot11MeshHWMPnetDiameterTraversalTime = MESH_DIAM_TRAVERSAL_TIME,
.dot11MeshHWMPmaxPREQretries = MESH_MAX_PREQ_RETRIES,
.path_refresh_time = MESH_PATH_REFRESH_TIME,
.min_discovery_timeout = MESH_MIN_DISCOVERY_TIMEOUT,
.dot11MeshHWMPRannInterval = MESH_RANN_INTERVAL,
.dot11MeshGateAnnouncementProtocol = false,
.dot11MeshForwarding = true,
.rssi_threshold = MESH_RSSI_THRESHOLD,
.ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED,
.dot11MeshHWMPactivePathToRootTimeout = MESH_PATH_TO_ROOT_TIMEOUT,
.dot11MeshHWMProotInterval = MESH_ROOT_INTERVAL,
.dot11MeshHWMPconfirmationInterval = MESH_ROOT_CONFIRMATION_INTERVAL,
.power_mode = NL80211_MESH_POWER_ACTIVE,
.dot11MeshAwakeWindowDuration = MESH_DEFAULT_AWAKE_WINDOW,
.plink_timeout = MESH_DEFAULT_PLINK_TIMEOUT,
.dot11MeshNolearn = false,
};
const struct mesh_setup default_mesh_setup = {
/* cfg80211_join_mesh() will pick a channel if needed */
.sync_method = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET,
.path_sel_proto = IEEE80211_PATH_PROTOCOL_HWMP,
.path_metric = IEEE80211_PATH_METRIC_AIRTIME,
.auth_id = 0, /* open */
.ie = NULL,
.ie_len = 0,
.is_secure = false,
.user_mpm = false,
.beacon_interval = MESH_DEFAULT_BEACON_INTERVAL,
.dtim_period = MESH_DEFAULT_DTIM_PERIOD,
};
int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct mesh_setup *setup,
const struct mesh_config *conf)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
int err;
BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN != IEEE80211_MAX_MESH_ID_LEN);
lockdep_assert_wiphy(wdev->wiphy);
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
return -EOPNOTSUPP;
if (!(rdev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) &&
setup->is_secure)
return -EOPNOTSUPP;
if (wdev->u.mesh.id_len)
return -EALREADY;
if (!setup->mesh_id_len)
return -EINVAL;
if (!rdev->ops->join_mesh)
return -EOPNOTSUPP;
if (wdev->links[0].cac_started)
return -EBUSY;
if (!setup->chandef.chan) {
/* if no channel explicitly given, use preset channel */
setup->chandef = wdev->u.mesh.preset_chandef;
}
if (!setup->chandef.chan) {
/* if we don't have that either, use the first usable channel */
enum nl80211_band band;
for (band = 0; band < NUM_NL80211_BANDS; band++) {
struct ieee80211_supported_band *sband;
struct ieee80211_channel *chan;
int i;
sband = rdev->wiphy.bands[band];
if (!sband)
continue;
for (i = 0; i < sband->n_channels; i++) {
chan = &sband->channels[i];
if (chan->flags & (IEEE80211_CHAN_NO_IR |
IEEE80211_CHAN_DISABLED |
IEEE80211_CHAN_RADAR))
continue;
setup->chandef.chan = chan;
break;
}
if (setup->chandef.chan)
break;
}
/* no usable channel ... */
if (!setup->chandef.chan)
return -EINVAL;
setup->chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
setup->chandef.center_freq1 = setup->chandef.chan->center_freq;
}
/*
* check if basic rates are available otherwise use mandatory rates as
* basic rates
*/
if (!setup->basic_rates) {
struct ieee80211_supported_band *sband =
rdev->wiphy.bands[setup->chandef.chan->band];
if (setup->chandef.chan->band == NL80211_BAND_2GHZ) {
int i;
/*
* Older versions selected the mandatory rates for
* 2.4 GHz as well, but were broken in that only
* 1 Mbps was regarded as a mandatory rate. Keep
* using just 1 Mbps as the default basic rate for
* mesh to be interoperable with older versions.
*/
for (i = 0; i < sband->n_bitrates; i++) {
if (sband->bitrates[i].bitrate == 10) {
setup->basic_rates = BIT(i);
break;
}
}
} else {
setup->basic_rates = ieee80211_mandatory_rates(sband);
}
}
err = cfg80211_chandef_dfs_required(&rdev->wiphy,
&setup->chandef,
NL80211_IFTYPE_MESH_POINT);
if (err < 0)
return err;
if (err > 0 && !setup->userspace_handles_dfs)
return -EINVAL;
if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef,
NL80211_IFTYPE_MESH_POINT))
return -EINVAL;
err = rdev_join_mesh(rdev, dev, conf, setup);
if (!err) {
memcpy(wdev->u.mesh.id, setup->mesh_id, setup->mesh_id_len);
wdev->u.mesh.id_len = setup->mesh_id_len;
wdev->u.mesh.chandef = setup->chandef;
wdev->u.mesh.beacon_interval = setup->beacon_interval;
}
return err;
}
int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
struct cfg80211_chan_def *chandef)
{
int err;
/*
* Workaround for libertas (only!), it puts the interface
* into mesh mode but doesn't implement join_mesh. Instead,
* it is configured via sysfs and then joins the mesh when
* you set the channel. Note that the libertas mesh isn't
* compatible with 802.11 mesh.
*/
if (rdev->ops->libertas_set_mesh_channel) {
if (chandef->width != NL80211_CHAN_WIDTH_20_NOHT)
return -EINVAL;
if (!netif_running(wdev->netdev))
return -ENETDOWN;
err = rdev_libertas_set_mesh_channel(rdev, wdev->netdev,
chandef->chan);
if (!err)
wdev->u.mesh.chandef = *chandef;
return err;
}
if (wdev->u.mesh.id_len)
return -EBUSY;
wdev->u.mesh.preset_chandef = *chandef;
return 0;
}
int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,
struct net_device *dev)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
int err;
lockdep_assert_wiphy(wdev->wiphy);
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
return -EOPNOTSUPP;
if (!rdev->ops->leave_mesh)
return -EOPNOTSUPP;
if (!wdev->u.mesh.id_len)
return -ENOTCONN;
err = rdev_leave_mesh(rdev, dev);
if (!err) {
wdev->conn_owner_nlportid = 0;
wdev->u.mesh.id_len = 0;
wdev->u.mesh.beacon_interval = 0;
memset(&wdev->u.mesh.chandef, 0,
sizeof(wdev->u.mesh.chandef));
rdev_set_qos_map(rdev, dev, NULL);
cfg80211_sched_dfs_chan_update(rdev);
}
return err;
}
+87
View File
@@ -0,0 +1,87 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Michael MIC implementation - optimized for TKIP MIC operations
* Copyright 2002-2003, Instant802 Networks, Inc.
*/
#include <linux/types.h>
#include <linux/bitops.h>
#include <linux/export.h>
#include <linux/ieee80211.h>
#include <linux/unaligned.h>
struct michael_mic_ctx {
u32 l, r;
};
static void michael_block(struct michael_mic_ctx *mctx, u32 val)
{
mctx->l ^= val;
mctx->r ^= rol32(mctx->l, 17);
mctx->l += mctx->r;
mctx->r ^= ((mctx->l & 0xff00ff00) >> 8) |
((mctx->l & 0x00ff00ff) << 8);
mctx->l += mctx->r;
mctx->r ^= rol32(mctx->l, 3);
mctx->l += mctx->r;
mctx->r ^= ror32(mctx->l, 2);
mctx->l += mctx->r;
}
static void michael_mic_hdr(struct michael_mic_ctx *mctx, const u8 *key,
struct ieee80211_hdr *hdr)
{
u8 *da, *sa, tid;
da = ieee80211_get_DA(hdr);
sa = ieee80211_get_SA(hdr);
if (ieee80211_is_data_qos(hdr->frame_control))
tid = ieee80211_get_tid(hdr);
else
tid = 0;
mctx->l = get_unaligned_le32(key);
mctx->r = get_unaligned_le32(key + 4);
/*
* A pseudo header (DA, SA, Priority, 0, 0, 0) is used in Michael MIC
* calculation, but it is _not_ transmitted
*/
michael_block(mctx, get_unaligned_le32(da));
michael_block(mctx, get_unaligned_le16(&da[4]) |
(get_unaligned_le16(sa) << 16));
michael_block(mctx, get_unaligned_le32(&sa[2]));
michael_block(mctx, tid);
}
void michael_mic(const u8 *key, struct ieee80211_hdr *hdr,
const u8 *data, size_t data_len, u8 *mic)
{
u32 val;
size_t block, blocks, left;
struct michael_mic_ctx mctx;
michael_mic_hdr(&mctx, key, hdr);
/* Real data */
blocks = data_len / 4;
left = data_len % 4;
for (block = 0; block < blocks; block++)
michael_block(&mctx, get_unaligned_le32(&data[block * 4]));
/* Partial block of 0..3 bytes and padding: 0x5a + 4..7 zeros to make
* total length a multiple of 4. */
val = 0x5a;
while (left > 0) {
val <<= 8;
left--;
val |= data[blocks * 4 + left];
}
michael_block(&mctx, val);
michael_block(&mctx, 0);
put_unaligned_le32(mctx.l, mic);
put_unaligned_le32(mctx.r, mic + 4);
}
EXPORT_SYMBOL_GPL(michael_mic);
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+132
View File
@@ -0,0 +1,132 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Portions of this file
* Copyright (C) 2018, 2020-2026 Intel Corporation
*/
#ifndef __NET_WIRELESS_NL80211_H
#define __NET_WIRELESS_NL80211_H
#include "core.h"
int nl80211_init(void);
void nl80211_exit(void);
void *nl80211hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
int flags, u8 cmd);
bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info,
int attr);
static inline u64 wdev_id(struct wireless_dev *wdev)
{
return (u64)wdev->identifier |
((u64)wiphy_to_rdev(wdev->wiphy)->wiphy_idx << 32);
}
int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
struct netlink_ext_ack *extack,
struct nlattr **attrs,
struct cfg80211_chan_def *chandef,
bool npca_permitted);
int nl80211_parse_random_mac(struct nlattr **attrs,
u8 *mac_addr, u8 *mac_addr_mask);
void nl80211_notify_wiphy(struct cfg80211_registered_device *rdev,
enum nl80211_commands cmd);
void nl80211_notify_iface(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
enum nl80211_commands cmd);
void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev);
struct sk_buff *nl80211_build_scan_msg(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev, bool aborted);
void nl80211_send_scan_msg(struct cfg80211_registered_device *rdev,
struct sk_buff *msg);
void nl80211_send_sched_scan(struct cfg80211_sched_scan_request *req, u32 cmd);
void nl80211_common_reg_change_event(enum nl80211_commands cmd_id,
struct regulatory_request *request);
static inline void
nl80211_send_reg_change_event(struct regulatory_request *request)
{
nl80211_common_reg_change_event(NL80211_CMD_REG_CHANGE, request);
}
static inline void
nl80211_send_wiphy_reg_change_event(struct regulatory_request *request)
{
nl80211_common_reg_change_event(NL80211_CMD_WIPHY_REG_CHANGE, request);
}
void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
struct net_device *netdev,
const u8 *buf, size_t len, gfp_t gfp);
void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
struct net_device *netdev,
const struct cfg80211_rx_assoc_resp_data *data);
void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
struct net_device *netdev,
const u8 *buf, size_t len,
bool reconnect, gfp_t gfp);
void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
struct net_device *netdev,
const u8 *buf, size_t len,
bool reconnect, gfp_t gfp);
void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev,
struct net_device *netdev,
const u8 *addr, gfp_t gfp);
void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev,
struct net_device *netdev,
const u8 *addr, gfp_t gfp);
void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
struct net_device *netdev,
struct cfg80211_connect_resp_params *params,
gfp_t gfp);
void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
struct net_device *netdev,
struct cfg80211_roam_info *info, gfp_t gfp);
/* For STA/GC, indicate port authorized with AP/GO bssid.
* For GO/AP, use peer GC/STA mac_addr.
*/
void nl80211_send_port_authorized(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *peer_addr,
const u8 *td_bitmap, u8 td_bitmap_len);
void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
struct net_device *netdev, u16 reason,
const u8 *ie, size_t ie_len, bool from_ap);
void
nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *addr,
enum nl80211_key_type key_type,
int key_id, const u8 *tsc, gfp_t gfp);
void
nl80211_send_beacon_hint_event(struct wiphy *wiphy,
struct ieee80211_channel *channel_before,
struct ieee80211_channel *channel_after);
void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *bssid,
gfp_t gfp);
int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev, u32 nlpid,
struct cfg80211_rx_info *info, gfp_t gfp);
void
nl80211_radar_notify(struct cfg80211_registered_device *rdev,
const struct cfg80211_chan_def *chandef,
enum nl80211_radar_event event,
struct net_device *netdev, gfp_t gfp);
void nl80211_send_ap_stopped(struct wireless_dev *wdev, unsigned int link_id);
void cfg80211_free_coalesce(struct cfg80211_coalesce *coalesce);
/* peer measurement */
int nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info);
void nl80211_mlo_reconf_add_done(struct net_device *dev,
struct cfg80211_mlo_reconf_done_data *data);
#endif /* __NET_WIRELESS_NL80211_H */
+65
View File
@@ -0,0 +1,65 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* OCB mode implementation
*
* Copyright: (c) 2014 Czech Technical University in Prague
* (c) 2014 Volkswagen Group Research
* Copyright (C) 2022-2023 Intel Corporation
* Author: Rostislav Lisovy <rostislav.lisovy@fel.cvut.cz>
* Funded by: Volkswagen Group Research
*/
#include <linux/ieee80211.h>
#include <net/cfg80211.h>
#include "nl80211.h"
#include "core.h"
#include "rdev-ops.h"
int cfg80211_join_ocb(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct ocb_setup *setup)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
int err;
lockdep_assert_wiphy(wdev->wiphy);
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_OCB)
return -EOPNOTSUPP;
if (!rdev->ops->join_ocb)
return -EOPNOTSUPP;
if (WARN_ON(!setup->chandef.chan))
return -EINVAL;
err = rdev_join_ocb(rdev, dev, setup);
if (!err)
wdev->u.ocb.chandef = setup->chandef;
return err;
}
int cfg80211_leave_ocb(struct cfg80211_registered_device *rdev,
struct net_device *dev)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
int err;
lockdep_assert_wiphy(wdev->wiphy);
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_OCB)
return -EOPNOTSUPP;
if (!rdev->ops->leave_ocb)
return -EOPNOTSUPP;
if (!wdev->u.ocb.chandef.chan)
return -ENOTCONN;
err = rdev_leave_ocb(rdev, dev);
if (!err)
memset(&wdev->u.ocb.chandef, 0, sizeof(wdev->u.ocb.chandef));
return err;
}
+127
View File
@@ -0,0 +1,127 @@
// SPDX-License-Identifier: ISC
/*
* Copyright (C) 2017 Rafał Miłecki <rafal@milecki.pl>
*/
#include <linux/of.h>
#include <net/cfg80211.h>
#include "core.h"
static bool wiphy_freq_limits_valid_chan(struct wiphy *wiphy,
struct ieee80211_freq_range *freq_limits,
unsigned int n_freq_limits,
struct ieee80211_channel *chan)
{
u32 bw = MHZ_TO_KHZ(20);
int i;
for (i = 0; i < n_freq_limits; i++) {
struct ieee80211_freq_range *limit = &freq_limits[i];
if (cfg80211_does_bw_fit_range(limit,
MHZ_TO_KHZ(chan->center_freq),
bw))
return true;
}
return false;
}
static void wiphy_freq_limits_apply(struct wiphy *wiphy,
struct ieee80211_freq_range *freq_limits,
unsigned int n_freq_limits)
{
enum nl80211_band band;
int i;
if (WARN_ON(!n_freq_limits))
return;
for (band = 0; band < NUM_NL80211_BANDS; band++) {
struct ieee80211_supported_band *sband = wiphy->bands[band];
if (!sband)
continue;
for (i = 0; i < sband->n_channels; i++) {
struct ieee80211_channel *chan = &sband->channels[i];
if (chan->flags & IEEE80211_CHAN_DISABLED)
continue;
if (!wiphy_freq_limits_valid_chan(wiphy, freq_limits,
n_freq_limits,
chan)) {
pr_debug("Disabling freq %d MHz as it's out of OF limits\n",
chan->center_freq);
chan->flags |= IEEE80211_CHAN_DISABLED;
}
}
}
}
void wiphy_read_of_freq_limits(struct wiphy *wiphy)
{
struct device *dev = wiphy_dev(wiphy);
struct device_node *np;
struct property *prop;
struct ieee80211_freq_range *freq_limits;
unsigned int n_freq_limits;
const __be32 *p;
int len, i;
int err = 0;
if (!dev)
return;
np = dev_of_node(dev);
if (!np)
return;
prop = of_find_property(np, "ieee80211-freq-limit", &len);
if (!prop)
return;
if (!len || len % sizeof(u32) || len / sizeof(u32) % 2) {
dev_err(dev, "ieee80211-freq-limit wrong format");
return;
}
n_freq_limits = len / sizeof(u32) / 2;
freq_limits = kzalloc_objs(*freq_limits, n_freq_limits);
if (!freq_limits) {
err = -ENOMEM;
goto out_kfree;
}
p = NULL;
for (i = 0; i < n_freq_limits; i++) {
struct ieee80211_freq_range *limit = &freq_limits[i];
p = of_prop_next_u32(prop, p, &limit->start_freq_khz);
if (!p) {
err = -EINVAL;
goto out_kfree;
}
p = of_prop_next_u32(prop, p, &limit->end_freq_khz);
if (!p) {
err = -EINVAL;
goto out_kfree;
}
if (!limit->start_freq_khz ||
!limit->end_freq_khz ||
limit->start_freq_khz >= limit->end_freq_khz) {
err = -EINVAL;
goto out_kfree;
}
}
wiphy_freq_limits_apply(wiphy, freq_limits, n_freq_limits);
out_kfree:
kfree(freq_limits);
if (err)
dev_err(dev, "Failed to get limits: %d\n", err);
}
EXPORT_SYMBOL(wiphy_read_of_freq_limits);
+886
View File
@@ -0,0 +1,886 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2018 - 2021, 2023 - 2026 Intel Corporation
*/
#include <net/cfg80211.h>
#include "core.h"
#include "nl80211.h"
#include "rdev-ops.h"
static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev,
struct nlattr *ftmreq,
struct cfg80211_pmsr_request_peer *out,
struct genl_info *info)
{
const struct cfg80211_pmsr_capabilities *capa = rdev->wiphy.pmsr_capa;
struct nlattr *tb[NL80211_PMSR_FTM_REQ_ATTR_MAX + 1];
u32 preamble = NL80211_PREAMBLE_DMG; /* only optional in DMG */
/* validate existing data */
if (out->ftm.request_type == NL80211_PMSR_FTM_REQ_TYPE_INFRA &&
!(capa->ftm.bandwidths & BIT(out->chandef.width))) {
NL_SET_ERR_MSG(info->extack, "FTM: unsupported bandwidth");
return -EINVAL;
}
if (out->ftm.request_type == NL80211_PMSR_FTM_REQ_TYPE_PD &&
!(capa->ftm.pd_bandwidths & BIT(out->chandef.width))) {
NL_SET_ERR_MSG(info->extack,
"FTM: unsupported bandwidth for PD request");
return -EINVAL;
}
/* no validation needed - was already done via nested policy */
nla_parse_nested_deprecated(tb, NL80211_PMSR_FTM_REQ_ATTR_MAX, ftmreq,
NULL, NULL);
if (tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE])
preamble = nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE]);
/* set up values - struct is 0-initialized */
out->ftm.requested = true;
switch (out->chandef.chan->band) {
case NL80211_BAND_60GHZ:
/* optional */
break;
default:
if (!tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE]) {
NL_SET_ERR_MSG(info->extack,
"FTM: must specify preamble");
return -EINVAL;
}
}
if (out->ftm.request_type == NL80211_PMSR_FTM_REQ_TYPE_INFRA &&
!(capa->ftm.preambles & BIT(preamble))) {
NL_SET_ERR_MSG_ATTR(info->extack,
tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE],
"FTM: invalid preamble");
return -EINVAL;
}
if (out->ftm.request_type == NL80211_PMSR_FTM_REQ_TYPE_PD &&
!(capa->ftm.pd_preambles & BIT(preamble))) {
NL_SET_ERR_MSG_ATTR(info->extack,
tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE],
"FTM: invalid preamble for PD request");
return -EINVAL;
}
out->ftm.preamble = preamble;
out->ftm.burst_period = 0;
if (tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD])
out->ftm.burst_period =
nla_get_u16(tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD]);
out->ftm.asap = !!tb[NL80211_PMSR_FTM_REQ_ATTR_ASAP];
if (out->ftm.asap && !capa->ftm.asap) {
NL_SET_ERR_MSG_ATTR(info->extack,
tb[NL80211_PMSR_FTM_REQ_ATTR_ASAP],
"FTM: ASAP mode not supported");
return -EINVAL;
}
if (!out->ftm.asap && !capa->ftm.non_asap) {
NL_SET_ERR_MSG(info->extack,
"FTM: non-ASAP mode not supported");
return -EINVAL;
}
out->ftm.num_bursts_exp = 0;
if (tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP])
out->ftm.num_bursts_exp =
nla_get_u8(tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP]);
if (capa->ftm.max_bursts_exponent >= 0 &&
out->ftm.num_bursts_exp > capa->ftm.max_bursts_exponent) {
NL_SET_ERR_MSG_ATTR(info->extack,
tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP],
"FTM: max NUM_BURSTS_EXP must be set lower than the device limit");
return -EINVAL;
}
out->ftm.ftms_per_burst = 0;
if (tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST])
out->ftm.ftms_per_burst =
nla_get_u8(tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST]);
if (capa->ftm.max_ftms_per_burst &&
out->ftm.ftms_per_burst > capa->ftm.max_ftms_per_burst) {
NL_SET_ERR_MSG_ATTR(info->extack,
tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST],
"FTM: FTMs per burst must be set lower than the device limit");
return -EINVAL;
}
out->ftm.ftmr_retries = 3;
if (tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES])
out->ftm.ftmr_retries =
nla_get_u8(tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES]);
out->ftm.request_lci = !!tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI];
if (out->ftm.request_lci && !capa->ftm.request_lci) {
NL_SET_ERR_MSG_ATTR(info->extack,
tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI],
"FTM: LCI request not supported");
return -EOPNOTSUPP;
}
out->ftm.request_civicloc =
!!tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC];
if (out->ftm.request_civicloc && !capa->ftm.request_civicloc) {
NL_SET_ERR_MSG_ATTR(info->extack,
tb[NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC],
"FTM: civic location request not supported");
return -EOPNOTSUPP;
}
out->ftm.trigger_based =
!!tb[NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED];
if (out->ftm.trigger_based && !capa->ftm.trigger_based) {
NL_SET_ERR_MSG_ATTR(info->extack,
tb[NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED],
"FTM: trigger based ranging is not supported");
return -EINVAL;
}
if (out->ftm.request_type == NL80211_PMSR_FTM_REQ_TYPE_PD &&
out->ftm.trigger_based) {
NL_SET_ERR_MSG_ATTR(info->extack,
ftmreq,
"FTM: TB ranging is not supported for PD request type");
return -EINVAL;
}
out->ftm.non_trigger_based =
!!tb[NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED];
if (out->ftm.non_trigger_based && !capa->ftm.non_trigger_based) {
NL_SET_ERR_MSG_ATTR(info->extack,
tb[NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED],
"FTM: trigger based ranging is not supported");
return -EINVAL;
}
if (out->ftm.trigger_based && out->ftm.non_trigger_based) {
NL_SET_ERR_MSG(info->extack,
"FTM: can't set both trigger based and non trigger based");
return -EINVAL;
}
if (out->ftm.request_type == NL80211_PMSR_FTM_REQ_TYPE_PD &&
out->ftm.non_trigger_based && out->ftm.ftms_per_burst > 4) {
NL_SET_ERR_MSG_ATTR(info->extack,
tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST],
"FTM: FTMs per burst must not exceed 4 for PD NTB ranging");
return -ERANGE;
}
if (out->ftm.ftms_per_burst > 31 && !out->ftm.non_trigger_based &&
!out->ftm.trigger_based) {
NL_SET_ERR_MSG_ATTR(info->extack,
tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST],
"FTM: FTMs per burst must be set lower than 31");
return -ERANGE;
}
if ((out->ftm.trigger_based || out->ftm.non_trigger_based) &&
out->ftm.preamble != NL80211_PREAMBLE_HE) {
NL_SET_ERR_MSG_ATTR(info->extack,
tb[NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE],
"FTM: non EDCA based ranging must use HE preamble");
return -EINVAL;
}
if (tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION])
out->ftm.burst_duration =
nla_get_u8(tb[NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION]);
else if (!out->ftm.non_trigger_based && !out->ftm.trigger_based)
out->ftm.burst_duration = 15;
out->ftm.lmr_feedback =
!!tb[NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK];
if (!out->ftm.trigger_based && !out->ftm.non_trigger_based &&
out->ftm.lmr_feedback) {
NL_SET_ERR_MSG_ATTR(info->extack,
tb[NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK],
"FTM: LMR feedback set for EDCA based ranging");
return -EINVAL;
}
if (tb[NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR]) {
if (!out->ftm.non_trigger_based && !out->ftm.trigger_based) {
NL_SET_ERR_MSG_ATTR(info->extack,
tb[NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR],
"FTM: BSS color set for EDCA based ranging");
return -EINVAL;
}
out->ftm.bss_color =
nla_get_u8(tb[NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR]);
}
out->ftm.rsta = !!tb[NL80211_PMSR_FTM_REQ_ATTR_RSTA];
if (out->ftm.rsta && out->ftm.non_trigger_based &&
!capa->ftm.rsta.support_ntb) {
NL_SET_ERR_MSG_ATTR(info->extack,
tb[NL80211_PMSR_FTM_REQ_ATTR_RSTA],
"FTM: NTB RSTA not supported by device");
return -EOPNOTSUPP;
}
if (out->ftm.rsta && out->ftm.trigger_based &&
!capa->ftm.rsta.support_tb) {
NL_SET_ERR_MSG_ATTR(info->extack,
tb[NL80211_PMSR_FTM_REQ_ATTR_RSTA],
"FTM: TB RSTA not supported by device");
return -EOPNOTSUPP;
}
if (out->ftm.rsta && !out->ftm.non_trigger_based &&
!out->ftm.trigger_based &&
!capa->ftm.rsta.support_edca) {
NL_SET_ERR_MSG_ATTR(info->extack,
tb[NL80211_PMSR_FTM_REQ_ATTR_RSTA],
"FTM: EDCA RSTA not supported by device");
return -EOPNOTSUPP;
}
if (out->ftm.rsta &&
(out->ftm.non_trigger_based || out->ftm.trigger_based) &&
!out->ftm.lmr_feedback) {
NL_SET_ERR_MSG_ATTR(info->extack,
tb[NL80211_PMSR_FTM_REQ_ATTR_RSTA],
"FTM: RSTA set without LMR feedback");
return -EINVAL;
}
if (out->ftm.non_trigger_based) {
if (out->ftm.request_type == NL80211_PMSR_FTM_REQ_TYPE_PD &&
!tb[NL80211_PMSR_FTM_REQ_ATTR_NOMINAL_TIME]) {
NL_SET_ERR_MSG(info->extack,
"FTM: nominal time is required for PD NTB ranging");
return -EINVAL;
}
if (tb[NL80211_PMSR_FTM_REQ_ATTR_NOMINAL_TIME])
out->ftm.nominal_time =
nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_NOMINAL_TIME]);
if (tb[NL80211_PMSR_FTM_REQ_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS])
out->ftm.min_time_between_measurements =
nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS]);
if (tb[NL80211_PMSR_FTM_REQ_ATTR_MAX_TIME_BETWEEN_MEASUREMENTS])
out->ftm.max_time_between_measurements =
nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_MAX_TIME_BETWEEN_MEASUREMENTS]);
if (tb[NL80211_PMSR_FTM_REQ_ATTR_AW_DURATION])
out->ftm.availability_window =
nla_get_u8(tb[NL80211_PMSR_FTM_REQ_ATTR_AW_DURATION]);
if (tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_MEASUREMENTS])
out->ftm.num_measurements =
nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_NUM_MEASUREMENTS]);
}
if (tb[NL80211_PMSR_FTM_REQ_ATTR_INGRESS])
out->ftm.ingress_distance =
nla_get_u64(tb[NL80211_PMSR_FTM_REQ_ATTR_INGRESS]);
if (tb[NL80211_PMSR_FTM_REQ_ATTR_EGRESS])
out->ftm.egress_distance =
nla_get_u64(tb[NL80211_PMSR_FTM_REQ_ATTR_EGRESS]);
out->ftm.pd_suppress_range_results =
nla_get_flag(tb[NL80211_PMSR_FTM_REQ_ATTR_PD_SUPPRESS_RESULTS]);
if (out->ftm.request_type != NL80211_PMSR_FTM_REQ_TYPE_PD &&
out->ftm.pd_suppress_range_results) {
NL_SET_ERR_MSG_ATTR(info->extack,
tb[NL80211_PMSR_FTM_REQ_ATTR_PD_SUPPRESS_RESULTS],
"FTM: suppress range result flag only valid for PD requests");
return -EINVAL;
}
return 0;
}
static int pmsr_parse_peer(struct cfg80211_registered_device *rdev,
struct nlattr *peer,
struct cfg80211_pmsr_request_peer *out,
struct genl_info *info)
{
struct nlattr *tb[NL80211_PMSR_PEER_ATTR_MAX + 1];
struct nlattr *req[NL80211_PMSR_REQ_ATTR_MAX + 1];
bool have_measurement_type = false;
struct nlattr *treq;
int err, rem;
/* no validation needed - was already done via nested policy */
nla_parse_nested_deprecated(tb, NL80211_PMSR_PEER_ATTR_MAX, peer,
NULL, NULL);
if (!tb[NL80211_PMSR_PEER_ATTR_ADDR] ||
!tb[NL80211_PMSR_PEER_ATTR_CHAN] ||
!tb[NL80211_PMSR_PEER_ATTR_REQ]) {
NL_SET_ERR_MSG_ATTR(info->extack, peer,
"insufficient peer data");
return -EINVAL;
}
memcpy(out->addr, nla_data(tb[NL80211_PMSR_PEER_ATTR_ADDR]), ETH_ALEN);
if (tb[NL80211_PMSR_PEER_ATTR_REQ_TYPE])
out->ftm.request_type =
nla_get_u32(tb[NL80211_PMSR_PEER_ATTR_REQ_TYPE]);
else
out->ftm.request_type = NL80211_PMSR_FTM_REQ_TYPE_INFRA;
if (out->ftm.request_type == NL80211_PMSR_FTM_REQ_TYPE_PD &&
!rdev->wiphy.pmsr_capa->ftm.type.pd_support) {
NL_SET_ERR_MSG_ATTR(info->extack,
tb[NL80211_PMSR_PEER_ATTR_REQ_TYPE],
"FTM: PD request type not supported by device");
return -EINVAL;
}
/* reuse info->attrs */
memset(info->attrs, 0, sizeof(*info->attrs) * (NL80211_ATTR_MAX + 1));
err = nla_parse_nested_deprecated(info->attrs, NL80211_ATTR_MAX,
tb[NL80211_PMSR_PEER_ATTR_CHAN],
NULL, info->extack);
if (err)
return err;
err = nl80211_parse_chandef(rdev, info->extack, info->attrs,
&out->chandef, false);
if (err)
return err;
/* no validation needed - was already done via nested policy */
nla_parse_nested_deprecated(req, NL80211_PMSR_REQ_ATTR_MAX,
tb[NL80211_PMSR_PEER_ATTR_REQ], NULL,
NULL);
if (!req[NL80211_PMSR_REQ_ATTR_DATA]) {
NL_SET_ERR_MSG_ATTR(info->extack,
tb[NL80211_PMSR_PEER_ATTR_REQ],
"missing request type/data");
return -EINVAL;
}
if (req[NL80211_PMSR_REQ_ATTR_GET_AP_TSF])
out->report_ap_tsf = true;
if (out->report_ap_tsf && !rdev->wiphy.pmsr_capa->report_ap_tsf) {
NL_SET_ERR_MSG_ATTR(info->extack,
req[NL80211_PMSR_REQ_ATTR_GET_AP_TSF],
"reporting AP TSF is not supported");
return -EINVAL;
}
nla_for_each_nested(treq, req[NL80211_PMSR_REQ_ATTR_DATA], rem) {
if (have_measurement_type) {
NL_SET_ERR_MSG_ATTR(info->extack, treq,
"multiple measurement types in request data");
return -EINVAL;
}
have_measurement_type = true;
switch (nla_type(treq)) {
case NL80211_PMSR_TYPE_FTM:
err = pmsr_parse_ftm(rdev, treq, out, info);
break;
default:
NL_SET_ERR_MSG_ATTR(info->extack, treq,
"unsupported measurement type");
err = -EINVAL;
}
if (err)
return err;
}
if (!have_measurement_type) {
NL_SET_ERR_MSG_ATTR(info->extack,
req[NL80211_PMSR_REQ_ATTR_DATA],
"missing measurement type in request data");
return -EINVAL;
}
return 0;
}
int nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info)
{
struct nlattr *reqattr = info->attrs[NL80211_ATTR_PEER_MEASUREMENTS];
struct cfg80211_registered_device *rdev = info->user_ptr[0];
int count, rem, err, idx, peer_count;
struct wireless_dev *wdev = info->user_ptr[1];
const struct cfg80211_pmsr_capabilities *capa;
struct cfg80211_pmsr_request *req;
struct nlattr *peers, *peer;
u64 cookie;
capa = rdev->wiphy.pmsr_capa;
if (!capa)
return -EOPNOTSUPP;
if (!reqattr)
return -EINVAL;
peers = nla_find(nla_data(reqattr), nla_len(reqattr),
NL80211_PMSR_ATTR_PEERS);
if (!peers)
return -EINVAL;
count = 0;
nla_for_each_nested(peer, peers, rem) {
count++;
if (count > capa->max_peers) {
NL_SET_ERR_MSG_ATTR(info->extack, peer,
"Too many peers used");
return -EINVAL;
}
}
if (!count) {
NL_SET_ERR_MSG_ATTR(info->extack, peers, "No peers specified");
return -EINVAL;
}
req = kzalloc_flex(*req, peers, count);
if (!req)
return -ENOMEM;
req->n_peers = count;
if (info->attrs[NL80211_ATTR_TIMEOUT])
req->timeout = nla_get_u32(info->attrs[NL80211_ATTR_TIMEOUT]);
if (info->attrs[NL80211_ATTR_MAC]) {
if (!capa->randomize_mac_addr) {
NL_SET_ERR_MSG_ATTR(info->extack,
info->attrs[NL80211_ATTR_MAC],
"device cannot randomize MAC address");
err = -EINVAL;
goto out_err;
}
err = nl80211_parse_random_mac(info->attrs, req->mac_addr,
req->mac_addr_mask);
if (err)
goto out_err;
} else {
memcpy(req->mac_addr, wdev_address(wdev), ETH_ALEN);
eth_broadcast_addr(req->mac_addr_mask);
}
idx = 0;
nla_for_each_nested(peer, peers, rem) {
/* NB: this reuses info->attrs, but we no longer need it */
err = pmsr_parse_peer(rdev, peer, &req->peers[idx], info);
if (err)
goto out_err;
idx++;
}
/* Validate per-role peer limits if advertised */
if (capa->ftm.ista.max_peers) {
peer_count = 0;
for (idx = 0; idx < req->n_peers; idx++) {
if (!req->peers[idx].ftm.rsta) {
peer_count++;
if (peer_count > capa->ftm.ista.max_peers) {
NL_SET_ERR_MSG(info->extack,
"Too many ISTA peers for device limit");
err = -EINVAL;
goto out_err;
}
}
}
}
if (capa->ftm.rsta.max_peers) {
peer_count = 0;
for (idx = 0; idx < req->n_peers; idx++) {
if (req->peers[idx].ftm.rsta) {
peer_count++;
if (peer_count > capa->ftm.rsta.max_peers) {
NL_SET_ERR_MSG(info->extack,
"Too many RSTA peers for device limit");
err = -EINVAL;
goto out_err;
}
}
}
}
req->cookie = cfg80211_assign_cookie(rdev);
req->nl_portid = info->snd_portid;
cookie = req->cookie;
/*
* Add to the list before the driver call; under races or broken
* drivers, completion may free the request before rdev_start_pmsr()
* returns. Use the saved cookie below.
*/
spin_lock_bh(&wdev->pmsr_lock);
list_add_tail(&req->list, &wdev->pmsr_list);
spin_unlock_bh(&wdev->pmsr_lock);
err = rdev_start_pmsr(rdev, wdev, req);
if (err) {
/* An error return leaves the request owned by this path. */
spin_lock_bh(&wdev->pmsr_lock);
list_del(&req->list);
spin_unlock_bh(&wdev->pmsr_lock);
goto out_err;
}
nl_set_extack_cookie_u64(info->extack, cookie);
return 0;
out_err:
kfree(req);
return err;
}
void cfg80211_pmsr_complete(struct wireless_dev *wdev,
struct cfg80211_pmsr_request *req,
gfp_t gfp)
{
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
struct cfg80211_pmsr_request *tmp, *prev, *to_free = NULL;
struct sk_buff *msg;
void *hdr;
trace_cfg80211_pmsr_complete(wdev->wiphy, wdev, req->cookie);
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
if (!msg)
goto free_request;
hdr = nl80211hdr_put(msg, 0, 0, 0,
NL80211_CMD_PEER_MEASUREMENT_COMPLETE);
if (!hdr)
goto free_msg;
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
NL80211_ATTR_PAD))
goto free_msg;
if (nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, req->cookie,
NL80211_ATTR_PAD))
goto free_msg;
genlmsg_end(msg, hdr);
genlmsg_unicast(wiphy_net(wdev->wiphy), msg, req->nl_portid);
goto free_request;
free_msg:
nlmsg_free(msg);
free_request:
spin_lock_bh(&wdev->pmsr_lock);
/*
* cfg80211_pmsr_process_abort() may have already moved this request
* to the free list, and will free it later. In this case, don't free
* it here.
*/
list_for_each_entry_safe(tmp, prev, &wdev->pmsr_list, list) {
if (tmp == req) {
list_del(&req->list);
to_free = req;
break;
}
}
spin_unlock_bh(&wdev->pmsr_lock);
kfree(to_free);
}
EXPORT_SYMBOL_GPL(cfg80211_pmsr_complete);
static int nl80211_pmsr_send_ftm_res(struct sk_buff *msg,
struct cfg80211_pmsr_result *res)
{
if (res->status == NL80211_PMSR_STATUS_FAILURE) {
if (nla_put_u32(msg, NL80211_PMSR_FTM_RESP_ATTR_FAIL_REASON,
res->ftm.failure_reason))
goto error;
if (res->ftm.failure_reason ==
NL80211_PMSR_FTM_FAILURE_PEER_BUSY &&
res->ftm.busy_retry_time &&
nla_put_u32(msg, NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME,
res->ftm.busy_retry_time))
goto error;
return 0;
}
#define PUT(tp, attr, val) \
do { \
if (nla_put_##tp(msg, \
NL80211_PMSR_FTM_RESP_ATTR_##attr, \
res->ftm.val)) \
goto error; \
} while (0)
#define PUTOPT(tp, attr, val) \
do { \
if (res->ftm.val##_valid) \
PUT(tp, attr, val); \
} while (0)
#define PUT_U64(attr, val) \
do { \
if (nla_put_u64_64bit(msg, \
NL80211_PMSR_FTM_RESP_ATTR_##attr,\
res->ftm.val, \
NL80211_PMSR_FTM_RESP_ATTR_PAD)) \
goto error; \
} while (0)
#define PUTOPT_U64(attr, val) \
do { \
if (res->ftm.val##_valid) \
PUT_U64(attr, val); \
} while (0)
if (res->ftm.burst_index >= 0)
PUT(u32, BURST_INDEX, burst_index);
PUTOPT(u32, NUM_FTMR_ATTEMPTS, num_ftmr_attempts);
PUTOPT(u32, NUM_FTMR_SUCCESSES, num_ftmr_successes);
PUT(u8, NUM_BURSTS_EXP, num_bursts_exp);
PUT(u8, BURST_DURATION, burst_duration);
PUT(u8, FTMS_PER_BURST, ftms_per_burst);
PUT(u16, BURST_PERIOD, burst_period);
PUTOPT(s32, RSSI_AVG, rssi_avg);
PUTOPT(s32, RSSI_SPREAD, rssi_spread);
if (res->ftm.tx_rate_valid &&
!nl80211_put_sta_rate(msg, &res->ftm.tx_rate,
NL80211_PMSR_FTM_RESP_ATTR_TX_RATE))
goto error;
if (res->ftm.rx_rate_valid &&
!nl80211_put_sta_rate(msg, &res->ftm.rx_rate,
NL80211_PMSR_FTM_RESP_ATTR_RX_RATE))
goto error;
PUTOPT_U64(RTT_AVG, rtt_avg);
PUTOPT_U64(RTT_VARIANCE, rtt_variance);
PUTOPT_U64(RTT_SPREAD, rtt_spread);
PUTOPT_U64(DIST_AVG, dist_avg);
PUTOPT_U64(DIST_VARIANCE, dist_variance);
PUTOPT_U64(DIST_SPREAD, dist_spread);
PUTOPT(u32, TX_LTF_REPETITION_COUNT, tx_ltf_repetition_count);
PUTOPT(u32, RX_LTF_REPETITION_COUNT, rx_ltf_repetition_count);
PUTOPT(u32, MAX_TIME_BETWEEN_MEASUREMENTS,
max_time_between_measurements);
PUTOPT(u32, MIN_TIME_BETWEEN_MEASUREMENTS,
min_time_between_measurements);
PUTOPT(u8, NUM_TX_SPATIAL_STREAMS, num_tx_spatial_streams);
PUTOPT(u8, NUM_RX_SPATIAL_STREAMS, num_rx_spatial_streams);
PUTOPT(u32, NOMINAL_TIME, nominal_time);
PUTOPT(u8, AVAILABILITY_WINDOW, availability_window);
PUTOPT(u32, CHANNEL_WIDTH, chan_width);
PUTOPT(u32, PREAMBLE, preamble);
if (res->ftm.is_delayed_lmr &&
nla_put_flag(msg, NL80211_PMSR_FTM_RESP_ATTR_IS_DELAYED_LMR))
goto error;
if (res->ftm.lci && res->ftm.lci_len &&
nla_put(msg, NL80211_PMSR_FTM_RESP_ATTR_LCI,
res->ftm.lci_len, res->ftm.lci))
goto error;
if (res->ftm.civicloc && res->ftm.civicloc_len &&
nla_put(msg, NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC,
res->ftm.civicloc_len, res->ftm.civicloc))
goto error;
#undef PUT
#undef PUTOPT
#undef PUT_U64
#undef PUTOPT_U64
return 0;
error:
return -ENOSPC;
}
static int nl80211_pmsr_send_result(struct sk_buff *msg,
struct cfg80211_pmsr_result *res)
{
struct nlattr *pmsr, *peers, *peer, *resp, *data, *typedata;
pmsr = nla_nest_start_noflag(msg, NL80211_ATTR_PEER_MEASUREMENTS);
if (!pmsr)
goto error;
peers = nla_nest_start_noflag(msg, NL80211_PMSR_ATTR_PEERS);
if (!peers)
goto error;
peer = nla_nest_start_noflag(msg, 1);
if (!peer)
goto error;
if (nla_put(msg, NL80211_PMSR_PEER_ATTR_ADDR, ETH_ALEN, res->addr))
goto error;
resp = nla_nest_start_noflag(msg, NL80211_PMSR_PEER_ATTR_RESP);
if (!resp)
goto error;
if (nla_put_u32(msg, NL80211_PMSR_RESP_ATTR_STATUS, res->status) ||
nla_put_u64_64bit(msg, NL80211_PMSR_RESP_ATTR_HOST_TIME,
res->host_time, NL80211_PMSR_RESP_ATTR_PAD))
goto error;
if (res->ap_tsf_valid &&
nla_put_u64_64bit(msg, NL80211_PMSR_RESP_ATTR_AP_TSF,
res->ap_tsf, NL80211_PMSR_RESP_ATTR_PAD))
goto error;
if (res->final && nla_put_flag(msg, NL80211_PMSR_RESP_ATTR_FINAL))
goto error;
data = nla_nest_start_noflag(msg, NL80211_PMSR_RESP_ATTR_DATA);
if (!data)
goto error;
typedata = nla_nest_start_noflag(msg, res->type);
if (!typedata)
goto error;
switch (res->type) {
case NL80211_PMSR_TYPE_FTM:
if (nl80211_pmsr_send_ftm_res(msg, res))
goto error;
break;
default:
WARN_ON(1);
}
nla_nest_end(msg, typedata);
nla_nest_end(msg, data);
nla_nest_end(msg, resp);
nla_nest_end(msg, peer);
nla_nest_end(msg, peers);
nla_nest_end(msg, pmsr);
return 0;
error:
return -ENOSPC;
}
void cfg80211_pmsr_report(struct wireless_dev *wdev,
struct cfg80211_pmsr_request *req,
struct cfg80211_pmsr_result *result,
gfp_t gfp)
{
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
struct sk_buff *msg;
void *hdr;
int err;
trace_cfg80211_pmsr_report(wdev->wiphy, wdev, req->cookie,
result->addr);
/*
* Currently, only variable items are LCI and civic location,
* both of which are reasonably short so we don't need to
* worry about them here for the allocation.
*/
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
if (!msg)
return;
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PEER_MEASUREMENT_RESULT);
if (!hdr)
goto free;
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
NL80211_ATTR_PAD))
goto free;
if (nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, req->cookie,
NL80211_ATTR_PAD))
goto free;
err = nl80211_pmsr_send_result(msg, result);
if (err) {
pr_err_ratelimited("peer measurement result: message didn't fit!");
goto free;
}
genlmsg_end(msg, hdr);
genlmsg_unicast(wiphy_net(wdev->wiphy), msg, req->nl_portid);
return;
free:
nlmsg_free(msg);
}
EXPORT_SYMBOL_GPL(cfg80211_pmsr_report);
static void cfg80211_pmsr_process_abort(struct wireless_dev *wdev)
{
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
struct cfg80211_pmsr_request *req, *tmp;
LIST_HEAD(free_list);
lockdep_assert_wiphy(wdev->wiphy);
spin_lock_bh(&wdev->pmsr_lock);
list_for_each_entry_safe(req, tmp, &wdev->pmsr_list, list) {
if (req->nl_portid)
continue;
list_move_tail(&req->list, &free_list);
}
spin_unlock_bh(&wdev->pmsr_lock);
list_for_each_entry_safe(req, tmp, &free_list, list) {
rdev_abort_pmsr(rdev, wdev, req);
kfree(req);
}
}
void cfg80211_pmsr_free_wk(struct wiphy *wiphy, struct wiphy_work *work)
{
struct wireless_dev *wdev = container_of(work, struct wireless_dev,
pmsr_free_wk);
cfg80211_pmsr_process_abort(wdev);
}
void cfg80211_pmsr_wdev_down(struct wireless_dev *wdev)
{
struct cfg80211_pmsr_request *req;
bool found = false;
spin_lock_bh(&wdev->pmsr_lock);
list_for_each_entry(req, &wdev->pmsr_list, list) {
found = true;
req->nl_portid = 0;
}
spin_unlock_bh(&wdev->pmsr_lock);
wiphy_work_cancel(wdev->wiphy, &wdev->pmsr_free_wk);
if (found)
cfg80211_pmsr_process_abort(wdev);
WARN_ON(!list_empty(&wdev->pmsr_list));
}
void cfg80211_release_pmsr(struct wireless_dev *wdev, u32 portid)
{
struct cfg80211_pmsr_request *req;
spin_lock_bh(&wdev->pmsr_lock);
list_for_each_entry(req, &wdev->pmsr_list, list) {
if (req->nl_portid == portid) {
req->nl_portid = 0;
wiphy_work_queue(wdev->wiphy, &wdev->pmsr_free_wk);
}
}
spin_unlock_bh(&wdev->pmsr_lock);
}
+362
View File
@@ -0,0 +1,362 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
/*
* Radiotap parser
*
* Copyright 2007 Andy Green <andy@warmcat.com>
* Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
*/
#include <linux/kernel.h>
#include <linux/export.h>
#include <net/cfg80211.h>
#include <net/ieee80211_radiotap.h>
#include <linux/unaligned.h>
/* function prototypes and related defs are in include/net/cfg80211.h */
static const struct radiotap_align_size rtap_namespace_sizes[] = {
[IEEE80211_RADIOTAP_TSFT] = { .align = 8, .size = 8, },
[IEEE80211_RADIOTAP_FLAGS] = { .align = 1, .size = 1, },
[IEEE80211_RADIOTAP_RATE] = { .align = 1, .size = 1, },
[IEEE80211_RADIOTAP_CHANNEL] = { .align = 2, .size = 4, },
[IEEE80211_RADIOTAP_FHSS] = { .align = 2, .size = 2, },
[IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = { .align = 1, .size = 1, },
[IEEE80211_RADIOTAP_DBM_ANTNOISE] = { .align = 1, .size = 1, },
[IEEE80211_RADIOTAP_LOCK_QUALITY] = { .align = 2, .size = 2, },
[IEEE80211_RADIOTAP_TX_ATTENUATION] = { .align = 2, .size = 2, },
[IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = { .align = 2, .size = 2, },
[IEEE80211_RADIOTAP_DBM_TX_POWER] = { .align = 1, .size = 1, },
[IEEE80211_RADIOTAP_ANTENNA] = { .align = 1, .size = 1, },
[IEEE80211_RADIOTAP_DB_ANTSIGNAL] = { .align = 1, .size = 1, },
[IEEE80211_RADIOTAP_DB_ANTNOISE] = { .align = 1, .size = 1, },
[IEEE80211_RADIOTAP_RX_FLAGS] = { .align = 2, .size = 2, },
[IEEE80211_RADIOTAP_TX_FLAGS] = { .align = 2, .size = 2, },
[IEEE80211_RADIOTAP_RTS_RETRIES] = { .align = 1, .size = 1, },
[IEEE80211_RADIOTAP_DATA_RETRIES] = { .align = 1, .size = 1, },
[IEEE80211_RADIOTAP_MCS] = { .align = 1, .size = 3, },
[IEEE80211_RADIOTAP_AMPDU_STATUS] = { .align = 4, .size = 8, },
[IEEE80211_RADIOTAP_VHT] = { .align = 2, .size = 12, },
/*
* add more here as they are defined in radiotap.h
*/
};
static const struct ieee80211_radiotap_namespace radiotap_ns = {
.n_bits = ARRAY_SIZE(rtap_namespace_sizes),
.align_size = rtap_namespace_sizes,
};
/**
* ieee80211_radiotap_iterator_init - radiotap parser iterator initialization
* @iterator: radiotap_iterator to initialize
* @radiotap_header: radiotap header to parse
* @max_length: total length we can parse into (eg, whole packet length)
* @vns: vendor namespaces to parse
*
* Returns: 0 or a negative error code if there is a problem.
*
* This function initializes an opaque iterator struct which can then
* be passed to ieee80211_radiotap_iterator_next() to visit every radiotap
* argument which is present in the header. It knows about extended
* present headers and handles them.
*
* How to use:
* call __ieee80211_radiotap_iterator_init() to init a semi-opaque iterator
* struct ieee80211_radiotap_iterator (no need to init the struct beforehand)
* checking for a good 0 return code. Then loop calling
* __ieee80211_radiotap_iterator_next()... it returns either 0,
* -ENOENT if there are no more args to parse, or -EINVAL if there is a problem.
* The iterator's @this_arg member points to the start of the argument
* associated with the current argument index that is present, which can be
* found in the iterator's @this_arg_index member. This arg index corresponds
* to the IEEE80211_RADIOTAP_... defines.
*
* Radiotap header length:
* You can find the CPU-endian total radiotap header length in
* iterator->max_length after executing ieee80211_radiotap_iterator_init()
* successfully.
*
* Alignment Gotcha:
* You must take care when dereferencing iterator.this_arg
* for multibyte types... the pointer is not aligned. Use
* get_unaligned((type *)iterator.this_arg) to dereference
* iterator.this_arg for type "type" safely on all arches.
*
* Example code:
* See Documentation/networking/radiotap-headers.rst
*/
int ieee80211_radiotap_iterator_init(
struct ieee80211_radiotap_iterator *iterator,
struct ieee80211_radiotap_header *radiotap_header,
int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns)
{
/* check the radiotap header can actually be present */
if (max_length < sizeof(struct ieee80211_radiotap_header))
return -EINVAL;
/* Linux only supports version 0 radiotap format */
if (radiotap_header->it_version)
return -EINVAL;
/* sanity check for allowed length and radiotap length field */
if (max_length < get_unaligned_le16(&radiotap_header->it_len))
return -EINVAL;
iterator->_rtheader = radiotap_header;
iterator->_max_length = get_unaligned_le16(&radiotap_header->it_len);
iterator->_arg_index = 0;
iterator->_bitmap_shifter = get_unaligned_le32(&radiotap_header->it_present);
iterator->_arg = (uint8_t *)radiotap_header->it_optional;
iterator->_reset_on_ext = 0;
iterator->_next_bitmap = radiotap_header->it_optional;
iterator->_vns = vns;
iterator->current_namespace = &radiotap_ns;
iterator->is_radiotap_ns = 1;
/* find payload start allowing for extended bitmap(s) */
if (iterator->_bitmap_shifter & (BIT(IEEE80211_RADIOTAP_EXT))) {
if ((unsigned long)iterator->_arg -
(unsigned long)iterator->_rtheader + sizeof(uint32_t) >
(unsigned long)iterator->_max_length)
return -EINVAL;
while (get_unaligned_le32(iterator->_arg) &
(BIT(IEEE80211_RADIOTAP_EXT))) {
iterator->_arg += sizeof(uint32_t);
/*
* check for insanity where the present bitmaps
* keep claiming to extend up to or even beyond the
* stated radiotap header length
*/
if ((unsigned long)iterator->_arg -
(unsigned long)iterator->_rtheader +
sizeof(uint32_t) >
(unsigned long)iterator->_max_length)
return -EINVAL;
}
iterator->_arg += sizeof(uint32_t);
/*
* no need to check again for blowing past stated radiotap
* header length, because ieee80211_radiotap_iterator_next
* checks it before it is dereferenced
*/
}
iterator->this_arg = iterator->_arg;
/* we are all initialized happily */
return 0;
}
EXPORT_SYMBOL(ieee80211_radiotap_iterator_init);
static void find_ns(struct ieee80211_radiotap_iterator *iterator,
uint32_t oui, uint8_t subns)
{
int i;
iterator->current_namespace = NULL;
if (!iterator->_vns)
return;
for (i = 0; i < iterator->_vns->n_ns; i++) {
if (iterator->_vns->ns[i].oui != oui)
continue;
if (iterator->_vns->ns[i].subns != subns)
continue;
iterator->current_namespace = &iterator->_vns->ns[i];
break;
}
}
/**
* ieee80211_radiotap_iterator_next - return next radiotap parser iterator arg
* @iterator: radiotap_iterator to move to next arg (if any)
*
* Returns: 0 if there is an argument to handle,
* -ENOENT if there are no more args or -EINVAL
* if there is something else wrong.
*
* This function provides the next radiotap arg index (IEEE80211_RADIOTAP_*)
* in @this_arg_index and sets @this_arg to point to the
* payload for the field. It takes care of alignment handling and extended
* present fields. @this_arg can be changed by the caller (eg,
* incremented to move inside a compound argument like
* IEEE80211_RADIOTAP_CHANNEL). The args pointed to are in
* little-endian format whatever the endianness of your CPU.
*
* Alignment Gotcha:
* You must take care when dereferencing iterator.this_arg
* for multibyte types... the pointer is not aligned. Use
* get_unaligned((type *)iterator.this_arg) to dereference
* iterator.this_arg for type "type" safely on all arches.
*/
int ieee80211_radiotap_iterator_next(
struct ieee80211_radiotap_iterator *iterator)
{
while (1) {
int hit = 0;
int pad, align, size, subns;
uint32_t oui;
/* if no more EXT bits, that's it */
if ((iterator->_arg_index % 32) == IEEE80211_RADIOTAP_EXT &&
!(iterator->_bitmap_shifter & 1))
return -ENOENT;
if (!(iterator->_bitmap_shifter & 1))
goto next_entry; /* arg not present */
/* get alignment/size of data */
switch (iterator->_arg_index % 32) {
case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE:
case IEEE80211_RADIOTAP_EXT:
align = 1;
size = 0;
break;
case IEEE80211_RADIOTAP_VENDOR_NAMESPACE:
align = 2;
size = 6;
break;
default:
if (!iterator->current_namespace ||
iterator->_arg_index >= iterator->current_namespace->n_bits) {
align = 0;
} else {
align = iterator->current_namespace->align_size[iterator->_arg_index].align;
size = iterator->current_namespace->align_size[iterator->_arg_index].size;
}
if (!align) {
if (iterator->current_namespace == &radiotap_ns)
return -ENOENT;
/* skip all subsequent data */
iterator->_arg = iterator->_next_ns_data;
/* give up on this namespace */
iterator->current_namespace = NULL;
goto next_entry;
}
break;
}
/*
* arg is present, account for alignment padding
*
* Note that these alignments are relative to the start
* of the radiotap header. There is no guarantee
* that the radiotap header itself is aligned on any
* kind of boundary.
*
* The above is why get_unaligned() is used to dereference
* multibyte elements from the radiotap area.
*/
pad = ((unsigned long)iterator->_arg -
(unsigned long)iterator->_rtheader) & (align - 1);
if (pad)
iterator->_arg += align - pad;
if (iterator->_arg_index % 32 == IEEE80211_RADIOTAP_VENDOR_NAMESPACE) {
int vnslen;
if ((unsigned long)iterator->_arg + size -
(unsigned long)iterator->_rtheader >
(unsigned long)iterator->_max_length)
return -EINVAL;
oui = (*iterator->_arg << 16) |
(*(iterator->_arg + 1) << 8) |
*(iterator->_arg + 2);
subns = *(iterator->_arg + 3);
find_ns(iterator, oui, subns);
vnslen = get_unaligned_le16(iterator->_arg + 4);
iterator->_next_ns_data = iterator->_arg + size + vnslen;
if (!iterator->current_namespace)
size += vnslen;
}
/*
* this is what we will return to user, but we need to
* move on first so next call has something fresh to test
*/
iterator->this_arg_index = iterator->_arg_index;
iterator->this_arg = iterator->_arg;
iterator->this_arg_size = size;
/* internally move on the size of this arg */
iterator->_arg += size;
/*
* check for insanity where we are given a bitmap that
* claims to have more arg content than the length of the
* radiotap section. We will normally end up equalling this
* max_length on the last arg, never exceeding it.
*/
if ((unsigned long)iterator->_arg -
(unsigned long)iterator->_rtheader >
(unsigned long)iterator->_max_length)
return -EINVAL;
/* these special ones are valid in each bitmap word */
switch (iterator->_arg_index % 32) {
case IEEE80211_RADIOTAP_VENDOR_NAMESPACE:
iterator->_reset_on_ext = 1;
iterator->is_radiotap_ns = 0;
/*
* If parser didn't register this vendor
* namespace with us, allow it to show it
* as 'raw. Do do that, set argument index
* to vendor namespace.
*/
iterator->this_arg_index =
IEEE80211_RADIOTAP_VENDOR_NAMESPACE;
if (!iterator->current_namespace)
hit = 1;
goto next_entry;
case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE:
iterator->_reset_on_ext = 1;
iterator->current_namespace = &radiotap_ns;
iterator->is_radiotap_ns = 1;
goto next_entry;
case IEEE80211_RADIOTAP_EXT:
/*
* bit 31 was set, there is more
* -- move to next u32 bitmap
*/
iterator->_bitmap_shifter =
get_unaligned_le32(iterator->_next_bitmap);
iterator->_next_bitmap++;
if (iterator->_reset_on_ext)
iterator->_arg_index = 0;
else
iterator->_arg_index++;
iterator->_reset_on_ext = 0;
break;
default:
/* we've got a hit! */
hit = 1;
next_entry:
iterator->_bitmap_shifter >>= 1;
iterator->_arg_index++;
}
/* if we found a valid arg earlier, return it now */
if (hit)
return 0;
}
}
EXPORT_SYMBOL(ieee80211_radiotap_iterator_next);
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+186
View File
@@ -0,0 +1,186 @@
/* SPDX-License-Identifier: ISC */
#ifndef __NET_WIRELESS_REG_H
#define __NET_WIRELESS_REG_H
#include <net/cfg80211.h>
/*
* Copyright 2008-2011 Luis R. Rodriguez <mcgrof@qca.qualcomm.com>
* Copyright (C) 2019, 2023 Intel Corporation
*/
enum ieee80211_regd_source {
REGD_SOURCE_INTERNAL_DB,
REGD_SOURCE_CRDA,
REGD_SOURCE_CACHED,
};
extern const struct ieee80211_regdomain __rcu *cfg80211_regdomain;
bool reg_is_valid_request(const char *alpha2);
bool is_world_regdom(const char *alpha2);
bool reg_supported_dfs_region(enum nl80211_dfs_regions dfs_region);
enum nl80211_dfs_regions reg_get_dfs_region(struct wiphy *wiphy);
int regulatory_hint_user(const char *alpha2,
enum nl80211_user_reg_hint_type user_reg_hint_type);
/**
* regulatory_hint_indoor - hint operation in indoor env. or not
* @is_indoor: if true indicates that user space thinks that the
* device is operating in an indoor environment.
* @portid: the netlink port ID on which the hint was given.
*/
void regulatory_hint_indoor(bool is_indoor, u32 portid);
/**
* regulatory_netlink_notify - notify on released netlink socket
* @portid: the netlink socket port ID
*/
void regulatory_netlink_notify(u32 portid);
void wiphy_regulatory_register(struct wiphy *wiphy);
void wiphy_regulatory_deregister(struct wiphy *wiphy);
int __init regulatory_init(void);
void regulatory_exit(void);
int set_regdom(const struct ieee80211_regdomain *rd,
enum ieee80211_regd_source regd_src);
unsigned int reg_get_max_bandwidth(const struct ieee80211_regdomain *rd,
const struct ieee80211_reg_rule *rule);
bool reg_last_request_cell_base(void);
/**
* regulatory_hint_found_beacon - hints a beacon was found on a channel
* @wiphy: the wireless device where the beacon was found on
* @beacon_chan: the channel on which the beacon was found on
* @gfp: context flags
*
* This informs the wireless core that a beacon from an AP was found on
* the channel provided. This allows the wireless core to make educated
* guesses on regulatory to help with world roaming. This is only used for
* world roaming -- when we do not know our current location. This is
* only useful on channels 12, 13 and 14 on the 2 GHz band as channels
* 1-11 are already enabled by the world regulatory domain; and on
* non-radar 5 GHz channels.
*
* Drivers do not need to call this, cfg80211 will do it for after a scan
* on a newly found BSS. If you cannot make use of this feature you can
* set the wiphy->disable_beacon_hints to true.
*/
void regulatory_hint_found_beacon(struct wiphy *wiphy,
struct ieee80211_channel *beacon_chan,
gfp_t gfp);
/**
* regulatory_hint_country_ie - hints a country IE as a regulatory domain
* @wiphy: the wireless device giving the hint (used only for reporting
* conflicts)
* @band: the band on which the country IE was received on. This determines
* the band we'll process the country IE channel triplets for.
* @country_ie: pointer to the country IE
* @country_ie_len: length of the country IE
*
* We will intersect the rd with the what CRDA tells us should apply
* for the alpha2 this country IE belongs to, this prevents APs from
* sending us incorrect or outdated information against a country.
*
* The AP is expected to provide Country IE channel triplets for the
* band it is on. It is technically possible for APs to send channel
* country IE triplets even for channels outside of the band they are
* in but for that they would have to use the regulatory extension
* in combination with a triplet but this behaviour is currently
* not observed. For this reason if a triplet is seen with channel
* information for a band the BSS is not present in it will be ignored.
*/
void regulatory_hint_country_ie(struct wiphy *wiphy,
enum nl80211_band band,
const u8 *country_ie,
u8 country_ie_len);
/**
* regulatory_hint_disconnect - informs all devices have been disconnected
*
* Regulotory rules can be enhanced further upon scanning and upon
* connection to an AP. These rules become stale if we disconnect
* and go to another country, whether or not we suspend and resume.
* If we suspend, go to another country and resume we'll automatically
* get disconnected shortly after resuming and things will be reset as well.
* This routine is a helper to restore regulatory settings to how they were
* prior to our first connect attempt. This includes ignoring country IE and
* beacon regulatory hints. The ieee80211_regdom module parameter will always
* be respected but if a user had set the regulatory domain that will take
* precedence.
*
* Must be called from process context.
*/
void regulatory_hint_disconnect(void);
/**
* cfg80211_get_unii - get the U-NII band for the frequency
* @freq: the frequency for which we want to get the UNII band.
*
* Get a value specifying the U-NII band frequency belongs to.
* U-NII bands are defined by the FCC in C.F.R 47 part 15.
*
* Return: -EINVAL if freq is invalid, 0 for UNII-1, 1 for UNII-2A,
* 2 for UNII-2B, 3 for UNII-2C and 4 for UNII-3.
*/
int cfg80211_get_unii(int freq);
/**
* regulatory_indoor_allowed - is indoor operation allowed
* Return: %true if indoor operation is allowed, %false otherwise
*/
bool regulatory_indoor_allowed(void);
/*
* Grace period to timeout pre-CAC results on the dfs channels. This timeout
* value is used for Non-ETSI domain.
* TODO: May be make this timeout available through regdb?
*/
#define REG_PRE_CAC_EXPIRY_GRACE_MS 2000
/**
* regulatory_propagate_dfs_state - Propagate DFS channel state to other wiphys
* @wiphy: wiphy on which radar is detected and the event will be propagated
* to other available wiphys having the same DFS domain
* @chandef: Channel definition of radar detected channel
* @dfs_state: DFS channel state to be set
* @event: Type of radar event which triggered this DFS state change
*
* This function should be called with rtnl lock held.
*/
void regulatory_propagate_dfs_state(struct wiphy *wiphy,
struct cfg80211_chan_def *chandef,
enum nl80211_dfs_state dfs_state,
enum nl80211_radar_event event);
/**
* reg_dfs_domain_same - Checks if both wiphy have same DFS domain configured
* @wiphy1: wiphy it's dfs_region to be checked against that of wiphy2
* @wiphy2: wiphy it's dfs_region to be checked against that of wiphy1
* Return: %true if both wiphys have the same DFS domain, %false otherwise
*/
bool reg_dfs_domain_same(struct wiphy *wiphy1, struct wiphy *wiphy2);
/**
* reg_reload_regdb - reload the regulatory.db firmware file
* Return: 0 for success, an error code otherwise
*/
int reg_reload_regdb(void);
/**
* reg_check_channels - schedule regulatory enforcement
*/
void reg_check_channels(void);
extern const u8 shipped_regdb_certs[];
extern unsigned int shipped_regdb_certs_len;
extern const u8 extra_regdb_certs[];
extern unsigned int extra_regdb_certs_len;
#endif /* __NET_WIRELESS_REG_H */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+187
View File
@@ -0,0 +1,187 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* This file provides /sys/class/ieee80211/<wiphy name>/
* and some default attributes.
*
* Copyright 2005-2006 Jiri Benc <jbenc@suse.cz>
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
* Copyright (C) 2020-2021, 2023-2024 Intel Corporation
*/
#include <linux/device.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/nl80211.h>
#include <linux/rtnetlink.h>
#include <net/cfg80211.h>
#include "sysfs.h"
#include "core.h"
#include "rdev-ops.h"
static inline struct cfg80211_registered_device *dev_to_rdev(
struct device *dev)
{
return container_of(dev, struct cfg80211_registered_device, wiphy.dev);
}
#define SHOW_FMT(name, fmt, member) \
static ssize_t name ## _show(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
return sprintf(buf, fmt "\n", dev_to_rdev(dev)->member); \
} \
static DEVICE_ATTR_RO(name)
SHOW_FMT(index, "%d", wiphy_idx);
SHOW_FMT(macaddress, "%pM", wiphy.perm_addr);
SHOW_FMT(address_mask, "%pM", wiphy.addr_mask);
static ssize_t name_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct wiphy *wiphy = &dev_to_rdev(dev)->wiphy;
return sprintf(buf, "%s\n", wiphy_name(wiphy));
}
static DEVICE_ATTR_RO(name);
static ssize_t addresses_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct wiphy *wiphy = &dev_to_rdev(dev)->wiphy;
char *start = buf;
int i;
if (!wiphy->addresses)
return sprintf(buf, "%pM\n", wiphy->perm_addr);
for (i = 0; i < wiphy->n_addresses; i++)
buf += sprintf(buf, "%pM\n", wiphy->addresses[i].addr);
return buf - start;
}
static DEVICE_ATTR_RO(addresses);
static struct attribute *ieee80211_attrs[] = {
&dev_attr_index.attr,
&dev_attr_macaddress.attr,
&dev_attr_address_mask.attr,
&dev_attr_addresses.attr,
&dev_attr_name.attr,
NULL,
};
ATTRIBUTE_GROUPS(ieee80211);
static void wiphy_dev_release(struct device *dev)
{
struct cfg80211_registered_device *rdev = dev_to_rdev(dev);
cfg80211_dev_free(rdev);
}
#ifdef CONFIG_PM_SLEEP
static void cfg80211_leave_all(struct cfg80211_registered_device *rdev)
{
struct wireless_dev *wdev;
list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list)
cfg80211_leave(rdev, wdev, -1);
}
static int wiphy_suspend(struct device *dev)
{
struct cfg80211_registered_device *rdev = dev_to_rdev(dev);
int ret = 0;
rdev->suspend_at = ktime_get_boottime_seconds();
rtnl_lock();
if (!rdev->wiphy.registered)
goto out_unlock_rtnl;
if (rdev->wiphy.wowlan_config) {
scoped_guard(wiphy, &rdev->wiphy) {
cfg80211_process_wiphy_works(rdev, NULL);
if (rdev->ops->suspend)
ret = rdev_suspend(rdev,
rdev->wiphy.wowlan_config);
if (ret <= 0)
goto out_unlock_rtnl;
}
}
/* Driver refused to configure wowlan (ret = 1) or no wowlan */
cfg80211_leave_all(rdev);
scoped_guard(wiphy, &rdev->wiphy) {
cfg80211_process_rdev_events(rdev);
cfg80211_process_wiphy_works(rdev, NULL);
if (rdev->ops->suspend)
ret = rdev_suspend(rdev, NULL);
}
out_unlock_rtnl:
if (ret == 0)
rdev->suspended = true;
rtnl_unlock();
return ret;
}
static int wiphy_resume(struct device *dev)
{
struct cfg80211_registered_device *rdev = dev_to_rdev(dev);
int ret = 0;
/* Age scan results with time spent in suspend */
cfg80211_bss_age(rdev, ktime_get_boottime_seconds() - rdev->suspend_at);
rtnl_lock();
wiphy_lock(&rdev->wiphy);
if (rdev->wiphy.registered && rdev->ops->resume)
ret = rdev_resume(rdev);
rdev->suspended = false;
queue_work(system_dfl_wq, &rdev->wiphy_work);
wiphy_unlock(&rdev->wiphy);
if (ret)
cfg80211_shutdown_all_interfaces(&rdev->wiphy);
rtnl_unlock();
return ret;
}
static SIMPLE_DEV_PM_OPS(wiphy_pm_ops, wiphy_suspend, wiphy_resume);
#define WIPHY_PM_OPS (&wiphy_pm_ops)
#else
#define WIPHY_PM_OPS NULL
#endif
static const struct ns_common *wiphy_namespace(const struct device *d)
{
struct wiphy *wiphy = container_of(d, struct wiphy, dev);
return to_ns_common(wiphy_net(wiphy));
}
struct class ieee80211_class = {
.name = "ieee80211",
.dev_release = wiphy_dev_release,
.dev_groups = ieee80211_groups,
.pm = WIPHY_PM_OPS,
.ns_type = &net_ns_type_operations,
.namespace = wiphy_namespace,
};
int wiphy_sysfs_init(void)
{
return class_register(&ieee80211_class);
}
void wiphy_sysfs_exit(void)
{
class_unregister(&ieee80211_class);
}
+10
View File
@@ -0,0 +1,10 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __WIRELESS_SYSFS_H
#define __WIRELESS_SYSFS_H
int wiphy_sysfs_init(void);
void wiphy_sysfs_exit(void);
extern struct class ieee80211_class;
#endif /* __WIRELESS_SYSFS_H */
+3
View File
@@ -0,0 +1,3 @@
cfg80211-tests-y += module.o fragmentation.o scan.o util.o chan.o
obj-$(CONFIG_CFG80211_KUNIT_TEST) += cfg80211-tests.o
+408
View File
@@ -0,0 +1,408 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* KUnit tests for channel helper functions
*
* Copyright (C) 2023-2024, 2026 Intel Corporation
*/
#include <net/cfg80211.h>
#include <kunit/test.h>
MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
static struct ieee80211_channel chan_2ghz_1 = {
.band = NL80211_BAND_2GHZ,
.center_freq = 2412,
};
static struct ieee80211_channel chan_6ghz_1 = {
.band = NL80211_BAND_6GHZ,
.center_freq = 5955,
};
static struct ieee80211_channel chan_6ghz_5 = {
.band = NL80211_BAND_6GHZ,
.center_freq = 5975,
};
static struct ieee80211_channel chan_6ghz_105 = {
.band = NL80211_BAND_6GHZ,
.center_freq = 6475,
};
static const struct chandef_compat_case {
const char *desc;
/* leave c1 empty for tests for identical */
struct cfg80211_chan_def c1, c2;
/* we test both ways around, so c2 should always be the compat one */
bool compat;
} chandef_compat_cases[] = {
{
.desc = "identical non-HT",
.c2 = {
.width = NL80211_CHAN_WIDTH_20_NOHT,
.chan = &chan_6ghz_1,
.center_freq1 = 5955,
},
.compat = true,
},
{
.desc = "identical 20 MHz",
.c2 = {
.width = NL80211_CHAN_WIDTH_20,
.chan = &chan_6ghz_1,
.center_freq1 = 5955,
},
.compat = true,
},
{
.desc = "identical 40 MHz",
.c2 = {
.width = NL80211_CHAN_WIDTH_40,
.chan = &chan_6ghz_1,
.center_freq1 = 5955 + 10,
},
.compat = true,
},
{
.desc = "identical 80 MHz",
.c2 = {
.width = NL80211_CHAN_WIDTH_80,
.chan = &chan_6ghz_1,
.center_freq1 = 5955 + 10 + 20,
},
.compat = true,
},
{
.desc = "identical 160 MHz",
.c2 = {
.width = NL80211_CHAN_WIDTH_160,
.chan = &chan_6ghz_1,
.center_freq1 = 5955 + 10 + 20 + 40,
},
.compat = true,
},
{
.desc = "identical 320 MHz",
.c2 = {
.width = NL80211_CHAN_WIDTH_320,
.chan = &chan_6ghz_1,
.center_freq1 = 5955 + 10 + 20 + 40 + 80,
},
.compat = true,
},
{
.desc = "20 MHz in 320 MHz\n",
.c1 = {
.width = NL80211_CHAN_WIDTH_20,
.chan = &chan_6ghz_1,
.center_freq1 = 5955,
},
.c2 = {
.width = NL80211_CHAN_WIDTH_320,
.chan = &chan_6ghz_1,
.center_freq1 = 5955 + 10 + 20 + 40 + 80,
},
.compat = true,
},
{
.desc = "different 20 MHz",
.c1 = {
.width = NL80211_CHAN_WIDTH_20,
.chan = &chan_6ghz_1,
.center_freq1 = 5955,
},
.c2 = {
.width = NL80211_CHAN_WIDTH_20,
.chan = &chan_6ghz_5,
.center_freq1 = 5975,
},
},
{
.desc = "different primary 320 MHz",
.c1 = {
.width = NL80211_CHAN_WIDTH_320,
.chan = &chan_6ghz_105,
.center_freq1 = 6475 + 110,
},
.c2 = {
.width = NL80211_CHAN_WIDTH_320,
.chan = &chan_6ghz_105,
.center_freq1 = 6475 - 50,
},
},
{
/* similar to previous test but one has lower BW */
.desc = "matching primary 160 MHz",
.c1 = {
.width = NL80211_CHAN_WIDTH_160,
.chan = &chan_6ghz_105,
.center_freq1 = 6475 + 30,
},
.c2 = {
.width = NL80211_CHAN_WIDTH_320,
.chan = &chan_6ghz_105,
.center_freq1 = 6475 - 50,
},
.compat = true,
},
{
.desc = "matching primary 160 MHz & punctured secondary 160 Mhz",
.c1 = {
.width = NL80211_CHAN_WIDTH_160,
.chan = &chan_6ghz_105,
.center_freq1 = 6475 + 30,
},
.c2 = {
.width = NL80211_CHAN_WIDTH_320,
.chan = &chan_6ghz_105,
.center_freq1 = 6475 - 50,
.punctured = 0xf,
},
.compat = true,
},
{
.desc = "matching primary 160 MHz & punctured matching",
.c1 = {
.width = NL80211_CHAN_WIDTH_160,
.chan = &chan_6ghz_105,
.center_freq1 = 6475 + 30,
.punctured = 0xc0,
},
.c2 = {
.width = NL80211_CHAN_WIDTH_320,
.chan = &chan_6ghz_105,
.center_freq1 = 6475 - 50,
.punctured = 0xc000,
},
.compat = true,
},
{
.desc = "matching primary 160 MHz & punctured not matching",
.c1 = {
.width = NL80211_CHAN_WIDTH_160,
.chan = &chan_6ghz_105,
.center_freq1 = 6475 + 30,
.punctured = 0x80,
},
.c2 = {
.width = NL80211_CHAN_WIDTH_320,
.chan = &chan_6ghz_105,
.center_freq1 = 6475 - 50,
.punctured = 0xc000,
},
},
};
KUNIT_ARRAY_PARAM_DESC(chandef_compat, chandef_compat_cases, desc)
static void test_chandef_compat(struct kunit *test)
{
const struct chandef_compat_case *params = test->param_value;
const struct cfg80211_chan_def *ret, *expect;
struct cfg80211_chan_def c1 = params->c1;
/* tests with identical ones */
if (!params->c1.chan)
c1 = params->c2;
KUNIT_EXPECT_EQ(test, cfg80211_chandef_valid(&c1), true);
KUNIT_EXPECT_EQ(test, cfg80211_chandef_valid(&params->c2), true);
expect = params->compat ? &params->c2 : NULL;
ret = cfg80211_chandef_compatible(&c1, &params->c2);
KUNIT_EXPECT_PTR_EQ(test, ret, expect);
if (!params->c1.chan)
expect = &c1;
ret = cfg80211_chandef_compatible(&params->c2, &c1);
KUNIT_EXPECT_PTR_EQ(test, ret, expect);
}
static const struct chandef_dbe_case {
const char *desc;
struct cfg80211_chan_def c;
u8 dbe[3];
bool fails;
u16 cf1;
} chandef_dbe_cases[] = {
{
.desc = "non-HT failure",
.c = {
.width = NL80211_CHAN_WIDTH_20_NOHT,
.chan = &chan_6ghz_1,
.center_freq1 = 5955,
},
.dbe[0] = IEEE80211_UHR_DBE_OPER_BW_40,
.fails = true,
},
{
.desc = "2.4 GHz fails",
.c = {
.width = NL80211_CHAN_WIDTH_20,
.chan = &chan_2ghz_1,
.center_freq1 = 2412,
},
.dbe[0] = IEEE80211_UHR_DBE_OPER_BW_40,
.fails = true,
},
{
.desc = "DBE narrower",
.c = {
.width = NL80211_CHAN_WIDTH_320,
.chan = &chan_6ghz_1,
.center_freq1 = 5955 + 10 + 20 + 40 + 80,
},
.dbe[0] = IEEE80211_UHR_DBE_OPER_BW_160,
.fails = true,
},
{
.desc = "DBE to 320-1",
.c = {
.width = NL80211_CHAN_WIDTH_160,
.chan = &chan_6ghz_105,
.center_freq1 = 6475 + 30,
},
.dbe[0] = IEEE80211_UHR_DBE_OPER_BW_320_1,
.cf1 = 6425,
},
{
.desc = "DBE to 320-2",
.c = {
.width = NL80211_CHAN_WIDTH_160,
.chan = &chan_6ghz_105,
.center_freq1 = 6475 + 30,
},
.dbe[0] = IEEE80211_UHR_DBE_OPER_BW_320_2,
.cf1 = 6585,
},
{
.desc = "bad disabled subchannel bitmap - not enough in BSS (1)",
.c = {
.width = NL80211_CHAN_WIDTH_160,
.chan = &chan_6ghz_105,
.center_freq1 = 6475 + 30,
.punctured = 0x0001,
},
.dbe[0] = IEEE80211_UHR_DBE_OPER_BW_320_1 |
IEEE80211_UHR_DBE_OPER_DIS_SUBCHANNEL_BITMAP_PRES,
/* DBE disabled subchannel bitmap == 0 */
.fails = true,
},
{
.desc = "bad disabled subchannel bitmap - too much in BSS (1)",
.c = {
.width = NL80211_CHAN_WIDTH_160,
.chan = &chan_6ghz_105,
.center_freq1 = 6475 + 30,
.punctured = 0x0001,
},
.dbe[0] = IEEE80211_UHR_DBE_OPER_BW_320_1 |
IEEE80211_UHR_DBE_OPER_DIS_SUBCHANNEL_BITMAP_PRES,
/* DBE disabled subchannel bitmap == 0x0300 */
.dbe[2] = 0x03,
.fails = true,
},
{
.desc = "bad disabled subchannel bitmap - not enough in BSS (2)",
.c = {
.width = NL80211_CHAN_WIDTH_160,
.chan = &chan_6ghz_105,
.center_freq1 = 6475 + 30,
.punctured = 0x0001,
},
.dbe[0] = IEEE80211_UHR_DBE_OPER_BW_320_2 |
IEEE80211_UHR_DBE_OPER_DIS_SUBCHANNEL_BITMAP_PRES,
/* DBE disabled subchannel bitmap == 0 */
.fails = true,
},
{
.desc = "bad disabled subchannel bitmap - too much in BSS (2)",
.c = {
.width = NL80211_CHAN_WIDTH_160,
.chan = &chan_6ghz_105,
.center_freq1 = 6475 + 30,
.punctured = 0x0001,
},
.dbe[0] = IEEE80211_UHR_DBE_OPER_BW_320_2 |
IEEE80211_UHR_DBE_OPER_DIS_SUBCHANNEL_BITMAP_PRES,
/* DBE disabled subchannel bitmap == 0x03 */
.dbe[1] = 0x03,
.fails = true,
},
{
.desc = "bad disabled subchannel bitmap - bad bitmap",
.c = {
.width = NL80211_CHAN_WIDTH_160,
.chan = &chan_6ghz_105,
.center_freq1 = 6475 + 30,
.punctured = 0x0001,
},
.dbe[0] = IEEE80211_UHR_DBE_OPER_BW_320_1 |
IEEE80211_UHR_DBE_OPER_DIS_SUBCHANNEL_BITMAP_PRES,
/* DBE disabled subchannel bitmap == 0x1100 */
.dbe[2] = 0x11,
.fails = true,
},
{
.desc = "good disabled subchannel bitmap (1)",
.c = {
.width = NL80211_CHAN_WIDTH_160,
.chan = &chan_6ghz_105,
.center_freq1 = 6475 + 30,
.punctured = 0x0003,
},
.dbe[0] = IEEE80211_UHR_DBE_OPER_BW_320_1 |
IEEE80211_UHR_DBE_OPER_DIS_SUBCHANNEL_BITMAP_PRES,
/* DBE disabled subchannel bitmap == 0x0300 */
.dbe[2] = 0x03,
.cf1 = 6425,
},
{
.desc = "good disabled subchannel bitmap (2)",
.c = {
.width = NL80211_CHAN_WIDTH_160,
.chan = &chan_6ghz_105,
.center_freq1 = 6475 + 30,
.punctured = 0x0003,
},
.dbe[0] = IEEE80211_UHR_DBE_OPER_BW_320_2 |
IEEE80211_UHR_DBE_OPER_DIS_SUBCHANNEL_BITMAP_PRES,
/* DBE disabled subchannel bitmap == 0x0003 */
.dbe[1] = 0x03,
.cf1 = 6585,
},
};
KUNIT_ARRAY_PARAM_DESC(chandef_dbe, chandef_dbe_cases, desc)
static void test_chandef_dbe(struct kunit *test)
{
const struct chandef_dbe_case *params = test->param_value;
struct cfg80211_chan_def c = params->c;
int ret;
KUNIT_EXPECT_EQ(test, cfg80211_chandef_valid(&params->c), true);
ret = cfg80211_chandef_add_dbe(&c, (void *)params->dbe);
KUNIT_EXPECT_EQ(test, ret != 0, params->fails);
if (params->fails)
return;
KUNIT_EXPECT_EQ(test, c.center_freq1, params->cf1);
}
static struct kunit_case chandef_test_cases[] = {
KUNIT_CASE_PARAM(test_chandef_compat, chandef_compat_gen_params),
KUNIT_CASE_PARAM(test_chandef_dbe, chandef_dbe_gen_params),
{}
};
static struct kunit_suite chandef = {
.name = "cfg80211-chandef",
.test_cases = chandef_test_cases,
};
kunit_test_suite(chandef);
@@ -0,0 +1,177 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* KUnit tests for element fragmentation
*
* Copyright (C) 2023-2024 Intel Corporation
*/
#include <linux/ieee80211.h>
#include <net/cfg80211.h>
#include <kunit/test.h>
static void defragment_0(struct kunit *test)
{
ssize_t ret;
static const u8 input[] = {
[0] = WLAN_EID_EXTENSION,
[1] = 254,
[2] = WLAN_EID_EXT_EHT_MULTI_LINK,
[27] = 27,
[123] = 123,
[254 + 2] = WLAN_EID_FRAGMENT,
[254 + 3] = 7,
[254 + 3 + 7] = 0, /* for size */
};
u8 *data = kunit_kzalloc(test, sizeof(input), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, data);
ret = cfg80211_defragment_element((void *)input,
input, sizeof(input),
NULL, 0,
WLAN_EID_FRAGMENT);
KUNIT_EXPECT_EQ(test, ret, 253);
ret = cfg80211_defragment_element((void *)input,
input, sizeof(input),
data, ret,
WLAN_EID_FRAGMENT);
KUNIT_EXPECT_EQ(test, ret, 253);
KUNIT_EXPECT_MEMEQ(test, data, input + 3, 253);
}
static void defragment_1(struct kunit *test)
{
ssize_t ret;
static const u8 input[] = {
[0] = WLAN_EID_EXTENSION,
[1] = 255,
[2] = WLAN_EID_EXT_EHT_MULTI_LINK,
[27] = 27,
[123] = 123,
[255 + 2] = WLAN_EID_FRAGMENT,
[255 + 3] = 7,
[255 + 3 + 1] = 0xaa,
[255 + 3 + 8] = WLAN_EID_FRAGMENT, /* not used */
[255 + 3 + 9] = 1,
[255 + 3 + 10] = 0, /* for size */
};
u8 *data = kunit_kzalloc(test, sizeof(input), GFP_KERNEL);
const struct element *elem;
int count = 0;
KUNIT_ASSERT_NOT_NULL(test, data);
for_each_element(elem, input, sizeof(input))
count++;
/* check the elements are right */
KUNIT_ASSERT_EQ(test, count, 3);
ret = cfg80211_defragment_element((void *)input,
input, sizeof(input),
NULL, 0,
WLAN_EID_FRAGMENT);
KUNIT_EXPECT_EQ(test, ret, 254 + 7);
ret = cfg80211_defragment_element((void *)input,
input, sizeof(input),
data, ret,
WLAN_EID_FRAGMENT);
/* this means the last fragment was not used */
KUNIT_EXPECT_EQ(test, ret, 254 + 7);
KUNIT_EXPECT_MEMEQ(test, data, input + 3, 254);
KUNIT_EXPECT_MEMEQ(test, data + 254, input + 255 + 4, 7);
}
static void defragment_2(struct kunit *test)
{
ssize_t ret;
static const u8 input[] = {
[0] = WLAN_EID_EXTENSION,
[1] = 255,
[2] = WLAN_EID_EXT_EHT_MULTI_LINK,
[27] = 27,
[123] = 123,
[257 + 0] = WLAN_EID_FRAGMENT,
[257 + 1] = 255,
[257 + 20] = 0xaa,
[2 * 257 + 0] = WLAN_EID_FRAGMENT,
[2 * 257 + 1] = 1,
[2 * 257 + 2] = 0xcc,
[2 * 257 + 3] = WLAN_EID_FRAGMENT, /* not used */
[2 * 257 + 4] = 1,
[2 * 257 + 5] = 0, /* for size */
};
u8 *data = kunit_kzalloc(test, sizeof(input), GFP_KERNEL);
const struct element *elem;
int count = 0;
KUNIT_ASSERT_NOT_NULL(test, data);
for_each_element(elem, input, sizeof(input))
count++;
/* check the elements are right */
KUNIT_ASSERT_EQ(test, count, 4);
ret = cfg80211_defragment_element((void *)input,
input, sizeof(input),
NULL, 0,
WLAN_EID_FRAGMENT);
/* this means the last fragment was not used */
KUNIT_EXPECT_EQ(test, ret, 254 + 255 + 1);
ret = cfg80211_defragment_element((void *)input,
input, sizeof(input),
data, ret,
WLAN_EID_FRAGMENT);
KUNIT_EXPECT_EQ(test, ret, 254 + 255 + 1);
KUNIT_EXPECT_MEMEQ(test, data, input + 3, 254);
KUNIT_EXPECT_MEMEQ(test, data + 254, input + 257 + 2, 255);
KUNIT_EXPECT_MEMEQ(test, data + 254 + 255, input + 2 * 257 + 2, 1);
}
static void defragment_at_end(struct kunit *test)
{
ssize_t ret;
static const u8 input[] = {
[0] = WLAN_EID_EXTENSION,
[1] = 255,
[2] = WLAN_EID_EXT_EHT_MULTI_LINK,
[27] = 27,
[123] = 123,
[255 + 2] = WLAN_EID_FRAGMENT,
[255 + 3] = 7,
[255 + 3 + 7] = 0, /* for size */
};
u8 *data = kunit_kzalloc(test, sizeof(input), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, data);
ret = cfg80211_defragment_element((void *)input,
input, sizeof(input),
NULL, 0,
WLAN_EID_FRAGMENT);
KUNIT_EXPECT_EQ(test, ret, 254 + 7);
ret = cfg80211_defragment_element((void *)input,
input, sizeof(input),
data, ret,
WLAN_EID_FRAGMENT);
KUNIT_EXPECT_EQ(test, ret, 254 + 7);
KUNIT_EXPECT_MEMEQ(test, data, input + 3, 254);
KUNIT_EXPECT_MEMEQ(test, data + 254, input + 255 + 4, 7);
}
static struct kunit_case element_fragmentation_test_cases[] = {
KUNIT_CASE(defragment_0),
KUNIT_CASE(defragment_1),
KUNIT_CASE(defragment_2),
KUNIT_CASE(defragment_at_end),
{}
};
static struct kunit_suite element_fragmentation = {
.name = "cfg80211-element-defragmentation",
.test_cases = element_fragmentation_test_cases,
};
kunit_test_suite(element_fragmentation);
+10
View File
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* This is just module boilerplate for the cfg80211 kunit module.
*
* Copyright (C) 2023 Intel Corporation
*/
#include <linux/module.h>
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("tests for cfg80211");
+880
View File
@@ -0,0 +1,880 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* KUnit tests for inform_bss functions
*
* Copyright (C) 2023-2024 Intel Corporation
*/
#include <linux/ieee80211.h>
#include <net/cfg80211.h>
#include <kunit/test.h>
#include <kunit/skbuff.h>
#include "../core.h"
#include "util.h"
/* mac80211 helpers for element building */
#include "../../mac80211/ieee80211_i.h"
MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
struct test_elem {
u8 id;
u8 len;
union {
u8 data[255];
struct {
u8 eid;
u8 edata[254];
};
};
};
static struct gen_new_ie_case {
const char *desc;
struct test_elem parent_ies[16];
struct test_elem child_ies[16];
struct test_elem result_ies[16];
} gen_new_ie_cases[] = {
{
.desc = "ML not inherited",
.parent_ies = {
{ .id = WLAN_EID_EXTENSION, .len = 255,
.eid = WLAN_EID_EXT_EHT_MULTI_LINK },
},
.child_ies = {
{ .id = WLAN_EID_SSID, .len = 2 },
},
.result_ies = {
{ .id = WLAN_EID_SSID, .len = 2 },
},
},
{
.desc = "fragments are ignored if previous len not 255",
.parent_ies = {
{ .id = WLAN_EID_REDUCED_NEIGHBOR_REPORT, .len = 254, },
{ .id = WLAN_EID_FRAGMENT, .len = 125, },
},
.child_ies = {
{ .id = WLAN_EID_SSID, .len = 2 },
{ .id = WLAN_EID_FRAGMENT, .len = 125, },
},
.result_ies = {
{ .id = WLAN_EID_REDUCED_NEIGHBOR_REPORT, .len = 254, },
{ .id = WLAN_EID_SSID, .len = 2 },
},
},
{
.desc = "fragments inherited",
.parent_ies = {
{ .id = WLAN_EID_REDUCED_NEIGHBOR_REPORT, .len = 255, },
{ .id = WLAN_EID_FRAGMENT, .len = 125, },
},
.child_ies = {
{ .id = WLAN_EID_SSID, .len = 2 },
},
.result_ies = {
{ .id = WLAN_EID_REDUCED_NEIGHBOR_REPORT, .len = 255, },
{ .id = WLAN_EID_FRAGMENT, .len = 125, },
{ .id = WLAN_EID_SSID, .len = 2 },
},
},
{
.desc = "fragments copied",
.parent_ies = {
{ .id = WLAN_EID_REDUCED_NEIGHBOR_REPORT, .len = 255, },
{ .id = WLAN_EID_FRAGMENT, .len = 125, },
},
.child_ies = {
{ .id = WLAN_EID_SSID, .len = 2 },
},
.result_ies = {
{ .id = WLAN_EID_REDUCED_NEIGHBOR_REPORT, .len = 255, },
{ .id = WLAN_EID_FRAGMENT, .len = 125, },
{ .id = WLAN_EID_SSID, .len = 2 },
},
},
{
.desc = "multiple elements inherit",
.parent_ies = {
{ .id = WLAN_EID_REDUCED_NEIGHBOR_REPORT, .len = 255, },
{ .id = WLAN_EID_FRAGMENT, .len = 125, },
{ .id = WLAN_EID_REDUCED_NEIGHBOR_REPORT, .len = 123, },
},
.child_ies = {
{ .id = WLAN_EID_SSID, .len = 2 },
},
.result_ies = {
{ .id = WLAN_EID_REDUCED_NEIGHBOR_REPORT, .len = 255, },
{ .id = WLAN_EID_FRAGMENT, .len = 125, },
{ .id = WLAN_EID_REDUCED_NEIGHBOR_REPORT, .len = 123, },
{ .id = WLAN_EID_SSID, .len = 2 },
},
},
{
.desc = "one child element overrides",
.parent_ies = {
{ .id = WLAN_EID_REDUCED_NEIGHBOR_REPORT, .len = 255, },
{ .id = WLAN_EID_FRAGMENT, .len = 125, },
{ .id = WLAN_EID_REDUCED_NEIGHBOR_REPORT, .len = 123, },
},
.child_ies = {
{ .id = WLAN_EID_REDUCED_NEIGHBOR_REPORT, .len = 127, },
{ .id = WLAN_EID_SSID, .len = 2 },
},
.result_ies = {
{ .id = WLAN_EID_REDUCED_NEIGHBOR_REPORT, .len = 127, },
{ .id = WLAN_EID_SSID, .len = 2 },
},
},
{
.desc = "empty elements from parent",
.parent_ies = {
{ .id = 0x1, .len = 0, },
{ .id = WLAN_EID_EXTENSION, .len = 1, .eid = 0x10 },
},
.child_ies = {
},
.result_ies = {
{ .id = 0x1, .len = 0, },
{ .id = WLAN_EID_EXTENSION, .len = 1, .eid = 0x10 },
},
},
{
.desc = "empty elements from child",
.parent_ies = {
},
.child_ies = {
{ .id = 0x1, .len = 0, },
{ .id = WLAN_EID_EXTENSION, .len = 1, .eid = 0x10 },
},
.result_ies = {
{ .id = 0x1, .len = 0, },
{ .id = WLAN_EID_EXTENSION, .len = 1, .eid = 0x10 },
},
},
{
.desc = "invalid extended elements ignored",
.parent_ies = {
{ .id = WLAN_EID_EXTENSION, .len = 0 },
},
.child_ies = {
{ .id = WLAN_EID_EXTENSION, .len = 0 },
},
.result_ies = {
},
},
{
.desc = "multiple extended elements",
.parent_ies = {
{ .id = WLAN_EID_EXTENSION, .len = 3,
.eid = WLAN_EID_EXT_HE_CAPABILITY },
{ .id = WLAN_EID_EXTENSION, .len = 5,
.eid = WLAN_EID_EXT_ASSOC_DELAY_INFO },
{ .id = WLAN_EID_EXTENSION, .len = 7,
.eid = WLAN_EID_EXT_HE_OPERATION },
{ .id = WLAN_EID_EXTENSION, .len = 11,
.eid = WLAN_EID_EXT_FILS_REQ_PARAMS },
},
.child_ies = {
{ .id = WLAN_EID_SSID, .len = 13 },
{ .id = WLAN_EID_EXTENSION, .len = 17,
.eid = WLAN_EID_EXT_HE_CAPABILITY },
{ .id = WLAN_EID_EXTENSION, .len = 11,
.eid = WLAN_EID_EXT_FILS_KEY_CONFIRM },
{ .id = WLAN_EID_EXTENSION, .len = 19,
.eid = WLAN_EID_EXT_HE_OPERATION },
},
.result_ies = {
{ .id = WLAN_EID_EXTENSION, .len = 17,
.eid = WLAN_EID_EXT_HE_CAPABILITY },
{ .id = WLAN_EID_EXTENSION, .len = 5,
.eid = WLAN_EID_EXT_ASSOC_DELAY_INFO },
{ .id = WLAN_EID_EXTENSION, .len = 19,
.eid = WLAN_EID_EXT_HE_OPERATION },
{ .id = WLAN_EID_EXTENSION, .len = 11,
.eid = WLAN_EID_EXT_FILS_REQ_PARAMS },
{ .id = WLAN_EID_SSID, .len = 13 },
{ .id = WLAN_EID_EXTENSION, .len = 11,
.eid = WLAN_EID_EXT_FILS_KEY_CONFIRM },
},
},
{
.desc = "non-inherit element",
.parent_ies = {
{ .id = 0x1, .len = 7, },
{ .id = 0x2, .len = 11, },
{ .id = 0x3, .len = 13, },
{ .id = WLAN_EID_EXTENSION, .len = 17, .eid = 0x10 },
{ .id = WLAN_EID_EXTENSION, .len = 19, .eid = 0x11 },
{ .id = WLAN_EID_EXTENSION, .len = 23, .eid = 0x12 },
{ .id = WLAN_EID_EXTENSION, .len = 29, .eid = 0x14 },
},
.child_ies = {
{ .id = WLAN_EID_EXTENSION,
.eid = WLAN_EID_EXT_NON_INHERITANCE,
.len = 10,
.edata = { 0x3, 0x1, 0x2, 0x3,
0x4, 0x10, 0x11, 0x13, 0x14 } },
{ .id = WLAN_EID_SSID, .len = 2 },
},
.result_ies = {
{ .id = WLAN_EID_EXTENSION, .len = 23, .eid = 0x12 },
{ .id = WLAN_EID_SSID, .len = 2 },
},
},
};
KUNIT_ARRAY_PARAM_DESC(gen_new_ie, gen_new_ie_cases, desc)
static void test_gen_new_ie(struct kunit *test)
{
const struct gen_new_ie_case *params = test->param_value;
struct sk_buff *parent = kunit_zalloc_skb(test, 1024, GFP_KERNEL);
struct sk_buff *child = kunit_zalloc_skb(test, 1024, GFP_KERNEL);
struct sk_buff *reference = kunit_zalloc_skb(test, 1024, GFP_KERNEL);
u8 *out = kunit_kzalloc(test, IEEE80211_MAX_DATA_LEN, GFP_KERNEL);
size_t len;
int i;
KUNIT_ASSERT_NOT_NULL(test, parent);
KUNIT_ASSERT_NOT_NULL(test, child);
KUNIT_ASSERT_NOT_NULL(test, reference);
KUNIT_ASSERT_NOT_NULL(test, out);
for (i = 0; i < ARRAY_SIZE(params->parent_ies); i++) {
if (params->parent_ies[i].len != 0) {
skb_put_u8(parent, params->parent_ies[i].id);
skb_put_u8(parent, params->parent_ies[i].len);
skb_put_data(parent, params->parent_ies[i].data,
params->parent_ies[i].len);
}
if (params->child_ies[i].len != 0) {
skb_put_u8(child, params->child_ies[i].id);
skb_put_u8(child, params->child_ies[i].len);
skb_put_data(child, params->child_ies[i].data,
params->child_ies[i].len);
}
if (params->result_ies[i].len != 0) {
skb_put_u8(reference, params->result_ies[i].id);
skb_put_u8(reference, params->result_ies[i].len);
skb_put_data(reference, params->result_ies[i].data,
params->result_ies[i].len);
}
}
len = cfg80211_gen_new_ie(parent->data, parent->len,
child->data, child->len,
out, IEEE80211_MAX_DATA_LEN);
KUNIT_EXPECT_EQ(test, len, reference->len);
KUNIT_EXPECT_MEMEQ(test, out, reference->data, reference->len);
memset(out, 0, IEEE80211_MAX_DATA_LEN);
/* Exactly enough space */
len = cfg80211_gen_new_ie(parent->data, parent->len,
child->data, child->len,
out, reference->len);
KUNIT_EXPECT_EQ(test, len, reference->len);
KUNIT_EXPECT_MEMEQ(test, out, reference->data, reference->len);
memset(out, 0, IEEE80211_MAX_DATA_LEN);
/* Not enough space (or expected zero length) */
len = cfg80211_gen_new_ie(parent->data, parent->len,
child->data, child->len,
out, reference->len - 1);
KUNIT_EXPECT_EQ(test, len, 0);
}
static void test_gen_new_ie_malformed(struct kunit *test)
{
struct sk_buff *malformed = kunit_zalloc_skb(test, 1024, GFP_KERNEL);
u8 *out = kunit_kzalloc(test, IEEE80211_MAX_DATA_LEN, GFP_KERNEL);
size_t len;
KUNIT_ASSERT_NOT_NULL(test, malformed);
KUNIT_ASSERT_NOT_NULL(test, out);
skb_put_u8(malformed, WLAN_EID_SSID);
skb_put_u8(malformed, 3);
skb_put(malformed, 3);
skb_put_u8(malformed, WLAN_EID_REDUCED_NEIGHBOR_REPORT);
skb_put_u8(malformed, 10);
skb_put(malformed, 9);
len = cfg80211_gen_new_ie(malformed->data, malformed->len,
out, 0,
out, IEEE80211_MAX_DATA_LEN);
KUNIT_EXPECT_EQ(test, len, 5);
len = cfg80211_gen_new_ie(out, 0,
malformed->data, malformed->len,
out, IEEE80211_MAX_DATA_LEN);
KUNIT_EXPECT_EQ(test, len, 5);
}
struct inform_bss {
struct kunit *test;
int inform_bss_count;
};
static void inform_bss_inc_counter(struct wiphy *wiphy,
struct cfg80211_bss *bss,
const struct cfg80211_bss_ies *ies,
void *drv_data)
{
struct inform_bss *ctx = t_wiphy_ctx(wiphy);
ctx->inform_bss_count++;
rcu_read_lock();
KUNIT_EXPECT_PTR_EQ(ctx->test, drv_data, ctx);
KUNIT_EXPECT_PTR_EQ(ctx->test, ies, rcu_dereference(bss->ies));
rcu_read_unlock();
}
static void test_inform_bss_ssid_only(struct kunit *test)
{
struct inform_bss ctx = {
.test = test,
};
struct wiphy *wiphy = T_WIPHY(test, ctx);
struct t_wiphy_priv *w_priv = wiphy_priv(wiphy);
struct cfg80211_inform_bss inform_bss = {
.signal = 50,
.drv_data = &ctx,
};
const u8 bssid[ETH_ALEN] = { 0x10, 0x22, 0x33, 0x44, 0x55, 0x66 };
u64 tsf = 0x1000000000000000ULL;
int beacon_int = 100;
u16 capability = 0x1234;
static const u8 input[] = {
[0] = WLAN_EID_SSID,
[1] = 4,
[2] = 'T', 'E', 'S', 'T'
};
struct cfg80211_bss *bss, *other;
const struct cfg80211_bss_ies *ies;
w_priv->ops->inform_bss = inform_bss_inc_counter;
inform_bss.chan = ieee80211_get_channel_khz(wiphy, MHZ_TO_KHZ(2412));
KUNIT_ASSERT_NOT_NULL(test, inform_bss.chan);
bss = cfg80211_inform_bss_data(wiphy, &inform_bss,
CFG80211_BSS_FTYPE_PRESP, bssid, tsf,
capability, beacon_int,
input, sizeof(input),
GFP_KERNEL);
KUNIT_EXPECT_NOT_NULL(test, bss);
KUNIT_EXPECT_EQ(test, ctx.inform_bss_count, 1);
/* Check values in returned bss are correct */
KUNIT_EXPECT_EQ(test, bss->signal, inform_bss.signal);
KUNIT_EXPECT_EQ(test, bss->beacon_interval, beacon_int);
KUNIT_EXPECT_EQ(test, bss->capability, capability);
KUNIT_EXPECT_EQ(test, bss->bssid_index, 0);
KUNIT_EXPECT_PTR_EQ(test, bss->channel, inform_bss.chan);
KUNIT_EXPECT_MEMEQ(test, bssid, bss->bssid, sizeof(bssid));
/* Check the IEs have the expected value */
rcu_read_lock();
ies = rcu_dereference(bss->ies);
KUNIT_EXPECT_NOT_NULL(test, ies);
KUNIT_EXPECT_EQ(test, ies->tsf, tsf);
KUNIT_EXPECT_EQ(test, ies->len, sizeof(input));
KUNIT_EXPECT_MEMEQ(test, ies->data, input, sizeof(input));
rcu_read_unlock();
/* Check we can look up the BSS - by SSID */
other = cfg80211_get_bss(wiphy, NULL, NULL, "TEST", 4,
IEEE80211_BSS_TYPE_ANY,
IEEE80211_PRIVACY_ANY);
KUNIT_EXPECT_PTR_EQ(test, bss, other);
cfg80211_put_bss(wiphy, other);
/* Check we can look up the BSS - by BSSID */
other = cfg80211_get_bss(wiphy, NULL, bssid, NULL, 0,
IEEE80211_BSS_TYPE_ANY,
IEEE80211_PRIVACY_ANY);
KUNIT_EXPECT_PTR_EQ(test, bss, other);
cfg80211_put_bss(wiphy, other);
cfg80211_put_bss(wiphy, bss);
}
static struct inform_bss_ml_sta_case {
const char *desc;
int mld_id;
bool sta_prof_vendor_elems;
bool include_oper_class;
bool nstr;
} inform_bss_ml_sta_cases[] = {
{
.desc = "zero_mld_id",
.mld_id = 0,
.sta_prof_vendor_elems = false,
}, {
.desc = "zero_mld_id_with_oper_class",
.mld_id = 0,
.sta_prof_vendor_elems = false,
.include_oper_class = true,
}, {
.desc = "mld_id_eq_1",
.mld_id = 1,
.sta_prof_vendor_elems = true,
}, {
.desc = "mld_id_eq_1_with_oper_class",
.mld_id = 1,
.sta_prof_vendor_elems = true,
.include_oper_class = true,
}, {
.desc = "nstr",
.mld_id = 0,
.nstr = true,
},
};
KUNIT_ARRAY_PARAM_DESC(inform_bss_ml_sta, inform_bss_ml_sta_cases, desc)
static void test_inform_bss_ml_sta(struct kunit *test)
{
const struct inform_bss_ml_sta_case *params = test->param_value;
struct inform_bss ctx = {
.test = test,
};
struct wiphy *wiphy = T_WIPHY(test, ctx);
struct t_wiphy_priv *w_priv = wiphy_priv(wiphy);
struct cfg80211_inform_bss inform_bss = {
.signal = 50,
.drv_data = &ctx,
};
struct cfg80211_bss *bss, *link_bss;
const struct cfg80211_bss_ies *ies;
/* sending station */
const u8 bssid[ETH_ALEN] = { 0x10, 0x22, 0x33, 0x44, 0x55, 0x66 };
u64 tsf = 0x1000000000000000ULL;
int beacon_int = 100;
u16 capability = 0x1234;
/* Building the frame *************************************************/
struct sk_buff *input = kunit_zalloc_skb(test, 1024, GFP_KERNEL);
u8 *len_mle, *len_prof;
u8 link_id = 2;
struct {
struct ieee80211_neighbor_ap_info info;
struct ieee80211_tbtt_info_ge_11 ap;
} __packed rnr_normal = {
.info = {
.tbtt_info_hdr = u8_encode_bits(0, IEEE80211_AP_INFO_TBTT_HDR_COUNT),
.tbtt_info_len = sizeof(struct ieee80211_tbtt_info_ge_11),
.op_class = 81,
.channel = 11,
},
.ap = {
.tbtt_offset = 0xff,
.bssid = { 0x10, 0x22, 0x33, 0x44, 0x55, 0x67 },
.short_ssid = 0, /* unused */
.bss_params = 0,
.psd_20 = 0,
.mld_params.mld_id = params->mld_id,
.mld_params.params =
le16_encode_bits(link_id,
IEEE80211_RNR_MLD_PARAMS_LINK_ID),
}
};
struct {
struct ieee80211_neighbor_ap_info info;
struct ieee80211_rnr_mld_params mld_params;
} __packed rnr_nstr = {
.info = {
.tbtt_info_hdr =
u8_encode_bits(0, IEEE80211_AP_INFO_TBTT_HDR_COUNT) |
u8_encode_bits(IEEE80211_TBTT_INFO_TYPE_MLD,
IEEE80211_AP_INFO_TBTT_HDR_TYPE),
.tbtt_info_len = sizeof(struct ieee80211_rnr_mld_params),
.op_class = 81,
.channel = 11,
},
.mld_params = {
.mld_id = params->mld_id,
.params =
le16_encode_bits(link_id,
IEEE80211_RNR_MLD_PARAMS_LINK_ID),
}
};
size_t rnr_len = params->nstr ? sizeof(rnr_nstr) : sizeof(rnr_normal);
void *rnr = params->nstr ? (void *)&rnr_nstr : (void *)&rnr_normal;
struct {
__le16 control;
u8 var_len;
u8 mld_mac_addr[ETH_ALEN];
u8 link_id_info;
u8 params_change_count;
__le16 mld_caps_and_ops;
u8 mld_id;
__le16 ext_mld_caps_and_ops;
} __packed mle_basic_common_info = {
.control =
cpu_to_le16(IEEE80211_ML_CONTROL_TYPE_BASIC |
IEEE80211_MLC_BASIC_PRES_BSS_PARAM_CH_CNT |
IEEE80211_MLC_BASIC_PRES_LINK_ID |
(params->mld_id ? IEEE80211_MLC_BASIC_PRES_MLD_ID : 0) |
IEEE80211_MLC_BASIC_PRES_MLD_CAPA_OP),
.mld_id = params->mld_id,
.mld_caps_and_ops = cpu_to_le16(0x0102),
.ext_mld_caps_and_ops = cpu_to_le16(0x0304),
.var_len = sizeof(mle_basic_common_info) - 2 -
(params->mld_id ? 0 : 1),
.mld_mac_addr = { 0x10, 0x22, 0x33, 0x44, 0x55, 0x60 },
};
struct {
__le16 control;
u8 var_len;
u8 bssid[ETH_ALEN];
__le16 beacon_int;
__le64 tsf_offset;
__le16 capabilities; /* already part of payload */
} __packed sta_prof = {
.control =
cpu_to_le16(IEEE80211_MLE_STA_CONTROL_COMPLETE_PROFILE |
IEEE80211_MLE_STA_CONTROL_STA_MAC_ADDR_PRESENT |
IEEE80211_MLE_STA_CONTROL_BEACON_INT_PRESENT |
IEEE80211_MLE_STA_CONTROL_TSF_OFFS_PRESENT |
u16_encode_bits(link_id,
IEEE80211_MLE_STA_CONTROL_LINK_ID)),
.var_len = sizeof(sta_prof) - 2 - 2,
.bssid = { *rnr_normal.ap.bssid },
.beacon_int = cpu_to_le16(101),
.tsf_offset = cpu_to_le64(-123ll),
.capabilities = cpu_to_le16(0xdead),
};
KUNIT_ASSERT_NOT_NULL(test, input);
w_priv->ops->inform_bss = inform_bss_inc_counter;
inform_bss.chan = ieee80211_get_channel_khz(wiphy, MHZ_TO_KHZ(2412));
KUNIT_ASSERT_NOT_NULL(test, inform_bss.chan);
skb_put_u8(input, WLAN_EID_SSID);
skb_put_u8(input, 4);
skb_put_data(input, "TEST", 4);
if (params->include_oper_class) {
skb_put_u8(input, WLAN_EID_SUPPORTED_REGULATORY_CLASSES);
skb_put_u8(input, 1);
skb_put_u8(input, 81);
}
skb_put_u8(input, WLAN_EID_REDUCED_NEIGHBOR_REPORT);
skb_put_u8(input, rnr_len);
skb_put_data(input, rnr, rnr_len);
/* build a multi-link element */
skb_put_u8(input, WLAN_EID_EXTENSION);
len_mle = skb_put(input, 1);
skb_put_u8(input, WLAN_EID_EXT_EHT_MULTI_LINK);
skb_put_data(input, &mle_basic_common_info, sizeof(mle_basic_common_info));
if (!params->mld_id)
t_skb_remove_member(input, typeof(mle_basic_common_info), mld_id);
/* with a STA profile inside */
skb_put_u8(input, IEEE80211_MLE_SUBELEM_PER_STA_PROFILE);
len_prof = skb_put(input, 1);
skb_put_data(input, &sta_prof, sizeof(sta_prof));
if (params->sta_prof_vendor_elems) {
/* Put two (vendor) element into sta_prof */
skb_put_u8(input, WLAN_EID_VENDOR_SPECIFIC);
skb_put_u8(input, 160);
skb_put(input, 160);
skb_put_u8(input, WLAN_EID_VENDOR_SPECIFIC);
skb_put_u8(input, 165);
skb_put(input, 165);
}
/* fragment STA profile */
ieee80211_fragment_element(input, len_prof,
IEEE80211_MLE_SUBELEM_FRAGMENT);
/* fragment MLE */
ieee80211_fragment_element(input, len_mle, WLAN_EID_FRAGMENT);
/* Put a (vendor) element after the ML element */
skb_put_u8(input, WLAN_EID_VENDOR_SPECIFIC);
skb_put_u8(input, 155);
skb_put(input, 155);
/* Submit *************************************************************/
bss = cfg80211_inform_bss_data(wiphy, &inform_bss,
CFG80211_BSS_FTYPE_PRESP, bssid, tsf,
capability, beacon_int,
input->data, input->len,
GFP_KERNEL);
KUNIT_EXPECT_NOT_NULL(test, bss);
KUNIT_EXPECT_EQ(test, ctx.inform_bss_count, 2);
/* Check link_bss *****************************************************/
link_bss = __cfg80211_get_bss(wiphy, NULL, sta_prof.bssid, NULL, 0,
IEEE80211_BSS_TYPE_ANY,
IEEE80211_PRIVACY_ANY,
0);
KUNIT_ASSERT_NOT_NULL(test, link_bss);
KUNIT_EXPECT_EQ(test, link_bss->signal, 0);
KUNIT_EXPECT_EQ(test, link_bss->beacon_interval,
le16_to_cpu(sta_prof.beacon_int));
KUNIT_EXPECT_EQ(test, link_bss->capability,
le16_to_cpu(sta_prof.capabilities));
KUNIT_EXPECT_EQ(test, link_bss->bssid_index, 0);
KUNIT_EXPECT_PTR_EQ(test, link_bss->channel,
ieee80211_get_channel_khz(wiphy, MHZ_TO_KHZ(2462)));
/* Test wiphy does not set WIPHY_FLAG_SUPPORTS_NSTR_NONPRIMARY */
if (params->nstr) {
KUNIT_EXPECT_EQ(test, link_bss->use_for, 0);
KUNIT_EXPECT_EQ(test, link_bss->cannot_use_reasons,
NL80211_BSS_CANNOT_USE_NSTR_NONPRIMARY);
KUNIT_EXPECT_NULL(test,
cfg80211_get_bss(wiphy, NULL, sta_prof.bssid,
NULL, 0,
IEEE80211_BSS_TYPE_ANY,
IEEE80211_PRIVACY_ANY));
} else {
KUNIT_EXPECT_EQ(test, link_bss->use_for,
NL80211_BSS_USE_FOR_ALL);
KUNIT_EXPECT_EQ(test, link_bss->cannot_use_reasons, 0);
}
rcu_read_lock();
ies = rcu_dereference(link_bss->ies);
KUNIT_EXPECT_NOT_NULL(test, ies);
KUNIT_EXPECT_EQ(test, ies->tsf, tsf + le64_to_cpu(sta_prof.tsf_offset));
/* Resulting length should be:
* SSID (inherited) + RNR (inherited) + vendor element(s) +
* operating class (if requested) +
* generated RNR (if MLD ID == 0 and not NSTR) +
* MLE common info + MLE header and control
*/
if (params->sta_prof_vendor_elems)
KUNIT_EXPECT_EQ(test, ies->len,
6 + 2 + rnr_len + 2 + 160 + 2 + 165 +
(params->include_oper_class ? 3 : 0) +
(!params->mld_id && !params->nstr ? 22 : 0) +
mle_basic_common_info.var_len + 5);
else
KUNIT_EXPECT_EQ(test, ies->len,
6 + 2 + rnr_len + 2 + 155 +
(params->include_oper_class ? 3 : 0) +
(!params->mld_id && !params->nstr ? 22 : 0) +
mle_basic_common_info.var_len + 5);
rcu_read_unlock();
cfg80211_put_bss(wiphy, bss);
cfg80211_put_bss(wiphy, link_bss);
}
static struct cfg80211_parse_colocated_ap_case {
const char *desc;
u8 op_class;
u8 channel;
struct ieee80211_neighbor_ap_info info;
union {
struct ieee80211_tbtt_info_ge_11 tbtt_long;
struct ieee80211_tbtt_info_7_8_9 tbtt_short;
};
bool add_junk;
bool same_ssid;
bool valid;
} cfg80211_parse_colocated_ap_cases[] = {
{
.desc = "wrong_band",
.info.op_class = 81,
.info.channel = 11,
.tbtt_long = {
.bssid = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 },
.bss_params = IEEE80211_RNR_TBTT_PARAMS_COLOC_AP,
},
.valid = false,
},
{
.desc = "wrong_type",
/* IEEE80211_AP_INFO_TBTT_HDR_TYPE is in the least significant bits */
.info.tbtt_info_hdr = IEEE80211_TBTT_INFO_TYPE_MLD,
.tbtt_long = {
.bssid = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 },
.bss_params = IEEE80211_RNR_TBTT_PARAMS_COLOC_AP,
},
.valid = false,
},
{
.desc = "colocated_invalid_len_short",
.info.tbtt_info_len = 6,
.tbtt_short = {
.bssid = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 },
.bss_params = IEEE80211_RNR_TBTT_PARAMS_COLOC_AP |
IEEE80211_RNR_TBTT_PARAMS_SAME_SSID,
},
.valid = false,
},
{
.desc = "colocated_invalid_len_short_mld",
.info.tbtt_info_len = 10,
.tbtt_long = {
.bssid = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 },
.bss_params = IEEE80211_RNR_TBTT_PARAMS_COLOC_AP,
},
.valid = false,
},
{
.desc = "colocated_non_mld",
.info.tbtt_info_len = sizeof(struct ieee80211_tbtt_info_7_8_9),
.tbtt_short = {
.bssid = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 },
.bss_params = IEEE80211_RNR_TBTT_PARAMS_COLOC_AP |
IEEE80211_RNR_TBTT_PARAMS_SAME_SSID,
},
.same_ssid = true,
.valid = true,
},
{
.desc = "colocated_non_mld_invalid_bssid",
.info.tbtt_info_len = sizeof(struct ieee80211_tbtt_info_7_8_9),
.tbtt_short = {
.bssid = { 0xff, 0x11, 0x22, 0x33, 0x44, 0x55 },
.bss_params = IEEE80211_RNR_TBTT_PARAMS_COLOC_AP |
IEEE80211_RNR_TBTT_PARAMS_SAME_SSID,
},
.same_ssid = true,
.valid = false,
},
{
.desc = "colocated_mld",
.tbtt_long = {
.bssid = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 },
.bss_params = IEEE80211_RNR_TBTT_PARAMS_COLOC_AP,
},
.valid = true,
},
{
.desc = "colocated_mld",
.tbtt_long = {
.bssid = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 },
.bss_params = IEEE80211_RNR_TBTT_PARAMS_COLOC_AP,
},
.add_junk = true,
.valid = false,
},
{
.desc = "colocated_disabled_mld",
.tbtt_long = {
.bssid = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 },
.bss_params = IEEE80211_RNR_TBTT_PARAMS_COLOC_AP,
.mld_params.params = cpu_to_le16(IEEE80211_RNR_MLD_PARAMS_DISABLED_LINK),
},
.valid = false,
},
};
KUNIT_ARRAY_PARAM_DESC(cfg80211_parse_colocated_ap, cfg80211_parse_colocated_ap_cases, desc)
static void test_cfg80211_parse_colocated_ap(struct kunit *test)
{
const struct cfg80211_parse_colocated_ap_case *params = test->param_value;
struct sk_buff *input = kunit_zalloc_skb(test, 1024, GFP_KERNEL);
struct cfg80211_bss_ies *ies;
struct ieee80211_neighbor_ap_info info;
LIST_HEAD(coloc_ap_list);
int count;
KUNIT_ASSERT_NOT_NULL(test, input);
info = params->info;
/* Reasonable values for a colocated AP */
if (!info.tbtt_info_len)
info.tbtt_info_len = sizeof(params->tbtt_long);
if (!info.op_class)
info.op_class = 131;
if (!info.channel)
info.channel = 33;
/* Zero is the correct default for .btt_info_hdr (one entry, TBTT type) */
skb_put_u8(input, WLAN_EID_SSID);
skb_put_u8(input, 4);
skb_put_data(input, "TEST", 4);
skb_put_u8(input, WLAN_EID_REDUCED_NEIGHBOR_REPORT);
skb_put_u8(input, sizeof(info) + info.tbtt_info_len + (params->add_junk ? 3 : 0));
skb_put_data(input, &info, sizeof(info));
skb_put_data(input, &params->tbtt_long, info.tbtt_info_len);
if (params->add_junk)
skb_put_data(input, "123", 3);
ies = kunit_kzalloc(test, struct_size(ies, data, input->len), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ies);
ies->len = input->len;
memcpy(ies->data, input->data, input->len);
count = cfg80211_parse_colocated_ap(ies, &coloc_ap_list);
KUNIT_EXPECT_EQ(test, count, params->valid);
KUNIT_EXPECT_EQ(test, list_count_nodes(&coloc_ap_list), params->valid);
if (params->valid && !list_empty(&coloc_ap_list)) {
struct cfg80211_colocated_ap *ap;
ap = list_first_entry(&coloc_ap_list, typeof(*ap), list);
if (info.tbtt_info_len <= sizeof(params->tbtt_short))
KUNIT_EXPECT_MEMEQ(test, ap->bssid, params->tbtt_short.bssid, ETH_ALEN);
else
KUNIT_EXPECT_MEMEQ(test, ap->bssid, params->tbtt_long.bssid, ETH_ALEN);
if (params->same_ssid) {
KUNIT_EXPECT_EQ(test, ap->ssid_len, 4);
KUNIT_EXPECT_MEMEQ(test, ap->ssid, "TEST", 4);
} else {
KUNIT_EXPECT_EQ(test, ap->ssid_len, 0);
}
}
cfg80211_free_coloc_ap_list(&coloc_ap_list);
}
static struct kunit_case gen_new_ie_test_cases[] = {
KUNIT_CASE_PARAM(test_gen_new_ie, gen_new_ie_gen_params),
KUNIT_CASE(test_gen_new_ie_malformed),
{}
};
static struct kunit_suite gen_new_ie = {
.name = "cfg80211-ie-generation",
.test_cases = gen_new_ie_test_cases,
};
kunit_test_suite(gen_new_ie);
static struct kunit_case inform_bss_test_cases[] = {
KUNIT_CASE(test_inform_bss_ssid_only),
KUNIT_CASE_PARAM(test_inform_bss_ml_sta, inform_bss_ml_sta_gen_params),
{}
};
static struct kunit_suite inform_bss = {
.name = "cfg80211-inform-bss",
.test_cases = inform_bss_test_cases,
};
kunit_test_suite(inform_bss);
static struct kunit_case scan_6ghz_cases[] = {
KUNIT_CASE_PARAM(test_cfg80211_parse_colocated_ap,
cfg80211_parse_colocated_ap_gen_params),
{}
};
static struct kunit_suite scan_6ghz = {
.name = "cfg80211-scan-6ghz",
.test_cases = scan_6ghz_cases,
};
kunit_test_suite(scan_6ghz);
+56
View File
@@ -0,0 +1,56 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* KUnit fixture to have a (configurable) wiphy
*
* Copyright (C) 2023 Intel Corporation
*/
#include <linux/ieee80211.h>
#include <net/cfg80211.h>
#include <kunit/test.h>
#include <kunit/test-bug.h>
#include "util.h"
int t_wiphy_init(struct kunit_resource *resource, void *ctx)
{
struct kunit *test = kunit_get_current_test();
struct cfg80211_ops *ops;
struct wiphy *wiphy;
struct t_wiphy_priv *priv;
ops = kzalloc_obj(*ops);
KUNIT_ASSERT_NOT_NULL(test, ops);
wiphy = wiphy_new_nm(ops, sizeof(*priv), "kunit");
KUNIT_ASSERT_NOT_NULL(test, wiphy);
priv = wiphy_priv(wiphy);
priv->ctx = ctx;
priv->ops = ops;
/* Initialize channels, feel free to add more here channels/bands */
memcpy(priv->channels_2ghz, channels_2ghz, sizeof(channels_2ghz));
wiphy->bands[NL80211_BAND_2GHZ] = &priv->band_2ghz;
priv->band_2ghz.channels = priv->channels_2ghz;
priv->band_2ghz.n_channels = ARRAY_SIZE(channels_2ghz);
resource->data = wiphy;
resource->name = "wiphy";
return 0;
}
void t_wiphy_exit(struct kunit_resource *resource)
{
struct t_wiphy_priv *priv;
struct cfg80211_ops *ops;
priv = wiphy_priv(resource->data);
ops = priv->ops;
/* Should we ensure anything about the state here?
* e.g. full destruction or no calls to any ops on destruction?
*/
wiphy_free(resource->data);
kfree(ops);
}
+66
View File
@@ -0,0 +1,66 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Utilities for cfg80211 unit testing
*
* Copyright (C) 2023 Intel Corporation
*/
#ifndef __CFG80211_UTILS_H
#define __CFG80211_UTILS_H
#define CHAN2G(_freq) { \
.band = NL80211_BAND_2GHZ, \
.center_freq = (_freq), \
.hw_value = (_freq), \
}
static const struct ieee80211_channel channels_2ghz[] = {
CHAN2G(2412), /* Channel 1 */
CHAN2G(2417), /* Channel 2 */
CHAN2G(2422), /* Channel 3 */
CHAN2G(2427), /* Channel 4 */
CHAN2G(2432), /* Channel 5 */
CHAN2G(2437), /* Channel 6 */
CHAN2G(2442), /* Channel 7 */
CHAN2G(2447), /* Channel 8 */
CHAN2G(2452), /* Channel 9 */
CHAN2G(2457), /* Channel 10 */
CHAN2G(2462), /* Channel 11 */
CHAN2G(2467), /* Channel 12 */
CHAN2G(2472), /* Channel 13 */
CHAN2G(2484), /* Channel 14 */
};
struct t_wiphy_priv {
struct kunit *test;
struct cfg80211_ops *ops;
void *ctx;
struct ieee80211_supported_band band_2ghz;
struct ieee80211_channel channels_2ghz[ARRAY_SIZE(channels_2ghz)];
};
#define T_WIPHY(test, ctx) ({ \
struct wiphy *__wiphy = \
kunit_alloc_resource(test, t_wiphy_init, \
t_wiphy_exit, \
GFP_KERNEL, &(ctx)); \
\
KUNIT_ASSERT_NOT_NULL(test, __wiphy); \
__wiphy; \
})
#define t_wiphy_ctx(wiphy) (((struct t_wiphy_priv *)wiphy_priv(wiphy))->ctx)
int t_wiphy_init(struct kunit_resource *resource, void *data);
void t_wiphy_exit(struct kunit_resource *resource);
#define t_skb_remove_member(skb, type, member) do { \
memmove((skb)->data + (skb)->len - sizeof(type) + \
offsetof(type, member), \
(skb)->data + (skb)->len - sizeof(type) + \
offsetofend(type, member), \
offsetofend(type, member)); \
skb_trim(skb, (skb)->len - sizeof_field(type, member)); \
} while (0)
#endif /* __CFG80211_UTILS_H */
+8
View File
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/module.h>
#ifndef __CHECKER__
#define CREATE_TRACE_POINTS
#include "trace.h"
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+58
View File
@@ -0,0 +1,58 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __WEXT_COMPAT
#define __WEXT_COMPAT
#include <net/iw_handler.h>
#include <linux/wireless.h>
int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
struct iw_request_info *info,
struct iw_freq *wextfreq, char *extra);
int cfg80211_ibss_wext_giwfreq(struct net_device *dev,
struct iw_request_info *info,
struct iw_freq *freq, char *extra);
int cfg80211_ibss_wext_siwap(struct net_device *dev,
struct iw_request_info *info,
struct sockaddr *ap_addr, char *extra);
int cfg80211_ibss_wext_giwap(struct net_device *dev,
struct iw_request_info *info,
struct sockaddr *ap_addr, char *extra);
int cfg80211_ibss_wext_siwessid(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *data, char *ssid);
int cfg80211_ibss_wext_giwessid(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *data, char *ssid);
int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
struct iw_request_info *info,
struct iw_freq *wextfreq, char *extra);
int cfg80211_mgd_wext_giwfreq(struct net_device *dev,
struct iw_request_info *info,
struct iw_freq *freq, char *extra);
int cfg80211_mgd_wext_siwap(struct net_device *dev,
struct iw_request_info *info,
struct sockaddr *ap_addr, char *extra);
int cfg80211_mgd_wext_giwap(struct net_device *dev,
struct iw_request_info *info,
struct sockaddr *ap_addr, char *extra);
int cfg80211_mgd_wext_siwessid(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *data, char *ssid);
int cfg80211_mgd_wext_giwessid(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *data, char *ssid);
int cfg80211_wext_siwmlme(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra);
int cfg80211_wext_siwgenie(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra);
int cfg80211_wext_freq(struct iw_freq *freq);
extern const struct iw_handler_def cfg80211_wext_handler;
#endif /* __WEXT_COMPAT */
File diff suppressed because it is too large Load Diff
+252
View File
@@ -0,0 +1,252 @@
// SPDX-License-Identifier: GPL-2.0
/*
* This file implement the Wireless Extensions priv API.
*
* Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
* Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
* Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
*/
#include <linux/slab.h>
#include <linux/wireless.h>
#include <linux/netdevice.h>
#include <net/iw_handler.h>
#include <net/wext.h>
int iw_handler_get_private(struct net_device * dev,
struct iw_request_info * info,
union iwreq_data * wrqu,
char * extra)
{
/* Check if the driver has something to export */
if ((dev->wireless_handlers->num_private_args == 0) ||
(dev->wireless_handlers->private_args == NULL))
return -EOPNOTSUPP;
/* Check if there is enough buffer up there */
if (wrqu->data.length < dev->wireless_handlers->num_private_args) {
/* User space can't know in advance how large the buffer
* needs to be. Give it a hint, so that we can support
* any size buffer we want somewhat efficiently... */
wrqu->data.length = dev->wireless_handlers->num_private_args;
return -E2BIG;
}
/* Set the number of available ioctls. */
wrqu->data.length = dev->wireless_handlers->num_private_args;
/* Copy structure to the user buffer. */
memcpy(extra, dev->wireless_handlers->private_args,
sizeof(struct iw_priv_args) * wrqu->data.length);
return 0;
}
/* Size (in bytes) of the various private data types */
static const char iw_priv_type_size[] = {
0, /* IW_PRIV_TYPE_NONE */
1, /* IW_PRIV_TYPE_BYTE */
1, /* IW_PRIV_TYPE_CHAR */
0, /* Not defined */
sizeof(__u32), /* IW_PRIV_TYPE_INT */
sizeof(struct iw_freq), /* IW_PRIV_TYPE_FLOAT */
sizeof(struct sockaddr), /* IW_PRIV_TYPE_ADDR */
0, /* Not defined */
};
static int get_priv_size(__u16 args)
{
int num = args & IW_PRIV_SIZE_MASK;
int type = (args & IW_PRIV_TYPE_MASK) >> 12;
return num * iw_priv_type_size[type];
}
static int adjust_priv_size(__u16 args, struct iw_point *iwp)
{
int num = iwp->length;
int max = args & IW_PRIV_SIZE_MASK;
int type = (args & IW_PRIV_TYPE_MASK) >> 12;
/* Make sure the driver doesn't goof up */
if (max < num)
num = max;
return num * iw_priv_type_size[type];
}
/*
* Wrapper to call a private Wireless Extension handler.
* We do various checks and also take care of moving data between
* user space and kernel space.
* It's not as nice and slimline as the standard wrapper. The cause
* is struct iw_priv_args, which was not really designed for the
* job we are going here.
*
* IMPORTANT : This function prevent to set and get data on the same
* IOCTL and enforce the SET/GET convention. Not doing it would be
* far too hairy...
* If you need to set and get data at the same time, please don't use
* a iw_handler but process it in your ioctl handler (i.e. use the
* old driver API).
*/
static int get_priv_descr_and_size(struct net_device *dev, unsigned int cmd,
const struct iw_priv_args **descrp)
{
const struct iw_priv_args *descr;
int i, extra_size;
descr = NULL;
for (i = 0; i < dev->wireless_handlers->num_private_args; i++) {
if (cmd == dev->wireless_handlers->private_args[i].cmd) {
descr = &dev->wireless_handlers->private_args[i];
break;
}
}
extra_size = 0;
if (descr) {
if (IW_IS_SET(cmd)) {
int offset = 0; /* For sub-ioctls */
/* Check for sub-ioctl handler */
if (descr->name[0] == '\0')
/* Reserve one int for sub-ioctl index */
offset = sizeof(__u32);
/* Size of set arguments */
extra_size = get_priv_size(descr->set_args);
/* Does it fits in iwr ? */
if ((descr->set_args & IW_PRIV_SIZE_FIXED) &&
((extra_size + offset) <= IFNAMSIZ))
extra_size = 0;
} else {
/* Size of get arguments */
extra_size = get_priv_size(descr->get_args);
/* Does it fits in iwr ? */
if ((descr->get_args & IW_PRIV_SIZE_FIXED) &&
(extra_size <= IFNAMSIZ))
extra_size = 0;
}
}
*descrp = descr;
return extra_size;
}
static int ioctl_private_iw_point(struct iw_point *iwp, unsigned int cmd,
const struct iw_priv_args *descr,
iw_handler handler, struct net_device *dev,
struct iw_request_info *info, int extra_size)
{
char *extra;
int err;
/* Check what user space is giving us */
if (IW_IS_SET(cmd)) {
if (!iwp->pointer && iwp->length != 0)
return -EFAULT;
if (iwp->length > (descr->set_args & IW_PRIV_SIZE_MASK))
return -E2BIG;
} else if (!iwp->pointer)
return -EFAULT;
extra = kzalloc(extra_size, GFP_KERNEL);
if (!extra)
return -ENOMEM;
/* If it is a SET, get all the extra data in here */
if (IW_IS_SET(cmd) && (iwp->length != 0)) {
if (copy_from_user(extra, iwp->pointer, extra_size)) {
err = -EFAULT;
goto out;
}
}
/* Call the handler */
err = handler(dev, info, (union iwreq_data *) iwp, extra);
/* If we have something to return to the user */
if (!err && IW_IS_GET(cmd)) {
/* Adjust for the actual length if it's variable,
* avoid leaking kernel bits outside.
*/
if (!(descr->get_args & IW_PRIV_SIZE_FIXED))
extra_size = adjust_priv_size(descr->get_args, iwp);
if (copy_to_user(iwp->pointer, extra, extra_size))
err = -EFAULT;
}
out:
kfree(extra);
return err;
}
int ioctl_private_call(struct net_device *dev, struct iwreq *iwr,
unsigned int cmd, struct iw_request_info *info,
iw_handler handler)
{
int extra_size = 0, ret = -EINVAL;
const struct iw_priv_args *descr;
extra_size = get_priv_descr_and_size(dev, cmd, &descr);
/* Check if we have a pointer to user space data or not. */
if (extra_size == 0) {
/* No extra arguments. Trivial to handle */
ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u));
} else {
ret = ioctl_private_iw_point(&iwr->u.data, cmd, descr,
handler, dev, info, extra_size);
}
/* Call commit handler if needed and defined */
if (ret == -EIWCOMMIT)
ret = call_commit_handler(dev);
return ret;
}
#ifdef CONFIG_COMPAT
int compat_private_call(struct net_device *dev, struct iwreq *iwr,
unsigned int cmd, struct iw_request_info *info,
iw_handler handler)
{
const struct iw_priv_args *descr;
int ret, extra_size;
extra_size = get_priv_descr_and_size(dev, cmd, &descr);
/* Check if we have a pointer to user space data or not. */
if (extra_size == 0) {
/* No extra arguments. Trivial to handle */
ret = handler(dev, info, &(iwr->u), (char *) &(iwr->u));
} else {
struct compat_iw_point *iwp_compat;
struct iw_point iwp;
iwp_compat = (struct compat_iw_point *) &iwr->u.data;
/* struct iw_point has a 32bit hole on 64bit arches. */
memset(&iwp, 0, sizeof(iwp));
iwp.pointer = compat_ptr(iwp_compat->pointer);
iwp.length = iwp_compat->length;
iwp.flags = iwp_compat->flags;
ret = ioctl_private_iw_point(&iwp, cmd, descr,
handler, dev, info, extra_size);
iwp_compat->pointer = ptr_to_compat(iwp.pointer);
iwp_compat->length = iwp.length;
iwp_compat->flags = iwp.flags;
}
/* Call commit handler if needed and defined */
if (ret == -EIWCOMMIT)
ret = call_commit_handler(dev);
return ret;
}
#endif
+141
View File
@@ -0,0 +1,141 @@
// SPDX-License-Identifier: GPL-2.0
/*
* This file implement the Wireless Extensions proc API.
*
* Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
* Copyright (c) 1997-2007 Jean Tourrilhes, All Rights Reserved.
*/
/*
* The /proc/net/wireless file is a human readable user-space interface
* exporting various wireless specific statistics from the wireless devices.
* This is the most popular part of the Wireless Extensions ;-)
*
* This interface is a pure clone of /proc/net/dev (in net/core/dev.c).
* The content of the file is basically the content of "struct iw_statistics".
*/
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/wireless.h>
#include <linux/netdevice.h>
#include <linux/rtnetlink.h>
#include <net/iw_handler.h>
#include <net/wext.h>
static void wireless_seq_printf_stats(struct seq_file *seq,
struct net_device *dev)
{
/* Get stats from the driver */
struct iw_statistics *stats = get_wireless_stats(dev);
static struct iw_statistics nullstats = {};
/* show device if it's wireless regardless of current stats */
if (!stats) {
#ifdef CONFIG_WIRELESS_EXT
if (dev->wireless_handlers)
stats = &nullstats;
#endif
#ifdef CONFIG_CFG80211
if (dev->ieee80211_ptr)
stats = &nullstats;
#endif
}
if (stats) {
seq_printf(seq, "%6s: %04x %3d%c %3d%c %3d%c %6d %6d %6d "
"%6d %6d %6d\n",
dev->name, stats->status, stats->qual.qual,
stats->qual.updated & IW_QUAL_QUAL_UPDATED
? '.' : ' ',
((__s32) stats->qual.level) -
((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
stats->qual.updated & IW_QUAL_LEVEL_UPDATED
? '.' : ' ',
((__s32) stats->qual.noise) -
((stats->qual.updated & IW_QUAL_DBM) ? 0x100 : 0),
stats->qual.updated & IW_QUAL_NOISE_UPDATED
? '.' : ' ',
stats->discard.nwid, stats->discard.code,
stats->discard.fragment, stats->discard.retries,
stats->discard.misc, stats->miss.beacon);
if (stats != &nullstats)
stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
}
}
/* ---------------------------------------------------------------- */
/*
* Print info for /proc/net/wireless (print all entries)
*/
static int wireless_dev_seq_show(struct seq_file *seq, void *v)
{
might_sleep();
if (v == SEQ_START_TOKEN)
seq_printf(seq, "Inter-| sta-| Quality | Discarded "
"packets | Missed | WE\n"
" face | tus | link level noise | nwid "
"crypt frag retry misc | beacon | %d\n",
WIRELESS_EXT);
else
wireless_seq_printf_stats(seq, v);
return 0;
}
static void *wireless_dev_seq_start(struct seq_file *seq, loff_t *pos)
{
struct net *net = seq_file_net(seq);
loff_t off;
struct net_device *dev;
rtnl_lock();
if (!*pos)
return SEQ_START_TOKEN;
off = 1;
for_each_netdev(net, dev)
if (off++ == *pos)
return dev;
return NULL;
}
static void *wireless_dev_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
struct net *net = seq_file_net(seq);
++*pos;
return v == SEQ_START_TOKEN ?
first_net_device(net) : next_net_device(v);
}
static void wireless_dev_seq_stop(struct seq_file *seq, void *v)
{
rtnl_unlock();
}
static const struct seq_operations wireless_seq_ops = {
.start = wireless_dev_seq_start,
.next = wireless_dev_seq_next,
.stop = wireless_dev_seq_stop,
.show = wireless_dev_seq_show,
};
int __net_init wext_proc_init(struct net *net)
{
/* Create /proc/net/wireless entry */
if (!proc_create_net("wireless", 0444, net->proc_net,
&wireless_seq_ops, sizeof(struct seq_net_private)))
return -ENOMEM;
return 0;
}
void __net_exit wext_proc_exit(struct net *net)
{
remove_proc_entry("wireless", net->proc_net);
}
+378
View File
@@ -0,0 +1,378 @@
// SPDX-License-Identifier: GPL-2.0
/*
* cfg80211 wext compat for managed mode.
*
* Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
* Copyright (C) 2009, 2020-2023 Intel Corporation
*/
#include <linux/export.h>
#include <linux/etherdevice.h>
#include <linux/if_arp.h>
#include <linux/slab.h>
#include <net/cfg80211.h>
#include <net/cfg80211-wext.h>
#include "wext-compat.h"
#include "nl80211.h"
int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev)
{
struct cfg80211_cached_keys *ck = NULL;
const u8 *prev_bssid = NULL;
int err, i;
ASSERT_RTNL();
lockdep_assert_wiphy(wdev->wiphy);
if (!netif_running(wdev->netdev))
return 0;
wdev->wext.connect.ie = wdev->wext.ie;
wdev->wext.connect.ie_len = wdev->wext.ie_len;
/* Use default background scan period */
wdev->wext.connect.bg_scan_period = -1;
if (wdev->wext.keys) {
wdev->wext.keys->def = wdev->wext.default_key;
if (wdev->wext.default_key != -1)
wdev->wext.connect.privacy = true;
}
if (!wdev->wext.connect.ssid_len)
return 0;
if (wdev->wext.keys && wdev->wext.keys->def != -1) {
ck = kmemdup(wdev->wext.keys, sizeof(*ck), GFP_KERNEL);
if (!ck)
return -ENOMEM;
for (i = 0; i < 4; i++)
ck->params[i].key = ck->data[i];
}
if (wdev->wext.prev_bssid_valid)
prev_bssid = wdev->wext.prev_bssid;
err = cfg80211_connect(rdev, wdev->netdev,
&wdev->wext.connect, ck, prev_bssid);
if (err)
kfree_sensitive(ck);
return err;
}
int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
struct iw_request_info *info,
struct iw_freq *wextfreq, char *extra)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
struct ieee80211_channel *chan = NULL;
int err, freq;
/* call only for station! */
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
return -EINVAL;
freq = cfg80211_wext_freq(wextfreq);
if (freq < 0)
return freq;
if (freq) {
chan = ieee80211_get_channel(wdev->wiphy, freq);
if (!chan)
return -EINVAL;
if (chan->flags & IEEE80211_CHAN_DISABLED)
return -EINVAL;
}
if (wdev->conn) {
bool event = true;
if (wdev->wext.connect.channel == chan)
return 0;
/* if SSID set, we'll try right again, avoid event */
if (wdev->wext.connect.ssid_len)
event = false;
err = cfg80211_disconnect(rdev, dev,
WLAN_REASON_DEAUTH_LEAVING, event);
if (err)
return err;
}
wdev->wext.connect.channel = chan;
return cfg80211_mgd_wext_connect(rdev, wdev);
}
int cfg80211_mgd_wext_giwfreq(struct net_device *dev,
struct iw_request_info *info,
struct iw_freq *freq, char *extra)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct ieee80211_channel *chan = NULL;
/* call only for station! */
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
return -EINVAL;
if (wdev->valid_links)
return -EOPNOTSUPP;
if (wdev->links[0].client.current_bss)
chan = wdev->links[0].client.current_bss->pub.channel;
else if (wdev->wext.connect.channel)
chan = wdev->wext.connect.channel;
if (chan) {
freq->m = chan->center_freq;
freq->e = 6;
return 0;
}
/* no channel if not joining */
return -EINVAL;
}
int cfg80211_mgd_wext_siwessid(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *data, char *ssid)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
size_t len = data->length;
int err;
/* call only for station! */
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
return -EINVAL;
if (!data->flags)
len = 0;
/* iwconfig uses nul termination in SSID.. */
if (len > 0 && ssid[len - 1] == '\0')
len--;
if (wdev->conn) {
bool event = true;
if (wdev->wext.connect.ssid && len &&
len == wdev->wext.connect.ssid_len &&
memcmp(wdev->wext.connect.ssid, ssid, len) == 0)
return 0;
/* if SSID set now, we'll try to connect, avoid event */
if (len)
event = false;
err = cfg80211_disconnect(rdev, dev,
WLAN_REASON_DEAUTH_LEAVING, event);
if (err)
return err;
}
wdev->wext.prev_bssid_valid = false;
wdev->wext.connect.ssid = wdev->wext.ssid;
memcpy(wdev->wext.ssid, ssid, len);
wdev->wext.connect.ssid_len = len;
wdev->wext.connect.crypto.control_port = false;
wdev->wext.connect.crypto.control_port_ethertype =
cpu_to_be16(ETH_P_PAE);
return cfg80211_mgd_wext_connect(rdev, wdev);
}
int cfg80211_mgd_wext_giwessid(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *data, char *ssid)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
int ret = 0;
/* call only for station! */
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
return -EINVAL;
if (wdev->valid_links)
return -EINVAL;
data->flags = 0;
if (wdev->links[0].client.current_bss) {
const struct element *ssid_elem;
rcu_read_lock();
ssid_elem = ieee80211_bss_get_elem(
&wdev->links[0].client.current_bss->pub,
WLAN_EID_SSID);
if (ssid_elem) {
data->flags = 1;
data->length = ssid_elem->datalen;
if (data->length > IW_ESSID_MAX_SIZE)
ret = -EINVAL;
else
memcpy(ssid, ssid_elem->data, data->length);
}
rcu_read_unlock();
} else if (wdev->wext.connect.ssid && wdev->wext.connect.ssid_len) {
data->flags = 1;
data->length = wdev->wext.connect.ssid_len;
memcpy(ssid, wdev->wext.connect.ssid, data->length);
}
return ret;
}
int cfg80211_mgd_wext_siwap(struct net_device *dev,
struct iw_request_info *info,
struct sockaddr *ap_addr, char *extra)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
u8 *bssid = ap_addr->sa_data;
int err;
/* call only for station! */
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
return -EINVAL;
if (ap_addr->sa_family != ARPHRD_ETHER)
return -EINVAL;
/* automatic mode */
if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid))
bssid = NULL;
if (wdev->conn) {
/* both automatic */
if (!bssid && !wdev->wext.connect.bssid)
return 0;
/* fixed already - and no change */
if (wdev->wext.connect.bssid && bssid &&
ether_addr_equal(bssid, wdev->wext.connect.bssid))
return 0;
err = cfg80211_disconnect(rdev, dev,
WLAN_REASON_DEAUTH_LEAVING, false);
if (err)
return err;
}
if (bssid) {
memcpy(wdev->wext.bssid, bssid, ETH_ALEN);
wdev->wext.connect.bssid = wdev->wext.bssid;
} else
wdev->wext.connect.bssid = NULL;
return cfg80211_mgd_wext_connect(rdev, wdev);
}
int cfg80211_mgd_wext_giwap(struct net_device *dev,
struct iw_request_info *info,
struct sockaddr *ap_addr, char *extra)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
/* call only for station! */
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
return -EINVAL;
ap_addr->sa_family = ARPHRD_ETHER;
if (wdev->valid_links)
return -EOPNOTSUPP;
if (wdev->links[0].client.current_bss)
memcpy(ap_addr->sa_data,
wdev->links[0].client.current_bss->pub.bssid,
ETH_ALEN);
else
eth_zero_addr(ap_addr->sa_data);
return 0;
}
int cfg80211_wext_siwgenie(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
struct iw_point *data = &wrqu->data;
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
int ie_len = data->length;
u8 *ie = extra;
if (wdev->iftype != NL80211_IFTYPE_STATION)
return -EOPNOTSUPP;
if (!ie_len)
ie = NULL;
guard(wiphy)(wdev->wiphy);
/* no change */
if (wdev->wext.ie_len == ie_len &&
memcmp(wdev->wext.ie, ie, ie_len) == 0)
return 0;
if (ie_len) {
const struct element *elem;
for_each_element(elem, extra, ie_len) {
/* nothing */
}
if (!for_each_element_completed(elem, extra, ie_len))
return -EINVAL;
ie = kmemdup(extra, ie_len, GFP_KERNEL);
if (!ie)
return -ENOMEM;
} else {
ie = NULL;
}
kfree(wdev->wext.ie);
wdev->wext.ie = ie;
wdev->wext.ie_len = ie_len;
if (wdev->conn)
return cfg80211_disconnect(rdev, dev,
WLAN_REASON_DEAUTH_LEAVING, false);
/* userspace better not think we'll reconnect */
return 0;
}
int cfg80211_wext_siwmlme(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct iw_mlme *mlme = (struct iw_mlme *)extra;
struct cfg80211_registered_device *rdev;
if (!wdev)
return -EOPNOTSUPP;
rdev = wiphy_to_rdev(wdev->wiphy);
if (wdev->iftype != NL80211_IFTYPE_STATION)
return -EINVAL;
if (mlme->addr.sa_family != ARPHRD_ETHER)
return -EINVAL;
guard(wiphy)(&rdev->wiphy);
switch (mlme->cmd) {
case IW_MLME_DEAUTH:
case IW_MLME_DISASSOC:
return cfg80211_disconnect(rdev, dev, mlme->reason_code, true);
default:
return -EOPNOTSUPP;
}
}