diff options
author | Andrew Shadura <andrewsh@debian.org> | 2016-07-21 14:48:54 +0200 |
---|---|---|
committer | Andrew Shadura <andrewsh@debian.org> | 2016-07-21 14:48:54 +0200 |
commit | 2b2675a5c8455e6a7224b153a28a6c2b5f1acd70 (patch) | |
tree | 3f83c6ef1b28171f58dccff1397bc01be1c01956 /src/p2p | |
parent | 6e1f9c092d6958a8a1c01629f43941647dc7fc20 (diff) |
Imported Upstream version 2.4
Diffstat (limited to 'src/p2p')
-rw-r--r-- | src/p2p/p2p.c | 738 | ||||
-rw-r--r-- | src/p2p/p2p.h | 237 | ||||
-rw-r--r-- | src/p2p/p2p_build.c | 257 | ||||
-rw-r--r-- | src/p2p/p2p_go_neg.c | 54 | ||||
-rw-r--r-- | src/p2p/p2p_group.c | 12 | ||||
-rw-r--r-- | src/p2p/p2p_i.h | 75 | ||||
-rw-r--r-- | src/p2p/p2p_invitation.c | 69 | ||||
-rw-r--r-- | src/p2p/p2p_parse.c | 142 | ||||
-rw-r--r-- | src/p2p/p2p_pd.c | 735 | ||||
-rw-r--r-- | src/p2p/p2p_sd.c | 14 | ||||
-rw-r--r-- | src/p2p/p2p_utils.c | 116 |
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, ¶ms->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, ¶ms->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; +} |