| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 1 | /* | 
|  | 2 | * Host AP crypto routines | 
|  | 3 | * | 
|  | 4 | * Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi> | 
|  | 5 | * Portions Copyright (C) 2004, Intel Corporation <jketreno@linux.intel.com> | 
|  | 6 | * | 
|  | 7 | * This program is free software; you can redistribute it and/or modify | 
|  | 8 | * it under the terms of the GNU General Public License version 2 as | 
|  | 9 | * published by the Free Software Foundation. See README and COPYING for | 
|  | 10 | * more details. | 
|  | 11 | * | 
|  | 12 | */ | 
|  | 13 |  | 
| Christoph Hellwig | e330562 | 2005-11-09 01:01:04 -0500 | [diff] [blame] | 14 | #include <linux/errno.h> | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 15 | #include <linux/module.h> | 
|  | 16 | #include <linux/init.h> | 
|  | 17 | #include <linux/slab.h> | 
| Christoph Hellwig | e330562 | 2005-11-09 01:01:04 -0500 | [diff] [blame] | 18 | #include <linux/string.h> | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 19 | #include <net/ieee80211.h> | 
|  | 20 |  | 
|  | 21 | MODULE_AUTHOR("Jouni Malinen"); | 
|  | 22 | MODULE_DESCRIPTION("HostAP crypto"); | 
|  | 23 | MODULE_LICENSE("GPL"); | 
|  | 24 |  | 
|  | 25 | struct ieee80211_crypto_alg { | 
|  | 26 | struct list_head list; | 
|  | 27 | struct ieee80211_crypto_ops *ops; | 
|  | 28 | }; | 
|  | 29 |  | 
| Christoph Hellwig | e330562 | 2005-11-09 01:01:04 -0500 | [diff] [blame] | 30 | static LIST_HEAD(ieee80211_crypto_algs); | 
|  | 31 | static DEFINE_SPINLOCK(ieee80211_crypto_lock); | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 32 |  | 
| Jeff Garzik | 0edd5b4 | 2005-09-07 00:48:31 -0400 | [diff] [blame] | 33 | void ieee80211_crypt_deinit_entries(struct ieee80211_device *ieee, int force) | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 34 | { | 
| Zhu Yi | d652923 | 2006-01-19 16:20:49 +0800 | [diff] [blame] | 35 | struct ieee80211_crypt_data *entry, *next; | 
| James Ketrenos | 20d6471 | 2005-09-21 11:53:43 -0500 | [diff] [blame] | 36 | unsigned long flags; | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 37 |  | 
| James Ketrenos | 20d6471 | 2005-09-21 11:53:43 -0500 | [diff] [blame] | 38 | spin_lock_irqsave(&ieee->lock, flags); | 
| Zhu Yi | d652923 | 2006-01-19 16:20:49 +0800 | [diff] [blame] | 39 | list_for_each_entry_safe(entry, next, &ieee->crypt_deinit_list, list) { | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 40 | if (atomic_read(&entry->refcnt) != 0 && !force) | 
|  | 41 | continue; | 
|  | 42 |  | 
| Christoph Hellwig | e330562 | 2005-11-09 01:01:04 -0500 | [diff] [blame] | 43 | list_del(&entry->list); | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 44 |  | 
|  | 45 | if (entry->ops) { | 
|  | 46 | entry->ops->deinit(entry->priv); | 
|  | 47 | module_put(entry->ops->owner); | 
|  | 48 | } | 
|  | 49 | kfree(entry); | 
|  | 50 | } | 
| James Ketrenos | 0ad0c3c | 2005-09-21 11:54:15 -0500 | [diff] [blame] | 51 | spin_unlock_irqrestore(&ieee->lock, flags); | 
|  | 52 | } | 
|  | 53 |  | 
|  | 54 | /* After this, crypt_deinit_list won't accept new members */ | 
|  | 55 | void ieee80211_crypt_quiescing(struct ieee80211_device *ieee) | 
|  | 56 | { | 
|  | 57 | unsigned long flags; | 
|  | 58 |  | 
|  | 59 | spin_lock_irqsave(&ieee->lock, flags); | 
|  | 60 | ieee->crypt_quiesced = 1; | 
| James Ketrenos | 20d6471 | 2005-09-21 11:53:43 -0500 | [diff] [blame] | 61 | spin_unlock_irqrestore(&ieee->lock, flags); | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 62 | } | 
|  | 63 |  | 
|  | 64 | void ieee80211_crypt_deinit_handler(unsigned long data) | 
|  | 65 | { | 
|  | 66 | struct ieee80211_device *ieee = (struct ieee80211_device *)data; | 
| James Ketrenos | 0ad0c3c | 2005-09-21 11:54:15 -0500 | [diff] [blame] | 67 | unsigned long flags; | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 68 |  | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 69 | ieee80211_crypt_deinit_entries(ieee, 0); | 
| James Ketrenos | 0ad0c3c | 2005-09-21 11:54:15 -0500 | [diff] [blame] | 70 |  | 
|  | 71 | spin_lock_irqsave(&ieee->lock, flags); | 
|  | 72 | if (!list_empty(&ieee->crypt_deinit_list) && !ieee->crypt_quiesced) { | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 73 | printk(KERN_DEBUG "%s: entries remaining in delayed crypt " | 
|  | 74 | "deletion list\n", ieee->dev->name); | 
|  | 75 | ieee->crypt_deinit_timer.expires = jiffies + HZ; | 
|  | 76 | add_timer(&ieee->crypt_deinit_timer); | 
|  | 77 | } | 
| James Ketrenos | 0ad0c3c | 2005-09-21 11:54:15 -0500 | [diff] [blame] | 78 | spin_unlock_irqrestore(&ieee->lock, flags); | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 79 | } | 
|  | 80 |  | 
|  | 81 | void ieee80211_crypt_delayed_deinit(struct ieee80211_device *ieee, | 
|  | 82 | struct ieee80211_crypt_data **crypt) | 
|  | 83 | { | 
|  | 84 | struct ieee80211_crypt_data *tmp; | 
|  | 85 | unsigned long flags; | 
|  | 86 |  | 
|  | 87 | if (*crypt == NULL) | 
|  | 88 | return; | 
|  | 89 |  | 
|  | 90 | tmp = *crypt; | 
|  | 91 | *crypt = NULL; | 
|  | 92 |  | 
|  | 93 | /* must not run ops->deinit() while there may be pending encrypt or | 
|  | 94 | * decrypt operations. Use a list of delayed deinits to avoid needing | 
|  | 95 | * locking. */ | 
|  | 96 |  | 
|  | 97 | spin_lock_irqsave(&ieee->lock, flags); | 
| James Ketrenos | 0ad0c3c | 2005-09-21 11:54:15 -0500 | [diff] [blame] | 98 | if (!ieee->crypt_quiesced) { | 
|  | 99 | list_add(&tmp->list, &ieee->crypt_deinit_list); | 
|  | 100 | if (!timer_pending(&ieee->crypt_deinit_timer)) { | 
|  | 101 | ieee->crypt_deinit_timer.expires = jiffies + HZ; | 
|  | 102 | add_timer(&ieee->crypt_deinit_timer); | 
|  | 103 | } | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 104 | } | 
|  | 105 | spin_unlock_irqrestore(&ieee->lock, flags); | 
|  | 106 | } | 
|  | 107 |  | 
|  | 108 | int ieee80211_register_crypto_ops(struct ieee80211_crypto_ops *ops) | 
|  | 109 | { | 
|  | 110 | unsigned long flags; | 
|  | 111 | struct ieee80211_crypto_alg *alg; | 
|  | 112 |  | 
| Panagiotis Issaris | 0da974f | 2006-07-21 14:51:30 -0700 | [diff] [blame] | 113 | alg = kzalloc(sizeof(*alg), GFP_KERNEL); | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 114 | if (alg == NULL) | 
|  | 115 | return -ENOMEM; | 
|  | 116 |  | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 117 | alg->ops = ops; | 
|  | 118 |  | 
| Christoph Hellwig | e330562 | 2005-11-09 01:01:04 -0500 | [diff] [blame] | 119 | spin_lock_irqsave(&ieee80211_crypto_lock, flags); | 
|  | 120 | list_add(&alg->list, &ieee80211_crypto_algs); | 
|  | 121 | spin_unlock_irqrestore(&ieee80211_crypto_lock, flags); | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 122 |  | 
|  | 123 | printk(KERN_DEBUG "ieee80211_crypt: registered algorithm '%s'\n", | 
|  | 124 | ops->name); | 
|  | 125 |  | 
|  | 126 | return 0; | 
|  | 127 | } | 
|  | 128 |  | 
|  | 129 | int ieee80211_unregister_crypto_ops(struct ieee80211_crypto_ops *ops) | 
|  | 130 | { | 
| Christoph Hellwig | e330562 | 2005-11-09 01:01:04 -0500 | [diff] [blame] | 131 | struct ieee80211_crypto_alg *alg; | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 132 | unsigned long flags; | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 133 |  | 
| Christoph Hellwig | e330562 | 2005-11-09 01:01:04 -0500 | [diff] [blame] | 134 | spin_lock_irqsave(&ieee80211_crypto_lock, flags); | 
|  | 135 | list_for_each_entry(alg, &ieee80211_crypto_algs, list) { | 
|  | 136 | if (alg->ops == ops) | 
|  | 137 | goto found; | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 138 | } | 
| Christoph Hellwig | e330562 | 2005-11-09 01:01:04 -0500 | [diff] [blame] | 139 | spin_unlock_irqrestore(&ieee80211_crypto_lock, flags); | 
|  | 140 | return -EINVAL; | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 141 |  | 
| Zhu Yi | d652923 | 2006-01-19 16:20:49 +0800 | [diff] [blame] | 142 | found: | 
| Christoph Hellwig | e330562 | 2005-11-09 01:01:04 -0500 | [diff] [blame] | 143 | printk(KERN_DEBUG "ieee80211_crypt: unregistered algorithm " | 
| Zhu Yi | d652923 | 2006-01-19 16:20:49 +0800 | [diff] [blame] | 144 | "'%s'\n", ops->name); | 
| Christoph Hellwig | e330562 | 2005-11-09 01:01:04 -0500 | [diff] [blame] | 145 | list_del(&alg->list); | 
|  | 146 | spin_unlock_irqrestore(&ieee80211_crypto_lock, flags); | 
|  | 147 | kfree(alg); | 
|  | 148 | return 0; | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 149 | } | 
|  | 150 |  | 
| Jeff Garzik | 0edd5b4 | 2005-09-07 00:48:31 -0400 | [diff] [blame] | 151 | struct ieee80211_crypto_ops *ieee80211_get_crypto_ops(const char *name) | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 152 | { | 
| Christoph Hellwig | e330562 | 2005-11-09 01:01:04 -0500 | [diff] [blame] | 153 | struct ieee80211_crypto_alg *alg; | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 154 | unsigned long flags; | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 155 |  | 
| Christoph Hellwig | e330562 | 2005-11-09 01:01:04 -0500 | [diff] [blame] | 156 | spin_lock_irqsave(&ieee80211_crypto_lock, flags); | 
|  | 157 | list_for_each_entry(alg, &ieee80211_crypto_algs, list) { | 
|  | 158 | if (strcmp(alg->ops->name, name) == 0) | 
|  | 159 | goto found; | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 160 | } | 
| Christoph Hellwig | e330562 | 2005-11-09 01:01:04 -0500 | [diff] [blame] | 161 | spin_unlock_irqrestore(&ieee80211_crypto_lock, flags); | 
|  | 162 | return NULL; | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 163 |  | 
| Zhu Yi | d652923 | 2006-01-19 16:20:49 +0800 | [diff] [blame] | 164 | found: | 
| Christoph Hellwig | e330562 | 2005-11-09 01:01:04 -0500 | [diff] [blame] | 165 | spin_unlock_irqrestore(&ieee80211_crypto_lock, flags); | 
|  | 166 | return alg->ops; | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 167 | } | 
|  | 168 |  | 
| James Ketrenos | 6eb6edf | 2005-09-22 10:34:15 +0000 | [diff] [blame] | 169 | static void *ieee80211_crypt_null_init(int keyidx) | 
| Jeff Garzik | 0edd5b4 | 2005-09-07 00:48:31 -0400 | [diff] [blame] | 170 | { | 
|  | 171 | return (void *)1; | 
|  | 172 | } | 
| Christoph Hellwig | e330562 | 2005-11-09 01:01:04 -0500 | [diff] [blame] | 173 |  | 
| Jeff Garzik | 0edd5b4 | 2005-09-07 00:48:31 -0400 | [diff] [blame] | 174 | static void ieee80211_crypt_null_deinit(void *priv) | 
|  | 175 | { | 
|  | 176 | } | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 177 |  | 
|  | 178 | static struct ieee80211_crypto_ops ieee80211_crypt_null = { | 
| James Ketrenos | 74079fd | 2005-09-13 17:35:21 -0500 | [diff] [blame] | 179 | .name = "NULL", | 
|  | 180 | .init = ieee80211_crypt_null_init, | 
|  | 181 | .deinit = ieee80211_crypt_null_deinit, | 
| James Ketrenos | 74079fd | 2005-09-13 17:35:21 -0500 | [diff] [blame] | 182 | .owner = THIS_MODULE, | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 183 | }; | 
|  | 184 |  | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 185 | static int __init ieee80211_crypto_init(void) | 
|  | 186 | { | 
| Christoph Hellwig | e330562 | 2005-11-09 01:01:04 -0500 | [diff] [blame] | 187 | return ieee80211_register_crypto_ops(&ieee80211_crypt_null); | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 188 | } | 
|  | 189 |  | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 190 | static void __exit ieee80211_crypto_deinit(void) | 
|  | 191 | { | 
| Christoph Hellwig | e330562 | 2005-11-09 01:01:04 -0500 | [diff] [blame] | 192 | ieee80211_unregister_crypto_ops(&ieee80211_crypt_null); | 
|  | 193 | BUG_ON(!list_empty(&ieee80211_crypto_algs)); | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 194 | } | 
|  | 195 |  | 
|  | 196 | EXPORT_SYMBOL(ieee80211_crypt_deinit_entries); | 
|  | 197 | EXPORT_SYMBOL(ieee80211_crypt_deinit_handler); | 
|  | 198 | EXPORT_SYMBOL(ieee80211_crypt_delayed_deinit); | 
| James Ketrenos | 0ad0c3c | 2005-09-21 11:54:15 -0500 | [diff] [blame] | 199 | EXPORT_SYMBOL(ieee80211_crypt_quiescing); | 
| Jeff Garzik | b453872 | 2005-05-12 22:48:20 -0400 | [diff] [blame] | 200 |  | 
|  | 201 | EXPORT_SYMBOL(ieee80211_register_crypto_ops); | 
|  | 202 | EXPORT_SYMBOL(ieee80211_unregister_crypto_ops); | 
|  | 203 | EXPORT_SYMBOL(ieee80211_get_crypto_ops); | 
|  | 204 |  | 
|  | 205 | module_init(ieee80211_crypto_init); | 
|  | 206 | module_exit(ieee80211_crypto_deinit); |