TDLS: Declare tdls_testing as extern in a header file
[mech_eap.git] / wpa_supplicant / wps_supplicant.c
index ef50878..74a420c 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * wpa_supplicant / WPS integration
- * Copyright (c) 2008-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
 #define WPS_PIN_SCAN_IGNORE_SEL_REG 3
 #endif /* WPS_PIN_SCAN_IGNORE_SEL_REG */
 
+/*
+ * The minimum time in seconds before trying to associate to a WPS PIN AP that
+ * does not have Selected Registrar TRUE.
+ */
+#ifndef WPS_PIN_TIME_IGNORE_SEL_REG
+#define WPS_PIN_TIME_IGNORE_SEL_REG 5
+#endif /* WPS_PIN_TIME_IGNORE_SEL_REG */
+
 static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx);
 static void wpas_clear_wps(struct wpa_supplicant *wpa_s);
 
@@ -52,8 +60,30 @@ static void wpas_wps_clear_ap_info(struct wpa_supplicant *wpa_s)
 }
 
 
+static void wpas_wps_assoc_with_cred(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+       int use_fast_assoc = timeout_ctx != NULL;
+
+       wpa_printf(MSG_DEBUG, "WPS: Continuing association after eapol_cb");
+       if (!use_fast_assoc ||
+           wpa_supplicant_fast_associate(wpa_s) != 1)
+               wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+
+static void wpas_wps_assoc_with_cred_cancel(struct wpa_supplicant *wpa_s)
+{
+       eloop_cancel_timeout(wpas_wps_assoc_with_cred, wpa_s, (void *) 0);
+       eloop_cancel_timeout(wpas_wps_assoc_with_cred, wpa_s, (void *) 1);
+}
+
+
 int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s)
 {
+       if (wpas_p2p_wps_eapol_cb(wpa_s) > 0)
+               return 1;
+
        if (!wpa_s->wps_success &&
            wpa_s->current_ssid &&
            eap_is_wps_pin_enrollee(&wpa_s->current_ssid->eap)) {
@@ -91,6 +121,7 @@ int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s)
                wpa_printf(MSG_DEBUG, "WPS: Network configuration replaced - "
                           "try to associate with the received credential "
                           "(freq=%u)", freq);
+               wpa_s->own_disconnect_req = 1;
                wpa_supplicant_deauthenticate(wpa_s,
                                              WLAN_REASON_DEAUTH_LEAVING);
                if (disabled) {
@@ -119,9 +150,18 @@ int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s)
                        wpabuf_free(wps);
                }
 
-               if (!use_fast_assoc ||
-                   wpa_supplicant_fast_associate(wpa_s) != 1)
-                       wpa_supplicant_req_scan(wpa_s, 0, 0);
+               /*
+                * Complete the next step from an eloop timeout to allow pending
+                * driver events related to the disconnection to be processed
+                * first. This makes it less likely for disconnection event to
+                * cause problems with the following connection.
+                */
+               wpa_printf(MSG_DEBUG, "WPS: Continue association from timeout");
+               wpas_wps_assoc_with_cred_cancel(wpa_s);
+               eloop_register_timeout(0, 10000,
+                                      wpas_wps_assoc_with_cred, wpa_s,
+                                      use_fast_assoc ? (void *) 1 :
+                                      (void *) 0);
                return 1;
        }
 
@@ -129,6 +169,7 @@ int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s)
                wpa_printf(MSG_DEBUG, "WPS: Registration completed - waiting "
                           "for external credential processing");
                wpas_clear_wps(wpa_s);
+               wpa_s->own_disconnect_req = 1;
                wpa_supplicant_deauthenticate(wpa_s,
                                              WLAN_REASON_DEAUTH_LEAVING);
                return 1;
@@ -255,39 +296,61 @@ static void wpas_wps_remove_dup_network(struct wpa_supplicant *wpa_s,
                /* compare security parameters */
                if (ssid->auth_alg != new_ssid->auth_alg ||
                    ssid->key_mgmt != new_ssid->key_mgmt ||
-                   ssid->proto != new_ssid->proto ||
-                   ssid->pairwise_cipher != new_ssid->pairwise_cipher ||
-                   ssid->group_cipher != new_ssid->group_cipher)
+                   (ssid->group_cipher != new_ssid->group_cipher &&
+                    !(ssid->group_cipher & new_ssid->group_cipher &
+                      WPA_CIPHER_CCMP)))
                        continue;
 
-               if (ssid->passphrase && new_ssid->passphrase) {
-                       if (os_strlen(ssid->passphrase) !=
-                           os_strlen(new_ssid->passphrase))
-                               continue;
-                       if (os_strcmp(ssid->passphrase, new_ssid->passphrase) !=
-                           0)
+               /*
+                * Some existing WPS APs will send two creds in case they are
+                * configured for mixed mode operation (WPA+WPA2 and TKIP+CCMP).
+                * Try to merge these two creds if they are received in the same
+                * M8 message.
+                */
+               if (ssid->wps_run && ssid->wps_run == new_ssid->wps_run &&
+                   wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) {
+                       if (new_ssid->passphrase && ssid->passphrase &&
+                           os_strcmp(new_ssid->passphrase, ssid->passphrase) !=
+                           0) {
+                               wpa_printf(MSG_DEBUG,
+                                          "WPS: M8 Creds with different passphrase - do not merge");
                                continue;
-               } else if (ssid->passphrase || new_ssid->passphrase)
-                       continue;
-
-               if ((ssid->psk_set || new_ssid->psk_set) &&
-                   os_memcmp(ssid->psk, new_ssid->psk, sizeof(ssid->psk)) != 0)
-                       continue;
+                       }
 
-               if (ssid->auth_alg == WPA_ALG_WEP) {
-                       if (ssid->wep_tx_keyidx != new_ssid->wep_tx_keyidx)
+                       if (new_ssid->psk_set &&
+                           (!ssid->psk_set ||
+                            os_memcmp(new_ssid->psk, ssid->psk, 32) != 0)) {
+                               wpa_printf(MSG_DEBUG,
+                                          "WPS: M8 Creds with different PSK - do not merge");
                                continue;
-                       if (os_memcmp(ssid->wep_key, new_ssid->wep_key,
-                                     sizeof(ssid->wep_key)))
+                       }
+
+                       if ((new_ssid->passphrase && !ssid->passphrase) ||
+                           (!new_ssid->passphrase && ssid->passphrase)) {
+                               wpa_printf(MSG_DEBUG,
+                                          "WPS: M8 Creds with different passphrase/PSK type - do not merge");
                                continue;
-                       if (os_memcmp(ssid->wep_key_len, new_ssid->wep_key_len,
-                                     sizeof(ssid->wep_key_len)))
+                       }
+
+                       wpa_printf(MSG_DEBUG,
+                                  "WPS: Workaround - merge likely WPA/WPA2-mixed mode creds in same M8 message");
+                       new_ssid->proto |= ssid->proto;
+                       new_ssid->pairwise_cipher |= ssid->pairwise_cipher;
+               } else {
+                       /*
+                        * proto and pairwise_cipher difference matter for
+                        * non-mixed-mode creds.
+                        */
+                       if (ssid->proto != new_ssid->proto ||
+                           ssid->pairwise_cipher != new_ssid->pairwise_cipher)
                                continue;
                }
 
                /* Remove the duplicated older network entry. */
                wpa_printf(MSG_DEBUG, "Remove duplicate network %d", ssid->id);
                wpas_notify_network_removed(wpa_s, ssid);
+               if (wpa_s->current_ssid == ssid)
+                       wpa_s->current_ssid = NULL;
                wpa_config_remove_network(wpa_s->conf, ssid->id);
        }
 }
@@ -298,7 +361,6 @@ static int wpa_supplicant_wps_cred(void *ctx,
 {
        struct wpa_supplicant *wpa_s = ctx;
        struct wpa_ssid *ssid = wpa_s->current_ssid;
-       u8 key_idx = 0;
        u16 auth_type;
 #ifdef CONFIG_WPS_REG_DISABLE_OPEN
        int registrar = 0;
@@ -344,7 +406,6 @@ static int wpa_supplicant_wps_cred(void *ctx,
        }
 
        if (auth_type != WPS_AUTH_OPEN &&
-           auth_type != WPS_AUTH_SHARED &&
            auth_type != WPS_AUTH_WPAPSK &&
            auth_type != WPS_AUTH_WPA2PSK) {
                wpa_printf(MSG_DEBUG, "WPS: Ignored credentials for "
@@ -392,10 +453,22 @@ static int wpa_supplicant_wps_cred(void *ctx,
                ssid = wpa_config_add_network(wpa_s->conf);
                if (ssid == NULL)
                        return -1;
+               if (wpa_s->current_ssid) {
+                       /*
+                        * Should the GO issue multiple credentials for some
+                        * reason, each credential should be marked as a
+                        * temporary P2P group similarly to the one that gets
+                        * marked as such based on the pre-configured values
+                        * used for the WPS network block.
+                        */
+                       ssid->p2p_group = wpa_s->current_ssid->p2p_group;
+                       ssid->temporary = wpa_s->current_ssid->temporary;
+               }
                wpas_notify_network_added(wpa_s, ssid);
        }
 
        wpa_config_set_network_defaults(ssid);
+       ssid->wps_run = wpa_s->wps_run;
 
        os_free(ssid->ssid);
        ssid->ssid = os_malloc(cred->ssid_len);
@@ -407,43 +480,16 @@ static int wpa_supplicant_wps_cred(void *ctx,
        switch (cred->encr_type) {
        case WPS_ENCR_NONE:
                break;
-       case WPS_ENCR_WEP:
-               if (cred->key_len <= 0)
-                       break;
-               if (cred->key_len != 5 && cred->key_len != 13 &&
-                   cred->key_len != 10 && cred->key_len != 26) {
-                       wpa_printf(MSG_ERROR, "WPS: Invalid WEP Key length "
-                                  "%lu", (unsigned long) cred->key_len);
-                       return -1;
-               }
-               if (cred->key_idx > NUM_WEP_KEYS) {
-                       wpa_printf(MSG_ERROR, "WPS: Invalid WEP Key index %d",
-                                  cred->key_idx);
-                       return -1;
-               }
-               if (cred->key_idx)
-                       key_idx = cred->key_idx - 1;
-               if (cred->key_len == 10 || cred->key_len == 26) {
-                       if (hexstr2bin((char *) cred->key,
-                                      ssid->wep_key[key_idx],
-                                      cred->key_len / 2) < 0) {
-                               wpa_printf(MSG_ERROR, "WPS: Invalid WEP Key "
-                                          "%d", key_idx);
-                               return -1;
-                       }
-                       ssid->wep_key_len[key_idx] = cred->key_len / 2;
-               } else {
-                       os_memcpy(ssid->wep_key[key_idx], cred->key,
-                                 cred->key_len);
-                       ssid->wep_key_len[key_idx] = cred->key_len;
-               }
-               ssid->wep_tx_keyidx = key_idx;
-               break;
        case WPS_ENCR_TKIP:
                ssid->pairwise_cipher = WPA_CIPHER_TKIP;
                break;
        case WPS_ENCR_AES:
                ssid->pairwise_cipher = WPA_CIPHER_CCMP;
+               if (wpa_s->drv_capa_known &&
+                   (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_GCMP)) {
+                       ssid->pairwise_cipher |= WPA_CIPHER_GCMP;
+                       ssid->group_cipher |= WPA_CIPHER_GCMP;
+               }
                break;
        }
 
@@ -463,11 +509,6 @@ static int wpa_supplicant_wps_cred(void *ctx,
                }
 #endif /* CONFIG_WPS_REG_DISABLE_OPEN */
                break;
-       case WPS_AUTH_SHARED:
-               ssid->auth_alg = WPA_AUTH_ALG_SHARED;
-               ssid->key_mgmt = WPA_KEY_MGMT_NONE;
-               ssid->proto = 0;
-               break;
        case WPS_AUTH_WPAPSK:
                ssid->auth_alg = WPA_AUTH_ALG_OPEN;
                ssid->key_mgmt = WPA_KEY_MGMT_PSK;
@@ -506,12 +547,10 @@ static int wpa_supplicant_wps_cred(void *ctx,
                        return -1;
                }
        }
+       ssid->priority = wpa_s->conf->wps_priority;
 
        wpas_wps_security_workaround(wpa_s, ssid, cred);
 
-       if (cred->ap_channel)
-               wpa_s->wps_ap_channel = cred->ap_channel;
-
        wpas_wps_remove_dup_network(wpa_s, ssid);
 
 #ifndef CONFIG_NO_CONFIG_WRITE
@@ -522,6 +561,9 @@ static int wpa_supplicant_wps_cred(void *ctx,
        }
 #endif /* CONFIG_NO_CONFIG_WRITE */
 
+       if (ssid->priority)
+               wpa_config_update_prio_list(wpa_s->conf);
+
        /*
         * Optimize the post-WPS scan based on the channel used during
         * the provisioning in case EAP-Failure is not received.
@@ -533,15 +575,6 @@ static int wpa_supplicant_wps_cred(void *ctx,
 }
 
 
-#ifdef CONFIG_P2P
-static void wpas_wps_pbc_overlap_cb(void *eloop_ctx, void *timeout_ctx)
-{
-       struct wpa_supplicant *wpa_s = eloop_ctx;
-       wpas_p2p_notif_pbc_overlap(wpa_s);
-}
-#endif /* CONFIG_P2P */
-
-
 static void wpa_supplicant_wps_event_m2d(struct wpa_supplicant *wpa_s,
                                         struct wps_event_m2d *m2d)
 {
@@ -550,8 +583,8 @@ static void wpa_supplicant_wps_event_m2d(struct wpa_supplicant *wpa_s,
                m2d->dev_password_id, m2d->config_error);
        wpas_notify_wps_event_m2d(wpa_s, m2d);
 #ifdef CONFIG_P2P
-       if (wpa_s->parent && wpa_s->parent != wpa_s) {
-               wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_M2D
+       if (wpa_s->p2pdev && wpa_s->p2pdev != wpa_s) {
+               wpa_msg(wpa_s->p2pdev, MSG_INFO, WPS_EVENT_M2D
                        "dev_password_id=%d config_error=%d",
                        m2d->dev_password_id, m2d->config_error);
        }
@@ -560,7 +593,7 @@ static void wpa_supplicant_wps_event_m2d(struct wpa_supplicant *wpa_s,
                 * Notify P2P from eloop timeout to avoid issues with the
                 * interface getting removed while processing a message.
                 */
-               eloop_register_timeout(0, 0, wpas_wps_pbc_overlap_cb, wpa_s,
+               eloop_register_timeout(0, 0, wpas_p2p_pbc_overlap_cb, wpa_s,
                                       NULL);
        }
 #endif /* CONFIG_P2P */
@@ -584,8 +617,8 @@ static void wpa_supplicant_wps_event_fail(struct wpa_supplicant *wpa_s,
                        WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)",
                        fail->msg, fail->config_error, fail->error_indication,
                        wps_ei_str(fail->error_indication));
-               if (wpa_s->parent && wpa_s->parent != wpa_s)
-                       wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_FAIL
+               if (wpa_s->p2pdev && wpa_s->p2pdev != wpa_s)
+                       wpa_msg(wpa_s->p2pdev, MSG_INFO, WPS_EVENT_FAIL
                                "msg=%d config_error=%d reason=%d (%s)",
                                fail->msg, fail->config_error,
                                fail->error_indication,
@@ -594,8 +627,8 @@ static void wpa_supplicant_wps_event_fail(struct wpa_supplicant *wpa_s,
                wpa_msg(wpa_s, MSG_INFO,
                        WPS_EVENT_FAIL "msg=%d config_error=%d",
                        fail->msg, fail->config_error);
-               if (wpa_s->parent && wpa_s->parent != wpa_s)
-                       wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_FAIL
+               if (wpa_s->p2pdev && wpa_s->p2pdev != wpa_s)
+                       wpa_msg(wpa_s->p2pdev, MSG_INFO, WPS_EVENT_FAIL
                                "msg=%d config_error=%d",
                                fail->msg, fail->config_error);
        }
@@ -608,9 +641,7 @@ static void wpa_supplicant_wps_event_fail(struct wpa_supplicant *wpa_s,
        eloop_register_timeout(0, 100000, wpas_wps_clear_timeout, wpa_s, NULL);
 
        wpas_notify_wps_event_fail(wpa_s, fail);
-#ifdef CONFIG_P2P
        wpas_p2p_wps_failed(wpa_s, fail);
-#endif /* CONFIG_P2P */
 }
 
 
@@ -652,6 +683,13 @@ static void wpas_wps_reenable_networks_cb(void *eloop_ctx, void *timeout_ctx)
 }
 
 
+int wpas_wps_reenable_networks_pending(struct wpa_supplicant *wpa_s)
+{
+       return eloop_is_timeout_registered(wpas_wps_reenable_networks_cb,
+                                          wpa_s, NULL);
+}
+
+
 static void wpa_supplicant_wps_event_success(struct wpa_supplicant *wpa_s)
 {
        wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_SUCCESS);
@@ -669,9 +707,7 @@ static void wpa_supplicant_wps_event_success(struct wpa_supplicant *wpa_s)
        eloop_register_timeout(10, 0, wpas_wps_reenable_networks_cb, wpa_s,
                               NULL);
 
-#ifdef CONFIG_P2P
        wpas_p2p_wps_success(wpa_s, wpa_s->bssid, 0);
-#endif /* CONFIG_P2P */
 }
 
 
@@ -856,6 +892,18 @@ static void wpa_supplicant_wps_event(void *ctx, enum wps_event event,
 }
 
 
+static int wpa_supplicant_wps_rf_band(void *ctx)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+
+       if (!wpa_s->current_ssid || !wpa_s->assoc_freq)
+               return 0;
+
+       return (wpa_s->assoc_freq > 50000) ? WPS_RF_60GHZ :
+               (wpa_s->assoc_freq > 2484) ? WPS_RF_50GHZ : WPS_RF_24GHZ;
+}
+
+
 enum wps_request_type wpas_wps_get_req_type(struct wpa_ssid *ssid)
 {
        if (eap_is_wps_pbc_enrollee(&ssid->eap) ||
@@ -871,18 +919,23 @@ static void wpas_clear_wps(struct wpa_supplicant *wpa_s)
        int id;
        struct wpa_ssid *ssid, *remove_ssid = NULL, *prev_current;
 
+       wpa_s->after_wps = 0;
+       wpa_s->known_wps_freq = 0;
+
        prev_current = wpa_s->current_ssid;
 
        /* Enable the networks disabled during wpas_wps_reassoc */
        wpas_wps_reenable_networks(wpa_s);
 
        eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL);
+       eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL);
 
        /* Remove any existing WPS network from configuration */
        ssid = wpa_s->conf->ssid;
        while (ssid) {
                if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
                        if (ssid == wpa_s->current_ssid) {
+                               wpa_s->own_disconnect_req = 1;
                                wpa_supplicant_deauthenticate(
                                        wpa_s, WLAN_REASON_DEAUTH_LEAVING);
                        }
@@ -909,14 +962,27 @@ static void wpas_clear_wps(struct wpa_supplicant *wpa_s)
 static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx)
 {
        struct wpa_supplicant *wpa_s = eloop_ctx;
+       union wps_event_data data;
+
        wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_TIMEOUT "Requested operation timed "
                "out");
+       os_memset(&data, 0, sizeof(data));
+       data.fail.config_error = WPS_CFG_MSG_TIMEOUT;
+       data.fail.error_indication = WPS_EI_NO_ERROR;
+       /*
+        * Call wpas_notify_wps_event_fail() directly instead of through
+        * wpa_supplicant_wps_event() which would end up registering unnecessary
+        * timeouts (those are only for the case where the failure happens
+        * during an EAP-WSC exchange).
+        */
+       wpas_notify_wps_event_fail(wpa_s, &data.fail);
        wpas_clear_wps(wpa_s);
 }
 
 
 static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s,
-                                             int registrar, const u8 *bssid)
+                                             int registrar, const u8 *dev_addr,
+                                             const u8 *bssid)
 {
        struct wpa_ssid *ssid;
 
@@ -936,6 +1002,11 @@ static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s,
                return NULL;
        }
 
+#ifdef CONFIG_P2P
+       if (dev_addr)
+               os_memcpy(ssid->go_p2p_dev_addr, dev_addr, ETH_ALEN);
+#endif /* CONFIG_P2P */
+
        if (bssid) {
 #ifndef CONFIG_P2P
                struct wpa_bss *bss;
@@ -986,9 +1057,11 @@ static void wpas_wps_temp_disable(struct wpa_supplicant *wpa_s,
 {
        struct wpa_ssid *ssid;
 
-       if (wpa_s->current_ssid)
+       if (wpa_s->current_ssid) {
+               wpa_s->own_disconnect_req = 1;
                wpa_supplicant_deauthenticate(
                        wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+       }
 
        /* Mark all other networks disabled and trigger reassociation */
        ssid = wpa_s->conf->ssid;
@@ -1016,13 +1089,20 @@ static void wpas_wps_temp_disable(struct wpa_supplicant *wpa_s,
 
 
 static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s,
-                            struct wpa_ssid *selected, const u8 *bssid)
+                            struct wpa_ssid *selected, const u8 *bssid,
+                            int freq)
 {
        struct wpa_bss *bss;
 
+       wpa_s->wps_run++;
+       if (wpa_s->wps_run == 0)
+               wpa_s->wps_run++;
        wpa_s->after_wps = 0;
        wpa_s->known_wps_freq = 0;
-       if (bssid) {
+       if (freq) {
+               wpa_s->after_wps = 5;
+               wpa_s->wps_freq = freq;
+       } else if (bssid) {
                bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
                if (bss && bss->freq > 0) {
                        wpa_s->known_wps_freq = 1;
@@ -1038,6 +1118,8 @@ static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s,
        wpa_s->normal_scans = 0;
        wpa_s->wps_success = 0;
        wpa_s->blacklist_cleared = 0;
+
+       wpa_supplicant_cancel_sched_scan(wpa_s);
        wpa_supplicant_req_scan(wpa_s, 0, 0);
 }
 
@@ -1046,12 +1128,27 @@ int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid,
                       int p2p_group)
 {
        struct wpa_ssid *ssid;
+
+#ifdef CONFIG_AP
+       if (wpa_s->ap_iface) {
+               wpa_printf(MSG_DEBUG,
+                          "WPS: Reject request to start Registrar(as station) operation while AP mode is enabled");
+               return -1;
+       }
+#endif /* CONFIG_AP */
        wpas_clear_wps(wpa_s);
-       ssid = wpas_wps_add_network(wpa_s, 0, bssid);
+       ssid = wpas_wps_add_network(wpa_s, 0, NULL, bssid);
        if (ssid == NULL)
                return -1;
        ssid->temporary = 1;
        ssid->p2p_group = p2p_group;
+       /*
+        * When starting a regular WPS process (not P2P group formation)
+        * the registrar/final station can be either AP or PCP
+        * so use a "don't care" value for the pbss flag.
+        */
+       if (!p2p_group)
+               ssid->pbss = 2;
 #ifdef CONFIG_P2P
        if (p2p_group && wpa_s->go_params && wpa_s->go_params->ssid_len) {
                ssid->ssid = os_zalloc(wpa_s->go_params->ssid_len + 1);
@@ -1059,6 +1156,10 @@ int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid,
                        ssid->ssid_len = wpa_s->go_params->ssid_len;
                        os_memcpy(ssid->ssid, wpa_s->go_params->ssid,
                                  ssid->ssid_len);
+                       if (wpa_s->go_params->freq > 56160) {
+                               /* P2P in 60 GHz uses PBSS */
+                               ssid->pbss = 1;
+                       }
                        wpa_hexdump_ascii(MSG_DEBUG, "WPS: Use specific AP "
                                          "SSID", ssid->ssid, ssid->ssid_len);
                }
@@ -1070,56 +1171,131 @@ int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid,
                ssid->eap.fragment_size = wpa_s->wps_fragment_size;
        eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
                               wpa_s, NULL);
-       wpas_wps_reassoc(wpa_s, ssid, bssid);
+       wpas_wps_reassoc(wpa_s, ssid, bssid, 0);
        return 0;
 }
 
 
-int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
-                      const char *pin, int p2p_group, u16 dev_pw_id)
+static int wpas_wps_start_dev_pw(struct wpa_supplicant *wpa_s,
+                                const u8 *dev_addr, const u8 *bssid,
+                                const char *pin, int p2p_group, u16 dev_pw_id,
+                                const u8 *peer_pubkey_hash,
+                                const u8 *ssid_val, size_t ssid_len, int freq)
 {
        struct wpa_ssid *ssid;
-       char val[128];
+       char val[128 + 2 * WPS_OOB_PUBKEY_HASH_LEN];
        unsigned int rpin = 0;
+       char hash[2 * WPS_OOB_PUBKEY_HASH_LEN + 10];
 
+#ifdef CONFIG_AP
+       if (wpa_s->ap_iface) {
+               wpa_printf(MSG_DEBUG,
+                          "WPS: Reject request to start Registrar(as station) operation while AP mode is enabled");
+               return -1;
+       }
+#endif /* CONFIG_AP */
        wpas_clear_wps(wpa_s);
-       ssid = wpas_wps_add_network(wpa_s, 0, bssid);
-       if (ssid == NULL)
+       if (bssid && is_zero_ether_addr(bssid))
+               bssid = NULL;
+       ssid = wpas_wps_add_network(wpa_s, 0, dev_addr, bssid);
+       if (ssid == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: Could not add network");
                return -1;
+       }
        ssid->temporary = 1;
        ssid->p2p_group = p2p_group;
+       /*
+        * When starting a regular WPS process (not P2P group formation)
+        * the registrar/final station can be either AP or PCP
+        * so use a "don't care" value for the pbss flag.
+        */
+       if (!p2p_group)
+               ssid->pbss = 2;
+       if (ssid_val) {
+               ssid->ssid = os_malloc(ssid_len);
+               if (ssid->ssid) {
+                       os_memcpy(ssid->ssid, ssid_val, ssid_len);
+                       ssid->ssid_len = ssid_len;
+               }
+       }
+       if (peer_pubkey_hash) {
+               os_memcpy(hash, " pkhash=", 8);
+               wpa_snprintf_hex_uppercase(hash + 8, sizeof(hash) - 8,
+                                          peer_pubkey_hash,
+                                          WPS_OOB_PUBKEY_HASH_LEN);
+       } else {
+               hash[0] = '\0';
+       }
 #ifdef CONFIG_P2P
        if (p2p_group && wpa_s->go_params && wpa_s->go_params->ssid_len) {
+               os_free(ssid->ssid);
                ssid->ssid = os_zalloc(wpa_s->go_params->ssid_len + 1);
                if (ssid->ssid) {
                        ssid->ssid_len = wpa_s->go_params->ssid_len;
                        os_memcpy(ssid->ssid, wpa_s->go_params->ssid,
                                  ssid->ssid_len);
+                       if (wpa_s->go_params->freq > 56160) {
+                               /* P2P in 60 GHz uses PBSS */
+                               ssid->pbss = 1;
+                       }
                        wpa_hexdump_ascii(MSG_DEBUG, "WPS: Use specific AP "
                                          "SSID", ssid->ssid, ssid->ssid_len);
                }
        }
 #endif /* CONFIG_P2P */
        if (pin)
-               os_snprintf(val, sizeof(val), "\"pin=%s dev_pw_id=%u\"",
-                           pin, dev_pw_id);
-       else {
-               rpin = wps_generate_pin();
-               os_snprintf(val, sizeof(val), "\"pin=%08d dev_pw_id=%u\"",
-                           rpin, dev_pw_id);
+               os_snprintf(val, sizeof(val), "\"pin=%s dev_pw_id=%u%s\"",
+                           pin, dev_pw_id, hash);
+       else if (pin == NULL && dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) {
+               os_snprintf(val, sizeof(val), "\"dev_pw_id=%u%s\"",
+                           dev_pw_id, hash);
+       } else {
+               if (wps_generate_pin(&rpin) < 0) {
+                       wpa_printf(MSG_DEBUG, "WPS: Could not generate PIN");
+                       return -1;
+               }
+               os_snprintf(val, sizeof(val), "\"pin=%08d dev_pw_id=%u%s\"",
+                           rpin, dev_pw_id, hash);
        }
-       if (wpa_config_set(ssid, "phase1", val, 0) < 0)
+       if (wpa_config_set(ssid, "phase1", val, 0) < 0) {
+               wpa_printf(MSG_DEBUG, "WPS: Failed to set phase1 '%s'", val);
                return -1;
+       }
        if (wpa_s->wps_fragment_size)
                ssid->eap.fragment_size = wpa_s->wps_fragment_size;
        eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
                               wpa_s, NULL);
        wpa_s->wps_ap_iter = 1;
-       wpas_wps_reassoc(wpa_s, ssid, bssid);
+       wpas_wps_reassoc(wpa_s, ssid, bssid, freq);
        return rpin;
 }
 
 
+int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
+                      const char *pin, int p2p_group, u16 dev_pw_id)
+{
+       os_get_reltime(&wpa_s->wps_pin_start_time);
+       return wpas_wps_start_dev_pw(wpa_s, NULL, bssid, pin, p2p_group,
+                                    dev_pw_id, NULL, NULL, 0, 0);
+}
+
+
+void wpas_wps_pbc_overlap(struct wpa_supplicant *wpa_s)
+{
+       union wps_event_data data;
+
+       os_memset(&data, 0, sizeof(data));
+       data.fail.config_error = WPS_CFG_MULTIPLE_PBC_DETECTED;
+       data.fail.error_indication = WPS_EI_NO_ERROR;
+       /*
+        * Call wpas_notify_wps_event_fail() directly instead of through
+        * wpa_supplicant_wps_event() which would end up registering unnecessary
+        * timeouts (those are only for the case where the failure happens
+        * during an EAP-WSC exchange).
+        */
+       wpas_notify_wps_event_fail(wpa_s, &data.fail);
+}
+
 /* Cancel the wps pbc/pin requests */
 int wpas_wps_cancel(struct wpa_supplicant *wpa_s)
 {
@@ -1138,14 +1314,20 @@ int wpas_wps_cancel(struct wpa_supplicant *wpa_s)
        } else if (wpa_s->wpa_state >= WPA_ASSOCIATED) {
                wpa_printf(MSG_DEBUG, "WPS: Cancel operation - "
                           "deauthenticate");
+               wpa_s->own_disconnect_req = 1;
                wpa_supplicant_deauthenticate(wpa_s,
                                              WLAN_REASON_DEAUTH_LEAVING);
                wpas_clear_wps(wpa_s);
        } else {
                wpas_wps_reenable_networks(wpa_s);
                wpas_wps_clear_ap_info(wpa_s);
+               if (eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL) >
+                   0)
+                       wpas_clear_wps(wpa_s);
        }
 
+       wpa_s->after_wps = 0;
+
        return 0;
 }
 
@@ -1158,17 +1340,24 @@ int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
        char *pos, *end;
        int res;
 
+#ifdef CONFIG_AP
+       if (wpa_s->ap_iface) {
+               wpa_printf(MSG_DEBUG,
+                          "WPS: Reject request to start Registrar(as station) operation while AP mode is enabled");
+               return -1;
+       }
+#endif /* CONFIG_AP */
        if (!pin)
                return -1;
        wpas_clear_wps(wpa_s);
-       ssid = wpas_wps_add_network(wpa_s, 1, bssid);
+       ssid = wpas_wps_add_network(wpa_s, 1, NULL, bssid);
        if (ssid == NULL)
                return -1;
        ssid->temporary = 1;
        pos = val;
        end = pos + sizeof(val);
        res = os_snprintf(pos, end - pos, "\"pin=%s", pin);
-       if (res < 0 || res >= end - pos)
+       if (os_snprintf_error(end - pos, res))
                return -1;
        pos += res;
        if (settings) {
@@ -1176,12 +1365,12 @@ int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
                                  "new_encr=%s new_key=%s",
                                  settings->ssid_hex, settings->auth,
                                  settings->encr, settings->key_hex);
-               if (res < 0 || res >= end - pos)
+               if (os_snprintf_error(end - pos, res))
                        return -1;
                pos += res;
        }
        res = os_snprintf(pos, end - pos, "\"");
-       if (res < 0 || res >= end - pos)
+       if (os_snprintf_error(end - pos, res))
                return -1;
        if (wpa_config_set(ssid, "phase1", val, 0) < 0)
                return -1;
@@ -1189,16 +1378,25 @@ int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
                ssid->eap.fragment_size = wpa_s->wps_fragment_size;
        eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
                               wpa_s, NULL);
-       wpas_wps_reassoc(wpa_s, ssid, bssid);
+       wpas_wps_reassoc(wpa_s, ssid, bssid, 0);
        return 0;
 }
 
 
-static int wpas_wps_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *psk,
+static int wpas_wps_new_psk_cb(void *ctx, const u8 *mac_addr,
+                              const u8 *p2p_dev_addr, const u8 *psk,
                               size_t psk_len)
 {
-       wpa_printf(MSG_DEBUG, "WPS: Received new WPA/WPA2-PSK from WPS for "
-                  "STA " MACSTR, MAC2STR(mac_addr));
+       if (is_zero_ether_addr(p2p_dev_addr)) {
+               wpa_printf(MSG_DEBUG,
+                          "Received new WPA/WPA2-PSK from WPS for STA " MACSTR,
+                          MAC2STR(mac_addr));
+       } else {
+               wpa_printf(MSG_DEBUG,
+                          "Received new WPA/WPA2-PSK from WPS for STA " MACSTR
+                          " P2P Device Addr " MACSTR,
+                          MAC2STR(mac_addr), MAC2STR(p2p_dev_addr));
+       }
        wpa_hexdump_key(MSG_DEBUG, "Per-device PSK", psk, psk_len);
 
        /* TODO */
@@ -1223,7 +1421,7 @@ static void wpas_wps_pin_needed_cb(void *ctx, const u8 *uuid_e,
                          dev->model_number, dev->serial_number,
                          wps_dev_type_bin2str(dev->pri_dev_type, devtype,
                                               sizeof(devtype)));
-       if (len > 0 && len < (int) sizeof(txt))
+       if (!os_snprintf_error(sizeof(txt), len))
                wpa_printf(MSG_INFO, "%s", txt);
 }
 
@@ -1247,7 +1445,6 @@ static void wpas_wps_set_sel_reg_cb(void *ctx, int sel_reg, u16 dev_passwd_id,
 
 static u16 wps_fix_config_methods(u16 config_methods)
 {
-#ifdef CONFIG_WPS2
        if ((config_methods &
             (WPS_CONFIG_DISPLAY | WPS_CONFIG_VIRT_DISPLAY |
              WPS_CONFIG_PHY_DISPLAY)) == WPS_CONFIG_DISPLAY) {
@@ -1262,7 +1459,6 @@ static u16 wps_fix_config_methods(u16 config_methods)
                           "virtual_push_button for WPS 2.0 compliance");
                config_methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
        }
-#endif /* CONFIG_WPS2 */
 
        return config_methods;
 }
@@ -1271,7 +1467,9 @@ static u16 wps_fix_config_methods(u16 config_methods)
 static void wpas_wps_set_uuid(struct wpa_supplicant *wpa_s,
                              struct wps_context *wps)
 {
-       wpa_printf(MSG_DEBUG, "WPS: Set UUID for interface %s", wpa_s->ifname);
+       char buf[50];
+       const char *src;
+
        if (is_nil_uuid(wpa_s->conf->uuid)) {
                struct wpa_supplicant *first;
                first = wpa_s->global->ifaces;
@@ -1282,18 +1480,18 @@ static void wpas_wps_set_uuid(struct wpa_supplicant *wpa_s,
                                os_memcpy(wps->uuid,
                                          wpa_s->global->ifaces->wps->uuid,
                                          WPS_UUID_LEN);
-                       wpa_hexdump(MSG_DEBUG, "WPS: UUID from the first "
-                                   "interface", wps->uuid, WPS_UUID_LEN);
+                       src = "from the first interface";
                } else {
                        uuid_gen_mac_addr(wpa_s->own_addr, wps->uuid);
-                       wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC "
-                                   "address", wps->uuid, WPS_UUID_LEN);
+                       src = "based on MAC address";
                }
        } else {
                os_memcpy(wps->uuid, wpa_s->conf->uuid, WPS_UUID_LEN);
-               wpa_hexdump(MSG_DEBUG, "WPS: UUID based on configuration",
-                           wps->uuid, WPS_UUID_LEN);
+               src = "based on configuration";
        }
+
+       uuid_bin2str(wps->uuid, buf, sizeof(buf));
+       wpa_dbg(wpa_s, MSG_DEBUG, "WPS: UUID %s: %s", src, buf);
 }
 
 
@@ -1327,6 +1525,7 @@ int wpas_wps_init(struct wpa_supplicant *wpa_s)
 
        wps->cred_cb = wpa_supplicant_wps_cred;
        wps->event_cb = wpa_supplicant_wps_event;
+       wps->rf_band_cb = wpa_supplicant_wps_rf_band;
        wps->cb_ctx = wpa_s;
 
        wps->dev.device_name = wpa_s->conf->device_name;
@@ -1363,6 +1562,8 @@ int wpas_wps_init(struct wpa_supplicant *wpa_s)
                                wps->dev.rf_bands |= WPS_RF_24GHZ;
                        else if (modes[m].mode == HOSTAPD_MODE_IEEE80211A)
                                wps->dev.rf_bands |= WPS_RF_50GHZ;
+                       else if (modes[m].mode == HOSTAPD_MODE_IEEE80211AD)
+                               wps->dev.rf_bands |= WPS_RF_60GHZ;
                }
        }
        if (wps->dev.rf_bands == 0) {
@@ -1397,19 +1598,39 @@ int wpas_wps_init(struct wpa_supplicant *wpa_s)
 }
 
 
+#ifdef CONFIG_WPS_ER
+static void wpas_wps_nfc_clear(struct wps_context *wps)
+{
+       wps->ap_nfc_dev_pw_id = 0;
+       wpabuf_free(wps->ap_nfc_dh_pubkey);
+       wps->ap_nfc_dh_pubkey = NULL;
+       wpabuf_free(wps->ap_nfc_dh_privkey);
+       wps->ap_nfc_dh_privkey = NULL;
+       wpabuf_free(wps->ap_nfc_dev_pw);
+       wps->ap_nfc_dev_pw = NULL;
+}
+#endif /* CONFIG_WPS_ER */
+
+
 void wpas_wps_deinit(struct wpa_supplicant *wpa_s)
 {
+       wpas_wps_assoc_with_cred_cancel(wpa_s);
        eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL);
        eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL);
        eloop_cancel_timeout(wpas_wps_reenable_networks_cb, wpa_s, NULL);
        wpas_wps_clear_ap_info(wpa_s);
 
+#ifdef CONFIG_P2P
+       eloop_cancel_timeout(wpas_p2p_pbc_overlap_cb, wpa_s, NULL);
+#endif /* CONFIG_P2P */
+
        if (wpa_s->wps == NULL)
                return;
 
 #ifdef CONFIG_WPS_ER
        wps_er_deinit(wpa_s->wps_er, NULL, NULL);
        wpa_s->wps_er = NULL;
+       wpas_wps_nfc_clear(wpa_s->wps);
 #endif /* CONFIG_WPS_ER */
 
        wps_registrar_deinit(wpa_s->wps->registrar);
@@ -1465,9 +1686,15 @@ int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s,
                 * external Registrar.
                 */
                if (!wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1)) {
-                       if (wpa_s->scan_runs < WPS_PIN_SCAN_IGNORE_SEL_REG) {
-                               wpa_printf(MSG_DEBUG, "   skip - WPS AP "
-                                          "without active PIN Registrar");
+                       struct os_reltime age;
+
+                       os_reltime_age(&wpa_s->wps_pin_start_time, &age);
+
+                       if (wpa_s->scan_runs < WPS_PIN_SCAN_IGNORE_SEL_REG ||
+                           age.sec < WPS_PIN_TIME_IGNORE_SEL_REG) {
+                               wpa_printf(MSG_DEBUG,
+                                          "   skip - WPS AP without active PIN Registrar (scan_runs=%d age=%d)",
+                                          wpa_s->scan_runs, (int) age.sec);
                                wpabuf_free(wps_ie);
                                return 0;
                        }
@@ -1550,10 +1777,10 @@ int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s,
 int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s,
                              struct wpa_bss *selected, struct wpa_ssid *ssid)
 {
-       const u8 *sel_uuid, *uuid;
+       const u8 *sel_uuid;
        struct wpabuf *wps_ie;
        int ret = 0;
-       struct wpa_bss *bss;
+       size_t i;
 
        if (!eap_is_wps_pbc_enrollee(&ssid->eap))
                return 0;
@@ -1574,36 +1801,28 @@ int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s,
                sel_uuid = NULL;
        }
 
-       dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
-               struct wpabuf *ie;
-               if (bss == selected)
-                       continue;
-               ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
-               if (!ie)
-                       continue;
-               if (!wps_is_selected_pbc_registrar(ie)) {
-                       wpabuf_free(ie);
+       for (i = 0; i < wpa_s->num_wps_ap; i++) {
+               struct wps_ap_info *ap = &wpa_s->wps_ap[i];
+
+               if (!ap->pbc_active ||
+                   os_memcmp(selected->bssid, ap->bssid, ETH_ALEN) == 0)
                        continue;
-               }
+
                wpa_printf(MSG_DEBUG, "WPS: Another BSS in active PBC mode: "
-                          MACSTR, MAC2STR(bss->bssid));
-               uuid = wps_get_uuid_e(ie);
+                          MACSTR, MAC2STR(ap->bssid));
                wpa_hexdump(MSG_DEBUG, "WPS: UUID of the other BSS",
-                           uuid, UUID_LEN);
-               if (sel_uuid == NULL || uuid == NULL ||
-                   os_memcmp(sel_uuid, uuid, UUID_LEN) != 0) {
+                           ap->uuid, UUID_LEN);
+               if (sel_uuid == NULL ||
+                   os_memcmp(sel_uuid, ap->uuid, UUID_LEN) != 0) {
                        ret = 1; /* PBC overlap */
                        wpa_msg(wpa_s, MSG_INFO, "WPS: PBC overlap detected: "
                                MACSTR " and " MACSTR,
                                MAC2STR(selected->bssid),
-                               MAC2STR(bss->bssid));
-                       wpabuf_free(ie);
+                               MAC2STR(ap->bssid));
                        break;
                }
 
                /* TODO: verify that this is reasonable dual-band situation */
-
-               wpabuf_free(ie);
        }
 
        wpabuf_free(wps_ie);
@@ -1693,13 +1912,12 @@ int wpas_wps_er_start(struct wpa_supplicant *wpa_s, const char *filter)
 }
 
 
-int wpas_wps_er_stop(struct wpa_supplicant *wpa_s)
+void wpas_wps_er_stop(struct wpa_supplicant *wpa_s)
 {
 #ifdef CONFIG_WPS_ER
        wps_er_deinit(wpa_s->wps_er, NULL, NULL);
        wpa_s->wps_er = NULL;
 #endif /* CONFIG_WPS_ER */
-       return 0;
 }
 
 
@@ -1763,7 +1981,7 @@ static int wpas_wps_network_to_cred(struct wpa_ssid *ssid,
                                    struct wps_credential *cred)
 {
        os_memset(cred, 0, sizeof(*cred));
-       if (ssid->ssid_len > 32)
+       if (ssid->ssid_len > SSID_MAX_LEN)
                return -1;
        os_memcpy(cred->ssid, ssid->ssid, ssid->ssid_len);
        cred->ssid_len = ssid->ssid_len;
@@ -1800,6 +2018,7 @@ int wpas_wps_er_set_config(struct wpa_supplicant *wpa_s, const char *uuid,
        u8 addr[ETH_ALEN], *use_addr = NULL;
        struct wpa_ssid *ssid;
        struct wps_credential cred;
+       int ret;
 
        if (uuid_str2bin(uuid, u) == 0)
                use_uuid = u;
@@ -1813,7 +2032,9 @@ int wpas_wps_er_set_config(struct wpa_supplicant *wpa_s, const char *uuid,
 
        if (wpas_wps_network_to_cred(ssid, &cred) < 0)
                return -1;
-       return wps_er_set_config(wpa_s->wps_er, use_uuid, use_addr, &cred);
+       ret = wps_er_set_config(wpa_s->wps_er, use_uuid, use_addr, &cred);
+       os_memset(&cred, 0, sizeof(cred));
+       return ret;
 }
 
 
@@ -1859,8 +2080,10 @@ int wpas_wps_er_config(struct wpa_supplicant *wpa_s, const char *uuid,
 
        if (os_strcmp(settings->encr, "NONE") == 0)
                cred.encr_type = WPS_ENCR_NONE;
+#ifdef CONFIG_TESTING_OPTIONS
        else if (os_strcmp(settings->encr, "WEP") == 0)
                cred.encr_type = WPS_ENCR_WEP;
+#endif /* CONFIG_TESTING_OPTIONS */
        else if (os_strcmp(settings->encr, "TKIP") == 0)
                cred.encr_type = WPS_ENCR_TKIP;
        else if (os_strcmp(settings->encr, "CCMP") == 0)
@@ -1931,19 +2154,6 @@ int wpas_wps_terminate_pending(struct wpa_supplicant *wpa_s)
 }
 
 
-int wpas_wps_in_progress(struct wpa_supplicant *wpa_s)
-{
-       struct wpa_ssid *ssid;
-
-       for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
-               if (!ssid->disabled && ssid->key_mgmt == WPA_KEY_MGMT_WPS)
-                       return 1;
-       }
-
-       return 0;
-}
-
-
 void wpas_wps_update_config(struct wpa_supplicant *wpa_s)
 {
        struct wps_context *wps = wpa_s->wps;
@@ -2069,15 +2279,32 @@ struct wpabuf * wpas_wps_nfc_token(struct wpa_supplicant *wpa_s, int ndef)
 }
 
 
-int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *bssid)
+int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *go_dev_addr,
+                      const u8 *bssid,
+                      const struct wpabuf *dev_pw, u16 dev_pw_id,
+                      int p2p_group, const u8 *peer_pubkey_hash,
+                      const u8 *ssid, size_t ssid_len, int freq)
 {
        struct wps_context *wps = wpa_s->wps;
        char pw[32 * 2 + 1];
 
+       if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER && dev_pw == NULL) {
+               dev_pw = wpa_s->conf->wps_nfc_dev_pw;
+               dev_pw_id = wpa_s->conf->wps_nfc_dev_pw_id;
+       }
+
        if (wpa_s->conf->wps_nfc_dh_pubkey == NULL ||
-           wpa_s->conf->wps_nfc_dh_privkey == NULL ||
-           wpa_s->conf->wps_nfc_dev_pw == NULL)
+           wpa_s->conf->wps_nfc_dh_privkey == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: Missing DH params - "
+                          "cannot start NFC-triggered connection");
+               return -1;
+       }
+
+       if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER && dev_pw == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: Missing Device Password (id=%u) - "
+                          "cannot start NFC-triggered connection", dev_pw_id);
                return -1;
+       }
 
        dh5_free(wps->dh_ctx);
        wpabuf_free(wps->dh_pubkey);
@@ -2090,6 +2317,7 @@ int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *bssid)
                wps->dh_pubkey = NULL;
                wpabuf_free(wps->dh_privkey);
                wps->dh_privkey = NULL;
+               wpa_printf(MSG_DEBUG, "WPS: Failed to get DH priv/pub key");
                return -1;
        }
        wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, wps->dh_pubkey);
@@ -2098,22 +2326,25 @@ int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *bssid)
                wps->dh_pubkey = NULL;
                wpabuf_free(wps->dh_privkey);
                wps->dh_privkey = NULL;
+               wpa_printf(MSG_DEBUG, "WPS: Failed to initialize DH context");
                return -1;
        }
 
-       wpa_snprintf_hex_uppercase(pw, sizeof(pw),
-                                  wpabuf_head(wpa_s->conf->wps_nfc_dev_pw),
-                                  wpabuf_len(wpa_s->conf->wps_nfc_dev_pw));
-       return wpas_wps_start_pin(wpa_s, bssid, pw, 0,
-                                 wpa_s->conf->wps_nfc_dev_pw_id);
+       if (dev_pw) {
+               wpa_snprintf_hex_uppercase(pw, sizeof(pw),
+                                          wpabuf_head(dev_pw),
+                                          wpabuf_len(dev_pw));
+       }
+       return wpas_wps_start_dev_pw(wpa_s, go_dev_addr, bssid,
+                                    dev_pw ? pw : NULL,
+                                    p2p_group, dev_pw_id, peer_pubkey_hash,
+                                    ssid, ssid_len, freq);
 }
 
 
 static int wpas_wps_use_cred(struct wpa_supplicant *wpa_s,
                             struct wps_parse_attr *attr)
 {
-       wpa_s->wps_ap_channel = 0;
-
        /*
         * Disable existing networks temporarily to allow the newly learned
         * credential to be preferred. Enable the temporarily disabled networks
@@ -2129,12 +2360,8 @@ static int wpas_wps_use_cred(struct wpa_supplicant *wpa_s,
        if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
                return 0;
 
-       wpa_printf(MSG_DEBUG, "WPS: Request reconnection with new network "
-                  "based on the received credential added");
-       wpa_s->normal_scans = 0;
-       wpa_supplicant_reinit_autoscan(wpa_s);
-       if (wpa_s->wps_ap_channel) {
-               u16 chan = wpa_s->wps_ap_channel;
+       if (attr->ap_channel) {
+               u16 chan = WPA_GET_BE16(attr->ap_channel);
                int freq = 0;
 
                if (chan >= 1 && chan <= 13)
@@ -2145,14 +2372,21 @@ static int wpas_wps_use_cred(struct wpa_supplicant *wpa_s,
                        freq = 5000 + 5 * chan;
 
                if (freq) {
-                       wpa_printf(MSG_DEBUG, "WPS: Credential indicated "
-                                  "AP channel %u -> %u MHz", chan, freq);
+                       wpa_printf(MSG_DEBUG, "WPS: Credential container indicated AP channel %u -> %u MHz",
+                                  chan, freq);
                        wpa_s->after_wps = 5;
                        wpa_s->wps_freq = freq;
                }
        }
+
+       wpa_printf(MSG_DEBUG, "WPS: Request reconnection with new network "
+                  "based on the received credential added");
+       wpa_s->normal_scans = 0;
+       wpa_supplicant_reinit_autoscan(wpa_s);
        wpa_s->disconnected = 0;
        wpa_s->reassociate = 1;
+
+       wpa_supplicant_cancel_sched_scan(wpa_s);
        wpa_supplicant_req_scan(wpa_s, 0, 0);
 
        return 0;
@@ -2196,7 +2430,7 @@ static int wpas_wps_nfc_tag_process(struct wpa_supplicant *wpa_s,
 
 
 int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s,
-                         const struct wpabuf *data)
+                         const struct wpabuf *data, int forced_freq)
 {
        const struct wpabuf *wps = data;
        struct wpabuf *tmp = NULL;
@@ -2209,6 +2443,15 @@ int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s,
                /* Assume this contains full NDEF record */
                tmp = ndef_parse_wifi(data);
                if (tmp == NULL) {
+#ifdef CONFIG_P2P
+                       tmp = ndef_parse_p2p(data);
+                       if (tmp) {
+                               ret = wpas_p2p_nfc_tag_process(wpa_s, tmp,
+                                                              forced_freq);
+                               wpabuf_free(tmp);
+                               return ret;
+                       }
+#endif /* CONFIG_P2P */
                        wpa_printf(MSG_DEBUG, "WPS: Could not parse NDEF");
                        return -1;
                }
@@ -2221,24 +2464,45 @@ int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s,
 }
 
 
-struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s, int cr)
+struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s,
+                                         int ndef)
 {
-       if (cr)
-               return ndef_build_wifi_hc(1);
-       return ndef_build_wifi_hr();
+       struct wpabuf *ret;
+
+       if (wpa_s->conf->wps_nfc_dh_pubkey == NULL &&
+           wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey,
+                          &wpa_s->conf->wps_nfc_dh_privkey) < 0)
+               return NULL;
+
+       ret = wps_build_nfc_handover_req(wpa_s->wps,
+                                        wpa_s->conf->wps_nfc_dh_pubkey);
+
+       if (ndef && ret) {
+               struct wpabuf *tmp;
+               tmp = ndef_build_wifi(ret);
+               wpabuf_free(ret);
+               if (tmp == NULL)
+                       return NULL;
+               ret = tmp;
+       }
+
+       return ret;
 }
 
 
 #ifdef CONFIG_WPS_NFC
-struct wpabuf * wpas_wps_er_nfc_handover_sel(struct wpa_supplicant *wpa_s,
-                                            int ndef, const char *uuid)
+
+static struct wpabuf *
+wpas_wps_er_nfc_handover_sel(struct wpa_supplicant *wpa_s, int ndef,
+                            const char *uuid)
 {
 #ifdef CONFIG_WPS_ER
        struct wpabuf *ret;
        u8 u[UUID_LEN], *use_uuid = NULL;
        u8 addr[ETH_ALEN], *use_addr = NULL;
+       struct wps_context *wps = wpa_s->wps;
 
-       if (!wpa_s->wps_er)
+       if (wps == NULL)
                return NULL;
 
        if (uuid == NULL)
@@ -2250,11 +2514,23 @@ struct wpabuf * wpas_wps_er_nfc_handover_sel(struct wpa_supplicant *wpa_s,
        else
                return NULL;
 
-       /*
-        * Handover Select carrier record for WPS uses the same format as
-        * configuration token.
-        */
-       ret = wps_er_nfc_config_token(wpa_s->wps_er, use_uuid, use_addr);
+       if (wpa_s->conf->wps_nfc_dh_pubkey == NULL) {
+               if (wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey,
+                                  &wpa_s->conf->wps_nfc_dh_privkey) < 0)
+                       return NULL;
+       }
+
+       wpas_wps_nfc_clear(wps);
+       wps->ap_nfc_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER;
+       wps->ap_nfc_dh_pubkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_pubkey);
+       wps->ap_nfc_dh_privkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_privkey);
+       if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey) {
+               wpas_wps_nfc_clear(wps);
+               return NULL;
+       }
+
+       ret = wps_er_nfc_handover_sel(wpa_s->wps_er, wpa_s->wps, use_uuid,
+                                     use_addr, wpa_s->conf->wps_nfc_dh_pubkey);
        if (ndef && ret) {
                struct wpabuf *tmp;
                tmp = ndef_build_wifi(ret);
@@ -2285,29 +2561,129 @@ struct wpabuf * wpas_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s,
 }
 
 
-int wpas_wps_nfc_rx_handover_req(struct wpa_supplicant *wpa_s,
-                                const struct wpabuf *data)
-{
-       /* TODO */
-       return -1;
-}
-
-
-int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s,
-                                const struct wpabuf *data)
+static int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s,
+                                       const struct wpabuf *data)
 {
        struct wpabuf *wps;
-       int ret;
+       int ret = -1;
+       u16 wsc_len;
+       const u8 *pos;
+       struct wpabuf msg;
+       struct wps_parse_attr attr;
+       u16 dev_pw_id;
+       const u8 *bssid = NULL;
+       int freq = 0;
 
        wps = ndef_parse_wifi(data);
        if (wps == NULL)
                return -1;
        wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc "
                   "payload from NFC connection handover");
-       wpa_hexdump_buf_key(MSG_DEBUG, "WPS: NFC payload", wps);
-       ret = wpas_wps_nfc_tag_process(wpa_s, wps);
-       wpabuf_free(wps);
+       wpa_hexdump_buf(MSG_DEBUG, "WPS: NFC payload", wps);
+       if (wpabuf_len(wps) < 2) {
+               wpa_printf(MSG_DEBUG, "WPS: Too short Wi-Fi Handover Select "
+                          "Message");
+               goto out;
+       }
+       pos = wpabuf_head(wps);
+       wsc_len = WPA_GET_BE16(pos);
+       if (wsc_len > wpabuf_len(wps) - 2) {
+               wpa_printf(MSG_DEBUG, "WPS: Invalid WSC attribute length (%u) "
+                          "in Wi-Fi Handover Select Message", wsc_len);
+               goto out;
+       }
+       pos += 2;
+
+       wpa_hexdump(MSG_DEBUG,
+                   "WPS: WSC attributes in Wi-Fi Handover Select Message",
+                   pos, wsc_len);
+       if (wsc_len < wpabuf_len(wps) - 2) {
+               wpa_hexdump(MSG_DEBUG,
+                           "WPS: Ignore extra data after WSC attributes",
+                           pos + wsc_len, wpabuf_len(wps) - 2 - wsc_len);
+       }
+
+       wpabuf_set(&msg, pos, wsc_len);
+       ret = wps_parse_msg(&msg, &attr);
+       if (ret < 0) {
+               wpa_printf(MSG_DEBUG, "WPS: Could not parse WSC attributes in "
+                          "Wi-Fi Handover Select Message");
+               goto out;
+       }
+
+       if (attr.oob_dev_password == NULL ||
+           attr.oob_dev_password_len < WPS_OOB_PUBKEY_HASH_LEN + 2) {
+               wpa_printf(MSG_DEBUG, "WPS: No Out-of-Band Device Password "
+                          "included in Wi-Fi Handover Select Message");
+               ret = -1;
+               goto out;
+       }
+
+       if (attr.ssid == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No SSID included in Wi-Fi Handover "
+                          "Select Message");
+               ret = -1;
+               goto out;
+       }
+
+       wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", attr.ssid, attr.ssid_len);
+
+       if (attr.mac_addr) {
+               bssid = attr.mac_addr;
+               wpa_printf(MSG_DEBUG, "WPS: MAC Address (BSSID): " MACSTR,
+                          MAC2STR(bssid));
+       }
+
+       if (attr.rf_bands)
+               wpa_printf(MSG_DEBUG, "WPS: RF Bands: %d", *attr.rf_bands);
+
+       if (attr.ap_channel) {
+               u16 chan = WPA_GET_BE16(attr.ap_channel);
+
+               wpa_printf(MSG_DEBUG, "WPS: AP Channel: %d", chan);
 
+               if (chan >= 1 && chan <= 13 &&
+                   (attr.rf_bands == NULL || *attr.rf_bands & WPS_RF_24GHZ))
+                       freq = 2407 + 5 * chan;
+               else if (chan == 14 &&
+                        (attr.rf_bands == NULL ||
+                         *attr.rf_bands & WPS_RF_24GHZ))
+                       freq = 2484;
+               else if (chan >= 30 &&
+                        (attr.rf_bands == NULL ||
+                         *attr.rf_bands & WPS_RF_50GHZ))
+                       freq = 5000 + 5 * chan;
+               else if (chan >= 1 && chan <= 4 &&
+                        (attr.rf_bands == NULL ||
+                         *attr.rf_bands & WPS_RF_60GHZ))
+                       freq = 56160 + 2160 * chan;
+
+               if (freq) {
+                       wpa_printf(MSG_DEBUG,
+                                  "WPS: AP indicated channel %u -> %u MHz",
+                                  chan, freq);
+               }
+       }
+
+       wpa_hexdump(MSG_DEBUG, "WPS: Out-of-Band Device Password",
+                   attr.oob_dev_password, attr.oob_dev_password_len);
+       dev_pw_id = WPA_GET_BE16(attr.oob_dev_password +
+                                WPS_OOB_PUBKEY_HASH_LEN);
+       if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER) {
+               wpa_printf(MSG_DEBUG, "WPS: Unexpected OOB Device Password ID "
+                          "%u in Wi-Fi Handover Select Message", dev_pw_id);
+               ret = -1;
+               goto out;
+       }
+       wpa_hexdump(MSG_DEBUG, "WPS: AP Public Key hash",
+                   attr.oob_dev_password, WPS_OOB_PUBKEY_HASH_LEN);
+
+       ret = wpas_wps_start_nfc(wpa_s, NULL, bssid, NULL, dev_pw_id, 0,
+                                attr.oob_dev_password,
+                                attr.ssid, attr.ssid_len, freq);
+
+out:
+       wpabuf_free(wps);
        return ret;
 }
 
@@ -2322,15 +2698,107 @@ int wpas_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
        return wpas_wps_nfc_rx_handover_sel(wpa_s, sel);
 }
 
-#endif /* CONFIG_WPS_NFC */
 
+int wpas_er_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
+                                   const struct wpabuf *req,
+                                   const struct wpabuf *sel)
+{
+       struct wpabuf *wps;
+       int ret = -1;
+       u16 wsc_len;
+       const u8 *pos;
+       struct wpabuf msg;
+       struct wps_parse_attr attr;
+       u16 dev_pw_id;
+
+       /*
+        * Enrollee/station is always initiator of the NFC connection handover,
+        * so use the request message here to find Enrollee public key hash.
+        */
+       wps = ndef_parse_wifi(req);
+       if (wps == NULL)
+               return -1;
+       wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc "
+                  "payload from NFC connection handover");
+       wpa_hexdump_buf(MSG_DEBUG, "WPS: NFC payload", wps);
+       if (wpabuf_len(wps) < 2) {
+               wpa_printf(MSG_DEBUG, "WPS: Too short Wi-Fi Handover Request "
+                          "Message");
+               goto out;
+       }
+       pos = wpabuf_head(wps);
+       wsc_len = WPA_GET_BE16(pos);
+       if (wsc_len > wpabuf_len(wps) - 2) {
+               wpa_printf(MSG_DEBUG, "WPS: Invalid WSC attribute length (%u) "
+                          "in rt Wi-Fi Handover Request Message", wsc_len);
+               goto out;
+       }
+       pos += 2;
+
+       wpa_hexdump(MSG_DEBUG,
+                   "WPS: WSC attributes in Wi-Fi Handover Request Message",
+                   pos, wsc_len);
+       if (wsc_len < wpabuf_len(wps) - 2) {
+               wpa_hexdump(MSG_DEBUG,
+                           "WPS: Ignore extra data after WSC attributes",
+                           pos + wsc_len, wpabuf_len(wps) - 2 - wsc_len);
+       }
+
+       wpabuf_set(&msg, pos, wsc_len);
+       ret = wps_parse_msg(&msg, &attr);
+       if (ret < 0) {
+               wpa_printf(MSG_DEBUG, "WPS: Could not parse WSC attributes in "
+                          "Wi-Fi Handover Request Message");
+               goto out;
+       }
+
+       if (attr.oob_dev_password == NULL ||
+           attr.oob_dev_password_len < WPS_OOB_PUBKEY_HASH_LEN + 2) {
+               wpa_printf(MSG_DEBUG, "WPS: No Out-of-Band Device Password "
+                          "included in Wi-Fi Handover Request Message");
+               ret = -1;
+               goto out;
+       }
+
+       if (attr.uuid_e == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No UUID-E included in Wi-Fi "
+                          "Handover Request Message");
+               ret = -1;
+               goto out;
+       }
+
+       wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", attr.uuid_e, WPS_UUID_LEN);
+
+       wpa_hexdump(MSG_DEBUG, "WPS: Out-of-Band Device Password",
+                   attr.oob_dev_password, attr.oob_dev_password_len);
+       dev_pw_id = WPA_GET_BE16(attr.oob_dev_password +
+                                WPS_OOB_PUBKEY_HASH_LEN);
+       if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER) {
+               wpa_printf(MSG_DEBUG, "WPS: Unexpected OOB Device Password ID "
+                          "%u in Wi-Fi Handover Request Message", dev_pw_id);
+               ret = -1;
+               goto out;
+       }
+       wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Public Key hash",
+                   attr.oob_dev_password, WPS_OOB_PUBKEY_HASH_LEN);
+
+       ret = wps_registrar_add_nfc_pw_token(wpa_s->wps->registrar,
+                                            attr.oob_dev_password,
+                                            DEV_PW_NFC_CONNECTION_HANDOVER,
+                                            NULL, 0, 1);
+
+out:
+       wpabuf_free(wps);
+       return ret;
+}
+
+#endif /* CONFIG_WPS_NFC */
 
-extern int wpa_debug_level;
 
 static void wpas_wps_dump_ap_info(struct wpa_supplicant *wpa_s)
 {
        size_t i;
-       struct os_time now;
+       struct os_reltime now;
 
        if (wpa_debug_level > MSG_DEBUG)
                return;
@@ -2338,7 +2806,7 @@ static void wpas_wps_dump_ap_info(struct wpa_supplicant *wpa_s)
        if (wpa_s->wps_ap == NULL)
                return;
 
-       os_get_time(&now);
+       os_get_reltime(&now);
 
        for (i = 0; i < wpa_s->num_wps_ap; i++) {
                struct wps_ap_info *ap = &wpa_s->wps_ap[i];
@@ -2378,7 +2846,8 @@ static void wpas_wps_update_ap_info_bss(struct wpa_supplicant *wpa_s,
        struct wpabuf *wps;
        enum wps_ap_info_type type;
        struct wps_ap_info *ap;
-       int r;
+       int r, pbc_active;
+       const u8 *uuid;
 
        if (wpa_scan_get_vendor_ie(res, WPS_IE_VENDOR_TYPE) == NULL)
                return;
@@ -2395,7 +2864,8 @@ static void wpas_wps_update_ap_info_bss(struct wpa_supplicant *wpa_s,
        else
                type = WPS_AP_NOT_SEL_REG;
 
-       wpabuf_free(wps);
+       uuid = wps_get_uuid_e(wps);
+       pbc_active = wps_is_selected_pbc_registrar(wps);
 
        ap = wpas_wps_get_ap_info(wpa_s, res->bssid);
        if (ap) {
@@ -2407,13 +2877,16 @@ static void wpas_wps_update_ap_info_bss(struct wpa_supplicant *wpa_s,
                        if (type != WPS_AP_NOT_SEL_REG)
                                wpa_blacklist_del(wpa_s, ap->bssid);
                }
-               return;
+               ap->pbc_active = pbc_active;
+               if (uuid)
+                       os_memcpy(ap->uuid, uuid, WPS_UUID_LEN);
+               goto out;
        }
 
        ap = os_realloc_array(wpa_s->wps_ap, wpa_s->num_wps_ap + 1,
                              sizeof(struct wps_ap_info));
        if (ap == NULL)
-               return;
+               goto out;
 
        wpa_s->wps_ap = ap;
        ap = &wpa_s->wps_ap[wpa_s->num_wps_ap];
@@ -2422,8 +2895,14 @@ static void wpas_wps_update_ap_info_bss(struct wpa_supplicant *wpa_s,
        os_memset(ap, 0, sizeof(*ap));
        os_memcpy(ap->bssid, res->bssid, ETH_ALEN);
        ap->type = type;
+       ap->pbc_active = pbc_active;
+       if (uuid)
+               os_memcpy(ap->uuid, uuid, WPS_UUID_LEN);
        wpa_printf(MSG_DEBUG, "WPS: AP " MACSTR " type %d added",
                   MAC2STR(ap->bssid), ap->type);
+
+out:
+       wpabuf_free(wps);
 }
 
 
@@ -2442,11 +2921,14 @@ void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s,
 void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid)
 {
        struct wps_ap_info *ap;
+
+       wpa_s->after_wps = 0;
+
        if (!wpa_s->wps_ap_iter)
                return;
        ap = wpas_wps_get_ap_info(wpa_s, bssid);
        if (ap == NULL)
                return;
        ap->tries++;
-       os_get_time(&ap->last_attempt);
+       os_get_reltime(&ap->last_attempt);
 }