Blame view

net/wireless/lib80211.c 6.56 KB
09c434b8a   Thomas Gleixner   treewide: Add SPD...
1
  // SPDX-License-Identifier: GPL-2.0-only
7e272fcff   John W. Linville   wireless: consoli...
2
3
4
5
6
  /*
   * lib80211 -- common bits for IEEE802.11 drivers
   *
   * Copyright(c) 2008 John W. Linville <linville@tuxdriver.com>
   *
274bfb8dc   John W. Linville   lib80211: absorb ...
7
8
9
10
11
12
13
14
   * Portions copied from old ieee80211 component, w/ original copyright
   * notices below:
   *
   * Host AP crypto routines
   *
   * Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi>
   * Portions Copyright (C) 2004, Intel Corporation <jketreno@linux.intel.com>
   *
7e272fcff   John W. Linville   wireless: consoli...
15
   */
e9c0268f0   Joe Perches   net/wireless: Use...
16
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
7e272fcff   John W. Linville   wireless: consoli...
17
  #include <linux/module.h>
2819f8ad6   John W. Linville   wireless: escape_...
18
  #include <linux/ctype.h>
7e272fcff   John W. Linville   wireless: consoli...
19
  #include <linux/ieee80211.h>
274bfb8dc   John W. Linville   lib80211: absorb ...
20
21
22
23
  #include <linux/errno.h>
  #include <linux/init.h>
  #include <linux/slab.h>
  #include <linux/string.h>
7e272fcff   John W. Linville   wireless: consoli...
24
25
  
  #include <net/lib80211.h>
7e272fcff   John W. Linville   wireless: consoli...
26
27
28
29
30
  #define DRV_DESCRIPTION	"common routines for IEEE802.11 drivers"
  
  MODULE_DESCRIPTION(DRV_DESCRIPTION);
  MODULE_AUTHOR("John W. Linville <linville@tuxdriver.com>");
  MODULE_LICENSE("GPL");
274bfb8dc   John W. Linville   lib80211: absorb ...
31
32
33
34
35
36
37
  struct lib80211_crypto_alg {
  	struct list_head list;
  	struct lib80211_crypto_ops *ops;
  };
  
  static LIST_HEAD(lib80211_crypto_algs);
  static DEFINE_SPINLOCK(lib80211_crypto_lock);
9d630c779   Pavel Roskin   lib80211: remove ...
38
39
40
  static void lib80211_crypt_deinit_entries(struct lib80211_crypt_info *info,
  					  int force);
  static void lib80211_crypt_quiescing(struct lib80211_crypt_info *info);
e99e88a9d   Kees Cook   treewide: setup_t...
41
  static void lib80211_crypt_deinit_handler(struct timer_list *t);
9d630c779   Pavel Roskin   lib80211: remove ...
42

2ba4b32ec   John W. Linville   lib80211: consoli...
43
44
45
46
47
48
49
50
51
  int lib80211_crypt_info_init(struct lib80211_crypt_info *info, char *name,
  				spinlock_t *lock)
  {
  	memset(info, 0, sizeof(*info));
  
  	info->name = name;
  	info->lock = lock;
  
  	INIT_LIST_HEAD(&info->crypt_deinit_list);
e99e88a9d   Kees Cook   treewide: setup_t...
52
53
  	timer_setup(&info->crypt_deinit_timer, lib80211_crypt_deinit_handler,
  		    0);
2ba4b32ec   John W. Linville   lib80211: consoli...
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
  
  	return 0;
  }
  EXPORT_SYMBOL(lib80211_crypt_info_init);
  
  void lib80211_crypt_info_free(struct lib80211_crypt_info *info)
  {
  	int i;
  
          lib80211_crypt_quiescing(info);
          del_timer_sync(&info->crypt_deinit_timer);
          lib80211_crypt_deinit_entries(info, 1);
  
          for (i = 0; i < NUM_WEP_KEYS; i++) {
                  struct lib80211_crypt_data *crypt = info->crypt[i];
                  if (crypt) {
                          if (crypt->ops) {
                                  crypt->ops->deinit(crypt->priv);
                                  module_put(crypt->ops->owner);
                          }
                          kfree(crypt);
                          info->crypt[i] = NULL;
                  }
          }
  }
  EXPORT_SYMBOL(lib80211_crypt_info_free);
9d630c779   Pavel Roskin   lib80211: remove ...
80
81
  static void lib80211_crypt_deinit_entries(struct lib80211_crypt_info *info,
  					  int force)
7e272fcff   John W. Linville   wireless: consoli...
82
  {
274bfb8dc   John W. Linville   lib80211: absorb ...
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
  	struct lib80211_crypt_data *entry, *next;
  	unsigned long flags;
  
  	spin_lock_irqsave(info->lock, flags);
  	list_for_each_entry_safe(entry, next, &info->crypt_deinit_list, list) {
  		if (atomic_read(&entry->refcnt) != 0 && !force)
  			continue;
  
  		list_del(&entry->list);
  
  		if (entry->ops) {
  			entry->ops->deinit(entry->priv);
  			module_put(entry->ops->owner);
  		}
  		kfree(entry);
  	}
  	spin_unlock_irqrestore(info->lock, flags);
  }
274bfb8dc   John W. Linville   lib80211: absorb ...
101
102
  
  /* After this, crypt_deinit_list won't accept new members */
9d630c779   Pavel Roskin   lib80211: remove ...
103
  static void lib80211_crypt_quiescing(struct lib80211_crypt_info *info)
274bfb8dc   John W. Linville   lib80211: absorb ...
104
105
106
107
108
109
110
  {
  	unsigned long flags;
  
  	spin_lock_irqsave(info->lock, flags);
  	info->crypt_quiesced = 1;
  	spin_unlock_irqrestore(info->lock, flags);
  }
274bfb8dc   John W. Linville   lib80211: absorb ...
111

e99e88a9d   Kees Cook   treewide: setup_t...
112
  static void lib80211_crypt_deinit_handler(struct timer_list *t)
274bfb8dc   John W. Linville   lib80211: absorb ...
113
  {
e99e88a9d   Kees Cook   treewide: setup_t...
114
115
  	struct lib80211_crypt_info *info = from_timer(info, t,
  						      crypt_deinit_timer);
274bfb8dc   John W. Linville   lib80211: absorb ...
116
117
118
119
120
121
122
123
124
125
126
127
128
129
  	unsigned long flags;
  
  	lib80211_crypt_deinit_entries(info, 0);
  
  	spin_lock_irqsave(info->lock, flags);
  	if (!list_empty(&info->crypt_deinit_list) && !info->crypt_quiesced) {
  		printk(KERN_DEBUG "%s: entries remaining in delayed crypt "
  		       "deletion list
  ", info->name);
  		info->crypt_deinit_timer.expires = jiffies + HZ;
  		add_timer(&info->crypt_deinit_timer);
  	}
  	spin_unlock_irqrestore(info->lock, flags);
  }
274bfb8dc   John W. Linville   lib80211: absorb ...
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
  
  void lib80211_crypt_delayed_deinit(struct lib80211_crypt_info *info,
  				    struct lib80211_crypt_data **crypt)
  {
  	struct lib80211_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(info->lock, flags);
  	if (!info->crypt_quiesced) {
  		list_add(&tmp->list, &info->crypt_deinit_list);
  		if (!timer_pending(&info->crypt_deinit_timer)) {
  			info->crypt_deinit_timer.expires = jiffies + HZ;
  			add_timer(&info->crypt_deinit_timer);
  		}
  	}
  	spin_unlock_irqrestore(info->lock, flags);
  }
  EXPORT_SYMBOL(lib80211_crypt_delayed_deinit);
  
  int lib80211_register_crypto_ops(struct lib80211_crypto_ops *ops)
  {
  	unsigned long flags;
  	struct lib80211_crypto_alg *alg;
  
  	alg = kzalloc(sizeof(*alg), GFP_KERNEL);
  	if (alg == NULL)
  		return -ENOMEM;
  
  	alg->ops = ops;
  
  	spin_lock_irqsave(&lib80211_crypto_lock, flags);
  	list_add(&alg->list, &lib80211_crypto_algs);
  	spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
  
  	printk(KERN_DEBUG "lib80211_crypt: registered algorithm '%s'
  ",
  	       ops->name);
  
  	return 0;
  }
  EXPORT_SYMBOL(lib80211_register_crypto_ops);
  
  int lib80211_unregister_crypto_ops(struct lib80211_crypto_ops *ops)
  {
  	struct lib80211_crypto_alg *alg;
  	unsigned long flags;
  
  	spin_lock_irqsave(&lib80211_crypto_lock, flags);
  	list_for_each_entry(alg, &lib80211_crypto_algs, list) {
  		if (alg->ops == ops)
  			goto found;
  	}
  	spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
  	return -EINVAL;
  
        found:
e9c0268f0   Joe Perches   net/wireless: Use...
196
197
198
  	printk(KERN_DEBUG "lib80211_crypt: unregistered algorithm '%s'
  ",
  	       ops->name);
274bfb8dc   John W. Linville   lib80211: absorb ...
199
200
201
  	list_del(&alg->list);
  	spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
  	kfree(alg);
7e272fcff   John W. Linville   wireless: consoli...
202
203
  	return 0;
  }
274bfb8dc   John W. Linville   lib80211: absorb ...
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
  EXPORT_SYMBOL(lib80211_unregister_crypto_ops);
  
  struct lib80211_crypto_ops *lib80211_get_crypto_ops(const char *name)
  {
  	struct lib80211_crypto_alg *alg;
  	unsigned long flags;
  
  	spin_lock_irqsave(&lib80211_crypto_lock, flags);
  	list_for_each_entry(alg, &lib80211_crypto_algs, list) {
  		if (strcmp(alg->ops->name, name) == 0)
  			goto found;
  	}
  	spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
  	return NULL;
  
        found:
  	spin_unlock_irqrestore(&lib80211_crypto_lock, flags);
  	return alg->ops;
  }
  EXPORT_SYMBOL(lib80211_get_crypto_ops);
  
  static void *lib80211_crypt_null_init(int keyidx)
  {
  	return (void *)1;
  }
  
  static void lib80211_crypt_null_deinit(void *priv)
  {
  }
  
  static struct lib80211_crypto_ops lib80211_crypt_null = {
  	.name = "NULL",
  	.init = lib80211_crypt_null_init,
  	.deinit = lib80211_crypt_null_deinit,
  	.owner = THIS_MODULE,
  };
  
  static int __init lib80211_init(void)
  {
e9c0268f0   Joe Perches   net/wireless: Use...
243
244
  	pr_info(DRV_DESCRIPTION "
  ");
274bfb8dc   John W. Linville   lib80211: absorb ...
245
246
  	return lib80211_register_crypto_ops(&lib80211_crypt_null);
  }
7e272fcff   John W. Linville   wireless: consoli...
247

274bfb8dc   John W. Linville   lib80211: absorb ...
248
  static void __exit lib80211_exit(void)
7e272fcff   John W. Linville   wireless: consoli...
249
  {
274bfb8dc   John W. Linville   lib80211: absorb ...
250
251
  	lib80211_unregister_crypto_ops(&lib80211_crypt_null);
  	BUG_ON(!list_empty(&lib80211_crypto_algs));
7e272fcff   John W. Linville   wireless: consoli...
252
  }
274bfb8dc   John W. Linville   lib80211: absorb ...
253
254
  module_init(lib80211_init);
  module_exit(lib80211_exit);