aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2013-08-07 14:11:55 -0400
committerJohannes Berg <johannes.berg@intel.com>2013-08-16 06:58:43 -0400
commit27b3eb9c06a7193bdc9800cd00764a130343bc8a (patch)
treed73ca7dfb8ccc2e967f4acf603c5bd52ca641db5 /net/mac80211
parentd51b70ff5122d31e27733ba03c3afd62bb86bd63 (diff)
mac80211: add APIs to allow keeping connections after WoWLAN
In order to be able to (securely) keep connections alive after the system was suspended for WoWLAN, we need some additional APIs. We already have API (ieee80211_gtk_rekey_notify) to tell wpa_supplicant about the new replay counter if GTK rekeying was done by the device while the host was asleep, but that's not sufficient. If GTK rekeying wasn't done, we need to tell the host about sequence counters for the GTK (and PTK regardless of rekeying) that was used while asleep, add ieee80211_set_key_rx_seq() for that. If GTK rekeying was done, then we need to be able to disable the old keys (with ieee80211_remove_key()) and allocate the new GTK key(s) in mac80211 (with ieee80211_gtk_rekey_add()). If protocol offload (e.g. ARP) is implemented, then also the TX sequence counter for the PTK must be updated, using the new ieee80211_set_key_tx_seq() function. Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211')
-rw-r--r--net/mac80211/key.c154
-rw-r--r--net/mac80211/util.c2
2 files changed, 148 insertions, 8 deletions
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index e39cc91d0cf1..620677e897bd 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -93,6 +93,9 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
93 93
94 might_sleep(); 94 might_sleep();
95 95
96 if (key->flags & KEY_FLAG_TAINTED)
97 return -EINVAL;
98
96 if (!key->local->ops->set_key) 99 if (!key->local->ops->set_key)
97 goto out_unsupported; 100 goto out_unsupported;
98 101
@@ -455,6 +458,7 @@ int ieee80211_key_link(struct ieee80211_key *key,
455 struct ieee80211_sub_if_data *sdata, 458 struct ieee80211_sub_if_data *sdata,
456 struct sta_info *sta) 459 struct sta_info *sta)
457{ 460{
461 struct ieee80211_local *local = sdata->local;
458 struct ieee80211_key *old_key; 462 struct ieee80211_key *old_key;
459 int idx, ret; 463 int idx, ret;
460 bool pairwise; 464 bool pairwise;
@@ -484,10 +488,13 @@ int ieee80211_key_link(struct ieee80211_key *key,
484 488
485 ieee80211_debugfs_key_add(key); 489 ieee80211_debugfs_key_add(key);
486 490
487 ret = ieee80211_key_enable_hw_accel(key); 491 if (!local->wowlan) {
488 492 ret = ieee80211_key_enable_hw_accel(key);
489 if (ret) 493 if (ret)
490 ieee80211_key_free(key, true); 494 ieee80211_key_free(key, true);
495 } else {
496 ret = 0;
497 }
491 498
492 mutex_unlock(&sdata->local->key_mtx); 499 mutex_unlock(&sdata->local->key_mtx);
493 500
@@ -540,7 +547,7 @@ void ieee80211_iter_keys(struct ieee80211_hw *hw,
540 void *iter_data) 547 void *iter_data)
541{ 548{
542 struct ieee80211_local *local = hw_to_local(hw); 549 struct ieee80211_local *local = hw_to_local(hw);
543 struct ieee80211_key *key; 550 struct ieee80211_key *key, *tmp;
544 struct ieee80211_sub_if_data *sdata; 551 struct ieee80211_sub_if_data *sdata;
545 552
546 ASSERT_RTNL(); 553 ASSERT_RTNL();
@@ -548,13 +555,14 @@ void ieee80211_iter_keys(struct ieee80211_hw *hw,
548 mutex_lock(&local->key_mtx); 555 mutex_lock(&local->key_mtx);
549 if (vif) { 556 if (vif) {
550 sdata = vif_to_sdata(vif); 557 sdata = vif_to_sdata(vif);
551 list_for_each_entry(key, &sdata->key_list, list) 558 list_for_each_entry_safe(key, tmp, &sdata->key_list, list)
552 iter(hw, &sdata->vif, 559 iter(hw, &sdata->vif,
553 key->sta ? &key->sta->sta : NULL, 560 key->sta ? &key->sta->sta : NULL,
554 &key->conf, iter_data); 561 &key->conf, iter_data);
555 } else { 562 } else {
556 list_for_each_entry(sdata, &local->interfaces, list) 563 list_for_each_entry(sdata, &local->interfaces, list)
557 list_for_each_entry(key, &sdata->key_list, list) 564 list_for_each_entry_safe(key, tmp,
565 &sdata->key_list, list)
558 iter(hw, &sdata->vif, 566 iter(hw, &sdata->vif,
559 key->sta ? &key->sta->sta : NULL, 567 key->sta ? &key->sta->sta : NULL,
560 &key->conf, iter_data); 568 &key->conf, iter_data);
@@ -751,3 +759,135 @@ void ieee80211_get_key_rx_seq(struct ieee80211_key_conf *keyconf,
751 } 759 }
752} 760}
753EXPORT_SYMBOL(ieee80211_get_key_rx_seq); 761EXPORT_SYMBOL(ieee80211_get_key_rx_seq);
762
763void ieee80211_set_key_tx_seq(struct ieee80211_key_conf *keyconf,
764 struct ieee80211_key_seq *seq)
765{
766 struct ieee80211_key *key;
767 u64 pn64;
768
769 key = container_of(keyconf, struct ieee80211_key, conf);
770
771 switch (key->conf.cipher) {
772 case WLAN_CIPHER_SUITE_TKIP:
773 key->u.tkip.tx.iv32 = seq->tkip.iv32;
774 key->u.tkip.tx.iv16 = seq->tkip.iv16;
775 break;
776 case WLAN_CIPHER_SUITE_CCMP:
777 pn64 = (u64)seq->ccmp.pn[5] |
778 ((u64)seq->ccmp.pn[4] << 8) |
779 ((u64)seq->ccmp.pn[3] << 16) |
780 ((u64)seq->ccmp.pn[2] << 24) |
781 ((u64)seq->ccmp.pn[1] << 32) |
782 ((u64)seq->ccmp.pn[0] << 40);
783 atomic64_set(&key->u.ccmp.tx_pn, pn64);
784 break;
785 case WLAN_CIPHER_SUITE_AES_CMAC:
786 pn64 = (u64)seq->aes_cmac.pn[5] |
787 ((u64)seq->aes_cmac.pn[4] << 8) |
788 ((u64)seq->aes_cmac.pn[3] << 16) |
789 ((u64)seq->aes_cmac.pn[2] << 24) |
790 ((u64)seq->aes_cmac.pn[1] << 32) |
791 ((u64)seq->aes_cmac.pn[0] << 40);
792 atomic64_set(&key->u.aes_cmac.tx_pn, pn64);
793 break;
794 default:
795 WARN_ON(1);
796 break;
797 }
798}
799EXPORT_SYMBOL_GPL(ieee80211_set_key_tx_seq);
800
801void ieee80211_set_key_rx_seq(struct ieee80211_key_conf *keyconf,
802 int tid, struct ieee80211_key_seq *seq)
803{
804 struct ieee80211_key *key;
805 u8 *pn;
806
807 key = container_of(keyconf, struct ieee80211_key, conf);
808
809 switch (key->conf.cipher) {
810 case WLAN_CIPHER_SUITE_TKIP:
811 if (WARN_ON(tid < 0 || tid >= IEEE80211_NUM_TIDS))
812 return;
813 key->u.tkip.rx[tid].iv32 = seq->tkip.iv32;
814 key->u.tkip.rx[tid].iv16 = seq->tkip.iv16;
815 break;
816 case WLAN_CIPHER_SUITE_CCMP:
817 if (WARN_ON(tid < -1 || tid >= IEEE80211_NUM_TIDS))
818 return;
819 if (tid < 0)
820 pn = key->u.ccmp.rx_pn[IEEE80211_NUM_TIDS];
821 else
822 pn = key->u.ccmp.rx_pn[tid];
823 memcpy(pn, seq->ccmp.pn, IEEE80211_CCMP_PN_LEN);
824 break;
825 case WLAN_CIPHER_SUITE_AES_CMAC:
826 if (WARN_ON(tid != 0))
827 return;
828 pn = key->u.aes_cmac.rx_pn;
829 memcpy(pn, seq->aes_cmac.pn, IEEE80211_CMAC_PN_LEN);
830 break;
831 default:
832 WARN_ON(1);
833 break;
834 }
835}
836EXPORT_SYMBOL_GPL(ieee80211_set_key_rx_seq);
837
838void ieee80211_remove_key(struct ieee80211_key_conf *keyconf)
839{
840 struct ieee80211_key *key;
841
842 key = container_of(keyconf, struct ieee80211_key, conf);
843
844 assert_key_lock(key->local);
845
846 /*
847 * if key was uploaded, we assume the driver will/has remove(d)
848 * it, so adjust bookkeeping accordingly
849 */
850 if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) {
851 key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE;
852
853 if (!((key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) ||
854 (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV) ||
855 (key->conf.flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)))
856 increment_tailroom_need_count(key->sdata);
857 }
858
859 ieee80211_key_free(key, false);
860}
861EXPORT_SYMBOL_GPL(ieee80211_remove_key);
862
863struct ieee80211_key_conf *
864ieee80211_gtk_rekey_add(struct ieee80211_vif *vif,
865 struct ieee80211_key_conf *keyconf)
866{
867 struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
868 struct ieee80211_local *local = sdata->local;
869 struct ieee80211_key *key;
870 int err;
871
872 if (WARN_ON(!local->wowlan))
873 return ERR_PTR(-EINVAL);
874
875 if (WARN_ON(vif->type != NL80211_IFTYPE_STATION))
876 return ERR_PTR(-EINVAL);
877
878 key = ieee80211_key_alloc(keyconf->cipher, keyconf->keyidx,
879 keyconf->keylen, keyconf->key,
880 0, NULL);
881 if (IS_ERR(key))
882 return ERR_PTR(PTR_ERR(key));
883
884 if (sdata->u.mgd.mfp != IEEE80211_MFP_DISABLED)
885 key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT;
886
887 err = ieee80211_key_link(key, sdata, NULL);
888 if (err)
889 return ERR_PTR(err);
890
891 return &key->conf;
892}
893EXPORT_SYMBOL_GPL(ieee80211_gtk_rekey_add);
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index d23c5a705a68..e1b34a18b243 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1453,8 +1453,8 @@ int ieee80211_reconfig(struct ieee80211_local *local)
1453 local->resuming = true; 1453 local->resuming = true;
1454 1454
1455 if (local->wowlan) { 1455 if (local->wowlan) {
1456 local->wowlan = false;
1457 res = drv_resume(local); 1456 res = drv_resume(local);
1457 local->wowlan = false;
1458 if (res < 0) { 1458 if (res < 0) {
1459 local->resuming = false; 1459 local->resuming = false;
1460 return res; 1460 return res;