* wpa_supplicant - P2P
* Copyright (c) 2009-2010, Atheros Communications
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
*/
#include "includes.h"
*/
#define P2P_MAX_JOIN_SCAN_ATTEMPTS 10
+#ifndef P2P_MAX_CLIENT_IDLE
+/*
+ * How many seconds to try to reconnect to the GO when connection in P2P client
+ * role has been lost.
+ */
+#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 *
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,
static int wpas_p2p_scan(void *ctx, enum p2p_scan_type type, int freq,
unsigned int num_req_dev_types,
- const u8 *req_dev_types)
+ const u8 *req_dev_types, const u8 *dev_id, u16 pw_id)
{
struct wpa_supplicant *wpa_s = ctx;
struct wpa_driver_scan_params params;
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,
+ wps_ie = wps_build_probe_req_ie(pw_id, &wpa_s->wps->dev,
+ wpa_s->wps->uuid, WPS_REQ_ENROLLEE,
num_req_dev_types, req_dev_types);
if (wps_ie == NULL)
return -1;
wpabuf_put_buf(ies, wps_ie);
wpabuf_free(wps_ie);
- p2p_scan_ie(wpa_s->global->p2p, ies);
+ p2p_scan_ie(wpa_s->global->p2p, ies, dev_id);
params.p2p_probe = 1;
params.extra_ies = wpabuf_head(ies);
}
-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;
const char *reason;
- eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
-
ssid = wpa_s->current_ssid;
if (ssid == NULL) {
/*
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) {
global = wpa_s->global;
ifname = os_strdup(wpa_s->ifname);
type = wpas_p2p_if_type(wpa_s->p2p_group_interface);
- wpa_supplicant_remove_iface(wpa_s->global, wpa_s);
+ wpa_supplicant_remove_iface(wpa_s->global, wpa_s, 0);
wpa_s = global->ifaces;
if (wpa_s && ifname)
wpa_drv_if_remove(wpa_s, type, ifname);
*/
wpa_config_remove_network(wpa_s->conf, id);
wpa_supplicant_clear_status(wpa_s);
+ wpa_supplicant_cancel_sched_scan(wpa_s);
} else {
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);
}
}
+static void wpas_p2p_add_persistent_group_client(struct wpa_supplicant *wpa_s,
+ const u8 *addr)
+{
+ struct wpa_ssid *ssid, *s;
+ u8 *n;
+ size_t i;
+
+ ssid = wpa_s->current_ssid;
+ if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GO ||
+ !ssid->p2p_persistent_group)
+ return;
+
+ for (s = wpa_s->parent->conf->ssid; s; s = s->next) {
+ if (s->disabled != 2 || s->mode != WPAS_MODE_P2P_GO)
+ continue;
+
+ if (s->ssid_len == ssid->ssid_len &&
+ os_memcmp(s->ssid, ssid->ssid, s->ssid_len) == 0)
+ break;
+ }
+
+ if (s == NULL)
+ return;
+
+ for (i = 0; s->p2p_client_list && i < s->num_p2p_clients; i++) {
+ if (os_memcmp(s->p2p_client_list + i * ETH_ALEN, addr,
+ ETH_ALEN) == 0)
+ return; /* already in list */
+ }
+
+ n = os_realloc(s->p2p_client_list,
+ (s->num_p2p_clients + 1) * ETH_ALEN);
+ if (n == NULL)
+ return;
+ os_memcpy(n + s->num_p2p_clients * ETH_ALEN, addr, ETH_ALEN);
+ s->p2p_client_list = n;
+ s->num_p2p_clients++;
+
+#ifndef CONFIG_NO_CONFIG_WRITE
+ if (wpa_s->parent->conf->update_config &&
+ wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf))
+ wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
+#endif /* CONFIG_NO_CONFIG_WRITE */
+}
+
+
static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s,
int success)
{
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;
}
client = ssid->mode == WPAS_MODE_INFRA;
if (ssid->mode == WPAS_MODE_P2P_GO) {
persistent = ssid->p2p_persistent_group;
- os_memcpy(go_dev_addr, wpa_s->parent->own_addr,
+ os_memcpy(go_dev_addr, wpa_s->global->p2p_dev_addr,
ETH_ALEN);
} else
persistent = wpas_p2p_persistent_group(wpa_s,
p2p_send_action_cb(wpa_s->global->p2p, freq, dst, src, bssid, res);
- if (wpa_s->pending_pd_before_join &&
+ if (result != OFFCHANNEL_SEND_ACTION_SUCCESS &&
+ wpa_s->pending_pd_before_join &&
(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");
+ "join-existing-group operation (no ACK for PD "
+ "Req)");
wpas_p2p_join_start(wpa_s);
}
}
wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
ssid->frequency,
params->passphrase ? params->passphrase : "",
- MAC2STR(wpa_s->parent->own_addr),
+ MAC2STR(wpa_s->global->p2p_dev_addr),
params->persistent_group ? " [PERSISTENT]" : "");
if (params->persistent_group)
network_id = wpas_p2p_store_persistent_group(
wpa_s->parent, ssid,
- wpa_s->parent->own_addr);
+ wpa_s->global->p2p_dev_addr);
if (network_id < 0)
network_id = ssid->id;
wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 0);
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;
}
if (res->status) {
wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_FAILURE "status=%d",
res->status);
- wpas_notify_p2p_go_neg_completed(wpa_s, res->status);
+ wpas_notify_p2p_go_neg_completed(wpa_s, res);
wpas_p2p_remove_pending_group_interface(wpa_s);
return;
}
wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_SUCCESS);
- wpas_notify_p2p_go_neg_completed(wpa_s, P2P_SC_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 =
const struct p2p_peer_info *info,
int new_device)
{
+#ifndef CONFIG_NO_STDOUT_DEBUG
struct wpa_supplicant *wpa_s = ctx;
char devtype[WPS_DEV_TYPE_BUFSIZE];
sizeof(devtype)),
info->device_name, info->config_methods,
info->dev_capab, info->group_capab);
+#endif /* CONFIG_NO_STDOUT_DEBUG */
wpas_notify_p2p_device_found(ctx, info->p2p_device_addr, new_device);
}
os_free(buf);
}
- if (wpa_s->p2p_sd_over_ctrl_iface)
+ if (wpa_s->p2p_sd_over_ctrl_iface) {
+ wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token,
+ update_indic, tlvs, tlvs_len);
return; /* to be processed by an external program */
+ }
resp = wpabuf_alloc(10000);
if (resp == NULL)
{
struct wpa_supplicant *wpa_s = ctx;
unsigned int generated_pin = 0;
-
- if (config_methods & WPS_CONFIG_DISPLAY)
- wpas_prov_disc_local_keypad(wpa_s, peer, "");
- else if (config_methods & WPS_CONFIG_KEYPAD) {
- generated_pin = wps_generate_pin();
- wpas_prov_disc_local_display(wpa_s, peer, "", generated_pin);
- } else if (config_methods & WPS_CONFIG_PUSHBUTTON)
- wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_PBC_RESP MACSTR,
- MAC2STR(peer));
-
- wpas_notify_p2p_provision_discovery(wpa_s, peer, 0 /* response */,
- P2P_PROV_DISC_SUCCESS,
- config_methods, generated_pin);
+ char params[20];
if (wpa_s->pending_pd_before_join &&
(os_memcmp(peer, wpa_s->pending_join_dev_addr, ETH_ALEN) == 0 ||
wpa_printf(MSG_DEBUG, "P2P: Starting pending "
"join-existing-group operation");
wpas_p2p_join_start(wpa_s);
+ 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, params);
+ else if (config_methods & WPS_CONFIG_KEYPAD) {
+ generated_pin = wps_generate_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
+ "%s", MAC2STR(peer), params);
+
+ wpas_notify_p2p_provision_discovery(wpa_s, peer, 0 /* response */,
+ P2P_PROV_DISC_SUCCESS,
+ config_methods, generated_pin);
}
{
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);
}
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;
}
}
+static int wpas_p2p_disallowed_freq(struct wpa_global *global,
+ unsigned int freq)
+{
+ unsigned int i;
+
+ if (global->p2p_disallow_freq == NULL)
+ return 0;
+
+ for (i = 0; i < global->num_p2p_disallow_freq; i++) {
+ if (freq >= global->p2p_disallow_freq[i].min &&
+ freq <= global->p2p_disallow_freq[i].max)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void wpas_p2p_add_chan(struct p2p_reg_class *reg, u8 chan)
+{
+ reg->channel[reg->channels] = chan;
+ reg->channels++;
+}
+
+
static int wpas_p2p_default_channels(struct wpa_supplicant *wpa_s,
struct p2p_channels *chan)
{
/* Operating class 81 - 2.4 GHz band channels 1..13 */
chan->reg_class[cla].reg_class = 81;
- chan->reg_class[cla].channels = 11;
- for (i = 0; i < 11; i++)
- chan->reg_class[cla].channel[i] = i + 1;
- cla++;
+ chan->reg_class[cla].channels = 0;
+ for (i = 0; i < 11; i++) {
+ if (!wpas_p2p_disallowed_freq(wpa_s->global, 2412 + i * 5))
+ wpas_p2p_add_chan(&chan->reg_class[cla], i + 1);
+ }
+ if (chan->reg_class[cla].channels)
+ cla++;
wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for lower 5 GHz "
"band");
/* Operating class 115 - 5 GHz, channels 36-48 */
chan->reg_class[cla].reg_class = 115;
- chan->reg_class[cla].channels = 4;
- chan->reg_class[cla].channel[0] = 36;
- chan->reg_class[cla].channel[1] = 40;
- chan->reg_class[cla].channel[2] = 44;
- chan->reg_class[cla].channel[3] = 48;
- cla++;
+ chan->reg_class[cla].channels = 0;
+ if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 36 * 5))
+ wpas_p2p_add_chan(&chan->reg_class[cla], 36);
+ if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 40 * 5))
+ wpas_p2p_add_chan(&chan->reg_class[cla], 40);
+ if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 44 * 5))
+ wpas_p2p_add_chan(&chan->reg_class[cla], 44);
+ if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 48 * 5))
+ wpas_p2p_add_chan(&chan->reg_class[cla], 48);
+ if (chan->reg_class[cla].channels)
+ cla++;
wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for higher 5 GHz "
"band");
/* Operating class 124 - 5 GHz, channels 149,153,157,161 */
chan->reg_class[cla].reg_class = 124;
- chan->reg_class[cla].channels = 4;
- chan->reg_class[cla].channel[0] = 149;
- chan->reg_class[cla].channel[1] = 153;
- chan->reg_class[cla].channel[2] = 157;
- chan->reg_class[cla].channel[3] = 161;
- cla++;
+ chan->reg_class[cla].channels = 0;
+ if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 149 * 5))
+ wpas_p2p_add_chan(&chan->reg_class[cla], 149);
+ if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 153 * 5))
+ wpas_p2p_add_chan(&chan->reg_class[cla], 153);
+ if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 156 * 5))
+ wpas_p2p_add_chan(&chan->reg_class[cla], 157);
+ if (!wpas_p2p_disallowed_freq(wpa_s->global, 5000 + 161 * 5))
+ wpas_p2p_add_chan(&chan->reg_class[cla], 161);
+ if (chan->reg_class[cla].channels)
+ cla++;
chan->reg_classes = cla;
return 0;
}
-static int has_channel(struct hostapd_hw_modes *mode, u8 chan, int *flags)
+static int has_channel(struct wpa_global *global,
+ struct hostapd_hw_modes *mode, u8 chan, int *flags)
{
int i;
+ unsigned int freq;
+
+ freq = (mode->mode == HOSTAPD_MODE_IEEE80211A ? 5000 : 2407) +
+ chan * 5;
+ if (wpas_p2p_disallowed_freq(global, freq))
+ return 0;
for (i = 0; i < mode->num_channels; i++) {
if (mode->channels[i].chan == chan) {
continue;
for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) {
int flag;
- if (!has_channel(mode, ch, &flag))
+ if (!has_channel(wpa_s->global, mode, ch, &flag))
continue;
if (o->bw == BW40MINUS &&
(!(flag & HOSTAPD_CHAN_HT40MINUS) ||
- !has_channel(mode, ch - 4, NULL)))
+ !has_channel(wpa_s->global, mode, ch - 4, NULL)))
continue;
if (o->bw == BW40PLUS &&
(!(flag & HOSTAPD_CHAN_HT40PLUS) ||
- !has_channel(mode, ch + 4, NULL)))
+ !has_channel(wpa_s->global, mode, ch + 4, NULL)))
continue;
if (reg == NULL) {
wpa_printf(MSG_DEBUG, "P2P: Add operating "
}
+static int wpas_go_connected(void *ctx, const u8 *dev_addr)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+ if (ssid == NULL)
+ continue;
+ if (ssid->mode != WPAS_MODE_INFRA)
+ continue;
+ if (wpa_s->wpa_state != WPA_COMPLETED &&
+ wpa_s->wpa_state != WPA_GROUP_HANDSHAKE)
+ continue;
+ if (os_memcmp(wpa_s->go_dev_addr, dev_addr, ETH_ALEN) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+
/**
* wpas_p2p_init - Initialize P2P module for %wpa_supplicant
* @global: Pointer to global data from wpa_supplicant_init()
p2p.invitation_received = wpas_invitation_received;
p2p.invitation_result = wpas_invitation_result;
p2p.get_noa = wpas_get_noa;
+ p2p.go_connected = wpas_go_connected;
os_memcpy(wpa_s->global->p2p_dev_addr, wpa_s->own_addr, ETH_ALEN);
- os_memcpy(p2p.dev_addr, wpa_s->own_addr, ETH_ALEN);
+ os_memcpy(p2p.dev_addr, wpa_s->global->p2p_dev_addr, ETH_ALEN);
p2p.dev_name = wpa_s->conf->device_name;
p2p.manufacturer = wpa_s->conf->manufacturer;
p2p.model_name = wpa_s->conf->model_name;
/* 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);
wpa_s->go_params = NULL;
eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL);
eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
+ eloop_cancel_timeout(wpas_p2p_pd_before_join_timeout, wpa_s, NULL);
wpa_s->p2p_long_listen = 0;
eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
void wpas_p2p_deinit_global(struct wpa_global *global)
{
struct wpa_supplicant *wpa_s, *tmp;
- char *ifname;
if (global->p2p == NULL)
return;
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 ||
}
if (tmp == NULL)
break;
- ifname = os_strdup(tmp->ifname);
- type = wpas_p2p_if_type(tmp->p2p_group_interface);
- wpa_supplicant_remove_iface(global, tmp);
- 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);
}
/*
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;
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);
}
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;
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);
}
" 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);
}
}
+static void wpas_p2p_pd_before_join_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ if (!wpa_s->pending_pd_before_join)
+ return;
+ /*
+ * Provision Discovery Response may have been lost - try to connect
+ * anyway since we do not need any information from this PD.
+ */
+ wpa_printf(MSG_DEBUG, "P2P: PD timeout for join-existing-group - "
+ "try to connect anyway");
+ wpas_p2p_join_start(wpa_s);
+}
+
+
+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)
{
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 &&
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)",
}
if (p2p_prov_disc_req(wpa_s->global->p2p,
- wpa_s->pending_join_dev_addr, method, 1)
- < 0) {
+ wpa_s->pending_join_dev_addr, method, 1,
+ freq) < 0) {
wpa_printf(MSG_DEBUG, "P2P: Failed to send Provision "
"Discovery Request before joining an "
"existing group");
/*
* Actual join operation will be started from the Action frame
- * TX status callback.
+ * TX status callback (if no ACK is received) or when the
+ * Provision Discovery Response is received. Use a short
+ * timeout as a backup mechanism should the Provision Discovery
+ * Response be lost for any reason.
*/
+ eloop_cancel_timeout(wpas_p2p_pd_before_join_timeout, wpa_s,
+ NULL);
+ eloop_register_timeout(2, 0, wpas_p2p_pd_before_join_timeout,
+ wpa_s, NULL);
return;
}
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, 0, NULL);
+ wps_ie = wps_build_probe_req_ie(DEV_PW_DEFAULT, &wpa_s->wps->dev,
+ wpa_s->wps->uuid, WPS_REQ_ENROLLEE, 0,
+ NULL);
if (wps_ie == NULL) {
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);
+ p2p_scan_ie(wpa_s->global->p2p, ies, NULL);
params.p2p_probe = 1;
params.extra_ies = wpabuf_head(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;
ret = wpa_drv_scan(wpa_s, ¶ms);
+ if (!ret)
+ wpa_s->scan_res_handler = wpas_p2p_scan_res_join;
wpabuf_free(ies);
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;
{
struct wpa_supplicant *group;
struct p2p_go_neg_results res;
+ struct wpa_bss *bss;
+ eloop_cancel_timeout(wpas_p2p_pd_before_join_timeout, wpa_s, NULL);
group = wpas_p2p_get_group_iface(wpa_s, 0, 0);
if (group == NULL)
return -1;
}
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,
ETH_ALEN);
res.wps_method = wpa_s->pending_join_wps_method;
+ bss = wpa_bss_get_bssid(wpa_s, wpa_s->pending_join_iface_addr);
+ if (bss) {
+ res.freq = bss->freq;
+ res.ssid_len = bss->ssid_len;
+ os_memcpy(res.ssid, bss->ssid, bss->ssid_len);
+ }
+
+ if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
+ wpa_printf(MSG_DEBUG, "P2P: Cancel remain-on-channel prior to "
+ "starting client");
+ wpa_drv_cancel_remain_on_channel(wpa_s);
+ wpa_s->off_channel_freq = 0;
+ wpa_s->roc_waiting_drv_freq = 0;
+ }
wpas_start_wps_enrollee(group, &res);
/*
* @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;
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));
} 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 "
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;
}
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;
}
-static void wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s,
- struct p2p_go_neg_results *params,
- int freq)
+static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s,
+ struct p2p_go_neg_results *params,
+ int freq)
{
u8 bssid[ETH_ALEN];
int res;
wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 5 GHz "
"channel %d MHz", params->freq);
} else {
- params->freq = 2412;
+ int chan;
+ for (chan = 0; chan < 11; chan++) {
+ params->freq = 2412 + chan * 5;
+ if (!wpas_p2p_disallowed_freq(wpa_s->global,
+ params->freq))
+ break;
+ }
+ if (chan == 11) {
+ wpa_printf(MSG_DEBUG, "P2P: No 2.4 GHz channel "
+ "allowed");
+ return -1;
+ }
wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz (no preference "
"known)", params->freq);
}
wpa_printf(MSG_DEBUG, "P2P: Force GO on the channel we are "
"already using on a shared interface");
params->freq = res;
+ } else if (res > 0 && freq != res &&
+ !(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, res);
+ return -1;
}
+
+ return 0;
}
return -1;
}
- wpas_p2p_init_go_params(wpa_s, ¶ms, freq);
+ if (wpas_p2p_init_go_params(wpa_s, ¶ms, 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, ¶ms);
params.persistent_group = persistent_group;
/* 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);
if (ssid->mode != WPAS_MODE_P2P_GO)
return -1;
- wpas_p2p_init_go_params(wpa_s, ¶ms, freq);
+ if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq))
+ return -1;
params.role_go = 1;
if (ssid->passphrase == NULL ||
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)
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);
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)
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);
+ config_methods, use == WPAS_P2P_PD_FOR_JOIN,
+ 0);
}
int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout,
enum p2p_discovery_type type,
- unsigned int num_req_dev_types, const u8 *req_dev_types)
+ unsigned int num_req_dev_types, const u8 *req_dev_types,
+ const u8 *dev_id)
{
wpas_p2p_clear_pending_action_tx(wpa_s);
wpa_s->p2p_long_listen = 0;
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return -1;
+ wpa_supplicant_cancel_sched_scan(wpa_s);
+
return p2p_find(wpa_s->global->p2p, timeout, type,
- num_req_dev_types, req_dev_types);
+ num_req_dev_types, req_dev_types, dev_id);
}
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return -1;
+ wpa_supplicant_cancel_sched_scan(wpa_s);
wpas_p2p_clear_pending_action_tx(wpa_s);
if (timeout == 0) {
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;
+ }
}
if (wpa_s->global->p2p == NULL)
return;
- p2p_scan_ie(wpa_s->global->p2p, ies);
+ p2p_scan_ie(wpa_s->global->p2p, ies, NULL);
}
enum p2p_invite_role role;
u8 *bssid = NULL;
struct wpa_ssid *ssid;
+ int persistent;
for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
if (os_strcmp(wpa_s->ifname, ifname) == 0)
return -1;
}
+ persistent = ssid->p2p_persistent_group &&
+ wpas_p2p_get_persistent(wpa_s->parent, peer_addr,
+ ssid->ssid, ssid->ssid_len);
+
if (ssid->mode == WPAS_MODE_P2P_GO) {
role = P2P_INVITE_ROLE_ACTIVE_GO;
bssid = wpa_s->own_addr;
if (go_dev_addr == NULL)
- go_dev_addr = wpa_s->parent->own_addr;
+ go_dev_addr = wpa_s->global->p2p_dev_addr;
} else {
role = P2P_INVITE_ROLE_CLIENT;
if (wpa_s->wpa_state < WPA_ASSOCIATED) {
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,
- go_dev_addr, 0);
+ go_dev_addr, persistent);
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return -1;
return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid,
ssid->ssid, ssid->ssid_len, wpa_s->assoc_freq,
- go_dev_addr, 0);
+ go_dev_addr, persistent);
}
int persistent;
int freq;
+ eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->parent,
+ NULL);
+
if (!wpa_s->show_group_started || !ssid)
return;
}
+static int wpas_p2p_is_client(struct wpa_supplicant *wpa_s)
+{
+ return wpa_s->current_ssid != NULL &&
+ wpa_s->current_ssid->p2p_group &&
+ wpa_s->current_ssid->mode == WPAS_MODE_INFRA;
+}
+
+
static void wpas_p2p_group_idle_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
- if (wpa_s->conf->p2p_group_idle == 0) {
+ if (wpa_s->conf->p2p_group_idle == 0 && !wpas_p2p_is_client(wpa_s)) {
wpa_printf(MSG_DEBUG, "P2P: Ignore group idle timeout - "
"disabled");
return;
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)
{
- eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
- if (wpa_s->conf->p2p_group_idle == 0)
- return;
+ 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");
if (wpa_s->current_ssid == NULL || !wpa_s->current_ssid->p2p_group)
return;
+ timeout = wpa_s->conf->p2p_group_idle;
+ if (wpa_s->current_ssid->mode == WPAS_MODE_INFRA &&
+ (timeout == 0 || timeout > P2P_MAX_CLIENT_IDLE))
+ timeout = P2P_MAX_CLIENT_IDLE;
+
+ 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",
- wpa_s->conf->p2p_group_idle);
- eloop_register_timeout(wpa_s->conf->p2p_group_idle, 0,
- wpas_p2p_group_idle_timeout, wpa_s, NULL);
+ timeout);
+ eloop_register_timeout(timeout, 0, wpas_p2p_group_idle_timeout,
+ wpa_s, NULL);
}
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);
}
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");
+ }
+ }
}
wpas_p2p_disable_cross_connect(wpa_s);
else
wpas_p2p_enable_cross_connect(wpa_s);
- if (!wpa_s->ap_iface)
- eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
+ if (!wpa_s->ap_iface &&
+ eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL) > 0)
+ wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group idle timeout");
}
void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s)
{
wpas_p2p_disable_cross_connect(wpa_s);
- if (!wpa_s->ap_iface)
+ if (!wpa_s->ap_iface &&
+ !eloop_is_timeout_registered(wpas_p2p_group_idle_timeout,
+ wpa_s, NULL))
wpas_p2p_set_group_idle_timeout(wpa_s);
}
wpa_printf(MSG_DEBUG, "P2P: Unauthorize pending GO Neg peer "
MACSTR, MAC2STR(peer));
p2p_unauthorize(global->p2p, peer);
+ found = 1;
}
wpas_p2p_stop_find(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;
}
}
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);
}
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;
}
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);
+ }
+}
+
+
+struct wpa_ssid * wpas_p2p_get_persistent(struct wpa_supplicant *wpa_s,
+ const u8 *addr, const u8 *ssid,
+ size_t ssid_len)
+{
+ struct wpa_ssid *s;
+ size_t i;
+
+ for (s = wpa_s->conf->ssid; s; s = s->next) {
+ if (s->disabled != 2)
+ continue;
+ if (ssid &&
+ (ssid_len != s->ssid_len ||
+ os_memcmp(ssid, s->ssid, ssid_len) != 0))
+ continue;
+ if (os_memcmp(s->bssid, addr, ETH_ALEN) == 0)
+ return s; /* peer is GO in the persistent group */
+ if (s->mode != WPAS_MODE_P2P_GO || s->p2p_client_list == NULL)
+ continue;
+ for (i = 0; i < s->num_p2p_clients; i++) {
+ if (os_memcmp(s->p2p_client_list + i * ETH_ALEN,
+ addr, ETH_ALEN) == 0)
+ return s; /* peer is P2P client in persistent
+ * group */
+ }
}
+
+ return NULL;
+}
+
+
+void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s,
+ const u8 *addr)
+{
+ if (addr == NULL)
+ 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;
}