Branch data Line data Source code
1 : : /*
2 : : * Wi-Fi Direct - P2P provision discovery
3 : : * Copyright (c) 2009-2010, Atheros Communications
4 : : *
5 : : * This software may be distributed under the terms of the BSD license.
6 : : * See README for more details.
7 : : */
8 : :
9 : : #include "includes.h"
10 : :
11 : : #include "common.h"
12 : : #include "common/ieee802_11_defs.h"
13 : : #include "wps/wps_defs.h"
14 : : #include "p2p_i.h"
15 : : #include "p2p.h"
16 : :
17 : :
18 : : /*
19 : : * Number of retries to attempt for provision discovery requests
20 : : * in case the peer is not listening.
21 : : */
22 : : #define MAX_PROV_DISC_REQ_RETRIES 120
23 : :
24 : :
25 : 45 : static void p2p_build_wps_ie_config_methods(struct wpabuf *buf,
26 : : u16 config_methods)
27 : : {
28 : : u8 *len;
29 : 45 : wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
30 : 45 : len = wpabuf_put(buf, 1);
31 : 45 : wpabuf_put_be32(buf, WPS_DEV_OUI_WFA);
32 : :
33 : : /* Config Methods */
34 : 45 : wpabuf_put_be16(buf, ATTR_CONFIG_METHODS);
35 : 45 : wpabuf_put_be16(buf, 2);
36 : 45 : wpabuf_put_be16(buf, config_methods);
37 : :
38 : 45 : p2p_buf_update_ie_hdr(buf, len);
39 : 45 : }
40 : :
41 : :
42 : 24 : static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p,
43 : : u8 dialog_token,
44 : : u16 config_methods,
45 : : struct p2p_device *go)
46 : : {
47 : : struct wpabuf *buf;
48 : : u8 *len;
49 : 24 : size_t extra = 0;
50 : :
51 : : #ifdef CONFIG_WIFI_DISPLAY
52 [ + + ]: 24 : if (p2p->wfd_ie_prov_disc_req)
53 : 22 : extra = wpabuf_len(p2p->wfd_ie_prov_disc_req);
54 : : #endif /* CONFIG_WIFI_DISPLAY */
55 : :
56 : 24 : buf = wpabuf_alloc(1000 + extra);
57 [ - + ]: 24 : if (buf == NULL)
58 : 0 : return NULL;
59 : :
60 : 24 : p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_REQ, dialog_token);
61 : :
62 : 24 : len = p2p_buf_add_ie_hdr(buf);
63 : 24 : p2p_buf_add_capability(buf, p2p->dev_capab &
64 : : ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
65 : 24 : p2p_buf_add_device_info(buf, p2p, NULL);
66 [ + + ]: 24 : if (go) {
67 : 18 : p2p_buf_add_group_id(buf, go->info.p2p_device_addr,
68 : 18 : go->oper_ssid, go->oper_ssid_len);
69 : : }
70 : 24 : p2p_buf_update_ie_hdr(buf, len);
71 : :
72 : : /* WPS IE with Config Methods attribute */
73 : 24 : p2p_build_wps_ie_config_methods(buf, config_methods);
74 : :
75 : : #ifdef CONFIG_WIFI_DISPLAY
76 [ + + ]: 24 : if (p2p->wfd_ie_prov_disc_req)
77 : 22 : wpabuf_put_buf(buf, p2p->wfd_ie_prov_disc_req);
78 : : #endif /* CONFIG_WIFI_DISPLAY */
79 : :
80 : 24 : return buf;
81 : : }
82 : :
83 : :
84 : 21 : static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p,
85 : : u8 dialog_token,
86 : : u16 config_methods,
87 : : const u8 *group_id,
88 : : size_t group_id_len)
89 : : {
90 : : struct wpabuf *buf;
91 : 21 : size_t extra = 0;
92 : :
93 : : #ifdef CONFIG_WIFI_DISPLAY
94 : 21 : struct wpabuf *wfd_ie = p2p->wfd_ie_prov_disc_resp;
95 [ + + ][ + + ]: 21 : if (wfd_ie && group_id) {
96 : : size_t i;
97 [ + - ]: 16 : for (i = 0; i < p2p->num_groups; i++) {
98 : 16 : struct p2p_group *g = p2p->groups[i];
99 : : struct wpabuf *ie;
100 [ - + ]: 16 : if (!p2p_group_is_group_id_match(g, group_id,
101 : : group_id_len))
102 : 0 : continue;
103 : 16 : ie = p2p_group_get_wfd_ie(g);
104 [ + - ]: 16 : if (ie) {
105 : 16 : wfd_ie = ie;
106 : 16 : break;
107 : : }
108 : : }
109 : : }
110 [ + + ]: 21 : if (wfd_ie)
111 : 19 : extra = wpabuf_len(wfd_ie);
112 : : #endif /* CONFIG_WIFI_DISPLAY */
113 : :
114 : 21 : buf = wpabuf_alloc(100 + extra);
115 [ - + ]: 21 : if (buf == NULL)
116 : 0 : return NULL;
117 : :
118 : 21 : p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_RESP, dialog_token);
119 : :
120 : : /* WPS IE with Config Methods attribute */
121 : 21 : p2p_build_wps_ie_config_methods(buf, config_methods);
122 : :
123 : : #ifdef CONFIG_WIFI_DISPLAY
124 [ + + ]: 21 : if (wfd_ie)
125 : 19 : wpabuf_put_buf(buf, wfd_ie);
126 : : #endif /* CONFIG_WIFI_DISPLAY */
127 : :
128 : 21 : return buf;
129 : : }
130 : :
131 : :
132 : 21 : void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
133 : : const u8 *data, size_t len, int rx_freq)
134 : : {
135 : : struct p2p_message msg;
136 : : struct p2p_device *dev;
137 : : int freq;
138 : 21 : int reject = 1;
139 : : struct wpabuf *resp;
140 : :
141 [ - + ]: 21 : if (p2p_parse(data, len, &msg))
142 : 0 : return;
143 : :
144 : 21 : p2p_dbg(p2p, "Received Provision Discovery Request from " MACSTR
145 : : " with config methods 0x%x (freq=%d)",
146 : 147 : MAC2STR(sa), msg.wps_config_methods, rx_freq);
147 : :
148 : 21 : dev = p2p_get_device(p2p, sa);
149 [ + - ][ + + ]: 21 : if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
150 : 15 : p2p_dbg(p2p, "Provision Discovery Request from unknown peer "
151 : 90 : MACSTR, MAC2STR(sa));
152 : :
153 [ - + ]: 15 : if (p2p_add_device(p2p, sa, rx_freq, NULL, 0, data + 1, len - 1,
154 : : 0)) {
155 : 0 : p2p_dbg(p2p, "Provision Discovery Request add device failed "
156 : 0 : MACSTR, MAC2STR(sa));
157 : : }
158 [ + - ]: 6 : } else if (msg.wfd_subelems) {
159 : 6 : wpabuf_free(dev->info.wfd_subelems);
160 : 6 : dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
161 : : }
162 : :
163 [ - + ]: 21 : if (!(msg.wps_config_methods &
164 : : (WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD |
165 : : WPS_CONFIG_PUSHBUTTON))) {
166 : 0 : p2p_dbg(p2p, "Unsupported Config Methods in Provision Discovery Request");
167 : 0 : goto out;
168 : : }
169 : :
170 [ + + ]: 21 : if (msg.group_id) {
171 : : size_t i;
172 [ + - ]: 18 : for (i = 0; i < p2p->num_groups; i++) {
173 [ + - ]: 18 : if (p2p_group_is_group_id_match(p2p->groups[i],
174 : : msg.group_id,
175 : : msg.group_id_len))
176 : 18 : break;
177 : : }
178 [ - + ]: 18 : if (i == p2p->num_groups) {
179 : 0 : p2p_dbg(p2p, "PD request for unknown P2P Group ID - reject");
180 : 0 : goto out;
181 : : }
182 : : }
183 : :
184 [ + - ]: 21 : if (dev)
185 : 21 : dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
186 : : P2P_DEV_PD_PEER_KEYPAD);
187 [ + + ]: 21 : if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) {
188 : 19 : p2p_dbg(p2p, "Peer " MACSTR
189 : 114 : " requested us to show a PIN on display", MAC2STR(sa));
190 [ + - ]: 19 : if (dev)
191 : 19 : dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
192 [ + + ]: 2 : } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
193 : 1 : p2p_dbg(p2p, "Peer " MACSTR
194 : : " requested us to write its PIN using keypad",
195 : 6 : MAC2STR(sa));
196 [ + - ]: 1 : if (dev)
197 : 1 : dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
198 : : }
199 : :
200 : 21 : reject = 0;
201 : :
202 : : out:
203 [ + - ]: 21 : resp = p2p_build_prov_disc_resp(p2p, msg.dialog_token,
204 : 21 : reject ? 0 : msg.wps_config_methods,
205 : : msg.group_id, msg.group_id_len);
206 [ - + ]: 21 : if (resp == NULL) {
207 : 0 : p2p_parse_free(&msg);
208 : 0 : return;
209 : : }
210 : 21 : p2p_dbg(p2p, "Sending Provision Discovery Response");
211 [ + - ]: 21 : if (rx_freq > 0)
212 : 21 : freq = rx_freq;
213 : : else
214 : 0 : freq = p2p_channel_to_freq(p2p->cfg->reg_class,
215 : 0 : p2p->cfg->channel);
216 [ - + ]: 21 : if (freq < 0) {
217 : 0 : p2p_dbg(p2p, "Unknown regulatory class/channel");
218 : 0 : wpabuf_free(resp);
219 : 0 : p2p_parse_free(&msg);
220 : 0 : return;
221 : : }
222 : 21 : p2p->pending_action_state = P2P_NO_PENDING_ACTION;
223 [ - + ]: 42 : if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr,
224 : 21 : p2p->cfg->dev_addr,
225 : 21 : wpabuf_head(resp), wpabuf_len(resp), 200) < 0) {
226 : 0 : p2p_dbg(p2p, "Failed to send Action frame");
227 : : }
228 : :
229 : 21 : wpabuf_free(resp);
230 : :
231 [ + - ][ + - ]: 21 : if (!reject && p2p->cfg->prov_disc_req) {
232 : 21 : const u8 *dev_addr = sa;
233 [ + - ]: 21 : if (msg.p2p_device_addr)
234 : 21 : dev_addr = msg.p2p_device_addr;
235 [ + - ][ + - ]: 21 : p2p->cfg->prov_disc_req(p2p->cfg->cb_ctx, sa,
236 : 21 : msg.wps_config_methods,
237 : : dev_addr, msg.pri_dev_type,
238 : 21 : msg.device_name, msg.config_methods,
239 : 42 : msg.capability ? msg.capability[0] : 0,
240 : 42 : msg.capability ? msg.capability[1] :
241 : : 0,
242 : : msg.group_id, msg.group_id_len);
243 : : }
244 : 21 : p2p_parse_free(&msg);
245 : : }
246 : :
247 : :
248 : 21 : void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa,
249 : : const u8 *data, size_t len)
250 : : {
251 : : struct p2p_message msg;
252 : : struct p2p_device *dev;
253 : 21 : u16 report_config_methods = 0, req_config_methods;
254 : 21 : int success = 0;
255 : :
256 [ - + ]: 21 : if (p2p_parse(data, len, &msg))
257 : 0 : return;
258 : :
259 : 21 : p2p_dbg(p2p, "Received Provision Discovery Response from " MACSTR
260 : : " with config methods 0x%x",
261 : 147 : MAC2STR(sa), msg.wps_config_methods);
262 : :
263 : 21 : dev = p2p_get_device(p2p, sa);
264 [ + - ][ - + ]: 21 : if (dev == NULL || !dev->req_config_methods) {
265 : 0 : p2p_dbg(p2p, "Ignore Provision Discovery Response from " MACSTR
266 : 0 : " with no pending request", MAC2STR(sa));
267 : 0 : p2p_parse_free(&msg);
268 : 0 : return;
269 : : }
270 : :
271 [ - + ]: 21 : if (dev->dialog_token != msg.dialog_token) {
272 : 0 : p2p_dbg(p2p, "Ignore Provision Discovery Response with unexpected Dialog Token %u (expected %u)",
273 : 0 : msg.dialog_token, dev->dialog_token);
274 : 0 : p2p_parse_free(&msg);
275 : 0 : return;
276 : : }
277 : :
278 [ + + ]: 21 : if (p2p->pending_action_state == P2P_PENDING_PD) {
279 : 20 : os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN);
280 : 20 : p2p->pending_action_state = P2P_NO_PENDING_ACTION;
281 : : }
282 : :
283 : : /*
284 : : * Use a local copy of the requested config methods since
285 : : * p2p_reset_pending_pd() can clear this in the peer entry.
286 : : */
287 : 21 : req_config_methods = dev->req_config_methods;
288 : :
289 : : /*
290 : : * If the response is from the peer to whom a user initiated request
291 : : * was sent earlier, we reset that state info here.
292 : : */
293 [ + + ][ - + ]: 21 : if (p2p->user_initiated_pd &&
294 : 20 : os_memcmp(p2p->pending_pd_devaddr, sa, ETH_ALEN) == 0)
295 : 0 : p2p_reset_pending_pd(p2p);
296 : :
297 [ - + ]: 21 : if (msg.wps_config_methods != req_config_methods) {
298 : 0 : p2p_dbg(p2p, "Peer rejected our Provision Discovery Request (received config_methods 0x%x expected 0x%x",
299 : 0 : msg.wps_config_methods, req_config_methods);
300 [ # # ]: 0 : if (p2p->cfg->prov_disc_fail)
301 : 0 : p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa,
302 : : P2P_PROV_DISC_REJECTED);
303 : 0 : p2p_parse_free(&msg);
304 : 0 : goto out;
305 : : }
306 : :
307 : 21 : report_config_methods = req_config_methods;
308 : 21 : dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
309 : : P2P_DEV_PD_PEER_KEYPAD);
310 [ + + ]: 21 : if (req_config_methods & WPS_CONFIG_DISPLAY) {
311 : 19 : p2p_dbg(p2p, "Peer " MACSTR
312 : 114 : " accepted to show a PIN on display", MAC2STR(sa));
313 : 19 : dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
314 [ + + ]: 2 : } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
315 : 1 : p2p_dbg(p2p, "Peer " MACSTR
316 : : " accepted to write our PIN using keypad",
317 : 6 : MAC2STR(sa));
318 : 1 : dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
319 : : }
320 : :
321 : : /* Store the provisioning info */
322 : 21 : dev->wps_prov_info = msg.wps_config_methods;
323 : :
324 : 21 : p2p_parse_free(&msg);
325 : 21 : success = 1;
326 : :
327 : : out:
328 : 21 : dev->req_config_methods = 0;
329 : 21 : p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
330 [ - + ]: 21 : if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG) {
331 : 0 : p2p_dbg(p2p, "Start GO Neg after the PD-before-GO-Neg workaround with "
332 : 0 : MACSTR, MAC2STR(dev->info.p2p_device_addr));
333 : 0 : dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG;
334 : 0 : p2p_connect_send(p2p, dev);
335 : 0 : return;
336 : : }
337 [ + - ][ + - ]: 21 : if (success && p2p->cfg->prov_disc_resp)
338 : 21 : p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa,
339 : : report_config_methods);
340 : :
341 [ + + ]: 21 : if (p2p->state == P2P_PD_DURING_FIND) {
342 : 3 : p2p_clear_timeout(p2p);
343 : 21 : p2p_continue_find(p2p);
344 : : }
345 : : }
346 : :
347 : :
348 : 24 : int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev,
349 : : int join, int force_freq)
350 : : {
351 : : struct wpabuf *req;
352 : : int freq;
353 : :
354 [ + + ]: 24 : if (force_freq > 0)
355 : 18 : freq = force_freq;
356 : : else
357 [ + - ]: 6 : freq = dev->listen_freq > 0 ? dev->listen_freq :
358 : : dev->oper_freq;
359 [ - + ]: 24 : if (freq <= 0) {
360 : 0 : p2p_dbg(p2p, "No Listen/Operating frequency known for the peer "
361 : : MACSTR " to send Provision Discovery Request",
362 : 0 : MAC2STR(dev->info.p2p_device_addr));
363 : 0 : return -1;
364 : : }
365 : :
366 [ - + ]: 24 : if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
367 [ # # ]: 0 : if (!(dev->info.dev_capab &
368 : : P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
369 : 0 : p2p_dbg(p2p, "Cannot use PD with P2P Device " MACSTR
370 : : " that is in a group and is not discoverable",
371 : 0 : MAC2STR(dev->info.p2p_device_addr));
372 : 0 : return -1;
373 : : }
374 : : /* TODO: use device discoverability request through GO */
375 : : }
376 : :
377 [ + + ]: 24 : req = p2p_build_prov_disc_req(p2p, dev->dialog_token,
378 : 24 : dev->req_config_methods,
379 : : join ? dev : NULL);
380 [ - + ]: 24 : if (req == NULL)
381 : 0 : return -1;
382 : :
383 [ + + ]: 24 : if (p2p->state != P2P_IDLE)
384 : 6 : p2p_stop_listen_for_freq(p2p, freq);
385 : 24 : p2p->pending_action_state = P2P_PENDING_PD;
386 [ - + ]: 48 : if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
387 : 24 : p2p->cfg->dev_addr, dev->info.p2p_device_addr,
388 : 24 : wpabuf_head(req), wpabuf_len(req), 200) < 0) {
389 : 0 : p2p_dbg(p2p, "Failed to send Action frame");
390 : 0 : wpabuf_free(req);
391 : 0 : return -1;
392 : : }
393 : :
394 : 24 : os_memcpy(p2p->pending_pd_devaddr, dev->info.p2p_device_addr, ETH_ALEN);
395 : :
396 : 24 : wpabuf_free(req);
397 : 24 : return 0;
398 : : }
399 : :
400 : :
401 : 21 : int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr,
402 : : u16 config_methods, int join, int force_freq,
403 : : int user_initiated_pd)
404 : : {
405 : : struct p2p_device *dev;
406 : :
407 : 21 : dev = p2p_get_device(p2p, peer_addr);
408 [ - + ]: 21 : if (dev == NULL)
409 : 0 : dev = p2p_get_device_interface(p2p, peer_addr);
410 [ + - ][ - + ]: 21 : if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
411 : 0 : p2p_dbg(p2p, "Provision Discovery Request destination " MACSTR
412 : 0 : " not yet known", MAC2STR(peer_addr));
413 : 0 : return -1;
414 : : }
415 : :
416 : 21 : p2p_dbg(p2p, "Provision Discovery Request with " MACSTR
417 : : " (config methods 0x%x)",
418 : 126 : MAC2STR(peer_addr), config_methods);
419 [ - + ]: 21 : if (config_methods == 0)
420 : 0 : return -1;
421 : :
422 : : /* Reset provisioning info */
423 : 21 : dev->wps_prov_info = 0;
424 : :
425 : 21 : dev->req_config_methods = config_methods;
426 [ + + ]: 21 : if (join)
427 : 18 : dev->flags |= P2P_DEV_PD_FOR_JOIN;
428 : : else
429 : 3 : dev->flags &= ~P2P_DEV_PD_FOR_JOIN;
430 : :
431 [ + + ][ - + ]: 21 : if (p2p->state != P2P_IDLE && p2p->state != P2P_SEARCH &&
[ # # ]
432 : 0 : p2p->state != P2P_LISTEN_ONLY) {
433 : 0 : p2p_dbg(p2p, "Busy with other operations; postpone Provision Discovery Request with "
434 : : MACSTR " (config methods 0x%x)",
435 : 0 : MAC2STR(peer_addr), config_methods);
436 : 0 : return 0;
437 : : }
438 : :
439 : 21 : p2p->user_initiated_pd = user_initiated_pd;
440 : 21 : p2p->pd_force_freq = force_freq;
441 : :
442 [ + + ]: 21 : if (p2p->user_initiated_pd)
443 : 20 : p2p->pd_retries = MAX_PROV_DISC_REQ_RETRIES;
444 : :
445 : : /*
446 : : * Assign dialog token here to use the same value in each retry within
447 : : * the same PD exchange.
448 : : */
449 : 21 : dev->dialog_token++;
450 [ - + ]: 21 : if (dev->dialog_token == 0)
451 : 0 : dev->dialog_token = 1;
452 : :
453 : 21 : return p2p_send_prov_disc_req(p2p, dev, join, force_freq);
454 : : }
455 : :
456 : :
457 : 0 : void p2p_reset_pending_pd(struct p2p_data *p2p)
458 : : {
459 : : struct p2p_device *dev;
460 : :
461 [ # # ]: 0 : dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
462 [ # # ]: 0 : if (os_memcmp(p2p->pending_pd_devaddr,
463 : : dev->info.p2p_device_addr, ETH_ALEN))
464 : 0 : continue;
465 [ # # ]: 0 : if (!dev->req_config_methods)
466 : 0 : continue;
467 [ # # ]: 0 : if (dev->flags & P2P_DEV_PD_FOR_JOIN)
468 : 0 : continue;
469 : : /* Reset the config methods of the device */
470 : 0 : dev->req_config_methods = 0;
471 : : }
472 : :
473 : 0 : p2p->user_initiated_pd = 0;
474 : 0 : os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN);
475 : 0 : p2p->pd_retries = 0;
476 : 0 : p2p->pd_force_freq = 0;
477 : 0 : }
|