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