static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
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_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);
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->level,
+ bss->freq, &entry_ts, bss->level,
(const u8 *) (bss + 1),
bss->ie_len) > 0)
break;
wpa_printf(MSG_DEBUG, "Delaying P2P scan to allow "
"pending station mode scan to be "
"completed on interface %s", ifs->ifname);
- wpa_s->p2p_cb_on_scan_complete = 1;
+ wpa_s->global->p2p_cb_on_scan_complete = 1;
wpa_supplicant_req_scan(ifs, 0, 0);
return 1;
}
wpabuf_free(ies);
if (ret) {
- if (wpa_s->scanning ||
- wpa_s->scan_res_handler == wpas_p2p_scan_res_handler) {
- wpa_s->p2p_cb_on_scan_complete = 1;
- ret = 1;
+ for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
+ if (ifs->scanning ||
+ ifs->scan_res_handler == wpas_p2p_scan_res_handler) {
+ wpa_s->global->p2p_cb_on_scan_complete = 1;
+ ret = 1;
+ break;
+ }
}
} else
wpa_s->scan_res_handler = wpas_p2p_scan_res_handler;
if (ssid == NULL) {
/*
* The current SSID was not known, but there may still be a
- * pending P2P group interface waiting for provisioning.
+ * pending P2P group interface waiting for provisioning or a
+ * P2P group that is trying to reconnect.
*/
ssid = wpa_s->conf->ssid;
while (ssid) {
- if (ssid->p2p_group &&
- (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION ||
- (ssid->key_mgmt & WPA_KEY_MGMT_WPS)))
+ if (ssid->p2p_group && ssid->disabled != 2)
break;
ssid = ssid->next;
}
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);
if (wpa_s && ifname)
wpa_drv_if_remove(wpa_s, type, ifname);
os_free(ifname);
- return 0;
+ return 1;
}
wpa_printf(MSG_DEBUG, "P2P: Remove temporary group network");
wpa_config_remove_network(wpa_s->conf, id);
wpa_supplicant_clear_status(wpa_s);
wpa_supplicant_cancel_sched_scan(wpa_s);
+ wpa_s->sta_scan_pending = 0;
} else {
wpa_printf(MSG_DEBUG, "P2P: Temporary group network not "
"found");
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)) {
+ os_memcmp(dst, wpa_s->pending_join_iface_addr, ETH_ALEN) == 0) &&
+ wpa_s->p2p_fallback_to_go_neg) {
wpa_s->pending_pd_before_join = 0;
- if (wpa_s->p2p_fallback_to_go_neg) {
- wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No ACK for PD Req "
- "during p2p_connect-auto");
- wpas_p2p_fallback_to_go_neg(wpa_s, 0);
- return;
- }
-
- wpa_printf(MSG_DEBUG, "P2P: Starting pending "
- "join-existing-group operation (no ACK for PD "
- "Req)");
- wpas_p2p_join_start(wpa_s);
+ 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: 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(
params->peer_device_addr);
else if (wpa_s->p2p_pin[0])
wpa_supplicant_ap_wps_pin(wpa_s, params->peer_interface_addr,
- wpa_s->p2p_pin, NULL, 0);
+ wpa_s->p2p_pin, NULL, 0, 0);
os_free(wpa_s->go_params);
wpa_s->go_params = NULL;
}
{
struct wpa_ssid *ssid;
- if (wpas_copy_go_neg_results(wpa_s, params) < 0)
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Starting GO");
+ if (wpas_copy_go_neg_results(wpa_s, params) < 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not copy GO Negotiation "
+ "results");
return;
+ }
ssid = wpa_config_add_network(wpa_s->conf);
- if (ssid == NULL)
+ if (ssid == NULL) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not add network for GO");
return;
+ }
wpa_s->show_group_started = 0;
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 (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 if (ssid->passphrase)
+ wpa_config_update_psk(ssid);
ssid->ap_max_inactivity = wpa_s->parent->conf->p2p_go_max_inactivity;
wpa_s->ap_configured_cb = p2p_go_configured;
wpa_s->connect_without_scan = ssid;
wpa_s->reassociate = 1;
wpa_s->disconnected = 0;
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Request scan (that will be skipped) to "
+ "start GO)");
wpa_supplicant_req_scan(wpa_s, 0, 0);
}
}
+/*
+ * DNS Header section is used only to calculate compression pointers, so the
+ * contents of this data does not matter, but the length needs to be reserved
+ * in the virtual packet.
+ */
+#define DNS_HEADER_LEN 12
+
+/*
+ * 27-octet in-memory packet from P2P specification containing two implied
+ * queries for _tcp.lcoal. PTR IN and _udp.local. PTR IN
+ */
+#define P2P_SD_IN_MEMORY_LEN 27
+
+static int p2p_sd_dns_uncompress_label(char **upos, char *uend, u8 *start,
+ u8 **spos, const u8 *end)
+{
+ while (*spos < end) {
+ u8 val = ((*spos)[0] & 0xc0) >> 6;
+ int len;
+
+ if (val == 1 || val == 2) {
+ /* These are reserved values in RFC 1035 */
+ wpa_printf(MSG_DEBUG, "P2P: Invalid domain name "
+ "sequence starting with 0x%x", val);
+ return -1;
+ }
+
+ if (val == 3) {
+ u16 offset;
+ u8 *spos_tmp;
+
+ /* Offset */
+ if (*spos + 2 > end) {
+ wpa_printf(MSG_DEBUG, "P2P: No room for full "
+ "DNS offset field");
+ return -1;
+ }
+
+ offset = (((*spos)[0] & 0x3f) << 8) | (*spos)[1];
+ if (offset >= *spos - start) {
+ wpa_printf(MSG_DEBUG, "P2P: Invalid DNS "
+ "pointer offset %u", offset);
+ return -1;
+ }
+
+ (*spos) += 2;
+ spos_tmp = start + offset;
+ return p2p_sd_dns_uncompress_label(upos, uend, start,
+ &spos_tmp,
+ *spos - 2);
+ }
+
+ /* Label */
+ len = (*spos)[0] & 0x3f;
+ if (len == 0)
+ return 0;
+
+ (*spos)++;
+ if (*spos + len > end) {
+ wpa_printf(MSG_DEBUG, "P2P: Invalid domain name "
+ "sequence - no room for label with length "
+ "%u", len);
+ return -1;
+ }
+
+ if (*upos + len + 2 > uend)
+ return -2;
+
+ os_memcpy(*upos, *spos, len);
+ *spos += len;
+ *upos += len;
+ (*upos)[0] = '.';
+ (*upos)++;
+ (*upos)[0] = '\0';
+ }
+
+ return 0;
+}
+
+
+/* Uncompress domain names per RFC 1035 using the P2P SD in-memory packet.
+ * Returns -1 on parsing error (invalid input sequence), -2 if output buffer is
+ * not large enough */
+static int p2p_sd_dns_uncompress(char *buf, size_t buf_len, const u8 *msg,
+ size_t msg_len, size_t offset)
+{
+ /* 27-octet in-memory packet from P2P specification */
+ const char *prefix = "\x04_tcp\x05local\x00\x00\x0C\x00\x01"
+ "\x04_udp\xC0\x11\x00\x0C\x00\x01";
+ u8 *tmp, *end, *spos;
+ char *upos, *uend;
+ int ret = 0;
+
+ if (buf_len < 2)
+ return -1;
+ if (offset > msg_len)
+ return -1;
+
+ tmp = os_malloc(DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN + msg_len);
+ if (tmp == NULL)
+ return -1;
+ spos = tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN;
+ end = spos + msg_len;
+ spos += offset;
+
+ os_memset(tmp, 0, DNS_HEADER_LEN);
+ os_memcpy(tmp + DNS_HEADER_LEN, prefix, P2P_SD_IN_MEMORY_LEN);
+ os_memcpy(tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN, msg, msg_len);
+
+ upos = buf;
+ uend = buf + buf_len;
+
+ ret = p2p_sd_dns_uncompress_label(&upos, uend, tmp, &spos, end);
+ if (ret) {
+ os_free(tmp);
+ return ret;
+ }
+
+ if (upos == buf) {
+ upos[0] = '.';
+ upos[1] = '\0';
+ } else if (upos[-1] == '.')
+ upos[-1] = '\0';
+
+ os_free(tmp);
+ return 0;
+}
+
+
static struct p2p_srv_bonjour *
wpas_p2p_service_get_bonjour(struct wpa_supplicant *wpa_s,
const struct wpabuf *query)
}
+static int match_bonjour_query(struct p2p_srv_bonjour *bsrv, const u8 *query,
+ size_t query_len)
+{
+ char str_rx[256], str_srv[256];
+
+ if (query_len < 3 || wpabuf_len(bsrv->query) < 3)
+ return 0; /* Too short to include DNS Type and Version */
+ if (os_memcmp(query + query_len - 3,
+ wpabuf_head_u8(bsrv->query) + wpabuf_len(bsrv->query) - 3,
+ 3) != 0)
+ return 0; /* Mismatch in DNS Type or Version */
+ if (query_len == wpabuf_len(bsrv->query) &&
+ os_memcmp(query, wpabuf_head(bsrv->query), query_len - 3) == 0)
+ return 1; /* Binary match */
+
+ if (p2p_sd_dns_uncompress(str_rx, sizeof(str_rx), query, query_len - 3,
+ 0))
+ return 0; /* Failed to uncompress query */
+ if (p2p_sd_dns_uncompress(str_srv, sizeof(str_srv),
+ wpabuf_head(bsrv->query),
+ wpabuf_len(bsrv->query) - 3, 0))
+ return 0; /* Failed to uncompress service */
+
+ return os_strcmp(str_rx, str_srv) == 0;
+}
+
+
static void wpas_sd_req_bonjour(struct wpa_supplicant *wpa_s,
struct wpabuf *resp, u8 srv_trans_id,
const u8 *query, size_t query_len)
{
struct p2p_srv_bonjour *bsrv;
- struct wpabuf buf;
u8 *len_pos;
+ int matches = 0;
wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for Bonjour",
query, query_len);
return;
}
- if (wpabuf_tailroom(resp) < 5)
- return;
- /* Length (to be filled) */
- len_pos = wpabuf_put(resp, 2);
- wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
- wpabuf_put_u8(resp, srv_trans_id);
+ dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
+ struct p2p_srv_bonjour, list) {
+ if (!match_bonjour_query(bsrv, query, query_len))
+ continue;
+
+ if (wpabuf_tailroom(resp) <
+ 5 + query_len + wpabuf_len(bsrv->resp))
+ return;
+
+ matches++;
+
+ /* Length (to be filled) */
+ len_pos = wpabuf_put(resp, 2);
+ wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
+ wpabuf_put_u8(resp, srv_trans_id);
+
+ /* Status Code */
+ wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+ wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service",
+ wpabuf_head(bsrv->resp),
+ wpabuf_len(bsrv->resp));
+
+ /* Response Data */
+ wpabuf_put_data(resp, query, query_len); /* Key */
+ wpabuf_put_buf(resp, bsrv->resp); /* Value */
+
+ WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
+ }
- wpabuf_set(&buf, query, query_len);
- bsrv = wpas_p2p_service_get_bonjour(wpa_s, &buf);
- if (bsrv == NULL) {
+ if (matches == 0) {
wpa_printf(MSG_DEBUG, "P2P: Requested Bonjour service not "
"available");
+ if (wpabuf_tailroom(resp) < 5)
+ return;
+
+ /* Length (to be filled) */
+ len_pos = wpabuf_put(resp, 2);
+ wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
+ wpabuf_put_u8(resp, srv_trans_id);
/* Status Code */
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);
- return;
}
-
- /* Status Code */
- wpabuf_put_u8(resp, P2P_SD_SUCCESS);
- wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service",
- wpabuf_head(bsrv->resp), wpabuf_len(bsrv->resp));
-
- if (wpabuf_tailroom(resp) >=
- wpabuf_len(bsrv->query) + wpabuf_len(bsrv->resp)) {
- /* Response Data */
- wpabuf_put_buf(resp, bsrv->query); /* Key */
- wpabuf_put_buf(resp, bsrv->resp); /* Value */
- }
- WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
}
{
struct p2p_srv_bonjour *bsrv;
- bsrv = wpas_p2p_service_get_bonjour(wpa_s, query);
- if (bsrv) {
- wpabuf_free(query);
- wpabuf_free(bsrv->resp);
- bsrv->resp = resp;
- return 0;
- }
-
bsrv = os_zalloc(sizeof(*bsrv));
if (bsrv == NULL)
return -1;
return;
}
+ if (status == P2P_PROV_DISC_TIMEOUT_JOIN) {
+ wpa_s->pending_pd_before_join = 0;
+ wpa_printf(MSG_DEBUG, "P2P: Starting pending "
+ "join-existing-group operation (no ACK for PD "
+ "Req attempts)");
+ wpas_p2p_join_start(wpa_s);
+ return;
+ }
+
wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE
" p2p_dev_addr=" MACSTR " status=%d",
MAC2STR(peer), status);
" was accepted; op_freq=%d MHz",
MAC2STR(sa), op_freq);
if (s) {
+ int go = s->mode == WPAS_MODE_P2P_GO;
wpas_p2p_group_add_persistent(
- wpa_s, s, s->mode == WPAS_MODE_P2P_GO, 0, 0);
+ wpa_s, s, go, go ? op_freq : 0, 0);
} else if (bssid) {
+ wpa_s->user_initiated_pd = 0;
wpas_p2p_join(wpa_s, bssid, go_dev_addr,
wpa_s->p2p_wps_method, 0);
}
return;
}
+ /*
+ * The peer could have missed our ctrl::ack frame for Invitation
+ * Response and continue retransmitting the frame. To reduce the
+ * likelihood of the peer not getting successful TX status for the
+ * Invitation Response frame, wait a short time here before starting
+ * the persistent group so that we will remain on the current channel to
+ * acknowledge any possible retransmission from the peer.
+ */
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: 50 ms wait on current channel before "
+ "starting persistent group");
+ os_sleep(0, 50000);
+
wpas_p2p_group_add_persistent(wpa_s, ssid,
ssid->mode == WPAS_MODE_P2P_GO,
wpa_s->p2p_persistent_go_freq,
static struct p2p_oper_class_map op_class[] = {
{ HOSTAPD_MODE_IEEE80211G, 81, 1, 13, 1, BW20 },
- { HOSTAPD_MODE_IEEE80211G, 82, 14, 14, 1, BW20 },
#if 0 /* Do not enable HT40 on 2 GHz for now */
{ HOSTAPD_MODE_IEEE80211G, 83, 1, 9, 1, BW40PLUS },
{ HOSTAPD_MODE_IEEE80211G, 84, 5, 13, 1, BW40MINUS },
p2p.p2p_intra_bss = wpa_s->conf->p2p_intra_bss;
+ p2p.max_listen = wpa_s->max_remain_on_chan;
+
global->p2p = p2p_init(&p2p);
if (global->p2p == NULL)
return -1;
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);
static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s)
{
+ if (wpa_s->conf->p2p_no_group_iface)
+ return 0; /* separate interface disabled per configuration */
if (wpa_s->drv_flags &
(WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE |
WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P))
enum p2p_wps_method wps_method,
int go_intent, const u8 *own_interface_addr,
unsigned int force_freq, int persistent_group,
- struct wpa_ssid *ssid)
+ struct wpa_ssid *ssid, unsigned int pref_freq)
{
if (persistent_group && wpa_s->conf->persistent_reconnect)
persistent_group = 2;
go_intent, own_interface_addr, force_freq,
persistent_group, ssid ? ssid->ssid : NULL,
ssid ? ssid->ssid_len : 0,
- wpa_s->p2p_pd_before_go_neg);
+ wpa_s->p2p_pd_before_go_neg, pref_freq);
}
enum p2p_wps_method wps_method,
int go_intent, const u8 *own_interface_addr,
unsigned int force_freq, int persistent_group,
- struct wpa_ssid *ssid)
+ struct wpa_ssid *ssid, unsigned int pref_freq)
{
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, ssid ? ssid->ssid : NULL,
- ssid ? ssid->ssid_len : 0);
+ ssid ? ssid->ssid_len : 0, pref_freq);
}
}
-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;
if (p2p_prov_disc_req(wpa_s->global->p2p,
wpa_s->pending_join_dev_addr,
wpa_s->pending_pd_config_methods, join,
- 0) < 0) {
+ 0, wpa_s->user_initiated_pd) < 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",
if (p2p_prov_disc_req(wpa_s->global->p2p,
wpa_s->pending_join_dev_addr, method, 1,
- freq) < 0) {
+ freq, wpa_s->user_initiated_pd) < 0) {
wpa_printf(MSG_DEBUG, "P2P: Failed to send Provision "
"Discovery Request before joining an "
"existing group");
wpa_s->pending_pd_before_join = 0;
goto start;
}
-
- /*
- * Actual join operation will be started from the Action frame
- * 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;
}
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;
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;
}
+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()
int go_intent, int freq, int persistent_id, int pd,
int ht40)
{
- int force_freq = 0, oper_freq = 0;
+ 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;
wpa_s->p2p_auto_started.sec,
wpa_s->p2p_auto_started.usec);
}
+ wpa_s->user_initiated_pd = 1;
if (wpas_p2p_join(wpa_s, iface_addr, dev_addr, wps_method,
auto_join) < 0)
return -1;
}
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_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);
if (auth) {
if (wpas_p2p_auth_go_neg(wpa_s, peer_addr, wps_method,
go_intent, if_addr,
- force_freq, persistent_group, ssid) <
- 0)
+ force_freq, persistent_group, ssid,
+ pref_freq) < 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, ssid) < 0) {
+ persistent_group, ssid, pref_freq) < 0) {
if (wpa_s->create_p2p_iface)
wpas_p2p_remove_pending_group_interface(wpa_s);
return -1;
{
wpa_printf(MSG_DEBUG, "P2P: Cancel remain-on-channel callback "
"(p2p_long_listen=%d ms pending_action_tx=%p)",
- wpa_s->p2p_long_listen, wpa_s->pending_action_tx);
+ wpa_s->p2p_long_listen, offchannel_pending_action_tx(wpa_s));
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return;
if (p2p_listen_end(wpa_s->global->p2p, freq) > 0)
return; /* P2P module started a new operation */
- if (wpa_s->pending_action_tx)
+ if (offchannel_pending_action_tx(wpa_s))
return;
if (wpa_s->p2p_long_listen > 0)
wpa_s->p2p_long_listen -= wpa_s->max_remain_on_chan;
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);
{
struct wpa_supplicant *group_wpa_s;
- if (!wpas_p2p_create_iface(wpa_s))
+ if (!wpas_p2p_create_iface(wpa_s)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use same interface for group "
+ "operations");
return wpa_s;
+ }
if (wpas_p2p_add_group_interface(wpa_s, go ? WPA_IF_P2P_GO :
- WPA_IF_P2P_CLIENT) < 0)
+ WPA_IF_P2P_CLIENT) < 0) {
+ wpa_msg(wpa_s, MSG_ERROR, "P2P: Failed to add group interface");
return NULL;
+ }
group_wpa_s = wpas_p2p_init_group_interface(wpa_s, go);
if (group_wpa_s == NULL) {
+ wpa_msg(wpa_s, MSG_ERROR, "P2P: Failed to initialize group "
+ "interface");
wpas_p2p_remove_pending_group_interface(wpa_s);
return NULL;
}
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use separate group interface %s",
+ group_wpa_s->ifname);
return group_wpa_s;
}
/* Make sure we are not running find during connection establishment */
wpa_printf(MSG_DEBUG, "P2P: Stop any on-going P2P FIND");
- wpas_p2p_stop_find(wpa_s);
+ wpas_p2p_stop_find_oper(wpa_s);
if (freq == 2) {
wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 2.4 GHz "
}
/* Make sure we are not running find during connection establishment */
- wpas_p2p_stop_find(wpa_s);
+ wpas_p2p_stop_find_oper(wpa_s);
wpa_s->p2p_fallback_to_go_neg = 0;
return -1;
params.role_go = 1;
- if (ssid->passphrase == NULL ||
- os_strlen(ssid->passphrase) >= sizeof(params.passphrase)) {
- wpa_printf(MSG_DEBUG, "P2P: Invalid passphrase in persistent "
- "group");
- return -1;
+ params.psk_set = ssid->psk_set;
+ if (params.psk_set)
+ os_memcpy(params.psk, ssid->psk, sizeof(params.psk));
+ 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;
return p2p_prov_disc_req(wpa_s->global->p2p, peer_addr,
config_methods, use == WPAS_P2P_PD_FOR_JOIN,
- 0);
+ 0, 1);
}
static void wpas_p2p_clear_pending_action_tx(struct wpa_supplicant *wpa_s)
{
- if (!wpa_s->pending_action_tx)
+ if (!offchannel_pending_action_tx(wpa_s))
return;
wpa_printf(MSG_DEBUG, "P2P: Drop pending Action TX due to new "
"operation request");
- wpabuf_free(wpa_s->pending_action_tx);
- wpa_s->pending_action_tx = NULL;
+ offchannel_clear_pending_action_tx(wpa_s);
}
}
-void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s)
+static int wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s)
{
wpas_p2p_clear_pending_action_tx(wpa_s);
wpa_s->p2p_long_listen = 0;
eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
- wpa_s->p2p_cb_on_scan_complete = 0;
+ wpa_s->global->p2p_cb_on_scan_complete = 0;
if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
wpa_drv_p2p_stop_find(wpa_s);
- return;
+ return 1;
}
if (wpa_s->global->p2p)
p2p_stop_find(wpa_s->global->p2p);
+ return 0;
+}
+
+
+void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s)
+{
+ if (wpas_p2p_stop_find_oper(wpa_s) > 0)
+ return;
wpas_p2p_remove_pending_group_interface(wpa_s);
}
/* 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;
}
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,
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);
}
{
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;
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);
}
wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 1);
done:
- if (wpa_s->p2p_cb_on_scan_complete && !wpa_s->global->p2p_disabled &&
+ if (wpa_s->global->p2p_cb_on_scan_complete && !wpa_s->global->p2p_disabled &&
wpa_s->global->p2p != NULL) {
- wpa_s->p2p_cb_on_scan_complete = 0;
+ wpa_s->global->p2p_cb_on_scan_complete = 0;
if (p2p_other_scan_completed(wpa_s->global->p2p) == 1) {
wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Pending P2P operation "
"continued after successful connection");
static int wpas_p2p_is_client(struct wpa_supplicant *wpa_s)
{
- return wpa_s->current_ssid != NULL &&
- wpa_s->current_ssid->p2p_group &&
+ if (wpa_s->current_ssid == NULL) {
+ /*
+ * current_ssid can be cleared when P2P client interface gets
+ * disconnected, so assume this interface was used as P2P
+ * client.
+ */
+ return 1;
+ }
+ return wpa_s->current_ssid->p2p_group &&
wpa_s->current_ssid->mode == WPAS_MODE_INFRA;
}
}
-void wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
- u16 reason_code, const u8 *ie, size_t ie_len,
- int locally_generated)
+/* Returns 1 if the interface was removed */
+int wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ 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;
+ return 0;
if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
- return;
+ return 0;
if (!locally_generated)
p2p_deauth_notif(wpa_s->global->p2p, bssid, reason_code, ie,
wpa_s->current_ssid->mode == WPAS_MODE_INFRA) {
wpa_printf(MSG_DEBUG, "P2P: GO indicated that the P2P Group "
"session is ending");
- wpas_p2p_group_delete(wpa_s,
- P2P_GROUP_REMOVAL_GO_ENDING_SESSION);
+ if (wpas_p2p_group_delete(wpa_s,
+ P2P_GROUP_REMOVAL_GO_ENDING_SESSION)
+ > 0)
+ return 1;
}
+
+ return 0;
}
}
+static void wpas_p2p_scan_res_ignore(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res)
+{
+ wpa_printf(MSG_DEBUG, "P2P: Ignore scan results");
+}
+
+
int wpas_p2p_cancel(struct wpa_supplicant *wpa_s)
{
struct wpa_global *global = wpa_s->global;
found = 1;
}
+ if (wpa_s->scan_res_handler == wpas_p2p_scan_res_join) {
+ wpa_printf(MSG_DEBUG, "P2P: Stop pending scan for join");
+ wpa_s->scan_res_handler = wpas_p2p_scan_res_ignore;
+ found = 1;
+ }
+
+ if (wpa_s->pending_pd_before_join) {
+ wpa_printf(MSG_DEBUG, "P2P: Stop pending PD before join");
+ wpa_s->pending_pd_before_join = 0;
+ found = 1;
+ }
+
wpas_p2p_stop_find(wpa_s);
for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
found = 1;
eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
wpa_s->parent, NULL);
+ if (wpa_s->p2p_in_provisioning) {
+ wpas_group_formation_completed(wpa_s, 0);
+ break;
+ }
wpas_p2p_group_delete(wpa_s,
P2P_GROUP_REMOVAL_REQUESTED);
break;
if (wpa_s == NULL)
return -1;
- return wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_REQUESTED);
+ return wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_REQUESTED) < 0 ?
+ -1 : 0;
}
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;
}
int group_added)
{
struct wpa_supplicant *group = wpa_s;
- eloop_cancel_timeout(wpas_p2p_pd_before_join_timeout, wpa_s, NULL);
if (wpa_s->global->p2p_group_formation)
group = wpa_s->global->p2p_group_formation;
wpa_s = wpa_s->parent;