Merge branch 'master'
[linux-2.6] / net / ieee80211 / ieee80211_crypt.c
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
14 #include <linux/errno.h>
15 #include <linux/module.h>
16 #include <linux/init.h>
17 #include <linux/slab.h>
18 #include <linux/string.h>
19 #include <net/ieee80211.h>
20
21
22 MODULE_AUTHOR("Jouni Malinen");
23 MODULE_DESCRIPTION("HostAP crypto");
24 MODULE_LICENSE("GPL");
25
26 struct ieee80211_crypto_alg {
27         struct list_head list;
28         struct ieee80211_crypto_ops *ops;
29 };
30
31 static LIST_HEAD(ieee80211_crypto_algs);
32 static DEFINE_SPINLOCK(ieee80211_crypto_lock);
33
34 void ieee80211_crypt_deinit_entries(struct ieee80211_device *ieee, int force)
35 {
36         struct ieee80211_crypt_data *entry, *next;
37         unsigned long flags;
38
39         spin_lock_irqsave(&ieee->lock, flags);
40         list_for_each_entry_safe(entry, next, &ieee->crypt_deinit_list, list) {
41                 if (atomic_read(&entry->refcnt) != 0 && !force)
42                         continue;
43
44                 list_del(&entry->list);
45
46                 if (entry->ops) {
47                         entry->ops->deinit(entry->priv);
48                         module_put(entry->ops->owner);
49                 }
50                 kfree(entry);
51         }
52         spin_unlock_irqrestore(&ieee->lock, flags);
53 }
54
55 /* After this, crypt_deinit_list won't accept new members */
56 void ieee80211_crypt_quiescing(struct ieee80211_device *ieee)
57 {
58         unsigned long flags;
59
60         spin_lock_irqsave(&ieee->lock, flags);
61         ieee->crypt_quiesced = 1;
62         spin_unlock_irqrestore(&ieee->lock, flags);
63 }
64
65 void ieee80211_crypt_deinit_handler(unsigned long data)
66 {
67         struct ieee80211_device *ieee = (struct ieee80211_device *)data;
68         unsigned long flags;
69
70         ieee80211_crypt_deinit_entries(ieee, 0);
71
72         spin_lock_irqsave(&ieee->lock, flags);
73         if (!list_empty(&ieee->crypt_deinit_list) && !ieee->crypt_quiesced) {
74                 printk(KERN_DEBUG "%s: entries remaining in delayed crypt "
75                        "deletion list\n", ieee->dev->name);
76                 ieee->crypt_deinit_timer.expires = jiffies + HZ;
77                 add_timer(&ieee->crypt_deinit_timer);
78         }
79         spin_unlock_irqrestore(&ieee->lock, flags);
80 }
81
82 void ieee80211_crypt_delayed_deinit(struct ieee80211_device *ieee,
83                                     struct ieee80211_crypt_data **crypt)
84 {
85         struct ieee80211_crypt_data *tmp;
86         unsigned long flags;
87
88         if (*crypt == NULL)
89                 return;
90
91         tmp = *crypt;
92         *crypt = NULL;
93
94         /* must not run ops->deinit() while there may be pending encrypt or
95          * decrypt operations. Use a list of delayed deinits to avoid needing
96          * locking. */
97
98         spin_lock_irqsave(&ieee->lock, flags);
99         if (!ieee->crypt_quiesced) {
100                 list_add(&tmp->list, &ieee->crypt_deinit_list);
101                 if (!timer_pending(&ieee->crypt_deinit_timer)) {
102                         ieee->crypt_deinit_timer.expires = jiffies + HZ;
103                         add_timer(&ieee->crypt_deinit_timer);
104                 }
105         }
106         spin_unlock_irqrestore(&ieee->lock, flags);
107 }
108
109 int ieee80211_register_crypto_ops(struct ieee80211_crypto_ops *ops)
110 {
111         unsigned long flags;
112         struct ieee80211_crypto_alg *alg;
113
114         alg = kmalloc(sizeof(*alg), GFP_KERNEL);
115         if (alg == NULL)
116                 return -ENOMEM;
117
118         memset(alg, 0, sizeof(*alg));
119         alg->ops = ops;
120
121         spin_lock_irqsave(&ieee80211_crypto_lock, flags);
122         list_add(&alg->list, &ieee80211_crypto_algs);
123         spin_unlock_irqrestore(&ieee80211_crypto_lock, flags);
124
125         printk(KERN_DEBUG "ieee80211_crypt: registered algorithm '%s'\n",
126                ops->name);
127
128         return 0;
129 }
130
131 int ieee80211_unregister_crypto_ops(struct ieee80211_crypto_ops *ops)
132 {
133         struct ieee80211_crypto_alg *alg;
134         unsigned long flags;
135
136         spin_lock_irqsave(&ieee80211_crypto_lock, flags);
137         list_for_each_entry(alg, &ieee80211_crypto_algs, list) {
138                 if (alg->ops == ops)
139                         goto found;
140         }
141         spin_unlock_irqrestore(&ieee80211_crypto_lock, flags);
142         return -EINVAL;
143
144  found:
145         printk(KERN_DEBUG "ieee80211_crypt: unregistered algorithm "
146                           "'%s'\n", ops->name);
147         list_del(&alg->list);
148         spin_unlock_irqrestore(&ieee80211_crypto_lock, flags);
149         kfree(alg);
150         return 0;
151 }
152
153 struct ieee80211_crypto_ops *ieee80211_get_crypto_ops(const char *name)
154 {
155         struct ieee80211_crypto_alg *alg;
156         unsigned long flags;
157
158         spin_lock_irqsave(&ieee80211_crypto_lock, flags);
159         list_for_each_entry(alg, &ieee80211_crypto_algs, list) {
160                 if (strcmp(alg->ops->name, name) == 0)
161                         goto found;
162         }
163         spin_unlock_irqrestore(&ieee80211_crypto_lock, flags);
164         return NULL;
165
166  found:
167         spin_unlock_irqrestore(&ieee80211_crypto_lock, flags);
168         return alg->ops;
169 }
170
171 static void *ieee80211_crypt_null_init(int keyidx)
172 {
173         return (void *)1;
174 }
175
176 static void ieee80211_crypt_null_deinit(void *priv)
177 {
178 }
179
180 static struct ieee80211_crypto_ops ieee80211_crypt_null = {
181         .name = "NULL",
182         .init = ieee80211_crypt_null_init,
183         .deinit = ieee80211_crypt_null_deinit,
184         .owner = THIS_MODULE,
185 };
186
187 static int __init ieee80211_crypto_init(void)
188 {
189         return ieee80211_register_crypto_ops(&ieee80211_crypt_null);
190 }
191
192 static void __exit ieee80211_crypto_deinit(void)
193 {
194         ieee80211_unregister_crypto_ops(&ieee80211_crypt_null);
195         BUG_ON(!list_empty(&ieee80211_crypto_algs));
196 }
197
198 EXPORT_SYMBOL(ieee80211_crypt_deinit_entries);
199 EXPORT_SYMBOL(ieee80211_crypt_deinit_handler);
200 EXPORT_SYMBOL(ieee80211_crypt_delayed_deinit);
201 EXPORT_SYMBOL(ieee80211_crypt_quiescing);
202
203 EXPORT_SYMBOL(ieee80211_register_crypto_ops);
204 EXPORT_SYMBOL(ieee80211_unregister_crypto_ops);
205 EXPORT_SYMBOL(ieee80211_get_crypto_ops);
206
207 module_init(ieee80211_crypto_init);
208 module_exit(ieee80211_crypto_deinit);