diff options
Diffstat (limited to 'net/mac80211/key.c')
| -rw-r--r-- | net/mac80211/key.c | 208 | 
1 files changed, 139 insertions, 69 deletions
diff --git a/net/mac80211/key.c b/net/mac80211/key.c index ef252eb58c3..67059b88fea 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -248,11 +248,11 @@ void ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata,  } -static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, -				    struct sta_info *sta, -				    bool pairwise, -				    struct ieee80211_key *old, -				    struct ieee80211_key *new) +static void ieee80211_key_replace(struct ieee80211_sub_if_data *sdata, +				  struct sta_info *sta, +				  bool pairwise, +				  struct ieee80211_key *old, +				  struct ieee80211_key *new)  {  	int idx;  	bool defunikey, defmultikey, defmgmtkey; @@ -397,7 +397,41 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,  	return key;  } -static void __ieee80211_key_destroy(struct ieee80211_key *key) +static void ieee80211_key_free_common(struct ieee80211_key *key) +{ +	if (key->conf.cipher == WLAN_CIPHER_SUITE_CCMP) +		ieee80211_aes_key_free(key->u.ccmp.tfm); +	if (key->conf.cipher == WLAN_CIPHER_SUITE_AES_CMAC) +		ieee80211_aes_cmac_key_free(key->u.aes_cmac.tfm); +	kfree(key); +} + +static void __ieee80211_key_destroy(struct ieee80211_key *key, +				    bool delay_tailroom) +{ +	if (key->local) +		ieee80211_key_disable_hw_accel(key); + +	if (key->local) { +		struct ieee80211_sub_if_data *sdata = key->sdata; + +		ieee80211_debugfs_key_remove(key); + +		if (delay_tailroom) { +			/* see ieee80211_delayed_tailroom_dec */ +			sdata->crypto_tx_tailroom_pending_dec++; +			schedule_delayed_work(&sdata->dec_tailroom_needed_wk, +					      HZ/2); +		} else { +			sdata->crypto_tx_tailroom_needed_cnt--; +		} +	} + +	ieee80211_key_free_common(key); +} + +static void ieee80211_key_destroy(struct ieee80211_key *key, +				  bool delay_tailroom)  {  	if (!key)  		return; @@ -408,19 +442,13 @@ static void __ieee80211_key_destroy(struct ieee80211_key *key)  	 */  	synchronize_net(); -	if (key->local) -		ieee80211_key_disable_hw_accel(key); - -	if (key->conf.cipher == WLAN_CIPHER_SUITE_CCMP) -		ieee80211_aes_key_free(key->u.ccmp.tfm); -	if (key->conf.cipher == WLAN_CIPHER_SUITE_AES_CMAC) -		ieee80211_aes_cmac_key_free(key->u.aes_cmac.tfm); -	if (key->local) { -		ieee80211_debugfs_key_remove(key); -		key->sdata->crypto_tx_tailroom_needed_cnt--; -	} +	__ieee80211_key_destroy(key, delay_tailroom); +} -	kfree(key); +void ieee80211_key_free_unused(struct ieee80211_key *key) +{ +	WARN_ON(key->sdata || key->local); +	ieee80211_key_free_common(key);  }  int ieee80211_key_link(struct ieee80211_key *key, @@ -440,32 +468,6 @@ int ieee80211_key_link(struct ieee80211_key *key,  	key->sdata = sdata;  	key->sta = sta; -	if (sta) { -		/* -		 * some hardware cannot handle TKIP with QoS, so -		 * we indicate whether QoS could be in use. -		 */ -		if (test_sta_flag(sta, WLAN_STA_WME)) -			key->conf.flags |= IEEE80211_KEY_FLAG_WMM_STA; -	} else { -		if (sdata->vif.type == NL80211_IFTYPE_STATION) { -			struct sta_info *ap; - -			/* -			 * We're getting a sta pointer in, so must be under -			 * appropriate locking for sta_info_get(). -			 */ - -			/* same here, the AP could be using QoS */ -			ap = sta_info_get(key->sdata, key->sdata->u.mgd.bssid); -			if (ap) { -				if (test_sta_flag(ap, WLAN_STA_WME)) -					key->conf.flags |= -						IEEE80211_KEY_FLAG_WMM_STA; -			} -		} -	} -  	mutex_lock(&sdata->local->key_mtx);  	if (sta && pairwise) @@ -477,19 +479,22 @@ int ieee80211_key_link(struct ieee80211_key *key,  	increment_tailroom_need_count(sdata); -	__ieee80211_key_replace(sdata, sta, pairwise, old_key, key); -	__ieee80211_key_destroy(old_key); +	ieee80211_key_replace(sdata, sta, pairwise, old_key, key); +	ieee80211_key_destroy(old_key, true);  	ieee80211_debugfs_key_add(key);  	ret = ieee80211_key_enable_hw_accel(key); +	if (ret) +		ieee80211_key_free(key, true); +  	mutex_unlock(&sdata->local->key_mtx);  	return ret;  } -void __ieee80211_key_free(struct ieee80211_key *key) +void ieee80211_key_free(struct ieee80211_key *key, bool delay_tailroom)  {  	if (!key)  		return; @@ -498,18 +503,10 @@ void __ieee80211_key_free(struct ieee80211_key *key)  	 * Replace key with nothingness if it was ever used.  	 */  	if (key->sdata) -		__ieee80211_key_replace(key->sdata, key->sta, +		ieee80211_key_replace(key->sdata, key->sta,  				key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,  				key, NULL); -	__ieee80211_key_destroy(key); -} - -void ieee80211_key_free(struct ieee80211_local *local, -			struct ieee80211_key *key) -{ -	mutex_lock(&local->key_mtx); -	__ieee80211_key_free(key); -	mutex_unlock(&local->key_mtx); +	ieee80211_key_destroy(key, delay_tailroom);  }  void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata) @@ -566,36 +563,109 @@ void ieee80211_iter_keys(struct ieee80211_hw *hw,  }  EXPORT_SYMBOL(ieee80211_iter_keys); -void ieee80211_disable_keys(struct ieee80211_sub_if_data *sdata) +void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata)  { -	struct ieee80211_key *key; +	struct ieee80211_key *key, *tmp; +	LIST_HEAD(keys); -	ASSERT_RTNL(); +	cancel_delayed_work_sync(&sdata->dec_tailroom_needed_wk);  	mutex_lock(&sdata->local->key_mtx); -	list_for_each_entry(key, &sdata->key_list, list) -		ieee80211_key_disable_hw_accel(key); +	sdata->crypto_tx_tailroom_needed_cnt -= +		sdata->crypto_tx_tailroom_pending_dec; +	sdata->crypto_tx_tailroom_pending_dec = 0; + +	ieee80211_debugfs_key_remove_mgmt_default(sdata); + +	list_for_each_entry_safe(key, tmp, &sdata->key_list, list) { +		ieee80211_key_replace(key->sdata, key->sta, +				key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE, +				key, NULL); +		list_add_tail(&key->list, &keys); +	} + +	ieee80211_debugfs_key_update_default(sdata); + +	if (!list_empty(&keys)) { +		synchronize_net(); +		list_for_each_entry_safe(key, tmp, &keys, list) +			__ieee80211_key_destroy(key, false); +	} + +	WARN_ON_ONCE(sdata->crypto_tx_tailroom_needed_cnt || +		     sdata->crypto_tx_tailroom_pending_dec);  	mutex_unlock(&sdata->local->key_mtx);  } -void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata) +void ieee80211_free_sta_keys(struct ieee80211_local *local, +			     struct sta_info *sta)  {  	struct ieee80211_key *key, *tmp; +	LIST_HEAD(keys); +	int i; -	mutex_lock(&sdata->local->key_mtx); +	mutex_lock(&local->key_mtx); +	for (i = 0; i < NUM_DEFAULT_KEYS; i++) { +		key = key_mtx_dereference(local, sta->gtk[i]); +		if (!key) +			continue; +		ieee80211_key_replace(key->sdata, key->sta, +				key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE, +				key, NULL); +		list_add(&key->list, &keys); +	} -	ieee80211_debugfs_key_remove_mgmt_default(sdata); +	key = key_mtx_dereference(local, sta->ptk); +	if (key) { +		ieee80211_key_replace(key->sdata, key->sta, +				key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE, +				key, NULL); +		list_add(&key->list, &keys); +	} -	list_for_each_entry_safe(key, tmp, &sdata->key_list, list) -		__ieee80211_key_free(key); +	/* +	 * NB: the station code relies on this being +	 * done even if there aren't any keys +	 */ +	synchronize_net(); -	ieee80211_debugfs_key_update_default(sdata); +	list_for_each_entry_safe(key, tmp, &keys, list) +		__ieee80211_key_destroy(key, true); -	mutex_unlock(&sdata->local->key_mtx); +	mutex_unlock(&local->key_mtx);  } +void ieee80211_delayed_tailroom_dec(struct work_struct *wk) +{ +	struct ieee80211_sub_if_data *sdata; + +	sdata = container_of(wk, struct ieee80211_sub_if_data, +			     dec_tailroom_needed_wk.work); + +	/* +	 * The reason for the delayed tailroom needed decrementing is to +	 * make roaming faster: during roaming, all keys are first deleted +	 * and then new keys are installed. The first new key causes the +	 * crypto_tx_tailroom_needed_cnt to go from 0 to 1, which invokes +	 * the cost of synchronize_net() (which can be slow). Avoid this +	 * by deferring the crypto_tx_tailroom_needed_cnt decrementing on +	 * key removal for a while, so if we roam the value is larger than +	 * zero and no 0->1 transition happens. +	 * +	 * The cost is that if the AP switching was from an AP with keys +	 * to one without, we still allocate tailroom while it would no +	 * longer be needed. However, in the typical (fast) roaming case +	 * within an ESS this usually won't happen. +	 */ + +	mutex_lock(&sdata->local->key_mtx); +	sdata->crypto_tx_tailroom_needed_cnt -= +		sdata->crypto_tx_tailroom_pending_dec; +	sdata->crypto_tx_tailroom_pending_dec = 0; +	mutex_unlock(&sdata->local->key_mtx); +}  void ieee80211_gtk_rekey_notify(struct ieee80211_vif *vif, const u8 *bssid,  				const u8 *replay_ctr, gfp_t gfp)  |