1252190Srpaulo/* 2252190Srpaulo * Wi-Fi Direct - P2P provision discovery 3252190Srpaulo * Copyright (c) 2009-2010, Atheros Communications 4252190Srpaulo * 5252190Srpaulo * This software may be distributed under the terms of the BSD license. 6252190Srpaulo * See README for more details. 7252190Srpaulo */ 8252190Srpaulo 9252190Srpaulo#include "includes.h" 10252190Srpaulo 11252190Srpaulo#include "common.h" 12252190Srpaulo#include "common/ieee802_11_defs.h" 13281806Srpaulo#include "common/wpa_ctrl.h" 14252190Srpaulo#include "wps/wps_defs.h" 15252190Srpaulo#include "p2p_i.h" 16252190Srpaulo#include "p2p.h" 17252190Srpaulo 18252190Srpaulo 19252190Srpaulo/* 20252190Srpaulo * Number of retries to attempt for provision discovery requests 21252190Srpaulo * in case the peer is not listening. 22252190Srpaulo */ 23252190Srpaulo#define MAX_PROV_DISC_REQ_RETRIES 120 24252190Srpaulo 25252190Srpaulo 26252190Srpaulostatic void p2p_build_wps_ie_config_methods(struct wpabuf *buf, 27252190Srpaulo u16 config_methods) 28252190Srpaulo{ 29252190Srpaulo u8 *len; 30252190Srpaulo wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); 31252190Srpaulo len = wpabuf_put(buf, 1); 32252190Srpaulo wpabuf_put_be32(buf, WPS_DEV_OUI_WFA); 33252190Srpaulo 34252190Srpaulo /* Config Methods */ 35252190Srpaulo wpabuf_put_be16(buf, ATTR_CONFIG_METHODS); 36252190Srpaulo wpabuf_put_be16(buf, 2); 37252190Srpaulo wpabuf_put_be16(buf, config_methods); 38252190Srpaulo 39252190Srpaulo p2p_buf_update_ie_hdr(buf, len); 40252190Srpaulo} 41252190Srpaulo 42252190Srpaulo 43281806Srpaulostatic void p2ps_add_new_group_info(struct p2p_data *p2p, struct wpabuf *buf) 44281806Srpaulo{ 45281806Srpaulo int found; 46281806Srpaulo u8 intended_addr[ETH_ALEN]; 47289549Srpaulo u8 ssid[SSID_MAX_LEN]; 48281806Srpaulo size_t ssid_len; 49281806Srpaulo int group_iface; 50281806Srpaulo 51281806Srpaulo if (!p2p->cfg->get_go_info) 52281806Srpaulo return; 53281806Srpaulo 54281806Srpaulo found = p2p->cfg->get_go_info( 55281806Srpaulo p2p->cfg->cb_ctx, intended_addr, ssid, 56281806Srpaulo &ssid_len, &group_iface); 57281806Srpaulo if (found) { 58281806Srpaulo p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, 59281806Srpaulo ssid, ssid_len); 60289549Srpaulo 61289549Srpaulo if (group_iface) 62289549Srpaulo p2p_buf_add_intended_addr(buf, p2p->intended_addr); 63289549Srpaulo else 64289549Srpaulo p2p_buf_add_intended_addr(buf, intended_addr); 65281806Srpaulo } else { 66281806Srpaulo if (!p2p->ssid_set) { 67281806Srpaulo p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len); 68281806Srpaulo p2p->ssid_set = 1; 69281806Srpaulo } 70281806Srpaulo 71281806Srpaulo /* Add pre-composed P2P Group ID */ 72281806Srpaulo p2p_buf_add_group_id(buf, p2p->cfg->dev_addr, 73281806Srpaulo p2p->ssid, p2p->ssid_len); 74281806Srpaulo 75281806Srpaulo if (group_iface) 76281806Srpaulo p2p_buf_add_intended_addr( 77281806Srpaulo buf, p2p->intended_addr); 78281806Srpaulo else 79281806Srpaulo p2p_buf_add_intended_addr( 80281806Srpaulo buf, p2p->cfg->dev_addr); 81281806Srpaulo } 82281806Srpaulo} 83281806Srpaulo 84281806Srpaulo 85281806Srpaulostatic void p2ps_add_pd_req_attrs(struct p2p_data *p2p, struct p2p_device *dev, 86281806Srpaulo struct wpabuf *buf, u16 config_methods) 87281806Srpaulo{ 88281806Srpaulo struct p2ps_provision *prov = p2p->p2ps_prov; 89289549Srpaulo struct p2ps_feature_capab fcap = { prov->cpt_mask, 0 }; 90281806Srpaulo int shared_group = 0; 91289549Srpaulo u8 ssid[SSID_MAX_LEN]; 92281806Srpaulo size_t ssid_len; 93281806Srpaulo u8 go_dev_addr[ETH_ALEN]; 94289549Srpaulo u8 intended_addr[ETH_ALEN]; 95281806Srpaulo 96281806Srpaulo /* If we might be explicite group owner, add GO details */ 97281806Srpaulo if (prov->conncap & (P2PS_SETUP_GROUP_OWNER | 98281806Srpaulo P2PS_SETUP_NEW)) 99281806Srpaulo p2ps_add_new_group_info(p2p, buf); 100281806Srpaulo 101281806Srpaulo if (prov->status >= 0) 102281806Srpaulo p2p_buf_add_status(buf, (u8) prov->status); 103281806Srpaulo else 104281806Srpaulo prov->method = config_methods; 105281806Srpaulo 106281806Srpaulo if (p2p->cfg->get_persistent_group) { 107281806Srpaulo shared_group = p2p->cfg->get_persistent_group( 108281806Srpaulo p2p->cfg->cb_ctx, dev->info.p2p_device_addr, NULL, 0, 109289549Srpaulo go_dev_addr, ssid, &ssid_len, intended_addr); 110281806Srpaulo } 111281806Srpaulo 112281806Srpaulo /* Add Operating Channel if conncap includes GO */ 113281806Srpaulo if (shared_group || 114281806Srpaulo (prov->conncap & (P2PS_SETUP_GROUP_OWNER | 115281806Srpaulo P2PS_SETUP_NEW))) { 116281806Srpaulo u8 tmp; 117281806Srpaulo 118281806Srpaulo p2p_go_select_channel(p2p, dev, &tmp); 119281806Srpaulo 120281806Srpaulo if (p2p->op_reg_class && p2p->op_channel) 121281806Srpaulo p2p_buf_add_operating_channel(buf, p2p->cfg->country, 122281806Srpaulo p2p->op_reg_class, 123281806Srpaulo p2p->op_channel); 124281806Srpaulo else 125281806Srpaulo p2p_buf_add_operating_channel(buf, p2p->cfg->country, 126281806Srpaulo p2p->cfg->op_reg_class, 127281806Srpaulo p2p->cfg->op_channel); 128281806Srpaulo } 129281806Srpaulo 130281806Srpaulo p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->cfg->channels); 131281806Srpaulo 132281806Srpaulo if (prov->info[0]) 133281806Srpaulo p2p_buf_add_session_info(buf, prov->info); 134281806Srpaulo 135281806Srpaulo p2p_buf_add_connection_capability(buf, prov->conncap); 136281806Srpaulo 137281806Srpaulo p2p_buf_add_advertisement_id(buf, prov->adv_id, prov->adv_mac); 138281806Srpaulo 139281806Srpaulo if (shared_group || prov->conncap == P2PS_SETUP_NEW || 140281806Srpaulo prov->conncap == 141281806Srpaulo (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW) || 142281806Srpaulo prov->conncap == 143281806Srpaulo (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT)) { 144281806Srpaulo /* Add Config Timeout */ 145281806Srpaulo p2p_buf_add_config_timeout(buf, p2p->go_timeout, 146281806Srpaulo p2p->client_timeout); 147281806Srpaulo } 148281806Srpaulo 149281806Srpaulo p2p_buf_add_listen_channel(buf, p2p->cfg->country, p2p->cfg->reg_class, 150281806Srpaulo p2p->cfg->channel); 151281806Srpaulo 152281806Srpaulo p2p_buf_add_session_id(buf, prov->session_id, prov->session_mac); 153281806Srpaulo 154289549Srpaulo p2p_buf_add_feature_capability(buf, sizeof(fcap), (const u8 *) &fcap); 155281806Srpaulo 156289549Srpaulo if (shared_group) { 157281806Srpaulo p2p_buf_add_persistent_group_info(buf, go_dev_addr, 158281806Srpaulo ssid, ssid_len); 159289549Srpaulo /* Add intended interface address if it is not added yet */ 160289549Srpaulo if ((prov->conncap == P2PS_SETUP_NONE || 161289549Srpaulo prov->conncap == P2PS_SETUP_CLIENT) && 162289549Srpaulo !is_zero_ether_addr(intended_addr)) 163289549Srpaulo p2p_buf_add_intended_addr(buf, intended_addr); 164289549Srpaulo } 165281806Srpaulo} 166281806Srpaulo 167281806Srpaulo 168252190Srpaulostatic struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p, 169281806Srpaulo struct p2p_device *dev, 170281806Srpaulo int join) 171252190Srpaulo{ 172252190Srpaulo struct wpabuf *buf; 173252190Srpaulo u8 *len; 174252190Srpaulo size_t extra = 0; 175281806Srpaulo u8 dialog_token = dev->dialog_token; 176281806Srpaulo u16 config_methods = dev->req_config_methods; 177281806Srpaulo struct p2p_device *go = join ? dev : NULL; 178281806Srpaulo u8 group_capab; 179252190Srpaulo 180252190Srpaulo#ifdef CONFIG_WIFI_DISPLAY 181252190Srpaulo if (p2p->wfd_ie_prov_disc_req) 182252190Srpaulo extra = wpabuf_len(p2p->wfd_ie_prov_disc_req); 183252190Srpaulo#endif /* CONFIG_WIFI_DISPLAY */ 184252190Srpaulo 185281806Srpaulo if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]) 186281806Srpaulo extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]); 187281806Srpaulo 188281806Srpaulo if (p2p->p2ps_prov) 189281806Srpaulo extra += os_strlen(p2p->p2ps_prov->info) + 1 + 190281806Srpaulo sizeof(struct p2ps_provision); 191281806Srpaulo 192252190Srpaulo buf = wpabuf_alloc(1000 + extra); 193252190Srpaulo if (buf == NULL) 194252190Srpaulo return NULL; 195252190Srpaulo 196252190Srpaulo p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_REQ, dialog_token); 197252190Srpaulo 198252190Srpaulo len = p2p_buf_add_ie_hdr(buf); 199281806Srpaulo 200281806Srpaulo group_capab = 0; 201281806Srpaulo if (p2p->p2ps_prov) { 202281806Srpaulo group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP; 203281806Srpaulo group_capab |= P2P_GROUP_CAPAB_PERSISTENT_RECONN; 204281806Srpaulo if (p2p->cross_connect) 205281806Srpaulo group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; 206281806Srpaulo if (p2p->cfg->p2p_intra_bss) 207281806Srpaulo group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; 208281806Srpaulo } 209252190Srpaulo p2p_buf_add_capability(buf, p2p->dev_capab & 210281806Srpaulo ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 211281806Srpaulo group_capab); 212252190Srpaulo p2p_buf_add_device_info(buf, p2p, NULL); 213281806Srpaulo if (p2p->p2ps_prov) { 214281806Srpaulo p2ps_add_pd_req_attrs(p2p, dev, buf, config_methods); 215281806Srpaulo } else if (go) { 216252190Srpaulo p2p_buf_add_group_id(buf, go->info.p2p_device_addr, 217252190Srpaulo go->oper_ssid, go->oper_ssid_len); 218252190Srpaulo } 219252190Srpaulo p2p_buf_update_ie_hdr(buf, len); 220252190Srpaulo 221252190Srpaulo /* WPS IE with Config Methods attribute */ 222252190Srpaulo p2p_build_wps_ie_config_methods(buf, config_methods); 223252190Srpaulo 224252190Srpaulo#ifdef CONFIG_WIFI_DISPLAY 225252190Srpaulo if (p2p->wfd_ie_prov_disc_req) 226252190Srpaulo wpabuf_put_buf(buf, p2p->wfd_ie_prov_disc_req); 227252190Srpaulo#endif /* CONFIG_WIFI_DISPLAY */ 228252190Srpaulo 229281806Srpaulo if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]) 230281806Srpaulo wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]); 231281806Srpaulo 232252190Srpaulo return buf; 233252190Srpaulo} 234252190Srpaulo 235252190Srpaulo 236252190Srpaulostatic struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, 237281806Srpaulo struct p2p_device *dev, 238252190Srpaulo u8 dialog_token, 239281806Srpaulo enum p2p_status_code status, 240252190Srpaulo u16 config_methods, 241281806Srpaulo u32 adv_id, 242252190Srpaulo const u8 *group_id, 243281806Srpaulo size_t group_id_len, 244281806Srpaulo const u8 *persist_ssid, 245289549Srpaulo size_t persist_ssid_len, 246289549Srpaulo const u8 *fcap, 247289549Srpaulo u16 fcap_len) 248252190Srpaulo{ 249252190Srpaulo struct wpabuf *buf; 250252190Srpaulo size_t extra = 0; 251281806Srpaulo int persist = 0; 252252190Srpaulo 253252190Srpaulo#ifdef CONFIG_WIFI_DISPLAY 254252190Srpaulo struct wpabuf *wfd_ie = p2p->wfd_ie_prov_disc_resp; 255252190Srpaulo if (wfd_ie && group_id) { 256252190Srpaulo size_t i; 257252190Srpaulo for (i = 0; i < p2p->num_groups; i++) { 258252190Srpaulo struct p2p_group *g = p2p->groups[i]; 259252190Srpaulo struct wpabuf *ie; 260252190Srpaulo if (!p2p_group_is_group_id_match(g, group_id, 261252190Srpaulo group_id_len)) 262252190Srpaulo continue; 263252190Srpaulo ie = p2p_group_get_wfd_ie(g); 264252190Srpaulo if (ie) { 265252190Srpaulo wfd_ie = ie; 266252190Srpaulo break; 267252190Srpaulo } 268252190Srpaulo } 269252190Srpaulo } 270252190Srpaulo if (wfd_ie) 271252190Srpaulo extra = wpabuf_len(wfd_ie); 272252190Srpaulo#endif /* CONFIG_WIFI_DISPLAY */ 273252190Srpaulo 274281806Srpaulo if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]) 275281806Srpaulo extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]); 276281806Srpaulo 277281806Srpaulo buf = wpabuf_alloc(1000 + extra); 278252190Srpaulo if (buf == NULL) 279252190Srpaulo return NULL; 280252190Srpaulo 281252190Srpaulo p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_RESP, dialog_token); 282252190Srpaulo 283281806Srpaulo /* Add P2P IE for P2PS */ 284281806Srpaulo if (p2p->p2ps_prov && p2p->p2ps_prov->adv_id == adv_id) { 285281806Srpaulo u8 *len = p2p_buf_add_ie_hdr(buf); 286281806Srpaulo struct p2ps_provision *prov = p2p->p2ps_prov; 287281806Srpaulo u8 group_capab; 288281806Srpaulo 289281806Srpaulo if (!status && prov->status != -1) 290281806Srpaulo status = prov->status; 291281806Srpaulo 292281806Srpaulo p2p_buf_add_status(buf, status); 293281806Srpaulo group_capab = P2P_GROUP_CAPAB_PERSISTENT_GROUP | 294281806Srpaulo P2P_GROUP_CAPAB_PERSISTENT_RECONN; 295281806Srpaulo if (p2p->cross_connect) 296281806Srpaulo group_capab |= P2P_GROUP_CAPAB_CROSS_CONN; 297281806Srpaulo if (p2p->cfg->p2p_intra_bss) 298281806Srpaulo group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST; 299281806Srpaulo p2p_buf_add_capability(buf, p2p->dev_capab & 300281806Srpaulo ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 301281806Srpaulo group_capab); 302281806Srpaulo p2p_buf_add_device_info(buf, p2p, NULL); 303281806Srpaulo 304281806Srpaulo if (persist_ssid && p2p->cfg->get_persistent_group && 305281806Srpaulo (status == P2P_SC_SUCCESS || 306281806Srpaulo status == P2P_SC_SUCCESS_DEFERRED)) { 307289549Srpaulo u8 ssid[SSID_MAX_LEN]; 308281806Srpaulo size_t ssid_len; 309281806Srpaulo u8 go_dev_addr[ETH_ALEN]; 310289549Srpaulo u8 intended_addr[ETH_ALEN]; 311281806Srpaulo 312281806Srpaulo persist = p2p->cfg->get_persistent_group( 313281806Srpaulo p2p->cfg->cb_ctx, 314281806Srpaulo dev->info.p2p_device_addr, 315281806Srpaulo persist_ssid, persist_ssid_len, go_dev_addr, 316289549Srpaulo ssid, &ssid_len, intended_addr); 317289549Srpaulo if (persist) { 318281806Srpaulo p2p_buf_add_persistent_group_info( 319281806Srpaulo buf, go_dev_addr, ssid, ssid_len); 320289549Srpaulo if (!is_zero_ether_addr(intended_addr)) 321289549Srpaulo p2p_buf_add_intended_addr( 322289549Srpaulo buf, intended_addr); 323289549Srpaulo } 324281806Srpaulo } 325281806Srpaulo 326281806Srpaulo if (!persist && (prov->conncap & P2PS_SETUP_GROUP_OWNER)) 327281806Srpaulo p2ps_add_new_group_info(p2p, buf); 328281806Srpaulo 329281806Srpaulo /* Add Operating Channel if conncap indicates GO */ 330281806Srpaulo if (persist || (prov->conncap & P2PS_SETUP_GROUP_OWNER)) { 331281806Srpaulo u8 tmp; 332281806Srpaulo 333281806Srpaulo if (dev) 334281806Srpaulo p2p_go_select_channel(p2p, dev, &tmp); 335281806Srpaulo 336281806Srpaulo if (p2p->op_reg_class && p2p->op_channel) 337281806Srpaulo p2p_buf_add_operating_channel( 338281806Srpaulo buf, p2p->cfg->country, 339281806Srpaulo p2p->op_reg_class, 340281806Srpaulo p2p->op_channel); 341281806Srpaulo else 342281806Srpaulo p2p_buf_add_operating_channel( 343281806Srpaulo buf, p2p->cfg->country, 344281806Srpaulo p2p->cfg->op_reg_class, 345281806Srpaulo p2p->cfg->op_channel); 346281806Srpaulo } 347281806Srpaulo 348281806Srpaulo p2p_buf_add_channel_list(buf, p2p->cfg->country, 349281806Srpaulo &p2p->cfg->channels); 350281806Srpaulo 351281806Srpaulo if (!persist && (status == P2P_SC_SUCCESS || 352281806Srpaulo status == P2P_SC_SUCCESS_DEFERRED)) 353281806Srpaulo p2p_buf_add_connection_capability(buf, prov->conncap); 354281806Srpaulo 355281806Srpaulo p2p_buf_add_advertisement_id(buf, adv_id, prov->adv_mac); 356281806Srpaulo 357281806Srpaulo p2p_buf_add_config_timeout(buf, p2p->go_timeout, 358281806Srpaulo p2p->client_timeout); 359281806Srpaulo 360281806Srpaulo p2p_buf_add_session_id(buf, prov->session_id, 361281806Srpaulo prov->session_mac); 362281806Srpaulo 363289549Srpaulo p2p_buf_add_feature_capability(buf, fcap_len, fcap); 364281806Srpaulo p2p_buf_update_ie_hdr(buf, len); 365281806Srpaulo } else if (status != P2P_SC_SUCCESS || adv_id) { 366281806Srpaulo u8 *len = p2p_buf_add_ie_hdr(buf); 367281806Srpaulo 368281806Srpaulo p2p_buf_add_status(buf, status); 369281806Srpaulo 370281806Srpaulo if (p2p->p2ps_prov) 371281806Srpaulo p2p_buf_add_advertisement_id(buf, adv_id, 372281806Srpaulo p2p->p2ps_prov->adv_mac); 373281806Srpaulo 374281806Srpaulo p2p_buf_update_ie_hdr(buf, len); 375281806Srpaulo } 376281806Srpaulo 377252190Srpaulo /* WPS IE with Config Methods attribute */ 378252190Srpaulo p2p_build_wps_ie_config_methods(buf, config_methods); 379252190Srpaulo 380252190Srpaulo#ifdef CONFIG_WIFI_DISPLAY 381252190Srpaulo if (wfd_ie) 382252190Srpaulo wpabuf_put_buf(buf, wfd_ie); 383252190Srpaulo#endif /* CONFIG_WIFI_DISPLAY */ 384252190Srpaulo 385281806Srpaulo if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]) 386281806Srpaulo wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]); 387281806Srpaulo 388252190Srpaulo return buf; 389252190Srpaulo} 390252190Srpaulo 391252190Srpaulo 392281806Srpaulostatic int p2ps_setup_p2ps_prov(struct p2p_data *p2p, u32 adv_id, 393281806Srpaulo u32 session_id, u16 method, 394281806Srpaulo const u8 *session_mac, const u8 *adv_mac) 395281806Srpaulo{ 396281806Srpaulo struct p2ps_provision *tmp; 397281806Srpaulo 398281806Srpaulo if (!p2p->p2ps_prov) { 399281806Srpaulo p2p->p2ps_prov = os_zalloc(sizeof(struct p2ps_provision) + 1); 400281806Srpaulo if (!p2p->p2ps_prov) 401281806Srpaulo return -1; 402281806Srpaulo } else { 403281806Srpaulo os_memset(p2p->p2ps_prov, 0, sizeof(struct p2ps_provision) + 1); 404281806Srpaulo } 405281806Srpaulo 406281806Srpaulo tmp = p2p->p2ps_prov; 407281806Srpaulo tmp->adv_id = adv_id; 408281806Srpaulo tmp->session_id = session_id; 409281806Srpaulo tmp->method = method; 410281806Srpaulo os_memcpy(tmp->session_mac, session_mac, ETH_ALEN); 411281806Srpaulo os_memcpy(tmp->adv_mac, adv_mac, ETH_ALEN); 412281806Srpaulo tmp->info[0] = '\0'; 413281806Srpaulo 414281806Srpaulo return 0; 415281806Srpaulo} 416281806Srpaulo 417281806Srpaulo 418289549Srpaulostatic u8 p2ps_own_preferred_cpt(const u8 *cpt_priority, u8 req_cpt_mask) 419289549Srpaulo{ 420289549Srpaulo int i; 421289549Srpaulo 422289549Srpaulo for (i = 0; cpt_priority[i]; i++) 423289549Srpaulo if (req_cpt_mask & cpt_priority[i]) 424289549Srpaulo return cpt_priority[i]; 425289549Srpaulo 426289549Srpaulo return 0; 427289549Srpaulo} 428289549Srpaulo 429289549Srpaulo 430252190Srpaulovoid p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa, 431252190Srpaulo const u8 *data, size_t len, int rx_freq) 432252190Srpaulo{ 433252190Srpaulo struct p2p_message msg; 434252190Srpaulo struct p2p_device *dev; 435252190Srpaulo int freq; 436281806Srpaulo enum p2p_status_code reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; 437252190Srpaulo struct wpabuf *resp; 438281806Srpaulo u32 adv_id = 0; 439281806Srpaulo struct p2ps_advertisement *p2ps_adv = NULL; 440281806Srpaulo u8 conncap = P2PS_SETUP_NEW; 441281806Srpaulo u8 auto_accept = 0; 442281806Srpaulo u32 session_id = 0; 443281806Srpaulo u8 session_mac[ETH_ALEN]; 444281806Srpaulo u8 adv_mac[ETH_ALEN]; 445289549Srpaulo const u8 *group_mac; 446281806Srpaulo int passwd_id = DEV_PW_DEFAULT; 447281806Srpaulo u16 config_methods; 448289549Srpaulo u16 allowed_config_methods = WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD; 449289549Srpaulo struct p2ps_feature_capab resp_fcap = { 0, 0 }; 450289549Srpaulo struct p2ps_feature_capab *req_fcap; 451252190Srpaulo 452252190Srpaulo if (p2p_parse(data, len, &msg)) 453252190Srpaulo return; 454252190Srpaulo 455281806Srpaulo p2p_dbg(p2p, "Received Provision Discovery Request from " MACSTR 456252190Srpaulo " with config methods 0x%x (freq=%d)", 457252190Srpaulo MAC2STR(sa), msg.wps_config_methods, rx_freq); 458289549Srpaulo group_mac = msg.intended_addr; 459252190Srpaulo 460252190Srpaulo dev = p2p_get_device(p2p, sa); 461252190Srpaulo if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { 462281806Srpaulo p2p_dbg(p2p, "Provision Discovery Request from unknown peer " 463281806Srpaulo MACSTR, MAC2STR(sa)); 464252190Srpaulo 465281806Srpaulo if (p2p_add_device(p2p, sa, rx_freq, NULL, 0, data + 1, len - 1, 466252190Srpaulo 0)) { 467281806Srpaulo p2p_dbg(p2p, "Provision Discovery Request add device failed " 468281806Srpaulo MACSTR, MAC2STR(sa)); 469252190Srpaulo } 470252190Srpaulo } else if (msg.wfd_subelems) { 471252190Srpaulo wpabuf_free(dev->info.wfd_subelems); 472252190Srpaulo dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); 473252190Srpaulo } 474252190Srpaulo 475289549Srpaulo if (msg.adv_id) 476289549Srpaulo allowed_config_methods |= WPS_CONFIG_P2PS; 477289549Srpaulo else 478289549Srpaulo allowed_config_methods |= WPS_CONFIG_PUSHBUTTON; 479289549Srpaulo 480289549Srpaulo if (!(msg.wps_config_methods & allowed_config_methods)) { 481281806Srpaulo p2p_dbg(p2p, "Unsupported Config Methods in Provision Discovery Request"); 482252190Srpaulo goto out; 483252190Srpaulo } 484252190Srpaulo 485281806Srpaulo /* Legacy (non-P2PS) - Unknown groups allowed for P2PS */ 486281806Srpaulo if (!msg.adv_id && msg.group_id) { 487252190Srpaulo size_t i; 488252190Srpaulo for (i = 0; i < p2p->num_groups; i++) { 489252190Srpaulo if (p2p_group_is_group_id_match(p2p->groups[i], 490252190Srpaulo msg.group_id, 491252190Srpaulo msg.group_id_len)) 492252190Srpaulo break; 493252190Srpaulo } 494252190Srpaulo if (i == p2p->num_groups) { 495281806Srpaulo p2p_dbg(p2p, "PD request for unknown P2P Group ID - reject"); 496252190Srpaulo goto out; 497252190Srpaulo } 498252190Srpaulo } 499252190Srpaulo 500281806Srpaulo if (dev) { 501252190Srpaulo dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY | 502281806Srpaulo P2P_DEV_PD_PEER_KEYPAD | 503281806Srpaulo P2P_DEV_PD_PEER_P2PS); 504281806Srpaulo 505281806Srpaulo /* Remove stale persistent groups */ 506281806Srpaulo if (p2p->cfg->remove_stale_groups) { 507281806Srpaulo p2p->cfg->remove_stale_groups( 508281806Srpaulo p2p->cfg->cb_ctx, dev->info.p2p_device_addr, 509281806Srpaulo msg.persistent_dev, 510281806Srpaulo msg.persistent_ssid, msg.persistent_ssid_len); 511281806Srpaulo } 512281806Srpaulo } 513252190Srpaulo if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) { 514281806Srpaulo p2p_dbg(p2p, "Peer " MACSTR 515252190Srpaulo " requested us to show a PIN on display", MAC2STR(sa)); 516252190Srpaulo if (dev) 517252190Srpaulo dev->flags |= P2P_DEV_PD_PEER_KEYPAD; 518281806Srpaulo passwd_id = DEV_PW_USER_SPECIFIED; 519252190Srpaulo } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) { 520281806Srpaulo p2p_dbg(p2p, "Peer " MACSTR 521252190Srpaulo " requested us to write its PIN using keypad", 522252190Srpaulo MAC2STR(sa)); 523252190Srpaulo if (dev) 524252190Srpaulo dev->flags |= P2P_DEV_PD_PEER_DISPLAY; 525281806Srpaulo passwd_id = DEV_PW_REGISTRAR_SPECIFIED; 526281806Srpaulo } else if (msg.wps_config_methods & WPS_CONFIG_P2PS) { 527281806Srpaulo p2p_dbg(p2p, "Peer " MACSTR " requesting P2PS PIN", 528281806Srpaulo MAC2STR(sa)); 529281806Srpaulo if (dev) 530281806Srpaulo dev->flags |= P2P_DEV_PD_PEER_P2PS; 531281806Srpaulo passwd_id = DEV_PW_P2PS_DEFAULT; 532252190Srpaulo } 533252190Srpaulo 534281806Srpaulo reject = P2P_SC_SUCCESS; 535252190Srpaulo 536281806Srpaulo os_memset(session_mac, 0, ETH_ALEN); 537281806Srpaulo os_memset(adv_mac, 0, ETH_ALEN); 538281806Srpaulo 539289549Srpaulo /* Note 1: A feature capability attribute structure can be changed 540289549Srpaulo * in the future. The assumption is that such modifications are 541289549Srpaulo * backwards compatible, therefore we allow processing of 542289549Srpaulo * msg.feature_cap exceeding the size of the p2ps_feature_capab 543289549Srpaulo * structure. 544289549Srpaulo * Note 2: Vverification of msg.feature_cap_len below has to be changed 545289549Srpaulo * to allow 2 byte feature capability processing if struct 546289549Srpaulo * p2ps_feature_capab is extended to include additional fields and it 547289549Srpaulo * affects the structure size. 548289549Srpaulo */ 549281806Srpaulo if (msg.adv_id && msg.session_id && msg.session_mac && msg.adv_mac && 550289549Srpaulo msg.feature_cap && msg.feature_cap_len >= sizeof(*req_fcap) && 551281806Srpaulo (msg.status || msg.conn_cap)) { 552281806Srpaulo u8 remote_conncap; 553281806Srpaulo 554289549Srpaulo req_fcap = (struct p2ps_feature_capab *) msg.feature_cap; 555281806Srpaulo 556281806Srpaulo os_memcpy(session_mac, msg.session_mac, ETH_ALEN); 557281806Srpaulo os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN); 558281806Srpaulo 559281806Srpaulo session_id = WPA_GET_LE32(msg.session_id); 560281806Srpaulo adv_id = WPA_GET_LE32(msg.adv_id); 561281806Srpaulo 562281806Srpaulo if (!msg.status) 563281806Srpaulo p2ps_adv = p2p_service_p2ps_id(p2p, adv_id); 564281806Srpaulo 565281806Srpaulo p2p_dbg(p2p, "adv_id: %x - p2ps_adv - %p", adv_id, p2ps_adv); 566281806Srpaulo 567281806Srpaulo if (msg.conn_cap) 568281806Srpaulo conncap = *msg.conn_cap; 569281806Srpaulo remote_conncap = conncap; 570281806Srpaulo 571281806Srpaulo if (p2ps_adv) { 572281806Srpaulo auto_accept = p2ps_adv->auto_accept; 573281806Srpaulo conncap = p2p->cfg->p2ps_group_capability( 574281806Srpaulo p2p->cfg->cb_ctx, conncap, auto_accept); 575281806Srpaulo 576281806Srpaulo p2p_dbg(p2p, "Conncap: local:%d remote:%d result:%d", 577281806Srpaulo auto_accept, remote_conncap, conncap); 578281806Srpaulo 579289549Srpaulo resp_fcap.cpt = 580289549Srpaulo p2ps_own_preferred_cpt(p2ps_adv->cpt_priority, 581289549Srpaulo req_fcap->cpt); 582289549Srpaulo 583289549Srpaulo p2p_dbg(p2p, 584289549Srpaulo "cpt: service:0x%x remote:0x%x result:0x%x", 585289549Srpaulo p2ps_adv->cpt_mask, req_fcap->cpt, 586289549Srpaulo resp_fcap.cpt); 587289549Srpaulo 588289549Srpaulo if (!resp_fcap.cpt) { 589281806Srpaulo p2p_dbg(p2p, 590289549Srpaulo "Incompatible P2PS feature capability CPT bitmask"); 591289549Srpaulo reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; 592289549Srpaulo } else if (p2ps_adv->config_methods && 593289549Srpaulo !(msg.wps_config_methods & 594289549Srpaulo p2ps_adv->config_methods)) { 595289549Srpaulo p2p_dbg(p2p, 596281806Srpaulo "Unsupported config methods in Provision Discovery Request (own=0x%x peer=0x%x)", 597281806Srpaulo p2ps_adv->config_methods, 598281806Srpaulo msg.wps_config_methods); 599281806Srpaulo reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; 600281806Srpaulo } else if (!p2ps_adv->state) { 601281806Srpaulo p2p_dbg(p2p, "P2PS state unavailable"); 602281806Srpaulo reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; 603281806Srpaulo } else if (!conncap) { 604281806Srpaulo p2p_dbg(p2p, "Conncap resolution failed"); 605281806Srpaulo reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; 606281806Srpaulo } 607281806Srpaulo 608281806Srpaulo if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) { 609281806Srpaulo p2p_dbg(p2p, "Keypad - always defer"); 610281806Srpaulo auto_accept = 0; 611281806Srpaulo } 612281806Srpaulo 613281806Srpaulo if (auto_accept || reject != P2P_SC_SUCCESS) { 614281806Srpaulo struct p2ps_provision *tmp; 615281806Srpaulo 616281806Srpaulo if (reject == P2P_SC_SUCCESS && !conncap) { 617281806Srpaulo reject = 618281806Srpaulo P2P_SC_FAIL_INCOMPATIBLE_PARAMS; 619281806Srpaulo } 620281806Srpaulo 621281806Srpaulo if (p2ps_setup_p2ps_prov( 622281806Srpaulo p2p, adv_id, session_id, 623281806Srpaulo msg.wps_config_methods, 624281806Srpaulo session_mac, adv_mac) < 0) { 625281806Srpaulo reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; 626281806Srpaulo goto out; 627281806Srpaulo } 628281806Srpaulo 629281806Srpaulo tmp = p2p->p2ps_prov; 630281806Srpaulo if (conncap) { 631281806Srpaulo tmp->conncap = conncap; 632281806Srpaulo tmp->status = P2P_SC_SUCCESS; 633281806Srpaulo } else { 634281806Srpaulo tmp->conncap = auto_accept; 635281806Srpaulo tmp->status = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; 636281806Srpaulo } 637281806Srpaulo 638281806Srpaulo if (reject != P2P_SC_SUCCESS) 639281806Srpaulo goto out; 640281806Srpaulo } 641281806Srpaulo } else if (!msg.status) { 642281806Srpaulo reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; 643281806Srpaulo goto out; 644281806Srpaulo } 645281806Srpaulo 646281806Srpaulo if (!msg.status && !auto_accept && 647281806Srpaulo (!p2p->p2ps_prov || p2p->p2ps_prov->adv_id != adv_id)) { 648281806Srpaulo struct p2ps_provision *tmp; 649281806Srpaulo 650281806Srpaulo if (!conncap) { 651281806Srpaulo reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS; 652281806Srpaulo goto out; 653281806Srpaulo } 654281806Srpaulo 655281806Srpaulo if (p2ps_setup_p2ps_prov(p2p, adv_id, session_id, 656281806Srpaulo msg.wps_config_methods, 657281806Srpaulo session_mac, adv_mac) < 0) { 658281806Srpaulo reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE; 659281806Srpaulo goto out; 660281806Srpaulo } 661281806Srpaulo tmp = p2p->p2ps_prov; 662281806Srpaulo reject = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; 663281806Srpaulo tmp->status = reject; 664281806Srpaulo } 665281806Srpaulo 666281806Srpaulo if (msg.status) { 667281806Srpaulo if (*msg.status && 668281806Srpaulo *msg.status != P2P_SC_SUCCESS_DEFERRED) { 669281806Srpaulo reject = *msg.status; 670281806Srpaulo } else if (*msg.status == P2P_SC_SUCCESS_DEFERRED && 671281806Srpaulo p2p->p2ps_prov) { 672281806Srpaulo u16 method = p2p->p2ps_prov->method; 673281806Srpaulo 674281806Srpaulo conncap = p2p->cfg->p2ps_group_capability( 675281806Srpaulo p2p->cfg->cb_ctx, remote_conncap, 676281806Srpaulo p2p->p2ps_prov->conncap); 677281806Srpaulo 678281806Srpaulo p2p_dbg(p2p, 679281806Srpaulo "Conncap: local:%d remote:%d result:%d", 680281806Srpaulo p2p->p2ps_prov->conncap, 681281806Srpaulo remote_conncap, conncap); 682281806Srpaulo 683289549Srpaulo resp_fcap.cpt = p2ps_own_preferred_cpt( 684289549Srpaulo p2p->p2ps_prov->cpt_priority, 685289549Srpaulo req_fcap->cpt); 686289549Srpaulo 687289549Srpaulo p2p_dbg(p2p, 688289549Srpaulo "cpt: local:0x%x remote:0x%x result:0x%x", 689289549Srpaulo p2p->p2ps_prov->cpt_mask, 690289549Srpaulo req_fcap->cpt, resp_fcap.cpt); 691289549Srpaulo 692281806Srpaulo /* 693281806Srpaulo * Ensure that if we asked for PIN originally, 694281806Srpaulo * our method is consistent with original 695281806Srpaulo * request. 696281806Srpaulo */ 697281806Srpaulo if (method & WPS_CONFIG_DISPLAY) 698281806Srpaulo method = WPS_CONFIG_KEYPAD; 699281806Srpaulo else if (method & WPS_CONFIG_KEYPAD) 700281806Srpaulo method = WPS_CONFIG_DISPLAY; 701281806Srpaulo 702281806Srpaulo if (!conncap || 703289549Srpaulo !(msg.wps_config_methods & method)) { 704289549Srpaulo /* 705289549Srpaulo * Reject this "Deferred Accept* 706289549Srpaulo * if incompatible conncap or method 707289549Srpaulo */ 708281806Srpaulo reject = 709281806Srpaulo P2P_SC_FAIL_INCOMPATIBLE_PARAMS; 710289549Srpaulo } else if (!resp_fcap.cpt) { 711289549Srpaulo p2p_dbg(p2p, 712289549Srpaulo "Incompatible P2PS feature capability CPT bitmask"); 713289549Srpaulo reject = 714289549Srpaulo P2P_SC_FAIL_INCOMPATIBLE_PARAMS; 715289549Srpaulo } else { 716281806Srpaulo reject = P2P_SC_SUCCESS; 717289549Srpaulo } 718281806Srpaulo 719281806Srpaulo p2p->p2ps_prov->status = reject; 720281806Srpaulo p2p->p2ps_prov->conncap = conncap; 721281806Srpaulo } 722281806Srpaulo } 723281806Srpaulo } 724281806Srpaulo 725252190Srpauloout: 726281806Srpaulo if (reject == P2P_SC_SUCCESS || 727281806Srpaulo reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) 728281806Srpaulo config_methods = msg.wps_config_methods; 729281806Srpaulo else 730281806Srpaulo config_methods = 0; 731281806Srpaulo resp = p2p_build_prov_disc_resp(p2p, dev, msg.dialog_token, reject, 732281806Srpaulo config_methods, adv_id, 733281806Srpaulo msg.group_id, msg.group_id_len, 734281806Srpaulo msg.persistent_ssid, 735289549Srpaulo msg.persistent_ssid_len, 736289549Srpaulo (const u8 *) &resp_fcap, 737289549Srpaulo sizeof(resp_fcap)); 738252190Srpaulo if (resp == NULL) { 739252190Srpaulo p2p_parse_free(&msg); 740252190Srpaulo return; 741252190Srpaulo } 742281806Srpaulo p2p_dbg(p2p, "Sending Provision Discovery Response"); 743252190Srpaulo if (rx_freq > 0) 744252190Srpaulo freq = rx_freq; 745252190Srpaulo else 746281806Srpaulo freq = p2p_channel_to_freq(p2p->cfg->reg_class, 747252190Srpaulo p2p->cfg->channel); 748252190Srpaulo if (freq < 0) { 749281806Srpaulo p2p_dbg(p2p, "Unknown regulatory class/channel"); 750252190Srpaulo wpabuf_free(resp); 751252190Srpaulo p2p_parse_free(&msg); 752252190Srpaulo return; 753252190Srpaulo } 754281806Srpaulo p2p->pending_action_state = P2P_PENDING_PD_RESPONSE; 755252190Srpaulo if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr, 756252190Srpaulo p2p->cfg->dev_addr, 757252190Srpaulo wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { 758281806Srpaulo p2p_dbg(p2p, "Failed to send Action frame"); 759281806Srpaulo } else 760281806Srpaulo p2p->send_action_in_progress = 1; 761252190Srpaulo 762252190Srpaulo wpabuf_free(resp); 763252190Srpaulo 764281806Srpaulo if (!p2p->cfg->p2ps_prov_complete) { 765281806Srpaulo /* Don't emit anything */ 766281806Srpaulo } else if (msg.status && *msg.status != P2P_SC_SUCCESS && 767281806Srpaulo *msg.status != P2P_SC_SUCCESS_DEFERRED) { 768281806Srpaulo reject = *msg.status; 769281806Srpaulo p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, reject, 770281806Srpaulo sa, adv_mac, session_mac, 771281806Srpaulo NULL, adv_id, session_id, 772281806Srpaulo 0, 0, msg.persistent_ssid, 773281806Srpaulo msg.persistent_ssid_len, 774289549Srpaulo 0, 0, NULL, NULL, 0); 775281806Srpaulo } else if (msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED && 776281806Srpaulo p2p->p2ps_prov) { 777281806Srpaulo p2p->p2ps_prov->status = reject; 778281806Srpaulo p2p->p2ps_prov->conncap = conncap; 779281806Srpaulo 780281806Srpaulo if (reject != P2P_SC_SUCCESS) 781281806Srpaulo p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, reject, 782281806Srpaulo sa, adv_mac, session_mac, 783281806Srpaulo NULL, adv_id, 784281806Srpaulo session_id, conncap, 0, 785281806Srpaulo msg.persistent_ssid, 786281806Srpaulo msg.persistent_ssid_len, 0, 787289549Srpaulo 0, NULL, NULL, 0); 788281806Srpaulo else 789281806Srpaulo p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, 790281806Srpaulo *msg.status, 791281806Srpaulo sa, adv_mac, session_mac, 792281806Srpaulo group_mac, adv_id, 793281806Srpaulo session_id, conncap, 794281806Srpaulo passwd_id, 795281806Srpaulo msg.persistent_ssid, 796281806Srpaulo msg.persistent_ssid_len, 0, 797289549Srpaulo 0, NULL, 798289549Srpaulo (const u8 *) &resp_fcap, 799289549Srpaulo sizeof(resp_fcap)); 800281806Srpaulo } else if (msg.status && p2p->p2ps_prov) { 801281806Srpaulo p2p->p2ps_prov->status = P2P_SC_SUCCESS; 802281806Srpaulo p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, *msg.status, sa, 803281806Srpaulo adv_mac, session_mac, group_mac, 804281806Srpaulo adv_id, session_id, conncap, 805281806Srpaulo passwd_id, 806281806Srpaulo msg.persistent_ssid, 807281806Srpaulo msg.persistent_ssid_len, 808289549Srpaulo 0, 0, NULL, 809289549Srpaulo (const u8 *) &resp_fcap, 810289549Srpaulo sizeof(resp_fcap)); 811281806Srpaulo } else if (msg.status) { 812281806Srpaulo } else if (auto_accept && reject == P2P_SC_SUCCESS) { 813281806Srpaulo p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS, 814281806Srpaulo sa, adv_mac, session_mac, 815281806Srpaulo group_mac, adv_id, session_id, 816281806Srpaulo conncap, passwd_id, 817281806Srpaulo msg.persistent_ssid, 818281806Srpaulo msg.persistent_ssid_len, 819289549Srpaulo 0, 0, NULL, 820289549Srpaulo (const u8 *) &resp_fcap, 821289549Srpaulo sizeof(resp_fcap)); 822281806Srpaulo } else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && 823281806Srpaulo (!msg.session_info || !msg.session_info_len)) { 824281806Srpaulo p2p->p2ps_prov->method = msg.wps_config_methods; 825281806Srpaulo 826281806Srpaulo p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS, 827281806Srpaulo sa, adv_mac, session_mac, 828281806Srpaulo group_mac, adv_id, session_id, 829281806Srpaulo conncap, passwd_id, 830281806Srpaulo msg.persistent_ssid, 831281806Srpaulo msg.persistent_ssid_len, 832289549Srpaulo 0, 1, NULL, 833289549Srpaulo (const u8 *) &resp_fcap, 834289549Srpaulo sizeof(resp_fcap)); 835281806Srpaulo } else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) { 836281806Srpaulo size_t buf_len = msg.session_info_len; 837281806Srpaulo char *buf = os_malloc(2 * buf_len + 1); 838281806Srpaulo 839281806Srpaulo if (buf) { 840281806Srpaulo p2p->p2ps_prov->method = msg.wps_config_methods; 841281806Srpaulo 842281806Srpaulo utf8_escape((char *) msg.session_info, buf_len, 843281806Srpaulo buf, 2 * buf_len + 1); 844281806Srpaulo 845281806Srpaulo p2p->cfg->p2ps_prov_complete( 846281806Srpaulo p2p->cfg->cb_ctx, P2P_SC_SUCCESS, sa, 847281806Srpaulo adv_mac, session_mac, group_mac, adv_id, 848281806Srpaulo session_id, conncap, passwd_id, 849281806Srpaulo msg.persistent_ssid, msg.persistent_ssid_len, 850289549Srpaulo 0, 1, buf, 851289549Srpaulo (const u8 *) &resp_fcap, sizeof(resp_fcap)); 852281806Srpaulo 853281806Srpaulo os_free(buf); 854281806Srpaulo } 855281806Srpaulo } 856281806Srpaulo 857289549Srpaulo /* 858289549Srpaulo * prov_disc_req callback is used to generate P2P-PROV-DISC-ENTER-PIN, 859289549Srpaulo * P2P-PROV-DISC-SHOW-PIN, and P2P-PROV-DISC-PBC-REQ events. 860289549Srpaulo * Call it either on legacy P2P PD or on P2PS PD only if we need to 861289549Srpaulo * enter/show PIN. 862289549Srpaulo * 863289549Srpaulo * The callback is called in the following cases: 864289549Srpaulo * 1. Legacy P2P PD request, response status SUCCESS 865289549Srpaulo * 2. P2PS advertiser, method: DISPLAY, autoaccept: TRUE, 866289549Srpaulo * response status: SUCCESS 867289549Srpaulo * 3. P2PS advertiser, method DISPLAY, autoaccept: FALSE, 868289549Srpaulo * response status: INFO_CURRENTLY_UNAVAILABLE 869289549Srpaulo * 4. P2PS advertiser, method: KEYPAD, autoaccept==any, 870289549Srpaulo * response status: INFO_CURRENTLY_UNAVAILABLE 871289549Srpaulo * 5. P2PS follow-on with SUCCESS_DEFERRED, 872289549Srpaulo * advertiser role: DISPLAY, autoaccept: FALSE, 873289549Srpaulo * seeker: KEYPAD, response status: SUCCESS 874289549Srpaulo */ 875289549Srpaulo if (p2p->cfg->prov_disc_req && 876289549Srpaulo ((reject == P2P_SC_SUCCESS && !msg.adv_id) || 877289549Srpaulo (!msg.status && 878289549Srpaulo (reject == P2P_SC_SUCCESS || 879289549Srpaulo reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) && 880289549Srpaulo passwd_id == DEV_PW_USER_SPECIFIED) || 881289549Srpaulo (!msg.status && 882289549Srpaulo reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && 883289549Srpaulo passwd_id == DEV_PW_REGISTRAR_SPECIFIED) || 884289549Srpaulo (reject == P2P_SC_SUCCESS && 885289549Srpaulo msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED && 886289549Srpaulo passwd_id == DEV_PW_REGISTRAR_SPECIFIED))) { 887252190Srpaulo const u8 *dev_addr = sa; 888289549Srpaulo 889252190Srpaulo if (msg.p2p_device_addr) 890252190Srpaulo dev_addr = msg.p2p_device_addr; 891252190Srpaulo p2p->cfg->prov_disc_req(p2p->cfg->cb_ctx, sa, 892252190Srpaulo msg.wps_config_methods, 893252190Srpaulo dev_addr, msg.pri_dev_type, 894252190Srpaulo msg.device_name, msg.config_methods, 895252190Srpaulo msg.capability ? msg.capability[0] : 0, 896252190Srpaulo msg.capability ? msg.capability[1] : 897252190Srpaulo 0, 898252190Srpaulo msg.group_id, msg.group_id_len); 899252190Srpaulo } 900289549Srpaulo 901289549Srpaulo if (dev && reject == P2P_SC_SUCCESS) { 902289549Srpaulo switch (config_methods) { 903289549Srpaulo case WPS_CONFIG_DISPLAY: 904289549Srpaulo dev->wps_prov_info = WPS_CONFIG_KEYPAD; 905289549Srpaulo break; 906289549Srpaulo case WPS_CONFIG_KEYPAD: 907289549Srpaulo dev->wps_prov_info = WPS_CONFIG_DISPLAY; 908289549Srpaulo break; 909289549Srpaulo case WPS_CONFIG_PUSHBUTTON: 910289549Srpaulo dev->wps_prov_info = WPS_CONFIG_PUSHBUTTON; 911289549Srpaulo break; 912289549Srpaulo case WPS_CONFIG_P2PS: 913289549Srpaulo dev->wps_prov_info = WPS_CONFIG_P2PS; 914289549Srpaulo break; 915289549Srpaulo default: 916289549Srpaulo dev->wps_prov_info = 0; 917289549Srpaulo break; 918289549Srpaulo } 919289549Srpaulo 920289549Srpaulo if (msg.intended_addr) 921289549Srpaulo os_memcpy(dev->interface_addr, msg.intended_addr, 922289549Srpaulo ETH_ALEN); 923289549Srpaulo } 924252190Srpaulo p2p_parse_free(&msg); 925252190Srpaulo} 926252190Srpaulo 927252190Srpaulo 928289549Srpaulostatic int p2p_validate_p2ps_pd_resp(struct p2p_data *p2p, 929289549Srpaulo struct p2p_message *msg) 930289549Srpaulo{ 931289549Srpaulo u8 conn_cap_go = 0; 932289549Srpaulo u8 conn_cap_cli = 0; 933289549Srpaulo u32 session_id; 934289549Srpaulo u32 adv_id; 935289549Srpaulo 936289549Srpaulo#define P2PS_PD_RESP_CHECK(_val, _attr) \ 937289549Srpaulo do { \ 938289549Srpaulo if ((_val) && !msg->_attr) { \ 939289549Srpaulo p2p_dbg(p2p, "P2PS PD Response missing " #_attr); \ 940289549Srpaulo return -1; \ 941289549Srpaulo } \ 942289549Srpaulo } while (0) 943289549Srpaulo 944289549Srpaulo P2PS_PD_RESP_CHECK(1, status); 945289549Srpaulo P2PS_PD_RESP_CHECK(1, adv_id); 946289549Srpaulo P2PS_PD_RESP_CHECK(1, adv_mac); 947289549Srpaulo P2PS_PD_RESP_CHECK(1, capability); 948289549Srpaulo P2PS_PD_RESP_CHECK(1, p2p_device_info); 949289549Srpaulo P2PS_PD_RESP_CHECK(1, session_id); 950289549Srpaulo P2PS_PD_RESP_CHECK(1, session_mac); 951289549Srpaulo P2PS_PD_RESP_CHECK(1, feature_cap); 952289549Srpaulo 953289549Srpaulo session_id = WPA_GET_LE32(msg->session_id); 954289549Srpaulo adv_id = WPA_GET_LE32(msg->adv_id); 955289549Srpaulo 956289549Srpaulo if (p2p->p2ps_prov->session_id != session_id) { 957289549Srpaulo p2p_dbg(p2p, 958289549Srpaulo "Ignore PD Response with unexpected Session ID"); 959289549Srpaulo return -1; 960289549Srpaulo } 961289549Srpaulo 962289549Srpaulo if (os_memcmp(p2p->p2ps_prov->session_mac, msg->session_mac, 963289549Srpaulo ETH_ALEN)) { 964289549Srpaulo p2p_dbg(p2p, 965289549Srpaulo "Ignore PD Response with unexpected Session MAC"); 966289549Srpaulo return -1; 967289549Srpaulo } 968289549Srpaulo 969289549Srpaulo if (p2p->p2ps_prov->adv_id != adv_id) { 970289549Srpaulo p2p_dbg(p2p, 971289549Srpaulo "Ignore PD Response with unexpected Advertisement ID"); 972289549Srpaulo return -1; 973289549Srpaulo } 974289549Srpaulo 975289549Srpaulo if (os_memcmp(p2p->p2ps_prov->adv_mac, msg->adv_mac, ETH_ALEN) != 0) { 976289549Srpaulo p2p_dbg(p2p, 977289549Srpaulo "Ignore PD Response with unexpected Advertisement MAC"); 978289549Srpaulo return -1; 979289549Srpaulo } 980289549Srpaulo 981289549Srpaulo if (msg->listen_channel) { 982289549Srpaulo p2p_dbg(p2p, 983289549Srpaulo "Ignore malformed PD Response - unexpected Listen Channel"); 984289549Srpaulo return -1; 985289549Srpaulo } 986289549Srpaulo 987289549Srpaulo if (*msg->status == P2P_SC_SUCCESS && 988289549Srpaulo !(!!msg->conn_cap ^ !!msg->persistent_dev)) { 989289549Srpaulo p2p_dbg(p2p, 990289549Srpaulo "Ignore malformed PD Response - either conn_cap or persistent group should be present"); 991289549Srpaulo return -1; 992289549Srpaulo } 993289549Srpaulo 994289549Srpaulo if (msg->persistent_dev && *msg->status != P2P_SC_SUCCESS) { 995289549Srpaulo p2p_dbg(p2p, 996289549Srpaulo "Ignore malformed PD Response - persistent group is present, but the status isn't success"); 997289549Srpaulo return -1; 998289549Srpaulo } 999289549Srpaulo 1000289549Srpaulo if (msg->conn_cap) { 1001289549Srpaulo conn_cap_go = *msg->conn_cap == P2PS_SETUP_GROUP_OWNER; 1002289549Srpaulo conn_cap_cli = *msg->conn_cap == P2PS_SETUP_CLIENT; 1003289549Srpaulo } 1004289549Srpaulo 1005289549Srpaulo P2PS_PD_RESP_CHECK(msg->persistent_dev || conn_cap_go || conn_cap_cli, 1006289549Srpaulo channel_list); 1007289549Srpaulo P2PS_PD_RESP_CHECK(msg->persistent_dev || conn_cap_go || conn_cap_cli, 1008289549Srpaulo config_timeout); 1009289549Srpaulo 1010289549Srpaulo P2PS_PD_RESP_CHECK(conn_cap_go, group_id); 1011289549Srpaulo P2PS_PD_RESP_CHECK(conn_cap_go, intended_addr); 1012289549Srpaulo P2PS_PD_RESP_CHECK(conn_cap_go, operating_channel); 1013289549Srpaulo /* 1014289549Srpaulo * TODO: Also validate that operating channel is present if the device 1015289549Srpaulo * is a GO in a persistent group. We can't do it here since we don't 1016289549Srpaulo * know what is the role of the peer. It should be probably done in 1017289549Srpaulo * p2ps_prov_complete callback, but currently operating channel isn't 1018289549Srpaulo * passed to it. 1019289549Srpaulo */ 1020289549Srpaulo 1021289549Srpaulo#undef P2PS_PD_RESP_CHECK 1022289549Srpaulo 1023289549Srpaulo return 0; 1024289549Srpaulo} 1025289549Srpaulo 1026289549Srpaulo 1027252190Srpaulovoid p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa, 1028252190Srpaulo const u8 *data, size_t len) 1029252190Srpaulo{ 1030252190Srpaulo struct p2p_message msg; 1031252190Srpaulo struct p2p_device *dev; 1032281806Srpaulo u16 report_config_methods = 0, req_config_methods; 1033281806Srpaulo u8 status = P2P_SC_SUCCESS; 1034281806Srpaulo u32 adv_id = 0; 1035281806Srpaulo u8 conncap = P2PS_SETUP_NEW; 1036281806Srpaulo u8 adv_mac[ETH_ALEN]; 1037289549Srpaulo const u8 *group_mac; 1038281806Srpaulo int passwd_id = DEV_PW_DEFAULT; 1039289549Srpaulo int p2ps_seeker; 1040252190Srpaulo 1041252190Srpaulo if (p2p_parse(data, len, &msg)) 1042252190Srpaulo return; 1043252190Srpaulo 1044289549Srpaulo if (p2p->p2ps_prov && p2p_validate_p2ps_pd_resp(p2p, &msg)) { 1045289549Srpaulo p2p_parse_free(&msg); 1046289549Srpaulo return; 1047289549Srpaulo } 1048289549Srpaulo 1049281806Srpaulo /* Parse the P2PS members present */ 1050281806Srpaulo if (msg.status) 1051281806Srpaulo status = *msg.status; 1052281806Srpaulo 1053289549Srpaulo group_mac = msg.intended_addr; 1054281806Srpaulo 1055281806Srpaulo if (msg.adv_mac) 1056281806Srpaulo os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN); 1057281806Srpaulo else 1058281806Srpaulo os_memset(adv_mac, 0, ETH_ALEN); 1059281806Srpaulo 1060281806Srpaulo if (msg.adv_id) 1061281806Srpaulo adv_id = WPA_GET_LE32(msg.adv_id); 1062281806Srpaulo 1063281806Srpaulo if (msg.conn_cap) { 1064281806Srpaulo conncap = *msg.conn_cap; 1065281806Srpaulo 1066281806Srpaulo /* Switch bits to local relative */ 1067281806Srpaulo switch (conncap) { 1068281806Srpaulo case P2PS_SETUP_GROUP_OWNER: 1069281806Srpaulo conncap = P2PS_SETUP_CLIENT; 1070281806Srpaulo break; 1071281806Srpaulo case P2PS_SETUP_CLIENT: 1072281806Srpaulo conncap = P2PS_SETUP_GROUP_OWNER; 1073281806Srpaulo break; 1074281806Srpaulo } 1075281806Srpaulo } 1076281806Srpaulo 1077281806Srpaulo p2p_dbg(p2p, "Received Provision Discovery Response from " MACSTR 1078252190Srpaulo " with config methods 0x%x", 1079252190Srpaulo MAC2STR(sa), msg.wps_config_methods); 1080252190Srpaulo 1081252190Srpaulo dev = p2p_get_device(p2p, sa); 1082252190Srpaulo if (dev == NULL || !dev->req_config_methods) { 1083281806Srpaulo p2p_dbg(p2p, "Ignore Provision Discovery Response from " MACSTR 1084281806Srpaulo " with no pending request", MAC2STR(sa)); 1085252190Srpaulo p2p_parse_free(&msg); 1086252190Srpaulo return; 1087252190Srpaulo } 1088252190Srpaulo 1089252190Srpaulo if (dev->dialog_token != msg.dialog_token) { 1090281806Srpaulo p2p_dbg(p2p, "Ignore Provision Discovery Response with unexpected Dialog Token %u (expected %u)", 1091252190Srpaulo msg.dialog_token, dev->dialog_token); 1092252190Srpaulo p2p_parse_free(&msg); 1093252190Srpaulo return; 1094252190Srpaulo } 1095252190Srpaulo 1096252190Srpaulo if (p2p->pending_action_state == P2P_PENDING_PD) { 1097252190Srpaulo os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN); 1098252190Srpaulo p2p->pending_action_state = P2P_NO_PENDING_ACTION; 1099252190Srpaulo } 1100252190Srpaulo 1101289549Srpaulo p2ps_seeker = p2p->p2ps_prov && p2p->p2ps_prov->pd_seeker; 1102289549Srpaulo 1103252190Srpaulo /* 1104281806Srpaulo * Use a local copy of the requested config methods since 1105281806Srpaulo * p2p_reset_pending_pd() can clear this in the peer entry. 1106281806Srpaulo */ 1107281806Srpaulo req_config_methods = dev->req_config_methods; 1108281806Srpaulo 1109281806Srpaulo /* 1110252190Srpaulo * If the response is from the peer to whom a user initiated request 1111252190Srpaulo * was sent earlier, we reset that state info here. 1112252190Srpaulo */ 1113252190Srpaulo if (p2p->user_initiated_pd && 1114252190Srpaulo os_memcmp(p2p->pending_pd_devaddr, sa, ETH_ALEN) == 0) 1115252190Srpaulo p2p_reset_pending_pd(p2p); 1116252190Srpaulo 1117281806Srpaulo if (msg.wps_config_methods != req_config_methods) { 1118281806Srpaulo p2p_dbg(p2p, "Peer rejected our Provision Discovery Request (received config_methods 0x%x expected 0x%x", 1119281806Srpaulo msg.wps_config_methods, req_config_methods); 1120252190Srpaulo if (p2p->cfg->prov_disc_fail) 1121252190Srpaulo p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa, 1122281806Srpaulo P2P_PROV_DISC_REJECTED, 1123281806Srpaulo adv_id, adv_mac, NULL); 1124252190Srpaulo p2p_parse_free(&msg); 1125289549Srpaulo p2ps_prov_free(p2p); 1126252190Srpaulo goto out; 1127252190Srpaulo } 1128252190Srpaulo 1129281806Srpaulo report_config_methods = req_config_methods; 1130252190Srpaulo dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY | 1131281806Srpaulo P2P_DEV_PD_PEER_KEYPAD | 1132281806Srpaulo P2P_DEV_PD_PEER_P2PS); 1133281806Srpaulo if (req_config_methods & WPS_CONFIG_DISPLAY) { 1134281806Srpaulo p2p_dbg(p2p, "Peer " MACSTR 1135252190Srpaulo " accepted to show a PIN on display", MAC2STR(sa)); 1136252190Srpaulo dev->flags |= P2P_DEV_PD_PEER_DISPLAY; 1137281806Srpaulo passwd_id = DEV_PW_REGISTRAR_SPECIFIED; 1138252190Srpaulo } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) { 1139281806Srpaulo p2p_dbg(p2p, "Peer " MACSTR 1140252190Srpaulo " accepted to write our PIN using keypad", 1141252190Srpaulo MAC2STR(sa)); 1142252190Srpaulo dev->flags |= P2P_DEV_PD_PEER_KEYPAD; 1143281806Srpaulo passwd_id = DEV_PW_USER_SPECIFIED; 1144281806Srpaulo } else if (msg.wps_config_methods & WPS_CONFIG_P2PS) { 1145281806Srpaulo p2p_dbg(p2p, "Peer " MACSTR " accepted P2PS PIN", 1146281806Srpaulo MAC2STR(sa)); 1147281806Srpaulo dev->flags |= P2P_DEV_PD_PEER_P2PS; 1148281806Srpaulo passwd_id = DEV_PW_P2PS_DEFAULT; 1149252190Srpaulo } 1150252190Srpaulo 1151281806Srpaulo if ((msg.conn_cap || msg.persistent_dev) && 1152281806Srpaulo (status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED) && 1153281806Srpaulo p2p->p2ps_prov) { 1154281806Srpaulo if (p2p->cfg->p2ps_prov_complete) { 1155281806Srpaulo p2p->cfg->p2ps_prov_complete( 1156281806Srpaulo p2p->cfg->cb_ctx, status, sa, adv_mac, 1157281806Srpaulo p2p->p2ps_prov->session_mac, 1158281806Srpaulo group_mac, adv_id, p2p->p2ps_prov->session_id, 1159281806Srpaulo conncap, passwd_id, msg.persistent_ssid, 1160289549Srpaulo msg.persistent_ssid_len, 1, 0, NULL, 1161289549Srpaulo msg.feature_cap, msg.feature_cap_len); 1162281806Srpaulo } 1163289549Srpaulo p2ps_prov_free(p2p); 1164289549Srpaulo } else if (status != P2P_SC_SUCCESS && 1165289549Srpaulo status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && 1166289549Srpaulo status != P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) { 1167281806Srpaulo if (p2p->cfg->p2ps_prov_complete) 1168281806Srpaulo p2p->cfg->p2ps_prov_complete( 1169281806Srpaulo p2p->cfg->cb_ctx, status, sa, adv_mac, 1170281806Srpaulo p2p->p2ps_prov->session_mac, 1171281806Srpaulo group_mac, adv_id, p2p->p2ps_prov->session_id, 1172289549Srpaulo 0, 0, NULL, 0, 1, 0, NULL, NULL, 0); 1173289549Srpaulo p2ps_prov_free(p2p); 1174281806Srpaulo } 1175281806Srpaulo 1176281806Srpaulo if (status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) { 1177281806Srpaulo if (p2p->cfg->remove_stale_groups) { 1178281806Srpaulo p2p->cfg->remove_stale_groups(p2p->cfg->cb_ctx, 1179281806Srpaulo dev->info.p2p_device_addr, 1180281806Srpaulo NULL, NULL, 0); 1181281806Srpaulo } 1182281806Srpaulo 1183281806Srpaulo if (msg.session_info && msg.session_info_len) { 1184281806Srpaulo size_t info_len = msg.session_info_len; 1185281806Srpaulo char *deferred_sess_resp = os_malloc(2 * info_len + 1); 1186281806Srpaulo 1187281806Srpaulo if (!deferred_sess_resp) { 1188281806Srpaulo p2p_parse_free(&msg); 1189289549Srpaulo p2ps_prov_free(p2p); 1190281806Srpaulo goto out; 1191281806Srpaulo } 1192281806Srpaulo utf8_escape((char *) msg.session_info, info_len, 1193281806Srpaulo deferred_sess_resp, 2 * info_len + 1); 1194281806Srpaulo 1195281806Srpaulo if (p2p->cfg->prov_disc_fail) 1196281806Srpaulo p2p->cfg->prov_disc_fail( 1197281806Srpaulo p2p->cfg->cb_ctx, sa, 1198281806Srpaulo P2P_PROV_DISC_INFO_UNAVAILABLE, 1199281806Srpaulo adv_id, adv_mac, 1200281806Srpaulo deferred_sess_resp); 1201281806Srpaulo os_free(deferred_sess_resp); 1202281806Srpaulo } else 1203281806Srpaulo if (p2p->cfg->prov_disc_fail) 1204281806Srpaulo p2p->cfg->prov_disc_fail( 1205281806Srpaulo p2p->cfg->cb_ctx, sa, 1206281806Srpaulo P2P_PROV_DISC_INFO_UNAVAILABLE, 1207281806Srpaulo adv_id, adv_mac, NULL); 1208289549Srpaulo } else if (status != P2P_SC_SUCCESS) { 1209281806Srpaulo p2p_dbg(p2p, "Peer rejected our Provision Discovery Request"); 1210281806Srpaulo if (p2p->cfg->prov_disc_fail) 1211281806Srpaulo p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa, 1212289549Srpaulo P2P_PROV_DISC_REJECTED, 1213289549Srpaulo adv_id, adv_mac, NULL); 1214281806Srpaulo p2p_parse_free(&msg); 1215289549Srpaulo p2ps_prov_free(p2p); 1216281806Srpaulo goto out; 1217281806Srpaulo } 1218281806Srpaulo 1219252190Srpaulo /* Store the provisioning info */ 1220252190Srpaulo dev->wps_prov_info = msg.wps_config_methods; 1221289549Srpaulo if (msg.intended_addr) 1222289549Srpaulo os_memcpy(dev->interface_addr, msg.intended_addr, ETH_ALEN); 1223252190Srpaulo 1224252190Srpaulo p2p_parse_free(&msg); 1225252190Srpaulo 1226252190Srpauloout: 1227252190Srpaulo dev->req_config_methods = 0; 1228252190Srpaulo p2p->cfg->send_action_done(p2p->cfg->cb_ctx); 1229252190Srpaulo if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG) { 1230281806Srpaulo p2p_dbg(p2p, "Start GO Neg after the PD-before-GO-Neg workaround with " 1231281806Srpaulo MACSTR, MAC2STR(dev->info.p2p_device_addr)); 1232252190Srpaulo dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG; 1233252190Srpaulo p2p_connect_send(p2p, dev); 1234252190Srpaulo return; 1235252190Srpaulo } 1236289549Srpaulo 1237289549Srpaulo /* 1238289549Srpaulo * prov_disc_resp callback is used to generate P2P-PROV-DISC-ENTER-PIN, 1239289549Srpaulo * P2P-PROV-DISC-SHOW-PIN, and P2P-PROV-DISC-PBC-REQ events. 1240289549Srpaulo * Call it only for a legacy P2P PD or for P2PS PD scenarios where 1241289549Srpaulo * show/enter PIN events are needed. 1242289549Srpaulo * 1243289549Srpaulo * The callback is called in the following cases: 1244289549Srpaulo * 1. Legacy P2P PD response with a status SUCCESS 1245289549Srpaulo * 2. P2PS, advertiser method: DISPLAY, autoaccept: true, 1246289549Srpaulo * response status: SUCCESS, local method KEYPAD 1247289549Srpaulo * 3. P2PS, advertiser method: KEYPAD,Seeker side, 1248289549Srpaulo * response status: INFO_CURRENTLY_UNAVAILABLE, 1249289549Srpaulo * local method: DISPLAY 1250289549Srpaulo */ 1251289549Srpaulo if (p2p->cfg->prov_disc_resp && 1252289549Srpaulo ((status == P2P_SC_SUCCESS && !adv_id) || 1253289549Srpaulo (p2ps_seeker && status == P2P_SC_SUCCESS && 1254289549Srpaulo passwd_id == DEV_PW_REGISTRAR_SPECIFIED) || 1255289549Srpaulo (p2ps_seeker && 1256289549Srpaulo status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE && 1257289549Srpaulo passwd_id == DEV_PW_USER_SPECIFIED))) 1258252190Srpaulo p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa, 1259252190Srpaulo report_config_methods); 1260281806Srpaulo 1261281806Srpaulo if (p2p->state == P2P_PD_DURING_FIND) { 1262281806Srpaulo p2p_clear_timeout(p2p); 1263281806Srpaulo p2p_continue_find(p2p); 1264281806Srpaulo } 1265252190Srpaulo} 1266252190Srpaulo 1267252190Srpaulo 1268252190Srpauloint p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev, 1269252190Srpaulo int join, int force_freq) 1270252190Srpaulo{ 1271252190Srpaulo struct wpabuf *req; 1272252190Srpaulo int freq; 1273252190Srpaulo 1274252190Srpaulo if (force_freq > 0) 1275252190Srpaulo freq = force_freq; 1276252190Srpaulo else 1277252190Srpaulo freq = dev->listen_freq > 0 ? dev->listen_freq : 1278252190Srpaulo dev->oper_freq; 1279252190Srpaulo if (freq <= 0) { 1280281806Srpaulo p2p_dbg(p2p, "No Listen/Operating frequency known for the peer " 1281281806Srpaulo MACSTR " to send Provision Discovery Request", 1282252190Srpaulo MAC2STR(dev->info.p2p_device_addr)); 1283252190Srpaulo return -1; 1284252190Srpaulo } 1285252190Srpaulo 1286252190Srpaulo if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) { 1287252190Srpaulo if (!(dev->info.dev_capab & 1288252190Srpaulo P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) { 1289281806Srpaulo p2p_dbg(p2p, "Cannot use PD with P2P Device " MACSTR 1290252190Srpaulo " that is in a group and is not discoverable", 1291252190Srpaulo MAC2STR(dev->info.p2p_device_addr)); 1292252190Srpaulo return -1; 1293252190Srpaulo } 1294252190Srpaulo /* TODO: use device discoverability request through GO */ 1295252190Srpaulo } 1296252190Srpaulo 1297281806Srpaulo if (p2p->p2ps_prov) { 1298281806Srpaulo if (p2p->p2ps_prov->status == P2P_SC_SUCCESS_DEFERRED) { 1299281806Srpaulo if (p2p->p2ps_prov->method == WPS_CONFIG_DISPLAY) 1300281806Srpaulo dev->req_config_methods = WPS_CONFIG_KEYPAD; 1301281806Srpaulo else if (p2p->p2ps_prov->method == WPS_CONFIG_KEYPAD) 1302281806Srpaulo dev->req_config_methods = WPS_CONFIG_DISPLAY; 1303281806Srpaulo else 1304281806Srpaulo dev->req_config_methods = WPS_CONFIG_P2PS; 1305281806Srpaulo } else { 1306281806Srpaulo /* Order of preference, based on peer's capabilities */ 1307281806Srpaulo if (p2p->p2ps_prov->method) 1308281806Srpaulo dev->req_config_methods = 1309281806Srpaulo p2p->p2ps_prov->method; 1310281806Srpaulo else if (dev->info.config_methods & WPS_CONFIG_P2PS) 1311281806Srpaulo dev->req_config_methods = WPS_CONFIG_P2PS; 1312281806Srpaulo else if (dev->info.config_methods & WPS_CONFIG_DISPLAY) 1313281806Srpaulo dev->req_config_methods = WPS_CONFIG_DISPLAY; 1314281806Srpaulo else 1315281806Srpaulo dev->req_config_methods = WPS_CONFIG_KEYPAD; 1316281806Srpaulo } 1317281806Srpaulo p2p_dbg(p2p, 1318281806Srpaulo "Building PD Request based on P2PS config method 0x%x status %d --> req_config_methods 0x%x", 1319281806Srpaulo p2p->p2ps_prov->method, p2p->p2ps_prov->status, 1320281806Srpaulo dev->req_config_methods); 1321281806Srpaulo } 1322281806Srpaulo 1323281806Srpaulo req = p2p_build_prov_disc_req(p2p, dev, join); 1324252190Srpaulo if (req == NULL) 1325252190Srpaulo return -1; 1326252190Srpaulo 1327252190Srpaulo if (p2p->state != P2P_IDLE) 1328252190Srpaulo p2p_stop_listen_for_freq(p2p, freq); 1329252190Srpaulo p2p->pending_action_state = P2P_PENDING_PD; 1330252190Srpaulo if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr, 1331252190Srpaulo p2p->cfg->dev_addr, dev->info.p2p_device_addr, 1332252190Srpaulo wpabuf_head(req), wpabuf_len(req), 200) < 0) { 1333281806Srpaulo p2p_dbg(p2p, "Failed to send Action frame"); 1334252190Srpaulo wpabuf_free(req); 1335252190Srpaulo return -1; 1336252190Srpaulo } 1337252190Srpaulo 1338252190Srpaulo os_memcpy(p2p->pending_pd_devaddr, dev->info.p2p_device_addr, ETH_ALEN); 1339252190Srpaulo 1340252190Srpaulo wpabuf_free(req); 1341252190Srpaulo return 0; 1342252190Srpaulo} 1343252190Srpaulo 1344252190Srpaulo 1345252190Srpauloint p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr, 1346281806Srpaulo struct p2ps_provision *p2ps_prov, 1347252190Srpaulo u16 config_methods, int join, int force_freq, 1348252190Srpaulo int user_initiated_pd) 1349252190Srpaulo{ 1350252190Srpaulo struct p2p_device *dev; 1351252190Srpaulo 1352252190Srpaulo dev = p2p_get_device(p2p, peer_addr); 1353252190Srpaulo if (dev == NULL) 1354252190Srpaulo dev = p2p_get_device_interface(p2p, peer_addr); 1355252190Srpaulo if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) { 1356281806Srpaulo p2p_dbg(p2p, "Provision Discovery Request destination " MACSTR 1357252190Srpaulo " not yet known", MAC2STR(peer_addr)); 1358281806Srpaulo os_free(p2ps_prov); 1359252190Srpaulo return -1; 1360252190Srpaulo } 1361252190Srpaulo 1362281806Srpaulo p2p_dbg(p2p, "Provision Discovery Request with " MACSTR 1363281806Srpaulo " (config methods 0x%x)", 1364252190Srpaulo MAC2STR(peer_addr), config_methods); 1365281806Srpaulo if (config_methods == 0 && !p2ps_prov) { 1366281806Srpaulo os_free(p2ps_prov); 1367252190Srpaulo return -1; 1368281806Srpaulo } 1369252190Srpaulo 1370281806Srpaulo if (p2ps_prov && p2ps_prov->status == P2P_SC_SUCCESS_DEFERRED && 1371281806Srpaulo p2p->p2ps_prov) { 1372281806Srpaulo /* Use cached method from deferred provisioning */ 1373281806Srpaulo p2ps_prov->method = p2p->p2ps_prov->method; 1374281806Srpaulo } 1375281806Srpaulo 1376252190Srpaulo /* Reset provisioning info */ 1377252190Srpaulo dev->wps_prov_info = 0; 1378289549Srpaulo p2ps_prov_free(p2p); 1379281806Srpaulo p2p->p2ps_prov = p2ps_prov; 1380252190Srpaulo 1381252190Srpaulo dev->req_config_methods = config_methods; 1382252190Srpaulo if (join) 1383252190Srpaulo dev->flags |= P2P_DEV_PD_FOR_JOIN; 1384252190Srpaulo else 1385252190Srpaulo dev->flags &= ~P2P_DEV_PD_FOR_JOIN; 1386252190Srpaulo 1387252190Srpaulo if (p2p->state != P2P_IDLE && p2p->state != P2P_SEARCH && 1388252190Srpaulo p2p->state != P2P_LISTEN_ONLY) { 1389281806Srpaulo p2p_dbg(p2p, "Busy with other operations; postpone Provision Discovery Request with " 1390281806Srpaulo MACSTR " (config methods 0x%x)", 1391252190Srpaulo MAC2STR(peer_addr), config_methods); 1392252190Srpaulo return 0; 1393252190Srpaulo } 1394252190Srpaulo 1395252190Srpaulo p2p->user_initiated_pd = user_initiated_pd; 1396281806Srpaulo p2p->pd_force_freq = force_freq; 1397252190Srpaulo 1398252190Srpaulo if (p2p->user_initiated_pd) 1399252190Srpaulo p2p->pd_retries = MAX_PROV_DISC_REQ_RETRIES; 1400252190Srpaulo 1401252190Srpaulo /* 1402252190Srpaulo * Assign dialog token here to use the same value in each retry within 1403252190Srpaulo * the same PD exchange. 1404252190Srpaulo */ 1405252190Srpaulo dev->dialog_token++; 1406252190Srpaulo if (dev->dialog_token == 0) 1407252190Srpaulo dev->dialog_token = 1; 1408252190Srpaulo 1409252190Srpaulo return p2p_send_prov_disc_req(p2p, dev, join, force_freq); 1410252190Srpaulo} 1411252190Srpaulo 1412252190Srpaulo 1413252190Srpaulovoid p2p_reset_pending_pd(struct p2p_data *p2p) 1414252190Srpaulo{ 1415252190Srpaulo struct p2p_device *dev; 1416252190Srpaulo 1417252190Srpaulo dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) { 1418252190Srpaulo if (os_memcmp(p2p->pending_pd_devaddr, 1419252190Srpaulo dev->info.p2p_device_addr, ETH_ALEN)) 1420252190Srpaulo continue; 1421252190Srpaulo if (!dev->req_config_methods) 1422252190Srpaulo continue; 1423252190Srpaulo if (dev->flags & P2P_DEV_PD_FOR_JOIN) 1424252190Srpaulo continue; 1425252190Srpaulo /* Reset the config methods of the device */ 1426252190Srpaulo dev->req_config_methods = 0; 1427252190Srpaulo } 1428252190Srpaulo 1429252190Srpaulo p2p->user_initiated_pd = 0; 1430252190Srpaulo os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN); 1431252190Srpaulo p2p->pd_retries = 0; 1432281806Srpaulo p2p->pd_force_freq = 0; 1433252190Srpaulo} 1434289549Srpaulo 1435289549Srpaulo 1436289549Srpaulovoid p2ps_prov_free(struct p2p_data *p2p) 1437289549Srpaulo{ 1438289549Srpaulo os_free(p2p->p2ps_prov); 1439289549Srpaulo p2p->p2ps_prov = NULL; 1440289549Srpaulo} 1441