P2P: Allow P2P client to specify preferred group channel
[mech_eap.git] / wpa_supplicant / p2p_supplicant.c
index 202857b..004cf74 100644 (file)
@@ -86,6 +86,8 @@ 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_group_formation_timeout(void *eloop_ctx,
+                                            void *timeout_ctx);
 static void wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s,
                                        int group_added);
 static int wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s);
@@ -104,8 +106,12 @@ static void wpas_p2p_scan_res_handler(struct wpa_supplicant *wpa_s,
 
        for (i = 0; i < scan_res->num; i++) {
                struct wpa_scan_res *bss = scan_res->res[i];
+               struct os_time time_tmp_age, entry_ts;
+               time_tmp_age.sec = bss->age / 1000;
+               time_tmp_age.usec = (bss->age % 1000) * 1000;
+               os_time_sub(&scan_res->fetch_time, &time_tmp_age, &entry_ts);
                if (p2p_scan_res_handler(wpa_s->global->p2p, bss->bssid,
-                                        bss->freq, bss->age, bss->level,
+                                        bss->freq, &entry_ts, bss->level,
                                         (const u8 *) (bss + 1),
                                         bss->ie_len) > 0)
                        break;
@@ -316,6 +322,10 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s,
 
        if (eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL) > 0)
                wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group idle timeout");
+       if (eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
+                                wpa_s->parent, NULL) > 0)
+               wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group formation "
+                          "timeout");
 
        if (removal_reason != P2P_GROUP_REMOVAL_SILENT && ssid)
                wpas_notify_p2p_group_removed(wpa_s, ssid, gtype);
@@ -796,15 +806,28 @@ static void p2p_go_configured(void *ctx, void *data)
                wpa_printf(MSG_DEBUG, "P2P: Group setup without provisioning");
                if (wpa_s->global->p2p_group_formation == wpa_s)
                        wpa_s->global->p2p_group_formation = NULL;
-               wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
-                       "%s GO ssid=\"%s\" freq=%d passphrase=\"%s\" "
-                       "go_dev_addr=" MACSTR "%s",
-                       wpa_s->ifname,
-                       wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
-                       ssid->frequency,
-                       params->passphrase ? params->passphrase : "",
-                       MAC2STR(wpa_s->global->p2p_dev_addr),
-                       params->persistent_group ? " [PERSISTENT]" : "");
+               if (os_strlen(params->passphrase) > 0) {
+                       wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
+                               "%s GO ssid=\"%s\" freq=%d passphrase=\"%s\" "
+                               "go_dev_addr=" MACSTR "%s", wpa_s->ifname,
+                               wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
+                               ssid->frequency, params->passphrase,
+                               MAC2STR(wpa_s->global->p2p_dev_addr),
+                               params->persistent_group ? " [PERSISTENT]" :
+                               "");
+               } else {
+                       char psk[65];
+                       wpa_snprintf_hex(psk, sizeof(psk), params->psk,
+                                        sizeof(params->psk));
+                       wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
+                               "%s GO ssid=\"%s\" freq=%d psk=%s "
+                               "go_dev_addr=" MACSTR "%s", wpa_s->ifname,
+                               wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
+                               ssid->frequency, psk,
+                               MAC2STR(wpa_s->global->p2p_dev_addr),
+                               params->persistent_group ? " [PERSISTENT]" :
+                               "");
+               }
 
                if (params->persistent_group)
                        network_id = wpas_p2p_store_persistent_group(
@@ -874,17 +897,20 @@ static void wpas_start_wps_go(struct wpa_supplicant *wpa_s,
        ssid->key_mgmt = WPA_KEY_MGMT_PSK;
        ssid->proto = WPA_PROTO_RSN;
        ssid->pairwise_cipher = WPA_CIPHER_CCMP;
-       ssid->passphrase = os_strdup(params->passphrase);
-       if (ssid->passphrase == NULL) {
-               wpa_msg(wpa_s, MSG_ERROR, "P2P: Failed to copy passphrase for "
-                       "GO");
-               wpa_config_remove_network(wpa_s->conf, ssid->id);
-               return;
-       }
+       if (os_strlen(params->passphrase) > 0) {
+               ssid->passphrase = os_strdup(params->passphrase);
+               if (ssid->passphrase == NULL) {
+                       wpa_msg(wpa_s, MSG_ERROR, "P2P: Failed to copy "
+                               "passphrase for GO");
+                       wpa_config_remove_network(wpa_s->conf, ssid->id);
+                       return;
+               }
+       } else
+               ssid->passphrase = NULL;
        ssid->psk_set = params->psk_set;
        if (ssid->psk_set)
                os_memcpy(ssid->psk, params->psk, sizeof(ssid->psk));
-       else
+       else if (ssid->passphrase)
                wpa_config_update_psk(ssid);
        ssid->ap_max_inactivity = wpa_s->parent->conf->p2p_go_max_inactivity;
 
@@ -3476,10 +3502,18 @@ static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s)
                os_memcpy(group->p2p_pin, wpa_s->p2p_pin,
                          sizeof(group->p2p_pin));
                group->p2p_wps_method = wpa_s->p2p_wps_method;
+       } else {
+               /*
+                * Need to mark the current interface for p2p_group_formation
+                * when a separate group interface is not used. This is needed
+                * to allow p2p_cancel stop a pending p2p_connect-join.
+                * wpas_p2p_init_group_interface() addresses this for the case
+                * where a separate group interface is used.
+                */
+               wpa_s->global->p2p_group_formation = wpa_s;
        }
 
        group->p2p_in_provisioning = 1;
-       wpa_s->global->p2p_group_formation = wpa_s;
        group->p2p_fallback_to_go_neg = wpa_s->p2p_fallback_to_go_neg;
 
        os_memset(&res, 0, sizeof(res));
@@ -3515,6 +3549,60 @@ static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s)
 }
 
 
+static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq,
+                               int *force_freq, int *pref_freq,
+                               int *oper_freq)
+{
+       if (freq > 0) {
+               if (!p2p_supported_freq(wpa_s->global->p2p, freq)) {
+                       wpa_printf(MSG_DEBUG, "P2P: The forced channel "
+                                  "(%u MHz) is not supported for P2P uses",
+                                  freq);
+                       return -3;
+               }
+
+               if (*oper_freq > 0 && freq != *oper_freq &&
+                   !(wpa_s->drv_flags &
+                     WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)) {
+                       wpa_printf(MSG_DEBUG, "P2P: Cannot start P2P group "
+                                  "on %u MHz while connected on another "
+                                  "channel (%u MHz)", freq, *oper_freq);
+                       return -2;
+               }
+               wpa_printf(MSG_DEBUG, "P2P: Trying to force us to use the "
+                          "requested channel (%u MHz)", freq);
+               *force_freq = freq;
+       } else if (*oper_freq > 0 &&
+                  !p2p_supported_freq(wpa_s->global->p2p, *oper_freq)) {
+               if (!(wpa_s->drv_flags &
+                     WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)) {
+                       wpa_printf(MSG_DEBUG, "P2P: Cannot start P2P group "
+                                  "while connected on non-P2P supported "
+                                  "channel (%u MHz)", *oper_freq);
+                       return -2;
+               }
+               wpa_printf(MSG_DEBUG, "P2P: Current operating channel "
+                          "(%u MHz) not available for P2P - try to use "
+                          "another channel", *oper_freq);
+               *force_freq = 0;
+       } else if (*oper_freq > 0 && *pref_freq == 0 &&
+                  (wpa_s->drv_flags &
+                   WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)) {
+               wpa_printf(MSG_DEBUG, "P2P: Trying to prefer the channel we "
+                          "are already using (%u MHz) on another interface",
+                          *oper_freq);
+               *pref_freq = *oper_freq;
+       } else if (*oper_freq > 0) {
+               wpa_printf(MSG_DEBUG, "P2P: Trying to force us to use the "
+                          "channel we are already using (%u MHz) on another "
+                          "interface", *oper_freq);
+               *force_freq = *oper_freq;
+       }
+
+       return 0;
+}
+
+
 /**
  * wpas_p2p_connect - Request P2P Group Formation to be started
  * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
@@ -3545,7 +3633,7 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
 {
        int force_freq = 0, pref_freq = 0, oper_freq = 0;
        u8 bssid[ETH_ALEN];
-       int ret = 0;
+       int ret = 0, res;
        enum wpa_driver_if_type iftype;
        const u8 *if_addr;
        struct wpa_ssid *ssid = NULL;
@@ -3617,59 +3705,18 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
        }
 
        if (wpa_s->current_ssid && wpa_drv_get_bssid(wpa_s, bssid) == 0 &&
-           wpa_s->assoc_freq)
+           wpa_s->assoc_freq) {
                oper_freq = wpa_s->assoc_freq;
-       else {
+       else {
                oper_freq = wpa_drv_shared_freq(wpa_s);
                if (oper_freq < 0)
                        oper_freq = 0;
        }
 
-       if (freq > 0) {
-               if (!p2p_supported_freq(wpa_s->global->p2p, freq)) {
-                       wpa_printf(MSG_DEBUG, "P2P: The forced channel "
-                                  "(%u MHz) is not supported for P2P uses",
-                                  freq);
-                       return -3;
-               }
-
-               if (oper_freq > 0 && freq != oper_freq &&
-                   !(wpa_s->drv_flags &
-                     WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)) {
-                       wpa_printf(MSG_DEBUG, "P2P: Cannot start P2P group "
-                                  "on %u MHz while connected on another "
-                                  "channel (%u MHz)", freq, oper_freq);
-                       return -2;
-               }
-               wpa_printf(MSG_DEBUG, "P2P: Trying to force us to use the "
-                          "requested channel (%u MHz)", freq);
-               force_freq = freq;
-       } else if (oper_freq > 0 &&
-                  !p2p_supported_freq(wpa_s->global->p2p, oper_freq)) {
-               if (!(wpa_s->drv_flags &
-                     WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)) {
-                       wpa_printf(MSG_DEBUG, "P2P: Cannot start P2P group "
-                                  "while connected on non-P2P supported "
-                                  "channel (%u MHz)", oper_freq);
-                       return -2;
-               }
-               wpa_printf(MSG_DEBUG, "P2P: Current operating channel "
-                          "(%u MHz) not available for P2P - try to use "
-                          "another channel", oper_freq);
-               force_freq = 0;
-       } else if (oper_freq > 0 &&
-                  (wpa_s->drv_flags &
-                   WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)) {
-               wpa_printf(MSG_DEBUG, "P2P: Trying to prefer the channel we "
-                          "are already using (%u MHz) on another interface",
-                          oper_freq);
-               pref_freq = oper_freq;
-       } else if (oper_freq > 0) {
-               wpa_printf(MSG_DEBUG, "P2P: Trying to force us to use the "
-                          "channel we are already using (%u MHz) on another "
-                          "interface", oper_freq);
-               force_freq = oper_freq;
-       }
+       res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
+                                  &oper_freq);
+       if (res)
+               return res;
 
        wpa_s->create_p2p_iface = wpas_p2p_create_iface(wpa_s);
 
@@ -3832,7 +3879,11 @@ static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s,
                wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured "
                           "frequency %d MHz", params->freq);
        } else if (wpa_s->conf->p2p_oper_reg_class == 115 ||
-                  wpa_s->conf->p2p_oper_reg_class == 124) {
+                  wpa_s->conf->p2p_oper_reg_class == 116 ||
+                  wpa_s->conf->p2p_oper_reg_class == 117 ||
+                  wpa_s->conf->p2p_oper_reg_class == 124 ||
+                  wpa_s->conf->p2p_oper_reg_class == 126 ||
+                  wpa_s->conf->p2p_oper_reg_class == 127) {
                params->freq = 5000 + 5 * wpa_s->conf->p2p_oper_channel;
                wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured "
                           "frequency %d MHz", params->freq);
@@ -4099,14 +4150,15 @@ int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
        params.psk_set = ssid->psk_set;
        if (params.psk_set)
                os_memcpy(params.psk, ssid->psk, sizeof(params.psk));
-       if (ssid->passphrase == NULL ||
-           os_strlen(ssid->passphrase) >= sizeof(params.passphrase)) {
-               wpa_printf(MSG_DEBUG, "P2P: Invalid passphrase in persistent "
-                          "group");
-               return -1;
+       if (ssid->passphrase) {
+               if (os_strlen(ssid->passphrase) >= sizeof(params.passphrase)) {
+                       wpa_printf(MSG_ERROR, "P2P: Invalid passphrase in "
+                                  "persistent group");
+                       return -1;
+               }
+               os_strlcpy(params.passphrase, ssid->passphrase,
+                          sizeof(params.passphrase));
        }
-       os_strlcpy(params.passphrase, ssid->passphrase,
-                  sizeof(params.passphrase));
        os_memcpy(params.ssid, ssid->ssid, ssid->ssid_len);
        params.ssid_len = ssid->ssid_len;
        params.persistent_group = 1;
@@ -4531,10 +4583,12 @@ int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr)
 /* Invite to reinvoke a persistent group */
 int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
                    struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq,
-                   int ht40)
+                   int ht40, int pref_freq)
 {
        enum p2p_invite_role role;
-       u8 *bssid = NULL;
+       u8 *bssid = NULL, bssid_buf[ETH_ALEN];
+       int force_freq = 0, oper_freq = 0;
+       int res;
 
        wpa_s->p2p_persistent_go_freq = freq;
        wpa_s->p2p_go_ht40 = !!ht40;
@@ -4562,6 +4616,22 @@ int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
        }
        wpa_s->pending_invite_ssid_id = ssid->id;
 
+       if (wpa_s->current_ssid && wpa_drv_get_bssid(wpa_s, bssid_buf) == 0 &&
+           wpa_s->assoc_freq) {
+               oper_freq = wpa_s->assoc_freq;
+               if (bssid == NULL)
+                       bssid = bssid_buf;
+       } else {
+               oper_freq = wpa_drv_shared_freq(wpa_s);
+               if (oper_freq < 0)
+                       oper_freq = 0;
+       }
+
+       res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
+                                  &oper_freq);
+       if (res)
+               return res;
+
        if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
                return wpa_drv_p2p_invite(wpa_s, peer_addr, role, bssid,
                                          ssid->ssid, ssid->ssid_len,
@@ -4571,7 +4641,8 @@ int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
                return -1;
 
        return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid,
-                         ssid->ssid, ssid->ssid_len, freq, go_dev_addr, 1);
+                         ssid->ssid, ssid->ssid_len, force_freq, go_dev_addr,
+                         1, pref_freq);
 }
 
 
@@ -4581,9 +4652,11 @@ int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname,
 {
        struct wpa_global *global = wpa_s->global;
        enum p2p_invite_role role;
-       u8 *bssid = NULL;
+       u8 *bssid = NULL, bssid_buf[ETH_ALEN];
        struct wpa_ssid *ssid;
        int persistent;
+       int force_freq = 0, oper_freq = 0, pref_freq = 0;
+       int res;
 
        wpa_s->p2p_persistent_go_freq = 0;
        wpa_s->p2p_go_ht40 = 0;
@@ -4635,9 +4708,25 @@ int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname,
        if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
                return -1;
 
+       if (wpa_s->current_ssid && wpa_drv_get_bssid(wpa_s, bssid_buf) == 0 &&
+           wpa_s->assoc_freq) {
+               oper_freq = wpa_s->assoc_freq;
+               if (bssid == NULL)
+                       bssid = bssid_buf;
+       } else {
+               oper_freq = wpa_drv_shared_freq(wpa_s);
+               if (oper_freq < 0)
+                       oper_freq = 0;
+       }
+
+       res = wpas_p2p_setup_freqs(wpa_s, 0, &force_freq, &pref_freq,
+                                  &oper_freq);
+       if (res)
+               return res;
+
        return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid,
-                         ssid->ssid, ssid->ssid_len, wpa_s->assoc_freq,
-                         go_dev_addr, persistent);
+                         ssid->ssid, ssid->ssid_len, force_freq,
+                         go_dev_addr, persistent, pref_freq);
 }
 
 
@@ -5324,10 +5413,29 @@ int wpas_p2p_disconnect(struct wpa_supplicant *wpa_s)
 
 int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s)
 {
+       int ret;
+
        if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
                return 0;
 
-       return p2p_in_progress(wpa_s->global->p2p);
+       ret = p2p_in_progress(wpa_s->global->p2p);
+       if (ret == 0) {
+               /*
+                * Check whether there is an ongoing WPS provisioning step (or
+                * other parts of group formation) on another interface since
+                * p2p_in_progress() does not report this to avoid issues for
+                * scans during such provisioning step.
+                */
+               if (wpa_s->global->p2p_group_formation &&
+                   wpa_s->global->p2p_group_formation != wpa_s) {
+                       wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Another interface (%s) "
+                               "in group formation",
+                               wpa_s->global->p2p_group_formation->ifname);
+                       ret = 1;
+               }
+       }
+
+       return ret;
 }