summaryrefslogtreecommitdiff
path: root/src/p2p
diff options
context:
space:
mode:
authorAndrew Shadura <andrewsh@debian.org>2016-07-21 14:48:54 +0200
committerAndrew Shadura <andrewsh@debian.org>2016-07-21 14:48:54 +0200
commit2b2675a5c8455e6a7224b153a28a6c2b5f1acd70 (patch)
tree3f83c6ef1b28171f58dccff1397bc01be1c01956 /src/p2p
parent6e1f9c092d6958a8a1c01629f43941647dc7fc20 (diff)
Imported Upstream version 2.4
Diffstat (limited to 'src/p2p')
-rw-r--r--src/p2p/p2p.c738
-rw-r--r--src/p2p/p2p.h237
-rw-r--r--src/p2p/p2p_build.c257
-rw-r--r--src/p2p/p2p_go_neg.c54
-rw-r--r--src/p2p/p2p_group.c12
-rw-r--r--src/p2p/p2p_i.h75
-rw-r--r--src/p2p/p2p_invitation.c69
-rw-r--r--src/p2p/p2p_parse.c142
-rw-r--r--src/p2p/p2p_pd.c735
-rw-r--r--src/p2p/p2p_sd.c14
-rw-r--r--src/p2p/p2p_utils.c116
11 files changed, 2194 insertions, 255 deletions
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index 9985e4f..6adb3dc 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -13,6 +13,8 @@
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "common/wpa_ctrl.h"
+#include "crypto/sha256.h"
+#include "crypto/crypto.h"
#include "wps/wps_i.h"
#include "p2p_i.h"
#include "p2p.h"
@@ -151,6 +153,19 @@ const char * p2p_get_state_txt(struct p2p_data *p2p)
}
+struct p2ps_advertisement * p2p_get_p2ps_adv_list(struct p2p_data *p2p)
+{
+ return p2p ? p2p->p2ps_adv_list : NULL;
+}
+
+
+void p2p_set_intended_addr(struct p2p_data *p2p, const u8 *intended_addr)
+{
+ if (p2p && intended_addr)
+ os_memcpy(p2p->intended_addr, intended_addr, ETH_ALEN);
+}
+
+
u16 p2p_get_provisioning_info(struct p2p_data *p2p, const u8 *addr)
{
struct p2p_device *dev = NULL;
@@ -211,29 +226,35 @@ void p2p_clear_timeout(struct p2p_data *p2p)
}
-void p2p_go_neg_failed(struct p2p_data *p2p, struct p2p_device *peer,
- int status)
+void p2p_go_neg_failed(struct p2p_data *p2p, int status)
{
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->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE;
- p2p->go_neg_peer->wps_method = WPS_NOT_READY;
- p2p->go_neg_peer->oob_pw_id = 0;
+ struct p2p_device *peer = p2p->go_neg_peer;
+
+ if (!peer)
+ return;
+
+ eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL);
+ if (p2p->state != P2P_SEARCH) {
+ /*
+ * Clear timeouts related to GO Negotiation if no new p2p_find
+ * has been started.
+ */
+ p2p_clear_timeout(p2p);
+ p2p_set_state(p2p, P2P_IDLE);
}
+
+ peer->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE;
+ peer->wps_method = WPS_NOT_READY;
+ peer->oob_pw_id = 0;
+ wpabuf_free(peer->go_neg_conf);
+ peer->go_neg_conf = NULL;
p2p->go_neg_peer = NULL;
os_memset(&res, 0, sizeof(res));
res.status = status;
- if (peer) {
- wpabuf_free(peer->go_neg_conf);
- peer->go_neg_conf = NULL;
- os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr,
- ETH_ALEN);
- os_memcpy(res.peer_interface_addr, peer->intended_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);
p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res);
}
@@ -348,8 +369,10 @@ int p2p_listen(struct p2p_data *p2p, unsigned int timeout)
static void p2p_device_clear_reported(struct p2p_data *p2p)
{
struct p2p_device *dev;
- dl_list_for_each(dev, &p2p->devices, struct p2p_device, list)
+ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
dev->flags &= ~P2P_DEV_REPORTED;
+ dev->sd_reqs = 0;
+ }
}
@@ -650,6 +673,24 @@ static void p2p_update_peer_vendor_elems(struct p2p_device *dev, const u8 *ies,
}
+static int p2p_compare_wfd_info(struct p2p_device *dev,
+ const struct p2p_message *msg)
+{
+ if (dev->info.wfd_subelems && msg->wfd_subelems) {
+ if (dev->info.wfd_subelems->used != msg->wfd_subelems->used)
+ return 1;
+
+ return os_memcmp(dev->info.wfd_subelems->buf,
+ msg->wfd_subelems->buf,
+ dev->info.wfd_subelems->used);
+ }
+ if (dev->info.wfd_subelems || msg->wfd_subelems)
+ return 1;
+
+ return 0;
+}
+
+
/**
* p2p_add_device - Add peer entries based on scan results or P2P frames
* @p2p: P2P module context from p2p_init()
@@ -675,6 +716,7 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
struct p2p_device *dev;
struct p2p_message msg;
const u8 *p2p_dev_addr;
+ int wfd_changed;
int i;
struct os_reltime time_now;
@@ -743,6 +785,12 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
dev->oper_ssid_len = msg.ssid[1];
}
+ if (msg.adv_service_instance && msg.adv_service_instance_len) {
+ wpabuf_free(dev->info.p2ps_instance);
+ dev->info.p2ps_instance = wpabuf_alloc_copy(
+ msg.adv_service_instance, msg.adv_service_instance_len);
+ }
+
if (freq >= 2412 && freq <= 2484 && msg.ds_params &&
*msg.ds_params >= 1 && *msg.ds_params <= 14) {
int ds_freq;
@@ -786,6 +834,8 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
break;
}
+ wfd_changed = p2p_compare_wfd_info(dev, &msg);
+
if (msg.wfd_subelems) {
wpabuf_free(dev->info.wfd_subelems);
dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
@@ -800,7 +850,9 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
p2p_update_peer_vendor_elems(dev, ies, ies_len);
- if (dev->flags & P2P_DEV_REPORTED)
+ if (dev->flags & P2P_DEV_REPORTED && !wfd_changed &&
+ (!msg.adv_service_instance ||
+ (dev->flags & P2P_DEV_P2PS_REPORTED)))
return 0;
p2p_dbg(p2p, "Peer found with Listen frequency %d MHz (rx_time=%u.%06u)",
@@ -836,6 +888,9 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
!(dev->flags & P2P_DEV_REPORTED_ONCE));
dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE;
+ if (msg.adv_service_instance)
+ dev->flags |= P2P_DEV_P2PS_REPORTED;
+
return 0;
}
@@ -848,8 +903,7 @@ static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev)
/*
* If GO Negotiation is in progress, report that it has failed.
*/
- p2p_go_neg_failed(p2p, dev, -1);
- p2p->go_neg_peer = NULL;
+ p2p_go_neg_failed(p2p, -1);
}
if (p2p->invite_peer == dev)
p2p->invite_peer = NULL;
@@ -871,6 +925,7 @@ static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev)
wpabuf_free(dev->info.wfd_subelems);
wpabuf_free(dev->info.vendor_elems);
wpabuf_free(dev->go_neg_conf);
+ wpabuf_free(dev->info.p2ps_instance);
os_free(dev);
}
@@ -956,14 +1011,8 @@ static void p2p_search(struct p2p_data *p2p)
p2p->num_req_dev_types, p2p->req_dev_types,
p2p->find_dev_id, pw_id);
if (res < 0) {
- p2p_dbg(p2p, "Scan request failed");
+ p2p_dbg(p2p, "Scan request schedule failed");
p2p_continue_find(p2p);
- } else {
- p2p_dbg(p2p, "Running p2p_scan");
- p2p->p2p_scan_running = 1;
- eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
- eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout,
- p2p, NULL);
}
}
@@ -976,6 +1025,22 @@ static void p2p_find_timeout(void *eloop_ctx, void *timeout_ctx)
}
+void p2p_notify_scan_trigger_status(struct p2p_data *p2p, int status)
+{
+ if (status != 0) {
+ p2p_dbg(p2p, "Scan request failed");
+ /* Do continue find even for the first p2p_find_scan */
+ p2p_continue_find(p2p);
+ } else {
+ p2p_dbg(p2p, "Running p2p_scan");
+ p2p->p2p_scan_running = 1;
+ eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
+ eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout,
+ p2p, NULL);
+ }
+}
+
+
static int p2p_run_after_scan(struct p2p_data *p2p)
{
struct p2p_device *dev;
@@ -1045,10 +1110,44 @@ static void p2p_free_req_dev_types(struct p2p_data *p2p)
}
+static int p2ps_gen_hash(struct p2p_data *p2p, const char *str, u8 *hash)
+{
+ u8 buf[SHA256_MAC_LEN];
+ char str_buf[256];
+ const u8 *adv_array;
+ size_t i, adv_len;
+
+ if (!str || !hash)
+ return 0;
+
+ if (!str[0]) {
+ os_memcpy(hash, p2p->wild_card_hash, P2PS_HASH_LEN);
+ return 1;
+ }
+
+ adv_array = (u8 *) str_buf;
+ adv_len = os_strlen(str);
+
+ for (i = 0; str[i] && i < adv_len; i++) {
+ if (str[i] >= 'A' && str[i] <= 'Z')
+ str_buf[i] = str[i] - 'A' + 'a';
+ else
+ str_buf[i] = str[i];
+ }
+
+ if (sha256_vector(1, &adv_array, &adv_len, buf))
+ return 0;
+
+ os_memcpy(hash, buf, P2PS_HASH_LEN);
+ 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,
- const u8 *dev_id, unsigned int search_delay)
+ const u8 *dev_id, unsigned int search_delay,
+ u8 seek_count, const char **seek, int freq)
{
int res;
@@ -1075,6 +1174,47 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout,
} else
p2p->find_dev_id = NULL;
+ if (seek_count == 0 || !seek) {
+ /* Not an ASP search */
+ p2p->p2ps_seek = 0;
+ } else if (seek_count == 1 && seek && (!seek[0] || !seek[0][0])) {
+ /*
+ * An empty seek string means no hash values, but still an ASP
+ * search.
+ */
+ p2p->p2ps_seek_count = 0;
+ p2p->p2ps_seek = 1;
+ } else if (seek && seek_count <= P2P_MAX_QUERY_HASH) {
+ u8 buf[P2PS_HASH_LEN];
+ int i;
+
+ p2p->p2ps_seek_count = seek_count;
+ for (i = 0; i < seek_count; i++) {
+ if (!p2ps_gen_hash(p2p, seek[i], buf))
+ continue;
+
+ /* If asking for wildcard, don't do others */
+ if (os_memcmp(buf, p2p->wild_card_hash,
+ P2PS_HASH_LEN) == 0) {
+ p2p->p2ps_seek_count = 0;
+ break;
+ }
+
+ os_memcpy(&p2p->query_hash[i * P2PS_HASH_LEN], buf,
+ P2PS_HASH_LEN);
+ }
+ p2p->p2ps_seek = 1;
+ } else {
+ p2p->p2ps_seek_count = 0;
+ p2p->p2ps_seek = 1;
+ }
+
+ /* Special case to perform wildcard search */
+ if (p2p->p2ps_seek_count == 0 && p2p->p2ps_seek) {
+ p2p->p2ps_seek_count = 1;
+ os_memcpy(&p2p->query_hash, p2p->wild_card_hash, P2PS_HASH_LEN);
+ }
+
p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
p2p_clear_timeout(p2p);
p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
@@ -1090,6 +1230,19 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout,
p2p, NULL);
switch (type) {
case P2P_FIND_START_WITH_FULL:
+ if (freq > 0) {
+ /*
+ * Start with the specified channel and then move to
+ * social channels only scans.
+ */
+ res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx,
+ P2P_SCAN_SPECIFIC, freq,
+ p2p->num_req_dev_types,
+ p2p->req_dev_types, dev_id,
+ DEV_PW_DEFAULT);
+ break;
+ }
+ /* fall through */
case P2P_FIND_PROGRESSIVE:
res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_FULL, 0,
p2p->num_req_dev_types,
@@ -1106,17 +1259,11 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout,
return -1;
}
- if (res == 0) {
- p2p_dbg(p2p, "Running p2p_scan");
- p2p->p2p_scan_running = 1;
- eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
- eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout,
- p2p, NULL);
- } else if (p2p->p2p_scan_running) {
+ if (res != 0 && p2p->p2p_scan_running) {
p2p_dbg(p2p, "Failed to start p2p_scan - another p2p_scan was already running");
/* wait for the previous p2p_scan to complete */
res = 0; /* do not report failure */
- } else {
+ } else if (res != 0) {
p2p_dbg(p2p, "Failed to start p2p_scan");
p2p_set_state(p2p, P2P_IDLE);
eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
@@ -1131,8 +1278,11 @@ void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq)
p2p_dbg(p2p, "Stopping find");
eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
p2p_clear_timeout(p2p);
- if (p2p->state == P2P_SEARCH)
+ if (p2p->state == P2P_SEARCH || p2p->state == P2P_SD_DURING_FIND)
p2p->cfg->find_stopped(p2p->cfg->cb_ctx);
+
+ p2p->p2ps_seek_count = 0;
+
p2p_set_state(p2p, P2P_IDLE);
p2p_free_req_dev_types(p2p);
p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
@@ -1142,6 +1292,7 @@ void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq)
p2p->sd_peer = NULL;
p2p->invite_peer = NULL;
p2p_stop_listen_for_freq(p2p, freq);
+ p2p->send_action_in_progress = 0;
}
@@ -1334,8 +1485,8 @@ int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev,
if (go)
p2p_channels_remove_freqs(&p2p->channels, &p2p->no_go_freq);
else if (!force_freq)
- p2p_channels_union(&p2p->channels, &p2p->cfg->cli_channels,
- &p2p->channels);
+ p2p_channels_union_inplace(&p2p->channels,
+ &p2p->cfg->cli_channels);
p2p_channels_dump(p2p, "after go/cli filter/add", &p2p->channels);
p2p_dbg(p2p, "Own preference for operation channel: Operating Class %u Channel %u%s",
@@ -1605,7 +1756,14 @@ void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len)
int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params)
{
- p2p_build_ssid(p2p, params->ssid, &params->ssid_len);
+ if (p2p->ssid_set) {
+ os_memcpy(params->ssid, p2p->ssid, p2p->ssid_len);
+ params->ssid_len = p2p->ssid_len;
+ } else {
+ p2p_build_ssid(p2p, params->ssid, &params->ssid_len);
+ }
+ p2p->ssid_set = 0;
+
p2p_random(params->passphrase, p2p->cfg->passphrase_len);
return 0;
}
@@ -1616,8 +1774,6 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer)
struct p2p_go_neg_results res;
int go = peer->go_state == LOCAL_GO;
struct p2p_channels intersection;
- int freqs;
- size_t i, j;
p2p_dbg(p2p, "GO Negotiation with " MACSTR " completed (%s will be GO)",
MAC2STR(peer->info.p2p_device_addr), go ? "local end" : "peer");
@@ -1658,21 +1814,9 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer)
p2p_channels_dump(p2p, "intersection after no-GO removal",
&intersection);
}
- freqs = 0;
- for (i = 0; i < intersection.reg_classes; i++) {
- struct p2p_reg_class *c = &intersection.reg_class[i];
- if (freqs + 1 == P2P_MAX_CHANNELS)
- break;
- for (j = 0; j < c->channels; j++) {
- int freq;
- if (freqs + 1 == P2P_MAX_CHANNELS)
- break;
- freq = p2p_channel_to_freq(c->reg_class, c->channel[j]);
- if (freq < 0)
- continue;
- res.freq_list[freqs++] = freq;
- }
- }
+
+ p2p_channels_to_freqs(&intersection, res.freq_list,
+ P2P_MAX_CHANNELS);
res.peer_config_timeout = go ? peer->client_timeout : peer->go_timeout;
@@ -1713,7 +1857,6 @@ static void p2p_rx_p2p_action(struct p2p_data *p2p, const u8 *sa,
rx_freq);
break;
case P2P_INVITATION_RESP:
- p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
p2p_process_invitation_resp(p2p, sa, data + 1, len - 1);
break;
case P2P_PROV_DISC_REQ:
@@ -1992,11 +2135,12 @@ int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps)
attr.num_req_dev_type))
return 1; /* Own Primary Device Type matches */
- for (i = 0; i < p2p->cfg->num_sec_dev_types; i++)
+ for (i = 0; i < p2p->cfg->num_sec_dev_types; i++) {
if (dev_type_list_match(p2p->cfg->sec_dev_type[i],
attr.req_dev_type,
attr.num_req_dev_type))
- return 1; /* Own Secondary Device Type matches */
+ return 1; /* Own Secondary Device Type matches */
+ }
/* No matching device type found */
return 0;
@@ -2018,6 +2162,9 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p)
if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P])
extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P]);
+ if (p2p->query_count)
+ extra += MAX_SVC_ADV_IE_LEN;
+
buf = wpabuf_alloc(1000 + extra);
if (buf == NULL)
return NULL;
@@ -2052,10 +2199,38 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p)
p2p_buf_add_device_info(buf, p2p, NULL);
p2p_buf_update_ie_hdr(buf, len);
+ if (p2p->query_count) {
+ p2p_buf_add_service_instance(buf, p2p, p2p->query_count,
+ p2p->query_hash,
+ p2p->p2ps_adv_list);
+ }
+
return buf;
}
+static int p2p_service_find_asp(struct p2p_data *p2p, const u8 *hash)
+{
+ struct p2ps_advertisement *adv_data;
+
+ p2p_dbg(p2p, "ASP find - ASP list: %p", p2p->p2ps_adv_list);
+
+ /* Wildcard always matches if we have actual services */
+ if (os_memcmp(hash, p2p->wild_card_hash, P2PS_HASH_LEN) == 0)
+ return p2p->p2ps_adv_list != NULL;
+
+ adv_data = p2p->p2ps_adv_list;
+ while (adv_data) {
+ p2p_dbg(p2p, "ASP hash: %x =? %x", hash[0], adv_data->hash[0]);
+ if (os_memcmp(hash, adv_data->hash, P2PS_HASH_LEN) == 0)
+ return 1;
+ adv_data = adv_data->next;
+ }
+
+ return 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)
@@ -2066,13 +2241,6 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
struct p2p_message msg;
struct wpabuf *ies;
- if (!p2p->in_listen || !p2p->drv_in_listen) {
- /* not in Listen state - ignore Probe Request */
- p2p_dbg(p2p, "Not in Listen state (in_listen=%d drv_in_listen=%d) - ignore Probe Request",
- p2p->in_listen, p2p->drv_in_listen);
- return P2P_PREQ_NOT_LISTEN;
- }
-
if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) ==
ParseFailed) {
/* Ignore invalid Probe Request frames */
@@ -2123,6 +2291,64 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
return P2P_PREQ_NOT_P2P;
}
+ p2p->p2ps_svc_found = 0;
+
+ if (msg.service_hash && msg.service_hash_count) {
+ const u8 *hash = msg.service_hash;
+ u8 *dest = p2p->query_hash;
+ u8 i;
+
+ p2p->query_count = 0;
+ for (i = 0; i < msg.service_hash_count; i++) {
+ if (p2p_service_find_asp(p2p, hash)) {
+ p2p->p2ps_svc_found = 1;
+
+ if (!os_memcmp(hash, p2p->wild_card_hash,
+ P2PS_HASH_LEN)) {
+ /* We found match(es) but wildcard
+ * will return all */
+ p2p->query_count = 1;
+ os_memcpy(p2p->query_hash, hash,
+ P2PS_HASH_LEN);
+ break;
+ }
+
+ /* Save each matching hash */
+ if (p2p->query_count < P2P_MAX_QUERY_HASH) {
+ os_memcpy(dest, hash, P2PS_HASH_LEN);
+ dest += P2PS_HASH_LEN;
+ p2p->query_count++;
+ } else {
+ /* We found match(es) but too many to
+ * return all */
+ p2p->query_count = 0;
+ break;
+ }
+ }
+ hash += P2PS_HASH_LEN;
+ }
+
+ p2p_dbg(p2p, "ASP adv found: %d", p2p->p2ps_svc_found);
+
+ /* Probed hash unknown */
+ if (!p2p->p2ps_svc_found) {
+ p2p_parse_free(&msg);
+ return P2P_PREQ_NOT_PROCESSED;
+ }
+ } else {
+ /* This is not a P2PS Probe Request */
+ p2p->query_count = 0;
+ p2p_dbg(p2p, "No P2PS Hash in Probe Request");
+
+ if (!p2p->in_listen || !p2p->drv_in_listen) {
+ /* not in Listen state - ignore Probe Request */
+ p2p_dbg(p2p, "Not in Listen state (in_listen=%d drv_in_listen=%d) - ignore Probe Request",
+ p2p->in_listen, p2p->drv_in_listen);
+ p2p_parse_free(&msg);
+ return P2P_PREQ_NOT_LISTEN;
+ }
+ }
+
if (msg.device_id &&
os_memcmp(msg.device_id, p2p->cfg->dev_addr, ETH_ALEN) != 0) {
/* Device ID did not match */
@@ -2220,6 +2446,7 @@ p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
p2p_add_dev_from_probe_req(p2p, addr, ie, ie_len);
res = p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len);
+ p2p->query_count = 0;
if ((p2p->state == P2P_CONNECT || p2p->state == P2P_CONNECT_LISTEN) &&
p2p->go_neg_peer &&
@@ -2377,6 +2604,132 @@ int p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end)
}
+struct p2ps_advertisement *
+p2p_service_p2ps_id(struct p2p_data *p2p, u32 adv_id)
+{
+ struct p2ps_advertisement *adv_data;
+
+ if (!p2p)
+ return NULL;
+
+ adv_data = p2p->p2ps_adv_list;
+ while (adv_data) {
+ if (adv_data->id == adv_id)
+ return adv_data;
+ adv_data = adv_data->next;
+ }
+
+ return NULL;
+}
+
+
+int p2p_service_del_asp(struct p2p_data *p2p, u32 adv_id)
+{
+ struct p2ps_advertisement *adv_data;
+ struct p2ps_advertisement **prior;
+
+ if (!p2p)
+ return -1;
+
+ adv_data = p2p->p2ps_adv_list;
+ prior = &p2p->p2ps_adv_list;
+ while (adv_data) {
+ if (adv_data->id == adv_id) {
+ p2p_dbg(p2p, "Delete ASP adv_id=0x%x", adv_id);
+ *prior = adv_data->next;
+ os_free(adv_data);
+ return 0;
+ }
+ prior = &adv_data->next;
+ adv_data = adv_data->next;
+ }
+
+ return -1;
+}
+
+
+int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id,
+ const char *adv_str, u8 svc_state, u16 config_methods,
+ const char *svc_info)
+{
+ struct p2ps_advertisement *adv_data, *tmp, **prev;
+ u8 buf[P2PS_HASH_LEN];
+ size_t adv_data_len, adv_len, info_len = 0;
+
+ if (!p2p || !adv_str || !adv_str[0])
+ return -1;
+
+ if (!(config_methods & p2p->cfg->config_methods)) {
+ p2p_dbg(p2p, "Config methods not supported svc: 0x%x dev: 0x%x",
+ config_methods, p2p->cfg->config_methods);
+ return -1;
+ }
+
+ if (!p2ps_gen_hash(p2p, adv_str, buf))
+ return -1;
+
+ if (svc_info)
+ info_len = os_strlen(svc_info);
+ adv_len = os_strlen(adv_str);
+ adv_data_len = sizeof(struct p2ps_advertisement) + adv_len + 1 +
+ info_len + 1;
+
+ adv_data = os_zalloc(adv_data_len);
+ if (!adv_data)
+ return -1;
+
+ os_memcpy(adv_data->hash, buf, P2PS_HASH_LEN);
+ adv_data->id = adv_id;
+ adv_data->state = svc_state;
+ adv_data->config_methods = config_methods & p2p->cfg->config_methods;
+ adv_data->auto_accept = (u8) auto_accept;
+ os_memcpy(adv_data->svc_name, adv_str, adv_len);
+
+ if (svc_info && info_len) {
+ adv_data->svc_info = &adv_data->svc_name[adv_len + 1];
+ os_memcpy(adv_data->svc_info, svc_info, info_len);
+ }
+
+ /*
+ * Group Advertisements by service string. They do not need to be
+ * sorted, but groups allow easier Probe Response instance grouping
+ */
+ tmp = p2p->p2ps_adv_list;
+ prev = &p2p->p2ps_adv_list;
+ while (tmp) {
+ if (tmp->id == adv_data->id) {
+ if (os_strcmp(tmp->svc_name, adv_data->svc_name) != 0) {
+ os_free(adv_data);
+ return -1;
+ }
+ adv_data->next = tmp->next;
+ *prev = adv_data;
+ os_free(tmp);
+ goto inserted;
+ } else {
+ if (os_strcmp(tmp->svc_name, adv_data->svc_name) == 0) {
+ adv_data->next = tmp->next;
+ tmp->next = adv_data;
+ goto inserted;
+ }
+ }
+ prev = &tmp->next;
+ tmp = tmp->next;
+ }
+
+ /* No svc_name match found */
+ adv_data->next = p2p->p2ps_adv_list;
+ p2p->p2ps_adv_list = adv_data;
+
+inserted:
+ p2p_dbg(p2p,
+ "Added ASP advertisement adv_id=0x%x config_methods=0x%x svc_state=0x%x adv_str='%s'",
+ adv_id, adv_data->config_methods, svc_state, adv_str);
+
+ return 0;
+}
+
+
int p2p_parse_dev_addr_in_p2p_ie(struct wpabuf *p2p_ie, u8 *dev_addr)
{
struct p2p_message msg;
@@ -2490,6 +2843,8 @@ struct p2p_data * p2p_init(const struct p2p_config *cfg)
p2p->cfg->num_pref_chan = 0;
}
+ p2ps_gen_hash(p2p, P2PS_WILD_HASH_STR, p2p->wild_card_hash);
+
p2p->min_disc_int = 1;
p2p->max_disc_int = 3;
p2p->max_disc_tu = -1;
@@ -2523,6 +2878,8 @@ struct p2p_data * p2p_init(const struct p2p_config *cfg)
void p2p_deinit(struct p2p_data *p2p)
{
+ struct p2ps_advertisement *adv, *prev;
+
#ifdef CONFIG_WIFI_DISPLAY
wpabuf_free(p2p->wfd_ie_beacon);
wpabuf_free(p2p->wfd_ie_probe_req);
@@ -2541,6 +2898,7 @@ void p2p_deinit(struct p2p_data *p2p)
eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL);
eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
eloop_cancel_timeout(p2p_go_neg_start, p2p, NULL);
+ eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL);
p2p_flush(p2p);
p2p_free_req_dev_types(p2p);
os_free(p2p->cfg->dev_name);
@@ -2550,10 +2908,19 @@ void p2p_deinit(struct p2p_data *p2p)
os_free(p2p->cfg->serial_number);
os_free(p2p->cfg->pref_chan);
os_free(p2p->groups);
+ os_free(p2p->p2ps_prov);
wpabuf_free(p2p->sd_resp);
os_free(p2p->after_scan_tx);
p2p_remove_wps_vendor_extensions(p2p);
os_free(p2p->no_go_freq.range);
+
+ adv = p2p->p2ps_adv_list;
+ while (adv) {
+ prev = adv;
+ adv = adv->next;
+ os_free(prev);
+ }
+
os_free(p2p);
}
@@ -2583,8 +2950,10 @@ int p2p_unauthorize(struct p2p_data *p2p, const u8 *addr)
p2p_dbg(p2p, "Unauthorizing " MACSTR, MAC2STR(addr));
- if (p2p->go_neg_peer == dev)
+ if (p2p->go_neg_peer == dev) {
+ eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL);
p2p->go_neg_peer = NULL;
+ }
dev->wps_method = WPS_NOT_READY;
dev->oob_pw_id = 0;
@@ -2742,28 +3111,64 @@ int p2p_set_country(struct p2p_data *p2p, const char *country)
}
+static int p2p_pre_find_operation(struct p2p_data *p2p, struct p2p_device *dev)
+{
+ if (dev->sd_pending_bcast_queries == 0) {
+ /* Initialize with total number of registered broadcast
+ * SD queries. */
+ dev->sd_pending_bcast_queries = p2p->num_p2p_sd_queries;
+ }
+
+ if (p2p_start_sd(p2p, dev) == 0)
+ return 1;
+
+ if (dev->req_config_methods &&
+ !(dev->flags & P2P_DEV_PD_FOR_JOIN)) {
+ p2p_dbg(p2p, "Send 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) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+
void p2p_continue_find(struct p2p_data *p2p)
{
struct p2p_device *dev;
+ int found;
+
p2p_set_state(p2p, P2P_SEARCH);
+
+ /* Continue from the device following the last iteration */
+ found = 0;
dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
- if (dev->sd_pending_bcast_queries == 0) {
- /* Initialize with total number of registered broadcast
- * SD queries. */
- dev->sd_pending_bcast_queries = p2p->num_p2p_sd_queries;
+ if (dev == p2p->last_p2p_find_oper) {
+ found = 1;
+ continue;
}
+ if (!found)
+ continue;
+ if (p2p_pre_find_operation(p2p, dev) > 0) {
+ p2p->last_p2p_find_oper = dev;
+ return;
+ }
+ }
- if (p2p_start_sd(p2p, dev) == 0)
+ /*
+ * Wrap around to the beginning of the list and continue until the last
+ * iteration device.
+ */
+ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+ if (p2p_pre_find_operation(p2p, dev) > 0) {
+ p2p->last_p2p_find_oper = dev;
return;
- if (dev->req_config_methods &&
- !(dev->flags & P2P_DEV_PD_FOR_JOIN)) {
- p2p_dbg(p2p, "Send 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) == 0)
- return;
}
+ if (dev == p2p->last_p2p_find_oper)
+ break;
}
p2p_listen_in_find(p2p, 1);
@@ -2777,18 +3182,22 @@ static void p2p_sd_cb(struct p2p_data *p2p, int success)
p2p->pending_action_state = P2P_NO_PENDING_ACTION;
if (!success) {
+ if (p2p->sd_peer)
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
p2p->sd_peer = NULL;
- p2p_continue_find(p2p);
+ if (p2p->state != P2P_IDLE)
+ p2p_continue_find(p2p);
return;
}
if (p2p->sd_peer == NULL) {
p2p_dbg(p2p, "No SD peer entry known");
- p2p_continue_find(p2p);
+ if (p2p->state != P2P_IDLE)
+ p2p_continue_find(p2p);
return;
}
- if (p2p->sd_query->for_all_peers) {
+ if (p2p->sd_query && p2p->sd_query->for_all_peers) {
/* Update the pending broadcast SD query count for this device
*/
p2p->sd_peer->sd_pending_bcast_queries--;
@@ -2815,9 +3224,6 @@ static void p2p_retry_pd(struct p2p_data *p2p)
{
struct p2p_device *dev;
- if (p2p->state != P2P_IDLE)
- return;
-
/*
* Retry the prov disc req attempt only for the peer that the user had
* requested.
@@ -2891,6 +3297,51 @@ static void p2p_prov_disc_cb(struct p2p_data *p2p, int success)
}
+static int p2p_check_after_scan_tx_continuation(struct p2p_data *p2p)
+{
+ if (p2p->after_scan_tx_in_progress) {
+ p2p->after_scan_tx_in_progress = 0;
+ if (p2p->start_after_scan != P2P_AFTER_SCAN_NOTHING &&
+ p2p_run_after_scan(p2p))
+ return 1;
+ if (p2p->state == P2P_SEARCH) {
+ p2p_dbg(p2p, "Continue find after after_scan_tx completion");
+ p2p_continue_find(p2p);
+ }
+ }
+
+ return 0;
+}
+
+
+static void p2p_prov_disc_resp_cb(struct p2p_data *p2p, int success)
+{
+ p2p_dbg(p2p, "Provision Discovery Response TX callback: success=%d",
+ success);
+
+ if (p2p->send_action_in_progress) {
+ p2p->send_action_in_progress = 0;
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ }
+
+ p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+
+ if (!success)
+ goto continue_search;
+
+ if (!p2p->cfg->prov_disc_resp_cb ||
+ p2p->cfg->prov_disc_resp_cb(p2p->cfg->cb_ctx) < 1)
+ goto continue_search;
+
+ p2p_dbg(p2p,
+ "Post-Provision Discovery operations started - do not try to continue other P2P operations");
+ return;
+
+continue_search:
+ p2p_check_after_scan_tx_continuation(p2p);
+}
+
+
int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq,
struct os_reltime *rx_time, int level, const u8 *ies,
size_t ies_len)
@@ -2933,6 +3384,7 @@ void p2p_scan_res_handled(struct p2p_data *p2p)
void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id)
{
+ u8 dev_capab;
u8 *len;
#ifdef CONFIG_WIFI_DISPLAY
@@ -2945,8 +3397,15 @@ void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id)
p2p->vendor_elem[VENDOR_ELEM_PROBE_REQ_P2P]);
len = p2p_buf_add_ie_hdr(ies);
- p2p_buf_add_capability(ies, p2p->dev_capab &
- ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
+
+ dev_capab = p2p->dev_capab & ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+
+ /* P2PS requires Probe Request frames to include SD bit */
+ if (p2p->p2ps_seek && p2p->p2ps_seek_count)
+ dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY;
+
+ p2p_buf_add_capability(ies, dev_capab, 0);
+
if (dev_id)
p2p_buf_add_device_id(ies, dev_id);
if (p2p->cfg->reg_class && p2p->cfg->channel)
@@ -2956,6 +3415,10 @@ void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id)
if (p2p->ext_listen_interval)
p2p_buf_add_ext_listen_timing(ies, p2p->ext_listen_period,
p2p->ext_listen_interval);
+
+ if (p2p->p2ps_seek && p2p->p2ps_seek_count)
+ p2p_buf_add_service_hash(ies, p2p);
+
/* TODO: p2p_buf_add_operating_channel() if GO */
p2p_buf_update_ie_hdr(ies, len);
}
@@ -3057,15 +3520,20 @@ static void p2p_go_neg_resp_failure_cb(struct p2p_data *p2p, int success,
{
p2p_dbg(p2p, "GO Negotiation Response (failure) TX callback: success=%d", success);
if (p2p->go_neg_peer && p2p->go_neg_peer->status != P2P_SC_SUCCESS) {
- p2p_go_neg_failed(p2p, p2p->go_neg_peer,
- p2p->go_neg_peer->status);
- } else if (success) {
+ p2p_go_neg_failed(p2p, p2p->go_neg_peer->status);
+ return;
+ }
+
+ if (success) {
struct p2p_device *dev;
dev = p2p_get_device(p2p, addr);
if (dev &&
dev->status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE)
dev->flags |= P2P_DEV_PEER_WAITING_RESPONSE;
}
+
+ if (p2p->state == P2P_SEARCH || p2p->state == P2P_SD_DURING_FIND)
+ p2p_continue_find(p2p);
}
@@ -3077,7 +3545,7 @@ static void p2p_go_neg_conf_cb(struct p2p_data *p2p,
p2p_dbg(p2p, "GO Negotiation Confirm TX callback: result=%d", result);
if (result == P2P_SEND_ACTION_FAILED) {
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
- p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1);
+ p2p_go_neg_failed(p2p, -1);
return;
}
@@ -3142,9 +3610,9 @@ void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
int success;
p2p_dbg(p2p, "Action frame TX callback (state=%d freq=%u dst=" MACSTR
- " src=" MACSTR " bssid=" MACSTR " result=%d",
+ " src=" MACSTR " bssid=" MACSTR " result=%d p2p_state=%s)",
p2p->pending_action_state, freq, MAC2STR(dst), MAC2STR(src),
- MAC2STR(bssid), result);
+ MAC2STR(bssid), result, p2p_state_txt(p2p->state));
success = result == P2P_SEND_ACTION_SUCCESS;
state = p2p->pending_action_state;
p2p->pending_action_state = P2P_NO_PENDING_ACTION;
@@ -3154,16 +3622,7 @@ void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
p2p->send_action_in_progress = 0;
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
}
- if (p2p->after_scan_tx_in_progress) {
- p2p->after_scan_tx_in_progress = 0;
- if (p2p->start_after_scan != P2P_AFTER_SCAN_NOTHING &&
- p2p_run_after_scan(p2p))
- break;
- if (p2p->state == P2P_SEARCH) {
- p2p_dbg(p2p, "Continue find after after_scan_tx completion");
- p2p_continue_find(p2p);
- }
- }
+ p2p_check_after_scan_tx_continuation(p2p);
break;
case P2P_PENDING_GO_NEG_REQUEST:
p2p_go_neg_req_cb(p2p, success);
@@ -3183,6 +3642,9 @@ void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
case P2P_PENDING_PD:
p2p_prov_disc_cb(p2p, success);
break;
+ case P2P_PENDING_PD_RESPONSE:
+ p2p_prov_disc_resp_cb(p2p, success);
+ break;
case P2P_PENDING_INVITATION_REQUEST:
p2p_invitation_req_cb(p2p, success);
break;
@@ -3248,7 +3710,7 @@ int p2p_listen_end(struct p2p_data *p2p, unsigned int freq)
if (p2p->state == P2P_CONNECT_LISTEN && p2p->go_neg_peer) {
if (p2p->go_neg_peer->connect_reqs >= 120) {
p2p_dbg(p2p, "Timeout on sending GO Negotiation Request without getting response");
- p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1);
+ p2p_go_neg_failed(p2p, -1);
return 0;
}
@@ -3299,7 +3761,7 @@ static void p2p_timeout_connect(struct p2p_data *p2p)
if (p2p->go_neg_peer &&
(p2p->go_neg_peer->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) {
p2p_dbg(p2p, "Wait for GO Negotiation Confirm timed out - assume GO Negotiation failed");
- p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1);
+ p2p_go_neg_failed(p2p, -1);
return;
}
if (p2p->go_neg_peer &&
@@ -3330,7 +3792,7 @@ static void p2p_timeout_connect_listen(struct p2p_data *p2p)
if (p2p->go_neg_peer->connect_reqs >= 120) {
p2p_dbg(p2p, "Timeout on sending GO Negotiation Request without getting response");
- p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1);
+ p2p_go_neg_failed(p2p, -1);
return;
}
@@ -3356,20 +3818,12 @@ static void p2p_timeout_wait_peer_connect(struct p2p_data *p2p)
static void p2p_timeout_wait_peer_idle(struct p2p_data *p2p)
{
struct p2p_device *dev = p2p->go_neg_peer;
- struct os_reltime now;
if (dev == NULL) {
p2p_dbg(p2p, "Unknown GO Neg peer - stop GO Neg wait");
return;
}
- os_get_reltime(&now);
- if (os_reltime_expired(&now, &dev->go_neg_wait_started, 120)) {
- p2p_dbg(p2p, "Timeout on waiting peer to become ready for GO Negotiation");
- p2p_go_neg_failed(p2p, dev, -1);
- return;
- }
-
p2p_dbg(p2p, "Go to Listen state while waiting for the peer to become ready for GO Negotiation");
p2p_set_state(p2p, P2P_WAIT_PEER_CONNECT);
p2p_listen_in_find(p2p, 0);
@@ -3397,6 +3851,9 @@ static void p2p_timeout_prov_disc_during_find(struct p2p_data *p2p)
static void p2p_timeout_prov_disc_req(struct p2p_data *p2p)
{
+ u32 adv_id = 0;
+ u8 *adv_mac = NULL;
+
p2p->pending_action_state = P2P_NO_PENDING_ACTION;
/*
@@ -3425,12 +3882,18 @@ static void p2p_timeout_prov_disc_req(struct p2p_data *p2p)
for_join = 1;
}
+ if (p2p->p2ps_prov) {
+ adv_id = p2p->p2ps_prov->adv_id;
+ adv_mac = p2p->p2ps_prov->adv_mac;
+ }
+
if (p2p->cfg->prov_disc_fail)
p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx,
p2p->pending_pd_devaddr,
for_join ?
P2P_PROV_DISC_TIMEOUT_JOIN :
- P2P_PROV_DISC_TIMEOUT);
+ P2P_PROV_DISC_TIMEOUT,
+ adv_id, adv_mac, NULL);
p2p_reset_pending_pd(p2p);
}
}
@@ -3480,6 +3943,10 @@ static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx)
p2p_dbg(p2p, "Timeout (state=%s)", p2p_state_txt(p2p->state));
p2p->in_listen = 0;
+ if (p2p->drv_in_listen) {
+ p2p_dbg(p2p, "Driver is still in listen state - stop it");
+ p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+ }
switch (p2p->state) {
case P2P_IDLE:
@@ -3575,6 +4042,8 @@ const char * p2p_wps_method_text(enum p2p_wps_method method)
return "PBC";
case WPS_NFC:
return "NFC";
+ case WPS_P2PS:
+ return "P2PS";
}
return "??";
@@ -3651,7 +4120,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
"country=%c%c\n"
"oper_freq=%d\n"
"req_config_methods=0x%x\n"
- "flags=%s%s%s%s%s%s%s%s%s%s%s%s%s\n"
+ "flags=%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n"
"status=%d\n"
"invitation_reqs=%u\n",
(int) (now.sec - dev->last_seen.sec),
@@ -3677,6 +4146,8 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
"[PD_PEER_DISPLAY]" : "",
dev->flags & P2P_DEV_PD_PEER_KEYPAD ?
"[PD_PEER_KEYPAD]" : "",
+ dev->flags & P2P_DEV_PD_PEER_P2PS ?
+ "[PD_PEER_P2PS]" : "",
dev->flags & P2P_DEV_USER_REJECTED ?
"[USER_REJECTED]" : "",
dev->flags & P2P_DEV_PEER_WAITING_RESPONSE ?
@@ -3695,7 +4166,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
"[PD_FOR_JOIN]" : "",
dev->status,
dev->invitation_reqs);
- if (res < 0 || res >= end - pos)
+ if (os_snprintf_error(end - pos, res))
return pos - buf;
pos += res;
@@ -3705,7 +4176,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
"ext_listen_interval=%u\n",
dev->ext_listen_period,
dev->ext_listen_interval);
- if (res < 0 || res >= end - pos)
+ if (os_snprintf_error(end - pos, res))
return pos - buf;
pos += res;
}
@@ -3715,7 +4186,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
"oper_ssid=%s\n",
wpa_ssid_txt(dev->oper_ssid,
dev->oper_ssid_len));
- if (res < 0 || res >= end - pos)
+ if (os_snprintf_error(end - pos, res))
return pos - buf;
pos += res;
}
@@ -3723,7 +4194,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
#ifdef CONFIG_WIFI_DISPLAY
if (dev->info.wfd_subelems) {
res = os_snprintf(pos, end - pos, "wfd_subelems=");
- if (res < 0 || res >= end - pos)
+ if (os_snprintf_error(end - pos, res))
return pos - buf;
pos += res;
@@ -3732,7 +4203,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
wpabuf_len(dev->info.wfd_subelems));
res = os_snprintf(pos, end - pos, "\n");
- if (res < 0 || res >= end - pos)
+ if (os_snprintf_error(end - pos, res))
return pos - buf;
pos += res;
}
@@ -4108,15 +4579,18 @@ int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel,
if (p2p_channel_to_freq(reg_class, channel) < 0)
return -1;
- p2p_dbg(p2p, "Set Listen channel: reg_class %u channel %u",
- reg_class, channel);
-
/*
* Listen channel was set in configuration or set by control interface;
* cannot override it.
*/
- if (p2p->cfg->channel_forced && forced == 0)
+ if (p2p->cfg->channel_forced && forced == 0) {
+ p2p_dbg(p2p,
+ "Listen channel was previously configured - do not override based on optimization");
return -1;
+ }
+
+ p2p_dbg(p2p, "Set Listen channel: reg_class %u channel %u",
+ reg_class, channel);
if (p2p->state == P2P_IDLE) {
p2p->cfg->reg_class = reg_class;
@@ -4850,3 +5324,13 @@ void p2p_set_vendor_elems(struct p2p_data *p2p, struct wpabuf **vendor_elem)
{
p2p->vendor_elem = vendor_elem;
}
+
+
+void p2p_go_neg_wait_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct p2p_data *p2p = eloop_ctx;
+
+ p2p_dbg(p2p,
+ "Timeout on waiting peer to become ready for GO Negotiation");
+ p2p_go_neg_failed(p2p, -1);
+}
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
index 076a2ac..2402db6 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -11,6 +11,16 @@
#include "wps/wps_defs.h"
+/* P2P ASP Setup Capability */
+#define P2PS_SETUP_NONE 0
+#define P2PS_SETUP_NEW BIT(0)
+#define P2PS_SETUP_CLIENT BIT(1)
+#define P2PS_SETUP_GROUP_OWNER BIT(2)
+
+#define P2PS_WILD_HASH_STR "org.wi-fi.wfds"
+#define P2PS_HASH_LEN 6
+#define P2P_MAX_QUERY_HASH 6
+
/**
* P2P_MAX_REG_CLASSES - Maximum number of regulatory classes
*/
@@ -52,7 +62,8 @@ struct p2p_channels {
};
enum p2p_wps_method {
- WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC, WPS_NFC
+ WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC, WPS_NFC,
+ WPS_P2PS
};
/**
@@ -142,11 +153,99 @@ struct p2p_go_neg_results {
unsigned int peer_config_timeout;
};
+struct p2ps_provision {
+ /**
+ * status - Remote returned provisioning status code
+ */
+ int status;
+
+ /**
+ * adv_id - P2PS Advertisement ID
+ */
+ u32 adv_id;
+
+ /**
+ * session_id - P2PS Session ID
+ */
+ u32 session_id;
+
+ /**
+ * method - WPS Method (to be) used to establish session
+ */
+ u16 method;
+
+ /**
+ * conncap - Connection Capabilities negotiated between P2P peers
+ */
+ u8 conncap;
+
+ /**
+ * role - Info about the roles to be used for this connection
+ */
+ u8 role;
+
+ /**
+ * session_mac - MAC address of the peer that started the session
+ */
+ u8 session_mac[ETH_ALEN];
+
+ /**
+ * adv_mac - MAC address of the peer advertised the service
+ */
+ u8 adv_mac[ETH_ALEN];
+
+ /**
+ * info - Vendor defined extra Provisioning information
+ */
+ char info[0];
+};
+
+struct p2ps_advertisement {
+ struct p2ps_advertisement *next;
+
+ /**
+ * svc_info - Pointer to (internal) Service defined information
+ */
+ char *svc_info;
+
+ /**
+ * id - P2PS Advertisement ID
+ */
+ u32 id;
+
+ /**
+ * config_methods - WPS Methods which are allowed for this service
+ */
+ u16 config_methods;
+
+ /**
+ * state - Current state of the service: 0 - Out Of Service, 1-255 Vendor defined
+ */
+ u8 state;
+
+ /**
+ * auto_accept - Automatically Accept provisioning request if possible.
+ */
+ u8 auto_accept;
+
+ /**
+ * hash - 6 octet Service Name has to match against incoming Probe Requests
+ */
+ u8 hash[P2PS_HASH_LEN];
+
+ /**
+ * svc_name - NULL Terminated UTF-8 Service Name, and svc_info storage
+ */
+ char svc_name[0];
+};
+
+
struct p2p_data;
enum p2p_scan_type {
P2P_SCAN_SOCIAL,
P2P_SCAN_FULL,
+ P2P_SCAN_SPECIFIC,
P2P_SCAN_SOCIAL_PLUS_ONE
};
@@ -238,6 +337,11 @@ struct p2p_peer_info {
* IE(s) from the frame that was used to discover the peer.
*/
struct wpabuf *vendor_elems;
+
+ /**
+ * p2ps_instance - P2PS Application Service Info
+ */
+ struct wpabuf *p2ps_instance;
};
enum p2p_prov_disc_status {
@@ -245,6 +349,7 @@ enum p2p_prov_disc_status {
P2P_PROV_DISC_TIMEOUT,
P2P_PROV_DISC_REJECTED,
P2P_PROV_DISC_TIMEOUT_JOIN,
+ P2P_PROV_DISC_INFO_UNAVAILABLE,
};
struct p2p_channel {
@@ -441,7 +546,8 @@ struct p2p_config {
* operation to be completed. Type type argument specifies which type
* of scan is to be done. @P2P_SCAN_SOCIAL indicates that only the
* social channels (1, 6, 11) should be scanned. @P2P_SCAN_FULL
- * indicates that all channels are to be scanned.
+ * indicates that all channels are to be scanned. @P2P_SCAN_SPECIFIC
+ * request a scan of a single channel specified by freq.
* @P2P_SCAN_SOCIAL_PLUS_ONE request scan of all the social channels
* plus one extra channel specified by freq.
*
@@ -705,6 +811,9 @@ struct p2p_config {
* @ctx: Callback context from cb_ctx
* @peer: Source address of the response
* @status: Cause of failure, will not be %P2P_PROV_DISC_SUCCESS
+ * @adv_id: If non-zero, then the adv_id of the PD Request
+ * @adv_mac: P2P Device Address of the advertizer
+ * @deferred_session_resp: Deferred session response sent by advertizer
*
* This callback is used to indicate either a failure or no response
* to an earlier provision discovery request.
@@ -713,7 +822,9 @@ struct p2p_config {
* is not used or failures do not need to be indicated.
*/
void (*prov_disc_fail)(void *ctx, const u8 *peer,
- enum p2p_prov_disc_status status);
+ enum p2p_prov_disc_status status,
+ u32 adv_id, const u8 *adv_mac,
+ const char *deferred_session_resp);
/**
* invitation_process - Optional callback for processing Invitations
@@ -835,6 +946,83 @@ struct p2p_config {
* or 0 if not.
*/
int (*is_p2p_in_progress)(void *ctx);
+
+ /**
+ * Determine if we have a persistent group we share with remote peer
+ * @ctx: Callback context from cb_ctx
+ * @addr: Peer device address to search for
+ * @ssid: Persistent group SSID or %NULL if any
+ * @ssid_len: Length of @ssid
+ * @go_dev_addr: Buffer for returning intended GO P2P Device Address
+ * @ret_ssid: Buffer for returning group SSID
+ * @ret_ssid_len: Buffer for returning length of @ssid
+ * Returns: 1 if a matching persistent group was found, 0 otherwise
+ */
+ int (*get_persistent_group)(void *ctx, const u8 *addr, const u8 *ssid,
+ size_t ssid_len, u8 *go_dev_addr,
+ u8 *ret_ssid, size_t *ret_ssid_len);
+
+ /**
+ * Get information about a possible local GO role
+ * @ctx: Callback context from cb_ctx
+ * @intended_addr: Buffer for returning intended GO interface address
+ * @ssid: Buffer for returning group SSID
+ * @ssid_len: Buffer for returning length of @ssid
+ * @group_iface: Buffer for returning whether a separate group interface
+ * would be used
+ * Returns: 1 if GO info found, 0 otherwise
+ *
+ * This is used to compose New Group settings (SSID, and intended
+ * address) during P2PS provisioning if results of provisioning *might*
+ * result in our being an autonomous GO.
+ */
+ int (*get_go_info)(void *ctx, u8 *intended_addr,
+ u8 *ssid, size_t *ssid_len, int *group_iface);
+
+ /**
+ * remove_stale_groups - Remove stale P2PS groups
+ *
+ * Because P2PS stages *potential* GOs, and remote devices can remove
+ * credentials unilaterally, we need to make sure we don't let stale
+ * unusable groups build up.
+ */
+ int (*remove_stale_groups)(void *ctx, const u8 *peer, const u8 *go,
+ const u8 *ssid, size_t ssid_len);
+
+ /**
+ * p2ps_prov_complete - P2PS provisioning complete
+ *
+ * When P2PS provisioning completes (successfully or not) we must
+ * transmit all of the results to the upper layers.
+ */
+ void (*p2ps_prov_complete)(void *ctx, u8 status, const u8 *dev,
+ const u8 *adv_mac, const u8 *ses_mac,
+ const u8 *grp_mac, u32 adv_id, u32 ses_id,
+ u8 conncap, int passwd_id,
+ const u8 *persist_ssid,
+ size_t persist_ssid_size, int response_done,
+ int prov_start, const char *session_info);
+
+ /**
+ * prov_disc_resp_cb - Callback for indicating completion of PD Response
+ * @ctx: Callback context from cb_ctx
+ * Returns: 1 if operation was started, 0 otherwise
+ *
+ * This callback can be used to perform any pending actions after
+ * provisioning. It is mainly used for P2PS pending group creation.
+ */
+ int (*prov_disc_resp_cb)(void *ctx);
+
+ /**
+ * p2ps_group_capability - Determine group capability
+ *
+ * This function can be used to determine group capability based on
+ * information from P2PS PD exchange and the current state of ongoing
+ * groups and driver capabilities.
+ *
+ * P2PS_SETUP_* bitmap is used as the parameters and return value.
+ */
+ u8 (*p2ps_group_capability)(void *ctx, u8 incoming, u8 role);
};
@@ -941,12 +1129,26 @@ enum p2p_discovery_type {
* requested device types.
* @dev_id: Device ID to search for or %NULL to find all devices
* @search_delay: Extra delay in milliseconds between search iterations
+ * @seek_count: Number of ASP Service Strings in the seek_string array
+ * @seek_string: ASP Service Strings to query for in Probe Requests
+ * @freq: Requested first scan frequency (in MHz) to modify type ==
+ * P2P_FIND_START_WITH_FULL behavior. 0 = Use normal full scan.
+ * If p2p_find is already in progress, this parameter is ignored and full
+ * scan will be executed.
* Returns: 0 on success, -1 on failure
*/
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,
- const u8 *dev_id, unsigned int search_delay);
+ const u8 *dev_id, unsigned int search_delay,
+ u8 seek_count, const char **seek_string, int freq);
+
+/**
+ * p2p_notify_scan_trigger_status - Indicate scan trigger status
+ * @p2p: P2P module context from p2p_init()
+ * @status: 0 on success, -1 on failure
+ */
+void p2p_notify_scan_trigger_status(struct p2p_data *p2p, int status);
/**
* p2p_stop_find - Stop P2P Find (Device Discovery)
@@ -1051,6 +1253,7 @@ int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr);
* p2p_prov_disc_req - Send Provision Discovery Request
* @p2p: P2P module context from p2p_init()
* @peer_addr: MAC address of the peer P2P client
+ * @p2ps_prov: Provisioning info for P2PS
* @config_methods: WPS Config Methods value (only one bit set)
* @join: Whether this is used by a client joining an active group
* @force_freq: Forced TX frequency for the frame (mainly for the join case)
@@ -1066,7 +1269,8 @@ int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr);
* indicated with the p2p_config::prov_disc_resp() callback.
*/
int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr,
- u16 config_methods, int join, int force_freq,
+ struct p2ps_provision *p2ps_prov, u16 config_methods,
+ int join, int force_freq,
int user_initiated_pd);
/**
@@ -1738,6 +1942,9 @@ void p2p_set_intra_bss_dist(struct p2p_data *p2p, int enabled);
int p2p_channels_includes_freq(const struct p2p_channels *channels,
unsigned int freq);
+int p2p_channels_to_freqs(const struct p2p_channels *channels,
+ int *freq_list, unsigned int max_len);
+
/**
* p2p_supported_freq - Check whether channel is supported for P2P
* @p2p: P2P module context from p2p_init()
@@ -1806,6 +2013,13 @@ const u8 * p2p_get_go_neg_peer(struct p2p_data *p2p);
unsigned int p2p_get_group_num_members(struct p2p_group *group);
/**
+ * p2p_client_limit_reached - Check if client limit is reached
+ * @group: P2P group context from p2p_group_init()
+ * Returns: 1 if no of clients limit reached
+ */
+int p2p_client_limit_reached(struct p2p_group *group);
+
+/**
* p2p_iterate_group_members - Iterate group members
* @group: P2P group context from p2p_group_init()
* @next: iteration pointer, must be a pointer to a void * that is set to %NULL
@@ -1912,7 +2126,8 @@ int p2p_set_no_go_freq(struct p2p_data *p2p,
/**
* p2p_in_progress - Check whether a P2P operation is progress
* @p2p: P2P module context from p2p_init()
- * Returns: 0 if P2P module is idle or 1 if an operation is in progress
+ * Returns: 0 if P2P module is idle, 1 if an operation is in progress but not
+ * in search state, or 2 if search state operation is in progress
*/
int p2p_in_progress(struct p2p_data *p2p);
@@ -2019,4 +2234,14 @@ void p2p_loop_on_known_peers(struct p2p_data *p2p,
void p2p_set_vendor_elems(struct p2p_data *p2p, struct wpabuf **vendor_elem);
+void p2p_set_intended_addr(struct p2p_data *p2p, const u8 *intended_addr);
+
+struct p2ps_advertisement *
+p2p_service_p2ps_id(struct p2p_data *p2p, u32 adv_id);
+int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id,
+ const char *adv_str, u8 svc_state,
+ u16 config_methods, const char *svc_info);
+int p2p_service_del_asp(struct p2p_data *p2p, u32 adv_id);
+struct p2ps_advertisement * p2p_get_p2ps_adv_list(struct p2p_data *p2p);
+
#endif /* P2P_H */
diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c
index e9b683d..92c9206 100644
--- a/src/p2p/p2p_build.c
+++ b/src/p2p/p2p_build.c
@@ -164,15 +164,18 @@ void p2p_buf_add_device_info(struct wpabuf *buf, struct p2p_data *p2p,
if (peer->wps_method == WPS_PBC)
methods |= WPS_CONFIG_PUSHBUTTON;
else if (peer->wps_method == WPS_PIN_DISPLAY ||
- peer->wps_method == WPS_PIN_KEYPAD)
+ peer->wps_method == WPS_PIN_KEYPAD) {
methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
+ methods |= WPS_CONFIG_P2PS;
+ }
} else if (p2p->cfg->config_methods) {
methods |= p2p->cfg->config_methods &
(WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_DISPLAY |
- WPS_CONFIG_KEYPAD);
+ WPS_CONFIG_KEYPAD | WPS_CONFIG_P2PS);
} else {
methods |= WPS_CONFIG_PUSHBUTTON;
methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
+ methods |= WPS_CONFIG_P2PS;
}
wpabuf_put_be16(buf, methods);
@@ -342,6 +345,256 @@ void p2p_buf_add_oob_go_neg_channel(struct wpabuf *buf, const char *country,
}
+void p2p_buf_add_service_hash(struct wpabuf *buf, struct p2p_data *p2p)
+{
+ if (!p2p)
+ return;
+
+ /* Service Hash */
+ wpabuf_put_u8(buf, P2P_ATTR_SERVICE_HASH);
+ wpabuf_put_le16(buf, p2p->p2ps_seek_count * P2PS_HASH_LEN);
+ wpabuf_put_data(buf, p2p->query_hash,
+ p2p->p2ps_seek_count * P2PS_HASH_LEN);
+ wpa_hexdump(MSG_DEBUG, "P2P: * Service Hash",
+ p2p->query_hash, p2p->p2ps_seek_count * P2PS_HASH_LEN);
+}
+
+
+void p2p_buf_add_session_info(struct wpabuf *buf, const char *info)
+{
+ size_t info_len = 0;
+
+ if (info && info[0])
+ info_len = os_strlen(info);
+
+ /* Session Information Data Info */
+ wpabuf_put_u8(buf, P2P_ATTR_SESSION_INFORMATION_DATA);
+ wpabuf_put_le16(buf, (u16) info_len);
+
+ if (info) {
+ wpabuf_put_data(buf, info, info_len);
+ wpa_printf(MSG_DEBUG, "P2P: * Session Info Data (%s)", info);
+ }
+}
+
+
+void p2p_buf_add_connection_capability(struct wpabuf *buf, u8 connection_cap)
+{
+ /* Connection Capability Info */
+ wpabuf_put_u8(buf, P2P_ATTR_CONNECTION_CAPABILITY);
+ wpabuf_put_le16(buf, 1);
+ wpabuf_put_u8(buf, connection_cap);
+ wpa_printf(MSG_DEBUG, "P2P: * Connection Capability: 0x%x",
+ connection_cap);
+}
+
+
+void p2p_buf_add_advertisement_id(struct wpabuf *buf, u32 id, const u8 *mac)
+{
+ if (!buf || !mac)
+ return;
+
+ /* Advertisement ID Info */
+ wpabuf_put_u8(buf, P2P_ATTR_ADVERTISEMENT_ID);
+ wpabuf_put_le16(buf, (u16) (sizeof(u32) + ETH_ALEN));
+ wpabuf_put_le32(buf, id);
+ wpabuf_put_data(buf, mac, ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "P2P: * Advertisement ID (%x) " MACSTR,
+ id, MAC2STR(mac));
+}
+
+
+void p2p_buf_add_service_instance(struct wpabuf *buf, struct p2p_data *p2p,
+ u8 hash_count, const u8 *hash,
+ struct p2ps_advertisement *adv_list)
+{
+ struct p2ps_advertisement *adv;
+ struct wpabuf *tmp_buf;
+ u8 *tag_len = NULL, *ie_len = NULL;
+ size_t svc_len = 0, remaining = 0, total_len = 0;
+
+ if (!adv_list || !hash)
+ return;
+
+ /* Allocate temp buffer, allowing for overflow of 1 instance */
+ tmp_buf = wpabuf_alloc(MAX_SVC_ADV_IE_LEN + 256 + P2PS_HASH_LEN);
+ if (!tmp_buf)
+ return;
+
+ for (adv = adv_list; adv && total_len <= MAX_SVC_ADV_LEN;
+ adv = adv->next) {
+ u8 count = hash_count;
+ const u8 *test = hash;
+
+ while (count--) {
+ /* Check for wildcard */
+ if (os_memcmp(test, p2p->wild_card_hash,
+ P2PS_HASH_LEN) == 0) {
+ total_len = MAX_SVC_ADV_LEN + 1;
+ goto wild_hash;
+ }
+
+ if (os_memcmp(test, adv->hash, P2PS_HASH_LEN) == 0)
+ goto hash_match;
+
+ test += P2PS_HASH_LEN;
+ }
+
+ /* No matches found - Skip this Adv Instance */
+ continue;
+
+hash_match:
+ if (!tag_len) {
+ tag_len = p2p_buf_add_ie_hdr(tmp_buf);
+ remaining = 255 - 4;
+ if (!ie_len) {
+ wpabuf_put_u8(tmp_buf,
+ P2P_ATTR_ADVERTISED_SERVICE);
+ ie_len = wpabuf_put(tmp_buf, sizeof(u16));
+ remaining -= (sizeof(u8) + sizeof(u16));
+ }
+ }
+
+ svc_len = os_strlen(adv->svc_name);
+
+ if (7 + svc_len + total_len > MAX_SVC_ADV_LEN) {
+ /* Can't fit... return wildcard */
+ total_len = MAX_SVC_ADV_LEN + 1;
+ break;
+ }
+
+ if (remaining <= (sizeof(adv->id) +
+ sizeof(adv->config_methods))) {
+ size_t front = remaining;
+ size_t back = (sizeof(adv->id) +
+ sizeof(adv->config_methods)) - front;
+ u8 holder[sizeof(adv->id) +
+ sizeof(adv->config_methods)];
+
+ /* This works even if front or back == 0 */
+ WPA_PUT_LE32(holder, adv->id);
+ WPA_PUT_BE16(&holder[sizeof(adv->id)],
+ adv->config_methods);
+ wpabuf_put_data(tmp_buf, holder, front);
+ p2p_buf_update_ie_hdr(tmp_buf, tag_len);
+ tag_len = p2p_buf_add_ie_hdr(tmp_buf);
+ wpabuf_put_data(tmp_buf, &holder[front], back);
+ remaining = 255 - (sizeof(adv->id) +
+ sizeof(adv->config_methods)) - back;
+ } else {
+ wpabuf_put_le32(tmp_buf, adv->id);
+ wpabuf_put_be16(tmp_buf, adv->config_methods);
+ remaining -= (sizeof(adv->id) +
+ sizeof(adv->config_methods));
+ }
+
+ /* We are guaranteed at least one byte for svc_len */
+ wpabuf_put_u8(tmp_buf, svc_len);
+ remaining -= sizeof(u8);
+
+ if (remaining < svc_len) {
+ size_t front = remaining;
+ size_t back = svc_len - front;
+
+ wpabuf_put_data(tmp_buf, adv->svc_name, front);
+ p2p_buf_update_ie_hdr(tmp_buf, tag_len);
+ tag_len = p2p_buf_add_ie_hdr(tmp_buf);
+
+ /* In rare cases, we must split across 3 attributes */
+ if (back > 255 - 4) {
+ wpabuf_put_data(tmp_buf,
+ &adv->svc_name[front], 255 - 4);
+ back -= 255 - 4;
+ front += 255 - 4;
+ p2p_buf_update_ie_hdr(tmp_buf, tag_len);
+ tag_len = p2p_buf_add_ie_hdr(tmp_buf);
+ }
+
+ wpabuf_put_data(tmp_buf, &adv->svc_name[front], back);
+ remaining = 255 - 4 - back;
+ } else {
+ wpabuf_put_data(tmp_buf, adv->svc_name, svc_len);
+ remaining -= svc_len;
+ }
+
+ /* adv_id config_methods svc_string */
+ total_len += sizeof(u32) + sizeof(u16) + sizeof(u8) + svc_len;
+ }
+
+ if (tag_len)
+ p2p_buf_update_ie_hdr(tmp_buf, tag_len);
+
+ if (ie_len)
+ WPA_PUT_LE16(ie_len, (u16) total_len);
+
+wild_hash:
+ /* If all fit, return matching instances, otherwise the wildcard */
+ if (total_len <= MAX_SVC_ADV_LEN) {
+ wpabuf_put_buf(buf, tmp_buf);
+ } else {
+ char *wild_card = P2PS_WILD_HASH_STR;
+ u8 wild_len;
+
+ /* Insert wildcard instance */
+ tag_len = p2p_buf_add_ie_hdr(buf);
+ wpabuf_put_u8(buf, P2P_ATTR_ADVERTISED_SERVICE);
+ ie_len = wpabuf_put(buf, sizeof(u16));
+
+ wild_len = (u8) os_strlen(wild_card);
+ wpabuf_put_le32(buf, 0);
+ wpabuf_put_be16(buf, 0);
+ wpabuf_put_u8(buf, wild_len);
+ wpabuf_put_data(buf, wild_card, wild_len);
+
+ WPA_PUT_LE16(ie_len, 4 + 2 + 1 + wild_len);
+ p2p_buf_update_ie_hdr(buf, tag_len);
+ }
+
+ wpabuf_free(tmp_buf);
+}
+
+
+void p2p_buf_add_session_id(struct wpabuf *buf, u32 id, const u8 *mac)
+{
+ if (!buf || !mac)
+ return;
+
+ /* Session ID Info */
+ wpabuf_put_u8(buf, P2P_ATTR_SESSION_ID);
+ wpabuf_put_le16(buf, (u16) (sizeof(u32) + ETH_ALEN));
+ wpabuf_put_le32(buf, id);
+ wpabuf_put_data(buf, mac, ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "P2P: * Session ID Info (%x) " MACSTR,
+ id, MAC2STR(mac));
+}
+
+
+void p2p_buf_add_feature_capability(struct wpabuf *buf, u16 len, const u8 *mask)
+{
+ if (!buf || !len || !mask)
+ return;
+
+ /* Feature Capability */
+ wpabuf_put_u8(buf, P2P_ATTR_FEATURE_CAPABILITY);
+ wpabuf_put_le16(buf, len);
+ wpabuf_put_data(buf, mask, len);
+ wpa_printf(MSG_DEBUG, "P2P: * Feature Capability (%d)", len);
+}
+
+
+void p2p_buf_add_persistent_group_info(struct wpabuf *buf, const u8 *dev_addr,
+ const u8 *ssid, size_t ssid_len)
+{
+ /* P2P Group ID */
+ wpabuf_put_u8(buf, P2P_ATTR_PERSISTENT_GROUP);
+ wpabuf_put_le16(buf, ETH_ALEN + ssid_len);
+ wpabuf_put_data(buf, dev_addr, ETH_ALEN);
+ wpabuf_put_data(buf, ssid, ssid_len);
+ wpa_printf(MSG_DEBUG, "P2P: * P2P Group ID " MACSTR,
+ MAC2STR(dev_addr));
+}
+
+
static int p2p_add_wps_string(struct wpabuf *buf, enum wps_attribute attr,
const char *val)
{
diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c
index 21fae3f..98abf9d 100644
--- a/src/p2p/p2p_go_neg.c
+++ b/src/p2p/p2p_go_neg.c
@@ -9,6 +9,7 @@
#include "includes.h"
#include "common.h"
+#include "utils/eloop.h"
#include "common/ieee802_11_defs.h"
#include "common/wpa_ctrl.h"
#include "wps/wps_defs.h"
@@ -106,6 +107,8 @@ u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method)
return DEV_PW_PUSHBUTTON;
case WPS_NFC:
return DEV_PW_NFC_CONNECTION_HANDOVER;
+ case WPS_P2PS:
+ return DEV_PW_P2PS_DEFAULT;
default:
return DEV_PW_DEFAULT;
}
@@ -123,6 +126,8 @@ static const char * p2p_wps_method_str(enum p2p_wps_method wps_method)
return "PBC";
case WPS_NFC:
return "NFC";
+ case WPS_P2PS:
+ return "P2PS";
default:
return "??";
}
@@ -217,10 +222,12 @@ int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev)
config_method = WPS_CONFIG_DISPLAY;
else if (dev->wps_method == WPS_PBC)
config_method = WPS_CONFIG_PUSHBUTTON;
+ else if (dev->wps_method == WPS_P2PS)
+ config_method = WPS_CONFIG_P2PS;
else
return -1;
return p2p_prov_disc_req(p2p, dev->info.p2p_device_addr,
- config_method, 0, 0, 1);
+ NULL, config_method, 0, 0, 1);
}
freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
@@ -240,6 +247,7 @@ int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev)
p2p_set_state(p2p, P2P_CONNECT);
p2p->pending_action_state = P2P_PENDING_GO_NEG_REQUEST;
p2p->go_neg_peer = dev;
+ eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL);
dev->flags |= P2P_DEV_WAIT_GO_NEG_RESPONSE;
dev->connect_reqs++;
if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
@@ -486,8 +494,8 @@ void p2p_reselect_channel(struct p2p_data *p2p,
}
-static int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev,
- u8 *status)
+int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev,
+ u8 *status)
{
struct p2p_channels tmp, intersection;
@@ -621,7 +629,7 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
* Request frame.
*/
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
- p2p_go_neg_failed(p2p, dev, *msg.status);
+ p2p_go_neg_failed(p2p, *msg.status);
p2p_parse_free(&msg);
return;
}
@@ -645,6 +653,9 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
p2p_add_dev_info(p2p, sa, dev, &msg);
}
+ if (p2p->go_neg_peer && p2p->go_neg_peer == dev)
+ eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL);
+
if (dev && dev->flags & P2P_DEV_USER_REJECTED) {
p2p_dbg(p2p, "User has rejected this peer");
status = P2P_SC_FAIL_REJECTED_BY_USER;
@@ -738,6 +749,16 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
goto fail;
}
break;
+ case DEV_PW_P2PS_DEFAULT:
+ p2p_dbg(p2p, "Peer using P2PS pin");
+ if (dev->wps_method != WPS_P2PS) {
+ p2p_dbg(p2p,
+ "We have wps_method=%s -> incompatible",
+ p2p_wps_method_str(dev->wps_method));
+ status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+ goto fail;
+ }
+ break;
default:
if (msg.dev_password_id &&
msg.dev_password_id == dev->oob_pw_id) {
@@ -789,6 +810,7 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
dev->dialog_token = msg.dialog_token;
os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN);
p2p->go_neg_peer = dev;
+ eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL);
status = P2P_SC_SUCCESS;
}
@@ -957,7 +979,10 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa,
if (*msg.status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
p2p_dbg(p2p, "Wait for the peer to become ready for GO Negotiation");
dev->flags |= P2P_DEV_NOT_YET_READY;
- os_get_reltime(&dev->go_neg_wait_started);
+ eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p,
+ NULL);
+ eloop_register_timeout(120, 0, p2p_go_neg_wait_timeout,
+ p2p, NULL);
if (p2p->state == P2P_CONNECT_LISTEN)
p2p_set_state(p2p, P2P_WAIT_PEER_CONNECT);
else
@@ -965,7 +990,7 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa,
p2p_set_timeout(p2p, 0, 0);
} else {
p2p_dbg(p2p, "Stop GO Negotiation attempt");
- p2p_go_neg_failed(p2p, dev, *msg.status);
+ p2p_go_neg_failed(p2p, *msg.status);
}
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
p2p_parse_free(&msg);
@@ -1093,6 +1118,15 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa,
goto fail;
}
break;
+ case DEV_PW_P2PS_DEFAULT:
+ p2p_dbg(p2p, "P2P: Peer using P2PS default pin");
+ if (dev->wps_method != WPS_P2PS) {
+ p2p_dbg(p2p, "We have wps_method=%s -> incompatible",
+ p2p_wps_method_str(dev->wps_method));
+ status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+ goto fail;
+ }
+ break;
default:
if (msg.dev_password_id &&
msg.dev_password_id == dev->oob_pw_id) {
@@ -1147,13 +1181,13 @@ fail:
wpabuf_head(dev->go_neg_conf),
wpabuf_len(dev->go_neg_conf), 200) < 0) {
p2p_dbg(p2p, "Failed to send Action frame");
- p2p_go_neg_failed(p2p, dev, -1);
+ p2p_go_neg_failed(p2p, -1);
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
} else
dev->go_neg_conf_sent++;
if (status != P2P_SC_SUCCESS) {
p2p_dbg(p2p, "GO Negotiation failed");
- p2p_go_neg_failed(p2p, dev, status);
+ p2p_go_neg_failed(p2p, status);
}
}
@@ -1204,7 +1238,7 @@ void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa,
}
if (*msg.status) {
p2p_dbg(p2p, "GO Negotiation rejected: status %d", *msg.status);
- p2p_go_neg_failed(p2p, dev, *msg.status);
+ p2p_go_neg_failed(p2p, *msg.status);
p2p_parse_free(&msg);
return;
}
@@ -1216,7 +1250,7 @@ void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa,
} else if (dev->go_state == REMOTE_GO) {
p2p_dbg(p2p, "Mandatory P2P Group ID attribute missing from GO Negotiation Confirmation");
p2p->ssid_len = 0;
- p2p_go_neg_failed(p2p, dev, P2P_SC_FAIL_INVALID_PARAMS);
+ p2p_go_neg_failed(p2p, P2P_SC_FAIL_INVALID_PARAMS);
p2p_parse_free(&msg);
return;
}
diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c
index da8588a..41ca99f 100644
--- a/src/p2p/p2p_group.c
+++ b/src/p2p/p2p_group.c
@@ -981,10 +981,22 @@ u8 p2p_group_presence_req(struct p2p_group *group,
unsigned int p2p_get_group_num_members(struct p2p_group *group)
{
+ if (!group)
+ return 0;
+
return group->num_members;
}
+int p2p_client_limit_reached(struct p2p_group *group)
+{
+ if (!group || !group->cfg)
+ return 1;
+
+ return group->num_members >= group->cfg->max_clients;
+}
+
+
const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next)
{
struct p2p_group_member *iter = *next;
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
index 3b60582..6af19ce 100644
--- a/src/p2p/p2p_i.h
+++ b/src/p2p/p2p_i.h
@@ -16,6 +16,13 @@
enum p2p_role_indication;
+/*
+ * To force Service Instances to fit within a single P2P Tag, MAX_SVC_ADV_LEN
+ * must equal 248 or less. Must have a minimum size of 19.
+ */
+#define MAX_SVC_ADV_LEN 600
+#define MAX_SVC_ADV_IE_LEN (9 + MAX_SVC_ADV_LEN + (5 * (MAX_SVC_ADV_LEN / 240)))
+
enum p2p_go_state {
UNKNOWN_GO,
LOCAL_GO,
@@ -98,13 +105,15 @@ struct p2p_device {
#define P2P_DEV_PD_BEFORE_GO_NEG BIT(17)
#define P2P_DEV_NO_PREF_CHAN BIT(18)
#define P2P_DEV_WAIT_INV_REQ_ACK BIT(19)
+#define P2P_DEV_P2PS_REPORTED BIT(20)
+#define P2P_DEV_PD_PEER_P2PS BIT(21)
unsigned int flags;
int status; /* enum p2p_status_code */
- struct os_reltime go_neg_wait_started;
unsigned int wait_count;
unsigned int connect_reqs;
unsigned int invitation_reqs;
+ unsigned int sd_reqs;
u16 ext_listen_period;
u16 ext_listen_interval;
@@ -260,10 +269,18 @@ struct p2p_data {
*/
struct p2p_device *invite_peer;
+ /**
+ * last_p2p_find_oper - Pointer to last pre-find operation peer
+ */
+ struct p2p_device *last_p2p_find_oper;
+
const u8 *invite_go_dev_addr;
u8 invite_go_dev_addr_buf[ETH_ALEN];
int invite_dev_pw_id;
+ unsigned int retry_invite_req:1;
+ unsigned int retry_invite_req_sent:1;
+
/**
* sd_peer - Pointer to Service Discovery peer
*/
@@ -346,6 +363,7 @@ struct p2p_data {
P2P_PENDING_GO_NEG_CONFIRM,
P2P_PENDING_SD,
P2P_PENDING_PD,
+ P2P_PENDING_PD_RESPONSE,
P2P_PENDING_INVITATION_REQUEST,
P2P_PENDING_INVITATION_RESPONSE,
P2P_PENDING_DEV_DISC_REQUEST,
@@ -484,6 +502,16 @@ struct p2p_data {
u8 pending_channel;
u8 pending_channel_forced;
+ /* ASP Support */
+ struct p2ps_advertisement *p2ps_adv_list;
+ struct p2ps_provision *p2ps_prov;
+ u8 wild_card_hash[P2PS_HASH_LEN];
+ u8 query_hash[P2P_MAX_QUERY_HASH * P2PS_HASH_LEN];
+ u8 query_count;
+ u8 p2ps_seek;
+ u8 p2ps_seek_count;
+ u8 p2ps_svc_found;
+
#ifdef CONFIG_WIFI_DISPLAY
struct wpabuf *wfd_ie_beacon;
struct wpabuf *wfd_ie_probe_req;
@@ -578,6 +606,31 @@ struct p2p_message {
/* SSID IE */
const u8 *ssid;
+
+ /* P2PS */
+ u8 service_hash_count;
+ const u8 *service_hash;
+
+ const u8 *session_info;
+ size_t session_info_len;
+
+ const u8 *conn_cap;
+
+ const u8 *adv_id;
+ const u8 *adv_mac;
+
+ const u8 *adv_service_instance;
+ size_t adv_service_instance_len;
+
+ const u8 *session_id;
+ const u8 *session_mac;
+
+ const u8 *feature_cap;
+ size_t feature_cap_len;
+
+ const u8 *persistent_dev;
+ const u8 *persistent_ssid;
+ size_t persistent_ssid_len;
};
@@ -606,6 +659,8 @@ int p2p_freq_to_channel(unsigned int freq, u8 *op_class, u8 *channel);
void p2p_channels_intersect(const struct p2p_channels *a,
const struct p2p_channels *b,
struct p2p_channels *res);
+void p2p_channels_union_inplace(struct p2p_channels *res,
+ const struct p2p_channels *b);
void p2p_channels_union(const struct p2p_channels *a,
const struct p2p_channels *b,
struct p2p_channels *res);
@@ -688,6 +743,18 @@ void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p);
void p2p_buf_add_oob_go_neg_channel(struct wpabuf *buf, const char *country,
u8 oper_class, u8 channel,
enum p2p_role_indication role);
+void p2p_buf_add_service_hash(struct wpabuf *buf, struct p2p_data *p2p);
+void p2p_buf_add_session_info(struct wpabuf *buf, const char *info);
+void p2p_buf_add_connection_capability(struct wpabuf *buf, u8 connection_cap);
+void p2p_buf_add_advertisement_id(struct wpabuf *buf, u32 id, const u8 *mac);
+void p2p_buf_add_service_instance(struct wpabuf *buf, struct p2p_data *p2p,
+ u8 count, const u8 *hash,
+ struct p2ps_advertisement *adv_list);
+void p2p_buf_add_session_id(struct wpabuf *buf, u32 id, const u8 *mac);
+void p2p_buf_add_feature_capability(struct wpabuf *buf, u16 len,
+ const u8 *mask);
+void p2p_buf_add_persistent_group_info(struct wpabuf *buf, const u8 *dev_addr,
+ const u8 *ssid, size_t ssid_len);
int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
int all_attr);
@@ -768,8 +835,7 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
struct p2p_device * p2p_get_device(struct p2p_data *p2p, const u8 *addr);
struct p2p_device * p2p_get_device_interface(struct p2p_data *p2p,
const u8 *addr);
-void p2p_go_neg_failed(struct p2p_data *p2p, struct p2p_device *peer,
- int status);
+void p2p_go_neg_failed(struct p2p_data *p2p, int status);
void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer);
int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps);
int dev_type_list_match(const u8 *dev_type, const u8 *req_dev_type[],
@@ -783,6 +849,9 @@ void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq);
int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev,
unsigned int force_freq, unsigned int pref_freq,
int go);
+void p2p_go_neg_wait_timeout(void *eloop_ctx, void *timeout_ctx);
+int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev,
+ u8 *status);
void p2p_dbg(struct p2p_data *p2p, const char *fmt, ...)
PRINTF_FORMAT(2, 3);
void p2p_info(struct p2p_data *p2p, const char *fmt, ...)
diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c
index ef01a66..558c6dd 100644
--- a/src/p2p/p2p_invitation.c
+++ b/src/p2p/p2p_invitation.c
@@ -174,7 +174,7 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa,
u8 group_bssid[ETH_ALEN], *bssid;
int op_freq = 0;
u8 reg_class = 0, channel = 0;
- struct p2p_channels intersection, *channels = NULL;
+ struct p2p_channels all_channels, intersection, *channels = NULL;
int persistent;
os_memset(group_bssid, 0, sizeof(group_bssid));
@@ -226,7 +226,10 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa,
persistent = 1;
}
- if (p2p_peer_channels_check(p2p, &p2p->cfg->channels, dev,
+ p2p_channels_union(&p2p->cfg->channels, &p2p->cfg->cli_channels,
+ &all_channels);
+
+ if (p2p_peer_channels_check(p2p, &all_channels, dev,
msg.channel_list, msg.channel_list_len) <
0) {
p2p_dbg(p2p, "No common channels found");
@@ -235,8 +238,9 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa,
}
p2p_channels_dump(p2p, "own channels", &p2p->cfg->channels);
+ p2p_channels_dump(p2p, "own client channels", &all_channels);
p2p_channels_dump(p2p, "peer channels", &dev->channels);
- p2p_channels_intersect(&p2p->cfg->channels, &dev->channels,
+ p2p_channels_intersect(&all_channels, &dev->channels,
&intersection);
p2p_channels_dump(p2p, "intersection", &intersection);
@@ -248,6 +252,17 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa,
msg.dev_password_id_present ? msg.dev_password_id : -1);
}
+ if (go) {
+ p2p_channels_intersect(&p2p->cfg->channels, &dev->channels,
+ &intersection);
+ p2p_channels_dump(p2p, "intersection(GO)", &intersection);
+ if (intersection.reg_classes == 0) {
+ p2p_dbg(p2p, "No common channels found (GO)");
+ status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+ goto fail;
+ }
+ }
+
if (op_freq) {
p2p_dbg(p2p, "Invitation processing forced frequency %d MHz",
op_freq);
@@ -412,25 +427,68 @@ void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa,
if (dev == NULL) {
p2p_dbg(p2p, "Ignore Invitation Response from unknown peer "
MACSTR, MAC2STR(sa));
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
return;
}
if (dev != p2p->invite_peer) {
p2p_dbg(p2p, "Ignore unexpected Invitation Response from peer "
MACSTR, MAC2STR(sa));
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
return;
}
- if (p2p_parse(data, len, &msg))
+ if (p2p_parse(data, len, &msg)) {
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
return;
+ }
if (!msg.status) {
p2p_dbg(p2p, "Mandatory Status attribute missing in Invitation Response from "
MACSTR, MAC2STR(sa));
p2p_parse_free(&msg);
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ return;
+ }
+
+ /*
+ * We should not really receive a replayed response twice since
+ * duplicate frames are supposed to be dropped. However, not all drivers
+ * do that for pre-association frames. We did not use to verify dialog
+ * token matches for invitation response frames, but that check can be
+ * safely used to drop a replayed response to the previous Invitation
+ * Request in case the suggested operating channel was changed. This
+ * allows a duplicated reject frame to be dropped with the assumption
+ * that the real response follows after it.
+ */
+ if (*msg.status == P2P_SC_FAIL_NO_COMMON_CHANNELS &&
+ p2p->retry_invite_req_sent &&
+ msg.dialog_token != dev->dialog_token) {
+ p2p_dbg(p2p, "Unexpected Dialog Token %u (expected %u)",
+ msg.dialog_token, dev->dialog_token);
+ p2p_parse_free(&msg);
return;
}
+ if (*msg.status == P2P_SC_FAIL_NO_COMMON_CHANNELS &&
+ p2p->retry_invite_req &&
+ p2p_channel_random_social(&p2p->cfg->channels, &p2p->op_reg_class,
+ &p2p->op_channel) == 0) {
+ p2p->retry_invite_req = 0;
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+ p2p_set_state(p2p, P2P_INVITE);
+ p2p_dbg(p2p, "Resend Invitation Request setting op_class %u channel %u as operating channel",
+ p2p->op_reg_class, p2p->op_channel);
+ p2p->retry_invite_req_sent = 1;
+ p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr,
+ p2p->invite_dev_pw_id);
+ p2p_parse_free(&msg);
+ return;
+ }
+ p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+ p2p->retry_invite_req = 0;
+
if (!msg.channel_list && *msg.status == P2P_SC_SUCCESS) {
p2p_dbg(p2p, "Mandatory Channel List attribute missing in Invitation Response from "
MACSTR, MAC2STR(sa));
@@ -592,6 +650,9 @@ int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role,
dev_pw_id);
}
p2p->invite_dev_pw_id = dev_pw_id;
+ p2p->retry_invite_req = role == P2P_INVITE_ROLE_GO &&
+ persistent_group && !force_freq;
+ p2p->retry_invite_req_sent = 0;
dev = p2p_get_device(p2p, peer);
if (dev == NULL || (dev->listen_freq <= 0 && dev->oper_freq <= 0 &&
diff --git a/src/p2p/p2p_parse.c b/src/p2p/p2p_parse.c
index d6144a0..fd6a461 100644
--- a/src/p2p/p2p_parse.c
+++ b/src/p2p/p2p_parse.c
@@ -281,6 +281,112 @@ static int p2p_parse_attribute(u8 id, const u8 *data, u16 len,
data[0], data[1], data[2], data[3], data[4],
data[5]);
break;
+ case P2P_ATTR_SERVICE_HASH:
+ if (len < P2PS_HASH_LEN) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: Too short Service Hash (length %u)",
+ len);
+ return -1;
+ }
+ msg->service_hash_count = len / P2PS_HASH_LEN;
+ msg->service_hash = data;
+ wpa_hexdump(MSG_DEBUG, "P2P: * Service Hash(s)", data, len);
+ break;
+ case P2P_ATTR_SESSION_INFORMATION_DATA:
+ msg->session_info = data;
+ msg->session_info_len = len;
+ wpa_printf(MSG_DEBUG, "P2P: * Service Instance: %u bytes - %p",
+ len, data);
+ break;
+ case P2P_ATTR_CONNECTION_CAPABILITY:
+ if (len < 1) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: Too short Connection Capability (length %u)",
+ len);
+ return -1;
+ }
+ msg->conn_cap = data;
+ wpa_printf(MSG_DEBUG, "P2P: * Connection Capability: 0x%x",
+ *msg->conn_cap);
+ break;
+ case P2P_ATTR_ADVERTISEMENT_ID:
+ if (len < 10) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: Too short Advertisement ID (length %u)",
+ len);
+ return -1;
+ }
+ msg->adv_id = data;
+ msg->adv_mac = &data[sizeof(u32)];
+ wpa_printf(MSG_DEBUG, "P2P: * Advertisement ID %x",
+ WPA_GET_LE32(data));
+ break;
+ case P2P_ATTR_ADVERTISED_SERVICE:
+ if (len < 8) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: Too short Service Instance (length %u)",
+ len);
+ return -1;
+ }
+ msg->adv_service_instance = data;
+ msg->adv_service_instance_len = len;
+ if (len <= 255 + 8) {
+ char str[256];
+ u8 namelen;
+
+ namelen = data[6];
+ if (namelen > len - 7)
+ break;
+ os_memcpy(str, &data[7], namelen);
+ str[namelen] = '\0';
+ wpa_printf(MSG_DEBUG, "P2P: * Service Instance: %x-%s",
+ WPA_GET_LE32(data), str);
+ } else {
+ wpa_printf(MSG_DEBUG, "P2P: * Service Instance: %p",
+ data);
+ }
+ break;
+ case P2P_ATTR_SESSION_ID:
+ if (len < sizeof(u32) + ETH_ALEN) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: Too short Session ID Info (length %u)",
+ len);
+ return -1;
+ }
+ msg->session_id = data;
+ msg->session_mac = &data[sizeof(u32)];
+ wpa_printf(MSG_DEBUG, "P2P: * Session ID: %x " MACSTR,
+ WPA_GET_LE32(data), MAC2STR(msg->session_mac));
+ break;
+ case P2P_ATTR_FEATURE_CAPABILITY:
+ if (!len) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: Too short Feature Capability (length %u)",
+ len);
+ return -1;
+ }
+ msg->feature_cap = data;
+ msg->feature_cap_len = len;
+ wpa_printf(MSG_DEBUG, "P2P: * Feature Cap (length=%u)", len);
+ break;
+ case P2P_ATTR_PERSISTENT_GROUP:
+ {
+ if (len < ETH_ALEN) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: Too short Persistent Group Info (length %u)",
+ len);
+ return -1;
+ }
+
+ msg->persistent_dev = data;
+ msg->persistent_ssid_len = len - ETH_ALEN;
+ msg->persistent_ssid = &data[ETH_ALEN];
+ wpa_printf(MSG_DEBUG, "P2P: * Persistent Group: " MACSTR " %s",
+ MAC2STR(msg->persistent_dev),
+ wpa_ssid_txt(msg->persistent_ssid,
+ msg->persistent_ssid_len));
+ break;
+ }
default:
wpa_printf(MSG_DEBUG, "P2P: Skipped unknown attribute %d "
"(length %d)", id, len);
@@ -309,23 +415,27 @@ int p2p_parse_p2p_ie(const struct wpabuf *buf, struct p2p_message *msg)
while (pos < end) {
u16 attr_len;
- if (pos + 2 >= end) {
+ u8 id;
+
+ if (end - pos < 3) {
wpa_printf(MSG_DEBUG, "P2P: Invalid P2P attribute");
return -1;
}
- attr_len = WPA_GET_LE16(pos + 1);
+ id = *pos++;
+ attr_len = WPA_GET_LE16(pos);
+ pos += 2;
wpa_printf(MSG_DEBUG, "P2P: Attribute %d length %u",
- pos[0], attr_len);
- if (pos + 3 + attr_len > end) {
+ id, attr_len);
+ if (attr_len > end - pos) {
wpa_printf(MSG_DEBUG, "P2P: Attribute underflow "
"(len=%u left=%d)",
- attr_len, (int) (end - pos - 3));
+ attr_len, (int) (end - pos));
wpa_hexdump(MSG_MSGDUMP, "P2P: Data", pos, end - pos);
return -1;
}
- if (p2p_parse_attribute(pos[0], pos + 3, attr_len, msg))
+ if (p2p_parse_attribute(id, pos, attr_len, msg))
return -1;
- pos += 3 + attr_len;
+ pos += attr_len;
}
return 0;
@@ -603,7 +713,7 @@ static int p2p_group_info_text(const u8 *gi, size_t gi_len, char *buf,
"dev=" MACSTR " iface=" MACSTR,
MAC2STR(cli->p2p_device_addr),
MAC2STR(cli->p2p_interface_addr));
- if (ret < 0 || ret >= end - pos)
+ if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
@@ -614,7 +724,7 @@ static int p2p_group_info_text(const u8 *gi, size_t gi_len, char *buf,
wps_dev_type_bin2str(cli->pri_dev_type,
devtype,
sizeof(devtype)));
- if (ret < 0 || ret >= end - pos)
+ if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
@@ -623,7 +733,7 @@ static int p2p_group_info_text(const u8 *gi, size_t gi_len, char *buf,
wps_dev_type_bin2str(
&cli->sec_dev_types[s * 8],
devtype, sizeof(devtype)));
- if (ret < 0 || ret >= end - pos)
+ if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
@@ -638,7 +748,7 @@ static int p2p_group_info_text(const u8 *gi, size_t gi_len, char *buf,
}
ret = os_snprintf(pos, end - pos, " dev_name='%s'\n", name);
- if (ret < 0 || ret >= end - pos)
+ if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
@@ -672,7 +782,7 @@ int p2p_attr_text(struct wpabuf *data, char *buf, char *end)
"p2p_dev_capab=0x%x\n"
"p2p_group_capab=0x%x\n",
msg.capability[0], msg.capability[1]);
- if (ret < 0 || ret >= end - pos)
+ if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
@@ -684,14 +794,14 @@ int p2p_attr_text(struct wpabuf *data, char *buf, char *end)
wps_dev_type_bin2str(msg.pri_dev_type,
devtype,
sizeof(devtype)));
- if (ret < 0 || ret >= end - pos)
+ if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
ret = os_snprintf(pos, end - pos, "p2p_device_name=%s\n",
msg.device_name);
- if (ret < 0 || ret >= end - pos)
+ if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
@@ -699,14 +809,14 @@ int p2p_attr_text(struct wpabuf *data, char *buf, char *end)
ret = os_snprintf(pos, end - pos, "p2p_device_addr=" MACSTR
"\n",
MAC2STR(msg.p2p_device_addr));
- if (ret < 0 || ret >= end - pos)
+ if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
}
ret = os_snprintf(pos, end - pos, "p2p_config_methods=0x%x\n",
msg.config_methods);
- if (ret < 0 || ret >= end - pos)
+ if (os_snprintf_error(end - pos, ret))
return pos - buf;
pos += ret;
diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c
index e101367..328b1e0 100644
--- a/src/p2p/p2p_pd.c
+++ b/src/p2p/p2p_pd.c
@@ -40,14 +40,132 @@ static void p2p_build_wps_ie_config_methods(struct wpabuf *buf,
}
+static void p2ps_add_new_group_info(struct p2p_data *p2p, struct wpabuf *buf)
+{
+ int found;
+ u8 intended_addr[ETH_ALEN];
+ u8 ssid[32];
+ size_t ssid_len;
+ int group_iface;
+
+ if (!p2p->cfg->get_go_info)
+ return;
+
+ found = p2p->cfg->get_go_info(
+ p2p->cfg->cb_ctx, intended_addr, ssid,
+ &ssid_len, &group_iface);
+ if (found) {
+ p2p_buf_add_group_id(buf, p2p->cfg->dev_addr,
+ ssid, ssid_len);
+ p2p_buf_add_intended_addr(buf, intended_addr);
+ } else {
+ if (!p2p->ssid_set) {
+ p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len);
+ p2p->ssid_set = 1;
+ }
+
+ /* Add pre-composed P2P Group ID */
+ p2p_buf_add_group_id(buf, p2p->cfg->dev_addr,
+ p2p->ssid, p2p->ssid_len);
+
+ if (group_iface)
+ p2p_buf_add_intended_addr(
+ buf, p2p->intended_addr);
+ else
+ p2p_buf_add_intended_addr(
+ buf, p2p->cfg->dev_addr);
+ }
+}
+
+
+static void p2ps_add_pd_req_attrs(struct p2p_data *p2p, struct p2p_device *dev,
+ struct wpabuf *buf, u16 config_methods)
+{
+ struct p2ps_provision *prov = p2p->p2ps_prov;
+ u8 feat_cap_mask[] = { 1, 0 };
+ int shared_group = 0;
+ u8 ssid[32];
+ size_t ssid_len;
+ u8 go_dev_addr[ETH_ALEN];
+
+ /* If we might be explicite group owner, add GO details */
+ if (prov->conncap & (P2PS_SETUP_GROUP_OWNER |
+ P2PS_SETUP_NEW))
+ p2ps_add_new_group_info(p2p, buf);
+
+ if (prov->status >= 0)
+ p2p_buf_add_status(buf, (u8) prov->status);
+ else
+ prov->method = config_methods;
+
+ if (p2p->cfg->get_persistent_group) {
+ shared_group = p2p->cfg->get_persistent_group(
+ p2p->cfg->cb_ctx, dev->info.p2p_device_addr, NULL, 0,
+ go_dev_addr, ssid, &ssid_len);
+ }
+
+ /* Add Operating Channel if conncap includes GO */
+ if (shared_group ||
+ (prov->conncap & (P2PS_SETUP_GROUP_OWNER |
+ P2PS_SETUP_NEW))) {
+ u8 tmp;
+
+ p2p_go_select_channel(p2p, dev, &tmp);
+
+ if (p2p->op_reg_class && p2p->op_channel)
+ p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+ p2p->op_reg_class,
+ p2p->op_channel);
+ else
+ p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+ p2p->cfg->op_reg_class,
+ p2p->cfg->op_channel);
+ }
+
+ p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->cfg->channels);
+
+ if (prov->info[0])
+ p2p_buf_add_session_info(buf, prov->info);
+
+ p2p_buf_add_connection_capability(buf, prov->conncap);
+
+ p2p_buf_add_advertisement_id(buf, prov->adv_id, prov->adv_mac);
+
+ if (shared_group || prov->conncap == P2PS_SETUP_NEW ||
+ prov->conncap ==
+ (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW) ||
+ prov->conncap ==
+ (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT)) {
+ /* Add Config Timeout */
+ p2p_buf_add_config_timeout(buf, p2p->go_timeout,
+ p2p->client_timeout);
+ }
+
+ p2p_buf_add_listen_channel(buf, p2p->cfg->country, p2p->cfg->reg_class,
+ p2p->cfg->channel);
+
+ p2p_buf_add_session_id(buf, prov->session_id, prov->session_mac);
+
+ p2p_buf_add_feature_capability(buf, sizeof(feat_cap_mask),
+ feat_cap_mask);
+
+ if (shared_group)
+ p2p_buf_add_persistent_group_info(buf, go_dev_addr,
+ ssid, ssid_len);
+}
+
+
static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p,
- u8 dialog_token,
- u16 config_methods,
- struct p2p_device *go)
+ struct p2p_device *dev,
+ int join)
{
struct wpabuf *buf;
u8 *len;
size_t extra = 0;
+ u8 dialog_token = dev->dialog_token;
+ u16 config_methods = dev->req_config_methods;
+ struct p2p_device *go = join ? dev : NULL;
+ u8 group_capab;
#ifdef CONFIG_WIFI_DISPLAY
if (p2p->wfd_ie_prov_disc_req)
@@ -57,6 +175,10 @@ static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p,
if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ])
extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]);
+ if (p2p->p2ps_prov)
+ extra += os_strlen(p2p->p2ps_prov->info) + 1 +
+ sizeof(struct p2ps_provision);
+
buf = wpabuf_alloc(1000 + extra);
if (buf == NULL)
return NULL;
@@ -64,10 +186,23 @@ static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p,
p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_REQ, dialog_token);
len = p2p_buf_add_ie_hdr(buf);
+
+ group_capab = 0;
+ if (p2p->p2ps_prov) {
+ group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
+ group_capab |= P2P_GROUP_CAPAB_PERSISTENT_RECONN;
+ if (p2p->cross_connect)
+ group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
+ if (p2p->cfg->p2p_intra_bss)
+ group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
+ }
p2p_buf_add_capability(buf, p2p->dev_capab &
- ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
+ ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY,
+ group_capab);
p2p_buf_add_device_info(buf, p2p, NULL);
- if (go) {
+ if (p2p->p2ps_prov) {
+ p2ps_add_pd_req_attrs(p2p, dev, buf, config_methods);
+ } else if (go) {
p2p_buf_add_group_id(buf, go->info.p2p_device_addr,
go->oper_ssid, go->oper_ssid_len);
}
@@ -89,13 +224,19 @@ static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p,
static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p,
+ struct p2p_device *dev,
u8 dialog_token,
+ enum p2p_status_code status,
u16 config_methods,
+ u32 adv_id,
const u8 *group_id,
- size_t group_id_len)
+ size_t group_id_len,
+ const u8 *persist_ssid,
+ size_t persist_ssid_len)
{
struct wpabuf *buf;
size_t extra = 0;
+ int persist = 0;
#ifdef CONFIG_WIFI_DISPLAY
struct wpabuf *wfd_ie = p2p->wfd_ie_prov_disc_resp;
@@ -121,12 +262,103 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p,
if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP])
extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]);
- buf = wpabuf_alloc(100 + extra);
+ buf = wpabuf_alloc(1000 + extra);
if (buf == NULL)
return NULL;
p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_RESP, dialog_token);
+ /* Add P2P IE for P2PS */
+ if (p2p->p2ps_prov && p2p->p2ps_prov->adv_id == adv_id) {
+ u8 feat_cap_mask[] = { 1, 0 };
+ u8 *len = p2p_buf_add_ie_hdr(buf);
+ struct p2ps_provision *prov = p2p->p2ps_prov;
+ u8 group_capab;
+
+ if (!status && prov->status != -1)
+ status = prov->status;
+
+ p2p_buf_add_status(buf, status);
+ group_capab = P2P_GROUP_CAPAB_PERSISTENT_GROUP |
+ P2P_GROUP_CAPAB_PERSISTENT_RECONN;
+ if (p2p->cross_connect)
+ group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
+ if (p2p->cfg->p2p_intra_bss)
+ group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
+ p2p_buf_add_capability(buf, p2p->dev_capab &
+ ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY,
+ group_capab);
+ p2p_buf_add_device_info(buf, p2p, NULL);
+
+ if (persist_ssid && p2p->cfg->get_persistent_group &&
+ (status == P2P_SC_SUCCESS ||
+ status == P2P_SC_SUCCESS_DEFERRED)) {
+ u8 ssid[32];
+ size_t ssid_len;
+ u8 go_dev_addr[ETH_ALEN];
+
+ persist = p2p->cfg->get_persistent_group(
+ p2p->cfg->cb_ctx,
+ dev->info.p2p_device_addr,
+ persist_ssid, persist_ssid_len, go_dev_addr,
+ ssid, &ssid_len);
+ if (persist)
+ p2p_buf_add_persistent_group_info(
+ buf, go_dev_addr, ssid, ssid_len);
+ }
+
+ if (!persist && (prov->conncap & P2PS_SETUP_GROUP_OWNER))
+ p2ps_add_new_group_info(p2p, buf);
+
+ /* Add Operating Channel if conncap indicates GO */
+ if (persist || (prov->conncap & P2PS_SETUP_GROUP_OWNER)) {
+ u8 tmp;
+
+ if (dev)
+ p2p_go_select_channel(p2p, dev, &tmp);
+
+ if (p2p->op_reg_class && p2p->op_channel)
+ p2p_buf_add_operating_channel(
+ buf, p2p->cfg->country,
+ p2p->op_reg_class,
+ p2p->op_channel);
+ else
+ p2p_buf_add_operating_channel(
+ buf, p2p->cfg->country,
+ p2p->cfg->op_reg_class,
+ p2p->cfg->op_channel);
+ }
+
+ p2p_buf_add_channel_list(buf, p2p->cfg->country,
+ &p2p->cfg->channels);
+
+ if (!persist && (status == P2P_SC_SUCCESS ||
+ status == P2P_SC_SUCCESS_DEFERRED))
+ p2p_buf_add_connection_capability(buf, prov->conncap);
+
+ p2p_buf_add_advertisement_id(buf, adv_id, prov->adv_mac);
+
+ p2p_buf_add_config_timeout(buf, p2p->go_timeout,
+ p2p->client_timeout);
+
+ p2p_buf_add_session_id(buf, prov->session_id,
+ prov->session_mac);
+
+ p2p_buf_add_feature_capability(buf, sizeof(feat_cap_mask),
+ feat_cap_mask);
+ p2p_buf_update_ie_hdr(buf, len);
+ } else if (status != P2P_SC_SUCCESS || adv_id) {
+ u8 *len = p2p_buf_add_ie_hdr(buf);
+
+ p2p_buf_add_status(buf, status);
+
+ if (p2p->p2ps_prov)
+ p2p_buf_add_advertisement_id(buf, adv_id,
+ p2p->p2ps_prov->adv_mac);
+
+ p2p_buf_update_ie_hdr(buf, len);
+ }
+
/* WPS IE with Config Methods attribute */
p2p_build_wps_ie_config_methods(buf, config_methods);
@@ -142,14 +374,50 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p,
}
+static int p2ps_setup_p2ps_prov(struct p2p_data *p2p, u32 adv_id,
+ u32 session_id, u16 method,
+ const u8 *session_mac, const u8 *adv_mac)
+{
+ struct p2ps_provision *tmp;
+
+ if (!p2p->p2ps_prov) {
+ p2p->p2ps_prov = os_zalloc(sizeof(struct p2ps_provision) + 1);
+ if (!p2p->p2ps_prov)
+ return -1;
+ } else {
+ os_memset(p2p->p2ps_prov, 0, sizeof(struct p2ps_provision) + 1);
+ }
+
+ tmp = p2p->p2ps_prov;
+ tmp->adv_id = adv_id;
+ tmp->session_id = session_id;
+ tmp->method = method;
+ os_memcpy(tmp->session_mac, session_mac, ETH_ALEN);
+ os_memcpy(tmp->adv_mac, adv_mac, ETH_ALEN);
+ tmp->info[0] = '\0';
+
+ return 0;
+}
+
+
void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len, int rx_freq)
{
struct p2p_message msg;
struct p2p_device *dev;
int freq;
- int reject = 1;
+ enum p2p_status_code reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
struct wpabuf *resp;
+ u32 adv_id = 0;
+ struct p2ps_advertisement *p2ps_adv = NULL;
+ u8 conncap = P2PS_SETUP_NEW;
+ u8 auto_accept = 0;
+ u32 session_id = 0;
+ u8 session_mac[ETH_ALEN];
+ u8 adv_mac[ETH_ALEN];
+ u8 group_mac[ETH_ALEN];
+ int passwd_id = DEV_PW_DEFAULT;
+ u16 config_methods;
if (p2p_parse(data, len, &msg))
return;
@@ -175,12 +443,13 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
if (!(msg.wps_config_methods &
(WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD |
- WPS_CONFIG_PUSHBUTTON))) {
+ WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_P2PS))) {
p2p_dbg(p2p, "Unsupported Config Methods in Provision Discovery Request");
goto out;
}
- if (msg.group_id) {
+ /* Legacy (non-P2PS) - Unknown groups allowed for P2PS */
+ if (!msg.adv_id && msg.group_id) {
size_t i;
for (i = 0; i < p2p->num_groups; i++) {
if (p2p_group_is_group_id_match(p2p->groups[i],
@@ -194,28 +463,203 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
}
}
- if (dev)
+ if (dev) {
dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
- P2P_DEV_PD_PEER_KEYPAD);
+ P2P_DEV_PD_PEER_KEYPAD |
+ P2P_DEV_PD_PEER_P2PS);
+
+ /* Remove stale persistent groups */
+ if (p2p->cfg->remove_stale_groups) {
+ p2p->cfg->remove_stale_groups(
+ p2p->cfg->cb_ctx, dev->info.p2p_device_addr,
+ msg.persistent_dev,
+ msg.persistent_ssid, msg.persistent_ssid_len);
+ }
+ }
if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) {
p2p_dbg(p2p, "Peer " MACSTR
" requested us to show a PIN on display", MAC2STR(sa));
if (dev)
dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
+ passwd_id = DEV_PW_USER_SPECIFIED;
} else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
p2p_dbg(p2p, "Peer " MACSTR
" requested us to write its PIN using keypad",
MAC2STR(sa));
if (dev)
dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
+ passwd_id = DEV_PW_REGISTRAR_SPECIFIED;
+ } else if (msg.wps_config_methods & WPS_CONFIG_P2PS) {
+ p2p_dbg(p2p, "Peer " MACSTR " requesting P2PS PIN",
+ MAC2STR(sa));
+ if (dev)
+ dev->flags |= P2P_DEV_PD_PEER_P2PS;
+ passwd_id = DEV_PW_P2PS_DEFAULT;
}
- reject = 0;
+ reject = P2P_SC_SUCCESS;
+
+ os_memset(session_mac, 0, ETH_ALEN);
+ os_memset(adv_mac, 0, ETH_ALEN);
+ os_memset(group_mac, 0, ETH_ALEN);
+
+ if (msg.adv_id && msg.session_id && msg.session_mac && msg.adv_mac &&
+ (msg.status || msg.conn_cap)) {
+ u8 remote_conncap;
+
+ if (msg.intended_addr)
+ os_memcpy(group_mac, msg.intended_addr, ETH_ALEN);
+
+ os_memcpy(session_mac, msg.session_mac, ETH_ALEN);
+ os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN);
+
+ session_id = WPA_GET_LE32(msg.session_id);
+ adv_id = WPA_GET_LE32(msg.adv_id);
+
+ if (!msg.status)
+ p2ps_adv = p2p_service_p2ps_id(p2p, adv_id);
+
+ p2p_dbg(p2p, "adv_id: %x - p2ps_adv - %p", adv_id, p2ps_adv);
+
+ if (msg.conn_cap)
+ conncap = *msg.conn_cap;
+ remote_conncap = conncap;
+
+ if (p2ps_adv) {
+ auto_accept = p2ps_adv->auto_accept;
+ conncap = p2p->cfg->p2ps_group_capability(
+ p2p->cfg->cb_ctx, conncap, auto_accept);
+
+ p2p_dbg(p2p, "Conncap: local:%d remote:%d result:%d",
+ auto_accept, remote_conncap, conncap);
+
+ if (p2ps_adv->config_methods &&
+ !(msg.wps_config_methods &
+ p2ps_adv->config_methods)) {
+ p2p_dbg(p2p,
+ "Unsupported config methods in Provision Discovery Request (own=0x%x peer=0x%x)",
+ p2ps_adv->config_methods,
+ msg.wps_config_methods);
+ reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+ } else if (!p2ps_adv->state) {
+ p2p_dbg(p2p, "P2PS state unavailable");
+ reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+ } else if (!conncap) {
+ p2p_dbg(p2p, "Conncap resolution failed");
+ reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+ }
+
+ if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
+ p2p_dbg(p2p, "Keypad - always defer");
+ auto_accept = 0;
+ }
+
+ if (auto_accept || reject != P2P_SC_SUCCESS) {
+ struct p2ps_provision *tmp;
+
+ if (reject == P2P_SC_SUCCESS && !conncap) {
+ reject =
+ P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+ }
+
+ if (p2ps_setup_p2ps_prov(
+ p2p, adv_id, session_id,
+ msg.wps_config_methods,
+ session_mac, adv_mac) < 0) {
+ reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+ goto out;
+ }
+
+ tmp = p2p->p2ps_prov;
+ if (conncap) {
+ tmp->conncap = conncap;
+ tmp->status = P2P_SC_SUCCESS;
+ } else {
+ tmp->conncap = auto_accept;
+ tmp->status = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+ }
+
+ if (reject != P2P_SC_SUCCESS)
+ goto out;
+ }
+ } else if (!msg.status) {
+ reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+ goto out;
+ }
+
+ if (!msg.status && !auto_accept &&
+ (!p2p->p2ps_prov || p2p->p2ps_prov->adv_id != adv_id)) {
+ struct p2ps_provision *tmp;
+
+ if (!conncap) {
+ reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+ goto out;
+ }
+
+ if (p2ps_setup_p2ps_prov(p2p, adv_id, session_id,
+ msg.wps_config_methods,
+ session_mac, adv_mac) < 0) {
+ reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+ goto out;
+ }
+ tmp = p2p->p2ps_prov;
+ reject = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
+ tmp->status = reject;
+ }
+
+ if (msg.status) {
+ if (*msg.status &&
+ *msg.status != P2P_SC_SUCCESS_DEFERRED) {
+ reject = *msg.status;
+ } else if (*msg.status == P2P_SC_SUCCESS_DEFERRED &&
+ p2p->p2ps_prov) {
+ u16 method = p2p->p2ps_prov->method;
+
+ conncap = p2p->cfg->p2ps_group_capability(
+ p2p->cfg->cb_ctx, remote_conncap,
+ p2p->p2ps_prov->conncap);
+
+ p2p_dbg(p2p,
+ "Conncap: local:%d remote:%d result:%d",
+ p2p->p2ps_prov->conncap,
+ remote_conncap, conncap);
+
+ /*
+ * Ensure that if we asked for PIN originally,
+ * our method is consistent with original
+ * request.
+ */
+ if (method & WPS_CONFIG_DISPLAY)
+ method = WPS_CONFIG_KEYPAD;
+ else if (method & WPS_CONFIG_KEYPAD)
+ method = WPS_CONFIG_DISPLAY;
+
+ /* Reject this "Deferred Accept* if incompatible
+ * conncap or method */
+ if (!conncap ||
+ !(msg.wps_config_methods & method))
+ reject =
+ P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+ else
+ reject = P2P_SC_SUCCESS;
+
+ p2p->p2ps_prov->status = reject;
+ p2p->p2ps_prov->conncap = conncap;
+ }
+ }
+ }
out:
- resp = p2p_build_prov_disc_resp(p2p, msg.dialog_token,
- reject ? 0 : msg.wps_config_methods,
- msg.group_id, msg.group_id_len);
+ if (reject == P2P_SC_SUCCESS ||
+ reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE)
+ config_methods = msg.wps_config_methods;
+ else
+ config_methods = 0;
+ resp = p2p_build_prov_disc_resp(p2p, dev, msg.dialog_token, reject,
+ config_methods, adv_id,
+ msg.group_id, msg.group_id_len,
+ msg.persistent_ssid,
+ msg.persistent_ssid_len);
if (resp == NULL) {
p2p_parse_free(&msg);
return;
@@ -232,7 +676,7 @@ out:
p2p_parse_free(&msg);
return;
}
- p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+ p2p->pending_action_state = P2P_PENDING_PD_RESPONSE;
if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr,
p2p->cfg->dev_addr,
wpabuf_head(resp), wpabuf_len(resp), 200) < 0) {
@@ -242,7 +686,91 @@ out:
wpabuf_free(resp);
- if (!reject && p2p->cfg->prov_disc_req) {
+ if (!p2p->cfg->p2ps_prov_complete) {
+ /* Don't emit anything */
+ } else if (msg.status && *msg.status != P2P_SC_SUCCESS &&
+ *msg.status != P2P_SC_SUCCESS_DEFERRED) {
+ reject = *msg.status;
+ p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, reject,
+ sa, adv_mac, session_mac,
+ NULL, adv_id, session_id,
+ 0, 0, msg.persistent_ssid,
+ msg.persistent_ssid_len,
+ 0, 0, NULL);
+ } else if (msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED &&
+ p2p->p2ps_prov) {
+ p2p->p2ps_prov->status = reject;
+ p2p->p2ps_prov->conncap = conncap;
+
+ if (reject != P2P_SC_SUCCESS)
+ p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, reject,
+ sa, adv_mac, session_mac,
+ NULL, adv_id,
+ session_id, conncap, 0,
+ msg.persistent_ssid,
+ msg.persistent_ssid_len, 0,
+ 0, NULL);
+ else
+ p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx,
+ *msg.status,
+ sa, adv_mac, session_mac,
+ group_mac, adv_id,
+ session_id, conncap,
+ passwd_id,
+ msg.persistent_ssid,
+ msg.persistent_ssid_len, 0,
+ 0, NULL);
+ } else if (msg.status && p2p->p2ps_prov) {
+ p2p->p2ps_prov->status = P2P_SC_SUCCESS;
+ p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, *msg.status, sa,
+ adv_mac, session_mac, group_mac,
+ adv_id, session_id, conncap,
+ passwd_id,
+ msg.persistent_ssid,
+ msg.persistent_ssid_len,
+ 0, 0, NULL);
+ } else if (msg.status) {
+ } else if (auto_accept && reject == P2P_SC_SUCCESS) {
+ p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS,
+ sa, adv_mac, session_mac,
+ group_mac, adv_id, session_id,
+ conncap, passwd_id,
+ msg.persistent_ssid,
+ msg.persistent_ssid_len,
+ 0, 0, NULL);
+ } else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
+ (!msg.session_info || !msg.session_info_len)) {
+ p2p->p2ps_prov->method = msg.wps_config_methods;
+
+ p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS,
+ sa, adv_mac, session_mac,
+ group_mac, adv_id, session_id,
+ conncap, passwd_id,
+ msg.persistent_ssid,
+ msg.persistent_ssid_len,
+ 0, 1, NULL);
+ } else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
+ size_t buf_len = msg.session_info_len;
+ char *buf = os_malloc(2 * buf_len + 1);
+
+ if (buf) {
+ p2p->p2ps_prov->method = msg.wps_config_methods;
+
+ utf8_escape((char *) msg.session_info, buf_len,
+ buf, 2 * buf_len + 1);
+
+ p2p->cfg->p2ps_prov_complete(
+ p2p->cfg->cb_ctx, P2P_SC_SUCCESS, sa,
+ adv_mac, session_mac, group_mac, adv_id,
+ session_id, conncap, passwd_id,
+ msg.persistent_ssid, msg.persistent_ssid_len,
+ 0, 1, buf);
+
+ os_free(buf);
+ }
+ }
+
+ if (reject == P2P_SC_SUCCESS && p2p->cfg->prov_disc_req) {
const u8 *dev_addr = sa;
if (msg.p2p_device_addr)
dev_addr = msg.p2p_device_addr;
@@ -265,11 +793,48 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa,
struct p2p_message msg;
struct p2p_device *dev;
u16 report_config_methods = 0, req_config_methods;
+ u8 status = P2P_SC_SUCCESS;
int success = 0;
+ u32 adv_id = 0;
+ u8 conncap = P2PS_SETUP_NEW;
+ u8 adv_mac[ETH_ALEN];
+ u8 group_mac[ETH_ALEN];
+ int passwd_id = DEV_PW_DEFAULT;
if (p2p_parse(data, len, &msg))
return;
+ /* Parse the P2PS members present */
+ if (msg.status)
+ status = *msg.status;
+
+ if (msg.intended_addr)
+ os_memcpy(group_mac, msg.intended_addr, ETH_ALEN);
+ else
+ os_memset(group_mac, 0, ETH_ALEN);
+
+ if (msg.adv_mac)
+ os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN);
+ else
+ os_memset(adv_mac, 0, ETH_ALEN);
+
+ if (msg.adv_id)
+ adv_id = WPA_GET_LE32(msg.adv_id);
+
+ if (msg.conn_cap) {
+ conncap = *msg.conn_cap;
+
+ /* Switch bits to local relative */
+ switch (conncap) {
+ case P2PS_SETUP_GROUP_OWNER:
+ conncap = P2PS_SETUP_CLIENT;
+ break;
+ case P2PS_SETUP_CLIENT:
+ conncap = P2PS_SETUP_GROUP_OWNER;
+ break;
+ }
+ }
+
p2p_dbg(p2p, "Received Provision Discovery Response from " MACSTR
" with config methods 0x%x",
MAC2STR(sa), msg.wps_config_methods);
@@ -313,23 +878,109 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa,
msg.wps_config_methods, req_config_methods);
if (p2p->cfg->prov_disc_fail)
p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa,
- P2P_PROV_DISC_REJECTED);
+ P2P_PROV_DISC_REJECTED,
+ adv_id, adv_mac, NULL);
p2p_parse_free(&msg);
+ os_free(p2p->p2ps_prov);
+ p2p->p2ps_prov = NULL;
goto out;
}
report_config_methods = req_config_methods;
dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
- P2P_DEV_PD_PEER_KEYPAD);
+ P2P_DEV_PD_PEER_KEYPAD |
+ P2P_DEV_PD_PEER_P2PS);
if (req_config_methods & WPS_CONFIG_DISPLAY) {
p2p_dbg(p2p, "Peer " MACSTR
" accepted to show a PIN on display", MAC2STR(sa));
dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
+ passwd_id = DEV_PW_REGISTRAR_SPECIFIED;
} else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
p2p_dbg(p2p, "Peer " MACSTR
" accepted to write our PIN using keypad",
MAC2STR(sa));
dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
+ passwd_id = DEV_PW_USER_SPECIFIED;
+ } else if (msg.wps_config_methods & WPS_CONFIG_P2PS) {
+ p2p_dbg(p2p, "Peer " MACSTR " accepted P2PS PIN",
+ MAC2STR(sa));
+ dev->flags |= P2P_DEV_PD_PEER_P2PS;
+ passwd_id = DEV_PW_P2PS_DEFAULT;
+ }
+
+ if ((msg.conn_cap || msg.persistent_dev) &&
+ msg.adv_id &&
+ (status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED) &&
+ p2p->p2ps_prov) {
+ if (p2p->cfg->p2ps_prov_complete) {
+ p2p->cfg->p2ps_prov_complete(
+ p2p->cfg->cb_ctx, status, sa, adv_mac,
+ p2p->p2ps_prov->session_mac,
+ group_mac, adv_id, p2p->p2ps_prov->session_id,
+ conncap, passwd_id, msg.persistent_ssid,
+ msg.persistent_ssid_len, 1, 0, NULL);
+ }
+ os_free(p2p->p2ps_prov);
+ p2p->p2ps_prov = NULL;
+ }
+
+ if (status != P2P_SC_SUCCESS &&
+ status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
+ status != P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) {
+ if (p2p->cfg->p2ps_prov_complete)
+ p2p->cfg->p2ps_prov_complete(
+ p2p->cfg->cb_ctx, status, sa, adv_mac,
+ p2p->p2ps_prov->session_mac,
+ group_mac, adv_id, p2p->p2ps_prov->session_id,
+ 0, 0, NULL, 0, 1, 0, NULL);
+ os_free(p2p->p2ps_prov);
+ p2p->p2ps_prov = NULL;
+ }
+
+ if (status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
+ if (p2p->cfg->remove_stale_groups) {
+ p2p->cfg->remove_stale_groups(p2p->cfg->cb_ctx,
+ dev->info.p2p_device_addr,
+ NULL, NULL, 0);
+ }
+
+ if (msg.session_info && msg.session_info_len) {
+ size_t info_len = msg.session_info_len;
+ char *deferred_sess_resp = os_malloc(2 * info_len + 1);
+
+ if (!deferred_sess_resp) {
+ p2p_parse_free(&msg);
+ os_free(p2p->p2ps_prov);
+ p2p->p2ps_prov = NULL;
+ goto out;
+ }
+ utf8_escape((char *) msg.session_info, info_len,
+ deferred_sess_resp, 2 * info_len + 1);
+
+ if (p2p->cfg->prov_disc_fail)
+ p2p->cfg->prov_disc_fail(
+ p2p->cfg->cb_ctx, sa,
+ P2P_PROV_DISC_INFO_UNAVAILABLE,
+ adv_id, adv_mac,
+ deferred_sess_resp);
+ os_free(deferred_sess_resp);
+ } else
+ if (p2p->cfg->prov_disc_fail)
+ p2p->cfg->prov_disc_fail(
+ p2p->cfg->cb_ctx, sa,
+ P2P_PROV_DISC_INFO_UNAVAILABLE,
+ adv_id, adv_mac, NULL);
+ } else if (msg.wps_config_methods != dev->req_config_methods ||
+ status != P2P_SC_SUCCESS) {
+ p2p_dbg(p2p, "Peer rejected our Provision Discovery Request");
+ if (p2p->cfg->prov_disc_fail)
+ p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa,
+ P2P_PROV_DISC_REJECTED, 0,
+ NULL, NULL);
+ p2p_parse_free(&msg);
+ os_free(p2p->p2ps_prov);
+ p2p->p2ps_prov = NULL;
+ goto out;
}
/* Store the provisioning info */
@@ -388,9 +1039,33 @@ int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev,
/* TODO: use device discoverability request through GO */
}
- req = p2p_build_prov_disc_req(p2p, dev->dialog_token,
- dev->req_config_methods,
- join ? dev : NULL);
+ if (p2p->p2ps_prov) {
+ if (p2p->p2ps_prov->status == P2P_SC_SUCCESS_DEFERRED) {
+ if (p2p->p2ps_prov->method == WPS_CONFIG_DISPLAY)
+ dev->req_config_methods = WPS_CONFIG_KEYPAD;
+ else if (p2p->p2ps_prov->method == WPS_CONFIG_KEYPAD)
+ dev->req_config_methods = WPS_CONFIG_DISPLAY;
+ else
+ dev->req_config_methods = WPS_CONFIG_P2PS;
+ } else {
+ /* Order of preference, based on peer's capabilities */
+ if (p2p->p2ps_prov->method)
+ dev->req_config_methods =
+ p2p->p2ps_prov->method;
+ else if (dev->info.config_methods & WPS_CONFIG_P2PS)
+ dev->req_config_methods = WPS_CONFIG_P2PS;
+ else if (dev->info.config_methods & WPS_CONFIG_DISPLAY)
+ dev->req_config_methods = WPS_CONFIG_DISPLAY;
+ else
+ dev->req_config_methods = WPS_CONFIG_KEYPAD;
+ }
+ p2p_dbg(p2p,
+ "Building PD Request based on P2PS config method 0x%x status %d --> req_config_methods 0x%x",
+ p2p->p2ps_prov->method, p2p->p2ps_prov->status,
+ dev->req_config_methods);
+ }
+
+ req = p2p_build_prov_disc_req(p2p, dev, join);
if (req == NULL)
return -1;
@@ -413,6 +1088,7 @@ int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev,
int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr,
+ struct p2ps_provision *p2ps_prov,
u16 config_methods, int join, int force_freq,
int user_initiated_pd)
{
@@ -424,17 +1100,28 @@ int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr,
if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
p2p_dbg(p2p, "Provision Discovery Request destination " MACSTR
" not yet known", MAC2STR(peer_addr));
+ os_free(p2ps_prov);
return -1;
}
p2p_dbg(p2p, "Provision Discovery Request with " MACSTR
" (config methods 0x%x)",
MAC2STR(peer_addr), config_methods);
- if (config_methods == 0)
+ if (config_methods == 0 && !p2ps_prov) {
+ os_free(p2ps_prov);
return -1;
+ }
+
+ if (p2ps_prov && p2ps_prov->status == P2P_SC_SUCCESS_DEFERRED &&
+ p2p->p2ps_prov) {
+ /* Use cached method from deferred provisioning */
+ p2ps_prov->method = p2p->p2ps_prov->method;
+ }
/* Reset provisioning info */
dev->wps_prov_info = 0;
+ os_free(p2p->p2ps_prov);
+ p2p->p2ps_prov = p2ps_prov;
dev->req_config_methods = config_methods;
if (join)
diff --git a/src/p2p/p2p_sd.c b/src/p2p/p2p_sd.c
index 13119c2..1a2af04 100644
--- a/src/p2p/p2p_sd.c
+++ b/src/p2p/p2p_sd.c
@@ -75,16 +75,25 @@ struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p,
return NULL;
/* query number that needs to be send to the device */
if (count == dev->sd_pending_bcast_queries - 1)
- return q;
+ goto found;
count++;
}
if (!q->for_all_peers &&
os_memcmp(q->peer, dev->info.p2p_device_addr, ETH_ALEN) ==
0)
- return q;
+ goto found;
}
return NULL;
+
+found:
+ if (dev->sd_reqs > 100) {
+ p2p_dbg(p2p, "Too many SD request attempts to " MACSTR
+ " - skip remaining queries",
+ MAC2STR(dev->info.p2p_device_addr));
+ return NULL;
+ }
+ return q;
}
@@ -287,6 +296,7 @@ int p2p_start_sd(struct p2p_data *p2p, struct p2p_device *dev)
if (req == NULL)
return -1;
+ dev->sd_reqs++;
p2p->sd_peer = dev;
p2p->sd_query = query;
p2p->pending_action_state = P2P_PENDING_SD;
diff --git a/src/p2p/p2p_utils.c b/src/p2p/p2p_utils.c
index 23acce7..f32751d 100644
--- a/src/p2p/p2p_utils.c
+++ b/src/p2p/p2p_utils.c
@@ -9,6 +9,7 @@
#include "includes.h"
#include "common.h"
+#include "common/ieee802_11_common.h"
#include "p2p_i.h"
@@ -54,56 +55,7 @@ int p2p_random(char *buf, size_t len)
*/
int p2p_channel_to_freq(int op_class, int channel)
{
- /* Table E-4 in IEEE Std 802.11-2012 - Global operating classes */
- /* TODO: more operating classes */
- switch (op_class) {
- case 81:
- /* channels 1..13 */
- if (channel < 1 || channel > 13)
- return -1;
- return 2407 + 5 * channel;
- case 82:
- /* channel 14 */
- if (channel != 14)
- return -1;
- return 2414 + 5 * channel;
- case 83: /* channels 1..9; 40 MHz */
- case 84: /* channels 5..13; 40 MHz */
- if (channel < 1 || channel > 13)
- return -1;
- return 2407 + 5 * channel;
- case 115: /* channels 36,40,44,48; indoor only */
- case 118: /* channels 52,56,60,64; dfs */
- if (channel < 36 || channel > 64)
- return -1;
- return 5000 + 5 * channel;
- case 124: /* channels 149,153,157,161 */
- case 125: /* channels 149,153,157,161,165,169 */
- if (channel < 149 || channel > 161)
- return -1;
- return 5000 + 5 * channel;
- case 116: /* channels 36,44; 40 MHz; indoor only */
- case 117: /* channels 40,48; 40 MHz; indoor only */
- case 119: /* channels 52,60; 40 MHz; dfs */
- case 120: /* channels 56,64; 40 MHz; dfs */
- if (channel < 36 || channel > 64)
- return -1;
- return 5000 + 5 * channel;
- case 126: /* channels 149,157; 40 MHz */
- case 127: /* channels 153,161; 40 MHz */
- if (channel < 149 || channel > 161)
- return -1;
- return 5000 + 5 * channel;
- case 128: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */
- if (channel < 36 || channel > 161)
- return -1;
- return 5000 + 5 * channel;
- case 180: /* 60 GHz band, channels 1..4 */
- if (channel < 1 || channel > 4)
- return -1;
- return 56160 + 2160 * channel;
- }
- return -1;
+ return ieee80211_chan_to_freq(NULL, op_class, channel);
}
@@ -241,20 +193,15 @@ static void p2p_op_class_union(struct p2p_reg_class *cl,
/**
- * p2p_channels_union - Union of channel lists
- * @a: First set of channels
+ * p2p_channels_union_inplace - Inplace union of channel lists
+ * @res: Input data and place for returning union of the channel sets
* @b: Second set of channels
- * @res: Data structure for returning the union of channels
*/
-void p2p_channels_union(const struct p2p_channels *a,
- const struct p2p_channels *b,
- struct p2p_channels *res)
+void p2p_channels_union_inplace(struct p2p_channels *res,
+ const struct p2p_channels *b)
{
size_t i, j;
- if (a != res)
- os_memcpy(res, a, sizeof(*res));
-
for (i = 0; i < res->reg_classes; i++) {
struct p2p_reg_class *cl = &res->reg_class[i];
for (j = 0; j < b->reg_classes; j++) {
@@ -284,6 +231,21 @@ void p2p_channels_union(const struct p2p_channels *a,
}
+/**
+ * p2p_channels_union - Union of channel lists
+ * @a: First set of channels
+ * @b: Second set of channels
+ * @res: Data structure for returning the union of channels
+ */
+void p2p_channels_union(const struct p2p_channels *a,
+ const struct p2p_channels *b,
+ struct p2p_channels *res)
+{
+ os_memcpy(res, a, sizeof(*res));
+ p2p_channels_union_inplace(res, b);
+}
+
+
void p2p_channels_remove_freqs(struct p2p_channels *chan,
const struct wpa_freq_range_list *list)
{
@@ -428,7 +390,7 @@ void p2p_channels_dump(struct p2p_data *p2p, const char *title,
const struct p2p_reg_class *c;
c = &chan->reg_class[i];
ret = os_snprintf(pos, end - pos, " %u:", c->reg_class);
- if (ret < 0 || ret >= end - pos)
+ if (os_snprintf_error(end - pos, ret))
break;
pos += ret;
@@ -436,7 +398,7 @@ void p2p_channels_dump(struct p2p_data *p2p, const char *title,
ret = os_snprintf(pos, end - pos, "%s%u",
j == 0 ? "" : ",",
c->channel[j]);
- if (ret < 0 || ret >= end - pos)
+ if (os_snprintf_error(end - pos, ret))
break;
pos += ret;
}
@@ -517,3 +479,35 @@ int p2p_channel_random_social(struct p2p_channels *chans, u8 *op_class,
return 0;
}
+
+
+int p2p_channels_to_freqs(const struct p2p_channels *channels, int *freq_list,
+ unsigned int max_len)
+{
+ unsigned int i, idx;
+
+ if (!channels || max_len == 0)
+ return 0;
+
+ for (i = 0, idx = 0; i < channels->reg_classes; i++) {
+ const struct p2p_reg_class *c = &channels->reg_class[i];
+ unsigned int j;
+
+ if (idx + 1 == max_len)
+ break;
+ for (j = 0; j < c->channels; j++) {
+ int freq;
+ if (idx + 1 == max_len)
+ break;
+ freq = p2p_channel_to_freq(c->reg_class,
+ c->channel[j]);
+ if (freq < 0)
+ continue;
+ freq_list[idx++] = freq;
+ }
+ }
+
+ freq_list[idx] = 0;
+
+ return idx;
+}