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
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEpeA8sTs3M8SN2hR410qiO8sPaAAFAmqqVJYACgkQ10qiO8sP
 aACbjQ//VK16MorAA6e+zC44Cq68erE/0kCXjkwHjNPFrg8P2gD3qVHtPMUi5GLG
 ldWD5Hm3SlmGlpXIljJPQpTaMHGwVG+1j+n4TgwonomXarsaftSriSPKMww3/O2c
 kk+LAujowo0OImPNxY4noWRVcEcSo/tsTvbYDcAKjc91yHs/gU6TSeT3E/YFAfbt
 HSNbMImaSdTBM6TnvwepGZG2RisKWMgiYhpPFzO26TvoYnXBCgN48kOVz+x/6M0P
 o7i3coZiRO1o7ec9SsxgKUZUDNVMNoiEDWPQBJpaq2vOV1eFCrwuJr4/KKYk738F
 zSGsIK8z1R6j7RRB2qk3qYRrrwuNS1FZWeo4S0iGWvLwqbL7nsrrvkFc5O9uFure
 RV/Uf2okycaIZICe1rSalTDtjWgp6beRQSj3Ep75MSa/iqj6Rtggo9CxM7+aPkoa
 z0Q9MKqmuY0yXZCtI0EOayXcOpdoHWot95NJQ5nkR0ge5WJ2tu+myzepesQ9a5Vb
 JEOw8N4gJ9Hdst94gDqiGzDQb0xRYeMSgET3we9gA9KMu3NXBrbRLhRbkk+16fdA
 DoqbwD6wrH6nXcgihLBEILRLQWHe8pC7PoI2LvuTfzP3xiD50NvRVfdwNDuWLg/+
 2uP1Yl4x67vXNUqa619taRwtwFuJn+M31uwZ57eMw3U1edNlfug=
 =/zWX
 -----END PGP SIGNATURE-----

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>
This commit is contained in:
Jakub Kicinski 2026-09-16 15:54:55 -07:00
commit fefaac1176
48 changed files with 694 additions and 326 deletions

View File

@ -4,6 +4,8 @@ Alyssa Rosenzweig <alyssa@rosenzweig.io>
Askar Safin <safinaskar@gmail.com>
Christoph Hellwig <hch@lst.de>
Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Johannes Berg <johannes.berg@intel.com>
Johannes Berg <johannes@sipsolutions.net>
Marc Gonzalez <marc.w.gonzalez@free.fr>
Nathan Chancellor <nathan@kernel.org>
Ralf Baechle <ralf@linux-mips.org>

View File

@ -873,6 +873,22 @@ static int ath11k_mac_set_kickout(struct ath11k_vif *arvif)
return 0;
}
static void ath11k_mac_station_cleanup(struct ieee80211_sta *sta)
{
struct ath11k_sta *arsta;
if (!sta)
return;
arsta = ath11k_sta_to_arsta(sta);
kfree(arsta->tx_stats);
arsta->tx_stats = NULL;
kfree(arsta->rx_stats);
arsta->rx_stats = NULL;
}
void ath11k_mac_peer_cleanup_all(struct ath11k *ar)
{
struct ath11k_peer *peer, *tmp;
@ -885,6 +901,7 @@ void ath11k_mac_peer_cleanup_all(struct ath11k *ar)
list_for_each_entry_safe(peer, tmp, &ab->peers, list) {
ath11k_peer_rx_tid_cleanup(ar, peer);
ath11k_peer_rhash_delete(ab, peer);
ath11k_mac_station_cleanup(peer->sta);
list_del(&peer->list);
kfree(peer);
}
@ -9892,7 +9909,6 @@ static int ath11k_mac_station_remove(struct ath11k *ar,
{
struct ath11k_base *ab = ar->ab;
struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif);
struct ath11k_sta *arsta = ath11k_sta_to_arsta(sta);
int ret;
if (ab->hw_params.vdev_start_delay &&
@ -9916,12 +9932,7 @@ static int ath11k_mac_station_remove(struct ath11k *ar,
sta->addr, arvif->vdev_id);
ath11k_mac_dec_num_stations(arvif, sta);
kfree(arsta->tx_stats);
arsta->tx_stats = NULL;
kfree(arsta->rx_stats);
arsta->rx_stats = NULL;
ath11k_mac_station_cleanup(sta);
return ret;
}

View File

@ -15,21 +15,6 @@
#include "dp.h"
#include "core.h"
/*
* Node name to UserPD ID mapping
*
* The io_start field is used for additional validation when the reg
* property is present in the device tree. If io_start is 0, only
* node_name matching is performed.
*
* For platforms where not all WiFi nodes have a 'reg' property, set
* io_start to 0 for those entries. The driver will match purely by
* node name in such cases.
*/
static const struct ath12k_ahb_userpd_map ath12k_wifi7_ahb_userpd_map[] = {
{ .io_start = 0x0c000000, .node_name = "wifi", .upd_id = ATH12K_AHB_USERPD_ID_0 },
};
static const struct ath12k_ahb_desc ath12k_wifi7_ahb_desc[] = {
[ATH12K_HW_IPQ5332_HW10] = {
.hw_rev = ATH12K_HW_IPQ5332_HW10,
@ -55,40 +40,6 @@ static const struct of_device_id ath12k_wifi7_ahb_of_match[] = {
MODULE_DEVICE_TABLE(of, ath12k_wifi7_ahb_of_match);
/*
* ath12k_wifi7_ahb_get_userpd_id - Resolve UserPD ID from DT properties
* @ab: ath12k base structure
*
* Returns: UserPD ID (1-based) on success, 0 on failure
*
* Resolution logic:
* 1. If reg property exist in DT, get userpd_id from io_start
* 2. If reg property is absent, get userpd_id from DT node name
* 3. Return 0 if no match found (probe will fail)
*/
static u32 ath12k_wifi7_ahb_get_userpd_id(struct ath12k_base *ab)
{
const struct ath12k_ahb_userpd_map *map;
struct resource *res;
size_t i;
res = platform_get_resource(ab->pdev, IORESOURCE_MEM, 0);
for (i = 0; i < ARRAY_SIZE(ath12k_wifi7_ahb_userpd_map); i++) {
map = &ath12k_wifi7_ahb_userpd_map[i];
if (res) {
if (map->io_start && map->io_start == res->start)
return map->upd_id;
} else if (map->node_name &&
of_node_name_eq(ab->dev->of_node, map->node_name)) {
return map->upd_id;
}
}
return 0;
}
static int ath12k_wifi7_ahb_probe(struct platform_device *pdev)
{
const struct ath12k_ahb_desc *desc;
@ -106,7 +57,7 @@ static int ath12k_wifi7_ahb_probe(struct platform_device *pdev)
ab->hw_rev = desc->hw_rev;
ab->hif.ops = desc->ops;
ab_ahb->scm_auth_enabled = desc->auth_enabled;
ab_ahb->userpd_id = ath12k_wifi7_ahb_get_userpd_id(ab);
ab_ahb->userpd_id = ATH12K_AHB_USERPD_ID_0;
if (!ab_ahb->userpd_id)
return -EOPNOTSUPP;

View File

@ -1055,7 +1055,7 @@ void wcn36xx_dxe_deinit(struct wcn36xx *wcn)
free_irq(wcn->tx_irq, wcn);
free_irq(wcn->rx_irq, wcn);
timer_delete(&wcn->tx_ack_timer);
timer_shutdown_sync(&wcn->tx_ack_timer);
if (wcn->tx_ack_skb) {
ieee80211_tx_status_irqsafe(wcn->hw, wcn->tx_ack_skb);

View File

@ -555,6 +555,8 @@ void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success)
if (type == ETH_P_PAE) {
atomic_dec(&ifp->pend_8021x_cnt);
/* Order the decrement before waitqueue_active() */
smp_mb__after_atomic();
if (waitqueue_active(&ifp->pend_8021x_wait))
wake_up(&ifp->pend_8021x_wait);
}

View File

@ -198,7 +198,7 @@ brcmf_cyw_external_auth(struct wiphy *wiphy, struct net_device *dev,
{
struct brcmf_if *ifp;
struct brcmf_pub *drvr;
struct brcmf_auth_req_status_le auth_status;
struct brcmf_auth_req_status_le auth_status = {};
int ret = 0;
brcmf_dbg(TRACE, "Enter\n");
@ -206,6 +206,9 @@ brcmf_cyw_external_auth(struct wiphy *wiphy, struct net_device *dev,
ifp = netdev_priv(dev);
drvr = ifp->drvr;
if (params->status == WLAN_STATUS_SUCCESS) {
if (params->pmkid)
memcpy(auth_status.pmkid, params->pmkid,
WLAN_PMKID_LEN);
auth_status.flags = cpu_to_le16(BRCMF_EXTAUTH_SUCCESS);
} else {
bphy_err(drvr, "External authentication failed: status=%d\n",

View File

@ -1571,6 +1571,10 @@ void brcms_free_timer(struct brcms_timer *t)
/* delete the timer in case it is active */
brcms_del_timer(t);
/* Ensure the callback has finished before freeing the timer
* structure, since brcms_del_timer() uses non-synchronous cancel.
*/
cancel_delayed_work_sync(&t->dly_wrk);
if (wl->timers == t) {
wl->timers = wl->timers->next;

View File

@ -2712,7 +2712,9 @@ static void __ipw2100_rx_process(struct ipw2100_priv *priv)
break;
}
#endif
if (stats.len < sizeof(struct libipw_hdr_3addr))
if (sq->drv[i].frame_size <
sizeof(struct libipw_hdr_3addr) ||
sq->drv[i].frame_size > IPW_RX_NIC_BUFFER_LENGTH)
break;
switch (WLAN_FC_GET_TYPE(le16_to_cpu(u->rx_data.header.frame_ctl))) {
case IEEE80211_FTYPE_MGMT:

View File

@ -8322,6 +8322,15 @@ static void ipw_rx(struct ipw_priv *priv)
break;
}
if (unlikely(le16_to_cpu(pkt->u.frame.length) >
IPW_RX_BUF_SIZE -
IPW_RX_FRAME_SIZE)) {
IPW_DEBUG_DROP("Received oversized packet. Dropping.\n");
priv->net_dev->stats.rx_errors++;
priv->wstats.discard.misc++;
break;
}
switch (WLAN_FC_GET_TYPE
(le16_to_cpu(header->frame_ctl))) {

View File

@ -474,14 +474,16 @@ static int libipw_michael_mic_verify(struct sk_buff *skb, int keyidx,
int hdr_len, void *priv)
{
struct libipw_tkip_data *tkey = priv;
u8 mic[8];
u8 mic[MICHAEL_MIC_LEN];
if (!tkey->key_set)
if (!tkey->key_set || skb->len < hdr_len + MICHAEL_MIC_LEN)
return -1;
michael_mic(&tkey->key[24], (struct ieee80211_hdr *)skb->data,
skb->data + hdr_len, skb->len - 8 - hdr_len, mic);
if (memcmp(mic, skb->data + skb->len - 8, 8) != 0) {
skb->data + hdr_len,
skb->len - MICHAEL_MIC_LEN - hdr_len, mic);
if (memcmp(mic, skb->data + skb->len - MICHAEL_MIC_LEN,
MICHAEL_MIC_LEN) != 0) {
struct ieee80211_hdr *hdr;
hdr = (struct ieee80211_hdr *)skb->data;
printk(KERN_DEBUG "%s: Michael MIC verification failed for "
@ -499,7 +501,7 @@ static int libipw_michael_mic_verify(struct sk_buff *skb, int keyidx,
tkey->rx_iv32 = tkey->rx_iv32_new;
tkey->rx_iv16 = tkey->rx_iv16_new;
skb_trim(skb, skb->len - 8);
skb_trim(skb, skb->len - MICHAEL_MIC_LEN);
return 0;
}

View File

@ -1209,6 +1209,9 @@ static int libipw_handle_assoc_resp(struct libipw_device *ieee, struct libipw_as
struct libipw_network *network = &network_resp;
struct net_device *dev = ieee->dev;
if (stats->len < sizeof(*frame))
return 1;
network->flags = 0;
network->qos_data.active = 0;
network->qos_data.supported = 0;
@ -1421,6 +1424,9 @@ static void libipw_process_probe_response(struct libipw_device
#endif
unsigned long flags;
if (stats->len < sizeof(*beacon))
return;
LIBIPW_DEBUG_SCAN("'%*pE' (%pM): %c%c%c%c %c%c%c%c-%c%c%c%c %c%c%c%c\n",
info_element->len, info_element->data,
beacon->header.addr3,

View File

@ -2326,7 +2326,7 @@ il_dealloc_bcast_stations(struct il_priv *il)
if (!(il->stations[i].used & IL_STA_BCAST))
continue;
il->stations[i].used &= ~IL_STA_UCODE_ACTIVE;
il->stations[i].used = 0;
il->num_stations--;
if (WARN_ON(il->num_stations < 0))
il->num_stations = 0;

View File

@ -414,17 +414,22 @@ static int p54_generate_channel_lists(struct ieee80211_hw *dev)
}
static int p54_convert_rev0(struct ieee80211_hw *dev,
struct pda_pa_curve_data *curve_data)
struct pda_pa_curve_data *curve_data, size_t len)
{
struct p54_common *priv = dev->priv;
struct p54_pa_curve_data_sample *dst;
struct pda_pa_curve_data_sample_rev0 *src;
size_t needed = curve_data->channels *
(sizeof(*src) * curve_data->points_per_channel + 2);
size_t cd_len = sizeof(*curve_data) +
(curve_data->points_per_channel*sizeof(*dst) + 2) *
curve_data->channels;
unsigned int i, j;
void *source, *target;
if (len < sizeof(*curve_data) + needed)
return -EINVAL;
priv->curve_data = kmalloc(sizeof(*priv->curve_data) + cd_len,
GFP_KERNEL);
if (!priv->curve_data)
@ -466,17 +471,22 @@ static int p54_convert_rev0(struct ieee80211_hw *dev,
}
static int p54_convert_rev1(struct ieee80211_hw *dev,
struct pda_pa_curve_data *curve_data)
struct pda_pa_curve_data *curve_data, size_t len)
{
struct p54_common *priv = dev->priv;
struct p54_pa_curve_data_sample *dst;
struct pda_pa_curve_data_sample_rev1 *src;
size_t needed = curve_data->channels *
(sizeof(*src) * curve_data->points_per_channel + 3);
size_t cd_len = sizeof(*curve_data) +
(curve_data->points_per_channel*sizeof(*dst) + 2) *
curve_data->channels;
unsigned int i, j;
void *source, *target;
if (len < sizeof(*curve_data) + needed)
return -EINVAL;
priv->curve_data = kzalloc(cd_len + sizeof(*priv->curve_data),
GFP_KERNEL);
if (!priv->curve_data)
@ -763,6 +773,7 @@ int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
case PDR_PRISM_PA_CAL_CURVE_DATA: {
struct pda_pa_curve_data *curve_data =
(struct pda_pa_curve_data *)entry->data;
if (data_len < sizeof(*curve_data)) {
err = -EINVAL;
goto err;
@ -770,10 +781,10 @@ int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
switch (curve_data->cal_method_rev) {
case 0:
err = p54_convert_rev0(dev, curve_data);
err = p54_convert_rev0(dev, curve_data, data_len);
break;
case 1:
err = p54_convert_rev1(dev, curve_data);
err = p54_convert_rev1(dev, curve_data, data_len);
break;
default:
wiphy_err(dev->wiphy,
@ -801,7 +812,8 @@ int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len)
break;
case PDR_INTERFACE_LIST:
tmp = entry->data;
while ((u8 *)tmp < entry->data + data_len) {
while ((u8 *)tmp + sizeof(struct exp_if) <=
entry->data + data_len) {
struct exp_if *exp_if = tmp;
if (exp_if->if_id == cpu_to_le16(IF_ID_ISL39000))
synth = le16_to_cpu(exp_if->variant);

View File

@ -173,8 +173,8 @@ static int lbtf_init_adapter(struct lbtf_private *priv)
static void lbtf_free_adapter(struct lbtf_private *priv)
{
lbtf_deb_enter(LBTF_DEB_MAIN);
lbtf_free_cmd_buffer(priv);
timer_delete_sync(&priv->command_timer);
lbtf_free_cmd_buffer(priv);
lbtf_deb_leave(LBTF_DEB_MAIN);
}

View File

@ -4277,6 +4277,7 @@ mwifiex_cfg80211_authenticate(struct wiphy *wiphy,
struct mwifiex_adapter *adapter = priv->adapter;
struct sk_buff *skb;
u16 pkt_len, auth_alg;
size_t frame_len;
int ret;
struct mwifiex_ieee80211_mgmt *mgmt;
struct mwifiex_txinfo *tx_info;
@ -4349,10 +4350,17 @@ mwifiex_cfg80211_authenticate(struct wiphy *wiphy,
mwifiex_cancel_scan(adapter);
pkt_len = (u16)req->ie_len + req->auth_data_len +
frame_len = req->ie_len + req->auth_data_len +
MWIFIEX_MGMT_HEADER_LEN + MWIFIEX_AUTH_BODY_LEN;
if (req->auth_data_len >= 4)
pkt_len -= 4;
frame_len -= 4;
if (frame_len > U16_MAX) {
mwifiex_dbg(priv->adapter, ERROR,
"auth frame too long: %zu bytes\n", frame_len);
return -EINVAL;
}
pkt_len = frame_len;
skb = dev_alloc_skb(MWIFIEX_MIN_DATA_HEADER_LEN +
MWIFIEX_MGMT_FRAME_HEADER_SIZE +

View File

@ -3068,7 +3068,7 @@ static int mwifiex_pcie_request_irq(struct mwifiex_adapter *adapter)
ret);
for (j = 0; j < i; j++)
free_irq(card->msix_entries[j].vector,
&card->msix_ctx[i]);
&card->msix_ctx[j]);
pci_disable_msix(pdev);
} else {
mwifiex_dbg(adapter, MSG, "MSIx enabled!");

View File

@ -104,12 +104,24 @@ has_vendor_hdr(struct ieee_types_vendor_specific *ie, u8 key)
* a given oui in PTK.
*/
static u8
mwifiex_search_oui_in_ie(struct ie_body *iebody, u8 *oui)
mwifiex_search_oui_in_ie(struct ie_body *iebody, u8 *oui, int ie_len)
{
const size_t ptk_body_offset = offsetof(struct ie_body, ptk_body);
u8 count;
/* ie_len is the number of bytes available at iebody. Keep it signed
* and reject a negative (underflowed) length before the unsigned
* comparisons below, so a small or zero IE length cannot wrap.
*/
if (ie_len < 0 || (size_t)ie_len < ptk_body_offset)
return MWIFIEX_OUI_NOT_PRESENT;
count = iebody->ptk_cnt[0];
/* Reject an OUI count whose list would run past the element. */
if (ptk_body_offset + count * sizeof(iebody->ptk_body) > (size_t)ie_len)
return MWIFIEX_OUI_NOT_PRESENT;
/* There could be multiple OUIs for PTK hence
1) Take the length.
2) Check all the OUIs for AES.
@ -143,11 +155,14 @@ mwifiex_is_rsn_oui_present(struct mwifiex_bssdescriptor *bss_desc, u32 cipher)
u8 ret = MWIFIEX_OUI_NOT_PRESENT;
if (has_ieee_hdr(bss_desc->bcn_rsn_ie, WLAN_EID_RSN)) {
int ie_len = (int)bss_desc->bcn_rsn_ie->ieee_hdr.len -
RSN_GTK_OUI_OFFSET;
iebody = (struct ie_body *)
(((u8 *) bss_desc->bcn_rsn_ie->data) +
RSN_GTK_OUI_OFFSET);
oui = &mwifiex_rsn_oui[cipher][0];
ret = mwifiex_search_oui_in_ie(iebody, oui);
ret = mwifiex_search_oui_in_ie(iebody, oui, ie_len);
if (ret)
return ret;
}
@ -169,10 +184,14 @@ mwifiex_is_wpa_oui_present(struct mwifiex_bssdescriptor *bss_desc, u32 cipher)
u8 ret = MWIFIEX_OUI_NOT_PRESENT;
if (has_vendor_hdr(bss_desc->bcn_wpa_ie, WLAN_EID_VENDOR_SPECIFIC)) {
int ie_len = (int)bss_desc->bcn_wpa_ie->vend_hdr.len -
(int)sizeof(bss_desc->bcn_wpa_ie->vend_hdr.oui) -
WPA_GTK_OUI_OFFSET;
iebody = (struct ie_body *)((u8 *)bss_desc->bcn_wpa_ie->data +
WPA_GTK_OUI_OFFSET);
oui = &mwifiex_wpa_oui[cipher][0];
ret = mwifiex_search_oui_in_ie(iebody, oui);
ret = mwifiex_search_oui_in_ie(iebody, oui, ie_len);
if (ret)
return ret;
}
@ -2096,6 +2115,7 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv,
u32 bytes_left;
u32 idx;
u32 tlv_buf_size;
size_t fixed_size;
struct mwifiex_ie_types_chan_band_list_param_set *chan_band_tlv;
struct chan_band_param_set *chan_band;
u8 is_bgscan_resp;
@ -2111,6 +2131,14 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv,
else
scan_rsp = &resp->params.scan_resp;
scan_resp_size = le16_to_cpu(resp->size);
fixed_size = scan_rsp->bss_desc_and_tlv_buffer - (u8 *)resp;
if (scan_resp_size < fixed_size) {
mwifiex_dbg(adapter, ERROR,
"SCAN_RESP: response is too short\n");
ret = -1;
goto check_next_scan;
}
if (scan_rsp->number_of_sets > MWIFIEX_MAX_AP) {
mwifiex_dbg(adapter, ERROR,
@ -2128,8 +2156,6 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv,
"info: SCAN_RESP: bss_descript_size %d\n",
bytes_left);
scan_resp_size = le16_to_cpu(resp->size);
mwifiex_dbg(adapter, INFO,
"info: SCAN_RESP: returned %d APs before parsing\n",
scan_rsp->number_of_sets);
@ -2137,15 +2163,17 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv,
bss_info = scan_rsp->bss_desc_and_tlv_buffer;
/*
* The size of the TLV buffer is equal to the entire command response
* size (scan_resp_size) minus the fixed fields (sizeof()'s), the
* BSS Descriptions (bss_descript_size as bytesLef) and the command
* response header (S_DS_GEN)
* The TLV buffer follows the command-specific fixed fields and the BSS
* descriptions. Background-scan responses have an additional fixed
* field before scan_rsp, which is included in fixed_size.
*/
tlv_buf_size = scan_resp_size - (bytes_left
+ sizeof(scan_rsp->bss_descript_size)
+ sizeof(scan_rsp->number_of_sets)
+ S_DS_GEN);
if (bytes_left > scan_resp_size - fixed_size) {
mwifiex_dbg(adapter, ERROR,
"SCAN_RESP: BSS data exceeds response\n");
ret = -1;
goto check_next_scan;
}
tlv_buf_size = scan_resp_size - fixed_size - bytes_left;
tlv_data = (struct mwifiex_ie_types_data *) (scan_rsp->
bss_desc_and_tlv_buffer +

View File

@ -317,10 +317,16 @@ mwifiex_parse_mgmt_packet(struct mwifiex_private *priv, u8 *payload, u16 len,
switch (stype) {
case IEEE80211_STYPE_ACTION:
category = *(payload + sizeof(struct ieee80211_hdr));
if (len < sizeof(*ieee_hdr) + 1)
return -1;
category = *(payload + sizeof(*ieee_hdr));
switch (category) {
case WLAN_CATEGORY_PUBLIC:
action_code = *(payload + sizeof(struct ieee80211_hdr)
if (len < sizeof(*ieee_hdr) + 2)
return -1;
action_code = *(payload + sizeof(*ieee_hdr)
+ 1);
if (action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) {
addr2 = ieee_hdr->addr2;

View File

@ -1058,6 +1058,13 @@ void wilc_wfi_p2p_rx(struct wilc_vif *vif, u8 *buff, u32 size)
if (!ieee80211_is_public_action((struct ieee80211_hdr *)buff, size))
goto out_rx_mgmt;
/* ieee80211_is_public_action() only validates up to the category
* byte, so reject frames too short for the P2P public action header
* before dereferencing it or computing size - ie_offset.
*/
if (size < ie_offset)
goto out_rx_mgmt;
d = (struct wilc_p2p_pub_act_frame *)(&mgmt->u.action);
if (d->oui_subtype != GO_NEG_REQ && d->oui_subtype != GO_NEG_RSP &&
d->oui_subtype != P2P_INV_REQ && d->oui_subtype != P2P_INV_RSP)
@ -1200,6 +1207,13 @@ static int mgmt_tx(struct wiphy *wiphy,
goto out_set_timeout;
}
/* ieee80211_is_public_action() only validates up to the category
* byte, so reject frames too short for the P2P public action header
* before dereferencing it or computing len - ie_offset.
*/
if (len < ie_offset)
goto out_set_timeout;
d = (struct wilc_p2p_pub_act_frame *)(&mgmt->u.action);
if (d->oui_type != WLAN_OUI_TYPE_WFA_P2P ||
d->oui_subtype != GO_NEG_CONF) {

View File

@ -1197,6 +1197,15 @@ static void wilc_wlan_handle_isr_ext(struct wilc *wilc, u32 int_status)
if (size <= 0)
return;
/* A size exceeding the RX buffer is bogus; drop the transfer
* instead of overflowing the buffer.
*/
if (size > WILC_RX_BUFF_SIZE) {
wilc->hif_func->hif_clear_int_ext(wilc,
DATA_INT_CLR | ENABLE_RX_VMM);
return;
}
if (WILC_RX_BUFF_SIZE - offset < size)
offset = 0;

View File

@ -852,8 +852,6 @@ int rsi_hal_load_key(struct rsi_common *common,
memcpy(set_key->tx_mic_key, &data[16], 8);
memcpy(set_key->rx_mic_key, &data[24], 8);
}
} else {
memset(&set_key[FRAME_DESC_SZ], 0, frame_len - FRAME_DESC_SZ);
}
skb_put(skb, frame_len);

View File

@ -3724,10 +3724,8 @@ void wlcore_regdomain_config(struct wl1271 *wl)
goto out;
ret = wlcore_cmd_regdomain_config_locked(wl);
if (ret < 0) {
if (ret < 0)
wl12xx_queue_recovery_work(wl);
goto out;
}
pm_runtime_put_autosuspend(wl->dev);
out:

View File

@ -2327,7 +2327,12 @@ static void mac80211_hwsim_stop(struct ieee80211_hw *hw, bool suspend)
struct sk_buff *skb;
int i;
data->started = false;
/*
* Serialise against wmediumd userspace, so no more frames
* can be handed to mac80211 after this returns.
*/
scoped_guard(mutex, &data->mutex)
data->started = false;
for (i = 0; i < ARRAY_SIZE(data->link_data); i++)
hrtimer_cancel(&data->link_data[i].beacon_timer);
@ -6505,12 +6510,12 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
if (frame_data_len < sizeof(struct ieee80211_hdr_3addr) ||
frame_data_len > IEEE80211_MAX_DATA_LEN)
goto err;
goto out;
/* Allocate new skb here */
skb = alloc_skb(frame_data_len, GFP_KERNEL);
if (skb == NULL)
goto err;
goto out;
/* Copy the data */
skb_put_data(skb, frame_data, frame_data_len);
@ -6535,10 +6540,17 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
goto out;
}
/*
* Serialise against mac80211_hwsim_stop() - mac80211 doesn't allow
* frames reported while the HW is down, hence the ->started check
* must be under mutex.
*/
mutex_lock(&data2->mutex);
/* check if radio is configured properly */
if ((data2->idle && !data2->tmp_chan) || !data2->started)
goto out;
goto out_unlock;
/* A frame is received from user space */
memset(&rx_status, 0, sizeof(rx_status));
@ -6557,22 +6569,18 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
iter_data.channel = ieee80211_get_channel(data2->hw->wiphy,
rx_status.freq);
if (!iter_data.channel)
goto out;
goto out_unlock;
rx_status.band = iter_data.channel->band;
mutex_lock(&data2->mutex);
if (!hwsim_chans_compat(iter_data.channel, channel)) {
ieee80211_iterate_active_interfaces_atomic(
data2->hw, IEEE80211_IFACE_ITER_NORMAL,
mac80211_hwsim_tx_iter, &iter_data);
if (!iter_data.receive) {
mutex_unlock(&data2->mutex);
goto out;
}
if (!iter_data.receive)
goto out_unlock;
}
mutex_unlock(&data2->mutex);
} else if (!channel) {
goto out;
goto out_unlock;
} else {
rx_status.freq = channel->center_freq;
rx_status.band = channel->band;
@ -6580,7 +6588,7 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
rx_status.rate_idx = nla_get_u32(info->attrs[HWSIM_ATTR_RX_RATE]);
if (rx_status.rate_idx >= data2->hw->wiphy->bands[rx_status.band]->n_bitrates)
goto out;
goto out_unlock;
rx_status.signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
hdr = (void *)skb->data;
@ -6590,10 +6598,11 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
rx_status.boottime_ns = ktime_get_boottime_ns();
mac80211_hwsim_rx(data2, &rx_status, skb);
mutex_unlock(&data2->mutex);
return 0;
err:
pr_debug("mac80211_hwsim: error occurred in %s\n", __func__);
out_unlock:
mutex_unlock(&data2->mutex);
out:
dev_kfree_skb(skb);
return -EINVAL;

View File

@ -434,6 +434,7 @@ static netdev_tx_t virt_wifi_start_xmit(struct sk_buff *skb,
priv->tx_packets++;
if (!priv->is_connected) {
priv->tx_failed++;
dev_kfree_skb_any(skb);
return NET_XMIT_DROP;
}
@ -557,7 +558,6 @@ static int virt_wifi_newlink(struct net_device *dev,
}
eth_hw_addr_inherit(dev, priv->lowerdev);
netif_stacked_transfer_operstate(priv->lowerdev, dev);
dev->ieee80211_ptr = kzalloc_obj(*dev->ieee80211_ptr);
@ -583,6 +583,8 @@ static int virt_wifi_newlink(struct net_device *dev,
goto unregister_netdev;
}
netif_stacked_transfer_operstate(priv->lowerdev, dev);
dev->priv_destructor = virt_wifi_net_device_destructor;
priv->being_deleted = false;
priv->is_connected = false;

View File

@ -361,8 +361,7 @@ ieee80211_mesh_hwmp_perr_get_rcode(const u8 *ie, u8 dst_idx)
/* IEEE Std 802.11-2016 9.4.2.113 PREQ element */
static inline bool ieee80211_mesh_preq_size_ok(const u8 *pos, u8 elen)
{
struct ieee80211_mesh_hwmp_preq_bottom *preq_elem_bottom =
ieee80211_mesh_hwmp_preq_get_bottom(pos);
struct ieee80211_mesh_hwmp_preq_bottom *preq_elem_bottom;
u8 target_count;
int needed;
@ -378,6 +377,7 @@ static inline bool ieee80211_mesh_preq_size_ok(const u8 *pos, u8 elen)
if (elen < needed)
return false;
preq_elem_bottom = ieee80211_mesh_hwmp_preq_get_bottom(pos);
target_count = preq_elem_bottom->target_count;
/* IEEE Std 802.11-2016 Table 14-10 to 14-16 */
if (target_count < 1)

View File

@ -7638,11 +7638,14 @@ bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,
*
* @skb: packet injected by userspace
* @dev: the &struct device of this 802.11 device
* @chandef: the channel definition the frame will be transmitted on, or
* %NULL to skip the bandwidth checks
*
* Return: %true if the radiotap header was parsed, %false otherwise
*/
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_noa_data - holds temporary data for tracking P2P NoA state

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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)
{

View File

@ -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)

View File

@ -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) +

View File

@ -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 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
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;

View File

@ -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 {

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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,10 +5117,18 @@ 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
@ -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;

View File

@ -153,68 +153,93 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
return 0;
}
static int cfg80211_switch_wdev_netns(struct wireless_dev *wdev,
struct net *net)
{
int err;
if (!wdev->netdev)
return 0;
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;
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;
}
scoped_guard(wiphy, &rdev->wiphy) {
list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
if (!wdev->netdev)
continue;
nl80211_notify_iface(rdev, wdev,
NL80211_CMD_DEL_INTERFACE);
}
nl80211_notify_wiphy(rdev, NL80211_CMD_DEL_WIPHY);
wiphy_net_set(&rdev->wiphy, net);
/* 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_NEW_WIPHY);
list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
if (!wdev->netdev)
continue;
nl80211_notify_iface(rdev, wdev,
NL80211_CMD_NEW_INTERFACE);
}
}
if (!err || force)
return err;
/* 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));
return err;
}
int cfg80211_switch_netns(struct cfg80211_registered_device *rdev,
struct net *net)
{
struct wireless_dev *wdev;
int err = 0;
if (!(rdev->wiphy.flags & WIPHY_FLAG_NETNS_OK))
return -EOPNOTSUPP;
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");
if (err)
break;
wdev->netdev->netns_immutable = true;
}
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) {
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;
}
return err;
}
guard(wiphy)(&rdev->wiphy);
list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
if (!wdev->netdev)
continue;
nl80211_notify_iface(rdev, wdev, NL80211_CMD_DEL_INTERFACE);
}
nl80211_notify_wiphy(rdev, NL80211_CMD_DEL_WIPHY);
wiphy_net_set(&rdev->wiphy, net);
err = device_rename(&rdev->wiphy.dev, dev_name(&rdev->wiphy.dev));
WARN_ON(err);
nl80211_notify_wiphy(rdev, NL80211_CMD_NEW_WIPHY);
list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
if (!wdev->netdev)
continue;
nl80211_notify_iface(rdev, wdev, NL80211_CMD_NEW_INTERFACE);
}
return 0;
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 @@ struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,
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();
}

View File

@ -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);

View File

@ -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;
ev = kzalloc_obj(*ev, gfp);
if (!ev)
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) {
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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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)

View File

@ -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[];

View File

@ -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;

View File

@ -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;