diff options
Diffstat (limited to 'src/p2p')
-rw-r--r-- | src/p2p/Makefile | 3 | ||||
-rw-r--r-- | src/p2p/p2p.c | 2296 | ||||
-rw-r--r-- | src/p2p/p2p.h | 514 | ||||
-rw-r--r-- | src/p2p/p2p_build.c | 348 | ||||
-rw-r--r-- | src/p2p/p2p_dev_disc.c | 88 | ||||
-rw-r--r-- | src/p2p/p2p_go_neg.c | 655 | ||||
-rw-r--r-- | src/p2p/p2p_group.c | 214 | ||||
-rw-r--r-- | src/p2p/p2p_i.h | 192 | ||||
-rw-r--r-- | src/p2p/p2p_invitation.c | 382 | ||||
-rw-r--r-- | src/p2p/p2p_parse.c | 186 | ||||
-rw-r--r-- | src/p2p/p2p_pd.c | 858 | ||||
-rw-r--r-- | src/p2p/p2p_sd.c | 328 | ||||
-rw-r--r-- | src/p2p/p2p_utils.c | 422 |
13 files changed, 4859 insertions, 1627 deletions
diff --git a/src/p2p/Makefile b/src/p2p/Makefile index cffba620da04f..adfd3dfd5b9be 100644 --- a/src/p2p/Makefile +++ b/src/p2p/Makefile @@ -2,8 +2,7 @@ all: @echo Nothing to be made. clean: - for d in $(SUBDIRS); do make -C $$d clean; done - rm -f *~ *.o *.d + rm -f *~ *.o *.d *.gcno *.gcda *.gcov install: @echo Nothing to be made. diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c index b994a44abc1cd..6adb3dc2049fb 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" @@ -42,21 +44,32 @@ static void p2p_scan_timeout(void *eloop_ctx, void *timeout_ctx); * P2P_PEER_EXPIRATION_AGE - Number of seconds after which inactive peer * entries will be removed */ -#define P2P_PEER_EXPIRATION_AGE 300 +#ifndef P2P_PEER_EXPIRATION_AGE +#define P2P_PEER_EXPIRATION_AGE 60 +#endif /* P2P_PEER_EXPIRATION_AGE */ #define P2P_PEER_EXPIRATION_INTERVAL (P2P_PEER_EXPIRATION_AGE / 2) static void p2p_expire_peers(struct p2p_data *p2p) { struct p2p_device *dev, *n; - struct os_time now; + struct os_reltime now; size_t i; - os_get_time(&now); + os_get_reltime(&now); dl_list_for_each_safe(dev, n, &p2p->devices, struct p2p_device, list) { if (dev->last_seen.sec + P2P_PEER_EXPIRATION_AGE >= now.sec) continue; + if (dev == p2p->go_neg_peer) { + /* + * GO Negotiation is in progress with the peer, so + * don't expire the peer entry until GO Negotiation + * fails or times out. + */ + continue; + } + if (p2p->cfg->go_connected && p2p->cfg->go_connected(p2p->cfg->cb_ctx, dev->info.p2p_device_addr)) { @@ -64,7 +77,7 @@ static void p2p_expire_peers(struct p2p_data *p2p) * We are connected as a client to a group in which the * peer is the GO, so do not expire the peer entry. */ - os_get_time(&dev->last_seen); + os_get_reltime(&dev->last_seen); continue; } @@ -78,12 +91,12 @@ static void p2p_expire_peers(struct p2p_data *p2p) * The peer is connected as a client in a group where * we are the GO, so do not expire the peer entry. */ - os_get_time(&dev->last_seen); + os_get_reltime(&dev->last_seen); continue; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Expiring old peer " - "entry " MACSTR, MAC2STR(dev->info.p2p_device_addr)); + p2p_dbg(p2p, "Expiring old peer entry " MACSTR, + MAC2STR(dev->info.p2p_device_addr)); dl_list_del(&dev->list); p2p_device_free(p2p, dev); } @@ -128,16 +141,31 @@ static const char * p2p_state_txt(int state) return "INVITE"; case P2P_INVITE_LISTEN: return "INVITE_LISTEN"; - case P2P_SEARCH_WHEN_READY: - return "SEARCH_WHEN_READY"; - case P2P_CONTINUE_SEARCH_WHEN_READY: - return "CONTINUE_SEARCH_WHEN_READY"; default: return "?"; } } +const char * p2p_get_state_txt(struct p2p_data *p2p) +{ + return p2p_state_txt(p2p->state); +} + + +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; @@ -168,16 +196,23 @@ void p2p_clear_provisioning_info(struct p2p_data *p2p, const u8 *addr) void p2p_set_state(struct p2p_data *p2p, int new_state) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: State %s -> %s", + p2p_dbg(p2p, "State %s -> %s", p2p_state_txt(p2p->state), p2p_state_txt(new_state)); p2p->state = new_state; + + if (new_state == P2P_IDLE && p2p->pending_channel) { + p2p_dbg(p2p, "Apply change in listen channel"); + p2p->cfg->reg_class = p2p->pending_reg_class; + p2p->cfg->channel = p2p->pending_channel; + p2p->pending_reg_class = 0; + p2p->pending_channel = 0; + } } void p2p_set_timeout(struct p2p_data *p2p, unsigned int sec, unsigned int usec) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Set timeout (state=%s): %u.%06u sec", + p2p_dbg(p2p, "Set timeout (state=%s): %u.%06u sec", p2p_state_txt(p2p->state), sec, usec); eloop_cancel_timeout(p2p_state_timeout, p2p, NULL); eloop_register_timeout(sec, usec, p2p_state_timeout, p2p, NULL); @@ -186,30 +221,40 @@ void p2p_set_timeout(struct p2p_data *p2p, unsigned int sec, unsigned int usec) void p2p_clear_timeout(struct p2p_data *p2p) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Clear timeout (state=%s)", - p2p_state_txt(p2p->state)); + p2p_dbg(p2p, "Clear timeout (state=%s)", p2p_state_txt(p2p->state)); eloop_cancel_timeout(p2p_state_timeout, p2p, NULL); } -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->wps_method = WPS_NOT_READY; + 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) { - 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); } @@ -220,19 +265,23 @@ static void p2p_listen_in_find(struct p2p_data *p2p, int dev_disc) int freq; struct wpabuf *ies; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Starting short listen state (state=%s)", + p2p_dbg(p2p, "Starting short listen state (state=%s)", p2p_state_txt(p2p->state)); - freq = p2p_channel_to_freq(p2p->cfg->country, p2p->cfg->reg_class, - p2p->cfg->channel); + if (p2p->pending_listen_freq) { + /* We have a pending p2p_listen request */ + p2p_dbg(p2p, "p2p_listen command pending already"); + return; + } + + freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel); if (freq < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unknown regulatory class/channel"); + p2p_dbg(p2p, "Unknown regulatory class/channel"); return; } - os_get_random((u8 *) &r, sizeof(r)); + if (os_get_random((u8 *) &r, sizeof(r)) < 0) + r = 0; tu = (r % ((p2p->max_disc_int - p2p->min_disc_int) + 1) + p2p->min_disc_int) * 100; if (p2p->max_disc_tu >= 0 && tu > (unsigned int) p2p->max_disc_tu) @@ -243,24 +292,22 @@ static void p2p_listen_in_find(struct p2p_data *p2p, int dev_disc) tu = p2p->cfg->max_listen * 1000 / 1024; if (tu == 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Skip listen state " - "since duration was 0 TU"); + p2p_dbg(p2p, "Skip listen state since duration was 0 TU"); p2p_set_timeout(p2p, 0, 0); return; } - p2p->pending_listen_freq = freq; - p2p->pending_listen_sec = 0; - p2p->pending_listen_usec = 1024 * tu; - ies = p2p_build_probe_resp_ies(p2p); if (ies == NULL) return; + p2p->pending_listen_freq = freq; + p2p->pending_listen_sec = 0; + p2p->pending_listen_usec = 1024 * tu; + if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, freq, 1024 * tu / 1000, ies) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to start listen mode"); + p2p_dbg(p2p, "Failed to start listen mode"); p2p->pending_listen_freq = 0; } wpabuf_free(ies); @@ -272,30 +319,29 @@ int p2p_listen(struct p2p_data *p2p, unsigned int timeout) int freq; struct wpabuf *ies; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Going to listen(only) state"); + p2p_dbg(p2p, "Going to listen(only) state"); - freq = p2p_channel_to_freq(p2p->cfg->country, p2p->cfg->reg_class, - p2p->cfg->channel); + if (p2p->pending_listen_freq) { + /* We have a pending p2p_listen request */ + p2p_dbg(p2p, "p2p_listen command pending already"); + return -1; + } + + freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel); if (freq < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unknown regulatory class/channel"); + p2p_dbg(p2p, "Unknown regulatory class/channel"); return -1; } - p2p->pending_listen_freq = freq; p2p->pending_listen_sec = timeout / 1000; p2p->pending_listen_usec = (timeout % 1000) * 1000; if (p2p->p2p_scan_running) { if (p2p->start_after_scan == P2P_AFTER_SCAN_CONNECT) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: p2p_scan running - connect is already " - "pending - skip listen"); + p2p_dbg(p2p, "p2p_scan running - connect is already pending - skip listen"); return 0; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: p2p_scan running - delay start of listen state"); + p2p_dbg(p2p, "p2p_scan running - delay start of listen state"); p2p->start_after_scan = P2P_AFTER_SCAN_LISTEN; return 0; } @@ -304,9 +350,10 @@ int p2p_listen(struct p2p_data *p2p, unsigned int timeout) if (ies == NULL) return -1; + p2p->pending_listen_freq = freq; + if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, freq, timeout, ies) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to start listen mode"); + p2p_dbg(p2p, "Failed to start listen mode"); p2p->pending_listen_freq = 0; wpabuf_free(ies); return -1; @@ -322,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; + } } @@ -384,13 +433,11 @@ static struct p2p_device * p2p_create_device(struct p2p_data *p2p, dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { count++; if (oldest == NULL || - os_time_before(&dev->last_seen, &oldest->last_seen)) + os_reltime_before(&dev->last_seen, &oldest->last_seen)) oldest = dev; } if (count + 1 > p2p->cfg->max_peers && oldest) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Remove oldest peer entry to make room for a new " - "peer"); + p2p_dbg(p2p, "Remove oldest peer entry to make room for a new peer"); dl_list_del(&oldest->list); p2p_device_free(p2p, oldest); } @@ -489,7 +536,7 @@ static int p2p_add_group_clients(struct p2p_data *p2p, const u8 *go_dev_addr, os_memcpy(dev->interface_addr, cli->p2p_interface_addr, ETH_ALEN); - os_get_time(&dev->last_seen); + os_get_reltime(&dev->last_seen); os_memcpy(dev->member_in_go_dev, go_dev_addr, ETH_ALEN); os_memcpy(dev->member_in_go_iface, go_interface_addr, ETH_ALEN); @@ -499,8 +546,8 @@ static int p2p_add_group_clients(struct p2p_data *p2p, const u8 *go_dev_addr, } -static void p2p_copy_wps_info(struct p2p_device *dev, int probe_req, - const struct p2p_message *msg) +static void p2p_copy_wps_info(struct p2p_data *p2p, struct p2p_device *dev, + int probe_req, const struct p2p_message *msg) { os_memcpy(dev->info.device_name, msg->device_name, sizeof(dev->info.device_name)); @@ -570,9 +617,77 @@ static void p2p_copy_wps_info(struct p2p_device *dev, int probe_req, } if (!probe_req) { - dev->info.config_methods = msg->config_methods ? + u16 new_config_methods; + new_config_methods = msg->config_methods ? msg->config_methods : msg->wps_config_methods; + if (new_config_methods && + dev->info.config_methods != new_config_methods) { + p2p_dbg(p2p, "Update peer " MACSTR + " config_methods 0x%x -> 0x%x", + MAC2STR(dev->info.p2p_device_addr), + dev->info.config_methods, + new_config_methods); + dev->info.config_methods = new_config_methods; + } + } +} + + +static void p2p_update_peer_vendor_elems(struct p2p_device *dev, const u8 *ies, + size_t ies_len) +{ + const u8 *pos, *end; + u8 id, len; + + wpabuf_free(dev->info.vendor_elems); + dev->info.vendor_elems = NULL; + + end = ies + ies_len; + + for (pos = ies; pos + 1 < end; pos += len) { + id = *pos++; + len = *pos++; + + if (pos + len > end) + break; + + if (id != WLAN_EID_VENDOR_SPECIFIC || len < 3) + continue; + + if (len >= 4) { + u32 type = WPA_GET_BE32(pos); + + if (type == WPA_IE_VENDOR_TYPE || + type == WMM_IE_VENDOR_TYPE || + type == WPS_IE_VENDOR_TYPE || + type == P2P_IE_VENDOR_TYPE || + type == WFD_IE_VENDOR_TYPE) + continue; + } + + /* Unknown vendor element - make raw IE data available */ + if (wpabuf_resize(&dev->info.vendor_elems, 2 + len) < 0) + break; + wpabuf_put_data(dev->info.vendor_elems, pos - 2, 2 + len); + } +} + + +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; } @@ -583,7 +698,7 @@ static void p2p_copy_wps_info(struct p2p_device *dev, int probe_req, * P2P Device Address or P2P Interface Address) * @level: Signal level (signal strength of the received frame from the peer) * @freq: Frequency on which the Beacon or Probe Response frame was received - * @age_ms: Age of the information in milliseconds + * @rx_time: Time when the result was received * @ies: IEs from the Beacon or Probe Response frame * @ies_len: Length of ies buffer in octets * @scan_res: Whether this was based on scan results @@ -595,19 +710,19 @@ static void p2p_copy_wps_info(struct p2p_device *dev, int probe_req, * Info attributes. */ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, - unsigned int age_ms, int level, const u8 *ies, + struct os_reltime *rx_time, int level, const u8 *ies, size_t ies_len, int scan_res) { struct p2p_device *dev; struct p2p_message msg; const u8 *p2p_dev_addr; + int wfd_changed; int i; - struct os_time time_now, time_tmp_age, entry_ts; + struct os_reltime time_now; os_memset(&msg, 0, sizeof(msg)); if (p2p_parse_ies(ies, ies_len, &msg)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to parse P2P IE for a device entry"); + p2p_dbg(p2p, "Failed to parse P2P IE for a device entry"); p2p_parse_free(&msg); return -1; } @@ -617,18 +732,15 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, else if (msg.device_id) p2p_dev_addr = msg.device_id; else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Ignore scan data without P2P Device Info or " - "P2P Device Id"); + p2p_dbg(p2p, "Ignore scan data without P2P Device Info or P2P Device Id"); p2p_parse_free(&msg); return -1; } if (!is_zero_ether_addr(p2p->peer_filter) && os_memcmp(p2p_dev_addr, p2p->peer_filter, ETH_ALEN) != 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Do not add peer " - "filter for " MACSTR " due to peer filter", - MAC2STR(p2p_dev_addr)); + p2p_dbg(p2p, "Do not add peer filter for " MACSTR + " due to peer filter", MAC2STR(p2p_dev_addr)); p2p_parse_free(&msg); return 0; } @@ -639,22 +751,27 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, return -1; } - os_get_time(&time_now); - time_tmp_age.sec = age_ms / 1000; - time_tmp_age.usec = (age_ms % 1000) * 1000; - os_time_sub(&time_now, &time_tmp_age, &entry_ts); + if (rx_time == NULL) { + os_get_reltime(&time_now); + rx_time = &time_now; + } /* * Update the device entry only if the new peer * entry is newer than the one previously stored. */ - if (dev->last_seen.usec > 0 && - os_time_before(&entry_ts, &dev->last_seen)) { + if (dev->last_seen.sec > 0 && + os_reltime_before(rx_time, &dev->last_seen)) { + p2p_dbg(p2p, "Do not update peer entry based on old frame (rx_time=%u.%06u last_seen=%u.%06u)", + (unsigned int) rx_time->sec, + (unsigned int) rx_time->usec, + (unsigned int) dev->last_seen.sec, + (unsigned int) dev->last_seen.usec); p2p_parse_free(&msg); return -1; } - os_memcpy(&dev->last_seen, &entry_ts, sizeof(struct os_time)); + os_memcpy(&dev->last_seen, rx_time, sizeof(struct os_reltime)); dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY); @@ -668,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; @@ -676,18 +799,15 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, else ds_freq = 2407 + *msg.ds_params * 5; if (freq != ds_freq) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Update Listen frequency based on DS " - "Parameter Set IE: %d -> %d MHz", + p2p_dbg(p2p, "Update Listen frequency based on DS Parameter Set IE: %d -> %d MHz", freq, ds_freq); freq = ds_freq; } } if (dev->listen_freq && dev->listen_freq != freq && scan_res) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Update Listen frequency based on scan " - "results (" MACSTR " %d -> %d MHz (DS param %d)", + p2p_dbg(p2p, "Update Listen frequency based on scan results (" + MACSTR " %d -> %d MHz (DS param %d)", MAC2STR(dev->info.p2p_device_addr), dev->listen_freq, freq, msg.ds_params ? *msg.ds_params : -1); } @@ -698,7 +818,7 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, } dev->info.level = level; - p2p_copy_wps_info(dev, 0, &msg); + p2p_copy_wps_info(p2p, dev, 0, &msg); for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { wpabuf_free(dev->info.wps_vendor_ext[i]); @@ -714,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); @@ -726,17 +848,39 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, p2p_parse_free(&msg); - if (p2p_pending_sd_req(p2p, dev)) - dev->flags |= P2P_DEV_SD_SCHEDULE; + 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; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Peer found with Listen frequency %d MHz", freq); + p2p_dbg(p2p, "Peer found with Listen frequency %d MHz (rx_time=%u.%06u)", + freq, (unsigned int) rx_time->sec, + (unsigned int) rx_time->usec); if (dev->flags & P2P_DEV_USER_REJECTED) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Do not report rejected device"); + p2p_dbg(p2p, "Do not report rejected device"); + return 0; + } + + if (dev->info.config_methods == 0 && + (freq == 2412 || freq == 2437 || freq == 2462)) { + /* + * If we have only seen a Beacon frame from a GO, we do not yet + * know what WPS config methods it supports. Since some + * applications use config_methods value from P2P-DEVICE-FOUND + * events, postpone reporting this peer until we've fully + * discovered its capabilities. + * + * At least for now, do this only if the peer was detected on + * one of the social channels since that peer can be easily be + * found again and there are no limitations of having to use + * passive scan on this channels, so this can be done through + * Probe Response frame that includes the config_methods + * information. + */ + p2p_dbg(p2p, "Do not report peer " MACSTR + " with unknown config methods", MAC2STR(addr)); return 0; } @@ -744,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; } @@ -756,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; @@ -777,6 +923,9 @@ 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); } @@ -824,9 +973,8 @@ static int p2p_get_next_prog_freq(struct p2p_data *p2p) channel = c->reg_class[cl].channel[ch]; } - freq = p2p_channel_to_freq(p2p->cfg->country, reg_class, channel); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Next progressive search " - "channel: reg_class %u channel %u -> %d MHz", + freq = p2p_channel_to_freq(reg_class, channel); + p2p_dbg(p2p, "Next progressive search channel: reg_class %u channel %u -> %d MHz", reg_class, channel, freq); p2p->last_prog_scan_class = reg_class; p2p->last_prog_scan_chan = channel; @@ -845,9 +993,7 @@ static void p2p_search(struct p2p_data *p2p) int res; if (p2p->drv_in_listen) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Driver is still " - "in Listen state - wait for it to end before " - "continuing"); + p2p_dbg(p2p, "Driver is still in Listen state - wait for it to end before continuing"); return; } p2p->cfg->stop_listen(p2p->cfg->cb_ctx); @@ -855,31 +1001,18 @@ static void p2p_search(struct p2p_data *p2p) if (p2p->find_type == P2P_FIND_PROGRESSIVE && (freq = p2p_get_next_prog_freq(p2p)) > 0) { type = P2P_SCAN_SOCIAL_PLUS_ONE; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search " - "(+ freq %u)", freq); + p2p_dbg(p2p, "Starting search (+ freq %u)", freq); } else { type = P2P_SCAN_SOCIAL; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting search"); + p2p_dbg(p2p, "Starting search"); } res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, type, freq, p2p->num_req_dev_types, p2p->req_dev_types, p2p->find_dev_id, pw_id); if (res < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Scan request failed"); + p2p_dbg(p2p, "Scan request schedule failed"); p2p_continue_find(p2p); - } else if (res == 1) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Could not start " - "p2p_scan at this point - will try again after " - "previous scan completes"); - p2p_set_state(p2p, P2P_CONTINUE_SEARCH_WHEN_READY); - } else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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); } } @@ -887,21 +1020,35 @@ static void p2p_search(struct p2p_data *p2p) static void p2p_find_timeout(void *eloop_ctx, void *timeout_ctx) { struct p2p_data *p2p = eloop_ctx; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Find timeout -> stop"); + p2p_dbg(p2p, "Find timeout -> stop"); p2p_stop_find(p2p); } +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; enum p2p_after_scan op; if (p2p->after_scan_tx) { - /* TODO: schedule p2p_run_after_scan to be called from TX - * status callback(?) */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send pending " - "Action frame at p2p_scan completion"); + p2p->after_scan_tx_in_progress = 1; + p2p_dbg(p2p, "Send pending Action frame at p2p_scan completion"); p2p->cfg->send_action(p2p->cfg->cb_ctx, p2p->after_scan_tx->freq, p2p->after_scan_tx->dst, @@ -921,19 +1068,16 @@ static int p2p_run_after_scan(struct p2p_data *p2p) case P2P_AFTER_SCAN_NOTHING: break; case P2P_AFTER_SCAN_LISTEN: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Start previously " - "requested Listen state"); + p2p_dbg(p2p, "Start previously requested Listen state"); p2p_listen(p2p, p2p->pending_listen_sec * 1000 + p2p->pending_listen_usec / 1000); return 1; case P2P_AFTER_SCAN_CONNECT: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Start previously " - "requested connect with " MACSTR, + p2p_dbg(p2p, "Start previously requested connect with " MACSTR, MAC2STR(p2p->after_scan_peer)); dev = p2p_get_device(p2p, p2p->after_scan_peer); if (dev == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer not " - "known anymore"); + p2p_dbg(p2p, "Peer not known anymore"); break; } p2p_connect_send(p2p, dev); @@ -948,8 +1092,7 @@ static void p2p_scan_timeout(void *eloop_ctx, void *timeout_ctx) { struct p2p_data *p2p = eloop_ctx; int running; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan timeout " - "(running=%d)", p2p->p2p_scan_running); + p2p_dbg(p2p, "p2p_scan timeout (running=%d)", p2p->p2p_scan_running); running = p2p->p2p_scan_running; /* Make sure we recover from missed scan results callback */ p2p->p2p_scan_running = 0; @@ -967,18 +1110,51 @@ 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; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting find (type=%d)", - type); + p2p_dbg(p2p, "Starting find (type=%d)", type); + os_get_reltime(&p2p->find_start); if (p2p->p2p_scan_running) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan is " - "already running"); + p2p_dbg(p2p, "p2p_scan is already running"); } p2p_free_req_dev_types(p2p); @@ -998,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); @@ -1013,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, @@ -1029,22 +1259,12 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, return -1; } - if (res == 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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 (res == 1) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Could not start " - "p2p_scan at this point - will try again after " - "previous scan completes"); - res = 0; - p2p_set_state(p2p, P2P_SEARCH_WHEN_READY); - eloop_cancel_timeout(p2p_find_timeout, p2p, NULL); - } else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Failed to start " - "p2p_scan"); + 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 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); } @@ -1053,47 +1273,33 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, } -int p2p_other_scan_completed(struct p2p_data *p2p) -{ - if (p2p->state == P2P_CONTINUE_SEARCH_WHEN_READY) { - p2p_set_state(p2p, P2P_SEARCH); - p2p_search(p2p); - return 1; - } - if (p2p->state != P2P_SEARCH_WHEN_READY) - return 0; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting pending P2P find " - "now that previous scan was completed"); - if (p2p_find(p2p, p2p->last_p2p_find_timeout, p2p->find_type, - p2p->num_req_dev_types, p2p->req_dev_types, - p2p->find_dev_id, p2p->search_delay) < 0) - return 0; - return 1; -} - - void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Stopping find"); + p2p_dbg(p2p, "Stopping find"); eloop_cancel_timeout(p2p_find_timeout, p2p, NULL); p2p_clear_timeout(p2p); - if (p2p->state == P2P_SEARCH) - wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, P2P_EVENT_FIND_STOPPED); + 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; + if (p2p->go_neg_peer) + p2p->go_neg_peer->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE; p2p->go_neg_peer = NULL; p2p->sd_peer = NULL; p2p->invite_peer = NULL; p2p_stop_listen_for_freq(p2p, freq); + p2p->send_action_in_progress = 0; } void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq) { if (freq > 0 && p2p->drv_in_listen == freq && p2p->in_listen) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Skip stop_listen " - "since we are on correct channel for response"); + p2p_dbg(p2p, "Skip stop_listen since we are on correct channel for response"); return; } if (p2p->in_listen) { @@ -1106,38 +1312,51 @@ void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq) * when the operation gets canceled, so clear the internal * variable that is tracking driver state. */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Clear " - "drv_in_listen (%d)", p2p->drv_in_listen); + p2p_dbg(p2p, "Clear drv_in_listen (%d)", p2p->drv_in_listen); p2p->drv_in_listen = 0; } p2p->cfg->stop_listen(p2p->cfg->cb_ctx); } +void p2p_stop_listen(struct p2p_data *p2p) +{ + if (p2p->state != P2P_LISTEN_ONLY) { + p2p_dbg(p2p, "Skip stop_listen since not in listen_only state."); + return; + } + + p2p_stop_listen_for_freq(p2p, 0); + p2p_set_state(p2p, P2P_IDLE); +} + + void p2p_stop_find(struct p2p_data *p2p) { + p2p->pending_listen_freq = 0; p2p_stop_find_for_freq(p2p, 0); } static int p2p_prepare_channel_pref(struct p2p_data *p2p, unsigned int force_freq, - unsigned int pref_freq) + unsigned int pref_freq, int go) { u8 op_class, op_channel; unsigned int freq = force_freq ? force_freq : pref_freq; - if (p2p_freq_to_channel(p2p->cfg->country, freq, - &op_class, &op_channel) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported frequency %u MHz", freq); + p2p_dbg(p2p, "Prepare channel pref - force_freq=%u pref_freq=%u go=%d", + force_freq, pref_freq, go); + if (p2p_freq_to_channel(freq, &op_class, &op_channel) < 0) { + p2p_dbg(p2p, "Unsupported frequency %u MHz", freq); return -1; } - if (!p2p_channels_includes(&p2p->cfg->channels, op_class, op_channel)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Frequency %u MHz (oper_class %u channel %u) not " - "allowed for P2P", freq, op_class, op_channel); + if (!p2p_channels_includes(&p2p->cfg->channels, op_class, op_channel) && + (go || !p2p_channels_includes(&p2p->cfg->cli_channels, op_class, + op_channel))) { + p2p_dbg(p2p, "Frequency %u MHz (oper_class %u channel %u) not allowed for P2P", + freq, op_class, op_channel); return -1; } @@ -1161,34 +1380,74 @@ static int p2p_prepare_channel_pref(struct p2p_data *p2p, static void p2p_prepare_channel_best(struct p2p_data *p2p) { u8 op_class, op_channel; + const int op_classes_5ghz[] = { 124, 115, 0 }; + const int op_classes_ht40[] = { 126, 127, 116, 117, 0 }; + const int op_classes_vht[] = { 128, 0 }; + + p2p_dbg(p2p, "Prepare channel best"); if (!p2p->cfg->cfg_op_channel && p2p->best_freq_overall > 0 && p2p_supported_freq(p2p, p2p->best_freq_overall) && - p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_overall, - &op_class, &op_channel) == 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Select best " - "overall channel as operating channel preference"); + p2p_freq_to_channel(p2p->best_freq_overall, &op_class, &op_channel) + == 0) { + p2p_dbg(p2p, "Select best overall channel as operating channel preference"); p2p->op_reg_class = op_class; p2p->op_channel = op_channel; } else if (!p2p->cfg->cfg_op_channel && p2p->best_freq_5 > 0 && p2p_supported_freq(p2p, p2p->best_freq_5) && - p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_5, - &op_class, &op_channel) == 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Select best 5 GHz " - "channel as operating channel preference"); + p2p_freq_to_channel(p2p->best_freq_5, &op_class, &op_channel) + == 0) { + p2p_dbg(p2p, "Select best 5 GHz channel as operating channel preference"); p2p->op_reg_class = op_class; p2p->op_channel = op_channel; } else if (!p2p->cfg->cfg_op_channel && p2p->best_freq_24 > 0 && p2p_supported_freq(p2p, p2p->best_freq_24) && - p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_24, - &op_class, &op_channel) == 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Select best 2.4 " - "GHz channel as operating channel preference"); + p2p_freq_to_channel(p2p->best_freq_24, &op_class, + &op_channel) == 0) { + p2p_dbg(p2p, "Select best 2.4 GHz channel as operating channel preference"); p2p->op_reg_class = op_class; p2p->op_channel = op_channel; - } else { + } else if (p2p->cfg->num_pref_chan > 0 && + p2p_channels_includes(&p2p->cfg->channels, + p2p->cfg->pref_chan[0].op_class, + p2p->cfg->pref_chan[0].chan)) { + p2p_dbg(p2p, "Select first pref_chan entry as operating channel preference"); + p2p->op_reg_class = p2p->cfg->pref_chan[0].op_class; + p2p->op_channel = p2p->cfg->pref_chan[0].chan; + } else if (p2p_channel_select(&p2p->cfg->channels, op_classes_vht, + &p2p->op_reg_class, &p2p->op_channel) == + 0) { + p2p_dbg(p2p, "Select possible VHT channel (op_class %u channel %u) as operating channel preference", + p2p->op_reg_class, p2p->op_channel); + } else if (p2p_channel_select(&p2p->cfg->channels, op_classes_ht40, + &p2p->op_reg_class, &p2p->op_channel) == + 0) { + p2p_dbg(p2p, "Select possible HT40 channel (op_class %u channel %u) as operating channel preference", + p2p->op_reg_class, p2p->op_channel); + } else if (p2p_channel_select(&p2p->cfg->channels, op_classes_5ghz, + &p2p->op_reg_class, &p2p->op_channel) == + 0) { + p2p_dbg(p2p, "Select possible 5 GHz channel (op_class %u channel %u) as operating channel preference", + p2p->op_reg_class, p2p->op_channel); + } else if (p2p_channels_includes(&p2p->cfg->channels, + p2p->cfg->op_reg_class, + p2p->cfg->op_channel)) { + p2p_dbg(p2p, "Select pre-configured channel as operating channel preference"); p2p->op_reg_class = p2p->cfg->op_reg_class; p2p->op_channel = p2p->cfg->op_channel; + } else if (p2p_channel_random_social(&p2p->cfg->channels, + &p2p->op_reg_class, + &p2p->op_channel) == 0) { + p2p_dbg(p2p, "Select random available social channel (op_class %u channel %u) as operating channel preference", + p2p->op_reg_class, p2p->op_channel); + } else { + /* Select any random available channel from the first available + * operating class */ + p2p_channel_select(&p2p->cfg->channels, NULL, + &p2p->op_reg_class, + &p2p->op_channel); + p2p_dbg(p2p, "Select random available channel %d from operating class %d as operating channel preference", + p2p->op_channel, p2p->op_reg_class); } os_memcpy(&p2p->channels, &p2p->cfg->channels, @@ -1202,6 +1461,7 @@ static void p2p_prepare_channel_best(struct p2p_data *p2p) * @dev: Selected peer device * @force_freq: Forced frequency in MHz or 0 if not forced * @pref_freq: Preferred frequency in MHz or 0 if no preference + * @go: Whether the local end will be forced to be GO * Returns: 0 on success, -1 on failure (channel not supported for P2P) * * This function is used to do initial operating channel selection for GO @@ -1209,18 +1469,27 @@ static void p2p_prepare_channel_best(struct p2p_data *p2p) * may be further optimized in p2p_reselect_channel() once the peer information * is available. */ -static int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev, - unsigned int force_freq, unsigned int pref_freq) +int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev, + unsigned int force_freq, unsigned int pref_freq, int go) { + p2p_dbg(p2p, "Prepare channel - force_freq=%u pref_freq=%u go=%d", + force_freq, pref_freq, go); if (force_freq || pref_freq) { - if (p2p_prepare_channel_pref(p2p, force_freq, pref_freq) < 0) + if (p2p_prepare_channel_pref(p2p, force_freq, pref_freq, go) < + 0) return -1; } else { p2p_prepare_channel_best(p2p); } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Own preference for operation channel: " - "Operating Class %u Channel %u%s", + p2p_channels_dump(p2p, "prepared channels", &p2p->channels); + if (go) + p2p_channels_remove_freqs(&p2p->channels, &p2p->no_go_freq); + else if (!force_freq) + 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", p2p->op_reg_class, p2p->op_channel, force_freq ? " (forced)" : ""); @@ -1258,40 +1527,38 @@ int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr, int go_intent, const u8 *own_interface_addr, unsigned int force_freq, int persistent_group, const u8 *force_ssid, size_t force_ssid_len, - int pd_before_go_neg, unsigned int pref_freq) + int pd_before_go_neg, unsigned int pref_freq, u16 oob_pw_id) { struct p2p_device *dev; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Request to start group negotiation - peer=" MACSTR + p2p_dbg(p2p, "Request to start group negotiation - peer=" MACSTR " GO Intent=%d Intended Interface Address=" MACSTR - " wps_method=%d persistent_group=%d pd_before_go_neg=%d", + " wps_method=%d persistent_group=%d pd_before_go_neg=%d " + "oob_pw_id=%u", MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr), - wps_method, persistent_group, pd_before_go_neg); + wps_method, persistent_group, pd_before_go_neg, oob_pw_id); dev = p2p_get_device(p2p, peer_addr); if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Cannot connect to unknown P2P Device " MACSTR, + p2p_dbg(p2p, "Cannot connect to unknown P2P Device " MACSTR, MAC2STR(peer_addr)); return -1; } - if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq) < 0) + if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq, + go_intent == 15) < 0) return -1; if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) { if (!(dev->info.dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Cannot connect to P2P Device " MACSTR + p2p_dbg(p2p, "Cannot connect to P2P Device " MACSTR " that is in a group and is not discoverable", MAC2STR(peer_addr)); return -1; } if (dev->oper_freq <= 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Cannot connect to P2P Device " MACSTR + p2p_dbg(p2p, "Cannot connect to P2P Device " MACSTR " with incomplete information", MAC2STR(peer_addr)); return -1; @@ -1319,8 +1586,18 @@ int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr, dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM; if (pd_before_go_neg) dev->flags |= P2P_DEV_PD_BEFORE_GO_NEG; - else + else { dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG; + /* + * Assign dialog token and tie breaker here to use the same + * values in each retry within the same GO Negotiation exchange. + */ + dev->dialog_token++; + if (dev->dialog_token == 0) + dev->dialog_token = 1; + dev->tie_breaker = p2p->next_tie_breaker; + p2p->next_tie_breaker = !p2p->next_tie_breaker; + } dev->connect_reqs = 0; dev->go_neg_req_sent = 0; dev->go_state = UNKNOWN_GO; @@ -1337,19 +1614,17 @@ int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr, * new GO Negotiation, e.g., when the pending frame was from a * previous attempt at starting a GO Negotiation. */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Dropped " - "previous pending Action frame TX that was waiting " - "for p2p_scan completion"); + p2p_dbg(p2p, "Dropped previous pending Action frame TX that was waiting for p2p_scan completion"); os_free(p2p->after_scan_tx); p2p->after_scan_tx = NULL; } dev->wps_method = wps_method; + dev->oob_pw_id = oob_pw_id; dev->status = P2P_SC_SUCCESS; if (p2p->p2p_scan_running) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: p2p_scan running - delay connect send"); + p2p_dbg(p2p, "p2p_scan running - delay connect send"); p2p->start_after_scan = P2P_AFTER_SCAN_CONNECT; os_memcpy(p2p->after_scan_peer, peer_addr, ETH_ALEN); return 0; @@ -1365,26 +1640,25 @@ int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr, int go_intent, const u8 *own_interface_addr, unsigned int force_freq, int persistent_group, const u8 *force_ssid, size_t force_ssid_len, - unsigned int pref_freq) + unsigned int pref_freq, u16 oob_pw_id) { struct p2p_device *dev; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Request to authorize group negotiation - peer=" MACSTR + p2p_dbg(p2p, "Request to authorize group negotiation - peer=" MACSTR " GO Intent=%d Intended Interface Address=" MACSTR - " wps_method=%d persistent_group=%d", + " wps_method=%d persistent_group=%d oob_pw_id=%u", MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr), - wps_method, persistent_group); + wps_method, persistent_group, oob_pw_id); dev = p2p_get_device(p2p, peer_addr); if (dev == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Cannot authorize unknown P2P Device " MACSTR, + p2p_dbg(p2p, "Cannot authorize unknown P2P Device " MACSTR, MAC2STR(peer_addr)); return -1; } - if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq) < 0) + if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq, go_intent == + 15) < 0) return -1; p2p->ssid_set = 0; @@ -1405,6 +1679,7 @@ int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr, os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN); dev->wps_method = wps_method; + dev->oob_pw_id = oob_pw_id; dev->status = P2P_SC_SUCCESS; return 0; @@ -1414,18 +1689,16 @@ int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr, void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr, struct p2p_device *dev, struct p2p_message *msg) { - os_get_time(&dev->last_seen); + os_get_reltime(&dev->last_seen); - p2p_copy_wps_info(dev, 0, msg); + p2p_copy_wps_info(p2p, dev, 0, msg); if (msg->listen_channel) { int freq; - freq = p2p_channel_to_freq((char *) msg->listen_channel, - msg->listen_channel[3], + freq = p2p_channel_to_freq(msg->listen_channel[3], msg->listen_channel[4]); if (freq < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unknown peer Listen channel: " + p2p_dbg(p2p, "Unknown peer Listen channel: " "country=%c%c(0x%02x) reg_class=%u channel=%u", msg->listen_channel[0], msg->listen_channel[1], @@ -1433,8 +1706,8 @@ void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr, msg->listen_channel[3], msg->listen_channel[4]); } else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Update " - "peer " MACSTR " Listen channel: %u -> %u MHz", + p2p_dbg(p2p, "Update peer " MACSTR + " Listen channel: %u -> %u MHz", MAC2STR(dev->info.p2p_device_addr), dev->listen_freq, freq); dev->listen_freq = freq; @@ -1448,12 +1721,9 @@ void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr, if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) { dev->flags &= ~P2P_DEV_PROBE_REQ_ONLY; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Completed device entry based on data from " - "GO Negotiation Request"); + p2p_dbg(p2p, "Completed device entry based on data from GO Negotiation Request"); } else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Created device entry based on GO Neg Req: " + p2p_dbg(p2p, "Created device entry based on GO Neg Req: " MACSTR " dev_capab=0x%x group_capab=0x%x name='%s' " "listen_freq=%d", MAC2STR(dev->info.p2p_device_addr), @@ -1464,8 +1734,7 @@ void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr, dev->flags &= ~P2P_DEV_GROUP_CLIENT_ONLY; if (dev->flags & P2P_DEV_USER_REJECTED) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Do not report rejected device"); + p2p_dbg(p2p, "Do not report rejected device"); return; } @@ -1487,8 +1756,15 @@ 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); - p2p_random(params->passphrase, 8); + 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; } @@ -1498,13 +1774,9 @@ 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; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: GO Negotiation with " MACSTR " completed (%s will be " - "GO)", MAC2STR(peer->info.p2p_device_addr), - go ? "local end" : "peer"); + p2p_dbg(p2p, "GO Negotiation with " MACSTR " completed (%s will be GO)", + MAC2STR(peer->info.p2p_device_addr), go ? "local end" : "peer"); os_memset(&res, 0, sizeof(res)); res.role_go = go; @@ -1520,12 +1792,11 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer) if (go) { /* Setup AP mode for WPS provisioning */ - res.freq = p2p_channel_to_freq(p2p->cfg->country, - p2p->op_reg_class, + res.freq = p2p_channel_to_freq(p2p->op_reg_class, p2p->op_channel); os_memcpy(res.ssid, p2p->ssid, p2p->ssid_len); res.ssid_len = p2p->ssid_len; - p2p_random(res.passphrase, 8); + p2p_random(res.passphrase, p2p->cfg->passphrase_len); } else { res.freq = peer->oper_freq; if (p2p->ssid_len) { @@ -1534,31 +1805,28 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer) } } + p2p_channels_dump(p2p, "own channels", &p2p->channels); + p2p_channels_dump(p2p, "peer channels", &peer->channels); p2p_channels_intersect(&p2p->channels, &peer->channels, &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(peer->country, c->reg_class, - c->channel[j]); - if (freq < 0) - continue; - res.freq_list[freqs++] = freq; - } + if (go) { + p2p_channels_remove_freqs(&intersection, &p2p->no_go_freq); + p2p_channels_dump(p2p, "intersection after no-GO removal", + &intersection); } + p2p_channels_to_freqs(&intersection, res.freq_list, + P2P_MAX_CHANNELS); + res.peer_config_timeout = go ? peer->client_timeout : peer->go_timeout; p2p_clear_timeout(p2p); p2p->ssid_set = 0; peer->go_neg_req_sent = 0; peer->wps_method = WPS_NOT_READY; + peer->oob_pw_id = 0; + wpabuf_free(peer->go_neg_conf); + peer->go_neg_conf = NULL; p2p_set_state(p2p, P2P_PROVISIONING); p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res); @@ -1568,8 +1836,7 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer) static void p2p_rx_p2p_action(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: RX P2P Public Action from " MACSTR, MAC2STR(sa)); + p2p_dbg(p2p, "RX P2P Public Action from " MACSTR, MAC2STR(sa)); wpa_hexdump(MSG_MSGDUMP, "P2P: P2P Public Action contents", data, len); if (len < 1) @@ -1605,8 +1872,7 @@ static void p2p_rx_p2p_action(struct p2p_data *p2p, const u8 *sa, p2p_process_dev_disc_resp(p2p, sa, data + 1, len - 1); break; default: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported P2P Public Action frame type %d", + p2p_dbg(p2p, "Unsupported P2P Public Action frame type %d", data[0]); break; } @@ -1624,20 +1890,15 @@ static void p2p_rx_action_public(struct p2p_data *p2p, const u8 *da, case WLAN_PA_VENDOR_SPECIFIC: data++; len--; - if (len < 3) + if (len < 4) return; - if (WPA_GET_BE24(data) != OUI_WFA) + if (WPA_GET_BE32(data) != P2P_IE_VENDOR_TYPE) return; - data += 3; - len -= 3; - if (len < 1) - return; - - if (*data != P2P_OUI_TYPE) - return; + data += 4; + len -= 4; - p2p_rx_p2p_action(p2p, sa, data + 1, len - 1, freq); + p2p_rx_p2p_action(p2p, sa, data, len, freq); break; case WLAN_PA_GAS_INITIAL_REQ: p2p_rx_gas_initial_req(p2p, sa, data + 1, len - 1, freq); @@ -1670,27 +1931,20 @@ void p2p_rx_action(struct p2p_data *p2p, const u8 *da, const u8 *sa, if (len < 4) return; - if (WPA_GET_BE24(data) != OUI_WFA) - return; - data += 3; - len -= 3; - - if (*data != P2P_OUI_TYPE) + if (WPA_GET_BE32(data) != P2P_IE_VENDOR_TYPE) return; - data++; - len--; + data += 4; + len -= 4; /* P2P action frame */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: RX P2P Action from " MACSTR, MAC2STR(sa)); + p2p_dbg(p2p, "RX P2P Action from " MACSTR, MAC2STR(sa)); wpa_hexdump(MSG_MSGDUMP, "P2P: P2P Action contents", data, len); if (len < 1) return; switch (data[0]) { case P2P_NOA: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received P2P Action - Notice of Absence"); + p2p_dbg(p2p, "Received P2P Action - Notice of Absence"); /* TODO */ break; case P2P_PRESENCE_REQ: @@ -1703,8 +1957,7 @@ void p2p_rx_action(struct p2p_data *p2p, const u8 *da, const u8 *sa, p2p_process_go_disc_req(p2p, da, sa, data + 1, len - 1, freq); break; default: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received P2P Action - unknown type %u", data[0]); + p2p_dbg(p2p, "Received P2P Action - unknown type %u", data[0]); break; } } @@ -1715,8 +1968,17 @@ static void p2p_go_neg_start(void *eloop_ctx, void *timeout_ctx) struct p2p_data *p2p = eloop_ctx; if (p2p->go_neg_peer == NULL) return; + if (p2p->pending_listen_freq) { + p2p_dbg(p2p, "Clear pending_listen_freq for p2p_go_neg_start"); + p2p->pending_listen_freq = 0; + } p2p->cfg->stop_listen(p2p->cfg->cb_ctx); p2p->go_neg_peer->status = P2P_SC_SUCCESS; + /* + * Set new timeout to make sure a previously set one does not expire + * too quickly while waiting for the GO Negotiation to complete. + */ + p2p_set_timeout(p2p, 0, 500000); p2p_connect_send(p2p, p2p->go_neg_peer); } @@ -1726,8 +1988,13 @@ static void p2p_invite_start(void *eloop_ctx, void *timeout_ctx) struct p2p_data *p2p = eloop_ctx; if (p2p->invite_peer == NULL) return; + if (p2p->pending_listen_freq) { + p2p_dbg(p2p, "Clear pending_listen_freq for p2p_invite_start"); + p2p->pending_listen_freq = 0; + } p2p->cfg->stop_listen(p2p->cfg->cb_ctx); - p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr); + p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr, + p2p->invite_dev_pw_id); } @@ -1760,7 +2027,7 @@ static void p2p_add_dev_from_probe_req(struct p2p_data *p2p, const u8 *addr, if (dev) { if (dev->country[0] == 0 && msg.listen_channel) os_memcpy(dev->country, msg.listen_channel, 3); - os_get_time(&dev->last_seen); + os_get_reltime(&dev->last_seen); p2p_parse_free(&msg); return; /* already known */ } @@ -1771,17 +2038,16 @@ static void p2p_add_dev_from_probe_req(struct p2p_data *p2p, const u8 *addr, return; } - os_get_time(&dev->last_seen); + os_get_reltime(&dev->last_seen); dev->flags |= P2P_DEV_PROBE_REQ_ONLY; if (msg.listen_channel) { os_memcpy(dev->country, msg.listen_channel, 3); - dev->listen_freq = p2p_channel_to_freq(dev->country, - msg.listen_channel[3], + dev->listen_freq = p2p_channel_to_freq(msg.listen_channel[3], msg.listen_channel[4]); } - p2p_copy_wps_info(dev, 1, &msg); + p2p_copy_wps_info(p2p, dev, 1, &msg); if (msg.wfd_subelems) { wpabuf_free(dev->info.wfd_subelems); @@ -1790,8 +2056,7 @@ static void p2p_add_dev_from_probe_req(struct p2p_data *p2p, const u8 *addr, p2p_parse_free(&msg); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Created device entry based on Probe Req: " MACSTR + p2p_dbg(p2p, "Created device entry based on Probe Req: " MACSTR " dev_capab=0x%x group_capab=0x%x name='%s' listen_freq=%d", MAC2STR(dev->info.p2p_device_addr), dev->info.dev_capab, dev->info.group_capab, dev->info.device_name, @@ -1807,7 +2072,7 @@ struct p2p_device * p2p_add_dev_from_go_neg_req(struct p2p_data *p2p, dev = p2p_get_device(p2p, addr); if (dev) { - os_get_time(&dev->last_seen); + os_get_reltime(&dev->last_seen); return dev; /* already known */ } @@ -1870,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; @@ -1893,6 +2159,12 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p) extra = wpabuf_len(p2p->wfd_ie_probe_resp); #endif /* CONFIG_WIFI_DISPLAY */ + 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; @@ -1902,13 +2174,21 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p) pw_id = p2p_wps_method_pw_id(p2p->go_neg_peer->wps_method); } - p2p_build_wps_ie(p2p, buf, pw_id, 1); + if (p2p_build_wps_ie(p2p, buf, pw_id, 1) < 0) { + p2p_dbg(p2p, "Failed to build WPS IE for Probe Response"); + wpabuf_free(buf); + return NULL; + } #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_probe_resp) wpabuf_put_buf(buf, p2p->wfd_ie_probe_resp); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P]) + wpabuf_put_buf(buf, + p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P]); + /* P2P IE */ len = p2p_buf_add_ie_hdr(buf); p2p_buf_add_capability(buf, p2p->dev_capab & @@ -1919,40 +2199,35 @@ 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 is_11b(u8 rate) +static int p2p_service_find_asp(struct p2p_data *p2p, const u8 *hash) { - return rate == 0x02 || rate == 0x04 || rate == 0x0b || rate == 0x16; -} + struct p2ps_advertisement *adv_data; + p2p_dbg(p2p, "ASP find - ASP list: %p", p2p->p2ps_adv_list); -static int supp_rates_11b_only(struct ieee802_11_elems *elems) -{ - int num_11b = 0, num_others = 0; - int i; + /* 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; - if (elems->supp_rates == NULL && elems->ext_supp_rates == NULL) - return 0; - - for (i = 0; elems->supp_rates && i < elems->supp_rates_len; i++) { - if (is_11b(elems->supp_rates[i])) - num_11b++; - else - num_others++; - } - - for (i = 0; elems->ext_supp_rates && i < elems->ext_supp_rates_len; - i++) { - if (is_11b(elems->ext_supp_rates[i])) - num_11b++; - else - num_others++; + 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 num_11b > 0 && num_others == 0; + return 0; } @@ -1966,19 +2241,16 @@ 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 */ - return P2P_PREQ_NOT_LISTEN; - } - if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) == ParseFailed) { /* Ignore invalid Probe Request frames */ + p2p_dbg(p2p, "Could not parse Probe Request frame - ignore it"); return P2P_PREQ_MALFORMED; } if (elems.p2p == NULL) { /* not a P2P probe - ignore it */ + p2p_dbg(p2p, "Not a P2P probe - ignore it"); return P2P_PREQ_NOT_P2P; } @@ -1986,11 +2258,15 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, os_memcmp(dst, p2p->cfg->dev_addr, ETH_ALEN) != 0) { /* Not sent to the broadcast address or our P2P Device Address */ + p2p_dbg(p2p, "Probe Req DA " MACSTR " not ours - ignore it", + MAC2STR(dst)); return P2P_PREQ_NOT_PROCESSED; } if (bssid && !is_broadcast_ether_addr(bssid)) { /* Not sent to the Wildcard BSSID */ + p2p_dbg(p2p, "Probe Req BSSID " MACSTR " not wildcard - ignore it", + MAC2STR(bssid)); return P2P_PREQ_NOT_PROCESSED; } @@ -1998,23 +2274,86 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, os_memcmp(elems.ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) != 0) { /* not using P2P Wildcard SSID - ignore */ + p2p_dbg(p2p, "Probe Req not using P2P Wildcard SSID - ignore it"); return P2P_PREQ_NOT_PROCESSED; } if (supp_rates_11b_only(&elems)) { /* Indicates support for 11b rates only */ + p2p_dbg(p2p, "Probe Req with 11b rates only supported - ignore it"); return P2P_PREQ_NOT_P2P; } os_memset(&msg, 0, sizeof(msg)); if (p2p_parse_ies(ie, ie_len, &msg) < 0) { /* Could not parse P2P attributes */ + p2p_dbg(p2p, "Could not parse P2P attributes in Probe Req - ignore it"); 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 */ + p2p_dbg(p2p, "Probe Req requested Device ID " MACSTR " did not match - ignore it", + MAC2STR(msg.device_id)); p2p_parse_free(&msg); return P2P_PREQ_NOT_PROCESSED; } @@ -2023,6 +2362,7 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, if (msg.wps_attributes && !p2p_match_dev_type(p2p, msg.wps_attributes)) { /* No match with Requested Device Type */ + p2p_dbg(p2p, "Probe Req requestred Device Type did not match - ignore it"); p2p_parse_free(&msg); return P2P_PREQ_NOT_PROCESSED; } @@ -2030,11 +2370,11 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, if (!p2p->cfg->send_probe_resp) { /* Response generated elsewhere */ + p2p_dbg(p2p, "Probe Resp generated elsewhere - do not generate additional response"); return P2P_PREQ_NOT_PROCESSED; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Reply to P2P Probe Request in Listen state"); + p2p_dbg(p2p, "Reply to P2P Probe Request in Listen state"); /* * We do not really have a specific BSS that this frame is advertising, @@ -2106,27 +2446,28 @@ 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 && os_memcmp(addr, p2p->go_neg_peer->info.p2p_device_addr, ETH_ALEN) - == 0) { + == 0 && + !(p2p->go_neg_peer->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) { /* Received a Probe Request from GO Negotiation peer */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Found GO Negotiation peer - try to start GO " - "negotiation from timeout"); + p2p_dbg(p2p, "Found GO Negotiation peer - try to start GO negotiation from timeout"); + eloop_cancel_timeout(p2p_go_neg_start, p2p, NULL); eloop_register_timeout(0, 0, p2p_go_neg_start, p2p, NULL); return P2P_PREQ_PROCESSED; } if ((p2p->state == P2P_INVITE || p2p->state == P2P_INVITE_LISTEN) && p2p->invite_peer && + (p2p->invite_peer->flags & P2P_DEV_WAIT_INV_REQ_ACK) && os_memcmp(addr, p2p->invite_peer->info.p2p_device_addr, ETH_ALEN) == 0) { /* Received a Probe Request from Invite peer */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Found Invite peer - try to start Invite from " - "timeout"); + p2p_dbg(p2p, "Found Invite peer - try to start Invite from timeout"); + eloop_cancel_timeout(p2p_invite_start, p2p, NULL); eloop_register_timeout(0, 0, p2p_invite_start, p2p, NULL); return P2P_PREQ_PROCESSED; } @@ -2203,6 +2544,9 @@ int p2p_assoc_req_ie(struct p2p_data *p2p, const u8 *bssid, u8 *buf, extra = wpabuf_len(p2p->wfd_ie_assoc_req); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_REQ]) + extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_REQ]); + /* * (Re)Association Request - P2P IE * P2P Capability attribute (shall be present) @@ -2218,6 +2562,10 @@ int p2p_assoc_req_ie(struct p2p_data *p2p, const u8 *bssid, u8 *buf, wpabuf_put_buf(tmp, p2p->wfd_ie_assoc_req); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_REQ]) + wpabuf_put_buf(tmp, + p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_REQ]); + peer = bssid ? p2p_get_device(p2p, bssid) : NULL; lpos = p2p_buf_add_ie_hdr(tmp); @@ -2256,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; @@ -2301,24 +2775,20 @@ static void p2p_clear_go_neg(struct p2p_data *p2p) void p2p_wps_success_cb(struct p2p_data *p2p, const u8 *mac_addr) { if (p2p->go_neg_peer == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No pending Group Formation - " - "ignore WPS registration success notification"); + p2p_dbg(p2p, "No pending Group Formation - ignore WPS registration success notification"); return; /* No pending Group Formation */ } if (os_memcmp(mac_addr, p2p->go_neg_peer->intended_addr, ETH_ALEN) != 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Ignore WPS registration success notification " - "for " MACSTR " (GO Negotiation peer " MACSTR ")", + p2p_dbg(p2p, "Ignore WPS registration success notification for " + MACSTR " (GO Negotiation peer " MACSTR ")", MAC2STR(mac_addr), MAC2STR(p2p->go_neg_peer->intended_addr)); return; /* Ignore unexpected peer address */ } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Group Formation completed successfully with " MACSTR, + p2p_dbg(p2p, "Group Formation completed successfully with " MACSTR, MAC2STR(mac_addr)); p2p_clear_go_neg(p2p); @@ -2328,14 +2798,11 @@ void p2p_wps_success_cb(struct p2p_data *p2p, const u8 *mac_addr) void p2p_group_formation_failed(struct p2p_data *p2p) { if (p2p->go_neg_peer == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No pending Group Formation - " - "ignore group formation failure notification"); + p2p_dbg(p2p, "No pending Group Formation - ignore group formation failure notification"); return; /* No pending Group Formation */ } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Group Formation failed with " MACSTR, + p2p_dbg(p2p, "Group Formation failed with " MACSTR, MAC2STR(p2p->go_neg_peer->intended_addr)); p2p_clear_go_neg(p2p); @@ -2346,7 +2813,8 @@ struct p2p_data * p2p_init(const struct p2p_config *cfg) { struct p2p_data *p2p; - if (cfg->max_peers < 1) + if (cfg->max_peers < 1 || + cfg->passphrase_len < 8 || cfg->passphrase_len > 63) return NULL; p2p = os_zalloc(sizeof(*p2p) + sizeof(*cfg)); @@ -2375,11 +2843,14 @@ 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; - os_get_random(&p2p->next_tie_breaker, 1); + if (os_get_random(&p2p->next_tie_breaker, 1) < 0) + p2p->next_tie_breaker = 0; p2p->next_tie_breaker &= 0x01; if (cfg->sd_request) p2p->dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY; @@ -2395,6 +2866,11 @@ struct p2p_data * p2p_init(const struct p2p_config *cfg) p2p->go_timeout = 100; p2p->client_timeout = 20; + p2p->num_p2p_sd_queries = 0; + + p2p_dbg(p2p, "initialized"); + p2p_channels_dump(p2p, "channels", &p2p->cfg->channels); + p2p_channels_dump(p2p, "cli_channels", &p2p->cfg->cli_channels); return p2p; } @@ -2402,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); @@ -2419,6 +2897,8 @@ void p2p_deinit(struct p2p_data *p2p) eloop_cancel_timeout(p2p_expiration_timeout, p2p, NULL); 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); @@ -2428,9 +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); } @@ -2458,13 +2948,15 @@ int p2p_unauthorize(struct p2p_data *p2p, const u8 *addr) if (dev == NULL) return -1; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Unauthorizing " MACSTR, - MAC2STR(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; dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE; dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM; @@ -2619,55 +3111,105 @@ 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->flags & P2P_DEV_SD_SCHEDULE) { - if (p2p_start_sd(p2p, dev) == 0) - return; - else - break; - } else if (dev->req_config_methods && - !(dev->flags & P2P_DEV_PD_FOR_JOIN)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "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) { + found = 1; + continue; + } + if (!found) + continue; + if (p2p_pre_find_operation(p2p, dev) > 0) { + p2p->last_p2p_find_oper = dev; + return; } } + /* + * 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 == p2p->last_p2p_find_oper) + break; + } + p2p_listen_in_find(p2p, 1); } static void p2p_sd_cb(struct p2p_data *p2p, int success) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Service Discovery Query TX callback: success=%d", + p2p_dbg(p2p, "Service Discovery Query TX callback: success=%d", success); p2p->pending_action_state = P2P_NO_PENDING_ACTION; if (!success) { - if (p2p->sd_peer) { - p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE; - p2p->sd_peer = NULL; - } - p2p_continue_find(p2p); + if (p2p->sd_peer) + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + p2p->sd_peer = NULL; + if (p2p->state != P2P_IDLE) + p2p_continue_find(p2p); return; } if (p2p->sd_peer == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No SD peer entry known"); - p2p_continue_find(p2p); + p2p_dbg(p2p, "No SD peer entry known"); + if (p2p->state != P2P_IDLE) + p2p_continue_find(p2p); return; } + 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--; + + /* + * If there are no pending broadcast queries for this device, + * mark it as done (-1). + */ + if (p2p->sd_peer->sd_pending_bcast_queries == 0) + p2p->sd_peer->sd_pending_bcast_queries = -1; + } + /* Wait for response from the peer */ p2p_set_state(p2p, P2P_SD_DURING_FIND); p2p_set_timeout(p2p, 0, 200000); @@ -2682,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. @@ -2697,13 +3236,13 @@ static void p2p_retry_pd(struct p2p_data *p2p) if (!dev->req_config_methods) continue; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send " - "pending Provision Discovery Request to " + p2p_dbg(p2p, "Send pending Provision Discovery Request to " MACSTR " (config methods 0x%x)", MAC2STR(dev->info.p2p_device_addr), dev->req_config_methods); p2p_send_prov_disc_req(p2p, dev, - dev->flags & P2P_DEV_PD_FOR_JOIN, 0); + dev->flags & P2P_DEV_PD_FOR_JOIN, + p2p->pd_force_freq); return; } } @@ -2711,8 +3250,7 @@ static void p2p_retry_pd(struct p2p_data *p2p) static void p2p_prov_disc_cb(struct p2p_data *p2p, int success) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Provision Discovery Request TX callback: success=%d", + p2p_dbg(p2p, "Provision Discovery Request TX callback: success=%d", success); /* @@ -2759,11 +3297,71 @@ 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, - unsigned int age, int level, const u8 *ies, + struct os_reltime *rx_time, int level, const u8 *ies, size_t ies_len) { - p2p_add_device(p2p, bssid, freq, age, level, ies, ies_len, 1); + if (os_reltime_before(rx_time, &p2p->find_start)) { + /* + * The driver may have cached (e.g., in cfg80211 BSS table) the + * scan results for relatively long time. To avoid reporting + * stale information, update P2P peers only based on results + * that have based on frames received after the last p2p_find + * operation was started. + */ + p2p_dbg(p2p, "Ignore old scan result for " MACSTR + " (rx_time=%u.%06u)", + MAC2STR(bssid), (unsigned int) rx_time->sec, + (unsigned int) rx_time->usec); + return 0; + } + + p2p_add_device(p2p, bssid, freq, rx_time, level, ies, ies_len, 1); return 0; } @@ -2772,8 +3370,7 @@ int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq, void p2p_scan_res_handled(struct p2p_data *p2p) { if (!p2p->p2p_scan_running) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan was not " - "running, but scan results received"); + p2p_dbg(p2p, "p2p_scan was not running, but scan results received"); } p2p->p2p_scan_running = 0; eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); @@ -2787,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 @@ -2794,9 +3392,20 @@ void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id) wpabuf_put_buf(ies, p2p->wfd_ie_probe_req); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_PROBE_REQ_P2P]) + wpabuf_put_buf(ies, + 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) @@ -2806,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); } @@ -2820,6 +3433,10 @@ size_t p2p_scan_ie_buf_len(struct p2p_data *p2p) len += wpabuf_len(p2p->wfd_ie_probe_req); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p && p2p->vendor_elem && + p2p->vendor_elem[VENDOR_ELEM_PROBE_REQ_P2P]) + len += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_PROBE_REQ_P2P]); + return len; } @@ -2833,14 +3450,12 @@ int p2p_ie_text(struct wpabuf *p2p_ie, char *buf, char *end) static void p2p_go_neg_req_cb(struct p2p_data *p2p, int success) { struct p2p_device *dev = p2p->go_neg_peer; + int timeout; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: GO Negotiation Request TX callback: success=%d", - success); + p2p_dbg(p2p, "GO Negotiation Request TX callback: success=%d", success); if (dev == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No pending GO Negotiation"); + p2p_dbg(p2p, "No pending GO Negotiation"); return; } @@ -2857,9 +3472,7 @@ static void p2p_go_neg_req_cb(struct p2p_data *p2p, int success) if (!success && (dev->info.dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY) && !is_zero_ether_addr(dev->member_in_go_dev)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Peer " MACSTR " did not acknowledge request - " - "try to use device discoverability through its GO", + p2p_dbg(p2p, "Peer " MACSTR " did not acknowledge request - try to use device discoverability through its GO", MAC2STR(dev->info.p2p_device_addr)); p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p_send_dev_disc_req(p2p, dev); @@ -2871,35 +3484,56 @@ static void p2p_go_neg_req_cb(struct p2p_data *p2p, int success) * channel. */ p2p_set_state(p2p, P2P_CONNECT); - p2p_set_timeout(p2p, 0, success ? 200000 : 100000); + timeout = success ? 500000 : 100000; + if (!success && p2p->go_neg_peer && + (p2p->go_neg_peer->flags & P2P_DEV_PEER_WAITING_RESPONSE)) { + unsigned int r; + /* + * Peer is expected to wait our response and we will skip the + * listen phase. Add some randomness to the wait time here to + * make it less likely to hit cases where we could end up in + * sync with peer not listening. + */ + if (os_get_random((u8 *) &r, sizeof(r)) < 0) + r = 0; + timeout += r % 100000; + } + p2p_set_timeout(p2p, 0, timeout); } static void p2p_go_neg_resp_cb(struct p2p_data *p2p, int success) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: GO Negotiation Response TX callback: success=%d", + p2p_dbg(p2p, "GO Negotiation Response TX callback: success=%d", success); if (!p2p->go_neg_peer && p2p->state == P2P_PROVISIONING) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Ignore TX callback event - GO Negotiation is " - "not running anymore"); + p2p_dbg(p2p, "Ignore TX callback event - GO Negotiation is not running anymore"); return; } p2p_set_state(p2p, P2P_CONNECT); - p2p_set_timeout(p2p, 0, 250000); + p2p_set_timeout(p2p, 0, 500000); } -static void p2p_go_neg_resp_failure_cb(struct p2p_data *p2p, int success) +static void p2p_go_neg_resp_failure_cb(struct p2p_data *p2p, int success, + const u8 *addr) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: GO Negotiation Response (failure) TX callback: " - "success=%d", 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); + 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); } @@ -2908,16 +3542,45 @@ static void p2p_go_neg_conf_cb(struct p2p_data *p2p, { struct p2p_device *dev; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: GO Negotiation Confirm TX callback: result=%d", - result); - p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + p2p_dbg(p2p, "GO Negotiation Confirm TX callback: result=%d", result); if (result == P2P_SEND_ACTION_FAILED) { - p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1); + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + p2p_go_neg_failed(p2p, -1); return; } + + dev = p2p->go_neg_peer; + if (result == P2P_SEND_ACTION_NO_ACK) { /* + * Retry GO Negotiation Confirmation + * P2P_GO_NEG_CNF_MAX_RETRY_COUNT times if we did not receive + * ACK for confirmation. + */ + if (dev && dev->go_neg_conf && + dev->go_neg_conf_sent <= P2P_GO_NEG_CNF_MAX_RETRY_COUNT) { + p2p_dbg(p2p, "GO Negotiation Confirm retry %d", + dev->go_neg_conf_sent); + p2p->pending_action_state = P2P_PENDING_GO_NEG_CONFIRM; + if (p2p_send_action(p2p, dev->go_neg_conf_freq, + dev->info.p2p_device_addr, + p2p->cfg->dev_addr, + dev->info.p2p_device_addr, + wpabuf_head(dev->go_neg_conf), + wpabuf_len(dev->go_neg_conf), 0) >= + 0) { + dev->go_neg_conf_sent++; + return; + } + p2p_dbg(p2p, "Failed to re-send Action frame"); + + /* + * Continue with the assumption that the first attempt + * went through and just the ACK frame was lost. + */ + } + + /* * It looks like the TX status for GO Negotiation Confirm is * often showing failure even when the peer has actually * received the frame. Since the peer may change channels @@ -2927,13 +3590,11 @@ static void p2p_go_neg_conf_cb(struct p2p_data *p2p, * peer did indeed receive the frame, continue regardless of * the TX status. */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Assume GO Negotiation Confirm TX was actually " - "received by the peer even though Ack was not " - "reported"); + p2p_dbg(p2p, "Assume GO Negotiation Confirm TX was actually received by the peer even though Ack was not reported"); } - dev = p2p->go_neg_peer; + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + if (dev == NULL) return; @@ -2948,16 +3609,20 @@ void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst, enum p2p_pending_action_state state; int success; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Action frame TX callback (state=%d freq=%u dst=" MACSTR - " src=" MACSTR " bssid=" MACSTR " result=%d", + p2p_dbg(p2p, "Action frame TX callback (state=%d freq=%u dst=" MACSTR + " 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; switch (state) { case P2P_NO_PENDING_ACTION: + if (p2p->send_action_in_progress) { + p2p->send_action_in_progress = 0; + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + } + p2p_check_after_scan_tx_continuation(p2p); break; case P2P_PENDING_GO_NEG_REQUEST: p2p_go_neg_req_cb(p2p, success); @@ -2966,7 +3631,7 @@ void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst, p2p_go_neg_resp_cb(p2p, success); break; case P2P_PENDING_GO_NEG_RESPONSE_FAILURE: - p2p_go_neg_resp_failure_cb(p2p, success); + p2p_go_neg_resp_failure_cb(p2p, success, dst); break; case P2P_PENDING_GO_NEG_CONFIRM: p2p_go_neg_conf_cb(p2p, result); @@ -2977,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; @@ -2993,6 +3661,8 @@ void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst, p2p_go_disc_req_cb(p2p, success); break; } + + p2p->after_scan_tx_in_progress = 0; } @@ -3000,23 +3670,18 @@ void p2p_listen_cb(struct p2p_data *p2p, unsigned int freq, unsigned int duration) { if (freq == p2p->pending_client_disc_freq) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Client discoverability remain-awake completed"); + p2p_dbg(p2p, "Client discoverability remain-awake completed"); p2p->pending_client_disc_freq = 0; return; } if (freq != p2p->pending_listen_freq) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unexpected listen callback for freq=%u " - "duration=%u (pending_listen_freq=%u)", + p2p_dbg(p2p, "Unexpected listen callback for freq=%u duration=%u (pending_listen_freq=%u)", freq, duration, p2p->pending_listen_freq); return; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Starting Listen timeout(%u,%u) on freq=%u based on " - "callback", + p2p_dbg(p2p, "Starting Listen timeout(%u,%u) on freq=%u based on callback", p2p->pending_listen_sec, p2p->pending_listen_usec, p2p->pending_listen_freq); p2p->in_listen = 1; @@ -3037,18 +3702,15 @@ void p2p_listen_cb(struct p2p_data *p2p, unsigned int freq, int p2p_listen_end(struct p2p_data *p2p, unsigned int freq) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Driver ended Listen " - "state (freq=%u)", freq); + p2p_dbg(p2p, "Driver ended Listen state (freq=%u)", freq); p2p->drv_in_listen = 0; if (p2p->in_listen) return 0; /* Internal timeout will trigger the next step */ if (p2p->state == P2P_CONNECT_LISTEN && p2p->go_neg_peer) { if (p2p->go_neg_peer->connect_reqs >= 120) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Timeout on sending GO Negotiation " - "Request without getting response"); - p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1); + p2p_dbg(p2p, "Timeout on sending GO Negotiation Request without getting response"); + p2p_go_neg_failed(p2p, -1); return 0; } @@ -3065,9 +3727,7 @@ int p2p_listen_end(struct p2p_data *p2p, unsigned int freq) * operation while in p2p_find. Avoid an attempt to * restart a scan here. */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: p2p_scan " - "already in progress - do not try to start a " - "new one"); + p2p_dbg(p2p, "p2p_scan already in progress - do not try to start a new one"); return 1; } if (p2p->pending_listen_freq) { @@ -3076,15 +3736,12 @@ int p2p_listen_end(struct p2p_data *p2p, unsigned int freq) * offchannel operation for some reason. p2p_search() * will be started from internal timeout. */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Listen " - "operation did not seem to start - delay " - "search phase to avoid busy loop"); + p2p_dbg(p2p, "Listen operation did not seem to start - delay search phase to avoid busy loop"); p2p_set_timeout(p2p, 0, 100000); return 1; } if (p2p->search_delay) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Delay " - "search operation by %u ms", + p2p_dbg(p2p, "Delay search operation by %u ms", p2p->search_delay); p2p_set_timeout(p2p, p2p->search_delay / 1000, (p2p->search_delay % 1000) * 1000); @@ -3103,10 +3760,21 @@ static void p2p_timeout_connect(struct p2p_data *p2p) p2p->cfg->send_action_done(p2p->cfg->cb_ctx); if (p2p->go_neg_peer && (p2p->go_neg_peer->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Wait for GO " - "Negotiation Confirm timed out - assume GO " - "Negotiation failed"); - p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1); + p2p_dbg(p2p, "Wait for GO Negotiation Confirm timed out - assume GO Negotiation failed"); + p2p_go_neg_failed(p2p, -1); + return; + } + if (p2p->go_neg_peer && + (p2p->go_neg_peer->flags & P2P_DEV_PEER_WAITING_RESPONSE) && + p2p->go_neg_peer->connect_reqs < 120) { + p2p_dbg(p2p, "Peer expected to wait our response - skip listen"); + p2p_connect_send(p2p, p2p->go_neg_peer); + return; + } + if (p2p->go_neg_peer && p2p->go_neg_peer->oob_go_neg_freq > 0) { + p2p_dbg(p2p, "Skip connect-listen since GO Neg channel known (OOB)"); + p2p_set_state(p2p, P2P_CONNECT_LISTEN); + p2p_set_timeout(p2p, 0, 30000); return; } p2p_set_state(p2p, P2P_CONNECT_LISTEN); @@ -3118,17 +3786,13 @@ static void p2p_timeout_connect_listen(struct p2p_data *p2p) { if (p2p->go_neg_peer) { if (p2p->drv_in_listen) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Driver is " - "still in Listen state; wait for it to " - "complete"); + p2p_dbg(p2p, "Driver is still in Listen state; wait for it to complete"); return; } if (p2p->go_neg_peer->connect_reqs >= 120) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Timeout on sending GO Negotiation " - "Request without getting response"); - p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1); + p2p_dbg(p2p, "Timeout on sending GO Negotiation Request without getting response"); + p2p_go_neg_failed(p2p, -1); return; } @@ -3141,13 +3805,13 @@ static void p2p_timeout_connect_listen(struct p2p_data *p2p) static void p2p_timeout_wait_peer_connect(struct p2p_data *p2p) { - /* - * TODO: could remain constantly in Listen state for some time if there - * are no other concurrent uses for the radio. For now, go to listen - * state once per second to give other uses a chance to use the radio. - */ p2p_set_state(p2p, P2P_WAIT_PEER_IDLE); - p2p_set_timeout(p2p, 0, 500000); + + if (p2p->cfg->is_concurrent_session_active && + p2p->cfg->is_concurrent_session_active(p2p->cfg->cb_ctx)) + p2p_set_timeout(p2p, 0, 500000); + else + p2p_set_timeout(p2p, 0, 200000); } @@ -3156,23 +3820,11 @@ static void p2p_timeout_wait_peer_idle(struct p2p_data *p2p) struct p2p_device *dev = p2p->go_neg_peer; if (dev == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unknown GO Neg peer - stop GO Neg wait"); + p2p_dbg(p2p, "Unknown GO Neg peer - stop GO Neg wait"); return; } - dev->wait_count++; - if (dev->wait_count >= 120) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Timeout on waiting peer to become ready for GO " - "Negotiation"); - p2p_go_neg_failed(p2p, dev, -1); - return; - } - - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Go to Listen state while waiting for the peer to become " - "ready for GO Negotiation"); + 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); } @@ -3180,11 +3832,9 @@ static void p2p_timeout_wait_peer_idle(struct p2p_data *p2p) static void p2p_timeout_sd_during_find(struct p2p_data *p2p) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Service Discovery Query timeout"); + p2p_dbg(p2p, "Service Discovery Query timeout"); if (p2p->sd_peer) { p2p->cfg->send_action_done(p2p->cfg->cb_ctx); - p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE; p2p->sd_peer = NULL; } p2p_continue_find(p2p); @@ -3193,8 +3843,7 @@ static void p2p_timeout_sd_during_find(struct p2p_data *p2p) static void p2p_timeout_prov_disc_during_find(struct p2p_data *p2p) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Provision Discovery Request timeout"); + p2p_dbg(p2p, "Provision Discovery Request timeout"); p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p_continue_find(p2p); } @@ -3202,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; /* @@ -3212,8 +3864,7 @@ static void p2p_timeout_prov_disc_req(struct p2p_data *p2p) if (!p2p->user_initiated_pd) return; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: User initiated Provision Discovery Request timeout"); + p2p_dbg(p2p, "User initiated Provision Discovery Request timeout"); if (p2p->pd_retries) { p2p->pd_retries--; @@ -3231,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); } } @@ -3251,8 +3908,7 @@ static void p2p_timeout_invite(struct p2p_data *p2p) * Better remain on operating channel instead of listen channel * when running a group. */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Inviting in " - "active GO role - wait on operating channel"); + p2p_dbg(p2p, "Inviting in active GO role - wait on operating channel"); p2p_set_timeout(p2p, 0, 100000); return; } @@ -3265,14 +3921,15 @@ static void p2p_timeout_invite_listen(struct p2p_data *p2p) if (p2p->invite_peer && p2p->invite_peer->invitation_reqs < 100) { p2p_set_state(p2p, P2P_INVITE); p2p_invite_send(p2p, p2p->invite_peer, - p2p->invite_go_dev_addr); + p2p->invite_go_dev_addr, p2p->invite_dev_pw_id); } else { if (p2p->invite_peer) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invitation Request retry limit reached"); + p2p_dbg(p2p, "Invitation Request retry limit reached"); if (p2p->cfg->invitation_result) p2p->cfg->invitation_result( - p2p->cfg->cb_ctx, -1, NULL); + p2p->cfg->cb_ctx, -1, NULL, NULL, + p2p->invite_peer->info.p2p_device_addr, + 0, 0); } p2p_set_state(p2p, P2P_IDLE); } @@ -3283,10 +3940,13 @@ static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx) { struct p2p_data *p2p = eloop_ctx; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Timeout (state=%s)", - p2p_state_txt(p2p->state)); + 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: @@ -3299,8 +3959,7 @@ static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx) if (p2p->pending_action_state == P2P_PENDING_PD) p2p_timeout_prov_disc_req(p2p); if (p2p->search_delay && !p2p->in_search_delay) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Delay " - "search operation by %u ms", + p2p_dbg(p2p, "Delay search operation by %u ms", p2p->search_delay); p2p->in_search_delay = 1; p2p_set_timeout(p2p, p2p->search_delay / 1000, @@ -3324,9 +3983,7 @@ static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx) p2p_timeout_prov_disc_req(p2p); if (p2p->ext_listen_only) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Extended Listen Timing - Listen State " - "completed"); + p2p_dbg(p2p, "Extended Listen Timing - Listen State completed"); p2p->ext_listen_only = 0; p2p_set_state(p2p, P2P_IDLE); } @@ -3351,10 +4008,6 @@ static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx) case P2P_INVITE_LISTEN: p2p_timeout_invite_listen(p2p); break; - case P2P_SEARCH_WHEN_READY: - break; - case P2P_CONTINUE_SEARCH_WHEN_READY: - break; } } @@ -3364,11 +4017,10 @@ int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr) struct p2p_device *dev; dev = p2p_get_device(p2p, peer_addr); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Local request to reject " - "connection attempts by peer " MACSTR, MAC2STR(peer_addr)); + p2p_dbg(p2p, "Local request to reject connection attempts by peer " + MACSTR, MAC2STR(peer_addr)); if (dev == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR - " unknown", MAC2STR(peer_addr)); + p2p_dbg(p2p, "Peer " MACSTR " unknown", MAC2STR(peer_addr)); return -1; } dev->status = P2P_SC_FAIL_REJECTED_BY_USER; @@ -3388,6 +4040,10 @@ const char * p2p_wps_method_text(enum p2p_wps_method method) return "Keypad"; case WPS_PBC: return "PBC"; + case WPS_NFC: + return "NFC"; + case WPS_P2PS: + return "P2PS"; } return "??"; @@ -3438,7 +4094,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info, struct p2p_device *dev; int res; char *pos, *end; - struct os_time now; + struct os_reltime now; if (info == NULL) return -1; @@ -3449,7 +4105,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info, pos = buf; end = buf + buflen; - os_get_time(&now); + os_get_reltime(&now); res = os_snprintf(pos, end - pos, "age=%d\n" "listen_freq=%d\n" @@ -3464,9 +4120,8 @@ 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%s%s\n" + "flags=%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n" "status=%d\n" - "wait_count=%u\n" "invitation_reqs=%u\n", (int) (now.sec - dev->last_seen.sec), dev->listen_freq, @@ -3487,13 +4142,12 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info, dev->flags & P2P_DEV_REPORTED ? "[REPORTED]" : "", dev->flags & P2P_DEV_NOT_YET_READY ? "[NOT_YET_READY]" : "", - dev->flags & P2P_DEV_SD_INFO ? "[SD_INFO]" : "", - dev->flags & P2P_DEV_SD_SCHEDULE ? "[SD_SCHEDULE]" : - "", dev->flags & P2P_DEV_PD_PEER_DISPLAY ? "[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 ? @@ -3511,9 +4165,8 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info, dev->flags & P2P_DEV_PD_FOR_JOIN ? "[PD_FOR_JOIN]" : "", dev->status, - dev->wait_count, dev->invitation_reqs); - if (res < 0 || res >= end - pos) + if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; @@ -3523,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; } @@ -3533,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; } @@ -3541,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; @@ -3550,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; } @@ -3569,12 +4222,10 @@ int p2p_peer_known(struct p2p_data *p2p, const u8 *addr) void p2p_set_client_discoverability(struct p2p_data *p2p, int enabled) { if (enabled) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Client " - "discoverability enabled"); + p2p_dbg(p2p, "Client discoverability enabled"); p2p->dev_capab |= P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; } else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Client " - "discoverability disabled"); + p2p_dbg(p2p, "Client discoverability disabled"); p2p->dev_capab &= ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY; } } @@ -3623,9 +4274,9 @@ int p2p_presence_req(struct p2p_data *p2p, const u8 *go_interface_addr, { struct wpabuf *req; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send Presence Request to " - "GO " MACSTR " (own interface " MACSTR ") freq=%u dur1=%u " - "int1=%u dur2=%u int2=%u", + p2p_dbg(p2p, "Send Presence Request to GO " MACSTR + " (own interface " MACSTR ") freq=%u dur1=%u int1=%u " + "dur2=%u int2=%u", MAC2STR(go_interface_addr), MAC2STR(own_interface_addr), freq, duration1, interval1, duration2, interval2); @@ -3638,8 +4289,7 @@ int p2p_presence_req(struct p2p_data *p2p, const u8 *go_interface_addr, if (p2p_send_action(p2p, freq, go_interface_addr, own_interface_addr, go_interface_addr, wpabuf_head(req), wpabuf_len(req), 200) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + p2p_dbg(p2p, "Failed to send Action frame"); } wpabuf_free(req); @@ -3685,8 +4335,7 @@ static void p2p_process_presence_req(struct p2p_data *p2p, const u8 *da, u8 noa[50]; int noa_len; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received P2P Action - P2P Presence Request"); + p2p_dbg(p2p, "Received P2P Action - P2P Presence Request"); for (g = 0; g < p2p->num_groups; g++) { if (os_memcmp(da, p2p_group_get_interface_addr(p2p->groups[g]), @@ -3696,23 +4345,20 @@ static void p2p_process_presence_req(struct p2p_data *p2p, const u8 *da, } } if (group == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Ignore P2P Presence Request for unknown group " + p2p_dbg(p2p, "Ignore P2P Presence Request for unknown group " MACSTR, MAC2STR(da)); return; } if (p2p_parse(data, len, &msg) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to parse P2P Presence Request"); + p2p_dbg(p2p, "Failed to parse P2P Presence Request"); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; } parsed = 1; if (msg.noa == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No NoA attribute in P2P Presence Request"); + p2p_dbg(p2p, "No NoA attribute in P2P Presence Request"); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; } @@ -3736,8 +4382,7 @@ fail: p2p->pending_action_state = P2P_NO_PENDING_ACTION; if (p2p_send_action(p2p, rx_freq, sa, da, da, wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + p2p_dbg(p2p, "Failed to send Action frame"); } wpabuf_free(resp); } @@ -3748,33 +4393,32 @@ static void p2p_process_presence_resp(struct p2p_data *p2p, const u8 *da, { struct p2p_message msg; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received P2P Action - P2P Presence Response"); + p2p_dbg(p2p, "Received P2P Action - P2P Presence Response"); if (p2p_parse(data, len, &msg) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to parse P2P Presence Response"); + p2p_dbg(p2p, "Failed to parse P2P Presence Response"); return; } if (msg.status == NULL || msg.noa == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Status or NoA attribute in P2P Presence " - "Response"); + p2p_dbg(p2p, "No Status or NoA attribute in P2P Presence Response"); p2p_parse_free(&msg); return; } + if (p2p->cfg->presence_resp) { + p2p->cfg->presence_resp(p2p->cfg->cb_ctx, sa, *msg.status, + msg.noa, msg.noa_len); + } + if (*msg.status) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: P2P Presence Request was rejected: status %u", + p2p_dbg(p2p, "P2P Presence Request was rejected: status %u", *msg.status); p2p_parse_free(&msg); return; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: P2P Presence Request was accepted"); + p2p_dbg(p2p, "P2P Presence Request was accepted"); wpa_hexdump(MSG_DEBUG, "P2P: P2P Presence Response - NoA", msg.noa, msg.noa_len); /* TODO: process NoA */ @@ -3793,6 +4437,15 @@ static void p2p_ext_listen_timeout(void *eloop_ctx, void *timeout_ctx) p2p_ext_listen_timeout, p2p, NULL); } + if ((p2p->cfg->is_p2p_in_progress && + p2p->cfg->is_p2p_in_progress(p2p->cfg->cb_ctx)) || + (p2p->pending_action_state == P2P_PENDING_PD && + p2p->pd_retries > 0)) { + p2p_dbg(p2p, "Operation in progress - skip Extended Listen timeout (%s)", + p2p_state_txt(p2p->state)); + return; + } + if (p2p->state == P2P_LISTEN_ONLY && p2p->ext_listen_only) { /* * This should not really happen, but it looks like the Listen @@ -3800,25 +4453,20 @@ static void p2p_ext_listen_timeout(void *eloop_ctx, void *timeout_ctx) * running at an inconvenient time. As a workaround, allow new * Extended Listen operation to be started. */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Previous " - "Extended Listen operation had not been completed - " - "try again"); + p2p_dbg(p2p, "Previous Extended Listen operation had not been completed - try again"); p2p->ext_listen_only = 0; p2p_set_state(p2p, P2P_IDLE); } if (p2p->state != P2P_IDLE) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Skip Extended " - "Listen timeout in active state (%s)", - p2p_state_txt(p2p->state)); + p2p_dbg(p2p, "Skip Extended Listen timeout in active state (%s)", p2p_state_txt(p2p->state)); return; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Extended Listen timeout"); + p2p_dbg(p2p, "Extended Listen timeout"); p2p->ext_listen_only = 1; if (p2p_listen(p2p, p2p->ext_listen_period) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Failed to start " - "Listen state for Extended Listen Timing"); + p2p_dbg(p2p, "Failed to start Listen state for Extended Listen Timing"); p2p->ext_listen_only = 0; } } @@ -3829,25 +4477,22 @@ int p2p_ext_listen(struct p2p_data *p2p, unsigned int period, { if (period > 65535 || interval > 65535 || period > interval || (period == 0 && interval > 0) || (period > 0 && interval == 0)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invalid Extended Listen Timing request: " - "period=%u interval=%u", period, interval); + p2p_dbg(p2p, "Invalid Extended Listen Timing request: period=%u interval=%u", + period, interval); return -1; } eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL); if (interval == 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Disabling Extended Listen Timing"); + p2p_dbg(p2p, "Disabling Extended Listen Timing"); p2p->ext_listen_period = 0; p2p->ext_listen_interval = 0; return 0; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Enabling Extended Listen Timing: period %u msec, " - "interval %u msec", period, interval); + p2p_dbg(p2p, "Enabling Extended Listen Timing: period %u msec, interval %u msec", + period, interval); p2p->ext_listen_period = period; p2p->ext_listen_interval = interval; p2p->ext_listen_interval_sec = interval / 1000; @@ -3872,11 +4517,12 @@ void p2p_deauth_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code, os_memset(&msg, 0, sizeof(msg)); if (p2p_parse_ies(ie, ie_len, &msg)) return; - if (msg.minor_reason_code == NULL) + if (msg.minor_reason_code == NULL) { + p2p_parse_free(&msg); return; + } - wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, - "P2P: Deauthentication notification BSSID " MACSTR + p2p_dbg(p2p, "Deauthentication notification BSSID " MACSTR " reason_code=%u minor_reason_code=%u", MAC2STR(bssid), reason_code, *msg.minor_reason_code); @@ -3895,11 +4541,12 @@ void p2p_disassoc_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code, os_memset(&msg, 0, sizeof(msg)); if (p2p_parse_ies(ie, ie_len, &msg)) return; - if (msg.minor_reason_code == NULL) + if (msg.minor_reason_code == NULL) { + p2p_parse_free(&msg); return; + } - wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, - "P2P: Disassociation notification BSSID " MACSTR + p2p_dbg(p2p, "Disassociation notification BSSID " MACSTR " reason_code=%u minor_reason_code=%u", MAC2STR(bssid), reason_code, *msg.minor_reason_code); @@ -3910,34 +4557,65 @@ void p2p_disassoc_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code, void p2p_set_managed_oper(struct p2p_data *p2p, int enabled) { if (enabled) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Managed P2P " - "Device operations enabled"); + p2p_dbg(p2p, "Managed P2P Device operations enabled"); p2p->dev_capab |= P2P_DEV_CAPAB_INFRA_MANAGED; } else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Managed P2P " - "Device operations disabled"); + p2p_dbg(p2p, "Managed P2P Device operations disabled"); p2p->dev_capab &= ~P2P_DEV_CAPAB_INFRA_MANAGED; } } -int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel) +int p2p_config_get_random_social(struct p2p_config *p2p, u8 *op_class, + u8 *op_channel) { - if (p2p_channel_to_freq(p2p->cfg->country, reg_class, channel) < 0) + return p2p_channel_random_social(&p2p->channels, op_class, op_channel); +} + + +int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel, + u8 forced) +{ + if (p2p_channel_to_freq(reg_class, channel) < 0) + return -1; + + /* + * Listen channel was set in configuration or set by control interface; + * cannot override it. + */ + if (p2p->cfg->channel_forced && forced == 0) { + p2p_dbg(p2p, + "Listen channel was previously configured - do not override based on optimization"); return -1; + } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Set Listen channel: " - "reg_class %u channel %u", reg_class, channel); - p2p->cfg->reg_class = reg_class; - p2p->cfg->channel = channel; + 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; + p2p->cfg->channel = channel; + p2p->cfg->channel_forced = forced; + } else { + p2p_dbg(p2p, "Defer setting listen channel"); + p2p->pending_reg_class = reg_class; + p2p->pending_channel = channel; + p2p->pending_channel_forced = forced; + } return 0; } +u8 p2p_get_listen_channel(struct p2p_data *p2p) +{ + return p2p->cfg->channel; +} + + int p2p_set_ssid_postfix(struct p2p_data *p2p, const u8 *postfix, size_t len) { - wpa_hexdump_ascii(MSG_DEBUG, "P2P: New SSID postfix", postfix, len); + p2p_dbg(p2p, "New SSID postfix: %s", wpa_ssid_txt(postfix, len)); if (postfix == NULL) { p2p->cfg->ssid_postfix_len = 0; return 0; @@ -3953,12 +4631,11 @@ int p2p_set_ssid_postfix(struct p2p_data *p2p, const u8 *postfix, size_t len) int p2p_set_oper_channel(struct p2p_data *p2p, u8 op_reg_class, u8 op_channel, int cfg_op_channel) { - if (p2p_channel_to_freq(p2p->cfg->country, op_reg_class, op_channel) - < 0) + if (p2p_channel_to_freq(op_reg_class, op_channel) < 0) return -1; - wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, "P2P: Set Operating channel: " - "reg_class %u channel %u", op_reg_class, op_channel); + p2p_dbg(p2p, "Set Operating channel: reg_class %u channel %u", + op_reg_class, op_channel); p2p->cfg->op_reg_class = op_reg_class; p2p->cfg->op_channel = op_channel; p2p->cfg->cfg_op_channel = cfg_op_channel; @@ -3988,6 +4665,31 @@ int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan, } +int p2p_set_no_go_freq(struct p2p_data *p2p, + const struct wpa_freq_range_list *list) +{ + struct wpa_freq_range *tmp; + + if (list == NULL || list->num == 0) { + os_free(p2p->no_go_freq.range); + p2p->no_go_freq.range = NULL; + p2p->no_go_freq.num = 0; + return 0; + } + + tmp = os_calloc(list->num, sizeof(struct wpa_freq_range)); + if (tmp == NULL) + return -1; + os_memcpy(tmp, list->range, list->num * sizeof(struct wpa_freq_range)); + os_free(p2p->no_go_freq.range); + p2p->no_go_freq.range = tmp; + p2p->no_go_freq.num = list->num; + p2p_dbg(p2p, "Updated no GO chan list"); + + return 0; +} + + int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr, u8 *iface_addr) { @@ -4014,18 +4716,16 @@ void p2p_set_peer_filter(struct p2p_data *p2p, const u8 *addr) { os_memcpy(p2p->peer_filter, addr, ETH_ALEN); if (is_zero_ether_addr(p2p->peer_filter)) - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Disable peer " - "filter"); + p2p_dbg(p2p, "Disable peer filter"); else - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Enable peer " - "filter for " MACSTR, MAC2STR(p2p->peer_filter)); + p2p_dbg(p2p, "Enable peer filter for " MACSTR, + MAC2STR(p2p->peer_filter)); } void p2p_set_cross_connect(struct p2p_data *p2p, int enabled) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Cross connection %s", - enabled ? "enabled" : "disabled"); + p2p_dbg(p2p, "Cross connection %s", enabled ? "enabled" : "disabled"); if (p2p->cross_connect == enabled) return; p2p->cross_connect = enabled; @@ -4046,16 +4746,22 @@ int p2p_get_oper_freq(struct p2p_data *p2p, const u8 *iface_addr) void p2p_set_intra_bss_dist(struct p2p_data *p2p, int enabled) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Intra BSS distribution %s", + p2p_dbg(p2p, "Intra BSS distribution %s", enabled ? "enabled" : "disabled"); p2p->cfg->p2p_intra_bss = enabled; } -void p2p_update_channel_list(struct p2p_data *p2p, struct p2p_channels *chan) +void p2p_update_channel_list(struct p2p_data *p2p, + const struct p2p_channels *chan, + const struct p2p_channels *cli_chan) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Update channel list"); + p2p_dbg(p2p, "Update channel list"); os_memcpy(&p2p->cfg->channels, chan, sizeof(struct p2p_channels)); + p2p_channels_dump(p2p, "channels", &p2p->cfg->channels); + os_memcpy(&p2p->cfg->cli_channels, cli_chan, + sizeof(struct p2p_channels)); + p2p_channels_dump(p2p, "cli_channels", &p2p->cfg->cli_channels); } @@ -4064,11 +4770,9 @@ int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst, size_t len, unsigned int wait_time) { if (p2p->p2p_scan_running) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Delay Action " - "frame TX until p2p_scan completes"); + p2p_dbg(p2p, "Delay Action frame TX until p2p_scan completes"); if (p2p->after_scan_tx) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Dropped " - "previous pending Action frame TX"); + p2p_dbg(p2p, "Dropped previous pending Action frame TX"); os_free(p2p->after_scan_tx); } p2p->after_scan_tx = os_malloc(sizeof(*p2p->after_scan_tx) + @@ -4093,14 +4797,21 @@ int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst, void p2p_set_best_channels(struct p2p_data *p2p, int freq_24, int freq_5, int freq_overall) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Best channel: 2.4 GHz: %d," - " 5 GHz: %d, overall: %d", freq_24, freq_5, freq_overall); + p2p_dbg(p2p, "Best channel: 2.4 GHz: %d, 5 GHz: %d, overall: %d", + freq_24, freq_5, freq_overall); p2p->best_freq_24 = freq_24; p2p->best_freq_5 = freq_5; p2p->best_freq_overall = freq_overall; } +void p2p_set_own_freq_preference(struct p2p_data *p2p, int freq) +{ + p2p_dbg(p2p, "Own frequency preference: %d MHz", freq); + p2p->own_freq_preference = freq; +} + + const u8 * p2p_get_go_neg_peer(struct p2p_data *p2p) { if (p2p == NULL || p2p->go_neg_peer == NULL) @@ -4129,7 +4840,7 @@ p2p_get_peer_found(struct p2p_data *p2p, const u8 *addr, int next) dev = dl_list_first(&dev->list, struct p2p_device, list); - if (&dev->list == &p2p->devices) + if (!dev || &dev->list == &p2p->devices) return NULL; } while (dev->flags & P2P_DEV_PROBE_REQ_ONLY); } @@ -4141,7 +4852,7 @@ p2p_get_peer_found(struct p2p_data *p2p, const u8 *addr, int next) dev = dl_list_first(&dev->list, struct p2p_device, list); - if (&dev->list == &p2p->devices) + if (!dev || &dev->list == &p2p->devices) return NULL; } } @@ -4154,8 +4865,7 @@ int p2p_in_progress(struct p2p_data *p2p) { if (p2p == NULL) return 0; - if (p2p->state == P2P_SEARCH || p2p->state == P2P_SEARCH_WHEN_READY || - p2p->state == P2P_CONTINUE_SEARCH_WHEN_READY) + if (p2p->state == P2P_SEARCH) return 2; return p2p->state != P2P_IDLE && p2p->state != P2P_PROVISIONING; } @@ -4171,13 +4881,6 @@ void p2p_set_config_timeout(struct p2p_data *p2p, u8 go_timeout, } -void p2p_increase_search_delay(struct p2p_data *p2p, unsigned int delay) -{ - if (p2p && p2p->search_delay < delay) - p2p->search_delay = delay; -} - - #ifdef CONFIG_WIFI_DISPLAY static void p2p_update_wfd_ie_groups(struct p2p_data *p2p) @@ -4187,7 +4890,7 @@ static void p2p_update_wfd_ie_groups(struct p2p_data *p2p) for (g = 0; g < p2p->num_groups; g++) { group = p2p->groups[g]; - p2p_group_update_ies(group); + p2p_group_force_beacon_update_ies(group); } } @@ -4312,9 +5015,322 @@ int p2p_set_disc_int(struct p2p_data *p2p, int min_disc_int, int max_disc_int, p2p->min_disc_int = min_disc_int; p2p->max_disc_int = max_disc_int; p2p->max_disc_tu = max_disc_tu; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Set discoverable interval: " - "min=%d max=%d max_tu=%d", min_disc_int, max_disc_int, - max_disc_tu); + p2p_dbg(p2p, "Set discoverable interval: min=%d max=%d max_tu=%d", + min_disc_int, max_disc_int, max_disc_tu); return 0; } + + +void p2p_dbg(struct p2p_data *p2p, const char *fmt, ...) +{ + va_list ap; + char buf[500]; + + if (!p2p->cfg->debug_print) + return; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + buf[sizeof(buf) - 1] = '\0'; + va_end(ap); + p2p->cfg->debug_print(p2p->cfg->cb_ctx, MSG_DEBUG, buf); +} + + +void p2p_info(struct p2p_data *p2p, const char *fmt, ...) +{ + va_list ap; + char buf[500]; + + if (!p2p->cfg->debug_print) + return; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + buf[sizeof(buf) - 1] = '\0'; + va_end(ap); + p2p->cfg->debug_print(p2p->cfg->cb_ctx, MSG_INFO, buf); +} + + +void p2p_err(struct p2p_data *p2p, const char *fmt, ...) +{ + va_list ap; + char buf[500]; + + if (!p2p->cfg->debug_print) + return; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + buf[sizeof(buf) - 1] = '\0'; + va_end(ap); + p2p->cfg->debug_print(p2p->cfg->cb_ctx, MSG_ERROR, buf); +} + + +void p2p_loop_on_known_peers(struct p2p_data *p2p, + void (*peer_callback)(struct p2p_peer_info *peer, + void *user_data), + void *user_data) +{ + struct p2p_device *dev, *n; + + dl_list_for_each_safe(dev, n, &p2p->devices, struct p2p_device, list) { + peer_callback(&dev->info, user_data); + } +} + + +#ifdef CONFIG_WPS_NFC + +static struct wpabuf * p2p_build_nfc_handover(struct p2p_data *p2p, + int client_freq, + const u8 *go_dev_addr, + const u8 *ssid, size_t ssid_len) +{ + struct wpabuf *buf; + u8 op_class, channel; + enum p2p_role_indication role = P2P_DEVICE_NOT_IN_GROUP; + + buf = wpabuf_alloc(1000); + if (buf == NULL) + return NULL; + + op_class = p2p->cfg->reg_class; + channel = p2p->cfg->channel; + + p2p_buf_add_capability(buf, p2p->dev_capab & + ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0); + p2p_buf_add_device_info(buf, p2p, NULL); + + if (p2p->num_groups > 0) { + int freq = p2p_group_get_freq(p2p->groups[0]); + role = P2P_GO_IN_A_GROUP; + if (p2p_freq_to_channel(freq, &op_class, &channel) < 0) { + p2p_dbg(p2p, + "Unknown GO operating frequency %d MHz for NFC handover", + freq); + wpabuf_free(buf); + return NULL; + } + } else if (client_freq > 0) { + role = P2P_CLIENT_IN_A_GROUP; + if (p2p_freq_to_channel(client_freq, &op_class, &channel) < 0) { + p2p_dbg(p2p, + "Unknown client operating frequency %d MHz for NFC handover", + client_freq); + wpabuf_free(buf); + return NULL; + } + } + + p2p_buf_add_oob_go_neg_channel(buf, p2p->cfg->country, op_class, + channel, role); + + if (p2p->num_groups > 0) { + /* Limit number of clients to avoid very long message */ + p2p_buf_add_group_info(p2p->groups[0], buf, 5); + p2p_group_buf_add_id(p2p->groups[0], buf); + } else if (client_freq > 0 && + go_dev_addr && !is_zero_ether_addr(go_dev_addr) && + ssid && ssid_len > 0) { + /* + * Add the optional P2P Group ID to indicate in which group this + * device is a P2P Client. + */ + p2p_buf_add_group_id(buf, go_dev_addr, ssid, ssid_len); + } + + return buf; +} + + +struct wpabuf * p2p_build_nfc_handover_req(struct p2p_data *p2p, + int client_freq, + const u8 *go_dev_addr, + const u8 *ssid, size_t ssid_len) +{ + return p2p_build_nfc_handover(p2p, client_freq, go_dev_addr, ssid, + ssid_len); +} + + +struct wpabuf * p2p_build_nfc_handover_sel(struct p2p_data *p2p, + int client_freq, + const u8 *go_dev_addr, + const u8 *ssid, size_t ssid_len) +{ + return p2p_build_nfc_handover(p2p, client_freq, go_dev_addr, ssid, + ssid_len); +} + + +int p2p_process_nfc_connection_handover(struct p2p_data *p2p, + struct p2p_nfc_params *params) +{ + struct p2p_message msg; + struct p2p_device *dev; + const u8 *p2p_dev_addr; + int freq; + enum p2p_role_indication role; + + params->next_step = NO_ACTION; + + if (p2p_parse_ies_separate(params->wsc_attr, params->wsc_len, + params->p2p_attr, params->p2p_len, &msg)) { + p2p_dbg(p2p, "Failed to parse WSC/P2P attributes from NFC"); + p2p_parse_free(&msg); + return -1; + } + + if (msg.p2p_device_addr) + p2p_dev_addr = msg.p2p_device_addr; + else if (msg.device_id) + p2p_dev_addr = msg.device_id; + else { + p2p_dbg(p2p, "Ignore scan data without P2P Device Info or P2P Device Id"); + p2p_parse_free(&msg); + return -1; + } + + if (msg.oob_dev_password) { + os_memcpy(params->oob_dev_pw, msg.oob_dev_password, + msg.oob_dev_password_len); + params->oob_dev_pw_len = msg.oob_dev_password_len; + } + + dev = p2p_create_device(p2p, p2p_dev_addr); + if (dev == NULL) { + p2p_parse_free(&msg); + return -1; + } + + params->peer = &dev->info; + + os_get_reltime(&dev->last_seen); + dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY); + p2p_copy_wps_info(p2p, dev, 0, &msg); + + if (!msg.oob_go_neg_channel) { + p2p_dbg(p2p, "OOB GO Negotiation Channel attribute not included"); + return -1; + } + + if (msg.oob_go_neg_channel[3] == 0 && + msg.oob_go_neg_channel[4] == 0) + freq = 0; + else + freq = p2p_channel_to_freq(msg.oob_go_neg_channel[3], + msg.oob_go_neg_channel[4]); + if (freq < 0) { + p2p_dbg(p2p, "Unknown peer OOB GO Neg channel"); + return -1; + } + role = msg.oob_go_neg_channel[5]; + + if (role == P2P_GO_IN_A_GROUP) { + p2p_dbg(p2p, "Peer OOB GO operating channel: %u MHz", freq); + params->go_freq = freq; + } else if (role == P2P_CLIENT_IN_A_GROUP) { + p2p_dbg(p2p, "Peer (client) OOB GO operating channel: %u MHz", + freq); + params->go_freq = freq; + } else + p2p_dbg(p2p, "Peer OOB GO Neg channel: %u MHz", freq); + dev->oob_go_neg_freq = freq; + + if (!params->sel && role != P2P_GO_IN_A_GROUP) { + freq = p2p_channel_to_freq(p2p->cfg->reg_class, + p2p->cfg->channel); + if (freq < 0) { + p2p_dbg(p2p, "Own listen channel not known"); + return -1; + } + p2p_dbg(p2p, "Use own Listen channel as OOB GO Neg channel: %u MHz", freq); + dev->oob_go_neg_freq = freq; + } + + if (msg.group_id) { + os_memcpy(params->go_dev_addr, msg.group_id, ETH_ALEN); + params->go_ssid_len = msg.group_id_len - ETH_ALEN; + os_memcpy(params->go_ssid, msg.group_id + ETH_ALEN, + params->go_ssid_len); + } + + if (dev->flags & P2P_DEV_USER_REJECTED) { + p2p_dbg(p2p, "Do not report rejected device"); + p2p_parse_free(&msg); + return 0; + } + + if (!(dev->flags & P2P_DEV_REPORTED)) { + p2p->cfg->dev_found(p2p->cfg->cb_ctx, p2p_dev_addr, &dev->info, + !(dev->flags & P2P_DEV_REPORTED_ONCE)); + dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE; + } + p2p_parse_free(&msg); + + if (role == P2P_GO_IN_A_GROUP && p2p->num_groups > 0) + params->next_step = BOTH_GO; + else if (role == P2P_GO_IN_A_GROUP) + params->next_step = JOIN_GROUP; + else if (role == P2P_CLIENT_IN_A_GROUP) { + dev->flags |= P2P_DEV_GROUP_CLIENT_ONLY; + params->next_step = PEER_CLIENT; + } else if (p2p->num_groups > 0) + params->next_step = AUTH_JOIN; + else if (params->sel) + params->next_step = INIT_GO_NEG; + else + params->next_step = RESP_GO_NEG; + + return 0; +} + + +void p2p_set_authorized_oob_dev_pw_id(struct p2p_data *p2p, u16 dev_pw_id, + int go_intent, + const u8 *own_interface_addr) +{ + + p2p->authorized_oob_dev_pw_id = dev_pw_id; + if (dev_pw_id == 0) { + p2p_dbg(p2p, "NFC OOB Password unauthorized for static handover"); + return; + } + + p2p_dbg(p2p, "NFC OOB Password (id=%u) authorized for static handover", + dev_pw_id); + + p2p->go_intent = go_intent; + os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN); +} + +#endif /* CONFIG_WPS_NFC */ + + +int p2p_set_passphrase_len(struct p2p_data *p2p, unsigned int len) +{ + if (len < 8 || len > 63) + return -1; + p2p->cfg->passphrase_len = len; + return 0; +} + + +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 2d8b2c25750b7..2402db6a7eb94 100644 --- a/src/p2p/p2p.h +++ b/src/p2p/p2p.h @@ -9,6 +9,18 @@ #ifndef P2P_H #define P2P_H +#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 */ @@ -50,7 +62,8 @@ struct p2p_channels { }; enum p2p_wps_method { - WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC + WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC, WPS_NFC, + WPS_P2PS }; /** @@ -77,6 +90,8 @@ struct p2p_go_neg_results { int ht40; + int vht; + /** * ssid - SSID of the group */ @@ -138,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 }; @@ -226,6 +329,19 @@ struct p2p_peer_info { * wfd_subelems - Wi-Fi Display subelements from WFD IE(s) */ struct wpabuf *wfd_subelems; + + /** + * vendor_elems - Unrecognized vendor elements + * + * This buffer includes any other vendor element than P2P, WPS, and WFD + * 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 { @@ -233,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 { @@ -263,6 +380,12 @@ struct p2p_config { u8 channel; /** + * channel_forced - the listen channel was forced by configuration + * or by control interface and cannot be overridden + */ + u8 channel_forced; + + /** * Regulatory class for own operational channel */ u8 op_reg_class; @@ -287,6 +410,20 @@ struct p2p_config { struct p2p_channels channels; /** + * cli_channels - Additional client channels + * + * This list of channels (if any) will be used when advertising local + * channels during GO Negotiation or Invitation for the cases where the + * local end may become the client. This may allow the peer to become a + * GO on additional channels if it supports these options. The main use + * case for this is to include passive-scan channels on devices that may + * not know their current location and have configured most channels to + * not allow initiation of radition (i.e., another device needs to take + * master responsibilities). + */ + struct p2p_channels cli_channels; + + /** * num_pref_chan - Number of pref_chan entries */ unsigned int num_pref_chan; @@ -371,15 +508,26 @@ struct p2p_config { unsigned int max_listen; /** - * msg_ctx - Context to use with wpa_msg() calls + * passphrase_len - Passphrase length (8..63) + * + * This parameter controls the length of the random passphrase that is + * generated at the GO. */ - void *msg_ctx; + unsigned int passphrase_len; /** * cb_ctx - Context to use with callback functions */ void *cb_ctx; + /** + * debug_print - Debug print + * @ctx: Callback context from cb_ctx + * @level: Debug verbosity level (MSG_*) + * @msg: Debug message + */ + void (*debug_print)(void *ctx, int level, const char *msg); + /* Callbacks to request lower layer driver operations */ @@ -398,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. * @@ -545,6 +694,12 @@ struct p2p_config { void (*dev_lost)(void *ctx, const u8 *dev_addr); /** + * find_stopped - Notification of a p2p_find operation stopping + * @ctx: Callback context from cb_ctx + */ + void (*find_stopped)(void *ctx); + + /** * go_neg_req_rx - Notification of a receive GO Negotiation Request * @ctx: Callback context from cb_ctx * @src: Source address of the message triggering this notification @@ -656,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. @@ -664,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 @@ -680,6 +840,9 @@ struct p2p_config { * @persistent_group: Whether this is an invitation to reinvoke a * persistent group (instead of invitation to join an active * group) + * @channels: Available operating channels for the group + * @dev_pw_id: Device Password ID for NFC static handover or -1 if not + * used * Returns: Status code (P2P_SC_*) * * This optional callback can be used to implement persistent reconnect @@ -700,7 +863,9 @@ struct p2p_config { u8 (*invitation_process)(void *ctx, const u8 *sa, const u8 *bssid, const u8 *go_dev_addr, const u8 *ssid, size_t ssid_len, int *go, u8 *group_bssid, - int *force_freq, int persistent_group); + int *force_freq, int persistent_group, + const struct p2p_channels *channels, + int dev_pw_id); /** * invitation_received - Callback on Invitation Request RX @@ -729,6 +894,11 @@ struct p2p_config { * @ctx: Callback context from cb_ctx * @status: Negotiation result (Status Code) * @bssid: P2P Group BSSID or %NULL if not received + * @channels: Available operating channels for the group + * @addr: Peer address + * @freq: Frequency (in MHz) indicated during invitation or 0 + * @peer_oper_freq: Operating frequency (in MHz) advertized by the peer + * during invitation or 0 * * This callback is used to indicate result of an Invitation procedure * started with a call to p2p_invite(). The indicated status code is @@ -736,7 +906,9 @@ struct p2p_config { * (P2P_SC_SUCCESS) indicating success or -1 to indicate a timeout or a * local failure in transmitting the Invitation Request. */ - void (*invitation_result)(void *ctx, int status, const u8 *bssid); + void (*invitation_result)(void *ctx, int status, const u8 *bssid, + const struct p2p_channels *channels, + const u8 *addr, int freq, int peer_oper_freq); /** * go_connected - Check whether we are connected to a GO @@ -746,6 +918,111 @@ struct p2p_config { * or 0 if not. */ int (*go_connected)(void *ctx, const u8 *dev_addr); + + /** + * presence_resp - Callback on Presence Response + * @ctx: Callback context from cb_ctx + * @src: Source address (GO's P2P Interface Address) + * @status: Result of the request (P2P_SC_*) + * @noa: Returned NoA value + * @noa_len: Length of the NoA buffer in octets + */ + void (*presence_resp)(void *ctx, const u8 *src, u8 status, + const u8 *noa, size_t noa_len); + + /** + * is_concurrent_session_active - Check whether concurrent session is + * active on other virtual interfaces + * @ctx: Callback context from cb_ctx + * Returns: 1 if concurrent session is active on other virtual interface + * or 0 if not. + */ + int (*is_concurrent_session_active)(void *ctx); + + /** + * is_p2p_in_progress - Check whether P2P operation is in progress + * @ctx: Callback context from cb_ctx + * Returns: 1 if P2P operation (e.g., group formation) is in progress + * 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); }; @@ -852,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) @@ -889,6 +1180,12 @@ void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq); int p2p_listen(struct p2p_data *p2p, unsigned int timeout); /** + * p2p_stop_listen - Stop P2P Listen + * @p2p: P2P module context from p2p_init() + */ +void p2p_stop_listen(struct p2p_data *p2p); + +/** * p2p_connect - Start P2P group formation (GO negotiation) * @p2p: P2P module context from p2p_init() * @peer_addr: MAC address of the peer P2P client @@ -914,7 +1211,7 @@ int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr, int go_intent, const u8 *own_interface_addr, unsigned int force_freq, int persistent_group, const u8 *force_ssid, size_t force_ssid_len, - int pd_before_go_neg, unsigned int pref_freq); + int pd_before_go_neg, unsigned int pref_freq, u16 oob_pw_id); /** * p2p_authorize - Authorize P2P group formation (GO negotiation) @@ -942,7 +1239,7 @@ int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr, int go_intent, const u8 *own_interface_addr, unsigned int force_freq, int persistent_group, const u8 *force_ssid, size_t force_ssid_len, - unsigned int pref_freq); + unsigned int pref_freq, u16 oob_pw_id); /** * p2p_reject - Reject peer device (explicitly block connection attempts) @@ -956,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) @@ -971,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); /** @@ -1042,12 +1341,16 @@ enum p2p_invite_role { * @force_freq: The only allowed channel frequency in MHz or 0 * @go_dev_addr: Forced GO Device Address or %NULL if none * @persistent_group: Whether this is to reinvoke a persistent group + * @pref_freq: Preferred operating frequency in MHz or 0 (this is only used if + * force_freq == 0) + * @dev_pw_id: Device Password ID from OOB Device Password (NFC) static handover + * case or -1 if not used * Returns: 0 on success, -1 on failure */ int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role, const u8 *bssid, const u8 *ssid, size_t ssid_len, unsigned int force_freq, const u8 *go_dev_addr, - int persistent_group); + int persistent_group, unsigned int pref_freq, int dev_pw_id); /** * p2p_presence_req - Request GO presence @@ -1183,7 +1486,7 @@ void p2p_rx_action(struct p2p_data *p2p, const u8 *da, const u8 *sa, * @p2p: P2P module context from p2p_init() * @bssid: BSSID of the scan result * @freq: Frequency of the channel on which the device was found in MHz - * @age: Age of the scan result in milliseconds + * @rx_time: Time when the result was received * @level: Signal level (signal strength of the received Beacon/Probe Response * frame) * @ies: Pointer to IEs from the scan result @@ -1205,7 +1508,7 @@ void p2p_rx_action(struct p2p_data *p2p, const u8 *da, const u8 *sa, * start of a pending operation, e.g., to start a pending GO negotiation. */ int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq, - unsigned int age, int level, const u8 *ies, + struct os_reltime *rx_time, int level, const u8 *ies, size_t ies_len); /** @@ -1312,6 +1615,11 @@ struct p2p_group_config { size_t ssid_len; /** + * freq - Operating channel of the group + */ + int freq; + + /** * cb_ctx - Context to use with callback functions */ void *cb_ctx; @@ -1587,7 +1895,24 @@ void p2p_set_client_discoverability(struct p2p_data *p2p, int enabled); */ void p2p_set_managed_oper(struct p2p_data *p2p, int enabled); -int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel); +/** + * p2p_config_get_random_social - Return a random social channel + * @p2p: P2P config + * @op_class: Selected operating class + * @op_channel: Selected social channel + * Returns: 0 on success, -1 on failure + * + * This function is used before p2p_init is called. A random social channel + * from supports bands 2.4 GHz (channels 1,6,11) and 60 GHz (channel 2) is + * returned on success. + */ +int p2p_config_get_random_social(struct p2p_config *p2p, u8 *op_class, + u8 *op_channel); + +int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel, + u8 forced); + +u8 p2p_get_listen_channel(struct p2p_data *p2p); int p2p_set_ssid_postfix(struct p2p_data *p2p, const u8 *postfix, size_t len); @@ -1614,6 +1939,12 @@ int p2p_get_oper_freq(struct p2p_data *p2p, const u8 *iface_addr); */ 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() @@ -1622,7 +1953,34 @@ void p2p_set_intra_bss_dist(struct p2p_data *p2p, int enabled); */ int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq); -void p2p_update_channel_list(struct p2p_data *p2p, struct p2p_channels *chan); +/** + * p2p_supported_freq_go - Check whether channel is supported for P2P GO operation + * @p2p: P2P module context from p2p_init() + * @freq: Channel frequency in MHz + * Returns: 0 if channel not usable for P2P, 1 if usable for P2P + */ +int p2p_supported_freq_go(struct p2p_data *p2p, unsigned int freq); + +/** + * p2p_supported_freq_cli - Check whether channel is supported for P2P client operation + * @p2p: P2P module context from p2p_init() + * @freq: Channel frequency in MHz + * Returns: 0 if channel not usable for P2P, 1 if usable for P2P + */ +int p2p_supported_freq_cli(struct p2p_data *p2p, unsigned int freq); + +/** + * p2p_get_pref_freq - Get channel from preferred channel list + * @p2p: P2P module context from p2p_init() + * @channels: List of channels + * Returns: Preferred channel + */ +unsigned int p2p_get_pref_freq(struct p2p_data *p2p, + const struct p2p_channels *channels); + +void p2p_update_channel_list(struct p2p_data *p2p, + const struct p2p_channels *chan, + const struct p2p_channels *cli_chan); /** * p2p_set_best_channels - Update best channel information @@ -1634,6 +1992,17 @@ void p2p_update_channel_list(struct p2p_data *p2p, struct p2p_channels *chan); void p2p_set_best_channels(struct p2p_data *p2p, int freq_24, int freq_5, int freq_overall); +/** + * p2p_set_own_freq_preference - Set own preference for channel + * @p2p: P2P module context from p2p_init() + * @freq: Frequency (MHz) of the preferred channel or 0 if no preference + * + * This function can be used to set a preference on the operating channel based + * on frequencies used on the other virtual interfaces that share the same + * radio. If non-zero, this is used to try to avoid multi-channel concurrency. + */ +void p2p_set_own_freq_preference(struct p2p_data *p2p, int freq); + const u8 * p2p_get_go_neg_peer(struct p2p_data *p2p); /** @@ -1644,11 +2013,18 @@ 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 * on the first call and not modified later - * Returns: A P2P Interface Address for each call and %NULL for no more members + * Returns: A P2P Device Address for each call and %NULL for no more members */ const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next); @@ -1670,6 +2046,26 @@ const u8 * p2p_group_get_dev_addr(struct p2p_group *group, const u8 *addr); int p2p_group_is_client_connected(struct p2p_group *group, const u8 *dev_addr); /** + * p2p_group_get_config - Get the group configuration + * @group: P2P group context from p2p_group_init() + * Returns: The group configuration pointer + */ +const struct p2p_group_config * p2p_group_get_config(struct p2p_group *group); + +/** + * p2p_loop_on_all_groups - Run the given callback on all groups + * @p2p: P2P module context from p2p_init() + * @group_callback: The callback function pointer + * @user_data: Some user data pointer which can be %NULL + * + * The group_callback function can stop the iteration by returning 0. + */ +void p2p_loop_on_all_groups(struct p2p_data *p2p, + int (*group_callback)(struct p2p_group *group, + void *user_data), + void *user_data); + +/** * p2p_get_peer_found - Get P2P peer info structure of a found peer * @p2p: P2P module context from p2p_init() * @addr: P2P Device Address of the peer or %NULL to indicate the first peer @@ -1719,18 +2115,21 @@ int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan, const struct p2p_channel *pref_chan); /** - * p2p_in_progress - Check whether a P2P operation is progress + * p2p_set_no_go_freq - Set no GO channel ranges * @p2p: P2P module context from p2p_init() - * Returns: 0 if P2P module is idle or 1 if an operation is in progress + * @list: Channel ranges or %NULL to remove restriction + * Returns: 0 on success, -1 on failure */ -int p2p_in_progress(struct p2p_data *p2p); +int p2p_set_no_go_freq(struct p2p_data *p2p, + const struct wpa_freq_range_list *list); /** - * p2p_other_scan_completed - Notify completion of non-P2P scan + * 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 was started + * 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_other_scan_completed(struct p2p_data *p2p); +int p2p_in_progress(struct p2p_data *p2p); const char * p2p_wps_method_text(enum p2p_wps_method method); @@ -1743,8 +2142,6 @@ const char * p2p_wps_method_text(enum p2p_wps_method method); void p2p_set_config_timeout(struct p2p_data *p2p, u8 go_timeout, u8 client_timeout); -void p2p_increase_search_delay(struct p2p_data *p2p, unsigned int delay); - int p2p_set_wfd_ie_beacon(struct p2p_data *p2p, struct wpabuf *ie); int p2p_set_wfd_ie_probe_req(struct p2p_data *p2p, struct wpabuf *ie); int p2p_set_wfd_ie_probe_resp(struct p2p_data *p2p, struct wpabuf *ie); @@ -1780,4 +2177,71 @@ struct wpabuf * wifi_display_encaps(struct wpabuf *subelems); int p2p_set_disc_int(struct p2p_data *p2p, int min_disc_int, int max_disc_int, int max_disc_tu); +/** + * p2p_get_state_txt - Get current P2P state for debug purposes + * @p2p: P2P module context from p2p_init() + * Returns: Name of the current P2P module state + * + * It should be noted that the P2P module state names are internal information + * and subject to change at any point, i.e., this information should be used + * mainly for debugging purposes. + */ +const char * p2p_get_state_txt(struct p2p_data *p2p); + +struct wpabuf * p2p_build_nfc_handover_req(struct p2p_data *p2p, + int client_freq, + const u8 *go_dev_addr, + const u8 *ssid, size_t ssid_len); +struct wpabuf * p2p_build_nfc_handover_sel(struct p2p_data *p2p, + int client_freq, + const u8 *go_dev_addr, + const u8 *ssid, size_t ssid_len); + +struct p2p_nfc_params { + int sel; + const u8 *wsc_attr; + size_t wsc_len; + const u8 *p2p_attr; + size_t p2p_len; + + enum { + NO_ACTION, JOIN_GROUP, AUTH_JOIN, INIT_GO_NEG, RESP_GO_NEG, + BOTH_GO, PEER_CLIENT + } next_step; + struct p2p_peer_info *peer; + u8 oob_dev_pw[WPS_OOB_PUBKEY_HASH_LEN + 2 + + WPS_OOB_DEVICE_PASSWORD_LEN]; + size_t oob_dev_pw_len; + int go_freq; + u8 go_dev_addr[ETH_ALEN]; + u8 go_ssid[32]; + size_t go_ssid_len; +}; + +int p2p_process_nfc_connection_handover(struct p2p_data *p2p, + struct p2p_nfc_params *params); + +void p2p_set_authorized_oob_dev_pw_id(struct p2p_data *p2p, u16 dev_pw_id, + int go_intent, + const u8 *own_interface_addr); + +int p2p_set_passphrase_len(struct p2p_data *p2p, unsigned int len); + +void p2p_loop_on_known_peers(struct p2p_data *p2p, + void (*peer_callback)(struct p2p_peer_info *peer, + void *user_data), + void *user_data); + +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 5838d35e977ee..92c920662edb3 100644 --- a/src/p2p/p2p_build.c +++ b/src/p2p/p2p_build.c @@ -17,8 +17,7 @@ void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token) { wpabuf_put_u8(buf, WLAN_ACTION_VENDOR_SPECIFIC); - wpabuf_put_be24(buf, OUI_WFA); - wpabuf_put_u8(buf, P2P_OUI_TYPE); + wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE); wpabuf_put_u8(buf, subtype); /* OUI Subtype */ wpabuf_put_u8(buf, dialog_token); @@ -31,8 +30,7 @@ void p2p_buf_add_public_action_hdr(struct wpabuf *buf, u8 subtype, { wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC); wpabuf_put_u8(buf, WLAN_PA_VENDOR_SPECIFIC); - wpabuf_put_be24(buf, OUI_WFA); - wpabuf_put_u8(buf, P2P_OUI_TYPE); + wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE); wpabuf_put_u8(buf, subtype); /* OUI Subtype */ wpabuf_put_u8(buf, dialog_token); @@ -47,8 +45,7 @@ u8 * p2p_buf_add_ie_hdr(struct wpabuf *buf) /* P2P IE header */ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); len = wpabuf_put(buf, 1); /* IE length to be filled */ - wpabuf_put_be24(buf, OUI_WFA); - wpabuf_put_u8(buf, P2P_OUI_TYPE); + wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE); wpa_printf(MSG_DEBUG, "P2P: * P2P IE header"); return len; } @@ -167,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); @@ -258,6 +258,7 @@ void p2p_buf_add_group_id(struct wpabuf *buf, const u8 *dev_addr, wpabuf_put_data(buf, ssid, ssid_len); wpa_printf(MSG_DEBUG, "P2P: * P2P Group ID " MACSTR, MAC2STR(dev_addr)); + wpa_hexdump_ascii(MSG_DEBUG, "P2P: P2P Group ID SSID", ssid, ssid_len); } @@ -327,13 +328,282 @@ void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p) } -static void p2p_add_wps_string(struct wpabuf *buf, enum wps_attribute attr, - const char *val) +void p2p_buf_add_oob_go_neg_channel(struct wpabuf *buf, const char *country, + u8 oper_class, u8 channel, + enum p2p_role_indication role) +{ + /* OOB Group Owner Negotiation Channel */ + wpabuf_put_u8(buf, P2P_ATTR_OOB_GO_NEG_CHANNEL); + wpabuf_put_le16(buf, 6); + wpabuf_put_data(buf, country, 3); + wpabuf_put_u8(buf, oper_class); /* Operating Class */ + wpabuf_put_u8(buf, channel); /* Channel Number */ + wpabuf_put_u8(buf, (u8) role); /* Role indication */ + wpa_printf(MSG_DEBUG, "P2P: * OOB GO Negotiation Channel: Operating " + "Class %u Channel %u Role %d", + oper_class, channel, role); +} + + +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) { size_t len; - wpabuf_put_be16(buf, attr); len = val ? os_strlen(val) : 0; + if (wpabuf_tailroom(buf) < 4 + len) + return -1; + wpabuf_put_be16(buf, attr); #ifndef CONFIG_WPS_STRICT if (len == 0) { /* @@ -341,36 +611,46 @@ static void p2p_add_wps_string(struct wpabuf *buf, enum wps_attribute attr, * attributes. As a workaround, send a space character if the * device attribute string is empty. */ + if (wpabuf_tailroom(buf) < 3) + return -1; wpabuf_put_be16(buf, 1); wpabuf_put_u8(buf, ' '); - return; + return 0; } #endif /* CONFIG_WPS_STRICT */ wpabuf_put_be16(buf, len); if (val) wpabuf_put_data(buf, val, len); + return 0; } -void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id, - int all_attr) +int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id, + int all_attr) { u8 *len; int i; + if (wpabuf_tailroom(buf) < 6) + return -1; wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); len = wpabuf_put(buf, 1); wpabuf_put_be32(buf, WPS_DEV_OUI_WFA); - wps_build_version(buf); + if (wps_build_version(buf) < 0) + return -1; if (all_attr) { + if (wpabuf_tailroom(buf) < 5) + return -1; wpabuf_put_be16(buf, ATTR_WPS_STATE); wpabuf_put_be16(buf, 1); wpabuf_put_u8(buf, WPS_STATE_NOT_CONFIGURED); } if (pw_id >= 0) { + if (wpabuf_tailroom(buf) < 6) + return -1; /* Device Password ID */ wpabuf_put_be16(buf, ATTR_DEV_PASSWORD_ID); wpabuf_put_be16(buf, 2); @@ -380,33 +660,47 @@ void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id, } if (all_attr) { + if (wpabuf_tailroom(buf) < 5) + return -1; wpabuf_put_be16(buf, ATTR_RESPONSE_TYPE); wpabuf_put_be16(buf, 1); wpabuf_put_u8(buf, WPS_RESP_ENROLLEE_INFO); - wps_build_uuid_e(buf, p2p->cfg->uuid); - p2p_add_wps_string(buf, ATTR_MANUFACTURER, - p2p->cfg->manufacturer); - p2p_add_wps_string(buf, ATTR_MODEL_NAME, p2p->cfg->model_name); - p2p_add_wps_string(buf, ATTR_MODEL_NUMBER, - p2p->cfg->model_number); - p2p_add_wps_string(buf, ATTR_SERIAL_NUMBER, - p2p->cfg->serial_number); - + if (wps_build_uuid_e(buf, p2p->cfg->uuid) < 0 || + p2p_add_wps_string(buf, ATTR_MANUFACTURER, + p2p->cfg->manufacturer) < 0 || + p2p_add_wps_string(buf, ATTR_MODEL_NAME, + p2p->cfg->model_name) < 0 || + p2p_add_wps_string(buf, ATTR_MODEL_NUMBER, + p2p->cfg->model_number) < 0 || + p2p_add_wps_string(buf, ATTR_SERIAL_NUMBER, + p2p->cfg->serial_number) < 0) + return -1; + + if (wpabuf_tailroom(buf) < 4 + WPS_DEV_TYPE_LEN) + return -1; wpabuf_put_be16(buf, ATTR_PRIMARY_DEV_TYPE); wpabuf_put_be16(buf, WPS_DEV_TYPE_LEN); wpabuf_put_data(buf, p2p->cfg->pri_dev_type, WPS_DEV_TYPE_LEN); - p2p_add_wps_string(buf, ATTR_DEV_NAME, p2p->cfg->dev_name); + if (p2p_add_wps_string(buf, ATTR_DEV_NAME, p2p->cfg->dev_name) + < 0) + return -1; + if (wpabuf_tailroom(buf) < 6) + return -1; wpabuf_put_be16(buf, ATTR_CONFIG_METHODS); wpabuf_put_be16(buf, 2); wpabuf_put_be16(buf, p2p->cfg->config_methods); } - wps_build_wfa_ext(buf, 0, NULL, 0); + if (wps_build_wfa_ext(buf, 0, NULL, 0) < 0) + return -1; if (all_attr && p2p->cfg->num_sec_dev_types) { + if (wpabuf_tailroom(buf) < + 4 + WPS_DEV_TYPE_LEN * p2p->cfg->num_sec_dev_types) + return -1; wpabuf_put_be16(buf, ATTR_SECONDARY_DEV_TYPE_LIST); wpabuf_put_be16(buf, WPS_DEV_TYPE_LEN * p2p->cfg->num_sec_dev_types); @@ -428,4 +722,6 @@ void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id, } p2p_buf_update_ie_hdr(buf, len); + + return 0; } diff --git a/src/p2p/p2p_dev_disc.c b/src/p2p/p2p_dev_disc.c index c976b7c6d62b2..86bae1a2c0db8 100644 --- a/src/p2p/p2p_dev_disc.c +++ b/src/p2p/p2p_dev_disc.c @@ -42,8 +42,7 @@ static struct wpabuf * p2p_build_dev_disc_req(struct p2p_data *p2p, void p2p_dev_disc_req_cb(struct p2p_data *p2p, int success) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Device Discoverability Request TX callback: success=%d", + p2p_dbg(p2p, "Device Discoverability Request TX callback: success=%d", success); if (!success) { @@ -56,9 +55,7 @@ void p2p_dev_disc_req_cb(struct p2p_data *p2p, int success) return; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: GO acknowledged Device Discoverability Request - wait " - "for response"); + p2p_dbg(p2p, "GO acknowledged Device Discoverability Request - wait for response"); /* * TODO: is the remain-on-channel from Action frame TX long enough for * most cases or should we try to increase its duration and/or start @@ -71,12 +68,11 @@ int p2p_send_dev_disc_req(struct p2p_data *p2p, struct p2p_device *dev) { struct p2p_device *go; struct wpabuf *req; + unsigned int wait_time; go = p2p_get_device(p2p, dev->member_in_go_dev); if (go == NULL || dev->oper_freq <= 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Could not find peer entry for GO and frequency " - "to send Device Discoverability Request"); + p2p_dbg(p2p, "Could not find peer entry for GO and frequency to send Device Discoverability Request"); return -1; } @@ -84,8 +80,7 @@ int p2p_send_dev_disc_req(struct p2p_data *p2p, struct p2p_device *dev) if (req == NULL) return -1; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Sending Device Discoverability Request to GO " MACSTR + p2p_dbg(p2p, "Sending Device Discoverability Request to GO " MACSTR " for client " MACSTR, MAC2STR(go->info.p2p_device_addr), MAC2STR(dev->info.p2p_device_addr)); @@ -94,11 +89,13 @@ int p2p_send_dev_disc_req(struct p2p_data *p2p, struct p2p_device *dev) os_memcpy(p2p->pending_client_disc_addr, dev->info.p2p_device_addr, ETH_ALEN); p2p->pending_action_state = P2P_PENDING_DEV_DISC_REQUEST; + wait_time = 1000; + if (p2p->cfg->max_listen && wait_time > p2p->cfg->max_listen) + wait_time = p2p->cfg->max_listen; if (p2p_send_action(p2p, dev->oper_freq, go->info.p2p_device_addr, p2p->cfg->dev_addr, go->info.p2p_device_addr, - wpabuf_head(req), wpabuf_len(req), 1000) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + wpabuf_head(req), wpabuf_len(req), wait_time) < 0) { + p2p_dbg(p2p, "Failed to send Action frame"); wpabuf_free(req); /* TODO: how to recover from failure? */ return -1; @@ -131,8 +128,7 @@ static struct wpabuf * p2p_build_dev_disc_resp(u8 dialog_token, u8 status) void p2p_dev_disc_resp_cb(struct p2p_data *p2p, int success) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Device Discoverability Response TX callback: success=%d", + p2p_dbg(p2p, "Device Discoverability Response TX callback: success=%d", success); p2p->cfg->send_action_done(p2p->cfg->cb_ctx); } @@ -147,8 +143,7 @@ static void p2p_send_dev_disc_resp(struct p2p_data *p2p, u8 dialog_token, if (resp == NULL) return; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Sending Device Discoverability Response to " MACSTR + p2p_dbg(p2p, "Sending Device Discoverability Response to " MACSTR " (status %u freq %d)", MAC2STR(addr), status, freq); @@ -156,8 +151,7 @@ static void p2p_send_dev_disc_resp(struct p2p_data *p2p, u8 dialog_token, if (p2p_send_action(p2p, freq, addr, p2p->cfg->dev_addr, p2p->cfg->dev_addr, wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + p2p_dbg(p2p, "Failed to send Action frame"); } wpabuf_free(resp); @@ -170,17 +164,14 @@ void p2p_process_dev_disc_req(struct p2p_data *p2p, const u8 *sa, struct p2p_message msg; size_t g; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received Device Discoverability Request from " MACSTR + p2p_dbg(p2p, "Received Device Discoverability Request from " MACSTR " (freq=%d)", MAC2STR(sa), rx_freq); if (p2p_parse(data, len, &msg)) return; if (msg.dialog_token == 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invalid Dialog Token 0 (must be nonzero) in " - "Device Discoverability Request"); + p2p_dbg(p2p, "Invalid Dialog Token 0 (must be nonzero) in Device Discoverability Request"); p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq, P2P_SC_FAIL_INVALID_PARAMS); p2p_parse_free(&msg); @@ -188,9 +179,7 @@ void p2p_process_dev_disc_req(struct p2p_data *p2p, const u8 *sa, } if (msg.device_id == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: P2P Device ID attribute missing from Device " - "Discoverability Request"); + p2p_dbg(p2p, "P2P Device ID attribute missing from Device Discoverability Request"); p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq, P2P_SC_FAIL_INVALID_PARAMS); p2p_parse_free(&msg); @@ -200,9 +189,7 @@ void p2p_process_dev_disc_req(struct p2p_data *p2p, const u8 *sa, for (g = 0; g < p2p->num_groups; g++) { if (p2p_group_go_discover(p2p->groups[g], msg.device_id, sa, rx_freq) == 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Scheduled " - "GO Discoverability Request for the target " - "device"); + p2p_dbg(p2p, "Scheduled GO Discoverability Request for the target device"); /* * P2P group code will use a callback to indicate TX * status, so that we can reply to the request once the @@ -217,9 +204,7 @@ void p2p_process_dev_disc_req(struct p2p_data *p2p, const u8 *sa, } } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Requested client " - "was not found in any group or did not support client " - "discoverability"); + p2p_dbg(p2p, "Requested client was not found in any group or did not support client discoverability"); p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq, P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE); p2p_parse_free(&msg); @@ -233,15 +218,13 @@ void p2p_process_dev_disc_resp(struct p2p_data *p2p, const u8 *sa, struct p2p_device *go; u8 status; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received Device Discoverability Response from " MACSTR, + p2p_dbg(p2p, "Received Device Discoverability Response from " MACSTR, MAC2STR(sa)); go = p2p->pending_client_disc_go; if (go == NULL || os_memcmp(sa, go->info.p2p_device_addr, ETH_ALEN) != 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Ignore unexpected " - "Device Discoverability Response"); + p2p_dbg(p2p, "Ignore unexpected Device Discoverability Response"); return; } @@ -254,9 +237,7 @@ void p2p_process_dev_disc_resp(struct p2p_data *p2p, const u8 *sa, } if (msg.dialog_token != go->dialog_token) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Ignore Device " - "Discoverability Response with unexpected dialog " - "token %u (expected %u)", + p2p_dbg(p2p, "Ignore Device Discoverability Response with unexpected dialog token %u (expected %u)", msg.dialog_token, go->dialog_token); p2p_parse_free(&msg); return; @@ -265,17 +246,14 @@ void p2p_process_dev_disc_resp(struct p2p_data *p2p, const u8 *sa, status = *msg.status; p2p_parse_free(&msg); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Device Discoverability Response status %u", status); + p2p_dbg(p2p, "Device Discoverability Response status %u", status); if (p2p->go_neg_peer == NULL || os_memcmp(p2p->pending_client_disc_addr, p2p->go_neg_peer->info.p2p_device_addr, ETH_ALEN) != 0 || os_memcmp(p2p->go_neg_peer->member_in_go_dev, go->info.p2p_device_addr, ETH_ALEN) != 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending " - "operation with the client discoverability peer " - "anymore"); + p2p_dbg(p2p, "No pending operation with the client discoverability peer anymore"); return; } @@ -284,8 +262,7 @@ void p2p_process_dev_disc_resp(struct p2p_data *p2p, const u8 *sa, * Peer is expected to be awake for at least 100 TU; try to * connect immediately. */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Client discoverability request succeeded"); + p2p_dbg(p2p, "Client discoverability request succeeded"); if (p2p->state == P2P_CONNECT) { /* * Change state to force the timeout to start in @@ -301,8 +278,7 @@ void p2p_process_dev_disc_resp(struct p2p_data *p2p, const u8 *sa, * Client discoverability request failed; try to connect from * timeout. */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Client discoverability request failed"); + p2p_dbg(p2p, "Client discoverability request failed"); p2p_set_timeout(p2p, 0, 500000); } @@ -311,14 +287,12 @@ void p2p_process_dev_disc_resp(struct p2p_data *p2p, const u8 *sa, void p2p_go_disc_req_cb(struct p2p_data *p2p, int success) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: GO Discoverability Request TX callback: success=%d", + p2p_dbg(p2p, "GO Discoverability Request TX callback: success=%d", success); p2p->cfg->send_action_done(p2p->cfg->cb_ctx); if (p2p->pending_dev_disc_dialog_token == 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending Device " - "Discoverability Request"); + p2p_dbg(p2p, "No pending Device Discoverability Request"); return; } @@ -338,9 +312,7 @@ void p2p_process_go_disc_req(struct p2p_data *p2p, const u8 *da, const u8 *sa, unsigned int tu; struct wpabuf *ies; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received GO Discoverability Request - remain awake for " - "100 TU"); + p2p_dbg(p2p, "Received GO Discoverability Request - remain awake for 100 TU"); ies = p2p_build_probe_resp_ies(p2p); if (ies == NULL) @@ -351,9 +323,7 @@ void p2p_process_go_disc_req(struct p2p_data *p2p, const u8 *da, const u8 *sa, tu = 100; if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, rx_freq, 1024 * tu / 1000, ies) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to start listen mode for client " - "discoverability"); + p2p_dbg(p2p, "Failed to start listen mode for client discoverability"); } wpabuf_free(ies); } diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c index 2fdc47fc5d0f2..98abf9d2293e9 100644 --- a/src/p2p/p2p_go_neg.c +++ b/src/p2p/p2p_go_neg.c @@ -9,7 +9,9 @@ #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" #include "p2p_i.h" #include "p2p.h" @@ -49,8 +51,7 @@ int p2p_peer_channels_check(struct p2p_data *p2p, struct p2p_channels *own, os_memcpy(dev->country, pos, 3); wpa_hexdump_ascii(MSG_DEBUG, "P2P: Peer country", pos, 3); if (pos[2] != 0x04 && os_memcmp(pos, p2p->cfg->country, 2) != 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, - "P2P: Mismatching country (ours=%c%c peer's=%c%c)", + p2p_info(p2p, "Mismatching country (ours=%c%c peer's=%c%c)", p2p->cfg->country[0], p2p->cfg->country[1], pos[0], pos[1]); return -1; @@ -61,8 +62,7 @@ int p2p_peer_channels_check(struct p2p_data *p2p, struct p2p_channels *own, struct p2p_reg_class *cl = &ch->reg_class[ch->reg_classes]; cl->reg_class = *pos++; if (pos + 1 + pos[0] > end) { - wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, - "P2P: Invalid peer Channel List"); + p2p_info(p2p, "Invalid peer Channel List"); return -1; } channels = *pos++; @@ -76,14 +76,12 @@ int p2p_peer_channels_check(struct p2p_data *p2p, struct p2p_channels *own, } p2p_channels_intersect(own, &dev->channels, &intersection); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Own reg_classes %d " - "peer reg_classes %d intersection reg_classes %d", + p2p_dbg(p2p, "Own reg_classes %d peer reg_classes %d intersection reg_classes %d", (int) own->reg_classes, (int) dev->channels.reg_classes, (int) intersection.reg_classes); if (intersection.reg_classes == 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_INFO, - "P2P: No common channels found"); + p2p_info(p2p, "No common channels found"); return -1; } return 0; @@ -107,6 +105,10 @@ u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method) return DEV_PW_USER_SPECIFIED; case WPS_PBC: 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; } @@ -122,6 +124,10 @@ static const char * p2p_wps_method_str(enum p2p_wps_method wps_method) return "Keypad"; case WPS_PBC: return "PBC"; + case WPS_NFC: + return "NFC"; + case WPS_P2PS: + return "P2PS"; default: return "??"; } @@ -135,19 +141,20 @@ static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p, u8 *len; u8 group_capab; size_t extra = 0; + u16 pw_id; #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_go_neg) extra = wpabuf_len(p2p->wfd_ie_go_neg); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ]) + extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ]); + buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; - peer->dialog_token++; - if (peer->dialog_token == 0) - peer->dialog_token = 1; p2p_buf_add_public_action_hdr(buf, P2P_GO_NEG_REQ, peer->dialog_token); len = p2p_buf_add_ie_hdr(buf); @@ -164,9 +171,7 @@ static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p, p2p_buf_add_capability(buf, p2p->dev_capab & ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, group_capab); - p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | - p2p->next_tie_breaker); - p2p->next_tie_breaker = !p2p->next_tie_breaker; + p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | peer->tie_breaker); 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); @@ -181,13 +186,23 @@ static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p, p2p_buf_update_ie_hdr(buf, len); /* WPS IE with Device Password ID attribute */ - p2p_build_wps_ie(p2p, buf, p2p_wps_method_pw_id(peer->wps_method), 0); + pw_id = p2p_wps_method_pw_id(peer->wps_method); + if (peer->oob_pw_id) + pw_id = peer->oob_pw_id; + if (p2p_build_wps_ie(p2p, buf, pw_id, 0) < 0) { + p2p_dbg(p2p, "Failed to build WPS IE for GO Negotiation Request"); + wpabuf_free(buf); + return NULL; + } #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_go_neg) wpabuf_put_buf(buf, p2p->wfd_ie_go_neg); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ]) + wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ]); + return buf; } @@ -199,8 +214,7 @@ int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev) if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG) { u16 config_method; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Use PD-before-GO-Neg workaround for " MACSTR, + p2p_dbg(p2p, "Use PD-before-GO-Neg workaround for " MACSTR, MAC2STR(dev->info.p2p_device_addr)); if (dev->wps_method == WPS_PIN_DISPLAY) config_method = WPS_CONFIG_KEYPAD; @@ -208,17 +222,20 @@ 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; + if (dev->oob_go_neg_freq > 0) + freq = dev->oob_go_neg_freq; if (freq <= 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Listen/Operating frequency known for the " - "peer " MACSTR " to send GO Negotiation Request", + p2p_dbg(p2p, "No Listen/Operating frequency known for the peer " + MACSTR " to send GO Negotiation Request", MAC2STR(dev->info.p2p_device_addr)); return -1; } @@ -226,18 +243,17 @@ int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev) req = p2p_build_go_neg_req(p2p, dev); if (req == NULL) return -1; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Sending GO Negotiation Request"); + p2p_dbg(p2p, "Sending GO Negotiation Request"); 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, p2p->cfg->dev_addr, dev->info.p2p_device_addr, - wpabuf_head(req), wpabuf_len(req), 200) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + wpabuf_head(req), wpabuf_len(req), 500) < 0) { + p2p_dbg(p2p, "Failed to send Action frame"); /* Use P2P find to recover and retry */ p2p_set_timeout(p2p, 0, 0); } else @@ -258,15 +274,18 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p, u8 *len; u8 group_capab; size_t extra = 0; + u16 pw_id; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Building GO Negotiation Response"); + p2p_dbg(p2p, "Building GO Negotiation Response"); #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_go_neg) extra = wpabuf_len(p2p->wfd_ie_go_neg); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP]) + extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP]); + buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; @@ -294,8 +313,7 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p, p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | tie_breaker); p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout); if (peer && peer->go_state == REMOTE_GO) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Omit Operating " - "Channel attribute"); + p2p_dbg(p2p, "Omit Operating Channel attribute"); } else { p2p_buf_add_operating_channel(buf, p2p->cfg->country, p2p->op_reg_class, @@ -322,15 +340,22 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p, p2p_buf_update_ie_hdr(buf, len); /* WPS IE with Device Password ID attribute */ - p2p_build_wps_ie(p2p, buf, - p2p_wps_method_pw_id(peer ? peer->wps_method : - WPS_NOT_READY), 0); + pw_id = p2p_wps_method_pw_id(peer ? peer->wps_method : WPS_NOT_READY); + if (peer && peer->oob_pw_id) + pw_id = peer->oob_pw_id; + if (p2p_build_wps_ie(p2p, buf, pw_id, 0) < 0) { + p2p_dbg(p2p, "Failed to build WPS IE for GO Negotiation Response"); + wpabuf_free(buf); + return NULL; + } #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_go_neg) wpabuf_put_buf(buf, p2p->wfd_ie_go_neg); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP]) + wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP]); return buf; } @@ -354,16 +379,41 @@ void p2p_reselect_channel(struct p2p_data *p2p, int freq; u8 op_reg_class, op_channel; unsigned int i; + const int op_classes_5ghz[] = { 124, 115, 0 }; + const int op_classes_ht40[] = { 126, 127, 116, 117, 0 }; + const int op_classes_vht[] = { 128, 0 }; + + if (p2p->own_freq_preference > 0 && + p2p_freq_to_channel(p2p->own_freq_preference, + &op_reg_class, &op_channel) == 0 && + p2p_channels_includes(intersection, op_reg_class, op_channel)) { + p2p_dbg(p2p, "Pick own channel preference (reg_class %u channel %u) from intersection", + op_reg_class, op_channel); + p2p->op_reg_class = op_reg_class; + p2p->op_channel = op_channel; + return; + } + + if (p2p->best_freq_overall > 0 && + p2p_freq_to_channel(p2p->best_freq_overall, + &op_reg_class, &op_channel) == 0 && + p2p_channels_includes(intersection, op_reg_class, op_channel)) { + p2p_dbg(p2p, "Pick best overall channel (reg_class %u channel %u) from intersection", + op_reg_class, op_channel); + p2p->op_reg_class = op_reg_class; + p2p->op_channel = op_channel; + return; + } /* First, try to pick the best channel from another band */ - freq = p2p_channel_to_freq(p2p->cfg->country, p2p->op_reg_class, - p2p->op_channel); + freq = p2p_channel_to_freq(p2p->op_reg_class, p2p->op_channel); if (freq >= 2400 && freq < 2500 && p2p->best_freq_5 > 0 && - p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_5, + !p2p_channels_includes(intersection, p2p->op_reg_class, + p2p->op_channel) && + p2p_freq_to_channel(p2p->best_freq_5, &op_reg_class, &op_channel) == 0 && p2p_channels_includes(intersection, op_reg_class, op_channel)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Pick best 5 GHz " - "channel (reg_class %u channel %u) from intersection", + p2p_dbg(p2p, "Pick best 5 GHz channel (reg_class %u channel %u) from intersection", op_reg_class, op_channel); p2p->op_reg_class = op_reg_class; p2p->op_channel = op_channel; @@ -371,11 +421,12 @@ void p2p_reselect_channel(struct p2p_data *p2p, } if (freq >= 4900 && freq < 6000 && p2p->best_freq_24 > 0 && - p2p_freq_to_channel(p2p->cfg->country, p2p->best_freq_24, + !p2p_channels_includes(intersection, p2p->op_reg_class, + p2p->op_channel) && + p2p_freq_to_channel(p2p->best_freq_24, &op_reg_class, &op_channel) == 0 && p2p_channels_includes(intersection, op_reg_class, op_channel)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Pick best 2.4 GHz " - "channel (reg_class %u channel %u) from intersection", + p2p_dbg(p2p, "Pick best 2.4 GHz channel (reg_class %u channel %u) from intersection", op_reg_class, op_channel); p2p->op_reg_class = op_reg_class; p2p->op_channel = op_channel; @@ -389,27 +440,34 @@ void p2p_reselect_channel(struct p2p_data *p2p, p2p->cfg->pref_chan[i].chan)) { p2p->op_reg_class = p2p->cfg->pref_chan[i].op_class; p2p->op_channel = p2p->cfg->pref_chan[i].chan; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Pick " - "highest preferred chnnel (op_class %u " - "channel %u) from intersection", + p2p_dbg(p2p, "Pick highest preferred channel (op_class %u channel %u) from intersection", p2p->op_reg_class, p2p->op_channel); return; } } + /* Try a channel where we might be able to use VHT */ + if (p2p_channel_select(intersection, op_classes_vht, + &p2p->op_reg_class, &p2p->op_channel) == 0) { + p2p_dbg(p2p, "Pick possible VHT channel (op_class %u channel %u) from intersection", + p2p->op_reg_class, p2p->op_channel); + return; + } + /* Try a channel where we might be able to use HT40 */ - for (i = 0; i < intersection->reg_classes; i++) { - struct p2p_reg_class *c = &intersection->reg_class[i]; - if (c->reg_class == 116 || c->reg_class == 117 || - c->reg_class == 126 || c->reg_class == 127) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Pick possible HT40 channel (reg_class " - "%u channel %u) from intersection", - c->reg_class, c->channel[0]); - p2p->op_reg_class = c->reg_class; - p2p->op_channel = c->channel[0]; - return; - } + if (p2p_channel_select(intersection, op_classes_ht40, + &p2p->op_reg_class, &p2p->op_channel) == 0) { + p2p_dbg(p2p, "Pick possible HT40 channel (op_class %u channel %u) from intersection", + p2p->op_reg_class, p2p->op_channel); + return; + } + + /* Prefer a 5 GHz channel */ + if (p2p_channel_select(intersection, op_classes_5ghz, + &p2p->op_reg_class, &p2p->op_channel) == 0) { + p2p_dbg(p2p, "Pick possible 5 GHz channel (op_class %u channel %u) from intersection", + p2p->op_reg_class, p2p->op_channel); + return; } /* @@ -419,9 +477,7 @@ void p2p_reselect_channel(struct p2p_data *p2p, */ if (p2p_channels_includes(intersection, p2p->op_reg_class, p2p->op_channel)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Using original operating class and channel " - "(op_class %u channel %u) from intersection", + p2p_dbg(p2p, "Using original operating class and channel (op_class %u channel %u) from intersection", p2p->op_reg_class, p2p->op_channel); return; } @@ -431,55 +487,48 @@ void p2p_reselect_channel(struct p2p_data *p2p, * no better options seems to be available. */ cl = &intersection->reg_class[0]; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Pick another channel " - "(reg_class %u channel %u) from intersection", + p2p_dbg(p2p, "Pick another channel (reg_class %u channel %u) from intersection", cl->reg_class, cl->channel[0]); p2p->op_reg_class = cl->reg_class; p2p->op_channel = cl->channel[0]; } -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 intersection; - size_t i; - - p2p_channels_intersect(&p2p->channels, &dev->channels, &intersection); + struct p2p_channels tmp, intersection; + + p2p_channels_dump(p2p, "own channels", &p2p->channels); + p2p_channels_dump(p2p, "peer channels", &dev->channels); + p2p_channels_intersect(&p2p->channels, &dev->channels, &tmp); + p2p_channels_dump(p2p, "intersection", &tmp); + p2p_channels_remove_freqs(&tmp, &p2p->no_go_freq); + p2p_channels_dump(p2p, "intersection after no-GO removal", &tmp); + p2p_channels_intersect(&tmp, &p2p->cfg->channels, &intersection); + p2p_channels_dump(p2p, "intersection with local channel list", + &intersection); if (intersection.reg_classes == 0 || intersection.reg_class[0].channels == 0) { *status = P2P_SC_FAIL_NO_COMMON_CHANNELS; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No common channels found"); + p2p_dbg(p2p, "No common channels found"); return -1; } - for (i = 0; i < intersection.reg_classes; i++) { - struct p2p_reg_class *c; - c = &intersection.reg_class[i]; - wpa_printf(MSG_DEBUG, "P2P: reg_class %u", c->reg_class); - wpa_hexdump(MSG_DEBUG, "P2P: channels", - c->channel, c->channels); - } - if (!p2p_channels_includes(&intersection, p2p->op_reg_class, p2p->op_channel)) { if (dev->flags & P2P_DEV_FORCE_FREQ) { *status = P2P_SC_FAIL_NO_COMMON_CHANNELS; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer does " - "not support the forced channel"); + p2p_dbg(p2p, "Peer does not support the forced channel"); return -1; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Selected operating " - "channel (op_class %u channel %u) not acceptable to " - "the peer", p2p->op_reg_class, p2p->op_channel); + p2p_dbg(p2p, "Selected operating channel (op_class %u channel %u) not acceptable to the peer", + p2p->op_reg_class, p2p->op_channel); p2p_reselect_channel(p2p, &intersection); } else if (!(dev->flags & P2P_DEV_FORCE_FREQ) && !p2p->cfg->cfg_op_channel) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Try to optimize " - "channel selection with peer information received; " - "previously selected op_class %u channel %u", + p2p_dbg(p2p, "Try to optimize channel selection with peer information received; previously selected op_class %u channel %u", p2p->op_reg_class, p2p->op_channel); p2p_reselect_channel(p2p, &intersection); } @@ -503,17 +552,14 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, int tie_breaker = 0; int freq; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received GO Negotiation Request from " MACSTR - "(freq=%d)", MAC2STR(sa), rx_freq); + p2p_dbg(p2p, "Received GO Negotiation Request from " MACSTR "(freq=%d)", + MAC2STR(sa), rx_freq); if (p2p_parse(data, len, &msg)) return; if (!msg.capability) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Mandatory Capability attribute missing from GO " - "Negotiation Request"); + p2p_dbg(p2p, "Mandatory Capability attribute missing from GO Negotiation Request"); #ifdef CONFIG_P2P_STRICT goto fail; #endif /* CONFIG_P2P_STRICT */ @@ -522,53 +568,42 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, if (msg.go_intent) tie_breaker = *msg.go_intent & 0x01; else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Mandatory GO Intent attribute missing from GO " - "Negotiation Request"); + p2p_dbg(p2p, "Mandatory GO Intent attribute missing from GO Negotiation Request"); #ifdef CONFIG_P2P_STRICT goto fail; #endif /* CONFIG_P2P_STRICT */ } if (!msg.config_timeout) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Mandatory Configuration Timeout attribute " - "missing from GO Negotiation Request"); + p2p_dbg(p2p, "Mandatory Configuration Timeout attribute missing from GO Negotiation Request"); #ifdef CONFIG_P2P_STRICT goto fail; #endif /* CONFIG_P2P_STRICT */ } if (!msg.listen_channel) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Listen Channel attribute received"); + p2p_dbg(p2p, "No Listen Channel attribute received"); goto fail; } if (!msg.operating_channel) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Operating Channel attribute received"); + p2p_dbg(p2p, "No Operating Channel attribute received"); goto fail; } if (!msg.channel_list) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Channel List attribute received"); + p2p_dbg(p2p, "No Channel List attribute received"); goto fail; } if (!msg.intended_addr) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Intended P2P Interface Address attribute " - "received"); + p2p_dbg(p2p, "No Intended P2P Interface Address attribute received"); goto fail; } if (!msg.p2p_device_info) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No P2P Device Info attribute received"); + p2p_dbg(p2p, "No P2P Device Info attribute received"); goto fail; } if (os_memcmp(msg.p2p_device_addr, sa, ETH_ALEN) != 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unexpected GO Negotiation Request SA=" MACSTR + p2p_dbg(p2p, "Unexpected GO Negotiation Request SA=" MACSTR " != dev_addr=" MACSTR, MAC2STR(sa), MAC2STR(msg.p2p_device_addr)); goto fail; @@ -577,133 +612,177 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, dev = p2p_get_device(p2p, sa); if (msg.status && *msg.status) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unexpected Status attribute (%d) in GO " - "Negotiation Request", *msg.status); + p2p_dbg(p2p, "Unexpected Status attribute (%d) in GO Negotiation Request", + *msg.status); + if (dev && p2p->go_neg_peer == dev && + *msg.status == P2P_SC_FAIL_REJECTED_BY_USER) { + /* + * This mechanism for using Status attribute in GO + * Negotiation Request is not compliant with the P2P + * specification, but some deployed devices use it to + * indicate rejection of GO Negotiation in a case where + * they have sent out GO Negotiation Response with + * status 1. The P2P specification explicitly disallows + * this. To avoid unnecessary interoperability issues + * and extra frames, mark the pending negotiation as + * failed and do not reply to this GO Negotiation + * Request frame. + */ + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + p2p_go_neg_failed(p2p, *msg.status); + p2p_parse_free(&msg); + return; + } goto fail; } if (dev == NULL) dev = p2p_add_dev_from_go_neg_req(p2p, sa, &msg); - else if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) + else if ((dev->flags & P2P_DEV_PROBE_REQ_ONLY) || + !(dev->flags & P2P_DEV_REPORTED)) p2p_add_dev_info(p2p, sa, dev, &msg); + else if (!dev->listen_freq && !dev->oper_freq) { + /* + * This may happen if the peer entry was added based on PD + * Request and no Probe Request/Response frame has been received + * from this peer (or that information has timed out). + */ + p2p_dbg(p2p, "Update peer " MACSTR + " based on GO Neg Req since listen/oper freq not known", + MAC2STR(dev->info.p2p_device_addr)); + 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) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: User has rejected this peer"); + p2p_dbg(p2p, "User has rejected this peer"); status = P2P_SC_FAIL_REJECTED_BY_USER; - } else if (dev == NULL || dev->wps_method == WPS_NOT_READY) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Not ready for GO negotiation with " MACSTR, + } else if (dev == NULL || + (dev->wps_method == WPS_NOT_READY && + (p2p->authorized_oob_dev_pw_id == 0 || + p2p->authorized_oob_dev_pw_id != + msg.dev_password_id))) { + p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR, MAC2STR(sa)); status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; - if (dev) - dev->flags |= P2P_DEV_PEER_WAITING_RESPONSE; p2p->cfg->go_neg_req_rx(p2p->cfg->cb_ctx, sa, msg.dev_password_id); } else if (p2p->go_neg_peer && p2p->go_neg_peer != dev) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Already in Group Formation with another peer"); + p2p_dbg(p2p, "Already in Group Formation with another peer"); status = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; } else { int go; if (!p2p->go_neg_peer) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Starting " - "GO Negotiation with previously authorized " - "peer"); + p2p_dbg(p2p, "Starting GO Negotiation with previously authorized peer"); if (!(dev->flags & P2P_DEV_FORCE_FREQ)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Use default channel settings"); + p2p_dbg(p2p, "Use default channel settings"); p2p->op_reg_class = p2p->cfg->op_reg_class; p2p->op_channel = p2p->cfg->op_channel; os_memcpy(&p2p->channels, &p2p->cfg->channels, sizeof(struct p2p_channels)); } else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Use previously configured " - "forced channel settings"); + p2p_dbg(p2p, "Use previously configured forced channel settings"); } } dev->flags &= ~P2P_DEV_NOT_YET_READY; if (!msg.go_intent) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No GO Intent attribute received"); + p2p_dbg(p2p, "No GO Intent attribute received"); goto fail; } if ((*msg.go_intent >> 1) > P2P_MAX_GO_INTENT) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invalid GO Intent value (%u) received", + p2p_dbg(p2p, "Invalid GO Intent value (%u) received", *msg.go_intent >> 1); goto fail; } if (dev->go_neg_req_sent && os_memcmp(sa, p2p->cfg->dev_addr, ETH_ALEN) > 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Do not reply since peer has higher " - "address and GO Neg Request already sent"); + p2p_dbg(p2p, "Do not reply since peer has higher address and GO Neg Request already sent"); p2p_parse_free(&msg); return; } go = p2p_go_det(p2p->go_intent, *msg.go_intent); if (go < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Incompatible GO Intent"); + p2p_dbg(p2p, "Incompatible GO Intent"); status = P2P_SC_FAIL_BOTH_GO_INTENT_15; goto fail; } if (p2p_peer_channels(p2p, dev, msg.channel_list, msg.channel_list_len) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No common channels found"); + p2p_dbg(p2p, "No common channels found"); status = P2P_SC_FAIL_NO_COMMON_CHANNELS; goto fail; } switch (msg.dev_password_id) { case DEV_PW_REGISTRAR_SPECIFIED: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: PIN from peer Display"); + p2p_dbg(p2p, "PIN from peer Display"); if (dev->wps_method != WPS_PIN_KEYPAD) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: We have wps_method=%s -> " - "incompatible", + 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; case DEV_PW_USER_SPECIFIED: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Peer entered PIN on Keypad"); + p2p_dbg(p2p, "Peer entered PIN on Keypad"); if (dev->wps_method != WPS_PIN_DISPLAY) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: We have wps_method=%s -> " - "incompatible", + 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; case DEV_PW_PUSHBUTTON: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Peer using pushbutton"); + p2p_dbg(p2p, "Peer using pushbutton"); if (dev->wps_method != WPS_PBC) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: We have wps_method=%s -> " - "incompatible", + 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; + 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: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported Device Password ID %d", + if (msg.dev_password_id && + msg.dev_password_id == dev->oob_pw_id) { + p2p_dbg(p2p, "Peer using NFC"); + if (dev->wps_method != WPS_NFC) { + 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; + } +#ifdef CONFIG_WPS_NFC + if (p2p->authorized_oob_dev_pw_id && + msg.dev_password_id == + p2p->authorized_oob_dev_pw_id) { + p2p_dbg(p2p, "Using static handover with our device password from NFC Tag"); + dev->wps_method = WPS_NFC; + dev->oob_pw_id = p2p->authorized_oob_dev_pw_id; + break; + } +#endif /* CONFIG_WPS_NFC */ + p2p_dbg(p2p, "Unsupported Device Password ID %d", msg.dev_password_id); status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; goto fail; @@ -713,20 +792,17 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, goto fail; dev->go_state = go ? LOCAL_GO : REMOTE_GO; - dev->oper_freq = p2p_channel_to_freq((const char *) - msg.operating_channel, - msg.operating_channel[3], + dev->oper_freq = p2p_channel_to_freq(msg.operating_channel[3], msg.operating_channel[4]); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer operating " - "channel preference: %d MHz", dev->oper_freq); + p2p_dbg(p2p, "Peer operating channel preference: %d MHz", + dev->oper_freq); if (msg.config_timeout) { dev->go_timeout = msg.config_timeout[0]; dev->client_timeout = msg.config_timeout[1]; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: GO Negotiation with " MACSTR, MAC2STR(sa)); + p2p_dbg(p2p, "GO Negotiation with " MACSTR, MAC2STR(sa)); if (p2p->state != P2P_IDLE) p2p_stop_find_for_freq(p2p, rx_freq); p2p_set_state(p2p, P2P_GO_NEG); @@ -734,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; } @@ -745,17 +822,14 @@ fail: p2p_parse_free(&msg); if (resp == NULL) return; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Sending GO Negotiation Response"); + p2p_dbg(p2p, "Sending GO Negotiation Response"); if (rx_freq > 0) freq = rx_freq; else - freq = p2p_channel_to_freq(p2p->cfg->country, - p2p->cfg->reg_class, + freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel); if (freq < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unknown regulatory class/channel"); + p2p_dbg(p2p, "Unknown regulatory class/channel"); wpabuf_free(resp); return; } @@ -778,9 +852,8 @@ fail: P2P_PENDING_GO_NEG_RESPONSE_FAILURE; if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, p2p->cfg->dev_addr, - wpabuf_head(resp), wpabuf_len(resp), 250) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + wpabuf_head(resp), wpabuf_len(resp), 500) < 0) { + p2p_dbg(p2p, "Failed to send Action frame"); } wpabuf_free(resp); @@ -798,14 +871,16 @@ static struct wpabuf * p2p_build_go_neg_conf(struct p2p_data *p2p, u8 group_capab; size_t extra = 0; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Building GO Negotiation Confirm"); + p2p_dbg(p2p, "Building GO Negotiation Confirm"); #ifdef CONFIG_WIFI_DISPLAY if (p2p->wfd_ie_go_neg) extra = wpabuf_len(p2p->wfd_ie_go_neg); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF]) + extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF]); + buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; @@ -850,6 +925,9 @@ static struct wpabuf * p2p_build_go_neg_conf(struct p2p_data *p2p, wpabuf_put_buf(buf, p2p->wfd_ie_go_neg); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF]) + wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF]); + return buf; } @@ -858,20 +936,17 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq) { struct p2p_device *dev; - struct wpabuf *conf; int go = -1; struct p2p_message msg; u8 status = P2P_SC_SUCCESS; int freq; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received GO Negotiation Response from " MACSTR + p2p_dbg(p2p, "Received GO Negotiation Response from " MACSTR " (freq=%d)", MAC2STR(sa), rx_freq); dev = p2p_get_device(p2p, sa); if (dev == NULL || dev->wps_method == WPS_NOT_READY || dev != p2p->go_neg_peer) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Not ready for GO negotiation with " MACSTR, + p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR, MAC2STR(sa)); return; } @@ -880,45 +955,42 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, return; if (!(dev->flags & P2P_DEV_WAIT_GO_NEG_RESPONSE)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Was not expecting GO Negotiation Response - " - "ignore"); + p2p_dbg(p2p, "Was not expecting GO Negotiation Response - ignore"); p2p_parse_free(&msg); return; } dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE; if (msg.dialog_token != dev->dialog_token) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unexpected Dialog Token %u (expected %u)", + p2p_dbg(p2p, "Unexpected Dialog Token %u (expected %u)", msg.dialog_token, dev->dialog_token); p2p_parse_free(&msg); return; } if (!msg.status) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Status attribute received"); + p2p_dbg(p2p, "No Status attribute received"); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; } if (*msg.status) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: GO Negotiation rejected: status %d", - *msg.status); + p2p_dbg(p2p, "GO Negotiation rejected: status %d", *msg.status); dev->go_neg_req_sent = 0; if (*msg.status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Wait for the peer to become ready for " - "GO Negotiation"); + p2p_dbg(p2p, "Wait for the peer to become ready for GO Negotiation"); dev->flags |= P2P_DEV_NOT_YET_READY; - dev->wait_count = 0; - p2p_set_state(p2p, P2P_WAIT_PEER_IDLE); + 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 + p2p_set_state(p2p, P2P_WAIT_PEER_IDLE); p2p_set_timeout(p2p, 0, 0); } else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Stop GO Negotiation attempt"); - p2p_go_neg_failed(p2p, dev, *msg.status); + p2p_dbg(p2p, "Stop GO Negotiation attempt"); + p2p_go_neg_failed(p2p, *msg.status); } p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p_parse_free(&msg); @@ -926,9 +998,7 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, } if (!msg.capability) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Mandatory Capability attribute missing from GO " - "Negotiation Response"); + p2p_dbg(p2p, "Mandatory Capability attribute missing from GO Negotiation Response"); #ifdef CONFIG_P2P_STRICT status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; @@ -936,9 +1006,7 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, } if (!msg.p2p_device_info) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Mandatory P2P Device Info attribute missing " - "from GO Negotiation Response"); + p2p_dbg(p2p, "Mandatory P2P Device Info attribute missing from GO Negotiation Response"); #ifdef CONFIG_P2P_STRICT status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; @@ -946,22 +1014,18 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, } if (!msg.intended_addr) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Intended P2P Interface Address attribute " - "received"); + p2p_dbg(p2p, "No Intended P2P Interface Address attribute received"); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; } if (!msg.go_intent) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No GO Intent attribute received"); + p2p_dbg(p2p, "No GO Intent attribute received"); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; } if ((*msg.go_intent >> 1) > P2P_MAX_GO_INTENT) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invalid GO Intent value (%u) received", + p2p_dbg(p2p, "Invalid GO Intent value (%u) received", *msg.go_intent >> 1); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; @@ -969,8 +1033,7 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, go = p2p_go_det(p2p->go_intent, *msg.go_intent); if (go < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Incompatible GO Intent"); + p2p_dbg(p2p, "Incompatible GO Intent"); status = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; goto fail; } @@ -980,20 +1043,14 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, p2p->ssid_len = msg.group_id_len - ETH_ALEN; os_memcpy(p2p->ssid, msg.group_id + ETH_ALEN, p2p->ssid_len); } else if (!go) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Mandatory P2P Group ID attribute missing from " - "GO Negotiation Response"); + p2p_dbg(p2p, "Mandatory P2P Group ID attribute missing from GO Negotiation Response"); p2p->ssid_len = 0; -#ifdef CONFIG_P2P_STRICT status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; -#endif /* CONFIG_P2P_STRICT */ } if (!msg.config_timeout) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Mandatory Configuration Timeout attribute " - "missing from GO Negotiation Response"); + p2p_dbg(p2p, "Mandatory Configuration Timeout attribute missing from GO Negotiation Response"); #ifdef CONFIG_P2P_STRICT status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; @@ -1008,76 +1065,81 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, * Note: P2P Client may omit Operating Channel attribute to * indicate it does not have a preference. */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Operating Channel attribute received"); + p2p_dbg(p2p, "No Operating Channel attribute received"); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; } if (!msg.channel_list) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Channel List attribute received"); + p2p_dbg(p2p, "No Channel List attribute received"); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; } if (p2p_peer_channels(p2p, dev, msg.channel_list, msg.channel_list_len) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No common channels found"); + p2p_dbg(p2p, "No common channels found"); status = P2P_SC_FAIL_NO_COMMON_CHANNELS; goto fail; } if (msg.operating_channel) { - dev->oper_freq = p2p_channel_to_freq((const char *) - msg.operating_channel, - msg.operating_channel[3], + dev->oper_freq = p2p_channel_to_freq(msg.operating_channel[3], msg.operating_channel[4]); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer operating " - "channel preference: %d MHz", dev->oper_freq); + p2p_dbg(p2p, "Peer operating channel preference: %d MHz", + dev->oper_freq); } else dev->oper_freq = 0; switch (msg.dev_password_id) { case DEV_PW_REGISTRAR_SPECIFIED: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: PIN from peer Display"); + p2p_dbg(p2p, "PIN from peer Display"); if (dev->wps_method != WPS_PIN_KEYPAD) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: We have wps_method=%s -> " - "incompatible", + 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; case DEV_PW_USER_SPECIFIED: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Peer entered PIN on Keypad"); + p2p_dbg(p2p, "Peer entered PIN on Keypad"); if (dev->wps_method != WPS_PIN_DISPLAY) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: We have wps_method=%s -> " - "incompatible", + 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; case DEV_PW_PUSHBUTTON: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Peer using pushbutton"); + p2p_dbg(p2p, "Peer using pushbutton"); if (dev->wps_method != WPS_PBC) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: We have wps_method=%s -> " - "incompatible", + 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; + 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: - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported Device Password ID %d", + if (msg.dev_password_id && + msg.dev_password_id == dev->oob_pw_id) { + p2p_dbg(p2p, "Peer using NFC"); + if (dev->wps_method != WPS_NFC) { + 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; + } + p2p_dbg(p2p, "Unsupported Device Password ID %d", msg.dev_password_id); status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD; goto fail; @@ -1089,18 +1151,19 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, p2p_set_state(p2p, P2P_GO_NEG); p2p_clear_timeout(p2p); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: GO Negotiation with " MACSTR, MAC2STR(sa)); + p2p_dbg(p2p, "GO Negotiation with " MACSTR, MAC2STR(sa)); os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN); fail: - conf = p2p_build_go_neg_conf(p2p, dev, msg.dialog_token, status, - msg.operating_channel, go); + /* Store GO Negotiation Confirmation to allow retransmission */ + wpabuf_free(dev->go_neg_conf); + dev->go_neg_conf = p2p_build_go_neg_conf(p2p, dev, msg.dialog_token, + status, msg.operating_channel, + go); p2p_parse_free(&msg); - if (conf == NULL) + if (dev->go_neg_conf == NULL) return; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Sending GO Negotiation Confirm"); + p2p_dbg(p2p, "Sending GO Negotiation Confirm"); if (status == P2P_SC_SUCCESS) { p2p->pending_action_state = P2P_PENDING_GO_NEG_CONFIRM; dev->go_state = go ? LOCAL_GO : REMOTE_GO; @@ -1110,13 +1173,22 @@ fail: freq = rx_freq; else freq = dev->listen_freq; + + dev->go_neg_conf_freq = freq; + dev->go_neg_conf_sent = 0; + if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, sa, - wpabuf_head(conf), wpabuf_len(conf), 0) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); - p2p_go_neg_failed(p2p, dev, -1); + 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, -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, status); } - wpabuf_free(conf); } @@ -1126,22 +1198,18 @@ void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa, struct p2p_device *dev; struct p2p_message msg; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received GO Negotiation Confirm from " MACSTR, + p2p_dbg(p2p, "Received GO Negotiation Confirm from " MACSTR, MAC2STR(sa)); dev = p2p_get_device(p2p, sa); if (dev == NULL || dev->wps_method == WPS_NOT_READY || dev != p2p->go_neg_peer) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Not ready for GO negotiation with " MACSTR, + p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR, MAC2STR(sa)); return; } if (p2p->pending_action_state == P2P_PENDING_GO_NEG_RESPONSE) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Stopped waiting " - "for TX status on GO Negotiation Response since we " - "already received Confirmation"); + p2p_dbg(p2p, "Stopped waiting for TX status on GO Negotiation Response since we already received Confirmation"); p2p->pending_action_state = P2P_NO_PENDING_ACTION; } @@ -1149,31 +1217,28 @@ void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa, return; if (!(dev->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Was not expecting GO Negotiation Confirm - " - "ignore"); + p2p_dbg(p2p, "Was not expecting GO Negotiation Confirm - ignore"); + p2p_parse_free(&msg); return; } dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM; + p2p->cfg->send_action_done(p2p->cfg->cb_ctx); if (msg.dialog_token != dev->dialog_token) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unexpected Dialog Token %u (expected %u)", + p2p_dbg(p2p, "Unexpected Dialog Token %u (expected %u)", msg.dialog_token, dev->dialog_token); p2p_parse_free(&msg); return; } if (!msg.status) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Status attribute received"); + p2p_dbg(p2p, "No Status attribute received"); p2p_parse_free(&msg); return; } if (*msg.status) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: GO Negotiation rejected: status %d", - *msg.status); + p2p_dbg(p2p, "GO Negotiation rejected: status %d", *msg.status); + p2p_go_neg_failed(p2p, *msg.status); p2p_parse_free(&msg); return; } @@ -1183,30 +1248,31 @@ void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa, p2p->ssid_len = msg.group_id_len - ETH_ALEN; os_memcpy(p2p->ssid, msg.group_id + ETH_ALEN, p2p->ssid_len); } else if (dev->go_state == REMOTE_GO) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Mandatory P2P Group ID attribute missing from " - "GO Negotiation Confirmation"); + p2p_dbg(p2p, "Mandatory P2P Group ID attribute missing from GO Negotiation Confirmation"); p2p->ssid_len = 0; -#ifdef CONFIG_P2P_STRICT + p2p_go_neg_failed(p2p, P2P_SC_FAIL_INVALID_PARAMS); p2p_parse_free(&msg); return; -#endif /* CONFIG_P2P_STRICT */ } if (!msg.operating_channel) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Mandatory Operating Channel attribute missing " - "from GO Negotiation Confirmation"); + p2p_dbg(p2p, "Mandatory Operating Channel attribute missing from GO Negotiation Confirmation"); #ifdef CONFIG_P2P_STRICT p2p_parse_free(&msg); return; #endif /* CONFIG_P2P_STRICT */ + } else if (dev->go_state == REMOTE_GO) { + int oper_freq = p2p_channel_to_freq(msg.operating_channel[3], + msg.operating_channel[4]); + if (oper_freq != dev->oper_freq) { + p2p_dbg(p2p, "Updated peer (GO) operating channel preference from %d MHz to %d MHz", + dev->oper_freq, oper_freq); + dev->oper_freq = oper_freq; + } } if (!msg.channel_list) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Mandatory Operating Channel attribute missing " - "from GO Negotiation Confirmation"); + p2p_dbg(p2p, "Mandatory Operating Channel attribute missing from GO Negotiation Confirmation"); #ifdef CONFIG_P2P_STRICT p2p_parse_free(&msg); return; @@ -1220,9 +1286,7 @@ void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa, * This should not happen since GO negotiation has already * been completed. */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unexpected GO Neg state - do not know which end " - "becomes GO"); + p2p_dbg(p2p, "Unexpected GO Neg state - do not know which end becomes GO"); return; } @@ -1234,8 +1298,7 @@ void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa, * the group so that we will remain on the current channel to * acknowledge any possible retransmission from the peer. */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: 20 ms wait on current " - "channel before starting group"); + p2p_dbg(p2p, "20 ms wait on current channel before starting group"); os_sleep(0, 20000); p2p_go_complete(p2p, dev); diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c index 86873205b8301..41ca99faaf481 100644 --- a/src/p2p/p2p_group.c +++ b/src/p2p/p2p_group.c @@ -11,6 +11,7 @@ #include "common.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" +#include "common/wpa_ctrl.h" #include "wps/wps_defs.h" #include "wps/wps_i.h" #include "p2p_i.h" @@ -154,6 +155,7 @@ static void p2p_group_add_common_ies(struct p2p_group *group, group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; if (group->num_members >= group->cfg->max_clients) group_capab |= P2P_GROUP_CAPAB_GROUP_LIMIT; + group_capab |= P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION; p2p_buf_add_capability(ie, dev_capab, group_capab); } @@ -169,6 +171,39 @@ static void p2p_group_add_noa(struct wpabuf *ie, struct wpabuf *noa) } +static struct wpabuf * p2p_group_encaps_probe_resp(struct wpabuf *subelems) +{ + struct wpabuf *ie; + const u8 *pos, *end; + size_t len; + + if (subelems == NULL) + return NULL; + + len = wpabuf_len(subelems) + 100; + + ie = wpabuf_alloc(len); + if (ie == NULL) + return NULL; + + pos = wpabuf_head(subelems); + end = pos + wpabuf_len(subelems); + + while (end > pos) { + size_t frag_len = end - pos; + if (frag_len > 251) + frag_len = 251; + wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(ie, 4 + frag_len); + wpabuf_put_be32(ie, P2P_IE_VENDOR_TYPE); + wpabuf_put_data(ie, pos, frag_len); + pos += frag_len; + } + + return ie; +} + + static struct wpabuf * p2p_group_build_beacon_ie(struct p2p_group *group) { struct wpabuf *ie; @@ -180,6 +215,10 @@ static struct wpabuf * p2p_group_build_beacon_ie(struct p2p_group *group) extra = wpabuf_len(group->p2p->wfd_ie_beacon); #endif /* CONFIG_WIFI_DISPLAY */ + if (group->p2p->vendor_elem && + group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO]) + extra += wpabuf_len(group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO]); + ie = wpabuf_alloc(257 + extra); if (ie == NULL) return NULL; @@ -189,6 +228,11 @@ static struct wpabuf * p2p_group_build_beacon_ie(struct p2p_group *group) wpabuf_put_buf(ie, group->p2p->wfd_ie_beacon); #endif /* CONFIG_WIFI_DISPLAY */ + if (group->p2p->vendor_elem && + group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO]) + wpabuf_put_buf(ie, + group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO]); + len = p2p_buf_add_ie_hdr(ie); p2p_group_add_common_ies(group, ie); p2p_buf_add_device_id(ie, group->p2p->cfg->dev_addr); @@ -345,8 +389,8 @@ wifi_display_build_go_ie(struct p2p_group *group) } else { WPA_PUT_BE16(len, (u8 *) wpabuf_put(wfd_subelems, 0) - len - 2); - wpa_printf(MSG_DEBUG, "WFD: WFD Session Info: %u descriptors", - count); + p2p_dbg(group->p2p, "WFD: WFD Session Info: %u descriptors", + count); } wfd_ie = wifi_display_encaps(wfd_subelems); @@ -364,46 +408,69 @@ static void wifi_display_group_update(struct p2p_group *group) #endif /* CONFIG_WIFI_DISPLAY */ -static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group) +void p2p_buf_add_group_info(struct p2p_group *group, struct wpabuf *buf, + int max_clients) { u8 *group_info; - struct wpabuf *ie; + int count = 0; struct p2p_group_member *m; - u8 *len; - size_t extra = 0; -#ifdef CONFIG_WIFI_DISPLAY - if (group->wfd_ie) - extra += wpabuf_len(group->wfd_ie); -#endif /* CONFIG_WIFI_DISPLAY */ + p2p_dbg(group->p2p, "* P2P Group Info"); + group_info = wpabuf_put(buf, 0); + wpabuf_put_u8(buf, P2P_ATTR_GROUP_INFO); + wpabuf_put_le16(buf, 0); /* Length to be filled */ + for (m = group->members; m; m = m->next) { + p2p_client_info(buf, m); + count++; + if (max_clients >= 0 && count >= max_clients) + break; + } + WPA_PUT_LE16(group_info + 1, + (u8 *) wpabuf_put(buf, 0) - group_info - 3); +} - ie = wpabuf_alloc(257 + extra); - if (ie == NULL) - return NULL; -#ifdef CONFIG_WIFI_DISPLAY - if (group->wfd_ie) - wpabuf_put_buf(ie, group->wfd_ie); -#endif /* CONFIG_WIFI_DISPLAY */ +void p2p_group_buf_add_id(struct p2p_group *group, struct wpabuf *buf) +{ + p2p_buf_add_group_id(buf, group->p2p->cfg->dev_addr, group->cfg->ssid, + group->cfg->ssid_len); +} - len = p2p_buf_add_ie_hdr(ie); - p2p_group_add_common_ies(group, ie); - p2p_group_add_noa(ie, group->noa); +static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group) +{ + struct wpabuf *p2p_subelems, *ie; + + p2p_subelems = wpabuf_alloc(500); + if (p2p_subelems == NULL) + return NULL; + + p2p_group_add_common_ies(group, p2p_subelems); + p2p_group_add_noa(p2p_subelems, group->noa); /* P2P Device Info */ - p2p_buf_add_device_info(ie, group->p2p, NULL); - - /* P2P Group Info */ - group_info = wpabuf_put(ie, 0); - wpabuf_put_u8(ie, P2P_ATTR_GROUP_INFO); - wpabuf_put_le16(ie, 0); /* Length to be filled */ - for (m = group->members; m; m = m->next) - p2p_client_info(ie, m); - WPA_PUT_LE16(group_info + 1, - (u8 *) wpabuf_put(ie, 0) - group_info - 3); + p2p_buf_add_device_info(p2p_subelems, group->p2p, NULL); - p2p_buf_update_ie_hdr(ie, len); + /* P2P Group Info: Only when at least one P2P Client is connected */ + if (group->members) + p2p_buf_add_group_info(group, p2p_subelems, -1); + + ie = p2p_group_encaps_probe_resp(p2p_subelems); + wpabuf_free(p2p_subelems); + + if (group->p2p->vendor_elem && + group->p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P_GO]) { + struct wpabuf *extra; + extra = wpabuf_dup(group->p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P_GO]); + ie = wpabuf_concat(extra, ie); + } + +#ifdef CONFIG_WIFI_DISPLAY + if (group->wfd_ie) { + struct wpabuf *wfd = wpabuf_dup(group->wfd_ie); + ie = wpabuf_concat(wfd, ie); + } +#endif /* CONFIG_WIFI_DISPLAY */ return ie; } @@ -537,6 +604,8 @@ int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr, if (group == NULL) return -1; + p2p_add_device(group->p2p, addr, 0, NULL, 0, ie, len, 0); + m = os_zalloc(sizeof(*m)); if (m == NULL) return -1; @@ -556,7 +625,7 @@ int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr, m->next = group->members; group->members = m; group->num_members++; - wpa_msg(group->p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Add client " MACSTR + p2p_dbg(group->p2p, "Add client " MACSTR " to group (p2p=%d wfd=%d client_info=%d); num_members=%u/%u", MAC2STR(addr), m->p2p_ie ? 1 : 0, m->wfd_ie ? 1 : 0, m->client_info ? 1 : 0, @@ -582,6 +651,10 @@ struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status) extra = wpabuf_len(group->wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ + if (group->p2p->vendor_elem && + group->p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_RESP]) + extra += wpabuf_len(group->p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_RESP]); + /* * (Re)Association Response - P2P IE * Status attribute (shall be present when association request is @@ -597,6 +670,11 @@ struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status) wpabuf_put_buf(resp, group->wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ + if (group->p2p->vendor_elem && + group->p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_RESP]) + wpabuf_put_buf(resp, + group->p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_RESP]); + rlen = p2p_buf_add_ie_hdr(resp); if (status != P2P_SC_SUCCESS) p2p_buf_add_status(resp, status); @@ -609,8 +687,8 @@ struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status) void p2p_group_notif_disassoc(struct p2p_group *group, const u8 *addr) { if (p2p_group_remove_member(group, addr)) { - wpa_msg(group->p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Remove " - "client " MACSTR " from group; num_members=%u/%u", + p2p_dbg(group->p2p, "Remove client " MACSTR + " from group; num_members=%u/%u", MAC2STR(addr), group->num_members, group->cfg->max_clients); if (group->num_members == group->cfg->max_clients - 1) @@ -822,20 +900,18 @@ int p2p_group_go_discover(struct p2p_group *group, const u8 *dev_id, m = p2p_group_get_client(group, dev_id); if (m == NULL || m->client_info == NULL) { - wpa_printf(MSG_DEBUG, "P2P: Requested client was not in this " - "group " MACSTR, - MAC2STR(group->cfg->interface_addr)); + p2p_dbg(group->p2p, "Requested client was not in this group " + MACSTR, MAC2STR(group->cfg->interface_addr)); return -1; } if (!(m->dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) { - wpa_printf(MSG_DEBUG, "P2P: Requested client does not support " - "client discoverability"); + p2p_dbg(group->p2p, "Requested client does not support client discoverability"); return -1; } - wpa_printf(MSG_DEBUG, "P2P: Schedule GO Discoverability Request to be " - "sent to " MACSTR, MAC2STR(dev_id)); + p2p_dbg(group->p2p, "Schedule GO Discoverability Request to be sent to " + MACSTR, MAC2STR(dev_id)); req = p2p_build_go_disc_req(); if (req == NULL) @@ -850,8 +926,7 @@ int p2p_group_go_discover(struct p2p_group *group, const u8 *dev_id, group->cfg->interface_addr, wpabuf_head(req), wpabuf_len(req), 200) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + p2p_dbg(p2p, "Failed to send Action frame"); } wpabuf_free(req); @@ -876,7 +951,7 @@ u8 p2p_group_presence_req(struct p2p_group *group, m = p2p_group_get_client_iface(group, client_interface_addr); if (m == NULL || m->client_info == NULL) { - wpa_printf(MSG_DEBUG, "P2P: Client was not in this group"); + p2p_dbg(group->p2p, "Client was not in this group"); return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; } @@ -889,9 +964,9 @@ u8 p2p_group_presence_req(struct p2p_group *group, else curr_noa_len = -1; if (curr_noa_len < 0) - wpa_printf(MSG_DEBUG, "P2P: Failed to fetch current NoA"); + p2p_dbg(group->p2p, "Failed to fetch current NoA"); else if (curr_noa_len == 0) - wpa_printf(MSG_DEBUG, "P2P: No NoA being advertized"); + p2p_dbg(group->p2p, "No NoA being advertized"); else wpa_hexdump(MSG_DEBUG, "P2P: Current NoA", curr_noa, curr_noa_len); @@ -906,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; @@ -924,7 +1011,7 @@ const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next) if (!iter) return NULL; - return iter->addr; + return iter->dev_addr; } @@ -951,3 +1038,36 @@ int p2p_group_is_group_id_match(struct p2p_group *group, const u8 *group_id, return os_memcmp(group_id + ETH_ALEN, group->cfg->ssid, group->cfg->ssid_len) == 0; } + + +void p2p_group_force_beacon_update_ies(struct p2p_group *group) +{ + group->beacon_update = 1; + p2p_group_update_ies(group); +} + + +int p2p_group_get_freq(struct p2p_group *group) +{ + return group->cfg->freq; +} + + +const struct p2p_group_config * p2p_group_get_config(struct p2p_group *group) +{ + return group->cfg; +} + + +void p2p_loop_on_all_groups(struct p2p_data *p2p, + int (*group_callback)(struct p2p_group *group, + void *user_data), + void *user_data) +{ + unsigned int i; + + for (i = 0; i < p2p->num_groups; i++) { + if (!group_callback(p2p->groups[i], user_data)) + break; + } +} diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h index 27fef0182941a..6af19ceda450e 100644 --- a/src/p2p/p2p_i.h +++ b/src/p2p/p2p_i.h @@ -12,6 +12,17 @@ #include "utils/list.h" #include "p2p.h" +#define P2P_GO_NEG_CNF_MAX_RETRY_COUNT 1 + +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, @@ -23,9 +34,11 @@ enum p2p_go_state { */ struct p2p_device { struct dl_list list; - struct os_time last_seen; + struct os_reltime last_seen; int listen_freq; + int oob_go_neg_freq; enum p2p_wps_method wps_method; + u16 oob_pw_id; struct p2p_peer_info info; @@ -52,6 +65,7 @@ struct p2p_device { int go_neg_req_sent; enum p2p_go_state go_state; u8 dialog_token; + u8 tie_breaker; u8 intended_addr[ETH_ALEN]; char country[3]; @@ -76,8 +90,6 @@ struct p2p_device { #define P2P_DEV_PROBE_REQ_ONLY BIT(0) #define P2P_DEV_REPORTED BIT(1) #define P2P_DEV_NOT_YET_READY BIT(2) -#define P2P_DEV_SD_INFO BIT(3) -#define P2P_DEV_SD_SCHEDULE BIT(4) #define P2P_DEV_PD_PEER_DISPLAY BIT(5) #define P2P_DEV_PD_PEER_KEYPAD BIT(6) #define P2P_DEV_USER_REJECTED BIT(7) @@ -91,18 +103,40 @@ struct p2p_device { #define P2P_DEV_REPORTED_ONCE BIT(15) #define P2P_DEV_PREFER_PERSISTENT_RECONN BIT(16) #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 */ unsigned int wait_count; unsigned int connect_reqs; unsigned int invitation_reqs; + unsigned int sd_reqs; u16 ext_listen_period; u16 ext_listen_interval; u8 go_timeout; u8 client_timeout; + + /** + * go_neg_conf_sent - Number of GO Negotiation Confirmation retries + */ + u8 go_neg_conf_sent; + + /** + * freq - Frquency on which the GO Negotiation Confirmation is sent + */ + int go_neg_conf_freq; + + /** + * go_neg_conf - GO Negotiation Confirmation frame + */ + struct wpabuf *go_neg_conf; + + int sd_pending_bcast_queries; }; struct p2p_sd_query { @@ -203,16 +237,6 @@ struct p2p_data { * P2P_INVITE_LISTEN - Listen during Invite */ P2P_INVITE_LISTEN, - - /** - * P2P_SEARCH_WHEN_READY - Waiting to start Search - */ - P2P_SEARCH_WHEN_READY, - - /** - * P2P_CONTINUE_SEARCH_WHEN_READY - Waiting to continue Search - */ - P2P_CONTINUE_SEARCH_WHEN_READY, } state; /** @@ -245,8 +269,17 @@ 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 @@ -258,6 +291,12 @@ struct p2p_data { */ struct p2p_sd_query *sd_query; + /** + * num_p2p_sd_queries - Total number of broadcast SD queries present in + * the list + */ + int num_p2p_sd_queries; + /* GO Negotiation data */ /** @@ -314,6 +353,8 @@ struct p2p_data { */ struct p2p_channels channels; + struct wpa_freq_range_list no_go_freq; + enum p2p_pending_action_state { P2P_NO_PENDING_ACTION, P2P_PENDING_GO_NEG_REQUEST, @@ -322,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, @@ -383,6 +425,8 @@ struct p2p_data { } start_after_scan; u8 after_scan_peer[ETH_ALEN]; struct p2p_pending_action_tx *after_scan_tx; + unsigned int after_scan_tx_in_progress:1; + unsigned int send_action_in_progress:1; /* Requested device types for find/search */ unsigned int num_req_dev_types; @@ -390,6 +434,8 @@ struct p2p_data { u8 *find_dev_id; u8 find_dev_id_buf[ETH_ALEN]; + struct os_reltime find_start; /* time of last p2p_find start */ + struct p2p_group **groups; size_t num_groups; @@ -413,6 +459,7 @@ struct p2p_data { int best_freq_24; int best_freq_5; int best_freq_overall; + int own_freq_preference; /** * wps_vendor_ext - WPS Vendor Extensions to add @@ -436,6 +483,14 @@ struct p2p_data { */ int pd_retries; + /** + * pd_force_freq - Forced frequency for PD retries or 0 to auto-select + * + * This is is used during PD retries for join-a-group case to use the + * correct operating frequency determined from a BSS entry for the GO. + */ + int pd_force_freq; + u8 go_timeout; u8 client_timeout; @@ -443,6 +498,20 @@ struct p2p_data { unsigned int search_delay; int in_search_delay; + u8 pending_reg_class; + 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; @@ -456,6 +525,10 @@ struct p2p_data { struct wpabuf *wfd_assoc_bssid; struct wpabuf *wfd_coupled_sink_info; #endif /* CONFIG_WIFI_DISPLAY */ + + u16 authorized_oob_dev_pw_id; + + struct wpabuf **vendor_elem; }; /** @@ -497,6 +570,8 @@ struct p2p_message { const u8 *minor_reason_code; + const u8 *oob_go_neg_channel; + /* P2P Device Info */ const u8 *p2p_device_info; size_t p2p_device_info_len; @@ -508,6 +583,7 @@ struct p2p_message { /* WPS IE */ u16 dev_password_id; + int dev_password_id_present; u16 wps_config_methods; const u8 *wps_pri_dev_type; const u8 *wps_sec_dev_type_list; @@ -522,12 +598,39 @@ struct p2p_message { size_t model_number_len; const u8 *serial_number; size_t serial_number_len; + const u8 *oob_dev_password; + size_t oob_dev_password_len; /* DS Parameter Set IE */ const u8 *ds_params; /* 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; }; @@ -551,19 +654,33 @@ struct p2p_group_info { /* p2p_utils.c */ int p2p_random(char *buf, size_t len); -int p2p_channel_to_freq(const char *country, int reg_class, int channel); -int p2p_freq_to_channel(const char *country, unsigned int freq, u8 *reg_class, - u8 *channel); +int p2p_channel_to_freq(int op_class, int channel); +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); +void p2p_channels_remove_freqs(struct p2p_channels *chan, + const struct wpa_freq_range_list *list); int p2p_channels_includes(const struct p2p_channels *channels, u8 reg_class, u8 channel); +void p2p_channels_dump(struct p2p_data *p2p, const char *title, + const struct p2p_channels *chan); +int p2p_channel_select(struct p2p_channels *chans, const int *classes, + u8 *op_class, u8 *op_channel); +int p2p_channel_random_social(struct p2p_channels *chans, u8 *op_class, + u8 *op_channel); /* p2p_parse.c */ int p2p_parse_p2p_ie(const struct wpabuf *buf, struct p2p_message *msg); int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg); int p2p_parse(const u8 *data, size_t len, struct p2p_message *msg); +int p2p_parse_ies_separate(const u8 *wsc, size_t wsc_len, const u8 *p2p, + size_t p2p_len, struct p2p_message *msg); void p2p_parse_free(struct p2p_message *msg); int p2p_attr_text(struct wpabuf *data, char *buf, char *end); int p2p_group_info_parse(const u8 *gi, size_t gi_len, @@ -586,7 +703,12 @@ u8 p2p_group_presence_req(struct p2p_group *group, int p2p_group_is_group_id_match(struct p2p_group *group, const u8 *group_id, size_t group_id_len); void p2p_group_update_ies(struct p2p_group *group); +void p2p_group_force_beacon_update_ies(struct p2p_group *group); struct wpabuf * p2p_group_get_wfd_ie(struct p2p_group *g); +void p2p_buf_add_group_info(struct p2p_group *group, struct wpabuf *buf, + int max_clients); +void p2p_group_buf_add_id(struct p2p_group *group, struct wpabuf *buf); +int p2p_group_get_freq(struct p2p_group *group); void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token); @@ -618,8 +740,23 @@ void p2p_buf_add_noa(struct wpabuf *buf, u8 noa_index, u8 opp_ps, u8 ctwindow, void p2p_buf_add_ext_listen_timing(struct wpabuf *buf, u16 period, u16 interval); void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p); -void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id, - int all_attr); +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); /* p2p_sd.c */ struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p, @@ -665,7 +802,7 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len); int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev, - const u8 *go_dev_addr); + const u8 *go_dev_addr, int dev_pw_id); void p2p_invitation_req_cb(struct p2p_data *p2p, int success); void p2p_invitation_resp_cb(struct p2p_data *p2p, int success); @@ -693,13 +830,12 @@ struct p2p_device * p2p_add_dev_from_go_neg_req(struct p2p_data *p2p, void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr, struct p2p_device *dev, struct p2p_message *msg); int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, - unsigned int age_ms, int level, const u8 *ies, + struct os_reltime *rx_time, int level, const u8 *ies, size_t ies_len, int scan_res); 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[], @@ -710,5 +846,17 @@ int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *buf, size_t len, unsigned int wait_time); 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, ...) +PRINTF_FORMAT(2, 3); +void p2p_err(struct p2p_data *p2p, const char *fmt, ...) +PRINTF_FORMAT(2, 3); #endif /* P2P_I_H */ diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c index 983dd6b36e405..558c6dd0c58fa 100644 --- a/src/p2p/p2p_invitation.c +++ b/src/p2p/p2p_invitation.c @@ -10,13 +10,15 @@ #include "common.h" #include "common/ieee802_11_defs.h" +#include "common/wpa_ctrl.h" #include "p2p_i.h" #include "p2p.h" static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p, struct p2p_device *peer, - const u8 *go_dev_addr) + const u8 *go_dev_addr, + int dev_pw_id) { struct wpabuf *buf; u8 *len; @@ -44,6 +46,9 @@ static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p, extra = wpabuf_len(wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_INV_REQ]) + extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_INV_REQ]); + buf = wpabuf_alloc(1000 + extra); if (buf == NULL) return NULL; @@ -62,8 +67,11 @@ static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p, p2p->client_timeout); p2p_buf_add_invitation_flags(buf, p2p->inv_persistent ? P2P_INVITATION_FLAGS_TYPE : 0); - p2p_buf_add_operating_channel(buf, p2p->cfg->country, - p2p->op_reg_class, p2p->op_channel); + if (p2p->inv_role != P2P_INVITE_ROLE_CLIENT || + !(peer->flags & P2P_DEV_NO_PREF_CHAN)) + p2p_buf_add_operating_channel(buf, p2p->cfg->country, + p2p->op_reg_class, + p2p->op_channel); if (p2p->inv_bssid_set) p2p_buf_add_group_bssid(buf, p2p->inv_bssid); p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels); @@ -82,6 +90,14 @@ static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p, wpabuf_put_buf(buf, wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_INV_REQ]) + wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_INV_REQ]); + + if (dev_pw_id >= 0) { + /* WSC IE in Invitation Request for NFC static handover */ + p2p_build_wps_ie(p2p, buf, dev_pw_id, 0); + } + return buf; } @@ -158,13 +174,12 @@ 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)); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received Invitation Request from " MACSTR " (freq=%d)", + p2p_dbg(p2p, "Received Invitation Request from " MACSTR " (freq=%d)", MAC2STR(sa), rx_freq); if (p2p_parse(data, len, &msg)) @@ -172,14 +187,12 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, dev = p2p_get_device(p2p, sa); if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invitation Request from unknown peer " - MACSTR, MAC2STR(sa)); + p2p_dbg(p2p, "Invitation Request from unknown peer " MACSTR, + MAC2STR(sa)); - if (p2p_add_device(p2p, sa, rx_freq, 0, 0, data + 1, len - 1, + if (p2p_add_device(p2p, sa, rx_freq, NULL, 0, data + 1, len - 1, 0)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invitation Request add device failed " + p2p_dbg(p2p, "Invitation Request add device failed " MACSTR, MAC2STR(sa)); status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; goto fail; @@ -187,18 +200,16 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, dev = p2p_get_device(p2p, sa); if (dev == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Reject Invitation Request from unknown " - "peer " MACSTR, MAC2STR(sa)); + p2p_dbg(p2p, "Reject Invitation Request from unknown peer " + MACSTR, MAC2STR(sa)); status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; goto fail; } } if (!msg.group_id || !msg.channel_list) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Mandatory attribute missing in Invitation " - "Request from " MACSTR, MAC2STR(sa)); + p2p_dbg(p2p, "Mandatory attribute missing in Invitation Request from " + MACSTR, MAC2STR(sa)); status = P2P_SC_FAIL_INVALID_PARAMS; goto fail; } @@ -211,46 +222,61 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, * the request was for a persistent group if the attribute is * missing. */ - wpa_printf(MSG_DEBUG, "P2P: Mandatory Invitation Flags " - "attribute missing from Invitation Request"); + p2p_dbg(p2p, "Mandatory Invitation Flags attribute missing from Invitation Request"); 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) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No common channels found"); + p2p_dbg(p2p, "No common channels found"); status = P2P_SC_FAIL_NO_COMMON_CHANNELS; goto fail; } + 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(&all_channels, &dev->channels, + &intersection); + p2p_channels_dump(p2p, "intersection", &intersection); + if (p2p->cfg->invitation_process) { status = p2p->cfg->invitation_process( p2p->cfg->cb_ctx, sa, msg.group_bssid, msg.group_id, msg.group_id + ETH_ALEN, msg.group_id_len - ETH_ALEN, - &go, group_bssid, &op_freq, persistent); + &go, group_bssid, &op_freq, persistent, &intersection, + 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) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Invitation " - "processing forced frequency %d MHz", op_freq); - if (p2p_freq_to_channel(p2p->cfg->country, op_freq, - ®_class, &channel) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unknown forced freq %d MHz from " - "invitation_process()", op_freq); + p2p_dbg(p2p, "Invitation processing forced frequency %d MHz", + op_freq); + if (p2p_freq_to_channel(op_freq, ®_class, &channel) < 0) { + p2p_dbg(p2p, "Unknown forced freq %d MHz from invitation_process()", + op_freq); status = P2P_SC_FAIL_NO_COMMON_CHANNELS; goto fail; } - p2p_channels_intersect(&p2p->cfg->channels, &dev->channels, - &intersection); if (!p2p_channels_includes(&intersection, reg_class, channel)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: forced freq %d MHz not in the supported " - "channels interaction", op_freq); + p2p_dbg(p2p, "forced freq %d MHz not in the supported channels interaction", + op_freq); status = P2P_SC_FAIL_NO_COMMON_CHANNELS; goto fail; } @@ -258,28 +284,21 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, if (status == P2P_SC_SUCCESS) channels = &intersection; } else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No forced channel from invitation processing - " - "figure out best one to use"); + p2p_dbg(p2p, "No forced channel from invitation processing - figure out best one to use"); - p2p_channels_intersect(&p2p->cfg->channels, &dev->channels, - &intersection); /* Default to own configuration as a starting point */ p2p->op_reg_class = p2p->cfg->op_reg_class; p2p->op_channel = p2p->cfg->op_channel; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Own default " - "op_class %d channel %d", + p2p_dbg(p2p, "Own default op_class %d channel %d", p2p->op_reg_class, p2p->op_channel); /* Use peer preference if specified and compatible */ if (msg.operating_channel) { int req_freq; req_freq = p2p_channel_to_freq( - (const char *) msg.operating_channel, msg.operating_channel[3], msg.operating_channel[4]); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " - "operating channel preference: %d MHz", + p2p_dbg(p2p, "Peer operating channel preference: %d MHz", req_freq); if (req_freq > 0 && p2p_channels_includes(&intersection, @@ -287,56 +306,47 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, msg.operating_channel[4])) { p2p->op_reg_class = msg.operating_channel[3]; p2p->op_channel = msg.operating_channel[4]; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Use peer preference op_class %d " - "channel %d", + p2p_dbg(p2p, "Use peer preference op_class %d channel %d", p2p->op_reg_class, p2p->op_channel); } else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Cannot use peer channel " - "preference"); + p2p_dbg(p2p, "Cannot use peer channel preference"); } } - if (!p2p_channels_includes(&intersection, p2p->op_reg_class, + /* Reselect the channel only for the case of the GO */ + if (go && + !p2p_channels_includes(&intersection, p2p->op_reg_class, p2p->op_channel)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Initially selected channel (op_class %d " - "channel %d) not in channel intersection - try " - "to reselect", + p2p_dbg(p2p, "Initially selected channel (op_class %d channel %d) not in channel intersection - try to reselect", p2p->op_reg_class, p2p->op_channel); p2p_reselect_channel(p2p, &intersection); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Re-selection result: op_class %d " - "channel %d", + p2p_dbg(p2p, "Re-selection result: op_class %d channel %d", p2p->op_reg_class, p2p->op_channel); if (!p2p_channels_includes(&intersection, p2p->op_reg_class, p2p->op_channel)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Peer does not support selected " - "operating channel (reg_class=%u " - "channel=%u)", + p2p_dbg(p2p, "Peer does not support selected operating channel (reg_class=%u channel=%u)", p2p->op_reg_class, p2p->op_channel); status = P2P_SC_FAIL_NO_COMMON_CHANNELS; goto fail; } + } else if (go && !(dev->flags & P2P_DEV_FORCE_FREQ) && + !p2p->cfg->cfg_op_channel) { + p2p_dbg(p2p, "Try to reselect channel selection with peer information received; previously selected op_class %u channel %u", + p2p->op_reg_class, p2p->op_channel); + p2p_reselect_channel(p2p, &intersection); } - op_freq = p2p_channel_to_freq(p2p->cfg->country, - p2p->op_reg_class, + op_freq = p2p_channel_to_freq(p2p->op_reg_class, p2p->op_channel); if (op_freq < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unknown operational channel " - "(country=%c%c reg_class=%u channel=%u)", + p2p_dbg(p2p, "Unknown operational channel (country=%c%c reg_class=%u channel=%u)", p2p->cfg->country[0], p2p->cfg->country[1], p2p->op_reg_class, p2p->op_channel); status = P2P_SC_FAIL_NO_COMMON_CHANNELS; goto fail; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Selected operating " - "channel - %d MHz", op_freq); + p2p_dbg(p2p, "Selected operating channel - %d MHz", op_freq); if (status == P2P_SC_SUCCESS) { reg_class = p2p->op_reg_class; @@ -359,12 +369,10 @@ fail: if (rx_freq > 0) freq = rx_freq; else - freq = p2p_channel_to_freq(p2p->cfg->country, - p2p->cfg->reg_class, + freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel); if (freq < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unknown regulatory class/channel"); + p2p_dbg(p2p, "Unknown regulatory class/channel"); goto out; } @@ -378,12 +386,17 @@ fail: p2p->inv_group_bssid_ptr = p2p->inv_group_bssid; } else p2p->inv_group_bssid_ptr = NULL; - if (msg.group_id_len - ETH_ALEN <= 32) { - os_memcpy(p2p->inv_ssid, msg.group_id + ETH_ALEN, - msg.group_id_len - ETH_ALEN); - p2p->inv_ssid_len = msg.group_id_len - ETH_ALEN; + if (msg.group_id) { + if (msg.group_id_len - ETH_ALEN <= 32) { + os_memcpy(p2p->inv_ssid, msg.group_id + ETH_ALEN, + msg.group_id_len - ETH_ALEN); + p2p->inv_ssid_len = msg.group_id_len - ETH_ALEN; + } + os_memcpy(p2p->inv_go_dev_addr, msg.group_id, ETH_ALEN); + } else { + p2p->inv_ssid_len = 0; + os_memset(p2p->inv_go_dev_addr, 0, ETH_ALEN); } - os_memcpy(p2p->inv_go_dev_addr, msg.group_id, ETH_ALEN); p2p->inv_status = status; p2p->inv_op_freq = op_freq; @@ -391,8 +404,7 @@ fail: if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, p2p->cfg->dev_addr, wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + p2p_dbg(p2p, "Failed to send Action frame"); } out: @@ -406,40 +418,120 @@ void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa, { struct p2p_device *dev; struct p2p_message msg; + struct p2p_channels intersection, *channels = NULL; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received Invitation Response from " MACSTR, + p2p_dbg(p2p, "Received Invitation Response from " MACSTR, MAC2STR(sa)); dev = p2p_get_device(p2p, sa); if (dev == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Ignore Invitation Response from unknown peer " + 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) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Ignore unexpected Invitation Response from 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) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Mandatory Status attribute missing in " - "Invitation Response from " MACSTR, MAC2STR(sa)); + 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; } - if (p2p->cfg->invitation_result) + /* + * 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)); +#ifdef CONFIG_P2P_STRICT + p2p_parse_free(&msg); + return; +#endif /* CONFIG_P2P_STRICT */ + /* Try to survive without peer channel list */ + channels = &p2p->channels; + } else if (!msg.channel_list) { + /* Non-success cases are not required to include Channel List */ + channels = &p2p->channels; + } else if (p2p_peer_channels_check(p2p, &p2p->channels, dev, + msg.channel_list, + msg.channel_list_len) < 0) { + p2p_dbg(p2p, "No common channels found"); + p2p_parse_free(&msg); + return; + } else { + p2p_channels_intersect(&p2p->channels, &dev->channels, + &intersection); + channels = &intersection; + } + + if (p2p->cfg->invitation_result) { + int peer_oper_freq = 0; + int freq = p2p_channel_to_freq(p2p->op_reg_class, + p2p->op_channel); + if (freq < 0) + freq = 0; + + if (msg.operating_channel) { + peer_oper_freq = p2p_channel_to_freq( + msg.operating_channel[3], + msg.operating_channel[4]); + if (peer_oper_freq < 0) + peer_oper_freq = 0; + } + p2p->cfg->invitation_result(p2p->cfg->cb_ctx, *msg.status, - msg.group_bssid); + msg.group_bssid, channels, sa, + freq, peer_oper_freq); + } p2p_parse_free(&msg); @@ -450,38 +542,39 @@ void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa, int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev, - const u8 *go_dev_addr) + const u8 *go_dev_addr, int dev_pw_id) { struct wpabuf *req; int freq; freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq; + if (freq <= 0) + freq = dev->oob_go_neg_freq; if (freq <= 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Listen/Operating frequency known for the " - "peer " MACSTR " to send Invitation Request", + p2p_dbg(p2p, "No Listen/Operating frequency known for the peer " + MACSTR " to send Invitation Request", MAC2STR(dev->info.p2p_device_addr)); return -1; } - req = p2p_build_invitation_req(p2p, dev, go_dev_addr); + req = p2p_build_invitation_req(p2p, dev, go_dev_addr, dev_pw_id); if (req == NULL) return -1; if (p2p->state != P2P_IDLE) p2p_stop_listen_for_freq(p2p, freq); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Sending Invitation Request"); + p2p_dbg(p2p, "Sending Invitation Request"); p2p_set_state(p2p, P2P_INVITE); p2p->pending_action_state = P2P_PENDING_INVITATION_REQUEST; p2p->invite_peer = dev; dev->invitation_reqs++; if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr, p2p->cfg->dev_addr, dev->info.p2p_device_addr, - wpabuf_head(req), wpabuf_len(req), 200) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + wpabuf_head(req), wpabuf_len(req), 500) < 0) { + p2p_dbg(p2p, "Failed to send Action frame"); /* Use P2P find to recover and retry */ p2p_set_timeout(p2p, 0, 0); + } else { + dev->flags |= P2P_DEV_WAIT_INV_REQ_ACK; } wpabuf_free(req); @@ -492,31 +585,34 @@ int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev, void p2p_invitation_req_cb(struct p2p_data *p2p, int success) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invitation Request TX callback: success=%d", success); + p2p_dbg(p2p, "Invitation Request TX callback: success=%d", success); if (p2p->invite_peer == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No pending Invite"); + p2p_dbg(p2p, "No pending Invite"); return; } + if (success) + p2p->invite_peer->flags &= ~P2P_DEV_WAIT_INV_REQ_ACK; + /* * Use P2P find, if needed, to find the other device from its listen * channel. */ p2p_set_state(p2p, P2P_INVITE); - p2p_set_timeout(p2p, 0, 100000); + p2p_set_timeout(p2p, 0, success ? 500000 : 100000); } void p2p_invitation_resp_cb(struct p2p_data *p2p, int success) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invitation Response TX callback: success=%d", success); + p2p_dbg(p2p, "Invitation Response TX callback: success=%d", success); p2p->cfg->send_action_done(p2p->cfg->cb_ctx); - if (success && p2p->cfg->invitation_received) { + if (!success) + p2p_dbg(p2p, "Assume Invitation Response was actually received by the peer even though Ack was not reported"); + + if (p2p->cfg->invitation_received) { p2p->cfg->invitation_received(p2p->cfg->cb_ctx, p2p->inv_sa, p2p->inv_group_bssid_ptr, @@ -531,41 +627,55 @@ void p2p_invitation_resp_cb(struct p2p_data *p2p, int success) int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role, const u8 *bssid, const u8 *ssid, size_t ssid_len, unsigned int force_freq, const u8 *go_dev_addr, - int persistent_group) + int persistent_group, unsigned int pref_freq, int dev_pw_id) { struct p2p_device *dev; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Request to invite peer " MACSTR " role=%d persistent=%d " + p2p_dbg(p2p, "Request to invite peer " MACSTR " role=%d persistent=%d " "force_freq=%u", MAC2STR(peer), role, persistent_group, force_freq); if (bssid) - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invitation for BSSID " MACSTR, MAC2STR(bssid)); + p2p_dbg(p2p, "Invitation for BSSID " MACSTR, MAC2STR(bssid)); if (go_dev_addr) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invitation for GO Device Address " MACSTR, + p2p_dbg(p2p, "Invitation for GO Device Address " MACSTR, MAC2STR(go_dev_addr)); os_memcpy(p2p->invite_go_dev_addr_buf, go_dev_addr, ETH_ALEN); p2p->invite_go_dev_addr = p2p->invite_go_dev_addr_buf; } else p2p->invite_go_dev_addr = NULL; - wpa_hexdump_ascii(MSG_DEBUG, "P2P: Invitation for SSID", + wpa_hexdump_ascii(MSG_DEBUG, "Invitation for SSID", ssid, ssid_len); + if (dev_pw_id >= 0) { + p2p_dbg(p2p, "Invitation to use Device Password ID %d", + 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)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Cannot invite unknown P2P Device " MACSTR, + if (dev == NULL || (dev->listen_freq <= 0 && dev->oper_freq <= 0 && + dev->oob_go_neg_freq <= 0)) { + p2p_dbg(p2p, "Cannot invite unknown P2P Device " MACSTR, MAC2STR(peer)); return -1; } + if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq, + role != P2P_INVITE_ROLE_CLIENT) < 0) + return -1; + + if (persistent_group && role == P2P_INVITE_ROLE_CLIENT && !force_freq && + !pref_freq) + dev->flags |= P2P_DEV_NO_PREF_CHAN; + else + dev->flags &= ~P2P_DEV_NO_PREF_CHAN; + if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) { if (!(dev->info.dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Cannot invite a P2P Device " MACSTR + p2p_dbg(p2p, "Cannot invite a P2P Device " MACSTR " that is in a group and is not discoverable", MAC2STR(peer)); } @@ -574,26 +684,6 @@ int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role, dev->invitation_reqs = 0; - if (force_freq) { - if (p2p_freq_to_channel(p2p->cfg->country, force_freq, - &p2p->op_reg_class, &p2p->op_channel) < - 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported frequency %u MHz", - force_freq); - return -1; - } - p2p->channels.reg_classes = 1; - p2p->channels.reg_class[0].channels = 1; - p2p->channels.reg_class[0].reg_class = p2p->op_reg_class; - p2p->channels.reg_class[0].channel[0] = p2p->op_channel; - } else { - p2p->op_reg_class = p2p->cfg->op_reg_class; - p2p->op_channel = p2p->cfg->op_channel; - os_memcpy(&p2p->channels, &p2p->cfg->channels, - sizeof(struct p2p_channels)); - } - if (p2p->state != P2P_IDLE) p2p_stop_find(p2p); @@ -604,5 +694,5 @@ int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role, os_memcpy(p2p->inv_ssid, ssid, ssid_len); p2p->inv_ssid_len = ssid_len; p2p->inv_persistent = persistent_group; - return p2p_invite_send(p2p, dev, go_dev_addr); + return p2p_invite_send(p2p, dev, go_dev_addr, dev_pw_id); } diff --git a/src/p2p/p2p_parse.c b/src/p2p/p2p_parse.c index 097a31de19d34..fd6a4610d839f 100644 --- a/src/p2p/p2p_parse.c +++ b/src/p2p/p2p_parse.c @@ -268,6 +268,125 @@ static int p2p_parse_attribute(u8 id, const u8 *data, u16 len, wpa_printf(MSG_DEBUG, "P2P: * Minor Reason Code: %u", *msg->minor_reason_code); break; + case P2P_ATTR_OOB_GO_NEG_CHANNEL: + if (len < 6) { + wpa_printf(MSG_DEBUG, "P2P: Too short OOB GO Neg " + "Channel attribute (length %d)", len); + return -1; + } + msg->oob_go_neg_channel = data; + wpa_printf(MSG_DEBUG, "P2P: * OOB GO Neg Channel: " + "Country %c%c(0x%02x) Operating Class %d " + "Channel Number %d Role %d", + 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); @@ -296,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; @@ -340,6 +463,7 @@ static int p2p_parse_wps_ie(const struct wpabuf *buf, struct p2p_message *msg) msg->dev_password_id = WPA_GET_BE16(attr.dev_password_id); wpa_printf(MSG_DEBUG, "P2P: Device Password ID: %d", msg->dev_password_id); + msg->dev_password_id_present = 1; } if (attr.primary_dev_type) { char devtype[WPS_DEV_TYPE_BUFSIZE]; @@ -367,6 +491,9 @@ static int p2p_parse_wps_ie(const struct wpabuf *buf, struct p2p_message *msg) msg->serial_number = attr.serial_number; msg->serial_number_len = attr.serial_number_len; + msg->oob_dev_password = attr.oob_dev_password; + msg->oob_dev_password_len = attr.oob_dev_password_len; + return 0; } @@ -450,6 +577,33 @@ int p2p_parse(const u8 *data, size_t len, struct p2p_message *msg) } +int p2p_parse_ies_separate(const u8 *wsc, size_t wsc_len, const u8 *p2p, + size_t p2p_len, struct p2p_message *msg) +{ + os_memset(msg, 0, sizeof(*msg)); + + msg->wps_attributes = wpabuf_alloc_copy(wsc, wsc_len); + if (msg->wps_attributes && + p2p_parse_wps_ie(msg->wps_attributes, msg)) { + p2p_parse_free(msg); + return -1; + } + + msg->p2p_attributes = wpabuf_alloc_copy(p2p, p2p_len); + if (msg->p2p_attributes && + p2p_parse_p2p_ie(msg->p2p_attributes, msg)) { + wpa_printf(MSG_DEBUG, "P2P: Failed to parse P2P IE data"); + if (msg->p2p_attributes) + wpa_hexdump_buf(MSG_MSGDUMP, "P2P: P2P IE data", + msg->p2p_attributes); + p2p_parse_free(msg); + return -1; + } + + return 0; +} + + /** * p2p_parse_free - Free temporary data from P2P parsing * @msg: Parsed attributes @@ -559,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; @@ -570,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; @@ -579,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; } @@ -594,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; } @@ -628,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; } @@ -640,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; @@ -655,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 ca33f17a17b8f..328b1e029ce57 100644 --- a/src/p2p/p2p_pd.c +++ b/src/p2p/p2p_pd.c @@ -10,6 +10,7 @@ #include "common.h" #include "common/ieee802_11_defs.h" +#include "common/wpa_ctrl.h" #include "wps/wps_defs.h" #include "p2p_i.h" #include "p2p.h" @@ -39,20 +40,145 @@ 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) extra = wpabuf_len(p2p->wfd_ie_prov_disc_req); #endif /* CONFIG_WIFI_DISPLAY */ + 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; @@ -60,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); } @@ -77,18 +216,27 @@ static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p, wpabuf_put_buf(buf, p2p->wfd_ie_prov_disc_req); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]) + wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]); + return buf; } 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; @@ -111,12 +259,106 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, extra = wpabuf_len(wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ - buf = wpabuf_alloc(100 + extra); + 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(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); @@ -125,38 +367,74 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, wpabuf_put_buf(buf, wfd_ie); #endif /* CONFIG_WIFI_DISPLAY */ + if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]) + wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]); + return buf; } +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; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received Provision Discovery Request from " MACSTR + p2p_dbg(p2p, "Received Provision Discovery Request from " MACSTR " with config methods 0x%x (freq=%d)", MAC2STR(sa), msg.wps_config_methods, rx_freq); dev = p2p_get_device(p2p, sa); if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Provision Discovery Request from " - "unknown peer " MACSTR, MAC2STR(sa)); + p2p_dbg(p2p, "Provision Discovery Request from unknown peer " + MACSTR, MAC2STR(sa)); - if (p2p_add_device(p2p, sa, rx_freq, 0, 0, data + 1, len - 1, + if (p2p_add_device(p2p, sa, rx_freq, NULL, 0, data + 1, len - 1, 0)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Provision Discovery Request add device " - "failed " MACSTR, MAC2STR(sa)); + p2p_dbg(p2p, "Provision Discovery Request add device failed " + MACSTR, MAC2STR(sa)); } } else if (msg.wfd_subelems) { wpabuf_free(dev->info.wfd_subelems); @@ -165,13 +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))) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Unsupported " - "Config Methods in Provision Discovery Request"); + 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], @@ -180,64 +458,319 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, break; } if (i == p2p->num_groups) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: PD " - "request for unknown P2P Group ID - reject"); + p2p_dbg(p2p, "PD request for unknown P2P Group ID - reject"); goto out; } } - 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) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR + 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) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR + 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; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Sending Provision Discovery Response"); + p2p_dbg(p2p, "Sending Provision Discovery Response"); if (rx_freq > 0) freq = rx_freq; else - freq = p2p_channel_to_freq(p2p->cfg->country, - p2p->cfg->reg_class, + freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel); if (freq < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unknown regulatory class/channel"); + p2p_dbg(p2p, "Unknown regulatory class/channel"); wpabuf_free(resp); 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) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); - } + p2p_dbg(p2p, "Failed to send Action frame"); + } else + p2p->send_action_in_progress = 1; 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; @@ -259,30 +792,63 @@ 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; + 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; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received Provision Discovery Response from " MACSTR + /* 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); dev = p2p_get_device(p2p, sa); if (dev == NULL || !dev->req_config_methods) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Ignore Provision Discovery Response from " - MACSTR " with no pending request", MAC2STR(sa)); + p2p_dbg(p2p, "Ignore Provision Discovery Response from " MACSTR + " with no pending request", MAC2STR(sa)); p2p_parse_free(&msg); return; } if (dev->dialog_token != msg.dialog_token) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Ignore Provision Discovery Response with " - "unexpected Dialog Token %u (expected %u)", + p2p_dbg(p2p, "Ignore Provision Discovery Response with unexpected Dialog Token %u (expected %u)", msg.dialog_token, dev->dialog_token); p2p_parse_free(&msg); return; @@ -294,6 +860,12 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, } /* + * Use a local copy of the requested config methods since + * p2p_reset_pending_pd() can clear this in the peer entry. + */ + req_config_methods = dev->req_config_methods; + + /* * If the response is from the peer to whom a user initiated request * was sent earlier, we reset that state info here. */ @@ -301,28 +873,114 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, os_memcmp(p2p->pending_pd_devaddr, sa, ETH_ALEN) == 0) p2p_reset_pending_pd(p2p); - if (msg.wps_config_methods != dev->req_config_methods) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer rejected " - "our Provision Discovery Request"); + if (msg.wps_config_methods != req_config_methods) { + p2p_dbg(p2p, "Peer rejected our Provision Discovery Request (received config_methods 0x%x expected 0x%x", + 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 = dev->req_config_methods; + report_config_methods = req_config_methods; dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY | - P2P_DEV_PD_PEER_KEYPAD); - if (dev->req_config_methods & WPS_CONFIG_DISPLAY) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR + 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) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Peer " MACSTR + 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 */ @@ -335,10 +993,8 @@ out: dev->req_config_methods = 0; p2p->cfg->send_action_done(p2p->cfg->cb_ctx); if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Start GO Neg after the PD-before-GO-Neg " - "workaround with " MACSTR, - MAC2STR(dev->info.p2p_device_addr)); + p2p_dbg(p2p, "Start GO Neg after the PD-before-GO-Neg workaround with " + MACSTR, MAC2STR(dev->info.p2p_device_addr)); dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG; p2p_connect_send(p2p, dev); return; @@ -346,6 +1002,11 @@ out: if (success && p2p->cfg->prov_disc_resp) p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa, report_config_methods); + + if (p2p->state == P2P_PD_DURING_FIND) { + p2p_clear_timeout(p2p); + p2p_continue_find(p2p); + } } @@ -361,9 +1022,8 @@ int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq; if (freq <= 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Listen/Operating frequency known for the " - "peer " MACSTR " to send Provision Discovery Request", + p2p_dbg(p2p, "No Listen/Operating frequency known for the peer " + MACSTR " to send Provision Discovery Request", MAC2STR(dev->info.p2p_device_addr)); return -1; } @@ -371,8 +1031,7 @@ int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) { if (!(dev->info.dev_capab & P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Cannot use PD with P2P Device " MACSTR + p2p_dbg(p2p, "Cannot use PD with P2P Device " MACSTR " that is in a group and is not discoverable", MAC2STR(dev->info.p2p_device_addr)); return -1; @@ -380,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; @@ -392,8 +1075,7 @@ int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr, p2p->cfg->dev_addr, dev->info.p2p_device_addr, wpabuf_head(req), wpabuf_len(req), 200) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + p2p_dbg(p2p, "Failed to send Action frame"); wpabuf_free(req); return -1; } @@ -406,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) { @@ -415,20 +1098,30 @@ int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr, if (dev == NULL) dev = p2p_get_device_interface(p2p, peer_addr); if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Provision " - "Discovery Request destination " MACSTR + p2p_dbg(p2p, "Provision Discovery Request destination " MACSTR " not yet known", MAC2STR(peer_addr)); + os_free(p2ps_prov); return -1; } - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Provision Discovery " - "Request with " MACSTR " (config methods 0x%x)", + 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) @@ -438,14 +1131,14 @@ int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr, if (p2p->state != P2P_IDLE && p2p->state != P2P_SEARCH && p2p->state != P2P_LISTEN_ONLY) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Busy with other " - "operations; postpone Provision Discovery Request " - "with " MACSTR " (config methods 0x%x)", + p2p_dbg(p2p, "Busy with other operations; postpone Provision Discovery Request with " + MACSTR " (config methods 0x%x)", MAC2STR(peer_addr), config_methods); return 0; } p2p->user_initiated_pd = user_initiated_pd; + p2p->pd_force_freq = force_freq; if (p2p->user_initiated_pd) p2p->pd_retries = MAX_PROV_DISC_REQ_RETRIES; @@ -481,4 +1174,5 @@ void p2p_reset_pending_pd(struct p2p_data *p2p) p2p->user_initiated_pd = 0; os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN); p2p->pd_retries = 0; + p2p->pd_force_freq = 0; } diff --git a/src/p2p/p2p_sd.c b/src/p2p/p2p_sd.c index abe1d6b586615..1a2af04b80043 100644 --- a/src/p2p/p2p_sd.c +++ b/src/p2p/p2p_sd.c @@ -52,6 +52,7 @@ struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p, { struct p2p_sd_query *q; int wsd = 0; + int count = 0; if (!(dev->info.dev_capab & P2P_DEV_CAPAB_SERVICE_DISCOVERY)) return NULL; /* peer does not support SD */ @@ -64,15 +65,52 @@ struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p, /* Use WSD only if the peer indicates support or it */ if (q->wsd && !wsd) continue; - if (q->for_all_peers && !(dev->flags & P2P_DEV_SD_INFO)) - return q; + /* if the query is a broadcast query */ + if (q->for_all_peers) { + /* + * check if there are any broadcast queries pending for + * this device + */ + if (dev->sd_pending_bcast_queries <= 0) + return NULL; + /* query number that needs to be send to the device */ + if (count == dev->sd_pending_bcast_queries - 1) + 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; +} + + +static void p2p_decrease_sd_bc_queries(struct p2p_data *p2p, int query_number) +{ + struct p2p_device *dev; + + p2p->num_p2p_sd_queries--; + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { + if (query_number <= dev->sd_pending_bcast_queries - 1) { + /* + * Query not yet sent to the device and it is to be + * removed, so update the pending count. + */ + dev->sd_pending_bcast_queries--; + } + } } @@ -80,10 +118,16 @@ static int p2p_unlink_sd_query(struct p2p_data *p2p, struct p2p_sd_query *query) { struct p2p_sd_query *q, *prev; + int query_number = 0; + q = p2p->sd_queries; prev = NULL; while (q) { if (q == query) { + /* If the query is a broadcast query, decrease one from + * all the devices */ + if (query->for_all_peers) + p2p_decrease_sd_bc_queries(p2p, query_number); if (prev) prev->next = q->next; else @@ -92,6 +136,8 @@ static int p2p_unlink_sd_query(struct p2p_data *p2p, p2p->sd_query = NULL; return 1; } + if (q->for_all_peers) + query_number++; prev = q; q = q->next; } @@ -118,6 +164,7 @@ void p2p_free_sd_queries(struct p2p_data *p2p) q = q->next; p2p_free_sd_query(prev); } + p2p->num_p2p_sd_queries = 0; } @@ -133,8 +180,7 @@ static struct wpabuf * p2p_build_sd_query(u16 update_indic, /* ANQP Query Request Frame */ len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); - wpabuf_put_be24(buf, OUI_WFA); - wpabuf_put_u8(buf, P2P_OUI_TYPE); + wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE); wpabuf_put_le16(buf, update_indic); /* Service Update Indicator */ wpabuf_put_buf(buf, tlvs); gas_anqp_set_element_len(buf, len_pos); @@ -157,8 +203,7 @@ static void p2p_send_gas_comeback_req(struct p2p_data *p2p, const u8 *dst, p2p->pending_action_state = P2P_NO_PENDING_ACTION; if (p2p_send_action(p2p, freq, dst, p2p->cfg->dev_addr, dst, wpabuf_head(req), wpabuf_len(req), 200) < 0) - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + p2p_dbg(p2p, "Failed to send Action frame"); wpabuf_free(req); } @@ -181,8 +226,7 @@ static struct wpabuf * p2p_build_sd_response(u8 dialog_token, u16 status_code, if (tlvs) { /* ANQP Query Response Frame */ len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); - wpabuf_put_be24(buf, OUI_WFA); - wpabuf_put_u8(buf, P2P_OUI_TYPE); + wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE); /* Service Update Indicator */ wpabuf_put_le16(buf, update_indic); wpabuf_put_buf(buf, tlvs); @@ -213,8 +257,7 @@ static struct wpabuf * p2p_build_gas_comeback_resp(u8 dialog_token, /* ANQP Query Response Frame */ wpabuf_put_le16(buf, ANQP_VENDOR_SPECIFIC); /* Info ID */ wpabuf_put_le16(buf, 3 + 1 + 2 + total_len); - wpabuf_put_be24(buf, OUI_WFA); - wpabuf_put_u8(buf, P2P_OUI_TYPE); + wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE); /* Service Update Indicator */ wpabuf_put_le16(buf, update_indic); } @@ -232,12 +275,12 @@ int p2p_start_sd(struct p2p_data *p2p, struct p2p_device *dev) int ret = 0; struct p2p_sd_query *query; int freq; + unsigned int wait_time; freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq; if (freq <= 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: No Listen/Operating frequency known for the " - "peer " MACSTR " to send SD Request", + p2p_dbg(p2p, "No Listen/Operating frequency known for the peer " + MACSTR " to send SD Request", MAC2STR(dev->info.p2p_device_addr)); return -1; } @@ -246,23 +289,25 @@ int p2p_start_sd(struct p2p_data *p2p, struct p2p_device *dev) if (query == NULL) return -1; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Start Service Discovery with " MACSTR, + p2p_dbg(p2p, "Start Service Discovery with " MACSTR, MAC2STR(dev->info.p2p_device_addr)); req = p2p_build_sd_query(p2p->srv_update_indic, query->tlvs); if (req == NULL) return -1; + dev->sd_reqs++; p2p->sd_peer = dev; p2p->sd_query = query; p2p->pending_action_state = P2P_PENDING_SD; + wait_time = 5000; + if (p2p->cfg->max_listen && wait_time > p2p->cfg->max_listen) + wait_time = p2p->cfg->max_listen; if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr, p2p->cfg->dev_addr, dev->info.p2p_device_addr, - wpabuf_head(req), wpabuf_len(req), 5000) < 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + wpabuf_head(req), wpabuf_len(req), wait_time) < 0) { + p2p_dbg(p2p, "Failed to send Action frame"); ret = -1; } @@ -290,8 +335,7 @@ void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa, if (rx_freq > 0) freq = rx_freq; else - freq = p2p_channel_to_freq(p2p->cfg->country, - p2p->cfg->reg_class, + freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel); if (freq < 0) return; @@ -300,14 +344,12 @@ void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa, return; dialog_token = *pos++; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: GAS Initial Request from " MACSTR " (dialog token %u, " - "freq %d)", + p2p_dbg(p2p, "GAS Initial Request from " MACSTR + " (dialog token %u, freq %d)", MAC2STR(sa), dialog_token, rx_freq); if (*pos != WLAN_EID_ADV_PROTO) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unexpected IE in GAS Initial Request: %u", *pos); + p2p_dbg(p2p, "Unexpected IE in GAS Initial Request: %u", *pos); return; } pos++; @@ -315,15 +357,13 @@ void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa, slen = *pos++; next = pos + slen; if (next > end || slen < 2) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invalid IE in GAS Initial Request"); + p2p_dbg(p2p, "Invalid IE in GAS Initial Request"); return; } pos++; /* skip QueryRespLenLimit and PAME-BI */ if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported GAS advertisement protocol id %u", + p2p_dbg(p2p, "Unsupported GAS advertisement protocol id %u", *pos); return; } @@ -342,8 +382,7 @@ void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa, if (pos + 4 > end) return; if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); + p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); return; } pos += 2; @@ -351,30 +390,21 @@ void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa, slen = WPA_GET_LE16(pos); pos += 2; if (pos + slen > end || slen < 3 + 1) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invalid ANQP Query Request length"); + p2p_dbg(p2p, "Invalid ANQP Query Request length"); return; } - if (WPA_GET_BE24(pos) != OUI_WFA) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported ANQP OUI %06x", WPA_GET_BE24(pos)); + if (WPA_GET_BE32(pos) != P2P_IE_VENDOR_TYPE) { + p2p_dbg(p2p, "Unsupported ANQP vendor OUI-type %08x", + WPA_GET_BE32(pos)); return; } - pos += 3; - - if (*pos != P2P_OUI_TYPE) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported ANQP vendor type %u", *pos); - return; - } - pos++; + pos += 4; if (pos + 2 > end) return; update_indic = WPA_GET_LE16(pos); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Service Update Indicator: %u", update_indic); + p2p_dbg(p2p, "Service Update Indicator: %u", update_indic); pos += 2; p2p->cfg->sd_request(p2p->cfg->cb_ctx, freq, sa, dialog_token, @@ -390,8 +420,7 @@ void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst, /* TODO: fix the length limit to match with the maximum frame length */ if (wpabuf_len(resp_tlvs) > 1400) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: SD response long " - "enough to require fragmentation"); + p2p_dbg(p2p, "SD response long enough to require fragmentation"); if (p2p->sd_resp) { /* * TODO: Could consider storing the fragmented response @@ -400,14 +429,12 @@ void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst, * Though, that would eat more memory, so there are * also benefits to just using a single buffer. */ - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Drop " - "previous SD response"); + p2p_dbg(p2p, "Drop previous SD response"); wpabuf_free(p2p->sd_resp); } p2p->sd_resp = wpabuf_dup(resp_tlvs); if (p2p->sd_resp == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_ERROR, "P2P: Failed to " - "allocate SD response fragmentation area"); + p2p_err(p2p, "Failed to allocate SD response fragmentation area"); return; } os_memcpy(p2p->sd_resp_addr, dst, ETH_ALEN); @@ -417,8 +444,7 @@ void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst, resp = p2p_build_sd_response(dialog_token, WLAN_STATUS_SUCCESS, 1, p2p->srv_update_indic, NULL); } else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: SD response fits " - "in initial response"); + p2p_dbg(p2p, "SD response fits in initial response"); resp = p2p_build_sd_response(dialog_token, WLAN_STATUS_SUCCESS, 0, p2p->srv_update_indic, resp_tlvs); @@ -430,8 +456,7 @@ void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst, if (p2p_send_action(p2p, freq, dst, p2p->cfg->dev_addr, p2p->cfg->dev_addr, wpabuf_head(resp), wpabuf_len(resp), 200) < 0) - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + p2p_dbg(p2p, "Failed to send Action frame"); wpabuf_free(resp); } @@ -451,21 +476,18 @@ void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL || os_memcmp(sa, p2p->sd_peer->info.p2p_device_addr, ETH_ALEN) != 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Ignore unexpected GAS Initial Response from " + p2p_dbg(p2p, "Ignore unexpected GAS Initial Response from " MACSTR, MAC2STR(sa)); return; } p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p_clear_timeout(p2p); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received GAS Initial Response from " MACSTR " (len=%d)", + p2p_dbg(p2p, "Received GAS Initial Response from " MACSTR " (len=%d)", MAC2STR(sa), (int) len); if (len < 5 + 2) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Too short GAS Initial Response frame"); + p2p_dbg(p2p, "Too short GAS Initial Response frame"); return; } @@ -475,20 +497,16 @@ void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, pos += 2; comeback_delay = WPA_GET_LE16(pos); pos += 2; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: dialog_token=%u status_code=%u comeback_delay=%u", + p2p_dbg(p2p, "dialog_token=%u status_code=%u comeback_delay=%u", dialog_token, status_code, comeback_delay); if (status_code) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Service Discovery failed: status code %u", + p2p_dbg(p2p, "Service Discovery failed: status code %u", status_code); return; } if (*pos != WLAN_EID_ADV_PROTO) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unexpected IE in GAS Initial Response: %u", - *pos); + p2p_dbg(p2p, "Unexpected IE in GAS Initial Response: %u", *pos); return; } pos++; @@ -496,15 +514,13 @@ void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, slen = *pos++; next = pos + slen; if (next > end || slen < 2) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invalid IE in GAS Initial Response"); + p2p_dbg(p2p, "Invalid IE in GAS Initial Response"); return; } pos++; /* skip QueryRespLenLimit and PAME-BI */ if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported GAS advertisement protocol id %u", + p2p_dbg(p2p, "Unsupported GAS advertisement protocol id %u", *pos); return; } @@ -512,27 +528,22 @@ void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, pos = next; /* Query Response */ if (pos + 2 > end) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Too short Query " - "Response"); + p2p_dbg(p2p, "Too short Query Response"); return; } slen = WPA_GET_LE16(pos); pos += 2; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Query Response Length: %d", - slen); + p2p_dbg(p2p, "Query Response Length: %d", slen); if (pos + slen > end) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Not enough Query " - "Response data"); + p2p_dbg(p2p, "Not enough Query Response data"); return; } end = pos + slen; if (comeback_delay) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Fragmented " - "response - request fragments"); + p2p_dbg(p2p, "Fragmented response - request fragments"); if (p2p->sd_rx_resp) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Drop " - "old SD reassembly buffer"); + p2p_dbg(p2p, "Drop old SD reassembly buffer"); wpabuf_free(p2p->sd_rx_resp); p2p->sd_rx_resp = NULL; } @@ -544,8 +555,7 @@ void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, if (pos + 4 > end) return; if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); + p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); return; } pos += 2; @@ -553,41 +563,29 @@ void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, slen = WPA_GET_LE16(pos); pos += 2; if (pos + slen > end || slen < 3 + 1) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invalid ANQP Query Response length"); + p2p_dbg(p2p, "Invalid ANQP Query Response length"); return; } - if (WPA_GET_BE24(pos) != OUI_WFA) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported ANQP OUI %06x", WPA_GET_BE24(pos)); + if (WPA_GET_BE32(pos) != P2P_IE_VENDOR_TYPE) { + p2p_dbg(p2p, "Unsupported ANQP vendor OUI-type %08x", + WPA_GET_BE32(pos)); return; } - pos += 3; - - if (*pos != P2P_OUI_TYPE) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported ANQP vendor type %u", *pos); - return; - } - pos++; + pos += 4; if (pos + 2 > end) return; update_indic = WPA_GET_LE16(pos); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Service Update Indicator: %u", update_indic); + p2p_dbg(p2p, "Service Update Indicator: %u", update_indic); pos += 2; - p2p->sd_peer->flags |= P2P_DEV_SD_INFO; - p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE; p2p->sd_peer = NULL; if (p2p->sd_query) { if (!p2p->sd_query->for_all_peers) { struct p2p_sd_query *q; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Remove completed SD query %p", + p2p_dbg(p2p, "Remove completed SD query %p", p2p->sd_query); q = p2p->sd_query; p2p_unlink_sd_query(p2p, p2p->sd_query); @@ -615,22 +613,20 @@ void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa, if (len < 1) return; dialog_token = *data; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Dialog Token: %u", - dialog_token); + p2p_dbg(p2p, "Dialog Token: %u", dialog_token); if (dialog_token != p2p->sd_resp_dialog_token) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending SD " - "response fragment for dialog token %u", dialog_token); + p2p_dbg(p2p, "No pending SD response fragment for dialog token %u", + dialog_token); return; } if (p2p->sd_resp == NULL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending SD " - "response fragment available"); + p2p_dbg(p2p, "No pending SD response fragment available"); return; } if (os_memcmp(sa, p2p->sd_resp_addr, ETH_ALEN) != 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending SD " - "response fragment for " MACSTR, MAC2STR(sa)); + p2p_dbg(p2p, "No pending SD response fragment for " MACSTR, + MAC2STR(sa)); return; } @@ -647,19 +643,16 @@ void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa, wpabuf_len(p2p->sd_resp)); if (resp == NULL) return; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Send GAS Comeback " - "Response (frag_id %d more=%d frag_len=%d)", + p2p_dbg(p2p, "Send GAS Comeback Response (frag_id %d more=%d frag_len=%d)", p2p->sd_frag_id, more, (int) frag_len); p2p->sd_frag_id++; p2p->sd_resp_pos += frag_len; if (more) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: %d more bytes " - "remain to be sent", + p2p_dbg(p2p, "%d more bytes remain to be sent", (int) (wpabuf_len(p2p->sd_resp) - p2p->sd_resp_pos)); } else { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: All fragments of " - "SD response sent"); + p2p_dbg(p2p, "All fragments of SD response sent"); wpabuf_free(p2p->sd_resp); p2p->sd_resp = NULL; } @@ -668,8 +661,7 @@ void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa, if (p2p_send_action(p2p, rx_freq, sa, p2p->cfg->dev_addr, p2p->cfg->dev_addr, wpabuf_head(resp), wpabuf_len(resp), 200) < 0) - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Failed to send Action frame"); + p2p_dbg(p2p, "Failed to send Action frame"); wpabuf_free(resp); } @@ -692,21 +684,18 @@ void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, if (p2p->state != P2P_SD_DURING_FIND || p2p->sd_peer == NULL || os_memcmp(sa, p2p->sd_peer->info.p2p_device_addr, ETH_ALEN) != 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Ignore unexpected GAS Comeback Response from " + p2p_dbg(p2p, "Ignore unexpected GAS Comeback Response from " MACSTR, MAC2STR(sa)); return; } p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p_clear_timeout(p2p); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Received GAS Comeback Response from " MACSTR " (len=%d)", + p2p_dbg(p2p, "Received GAS Comeback Response from " MACSTR " (len=%d)", MAC2STR(sa), (int) len); if (len < 6 + 2) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Too short GAS Comeback Response frame"); + p2p_dbg(p2p, "Too short GAS Comeback Response frame"); return; } @@ -719,22 +708,19 @@ void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, pos++; comeback_delay = WPA_GET_LE16(pos); pos += 2; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: dialog_token=%u status_code=%u frag_id=%d more_frags=%d " + p2p_dbg(p2p, "dialog_token=%u status_code=%u frag_id=%d more_frags=%d " "comeback_delay=%u", dialog_token, status_code, frag_id, more_frags, comeback_delay); /* TODO: check frag_id match */ if (status_code) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Service Discovery failed: status code %u", + p2p_dbg(p2p, "Service Discovery failed: status code %u", status_code); return; } if (*pos != WLAN_EID_ADV_PROTO) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unexpected IE in GAS Comeback Response: %u", + p2p_dbg(p2p, "Unexpected IE in GAS Comeback Response: %u", *pos); return; } @@ -743,15 +729,13 @@ void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, slen = *pos++; next = pos + slen; if (next > end || slen < 2) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invalid IE in GAS Comeback Response"); + p2p_dbg(p2p, "Invalid IE in GAS Comeback Response"); return; } pos++; /* skip QueryRespLenLimit and PAME-BI */ if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported GAS advertisement protocol id %u", + p2p_dbg(p2p, "Unsupported GAS advertisement protocol id %u", *pos); return; } @@ -759,22 +743,18 @@ void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, pos = next; /* Query Response */ if (pos + 2 > end) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Too short Query " - "Response"); + p2p_dbg(p2p, "Too short Query Response"); return; } slen = WPA_GET_LE16(pos); pos += 2; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Query Response Length: %d", - slen); + p2p_dbg(p2p, "Query Response Length: %d", slen); if (pos + slen > end) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Not enough Query " - "Response data"); + p2p_dbg(p2p, "Not enough Query Response data"); return; } if (slen == 0) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No Query Response " - "data"); + p2p_dbg(p2p, "No Query Response data"); return; } end = pos + slen; @@ -791,77 +771,60 @@ void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, if (pos + 4 > end) return; if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); + p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); return; } pos += 2; slen = WPA_GET_LE16(pos); pos += 2; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: ANQP Query Response " - "length: %u", slen); + p2p_dbg(p2p, "ANQP Query Response length: %u", slen); if (slen < 3 + 1) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Invalid ANQP Query Response length"); + p2p_dbg(p2p, "Invalid ANQP Query Response length"); return; } if (pos + 4 > end) return; - if (WPA_GET_BE24(pos) != OUI_WFA) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported ANQP OUI %06x", WPA_GET_BE24(pos)); + if (WPA_GET_BE32(pos) != P2P_IE_VENDOR_TYPE) { + p2p_dbg(p2p, "Unsupported ANQP vendor OUI-type %08x", + WPA_GET_BE32(pos)); return; } - pos += 3; - - if (*pos != P2P_OUI_TYPE) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Unsupported ANQP vendor type %u", *pos); - return; - } - pos++; + pos += 4; if (pos + 2 > end) return; p2p->sd_rx_update_indic = WPA_GET_LE16(pos); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Service Update Indicator: %u", p2p->sd_rx_update_indic); + p2p_dbg(p2p, "Service Update Indicator: %u", p2p->sd_rx_update_indic); pos += 2; skip_nqp_header: if (wpabuf_resize(&p2p->sd_rx_resp, end - pos) < 0) return; wpabuf_put_data(p2p->sd_rx_resp, pos, end - pos); - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Current SD reassembly " - "buffer length: %u", + p2p_dbg(p2p, "Current SD reassembly buffer length: %u", (unsigned int) wpabuf_len(p2p->sd_rx_resp)); if (more_frags) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: More fragments " - "remains"); + p2p_dbg(p2p, "More fragments remains"); /* TODO: what would be a good size limit? */ if (wpabuf_len(p2p->sd_rx_resp) > 64000) { wpabuf_free(p2p->sd_rx_resp); p2p->sd_rx_resp = NULL; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Too long " - "SD response - drop it"); + p2p_dbg(p2p, "Too long SD response - drop it"); return; } p2p_send_gas_comeback_req(p2p, sa, dialog_token, rx_freq); return; } - p2p->sd_peer->flags |= P2P_DEV_SD_INFO; - p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE; p2p->sd_peer = NULL; if (p2p->sd_query) { if (!p2p->sd_query->for_all_peers) { struct p2p_sd_query *q; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Remove completed SD query %p", + p2p_dbg(p2p, "Remove completed SD query %p", p2p->sd_query); q = p2p->sd_query; p2p_unlink_sd_query(p2p, p2p->sd_query); @@ -904,12 +867,20 @@ void * p2p_sd_request(struct p2p_data *p2p, const u8 *dst, q->next = p2p->sd_queries; p2p->sd_queries = q; - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Added SD Query %p", q); + p2p_dbg(p2p, "Added SD Query %p", q); if (dst == NULL) { struct p2p_device *dev; - dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) - dev->flags &= ~P2P_DEV_SD_INFO; + + p2p->num_p2p_sd_queries++; + + /* Update all the devices for the newly added broadcast query */ + dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { + if (dev->sd_pending_bcast_queries <= 0) + dev->sd_pending_bcast_queries = 1; + else + dev->sd_pending_bcast_queries++; + } } return q; @@ -938,8 +909,7 @@ void p2p_sd_service_update(struct p2p_data *p2p) int p2p_sd_cancel_request(struct p2p_data *p2p, void *req) { if (p2p_unlink_sd_query(p2p, req)) { - wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, - "P2P: Cancel pending SD query %p", req); + p2p_dbg(p2p, "Cancel pending SD query %p", req); p2p_free_sd_query(req); return 0; } diff --git a/src/p2p/p2p_utils.c b/src/p2p/p2p_utils.c index bcc690d8469bb..f32751d79ca8b 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" @@ -46,127 +47,69 @@ int p2p_random(char *buf, size_t len) } -static int p2p_channel_to_freq_j4(int reg_class, int channel) -{ - /* Table J-4 in P802.11REVmb/D4.0 - Global operating classes */ - /* TODO: more regulatory classes */ - switch (reg_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; - } - return -1; -} - - /** * p2p_channel_to_freq - Convert channel info to frequency - * @country: Country code - * @reg_class: Regulatory class + * @op_class: Operating class * @channel: Channel number * Returns: Frequency in MHz or -1 if the specified channel is unknown */ -int p2p_channel_to_freq(const char *country, int reg_class, int channel) +int p2p_channel_to_freq(int op_class, int channel) { - if (country[2] == 0x04) - return p2p_channel_to_freq_j4(reg_class, channel); - - /* These are mainly for backwards compatibility; to be removed */ - switch (reg_class) { - case 1: /* US/1, EU/1, JP/1 = 5 GHz, channels 36,40,44,48 */ - if (channel < 36 || channel > 48) - return -1; - return 5000 + 5 * channel; - case 3: /* US/3 = 5 GHz, channels 149,153,157,161 */ - case 5: /* US/5 = 5 GHz, channels 149,153,157,161 */ - if (channel < 149 || channel > 161) - return -1; - return 5000 + 5 * channel; - case 4: /* EU/4 = 2.407 GHz, channels 1..13 */ - case 12: /* US/12 = 2.407 GHz, channels 1..11 */ - case 30: /* JP/30 = 2.407 GHz, channels 1..13 */ - if (channel < 1 || channel > 13) - return -1; - return 2407 + 5 * channel; - case 31: /* JP/31 = 2.414 GHz, channel 14 */ - if (channel != 14) - return -1; - return 2414 + 5 * channel; - } - - return -1; + return ieee80211_chan_to_freq(NULL, op_class, channel); } /** * p2p_freq_to_channel - Convert frequency into channel info - * @country: Country code - * @reg_class: Buffer for returning regulatory class + * @op_class: Buffer for returning operating class * @channel: Buffer for returning channel number * Returns: 0 on success, -1 if the specified frequency is unknown */ -int p2p_freq_to_channel(const char *country, unsigned int freq, u8 *reg_class, - u8 *channel) +int p2p_freq_to_channel(unsigned int freq, u8 *op_class, u8 *channel) { /* TODO: more operating classes */ if (freq >= 2412 && freq <= 2472) { - *reg_class = 81; /* 2.407 GHz, channels 1..13 */ + if ((freq - 2407) % 5) + return -1; + + *op_class = 81; /* 2.407 GHz, channels 1..13 */ *channel = (freq - 2407) / 5; return 0; } if (freq == 2484) { - *reg_class = 82; /* channel 14 */ + *op_class = 82; /* channel 14 */ *channel = 14; return 0; } if (freq >= 5180 && freq <= 5240) { - *reg_class = 115; /* 5 GHz, channels 36..48 */ + if ((freq - 5000) % 5) + return -1; + + *op_class = 115; /* 5 GHz, channels 36..48 */ *channel = (freq - 5000) / 5; return 0; } if (freq >= 5745 && freq <= 5805) { - *reg_class = 124; /* 5 GHz, channels 149..161 */ + if ((freq - 5000) % 5) + return -1; + + *op_class = 124; /* 5 GHz, channels 149..161 */ *channel = (freq - 5000) / 5; return 0; } + if (freq >= 58320 && freq <= 64800) { + if ((freq - 58320) % 2160) + return -1; + + *op_class = 180; /* 60 GHz, channels 1..4 */ + *channel = (freq - 56160) / 2160; + return 0; + } + return -1; } @@ -230,6 +173,115 @@ void p2p_channels_intersect(const struct p2p_channels *a, } +static void p2p_op_class_union(struct p2p_reg_class *cl, + const struct p2p_reg_class *b_cl) +{ + size_t i, j; + + for (i = 0; i < b_cl->channels; i++) { + for (j = 0; j < cl->channels; j++) { + if (b_cl->channel[i] == cl->channel[j]) + break; + } + if (j == cl->channels) { + if (cl->channels == P2P_MAX_REG_CLASS_CHANNELS) + return; + cl->channel[cl->channels++] = b_cl->channel[i]; + } + } +} + + +/** + * 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 + */ +void p2p_channels_union_inplace(struct p2p_channels *res, + const struct p2p_channels *b) +{ + size_t i, j; + + 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++) { + const struct p2p_reg_class *b_cl = &b->reg_class[j]; + if (cl->reg_class != b_cl->reg_class) + continue; + p2p_op_class_union(cl, b_cl); + } + } + + for (j = 0; j < b->reg_classes; j++) { + const struct p2p_reg_class *b_cl = &b->reg_class[j]; + + for (i = 0; i < res->reg_classes; i++) { + struct p2p_reg_class *cl = &res->reg_class[i]; + if (cl->reg_class == b_cl->reg_class) + break; + } + + if (i == res->reg_classes) { + if (res->reg_classes == P2P_MAX_REG_CLASSES) + return; + os_memcpy(&res->reg_class[res->reg_classes++], + b_cl, sizeof(struct p2p_reg_class)); + } + } +} + + +/** + * 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) +{ + size_t o, c; + + if (list == NULL) + return; + + o = 0; + while (o < chan->reg_classes) { + struct p2p_reg_class *op = &chan->reg_class[o]; + + c = 0; + while (c < op->channels) { + int freq = p2p_channel_to_freq(op->reg_class, + op->channel[c]); + if (freq > 0 && freq_range_list_includes(list, freq)) { + op->channels--; + os_memmove(&op->channel[c], + &op->channel[c + 1], + op->channels - c); + } else + c++; + } + + if (op->channels == 0) { + chan->reg_classes--; + os_memmove(&chan->reg_class[o], &chan->reg_class[o + 1], + (chan->reg_classes - o) * + sizeof(struct p2p_reg_class)); + } else + o++; + } +} + + /** * p2p_channels_includes - Check whether a channel is included in the list * @channels: List of supported channels @@ -254,12 +306,208 @@ int p2p_channels_includes(const struct p2p_channels *channels, u8 reg_class, } +int p2p_channels_includes_freq(const struct p2p_channels *channels, + unsigned int freq) +{ + size_t i, j; + for (i = 0; i < channels->reg_classes; i++) { + const struct p2p_reg_class *reg = &channels->reg_class[i]; + for (j = 0; j < reg->channels; j++) { + if (p2p_channel_to_freq(reg->reg_class, + reg->channel[j]) == (int) freq) + return 1; + } + } + return 0; +} + + int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq) { u8 op_reg_class, op_channel; - if (p2p_freq_to_channel(p2p->cfg->country, freq, - &op_reg_class, &op_channel) < 0) + if (p2p_freq_to_channel(freq, &op_reg_class, &op_channel) < 0) return 0; return p2p_channels_includes(&p2p->cfg->channels, op_reg_class, op_channel); } + + +int p2p_supported_freq_go(struct p2p_data *p2p, unsigned int freq) +{ + u8 op_reg_class, op_channel; + if (p2p_freq_to_channel(freq, &op_reg_class, &op_channel) < 0) + return 0; + return p2p_channels_includes(&p2p->cfg->channels, op_reg_class, + op_channel) && + !freq_range_list_includes(&p2p->no_go_freq, freq); +} + + +int p2p_supported_freq_cli(struct p2p_data *p2p, unsigned int freq) +{ + u8 op_reg_class, op_channel; + if (p2p_freq_to_channel(freq, &op_reg_class, &op_channel) < 0) + return 0; + return p2p_channels_includes(&p2p->cfg->channels, op_reg_class, + op_channel) || + p2p_channels_includes(&p2p->cfg->cli_channels, op_reg_class, + op_channel); +} + + +unsigned int p2p_get_pref_freq(struct p2p_data *p2p, + const struct p2p_channels *channels) +{ + unsigned int i; + int freq = 0; + const struct p2p_channels *tmpc = channels ? + channels : &p2p->cfg->channels; + + if (tmpc == NULL) + return 0; + + for (i = 0; p2p->cfg->pref_chan && i < p2p->cfg->num_pref_chan; i++) { + freq = p2p_channel_to_freq(p2p->cfg->pref_chan[i].op_class, + p2p->cfg->pref_chan[i].chan); + if (p2p_channels_includes_freq(tmpc, freq)) + return freq; + } + return 0; +} + + +void p2p_channels_dump(struct p2p_data *p2p, const char *title, + const struct p2p_channels *chan) +{ + char buf[500], *pos, *end; + size_t i, j; + int ret; + + pos = buf; + end = pos + sizeof(buf); + + for (i = 0; i < chan->reg_classes; i++) { + const struct p2p_reg_class *c; + c = &chan->reg_class[i]; + ret = os_snprintf(pos, end - pos, " %u:", c->reg_class); + if (os_snprintf_error(end - pos, ret)) + break; + pos += ret; + + for (j = 0; j < c->channels; j++) { + ret = os_snprintf(pos, end - pos, "%s%u", + j == 0 ? "" : ",", + c->channel[j]); + if (os_snprintf_error(end - pos, ret)) + break; + pos += ret; + } + } + *pos = '\0'; + + p2p_dbg(p2p, "%s:%s", title, buf); +} + + +static u8 p2p_channel_pick_random(const u8 *channels, unsigned int num_channels) +{ + unsigned int r; + if (os_get_random((u8 *) &r, sizeof(r)) < 0) + r = 0; + r %= num_channels; + return channels[r]; +} + + +int p2p_channel_select(struct p2p_channels *chans, const int *classes, + u8 *op_class, u8 *op_channel) +{ + unsigned int i, j; + + for (j = 0; classes == NULL || classes[j]; j++) { + for (i = 0; i < chans->reg_classes; i++) { + struct p2p_reg_class *c = &chans->reg_class[i]; + + if (c->channels == 0) + continue; + + if (classes == NULL || c->reg_class == classes[j]) { + /* + * Pick one of the available channels in the + * operating class at random. + */ + *op_class = c->reg_class; + *op_channel = p2p_channel_pick_random( + c->channel, c->channels); + return 0; + } + } + if (classes == NULL) + break; + } + + return -1; +} + + +int p2p_channel_random_social(struct p2p_channels *chans, u8 *op_class, + u8 *op_channel) +{ + u8 chan[4]; + unsigned int num_channels = 0; + + /* Try to find available social channels from 2.4 GHz */ + if (p2p_channels_includes(chans, 81, 1)) + chan[num_channels++] = 1; + if (p2p_channels_includes(chans, 81, 6)) + chan[num_channels++] = 6; + if (p2p_channels_includes(chans, 81, 11)) + chan[num_channels++] = 11; + + /* Try to find available social channels from 60 GHz */ + if (p2p_channels_includes(chans, 180, 2)) + chan[num_channels++] = 2; + + if (num_channels == 0) + return -1; + + *op_channel = p2p_channel_pick_random(chan, num_channels); + if (*op_channel == 2) + *op_class = 180; + else + *op_class = 81; + + 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; +} |