mac80211: Use a separate CCMP PN receive counter for management frames
authorJouni Malinen <jouni.malinen@atheros.com>
Fri, 11 Jun 2010 17:27:33 +0000 (10:27 -0700)
committerJohn W. Linville <linville@tuxdriver.com>
Tue, 15 Jun 2010 20:00:49 +0000 (16:00 -0400)
When management frame protection (IEEE 802.11w) is used, we must use a
separate counter for tracking received CCMP packet number for the
management frames. The previously used NUM_RX_DATA_QUEUESth queue was
shared with data frames when QoS was not used and that can cause
problems in detecting replays incorrectly for robust management frames.
Add a new counter just for robust management frames to avoid this issue.

Signed-off-by: Jouni Malinen <jouni.malinen@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
net/mac80211/debugfs_key.c
net/mac80211/key.c
net/mac80211/key.h
net/mac80211/rx.c
net/mac80211/wpa.c

index 97c9e46e859e9dbd081d5964437c9db73440441a..fa5e76e658ef058b5fb87c4d4647c5025a3e4d71 100644 (file)
@@ -143,7 +143,7 @@ static ssize_t key_rx_spec_read(struct file *file, char __user *userbuf,
                len = p - buf;
                break;
        case ALG_CCMP:
-               for (i = 0; i < NUM_RX_DATA_QUEUES; i++) {
+               for (i = 0; i < NUM_RX_DATA_QUEUES + 1; i++) {
                        rpn = key->u.ccmp.rx_pn[i];
                        p += scnprintf(p, sizeof(buf)+buf-p,
                                       "%02x%02x%02x%02x%02x%02x\n",
index d0d9001a4a6a32d34c2eba43412715d6bd3cd252..50d1cff23d8eef828050a4e0cd023fa649bd277e 100644 (file)
@@ -273,7 +273,7 @@ struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg,
                key->conf.iv_len = CCMP_HDR_LEN;
                key->conf.icv_len = CCMP_MIC_LEN;
                if (seq) {
-                       for (i = 0; i < NUM_RX_DATA_QUEUES; i++)
+                       for (i = 0; i < NUM_RX_DATA_QUEUES + 1; i++)
                                for (j = 0; j < CCMP_PN_LEN; j++)
                                        key->u.ccmp.rx_pn[i][j] =
                                                seq[CCMP_PN_LEN - j - 1];
index 9996e3be6e63805a31d459363032318de1a8a694..a3849fa3fce833ffa4c10133fabfb4723280a4e3 100644 (file)
@@ -77,7 +77,13 @@ struct ieee80211_key {
                } tkip;
                struct {
                        u8 tx_pn[6];
-                       u8 rx_pn[NUM_RX_DATA_QUEUES][6];
+                       /*
+                        * Last received packet number. The first
+                        * NUM_RX_DATA_QUEUES counters are used with Data
+                        * frames and the last counter is used with Robust
+                        * Management frames.
+                        */
+                       u8 rx_pn[NUM_RX_DATA_QUEUES + 1][6];
                        struct crypto_cipher *tfm;
                        u32 replays; /* dot11RSNAStatsCCMPReplays */
                        /* scratch buffers for virt_to_page() (crypto API) */
index 1594ebe80a4f17b255da03772950877c7484319b..1f76352caa9eb0e2995dadb904d150b3143d7974 100644 (file)
@@ -1267,11 +1267,13 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
                                                 rx->queue, &(rx->skb));
                if (rx->key && rx->key->conf.alg == ALG_CCMP &&
                    ieee80211_has_protected(fc)) {
+                       int queue = ieee80211_is_mgmt(fc) ?
+                               NUM_RX_DATA_QUEUES : rx->queue;
                        /* Store CCMP PN so that we can verify that the next
                         * fragment has a sequential PN value. */
                        entry->ccmp = 1;
                        memcpy(entry->last_pn,
-                              rx->key->u.ccmp.rx_pn[rx->queue],
+                              rx->key->u.ccmp.rx_pn[queue],
                               CCMP_PN_LEN);
                }
                return RX_QUEUED;
@@ -1291,6 +1293,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
        if (entry->ccmp) {
                int i;
                u8 pn[CCMP_PN_LEN], *rpn;
+               int queue;
                if (!rx->key || rx->key->conf.alg != ALG_CCMP)
                        return RX_DROP_UNUSABLE;
                memcpy(pn, entry->last_pn, CCMP_PN_LEN);
@@ -1299,7 +1302,9 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
                        if (pn[i])
                                break;
                }
-               rpn = rx->key->u.ccmp.rx_pn[rx->queue];
+               queue = ieee80211_is_mgmt(fc) ?
+                       NUM_RX_DATA_QUEUES : rx->queue;
+               rpn = rx->key->u.ccmp.rx_pn[queue];
                if (memcmp(pn, rpn, CCMP_PN_LEN))
                        return RX_DROP_UNUSABLE;
                memcpy(entry->last_pn, pn, CCMP_PN_LEN);
index 0adbcc941ac917952d9eb7ed9308e23c4644c720..a14e67707476ac4e7cb4e1f24b6ce77b2cc57969 100644 (file)
@@ -436,6 +436,7 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
        struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
        u8 pn[CCMP_PN_LEN];
        int data_len;
+       int queue;
 
        hdrlen = ieee80211_hdrlen(hdr->frame_control);
 
@@ -453,7 +454,10 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
 
        ccmp_hdr2pn(pn, skb->data + hdrlen);
 
-       if (memcmp(pn, key->u.ccmp.rx_pn[rx->queue], CCMP_PN_LEN) <= 0) {
+       queue = ieee80211_is_mgmt(hdr->frame_control) ?
+               NUM_RX_DATA_QUEUES : rx->queue;
+
+       if (memcmp(pn, key->u.ccmp.rx_pn[queue], CCMP_PN_LEN) <= 0) {
                key->u.ccmp.replays++;
                return RX_DROP_UNUSABLE;
        }
@@ -470,7 +474,7 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
                        return RX_DROP_UNUSABLE;
        }
 
-       memcpy(key->u.ccmp.rx_pn[rx->queue], pn, CCMP_PN_LEN);
+       memcpy(key->u.ccmp.rx_pn[queue], pn, CCMP_PN_LEN);
 
        /* Remove CCMP header and MIC */
        skb_trim(skb, skb->len - CCMP_MIC_LEN);