[v2] ath10k: rebuild crypto header in Rx data frames
diff mbox

Message ID 1508867652-16199-1-git-send-email-kvalo@qca.qualcomm.com
State New
Headers show

Commit Message

Kalle Valo Oct. 24, 2017, 5:54 p.m. UTC
From: Vasanthakumar Thiagarajan <vthiagar@qti.qualcomm.com>

Rx data frames notified through HTT_T2H_MSG_TYPE_RX_IND and
HTT_T2H_MSG_TYPE_RX_FRAG_IND expect PN/TSC check to be done
on host (mac80211) rather than firmware. Rebuild cipher header
in every received data frames (that are notified through those
HTT interfaces) from the rx_hdr_status tlv available in the
rx descriptor of the first msdu. Skip setting RX_FLAG_IV_STRIPPED
flag for the packets which requires mac80211 PN/TSC check support
and set appropriate RX_FLAG for stripped crypto tail. Hw QCA988X,
QCA9887, QCA99X0, QCA9984, QCA9888 and QCA4019 currently need the
rebuilding of cipher header to perform PN/TSC check for replay
attack.

Tested-by: Manikanta Pubbisetty <mpubbise@qti.qualcomm.com>
Signed-off-by: Vasanthakumar Thiagarajan <vthiagar@qti.qualcomm.com>
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
---

v2:

* Construct cipher header from rx_hdr_status tlv rather than from
  the msdu_start and mpdu_start tlvs. This fixes connection
  issues, also reduces the amount of code change.

* Make sure the frame is qos data before clearing AMSDU_PRESENT
  bit of qos control field.

* Fix traffic issue with QCA988X and QCA9887 hw by taking care of
  padding bytes added for 4-byte alignment before the cipher
  header.

TODO:

* CCMP-256 and GCMP/GCMP-256 are not yet supported 

 drivers/net/wireless/ath/ath10k/htt_rx.c | 91 ++++++++++++++++++++++++++------
 1 file changed, 74 insertions(+), 17 deletions(-)

Comments

Sebastian Gottschall Oct. 24, 2017, 7:27 p.m. UTC | #1
will test it and report success (or not)

Am 24.10.2017 um 19:54 schrieb Kalle Valo:
> From: Vasanthakumar Thiagarajan <vthiagar@qti.qualcomm.com>
>
> Rx data frames notified through HTT_T2H_MSG_TYPE_RX_IND and
> HTT_T2H_MSG_TYPE_RX_FRAG_IND expect PN/TSC check to be done
> on host (mac80211) rather than firmware. Rebuild cipher header
> in every received data frames (that are notified through those
> HTT interfaces) from the rx_hdr_status tlv available in the
> rx descriptor of the first msdu. Skip setting RX_FLAG_IV_STRIPPED
> flag for the packets which requires mac80211 PN/TSC check support
> and set appropriate RX_FLAG for stripped crypto tail. Hw QCA988X,
> QCA9887, QCA99X0, QCA9984, QCA9888 and QCA4019 currently need the
> rebuilding of cipher header to perform PN/TSC check for replay
> attack.
>
> Tested-by: Manikanta Pubbisetty <mpubbise@qti.qualcomm.com>
> Signed-off-by: Vasanthakumar Thiagarajan <vthiagar@qti.qualcomm.com>
> Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
> ---
>
> v2:
>
> * Construct cipher header from rx_hdr_status tlv rather than from
>    the msdu_start and mpdu_start tlvs. This fixes connection
>    issues, also reduces the amount of code change.
>
> * Make sure the frame is qos data before clearing AMSDU_PRESENT
>    bit of qos control field.
>
> * Fix traffic issue with QCA988X and QCA9887 hw by taking care of
>    padding bytes added for 4-byte alignment before the cipher
>    header.
>
> TODO:
>
> * CCMP-256 and GCMP/GCMP-256 are not yet supported
>
>   drivers/net/wireless/ath/ath10k/htt_rx.c | 91 ++++++++++++++++++++++++++------
>   1 file changed, 74 insertions(+), 17 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
> index a3f5dc78353f..aae4f0ea9360 100644
> --- a/drivers/net/wireless/ath/ath10k/htt_rx.c
> +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
> @@ -1050,8 +1050,14 @@ static void ath10k_htt_rx_h_undecap_raw(struct ath10k *ar,
>   
>   	hdr = (void *)msdu->data;
>   
> -	/* Tail */
> -	if (status->flag & RX_FLAG_IV_STRIPPED)
> +	/* MIC */
> +	if ((status->flag & RX_FLAG_MIC_STRIPPED) &&
> +	    enctype == HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2)
> +		skb_trim(msdu, msdu->len - 8);
> +
> +	/* ICV */
> +	if (status->flag & RX_FLAG_ICV_STRIPPED &&
> +	    enctype != HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2)
>   		skb_trim(msdu, msdu->len -
>   			 ath10k_htt_rx_crypto_tail_len(ar, enctype));
>   
> @@ -1075,7 +1081,8 @@ static void ath10k_htt_rx_h_undecap_raw(struct ath10k *ar,
>   static void ath10k_htt_rx_h_undecap_nwifi(struct ath10k *ar,
>   					  struct sk_buff *msdu,
>   					  struct ieee80211_rx_status *status,
> -					  const u8 first_hdr[64])
> +					  const u8 first_hdr[64],
> +					  enum htt_rx_mpdu_encrypt_type enctype)
>   {
>   	struct ieee80211_hdr *hdr;
>   	struct htt_rx_desc *rxd;
> @@ -1083,6 +1090,7 @@ static void ath10k_htt_rx_h_undecap_nwifi(struct ath10k *ar,
>   	u8 da[ETH_ALEN];
>   	u8 sa[ETH_ALEN];
>   	int l3_pad_bytes;
> +	int bytes_aligned = ar->hw_params.decap_align_bytes;
>   
>   	/* Delivered decapped frame:
>   	 * [nwifi 802.11 header] <-- replaced with 802.11 hdr
> @@ -1111,6 +1119,14 @@ static void ath10k_htt_rx_h_undecap_nwifi(struct ath10k *ar,
>   	/* push original 802.11 header */
>   	hdr = (struct ieee80211_hdr *)first_hdr;
>   	hdr_len = ieee80211_hdrlen(hdr->frame_control);
> +
> +	if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
> +		memcpy(skb_push(msdu,
> +				ath10k_htt_rx_crypto_param_len(ar, enctype)),
> +		       (void *)hdr + round_up(hdr_len, bytes_aligned),
> +			ath10k_htt_rx_crypto_param_len(ar, enctype));
> +	}
> +
>   	memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
>   
>   	/* original 802.11 header has a different DA and in
> @@ -1171,6 +1187,7 @@ static void ath10k_htt_rx_h_undecap_eth(struct ath10k *ar,
>   	u8 sa[ETH_ALEN];
>   	int l3_pad_bytes;
>   	struct htt_rx_desc *rxd;
> +	int bytes_aligned = ar->hw_params.decap_align_bytes;
>   
>   	/* Delivered decapped frame:
>   	 * [eth header] <-- replaced with 802.11 hdr & rfc1042/llc
> @@ -1199,6 +1216,14 @@ static void ath10k_htt_rx_h_undecap_eth(struct ath10k *ar,
>   	/* push original 802.11 header */
>   	hdr = (struct ieee80211_hdr *)first_hdr;
>   	hdr_len = ieee80211_hdrlen(hdr->frame_control);
> +
> +	if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
> +		memcpy(skb_push(msdu,
> +				ath10k_htt_rx_crypto_param_len(ar, enctype)),
> +		       (void *)hdr + round_up(hdr_len, bytes_aligned),
> +			ath10k_htt_rx_crypto_param_len(ar, enctype));
> +	}
> +
>   	memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
>   
>   	/* original 802.11 header has a different DA and in
> @@ -1212,12 +1237,14 @@ static void ath10k_htt_rx_h_undecap_eth(struct ath10k *ar,
>   static void ath10k_htt_rx_h_undecap_snap(struct ath10k *ar,
>   					 struct sk_buff *msdu,
>   					 struct ieee80211_rx_status *status,
> -					 const u8 first_hdr[64])
> +					 const u8 first_hdr[64],
> +					 enum htt_rx_mpdu_encrypt_type enctype)
>   {
>   	struct ieee80211_hdr *hdr;
>   	size_t hdr_len;
>   	int l3_pad_bytes;
>   	struct htt_rx_desc *rxd;
> +	int bytes_aligned = ar->hw_params.decap_align_bytes;
>   
>   	/* Delivered decapped frame:
>   	 * [amsdu header] <-- replaced with 802.11 hdr
> @@ -1233,6 +1260,14 @@ static void ath10k_htt_rx_h_undecap_snap(struct ath10k *ar,
>   
>   	hdr = (struct ieee80211_hdr *)first_hdr;
>   	hdr_len = ieee80211_hdrlen(hdr->frame_control);
> +
> +	if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
> +		memcpy(skb_push(msdu,
> +				ath10k_htt_rx_crypto_param_len(ar, enctype)),
> +		       (void *)hdr + round_up(hdr_len, bytes_aligned),
> +			ath10k_htt_rx_crypto_param_len(ar, enctype));
> +	}
> +
>   	memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
>   }
>   
> @@ -1267,13 +1302,15 @@ static void ath10k_htt_rx_h_undecap(struct ath10k *ar,
>   					    is_decrypted);
>   		break;
>   	case RX_MSDU_DECAP_NATIVE_WIFI:
> -		ath10k_htt_rx_h_undecap_nwifi(ar, msdu, status, first_hdr);
> +		ath10k_htt_rx_h_undecap_nwifi(ar, msdu, status, first_hdr,
> +					      enctype);
>   		break;
>   	case RX_MSDU_DECAP_ETHERNET2_DIX:
>   		ath10k_htt_rx_h_undecap_eth(ar, msdu, status, first_hdr, enctype);
>   		break;
>   	case RX_MSDU_DECAP_8023_SNAP_LLC:
> -		ath10k_htt_rx_h_undecap_snap(ar, msdu, status, first_hdr);
> +		ath10k_htt_rx_h_undecap_snap(ar, msdu, status, first_hdr,
> +					     enctype);
>   		break;
>   	}
>   }
> @@ -1316,7 +1353,8 @@ static void ath10k_htt_rx_h_csum_offload(struct sk_buff *msdu)
>   
>   static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
>   				 struct sk_buff_head *amsdu,
> -				 struct ieee80211_rx_status *status)
> +				 struct ieee80211_rx_status *status,
> +				 bool fill_crypt_header)
>   {
>   	struct sk_buff *first;
>   	struct sk_buff *last;
> @@ -1326,7 +1364,6 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
>   	enum htt_rx_mpdu_encrypt_type enctype;
>   	u8 first_hdr[64];
>   	u8 *qos;
> -	size_t hdr_len;
>   	bool has_fcs_err;
>   	bool has_crypto_err;
>   	bool has_tkip_err;
> @@ -1351,15 +1388,17 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
>   	 * decapped header. It'll be used for undecapping of each MSDU.
>   	 */
>   	hdr = (void *)rxd->rx_hdr_status;
> -	hdr_len = ieee80211_hdrlen(hdr->frame_control);
> -	memcpy(first_hdr, hdr, hdr_len);
> +	memcpy(first_hdr, hdr, RX_HTT_HDR_STATUS_LEN);
>   
>   	/* Each A-MSDU subframe will use the original header as the base and be
>   	 * reported as a separate MSDU so strip the A-MSDU bit from QoS Ctl.
>   	 */
>   	hdr = (void *)first_hdr;
> -	qos = ieee80211_get_qos_ctl(hdr);
> -	qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
> +
> +	if (ieee80211_is_data_qos(hdr->frame_control)) {
> +		qos = ieee80211_get_qos_ctl(hdr);
> +		qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
> +	}
>   
>   	/* Some attention flags are valid only in the last MSDU. */
>   	last = skb_peek_tail(amsdu);
> @@ -1406,9 +1445,14 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
>   		status->flag |= RX_FLAG_DECRYPTED;
>   
>   		if (likely(!is_mgmt))
> -			status->flag |= RX_FLAG_IV_STRIPPED |
> -					RX_FLAG_MMIC_STRIPPED;
> -}
> +			status->flag |= RX_FLAG_MMIC_STRIPPED;
> +
> +		if (fill_crypt_header)
> +			status->flag |= RX_FLAG_MIC_STRIPPED |
> +					RX_FLAG_ICV_STRIPPED;
> +		else
> +			status->flag |= RX_FLAG_IV_STRIPPED;
> +	}
>   
>   	skb_queue_walk(amsdu, msdu) {
>   		ath10k_htt_rx_h_csum_offload(msdu);
> @@ -1424,6 +1468,9 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
>   		if (is_mgmt)
>   			continue;
>   
> +		if (fill_crypt_header)
> +			continue;
> +
>   		hdr = (void *)msdu->data;
>   		hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED);
>   	}
> @@ -1434,6 +1481,9 @@ static void ath10k_htt_rx_h_deliver(struct ath10k *ar,
>   				    struct ieee80211_rx_status *status)
>   {
>   	struct sk_buff *msdu;
> +	struct sk_buff *first_subframe;
> +
> +	first_subframe = skb_peek(amsdu);
>   
>   	while ((msdu = __skb_dequeue(amsdu))) {
>   		/* Setup per-MSDU flags */
> @@ -1442,6 +1492,13 @@ static void ath10k_htt_rx_h_deliver(struct ath10k *ar,
>   		else
>   			status->flag |= RX_FLAG_AMSDU_MORE;
>   
> +		if (msdu == first_subframe) {
> +			first_subframe = NULL;
> +			status->flag &= ~RX_FLAG_ALLOW_SAME_PN;
> +		} else {
> +			status->flag |= RX_FLAG_ALLOW_SAME_PN;
> +		}
> +
>   		ath10k_process_rx(ar, status, msdu);
>   	}
>   }
> @@ -1584,7 +1641,7 @@ static int ath10k_htt_rx_handle_amsdu(struct ath10k_htt *htt)
>   		ath10k_htt_rx_h_unchain(ar, &amsdu);
>   
>   	ath10k_htt_rx_h_filter(ar, &amsdu, rx_status);
> -	ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status);
> +	ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status, true);
>   	ath10k_htt_rx_h_deliver(ar, &amsdu, rx_status);
>   
>   	return num_msdus;
> @@ -1923,7 +1980,7 @@ static int ath10k_htt_rx_in_ord_ind(struct ath10k *ar, struct sk_buff *skb,
>   			budget_left -= skb_queue_len(&amsdu);
>   			ath10k_htt_rx_h_ppdu(ar, &amsdu, status, vdev_id);
>   			ath10k_htt_rx_h_filter(ar, &amsdu, status);
> -			ath10k_htt_rx_h_mpdu(ar, &amsdu, status);
> +			ath10k_htt_rx_h_mpdu(ar, &amsdu, status, false);
>   			ath10k_htt_rx_h_deliver(ar, &amsdu, status);
>   			break;
>   		case -EAGAIN:
Sebastian Gottschall Oct. 24, 2017, 8:41 p.m. UTC | #2
patch works. tested in sta and ap mode so far on 9984

Am 24.10.2017 um 19:54 schrieb Kalle Valo:
> From: Vasanthakumar Thiagarajan <vthiagar@qti.qualcomm.com>
>
> Rx data frames notified through HTT_T2H_MSG_TYPE_RX_IND and
> HTT_T2H_MSG_TYPE_RX_FRAG_IND expect PN/TSC check to be done
> on host (mac80211) rather than firmware. Rebuild cipher header
> in every received data frames (that are notified through those
> HTT interfaces) from the rx_hdr_status tlv available in the
> rx descriptor of the first msdu. Skip setting RX_FLAG_IV_STRIPPED
> flag for the packets which requires mac80211 PN/TSC check support
> and set appropriate RX_FLAG for stripped crypto tail. Hw QCA988X,
> QCA9887, QCA99X0, QCA9984, QCA9888 and QCA4019 currently need the
> rebuilding of cipher header to perform PN/TSC check for replay
> attack.
>
> Tested-by: Manikanta Pubbisetty <mpubbise@qti.qualcomm.com>
> Signed-off-by: Vasanthakumar Thiagarajan <vthiagar@qti.qualcomm.com>
> Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
> ---
>
> v2:
>
> * Construct cipher header from rx_hdr_status tlv rather than from
>    the msdu_start and mpdu_start tlvs. This fixes connection
>    issues, also reduces the amount of code change.
>
> * Make sure the frame is qos data before clearing AMSDU_PRESENT
>    bit of qos control field.
>
> * Fix traffic issue with QCA988X and QCA9887 hw by taking care of
>    padding bytes added for 4-byte alignment before the cipher
>    header.
>
> TODO:
>
> * CCMP-256 and GCMP/GCMP-256 are not yet supported
>
>   drivers/net/wireless/ath/ath10k/htt_rx.c | 91 ++++++++++++++++++++++++++------
>   1 file changed, 74 insertions(+), 17 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
> index a3f5dc78353f..aae4f0ea9360 100644
> --- a/drivers/net/wireless/ath/ath10k/htt_rx.c
> +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
> @@ -1050,8 +1050,14 @@ static void ath10k_htt_rx_h_undecap_raw(struct ath10k *ar,
>   
>   	hdr = (void *)msdu->data;
>   
> -	/* Tail */
> -	if (status->flag & RX_FLAG_IV_STRIPPED)
> +	/* MIC */
> +	if ((status->flag & RX_FLAG_MIC_STRIPPED) &&
> +	    enctype == HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2)
> +		skb_trim(msdu, msdu->len - 8);
> +
> +	/* ICV */
> +	if (status->flag & RX_FLAG_ICV_STRIPPED &&
> +	    enctype != HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2)
>   		skb_trim(msdu, msdu->len -
>   			 ath10k_htt_rx_crypto_tail_len(ar, enctype));
>   
> @@ -1075,7 +1081,8 @@ static void ath10k_htt_rx_h_undecap_raw(struct ath10k *ar,
>   static void ath10k_htt_rx_h_undecap_nwifi(struct ath10k *ar,
>   					  struct sk_buff *msdu,
>   					  struct ieee80211_rx_status *status,
> -					  const u8 first_hdr[64])
> +					  const u8 first_hdr[64],
> +					  enum htt_rx_mpdu_encrypt_type enctype)
>   {
>   	struct ieee80211_hdr *hdr;
>   	struct htt_rx_desc *rxd;
> @@ -1083,6 +1090,7 @@ static void ath10k_htt_rx_h_undecap_nwifi(struct ath10k *ar,
>   	u8 da[ETH_ALEN];
>   	u8 sa[ETH_ALEN];
>   	int l3_pad_bytes;
> +	int bytes_aligned = ar->hw_params.decap_align_bytes;
>   
>   	/* Delivered decapped frame:
>   	 * [nwifi 802.11 header] <-- replaced with 802.11 hdr
> @@ -1111,6 +1119,14 @@ static void ath10k_htt_rx_h_undecap_nwifi(struct ath10k *ar,
>   	/* push original 802.11 header */
>   	hdr = (struct ieee80211_hdr *)first_hdr;
>   	hdr_len = ieee80211_hdrlen(hdr->frame_control);
> +
> +	if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
> +		memcpy(skb_push(msdu,
> +				ath10k_htt_rx_crypto_param_len(ar, enctype)),
> +		       (void *)hdr + round_up(hdr_len, bytes_aligned),
> +			ath10k_htt_rx_crypto_param_len(ar, enctype));
> +	}
> +
>   	memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
>   
>   	/* original 802.11 header has a different DA and in
> @@ -1171,6 +1187,7 @@ static void ath10k_htt_rx_h_undecap_eth(struct ath10k *ar,
>   	u8 sa[ETH_ALEN];
>   	int l3_pad_bytes;
>   	struct htt_rx_desc *rxd;
> +	int bytes_aligned = ar->hw_params.decap_align_bytes;
>   
>   	/* Delivered decapped frame:
>   	 * [eth header] <-- replaced with 802.11 hdr & rfc1042/llc
> @@ -1199,6 +1216,14 @@ static void ath10k_htt_rx_h_undecap_eth(struct ath10k *ar,
>   	/* push original 802.11 header */
>   	hdr = (struct ieee80211_hdr *)first_hdr;
>   	hdr_len = ieee80211_hdrlen(hdr->frame_control);
> +
> +	if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
> +		memcpy(skb_push(msdu,
> +				ath10k_htt_rx_crypto_param_len(ar, enctype)),
> +		       (void *)hdr + round_up(hdr_len, bytes_aligned),
> +			ath10k_htt_rx_crypto_param_len(ar, enctype));
> +	}
> +
>   	memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
>   
>   	/* original 802.11 header has a different DA and in
> @@ -1212,12 +1237,14 @@ static void ath10k_htt_rx_h_undecap_eth(struct ath10k *ar,
>   static void ath10k_htt_rx_h_undecap_snap(struct ath10k *ar,
>   					 struct sk_buff *msdu,
>   					 struct ieee80211_rx_status *status,
> -					 const u8 first_hdr[64])
> +					 const u8 first_hdr[64],
> +					 enum htt_rx_mpdu_encrypt_type enctype)
>   {
>   	struct ieee80211_hdr *hdr;
>   	size_t hdr_len;
>   	int l3_pad_bytes;
>   	struct htt_rx_desc *rxd;
> +	int bytes_aligned = ar->hw_params.decap_align_bytes;
>   
>   	/* Delivered decapped frame:
>   	 * [amsdu header] <-- replaced with 802.11 hdr
> @@ -1233,6 +1260,14 @@ static void ath10k_htt_rx_h_undecap_snap(struct ath10k *ar,
>   
>   	hdr = (struct ieee80211_hdr *)first_hdr;
>   	hdr_len = ieee80211_hdrlen(hdr->frame_control);
> +
> +	if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
> +		memcpy(skb_push(msdu,
> +				ath10k_htt_rx_crypto_param_len(ar, enctype)),
> +		       (void *)hdr + round_up(hdr_len, bytes_aligned),
> +			ath10k_htt_rx_crypto_param_len(ar, enctype));
> +	}
> +
>   	memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
>   }
>   
> @@ -1267,13 +1302,15 @@ static void ath10k_htt_rx_h_undecap(struct ath10k *ar,
>   					    is_decrypted);
>   		break;
>   	case RX_MSDU_DECAP_NATIVE_WIFI:
> -		ath10k_htt_rx_h_undecap_nwifi(ar, msdu, status, first_hdr);
> +		ath10k_htt_rx_h_undecap_nwifi(ar, msdu, status, first_hdr,
> +					      enctype);
>   		break;
>   	case RX_MSDU_DECAP_ETHERNET2_DIX:
>   		ath10k_htt_rx_h_undecap_eth(ar, msdu, status, first_hdr, enctype);
>   		break;
>   	case RX_MSDU_DECAP_8023_SNAP_LLC:
> -		ath10k_htt_rx_h_undecap_snap(ar, msdu, status, first_hdr);
> +		ath10k_htt_rx_h_undecap_snap(ar, msdu, status, first_hdr,
> +					     enctype);
>   		break;
>   	}
>   }
> @@ -1316,7 +1353,8 @@ static void ath10k_htt_rx_h_csum_offload(struct sk_buff *msdu)
>   
>   static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
>   				 struct sk_buff_head *amsdu,
> -				 struct ieee80211_rx_status *status)
> +				 struct ieee80211_rx_status *status,
> +				 bool fill_crypt_header)
>   {
>   	struct sk_buff *first;
>   	struct sk_buff *last;
> @@ -1326,7 +1364,6 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
>   	enum htt_rx_mpdu_encrypt_type enctype;
>   	u8 first_hdr[64];
>   	u8 *qos;
> -	size_t hdr_len;
>   	bool has_fcs_err;
>   	bool has_crypto_err;
>   	bool has_tkip_err;
> @@ -1351,15 +1388,17 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
>   	 * decapped header. It'll be used for undecapping of each MSDU.
>   	 */
>   	hdr = (void *)rxd->rx_hdr_status;
> -	hdr_len = ieee80211_hdrlen(hdr->frame_control);
> -	memcpy(first_hdr, hdr, hdr_len);
> +	memcpy(first_hdr, hdr, RX_HTT_HDR_STATUS_LEN);
>   
>   	/* Each A-MSDU subframe will use the original header as the base and be
>   	 * reported as a separate MSDU so strip the A-MSDU bit from QoS Ctl.
>   	 */
>   	hdr = (void *)first_hdr;
> -	qos = ieee80211_get_qos_ctl(hdr);
> -	qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
> +
> +	if (ieee80211_is_data_qos(hdr->frame_control)) {
> +		qos = ieee80211_get_qos_ctl(hdr);
> +		qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
> +	}
>   
>   	/* Some attention flags are valid only in the last MSDU. */
>   	last = skb_peek_tail(amsdu);
> @@ -1406,9 +1445,14 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
>   		status->flag |= RX_FLAG_DECRYPTED;
>   
>   		if (likely(!is_mgmt))
> -			status->flag |= RX_FLAG_IV_STRIPPED |
> -					RX_FLAG_MMIC_STRIPPED;
> -}
> +			status->flag |= RX_FLAG_MMIC_STRIPPED;
> +
> +		if (fill_crypt_header)
> +			status->flag |= RX_FLAG_MIC_STRIPPED |
> +					RX_FLAG_ICV_STRIPPED;
> +		else
> +			status->flag |= RX_FLAG_IV_STRIPPED;
> +	}
>   
>   	skb_queue_walk(amsdu, msdu) {
>   		ath10k_htt_rx_h_csum_offload(msdu);
> @@ -1424,6 +1468,9 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
>   		if (is_mgmt)
>   			continue;
>   
> +		if (fill_crypt_header)
> +			continue;
> +
>   		hdr = (void *)msdu->data;
>   		hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED);
>   	}
> @@ -1434,6 +1481,9 @@ static void ath10k_htt_rx_h_deliver(struct ath10k *ar,
>   				    struct ieee80211_rx_status *status)
>   {
>   	struct sk_buff *msdu;
> +	struct sk_buff *first_subframe;
> +
> +	first_subframe = skb_peek(amsdu);
>   
>   	while ((msdu = __skb_dequeue(amsdu))) {
>   		/* Setup per-MSDU flags */
> @@ -1442,6 +1492,13 @@ static void ath10k_htt_rx_h_deliver(struct ath10k *ar,
>   		else
>   			status->flag |= RX_FLAG_AMSDU_MORE;
>   
> +		if (msdu == first_subframe) {
> +			first_subframe = NULL;
> +			status->flag &= ~RX_FLAG_ALLOW_SAME_PN;
> +		} else {
> +			status->flag |= RX_FLAG_ALLOW_SAME_PN;
> +		}
> +
>   		ath10k_process_rx(ar, status, msdu);
>   	}
>   }
> @@ -1584,7 +1641,7 @@ static int ath10k_htt_rx_handle_amsdu(struct ath10k_htt *htt)
>   		ath10k_htt_rx_h_unchain(ar, &amsdu);
>   
>   	ath10k_htt_rx_h_filter(ar, &amsdu, rx_status);
> -	ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status);
> +	ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status, true);
>   	ath10k_htt_rx_h_deliver(ar, &amsdu, rx_status);
>   
>   	return num_msdus;
> @@ -1923,7 +1980,7 @@ static int ath10k_htt_rx_in_ord_ind(struct ath10k *ar, struct sk_buff *skb,
>   			budget_left -= skb_queue_len(&amsdu);
>   			ath10k_htt_rx_h_ppdu(ar, &amsdu, status, vdev_id);
>   			ath10k_htt_rx_h_filter(ar, &amsdu, status);
> -			ath10k_htt_rx_h_mpdu(ar, &amsdu, status);
> +			ath10k_htt_rx_h_mpdu(ar, &amsdu, status, false);
>   			ath10k_htt_rx_h_deliver(ar, &amsdu, status);
>   			break;
>   		case -EAGAIN:
Jasmine Strong Oct. 24, 2017, 9:15 p.m. UTC | #3
Seems to work okay on mixed networks of 9882, 4019 and 9888.

On Tue, Oct 24, 2017 at 1:41 PM, Sebastian Gottschall
<s.gottschall@dd-wrt.com> wrote:
> patch works. tested in sta and ap mode so far on 9984
>
> Am 24.10.2017 um 19:54 schrieb Kalle Valo:
>>
>> From: Vasanthakumar Thiagarajan <vthiagar@qti.qualcomm.com>
>>
>> Rx data frames notified through HTT_T2H_MSG_TYPE_RX_IND and
>> HTT_T2H_MSG_TYPE_RX_FRAG_IND expect PN/TSC check to be done
>> on host (mac80211) rather than firmware. Rebuild cipher header
>> in every received data frames (that are notified through those
>> HTT interfaces) from the rx_hdr_status tlv available in the
>> rx descriptor of the first msdu. Skip setting RX_FLAG_IV_STRIPPED
>> flag for the packets which requires mac80211 PN/TSC check support
>> and set appropriate RX_FLAG for stripped crypto tail. Hw QCA988X,
>> QCA9887, QCA99X0, QCA9984, QCA9888 and QCA4019 currently need the
>> rebuilding of cipher header to perform PN/TSC check for replay
>> attack.
>>
>> Tested-by: Manikanta Pubbisetty <mpubbise@qti.qualcomm.com>
>> Signed-off-by: Vasanthakumar Thiagarajan <vthiagar@qti.qualcomm.com>
>> Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
>> ---
>>
>> v2:
>>
>> * Construct cipher header from rx_hdr_status tlv rather than from
>>    the msdu_start and mpdu_start tlvs. This fixes connection
>>    issues, also reduces the amount of code change.
>>
>> * Make sure the frame is qos data before clearing AMSDU_PRESENT
>>    bit of qos control field.
>>
>> * Fix traffic issue with QCA988X and QCA9887 hw by taking care of
>>    padding bytes added for 4-byte alignment before the cipher
>>    header.
>>
>> TODO:
>>
>> * CCMP-256 and GCMP/GCMP-256 are not yet supported
>>
>>   drivers/net/wireless/ath/ath10k/htt_rx.c | 91
>> ++++++++++++++++++++++++++------
>>   1 file changed, 74 insertions(+), 17 deletions(-)
>>
>> diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c
>> b/drivers/net/wireless/ath/ath10k/htt_rx.c
>> index a3f5dc78353f..aae4f0ea9360 100644
>> --- a/drivers/net/wireless/ath/ath10k/htt_rx.c
>> +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
>> @@ -1050,8 +1050,14 @@ static void ath10k_htt_rx_h_undecap_raw(struct
>> ath10k *ar,
>>         hdr = (void *)msdu->data;
>>   -     /* Tail */
>> -       if (status->flag & RX_FLAG_IV_STRIPPED)
>> +       /* MIC */
>> +       if ((status->flag & RX_FLAG_MIC_STRIPPED) &&
>> +           enctype == HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2)
>> +               skb_trim(msdu, msdu->len - 8);
>> +
>> +       /* ICV */
>> +       if (status->flag & RX_FLAG_ICV_STRIPPED &&
>> +           enctype != HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2)
>>                 skb_trim(msdu, msdu->len -
>>                          ath10k_htt_rx_crypto_tail_len(ar, enctype));
>>   @@ -1075,7 +1081,8 @@ static void ath10k_htt_rx_h_undecap_raw(struct
>> ath10k *ar,
>>   static void ath10k_htt_rx_h_undecap_nwifi(struct ath10k *ar,
>>                                           struct sk_buff *msdu,
>>                                           struct ieee80211_rx_status
>> *status,
>> -                                         const u8 first_hdr[64])
>> +                                         const u8 first_hdr[64],
>> +                                         enum htt_rx_mpdu_encrypt_type
>> enctype)
>>   {
>>         struct ieee80211_hdr *hdr;
>>         struct htt_rx_desc *rxd;
>> @@ -1083,6 +1090,7 @@ static void ath10k_htt_rx_h_undecap_nwifi(struct
>> ath10k *ar,
>>         u8 da[ETH_ALEN];
>>         u8 sa[ETH_ALEN];
>>         int l3_pad_bytes;
>> +       int bytes_aligned = ar->hw_params.decap_align_bytes;
>>         /* Delivered decapped frame:
>>          * [nwifi 802.11 header] <-- replaced with 802.11 hdr
>> @@ -1111,6 +1119,14 @@ static void ath10k_htt_rx_h_undecap_nwifi(struct
>> ath10k *ar,
>>         /* push original 802.11 header */
>>         hdr = (struct ieee80211_hdr *)first_hdr;
>>         hdr_len = ieee80211_hdrlen(hdr->frame_control);
>> +
>> +       if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
>> +               memcpy(skb_push(msdu,
>> +                               ath10k_htt_rx_crypto_param_len(ar,
>> enctype)),
>> +                      (void *)hdr + round_up(hdr_len, bytes_aligned),
>> +                       ath10k_htt_rx_crypto_param_len(ar, enctype));
>> +       }
>> +
>>         memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
>>         /* original 802.11 header has a different DA and in
>> @@ -1171,6 +1187,7 @@ static void ath10k_htt_rx_h_undecap_eth(struct
>> ath10k *ar,
>>         u8 sa[ETH_ALEN];
>>         int l3_pad_bytes;
>>         struct htt_rx_desc *rxd;
>> +       int bytes_aligned = ar->hw_params.decap_align_bytes;
>>         /* Delivered decapped frame:
>>          * [eth header] <-- replaced with 802.11 hdr & rfc1042/llc
>> @@ -1199,6 +1216,14 @@ static void ath10k_htt_rx_h_undecap_eth(struct
>> ath10k *ar,
>>         /* push original 802.11 header */
>>         hdr = (struct ieee80211_hdr *)first_hdr;
>>         hdr_len = ieee80211_hdrlen(hdr->frame_control);
>> +
>> +       if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
>> +               memcpy(skb_push(msdu,
>> +                               ath10k_htt_rx_crypto_param_len(ar,
>> enctype)),
>> +                      (void *)hdr + round_up(hdr_len, bytes_aligned),
>> +                       ath10k_htt_rx_crypto_param_len(ar, enctype));
>> +       }
>> +
>>         memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
>>         /* original 802.11 header has a different DA and in
>> @@ -1212,12 +1237,14 @@ static void ath10k_htt_rx_h_undecap_eth(struct
>> ath10k *ar,
>>   static void ath10k_htt_rx_h_undecap_snap(struct ath10k *ar,
>>                                          struct sk_buff *msdu,
>>                                          struct ieee80211_rx_status
>> *status,
>> -                                        const u8 first_hdr[64])
>> +                                        const u8 first_hdr[64],
>> +                                        enum htt_rx_mpdu_encrypt_type
>> enctype)
>>   {
>>         struct ieee80211_hdr *hdr;
>>         size_t hdr_len;
>>         int l3_pad_bytes;
>>         struct htt_rx_desc *rxd;
>> +       int bytes_aligned = ar->hw_params.decap_align_bytes;
>>         /* Delivered decapped frame:
>>          * [amsdu header] <-- replaced with 802.11 hdr
>> @@ -1233,6 +1260,14 @@ static void ath10k_htt_rx_h_undecap_snap(struct
>> ath10k *ar,
>>         hdr = (struct ieee80211_hdr *)first_hdr;
>>         hdr_len = ieee80211_hdrlen(hdr->frame_control);
>> +
>> +       if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
>> +               memcpy(skb_push(msdu,
>> +                               ath10k_htt_rx_crypto_param_len(ar,
>> enctype)),
>> +                      (void *)hdr + round_up(hdr_len, bytes_aligned),
>> +                       ath10k_htt_rx_crypto_param_len(ar, enctype));
>> +       }
>> +
>>         memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
>>   }
>>   @@ -1267,13 +1302,15 @@ static void ath10k_htt_rx_h_undecap(struct
>> ath10k *ar,
>>                                             is_decrypted);
>>                 break;
>>         case RX_MSDU_DECAP_NATIVE_WIFI:
>> -               ath10k_htt_rx_h_undecap_nwifi(ar, msdu, status,
>> first_hdr);
>> +               ath10k_htt_rx_h_undecap_nwifi(ar, msdu, status, first_hdr,
>> +                                             enctype);
>>                 break;
>>         case RX_MSDU_DECAP_ETHERNET2_DIX:
>>                 ath10k_htt_rx_h_undecap_eth(ar, msdu, status, first_hdr,
>> enctype);
>>                 break;
>>         case RX_MSDU_DECAP_8023_SNAP_LLC:
>> -               ath10k_htt_rx_h_undecap_snap(ar, msdu, status, first_hdr);
>> +               ath10k_htt_rx_h_undecap_snap(ar, msdu, status, first_hdr,
>> +                                            enctype);
>>                 break;
>>         }
>>   }
>> @@ -1316,7 +1353,8 @@ static void ath10k_htt_rx_h_csum_offload(struct
>> sk_buff *msdu)
>>     static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
>>                                  struct sk_buff_head *amsdu,
>> -                                struct ieee80211_rx_status *status)
>> +                                struct ieee80211_rx_status *status,
>> +                                bool fill_crypt_header)
>>   {
>>         struct sk_buff *first;
>>         struct sk_buff *last;
>> @@ -1326,7 +1364,6 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
>>         enum htt_rx_mpdu_encrypt_type enctype;
>>         u8 first_hdr[64];
>>         u8 *qos;
>> -       size_t hdr_len;
>>         bool has_fcs_err;
>>         bool has_crypto_err;
>>         bool has_tkip_err;
>> @@ -1351,15 +1388,17 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k
>> *ar,
>>          * decapped header. It'll be used for undecapping of each MSDU.
>>          */
>>         hdr = (void *)rxd->rx_hdr_status;
>> -       hdr_len = ieee80211_hdrlen(hdr->frame_control);
>> -       memcpy(first_hdr, hdr, hdr_len);
>> +       memcpy(first_hdr, hdr, RX_HTT_HDR_STATUS_LEN);
>>         /* Each A-MSDU subframe will use the original header as the base
>> and be
>>          * reported as a separate MSDU so strip the A-MSDU bit from QoS
>> Ctl.
>>          */
>>         hdr = (void *)first_hdr;
>> -       qos = ieee80211_get_qos_ctl(hdr);
>> -       qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
>> +
>> +       if (ieee80211_is_data_qos(hdr->frame_control)) {
>> +               qos = ieee80211_get_qos_ctl(hdr);
>> +               qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
>> +       }
>>         /* Some attention flags are valid only in the last MSDU. */
>>         last = skb_peek_tail(amsdu);
>> @@ -1406,9 +1445,14 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
>>                 status->flag |= RX_FLAG_DECRYPTED;
>>                 if (likely(!is_mgmt))
>> -                       status->flag |= RX_FLAG_IV_STRIPPED |
>> -                                       RX_FLAG_MMIC_STRIPPED;
>> -}
>> +                       status->flag |= RX_FLAG_MMIC_STRIPPED;
>> +
>> +               if (fill_crypt_header)
>> +                       status->flag |= RX_FLAG_MIC_STRIPPED |
>> +                                       RX_FLAG_ICV_STRIPPED;
>> +               else
>> +                       status->flag |= RX_FLAG_IV_STRIPPED;
>> +       }
>>         skb_queue_walk(amsdu, msdu) {
>>                 ath10k_htt_rx_h_csum_offload(msdu);
>> @@ -1424,6 +1468,9 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
>>                 if (is_mgmt)
>>                         continue;
>>   +             if (fill_crypt_header)
>> +                       continue;
>> +
>>                 hdr = (void *)msdu->data;
>>                 hdr->frame_control &=
>> ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED);
>>         }
>> @@ -1434,6 +1481,9 @@ static void ath10k_htt_rx_h_deliver(struct ath10k
>> *ar,
>>                                     struct ieee80211_rx_status *status)
>>   {
>>         struct sk_buff *msdu;
>> +       struct sk_buff *first_subframe;
>> +
>> +       first_subframe = skb_peek(amsdu);
>>         while ((msdu = __skb_dequeue(amsdu))) {
>>                 /* Setup per-MSDU flags */
>> @@ -1442,6 +1492,13 @@ static void ath10k_htt_rx_h_deliver(struct ath10k
>> *ar,
>>                 else
>>                         status->flag |= RX_FLAG_AMSDU_MORE;
>>   +             if (msdu == first_subframe) {
>> +                       first_subframe = NULL;
>> +                       status->flag &= ~RX_FLAG_ALLOW_SAME_PN;
>> +               } else {
>> +                       status->flag |= RX_FLAG_ALLOW_SAME_PN;
>> +               }
>> +
>>                 ath10k_process_rx(ar, status, msdu);
>>         }
>>   }
>> @@ -1584,7 +1641,7 @@ static int ath10k_htt_rx_handle_amsdu(struct
>> ath10k_htt *htt)
>>                 ath10k_htt_rx_h_unchain(ar, &amsdu);
>>         ath10k_htt_rx_h_filter(ar, &amsdu, rx_status);
>> -       ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status);
>> +       ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status, true);
>>         ath10k_htt_rx_h_deliver(ar, &amsdu, rx_status);
>>         return num_msdus;
>> @@ -1923,7 +1980,7 @@ static int ath10k_htt_rx_in_ord_ind(struct ath10k
>> *ar, struct sk_buff *skb,
>>                         budget_left -= skb_queue_len(&amsdu);
>>                         ath10k_htt_rx_h_ppdu(ar, &amsdu, status, vdev_id);
>>                         ath10k_htt_rx_h_filter(ar, &amsdu, status);
>> -                       ath10k_htt_rx_h_mpdu(ar, &amsdu, status);
>> +                       ath10k_htt_rx_h_mpdu(ar, &amsdu, status, false);
>>                         ath10k_htt_rx_h_deliver(ar, &amsdu, status);
>>                         break;
>>                 case -EAGAIN:
>
>
>
> --
> Mit freundlichen Grüssen / Regards
>
> Sebastian Gottschall / CTO
>
> NewMedia-NET GmbH - DD-WRT
> Firmensitz:  Stubenwaldallee 21a, 64625 Bensheim
> Registergericht: Amtsgericht Darmstadt, HRB 25473
> Geschäftsführer: Peter Steinhäuser, Christian Scheele
> http://www.dd-wrt.com
> email: s.gottschall@dd-wrt.com
> Tel.: +496251-582650 / Fax: +496251-5826565
>
>
> _______________________________________________
> ath10k mailing list
> ath10k@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/ath10k
Kalle Valo Oct. 25, 2017, 9:07 a.m. UTC | #4
(fixing top posting)

Jasmine Strong <jas@eero.com> writes:

> On Tue, Oct 24, 2017 at 1:41 PM, Sebastian Gottschall
> <s.gottschall@dd-wrt.com> wrote:
>
>> patch works. tested in sta and ap mode so far on 9984
>
> Seems to work okay on mixed networks of 9882, 4019 and 9888.

Thanks Sebastian and Jasmine for testing this, very much appreciated. We
have a v3 coming up soon which should fix GCMP, GCMP-256 and CCMP-256 as
well.
Sebastian Gottschall Oct. 25, 2017, 9:28 a.m. UTC | #5
Am 25.10.2017 um 11:07 schrieb Kalle Valo:
> (fixing top posting)
>
> Jasmine Strong <jas@eero.com> writes:
>
>> On Tue, Oct 24, 2017 at 1:41 PM, Sebastian Gottschall
>> <s.gottschall@dd-wrt.com> wrote:
>>
>>> patch works. tested in sta and ap mode so far on 9984
>> Seems to work okay on mixed networks of 9882, 4019 and 9888.
> Thanks Sebastian and Jasmine for testing this, very much appreciated. We
> have a v3 coming up soon which should fix GCMP, GCMP-256 and CCMP-256 as
> well.

i have my own v3, but will wait for your version to check how much its 
different from mine

i'm also able to test gmp / aes 256 using 2 QCA 9984 devices

Patch
diff mbox

diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
index a3f5dc78353f..aae4f0ea9360 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -1050,8 +1050,14 @@  static void ath10k_htt_rx_h_undecap_raw(struct ath10k *ar,
 
 	hdr = (void *)msdu->data;
 
-	/* Tail */
-	if (status->flag & RX_FLAG_IV_STRIPPED)
+	/* MIC */
+	if ((status->flag & RX_FLAG_MIC_STRIPPED) &&
+	    enctype == HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2)
+		skb_trim(msdu, msdu->len - 8);
+
+	/* ICV */
+	if (status->flag & RX_FLAG_ICV_STRIPPED &&
+	    enctype != HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2)
 		skb_trim(msdu, msdu->len -
 			 ath10k_htt_rx_crypto_tail_len(ar, enctype));
 
@@ -1075,7 +1081,8 @@  static void ath10k_htt_rx_h_undecap_raw(struct ath10k *ar,
 static void ath10k_htt_rx_h_undecap_nwifi(struct ath10k *ar,
 					  struct sk_buff *msdu,
 					  struct ieee80211_rx_status *status,
-					  const u8 first_hdr[64])
+					  const u8 first_hdr[64],
+					  enum htt_rx_mpdu_encrypt_type enctype)
 {
 	struct ieee80211_hdr *hdr;
 	struct htt_rx_desc *rxd;
@@ -1083,6 +1090,7 @@  static void ath10k_htt_rx_h_undecap_nwifi(struct ath10k *ar,
 	u8 da[ETH_ALEN];
 	u8 sa[ETH_ALEN];
 	int l3_pad_bytes;
+	int bytes_aligned = ar->hw_params.decap_align_bytes;
 
 	/* Delivered decapped frame:
 	 * [nwifi 802.11 header] <-- replaced with 802.11 hdr
@@ -1111,6 +1119,14 @@  static void ath10k_htt_rx_h_undecap_nwifi(struct ath10k *ar,
 	/* push original 802.11 header */
 	hdr = (struct ieee80211_hdr *)first_hdr;
 	hdr_len = ieee80211_hdrlen(hdr->frame_control);
+
+	if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
+		memcpy(skb_push(msdu,
+				ath10k_htt_rx_crypto_param_len(ar, enctype)),
+		       (void *)hdr + round_up(hdr_len, bytes_aligned),
+			ath10k_htt_rx_crypto_param_len(ar, enctype));
+	}
+
 	memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
 
 	/* original 802.11 header has a different DA and in
@@ -1171,6 +1187,7 @@  static void ath10k_htt_rx_h_undecap_eth(struct ath10k *ar,
 	u8 sa[ETH_ALEN];
 	int l3_pad_bytes;
 	struct htt_rx_desc *rxd;
+	int bytes_aligned = ar->hw_params.decap_align_bytes;
 
 	/* Delivered decapped frame:
 	 * [eth header] <-- replaced with 802.11 hdr & rfc1042/llc
@@ -1199,6 +1216,14 @@  static void ath10k_htt_rx_h_undecap_eth(struct ath10k *ar,
 	/* push original 802.11 header */
 	hdr = (struct ieee80211_hdr *)first_hdr;
 	hdr_len = ieee80211_hdrlen(hdr->frame_control);
+
+	if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
+		memcpy(skb_push(msdu,
+				ath10k_htt_rx_crypto_param_len(ar, enctype)),
+		       (void *)hdr + round_up(hdr_len, bytes_aligned),
+			ath10k_htt_rx_crypto_param_len(ar, enctype));
+	}
+
 	memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
 
 	/* original 802.11 header has a different DA and in
@@ -1212,12 +1237,14 @@  static void ath10k_htt_rx_h_undecap_eth(struct ath10k *ar,
 static void ath10k_htt_rx_h_undecap_snap(struct ath10k *ar,
 					 struct sk_buff *msdu,
 					 struct ieee80211_rx_status *status,
-					 const u8 first_hdr[64])
+					 const u8 first_hdr[64],
+					 enum htt_rx_mpdu_encrypt_type enctype)
 {
 	struct ieee80211_hdr *hdr;
 	size_t hdr_len;
 	int l3_pad_bytes;
 	struct htt_rx_desc *rxd;
+	int bytes_aligned = ar->hw_params.decap_align_bytes;
 
 	/* Delivered decapped frame:
 	 * [amsdu header] <-- replaced with 802.11 hdr
@@ -1233,6 +1260,14 @@  static void ath10k_htt_rx_h_undecap_snap(struct ath10k *ar,
 
 	hdr = (struct ieee80211_hdr *)first_hdr;
 	hdr_len = ieee80211_hdrlen(hdr->frame_control);
+
+	if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
+		memcpy(skb_push(msdu,
+				ath10k_htt_rx_crypto_param_len(ar, enctype)),
+		       (void *)hdr + round_up(hdr_len, bytes_aligned),
+			ath10k_htt_rx_crypto_param_len(ar, enctype));
+	}
+
 	memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
 }
 
@@ -1267,13 +1302,15 @@  static void ath10k_htt_rx_h_undecap(struct ath10k *ar,
 					    is_decrypted);
 		break;
 	case RX_MSDU_DECAP_NATIVE_WIFI:
-		ath10k_htt_rx_h_undecap_nwifi(ar, msdu, status, first_hdr);
+		ath10k_htt_rx_h_undecap_nwifi(ar, msdu, status, first_hdr,
+					      enctype);
 		break;
 	case RX_MSDU_DECAP_ETHERNET2_DIX:
 		ath10k_htt_rx_h_undecap_eth(ar, msdu, status, first_hdr, enctype);
 		break;
 	case RX_MSDU_DECAP_8023_SNAP_LLC:
-		ath10k_htt_rx_h_undecap_snap(ar, msdu, status, first_hdr);
+		ath10k_htt_rx_h_undecap_snap(ar, msdu, status, first_hdr,
+					     enctype);
 		break;
 	}
 }
@@ -1316,7 +1353,8 @@  static void ath10k_htt_rx_h_csum_offload(struct sk_buff *msdu)
 
 static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
 				 struct sk_buff_head *amsdu,
-				 struct ieee80211_rx_status *status)
+				 struct ieee80211_rx_status *status,
+				 bool fill_crypt_header)
 {
 	struct sk_buff *first;
 	struct sk_buff *last;
@@ -1326,7 +1364,6 @@  static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
 	enum htt_rx_mpdu_encrypt_type enctype;
 	u8 first_hdr[64];
 	u8 *qos;
-	size_t hdr_len;
 	bool has_fcs_err;
 	bool has_crypto_err;
 	bool has_tkip_err;
@@ -1351,15 +1388,17 @@  static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
 	 * decapped header. It'll be used for undecapping of each MSDU.
 	 */
 	hdr = (void *)rxd->rx_hdr_status;
-	hdr_len = ieee80211_hdrlen(hdr->frame_control);
-	memcpy(first_hdr, hdr, hdr_len);
+	memcpy(first_hdr, hdr, RX_HTT_HDR_STATUS_LEN);
 
 	/* Each A-MSDU subframe will use the original header as the base and be
 	 * reported as a separate MSDU so strip the A-MSDU bit from QoS Ctl.
 	 */
 	hdr = (void *)first_hdr;
-	qos = ieee80211_get_qos_ctl(hdr);
-	qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
+
+	if (ieee80211_is_data_qos(hdr->frame_control)) {
+		qos = ieee80211_get_qos_ctl(hdr);
+		qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
+	}
 
 	/* Some attention flags are valid only in the last MSDU. */
 	last = skb_peek_tail(amsdu);
@@ -1406,9 +1445,14 @@  static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
 		status->flag |= RX_FLAG_DECRYPTED;
 
 		if (likely(!is_mgmt))
-			status->flag |= RX_FLAG_IV_STRIPPED |
-					RX_FLAG_MMIC_STRIPPED;
-}
+			status->flag |= RX_FLAG_MMIC_STRIPPED;
+
+		if (fill_crypt_header)
+			status->flag |= RX_FLAG_MIC_STRIPPED |
+					RX_FLAG_ICV_STRIPPED;
+		else
+			status->flag |= RX_FLAG_IV_STRIPPED;
+	}
 
 	skb_queue_walk(amsdu, msdu) {
 		ath10k_htt_rx_h_csum_offload(msdu);
@@ -1424,6 +1468,9 @@  static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
 		if (is_mgmt)
 			continue;
 
+		if (fill_crypt_header)
+			continue;
+
 		hdr = (void *)msdu->data;
 		hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED);
 	}
@@ -1434,6 +1481,9 @@  static void ath10k_htt_rx_h_deliver(struct ath10k *ar,
 				    struct ieee80211_rx_status *status)
 {
 	struct sk_buff *msdu;
+	struct sk_buff *first_subframe;
+
+	first_subframe = skb_peek(amsdu);
 
 	while ((msdu = __skb_dequeue(amsdu))) {
 		/* Setup per-MSDU flags */
@@ -1442,6 +1492,13 @@  static void ath10k_htt_rx_h_deliver(struct ath10k *ar,
 		else
 			status->flag |= RX_FLAG_AMSDU_MORE;
 
+		if (msdu == first_subframe) {
+			first_subframe = NULL;
+			status->flag &= ~RX_FLAG_ALLOW_SAME_PN;
+		} else {
+			status->flag |= RX_FLAG_ALLOW_SAME_PN;
+		}
+
 		ath10k_process_rx(ar, status, msdu);
 	}
 }
@@ -1584,7 +1641,7 @@  static int ath10k_htt_rx_handle_amsdu(struct ath10k_htt *htt)
 		ath10k_htt_rx_h_unchain(ar, &amsdu);
 
 	ath10k_htt_rx_h_filter(ar, &amsdu, rx_status);
-	ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status);
+	ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status, true);
 	ath10k_htt_rx_h_deliver(ar, &amsdu, rx_status);
 
 	return num_msdus;
@@ -1923,7 +1980,7 @@  static int ath10k_htt_rx_in_ord_ind(struct ath10k *ar, struct sk_buff *skb,
 			budget_left -= skb_queue_len(&amsdu);
 			ath10k_htt_rx_h_ppdu(ar, &amsdu, status, vdev_id);
 			ath10k_htt_rx_h_filter(ar, &amsdu, status);
-			ath10k_htt_rx_h_mpdu(ar, &amsdu, status);
+			ath10k_htt_rx_h_mpdu(ar, &amsdu, status, false);
 			ath10k_htt_rx_h_deliver(ar, &amsdu, status);
 			break;
 		case -EAGAIN: