summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c')
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c106
1 files changed, 93 insertions, 13 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
index e2a872deb668..0c073e02fd4c 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
@@ -78,12 +78,83 @@ void iwl_mvm_rx_phy_cmd_mq(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb)
#endif
}
-static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
- struct napi_struct *napi,
- struct sk_buff *skb,
- struct ieee80211_hdr *hdr, u16 len,
- u32 ampdu_status, u8 crypt_len,
- struct iwl_rx_cmd_buffer *rxb)
+static inline int iwl_mvm_check_pn(struct iwl_mvm *mvm, struct sk_buff *skb,
+ int queue, struct ieee80211_sta *sta)
+{
+ struct iwl_mvm_sta *mvmsta;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ struct ieee80211_rx_status *stats = IEEE80211_SKB_RXCB(skb);
+ struct iwl_mvm_key_pn *ptk_pn;
+ u8 tid, keyidx;
+ u8 pn[IEEE80211_CCMP_PN_LEN];
+ u8 *extiv;
+
+ /* do PN checking */
+
+ /* multicast and non-data only arrives on default queue */
+ if (!ieee80211_is_data(hdr->frame_control) ||
+ is_multicast_ether_addr(hdr->addr1))
+ return 0;
+
+ /* do not check PN for open AP */
+ if (!(stats->flag & RX_FLAG_DECRYPTED))
+ return 0;
+
+ /*
+ * avoid checking for default queue - we don't want to replicate
+ * all the logic that's necessary for checking the PN on fragmented
+ * frames, leave that to mac80211
+ */
+ if (queue == 0)
+ return 0;
+
+ /* if we are here - this for sure is either CCMP or GCMP */
+ if (IS_ERR_OR_NULL(sta)) {
+ IWL_ERR(mvm,
+ "expected hw-decrypted unicast frame for station\n");
+ return -1;
+ }
+
+ mvmsta = iwl_mvm_sta_from_mac80211(sta);
+
+ extiv = (u8 *)hdr + ieee80211_hdrlen(hdr->frame_control);
+ keyidx = extiv[3] >> 6;
+
+ ptk_pn = rcu_dereference(mvmsta->ptk_pn[keyidx]);
+ if (!ptk_pn)
+ return -1;
+
+ if (ieee80211_is_data_qos(hdr->frame_control))
+ tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
+ else
+ tid = 0;
+
+ /* we don't use HCCA/802.11 QoS TSPECs, so drop such frames */
+ if (tid >= IWL_MAX_TID_COUNT)
+ return -1;
+
+ /* load pn */
+ pn[0] = extiv[7];
+ pn[1] = extiv[6];
+ pn[2] = extiv[5];
+ pn[3] = extiv[4];
+ pn[4] = extiv[1];
+ pn[5] = extiv[0];
+
+ if (memcmp(pn, ptk_pn->q[queue].pn[tid],
+ IEEE80211_CCMP_PN_LEN) <= 0)
+ return -1;
+
+ memcpy(ptk_pn->q[queue].pn[tid], pn, IEEE80211_CCMP_PN_LEN);
+ stats->flag |= RX_FLAG_PN_VALIDATED;
+
+ return 0;
+}
+
+/* iwl_mvm_create_skb Adds the rxb to a new skb */
+static void iwl_mvm_create_skb(struct sk_buff *skb, struct ieee80211_hdr *hdr,
+ u16 len, u8 crypt_len,
+ struct iwl_rx_cmd_buffer *rxb)
{
unsigned int hdrlen, fraglen;
@@ -112,8 +183,18 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset,
fraglen, rxb->truesize);
}
+}
- ieee80211_rx_napi(mvm->hw, skb, napi);
+/* iwl_mvm_pass_packet_to_mac80211 - passes the packet for mac80211 */
+static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
+ struct napi_struct *napi,
+ struct sk_buff *skb, int queue,
+ struct ieee80211_sta *sta)
+{
+ if (iwl_mvm_check_pn(mvm, skb, queue, sta))
+ kfree_skb(skb);
+ else
+ ieee80211_rx_napi(mvm->hw, skb, napi);
}
static void iwl_mvm_get_signal_strength(struct iwl_mvm *mvm,
@@ -141,7 +222,7 @@ static void iwl_mvm_get_signal_strength(struct iwl_mvm *mvm,
rx_status->chain_signal[2] = energy_c;
}
-static u32 iwl_mvm_rx_crypto(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,
+static int iwl_mvm_rx_crypto(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,
struct ieee80211_rx_status *stats,
struct iwl_rx_mpdu_desc *desc, int queue,
u8 *crypt_len)
@@ -158,6 +239,7 @@ static u32 iwl_mvm_rx_crypto(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,
switch (status & IWL_RX_MPDU_STATUS_SEC_MASK) {
case IWL_RX_MPDU_STATUS_SEC_CCM:
case IWL_RX_MPDU_STATUS_SEC_GCM:
+ BUILD_BUG_ON(IEEE80211_CCMP_PN_LEN != IEEE80211_GCMP_PN_LEN);
/* alg is CCM: check MIC only */
if (!(status & IWL_RX_MPDU_STATUS_MIC_OK))
return -1;
@@ -217,7 +299,6 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
u32 rate_n_flags = le32_to_cpu(desc->rate_n_flags);
struct ieee80211_sta *sta = NULL;
struct sk_buff *skb;
- u32 ampdu_status;
u8 crypt_len = 0;
/* Dont use dev_alloc_skb(), we'll have enough headroom once
@@ -311,8 +392,6 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
iwl_mvm_rx_csum(sta, skb, desc);
}
- rcu_read_unlock();
-
/*
* TODO: PHY info.
* Verify we don't have the information in the MPDU descriptor and
@@ -367,8 +446,9 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
/* TODO: PHY info - update ampdu queue statistics (for debugfs) */
/* TODO: PHY info - gscan */
- iwl_mvm_pass_packet_to_mac80211(mvm, napi, skb, hdr, len, ampdu_status,
- crypt_len, rxb);
+ iwl_mvm_create_skb(skb, hdr, len, crypt_len, rxb);
+ iwl_mvm_pass_packet_to_mac80211(mvm, napi, skb, queue, sta);
+ rcu_read_unlock();
}
void iwl_mvm_rx_frame_release(struct iwl_mvm *mvm,