mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-28 17:48:58 +00:00
181 lines
5.4 KiB
Diff
181 lines
5.4 KiB
Diff
|
From: Wen Gong <wgong@codeaurora.org>
|
||
|
Date: Tue, 11 May 2021 20:02:52 +0200
|
||
|
Subject: [PATCH] ath10k: add CCMP PN replay protection for fragmented
|
||
|
frames for PCIe
|
||
|
|
||
|
PN replay check for not fragmented frames is finished in the firmware,
|
||
|
but this was not done for fragmented frames when ath10k is used with
|
||
|
QCA6174/QCA6377 PCIe. mac80211 has the function
|
||
|
ieee80211_rx_h_defragment() for PN replay check for fragmented frames,
|
||
|
but this does not get checked with QCA6174 due to the
|
||
|
ieee80211_has_protected() condition not matching the cleared Protected
|
||
|
bit case.
|
||
|
|
||
|
Validate the PN of received fragmented frames within ath10k when CCMP is
|
||
|
used and drop the fragment if the PN is not correct (incremented by
|
||
|
exactly one from the previous fragment). This applies only for
|
||
|
QCA6174/QCA6377 PCIe.
|
||
|
|
||
|
Tested-on: QCA6174 hw3.2 PCI WLAN.RM.4.4.1-00110-QCARMSWP-1
|
||
|
|
||
|
Cc: stable@vger.kernel.org
|
||
|
Signed-off-by: Wen Gong <wgong@codeaurora.org>
|
||
|
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
|
||
|
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
||
|
---
|
||
|
|
||
|
--- a/drivers/net/wireless/ath/ath10k/htt.h
|
||
|
+++ b/drivers/net/wireless/ath/ath10k/htt.h
|
||
|
@@ -846,6 +846,7 @@ enum htt_security_types {
|
||
|
|
||
|
#define ATH10K_HTT_TXRX_PEER_SECURITY_MAX 2
|
||
|
#define ATH10K_TXRX_NUM_EXT_TIDS 19
|
||
|
+#define ATH10K_TXRX_NON_QOS_TID 16
|
||
|
|
||
|
enum htt_security_flags {
|
||
|
#define HTT_SECURITY_TYPE_MASK 0x7F
|
||
|
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
|
||
|
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
|
||
|
@@ -1746,16 +1746,87 @@ static void ath10k_htt_rx_h_csum_offload
|
||
|
msdu->ip_summed = ath10k_htt_rx_get_csum_state(msdu);
|
||
|
}
|
||
|
|
||
|
+static u64 ath10k_htt_rx_h_get_pn(struct ath10k *ar, struct sk_buff *skb,
|
||
|
+ u16 offset,
|
||
|
+ enum htt_rx_mpdu_encrypt_type enctype)
|
||
|
+{
|
||
|
+ struct ieee80211_hdr *hdr;
|
||
|
+ u64 pn = 0;
|
||
|
+ u8 *ehdr;
|
||
|
+
|
||
|
+ hdr = (struct ieee80211_hdr *)(skb->data + offset);
|
||
|
+ ehdr = skb->data + offset + ieee80211_hdrlen(hdr->frame_control);
|
||
|
+
|
||
|
+ if (enctype == HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2) {
|
||
|
+ pn = ehdr[0];
|
||
|
+ pn |= (u64)ehdr[1] << 8;
|
||
|
+ pn |= (u64)ehdr[4] << 16;
|
||
|
+ pn |= (u64)ehdr[5] << 24;
|
||
|
+ pn |= (u64)ehdr[6] << 32;
|
||
|
+ pn |= (u64)ehdr[7] << 40;
|
||
|
+ }
|
||
|
+ return pn;
|
||
|
+}
|
||
|
+
|
||
|
+static bool ath10k_htt_rx_h_frag_pn_check(struct ath10k *ar,
|
||
|
+ struct sk_buff *skb,
|
||
|
+ u16 peer_id,
|
||
|
+ u16 offset,
|
||
|
+ enum htt_rx_mpdu_encrypt_type enctype)
|
||
|
+{
|
||
|
+ struct ath10k_peer *peer;
|
||
|
+ union htt_rx_pn_t *last_pn, new_pn = {0};
|
||
|
+ struct ieee80211_hdr *hdr;
|
||
|
+ bool more_frags;
|
||
|
+ u8 tid, frag_number;
|
||
|
+ u32 seq;
|
||
|
+
|
||
|
+ peer = ath10k_peer_find_by_id(ar, peer_id);
|
||
|
+ if (!peer) {
|
||
|
+ ath10k_dbg(ar, ATH10K_DBG_HTT, "invalid peer for frag pn check\n");
|
||
|
+ return false;
|
||
|
+ }
|
||
|
+
|
||
|
+ hdr = (struct ieee80211_hdr *)(skb->data + offset);
|
||
|
+ if (ieee80211_is_data_qos(hdr->frame_control))
|
||
|
+ tid = ieee80211_get_tid(hdr);
|
||
|
+ else
|
||
|
+ tid = ATH10K_TXRX_NON_QOS_TID;
|
||
|
+
|
||
|
+ last_pn = &peer->frag_tids_last_pn[tid];
|
||
|
+ new_pn.pn48 = ath10k_htt_rx_h_get_pn(ar, skb, offset, enctype);
|
||
|
+ more_frags = ieee80211_has_morefrags(hdr->frame_control);
|
||
|
+ frag_number = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG;
|
||
|
+ seq = (__le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4;
|
||
|
+
|
||
|
+ if (frag_number == 0) {
|
||
|
+ last_pn->pn48 = new_pn.pn48;
|
||
|
+ peer->frag_tids_seq[tid] = seq;
|
||
|
+ } else {
|
||
|
+ if (seq != peer->frag_tids_seq[tid])
|
||
|
+ return false;
|
||
|
+
|
||
|
+ if (new_pn.pn48 != last_pn->pn48 + 1)
|
||
|
+ return false;
|
||
|
+
|
||
|
+ last_pn->pn48 = new_pn.pn48;
|
||
|
+ }
|
||
|
+
|
||
|
+ return true;
|
||
|
+}
|
||
|
+
|
||
|
static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
|
||
|
struct sk_buff_head *amsdu,
|
||
|
struct ieee80211_rx_status *status,
|
||
|
bool fill_crypt_header,
|
||
|
u8 *rx_hdr,
|
||
|
- enum ath10k_pkt_rx_err *err)
|
||
|
+ enum ath10k_pkt_rx_err *err,
|
||
|
+ u16 peer_id,
|
||
|
+ bool frag)
|
||
|
{
|
||
|
struct sk_buff *first;
|
||
|
struct sk_buff *last;
|
||
|
- struct sk_buff *msdu;
|
||
|
+ struct sk_buff *msdu, *temp;
|
||
|
struct htt_rx_desc *rxd;
|
||
|
struct ieee80211_hdr *hdr;
|
||
|
enum htt_rx_mpdu_encrypt_type enctype;
|
||
|
@@ -1768,6 +1839,7 @@ static void ath10k_htt_rx_h_mpdu(struct
|
||
|
bool is_decrypted;
|
||
|
bool is_mgmt;
|
||
|
u32 attention;
|
||
|
+ bool frag_pn_check = true;
|
||
|
|
||
|
if (skb_queue_empty(amsdu))
|
||
|
return;
|
||
|
@@ -1866,6 +1938,24 @@ static void ath10k_htt_rx_h_mpdu(struct
|
||
|
}
|
||
|
|
||
|
skb_queue_walk(amsdu, msdu) {
|
||
|
+ if (frag && !fill_crypt_header && is_decrypted &&
|
||
|
+ enctype == HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2)
|
||
|
+ frag_pn_check = ath10k_htt_rx_h_frag_pn_check(ar,
|
||
|
+ msdu,
|
||
|
+ peer_id,
|
||
|
+ 0,
|
||
|
+ enctype);
|
||
|
+
|
||
|
+ if (!frag_pn_check) {
|
||
|
+ /* Discard the fragment with invalid PN */
|
||
|
+ temp = msdu->prev;
|
||
|
+ __skb_unlink(msdu, amsdu);
|
||
|
+ dev_kfree_skb_any(msdu);
|
||
|
+ msdu = temp;
|
||
|
+ frag_pn_check = true;
|
||
|
+ continue;
|
||
|
+ }
|
||
|
+
|
||
|
ath10k_htt_rx_h_csum_offload(msdu);
|
||
|
ath10k_htt_rx_h_undecap(ar, msdu, status, first_hdr, enctype,
|
||
|
is_decrypted);
|
||
|
@@ -2071,7 +2161,8 @@ static int ath10k_htt_rx_handle_amsdu(st
|
||
|
ath10k_htt_rx_h_unchain(ar, &amsdu, &drop_cnt, &unchain_cnt);
|
||
|
|
||
|
ath10k_htt_rx_h_filter(ar, &amsdu, rx_status, &drop_cnt_filter);
|
||
|
- ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status, true, first_hdr, &err);
|
||
|
+ ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status, true, first_hdr, &err, 0,
|
||
|
+ false);
|
||
|
msdus_to_queue = skb_queue_len(&amsdu);
|
||
|
ath10k_htt_rx_h_enqueue(ar, &amsdu, rx_status);
|
||
|
|
||
|
@@ -3027,7 +3118,7 @@ static int ath10k_htt_rx_in_ord_ind(stru
|
||
|
ath10k_htt_rx_h_ppdu(ar, &amsdu, status, vdev_id);
|
||
|
ath10k_htt_rx_h_filter(ar, &amsdu, status, NULL);
|
||
|
ath10k_htt_rx_h_mpdu(ar, &amsdu, status, false, NULL,
|
||
|
- NULL);
|
||
|
+ NULL, peer_id, frag);
|
||
|
ath10k_htt_rx_h_enqueue(ar, &amsdu, status);
|
||
|
break;
|
||
|
case -EAGAIN:
|