ath10k: fix broken traffic for 802.1x in client mode
authorMichal Kazior <michal.kazior@tieto.com>
Wed, 18 Feb 2015 13:02:26 +0000 (14:02 +0100)
committerKalle Valo <kvalo@qca.qualcomm.com>
Sat, 7 Mar 2015 07:50:47 +0000 (09:50 +0200)
When running 802.1x WEP keys must be installed
without pairwise-groupwise swap (which is
necessary for static WEP).

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
drivers/net/wireless/ath/ath10k/mac.c

index 8b5e2c1..722ff38 100644 (file)
@@ -37,7 +37,7 @@
 static int ath10k_send_key(struct ath10k_vif *arvif,
                           struct ieee80211_key_conf *key,
                           enum set_key_cmd cmd,
-                          const u8 *macaddr, bool def_idx)
+                          const u8 *macaddr, u32 flags)
 {
        struct ath10k *ar = arvif->ar;
        struct wmi_vdev_install_key_arg arg = {
@@ -45,16 +45,12 @@ static int ath10k_send_key(struct ath10k_vif *arvif,
                .key_idx = key->keyidx,
                .key_len = key->keylen,
                .key_data = key->key,
+               .key_flags = flags,
                .macaddr = macaddr,
        };
 
        lockdep_assert_held(&arvif->ar->conf_mutex);
 
-       if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
-               arg.key_flags = WMI_KEY_PAIRWISE;
-       else
-               arg.key_flags = WMI_KEY_GROUP;
-
        switch (key->cipher) {
        case WLAN_CIPHER_SUITE_CCMP:
                arg.key_cipher = WMI_CIPHER_AES_CCM;
@@ -68,13 +64,6 @@ static int ath10k_send_key(struct ath10k_vif *arvif,
        case WLAN_CIPHER_SUITE_WEP40:
        case WLAN_CIPHER_SUITE_WEP104:
                arg.key_cipher = WMI_CIPHER_WEP;
-               /* AP/IBSS mode requires self-key to be groupwise
-                * Otherwise pairwise key must be set */
-               if (memcmp(macaddr, arvif->vif->addr, ETH_ALEN))
-                       arg.key_flags = WMI_KEY_PAIRWISE;
-
-               if (def_idx)
-                       arg.key_flags |= WMI_KEY_TX_USAGE;
                break;
        case WLAN_CIPHER_SUITE_AES_CMAC:
                /* this one needs to be done in software */
@@ -95,7 +84,7 @@ static int ath10k_send_key(struct ath10k_vif *arvif,
 static int ath10k_install_key(struct ath10k_vif *arvif,
                              struct ieee80211_key_conf *key,
                              enum set_key_cmd cmd,
-                             const u8 *macaddr, bool def_idx)
+                             const u8 *macaddr, u32 flags)
 {
        struct ath10k *ar = arvif->ar;
        int ret;
@@ -104,7 +93,7 @@ static int ath10k_install_key(struct ath10k_vif *arvif,
 
        reinit_completion(&ar->install_key_done);
 
-       ret = ath10k_send_key(arvif, key, cmd, macaddr, def_idx);
+       ret = ath10k_send_key(arvif, key, cmd, macaddr, flags);
        if (ret)
                return ret;
 
@@ -122,7 +111,7 @@ static int ath10k_install_peer_wep_keys(struct ath10k_vif *arvif,
        struct ath10k_peer *peer;
        int ret;
        int i;
-       bool def_idx;
+       u32 flags;
 
        lockdep_assert_held(&ar->conf_mutex);
 
@@ -136,14 +125,16 @@ static int ath10k_install_peer_wep_keys(struct ath10k_vif *arvif,
        for (i = 0; i < ARRAY_SIZE(arvif->wep_keys); i++) {
                if (arvif->wep_keys[i] == NULL)
                        continue;
+
+               flags = 0;
+               flags |= WMI_KEY_PAIRWISE;
+
                /* set TX_USAGE flag for default key id */
                if (arvif->def_wep_key_idx == i)
-                       def_idx = true;
-               else
-                       def_idx = false;
+                       flags |= WMI_KEY_TX_USAGE;
 
                ret = ath10k_install_key(arvif, arvif->wep_keys[i], SET_KEY,
-                                        addr, def_idx);
+                                        addr, flags);
                if (ret)
                        return ret;
 
@@ -163,6 +154,7 @@ static int ath10k_clear_peer_keys(struct ath10k_vif *arvif,
        int first_errno = 0;
        int ret;
        int i;
+       u32 flags = 0;
 
        lockdep_assert_held(&ar->conf_mutex);
 
@@ -179,7 +171,7 @@ static int ath10k_clear_peer_keys(struct ath10k_vif *arvif,
 
                /* key flags are not required to delete the key */
                ret = ath10k_install_key(arvif, peer->keys[i],
-                                        DISABLE_KEY, addr, false);
+                                        DISABLE_KEY, addr, flags);
                if (ret && first_errno == 0)
                        first_errno = ret;
 
@@ -229,6 +221,7 @@ static int ath10k_clear_vdev_key(struct ath10k_vif *arvif,
        int first_errno = 0;
        int ret;
        int i;
+       u32 flags = 0;
 
        lockdep_assert_held(&ar->conf_mutex);
 
@@ -254,7 +247,7 @@ static int ath10k_clear_vdev_key(struct ath10k_vif *arvif,
                if (i == ARRAY_SIZE(peer->keys))
                        break;
                /* key flags are not required to delete the key */
-               ret = ath10k_install_key(arvif, key, DISABLE_KEY, addr, false);
+               ret = ath10k_install_key(arvif, key, DISABLE_KEY, addr, flags);
                if (ret && first_errno == 0)
                        first_errno = ret;
 
@@ -266,6 +259,44 @@ static int ath10k_clear_vdev_key(struct ath10k_vif *arvif,
        return first_errno;
 }
 
+static int ath10k_mac_vif_sta_fix_wep_key(struct ath10k_vif *arvif)
+{
+       struct ath10k *ar = arvif->ar;
+       enum nl80211_iftype iftype = arvif->vif->type;
+       struct ieee80211_key_conf *key;
+       u32 flags = 0;
+       int num = 0;
+       int i;
+       int ret;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       if (iftype != NL80211_IFTYPE_STATION)
+               return 0;
+
+       for (i = 0; i < ARRAY_SIZE(arvif->wep_keys); i++) {
+               if (arvif->wep_keys[i]) {
+                       key = arvif->wep_keys[i];
+                       ++num;
+               }
+       }
+
+       if (num != 1)
+               return 0;
+
+       flags |= WMI_KEY_PAIRWISE;
+       flags |= WMI_KEY_TX_USAGE;
+
+       ret = ath10k_install_key(arvif, key, SET_KEY, arvif->bssid, flags);
+       if (ret) {
+               ath10k_warn(ar, "failed to install key %i on vdev %i: %d\n",
+                           key->keyidx, arvif->vdev_id, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
 /*********************/
 /* General utilities */
 /*********************/
@@ -3891,8 +3922,8 @@ static int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
        const u8 *peer_addr;
        bool is_wep = key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
                      key->cipher == WLAN_CIPHER_SUITE_WEP104;
-       bool def_idx = false;
        int ret = 0;
+       u32 flags = 0;
 
        if (key->keyidx > WMI_MAX_KEY_INDEX)
                return -ENOSPC;
@@ -3935,16 +3966,41 @@ static int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
 
                if (cmd == DISABLE_KEY)
                        ath10k_clear_vdev_key(arvif, key);
+
+               /* 802.1x never sets the def_wep_key_idx so each set_key()
+                * call changes default tx key.
+                *
+                * Static WEP sets def_wep_key_idx via .set_default_unicast_key
+                * after first set_key().
+                */
+               if (cmd == SET_KEY && arvif->def_wep_key_idx == -1)
+                       flags |= WMI_KEY_TX_USAGE;
        }
 
-       /* set TX_USAGE flag for all the keys incase of dot1x-WEP. For
-        * static WEP, do not set this flag for the keys whose key id
-        * is  greater than default key id.
+       if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
+               flags |= WMI_KEY_PAIRWISE;
+       else
+               flags |= WMI_KEY_GROUP;
+
+       /* mac80211 uploads static WEP keys as groupwise while fw/hw requires
+        * pairwise keys for non-self peers, i.e. BSSID in STA mode and
+        * associated stations in AP/IBSS.
+        *
+        * Static WEP keys for peer_addr=vif->addr and 802.1X WEP keys work
+        * fine when mapped directly from mac80211.
+        *
+        * Note: When installing first static WEP groupwise key (which should
+        * be pairwise) def_wep_key_idx isn't known yet (it's equal to -1).
+        * Since .set_default_unicast_key is called only for static WEP it's
+        * used to re-upload the key as pairwise.
         */
-       if (arvif->def_wep_key_idx == -1)
-               def_idx = true;
+       if (arvif->def_wep_key_idx >= 0 &&
+           memcmp(peer_addr, arvif->vif->addr, ETH_ALEN)) {
+               flags &= ~WMI_KEY_GROUP;
+               flags |= WMI_KEY_PAIRWISE;
+       }
 
-       ret = ath10k_install_key(arvif, key, cmd, peer_addr, def_idx);
+       ret = ath10k_install_key(arvif, key, cmd, peer_addr, flags);
        if (ret) {
                ath10k_warn(ar, "failed to install key for vdev %i peer %pM: %d\n",
                            arvif->vdev_id, peer_addr, ret);
@@ -3998,6 +4054,14 @@ static void ath10k_set_default_unicast_key(struct ieee80211_hw *hw,
        }
 
        arvif->def_wep_key_idx = keyidx;
+
+       ret = ath10k_mac_vif_sta_fix_wep_key(arvif);
+       if (ret) {
+               ath10k_warn(ar, "failed to fix sta wep key on vdev %i: %d\n",
+                           arvif->vdev_id, ret);
+               goto unlock;
+       }
+
 unlock:
        mutex_unlock(&arvif->ar->conf_mutex);
 }