summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorJakub Kicinski <kuba@kernel.org>2026-09-16 15:54:55 -0700
committerJakub Kicinski <kuba@kernel.org>2026-09-16 15:54:56 -0700
commitfefaac1176bf3cf002a8dc83339d6ed6a369941a (patch)
treeab8893c9e199bb9f981ab9fbc8840a3c5a6d28b5 /net
parent7c7d5e9d7e3942ba5aec9847f61a6e76d7773191 (diff)
parent1eeca1d5e0920fbdad6449768fd2d4364e714180 (diff)
downloadlinux-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.c18
-rw-r--r--net/mac80211/debugfs.c2
-rw-r--r--net/mac80211/debugfs_netdev.c9
-rw-r--r--net/mac80211/ieee80211_i.h2
-rw-r--r--net/mac80211/iface.c39
-rw-r--r--net/mac80211/main.c4
-rw-r--r--net/mac80211/mesh.c34
-rw-r--r--net/mac80211/offchannel.c7
-rw-r--r--net/mac80211/pm.c8
-rw-r--r--net/mac80211/rate.c8
-rw-r--r--net/mac80211/scan.c2
-rw-r--r--net/mac80211/tdls.c40
-rw-r--r--net/mac80211/tx.c115
-rw-r--r--net/wireless/core.c129
-rw-r--r--net/wireless/core.h18
-rw-r--r--net/wireless/ibss.c36
-rw-r--r--net/wireless/nl80211.c64
-rw-r--r--net/wireless/rdev-ops.h3
-rw-r--r--net/wireless/reg.c52
-rw-r--r--net/wireless/reg.h16
-rw-r--r--net/wireless/scan.c43
-rw-r--r--net/wireless/util.c38
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, &params);
-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;