diff options
Diffstat (limited to 'drivers/net/wireless/ath/wil6210/txrx.c')
-rw-r--r-- | drivers/net/wireless/ath/wil6210/txrx.c | 63 |
1 files changed, 63 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 6af20903cf89..f383001b86aa 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -549,6 +549,60 @@ static int wil_rx_refill(struct wil6210_priv *wil, int count) return rc; } +/** + * reverse_memcmp - Compare two areas of memory, in reverse order + * @cs: One area of memory + * @ct: Another area of memory + * @count: The size of the area. + * + * Cut'n'paste from original memcmp (see lib/string.c) + * with minimal modifications + */ +static int reverse_memcmp(const void *cs, const void *ct, size_t count) +{ + const unsigned char *su1, *su2; + int res = 0; + + for (su1 = cs + count - 1, su2 = ct + count - 1; count > 0; + --su1, --su2, count--) { + res = *su1 - *su2; + if (res) + break; + } + return res; +} + +static int wil_rx_crypto_check(struct wil6210_priv *wil, struct sk_buff *skb) +{ + struct vring_rx_desc *d = wil_skb_rxdesc(skb); + int cid = wil_rxdesc_cid(d); + int tid = wil_rxdesc_tid(d); + int key_id = wil_rxdesc_key_id(d); + int mc = wil_rxdesc_mcast(d); + struct wil_sta_info *s = &wil->sta[cid]; + struct wil_tid_crypto_rx *c = mc ? &s->group_crypto_rx : + &s->tid_crypto_rx[tid]; + struct wil_tid_crypto_rx_single *cc = &c->key_id[key_id]; + const u8 *pn = (u8 *)&d->mac.pn_15_0; + + if (!cc->key_set) { + wil_err_ratelimited(wil, + "Key missing. CID %d TID %d MCast %d KEY_ID %d\n", + cid, tid, mc, key_id); + return -EINVAL; + } + + if (reverse_memcmp(pn, cc->pn, IEEE80211_GCMP_PN_LEN) <= 0) { + wil_err_ratelimited(wil, + "Replay attack. CID %d TID %d MCast %d KEY_ID %d PN %6phN last %6phN\n", + cid, tid, mc, key_id, pn, cc->pn); + return -EINVAL; + } + memcpy(cc->pn, pn, IEEE80211_GCMP_PN_LEN); + + return 0; +} + /* * Pass Rx packet to the netif. Update statistics. * Called in softirq context (NAPI poll). @@ -561,6 +615,7 @@ void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev) unsigned int len = skb->len; struct vring_rx_desc *d = wil_skb_rxdesc(skb); int cid = wil_rxdesc_cid(d); /* always 0..7, no need to check */ + int security = wil_rxdesc_security(d); struct ethhdr *eth = (void *)skb->data; /* here looking for DA, not A1, thus Rxdesc's 'mcast' indication * is not suitable, need to look at data @@ -586,6 +641,13 @@ void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev) skb_orphan(skb); + if (security && (wil_rx_crypto_check(wil, skb) != 0)) { + rc = GRO_DROP; + dev_kfree_skb(skb); + stats->rx_replay++; + goto stats; + } + if (wdev->iftype == NL80211_IFTYPE_AP && !wil->ap_isolate) { if (mcast) { /* send multicast frames both to higher layers in @@ -627,6 +689,7 @@ void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev) wil_dbg_txrx(wil, "Rx complete %d bytes => %s\n", len, gro_res_str[rc]); } +stats: /* statistics. rc set to GRO_NORMAL for AP bridging */ if (unlikely(rc == GRO_DROP)) { ndev->stats.rx_dropped++; |