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