aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/key.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2010-10-05 13:39:30 -0400
committerJohn W. Linville <linville@tuxdriver.com>2010-10-06 16:30:40 -0400
commite31b82136d1adc7a599b6e99d3321e5831841f5a (patch)
treec72d78d4cccfd08587e909c7efe59956f1cbc23e /net/mac80211/key.c
parent53f73c09d64f1fa7d7e6e8b6bb7468d42eddc92d (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.c95
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
96out_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
115static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) 118static 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
153void 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}
171EXPORT_SYMBOL_GPL(ieee80211_key_removed);
172
150static void __ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, 173static 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
203static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, 226static 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