1252190Srpaulo/* 2252190Srpaulo * Wi-Fi Direct - P2P Device Discoverability procedure 3252190Srpaulo * Copyright (c) 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" 13252190Srpaulo#include "p2p_i.h" 14252190Srpaulo#include "p2p.h" 15252190Srpaulo 16252190Srpaulo 17252190Srpaulostatic struct wpabuf * p2p_build_dev_disc_req(struct p2p_data *p2p, 18252190Srpaulo struct p2p_device *go, 19252190Srpaulo const u8 *dev_id) 20252190Srpaulo{ 21252190Srpaulo struct wpabuf *buf; 22252190Srpaulo u8 *len; 23252190Srpaulo 24252190Srpaulo buf = wpabuf_alloc(100); 25252190Srpaulo if (buf == NULL) 26252190Srpaulo return NULL; 27252190Srpaulo 28252190Srpaulo go->dialog_token++; 29252190Srpaulo if (go->dialog_token == 0) 30252190Srpaulo go->dialog_token = 1; 31252190Srpaulo p2p_buf_add_public_action_hdr(buf, P2P_DEV_DISC_REQ, go->dialog_token); 32252190Srpaulo 33252190Srpaulo len = p2p_buf_add_ie_hdr(buf); 34252190Srpaulo p2p_buf_add_device_id(buf, dev_id); 35252190Srpaulo p2p_buf_add_group_id(buf, go->info.p2p_device_addr, go->oper_ssid, 36252190Srpaulo go->oper_ssid_len); 37252190Srpaulo p2p_buf_update_ie_hdr(buf, len); 38252190Srpaulo 39252190Srpaulo return buf; 40252190Srpaulo} 41252190Srpaulo 42252190Srpaulo 43252190Srpaulovoid p2p_dev_disc_req_cb(struct p2p_data *p2p, int success) 44252190Srpaulo{ 45252190Srpaulo wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, 46252190Srpaulo "P2P: Device Discoverability Request TX callback: success=%d", 47252190Srpaulo success); 48252190Srpaulo 49252190Srpaulo if (!success) { 50252190Srpaulo /* 51252190Srpaulo * Use P2P find, if needed, to find the other device or to 52252190Srpaulo * retry device discoverability. 53252190Srpaulo */ 54252190Srpaulo p2p_set_state(p2p, P2P_CONNECT); 55252190Srpaulo p2p_set_timeout(p2p, 0, 100000); 56252190Srpaulo return; 57252190Srpaulo } 58252190Srpaulo 59252190Srpaulo wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, 60252190Srpaulo "P2P: GO acknowledged Device Discoverability Request - wait " 61252190Srpaulo "for response"); 62252190Srpaulo /* 63252190Srpaulo * TODO: is the remain-on-channel from Action frame TX long enough for 64252190Srpaulo * most cases or should we try to increase its duration and/or start 65252190Srpaulo * another remain-on-channel if needed once the previous one expires? 66252190Srpaulo */ 67252190Srpaulo} 68252190Srpaulo 69252190Srpaulo 70252190Srpauloint p2p_send_dev_disc_req(struct p2p_data *p2p, struct p2p_device *dev) 71252190Srpaulo{ 72252190Srpaulo struct p2p_device *go; 73252190Srpaulo struct wpabuf *req; 74252190Srpaulo 75252190Srpaulo go = p2p_get_device(p2p, dev->member_in_go_dev); 76252190Srpaulo if (go == NULL || dev->oper_freq <= 0) { 77252190Srpaulo wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, 78252190Srpaulo "P2P: Could not find peer entry for GO and frequency " 79252190Srpaulo "to send Device Discoverability Request"); 80252190Srpaulo return -1; 81252190Srpaulo } 82252190Srpaulo 83252190Srpaulo req = p2p_build_dev_disc_req(p2p, go, dev->info.p2p_device_addr); 84252190Srpaulo if (req == NULL) 85252190Srpaulo return -1; 86252190Srpaulo 87252190Srpaulo wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, 88252190Srpaulo "P2P: Sending Device Discoverability Request to GO " MACSTR 89252190Srpaulo " for client " MACSTR, 90252190Srpaulo MAC2STR(go->info.p2p_device_addr), 91252190Srpaulo MAC2STR(dev->info.p2p_device_addr)); 92252190Srpaulo 93252190Srpaulo p2p->pending_client_disc_go = go; 94252190Srpaulo os_memcpy(p2p->pending_client_disc_addr, dev->info.p2p_device_addr, 95252190Srpaulo ETH_ALEN); 96252190Srpaulo p2p->pending_action_state = P2P_PENDING_DEV_DISC_REQUEST; 97252190Srpaulo if (p2p_send_action(p2p, dev->oper_freq, go->info.p2p_device_addr, 98252190Srpaulo p2p->cfg->dev_addr, go->info.p2p_device_addr, 99252190Srpaulo wpabuf_head(req), wpabuf_len(req), 1000) < 0) { 100252190Srpaulo wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, 101252190Srpaulo "P2P: Failed to send Action frame"); 102252190Srpaulo wpabuf_free(req); 103252190Srpaulo /* TODO: how to recover from failure? */ 104252190Srpaulo return -1; 105252190Srpaulo } 106252190Srpaulo 107252190Srpaulo wpabuf_free(req); 108252190Srpaulo 109252190Srpaulo return 0; 110252190Srpaulo} 111252190Srpaulo 112252190Srpaulo 113252190Srpaulostatic struct wpabuf * p2p_build_dev_disc_resp(u8 dialog_token, u8 status) 114252190Srpaulo{ 115252190Srpaulo struct wpabuf *buf; 116252190Srpaulo u8 *len; 117252190Srpaulo 118252190Srpaulo buf = wpabuf_alloc(100); 119252190Srpaulo if (buf == NULL) 120252190Srpaulo return NULL; 121252190Srpaulo 122252190Srpaulo p2p_buf_add_public_action_hdr(buf, P2P_DEV_DISC_RESP, dialog_token); 123252190Srpaulo 124252190Srpaulo len = p2p_buf_add_ie_hdr(buf); 125252190Srpaulo p2p_buf_add_status(buf, status); 126252190Srpaulo p2p_buf_update_ie_hdr(buf, len); 127252190Srpaulo 128252190Srpaulo return buf; 129252190Srpaulo} 130252190Srpaulo 131252190Srpaulo 132252190Srpaulovoid p2p_dev_disc_resp_cb(struct p2p_data *p2p, int success) 133252190Srpaulo{ 134252190Srpaulo wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, 135252190Srpaulo "P2P: Device Discoverability Response TX callback: success=%d", 136252190Srpaulo success); 137252190Srpaulo p2p->cfg->send_action_done(p2p->cfg->cb_ctx); 138252190Srpaulo} 139252190Srpaulo 140252190Srpaulo 141252190Srpaulostatic void p2p_send_dev_disc_resp(struct p2p_data *p2p, u8 dialog_token, 142252190Srpaulo const u8 *addr, int freq, u8 status) 143252190Srpaulo{ 144252190Srpaulo struct wpabuf *resp; 145252190Srpaulo 146252190Srpaulo resp = p2p_build_dev_disc_resp(dialog_token, status); 147252190Srpaulo if (resp == NULL) 148252190Srpaulo return; 149252190Srpaulo 150252190Srpaulo wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, 151252190Srpaulo "P2P: Sending Device Discoverability Response to " MACSTR 152252190Srpaulo " (status %u freq %d)", 153252190Srpaulo MAC2STR(addr), status, freq); 154252190Srpaulo 155252190Srpaulo p2p->pending_action_state = P2P_PENDING_DEV_DISC_RESPONSE; 156252190Srpaulo if (p2p_send_action(p2p, freq, addr, p2p->cfg->dev_addr, 157252190Srpaulo p2p->cfg->dev_addr, 158252190Srpaulo wpabuf_head(resp), wpabuf_len(resp), 200) < 0) { 159252190Srpaulo wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, 160252190Srpaulo "P2P: Failed to send Action frame"); 161252190Srpaulo } 162252190Srpaulo 163252190Srpaulo wpabuf_free(resp); 164252190Srpaulo} 165252190Srpaulo 166252190Srpaulo 167252190Srpaulovoid p2p_process_dev_disc_req(struct p2p_data *p2p, const u8 *sa, 168252190Srpaulo const u8 *data, size_t len, int rx_freq) 169252190Srpaulo{ 170252190Srpaulo struct p2p_message msg; 171252190Srpaulo size_t g; 172252190Srpaulo 173252190Srpaulo wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, 174252190Srpaulo "P2P: Received Device Discoverability Request from " MACSTR 175252190Srpaulo " (freq=%d)", MAC2STR(sa), rx_freq); 176252190Srpaulo 177252190Srpaulo if (p2p_parse(data, len, &msg)) 178252190Srpaulo return; 179252190Srpaulo 180252190Srpaulo if (msg.dialog_token == 0) { 181252190Srpaulo wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, 182252190Srpaulo "P2P: Invalid Dialog Token 0 (must be nonzero) in " 183252190Srpaulo "Device Discoverability Request"); 184252190Srpaulo p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq, 185252190Srpaulo P2P_SC_FAIL_INVALID_PARAMS); 186252190Srpaulo p2p_parse_free(&msg); 187252190Srpaulo return; 188252190Srpaulo } 189252190Srpaulo 190252190Srpaulo if (msg.device_id == NULL) { 191252190Srpaulo wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, 192252190Srpaulo "P2P: P2P Device ID attribute missing from Device " 193252190Srpaulo "Discoverability Request"); 194252190Srpaulo p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq, 195252190Srpaulo P2P_SC_FAIL_INVALID_PARAMS); 196252190Srpaulo p2p_parse_free(&msg); 197252190Srpaulo return; 198252190Srpaulo } 199252190Srpaulo 200252190Srpaulo for (g = 0; g < p2p->num_groups; g++) { 201252190Srpaulo if (p2p_group_go_discover(p2p->groups[g], msg.device_id, sa, 202252190Srpaulo rx_freq) == 0) { 203252190Srpaulo wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Scheduled " 204252190Srpaulo "GO Discoverability Request for the target " 205252190Srpaulo "device"); 206252190Srpaulo /* 207252190Srpaulo * P2P group code will use a callback to indicate TX 208252190Srpaulo * status, so that we can reply to the request once the 209252190Srpaulo * target client has acknowledged the request or it has 210252190Srpaulo * timed out. 211252190Srpaulo */ 212252190Srpaulo p2p->pending_dev_disc_dialog_token = msg.dialog_token; 213252190Srpaulo os_memcpy(p2p->pending_dev_disc_addr, sa, ETH_ALEN); 214252190Srpaulo p2p->pending_dev_disc_freq = rx_freq; 215252190Srpaulo p2p_parse_free(&msg); 216252190Srpaulo return; 217252190Srpaulo } 218252190Srpaulo } 219252190Srpaulo 220252190Srpaulo wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Requested client " 221252190Srpaulo "was not found in any group or did not support client " 222252190Srpaulo "discoverability"); 223252190Srpaulo p2p_send_dev_disc_resp(p2p, msg.dialog_token, sa, rx_freq, 224252190Srpaulo P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE); 225252190Srpaulo p2p_parse_free(&msg); 226252190Srpaulo} 227252190Srpaulo 228252190Srpaulo 229252190Srpaulovoid p2p_process_dev_disc_resp(struct p2p_data *p2p, const u8 *sa, 230252190Srpaulo const u8 *data, size_t len) 231252190Srpaulo{ 232252190Srpaulo struct p2p_message msg; 233252190Srpaulo struct p2p_device *go; 234252190Srpaulo u8 status; 235252190Srpaulo 236252190Srpaulo wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, 237252190Srpaulo "P2P: Received Device Discoverability Response from " MACSTR, 238252190Srpaulo MAC2STR(sa)); 239252190Srpaulo 240252190Srpaulo go = p2p->pending_client_disc_go; 241252190Srpaulo if (go == NULL || 242252190Srpaulo os_memcmp(sa, go->info.p2p_device_addr, ETH_ALEN) != 0) { 243252190Srpaulo wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Ignore unexpected " 244252190Srpaulo "Device Discoverability Response"); 245252190Srpaulo return; 246252190Srpaulo } 247252190Srpaulo 248252190Srpaulo if (p2p_parse(data, len, &msg)) 249252190Srpaulo return; 250252190Srpaulo 251252190Srpaulo if (msg.status == NULL) { 252252190Srpaulo p2p_parse_free(&msg); 253252190Srpaulo return; 254252190Srpaulo } 255252190Srpaulo 256252190Srpaulo if (msg.dialog_token != go->dialog_token) { 257252190Srpaulo wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Ignore Device " 258252190Srpaulo "Discoverability Response with unexpected dialog " 259252190Srpaulo "token %u (expected %u)", 260252190Srpaulo msg.dialog_token, go->dialog_token); 261252190Srpaulo p2p_parse_free(&msg); 262252190Srpaulo return; 263252190Srpaulo } 264252190Srpaulo 265252190Srpaulo status = *msg.status; 266252190Srpaulo p2p_parse_free(&msg); 267252190Srpaulo 268252190Srpaulo wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, 269252190Srpaulo "P2P: Device Discoverability Response status %u", status); 270252190Srpaulo 271252190Srpaulo if (p2p->go_neg_peer == NULL || 272252190Srpaulo os_memcmp(p2p->pending_client_disc_addr, 273252190Srpaulo p2p->go_neg_peer->info.p2p_device_addr, ETH_ALEN) != 0 || 274252190Srpaulo os_memcmp(p2p->go_neg_peer->member_in_go_dev, 275252190Srpaulo go->info.p2p_device_addr, ETH_ALEN) != 0) { 276252190Srpaulo wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending " 277252190Srpaulo "operation with the client discoverability peer " 278252190Srpaulo "anymore"); 279252190Srpaulo return; 280252190Srpaulo } 281252190Srpaulo 282252190Srpaulo if (status == 0) { 283252190Srpaulo /* 284252190Srpaulo * Peer is expected to be awake for at least 100 TU; try to 285252190Srpaulo * connect immediately. 286252190Srpaulo */ 287252190Srpaulo wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, 288252190Srpaulo "P2P: Client discoverability request succeeded"); 289252190Srpaulo if (p2p->state == P2P_CONNECT) { 290252190Srpaulo /* 291252190Srpaulo * Change state to force the timeout to start in 292252190Srpaulo * P2P_CONNECT again without going through the short 293252190Srpaulo * Listen state. 294252190Srpaulo */ 295252190Srpaulo p2p_set_state(p2p, P2P_CONNECT_LISTEN); 296252190Srpaulo p2p->cfg->send_action_done(p2p->cfg->cb_ctx); 297252190Srpaulo } 298252190Srpaulo p2p_set_timeout(p2p, 0, 0); 299252190Srpaulo } else { 300252190Srpaulo /* 301252190Srpaulo * Client discoverability request failed; try to connect from 302252190Srpaulo * timeout. 303252190Srpaulo */ 304252190Srpaulo wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, 305252190Srpaulo "P2P: Client discoverability request failed"); 306252190Srpaulo p2p_set_timeout(p2p, 0, 500000); 307252190Srpaulo } 308252190Srpaulo 309252190Srpaulo} 310252190Srpaulo 311252190Srpaulo 312252190Srpaulovoid p2p_go_disc_req_cb(struct p2p_data *p2p, int success) 313252190Srpaulo{ 314252190Srpaulo wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, 315252190Srpaulo "P2P: GO Discoverability Request TX callback: success=%d", 316252190Srpaulo success); 317252190Srpaulo p2p->cfg->send_action_done(p2p->cfg->cb_ctx); 318252190Srpaulo 319252190Srpaulo if (p2p->pending_dev_disc_dialog_token == 0) { 320252190Srpaulo wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: No pending Device " 321252190Srpaulo "Discoverability Request"); 322252190Srpaulo return; 323252190Srpaulo } 324252190Srpaulo 325252190Srpaulo p2p_send_dev_disc_resp(p2p, p2p->pending_dev_disc_dialog_token, 326252190Srpaulo p2p->pending_dev_disc_addr, 327252190Srpaulo p2p->pending_dev_disc_freq, 328252190Srpaulo success ? P2P_SC_SUCCESS : 329252190Srpaulo P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE); 330252190Srpaulo 331252190Srpaulo p2p->pending_dev_disc_dialog_token = 0; 332252190Srpaulo} 333252190Srpaulo 334252190Srpaulo 335252190Srpaulovoid p2p_process_go_disc_req(struct p2p_data *p2p, const u8 *da, const u8 *sa, 336252190Srpaulo const u8 *data, size_t len, int rx_freq) 337252190Srpaulo{ 338252190Srpaulo unsigned int tu; 339252190Srpaulo struct wpabuf *ies; 340252190Srpaulo 341252190Srpaulo wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, 342252190Srpaulo "P2P: Received GO Discoverability Request - remain awake for " 343252190Srpaulo "100 TU"); 344252190Srpaulo 345252190Srpaulo ies = p2p_build_probe_resp_ies(p2p); 346252190Srpaulo if (ies == NULL) 347252190Srpaulo return; 348252190Srpaulo 349252190Srpaulo /* Remain awake 100 TU on operating channel */ 350252190Srpaulo p2p->pending_client_disc_freq = rx_freq; 351252190Srpaulo tu = 100; 352252190Srpaulo if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, rx_freq, 1024 * tu / 1000, 353252190Srpaulo ies) < 0) { 354252190Srpaulo wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, 355252190Srpaulo "P2P: Failed to start listen mode for client " 356252190Srpaulo "discoverability"); 357252190Srpaulo } 358252190Srpaulo wpabuf_free(ies); 359252190Srpaulo} 360