P2P: Fix P2P IE generation for AssocReq when BSS info is not available
[libeap.git] / wpa_supplicant / p2p_supplicant.c
index a89faae..93c23e3 100644 (file)
@@ -22,6 +22,7 @@
 #include "wps/wps_i.h"
 #include "p2p/p2p.h"
 #include "ap/hostapd.h"
+#include "ap/p2p_hostapd.h"
 #include "wpa_supplicant_i.h"
 #include "driver_i.h"
 #include "ap.h"
@@ -40,7 +41,11 @@ static struct wpa_supplicant *
 wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated,
                         int go);
 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);
 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_scan_res_handler(struct wpa_supplicant *wpa_s,
@@ -197,6 +202,12 @@ static void wpas_p2p_group_delete(struct wpa_supplicant *wpa_s)
                gtype = "client";
        } else
                gtype = "GO";
+       if (wpa_s->cross_connect_in_use) {
+               wpa_s->cross_connect_in_use = 0;
+               wpa_msg(wpa_s->parent, MSG_INFO,
+                       P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s",
+                       wpa_s->ifname, wpa_s->cross_connect_uplink);
+       }
        wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_REMOVED "%s %s",
                wpa_s->ifname, gtype);
        if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) {
@@ -437,6 +448,7 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s,
                        "%s GO ssid=\"%s\" psk=%s go_dev_addr=" MACSTR "%s",
                        wpa_s->ifname, ssid_txt, psk, MAC2STR(go_dev_addr),
                        persistent ? " [PERSISTENT]" : "");
+               wpas_p2p_cross_connect_setup(wpa_s);
        } else {
                wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
                        "%s GO ssid=\"%s\" passphrase=\"%s\" go_dev_addr="
@@ -445,6 +457,7 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s,
                        ssid && ssid->passphrase ? ssid->passphrase : "",
                        MAC2STR(go_dev_addr),
                        persistent ? " [PERSISTENT]" : "");
+               wpas_p2p_cross_connect_setup(wpa_s);
        }
 
        if (persistent)
@@ -487,7 +500,9 @@ static void wpas_send_action_cb(void *eloop_ctx, void *timeout_ctx)
                                           "driver to remain on channel (%u "
                                           "MHz) for Action Frame TX",
                                           wpa_s->pending_action_freq);
-                       }
+                       } else
+                               wpa_s->roc_waiting_drv_freq =
+                                       wpa_s->pending_action_freq;
                }
                return;
        }
@@ -622,6 +637,13 @@ static int wpas_send_action(void *ctx, unsigned int freq, const u8 *dst,
        }
        wpa_s->pending_action_without_roc = 0;
 
+       if (wpa_s->roc_waiting_drv_freq == freq) {
+               wpa_printf(MSG_DEBUG, "P2P: Already waiting for driver to get "
+                          "to frequency %u MHz; continue waiting to send the "
+                          "Action frame", freq);
+               return 0;
+       }
+
        wpa_printf(MSG_DEBUG, "P2P: Schedule Action frame to be transmitted "
                   "once the driver gets to the requested channel");
        if (wait_time > wpa_s->max_remain_on_chan)
@@ -632,6 +654,7 @@ static int wpas_send_action(void *ctx, unsigned int freq, const u8 *dst,
                           "Frame TX", freq);
                return -1;
        }
+       wpa_s->roc_waiting_drv_freq = freq;
 
        return 0;
 }
@@ -646,6 +669,7 @@ static void wpas_send_action_done(void *ctx)
        if (wpa_s->off_channel_freq) {
                wpa_drv_cancel_remain_on_channel(wpa_s);
                wpa_s->off_channel_freq = 0;
+               wpa_s->roc_waiting_drv_freq = 0;
        }
 }
 
@@ -671,9 +695,13 @@ static void wpas_start_wps_enrollee(struct wpa_supplicant *wpa_s,
        if (res->wps_method == WPS_PBC)
                wpas_wps_start_pbc(wpa_s, NULL /* res->peer_interface_addr */,
                                   1);
-       else
+       else {
+               u16 dev_pw_id = DEV_PW_DEFAULT;
+               if (wpa_s->p2p_wps_method == WPS_PIN_KEYPAD)
+                       dev_pw_id = DEV_PW_REGISTRAR_SPECIFIED;
                wpas_wps_start_pin(wpa_s, res->peer_interface_addr,
-                                  wpa_s->p2p_pin, 1);
+                                  wpa_s->p2p_pin, 1, dev_pw_id);
+       }
 }
 
 
@@ -698,6 +726,7 @@ static void p2p_go_configured(void *ctx, void *data)
                        wpas_p2p_store_persistent_group(
                                wpa_s->parent, ssid,
                                wpa_s->parent->own_addr);
+               wpas_p2p_cross_connect_setup(wpa_s);
                return;
        }
 
@@ -897,6 +926,7 @@ void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res)
        if (wpa_s->off_channel_freq) {
                wpa_drv_cancel_remain_on_channel(wpa_s);
                wpa_s->off_channel_freq = 0;
+               wpa_s->roc_waiting_drv_freq = 0;
        }
 
        if (res->status) {
@@ -915,9 +945,11 @@ void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res)
                        wpas_p2p_remove_pending_group_interface(wpa_s);
                        return;
                }
-               if (group_wpa_s != wpa_s)
+               if (group_wpa_s != wpa_s) {
                        os_memcpy(group_wpa_s->p2p_pin, wpa_s->p2p_pin,
                                  sizeof(group_wpa_s->p2p_pin));
+                       group_wpa_s->p2p_wps_method = wpa_s->p2p_wps_method;
+               }
                os_memset(wpa_s->pending_interface_addr, 0, ETH_ALEN);
                wpa_s->pending_interface_name[0] = '\0';
                group_wpa_s->p2p_in_provisioning = 1;
@@ -946,11 +978,11 @@ void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res)
 }
 
 
-void wpas_go_neg_req_rx(void *ctx, const u8 *src)
+void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id)
 {
        struct wpa_supplicant *wpa_s = ctx;
-       wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_REQUEST MACSTR,
-               MAC2STR(src));
+       wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_REQUEST MACSTR
+               " dev_passwd_id=%u", MAC2STR(src), dev_passwd_id);
 }
 
 
@@ -994,6 +1026,7 @@ static int wpas_start_listen(void *ctx, unsigned int freq,
                wpa_s->pending_listen_freq = 0;
                return -1;
        }
+       wpa_s->roc_waiting_drv_freq = freq;
 
        return 0;
 }
@@ -1005,6 +1038,7 @@ static void wpas_stop_listen(void *ctx)
        if (wpa_s->off_channel_freq) {
                wpa_drv_cancel_remain_on_channel(wpa_s);
                wpa_s->off_channel_freq = 0;
+               wpa_s->roc_waiting_drv_freq = 0;
        }
        wpa_drv_probe_req_report(wpa_s, 0);
 }
@@ -1143,7 +1177,7 @@ static void wpas_sd_req_bonjour(struct wpa_supplicant *wpa_s,
                           "available");
 
                /* Status Code */
-               wpabuf_put_u8(resp, P2P_SD_QUERY_DATA_NOT_AVAILABLE);
+               wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
                /* Response Data: empty */
                WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
                             2);
@@ -1273,7 +1307,7 @@ static void wpas_sd_req_upnp(struct wpa_supplicant *wpa_s,
                wpa_printf(MSG_DEBUG, "P2P: Requested UPnP service not "
                           "available");
                /* Status Code */
-               wpabuf_put_u8(resp, P2P_SD_QUERY_DATA_NOT_AVAILABLE);
+               wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
                /* Response Data: empty */
        }
 
@@ -1336,6 +1370,15 @@ void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
                wpa_hexdump(MSG_MSGDUMP, "P2P: Query Data",
                            pos, tlv_end - pos);
 
+
+               if (wpa_s->force_long_sd) {
+                       wpa_printf(MSG_DEBUG, "P2P: SD test - force long "
+                                  "response");
+                       wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
+                       wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
+                       goto done;
+               }
+
                switch (srv_proto) {
                case P2P_SERV_ALL_SERVICES:
                        wpa_printf(MSG_DEBUG, "P2P: Service Discovery Request "
@@ -1371,6 +1414,7 @@ void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
                pos = tlv_end;
        }
 
+done:
        wpas_p2p_sd_response(wpa_s, freq, sa, dialog_token, resp);
 
        wpabuf_free(resp);
@@ -1390,14 +1434,23 @@ void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic,
 
        wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Response TLVs",
                    tlvs, tlvs_len);
-       buf_len = 2 * tlvs_len + 1;
-       buf = os_malloc(buf_len);
-       if (buf) {
-               wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len);
-               wpa_msg_ctrl(wpa_s, MSG_INFO, P2P_EVENT_SERV_DISC_RESP MACSTR
-                            " %u %s",
-                            MAC2STR(sa), update_indic, buf);
-               os_free(buf);
+       if (tlvs_len > 1500) {
+               /* TODO: better way for handling this */
+               wpa_msg_ctrl(wpa_s, MSG_INFO,
+                            P2P_EVENT_SERV_DISC_RESP MACSTR
+                            " %u <long response: %u bytes>",
+                            MAC2STR(sa), update_indic,
+                            (unsigned int) tlvs_len);
+       } else {
+               buf_len = 2 * tlvs_len + 1;
+               buf = os_malloc(buf_len);
+               if (buf) {
+                       wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len);
+                       wpa_msg_ctrl(wpa_s, MSG_INFO,
+                                    P2P_EVENT_SERV_DISC_RESP MACSTR " %u %s",
+                                    MAC2STR(sa), update_indic, buf);
+                       os_free(buf);
+               }
        }
 
        while (pos < end) {
@@ -1664,6 +1717,14 @@ static u8 wpas_invitation_process(void *ctx, const u8 *sa, const u8 *bssid,
        if (!persistent_group) {
                wpa_printf(MSG_DEBUG, "P2P: Invitation from " MACSTR
                           " to join an active group", MAC2STR(sa));
+               if (!is_zero_ether_addr(wpa_s->p2p_auth_invite) &&
+                   (os_memcmp(go_dev_addr, wpa_s->p2p_auth_invite, ETH_ALEN)
+                    == 0 ||
+                    os_memcmp(sa, wpa_s->p2p_auth_invite, ETH_ALEN) == 0)) {
+                       wpa_printf(MSG_DEBUG, "P2P: Accept previously "
+                                  "authorized invitation");
+                       goto accept_inv;
+               }
                /*
                 * Do not accept the invitation automatically; notify user and
                 * request approval.
@@ -1710,6 +1771,7 @@ static u8 wpas_invitation_process(void *ctx, const u8 *sa, const u8 *bssid,
                          ETH_ALEN);
        }
 
+accept_inv:
        if (wpa_s->current_ssid && wpa_drv_get_bssid(wpa_s, cur_bssid) == 0 &&
            wpa_s->assoc_freq) {
                wpa_printf(MSG_DEBUG, "P2P: Trying to force channel to match "
@@ -1751,6 +1813,9 @@ static void wpas_invitation_received(void *ctx, const u8 *sa, const u8 *bssid,
                if (s) {
                        wpas_p2p_group_add_persistent(
                                wpa_s, s, s->mode == WPAS_MODE_P2P_GO, 0);
+               } else {
+                       wpas_p2p_join(wpa_s, bssid, go_dev_addr,
+                                     wpa_s->p2p_wps_method);
                }
                return;
        }
@@ -1796,6 +1861,9 @@ static void wpas_invitation_result(void *ctx, int status, const u8 *bssid)
                        "status=%d ", status);
        }
 
+       if (wpa_s->pending_invite_ssid_id == -1)
+               return; /* Invitation to active group */
+
        if (status != P2P_SC_SUCCESS) {
                wpas_p2p_remove_pending_group_interface(wpa_s);
                return;
@@ -2092,7 +2160,7 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s)
        p2p.concurrent_operations = !!(wpa_s->drv_flags &
                                       WPA_DRIVER_FLAGS_P2P_CONCURRENT);
 
-       p2p.max_peers = 100;
+       p2p.max_peers = wpa_s->max_stations ? wpa_s->max_stations : 100;
 
        if (wpa_s->conf->p2p_ssid_postfix) {
                p2p.ssid_postfix_len =
@@ -2127,6 +2195,7 @@ void wpas_p2p_deinit(struct wpa_supplicant *wpa_s)
        wpa_s->pending_action_tx = NULL;
        eloop_cancel_timeout(wpas_send_action_cb, wpa_s, NULL);
        eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL);
+       eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
        wpa_s->p2p_long_listen = 0;
        eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
        wpas_p2p_remove_pending_group_interface(wpa_s);
@@ -2152,6 +2221,8 @@ void wpas_p2p_deinit_global(struct wpa_global *global)
 
        /* Remove remaining P2P group interfaces */
        wpa_s = global->ifaces;
+       if (wpa_s)
+               wpas_p2p_service_flush(wpa_s);
        while (wpa_s && wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE)
                wpa_s = wpa_s->next;
        while (wpa_s) {
@@ -2216,33 +2287,33 @@ static int wpas_p2p_auth_go_neg(struct wpa_supplicant *wpa_s,
 }
 
 
-static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
-                        const u8 *dev_addr, enum p2p_wps_method wps_method)
+static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s,
+                                  struct wpa_scan_results *scan_res)
 {
        struct wpa_bss *bss;
 
-       wpa_printf(MSG_DEBUG, "P2P: Request to join existing group (iface "
-                  MACSTR " dev " MACSTR ")",
-                  MAC2STR(iface_addr), MAC2STR(dev_addr));
+       eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
 
-       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;
+       if (wpa_s->global->p2p_disabled)
+               return;
 
-       /* Make sure we are not running find during connection establishment */
-       wpas_p2p_stop_find(wpa_s);
+       wpa_printf(MSG_DEBUG, "P2P: Scan results received (%d BSS) for join",
+                  scan_res ? (int) scan_res->num : -1);
 
-       bss = wpa_bss_get_bssid(wpa_s, iface_addr);
+       if (scan_res)
+               wpas_p2p_scan_res_handler(wpa_s, scan_res);
+
+       bss = wpa_bss_get_bssid(wpa_s, wpa_s->pending_join_iface_addr);
        if (bss) {
                u16 method;
 
                wpa_printf(MSG_DEBUG, "P2P: Send Provision Discovery Request "
                           "prior to joining an existing group (GO " MACSTR
                           " freq=%u MHz)",
-                          MAC2STR(dev_addr), bss->freq);
+                          MAC2STR(wpa_s->pending_join_dev_addr), bss->freq);
                wpa_s->pending_pd_before_join = 1;
 
-               switch (wps_method) {
+               switch (wpa_s->pending_join_wps_method) {
                case WPS_PIN_LABEL:
                case WPS_PIN_DISPLAY:
                        method = WPS_CONFIG_KEYPAD;
@@ -2258,7 +2329,8 @@ static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
                        break;
                }
 
-               if (p2p_prov_disc_req(wpa_s->global->p2p, dev_addr, method, 1)
+               if (p2p_prov_disc_req(wpa_s->global->p2p,
+                                     wpa_s->pending_join_dev_addr, method, 1)
                    < 0) {
                        wpa_printf(MSG_DEBUG, "P2P: Failed to send Provision "
                                   "Discovery Request before joining an "
@@ -2271,7 +2343,7 @@ static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
                 * Actual join operation will be started from the Action frame
                 * TX status callback.
                 */
-               return 0;
+               return;
        }
 
        wpa_printf(MSG_DEBUG, "P2P: Target BSS/GO not yet in BSS table - "
@@ -2279,7 +2351,83 @@ static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
 
 start:
        /* Start join operation immediately */
-       return wpas_p2p_join_start(wpa_s);
+       wpas_p2p_join_start(wpa_s);
+}
+
+
+static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+       int ret;
+       struct wpa_driver_scan_params params;
+       struct wpabuf *wps_ie, *ies;
+
+       os_memset(&params, 0, sizeof(params));
+
+       /* P2P Wildcard SSID */
+       params.num_ssids = 1;
+       params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID;
+       params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN;
+
+       wpa_s->wps->dev.p2p = 1;
+       wps_ie = wps_build_probe_req_ie(0, &wpa_s->wps->dev, wpa_s->wps->uuid,
+                                       WPS_REQ_ENROLLEE);
+       if (wps_ie == NULL) {
+               wpas_p2p_scan_res_join(wpa_s, NULL);
+               return;
+       }
+
+       ies = wpabuf_alloc(wpabuf_len(wps_ie) + 100);
+       if (ies == NULL) {
+               wpabuf_free(wps_ie);
+               wpas_p2p_scan_res_join(wpa_s, NULL);
+               return;
+       }
+       wpabuf_put_buf(ies, wps_ie);
+       wpabuf_free(wps_ie);
+
+       p2p_scan_ie(wpa_s->global->p2p, ies);
+
+       params.extra_ies = wpabuf_head(ies);
+       params.extra_ies_len = wpabuf_len(ies);
+
+       /*
+        * 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;
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
+               ret = ieee80211_sta_req_scan(wpa_s, &params);
+       else
+               ret = wpa_drv_scan(wpa_s, &params);
+
+       wpabuf_free(ies);
+
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "P2P: Failed to start scan for join - "
+                          "try again later");
+               eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
+               eloop_register_timeout(1, 0, wpas_p2p_join_scan, wpa_s, NULL);
+       }
+}
+
+
+static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
+                        const u8 *dev_addr, enum p2p_wps_method wps_method)
+{
+       wpa_printf(MSG_DEBUG, "P2P: Request to join existing group (iface "
+                  MACSTR " dev " MACSTR ")",
+                  MAC2STR(iface_addr), MAC2STR(dev_addr));
+
+       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;
+
+       /* Make sure we are not running find during connection establishment */
+       wpas_p2p_stop_find(wpa_s);
+
+       wpas_p2p_join_scan(wpa_s, NULL);
+       return 0;
 }
 
 
@@ -2291,9 +2439,11 @@ static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s)
        group = wpas_p2p_get_group_iface(wpa_s, 0, 0);
        if (group == NULL)
                return -1;
-       if (group != wpa_s)
+       if (group != wpa_s) {
                os_memcpy(group->p2p_pin, wpa_s->p2p_pin,
                          sizeof(group->p2p_pin));
+               group->p2p_wps_method = wpa_s->p2p_wps_method;
+       }
 
        group->p2p_in_provisioning = 1;
 
@@ -2337,6 +2487,8 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
        if (!auth)
                wpa_s->p2p_long_listen = 0;
 
+       wpa_s->p2p_wps_method = wps_method;
+
        if (pin)
                os_strlcpy(wpa_s->p2p_pin, pin, sizeof(wpa_s->p2p_pin));
        else if (wps_method == WPS_PIN_DISPLAY) {
@@ -2349,11 +2501,22 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
                wpa_s->p2p_pin[0] = '\0';
 
        if (join) {
-               u8 iface_addr[ETH_ALEN];
+               u8 iface_addr[ETH_ALEN], dev_addr[ETH_ALEN];
+               if (auth) {
+                       wpa_printf(MSG_DEBUG, "P2P: Authorize invitation to "
+                                  "connect a running group from " MACSTR,
+                                  MAC2STR(peer_addr));
+                       os_memcpy(wpa_s->p2p_auth_invite, peer_addr, ETH_ALEN);
+                       return ret;
+               }
+               os_memcpy(dev_addr, peer_addr, ETH_ALEN);
                if (p2p_get_interface_addr(wpa_s->global->p2p, peer_addr,
-                                          iface_addr) < 0)
+                                          iface_addr) < 0) {
                        os_memcpy(iface_addr, peer_addr, ETH_ALEN);
-               if (wpas_p2p_join(wpa_s, iface_addr, peer_addr, wps_method) <
+                       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)
                        return -1;
                return ret;
@@ -2436,6 +2599,7 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
 void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
                                   unsigned int freq, unsigned int duration)
 {
+       wpa_s->roc_waiting_drv_freq = 0;
        wpa_s->off_channel_freq = freq;
        wpas_send_action_cb(wpa_s, NULL);
        if (wpa_s->off_channel_freq == wpa_s->pending_listen_freq) {
@@ -2803,6 +2967,7 @@ int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout,
 void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s)
 {
        wpa_s->p2p_long_listen = 0;
+       eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
 
        p2p_stop_find(wpa_s->global->p2p);
 
@@ -2844,16 +3009,25 @@ int wpas_p2p_listen(struct wpa_supplicant *wpa_s, unsigned int timeout)
 }
 
 
-int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, const u8 *bssid,
+int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
                          u8 *buf, size_t len, int p2p_group)
 {
+       struct wpabuf *p2p_ie;
+       int ret;
+
        if (wpa_s->global->p2p_disabled)
                return -1;
        if (wpa_s->global->p2p == NULL)
                return -1;
+       if (bss == NULL)
+               return -1;
+
+       p2p_ie = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE);
+       ret = p2p_assoc_req_ie(wpa_s->global->p2p, bss->bssid, buf, len,
+                              p2p_group, p2p_ie);
+       wpabuf_free(p2p_ie);
 
-       return p2p_assoc_req_ie(wpa_s->global->p2p, bssid, buf, len,
-                               p2p_group);
+       return ret;
 }
 
 
@@ -2974,7 +3148,7 @@ int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname,
                role = P2P_INVITE_ROLE_ACTIVE_GO;
                bssid = wpa_s->own_addr;
                if (go_dev_addr == NULL)
-                       go_dev_addr = wpa_s->own_addr;
+                       go_dev_addr = wpa_s->parent->own_addr;
        } else {
                role = P2P_INVITE_ROLE_CLIENT;
                if (wpa_s->wpa_state < WPA_ASSOCIATED) {
@@ -2990,7 +3164,8 @@ int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname,
        wpa_s->pending_invite_ssid_id = -1;
 
        return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid,
-                         ssid->ssid, ssid->ssid_len, 0, go_dev_addr, 0);
+                         ssid->ssid, ssid->ssid_len, wpa_s->assoc_freq,
+                         go_dev_addr, 0);
 }
 
 
@@ -3138,3 +3313,145 @@ void wpas_p2p_update_config(struct wpa_supplicant *wpa_s)
                                     0);
        }
 }
+
+
+int wpas_p2p_set_noa(struct wpa_supplicant *wpa_s, u8 count, int start,
+                    int duration)
+{
+       if (!wpa_s->ap_iface)
+               return -1;
+       return hostapd_p2p_set_noa(wpa_s->ap_iface->bss[0], count, start,
+                                  duration);
+}
+
+
+int wpas_p2p_set_cross_connect(struct wpa_supplicant *wpa_s, int enabled)
+{
+       if (wpa_s->global->p2p_disabled)
+               return -1;
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+               return -1;
+
+       wpa_s->global->cross_connection = enabled;
+       p2p_set_cross_connect(wpa_s->global->p2p, enabled);
+
+       if (!enabled) {
+               struct wpa_supplicant *iface;
+
+               for (iface = wpa_s->global->ifaces; iface; iface = iface->next)
+               {
+                       if (iface->cross_connect_enabled == 0)
+                               continue;
+
+                       iface->cross_connect_enabled = 0;
+                       iface->cross_connect_in_use = 0;
+                       wpa_msg(iface->parent, MSG_INFO,
+                               P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s",
+                               iface->ifname, iface->cross_connect_uplink);
+               }
+       }
+
+       return 0;
+}
+
+
+static void wpas_p2p_enable_cross_connect(struct wpa_supplicant *uplink)
+{
+       struct wpa_supplicant *iface;
+
+       if (!uplink->global->cross_connection)
+               return;
+
+       for (iface = uplink->global->ifaces; iface; iface = iface->next) {
+               if (!iface->cross_connect_enabled)
+                       continue;
+               if (os_strcmp(uplink->ifname, iface->cross_connect_uplink) !=
+                   0)
+                       continue;
+               if (iface->ap_iface == NULL)
+                       continue;
+               if (iface->cross_connect_in_use)
+                       continue;
+
+               iface->cross_connect_in_use = 1;
+               wpa_msg(iface->parent, MSG_INFO,
+                       P2P_EVENT_CROSS_CONNECT_ENABLE "%s %s",
+                       iface->ifname, iface->cross_connect_uplink);
+       }
+}
+
+
+static void wpas_p2p_disable_cross_connect(struct wpa_supplicant *uplink)
+{
+       struct wpa_supplicant *iface;
+
+       for (iface = uplink->global->ifaces; iface; iface = iface->next) {
+               if (!iface->cross_connect_enabled)
+                       continue;
+               if (os_strcmp(uplink->ifname, iface->cross_connect_uplink) !=
+                   0)
+                       continue;
+               if (!iface->cross_connect_in_use)
+                       continue;
+
+               wpa_msg(iface->parent, MSG_INFO,
+                       P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s",
+                       iface->ifname, iface->cross_connect_uplink);
+               iface->cross_connect_in_use = 0;
+       }
+}
+
+
+void wpas_p2p_notif_connected(struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s->ap_iface || wpa_s->current_ssid == NULL ||
+           wpa_s->current_ssid->mode != WPAS_MODE_INFRA ||
+           wpa_s->cross_connect_disallowed)
+               wpas_p2p_disable_cross_connect(wpa_s);
+       else
+               wpas_p2p_enable_cross_connect(wpa_s);
+}
+
+
+void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s)
+{
+       wpas_p2p_disable_cross_connect(wpa_s);
+}
+
+
+static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_supplicant *iface;
+
+       if (!wpa_s->global->cross_connection)
+               return;
+
+       for (iface = wpa_s->global->ifaces; iface; iface = iface->next) {
+               if (iface == wpa_s)
+                       continue;
+               if (iface->drv_flags &
+                   WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE)
+                       continue;
+               if (iface->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE)
+                       continue;
+
+               wpa_s->cross_connect_enabled = 1;
+               os_strlcpy(wpa_s->cross_connect_uplink, iface->ifname,
+                          sizeof(wpa_s->cross_connect_uplink));
+               wpa_printf(MSG_DEBUG, "P2P: Enable cross connection from "
+                          "%s to %s whenever uplink is available",
+                          wpa_s->ifname, wpa_s->cross_connect_uplink);
+
+               if (iface->ap_iface || iface->current_ssid == NULL ||
+                   iface->current_ssid->mode != WPAS_MODE_INFRA ||
+                   iface->cross_connect_disallowed ||
+                   iface->wpa_state != WPA_COMPLETED)
+                       break;
+
+               wpa_s->cross_connect_in_use = 1;
+               wpa_msg(wpa_s->parent, MSG_INFO,
+                       P2P_EVENT_CROSS_CONNECT_ENABLE "%s %s",
+                       wpa_s->ifname, wpa_s->cross_connect_uplink);
+               break;
+       }
+}