aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/key.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2007-08-28 17:01:55 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2007-10-10 19:48:53 -0400
commit11a843b7e16062389c53ba393c7913956e034eb2 (patch)
tree7f557a55220a0de38f0eafe1a5147487ec39b790 /net/mac80211/key.c
parent3aefaa3294193c931b20a574f718efee6baf27d4 (diff)
[MAC80211]: rework key handling
This moves all the key handling code out from ieee80211_ioctl.c into key.c and also does the following changes including documentation updates in mac80211.h: 1) Turn off hardware acceleration for keys when the interface is down. This is necessary because otherwise monitor interfaces could be decrypting frames for other interfaces that are down at the moment. Also, it should go some way towards better suspend/resume support, in any case the routines used here could be used for that as well. Additionally, this makes the driver interface nicer, keys for a specific local MAC address are only ever present while an interface with that MAC address is enabled. 2) Change driver set_key() callback interface to allow only return values of -ENOSPC, -EOPNOTSUPP and 0, warn on all other return values. This allows debugging the stack when a driver notices it's handed a key while it is down. 3) Invert the flag meaning to KEY_FLAG_UPLOADED_TO_HARDWARE. 4) Remove REMOVE_ALL_KEYS command as it isn't used nor do we want to use it, we'll use DISABLE_KEY for each key. It is hard to use REMOVE_ALL_KEYS because we can handle multiple virtual interfaces with different key configuration, so we'd have to keep track of a lot of state for this and that isn't worth it. 5) Warn when disabling a key fails, it musn't. 6) Remove IEEE80211_HW_NO_TKIP_WMM_HWACCEL in favour of per-key IEEE80211_KEY_FLAG_WMM_STA to let driver sort it out itself. 7) Tell driver that a (non-WEP) key is used only for transmission by using an all-zeroes station MAC address when configuring. 8) Change the set_key() callback to have access to the local MAC address the key is being added for. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Acked-by: Michael Wu <flamingice@sourmilk.net> Signed-off-by: John W. Linville <linville@tuxdriver.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/mac80211/key.c')
-rw-r--r--net/mac80211/key.c243
1 files changed, 241 insertions, 2 deletions
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index 843d1577f00f..178f00cf61b9 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -2,25 +2,198 @@
2 * Copyright 2002-2005, Instant802 Networks, Inc. 2 * Copyright 2002-2005, Instant802 Networks, Inc.
3 * Copyright 2005-2006, Devicescape Software, Inc. 3 * Copyright 2005-2006, Devicescape Software, Inc.
4 * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> 4 * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
5 * Copyright 2007 Johannes Berg <johannes@sipsolutions.net>
5 * 6 *
6 * This program is free software; you can redistribute it and/or modify 7 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as 8 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation. 9 * published by the Free Software Foundation.
9 */ 10 */
10 11
12#include <linux/if_ether.h>
13#include <linux/etherdevice.h>
14#include <linux/list.h>
11#include <net/mac80211.h> 15#include <net/mac80211.h>
12#include "ieee80211_i.h" 16#include "ieee80211_i.h"
13#include "debugfs_key.h" 17#include "debugfs_key.h"
14#include "aes_ccm.h" 18#include "aes_ccm.h"
15 19
20
21/*
22 * Key handling basics
23 *
24 * Key handling in mac80211 is done based on per-interface (sub_if_data)
25 * keys and per-station keys. Since each station belongs to an interface,
26 * each station key also belongs to that interface.
27 *
28 * Hardware acceleration is done on a best-effort basis, for each key
29 * that is eligible the hardware is asked to enable that key but if
30 * it cannot do that they key is simply kept for software encryption.
31 * There is currently no way of knowing this except by looking into
32 * debugfs.
33 *
34 * All operations here are called under RTNL so no extra locking is
35 * required.
36 */
37
38static const u8 bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
39static const u8 zero_addr[ETH_ALEN];
40
41static const u8 *get_mac_for_key(struct ieee80211_key *key)
42{
43 const u8 *addr = bcast_addr;
44
45 /*
46 * If we're an AP we won't ever receive frames with a non-WEP
47 * group key so we tell the driver that by using the zero MAC
48 * address to indicate a transmit-only key.
49 */
50 if (key->conf.alg != ALG_WEP &&
51 (key->sdata->type == IEEE80211_IF_TYPE_AP ||
52 key->sdata->type == IEEE80211_IF_TYPE_VLAN))
53 addr = zero_addr;
54
55 if (key->sta)
56 addr = key->sta->addr;
57
58 return addr;
59}
60
61static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
62{
63 const u8 *addr;
64 int ret;
65
66 if (!key->local->ops->set_key)
67 return;
68
69 addr = get_mac_for_key(key);
70
71 ret = key->local->ops->set_key(local_to_hw(key->local), SET_KEY,
72 key->sdata->dev->dev_addr, addr,
73 &key->conf);
74
75 WARN_ON(!ret && (key->conf.hw_key_idx == HW_KEY_IDX_INVALID));
76
77 if (!ret)
78 key->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE;
79
80 if (ret && ret != -ENOSPC && ret != -EOPNOTSUPP)
81 printk(KERN_ERR "mac80211-%s: failed to set key "
82 "(%d, " MAC_FMT ") to hardware (%d)\n",
83 wiphy_name(key->local->hw.wiphy),
84 key->conf.keyidx, MAC_ARG(addr), ret);
85}
86
87static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)
88{
89 const u8 *addr;
90 int ret;
91
92 if (!key->local->ops->set_key)
93 return;
94
95 if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
96 return;
97
98 addr = get_mac_for_key(key);
99
100 ret = key->local->ops->set_key(local_to_hw(key->local), DISABLE_KEY,
101 key->sdata->dev->dev_addr, addr,
102 &key->conf);
103
104 if (ret)
105 printk(KERN_ERR "mac80211-%s: failed to remove key "
106 "(%d, " MAC_FMT ") from hardware (%d)\n",
107 wiphy_name(key->local->hw.wiphy),
108 key->conf.keyidx, MAC_ARG(addr), ret);
109
110 key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE;
111 key->conf.hw_key_idx = HW_KEY_IDX_INVALID;
112}
113
16struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata, 114struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata,
17 int idx, size_t key_len, gfp_t flags) 115 struct sta_info *sta,
116 ieee80211_key_alg alg,
117 int idx,
118 size_t key_len,
119 const u8 *key_data)
18{ 120{
19 struct ieee80211_key *key; 121 struct ieee80211_key *key;
20 122
21 key = kzalloc(sizeof(struct ieee80211_key) + key_len, flags); 123 BUG_ON(alg == ALG_NONE);
124
125 key = kzalloc(sizeof(struct ieee80211_key) + key_len, GFP_KERNEL);
22 if (!key) 126 if (!key)
23 return NULL; 127 return NULL;
128
129 /*
130 * Default to software encryption; we'll later upload the
131 * key to the hardware if possible.
132 */
133 key->conf.hw_key_idx = HW_KEY_IDX_INVALID;
134 key->conf.flags = 0;
135 key->flags = 0;
136
137 key->conf.alg = alg;
138 key->conf.keyidx = idx;
139 key->conf.keylen = key_len;
140 memcpy(key->conf.key, key_data, key_len);
141
142 key->local = sdata->local;
143 key->sdata = sdata;
144 key->sta = sta;
145
146 if (alg == ALG_CCMP) {
147 /*
148 * Initialize AES key state here as an optimization so that
149 * it does not need to be initialized for every packet.
150 */
151 key->u.ccmp.tfm = ieee80211_aes_key_setup_encrypt(key_data);
152 if (!key->u.ccmp.tfm) {
153 ieee80211_key_free(key);
154 return NULL;
155 }
156 }
157
158 ieee80211_debugfs_key_add(key->local, key);
159
160 if (sta) {
161 ieee80211_debugfs_key_sta_link(key, sta);
162 sta->key = key;
163 /*
164 * some hardware cannot handle TKIP with QoS, so
165 * we indicate whether QoS could be in use.
166 */
167 if (sta->flags & WLAN_STA_WME)
168 key->conf.flags |= IEEE80211_KEY_FLAG_WMM_STA;
169 } else {
170 if (sdata->type == IEEE80211_IF_TYPE_STA) {
171 struct sta_info *ap;
172
173 /* same here, the AP could be using QoS */
174 ap = sta_info_get(key->local, key->sdata->u.sta.bssid);
175 if (ap) {
176 if (ap->flags & WLAN_STA_WME)
177 key->conf.flags |=
178 IEEE80211_KEY_FLAG_WMM_STA;
179 sta_info_put(ap);
180 }
181 }
182
183 if (idx >= 0 && idx < NUM_DEFAULT_KEYS) {
184 if (!sdata->keys[idx])
185 sdata->keys[idx] = key;
186 else
187 WARN_ON(1);
188 } else
189 WARN_ON(1);
190 }
191
192 list_add(&key->list, &sdata->key_list);
193
194 if (netif_running(key->sdata->dev))
195 ieee80211_key_enable_hw_accel(key);
196
24 return key; 197 return key;
25} 198}
26 199
@@ -29,8 +202,74 @@ void ieee80211_key_free(struct ieee80211_key *key)
29 if (!key) 202 if (!key)
30 return; 203 return;
31 204
205 ieee80211_key_disable_hw_accel(key);
206
207 if (key->sta) {
208 key->sta->key = NULL;
209 } else {
210 if (key->sdata->default_key == key)
211 ieee80211_set_default_key(key->sdata, -1);
212 if (key->conf.keyidx >= 0 &&
213 key->conf.keyidx < NUM_DEFAULT_KEYS)
214 key->sdata->keys[key->conf.keyidx] = NULL;
215 else
216 WARN_ON(1);
217 }
218
32 if (key->conf.alg == ALG_CCMP) 219 if (key->conf.alg == ALG_CCMP)
33 ieee80211_aes_key_free(key->u.ccmp.tfm); 220 ieee80211_aes_key_free(key->u.ccmp.tfm);
34 ieee80211_debugfs_key_remove(key); 221 ieee80211_debugfs_key_remove(key);
222
223 list_del(&key->list);
224
35 kfree(key); 225 kfree(key);
36} 226}
227
228void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx)
229{
230 struct ieee80211_key *key = NULL;
231
232 if (idx >= 0 && idx < NUM_DEFAULT_KEYS)
233 key = sdata->keys[idx];
234
235 if (sdata->default_key != key) {
236 ieee80211_debugfs_key_remove_default(sdata);
237
238 sdata->default_key = key;
239
240 if (sdata->default_key)
241 ieee80211_debugfs_key_add_default(sdata);
242
243 if (sdata->local->ops->set_key_idx)
244 sdata->local->ops->set_key_idx(
245 local_to_hw(sdata->local), idx);
246 }
247}
248
249void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata)
250{
251 struct ieee80211_key *key, *tmp;
252
253 list_for_each_entry_safe(key, tmp, &sdata->key_list, list)
254 ieee80211_key_free(key);
255}
256
257void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata)
258{
259 struct ieee80211_key *key;
260
261 WARN_ON(!netif_running(sdata->dev));
262 if (!netif_running(sdata->dev))
263 return;
264
265 list_for_each_entry(key, &sdata->key_list, list)
266 ieee80211_key_enable_hw_accel(key);
267}
268
269void ieee80211_disable_keys(struct ieee80211_sub_if_data *sdata)
270{
271 struct ieee80211_key *key;
272
273 list_for_each_entry(key, &sdata->key_list, list)
274 ieee80211_key_disable_hw_accel(key);
275}