diff --git a/mac80211.c b/mac80211.c index 1d8b8ee1..8c0781fb 100644 --- a/mac80211.c +++ b/mac80211.c @@ -435,6 +435,7 @@ mt76_check_ccmp_pn(struct sk_buff *skb) { struct mt76_rx_status *status = (struct mt76_rx_status *) skb->cb; struct mt76_wcid *wcid = status->wcid; + struct ieee80211_hdr *hdr; int ret; if (!(status->flag & RX_FLAG_DECRYPTED)) @@ -443,6 +444,17 @@ mt76_check_ccmp_pn(struct sk_buff *skb) if (!wcid || !wcid->rx_check_pn) return 0; + if (!(status->flag & RX_FLAG_IV_STRIPPED)) { + /* + * Validate the first fragment both here and in mac80211 + * All further fragments will be validated by mac80211 only. + */ + hdr = (struct ieee80211_hdr *) skb->data; + if (ieee80211_is_frag(hdr) && + !ieee80211_is_first_frag(hdr->frame_control)) + return 0; + } + BUILD_BUG_ON(sizeof(status->iv) != sizeof(wcid->rx_key_pn[0])); ret = memcmp(status->iv, wcid->rx_key_pn[status->tid], sizeof(status->iv)); @@ -450,7 +462,9 @@ mt76_check_ccmp_pn(struct sk_buff *skb) return -EINVAL; /* replay */ memcpy(wcid->rx_key_pn[status->tid], status->iv, sizeof(status->iv)); - status->flag |= RX_FLAG_PN_VALIDATED; + + if (status->flag & RX_FLAG_IV_STRIPPED) + status->flag |= RX_FLAG_PN_VALIDATED; return 0; } diff --git a/mt7603_mac.c b/mt7603_mac.c index e8fea243..1ba9d012 100644 --- a/mt7603_mac.c +++ b/mt7603_mac.c @@ -367,6 +367,30 @@ mt7603_rx_get_wcid(struct mt7603_dev *dev, u8 idx, bool unicast) return &sta->vif->wcid; } +static void +mt7603_insert_ccmp_hdr(struct sk_buff *skb, u8 key_id) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *) skb->cb; + int hdr_len = ieee80211_get_hdrlen_from_skb(skb); + u8 *pn = status->iv; + u8 *hdr; + + __skb_push(skb, 8); + memmove(skb->data, skb->data + 8, hdr_len); + hdr = skb->data + hdr_len; + + hdr[0] = pn[5]; + hdr[1] = pn[4]; + hdr[2] = 0; + hdr[3] = 0x20 | (key_id << 6); + hdr[4] = pn[3]; + hdr[5] = pn[2]; + hdr[6] = pn[1]; + hdr[7] = pn[0]; + + status->flag &= ~RX_FLAG_IV_STRIPPED; +} + int mt7603_mac_fill_rx(struct mt7603_dev *dev, struct sk_buff *skb) { @@ -378,6 +402,7 @@ mt7603_mac_fill_rx(struct mt7603_dev *dev, struct sk_buff *skb) u32 rxd1 = le32_to_cpu(rxd[1]); u32 rxd2 = le32_to_cpu(rxd[2]); bool unicast = rxd1 & MT_RXD1_NORMAL_U2M; + bool insert_ccmp_hdr = false; bool remove_pad; int idx; int i; @@ -404,7 +429,8 @@ mt7603_mac_fill_rx(struct mt7603_dev *dev, struct sk_buff *skb) if (FIELD_GET(MT_RXD2_NORMAL_SEC_MODE, rxd2) != 0 && !(rxd2 & (MT_RXD2_NORMAL_CLM | MT_RXD2_NORMAL_CM))) { status->flag |= RX_FLAG_DECRYPTED; - status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED; + status->flag |= RX_FLAG_IV_STRIPPED; + status->flag |= RX_FLAG_MMIC_STRIPPED | RX_FLAG_MIC_STRIPPED; } remove_pad = rxd1 & MT_RXD1_NORMAL_HDR_OFFSET; @@ -424,12 +450,16 @@ mt7603_mac_fill_rx(struct mt7603_dev *dev, struct sk_buff *skb) if (rxd0 & MT_RXD0_NORMAL_GROUP_1) { u8 *data = (u8 *) rxd; - status->iv[0] = data[5]; - status->iv[1] = data[4]; - status->iv[2] = data[3]; - status->iv[3] = data[2]; - status->iv[4] = data[1]; - status->iv[5] = data[0]; + if (status->flag & RX_FLAG_DECRYPTED) { + status->iv[0] = data[5]; + status->iv[1] = data[4]; + status->iv[2] = data[3]; + status->iv[3] = data[2]; + status->iv[4] = data[1]; + status->iv[5] = data[0]; + + insert_ccmp_hdr = FIELD_GET(MT_RXD2_NORMAL_FRAG, rxd2); + } rxd += 4; if ((u8 *) rxd - skb->data >= skb->len) @@ -486,6 +516,11 @@ mt7603_mac_fill_rx(struct mt7603_dev *dev, struct sk_buff *skb) skb_pull(skb, (u8 *) rxd - skb->data + 2 * remove_pad); + if (insert_ccmp_hdr) { + u8 key_id = FIELD_GET(MT_RXD1_NORMAL_KEY_ID, rxd1); + mt7603_insert_ccmp_hdr(skb, key_id); + } + hdr = (struct ieee80211_hdr *) skb->data; if (!status->wcid || !ieee80211_is_data_qos(hdr->frame_control)) return 0; diff --git a/mt76x2_mac.c b/mt76x2_mac.c index c0f44f3c..cd3a219e 100644 --- a/mt76x2_mac.c +++ b/mt76x2_mac.c @@ -317,6 +317,16 @@ int mt76x2_mac_process_rx(struct mt76x2_dev *dev, struct sk_buff *skb, if (rxinfo & MT_RXINFO_L2PAD) pad_len += 2; + if (rxinfo & MT_RXINFO_DECRYPT) { + status->flag |= RX_FLAG_DECRYPTED; + status->flag |= RX_FLAG_MMIC_STRIPPED; + status->flag |= RX_FLAG_MIC_STRIPPED; + status->flag |= RX_FLAG_IV_STRIPPED; + } + + wcid = FIELD_GET(MT_RXWI_CTL_WCID, ctl); + status->wcid = mt76x2_rx_get_sta_wcid(dev, wcid, unicast); + len = FIELD_GET(MT_RXWI_CTL_MPDU_LEN, ctl); pn_len = FIELD_GET(MT_RXINFO_PN_LEN, rxinfo); if (pn_len) { @@ -330,23 +340,23 @@ int mt76x2_mac_process_rx(struct mt76x2_dev *dev, struct sk_buff *skb, status->iv[4] = data[1]; status->iv[5] = data[0]; - pad_len += pn_len << 2; - len -= pn_len << 2; + /* + * Driver CCMP validation can't deal with fragments. + * Let mac80211 take care of it. + */ + if (rxinfo & MT_RXINFO_FRAG) { + status->flag &= ~RX_FLAG_IV_STRIPPED; + } else { + pad_len += pn_len << 2; + len -= pn_len << 2; + } } mt76x2_remove_hdr_pad(skb, pad_len); - wcid = FIELD_GET(MT_RXWI_CTL_WCID, ctl); - status->wcid = mt76x2_rx_get_sta_wcid(dev, wcid, unicast); - if (rxinfo & MT_RXINFO_BA) status->aggr = true; - if (rxinfo & MT_RXINFO_DECRYPT) { - status->flag |= RX_FLAG_DECRYPTED; - status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED; - } - if (WARN_ON_ONCE(len > skb->len)) return -EINVAL;