wil6210: check integrity of received AMSDU packets

Check integrity of received AMSDU packet, 802.11ad QoS spec requires
that AMSDU frame contains only MSDUs whose destination address (DA)
and sender address (SA) parameter values map to the same receiver
address (RA) and transmitter address (TA) values.
wil6210 Talyn HW does not check this before it cuts the AMSDU frame
into multiple received MSDU packets.
Adding checks to wil6210 driver to enforce spec compliance behavior
by checking all AMSDU sub frames if it complies with the following:
1- On AP, check packet SA is its client mac address, and on Client
   check that the DA is the local mac address. If not drop the packet
2- if AMSDU sub frame was dropped on item 1, drop all next sub frame
   of the AMSDU by checking it has same sn/tid.

This patch drops all WDS frames before it checks valid AMSDU, WDS
includes supporting MAC header with 4 addresses which is not supported
yet, moreover, WDS implies different validity checks on AMSDU frame.

Change-Id: I71a39f95c034f05023e0e7ae3ffb5d2b4f8c6b24
Signed-off-by: Ahmad Masri <amasri@codeaurora.org>
This commit is contained in:
Ahmad Masri 2020-11-15 17:43:32 +02:00 • committed by Gerrit - the friendly Code Review server
commit b2c81339a9
6 changed files with 131 additions and 7 deletions

View file

@ -1,7 +1,7 @@
// SPDX-License-Identifier: ISC
/*
* Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
* Copyright (c) 2018-2021, The Linux Foundation. All rights reserved.
*/
#include <linux/moduleparam.h>
@ -310,6 +310,7 @@ __acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock)
/* statistics */
memset(&sta->stats, 0, sizeof(sta->stats));
sta->stats.tx_latency_min_us = U32_MAX;
wil_sta_info_amsdu_init(sta);
}
static void _wil6210_disconnect_complete(struct wil6210_vif *vif,
@ -712,6 +713,13 @@ void wil_bcast_fini_all(struct wil6210_priv *wil)
}
}
void wil_sta_info_amsdu_init(struct wil_sta_info *sta)
{
sta->amsdu_drop_sn = -1;
sta->amsdu_drop_tid = -1;
sta->amsdu_drop = 0;
}
int wil_priv_init(struct wil6210_priv *wil)
{
uint i;

View file

@ -1,7 +1,7 @@
// SPDX-License-Identifier: ISC
/*
* Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
* Copyright (c) 2018-2021, The Linux Foundation. All rights reserved.
*/
#include <linux/module.h>
@ -98,7 +98,6 @@ int wil_set_capabilities(struct wil6210_priv *wil)
set_bit(hw_capa_no_flash, wil->hw_capa);
wil->use_enhanced_dma_hw = true;
wil->use_rx_hw_reordering = true;
wil->use_compressed_rx_status = true;
if (wil_ipa_offload())
/* IPA offload must use single MSI */
n_msi = 1;

View file

@ -1,6 +1,6 @@
// SPDX-License-Identifier: ISC
/*
* Copyright (c) 2012-2019 The Linux Foundation. All rights reserved.
* Copyright (c) 2012-2021 The Linux Foundation. All rights reserved.
*/
#include <linux/etherdevice.h>
@ -806,6 +806,92 @@ static int wil_tx_ring_modify_edma(struct wil6210_vif *vif, int ring_id,
return -EOPNOTSUPP;
}
static int wil_check_amsdu(struct wil6210_priv *wil, void *msg, int cid,
struct wil_ring_rx_data *rxdata,
struct sk_buff *skb)
{
u8 *sa, *da;
int mid, tid;
u16 seq;
struct wil6210_vif *vif;
struct net_device *ndev;
struct wil_sta_info *sta;
/* drop all WDS packets - not supported */
if (wil_rx_status_get_ds_type(wil, msg) == WIL_RX_EDMA_DS_TYPE_WDS) {
wil_dbg_txrx(wil, "WDS is not supported");
return -EAGAIN;
}
/* check amsdu packets */
sta = &wil->sta[cid];
if (!wil_rx_status_is_basic_amsdu(msg)) {
if (sta->amsdu_drop_sn != -1)
wil_sta_info_amsdu_init(sta);
return 0;
}
mid = wil_rx_status_get_mid(msg);
tid = wil_rx_status_get_tid(msg);
seq = le16_to_cpu(wil_rx_status_get_seq(wil, msg));
vif = wil->vifs[mid];
if (unlikely(!vif)) {
wil_dbg_txrx(wil, "amsdu with invalid mid %d", mid);
return -EAGAIN;
}
if (unlikely(sta->amsdu_drop)) {
if (sta->amsdu_drop_sn == seq && sta->amsdu_drop_tid == tid) {
wil_dbg_txrx(wil, "Drop AMSDU sub frame, sn=%d\n",
seq);
return -EAGAIN;
}
/* previous AMSDU finished - clear drop amsdu flag */
sta->amsdu_drop = 0;
}
da = wil_skb_get_da(skb);
/* for all sub frame of the AMSDU, check that the SA or DA are valid
* compared with client/AP mac addresses
*/
switch (vif->wdev.iftype) {
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_P2P_CLIENT:
if (is_multicast_ether_addr(da))
return 0;
/* On client side, DA should be the client mac address */
ndev = vif_to_ndev(vif);
if (ether_addr_equal(ndev->dev_addr, da))
return 0;
break;
case NL80211_IFTYPE_P2P_GO:
case NL80211_IFTYPE_AP:
sa = wil_skb_get_sa(skb);
/* On AP side, the packet SA should be the client mac address.
* check also the DA is not rfc 1042 header
*/
if (ether_addr_equal(sta->addr, sa) &&
!ether_addr_equal(rfc1042_header, da))
return 0;
break;
default:
return 0;
}
sta->amsdu_drop_sn = seq;
sta->amsdu_drop_tid = tid;
sta->amsdu_drop = 1;
wil_dbg_txrx(wil,
"Drop AMSDU frame, sn=%d. Drop this and all next sub frames\n",
seq);
return -EAGAIN;
}
/* This function is used only for RX SW reorder */
static int wil_check_bar(struct wil6210_priv *wil, void *msg, int cid,
struct sk_buff *skb, struct wil_net_stats *stats)
@ -1107,6 +1193,12 @@ skipping:
wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1,
skb->data, skb_headlen(skb), false);
if (!wil->use_compressed_rx_status &&
wil_check_amsdu(wil, msg, cid, rxdata, skb)) {
kfree_skb(skb);
goto again;
}
/* Has to be done after dma_unmap_single as skb->cb is also
* used for holding the pa
*/

View file

@ -1,6 +1,6 @@
/* SPDX-License-Identifier: ISC */
/*
* Copyright (c) 2012-2016,2018-2019, The Linux Foundation. All rights reserved.
* Copyright (c) 2012-2016,2018-2021, The Linux Foundation. All rights reserved.
*/
#ifndef WIL6210_TXRX_EDMA_H
@ -47,6 +47,9 @@
#define WIL_RX_EDMA_MID_VALID_BIT BIT(22)
#define WIL_RX_EDMA_AMSDU_BASIC_MASK 0x1
#define WIL_RX_EDMA_DS_TYPE_WDS 0x3
#define WIL_EDMA_DESC_TX_MAC_CFG_0_QID_POS 16
#define WIL_EDMA_DESC_TX_MAC_CFG_0_QID_LEN 6
@ -457,6 +460,21 @@ static inline int wil_rx_status_get_fc1(struct wil6210_priv *wil, void *msg)
0, 5) << 2;
}
static inline int wil_rx_status_get_ds_type(struct wil6210_priv *wil, void *msg)
{
if (wil->use_compressed_rx_status)
return 0;
return WIL_GET_BITS(((struct wil_rx_status_extended *)msg)->ext.d0,
19, 20);
}
static inline int wil_rx_status_is_basic_amsdu(void *msg)
{
return (WIL_GET_BITS(((struct wil_rx_status_compressed *)msg)->d1,
28, 29) == WIL_RX_EDMA_AMSDU_BASIC_MASK);
}
static inline __le16 wil_rx_status_get_seq(struct wil6210_priv *wil, void *msg)
{
if (wil->use_compressed_rx_status)

View file

@ -1,7 +1,7 @@
/* SPDX-License-Identifier: ISC */
/*
* Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
* Copyright (c) 2018-2021, The Linux Foundation. All rights reserved.
*/
#ifndef __WIL6210_H__
@ -783,6 +783,11 @@ struct wil_sta_info {
struct wil_tid_crypto_rx group_crypto_rx;
u8 aid; /* 1-254; 0 if unknown/not reported */
u8 fst_link_loss;
/* amsdu frame related info to check if the frame is valid */
int amsdu_drop_sn;
int amsdu_drop_tid;
u8 amsdu_drop;
};
enum {
@ -1563,4 +1568,5 @@ void update_supported_bands(struct wil6210_priv *wil);
int wmi_reset_spi_slave(struct wil6210_priv *wil);
void wil_clear_fw_log_addr(struct wil6210_priv *wil);
void wil_sta_info_amsdu_init(struct wil_sta_info *sta);
#endif /* __WIL6210_H__ */

View file

@ -1,7 +1,7 @@
// SPDX-License-Identifier: ISC
/*
* Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
* Copyright (c) 2018-2021, The Linux Foundation. All rights reserved.
*/
#include <linux/moduleparam.h>
@ -1092,6 +1092,7 @@ static void wmi_evt_connect(struct wil6210_vif *vif, int id, void *d, int len)
ether_addr_copy(wil->sta[evt->cid].addr, evt->bssid);
wil->sta[evt->cid].mid = vif->mid;
wil->sta[evt->cid].status = wil_sta_conn_pending;
wil_sta_info_amsdu_init(&wil->sta[evt->cid]);
rc = wil_ring_init_tx(vif, evt->cid);
if (rc) {