diff options
| author | Jakub Kicinski <kuba@kernel.org> | 2026-09-16 15:54:55 -0700 |
|---|---|---|
| committer | Jakub Kicinski <kuba@kernel.org> | 2026-09-16 15:54:56 -0700 |
| commit | fefaac1176bf3cf002a8dc83339d6ed6a369941a (patch) | |
| tree | ab8893c9e199bb9f981ab9fbc8840a3c5a6d28b5 /net | |
| parent | 7c7d5e9d7e3942ba5aec9847f61a6e76d7773191 (diff) | |
| parent | 1eeca1d5e0920fbdad6449768fd2d4364e714180 (diff) | |
| download | linux-next-fefaac1176bf3cf002a8dc83339d6ed6a369941a.tar.gz linux-next-fefaac1176bf3cf002a8dc83339d6ed6a369941a.zip | |
Merge tag 'wireless-2026-09-16' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless
Johannes Berg says:
====================
Many fixes:
- mac80211: S1G TIM bitmap fix
- ath12k: remove undocumented DT ABI implementation
- various firmware API and over-the-air hardening changes
- fixes for most cfg80211/mac80211 syzbot reports
* tag 'wireless-2026-09-16' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless: (67 commits)
wifi: brcmsmac: fix UAF in brcms_free_timer()
wifi: brcmfmac: fix lost 802.1x TX completion wakeup
wifi: ath11k: cleanup arsta in ath11k_mac_peer_cleanup_all()
wifi: wcn36xx: Fix potential use-after-free in TX ack timer teardown
wifi: ath12k: ahb: Revert undocumented ABI and dead code
wifi: mac80211: refuse to make a monitor active when it has no queue
wifi: libipw: reject TKIP frames without a full MIC
wifi: virt_wifi: don't transfer operstate before register
wifi: cfg80211: check if AP has been started or joined a mesh before adding new station
wifi: cfg80211: move link_id validation earlier in nl80211_new_station()
wifi: cfg80211: do not support direct add of station to AP_VLAN interfaces
wifi: cfg80211: verify if AP_VLAN belongs to the correct AP
wifi: mac80211: set up the TX info early to fix failure paths
wifi: mac80211: mesh: release the channel if start fails
wifi: mac80211: mesh: reset the CSA state when leaving
wifi: mac80211: add HE 6 GHz capability in the scan elems len
wifi: mac80211: don't access the TSF of a down interface
wifi: mac80211: don't RCU-dereference the mesh CSA settings we just set
wifi: mac80211: don't allow link changes when iface is down
wifi: mac80211: require a peer station for TDLS setup confirm
...
====================
Link: https://patch.msgid.link/20260916083642.110609-3-johannes@sipsolutions.net
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Diffstat (limited to 'net')
| -rw-r--r-- | net/mac80211/cfg.c | 18 | ||||
| -rw-r--r-- | net/mac80211/debugfs.c | 2 | ||||
| -rw-r--r-- | net/mac80211/debugfs_netdev.c | 9 | ||||
| -rw-r--r-- | net/mac80211/ieee80211_i.h | 2 | ||||
| -rw-r--r-- | net/mac80211/iface.c | 39 | ||||
| -rw-r--r-- | net/mac80211/main.c | 4 | ||||
| -rw-r--r-- | net/mac80211/mesh.c | 34 | ||||
| -rw-r--r-- | net/mac80211/offchannel.c | 7 | ||||
| -rw-r--r-- | net/mac80211/pm.c | 8 | ||||
| -rw-r--r-- | net/mac80211/rate.c | 8 | ||||
| -rw-r--r-- | net/mac80211/scan.c | 2 | ||||
| -rw-r--r-- | net/mac80211/tdls.c | 40 | ||||
| -rw-r--r-- | net/mac80211/tx.c | 115 | ||||
| -rw-r--r-- | net/wireless/core.c | 129 | ||||
| -rw-r--r-- | net/wireless/core.h | 18 | ||||
| -rw-r--r-- | net/wireless/ibss.c | 36 | ||||
| -rw-r--r-- | net/wireless/nl80211.c | 64 | ||||
| -rw-r--r-- | net/wireless/rdev-ops.h | 3 | ||||
| -rw-r--r-- | net/wireless/reg.c | 52 | ||||
| -rw-r--r-- | net/wireless/reg.h | 16 | ||||
| -rw-r--r-- | net/wireless/scan.c | 43 | ||||
| -rw-r--r-- | net/wireless/util.c | 38 |
22 files changed, 488 insertions, 199 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 23f4f9ec86d0..a1753335eb9d 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -115,6 +115,10 @@ static int ieee80211_set_mon_options(struct ieee80211_sub_if_data *sdata, return -EBUSY; } + /* TXQs are reserved in ieee80211_if_add() and cannot be added later */ + if ((params->flags & MONITOR_FLAG_ACTIVE) && !sdata->vif.txq) + return -EOPNOTSUPP; + /* validate whether MU-MIMO can be configured */ if (!ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF) && !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR) && @@ -1929,6 +1933,9 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, return 0; error: + link_conf->enable_beacon = false; + link_conf->beacon_int = prev_beacon_int; + sdata->vif.cfg.ssid_len = 0; ieee80211_link_release_channel(link); return err; @@ -3320,7 +3327,11 @@ static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev, if (err) return err; - return ieee80211_start_mesh(sdata); + err = ieee80211_start_mesh(sdata); + if (err) + ieee80211_link_release_channel(&sdata->deflink); + + return err; } static int ieee80211_leave_mesh(struct wiphy *wiphy, struct net_device *dev) @@ -3475,7 +3486,7 @@ static int ieee80211_set_txq_params(struct wiphy *wiphy, static int ieee80211_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wowlan) { - return __ieee80211_suspend(wiphy_priv(wiphy), wowlan); + return __ieee80211_suspend(wiphy_priv(wiphy), wowlan, false); } static int ieee80211_resume(struct wiphy *wiphy) @@ -4113,6 +4124,9 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy, if (!ieee80211_sdata_running(sdata)) return -ENETDOWN; + if (!(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) + return -ENETDOWN; + /* * If active validate the setting and reject it if it doesn't leave * at least one basic rate usable, since we really have to be able diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index 105653a16b68..e38631d7cfb4 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -384,7 +384,7 @@ static ssize_t reset_write(struct file *file, const char __user *user_buf, rtnl_lock(); wiphy_lock(local->hw.wiphy); - __ieee80211_suspend(&local->hw, NULL); + __ieee80211_suspend(&local->hw, NULL, true); ret = __ieee80211_resume(&local->hw); wiphy_unlock(local->hw.wiphy); diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index f3c6a41e4911..6aba22493670 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -657,6 +657,9 @@ static ssize_t ieee80211_if_fmt_tsf( struct ieee80211_local *local = sdata->local; u64 tsf; + if (!ieee80211_sdata_running((struct ieee80211_sub_if_data *)sdata)) + return -ENETDOWN; + tsf = drv_get_tsf(local, (struct ieee80211_sub_if_data *)sdata); return scnprintf(buf, buflen, "0x%016llx\n", (unsigned long long) tsf); @@ -670,6 +673,9 @@ static ssize_t ieee80211_if_parse_tsf( int ret; int tsf_is_delta = 0; + if (!ieee80211_sdata_running(sdata)) + return -ENETDOWN; + if (strncmp(buf, "reset", 5) == 0) { if (local->ops->reset_tsf) { drv_reset_tsf(local, sdata); @@ -729,6 +735,9 @@ static ssize_t ieee80211_if_parse_active_links(struct ieee80211_sub_if_data *sda if (kstrtou16(buf, 0, &active_links) || !active_links) return -EINVAL; + if (!ieee80211_sdata_running(sdata)) + return -ENETDOWN; + return ieee80211_set_active_links(&sdata->vif, active_links) ?: buflen; } IEEE80211_IF_FILE_RW(active_links); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 5761e9621491..d05f59467399 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -2432,7 +2432,7 @@ int ieee80211_reconfig(struct ieee80211_local *local); void ieee80211_stop_device(struct ieee80211_local *local, bool suspend); int __ieee80211_suspend(struct ieee80211_hw *hw, - struct cfg80211_wowlan *wowlan); + struct cfg80211_wowlan *wowlan, bool reset); static inline int __ieee80211_resume(struct ieee80211_hw *hw) { diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 43460a705a6b..889c32fd8de1 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -616,6 +616,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do RCU_INIT_POINTER(sdata->vif.bss_conf.chanctx_conf, NULL); /* see comment in the default case below */ ieee80211_free_keys(sdata, true); + /* increased by AP value on ifup, so reset on ifdown */ + sdata->crypto_tx_tailroom_needed_cnt = 0; /* no need to tell driver */ break; case NL80211_IFTYPE_MONITOR: @@ -924,9 +926,33 @@ static void ieee80211_teardown_sdata(struct ieee80211_sub_if_data *sdata) } } +/* + * The netdev can be unregistered without mac80211 doing it, e.g. by the netdev + * core when cfg80211 couldn't move it out of a network namespace that's being + * destroyed. Drop it from the interface list either way. + */ +static void ieee80211_unlist_sdata(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_sub_if_data *iter; + + ASSERT_RTNL(); + + list_for_each_entry(iter, &local->interfaces, list) { + if (iter != sdata) + continue; + guard(mutex)(&local->iflist_mtx); + list_del_rcu(&sdata->list); + return; + } +} + static void ieee80211_uninit(struct net_device *dev) { - ieee80211_teardown_sdata(IEEE80211_DEV_TO_SUB_IF(dev)); + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + ieee80211_unlist_sdata(sdata); + ieee80211_teardown_sdata(sdata); } static int ieee80211_netdev_setup_tc(struct net_device *dev, @@ -935,6 +961,9 @@ static int ieee80211_netdev_setup_tc(struct net_device *dev, struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; + if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) + return -EOPNOTSUPP; + return drv_net_setup_tc(local, sdata, dev, type, type_data); } @@ -964,7 +993,7 @@ static u16 ieee80211_monitor_select_queue(struct net_device *dev, /* reset flags and info before parsing radiotap header */ memset(info, 0, sizeof(*info)); - if (!ieee80211_parse_tx_radiotap(skb, dev)) + if (!ieee80211_parse_tx_radiotap(skb, dev, NULL)) return 0; /* doesn't matter, frame will be dropped */ len_rthdr = ieee80211_get_radiotap_len(skb->data); @@ -1603,8 +1632,12 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up) err_del_interface: drv_remove_interface(local, sdata); err_stop: - if (!local->open_count) + if (!local->open_count) { + ieee80211_led_radio(local, false); + ieee80211_mod_tpt_led_trig(local, 0, + IEEE80211_TPT_LEDTRIG_FL_RADIO); drv_stop(local, false); + } if (sdata->vif.type == NL80211_IFTYPE_NAN_DATA) RCU_INIT_POINTER(sdata->u.nan_data.nmi, NULL); if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) diff --git a/net/mac80211/main.c b/net/mac80211/main.c index a59837b9f480..6408e8464338 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -1453,6 +1453,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) sizeof(struct ieee80211_he_mcs_nss_supp) + IEEE80211_HE_PPE_THRES_MAX_LEN; + if (local->hw.wiphy->bands[NL80211_BAND_6GHZ]) + local->scan_ies_len += + 3 + sizeof(struct ieee80211_he_6ghz_capa); + if (supp_eht) local->scan_ies_len += 3 + sizeof(struct ieee80211_eht_cap_elem) + diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index d4507e4e6ec1..8f8814125375 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -1196,6 +1196,21 @@ int ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata) return 0; } +static void ieee80211_mesh_reset_csa(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + struct mesh_csa_settings *csa; + + /* Reset the TTL value and Initiator flag */ + ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_NONE; + ifmsh->chsw_ttl = 0; + + /* Remove the CSA and MCSP elements from the beacon */ + csa = sdata_dereference(ifmsh->csa, sdata); + RCU_INIT_POINTER(ifmsh->csa, NULL); + kfree_rcu(csa, rcu_head); +} + void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; @@ -1204,6 +1219,11 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata) netif_carrier_off(sdata->dev); + /* abort any running channel switch */ + sdata->vif.bss_conf.csa_active = false; + ieee80211_mesh_reset_csa(sdata); + ieee80211_vif_unblock_queues_csa(sdata); + /* flush STAs and mpaths on this iface */ sta_info_flush(sdata, -1); ieee80211_free_keys(sdata, true); @@ -1510,19 +1530,10 @@ free: int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata, u64 *changed) { - struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; - struct mesh_csa_settings *tmp_csa_settings; - int ret = 0; + int ret; - /* Reset the TTL value and Initiator flag */ - ifmsh->csa_role = IEEE80211_MESH_CSA_ROLE_NONE; - ifmsh->chsw_ttl = 0; + ieee80211_mesh_reset_csa(sdata); - /* Remove the CSA and MCSP elements from the beacon */ - tmp_csa_settings = sdata_dereference(ifmsh->csa, sdata); - RCU_INIT_POINTER(ifmsh->csa, NULL); - if (tmp_csa_settings) - kfree_rcu(tmp_csa_settings, rcu_head); ret = ieee80211_mesh_rebuild_beacon(sdata); if (ret) return -EINVAL; @@ -1555,7 +1566,6 @@ int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata, ret = ieee80211_mesh_rebuild_beacon(sdata); if (ret) { - tmp_csa_settings = rcu_dereference(ifmsh->csa); RCU_INIT_POINTER(ifmsh->csa, NULL); kfree_rcu(tmp_csa_settings, rcu_head); return ret; diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index 7acef80d5f1f..e30767853c36 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -460,6 +460,13 @@ static void __ieee80211_roc_work(struct ieee80211_local *local) return; if (!roc->started) { + /* + * The work can be started by a previous ROC work, but a scan + * can get between things; scan finish will retrigger us. + */ + if (local->scanning) + return; + WARN_ON(!local->emulate_chanctx); _ieee80211_start_next_roc(local); } else { diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index 5a508d99e84f..f63676c44853 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -18,7 +18,8 @@ static void ieee80211_sched_scan_cancel(struct ieee80211_local *local) cfg80211_sched_scan_stopped_locked(local->hw.wiphy, 0); } -int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) +int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan, + bool reset) { struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_sub_if_data *sdata; @@ -166,9 +167,10 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) /* * We disconnected on all interfaces before suspend, all channel - * contexts should be released. + * contexts should be released, but on 'reset' debugfs that's + * not true so don't check there. */ - WARN_ON(!list_empty(&local->chanctx_list)); + WARN_ON(!reset && !list_empty(&local->chanctx_list)); /* stop hardware - this must stop RX */ ieee80211_stop_device(local, true); diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index 64768abb0a5f..e910f03af777 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -372,6 +372,14 @@ static void __rate_control_send_low(struct ieee80211_hw *hw, u32 rate_flags = 0; int i; + /* + * Frames that shouldn't use the rate mask could be anything, + * even on a different band, so don't take the sta into account + * to avoid ending up without rates. + */ + if (info->control.flags & IEEE80211_TX_CTRL_DONT_USE_RATE_MASK) + sta = NULL; + if (sband->band == NL80211_BAND_S1GHZ) { info->control.rates[0].flags |= IEEE80211_TX_RC_S1G_MCS; info->control.rates[0].idx = 0; diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index eeff230bd909..8e950ef6d1ea 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -1242,7 +1242,7 @@ int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata, } } - if (WARN_ON_ONCE(n_ch == 0)) + if (n_ch == 0) return -EINVAL; local->int_scan_req->n_channels = n_ch; diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index dc2f662fe4c4..f663d28d9209 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -1142,6 +1142,7 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_local *local = sdata->local; enum ieee80211_smps_mode smps_mode = sdata->deflink.u.mgd.driver_smps_mode; + struct sta_info *sta; int ret; /* don't support setup with forced SMPS mode that's not off */ @@ -1168,14 +1169,10 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev, * Allow error packets to be sent - sometimes we don't even add a STA * before failing the setup. */ - if (status_code == 0) { - rcu_read_lock(); - if (!sta_info_get(sdata, peer)) { - rcu_read_unlock(); - ret = -ENOLINK; - goto out_unlock; - } - rcu_read_unlock(); + sta = sta_info_get(sdata, peer); + if ((status_code == 0 && !sta) || (sta && !sta->sta.tdls)) { + ret = -ENOLINK; + goto out_unlock; } ieee80211_flush_queues(local, sdata, false); @@ -1284,6 +1281,24 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, peer_capability, initiator, extra_ies, extra_ies_len); break; + case WLAN_TDLS_SETUP_CONFIRM: { + struct sta_info *sta; + + sta = sta_info_get(sdata, peer); + if (!sta || !sta->sta.tdls) { + ret = -ENOLINK; + break; + } + + ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, + link_id, action_code, + dialog_token, + status_code, + peer_capability, + initiator, extra_ies, + extra_ies_len, 0, NULL); + break; + } case WLAN_TDLS_DISCOVERY_REQUEST: /* * Protect the discovery so we can hear the TDLS discovery @@ -1292,7 +1307,6 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, */ drv_mgd_protect_tdls_discover(sdata->local, sdata, link_id); fallthrough; - case WLAN_TDLS_SETUP_CONFIRM: case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: /* no special handling */ ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, @@ -1442,6 +1456,10 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, */ tdls_dbg(sdata, "TDLS oper %d peer %pM\n", oper, peer); + sta = sta_info_get(sdata, peer); + if (!sta || !sta->sta.tdls) + return -ENOLINK; + switch (oper) { case NL80211_TDLS_ENABLE_LINK: if (sdata->vif.bss_conf.csa_active) { @@ -1449,10 +1467,6 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, return -EBUSY; } - sta = sta_info_get(sdata, peer); - if (!sta || !sta->sta.tdls) - return -ENOLINK; - iee80211_tdls_recalc_chanctx(sdata, sta); iee80211_tdls_recalc_ht_protection(sdata, sta); diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 3a1e2c9e1565..814399989b5e 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -744,10 +744,12 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx) assoc = test_sta_flag(tx->sta, WLAN_STA_ASSOC); /* - * Lets not bother rate control if we're associated and cannot - * talk to the sta. This should not happen. + * Lets not bother rate control if we're associated and cannot talk to + * the sta. This should not happen - except for frames that aren't + * really for the peer to start with and already ignore rates. */ - if (WARN(test_bit(SCAN_SW_SCANNING, &tx->local->scanning) && assoc && + if (!(info->control.flags & IEEE80211_TX_CTRL_DONT_USE_RATE_MASK) && + WARN(test_bit(SCAN_SW_SCANNING, &tx->local->scanning) && assoc && !rate_usable_index_exists(sband, &tx->sta->sta), "%s: Dropped data frame as no usable bitrate found while " "scanning and associated. Target station: " @@ -2103,8 +2105,29 @@ static bool ieee80211_validate_radiotap_len(struct sk_buff *skb) return true; } +static bool ieee80211_rate_bw_usable(u16 rate_flags, + const struct cfg80211_chan_def *chandef) +{ + int width; + + if (!chandef) + return true; + + if (rate_flags & IEEE80211_TX_RC_160_MHZ_WIDTH) + width = 160; + else if (rate_flags & IEEE80211_TX_RC_80_MHZ_WIDTH) + width = 80; + else if (rate_flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + width = 40; + else + return true; + + return width <= cfg80211_chandef_get_width(chandef); +} + bool ieee80211_parse_tx_radiotap(struct sk_buff *skb, - struct net_device *dev) + struct net_device *dev, + const struct cfg80211_chan_def *chandef) { struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); struct ieee80211_radiotap_iterator iterator; @@ -2278,6 +2301,9 @@ bool ieee80211_parse_tx_radiotap(struct sk_buff *skb, struct ieee80211_supported_band *sband = local->hw.wiphy->bands[info->band]; + if (!ieee80211_rate_bw_usable(rate_flags, chandef)) + return false; + info->control.flags |= IEEE80211_TX_CTRL_RATE_INJECT; for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { @@ -2477,7 +2503,7 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, * selected chandef above to accurately set injection rates and * retransmissions. */ - if (!ieee80211_parse_tx_radiotap(skb, dev)) + if (!ieee80211_parse_tx_radiotap(skb, dev, chandef)) goto fail_rcu; /* remove the injection radiotap header */ @@ -2955,10 +2981,23 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, */ skb = skb_share_check(skb, GFP_ATOMIC); if (unlikely(!skb)) { - ret = -ENOMEM; - goto free; + /* skb_share_check() already freed the skb */ + if (info_id) + ieee80211_remove_ack_skb(local, info_id); + return ERR_PTR(-ENOMEM); } + /* set this up so failure paths can clean up ack skb */ + info = IEEE80211_SKB_CB(skb); + memset(info, 0, sizeof(*info)); + + info->flags = info_flags; + if (info_id) { + info->status_data = info_id; + info->status_data_idr = 1; + } + info->band = band; + hdr.frame_control = fc; hdr.duration_id = 0; hdr.seq_ctrl = 0; @@ -2997,10 +3036,8 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, head_need += local->tx_headroom; head_need = max_t(int, 0, head_need); if (ieee80211_skb_resize(sdata, skb, head_need, ENCRYPT_DATA)) { - ieee80211_free_txskb(&local->hw, skb); - skb = NULL; ret = -ENOMEM; - goto free; + goto free_txskb; } } @@ -3027,16 +3064,6 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, skb_reset_mac_header(skb); - info = IEEE80211_SKB_CB(skb); - memset(info, 0, sizeof(*info)); - - info->flags = info_flags; - if (info_id) { - info->status_data = info_id; - info->status_data_idr = 1; - } - info->band = band; - if (likely(!cookie)) { ctrl_flags |= u32_encode_bits(link_id, IEEE80211_TX_CTRL_MLO_LINK); @@ -3060,16 +3087,17 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, pre_conf_link_id, link_id); #endif ret = -EINVAL; - goto free; + goto free_txskb; } } info->control.flags = ctrl_flags; return skb; + free_txskb: + ieee80211_free_txskb(&local->hw, skb); + return ERR_PTR(ret); free: - if (info_id) - ieee80211_remove_ack_skb(local, info_id); kfree_skb(skb); return ERR_PTR(ret); } @@ -5089,11 +5117,19 @@ static void ieee80211_beacon_add_tim_pvb(struct ps_data *ps, */ static void ieee80211_s1g_beacon_add_tim_pvb(struct ps_data *ps, struct sk_buff *skb, - bool mcast_traffic) + bool mcast_traffic, + bool ucast_traffic) { int blk; /* + * if no unicast and multicast traffic don't emit a bitmap control + * or pvb + */ + if (!mcast_traffic && !ucast_traffic) + return; + + /* * Emit a bitmap control block with a page slice number of 31 and a * page index of 0 which indicates as per IEEE80211-2024 9.4.2.5.1 * that the entire page (2048 bits) indicated by the page index @@ -5101,6 +5137,10 @@ static void ieee80211_s1g_beacon_add_tim_pvb(struct ps_data *ps, */ skb_put_u8(skb, mcast_traffic | (31 << 1)); + /* If there's no unicast traffic we don't need to include a PVB. */ + if (!ucast_traffic) + return; + /* Emit an encoded block for each non-zero sub-block */ for (blk = 0; blk < IEEE80211_MAX_SUPPORTED_S1G_TIM_BLOCKS; blk++) { u8 blk_bmap = 0; @@ -5182,25 +5222,16 @@ static void __ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, ps->dtim_bc_mc = mcast_traffic; - if (have_bits) { - if (s1g) - ieee80211_s1g_beacon_add_tim_pvb(ps, skb, - mcast_traffic); - else - ieee80211_beacon_add_tim_pvb(ps, skb, mcast_traffic); + if (s1g) { + ieee80211_s1g_beacon_add_tim_pvb(ps, skb, mcast_traffic, + have_bits); + } else if (have_bits) { + ieee80211_beacon_add_tim_pvb(ps, skb, mcast_traffic); } else { - /* - * If there is no buffered unicast traffic for an S1G - * interface, we can exclude the bitmap control. This is in - * contrast to other phy types as they do include the bitmap - * control and pvb even when there is no buffered traffic. - */ - if (!s1g) { - /* Bitmap control */ - skb_put_u8(skb, mcast_traffic); - /* Part Virt Bitmap */ - skb_put_u8(skb, 0); - } + /* Bitmap control */ + skb_put_u8(skb, mcast_traffic); + /* Part Virt Bitmap */ + skb_put_u8(skb, 0); } tim->datalen = skb_tail_pointer(skb) - tim->data; diff --git a/net/wireless/core.c b/net/wireless/core.c index 3032993ba5dc..cde3ca85494d 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -153,68 +153,93 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, return 0; } -int cfg80211_switch_netns(struct cfg80211_registered_device *rdev, - struct net *net) +static int cfg80211_switch_wdev_netns(struct wireless_dev *wdev, + struct net *net) { - struct wireless_dev *wdev; - int err = 0; + int err; - if (!(rdev->wiphy.flags & WIPHY_FLAG_NETNS_OK)) - return -EOPNOTSUPP; + if (!wdev->netdev) + return 0; - list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { - if (!wdev->netdev) + wdev->netdev->netns_immutable = false; + err = dev_change_net_namespace(wdev->netdev, net, "wlan%d"); + wdev->netdev->netns_immutable = true; + + return err; +} + +static int __cfg80211_switch_netns(struct cfg80211_registered_device *rdev, + struct net *net, bool force) +{ + struct net *old_net = wiphy_net(&rdev->wiphy); + struct wireless_dev *wdev, *tmp; + int err = 0; + + list_for_each_entry_safe(wdev, tmp, &rdev->wiphy.wdev_list, list) { + err = cfg80211_switch_wdev_netns(wdev, net); + if (!err) continue; - wdev->netdev->netns_immutable = false; - err = dev_change_net_namespace(wdev->netdev, net, "wlan%d"); - if (err) - break; - wdev->netdev->netns_immutable = true; + if (!force) + goto undo; + /* remove interfaces that fail to allow wiphy switching */ + dev_close(wdev->netdev); + scoped_guard(wiphy, &rdev->wiphy) + cfg80211_unregister_wdev(wdev); + err = 0; } - if (err) { - /* failed -- clean up to old netns */ - net = wiphy_net(&rdev->wiphy); - - list_for_each_entry_continue_reverse(wdev, - &rdev->wiphy.wdev_list, - list) { + scoped_guard(wiphy, &rdev->wiphy) { + list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { if (!wdev->netdev) continue; - wdev->netdev->netns_immutable = false; - err = dev_change_net_namespace(wdev->netdev, net, - "wlan%d"); - WARN_ON(err); - wdev->netdev->netns_immutable = true; + nl80211_notify_iface(rdev, wdev, + NL80211_CMD_DEL_INTERFACE); } - return err; - } + nl80211_notify_wiphy(rdev, NL80211_CMD_DEL_WIPHY); - guard(wiphy)(&rdev->wiphy); + wiphy_net_set(&rdev->wiphy, net); - list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { - if (!wdev->netdev) - continue; - nl80211_notify_iface(rdev, wdev, NL80211_CMD_DEL_INTERFACE); - } + /* this only fails on allocation failure */ + err = device_rename(&rdev->wiphy.dev, + dev_name(&rdev->wiphy.dev)); + if (err && !force) + wiphy_net_set(&rdev->wiphy, old_net); - nl80211_notify_wiphy(rdev, NL80211_CMD_DEL_WIPHY); + nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY); - wiphy_net_set(&rdev->wiphy, net); + list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { + if (!wdev->netdev) + continue; + nl80211_notify_iface(rdev, wdev, + NL80211_CMD_NEW_INTERFACE); + } + } - err = device_rename(&rdev->wiphy.dev, dev_name(&rdev->wiphy.dev)); - WARN_ON(err); + if (!err || force) + return err; - nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY); + /* set to the last one to undo all of them */ + wdev = list_entry(&rdev->wiphy.wdev_list, typeof(*wdev), list); +undo: + /* + * Move back everything, if this fails again (allocation failures) + * then things get stuck in different network namespaces. + */ + list_for_each_entry_continue_reverse(wdev, &rdev->wiphy.wdev_list, + list) + WARN_ON(cfg80211_switch_wdev_netns(wdev, old_net)); - list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { - if (!wdev->netdev) - continue; - nl80211_notify_iface(rdev, wdev, NL80211_CMD_NEW_INTERFACE); - } + return err; +} - return 0; +int cfg80211_switch_netns(struct cfg80211_registered_device *rdev, + struct net *net) +{ + if (!(rdev->wiphy.flags & WIPHY_FLAG_NETNS_OK)) + return -EOPNOTSUPP; + + return __cfg80211_switch_netns(rdev, net, false); } static void cfg80211_rfkill_poll(struct rfkill *rfkill, void *data) @@ -244,9 +269,8 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev, rdev->opencount--; if (rdev->scan_req && rdev->scan_req->req.wdev == wdev) { - if (WARN_ON(!rdev->scan_req->notified && - (!rdev->int_scan_req || - !rdev->int_scan_req->notified))) + if (!rdev->scan_req->notified && + (!rdev->int_scan_req || !rdev->int_scan_req->notified)) rdev->scan_req->info.aborted = true; ___cfg80211_scan_done(rdev, false); } @@ -645,6 +669,8 @@ use_default_name: INIT_WORK(&rdev->destroy_work, cfg80211_destroy_iface_wk); wiphy_work_init(&rdev->sched_scan_stop_wk, cfg80211_sched_scan_stop_wk); INIT_WORK(&rdev->sched_scan_res_wk, cfg80211_sched_scan_results_wk); + wiphy_work_init(&rdev->reg_check_chans_wk, reg_leave_invalid_chans_wk); + INIT_WORK(&rdev->reg_leave_nan_wk, reg_leave_invalid_nan_wk); INIT_WORK(&rdev->propagate_radar_detect_wk, cfg80211_propagate_radar_detect_wk); INIT_WORK(&rdev->propagate_cac_done_wk, cfg80211_propagate_cac_done_wk); @@ -1344,6 +1370,7 @@ void wiphy_unregister(struct wiphy *wiphy) cancel_delayed_work_sync(&rdev->dfs_update_channels_wk); cancel_delayed_work_sync(&rdev->background_cac_done_wk); flush_work(&rdev->destroy_work); + flush_work(&rdev->reg_leave_nan_wk); flush_work(&rdev->propagate_radar_detect_wk); flush_work(&rdev->propagate_cac_done_wk); flush_work(&rdev->mgmt_registrations_update_wk); @@ -1757,9 +1784,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, wiphy_lock(&rdev->wiphy); cfg80211_update_iface_num(rdev, wdev->iftype, -1); if (rdev->scan_req && rdev->scan_req->req.wdev == wdev) { - if (WARN_ON(!rdev->scan_req->notified && - (!rdev->int_scan_req || - !rdev->int_scan_req->notified))) + if (!rdev->scan_req->notified && + (!rdev->int_scan_req || + !rdev->int_scan_req->notified)) rdev->scan_req->info.aborted = true; ___cfg80211_scan_done(rdev, false); } @@ -1867,7 +1894,7 @@ static void __net_exit cfg80211_pernet_exit(struct net *net) rtnl_lock(); for_each_rdev(rdev) { if (net_eq(wiphy_net(&rdev->wiphy), net)) - WARN_ON(cfg80211_switch_netns(rdev, &init_net)); + WARN_ON(__cfg80211_switch_netns(rdev, &init_net, true)); } rtnl_unlock(); } diff --git a/net/wireless/core.h b/net/wireless/core.h index b4610f6685dc..6138d207caf4 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -24,6 +24,16 @@ struct cfg80211_scan_request_int { struct cfg80211_scan_info info; bool notified; + /* + * set while the request is handed to the driver, i.e. between + * rdev_scan() and cfg80211_scan_done() + */ + bool driver_owns; + /* + * set when cfg80211 is done with the request but the driver still + * owns it, so that cfg80211_scan_done() knows to just free it + */ + bool stale; /* must be last - variable members */ struct cfg80211_scan_request req; }; @@ -104,6 +114,8 @@ struct cfg80211_registered_device { struct work_struct destroy_work; struct wiphy_work sched_scan_stop_wk; struct work_struct sched_scan_res_wk; + struct wiphy_work reg_check_chans_wk; + struct work_struct reg_leave_nan_wk; struct cfg80211_chan_def radar_chandef; struct work_struct propagate_radar_detect_wk; @@ -280,8 +292,7 @@ struct cfg80211_event { bool locally_generated; } dc; struct { - u8 bssid[ETH_ALEN]; - struct ieee80211_channel *channel; + struct cfg80211_bss *bss; } ij; struct { u8 peer_addr[ETH_ALEN]; @@ -344,8 +355,7 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, void cfg80211_clear_ibss(struct net_device *dev, bool nowext); int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, struct net_device *dev, bool nowext); -void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, - struct ieee80211_channel *channel); +void __cfg80211_ibss_joined(struct net_device *dev, struct cfg80211_bss *bss); int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev); diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index b1d748bdb504..7f6779d326b8 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -16,26 +16,18 @@ #include "rdev-ops.h" -void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, - struct ieee80211_channel *channel) +void __cfg80211_ibss_joined(struct net_device *dev, struct cfg80211_bss *bss) { struct wireless_dev *wdev = dev->ieee80211_ptr; - struct cfg80211_bss *bss; #ifdef CONFIG_CFG80211_WEXT union iwreq_data wrqu; #endif if (WARN_ON(wdev->iftype != NL80211_IFTYPE_ADHOC)) - return; + goto put_bss; if (!wdev->u.ibss.ssid_len) - return; - - bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, NULL, 0, - IEEE80211_BSS_TYPE_IBSS, IEEE80211_PRIVACY_ANY); - - if (WARN_ON(!bss)) - return; + goto put_bss; if (wdev->u.ibss.current_bss) { cfg80211_unhold_bss(wdev->u.ibss.current_bss); @@ -43,17 +35,22 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, } cfg80211_hold_bss(bss_from_pub(bss)); + /* the reference from the event is transferred to current_bss */ wdev->u.ibss.current_bss = bss_from_pub(bss); cfg80211_upload_connect_keys(wdev); - nl80211_send_ibss_bssid(wiphy_to_rdev(wdev->wiphy), dev, bssid, + nl80211_send_ibss_bssid(wiphy_to_rdev(wdev->wiphy), dev, bss->bssid, GFP_KERNEL); #ifdef CONFIG_CFG80211_WEXT memset(&wrqu, 0, sizeof(wrqu)); - memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); + memcpy(wrqu.ap_addr.sa_data, bss->bssid, ETH_ALEN); wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); #endif + return; + +put_bss: + cfg80211_put_bss(wdev->wiphy, bss); } void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, @@ -62,6 +59,7 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); struct cfg80211_event *ev; + struct cfg80211_bss *bss; unsigned long flags; trace_cfg80211_ibss_joined(dev, bssid, channel); @@ -69,13 +67,19 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, if (WARN_ON(!channel)) return; + bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, NULL, 0, + IEEE80211_BSS_TYPE_IBSS, IEEE80211_PRIVACY_ANY); + if (WARN_ON(!bss)) + return; + ev = kzalloc_obj(*ev, gfp); - if (!ev) + if (!ev) { + cfg80211_put_bss(wdev->wiphy, bss); return; + } ev->type = EVENT_IBSS_JOINED; - memcpy(ev->ij.bssid, bssid, ETH_ALEN); - ev->ij.channel = channel; + ev->ij.bss = bss; spin_lock_irqsave(&wdev->event_lock, flags); list_add_tail(&ev->list, &wdev->event_list); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 899b6374c550..9fafd7673d85 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -8966,10 +8966,12 @@ int cfg80211_check_station_change(struct wiphy *wiphy, EXPORT_SYMBOL(cfg80211_check_station_change); /* - * Get vlan interface making sure it is running and on the right wiphy. + * Get vlan interface making sure it is running, on the right wiphy + * and actually belongs to the given AP/P2P_GO interface. */ static struct net_device *get_vlan(struct genl_info *info, - struct cfg80211_registered_device *rdev) + struct cfg80211_registered_device *rdev, + struct net_device *dev) { struct nlattr *vlanattr = info->attrs[NL80211_ATTR_STA_VLAN]; struct net_device *v; @@ -8999,6 +9001,12 @@ static struct net_device *get_vlan(struct genl_info *info, goto error; } + /* Check if the VLAN interface belongs to the AP interface */ + if (!dev || !ether_addr_equal(v->dev_addr, dev->dev_addr)) { + ret = -EINVAL; + goto error; + } + return v; error: dev_put(v); @@ -9296,7 +9304,7 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) if (err) return err; - params.vlan = get_vlan(info, rdev); + params.vlan = get_vlan(info, rdev, dev); if (IS_ERR(params.vlan)) return PTR_ERR(params.vlan); @@ -9328,7 +9336,7 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; - int err; + int err, link_id; struct wireless_dev *wdev = info->user_ptr[1]; struct net_device *dev = wdev->netdev; struct station_parameters params; @@ -9376,6 +9384,16 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) params.link_sta_params.link_id = nl80211_link_id_or_invalid(info->attrs); + if (wdev->valid_links) { + if (params.link_sta_params.link_id < 0) + return -EINVAL; + if (!(wdev->valid_links & BIT(params.link_sta_params.link_id))) + return -ENOLINK; + } else { + if (params.link_sta_params.link_id >= 0) + return -EINVAL; + } + if (info->attrs[NL80211_ATTR_MLD_ADDR]) { mac_addr = nla_data(info->attrs[NL80211_ATTR_MLD_ADDR]); params.link_sta_params.mld_mac = mac_addr; @@ -9556,8 +9574,12 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) switch (wdev->iftype) { case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_P2P_GO: + /* Add a new station only after the AP and link has been started */ + link_id = wdev->valid_links ? params.link_sta_params.link_id : 0; + if (!wdev->links[link_id].ap.beacon_interval) + return -ENETDOWN; + /* ignore WME attributes if iface/sta is not capable */ if (!(rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) || !(params.sta_flags_set & BIT(NL80211_STA_FLAG_WME))) @@ -9597,11 +9619,24 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) } /* must be last in here for error handling */ - params.vlan = get_vlan(info, rdev); + params.vlan = get_vlan(info, rdev, dev); if (IS_ERR(params.vlan)) return PTR_ERR(params.vlan); break; case NL80211_IFTYPE_MESH_POINT: + /* + * Add a new station only after the mesh has been started. + * libertas doesn't implement join_mesh(); it configures the + * mesh via sysfs and joins it when the channel is set, so + * use that as the started indication instead. + */ + if (rdev->ops->libertas_set_mesh_channel) { + if (!wdev->u.mesh.chandef.chan) + return -ENETDOWN; + } else if (!wdev->u.mesh.beacon_interval) { + return -ENETDOWN; + } + /* ignore uAPSD data */ params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD; @@ -9649,27 +9684,10 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) /* be aware of params.vlan when changing code here */ - if (wdev->valid_links) { - if (params.link_sta_params.link_id < 0) { - err = -EINVAL; - goto out; - } - if (!(wdev->valid_links & BIT(params.link_sta_params.link_id))) { - err = -ENOLINK; - goto out; - } - } else { - if (params.link_sta_params.link_id >= 0) { - err = -EINVAL; - goto out; - } - } - params.epp_peer = nla_get_flag(info->attrs[NL80211_ATTR_EPP_PEER]); err = rdev_add_station(rdev, wdev, mac_addr, ¶ms); -out: dev_put(params.vlan); return err; } diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 46849fe8d0b3..adcfd0278da3 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -464,7 +464,10 @@ static inline int rdev_scan(struct cfg80211_registered_device *rdev, return -EINVAL; trace_rdev_scan(&rdev->wiphy, request); + request->driver_owns = true; ret = rdev->ops->scan(&rdev->wiphy, &request->req); + if (ret) + request->driver_owns = false; trace_rdev_return_int(&rdev->wiphy, ret); return ret; } diff --git a/net/wireless/reg.c b/net/wireless/reg.c index a8336baf85dc..11665e0a7efc 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -2345,7 +2345,7 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev) iftype = wdev->iftype; /* make sure the interface is active */ - if (!wdev->netdev || !netif_running(wdev->netdev)) + if (!wdev_running(wdev)) return true; /* NAN doesn't have links, handle it separately */ @@ -2446,19 +2446,52 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev) return true; } -static void reg_leave_invalid_chans(struct wiphy *wiphy) +void reg_leave_invalid_nan_wk(struct work_struct *work) { + struct cfg80211_registered_device *rdev; struct wireless_dev *wdev; - struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); + + rdev = container_of(work, struct cfg80211_registered_device, + reg_leave_nan_wk); + + /* stopping NAN closes its data interfaces, which needs the RTNL */ + rtnl_lock(); list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { bool valid; - scoped_guard(wiphy, wiphy) - valid = reg_wdev_chan_valid(wiphy, wdev); + if (wdev->iftype != NL80211_IFTYPE_NAN) + continue; + + scoped_guard(wiphy, &rdev->wiphy) + valid = reg_wdev_chan_valid(&rdev->wiphy, wdev); if (!valid) cfg80211_leave(rdev, wdev, -1); } + + rtnl_unlock(); +} + +void reg_leave_invalid_chans_wk(struct wiphy *wiphy, struct wiphy_work *work) +{ + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); + struct wireless_dev *wdev; + + lockdep_assert_held(&wiphy->mtx); + + list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { + if (reg_wdev_chan_valid(wiphy, wdev)) + continue; + + /* + * Tearing down NAN needs the RTNL for closing NAN_DATA + * interfaces, handle that separately. + */ + if (wdev->iftype == NL80211_IFTYPE_NAN) + schedule_work(&rdev->reg_leave_nan_wk); + else + cfg80211_leave_locked(rdev, wdev, -1); + } } static void reg_check_chans_work(struct work_struct *work) @@ -2466,12 +2499,13 @@ static void reg_check_chans_work(struct work_struct *work) struct cfg80211_registered_device *rdev; pr_debug("Verifying active interfaces after reg change\n"); - rtnl_lock(); - for_each_rdev(rdev) - reg_leave_invalid_chans(&rdev->wiphy); + rcu_read_lock(); - rtnl_unlock(); + list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) + wiphy_work_queue(&rdev->wiphy, &rdev->reg_check_chans_wk); + + rcu_read_unlock(); } void reg_check_channels(void) diff --git a/net/wireless/reg.h b/net/wireless/reg.h index fc31c5f9a61a..c587079ead8f 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -178,6 +178,22 @@ int reg_reload_regdb(void); */ void reg_check_channels(void); +/** + * reg_leave_invalid_chans_wk - check if channels are no longer usable and leave + * @wiphy: the wiphy to check + * @work: the work struct + */ +void reg_leave_invalid_chans_wk(struct wiphy *wiphy, struct wiphy_work *work); + +/** + * reg_leave_invalid_nan_wk - check channels and tear down NAN when unusable + * @work: the work struct + * + * Stopping a NAN interface needs the RTNL, so it cannot be done from + * reg_leave_invalid_chans_wk() which runs with the wiphy mutex held. + */ +void reg_leave_invalid_nan_wk(struct work_struct *work); + extern const u8 shipped_regdb_certs[]; extern unsigned int shipped_regdb_certs_len; extern const u8 extra_regdb_certs[]; diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 9e934b185e34..caa9c6495f20 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -1114,6 +1114,21 @@ int cfg80211_scan(struct cfg80211_registered_device *rdev) return 0; } +/* + * Release the scan request, but free it only if the driver is also done, + * e.g. mac80211 may cancel it asynchronously and still use it. + */ +static void cfg80211_put_scan_req(struct cfg80211_scan_request_int *req) +{ + if (!req) + return; + + if (req->driver_owns) + req->stale = true; + else + kfree(req); +} + void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool send_message) { @@ -1173,10 +1188,10 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, dev_put(wdev->netdev); - kfree(rdev->int_scan_req); + cfg80211_put_scan_req(rdev->int_scan_req); rdev->int_scan_req = NULL; - kfree(rdev->scan_req); + cfg80211_put_scan_req(rdev->scan_req); rdev->scan_req = NULL; if (!send_message) @@ -1199,6 +1214,18 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request, struct cfg80211_scan_info old_info = intreq->info; trace_cfg80211_scan_done(intreq, info); + + intreq->driver_owns = false; + + if (intreq->stale) { + /* + * The scan is already completed as far as we're concerned, + * it was just kept around for the driver - done now, free it. + */ + kfree(intreq); + return; + } + WARN_ON(intreq != rdev->scan_req && intreq != rdev->int_scan_req); @@ -2050,6 +2077,13 @@ __cfg80211_bss_update(struct cfg80211_registered_device *rdev, if (!hidden) hidden = rb_find_bss(rdev, tmp, BSS_CMP_HIDE_NUL); + /* + * Only group with an entry with beacon data, otherwise + * beacon data can never be filled/updated. + */ + if (hidden && + !rcu_access_pointer(hidden->pub.beacon_ies)) + hidden = NULL; if (hidden) { new->pub.hidden_beacon_bss = &hidden->pub; list_add(&new->hidden_list, @@ -3468,11 +3502,6 @@ void cfg80211_update_assoc_bss_entry(struct wireless_dev *wdev, cbss->pub.channel = chan; list_for_each_entry(bss, &rdev->bss_list, list) { - if (!cfg80211_bss_type_match(bss->pub.capability, - bss->pub.channel->band, - wdev->conn_bss_type)) - continue; - if (bss == cbss) continue; diff --git a/net/wireless/util.c b/net/wireless/util.c index 3e584d0ca3e2..f2464d2ce0d5 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -1039,12 +1039,30 @@ unsigned int cfg80211_classify8021d(struct sk_buff *skb, } switch (skb->protocol) { - case htons(ETH_P_IP): - dscp = ipv4_get_dsfield(ip_hdr(skb)) & 0xfc; + case htons(ETH_P_IP): { + const struct iphdr *iph; + struct iphdr _iph; + + iph = skb_header_pointer(skb, sizeof(struct ethhdr), + sizeof(*iph), &_iph); + if (!iph) + return 0; + + dscp = ipv4_get_dsfield(iph) & 0xfc; break; - case htons(ETH_P_IPV6): - dscp = ipv6_get_dsfield(ipv6_hdr(skb)) & 0xfc; + } + case htons(ETH_P_IPV6): { + const struct ipv6hdr *ip6h; + struct ipv6hdr _ip6h; + + ip6h = skb_header_pointer(skb, sizeof(struct ethhdr), + sizeof(*ip6h), &_ip6h); + if (!ip6h) + return 0; + + dscp = ipv6_get_dsfield(ip6h) & 0xfc; break; + } case htons(ETH_P_MPLS_UC): case htons(ETH_P_MPLS_MC): { struct mpls_label mpls_tmp, *mpls; @@ -1217,8 +1235,7 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev) !ev->dc.locally_generated); break; case EVENT_IBSS_JOINED: - __cfg80211_ibss_joined(wdev->netdev, ev->ij.bssid, - ev->ij.channel); + __cfg80211_ibss_joined(wdev->netdev, ev->ij.bss); break; case EVENT_STOPPED: /* @@ -2477,16 +2494,15 @@ static void cfg80211_calculate_bi_data(struct wiphy *wiphy, u32 new_beacon_int, if (wdev->valid_links) continue; + wdev_bi = cfg80211_wdev_bi(wdev); + if (!wdev_bi) + continue; + /* skip wdevs not active on the given wiphy radio */ if (radio_idx >= 0 && !(rdev_get_radio_mask(rdev, wdev->netdev) & BIT(radio_idx))) continue; - wdev_bi = cfg80211_wdev_bi(wdev); - - if (!wdev_bi) - continue; - if (!*beacon_int_gcd) { *beacon_int_gcd = wdev_bi; continue; |
