1264fc0498
tree 8428e9f510e6ad6c77baec89cb57374842abf733 parent d78bfd3ddae9c422dd350159110f9c4d7cfc50de author Liu Hong <hong.liu@intel.com> 1124446520 -0500 committer James Ketrenos <jketreno@linux.intel.com> 1127313183 -0500 Fix TKIP, repeated fragmentation problem, and payload_size reporting 1. TKIP encryption Originally, TKIP encryption issues msdu + mpdu encryption on every fragment. Change the behavior to msdu encryption on the whole packet, then mpdu encryption on every fragment. 2. Avoid repeated fragmentation when !host_encrypt. We only need do fragmentation when using host encryption. Otherwise we only need pass the whole packet to driver, letting driver do the fragmentation. 3. change the txb->payload_size to correct value FW will use this value to determine whether to do fragmentation. If we pass the wrong value, fw may cut on the wrong bound which will make decryption fail when we do host encryption. NOTE: This requires changing drivers (hostap) that have extra_prefix_len used within them (structure member name change). Signed-off-by: Hong Liu <liu.hong@intel.com> Signed-off-by: James Ketrenos <jketreno@linux.intel.com> Signed-off-by: Jeff Garzik <jgarzik@pobox.com>
281 lines
6.6 KiB
C
281 lines
6.6 KiB
C
/*
|
|
* Host AP crypto routines
|
|
*
|
|
* Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi>
|
|
* Portions Copyright (C) 2004, Intel Corporation <jketreno@linux.intel.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation. See README and COPYING for
|
|
* more details.
|
|
*
|
|
*/
|
|
|
|
#include <linux/config.h>
|
|
#include <linux/version.h>
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/slab.h>
|
|
#include <asm/string.h>
|
|
#include <asm/errno.h>
|
|
|
|
#include <net/ieee80211.h>
|
|
|
|
MODULE_AUTHOR("Jouni Malinen");
|
|
MODULE_DESCRIPTION("HostAP crypto");
|
|
MODULE_LICENSE("GPL");
|
|
|
|
struct ieee80211_crypto_alg {
|
|
struct list_head list;
|
|
struct ieee80211_crypto_ops *ops;
|
|
};
|
|
|
|
struct ieee80211_crypto {
|
|
struct list_head algs;
|
|
spinlock_t lock;
|
|
};
|
|
|
|
static struct ieee80211_crypto *hcrypt;
|
|
|
|
void ieee80211_crypt_deinit_entries(struct ieee80211_device *ieee, int force)
|
|
{
|
|
struct list_head *ptr, *n;
|
|
struct ieee80211_crypt_data *entry;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&ieee->lock, flags);
|
|
|
|
if (list_empty(&ieee->crypt_deinit_list))
|
|
goto unlock;
|
|
|
|
for (ptr = ieee->crypt_deinit_list.next, n = ptr->next;
|
|
ptr != &ieee->crypt_deinit_list; ptr = n, n = ptr->next) {
|
|
entry = list_entry(ptr, struct ieee80211_crypt_data, list);
|
|
|
|
if (atomic_read(&entry->refcnt) != 0 && !force)
|
|
continue;
|
|
|
|
list_del(ptr);
|
|
|
|
if (entry->ops) {
|
|
entry->ops->deinit(entry->priv);
|
|
module_put(entry->ops->owner);
|
|
}
|
|
kfree(entry);
|
|
}
|
|
unlock:
|
|
spin_unlock_irqrestore(&ieee->lock, flags);
|
|
}
|
|
|
|
/* After this, crypt_deinit_list won't accept new members */
|
|
void ieee80211_crypt_quiescing(struct ieee80211_device *ieee)
|
|
{
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&ieee->lock, flags);
|
|
ieee->crypt_quiesced = 1;
|
|
spin_unlock_irqrestore(&ieee->lock, flags);
|
|
}
|
|
|
|
void ieee80211_crypt_deinit_handler(unsigned long data)
|
|
{
|
|
struct ieee80211_device *ieee = (struct ieee80211_device *)data;
|
|
unsigned long flags;
|
|
|
|
ieee80211_crypt_deinit_entries(ieee, 0);
|
|
|
|
spin_lock_irqsave(&ieee->lock, flags);
|
|
if (!list_empty(&ieee->crypt_deinit_list) && !ieee->crypt_quiesced) {
|
|
printk(KERN_DEBUG "%s: entries remaining in delayed crypt "
|
|
"deletion list\n", ieee->dev->name);
|
|
ieee->crypt_deinit_timer.expires = jiffies + HZ;
|
|
add_timer(&ieee->crypt_deinit_timer);
|
|
}
|
|
spin_unlock_irqrestore(&ieee->lock, flags);
|
|
}
|
|
|
|
void ieee80211_crypt_delayed_deinit(struct ieee80211_device *ieee,
|
|
struct ieee80211_crypt_data **crypt)
|
|
{
|
|
struct ieee80211_crypt_data *tmp;
|
|
unsigned long flags;
|
|
|
|
if (*crypt == NULL)
|
|
return;
|
|
|
|
tmp = *crypt;
|
|
*crypt = NULL;
|
|
|
|
/* must not run ops->deinit() while there may be pending encrypt or
|
|
* decrypt operations. Use a list of delayed deinits to avoid needing
|
|
* locking. */
|
|
|
|
spin_lock_irqsave(&ieee->lock, flags);
|
|
if (!ieee->crypt_quiesced) {
|
|
list_add(&tmp->list, &ieee->crypt_deinit_list);
|
|
if (!timer_pending(&ieee->crypt_deinit_timer)) {
|
|
ieee->crypt_deinit_timer.expires = jiffies + HZ;
|
|
add_timer(&ieee->crypt_deinit_timer);
|
|
}
|
|
}
|
|
spin_unlock_irqrestore(&ieee->lock, flags);
|
|
}
|
|
|
|
int ieee80211_register_crypto_ops(struct ieee80211_crypto_ops *ops)
|
|
{
|
|
unsigned long flags;
|
|
struct ieee80211_crypto_alg *alg;
|
|
|
|
if (hcrypt == NULL)
|
|
return -1;
|
|
|
|
alg = kmalloc(sizeof(*alg), GFP_KERNEL);
|
|
if (alg == NULL)
|
|
return -ENOMEM;
|
|
|
|
memset(alg, 0, sizeof(*alg));
|
|
alg->ops = ops;
|
|
|
|
spin_lock_irqsave(&hcrypt->lock, flags);
|
|
list_add(&alg->list, &hcrypt->algs);
|
|
spin_unlock_irqrestore(&hcrypt->lock, flags);
|
|
|
|
printk(KERN_DEBUG "ieee80211_crypt: registered algorithm '%s'\n",
|
|
ops->name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ieee80211_unregister_crypto_ops(struct ieee80211_crypto_ops *ops)
|
|
{
|
|
unsigned long flags;
|
|
struct list_head *ptr;
|
|
struct ieee80211_crypto_alg *del_alg = NULL;
|
|
|
|
if (hcrypt == NULL)
|
|
return -1;
|
|
|
|
spin_lock_irqsave(&hcrypt->lock, flags);
|
|
for (ptr = hcrypt->algs.next; ptr != &hcrypt->algs; ptr = ptr->next) {
|
|
struct ieee80211_crypto_alg *alg =
|
|
(struct ieee80211_crypto_alg *)ptr;
|
|
if (alg->ops == ops) {
|
|
list_del(&alg->list);
|
|
del_alg = alg;
|
|
break;
|
|
}
|
|
}
|
|
spin_unlock_irqrestore(&hcrypt->lock, flags);
|
|
|
|
if (del_alg) {
|
|
printk(KERN_DEBUG "ieee80211_crypt: unregistered algorithm "
|
|
"'%s'\n", ops->name);
|
|
kfree(del_alg);
|
|
}
|
|
|
|
return del_alg ? 0 : -1;
|
|
}
|
|
|
|
struct ieee80211_crypto_ops *ieee80211_get_crypto_ops(const char *name)
|
|
{
|
|
unsigned long flags;
|
|
struct list_head *ptr;
|
|
struct ieee80211_crypto_alg *found_alg = NULL;
|
|
|
|
if (hcrypt == NULL)
|
|
return NULL;
|
|
|
|
spin_lock_irqsave(&hcrypt->lock, flags);
|
|
for (ptr = hcrypt->algs.next; ptr != &hcrypt->algs; ptr = ptr->next) {
|
|
struct ieee80211_crypto_alg *alg =
|
|
(struct ieee80211_crypto_alg *)ptr;
|
|
if (strcmp(alg->ops->name, name) == 0) {
|
|
found_alg = alg;
|
|
break;
|
|
}
|
|
}
|
|
spin_unlock_irqrestore(&hcrypt->lock, flags);
|
|
|
|
if (found_alg)
|
|
return found_alg->ops;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
static void *ieee80211_crypt_null_init(struct ieee80211_device *ieee,
|
|
int keyidx)
|
|
{
|
|
return (void *)1;
|
|
}
|
|
static void ieee80211_crypt_null_deinit(void *priv)
|
|
{
|
|
}
|
|
|
|
static struct ieee80211_crypto_ops ieee80211_crypt_null = {
|
|
.name = "NULL",
|
|
.init = ieee80211_crypt_null_init,
|
|
.deinit = ieee80211_crypt_null_deinit,
|
|
.encrypt_mpdu = NULL,
|
|
.decrypt_mpdu = NULL,
|
|
.encrypt_msdu = NULL,
|
|
.decrypt_msdu = NULL,
|
|
.set_key = NULL,
|
|
.get_key = NULL,
|
|
.extra_mpdu_prefix_len = 0,
|
|
.extra_mpdu_postfix_len = 0,
|
|
.owner = THIS_MODULE,
|
|
};
|
|
|
|
static int __init ieee80211_crypto_init(void)
|
|
{
|
|
int ret = -ENOMEM;
|
|
|
|
hcrypt = kmalloc(sizeof(*hcrypt), GFP_KERNEL);
|
|
if (!hcrypt)
|
|
goto out;
|
|
|
|
memset(hcrypt, 0, sizeof(*hcrypt));
|
|
INIT_LIST_HEAD(&hcrypt->algs);
|
|
spin_lock_init(&hcrypt->lock);
|
|
|
|
ret = ieee80211_register_crypto_ops(&ieee80211_crypt_null);
|
|
if (ret < 0) {
|
|
kfree(hcrypt);
|
|
hcrypt = NULL;
|
|
}
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static void __exit ieee80211_crypto_deinit(void)
|
|
{
|
|
struct list_head *ptr, *n;
|
|
|
|
if (hcrypt == NULL)
|
|
return;
|
|
|
|
for (ptr = hcrypt->algs.next, n = ptr->next; ptr != &hcrypt->algs;
|
|
ptr = n, n = ptr->next) {
|
|
struct ieee80211_crypto_alg *alg =
|
|
(struct ieee80211_crypto_alg *)ptr;
|
|
list_del(ptr);
|
|
printk(KERN_DEBUG "ieee80211_crypt: unregistered algorithm "
|
|
"'%s' (deinit)\n", alg->ops->name);
|
|
kfree(alg);
|
|
}
|
|
|
|
kfree(hcrypt);
|
|
}
|
|
|
|
EXPORT_SYMBOL(ieee80211_crypt_deinit_entries);
|
|
EXPORT_SYMBOL(ieee80211_crypt_deinit_handler);
|
|
EXPORT_SYMBOL(ieee80211_crypt_delayed_deinit);
|
|
EXPORT_SYMBOL(ieee80211_crypt_quiescing);
|
|
|
|
EXPORT_SYMBOL(ieee80211_register_crypto_ops);
|
|
EXPORT_SYMBOL(ieee80211_unregister_crypto_ops);
|
|
EXPORT_SYMBOL(ieee80211_get_crypto_ops);
|
|
|
|
module_init(ieee80211_crypto_init);
|
|
module_exit(ieee80211_crypto_deinit);
|