diff options
author | Jouni Malinen <jouni.malinen@atheros.com> | 2008-12-17 06:32:17 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-12-19 15:23:46 -0500 |
commit | 6ace2891a1d632303283816ae050849da48df142 (patch) | |
tree | 87912574efd8120184ce7d618fbd8f1665788cdf /drivers | |
parent | f66000f7a39769b0ce2355fca13c40ac2f1c39db (diff) |
ath9k: Key cache allocation for AP mode
Enhance allocation of key cache entries to support multiple pairwise
keys to fix AP mode with more than one associated STA.
Signed-off-by: Jouni Malinen <jouni.malinen@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/wireless/ath9k/main.c | 200 |
1 files changed, 132 insertions, 68 deletions
diff --git a/drivers/net/wireless/ath9k/main.c b/drivers/net/wireless/ath9k/main.c index 11064cae8af..e768be4669e 100644 --- a/drivers/net/wireless/ath9k/main.c +++ b/drivers/net/wireless/ath9k/main.c | |||
@@ -664,13 +664,6 @@ static u32 ath_get_extchanmode(struct ath_softc *sc, | |||
664 | return chanmode; | 664 | return chanmode; |
665 | } | 665 | } |
666 | 666 | ||
667 | static void ath_key_reset(struct ath_softc *sc, u16 keyix, int freeslot) | ||
668 | { | ||
669 | ath9k_hw_keyreset(sc->sc_ah, keyix); | ||
670 | if (freeslot) | ||
671 | clear_bit(keyix, sc->sc_keymap); | ||
672 | } | ||
673 | |||
674 | static int ath_keyset(struct ath_softc *sc, u16 keyix, | 667 | static int ath_keyset(struct ath_softc *sc, u16 keyix, |
675 | struct ath9k_keyval *hk, const u8 mac[ETH_ALEN]) | 668 | struct ath9k_keyval *hk, const u8 mac[ETH_ALEN]) |
676 | { | 669 | { |
@@ -682,21 +675,20 @@ static int ath_keyset(struct ath_softc *sc, u16 keyix, | |||
682 | return status != false; | 675 | return status != false; |
683 | } | 676 | } |
684 | 677 | ||
685 | static int ath_setkey_tkip(struct ath_softc *sc, | 678 | static int ath_setkey_tkip(struct ath_softc *sc, u16 keyix, const u8 *key, |
686 | struct ieee80211_key_conf *key, | ||
687 | struct ath9k_keyval *hk, | 679 | struct ath9k_keyval *hk, |
688 | const u8 *addr) | 680 | const u8 *addr) |
689 | { | 681 | { |
690 | u8 *key_rxmic = NULL; | 682 | const u8 *key_rxmic; |
691 | u8 *key_txmic = NULL; | 683 | const u8 *key_txmic; |
692 | 684 | ||
693 | key_txmic = key->key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY; | 685 | key_txmic = key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY; |
694 | key_rxmic = key->key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY; | 686 | key_rxmic = key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY; |
695 | 687 | ||
696 | if (addr == NULL) { | 688 | if (addr == NULL) { |
697 | /* Group key installation */ | 689 | /* Group key installation */ |
698 | memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); | 690 | memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); |
699 | return ath_keyset(sc, key->keyidx, hk, addr); | 691 | return ath_keyset(sc, keyix, hk, addr); |
700 | } | 692 | } |
701 | if (!sc->sc_splitmic) { | 693 | if (!sc->sc_splitmic) { |
702 | /* | 694 | /* |
@@ -705,14 +697,14 @@ static int ath_setkey_tkip(struct ath_softc *sc, | |||
705 | */ | 697 | */ |
706 | memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); | 698 | memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); |
707 | memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_txmic)); | 699 | memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_txmic)); |
708 | return ath_keyset(sc, key->keyidx, hk, addr); | 700 | return ath_keyset(sc, keyix, hk, addr); |
709 | } | 701 | } |
710 | /* | 702 | /* |
711 | * TX key goes at first index, RX key at +32. | 703 | * TX key goes at first index, RX key at +32. |
712 | * The hal handles the MIC keys at index+64. | 704 | * The hal handles the MIC keys at index+64. |
713 | */ | 705 | */ |
714 | memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic)); | 706 | memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic)); |
715 | if (!ath_keyset(sc, key->keyidx, hk, NULL)) { | 707 | if (!ath_keyset(sc, keyix, hk, NULL)) { |
716 | /* Txmic entry failed. No need to proceed further */ | 708 | /* Txmic entry failed. No need to proceed further */ |
717 | DPRINTF(sc, ATH_DBG_KEYCACHE, | 709 | DPRINTF(sc, ATH_DBG_KEYCACHE, |
718 | "Setting TX MIC Key Failed\n"); | 710 | "Setting TX MIC Key Failed\n"); |
@@ -721,18 +713,85 @@ static int ath_setkey_tkip(struct ath_softc *sc, | |||
721 | 713 | ||
722 | memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); | 714 | memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); |
723 | /* XXX delete tx key on failure? */ | 715 | /* XXX delete tx key on failure? */ |
724 | return ath_keyset(sc, key->keyidx+32, hk, addr); | 716 | return ath_keyset(sc, keyix + 32, hk, addr); |
717 | } | ||
718 | |||
719 | static int ath_reserve_key_cache_slot_tkip(struct ath_softc *sc) | ||
720 | { | ||
721 | int i; | ||
722 | |||
723 | for (i = IEEE80211_WEP_NKID; i < sc->sc_keymax / 2; i++) { | ||
724 | if (test_bit(i, sc->sc_keymap) || | ||
725 | test_bit(i + 64, sc->sc_keymap)) | ||
726 | continue; /* At least one part of TKIP key allocated */ | ||
727 | if (sc->sc_splitmic && | ||
728 | (test_bit(i + 32, sc->sc_keymap) || | ||
729 | test_bit(i + 64 + 32, sc->sc_keymap))) | ||
730 | continue; /* At least one part of TKIP key allocated */ | ||
731 | |||
732 | /* Found a free slot for a TKIP key */ | ||
733 | return i; | ||
734 | } | ||
735 | return -1; | ||
736 | } | ||
737 | |||
738 | static int ath_reserve_key_cache_slot(struct ath_softc *sc) | ||
739 | { | ||
740 | int i; | ||
741 | |||
742 | /* First, try to find slots that would not be available for TKIP. */ | ||
743 | if (sc->sc_splitmic) { | ||
744 | for (i = IEEE80211_WEP_NKID; i < sc->sc_keymax / 4; i++) { | ||
745 | if (!test_bit(i, sc->sc_keymap) && | ||
746 | (test_bit(i + 32, sc->sc_keymap) || | ||
747 | test_bit(i + 64, sc->sc_keymap) || | ||
748 | test_bit(i + 64 + 32, sc->sc_keymap))) | ||
749 | return i; | ||
750 | if (!test_bit(i + 32, sc->sc_keymap) && | ||
751 | (test_bit(i, sc->sc_keymap) || | ||
752 | test_bit(i + 64, sc->sc_keymap) || | ||
753 | test_bit(i + 64 + 32, sc->sc_keymap))) | ||
754 | return i + 32; | ||
755 | if (!test_bit(i + 64, sc->sc_keymap) && | ||
756 | (test_bit(i , sc->sc_keymap) || | ||
757 | test_bit(i + 32, sc->sc_keymap) || | ||
758 | test_bit(i + 64 + 32, sc->sc_keymap))) | ||
759 | return i; | ||
760 | if (!test_bit(i + 64 + 32, sc->sc_keymap) && | ||
761 | (test_bit(i, sc->sc_keymap) || | ||
762 | test_bit(i + 32, sc->sc_keymap) || | ||
763 | test_bit(i + 64, sc->sc_keymap))) | ||
764 | return i; | ||
765 | } | ||
766 | } else { | ||
767 | for (i = IEEE80211_WEP_NKID; i < sc->sc_keymax / 2; i++) { | ||
768 | if (!test_bit(i, sc->sc_keymap) && | ||
769 | test_bit(i + 64, sc->sc_keymap)) | ||
770 | return i; | ||
771 | if (test_bit(i, sc->sc_keymap) && | ||
772 | !test_bit(i + 64, sc->sc_keymap)) | ||
773 | return i + 64; | ||
774 | } | ||
775 | } | ||
776 | |||
777 | /* No partially used TKIP slots, pick any available slot */ | ||
778 | for (i = IEEE80211_WEP_NKID; i < sc->sc_keymax; i++) { | ||
779 | if (!test_bit(i, sc->sc_keymap)) | ||
780 | return i; /* Found a free slot for a key */ | ||
781 | } | ||
782 | |||
783 | /* No free slot found */ | ||
784 | return -1; | ||
725 | } | 785 | } |
726 | 786 | ||
727 | static int ath_key_config(struct ath_softc *sc, | 787 | static int ath_key_config(struct ath_softc *sc, |
728 | const u8 *addr, | 788 | const u8 *addr, |
729 | struct ieee80211_key_conf *key) | 789 | struct ieee80211_key_conf *key) |
730 | { | 790 | { |
731 | struct ieee80211_vif *vif; | ||
732 | struct ath9k_keyval hk; | 791 | struct ath9k_keyval hk; |
733 | const u8 *mac = NULL; | 792 | const u8 *mac = NULL; |
734 | int ret = 0; | 793 | int ret = 0; |
735 | enum nl80211_iftype opmode; | 794 | int idx; |
736 | 795 | ||
737 | memset(&hk, 0, sizeof(hk)); | 796 | memset(&hk, 0, sizeof(hk)); |
738 | 797 | ||
@@ -750,65 +809,69 @@ static int ath_key_config(struct ath_softc *sc, | |||
750 | return -EINVAL; | 809 | return -EINVAL; |
751 | } | 810 | } |
752 | 811 | ||
753 | hk.kv_len = key->keylen; | 812 | hk.kv_len = key->keylen; |
754 | memcpy(hk.kv_val, key->key, key->keylen); | 813 | memcpy(hk.kv_val, key->key, key->keylen); |
755 | 814 | ||
756 | if (!sc->sc_vaps[0]) | 815 | if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { |
757 | return -EIO; | 816 | /* For now, use the default keys for broadcast keys. This may |
758 | 817 | * need to change with virtual interfaces. */ | |
759 | vif = sc->sc_vaps[0]; | 818 | idx = key->keyidx; |
760 | opmode = vif->type; | 819 | } else if (key->keyidx) { |
820 | struct ieee80211_vif *vif; | ||
761 | 821 | ||
762 | /* | 822 | mac = addr; |
763 | * Strategy: | 823 | vif = sc->sc_vaps[0]; |
764 | * For STA mc tx, we will not setup a key at | 824 | if (vif->type != NL80211_IFTYPE_AP) { |
765 | * all since we never tx mc. | 825 | /* Only keyidx 0 should be used with unicast key, but |
766 | * | 826 | * allow this for client mode for now. */ |
767 | * For STA mc rx, we will use the keyID. | 827 | idx = key->keyidx; |
768 | * | 828 | } else |
769 | * For ADHOC mc tx, we will use the keyID, and no macaddr. | 829 | return -EIO; |
770 | * | ||
771 | * For ADHOC mc rx, we will alloc a slot and plumb the mac of | ||
772 | * the peer node. | ||
773 | * BUT we will plumb a cleartext key so that we can do | ||
774 | * per-Sta default key table lookup in software. | ||
775 | */ | ||
776 | if (is_broadcast_ether_addr(addr)) { | ||
777 | switch (opmode) { | ||
778 | case NL80211_IFTYPE_STATION: | ||
779 | /* default key: could be group WPA key | ||
780 | * or could be static WEP key */ | ||
781 | mac = NULL; | ||
782 | break; | ||
783 | case NL80211_IFTYPE_ADHOC: | ||
784 | break; | ||
785 | case NL80211_IFTYPE_AP: | ||
786 | break; | ||
787 | default: | ||
788 | ASSERT(0); | ||
789 | break; | ||
790 | } | ||
791 | } else { | 830 | } else { |
792 | mac = addr; | 831 | mac = addr; |
832 | if (key->alg == ALG_TKIP) | ||
833 | idx = ath_reserve_key_cache_slot_tkip(sc); | ||
834 | else | ||
835 | idx = ath_reserve_key_cache_slot(sc); | ||
836 | if (idx < 0) | ||
837 | return -EIO; /* no free key cache entries */ | ||
793 | } | 838 | } |
794 | 839 | ||
795 | if (key->alg == ALG_TKIP) | 840 | if (key->alg == ALG_TKIP) |
796 | ret = ath_setkey_tkip(sc, key, &hk, mac); | 841 | ret = ath_setkey_tkip(sc, idx, key->key, &hk, mac); |
797 | else | 842 | else |
798 | ret = ath_keyset(sc, key->keyidx, &hk, mac); | 843 | ret = ath_keyset(sc, idx, &hk, mac); |
799 | 844 | ||
800 | if (!ret) | 845 | if (!ret) |
801 | return -EIO; | 846 | return -EIO; |
802 | 847 | ||
803 | return 0; | 848 | set_bit(idx, sc->sc_keymap); |
849 | if (key->alg == ALG_TKIP) { | ||
850 | set_bit(idx + 64, sc->sc_keymap); | ||
851 | if (sc->sc_splitmic) { | ||
852 | set_bit(idx + 32, sc->sc_keymap); | ||
853 | set_bit(idx + 64 + 32, sc->sc_keymap); | ||
854 | } | ||
855 | } | ||
856 | |||
857 | return idx; | ||
804 | } | 858 | } |
805 | 859 | ||
806 | static void ath_key_delete(struct ath_softc *sc, struct ieee80211_key_conf *key) | 860 | static void ath_key_delete(struct ath_softc *sc, struct ieee80211_key_conf *key) |
807 | { | 861 | { |
808 | int freeslot; | 862 | ath9k_hw_keyreset(sc->sc_ah, key->hw_key_idx); |
863 | if (key->hw_key_idx < IEEE80211_WEP_NKID) | ||
864 | return; | ||
865 | |||
866 | clear_bit(key->hw_key_idx, sc->sc_keymap); | ||
867 | if (key->alg != ALG_TKIP) | ||
868 | return; | ||
809 | 869 | ||
810 | freeslot = (key->keyidx >= 4) ? 1 : 0; | 870 | clear_bit(key->hw_key_idx + 64, sc->sc_keymap); |
811 | ath_key_reset(sc, key->keyidx, freeslot); | 871 | if (sc->sc_splitmic) { |
872 | clear_bit(key->hw_key_idx + 32, sc->sc_keymap); | ||
873 | clear_bit(key->hw_key_idx + 64 + 32, sc->sc_keymap); | ||
874 | } | ||
812 | } | 875 | } |
813 | 876 | ||
814 | static void setup_ht_cap(struct ieee80211_sta_ht_cap *ht_info) | 877 | static void setup_ht_cap(struct ieee80211_sta_ht_cap *ht_info) |
@@ -1301,13 +1364,15 @@ static int ath_init(u16 devid, struct ath_softc *sc) | |||
1301 | * Mark key cache slots associated with global keys | 1364 | * Mark key cache slots associated with global keys |
1302 | * as in use. If we knew TKIP was not to be used we | 1365 | * as in use. If we knew TKIP was not to be used we |
1303 | * could leave the +32, +64, and +32+64 slots free. | 1366 | * could leave the +32, +64, and +32+64 slots free. |
1304 | * XXX only for splitmic. | ||
1305 | */ | 1367 | */ |
1306 | for (i = 0; i < IEEE80211_WEP_NKID; i++) { | 1368 | for (i = 0; i < IEEE80211_WEP_NKID; i++) { |
1307 | set_bit(i, sc->sc_keymap); | 1369 | set_bit(i, sc->sc_keymap); |
1308 | set_bit(i + 32, sc->sc_keymap); | ||
1309 | set_bit(i + 64, sc->sc_keymap); | 1370 | set_bit(i + 64, sc->sc_keymap); |
1310 | set_bit(i + 32 + 64, sc->sc_keymap); | 1371 | if (ath9k_hw_getcapability(ah, ATH9K_CAP_TKIP_SPLIT, |
1372 | 0, NULL)) { | ||
1373 | set_bit(i + 32, sc->sc_keymap); | ||
1374 | set_bit(i + 32 + 64, sc->sc_keymap); | ||
1375 | } | ||
1311 | } | 1376 | } |
1312 | 1377 | ||
1313 | /* Collect the channel list using the default country code */ | 1378 | /* Collect the channel list using the default country code */ |
@@ -2292,18 +2357,17 @@ static int ath9k_set_key(struct ieee80211_hw *hw, | |||
2292 | switch (cmd) { | 2357 | switch (cmd) { |
2293 | case SET_KEY: | 2358 | case SET_KEY: |
2294 | ret = ath_key_config(sc, addr, key); | 2359 | ret = ath_key_config(sc, addr, key); |
2295 | if (!ret) { | 2360 | if (ret >= 0) { |
2296 | set_bit(key->keyidx, sc->sc_keymap); | 2361 | key->hw_key_idx = ret; |
2297 | key->hw_key_idx = key->keyidx; | ||
2298 | /* push IV and Michael MIC generation to stack */ | 2362 | /* push IV and Michael MIC generation to stack */ |
2299 | key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; | 2363 | key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; |
2300 | if (key->alg == ALG_TKIP) | 2364 | if (key->alg == ALG_TKIP) |
2301 | key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; | 2365 | key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; |
2366 | ret = 0; | ||
2302 | } | 2367 | } |
2303 | break; | 2368 | break; |
2304 | case DISABLE_KEY: | 2369 | case DISABLE_KEY: |
2305 | ath_key_delete(sc, key); | 2370 | ath_key_delete(sc, key); |
2306 | clear_bit(key->keyidx, sc->sc_keymap); | ||
2307 | break; | 2371 | break; |
2308 | default: | 2372 | default: |
2309 | ret = -EINVAL; | 2373 | ret = -EINVAL; |