P2P: Allow older scan results to improve p2p_connect-auto robustness
[mech_eap.git] / wpa_supplicant / p2p_supplicant.c
index f70f790..79c7b65 100644 (file)
 #define P2P_MAX_CLIENT_IDLE 10
 #endif /* P2P_MAX_CLIENT_IDLE */
 
+#ifndef P2P_MAX_INITIAL_CONN_WAIT
+/*
+ * How many seconds to wait for initial 4-way handshake to get completed after
+ * WPS provisioning step.
+ */
+#define P2P_MAX_INITIAL_CONN_WAIT 10
+#endif /* P2P_MAX_INITIAL_CONN_WAIT */
+
 
 static void wpas_p2p_long_listen_timeout(void *eloop_ctx, void *timeout_ctx);
 static struct wpa_supplicant *
@@ -55,13 +63,16 @@ wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated,
 static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s);
 static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx);
 static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
-                        const u8 *dev_addr, enum p2p_wps_method wps_method);
+                        const u8 *dev_addr, enum p2p_wps_method wps_method,
+                        int auto_join);
 static void wpas_p2p_pd_before_join_timeout(void *eloop_ctx,
                                            void *timeout_ctx);
 static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s);
 static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s);
 static void wpas_p2p_group_idle_timeout(void *eloop_ctx, void *timeout_ctx);
 static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s);
+static void wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s,
+                                       int group_added);
 
 
 static void wpas_p2p_scan_res_handler(struct wpa_supplicant *wpa_s,
@@ -207,7 +218,7 @@ static struct wpa_supplicant * wpas_get_p2p_group(struct wpa_supplicant *wpa_s,
 }
 
 
-static void wpas_p2p_group_delete(struct wpa_supplicant *wpa_s)
+static void wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, int silent)
 {
        struct wpa_ssid *ssid;
        char *gtype;
@@ -255,17 +266,23 @@ static void wpas_p2p_group_delete(struct wpa_supplicant *wpa_s)
        case P2P_GROUP_REMOVAL_UNAVAILABLE:
                reason = " reason=UNAVAILABLE";
                break;
+       case P2P_GROUP_REMOVAL_GO_ENDING_SESSION:
+               reason = " reason=GO_ENDING_SESSION";
+               break;
        default:
                reason = "";
                break;
        }
-       wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_REMOVED "%s %s%s",
-               wpa_s->ifname, gtype, reason);
+       if (!silent) {
+               wpa_msg(wpa_s->parent, MSG_INFO,
+                       P2P_EVENT_GROUP_REMOVED "%s %s%s",
+                       wpa_s->ifname, gtype, reason);
+       }
 
        if (eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL) > 0)
                wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group idle timeout");
 
-       if (ssid)
+       if (!silent && ssid)
                wpas_notify_p2p_group_removed(wpa_s, ssid, gtype);
 
        if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) {
@@ -311,7 +328,10 @@ static void wpas_p2p_group_delete(struct wpa_supplicant *wpa_s)
                wpa_printf(MSG_DEBUG, "P2P: Temporary group network not "
                           "found");
        }
-       wpa_supplicant_ap_deinit(wpa_s);
+       if (wpa_s->ap_iface)
+               wpa_supplicant_ap_deinit(wpa_s);
+       else
+               wpa_drv_deinit_p2p_cli(wpa_s);
 }
 
 
@@ -532,7 +552,7 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s,
        if (!success) {
                wpa_msg(wpa_s->parent, MSG_INFO,
                        P2P_EVENT_GROUP_FORMATION_FAILURE);
-               wpas_p2p_group_delete(wpa_s);
+               wpas_p2p_group_delete(wpa_s, 0);
                return;
        }
 
@@ -640,6 +660,13 @@ static void wpas_p2p_send_action_tx_status(struct wpa_supplicant *wpa_s,
            (os_memcmp(dst, wpa_s->pending_join_dev_addr, ETH_ALEN) == 0 ||
             os_memcmp(dst, wpa_s->pending_join_iface_addr, ETH_ALEN) == 0)) {
                wpa_s->pending_pd_before_join = 0;
+               if (wpa_s->p2p_fallback_to_go_neg) {
+                       wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No ACK for PD Req "
+                               "during p2p_connect-auto");
+                       wpas_p2p_fallback_to_go_neg(wpa_s, 0);
+                       return;
+               }
+
                wpa_printf(MSG_DEBUG, "P2P: Starting pending "
                           "join-existing-group operation (no ACK for PD "
                           "Req)");
@@ -821,6 +848,8 @@ static void wpas_p2p_clone_config(struct wpa_supplicant *dst,
        d->p2p_group_idle = s->p2p_group_idle;
        d->p2p_intra_bss = s->p2p_intra_bss;
        d->persistent_reconnect = s->persistent_reconnect;
+       d->max_num_sta = s->max_num_sta;
+       d->pbc_in_m1 = s->pbc_in_m1;
 }
 
 
@@ -972,6 +1001,20 @@ void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res)
        wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_SUCCESS);
        wpas_notify_p2p_go_neg_completed(wpa_s, res);
 
+       if (res->role_go && wpa_s->p2p_persistent_id >= 0) {
+               struct wpa_ssid *ssid;
+               ssid = wpa_config_get_network(wpa_s->conf,
+                                             wpa_s->p2p_persistent_id);
+               if (ssid && ssid->disabled == 2 &&
+                   ssid->mode == WPAS_MODE_P2P_GO && ssid->passphrase) {
+                       size_t len = os_strlen(ssid->passphrase);
+                       wpa_printf(MSG_DEBUG, "P2P: Override passphrase based "
+                                  "on requested persistent group");
+                       os_memcpy(res->passphrase, ssid->passphrase, len);
+                       res->passphrase[len] = '\0';
+               }
+       }
+
        if (wpa_s->create_p2p_iface) {
                struct wpa_supplicant *group_wpa_s =
                        wpas_p2p_init_group_interface(wpa_s, res->role_go);
@@ -1806,6 +1849,7 @@ void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods)
 {
        struct wpa_supplicant *wpa_s = ctx;
        unsigned int generated_pin = 0;
+       char params[20];
 
        if (wpa_s->pending_pd_before_join &&
            (os_memcmp(peer, wpa_s->pending_join_dev_addr, ETH_ALEN) == 0 ||
@@ -1817,14 +1861,22 @@ void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods)
                return;
        }
 
+       if (wpa_s->pending_pd_use == AUTO_PD_JOIN ||
+           wpa_s->pending_pd_use == AUTO_PD_GO_NEG)
+               os_snprintf(params, sizeof(params), " peer_go=%d",
+                           wpa_s->pending_pd_use == AUTO_PD_JOIN);
+       else
+               params[0] = '\0';
+
        if (config_methods & WPS_CONFIG_DISPLAY)
-               wpas_prov_disc_local_keypad(wpa_s, peer, "");
+               wpas_prov_disc_local_keypad(wpa_s, peer, params);
        else if (config_methods & WPS_CONFIG_KEYPAD) {
                generated_pin = wps_generate_pin();
-               wpas_prov_disc_local_display(wpa_s, peer, "", generated_pin);
+               wpas_prov_disc_local_display(wpa_s, peer, params,
+                                            generated_pin);
        } else if (config_methods & WPS_CONFIG_PUSHBUTTON)
-               wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_PBC_RESP MACSTR,
-                       MAC2STR(peer));
+               wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_PBC_RESP MACSTR
+                       "%s", MAC2STR(peer), params);
 
        wpas_notify_p2p_provision_discovery(wpa_s, peer, 0 /* response */,
                                            P2P_PROV_DISC_SUCCESS,
@@ -1837,6 +1889,17 @@ static void wpas_prov_disc_fail(void *ctx, const u8 *peer,
 {
        struct wpa_supplicant *wpa_s = ctx;
 
+       if (wpa_s->p2p_fallback_to_go_neg) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "P2P: PD for p2p_connect-auto "
+                       "failed - fall back to GO Negotiation");
+               wpas_p2p_fallback_to_go_neg(wpa_s, 0);
+               return;
+       }
+
+       wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE
+               " p2p_dev_addr=" MACSTR " status=%d",
+               MAC2STR(peer), status);
+
        wpas_notify_p2p_provision_discovery(wpa_s, peer, 0 /* response */,
                                            status, 0, 0);
 }
@@ -1963,7 +2026,7 @@ static void wpas_invitation_received(void *ctx, const u8 *sa, const u8 *bssid,
                                wpa_s, s, s->mode == WPAS_MODE_P2P_GO, 0);
                } else if (bssid) {
                        wpas_p2p_join(wpa_s, bssid, go_dev_addr,
-                                     wpa_s->p2p_wps_method);
+                                     wpa_s->p2p_wps_method, 0);
                }
                return;
        }
@@ -2446,7 +2509,7 @@ void wpas_p2p_deinit(struct wpa_supplicant *wpa_s)
                /* Clear any stored provisioning info */
                p2p_clear_provisioning_info(
                        wpa_s->global->p2p,
-                       wpa_s->go_params->peer_interface_addr);
+                       wpa_s->go_params->peer_device_addr);
        }
 
        os_free(wpa_s->go_params);
@@ -2473,7 +2536,6 @@ void wpas_p2p_deinit(struct wpa_supplicant *wpa_s)
 void wpas_p2p_deinit_global(struct wpa_global *global)
 {
        struct wpa_supplicant *wpa_s, *tmp;
-       char *ifname;
 
        if (global->p2p == NULL)
                return;
@@ -2485,7 +2547,6 @@ void wpas_p2p_deinit_global(struct wpa_global *global)
        while (wpa_s && wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE)
                wpa_s = wpa_s->next;
        while (wpa_s) {
-               enum wpa_driver_if_type type;
                tmp = global->ifaces;
                while (tmp &&
                       (tmp == wpa_s ||
@@ -2494,12 +2555,8 @@ void wpas_p2p_deinit_global(struct wpa_global *global)
                }
                if (tmp == NULL)
                        break;
-               ifname = os_strdup(tmp->ifname);
-               type = wpas_p2p_if_type(tmp->p2p_group_interface);
-               wpa_supplicant_remove_iface(global, tmp, 0);
-               if (ifname)
-                       wpa_drv_if_remove(wpa_s, type, ifname);
-               os_free(ifname);
+               /* Disconnect from the P2P group and deinit the interface */
+               wpas_p2p_disconnect(tmp);
        }
 
        /*
@@ -2537,7 +2594,8 @@ static int wpas_p2p_start_go_neg(struct wpa_supplicant *wpa_s,
                                 const u8 *peer_addr,
                                 enum p2p_wps_method wps_method,
                                 int go_intent, const u8 *own_interface_addr,
-                                unsigned int force_freq, int persistent_group)
+                                unsigned int force_freq, int persistent_group,
+                                struct wpa_ssid *ssid)
 {
        if (persistent_group && wpa_s->conf->persistent_reconnect)
                persistent_group = 2;
@@ -2550,7 +2608,8 @@ static int wpas_p2p_start_go_neg(struct wpa_supplicant *wpa_s,
 
        return p2p_connect(wpa_s->global->p2p, peer_addr, wps_method,
                           go_intent, own_interface_addr, force_freq,
-                          persistent_group);
+                          persistent_group, ssid ? ssid->ssid : NULL,
+                          ssid ? ssid->ssid_len : 0);
 }
 
 
@@ -2558,7 +2617,8 @@ static int wpas_p2p_auth_go_neg(struct wpa_supplicant *wpa_s,
                                const u8 *peer_addr,
                                enum p2p_wps_method wps_method,
                                int go_intent, const u8 *own_interface_addr,
-                               unsigned int force_freq, int persistent_group)
+                               unsigned int force_freq, int persistent_group,
+                               struct wpa_ssid *ssid)
 {
        if (persistent_group && wpa_s->conf->persistent_reconnect)
                persistent_group = 2;
@@ -2568,7 +2628,8 @@ static int wpas_p2p_auth_go_neg(struct wpa_supplicant *wpa_s,
 
        return p2p_authorize(wpa_s->global->p2p, peer_addr, wps_method,
                             go_intent, own_interface_addr, force_freq,
-                            persistent_group);
+                            persistent_group, ssid ? ssid->ssid : NULL,
+                            ssid ? ssid->ssid_len : 0);
 }
 
 
@@ -2582,6 +2643,13 @@ static void wpas_p2p_check_join_scan_limit(struct wpa_supplicant *wpa_s)
                           " for join operationg - stop join attempt",
                           MAC2STR(wpa_s->pending_join_iface_addr));
                eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
+               if (wpa_s->p2p_auto_pd) {
+                       wpa_s->p2p_auto_pd = 0;
+                       wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE
+                               " p2p_dev_addr=" MACSTR " status=N/A",
+                               MAC2STR(wpa_s->pending_join_dev_addr));
+                       return;
+               }
                wpa_msg(wpa_s->parent, MSG_INFO,
                        P2P_EVENT_GROUP_FORMATION_FAILURE);
        }
@@ -2603,6 +2671,73 @@ static void wpas_p2p_pd_before_join_timeout(void *eloop_ctx, void *timeout_ctx)
 }
 
 
+static int wpas_check_freq_conflict(struct wpa_supplicant *wpa_s, int freq)
+{
+       struct wpa_supplicant *iface;
+       int shared_freq;
+       u8 bssid[ETH_ALEN];
+
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)
+               return 0;
+
+       for (iface = wpa_s->global->ifaces; iface; iface = iface->next) {
+               if (!wpas_p2p_create_iface(wpa_s) && iface == wpa_s)
+                       continue;
+               if (iface->current_ssid == NULL || iface->assoc_freq == 0)
+                       continue;
+               if (iface->current_ssid->mode == WPAS_MODE_AP ||
+                   iface->current_ssid->mode == WPAS_MODE_P2P_GO)
+                       shared_freq = iface->current_ssid->frequency;
+               else if (wpa_drv_get_bssid(iface, bssid) == 0)
+                       shared_freq = iface->assoc_freq;
+               else
+                       shared_freq = 0;
+
+               if (shared_freq && freq != shared_freq) {
+                       wpa_printf(MSG_DEBUG, "P2P: Frequency conflict - %s "
+                                  "connected on %d MHz - new connection on "
+                                  "%d MHz", iface->ifname, shared_freq, freq);
+                       return 1;
+               }
+       }
+
+       shared_freq = wpa_drv_shared_freq(wpa_s);
+       if (shared_freq > 0 && shared_freq != freq) {
+               wpa_printf(MSG_DEBUG, "P2P: Frequency conflict - shared "
+                          "virtual interface connected on %d MHz - new "
+                          "connection on %d MHz", shared_freq, freq);
+               return 1;
+       }
+
+       return 0;
+}
+
+
+static int wpas_p2p_peer_go(struct wpa_supplicant *wpa_s,
+                           const u8 *peer_dev_addr)
+{
+       struct wpa_bss *bss;
+       int updated;
+
+       bss = wpa_bss_get_p2p_dev_addr(wpa_s, peer_dev_addr);
+       if (bss == NULL)
+               return -1;
+       if (bss->last_update_idx < wpa_s->bss_update_idx) {
+               wpa_printf(MSG_DEBUG, "P2P: Peer BSS entry not updated in the "
+                          "last scan");
+               return 0;
+       }
+
+       updated = os_time_before(&wpa_s->p2p_auto_started, &bss->last_update);
+       wpa_printf(MSG_DEBUG, "P2P: Current BSS entry for peer updated at "
+                  "%ld.%06ld (%supdated in last scan)",
+                  bss->last_update.sec, bss->last_update.usec,
+                  updated ? "": "not ");
+
+       return updated;
+}
+
+
 static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s,
                                   struct wpa_scan_results *scan_res)
 {
@@ -2615,12 +2750,56 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s,
        if (wpa_s->global->p2p_disabled)
                return;
 
-       wpa_printf(MSG_DEBUG, "P2P: Scan results received (%d BSS) for join",
-                  scan_res ? (int) scan_res->num : -1);
+       wpa_printf(MSG_DEBUG, "P2P: Scan results received (%d BSS) for %sjoin",
+                  scan_res ? (int) scan_res->num : -1,
+                  wpa_s->p2p_auto_join ? "auto_" : "");
 
        if (scan_res)
                wpas_p2p_scan_res_handler(wpa_s, scan_res);
 
+       if (wpa_s->p2p_auto_pd) {
+               int join = wpas_p2p_peer_go(wpa_s,
+                                           wpa_s->pending_join_dev_addr);
+               if (join < 0)
+                       join = 0;
+               wpa_s->p2p_auto_pd = 0;
+               wpa_s->pending_pd_use = join ? AUTO_PD_JOIN : AUTO_PD_GO_NEG;
+               wpa_printf(MSG_DEBUG, "P2P: Auto PD with " MACSTR " join=%d",
+                          MAC2STR(wpa_s->pending_join_dev_addr), join);
+               if (p2p_prov_disc_req(wpa_s->global->p2p,
+                                     wpa_s->pending_join_dev_addr,
+                                     wpa_s->pending_pd_config_methods, join,
+                                     0) < 0) {
+                       wpa_s->p2p_auto_pd = 0;
+                       wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE
+                               " p2p_dev_addr=" MACSTR " status=N/A",
+                               MAC2STR(wpa_s->pending_join_dev_addr));
+               }
+               return;
+       }
+
+       if (wpa_s->p2p_auto_join) {
+               int join = wpas_p2p_peer_go(wpa_s,
+                                           wpa_s->pending_join_dev_addr);
+               if (join < 0) {
+                       wpa_printf(MSG_DEBUG, "P2P: Peer was not found to be "
+                                  "running a GO -> use GO Negotiation");
+                       wpas_p2p_connect(wpa_s, wpa_s->pending_join_dev_addr,
+                                        wpa_s->p2p_pin, wpa_s->p2p_wps_method,
+                                        wpa_s->p2p_persistent_group, 0, 0, 0,
+                                        wpa_s->p2p_go_intent,
+                                        wpa_s->p2p_connect_freq,
+                                        wpa_s->p2p_persistent_id);
+                       return;
+               }
+
+               wpa_printf(MSG_DEBUG, "P2P: Peer was found running GO%s -> "
+                          "try to join the group", join ? "" :
+                          " in older scan");
+               if (!join)
+                       wpa_s->p2p_fallback_to_go_neg = 1;
+       }
+
        freq = p2p_get_oper_freq(wpa_s->global->p2p,
                                 wpa_s->pending_join_iface_addr);
        if (freq < 0 &&
@@ -2653,6 +2832,13 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s,
        if (freq > 0) {
                u16 method;
 
+               if (wpas_check_freq_conflict(wpa_s, freq) > 0) {
+                       wpa_msg(wpa_s->parent, MSG_INFO,
+                               P2P_EVENT_GROUP_FORMATION_FAILURE
+                               "reason=FREQ_CONFLICT");
+                       return;
+               }
+
                wpa_printf(MSG_DEBUG, "P2P: Send Provision Discovery Request "
                           "prior to joining an existing group (GO " MACSTR
                           " freq=%u MHz)",
@@ -2769,8 +2955,9 @@ static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx)
         * Run a scan to update BSS table and start Provision Discovery once
         * the new scan results become available.
         */
-       wpa_s->scan_res_handler = wpas_p2p_scan_res_join;
        ret = wpa_drv_scan(wpa_s, &params);
+       if (!ret)
+               wpa_s->scan_res_handler = wpas_p2p_scan_res_join;
 
        wpabuf_free(ies);
 
@@ -2785,12 +2972,16 @@ static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx)
 
 
 static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
-                        const u8 *dev_addr, enum p2p_wps_method wps_method)
+                        const u8 *dev_addr, enum p2p_wps_method wps_method,
+                        int auto_join)
 {
        wpa_printf(MSG_DEBUG, "P2P: Request to join existing group (iface "
-                  MACSTR " dev " MACSTR ")",
-                  MAC2STR(iface_addr), MAC2STR(dev_addr));
+                  MACSTR " dev " MACSTR ")%s",
+                  MAC2STR(iface_addr), MAC2STR(dev_addr),
+                  auto_join ? " (auto_join)" : "");
 
+       wpa_s->p2p_auto_pd = 0;
+       wpa_s->p2p_auto_join = !!auto_join;
        os_memcpy(wpa_s->pending_join_iface_addr, iface_addr, ETH_ALEN);
        os_memcpy(wpa_s->pending_join_dev_addr, dev_addr, ETH_ALEN);
        wpa_s->pending_join_wps_method = wps_method;
@@ -2821,6 +3012,7 @@ static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s)
        }
 
        group->p2p_in_provisioning = 1;
+       group->p2p_fallback_to_go_neg = wpa_s->p2p_fallback_to_go_neg;
 
        os_memset(&res, 0, sizeof(res));
        os_memcpy(res.peer_interface_addr, wpa_s->pending_join_iface_addr,
@@ -2861,30 +3053,41 @@ static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s)
  * @peer_addr: Address of the peer P2P Device
  * @pin: PIN to use during provisioning or %NULL to indicate PBC mode
  * @persistent_group: Whether to create a persistent group
+ * @auto_join: Whether to select join vs. GO Negotiation automatically
  * @join: Whether to join an existing group (as a client) instead of starting
  *     Group Owner negotiation; @peer_addr is BSSID in that case
  * @auth: Whether to only authorize the connection instead of doing that and
  *     initiating Group Owner negotiation
  * @go_intent: GO Intent or -1 to use default
  * @freq: Frequency for the group or 0 for auto-selection
+ * @persistent_id: Persistent group credentials to use for forcing GO
+ *     parameters or -1 to generate new values (SSID/passphrase)
  * Returns: 0 or new PIN (if pin was %NULL) on success, -1 on unspecified
  *     failure, -2 on failure due to channel not currently available,
  *     -3 if forced channel is not supported
  */
 int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
                     const char *pin, enum p2p_wps_method wps_method,
-                    int persistent_group, int join, int auth, int go_intent,
-                    int freq)
+                    int persistent_group, int auto_join, int join, int auth,
+                    int go_intent, int freq, int persistent_id)
 {
        int force_freq = 0, oper_freq = 0;
        u8 bssid[ETH_ALEN];
        int ret = 0;
        enum wpa_driver_if_type iftype;
        const u8 *if_addr;
+       struct wpa_ssid *ssid = NULL;
 
        if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
                return -1;
 
+       if (persistent_id >= 0) {
+               ssid = wpa_config_get_network(wpa_s->conf, persistent_id);
+               if (ssid == NULL || ssid->disabled != 2 ||
+                   ssid->mode != WPAS_MODE_P2P_GO)
+                       return -1;
+       }
+
        if (go_intent < 0)
                go_intent = wpa_s->conf->p2p_go_intent;
 
@@ -2892,6 +3095,11 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
                wpa_s->p2p_long_listen = 0;
 
        wpa_s->p2p_wps_method = wps_method;
+       wpa_s->p2p_persistent_group = !!persistent_group;
+       wpa_s->p2p_persistent_id = persistent_id;
+       wpa_s->p2p_go_intent = go_intent;
+       wpa_s->p2p_connect_freq = freq;
+       wpa_s->p2p_fallback_to_go_neg = 0;
 
        if (pin)
                os_strlcpy(wpa_s->p2p_pin, pin, sizeof(wpa_s->p2p_pin));
@@ -2904,7 +3112,7 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
        } else
                wpa_s->p2p_pin[0] = '\0';
 
-       if (join) {
+       if (join || auto_join) {
                u8 iface_addr[ETH_ALEN], dev_addr[ETH_ALEN];
                if (auth) {
                        wpa_printf(MSG_DEBUG, "P2P: Authorize invitation to "
@@ -2920,8 +3128,15 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
                        p2p_get_dev_addr(wpa_s->global->p2p, peer_addr,
                                         dev_addr);
                }
-               if (wpas_p2p_join(wpa_s, iface_addr, dev_addr, wps_method) <
-                   0)
+               if (auto_join) {
+                       os_get_time(&wpa_s->p2p_auto_started);
+                       wpa_printf(MSG_DEBUG, "P2P: Auto join started at "
+                                  "%ld.%06ld",
+                                  wpa_s->p2p_auto_started.sec,
+                                  wpa_s->p2p_auto_started.usec);
+               }
+               if (wpas_p2p_join(wpa_s, iface_addr, dev_addr, wps_method,
+                                 auto_join) < 0)
                        return -1;
                return ret;
        }
@@ -2994,14 +3209,15 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
        if (auth) {
                if (wpas_p2p_auth_go_neg(wpa_s, peer_addr, wps_method,
                                         go_intent, if_addr,
-                                        force_freq, persistent_group) < 0)
+                                        force_freq, persistent_group, ssid) <
+                   0)
                        return -1;
                return ret;
        }
 
        if (wpas_p2p_start_go_neg(wpa_s, peer_addr, wps_method,
                                  go_intent, if_addr, force_freq,
-                                 persistent_group) < 0) {
+                                 persistent_group, ssid) < 0) {
                if (wpa_s->create_p2p_iface)
                        wpas_p2p_remove_pending_group_interface(wpa_s);
                return -1;
@@ -3293,6 +3509,13 @@ int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
 
        if (wpas_p2p_init_go_params(wpa_s, &params, freq))
                return -1;
+       if (params.freq &&
+           !p2p_supported_freq(wpa_s->global->p2p, params.freq)) {
+               wpa_printf(MSG_DEBUG, "P2P: The selected channel for GO "
+                          "(%u MHz) is not supported for P2P uses",
+                          params.freq);
+               return -1;
+       }
        p2p_go_params(wpa_s->global->p2p, &params);
        params.persistent_group = persistent_group;
 
@@ -3369,6 +3592,8 @@ int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
        /* Make sure we are not running find during connection establishment */
        wpas_p2p_stop_find(wpa_s);
 
+       wpa_s->p2p_fallback_to_go_neg = 0;
+
        if (ssid->mode == WPAS_MODE_INFRA)
                return wpas_start_p2p_client(wpa_s, ssid, addr_allocated);
 
@@ -3482,17 +3707,36 @@ struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
 void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
                          int registrar)
 {
+       struct wpa_ssid *ssid = wpa_s->current_ssid;
+
        if (!wpa_s->p2p_in_provisioning) {
                wpa_printf(MSG_DEBUG, "P2P: Ignore WPS success event - P2P "
                           "provisioning not in progress");
                return;
        }
 
-       /* Clear any stored provisioning info */
-       p2p_clear_provisioning_info(wpa_s->global->p2p, peer_addr);
+       if (ssid && ssid->mode == WPAS_MODE_INFRA) {
+               u8 go_dev_addr[ETH_ALEN];
+               os_memcpy(go_dev_addr, wpa_s->bssid, ETH_ALEN);
+               wpas_p2p_persistent_group(wpa_s, go_dev_addr, ssid->ssid,
+                                         ssid->ssid_len);
+               /* Clear any stored provisioning info */
+               p2p_clear_provisioning_info(wpa_s->global->p2p, go_dev_addr);
+       }
 
        eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->parent,
                             NULL);
+       if (ssid && ssid->mode == WPAS_MODE_INFRA) {
+               /*
+                * Use a separate timeout for initial data connection to
+                * complete to allow the group to be removed automatically if
+                * something goes wrong in this step before the P2P group idle
+                * timeout mechanism is taken into use.
+                */
+               eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0,
+                                      wpas_p2p_group_formation_timeout,
+                                      wpa_s, NULL);
+       }
        if (wpa_s->global->p2p)
                p2p_wps_success_cb(wpa_s->global->p2p, peer_addr);
        else if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
@@ -3513,7 +3757,7 @@ void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s,
        if (wpa_s->go_params) {
                p2p_clear_provisioning_info(
                        wpa_s->global->p2p,
-                       wpa_s->go_params->peer_interface_addr);
+                       wpa_s->go_params->peer_device_addr);
        }
 
        wpas_notify_p2p_wps_failed(wpa_s, fail);
@@ -3521,10 +3765,13 @@ void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s,
 
 
 int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
-                      const char *config_method, int join)
+                      const char *config_method,
+                      enum wpas_p2p_prov_disc_use use)
 {
        u16 config_methods;
 
+       wpa_s->p2p_fallback_to_go_neg = 0;
+       wpa_s->pending_pd_use = NORMAL_PD;
        if (os_strncmp(config_method, "display", 7) == 0)
                config_methods = WPS_CONFIG_DISPLAY;
        else if (os_strncmp(config_method, "keypad", 6) == 0)
@@ -3537,16 +3784,30 @@ int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
                return -1;
        }
 
+       if (use == WPAS_P2P_PD_AUTO) {
+               os_memcpy(wpa_s->pending_join_dev_addr, peer_addr, ETH_ALEN);
+               wpa_s->pending_pd_config_methods = config_methods;
+               wpa_s->p2p_auto_pd = 1;
+               wpa_s->p2p_auto_join = 0;
+               wpa_s->pending_pd_before_join = 0;
+               wpas_p2p_stop_find(wpa_s);
+               wpa_s->p2p_join_scan_count = 0;
+               wpas_p2p_join_scan(wpa_s, NULL);
+               return 0;
+       }
+
        if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
                return wpa_drv_p2p_prov_disc_req(wpa_s, peer_addr,
-                                                config_methods, join);
+                                                config_methods,
+                                                use == WPAS_P2P_PD_FOR_JOIN);
        }
 
        if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled)
                return -1;
 
        return p2p_prov_disc_req(wpa_s->global->p2p, peer_addr,
-                                config_methods, join, 0);
+                                config_methods, use == WPAS_P2P_PD_FOR_JOIN,
+                                0);
 }
 
 
@@ -3682,15 +3943,27 @@ int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
 
 int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr,
                          const u8 *dst, const u8 *bssid,
-                         const u8 *ie, size_t ie_len)
+                         const u8 *ie, size_t ie_len, int ssi_signal)
 {
        if (wpa_s->global->p2p_disabled)
                return 0;
        if (wpa_s->global->p2p == NULL)
                return 0;
 
-       return p2p_probe_req_rx(wpa_s->global->p2p, addr, dst, bssid,
-                               ie, ie_len);
+       switch (p2p_probe_req_rx(wpa_s->global->p2p, addr, dst, bssid,
+                                ie, ie_len)) {
+       case P2P_PREQ_NOT_P2P:
+               wpas_notify_preq(wpa_s, addr, dst, bssid, ie, ie_len,
+                                ssi_signal);
+               /* fall through */
+       case P2P_PREQ_MALFORMED:
+       case P2P_PREQ_NOT_LISTEN:
+       case P2P_PREQ_NOT_PROCESSED:
+       default: /* make gcc happy */
+               return 0;
+       case P2P_PREQ_PROCESSED:
+               return 1;
+       }
 }
 
 
@@ -3861,6 +4134,9 @@ void wpas_p2p_completed(struct wpa_supplicant *wpa_s)
        int persistent;
        int freq;
 
+       eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->parent,
+                            NULL);
+
        if (!wpa_s->show_group_started || !ssid)
                return;
 
@@ -3960,13 +4236,13 @@ static void wpas_p2p_group_idle_timeout(void *eloop_ctx, void *timeout_ctx)
        wpa_printf(MSG_DEBUG, "P2P: Group idle timeout reached - terminate "
                   "group");
        wpa_s->removal_reason = P2P_GROUP_REMOVAL_IDLE_TIMEOUT;
-       wpas_p2p_group_delete(wpa_s);
+       wpas_p2p_group_delete(wpa_s, 0);
 }
 
 
 static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s)
 {
-       unsigned int timeout;
+       int timeout;
 
        if (eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL) > 0)
                wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group idle timeout");
@@ -3982,6 +4258,37 @@ static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s)
        if (timeout == 0)
                return;
 
+       if (timeout < 0) {
+               if (wpa_s->current_ssid->mode == WPAS_MODE_INFRA)
+                       timeout = 0; /* special client mode no-timeout */
+               else
+                       return;
+       }
+
+       if (wpa_s->p2p_in_provisioning) {
+               /*
+                * Use the normal group formation timeout during the
+                * provisioning phase to avoid terminating this process too
+                * early due to group idle timeout.
+                */
+               wpa_printf(MSG_DEBUG, "P2P: Do not use P2P group idle timeout "
+                          "during provisioning");
+               return;
+       }
+
+       if (wpa_s->show_group_started) {
+               /*
+                * Use the normal group formation timeout between the end of
+                * the provisioning phase and completion of 4-way handshake to
+                * avoid terminating this process too early due to group idle
+                * timeout.
+                */
+               wpa_printf(MSG_DEBUG, "P2P: Do not use P2P group idle timeout "
+                          "while waiting for initial 4-way handshake to "
+                          "complete");
+               return;
+       }
+
        wpa_printf(MSG_DEBUG, "P2P: Set P2P group idle timeout to %u seconds",
                   timeout);
        eloop_register_timeout(timeout, 0, wpas_p2p_group_idle_timeout,
@@ -3990,26 +4297,42 @@ static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s)
 
 
 void wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
-                          u16 reason_code, const u8 *ie, size_t ie_len)
+                          u16 reason_code, const u8 *ie, size_t ie_len,
+                          int locally_generated)
 {
        if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
                return;
        if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
                return;
 
-       p2p_deauth_notif(wpa_s->global->p2p, bssid, reason_code, ie, ie_len);
+       if (!locally_generated)
+               p2p_deauth_notif(wpa_s->global->p2p, bssid, reason_code, ie,
+                                ie_len);
+
+       if (reason_code == WLAN_REASON_DEAUTH_LEAVING && !locally_generated &&
+           wpa_s->current_ssid &&
+           wpa_s->current_ssid->p2p_group &&
+           wpa_s->current_ssid->mode == WPAS_MODE_INFRA) {
+               wpa_printf(MSG_DEBUG, "P2P: GO indicated that the P2P Group "
+                          "session is ending");
+               wpa_s->removal_reason = P2P_GROUP_REMOVAL_GO_ENDING_SESSION;
+               wpas_p2p_group_delete(wpa_s, 0);
+       }
 }
 
 
 void wpas_p2p_disassoc_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
-                            u16 reason_code, const u8 *ie, size_t ie_len)
+                            u16 reason_code, const u8 *ie, size_t ie_len,
+                            int locally_generated)
 {
        if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
                return;
        if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
                return;
 
-       p2p_disassoc_notif(wpa_s->global->p2p, bssid, reason_code, ie, ie_len);
+       if (!locally_generated)
+               p2p_disassoc_notif(wpa_s->global->p2p, bssid, reason_code, ie,
+                                  ie_len);
 }
 
 
@@ -4125,6 +4448,14 @@ void wpas_p2p_update_config(struct wpa_supplicant *wpa_s)
                        wpa_printf(MSG_ERROR, "P2P: Own oper channel update "
                                   "failed: %d", ret);
        }
+
+       if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_PREF_CHAN) {
+               if (p2p_set_pref_chan(p2p, wpa_s->conf->num_p2p_pref_chan,
+                                     wpa_s->conf->p2p_pref_chan) < 0) {
+                       wpa_printf(MSG_ERROR, "P2P: Preferred channel list "
+                                  "update failed");
+               }
+       }
 }
 
 
@@ -4353,7 +4684,7 @@ int wpas_p2p_cancel(struct wpa_supplicant *wpa_s)
                        found = 1;
                        eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
                                             wpa_s->parent, NULL);
-                       wpas_p2p_group_delete(wpa_s);
+                       wpas_p2p_group_delete(wpa_s, 0);
                        break;
                }
        }
@@ -4375,7 +4706,7 @@ void wpas_p2p_interface_unavailable(struct wpa_supplicant *wpa_s)
        wpa_printf(MSG_DEBUG, "P2P: Remove group due to driver resource not "
                   "being available anymore");
        wpa_s->removal_reason = P2P_GROUP_REMOVAL_UNAVAILABLE;
-       wpas_p2p_group_delete(wpa_s);
+       wpas_p2p_group_delete(wpa_s, 0);
 }
 
 
@@ -4422,7 +4753,7 @@ int wpas_p2p_disconnect(struct wpa_supplicant *wpa_s)
                return -1;
 
        wpa_s->removal_reason = P2P_GROUP_REMOVAL_REQUESTED;
-       wpas_p2p_group_delete(wpa_s);
+       wpas_p2p_group_delete(wpa_s, 0);
 
        return 0;
 }
@@ -4439,14 +4770,22 @@ int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s)
 
 void wpas_p2p_network_removed(struct wpa_supplicant *wpa_s,
                              struct wpa_ssid *ssid)
-
 {
        if (wpa_s->p2p_in_provisioning && ssid->p2p_group &&
            eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
                                 wpa_s->parent, NULL) > 0) {
+               /**
+                * Remove the network by scheduling the group formation
+                * timeout to happen immediately. The teardown code
+                * needs to be scheduled to run asynch later so that we
+                * don't delete data from under ourselves unexpectedly.
+                * Calling wpas_p2p_group_formation_timeout directly
+                * causes a series of crashes in WPS failure scenarios.
+                */
                wpa_printf(MSG_DEBUG, "P2P: Canceled group formation due to "
                           "P2P group network getting removed");
-               wpas_p2p_group_formation_timeout(wpa_s->parent, NULL);
+               eloop_register_timeout(0, 0, wpas_p2p_group_formation_timeout,
+                                      wpa_s->parent, NULL);
        }
 }
 
@@ -4488,3 +4827,34 @@ void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s,
                return;
        wpas_p2p_add_persistent_group_client(wpa_s, addr);
 }
+
+
+static void wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s,
+                                       int group_added)
+{
+       struct wpa_supplicant *group = wpa_s;
+       if (wpa_s->global->p2p_group_formation)
+               group = wpa_s->global->p2p_group_formation;
+       wpa_s = wpa_s->parent;
+       if (group_added)
+               wpas_p2p_group_delete(group, 1);
+       wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Fall back to GO Negotiation");
+       wpas_p2p_connect(wpa_s, wpa_s->pending_join_dev_addr, wpa_s->p2p_pin,
+                        wpa_s->p2p_wps_method, wpa_s->p2p_persistent_group, 0,
+                        0, 0, wpa_s->p2p_go_intent, wpa_s->p2p_connect_freq,
+                        wpa_s->p2p_persistent_id);
+}
+
+
+int wpas_p2p_scan_no_go_seen(struct wpa_supplicant *wpa_s)
+{
+       if (!wpa_s->p2p_fallback_to_go_neg ||
+           wpa_s->p2p_in_provisioning <= 5)
+               return 0;
+
+       wpa_dbg(wpa_s, MSG_DEBUG, "P2P: GO not found for p2p_connect-auto - "
+               "fallback to GO Negotiation");
+       wpas_p2p_fallback_to_go_neg(wpa_s, 1);
+
+       return 1;
+}