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