mac80211: allow vendor specific cipher suites
authorJohannes Berg <johannes.berg@intel.com>
Fri, 27 Aug 2010 11:26:52 +0000 (14:26 +0300)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 27 Aug 2010 17:27:07 +0000 (13:27 -0400)
Allow drivers to specify their own set of cipher
suites to advertise vendor-specific ciphers. The
driver is then required to implement hardware
crypto offload for it.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Juuso Oikarinen <juuso.oikarinen@nokia.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
net/mac80211/cfg.c
net/mac80211/ieee80211_i.h
net/mac80211/key.c
net/mac80211/key.h
net/mac80211/main.c
net/mac80211/rx.c
net/mac80211/tx.c

index 581438255d7e949dc67d4a518b4c36caaa24e6ed..f149b4eb28d9acfe5efe47ea58c3fcc97b0522ff 100644 (file)
@@ -119,9 +119,10 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
                }
        }
 
-       ieee80211_key_link(key, sdata, sta);
+       err = ieee80211_key_link(key, sdata, sta);
+       if (err)
+               ieee80211_key_free(sdata->local, key);
 
-       err = 0;
  out_unlock:
        mutex_unlock(&sdata->local->sta_mtx);
 
index 31713320258c38fa3b4313e1fab9516c4ed014e2..7d2bb6fbc2e6dc0fbe63b221d1cbc860b13c1f45 100644 (file)
@@ -662,6 +662,8 @@ struct ieee80211_local {
        int fif_fcsfail, fif_plcpfail, fif_control, fif_other_bss, fif_pspoll;
        unsigned int filter_flags; /* FIF_* */
 
+       bool wiphy_ciphers_allocated;
+
        /* protects the aggregated multicast list and filter calls */
        spinlock_t filter_lock;
 
index 2ce2dbbf63097dfb466254c79f6ccac09c834261..3570f8c2bb401d3d4d20cbac6dbc0fddfb395235 100644 (file)
@@ -60,7 +60,7 @@ static struct ieee80211_sta *get_sta_for_key(struct ieee80211_key *key)
        return NULL;
 }
 
-static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
+static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
 {
        struct ieee80211_sub_if_data *sdata;
        struct ieee80211_sta *sta;
@@ -68,8 +68,10 @@ static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
 
        might_sleep();
 
-       if (!key->local->ops->set_key)
-               return;
+       if (!key->local->ops->set_key) {
+               ret = -EOPNOTSUPP;
+               goto out_unsupported;
+       }
 
        assert_key_lock(key->local);
 
@@ -90,6 +92,24 @@ static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
                wiphy_err(key->local->hw.wiphy,
                          "failed to set key (%d, %pM) to hardware (%d)\n",
                          key->conf.keyidx, sta ? sta->addr : bcast_addr, ret);
+
+out_unsupported:
+       if (ret) {
+               switch (key->conf.cipher) {
+               case WLAN_CIPHER_SUITE_WEP40:
+               case WLAN_CIPHER_SUITE_WEP104:
+               case WLAN_CIPHER_SUITE_TKIP:
+               case WLAN_CIPHER_SUITE_CCMP:
+               case WLAN_CIPHER_SUITE_AES_CMAC:
+                       /* all of these we can do in software */
+                       ret = 0;
+                       break;
+               default:
+                       ret = -EINVAL;
+               }
+       }
+
+       return ret;
 }
 
 static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)
@@ -329,12 +349,12 @@ static void __ieee80211_key_destroy(struct ieee80211_key *key)
        kfree(key);
 }
 
-void ieee80211_key_link(struct ieee80211_key *key,
-                       struct ieee80211_sub_if_data *sdata,
-                       struct sta_info *sta)
+int ieee80211_key_link(struct ieee80211_key *key,
+                      struct ieee80211_sub_if_data *sdata,
+                      struct sta_info *sta)
 {
        struct ieee80211_key *old_key;
-       int idx;
+       int idx, ret;
 
        BUG_ON(!sdata);
        BUG_ON(!key);
@@ -389,9 +409,11 @@ void ieee80211_key_link(struct ieee80211_key *key,
 
        ieee80211_debugfs_key_add(key);
 
-       ieee80211_key_enable_hw_accel(key);
+       ret = ieee80211_key_enable_hw_accel(key);
 
        mutex_unlock(&sdata->local->key_mtx);
+
+       return ret;
 }
 
 static void __ieee80211_key_free(struct ieee80211_key *key)
index 53b5ce12536f629bc4fb99365662ca0c2047bfb2..cb9a4a65cc68fae008b1ea79a0c0ff84d9651cb8 100644 (file)
@@ -130,9 +130,9 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
  * Insert a key into data structures (sdata, sta if necessary)
  * to make it used, free old key.
  */
-void ieee80211_key_link(struct ieee80211_key *key,
-                       struct ieee80211_sub_if_data *sdata,
-                       struct sta_info *sta);
+int __must_check ieee80211_key_link(struct ieee80211_key *key,
+                                   struct ieee80211_sub_if_data *sdata,
+                                   struct sta_info *sta);
 void ieee80211_key_free(struct ieee80211_local *local,
                        struct ieee80211_key *key);
 void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx);
index 80db5ea0205220b26575d523d292db3e2109d961..15f0e960fde843a423b2c3d46c97ed9e1656f250 100644 (file)
@@ -662,13 +662,40 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
        if (local->hw.wiphy->max_scan_ie_len)
                local->hw.wiphy->max_scan_ie_len -= local->scan_ies_len;
 
-       local->hw.wiphy->cipher_suites = cipher_suites;
-       local->hw.wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
-       if (!(local->hw.flags & IEEE80211_HW_MFP_CAPABLE))
-               local->hw.wiphy->n_cipher_suites--;
+       /* Set up cipher suites unless driver already did */
+       if (!local->hw.wiphy->cipher_suites) {
+               local->hw.wiphy->cipher_suites = cipher_suites;
+               local->hw.wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
+               if (!(local->hw.flags & IEEE80211_HW_MFP_CAPABLE))
+                       local->hw.wiphy->n_cipher_suites--;
+       }
        if (IS_ERR(local->wep_tx_tfm) || IS_ERR(local->wep_rx_tfm)) {
-               local->hw.wiphy->cipher_suites += 2;
-               local->hw.wiphy->n_cipher_suites -= 2;
+               if (local->hw.wiphy->cipher_suites == cipher_suites) {
+                       local->hw.wiphy->cipher_suites += 2;
+                       local->hw.wiphy->n_cipher_suites -= 2;
+               } else {
+                       u32 *suites;
+                       int r, w = 0;
+
+                       /* Filter out WEP */
+
+                       suites = kmemdup(
+                               local->hw.wiphy->cipher_suites,
+                               sizeof(u32) * local->hw.wiphy->n_cipher_suites,
+                               GFP_KERNEL);
+                       if (!suites)
+                               return -ENOMEM;
+                       for (r = 0; r < local->hw.wiphy->n_cipher_suites; r++) {
+                               u32 suite = local->hw.wiphy->cipher_suites[r];
+                               if (suite == WLAN_CIPHER_SUITE_WEP40 ||
+                                   suite == WLAN_CIPHER_SUITE_WEP104)
+                                       continue;
+                               suites[w++] = suite;
+                       }
+                       local->hw.wiphy->cipher_suites = suites;
+                       local->hw.wiphy->n_cipher_suites = w;
+                       local->wiphy_ciphers_allocated = true;
+               }
        }
 
        result = wiphy_register(local->hw.wiphy);
@@ -783,6 +810,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
  fail_workqueue:
        wiphy_unregister(local->hw.wiphy);
  fail_wiphy_register:
+       if (local->wiphy_ciphers_allocated)
+               kfree(local->hw.wiphy->cipher_suites);
        kfree(local->int_scan_req);
        return result;
 }
@@ -840,6 +869,9 @@ void ieee80211_free_hw(struct ieee80211_hw *hw)
        mutex_destroy(&local->iflist_mtx);
        mutex_destroy(&local->mtx);
 
+       if (local->wiphy_ciphers_allocated)
+               kfree(local->hw.wiphy->cipher_suites);
+
        wiphy_free(local->hw.wiphy);
 }
 EXPORT_SYMBOL(ieee80211_free_hw);
index e67deb48af5cc31653730472316486a3016be7e9..6e5fb16af55c99ec8e141b3466dff979358636ea 100644 (file)
@@ -1000,6 +1000,12 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
        case WLAN_CIPHER_SUITE_AES_CMAC:
                result = ieee80211_crypto_aes_cmac_decrypt(rx);
                break;
+       default:
+               /*
+                * We can reach here only with HW-only algorithms
+                * but why didn't it decrypt the frame?!
+                */
+               return RX_DROP_UNUSABLE;
        }
 
        /* either the frame has been decrypted or will be dropped */
index a6ac9fd248f2feeb1ffdab74510aaf09ffb98cba..31a8903a45afbb21b47c747d213177d6085adb52 100644 (file)
@@ -947,6 +947,8 @@ ieee80211_tx_h_stats(struct ieee80211_tx_data *tx)
 static ieee80211_tx_result debug_noinline
 ieee80211_tx_h_encrypt(struct ieee80211_tx_data *tx)
 {
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
+
        if (!tx->key)
                return TX_CONTINUE;
 
@@ -960,10 +962,16 @@ ieee80211_tx_h_encrypt(struct ieee80211_tx_data *tx)
                return ieee80211_crypto_ccmp_encrypt(tx);
        case WLAN_CIPHER_SUITE_AES_CMAC:
                return ieee80211_crypto_aes_cmac_encrypt(tx);
+       default:
+               /* handle hw-only algorithm */
+               if (info->control.hw_key) {
+                       ieee80211_tx_set_protected(tx);
+                       return TX_CONTINUE;
+               }
+               break;
+
        }
 
-       /* not reached */
-       WARN_ON(1);
        return TX_DROP;
 }