summaryrefslogtreecommitdiff
path: root/net/wireless
diff options
context:
space:
mode:
Diffstat (limited to 'net/wireless')
-rw-r--r--net/wireless/chan.c289
-rw-r--r--net/wireless/core.c78
-rw-r--r--net/wireless/core.h16
-rw-r--r--net/wireless/mlme.c114
-rw-r--r--net/wireless/nl80211.c1262
-rw-r--r--net/wireless/nl80211.h5
-rw-r--r--net/wireless/pmsr.c249
-rw-r--r--net/wireless/rdev-ops.h48
-rw-r--r--net/wireless/reg.c6
-rw-r--r--net/wireless/scan.c123
-rw-r--r--net/wireless/sme.c7
-rw-r--r--net/wireless/tests/chan.c192
-rw-r--r--net/wireless/tests/scan.c121
-rw-r--r--net/wireless/trace.h40
-rw-r--r--net/wireless/util.c137
-rw-r--r--net/wireless/wext-compat.c11
-rw-r--r--net/wireless/wext-core.c6
-rw-r--r--net/wireless/wext-sme.c9
18 files changed, 2177 insertions, 536 deletions
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index 8b94c0de80ad..7f6af1790736 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -138,9 +138,10 @@ static const struct cfg80211_per_bw_puncturing_values per_bw_puncturing[] = {
CFG80211_PER_BW_VALID_PUNCTURING_VALUES(320)
};
-static bool valid_puncturing_bitmap(const struct cfg80211_chan_def *chandef)
+static bool valid_puncturing_bitmap(const struct cfg80211_chan_def *chandef,
+ u32 primary_center, u32 punctured)
{
- u32 idx, i, start_freq, primary_center = chandef->chan->center_freq;
+ u32 idx, i, start_freq;
switch (chandef->width) {
case NL80211_CHAN_WIDTH_80:
@@ -156,18 +157,18 @@ static bool valid_puncturing_bitmap(const struct cfg80211_chan_def *chandef)
start_freq = chandef->center_freq1 - 160;
break;
default:
- return chandef->punctured == 0;
+ return punctured == 0;
}
- if (!chandef->punctured)
+ if (!punctured)
return true;
/* check if primary channel is punctured */
- if (chandef->punctured & (u16)BIT((primary_center - start_freq) / 20))
+ if (punctured & (u16)BIT((primary_center - start_freq) / 20))
return false;
for (i = 0; i < per_bw_puncturing[idx].len; i++) {
- if (per_bw_puncturing[idx].valid_values[i] == chandef->punctured)
+ if (per_bw_puncturing[idx].valid_values[i] == punctured)
return true;
}
@@ -279,12 +280,6 @@ int nl80211_chan_width_to_mhz(enum nl80211_chan_width chan_width)
case NL80211_CHAN_WIDTH_16:
mhz = 16;
break;
- case NL80211_CHAN_WIDTH_5:
- mhz = 5;
- break;
- case NL80211_CHAN_WIDTH_10:
- mhz = 10;
- break;
case NL80211_CHAN_WIDTH_20:
case NL80211_CHAN_WIDTH_20_NOHT:
mhz = 20;
@@ -346,8 +341,6 @@ cfg80211_chandef_valid_control_freq(const struct cfg80211_chan_def *chandef,
u32 control_freq)
{
switch (chandef->width) {
- case NL80211_CHAN_WIDTH_5:
- case NL80211_CHAN_WIDTH_10:
case NL80211_CHAN_WIDTH_20:
case NL80211_CHAN_WIDTH_20_NOHT:
case NL80211_CHAN_WIDTH_1:
@@ -414,8 +407,6 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
return false;
switch (chandef->width) {
- case NL80211_CHAN_WIDTH_5:
- case NL80211_CHAN_WIDTH_10:
case NL80211_CHAN_WIDTH_20:
case NL80211_CHAN_WIDTH_20_NOHT:
if (ieee80211_chandef_to_khz(chandef) !=
@@ -458,6 +449,19 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
if (!cfg80211_chandef_valid_control_freq(chandef, control_freq))
return false;
+ if (chandef->npca_chan) {
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_80:
+ case NL80211_CHAN_WIDTH_160:
+ case NL80211_CHAN_WIDTH_320:
+ break;
+ default:
+ return false;
+ }
+ } else if (chandef->npca_punctured) {
+ return false;
+ }
+
if (!cfg80211_valid_center_freq(chandef->center_freq1, chandef->width))
return false;
@@ -477,7 +481,8 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
if (!cfg80211_chandef_is_s1g(chandef) && chandef->s1g_primary_2mhz)
return false;
- return valid_puncturing_bitmap(chandef);
+ return valid_puncturing_bitmap(chandef, control_freq,
+ chandef->punctured);
}
EXPORT_SYMBOL(cfg80211_chandef_valid);
@@ -520,6 +525,220 @@ int cfg80211_chandef_primary(const struct cfg80211_chan_def *c,
}
EXPORT_SYMBOL(cfg80211_chandef_primary);
+bool cfg80211_chandef_npca_valid(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef,
+ const struct ieee80211_uhr_npca_info *npca)
+{
+ struct cfg80211_chan_def tmp = *chandef;
+ bool pri_upper, npca_upper;
+ u32 cf1;
+
+ if (chandef->npca_chan || chandef->npca_punctured)
+ return false;
+
+ if (!npca)
+ return true;
+
+ if (cfg80211_chandef_add_npca(wiphy, &tmp, npca))
+ return false;
+
+ if (!cfg80211_chandef_valid_control_freq(&tmp,
+ tmp.npca_chan->center_freq))
+ return false;
+
+ cf1 = tmp.center_freq1;
+ pri_upper = tmp.chan->center_freq > cf1;
+ npca_upper = tmp.npca_chan->center_freq > cf1;
+
+ if (pri_upper == npca_upper)
+ return false;
+
+ if (!valid_puncturing_bitmap(&tmp,
+ tmp.npca_chan->center_freq,
+ tmp.npca_punctured) ||
+ (tmp.punctured & tmp.npca_punctured) != tmp.punctured)
+ return false;
+
+ return true;
+}
+EXPORT_SYMBOL(cfg80211_chandef_npca_valid);
+
+int cfg80211_chandef_add_npca(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef,
+ const struct ieee80211_uhr_npca_info *npca)
+{
+ struct cfg80211_chan_def new_chandef = *chandef;
+ u32 width, npca_freq;
+ u8 offs;
+
+ if (chandef->npca_chan || chandef->npca_punctured)
+ return -EINVAL;
+
+ if (WARN_ON(!cfg80211_chandef_valid(chandef)))
+ return -EINVAL;
+
+ if (!npca)
+ return 0;
+
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_80:
+ case NL80211_CHAN_WIDTH_160:
+ case NL80211_CHAN_WIDTH_320:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ offs = le32_get_bits(npca->params,
+ IEEE80211_UHR_NPCA_PARAMS_PRIMARY_CHAN_OFFS);
+
+ width = cfg80211_chandef_get_width(chandef);
+ npca_freq = chandef->center_freq1 - width / 2 + 10 + 20 * offs;
+ new_chandef.npca_chan = ieee80211_get_channel(wiphy, npca_freq);
+ if (!new_chandef.npca_chan)
+ return -EINVAL;
+
+ if (npca->params & cpu_to_le32(IEEE80211_UHR_NPCA_PARAMS_DIS_SUBCH_BMAP_PRES))
+ new_chandef.npca_punctured = le16_to_cpu(npca->dis_subch_bmap[0]);
+
+ if (!cfg80211_chandef_valid(&new_chandef))
+ return -EINVAL;
+
+ *chandef = new_chandef;
+ return 0;
+}
+EXPORT_SYMBOL(cfg80211_chandef_add_npca);
+
+int cfg80211_chandef_add_dbe(struct cfg80211_chan_def *chandef,
+ const struct ieee80211_uhr_dbe_info *dbe)
+{
+ struct cfg80211_chan_def new_chandef = *chandef;
+ u16 starting_freq, bw_mhz, start_old, start_new;
+ u8 bw, punct_shift;
+ int offset, index;
+
+ if (!dbe)
+ return 0;
+
+ if (!cfg80211_chandef_valid(chandef))
+ return -EINVAL;
+
+ if (chandef->width == NL80211_CHAN_WIDTH_20_NOHT)
+ return -EINVAL;
+
+ bw = u8_get_bits(dbe->params, IEEE80211_UHR_DBE_OPER_BANDWIDTH);
+
+ switch (chandef->chan->band) {
+ case NL80211_BAND_5GHZ:
+ if (bw > IEEE80211_UHR_DBE_OPER_BW_160)
+ return -EINVAL;
+ if (chandef->chan->center_freq < 5745)
+ starting_freq = 5180; /* channel 36 */
+ else
+ starting_freq = 5745; /* channel 149 */
+ break;
+ case NL80211_BAND_6GHZ:
+ starting_freq = 5955; /* channel 1 center */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (bw) {
+ case IEEE80211_UHR_DBE_OPER_BW_320_2:
+ case IEEE80211_UHR_DBE_OPER_BW_320_1:
+ if (chandef->width == NL80211_CHAN_WIDTH_160)
+ break;
+ fallthrough;
+ case IEEE80211_UHR_DBE_OPER_BW_160:
+ if (chandef->width == NL80211_CHAN_WIDTH_80)
+ break;
+ fallthrough;
+ case IEEE80211_UHR_DBE_OPER_BW_80:
+ if (chandef->width == NL80211_CHAN_WIDTH_40)
+ break;
+ fallthrough;
+ case IEEE80211_UHR_DBE_OPER_BW_40:
+ if (chandef->width == NL80211_CHAN_WIDTH_20)
+ break;
+ fallthrough;
+ default:
+ return -EINVAL;
+ }
+
+ switch (bw) {
+ case IEEE80211_UHR_DBE_OPER_BW_320_2:
+ /* 320-2 starts shifted by 160 */
+ starting_freq += 160;
+ fallthrough;
+ case IEEE80211_UHR_DBE_OPER_BW_320_1:
+ new_chandef.width = NL80211_CHAN_WIDTH_320;
+ bw_mhz = 320;
+ break;
+ case IEEE80211_UHR_DBE_OPER_BW_160:
+ new_chandef.width = NL80211_CHAN_WIDTH_160;
+ bw_mhz = 160;
+ break;
+ case IEEE80211_UHR_DBE_OPER_BW_80:
+ new_chandef.width = NL80211_CHAN_WIDTH_80;
+ bw_mhz = 80;
+ break;
+ case IEEE80211_UHR_DBE_OPER_BW_40:
+ new_chandef.width = NL80211_CHAN_WIDTH_40;
+ bw_mhz = 40;
+ break;
+ }
+
+ /* this should only happen for 320-2 and misconfigured AP */
+ if (chandef->chan->center_freq < starting_freq)
+ return -EINVAL;
+
+ offset = chandef->chan->center_freq - starting_freq;
+ index = offset / bw_mhz;
+ start_new = starting_freq - 10 + index * bw_mhz;
+ new_chandef.center_freq1 = start_new + bw_mhz / 2;
+
+ start_old = chandef->center_freq1 -
+ cfg80211_chandef_get_width(chandef) / 2;
+
+ /*
+ * If the DBE channel extends downward below the lower
+ * edge of the BSS channel, we need to shift puncturing
+ * bitmaps up to adjust for that.
+ */
+ if (start_new < start_old)
+ punct_shift = (start_old - start_new) / 20;
+ else
+ punct_shift = 0;
+
+ new_chandef.punctured <<= punct_shift;
+ new_chandef.npca_punctured <<= punct_shift;
+
+ if (dbe->params & IEEE80211_UHR_DBE_OPER_DIS_SUBCHANNEL_BITMAP_PRES) {
+ u16 punct_mask = ((1 << (bw_mhz / 40)) - 1) << punct_shift;
+ u16 punctured = le16_to_cpu(dbe->dis_subch_bmap[0]);
+
+ if ((punctured & punct_mask) != (new_chandef.punctured & punct_mask))
+ return -EINVAL;
+
+ new_chandef.punctured = punctured;
+ }
+
+ if (!cfg80211_chandef_valid(&new_chandef))
+ return -EINVAL;
+
+ /*
+ * If e.g. a 40 MHz BSS channel (erroneously) occupies the center of the
+ * DBE 80 MHz channel, they would be incompatible; check and reject.
+ */
+ if (!cfg80211_chandef_compatible(&new_chandef, chandef))
+ return -EINVAL;
+
+ *chandef = new_chandef;
+ return 0;
+}
+EXPORT_SYMBOL(cfg80211_chandef_add_dbe);
+
static const struct cfg80211_chan_def *
check_chandef_primary_compat(const struct cfg80211_chan_def *c1,
const struct cfg80211_chan_def *c2,
@@ -565,18 +784,25 @@ _cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1,
return NULL;
/*
- * can't be compatible if one of them is 5/10 MHz or S1G
+ * We need NPCA to be compatible for some scenarios such as
+ * multiple APs, but in this case userspace should configure
+ * identical chandefs including NPCA, even if perhaps one of
+ * the AP interfaces doesn't even advertise it.
+ */
+ if (c1->npca_chan || c2->npca_chan)
+ return NULL;
+
+ /*
+ * can't be compatible if one of them is S1G
* but they don't have the same width.
*/
-#define NARROW_OR_S1G(width) ((width) == NL80211_CHAN_WIDTH_5 || \
- (width) == NL80211_CHAN_WIDTH_10 || \
- (width) == NL80211_CHAN_WIDTH_1 || \
- (width) == NL80211_CHAN_WIDTH_2 || \
- (width) == NL80211_CHAN_WIDTH_4 || \
- (width) == NL80211_CHAN_WIDTH_8 || \
- (width) == NL80211_CHAN_WIDTH_16)
-
- if (NARROW_OR_S1G(c1->width) || NARROW_OR_S1G(c2->width))
+#define IS_S1G(width) ((width) == NL80211_CHAN_WIDTH_1 || \
+ (width) == NL80211_CHAN_WIDTH_2 || \
+ (width) == NL80211_CHAN_WIDTH_4 || \
+ (width) == NL80211_CHAN_WIDTH_8 || \
+ (width) == NL80211_CHAN_WIDTH_16)
+
+ if (IS_S1G(c1->width) || IS_S1G(c2->width))
return NULL;
/*
@@ -817,6 +1043,7 @@ int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_P2P_DEVICE:
case NL80211_IFTYPE_NAN_DATA:
+ case NL80211_IFTYPE_PD:
break;
case NL80211_IFTYPE_WDS:
case NL80211_IFTYPE_UNSPECIFIED:
@@ -941,6 +1168,7 @@ bool cfg80211_beaconing_iface_active(struct wireless_dev *wdev)
/* Can NAN type be considered as beaconing interface? */
case NL80211_IFTYPE_NAN:
case NL80211_IFTYPE_NAN_DATA:
+ case NL80211_IFTYPE_PD:
break;
case NL80211_IFTYPE_UNSPECIFIED:
case NL80211_IFTYPE_WDS:
@@ -1266,13 +1494,6 @@ bool _cfg80211_chandef_usable(struct wiphy *wiphy,
control_freq = chandef->chan->center_freq;
switch (chandef->width) {
- case NL80211_CHAN_WIDTH_5:
- width = 5;
- break;
- case NL80211_CHAN_WIDTH_10:
- prohibited_flags |= IEEE80211_CHAN_NO_10MHZ;
- width = 10;
- break;
case NL80211_CHAN_WIDTH_20:
if (!ht_cap->ht_supported &&
chandef->chan->band != NL80211_BAND_6GHZ)
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 6783e0672dcb..d13310fef691 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -237,6 +237,7 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,
if (!wdev_running(wdev))
return;
+ cfg80211_pmsr_wdev_down(wdev);
rdev_stop_p2p_device(rdev, wdev);
wdev->is_running = false;
@@ -264,6 +265,8 @@ void cfg80211_stop_nan(struct cfg80211_registered_device *rdev,
if (!wdev_running(wdev))
return;
+ cfg80211_pmsr_wdev_down(wdev);
+
/*
* If there is a scheduled update pending, mark it as canceled, so the
* empty schedule will be accepted
@@ -322,6 +325,28 @@ int cfg80211_nan_set_local_schedule(struct cfg80211_registered_device *rdev,
return 0;
}
+void cfg80211_stop_pd(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev)
+{
+ lockdep_assert_held(&rdev->wiphy.mtx);
+
+ if (WARN_ON(wdev->iftype != NL80211_IFTYPE_PD))
+ return;
+
+ if (!rdev->ops->stop_pd)
+ return;
+
+ if (!wdev_running(wdev))
+ return;
+
+ cfg80211_pmsr_wdev_down(wdev);
+
+ rdev_stop_pd(rdev, wdev);
+ wdev->is_running = false;
+
+ rdev->opencount--;
+}
+
void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy)
{
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
@@ -351,6 +376,9 @@ void cfg80211_shutdown_all_interfaces(struct wiphy *wiphy)
case NL80211_IFTYPE_NAN:
cfg80211_stop_nan(rdev, wdev);
break;
+ case NL80211_IFTYPE_PD:
+ cfg80211_stop_pd(rdev, wdev);
+ break;
default:
break;
}
@@ -804,6 +832,24 @@ static int wiphy_verify_combinations(struct wiphy *wiphy)
return ret;
}
+static bool wiphy_cipher_suites_valid(const struct wiphy *wiphy)
+{
+ int i, j;
+
+ if (wiphy->n_cipher_suites && !wiphy->cipher_suites)
+ return false;
+
+ for (i = 0; i < wiphy->n_cipher_suites; i++) {
+ for (j = 0; j < i; j++) {
+ if (wiphy->cipher_suites[i] ==
+ wiphy->cipher_suites[j])
+ return false;
+ }
+ }
+
+ return true;
+}
+
int wiphy_register(struct wiphy *wiphy)
{
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
@@ -828,6 +874,9 @@ int wiphy_register(struct wiphy *wiphy)
(!rdev->ops->tdls_channel_switch ||
!rdev->ops->tdls_cancel_channel_switch)))
return -EINVAL;
+ if (WARN_ON((wiphy->interface_modes & BIT(NL80211_IFTYPE_PD)) &&
+ (!rdev->ops->start_pd || !rdev->ops->stop_pd)))
+ return -EINVAL;
if (WARN_ON((wiphy->interface_modes & BIT(NL80211_IFTYPE_NAN)) &&
(!rdev->ops->start_nan || !rdev->ops->stop_nan ||
@@ -836,7 +885,7 @@ int wiphy_register(struct wiphy *wiphy)
return -EINVAL;
if (WARN_ON((wiphy->interface_modes & BIT(NL80211_IFTYPE_NAN_DATA)) &&
- !wiphy->nan_capa.phy.ht.ht_supported))
+ (!wiphy->nan_capa.phy.ht.ht_supported || wiphy->n_radio > 1)))
return -EINVAL;
if (WARN_ON(wiphy->interface_modes & BIT(NL80211_IFTYPE_WDS)))
@@ -871,9 +920,7 @@ int wiphy_register(struct wiphy *wiphy)
BIT(NL80211_CHAN_WIDTH_80) |
BIT(NL80211_CHAN_WIDTH_80P80) |
BIT(NL80211_CHAN_WIDTH_160) |
- BIT(NL80211_CHAN_WIDTH_320) |
- BIT(NL80211_CHAN_WIDTH_5) |
- BIT(NL80211_CHAN_WIDTH_10))))
+ BIT(NL80211_CHAN_WIDTH_320))))
return -EINVAL;
}
@@ -940,6 +987,9 @@ int wiphy_register(struct wiphy *wiphy)
if (res)
return res;
+ if (!wiphy_cipher_suites_valid(wiphy))
+ return -EINVAL;
+
/* sanity check supported bands/channels */
for (band = 0; band < NUM_NL80211_BANDS; band++) {
const struct ieee80211_sband_iftype_data *iftd;
@@ -1288,6 +1338,7 @@ void wiphy_unregister(struct wiphy *wiphy)
/* this has nothing to do now but make sure it's gone */
cancel_work_sync(&rdev->wiphy_work);
+ cancel_work_sync(&rdev->sched_scan_res_wk);
cancel_work_sync(&rdev->rfkill_block);
cancel_work_sync(&rdev->conn_work);
flush_work(&rdev->event_work);
@@ -1377,6 +1428,7 @@ static void _cfg80211_unregister_wdev(struct wireless_dev *wdev,
list_del_rcu(&wdev->list);
synchronize_net();
rdev->devlist_generation++;
+ wiphy_work_cancel(wdev->wiphy, &wdev->disconnect_wk);
cfg80211_mlme_purge_registrations(wdev);
@@ -1387,6 +1439,9 @@ static void _cfg80211_unregister_wdev(struct wireless_dev *wdev,
case NL80211_IFTYPE_NAN:
cfg80211_stop_nan(rdev, wdev);
break;
+ case NL80211_IFTYPE_PD:
+ cfg80211_stop_pd(rdev, wdev);
+ break;
default:
break;
}
@@ -1495,6 +1550,9 @@ void cfg80211_leave_locked(struct cfg80211_registered_device *rdev,
case NL80211_IFTYPE_NAN:
cfg80211_stop_nan(rdev, wdev);
break;
+ case NL80211_IFTYPE_PD:
+ cfg80211_stop_pd(rdev, wdev);
+ break;
case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_MONITOR:
case NL80211_IFTYPE_NAN_DATA:
@@ -1560,7 +1618,7 @@ void cfg80211_init_wdev(struct wireless_dev *wdev)
INIT_LIST_HEAD(&wdev->mgmt_registrations);
INIT_LIST_HEAD(&wdev->pmsr_list);
spin_lock_init(&wdev->pmsr_lock);
- INIT_WORK(&wdev->pmsr_free_wk, cfg80211_pmsr_free_wk);
+ wiphy_work_init(&wdev->pmsr_free_wk, cfg80211_pmsr_free_wk);
#ifdef CONFIG_CFG80211_WEXT
wdev->wext.default_key = -1;
@@ -1584,7 +1642,7 @@ void cfg80211_init_wdev(struct wireless_dev *wdev)
wdev->iftype == NL80211_IFTYPE_ADHOC) && !wdev->use_4addr)
wdev->netdev->priv_flags |= IFF_DONT_BRIDGE;
- INIT_WORK(&wdev->disconnect_wk, cfg80211_autodisconnect_wk);
+ wiphy_work_init(&wdev->disconnect_wk, cfg80211_autodisconnect_wk);
}
void cfg80211_register_wdev(struct cfg80211_registered_device *rdev,
@@ -1690,11 +1748,11 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
break;
case NETDEV_GOING_DOWN:
cfg80211_leave(rdev, wdev, -1);
- scoped_guard(wiphy, &rdev->wiphy)
+ scoped_guard(wiphy, &rdev->wiphy) {
cfg80211_remove_links(wdev);
- /* since we just did cfg80211_leave() nothing to do there */
- cancel_work_sync(&wdev->disconnect_wk);
- cancel_work_sync(&wdev->pmsr_free_wk);
+ /* since we just did cfg80211_leave() nothing to do there */
+ wiphy_work_cancel(wdev->wiphy, &wdev->disconnect_wk);
+ }
break;
case NETDEV_DOWN:
wiphy_lock(&rdev->wiphy);
diff --git a/net/wireless/core.h b/net/wireless/core.h
index ae2d56d3ad90..b4610f6685dc 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -3,7 +3,7 @@
* Wireless configuration interface internals.
*
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
- * Copyright (C) 2018-2025 Intel Corporation
+ * Copyright (C) 2018-2026 Intel Corporation
*/
#ifndef __NET_WIRELESS_CORE_H
#define __NET_WIRELESS_CORE_H
@@ -402,7 +402,7 @@ 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);
+ 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,
@@ -428,7 +428,7 @@ 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 work_struct *work);
+void cfg80211_autodisconnect_wk(struct wiphy *wiphy, struct wiphy_work *work);
/* SME implementation */
void cfg80211_conn_work(struct work_struct *work);
@@ -443,9 +443,11 @@ 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);
+bool cfg80211_valid_key_idx(struct wireless_dev *wdev,
+ int key_idx, bool pairwise,
+ const u8 *mac_addr);
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);
@@ -556,6 +558,8 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,
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,
@@ -583,7 +587,7 @@ cfg80211_get_6ghz_power_type(const u8 *elems, size_t elems_len,
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 work_struct *work);
+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);
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index bd72317c4964..a0d1cde26f0c 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -32,14 +32,11 @@ void cfg80211_rx_assoc_resp(struct net_device *dev,
.timeout_reason = NL80211_TIMEOUT_UNSPECIFIED,
.req_ie = data->req_ies,
.req_ie_len = data->req_ies_len,
- .resp_ie = mgmt->u.assoc_resp.variable,
- .resp_ie_len = data->len -
- offsetof(struct ieee80211_mgmt,
- u.assoc_resp.variable),
- .status = le16_to_cpu(mgmt->u.assoc_resp.status_code),
.ap_mld_addr = data->ap_mld_addr,
+ .assoc_encrypted = data->assoc_encrypted,
};
unsigned int link_id;
+ bool is_s1g = false;
for (link_id = 0; link_id < ARRAY_SIZE(data->links); link_id++) {
cr.links[link_id].status = data->links[link_id].status;
@@ -60,16 +57,32 @@ void cfg80211_rx_assoc_resp(struct net_device *dev,
if (cr.links[link_id].bss->channel->band == NL80211_BAND_S1GHZ) {
WARN_ON(link_id);
- cr.resp_ie = (u8 *)&mgmt->u.s1g_assoc_resp.variable;
- cr.resp_ie_len = data->len -
- offsetof(struct ieee80211_mgmt,
- u.s1g_assoc_resp.variable);
+ is_s1g = true;
}
if (cr.ap_mld_addr)
cr.valid_links |= BIT(link_id);
}
+ if (is_s1g) {
+ if (data->len < offsetof(struct ieee80211_mgmt,
+ u.s1g_assoc_resp.variable))
+ goto free_bss;
+ cr.resp_ie = (u8 *)&mgmt->u.s1g_assoc_resp.variable;
+ cr.resp_ie_len = data->len -
+ offsetof(struct ieee80211_mgmt,
+ u.s1g_assoc_resp.variable);
+ } else {
+ if (data->len < offsetof(struct ieee80211_mgmt,
+ u.assoc_resp.variable))
+ goto free_bss;
+ cr.resp_ie = mgmt->u.assoc_resp.variable;
+ cr.resp_ie_len = data->len -
+ offsetof(struct ieee80211_mgmt,
+ u.assoc_resp.variable);
+ }
+ cr.status = le16_to_cpu(mgmt->u.assoc_resp.status_code);
+
trace_cfg80211_send_rx_assoc(dev, data);
/*
@@ -78,22 +91,24 @@ void cfg80211_rx_assoc_resp(struct net_device *dev,
* and got a reject -- we only try again with an assoc
* frame instead of reassoc.
*/
- if (cfg80211_sme_rx_assoc_resp(wdev, cr.status)) {
- for (link_id = 0; link_id < ARRAY_SIZE(data->links); link_id++) {
- struct cfg80211_bss *bss = data->links[link_id].bss;
-
- if (!bss)
- continue;
-
- cfg80211_unhold_bss(bss_from_pub(bss));
- cfg80211_put_bss(wiphy, bss);
- }
- return;
- }
+ if (cfg80211_sme_rx_assoc_resp(wdev, cr.status))
+ goto free_bss;
nl80211_send_rx_assoc(rdev, dev, data);
/* update current_bss etc., consumes the bss reference */
__cfg80211_connect_result(dev, &cr, cr.status == WLAN_STATUS_SUCCESS);
+ return;
+
+free_bss:
+ for (link_id = 0; link_id < ARRAY_SIZE(data->links); link_id++) {
+ struct cfg80211_bss *bss = data->links[link_id].bss;
+
+ if (!bss)
+ continue;
+
+ cfg80211_unhold_bss(bss_from_pub(bss));
+ cfg80211_put_bss(wiphy, bss);
+ }
}
EXPORT_SYMBOL(cfg80211_rx_assoc_resp);
@@ -150,19 +165,35 @@ void cfg80211_rx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct ieee80211_mgmt *mgmt = (void *)buf;
+ __le16 fc;
lockdep_assert_wiphy(wdev->wiphy);
- trace_cfg80211_rx_mlme_mgmt(dev, buf, len);
+ if (len < sizeof(fc))
+ return;
+
+ fc = mgmt->frame_control;
- if (WARN_ON(len < 2))
+ if (ieee80211_is_auth(fc)) {
+ if (len < offsetofend(struct ieee80211_mgmt, u.auth.status_code))
+ return;
+ } else if (ieee80211_is_deauth(fc)) {
+ if (len < offsetofend(struct ieee80211_mgmt, u.deauth.reason_code))
+ return;
+ } else if (ieee80211_is_disassoc(fc)) {
+ if (len < offsetofend(struct ieee80211_mgmt, u.disassoc.reason_code))
+ return;
+ } else {
return;
+ }
+
+ trace_cfg80211_rx_mlme_mgmt(dev, buf, len);
- if (ieee80211_is_auth(mgmt->frame_control))
+ if (ieee80211_is_auth(fc))
cfg80211_process_auth(wdev, buf, len);
- else if (ieee80211_is_deauth(mgmt->frame_control))
+ else if (ieee80211_is_deauth(fc))
cfg80211_process_deauth(wdev, buf, len, false);
- else if (ieee80211_is_disassoc(mgmt->frame_control))
+ else
cfg80211_process_disassoc(wdev, buf, len, false);
}
EXPORT_SYMBOL(cfg80211_rx_mlme_mgmt);
@@ -215,15 +246,28 @@ void cfg80211_tx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len,
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct ieee80211_mgmt *mgmt = (void *)buf;
+ __le16 fc;
lockdep_assert_wiphy(wdev->wiphy);
- trace_cfg80211_tx_mlme_mgmt(dev, buf, len, reconnect);
+ if (len < sizeof(fc))
+ return;
- if (WARN_ON(len < 2))
+ fc = mgmt->frame_control;
+
+ if (ieee80211_is_deauth(fc)) {
+ if (len < offsetofend(struct ieee80211_mgmt, u.deauth.reason_code))
+ return;
+ } else if (ieee80211_is_disassoc(fc)) {
+ if (len < offsetofend(struct ieee80211_mgmt, u.disassoc.reason_code))
+ return;
+ } else {
return;
+ }
+
+ trace_cfg80211_tx_mlme_mgmt(dev, buf, len, reconnect);
- if (ieee80211_is_deauth(mgmt->frame_control))
+ if (ieee80211_is_deauth(fc))
cfg80211_process_deauth(wdev, buf, len, reconnect);
else
cfg80211_process_disassoc(wdev, buf, len, reconnect);
@@ -360,17 +404,18 @@ cfg80211_mlme_check_mlo_compat(const struct ieee80211_multi_link_elem *mle_a,
* reserved when included in a unicast Probe Response frame and may
* also change when the AP adds/removes links. The BTM MLD
* Recommendation For Multiple APs Support subfield is reserved when
- * transmitted by an AP. All other bits are currently reserved.
- * See IEEE P802.11be/D7.0, Table 9-417o.
+ * transmitted by an AP.
*/
if ((ieee80211_mle_get_ext_mld_capa_op((const u8 *)mle_a) &
(IEEE80211_EHT_ML_EXT_MLD_CAPA_OP_PARAM_UPDATE |
IEEE80211_EHT_ML_EXT_MLD_CAPA_NSTR_UPDATE |
- IEEE80211_EHT_ML_EXT_MLD_CAPA_EMLSR_ENA_ON_ONE_LINK)) !=
+ IEEE80211_EHT_ML_EXT_MLD_CAPA_EMLSR_ENA_ON_ONE_LINK |
+ IEEE80211_UHR_ML_EXT_MLD_CAPA_ML_PM)) !=
(ieee80211_mle_get_ext_mld_capa_op((const u8 *)mle_b) &
(IEEE80211_EHT_ML_EXT_MLD_CAPA_OP_PARAM_UPDATE |
IEEE80211_EHT_ML_EXT_MLD_CAPA_NSTR_UPDATE |
- IEEE80211_EHT_ML_EXT_MLD_CAPA_EMLSR_ENA_ON_ONE_LINK))) {
+ IEEE80211_EHT_ML_EXT_MLD_CAPA_EMLSR_ENA_ON_ONE_LINK |
+ IEEE80211_UHR_ML_EXT_MLD_CAPA_ML_PM))) {
NL_SET_ERR_MSG(extack,
"extended link MLD capabilities/ops mismatch");
return -EINVAL;
@@ -849,7 +894,7 @@ static bool cfg80211_allowed_random_address(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)
+ struct cfg80211_mgmt_tx_params *params, u64 cookie)
{
const struct ieee80211_mgmt *mgmt;
u16 stype;
@@ -944,6 +989,7 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
* fall through, P2P device only supports
* public action frames
*/
+ case NL80211_IFTYPE_PD:
default:
err = -EOPNOTSUPP;
break;
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index f334cdef8958..44f2bad08670 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -441,7 +441,7 @@ static int validate_uhr_operation(const struct nlattr *attr,
const u8 *data = nla_data(attr);
unsigned int len = nla_len(attr);
- if (!ieee80211_uhr_oper_size_ok(data, len, false))
+ if (!ieee80211_uhr_oper_size_ok(data, len))
return -EINVAL;
return 0;
}
@@ -461,7 +461,9 @@ nl80211_ftm_responder_policy[NL80211_FTM_RESP_ATTR_MAX + 1] = {
static const struct nla_policy
nl80211_pmsr_ftm_req_attr_policy[NL80211_PMSR_FTM_REQ_ATTR_MAX + 1] = {
[NL80211_PMSR_FTM_REQ_ATTR_ASAP] = { .type = NLA_FLAG },
- [NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE] = { .type = NLA_U32 },
+ [NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE] =
+ NLA_POLICY_RANGE(NLA_U32, NL80211_PREAMBLE_LEGACY,
+ NL80211_PREAMBLE_HE),
[NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP] =
NLA_POLICY_MAX(NLA_U8, 15),
[NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD] = { .type = NLA_U16 },
@@ -476,6 +478,18 @@ nl80211_pmsr_ftm_req_attr_policy[NL80211_PMSR_FTM_REQ_ATTR_MAX + 1] = {
[NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK] = { .type = NLA_FLAG },
[NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR] = { .type = NLA_U8 },
[NL80211_PMSR_FTM_REQ_ATTR_RSTA] = { .type = NLA_FLAG },
+ [NL80211_PMSR_FTM_REQ_ATTR_MIN_TIME_BETWEEN_MEASUREMENTS] = {
+ .type = NLA_U32
+ },
+ [NL80211_PMSR_FTM_REQ_ATTR_MAX_TIME_BETWEEN_MEASUREMENTS] = {
+ .type = NLA_U32
+ },
+ [NL80211_PMSR_FTM_REQ_ATTR_NOMINAL_TIME] = { .type = NLA_U32 },
+ [NL80211_PMSR_FTM_REQ_ATTR_AW_DURATION] = NLA_POLICY_MAX(NLA_U32, 255),
+ [NL80211_PMSR_FTM_REQ_ATTR_NUM_MEASUREMENTS] = { .type = NLA_U32 },
+ [NL80211_PMSR_FTM_REQ_ATTR_INGRESS] = { .type = NLA_U64 },
+ [NL80211_PMSR_FTM_REQ_ATTR_EGRESS] = { .type = NLA_U64 },
+ [NL80211_PMSR_FTM_REQ_ATTR_PD_SUPPRESS_RESULTS] = { .type = NLA_FLAG },
};
static const struct nla_policy
@@ -498,6 +512,8 @@ nl80211_pmsr_peer_attr_policy[NL80211_PMSR_PEER_ATTR_MAX + 1] = {
[NL80211_PMSR_PEER_ATTR_REQ] =
NLA_POLICY_NESTED(nl80211_pmsr_req_attr_policy),
[NL80211_PMSR_PEER_ATTR_RESP] = { .type = NLA_REJECT },
+ [NL80211_PMSR_PEER_ATTR_REQ_TYPE] =
+ NLA_POLICY_MAX(NLA_U32, NL80211_PMSR_FTM_REQ_TYPE_MAX),
};
static const struct nla_policy
@@ -616,7 +632,7 @@ nl80211_mbssid_config_policy[NL80211_MBSSID_CONFIG_ATTR_MAX + 1] = {
[NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX] = { .type = NLA_U32 },
[NL80211_MBSSID_CONFIG_ATTR_EMA] = { .type = NLA_FLAG },
[NL80211_MBSSID_CONFIG_ATTR_TX_LINK_ID] =
- NLA_POLICY_MAX(NLA_U8, IEEE80211_MLD_MAX_NUM_LINKS),
+ NLA_POLICY_RANGE(NLA_U8, 0, IEEE80211_MLD_MAX_NUM_LINKS - 1),
};
static const struct nla_policy
@@ -1047,7 +1063,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
NL80211_MAX_SUPP_SELECTORS),
[NL80211_ATTR_MLO_RECONF_REM_LINKS] = { .type = NLA_U16 },
[NL80211_ATTR_EPCS] = { .type = NLA_FLAG },
- [NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS] = { .type = NLA_U16 },
+ [NL80211_ATTR_EXT_MLD_CAPA_AND_OPS] = { .type = NLA_U16 },
[NL80211_ATTR_WIPHY_RADIO_INDEX] = { .type = NLA_U8 },
[NL80211_ATTR_S1G_LONG_BEACON_PERIOD] = NLA_POLICY_MIN(NLA_U8, 2),
[NL80211_ATTR_S1G_SHORT_BEACON] =
@@ -1076,6 +1092,10 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[NL80211_ATTR_NAN_MAX_CHAN_SWITCH_TIME] = { .type = NLA_U16 },
[NL80211_ATTR_NAN_PEER_MAPS] =
NLA_POLICY_NESTED_ARRAY(nl80211_nan_peer_map_policy),
+ [NL80211_ATTR_NPCA_PRIMARY_FREQ] = { .type = NLA_U32 },
+ [NL80211_ATTR_NPCA_PUNCT_BITMAP] =
+ NLA_POLICY_FULL_RANGE(NLA_U32, &nl80211_punct_bitmap_range),
+ [NL80211_ATTR_STA_DUMP_LINK_STATS] = { .type = NLA_FLAG },
};
/* policy for the key attributes */
@@ -1089,6 +1109,10 @@ static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = {
[NL80211_KEY_TYPE] = NLA_POLICY_MAX(NLA_U32, NUM_NL80211_KEYTYPES - 1),
[NL80211_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED },
[NL80211_KEY_MODE] = NLA_POLICY_RANGE(NLA_U8, 0, NL80211_KEY_SET_TX),
+ [NL80211_KEY_LTF_SEED] = {
+ .type = NLA_BINARY,
+ .len = WLAN_MAX_SECURE_LTF_KEYSEED_LEN,
+ },
};
/* policy for the key default flags */
@@ -1276,6 +1300,18 @@ static int nl80211_prepare_wdev_dump(struct netlink_callback *cb,
rtnl_unlock();
return -ENODEV;
}
+
+ /*
+ * The first invocation validated the wdev's netns against
+ * the caller via __cfg80211_wdev_from_attrs(). The wiphy
+ * may have moved netns between dumpit invocations (via
+ * NL80211_CMD_SET_WIPHY_NETNS), so re-check here.
+ */
+ if (!net_eq(wiphy_net(wiphy), sock_net(cb->skb->sk))) {
+ rtnl_unlock();
+ return -ENODEV;
+ }
+
*rdev = wiphy_to_rdev(wiphy);
*wdev = NULL;
@@ -1620,6 +1656,11 @@ static int nl80211_parse_key_new(struct genl_info *info, struct nlattr *key,
if (tb[NL80211_KEY_MODE])
k->p.mode = nla_get_u8(tb[NL80211_KEY_MODE]);
+ if (tb[NL80211_KEY_LTF_SEED]) {
+ k->p.ltf_keyseed = nla_data(tb[NL80211_KEY_LTF_SEED]);
+ k->p.ltf_keyseed_len = nla_len(tb[NL80211_KEY_LTF_SEED]);
+ }
+
return 0;
}
@@ -1733,6 +1774,7 @@ static int nl80211_parse_key(struct genl_info *info, struct key_parse *k)
static struct cfg80211_cached_keys *
nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev,
struct genl_info *info, bool *no_ht)
{
struct nlattr *keys = info->attrs[NL80211_ATTR_KEYS];
@@ -1782,7 +1824,7 @@ nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
goto error;
} else if (parse.defmgmt)
goto error;
- err = cfg80211_validate_key_settings(rdev, &parse.p,
+ err = cfg80211_validate_key_settings(rdev, wdev, &parse.p,
parse.idx, false, NULL);
if (err)
goto error;
@@ -1841,6 +1883,11 @@ static int nl80211_key_allowed(struct wireless_dev *wdev)
NL80211_EXT_FEATURE_SECURE_NAN))
return 0;
return -EINVAL;
+ case NL80211_IFTYPE_PD:
+ if (wiphy_ext_feature_isset(wdev->wiphy,
+ NL80211_EXT_FEATURE_SECURE_RTT))
+ return 0;
+ return -EINVAL;
case NL80211_IFTYPE_UNSPECIFIED:
case NL80211_IFTYPE_OCB:
case NL80211_IFTYPE_MONITOR:
@@ -2400,7 +2447,7 @@ static int nl80211_add_commands_unsplit(struct cfg80211_registered_device *rdev,
}
if (rdev->wiphy.max_sched_scan_reqs)
CMD(sched_scan_start, START_SCHED_SCAN);
- CMD(probe_client, PROBE_CLIENT);
+ CMD(probe_peer, PROBE_PEER);
CMD(set_noack_map, SET_NOACK_MAP);
if (rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) {
i++;
@@ -2497,10 +2544,117 @@ nl80211_send_pmsr_ftm_capa(const struct cfg80211_pmsr_capabilities *cap,
nla_put_u32(msg, NL80211_PMSR_FTM_CAPA_ATTR_MAX_TOTAL_LTF_RX,
cap->ftm.max_total_ltf_rx))
return -ENOBUFS;
- if (cap->ftm.support_rsta &&
- nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_RSTA_SUPPORT))
+
+ if (cap->ftm.ista.support_ntb || cap->ftm.ista.support_tb ||
+ cap->ftm.ista.support_edca) {
+ struct nlattr *ista_caps;
+
+ ista_caps = nla_nest_start_noflag(msg,
+ NL80211_PMSR_FTM_CAPA_ATTR_ISTA_CAPS);
+ if (!ista_caps)
+ return -ENOBUFS;
+ if (cap->ftm.ista.support_ntb &&
+ nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_NTB))
+ return -ENOBUFS;
+ if (cap->ftm.ista.support_tb &&
+ nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_TB))
+ return -ENOBUFS;
+ if (cap->ftm.ista.support_edca &&
+ nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_EDCA))
+ return -ENOBUFS;
+ if (cap->ftm.ista.max_peers &&
+ nla_put_u32(msg, NL80211_PMSR_ATTR_MAX_PEER_ISTA_ROLE,
+ cap->ftm.ista.max_peers))
+ return -ENOBUFS;
+ nla_nest_end(msg, ista_caps);
+ }
+
+ if (cap->ftm.rsta.support_ntb || cap->ftm.rsta.support_tb ||
+ cap->ftm.rsta.support_edca) {
+ struct nlattr *rsta_caps;
+
+ /*
+ * Set the generic RSTA_SUPPORT flag if any of the specific
+ * ranging modes is supported to maintain the backward
+ * compatibility.
+ */
+ if (nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_RSTA_SUPPORT))
+ return -ENOBUFS;
+
+ rsta_caps = nla_nest_start_noflag(msg,
+ NL80211_PMSR_FTM_CAPA_ATTR_RSTA_CAPS);
+ if (!rsta_caps)
+ return -ENOBUFS;
+ if (cap->ftm.rsta.support_ntb &&
+ nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_NTB))
+ return -ENOBUFS;
+ if (cap->ftm.rsta.support_tb &&
+ nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_TB))
+ return -ENOBUFS;
+ if (cap->ftm.rsta.support_edca &&
+ nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_SUPPORT_EDCA))
+ return -ENOBUFS;
+ if (cap->ftm.rsta.max_peers &&
+ nla_put_u32(msg, NL80211_PMSR_ATTR_MAX_PEER_RSTA_ROLE,
+ cap->ftm.rsta.max_peers))
+ return -ENOBUFS;
+ nla_nest_end(msg, rsta_caps);
+ }
+
+ if (cap->ftm.max_no_of_tx_antennas &&
+ nla_put_u8(msg, NL80211_PMSR_FTM_CAPA_ATTR_MAX_NUM_TX_ANTENNAS,
+ cap->ftm.max_no_of_tx_antennas))
+ return -ENOBUFS;
+
+ if (cap->ftm.max_no_of_rx_antennas &&
+ nla_put_u8(msg, NL80211_PMSR_FTM_CAPA_ATTR_MAX_NUM_RX_ANTENNAS,
+ cap->ftm.max_no_of_rx_antennas))
+ return -ENOBUFS;
+
+ if (cap->ftm.min_allowed_ranging_interval_edca &&
+ nla_put_u32(msg, NL80211_PMSR_FTM_CAPA_ATTR_MIN_INTERVAL_EDCA,
+ cap->ftm.min_allowed_ranging_interval_edca))
+ return -ENOBUFS;
+
+ if (cap->ftm.min_allowed_ranging_interval_ntb &&
+ nla_put_u32(msg, NL80211_PMSR_FTM_CAPA_ATTR_MIN_INTERVAL_NTB,
+ cap->ftm.min_allowed_ranging_interval_ntb))
return -ENOBUFS;
+ if (cap->ftm.type.infra_support || cap->ftm.type.pd_support) {
+ struct nlattr *pd_caps;
+
+ pd_caps = nla_nest_start_noflag(msg,
+ NL80211_PMSR_FTM_CAPA_ATTR_TYPE_CAPS);
+ if (!pd_caps)
+ return -ENOBUFS;
+
+ if (cap->ftm.type.infra_support &&
+ nla_put_flag(msg, NL80211_PMSR_FTM_TYPE_CAPA_ATTR_INFRA_SUPPORT))
+ return -ENOBUFS;
+
+ if (cap->ftm.type.pd_support &&
+ nla_put_flag(msg, NL80211_PMSR_FTM_TYPE_CAPA_ATTR_PD_SUPPORT))
+ return -ENOBUFS;
+
+ nla_nest_end(msg, pd_caps);
+ }
+
+ if (cap->ftm.concurrent_ista_rsta_support &&
+ nla_put_flag(msg, NL80211_PMSR_FTM_CAPA_ATTR_CONCURRENT_ISTA_RSTA_SUPPORT))
+ return -ENOBUFS;
+
+ if (cap->ftm.type.pd_support) {
+ if (cap->ftm.pd_preambles &&
+ nla_put_u32(msg, NL80211_PMSR_FTM_CAPA_ATTR_PD_PREAMBLES,
+ cap->ftm.pd_preambles))
+ return -ENOBUFS;
+ if (cap->ftm.pd_bandwidths &&
+ nla_put_u32(msg, NL80211_PMSR_FTM_CAPA_ATTR_PD_BANDWIDTHS,
+ cap->ftm.pd_bandwidths))
+ return -ENOBUFS;
+ }
+
nla_nest_end(msg, ftm);
return 0;
}
@@ -3299,11 +3453,6 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
if (nl80211_send_coalesce(msg, rdev))
goto nla_put_failure;
- if ((rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ) &&
- (nla_put_flag(msg, NL80211_ATTR_SUPPORT_5_MHZ) ||
- nla_put_flag(msg, NL80211_ATTR_SUPPORT_10_MHZ)))
- goto nla_put_failure;
-
if (rdev->wiphy.max_ap_assoc_sta &&
nla_put_u32(msg, NL80211_ATTR_MAX_AP_ASSOC_STA,
rdev->wiphy.max_ap_assoc_sta))
@@ -3460,6 +3609,12 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
NL80211_ATTR_MLD_CAPA_AND_OPS,
capab->mld_capa_and_ops)))
goto nla_put_failure;
+ if (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_MLO &&
+ capab->ext_mld_capa_and_ops &&
+ nla_put_u16(msg,
+ NL80211_ATTR_EXT_MLD_CAPA_AND_OPS,
+ capab->ext_mld_capa_and_ops))
+ goto nla_put_failure;
nla_nest_end(msg, nested_ext_capab);
if (state->split)
@@ -3771,7 +3926,8 @@ static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev)
static int _nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
struct netlink_ext_ack *extack,
struct nlattr **attrs, bool monitor,
- struct cfg80211_chan_def *chandef)
+ struct cfg80211_chan_def *chandef,
+ bool permit_npca)
{
u32 control_freq;
@@ -3885,6 +4041,34 @@ static int _nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
}
}
+ if (attrs[NL80211_ATTR_NPCA_PRIMARY_FREQ]) {
+ if (!permit_npca) {
+ NL_SET_ERR_MSG_ATTR(extack,
+ attrs[NL80211_ATTR_NPCA_PRIMARY_FREQ],
+ "NPCA not supported");
+ return -EINVAL;
+ }
+
+ chandef->npca_chan =
+ ieee80211_get_channel(&rdev->wiphy,
+ nla_get_u32(attrs[NL80211_ATTR_NPCA_PRIMARY_FREQ]));
+ if (!chandef->npca_chan) {
+ NL_SET_ERR_MSG_ATTR(extack,
+ attrs[NL80211_ATTR_NPCA_PRIMARY_FREQ],
+ "invalid NPCA primary channel");
+ return -EINVAL;
+ }
+
+ chandef->npca_punctured =
+ nla_get_u32_default(attrs[NL80211_ATTR_NPCA_PUNCT_BITMAP],
+ chandef->punctured);
+ } else if (attrs[NL80211_ATTR_NPCA_PUNCT_BITMAP]) {
+ NL_SET_ERR_MSG_ATTR(extack,
+ attrs[NL80211_ATTR_NPCA_PUNCT_BITMAP],
+ "NPCA puncturing only valid with NPCA");
+ return -EINVAL;
+ }
+
if (!cfg80211_chandef_valid(chandef)) {
NL_SET_ERR_MSG_ATTR(extack, attrs[NL80211_ATTR_WIPHY_FREQ],
"invalid channel definition");
@@ -3899,10 +4083,9 @@ static int _nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
return -EINVAL;
}
- if ((chandef->width == NL80211_CHAN_WIDTH_5 ||
- chandef->width == NL80211_CHAN_WIDTH_10) &&
- !(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ)) {
- NL_SET_ERR_MSG(extack, "5/10 MHz not supported");
+ if (chandef->width == NL80211_CHAN_WIDTH_5 ||
+ chandef->width == NL80211_CHAN_WIDTH_10) {
+ NL_SET_ERR_MSG(extack, "5/10 MHz not supported any more");
return -EINVAL;
}
@@ -3912,9 +4095,11 @@ static int _nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
struct netlink_ext_ack *extack,
struct nlattr **attrs,
- struct cfg80211_chan_def *chandef)
+ struct cfg80211_chan_def *chandef,
+ bool permit_npca)
{
- return _nl80211_parse_chandef(rdev, extack, attrs, false, chandef);
+ return _nl80211_parse_chandef(rdev, extack, attrs, false, chandef,
+ permit_npca);
}
static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
@@ -3927,6 +4112,7 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
enum nl80211_iftype iftype = NL80211_IFTYPE_MONITOR;
struct wireless_dev *wdev = NULL;
int link_id = _link_id;
+ bool permit_npca;
if (dev)
wdev = dev->ieee80211_ptr;
@@ -3941,9 +4127,13 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
link_id = 0;
}
+ /* allow parsing it - will check on start_ap or below */
+ permit_npca = iftype == NL80211_IFTYPE_AP ||
+ iftype == NL80211_IFTYPE_P2P_GO;
+
result = _nl80211_parse_chandef(rdev, info->extack, info->attrs,
iftype == NL80211_IFTYPE_MONITOR,
- &chandef);
+ &chandef, permit_npca);
if (result)
return result;
@@ -3962,6 +4152,9 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
return -EBUSY;
/* Only allow dynamic channel width changes */
+ cur_chan = wdev->links[link_id].ap.chandef.npca_chan;
+ if (chandef.npca_chan != cur_chan)
+ return -EBUSY;
cur_chan = wdev->links[link_id].ap.chandef.chan;
if (chandef.chan != cur_chan)
return -EBUSY;
@@ -4436,6 +4629,10 @@ int nl80211_send_chandef(struct sk_buff *msg, const struct cfg80211_chan_def *ch
return -ENOBUFS;
if (nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ1, chandef->center_freq1))
return -ENOBUFS;
+ if (chandef->freq1_offset &&
+ nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ1_OFFSET,
+ chandef->freq1_offset))
+ return -ENOBUFS;
if (chandef->center_freq2 &&
nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ2, chandef->center_freq2))
return -ENOBUFS;
@@ -4446,6 +4643,15 @@ int nl80211_send_chandef(struct sk_buff *msg, const struct cfg80211_chan_def *ch
nla_put_flag(msg, NL80211_ATTR_S1G_PRIMARY_2MHZ))
return -ENOBUFS;
+ if (chandef->npca_chan &&
+ nla_put_u32(msg, NL80211_ATTR_NPCA_PRIMARY_FREQ,
+ chandef->npca_chan->center_freq))
+ return -ENOBUFS;
+ if (chandef->npca_punctured &&
+ nla_put_u32(msg, NL80211_ATTR_NPCA_PUNCT_BITMAP,
+ chandef->npca_punctured))
+ return -ENOBUFS;
+
return 0;
}
EXPORT_SYMBOL(nl80211_send_chandef);
@@ -4937,6 +5143,7 @@ static int _nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
return -EOPNOTSUPP;
if ((type == NL80211_IFTYPE_P2P_DEVICE || type == NL80211_IFTYPE_NAN ||
+ type == NL80211_IFTYPE_PD ||
rdev->wiphy.features & NL80211_FEATURE_MAC_ON_CREATE) &&
info->attrs[NL80211_ATTR_MAC]) {
nla_memcpy(params.macaddr, info->attrs[NL80211_ATTR_MAC],
@@ -4993,8 +5200,9 @@ static int _nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
break;
case NL80211_IFTYPE_NAN:
case NL80211_IFTYPE_P2P_DEVICE:
+ case NL80211_IFTYPE_PD:
/*
- * P2P Device and NAN do not have a netdev, so don't go
+ * P2P Device, NAN and PD do not have a netdev, so don't go
* through the netdev notifier and must be added here
*/
cfg80211_init_wdev(wdev);
@@ -5100,7 +5308,7 @@ static int nl80211_validate_key_link_id(struct genl_info *info,
if (wdev->valid_links) {
if (link_id == -1) {
GENL_SET_ERR_MSG(info,
- "link ID must for MLO group key");
+ "link ID must be set for MLO group key");
return -EINVAL;
}
if (!(wdev->valid_links & BIT(link_id))) {
@@ -5207,7 +5415,7 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
if (!rdev->ops->get_key)
return -EOPNOTSUPP;
- if (!pairwise && mac_addr && !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
+ if (!cfg80211_valid_key_idx(wdev, key_idx, pairwise, mac_addr))
return -ENOENT;
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
@@ -5407,7 +5615,7 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
if (!rdev->ops->add_key)
return -EOPNOTSUPP;
- if (cfg80211_validate_key_settings(rdev, &key.p, key.idx,
+ if (cfg80211_validate_key_settings(rdev, wdev, &key.p, key.idx,
key.type == NL80211_KEYTYPE_PAIRWISE,
mac_addr)) {
GENL_SET_ERR_MSG(info, "key setting validation failed");
@@ -5461,8 +5669,9 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
key.type != NL80211_KEYTYPE_GROUP)
return -EINVAL;
- if (!cfg80211_valid_key_idx(rdev, key.idx,
- key.type == NL80211_KEYTYPE_PAIRWISE))
+ if (!cfg80211_valid_key_idx(wdev, key.idx,
+ key.type == NL80211_KEYTYPE_PAIRWISE,
+ mac_addr))
return -EINVAL;
if (!rdev->ops->del_key)
@@ -5470,10 +5679,6 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
err = nl80211_key_allowed(wdev);
- if (key.type == NL80211_KEYTYPE_GROUP && mac_addr &&
- !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
- err = -ENOENT;
-
if (!err)
err = nl80211_validate_key_link_id(info, wdev, link_id,
key.type == NL80211_KEYTYPE_PAIRWISE);
@@ -6309,7 +6514,8 @@ static int nl80211_parse_mbssid_config(struct wiphy *wiphy,
}
static struct cfg80211_mbssid_elems *
-nl80211_parse_mbssid_elems(struct wiphy *wiphy, struct nlattr *attrs)
+nl80211_parse_mbssid_elems(struct wiphy *wiphy, struct nlattr *attrs,
+ struct netlink_ext_ack *extack)
{
struct nlattr *nl_elems;
struct cfg80211_mbssid_elems *elems;
@@ -6320,6 +6526,12 @@ nl80211_parse_mbssid_elems(struct wiphy *wiphy, struct nlattr *attrs)
return ERR_PTR(-EINVAL);
nla_for_each_nested(nl_elems, attrs, rem_elems) {
+ int ret;
+
+ ret = validate_ie_attr(nl_elems, extack);
+ if (ret)
+ return ERR_PTR(ret);
+
if (num_elems >= 255)
return ERR_PTR(-EINVAL);
num_elems++;
@@ -6354,6 +6566,9 @@ nl80211_parse_rnr_elems(struct wiphy *wiphy, struct nlattr *attrs,
if (ret)
return ERR_PTR(ret);
+ if (num_elems >= 255)
+ return ERR_PTR(-EINVAL);
+
num_elems++;
}
@@ -6394,9 +6609,104 @@ static int nl80211_parse_he_bss_color(struct nlattr *attrs,
return 0;
}
+static void nl80211_check_ap_rate_selectors(struct cfg80211_beacon_data *bcn,
+ const struct element *rates)
+{
+ int i;
+
+ if (!rates)
+ return;
+
+ for (i = 0; i < rates->datalen; i++) {
+ if (rates->data[i] == BSS_MEMBERSHIP_SELECTOR_HT_PHY)
+ bcn->ht_required = true;
+ if (rates->data[i] == BSS_MEMBERSHIP_SELECTOR_VHT_PHY)
+ bcn->vht_required = true;
+ }
+}
+
+/*
+ * Since the nl80211 API didn't include, from the beginning, attributes about
+ * HT/VHT/... operation, we parse them out of the elements and check for
+ * validity for use by drivers/mac80211.
+ */
+static int nl80211_calculate_ap_operation(struct nlattr *attrs[],
+ struct cfg80211_beacon_data *bcn,
+ struct netlink_ext_ack *extack)
+{
+ size_t ies_len = bcn->tail_len;
+ const u8 *ies = bcn->tail;
+ const struct element *rates;
+ const struct element *op;
+
+ rates = cfg80211_find_elem(WLAN_EID_SUPP_RATES, ies, ies_len);
+ nl80211_check_ap_rate_selectors(bcn, rates);
+
+ rates = cfg80211_find_elem(WLAN_EID_EXT_SUPP_RATES, ies, ies_len);
+ nl80211_check_ap_rate_selectors(bcn, rates);
+
+ op = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_OPERATION, ies, ies_len);
+ if (op) {
+ if (op->datalen < sizeof(*bcn->he_oper) + 1) {
+ NL_SET_ERR_MSG(extack, "bad HE operation in beacon");
+ return -EINVAL;
+ }
+ bcn->he_oper = (void *)(op->data + 1);
+ /* takes extension ID into account */
+ if (op->datalen < ieee80211_he_oper_size((void *)bcn->he_oper)) {
+ NL_SET_ERR_MSG(extack, "bad HE operation in beacon");
+ return -EINVAL;
+ }
+ }
+
+ op = cfg80211_find_elem(WLAN_EID_HT_OPERATION, ies, ies_len);
+ if (op) {
+ if (op->datalen < sizeof(*bcn->ht_oper)) {
+ NL_SET_ERR_MSG(extack, "bad HT operation in beacon");
+ return -EINVAL;
+ }
+ bcn->ht_oper = (void *)op->data;
+ }
+
+ op = cfg80211_find_elem(WLAN_EID_VHT_OPERATION, ies, ies_len);
+ if (op) {
+ if (op->datalen < sizeof(*bcn->vht_oper)) {
+ NL_SET_ERR_MSG(extack, "bad VHT operation in beacon");
+ return -EINVAL;
+ }
+ bcn->vht_oper = (void *)op->data;
+ }
+
+ op = cfg80211_find_ext_elem(WLAN_EID_EXT_EHT_OPERATION, ies, ies_len);
+ if (op) {
+ if (!ieee80211_eht_oper_size_ok(op->data + 1,
+ op->datalen - 1)) {
+ NL_SET_ERR_MSG(extack, "bad EHT operation in beacon");
+ return -EINVAL;
+ }
+ bcn->eht_oper = (void *)(op->data + 1);
+ }
+
+ op = cfg80211_find_ext_elem(WLAN_EID_EXT_UHR_OPER, ies, ies_len);
+ if (op) {
+ /* need full UHR operation separately */
+ if (!attrs[NL80211_ATTR_UHR_OPERATION]) {
+ NL_SET_ERR_MSG(extack, "missing UHR operation");
+ return -EINVAL;
+ }
+ bcn->uhr_oper = nla_data(attrs[NL80211_ATTR_UHR_OPERATION]);
+ } else if (attrs[NL80211_ATTR_UHR_OPERATION]) {
+ NL_SET_ERR_MSG(extack, "unexpected UHR operation");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int nl80211_parse_beacon(struct cfg80211_registered_device *rdev,
struct nlattr *attrs[],
struct cfg80211_beacon_data *bcn,
+ struct ieee80211_channel *chan,
struct netlink_ext_ack *extack)
{
bool haveinfo = false;
@@ -6488,7 +6798,8 @@ static int nl80211_parse_beacon(struct cfg80211_registered_device *rdev,
if (attrs[NL80211_ATTR_MBSSID_ELEMS]) {
struct cfg80211_mbssid_elems *mbssid =
nl80211_parse_mbssid_elems(&rdev->wiphy,
- attrs[NL80211_ATTR_MBSSID_ELEMS]);
+ attrs[NL80211_ATTR_MBSSID_ELEMS],
+ extack);
if (IS_ERR(mbssid))
return PTR_ERR(mbssid);
@@ -6504,13 +6815,28 @@ static int nl80211_parse_beacon(struct cfg80211_registered_device *rdev,
if (IS_ERR(rnr))
return PTR_ERR(rnr);
- if (rnr && rnr->cnt < bcn->mbssid_ies->cnt)
+ if (rnr && rnr->cnt < bcn->mbssid_ies->cnt) {
+ kfree(rnr);
return -EINVAL;
+ }
bcn->rnr_ies = rnr;
}
}
+ err = nl80211_calculate_ap_operation(attrs, bcn, extack);
+ if (err)
+ return err;
+
+ if (bcn->he_oper && (chan->flags & IEEE80211_CHAN_NO_HE))
+ return -EOPNOTSUPP;
+
+ if (bcn->eht_oper && (chan->flags & IEEE80211_CHAN_NO_EHT))
+ return -EOPNOTSUPP;
+
+ if (bcn->uhr_oper && (chan->flags & IEEE80211_CHAN_NO_UHR))
+ return -EOPNOTSUPP;
+
return 0;
}
@@ -6628,77 +6954,62 @@ nl80211_parse_unsol_bcast_probe_resp(struct cfg80211_registered_device *rdev,
return 0;
}
-static void nl80211_check_ap_rate_selectors(struct cfg80211_ap_settings *params,
- const struct element *rates)
-{
- int i;
-
- if (!rates)
- return;
-
- for (i = 0; i < rates->datalen; i++) {
- if (rates->data[i] == BSS_MEMBERSHIP_SELECTOR_HT_PHY)
- params->ht_required = true;
- if (rates->data[i] == BSS_MEMBERSHIP_SELECTOR_VHT_PHY)
- params->vht_required = true;
- if (rates->data[i] == BSS_MEMBERSHIP_SELECTOR_HE_PHY)
- params->he_required = true;
- if (rates->data[i] == BSS_MEMBERSHIP_SELECTOR_SAE_H2E)
- params->sae_h2e_required = true;
- }
-}
-
/*
* Since the nl80211 API didn't include, from the beginning, attributes about
- * HT/VHT requirements/capabilities, we parse them out of the IEs for the
- * benefit of drivers that rebuild IEs in the firmware.
+ * HT/VHT/... capabilities, we parse them out of the elements and check for
+ * validity for use by drivers/mac80211.
*/
-static int nl80211_calculate_ap_params(struct cfg80211_ap_settings *params)
+static int nl80211_calculate_ap_capabilities(struct genl_info *info,
+ struct cfg80211_ap_settings *params)
{
- const struct cfg80211_beacon_data *bcn = &params->beacon;
- size_t ies_len = bcn->tail_len;
- const u8 *ies = bcn->tail;
- const struct element *rates;
+ size_t ies_len = params->beacon.tail_len;
+ const u8 *ies = params->beacon.tail;
const struct element *cap;
- rates = cfg80211_find_elem(WLAN_EID_SUPP_RATES, ies, ies_len);
- nl80211_check_ap_rate_selectors(params, rates);
-
- rates = cfg80211_find_elem(WLAN_EID_EXT_SUPP_RATES, ies, ies_len);
- nl80211_check_ap_rate_selectors(params, rates);
-
cap = cfg80211_find_elem(WLAN_EID_HT_CAPABILITY, ies, ies_len);
- if (cap && cap->datalen >= sizeof(*params->ht_cap))
+ if (cap) {
+ if (cap->datalen < sizeof(*params->ht_cap)) {
+ GENL_SET_ERR_MSG(info, "bad HT capability in beacon");
+ return -EINVAL;
+ }
params->ht_cap = (void *)cap->data;
+ }
+
cap = cfg80211_find_elem(WLAN_EID_VHT_CAPABILITY, ies, ies_len);
- if (cap && cap->datalen >= sizeof(*params->vht_cap))
+ if (cap) {
+ if (cap->datalen < sizeof(*params->vht_cap)) {
+ GENL_SET_ERR_MSG(info, "bad VHT capability in beacon");
+ return -EINVAL;
+ }
params->vht_cap = (void *)cap->data;
+ }
+
cap = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_CAPABILITY, ies, ies_len);
- if (cap && cap->datalen >= sizeof(*params->he_cap) + 1)
+ if (cap) {
+ if (cap->datalen < sizeof(*params->he_cap) + 1) {
+ GENL_SET_ERR_MSG(info, "bad HE capability in beacon");
+ return -EINVAL;
+ }
params->he_cap = (void *)(cap->data + 1);
- cap = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_OPERATION, ies, ies_len);
- if (cap && cap->datalen >= sizeof(*params->he_oper) + 1)
- params->he_oper = (void *)(cap->data + 1);
+ }
+
cap = cfg80211_find_ext_elem(WLAN_EID_EXT_EHT_CAPABILITY, ies, ies_len);
if (cap) {
- if (!cap->datalen)
- return -EINVAL;
params->eht_cap = (void *)(cap->data + 1);
if (!ieee80211_eht_capa_size_ok((const u8 *)params->he_cap,
(const u8 *)params->eht_cap,
- cap->datalen - 1, true))
- return -EINVAL;
- }
- cap = cfg80211_find_ext_elem(WLAN_EID_EXT_EHT_OPERATION, ies, ies_len);
- if (cap) {
- if (!cap->datalen)
- return -EINVAL;
- params->eht_oper = (void *)(cap->data + 1);
- if (!ieee80211_eht_oper_size_ok((const u8 *)params->eht_oper,
- cap->datalen - 1))
+ cap->datalen - 1, true)) {
+ GENL_SET_ERR_MSG(info, "bad EHT capability in beacon");
return -EINVAL;
+ }
}
+ if (!!params->he_cap != !!params->beacon.he_oper)
+ return -EINVAL;
+
+ if (!!params->eht_cap != !!params->beacon.eht_oper)
+ return -EINVAL;
+
return 0;
}
@@ -6826,24 +7137,6 @@ out:
nlmsg_free(msg);
}
-static int nl80211_validate_ap_phy_operation(struct cfg80211_ap_settings *params)
-{
- struct ieee80211_channel *channel = params->chandef.chan;
-
- if ((params->he_cap || params->he_oper) &&
- (channel->flags & IEEE80211_CHAN_NO_HE))
- return -EOPNOTSUPP;
-
- if ((params->eht_cap || params->eht_oper) &&
- (channel->flags & IEEE80211_CHAN_NO_EHT))
- return -EOPNOTSUPP;
-
- if (params->uhr_oper && (channel->flags & IEEE80211_CHAN_NO_UHR))
- return -EOPNOTSUPP;
-
- return 0;
-}
-
static int
nl80211_parse_s1g_short_beacon(struct cfg80211_registered_device *rdev,
struct nlattr *attrs,
@@ -6879,6 +7172,28 @@ nl80211_parse_s1g_short_beacon(struct cfg80211_registered_device *rdev,
return 0;
}
+static int nl80211_check_npca(struct cfg80211_registered_device *rdev,
+ const struct cfg80211_chan_def *chandef,
+ enum nl80211_iftype iftype,
+ struct netlink_ext_ack *extack)
+{
+ const struct ieee80211_supported_band *sband;
+ const struct ieee80211_sta_uhr_cap *uhr_cap;
+
+ if (!chandef->npca_chan)
+ return 0;
+
+ sband = rdev->wiphy.bands[chandef->chan->band];
+ uhr_cap = ieee80211_get_uhr_iftype_cap(sband, iftype);
+
+ if (uhr_cap &&
+ (uhr_cap->mac.mac_cap[0] & IEEE80211_UHR_MAC_CAP0_NPCA_SUPP))
+ return 0;
+
+ NL_SET_ERR_MSG(extack, "NPCA not supported");
+ return -EINVAL;
+}
+
static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -6916,11 +7231,6 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
if (!params)
return -ENOMEM;
- err = nl80211_parse_beacon(rdev, info->attrs, &params->beacon,
- info->extack);
- if (err)
- goto out;
-
params->beacon_interval =
nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
params->dtim_period =
@@ -7023,7 +7333,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
err = nl80211_parse_chandef(rdev, info->extack, info->attrs,
- &params->chandef);
+ &params->chandef, true);
if (err)
goto out;
} else if (wdev->valid_links) {
@@ -7037,6 +7347,16 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
goto out;
}
+ err = nl80211_parse_beacon(rdev, info->attrs, &params->beacon,
+ params->chandef.chan, info->extack);
+ if (err)
+ goto out;
+
+ err = nl80211_check_npca(rdev, &params->chandef, wdev->iftype,
+ info->extack);
+ if (err)
+ goto out;
+
beacon_check.iftype = wdev->iftype;
beacon_check.relax = true;
beacon_check.reg_power =
@@ -7136,14 +7456,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
goto out;
}
- err = nl80211_calculate_ap_params(params);
- if (err)
- goto out;
-
- if (info->attrs[NL80211_ATTR_UHR_OPERATION])
- params->uhr_oper = nla_data(info->attrs[NL80211_ATTR_UHR_OPERATION]);
-
- err = nl80211_validate_ap_phy_operation(params);
+ err = nl80211_calculate_ap_capabilities(info, params);
if (err)
goto out;
@@ -7214,6 +7527,7 @@ static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info)
return -ENOMEM;
err = nl80211_parse_beacon(rdev, info->attrs, &params->beacon,
+ wdev->links[link_id].ap.chandef.chan,
info->extack);
if (err)
goto out;
@@ -7570,7 +7884,7 @@ static int nl80211_fill_link_station(struct sk_buff *msg,
goto nla_put_failure; \
} while (0)
- link_sinfoattr = nla_nest_start_noflag(msg, NL80211_ATTR_STA_INFO);
+ link_sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO);
if (!link_sinfoattr)
goto nla_put_failure;
@@ -7636,8 +7950,8 @@ static int nl80211_fill_link_station(struct sk_buff *msg,
PUT_LINK_SINFO(BEACON_LOSS, beacon_loss_count, u32);
if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_BSS_PARAM)) {
- bss_param = nla_nest_start_noflag(msg,
- NL80211_STA_INFO_BSS_PARAM);
+ bss_param = nla_nest_start(msg,
+ NL80211_STA_INFO_BSS_PARAM);
if (!bss_param)
goto nla_put_failure;
@@ -7679,8 +7993,7 @@ static int nl80211_fill_link_station(struct sk_buff *msg,
struct nlattr *tidsattr;
int tid;
- tidsattr = nla_nest_start_noflag(msg,
- NL80211_STA_INFO_TID_STATS);
+ tidsattr = nla_nest_start(msg, NL80211_STA_INFO_TID_STATS);
if (!tidsattr)
goto nla_put_failure;
@@ -7693,7 +8006,7 @@ static int nl80211_fill_link_station(struct sk_buff *msg,
if (!tidstats->filled)
continue;
- tidattr = nla_nest_start_noflag(msg, tid + 1);
+ tidattr = nla_nest_start(msg, tid + 1);
if (!tidattr)
goto nla_put_failure;
@@ -7729,36 +8042,15 @@ nla_put_failure:
return -EMSGSIZE;
}
-static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
- u32 seq, int flags,
- struct cfg80211_registered_device *rdev,
- struct wireless_dev *wdev,
- const u8 *mac_addr, struct station_info *sinfo,
- bool link_stats)
+static int nl80211_put_sta_info_common(struct sk_buff *msg,
+ struct cfg80211_registered_device *rdev,
+ struct station_info *sinfo)
{
- void *hdr;
struct nlattr *sinfoattr, *bss_param;
- struct link_station_info *link_sinfo;
- struct nlattr *links, *link;
- int link_id;
-
- hdr = nl80211hdr_put(msg, portid, seq, flags, cmd);
- if (!hdr) {
- cfg80211_sinfo_release_content(sinfo);
- return -1;
- }
-
- if ((wdev->netdev &&
- nla_put_u32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex)) ||
- nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
- NL80211_ATTR_PAD) ||
- nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr) ||
- nla_put_u32(msg, NL80211_ATTR_GENERATION, sinfo->generation))
- goto nla_put_failure;
- sinfoattr = nla_nest_start_noflag(msg, NL80211_ATTR_STA_INFO);
+ sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO);
if (!sinfoattr)
- goto nla_put_failure;
+ return -EMSGSIZE;
#define PUT_SINFO(attr, memb, type) do { \
BUILD_BUG_ON(sizeof(type) == sizeof(u64)); \
@@ -7849,8 +8141,7 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
PUT_SINFO_U64(T_OFFSET, t_offset);
if (sinfo->filled & BIT_ULL(NL80211_STA_INFO_BSS_PARAM)) {
- bss_param = nla_nest_start_noflag(msg,
- NL80211_STA_INFO_BSS_PARAM);
+ bss_param = nla_nest_start(msg, NL80211_STA_INFO_BSS_PARAM);
if (!bss_param)
goto nla_put_failure;
@@ -7892,8 +8183,7 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
struct nlattr *tidsattr;
int tid;
- tidsattr = nla_nest_start_noflag(msg,
- NL80211_STA_INFO_TID_STATS);
+ tidsattr = nla_nest_start(msg, NL80211_STA_INFO_TID_STATS);
if (!tidsattr)
goto nla_put_failure;
@@ -7906,7 +8196,7 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
if (!tidstats->filled)
continue;
- tidattr = nla_nest_start_noflag(msg, tid + 1);
+ tidattr = nla_nest_start(msg, tid + 1);
if (!tidattr)
goto nla_put_failure;
@@ -7936,6 +8226,37 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
}
nla_nest_end(msg, sinfoattr);
+ return 0;
+
+nla_put_failure:
+ nla_nest_cancel(msg, sinfoattr);
+ return -EMSGSIZE;
+}
+
+static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
+ u32 seq, int flags,
+ struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev,
+ const u8 *mac_addr, struct station_info *sinfo)
+{
+ void *hdr;
+
+ hdr = nl80211hdr_put(msg, portid, seq, flags, cmd);
+ if (!hdr) {
+ cfg80211_sinfo_release_content(sinfo);
+ return -1;
+ }
+
+ if ((wdev->netdev &&
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex)) ||
+ nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
+ NL80211_ATTR_PAD) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr) ||
+ nla_put_u32(msg, NL80211_ATTR_GENERATION, sinfo->generation))
+ goto nla_put_failure;
+
+ if (nl80211_put_sta_info_common(msg, rdev, sinfo))
+ goto nla_put_failure;
if (sinfo->assoc_req_ies_len &&
nla_put(msg, NL80211_ATTR_IE, sinfo->assoc_req_ies_len,
@@ -7958,45 +8279,11 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
goto nla_put_failure;
}
- if (link_stats && sinfo->valid_links) {
- links = nla_nest_start(msg, NL80211_ATTR_MLO_LINKS);
- if (!links)
- goto nla_put_failure;
-
- for_each_valid_link(sinfo, link_id) {
- link_sinfo = sinfo->links[link_id];
-
- if (WARN_ON_ONCE(!link_sinfo))
- continue;
-
- if (!is_valid_ether_addr(link_sinfo->addr))
- continue;
-
- link = nla_nest_start(msg, link_id + 1);
- if (!link)
- goto nla_put_failure;
-
- if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID,
- link_id))
- goto nla_put_failure;
-
- if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN,
- link_sinfo->addr))
- goto nla_put_failure;
-
- if (nl80211_fill_link_station(msg, rdev, link_sinfo))
- goto nla_put_failure;
-
- nla_nest_end(msg, link);
- }
- nla_nest_end(msg, links);
- }
-
cfg80211_sinfo_release_content(sinfo);
genlmsg_end(msg, hdr);
return 0;
- nla_put_failure:
+nla_put_failure:
cfg80211_sinfo_release_content(sinfo);
genlmsg_cancel(msg, hdr);
return -EMSGSIZE;
@@ -8190,82 +8477,261 @@ static void cfg80211_sta_set_mld_sinfo(struct station_info *sinfo)
sinfo->filled &= ~BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL_AVG);
}
+enum nl80211_dump_station_phase {
+ NL80211_DUMP_STA_PHASE_AGGREGATED = 0,
+ NL80211_DUMP_STA_PHASE_PER_LINK = 1,
+};
+
+struct nl80211_dump_station_ctx {
+ int sta_idx;
+ int link_idx;
+ enum nl80211_dump_station_phase phase;
+ bool dump_link_stats;
+ bool filter_mac;
+ u8 filter_mac_addr[ETH_ALEN];
+ u8 mac_addr[ETH_ALEN];
+ struct station_info sinfo;
+};
+
+static int nl80211_put_link_station_payload(struct sk_buff *msg,
+ struct cfg80211_registered_device *rdev,
+ struct station_info *sinfo,
+ int link_idx)
+{
+ struct link_station_info *link_sinfo = sinfo->links[link_idx];
+ struct nlattr *links, *link;
+
+ if (WARN_ON_ONCE(!link_sinfo))
+ return -ENOENT;
+
+ if (!is_valid_ether_addr(link_sinfo->addr))
+ return -EADDRNOTAVAIL;
+
+ links = nla_nest_start(msg, NL80211_ATTR_MLO_LINKS);
+ if (!links)
+ return -EMSGSIZE;
+
+ link = nla_nest_start(msg, link_idx + 1);
+ if (!link)
+ goto nla_put_failure;
+
+ if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_idx) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, link_sinfo->addr))
+ goto nla_put_failure;
+
+ if (nl80211_fill_link_station(msg, rdev, link_sinfo))
+ goto nla_put_failure;
+
+ nla_nest_end(msg, link);
+ nla_nest_end(msg, links);
+ return 0;
+
+nla_put_failure:
+ nla_nest_cancel(msg, links);
+ return -EMSGSIZE;
+}
+
static int nl80211_dump_station(struct sk_buff *skb,
struct netlink_callback *cb)
{
- struct station_info sinfo;
struct cfg80211_registered_device *rdev;
struct wireless_dev *wdev;
- u8 mac_addr[ETH_ALEN];
- int sta_idx = cb->args[2];
- bool sinfo_alloc = false;
- int err, i;
+ struct nl80211_dump_station_ctx *ctx = (void *)cb->args[2];
+ struct nlattr **attrbuf __free(kfree) = NULL;
+ int err;
- err = nl80211_prepare_wdev_dump(cb, &rdev, &wdev, NULL);
+ if (!ctx) {
+ attrbuf = kzalloc_objs(*attrbuf, NUM_NL80211_ATTR);
+ if (!attrbuf)
+ return -ENOMEM;
+ }
+
+ err = nl80211_prepare_wdev_dump(cb, &rdev, &wdev, attrbuf);
if (err)
return err;
/* nl80211_prepare_wdev_dump acquired it in the successful case */
__acquire(&rdev->wiphy.mtx);
+ if (!ctx) {
+ ctx = kzalloc_obj(*ctx);
+ if (!ctx) {
+ err = -ENOMEM;
+ goto out_err;
+ }
+ cb->args[2] = (long)ctx;
+ ctx->phase = NL80211_DUMP_STA_PHASE_AGGREGATED;
+ ctx->dump_link_stats =
+ !!attrbuf[NL80211_ATTR_STA_DUMP_LINK_STATS];
+ if (attrbuf[NL80211_ATTR_MAC]) {
+ const u8 *mac = nla_data(attrbuf[NL80211_ATTR_MAC]);
+
+ if (!is_valid_ether_addr(mac)) {
+ kfree(ctx);
+ cb->args[2] = 0;
+ err = -EINVAL;
+ goto out_err;
+ }
+ ctx->filter_mac = true;
+ memcpy(ctx->filter_mac_addr, mac, ETH_ALEN);
+ }
+ }
+
if (!wdev->netdev && wdev->iftype != NL80211_IFTYPE_NAN) {
err = -EINVAL;
goto out_err;
}
- if (!rdev->ops->dump_station) {
+ if (ctx->filter_mac) {
+ if (!rdev->ops->get_station) {
+ err = -EOPNOTSUPP;
+ goto out_err;
+ }
+ } else if (!rdev->ops->dump_station) {
err = -EOPNOTSUPP;
goto out_err;
}
- while (1) {
- memset(&sinfo, 0, sizeof(sinfo));
+ while (true) {
+ void *hdr;
+ int ret;
- for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) {
- sinfo.links[i] =
- kzalloc_obj(*sinfo.links[0]);
- if (!sinfo.links[i]) {
- err = -ENOMEM;
- goto out_err;
+ /* AGGREGATED phase: fetch sinfo from driver once per station */
+ if (ctx->phase == NL80211_DUMP_STA_PHASE_AGGREGATED) {
+ memset(&ctx->sinfo, 0, sizeof(ctx->sinfo));
+ for (int i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) {
+ ctx->sinfo.links[i] =
+ kzalloc_obj(*ctx->sinfo.links[0]);
+ if (!ctx->sinfo.links[i]) {
+ err = -ENOMEM;
+ goto out_err_release;
+ }
}
- sinfo_alloc = true;
- }
- err = rdev_dump_station(rdev, wdev, sta_idx,
- mac_addr, &sinfo);
- if (err == -ENOENT)
- break;
- if (err)
- goto out_err;
+ if (ctx->filter_mac) {
+ if (ctx->sta_idx > 0) {
+ err = skb->len;
+ goto out_err_release;
+ }
+ err = rdev_get_station(rdev, wdev,
+ ctx->filter_mac_addr,
+ &ctx->sinfo);
+ if (!err)
+ memcpy(ctx->mac_addr,
+ ctx->filter_mac_addr, ETH_ALEN);
+ } else {
+ err = rdev_dump_station(rdev, wdev, ctx->sta_idx,
+ ctx->mac_addr,
+ &ctx->sinfo);
+ }
+ if (err == -ENOENT) {
+ err = skb->len;
+ goto out_err_release;
+ }
+ if (err)
+ goto out_err_release;
- if (sinfo.valid_links)
- cfg80211_sta_set_mld_sinfo(&sinfo);
+ if (ctx->sinfo.valid_links)
+ cfg80211_sta_set_mld_sinfo(&ctx->sinfo);
+ } else {
+ /* PER_LINK phase: advance to next valid link */
+ while (ctx->link_idx < IEEE80211_MLD_MAX_NUM_LINKS &&
+ !(ctx->sinfo.valid_links & BIT(ctx->link_idx)))
+ ctx->link_idx++;
+
+ if (ctx->link_idx >= IEEE80211_MLD_MAX_NUM_LINKS) {
+ cfg80211_sinfo_release_content(&ctx->sinfo);
+ ctx->sta_idx++;
+ ctx->phase = NL80211_DUMP_STA_PHASE_AGGREGATED;
+ continue;
+ }
+ }
- /* reset the sinfo_alloc flag as nl80211_send_station()
- * always releases sinfo
- */
- sinfo_alloc = false;
+ /* Build common header for both phases */
+ hdr = nl80211hdr_put(skb, NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq, NLM_F_MULTI,
+ NL80211_CMD_NEW_STATION);
+ if (!hdr) {
+ err = skb->len;
+ if (ctx->phase == NL80211_DUMP_STA_PHASE_PER_LINK)
+ goto out_err;
+ goto out_err_release;
+ }
- if (nl80211_send_station(skb, NL80211_CMD_NEW_STATION,
- NETLINK_CB(cb->skb).portid,
- cb->nlh->nlmsg_seq, NLM_F_MULTI,
- rdev, wdev, mac_addr,
- &sinfo, false) < 0)
- goto out;
+ if ((wdev->netdev &&
+ nla_put_u32(skb, NL80211_ATTR_IFINDEX,
+ wdev->netdev->ifindex)) ||
+ nla_put_u64_64bit(skb, NL80211_ATTR_WDEV,
+ wdev_id(wdev), NL80211_ATTR_PAD) ||
+ nla_put(skb, NL80211_ATTR_MAC, ETH_ALEN, ctx->mac_addr) ||
+ nla_put_u32(skb, NL80211_ATTR_GENERATION,
+ ctx->sinfo.generation)) {
+ genlmsg_cancel(skb, hdr);
+ err = skb->len;
+ if (ctx->phase == NL80211_DUMP_STA_PHASE_PER_LINK)
+ goto out_err;
+ goto out_err_release;
+ }
- sta_idx++;
+ switch (ctx->phase) {
+ case NL80211_DUMP_STA_PHASE_AGGREGATED:
+ ret = nl80211_put_sta_info_common(skb, rdev, &ctx->sinfo);
+ if (ret) {
+ genlmsg_cancel(skb, hdr);
+ err = ret;
+ goto out_err_release;
+ }
+ genlmsg_end(skb, hdr);
+
+ if (ctx->dump_link_stats && ctx->sinfo.valid_links) {
+ ctx->phase = NL80211_DUMP_STA_PHASE_PER_LINK;
+ ctx->link_idx = 0;
+ } else {
+ cfg80211_sinfo_release_content(&ctx->sinfo);
+ ctx->sta_idx++;
+ }
+ break;
+
+ case NL80211_DUMP_STA_PHASE_PER_LINK:
+ ret = nl80211_put_link_station_payload(skb, rdev,
+ &ctx->sinfo,
+ ctx->link_idx);
+ if (ret == -EMSGSIZE) {
+ genlmsg_cancel(skb, hdr);
+ err = skb->len;
+ goto out_err;
+ }
+ if (ret) {
+ /* skip invalid link, do not abort the dump */
+ genlmsg_cancel(skb, hdr);
+ ctx->link_idx++;
+ continue;
+ }
+ genlmsg_end(skb, hdr);
+ ctx->link_idx++;
+ break;
+ }
}
- out:
- cb->args[2] = sta_idx;
- err = skb->len;
- out_err:
- if (sinfo_alloc)
- cfg80211_sinfo_release_content(&sinfo);
+out_err_release:
+ cfg80211_sinfo_release_content(&ctx->sinfo);
+ memset(&ctx->sinfo, 0, sizeof(ctx->sinfo));
+out_err:
wiphy_unlock(&rdev->wiphy);
return err;
}
+static int nl80211_dump_station_done(struct netlink_callback *cb)
+{
+ struct nl80211_dump_station_ctx *ctx = (void *)cb->args[2];
+
+ if (ctx) {
+ cfg80211_sinfo_release_content(&ctx->sinfo);
+ kfree(ctx);
+ }
+ return 0;
+}
+
static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -8313,7 +8779,7 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
if (nl80211_send_station(msg, NL80211_CMD_NEW_STATION,
info->snd_portid, info->snd_seq, 0,
- rdev, wdev, mac_addr, &sinfo, false) < 0) {
+ rdev, wdev, mac_addr, &sinfo) < 0) {
nlmsg_free(msg);
return -ENOBUFS;
}
@@ -10805,7 +11271,8 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
wiphy = &rdev->wiphy;
- if (wdev->iftype == NL80211_IFTYPE_NAN)
+ if (wdev->iftype == NL80211_IFTYPE_NAN ||
+ wdev->iftype == NL80211_IFTYPE_PD)
return -EOPNOTSUPP;
if (!rdev->ops->scan)
@@ -11593,7 +12060,8 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
if (dfs_region == NL80211_DFS_UNSET)
return -EINVAL;
- err = nl80211_parse_chandef(rdev, info->extack, info->attrs, &chandef);
+ err = nl80211_parse_chandef(rdev, info->extack, info->attrs, &chandef,
+ false);
if (err)
return err;
@@ -11682,7 +12150,8 @@ static int nl80211_notify_radar_detection(struct sk_buff *skb,
return -EINVAL;
}
- err = nl80211_parse_chandef(rdev, info->extack, info->attrs, &chandef);
+ err = nl80211_parse_chandef(rdev, info->extack, info->attrs, &chandef,
+ false);
if (err) {
GENL_SET_ERR_MSG(info, "Unable to extract chandef info");
return err;
@@ -11765,6 +12234,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
int err;
bool need_new_beacon = false;
bool need_handle_dfs_flag = true;
+ bool permit_npca = false;
u32 cs_count;
if (!rdev->ops->channel_switch ||
@@ -11782,6 +12252,8 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
*/
need_handle_dfs_flag = false;
+ permit_npca = true;
+
/* useless if AP is not running */
if (!wdev->links[link_id].ap.beacon_interval)
return -ENOTCONN;
@@ -11818,11 +12290,21 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
params.count = cs_count;
+ err = nl80211_parse_chandef(rdev, info->extack, info->attrs,
+ &params.chandef, permit_npca);
+ if (err)
+ goto free;
+
+ err = nl80211_check_npca(rdev, &params.chandef, wdev->iftype,
+ info->extack);
+ if (err)
+ goto free;
+
if (!need_new_beacon)
goto skip_beacons;
err = nl80211_parse_beacon(rdev, info->attrs, &params.beacon_after,
- info->extack);
+ params.chandef.chan, info->extack);
if (err)
goto free;
@@ -11839,6 +12321,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
goto free;
err = nl80211_parse_beacon(rdev, csa_attrs, &params.beacon_csa,
+ wdev->links[link_id].ap.chandef.chan,
info->extack);
if (err)
goto free;
@@ -11867,11 +12350,6 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
goto free;
skip_beacons:
- err = nl80211_parse_chandef(rdev, info->extack, info->attrs,
- &params.chandef);
- if (err)
- goto free;
-
if (!cfg80211_reg_can_beacon_relax(&rdev->wiphy, &params.chandef,
wdev->iftype)) {
err = -EINVAL;
@@ -12424,9 +12902,11 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
return -EINVAL;
}
- req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
- IEEE80211_BSS_TYPE_ESS,
- IEEE80211_PRIVACY_ANY);
+ req.bss = __cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
+ IEEE80211_BSS_TYPE_ESS,
+ IEEE80211_PRIVACY_ANY,
+ NL80211_BSS_USE_FOR_NORMAL,
+ info->extack);
if (!req.bss)
return -ENOENT;
@@ -12571,6 +13051,7 @@ static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
}
static struct cfg80211_bss *nl80211_assoc_bss(struct cfg80211_registered_device *rdev,
+ struct genl_info *info,
const u8 *ssid, int ssid_len,
struct nlattr **attrs,
int assoc_link_id, int link_id)
@@ -12580,8 +13061,10 @@ static struct cfg80211_bss *nl80211_assoc_bss(struct cfg80211_registered_device
const u8 *bssid;
u32 freq, use_for = 0;
- if (!attrs[NL80211_ATTR_MAC] || !attrs[NL80211_ATTR_WIPHY_FREQ])
+ if (!attrs[NL80211_ATTR_MAC] || !attrs[NL80211_ATTR_WIPHY_FREQ]) {
+ GENL_SET_ERR_MSG(info, "BSSID or frequency missing");
return ERR_PTR(-EINVAL);
+ }
bssid = nla_data(attrs[NL80211_ATTR_MAC]);
@@ -12590,8 +13073,10 @@ static struct cfg80211_bss *nl80211_assoc_bss(struct cfg80211_registered_device
freq += nla_get_u32(attrs[NL80211_ATTR_WIPHY_FREQ_OFFSET]);
chan = nl80211_get_valid_chan(&rdev->wiphy, freq);
- if (!chan)
+ if (!chan) {
+ GENL_SET_ERR_MSG(info, "invalid or disabled channel");
return ERR_PTR(-EINVAL);
+ }
if (assoc_link_id >= 0)
use_for = NL80211_BSS_USE_FOR_MLD_LINK;
@@ -12602,7 +13087,7 @@ static struct cfg80211_bss *nl80211_assoc_bss(struct cfg80211_registered_device
ssid, ssid_len,
IEEE80211_BSS_TYPE_ESS,
IEEE80211_PRIVACY_ANY,
- use_for);
+ use_for, info->extack);
if (!bss)
return ERR_PTR(-ENOENT);
@@ -12641,13 +13126,13 @@ static int nl80211_process_links(struct cfg80211_registered_device *rdev,
return -EINVAL;
}
links[link_id].bss =
- nl80211_assoc_bss(rdev, ssid, ssid_len, attrs,
+ nl80211_assoc_bss(rdev, info, ssid, ssid_len, attrs,
assoc_link_id, link_id);
if (IS_ERR(links[link_id].bss)) {
err = PTR_ERR(links[link_id].bss);
links[link_id].bss = NULL;
- NL_SET_ERR_MSG_ATTR(info->extack, link,
- "Error fetching BSS for link");
+ /* the BSS lookup set the specific message already */
+ NL_SET_BAD_ATTR(info->extack, link);
return err;
}
@@ -12856,20 +13341,20 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
goto free;
}
- if (info->attrs[NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS])
+ if (info->attrs[NL80211_ATTR_EXT_MLD_CAPA_AND_OPS])
req.ext_mld_capa_ops =
- nla_get_u16(info->attrs[NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS]);
+ nla_get_u16(info->attrs[NL80211_ATTR_EXT_MLD_CAPA_AND_OPS]);
} else {
if (req.link_id >= 0)
return -EINVAL;
- req.bss = nl80211_assoc_bss(rdev, ssid, ssid_len, info->attrs,
+ req.bss = nl80211_assoc_bss(rdev, info, ssid, ssid_len, info->attrs,
-1, -1);
if (IS_ERR(req.bss))
return PTR_ERR(req.bss);
ap_addr = req.bss->bssid;
- if (info->attrs[NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS])
+ if (info->attrs[NL80211_ATTR_EXT_MLD_CAPA_AND_OPS])
return -EINVAL;
}
@@ -13092,7 +13577,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
}
err = nl80211_parse_chandef(rdev, info->extack, info->attrs,
- &ibss.chandef);
+ &ibss.chandef, false);
if (err)
return err;
@@ -13101,8 +13586,6 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
return -EINVAL;
switch (ibss.chandef.width) {
- case NL80211_CHAN_WIDTH_5:
- case NL80211_CHAN_WIDTH_10:
case NL80211_CHAN_WIDTH_20_NOHT:
break;
case NL80211_CHAN_WIDTH_20:
@@ -13163,7 +13646,8 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) {
bool no_ht = false;
- connkeys = nl80211_parse_connkeys(rdev, info, &no_ht);
+ connkeys = nl80211_parse_connkeys(rdev, dev->ieee80211_ptr,
+ info, &no_ht);
if (IS_ERR(connkeys))
return PTR_ERR(connkeys);
@@ -13420,6 +13904,16 @@ static int nl80211_testmode_dump(struct sk_buff *skb,
err = -ENOENT;
goto out_err;
}
+
+ /*
+ * The wiphy may have moved netns between dumpit
+ * invocations (via NL80211_CMD_SET_WIPHY_NETNS), so
+ * re-check that it still matches the caller's netns.
+ */
+ if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk))) {
+ err = -ENODEV;
+ goto out_err;
+ }
} else {
attrbuf = kzalloc_objs(*attrbuf, NUM_NL80211_ATTR);
if (!attrbuf) {
@@ -13605,7 +14099,8 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
}
if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) {
- connkeys = nl80211_parse_connkeys(rdev, info, NULL);
+ connkeys = nl80211_parse_connkeys(rdev, dev->ieee80211_ptr,
+ info, NULL);
if (IS_ERR(connkeys))
return PTR_ERR(connkeys);
}
@@ -13867,6 +14362,19 @@ static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info)
if (IS_ERR(net))
return PTR_ERR(net);
+ /*
+ * The caller already has CAP_NET_ADMIN over the source netns
+ * (enforced by GENL_UNS_ADMIN_PERM on the genl op). Mirror the
+ * convention used by net/core/rtnetlink.c::rtnl_get_net_ns_capable()
+ * and require CAP_NET_ADMIN over the target netns as well, so that
+ * a caller that is privileged in their own user namespace cannot
+ * push a wiphy into a netns where they have no privilege.
+ */
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) {
+ put_net(net);
+ return -EPERM;
+ }
+
err = 0;
/* check if anything to do */
@@ -14066,6 +14574,7 @@ static int nl80211_remain_on_channel(struct sk_buff *skb,
unsigned int link_id = nl80211_link_id(info->attrs);
struct wireless_dev *wdev = info->user_ptr[1];
struct cfg80211_chan_def chandef;
+ const u8 *rx_addr = NULL;
struct sk_buff *msg;
void *hdr;
u64 cookie;
@@ -14078,6 +14587,14 @@ static int nl80211_remain_on_channel(struct sk_buff *skb,
duration = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
+ if (info->attrs[NL80211_ATTR_MAC])
+ rx_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+ if (rx_addr &&
+ !wiphy_ext_feature_isset(wdev->wiphy,
+ NL80211_EXT_FEATURE_ROC_ADDR_FILTER))
+ return -EOPNOTSUPP;
+
if (!rdev->ops->remain_on_channel ||
!(rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL))
return -EOPNOTSUPP;
@@ -14090,7 +14607,8 @@ static int nl80211_remain_on_channel(struct sk_buff *skb,
duration > rdev->wiphy.max_remain_on_channel_duration)
return -EINVAL;
- err = nl80211_parse_chandef(rdev, info->extack, info->attrs, &chandef);
+ err = nl80211_parse_chandef(rdev, info->extack, info->attrs, &chandef,
+ false);
if (err)
return err;
@@ -14124,8 +14642,9 @@ static int nl80211_remain_on_channel(struct sk_buff *skb,
goto free_msg;
}
+ cookie = cfg80211_assign_cookie(rdev);
err = rdev_remain_on_channel(rdev, wdev, chandef.chan,
- duration, &cookie);
+ duration, cookie, rx_addr);
if (err)
goto free_msg;
@@ -14214,6 +14733,11 @@ static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info)
WIPHY_NAN_FLAGS_USERSPACE_DE))
return -EOPNOTSUPP;
break;
+ case NL80211_IFTYPE_PD:
+ if (!wiphy_ext_feature_isset(wdev->wiphy,
+ NL80211_EXT_FEATURE_SECURE_RTT))
+ return -EOPNOTSUPP;
+ break;
default:
return -EOPNOTSUPP;
}
@@ -14278,6 +14802,11 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
WIPHY_NAN_FLAGS_USERSPACE_DE))
return -EOPNOTSUPP;
break;
+ case NL80211_IFTYPE_PD:
+ if (!wiphy_ext_feature_isset(wdev->wiphy,
+ NL80211_EXT_FEATURE_SECURE_RTT))
+ return -EOPNOTSUPP;
+ break;
default:
return -EOPNOTSUPP;
}
@@ -14309,7 +14838,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
chandef.chan = NULL;
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
err = nl80211_parse_chandef(rdev, info->extack, info->attrs,
- &chandef);
+ &chandef, false);
if (err)
return err;
}
@@ -14355,7 +14884,8 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
}
params.chan = chandef.chan;
- err = cfg80211_mlme_mgmt_tx(rdev, wdev, &params, &cookie);
+ cookie = cfg80211_assign_cookie(rdev);
+ err = cfg80211_mlme_mgmt_tx(rdev, wdev, &params, cookie);
if (err)
goto free_msg;
@@ -14403,6 +14933,11 @@ static int nl80211_tx_mgmt_cancel_wait(struct sk_buff *skb, struct genl_info *in
NL80211_EXT_FEATURE_SECURE_NAN))
return -EOPNOTSUPP;
break;
+ case NL80211_IFTYPE_PD:
+ if (!wiphy_ext_feature_isset(wdev->wiphy,
+ NL80211_EXT_FEATURE_SECURE_RTT))
+ return -EOPNOTSUPP;
+ break;
default:
return -EOPNOTSUPP;
}
@@ -14712,7 +15247,7 @@ static int nl80211_join_ocb(struct sk_buff *skb, struct genl_info *info)
int err;
err = nl80211_parse_chandef(rdev, info->extack, info->attrs,
- &setup.chandef);
+ &setup.chandef, false);
if (err)
return err;
@@ -14788,7 +15323,7 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
err = nl80211_parse_chandef(rdev, info->extack, info->attrs,
- &setup.chandef);
+ &setup.chandef, false);
if (err)
return err;
} else {
@@ -15790,26 +16325,41 @@ static int nl80211_register_unexpected_frame(struct sk_buff *skb,
return 0;
}
-static int nl80211_probe_client(struct sk_buff *skb,
- struct genl_info *info)
+static int nl80211_probe_peer(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct net_device *dev = info->user_ptr[1];
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct sk_buff *msg;
void *hdr;
- const u8 *addr;
+ const u8 *addr = NULL;
u64 cookie;
int err;
- if (wdev->iftype != NL80211_IFTYPE_AP &&
- wdev->iftype != NL80211_IFTYPE_P2P_GO)
+ /* Allow in AP, STA, and their P2P counterparts */
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ if (!info->attrs[NL80211_ATTR_MAC])
+ return -EINVAL;
+ addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+ break;
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ if (!wiphy_ext_feature_isset(&rdev->wiphy,
+ NL80211_EXT_FEATURE_PROBE_AP))
+ return -EOPNOTSUPP;
+ if (!wdev->connected)
+ return -ENOLINK;
+ /* STA/P2P-client probes the currently associated AP/GO. */
+ if (info->attrs[NL80211_ATTR_MAC])
+ return -EINVAL;
+ break;
+ default:
return -EOPNOTSUPP;
+ }
- if (!info->attrs[NL80211_ATTR_MAC])
- return -EINVAL;
-
- if (!rdev->ops->probe_client)
+ if (!rdev->ops->probe_peer)
return -EOPNOTSUPP;
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
@@ -15817,15 +16367,14 @@ static int nl80211_probe_client(struct sk_buff *skb,
return -ENOMEM;
hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
- NL80211_CMD_PROBE_CLIENT);
+ NL80211_CMD_PROBE_PEER);
if (!hdr) {
err = -ENOBUFS;
goto free_msg;
}
- addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
-
- err = rdev_probe_client(rdev, dev, addr, &cookie);
+ cookie = cfg80211_assign_cookie(rdev);
+ err = rdev_probe_peer(rdev, dev, addr, cookie);
if (err)
goto free_msg;
@@ -16539,6 +17088,46 @@ static int nl80211_nan_change_config(struct sk_buff *skb,
return rdev_nan_change_conf(rdev, wdev, &conf, changed);
}
+static int nl80211_start_pd(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct wireless_dev *wdev = info->user_ptr[1];
+ int err;
+
+ if (wdev->iftype != NL80211_IFTYPE_PD)
+ return -EOPNOTSUPP;
+
+ if (wdev_running(wdev))
+ return -EEXIST;
+
+ if (rfkill_blocked(rdev->wiphy.rfkill))
+ return -ERFKILL;
+
+ if (!rdev->ops->start_pd)
+ return -EOPNOTSUPP;
+
+ err = rdev_start_pd(rdev, wdev);
+ if (err)
+ return err;
+ wdev->is_running = true;
+ rdev->opencount++;
+
+ return 0;
+}
+
+static int nl80211_stop_pd(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct wireless_dev *wdev = info->user_ptr[1];
+
+ if (wdev->iftype != NL80211_IFTYPE_PD)
+ return -EOPNOTSUPP;
+
+ cfg80211_stop_pd(rdev, wdev);
+
+ return 0;
+}
+
void cfg80211_nan_match(struct wireless_dev *wdev,
struct cfg80211_nan_match_params *match, gfp_t gfp)
{
@@ -16752,7 +17341,7 @@ static int nl80211_parse_nan_channel(struct cfg80211_registered_device *rdev,
return ret;
ret = nl80211_parse_chandef(rdev, info->extack, channel_parsed,
- &chandef);
+ &chandef, false);
if (ret)
return ret;
@@ -17382,6 +17971,15 @@ static int nl80211_prepare_vendor_dump(struct sk_buff *skb,
if (!wiphy)
return -ENODEV;
+
+ /*
+ * The wiphy may have moved netns between dumpit
+ * invocations (via NL80211_CMD_SET_WIPHY_NETNS), so
+ * re-check that it still matches the caller's netns.
+ */
+ if (!net_eq(wiphy_net(wiphy), sock_net(skb->sk)))
+ return -ENODEV;
+
*rdev = wiphy_to_rdev(wiphy);
*wdev = NULL;
@@ -17751,7 +18349,8 @@ static int nl80211_tdls_channel_switch(struct sk_buff *skb,
!info->attrs[NL80211_ATTR_OPER_CLASS])
return -EINVAL;
- err = nl80211_parse_chandef(rdev, info->extack, info->attrs, &chandef);
+ err = nl80211_parse_chandef(rdev, info->extack, info->attrs, &chandef,
+ false);
if (err)
return err;
@@ -17990,10 +18589,11 @@ static int nl80211_tx_control_port(struct sk_buff *skb, struct genl_info *info)
link_id = nl80211_link_id_or_invalid(info->attrs);
+ cookie = dont_wait_for_ack ? 0 : cfg80211_assign_cookie(rdev);
err = rdev_tx_control_port(rdev, dev, buf, len,
dest, cpu_to_be16(proto), noencrypt, link_id,
- dont_wait_for_ack ? NULL : &cookie);
- if (!err && !dont_wait_for_ack)
+ cookie);
+ if (!err && cookie)
nl_set_extack_cookie_u64(info->extack, cookie);
return err;
}
@@ -18324,15 +18924,20 @@ static int nl80211_color_change(struct sk_buff *skb, struct genl_info *info)
params.count = nla_get_u8(info->attrs[NL80211_ATTR_COLOR_CHANGE_COUNT]);
params.color = nla_get_u8(info->attrs[NL80211_ATTR_COLOR_CHANGE_COLOR]);
- err = nl80211_parse_beacon(rdev, info->attrs, &params.beacon_next,
- info->extack);
- if (err)
- return err;
+ params.link_id = nl80211_link_id(info->attrs);
+ if (!wdev->links[params.link_id].ap.beacon_interval)
+ return -EINVAL;
tb = kzalloc_objs(*tb, NL80211_ATTR_MAX + 1);
if (!tb)
return -ENOMEM;
+ err = nl80211_parse_beacon(rdev, info->attrs, &params.beacon_next,
+ wdev->links[params.link_id].ap.chandef.chan,
+ info->extack);
+ if (err)
+ goto out;
+
err = nla_parse_nested(tb, NL80211_ATTR_MAX,
info->attrs[NL80211_ATTR_COLOR_CHANGE_ELEMS],
nl80211_policy, info->extack);
@@ -18340,6 +18945,7 @@ static int nl80211_color_change(struct sk_buff *skb, struct genl_info *info)
goto out;
err = nl80211_parse_beacon(rdev, tb, &params.beacon_color_change,
+ wdev->links[params.link_id].ap.chandef.chan,
info->extack);
if (err)
goto out;
@@ -18397,7 +19003,6 @@ static int nl80211_color_change(struct sk_buff *skb, struct genl_info *info)
goto out;
}
- params.link_id = nl80211_link_id(info->attrs);
err = rdev_color_change(rdev, dev, &params);
out:
@@ -18726,9 +19331,9 @@ static int nl80211_assoc_ml_reconf(struct sk_buff *skb, struct genl_info *info)
goto out;
}
- if (info->attrs[NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS])
+ if (info->attrs[NL80211_ATTR_EXT_MLD_CAPA_AND_OPS])
req.ext_mld_capa_ops =
- nla_get_u16(info->attrs[NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS]);
+ nla_get_u16(info->attrs[NL80211_ATTR_EXT_MLD_CAPA_AND_OPS]);
err = cfg80211_assoc_ml_reconf(rdev, dev, &req);
@@ -19115,6 +19720,14 @@ static const struct genl_ops nl80211_ops[] = {
/* can be retrieved by unprivileged users */
.internal_flags = IFLAGS(NL80211_FLAG_NEED_WIPHY),
},
+ {
+ .cmd = NL80211_CMD_GET_STATION,
+ .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+ .doit = nl80211_get_station,
+ .dumpit = nl80211_dump_station,
+ .done = nl80211_dump_station_done,
+ .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV),
+ },
};
static const struct genl_small_ops nl80211_small_ops[] = {
@@ -19215,13 +19828,6 @@ static const struct genl_small_ops nl80211_small_ops[] = {
NL80211_FLAG_MLO_VALID_LINK_ID),
},
{
- .cmd = NL80211_CMD_GET_STATION,
- .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
- .doit = nl80211_get_station,
- .dumpit = nl80211_dump_station,
- .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV),
- },
- {
.cmd = NL80211_CMD_SET_STATION,
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
.doit = nl80211_set_station,
@@ -19627,9 +20233,9 @@ static const struct genl_small_ops nl80211_small_ops[] = {
.internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV),
},
{
- .cmd = NL80211_CMD_PROBE_CLIENT,
+ .cmd = NL80211_CMD_PROBE_PEER,
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
- .doit = nl80211_probe_client,
+ .doit = nl80211_probe_peer,
.flags = GENL_UNS_ADMIN_PERM,
.internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP),
},
@@ -19702,6 +20308,20 @@ static const struct genl_small_ops nl80211_small_ops[] = {
.internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP),
},
{
+ .cmd = NL80211_CMD_START_PD,
+ .doit = nl80211_start_pd,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV |
+ NL80211_FLAG_NEED_RTNL),
+ },
+ {
+ .cmd = NL80211_CMD_STOP_PD,
+ .doit = nl80211_stop_pd,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP |
+ NL80211_FLAG_NEED_RTNL),
+ },
+ {
.cmd = NL80211_CMD_SET_MCAST_RATE,
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
.doit = nl80211_set_mcast_rate,
@@ -19828,6 +20448,7 @@ static const struct genl_small_ops nl80211_small_ops[] = {
.cmd = NL80211_CMD_SET_PMK,
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
.doit = nl80211_set_pmk,
+ .flags = GENL_UNS_ADMIN_PERM,
.internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_CLEAR_SKB),
},
@@ -19835,6 +20456,7 @@ static const struct genl_small_ops nl80211_small_ops[] = {
.cmd = NL80211_CMD_DEL_PMK,
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
.doit = nl80211_del_pmk,
+ .flags = GENL_UNS_ADMIN_PERM,
.internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP),
},
{
@@ -20459,6 +21081,9 @@ void cfg80211_rx_unprot_mlme_mgmt(struct net_device *dev, const u8 *buf,
} else if (ieee80211_is_disassoc(mgmt->frame_control)) {
event.cmd = NL80211_CMD_UNPROT_DISASSOCIATE;
} else if (ieee80211_is_beacon(mgmt->frame_control)) {
+ if (wdev->iftype == NL80211_IFTYPE_AP ||
+ wdev->iftype == NL80211_IFTYPE_P2P_GO)
+ return;
if (wdev->unprot_beacon_reported &&
elapsed_jiffies_msecs(wdev->unprot_beacon_reported) < 10000)
return;
@@ -20588,7 +21213,9 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
(cr->fils.pmk &&
nla_put(msg, NL80211_ATTR_PMK, cr->fils.pmk_len, cr->fils.pmk)) ||
(cr->fils.pmkid &&
- nla_put(msg, NL80211_ATTR_PMKID, WLAN_PMKID_LEN, cr->fils.pmkid)))))
+ nla_put(msg, NL80211_ATTR_PMKID, WLAN_PMKID_LEN, cr->fils.pmkid)))) ||
+ (cr->assoc_encrypted &&
+ nla_put_flag(msg, NL80211_ATTR_ASSOC_ENCRYPTED)))
goto nla_put_failure;
if (cr->valid_links) {
@@ -20931,7 +21558,7 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
}
void cfg80211_notify_new_peer_candidate(struct net_device *dev, const u8 *addr,
- const u8 *ie, u8 ie_len,
+ const u8 *ie, size_t ie_len,
int sig_dbm, gfp_t gfp)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
@@ -21202,7 +21829,7 @@ void cfg80211_new_sta(struct wireless_dev *wdev, const u8 *mac_addr,
return;
if (nl80211_send_station(msg, NL80211_CMD_NEW_STATION, 0, 0, 0,
- rdev, wdev, mac_addr, sinfo, false) < 0) {
+ rdev, wdev, mac_addr, sinfo) < 0) {
nlmsg_free(msg);
return;
}
@@ -21232,7 +21859,7 @@ void cfg80211_del_sta_sinfo(struct wireless_dev *wdev, const u8 *mac_addr,
}
if (nl80211_send_station(msg, NL80211_CMD_DEL_STATION, 0, 0, 0,
- rdev, wdev, mac_addr, sinfo, false) < 0) {
+ rdev, wdev, mac_addr, sinfo) < 0) {
nlmsg_free(msg);
return;
}
@@ -21419,6 +22046,10 @@ static void nl80211_frame_tx_status(struct wireless_dev *wdev,
struct sk_buff *msg;
void *hdr;
+ /* userspace not interested in zero-cookie status */
+ if (!status->cookie)
+ return;
+
if (command == NL80211_CMD_FRAME_TX_STATUS)
trace_cfg80211_mgmt_tx_status(wdev, status->cookie,
status->ack);
@@ -22165,8 +22796,8 @@ nla_put_failure:
}
EXPORT_SYMBOL(cfg80211_sta_opmode_change_notify);
-void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
- u64 cookie, bool acked, s32 ack_signal,
+void cfg80211_probe_status(struct net_device *dev, const u8 *peer, u64 cookie,
+ int link_id, bool acked, s32 ack_signal,
bool is_valid_ack_signal, gfp_t gfp)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
@@ -22174,14 +22805,14 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
struct sk_buff *msg;
void *hdr;
- trace_cfg80211_probe_status(dev, addr, cookie, acked);
+ trace_cfg80211_probe_status(dev, peer, cookie, acked);
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
if (!msg)
return;
- hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PROBE_CLIENT);
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PROBE_PEER);
if (!hdr) {
nlmsg_free(msg);
return;
@@ -22189,12 +22820,18 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
- nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+ (peer && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer)) ||
nla_put_u64_64bit(msg, NL80211_ATTR_COOKIE, cookie,
- NL80211_ATTR_PAD) ||
- (acked && nla_put_flag(msg, NL80211_ATTR_ACK)) ||
- (is_valid_ack_signal && nla_put_s32(msg, NL80211_ATTR_ACK_SIGNAL,
- ack_signal)))
+ NL80211_ATTR_PAD))
+ goto nla_put_failure;
+
+ if (link_id >= 0 &&
+ nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id))
+ goto nla_put_failure;
+
+ if ((acked && nla_put_flag(msg, NL80211_ATTR_ACK)) ||
+ (is_valid_ack_signal &&
+ nla_put_s32(msg, NL80211_ATTR_ACK_SIGNAL, ack_signal)))
goto nla_put_failure;
genlmsg_end(msg, hdr);
@@ -22509,7 +23146,8 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
wdev->nl_owner_dead = true;
schedule_work(&rdev->destroy_work);
} else if (wdev->conn_owner_nlportid == notify->portid) {
- schedule_work(&wdev->disconnect_wk);
+ wiphy_work_queue(wdev->wiphy,
+ &wdev->disconnect_wk);
}
cfg80211_release_pmsr(wdev, notify->portid);
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
index 048ba92c3e42..bdb065d14054 100644
--- a/net/wireless/nl80211.h
+++ b/net/wireless/nl80211.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Portions of this file
- * Copyright (C) 2018, 2020-2025 Intel Corporation
+ * Copyright (C) 2018, 2020-2026 Intel Corporation
*/
#ifndef __NET_WIRELESS_NL80211_H
#define __NET_WIRELESS_NL80211_H
@@ -25,7 +25,8 @@ static inline u64 wdev_id(struct wireless_dev *wdev)
int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
struct netlink_ext_ack *extack,
struct nlattr **attrs,
- struct cfg80211_chan_def *chandef);
+ struct cfg80211_chan_def *chandef,
+ bool npca_permitted);
int nl80211_parse_random_mac(struct nlattr **attrs,
u8 *mac_addr, u8 *mac_addr_mask);
diff --git a/net/wireless/pmsr.c b/net/wireless/pmsr.c
index 4c8ea0583f94..97449bcb9a22 100644
--- a/net/wireless/pmsr.c
+++ b/net/wireless/pmsr.c
@@ -17,11 +17,19 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev,
u32 preamble = NL80211_PREAMBLE_DMG; /* only optional in DMG */
/* validate existing data */
- if (!(rdev->wiphy.pmsr_capa->ftm.bandwidths & BIT(out->chandef.width))) {
+ 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);
@@ -44,13 +52,22 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev,
}
}
- if (!(capa->ftm.preambles & BIT(preamble))) {
+ 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;
@@ -88,14 +105,13 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev,
out->ftm.ftms_per_burst = 0;
if (tb[NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST])
out->ftm.ftms_per_burst =
- nla_get_u32(tb[NL80211_PMSR_FTM_REQ_ATTR_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 ||
- out->ftm.ftms_per_burst == 0)) {
+ 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 but non-zero");
+ "FTM: FTMs per burst must be set lower than the device limit");
return -EINVAL;
}
@@ -109,6 +125,7 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev,
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 =
@@ -117,6 +134,7 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev,
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 =
@@ -128,6 +146,14 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev,
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) {
@@ -143,6 +169,14 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev,
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,
@@ -188,20 +222,87 @@ static int pmsr_parse_ftm(struct cfg80211_registered_device *rdev,
}
out->ftm.rsta = !!tb[NL80211_PMSR_FTM_REQ_ATTR_RSTA];
- if (out->ftm.rsta && !capa->ftm.support_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: RSTA not supported by device");
+ "FTM: TB RSTA not supported by device");
return -EOPNOTSUPP;
}
- if (out->ftm.rsta && !out->ftm.lmr_feedback) {
+ 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;
}
@@ -212,6 +313,7 @@ static int pmsr_parse_peer(struct cfg80211_registered_device *rdev,
{
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;
@@ -229,6 +331,19 @@ static int pmsr_parse_peer(struct cfg80211_registered_device *rdev,
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,
@@ -238,7 +353,7 @@ static int pmsr_parse_peer(struct cfg80211_registered_device *rdev,
return err;
err = nl80211_parse_chandef(rdev, info->extack, info->attrs,
- &out->chandef);
+ &out->chandef, false);
if (err)
return err;
@@ -265,6 +380,14 @@ static int pmsr_parse_peer(struct cfg80211_registered_device *rdev,
}
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);
@@ -274,10 +397,16 @@ static int pmsr_parse_peer(struct cfg80211_registered_device *rdev,
"unsupported measurement type");
err = -EINVAL;
}
+ if (err)
+ return err;
}
- 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;
}
@@ -286,12 +415,16 @@ 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;
- int count, rem, err, idx;
+ u64 cookie;
- if (!rdev->wiphy.pmsr_capa)
+ capa = rdev->wiphy.pmsr_capa;
+
+ if (!capa)
return -EOPNOTSUPP;
if (!reqattr)
@@ -306,13 +439,18 @@ int nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info)
nla_for_each_nested(peer, peers, rem) {
count++;
- if (count > rdev->wiphy.pmsr_capa->max_peers) {
+ 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;
@@ -322,7 +460,7 @@ int nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info)
req->timeout = nla_get_u32(info->attrs[NL80211_ATTR_TIMEOUT]);
if (info->attrs[NL80211_ATTR_MAC]) {
- if (!rdev->wiphy.pmsr_capa->randomize_mac_addr) {
+ if (!capa->randomize_mac_addr) {
NL_SET_ERR_MSG_ATTR(info->extack,
info->attrs[NL80211_ATTR_MAC],
"device cannot randomize MAC address");
@@ -347,16 +485,64 @@ int nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info)
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)
+ 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;
+ }
- list_add_tail(&req->list, &wdev->pmsr_list);
-
- nl_set_extack_cookie_u64(info->extack, req->cookie);
+ nl_set_extack_cookie_u64(info->extack, cookie);
return 0;
out_err:
kfree(req);
@@ -487,6 +673,21 @@ static int nl80211_pmsr_send_ftm_res(struct sk_buff *msg,
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))
@@ -643,13 +844,11 @@ static void cfg80211_pmsr_process_abort(struct wireless_dev *wdev)
}
}
-void cfg80211_pmsr_free_wk(struct work_struct *work)
+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);
- guard(wiphy)(wdev->wiphy);
-
cfg80211_pmsr_process_abort(wdev);
}
@@ -665,7 +864,7 @@ void cfg80211_pmsr_wdev_down(struct wireless_dev *wdev)
}
spin_unlock_bh(&wdev->pmsr_lock);
- cancel_work_sync(&wdev->pmsr_free_wk);
+ wiphy_work_cancel(wdev->wiphy, &wdev->pmsr_free_wk);
if (found)
cfg80211_pmsr_process_abort(wdev);
@@ -680,7 +879,7 @@ void cfg80211_release_pmsr(struct wireless_dev *wdev, u32 portid)
list_for_each_entry(req, &wdev->pmsr_list, list) {
if (req->nl_portid == portid) {
req->nl_portid = 0;
- schedule_work(&wdev->pmsr_free_wk);
+ wiphy_work_queue(wdev->wiphy, &wdev->pmsr_free_wk);
}
}
spin_unlock_bh(&wdev->pmsr_lock);
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index bba239a068f6..46849fe8d0b3 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -736,13 +736,14 @@ static inline int
rdev_remain_on_channel(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
struct ieee80211_channel *chan,
- unsigned int duration, u64 *cookie)
+ unsigned int duration, u64 cookie, const u8 *rx_addr)
{
int ret;
- trace_rdev_remain_on_channel(&rdev->wiphy, wdev, chan, duration);
+ trace_rdev_remain_on_channel(&rdev->wiphy, wdev, chan, duration,
+ rx_addr);
ret = rdev->ops->remain_on_channel(&rdev->wiphy, wdev, chan,
- duration, cookie);
- trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie);
+ duration, cookie, rx_addr);
+ trace_rdev_return_int_cookie(&rdev->wiphy, ret, cookie);
return ret;
}
@@ -760,12 +761,12 @@ rdev_cancel_remain_on_channel(struct cfg80211_registered_device *rdev,
static inline int rdev_mgmt_tx(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
struct cfg80211_mgmt_tx_params *params,
- u64 *cookie)
+ u64 cookie)
{
int ret;
trace_rdev_mgmt_tx(&rdev->wiphy, wdev, params);
ret = rdev->ops->mgmt_tx(&rdev->wiphy, wdev, params, cookie);
- trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie);
+ trace_rdev_return_int_cookie(&rdev->wiphy, ret, cookie);
return ret;
}
@@ -774,7 +775,7 @@ static inline int rdev_tx_control_port(struct cfg80211_registered_device *rdev,
const void *buf, size_t len,
const u8 *dest, __be16 proto,
const bool noencrypt, int link,
- u64 *cookie)
+ u64 cookie)
{
int ret;
trace_rdev_tx_control_port(&rdev->wiphy, dev, buf, len,
@@ -782,7 +783,7 @@ static inline int rdev_tx_control_port(struct cfg80211_registered_device *rdev,
ret = rdev->ops->tx_control_port(&rdev->wiphy, dev, buf, len,
dest, proto, noencrypt, link, cookie);
if (cookie)
- trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie);
+ trace_rdev_return_int_cookie(&rdev->wiphy, ret, cookie);
else
trace_rdev_return_int(&rdev->wiphy, ret);
return ret;
@@ -947,14 +948,14 @@ static inline int rdev_tdls_oper(struct cfg80211_registered_device *rdev,
return ret;
}
-static inline int rdev_probe_client(struct cfg80211_registered_device *rdev,
- struct net_device *dev, const u8 *peer,
- u64 *cookie)
+static inline int rdev_probe_peer(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, const u8 *peer,
+ u64 cookie)
{
int ret;
- trace_rdev_probe_client(&rdev->wiphy, dev, peer);
- ret = rdev->ops->probe_client(&rdev->wiphy, dev, peer, cookie);
- trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie);
+ trace_rdev_probe_peer(&rdev->wiphy, dev, peer);
+ ret = rdev->ops->probe_peer(&rdev->wiphy, dev, peer, cookie);
+ trace_rdev_return_int_cookie(&rdev->wiphy, ret, cookie);
return ret;
}
@@ -1092,6 +1093,25 @@ rdev_nan_set_peer_sched(struct cfg80211_registered_device *rdev,
return ret;
}
+static inline int rdev_start_pd(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev)
+{
+ int ret;
+
+ trace_rdev_start_pd(&rdev->wiphy, wdev);
+ ret = rdev->ops->start_pd(&rdev->wiphy, wdev);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline void rdev_stop_pd(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev)
+{
+ trace_rdev_stop_pd(&rdev->wiphy, wdev);
+ rdev->ops->stop_pd(&rdev->wiphy, wdev);
+ trace_rdev_return_void(&rdev->wiphy);
+}
+
static inline int rdev_set_mac_acl(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct cfg80211_acl_data *params)
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 5db2121c0b57..a8336baf85dc 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -2412,6 +2412,9 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev)
case NL80211_IFTYPE_NAN_DATA:
/* NAN channels are checked in NL80211_IFTYPE_NAN interface */
break;
+ case NL80211_IFTYPE_PD:
+ /* we have no info, but PD is also pretty universal */
+ continue;
default:
/* others not implemented for now */
WARN_ON_ONCE(1);
@@ -3789,7 +3792,8 @@ static void print_regdomain(const struct ieee80211_regdomain *rd)
}
}
- pr_debug(" DFS Master region: %s", reg_dfs_region_str(rd->dfs_region));
+ pr_debug(" DFS Master region: %s\n",
+ reg_dfs_region_str(rd->dfs_region));
print_rd_rules(rd);
}
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 328af43ef832..9e934b185e34 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -5,7 +5,7 @@
* Copyright 2008 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright 2016 Intel Deutschland GmbH
- * Copyright (C) 2018-2025 Intel Corporation
+ * Copyright (C) 2018-2026 Intel Corporation
*/
#include <linux/kernel.h>
#include <linux/slab.h>
@@ -205,7 +205,7 @@ bool cfg80211_is_element_inherited(const struct element *elem,
return true;
if (elem->id == WLAN_EID_EXTENSION) {
- if (!ext_id_len)
+ if (!ext_id_len || !elem->datalen)
return true;
loop_len = ext_id_len;
list = &non_inherit_elem->data[3 + id_len];
@@ -326,8 +326,11 @@ cfg80211_gen_new_ie(const u8 *ie, size_t ielen,
/* For ML probe response, match the MLE in the frame body with
* MLD id being 'bssid_index'
*/
- if (parent->id == WLAN_EID_EXTENSION && parent->datalen > 1 &&
+ if (parent->id == WLAN_EID_EXTENSION &&
parent->data[0] == WLAN_EID_EXT_EHT_MULTI_LINK &&
+ ieee80211_mle_type_ok(parent->data + 1,
+ IEEE80211_ML_CONTROL_TYPE_BASIC,
+ parent->datalen - 1) &&
bssid_index == ieee80211_mle_get_mld_id(parent->data + 1)) {
if (!cfg80211_copy_elem_with_frags(parent,
ie, ielen,
@@ -1071,6 +1074,7 @@ int cfg80211_scan(struct cfg80211_registered_device *rdev)
struct cfg80211_scan_request_int *request;
struct cfg80211_scan_request_int *rdev_req = rdev->scan_req;
u32 n_channels = 0, idx, i;
+ int err;
if (!(rdev->wiphy.flags & WIPHY_FLAG_SPLIT_SCAN_6GHZ)) {
rdev_req->req.first_part = true;
@@ -1100,8 +1104,14 @@ int cfg80211_scan(struct cfg80211_registered_device *rdev)
rdev_req->req.scan_6ghz = false;
rdev_req->req.first_part = true;
+ err = rdev_scan(rdev, request);
+ if (err) {
+ kfree(request);
+ return err;
+ }
+
rdev->int_scan_req = request;
- return rdev_scan(rdev, request);
+ return 0;
}
void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev,
@@ -1602,10 +1612,12 @@ struct cfg80211_bss *__cfg80211_get_bss(struct wiphy *wiphy,
const u8 *ssid, size_t ssid_len,
enum ieee80211_bss_type bss_type,
enum ieee80211_privacy privacy,
- u32 use_for)
+ u32 use_for,
+ struct netlink_ext_ack *extack)
{
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
struct cfg80211_internal_bss *bss, *res = NULL;
+ bool expired = false, unusable = false;
unsigned long now = jiffies;
int bss_privacy;
@@ -1627,22 +1639,48 @@ struct cfg80211_bss *__cfg80211_get_bss(struct wiphy *wiphy,
continue;
if (!is_valid_ether_addr(bss->pub.bssid))
continue;
- if ((bss->pub.use_for & use_for) != use_for)
+ if (!is_bss(&bss->pub, bssid, ssid, ssid_len))
continue;
+
+ /*
+ * The identity checks above must all come first so that
+ * the expired/unusable classification below only ever
+ * applies to entries that actually match the request.
+ */
+
/* Don't get expired BSS structs */
if (time_after(now, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE) &&
- !atomic_read(&bss->hold))
+ !atomic_read(&bss->hold)) {
+ expired = true;
+ continue;
+ }
+
+ if ((bss->pub.use_for & use_for) != use_for) {
+ unusable = true;
continue;
- if (is_bss(&bss->pub, bssid, ssid, ssid_len)) {
- res = bss;
- bss_ref_get(rdev, res);
- break;
}
+
+ res = bss;
+ bss_ref_get(rdev, res);
+ break;
}
spin_unlock_bh(&rdev->bss_lock);
- if (!res)
+ if (!res) {
+ if (expired && unusable)
+ NL_SET_ERR_MSG(extack,
+ "BSS entries are expired or cannot be used for the requested operation");
+ else if (unusable)
+ NL_SET_ERR_MSG(extack,
+ "BSS cannot be used for the requested operation");
+ else if (expired)
+ NL_SET_ERR_MSG(extack,
+ "BSS entry in scan results is expired");
+ else
+ NL_SET_ERR_MSG(extack,
+ "BSS not found in scan results");
return NULL;
+ }
trace_cfg80211_return_bss(&res->pub);
return &res->pub;
}
@@ -2396,12 +2434,11 @@ drop:
return NULL;
}
-static const struct element
-*cfg80211_get_profile_continuation(const u8 *ie, size_t ielen,
- const struct element *mbssid_elem,
- const struct element *sub_elem)
+static bool cfg80211_iter_profile_continuation(const u8 *ie, size_t ielen,
+ const struct element **mbssid,
+ const struct element **sub_elem)
{
- const u8 *mbssid_end = mbssid_elem->data + mbssid_elem->datalen;
+ const u8 *mbssid_end = (*mbssid)->data + (*mbssid)->datalen;
const struct element *next_mbssid;
const struct element *next_sub;
@@ -2413,30 +2450,34 @@ static const struct element
* If it is not the last subelement in current MBSSID IE or there isn't
* a next MBSSID IE - profile is complete.
*/
- if ((sub_elem->data + sub_elem->datalen < mbssid_end - 1) ||
+ if (((*sub_elem)->data + (*sub_elem)->datalen < mbssid_end - 1) ||
!next_mbssid)
- return NULL;
+ return false;
- /* For any length error, just return NULL */
+ /* For any length error, just return false to stop iteration */
if (next_mbssid->datalen < 4)
- return NULL;
+ return false;
next_sub = (void *)&next_mbssid->data[1];
if (next_mbssid->data + next_mbssid->datalen <
next_sub->data + next_sub->datalen)
- return NULL;
+ return false;
if (next_sub->id != 0 || next_sub->datalen < 2)
- return NULL;
+ return false;
/*
* Check if the first element in the next sub element is a start
* of a new profile
*/
- return next_sub->data[0] == WLAN_EID_NON_TX_BSSID_CAP ?
- NULL : next_mbssid;
+ if (next_sub->data[0] == WLAN_EID_NON_TX_BSSID_CAP)
+ return false;
+
+ *mbssid = next_mbssid;
+ *sub_elem = next_sub;
+ return true;
}
size_t cfg80211_merge_profile(const u8 *ie, size_t ielen,
@@ -2445,23 +2486,20 @@ size_t cfg80211_merge_profile(const u8 *ie, size_t ielen,
u8 *merged_ie, size_t max_copy_len)
{
size_t copied_len = sub_elem->datalen;
- const struct element *next_mbssid;
if (sub_elem->datalen > max_copy_len)
return 0;
memcpy(merged_ie, sub_elem->data, sub_elem->datalen);
- while ((next_mbssid = cfg80211_get_profile_continuation(ie, ielen,
- mbssid_elem,
- sub_elem))) {
- const struct element *next_sub = (void *)&next_mbssid->data[1];
-
- if (copied_len + next_sub->datalen > max_copy_len)
+ while (cfg80211_iter_profile_continuation(ie, ielen,
+ &mbssid_elem,
+ &sub_elem)) {
+ if (copied_len + sub_elem->datalen > max_copy_len)
break;
- memcpy(merged_ie + copied_len, next_sub->data,
- next_sub->datalen);
- copied_len += next_sub->datalen;
+ memcpy(merged_ie + copied_len, sub_elem->data,
+ sub_elem->datalen);
+ copied_len += sub_elem->datalen;
}
return copied_len;
@@ -2600,7 +2638,9 @@ ssize_t cfg80211_defragment_element(const struct element *elem, const u8 *ies,
ssize_t copied;
u8 elem_datalen;
- if (!elem)
+ if (!elem || (const u8 *)elem < ies ||
+ (const u8 *)elem + sizeof(*elem) > ies + ieslen ||
+ (const u8 *)elem + sizeof(*elem) + elem->datalen > ies + ieslen)
return -EINVAL;
/* elem might be invalid after the memmove */
@@ -3299,14 +3339,15 @@ cfg80211_inform_bss_frame_data(struct wiphy *wiphy,
bssid = ext->u.s1g_beacon.sa;
capability = le16_to_cpu(compat->compat_info);
beacon_interval = le16_to_cpu(compat->beacon_int);
+ tsf = le32_to_cpu(ext->u.s1g_beacon.timestamp);
+ tsf |= (u64)le32_to_cpu(compat->tsf_completion) << 32;
} else {
bssid = mgmt->bssid;
beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
+ tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
}
- tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
-
if (ieee80211_is_probe_resp(mgmt->frame_control))
ftype = CFG80211_BSS_FTYPE_PRESP;
else if (ext)
@@ -3600,8 +3641,10 @@ int cfg80211_wext_siwscan(struct net_device *dev,
/* translate "Scan for SSID" request */
if (wreq) {
if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
- if (wreq->essid_len > IEEE80211_MAX_SSID_LEN)
- return -EINVAL;
+ if (wreq->essid_len > IEEE80211_MAX_SSID_LEN) {
+ err = -EINVAL;
+ goto out;
+ }
memcpy(creq->req.ssids[0].ssid, wreq->essid,
wreq->essid_len);
creq->req.ssids[0].ssid_len = wreq->essid_len;
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index 86e2ccaa678c..2a719b5c487e 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -1066,6 +1066,7 @@ void cfg80211_connect_done(struct net_device *dev,
}
ev->cr.status = params->status;
ev->cr.timeout_reason = params->timeout_reason;
+ ev->cr.assoc_encrypted = params->assoc_encrypted;
spin_lock_irqsave(&wdev->event_lock, flags);
list_add_tail(&ev->list, &wdev->event_list);
@@ -1577,13 +1578,11 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
* Used to clean up after the connection / connection attempt owner socket
* disconnects
*/
-void cfg80211_autodisconnect_wk(struct work_struct *work)
+void cfg80211_autodisconnect_wk(struct wiphy *wiphy, struct wiphy_work *work)
{
struct wireless_dev *wdev =
container_of(work, struct wireless_dev, disconnect_wk);
- struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
-
- guard(wiphy)(wdev->wiphy);
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
if (wdev->conn_owner_nlportid) {
switch (wdev->iftype) {
diff --git a/net/wireless/tests/chan.c b/net/wireless/tests/chan.c
index 7b97b731993c..65eb18c498de 100644
--- a/net/wireless/tests/chan.c
+++ b/net/wireless/tests/chan.c
@@ -2,13 +2,18 @@
/*
* KUnit tests for channel helper functions
*
- * Copyright (C) 2023-2024 Intel Corporation
+ * 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,
@@ -215,14 +220,189 @@ static void test_chandef_compat(struct kunit *test)
KUNIT_EXPECT_PTR_EQ(test, ret, expect);
}
-static struct kunit_case chandef_compat_test_cases[] = {
+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_compat = {
- .name = "cfg80211-chandef-compat",
- .test_cases = chandef_compat_test_cases,
+static struct kunit_suite chandef = {
+ .name = "cfg80211-chandef",
+ .test_cases = chandef_test_cases,
};
-kunit_test_suite(chandef_compat);
+kunit_test_suite(chandef);
diff --git a/net/wireless/tests/scan.c b/net/wireless/tests/scan.c
index b1a9c1466d6c..8c20278b5d3a 100644
--- a/net/wireless/tests/scan.c
+++ b/net/wireless/tests/scan.c
@@ -402,6 +402,124 @@ static void test_inform_bss_ssid_only(struct kunit *test)
cfg80211_put_bss(wiphy, bss);
}
+static void test_get_bss_miss_reason(struct kunit *test)
+{
+ struct inform_bss ctx = {
+ .test = test,
+ };
+ struct wiphy *wiphy = T_WIPHY(test, ctx);
+ struct cfg80211_inform_bss inform_bss = {
+ .signal = 50,
+ .drv_data = &ctx,
+ };
+ const u8 bssid[ETH_ALEN] = { 0x10, 0x22, 0x33, 0x44, 0x55, 0x66 };
+ const u8 other_bssid[ETH_ALEN] = { 0x66, 0x55, 0x44, 0x33, 0x22, 0x11 };
+ static const u8 ies[] = {
+ [0] = WLAN_EID_SSID,
+ [1] = 4,
+ [2] = 'T', 'E', 'S', 'T'
+ };
+ struct cfg80211_internal_bss *ibss;
+ struct netlink_ext_ack extack = {};
+ struct cfg80211_bss *bss, *bss2, *found;
+
+ 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, 0,
+ 0x1234, 100, ies, sizeof(ies),
+ GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, bss);
+ ibss = container_of(bss, struct cfg80211_internal_bss, pub);
+
+ /* Fresh usable entry: found, no message is set */
+ found = __cfg80211_get_bss(wiphy, NULL, bssid, NULL, 0,
+ IEEE80211_BSS_TYPE_ANY,
+ IEEE80211_PRIVACY_ANY,
+ NL80211_BSS_USE_FOR_NORMAL, &extack);
+ KUNIT_ASSERT_PTR_EQ(test, found, bss);
+ KUNIT_EXPECT_NULL(test, extack._msg);
+ cfg80211_put_bss(wiphy, found);
+
+ /* No entry at all for this BSSID */
+ found = __cfg80211_get_bss(wiphy, NULL, other_bssid, NULL, 0,
+ IEEE80211_BSS_TYPE_ANY,
+ IEEE80211_PRIVACY_ANY,
+ NL80211_BSS_USE_FOR_NORMAL, &extack);
+ KUNIT_EXPECT_NULL(test, found);
+ KUNIT_EXPECT_STREQ(test, extack._msg, "BSS not found in scan results");
+
+ /* Fresh entry that is not usable for the requested use */
+ extack._msg = NULL;
+ bss->use_for = 0;
+ found = __cfg80211_get_bss(wiphy, NULL, bssid, NULL, 0,
+ IEEE80211_BSS_TYPE_ANY,
+ IEEE80211_PRIVACY_ANY,
+ NL80211_BSS_USE_FOR_NORMAL, &extack);
+ KUNIT_EXPECT_NULL(test, found);
+ KUNIT_EXPECT_STREQ(test, extack._msg,
+ "BSS cannot be used for the requested operation");
+ bss->use_for = NL80211_BSS_USE_FOR_ALL;
+
+ /* Expired entry, > IEEE80211_SCAN_RESULT_EXPIRE (30s) old */
+ extack._msg = NULL;
+ ibss->ts = jiffies - 60 * HZ;
+ found = __cfg80211_get_bss(wiphy, NULL, bssid, NULL, 0,
+ IEEE80211_BSS_TYPE_ANY,
+ IEEE80211_PRIVACY_ANY,
+ NL80211_BSS_USE_FOR_NORMAL, &extack);
+ KUNIT_EXPECT_NULL(test, found);
+ KUNIT_EXPECT_STREQ(test, extack._msg,
+ "BSS entry in scan results is expired");
+
+ /* An entry both expired and unusable reports expired */
+ extack._msg = NULL;
+ bss->use_for = 0;
+ found = __cfg80211_get_bss(wiphy, NULL, bssid, NULL, 0,
+ IEEE80211_BSS_TYPE_ANY,
+ IEEE80211_PRIVACY_ANY,
+ NL80211_BSS_USE_FOR_NORMAL, &extack);
+ KUNIT_EXPECT_NULL(test, found);
+ KUNIT_EXPECT_STREQ(test, extack._msg,
+ "BSS entry in scan results is expired");
+ bss->use_for = NL80211_BSS_USE_FOR_ALL;
+
+ /* Expired but held entries are still usable, no message is set */
+ extack._msg = NULL;
+ atomic_set(&ibss->hold, 1);
+ found = __cfg80211_get_bss(wiphy, NULL, bssid, NULL, 0,
+ IEEE80211_BSS_TYPE_ANY,
+ IEEE80211_PRIVACY_ANY,
+ NL80211_BSS_USE_FOR_NORMAL, &extack);
+ KUNIT_ASSERT_PTR_EQ(test, found, bss);
+ KUNIT_EXPECT_NULL(test, extack._msg);
+ cfg80211_put_bss(wiphy, found);
+ atomic_set(&ibss->hold, 0);
+
+ /*
+ * With one matching entry expired and another current but
+ * unusable, both reasons are reported.
+ */
+ bss2 = cfg80211_inform_bss_data(wiphy, &inform_bss,
+ CFG80211_BSS_FTYPE_PRESP, other_bssid,
+ 0, 0x1234, 100, ies, sizeof(ies),
+ GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, bss2);
+ bss2->use_for = 0;
+ extack._msg = NULL;
+ found = __cfg80211_get_bss(wiphy, NULL, NULL, "TEST", 4,
+ IEEE80211_BSS_TYPE_ANY,
+ IEEE80211_PRIVACY_ANY,
+ NL80211_BSS_USE_FOR_NORMAL, &extack);
+ KUNIT_EXPECT_NULL(test, found);
+ KUNIT_EXPECT_STREQ(test, extack._msg,
+ "BSS entries are expired or cannot be used for the requested operation");
+
+ cfg80211_put_bss(wiphy, bss2);
+ cfg80211_put_bss(wiphy, bss);
+}
+
static struct inform_bss_ml_sta_case {
const char *desc;
int mld_id;
@@ -617,7 +735,7 @@ static void test_inform_bss_ml_sta(struct kunit *test)
link_bss = __cfg80211_get_bss(wiphy, NULL, sta_prof.bssid, NULL, 0,
IEEE80211_BSS_TYPE_ANY,
IEEE80211_PRIVACY_ANY,
- 0);
+ 0, NULL);
KUNIT_ASSERT_NOT_NULL(test, link_bss);
KUNIT_EXPECT_EQ(test, link_bss->signal, 0);
KUNIT_EXPECT_EQ(test, link_bss->beacon_interval,
@@ -855,6 +973,7 @@ kunit_test_suite(gen_new_ie);
static struct kunit_case inform_bss_test_cases[] = {
KUNIT_CASE(test_inform_bss_ssid_only),
+ KUNIT_CASE(test_get_bss_miss_reason),
KUNIT_CASE_PARAM(test_inform_bss_ml_sta, inform_bss_ml_sta_gen_params),
{}
};
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index eb5bedf9c92a..8c2a91b85c39 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -141,7 +141,9 @@
__field(u32, center_freq1) \
__field(u32, freq1_offset) \
__field(u32, center_freq2) \
- __field(u16, punctured)
+ __field(u16, punctured) \
+ __field(u32, npca_pri_freq) \
+ __field(u16, npca_punctured)
#define CHAN_DEF_ASSIGN(chandef) \
do { \
if ((chandef) && (chandef)->chan) { \
@@ -155,6 +157,11 @@
__entry->freq1_offset = (chandef)->freq1_offset;\
__entry->center_freq2 = (chandef)->center_freq2;\
__entry->punctured = (chandef)->punctured; \
+ __entry->npca_pri_freq = \
+ (chandef)->npca_chan ? \
+ (chandef)->npca_chan->center_freq : 0; \
+ __entry->npca_punctured = \
+ (chandef)->npca_punctured; \
} else { \
__entry->band = 0; \
__entry->control_freq = 0; \
@@ -164,14 +171,17 @@
__entry->freq1_offset = 0; \
__entry->center_freq2 = 0; \
__entry->punctured = 0; \
+ __entry->npca_pri_freq = 0; \
+ __entry->npca_punctured = 0; \
} \
} while (0)
#define CHAN_DEF_PR_FMT \
- "band: %d, control freq: %u.%03u, width: %d, cf1: %u.%03u, cf2: %u, punct: 0x%x"
+ "band: %d, control freq: %u.%03u, width: %d, cf1: %u.%03u, cf2: %u, punct: 0x%x, npca:%u, npca_punct:0x%x"
#define CHAN_DEF_PR_ARG __entry->band, __entry->control_freq, \
__entry->freq_offset, __entry->width, \
__entry->center_freq1, __entry->freq1_offset, \
- __entry->center_freq2, __entry->punctured
+ __entry->center_freq2, __entry->punctured, \
+ __entry->npca_pri_freq, __entry->npca_punctured
#define FILS_AAD_ASSIGN(fa) \
do { \
@@ -2122,7 +2132,7 @@ DECLARE_EVENT_CLASS(rdev_pmksa,
WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->bssid)
);
-TRACE_EVENT(rdev_probe_client,
+TRACE_EVENT(rdev_probe_peer,
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
const u8 *peer),
TP_ARGS(wiphy, netdev, peer),
@@ -2155,22 +2165,26 @@ DEFINE_EVENT(rdev_pmksa, rdev_del_pmksa,
TRACE_EVENT(rdev_remain_on_channel,
TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
struct ieee80211_channel *chan,
- unsigned int duration),
- TP_ARGS(wiphy, wdev, chan, duration),
+ unsigned int duration, const u8 *rx_addr),
+ TP_ARGS(wiphy, wdev, chan, duration, rx_addr),
TP_STRUCT__entry(
WIPHY_ENTRY
WDEV_ENTRY
CHAN_ENTRY
__field(unsigned int, duration)
+ MAC_ENTRY(rx_addr)
),
TP_fast_assign(
WIPHY_ASSIGN;
WDEV_ASSIGN;
CHAN_ASSIGN(chan);
__entry->duration = duration;
+ MAC_ASSIGN(rx_addr, rx_addr);
),
- TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", " CHAN_PR_FMT ", duration: %u",
- WIPHY_PR_ARG, WDEV_PR_ARG, CHAN_PR_ARG, __entry->duration)
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", " CHAN_PR_FMT
+ ", duration: %u, %pM",
+ WIPHY_PR_ARG, WDEV_PR_ARG, CHAN_PR_ARG, __entry->duration,
+ __entry->rx_addr)
);
TRACE_EVENT(rdev_return_int_cookie,
@@ -2371,6 +2385,16 @@ DEFINE_EVENT(wiphy_wdev_evt, rdev_stop_nan,
TP_ARGS(wiphy, wdev)
);
+DEFINE_EVENT(wiphy_wdev_evt, rdev_start_pd,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
+ TP_ARGS(wiphy, wdev)
+);
+
+DEFINE_EVENT(wiphy_wdev_evt, rdev_stop_pd,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
+ TP_ARGS(wiphy, wdev)
+);
+
TRACE_EVENT(rdev_add_nan_func,
TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
const struct cfg80211_nan_func *func),
diff --git a/net/wireless/util.c b/net/wireless/util.c
index cff5a1bd95cc..3e584d0ca3e2 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -241,10 +241,8 @@ bool cfg80211_supported_cipher_suite(struct wiphy *wiphy, u32 cipher)
return false;
}
-static bool
-cfg80211_igtk_cipher_supported(struct cfg80211_registered_device *rdev)
+static bool cfg80211_igtk_cipher_supported(struct wiphy *wiphy)
{
- struct wiphy *wiphy = &rdev->wiphy;
int i;
for (i = 0; i < wiphy->n_cipher_suites; i++) {
@@ -260,40 +258,94 @@ cfg80211_igtk_cipher_supported(struct cfg80211_registered_device *rdev)
return false;
}
-bool cfg80211_valid_key_idx(struct cfg80211_registered_device *rdev,
- int key_idx, bool pairwise)
+bool cfg80211_valid_key_idx(struct wireless_dev *wdev,
+ int key_idx, bool pairwise,
+ const u8 *mac_addr)
{
- int max_key_idx;
-
- if (pairwise)
- max_key_idx = 3;
- else if (wiphy_ext_feature_isset(&rdev->wiphy,
- NL80211_EXT_FEATURE_BEACON_PROTECTION) ||
- wiphy_ext_feature_isset(&rdev->wiphy,
- NL80211_EXT_FEATURE_BEACON_PROTECTION_CLIENT))
- max_key_idx = 7;
- else if (cfg80211_igtk_cipher_supported(rdev))
- max_key_idx = 5;
- else
- max_key_idx = 3;
+ if (WARN_ON(!wdev))
+ return false;
- if (key_idx < 0 || key_idx > max_key_idx)
+ if (key_idx < 0)
return false;
- return true;
+ /*
+ * Can't differentiate ciphers here so allow 0..3.
+ * Pairwise keys must be for a station (MAC address given).
+ */
+ if (pairwise) {
+ if (!mac_addr)
+ return false;
+
+ return key_idx < 4;
+ }
+
+ /*
+ * For group keys, mac_addr==NULL means setting a group key
+ * for TX, which is only supported on some interface types,
+ * except for STATION/P2P_CLIENT, where it's setting the RX
+ * key with the current AP (for legacy reasons.)
+ *
+ * Apart from that exception, a non-NULL mac_addr means RX
+ * key being set.
+ */
+
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_ADHOC:
+ if (!(wdev->wiphy->flags & WIPHY_FLAG_IBSS_RSN))
+ return false;
+ fallthrough;
+ case NL80211_IFTYPE_MESH_POINT:
+ /* no support for IGTK/BIGTK (yet?) */
+ return key_idx < 4;
+ case NL80211_IFTYPE_NAN_DATA:
+ /* these always need to support per-STA GTK */
+ return key_idx < 4;
+ case NL80211_IFTYPE_NAN:
+ /* no data */
+ if (key_idx < 4)
+ return false;
+ /* NAN reused this flag */
+ if (wiphy_ext_feature_isset(wdev->wiphy,
+ NL80211_EXT_FEATURE_BEACON_PROTECTION))
+ return key_idx <= 7;
+ return key_idx <= 5;
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ /* see note about exception above */
+ if (mac_addr)
+ return false;
+ /* BIGTK support implies IGTK support */
+ if (wiphy_ext_feature_isset(wdev->wiphy,
+ NL80211_EXT_FEATURE_BEACON_PROTECTION_CLIENT))
+ return key_idx <= 7;
+ fallthrough;
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ /* no RX with [B]IGTK */
+ if (mac_addr)
+ return false;
+ if (wiphy_ext_feature_isset(wdev->wiphy,
+ NL80211_EXT_FEATURE_BEACON_PROTECTION))
+ return key_idx <= 7;
+ fallthrough;
+ case NL80211_IFTYPE_AP_VLAN:
+ /* no RX with GTK */
+ if (mac_addr)
+ return false;
+ if (cfg80211_igtk_cipher_supported(wdev->wiphy))
+ return key_idx <= 5;
+ return key_idx <= 3;
+ default:
+ return false;
+ }
}
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)
{
- if (!cfg80211_valid_key_idx(rdev, key_idx, pairwise))
- return -EINVAL;
-
- if (!pairwise && mac_addr && !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
- return -EINVAL;
-
- if (pairwise && !mac_addr)
+ if (!cfg80211_valid_key_idx(wdev, key_idx, pairwise, mac_addr))
return -EINVAL;
switch (params->cipher) {
@@ -344,6 +396,15 @@ int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,
break;
}
+ /*
+ * Per Wi-Fi Aware v4.0 section 7.1.2, NAN Data interfaces
+ * shall only use CCMP-128 or GCMP-256.
+ */
+ if (wdev->iftype == NL80211_IFTYPE_NAN_DATA &&
+ params->cipher != WLAN_CIPHER_SUITE_CCMP &&
+ params->cipher != WLAN_CIPHER_SUITE_GCMP_256)
+ return -EINVAL;
+
switch (params->cipher) {
case WLAN_CIPHER_SUITE_WEP40:
if (params->key_len != WLAN_KEY_LEN_WEP40)
@@ -424,6 +485,21 @@ int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,
if (!cfg80211_supported_cipher_suite(&rdev->wiphy, params->cipher))
return -EINVAL;
+ if (params->ltf_keyseed) {
+ if (!wiphy_ext_feature_isset(&rdev->wiphy,
+ NL80211_EXT_FEATURE_SECURE_LTF) ||
+ !wiphy_ext_feature_isset(&rdev->wiphy,
+ NL80211_EXT_FEATURE_SET_KEY_LTF_SEED))
+ return -EOPNOTSUPP;
+
+ /*
+ * LTF key seed is pairwise key material and must only be
+ * used with a pairwise key
+ */
+ if (!pairwise)
+ return -EINVAL;
+ }
+
return 0;
}
@@ -1201,7 +1277,8 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
/* cannot change into P2P device or NAN */
if (ntype == NL80211_IFTYPE_P2P_DEVICE ||
- ntype == NL80211_IFTYPE_NAN)
+ ntype == NL80211_IFTYPE_NAN ||
+ ntype == NL80211_IFTYPE_PD)
return -EOPNOTSUPP;
if (!rdev->ops->change_virtual_intf ||
@@ -1266,6 +1343,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
case NL80211_IFTYPE_P2P_DEVICE:
case NL80211_IFTYPE_WDS:
case NL80211_IFTYPE_NAN:
+ case NL80211_IFTYPE_PD:
WARN_ON(1);
break;
}
@@ -2277,9 +2355,6 @@ bool ieee80211_chandef_to_operating_class(struct cfg80211_chan_def *chandef,
case NL80211_CHAN_WIDTH_80P80:
vht_opclass = 130;
break;
- case NL80211_CHAN_WIDTH_10:
- case NL80211_CHAN_WIDTH_5:
- return false; /* unsupported for now */
default:
vht_opclass = 0;
break;
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
index 22d9d9bae8f5..d45bc08c0de4 100644
--- a/net/wireless/wext-compat.c
+++ b/net/wireless/wext-compat.c
@@ -16,6 +16,7 @@
#include <linux/if_arp.h>
#include <linux/etherdevice.h>
#include <linux/slab.h>
+#include <linux/string.h>
#include <net/iw_handler.h>
#include <net/cfg80211.h>
#include <net/cfg80211-wext.h>
@@ -27,7 +28,7 @@ int cfg80211_wext_giwname(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
- strcpy(wrqu->name, "IEEE 802.11");
+ strscpy(wrqu->name, "IEEE 802.11");
return 0;
}
@@ -453,8 +454,7 @@ static int cfg80211_set_encryption(struct cfg80211_registered_device *rdev,
rejoin = true;
}
- if (!pairwise && addr &&
- !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
+ if (!cfg80211_valid_key_idx(wdev, idx, pairwise, addr))
err = -ENOENT;
else
err = rdev_del_key(rdev, wdev, -1, idx, pairwise,
@@ -489,7 +489,8 @@ static int cfg80211_set_encryption(struct cfg80211_registered_device *rdev,
if (addr)
tx_key = false;
- if (cfg80211_validate_key_settings(rdev, params, idx, pairwise, addr))
+ if (cfg80211_validate_key_settings(rdev, wdev, params, idx,
+ pairwise, addr))
return -EINVAL;
err = 0;
@@ -789,6 +790,8 @@ static int cfg80211_wext_siwfreq(struct net_device *dev,
chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq);
if (!chandef.chan)
return -EINVAL;
+ if (!cfg80211_chandef_valid(&chandef))
+ return -EINVAL;
return cfg80211_set_monitor_channel(rdev, dev, &chandef);
case NL80211_IFTYPE_MESH_POINT:
freq = cfg80211_wext_freq(wextfreq);
diff --git a/net/wireless/wext-core.c b/net/wireless/wext-core.c
index c19dece2bc6e..db77912b3994 100644
--- a/net/wireless/wext-core.c
+++ b/net/wireless/wext-core.c
@@ -660,8 +660,7 @@ struct iw_statistics *get_wireless_stats(struct net_device *dev)
dev->ieee80211_ptr->wiphy->wext &&
dev->ieee80211_ptr->wiphy->wext->get_wireless_stats) {
wireless_warn_cfg80211_wext();
- if (dev->ieee80211_ptr->wiphy->flags & (WIPHY_FLAG_SUPPORTS_MLO |
- WIPHY_FLAG_DISABLE_WEXT))
+ if (dev->ieee80211_ptr->wiphy->flags & WIPHY_FLAG_SUPPORTS_MLO)
return NULL;
return dev->ieee80211_ptr->wiphy->wext->get_wireless_stats(dev);
}
@@ -703,8 +702,7 @@ static iw_handler get_handler(struct net_device *dev, unsigned int cmd)
#ifdef CONFIG_CFG80211_WEXT
if (dev->ieee80211_ptr && dev->ieee80211_ptr->wiphy) {
wireless_warn_cfg80211_wext();
- if (dev->ieee80211_ptr->wiphy->flags & (WIPHY_FLAG_SUPPORTS_MLO |
- WIPHY_FLAG_DISABLE_WEXT))
+ if (dev->ieee80211_ptr->wiphy->flags & WIPHY_FLAG_SUPPORTS_MLO)
return NULL;
handlers = dev->ieee80211_ptr->wiphy->wext;
}
diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c
index 573b6b15a446..b5914f3658db 100644
--- a/net/wireless/wext-sme.c
+++ b/net/wireless/wext-sme.c
@@ -319,6 +319,15 @@ int cfg80211_wext_siwgenie(struct net_device *dev,
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;