diff options
Diffstat (limited to 'contrib/wpa/src/p2p')
-rw-r--r-- | contrib/wpa/src/p2p/p2p.c | 387 | ||||
-rw-r--r-- | contrib/wpa/src/p2p/p2p.h | 89 | ||||
-rw-r--r-- | contrib/wpa/src/p2p/p2p_build.c | 8 | ||||
-rw-r--r-- | contrib/wpa/src/p2p/p2p_go_neg.c | 141 | ||||
-rw-r--r-- | contrib/wpa/src/p2p/p2p_group.c | 28 | ||||
-rw-r--r-- | contrib/wpa/src/p2p/p2p_i.h | 25 | ||||
-rw-r--r-- | contrib/wpa/src/p2p/p2p_invitation.c | 6 | ||||
-rw-r--r-- | contrib/wpa/src/p2p/p2p_parse.c | 98 | ||||
-rw-r--r-- | contrib/wpa/src/p2p/p2p_pd.c | 893 | ||||
-rw-r--r-- | contrib/wpa/src/p2p/p2p_sd.c | 82 | ||||
-rw-r--r-- | contrib/wpa/src/p2p/p2p_utils.c | 23 |
11 files changed, 1244 insertions, 536 deletions
diff --git a/contrib/wpa/src/p2p/p2p.c b/contrib/wpa/src/p2p/p2p.c index 767706c..157bf89 100644 --- a/contrib/wpa/src/p2p/p2p.c +++ b/contrib/wpa/src/p2p/p2p.c @@ -10,6 +10,7 @@ #include "common.h" #include "eloop.h" +#include "common/defs.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/wpa_ctrl.h" @@ -445,8 +446,9 @@ static struct p2p_device * p2p_create_device(struct p2p_data *p2p, static void p2p_copy_client_info(struct p2p_device *dev, struct p2p_client_info *cli) { - os_memcpy(dev->info.device_name, cli->dev_name, cli->dev_name_len); - dev->info.device_name[cli->dev_name_len] = '\0'; + p2p_copy_filter_devname(dev->info.device_name, + sizeof(dev->info.device_name), + cli->dev_name, cli->dev_name_len); dev->info.dev_capab = cli->dev_capab; dev->info.config_methods = cli->config_methods; os_memcpy(dev->info.pri_dev_type, cli->pri_dev_type, 8); @@ -636,11 +638,11 @@ static void p2p_update_peer_vendor_elems(struct p2p_device *dev, const u8 *ies, end = ies + ies_len; - for (pos = ies; pos + 1 < end; pos += len) { + for (pos = ies; end - pos > 1; pos += len) { id = *pos++; len = *pos++; - if (pos + len > end) + if (len > end - pos) break; if (id != WLAN_EID_VENDOR_SPECIFIC || len < 3) @@ -709,6 +711,7 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, struct p2p_message msg; const u8 *p2p_dev_addr; int wfd_changed; + int dev_name_changed; int i; struct os_reltime time_now; @@ -786,11 +789,11 @@ 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); + wpabuf_free(dev->info.p2ps_instance); + dev->info.p2ps_instance = NULL; + if (msg.adv_service_instance && msg.adv_service_instance_len) 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) { @@ -819,6 +822,9 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, } dev->info.level = level; + dev_name_changed = os_strncmp(dev->info.device_name, msg.device_name, + WPS_DEV_NAME_MAX_LEN) != 0; + p2p_copy_wps_info(p2p, dev, 0, &msg); for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { @@ -837,9 +843,12 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, wfd_changed = p2p_compare_wfd_info(dev, &msg); - if (msg.wfd_subelems) { + if (wfd_changed) { wpabuf_free(dev->info.wfd_subelems); - dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); + if (msg.wfd_subelems) + dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); + else + dev->info.wfd_subelems = NULL; } if (scan_res) { @@ -853,6 +862,7 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, p2p_update_peer_vendor_elems(dev, ies, ies_len); if (dev->flags & P2P_DEV_REPORTED && !wfd_changed && + !dev_name_changed && (!msg.adv_service_instance || (dev->flags & P2P_DEV_P2PS_REPORTED))) return 0; @@ -1000,8 +1010,16 @@ static void p2p_search(struct p2p_data *p2p) } p2p->cfg->stop_listen(p2p->cfg->cb_ctx); - if (p2p->find_type == P2P_FIND_PROGRESSIVE && - (freq = p2p_get_next_prog_freq(p2p)) > 0) { + if (p2p->find_pending_full && + (p2p->find_type == P2P_FIND_PROGRESSIVE || + p2p->find_type == P2P_FIND_START_WITH_FULL)) { + type = P2P_SCAN_FULL; + p2p_dbg(p2p, "Starting search (pending full scan)"); + p2p->find_pending_full = 0; + } else if ((p2p->find_type == P2P_FIND_PROGRESSIVE && + (freq = p2p_get_next_prog_freq(p2p)) > 0) || + (p2p->find_type == P2P_FIND_START_WITH_FULL && + (freq = p2p->find_specified_freq) > 0)) { type = P2P_SCAN_SOCIAL_PLUS_ONE; p2p_dbg(p2p, "Starting search (+ freq %u)", freq); } else { @@ -1058,7 +1076,7 @@ static int p2p_run_after_scan(struct p2p_data *p2p) p2p->after_scan_tx->bssid, (u8 *) (p2p->after_scan_tx + 1), p2p->after_scan_tx->len, - p2p->after_scan_tx->wait_time); + p2p->after_scan_tx->wait_time, NULL); os_free(p2p->after_scan_tx); p2p->after_scan_tx = NULL; return 1; @@ -1154,21 +1172,20 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, u8 seek_count, const char **seek, int freq) { int res; + struct os_reltime start; p2p_dbg(p2p, "Starting find (type=%d)", type); - os_get_reltime(&p2p->find_start); if (p2p->p2p_scan_running) { p2p_dbg(p2p, "p2p_scan is already running"); } p2p_free_req_dev_types(p2p); if (req_dev_types && num_req_dev_types) { - p2p->req_dev_types = os_malloc(num_req_dev_types * + p2p->req_dev_types = os_memdup(req_dev_types, + num_req_dev_types * WPS_DEV_TYPE_LEN); if (p2p->req_dev_types == NULL) return -1; - os_memcpy(p2p->req_dev_types, req_dev_types, - num_req_dev_types * WPS_DEV_TYPE_LEN); p2p->num_req_dev_types = num_req_dev_types; } @@ -1220,9 +1237,19 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING; p2p_clear_timeout(p2p); + if (p2p->pending_listen_freq) { + p2p_dbg(p2p, "Clear pending_listen_freq for p2p_find"); + p2p->pending_listen_freq = 0; + } p2p->cfg->stop_listen(p2p->cfg->cb_ctx); + p2p->find_pending_full = 0; p2p->find_type = type; + if (freq != 2412 && freq != 2437 && freq != 2462 && freq != 60480) + p2p->find_specified_freq = freq; + else + p2p->find_specified_freq = 0; p2p_device_clear_reported(p2p); + os_memset(p2p->sd_query_no_ack, 0, ETH_ALEN); p2p_set_state(p2p, P2P_SEARCH); p2p->search_delay = search_delay; p2p->in_search_delay = 0; @@ -1231,12 +1258,13 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, if (timeout) eloop_register_timeout(timeout, 0, p2p_find_timeout, p2p, NULL); + os_get_reltime(&start); switch (type) { case P2P_FIND_START_WITH_FULL: if (freq > 0) { /* * Start with the specified channel and then move to - * social channels only scans. + * scans for social channels and this specific channel. */ res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_SPECIFIC, freq, @@ -1262,9 +1290,15 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, return -1; } + if (!res) + p2p->find_start = start; + 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 */ + if (type == P2P_FIND_PROGRESSIVE || + (type == P2P_FIND_START_WITH_FULL && freq == 0)) + p2p->find_pending_full = 1; res = 0; /* do not report failure */ } else if (res != 0) { p2p_dbg(p2p, "Failed to start p2p_scan"); @@ -1440,7 +1474,8 @@ static void p2p_prepare_channel_best(struct p2p_data *p2p) 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->op_channel, + NULL, NULL) == 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 { @@ -1459,7 +1494,7 @@ static void p2p_prepare_channel_best(struct p2p_data *p2p) /** - * p2p_prepare_channel - Select operating channel for GO Negotiation + * p2p_prepare_channel - Select operating channel for GO Negotiation or P2PS PD * @p2p: P2P module context from p2p_init() * @dev: Selected peer device * @force_freq: Forced frequency in MHz or 0 if not forced @@ -1468,9 +1503,9 @@ static void p2p_prepare_channel_best(struct p2p_data *p2p) * Returns: 0 on success, -1 on failure (channel not supported for P2P) * * This function is used to do initial operating channel selection for GO - * Negotiation prior to having received peer information. The selected channel - * may be further optimized in p2p_reselect_channel() once the peer information - * is available. + * Negotiation prior to having received peer information or for P2PS PD + * signalling. The selected channel may be further optimized in + * p2p_reselect_channel() once the peer information is available. */ int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev, unsigned int force_freq, unsigned int pref_freq, int go) @@ -1826,6 +1861,7 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer) p2p_clear_timeout(p2p); p2p->ssid_set = 0; peer->go_neg_req_sent = 0; + peer->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE; peer->wps_method = WPS_NOT_READY; peer->oob_pw_id = 0; wpabuf_free(peer->go_neg_conf); @@ -2028,8 +2064,23 @@ static void p2p_add_dev_from_probe_req(struct p2p_data *p2p, const u8 *addr, dev = p2p_get_device(p2p, addr); if (dev) { - if (dev->country[0] == 0 && msg.listen_channel) - os_memcpy(dev->country, msg.listen_channel, 3); + if (msg.listen_channel) { + int freq; + + if (dev->country[0] == 0) + os_memcpy(dev->country, msg.listen_channel, 3); + + freq = p2p_channel_to_freq(msg.listen_channel[3], + msg.listen_channel[4]); + + if (freq > 0 && dev->listen_freq != freq) { + p2p_dbg(p2p, + "Updated peer " MACSTR " Listen channel (Probe Request): %d -> %d MHz", + MAC2STR(addr), dev->listen_freq, freq); + dev->listen_freq = freq; + } + } + os_get_reltime(&dev->last_seen); p2p_parse_free(&msg); return; /* already known */ @@ -2212,6 +2263,58 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p, return buf; } +static int p2p_build_probe_resp_buf(struct p2p_data *p2p, struct wpabuf *buf, + struct wpabuf *ies, + const u8 *addr, int rx_freq) +{ + struct ieee80211_mgmt *resp; + u8 channel, op_class; + + resp = wpabuf_put(buf, offsetof(struct ieee80211_mgmt, + u.probe_resp.variable)); + + resp->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) | + (WLAN_FC_STYPE_PROBE_RESP << 4)); + os_memcpy(resp->da, addr, ETH_ALEN); + os_memcpy(resp->sa, p2p->cfg->dev_addr, ETH_ALEN); + os_memcpy(resp->bssid, p2p->cfg->dev_addr, ETH_ALEN); + resp->u.probe_resp.beacon_int = host_to_le16(100); + /* hardware or low-level driver will setup seq_ctrl and timestamp */ + resp->u.probe_resp.capab_info = + host_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE | + WLAN_CAPABILITY_PRIVACY | + WLAN_CAPABILITY_SHORT_SLOT_TIME); + + wpabuf_put_u8(buf, WLAN_EID_SSID); + wpabuf_put_u8(buf, P2P_WILDCARD_SSID_LEN); + wpabuf_put_data(buf, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN); + + wpabuf_put_u8(buf, WLAN_EID_SUPP_RATES); + wpabuf_put_u8(buf, 8); + wpabuf_put_u8(buf, (60 / 5) | 0x80); + wpabuf_put_u8(buf, 90 / 5); + wpabuf_put_u8(buf, (120 / 5) | 0x80); + wpabuf_put_u8(buf, 180 / 5); + wpabuf_put_u8(buf, (240 / 5) | 0x80); + wpabuf_put_u8(buf, 360 / 5); + wpabuf_put_u8(buf, 480 / 5); + wpabuf_put_u8(buf, 540 / 5); + + if (!rx_freq) { + channel = p2p->cfg->channel; + } else if (p2p_freq_to_channel(rx_freq, &op_class, &channel)) { + p2p_err(p2p, "Failed to convert freq to channel"); + return -1; + } + + wpabuf_put_u8(buf, WLAN_EID_DS_PARAMS); + wpabuf_put_u8(buf, 1); + wpabuf_put_u8(buf, channel); + + wpabuf_put_buf(buf, ies); + + return 0; +} static int p2p_service_find_asp(struct p2p_data *p2p, const u8 *hash) { @@ -2245,10 +2348,8 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, { struct ieee802_11_elems elems; struct wpabuf *buf; - struct ieee80211_mgmt *resp; struct p2p_message msg; struct wpabuf *ies; - u8 channel, op_class; if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) == ParseFailed) { @@ -2360,7 +2461,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_dbg(p2p, "Probe Req requested Device Type did not match - ignore it"); p2p_parse_free(&msg); return P2P_PREQ_NOT_PROCESSED; } @@ -2392,49 +2493,12 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, return P2P_PREQ_NOT_PROCESSED; } - resp = wpabuf_put(buf, offsetof(struct ieee80211_mgmt, - u.probe_resp.variable)); - - resp->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) | - (WLAN_FC_STYPE_PROBE_RESP << 4)); - os_memcpy(resp->da, addr, ETH_ALEN); - os_memcpy(resp->sa, p2p->cfg->dev_addr, ETH_ALEN); - os_memcpy(resp->bssid, p2p->cfg->dev_addr, ETH_ALEN); - resp->u.probe_resp.beacon_int = host_to_le16(100); - /* hardware or low-level driver will setup seq_ctrl and timestamp */ - resp->u.probe_resp.capab_info = - host_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE | - WLAN_CAPABILITY_PRIVACY | - WLAN_CAPABILITY_SHORT_SLOT_TIME); - - wpabuf_put_u8(buf, WLAN_EID_SSID); - wpabuf_put_u8(buf, P2P_WILDCARD_SSID_LEN); - wpabuf_put_data(buf, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN); - - wpabuf_put_u8(buf, WLAN_EID_SUPP_RATES); - wpabuf_put_u8(buf, 8); - wpabuf_put_u8(buf, (60 / 5) | 0x80); - wpabuf_put_u8(buf, 90 / 5); - wpabuf_put_u8(buf, (120 / 5) | 0x80); - wpabuf_put_u8(buf, 180 / 5); - wpabuf_put_u8(buf, (240 / 5) | 0x80); - wpabuf_put_u8(buf, 360 / 5); - wpabuf_put_u8(buf, 480 / 5); - wpabuf_put_u8(buf, 540 / 5); - - if (!rx_freq) { - channel = p2p->cfg->channel; - } else if (p2p_freq_to_channel(rx_freq, &op_class, &channel)) { + if (p2p_build_probe_resp_buf(p2p, buf, ies, addr, rx_freq)) { wpabuf_free(ies); wpabuf_free(buf); return P2P_PREQ_NOT_PROCESSED; } - wpabuf_put_u8(buf, WLAN_EID_DS_PARAMS); - wpabuf_put_u8(buf, 1); - wpabuf_put_u8(buf, channel); - - wpabuf_put_buf(buf, ies); wpabuf_free(ies); p2p->cfg->send_probe_resp(p2p->cfg->cb_ctx, buf, rx_freq); @@ -2448,12 +2512,18 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst, enum p2p_probe_req_status p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, const u8 *bssid, const u8 *ie, size_t ie_len, - unsigned int rx_freq) + unsigned int rx_freq, int p2p_lo_started) { enum p2p_probe_req_status res; p2p_add_dev_from_probe_req(p2p, addr, ie, ie_len); + if (p2p_lo_started) { + p2p_dbg(p2p, + "Probe Response is offloaded, do not reply Probe Request"); + return P2P_PREQ_PROCESSED; + } + res = p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len, rx_freq); if (res != P2P_PREQ_PROCESSED && res != P2P_PREQ_NOT_PROCESSED) return res; @@ -2781,6 +2851,7 @@ void p2p_service_flush_asp(struct p2p_data *p2p) } p2p->p2ps_adv_list = NULL; + p2ps_prov_free(p2p); p2p_dbg(p2p, "All ASP advertisements flushed"); } @@ -2942,9 +3013,9 @@ void p2p_deinit(struct p2p_data *p2p) wpabuf_free(p2p->wfd_dev_info); wpabuf_free(p2p->wfd_assoc_bssid); wpabuf_free(p2p->wfd_coupled_sink_info); + wpabuf_free(p2p->wfd_r2_dev_info); #endif /* CONFIG_WIFI_DISPLAY */ - 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); @@ -2959,7 +3030,6 @@ void p2p_deinit(struct p2p_data *p2p) os_free(p2p->groups); p2ps_prov_free(p2p); wpabuf_free(p2p->sd_resp); - os_free(p2p->after_scan_tx); p2p_remove_wps_vendor_extensions(p2p); os_free(p2p->no_go_freq.range); p2p_service_flush_asp(p2p); @@ -2971,6 +3041,8 @@ void p2p_deinit(struct p2p_data *p2p) void p2p_flush(struct p2p_data *p2p) { struct p2p_device *dev, *prev; + + p2p_ext_listen(p2p, 0, 0); p2p_stop_find(p2p); dl_list_for_each_safe(dev, prev, &p2p->devices, struct p2p_device, list) { @@ -2981,6 +3053,10 @@ void p2p_flush(struct p2p_data *p2p) os_free(p2p->after_scan_tx); p2p->after_scan_tx = NULL; p2p->ssid_set = 0; + p2ps_prov_free(p2p); + p2p_reset_pending_pd(p2p); + p2p->override_pref_op_class = 0; + p2p->override_pref_channel = 0; } @@ -3157,13 +3233,18 @@ 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) { + int res; + 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) + res = p2p_start_sd(p2p, dev); + if (res == -2) + return -2; + if (res == 0) return 1; if (dev->req_config_methods && @@ -3183,7 +3264,7 @@ static int p2p_pre_find_operation(struct p2p_data *p2p, struct p2p_device *dev) void p2p_continue_find(struct p2p_data *p2p) { struct p2p_device *dev; - int found; + int found, res; p2p_set_state(p2p, P2P_SEARCH); @@ -3196,10 +3277,13 @@ void p2p_continue_find(struct p2p_data *p2p) } if (!found) continue; - if (p2p_pre_find_operation(p2p, dev) > 0) { + res = p2p_pre_find_operation(p2p, dev); + if (res > 0) { p2p->last_p2p_find_oper = dev; return; } + if (res == -2) + goto skip_sd; } /* @@ -3207,14 +3291,19 @@ void p2p_continue_find(struct p2p_data *p2p) * iteration device. */ dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { - if (p2p_pre_find_operation(p2p, dev) > 0) { + res = p2p_pre_find_operation(p2p, dev); + if (res > 0) { p2p->last_p2p_find_oper = dev; return; } + if (res == -2) + goto skip_sd; if (dev == p2p->last_p2p_find_oper) break; } +skip_sd: + os_memset(p2p->sd_query_no_ack, 0, ETH_ALEN); p2p_listen_in_find(p2p, 1); } @@ -3226,8 +3315,17 @@ static void p2p_sd_cb(struct p2p_data *p2p, int success) p2p->pending_action_state = P2P_NO_PENDING_ACTION; if (!success) { - if (p2p->sd_peer) + if (p2p->sd_peer) { + if (is_zero_ether_addr(p2p->sd_query_no_ack)) { + os_memcpy(p2p->sd_query_no_ack, + p2p->sd_peer->info.p2p_device_addr, + ETH_ALEN); + p2p_dbg(p2p, + "First SD Query no-ACK in this search iteration: " + MACSTR, MAC2STR(p2p->sd_query_no_ack)); + } p2p->cfg->send_action_done(p2p->cfg->cb_ctx); + } p2p->sd_peer = NULL; if (p2p->state != P2P_IDLE) p2p_continue_find(p2p); @@ -3326,6 +3424,43 @@ static void p2p_prov_disc_cb(struct p2p_data *p2p, int success) } /* + * If after PD Request the peer doesn't expect to receive PD Response + * the PD Request ACK indicates a completion of the current PD. This + * happens only on the advertiser side sending the follow-on PD Request + * with the status different than 12 (Success: accepted by user). + */ + if (p2p->p2ps_prov && !p2p->p2ps_prov->pd_seeker && + p2p->p2ps_prov->status != P2P_SC_SUCCESS_DEFERRED) { + p2p_dbg(p2p, "P2PS PD completion on Follow-on PD Request ACK"); + + 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 (p2p->cfg->p2ps_prov_complete) { + p2p->cfg->p2ps_prov_complete( + p2p->cfg->cb_ctx, + p2p->p2ps_prov->status, + p2p->p2ps_prov->adv_mac, + p2p->p2ps_prov->adv_mac, + p2p->p2ps_prov->session_mac, + NULL, p2p->p2ps_prov->adv_id, + p2p->p2ps_prov->session_id, + 0, 0, NULL, 0, 0, 0, + NULL, NULL, 0, 0, NULL, 0); + } + + if (p2p->user_initiated_pd) + p2p_reset_pending_pd(p2p); + + p2ps_prov_free(p2p); + return; + } + + /* * This postponing, of resetting pending_action_state, needs to be * done only for user initiated PD requests and not internal ones. */ @@ -3399,9 +3534,11 @@ int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq, * operation was started. */ p2p_dbg(p2p, "Ignore old scan result for " MACSTR - " (rx_time=%u.%06u)", + " (rx_time=%u.%06u find_start=%u.%06u)", MAC2STR(bssid), (unsigned int) rx_time->sec, - (unsigned int) rx_time->usec); + (unsigned int) rx_time->usec, + (unsigned int) p2p->find_start.sec, + (unsigned int) p2p->find_start.usec); return 0; } @@ -3426,7 +3563,8 @@ void p2p_scan_res_handled(struct p2p_data *p2p) } -void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id) +void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id, + unsigned int bands) { u8 dev_capab; u8 *len; @@ -3460,6 +3598,9 @@ void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id) p2p_buf_add_ext_listen_timing(ies, p2p->ext_listen_period, p2p->ext_listen_interval); + if (bands & BAND_60_GHZ) + p2p_buf_add_device_info(ies, p2p, NULL); + if (p2p->p2ps_seek && p2p->p2ps_seek_count) p2p_buf_add_service_hash(ies, p2p); @@ -3694,6 +3835,8 @@ void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst, break; case P2P_PENDING_INVITATION_RESPONSE: p2p_invitation_resp_cb(p2p, success); + if (p2p->inv_status != P2P_SC_SUCCESS) + p2p_check_after_scan_tx_continuation(p2p); break; case P2P_PENDING_DEV_DISC_REQUEST: p2p_dev_disc_req_cb(p2p, success); @@ -3751,6 +3894,19 @@ int p2p_listen_end(struct p2p_data *p2p, unsigned int freq) if (p2p->in_listen) return 0; /* Internal timeout will trigger the next step */ + if (p2p->state == P2P_WAIT_PEER_CONNECT && p2p->go_neg_peer && + p2p->pending_listen_freq) { + /* + * Better wait a bit if the driver is unable to start + * offchannel operation for some reason to continue with + * P2P_WAIT_PEER_(IDLE/CONNECT) state transitions. + */ + p2p_dbg(p2p, + "Listen operation did not seem to start - delay idle phase to avoid busy loop"); + p2p_set_timeout(p2p, 0, 100000); + return 1; + } + if (p2p->state == P2P_CONNECT_LISTEN && p2p->go_neg_peer) { if (p2p->go_neg_peer->connect_reqs >= 120) { p2p_dbg(p2p, "Timeout on sending GO Negotiation Request without getting response"); @@ -4613,9 +4769,12 @@ void p2p_set_managed_oper(struct p2p_data *p2p, int enabled) int p2p_config_get_random_social(struct p2p_config *p2p, u8 *op_class, - u8 *op_channel) + u8 *op_channel, + struct wpa_freq_range_list *avoid_list, + struct wpa_freq_range_list *disallow_list) { - return p2p_channel_random_social(&p2p->channels, op_class, op_channel); + return p2p_channel_random_social(&p2p->channels, op_class, op_channel, + avoid_list, disallow_list); } @@ -4695,11 +4854,10 @@ int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan, struct p2p_channel *n; if (pref_chan) { - n = os_malloc(num_pref_chan * sizeof(struct p2p_channel)); + n = os_memdup(pref_chan, + num_pref_chan * sizeof(struct p2p_channel)); if (n == NULL) return -1; - os_memcpy(n, pref_chan, - num_pref_chan * sizeof(struct p2p_channel)); } else n = NULL; @@ -4815,6 +4973,8 @@ 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) { + int res, scheduled; + if (p2p->p2p_scan_running) { p2p_dbg(p2p, "Delay Action frame TX until p2p_scan completes"); if (p2p->after_scan_tx) { @@ -4835,8 +4995,16 @@ int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst, return 0; } - return p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, dst, src, bssid, - buf, len, wait_time); + res = p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, dst, src, bssid, + buf, len, wait_time, &scheduled); + if (res == 0 && scheduled && p2p->in_listen && freq > 0 && + (unsigned int) p2p->drv_in_listen != freq) { + p2p_dbg(p2p, + "Stop listen on %d MHz to allow a frame to be sent immediately on %d MHz", + p2p->drv_in_listen, freq); + p2p_stop_listen_for_freq(p2p, freq); + } + return res; } @@ -5021,6 +5189,20 @@ int p2p_set_wfd_dev_info(struct p2p_data *p2p, const struct wpabuf *elem) } +int p2p_set_wfd_r2_dev_info(struct p2p_data *p2p, const struct wpabuf *elem) +{ + wpabuf_free(p2p->wfd_r2_dev_info); + if (elem) { + p2p->wfd_r2_dev_info = wpabuf_dup(elem); + if (p2p->wfd_r2_dev_info == NULL) + return -1; + } else + p2p->wfd_r2_dev_info = NULL; + + return 0; +} + + int p2p_set_wfd_assoc_bssid(struct p2p_data *p2p, const struct wpabuf *elem) { wpabuf_free(p2p->wfd_assoc_bssid); @@ -5400,3 +5582,42 @@ void p2p_set_own_pref_freq_list(struct p2p_data *p2p, i, p2p->pref_freq_list[i]); } } + + +void p2p_set_override_pref_op_chan(struct p2p_data *p2p, u8 op_class, + u8 chan) +{ + p2p->override_pref_op_class = op_class; + p2p->override_pref_channel = chan; +} + + +struct wpabuf * p2p_build_probe_resp_template(struct p2p_data *p2p, + unsigned int freq) +{ + struct wpabuf *ies, *buf; + u8 addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + int ret; + + ies = p2p_build_probe_resp_ies(p2p, NULL, 0); + if (!ies) { + wpa_printf(MSG_ERROR, + "CTRL: Failed to build Probe Response IEs"); + return NULL; + } + + buf = wpabuf_alloc(200 + wpabuf_len(ies)); + if (!buf) { + wpabuf_free(ies); + return NULL; + } + + ret = p2p_build_probe_resp_buf(p2p, buf, ies, addr, freq); + wpabuf_free(ies); + if (ret) { + wpabuf_free(buf); + return NULL; + } + + return buf; +} diff --git a/contrib/wpa/src/p2p/p2p.h b/contrib/wpa/src/p2p/p2p.h index b4060be..425b037 100644 --- a/contrib/wpa/src/p2p/p2p.h +++ b/contrib/wpa/src/p2p/p2p.h @@ -31,7 +31,7 @@ /** * P2P_MAX_REG_CLASSES - Maximum number of regulatory classes */ -#define P2P_MAX_REG_CLASSES 10 +#define P2P_MAX_REG_CLASSES 15 /** * P2P_MAX_REG_CLASS_CHANNELS - Maximum number of channels per regulatory class @@ -99,6 +99,15 @@ struct p2p_go_neg_results { int vht; + u8 max_oper_chwidth; + + unsigned int vht_center_freq2; + + /** + * he - Indicates if IEEE 802.11ax HE is enabled + */ + int he; + /** * ssid - SSID of the group */ @@ -224,6 +233,16 @@ struct p2ps_provision { u8 cpt_priority[P2PS_FEATURE_CAPAB_CPT_MAX + 1]; /** + * force_freq - The only allowed channel frequency in MHz or 0. + */ + unsigned int force_freq; + + /** + * pref_freq - Preferred operating frequency in MHz or 0. + */ + unsigned int pref_freq; + + /** * info - Vendor defined extra Provisioning information */ char info[0]; @@ -646,6 +665,8 @@ struct p2p_config { * @buf: Frame body (starting from Category field) * @len: Length of buf in octets * @wait_time: How many msec to wait for a response frame + * @scheduled: Return value indicating whether the transmissions was + * scheduled to happen once the radio is available * Returns: 0 on success, -1 on failure * * The Action frame may not be transmitted immediately and the status @@ -656,7 +677,7 @@ struct p2p_config { */ int (*send_action)(void *ctx, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, const u8 *buf, - size_t len, unsigned int wait_time); + size_t len, unsigned int wait_time, int *scheduled); /** * send_action_done - Notify that Action frame sequence was completed @@ -1024,6 +1045,8 @@ struct p2p_config { * @ssid_len: Buffer for returning length of @ssid * @group_iface: Buffer for returning whether a separate group interface * would be used + * @freq: Variable for returning the current operating frequency of a + * currently running P2P GO. * Returns: 1 if GO info found, 0 otherwise * * This is used to compose New Group settings (SSID, and intended @@ -1031,7 +1054,8 @@ struct p2p_config { * 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); + u8 *ssid, size_t *ssid_len, int *group_iface, + unsigned int *freq); /** * remove_stale_groups - Remove stale P2PS groups @@ -1056,7 +1080,9 @@ struct p2p_config { const u8 *persist_ssid, size_t persist_ssid_size, int response_done, int prov_start, const char *session_info, - const u8 *feat_cap, size_t feat_cap_len); + const u8 *feat_cap, size_t feat_cap_len, + unsigned int freq, const u8 *group_ssid, + size_t group_ssid_len); /** * prov_disc_resp_cb - Callback for indicating completion of PD Response @@ -1070,14 +1096,20 @@ struct p2p_config { /** * p2ps_group_capability - Determine group capability + * @ctx: Callback context from cb_ctx + * @incoming: Peer requested roles, expressed with P2PS_SETUP_* bitmap. + * @role: Local roles, expressed with P2PS_SETUP_* bitmap. + * @force_freq: Variable for returning forced frequency for the group. + * @pref_freq: Variable for returning preferred frequency for the group. + * Returns: P2PS_SETUP_* bitmap of group capability result. * - * 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. + * This function can be used to determine group capability and + * frequencies based on information from P2PS PD exchange and the + * current state of ongoing groups and driver capabilities. */ - u8 (*p2ps_group_capability)(void *ctx, u8 incoming, u8 role); + u8 (*p2ps_group_capability)(void *ctx, u8 incoming, u8 role, + unsigned int *force_freq, + unsigned int *pref_freq); /** * get_pref_freq_list - Get preferred frequency list for an interface @@ -1530,12 +1562,13 @@ enum p2p_probe_req_status { * @ie: Information elements from the Probe Request frame body * @ie_len: Length of ie buffer in octets * @rx_freq: Probe Request frame RX frequency + * @p2p_lo_started: Whether P2P Listen Offload is started * Returns: value indicating the type and status of the probe request */ enum p2p_probe_req_status p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst, const u8 *bssid, const u8 *ie, size_t ie_len, - unsigned int rx_freq); + unsigned int rx_freq, int p2p_lo_started); /** * p2p_rx_action - Report received Action frame @@ -1691,6 +1724,12 @@ struct p2p_group_config { int freq; /** + * ip_addr_alloc - Whether IP address allocation within 4-way handshake + * is supported + */ + int ip_addr_alloc; + + /** * cb_ctx - Context to use with callback functions */ void *cb_ctx; @@ -1877,8 +1916,10 @@ int p2p_assoc_req_ie(struct p2p_data *p2p, const u8 *bssid, u8 *buf, * @p2p: P2P module context from p2p_init() * @ies: Buffer for writing P2P IE * @dev_id: Device ID to search for or %NULL for any + * @bands: Frequency bands used in the scan (enum wpa_radio_work_band bitmap) */ -void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id); +void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id, + unsigned int bands); /** * p2p_scan_ie_buf_len - Get maximum buffer length needed for p2p_scan_ie @@ -1971,6 +2012,8 @@ void p2p_set_managed_oper(struct p2p_data *p2p, int enabled); * @p2p: P2P config * @op_class: Selected operating class * @op_channel: Selected social channel + * @avoid_list: Channel ranges to try to avoid or %NULL + * @disallow_list: Channel ranges to discard or %NULL * Returns: 0 on success, -1 on failure * * This function is used before p2p_init is called. A random social channel @@ -1978,7 +2021,9 @@ void p2p_set_managed_oper(struct p2p_data *p2p, int enabled); * returned on success. */ int p2p_config_get_random_social(struct p2p_config *p2p, u8 *op_class, - u8 *op_channel); + u8 *op_channel, + struct wpa_freq_range_list *avoid_list, + struct wpa_freq_range_list *disallow_list); int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel, u8 forced); @@ -2100,6 +2145,16 @@ int p2p_client_limit_reached(struct p2p_group *group); const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next); /** + * p2p_group_get_client_interface_addr - Get P2P Interface Address of a client in a group + * @group: P2P group context from p2p_group_init() + * @dev_addr: P2P Device Address of the client + * Returns: P2P Interface Address of the client if found or %NULL if no match + * found + */ +const u8 * p2p_group_get_client_interface_addr(struct p2p_group *group, + const u8 *dev_addr); + +/** * p2p_group_get_dev_addr - Get a P2P Device Address of a client in a group * @group: P2P group context from p2p_group_init() * @addr: P2P Interface Address of the client @@ -2222,6 +2277,7 @@ int p2p_set_wfd_ie_prov_disc_req(struct p2p_data *p2p, struct wpabuf *ie); int p2p_set_wfd_ie_prov_disc_resp(struct p2p_data *p2p, struct wpabuf *ie); int p2p_set_wfd_ie_go_neg(struct p2p_data *p2p, struct wpabuf *ie); int p2p_set_wfd_dev_info(struct p2p_data *p2p, const struct wpabuf *elem); +int p2p_set_wfd_r2_dev_info(struct p2p_data *p2p, const struct wpabuf *elem); int p2p_set_wfd_assoc_bssid(struct p2p_data *p2p, const struct wpabuf *elem); int p2p_set_wfd_coupled_sink_info(struct p2p_data *p2p, const struct wpabuf *elem); @@ -2241,7 +2297,7 @@ struct wpabuf * wifi_display_encaps(struct wpabuf *subelems); * discovery (p2p_find). A random number of 100 TU units is picked for each * Listen state iteration from [min_disc_int,max_disc_int] range. * - * max_disc_tu can be used to futher limit the discoverable duration. However, + * max_disc_tu can be used to further limit the discoverable duration. However, * it should be noted that use of this parameter is not recommended since it * would not be compliant with the P2P specification. */ @@ -2329,6 +2385,8 @@ void p2p_expire_peers(struct p2p_data *p2p); void p2p_set_own_pref_freq_list(struct p2p_data *p2p, const unsigned int *pref_freq_list, unsigned int size); +void p2p_set_override_pref_op_chan(struct p2p_data *p2p, u8 op_class, + u8 chan); /** * p2p_group_get_common_freqs - Get the group common frequencies @@ -2340,4 +2398,7 @@ void p2p_set_own_pref_freq_list(struct p2p_data *p2p, int p2p_group_get_common_freqs(struct p2p_group *group, int *common_freqs, unsigned int *num); +struct wpabuf * p2p_build_probe_resp_template(struct p2p_data *p2p, + unsigned int freq); + #endif /* P2P_H */ diff --git a/contrib/wpa/src/p2p/p2p_build.c b/contrib/wpa/src/p2p/p2p_build.c index 793d28b..63eb2e8 100644 --- a/contrib/wpa/src/p2p/p2p_build.c +++ b/contrib/wpa/src/p2p/p2p_build.c @@ -202,11 +202,11 @@ void p2p_buf_add_device_info(struct wpabuf *buf, struct p2p_data *p2p, if (peer && peer->wps_method != WPS_NOT_READY) { if (peer->wps_method == WPS_PBC) methods |= WPS_CONFIG_PUSHBUTTON; + else if (peer->wps_method == WPS_P2PS) + methods |= WPS_CONFIG_P2PS; 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 | @@ -802,7 +802,7 @@ int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id, wpabuf_put_be16(buf, p2p->cfg->config_methods); } - if (wps_build_wfa_ext(buf, 0, NULL, 0) < 0) + if (wps_build_wfa_ext(buf, 0, NULL, 0, 0) < 0) return -1; if (all_attr && p2p->cfg->num_sec_dev_types) { diff --git a/contrib/wpa/src/p2p/p2p_go_neg.c b/contrib/wpa/src/p2p/p2p_go_neg.c index 83b4356..65ab4b8 100644 --- a/contrib/wpa/src/p2p/p2p_go_neg.c +++ b/contrib/wpa/src/p2p/p2p_go_neg.c @@ -38,7 +38,7 @@ int p2p_peer_channels_check(struct p2p_data *p2p, struct p2p_channels *own, { const u8 *pos, *end; struct p2p_channels *ch; - size_t channels; + u8 channels; struct p2p_channels intersection; ch = &dev->channels; @@ -58,14 +58,14 @@ int p2p_peer_channels_check(struct p2p_data *p2p, struct p2p_channels *own, } pos += 3; - while (pos + 2 < end) { + while (end - pos > 2) { struct p2p_reg_class *cl = &ch->reg_class[ch->reg_classes]; cl->reg_class = *pos++; - if (pos + 1 + pos[0] > end) { + channels = *pos++; + if (channels > end - pos) { p2p_info(p2p, "Invalid peer Channel List"); return -1; } - channels = *pos++; cl->channels = channels > P2P_MAX_REG_CLASS_CHANNELS ? P2P_MAX_REG_CLASS_CHANNELS : channels; os_memcpy(cl->channel, pos, cl->channels); @@ -315,7 +315,12 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p, group_capab); 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 && !p2p->num_pref_freq) { + if (p2p->override_pref_op_class) { + p2p_dbg(p2p, "Override operating channel preference"); + p2p_buf_add_operating_channel(buf, p2p->cfg->country, + p2p->override_pref_op_class, + p2p->override_pref_channel); + } else if (peer && peer->go_state == REMOTE_GO && !p2p->num_pref_freq) { p2p_dbg(p2p, "Omit Operating Channel attribute"); } else { p2p_buf_add_operating_channel(buf, p2p->cfg->country, @@ -384,7 +389,7 @@ void p2p_reselect_channel(struct p2p_data *p2p, unsigned int i; const int op_classes_5ghz[] = { 124, 125, 115, 0 }; const int op_classes_ht40[] = { 126, 127, 116, 117, 0 }; - const int op_classes_vht[] = { 128, 0 }; + const int op_classes_vht[] = { 128, 129, 130, 0 }; if (p2p->own_freq_preference > 0 && p2p_freq_to_channel(p2p->own_freq_preference, @@ -562,26 +567,11 @@ static void p2p_check_pref_chan_no_recv(struct p2p_data *p2p, int go, * also supported by the peer device. */ for (i = 0; i < size && !found; i++) { - /* - * Make sure that the common frequency is: - * 1. Supported by peer - * 2. Allowed for P2P use. - */ + /* Make sure that the common frequency is supported by peer. */ oper_freq = freq_list[i]; if (p2p_freq_to_channel(oper_freq, &op_class, - &op_channel) < 0) { - p2p_dbg(p2p, "Unsupported frequency %u MHz", oper_freq); - continue; - } - 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, - "Freq %u MHz (oper_class %u channel %u) not allowed for P2P", - oper_freq, op_class, op_channel); - break; - } + &op_channel) < 0) + continue; /* cannot happen due to earlier check */ for (j = 0; j < msg->channel_list_len; j++) { if (op_channel != msg->channel_list[j]) @@ -602,8 +592,7 @@ static void p2p_check_pref_chan_no_recv(struct p2p_data *p2p, int go, oper_freq); } else { p2p_dbg(p2p, - "None of our preferred channels are supported by peer!. Use: %d MHz for oper_channel", - dev->oper_freq); + "None of our preferred channels are supported by peer!"); } } @@ -629,29 +618,9 @@ static void p2p_check_pref_chan_recv(struct p2p_data *p2p, int go, msg->pref_freq_list[2 * j + 1]); if (freq_list[i] != oper_freq) continue; - - /* - * Make sure that the found frequency is: - * 1. Supported - * 2. Allowed for P2P use. - */ if (p2p_freq_to_channel(oper_freq, &op_class, - &op_channel) < 0) { - p2p_dbg(p2p, "Unsupported frequency %u MHz", - oper_freq); - continue; - } - - 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, - "Freq %u MHz (oper_class %u channel %u) not allowed for P2P", - oper_freq, op_class, op_channel); - break; - } + &op_channel) < 0) + continue; /* cannot happen */ p2p->op_reg_class = op_class; p2p->op_channel = op_channel; os_memcpy(&p2p->channels, &p2p->cfg->channels, @@ -666,9 +635,7 @@ static void p2p_check_pref_chan_recv(struct p2p_data *p2p, int go, "Freq %d MHz is a common preferred channel for both peer and local, use it as operating channel", oper_freq); } else { - p2p_dbg(p2p, - "No common preferred channels found! Use: %d MHz for oper_channel", - dev->oper_freq); + p2p_dbg(p2p, "No common preferred channels found!"); } } @@ -679,6 +646,8 @@ void p2p_check_pref_chan(struct p2p_data *p2p, int go, unsigned int freq_list[P2P_MAX_PREF_CHANNELS], size; unsigned int i; u8 op_class, op_channel; + char txt[100], *pos, *end; + int res; /* * Use the preferred channel list from the driver only if there is no @@ -694,6 +663,39 @@ void p2p_check_pref_chan(struct p2p_data *p2p, int go, if (p2p->cfg->get_pref_freq_list(p2p->cfg->cb_ctx, go, &size, freq_list)) return; + /* Filter out frequencies that are not acceptable for P2P use */ + i = 0; + while (i < size) { + if (p2p_freq_to_channel(freq_list[i], &op_class, + &op_channel) < 0 || + (!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, + "Ignore local driver frequency preference %u MHz since it is not acceptable for P2P use (go=%d)", + freq_list[i], go); + if (size - i - 1 > 0) + os_memmove(&freq_list[i], &freq_list[i + 1], size - i - 1); + size--; + continue; + } + + /* Preferred frequency is acceptable for P2P use */ + i++; + } + + pos = txt; + end = pos + sizeof(txt); + for (i = 0; i < size; i++) { + res = os_snprintf(pos, end - pos, " %u", freq_list[i]); + if (os_snprintf_error(end - pos, res)) + break; + pos += res; + } + *pos = '\0'; + p2p_dbg(p2p, "Local driver frequency preference (size=%u):%s", + size, txt); /* * Check if peer's preference of operating channel is in @@ -703,20 +705,14 @@ void p2p_check_pref_chan(struct p2p_data *p2p, int go, if (freq_list[i] == (unsigned int) dev->oper_freq) break; } - if (i != size) { + if (i != size && + p2p_freq_to_channel(freq_list[i], &op_class, &op_channel) == 0) { /* Peer operating channel preference matches our preference */ - if (p2p_freq_to_channel(freq_list[i], &op_class, &op_channel) < - 0) { - p2p_dbg(p2p, - "Peer operating channel preference is unsupported frequency %u MHz", - freq_list[i]); - } else { - p2p->op_reg_class = op_class; - p2p->op_channel = op_channel; - os_memcpy(&p2p->channels, &p2p->cfg->channels, - sizeof(struct p2p_channels)); - return; - } + p2p->op_reg_class = op_class; + p2p->op_channel = op_channel; + os_memcpy(&p2p->channels, &p2p->cfg->channels, + sizeof(struct p2p_channels)); + return; } p2p_dbg(p2p, @@ -901,6 +897,14 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa, return; } + if (dev->go_neg_req_sent && + (dev->flags & P2P_DEV_PEER_WAITING_RESPONSE)) { + p2p_dbg(p2p, + "Do not reply since peer is waiting for us to start a new GO Negotiation and GO Neg Request already sent"); + p2p_parse_free(&msg); + return; + } + go = p2p_go_det(p2p->go_intent, *msg.go_intent); if (go < 0) { p2p_dbg(p2p, "Incompatible GO Intent"); @@ -1052,7 +1056,7 @@ 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), 500) < 0) { + wpabuf_head(resp), wpabuf_len(resp), 100) < 0) { p2p_dbg(p2p, "Failed to send Action frame"); } @@ -1260,6 +1264,11 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa, dev->client_timeout = msg.config_timeout[1]; } + if (msg.wfd_subelems) { + wpabuf_free(dev->info.wfd_subelems); + dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); + } + if (!msg.operating_channel && !go) { /* * Note: P2P Client may omit Operating Channel attribute to @@ -1386,7 +1395,7 @@ fail: if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, sa, wpabuf_head(dev->go_neg_conf), - wpabuf_len(dev->go_neg_conf), 200) < 0) { + wpabuf_len(dev->go_neg_conf), 50) < 0) { p2p_dbg(p2p, "Failed to send Action frame"); p2p_go_neg_failed(p2p, -1); p2p->cfg->send_action_done(p2p->cfg->cb_ctx); diff --git a/contrib/wpa/src/p2p/p2p_group.c b/contrib/wpa/src/p2p/p2p_group.c index 0d66993..aa18af6 100644 --- a/contrib/wpa/src/p2p/p2p_group.c +++ b/contrib/wpa/src/p2p/p2p_group.c @@ -155,7 +155,8 @@ 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; + if (group->cfg->ip_addr_alloc) + group_capab |= P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION; p2p_buf_add_capability(ie, dev_capab, group_capab); } @@ -296,14 +297,14 @@ static int wifi_display_add_dev_info_descr(struct wpabuf *buf, os_memset(zero_addr, 0, ETH_ALEN); pos = wpabuf_head_u8(m->wfd_ie); end = pos + wpabuf_len(m->wfd_ie); - while (pos + 1 < end) { + while (end - pos >= 3) { u8 id; u16 len; id = *pos++; len = WPA_GET_BE16(pos); pos += 2; - if (pos + len > end) + if (len > end - pos) break; switch (id) { @@ -366,6 +367,8 @@ wifi_display_build_go_ie(struct p2p_group *group) return NULL; if (group->p2p->wfd_dev_info) wpabuf_put_buf(wfd_subelems, group->p2p->wfd_dev_info); + if (group->p2p->wfd_r2_dev_info) + wpabuf_put_buf(wfd_subelems, group->p2p->wfd_r2_dev_info); if (group->p2p->wfd_assoc_bssid) wpabuf_put_buf(wfd_subelems, group->p2p->wfd_assoc_bssid); @@ -849,6 +852,20 @@ static struct p2p_group_member * p2p_group_get_client(struct p2p_group *group, } +const u8 * p2p_group_get_client_interface_addr(struct p2p_group *group, + const u8 *dev_addr) +{ + struct p2p_group_member *m; + + if (!group) + return NULL; + m = p2p_group_get_client(group, dev_addr); + if (m) + return m->addr; + return NULL; +} + + static struct p2p_group_member * p2p_group_get_client_iface( struct p2p_group *group, const u8 *interface_addr) { @@ -924,7 +941,8 @@ int p2p_group_go_discover(struct p2p_group *group, const u8 *dev_id, if (p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, m->addr, group->cfg->interface_addr, group->cfg->interface_addr, - wpabuf_head(req), wpabuf_len(req), 200) < 0) + wpabuf_head(req), wpabuf_len(req), 200, NULL) + < 0) { p2p_dbg(p2p, "Failed to send Action frame"); } @@ -1097,7 +1115,7 @@ int p2p_group_get_common_freqs(struct p2p_group *group, int *common_freqs, struct p2p_device *dev; dev = p2p_get_device(group->p2p, m->dev_addr); - if (!dev) + if (!dev || dev->channels.reg_classes == 0) continue; p2p_channels_intersect(&intersect, &dev->channels, &res); diff --git a/contrib/wpa/src/p2p/p2p_i.h b/contrib/wpa/src/p2p/p2p_i.h index 0ce4058..d2c55c9 100644 --- a/contrib/wpa/src/p2p/p2p_i.h +++ b/contrib/wpa/src/p2p/p2p_i.h @@ -308,6 +308,18 @@ struct p2p_data { */ int num_p2p_sd_queries; + /** + * sd_query_no_ack - The first peer (Dev Addr) that did not ACK SD Query + * + * This is used to track the first peer that did not ACK an SD Query + * within a single P2P Search iteration. All zeros address means no such + * peer was yet seen. This information is used to allow a new Listen and + * Search phases to be once every pending SD Query has been sent once to + * each peer instead of looping all pending attempts continuously until + * running out of retry maximums. + */ + u8 sd_query_no_ack[ETH_ALEN]; + /* GO Negotiation data */ /** @@ -425,9 +437,11 @@ struct p2p_data { int inv_persistent; enum p2p_discovery_type find_type; + int find_specified_freq; unsigned int last_p2p_find_timeout; u8 last_prog_scan_class; u8 last_prog_scan_chan; + unsigned int find_pending_full:1; int p2p_scan_running; enum p2p_after_scan { P2P_AFTER_SCAN_NOTHING, @@ -533,6 +547,7 @@ struct p2p_data { struct wpabuf *wfd_dev_info; struct wpabuf *wfd_assoc_bssid; struct wpabuf *wfd_coupled_sink_info; + struct wpabuf *wfd_r2_dev_info; #endif /* CONFIG_WIFI_DISPLAY */ u16 authorized_oob_dev_pw_id; @@ -541,6 +556,10 @@ struct p2p_data { unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS]; unsigned int num_pref_freq; + + /* Override option for preferred operating channel in GO Negotiation */ + u8 override_pref_op_class; + u8 override_pref_channel; }; /** @@ -688,9 +707,13 @@ void p2p_channels_dump(struct p2p_data *p2p, const char *title, 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); + u8 *op_channel, + struct wpa_freq_range_list *avoid_list, + struct wpa_freq_range_list *disallow_list); /* p2p_parse.c */ +void p2p_copy_filter_devname(char *dst, size_t dst_len, + const void *src, size_t src_len); 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); diff --git a/contrib/wpa/src/p2p/p2p_invitation.c b/contrib/wpa/src/p2p/p2p_invitation.c index 108e5b7..77d662a 100644 --- a/contrib/wpa/src/p2p/p2p_invitation.c +++ b/contrib/wpa/src/p2p/p2p_invitation.c @@ -284,7 +284,7 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa, if (!p2p_channels_includes(&intersection, reg_class, channel)) { - p2p_dbg(p2p, "forced freq %d MHz not in the supported channels interaction", + p2p_dbg(p2p, "forced freq %d MHz not in the supported channels intersection", op_freq); status = P2P_SC_FAIL_NO_COMMON_CHANNELS; goto fail; @@ -418,7 +418,7 @@ fail: p2p->pending_action_state = P2P_PENDING_INVITATION_RESPONSE; if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, p2p->cfg->dev_addr, - wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { + wpabuf_head(resp), wpabuf_len(resp), 50) < 0) { p2p_dbg(p2p, "Failed to send Action frame"); } @@ -488,7 +488,7 @@ void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa, 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->op_channel, NULL, NULL) == 0) { p2p->retry_invite_req = 0; p2p->cfg->send_action_done(p2p->cfg->cb_ctx); p2p->cfg->stop_listen(p2p->cfg->cb_ctx); diff --git a/contrib/wpa/src/p2p/p2p_parse.c b/contrib/wpa/src/p2p/p2p_parse.c index bd1e68b..5d2299c 100644 --- a/contrib/wpa/src/p2p/p2p_parse.c +++ b/contrib/wpa/src/p2p/p2p_parse.c @@ -15,11 +15,29 @@ #include "p2p_i.h" +void p2p_copy_filter_devname(char *dst, size_t dst_len, + const void *src, size_t src_len) +{ + size_t i; + + if (src_len >= dst_len) + src_len = dst_len - 1; + os_memcpy(dst, src, src_len); + dst[src_len] = '\0'; + for (i = 0; i < src_len; i++) { + if (dst[i] == '\0') + break; + if (is_ctrl_char(dst[i])) + dst[i] = '_'; + } +} + + static int p2p_parse_attribute(u8 id, const u8 *data, u16 len, struct p2p_message *msg) { const u8 *pos; - size_t i, nlen; + u16 nlen; char devtype[WPS_DEV_TYPE_BUFSIZE]; switch (id) { @@ -149,21 +167,14 @@ static int p2p_parse_attribute(u8 id, const u8 *data, u16 len, pos += 2; nlen = WPA_GET_BE16(pos); pos += 2; - if (data + len - pos < (int) nlen || - nlen > WPS_DEV_NAME_MAX_LEN) { + if (nlen > data + len - pos || nlen > WPS_DEV_NAME_MAX_LEN) { wpa_printf(MSG_DEBUG, "P2P: Invalid Device Name " - "length %d (buf len %d)", (int) nlen, + "length %u (buf len %d)", nlen, (int) (data + len - pos)); return -1; } - os_memcpy(msg->device_name, pos, nlen); - msg->device_name[nlen] = '\0'; - for (i = 0; i < nlen; i++) { - if (msg->device_name[i] == '\0') - break; - if (is_ctrl_char(msg->device_name[i])) - msg->device_name[i] = '_'; - } + p2p_copy_filter_devname(msg->device_name, + sizeof(msg->device_name), pos, nlen); wpa_printf(MSG_DEBUG, "P2P: * Device Info: addr " MACSTR " primary device type %s device name '%s' " "config methods 0x%x", @@ -637,49 +648,48 @@ int p2p_group_info_parse(const u8 *gi, size_t gi_len, gend = gi + gi_len; while (g < gend) { struct p2p_client_info *cli; - const u8 *t, *cend; - int count; + const u8 *cend; + u16 count; + u8 len; cli = &info->client[info->num_clients]; - cend = g + 1 + g[0]; - if (cend > gend) + len = *g++; + if (len > gend - g || len < 2 * ETH_ALEN + 1 + 2 + 8 + 1) return -1; /* invalid data */ + cend = g + len; /* g at start of P2P Client Info Descriptor */ - /* t at Device Capability Bitmap */ - t = g + 1 + 2 * ETH_ALEN; - if (t > cend) - return -1; /* invalid data */ - cli->p2p_device_addr = g + 1; - cli->p2p_interface_addr = g + 1 + ETH_ALEN; - cli->dev_capab = t[0]; - - if (t + 1 + 2 + 8 + 1 > cend) - return -1; /* invalid data */ - - cli->config_methods = WPA_GET_BE16(&t[1]); - cli->pri_dev_type = &t[3]; - - t += 1 + 2 + 8; - /* t at Number of Secondary Device Types */ - cli->num_sec_dev_types = *t++; - if (t + 8 * cli->num_sec_dev_types > cend) + cli->p2p_device_addr = g; + g += ETH_ALEN; + cli->p2p_interface_addr = g; + g += ETH_ALEN; + cli->dev_capab = *g++; + + cli->config_methods = WPA_GET_BE16(g); + g += 2; + cli->pri_dev_type = g; + g += 8; + + /* g at Number of Secondary Device Types */ + len = *g++; + if (8 * len > cend - g) return -1; /* invalid data */ - cli->sec_dev_types = t; - t += 8 * cli->num_sec_dev_types; + cli->num_sec_dev_types = len; + cli->sec_dev_types = g; + g += 8 * len; - /* t at Device Name in WPS TLV format */ - if (t + 2 + 2 > cend) + /* g at Device Name in WPS TLV format */ + if (cend - g < 2 + 2) return -1; /* invalid data */ - if (WPA_GET_BE16(t) != ATTR_DEV_NAME) + if (WPA_GET_BE16(g) != ATTR_DEV_NAME) return -1; /* invalid Device Name TLV */ - t += 2; - count = WPA_GET_BE16(t); - t += 2; - if (count > cend - t) + g += 2; + count = WPA_GET_BE16(g); + g += 2; + if (count > cend - g) return -1; /* invalid Device Name TLV */ if (count >= WPS_DEV_NAME_MAX_LEN) count = WPS_DEV_NAME_MAX_LEN; - cli->dev_name = (const char *) t; + cli->dev_name = (const char *) g; cli->dev_name_len = count; g = cend; diff --git a/contrib/wpa/src/p2p/p2p_pd.c b/contrib/wpa/src/p2p/p2p_pd.c index 8900945..3994ec0 100644 --- a/contrib/wpa/src/p2p/p2p_pd.c +++ b/contrib/wpa/src/p2p/p2p_pd.c @@ -40,21 +40,31 @@ 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) +static void p2ps_add_new_group_info(struct p2p_data *p2p, + struct p2p_device *dev, + struct wpabuf *buf) { int found; u8 intended_addr[ETH_ALEN]; u8 ssid[SSID_MAX_LEN]; size_t ssid_len; int group_iface; + unsigned int force_freq; if (!p2p->cfg->get_go_info) return; found = p2p->cfg->get_go_info( p2p->cfg->cb_ctx, intended_addr, ssid, - &ssid_len, &group_iface); + &ssid_len, &group_iface, &force_freq); if (found) { + if (force_freq > 0) { + p2p->p2ps_prov->force_freq = force_freq; + p2p->p2ps_prov->pref_freq = 0; + + if (dev) + p2p_prepare_channel(p2p, dev, force_freq, 0, 0); + } p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, ssid, ssid_len); @@ -92,62 +102,62 @@ static void p2ps_add_pd_req_attrs(struct p2p_data *p2p, struct p2p_device *dev, size_t ssid_len; u8 go_dev_addr[ETH_ALEN]; u8 intended_addr[ETH_ALEN]; + int follow_on_req_fail = prov->status >= 0 && + prov->status != P2P_SC_SUCCESS_DEFERRED; /* 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 (!follow_on_req_fail && + (prov->conncap & (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW))) + p2ps_add_new_group_info(p2p, dev, 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, intended_addr); - } - - /* Add Operating Channel if conncap includes GO */ - if (shared_group || - (prov->conncap & (P2PS_SETUP_GROUP_OWNER | - P2PS_SETUP_NEW))) { - u8 tmp; + if (!follow_on_req_fail) { + 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, + intended_addr); + } - p2p_go_select_channel(p2p, dev, &tmp); + if (shared_group || + (prov->conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_NEW))) + p2p_buf_add_channel_list(buf, p2p->cfg->country, + &p2p->channels); - if (p2p->op_reg_class && p2p->op_channel) + if ((shared_group && !is_zero_ether_addr(intended_addr)) || + (prov->conncap & (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW))) 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]) + if (prov->status < 0 && prov->info[0]) p2p_buf_add_session_info(buf, prov->info); - p2p_buf_add_connection_capability(buf, prov->conncap); + if (!follow_on_req_fail) + 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); - } + if (!follow_on_req_fail) { + 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_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); @@ -285,6 +295,11 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, u8 *len = p2p_buf_add_ie_hdr(buf); struct p2ps_provision *prov = p2p->p2ps_prov; u8 group_capab; + u8 conncap = 0; + + if (status == P2P_SC_SUCCESS || + status == P2P_SC_SUCCESS_DEFERRED) + conncap = prov->conncap; if (!status && prov->status != -1) status = prov->status; @@ -301,7 +316,7 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, group_capab); p2p_buf_add_device_info(buf, p2p, NULL); - if (persist_ssid && p2p->cfg->get_persistent_group && + if (persist_ssid && p2p->cfg->get_persistent_group && dev && (status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED)) { u8 ssid[SSID_MAX_LEN]; @@ -323,16 +338,11 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, } } - if (!persist && (prov->conncap & P2PS_SETUP_GROUP_OWNER)) - p2ps_add_new_group_info(p2p, buf); + if (!persist && (conncap & P2PS_SETUP_GROUP_OWNER)) + p2ps_add_new_group_info(p2p, dev, 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 (persist || (conncap & P2PS_SETUP_GROUP_OWNER)) { if (p2p->op_reg_class && p2p->op_channel) p2p_buf_add_operating_channel( buf, p2p->cfg->country, @@ -345,17 +355,20 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, p2p->cfg->op_channel); } - p2p_buf_add_channel_list(buf, p2p->cfg->country, - &p2p->cfg->channels); + if (persist || + (conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER))) + p2p_buf_add_channel_list(buf, p2p->cfg->country, + &p2p->channels); - if (!persist && (status == P2P_SC_SUCCESS || - status == P2P_SC_SUCCESS_DEFERRED)) - p2p_buf_add_connection_capability(buf, prov->conncap); + if (!persist && conncap) + p2p_buf_add_connection_capability(buf, conncap); p2p_buf_add_advertisement_id(buf, adv_id, prov->adv_mac); - p2p_buf_add_config_timeout(buf, p2p->go_timeout, - p2p->client_timeout); + if (persist || + (conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER))) + p2p_buf_add_config_timeout(buf, p2p->go_timeout, + p2p->client_timeout); p2p_buf_add_session_id(buf, prov->session_id, prov->session_mac); @@ -427,6 +440,117 @@ static u8 p2ps_own_preferred_cpt(const u8 *cpt_priority, u8 req_cpt_mask) } +/* Check if the message contains a valid P2PS PD Request */ +static int p2ps_validate_pd_req(struct p2p_data *p2p, struct p2p_message *msg, + const u8 *addr) +{ + u8 group_id = 0; + u8 intended_addr = 0; + u8 operating_channel = 0; + u8 channel_list = 0; + u8 config_timeout = 0; + u8 listen_channel = 0; + +#define P2PS_PD_REQ_CHECK(_val, _attr) \ +do { \ + if ((_val) && !msg->_attr) { \ + p2p_dbg(p2p, "Not P2PS PD Request. Missing %s", #_attr); \ + return -1; \ + } \ +} while (0) + + P2PS_PD_REQ_CHECK(1, adv_id); + P2PS_PD_REQ_CHECK(1, session_id); + P2PS_PD_REQ_CHECK(1, session_mac); + P2PS_PD_REQ_CHECK(1, adv_mac); + P2PS_PD_REQ_CHECK(1, capability); + P2PS_PD_REQ_CHECK(1, p2p_device_info); + P2PS_PD_REQ_CHECK(1, feature_cap); + + /* + * We don't need to check Connection Capability, Persistent Group, + * and related attributes for follow-on PD Request with a status + * other than SUCCESS_DEFERRED. + */ + if (msg->status && *msg->status != P2P_SC_SUCCESS_DEFERRED) + return 0; + + P2PS_PD_REQ_CHECK(1, conn_cap); + + /* + * Note 1: A feature capability attribute structure can be changed + * in the future. The assumption is that such modifications are + * backward compatible, therefore we allow processing of msg.feature_cap + * exceeding the size of the p2ps_feature_capab structure. + * Note 2: Verification of msg.feature_cap_len below has to be changed + * to allow 2 byte feature capability processing if + * struct p2ps_feature_capab is extended to include additional fields + * and it affects the structure size. + */ + if (msg->feature_cap_len < sizeof(struct p2ps_feature_capab)) { + p2p_dbg(p2p, "P2PS: Invalid feature capability len"); + return -1; + } + + switch (*msg->conn_cap) { + case P2PS_SETUP_NEW: + group_id = 1; + intended_addr = 1; + operating_channel = 1; + channel_list = 1; + config_timeout = 1; + listen_channel = 1; + break; + case P2PS_SETUP_CLIENT: + channel_list = 1; + listen_channel = 1; + break; + case P2PS_SETUP_GROUP_OWNER: + group_id = 1; + intended_addr = 1; + operating_channel = 1; + break; + case P2PS_SETUP_NEW | P2PS_SETUP_GROUP_OWNER: + group_id = 1; + operating_channel = 1; + intended_addr = 1; + channel_list = 1; + config_timeout = 1; + break; + case P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER: + group_id = 1; + intended_addr = 1; + operating_channel = 1; + channel_list = 1; + config_timeout = 1; + break; + default: + p2p_dbg(p2p, "Invalid P2PS PD connection capability"); + return -1; + } + + if (msg->persistent_dev) { + channel_list = 1; + config_timeout = 1; + if (os_memcmp(msg->persistent_dev, addr, ETH_ALEN) == 0) { + intended_addr = 1; + operating_channel = 1; + } + } + + P2PS_PD_REQ_CHECK(group_id, group_id); + P2PS_PD_REQ_CHECK(intended_addr, intended_addr); + P2PS_PD_REQ_CHECK(operating_channel, operating_channel); + P2PS_PD_REQ_CHECK(channel_list, channel_list); + P2PS_PD_REQ_CHECK(config_timeout, config_timeout); + P2PS_PD_REQ_CHECK(listen_channel, listen_channel); + +#undef P2PS_PD_REQ_CHECK + + return 0; +} + + void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, const u8 *data, size_t len, int rx_freq) { @@ -440,14 +564,16 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, u8 conncap = P2PS_SETUP_NEW; u8 auto_accept = 0; u32 session_id = 0; - u8 session_mac[ETH_ALEN]; - u8 adv_mac[ETH_ALEN]; + u8 session_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; + u8 adv_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; const u8 *group_mac; int passwd_id = DEV_PW_DEFAULT; u16 config_methods; u16 allowed_config_methods = WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD; struct p2ps_feature_capab resp_fcap = { 0, 0 }; - struct p2ps_feature_capab *req_fcap; + struct p2ps_feature_capab *req_fcap = NULL; + u8 remote_conncap; + u16 method; if (p2p_parse(data, len, &msg)) return; @@ -466,300 +592,431 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, 0)) { p2p_dbg(p2p, "Provision Discovery Request add device failed " MACSTR, MAC2STR(sa)); + goto out; + } + + if (!dev) { + dev = p2p_get_device(p2p, sa); + if (!dev) { + p2p_dbg(p2p, + "Provision Discovery device not found " + MACSTR, MAC2STR(sa)); + goto out; + } } } else if (msg.wfd_subelems) { wpabuf_free(dev->info.wfd_subelems); dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); } - if (msg.adv_id) - allowed_config_methods |= WPS_CONFIG_P2PS; - else + if (!msg.adv_id) { allowed_config_methods |= WPS_CONFIG_PUSHBUTTON; + if (!(msg.wps_config_methods & allowed_config_methods)) { + p2p_dbg(p2p, + "Unsupported Config Methods in Provision Discovery Request"); + goto out; + } - if (!(msg.wps_config_methods & allowed_config_methods)) { - p2p_dbg(p2p, "Unsupported Config Methods in Provision Discovery Request"); - goto out; - } + /* Legacy (non-P2PS) - Unknown groups allowed for P2PS */ + if (msg.group_id) { + size_t i; - /* 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], - msg.group_id, - msg.group_id_len)) - break; + for (i = 0; i < p2p->num_groups; i++) { + if (p2p_group_is_group_id_match( + p2p->groups[i], + msg.group_id, msg.group_id_len)) + break; + } + if (i == p2p->num_groups) { + p2p_dbg(p2p, + "PD request for unknown P2P Group ID - reject"); + goto out; + } } - if (i == p2p->num_groups) { - p2p_dbg(p2p, "PD request for unknown P2P Group ID - reject"); + } else { + allowed_config_methods |= WPS_CONFIG_P2PS; + + /* + * Set adv_id here, so in case of an error, a P2PS PD Response + * will be sent. + */ + adv_id = WPA_GET_LE32(msg.adv_id); + if (p2ps_validate_pd_req(p2p, &msg, sa) < 0) { + reject = P2P_SC_FAIL_INVALID_PARAMS; goto out; } - } - if (dev) { - dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY | - P2P_DEV_PD_PEER_KEYPAD | - P2P_DEV_PD_PEER_P2PS); + req_fcap = (struct p2ps_feature_capab *) msg.feature_cap; - /* 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); + 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); + + if (msg.conn_cap) + conncap = *msg.conn_cap; + + /* + * We need to verify a P2PS config methog in an initial PD + * request or in a follow-on PD request with the status + * SUCCESS_DEFERRED. + */ + if ((!msg.status || *msg.status == P2P_SC_SUCCESS_DEFERRED) && + !(msg.wps_config_methods & allowed_config_methods)) { + p2p_dbg(p2p, + "Unsupported Config Methods in Provision Discovery Request"); + goto out; } + + /* + * TODO: since we don't support multiple PD, reject PD request + * if we are in the middle of P2PS PD with some other peer + */ } + + dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY | + P2P_DEV_PD_PEER_KEYPAD | + P2P_DEV_PD_PEER_P2PS); + if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) { p2p_dbg(p2p, "Peer " MACSTR " requested us to show a PIN on display", MAC2STR(sa)); - if (dev) - dev->flags |= P2P_DEV_PD_PEER_KEYPAD; + dev->flags |= P2P_DEV_PD_PEER_KEYPAD; passwd_id = DEV_PW_USER_SPECIFIED; } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) { p2p_dbg(p2p, "Peer " MACSTR " requested us to write its PIN using keypad", MAC2STR(sa)); - if (dev) - dev->flags |= P2P_DEV_PD_PEER_DISPLAY; + 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; + dev->flags |= P2P_DEV_PD_PEER_P2PS; passwd_id = DEV_PW_P2PS_DEFAULT; } - reject = P2P_SC_SUCCESS; + /* 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); + } - os_memset(session_mac, 0, ETH_ALEN); - os_memset(adv_mac, 0, ETH_ALEN); + reject = P2P_SC_SUCCESS; - /* Note 1: A feature capability attribute structure can be changed - * in the future. The assumption is that such modifications are - * backwards compatible, therefore we allow processing of - * msg.feature_cap exceeding the size of the p2ps_feature_capab - * structure. - * Note 2: Vverification of msg.feature_cap_len below has to be changed - * to allow 2 byte feature capability processing if struct - * p2ps_feature_capab is extended to include additional fields and it - * affects the structure size. + /* + * End of a legacy P2P PD Request processing, from this point continue + * with P2PS one. */ - if (msg.adv_id && msg.session_id && msg.session_mac && msg.adv_mac && - msg.feature_cap && msg.feature_cap_len >= sizeof(*req_fcap) && - (msg.status || msg.conn_cap)) { - u8 remote_conncap; + if (!msg.adv_id) + goto out; - req_fcap = (struct p2ps_feature_capab *) msg.feature_cap; + remote_conncap = conncap; - os_memcpy(session_mac, msg.session_mac, ETH_ALEN); - os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN); + if (!msg.status) { + unsigned int forced_freq, pref_freq; - 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); + if (os_memcmp(p2p->cfg->dev_addr, msg.adv_mac, ETH_ALEN)) { + p2p_dbg(p2p, + "P2PS PD adv mac does not match the local one"); + reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + goto out; + } - p2p_dbg(p2p, "adv_id: %x - p2ps_adv - %p", adv_id, p2ps_adv); + p2ps_adv = p2p_service_p2ps_id(p2p, adv_id); + if (!p2ps_adv) { + p2p_dbg(p2p, "P2PS PD invalid adv_id=0x%X", adv_id); + reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + goto out; + } + p2p_dbg(p2p, "adv_id: 0x%X, p2ps_adv: %p", adv_id, p2ps_adv); - if (msg.conn_cap) - conncap = *msg.conn_cap; - remote_conncap = conncap; + auto_accept = p2ps_adv->auto_accept; + conncap = p2p->cfg->p2ps_group_capability(p2p->cfg->cb_ctx, + conncap, auto_accept, + &forced_freq, + &pref_freq); - 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); - p2p_dbg(p2p, "Conncap: local:%d remote:%d result:%d", - auto_accept, remote_conncap, conncap); + p2p_prepare_channel(p2p, dev, forced_freq, pref_freq, 0); - resp_fcap.cpt = - p2ps_own_preferred_cpt(p2ps_adv->cpt_priority, + resp_fcap.cpt = p2ps_own_preferred_cpt(p2ps_adv->cpt_priority, req_fcap->cpt); + p2p_dbg(p2p, "cpt: service:0x%x remote:0x%x result:0x%x", + p2ps_adv->cpt_mask, req_fcap->cpt, resp_fcap.cpt); + + if (!resp_fcap.cpt) { + p2p_dbg(p2p, + "Incompatible P2PS feature capability CPT bitmask"); + reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + } else if (p2ps_adv->config_methods && + !(msg.wps_config_methods & + p2ps_adv->config_methods)) { p2p_dbg(p2p, - "cpt: service:0x%x remote:0x%x result:0x%x", - p2ps_adv->cpt_mask, req_fcap->cpt, - resp_fcap.cpt); + "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 (!resp_fcap.cpt) { - p2p_dbg(p2p, - "Incompatible P2PS feature capability CPT bitmask"); - reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; - } else 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 (msg.wps_config_methods & WPS_CONFIG_KEYPAD) { - p2p_dbg(p2p, "Keypad - always defer"); - auto_accept = 0; + if ((remote_conncap & (P2PS_SETUP_NEW | P2PS_SETUP_CLIENT) || + msg.persistent_dev) && conncap != P2PS_SETUP_NEW && + msg.channel_list && msg.channel_list_len && + p2p_peer_channels_check(p2p, &p2p->channels, dev, + msg.channel_list, + msg.channel_list_len) < 0) { + p2p_dbg(p2p, + "No common channels - force deferred flow"); + auto_accept = 0; + } + + if (((remote_conncap & P2PS_SETUP_GROUP_OWNER) || + msg.persistent_dev) && msg.operating_channel) { + struct p2p_channels intersect; + + /* + * There are cases where only the operating channel is + * provided. This requires saving the channel as the + * supported channel list, and verifying that it is + * supported. + */ + if (dev->channels.reg_classes == 0 || + !p2p_channels_includes(&dev->channels, + msg.operating_channel[3], + msg.operating_channel[4])) { + struct p2p_channels *ch = &dev->channels; + + os_memset(ch, 0, sizeof(*ch)); + ch->reg_class[0].reg_class = + msg.operating_channel[3]; + ch->reg_class[0].channel[0] = + msg.operating_channel[4]; + ch->reg_class[0].channels = 1; + ch->reg_classes = 1; } - 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; + p2p_channels_intersect(&p2p->channels, &dev->channels, + &intersect); + + if (intersect.reg_classes == 0) { + p2p_dbg(p2p, + "No common channels - force deferred flow"); + auto_accept = 0; } - } 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)) { + if (auto_accept || reject != P2P_SC_SUCCESS) { 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; + tmp->force_freq = forced_freq; + tmp->pref_freq = pref_freq; + 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; } + } - 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; + if (!msg.status && !auto_accept && + (!p2p->p2ps_prov || p2p->p2ps_prov->adv_id != adv_id)) { + struct p2ps_provision *tmp; - conncap = p2p->cfg->p2ps_group_capability( - p2p->cfg->cb_ctx, remote_conncap, - p2p->p2ps_prov->conncap); + if (!conncap) { + reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + goto out; + } - p2p_dbg(p2p, - "Conncap: local:%d remote:%d result:%d", - p2p->p2ps_prov->conncap, - remote_conncap, conncap); + 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; + } - resp_fcap.cpt = p2ps_own_preferred_cpt( - p2p->p2ps_prov->cpt_priority, - req_fcap->cpt); + /* Not a P2PS Follow-on PD */ + if (!msg.status) + goto out; - p2p_dbg(p2p, - "cpt: local:0x%x remote:0x%x result:0x%x", - p2p->p2ps_prov->cpt_mask, - req_fcap->cpt, resp_fcap.cpt); + if (*msg.status && *msg.status != P2P_SC_SUCCESS_DEFERRED) { + reject = *msg.status; + goto out; + } - /* - * 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; - - if (!conncap || - !(msg.wps_config_methods & method)) { - /* - * Reject this "Deferred Accept* - * if incompatible conncap or method - */ - reject = - P2P_SC_FAIL_INCOMPATIBLE_PARAMS; - } else if (!resp_fcap.cpt) { - p2p_dbg(p2p, - "Incompatible P2PS feature capability CPT bitmask"); - reject = - P2P_SC_FAIL_INCOMPATIBLE_PARAMS; - } else { - reject = P2P_SC_SUCCESS; - } - - p2p->p2ps_prov->status = reject; - p2p->p2ps_prov->conncap = conncap; - } - } + if (*msg.status != P2P_SC_SUCCESS_DEFERRED || !p2p->p2ps_prov) + goto out; + + if (p2p->p2ps_prov->adv_id != adv_id || + os_memcmp(p2p->p2ps_prov->adv_mac, msg.adv_mac, ETH_ALEN)) { + p2p_dbg(p2p, + "P2PS Follow-on PD with mismatch Advertisement ID/MAC"); + goto out; + } + + if (p2p->p2ps_prov->session_id != session_id || + os_memcmp(p2p->p2ps_prov->session_mac, msg.session_mac, ETH_ALEN)) { + p2p_dbg(p2p, "P2PS Follow-on PD with mismatch Session ID/MAC"); + goto out; + } + + method = p2p->p2ps_prov->method; + + conncap = p2p->cfg->p2ps_group_capability(p2p->cfg->cb_ctx, + remote_conncap, + p2p->p2ps_prov->conncap, + &p2p->p2ps_prov->force_freq, + &p2p->p2ps_prov->pref_freq); + + resp_fcap.cpt = p2ps_own_preferred_cpt(p2p->p2ps_prov->cpt_priority, + req_fcap->cpt); + + p2p_dbg(p2p, "cpt: local:0x%x remote:0x%x result:0x%x", + p2p->p2ps_prov->cpt_mask, req_fcap->cpt, resp_fcap.cpt); + + p2p_prepare_channel(p2p, dev, p2p->p2ps_prov->force_freq, + p2p->p2ps_prov->pref_freq, 0); + + /* + * 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; + + if (!conncap || !(msg.wps_config_methods & method)) { + /* + * Reject this "Deferred Accept* + * if incompatible conncap or method + */ + reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + } else if (!resp_fcap.cpt) { + p2p_dbg(p2p, + "Incompatible P2PS feature capability CPT bitmask"); + reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; + } else if ((remote_conncap & (P2PS_SETUP_NEW | P2PS_SETUP_CLIENT) || + msg.persistent_dev) && conncap != P2PS_SETUP_NEW && + msg.channel_list && msg.channel_list_len && + p2p_peer_channels_check(p2p, &p2p->channels, dev, + msg.channel_list, + msg.channel_list_len) < 0) { + p2p_dbg(p2p, + "No common channels in Follow-On Provision Discovery Request"); + reject = P2P_SC_FAIL_NO_COMMON_CHANNELS; + } else { + reject = P2P_SC_SUCCESS; + } + + dev->oper_freq = 0; + if (reject == P2P_SC_SUCCESS || reject == P2P_SC_SUCCESS_DEFERRED) { + u8 tmp; + + if (msg.operating_channel) + dev->oper_freq = + p2p_channel_to_freq(msg.operating_channel[3], + msg.operating_channel[4]); + + if ((conncap & P2PS_SETUP_GROUP_OWNER) && + p2p_go_select_channel(p2p, dev, &tmp) < 0) + reject = P2P_SC_FAIL_NO_COMMON_CHANNELS; } + p2p->p2ps_prov->status = reject; + p2p->p2ps_prov->conncap = conncap; + out: 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, - (const u8 *) &resp_fcap, - sizeof(resp_fcap)); - if (resp == NULL) { - p2p_parse_free(&msg); - return; - } - p2p_dbg(p2p, "Sending Provision Discovery Response"); - if (rx_freq > 0) - freq = rx_freq; - else - freq = p2p_channel_to_freq(p2p->cfg->reg_class, - p2p->cfg->channel); - if (freq < 0) { - p2p_dbg(p2p, "Unknown regulatory class/channel"); + + /* + * Send PD Response for an initial PD Request or for follow-on + * PD Request with P2P_SC_SUCCESS_DEFERRED status. + */ + if (!msg.status || *msg.status == P2P_SC_SUCCESS_DEFERRED) { + 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, + (const u8 *) &resp_fcap, + sizeof(resp_fcap)); + if (!resp) { + p2p_parse_free(&msg); + return; + } + p2p_dbg(p2p, "Sending Provision Discovery Response"); + if (rx_freq > 0) + freq = rx_freq; + else + freq = p2p_channel_to_freq(p2p->cfg->reg_class, + p2p->cfg->channel); + if (freq < 0) { + p2p_dbg(p2p, "Unknown regulatory class/channel"); + wpabuf_free(resp); + p2p_parse_free(&msg); + return; + } + 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), + 50) < 0) + p2p_dbg(p2p, "Failed to send Action frame"); + else + p2p->send_action_in_progress = 1; + wpabuf_free(resp); + } + + if (!dev) { p2p_parse_free(&msg); return; } - 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) { - p2p_dbg(p2p, "Failed to send Action frame"); - } else - p2p->send_action_in_progress = 1; - wpabuf_free(resp); + freq = 0; + if (reject == P2P_SC_SUCCESS && conncap == P2PS_SETUP_GROUP_OWNER) { + freq = p2p_channel_to_freq(p2p->op_reg_class, + p2p->op_channel); + if (freq < 0) + freq = 0; + } if (!p2p->cfg->p2ps_prov_complete) { /* Don't emit anything */ @@ -771,7 +1028,8 @@ out: NULL, adv_id, session_id, 0, 0, msg.persistent_ssid, msg.persistent_ssid_len, - 0, 0, NULL, NULL, 0); + 0, 0, NULL, NULL, 0, freq, + NULL, 0); } else if (msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) { p2p->p2ps_prov->status = reject; @@ -784,7 +1042,8 @@ out: session_id, conncap, 0, msg.persistent_ssid, msg.persistent_ssid_len, 0, - 0, NULL, NULL, 0); + 0, NULL, NULL, 0, freq, + NULL, 0); else p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, *msg.status, @@ -796,7 +1055,8 @@ out: msg.persistent_ssid_len, 0, 0, NULL, (const u8 *) &resp_fcap, - sizeof(resp_fcap)); + sizeof(resp_fcap), freq, + NULL, 0); } 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, @@ -807,7 +1067,7 @@ out: msg.persistent_ssid_len, 0, 0, NULL, (const u8 *) &resp_fcap, - sizeof(resp_fcap)); + sizeof(resp_fcap), freq, NULL, 0); } else if (msg.status) { } else if (auto_accept && reject == P2P_SC_SUCCESS) { p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS, @@ -818,7 +1078,11 @@ out: msg.persistent_ssid_len, 0, 0, NULL, (const u8 *) &resp_fcap, - sizeof(resp_fcap)); + sizeof(resp_fcap), freq, + msg.group_id ? + msg.group_id + ETH_ALEN : NULL, + msg.group_id ? + msg.group_id_len - ETH_ALEN : 0); } else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && (!msg.session_info || !msg.session_info_len)) { p2p->p2ps_prov->method = msg.wps_config_methods; @@ -831,7 +1095,7 @@ out: msg.persistent_ssid_len, 0, 1, NULL, (const u8 *) &resp_fcap, - sizeof(resp_fcap)); + sizeof(resp_fcap), freq, NULL, 0); } 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); @@ -848,7 +1112,8 @@ out: session_id, conncap, passwd_id, msg.persistent_ssid, msg.persistent_ssid_len, 0, 1, buf, - (const u8 *) &resp_fcap, sizeof(resp_fcap)); + (const u8 *) &resp_fcap, sizeof(resp_fcap), + freq, NULL, 0); os_free(buf); } @@ -898,7 +1163,10 @@ out: msg.group_id, msg.group_id_len); } - if (dev && reject == P2P_SC_SUCCESS) { + if (reject != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) + p2ps_prov_free(p2p); + + if (reject == P2P_SC_SUCCESS) { switch (config_methods) { case WPS_CONFIG_DISPLAY: dev->wps_prov_info = WPS_CONFIG_KEYPAD; @@ -1084,6 +1352,9 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, " with no pending request", MAC2STR(sa)); p2p_parse_free(&msg); return; + } else if (msg.wfd_subelems) { + wpabuf_free(dev->info.wfd_subelems); + dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); } if (dev->dialog_token != msg.dialog_token) { @@ -1148,17 +1419,71 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, passwd_id = DEV_PW_P2PS_DEFAULT; } - if ((msg.conn_cap || msg.persistent_dev) && - (status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED) && + if ((status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED) && p2p->p2ps_prov) { + dev->oper_freq = 0; + + /* + * Save the reported channel list and operating frequency. + * Note that the specification mandates that the responder + * should include in the channel list only channels reported by + * the initiator, so this is only a sanity check, and if this + * fails the flow would continue, although it would probably + * fail. Same is true for the operating channel. + */ + if (msg.channel_list && msg.channel_list_len && + p2p_peer_channels_check(p2p, &p2p->channels, dev, + msg.channel_list, + msg.channel_list_len) < 0) + p2p_dbg(p2p, "P2PS PD Response - no common channels"); + + if (msg.operating_channel) { + if (p2p_channels_includes(&p2p->channels, + msg.operating_channel[3], + msg.operating_channel[4]) && + p2p_channels_includes(&dev->channels, + msg.operating_channel[3], + msg.operating_channel[4])) { + dev->oper_freq = + p2p_channel_to_freq( + msg.operating_channel[3], + msg.operating_channel[4]); + } else { + p2p_dbg(p2p, + "P2PS PD Response - invalid operating channel"); + } + } + if (p2p->cfg->p2ps_prov_complete) { + int freq = 0; + + if (conncap == P2PS_SETUP_GROUP_OWNER) { + u8 tmp; + + /* + * Re-select the operating channel as it is + * possible that original channel is no longer + * valid. This should not really fail. + */ + if (p2p_go_select_channel(p2p, dev, &tmp) < 0) + p2p_dbg(p2p, + "P2PS PD channel selection failed"); + + freq = p2p_channel_to_freq(p2p->op_reg_class, + p2p->op_channel); + if (freq < 0) + freq = 0; + } + 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, - msg.feature_cap, msg.feature_cap_len); + msg.feature_cap, msg.feature_cap_len, freq, + msg.group_id ? msg.group_id + ETH_ALEN : NULL, + msg.group_id ? msg.group_id_len - ETH_ALEN : 0); } p2ps_prov_free(p2p); } else if (status != P2P_SC_SUCCESS && @@ -1169,7 +1494,7 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, 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, NULL, 0); + 0, 0, NULL, 0, 1, 0, NULL, NULL, 0, 0, NULL, 0); p2ps_prov_free(p2p); } @@ -1259,7 +1584,7 @@ out: report_config_methods); if (p2p->state == P2P_PD_DURING_FIND) { - p2p_clear_timeout(p2p); + p2p_stop_listen_for_freq(p2p, 0); p2p_continue_find(p2p); } } @@ -1318,6 +1643,10 @@ int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, "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); + + if (p2p_prepare_channel(p2p, dev, p2p->p2ps_prov->force_freq, + p2p->p2ps_prov->pref_freq, 1) < 0) + return -1; } req = p2p_build_prov_disc_req(p2p, dev, join); diff --git a/contrib/wpa/src/p2p/p2p_sd.c b/contrib/wpa/src/p2p/p2p_sd.c index 1a2af04..b9e753f 100644 --- a/contrib/wpa/src/p2p/p2p_sd.c +++ b/contrib/wpa/src/p2p/p2p_sd.c @@ -28,11 +28,11 @@ static int wfd_wsd_supported(struct wpabuf *wfd) pos = wpabuf_head(wfd); end = pos + wpabuf_len(wfd); - while (pos + 3 <= end) { + while (end - pos >= 3) { subelem = *pos++; len = WPA_GET_BE16(pos); pos += 2; - if (pos + len > end) + if (len > end - pos) break; if (subelem == WFD_SUBELEM_DEVICE_INFO && len >= 6) { @@ -288,6 +288,14 @@ int p2p_start_sd(struct p2p_data *p2p, struct p2p_device *dev) query = p2p_pending_sd_req(p2p, dev); if (query == NULL) return -1; + if (p2p->state == P2P_SEARCH && + os_memcmp(p2p->sd_query_no_ack, dev->info.p2p_device_addr, + ETH_ALEN) == 0) { + p2p_dbg(p2p, "Do not start Service Discovery with " MACSTR + " due to it being the first no-ACK peer in this search iteration", + MAC2STR(dev->info.p2p_device_addr)); + return -2; + } p2p_dbg(p2p, "Start Service Discovery with " MACSTR, MAC2STR(dev->info.p2p_device_addr)); @@ -355,11 +363,11 @@ void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa, pos++; slen = *pos++; - next = pos + slen; - if (next > end || slen < 2) { + if (slen > end - pos || slen < 2) { p2p_dbg(p2p, "Invalid IE in GAS Initial Request"); return; } + next = pos + slen; pos++; /* skip QueryRespLenLimit and PAME-BI */ if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { @@ -370,16 +378,16 @@ void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa, pos = next; /* Query Request */ - if (pos + 2 > end) + if (end - pos < 2) return; slen = WPA_GET_LE16(pos); pos += 2; - if (pos + slen > end) + if (slen > end - pos) return; end = pos + slen; /* ANQP Query Request */ - if (pos + 4 > end) + if (end - pos < 4) return; if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) { p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); @@ -389,7 +397,7 @@ 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) { + if (slen > end - pos || slen < 3 + 1) { p2p_dbg(p2p, "Invalid ANQP Query Request length"); return; } @@ -401,7 +409,7 @@ void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa, } pos += 4; - if (pos + 2 > end) + if (end - pos < 2) return; update_indic = WPA_GET_LE16(pos); p2p_dbg(p2p, "Service Update Indicator: %u", update_indic); @@ -417,9 +425,17 @@ void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst, u8 dialog_token, const struct wpabuf *resp_tlvs) { struct wpabuf *resp; + size_t max_len; + unsigned int wait_time = 200; + + /* + * In the 60 GHz, we have a smaller maximum frame length for management + * frames. + */ + max_len = (freq > 56160) ? 928 : 1400; /* TODO: fix the length limit to match with the maximum frame length */ - if (wpabuf_len(resp_tlvs) > 1400) { + if (wpabuf_len(resp_tlvs) > max_len) { p2p_dbg(p2p, "SD response long enough to require fragmentation"); if (p2p->sd_resp) { /* @@ -445,6 +461,7 @@ void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst, 1, p2p->srv_update_indic, NULL); } else { p2p_dbg(p2p, "SD response fits in initial response"); + wait_time = 0; /* no more SD frames in the sequence */ resp = p2p_build_sd_response(dialog_token, WLAN_STATUS_SUCCESS, 0, p2p->srv_update_indic, resp_tlvs); @@ -455,7 +472,7 @@ void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst, p2p->pending_action_state = P2P_NO_PENDING_ACTION; if (p2p_send_action(p2p, freq, dst, p2p->cfg->dev_addr, p2p->cfg->dev_addr, - wpabuf_head(resp), wpabuf_len(resp), 200) < 0) + wpabuf_head(resp), wpabuf_len(resp), wait_time) < 0) p2p_dbg(p2p, "Failed to send Action frame"); wpabuf_free(resp); @@ -512,11 +529,11 @@ void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, pos++; slen = *pos++; - next = pos + slen; - if (next > end || slen < 2) { + if (slen > end - pos || slen < 2) { p2p_dbg(p2p, "Invalid IE in GAS Initial Response"); return; } + next = pos + slen; pos++; /* skip QueryRespLenLimit and PAME-BI */ if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { @@ -527,14 +544,14 @@ void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, pos = next; /* Query Response */ - if (pos + 2 > end) { + if (end - pos < 2) { p2p_dbg(p2p, "Too short Query Response"); return; } slen = WPA_GET_LE16(pos); pos += 2; p2p_dbg(p2p, "Query Response Length: %d", slen); - if (pos + slen > end) { + if (slen > end - pos) { p2p_dbg(p2p, "Not enough Query Response data"); return; } @@ -552,7 +569,7 @@ void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, } /* ANQP Query Response */ - if (pos + 4 > end) + if (end - pos < 4) return; if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) { p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); @@ -562,7 +579,7 @@ 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) { + if (slen > end - pos || slen < 3 + 1) { p2p_dbg(p2p, "Invalid ANQP Query Response length"); return; } @@ -574,7 +591,7 @@ void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa, } pos += 4; - if (pos + 2 > end) + if (end - pos < 2) return; update_indic = WPA_GET_LE16(pos); p2p_dbg(p2p, "Service Update Indicator: %u", update_indic); @@ -606,8 +623,9 @@ void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa, { struct wpabuf *resp; u8 dialog_token; - size_t frag_len; + size_t frag_len, max_len; int more = 0; + unsigned int wait_time = 200; wpa_hexdump(MSG_DEBUG, "P2P: RX GAS Comeback Request", data, len); if (len < 1) @@ -630,9 +648,14 @@ void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa, return; } + /* + * In the 60 GHz, we have a smaller maximum frame length for management + * frames. + */ + max_len = (rx_freq > 56160) ? 928 : 1400; frag_len = wpabuf_len(p2p->sd_resp) - p2p->sd_resp_pos; - if (frag_len > 1400) { - frag_len = 1400; + if (frag_len > max_len) { + frag_len = max_len; more = 1; } resp = p2p_build_gas_comeback_resp(dialog_token, WLAN_STATUS_SUCCESS, @@ -655,12 +678,13 @@ void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa, p2p_dbg(p2p, "All fragments of SD response sent"); wpabuf_free(p2p->sd_resp); p2p->sd_resp = NULL; + wait_time = 0; /* no more SD frames in the sequence */ } p2p->pending_action_state = P2P_NO_PENDING_ACTION; if (p2p_send_action(p2p, rx_freq, sa, p2p->cfg->dev_addr, p2p->cfg->dev_addr, - wpabuf_head(resp), wpabuf_len(resp), 200) < 0) + wpabuf_head(resp), wpabuf_len(resp), wait_time) < 0) p2p_dbg(p2p, "Failed to send Action frame"); wpabuf_free(resp); @@ -727,11 +751,11 @@ void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, pos++; slen = *pos++; - next = pos + slen; - if (next > end || slen < 2) { + if (slen > end - pos || slen < 2) { p2p_dbg(p2p, "Invalid IE in GAS Comeback Response"); return; } + next = pos + slen; pos++; /* skip QueryRespLenLimit and PAME-BI */ if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { @@ -742,14 +766,14 @@ void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, pos = next; /* Query Response */ - if (pos + 2 > end) { + if (end - pos < 2) { p2p_dbg(p2p, "Too short Query Response"); return; } slen = WPA_GET_LE16(pos); pos += 2; p2p_dbg(p2p, "Query Response Length: %d", slen); - if (pos + slen > end) { + if (slen > end - pos) { p2p_dbg(p2p, "Not enough Query Response data"); return; } @@ -768,7 +792,7 @@ void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, } /* ANQP Query Response */ - if (pos + 4 > end) + if (end - pos < 4) return; if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) { p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos)); @@ -783,7 +807,7 @@ void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, p2p_dbg(p2p, "Invalid ANQP Query Response length"); return; } - if (pos + 4 > end) + if (end - pos < 4) return; if (WPA_GET_BE32(pos) != P2P_IE_VENDOR_TYPE) { @@ -793,7 +817,7 @@ void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa, } pos += 4; - if (pos + 2 > end) + if (end - pos < 2) return; p2p->sd_rx_update_indic = WPA_GET_LE16(pos); p2p_dbg(p2p, "Service Update Indicator: %u", p2p->sd_rx_update_indic); diff --git a/contrib/wpa/src/p2p/p2p_utils.c b/contrib/wpa/src/p2p/p2p_utils.c index 2e2aa8a..1a62a44 100644 --- a/contrib/wpa/src/p2p/p2p_utils.c +++ b/contrib/wpa/src/p2p/p2p_utils.c @@ -413,17 +413,30 @@ int p2p_channel_select(struct p2p_channels *chans, const int *classes, int p2p_channel_random_social(struct p2p_channels *chans, u8 *op_class, - u8 *op_channel) + u8 *op_channel, + struct wpa_freq_range_list *avoid_list, + struct wpa_freq_range_list *disallow_list) { 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)) + /* Try to find available social channels from 2.4 GHz. + * If the avoid_list includes any of the 2.4 GHz social channels, that + * channel is not allowed by p2p_channels_includes() rules. However, it + * is assumed to allow minimal traffic for P2P negotiation, so allow it + * here for social channel selection unless explicitly disallowed in the + * disallow_list. */ + if (p2p_channels_includes(chans, 81, 1) || + (freq_range_list_includes(avoid_list, 2412) && + !freq_range_list_includes(disallow_list, 2412))) chan[num_channels++] = 1; - if (p2p_channels_includes(chans, 81, 6)) + if (p2p_channels_includes(chans, 81, 6) || + (freq_range_list_includes(avoid_list, 2437) && + !freq_range_list_includes(disallow_list, 2437))) chan[num_channels++] = 6; - if (p2p_channels_includes(chans, 81, 11)) + if (p2p_channels_includes(chans, 81, 11) || + (freq_range_list_includes(avoid_list, 2462) && + !freq_range_list_includes(disallow_list, 2462))) chan[num_channels++] = 11; /* Try to find available social channels from 60 GHz */ |