mirror of
https://github.com/torvalds/linux.git
synced 2026-10-23 23:51:02 +02:00
Still more fixes coming in, notably:
- ath11k: avoid running out of stations on HW restart
- mac80211:
- drop too large fragmented MPDUs
- mesh path handling fixes
- validation improvements
- reject CSA with bad 320 MHz bandwidth
- cfg80211: fix RTS for single radio devices
-----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEEpeA8sTs3M8SN2hR410qiO8sPaAAFAmq9AkAACgkQ10qiO8sP
aAD8mBAAhYUPz08049fTa9JV2yYjuSpeQPk6G0GgEWCxCeAFN/2MvYYB+/dP1lST
4hNUMq3b8JWh3n7jJopkpmFsiMRP2yslXq7osTrDuliuv2cTgRJiUaEF+jVplANQ
kMPvOgeGhks9MAZSD5tLf2wBHNJ3hk7OR8YniUxymvSxh51yg4j4eFvklVgd1m5S
5TJAQeT8iUkDjo08SHkhEab8D4tkkapdXJA4itnKD9kvT/jM/HPc7FjRdDE23zmD
2jbF+oiUDfcUqQ81sBhCTUfU+MdeWLiNvMz6kvMOl9jhH2tBoVSaDgiL20vU9a4N
1rdbHowwb6bGdl857q2ET+X5UhR3ygpF8wsUlAykJmKpCSk9KRfHSCOhd28CikIm
sDJO9+gU3tqiGPjYH344XM5NGXs18QM/v6SVnsKxyEtDVK9sXc09JKcAMF1rM+0u
YRIF7+Rl4cbfmO0ys7wKG7gJV48iZPFJbtFwU80aDizpfGk/eGkCLpEc3z4UROHL
YfRpziOD2QNbj8MuGbRYUMQYudYcV7FCaBmbzRYrbtuyC0SYJwBquXOcG+GTJy6d
tZ6ZahMc3o8oHjIxf9lfJ594DAw42ClL4PT/yhuP72TQR1oIoFq+pKYhLK5oc/Qt
mIDjZ6LMzGd3Ob0pk0kVHvpjKq509sLUAXoW6k2pHSne5EU3YRs=
=2Up4
-----END PGP SIGNATURE-----
Merge tag 'wireless-2026-09-30' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless
Johannes Berg says:
====================
Still more fixes coming in, notably:
- ath11k: avoid running out of stations on HW restart
- mac80211:
- drop too large fragmented MPDUs
- mesh path handling fixes
- validation improvements
- reject CSA with bad 320 MHz bandwidth
- cfg80211: fix RTS for single radio devices
* tag 'wireless-2026-09-30' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless: (27 commits)
wifi: mac80211: fix slab-out-of-bounds read in ieee80211_monitor_select_queue()
wifi: mac80211: reject invalid 320 MHz CSA bandwidth
wifi: mac80211: set info->band for 802.3 encap offload frames
wifi: mac80211: prevent AP VLAN tx from other interfaces
wifi: cfg80211: preserve hidden-group beacon IE ownership
wifi: mac80211: keep fallback association elements alive
wifi: mac80211: shut down RX BA session timer on teardown
wifi: mac80211: validate TX status rate metadata
wifi: ath9k_htc: bound TX aggregation to MAX_TX_BUF_SIZE
wifi: ath9k: reject short WMI command responses
wifi: ath9k: Clean up device initialisation guards
wifi: ath11k: reset ar->num_stations on hardware start
wifi: cfg80211: fix RTS threshold setting for single-radio PHY
wifi: mac80211: handle empty FILS association request payload
wifi: mac80211: minstrel_ht: validate fixed rate index
wifi: p54: validate firmware record lengths
wifi: mac80211: fix mesh fast xmit path deletion UAF
wifi: mac80211: drop oversized fragments to avoid extra_len overflow
wifi: wlcore: Fix runtime PM leak in wlcore_remove()
wifi: mac80211: drain PS delivery work during station teardown
...
====================
Link: https://patch.msgid.link/20260930124440.224799-3-johannes@sipsolutions.net
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
be35a3e003
|
|
@ -6716,6 +6716,7 @@ static int ath11k_mac_op_start(struct ieee80211_hw *hw)
|
|||
ar->num_started_vdevs = 0;
|
||||
ar->num_created_vdevs = 0;
|
||||
ar->num_peers = 0;
|
||||
ar->num_stations = 0;
|
||||
ar->allocated_vdev_map = 0;
|
||||
|
||||
/* Configure monitor status ring with default rx_filter to get rx status
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <linux/math.h>
|
||||
#include <linux/unaligned.h>
|
||||
#include "htc.h"
|
||||
|
||||
|
|
@ -328,11 +329,14 @@ static int __hif_usb_tx(struct hif_device_usb *hif_dev)
|
|||
tx_skb_cnt = min_t(u16, hif_dev->tx.tx_skb_cnt, MAX_TX_AGGR_NUM);
|
||||
|
||||
for (i = 0; i < tx_skb_cnt; i++) {
|
||||
nskb = skb_peek(&hif_dev->tx.tx_skb_queue);
|
||||
if (!nskb)
|
||||
break;
|
||||
|
||||
if (tx_buf->offset + nskb->len + 4 > MAX_TX_BUF_SIZE)
|
||||
break;
|
||||
|
||||
nskb = __skb_dequeue(&hif_dev->tx.tx_skb_queue);
|
||||
|
||||
/* Should never be NULL */
|
||||
BUG_ON(!nskb);
|
||||
|
||||
hif_dev->tx.tx_skb_cnt--;
|
||||
|
||||
buf = tx_buf->buf;
|
||||
|
|
@ -342,13 +346,8 @@ static int __hif_usb_tx(struct hif_device_usb *hif_dev)
|
|||
*hdr++ = cpu_to_le16(ATH_USB_TX_STREAM_MODE_TAG);
|
||||
buf += 4;
|
||||
memcpy(buf, nskb->data, nskb->len);
|
||||
tx_buf->len = nskb->len + 4;
|
||||
|
||||
if (i < (tx_skb_cnt - 1))
|
||||
tx_buf->offset += (((tx_buf->len - 1) / 4) + 1) * 4;
|
||||
|
||||
if (i == (tx_skb_cnt - 1))
|
||||
tx_buf->len += tx_buf->offset;
|
||||
tx_buf->len = tx_buf->offset + nskb->len + 4;
|
||||
tx_buf->offset += round_up(nskb->len + 4, 4);
|
||||
|
||||
__skb_queue_tail(&tx_buf->skb_queue, nskb);
|
||||
TX_STAT_INC(hif_dev, skb_queued);
|
||||
|
|
|
|||
|
|
@ -281,7 +281,6 @@ struct ath9k_htc_rxbuf {
|
|||
struct ath9k_htc_rx {
|
||||
struct list_head rxbuf;
|
||||
spinlock_t rxbuflock;
|
||||
bool initialized;
|
||||
};
|
||||
|
||||
#define ATH9K_HTC_TX_CLEANUP_INTERVAL 50 /* ms */
|
||||
|
|
|
|||
|
|
@ -910,6 +910,9 @@ static int ath9k_init_device(struct ath9k_htc_priv *priv,
|
|||
ath9k_init_leds(priv);
|
||||
ath9k_start_rfkill_poll(priv);
|
||||
|
||||
/* signal completion to ath9k_htc_rxep() and ath9k_wmi_event_tasklet() */
|
||||
smp_store_release(&priv->initialized, true);
|
||||
|
||||
return 0;
|
||||
|
||||
err_world:
|
||||
|
|
@ -966,10 +969,6 @@ int ath9k_htc_probe_device(struct htc_target *htc_handle, struct device *dev,
|
|||
|
||||
htc_handle->drv_priv = priv;
|
||||
|
||||
/* Allow ath9k_wmi_event_tasklet() to operate. */
|
||||
smp_wmb();
|
||||
priv->initialized = true;
|
||||
|
||||
return 0;
|
||||
|
||||
err_init:
|
||||
|
|
|
|||
|
|
@ -1141,8 +1141,8 @@ void ath9k_htc_rxep(void *drv_priv, struct sk_buff *skb,
|
|||
struct ath9k_htc_rxbuf *rxbuf = NULL, *tmp_buf = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
/* Check if ath9k_rx_init() completed. */
|
||||
if (!data_race(priv->rx.initialized))
|
||||
/* Check if ath9k_init_device() completed. */
|
||||
if (!smp_load_acquire(&priv->initialized))
|
||||
goto err;
|
||||
|
||||
spin_lock_irqsave(&priv->rx.rxbuflock, flags);
|
||||
|
|
@ -1200,10 +1200,6 @@ int ath9k_rx_init(struct ath9k_htc_priv *priv)
|
|||
list_add_tail(&rxbuf->list, &priv->rx.rxbuf);
|
||||
}
|
||||
|
||||
/* Allow ath9k_htc_rxep() to operate. */
|
||||
smp_wmb();
|
||||
priv->rx.initialized = true;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
|
|
|
|||
|
|
@ -146,6 +146,12 @@ void ath9k_wmi_event_tasklet(struct tasklet_struct *t)
|
|||
unsigned long flags;
|
||||
u16 cmd_id;
|
||||
|
||||
/* Check if ath9k_init_device() completed. */
|
||||
if (!smp_load_acquire(&priv->initialized)) {
|
||||
tasklet_schedule(t);
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
spin_lock_irqsave(&wmi->wmi_lock, flags);
|
||||
skb = __skb_dequeue(&wmi->wmi_event_queue);
|
||||
|
|
@ -155,12 +161,6 @@ void ath9k_wmi_event_tasklet(struct tasklet_struct *t)
|
|||
}
|
||||
spin_unlock_irqrestore(&wmi->wmi_lock, flags);
|
||||
|
||||
/* Check if ath9k_htc_probe_device() completed. */
|
||||
if (!data_race(priv->initialized)) {
|
||||
kfree_skb(skb);
|
||||
continue;
|
||||
}
|
||||
|
||||
hdr = (struct wmi_cmd_hdr *) skb->data;
|
||||
cmd_id = be16_to_cpu(hdr->command_id);
|
||||
wmi_event = skb_pull(skb, sizeof(struct wmi_cmd_hdr));
|
||||
|
|
@ -206,9 +206,16 @@ static void ath9k_wmi_rsp_callback(struct wmi *wmi, struct sk_buff *skb)
|
|||
{
|
||||
skb_pull(skb, sizeof(struct wmi_cmd_hdr));
|
||||
|
||||
if (wmi->cmd_rsp_buf != NULL && wmi->cmd_rsp_len != 0)
|
||||
if (wmi->cmd_rsp_buf && wmi->cmd_rsp_len) {
|
||||
if (skb->len < wmi->cmd_rsp_len) {
|
||||
wmi->cmd_rsp_status = -EMSGSIZE;
|
||||
goto complete;
|
||||
}
|
||||
memcpy(wmi->cmd_rsp_buf, skb->data, wmi->cmd_rsp_len);
|
||||
}
|
||||
wmi->cmd_rsp_status = 0;
|
||||
|
||||
complete:
|
||||
complete(&wmi->cmd_wait);
|
||||
}
|
||||
|
||||
|
|
@ -300,6 +307,7 @@ static int ath9k_wmi_cmd_issue(struct wmi *wmi,
|
|||
/* record the rsp buffer and length */
|
||||
wmi->cmd_rsp_buf = rsp_buf;
|
||||
wmi->cmd_rsp_len = rsp_len;
|
||||
wmi->cmd_rsp_status = 0;
|
||||
|
||||
wmi->last_seq_id = wmi->tx_seq_id;
|
||||
spin_unlock_irqrestore(&wmi->wmi_lock, flags);
|
||||
|
|
@ -356,9 +364,13 @@ int ath9k_wmi_cmd(struct wmi *wmi, enum wmi_cmd_id cmd_id,
|
|||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&wmi->wmi_lock, flags);
|
||||
ret = wmi->cmd_rsp_status;
|
||||
spin_unlock_irqrestore(&wmi->wmi_lock, flags);
|
||||
|
||||
mutex_unlock(&wmi->op_mutex);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
|
||||
out:
|
||||
ath_dbg(common, WMI, "WMI failure for: %s\n", wmi_cmd_to_name(cmd_id));
|
||||
|
|
|
|||
|
|
@ -157,6 +157,7 @@ struct wmi {
|
|||
u16 tx_seq_id;
|
||||
u8 *cmd_rsp_buf;
|
||||
u32 cmd_rsp_len;
|
||||
int cmd_rsp_status;
|
||||
bool stopped;
|
||||
|
||||
struct list_head pending_tx_events;
|
||||
|
|
|
|||
|
|
@ -3807,6 +3807,7 @@ il3945_pci_remove(struct pci_dev *pdev)
|
|||
|
||||
il_free_channel_map(il);
|
||||
il_free_geos(il);
|
||||
il_eeprom_free(il);
|
||||
kfree(il->scan_cmd);
|
||||
dev_kfree_skb(il->beacon_skb);
|
||||
ieee80211_free_hw(il->hw);
|
||||
|
|
|
|||
|
|
@ -52,6 +52,13 @@ int p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw)
|
|||
u32 code = le32_to_cpu(bootrec->code);
|
||||
switch (code) {
|
||||
case BR_CODE_COMPONENT_ID:
|
||||
if (len < sizeof(struct bootrec_comp_id) /
|
||||
sizeof(*bootrec->data)) {
|
||||
wiphy_err(priv->hw->wiphy,
|
||||
"firmware component ID is too short\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
priv->fw_interface = be32_to_cpup((__be32 *)
|
||||
bootrec->data);
|
||||
switch (priv->fw_interface) {
|
||||
|
|
@ -72,16 +79,41 @@ int p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw)
|
|||
}
|
||||
break;
|
||||
case BR_CODE_COMPONENT_VERSION:
|
||||
if (len < DIV_ROUND_UP(sizeof(struct bootrec_comp_ver),
|
||||
sizeof(*bootrec->data))) {
|
||||
wiphy_err(priv->hw->wiphy,
|
||||
"firmware component version is too short\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* 24 bytes should be enough for all firmwares */
|
||||
if (strnlen((unsigned char *) bootrec->data, 24) < 24)
|
||||
fw_version = (unsigned char *) bootrec->data;
|
||||
if (strnlen((unsigned char *)bootrec->data,
|
||||
sizeof(struct bootrec_comp_ver)) <
|
||||
sizeof(struct bootrec_comp_ver))
|
||||
fw_version = (unsigned char *)bootrec->data;
|
||||
break;
|
||||
case BR_CODE_DESCR: {
|
||||
struct bootrec_desc *desc =
|
||||
(struct bootrec_desc *)bootrec->data;
|
||||
priv->rx_start = le32_to_cpu(desc->rx_start);
|
||||
/* FIXME add sanity checking */
|
||||
priv->rx_end = le32_to_cpu(desc->rx_end) - 0x3500;
|
||||
u32 rx_start, rx_end;
|
||||
|
||||
/* 0xa is the shortest descriptor in supported firmware. */
|
||||
if (len < 0xa) {
|
||||
wiphy_err(priv->hw->wiphy,
|
||||
"firmware descriptor is too short\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rx_start = le32_to_cpu(desc->rx_start);
|
||||
rx_end = le32_to_cpu(desc->rx_end);
|
||||
if (rx_end < 0x3500 || rx_end - 0x3500 <= rx_start) {
|
||||
wiphy_err(priv->hw->wiphy,
|
||||
"firmware descriptor has invalid RX range\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
priv->rx_start = rx_start;
|
||||
priv->rx_end = rx_end - 0x3500;
|
||||
priv->headroom = desc->headroom;
|
||||
priv->tailroom = desc->tailroom;
|
||||
priv->privacy_caps = desc->privacy_caps;
|
||||
|
|
|
|||
|
|
@ -207,9 +207,11 @@ int wfx_hif_read_mib(struct wfx_dev *wdev, int vif_id, u16 mib_id, void *val, si
|
|||
dev_warn(wdev->dev, "%s: confirmation mismatch request\n", __func__);
|
||||
ret = -EIO;
|
||||
}
|
||||
if (ret == -ENOMEM)
|
||||
if (!ret && le16_to_cpu(reply->length) > val_len) {
|
||||
dev_err(wdev->dev, "buffer is too small to receive %s (%zu < %d)\n",
|
||||
wfx_get_mib_name(mib_id), val_len, le16_to_cpu(reply->length));
|
||||
ret = -EIO;
|
||||
}
|
||||
if (!ret)
|
||||
memcpy(val, &reply->mib_data, le16_to_cpu(reply->length));
|
||||
else
|
||||
|
|
|
|||
|
|
@ -344,6 +344,7 @@ static int cw1200_bh_tx_helper(struct cw1200_common *priv,
|
|||
int *tx_burst)
|
||||
{
|
||||
size_t tx_len;
|
||||
size_t aligned_len;
|
||||
u8 *data;
|
||||
int ret;
|
||||
struct wsm_hdr *wsm;
|
||||
|
|
@ -376,9 +377,14 @@ static int cw1200_bh_tx_helper(struct cw1200_common *priv,
|
|||
|
||||
atomic_inc(&priv->bh_tx);
|
||||
|
||||
tx_len = priv->hwbus_ops->align_size(
|
||||
aligned_len = priv->hwbus_ops->align_size(
|
||||
priv->hwbus_priv, tx_len);
|
||||
|
||||
/* Zero the bus alignment padding so no stale data is sent. */
|
||||
if (aligned_len > tx_len)
|
||||
memset(data + tx_len, 0, aligned_len - tx_len);
|
||||
tx_len = aligned_len;
|
||||
|
||||
/* Check if not exceeding CW1200 capabilities */
|
||||
if (WARN_ON_ONCE(tx_len > EFFECTIVE_BUF_SIZE))
|
||||
pr_debug("Write aligned len: %zu\n", tx_len);
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include "wsm.h"
|
||||
#include "bh.h"
|
||||
#include "sta.h"
|
||||
#include "hwbus.h"
|
||||
#include "debug.h"
|
||||
|
||||
#define CW1200_INVALID_RATE_ID (0xFF)
|
||||
|
|
@ -716,6 +717,7 @@ void cw1200_tx(struct ieee80211_hw *dev,
|
|||
struct ieee80211_sta *sta;
|
||||
struct wsm_tx *wsm;
|
||||
bool tid_update = false;
|
||||
size_t pad_len;
|
||||
u8 flags = 0;
|
||||
int ret;
|
||||
|
||||
|
|
@ -764,6 +766,18 @@ void cw1200_tx(struct ieee80211_hw *dev,
|
|||
|
||||
sta = t.sta;
|
||||
|
||||
/*
|
||||
* The bus transfer is padded up to the bus alignment and the
|
||||
* padding is transferred from the frame buffer. Make sure the
|
||||
* buffer has room for the padding so the device never receives
|
||||
* data past the end of the frame.
|
||||
*/
|
||||
pad_len = priv->hwbus_ops->align_size(priv->hwbus_priv, skb->len) -
|
||||
skb->len;
|
||||
if (skb_tailroom(skb) < pad_len &&
|
||||
pskb_expand_head(skb, 0, pad_len, GFP_ATOMIC))
|
||||
goto drop;
|
||||
|
||||
spin_lock_bh(&priv->ps_state_lock);
|
||||
{
|
||||
tid_update = cw1200_tx_h_pm_state(priv, &t);
|
||||
|
|
|
|||
|
|
@ -6809,8 +6809,10 @@ void wlcore_remove(struct platform_device *pdev)
|
|||
|
||||
if (pdev_data->family && pdev_data->family->nvs_name)
|
||||
wait_for_completion(&wl->nvs_loading_complete);
|
||||
if (!wl->initialized)
|
||||
if (!wl->initialized) {
|
||||
pm_runtime_put_noidle(wl->dev);
|
||||
return;
|
||||
}
|
||||
|
||||
if (wl->wakeirq >= 0) {
|
||||
dev_pm_clear_wake_irq(wl->dev);
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
|
|||
if (!tid_rx)
|
||||
return;
|
||||
|
||||
timer_delete_sync(&tid_rx->session_timer);
|
||||
timer_shutdown_sync(&tid_rx->session_timer);
|
||||
|
||||
/* make sure ieee80211_sta_reorder_release() doesn't re-arm the timer */
|
||||
spin_lock_bh(&tid_rx->reorder_lock);
|
||||
|
|
|
|||
|
|
@ -231,6 +231,13 @@ int ieee80211_chanctx_refcount(struct ieee80211_local *local,
|
|||
return num;
|
||||
}
|
||||
|
||||
static bool
|
||||
ieee80211_chanctx_has_replace_partner(struct ieee80211_chanctx *ctx)
|
||||
{
|
||||
return ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED &&
|
||||
ctx->replace_ctx;
|
||||
}
|
||||
|
||||
static int ieee80211_num_chanctx(struct ieee80211_local *local, int radio_idx)
|
||||
{
|
||||
struct ieee80211_chanctx *ctx;
|
||||
|
|
@ -1405,6 +1412,7 @@ void ieee80211_link_unreserve_chanctx(struct ieee80211_link_data *link)
|
|||
{
|
||||
struct ieee80211_sub_if_data *sdata = link->sdata;
|
||||
struct ieee80211_chanctx *ctx = link->reserved_chanctx;
|
||||
struct ieee80211_chanctx *old_ctx = NULL;
|
||||
|
||||
lockdep_assert_wiphy(sdata->local->hw.wiphy);
|
||||
|
||||
|
|
@ -1418,6 +1426,7 @@ void ieee80211_link_unreserve_chanctx(struct ieee80211_link_data *link)
|
|||
if (WARN_ON(!ctx->replace_ctx))
|
||||
return;
|
||||
|
||||
old_ctx = ctx->replace_ctx;
|
||||
WARN_ON(ctx->replace_ctx->replace_state !=
|
||||
IEEE80211_CHANCTX_WILL_BE_REPLACED);
|
||||
WARN_ON(ctx->replace_ctx->replace_ctx != ctx);
|
||||
|
|
@ -1428,6 +1437,11 @@ void ieee80211_link_unreserve_chanctx(struct ieee80211_link_data *link)
|
|||
|
||||
list_del_rcu(&ctx->list);
|
||||
kfree_rcu(ctx, rcu_head);
|
||||
|
||||
if (ieee80211_chanctx_refcount(sdata->local,
|
||||
old_ctx) == 0)
|
||||
ieee80211_free_chanctx(sdata->local, old_ctx,
|
||||
false);
|
||||
} else {
|
||||
ieee80211_free_chanctx(sdata->local, ctx, false);
|
||||
}
|
||||
|
|
@ -1707,7 +1721,8 @@ ieee80211_link_use_reserved_reassign(struct ieee80211_link_data *link)
|
|||
|
||||
ieee80211_check_fast_xmit_iface(sdata);
|
||||
|
||||
if (ieee80211_chanctx_refcount(local, old_ctx) == 0)
|
||||
if (ieee80211_chanctx_refcount(local, old_ctx) == 0 &&
|
||||
!ieee80211_chanctx_has_replace_partner(old_ctx))
|
||||
ieee80211_free_chanctx(local, old_ctx, false);
|
||||
|
||||
ieee80211_recalc_chanctx_min_def(local, new_ctx);
|
||||
|
|
@ -1772,7 +1787,8 @@ ieee80211_link_use_reserved_assign(struct ieee80211_link_data *link)
|
|||
}
|
||||
|
||||
static bool
|
||||
ieee80211_link_has_in_place_reservation(struct ieee80211_link_data *link)
|
||||
ieee80211_link_has_in_place_reservation(struct ieee80211_link_data *link,
|
||||
struct ieee80211_chanctx *ctx)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = link->sdata;
|
||||
struct ieee80211_chanctx *old_ctx, *new_ctx;
|
||||
|
|
@ -1782,6 +1798,9 @@ ieee80211_link_has_in_place_reservation(struct ieee80211_link_data *link)
|
|||
new_ctx = link->reserved_chanctx;
|
||||
old_ctx = ieee80211_link_get_chanctx(link);
|
||||
|
||||
if (new_ctx != ctx)
|
||||
return false;
|
||||
|
||||
if (!old_ctx)
|
||||
return false;
|
||||
|
||||
|
|
@ -1794,6 +1813,12 @@ ieee80211_link_has_in_place_reservation(struct ieee80211_link_data *link)
|
|||
if (new_ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER)
|
||||
return false;
|
||||
|
||||
if (new_ctx->replace_ctx != old_ctx)
|
||||
return false;
|
||||
|
||||
if (old_ctx->replace_ctx != new_ctx)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -1823,7 +1848,8 @@ static int ieee80211_chsw_switch_vifs(struct ieee80211_local *local,
|
|||
}
|
||||
|
||||
for_each_chanctx_user_reserved(local, ctx, &iter) {
|
||||
if (!ieee80211_link_has_in_place_reservation(iter.link))
|
||||
if (!ieee80211_link_has_in_place_reservation(iter.link,
|
||||
ctx))
|
||||
continue;
|
||||
|
||||
old_ctx = ieee80211_link_get_chanctx(iter.link);
|
||||
|
|
@ -1925,7 +1951,9 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
|
|||
|
||||
for_each_chanctx_user_assigned(local, ctx->replace_ctx, &iter) {
|
||||
n_assigned++;
|
||||
if (iter.link && iter.link->reserved_chanctx) {
|
||||
if (iter.link &&
|
||||
ieee80211_link_has_in_place_reservation(iter.link,
|
||||
ctx)) {
|
||||
n_reserved++;
|
||||
if (iter.link->reserved_ready)
|
||||
n_ready++;
|
||||
|
|
@ -1950,7 +1978,8 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
|
|||
use_reserved:
|
||||
ctx->conf.radar_enabled = false;
|
||||
for_each_chanctx_user_reserved(local, ctx, &iter) {
|
||||
if (ieee80211_link_has_in_place_reservation(iter.link) &&
|
||||
if (ieee80211_link_has_in_place_reservation(iter.link,
|
||||
ctx) &&
|
||||
!iter.link->reserved_ready)
|
||||
return -EAGAIN;
|
||||
|
||||
|
|
@ -1991,7 +2020,8 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
|
|||
}
|
||||
|
||||
for_each_chanctx_user_reserved(local, ctx, &iter) {
|
||||
if (!ieee80211_link_has_in_place_reservation(iter.link))
|
||||
if (!ieee80211_link_has_in_place_reservation(iter.link,
|
||||
ctx))
|
||||
continue;
|
||||
|
||||
ieee80211_chan_bw_change(local,
|
||||
|
|
@ -2040,7 +2070,7 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
|
|||
struct ieee80211_bss_conf *link_conf = link->conf;
|
||||
u64 changed = 0;
|
||||
|
||||
if (!ieee80211_link_has_in_place_reservation(link))
|
||||
if (!ieee80211_link_has_in_place_reservation(link, ctx))
|
||||
continue;
|
||||
|
||||
rcu_assign_pointer(link_conf->chanctx_conf,
|
||||
|
|
@ -2091,7 +2121,8 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
|
|||
for_each_chanctx_user_reserved(local, ctx, &iter) {
|
||||
struct ieee80211_link_data *link = iter.link;
|
||||
|
||||
if (WARN_ON(ieee80211_link_has_in_place_reservation(link)))
|
||||
if (WARN_ON(ieee80211_link_has_in_place_reservation(link,
|
||||
ctx)))
|
||||
continue;
|
||||
|
||||
if (!link->reserved_ready)
|
||||
|
|
@ -2176,7 +2207,8 @@ void __ieee80211_link_release_channel(struct ieee80211_link_data *link,
|
|||
}
|
||||
|
||||
ieee80211_assign_link_chanctx(link, NULL, false);
|
||||
if (ieee80211_chanctx_refcount(local, ctx) == 0)
|
||||
if (ieee80211_chanctx_refcount(local, ctx) == 0 &&
|
||||
!ieee80211_chanctx_has_replace_partner(ctx))
|
||||
ieee80211_free_chanctx(local, ctx, skip_idle_recalc);
|
||||
|
||||
link->radar_required = false;
|
||||
|
|
|
|||
|
|
@ -108,6 +108,7 @@ typedef unsigned int __bitwise ieee80211_rx_result;
|
|||
R(RX_DROP_U_UNHANDLED_PREQ) \
|
||||
R(RX_DROP_U_UNHANDLED_MGMT_STYPE) \
|
||||
R(RX_DROP_U_NO_LINK) \
|
||||
R(RX_DROP_U_DEFRAG_OVERFLOW) \
|
||||
/* this line for the trailing \ - add before this */
|
||||
|
||||
/* having two enums allows for checking ieee80211_rx_result use with sparse */
|
||||
|
|
|
|||
|
|
@ -89,6 +89,14 @@ static int aes_siv_encrypt(const u8 *key, size_t key_len,
|
|||
if (res)
|
||||
return res;
|
||||
|
||||
/* With an empty plaintext there is no ciphertext to produce and
|
||||
* the S2V result is the complete output.
|
||||
*/
|
||||
if (!plain_len) {
|
||||
memcpy(out, v, AES_BLOCK_SIZE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Use a temporary buffer of the plaintext to handle need for
|
||||
* overwriting this during AES-CTR.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -2475,7 +2475,7 @@ static inline bool ieee80211_require_encrypted_assoc(__le16 fc,
|
|||
/* sta_out needs to be checked for ERR_PTR() before using */
|
||||
int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb,
|
||||
struct sta_info **sta_out);
|
||||
struct sta_info **sta_out, bool bss);
|
||||
|
||||
static inline void
|
||||
ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
|
||||
|
|
|
|||
|
|
@ -998,8 +998,8 @@ static u16 ieee80211_monitor_select_queue(struct net_device *dev,
|
|||
|
||||
len_rthdr = ieee80211_get_radiotap_len(skb->data);
|
||||
hdr = (struct ieee80211_hdr *)(skb->data + len_rthdr);
|
||||
if (skb->len < len_rthdr + 2 ||
|
||||
skb->len < len_rthdr + ieee80211_hdrlen(hdr->frame_control))
|
||||
if (skb_headlen(skb) < len_rthdr + 2 ||
|
||||
skb_headlen(skb) < len_rthdr + ieee80211_hdrlen(hdr->frame_control))
|
||||
return 0; /* doesn't matter, frame will be dropped */
|
||||
|
||||
return ieee80211_select_queue_80211(sdata, skb, hdr);
|
||||
|
|
|
|||
|
|
@ -577,6 +577,12 @@ void mesh_fast_tx_cache(struct ieee80211_sub_if_data *sdata,
|
|||
goto unlock_sta;
|
||||
|
||||
spin_lock(&cache->walk_lock);
|
||||
if ((READ_ONCE(mpath->flags) & MESH_PATH_DELETED) ||
|
||||
(mppath && (READ_ONCE(mppath->flags) & MESH_PATH_DELETED))) {
|
||||
kfree(entry);
|
||||
goto unlock_cache;
|
||||
}
|
||||
|
||||
prev = rhashtable_lookup_get_insert_fast(&cache->rht,
|
||||
&entry->rhash,
|
||||
fast_tx_rht_params);
|
||||
|
|
@ -694,8 +700,10 @@ struct mesh_path *mesh_path_add(struct ieee80211_sub_if_data *sdata,
|
|||
return ERR_PTR(-ENOSPC);
|
||||
|
||||
new_mpath = mesh_path_new(sdata, dst, GFP_ATOMIC);
|
||||
if (!new_mpath)
|
||||
if (!new_mpath) {
|
||||
atomic_dec(&sdata->u.mesh.mpaths);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
tbl = &sdata->u.mesh.mesh_paths;
|
||||
spin_lock_bh(&tbl->walk_lock);
|
||||
|
|
@ -708,6 +716,7 @@ struct mesh_path *mesh_path_add(struct ieee80211_sub_if_data *sdata,
|
|||
|
||||
if (mpath) {
|
||||
kfree(new_mpath);
|
||||
atomic_dec(&sdata->u.mesh.mpaths);
|
||||
|
||||
if (IS_ERR(mpath))
|
||||
return mpath;
|
||||
|
|
@ -733,10 +742,15 @@ int mpp_path_add(struct ieee80211_sub_if_data *sdata,
|
|||
if (is_multicast_ether_addr(dst))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!atomic_add_unless(&sdata->u.mesh.mpaths, 1, MESH_MAX_MPATHS))
|
||||
return -ENOSPC;
|
||||
|
||||
new_mpath = mesh_path_new(sdata, dst, GFP_ATOMIC);
|
||||
|
||||
if (!new_mpath)
|
||||
if (!new_mpath) {
|
||||
atomic_dec(&sdata->u.mesh.mpaths);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memcpy(new_mpath->mpp, mpp, ETH_ALEN);
|
||||
tbl = &sdata->u.mesh.mpp_paths;
|
||||
|
|
@ -749,10 +763,12 @@ int mpp_path_add(struct ieee80211_sub_if_data *sdata,
|
|||
hlist_add_head_rcu(&new_mpath->walk_list, &tbl->walk_head);
|
||||
spin_unlock_bh(&tbl->walk_lock);
|
||||
|
||||
if (ret)
|
||||
if (ret) {
|
||||
kfree(new_mpath);
|
||||
else
|
||||
atomic_dec(&sdata->u.mesh.mpaths);
|
||||
} else {
|
||||
mesh_fast_tx_flush_addr(sdata, dst);
|
||||
}
|
||||
|
||||
sdata->u.mesh.mpp_paths_generation++;
|
||||
return ret;
|
||||
|
|
@ -798,7 +814,8 @@ static void mesh_path_free_rcu(struct mesh_table *tbl,
|
|||
struct ieee80211_sub_if_data *sdata = mpath->sdata;
|
||||
|
||||
spin_lock_bh(&mpath->state_lock);
|
||||
mpath->flags |= MESH_PATH_RESOLVING | MESH_PATH_DELETED;
|
||||
WRITE_ONCE(mpath->flags,
|
||||
mpath->flags | MESH_PATH_RESOLVING | MESH_PATH_DELETED);
|
||||
mesh_gate_del(tbl, mpath);
|
||||
spin_unlock_bh(&mpath->state_lock);
|
||||
timer_shutdown_sync(&mpath->timer);
|
||||
|
|
@ -812,6 +829,9 @@ static void __mesh_path_del(struct mesh_table *tbl, struct mesh_path *mpath)
|
|||
{
|
||||
hlist_del_rcu(&mpath->walk_list);
|
||||
rhashtable_remove_fast(&tbl->rhead, &mpath->rhash, mesh_rht_params);
|
||||
spin_lock_bh(&mpath->state_lock);
|
||||
WRITE_ONCE(mpath->flags, mpath->flags | MESH_PATH_DELETED);
|
||||
spin_unlock_bh(&mpath->state_lock);
|
||||
if (tbl == &mpath->sdata->u.mesh.mpp_paths)
|
||||
mesh_fast_tx_flush_addr(mpath->sdata, mpath->dst);
|
||||
else
|
||||
|
|
|
|||
|
|
@ -5877,6 +5877,7 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link,
|
|||
bool is_6ghz = cbss->channel->band == NL80211_BAND_6GHZ;
|
||||
bool is_s1g = cbss->channel->band == NL80211_BAND_S1GHZ;
|
||||
const struct cfg80211_bss_ies *bss_ies = NULL;
|
||||
struct ieee802_11_elems *bss_elems = NULL;
|
||||
struct ieee80211_supported_band *sband;
|
||||
struct ieee802_11_elems *elems;
|
||||
u16 capab_info;
|
||||
|
|
@ -6007,7 +6008,6 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link,
|
|||
(is_5ghz && link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_VHT &&
|
||||
(!elems->vht_cap_elem || !elems->vht_operation)))) {
|
||||
const struct cfg80211_bss_ies *ies;
|
||||
struct ieee802_11_elems *bss_elems;
|
||||
|
||||
rcu_read_lock();
|
||||
ies = rcu_dereference(cbss->ies);
|
||||
|
|
@ -6069,7 +6069,6 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link,
|
|||
"AP bug: VHT operation missing from AssocResp\n");
|
||||
}
|
||||
}
|
||||
kfree(bss_elems);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -6342,6 +6341,7 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link,
|
|||
ret = true;
|
||||
out:
|
||||
kfree(elems);
|
||||
kfree(bss_elems);
|
||||
kfree(bss_ies);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/ieee80211.h>
|
||||
|
|
@ -1193,6 +1194,54 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
|
|||
mi->sample_time = jiffies;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether an HT/VHT rate from a TX status entry maps to an entry
|
||||
* in the minstrel_ht rate tables. Values that cannot be represented
|
||||
* there must not be used for indexing mi->groups[] and the MCS groups.
|
||||
*/
|
||||
static bool
|
||||
minstrel_ht_txstat_rate_valid(struct ieee80211_tx_rate *rate)
|
||||
{
|
||||
unsigned int bw;
|
||||
|
||||
if (!(rate->flags & (IEEE80211_TX_RC_MCS | IEEE80211_TX_RC_VHT_MCS)))
|
||||
return true;
|
||||
|
||||
if (rate->flags & IEEE80211_TX_RC_MCS) {
|
||||
/* minstrel_ht supports up to MINSTREL_MAX_STREAMS streams */
|
||||
return rate->idx < MINSTREL_MAX_STREAMS * 8;
|
||||
}
|
||||
|
||||
/* minstrel_ht has no VHT groups for 160 MHz and wider */
|
||||
bw = !!(rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) +
|
||||
2 * !!(rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH);
|
||||
if ((rate->flags & IEEE80211_TX_RC_160_MHZ_WIDTH) || bw > BW_80)
|
||||
return false;
|
||||
|
||||
return ieee80211_rate_get_vht_nss(rate) <= MINSTREL_MAX_STREAMS &&
|
||||
ieee80211_rate_get_vht_mcs(rate) < MCS_GROUP_RATES;
|
||||
}
|
||||
|
||||
static bool
|
||||
minstrel_ht_ri_txstat_rate_valid(struct rate_info *rate)
|
||||
{
|
||||
if (!(rate->flags & (RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_VHT_MCS)))
|
||||
return true;
|
||||
|
||||
if (rate->flags & RATE_INFO_FLAGS_MCS) {
|
||||
/* minstrel_ht supports up to MINSTREL_MAX_STREAMS streams */
|
||||
return rate->mcs < MINSTREL_MAX_STREAMS * 8;
|
||||
}
|
||||
|
||||
/* minstrel_ht has VHT groups only for 20/40/80 MHz */
|
||||
if (rate->bw != RATE_INFO_BW_20 && rate->bw != RATE_INFO_BW_40 &&
|
||||
rate->bw != RATE_INFO_BW_80)
|
||||
return false;
|
||||
|
||||
return rate->nss <= MINSTREL_MAX_STREAMS &&
|
||||
rate->mcs < MCS_GROUP_RATES;
|
||||
}
|
||||
|
||||
static bool
|
||||
minstrel_ht_txstat_valid(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
|
||||
struct ieee80211_tx_rate *rate)
|
||||
|
|
@ -1205,9 +1254,8 @@ minstrel_ht_txstat_valid(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
|
|||
if (!rate->count)
|
||||
return false;
|
||||
|
||||
if (rate->flags & IEEE80211_TX_RC_MCS ||
|
||||
rate->flags & IEEE80211_TX_RC_VHT_MCS)
|
||||
return true;
|
||||
if (rate->flags & (IEEE80211_TX_RC_MCS | IEEE80211_TX_RC_VHT_MCS))
|
||||
return minstrel_ht_txstat_rate_valid(rate);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mp->cck_rates); i++)
|
||||
if (rate->idx == mp->cck_rates[i])
|
||||
|
|
@ -1235,9 +1283,9 @@ minstrel_ht_ri_txstat_valid(struct minstrel_priv *mp,
|
|||
if (!rate_status->try_count)
|
||||
return false;
|
||||
|
||||
if (rate_status->rate_idx.flags & RATE_INFO_FLAGS_MCS ||
|
||||
rate_status->rate_idx.flags & RATE_INFO_FLAGS_VHT_MCS)
|
||||
return true;
|
||||
if (rate_status->rate_idx.flags &
|
||||
(RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_VHT_MCS))
|
||||
return minstrel_ht_ri_txstat_rate_valid(&rate_status->rate_idx);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mp->cck_rates); i++) {
|
||||
if (rate_status->rate_idx.legacy ==
|
||||
|
|
@ -1947,14 +1995,39 @@ minstrel_ht_alloc(struct ieee80211_hw *hw)
|
|||
}
|
||||
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
static int minstrel_ht_fixed_rate_idx_get(void *data, u64 *val)
|
||||
{
|
||||
*val = *(u32 *)data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int minstrel_ht_fixed_rate_idx_set(void *data, u64 val)
|
||||
{
|
||||
u32 idx = val;
|
||||
|
||||
/* U32_MAX is the default and keeps fixed rate processing disabled */
|
||||
if (val != U32_MAX &&
|
||||
(val > U16_MAX ||
|
||||
MI_RATE_GROUP(idx) >= ARRAY_SIZE(minstrel_mcs_groups) ||
|
||||
MI_RATE_IDX(idx) >= MCS_GROUP_RATES))
|
||||
return -EINVAL;
|
||||
|
||||
*(u32 *)data = idx;
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(minstrel_ht_fixed_rate_idx_fops,
|
||||
minstrel_ht_fixed_rate_idx_get,
|
||||
minstrel_ht_fixed_rate_idx_set, "%llu\n");
|
||||
|
||||
static void minstrel_ht_add_debugfs(struct ieee80211_hw *hw, void *priv,
|
||||
struct dentry *debugfsdir)
|
||||
{
|
||||
struct minstrel_priv *mp = priv;
|
||||
|
||||
mp->fixed_rate_idx = (u32) -1;
|
||||
debugfs_create_u32("fixed_rate_idx", S_IRUGO | S_IWUGO, debugfsdir,
|
||||
&mp->fixed_rate_idx);
|
||||
debugfs_create_file("fixed_rate_idx", S_IRUGO | S_IWUGO, debugfsdir,
|
||||
&mp->fixed_rate_idx, &minstrel_ht_fixed_rate_idx_fops);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -2499,6 +2499,12 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
|
|||
}
|
||||
|
||||
skb_pull(rx->skb, ieee80211_hdrlen(fc));
|
||||
if (unlikely((u32)entry->extra_len + rx->skb->len > U16_MAX)) {
|
||||
I802_DEBUG_INC(rx->local->rx_handlers_drop_defrag);
|
||||
__skb_queue_purge(&entry->skb_list);
|
||||
return RX_DROP_U_DEFRAG_OVERFLOW;
|
||||
}
|
||||
|
||||
__skb_queue_tail(&entry->skb_list, rx->skb);
|
||||
entry->last_frag = frag;
|
||||
entry->extra_len += rx->skb->len;
|
||||
|
|
|
|||
|
|
@ -111,8 +111,8 @@ validate_chandef_by_ht_vht_oper(struct ieee80211_sub_if_data *sdata,
|
|||
|
||||
switch (chan_width) {
|
||||
case NL80211_CHAN_WIDTH_320:
|
||||
WARN_ON(1);
|
||||
break;
|
||||
chandef->chan = NULL;
|
||||
return;
|
||||
case NL80211_CHAN_WIDTH_160:
|
||||
vht_oper.chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ;
|
||||
vht_oper.center_freq_seg1_idx = vht_oper.center_freq_seg0_idx;
|
||||
|
|
|
|||
|
|
@ -137,6 +137,8 @@ static void __cleanup_single_sta(struct sta_info *sta)
|
|||
struct ieee80211_local *local = sdata->local;
|
||||
struct ps_data *ps;
|
||||
|
||||
cancel_work_sync(&sta->drv_deliver_wk);
|
||||
|
||||
if (test_sta_flag(sta, WLAN_STA_PS_STA) ||
|
||||
test_sta_flag(sta, WLAN_STA_PS_DRIVER) ||
|
||||
test_sta_flag(sta, WLAN_STA_PS_DELIVER)) {
|
||||
|
|
@ -166,8 +168,6 @@ static void __cleanup_single_sta(struct sta_info *sta)
|
|||
if (ieee80211_vif_is_mesh(&sdata->vif))
|
||||
mesh_sta_cleanup(sta);
|
||||
|
||||
cancel_work_sync(&sta->drv_deliver_wk);
|
||||
|
||||
/*
|
||||
* Destroy aggregation state here. It would be nice to wait for the
|
||||
* driver to finish aggregation stop and then clean up, but for now
|
||||
|
|
@ -1582,6 +1582,8 @@ static void __sta_info_destroy_part2(struct sta_info *sta, bool recalc)
|
|||
|
||||
sta->dead = true;
|
||||
|
||||
cancel_work_sync(&sta->drv_deliver_wk);
|
||||
|
||||
local->num_sta--;
|
||||
local->sta_generation++;
|
||||
|
||||
|
|
|
|||
|
|
@ -1164,6 +1164,73 @@ void ieee80211_tx_status_skb(struct ieee80211_hw *hw, struct sk_buff *skb)
|
|||
}
|
||||
EXPORT_SYMBOL(ieee80211_tx_status_skb);
|
||||
|
||||
/*
|
||||
* Check whether the HT/VHT rate information in a TX status entry is
|
||||
* valid for its encoding. This is not specific to any rate control
|
||||
* algorithm; all status consumers rely on the values being sane.
|
||||
*/
|
||||
static bool ieee80211_tx_status_rate_info_valid(const struct rate_info *rate)
|
||||
{
|
||||
if (rate->flags & RATE_INFO_FLAGS_MCS)
|
||||
return rate->mcs < 32;
|
||||
|
||||
if (rate->flags & RATE_INFO_FLAGS_VHT_MCS)
|
||||
return rate->nss >= 1 && rate->nss <= 8 && rate->mcs <= 11;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
ieee80211_tx_status_tx_rate_valid(const struct ieee80211_tx_rate *rate)
|
||||
{
|
||||
if (rate->flags & IEEE80211_TX_RC_MCS)
|
||||
return rate->idx < 32;
|
||||
|
||||
if (rate->flags & IEEE80211_TX_RC_VHT_MCS)
|
||||
return ieee80211_rate_get_vht_mcs(rate) <= 11;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Drop TX status rate entries that don't describe a valid rate. The
|
||||
* status information is used by rate control and by other mac80211
|
||||
* code, and a malformed entry must not be able to corrupt state beyond
|
||||
* the driver that reported it.
|
||||
*/
|
||||
static void
|
||||
ieee80211_tx_status_drop_invalid_rates(struct ieee80211_tx_status *status)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < status->n_rates; i++) {
|
||||
struct ieee80211_rate_status *rs = &status->rates[i];
|
||||
|
||||
if (ieee80211_tx_status_rate_info_valid(&rs->rate_idx))
|
||||
continue;
|
||||
|
||||
rs->try_count = 0;
|
||||
memset(&rs->rate_idx, 0, sizeof(rs->rate_idx));
|
||||
}
|
||||
|
||||
if (!status->info)
|
||||
return;
|
||||
|
||||
for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
|
||||
struct ieee80211_tx_rate *rate;
|
||||
|
||||
rate = &status->info->status.rates[i];
|
||||
if (rate->idx < 0)
|
||||
break;
|
||||
|
||||
if (ieee80211_tx_status_tx_rate_valid(rate))
|
||||
continue;
|
||||
|
||||
rate->idx = -1;
|
||||
rate->count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ieee80211_tx_status_ext(struct ieee80211_hw *hw,
|
||||
struct ieee80211_tx_status *status)
|
||||
{
|
||||
|
|
@ -1176,6 +1243,8 @@ void ieee80211_tx_status_ext(struct ieee80211_hw *hw,
|
|||
bool acked, noack_success, ack_signal_valid;
|
||||
u16 tx_time_est;
|
||||
|
||||
ieee80211_tx_status_drop_invalid_rates(status);
|
||||
|
||||
if (pubsta) {
|
||||
sta = container_of(pubsta, struct sta_info, sta);
|
||||
|
||||
|
|
@ -1300,6 +1369,7 @@ void ieee80211_tx_rate_update(struct ieee80211_hw *hw,
|
|||
.sta = pubsta,
|
||||
};
|
||||
|
||||
ieee80211_tx_status_drop_invalid_rates(&status);
|
||||
rate_control_tx_status(local, &status);
|
||||
|
||||
if (ieee80211_hw_check(&local->hw, HAS_RATE_CONTROL))
|
||||
|
|
|
|||
|
|
@ -2091,7 +2091,7 @@ static bool ieee80211_validate_radiotap_len(struct sk_buff *skb)
|
|||
(struct ieee80211_radiotap_header *)skb->data;
|
||||
|
||||
/* check for not even having the fixed radiotap header part */
|
||||
if (unlikely(skb->len < sizeof(struct ieee80211_radiotap_header)))
|
||||
if (unlikely(skb_headlen(skb) < sizeof(struct ieee80211_radiotap_header)))
|
||||
return false; /* too short to be possibly valid */
|
||||
|
||||
/* is it a header version we can trust to find length from? */
|
||||
|
|
@ -2099,7 +2099,7 @@ static bool ieee80211_validate_radiotap_len(struct sk_buff *skb)
|
|||
return false; /* only version 0 is supported */
|
||||
|
||||
/* does the skb contain enough to deliver on the alleged length? */
|
||||
if (unlikely(skb->len < ieee80211_get_radiotap_len(skb->data)))
|
||||
if (unlikely(skb_headlen(skb) < ieee80211_get_radiotap_len(skb->data)))
|
||||
return false; /* skb too short for claimed rt header extent */
|
||||
|
||||
return true;
|
||||
|
|
@ -2388,13 +2388,13 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
|
|||
skb_set_network_header(skb, len_rthdr);
|
||||
skb_set_transport_header(skb, len_rthdr);
|
||||
|
||||
if (skb->len < len_rthdr + 2)
|
||||
if (skb_headlen(skb) < len_rthdr + 2)
|
||||
goto fail;
|
||||
|
||||
hdr = (struct ieee80211_hdr *)(skb->data + len_rthdr);
|
||||
hdrlen = ieee80211_hdrlen(hdr->frame_control);
|
||||
|
||||
if (skb->len < len_rthdr + hdrlen)
|
||||
if (skb_headlen(skb) < len_rthdr + hdrlen)
|
||||
goto fail;
|
||||
|
||||
/*
|
||||
|
|
@ -2402,7 +2402,7 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
|
|||
* carrying a rfc1042 header
|
||||
*/
|
||||
if (ieee80211_is_data(hdr->frame_control) &&
|
||||
skb->len >= len_rthdr + hdrlen + sizeof(rfc1042_header) + 2) {
|
||||
skb_headlen(skb) >= len_rthdr + hdrlen + sizeof(rfc1042_header) + 2) {
|
||||
u8 *payload = (u8 *)hdr + hdrlen;
|
||||
|
||||
if (ether_addr_equal(payload, rfc1042_header))
|
||||
|
|
@ -2532,7 +2532,7 @@ static inline bool ieee80211_is_tdls_setup(struct sk_buff *skb)
|
|||
|
||||
int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb,
|
||||
struct sta_info **sta_out)
|
||||
struct sta_info **sta_out, bool bss)
|
||||
{
|
||||
struct sta_info *sta;
|
||||
|
||||
|
|
@ -2553,7 +2553,10 @@ int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
|
|||
*sta_out = ERR_PTR(-ENOENT);
|
||||
return 0;
|
||||
}
|
||||
sta = sta_info_get_bss(sdata, skb->data);
|
||||
if (bss)
|
||||
sta = sta_info_get_bss(sdata, skb->data);
|
||||
else
|
||||
sta = sta_info_get(sdata, skb->data);
|
||||
break;
|
||||
#ifdef CONFIG_MAC80211_MESH
|
||||
case NL80211_IFTYPE_MESH_POINT:
|
||||
|
|
@ -4419,7 +4422,8 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb,
|
|||
ieee80211_mesh_xmit_fast(sdata, skb, ctrl_flags))
|
||||
goto out;
|
||||
|
||||
if (ieee80211_lookup_ra_sta(sdata, skb, &sta))
|
||||
if (ieee80211_lookup_ra_sta(sdata, skb, &sta,
|
||||
skb->protocol == sdata->control_port_protocol))
|
||||
goto out_free;
|
||||
|
||||
if (IS_ERR(sta))
|
||||
|
|
@ -4731,6 +4735,7 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
|
|||
{
|
||||
struct ieee80211_tx_info *info;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_chanctx_conf *chanctx_conf;
|
||||
struct tid_ampdu_tx *tid_tx = NULL;
|
||||
struct sk_buff *seg, *next;
|
||||
unsigned int skbs = 0, len = 0;
|
||||
|
|
@ -4780,6 +4785,16 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
|
|||
sdata = container_of(sdata->bss,
|
||||
struct ieee80211_sub_if_data, u.ap);
|
||||
|
||||
/* MLD transmissions must not rely on the band */
|
||||
if (!ieee80211_vif_is_mld(&sdata->vif)) {
|
||||
chanctx_conf = rcu_dereference(sdata->vif.bss_conf.chanctx_conf);
|
||||
if (unlikely(!chanctx_conf)) {
|
||||
kfree_skb_list(skb);
|
||||
return;
|
||||
}
|
||||
info->band = chanctx_conf->def.chan->band;
|
||||
}
|
||||
|
||||
info->flags |= IEEE80211_TX_CTL_HW_80211_ENCAP;
|
||||
info->control.vif = &sdata->vif;
|
||||
|
||||
|
|
@ -4848,7 +4863,10 @@ static void __ieee80211_subif_start_xmit_8023(struct sk_buff *skb,
|
|||
|
||||
rcu_read_lock();
|
||||
|
||||
if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) {
|
||||
if (unlikely(sdata->control_port_protocol == ehdr->h_proto))
|
||||
goto skip_offload;
|
||||
|
||||
if (ieee80211_lookup_ra_sta(sdata, skb, &sta, false)) {
|
||||
kfree_skb(skb);
|
||||
goto out;
|
||||
}
|
||||
|
|
@ -4869,8 +4887,7 @@ static void __ieee80211_subif_start_xmit_8023(struct sk_buff *skb,
|
|||
link = &sdata->deflink;
|
||||
key = rcu_dereference(link->default_multicast_key);
|
||||
} else if (unlikely(IS_ERR_OR_NULL(sta) || !sta->uploaded ||
|
||||
!test_sta_flag(sta, WLAN_STA_AUTHORIZED) ||
|
||||
sdata->control_port_protocol == ehdr->h_proto)) {
|
||||
!test_sta_flag(sta, WLAN_STA_AUTHORIZED))) {
|
||||
goto skip_offload;
|
||||
} else {
|
||||
key = rcu_dereference(sta->ptk[sta->ptk_idx]);
|
||||
|
|
@ -4931,7 +4948,7 @@ ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata,
|
|||
|
||||
rcu_read_lock();
|
||||
|
||||
if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) {
|
||||
if (ieee80211_lookup_ra_sta(sdata, skb, &sta, false)) {
|
||||
kfree_skb(skb);
|
||||
skb = ERR_PTR(-EINVAL);
|
||||
goto out;
|
||||
|
|
@ -5002,7 +5019,7 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local,
|
|||
}
|
||||
result = ieee80211_tx(sdata, NULL, skb, true);
|
||||
} else if (info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) {
|
||||
if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) {
|
||||
if (ieee80211_lookup_ra_sta(sdata, skb, &sta, true)) {
|
||||
dev_kfree_skb(skb);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -6639,7 +6656,7 @@ int ieee80211_tx_control_port(struct wiphy *wiphy, struct net_device *dev,
|
|||
* AF_PACKET
|
||||
*/
|
||||
rcu_read_lock();
|
||||
err = ieee80211_lookup_ra_sta(sdata, skb, &sta);
|
||||
err = ieee80211_lookup_ra_sta(sdata, skb, &sta, true);
|
||||
if (err) {
|
||||
dev_kfree_skb(skb);
|
||||
rcu_read_unlock();
|
||||
|
|
|
|||
|
|
@ -4563,12 +4563,13 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
|
|||
rdev->wiphy.retry_long = retry_long;
|
||||
if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
|
||||
rdev->wiphy.frag_threshold = frag_threshold;
|
||||
if ((changed & WIPHY_PARAM_RTS_THRESHOLD) &&
|
||||
old_radio_rts_threshold) {
|
||||
if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
|
||||
rdev->wiphy.rts_threshold = rts_threshold;
|
||||
for (i = 0 ; i < rdev->wiphy.n_radio; i++)
|
||||
rdev->wiphy.radio_cfg[i].rts_threshold =
|
||||
rdev->wiphy.rts_threshold;
|
||||
if (old_radio_rts_threshold) {
|
||||
for (i = 0; i < rdev->wiphy.n_radio; i++)
|
||||
rdev->wiphy.radio_cfg[i].rts_threshold =
|
||||
rdev->wiphy.rts_threshold;
|
||||
}
|
||||
}
|
||||
if (changed & WIPHY_PARAM_COVERAGE_CLASS)
|
||||
rdev->wiphy.coverage_class = coverage_class;
|
||||
|
|
|
|||
|
|
@ -1992,20 +1992,22 @@ cfg80211_update_known_bss(struct cfg80211_registered_device *rdev,
|
|||
return false;
|
||||
}
|
||||
|
||||
old = rcu_access_pointer(known->pub.beacon_ies);
|
||||
if (!new->pub.hidden_beacon_bss) {
|
||||
old = rcu_access_pointer(known->pub.beacon_ies);
|
||||
|
||||
rcu_assign_pointer(known->pub.beacon_ies, new->pub.beacon_ies);
|
||||
rcu_assign_pointer(known->pub.beacon_ies, new->pub.beacon_ies);
|
||||
|
||||
/* Override IEs if they were from a beacon before */
|
||||
if (old == rcu_access_pointer(known->pub.ies))
|
||||
rcu_assign_pointer(known->pub.ies, new->pub.beacon_ies);
|
||||
/* Override IEs if they were from a beacon before */
|
||||
if (old == rcu_access_pointer(known->pub.ies))
|
||||
rcu_assign_pointer(known->pub.ies, new->pub.beacon_ies);
|
||||
|
||||
cfg80211_update_hidden_bsses(known,
|
||||
rcu_access_pointer(new->pub.beacon_ies),
|
||||
old);
|
||||
cfg80211_update_hidden_bsses(known,
|
||||
rcu_access_pointer(new->pub.beacon_ies),
|
||||
old);
|
||||
|
||||
if (old)
|
||||
kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head);
|
||||
if (old)
|
||||
kfree_rcu((struct cfg80211_bss_ies *)old, rcu_head);
|
||||
}
|
||||
}
|
||||
|
||||
known->pub.beacon_interval = new->pub.beacon_interval;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user