diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2007-08-28 17:01:55 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-10-10 19:48:53 -0400 |
commit | 11a843b7e16062389c53ba393c7913956e034eb2 (patch) | |
tree | 7f557a55220a0de38f0eafe1a5147487ec39b790 /net/mac80211/ieee80211_ioctl.c | |
parent | 3aefaa3294193c931b20a574f718efee6baf27d4 (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/ieee80211_ioctl.c')
-rw-r--r-- | net/mac80211/ieee80211_ioctl.c | 177 |
1 files changed, 14 insertions, 163 deletions
diff --git a/net/mac80211/ieee80211_ioctl.c b/net/mac80211/ieee80211_ioctl.c index dc05bc66fbb8..8296e7de12c7 100644 --- a/net/mac80211/ieee80211_ioctl.c +++ b/net/mac80211/ieee80211_ioctl.c | |||
@@ -25,28 +25,6 @@ | |||
25 | #include "ieee80211_rate.h" | 25 | #include "ieee80211_rate.h" |
26 | #include "wpa.h" | 26 | #include "wpa.h" |
27 | #include "aes_ccm.h" | 27 | #include "aes_ccm.h" |
28 | #include "debugfs_key.h" | ||
29 | |||
30 | static void ieee80211_set_hw_encryption(struct net_device *dev, | ||
31 | struct sta_info *sta, u8 addr[ETH_ALEN], | ||
32 | struct ieee80211_key *key) | ||
33 | { | ||
34 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | ||
35 | |||
36 | /* default to sw encryption; this will be cleared by low-level | ||
37 | * driver if the hw supports requested encryption */ | ||
38 | if (key) | ||
39 | key->conf.flags |= IEEE80211_KEY_FORCE_SW_ENCRYPT; | ||
40 | |||
41 | if (key && local->ops->set_key) { | ||
42 | if (local->ops->set_key(local_to_hw(local), SET_KEY, addr, | ||
43 | &key->conf)) { | ||
44 | key->conf.flags |= IEEE80211_KEY_FORCE_SW_ENCRYPT; | ||
45 | key->conf.hw_key_idx = HW_KEY_IDX_INVALID; | ||
46 | } | ||
47 | } | ||
48 | } | ||
49 | |||
50 | 28 | ||
51 | static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr, | 29 | static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr, |
52 | int idx, int alg, int set_tx_key, | 30 | int idx, int alg, int set_tx_key, |
@@ -55,8 +33,7 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr, | |||
55 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | 33 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); |
56 | int ret = 0; | 34 | int ret = 0; |
57 | struct sta_info *sta; | 35 | struct sta_info *sta; |
58 | struct ieee80211_key *key, *old_key; | 36 | struct ieee80211_key *key; |
59 | int try_hwaccel = 1; | ||
60 | struct ieee80211_sub_if_data *sdata; | 37 | struct ieee80211_sub_if_data *sdata; |
61 | 38 | ||
62 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | 39 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
@@ -69,16 +46,6 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr, | |||
69 | return -EINVAL; | 46 | return -EINVAL; |
70 | } | 47 | } |
71 | key = sdata->keys[idx]; | 48 | key = sdata->keys[idx]; |
72 | |||
73 | /* TODO: consider adding hwaccel support for these; at least | ||
74 | * Atheros key cache should be able to handle this since AP is | ||
75 | * only transmitting frames with default keys. */ | ||
76 | /* FIX: hw key cache can be used when only one virtual | ||
77 | * STA is associated with each AP. If more than one STA | ||
78 | * is associated to the same AP, software encryption | ||
79 | * must be used. This should be done automatically | ||
80 | * based on configured station devices. For the time | ||
81 | * being, this can be only set at compile time. */ | ||
82 | } else { | 49 | } else { |
83 | set_tx_key = 0; | 50 | set_tx_key = 0; |
84 | if (idx != 0) { | 51 | if (idx != 0) { |
@@ -101,139 +68,28 @@ static int ieee80211_set_encryption(struct net_device *dev, u8 *sta_addr, | |||
101 | key = sta->key; | 68 | key = sta->key; |
102 | } | 69 | } |
103 | 70 | ||
104 | /* FIX: | ||
105 | * Cannot configure default hwaccel keys with WEP algorithm, if | ||
106 | * any of the virtual interfaces is using static WEP | ||
107 | * configuration because hwaccel would otherwise try to decrypt | ||
108 | * these frames. | ||
109 | * | ||
110 | * For now, just disable WEP hwaccel for broadcast when there is | ||
111 | * possibility of conflict with default keys. This can maybe later be | ||
112 | * optimized by using non-default keys (at least with Atheros ar521x). | ||
113 | */ | ||
114 | if (!sta && alg == ALG_WEP && | ||
115 | sdata->type != IEEE80211_IF_TYPE_IBSS && | ||
116 | sdata->type != IEEE80211_IF_TYPE_AP) { | ||
117 | try_hwaccel = 0; | ||
118 | } | ||
119 | |||
120 | if (local->hw.flags & IEEE80211_HW_DEVICE_HIDES_WEP) { | ||
121 | /* Software encryption cannot be used with devices that hide | ||
122 | * encryption from the host system, so always try to use | ||
123 | * hardware acceleration with such devices. */ | ||
124 | try_hwaccel = 1; | ||
125 | } | ||
126 | |||
127 | if ((local->hw.flags & IEEE80211_HW_NO_TKIP_WMM_HWACCEL) && | ||
128 | alg == ALG_TKIP) { | ||
129 | if (sta && (sta->flags & WLAN_STA_WME)) { | ||
130 | /* Hardware does not support hwaccel with TKIP when using WMM. | ||
131 | */ | ||
132 | try_hwaccel = 0; | ||
133 | } | ||
134 | else if (sdata->type == IEEE80211_IF_TYPE_STA) { | ||
135 | sta = sta_info_get(local, sdata->u.sta.bssid); | ||
136 | if (sta) { | ||
137 | if (sta->flags & WLAN_STA_WME) { | ||
138 | try_hwaccel = 0; | ||
139 | } | ||
140 | sta_info_put(sta); | ||
141 | sta = NULL; | ||
142 | } | ||
143 | } | ||
144 | } | ||
145 | |||
146 | if (alg == ALG_NONE) { | 71 | if (alg == ALG_NONE) { |
147 | if (try_hwaccel && key && | ||
148 | key->conf.hw_key_idx != HW_KEY_IDX_INVALID && | ||
149 | local->ops->set_key && | ||
150 | local->ops->set_key(local_to_hw(local), DISABLE_KEY, | ||
151 | sta_addr, &key->conf)) { | ||
152 | printk(KERN_DEBUG "%s: set_encrypt - low-level disable" | ||
153 | " failed\n", dev->name); | ||
154 | ret = -EINVAL; | ||
155 | } | ||
156 | |||
157 | if (set_tx_key || sdata->default_key == key) { | ||
158 | ieee80211_debugfs_key_remove_default(sdata); | ||
159 | sdata->default_key = NULL; | ||
160 | } | ||
161 | ieee80211_debugfs_key_remove(key); | ||
162 | if (sta) | ||
163 | sta->key = NULL; | ||
164 | else | ||
165 | sdata->keys[idx] = NULL; | ||
166 | ieee80211_key_free(key); | 72 | ieee80211_key_free(key); |
167 | key = NULL; | 73 | key = NULL; |
168 | } else { | 74 | } else { |
169 | old_key = key; | 75 | /* |
170 | key = ieee80211_key_alloc(sta ? NULL : sdata, idx, key_len, | 76 | * Need to free it before allocating a new one with |
171 | GFP_KERNEL); | 77 | * with the same index or the ordering to the driver's |
78 | * set_key() callback becomes confused. | ||
79 | */ | ||
80 | ieee80211_key_free(key); | ||
81 | key = ieee80211_key_alloc(sdata, sta, alg, idx, key_len, _key); | ||
172 | if (!key) { | 82 | if (!key) { |
173 | ret = -ENOMEM; | 83 | ret = -ENOMEM; |
174 | goto err_out; | 84 | goto err_out; |
175 | } | 85 | } |
176 | |||
177 | /* default to sw encryption; low-level driver sets these if the | ||
178 | * requested encryption is supported */ | ||
179 | key->conf.hw_key_idx = HW_KEY_IDX_INVALID; | ||
180 | key->conf.flags |= IEEE80211_KEY_FORCE_SW_ENCRYPT; | ||
181 | |||
182 | key->conf.alg = alg; | ||
183 | key->conf.keyidx = idx; | ||
184 | key->conf.keylen = key_len; | ||
185 | memcpy(key->conf.key, _key, key_len); | ||
186 | |||
187 | if (alg == ALG_CCMP) { | ||
188 | /* Initialize AES key state here as an optimization | ||
189 | * so that it does not need to be initialized for every | ||
190 | * packet. */ | ||
191 | key->u.ccmp.tfm = ieee80211_aes_key_setup_encrypt( | ||
192 | key->conf.key); | ||
193 | if (!key->u.ccmp.tfm) { | ||
194 | ret = -ENOMEM; | ||
195 | goto err_free; | ||
196 | } | ||
197 | } | ||
198 | |||
199 | if (set_tx_key || sdata->default_key == old_key) { | ||
200 | ieee80211_debugfs_key_remove_default(sdata); | ||
201 | sdata->default_key = NULL; | ||
202 | } | ||
203 | ieee80211_debugfs_key_remove(old_key); | ||
204 | if (sta) | ||
205 | sta->key = key; | ||
206 | else | ||
207 | sdata->keys[idx] = key; | ||
208 | ieee80211_key_free(old_key); | ||
209 | ieee80211_debugfs_key_add(local, key); | ||
210 | if (sta) | ||
211 | ieee80211_debugfs_key_sta_link(key, sta); | ||
212 | |||
213 | if (try_hwaccel && | ||
214 | (alg == ALG_WEP || alg == ALG_TKIP || alg == ALG_CCMP)) | ||
215 | ieee80211_set_hw_encryption(dev, sta, sta_addr, key); | ||
216 | } | ||
217 | |||
218 | if (set_tx_key || (!sta && !sdata->default_key && key)) { | ||
219 | sdata->default_key = key; | ||
220 | if (key) | ||
221 | ieee80211_debugfs_key_add_default(sdata); | ||
222 | |||
223 | if (local->ops->set_key_idx && | ||
224 | local->ops->set_key_idx(local_to_hw(local), idx)) | ||
225 | printk(KERN_DEBUG "%s: failed to set TX key idx for " | ||
226 | "low-level driver\n", dev->name); | ||
227 | } | 86 | } |
228 | 87 | ||
229 | if (sta) | 88 | if (set_tx_key || (!sta && !sdata->default_key && key)) |
230 | sta_info_put(sta); | 89 | ieee80211_set_default_key(sdata, idx); |
231 | 90 | ||
232 | return 0; | 91 | ret = 0; |
233 | 92 | err_out: | |
234 | err_free: | ||
235 | ieee80211_key_free(key); | ||
236 | err_out: | ||
237 | if (sta) | 93 | if (sta) |
238 | sta_info_put(sta); | 94 | sta_info_put(sta); |
239 | return ret; | 95 | return ret; |
@@ -1181,12 +1037,7 @@ static int ieee80211_ioctl_siwencode(struct net_device *dev, | |||
1181 | alg = ALG_NONE; | 1037 | alg = ALG_NONE; |
1182 | else if (erq->length == 0) { | 1038 | else if (erq->length == 0) { |
1183 | /* No key data - just set the default TX key index */ | 1039 | /* No key data - just set the default TX key index */ |
1184 | if (sdata->default_key != sdata->keys[idx]) { | 1040 | ieee80211_set_default_key(sdata, idx); |
1185 | ieee80211_debugfs_key_remove_default(sdata); | ||
1186 | sdata->default_key = sdata->keys[idx]; | ||
1187 | if (sdata->default_key) | ||
1188 | ieee80211_debugfs_key_add_default(sdata); | ||
1189 | } | ||
1190 | return 0; | 1041 | return 0; |
1191 | } | 1042 | } |
1192 | 1043 | ||
@@ -1232,7 +1083,7 @@ static int ieee80211_ioctl_giwencode(struct net_device *dev, | |||
1232 | } | 1083 | } |
1233 | 1084 | ||
1234 | memcpy(key, sdata->keys[idx]->conf.key, | 1085 | memcpy(key, sdata->keys[idx]->conf.key, |
1235 | min((int)erq->length, sdata->keys[idx]->conf.keylen)); | 1086 | min_t(int, erq->length, sdata->keys[idx]->conf.keylen)); |
1236 | erq->length = sdata->keys[idx]->conf.keylen; | 1087 | erq->length = sdata->keys[idx]->conf.keylen; |
1237 | erq->flags |= IW_ENCODE_ENABLED; | 1088 | erq->flags |= IW_ENCODE_ENABLED; |
1238 | 1089 | ||