From 65b6a2775102cd81e57158ef4b1cb89641f76cfd Mon Sep 17 00:00:00 2001 From: Zhu Yi Date: Mon, 21 Aug 2006 11:32:31 +0800 Subject: [PATCH] ieee80211: Fix header->qos_ctl endian issue Signed-off-by: Jackie Wu Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- net/ieee80211/ieee80211_crypt_tkip.c | 2 +- net/ieee80211/ieee80211_tx.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/ieee80211/ieee80211_crypt_tkip.c b/net/ieee80211/ieee80211_crypt_tkip.c index 34dba0ba545d..a61b09ef70f2 100644 --- a/net/ieee80211/ieee80211_crypt_tkip.c +++ b/net/ieee80211/ieee80211_crypt_tkip.c @@ -528,7 +528,7 @@ static void michael_mic_hdr(struct sk_buff *skb, u8 * hdr) if (stype & IEEE80211_STYPE_QOS_DATA) { const struct ieee80211_hdr_3addrqos *qoshdr = (struct ieee80211_hdr_3addrqos *)skb->data; - hdr[12] = le16_to_cpu(qoshdr->qos_ctl) & IEEE80211_QCTL_TID; + hdr[12] = qoshdr->qos_ctl & cpu_to_le16(IEEE80211_QCTL_TID); } else hdr[12] = 0; /* priority */ diff --git a/net/ieee80211/ieee80211_tx.c b/net/ieee80211/ieee80211_tx.c index bf042139c7ab..f2e61311552b 100644 --- a/net/ieee80211/ieee80211_tx.c +++ b/net/ieee80211/ieee80211_tx.c @@ -337,7 +337,7 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev) hdr_len += 2; skb->priority = ieee80211_classify(skb); - header.qos_ctl |= skb->priority & IEEE80211_QCTL_TID; + header.qos_ctl |= cpu_to_le16(skb->priority & IEEE80211_QCTL_TID); } header.frame_ctl = cpu_to_le16(fc); -- cgit v1.2.3 From 051562f7e980b53f7bc6529f2e55b68e20f5d0e6 Mon Sep 17 00:00:00 2001 From: Zhu Yi Date: Mon, 21 Aug 2006 11:32:47 +0800 Subject: [PATCH] ieee80211: remove ieee80211_tx() is_queue_full warning Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- net/ieee80211/ieee80211_tx.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'net') diff --git a/net/ieee80211/ieee80211_tx.c b/net/ieee80211/ieee80211_tx.c index f2e61311552b..ae254497ba3d 100644 --- a/net/ieee80211/ieee80211_tx.c +++ b/net/ieee80211/ieee80211_tx.c @@ -532,13 +532,6 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev) return 0; } - if (ret == NETDEV_TX_BUSY) { - printk(KERN_ERR "%s: NETDEV_TX_BUSY returned; " - "driver should report queue full via " - "ieee_device->is_queue_full.\n", - ieee->dev->name); - } - ieee80211_txb_free(txb); } -- cgit v1.2.3 From b4328d87ec5711543b818fea2e1cf64f09d326f1 Mon Sep 17 00:00:00 2001 From: Zhu Yi Date: Mon, 21 Aug 2006 11:33:09 +0800 Subject: [PATCH] ieee80211: TKIP and CCMP replay check rework Signed-off-by: Hong Liu Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- net/ieee80211/ieee80211_crypt_ccmp.c | 23 ++++++++++++++++++++++- net/ieee80211/ieee80211_crypt_tkip.c | 16 ++++++++++++++-- 2 files changed, 36 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/ieee80211/ieee80211_crypt_ccmp.c b/net/ieee80211/ieee80211_crypt_ccmp.c index ed90a8af1444..098c66846339 100644 --- a/net/ieee80211/ieee80211_crypt_ccmp.c +++ b/net/ieee80211/ieee80211_crypt_ccmp.c @@ -271,6 +271,27 @@ static int ieee80211_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv) return 0; } +/* + * deal with seq counter wrapping correctly. + * refer to timer_after() for jiffies wrapping handling + */ +static inline int ccmp_replay_check(u8 *pn_n, u8 *pn_o) +{ + u32 iv32_n, iv16_n; + u32 iv32_o, iv16_o; + + iv32_n = (pn_n[0] << 24) | (pn_n[1] << 16) | (pn_n[2] << 8) | pn_n[3]; + iv16_n = (pn_n[4] << 8) | pn_n[5]; + + iv32_o = (pn_o[0] << 24) | (pn_o[1] << 16) | (pn_o[2] << 8) | pn_o[3]; + iv16_o = (pn_o[4] << 8) | pn_o[5]; + + if ((s32)iv32_n - (s32)iv32_o < 0 || + (iv32_n == iv32_o && iv16_n <= iv16_o)) + return 1; + return 0; +} + static int ieee80211_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv) { struct ieee80211_ccmp_data *key = priv; @@ -323,7 +344,7 @@ static int ieee80211_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv) pn[5] = pos[0]; pos += 8; - if (memcmp(pn, key->rx_pn, CCMP_PN_LEN) <= 0) { + if (ccmp_replay_check(pn, key->rx_pn)) { if (net_ratelimit()) { printk(KERN_DEBUG "CCMP: replay detected: STA=" MAC_FMT " previous PN %02x%02x%02x%02x%02x%02x " diff --git a/net/ieee80211/ieee80211_crypt_tkip.c b/net/ieee80211/ieee80211_crypt_tkip.c index a61b09ef70f2..02abf2985b84 100644 --- a/net/ieee80211/ieee80211_crypt_tkip.c +++ b/net/ieee80211/ieee80211_crypt_tkip.c @@ -360,6 +360,19 @@ static int ieee80211_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv) return 0; } +/* + * deal with seq counter wrapping correctly. + * refer to timer_after() for jiffies wrapping handling + */ +static inline int tkip_replay_check(u32 iv32_n, u16 iv16_n, + u32 iv32_o, u16 iv16_o) +{ + if ((s32)iv32_n - (s32)iv32_o < 0 || + (iv32_n == iv32_o && iv16_n <= iv16_o)) + return 1; + return 0; +} + static int ieee80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv) { struct ieee80211_tkip_data *tkey = priv; @@ -414,8 +427,7 @@ static int ieee80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv) iv32 = pos[4] | (pos[5] << 8) | (pos[6] << 16) | (pos[7] << 24); pos += 8; - if (iv32 < tkey->rx_iv32 || - (iv32 == tkey->rx_iv32 && iv16 <= tkey->rx_iv16)) { + if (tkip_replay_check(iv32, iv16, tkey->rx_iv32, tkey->rx_iv16)) { if (net_ratelimit()) { printk(KERN_DEBUG "TKIP: replay detected: STA=" MAC_FMT " previous TSC %08x%04x received TSC " -- cgit v1.2.3 From 5a656949719bf8598ad1e93a56eb11e70a4c3208 Mon Sep 17 00:00:00 2001 From: Zhu Yi Date: Mon, 21 Aug 2006 11:33:56 +0800 Subject: [PATCH] ieee80211: Fix TKIP and WEP decryption error on SMP machines The IEEE80211 TKIP and WEP Tx and Rx paths use the same crypto_tfm to encrypt and decrypt data. During the encrypt and decrypt process, both of them will set a new key to crypto_tfm. If they happen on the same time, it will corrupt the crypto_tfm. Thus users will receive an ICV error or Michael MIC error. This only likely to happen on SMP box with heavy traffic both on Tx and Rx. The patch use two sets of crypto_tfms to avoid this problem. Signed-off-by: Hong Liu Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- net/ieee80211/ieee80211_crypt_tkip.c | 90 ++++++++++++++++++++++++------------ net/ieee80211/ieee80211_crypt_wep.c | 35 +++++++++----- 2 files changed, 84 insertions(+), 41 deletions(-) (limited to 'net') diff --git a/net/ieee80211/ieee80211_crypt_tkip.c b/net/ieee80211/ieee80211_crypt_tkip.c index 02abf2985b84..f2df2f5b3e4c 100644 --- a/net/ieee80211/ieee80211_crypt_tkip.c +++ b/net/ieee80211/ieee80211_crypt_tkip.c @@ -52,8 +52,10 @@ struct ieee80211_tkip_data { int key_idx; - struct crypto_tfm *tfm_arc4; - struct crypto_tfm *tfm_michael; + struct crypto_tfm *tx_tfm_arc4; + struct crypto_tfm *tx_tfm_michael; + struct crypto_tfm *rx_tfm_arc4; + struct crypto_tfm *rx_tfm_michael; /* scratch buffers for virt_to_page() (crypto API) */ u8 rx_hdr[16], tx_hdr[16]; @@ -85,15 +87,29 @@ static void *ieee80211_tkip_init(int key_idx) priv->key_idx = key_idx; - priv->tfm_arc4 = crypto_alloc_tfm("arc4", 0); - if (priv->tfm_arc4 == NULL) { + priv->tx_tfm_arc4 = crypto_alloc_tfm("arc4", 0); + if (priv->tx_tfm_arc4 == NULL) { printk(KERN_DEBUG "ieee80211_crypt_tkip: could not allocate " "crypto API arc4\n"); goto fail; } - priv->tfm_michael = crypto_alloc_tfm("michael_mic", 0); - if (priv->tfm_michael == NULL) { + priv->tx_tfm_michael = crypto_alloc_tfm("michael_mic", 0); + if (priv->tx_tfm_michael == NULL) { + printk(KERN_DEBUG "ieee80211_crypt_tkip: could not allocate " + "crypto API michael_mic\n"); + goto fail; + } + + priv->rx_tfm_arc4 = crypto_alloc_tfm("arc4", 0); + if (priv->rx_tfm_arc4 == NULL) { + printk(KERN_DEBUG "ieee80211_crypt_tkip: could not allocate " + "crypto API arc4\n"); + goto fail; + } + + priv->rx_tfm_michael = crypto_alloc_tfm("michael_mic", 0); + if (priv->rx_tfm_michael == NULL) { printk(KERN_DEBUG "ieee80211_crypt_tkip: could not allocate " "crypto API michael_mic\n"); goto fail; @@ -103,10 +119,14 @@ static void *ieee80211_tkip_init(int key_idx) fail: if (priv) { - if (priv->tfm_michael) - crypto_free_tfm(priv->tfm_michael); - if (priv->tfm_arc4) - crypto_free_tfm(priv->tfm_arc4); + if (priv->tx_tfm_michael) + crypto_free_tfm(priv->tx_tfm_michael); + if (priv->tx_tfm_arc4) + crypto_free_tfm(priv->tx_tfm_arc4); + if (priv->rx_tfm_michael) + crypto_free_tfm(priv->rx_tfm_michael); + if (priv->rx_tfm_arc4) + crypto_free_tfm(priv->rx_tfm_arc4); kfree(priv); } @@ -116,10 +136,16 @@ static void *ieee80211_tkip_init(int key_idx) static void ieee80211_tkip_deinit(void *priv) { struct ieee80211_tkip_data *_priv = priv; - if (_priv && _priv->tfm_michael) - crypto_free_tfm(_priv->tfm_michael); - if (_priv && _priv->tfm_arc4) - crypto_free_tfm(_priv->tfm_arc4); + if (_priv) { + if (_priv->tx_tfm_michael) + crypto_free_tfm(_priv->tx_tfm_michael); + if (_priv->tx_tfm_arc4) + crypto_free_tfm(_priv->tx_tfm_arc4); + if (_priv->rx_tfm_michael) + crypto_free_tfm(_priv->rx_tfm_michael); + if (_priv->rx_tfm_arc4) + crypto_free_tfm(_priv->rx_tfm_arc4); + } kfree(priv); } @@ -351,11 +377,11 @@ static int ieee80211_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv) icv[2] = crc >> 16; icv[3] = crc >> 24; - crypto_cipher_setkey(tkey->tfm_arc4, rc4key, 16); + crypto_cipher_setkey(tkey->tx_tfm_arc4, rc4key, 16); sg.page = virt_to_page(pos); sg.offset = offset_in_page(pos); sg.length = len + 4; - crypto_cipher_encrypt(tkey->tfm_arc4, &sg, &sg, len + 4); + crypto_cipher_encrypt(tkey->tx_tfm_arc4, &sg, &sg, len + 4); return 0; } @@ -446,11 +472,11 @@ static int ieee80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv) plen = skb->len - hdr_len - 12; - crypto_cipher_setkey(tkey->tfm_arc4, rc4key, 16); + crypto_cipher_setkey(tkey->rx_tfm_arc4, rc4key, 16); sg.page = virt_to_page(pos); sg.offset = offset_in_page(pos); sg.length = plen + 4; - crypto_cipher_decrypt(tkey->tfm_arc4, &sg, &sg, plen + 4); + crypto_cipher_decrypt(tkey->rx_tfm_arc4, &sg, &sg, plen + 4); crc = ~crc32_le(~0, pos, plen); icv[0] = crc; @@ -484,12 +510,12 @@ static int ieee80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv) return keyidx; } -static int michael_mic(struct ieee80211_tkip_data *tkey, u8 * key, u8 * hdr, +static int michael_mic(struct crypto_tfm *tfm_michael, u8 * key, u8 * hdr, u8 * data, size_t data_len, u8 * mic) { struct scatterlist sg[2]; - if (tkey->tfm_michael == NULL) { + if (tfm_michael == NULL) { printk(KERN_WARNING "michael_mic: tfm_michael == NULL\n"); return -1; } @@ -501,10 +527,10 @@ static int michael_mic(struct ieee80211_tkip_data *tkey, u8 * key, u8 * hdr, sg[1].offset = offset_in_page(data); sg[1].length = data_len; - crypto_digest_init(tkey->tfm_michael); - crypto_digest_setkey(tkey->tfm_michael, key, 8); - crypto_digest_update(tkey->tfm_michael, sg, 2); - crypto_digest_final(tkey->tfm_michael, mic); + crypto_digest_init(tfm_michael); + crypto_digest_setkey(tfm_michael, key, 8); + crypto_digest_update(tfm_michael, sg, 2); + crypto_digest_final(tfm_michael, mic); return 0; } @@ -562,7 +588,7 @@ static int ieee80211_michael_mic_add(struct sk_buff *skb, int hdr_len, michael_mic_hdr(skb, tkey->tx_hdr); pos = skb_put(skb, 8); - if (michael_mic(tkey, &tkey->key[16], tkey->tx_hdr, + if (michael_mic(tkey->tx_tfm_michael, &tkey->key[16], tkey->tx_hdr, skb->data + hdr_len, skb->len - 8 - hdr_len, pos)) return -1; @@ -600,7 +626,7 @@ static int ieee80211_michael_mic_verify(struct sk_buff *skb, int keyidx, return -1; michael_mic_hdr(skb, tkey->rx_hdr); - if (michael_mic(tkey, &tkey->key[24], tkey->rx_hdr, + if (michael_mic(tkey->rx_tfm_michael, &tkey->key[24], tkey->rx_hdr, skb->data + hdr_len, skb->len - 8 - hdr_len, mic)) return -1; if (memcmp(mic, skb->data + skb->len - 8, 8) != 0) { @@ -630,14 +656,18 @@ static int ieee80211_tkip_set_key(void *key, int len, u8 * seq, void *priv) { struct ieee80211_tkip_data *tkey = priv; int keyidx; - struct crypto_tfm *tfm = tkey->tfm_michael; - struct crypto_tfm *tfm2 = tkey->tfm_arc4; + struct crypto_tfm *tfm = tkey->tx_tfm_michael; + struct crypto_tfm *tfm2 = tkey->tx_tfm_arc4; + struct crypto_tfm *tfm3 = tkey->rx_tfm_michael; + struct crypto_tfm *tfm4 = tkey->rx_tfm_arc4; keyidx = tkey->key_idx; memset(tkey, 0, sizeof(*tkey)); tkey->key_idx = keyidx; - tkey->tfm_michael = tfm; - tkey->tfm_arc4 = tfm2; + tkey->tx_tfm_michael = tfm; + tkey->tx_tfm_arc4 = tfm2; + tkey->rx_tfm_michael = tfm3; + tkey->rx_tfm_arc4 = tfm4; if (len == TKIP_KEY_LEN) { memcpy(tkey->key, key, TKIP_KEY_LEN); tkey->key_set = 1; diff --git a/net/ieee80211/ieee80211_crypt_wep.c b/net/ieee80211/ieee80211_crypt_wep.c index 0ebf235f6939..b435b28857ed 100644 --- a/net/ieee80211/ieee80211_crypt_wep.c +++ b/net/ieee80211/ieee80211_crypt_wep.c @@ -32,7 +32,8 @@ struct prism2_wep_data { u8 key[WEP_KEY_LEN + 1]; u8 key_len; u8 key_idx; - struct crypto_tfm *tfm; + struct crypto_tfm *tx_tfm; + struct crypto_tfm *rx_tfm; }; static void *prism2_wep_init(int keyidx) @@ -44,13 +45,19 @@ static void *prism2_wep_init(int keyidx) goto fail; priv->key_idx = keyidx; - priv->tfm = crypto_alloc_tfm("arc4", 0); - if (priv->tfm == NULL) { + priv->tx_tfm = crypto_alloc_tfm("arc4", 0); + if (priv->tx_tfm == NULL) { printk(KERN_DEBUG "ieee80211_crypt_wep: could not allocate " "crypto API arc4\n"); goto fail; } + priv->rx_tfm = crypto_alloc_tfm("arc4", 0); + if (priv->rx_tfm == NULL) { + printk(KERN_DEBUG "ieee80211_crypt_wep: could not allocate " + "crypto API arc4\n"); + goto fail; + } /* start WEP IV from a random value */ get_random_bytes(&priv->iv, 4); @@ -58,8 +65,10 @@ static void *prism2_wep_init(int keyidx) fail: if (priv) { - if (priv->tfm) - crypto_free_tfm(priv->tfm); + if (priv->tx_tfm) + crypto_free_tfm(priv->tx_tfm); + if (priv->rx_tfm) + crypto_free_tfm(priv->rx_tfm); kfree(priv); } return NULL; @@ -68,8 +77,12 @@ static void *prism2_wep_init(int keyidx) static void prism2_wep_deinit(void *priv) { struct prism2_wep_data *_priv = priv; - if (_priv && _priv->tfm) - crypto_free_tfm(_priv->tfm); + if (_priv) { + if (_priv->tx_tfm) + crypto_free_tfm(_priv->tx_tfm); + if (_priv->rx_tfm) + crypto_free_tfm(_priv->rx_tfm); + } kfree(priv); } @@ -151,11 +164,11 @@ static int prism2_wep_encrypt(struct sk_buff *skb, int hdr_len, void *priv) icv[2] = crc >> 16; icv[3] = crc >> 24; - crypto_cipher_setkey(wep->tfm, key, klen); + crypto_cipher_setkey(wep->tx_tfm, key, klen); sg.page = virt_to_page(pos); sg.offset = offset_in_page(pos); sg.length = len + 4; - crypto_cipher_encrypt(wep->tfm, &sg, &sg, len + 4); + crypto_cipher_encrypt(wep->tx_tfm, &sg, &sg, len + 4); return 0; } @@ -194,11 +207,11 @@ static int prism2_wep_decrypt(struct sk_buff *skb, int hdr_len, void *priv) /* Apply RC4 to data and compute CRC32 over decrypted data */ plen = skb->len - hdr_len - 8; - crypto_cipher_setkey(wep->tfm, key, klen); + crypto_cipher_setkey(wep->rx_tfm, key, klen); sg.page = virt_to_page(pos); sg.offset = offset_in_page(pos); sg.length = plen + 4; - crypto_cipher_decrypt(wep->tfm, &sg, &sg, plen + 4); + crypto_cipher_decrypt(wep->rx_tfm, &sg, &sg, plen + 4); crc = ~crc32_le(~0, pos, plen); icv[0] = crc; -- cgit v1.2.3 From f09fc44d8c25f22c4d985bb93857338ed02feac6 Mon Sep 17 00:00:00 2001 From: Zhu Yi Date: Mon, 21 Aug 2006 11:34:19 +0800 Subject: [PATCH] ieee80211: Workaround malformed 802.11 frames from AP Stop processing further but return success when we receive a malformed packet from the AP. We need this patch to workaround some AP bugs. For example, the beacon frames from the Orinoco AP1000 contains an IE (value = 128) with length equals to 8 but the actual frame length is only 7. Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- net/ieee80211/ieee80211_rx.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/net/ieee80211/ieee80211_rx.c b/net/ieee80211/ieee80211_rx.c index d60358d702d7..770704183a1b 100644 --- a/net/ieee80211/ieee80211_rx.c +++ b/net/ieee80211/ieee80211_rx.c @@ -1078,13 +1078,16 @@ static int ieee80211_parse_info_param(struct ieee80211_info_element while (length >= sizeof(*info_element)) { if (sizeof(*info_element) + info_element->len > length) { - IEEE80211_DEBUG_MGMT("Info elem: parse failed: " - "info_element->len + 2 > left : " - "info_element->len+2=%zd left=%d, id=%d.\n", - info_element->len + - sizeof(*info_element), - length, info_element->id); - return 1; + IEEE80211_ERROR("Info elem: parse failed: " + "info_element->len + 2 > left : " + "info_element->len+2=%zd left=%d, id=%d.\n", + info_element->len + + sizeof(*info_element), + length, info_element->id); + /* We stop processing but don't return an error here + * because some misbehaviour APs break this rule. ie. + * Orinoco AP1000. */ + break; } switch (info_element->id) { -- cgit v1.2.3