Skip to content

Commit a1166b2

Browse files
Wen Gongjmberg-intel
authored andcommitted
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: [email protected] Signed-off-by: Wen Gong <[email protected]> Signed-off-by: Jouni Malinen <[email protected]> Link: https://lore.kernel.org/r/20210511200110.9ba2664866a4.I756e47b67e210dba69966d989c4711ffc02dc6bc@changeid Signed-off-by: Johannes Berg <[email protected]>
1 parent 3edc6b0 commit a1166b2

File tree

2 files changed

+96
-4
lines changed

2 files changed

+96
-4
lines changed

drivers/net/wireless/ath/ath10k/htt.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -845,6 +845,7 @@ enum htt_security_types {
845845

846846
#define ATH10K_HTT_TXRX_PEER_SECURITY_MAX 2
847847
#define ATH10K_TXRX_NUM_EXT_TIDS 19
848+
#define ATH10K_TXRX_NON_QOS_TID 16
848849

849850
enum htt_security_flags {
850851
#define HTT_SECURITY_TYPE_MASK 0x7F

drivers/net/wireless/ath/ath10k/htt_rx.c

Lines changed: 95 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1746,16 +1746,87 @@ static void ath10k_htt_rx_h_csum_offload(struct sk_buff *msdu)
17461746
msdu->ip_summed = ath10k_htt_rx_get_csum_state(msdu);
17471747
}
17481748

1749+
static u64 ath10k_htt_rx_h_get_pn(struct ath10k *ar, struct sk_buff *skb,
1750+
u16 offset,
1751+
enum htt_rx_mpdu_encrypt_type enctype)
1752+
{
1753+
struct ieee80211_hdr *hdr;
1754+
u64 pn = 0;
1755+
u8 *ehdr;
1756+
1757+
hdr = (struct ieee80211_hdr *)(skb->data + offset);
1758+
ehdr = skb->data + offset + ieee80211_hdrlen(hdr->frame_control);
1759+
1760+
if (enctype == HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2) {
1761+
pn = ehdr[0];
1762+
pn |= (u64)ehdr[1] << 8;
1763+
pn |= (u64)ehdr[4] << 16;
1764+
pn |= (u64)ehdr[5] << 24;
1765+
pn |= (u64)ehdr[6] << 32;
1766+
pn |= (u64)ehdr[7] << 40;
1767+
}
1768+
return pn;
1769+
}
1770+
1771+
static bool ath10k_htt_rx_h_frag_pn_check(struct ath10k *ar,
1772+
struct sk_buff *skb,
1773+
u16 peer_id,
1774+
u16 offset,
1775+
enum htt_rx_mpdu_encrypt_type enctype)
1776+
{
1777+
struct ath10k_peer *peer;
1778+
union htt_rx_pn_t *last_pn, new_pn = {0};
1779+
struct ieee80211_hdr *hdr;
1780+
bool more_frags;
1781+
u8 tid, frag_number;
1782+
u32 seq;
1783+
1784+
peer = ath10k_peer_find_by_id(ar, peer_id);
1785+
if (!peer) {
1786+
ath10k_dbg(ar, ATH10K_DBG_HTT, "invalid peer for frag pn check\n");
1787+
return false;
1788+
}
1789+
1790+
hdr = (struct ieee80211_hdr *)(skb->data + offset);
1791+
if (ieee80211_is_data_qos(hdr->frame_control))
1792+
tid = ieee80211_get_tid(hdr);
1793+
else
1794+
tid = ATH10K_TXRX_NON_QOS_TID;
1795+
1796+
last_pn = &peer->frag_tids_last_pn[tid];
1797+
new_pn.pn48 = ath10k_htt_rx_h_get_pn(ar, skb, offset, enctype);
1798+
more_frags = ieee80211_has_morefrags(hdr->frame_control);
1799+
frag_number = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG;
1800+
seq = (__le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4;
1801+
1802+
if (frag_number == 0) {
1803+
last_pn->pn48 = new_pn.pn48;
1804+
peer->frag_tids_seq[tid] = seq;
1805+
} else {
1806+
if (seq != peer->frag_tids_seq[tid])
1807+
return false;
1808+
1809+
if (new_pn.pn48 != last_pn->pn48 + 1)
1810+
return false;
1811+
1812+
last_pn->pn48 = new_pn.pn48;
1813+
}
1814+
1815+
return true;
1816+
}
1817+
17491818
static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
17501819
struct sk_buff_head *amsdu,
17511820
struct ieee80211_rx_status *status,
17521821
bool fill_crypt_header,
17531822
u8 *rx_hdr,
1754-
enum ath10k_pkt_rx_err *err)
1823+
enum ath10k_pkt_rx_err *err,
1824+
u16 peer_id,
1825+
bool frag)
17551826
{
17561827
struct sk_buff *first;
17571828
struct sk_buff *last;
1758-
struct sk_buff *msdu;
1829+
struct sk_buff *msdu, *temp;
17591830
struct htt_rx_desc *rxd;
17601831
struct ieee80211_hdr *hdr;
17611832
enum htt_rx_mpdu_encrypt_type enctype;
@@ -1768,6 +1839,7 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
17681839
bool is_decrypted;
17691840
bool is_mgmt;
17701841
u32 attention;
1842+
bool frag_pn_check = true;
17711843

17721844
if (skb_queue_empty(amsdu))
17731845
return;
@@ -1866,6 +1938,24 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
18661938
}
18671939

18681940
skb_queue_walk(amsdu, msdu) {
1941+
if (frag && !fill_crypt_header && is_decrypted &&
1942+
enctype == HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2)
1943+
frag_pn_check = ath10k_htt_rx_h_frag_pn_check(ar,
1944+
msdu,
1945+
peer_id,
1946+
0,
1947+
enctype);
1948+
1949+
if (!frag_pn_check) {
1950+
/* Discard the fragment with invalid PN */
1951+
temp = msdu->prev;
1952+
__skb_unlink(msdu, amsdu);
1953+
dev_kfree_skb_any(msdu);
1954+
msdu = temp;
1955+
frag_pn_check = true;
1956+
continue;
1957+
}
1958+
18691959
ath10k_htt_rx_h_csum_offload(msdu);
18701960
ath10k_htt_rx_h_undecap(ar, msdu, status, first_hdr, enctype,
18711961
is_decrypted);
@@ -2071,7 +2161,8 @@ static int ath10k_htt_rx_handle_amsdu(struct ath10k_htt *htt)
20712161
ath10k_htt_rx_h_unchain(ar, &amsdu, &drop_cnt, &unchain_cnt);
20722162

20732163
ath10k_htt_rx_h_filter(ar, &amsdu, rx_status, &drop_cnt_filter);
2074-
ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status, true, first_hdr, &err);
2164+
ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status, true, first_hdr, &err, 0,
2165+
false);
20752166
msdus_to_queue = skb_queue_len(&amsdu);
20762167
ath10k_htt_rx_h_enqueue(ar, &amsdu, rx_status);
20772168

@@ -3027,7 +3118,7 @@ static int ath10k_htt_rx_in_ord_ind(struct ath10k *ar, struct sk_buff *skb)
30273118
ath10k_htt_rx_h_ppdu(ar, &amsdu, status, vdev_id);
30283119
ath10k_htt_rx_h_filter(ar, &amsdu, status, NULL);
30293120
ath10k_htt_rx_h_mpdu(ar, &amsdu, status, false, NULL,
3030-
NULL);
3121+
NULL, peer_id, frag);
30313122
ath10k_htt_rx_h_enqueue(ar, &amsdu, status);
30323123
break;
30333124
case -EAGAIN:

0 commit comments

Comments
 (0)