diff options
Diffstat (limited to 'net/wireless')
| -rw-r--r-- | net/wireless/chan.c | 289 | ||||
| -rw-r--r-- | net/wireless/core.c | 78 | ||||
| -rw-r--r-- | net/wireless/core.h | 16 | ||||
| -rw-r--r-- | net/wireless/mlme.c | 114 | ||||
| -rw-r--r-- | net/wireless/nl80211.c | 1262 | ||||
| -rw-r--r-- | net/wireless/nl80211.h | 5 | ||||
| -rw-r--r-- | net/wireless/pmsr.c | 249 | ||||
| -rw-r--r-- | net/wireless/rdev-ops.h | 48 | ||||
| -rw-r--r-- | net/wireless/reg.c | 6 | ||||
| -rw-r--r-- | net/wireless/scan.c | 123 | ||||
| -rw-r--r-- | net/wireless/sme.c | 7 | ||||
| -rw-r--r-- | net/wireless/tests/chan.c | 192 | ||||
| -rw-r--r-- | net/wireless/tests/scan.c | 121 | ||||
| -rw-r--r-- | net/wireless/trace.h | 40 | ||||
| -rw-r--r-- | net/wireless/util.c | 137 | ||||
| -rw-r--r-- | net/wireless/wext-compat.c | 11 | ||||
| -rw-r--r-- | net/wireless/wext-core.c | 6 | ||||
| -rw-r--r-- | net/wireless/wext-sme.c | 9 |
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 = ¶ms->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, ¶ms->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, - ¶ms->chandef); + ¶ms->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, ¶ms->beacon, + params->chandef.chan, info->extack); + if (err) + goto out; + + err = nl80211_check_npca(rdev, ¶ms->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, ¶ms->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, + ¶ms.chandef, permit_npca); + if (err) + goto free; + + err = nl80211_check_npca(rdev, ¶ms.chandef, wdev->iftype, + info->extack); + if (err) + goto free; + if (!need_new_beacon) goto skip_beacons; err = nl80211_parse_beacon(rdev, info->attrs, ¶ms.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, ¶ms.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, - ¶ms.chandef); - if (err) - goto free; - if (!cfg80211_reg_can_beacon_relax(&rdev->wiphy, ¶ms.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, ¶ms, &cookie); + cookie = cfg80211_assign_cookie(rdev); + err = cfg80211_mlme_mgmt_tx(rdev, wdev, ¶ms, 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, ¶ms.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, ¶ms.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, ¶ms.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, ¶ms); 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(¶ms->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; |
