* Wi-Fi Direct - P2P module
* 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"
#include "eloop.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
#include "wps/wps_i.h"
#include "p2p_i.h"
#include "p2p.h"
{
struct p2p_device *dev, *n;
struct os_time now;
+ size_t i;
os_get_time(&now);
dl_list_for_each_safe(dev, n, &p2p->devices, struct p2p_device, list) {
if (dev->last_seen.sec + P2P_PEER_EXPIRATION_AGE >= now.sec)
continue;
+
+ if (p2p->cfg->go_connected &&
+ p2p->cfg->go_connected(p2p->cfg->cb_ctx,
+ dev->info.p2p_device_addr)) {
+ /*
+ * We are connected as a client to a group in which the
+ * peer is the GO, so do not expire the peer entry.
+ */
+ os_get_time(&dev->last_seen);
+ continue;
+ }
+
+ for (i = 0; i < p2p->num_groups; i++) {
+ if (p2p_group_is_client_connected(
+ p2p->groups[i], dev->info.p2p_device_addr))
+ break;
+ }
+ if (i < p2p->num_groups) {
+ /*
+ * The peer is connected as a client in a group where
+ * we are the GO, so do not expire the peer entry.
+ */
+ os_get_time(&dev->last_seen);
+ continue;
+ }
+
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Expiring old peer "
"entry " MACSTR, MAC2STR(dev->info.p2p_device_addr));
dl_list_del(&dev->list);
return "INVITE";
case P2P_INVITE_LISTEN:
return "INVITE_LISTEN";
+ case P2P_SEARCH_WHEN_READY:
+ return "SEARCH_WHEN_READY";
default:
return "?";
}
}
+u16 p2p_get_provisioning_info(struct p2p_data *p2p, const u8 *addr)
+{
+ struct p2p_device *dev = NULL;
+
+ if (!addr || !p2p)
+ return 0;
+
+ dev = p2p_get_device(p2p, addr);
+ if (dev)
+ return dev->wps_prov_info;
+ else
+ return 0;
+}
+
+
+void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *addr)
+{
+ struct p2p_device *dev = NULL;
+
+ if (!addr || !p2p)
+ return;
+
+ dev = p2p_get_device(p2p, addr);
+ if (dev)
+ dev->wps_prov_info = 0;
+}
+
+
void p2p_set_state(struct p2p_data *p2p, int new_state)
{
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: State %s -> %s",
struct p2p_go_neg_results res;
p2p_clear_timeout(p2p);
p2p_set_state(p2p, P2P_IDLE);
+ if (p2p->go_neg_peer)
+ p2p->go_neg_peer->wps_method = WPS_NOT_READY;
p2p->go_neg_peer = NULL;
os_memset(&res, 0, sizeof(res));
p2p->pending_listen_usec = (timeout % 1000) * 1000;
if (p2p->p2p_scan_running) {
+ if (p2p->start_after_scan == P2P_AFTER_SCAN_CONNECT) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+ "P2P: p2p_scan running - connect is already "
+ "pending - skip listen");
+ return 0;
+ }
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: p2p_scan running - delay start of listen state");
p2p->start_after_scan = P2P_AFTER_SCAN_LISTEN;
* group, the information will be restored in the loop following this.
*/
dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
- if (os_memcpy(dev->member_in_go_iface, go_interface_addr,
+ if (os_memcmp(dev->member_in_go_iface, go_interface_addr,
ETH_ALEN) == 0) {
os_memset(dev->member_in_go_iface, 0, ETH_ALEN);
os_memset(dev->member_in_go_dev, 0, ETH_ALEN);
for (c = 0; c < info.num_clients; c++) {
struct p2p_client_info *cli = &info.client[c];
+ if (os_memcmp(cli->p2p_device_addr, p2p->cfg->dev_addr,
+ ETH_ALEN) == 0)
+ continue; /* ignore our own entry */
dev = p2p_get_device(p2p, cli->p2p_device_addr);
if (dev) {
/*
/**
- * p2p_add_device - Add peer entries based on scan results
+ * p2p_add_device - Add peer entries based on scan results or P2P frames
* @p2p: P2P module context from p2p_init()
* @addr: Source address of Beacon or Probe Response frame (may be either
* P2P Device Address or P2P Interface Address)
* @freq: Frequency on which the Beacon or Probe Response frame was received
* @ies: IEs from the Beacon or Probe Response frame
* @ies_len: Length of ies buffer in octets
+ * @scan_res: Whether this was based on scan results
* Returns: 0 on success, -1 on failure
*
* If the scan result is for a GO, the clients in the group will also be added
* Info attributes.
*/
int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, int level,
- const u8 *ies, size_t ies_len)
+ const u8 *ies, size_t ies_len, int scan_res)
{
struct p2p_device *dev;
struct p2p_message msg;
}
}
- if (dev->listen_freq && dev->listen_freq != freq) {
+ if (dev->listen_freq && dev->listen_freq != freq && scan_res) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Update Listen frequency based on scan "
"results (" MACSTR " %d -> %d MHz (DS param %d)",
MAC2STR(dev->info.p2p_device_addr), dev->listen_freq,
freq, msg.ds_params ? *msg.ds_params : -1);
}
- dev->listen_freq = freq;
- if (msg.group_info)
- dev->oper_freq = freq;
- dev->level = level;
+ if (scan_res) {
+ dev->listen_freq = freq;
+ if (msg.group_info)
+ dev->oper_freq = freq;
+ }
+ dev->info.level = level;
p2p_copy_wps_info(dev, 0, &msg);
break;
}
- p2p_add_group_clients(p2p, p2p_dev_addr, addr, freq, msg.group_info,
- msg.group_info_len);
+ if (scan_res) {
+ p2p_add_group_clients(p2p, p2p_dev_addr, addr, freq,
+ msg.group_info, msg.group_info_len);
+ }
p2p_parse_free(&msg);
{
int i;
- if (p2p->go_neg_peer == dev)
+ if (p2p->go_neg_peer == dev) {
+ /*
+ * If GO Negotiation is in progress, report that it has failed.
+ */
+ p2p_go_neg_failed(p2p, dev, -1);
p2p->go_neg_peer = NULL;
+ }
if (p2p->invite_peer == dev)
p2p->invite_peer = NULL;
if (p2p->sd_peer == dev)
if (p2p->pending_client_disc_go == dev)
p2p->pending_client_disc_go = NULL;
- p2p->cfg->dev_lost(p2p->cfg->cb_ctx, dev->info.p2p_device_addr);
+ /* dev_lost() device, but only if it was previously dev_found() */
+ if (dev->flags & P2P_DEV_REPORTED_ONCE)
+ p2p->cfg->dev_lost(p2p->cfg->cb_ctx,
+ dev->info.p2p_device_addr);
for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
wpabuf_free(dev->info.wps_vendor_ext[i]);
{
int freq = 0;
enum p2p_scan_type type;
+ u16 pw_id = DEV_PW_DEFAULT;
if (p2p->drv_in_listen) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Driver is still "
type = P2P_SCAN_SPECIFIC;
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search "
"for freq %u (GO Neg)", freq);
+
+ /* Advertise immediate availability of WPS credential */
+ pw_id = p2p_wps_method_pw_id(p2p->go_neg_peer->wps_method);
} else if (p2p->invite_peer) {
/*
* Only scan the known listen frequency of the peer
}
if (p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, type, freq,
- p2p->num_req_dev_types, p2p->req_dev_types) < 0)
- {
+ p2p->num_req_dev_types, p2p->req_dev_types,
+ p2p->find_dev_id, pw_id)) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Scan request failed");
p2p_continue_find(p2p);
enum p2p_after_scan op;
if (p2p->after_scan_tx) {
- int ret;
/* TODO: schedule p2p_run_after_scan to be called from TX
* status callback(?) */
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send pending "
"Action frame at p2p_scan completion");
- ret = p2p->cfg->send_action(p2p->cfg->cb_ctx,
- p2p->after_scan_tx->freq,
- p2p->after_scan_tx->dst,
- p2p->after_scan_tx->src,
- p2p->after_scan_tx->bssid,
- (u8 *) (p2p->after_scan_tx + 1),
- p2p->after_scan_tx->len,
- p2p->after_scan_tx->wait_time);
+ p2p->cfg->send_action(p2p->cfg->cb_ctx,
+ p2p->after_scan_tx->freq,
+ p2p->after_scan_tx->dst,
+ p2p->after_scan_tx->src,
+ p2p->after_scan_tx->bssid,
+ (u8 *) (p2p->after_scan_tx + 1),
+ p2p->after_scan_tx->len,
+ p2p->after_scan_tx->wait_time);
os_free(p2p->after_scan_tx);
p2p->after_scan_tx = NULL;
return 1;
int p2p_find(struct p2p_data *p2p, 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)
{
int res;
p2p->num_req_dev_types = num_req_dev_types;
}
+ if (dev_id) {
+ os_memcpy(p2p->find_dev_id_buf, dev_id, ETH_ALEN);
+ p2p->find_dev_id = p2p->find_dev_id_buf;
+ } else
+ p2p->find_dev_id = NULL;
+
p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
p2p_clear_timeout(p2p);
p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
p2p_device_clear_reported(p2p);
p2p_set_state(p2p, P2P_SEARCH);
eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
+ p2p->last_p2p_find_timeout = timeout;
if (timeout)
eloop_register_timeout(timeout, 0, p2p_find_timeout,
p2p, NULL);
case P2P_FIND_PROGRESSIVE:
res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_FULL, 0,
p2p->num_req_dev_types,
- p2p->req_dev_types);
+ p2p->req_dev_types, dev_id,
+ DEV_PW_DEFAULT);
break;
case P2P_FIND_ONLY_SOCIAL:
res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_SOCIAL, 0,
p2p->num_req_dev_types,
- p2p->req_dev_types);
+ p2p->req_dev_types, dev_id,
+ DEV_PW_DEFAULT);
break;
default:
return -1;
eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout,
p2p, NULL);
+ } else if (res == 1) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Could not start "
+ "p2p_scan at this point - will try again after "
+ "previous scan completes");
+ res = 0;
+ p2p_set_state(p2p, P2P_SEARCH_WHEN_READY);
+ eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
} else {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Failed to start "
"p2p_scan");
+ p2p_set_state(p2p, P2P_IDLE);
+ eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
}
return res;
}
+int p2p_other_scan_completed(struct p2p_data *p2p)
+{
+ if (p2p->state != P2P_SEARCH_WHEN_READY)
+ return 0;
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting pending P2P find "
+ "now that previous scan was completed");
+ if (p2p_find(p2p, p2p->last_p2p_find_timeout, p2p->find_type,
+ p2p->num_req_dev_types, p2p->req_dev_types,
+ p2p->find_dev_id) < 0)
+ return 0;
+ return 1;
+}
+
+
void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq)
{
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Stopping find");
eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
p2p_clear_timeout(p2p);
+ if (p2p->state == P2P_SEARCH)
+ wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, P2P_EVENT_FIND_STOPPED);
p2p_set_state(p2p, P2P_IDLE);
p2p_free_req_dev_types(p2p);
p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
p2p->go_neg_peer = NULL;
p2p->sd_peer = NULL;
p2p->invite_peer = NULL;
+ p2p_stop_listen_for_freq(p2p, freq);
+}
+
+
+void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq)
+{
if (freq > 0 && p2p->drv_in_listen == freq && p2p->in_listen) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Skip stop_listen "
"since we are on correct channel for response");
return;
}
+ if (p2p->in_listen) {
+ p2p->in_listen = 0;
+ p2p_clear_timeout(p2p);
+ }
+ if (p2p->drv_in_listen) {
+ /*
+ * The driver may not deliver callback to p2p_listen_end()
+ * when the operation gets canceled, so clear the internal
+ * variable that is tracking driver state.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Clear "
+ "drv_in_listen (%d)", p2p->drv_in_listen);
+ p2p->drv_in_listen = 0;
+ }
p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
}
}
+static void p2p_set_dev_persistent(struct p2p_device *dev,
+ int persistent_group)
+{
+ switch (persistent_group) {
+ case 0:
+ dev->flags &= ~(P2P_DEV_PREFER_PERSISTENT_GROUP |
+ P2P_DEV_PREFER_PERSISTENT_RECONN);
+ break;
+ case 1:
+ dev->flags |= P2P_DEV_PREFER_PERSISTENT_GROUP;
+ dev->flags &= ~P2P_DEV_PREFER_PERSISTENT_RECONN;
+ break;
+ case 2:
+ dev->flags |= P2P_DEV_PREFER_PERSISTENT_GROUP |
+ P2P_DEV_PREFER_PERSISTENT_RECONN;
+ break;
+ }
+}
+
+
int p2p_connect(struct p2p_data *p2p, 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,
+ const u8 *force_ssid, size_t force_ssid_len,
+ int pd_before_go_neg)
{
struct p2p_device *dev;
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Request to start group negotiation - peer=" MACSTR
" GO Intent=%d Intended Interface Address=" MACSTR
- " wps_method=%d persistent_group=%d",
+ " wps_method=%d persistent_group=%d pd_before_go_neg=%d",
MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
- wps_method, persistent_group);
+ wps_method, persistent_group, pd_before_go_neg);
if (p2p_prepare_channel(p2p, force_freq) < 0)
return -1;
*/
}
+ p2p->ssid_set = 0;
+ if (force_ssid) {
+ wpa_hexdump_ascii(MSG_DEBUG, "P2P: Forced SSID",
+ force_ssid, force_ssid_len);
+ os_memcpy(p2p->ssid, force_ssid, force_ssid_len);
+ p2p->ssid_len = force_ssid_len;
+ p2p->ssid_set = 1;
+ }
+
dev->flags &= ~P2P_DEV_NOT_YET_READY;
dev->flags &= ~P2P_DEV_USER_REJECTED;
dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM;
+ if (pd_before_go_neg)
+ dev->flags |= P2P_DEV_PD_BEFORE_GO_NEG;
+ else
+ dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG;
dev->connect_reqs = 0;
dev->go_neg_req_sent = 0;
dev->go_state = UNKNOWN_GO;
- if (persistent_group)
- dev->flags |= P2P_DEV_PREFER_PERSISTENT_GROUP;
- else
- dev->flags &= ~P2P_DEV_PREFER_PERSISTENT_GROUP;
+ p2p_set_dev_persistent(dev, persistent_group);
p2p->go_intent = go_intent;
os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN);
int p2p_authorize(struct p2p_data *p2p, 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,
+ const u8 *force_ssid, size_t force_ssid_len)
{
struct p2p_device *dev;
return -1;
}
+ p2p->ssid_set = 0;
+ if (force_ssid) {
+ wpa_hexdump_ascii(MSG_DEBUG, "P2P: Forced SSID",
+ force_ssid, force_ssid_len);
+ os_memcpy(p2p->ssid, force_ssid, force_ssid_len);
+ p2p->ssid_len = force_ssid_len;
+ p2p->ssid_set = 1;
+ }
+
dev->flags &= ~P2P_DEV_NOT_YET_READY;
dev->flags &= ~P2P_DEV_USER_REJECTED;
dev->go_neg_req_sent = 0;
dev->go_state = UNKNOWN_GO;
- if (persistent_group)
- dev->flags |= P2P_DEV_PREFER_PERSISTENT_GROUP;
- else
- dev->flags &= ~P2P_DEV_PREFER_PERSISTENT_GROUP;
+ p2p_set_dev_persistent(dev, persistent_group);
p2p->go_intent = go_intent;
os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN);
os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr, ETH_ALEN);
os_memcpy(res.peer_interface_addr, peer->intended_addr, ETH_ALEN);
res.wps_method = peer->wps_method;
- if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP)
- res.persistent_group = 1;
+ if (peer->flags & P2P_DEV_PREFER_PERSISTENT_GROUP) {
+ if (peer->flags & P2P_DEV_PREFER_PERSISTENT_RECONN)
+ res.persistent_group = 2;
+ else
+ res.persistent_group = 1;
+ }
if (go) {
/* Setup AP mode for WPS provisioning */
res.peer_config_timeout = go ? peer->client_timeout : peer->go_timeout;
p2p_clear_timeout(p2p);
+ p2p->ssid_set = 0;
peer->go_neg_req_sent = 0;
peer->wps_method = WPS_NOT_READY;
}
-void p2p_rx_action_public(struct p2p_data *p2p, const u8 *da, const u8 *sa,
- const u8 *bssid, const u8 *data, size_t len,
- int freq)
+static void p2p_rx_action_public(struct p2p_data *p2p, const u8 *da,
+ const u8 *sa, const u8 *bssid, const u8 *data,
+ size_t len, int freq)
{
if (len < 1)
return;
if (dev) {
if (dev->country[0] == 0 && msg.listen_channel)
os_memcpy(dev->country, msg.listen_channel, 3);
+ os_get_time(&dev->last_seen);
p2p_parse_free(&msg);
return; /* already known */
}
{
struct wpabuf *buf;
u8 *len;
+ int pw_id = -1;
buf = wpabuf_alloc(1000);
if (buf == NULL)
return NULL;
- p2p_build_wps_ie(p2p, buf, DEV_PW_DEFAULT, 1);
+ if (p2p->go_neg_peer) {
+ /* Advertise immediate availability of WPS credential */
+ pw_id = p2p_wps_method_pw_id(p2p->go_neg_peer->wps_method);
+ }
+
+ p2p_build_wps_ie(p2p, buf, pw_id, 1);
/* P2P IE */
len = p2p_buf_add_ie_hdr(buf);
- p2p_buf_add_capability(buf, p2p->dev_capab, 0);
+ p2p_buf_add_capability(buf, p2p->dev_capab &
+ ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
if (p2p->ext_listen_interval)
p2p_buf_add_ext_listen_timing(buf, p2p->ext_listen_period,
p2p->ext_listen_interval);
}
-static void p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *ie,
- size_t ie_len)
+static int is_11b(u8 rate)
+{
+ return rate == 0x02 || rate == 0x04 || rate == 0x0b || rate == 0x16;
+}
+
+
+static int supp_rates_11b_only(struct ieee802_11_elems *elems)
+{
+ int num_11b = 0, num_others = 0;
+ int i;
+
+ if (elems->supp_rates == NULL && elems->ext_supp_rates == NULL)
+ return 0;
+
+ for (i = 0; elems->supp_rates && i < elems->supp_rates_len; i++) {
+ if (is_11b(elems->supp_rates[i]))
+ num_11b++;
+ else
+ num_others++;
+ }
+
+ for (i = 0; elems->ext_supp_rates && i < elems->ext_supp_rates_len;
+ i++) {
+ if (is_11b(elems->ext_supp_rates[i]))
+ num_11b++;
+ else
+ num_others++;
+ }
+
+ return num_11b > 0 && num_others == 0;
+}
+
+
+static enum p2p_probe_req_status
+p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
+ const u8 *bssid, const u8 *ie, size_t ie_len)
{
struct ieee802_11_elems elems;
struct wpabuf *buf;
struct ieee80211_mgmt *resp;
- struct wpabuf *wps;
+ struct p2p_message msg;
struct wpabuf *ies;
if (!p2p->in_listen || !p2p->drv_in_listen) {
/* not in Listen state - ignore Probe Request */
- return;
+ return P2P_PREQ_NOT_LISTEN;
}
if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) ==
ParseFailed) {
/* Ignore invalid Probe Request frames */
- return;
+ return P2P_PREQ_MALFORMED;
}
if (elems.p2p == NULL) {
/* not a P2P probe - ignore it */
- return;
+ return P2P_PREQ_NOT_P2P;
+ }
+
+ if (dst && !is_broadcast_ether_addr(dst) &&
+ os_memcmp(dst, p2p->cfg->dev_addr, ETH_ALEN) != 0) {
+ /* Not sent to the broadcast address or our P2P Device Address
+ */
+ return P2P_PREQ_NOT_PROCESSED;
+ }
+
+ if (bssid && !is_broadcast_ether_addr(bssid)) {
+ /* Not sent to the Wildcard BSSID */
+ return P2P_PREQ_NOT_PROCESSED;
}
if (elems.ssid == NULL || elems.ssid_len != P2P_WILDCARD_SSID_LEN ||
os_memcmp(elems.ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) !=
0) {
/* not using P2P Wildcard SSID - ignore */
- return;
+ return P2P_PREQ_NOT_PROCESSED;
+ }
+
+ if (supp_rates_11b_only(&elems)) {
+ /* Indicates support for 11b rates only */
+ return P2P_PREQ_NOT_P2P;
+ }
+
+ os_memset(&msg, 0, sizeof(msg));
+ if (p2p_parse_ies(ie, ie_len, &msg) < 0) {
+ /* Could not parse P2P attributes */
+ return P2P_PREQ_NOT_P2P;
+ }
+
+ if (msg.device_id &&
+ os_memcmp(msg.device_id, p2p->cfg->dev_addr, ETH_ALEN) != 0) {
+ /* Device ID did not match */
+ p2p_parse_free(&msg);
+ return P2P_PREQ_NOT_PROCESSED;
}
/* Check Requested Device Type match */
- wps = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA);
- if (wps && !p2p_match_dev_type(p2p, wps)) {
- wpabuf_free(wps);
+ if (msg.wps_attributes &&
+ !p2p_match_dev_type(p2p, msg.wps_attributes)) {
/* No match with Requested Device Type */
- return;
+ p2p_parse_free(&msg);
+ return P2P_PREQ_NOT_PROCESSED;
}
- wpabuf_free(wps);
+ p2p_parse_free(&msg);
- if (!p2p->cfg->send_probe_resp)
- return; /* Response generated elsewhere */
+ if (!p2p->cfg->send_probe_resp) {
+ /* Response generated elsewhere */
+ return P2P_PREQ_NOT_PROCESSED;
+ }
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Reply to P2P Probe Request in Listen state");
*/
ies = p2p_build_probe_resp_ies(p2p);
if (ies == NULL)
- return;
+ return P2P_PREQ_NOT_PROCESSED;
buf = wpabuf_alloc(200 + wpabuf_len(ies));
if (buf == NULL) {
wpabuf_free(ies);
- return;
+ return P2P_PREQ_NOT_PROCESSED;
}
resp = NULL;
p2p->cfg->send_probe_resp(p2p->cfg->cb_ctx, buf);
wpabuf_free(buf);
+
+ return P2P_PREQ_NOT_PROCESSED;
}
-int p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *ie,
- size_t ie_len)
+enum p2p_probe_req_status
+p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
+ const u8 *bssid, const u8 *ie, size_t ie_len)
{
+ enum p2p_probe_req_status res;
+
p2p_add_dev_from_probe_req(p2p, addr, ie, ie_len);
- p2p_reply_probe(p2p, addr, ie, ie_len);
+ res = p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len);
if ((p2p->state == P2P_CONNECT || p2p->state == P2P_CONNECT_LISTEN) &&
p2p->go_neg_peer &&
"P2P: Found GO Negotiation peer - try to start GO "
"negotiation from timeout");
eloop_register_timeout(0, 0, p2p_go_neg_start, p2p, NULL);
- return 1;
+ return P2P_PREQ_PROCESSED;
}
if ((p2p->state == P2P_INVITE || p2p->state == P2P_INVITE_LISTEN) &&
"P2P: Found Invite peer - try to start Invite from "
"timeout");
eloop_register_timeout(0, 0, p2p_invite_start, p2p, NULL);
- return 1;
+ return P2P_PREQ_PROCESSED;
}
- return 0;
+ return res;
}
}
+int p2p_parse_dev_addr(const u8 *ies, size_t ies_len, u8 *dev_addr)
+{
+ struct wpabuf *p2p_ie;
+ struct p2p_message msg;
+ int ret = -1;
+
+ p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
+ P2P_IE_VENDOR_TYPE);
+ if (p2p_ie == NULL)
+ return -1;
+ os_memset(&msg, 0, sizeof(msg));
+ if (p2p_parse_p2p_ie(p2p_ie, &msg)) {
+ wpabuf_free(p2p_ie);
+ return -1;
+ }
+
+ if (msg.p2p_device_addr) {
+ os_memcpy(dev_addr, msg.p2p_device_addr, ETH_ALEN);
+ ret = 0;
+ } else if (msg.device_id) {
+ os_memcpy(dev_addr, msg.device_id, ETH_ALEN);
+ ret = 0;
+ }
+
+ wpabuf_free(p2p_ie);
+ return ret;
+}
+
+
static void p2p_clear_go_neg(struct p2p_data *p2p)
{
p2p->go_neg_peer = NULL;
p2p->cfg->model_number = os_strdup(cfg->model_number);
if (cfg->serial_number)
p2p->cfg->serial_number = os_strdup(cfg->serial_number);
+ if (cfg->pref_chan) {
+ p2p->cfg->pref_chan = os_malloc(cfg->num_pref_chan *
+ sizeof(struct p2p_channel));
+ if (p2p->cfg->pref_chan) {
+ os_memcpy(p2p->cfg->pref_chan, cfg->pref_chan,
+ cfg->num_pref_chan *
+ sizeof(struct p2p_channel));
+ } else
+ p2p->cfg->num_pref_chan = 0;
+ }
p2p->min_disc_int = 1;
p2p->max_disc_int = 3;
os_free(p2p->cfg->model_name);
os_free(p2p->cfg->model_number);
os_free(p2p->cfg->serial_number);
+ os_free(p2p->cfg->pref_chan);
os_free(p2p->groups);
wpabuf_free(p2p->sd_resp);
os_free(p2p->after_scan_tx);
void p2p_flush(struct p2p_data *p2p)
{
struct p2p_device *dev, *prev;
- p2p_clear_timeout(p2p);
- p2p_set_state(p2p, P2P_IDLE);
- p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
- p2p->go_neg_peer = NULL;
- eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
+ p2p_stop_find(p2p);
dl_list_for_each_safe(dev, prev, &p2p->devices, struct p2p_device,
list) {
dl_list_del(&dev->list);
} else if (dev->req_config_methods &&
!(dev->flags & P2P_DEV_PD_FOR_JOIN)) {
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send "
- "pending Provisioning Discovery Request to "
+ "pending Provision Discovery Request to "
MACSTR " (config methods 0x%x)",
MAC2STR(dev->info.p2p_device_addr),
dev->req_config_methods);
- if (p2p_send_prov_disc_req(p2p, dev, 0) == 0)
+ if (p2p_send_prov_disc_req(p2p, dev, 0, 0) == 0)
return;
}
}
* p2p_retry_pd - Retry any pending provision disc requests in IDLE state
* @p2p: P2P module context from p2p_init()
*/
-void p2p_retry_pd(struct p2p_data *p2p)
+static void p2p_retry_pd(struct p2p_data *p2p)
{
struct p2p_device *dev;
continue;
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send "
- "pending Provisioning Discovery Request to "
+ "pending Provision Discovery Request to "
MACSTR " (config methods 0x%x)",
MAC2STR(dev->info.p2p_device_addr),
dev->req_config_methods);
- p2p_send_prov_disc_req(p2p, dev, 0);
+ p2p_send_prov_disc_req(p2p, dev, 0, 0);
return;
}
}
int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq,
int level, const u8 *ies, size_t ies_len)
{
- p2p_add_device(p2p, bssid, freq, level, ies, ies_len);
+ p2p_add_device(p2p, bssid, freq, level, ies, ies_len, 1);
if (p2p->go_neg_peer && p2p->state == P2P_SEARCH &&
os_memcmp(p2p->go_neg_peer->info.p2p_device_addr, bssid, ETH_ALEN)
}
-void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies)
+void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id)
{
u8 *len = p2p_buf_add_ie_hdr(ies);
- p2p_buf_add_capability(ies, p2p->dev_capab, 0);
+ p2p_buf_add_capability(ies, p2p->dev_capab &
+ ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
+ if (dev_id)
+ p2p_buf_add_device_id(ies, dev_id);
if (p2p->cfg->reg_class && p2p->cfg->channel)
p2p_buf_add_listen_channel(ies, p2p->cfg->country,
p2p->cfg->reg_class,
}
+size_t p2p_scan_ie_buf_len(struct p2p_data *p2p)
+{
+ return 100;
+}
+
+
int p2p_ie_text(struct wpabuf *p2p_ie, char *buf, char *end)
{
return p2p_attr_text(p2p_ie, buf, end);
}
if (success) {
- dev->go_neg_req_sent++;
if (dev->flags & P2P_DEV_USER_REJECTED) {
p2p_set_state(p2p, P2P_IDLE);
return;
}
+ } else if (dev->go_neg_req_sent) {
+ /* Cancel the increment from p2p_connect_send() on failure */
+ dev->go_neg_req_sent--;
}
if (!success &&
p2p_connect_send(p2p, p2p->go_neg_peer);
return 1;
} else if (p2p->state == P2P_SEARCH) {
+ if (p2p->p2p_scan_running) {
+ /*
+ * Search is already in progress. This can happen if
+ * an Action frame RX is reported immediately after
+ * the end of a remain-on-channel operation and the
+ * response frame to that is sent using an offchannel
+ * operation while in p2p_find. Avoid an attempt to
+ * restart a scan here.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan "
+ "already in progress - do not try to start a "
+ "new one");
+ return 1;
+ }
+ if (p2p->pending_listen_freq) {
+ /*
+ * Better wait a bit if the driver is unable to start
+ * offchannel operation for some reason. p2p_search()
+ * will be started from internal timeout.
+ */
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Listen "
+ "operation did not seem to start - delay "
+ "search phase to avoid busy loop");
+ p2p_set_timeout(p2p, 0, 100000);
+ return 1;
+ }
p2p_search(p2p);
return 1;
}
static void p2p_timeout_connect(struct p2p_data *p2p)
{
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ if (p2p->go_neg_peer &&
+ (p2p->go_neg_peer->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) {
+ wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Wait for GO "
+ "Negotiation Confirm timed out - assume GO "
+ "Negotiation failed");
+ p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1);
+ return;
+ }
p2p_set_state(p2p, P2P_CONNECT_LISTEN);
p2p_listen_in_find(p2p);
}
* state once per second to give other uses a chance to use the radio.
*/
p2p_set_state(p2p, P2P_WAIT_PEER_IDLE);
- p2p_set_timeout(p2p, 1, 0);
+ p2p_set_timeout(p2p, 0, 500000);
}
p2p->pd_retries--;
p2p_retry_pd(p2p);
} else {
+ if (p2p->cfg->prov_disc_fail)
+ p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx,
+ p2p->pending_pd_devaddr,
+ P2P_PROV_DISC_TIMEOUT);
p2p_reset_pending_pd(p2p);
}
}
case P2P_INVITE_LISTEN:
p2p_timeout_invite_listen(p2p);
break;
+ case P2P_SEARCH_WHEN_READY:
+ break;
}
}
}
-static const char * p2p_wps_method_text(enum p2p_wps_method method)
+const char * p2p_wps_method_text(enum p2p_wps_method method)
{
switch (method) {
case WPS_NOT_READY:
return "not-ready";
- case WPS_PIN_LABEL:
- return "Label";
case WPS_PIN_DISPLAY:
return "Display";
case WPS_PIN_KEYPAD:
}
-int p2p_get_peer_info(struct p2p_data *p2p, const u8 *addr, int next,
- char *buf, size_t buflen)
+const struct p2p_peer_info * p2p_get_peer_info(struct p2p_data *p2p,
+ const u8 *addr, int next)
{
struct p2p_device *dev;
- int res;
- char *pos, *end;
- struct os_time now;
- char devtype[WPS_DEV_TYPE_BUFSIZE];
if (addr)
dev = p2p_get_device(p2p, addr);
}
if (dev == NULL)
+ return NULL;
+
+ return &dev->info;
+}
+
+
+int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
+ char *buf, size_t buflen)
+{
+ struct p2p_device *dev;
+ int res;
+ char *pos, *end;
+ struct os_time now;
+
+ if (info == NULL)
return -1;
+ dev = (struct p2p_device *) (((u8 *) info) -
+ offsetof(struct p2p_device, info));
+
pos = buf;
end = buf + buflen;
- res = os_snprintf(pos, end - pos, MACSTR "\n",
- MAC2STR(dev->info.p2p_device_addr));
- if (res < 0 || res >= end - pos)
- return pos - buf;
- pos += res;
-
os_get_time(&now);
res = os_snprintf(pos, end - pos,
"age=%d\n"
"listen_freq=%d\n"
- "level=%d\n"
"wps_method=%s\n"
"interface_addr=" MACSTR "\n"
"member_in_go_dev=" MACSTR "\n"
"member_in_go_iface=" MACSTR "\n"
- "pri_dev_type=%s\n"
- "device_name=%s\n"
- "manufacturer=%s\n"
- "model_name=%s\n"
- "model_number=%s\n"
- "serial_number=%s\n"
- "config_methods=0x%x\n"
- "dev_capab=0x%x\n"
- "group_capab=0x%x\n"
"go_neg_req_sent=%d\n"
"go_state=%s\n"
"dialog_token=%u\n"
"invitation_reqs=%u\n",
(int) (now.sec - dev->last_seen.sec),
dev->listen_freq,
- dev->level,
p2p_wps_method_text(dev->wps_method),
MAC2STR(dev->interface_addr),
MAC2STR(dev->member_in_go_dev),
MAC2STR(dev->member_in_go_iface),
- wps_dev_type_bin2str(dev->info.pri_dev_type,
- devtype, sizeof(devtype)),
- dev->info.device_name,
- dev->info.manufacturer,
- dev->info.model_name,
- dev->info.model_number,
- dev->info.serial_number,
- dev->info.config_methods,
- dev->info.dev_capab,
- dev->info.group_capab,
dev->go_neg_req_sent,
p2p_go_state_text(dev->go_state),
dev->dialog_token,
}
+int p2p_peer_known(struct p2p_data *p2p, const u8 *addr)
+{
+ return p2p_get_device(p2p, addr) != NULL;
+}
+
+
void p2p_set_client_discoverability(struct p2p_data *p2p, int enabled)
{
if (enabled) {
}
+int p2p_set_oper_channel(struct p2p_data *p2p, u8 op_reg_class, u8 op_channel,
+ int cfg_op_channel)
+{
+ if (p2p_channel_to_freq(p2p->cfg->country, op_reg_class, op_channel)
+ < 0)
+ return -1;
+
+ wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, "P2P: Set Operating channel: "
+ "reg_class %u channel %u", op_reg_class, op_channel);
+ p2p->cfg->op_reg_class = op_reg_class;
+ p2p->cfg->op_channel = op_channel;
+ p2p->cfg->cfg_op_channel = cfg_op_channel;
+ return 0;
+}
+
+
+int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan,
+ const struct p2p_channel *pref_chan)
+{
+ struct p2p_channel *n;
+
+ if (pref_chan) {
+ n = os_malloc(num_pref_chan * sizeof(struct p2p_channel));
+ if (n == NULL)
+ return -1;
+ os_memcpy(n, pref_chan,
+ num_pref_chan * sizeof(struct p2p_channel));
+ } else
+ n = NULL;
+
+ os_free(p2p->cfg->pref_chan);
+ p2p->cfg->pref_chan = n;
+ p2p->cfg->num_pref_chan = num_pref_chan;
+
+ return 0;
+}
+
+
int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr,
u8 *iface_addr)
{
return &dev->info;
}
+
+
+int p2p_in_progress(struct p2p_data *p2p)
+{
+ if (p2p == NULL)
+ return 0;
+ return p2p->state != P2P_IDLE && p2p->state != P2P_PROVISIONING;
+}