[PATCH] ieee80211: Move IV/ICV stripping into ieee80211_rx
[linux-2.6.git] / net / ieee80211 / ieee80211_rx.c
index 2759312..d926519 100644 (file)
@@ -415,17 +415,16 @@ int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb,
            ieee->host_mc_decrypt : ieee->host_decrypt;
 
        if (can_be_decrypted) {
-               int idx = 0;
                if (skb->len >= hdrlen + 3) {
                        /* Top two-bits of byte 3 are the key index */
-                       idx = skb->data[hdrlen + 3] >> 6;
+                       keyidx = skb->data[hdrlen + 3] >> 6;
                }
 
-               /* ieee->crypt[] is WEP_KEY (4) in length.  Given that idx
-                * is only allowed 2-bits of storage, no value of idx can
-                * be provided via above code that would result in idx
+               /* ieee->crypt[] is WEP_KEY (4) in length.  Given that keyidx
+                * is only allowed 2-bits of storage, no value of keyidx can
+                * be provided via above code that would result in keyidx
                 * being out of range */
-               crypt = ieee->crypt[idx];
+               crypt = ieee->crypt[keyidx];
 
 #ifdef NOT_YET
                sta = NULL;
@@ -655,6 +654,51 @@ int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb,
                goto rx_dropped;
        }
 
+       /* If the frame was decrypted in hardware, we may need to strip off
+        * any security data (IV, ICV, etc) that was left behind */
+       if (!can_be_decrypted && (fc & IEEE80211_FCTL_PROTECTED) &&
+           ieee->host_strip_iv_icv) {
+               int trimlen = 0;
+
+               /* Top two-bits of byte 3 are the key index */
+               if (skb->len >= hdrlen + 3)
+                       keyidx = skb->data[hdrlen + 3] >> 6;
+
+               /* To strip off any security data which appears before the
+                * payload, we simply increase hdrlen (as the header gets
+                * chopped off immediately below). For the security data which
+                * appears after the payload, we use skb_trim. */
+
+               switch (ieee->sec.encode_alg[keyidx]) {
+               case SEC_ALG_WEP:
+                       /* 4 byte IV */
+                       hdrlen += 4;
+                       /* 4 byte ICV */
+                       trimlen = 4;
+                       break;
+               case SEC_ALG_TKIP:
+                       /* 4 byte IV, 4 byte ExtIV */
+                       hdrlen += 8;
+                       /* 8 byte MIC, 4 byte ICV */
+                       trimlen = 12;
+                       break;
+               case SEC_ALG_CCMP:
+                       /* 8 byte CCMP header */
+                       hdrlen += 8;
+                       /* 8 byte MIC */
+                       trimlen = 8;
+                       break;
+               }
+
+               if (skb->len < trimlen)
+                       goto rx_dropped;
+
+               __skb_trim(skb, skb->len - trimlen);
+
+               if (skb->len < hdrlen)
+                       goto rx_dropped;
+       }
+
        /* skb: hdr + (possible reassembled) full plaintext payload */
 
        payload = skb->data + hdrlen;