xref: /freebsd/contrib/wpa/src/p2p/p2p_pd.c (revision a90b9d0159070121c221b966469c3e36d912bf82)
1f05cddf9SRui Paulo /*
2f05cddf9SRui Paulo  * Wi-Fi Direct - P2P provision discovery
3f05cddf9SRui Paulo  * Copyright (c) 2009-2010, Atheros Communications
4f05cddf9SRui Paulo  *
5f05cddf9SRui Paulo  * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo  * See README for more details.
7f05cddf9SRui Paulo  */
8f05cddf9SRui Paulo 
9f05cddf9SRui Paulo #include "includes.h"
10f05cddf9SRui Paulo 
11f05cddf9SRui Paulo #include "common.h"
12f05cddf9SRui Paulo #include "common/ieee802_11_defs.h"
135b9c547cSRui Paulo #include "common/wpa_ctrl.h"
14f05cddf9SRui Paulo #include "wps/wps_defs.h"
15f05cddf9SRui Paulo #include "p2p_i.h"
16f05cddf9SRui Paulo #include "p2p.h"
17f05cddf9SRui Paulo 
18f05cddf9SRui Paulo 
19f05cddf9SRui Paulo /*
20f05cddf9SRui Paulo  * Number of retries to attempt for provision discovery requests
21f05cddf9SRui Paulo  * in case the peer is not listening.
22f05cddf9SRui Paulo  */
23f05cddf9SRui Paulo #define MAX_PROV_DISC_REQ_RETRIES 120
24f05cddf9SRui Paulo 
25f05cddf9SRui Paulo 
p2p_build_wps_ie_config_methods(struct wpabuf * buf,u16 config_methods)26f05cddf9SRui Paulo static void p2p_build_wps_ie_config_methods(struct wpabuf *buf,
27f05cddf9SRui Paulo 					    u16 config_methods)
28f05cddf9SRui Paulo {
29f05cddf9SRui Paulo 	u8 *len;
30f05cddf9SRui Paulo 	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
31f05cddf9SRui Paulo 	len = wpabuf_put(buf, 1);
32f05cddf9SRui Paulo 	wpabuf_put_be32(buf, WPS_DEV_OUI_WFA);
33f05cddf9SRui Paulo 
34f05cddf9SRui Paulo 	/* Config Methods */
35f05cddf9SRui Paulo 	wpabuf_put_be16(buf, ATTR_CONFIG_METHODS);
36f05cddf9SRui Paulo 	wpabuf_put_be16(buf, 2);
37f05cddf9SRui Paulo 	wpabuf_put_be16(buf, config_methods);
38f05cddf9SRui Paulo 
39f05cddf9SRui Paulo 	p2p_buf_update_ie_hdr(buf, len);
40f05cddf9SRui Paulo }
41f05cddf9SRui Paulo 
42f05cddf9SRui Paulo 
p2ps_add_new_group_info(struct p2p_data * p2p,struct p2p_device * dev,struct wpabuf * buf)43780fb4a2SCy Schubert static void p2ps_add_new_group_info(struct p2p_data *p2p,
44780fb4a2SCy Schubert 				    struct p2p_device *dev,
45780fb4a2SCy Schubert 				    struct wpabuf *buf)
465b9c547cSRui Paulo {
475b9c547cSRui Paulo 	int found;
485b9c547cSRui Paulo 	u8 intended_addr[ETH_ALEN];
49325151a3SRui Paulo 	u8 ssid[SSID_MAX_LEN];
505b9c547cSRui Paulo 	size_t ssid_len;
515b9c547cSRui Paulo 	int group_iface;
52780fb4a2SCy Schubert 	unsigned int force_freq;
535b9c547cSRui Paulo 
545b9c547cSRui Paulo 	if (!p2p->cfg->get_go_info)
555b9c547cSRui Paulo 		return;
565b9c547cSRui Paulo 
575b9c547cSRui Paulo 	found = p2p->cfg->get_go_info(
585b9c547cSRui Paulo 		p2p->cfg->cb_ctx, intended_addr, ssid,
59780fb4a2SCy Schubert 		&ssid_len, &group_iface, &force_freq);
605b9c547cSRui Paulo 	if (found) {
61780fb4a2SCy Schubert 		if (force_freq > 0) {
62780fb4a2SCy Schubert 			p2p->p2ps_prov->force_freq = force_freq;
63780fb4a2SCy Schubert 			p2p->p2ps_prov->pref_freq = 0;
64780fb4a2SCy Schubert 
65780fb4a2SCy Schubert 			if (dev)
66780fb4a2SCy Schubert 				p2p_prepare_channel(p2p, dev, force_freq, 0, 0);
67780fb4a2SCy Schubert 		}
685b9c547cSRui Paulo 		p2p_buf_add_group_id(buf, p2p->cfg->dev_addr,
695b9c547cSRui Paulo 				     ssid, ssid_len);
70325151a3SRui Paulo 
71325151a3SRui Paulo 		if (group_iface)
72325151a3SRui Paulo 			p2p_buf_add_intended_addr(buf, p2p->intended_addr);
73325151a3SRui Paulo 		else
745b9c547cSRui Paulo 			p2p_buf_add_intended_addr(buf, intended_addr);
755b9c547cSRui Paulo 	} else {
765b9c547cSRui Paulo 		if (!p2p->ssid_set) {
775b9c547cSRui Paulo 			p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len);
785b9c547cSRui Paulo 			p2p->ssid_set = 1;
795b9c547cSRui Paulo 		}
805b9c547cSRui Paulo 
815b9c547cSRui Paulo 		/* Add pre-composed P2P Group ID */
825b9c547cSRui Paulo 		p2p_buf_add_group_id(buf, p2p->cfg->dev_addr,
835b9c547cSRui Paulo 				     p2p->ssid, p2p->ssid_len);
845b9c547cSRui Paulo 
855b9c547cSRui Paulo 		if (group_iface)
865b9c547cSRui Paulo 			p2p_buf_add_intended_addr(
875b9c547cSRui Paulo 				buf, p2p->intended_addr);
885b9c547cSRui Paulo 		else
895b9c547cSRui Paulo 			p2p_buf_add_intended_addr(
905b9c547cSRui Paulo 				buf, p2p->cfg->dev_addr);
915b9c547cSRui Paulo 	}
925b9c547cSRui Paulo }
935b9c547cSRui Paulo 
945b9c547cSRui Paulo 
p2ps_add_pd_req_attrs(struct p2p_data * p2p,struct p2p_device * dev,struct wpabuf * buf,u16 config_methods)955b9c547cSRui Paulo static void p2ps_add_pd_req_attrs(struct p2p_data *p2p, struct p2p_device *dev,
965b9c547cSRui Paulo 				  struct wpabuf *buf, u16 config_methods)
975b9c547cSRui Paulo {
985b9c547cSRui Paulo 	struct p2ps_provision *prov = p2p->p2ps_prov;
99325151a3SRui Paulo 	struct p2ps_feature_capab fcap = { prov->cpt_mask, 0 };
1005b9c547cSRui Paulo 	int shared_group = 0;
101325151a3SRui Paulo 	u8 ssid[SSID_MAX_LEN];
1025b9c547cSRui Paulo 	size_t ssid_len;
1035b9c547cSRui Paulo 	u8 go_dev_addr[ETH_ALEN];
104325151a3SRui Paulo 	u8 intended_addr[ETH_ALEN];
105780fb4a2SCy Schubert 	int follow_on_req_fail = prov->status >= 0 &&
106780fb4a2SCy Schubert 		prov->status != P2P_SC_SUCCESS_DEFERRED;
1075b9c547cSRui Paulo 
1085b9c547cSRui Paulo 	/* If we might be explicite group owner, add GO details */
109780fb4a2SCy Schubert 	if (!follow_on_req_fail &&
110780fb4a2SCy Schubert 	    (prov->conncap & (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW)))
111780fb4a2SCy Schubert 		p2ps_add_new_group_info(p2p, dev, buf);
1125b9c547cSRui Paulo 
1135b9c547cSRui Paulo 	if (prov->status >= 0)
1145b9c547cSRui Paulo 		p2p_buf_add_status(buf, (u8) prov->status);
1155b9c547cSRui Paulo 	else
1165b9c547cSRui Paulo 		prov->method = config_methods;
1175b9c547cSRui Paulo 
118780fb4a2SCy Schubert 	if (!follow_on_req_fail) {
1195b9c547cSRui Paulo 		if (p2p->cfg->get_persistent_group) {
1205b9c547cSRui Paulo 			shared_group = p2p->cfg->get_persistent_group(
121780fb4a2SCy Schubert 				p2p->cfg->cb_ctx, dev->info.p2p_device_addr,
122780fb4a2SCy Schubert 				NULL, 0, go_dev_addr, ssid, &ssid_len,
123780fb4a2SCy Schubert 				intended_addr);
1245b9c547cSRui Paulo 		}
1255b9c547cSRui Paulo 
1265b9c547cSRui Paulo 		if (shared_group ||
127*a90b9d01SCy Schubert 		    (prov->conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_NEW))) {
128*a90b9d01SCy Schubert 			bool is_6ghz_capab;
129*a90b9d01SCy Schubert 
130*a90b9d01SCy Schubert 			is_6ghz_capab = is_p2p_6ghz_capable(p2p) &&
131*a90b9d01SCy Schubert 				p2p_is_peer_6ghz_capab(
132*a90b9d01SCy Schubert 					p2p, dev->info.p2p_device_addr);
133780fb4a2SCy Schubert 			p2p_buf_add_channel_list(buf, p2p->cfg->country,
134*a90b9d01SCy Schubert 						 &p2p->channels, is_6ghz_capab);
135*a90b9d01SCy Schubert 		}
1365b9c547cSRui Paulo 
137780fb4a2SCy Schubert 		if ((shared_group && !is_zero_ether_addr(intended_addr)) ||
138780fb4a2SCy Schubert 		    (prov->conncap & (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW)))
1395b9c547cSRui Paulo 			p2p_buf_add_operating_channel(buf, p2p->cfg->country,
1405b9c547cSRui Paulo 						      p2p->op_reg_class,
1415b9c547cSRui Paulo 						      p2p->op_channel);
1425b9c547cSRui Paulo 	}
1435b9c547cSRui Paulo 
144780fb4a2SCy Schubert 	if (prov->status < 0 && prov->info[0])
1455b9c547cSRui Paulo 		p2p_buf_add_session_info(buf, prov->info);
1465b9c547cSRui Paulo 
147780fb4a2SCy Schubert 	if (!follow_on_req_fail)
1485b9c547cSRui Paulo 		p2p_buf_add_connection_capability(buf, prov->conncap);
1495b9c547cSRui Paulo 
1505b9c547cSRui Paulo 	p2p_buf_add_advertisement_id(buf, prov->adv_id, prov->adv_mac);
1515b9c547cSRui Paulo 
152780fb4a2SCy Schubert 	if (!follow_on_req_fail) {
1535b9c547cSRui Paulo 		if (shared_group || prov->conncap == P2PS_SETUP_NEW ||
1545b9c547cSRui Paulo 		    prov->conncap ==
1555b9c547cSRui Paulo 		    (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW) ||
1565b9c547cSRui Paulo 		    prov->conncap ==
1575b9c547cSRui Paulo 		    (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT)) {
1585b9c547cSRui Paulo 			/* Add Config Timeout */
1595b9c547cSRui Paulo 			p2p_buf_add_config_timeout(buf, p2p->go_timeout,
1605b9c547cSRui Paulo 						   p2p->client_timeout);
1615b9c547cSRui Paulo 		}
1625b9c547cSRui Paulo 
163780fb4a2SCy Schubert 		p2p_buf_add_listen_channel(buf, p2p->cfg->country,
164780fb4a2SCy Schubert 					   p2p->cfg->reg_class,
1655b9c547cSRui Paulo 					   p2p->cfg->channel);
166780fb4a2SCy Schubert 	}
1675b9c547cSRui Paulo 
1685b9c547cSRui Paulo 	p2p_buf_add_session_id(buf, prov->session_id, prov->session_mac);
1695b9c547cSRui Paulo 
170325151a3SRui Paulo 	p2p_buf_add_feature_capability(buf, sizeof(fcap), (const u8 *) &fcap);
1715b9c547cSRui Paulo 
172325151a3SRui Paulo 	if (shared_group) {
1735b9c547cSRui Paulo 		p2p_buf_add_persistent_group_info(buf, go_dev_addr,
1745b9c547cSRui Paulo 						  ssid, ssid_len);
175325151a3SRui Paulo 		/* Add intended interface address if it is not added yet */
176325151a3SRui Paulo 		if ((prov->conncap == P2PS_SETUP_NONE ||
177325151a3SRui Paulo 		     prov->conncap == P2PS_SETUP_CLIENT) &&
178325151a3SRui Paulo 		    !is_zero_ether_addr(intended_addr))
179325151a3SRui Paulo 			p2p_buf_add_intended_addr(buf, intended_addr);
180325151a3SRui Paulo 	}
1815b9c547cSRui Paulo }
1825b9c547cSRui Paulo 
1835b9c547cSRui Paulo 
p2p_build_prov_disc_req(struct p2p_data * p2p,struct p2p_device * dev,int join)184f05cddf9SRui Paulo static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p,
1855b9c547cSRui Paulo 					       struct p2p_device *dev,
1865b9c547cSRui Paulo 					       int join)
187f05cddf9SRui Paulo {
188f05cddf9SRui Paulo 	struct wpabuf *buf;
189f05cddf9SRui Paulo 	u8 *len;
190f05cddf9SRui Paulo 	size_t extra = 0;
1915b9c547cSRui Paulo 	u8 dialog_token = dev->dialog_token;
1925b9c547cSRui Paulo 	u16 config_methods = dev->req_config_methods;
1935b9c547cSRui Paulo 	struct p2p_device *go = join ? dev : NULL;
1945b9c547cSRui Paulo 	u8 group_capab;
195f05cddf9SRui Paulo 
196f05cddf9SRui Paulo #ifdef CONFIG_WIFI_DISPLAY
197f05cddf9SRui Paulo 	if (p2p->wfd_ie_prov_disc_req)
198f05cddf9SRui Paulo 		extra = wpabuf_len(p2p->wfd_ie_prov_disc_req);
199f05cddf9SRui Paulo #endif /* CONFIG_WIFI_DISPLAY */
200f05cddf9SRui Paulo 
2015b9c547cSRui Paulo 	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ])
2025b9c547cSRui Paulo 		extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]);
2035b9c547cSRui Paulo 
2045b9c547cSRui Paulo 	if (p2p->p2ps_prov)
2055b9c547cSRui Paulo 		extra += os_strlen(p2p->p2ps_prov->info) + 1 +
2065b9c547cSRui Paulo 			sizeof(struct p2ps_provision);
2075b9c547cSRui Paulo 
208f05cddf9SRui Paulo 	buf = wpabuf_alloc(1000 + extra);
209f05cddf9SRui Paulo 	if (buf == NULL)
210f05cddf9SRui Paulo 		return NULL;
211f05cddf9SRui Paulo 
212f05cddf9SRui Paulo 	p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_REQ, dialog_token);
213f05cddf9SRui Paulo 
214f05cddf9SRui Paulo 	len = p2p_buf_add_ie_hdr(buf);
2155b9c547cSRui Paulo 
2165b9c547cSRui Paulo 	group_capab = 0;
2175b9c547cSRui Paulo 	if (p2p->p2ps_prov) {
2185b9c547cSRui Paulo 		group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
2195b9c547cSRui Paulo 		group_capab |= P2P_GROUP_CAPAB_PERSISTENT_RECONN;
2205b9c547cSRui Paulo 		if (p2p->cross_connect)
2215b9c547cSRui Paulo 			group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
2225b9c547cSRui Paulo 		if (p2p->cfg->p2p_intra_bss)
2235b9c547cSRui Paulo 			group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
2245b9c547cSRui Paulo 	}
225f05cddf9SRui Paulo 	p2p_buf_add_capability(buf, p2p->dev_capab &
2265b9c547cSRui Paulo 			       ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY,
2275b9c547cSRui Paulo 			       group_capab);
228f05cddf9SRui Paulo 	p2p_buf_add_device_info(buf, p2p, NULL);
2295b9c547cSRui Paulo 	if (p2p->p2ps_prov) {
2305b9c547cSRui Paulo 		p2ps_add_pd_req_attrs(p2p, dev, buf, config_methods);
2315b9c547cSRui Paulo 	} else if (go) {
232f05cddf9SRui Paulo 		p2p_buf_add_group_id(buf, go->info.p2p_device_addr,
233f05cddf9SRui Paulo 				     go->oper_ssid, go->oper_ssid_len);
234f05cddf9SRui Paulo 	}
235f05cddf9SRui Paulo 	p2p_buf_update_ie_hdr(buf, len);
236f05cddf9SRui Paulo 
237f05cddf9SRui Paulo 	/* WPS IE with Config Methods attribute */
238f05cddf9SRui Paulo 	p2p_build_wps_ie_config_methods(buf, config_methods);
239f05cddf9SRui Paulo 
240f05cddf9SRui Paulo #ifdef CONFIG_WIFI_DISPLAY
241f05cddf9SRui Paulo 	if (p2p->wfd_ie_prov_disc_req)
242f05cddf9SRui Paulo 		wpabuf_put_buf(buf, p2p->wfd_ie_prov_disc_req);
243f05cddf9SRui Paulo #endif /* CONFIG_WIFI_DISPLAY */
244f05cddf9SRui Paulo 
2455b9c547cSRui Paulo 	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ])
2465b9c547cSRui Paulo 		wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]);
2475b9c547cSRui Paulo 
248f05cddf9SRui Paulo 	return buf;
249f05cddf9SRui Paulo }
250f05cddf9SRui Paulo 
251f05cddf9SRui Paulo 
p2p_build_prov_disc_resp(struct p2p_data * p2p,struct p2p_device * dev,u8 dialog_token,enum p2p_status_code status,u16 config_methods,u32 adv_id,const u8 * group_id,size_t group_id_len,const u8 * persist_ssid,size_t persist_ssid_len,const u8 * fcap,u16 fcap_len)252f05cddf9SRui Paulo static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p,
2535b9c547cSRui Paulo 						struct p2p_device *dev,
254f05cddf9SRui Paulo 						u8 dialog_token,
2555b9c547cSRui Paulo 						enum p2p_status_code status,
256f05cddf9SRui Paulo 						u16 config_methods,
2575b9c547cSRui Paulo 						u32 adv_id,
258f05cddf9SRui Paulo 						const u8 *group_id,
2595b9c547cSRui Paulo 						size_t group_id_len,
2605b9c547cSRui Paulo 						const u8 *persist_ssid,
261325151a3SRui Paulo 						size_t persist_ssid_len,
262325151a3SRui Paulo 						const u8 *fcap,
263325151a3SRui Paulo 						u16 fcap_len)
264f05cddf9SRui Paulo {
265f05cddf9SRui Paulo 	struct wpabuf *buf;
266f05cddf9SRui Paulo 	size_t extra = 0;
2675b9c547cSRui Paulo 	int persist = 0;
268f05cddf9SRui Paulo 
269f05cddf9SRui Paulo #ifdef CONFIG_WIFI_DISPLAY
270f05cddf9SRui Paulo 	struct wpabuf *wfd_ie = p2p->wfd_ie_prov_disc_resp;
271f05cddf9SRui Paulo 	if (wfd_ie && group_id) {
272f05cddf9SRui Paulo 		size_t i;
273f05cddf9SRui Paulo 		for (i = 0; i < p2p->num_groups; i++) {
274f05cddf9SRui Paulo 			struct p2p_group *g = p2p->groups[i];
275f05cddf9SRui Paulo 			struct wpabuf *ie;
276f05cddf9SRui Paulo 			if (!p2p_group_is_group_id_match(g, group_id,
277f05cddf9SRui Paulo 							 group_id_len))
278f05cddf9SRui Paulo 				continue;
279f05cddf9SRui Paulo 			ie = p2p_group_get_wfd_ie(g);
280f05cddf9SRui Paulo 			if (ie) {
281f05cddf9SRui Paulo 				wfd_ie = ie;
282f05cddf9SRui Paulo 				break;
283f05cddf9SRui Paulo 			}
284f05cddf9SRui Paulo 		}
285f05cddf9SRui Paulo 	}
286f05cddf9SRui Paulo 	if (wfd_ie)
287f05cddf9SRui Paulo 		extra = wpabuf_len(wfd_ie);
288f05cddf9SRui Paulo #endif /* CONFIG_WIFI_DISPLAY */
289f05cddf9SRui Paulo 
2905b9c547cSRui Paulo 	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP])
2915b9c547cSRui Paulo 		extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]);
2925b9c547cSRui Paulo 
2935b9c547cSRui Paulo 	buf = wpabuf_alloc(1000 + extra);
294f05cddf9SRui Paulo 	if (buf == NULL)
295f05cddf9SRui Paulo 		return NULL;
296f05cddf9SRui Paulo 
297f05cddf9SRui Paulo 	p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_RESP, dialog_token);
298f05cddf9SRui Paulo 
2995b9c547cSRui Paulo 	/* Add P2P IE for P2PS */
3005b9c547cSRui Paulo 	if (p2p->p2ps_prov && p2p->p2ps_prov->adv_id == adv_id) {
3015b9c547cSRui Paulo 		u8 *len = p2p_buf_add_ie_hdr(buf);
3025b9c547cSRui Paulo 		struct p2ps_provision *prov = p2p->p2ps_prov;
3035b9c547cSRui Paulo 		u8 group_capab;
304780fb4a2SCy Schubert 		u8 conncap = 0;
305780fb4a2SCy Schubert 
306780fb4a2SCy Schubert 		if (status == P2P_SC_SUCCESS ||
307780fb4a2SCy Schubert 		    status == P2P_SC_SUCCESS_DEFERRED)
308780fb4a2SCy Schubert 			conncap = prov->conncap;
3095b9c547cSRui Paulo 
3105b9c547cSRui Paulo 		if (!status && prov->status != -1)
3115b9c547cSRui Paulo 			status = prov->status;
3125b9c547cSRui Paulo 
3135b9c547cSRui Paulo 		p2p_buf_add_status(buf, status);
3145b9c547cSRui Paulo 		group_capab = P2P_GROUP_CAPAB_PERSISTENT_GROUP |
3155b9c547cSRui Paulo 			P2P_GROUP_CAPAB_PERSISTENT_RECONN;
3165b9c547cSRui Paulo 		if (p2p->cross_connect)
3175b9c547cSRui Paulo 			group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
3185b9c547cSRui Paulo 		if (p2p->cfg->p2p_intra_bss)
3195b9c547cSRui Paulo 			group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
3205b9c547cSRui Paulo 		p2p_buf_add_capability(buf, p2p->dev_capab &
3215b9c547cSRui Paulo 				       ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY,
3225b9c547cSRui Paulo 				       group_capab);
3235b9c547cSRui Paulo 		p2p_buf_add_device_info(buf, p2p, NULL);
3245b9c547cSRui Paulo 
325780fb4a2SCy Schubert 		if (persist_ssid && p2p->cfg->get_persistent_group && dev &&
3265b9c547cSRui Paulo 		    (status == P2P_SC_SUCCESS ||
3275b9c547cSRui Paulo 		     status == P2P_SC_SUCCESS_DEFERRED)) {
328325151a3SRui Paulo 			u8 ssid[SSID_MAX_LEN];
3295b9c547cSRui Paulo 			size_t ssid_len;
3305b9c547cSRui Paulo 			u8 go_dev_addr[ETH_ALEN];
331325151a3SRui Paulo 			u8 intended_addr[ETH_ALEN];
3325b9c547cSRui Paulo 
3335b9c547cSRui Paulo 			persist = p2p->cfg->get_persistent_group(
3345b9c547cSRui Paulo 				p2p->cfg->cb_ctx,
3355b9c547cSRui Paulo 				dev->info.p2p_device_addr,
3365b9c547cSRui Paulo 				persist_ssid, persist_ssid_len, go_dev_addr,
337325151a3SRui Paulo 				ssid, &ssid_len, intended_addr);
338325151a3SRui Paulo 			if (persist) {
3395b9c547cSRui Paulo 				p2p_buf_add_persistent_group_info(
3405b9c547cSRui Paulo 					buf, go_dev_addr, ssid, ssid_len);
341325151a3SRui Paulo 				if (!is_zero_ether_addr(intended_addr))
342325151a3SRui Paulo 					p2p_buf_add_intended_addr(
343325151a3SRui Paulo 						buf, intended_addr);
344325151a3SRui Paulo 			}
3455b9c547cSRui Paulo 		}
3465b9c547cSRui Paulo 
347780fb4a2SCy Schubert 		if (!persist && (conncap & P2PS_SETUP_GROUP_OWNER))
348780fb4a2SCy Schubert 			p2ps_add_new_group_info(p2p, dev, buf);
3495b9c547cSRui Paulo 
3505b9c547cSRui Paulo 		/* Add Operating Channel if conncap indicates GO */
351780fb4a2SCy Schubert 		if (persist || (conncap & P2PS_SETUP_GROUP_OWNER)) {
3525b9c547cSRui Paulo 			if (p2p->op_reg_class && p2p->op_channel)
3535b9c547cSRui Paulo 				p2p_buf_add_operating_channel(
3545b9c547cSRui Paulo 					buf, p2p->cfg->country,
3555b9c547cSRui Paulo 					p2p->op_reg_class,
3565b9c547cSRui Paulo 					p2p->op_channel);
3575b9c547cSRui Paulo 			else
3585b9c547cSRui Paulo 				p2p_buf_add_operating_channel(
3595b9c547cSRui Paulo 					buf, p2p->cfg->country,
3605b9c547cSRui Paulo 					p2p->cfg->op_reg_class,
3615b9c547cSRui Paulo 					p2p->cfg->op_channel);
3625b9c547cSRui Paulo 		}
3635b9c547cSRui Paulo 
364780fb4a2SCy Schubert 		if (persist ||
365*a90b9d01SCy Schubert 		    (conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER))) {
366*a90b9d01SCy Schubert 			bool is_6ghz_capab;
367*a90b9d01SCy Schubert 
368*a90b9d01SCy Schubert 			is_6ghz_capab = is_p2p_6ghz_capable(p2p) && dev &&
369*a90b9d01SCy Schubert 				p2p_is_peer_6ghz_capab(
370*a90b9d01SCy Schubert 					p2p, dev->info.p2p_device_addr);
3715b9c547cSRui Paulo 			p2p_buf_add_channel_list(buf, p2p->cfg->country,
372*a90b9d01SCy Schubert 						 &p2p->channels, is_6ghz_capab);
373*a90b9d01SCy Schubert 		}
3745b9c547cSRui Paulo 
375780fb4a2SCy Schubert 		if (!persist && conncap)
376780fb4a2SCy Schubert 			p2p_buf_add_connection_capability(buf, conncap);
3775b9c547cSRui Paulo 
3785b9c547cSRui Paulo 		p2p_buf_add_advertisement_id(buf, adv_id, prov->adv_mac);
3795b9c547cSRui Paulo 
380780fb4a2SCy Schubert 		if (persist ||
381780fb4a2SCy Schubert 		    (conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER)))
3825b9c547cSRui Paulo 			p2p_buf_add_config_timeout(buf, p2p->go_timeout,
3835b9c547cSRui Paulo 						   p2p->client_timeout);
3845b9c547cSRui Paulo 
3855b9c547cSRui Paulo 		p2p_buf_add_session_id(buf, prov->session_id,
3865b9c547cSRui Paulo 				       prov->session_mac);
3875b9c547cSRui Paulo 
388325151a3SRui Paulo 		p2p_buf_add_feature_capability(buf, fcap_len, fcap);
3895b9c547cSRui Paulo 		p2p_buf_update_ie_hdr(buf, len);
3905b9c547cSRui Paulo 	} else if (status != P2P_SC_SUCCESS || adv_id) {
3915b9c547cSRui Paulo 		u8 *len = p2p_buf_add_ie_hdr(buf);
3925b9c547cSRui Paulo 
3935b9c547cSRui Paulo 		p2p_buf_add_status(buf, status);
3945b9c547cSRui Paulo 
3955b9c547cSRui Paulo 		if (p2p->p2ps_prov)
3965b9c547cSRui Paulo 			p2p_buf_add_advertisement_id(buf, adv_id,
3975b9c547cSRui Paulo 						     p2p->p2ps_prov->adv_mac);
3985b9c547cSRui Paulo 
3995b9c547cSRui Paulo 		p2p_buf_update_ie_hdr(buf, len);
4005b9c547cSRui Paulo 	}
4015b9c547cSRui Paulo 
402f05cddf9SRui Paulo 	/* WPS IE with Config Methods attribute */
403f05cddf9SRui Paulo 	p2p_build_wps_ie_config_methods(buf, config_methods);
404f05cddf9SRui Paulo 
405f05cddf9SRui Paulo #ifdef CONFIG_WIFI_DISPLAY
406f05cddf9SRui Paulo 	if (wfd_ie)
407f05cddf9SRui Paulo 		wpabuf_put_buf(buf, wfd_ie);
408f05cddf9SRui Paulo #endif /* CONFIG_WIFI_DISPLAY */
409f05cddf9SRui Paulo 
4105b9c547cSRui Paulo 	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP])
4115b9c547cSRui Paulo 		wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]);
4125b9c547cSRui Paulo 
413f05cddf9SRui Paulo 	return buf;
414f05cddf9SRui Paulo }
415f05cddf9SRui Paulo 
416f05cddf9SRui Paulo 
p2ps_setup_p2ps_prov(struct p2p_data * p2p,u32 adv_id,u32 session_id,u16 method,const u8 * session_mac,const u8 * adv_mac)4175b9c547cSRui Paulo static int p2ps_setup_p2ps_prov(struct p2p_data *p2p, u32 adv_id,
4185b9c547cSRui Paulo 				u32 session_id, u16 method,
4195b9c547cSRui Paulo 				const u8 *session_mac, const u8 *adv_mac)
4205b9c547cSRui Paulo {
4215b9c547cSRui Paulo 	struct p2ps_provision *tmp;
4225b9c547cSRui Paulo 
4235b9c547cSRui Paulo 	if (!p2p->p2ps_prov) {
4245b9c547cSRui Paulo 		p2p->p2ps_prov = os_zalloc(sizeof(struct p2ps_provision) + 1);
4255b9c547cSRui Paulo 		if (!p2p->p2ps_prov)
4265b9c547cSRui Paulo 			return -1;
4275b9c547cSRui Paulo 	} else {
4285b9c547cSRui Paulo 		os_memset(p2p->p2ps_prov, 0, sizeof(struct p2ps_provision) + 1);
4295b9c547cSRui Paulo 	}
4305b9c547cSRui Paulo 
4315b9c547cSRui Paulo 	tmp = p2p->p2ps_prov;
4325b9c547cSRui Paulo 	tmp->adv_id = adv_id;
4335b9c547cSRui Paulo 	tmp->session_id = session_id;
4345b9c547cSRui Paulo 	tmp->method = method;
4355b9c547cSRui Paulo 	os_memcpy(tmp->session_mac, session_mac, ETH_ALEN);
4365b9c547cSRui Paulo 	os_memcpy(tmp->adv_mac, adv_mac, ETH_ALEN);
4375b9c547cSRui Paulo 	tmp->info[0] = '\0';
4385b9c547cSRui Paulo 
4395b9c547cSRui Paulo 	return 0;
4405b9c547cSRui Paulo }
4415b9c547cSRui Paulo 
4425b9c547cSRui Paulo 
p2ps_own_preferred_cpt(const u8 * cpt_priority,u8 req_cpt_mask)443325151a3SRui Paulo static u8 p2ps_own_preferred_cpt(const u8 *cpt_priority, u8 req_cpt_mask)
444325151a3SRui Paulo {
445325151a3SRui Paulo 	int i;
446325151a3SRui Paulo 
447325151a3SRui Paulo 	for (i = 0; cpt_priority[i]; i++)
448325151a3SRui Paulo 		if (req_cpt_mask & cpt_priority[i])
449325151a3SRui Paulo 			return cpt_priority[i];
450325151a3SRui Paulo 
451325151a3SRui Paulo 	return 0;
452325151a3SRui Paulo }
453325151a3SRui Paulo 
454325151a3SRui Paulo 
455780fb4a2SCy Schubert /* Check if the message contains a valid P2PS PD Request */
p2ps_validate_pd_req(struct p2p_data * p2p,struct p2p_message * msg,const u8 * addr)456780fb4a2SCy Schubert static int p2ps_validate_pd_req(struct p2p_data *p2p, struct p2p_message *msg,
457780fb4a2SCy Schubert 				const u8 *addr)
458780fb4a2SCy Schubert {
459780fb4a2SCy Schubert 	u8 group_id = 0;
460780fb4a2SCy Schubert 	u8 intended_addr = 0;
461780fb4a2SCy Schubert 	u8 operating_channel = 0;
462780fb4a2SCy Schubert 	u8 channel_list = 0;
463780fb4a2SCy Schubert 	u8 config_timeout = 0;
464780fb4a2SCy Schubert 	u8 listen_channel = 0;
465780fb4a2SCy Schubert 
466780fb4a2SCy Schubert #define P2PS_PD_REQ_CHECK(_val, _attr) \
467780fb4a2SCy Schubert do { \
468780fb4a2SCy Schubert 	if ((_val) && !msg->_attr) { \
469780fb4a2SCy Schubert 		p2p_dbg(p2p, "Not P2PS PD Request. Missing %s", #_attr); \
470780fb4a2SCy Schubert 		return -1; \
471780fb4a2SCy Schubert 	} \
472780fb4a2SCy Schubert } while (0)
473780fb4a2SCy Schubert 
474780fb4a2SCy Schubert 	P2PS_PD_REQ_CHECK(1, adv_id);
475780fb4a2SCy Schubert 	P2PS_PD_REQ_CHECK(1, session_id);
476780fb4a2SCy Schubert 	P2PS_PD_REQ_CHECK(1, session_mac);
477780fb4a2SCy Schubert 	P2PS_PD_REQ_CHECK(1, adv_mac);
478780fb4a2SCy Schubert 	P2PS_PD_REQ_CHECK(1, capability);
479780fb4a2SCy Schubert 	P2PS_PD_REQ_CHECK(1, p2p_device_info);
480780fb4a2SCy Schubert 	P2PS_PD_REQ_CHECK(1, feature_cap);
481780fb4a2SCy Schubert 
482780fb4a2SCy Schubert 	/*
483780fb4a2SCy Schubert 	 * We don't need to check Connection Capability, Persistent Group,
484780fb4a2SCy Schubert 	 * and related attributes for follow-on PD Request with a status
485780fb4a2SCy Schubert 	 * other than SUCCESS_DEFERRED.
486780fb4a2SCy Schubert 	 */
487780fb4a2SCy Schubert 	if (msg->status && *msg->status != P2P_SC_SUCCESS_DEFERRED)
488780fb4a2SCy Schubert 		return 0;
489780fb4a2SCy Schubert 
490780fb4a2SCy Schubert 	P2PS_PD_REQ_CHECK(1, conn_cap);
491780fb4a2SCy Schubert 
492780fb4a2SCy Schubert 	/*
493780fb4a2SCy Schubert 	 * Note 1: A feature capability attribute structure can be changed
494780fb4a2SCy Schubert 	 * in the future. The assumption is that such modifications are
495780fb4a2SCy Schubert 	 * backward compatible, therefore we allow processing of msg.feature_cap
496780fb4a2SCy Schubert 	 * exceeding the size of the p2ps_feature_capab structure.
497780fb4a2SCy Schubert 	 * Note 2: Verification of msg.feature_cap_len below has to be changed
498780fb4a2SCy Schubert 	 * to allow 2 byte feature capability processing if
499780fb4a2SCy Schubert 	 * struct p2ps_feature_capab is extended to include additional fields
500780fb4a2SCy Schubert 	 * and it affects the structure size.
501780fb4a2SCy Schubert 	 */
502780fb4a2SCy Schubert 	if (msg->feature_cap_len < sizeof(struct p2ps_feature_capab)) {
503780fb4a2SCy Schubert 		p2p_dbg(p2p, "P2PS: Invalid feature capability len");
504780fb4a2SCy Schubert 		return -1;
505780fb4a2SCy Schubert 	}
506780fb4a2SCy Schubert 
507780fb4a2SCy Schubert 	switch (*msg->conn_cap) {
508780fb4a2SCy Schubert 	case P2PS_SETUP_NEW:
509780fb4a2SCy Schubert 		group_id = 1;
510780fb4a2SCy Schubert 		intended_addr = 1;
511780fb4a2SCy Schubert 		operating_channel = 1;
512780fb4a2SCy Schubert 		channel_list = 1;
513780fb4a2SCy Schubert 		config_timeout = 1;
514780fb4a2SCy Schubert 		listen_channel = 1;
515780fb4a2SCy Schubert 		break;
516780fb4a2SCy Schubert 	case P2PS_SETUP_CLIENT:
517780fb4a2SCy Schubert 		channel_list = 1;
518780fb4a2SCy Schubert 		listen_channel = 1;
519780fb4a2SCy Schubert 		break;
520780fb4a2SCy Schubert 	case P2PS_SETUP_GROUP_OWNER:
521780fb4a2SCy Schubert 		group_id = 1;
522780fb4a2SCy Schubert 		intended_addr = 1;
523780fb4a2SCy Schubert 		operating_channel = 1;
524780fb4a2SCy Schubert 		break;
525780fb4a2SCy Schubert 	case P2PS_SETUP_NEW | P2PS_SETUP_GROUP_OWNER:
526780fb4a2SCy Schubert 		group_id = 1;
527780fb4a2SCy Schubert 		operating_channel = 1;
528780fb4a2SCy Schubert 		intended_addr = 1;
529780fb4a2SCy Schubert 		channel_list = 1;
530780fb4a2SCy Schubert 		config_timeout = 1;
531780fb4a2SCy Schubert 		break;
532780fb4a2SCy Schubert 	case P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER:
533780fb4a2SCy Schubert 		group_id = 1;
534780fb4a2SCy Schubert 		intended_addr = 1;
535780fb4a2SCy Schubert 		operating_channel = 1;
536780fb4a2SCy Schubert 		channel_list = 1;
537780fb4a2SCy Schubert 		config_timeout = 1;
538780fb4a2SCy Schubert 		break;
539780fb4a2SCy Schubert 	default:
540780fb4a2SCy Schubert 		p2p_dbg(p2p, "Invalid P2PS PD connection capability");
541780fb4a2SCy Schubert 		return -1;
542780fb4a2SCy Schubert 	}
543780fb4a2SCy Schubert 
544780fb4a2SCy Schubert 	if (msg->persistent_dev) {
545780fb4a2SCy Schubert 		channel_list = 1;
546780fb4a2SCy Schubert 		config_timeout = 1;
547*a90b9d01SCy Schubert 		if (ether_addr_equal(msg->persistent_dev, addr)) {
548780fb4a2SCy Schubert 			intended_addr = 1;
549780fb4a2SCy Schubert 			operating_channel = 1;
550780fb4a2SCy Schubert 		}
551780fb4a2SCy Schubert 	}
552780fb4a2SCy Schubert 
553780fb4a2SCy Schubert 	P2PS_PD_REQ_CHECK(group_id, group_id);
554780fb4a2SCy Schubert 	P2PS_PD_REQ_CHECK(intended_addr, intended_addr);
555780fb4a2SCy Schubert 	P2PS_PD_REQ_CHECK(operating_channel, operating_channel);
556780fb4a2SCy Schubert 	P2PS_PD_REQ_CHECK(channel_list, channel_list);
557780fb4a2SCy Schubert 	P2PS_PD_REQ_CHECK(config_timeout, config_timeout);
558780fb4a2SCy Schubert 	P2PS_PD_REQ_CHECK(listen_channel, listen_channel);
559780fb4a2SCy Schubert 
560780fb4a2SCy Schubert #undef P2PS_PD_REQ_CHECK
561780fb4a2SCy Schubert 
562780fb4a2SCy Schubert 	return 0;
563780fb4a2SCy Schubert }
564780fb4a2SCy Schubert 
565780fb4a2SCy Schubert 
p2p_process_prov_disc_req(struct p2p_data * p2p,const u8 * sa,const u8 * data,size_t len,int rx_freq)566f05cddf9SRui Paulo void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
567f05cddf9SRui Paulo 			       const u8 *data, size_t len, int rx_freq)
568f05cddf9SRui Paulo {
569f05cddf9SRui Paulo 	struct p2p_message msg;
570f05cddf9SRui Paulo 	struct p2p_device *dev;
571f05cddf9SRui Paulo 	int freq;
5725b9c547cSRui Paulo 	enum p2p_status_code reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
573f05cddf9SRui Paulo 	struct wpabuf *resp;
5745b9c547cSRui Paulo 	u32 adv_id = 0;
5755b9c547cSRui Paulo 	struct p2ps_advertisement *p2ps_adv = NULL;
5765b9c547cSRui Paulo 	u8 conncap = P2PS_SETUP_NEW;
5775b9c547cSRui Paulo 	u8 auto_accept = 0;
5785b9c547cSRui Paulo 	u32 session_id = 0;
579780fb4a2SCy Schubert 	u8 session_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
580780fb4a2SCy Schubert 	u8 adv_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
581325151a3SRui Paulo 	const u8 *group_mac;
5825b9c547cSRui Paulo 	int passwd_id = DEV_PW_DEFAULT;
5835b9c547cSRui Paulo 	u16 config_methods;
584325151a3SRui Paulo 	u16 allowed_config_methods = WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
585325151a3SRui Paulo 	struct p2ps_feature_capab resp_fcap = { 0, 0 };
586780fb4a2SCy Schubert 	struct p2ps_feature_capab *req_fcap = NULL;
587780fb4a2SCy Schubert 	u8 remote_conncap;
588780fb4a2SCy Schubert 	u16 method;
589f05cddf9SRui Paulo 
590f05cddf9SRui Paulo 	if (p2p_parse(data, len, &msg))
591f05cddf9SRui Paulo 		return;
592f05cddf9SRui Paulo 
5935b9c547cSRui Paulo 	p2p_dbg(p2p, "Received Provision Discovery Request from " MACSTR
594f05cddf9SRui Paulo 		" with config methods 0x%x (freq=%d)",
595f05cddf9SRui Paulo 		MAC2STR(sa), msg.wps_config_methods, rx_freq);
596325151a3SRui Paulo 	group_mac = msg.intended_addr;
597f05cddf9SRui Paulo 
598f05cddf9SRui Paulo 	dev = p2p_get_device(p2p, sa);
599f05cddf9SRui Paulo 	if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
6005b9c547cSRui Paulo 		p2p_dbg(p2p, "Provision Discovery Request from unknown peer "
6015b9c547cSRui Paulo 			MACSTR, MAC2STR(sa));
602f05cddf9SRui Paulo 
6035b9c547cSRui Paulo 		if (p2p_add_device(p2p, sa, rx_freq, NULL, 0, data + 1, len - 1,
604f05cddf9SRui Paulo 				   0)) {
6055b9c547cSRui Paulo 			p2p_dbg(p2p, "Provision Discovery Request add device failed "
6065b9c547cSRui Paulo 				MACSTR, MAC2STR(sa));
607780fb4a2SCy Schubert 			goto out;
608780fb4a2SCy Schubert 		}
609780fb4a2SCy Schubert 
610780fb4a2SCy Schubert 		dev = p2p_get_device(p2p, sa);
611780fb4a2SCy Schubert 		if (!dev) {
612780fb4a2SCy Schubert 			p2p_dbg(p2p,
613780fb4a2SCy Schubert 				"Provision Discovery device not found "
614780fb4a2SCy Schubert 				MACSTR, MAC2STR(sa));
615780fb4a2SCy Schubert 			goto out;
616780fb4a2SCy Schubert 		}
617f05cddf9SRui Paulo 	} else if (msg.wfd_subelems) {
618f05cddf9SRui Paulo 		wpabuf_free(dev->info.wfd_subelems);
619f05cddf9SRui Paulo 		dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
620f05cddf9SRui Paulo 	}
621f05cddf9SRui Paulo 
622*a90b9d01SCy Schubert 	p2p_update_peer_6ghz_capab(dev, &msg);
623*a90b9d01SCy Schubert 
624780fb4a2SCy Schubert 	if (!msg.adv_id) {
625325151a3SRui Paulo 		allowed_config_methods |= WPS_CONFIG_PUSHBUTTON;
626325151a3SRui Paulo 		if (!(msg.wps_config_methods & allowed_config_methods)) {
627780fb4a2SCy Schubert 			p2p_dbg(p2p,
628780fb4a2SCy Schubert 				"Unsupported Config Methods in Provision Discovery Request");
629f05cddf9SRui Paulo 			goto out;
630f05cddf9SRui Paulo 		}
631f05cddf9SRui Paulo 
6325b9c547cSRui Paulo 		/* Legacy (non-P2PS) - Unknown groups allowed for P2PS */
633780fb4a2SCy Schubert 		if (msg.group_id) {
634f05cddf9SRui Paulo 			size_t i;
635780fb4a2SCy Schubert 
636f05cddf9SRui Paulo 			for (i = 0; i < p2p->num_groups; i++) {
637780fb4a2SCy Schubert 				if (p2p_group_is_group_id_match(
638780fb4a2SCy Schubert 					    p2p->groups[i],
639780fb4a2SCy Schubert 					    msg.group_id, msg.group_id_len))
640f05cddf9SRui Paulo 					break;
641f05cddf9SRui Paulo 			}
642f05cddf9SRui Paulo 			if (i == p2p->num_groups) {
643780fb4a2SCy Schubert 				p2p_dbg(p2p,
644780fb4a2SCy Schubert 					"PD request for unknown P2P Group ID - reject");
645f05cddf9SRui Paulo 				goto out;
646f05cddf9SRui Paulo 			}
647f05cddf9SRui Paulo 		}
648780fb4a2SCy Schubert 	} else {
649780fb4a2SCy Schubert 		allowed_config_methods |= WPS_CONFIG_P2PS;
650f05cddf9SRui Paulo 
651780fb4a2SCy Schubert 		/*
652780fb4a2SCy Schubert 		 * Set adv_id here, so in case of an error, a P2PS PD Response
653780fb4a2SCy Schubert 		 * will be sent.
654780fb4a2SCy Schubert 		 */
655780fb4a2SCy Schubert 		adv_id = WPA_GET_LE32(msg.adv_id);
656780fb4a2SCy Schubert 		if (p2ps_validate_pd_req(p2p, &msg, sa) < 0) {
657780fb4a2SCy Schubert 			reject = P2P_SC_FAIL_INVALID_PARAMS;
658780fb4a2SCy Schubert 			goto out;
659780fb4a2SCy Schubert 		}
660780fb4a2SCy Schubert 
661780fb4a2SCy Schubert 		req_fcap = (struct p2ps_feature_capab *) msg.feature_cap;
662780fb4a2SCy Schubert 
663780fb4a2SCy Schubert 		os_memcpy(session_mac, msg.session_mac, ETH_ALEN);
664780fb4a2SCy Schubert 		os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN);
665780fb4a2SCy Schubert 
666780fb4a2SCy Schubert 		session_id = WPA_GET_LE32(msg.session_id);
667780fb4a2SCy Schubert 
668780fb4a2SCy Schubert 		if (msg.conn_cap)
669780fb4a2SCy Schubert 			conncap = *msg.conn_cap;
670780fb4a2SCy Schubert 
671780fb4a2SCy Schubert 		/*
672780fb4a2SCy Schubert 		 * We need to verify a P2PS config methog in an initial PD
673780fb4a2SCy Schubert 		 * request or in a follow-on PD request with the status
674780fb4a2SCy Schubert 		 * SUCCESS_DEFERRED.
675780fb4a2SCy Schubert 		 */
676780fb4a2SCy Schubert 		if ((!msg.status || *msg.status == P2P_SC_SUCCESS_DEFERRED) &&
677780fb4a2SCy Schubert 		    !(msg.wps_config_methods & allowed_config_methods)) {
678780fb4a2SCy Schubert 			p2p_dbg(p2p,
679780fb4a2SCy Schubert 				"Unsupported Config Methods in Provision Discovery Request");
680780fb4a2SCy Schubert 			goto out;
681780fb4a2SCy Schubert 		}
682780fb4a2SCy Schubert 
683780fb4a2SCy Schubert 		/*
684780fb4a2SCy Schubert 		 * TODO: since we don't support multiple PD, reject PD request
685780fb4a2SCy Schubert 		 * if we are in the middle of P2PS PD with some other peer
686780fb4a2SCy Schubert 		 */
687780fb4a2SCy Schubert 	}
688780fb4a2SCy Schubert 
689f05cddf9SRui Paulo 	dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
6905b9c547cSRui Paulo 			P2P_DEV_PD_PEER_KEYPAD |
6915b9c547cSRui Paulo 			P2P_DEV_PD_PEER_P2PS);
6925b9c547cSRui Paulo 
693780fb4a2SCy Schubert 	if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) {
694780fb4a2SCy Schubert 		p2p_dbg(p2p, "Peer " MACSTR
695780fb4a2SCy Schubert 			" requested us to show a PIN on display", MAC2STR(sa));
696780fb4a2SCy Schubert 		dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
697780fb4a2SCy Schubert 		passwd_id = DEV_PW_USER_SPECIFIED;
698780fb4a2SCy Schubert 	} else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
699780fb4a2SCy Schubert 		p2p_dbg(p2p, "Peer " MACSTR
700780fb4a2SCy Schubert 			" requested us to write its PIN using keypad",
701780fb4a2SCy Schubert 			MAC2STR(sa));
702780fb4a2SCy Schubert 		dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
703780fb4a2SCy Schubert 		passwd_id = DEV_PW_REGISTRAR_SPECIFIED;
704780fb4a2SCy Schubert 	} else if (msg.wps_config_methods & WPS_CONFIG_P2PS) {
705780fb4a2SCy Schubert 		p2p_dbg(p2p, "Peer " MACSTR " requesting P2PS PIN",
706780fb4a2SCy Schubert 			MAC2STR(sa));
707780fb4a2SCy Schubert 		dev->flags |= P2P_DEV_PD_PEER_P2PS;
708780fb4a2SCy Schubert 		passwd_id = DEV_PW_P2PS_DEFAULT;
709780fb4a2SCy Schubert 	}
710780fb4a2SCy Schubert 
7115b9c547cSRui Paulo 	/* Remove stale persistent groups */
7125b9c547cSRui Paulo 	if (p2p->cfg->remove_stale_groups) {
7135b9c547cSRui Paulo 		p2p->cfg->remove_stale_groups(
7145b9c547cSRui Paulo 			p2p->cfg->cb_ctx, dev->info.p2p_device_addr,
7155b9c547cSRui Paulo 			msg.persistent_dev,
7165b9c547cSRui Paulo 			msg.persistent_ssid, msg.persistent_ssid_len);
7175b9c547cSRui Paulo 	}
718f05cddf9SRui Paulo 
7195b9c547cSRui Paulo 	reject = P2P_SC_SUCCESS;
7205b9c547cSRui Paulo 
721780fb4a2SCy Schubert 	/*
722780fb4a2SCy Schubert 	 * End of a legacy P2P PD Request processing, from this point continue
723780fb4a2SCy Schubert 	 * with P2PS one.
724325151a3SRui Paulo 	 */
725780fb4a2SCy Schubert 	if (!msg.adv_id)
726780fb4a2SCy Schubert 		goto out;
7275b9c547cSRui Paulo 
7285b9c547cSRui Paulo 	remote_conncap = conncap;
7295b9c547cSRui Paulo 
730780fb4a2SCy Schubert 	if (!msg.status) {
731780fb4a2SCy Schubert 		unsigned int forced_freq, pref_freq;
732780fb4a2SCy Schubert 
733*a90b9d01SCy Schubert 		if (!ether_addr_equal(p2p->cfg->dev_addr, msg.adv_mac)) {
734780fb4a2SCy Schubert 			p2p_dbg(p2p,
735780fb4a2SCy Schubert 				"P2PS PD adv mac does not match the local one");
736780fb4a2SCy Schubert 			reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
737780fb4a2SCy Schubert 			goto out;
738780fb4a2SCy Schubert 		}
739780fb4a2SCy Schubert 
740780fb4a2SCy Schubert 		p2ps_adv = p2p_service_p2ps_id(p2p, adv_id);
741780fb4a2SCy Schubert 		if (!p2ps_adv) {
742780fb4a2SCy Schubert 			p2p_dbg(p2p, "P2PS PD invalid adv_id=0x%X", adv_id);
743780fb4a2SCy Schubert 			reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
744780fb4a2SCy Schubert 			goto out;
745780fb4a2SCy Schubert 		}
746780fb4a2SCy Schubert 		p2p_dbg(p2p, "adv_id: 0x%X, p2ps_adv: %p", adv_id, p2ps_adv);
747780fb4a2SCy Schubert 
7485b9c547cSRui Paulo 		auto_accept = p2ps_adv->auto_accept;
749780fb4a2SCy Schubert 		conncap = p2p->cfg->p2ps_group_capability(p2p->cfg->cb_ctx,
750780fb4a2SCy Schubert 							  conncap, auto_accept,
751780fb4a2SCy Schubert 							  &forced_freq,
752780fb4a2SCy Schubert 							  &pref_freq);
7535b9c547cSRui Paulo 
7545b9c547cSRui Paulo 		p2p_dbg(p2p, "Conncap: local:%d remote:%d result:%d",
7555b9c547cSRui Paulo 			auto_accept, remote_conncap, conncap);
7565b9c547cSRui Paulo 
757780fb4a2SCy Schubert 		p2p_prepare_channel(p2p, dev, forced_freq, pref_freq, 0);
758780fb4a2SCy Schubert 
759780fb4a2SCy Schubert 		resp_fcap.cpt = p2ps_own_preferred_cpt(p2ps_adv->cpt_priority,
760325151a3SRui Paulo 						       req_fcap->cpt);
761325151a3SRui Paulo 
762780fb4a2SCy Schubert 		p2p_dbg(p2p, "cpt: service:0x%x remote:0x%x result:0x%x",
763780fb4a2SCy Schubert 			p2ps_adv->cpt_mask, req_fcap->cpt, resp_fcap.cpt);
764325151a3SRui Paulo 
765325151a3SRui Paulo 		if (!resp_fcap.cpt) {
766325151a3SRui Paulo 			p2p_dbg(p2p,
767325151a3SRui Paulo 				"Incompatible P2PS feature capability CPT bitmask");
768325151a3SRui Paulo 			reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
769325151a3SRui Paulo 		} else if (p2ps_adv->config_methods &&
7705b9c547cSRui Paulo 			   !(msg.wps_config_methods &
7715b9c547cSRui Paulo 			     p2ps_adv->config_methods)) {
7725b9c547cSRui Paulo 			p2p_dbg(p2p,
7735b9c547cSRui Paulo 				"Unsupported config methods in Provision Discovery Request (own=0x%x peer=0x%x)",
7745b9c547cSRui Paulo 				p2ps_adv->config_methods,
7755b9c547cSRui Paulo 				msg.wps_config_methods);
7765b9c547cSRui Paulo 			reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
7775b9c547cSRui Paulo 		} else if (!p2ps_adv->state) {
7785b9c547cSRui Paulo 			p2p_dbg(p2p, "P2PS state unavailable");
7795b9c547cSRui Paulo 			reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
7805b9c547cSRui Paulo 		} else if (!conncap) {
7815b9c547cSRui Paulo 			p2p_dbg(p2p, "Conncap resolution failed");
7825b9c547cSRui Paulo 			reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
7835b9c547cSRui Paulo 		}
7845b9c547cSRui Paulo 
7855b9c547cSRui Paulo 		if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
7865b9c547cSRui Paulo 			p2p_dbg(p2p, "Keypad - always defer");
7875b9c547cSRui Paulo 			auto_accept = 0;
7885b9c547cSRui Paulo 		}
7895b9c547cSRui Paulo 
790780fb4a2SCy Schubert 		if ((remote_conncap & (P2PS_SETUP_NEW | P2PS_SETUP_CLIENT) ||
791780fb4a2SCy Schubert 		     msg.persistent_dev) && conncap != P2PS_SETUP_NEW &&
792780fb4a2SCy Schubert 		    msg.channel_list && msg.channel_list_len &&
793780fb4a2SCy Schubert 		    p2p_peer_channels_check(p2p, &p2p->channels, dev,
794780fb4a2SCy Schubert 					    msg.channel_list,
795780fb4a2SCy Schubert 					    msg.channel_list_len) < 0) {
796780fb4a2SCy Schubert 			p2p_dbg(p2p,
797780fb4a2SCy Schubert 				"No common channels - force deferred flow");
798780fb4a2SCy Schubert 			auto_accept = 0;
799780fb4a2SCy Schubert 		}
800780fb4a2SCy Schubert 
801780fb4a2SCy Schubert 		if (((remote_conncap & P2PS_SETUP_GROUP_OWNER) ||
802780fb4a2SCy Schubert 		     msg.persistent_dev) && msg.operating_channel) {
803780fb4a2SCy Schubert 			struct p2p_channels intersect;
804780fb4a2SCy Schubert 
805780fb4a2SCy Schubert 			/*
806780fb4a2SCy Schubert 			 * There are cases where only the operating channel is
807780fb4a2SCy Schubert 			 * provided. This requires saving the channel as the
808780fb4a2SCy Schubert 			 * supported channel list, and verifying that it is
809780fb4a2SCy Schubert 			 * supported.
810780fb4a2SCy Schubert 			 */
811780fb4a2SCy Schubert 			if (dev->channels.reg_classes == 0 ||
812780fb4a2SCy Schubert 			    !p2p_channels_includes(&dev->channels,
813780fb4a2SCy Schubert 						   msg.operating_channel[3],
814780fb4a2SCy Schubert 						   msg.operating_channel[4])) {
815780fb4a2SCy Schubert 				struct p2p_channels *ch = &dev->channels;
816780fb4a2SCy Schubert 
817780fb4a2SCy Schubert 				os_memset(ch, 0, sizeof(*ch));
818780fb4a2SCy Schubert 				ch->reg_class[0].reg_class =
819780fb4a2SCy Schubert 					msg.operating_channel[3];
820780fb4a2SCy Schubert 				ch->reg_class[0].channel[0] =
821780fb4a2SCy Schubert 					msg.operating_channel[4];
822780fb4a2SCy Schubert 				ch->reg_class[0].channels = 1;
823780fb4a2SCy Schubert 				ch->reg_classes = 1;
824780fb4a2SCy Schubert 			}
825780fb4a2SCy Schubert 
826780fb4a2SCy Schubert 			p2p_channels_intersect(&p2p->channels, &dev->channels,
827780fb4a2SCy Schubert 					       &intersect);
828780fb4a2SCy Schubert 
829780fb4a2SCy Schubert 			if (intersect.reg_classes == 0) {
830780fb4a2SCy Schubert 				p2p_dbg(p2p,
831780fb4a2SCy Schubert 					"No common channels - force deferred flow");
832780fb4a2SCy Schubert 				auto_accept = 0;
833780fb4a2SCy Schubert 			}
834780fb4a2SCy Schubert 		}
835780fb4a2SCy Schubert 
8365b9c547cSRui Paulo 		if (auto_accept || reject != P2P_SC_SUCCESS) {
8375b9c547cSRui Paulo 			struct p2ps_provision *tmp;
8385b9c547cSRui Paulo 
839780fb4a2SCy Schubert 			if (p2ps_setup_p2ps_prov(p2p, adv_id, session_id,
8405b9c547cSRui Paulo 						 msg.wps_config_methods,
8415b9c547cSRui Paulo 						 session_mac, adv_mac) < 0) {
8425b9c547cSRui Paulo 				reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
8435b9c547cSRui Paulo 				goto out;
8445b9c547cSRui Paulo 			}
8455b9c547cSRui Paulo 
8465b9c547cSRui Paulo 			tmp = p2p->p2ps_prov;
847780fb4a2SCy Schubert 			tmp->force_freq = forced_freq;
848780fb4a2SCy Schubert 			tmp->pref_freq = pref_freq;
8495b9c547cSRui Paulo 			if (conncap) {
8505b9c547cSRui Paulo 				tmp->conncap = conncap;
8515b9c547cSRui Paulo 				tmp->status = P2P_SC_SUCCESS;
8525b9c547cSRui Paulo 			} else {
8535b9c547cSRui Paulo 				tmp->conncap = auto_accept;
8545b9c547cSRui Paulo 				tmp->status = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
8555b9c547cSRui Paulo 			}
8565b9c547cSRui Paulo 
8575b9c547cSRui Paulo 			if (reject != P2P_SC_SUCCESS)
8585b9c547cSRui Paulo 				goto out;
8595b9c547cSRui Paulo 		}
8605b9c547cSRui Paulo 	}
8615b9c547cSRui Paulo 
8625b9c547cSRui Paulo 	if (!msg.status && !auto_accept &&
8635b9c547cSRui Paulo 	    (!p2p->p2ps_prov || p2p->p2ps_prov->adv_id != adv_id)) {
8645b9c547cSRui Paulo 		struct p2ps_provision *tmp;
8655b9c547cSRui Paulo 
8665b9c547cSRui Paulo 		if (!conncap) {
8675b9c547cSRui Paulo 			reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
8685b9c547cSRui Paulo 			goto out;
8695b9c547cSRui Paulo 		}
8705b9c547cSRui Paulo 
8715b9c547cSRui Paulo 		if (p2ps_setup_p2ps_prov(p2p, adv_id, session_id,
8725b9c547cSRui Paulo 					 msg.wps_config_methods,
8735b9c547cSRui Paulo 					 session_mac, adv_mac) < 0) {
8745b9c547cSRui Paulo 			reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
8755b9c547cSRui Paulo 			goto out;
8765b9c547cSRui Paulo 		}
8775b9c547cSRui Paulo 		tmp = p2p->p2ps_prov;
8785b9c547cSRui Paulo 		reject = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
8795b9c547cSRui Paulo 		tmp->status = reject;
8805b9c547cSRui Paulo 	}
8815b9c547cSRui Paulo 
882780fb4a2SCy Schubert 	/* Not a P2PS Follow-on PD */
883780fb4a2SCy Schubert 	if (!msg.status)
884780fb4a2SCy Schubert 		goto out;
885780fb4a2SCy Schubert 
886780fb4a2SCy Schubert 	if (*msg.status && *msg.status != P2P_SC_SUCCESS_DEFERRED) {
8875b9c547cSRui Paulo 		reject = *msg.status;
888780fb4a2SCy Schubert 		goto out;
889780fb4a2SCy Schubert 	}
8905b9c547cSRui Paulo 
891780fb4a2SCy Schubert 	if (*msg.status != P2P_SC_SUCCESS_DEFERRED || !p2p->p2ps_prov)
892780fb4a2SCy Schubert 		goto out;
8935b9c547cSRui Paulo 
894780fb4a2SCy Schubert 	if (p2p->p2ps_prov->adv_id != adv_id ||
895*a90b9d01SCy Schubert 	    !ether_addr_equal(p2p->p2ps_prov->adv_mac, msg.adv_mac)) {
8965b9c547cSRui Paulo 		p2p_dbg(p2p,
897780fb4a2SCy Schubert 			"P2PS Follow-on PD with mismatch Advertisement ID/MAC");
898780fb4a2SCy Schubert 		goto out;
899780fb4a2SCy Schubert 	}
9005b9c547cSRui Paulo 
901780fb4a2SCy Schubert 	if (p2p->p2ps_prov->session_id != session_id ||
902*a90b9d01SCy Schubert 	    !ether_addr_equal(p2p->p2ps_prov->session_mac, msg.session_mac)) {
903780fb4a2SCy Schubert 		p2p_dbg(p2p, "P2PS Follow-on PD with mismatch Session ID/MAC");
904780fb4a2SCy Schubert 		goto out;
905780fb4a2SCy Schubert 	}
906780fb4a2SCy Schubert 
907780fb4a2SCy Schubert 	method = p2p->p2ps_prov->method;
908780fb4a2SCy Schubert 
909780fb4a2SCy Schubert 	conncap = p2p->cfg->p2ps_group_capability(p2p->cfg->cb_ctx,
910780fb4a2SCy Schubert 						  remote_conncap,
911780fb4a2SCy Schubert 						  p2p->p2ps_prov->conncap,
912780fb4a2SCy Schubert 						  &p2p->p2ps_prov->force_freq,
913780fb4a2SCy Schubert 						  &p2p->p2ps_prov->pref_freq);
914780fb4a2SCy Schubert 
915780fb4a2SCy Schubert 	resp_fcap.cpt = p2ps_own_preferred_cpt(p2p->p2ps_prov->cpt_priority,
916325151a3SRui Paulo 					       req_fcap->cpt);
917325151a3SRui Paulo 
918780fb4a2SCy Schubert 	p2p_dbg(p2p, "cpt: local:0x%x remote:0x%x result:0x%x",
919780fb4a2SCy Schubert 		p2p->p2ps_prov->cpt_mask, req_fcap->cpt, resp_fcap.cpt);
920780fb4a2SCy Schubert 
921780fb4a2SCy Schubert 	p2p_prepare_channel(p2p, dev, p2p->p2ps_prov->force_freq,
922780fb4a2SCy Schubert 			    p2p->p2ps_prov->pref_freq, 0);
923325151a3SRui Paulo 
9245b9c547cSRui Paulo 	/*
925780fb4a2SCy Schubert 	 * Ensure that if we asked for PIN originally, our method is consistent
926780fb4a2SCy Schubert 	 * with original request.
9275b9c547cSRui Paulo 	 */
9285b9c547cSRui Paulo 	if (method & WPS_CONFIG_DISPLAY)
9295b9c547cSRui Paulo 		method = WPS_CONFIG_KEYPAD;
9305b9c547cSRui Paulo 	else if (method & WPS_CONFIG_KEYPAD)
9315b9c547cSRui Paulo 		method = WPS_CONFIG_DISPLAY;
9325b9c547cSRui Paulo 
933780fb4a2SCy Schubert 	if (!conncap || !(msg.wps_config_methods & method)) {
934325151a3SRui Paulo 		/*
935325151a3SRui Paulo 		 * Reject this "Deferred Accept*
936325151a3SRui Paulo 		 * if incompatible conncap or method
937325151a3SRui Paulo 		 */
938780fb4a2SCy Schubert 		reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
939325151a3SRui Paulo 	} else if (!resp_fcap.cpt) {
940325151a3SRui Paulo 		p2p_dbg(p2p,
941325151a3SRui Paulo 			"Incompatible P2PS feature capability CPT bitmask");
942780fb4a2SCy Schubert 		reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
943780fb4a2SCy Schubert 	} else if ((remote_conncap & (P2PS_SETUP_NEW | P2PS_SETUP_CLIENT) ||
944780fb4a2SCy Schubert 		    msg.persistent_dev) && conncap != P2PS_SETUP_NEW &&
945780fb4a2SCy Schubert 		   msg.channel_list && msg.channel_list_len &&
946780fb4a2SCy Schubert 		   p2p_peer_channels_check(p2p, &p2p->channels, dev,
947780fb4a2SCy Schubert 					   msg.channel_list,
948780fb4a2SCy Schubert 					   msg.channel_list_len) < 0) {
949780fb4a2SCy Schubert 		p2p_dbg(p2p,
950780fb4a2SCy Schubert 			"No common channels in Follow-On Provision Discovery Request");
951780fb4a2SCy Schubert 		reject = P2P_SC_FAIL_NO_COMMON_CHANNELS;
952325151a3SRui Paulo 	} else {
9535b9c547cSRui Paulo 		reject = P2P_SC_SUCCESS;
954325151a3SRui Paulo 	}
9555b9c547cSRui Paulo 
956780fb4a2SCy Schubert 	dev->oper_freq = 0;
957780fb4a2SCy Schubert 	if (reject == P2P_SC_SUCCESS || reject == P2P_SC_SUCCESS_DEFERRED) {
958780fb4a2SCy Schubert 		u8 tmp;
959780fb4a2SCy Schubert 
960780fb4a2SCy Schubert 		if (msg.operating_channel)
961780fb4a2SCy Schubert 			dev->oper_freq =
962780fb4a2SCy Schubert 				p2p_channel_to_freq(msg.operating_channel[3],
963780fb4a2SCy Schubert 						    msg.operating_channel[4]);
964780fb4a2SCy Schubert 
965780fb4a2SCy Schubert 		if ((conncap & P2PS_SETUP_GROUP_OWNER) &&
966780fb4a2SCy Schubert 		    p2p_go_select_channel(p2p, dev, &tmp) < 0)
967780fb4a2SCy Schubert 			reject = P2P_SC_FAIL_NO_COMMON_CHANNELS;
968780fb4a2SCy Schubert 	}
969780fb4a2SCy Schubert 
9705b9c547cSRui Paulo 	p2p->p2ps_prov->status = reject;
9715b9c547cSRui Paulo 	p2p->p2ps_prov->conncap = conncap;
972f05cddf9SRui Paulo 
973f05cddf9SRui Paulo out:
9745b9c547cSRui Paulo 	if (reject == P2P_SC_SUCCESS ||
9755b9c547cSRui Paulo 	    reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE)
9765b9c547cSRui Paulo 		config_methods = msg.wps_config_methods;
9775b9c547cSRui Paulo 	else
9785b9c547cSRui Paulo 		config_methods = 0;
979780fb4a2SCy Schubert 
980780fb4a2SCy Schubert 	/*
981780fb4a2SCy Schubert 	 * Send PD Response for an initial PD Request or for follow-on
982780fb4a2SCy Schubert 	 * PD Request with P2P_SC_SUCCESS_DEFERRED status.
983780fb4a2SCy Schubert 	 */
984780fb4a2SCy Schubert 	if (!msg.status || *msg.status == P2P_SC_SUCCESS_DEFERRED) {
985780fb4a2SCy Schubert 		resp = p2p_build_prov_disc_resp(p2p, dev, msg.dialog_token,
986780fb4a2SCy Schubert 						reject, config_methods, adv_id,
9875b9c547cSRui Paulo 						msg.group_id, msg.group_id_len,
9885b9c547cSRui Paulo 						msg.persistent_ssid,
989325151a3SRui Paulo 						msg.persistent_ssid_len,
990325151a3SRui Paulo 						(const u8 *) &resp_fcap,
991325151a3SRui Paulo 						sizeof(resp_fcap));
992780fb4a2SCy Schubert 		if (!resp) {
993f05cddf9SRui Paulo 			p2p_parse_free(&msg);
994f05cddf9SRui Paulo 			return;
995f05cddf9SRui Paulo 		}
9965b9c547cSRui Paulo 		p2p_dbg(p2p, "Sending Provision Discovery Response");
997f05cddf9SRui Paulo 		if (rx_freq > 0)
998f05cddf9SRui Paulo 			freq = rx_freq;
999f05cddf9SRui Paulo 		else
10005b9c547cSRui Paulo 			freq = p2p_channel_to_freq(p2p->cfg->reg_class,
1001f05cddf9SRui Paulo 						   p2p->cfg->channel);
1002f05cddf9SRui Paulo 		if (freq < 0) {
10035b9c547cSRui Paulo 			p2p_dbg(p2p, "Unknown regulatory class/channel");
1004f05cddf9SRui Paulo 			wpabuf_free(resp);
1005f05cddf9SRui Paulo 			p2p_parse_free(&msg);
1006f05cddf9SRui Paulo 			return;
1007f05cddf9SRui Paulo 		}
10085b9c547cSRui Paulo 		p2p->pending_action_state = P2P_PENDING_PD_RESPONSE;
1009f05cddf9SRui Paulo 		if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr,
1010f05cddf9SRui Paulo 				    p2p->cfg->dev_addr,
1011780fb4a2SCy Schubert 				    wpabuf_head(resp), wpabuf_len(resp),
1012780fb4a2SCy Schubert 				    50) < 0)
10135b9c547cSRui Paulo 			p2p_dbg(p2p, "Failed to send Action frame");
1014780fb4a2SCy Schubert 		else
10155b9c547cSRui Paulo 			p2p->send_action_in_progress = 1;
1016f05cddf9SRui Paulo 
1017f05cddf9SRui Paulo 		wpabuf_free(resp);
1018780fb4a2SCy Schubert 	}
1019780fb4a2SCy Schubert 
1020780fb4a2SCy Schubert 	if (!dev) {
1021780fb4a2SCy Schubert 		p2p_parse_free(&msg);
1022780fb4a2SCy Schubert 		return;
1023780fb4a2SCy Schubert 	}
1024780fb4a2SCy Schubert 
1025780fb4a2SCy Schubert 	freq = 0;
1026780fb4a2SCy Schubert 	if (reject == P2P_SC_SUCCESS && conncap == P2PS_SETUP_GROUP_OWNER) {
1027780fb4a2SCy Schubert 		freq = p2p_channel_to_freq(p2p->op_reg_class,
1028780fb4a2SCy Schubert 					   p2p->op_channel);
1029780fb4a2SCy Schubert 		if (freq < 0)
1030780fb4a2SCy Schubert 			freq = 0;
1031780fb4a2SCy Schubert 	}
1032f05cddf9SRui Paulo 
10335b9c547cSRui Paulo 	if (!p2p->cfg->p2ps_prov_complete) {
10345b9c547cSRui Paulo 		/* Don't emit anything */
10355b9c547cSRui Paulo 	} else if (msg.status && *msg.status != P2P_SC_SUCCESS &&
10365b9c547cSRui Paulo 		   *msg.status != P2P_SC_SUCCESS_DEFERRED) {
10375b9c547cSRui Paulo 		reject = *msg.status;
10385b9c547cSRui Paulo 		p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, reject,
10395b9c547cSRui Paulo 					     sa, adv_mac, session_mac,
10405b9c547cSRui Paulo 					     NULL, adv_id, session_id,
10415b9c547cSRui Paulo 					     0, 0, msg.persistent_ssid,
10425b9c547cSRui Paulo 					     msg.persistent_ssid_len,
1043780fb4a2SCy Schubert 					     0, 0, NULL, NULL, 0, freq,
1044780fb4a2SCy Schubert 					     NULL, 0);
10455b9c547cSRui Paulo 	} else if (msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED &&
10465b9c547cSRui Paulo 		   p2p->p2ps_prov) {
10475b9c547cSRui Paulo 		p2p->p2ps_prov->status = reject;
10485b9c547cSRui Paulo 		p2p->p2ps_prov->conncap = conncap;
10495b9c547cSRui Paulo 
10505b9c547cSRui Paulo 		if (reject != P2P_SC_SUCCESS)
10515b9c547cSRui Paulo 			p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, reject,
10525b9c547cSRui Paulo 						     sa, adv_mac, session_mac,
10535b9c547cSRui Paulo 						     NULL, adv_id,
10545b9c547cSRui Paulo 						     session_id, conncap, 0,
10555b9c547cSRui Paulo 						     msg.persistent_ssid,
10565b9c547cSRui Paulo 						     msg.persistent_ssid_len, 0,
1057780fb4a2SCy Schubert 						     0, NULL, NULL, 0, freq,
1058780fb4a2SCy Schubert 						     NULL, 0);
10595b9c547cSRui Paulo 		else
10605b9c547cSRui Paulo 			p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx,
10615b9c547cSRui Paulo 						     *msg.status,
10625b9c547cSRui Paulo 						     sa, adv_mac, session_mac,
10635b9c547cSRui Paulo 						     group_mac, adv_id,
10645b9c547cSRui Paulo 						     session_id, conncap,
10655b9c547cSRui Paulo 						     passwd_id,
10665b9c547cSRui Paulo 						     msg.persistent_ssid,
10675b9c547cSRui Paulo 						     msg.persistent_ssid_len, 0,
1068325151a3SRui Paulo 						     0, NULL,
1069325151a3SRui Paulo 						     (const u8 *) &resp_fcap,
1070780fb4a2SCy Schubert 						     sizeof(resp_fcap), freq,
1071780fb4a2SCy Schubert 						     NULL, 0);
10725b9c547cSRui Paulo 	} else if (msg.status && p2p->p2ps_prov) {
10735b9c547cSRui Paulo 		p2p->p2ps_prov->status = P2P_SC_SUCCESS;
10745b9c547cSRui Paulo 		p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, *msg.status, sa,
10755b9c547cSRui Paulo 					     adv_mac, session_mac, group_mac,
10765b9c547cSRui Paulo 					     adv_id, session_id, conncap,
10775b9c547cSRui Paulo 					     passwd_id,
10785b9c547cSRui Paulo 					     msg.persistent_ssid,
10795b9c547cSRui Paulo 					     msg.persistent_ssid_len,
1080325151a3SRui Paulo 					     0, 0, NULL,
1081325151a3SRui Paulo 					     (const u8 *) &resp_fcap,
1082780fb4a2SCy Schubert 					     sizeof(resp_fcap), freq, NULL, 0);
10835b9c547cSRui Paulo 	} else if (msg.status) {
10845b9c547cSRui Paulo 	} else if (auto_accept && reject == P2P_SC_SUCCESS) {
10855b9c547cSRui Paulo 		p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS,
10865b9c547cSRui Paulo 					     sa, adv_mac, session_mac,
10875b9c547cSRui Paulo 					     group_mac, adv_id, session_id,
10885b9c547cSRui Paulo 					     conncap, passwd_id,
10895b9c547cSRui Paulo 					     msg.persistent_ssid,
10905b9c547cSRui Paulo 					     msg.persistent_ssid_len,
1091325151a3SRui Paulo 					     0, 0, NULL,
1092325151a3SRui Paulo 					     (const u8 *) &resp_fcap,
1093780fb4a2SCy Schubert 					     sizeof(resp_fcap), freq,
1094780fb4a2SCy Schubert 					     msg.group_id ?
1095780fb4a2SCy Schubert 					     msg.group_id + ETH_ALEN : NULL,
1096780fb4a2SCy Schubert 					     msg.group_id ?
1097780fb4a2SCy Schubert 					     msg.group_id_len - ETH_ALEN : 0);
10985b9c547cSRui Paulo 	} else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
10995b9c547cSRui Paulo 		   (!msg.session_info || !msg.session_info_len)) {
11005b9c547cSRui Paulo 		p2p->p2ps_prov->method = msg.wps_config_methods;
11015b9c547cSRui Paulo 
11025b9c547cSRui Paulo 		p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS,
11035b9c547cSRui Paulo 					     sa, adv_mac, session_mac,
11045b9c547cSRui Paulo 					     group_mac, adv_id, session_id,
11055b9c547cSRui Paulo 					     conncap, passwd_id,
11065b9c547cSRui Paulo 					     msg.persistent_ssid,
11075b9c547cSRui Paulo 					     msg.persistent_ssid_len,
1108325151a3SRui Paulo 					     0, 1, NULL,
1109325151a3SRui Paulo 					     (const u8 *) &resp_fcap,
1110780fb4a2SCy Schubert 					     sizeof(resp_fcap), freq, NULL, 0);
11115b9c547cSRui Paulo 	} else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
11125b9c547cSRui Paulo 		size_t buf_len = msg.session_info_len;
11135b9c547cSRui Paulo 		char *buf = os_malloc(2 * buf_len + 1);
11145b9c547cSRui Paulo 
11155b9c547cSRui Paulo 		if (buf) {
11165b9c547cSRui Paulo 			p2p->p2ps_prov->method = msg.wps_config_methods;
11175b9c547cSRui Paulo 
11185b9c547cSRui Paulo 			utf8_escape((char *) msg.session_info, buf_len,
11195b9c547cSRui Paulo 				    buf, 2 * buf_len + 1);
11205b9c547cSRui Paulo 
11215b9c547cSRui Paulo 			p2p->cfg->p2ps_prov_complete(
11225b9c547cSRui Paulo 				p2p->cfg->cb_ctx, P2P_SC_SUCCESS, sa,
11235b9c547cSRui Paulo 				adv_mac, session_mac, group_mac, adv_id,
11245b9c547cSRui Paulo 				session_id, conncap, passwd_id,
11255b9c547cSRui Paulo 				msg.persistent_ssid, msg.persistent_ssid_len,
1126325151a3SRui Paulo 				0, 1, buf,
1127780fb4a2SCy Schubert 				(const u8 *) &resp_fcap, sizeof(resp_fcap),
1128780fb4a2SCy Schubert 				freq, NULL, 0);
11295b9c547cSRui Paulo 
11305b9c547cSRui Paulo 			os_free(buf);
11315b9c547cSRui Paulo 		}
11325b9c547cSRui Paulo 	}
11335b9c547cSRui Paulo 
1134325151a3SRui Paulo 	/*
1135325151a3SRui Paulo 	 * prov_disc_req callback is used to generate P2P-PROV-DISC-ENTER-PIN,
1136325151a3SRui Paulo 	 * P2P-PROV-DISC-SHOW-PIN, and P2P-PROV-DISC-PBC-REQ events.
1137325151a3SRui Paulo 	 * Call it either on legacy P2P PD or on P2PS PD only if we need to
1138325151a3SRui Paulo 	 * enter/show PIN.
1139325151a3SRui Paulo 	 *
1140325151a3SRui Paulo 	 * The callback is called in the following cases:
1141325151a3SRui Paulo 	 * 1. Legacy P2P PD request, response status SUCCESS
1142325151a3SRui Paulo 	 * 2. P2PS advertiser, method: DISPLAY, autoaccept: TRUE,
1143325151a3SRui Paulo 	 *    response status: SUCCESS
1144325151a3SRui Paulo 	 * 3. P2PS advertiser, method  DISPLAY, autoaccept: FALSE,
1145325151a3SRui Paulo 	 *    response status: INFO_CURRENTLY_UNAVAILABLE
1146325151a3SRui Paulo 	 * 4. P2PS advertiser, method: KEYPAD, autoaccept==any,
1147325151a3SRui Paulo 	 *    response status: INFO_CURRENTLY_UNAVAILABLE
1148325151a3SRui Paulo 	 * 5. P2PS follow-on with SUCCESS_DEFERRED,
1149325151a3SRui Paulo 	 *    advertiser role: DISPLAY, autoaccept: FALSE,
1150325151a3SRui Paulo 	 *    seeker: KEYPAD, response status: SUCCESS
1151325151a3SRui Paulo 	 */
1152325151a3SRui Paulo 	if (p2p->cfg->prov_disc_req &&
1153325151a3SRui Paulo 	    ((reject == P2P_SC_SUCCESS && !msg.adv_id) ||
1154325151a3SRui Paulo 	     (!msg.status &&
1155325151a3SRui Paulo 	     (reject == P2P_SC_SUCCESS ||
1156325151a3SRui Paulo 	      reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) &&
1157325151a3SRui Paulo 	      passwd_id == DEV_PW_USER_SPECIFIED) ||
1158325151a3SRui Paulo 	     (!msg.status &&
1159325151a3SRui Paulo 	      reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
1160325151a3SRui Paulo 	      passwd_id == DEV_PW_REGISTRAR_SPECIFIED) ||
1161325151a3SRui Paulo 	     (reject == P2P_SC_SUCCESS &&
1162325151a3SRui Paulo 	      msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED &&
1163325151a3SRui Paulo 	       passwd_id == DEV_PW_REGISTRAR_SPECIFIED))) {
1164f05cddf9SRui Paulo 		const u8 *dev_addr = sa;
1165325151a3SRui Paulo 
1166f05cddf9SRui Paulo 		if (msg.p2p_device_addr)
1167f05cddf9SRui Paulo 			dev_addr = msg.p2p_device_addr;
1168f05cddf9SRui Paulo 		p2p->cfg->prov_disc_req(p2p->cfg->cb_ctx, sa,
1169f05cddf9SRui Paulo 					msg.wps_config_methods,
1170f05cddf9SRui Paulo 					dev_addr, msg.pri_dev_type,
1171f05cddf9SRui Paulo 					msg.device_name, msg.config_methods,
1172f05cddf9SRui Paulo 					msg.capability ? msg.capability[0] : 0,
1173f05cddf9SRui Paulo 					msg.capability ? msg.capability[1] :
1174f05cddf9SRui Paulo 					0,
1175f05cddf9SRui Paulo 					msg.group_id, msg.group_id_len);
1176f05cddf9SRui Paulo 	}
1177325151a3SRui Paulo 
117885732ac8SCy Schubert 	if (reject != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE)
117985732ac8SCy Schubert 		p2ps_prov_free(p2p);
118085732ac8SCy Schubert 
1181780fb4a2SCy Schubert 	if (reject == P2P_SC_SUCCESS) {
1182325151a3SRui Paulo 		switch (config_methods) {
1183325151a3SRui Paulo 		case WPS_CONFIG_DISPLAY:
1184325151a3SRui Paulo 			dev->wps_prov_info = WPS_CONFIG_KEYPAD;
1185325151a3SRui Paulo 			break;
1186325151a3SRui Paulo 		case WPS_CONFIG_KEYPAD:
1187325151a3SRui Paulo 			dev->wps_prov_info = WPS_CONFIG_DISPLAY;
1188325151a3SRui Paulo 			break;
1189325151a3SRui Paulo 		case WPS_CONFIG_PUSHBUTTON:
1190325151a3SRui Paulo 			dev->wps_prov_info = WPS_CONFIG_PUSHBUTTON;
1191325151a3SRui Paulo 			break;
1192325151a3SRui Paulo 		case WPS_CONFIG_P2PS:
1193325151a3SRui Paulo 			dev->wps_prov_info = WPS_CONFIG_P2PS;
1194325151a3SRui Paulo 			break;
1195325151a3SRui Paulo 		default:
1196325151a3SRui Paulo 			dev->wps_prov_info = 0;
1197325151a3SRui Paulo 			break;
1198325151a3SRui Paulo 		}
1199325151a3SRui Paulo 
1200325151a3SRui Paulo 		if (msg.intended_addr)
1201325151a3SRui Paulo 			os_memcpy(dev->interface_addr, msg.intended_addr,
1202325151a3SRui Paulo 				  ETH_ALEN);
1203325151a3SRui Paulo 	}
1204f05cddf9SRui Paulo 	p2p_parse_free(&msg);
1205f05cddf9SRui Paulo }
1206f05cddf9SRui Paulo 
1207f05cddf9SRui Paulo 
p2p_validate_p2ps_pd_resp(struct p2p_data * p2p,struct p2p_message * msg)1208325151a3SRui Paulo static int p2p_validate_p2ps_pd_resp(struct p2p_data *p2p,
1209325151a3SRui Paulo 				     struct p2p_message *msg)
1210325151a3SRui Paulo {
1211325151a3SRui Paulo 	u8 conn_cap_go = 0;
1212325151a3SRui Paulo 	u8 conn_cap_cli = 0;
1213325151a3SRui Paulo 	u32 session_id;
1214325151a3SRui Paulo 	u32 adv_id;
1215325151a3SRui Paulo 
1216325151a3SRui Paulo #define P2PS_PD_RESP_CHECK(_val, _attr) \
1217325151a3SRui Paulo 	do { \
1218325151a3SRui Paulo 		if ((_val) && !msg->_attr) { \
1219325151a3SRui Paulo 			p2p_dbg(p2p, "P2PS PD Response missing " #_attr); \
1220325151a3SRui Paulo 			return -1; \
1221325151a3SRui Paulo 		} \
1222325151a3SRui Paulo 	} while (0)
1223325151a3SRui Paulo 
1224325151a3SRui Paulo 	P2PS_PD_RESP_CHECK(1, status);
1225325151a3SRui Paulo 	P2PS_PD_RESP_CHECK(1, adv_id);
1226325151a3SRui Paulo 	P2PS_PD_RESP_CHECK(1, adv_mac);
1227325151a3SRui Paulo 	P2PS_PD_RESP_CHECK(1, capability);
1228325151a3SRui Paulo 	P2PS_PD_RESP_CHECK(1, p2p_device_info);
1229325151a3SRui Paulo 	P2PS_PD_RESP_CHECK(1, session_id);
1230325151a3SRui Paulo 	P2PS_PD_RESP_CHECK(1, session_mac);
1231325151a3SRui Paulo 	P2PS_PD_RESP_CHECK(1, feature_cap);
1232325151a3SRui Paulo 
1233325151a3SRui Paulo 	session_id = WPA_GET_LE32(msg->session_id);
1234325151a3SRui Paulo 	adv_id = WPA_GET_LE32(msg->adv_id);
1235325151a3SRui Paulo 
1236325151a3SRui Paulo 	if (p2p->p2ps_prov->session_id != session_id) {
1237325151a3SRui Paulo 		p2p_dbg(p2p,
1238325151a3SRui Paulo 			"Ignore PD Response with unexpected Session ID");
1239325151a3SRui Paulo 		return -1;
1240325151a3SRui Paulo 	}
1241325151a3SRui Paulo 
1242*a90b9d01SCy Schubert 	if (!ether_addr_equal(p2p->p2ps_prov->session_mac, msg->session_mac)) {
1243325151a3SRui Paulo 		p2p_dbg(p2p,
1244325151a3SRui Paulo 			"Ignore PD Response with unexpected Session MAC");
1245325151a3SRui Paulo 		return -1;
1246325151a3SRui Paulo 	}
1247325151a3SRui Paulo 
1248325151a3SRui Paulo 	if (p2p->p2ps_prov->adv_id != adv_id) {
1249325151a3SRui Paulo 		p2p_dbg(p2p,
1250325151a3SRui Paulo 			"Ignore PD Response with unexpected Advertisement ID");
1251325151a3SRui Paulo 		return -1;
1252325151a3SRui Paulo 	}
1253325151a3SRui Paulo 
1254*a90b9d01SCy Schubert 	if (!ether_addr_equal(p2p->p2ps_prov->adv_mac, msg->adv_mac)) {
1255325151a3SRui Paulo 		p2p_dbg(p2p,
1256325151a3SRui Paulo 			"Ignore PD Response with unexpected Advertisement MAC");
1257325151a3SRui Paulo 		return -1;
1258325151a3SRui Paulo 	}
1259325151a3SRui Paulo 
1260325151a3SRui Paulo 	if (msg->listen_channel) {
1261325151a3SRui Paulo 		p2p_dbg(p2p,
1262325151a3SRui Paulo 			"Ignore malformed PD Response - unexpected Listen Channel");
1263325151a3SRui Paulo 		return -1;
1264325151a3SRui Paulo 	}
1265325151a3SRui Paulo 
1266325151a3SRui Paulo 	if (*msg->status == P2P_SC_SUCCESS &&
1267325151a3SRui Paulo 	    !(!!msg->conn_cap ^ !!msg->persistent_dev)) {
1268325151a3SRui Paulo 		p2p_dbg(p2p,
1269325151a3SRui Paulo 			"Ignore malformed PD Response - either conn_cap or persistent group should be present");
1270325151a3SRui Paulo 		return -1;
1271325151a3SRui Paulo 	}
1272325151a3SRui Paulo 
1273325151a3SRui Paulo 	if (msg->persistent_dev && *msg->status != P2P_SC_SUCCESS) {
1274325151a3SRui Paulo 		p2p_dbg(p2p,
1275325151a3SRui Paulo 			"Ignore malformed PD Response - persistent group is present, but the status isn't success");
1276325151a3SRui Paulo 		return -1;
1277325151a3SRui Paulo 	}
1278325151a3SRui Paulo 
1279325151a3SRui Paulo 	if (msg->conn_cap) {
1280325151a3SRui Paulo 		conn_cap_go = *msg->conn_cap == P2PS_SETUP_GROUP_OWNER;
1281325151a3SRui Paulo 		conn_cap_cli = *msg->conn_cap == P2PS_SETUP_CLIENT;
1282325151a3SRui Paulo 	}
1283325151a3SRui Paulo 
1284325151a3SRui Paulo 	P2PS_PD_RESP_CHECK(msg->persistent_dev || conn_cap_go || conn_cap_cli,
1285325151a3SRui Paulo 			   channel_list);
1286325151a3SRui Paulo 	P2PS_PD_RESP_CHECK(msg->persistent_dev || conn_cap_go || conn_cap_cli,
1287325151a3SRui Paulo 			   config_timeout);
1288325151a3SRui Paulo 
1289325151a3SRui Paulo 	P2PS_PD_RESP_CHECK(conn_cap_go, group_id);
1290325151a3SRui Paulo 	P2PS_PD_RESP_CHECK(conn_cap_go, intended_addr);
1291325151a3SRui Paulo 	P2PS_PD_RESP_CHECK(conn_cap_go, operating_channel);
1292325151a3SRui Paulo 	/*
1293325151a3SRui Paulo 	 * TODO: Also validate that operating channel is present if the device
1294325151a3SRui Paulo 	 * is a GO in a persistent group. We can't do it here since we don't
1295325151a3SRui Paulo 	 * know what is the role of the peer. It should be probably done in
1296325151a3SRui Paulo 	 * p2ps_prov_complete callback, but currently operating channel isn't
1297325151a3SRui Paulo 	 * passed to it.
1298325151a3SRui Paulo 	 */
1299325151a3SRui Paulo 
1300325151a3SRui Paulo #undef P2PS_PD_RESP_CHECK
1301325151a3SRui Paulo 
1302325151a3SRui Paulo 	return 0;
1303325151a3SRui Paulo }
1304325151a3SRui Paulo 
1305325151a3SRui Paulo 
p2p_process_prov_disc_resp(struct p2p_data * p2p,const u8 * sa,const u8 * data,size_t len)1306f05cddf9SRui Paulo void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa,
1307f05cddf9SRui Paulo 				const u8 *data, size_t len)
1308f05cddf9SRui Paulo {
1309f05cddf9SRui Paulo 	struct p2p_message msg;
1310f05cddf9SRui Paulo 	struct p2p_device *dev;
13115b9c547cSRui Paulo 	u16 report_config_methods = 0, req_config_methods;
13125b9c547cSRui Paulo 	u8 status = P2P_SC_SUCCESS;
13135b9c547cSRui Paulo 	u32 adv_id = 0;
13145b9c547cSRui Paulo 	u8 conncap = P2PS_SETUP_NEW;
13155b9c547cSRui Paulo 	u8 adv_mac[ETH_ALEN];
1316325151a3SRui Paulo 	const u8 *group_mac;
13175b9c547cSRui Paulo 	int passwd_id = DEV_PW_DEFAULT;
1318325151a3SRui Paulo 	int p2ps_seeker;
1319f05cddf9SRui Paulo 
1320f05cddf9SRui Paulo 	if (p2p_parse(data, len, &msg))
1321f05cddf9SRui Paulo 		return;
1322f05cddf9SRui Paulo 
1323325151a3SRui Paulo 	if (p2p->p2ps_prov && p2p_validate_p2ps_pd_resp(p2p, &msg)) {
1324325151a3SRui Paulo 		p2p_parse_free(&msg);
1325325151a3SRui Paulo 		return;
1326325151a3SRui Paulo 	}
1327325151a3SRui Paulo 
13285b9c547cSRui Paulo 	/* Parse the P2PS members present */
13295b9c547cSRui Paulo 	if (msg.status)
13305b9c547cSRui Paulo 		status = *msg.status;
13315b9c547cSRui Paulo 
1332325151a3SRui Paulo 	group_mac = msg.intended_addr;
13335b9c547cSRui Paulo 
13345b9c547cSRui Paulo 	if (msg.adv_mac)
13355b9c547cSRui Paulo 		os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN);
13365b9c547cSRui Paulo 	else
13375b9c547cSRui Paulo 		os_memset(adv_mac, 0, ETH_ALEN);
13385b9c547cSRui Paulo 
13395b9c547cSRui Paulo 	if (msg.adv_id)
13405b9c547cSRui Paulo 		adv_id = WPA_GET_LE32(msg.adv_id);
13415b9c547cSRui Paulo 
13425b9c547cSRui Paulo 	if (msg.conn_cap) {
13435b9c547cSRui Paulo 		conncap = *msg.conn_cap;
13445b9c547cSRui Paulo 
13455b9c547cSRui Paulo 		/* Switch bits to local relative */
13465b9c547cSRui Paulo 		switch (conncap) {
13475b9c547cSRui Paulo 		case P2PS_SETUP_GROUP_OWNER:
13485b9c547cSRui Paulo 			conncap = P2PS_SETUP_CLIENT;
13495b9c547cSRui Paulo 			break;
13505b9c547cSRui Paulo 		case P2PS_SETUP_CLIENT:
13515b9c547cSRui Paulo 			conncap = P2PS_SETUP_GROUP_OWNER;
13525b9c547cSRui Paulo 			break;
13535b9c547cSRui Paulo 		}
13545b9c547cSRui Paulo 	}
13555b9c547cSRui Paulo 
13565b9c547cSRui Paulo 	p2p_dbg(p2p, "Received Provision Discovery Response from " MACSTR
1357f05cddf9SRui Paulo 		" with config methods 0x%x",
1358f05cddf9SRui Paulo 		MAC2STR(sa), msg.wps_config_methods);
1359f05cddf9SRui Paulo 
1360f05cddf9SRui Paulo 	dev = p2p_get_device(p2p, sa);
1361f05cddf9SRui Paulo 	if (dev == NULL || !dev->req_config_methods) {
13625b9c547cSRui Paulo 		p2p_dbg(p2p, "Ignore Provision Discovery Response from " MACSTR
13635b9c547cSRui Paulo 			" with no pending request", MAC2STR(sa));
1364f05cddf9SRui Paulo 		p2p_parse_free(&msg);
1365f05cddf9SRui Paulo 		return;
1366780fb4a2SCy Schubert 	} else if (msg.wfd_subelems) {
1367780fb4a2SCy Schubert 		wpabuf_free(dev->info.wfd_subelems);
1368780fb4a2SCy Schubert 		dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
1369f05cddf9SRui Paulo 	}
1370f05cddf9SRui Paulo 
1371*a90b9d01SCy Schubert 	p2p_update_peer_6ghz_capab(dev, &msg);
1372*a90b9d01SCy Schubert 
1373f05cddf9SRui Paulo 	if (dev->dialog_token != msg.dialog_token) {
13745b9c547cSRui Paulo 		p2p_dbg(p2p, "Ignore Provision Discovery Response with unexpected Dialog Token %u (expected %u)",
1375f05cddf9SRui Paulo 			msg.dialog_token, dev->dialog_token);
1376f05cddf9SRui Paulo 		p2p_parse_free(&msg);
1377f05cddf9SRui Paulo 		return;
1378f05cddf9SRui Paulo 	}
1379f05cddf9SRui Paulo 
1380f05cddf9SRui Paulo 	if (p2p->pending_action_state == P2P_PENDING_PD) {
1381f05cddf9SRui Paulo 		os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN);
1382f05cddf9SRui Paulo 		p2p->pending_action_state = P2P_NO_PENDING_ACTION;
1383f05cddf9SRui Paulo 	}
1384f05cddf9SRui Paulo 
1385325151a3SRui Paulo 	p2ps_seeker = p2p->p2ps_prov && p2p->p2ps_prov->pd_seeker;
1386325151a3SRui Paulo 
1387f05cddf9SRui Paulo 	/*
13885b9c547cSRui Paulo 	 * Use a local copy of the requested config methods since
13895b9c547cSRui Paulo 	 * p2p_reset_pending_pd() can clear this in the peer entry.
13905b9c547cSRui Paulo 	 */
13915b9c547cSRui Paulo 	req_config_methods = dev->req_config_methods;
13925b9c547cSRui Paulo 
13935b9c547cSRui Paulo 	/*
1394f05cddf9SRui Paulo 	 * If the response is from the peer to whom a user initiated request
1395f05cddf9SRui Paulo 	 * was sent earlier, we reset that state info here.
1396f05cddf9SRui Paulo 	 */
1397f05cddf9SRui Paulo 	if (p2p->user_initiated_pd &&
1398*a90b9d01SCy Schubert 	    ether_addr_equal(p2p->pending_pd_devaddr, sa))
1399f05cddf9SRui Paulo 		p2p_reset_pending_pd(p2p);
1400f05cddf9SRui Paulo 
14015b9c547cSRui Paulo 	if (msg.wps_config_methods != req_config_methods) {
14025b9c547cSRui Paulo 		p2p_dbg(p2p, "Peer rejected our Provision Discovery Request (received config_methods 0x%x expected 0x%x",
14035b9c547cSRui Paulo 			msg.wps_config_methods, req_config_methods);
1404f05cddf9SRui Paulo 		if (p2p->cfg->prov_disc_fail)
1405f05cddf9SRui Paulo 			p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa,
14065b9c547cSRui Paulo 						 P2P_PROV_DISC_REJECTED,
14075b9c547cSRui Paulo 						 adv_id, adv_mac, NULL);
1408f05cddf9SRui Paulo 		p2p_parse_free(&msg);
1409325151a3SRui Paulo 		p2ps_prov_free(p2p);
1410f05cddf9SRui Paulo 		goto out;
1411f05cddf9SRui Paulo 	}
1412f05cddf9SRui Paulo 
14135b9c547cSRui Paulo 	report_config_methods = req_config_methods;
1414f05cddf9SRui Paulo 	dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
14155b9c547cSRui Paulo 			P2P_DEV_PD_PEER_KEYPAD |
14165b9c547cSRui Paulo 			P2P_DEV_PD_PEER_P2PS);
14175b9c547cSRui Paulo 	if (req_config_methods & WPS_CONFIG_DISPLAY) {
14185b9c547cSRui Paulo 		p2p_dbg(p2p, "Peer " MACSTR
1419f05cddf9SRui Paulo 			" accepted to show a PIN on display", MAC2STR(sa));
1420f05cddf9SRui Paulo 		dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
14215b9c547cSRui Paulo 		passwd_id = DEV_PW_REGISTRAR_SPECIFIED;
1422f05cddf9SRui Paulo 	} else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
14235b9c547cSRui Paulo 		p2p_dbg(p2p, "Peer " MACSTR
1424f05cddf9SRui Paulo 			" accepted to write our PIN using keypad",
1425f05cddf9SRui Paulo 			MAC2STR(sa));
1426f05cddf9SRui Paulo 		dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
14275b9c547cSRui Paulo 		passwd_id = DEV_PW_USER_SPECIFIED;
14285b9c547cSRui Paulo 	} else if (msg.wps_config_methods & WPS_CONFIG_P2PS) {
14295b9c547cSRui Paulo 		p2p_dbg(p2p, "Peer " MACSTR " accepted P2PS PIN",
14305b9c547cSRui Paulo 			MAC2STR(sa));
14315b9c547cSRui Paulo 		dev->flags |= P2P_DEV_PD_PEER_P2PS;
14325b9c547cSRui Paulo 		passwd_id = DEV_PW_P2PS_DEFAULT;
14335b9c547cSRui Paulo 	}
14345b9c547cSRui Paulo 
1435780fb4a2SCy Schubert 	if ((status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED) &&
14365b9c547cSRui Paulo 	    p2p->p2ps_prov) {
1437780fb4a2SCy Schubert 		dev->oper_freq = 0;
1438780fb4a2SCy Schubert 
1439780fb4a2SCy Schubert 		/*
1440780fb4a2SCy Schubert 		 * Save the reported channel list and operating frequency.
1441780fb4a2SCy Schubert 		 * Note that the specification mandates that the responder
1442780fb4a2SCy Schubert 		 * should include in the channel list only channels reported by
14434b72b91aSCy Schubert 		 * the initiator, so this is only a validity check, and if this
1444780fb4a2SCy Schubert 		 * fails the flow would continue, although it would probably
1445780fb4a2SCy Schubert 		 * fail. Same is true for the operating channel.
1446780fb4a2SCy Schubert 		 */
1447780fb4a2SCy Schubert 		if (msg.channel_list && msg.channel_list_len &&
1448780fb4a2SCy Schubert 		    p2p_peer_channels_check(p2p, &p2p->channels, dev,
1449780fb4a2SCy Schubert 					    msg.channel_list,
1450780fb4a2SCy Schubert 					    msg.channel_list_len) < 0)
1451780fb4a2SCy Schubert 			p2p_dbg(p2p, "P2PS PD Response - no common channels");
1452780fb4a2SCy Schubert 
1453780fb4a2SCy Schubert 		if (msg.operating_channel) {
1454780fb4a2SCy Schubert 			if (p2p_channels_includes(&p2p->channels,
1455780fb4a2SCy Schubert 						  msg.operating_channel[3],
1456780fb4a2SCy Schubert 						  msg.operating_channel[4]) &&
1457780fb4a2SCy Schubert 			    p2p_channels_includes(&dev->channels,
1458780fb4a2SCy Schubert 						  msg.operating_channel[3],
1459780fb4a2SCy Schubert 						  msg.operating_channel[4])) {
1460780fb4a2SCy Schubert 				dev->oper_freq =
1461780fb4a2SCy Schubert 					p2p_channel_to_freq(
1462780fb4a2SCy Schubert 						msg.operating_channel[3],
1463780fb4a2SCy Schubert 						msg.operating_channel[4]);
1464780fb4a2SCy Schubert 			} else {
1465780fb4a2SCy Schubert 				p2p_dbg(p2p,
1466780fb4a2SCy Schubert 					"P2PS PD Response - invalid operating channel");
1467780fb4a2SCy Schubert 			}
1468780fb4a2SCy Schubert 		}
1469780fb4a2SCy Schubert 
14705b9c547cSRui Paulo 		if (p2p->cfg->p2ps_prov_complete) {
1471780fb4a2SCy Schubert 			int freq = 0;
1472780fb4a2SCy Schubert 
1473780fb4a2SCy Schubert 			if (conncap == P2PS_SETUP_GROUP_OWNER) {
1474780fb4a2SCy Schubert 				u8 tmp;
1475780fb4a2SCy Schubert 
1476780fb4a2SCy Schubert 				/*
1477780fb4a2SCy Schubert 				 * Re-select the operating channel as it is
1478780fb4a2SCy Schubert 				 * possible that original channel is no longer
1479780fb4a2SCy Schubert 				 * valid. This should not really fail.
1480780fb4a2SCy Schubert 				 */
1481780fb4a2SCy Schubert 				if (p2p_go_select_channel(p2p, dev, &tmp) < 0)
1482780fb4a2SCy Schubert 					p2p_dbg(p2p,
1483780fb4a2SCy Schubert 						"P2PS PD channel selection failed");
1484780fb4a2SCy Schubert 
1485780fb4a2SCy Schubert 				freq = p2p_channel_to_freq(p2p->op_reg_class,
1486780fb4a2SCy Schubert 							   p2p->op_channel);
1487780fb4a2SCy Schubert 				if (freq < 0)
1488780fb4a2SCy Schubert 					freq = 0;
1489780fb4a2SCy Schubert 			}
1490780fb4a2SCy Schubert 
14915b9c547cSRui Paulo 			p2p->cfg->p2ps_prov_complete(
14925b9c547cSRui Paulo 				p2p->cfg->cb_ctx, status, sa, adv_mac,
14935b9c547cSRui Paulo 				p2p->p2ps_prov->session_mac,
14945b9c547cSRui Paulo 				group_mac, adv_id, p2p->p2ps_prov->session_id,
14955b9c547cSRui Paulo 				conncap, passwd_id, msg.persistent_ssid,
1496325151a3SRui Paulo 				msg.persistent_ssid_len, 1, 0, NULL,
1497780fb4a2SCy Schubert 				msg.feature_cap, msg.feature_cap_len, freq,
1498780fb4a2SCy Schubert 				msg.group_id ? msg.group_id + ETH_ALEN : NULL,
1499780fb4a2SCy Schubert 				msg.group_id ? msg.group_id_len - ETH_ALEN : 0);
15005b9c547cSRui Paulo 		}
1501325151a3SRui Paulo 		p2ps_prov_free(p2p);
1502325151a3SRui Paulo 	} else if (status != P2P_SC_SUCCESS &&
15035b9c547cSRui Paulo 		   status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
15045b9c547cSRui Paulo 		   status != P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) {
15055b9c547cSRui Paulo 		if (p2p->cfg->p2ps_prov_complete)
15065b9c547cSRui Paulo 			p2p->cfg->p2ps_prov_complete(
15075b9c547cSRui Paulo 				p2p->cfg->cb_ctx, status, sa, adv_mac,
15085b9c547cSRui Paulo 				p2p->p2ps_prov->session_mac,
15095b9c547cSRui Paulo 				group_mac, adv_id, p2p->p2ps_prov->session_id,
1510780fb4a2SCy Schubert 				0, 0, NULL, 0, 1, 0, NULL, NULL, 0, 0, NULL, 0);
1511325151a3SRui Paulo 		p2ps_prov_free(p2p);
15125b9c547cSRui Paulo 	}
15135b9c547cSRui Paulo 
15145b9c547cSRui Paulo 	if (status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
15155b9c547cSRui Paulo 		if (p2p->cfg->remove_stale_groups) {
15165b9c547cSRui Paulo 			p2p->cfg->remove_stale_groups(p2p->cfg->cb_ctx,
15175b9c547cSRui Paulo 						      dev->info.p2p_device_addr,
15185b9c547cSRui Paulo 						      NULL, NULL, 0);
15195b9c547cSRui Paulo 		}
15205b9c547cSRui Paulo 
15215b9c547cSRui Paulo 		if (msg.session_info && msg.session_info_len) {
15225b9c547cSRui Paulo 			size_t info_len = msg.session_info_len;
15235b9c547cSRui Paulo 			char *deferred_sess_resp = os_malloc(2 * info_len + 1);
15245b9c547cSRui Paulo 
15255b9c547cSRui Paulo 			if (!deferred_sess_resp) {
15265b9c547cSRui Paulo 				p2p_parse_free(&msg);
1527325151a3SRui Paulo 				p2ps_prov_free(p2p);
15285b9c547cSRui Paulo 				goto out;
15295b9c547cSRui Paulo 			}
15305b9c547cSRui Paulo 			utf8_escape((char *) msg.session_info, info_len,
15315b9c547cSRui Paulo 				    deferred_sess_resp, 2 * info_len + 1);
15325b9c547cSRui Paulo 
15335b9c547cSRui Paulo 			if (p2p->cfg->prov_disc_fail)
15345b9c547cSRui Paulo 				p2p->cfg->prov_disc_fail(
15355b9c547cSRui Paulo 					p2p->cfg->cb_ctx, sa,
15365b9c547cSRui Paulo 					P2P_PROV_DISC_INFO_UNAVAILABLE,
15375b9c547cSRui Paulo 					adv_id, adv_mac,
15385b9c547cSRui Paulo 					deferred_sess_resp);
15395b9c547cSRui Paulo 			os_free(deferred_sess_resp);
15405b9c547cSRui Paulo 		} else
15415b9c547cSRui Paulo 			if (p2p->cfg->prov_disc_fail)
15425b9c547cSRui Paulo 				p2p->cfg->prov_disc_fail(
15435b9c547cSRui Paulo 					p2p->cfg->cb_ctx, sa,
15445b9c547cSRui Paulo 					P2P_PROV_DISC_INFO_UNAVAILABLE,
15455b9c547cSRui Paulo 					adv_id, adv_mac, NULL);
1546325151a3SRui Paulo 	} else if (status != P2P_SC_SUCCESS) {
15475b9c547cSRui Paulo 		p2p_dbg(p2p, "Peer rejected our Provision Discovery Request");
15485b9c547cSRui Paulo 		if (p2p->cfg->prov_disc_fail)
15495b9c547cSRui Paulo 			p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa,
1550325151a3SRui Paulo 						 P2P_PROV_DISC_REJECTED,
1551325151a3SRui Paulo 						 adv_id, adv_mac, NULL);
15525b9c547cSRui Paulo 		p2p_parse_free(&msg);
1553325151a3SRui Paulo 		p2ps_prov_free(p2p);
15545b9c547cSRui Paulo 		goto out;
1555f05cddf9SRui Paulo 	}
1556f05cddf9SRui Paulo 
1557f05cddf9SRui Paulo 	/* Store the provisioning info */
1558f05cddf9SRui Paulo 	dev->wps_prov_info = msg.wps_config_methods;
1559325151a3SRui Paulo 	if (msg.intended_addr)
1560325151a3SRui Paulo 		os_memcpy(dev->interface_addr, msg.intended_addr, ETH_ALEN);
1561f05cddf9SRui Paulo 
1562f05cddf9SRui Paulo 	p2p_parse_free(&msg);
1563f05cddf9SRui Paulo 
1564f05cddf9SRui Paulo out:
1565f05cddf9SRui Paulo 	dev->req_config_methods = 0;
1566f05cddf9SRui Paulo 	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
1567f05cddf9SRui Paulo 	if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG) {
15685b9c547cSRui Paulo 		p2p_dbg(p2p, "Start GO Neg after the PD-before-GO-Neg workaround with "
15695b9c547cSRui Paulo 			MACSTR, MAC2STR(dev->info.p2p_device_addr));
1570f05cddf9SRui Paulo 		dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG;
1571f05cddf9SRui Paulo 		p2p_connect_send(p2p, dev);
1572f05cddf9SRui Paulo 		return;
1573f05cddf9SRui Paulo 	}
1574325151a3SRui Paulo 
1575325151a3SRui Paulo 	/*
1576325151a3SRui Paulo 	 * prov_disc_resp callback is used to generate P2P-PROV-DISC-ENTER-PIN,
1577325151a3SRui Paulo 	 * P2P-PROV-DISC-SHOW-PIN, and P2P-PROV-DISC-PBC-REQ events.
1578325151a3SRui Paulo 	 * Call it only for a legacy P2P PD or for P2PS PD scenarios where
1579325151a3SRui Paulo 	 * show/enter PIN events are needed.
1580325151a3SRui Paulo 	 *
1581325151a3SRui Paulo 	 * The callback is called in the following cases:
1582325151a3SRui Paulo 	 * 1. Legacy P2P PD response with a status SUCCESS
1583325151a3SRui Paulo 	 * 2. P2PS, advertiser method: DISPLAY, autoaccept: true,
1584325151a3SRui Paulo 	 *    response status: SUCCESS, local method KEYPAD
1585325151a3SRui Paulo 	 * 3. P2PS, advertiser method: KEYPAD,Seeker side,
1586325151a3SRui Paulo 	 *    response status: INFO_CURRENTLY_UNAVAILABLE,
1587325151a3SRui Paulo 	 *    local method: DISPLAY
1588325151a3SRui Paulo 	 */
1589325151a3SRui Paulo 	if (p2p->cfg->prov_disc_resp &&
1590325151a3SRui Paulo 	    ((status == P2P_SC_SUCCESS && !adv_id) ||
1591325151a3SRui Paulo 	     (p2ps_seeker && status == P2P_SC_SUCCESS &&
1592325151a3SRui Paulo 	      passwd_id == DEV_PW_REGISTRAR_SPECIFIED) ||
1593325151a3SRui Paulo 	     (p2ps_seeker &&
1594325151a3SRui Paulo 	      status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
1595325151a3SRui Paulo 	      passwd_id == DEV_PW_USER_SPECIFIED)))
1596f05cddf9SRui Paulo 		p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa,
1597f05cddf9SRui Paulo 					 report_config_methods);
15985b9c547cSRui Paulo 
15995b9c547cSRui Paulo 	if (p2p->state == P2P_PD_DURING_FIND) {
160085732ac8SCy Schubert 		p2p_stop_listen_for_freq(p2p, 0);
16015b9c547cSRui Paulo 		p2p_continue_find(p2p);
16025b9c547cSRui Paulo 	}
1603f05cddf9SRui Paulo }
1604f05cddf9SRui Paulo 
1605f05cddf9SRui Paulo 
p2p_send_prov_disc_req(struct p2p_data * p2p,struct p2p_device * dev,int join,int force_freq)1606f05cddf9SRui Paulo int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev,
1607f05cddf9SRui Paulo 			   int join, int force_freq)
1608f05cddf9SRui Paulo {
1609f05cddf9SRui Paulo 	struct wpabuf *req;
1610f05cddf9SRui Paulo 	int freq;
1611f05cddf9SRui Paulo 
1612f05cddf9SRui Paulo 	if (force_freq > 0)
1613f05cddf9SRui Paulo 		freq = force_freq;
1614f05cddf9SRui Paulo 	else
1615f05cddf9SRui Paulo 		freq = dev->listen_freq > 0 ? dev->listen_freq :
1616f05cddf9SRui Paulo 			dev->oper_freq;
1617f05cddf9SRui Paulo 	if (freq <= 0) {
16185b9c547cSRui Paulo 		p2p_dbg(p2p, "No Listen/Operating frequency known for the peer "
16195b9c547cSRui Paulo 			MACSTR " to send Provision Discovery Request",
1620f05cddf9SRui Paulo 			MAC2STR(dev->info.p2p_device_addr));
1621f05cddf9SRui Paulo 		return -1;
1622f05cddf9SRui Paulo 	}
1623f05cddf9SRui Paulo 
1624f05cddf9SRui Paulo 	if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
1625f05cddf9SRui Paulo 		if (!(dev->info.dev_capab &
1626f05cddf9SRui Paulo 		      P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
16275b9c547cSRui Paulo 			p2p_dbg(p2p, "Cannot use PD with P2P Device " MACSTR
1628f05cddf9SRui Paulo 				" that is in a group and is not discoverable",
1629f05cddf9SRui Paulo 				MAC2STR(dev->info.p2p_device_addr));
1630f05cddf9SRui Paulo 			return -1;
1631f05cddf9SRui Paulo 		}
1632f05cddf9SRui Paulo 		/* TODO: use device discoverability request through GO */
1633f05cddf9SRui Paulo 	}
1634f05cddf9SRui Paulo 
16355b9c547cSRui Paulo 	if (p2p->p2ps_prov) {
16365b9c547cSRui Paulo 		if (p2p->p2ps_prov->status == P2P_SC_SUCCESS_DEFERRED) {
16375b9c547cSRui Paulo 			if (p2p->p2ps_prov->method == WPS_CONFIG_DISPLAY)
16385b9c547cSRui Paulo 				dev->req_config_methods = WPS_CONFIG_KEYPAD;
16395b9c547cSRui Paulo 			else if (p2p->p2ps_prov->method == WPS_CONFIG_KEYPAD)
16405b9c547cSRui Paulo 				dev->req_config_methods = WPS_CONFIG_DISPLAY;
16415b9c547cSRui Paulo 			else
16425b9c547cSRui Paulo 				dev->req_config_methods = WPS_CONFIG_P2PS;
16435b9c547cSRui Paulo 		} else {
16445b9c547cSRui Paulo 			/* Order of preference, based on peer's capabilities */
16455b9c547cSRui Paulo 			if (p2p->p2ps_prov->method)
16465b9c547cSRui Paulo 				dev->req_config_methods =
16475b9c547cSRui Paulo 					p2p->p2ps_prov->method;
16485b9c547cSRui Paulo 			else if (dev->info.config_methods & WPS_CONFIG_P2PS)
16495b9c547cSRui Paulo 				dev->req_config_methods = WPS_CONFIG_P2PS;
16505b9c547cSRui Paulo 			else if (dev->info.config_methods & WPS_CONFIG_DISPLAY)
16515b9c547cSRui Paulo 				dev->req_config_methods = WPS_CONFIG_DISPLAY;
16525b9c547cSRui Paulo 			else
16535b9c547cSRui Paulo 				dev->req_config_methods = WPS_CONFIG_KEYPAD;
16545b9c547cSRui Paulo 		}
16555b9c547cSRui Paulo 		p2p_dbg(p2p,
16565b9c547cSRui Paulo 			"Building PD Request based on P2PS config method 0x%x status %d --> req_config_methods 0x%x",
16575b9c547cSRui Paulo 			p2p->p2ps_prov->method, p2p->p2ps_prov->status,
16585b9c547cSRui Paulo 			dev->req_config_methods);
1659780fb4a2SCy Schubert 
1660780fb4a2SCy Schubert 		if (p2p_prepare_channel(p2p, dev, p2p->p2ps_prov->force_freq,
1661780fb4a2SCy Schubert 					p2p->p2ps_prov->pref_freq, 1) < 0)
1662780fb4a2SCy Schubert 			return -1;
16635b9c547cSRui Paulo 	}
16645b9c547cSRui Paulo 
16655b9c547cSRui Paulo 	req = p2p_build_prov_disc_req(p2p, dev, join);
1666f05cddf9SRui Paulo 	if (req == NULL)
1667f05cddf9SRui Paulo 		return -1;
1668f05cddf9SRui Paulo 
1669f05cddf9SRui Paulo 	if (p2p->state != P2P_IDLE)
1670f05cddf9SRui Paulo 		p2p_stop_listen_for_freq(p2p, freq);
1671f05cddf9SRui Paulo 	p2p->pending_action_state = P2P_PENDING_PD;
1672f05cddf9SRui Paulo 	if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
1673f05cddf9SRui Paulo 			    p2p->cfg->dev_addr, dev->info.p2p_device_addr,
1674f05cddf9SRui Paulo 			    wpabuf_head(req), wpabuf_len(req), 200) < 0) {
16755b9c547cSRui Paulo 		p2p_dbg(p2p, "Failed to send Action frame");
1676f05cddf9SRui Paulo 		wpabuf_free(req);
1677f05cddf9SRui Paulo 		return -1;
1678f05cddf9SRui Paulo 	}
1679f05cddf9SRui Paulo 
1680f05cddf9SRui Paulo 	os_memcpy(p2p->pending_pd_devaddr, dev->info.p2p_device_addr, ETH_ALEN);
1681f05cddf9SRui Paulo 
1682f05cddf9SRui Paulo 	wpabuf_free(req);
1683f05cddf9SRui Paulo 	return 0;
1684f05cddf9SRui Paulo }
1685f05cddf9SRui Paulo 
1686f05cddf9SRui Paulo 
p2p_prov_disc_req(struct p2p_data * p2p,const u8 * peer_addr,struct p2ps_provision * p2ps_prov,u16 config_methods,int join,int force_freq,int user_initiated_pd)1687f05cddf9SRui Paulo int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr,
16885b9c547cSRui Paulo 		      struct p2ps_provision *p2ps_prov,
1689f05cddf9SRui Paulo 		      u16 config_methods, int join, int force_freq,
1690f05cddf9SRui Paulo 		      int user_initiated_pd)
1691f05cddf9SRui Paulo {
1692f05cddf9SRui Paulo 	struct p2p_device *dev;
1693f05cddf9SRui Paulo 
1694f05cddf9SRui Paulo 	dev = p2p_get_device(p2p, peer_addr);
1695f05cddf9SRui Paulo 	if (dev == NULL)
1696f05cddf9SRui Paulo 		dev = p2p_get_device_interface(p2p, peer_addr);
1697f05cddf9SRui Paulo 	if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
16985b9c547cSRui Paulo 		p2p_dbg(p2p, "Provision Discovery Request destination " MACSTR
1699f05cddf9SRui Paulo 			" not yet known", MAC2STR(peer_addr));
17005b9c547cSRui Paulo 		os_free(p2ps_prov);
1701f05cddf9SRui Paulo 		return -1;
1702f05cddf9SRui Paulo 	}
1703f05cddf9SRui Paulo 
17045b9c547cSRui Paulo 	p2p_dbg(p2p, "Provision Discovery Request with " MACSTR
17055b9c547cSRui Paulo 		" (config methods 0x%x)",
1706f05cddf9SRui Paulo 		MAC2STR(peer_addr), config_methods);
17075b9c547cSRui Paulo 	if (config_methods == 0 && !p2ps_prov) {
17085b9c547cSRui Paulo 		os_free(p2ps_prov);
1709f05cddf9SRui Paulo 		return -1;
17105b9c547cSRui Paulo 	}
17115b9c547cSRui Paulo 
17125b9c547cSRui Paulo 	if (p2ps_prov && p2ps_prov->status == P2P_SC_SUCCESS_DEFERRED &&
17135b9c547cSRui Paulo 	    p2p->p2ps_prov) {
17145b9c547cSRui Paulo 		/* Use cached method from deferred provisioning */
17155b9c547cSRui Paulo 		p2ps_prov->method = p2p->p2ps_prov->method;
17165b9c547cSRui Paulo 	}
1717f05cddf9SRui Paulo 
1718f05cddf9SRui Paulo 	/* Reset provisioning info */
1719f05cddf9SRui Paulo 	dev->wps_prov_info = 0;
1720325151a3SRui Paulo 	p2ps_prov_free(p2p);
17215b9c547cSRui Paulo 	p2p->p2ps_prov = p2ps_prov;
1722f05cddf9SRui Paulo 
1723f05cddf9SRui Paulo 	dev->req_config_methods = config_methods;
1724f05cddf9SRui Paulo 	if (join)
1725f05cddf9SRui Paulo 		dev->flags |= P2P_DEV_PD_FOR_JOIN;
1726f05cddf9SRui Paulo 	else
1727f05cddf9SRui Paulo 		dev->flags &= ~P2P_DEV_PD_FOR_JOIN;
1728f05cddf9SRui Paulo 
1729f05cddf9SRui Paulo 	if (p2p->state != P2P_IDLE && p2p->state != P2P_SEARCH &&
1730f05cddf9SRui Paulo 	    p2p->state != P2P_LISTEN_ONLY) {
17315b9c547cSRui Paulo 		p2p_dbg(p2p, "Busy with other operations; postpone Provision Discovery Request with "
17325b9c547cSRui Paulo 			MACSTR " (config methods 0x%x)",
1733f05cddf9SRui Paulo 			MAC2STR(peer_addr), config_methods);
1734f05cddf9SRui Paulo 		return 0;
1735f05cddf9SRui Paulo 	}
1736f05cddf9SRui Paulo 
1737f05cddf9SRui Paulo 	p2p->user_initiated_pd = user_initiated_pd;
17385b9c547cSRui Paulo 	p2p->pd_force_freq = force_freq;
1739f05cddf9SRui Paulo 
1740f05cddf9SRui Paulo 	if (p2p->user_initiated_pd)
1741f05cddf9SRui Paulo 		p2p->pd_retries = MAX_PROV_DISC_REQ_RETRIES;
1742f05cddf9SRui Paulo 
1743f05cddf9SRui Paulo 	/*
1744f05cddf9SRui Paulo 	 * Assign dialog token here to use the same value in each retry within
1745f05cddf9SRui Paulo 	 * the same PD exchange.
1746f05cddf9SRui Paulo 	 */
1747f05cddf9SRui Paulo 	dev->dialog_token++;
1748f05cddf9SRui Paulo 	if (dev->dialog_token == 0)
1749f05cddf9SRui Paulo 		dev->dialog_token = 1;
1750f05cddf9SRui Paulo 
1751f05cddf9SRui Paulo 	return p2p_send_prov_disc_req(p2p, dev, join, force_freq);
1752f05cddf9SRui Paulo }
1753f05cddf9SRui Paulo 
1754f05cddf9SRui Paulo 
p2p_reset_pending_pd(struct p2p_data * p2p)1755f05cddf9SRui Paulo void p2p_reset_pending_pd(struct p2p_data *p2p)
1756f05cddf9SRui Paulo {
1757f05cddf9SRui Paulo 	struct p2p_device *dev;
1758f05cddf9SRui Paulo 
1759f05cddf9SRui Paulo 	dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
1760*a90b9d01SCy Schubert 		if (!ether_addr_equal(p2p->pending_pd_devaddr,
1761*a90b9d01SCy Schubert 				      dev->info.p2p_device_addr))
1762f05cddf9SRui Paulo 			continue;
1763f05cddf9SRui Paulo 		if (!dev->req_config_methods)
1764f05cddf9SRui Paulo 			continue;
1765f05cddf9SRui Paulo 		if (dev->flags & P2P_DEV_PD_FOR_JOIN)
1766f05cddf9SRui Paulo 			continue;
1767f05cddf9SRui Paulo 		/* Reset the config methods of the device */
1768f05cddf9SRui Paulo 		dev->req_config_methods = 0;
1769f05cddf9SRui Paulo 	}
1770f05cddf9SRui Paulo 
1771f05cddf9SRui Paulo 	p2p->user_initiated_pd = 0;
1772f05cddf9SRui Paulo 	os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN);
1773f05cddf9SRui Paulo 	p2p->pd_retries = 0;
17745b9c547cSRui Paulo 	p2p->pd_force_freq = 0;
1775f05cddf9SRui Paulo }
1776325151a3SRui Paulo 
1777325151a3SRui Paulo 
p2ps_prov_free(struct p2p_data * p2p)1778325151a3SRui Paulo void p2ps_prov_free(struct p2p_data *p2p)
1779325151a3SRui Paulo {
1780325151a3SRui Paulo 	os_free(p2p->p2ps_prov);
1781325151a3SRui Paulo 	p2p->p2ps_prov = NULL;
1782325151a3SRui Paulo }
1783