brcmfmac: support 4-way handshake offloading for WPA/WPA2-PSK
authorArend van Spriel <arend@broadcom.com>
Fri, 9 Jun 2017 12:08:47 +0000 (13:08 +0100)
committerKalle Valo <kvalo@codeaurora.org>
Fri, 30 Jun 2017 06:38:22 +0000 (09:38 +0300)
The firmware may have supplicant code built-in. This is detected
by the driver and indicated in the wiphy features flags. User-space
can use this flag to determine whether or not to provide the
pre-shared key material in the nl80211 CONNECT command.

Reviewed-by: Gautam (Gautam Kumar) Shukla <gautams@broadcom.com>
Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
Signed-off-by: Arend van Spriel <arend.vanspriel@broadcom.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h

index 898c7b024c159df1761c179a53cf0ddcb94d9738..36945e0c8923275f6a91ac003ee4a98ab376be0d 100644 (file)
@@ -1357,6 +1357,27 @@ static u16 brcmf_map_fw_linkdown_reason(const struct brcmf_event_msg *e)
        return reason;
 }
 
+static int brcmf_set_pmk(struct brcmf_if *ifp, const u8 *pmk_data, u16 pmk_len)
+{
+       struct brcmf_wsec_pmk_le pmk;
+       int i, err;
+
+       /* convert to firmware key format */
+       pmk.key_len = cpu_to_le16(pmk_len << 1);
+       pmk.flags = cpu_to_le16(BRCMF_WSEC_PASSPHRASE);
+       for (i = 0; i < pmk_len; i++)
+               snprintf(&pmk.key[2 * i], 3, "%02x", pmk_data[i]);
+
+       /* store psk in firmware */
+       err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_WSEC_PMK,
+                                    &pmk, sizeof(pmk));
+       if (err < 0)
+               brcmf_err("failed to change PSK in firmware (len=%u)\n",
+                         pmk_len);
+
+       return err;
+}
+
 static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason)
 {
        struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy);
@@ -1379,6 +1400,10 @@ static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason)
        clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state);
        clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
        brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
+       if (vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_NONE) {
+               brcmf_set_pmk(vif->ifp, NULL, 0);
+               vif->profile.use_fwsup = BRCMF_PROFILE_FWSUP_NONE;
+       }
        brcmf_dbg(TRACE, "Exit\n");
 }
 
@@ -2012,6 +2037,23 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
                goto done;
        }
 
+       if (sme->crypto.psk) {
+               brcmf_dbg(INFO, "using PSK offload\n");
+
+               /* enable firmware supplicant for this interface */
+               err = brcmf_fil_iovar_int_set(ifp, "sup_wpa", 1);
+               if (err < 0) {
+                       brcmf_err("failed to enable fw supplicant\n");
+                       goto done;
+               }
+               ifp->vif->profile.use_fwsup = BRCMF_PROFILE_FWSUP_PSK;
+
+               err = brcmf_set_pmk(ifp, sme->crypto.psk,
+                                   BRCMF_WSEC_MAX_PSK_LEN);
+               if (err)
+                       goto done;
+       }
+
        /* Join with specific BSSID and cached SSID
         * If SSID is zero join based on BSSID only
         */
@@ -5247,16 +5289,30 @@ void brcmf_cfg80211_free_netdev(struct net_device *ndev)
                brcmf_free_vif(vif);
 }
 
-static bool brcmf_is_linkup(const struct brcmf_event_msg *e)
+static bool brcmf_is_linkup(struct brcmf_cfg80211_vif *vif,
+                           const struct brcmf_event_msg *e)
 {
        u32 event = e->event_code;
        u32 status = e->status;
 
+       if (event == BRCMF_E_PSK_SUP &&
+           status == BRCMF_E_STATUS_FWSUP_COMPLETED)
+               set_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state);
        if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) {
                brcmf_dbg(CONN, "Processing set ssid\n");
-               return true;
+               memcpy(vif->profile.bssid, e->addr, ETH_ALEN);
+               if (vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_PSK)
+                       return true;
+
+               set_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state);
        }
 
+       if (test_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state) &&
+           test_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state)) {
+               clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state);
+               clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state);
+               return true;
+       }
        return false;
 }
 
@@ -5291,6 +5347,13 @@ static bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg,
                return true;
        }
 
+       if (event == BRCMF_E_PSK_SUP &&
+           status != BRCMF_E_STATUS_FWSUP_COMPLETED) {
+               brcmf_dbg(CONN, "Processing failed supplicant state: %u\n",
+                         status);
+               return true;
+       }
+
        return false;
 }
 
@@ -5448,7 +5511,6 @@ brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
                               &ifp->vif->sme_state)) {
                if (completed) {
                        brcmf_get_assoc_ies(cfg, ifp);
-                       memcpy(profile->bssid, e->addr, ETH_ALEN);
                        brcmf_update_bss_info(cfg, ifp);
                        set_bit(BRCMF_VIF_STATUS_CONNECTED,
                                &ifp->vif->sme_state);
@@ -5527,7 +5589,7 @@ brcmf_notify_connect_status(struct brcmf_if *ifp,
 
        if (brcmf_is_apmode(ifp->vif)) {
                err = brcmf_notify_connect_status_ap(cfg, ndev, e, data);
-       } else if (brcmf_is_linkup(e)) {
+       } else if (brcmf_is_linkup(ifp->vif, e)) {
                brcmf_dbg(CONN, "Linkup\n");
                if (brcmf_is_ibssmode(ifp->vif)) {
                        brcmf_inform_ibss(cfg, ndev, e->addr);
@@ -5695,6 +5757,8 @@ static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg)
                            brcmf_p2p_notify_action_tx_complete);
        brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE,
                            brcmf_p2p_notify_action_tx_complete);
+       brcmf_fweh_register(cfg->pub, BRCMF_E_PSK_SUP,
+                           brcmf_notify_connect_status);
 }
 
 static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
@@ -6491,6 +6555,9 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
                wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
        if (!ifp->drvr->settings->roamoff)
                wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
+       if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_FWSUP))
+               wiphy_ext_feature_set(wiphy,
+                                     NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK);
        wiphy->mgmt_stypes = brcmf_txrx_stypes;
        wiphy->max_remain_on_channel_duration = 5000;
        if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) {
index 12ff15446c6c5d17f18a9b57ffc81b01c71dc674..9e7d3c6f625ee7017455af55c56474f0bda69b8c 100644 (file)
@@ -115,6 +115,11 @@ struct brcmf_cfg80211_security {
        u32 cipher_group;
 };
 
+enum brcmf_profile_fwsup {
+       BRCMF_PROFILE_FWSUP_NONE,
+       BRCMF_PROFILE_FWSUP_PSK
+};
+
 /**
  * struct brcmf_cfg80211_profile - profile information.
  *
@@ -126,6 +131,7 @@ struct brcmf_cfg80211_profile {
        u8 bssid[ETH_ALEN];
        struct brcmf_cfg80211_security sec;
        struct brcmf_wsec_key key[BRCMF_MAX_DEFAULT_KEYS];
+       enum brcmf_profile_fwsup use_fwsup;
 };
 
 /**
@@ -133,16 +139,20 @@ struct brcmf_cfg80211_profile {
  *
  * @BRCMF_VIF_STATUS_READY: ready for operation.
  * @BRCMF_VIF_STATUS_CONNECTING: connect/join in progress.
- * @BRCMF_VIF_STATUS_CONNECTED: connected/joined succesfully.
+ * @BRCMF_VIF_STATUS_CONNECTED: connected/joined successfully.
  * @BRCMF_VIF_STATUS_DISCONNECTING: disconnect/disable in progress.
  * @BRCMF_VIF_STATUS_AP_CREATED: AP operation started.
+ * @BRCMF_VIF_STATUS_EAP_SUCCUSS: EAPOL handshake successful.
+ * @BRCMF_VIF_STATUS_ASSOC_SUCCESS: successful SET_SSID received.
  */
 enum brcmf_vif_status {
        BRCMF_VIF_STATUS_READY,
        BRCMF_VIF_STATUS_CONNECTING,
        BRCMF_VIF_STATUS_CONNECTED,
        BRCMF_VIF_STATUS_DISCONNECTING,
-       BRCMF_VIF_STATUS_AP_CREATED
+       BRCMF_VIF_STATUS_AP_CREATED,
+       BRCMF_VIF_STATUS_EAP_SUCCESS,
+       BRCMF_VIF_STATUS_ASSOC_SUCCESS,
 };
 
 /**
index 8c7ef59944f08eabbd43c42682afa4828df23816..d21258d277ce4bca965e588e537df507d4e23892 100644 (file)
@@ -195,6 +195,7 @@ void brcmf_feat_attach(struct brcmf_pub *drvr)
                          drvr->settings->feature_disable);
                ifp->drvr->feat_flags &= ~drvr->settings->feature_disable;
        }
+       brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_FWSUP, "sup_wpa");
 
        /* set chip related quirks */
        switch (drvr->bus_if->chip) {
index c1dbd17506aa1a2f65c2d2b5d3c8821a75f3de18..1ab4f161711208a30240d91b06a03af26ea5ffd6 100644 (file)
@@ -32,6 +32,7 @@
  * WOWL_ARP_ND: ARP and Neighbor Discovery offload support during WOWL.
  * MFP: 802.11w Management Frame Protection.
  * GSCAN: enhanced scan offload feature.
+ * FWSUP: Firmware supplicant.
  */
 #define BRCMF_FEAT_LIST \
        BRCMF_FEAT_DEF(MBSS) \
@@ -46,7 +47,8 @@
        BRCMF_FEAT_DEF(WOWL_GTK) \
        BRCMF_FEAT_DEF(WOWL_ARP_ND) \
        BRCMF_FEAT_DEF(MFP) \
-       BRCMF_FEAT_DEF(GSCAN)
+       BRCMF_FEAT_DEF(GSCAN) \
+       BRCMF_FEAT_DEF(FWSUP)
 
 /*
  * Quirks:
index 5fba4b49f3b3536c1960dd42e70aa17f916a26ae..816f80ea925b110d79d0845617a70a1ac7e92ba8 100644 (file)
@@ -142,6 +142,16 @@ enum brcmf_fweh_event_code {
 #define BRCMF_E_STATUS_CS_ABORT                        15
 #define BRCMF_E_STATUS_ERROR                   16
 
+/* status field values for PSK_SUP event */
+#define BRCMF_E_STATUS_FWSUP_WAIT_M1           4
+#define BRCMF_E_STATUS_FWSUP_PREP_M2           5
+#define BRCMF_E_STATUS_FWSUP_COMPLETED         6
+#define BRCMF_E_STATUS_FWSUP_TIMEOUT           7
+#define BRCMF_E_STATUS_FWSUP_WAIT_M3           8
+#define BRCMF_E_STATUS_FWSUP_PREP_M4           9
+#define BRCMF_E_STATUS_FWSUP_WAIT_G1           10
+#define BRCMF_E_STATUS_FWSUP_PREP_G2           11
+
 /* reason field values in struct brcmf_event_msg */
 #define BRCMF_E_REASON_INITIAL_ASSOC           0
 #define BRCMF_E_REASON_LOW_RSSI                        1
@@ -161,6 +171,26 @@ enum brcmf_fweh_event_code {
 #define BRCMF_E_REASON_TDLS_PEER_CONNECTED     1
 #define BRCMF_E_REASON_TDLS_PEER_DISCONNECTED  2
 
+/* reason field values for PSK_SUP event */
+#define BRCMF_E_REASON_FWSUP_OTHER             0
+#define BRCMF_E_REASON_FWSUP_DECRYPT_KEY_DATA  1
+#define BRCMF_E_REASON_FWSUP_BAD_UCAST_WEP128  2
+#define BRCMF_E_REASON_FWSUP_BAD_UCAST_WEP40   3
+#define BRCMF_E_REASON_FWSUP_UNSUP_KEY_LEN     4
+#define BRCMF_E_REASON_FWSUP_PW_KEY_CIPHER     5
+#define BRCMF_E_REASON_FWSUP_MSG3_TOO_MANY_IE  6
+#define BRCMF_E_REASON_FWSUP_MSG3_IE_MISMATCH  7
+#define BRCMF_E_REASON_FWSUP_NO_INSTALL_FLAG   8
+#define BRCMF_E_REASON_FWSUP_MSG3_NO_GTK       9
+#define BRCMF_E_REASON_FWSUP_GRP_KEY_CIPHER    10
+#define BRCMF_E_REASON_FWSUP_GRP_MSG1_NO_GTK   11
+#define BRCMF_E_REASON_FWSUP_GTK_DECRYPT_FAIL  12
+#define BRCMF_E_REASON_FWSUP_SEND_FAIL         13
+#define BRCMF_E_REASON_FWSUP_DEAUTH            14
+#define BRCMF_E_REASON_FWSUP_WPA_PSK_TMO       15
+#define BRCMF_E_REASON_FWSUP_WPA_PSK_M1_TMO    16
+#define BRCMF_E_REASON_FWSUP_WPA_PSK_M3_TMO    17
+
 /* action field values for brcmf_ifevent */
 #define BRCMF_E_IF_ADD                         1
 #define BRCMF_E_IF_DEL                         2
index 3a9a76dd922265425802b1e7a49c5d33878c496c..63b1287e2e6d49122bdd2891f356c0e590ff978d 100644 (file)
@@ -85,6 +85,7 @@
 #define BRCMF_C_SET_SCAN_PASSIVE_TIME          258
 #define BRCMF_C_GET_VAR                                262
 #define BRCMF_C_SET_VAR                                263
+#define BRCMF_C_SET_WSEC_PMK                   268
 
 s32 brcmf_fil_cmd_data_set(struct brcmf_if *ifp, u32 cmd, void *data, u32 len);
 s32 brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len);
index b857d539b9ac42f20a09f74e39b0f42a0f37ba4a..8391989b188297b2736b1c1a2375a97f84e0dd4a 100644 (file)
@@ -45,6 +45,9 @@
 #define BRCMF_SCAN_PARAMS_COUNT_MASK   0x0000ffff
 #define BRCMF_SCAN_PARAMS_NSSID_SHIFT  16
 
+#define BRCMF_WSEC_MAX_PSK_LEN         32
+#define        BRCMF_WSEC_PASSPHRASE           BIT(0)
+
 /* primary (ie tx) key */
 #define BRCMF_PRIMARY_KEY              (1 << 1)
 #define DOT11_BSSTYPE_ANY              2
@@ -470,6 +473,19 @@ struct brcmf_wsec_key_le {
        u8 ea[ETH_ALEN];        /* per station */
 };
 
+/**
+ * struct brcmf_wsec_pmk_le - firmware pmk material.
+ *
+ * @key_len: number of octets in key material.
+ * @flags: key handling qualifiers.
+ * @key: PMK key material.
+ */
+struct brcmf_wsec_pmk_le {
+       __le16  key_len;
+       __le16  flags;
+       u8 key[2 * BRCMF_WSEC_MAX_PSK_LEN + 1];
+};
+
 /* Used to get specific STA parameters */
 struct brcmf_scb_val_le {
        __le32 val;