Merge tag 'mac80211-next-for-davem-2015-02-03' of git://git.kernel.org/pub/scm/linux...
authorDavid S. Miller <davem@davemloft.net>
Wed, 4 Feb 2015 22:57:45 +0000 (14:57 -0800)
committerDavid S. Miller <davem@davemloft.net>
Wed, 4 Feb 2015 22:57:45 +0000 (14:57 -0800)
Last round of updates for net-next:
 * revert a patch that caused a regression with mesh userspace (Bob)
 * fix a number of suspend/resume related races
   (from Emmanuel, Luca and myself - we'll look at backporting later)
 * add software implementations for new ciphers (Jouni)
 * add a new ACPI ID for Broadcom's rfkill (Mika)
 * allow using netns FD for wireless (Vadim)
 * some other cleanups (various)

Signed-off-by: David S. Miller <davem@davemloft.net>
38 files changed:
Documentation/DocBook/80211.tmpl
drivers/net/wireless/ath/ath10k/mac.c
include/linux/ieee80211.h
include/net/cfg80211.h
include/net/mac80211.h
include/uapi/linux/nl80211.h
net/core/net_namespace.c
net/mac80211/Kconfig
net/mac80211/Makefile
net/mac80211/aes_ccm.c
net/mac80211/aes_ccm.h
net/mac80211/aes_cmac.c
net/mac80211/aes_cmac.h
net/mac80211/aes_gcm.c [new file with mode: 0644]
net/mac80211/aes_gcm.h [new file with mode: 0644]
net/mac80211/aes_gmac.c [new file with mode: 0644]
net/mac80211/aes_gmac.h [new file with mode: 0644]
net/mac80211/cfg.c
net/mac80211/chan.c
net/mac80211/debugfs_key.c
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/key.c
net/mac80211/key.h
net/mac80211/main.c
net/mac80211/mesh_plink.c
net/mac80211/mlme.c
net/mac80211/rx.c
net/mac80211/scan.c
net/mac80211/sta_info.c
net/mac80211/tdls.c
net/mac80211/tx.c
net/mac80211/util.c
net/mac80211/wpa.c
net/mac80211/wpa.h
net/rfkill/rfkill-gpio.c
net/wireless/nl80211.c
net/wireless/util.c

index 49b8b8907f36e0b9797f47e5a7de0f00e0c90db4..aac9357d4866bda01fcf2a0e532a5068b5d4bfbf 100644 (file)
       <section id="ps-client">
         <title>support for powersaving clients</title>
 !Pinclude/net/mac80211.h AP support for powersaving clients
-      </section>
 !Finclude/net/mac80211.h ieee80211_get_buffered_bc
 !Finclude/net/mac80211.h ieee80211_beacon_get
 !Finclude/net/mac80211.h ieee80211_sta_eosp
 !Finclude/net/mac80211.h ieee80211_sta_ps_transition_ni
 !Finclude/net/mac80211.h ieee80211_sta_set_buffered
 !Finclude/net/mac80211.h ieee80211_sta_block_awake
+      </section>
       </chapter>
 
       <chapter id="multi-iface">
           <title>RX A-MPDU aggregation</title>
 !Pnet/mac80211/agg-rx.c RX A-MPDU aggregation
 !Cnet/mac80211/agg-rx.c
-        </sect1>
 !Finclude/net/mac80211.h ieee80211_ampdu_mlme_action
+        </sect1>
       </chapter>
 
       <chapter id="smps">
index 08f293411bf0957cc720c67b3b1a1e2bfe0733a3..60a524b732079c6487a68b5494d8ad74c938815f 100644 (file)
@@ -76,6 +76,9 @@ static int ath10k_send_key(struct ath10k_vif *arvif,
                if (memcmp(macaddr, arvif->vif->addr, ETH_ALEN))
                        arg.key_flags = WMI_KEY_PAIRWISE;
                break;
+       case WLAN_CIPHER_SUITE_AES_CMAC:
+               /* this one needs to be done in software */
+               return 1;
        default:
                ath10k_warn(ar, "cipher %d is not supported\n", key->cipher);
                return -EOPNOTSUPP;
@@ -5035,6 +5038,13 @@ struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id)
 
 int ath10k_mac_register(struct ath10k *ar)
 {
+       static const u32 cipher_suites[] = {
+               WLAN_CIPHER_SUITE_WEP40,
+               WLAN_CIPHER_SUITE_WEP104,
+               WLAN_CIPHER_SUITE_TKIP,
+               WLAN_CIPHER_SUITE_CCMP,
+               WLAN_CIPHER_SUITE_AES_CMAC,
+       };
        struct ieee80211_supported_band *band;
        struct ieee80211_sta_vht_cap vht_cap;
        struct ieee80211_sta_ht_cap ht_cap;
@@ -5108,7 +5118,8 @@ int ath10k_mac_register(struct ath10k *ar)
                        IEEE80211_HW_REPORTS_TX_ACK_STATUS |
                        IEEE80211_HW_HAS_RATE_CONTROL |
                        IEEE80211_HW_AP_LINK_PS |
-                       IEEE80211_HW_SPECTRUM_MGMT;
+                       IEEE80211_HW_SPECTRUM_MGMT |
+                       IEEE80211_HW_SW_CRYPTO_CONTROL;
 
        ar->hw->wiphy->features |= NL80211_FEATURE_STATIC_SMPS;
 
@@ -5182,6 +5193,9 @@ int ath10k_mac_register(struct ath10k *ar)
                goto err_free;
        }
 
+       ar->hw->wiphy->cipher_suites = cipher_suites;
+       ar->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
+
        ret = ieee80211_register_hw(ar->hw);
        if (ret) {
                ath10k_err(ar, "failed to register ieee80211: %d\n", ret);
index 4f4eea8a62882914a86c11a084928a02225c6e0b..b9c7897dc5668c3fe59c30ab8505d334a6689168 100644 (file)
@@ -1017,6 +1017,15 @@ struct ieee80211_mmie {
        u8 mic[8];
 } __packed;
 
+/* Management MIC information element (IEEE 802.11w) for GMAC and CMAC-256 */
+struct ieee80211_mmie_16 {
+       u8 element_id;
+       u8 length;
+       __le16 key_id;
+       u8 sequence_number[6];
+       u8 mic[16];
+} __packed;
+
 struct ieee80211_vendor_ie {
        u8 element_id;
        u8 len;
@@ -1994,9 +2003,15 @@ enum ieee80211_key_len {
        WLAN_KEY_LEN_WEP40 = 5,
        WLAN_KEY_LEN_WEP104 = 13,
        WLAN_KEY_LEN_CCMP = 16,
+       WLAN_KEY_LEN_CCMP_256 = 32,
        WLAN_KEY_LEN_TKIP = 32,
        WLAN_KEY_LEN_AES_CMAC = 16,
        WLAN_KEY_LEN_SMS4 = 32,
+       WLAN_KEY_LEN_GCMP = 16,
+       WLAN_KEY_LEN_GCMP_256 = 32,
+       WLAN_KEY_LEN_BIP_CMAC_256 = 32,
+       WLAN_KEY_LEN_BIP_GMAC_128 = 16,
+       WLAN_KEY_LEN_BIP_GMAC_256 = 32,
 };
 
 #define IEEE80211_WEP_IV_LEN           4
@@ -2004,9 +2019,16 @@ enum ieee80211_key_len {
 #define IEEE80211_CCMP_HDR_LEN         8
 #define IEEE80211_CCMP_MIC_LEN         8
 #define IEEE80211_CCMP_PN_LEN          6
+#define IEEE80211_CCMP_256_HDR_LEN     8
+#define IEEE80211_CCMP_256_MIC_LEN     16
+#define IEEE80211_CCMP_256_PN_LEN      6
 #define IEEE80211_TKIP_IV_LEN          8
 #define IEEE80211_TKIP_ICV_LEN         4
 #define IEEE80211_CMAC_PN_LEN          6
+#define IEEE80211_GMAC_PN_LEN          6
+#define IEEE80211_GCMP_HDR_LEN         8
+#define IEEE80211_GCMP_MIC_LEN         16
+#define IEEE80211_GCMP_PN_LEN          6
 
 /* Public action codes */
 enum ieee80211_pub_actioncode {
@@ -2230,6 +2252,11 @@ enum ieee80211_sa_query_action {
 #define WLAN_CIPHER_SUITE_WEP104       0x000FAC05
 #define WLAN_CIPHER_SUITE_AES_CMAC     0x000FAC06
 #define WLAN_CIPHER_SUITE_GCMP         0x000FAC08
+#define WLAN_CIPHER_SUITE_GCMP_256     0x000FAC09
+#define WLAN_CIPHER_SUITE_CCMP_256     0x000FAC0A
+#define WLAN_CIPHER_SUITE_BIP_GMAC_128 0x000FAC0B
+#define WLAN_CIPHER_SUITE_BIP_GMAC_256 0x000FAC0C
+#define WLAN_CIPHER_SUITE_BIP_CMAC_256 0x000FAC0D
 
 #define WLAN_CIPHER_SUITE_SMS4         0x00147201
 
index 7b44ba0a76328eed71693790923f8ebf8f516c4c..64e09e1e809960be7daaef505e2e39dbab01c238 100644 (file)
@@ -1493,6 +1493,10 @@ struct cfg80211_match_set {
  * @rcu_head: RCU callback used to free the struct
  * @owner_nlportid: netlink portid of owner (if this should is a request
  *     owned by a particular socket)
+ * @delay: delay in seconds to use before starting the first scan
+ *     cycle.  The driver may ignore this parameter and start
+ *     immediately (or at any other time), if this feature is not
+ *     supported.
  */
 struct cfg80211_sched_scan_request {
        struct cfg80211_ssid *ssids;
@@ -1506,6 +1510,7 @@ struct cfg80211_sched_scan_request {
        struct cfg80211_match_set *match_sets;
        int n_match_sets;
        s32 min_rssi_thold;
+       u32 delay;
 
        u8 mac_addr[ETH_ALEN] __aligned(2);
        u8 mac_addr_mask[ETH_ALEN] __aligned(2);
index 275ee56152ade074418d5485fdc74ab38db8d8d3..d52914b75331a7bf2a384e10d9ec4a4ce427260a 100644 (file)
@@ -376,6 +376,12 @@ enum ieee80211_rssi_event {
  * @ssid_len: Length of SSID given in @ssid.
  * @hidden_ssid: The SSID of the current vif is hidden. Only valid in AP-mode.
  * @txpower: TX power in dBm
+ * @txpower_type: TX power adjustment used to control per packet Transmit
+ *     Power Control (TPC) in lower driver for the current vif. In particular
+ *     TPC is enabled if value passed in %txpower_type is
+ *     NL80211_TX_POWER_LIMITED (allow using less than specified from
+ *     userspace), whereas TPC is disabled if %txpower_type is set to
+ *     NL80211_TX_POWER_FIXED (use value configured from userspace)
  * @p2p_noa_attr: P2P NoA attribute for P2P powersave
  */
 struct ieee80211_bss_conf {
@@ -411,6 +417,7 @@ struct ieee80211_bss_conf {
        size_t ssid_len;
        bool hidden_ssid;
        int txpower;
+       enum nl80211_tx_power_setting txpower_type;
        struct ieee80211_p2p_noa_attr p2p_noa_attr;
 };
 
@@ -1287,8 +1294,8 @@ struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev);
  * @IEEE80211_KEY_FLAG_PAIRWISE: Set by mac80211, this flag indicates
  *     that the key is pairwise rather then a shared key.
  * @IEEE80211_KEY_FLAG_SW_MGMT_TX: This flag should be set by the driver for a
- *     CCMP key if it requires CCMP encryption of management frames (MFP) to
- *     be done in software.
+ *     CCMP/GCMP key if it requires CCMP/GCMP encryption of management frames
+ *     (MFP) to be done in software.
  * @IEEE80211_KEY_FLAG_PUT_IV_SPACE: This flag should be set by the driver
  *     if space should be prepared for the IV, but the IV
  *     itself should not be generated. Do not set together with
@@ -1303,7 +1310,7 @@ struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev);
  *     RX, if your crypto engine can't deal with TX you can also set the
  *     %IEEE80211_KEY_FLAG_SW_MGMT_TX flag to encrypt such frames in SW.
  * @IEEE80211_KEY_FLAG_GENERATE_IV_MGMT: This flag should be set by the
- *     driver for a CCMP key to indicate that is requires IV generation
+ *     driver for a CCMP/GCMP key to indicate that is requires IV generation
  *     only for managment frames (MFP).
  * @IEEE80211_KEY_FLAG_RESERVE_TAILROOM: This flag should be set by the
  *     driver for a key to indicate that sufficient tailroom must always
@@ -1634,6 +1641,12 @@ struct ieee80211_tx_control {
  *     be created.  It is expected user-space will create vifs as
  *     desired (and thus have them named as desired).
  *
+ * @IEEE80211_HW_SW_CRYPTO_CONTROL: The driver wants to control which of the
+ *     crypto algorithms can be done in software - so don't automatically
+ *     try to fall back to it if hardware crypto fails, but do so only if
+ *     the driver returns 1. This also forces the driver to advertise its
+ *     supported cipher suites.
+ *
  * @IEEE80211_HW_QUEUE_CONTROL: The driver wants to control per-interface
  *     queue mapping in order to use different queues (not just one per AC)
  *     for different virtual interfaces. See the doc section on HW queue
@@ -1681,6 +1694,7 @@ enum ieee80211_hw_flags {
        IEEE80211_HW_MFP_CAPABLE                        = 1<<13,
        IEEE80211_HW_WANT_MONITOR_VIF                   = 1<<14,
        IEEE80211_HW_NO_AUTO_VIF                        = 1<<15,
+       IEEE80211_HW_SW_CRYPTO_CONTROL                  = 1<<16,
        /* free slots */
        IEEE80211_HW_REPORTS_TX_ACK_STATUS              = 1<<18,
        IEEE80211_HW_CONNECTION_MONITOR                 = 1<<19,
@@ -1955,6 +1969,11 @@ void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb);
  * added; if you return 0 then hw_key_idx must be assigned to the
  * hardware key index, you are free to use the full u8 range.
  *
+ * Note that in the case that the @IEEE80211_HW_SW_CRYPTO_CONTROL flag is
+ * set, mac80211 will not automatically fall back to software crypto if
+ * enabling hardware crypto failed. The set_key() call may also return the
+ * value 1 to permit this specific key/algorithm to be done in software.
+ *
  * When the cmd is %DISABLE_KEY then it must succeed.
  *
  * Note that it is permissible to not decrypt a frame even if a key
@@ -4079,6 +4098,10 @@ void ieee80211_aes_cmac_calculate_k1_k2(struct ieee80211_key_conf *keyconf,
  *     reverse order than in packet)
  * @aes_cmac: PN data, most significant byte first (big endian,
  *     reverse order than in packet)
+ * @aes_gmac: PN data, most significant byte first (big endian,
+ *     reverse order than in packet)
+ * @gcmp: PN data, most significant byte first (big endian,
+ *     reverse order than in packet)
  */
 struct ieee80211_key_seq {
        union {
@@ -4092,6 +4115,12 @@ struct ieee80211_key_seq {
                struct {
                        u8 pn[6];
                } aes_cmac;
+               struct {
+                       u8 pn[6];
+               } aes_gmac;
+               struct {
+                       u8 pn[6];
+               } gcmp;
        };
 };
 
@@ -4116,7 +4145,7 @@ void ieee80211_get_key_tx_seq(struct ieee80211_key_conf *keyconf,
  * ieee80211_get_key_rx_seq - get key RX sequence counter
  *
  * @keyconf: the parameter passed with the set key
- * @tid: The TID, or -1 for the management frame value (CCMP only);
+ * @tid: The TID, or -1 for the management frame value (CCMP/GCMP only);
  *     the value on TID 0 is also used for non-QoS frames. For
  *     CMAC, only TID 0 is valid.
  * @seq: buffer to receive the sequence data
@@ -4152,7 +4181,7 @@ void ieee80211_set_key_tx_seq(struct ieee80211_key_conf *keyconf,
  * ieee80211_set_key_rx_seq - set key RX sequence counter
  *
  * @keyconf: the parameter passed with the set key
- * @tid: The TID, or -1 for the management frame value (CCMP only);
+ * @tid: The TID, or -1 for the management frame value (CCMP/GCMP only);
  *     the value on TID 0 is also used for non-QoS frames. For
  *     CMAC, only TID 0 is valid.
  * @seq: new sequence data
index f52797a90816e022f752dd5ccf7465e43cfeb41b..68b294e839447ab5a4bfeeaf0b2bebf6fe2ba427 100644 (file)
  *     %NL80211_ATTR_WIPHY and %NL80211_ATTR_WIPHY_NAME.
  *
  * @NL80211_CMD_GET_INTERFACE: Request an interface's configuration;
- *     either a dump request on a %NL80211_ATTR_WIPHY or a specific get
- *     on an %NL80211_ATTR_IFINDEX is supported.
+ *     either a dump request for all interfaces or a specific get with a
+ *     single %NL80211_ATTR_IFINDEX is supported.
  * @NL80211_CMD_SET_INTERFACE: Set type of a virtual interface, requires
  *     %NL80211_ATTR_IFINDEX and %NL80211_ATTR_IFTYPE.
  * @NL80211_CMD_NEW_INTERFACE: Newly created virtual interface or response
  *     if passed, define which channels should be scanned; if not
  *     passed, all channels allowed for the current regulatory domain
  *     are used.  Extra IEs can also be passed from the userspace by
- *     using the %NL80211_ATTR_IE attribute.
+ *     using the %NL80211_ATTR_IE attribute.  The first cycle of the
+ *     scheduled scan can be delayed by %NL80211_ATTR_SCHED_SCAN_DELAY
+ *     is supplied.
  * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan. Returns -ENOENT if
  *     scheduled scan is not running. The caller may assume that as soon
  *     as the call returns, it is safe to start a new scheduled scan again.
@@ -1735,6 +1737,9 @@ enum nl80211_commands {
  *     should be contained in the result as the sum of the respective counters
  *     over all channels.
  *
+ * @NL80211_ATTR_SCHED_SCAN_DELAY: delay before a scheduled scan (or a
+ *     WoWLAN net-detect scan) is started, u32 in seconds.
+ *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2098,6 +2103,10 @@ enum nl80211_attrs {
 
        NL80211_ATTR_SURVEY_RADIO_STATS,
 
+       NL80211_ATTR_NETNS_FD,
+
+       NL80211_ATTR_SCHED_SCAN_DELAY,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
@@ -3741,11 +3750,12 @@ struct nl80211_pattern_support {
  * @NL80211_WOWLAN_TRIG_NET_DETECT: wake up when a configured network
  *     is detected.  This is a nested attribute that contains the
  *     same attributes used with @NL80211_CMD_START_SCHED_SCAN.  It
- *     specifies how the scan is performed (e.g. the interval and the
- *     channels to scan) as well as the scan results that will
- *     trigger a wake (i.e. the matchsets).  This attribute is also
- *     sent in a response to @NL80211_CMD_GET_WIPHY, indicating the
- *     number of match sets supported by the driver (u32).
+ *     specifies how the scan is performed (e.g. the interval, the
+ *     channels to scan and the initial delay) as well as the scan
+ *     results that will trigger a wake (i.e. the matchsets).  This
+ *     attribute is also sent in a response to
+ *     @NL80211_CMD_GET_WIPHY, indicating the number of match sets
+ *     supported by the driver (u32).
  * @NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS: nested attribute
  *     containing an array with information about what triggered the
  *     wake up.  If no elements are present in the array, it means
index b7bde551ef76901de0456d811112ba232aa350ba..cb5290b8c428c5c348b25d842ab9cf3797b70eba 100644 (file)
@@ -446,6 +446,7 @@ struct net *get_net_ns_by_fd(int fd)
        return ERR_PTR(-EINVAL);
 }
 #endif
+EXPORT_SYMBOL_GPL(get_net_ns_by_fd);
 
 struct net *get_net_ns_by_pid(pid_t pid)
 {
index 75cc6801a4316dbeb25958192f018b8808681096..64a012a0c6e52dba4d026701402e56c6e82f73b5 100644 (file)
@@ -5,6 +5,7 @@ config MAC80211
        select CRYPTO_ARC4
        select CRYPTO_AES
        select CRYPTO_CCM
+       select CRYPTO_GCM
        select CRC32
        select AVERAGE
        ---help---
index e53671b1105e039ad95fadc8c3f96fd090550cc3..3275f01881bee8a53a046e117873347fe04877c8 100644 (file)
@@ -15,7 +15,9 @@ mac80211-y := \
        michael.o \
        tkip.o \
        aes_ccm.o \
+       aes_gcm.o \
        aes_cmac.o \
+       aes_gmac.o \
        cfg.o \
        ethtool.o \
        rx.o \
index 09d9caaec59112f40b060951ae16796388e2e741..7869bb40acaa1acbe60763493a738bf32812bb34 100644 (file)
@@ -20,7 +20,8 @@
 #include "aes_ccm.h"
 
 void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
-                              u8 *data, size_t data_len, u8 *mic)
+                              u8 *data, size_t data_len, u8 *mic,
+                              size_t mic_len)
 {
        struct scatterlist assoc, pt, ct[2];
 
@@ -35,7 +36,7 @@ void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
        sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad));
        sg_init_table(ct, 2);
        sg_set_buf(&ct[0], data, data_len);
-       sg_set_buf(&ct[1], mic, IEEE80211_CCMP_MIC_LEN);
+       sg_set_buf(&ct[1], mic, mic_len);
 
        aead_request_set_tfm(aead_req, tfm);
        aead_request_set_assoc(aead_req, &assoc, assoc.length);
@@ -45,7 +46,8 @@ void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
 }
 
 int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
-                             u8 *data, size_t data_len, u8 *mic)
+                             u8 *data, size_t data_len, u8 *mic,
+                             size_t mic_len)
 {
        struct scatterlist assoc, pt, ct[2];
        char aead_req_data[sizeof(struct aead_request) +
@@ -62,17 +64,18 @@ int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
        sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad));
        sg_init_table(ct, 2);
        sg_set_buf(&ct[0], data, data_len);
-       sg_set_buf(&ct[1], mic, IEEE80211_CCMP_MIC_LEN);
+       sg_set_buf(&ct[1], mic, mic_len);
 
        aead_request_set_tfm(aead_req, tfm);
        aead_request_set_assoc(aead_req, &assoc, assoc.length);
-       aead_request_set_crypt(aead_req, ct, &pt,
-                              data_len + IEEE80211_CCMP_MIC_LEN, b_0);
+       aead_request_set_crypt(aead_req, ct, &pt, data_len + mic_len, b_0);
 
        return crypto_aead_decrypt(aead_req);
 }
 
-struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[])
+struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[],
+                                                   size_t key_len,
+                                                   size_t mic_len)
 {
        struct crypto_aead *tfm;
        int err;
@@ -81,9 +84,9 @@ struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[])
        if (IS_ERR(tfm))
                return tfm;
 
-       err = crypto_aead_setkey(tfm, key, WLAN_KEY_LEN_CCMP);
+       err = crypto_aead_setkey(tfm, key, key_len);
        if (!err)
-               err = crypto_aead_setauthsize(tfm, IEEE80211_CCMP_MIC_LEN);
+               err = crypto_aead_setauthsize(tfm, mic_len);
        if (!err)
                return tfm;
 
index 2c7ab1948a2edba3964a5c0edfb7e941752719f6..6a73d1e4d186d34a00da8c2f8e509985c3805607 100644 (file)
 
 #include <linux/crypto.h>
 
-struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[]);
+struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[],
+                                                   size_t key_len,
+                                                   size_t mic_len);
 void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
-                              u8 *data, size_t data_len, u8 *mic);
+                              u8 *data, size_t data_len, u8 *mic,
+                              size_t mic_len);
 int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
-                             u8 *data, size_t data_len, u8 *mic);
+                             u8 *data, size_t data_len, u8 *mic,
+                             size_t mic_len);
 void ieee80211_aes_key_free(struct crypto_aead *tfm);
 
 #endif /* AES_CCM_H */
index 9b9009f99551bb18b9613b4c470361d7fded4eb3..4192806be3d36884d22ce5830a43cae63d54745d 100644 (file)
@@ -18,8 +18,8 @@
 #include "key.h"
 #include "aes_cmac.h"
 
-#define AES_CMAC_KEY_LEN 16
 #define CMAC_TLEN 8 /* CMAC TLen = 64 bits (8 octets) */
+#define CMAC_TLEN_256 16 /* CMAC TLen = 128 bits (16 octets) */
 #define AAD_LEN 20
 
 
@@ -35,9 +35,9 @@ static void gf_mulx(u8 *pad)
                pad[AES_BLOCK_SIZE - 1] ^= 0x87;
 }
 
-
-static void aes_128_cmac_vector(struct crypto_cipher *tfm, size_t num_elem,
-                               const u8 *addr[], const size_t *len, u8 *mac)
+static void aes_cmac_vector(struct crypto_cipher *tfm, size_t num_elem,
+                           const u8 *addr[], const size_t *len, u8 *mac,
+                           size_t mac_len)
 {
        u8 cbc[AES_BLOCK_SIZE], pad[AES_BLOCK_SIZE];
        const u8 *pos, *end;
@@ -88,7 +88,7 @@ static void aes_128_cmac_vector(struct crypto_cipher *tfm, size_t num_elem,
        for (i = 0; i < AES_BLOCK_SIZE; i++)
                pad[i] ^= cbc[i];
        crypto_cipher_encrypt_one(tfm, pad, pad);
-       memcpy(mac, pad, CMAC_TLEN);
+       memcpy(mac, pad, mac_len);
 }
 
 
@@ -107,17 +107,35 @@ void ieee80211_aes_cmac(struct crypto_cipher *tfm, const u8 *aad,
        addr[2] = zero;
        len[2] = CMAC_TLEN;
 
-       aes_128_cmac_vector(tfm, 3, addr, len, mic);
+       aes_cmac_vector(tfm, 3, addr, len, mic, CMAC_TLEN);
 }
 
+void ieee80211_aes_cmac_256(struct crypto_cipher *tfm, const u8 *aad,
+                           const u8 *data, size_t data_len, u8 *mic)
+{
+       const u8 *addr[3];
+       size_t len[3];
+       u8 zero[CMAC_TLEN_256];
+
+       memset(zero, 0, CMAC_TLEN_256);
+       addr[0] = aad;
+       len[0] = AAD_LEN;
+       addr[1] = data;
+       len[1] = data_len - CMAC_TLEN_256;
+       addr[2] = zero;
+       len[2] = CMAC_TLEN_256;
+
+       aes_cmac_vector(tfm, 3, addr, len, mic, CMAC_TLEN_256);
+}
 
-struct crypto_cipher *ieee80211_aes_cmac_key_setup(const u8 key[])
+struct crypto_cipher *ieee80211_aes_cmac_key_setup(const u8 key[],
+                                                  size_t key_len)
 {
        struct crypto_cipher *tfm;
 
        tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
        if (!IS_ERR(tfm))
-               crypto_cipher_setkey(tfm, key, AES_CMAC_KEY_LEN);
+               crypto_cipher_setkey(tfm, key, key_len);
 
        return tfm;
 }
index 0ce6487af79536c03e7cae25639eeacd389f5f18..3702041f44fdb16ce382c701eb53ef9493009f52 100644 (file)
 
 #include <linux/crypto.h>
 
-struct crypto_cipher *ieee80211_aes_cmac_key_setup(const u8 key[]);
+struct crypto_cipher *ieee80211_aes_cmac_key_setup(const u8 key[],
+                                                  size_t key_len);
 void ieee80211_aes_cmac(struct crypto_cipher *tfm, const u8 *aad,
                        const u8 *data, size_t data_len, u8 *mic);
+void ieee80211_aes_cmac_256(struct crypto_cipher *tfm, const u8 *aad,
+                           const u8 *data, size_t data_len, u8 *mic);
 void ieee80211_aes_cmac_key_free(struct crypto_cipher *tfm);
 
 #endif /* AES_CMAC_H */
diff --git a/net/mac80211/aes_gcm.c b/net/mac80211/aes_gcm.c
new file mode 100644 (file)
index 0000000..c2bf669
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2014-2015, Qualcomm Atheros, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/crypto.h>
+#include <linux/err.h>
+#include <crypto/aes.h>
+
+#include <net/mac80211.h>
+#include "key.h"
+#include "aes_gcm.h"
+
+void ieee80211_aes_gcm_encrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad,
+                              u8 *data, size_t data_len, u8 *mic)
+{
+       struct scatterlist assoc, pt, ct[2];
+
+       char aead_req_data[sizeof(struct aead_request) +
+                          crypto_aead_reqsize(tfm)]
+               __aligned(__alignof__(struct aead_request));
+       struct aead_request *aead_req = (void *)aead_req_data;
+
+       memset(aead_req, 0, sizeof(aead_req_data));
+
+       sg_init_one(&pt, data, data_len);
+       sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad));
+       sg_init_table(ct, 2);
+       sg_set_buf(&ct[0], data, data_len);
+       sg_set_buf(&ct[1], mic, IEEE80211_GCMP_MIC_LEN);
+
+       aead_request_set_tfm(aead_req, tfm);
+       aead_request_set_assoc(aead_req, &assoc, assoc.length);
+       aead_request_set_crypt(aead_req, &pt, ct, data_len, j_0);
+
+       crypto_aead_encrypt(aead_req);
+}
+
+int ieee80211_aes_gcm_decrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad,
+                             u8 *data, size_t data_len, u8 *mic)
+{
+       struct scatterlist assoc, pt, ct[2];
+       char aead_req_data[sizeof(struct aead_request) +
+                          crypto_aead_reqsize(tfm)]
+               __aligned(__alignof__(struct aead_request));
+       struct aead_request *aead_req = (void *)aead_req_data;
+
+       if (data_len == 0)
+               return -EINVAL;
+
+       memset(aead_req, 0, sizeof(aead_req_data));
+
+       sg_init_one(&pt, data, data_len);
+       sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad));
+       sg_init_table(ct, 2);
+       sg_set_buf(&ct[0], data, data_len);
+       sg_set_buf(&ct[1], mic, IEEE80211_GCMP_MIC_LEN);
+
+       aead_request_set_tfm(aead_req, tfm);
+       aead_request_set_assoc(aead_req, &assoc, assoc.length);
+       aead_request_set_crypt(aead_req, ct, &pt,
+                              data_len + IEEE80211_GCMP_MIC_LEN, j_0);
+
+       return crypto_aead_decrypt(aead_req);
+}
+
+struct crypto_aead *ieee80211_aes_gcm_key_setup_encrypt(const u8 key[],
+                                                       size_t key_len)
+{
+       struct crypto_aead *tfm;
+       int err;
+
+       tfm = crypto_alloc_aead("gcm(aes)", 0, CRYPTO_ALG_ASYNC);
+       if (IS_ERR(tfm))
+               return tfm;
+
+       err = crypto_aead_setkey(tfm, key, key_len);
+       if (!err)
+               err = crypto_aead_setauthsize(tfm, IEEE80211_GCMP_MIC_LEN);
+       if (!err)
+               return tfm;
+
+       crypto_free_aead(tfm);
+       return ERR_PTR(err);
+}
+
+void ieee80211_aes_gcm_key_free(struct crypto_aead *tfm)
+{
+       crypto_free_aead(tfm);
+}
diff --git a/net/mac80211/aes_gcm.h b/net/mac80211/aes_gcm.h
new file mode 100644 (file)
index 0000000..1347fda
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014-2015, Qualcomm Atheros, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef AES_GCM_H
+#define AES_GCM_H
+
+#include <linux/crypto.h>
+
+void ieee80211_aes_gcm_encrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad,
+                              u8 *data, size_t data_len, u8 *mic);
+int ieee80211_aes_gcm_decrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad,
+                             u8 *data, size_t data_len, u8 *mic);
+struct crypto_aead *ieee80211_aes_gcm_key_setup_encrypt(const u8 key[],
+                                                       size_t key_len);
+void ieee80211_aes_gcm_key_free(struct crypto_aead *tfm);
+
+#endif /* AES_GCM_H */
diff --git a/net/mac80211/aes_gmac.c b/net/mac80211/aes_gmac.c
new file mode 100644 (file)
index 0000000..1c72edc
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * AES-GMAC for IEEE 802.11 BIP-GMAC-128 and BIP-GMAC-256
+ * Copyright 2015, Qualcomm Atheros, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/crypto.h>
+#include <linux/err.h>
+#include <crypto/aes.h>
+
+#include <net/mac80211.h>
+#include "key.h"
+#include "aes_gmac.h"
+
+#define GMAC_MIC_LEN 16
+#define GMAC_NONCE_LEN 12
+#define AAD_LEN 20
+
+int ieee80211_aes_gmac(struct crypto_aead *tfm, const u8 *aad, u8 *nonce,
+                      const u8 *data, size_t data_len, u8 *mic)
+{
+       struct scatterlist sg[3], ct[1];
+       char aead_req_data[sizeof(struct aead_request) +
+                          crypto_aead_reqsize(tfm)]
+               __aligned(__alignof__(struct aead_request));
+       struct aead_request *aead_req = (void *)aead_req_data;
+       u8 zero[GMAC_MIC_LEN], iv[AES_BLOCK_SIZE];
+
+       if (data_len < GMAC_MIC_LEN)
+               return -EINVAL;
+
+       memset(aead_req, 0, sizeof(aead_req_data));
+
+       memset(zero, 0, GMAC_MIC_LEN);
+       sg_init_table(sg, 3);
+       sg_set_buf(&sg[0], aad, AAD_LEN);
+       sg_set_buf(&sg[1], data, data_len - GMAC_MIC_LEN);
+       sg_set_buf(&sg[2], zero, GMAC_MIC_LEN);
+
+       memcpy(iv, nonce, GMAC_NONCE_LEN);
+       memset(iv + GMAC_NONCE_LEN, 0, sizeof(iv) - GMAC_NONCE_LEN);
+       iv[AES_BLOCK_SIZE - 1] = 0x01;
+
+       sg_init_table(ct, 1);
+       sg_set_buf(&ct[0], mic, GMAC_MIC_LEN);
+
+       aead_request_set_tfm(aead_req, tfm);
+       aead_request_set_assoc(aead_req, sg, AAD_LEN + data_len);
+       aead_request_set_crypt(aead_req, NULL, ct, 0, iv);
+
+       crypto_aead_encrypt(aead_req);
+
+       return 0;
+}
+
+struct crypto_aead *ieee80211_aes_gmac_key_setup(const u8 key[],
+                                                size_t key_len)
+{
+       struct crypto_aead *tfm;
+       int err;
+
+       tfm = crypto_alloc_aead("gcm(aes)", 0, CRYPTO_ALG_ASYNC);
+       if (IS_ERR(tfm))
+               return tfm;
+
+       err = crypto_aead_setkey(tfm, key, key_len);
+       if (!err)
+               return tfm;
+       if (!err)
+               err = crypto_aead_setauthsize(tfm, GMAC_MIC_LEN);
+
+       crypto_free_aead(tfm);
+       return ERR_PTR(err);
+}
+
+void ieee80211_aes_gmac_key_free(struct crypto_aead *tfm)
+{
+       crypto_free_aead(tfm);
+}
diff --git a/net/mac80211/aes_gmac.h b/net/mac80211/aes_gmac.h
new file mode 100644 (file)
index 0000000..d328204
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015, Qualcomm Atheros, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef AES_GMAC_H
+#define AES_GMAC_H
+
+#include <linux/crypto.h>
+
+struct crypto_aead *ieee80211_aes_gmac_key_setup(const u8 key[],
+                                                size_t key_len);
+int ieee80211_aes_gmac(struct crypto_aead *tfm, const u8 *aad, u8 *nonce,
+                      const u8 *data, size_t data_len, u8 *mic);
+void ieee80211_aes_gmac_key_free(struct crypto_aead *tfm);
+
+#endif /* AES_GMAC_H */
index ff090ef1ea2cdd57d85484b9f24ec4c801873e3e..dd4ff36c557a44158ef64cd18aa090600fec1faf 100644 (file)
@@ -162,8 +162,13 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
                        return -EINVAL;
                break;
        case WLAN_CIPHER_SUITE_CCMP:
+       case WLAN_CIPHER_SUITE_CCMP_256:
        case WLAN_CIPHER_SUITE_AES_CMAC:
+       case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+       case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+       case WLAN_CIPHER_SUITE_BIP_GMAC_256:
        case WLAN_CIPHER_SUITE_GCMP:
+       case WLAN_CIPHER_SUITE_GCMP_256:
                break;
        default:
                cs = ieee80211_cs_get(local, params->cipher, sdata->vif.type);
@@ -348,6 +353,7 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
                params.seq_len = 6;
                break;
        case WLAN_CIPHER_SUITE_CCMP:
+       case WLAN_CIPHER_SUITE_CCMP_256:
                pn64 = atomic64_read(&key->u.ccmp.tx_pn);
                seq[0] = pn64;
                seq[1] = pn64 >> 8;
@@ -359,6 +365,7 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
                params.seq_len = 6;
                break;
        case WLAN_CIPHER_SUITE_AES_CMAC:
+       case WLAN_CIPHER_SUITE_BIP_CMAC_256:
                pn64 = atomic64_read(&key->u.aes_cmac.tx_pn);
                seq[0] = pn64;
                seq[1] = pn64 >> 8;
@@ -369,6 +376,30 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
                params.seq = seq;
                params.seq_len = 6;
                break;
+       case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+       case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+               pn64 = atomic64_read(&key->u.aes_gmac.tx_pn);
+               seq[0] = pn64;
+               seq[1] = pn64 >> 8;
+               seq[2] = pn64 >> 16;
+               seq[3] = pn64 >> 24;
+               seq[4] = pn64 >> 32;
+               seq[5] = pn64 >> 40;
+               params.seq = seq;
+               params.seq_len = 6;
+               break;
+       case WLAN_CIPHER_SUITE_GCMP:
+       case WLAN_CIPHER_SUITE_GCMP_256:
+               pn64 = atomic64_read(&key->u.gcmp.tx_pn);
+               seq[0] = pn64;
+               seq[1] = pn64 >> 8;
+               seq[2] = pn64 >> 16;
+               seq[3] = pn64 >> 24;
+               seq[4] = pn64 >> 32;
+               seq[5] = pn64 >> 40;
+               params.seq = seq;
+               params.seq_len = 6;
+               break;
        }
 
        params.key = key->conf.key;
@@ -2110,6 +2141,8 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy,
 {
        struct ieee80211_local *local = wiphy_priv(wiphy);
        struct ieee80211_sub_if_data *sdata;
+       enum nl80211_tx_power_setting txp_type = type;
+       bool update_txp_type = false;
 
        if (wdev) {
                sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
@@ -2117,6 +2150,7 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy,
                switch (type) {
                case NL80211_TX_POWER_AUTOMATIC:
                        sdata->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
+                       txp_type = NL80211_TX_POWER_LIMITED;
                        break;
                case NL80211_TX_POWER_LIMITED:
                case NL80211_TX_POWER_FIXED:
@@ -2126,7 +2160,12 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy,
                        break;
                }
 
-               ieee80211_recalc_txpower(sdata);
+               if (txp_type != sdata->vif.bss_conf.txpower_type) {
+                       update_txp_type = true;
+                       sdata->vif.bss_conf.txpower_type = txp_type;
+               }
+
+               ieee80211_recalc_txpower(sdata, update_txp_type);
 
                return 0;
        }
@@ -2134,6 +2173,7 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy,
        switch (type) {
        case NL80211_TX_POWER_AUTOMATIC:
                local->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
+               txp_type = NL80211_TX_POWER_LIMITED;
                break;
        case NL80211_TX_POWER_LIMITED:
        case NL80211_TX_POWER_FIXED:
@@ -2144,10 +2184,14 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy,
        }
 
        mutex_lock(&local->iflist_mtx);
-       list_for_each_entry(sdata, &local->interfaces, list)
+       list_for_each_entry(sdata, &local->interfaces, list) {
                sdata->user_power_level = local->user_power_level;
+               if (txp_type != sdata->vif.bss_conf.txpower_type)
+                       update_txp_type = true;
+               sdata->vif.bss_conf.txpower_type = txp_type;
+       }
        list_for_each_entry(sdata, &local->interfaces, list)
-               ieee80211_recalc_txpower(sdata);
+               ieee80211_recalc_txpower(sdata, update_txp_type);
        mutex_unlock(&local->iflist_mtx);
 
        return 0;
index 35b11e11e0c49ba81cb4ecdc9b3624e02628f503..ff0d2db09df9db467a5831606971e02f2fe6d410 100644 (file)
@@ -655,7 +655,7 @@ out:
        }
 
        if (new_ctx && ieee80211_chanctx_num_assigned(local, new_ctx) > 0) {
-               ieee80211_recalc_txpower(sdata);
+               ieee80211_recalc_txpower(sdata, false);
                ieee80211_recalc_chanctx_min_def(local, new_ctx);
        }
 
@@ -1387,7 +1387,7 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
                                ieee80211_bss_info_change_notify(sdata,
                                                                 changed);
 
-                       ieee80211_recalc_txpower(sdata);
+                       ieee80211_recalc_txpower(sdata, false);
                }
 
                ieee80211_recalc_chanctx_chantype(local, ctx);
index 5523b94c7c908f89e7d489903f486a99d738e7d3..71ac1b5f4da5632ab64893dfc2305c77a5a8b4d7 100644 (file)
@@ -94,17 +94,33 @@ static ssize_t key_tx_spec_read(struct file *file, char __user *userbuf,
                                key->u.tkip.tx.iv16);
                break;
        case WLAN_CIPHER_SUITE_CCMP:
+       case WLAN_CIPHER_SUITE_CCMP_256:
                pn = atomic64_read(&key->u.ccmp.tx_pn);
                len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n",
                                (u8)(pn >> 40), (u8)(pn >> 32), (u8)(pn >> 24),
                                (u8)(pn >> 16), (u8)(pn >> 8), (u8)pn);
                break;
        case WLAN_CIPHER_SUITE_AES_CMAC:
+       case WLAN_CIPHER_SUITE_BIP_CMAC_256:
                pn = atomic64_read(&key->u.aes_cmac.tx_pn);
                len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n",
                                (u8)(pn >> 40), (u8)(pn >> 32), (u8)(pn >> 24),
                                (u8)(pn >> 16), (u8)(pn >> 8), (u8)pn);
                break;
+       case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+       case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+               pn = atomic64_read(&key->u.aes_gmac.tx_pn);
+               len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n",
+                               (u8)(pn >> 40), (u8)(pn >> 32), (u8)(pn >> 24),
+                               (u8)(pn >> 16), (u8)(pn >> 8), (u8)pn);
+               break;
+       case WLAN_CIPHER_SUITE_GCMP:
+       case WLAN_CIPHER_SUITE_GCMP_256:
+               pn = atomic64_read(&key->u.gcmp.tx_pn);
+               len = scnprintf(buf, sizeof(buf), "%02x%02x%02x%02x%02x%02x\n",
+                               (u8)(pn >> 40), (u8)(pn >> 32), (u8)(pn >> 24),
+                               (u8)(pn >> 16), (u8)(pn >> 8), (u8)pn);
+               break;
        default:
                return 0;
        }
@@ -134,6 +150,7 @@ static ssize_t key_rx_spec_read(struct file *file, char __user *userbuf,
                len = p - buf;
                break;
        case WLAN_CIPHER_SUITE_CCMP:
+       case WLAN_CIPHER_SUITE_CCMP_256:
                for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++) {
                        rpn = key->u.ccmp.rx_pn[i];
                        p += scnprintf(p, sizeof(buf)+buf-p,
@@ -144,6 +161,7 @@ static ssize_t key_rx_spec_read(struct file *file, char __user *userbuf,
                len = p - buf;
                break;
        case WLAN_CIPHER_SUITE_AES_CMAC:
+       case WLAN_CIPHER_SUITE_BIP_CMAC_256:
                rpn = key->u.aes_cmac.rx_pn;
                p += scnprintf(p, sizeof(buf)+buf-p,
                               "%02x%02x%02x%02x%02x%02x\n",
@@ -151,6 +169,26 @@ static ssize_t key_rx_spec_read(struct file *file, char __user *userbuf,
                               rpn[3], rpn[4], rpn[5]);
                len = p - buf;
                break;
+       case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+       case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+               rpn = key->u.aes_gmac.rx_pn;
+               p += scnprintf(p, sizeof(buf)+buf-p,
+                              "%02x%02x%02x%02x%02x%02x\n",
+                              rpn[0], rpn[1], rpn[2],
+                              rpn[3], rpn[4], rpn[5]);
+               len = p - buf;
+               break;
+       case WLAN_CIPHER_SUITE_GCMP:
+       case WLAN_CIPHER_SUITE_GCMP_256:
+               for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++) {
+                       rpn = key->u.gcmp.rx_pn[i];
+                       p += scnprintf(p, sizeof(buf)+buf-p,
+                                      "%02x%02x%02x%02x%02x%02x\n",
+                                      rpn[0], rpn[1], rpn[2],
+                                      rpn[3], rpn[4], rpn[5]);
+               }
+               len = p - buf;
+               break;
        default:
                return 0;
        }
@@ -167,12 +205,23 @@ static ssize_t key_replays_read(struct file *file, char __user *userbuf,
 
        switch (key->conf.cipher) {
        case WLAN_CIPHER_SUITE_CCMP:
+       case WLAN_CIPHER_SUITE_CCMP_256:
                len = scnprintf(buf, sizeof(buf), "%u\n", key->u.ccmp.replays);
                break;
        case WLAN_CIPHER_SUITE_AES_CMAC:
+       case WLAN_CIPHER_SUITE_BIP_CMAC_256:
                len = scnprintf(buf, sizeof(buf), "%u\n",
                                key->u.aes_cmac.replays);
                break;
+       case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+       case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+               len = scnprintf(buf, sizeof(buf), "%u\n",
+                               key->u.aes_gmac.replays);
+               break;
+       case WLAN_CIPHER_SUITE_GCMP:
+       case WLAN_CIPHER_SUITE_GCMP_256:
+               len = scnprintf(buf, sizeof(buf), "%u\n", key->u.gcmp.replays);
+               break;
        default:
                return 0;
        }
@@ -189,9 +238,15 @@ static ssize_t key_icverrors_read(struct file *file, char __user *userbuf,
 
        switch (key->conf.cipher) {
        case WLAN_CIPHER_SUITE_AES_CMAC:
+       case WLAN_CIPHER_SUITE_BIP_CMAC_256:
                len = scnprintf(buf, sizeof(buf), "%u\n",
                                key->u.aes_cmac.icverrors);
                break;
+       case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+       case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+               len = scnprintf(buf, sizeof(buf), "%u\n",
+                               key->u.aes_gmac.icverrors);
+               break;
        default:
                return 0;
        }
index 156ea79e01579405de37480fdb0797768f675ecf..3afe36824703f49dcfe374e103b6372a9851b8aa 100644 (file)
@@ -1621,7 +1621,8 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local);
 void ieee80211_del_virtual_monitor(struct ieee80211_local *local);
 
 bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
-void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
+void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata,
+                             bool update_bss);
 
 static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata)
 {
@@ -1751,7 +1752,8 @@ static inline int __ieee80211_resume(struct ieee80211_hw *hw)
 {
        struct ieee80211_local *local = hw_to_local(hw);
 
-       WARN(test_bit(SCAN_HW_SCANNING, &local->scanning),
+       WARN(test_bit(SCAN_HW_SCANNING, &local->scanning) &&
+            !test_bit(SCAN_COMPLETED, &local->scanning),
                "%s: resume with hardware scan still in progress\n",
                wiphy_name(hw->wiphy));
 
@@ -1885,6 +1887,36 @@ void __ieee80211_flush_queues(struct ieee80211_local *local,
                              struct ieee80211_sub_if_data *sdata,
                              unsigned int queues, bool drop);
 
+static inline bool ieee80211_can_run_worker(struct ieee80211_local *local)
+{
+       /*
+        * If quiescing is set, we are racing with __ieee80211_suspend.
+        * __ieee80211_suspend flushes the workers after setting quiescing,
+        * and we check quiescing / suspended before enqueing new workers.
+        * We should abort the worker to avoid the races below.
+        */
+       if (local->quiescing)
+               return false;
+
+       /*
+        * We might already be suspended if the following scenario occurs:
+        * __ieee80211_suspend          Control path
+        *
+        *                              if (local->quiescing)
+        *                                      return;
+        * local->quiescing = true;
+        * flush_workqueue();
+        *                              queue_work(...);
+        * local->suspended = true;
+        * local->quiescing = false;
+        *                              worker starts running...
+        */
+       if (local->suspended)
+               return false;
+
+       return true;
+}
+
 void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
                         u16 transaction, u16 auth_alg, u16 status,
                         const u8 *extra, size_t extra_len, const u8 *bssid,
index 677422e11e075137ce0c85bb6819ce0db17b187b..81a27516813e2f3473bec783ef9253b54967e62d 100644 (file)
@@ -73,9 +73,10 @@ bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
        return false;
 }
 
-void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
+void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata,
+                             bool update_bss)
 {
-       if (__ieee80211_recalc_txpower(sdata))
+       if (__ieee80211_recalc_txpower(sdata) || update_bss)
                ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_TXPOWER);
 }
 
@@ -1169,12 +1170,7 @@ static void ieee80211_iface_work(struct work_struct *work)
        if (local->scanning)
                return;
 
-       /*
-        * ieee80211_queue_work() should have picked up most cases,
-        * here we'll pick the rest.
-        */
-       if (WARN(local->suspended,
-                "interface work scheduled while going to suspend\n"))
+       if (!ieee80211_can_run_worker(local))
                return;
 
        /* first process frames */
index f8d9f0ee59bf1b549ba18c4be160bbf3533e2249..0825d76edcfc81d93c4afa70ec81e157a7ecc2d2 100644 (file)
@@ -24,6 +24,8 @@
 #include "debugfs_key.h"
 #include "aes_ccm.h"
 #include "aes_cmac.h"
+#include "aes_gmac.h"
+#include "aes_gcm.h"
 
 
 /**
@@ -90,7 +92,7 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
 {
        struct ieee80211_sub_if_data *sdata;
        struct sta_info *sta;
-       int ret;
+       int ret = -EOPNOTSUPP;
 
        might_sleep();
 
@@ -150,7 +152,7 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
                return 0;
        }
 
-       if (ret != -ENOSPC && ret != -EOPNOTSUPP)
+       if (ret != -ENOSPC && ret != -EOPNOTSUPP && ret != 1)
                sdata_err(sdata,
                          "failed to set key (%d, %pM) to hardware (%d)\n",
                          key->conf.keyidx,
@@ -162,8 +164,18 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
        case WLAN_CIPHER_SUITE_WEP104:
        case WLAN_CIPHER_SUITE_TKIP:
        case WLAN_CIPHER_SUITE_CCMP:
+       case WLAN_CIPHER_SUITE_CCMP_256:
        case WLAN_CIPHER_SUITE_AES_CMAC:
-               /* all of these we can do in software */
+       case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+       case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+       case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+       case WLAN_CIPHER_SUITE_GCMP:
+       case WLAN_CIPHER_SUITE_GCMP_256:
+               /* all of these we can do in software - if driver can */
+               if (ret == 1)
+                       return 0;
+               if (key->local->hw.flags & IEEE80211_HW_SW_CRYPTO_CONTROL)
+                       return -EINVAL;
                return 0;
        default:
                return -EINVAL;
@@ -382,7 +394,26 @@ ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
                 * Initialize AES key state here as an optimization so that
                 * it does not need to be initialized for every packet.
                 */
-               key->u.ccmp.tfm = ieee80211_aes_key_setup_encrypt(key_data);
+               key->u.ccmp.tfm = ieee80211_aes_key_setup_encrypt(
+                       key_data, key_len, IEEE80211_CCMP_MIC_LEN);
+               if (IS_ERR(key->u.ccmp.tfm)) {
+                       err = PTR_ERR(key->u.ccmp.tfm);
+                       kfree(key);
+                       return ERR_PTR(err);
+               }
+               break;
+       case WLAN_CIPHER_SUITE_CCMP_256:
+               key->conf.iv_len = IEEE80211_CCMP_256_HDR_LEN;
+               key->conf.icv_len = IEEE80211_CCMP_256_MIC_LEN;
+               for (i = 0; seq && i < IEEE80211_NUM_TIDS + 1; i++)
+                       for (j = 0; j < IEEE80211_CCMP_256_PN_LEN; j++)
+                               key->u.ccmp.rx_pn[i][j] =
+                                       seq[IEEE80211_CCMP_256_PN_LEN - j - 1];
+               /* Initialize AES key state here as an optimization so that
+                * it does not need to be initialized for every packet.
+                */
+               key->u.ccmp.tfm = ieee80211_aes_key_setup_encrypt(
+                       key_data, key_len, IEEE80211_CCMP_256_MIC_LEN);
                if (IS_ERR(key->u.ccmp.tfm)) {
                        err = PTR_ERR(key->u.ccmp.tfm);
                        kfree(key);
@@ -390,8 +421,12 @@ ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
                }
                break;
        case WLAN_CIPHER_SUITE_AES_CMAC:
+       case WLAN_CIPHER_SUITE_BIP_CMAC_256:
                key->conf.iv_len = 0;
-               key->conf.icv_len = sizeof(struct ieee80211_mmie);
+               if (cipher == WLAN_CIPHER_SUITE_AES_CMAC)
+                       key->conf.icv_len = sizeof(struct ieee80211_mmie);
+               else
+                       key->conf.icv_len = sizeof(struct ieee80211_mmie_16);
                if (seq)
                        for (j = 0; j < IEEE80211_CMAC_PN_LEN; j++)
                                key->u.aes_cmac.rx_pn[j] =
@@ -401,13 +436,51 @@ ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
                 * it does not need to be initialized for every packet.
                 */
                key->u.aes_cmac.tfm =
-                       ieee80211_aes_cmac_key_setup(key_data);
+                       ieee80211_aes_cmac_key_setup(key_data, key_len);
                if (IS_ERR(key->u.aes_cmac.tfm)) {
                        err = PTR_ERR(key->u.aes_cmac.tfm);
                        kfree(key);
                        return ERR_PTR(err);
                }
                break;
+       case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+       case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+               key->conf.iv_len = 0;
+               key->conf.icv_len = sizeof(struct ieee80211_mmie_16);
+               if (seq)
+                       for (j = 0; j < IEEE80211_GMAC_PN_LEN; j++)
+                               key->u.aes_gmac.rx_pn[j] =
+                                       seq[IEEE80211_GMAC_PN_LEN - j - 1];
+               /* Initialize AES key state here as an optimization so that
+                * it does not need to be initialized for every packet.
+                */
+               key->u.aes_gmac.tfm =
+                       ieee80211_aes_gmac_key_setup(key_data, key_len);
+               if (IS_ERR(key->u.aes_gmac.tfm)) {
+                       err = PTR_ERR(key->u.aes_gmac.tfm);
+                       kfree(key);
+                       return ERR_PTR(err);
+               }
+               break;
+       case WLAN_CIPHER_SUITE_GCMP:
+       case WLAN_CIPHER_SUITE_GCMP_256:
+               key->conf.iv_len = IEEE80211_GCMP_HDR_LEN;
+               key->conf.icv_len = IEEE80211_GCMP_MIC_LEN;
+               for (i = 0; seq && i < IEEE80211_NUM_TIDS + 1; i++)
+                       for (j = 0; j < IEEE80211_GCMP_PN_LEN; j++)
+                               key->u.gcmp.rx_pn[i][j] =
+                                       seq[IEEE80211_GCMP_PN_LEN - j - 1];
+               /* Initialize AES key state here as an optimization so that
+                * it does not need to be initialized for every packet.
+                */
+               key->u.gcmp.tfm = ieee80211_aes_gcm_key_setup_encrypt(key_data,
+                                                                     key_len);
+               if (IS_ERR(key->u.gcmp.tfm)) {
+                       err = PTR_ERR(key->u.gcmp.tfm);
+                       kfree(key);
+                       return ERR_PTR(err);
+               }
+               break;
        default:
                if (cs) {
                        size_t len = (seq_len > MAX_PN_LEN) ?
@@ -429,10 +502,24 @@ ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
 
 static void ieee80211_key_free_common(struct ieee80211_key *key)
 {
-       if (key->conf.cipher == WLAN_CIPHER_SUITE_CCMP)
+       switch (key->conf.cipher) {
+       case WLAN_CIPHER_SUITE_CCMP:
+       case WLAN_CIPHER_SUITE_CCMP_256:
                ieee80211_aes_key_free(key->u.ccmp.tfm);
-       if (key->conf.cipher == WLAN_CIPHER_SUITE_AES_CMAC)
+               break;
+       case WLAN_CIPHER_SUITE_AES_CMAC:
+       case WLAN_CIPHER_SUITE_BIP_CMAC_256:
                ieee80211_aes_cmac_key_free(key->u.aes_cmac.tfm);
+               break;
+       case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+       case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+               ieee80211_aes_gmac_key_free(key->u.aes_gmac.tfm);
+               break;
+       case WLAN_CIPHER_SUITE_GCMP:
+       case WLAN_CIPHER_SUITE_GCMP_256:
+               ieee80211_aes_gcm_key_free(key->u.gcmp.tfm);
+               break;
+       }
        kzfree(key);
 }
 
@@ -739,6 +826,7 @@ void ieee80211_get_key_tx_seq(struct ieee80211_key_conf *keyconf,
                seq->tkip.iv16 = key->u.tkip.tx.iv16;
                break;
        case WLAN_CIPHER_SUITE_CCMP:
+       case WLAN_CIPHER_SUITE_CCMP_256:
                pn64 = atomic64_read(&key->u.ccmp.tx_pn);
                seq->ccmp.pn[5] = pn64;
                seq->ccmp.pn[4] = pn64 >> 8;
@@ -748,6 +836,7 @@ void ieee80211_get_key_tx_seq(struct ieee80211_key_conf *keyconf,
                seq->ccmp.pn[0] = pn64 >> 40;
                break;
        case WLAN_CIPHER_SUITE_AES_CMAC:
+       case WLAN_CIPHER_SUITE_BIP_CMAC_256:
                pn64 = atomic64_read(&key->u.aes_cmac.tx_pn);
                seq->ccmp.pn[5] = pn64;
                seq->ccmp.pn[4] = pn64 >> 8;
@@ -756,6 +845,26 @@ void ieee80211_get_key_tx_seq(struct ieee80211_key_conf *keyconf,
                seq->ccmp.pn[1] = pn64 >> 32;
                seq->ccmp.pn[0] = pn64 >> 40;
                break;
+       case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+       case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+               pn64 = atomic64_read(&key->u.aes_gmac.tx_pn);
+               seq->ccmp.pn[5] = pn64;
+               seq->ccmp.pn[4] = pn64 >> 8;
+               seq->ccmp.pn[3] = pn64 >> 16;
+               seq->ccmp.pn[2] = pn64 >> 24;
+               seq->ccmp.pn[1] = pn64 >> 32;
+               seq->ccmp.pn[0] = pn64 >> 40;
+               break;
+       case WLAN_CIPHER_SUITE_GCMP:
+       case WLAN_CIPHER_SUITE_GCMP_256:
+               pn64 = atomic64_read(&key->u.gcmp.tx_pn);
+               seq->gcmp.pn[5] = pn64;
+               seq->gcmp.pn[4] = pn64 >> 8;
+               seq->gcmp.pn[3] = pn64 >> 16;
+               seq->gcmp.pn[2] = pn64 >> 24;
+               seq->gcmp.pn[1] = pn64 >> 32;
+               seq->gcmp.pn[0] = pn64 >> 40;
+               break;
        default:
                WARN_ON(1);
        }
@@ -778,6 +887,7 @@ void ieee80211_get_key_rx_seq(struct ieee80211_key_conf *keyconf,
                seq->tkip.iv16 = key->u.tkip.rx[tid].iv16;
                break;
        case WLAN_CIPHER_SUITE_CCMP:
+       case WLAN_CIPHER_SUITE_CCMP_256:
                if (WARN_ON(tid < -1 || tid >= IEEE80211_NUM_TIDS))
                        return;
                if (tid < 0)
@@ -787,11 +897,29 @@ void ieee80211_get_key_rx_seq(struct ieee80211_key_conf *keyconf,
                memcpy(seq->ccmp.pn, pn, IEEE80211_CCMP_PN_LEN);
                break;
        case WLAN_CIPHER_SUITE_AES_CMAC:
+       case WLAN_CIPHER_SUITE_BIP_CMAC_256:
                if (WARN_ON(tid != 0))
                        return;
                pn = key->u.aes_cmac.rx_pn;
                memcpy(seq->aes_cmac.pn, pn, IEEE80211_CMAC_PN_LEN);
                break;
+       case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+       case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+               if (WARN_ON(tid != 0))
+                       return;
+               pn = key->u.aes_gmac.rx_pn;
+               memcpy(seq->aes_gmac.pn, pn, IEEE80211_GMAC_PN_LEN);
+               break;
+       case WLAN_CIPHER_SUITE_GCMP:
+       case WLAN_CIPHER_SUITE_GCMP_256:
+               if (WARN_ON(tid < -1 || tid >= IEEE80211_NUM_TIDS))
+                       return;
+               if (tid < 0)
+                       pn = key->u.gcmp.rx_pn[IEEE80211_NUM_TIDS];
+               else
+                       pn = key->u.gcmp.rx_pn[tid];
+               memcpy(seq->gcmp.pn, pn, IEEE80211_GCMP_PN_LEN);
+               break;
        }
 }
 EXPORT_SYMBOL(ieee80211_get_key_rx_seq);
@@ -810,6 +938,7 @@ void ieee80211_set_key_tx_seq(struct ieee80211_key_conf *keyconf,
                key->u.tkip.tx.iv16 = seq->tkip.iv16;
                break;
        case WLAN_CIPHER_SUITE_CCMP:
+       case WLAN_CIPHER_SUITE_CCMP_256:
                pn64 = (u64)seq->ccmp.pn[5] |
                       ((u64)seq->ccmp.pn[4] << 8) |
                       ((u64)seq->ccmp.pn[3] << 16) |
@@ -819,6 +948,7 @@ void ieee80211_set_key_tx_seq(struct ieee80211_key_conf *keyconf,
                atomic64_set(&key->u.ccmp.tx_pn, pn64);
                break;
        case WLAN_CIPHER_SUITE_AES_CMAC:
+       case WLAN_CIPHER_SUITE_BIP_CMAC_256:
                pn64 = (u64)seq->aes_cmac.pn[5] |
                       ((u64)seq->aes_cmac.pn[4] << 8) |
                       ((u64)seq->aes_cmac.pn[3] << 16) |
@@ -827,6 +957,26 @@ void ieee80211_set_key_tx_seq(struct ieee80211_key_conf *keyconf,
                       ((u64)seq->aes_cmac.pn[0] << 40);
                atomic64_set(&key->u.aes_cmac.tx_pn, pn64);
                break;
+       case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+       case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+               pn64 = (u64)seq->aes_gmac.pn[5] |
+                      ((u64)seq->aes_gmac.pn[4] << 8) |
+                      ((u64)seq->aes_gmac.pn[3] << 16) |
+                      ((u64)seq->aes_gmac.pn[2] << 24) |
+                      ((u64)seq->aes_gmac.pn[1] << 32) |
+                      ((u64)seq->aes_gmac.pn[0] << 40);
+               atomic64_set(&key->u.aes_gmac.tx_pn, pn64);
+               break;
+       case WLAN_CIPHER_SUITE_GCMP:
+       case WLAN_CIPHER_SUITE_GCMP_256:
+               pn64 = (u64)seq->gcmp.pn[5] |
+                      ((u64)seq->gcmp.pn[4] << 8) |
+                      ((u64)seq->gcmp.pn[3] << 16) |
+                      ((u64)seq->gcmp.pn[2] << 24) |
+                      ((u64)seq->gcmp.pn[1] << 32) |
+                      ((u64)seq->gcmp.pn[0] << 40);
+               atomic64_set(&key->u.gcmp.tx_pn, pn64);
+               break;
        default:
                WARN_ON(1);
                break;
@@ -850,6 +1000,7 @@ void ieee80211_set_key_rx_seq(struct ieee80211_key_conf *keyconf,
                key->u.tkip.rx[tid].iv16 = seq->tkip.iv16;
                break;
        case WLAN_CIPHER_SUITE_CCMP:
+       case WLAN_CIPHER_SUITE_CCMP_256:
                if (WARN_ON(tid < -1 || tid >= IEEE80211_NUM_TIDS))
                        return;
                if (tid < 0)
@@ -859,11 +1010,29 @@ void ieee80211_set_key_rx_seq(struct ieee80211_key_conf *keyconf,
                memcpy(pn, seq->ccmp.pn, IEEE80211_CCMP_PN_LEN);
                break;
        case WLAN_CIPHER_SUITE_AES_CMAC:
+       case WLAN_CIPHER_SUITE_BIP_CMAC_256:
                if (WARN_ON(tid != 0))
                        return;
                pn = key->u.aes_cmac.rx_pn;
                memcpy(pn, seq->aes_cmac.pn, IEEE80211_CMAC_PN_LEN);
                break;
+       case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+       case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+               if (WARN_ON(tid != 0))
+                       return;
+               pn = key->u.aes_gmac.rx_pn;
+               memcpy(pn, seq->aes_gmac.pn, IEEE80211_GMAC_PN_LEN);
+               break;
+       case WLAN_CIPHER_SUITE_GCMP:
+       case WLAN_CIPHER_SUITE_GCMP_256:
+               if (WARN_ON(tid < -1 || tid >= IEEE80211_NUM_TIDS))
+                       return;
+               if (tid < 0)
+                       pn = key->u.gcmp.rx_pn[IEEE80211_NUM_TIDS];
+               else
+                       pn = key->u.gcmp.rx_pn[tid];
+               memcpy(pn, seq->gcmp.pn, IEEE80211_GCMP_PN_LEN);
+               break;
        default:
                WARN_ON(1);
                break;
index 19db68663d7555461768eeae45a0afad8b0b163b..d57a9915494f94eb44bfbf3ed609286d8eb9907f 100644 (file)
@@ -94,6 +94,24 @@ struct ieee80211_key {
                        u32 replays; /* dot11RSNAStatsCMACReplays */
                        u32 icverrors; /* dot11RSNAStatsCMACICVErrors */
                } aes_cmac;
+               struct {
+                       atomic64_t tx_pn;
+                       u8 rx_pn[IEEE80211_GMAC_PN_LEN];
+                       struct crypto_aead *tfm;
+                       u32 replays; /* dot11RSNAStatsCMACReplays */
+                       u32 icverrors; /* dot11RSNAStatsCMACICVErrors */
+               } aes_gmac;
+               struct {
+                       atomic64_t tx_pn;
+                       /* Last received packet number. The first
+                        * IEEE80211_NUM_TIDS counters are used with Data
+                        * frames and the last counter is used with Robust
+                        * Management frames.
+                        */
+                       u8 rx_pn[IEEE80211_NUM_TIDS + 1][IEEE80211_GCMP_PN_LEN];
+                       struct crypto_aead *tfm;
+                       u32 replays; /* dot11RSNAStatsGCMPReplays */
+               } gcmp;
                struct {
                        /* generic cipher scheme */
                        u8 rx_pn[IEEE80211_NUM_TIDS + 1][MAX_PN_LEN];
index d9ce33663c736f732b18906f90aba0c82c1c25c5..5e09d354c5a52f25a373740cbd54b8dddaf841df 100644 (file)
@@ -658,7 +658,6 @@ static int ieee80211_init_cipher_suites(struct ieee80211_local *local)
        bool have_wep = !(IS_ERR(local->wep_tx_tfm) ||
                          IS_ERR(local->wep_rx_tfm));
        bool have_mfp = local->hw.flags & IEEE80211_HW_MFP_CAPABLE;
-       const struct ieee80211_cipher_scheme *cs = local->hw.cipher_schemes;
        int n_suites = 0, r = 0, w = 0;
        u32 *suites;
        static const u32 cipher_suites[] = {
@@ -667,79 +666,109 @@ static int ieee80211_init_cipher_suites(struct ieee80211_local *local)
                WLAN_CIPHER_SUITE_WEP104,
                WLAN_CIPHER_SUITE_TKIP,
                WLAN_CIPHER_SUITE_CCMP,
+               WLAN_CIPHER_SUITE_CCMP_256,
+               WLAN_CIPHER_SUITE_GCMP,
+               WLAN_CIPHER_SUITE_GCMP_256,
 
                /* keep last -- depends on hw flags! */
-               WLAN_CIPHER_SUITE_AES_CMAC
+               WLAN_CIPHER_SUITE_AES_CMAC,
+               WLAN_CIPHER_SUITE_BIP_CMAC_256,
+               WLAN_CIPHER_SUITE_BIP_GMAC_128,
+               WLAN_CIPHER_SUITE_BIP_GMAC_256,
        };
 
-       /* Driver specifies the ciphers, we have nothing to do... */
-       if (local->hw.wiphy->cipher_suites && have_wep)
-               return 0;
+       if (local->hw.flags & IEEE80211_HW_SW_CRYPTO_CONTROL ||
+           local->hw.wiphy->cipher_suites) {
+               /* If the driver advertises, or doesn't support SW crypto,
+                * we only need to remove WEP if necessary.
+                */
+               if (have_wep)
+                       return 0;
+
+               /* well if it has _no_ ciphers ... fine */
+               if (!local->hw.wiphy->n_cipher_suites)
+                       return 0;
+
+               /* Driver provides cipher suites, but we need to exclude 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];
 
-       /* Set up cipher suites if driver relies on mac80211 cipher defs */
-       if (!local->hw.wiphy->cipher_suites && !cs) {
+                       if (suite == WLAN_CIPHER_SUITE_WEP40 ||
+                           suite == WLAN_CIPHER_SUITE_WEP104)
+                               continue;
+                       suites[w++] = suite;
+               }
+       } else if (!local->hw.cipher_schemes) {
+               /* If the driver doesn't have cipher schemes, there's nothing
+                * else to do other than assign the (software supported and
+                * perhaps offloaded) cipher suites.
+                */
                local->hw.wiphy->cipher_suites = cipher_suites;
                local->hw.wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
 
                if (!have_mfp)
-                       local->hw.wiphy->n_cipher_suites--;
+                       local->hw.wiphy->n_cipher_suites -= 4;
 
                if (!have_wep) {
                        local->hw.wiphy->cipher_suites += 2;
                        local->hw.wiphy->n_cipher_suites -= 2;
                }
 
+               /* not dynamically allocated, so just return */
                return 0;
-       }
+       } else {
+               const struct ieee80211_cipher_scheme *cs;
 
-       if (!local->hw.wiphy->cipher_suites) {
-               /*
-                * Driver specifies cipher schemes only
-                * We start counting ciphers defined by schemes, TKIP and CCMP
+               cs = local->hw.cipher_schemes;
+
+               /* Driver specifies cipher schemes only (but not cipher suites
+                * including the schemes)
+                *
+                * We start counting ciphers defined by schemes, TKIP, CCMP,
+                * CCMP-256, GCMP, and GCMP-256
                 */
-               n_suites = local->hw.n_cipher_schemes + 2;
+               n_suites = local->hw.n_cipher_schemes + 5;
 
                /* check if we have WEP40 and WEP104 */
                if (have_wep)
                        n_suites += 2;
 
-               /* check if we have AES_CMAC */
+               /* check if we have AES_CMAC, BIP-CMAC-256, BIP-GMAC-128,
+                * BIP-GMAC-256
+                */
                if (have_mfp)
-                       n_suites++;
+                       n_suites += 4;
 
                suites = kmalloc(sizeof(u32) * n_suites, GFP_KERNEL);
                if (!suites)
                        return -ENOMEM;
 
                suites[w++] = WLAN_CIPHER_SUITE_CCMP;
+               suites[w++] = WLAN_CIPHER_SUITE_CCMP_256;
                suites[w++] = WLAN_CIPHER_SUITE_TKIP;
+               suites[w++] = WLAN_CIPHER_SUITE_GCMP;
+               suites[w++] = WLAN_CIPHER_SUITE_GCMP_256;
 
                if (have_wep) {
                        suites[w++] = WLAN_CIPHER_SUITE_WEP40;
                        suites[w++] = WLAN_CIPHER_SUITE_WEP104;
                }
 
-               if (have_mfp)
+               if (have_mfp) {
                        suites[w++] = WLAN_CIPHER_SUITE_AES_CMAC;
+                       suites[w++] = WLAN_CIPHER_SUITE_BIP_CMAC_256;
+                       suites[w++] = WLAN_CIPHER_SUITE_BIP_GMAC_128;
+                       suites[w++] = WLAN_CIPHER_SUITE_BIP_GMAC_256;
+               }
 
                for (r = 0; r < local->hw.n_cipher_schemes; r++)
                        suites[w++] = cs[r].cipher;
-       } else {
-               /* Driver provides cipher suites, but we need to exclude 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;
@@ -1041,10 +1070,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
                ieee80211_max_network_latency;
        result = pm_qos_add_notifier(PM_QOS_NETWORK_LATENCY,
                                     &local->network_latency_notifier);
-       if (result) {
-               rtnl_lock();
+       if (result)
                goto fail_pm_qos;
-       }
 
 #ifdef CONFIG_INET
        local->ifa_notifier.notifier_call = ieee80211_ifa_changed;
@@ -1072,15 +1099,15 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
  fail_ifa:
        pm_qos_remove_notifier(PM_QOS_NETWORK_LATENCY,
                               &local->network_latency_notifier);
-       rtnl_lock();
 #endif
  fail_pm_qos:
-       ieee80211_led_exit(local);
+       rtnl_lock();
+       rate_control_deinitialize(local);
        ieee80211_remove_interfaces(local);
  fail_rate:
        rtnl_unlock();
+       ieee80211_led_exit(local);
        ieee80211_wep_free(local);
-       sta_info_stop(local);
        destroy_workqueue(local->workqueue);
  fail_workqueue:
        wiphy_unregister(local->hw.wiphy);
@@ -1176,6 +1203,8 @@ void ieee80211_free_hw(struct ieee80211_hw *hw)
 
        kfree(rcu_access_pointer(local->tx_latency));
 
+       sta_info_stop(local);
+
        wiphy_free(local->hw.wiphy);
 }
 EXPORT_SYMBOL(ieee80211_free_hw);
index fa94ca15ba95ba5cefac929a3031541f6a606d1f..b488e1859b18e8ed7797cffbb5ab2319138fdb28 100644 (file)
@@ -523,13 +523,6 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata,
            sdata->u.mesh.mshcfg.auto_open_plinks &&
            rssi_threshold_check(sdata, sta))
                changed = mesh_plink_open(sta);
-       else if (sta->plink_state == NL80211_PLINK_LISTEN &&
-                (sdata->u.mesh.user_mpm ||
-                 sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED))
-               cfg80211_notify_new_peer_candidate(sdata->dev, hw_addr,
-                                                  elems->ie_start,
-                                                  elems->total_len,
-                                                  GFP_ATOMIC);
 
        ieee80211_mps_frame_release(sta, elems);
 out:
index c1460e635c7fad42951494de88a0c396d5bbc98c..10ac6324c1d014c708749748ce89ef31055561cf 100644 (file)
@@ -2011,6 +2011,9 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        /* disable per-vif ps */
        ieee80211_recalc_ps_vif(sdata);
 
+       /* make sure ongoing transmission finishes */
+       synchronize_net();
+
        /*
         * drop any frame before deauth/disassoc, this can be data or
         * management frame. Since we are disconnecting, we should not
index 9491e8689a9d4fb0cd230b6b27da9000942dce43..1101563357eae365f1e1a1df926ecf36fdc0570b 100644 (file)
@@ -647,6 +647,7 @@ static int ieee80211_get_mmie_keyidx(struct sk_buff *skb)
 {
        struct ieee80211_mgmt *hdr = (struct ieee80211_mgmt *) skb->data;
        struct ieee80211_mmie *mmie;
+       struct ieee80211_mmie_16 *mmie16;
 
        if (skb->len < 24 + sizeof(*mmie) || !is_multicast_ether_addr(hdr->da))
                return -1;
@@ -656,11 +657,18 @@ static int ieee80211_get_mmie_keyidx(struct sk_buff *skb)
 
        mmie = (struct ieee80211_mmie *)
                (skb->data + skb->len - sizeof(*mmie));
-       if (mmie->element_id != WLAN_EID_MMIE ||
-           mmie->length != sizeof(*mmie) - 2)
-               return -1;
-
-       return le16_to_cpu(mmie->key_id);
+       if (mmie->element_id == WLAN_EID_MMIE &&
+           mmie->length == sizeof(*mmie) - 2)
+               return le16_to_cpu(mmie->key_id);
+
+       mmie16 = (struct ieee80211_mmie_16 *)
+               (skb->data + skb->len - sizeof(*mmie16));
+       if (skb->len >= 24 + sizeof(*mmie16) &&
+           mmie16->element_id == WLAN_EID_MMIE &&
+           mmie16->length == sizeof(*mmie16) - 2)
+               return le16_to_cpu(mmie16->key_id);
+
+       return -1;
 }
 
 static int iwl80211_get_cs_keyid(const struct ieee80211_cipher_scheme *cs,
@@ -1650,11 +1658,27 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
                result = ieee80211_crypto_tkip_decrypt(rx);
                break;
        case WLAN_CIPHER_SUITE_CCMP:
-               result = ieee80211_crypto_ccmp_decrypt(rx);
+               result = ieee80211_crypto_ccmp_decrypt(
+                       rx, IEEE80211_CCMP_MIC_LEN);
+               break;
+       case WLAN_CIPHER_SUITE_CCMP_256:
+               result = ieee80211_crypto_ccmp_decrypt(
+                       rx, IEEE80211_CCMP_256_MIC_LEN);
                break;
        case WLAN_CIPHER_SUITE_AES_CMAC:
                result = ieee80211_crypto_aes_cmac_decrypt(rx);
                break;
+       case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+               result = ieee80211_crypto_aes_cmac_256_decrypt(rx);
+               break;
+       case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+       case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+               result = ieee80211_crypto_aes_gmac_decrypt(rx);
+               break;
+       case WLAN_CIPHER_SUITE_GCMP:
+       case WLAN_CIPHER_SUITE_GCMP_256:
+               result = ieee80211_crypto_gcmp_decrypt(rx);
+               break;
        default:
                result = ieee80211_crypto_hw_decrypt(rx);
        }
@@ -1781,7 +1805,9 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
                /* This is the first fragment of a new frame. */
                entry = ieee80211_reassemble_add(rx->sdata, frag, seq,
                                                 rx->seqno_idx, &(rx->skb));
-               if (rx->key && rx->key->conf.cipher == WLAN_CIPHER_SUITE_CCMP &&
+               if (rx->key &&
+                   (rx->key->conf.cipher == WLAN_CIPHER_SUITE_CCMP ||
+                    rx->key->conf.cipher == WLAN_CIPHER_SUITE_CCMP_256) &&
                    ieee80211_has_protected(fc)) {
                        int queue = rx->security_idx;
                        /* Store CCMP PN so that we can verify that the next
@@ -1810,7 +1836,9 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
                int i;
                u8 pn[IEEE80211_CCMP_PN_LEN], *rpn;
                int queue;
-               if (!rx->key || rx->key->conf.cipher != WLAN_CIPHER_SUITE_CCMP)
+               if (!rx->key ||
+                   (rx->key->conf.cipher != WLAN_CIPHER_SUITE_CCMP &&
+                    rx->key->conf.cipher != WLAN_CIPHER_SUITE_CCMP_256))
                        return RX_DROP_UNUSABLE;
                memcpy(pn, entry->last_pn, IEEE80211_CCMP_PN_LEN);
                for (i = IEEE80211_CCMP_PN_LEN - 1; i >= 0; i--) {
@@ -2310,12 +2338,12 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
                return RX_DROP_MONITOR;
 
        if (rx->sta) {
-               /* The security index has the same property as needed
+               /* The seqno index has the same property as needed
                 * for the rx_msdu field, i.e. it is IEEE80211_NUM_TIDS
                 * for non-QoS-data frames. Here we know it's a data
                 * frame, so count MSDUs.
                 */
-               rx->sta->rx_msdu[rx->security_idx]++;
+               rx->sta->rx_msdu[rx->seqno_idx]++;
        }
 
        /*
index 7807fa42ed3f4fcd8fc6b066cbf89cd987e1e331..05f0d711b6d8666701e91262141fb67711d9dad7 100644 (file)
@@ -828,6 +828,11 @@ void ieee80211_scan_work(struct work_struct *work)
 
        mutex_lock(&local->mtx);
 
+       if (!ieee80211_can_run_worker(local)) {
+               aborted = true;
+               goto out_complete;
+       }
+
        sdata = rcu_dereference_protected(local->scan_sdata,
                                          lockdep_is_held(&local->mtx));
        scan_req = rcu_dereference_protected(local->scan_req,
index 79383ef0c26405eeff3dff62e48ffe364ff16646..00ca8dcc2bcf2d924fb24ed0d4ee674295086aff 100644 (file)
@@ -1764,6 +1764,13 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
 
        sinfo->generation = sdata->local->sta_generation;
 
+       /* do before driver, so beacon filtering drivers have a
+        * chance to e.g. just add the number of filtered beacons
+        * (or just modify the value entirely, of course)
+        */
+       if (sdata->vif.type == NL80211_IFTYPE_STATION)
+               sinfo->rx_beacon = sdata->u.mgd.count_beacon_signal;
+
        drv_sta_statistics(local, sdata, &sta->sta, sinfo);
 
        sinfo->filled |= BIT(NL80211_STA_INFO_INACTIVE_TIME) |
@@ -1816,6 +1823,13 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
        sinfo->rx_dropped_misc = sta->rx_dropped;
        sinfo->beacon_loss_count = sta->beacon_loss_count;
 
+       if (sdata->vif.type == NL80211_IFTYPE_STATION &&
+           !(sdata->vif.driver_flags & IEEE80211_VIF_BEACON_FILTER)) {
+               sinfo->filled |= BIT(NL80211_STA_INFO_BEACON_RX) |
+                                BIT(NL80211_STA_INFO_BEACON_SIGNAL_AVG);
+               sinfo->rx_beacon_signal_avg = ieee80211_ave_rssi(&sdata->vif);
+       }
+
        if ((sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) ||
            (sta->local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)) {
                if (!(sinfo->filled & BIT(NL80211_STA_INFO_SIGNAL))) {
index 917088dfd69659de6614b327cc0ee23864e429cd..c9f9752217ac8230056e90e28a9b0b02883a87d5 100644 (file)
@@ -345,24 +345,24 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
         */
        sband = local->hw.wiphy->bands[band];
        memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
-       if ((action_code == WLAN_TDLS_SETUP_REQUEST ||
-            action_code == WLAN_TDLS_SETUP_RESPONSE) &&
-           ht_cap.ht_supported && (!sta || sta->sta.ht_cap.ht_supported)) {
-               if (action_code == WLAN_TDLS_SETUP_REQUEST) {
-                       ieee80211_apply_htcap_overrides(sdata, &ht_cap);
-
-                       /* disable SMPS in TDLS initiator */
-                       ht_cap.cap |= (WLAN_HT_CAP_SM_PS_DISABLED
-                                      << IEEE80211_HT_CAP_SM_PS_SHIFT);
-               } else {
-                       /* disable SMPS in TDLS responder */
-                       sta->sta.ht_cap.cap |=
-                               (WLAN_HT_CAP_SM_PS_DISABLED
-                                << IEEE80211_HT_CAP_SM_PS_SHIFT);
-
-                       /* the peer caps are already intersected with our own */
-                       memcpy(&ht_cap, &sta->sta.ht_cap, sizeof(ht_cap));
-               }
+
+       if (action_code == WLAN_TDLS_SETUP_REQUEST && ht_cap.ht_supported) {
+               ieee80211_apply_htcap_overrides(sdata, &ht_cap);
+
+               /* disable SMPS in TDLS initiator */
+               ht_cap.cap |= WLAN_HT_CAP_SM_PS_DISABLED
+                               << IEEE80211_HT_CAP_SM_PS_SHIFT;
+
+               pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
+               ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap);
+       } else if (action_code == WLAN_TDLS_SETUP_RESPONSE &&
+                  ht_cap.ht_supported && sta->sta.ht_cap.ht_supported) {
+               /* disable SMPS in TDLS responder */
+               sta->sta.ht_cap.cap |= WLAN_HT_CAP_SM_PS_DISABLED
+                                       << IEEE80211_HT_CAP_SM_PS_SHIFT;
+
+               /* the peer caps are already intersected with our own */
+               memcpy(&ht_cap, &sta->sta.ht_cap, sizeof(ht_cap));
 
                pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
                ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap);
@@ -852,7 +852,6 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
         */
        if ((action_code == WLAN_TDLS_TEARDOWN) &&
            (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) {
-               struct sta_info *sta = NULL;
                bool try_resend; /* Should we keep skb for possible resend */
 
                /* If not sending directly to peer - no point in keeping skb */
index 02ed6f60629a5aa123513f759104ca57513d0f5f..88a18ffe2975520edbcc80733bc1bbc9b2655f11 100644 (file)
@@ -626,6 +626,9 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
                                tx->key = NULL;
                        break;
                case WLAN_CIPHER_SUITE_CCMP:
+               case WLAN_CIPHER_SUITE_CCMP_256:
+               case WLAN_CIPHER_SUITE_GCMP:
+               case WLAN_CIPHER_SUITE_GCMP_256:
                        if (!ieee80211_is_data_present(hdr->frame_control) &&
                            !ieee80211_use_mfp(hdr->frame_control, tx->sta,
                                               tx->skb))
@@ -636,6 +639,9 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
                                        ieee80211_is_mgmt(hdr->frame_control);
                        break;
                case WLAN_CIPHER_SUITE_AES_CMAC:
+               case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+               case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+               case WLAN_CIPHER_SUITE_BIP_GMAC_256:
                        if (!ieee80211_is_mgmt(hdr->frame_control))
                                tx->key = NULL;
                        break;
@@ -1011,9 +1017,21 @@ ieee80211_tx_h_encrypt(struct ieee80211_tx_data *tx)
        case WLAN_CIPHER_SUITE_TKIP:
                return ieee80211_crypto_tkip_encrypt(tx);
        case WLAN_CIPHER_SUITE_CCMP:
-               return ieee80211_crypto_ccmp_encrypt(tx);
+               return ieee80211_crypto_ccmp_encrypt(
+                       tx, IEEE80211_CCMP_MIC_LEN);
+       case WLAN_CIPHER_SUITE_CCMP_256:
+               return ieee80211_crypto_ccmp_encrypt(
+                       tx, IEEE80211_CCMP_256_MIC_LEN);
        case WLAN_CIPHER_SUITE_AES_CMAC:
                return ieee80211_crypto_aes_cmac_encrypt(tx);
+       case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+               return ieee80211_crypto_aes_cmac_256_encrypt(tx);
+       case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+       case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+               return ieee80211_crypto_aes_gmac_encrypt(tx);
+       case WLAN_CIPHER_SUITE_GCMP:
+       case WLAN_CIPHER_SUITE_GCMP_256:
+               return ieee80211_crypto_gcmp_encrypt(tx);
        default:
                return ieee80211_crypto_hw_encrypt(tx);
        }
index fbd37d43dfceb31ccf57da5231930560eeff6b09..8428f4a954795657a32a24f77a0f9c9ae6591b7e 100644 (file)
@@ -744,16 +744,19 @@ EXPORT_SYMBOL_GPL(wdev_to_ieee80211_vif);
 
 /*
  * Nothing should have been stuffed into the workqueue during
- * the suspend->resume cycle. If this WARN is seen then there
- * is a bug with either the driver suspend or something in
- * mac80211 stuffing into the workqueue which we haven't yet
- * cleared during mac80211's suspend cycle.
+ * the suspend->resume cycle. Since we can't check each caller
+ * of this function if we are already quiescing / suspended,
+ * check here and don't WARN since this can actually happen when
+ * the rx path (for example) is racing against __ieee80211_suspend
+ * and suspending / quiescing was set after the rx path checked
+ * them.
  */
 static bool ieee80211_can_queue_work(struct ieee80211_local *local)
 {
-       if (WARN(local->suspended && !local->resuming,
-                "queueing ieee80211 work while going to suspend\n"))
+       if (local->quiescing || (local->suspended && !local->resuming)) {
+               pr_warn("queueing ieee80211 work while going to suspend\n");
                return false;
+       }
 
        return true;
 }
@@ -2057,6 +2060,18 @@ int ieee80211_reconfig(struct ieee80211_local *local)
        mb();
        local->resuming = false;
 
+       /* It's possible that we don't handle the scan completion in
+        * time during suspend, so if it's still marked as completed
+        * here, queue the work and flush it to clean things up.
+        * Instead of calling the worker function directly here, we
+        * really queue it to avoid potential races with other flows
+        * scheduling the same work.
+        */
+       if (test_bit(SCAN_COMPLETED, &local->scanning)) {
+               ieee80211_queue_delayed_work(&local->hw, &local->scan_work, 0);
+               flush_delayed_work(&local->scan_work);
+       }
+
        if (local->open_count && !reconfig_due_to_wowlan)
                drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_SUSPEND);
 
index 12398fde02e87e7c7eb0eba1430e72287c6bb6f8..75de6fac40d1533fbb9836ab138d71e838ffb6af 100644 (file)
@@ -22,6 +22,8 @@
 #include "tkip.h"
 #include "aes_ccm.h"
 #include "aes_cmac.h"
+#include "aes_gmac.h"
+#include "aes_gcm.h"
 #include "wpa.h"
 
 ieee80211_tx_result
@@ -393,7 +395,8 @@ static inline void ccmp_hdr2pn(u8 *pn, u8 *hdr)
 }
 
 
-static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
+static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb,
+                           unsigned int mic_len)
 {
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
        struct ieee80211_key *key = tx->key;
@@ -424,7 +427,7 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
        if (info->control.hw_key)
                tail = 0;
        else
-               tail = IEEE80211_CCMP_MIC_LEN;
+               tail = mic_len;
 
        if (WARN_ON(skb_tailroom(skb) < tail ||
                    skb_headroom(skb) < IEEE80211_CCMP_HDR_LEN))
@@ -459,21 +462,22 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
        pos += IEEE80211_CCMP_HDR_LEN;
        ccmp_special_blocks(skb, pn, b_0, aad);
        ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, b_0, aad, pos, len,
-                                 skb_put(skb, IEEE80211_CCMP_MIC_LEN));
+                                 skb_put(skb, mic_len), mic_len);
 
        return 0;
 }
 
 
 ieee80211_tx_result
-ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data *tx)
+ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data *tx,
+                             unsigned int mic_len)
 {
        struct sk_buff *skb;
 
        ieee80211_tx_set_protected(tx);
 
        skb_queue_walk(&tx->skbs, skb) {
-               if (ccmp_encrypt_skb(tx, skb) < 0)
+               if (ccmp_encrypt_skb(tx, skb, mic_len) < 0)
                        return TX_DROP;
        }
 
@@ -482,7 +486,8 @@ ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data *tx)
 
 
 ieee80211_rx_result
-ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
+ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx,
+                             unsigned int mic_len)
 {
        struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
        int hdrlen;
@@ -499,8 +504,7 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
            !ieee80211_is_robust_mgmt_frame(skb))
                return RX_CONTINUE;
 
-       data_len = skb->len - hdrlen - IEEE80211_CCMP_HDR_LEN -
-                  IEEE80211_CCMP_MIC_LEN;
+       data_len = skb->len - hdrlen - IEEE80211_CCMP_HDR_LEN - mic_len;
        if (!rx->sta || data_len < 0)
                return RX_DROP_UNUSABLE;
 
@@ -531,14 +535,14 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
                            key->u.ccmp.tfm, b_0, aad,
                            skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN,
                            data_len,
-                           skb->data + skb->len - IEEE80211_CCMP_MIC_LEN))
+                           skb->data + skb->len - mic_len, mic_len))
                        return RX_DROP_UNUSABLE;
        }
 
        memcpy(key->u.ccmp.rx_pn[queue], pn, IEEE80211_CCMP_PN_LEN);
 
        /* Remove CCMP header and MIC */
-       if (pskb_trim(skb, skb->len - IEEE80211_CCMP_MIC_LEN))
+       if (pskb_trim(skb, skb->len - mic_len))
                return RX_DROP_UNUSABLE;
        memmove(skb->data + IEEE80211_CCMP_HDR_LEN, skb->data, hdrlen);
        skb_pull(skb, IEEE80211_CCMP_HDR_LEN);
@@ -546,6 +550,229 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
        return RX_CONTINUE;
 }
 
+static void gcmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *j_0, u8 *aad)
+{
+       __le16 mask_fc;
+       u8 qos_tid;
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+
+       memcpy(j_0, hdr->addr2, ETH_ALEN);
+       memcpy(&j_0[ETH_ALEN], pn, IEEE80211_GCMP_PN_LEN);
+       j_0[13] = 0;
+       j_0[14] = 0;
+       j_0[AES_BLOCK_SIZE - 1] = 0x01;
+
+       /* AAD (extra authenticate-only data) / masked 802.11 header
+        * FC | A1 | A2 | A3 | SC | [A4] | [QC]
+        */
+       put_unaligned_be16(ieee80211_hdrlen(hdr->frame_control) - 2, &aad[0]);
+       /* Mask FC: zero subtype b4 b5 b6 (if not mgmt)
+        * Retry, PwrMgt, MoreData; set Protected
+        */
+       mask_fc = hdr->frame_control;
+       mask_fc &= ~cpu_to_le16(IEEE80211_FCTL_RETRY |
+                               IEEE80211_FCTL_PM | IEEE80211_FCTL_MOREDATA);
+       if (!ieee80211_is_mgmt(hdr->frame_control))
+               mask_fc &= ~cpu_to_le16(0x0070);
+       mask_fc |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+
+       put_unaligned(mask_fc, (__le16 *)&aad[2]);
+       memcpy(&aad[4], &hdr->addr1, 3 * ETH_ALEN);
+
+       /* Mask Seq#, leave Frag# */
+       aad[22] = *((u8 *)&hdr->seq_ctrl) & 0x0f;
+       aad[23] = 0;
+
+       if (ieee80211_is_data_qos(hdr->frame_control))
+               qos_tid = *ieee80211_get_qos_ctl(hdr) &
+                       IEEE80211_QOS_CTL_TID_MASK;
+       else
+               qos_tid = 0;
+
+       if (ieee80211_has_a4(hdr->frame_control)) {
+               memcpy(&aad[24], hdr->addr4, ETH_ALEN);
+               aad[30] = qos_tid;
+               aad[31] = 0;
+       } else {
+               memset(&aad[24], 0, ETH_ALEN + IEEE80211_QOS_CTL_LEN);
+               aad[24] = qos_tid;
+       }
+}
+
+static inline void gcmp_pn2hdr(u8 *hdr, const u8 *pn, int key_id)
+{
+       hdr[0] = pn[5];
+       hdr[1] = pn[4];
+       hdr[2] = 0;
+       hdr[3] = 0x20 | (key_id << 6);
+       hdr[4] = pn[3];
+       hdr[5] = pn[2];
+       hdr[6] = pn[1];
+       hdr[7] = pn[0];
+}
+
+static inline void gcmp_hdr2pn(u8 *pn, const u8 *hdr)
+{
+       pn[0] = hdr[7];
+       pn[1] = hdr[6];
+       pn[2] = hdr[5];
+       pn[3] = hdr[4];
+       pn[4] = hdr[1];
+       pn[5] = hdr[0];
+}
+
+static int gcmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+       struct ieee80211_key *key = tx->key;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+       int hdrlen, len, tail;
+       u8 *pos;
+       u8 pn[6];
+       u64 pn64;
+       u8 aad[2 * AES_BLOCK_SIZE];
+       u8 j_0[AES_BLOCK_SIZE];
+
+       if (info->control.hw_key &&
+           !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) &&
+           !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE) &&
+           !((info->control.hw_key->flags &
+              IEEE80211_KEY_FLAG_GENERATE_IV_MGMT) &&
+             ieee80211_is_mgmt(hdr->frame_control))) {
+               /* hwaccel has no need for preallocated room for GCMP
+                * header or MIC fields
+                */
+               return 0;
+       }
+
+       hdrlen = ieee80211_hdrlen(hdr->frame_control);
+       len = skb->len - hdrlen;
+
+       if (info->control.hw_key)
+               tail = 0;
+       else
+               tail = IEEE80211_GCMP_MIC_LEN;
+
+       if (WARN_ON(skb_tailroom(skb) < tail ||
+                   skb_headroom(skb) < IEEE80211_GCMP_HDR_LEN))
+               return -1;
+
+       pos = skb_push(skb, IEEE80211_GCMP_HDR_LEN);
+       memmove(pos, pos + IEEE80211_GCMP_HDR_LEN, hdrlen);
+       skb_set_network_header(skb, skb_network_offset(skb) +
+                                   IEEE80211_GCMP_HDR_LEN);
+
+       /* the HW only needs room for the IV, but not the actual IV */
+       if (info->control.hw_key &&
+           (info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE))
+               return 0;
+
+       hdr = (struct ieee80211_hdr *)pos;
+       pos += hdrlen;
+
+       pn64 = atomic64_inc_return(&key->u.gcmp.tx_pn);
+
+       pn[5] = pn64;
+       pn[4] = pn64 >> 8;
+       pn[3] = pn64 >> 16;
+       pn[2] = pn64 >> 24;
+       pn[1] = pn64 >> 32;
+       pn[0] = pn64 >> 40;
+
+       gcmp_pn2hdr(pos, pn, key->conf.keyidx);
+
+       /* hwaccel - with software GCMP header */
+       if (info->control.hw_key)
+               return 0;
+
+       pos += IEEE80211_GCMP_HDR_LEN;
+       gcmp_special_blocks(skb, pn, j_0, aad);
+       ieee80211_aes_gcm_encrypt(key->u.gcmp.tfm, j_0, aad, pos, len,
+                                 skb_put(skb, IEEE80211_GCMP_MIC_LEN));
+
+       return 0;
+}
+
+ieee80211_tx_result
+ieee80211_crypto_gcmp_encrypt(struct ieee80211_tx_data *tx)
+{
+       struct sk_buff *skb;
+
+       ieee80211_tx_set_protected(tx);
+
+       skb_queue_walk(&tx->skbs, skb) {
+               if (gcmp_encrypt_skb(tx, skb) < 0)
+                       return TX_DROP;
+       }
+
+       return TX_CONTINUE;
+}
+
+ieee80211_rx_result
+ieee80211_crypto_gcmp_decrypt(struct ieee80211_rx_data *rx)
+{
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
+       int hdrlen;
+       struct ieee80211_key *key = rx->key;
+       struct sk_buff *skb = rx->skb;
+       struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+       u8 pn[IEEE80211_GCMP_PN_LEN];
+       int data_len;
+       int queue;
+
+       hdrlen = ieee80211_hdrlen(hdr->frame_control);
+
+       if (!ieee80211_is_data(hdr->frame_control) &&
+           !ieee80211_is_robust_mgmt_frame(skb))
+               return RX_CONTINUE;
+
+       data_len = skb->len - hdrlen - IEEE80211_GCMP_HDR_LEN -
+                  IEEE80211_GCMP_MIC_LEN;
+       if (!rx->sta || data_len < 0)
+               return RX_DROP_UNUSABLE;
+
+       if (status->flag & RX_FLAG_DECRYPTED) {
+               if (!pskb_may_pull(rx->skb, hdrlen + IEEE80211_GCMP_HDR_LEN))
+                       return RX_DROP_UNUSABLE;
+       } else {
+               if (skb_linearize(rx->skb))
+                       return RX_DROP_UNUSABLE;
+       }
+
+       gcmp_hdr2pn(pn, skb->data + hdrlen);
+
+       queue = rx->security_idx;
+
+       if (memcmp(pn, key->u.gcmp.rx_pn[queue], IEEE80211_GCMP_PN_LEN) <= 0) {
+               key->u.gcmp.replays++;
+               return RX_DROP_UNUSABLE;
+       }
+
+       if (!(status->flag & RX_FLAG_DECRYPTED)) {
+               u8 aad[2 * AES_BLOCK_SIZE];
+               u8 j_0[AES_BLOCK_SIZE];
+               /* hardware didn't decrypt/verify MIC */
+               gcmp_special_blocks(skb, pn, j_0, aad);
+
+               if (ieee80211_aes_gcm_decrypt(
+                           key->u.gcmp.tfm, j_0, aad,
+                           skb->data + hdrlen + IEEE80211_GCMP_HDR_LEN,
+                           data_len,
+                           skb->data + skb->len - IEEE80211_GCMP_MIC_LEN))
+                       return RX_DROP_UNUSABLE;
+       }
+
+       memcpy(key->u.gcmp.rx_pn[queue], pn, IEEE80211_GCMP_PN_LEN);
+
+       /* Remove GCMP header and MIC */
+       if (pskb_trim(skb, skb->len - IEEE80211_GCMP_MIC_LEN))
+               return RX_DROP_UNUSABLE;
+       memmove(skb->data + IEEE80211_GCMP_HDR_LEN, skb->data, hdrlen);
+       skb_pull(skb, IEEE80211_GCMP_HDR_LEN);
+
+       return RX_CONTINUE;
+}
+
 static ieee80211_tx_result
 ieee80211_crypto_cs_encrypt(struct ieee80211_tx_data *tx,
                            struct sk_buff *skb)
@@ -729,6 +956,48 @@ ieee80211_crypto_aes_cmac_encrypt(struct ieee80211_tx_data *tx)
        return TX_CONTINUE;
 }
 
+ieee80211_tx_result
+ieee80211_crypto_aes_cmac_256_encrypt(struct ieee80211_tx_data *tx)
+{
+       struct sk_buff *skb;
+       struct ieee80211_tx_info *info;
+       struct ieee80211_key *key = tx->key;
+       struct ieee80211_mmie_16 *mmie;
+       u8 aad[20];
+       u64 pn64;
+
+       if (WARN_ON(skb_queue_len(&tx->skbs) != 1))
+               return TX_DROP;
+
+       skb = skb_peek(&tx->skbs);
+
+       info = IEEE80211_SKB_CB(skb);
+
+       if (info->control.hw_key)
+               return TX_CONTINUE;
+
+       if (WARN_ON(skb_tailroom(skb) < sizeof(*mmie)))
+               return TX_DROP;
+
+       mmie = (struct ieee80211_mmie_16 *)skb_put(skb, sizeof(*mmie));
+       mmie->element_id = WLAN_EID_MMIE;
+       mmie->length = sizeof(*mmie) - 2;
+       mmie->key_id = cpu_to_le16(key->conf.keyidx);
+
+       /* PN = PN + 1 */
+       pn64 = atomic64_inc_return(&key->u.aes_cmac.tx_pn);
+
+       bip_ipn_set64(mmie->sequence_number, pn64);
+
+       bip_aad(skb, aad);
+
+       /* MIC = AES-256-CMAC(IGTK, AAD || Management Frame Body || MMIE, 128)
+        */
+       ieee80211_aes_cmac_256(key->u.aes_cmac.tfm, aad,
+                              skb->data + 24, skb->len - 24, mmie->mic);
+
+       return TX_CONTINUE;
+}
 
 ieee80211_rx_result
 ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx)
@@ -780,6 +1049,160 @@ ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx)
        return RX_CONTINUE;
 }
 
+ieee80211_rx_result
+ieee80211_crypto_aes_cmac_256_decrypt(struct ieee80211_rx_data *rx)
+{
+       struct sk_buff *skb = rx->skb;
+       struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+       struct ieee80211_key *key = rx->key;
+       struct ieee80211_mmie_16 *mmie;
+       u8 aad[20], mic[16], ipn[6];
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+
+       if (!ieee80211_is_mgmt(hdr->frame_control))
+               return RX_CONTINUE;
+
+       /* management frames are already linear */
+
+       if (skb->len < 24 + sizeof(*mmie))
+               return RX_DROP_UNUSABLE;
+
+       mmie = (struct ieee80211_mmie_16 *)
+               (skb->data + skb->len - sizeof(*mmie));
+       if (mmie->element_id != WLAN_EID_MMIE ||
+           mmie->length != sizeof(*mmie) - 2)
+               return RX_DROP_UNUSABLE; /* Invalid MMIE */
+
+       bip_ipn_swap(ipn, mmie->sequence_number);
+
+       if (memcmp(ipn, key->u.aes_cmac.rx_pn, 6) <= 0) {
+               key->u.aes_cmac.replays++;
+               return RX_DROP_UNUSABLE;
+       }
+
+       if (!(status->flag & RX_FLAG_DECRYPTED)) {
+               /* hardware didn't decrypt/verify MIC */
+               bip_aad(skb, aad);
+               ieee80211_aes_cmac_256(key->u.aes_cmac.tfm, aad,
+                                      skb->data + 24, skb->len - 24, mic);
+               if (memcmp(mic, mmie->mic, sizeof(mmie->mic)) != 0) {
+                       key->u.aes_cmac.icverrors++;
+                       return RX_DROP_UNUSABLE;
+               }
+       }
+
+       memcpy(key->u.aes_cmac.rx_pn, ipn, 6);
+
+       /* Remove MMIE */
+       skb_trim(skb, skb->len - sizeof(*mmie));
+
+       return RX_CONTINUE;
+}
+
+ieee80211_tx_result
+ieee80211_crypto_aes_gmac_encrypt(struct ieee80211_tx_data *tx)
+{
+       struct sk_buff *skb;
+       struct ieee80211_tx_info *info;
+       struct ieee80211_key *key = tx->key;
+       struct ieee80211_mmie_16 *mmie;
+       struct ieee80211_hdr *hdr;
+       u8 aad[20];
+       u64 pn64;
+       u8 nonce[12];
+
+       if (WARN_ON(skb_queue_len(&tx->skbs) != 1))
+               return TX_DROP;
+
+       skb = skb_peek(&tx->skbs);
+
+       info = IEEE80211_SKB_CB(skb);
+
+       if (info->control.hw_key)
+               return TX_CONTINUE;
+
+       if (WARN_ON(skb_tailroom(skb) < sizeof(*mmie)))
+               return TX_DROP;
+
+       mmie = (struct ieee80211_mmie_16 *)skb_put(skb, sizeof(*mmie));
+       mmie->element_id = WLAN_EID_MMIE;
+       mmie->length = sizeof(*mmie) - 2;
+       mmie->key_id = cpu_to_le16(key->conf.keyidx);
+
+       /* PN = PN + 1 */
+       pn64 = atomic64_inc_return(&key->u.aes_gmac.tx_pn);
+
+       bip_ipn_set64(mmie->sequence_number, pn64);
+
+       bip_aad(skb, aad);
+
+       hdr = (struct ieee80211_hdr *)skb->data;
+       memcpy(nonce, hdr->addr2, ETH_ALEN);
+       bip_ipn_swap(nonce + ETH_ALEN, mmie->sequence_number);
+
+       /* MIC = AES-GMAC(IGTK, AAD || Management Frame Body || MMIE, 128) */
+       if (ieee80211_aes_gmac(key->u.aes_gmac.tfm, aad, nonce,
+                              skb->data + 24, skb->len - 24, mmie->mic) < 0)
+               return TX_DROP;
+
+       return TX_CONTINUE;
+}
+
+ieee80211_rx_result
+ieee80211_crypto_aes_gmac_decrypt(struct ieee80211_rx_data *rx)
+{
+       struct sk_buff *skb = rx->skb;
+       struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+       struct ieee80211_key *key = rx->key;
+       struct ieee80211_mmie_16 *mmie;
+       u8 aad[20], mic[16], ipn[6], nonce[12];
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+
+       if (!ieee80211_is_mgmt(hdr->frame_control))
+               return RX_CONTINUE;
+
+       /* management frames are already linear */
+
+       if (skb->len < 24 + sizeof(*mmie))
+               return RX_DROP_UNUSABLE;
+
+       mmie = (struct ieee80211_mmie_16 *)
+               (skb->data + skb->len - sizeof(*mmie));
+       if (mmie->element_id != WLAN_EID_MMIE ||
+           mmie->length != sizeof(*mmie) - 2)
+               return RX_DROP_UNUSABLE; /* Invalid MMIE */
+
+       bip_ipn_swap(ipn, mmie->sequence_number);
+
+       if (memcmp(ipn, key->u.aes_gmac.rx_pn, 6) <= 0) {
+               key->u.aes_gmac.replays++;
+               return RX_DROP_UNUSABLE;
+       }
+
+       if (!(status->flag & RX_FLAG_DECRYPTED)) {
+               /* hardware didn't decrypt/verify MIC */
+               bip_aad(skb, aad);
+
+               memcpy(nonce, hdr->addr2, ETH_ALEN);
+               memcpy(nonce + ETH_ALEN, ipn, 6);
+
+               if (ieee80211_aes_gmac(key->u.aes_gmac.tfm, aad, nonce,
+                                      skb->data + 24, skb->len - 24,
+                                      mic) < 0 ||
+                   memcmp(mic, mmie->mic, sizeof(mmie->mic)) != 0) {
+                       key->u.aes_gmac.icverrors++;
+                       return RX_DROP_UNUSABLE;
+               }
+       }
+
+       memcpy(key->u.aes_gmac.rx_pn, ipn, 6);
+
+       /* Remove MMIE */
+       skb_trim(skb, skb->len - sizeof(*mmie));
+
+       return RX_CONTINUE;
+}
+
 ieee80211_tx_result
 ieee80211_crypto_hw_encrypt(struct ieee80211_tx_data *tx)
 {
index 62e5a12dfe0a24010b32eec210d7a38a1c4c3eb8..d98011ee8f554a512acd0d9711a4ee9bcb7583f0 100644 (file)
@@ -24,17 +24,32 @@ ieee80211_rx_result
 ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx);
 
 ieee80211_tx_result
-ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data *tx);
+ieee80211_crypto_ccmp_encrypt(struct ieee80211_tx_data *tx,
+                             unsigned int mic_len);
 ieee80211_rx_result
-ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx);
+ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx,
+                             unsigned int mic_len);
 
 ieee80211_tx_result
 ieee80211_crypto_aes_cmac_encrypt(struct ieee80211_tx_data *tx);
+ieee80211_tx_result
+ieee80211_crypto_aes_cmac_256_encrypt(struct ieee80211_tx_data *tx);
 ieee80211_rx_result
 ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx);
+ieee80211_rx_result
+ieee80211_crypto_aes_cmac_256_decrypt(struct ieee80211_rx_data *rx);
+ieee80211_tx_result
+ieee80211_crypto_aes_gmac_encrypt(struct ieee80211_tx_data *tx);
+ieee80211_rx_result
+ieee80211_crypto_aes_gmac_decrypt(struct ieee80211_rx_data *rx);
 ieee80211_tx_result
 ieee80211_crypto_hw_encrypt(struct ieee80211_tx_data *tx);
 ieee80211_rx_result
 ieee80211_crypto_hw_decrypt(struct ieee80211_rx_data *rx);
 
+ieee80211_tx_result
+ieee80211_crypto_gcmp_encrypt(struct ieee80211_tx_data *tx);
+ieee80211_rx_result
+ieee80211_crypto_gcmp_decrypt(struct ieee80211_rx_data *rx);
+
 #endif /* WPA_H */
index 3f4a0bbeed3da2bfe2863153dc44767f47e43686..d978f2f46ff35e0181e2a833e3bfab2b6d58221e 100644 (file)
@@ -170,6 +170,7 @@ static const struct acpi_device_id rfkill_acpi_match[] = {
        { "BCM2E1A", RFKILL_TYPE_BLUETOOTH },
        { "BCM2E39", RFKILL_TYPE_BLUETOOTH },
        { "BCM2E3D", RFKILL_TYPE_BLUETOOTH },
+       { "BCM2E40", RFKILL_TYPE_BLUETOOTH },
        { "BCM2E64", RFKILL_TYPE_BLUETOOTH },
        { "BCM4752", RFKILL_TYPE_GPS },
        { "LNV4752", RFKILL_TYPE_GPS },
index 7d60f4bf95d2ff0e3742906121684df6c5db55e0..d78fd8b54515e630b67bf38d710b2b698f703c4c 100644 (file)
@@ -397,6 +397,8 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
        [NL80211_ATTR_SMPS_MODE] = { .type = NLA_U8 },
        [NL80211_ATTR_MAC_MASK] = { .len = ETH_ALEN },
        [NL80211_ATTR_WIPHY_SELF_MANAGED_REG] = { .type = NLA_FLAG },
+       [NL80211_ATTR_NETNS_FD] = { .type = NLA_U32 },
+       [NL80211_ATTR_SCHED_SCAN_DELAY] = { .type = NLA_U32 },
 };
 
 /* policy for the key attributes */
@@ -5778,7 +5780,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
                request->ssids = (void *)&request->channels[n_channels];
        request->n_ssids = n_ssids;
        if (ie_len) {
-               if (request->ssids)
+               if (n_ssids)
                        request->ie = (void *)(request->ssids + n_ssids);
                else
                        request->ie = (void *)(request->channels + n_channels);
@@ -5834,7 +5836,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
        request->n_channels = i;
 
        i = 0;
-       if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
+       if (n_ssids) {
                nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) {
                        if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
                                err = -EINVAL;
@@ -6032,7 +6034,7 @@ nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev,
                request->ssids = (void *)&request->channels[n_channels];
        request->n_ssids = n_ssids;
        if (ie_len) {
-               if (request->ssids)
+               if (n_ssids)
                        request->ie = (void *)(request->ssids + n_ssids);
                else
                        request->ie = (void *)(request->channels + n_channels);
@@ -6041,7 +6043,7 @@ nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev,
        if (n_match_sets) {
                if (request->ie)
                        request->match_sets = (void *)(request->ie + ie_len);
-               else if (request->ssids)
+               else if (n_ssids)
                        request->match_sets =
                                (void *)(request->ssids + n_ssids);
                else
@@ -6100,7 +6102,7 @@ nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev,
        request->n_channels = i;
 
        i = 0;
-       if (attrs[NL80211_ATTR_SCAN_SSIDS]) {
+       if (n_ssids) {
                nla_for_each_nested(attr, attrs[NL80211_ATTR_SCAN_SSIDS],
                                    tmp) {
                        if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
@@ -6208,6 +6210,10 @@ nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev,
                }
        }
 
+       if (attrs[NL80211_ATTR_SCHED_SCAN_DELAY])
+               request->delay =
+                       nla_get_u32(attrs[NL80211_ATTR_SCHED_SCAN_DELAY]);
+
        request->interval = interval;
        request->scan_start = jiffies;
 
@@ -7768,14 +7774,19 @@ static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info)
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct net *net;
        int err;
-       u32 pid;
 
-       if (!info->attrs[NL80211_ATTR_PID])
-               return -EINVAL;
+       if (info->attrs[NL80211_ATTR_PID]) {
+               u32 pid = nla_get_u32(info->attrs[NL80211_ATTR_PID]);
+
+               net = get_net_ns_by_pid(pid);
+       } else if (info->attrs[NL80211_ATTR_NETNS_FD]) {
+               u32 fd = nla_get_u32(info->attrs[NL80211_ATTR_NETNS_FD]);
 
-       pid = nla_get_u32(info->attrs[NL80211_ATTR_PID]);
+               net = get_net_ns_by_fd(fd);
+       } else {
+               return -EINVAL;
+       }
 
-       net = get_net_ns_by_pid(pid);
        if (IS_ERR(net))
                return PTR_ERR(net);
 
index 0d1966d54aaa70af912b678bfffdc39e8ad2fbd0..6903dbdcb8c1f03bcef684ad1074e3dea9f18e18 100644 (file)
@@ -227,18 +227,32 @@ int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,
        if (pairwise && !mac_addr)
                return -EINVAL;
 
-       /*
-        * Disallow pairwise keys with non-zero index unless it's WEP
-        * or a vendor specific cipher (because current deployments use
-        * pairwise WEP keys with non-zero indices and for vendor specific
-        * ciphers this should be validated in the driver or hardware level
-        * - but 802.11i clearly specifies to use zero)
-        */
-       if (pairwise && key_idx &&
-           ((params->cipher == WLAN_CIPHER_SUITE_TKIP) ||
-            (params->cipher == WLAN_CIPHER_SUITE_CCMP) ||
-            (params->cipher == WLAN_CIPHER_SUITE_AES_CMAC)))
-               return -EINVAL;
+       switch (params->cipher) {
+       case WLAN_CIPHER_SUITE_TKIP:
+       case WLAN_CIPHER_SUITE_CCMP:
+       case WLAN_CIPHER_SUITE_CCMP_256:
+       case WLAN_CIPHER_SUITE_GCMP:
+       case WLAN_CIPHER_SUITE_GCMP_256:
+               /* Disallow pairwise keys with non-zero index unless it's WEP
+                * or a vendor specific cipher (because current deployments use
+                * pairwise WEP keys with non-zero indices and for vendor
+                * specific ciphers this should be validated in the driver or
+                * hardware level - but 802.11i clearly specifies to use zero)
+                */
+               if (pairwise && key_idx)
+                       return -EINVAL;
+               break;
+       case WLAN_CIPHER_SUITE_AES_CMAC:
+       case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+       case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+       case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+               /* Disallow BIP (group-only) cipher as pairwise cipher */
+               if (pairwise)
+                       return -EINVAL;
+               break;
+       default:
+               break;
+       }
 
        switch (params->cipher) {
        case WLAN_CIPHER_SUITE_WEP40:
@@ -253,6 +267,18 @@ int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,
                if (params->key_len != WLAN_KEY_LEN_CCMP)
                        return -EINVAL;
                break;
+       case WLAN_CIPHER_SUITE_CCMP_256:
+               if (params->key_len != WLAN_KEY_LEN_CCMP_256)
+                       return -EINVAL;
+               break;
+       case WLAN_CIPHER_SUITE_GCMP:
+               if (params->key_len != WLAN_KEY_LEN_GCMP)
+                       return -EINVAL;
+               break;
+       case WLAN_CIPHER_SUITE_GCMP_256:
+               if (params->key_len != WLAN_KEY_LEN_GCMP_256)
+                       return -EINVAL;
+               break;
        case WLAN_CIPHER_SUITE_WEP104:
                if (params->key_len != WLAN_KEY_LEN_WEP104)
                        return -EINVAL;
@@ -261,6 +287,18 @@ int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,
                if (params->key_len != WLAN_KEY_LEN_AES_CMAC)
                        return -EINVAL;
                break;
+       case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+               if (params->key_len != WLAN_KEY_LEN_BIP_CMAC_256)
+                       return -EINVAL;
+               break;
+       case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+               if (params->key_len != WLAN_KEY_LEN_BIP_GMAC_128)
+                       return -EINVAL;
+               break;
+       case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+               if (params->key_len != WLAN_KEY_LEN_BIP_GMAC_256)
+                       return -EINVAL;
+               break;
        default:
                /*
                 * We don't know anything about this algorithm,
@@ -280,7 +318,13 @@ int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,
                        return -EINVAL;
                case WLAN_CIPHER_SUITE_TKIP:
                case WLAN_CIPHER_SUITE_CCMP:
+               case WLAN_CIPHER_SUITE_CCMP_256:
+               case WLAN_CIPHER_SUITE_GCMP:
+               case WLAN_CIPHER_SUITE_GCMP_256:
                case WLAN_CIPHER_SUITE_AES_CMAC:
+               case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+               case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+               case WLAN_CIPHER_SUITE_BIP_GMAC_256:
                        if (params->seq_len != 6)
                                return -EINVAL;
                        break;