mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 20:54:03 +02:00
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:
commit
fefaac1176
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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))) {
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 +
|
||||
|
|
|
|||
|
|
@ -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!");
|
||||
|
|
|
|||
|
|
@ -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 +
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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) +
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -8966,10 +8966,12 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
|
|||
EXPORT_SYMBOL(cfg80211_check_station_change);
|
||||
|
||||
/*
|
||||
* Get vlan interface making sure it is running and on the right wiphy.
|
||||
* Get vlan interface making sure it is running, on the right wiphy
|
||||
* and actually belongs to the given AP/P2P_GO interface.
|
||||
*/
|
||||
static struct net_device *get_vlan(struct genl_info *info,
|
||||
struct cfg80211_registered_device *rdev)
|
||||
struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev)
|
||||
{
|
||||
struct nlattr *vlanattr = info->attrs[NL80211_ATTR_STA_VLAN];
|
||||
struct net_device *v;
|
||||
|
|
@ -8999,6 +9001,12 @@ static struct net_device *get_vlan(struct genl_info *info,
|
|||
goto error;
|
||||
}
|
||||
|
||||
/* Check if the VLAN interface belongs to the AP interface */
|
||||
if (!dev || !ether_addr_equal(v->dev_addr, dev->dev_addr)) {
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
return v;
|
||||
error:
|
||||
dev_put(v);
|
||||
|
|
@ -9296,7 +9304,7 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
params.vlan = get_vlan(info, rdev);
|
||||
params.vlan = get_vlan(info, rdev, dev);
|
||||
if (IS_ERR(params.vlan))
|
||||
return PTR_ERR(params.vlan);
|
||||
|
||||
|
|
@ -9328,7 +9336,7 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
|
|||
static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||
int err;
|
||||
int err, link_id;
|
||||
struct wireless_dev *wdev = info->user_ptr[1];
|
||||
struct net_device *dev = wdev->netdev;
|
||||
struct station_parameters params;
|
||||
|
|
@ -9376,6 +9384,16 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
|
|||
params.link_sta_params.link_id =
|
||||
nl80211_link_id_or_invalid(info->attrs);
|
||||
|
||||
if (wdev->valid_links) {
|
||||
if (params.link_sta_params.link_id < 0)
|
||||
return -EINVAL;
|
||||
if (!(wdev->valid_links & BIT(params.link_sta_params.link_id)))
|
||||
return -ENOLINK;
|
||||
} else {
|
||||
if (params.link_sta_params.link_id >= 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (info->attrs[NL80211_ATTR_MLD_ADDR]) {
|
||||
mac_addr = nla_data(info->attrs[NL80211_ATTR_MLD_ADDR]);
|
||||
params.link_sta_params.mld_mac = mac_addr;
|
||||
|
|
@ -9556,8 +9574,12 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
|
|||
|
||||
switch (wdev->iftype) {
|
||||
case NL80211_IFTYPE_AP:
|
||||
case NL80211_IFTYPE_AP_VLAN:
|
||||
case NL80211_IFTYPE_P2P_GO:
|
||||
/* Add a new station only after the AP and link has been started */
|
||||
link_id = wdev->valid_links ? params.link_sta_params.link_id : 0;
|
||||
if (!wdev->links[link_id].ap.beacon_interval)
|
||||
return -ENETDOWN;
|
||||
|
||||
/* ignore WME attributes if iface/sta is not capable */
|
||||
if (!(rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) ||
|
||||
!(params.sta_flags_set & BIT(NL80211_STA_FLAG_WME)))
|
||||
|
|
@ -9597,11 +9619,24 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
|
|||
}
|
||||
|
||||
/* must be last in here for error handling */
|
||||
params.vlan = get_vlan(info, rdev);
|
||||
params.vlan = get_vlan(info, rdev, dev);
|
||||
if (IS_ERR(params.vlan))
|
||||
return PTR_ERR(params.vlan);
|
||||
break;
|
||||
case NL80211_IFTYPE_MESH_POINT:
|
||||
/*
|
||||
* Add a new station only after the mesh has been started.
|
||||
* libertas doesn't implement join_mesh(); it configures the
|
||||
* mesh via sysfs and joins it when the channel is set, so
|
||||
* use that as the started indication instead.
|
||||
*/
|
||||
if (rdev->ops->libertas_set_mesh_channel) {
|
||||
if (!wdev->u.mesh.chandef.chan)
|
||||
return -ENETDOWN;
|
||||
} else if (!wdev->u.mesh.beacon_interval) {
|
||||
return -ENETDOWN;
|
||||
}
|
||||
|
||||
/* ignore uAPSD data */
|
||||
params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD;
|
||||
|
||||
|
|
@ -9649,27 +9684,10 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
|
|||
|
||||
/* be aware of params.vlan when changing code here */
|
||||
|
||||
if (wdev->valid_links) {
|
||||
if (params.link_sta_params.link_id < 0) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (!(wdev->valid_links & BIT(params.link_sta_params.link_id))) {
|
||||
err = -ENOLINK;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
if (params.link_sta_params.link_id >= 0) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
params.epp_peer =
|
||||
nla_get_flag(info->attrs[NL80211_ATTR_EPP_PEER]);
|
||||
|
||||
err = rdev_add_station(rdev, wdev, mac_addr, ¶ms);
|
||||
out:
|
||||
dev_put(params.vlan);
|
||||
return err;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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[];
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user