diff --git a/src/p2p.c b/src/p2p.c index e41cc4f7..0e57b375 100644 --- a/src/p2p.c +++ b/src/p2p.c @@ -86,11 +86,13 @@ struct p2p_device { char *conn_pin; uint8_t conn_addr[6]; uint16_t conn_password_id; + struct scan_bss *conn_wsc_bss; struct l_timeout *config_timeout; unsigned long go_config_delay; struct l_timeout *go_neg_req_timeout; uint8_t go_dialog_token; + unsigned int go_scan_retry; uint32_t go_oper_freq; struct p2p_group_id_attr go_group_id; uint8_t go_interface_addr[6]; @@ -329,6 +331,10 @@ static void p2p_connect_failed(struct p2p_device *dev) if (!peer) return; + /* Are we in the scan for the WSC provision bss */ + if (dev->scan_id) + scan_cancel(dev->wdev_id, dev->scan_id); + if (peer->wsc.pending_connect) dbus_pending_reply(&peer->wsc.pending_connect, dbus_error_failed(peer->wsc.pending_connect)); @@ -420,6 +426,11 @@ static const struct frame_xchg_prefix p2p_frame_pd_resp = { .len = 7, }; +static void p2p_device_interface_create(struct p2p_device *dev) +{ + /* TODO */ +} + static void p2p_scan_destroy(void *user_data) { struct p2p_device *dev = user_data; @@ -427,6 +438,215 @@ static void p2p_scan_destroy(void *user_data) dev->scan_id = 0; } +static void p2p_provision_scan_start(struct p2p_device *dev); + +static bool p2p_provision_scan_notify(int err, struct l_queue *bss_list, + void *user_data) +{ + struct p2p_device *dev = user_data; + const struct l_queue_entry *entry; + static const uint8_t wildcard_addr[6] = + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + l_debug("err=%i, len(bss_list)=%i", err, l_queue_length(bss_list)); + + if (err) { + l_error("P2P provision scan failed: %s (%i)", strerror(-err), + -err); + p2p_connect_failed(dev); + return false; + } + + for (entry = l_queue_get_entries(bss_list); entry; + entry = entry->next) { + struct scan_bss *bss = entry->data; + const uint8_t *group_id; + bool selected_reg; + struct p2p_capability_attr *capability; + enum wsc_device_password_id device_password_id; + const uint8_t *amacs; + + /* + * Check if we found our target GO, some of these checks may + * need to be gradually relaxed as we discover non-compliant + * implementations but at least print a debug statement when + * something doesn't match. + */ + + if (strncmp((const char *) bss->ssid, dev->go_group_id.ssid, + bss->ssid_len)) + continue; + + if (dev->go_group_id.ssid[bss->ssid_len] != '\0') + continue; + + if (!util_mem_is_zero(dev->go_interface_addr, 6) && + memcmp(bss->addr, dev->go_interface_addr, 6)) + l_debug("SSID matched but BSSID didn't match the GO's " + "intended interface addr, proceeding anyway"); + + if (!bss->wsc) { + l_error("SSID matched but no valid WSC IE"); + continue; + } + + if (bss->source_frame == SCAN_BSS_PROBE_RESP) { + struct wsc_probe_response wsc_info; + + if (!bss->p2p_probe_resp_info) { + l_error("SSID matched but no valid P2P IE"); + continue; + } + + if (wsc_parse_probe_response(bss->wsc, bss->wsc_size, + &wsc_info) < 0) { + l_error("SSID matched but can't parse WSC " + "Probe Response info"); + continue; + } + + group_id = bss->p2p_probe_resp_info-> + device_info.device_addr; + selected_reg = wsc_info.selected_registrar; + capability = &bss->p2p_probe_resp_info->capability; + device_password_id = wsc_info.device_password_id; + amacs = wsc_info.authorized_macs; + } else if (bss->source_frame == SCAN_BSS_BEACON) { + struct wsc_beacon wsc_info; + + if (!bss->p2p_beacon_info) { + l_error("SSID matched but no valid P2P IE"); + continue; + } + + if (wsc_parse_beacon(bss->wsc, bss->wsc_size, + &wsc_info) < 0) { + l_error("SSID matched but can't parse WSC " + "Beacon info"); + continue; + } + + group_id = bss->p2p_beacon_info->device_addr; + selected_reg = wsc_info.selected_registrar; + capability = &bss->p2p_beacon_info->capability; + device_password_id = wsc_info.device_password_id; + amacs = wsc_info.authorized_macs; + } else + continue; + + if (memcmp(group_id, dev->go_group_id.device_addr, 6)) { + l_error("SSID matched but Group ID address didn't"); + continue; + } + + if (!selected_reg) { + /* + * Debug level because this will sometimes happen + * while the target is setting up the GO mode in the + * course of normal operation, and gets set to true + * in a few seconds, we just need to keep scanning. + */ + l_debug("SSID matched but not a Selected Reg"); + continue; + } + + if (dev->conn_peer->group && (capability->group_caps & + P2P_GROUP_CAP_GROUP_FORMATION)) { + l_error("SSID matched but not in Group Formation"); + continue; + } + + if (!dev->conn_peer->group && !(capability->group_caps & + P2P_GROUP_CAP_GROUP_FORMATION)) + /* + * We have to ignore this one for interoperability + * with some devices. + */ + l_debug("SSID matched but GO not in Group Formation, " + "proceeding anyway"); + + if (capability->group_caps & P2P_GROUP_CAP_GROUP_LIMIT) { + l_error("SSID matched but group already full"); + continue; + } + + if (device_password_id != dev->conn_password_id) { + l_error("SSID matched wrong Password ID"); + continue; + } + + if (!util_mem_is_zero(amacs, 30)) { + bool amacs_match = false; + int i; + + for (i = 0; i < 5; i++, amacs += 6) + if (!memcmp(amacs, dev->addr, 6) || + !memcmp(amacs, wildcard_addr, 6)) + amacs_match = true; + + if (!amacs_match) { + l_error("SSID matched we're not in AMacs"); + continue; + } + } + + l_debug("GO found in the scan results"); + + dev->conn_wsc_bss = bss; + p2p_device_interface_create(dev); + l_queue_remove(bss_list, bss); + l_queue_destroy(bss_list, + (l_queue_destroy_func_t) scan_bss_free); + return true; + } + + /* Retry a few times if the WSC AP not found or not ready */ + dev->go_scan_retry++; + + if (dev->go_scan_retry > 15) { + p2p_connect_failed(dev); + return false; + } + + p2p_provision_scan_start(dev); + return false; +} + +static void p2p_provision_scan_start(struct p2p_device *dev) +{ + struct scan_parameters params = {}; + uint8_t buf[256]; + + params.flush = true; + params.no_cck_rates = true; + params.ssid = dev->go_group_id.ssid; + params.extra_ie = p2p_build_scan_ies(dev, buf, sizeof(buf), + ¶ms.extra_ie_size); + L_WARN_ON(!params.extra_ie); + + /* + * Initially scan just the Operating Channel the GO reported + * during the negotiation. In theory there's no guarantee that + * it is going to be on that channel so we should fall back + * to scanning all the channels listed in the Channel List + * attribute. For simplicity we just do a full scan in that + * scenario -- for most target P2P devices we wouldn't be saving + * ourselves any work anyway as the Channel List is going to + * contain all of the 2.4 and 5G channels. + */ + if (dev->go_scan_retry < 12) { + params.freqs = scan_freq_set_new(); + scan_freq_set_add(params.freqs, dev->go_oper_freq); + } + + dev->scan_id = scan_active_full(dev->wdev_id, ¶ms, NULL, + p2p_provision_scan_notify, dev, + p2p_scan_destroy); + + if (params.freqs) + scan_freq_set_free(params.freqs); +} + static void p2p_start_client_provision(struct p2p_device *dev) { char bssid_str[18]; @@ -437,7 +657,8 @@ static void p2p_start_client_provision(struct p2p_device *dev) util_address_to_string(dev->go_group_id.device_addr), bssid_str); - /* TODO: start client provisioning */ + dev->go_scan_retry = 0; + p2p_provision_scan_start(dev); } static void p2p_config_timeout_destroy(void *user_data)