diff options
author | Johannes Berg <johannes.berg@intel.com> | 2010-10-05 13:39:30 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-10-06 16:30:40 -0400 |
commit | e31b82136d1adc7a599b6e99d3321e5831841f5a (patch) | |
tree | c72d78d4cccfd08587e909c7efe59956f1cbc23e /net/mac80211/key.c | |
parent | 53f73c09d64f1fa7d7e6e8b6bb7468d42eddc92d (diff) |
cfg80211/mac80211: allow per-station GTKs
This adds API to allow adding per-station GTKs,
updates mac80211 to support it, and also allows
drivers to remove a key from hwaccel again when
this may be necessary due to multiple GTKs.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/key.c')
-rw-r--r-- | net/mac80211/key.c | 95 |
1 files changed, 61 insertions, 34 deletions
diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 6a63d1abd14d..ccd676b2f599 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c | |||
@@ -68,15 +68,21 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key) | |||
68 | 68 | ||
69 | might_sleep(); | 69 | might_sleep(); |
70 | 70 | ||
71 | if (!key->local->ops->set_key) { | 71 | if (!key->local->ops->set_key) |
72 | ret = -EOPNOTSUPP; | ||
73 | goto out_unsupported; | 72 | goto out_unsupported; |
74 | } | ||
75 | 73 | ||
76 | assert_key_lock(key->local); | 74 | assert_key_lock(key->local); |
77 | 75 | ||
78 | sta = get_sta_for_key(key); | 76 | sta = get_sta_for_key(key); |
79 | 77 | ||
78 | /* | ||
79 | * If this is a per-STA GTK, check if it | ||
80 | * is supported; if not, return. | ||
81 | */ | ||
82 | if (sta && !(key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE) && | ||
83 | !(key->local->hw.flags & IEEE80211_HW_SUPPORTS_PER_STA_GTK)) | ||
84 | goto out_unsupported; | ||
85 | |||
80 | sdata = key->sdata; | 86 | sdata = key->sdata; |
81 | if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) | 87 | if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) |
82 | sdata = container_of(sdata->bss, | 88 | sdata = container_of(sdata->bss, |
@@ -85,31 +91,28 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key) | |||
85 | 91 | ||
86 | ret = drv_set_key(key->local, SET_KEY, sdata, sta, &key->conf); | 92 | ret = drv_set_key(key->local, SET_KEY, sdata, sta, &key->conf); |
87 | 93 | ||
88 | if (!ret) | 94 | if (!ret) { |
89 | key->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE; | 95 | key->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE; |
96 | return 0; | ||
97 | } | ||
90 | 98 | ||
91 | if (ret && ret != -ENOSPC && ret != -EOPNOTSUPP) | 99 | if (ret != -ENOSPC && ret != -EOPNOTSUPP) |
92 | wiphy_err(key->local->hw.wiphy, | 100 | wiphy_err(key->local->hw.wiphy, |
93 | "failed to set key (%d, %pM) to hardware (%d)\n", | 101 | "failed to set key (%d, %pM) to hardware (%d)\n", |
94 | key->conf.keyidx, sta ? sta->addr : bcast_addr, ret); | 102 | key->conf.keyidx, sta ? sta->addr : bcast_addr, ret); |
95 | 103 | ||
96 | out_unsupported: | 104 | out_unsupported: |
97 | if (ret) { | 105 | switch (key->conf.cipher) { |
98 | switch (key->conf.cipher) { | 106 | case WLAN_CIPHER_SUITE_WEP40: |
99 | case WLAN_CIPHER_SUITE_WEP40: | 107 | case WLAN_CIPHER_SUITE_WEP104: |
100 | case WLAN_CIPHER_SUITE_WEP104: | 108 | case WLAN_CIPHER_SUITE_TKIP: |
101 | case WLAN_CIPHER_SUITE_TKIP: | 109 | case WLAN_CIPHER_SUITE_CCMP: |
102 | case WLAN_CIPHER_SUITE_CCMP: | 110 | case WLAN_CIPHER_SUITE_AES_CMAC: |
103 | case WLAN_CIPHER_SUITE_AES_CMAC: | 111 | /* all of these we can do in software */ |
104 | /* all of these we can do in software */ | 112 | return 0; |
105 | ret = 0; | 113 | default: |
106 | break; | 114 | return -EINVAL; |
107 | default: | ||
108 | ret = -EINVAL; | ||
109 | } | ||
110 | } | 115 | } |
111 | |||
112 | return ret; | ||
113 | } | 116 | } |
114 | 117 | ||
115 | static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) | 118 | static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) |
@@ -147,6 +150,26 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) | |||
147 | key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE; | 150 | key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE; |
148 | } | 151 | } |
149 | 152 | ||
153 | void ieee80211_key_removed(struct ieee80211_key_conf *key_conf) | ||
154 | { | ||
155 | struct ieee80211_key *key; | ||
156 | |||
157 | key = container_of(key_conf, struct ieee80211_key, conf); | ||
158 | |||
159 | might_sleep(); | ||
160 | assert_key_lock(key->local); | ||
161 | |||
162 | key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE; | ||
163 | |||
164 | /* | ||
165 | * Flush TX path to avoid attempts to use this key | ||
166 | * after this function returns. Until then, drivers | ||
167 | * must be prepared to handle the key. | ||
168 | */ | ||
169 | synchronize_rcu(); | ||
170 | } | ||
171 | EXPORT_SYMBOL_GPL(ieee80211_key_removed); | ||
172 | |||
150 | static void __ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, | 173 | static void __ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, |
151 | int idx) | 174 | int idx) |
152 | { | 175 | { |
@@ -202,6 +225,7 @@ void ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata, | |||
202 | 225 | ||
203 | static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, | 226 | static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, |
204 | struct sta_info *sta, | 227 | struct sta_info *sta, |
228 | bool pairwise, | ||
205 | struct ieee80211_key *old, | 229 | struct ieee80211_key *old, |
206 | struct ieee80211_key *new) | 230 | struct ieee80211_key *new) |
207 | { | 231 | { |
@@ -210,8 +234,14 @@ static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, | |||
210 | if (new) | 234 | if (new) |
211 | list_add(&new->list, &sdata->key_list); | 235 | list_add(&new->list, &sdata->key_list); |
212 | 236 | ||
213 | if (sta) { | 237 | if (sta && pairwise) { |
214 | rcu_assign_pointer(sta->key, new); | 238 | rcu_assign_pointer(sta->ptk, new); |
239 | } else if (sta) { | ||
240 | if (old) | ||
241 | idx = old->conf.keyidx; | ||
242 | else | ||
243 | idx = new->conf.keyidx; | ||
244 | rcu_assign_pointer(sta->gtk[idx], new); | ||
215 | } else { | 245 | } else { |
216 | WARN_ON(new && old && new->conf.keyidx != old->conf.keyidx); | 246 | WARN_ON(new && old && new->conf.keyidx != old->conf.keyidx); |
217 | 247 | ||
@@ -355,6 +385,7 @@ int ieee80211_key_link(struct ieee80211_key *key, | |||
355 | { | 385 | { |
356 | struct ieee80211_key *old_key; | 386 | struct ieee80211_key *old_key; |
357 | int idx, ret; | 387 | int idx, ret; |
388 | bool pairwise = key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE; | ||
358 | 389 | ||
359 | BUG_ON(!sdata); | 390 | BUG_ON(!sdata); |
360 | BUG_ON(!key); | 391 | BUG_ON(!key); |
@@ -371,13 +402,6 @@ int ieee80211_key_link(struct ieee80211_key *key, | |||
371 | */ | 402 | */ |
372 | if (test_sta_flags(sta, WLAN_STA_WME)) | 403 | if (test_sta_flags(sta, WLAN_STA_WME)) |
373 | key->conf.flags |= IEEE80211_KEY_FLAG_WMM_STA; | 404 | key->conf.flags |= IEEE80211_KEY_FLAG_WMM_STA; |
374 | |||
375 | /* | ||
376 | * This key is for a specific sta interface, | ||
377 | * inform the driver that it should try to store | ||
378 | * this key as pairwise key. | ||
379 | */ | ||
380 | key->conf.flags |= IEEE80211_KEY_FLAG_PAIRWISE; | ||
381 | } else { | 405 | } else { |
382 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { | 406 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { |
383 | struct sta_info *ap; | 407 | struct sta_info *ap; |
@@ -399,12 +423,14 @@ int ieee80211_key_link(struct ieee80211_key *key, | |||
399 | 423 | ||
400 | mutex_lock(&sdata->local->key_mtx); | 424 | mutex_lock(&sdata->local->key_mtx); |
401 | 425 | ||
402 | if (sta) | 426 | if (sta && pairwise) |
403 | old_key = sta->key; | 427 | old_key = sta->ptk; |
428 | else if (sta) | ||
429 | old_key = sta->gtk[idx]; | ||
404 | else | 430 | else |
405 | old_key = sdata->keys[idx]; | 431 | old_key = sdata->keys[idx]; |
406 | 432 | ||
407 | __ieee80211_key_replace(sdata, sta, old_key, key); | 433 | __ieee80211_key_replace(sdata, sta, pairwise, old_key, key); |
408 | __ieee80211_key_destroy(old_key); | 434 | __ieee80211_key_destroy(old_key); |
409 | 435 | ||
410 | ieee80211_debugfs_key_add(key); | 436 | ieee80211_debugfs_key_add(key); |
@@ -423,7 +449,8 @@ static void __ieee80211_key_free(struct ieee80211_key *key) | |||
423 | */ | 449 | */ |
424 | if (key->sdata) | 450 | if (key->sdata) |
425 | __ieee80211_key_replace(key->sdata, key->sta, | 451 | __ieee80211_key_replace(key->sdata, key->sta, |
426 | key, NULL); | 452 | key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE, |
453 | key, NULL); | ||
427 | __ieee80211_key_destroy(key); | 454 | __ieee80211_key_destroy(key); |
428 | } | 455 | } |
429 | 456 | ||