mac80211: consolidate MIC failure report handling
authorChristian Lamparter <chunkeey@googlemail.com>
Sat, 30 Apr 2011 13:24:30 +0000 (15:24 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Mon, 2 May 2011 18:49:14 +0000 (14:49 -0400)
Currently, mac80211 handles MIC failures differently
depending on whenever they are detected by the stack's
own software crypto or when are handed down from the
driver.

This patch tries to unify both by moving the special
branch out of mac80211 rx hotpath and into into the
software crypto part. This has the advantage that we
can run a few more sanity checks on the data and verify
if the key type was TKIP. This is very handy because
several devices generate false postive MIC failure
reports. Like carl9170, ath9k and wl12xx:
<http://www.spinics.net/lists/linux-wireless/msg68494.html>
"mac80211: report MIC failure for truncated packets in AP mode"

Cc: Luciano Coelho <coelho@ti.com>
Cc: Arik Nemtsov <arik@wizery.com>
Signed-off-by: Christian Lamparter <chunkeey@googlemail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
net/mac80211/rx.c
net/mac80211/wpa.c

index b04a4378adccb21440c1714d5744926bcb5c1f1c..81241e18f3a4535da4c0b2f321df3122102edb67 100644 (file)
@@ -2368,47 +2368,6 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
        return RX_QUEUED;
 }
 
-static void ieee80211_rx_michael_mic_report(struct ieee80211_hdr *hdr,
-                                           struct ieee80211_rx_data *rx)
-{
-       int keyidx;
-       unsigned int hdrlen;
-
-       hdrlen = ieee80211_hdrlen(hdr->frame_control);
-       if (rx->skb->len >= hdrlen + 4)
-               keyidx = rx->skb->data[hdrlen + 3] >> 6;
-       else
-               keyidx = -1;
-
-       if (!rx->sta) {
-               /*
-                * Some hardware seem to generate incorrect Michael MIC
-                * reports; ignore them to avoid triggering countermeasures.
-                */
-               return;
-       }
-
-       if (!ieee80211_has_protected(hdr->frame_control))
-               return;
-
-       if (rx->sdata->vif.type == NL80211_IFTYPE_AP && keyidx) {
-               /*
-                * APs with pairwise keys should never receive Michael MIC
-                * errors for non-zero keyidx because these are reserved for
-                * group keys and only the AP is sending real multicast
-                * frames in the BSS.
-                */
-               return;
-       }
-
-       if (!ieee80211_is_data(hdr->frame_control) &&
-           !ieee80211_is_auth(hdr->frame_control))
-               return;
-
-       mac80211_ev_michael_mic_failure(rx->sdata, keyidx, hdr, NULL,
-                                       GFP_ATOMIC);
-}
-
 /* TODO: use IEEE80211_RX_FRAGMENTED */
 static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx,
                                        struct ieee80211_rate *rate)
@@ -2752,12 +2711,6 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,
        if (!prepares)
                return false;
 
-       if (status->flag & RX_FLAG_MMIC_ERROR) {
-               if (status->rx_flags & IEEE80211_RX_RA_MATCH)
-                       ieee80211_rx_michael_mic_report(hdr, rx);
-               return false;
-       }
-
        if (!consume) {
                skb = skb_copy(skb, GFP_ATOMIC);
                if (!skb) {
index f1765de2f4bf7a42e06794b8155a79d6bb52b694..9dc3b5f26e80c800a306aca5287b0e22fcda5378 100644 (file)
@@ -87,42 +87,76 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx)
        struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
 
-       /* No way to verify the MIC if the hardware stripped it */
-       if (status->flag & RX_FLAG_MMIC_STRIPPED)
+       /*
+        * it makes no sense to check for MIC errors on anything other
+        * than data frames.
+        */
+       if (!ieee80211_is_data_present(hdr->frame_control))
+               return RX_CONTINUE;
+
+       /*
+        * No way to verify the MIC if the hardware stripped it or
+        * the IV with the key index. In this case we have solely rely
+        * on the driver to set RX_FLAG_MMIC_ERROR in the event of a
+        * MIC failure report.
+        */
+       if (status->flag & (RX_FLAG_MMIC_STRIPPED | RX_FLAG_IV_STRIPPED)) {
+               if (status->flag & RX_FLAG_MMIC_ERROR)
+                       goto mic_fail;
+
+               if (!(status->flag & RX_FLAG_IV_STRIPPED))
+                       goto update_iv;
+
                return RX_CONTINUE;
+       }
 
+       /*
+        * Some hardware seems to generate Michael MIC failure reports; even
+        * though, the frame was not encrypted with TKIP and therefore has no
+        * MIC. Ignore the flag them to avoid triggering countermeasures.
+        */
        if (!rx->key || rx->key->conf.cipher != WLAN_CIPHER_SUITE_TKIP ||
-           !ieee80211_has_protected(hdr->frame_control) ||
-           !ieee80211_is_data_present(hdr->frame_control))
+           !(status->flag & RX_FLAG_DECRYPTED))
                return RX_CONTINUE;
 
+       if (rx->sdata->vif.type == NL80211_IFTYPE_AP && rx->key->conf.keyidx) {
+               /*
+                * APs with pairwise keys should never receive Michael MIC
+                * errors for non-zero keyidx because these are reserved for
+                * group keys and only the AP is sending real multicast
+                * frames in the BSS. (
+                */
+               return RX_DROP_UNUSABLE;
+       }
+
+       if (status->flag & RX_FLAG_MMIC_ERROR)
+               goto mic_fail;
+
        hdrlen = ieee80211_hdrlen(hdr->frame_control);
        if (skb->len < hdrlen + MICHAEL_MIC_LEN)
                return RX_DROP_UNUSABLE;
 
        data = skb->data + hdrlen;
        data_len = skb->len - hdrlen - MICHAEL_MIC_LEN;
-
        key = &rx->key->conf.key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY];
        michael_mic(key, hdr, data, data_len, mic);
-       if (memcmp(mic, data + data_len, MICHAEL_MIC_LEN) != 0) {
-               if (!(status->rx_flags & IEEE80211_RX_RA_MATCH))
-                       return RX_DROP_UNUSABLE;
-
-               mac80211_ev_michael_mic_failure(rx->sdata, rx->key->conf.keyidx,
-                                               (void *) skb->data, NULL,
-                                               GFP_ATOMIC);
-               return RX_DROP_UNUSABLE;
-       }
+       if (memcmp(mic, data + data_len, MICHAEL_MIC_LEN) != 0)
+               goto mic_fail;
 
        /* remove Michael MIC from payload */
        skb_trim(skb, skb->len - MICHAEL_MIC_LEN);
 
+update_iv:
        /* update IV in key information to be able to detect replays */
        rx->key->u.tkip.rx[rx->queue].iv32 = rx->tkip_iv32;
        rx->key->u.tkip.rx[rx->queue].iv16 = rx->tkip_iv16;
 
        return RX_CONTINUE;
+
+mic_fail:
+       mac80211_ev_michael_mic_failure(rx->sdata, rx->key->conf.keyidx,
+                                       (void *) skb->data, NULL, GFP_ATOMIC);
+       return RX_DROP_UNUSABLE;
 }