xref: /dflybsd-src/contrib/wpa_supplicant/src/p2p/p2p_pd.c (revision 3a84a4273475ed07d0ab1c2dfeffdfedef35d9cd)
13ff40c12SJohn Marino /*
23ff40c12SJohn Marino  * Wi-Fi Direct - P2P provision discovery
33ff40c12SJohn Marino  * Copyright (c) 2009-2010, Atheros Communications
43ff40c12SJohn Marino  *
53ff40c12SJohn Marino  * This software may be distributed under the terms of the BSD license.
63ff40c12SJohn Marino  * See README for more details.
73ff40c12SJohn Marino  */
83ff40c12SJohn Marino 
93ff40c12SJohn Marino #include "includes.h"
103ff40c12SJohn Marino 
113ff40c12SJohn Marino #include "common.h"
123ff40c12SJohn Marino #include "common/ieee802_11_defs.h"
13*a1157835SDaniel Fojt #include "common/wpa_ctrl.h"
143ff40c12SJohn Marino #include "wps/wps_defs.h"
153ff40c12SJohn Marino #include "p2p_i.h"
163ff40c12SJohn Marino #include "p2p.h"
173ff40c12SJohn Marino 
183ff40c12SJohn Marino 
193ff40c12SJohn Marino /*
203ff40c12SJohn Marino  * Number of retries to attempt for provision discovery requests
213ff40c12SJohn Marino  * in case the peer is not listening.
223ff40c12SJohn Marino  */
233ff40c12SJohn Marino #define MAX_PROV_DISC_REQ_RETRIES 120
243ff40c12SJohn Marino 
253ff40c12SJohn Marino 
p2p_build_wps_ie_config_methods(struct wpabuf * buf,u16 config_methods)263ff40c12SJohn Marino static void p2p_build_wps_ie_config_methods(struct wpabuf *buf,
273ff40c12SJohn Marino 					    u16 config_methods)
283ff40c12SJohn Marino {
293ff40c12SJohn Marino 	u8 *len;
303ff40c12SJohn Marino 	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
313ff40c12SJohn Marino 	len = wpabuf_put(buf, 1);
323ff40c12SJohn Marino 	wpabuf_put_be32(buf, WPS_DEV_OUI_WFA);
333ff40c12SJohn Marino 
343ff40c12SJohn Marino 	/* Config Methods */
353ff40c12SJohn Marino 	wpabuf_put_be16(buf, ATTR_CONFIG_METHODS);
363ff40c12SJohn Marino 	wpabuf_put_be16(buf, 2);
373ff40c12SJohn Marino 	wpabuf_put_be16(buf, config_methods);
383ff40c12SJohn Marino 
393ff40c12SJohn Marino 	p2p_buf_update_ie_hdr(buf, len);
403ff40c12SJohn Marino }
413ff40c12SJohn Marino 
423ff40c12SJohn Marino 
p2ps_add_new_group_info(struct p2p_data * p2p,struct p2p_device * dev,struct wpabuf * buf)43*a1157835SDaniel Fojt static void p2ps_add_new_group_info(struct p2p_data *p2p,
44*a1157835SDaniel Fojt 				    struct p2p_device *dev,
45*a1157835SDaniel Fojt 				    struct wpabuf *buf)
46*a1157835SDaniel Fojt {
47*a1157835SDaniel Fojt 	int found;
48*a1157835SDaniel Fojt 	u8 intended_addr[ETH_ALEN];
49*a1157835SDaniel Fojt 	u8 ssid[SSID_MAX_LEN];
50*a1157835SDaniel Fojt 	size_t ssid_len;
51*a1157835SDaniel Fojt 	int group_iface;
52*a1157835SDaniel Fojt 	unsigned int force_freq;
53*a1157835SDaniel Fojt 
54*a1157835SDaniel Fojt 	if (!p2p->cfg->get_go_info)
55*a1157835SDaniel Fojt 		return;
56*a1157835SDaniel Fojt 
57*a1157835SDaniel Fojt 	found = p2p->cfg->get_go_info(
58*a1157835SDaniel Fojt 		p2p->cfg->cb_ctx, intended_addr, ssid,
59*a1157835SDaniel Fojt 		&ssid_len, &group_iface, &force_freq);
60*a1157835SDaniel Fojt 	if (found) {
61*a1157835SDaniel Fojt 		if (force_freq > 0) {
62*a1157835SDaniel Fojt 			p2p->p2ps_prov->force_freq = force_freq;
63*a1157835SDaniel Fojt 			p2p->p2ps_prov->pref_freq = 0;
64*a1157835SDaniel Fojt 
65*a1157835SDaniel Fojt 			if (dev)
66*a1157835SDaniel Fojt 				p2p_prepare_channel(p2p, dev, force_freq, 0, 0);
67*a1157835SDaniel Fojt 		}
68*a1157835SDaniel Fojt 		p2p_buf_add_group_id(buf, p2p->cfg->dev_addr,
69*a1157835SDaniel Fojt 				     ssid, ssid_len);
70*a1157835SDaniel Fojt 
71*a1157835SDaniel Fojt 		if (group_iface)
72*a1157835SDaniel Fojt 			p2p_buf_add_intended_addr(buf, p2p->intended_addr);
73*a1157835SDaniel Fojt 		else
74*a1157835SDaniel Fojt 			p2p_buf_add_intended_addr(buf, intended_addr);
75*a1157835SDaniel Fojt 	} else {
76*a1157835SDaniel Fojt 		if (!p2p->ssid_set) {
77*a1157835SDaniel Fojt 			p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len);
78*a1157835SDaniel Fojt 			p2p->ssid_set = 1;
79*a1157835SDaniel Fojt 		}
80*a1157835SDaniel Fojt 
81*a1157835SDaniel Fojt 		/* Add pre-composed P2P Group ID */
82*a1157835SDaniel Fojt 		p2p_buf_add_group_id(buf, p2p->cfg->dev_addr,
83*a1157835SDaniel Fojt 				     p2p->ssid, p2p->ssid_len);
84*a1157835SDaniel Fojt 
85*a1157835SDaniel Fojt 		if (group_iface)
86*a1157835SDaniel Fojt 			p2p_buf_add_intended_addr(
87*a1157835SDaniel Fojt 				buf, p2p->intended_addr);
88*a1157835SDaniel Fojt 		else
89*a1157835SDaniel Fojt 			p2p_buf_add_intended_addr(
90*a1157835SDaniel Fojt 				buf, p2p->cfg->dev_addr);
91*a1157835SDaniel Fojt 	}
92*a1157835SDaniel Fojt }
93*a1157835SDaniel Fojt 
94*a1157835SDaniel Fojt 
p2ps_add_pd_req_attrs(struct p2p_data * p2p,struct p2p_device * dev,struct wpabuf * buf,u16 config_methods)95*a1157835SDaniel Fojt static void p2ps_add_pd_req_attrs(struct p2p_data *p2p, struct p2p_device *dev,
96*a1157835SDaniel Fojt 				  struct wpabuf *buf, u16 config_methods)
97*a1157835SDaniel Fojt {
98*a1157835SDaniel Fojt 	struct p2ps_provision *prov = p2p->p2ps_prov;
99*a1157835SDaniel Fojt 	struct p2ps_feature_capab fcap = { prov->cpt_mask, 0 };
100*a1157835SDaniel Fojt 	int shared_group = 0;
101*a1157835SDaniel Fojt 	u8 ssid[SSID_MAX_LEN];
102*a1157835SDaniel Fojt 	size_t ssid_len;
103*a1157835SDaniel Fojt 	u8 go_dev_addr[ETH_ALEN];
104*a1157835SDaniel Fojt 	u8 intended_addr[ETH_ALEN];
105*a1157835SDaniel Fojt 	int follow_on_req_fail = prov->status >= 0 &&
106*a1157835SDaniel Fojt 		prov->status != P2P_SC_SUCCESS_DEFERRED;
107*a1157835SDaniel Fojt 
108*a1157835SDaniel Fojt 	/* If we might be explicite group owner, add GO details */
109*a1157835SDaniel Fojt 	if (!follow_on_req_fail &&
110*a1157835SDaniel Fojt 	    (prov->conncap & (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW)))
111*a1157835SDaniel Fojt 		p2ps_add_new_group_info(p2p, dev, buf);
112*a1157835SDaniel Fojt 
113*a1157835SDaniel Fojt 	if (prov->status >= 0)
114*a1157835SDaniel Fojt 		p2p_buf_add_status(buf, (u8) prov->status);
115*a1157835SDaniel Fojt 	else
116*a1157835SDaniel Fojt 		prov->method = config_methods;
117*a1157835SDaniel Fojt 
118*a1157835SDaniel Fojt 	if (!follow_on_req_fail) {
119*a1157835SDaniel Fojt 		if (p2p->cfg->get_persistent_group) {
120*a1157835SDaniel Fojt 			shared_group = p2p->cfg->get_persistent_group(
121*a1157835SDaniel Fojt 				p2p->cfg->cb_ctx, dev->info.p2p_device_addr,
122*a1157835SDaniel Fojt 				NULL, 0, go_dev_addr, ssid, &ssid_len,
123*a1157835SDaniel Fojt 				intended_addr);
124*a1157835SDaniel Fojt 		}
125*a1157835SDaniel Fojt 
126*a1157835SDaniel Fojt 		if (shared_group ||
127*a1157835SDaniel Fojt 		    (prov->conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_NEW)))
128*a1157835SDaniel Fojt 			p2p_buf_add_channel_list(buf, p2p->cfg->country,
129*a1157835SDaniel Fojt 						 &p2p->channels);
130*a1157835SDaniel Fojt 
131*a1157835SDaniel Fojt 		if ((shared_group && !is_zero_ether_addr(intended_addr)) ||
132*a1157835SDaniel Fojt 		    (prov->conncap & (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW)))
133*a1157835SDaniel Fojt 			p2p_buf_add_operating_channel(buf, p2p->cfg->country,
134*a1157835SDaniel Fojt 						      p2p->op_reg_class,
135*a1157835SDaniel Fojt 						      p2p->op_channel);
136*a1157835SDaniel Fojt 	}
137*a1157835SDaniel Fojt 
138*a1157835SDaniel Fojt 	if (prov->status < 0 && prov->info[0])
139*a1157835SDaniel Fojt 		p2p_buf_add_session_info(buf, prov->info);
140*a1157835SDaniel Fojt 
141*a1157835SDaniel Fojt 	if (!follow_on_req_fail)
142*a1157835SDaniel Fojt 		p2p_buf_add_connection_capability(buf, prov->conncap);
143*a1157835SDaniel Fojt 
144*a1157835SDaniel Fojt 	p2p_buf_add_advertisement_id(buf, prov->adv_id, prov->adv_mac);
145*a1157835SDaniel Fojt 
146*a1157835SDaniel Fojt 	if (!follow_on_req_fail) {
147*a1157835SDaniel Fojt 		if (shared_group || prov->conncap == P2PS_SETUP_NEW ||
148*a1157835SDaniel Fojt 		    prov->conncap ==
149*a1157835SDaniel Fojt 		    (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW) ||
150*a1157835SDaniel Fojt 		    prov->conncap ==
151*a1157835SDaniel Fojt 		    (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT)) {
152*a1157835SDaniel Fojt 			/* Add Config Timeout */
153*a1157835SDaniel Fojt 			p2p_buf_add_config_timeout(buf, p2p->go_timeout,
154*a1157835SDaniel Fojt 						   p2p->client_timeout);
155*a1157835SDaniel Fojt 		}
156*a1157835SDaniel Fojt 
157*a1157835SDaniel Fojt 		p2p_buf_add_listen_channel(buf, p2p->cfg->country,
158*a1157835SDaniel Fojt 					   p2p->cfg->reg_class,
159*a1157835SDaniel Fojt 					   p2p->cfg->channel);
160*a1157835SDaniel Fojt 	}
161*a1157835SDaniel Fojt 
162*a1157835SDaniel Fojt 	p2p_buf_add_session_id(buf, prov->session_id, prov->session_mac);
163*a1157835SDaniel Fojt 
164*a1157835SDaniel Fojt 	p2p_buf_add_feature_capability(buf, sizeof(fcap), (const u8 *) &fcap);
165*a1157835SDaniel Fojt 
166*a1157835SDaniel Fojt 	if (shared_group) {
167*a1157835SDaniel Fojt 		p2p_buf_add_persistent_group_info(buf, go_dev_addr,
168*a1157835SDaniel Fojt 						  ssid, ssid_len);
169*a1157835SDaniel Fojt 		/* Add intended interface address if it is not added yet */
170*a1157835SDaniel Fojt 		if ((prov->conncap == P2PS_SETUP_NONE ||
171*a1157835SDaniel Fojt 		     prov->conncap == P2PS_SETUP_CLIENT) &&
172*a1157835SDaniel Fojt 		    !is_zero_ether_addr(intended_addr))
173*a1157835SDaniel Fojt 			p2p_buf_add_intended_addr(buf, intended_addr);
174*a1157835SDaniel Fojt 	}
175*a1157835SDaniel Fojt }
176*a1157835SDaniel Fojt 
177*a1157835SDaniel Fojt 
p2p_build_prov_disc_req(struct p2p_data * p2p,struct p2p_device * dev,int join)1783ff40c12SJohn Marino static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p,
179*a1157835SDaniel Fojt 					       struct p2p_device *dev,
180*a1157835SDaniel Fojt 					       int join)
1813ff40c12SJohn Marino {
1823ff40c12SJohn Marino 	struct wpabuf *buf;
1833ff40c12SJohn Marino 	u8 *len;
1843ff40c12SJohn Marino 	size_t extra = 0;
185*a1157835SDaniel Fojt 	u8 dialog_token = dev->dialog_token;
186*a1157835SDaniel Fojt 	u16 config_methods = dev->req_config_methods;
187*a1157835SDaniel Fojt 	struct p2p_device *go = join ? dev : NULL;
188*a1157835SDaniel Fojt 	u8 group_capab;
1893ff40c12SJohn Marino 
1903ff40c12SJohn Marino #ifdef CONFIG_WIFI_DISPLAY
1913ff40c12SJohn Marino 	if (p2p->wfd_ie_prov_disc_req)
1923ff40c12SJohn Marino 		extra = wpabuf_len(p2p->wfd_ie_prov_disc_req);
1933ff40c12SJohn Marino #endif /* CONFIG_WIFI_DISPLAY */
1943ff40c12SJohn Marino 
195*a1157835SDaniel Fojt 	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ])
196*a1157835SDaniel Fojt 		extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]);
197*a1157835SDaniel Fojt 
198*a1157835SDaniel Fojt 	if (p2p->p2ps_prov)
199*a1157835SDaniel Fojt 		extra += os_strlen(p2p->p2ps_prov->info) + 1 +
200*a1157835SDaniel Fojt 			sizeof(struct p2ps_provision);
201*a1157835SDaniel Fojt 
2023ff40c12SJohn Marino 	buf = wpabuf_alloc(1000 + extra);
2033ff40c12SJohn Marino 	if (buf == NULL)
2043ff40c12SJohn Marino 		return NULL;
2053ff40c12SJohn Marino 
2063ff40c12SJohn Marino 	p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_REQ, dialog_token);
2073ff40c12SJohn Marino 
2083ff40c12SJohn Marino 	len = p2p_buf_add_ie_hdr(buf);
209*a1157835SDaniel Fojt 
210*a1157835SDaniel Fojt 	group_capab = 0;
211*a1157835SDaniel Fojt 	if (p2p->p2ps_prov) {
212*a1157835SDaniel Fojt 		group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
213*a1157835SDaniel Fojt 		group_capab |= P2P_GROUP_CAPAB_PERSISTENT_RECONN;
214*a1157835SDaniel Fojt 		if (p2p->cross_connect)
215*a1157835SDaniel Fojt 			group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
216*a1157835SDaniel Fojt 		if (p2p->cfg->p2p_intra_bss)
217*a1157835SDaniel Fojt 			group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
218*a1157835SDaniel Fojt 	}
2193ff40c12SJohn Marino 	p2p_buf_add_capability(buf, p2p->dev_capab &
220*a1157835SDaniel Fojt 			       ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY,
221*a1157835SDaniel Fojt 			       group_capab);
2223ff40c12SJohn Marino 	p2p_buf_add_device_info(buf, p2p, NULL);
223*a1157835SDaniel Fojt 	if (p2p->p2ps_prov) {
224*a1157835SDaniel Fojt 		p2ps_add_pd_req_attrs(p2p, dev, buf, config_methods);
225*a1157835SDaniel Fojt 	} else if (go) {
2263ff40c12SJohn Marino 		p2p_buf_add_group_id(buf, go->info.p2p_device_addr,
2273ff40c12SJohn Marino 				     go->oper_ssid, go->oper_ssid_len);
2283ff40c12SJohn Marino 	}
2293ff40c12SJohn Marino 	p2p_buf_update_ie_hdr(buf, len);
2303ff40c12SJohn Marino 
2313ff40c12SJohn Marino 	/* WPS IE with Config Methods attribute */
2323ff40c12SJohn Marino 	p2p_build_wps_ie_config_methods(buf, config_methods);
2333ff40c12SJohn Marino 
2343ff40c12SJohn Marino #ifdef CONFIG_WIFI_DISPLAY
2353ff40c12SJohn Marino 	if (p2p->wfd_ie_prov_disc_req)
2363ff40c12SJohn Marino 		wpabuf_put_buf(buf, p2p->wfd_ie_prov_disc_req);
2373ff40c12SJohn Marino #endif /* CONFIG_WIFI_DISPLAY */
2383ff40c12SJohn Marino 
239*a1157835SDaniel Fojt 	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ])
240*a1157835SDaniel Fojt 		wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]);
241*a1157835SDaniel Fojt 
2423ff40c12SJohn Marino 	return buf;
2433ff40c12SJohn Marino }
2443ff40c12SJohn Marino 
2453ff40c12SJohn Marino 
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)2463ff40c12SJohn Marino static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p,
247*a1157835SDaniel Fojt 						struct p2p_device *dev,
2483ff40c12SJohn Marino 						u8 dialog_token,
249*a1157835SDaniel Fojt 						enum p2p_status_code status,
2503ff40c12SJohn Marino 						u16 config_methods,
251*a1157835SDaniel Fojt 						u32 adv_id,
2523ff40c12SJohn Marino 						const u8 *group_id,
253*a1157835SDaniel Fojt 						size_t group_id_len,
254*a1157835SDaniel Fojt 						const u8 *persist_ssid,
255*a1157835SDaniel Fojt 						size_t persist_ssid_len,
256*a1157835SDaniel Fojt 						const u8 *fcap,
257*a1157835SDaniel Fojt 						u16 fcap_len)
2583ff40c12SJohn Marino {
2593ff40c12SJohn Marino 	struct wpabuf *buf;
2603ff40c12SJohn Marino 	size_t extra = 0;
261*a1157835SDaniel Fojt 	int persist = 0;
2623ff40c12SJohn Marino 
2633ff40c12SJohn Marino #ifdef CONFIG_WIFI_DISPLAY
2643ff40c12SJohn Marino 	struct wpabuf *wfd_ie = p2p->wfd_ie_prov_disc_resp;
2653ff40c12SJohn Marino 	if (wfd_ie && group_id) {
2663ff40c12SJohn Marino 		size_t i;
2673ff40c12SJohn Marino 		for (i = 0; i < p2p->num_groups; i++) {
2683ff40c12SJohn Marino 			struct p2p_group *g = p2p->groups[i];
2693ff40c12SJohn Marino 			struct wpabuf *ie;
2703ff40c12SJohn Marino 			if (!p2p_group_is_group_id_match(g, group_id,
2713ff40c12SJohn Marino 							 group_id_len))
2723ff40c12SJohn Marino 				continue;
2733ff40c12SJohn Marino 			ie = p2p_group_get_wfd_ie(g);
2743ff40c12SJohn Marino 			if (ie) {
2753ff40c12SJohn Marino 				wfd_ie = ie;
2763ff40c12SJohn Marino 				break;
2773ff40c12SJohn Marino 			}
2783ff40c12SJohn Marino 		}
2793ff40c12SJohn Marino 	}
2803ff40c12SJohn Marino 	if (wfd_ie)
2813ff40c12SJohn Marino 		extra = wpabuf_len(wfd_ie);
2823ff40c12SJohn Marino #endif /* CONFIG_WIFI_DISPLAY */
2833ff40c12SJohn Marino 
284*a1157835SDaniel Fojt 	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP])
285*a1157835SDaniel Fojt 		extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]);
286*a1157835SDaniel Fojt 
287*a1157835SDaniel Fojt 	buf = wpabuf_alloc(1000 + extra);
2883ff40c12SJohn Marino 	if (buf == NULL)
2893ff40c12SJohn Marino 		return NULL;
2903ff40c12SJohn Marino 
2913ff40c12SJohn Marino 	p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_RESP, dialog_token);
2923ff40c12SJohn Marino 
293*a1157835SDaniel Fojt 	/* Add P2P IE for P2PS */
294*a1157835SDaniel Fojt 	if (p2p->p2ps_prov && p2p->p2ps_prov->adv_id == adv_id) {
295*a1157835SDaniel Fojt 		u8 *len = p2p_buf_add_ie_hdr(buf);
296*a1157835SDaniel Fojt 		struct p2ps_provision *prov = p2p->p2ps_prov;
297*a1157835SDaniel Fojt 		u8 group_capab;
298*a1157835SDaniel Fojt 		u8 conncap = 0;
299*a1157835SDaniel Fojt 
300*a1157835SDaniel Fojt 		if (status == P2P_SC_SUCCESS ||
301*a1157835SDaniel Fojt 		    status == P2P_SC_SUCCESS_DEFERRED)
302*a1157835SDaniel Fojt 			conncap = prov->conncap;
303*a1157835SDaniel Fojt 
304*a1157835SDaniel Fojt 		if (!status && prov->status != -1)
305*a1157835SDaniel Fojt 			status = prov->status;
306*a1157835SDaniel Fojt 
307*a1157835SDaniel Fojt 		p2p_buf_add_status(buf, status);
308*a1157835SDaniel Fojt 		group_capab = P2P_GROUP_CAPAB_PERSISTENT_GROUP |
309*a1157835SDaniel Fojt 			P2P_GROUP_CAPAB_PERSISTENT_RECONN;
310*a1157835SDaniel Fojt 		if (p2p->cross_connect)
311*a1157835SDaniel Fojt 			group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
312*a1157835SDaniel Fojt 		if (p2p->cfg->p2p_intra_bss)
313*a1157835SDaniel Fojt 			group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
314*a1157835SDaniel Fojt 		p2p_buf_add_capability(buf, p2p->dev_capab &
315*a1157835SDaniel Fojt 				       ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY,
316*a1157835SDaniel Fojt 				       group_capab);
317*a1157835SDaniel Fojt 		p2p_buf_add_device_info(buf, p2p, NULL);
318*a1157835SDaniel Fojt 
319*a1157835SDaniel Fojt 		if (persist_ssid && p2p->cfg->get_persistent_group && dev &&
320*a1157835SDaniel Fojt 		    (status == P2P_SC_SUCCESS ||
321*a1157835SDaniel Fojt 		     status == P2P_SC_SUCCESS_DEFERRED)) {
322*a1157835SDaniel Fojt 			u8 ssid[SSID_MAX_LEN];
323*a1157835SDaniel Fojt 			size_t ssid_len;
324*a1157835SDaniel Fojt 			u8 go_dev_addr[ETH_ALEN];
325*a1157835SDaniel Fojt 			u8 intended_addr[ETH_ALEN];
326*a1157835SDaniel Fojt 
327*a1157835SDaniel Fojt 			persist = p2p->cfg->get_persistent_group(
328*a1157835SDaniel Fojt 				p2p->cfg->cb_ctx,
329*a1157835SDaniel Fojt 				dev->info.p2p_device_addr,
330*a1157835SDaniel Fojt 				persist_ssid, persist_ssid_len, go_dev_addr,
331*a1157835SDaniel Fojt 				ssid, &ssid_len, intended_addr);
332*a1157835SDaniel Fojt 			if (persist) {
333*a1157835SDaniel Fojt 				p2p_buf_add_persistent_group_info(
334*a1157835SDaniel Fojt 					buf, go_dev_addr, ssid, ssid_len);
335*a1157835SDaniel Fojt 				if (!is_zero_ether_addr(intended_addr))
336*a1157835SDaniel Fojt 					p2p_buf_add_intended_addr(
337*a1157835SDaniel Fojt 						buf, intended_addr);
338*a1157835SDaniel Fojt 			}
339*a1157835SDaniel Fojt 		}
340*a1157835SDaniel Fojt 
341*a1157835SDaniel Fojt 		if (!persist && (conncap & P2PS_SETUP_GROUP_OWNER))
342*a1157835SDaniel Fojt 			p2ps_add_new_group_info(p2p, dev, buf);
343*a1157835SDaniel Fojt 
344*a1157835SDaniel Fojt 		/* Add Operating Channel if conncap indicates GO */
345*a1157835SDaniel Fojt 		if (persist || (conncap & P2PS_SETUP_GROUP_OWNER)) {
346*a1157835SDaniel Fojt 			if (p2p->op_reg_class && p2p->op_channel)
347*a1157835SDaniel Fojt 				p2p_buf_add_operating_channel(
348*a1157835SDaniel Fojt 					buf, p2p->cfg->country,
349*a1157835SDaniel Fojt 					p2p->op_reg_class,
350*a1157835SDaniel Fojt 					p2p->op_channel);
351*a1157835SDaniel Fojt 			else
352*a1157835SDaniel Fojt 				p2p_buf_add_operating_channel(
353*a1157835SDaniel Fojt 					buf, p2p->cfg->country,
354*a1157835SDaniel Fojt 					p2p->cfg->op_reg_class,
355*a1157835SDaniel Fojt 					p2p->cfg->op_channel);
356*a1157835SDaniel Fojt 		}
357*a1157835SDaniel Fojt 
358*a1157835SDaniel Fojt 		if (persist ||
359*a1157835SDaniel Fojt 		    (conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER)))
360*a1157835SDaniel Fojt 			p2p_buf_add_channel_list(buf, p2p->cfg->country,
361*a1157835SDaniel Fojt 						 &p2p->channels);
362*a1157835SDaniel Fojt 
363*a1157835SDaniel Fojt 		if (!persist && conncap)
364*a1157835SDaniel Fojt 			p2p_buf_add_connection_capability(buf, conncap);
365*a1157835SDaniel Fojt 
366*a1157835SDaniel Fojt 		p2p_buf_add_advertisement_id(buf, adv_id, prov->adv_mac);
367*a1157835SDaniel Fojt 
368*a1157835SDaniel Fojt 		if (persist ||
369*a1157835SDaniel Fojt 		    (conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER)))
370*a1157835SDaniel Fojt 			p2p_buf_add_config_timeout(buf, p2p->go_timeout,
371*a1157835SDaniel Fojt 						   p2p->client_timeout);
372*a1157835SDaniel Fojt 
373*a1157835SDaniel Fojt 		p2p_buf_add_session_id(buf, prov->session_id,
374*a1157835SDaniel Fojt 				       prov->session_mac);
375*a1157835SDaniel Fojt 
376*a1157835SDaniel Fojt 		p2p_buf_add_feature_capability(buf, fcap_len, fcap);
377*a1157835SDaniel Fojt 		p2p_buf_update_ie_hdr(buf, len);
378*a1157835SDaniel Fojt 	} else if (status != P2P_SC_SUCCESS || adv_id) {
379*a1157835SDaniel Fojt 		u8 *len = p2p_buf_add_ie_hdr(buf);
380*a1157835SDaniel Fojt 
381*a1157835SDaniel Fojt 		p2p_buf_add_status(buf, status);
382*a1157835SDaniel Fojt 
383*a1157835SDaniel Fojt 		if (p2p->p2ps_prov)
384*a1157835SDaniel Fojt 			p2p_buf_add_advertisement_id(buf, adv_id,
385*a1157835SDaniel Fojt 						     p2p->p2ps_prov->adv_mac);
386*a1157835SDaniel Fojt 
387*a1157835SDaniel Fojt 		p2p_buf_update_ie_hdr(buf, len);
388*a1157835SDaniel Fojt 	}
389*a1157835SDaniel Fojt 
3903ff40c12SJohn Marino 	/* WPS IE with Config Methods attribute */
3913ff40c12SJohn Marino 	p2p_build_wps_ie_config_methods(buf, config_methods);
3923ff40c12SJohn Marino 
3933ff40c12SJohn Marino #ifdef CONFIG_WIFI_DISPLAY
3943ff40c12SJohn Marino 	if (wfd_ie)
3953ff40c12SJohn Marino 		wpabuf_put_buf(buf, wfd_ie);
3963ff40c12SJohn Marino #endif /* CONFIG_WIFI_DISPLAY */
3973ff40c12SJohn Marino 
398*a1157835SDaniel Fojt 	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP])
399*a1157835SDaniel Fojt 		wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]);
400*a1157835SDaniel Fojt 
4013ff40c12SJohn Marino 	return buf;
4023ff40c12SJohn Marino }
4033ff40c12SJohn Marino 
4043ff40c12SJohn Marino 
p2ps_setup_p2ps_prov(struct p2p_data * p2p,u32 adv_id,u32 session_id,u16 method,const u8 * session_mac,const u8 * adv_mac)405*a1157835SDaniel Fojt static int p2ps_setup_p2ps_prov(struct p2p_data *p2p, u32 adv_id,
406*a1157835SDaniel Fojt 				u32 session_id, u16 method,
407*a1157835SDaniel Fojt 				const u8 *session_mac, const u8 *adv_mac)
408*a1157835SDaniel Fojt {
409*a1157835SDaniel Fojt 	struct p2ps_provision *tmp;
410*a1157835SDaniel Fojt 
411*a1157835SDaniel Fojt 	if (!p2p->p2ps_prov) {
412*a1157835SDaniel Fojt 		p2p->p2ps_prov = os_zalloc(sizeof(struct p2ps_provision) + 1);
413*a1157835SDaniel Fojt 		if (!p2p->p2ps_prov)
414*a1157835SDaniel Fojt 			return -1;
415*a1157835SDaniel Fojt 	} else {
416*a1157835SDaniel Fojt 		os_memset(p2p->p2ps_prov, 0, sizeof(struct p2ps_provision) + 1);
417*a1157835SDaniel Fojt 	}
418*a1157835SDaniel Fojt 
419*a1157835SDaniel Fojt 	tmp = p2p->p2ps_prov;
420*a1157835SDaniel Fojt 	tmp->adv_id = adv_id;
421*a1157835SDaniel Fojt 	tmp->session_id = session_id;
422*a1157835SDaniel Fojt 	tmp->method = method;
423*a1157835SDaniel Fojt 	os_memcpy(tmp->session_mac, session_mac, ETH_ALEN);
424*a1157835SDaniel Fojt 	os_memcpy(tmp->adv_mac, adv_mac, ETH_ALEN);
425*a1157835SDaniel Fojt 	tmp->info[0] = '\0';
426*a1157835SDaniel Fojt 
427*a1157835SDaniel Fojt 	return 0;
428*a1157835SDaniel Fojt }
429*a1157835SDaniel Fojt 
430*a1157835SDaniel Fojt 
p2ps_own_preferred_cpt(const u8 * cpt_priority,u8 req_cpt_mask)431*a1157835SDaniel Fojt static u8 p2ps_own_preferred_cpt(const u8 *cpt_priority, u8 req_cpt_mask)
432*a1157835SDaniel Fojt {
433*a1157835SDaniel Fojt 	int i;
434*a1157835SDaniel Fojt 
435*a1157835SDaniel Fojt 	for (i = 0; cpt_priority[i]; i++)
436*a1157835SDaniel Fojt 		if (req_cpt_mask & cpt_priority[i])
437*a1157835SDaniel Fojt 			return cpt_priority[i];
438*a1157835SDaniel Fojt 
439*a1157835SDaniel Fojt 	return 0;
440*a1157835SDaniel Fojt }
441*a1157835SDaniel Fojt 
442*a1157835SDaniel Fojt 
443*a1157835SDaniel Fojt /* 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)444*a1157835SDaniel Fojt static int p2ps_validate_pd_req(struct p2p_data *p2p, struct p2p_message *msg,
445*a1157835SDaniel Fojt 				const u8 *addr)
446*a1157835SDaniel Fojt {
447*a1157835SDaniel Fojt 	u8 group_id = 0;
448*a1157835SDaniel Fojt 	u8 intended_addr = 0;
449*a1157835SDaniel Fojt 	u8 operating_channel = 0;
450*a1157835SDaniel Fojt 	u8 channel_list = 0;
451*a1157835SDaniel Fojt 	u8 config_timeout = 0;
452*a1157835SDaniel Fojt 	u8 listen_channel = 0;
453*a1157835SDaniel Fojt 
454*a1157835SDaniel Fojt #define P2PS_PD_REQ_CHECK(_val, _attr) \
455*a1157835SDaniel Fojt do { \
456*a1157835SDaniel Fojt 	if ((_val) && !msg->_attr) { \
457*a1157835SDaniel Fojt 		p2p_dbg(p2p, "Not P2PS PD Request. Missing %s", #_attr); \
458*a1157835SDaniel Fojt 		return -1; \
459*a1157835SDaniel Fojt 	} \
460*a1157835SDaniel Fojt } while (0)
461*a1157835SDaniel Fojt 
462*a1157835SDaniel Fojt 	P2PS_PD_REQ_CHECK(1, adv_id);
463*a1157835SDaniel Fojt 	P2PS_PD_REQ_CHECK(1, session_id);
464*a1157835SDaniel Fojt 	P2PS_PD_REQ_CHECK(1, session_mac);
465*a1157835SDaniel Fojt 	P2PS_PD_REQ_CHECK(1, adv_mac);
466*a1157835SDaniel Fojt 	P2PS_PD_REQ_CHECK(1, capability);
467*a1157835SDaniel Fojt 	P2PS_PD_REQ_CHECK(1, p2p_device_info);
468*a1157835SDaniel Fojt 	P2PS_PD_REQ_CHECK(1, feature_cap);
469*a1157835SDaniel Fojt 
470*a1157835SDaniel Fojt 	/*
471*a1157835SDaniel Fojt 	 * We don't need to check Connection Capability, Persistent Group,
472*a1157835SDaniel Fojt 	 * and related attributes for follow-on PD Request with a status
473*a1157835SDaniel Fojt 	 * other than SUCCESS_DEFERRED.
474*a1157835SDaniel Fojt 	 */
475*a1157835SDaniel Fojt 	if (msg->status && *msg->status != P2P_SC_SUCCESS_DEFERRED)
476*a1157835SDaniel Fojt 		return 0;
477*a1157835SDaniel Fojt 
478*a1157835SDaniel Fojt 	P2PS_PD_REQ_CHECK(1, conn_cap);
479*a1157835SDaniel Fojt 
480*a1157835SDaniel Fojt 	/*
481*a1157835SDaniel Fojt 	 * Note 1: A feature capability attribute structure can be changed
482*a1157835SDaniel Fojt 	 * in the future. The assumption is that such modifications are
483*a1157835SDaniel Fojt 	 * backward compatible, therefore we allow processing of msg.feature_cap
484*a1157835SDaniel Fojt 	 * exceeding the size of the p2ps_feature_capab structure.
485*a1157835SDaniel Fojt 	 * Note 2: Verification of msg.feature_cap_len below has to be changed
486*a1157835SDaniel Fojt 	 * to allow 2 byte feature capability processing if
487*a1157835SDaniel Fojt 	 * struct p2ps_feature_capab is extended to include additional fields
488*a1157835SDaniel Fojt 	 * and it affects the structure size.
489*a1157835SDaniel Fojt 	 */
490*a1157835SDaniel Fojt 	if (msg->feature_cap_len < sizeof(struct p2ps_feature_capab)) {
491*a1157835SDaniel Fojt 		p2p_dbg(p2p, "P2PS: Invalid feature capability len");
492*a1157835SDaniel Fojt 		return -1;
493*a1157835SDaniel Fojt 	}
494*a1157835SDaniel Fojt 
495*a1157835SDaniel Fojt 	switch (*msg->conn_cap) {
496*a1157835SDaniel Fojt 	case P2PS_SETUP_NEW:
497*a1157835SDaniel Fojt 		group_id = 1;
498*a1157835SDaniel Fojt 		intended_addr = 1;
499*a1157835SDaniel Fojt 		operating_channel = 1;
500*a1157835SDaniel Fojt 		channel_list = 1;
501*a1157835SDaniel Fojt 		config_timeout = 1;
502*a1157835SDaniel Fojt 		listen_channel = 1;
503*a1157835SDaniel Fojt 		break;
504*a1157835SDaniel Fojt 	case P2PS_SETUP_CLIENT:
505*a1157835SDaniel Fojt 		channel_list = 1;
506*a1157835SDaniel Fojt 		listen_channel = 1;
507*a1157835SDaniel Fojt 		break;
508*a1157835SDaniel Fojt 	case P2PS_SETUP_GROUP_OWNER:
509*a1157835SDaniel Fojt 		group_id = 1;
510*a1157835SDaniel Fojt 		intended_addr = 1;
511*a1157835SDaniel Fojt 		operating_channel = 1;
512*a1157835SDaniel Fojt 		break;
513*a1157835SDaniel Fojt 	case P2PS_SETUP_NEW | P2PS_SETUP_GROUP_OWNER:
514*a1157835SDaniel Fojt 		group_id = 1;
515*a1157835SDaniel Fojt 		operating_channel = 1;
516*a1157835SDaniel Fojt 		intended_addr = 1;
517*a1157835SDaniel Fojt 		channel_list = 1;
518*a1157835SDaniel Fojt 		config_timeout = 1;
519*a1157835SDaniel Fojt 		break;
520*a1157835SDaniel Fojt 	case P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER:
521*a1157835SDaniel Fojt 		group_id = 1;
522*a1157835SDaniel Fojt 		intended_addr = 1;
523*a1157835SDaniel Fojt 		operating_channel = 1;
524*a1157835SDaniel Fojt 		channel_list = 1;
525*a1157835SDaniel Fojt 		config_timeout = 1;
526*a1157835SDaniel Fojt 		break;
527*a1157835SDaniel Fojt 	default:
528*a1157835SDaniel Fojt 		p2p_dbg(p2p, "Invalid P2PS PD connection capability");
529*a1157835SDaniel Fojt 		return -1;
530*a1157835SDaniel Fojt 	}
531*a1157835SDaniel Fojt 
532*a1157835SDaniel Fojt 	if (msg->persistent_dev) {
533*a1157835SDaniel Fojt 		channel_list = 1;
534*a1157835SDaniel Fojt 		config_timeout = 1;
535*a1157835SDaniel Fojt 		if (os_memcmp(msg->persistent_dev, addr, ETH_ALEN) == 0) {
536*a1157835SDaniel Fojt 			intended_addr = 1;
537*a1157835SDaniel Fojt 			operating_channel = 1;
538*a1157835SDaniel Fojt 		}
539*a1157835SDaniel Fojt 	}
540*a1157835SDaniel Fojt 
541*a1157835SDaniel Fojt 	P2PS_PD_REQ_CHECK(group_id, group_id);
542*a1157835SDaniel Fojt 	P2PS_PD_REQ_CHECK(intended_addr, intended_addr);
543*a1157835SDaniel Fojt 	P2PS_PD_REQ_CHECK(operating_channel, operating_channel);
544*a1157835SDaniel Fojt 	P2PS_PD_REQ_CHECK(channel_list, channel_list);
545*a1157835SDaniel Fojt 	P2PS_PD_REQ_CHECK(config_timeout, config_timeout);
546*a1157835SDaniel Fojt 	P2PS_PD_REQ_CHECK(listen_channel, listen_channel);
547*a1157835SDaniel Fojt 
548*a1157835SDaniel Fojt #undef P2PS_PD_REQ_CHECK
549*a1157835SDaniel Fojt 
550*a1157835SDaniel Fojt 	return 0;
551*a1157835SDaniel Fojt }
552*a1157835SDaniel Fojt 
553*a1157835SDaniel Fojt 
p2p_process_prov_disc_req(struct p2p_data * p2p,const u8 * sa,const u8 * data,size_t len,int rx_freq)5543ff40c12SJohn Marino void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
5553ff40c12SJohn Marino 			       const u8 *data, size_t len, int rx_freq)
5563ff40c12SJohn Marino {
5573ff40c12SJohn Marino 	struct p2p_message msg;
5583ff40c12SJohn Marino 	struct p2p_device *dev;
5593ff40c12SJohn Marino 	int freq;
560*a1157835SDaniel Fojt 	enum p2p_status_code reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
5613ff40c12SJohn Marino 	struct wpabuf *resp;
562*a1157835SDaniel Fojt 	u32 adv_id = 0;
563*a1157835SDaniel Fojt 	struct p2ps_advertisement *p2ps_adv = NULL;
564*a1157835SDaniel Fojt 	u8 conncap = P2PS_SETUP_NEW;
565*a1157835SDaniel Fojt 	u8 auto_accept = 0;
566*a1157835SDaniel Fojt 	u32 session_id = 0;
567*a1157835SDaniel Fojt 	u8 session_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
568*a1157835SDaniel Fojt 	u8 adv_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
569*a1157835SDaniel Fojt 	const u8 *group_mac;
570*a1157835SDaniel Fojt 	int passwd_id = DEV_PW_DEFAULT;
571*a1157835SDaniel Fojt 	u16 config_methods;
572*a1157835SDaniel Fojt 	u16 allowed_config_methods = WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
573*a1157835SDaniel Fojt 	struct p2ps_feature_capab resp_fcap = { 0, 0 };
574*a1157835SDaniel Fojt 	struct p2ps_feature_capab *req_fcap = NULL;
575*a1157835SDaniel Fojt 	u8 remote_conncap;
576*a1157835SDaniel Fojt 	u16 method;
5773ff40c12SJohn Marino 
5783ff40c12SJohn Marino 	if (p2p_parse(data, len, &msg))
5793ff40c12SJohn Marino 		return;
5803ff40c12SJohn Marino 
5813ff40c12SJohn Marino 	p2p_dbg(p2p, "Received Provision Discovery Request from " MACSTR
5823ff40c12SJohn Marino 		" with config methods 0x%x (freq=%d)",
5833ff40c12SJohn Marino 		MAC2STR(sa), msg.wps_config_methods, rx_freq);
584*a1157835SDaniel Fojt 	group_mac = msg.intended_addr;
5853ff40c12SJohn Marino 
5863ff40c12SJohn Marino 	dev = p2p_get_device(p2p, sa);
5873ff40c12SJohn Marino 	if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
5883ff40c12SJohn Marino 		p2p_dbg(p2p, "Provision Discovery Request from unknown peer "
5893ff40c12SJohn Marino 			MACSTR, MAC2STR(sa));
5903ff40c12SJohn Marino 
5913ff40c12SJohn Marino 		if (p2p_add_device(p2p, sa, rx_freq, NULL, 0, data + 1, len - 1,
5923ff40c12SJohn Marino 				   0)) {
5933ff40c12SJohn Marino 			p2p_dbg(p2p, "Provision Discovery Request add device failed "
5943ff40c12SJohn Marino 				MACSTR, MAC2STR(sa));
595*a1157835SDaniel Fojt 			goto out;
596*a1157835SDaniel Fojt 		}
597*a1157835SDaniel Fojt 
598*a1157835SDaniel Fojt 		if (!dev) {
599*a1157835SDaniel Fojt 			dev = p2p_get_device(p2p, sa);
600*a1157835SDaniel Fojt 			if (!dev) {
601*a1157835SDaniel Fojt 				p2p_dbg(p2p,
602*a1157835SDaniel Fojt 					"Provision Discovery device not found "
603*a1157835SDaniel Fojt 					MACSTR, MAC2STR(sa));
604*a1157835SDaniel Fojt 				goto out;
605*a1157835SDaniel Fojt 			}
6063ff40c12SJohn Marino 		}
6073ff40c12SJohn Marino 	} else if (msg.wfd_subelems) {
6083ff40c12SJohn Marino 		wpabuf_free(dev->info.wfd_subelems);
6093ff40c12SJohn Marino 		dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
6103ff40c12SJohn Marino 	}
6113ff40c12SJohn Marino 
612*a1157835SDaniel Fojt 	if (!msg.adv_id) {
613*a1157835SDaniel Fojt 		allowed_config_methods |= WPS_CONFIG_PUSHBUTTON;
614*a1157835SDaniel Fojt 		if (!(msg.wps_config_methods & allowed_config_methods)) {
615*a1157835SDaniel Fojt 			p2p_dbg(p2p,
616*a1157835SDaniel Fojt 				"Unsupported Config Methods in Provision Discovery Request");
6173ff40c12SJohn Marino 			goto out;
6183ff40c12SJohn Marino 		}
6193ff40c12SJohn Marino 
620*a1157835SDaniel Fojt 		/* Legacy (non-P2PS) - Unknown groups allowed for P2PS */
6213ff40c12SJohn Marino 		if (msg.group_id) {
6223ff40c12SJohn Marino 			size_t i;
623*a1157835SDaniel Fojt 
6243ff40c12SJohn Marino 			for (i = 0; i < p2p->num_groups; i++) {
625*a1157835SDaniel Fojt 				if (p2p_group_is_group_id_match(
626*a1157835SDaniel Fojt 					    p2p->groups[i],
627*a1157835SDaniel Fojt 					    msg.group_id, msg.group_id_len))
6283ff40c12SJohn Marino 					break;
6293ff40c12SJohn Marino 			}
6303ff40c12SJohn Marino 			if (i == p2p->num_groups) {
631*a1157835SDaniel Fojt 				p2p_dbg(p2p,
632*a1157835SDaniel Fojt 					"PD request for unknown P2P Group ID - reject");
6333ff40c12SJohn Marino 				goto out;
6343ff40c12SJohn Marino 			}
6353ff40c12SJohn Marino 		}
636*a1157835SDaniel Fojt 	} else {
637*a1157835SDaniel Fojt 		allowed_config_methods |= WPS_CONFIG_P2PS;
6383ff40c12SJohn Marino 
639*a1157835SDaniel Fojt 		/*
640*a1157835SDaniel Fojt 		 * Set adv_id here, so in case of an error, a P2PS PD Response
641*a1157835SDaniel Fojt 		 * will be sent.
642*a1157835SDaniel Fojt 		 */
643*a1157835SDaniel Fojt 		adv_id = WPA_GET_LE32(msg.adv_id);
644*a1157835SDaniel Fojt 		if (p2ps_validate_pd_req(p2p, &msg, sa) < 0) {
645*a1157835SDaniel Fojt 			reject = P2P_SC_FAIL_INVALID_PARAMS;
646*a1157835SDaniel Fojt 			goto out;
647*a1157835SDaniel Fojt 		}
648*a1157835SDaniel Fojt 
649*a1157835SDaniel Fojt 		req_fcap = (struct p2ps_feature_capab *) msg.feature_cap;
650*a1157835SDaniel Fojt 
651*a1157835SDaniel Fojt 		os_memcpy(session_mac, msg.session_mac, ETH_ALEN);
652*a1157835SDaniel Fojt 		os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN);
653*a1157835SDaniel Fojt 
654*a1157835SDaniel Fojt 		session_id = WPA_GET_LE32(msg.session_id);
655*a1157835SDaniel Fojt 
656*a1157835SDaniel Fojt 		if (msg.conn_cap)
657*a1157835SDaniel Fojt 			conncap = *msg.conn_cap;
658*a1157835SDaniel Fojt 
659*a1157835SDaniel Fojt 		/*
660*a1157835SDaniel Fojt 		 * We need to verify a P2PS config methog in an initial PD
661*a1157835SDaniel Fojt 		 * request or in a follow-on PD request with the status
662*a1157835SDaniel Fojt 		 * SUCCESS_DEFERRED.
663*a1157835SDaniel Fojt 		 */
664*a1157835SDaniel Fojt 		if ((!msg.status || *msg.status == P2P_SC_SUCCESS_DEFERRED) &&
665*a1157835SDaniel Fojt 		    !(msg.wps_config_methods & allowed_config_methods)) {
666*a1157835SDaniel Fojt 			p2p_dbg(p2p,
667*a1157835SDaniel Fojt 				"Unsupported Config Methods in Provision Discovery Request");
668*a1157835SDaniel Fojt 			goto out;
669*a1157835SDaniel Fojt 		}
670*a1157835SDaniel Fojt 
671*a1157835SDaniel Fojt 		/*
672*a1157835SDaniel Fojt 		 * TODO: since we don't support multiple PD, reject PD request
673*a1157835SDaniel Fojt 		 * if we are in the middle of P2PS PD with some other peer
674*a1157835SDaniel Fojt 		 */
675*a1157835SDaniel Fojt 	}
676*a1157835SDaniel Fojt 
6773ff40c12SJohn Marino 	dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
678*a1157835SDaniel Fojt 			P2P_DEV_PD_PEER_KEYPAD |
679*a1157835SDaniel Fojt 			P2P_DEV_PD_PEER_P2PS);
680*a1157835SDaniel Fojt 
6813ff40c12SJohn Marino 	if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) {
6823ff40c12SJohn Marino 		p2p_dbg(p2p, "Peer " MACSTR
6833ff40c12SJohn Marino 			" requested us to show a PIN on display", MAC2STR(sa));
6843ff40c12SJohn Marino 		dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
685*a1157835SDaniel Fojt 		passwd_id = DEV_PW_USER_SPECIFIED;
6863ff40c12SJohn Marino 	} else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
6873ff40c12SJohn Marino 		p2p_dbg(p2p, "Peer " MACSTR
6883ff40c12SJohn Marino 			" requested us to write its PIN using keypad",
6893ff40c12SJohn Marino 			MAC2STR(sa));
6903ff40c12SJohn Marino 		dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
691*a1157835SDaniel Fojt 		passwd_id = DEV_PW_REGISTRAR_SPECIFIED;
692*a1157835SDaniel Fojt 	} else if (msg.wps_config_methods & WPS_CONFIG_P2PS) {
693*a1157835SDaniel Fojt 		p2p_dbg(p2p, "Peer " MACSTR " requesting P2PS PIN",
694*a1157835SDaniel Fojt 			MAC2STR(sa));
695*a1157835SDaniel Fojt 		dev->flags |= P2P_DEV_PD_PEER_P2PS;
696*a1157835SDaniel Fojt 		passwd_id = DEV_PW_P2PS_DEFAULT;
6973ff40c12SJohn Marino 	}
6983ff40c12SJohn Marino 
699*a1157835SDaniel Fojt 	/* Remove stale persistent groups */
700*a1157835SDaniel Fojt 	if (p2p->cfg->remove_stale_groups) {
701*a1157835SDaniel Fojt 		p2p->cfg->remove_stale_groups(
702*a1157835SDaniel Fojt 			p2p->cfg->cb_ctx, dev->info.p2p_device_addr,
703*a1157835SDaniel Fojt 			msg.persistent_dev,
704*a1157835SDaniel Fojt 			msg.persistent_ssid, msg.persistent_ssid_len);
705*a1157835SDaniel Fojt 	}
706*a1157835SDaniel Fojt 
707*a1157835SDaniel Fojt 	reject = P2P_SC_SUCCESS;
708*a1157835SDaniel Fojt 
709*a1157835SDaniel Fojt 	/*
710*a1157835SDaniel Fojt 	 * End of a legacy P2P PD Request processing, from this point continue
711*a1157835SDaniel Fojt 	 * with P2PS one.
712*a1157835SDaniel Fojt 	 */
713*a1157835SDaniel Fojt 	if (!msg.adv_id)
714*a1157835SDaniel Fojt 		goto out;
715*a1157835SDaniel Fojt 
716*a1157835SDaniel Fojt 	remote_conncap = conncap;
717*a1157835SDaniel Fojt 
718*a1157835SDaniel Fojt 	if (!msg.status) {
719*a1157835SDaniel Fojt 		unsigned int forced_freq, pref_freq;
720*a1157835SDaniel Fojt 
721*a1157835SDaniel Fojt 		if (os_memcmp(p2p->cfg->dev_addr, msg.adv_mac, ETH_ALEN)) {
722*a1157835SDaniel Fojt 			p2p_dbg(p2p,
723*a1157835SDaniel Fojt 				"P2PS PD adv mac does not match the local one");
724*a1157835SDaniel Fojt 			reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
725*a1157835SDaniel Fojt 			goto out;
726*a1157835SDaniel Fojt 		}
727*a1157835SDaniel Fojt 
728*a1157835SDaniel Fojt 		p2ps_adv = p2p_service_p2ps_id(p2p, adv_id);
729*a1157835SDaniel Fojt 		if (!p2ps_adv) {
730*a1157835SDaniel Fojt 			p2p_dbg(p2p, "P2PS PD invalid adv_id=0x%X", adv_id);
731*a1157835SDaniel Fojt 			reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
732*a1157835SDaniel Fojt 			goto out;
733*a1157835SDaniel Fojt 		}
734*a1157835SDaniel Fojt 		p2p_dbg(p2p, "adv_id: 0x%X, p2ps_adv: %p", adv_id, p2ps_adv);
735*a1157835SDaniel Fojt 
736*a1157835SDaniel Fojt 		auto_accept = p2ps_adv->auto_accept;
737*a1157835SDaniel Fojt 		conncap = p2p->cfg->p2ps_group_capability(p2p->cfg->cb_ctx,
738*a1157835SDaniel Fojt 							  conncap, auto_accept,
739*a1157835SDaniel Fojt 							  &forced_freq,
740*a1157835SDaniel Fojt 							  &pref_freq);
741*a1157835SDaniel Fojt 
742*a1157835SDaniel Fojt 		p2p_dbg(p2p, "Conncap: local:%d remote:%d result:%d",
743*a1157835SDaniel Fojt 			auto_accept, remote_conncap, conncap);
744*a1157835SDaniel Fojt 
745*a1157835SDaniel Fojt 		p2p_prepare_channel(p2p, dev, forced_freq, pref_freq, 0);
746*a1157835SDaniel Fojt 
747*a1157835SDaniel Fojt 		resp_fcap.cpt = p2ps_own_preferred_cpt(p2ps_adv->cpt_priority,
748*a1157835SDaniel Fojt 						       req_fcap->cpt);
749*a1157835SDaniel Fojt 
750*a1157835SDaniel Fojt 		p2p_dbg(p2p, "cpt: service:0x%x remote:0x%x result:0x%x",
751*a1157835SDaniel Fojt 			p2ps_adv->cpt_mask, req_fcap->cpt, resp_fcap.cpt);
752*a1157835SDaniel Fojt 
753*a1157835SDaniel Fojt 		if (!resp_fcap.cpt) {
754*a1157835SDaniel Fojt 			p2p_dbg(p2p,
755*a1157835SDaniel Fojt 				"Incompatible P2PS feature capability CPT bitmask");
756*a1157835SDaniel Fojt 			reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
757*a1157835SDaniel Fojt 		} else if (p2ps_adv->config_methods &&
758*a1157835SDaniel Fojt 			   !(msg.wps_config_methods &
759*a1157835SDaniel Fojt 			     p2ps_adv->config_methods)) {
760*a1157835SDaniel Fojt 			p2p_dbg(p2p,
761*a1157835SDaniel Fojt 				"Unsupported config methods in Provision Discovery Request (own=0x%x peer=0x%x)",
762*a1157835SDaniel Fojt 				p2ps_adv->config_methods,
763*a1157835SDaniel Fojt 				msg.wps_config_methods);
764*a1157835SDaniel Fojt 			reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
765*a1157835SDaniel Fojt 		} else if (!p2ps_adv->state) {
766*a1157835SDaniel Fojt 			p2p_dbg(p2p, "P2PS state unavailable");
767*a1157835SDaniel Fojt 			reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
768*a1157835SDaniel Fojt 		} else if (!conncap) {
769*a1157835SDaniel Fojt 			p2p_dbg(p2p, "Conncap resolution failed");
770*a1157835SDaniel Fojt 			reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
771*a1157835SDaniel Fojt 		}
772*a1157835SDaniel Fojt 
773*a1157835SDaniel Fojt 		if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
774*a1157835SDaniel Fojt 			p2p_dbg(p2p, "Keypad - always defer");
775*a1157835SDaniel Fojt 			auto_accept = 0;
776*a1157835SDaniel Fojt 		}
777*a1157835SDaniel Fojt 
778*a1157835SDaniel Fojt 		if ((remote_conncap & (P2PS_SETUP_NEW | P2PS_SETUP_CLIENT) ||
779*a1157835SDaniel Fojt 		     msg.persistent_dev) && conncap != P2PS_SETUP_NEW &&
780*a1157835SDaniel Fojt 		    msg.channel_list && msg.channel_list_len &&
781*a1157835SDaniel Fojt 		    p2p_peer_channels_check(p2p, &p2p->channels, dev,
782*a1157835SDaniel Fojt 					    msg.channel_list,
783*a1157835SDaniel Fojt 					    msg.channel_list_len) < 0) {
784*a1157835SDaniel Fojt 			p2p_dbg(p2p,
785*a1157835SDaniel Fojt 				"No common channels - force deferred flow");
786*a1157835SDaniel Fojt 			auto_accept = 0;
787*a1157835SDaniel Fojt 		}
788*a1157835SDaniel Fojt 
789*a1157835SDaniel Fojt 		if (((remote_conncap & P2PS_SETUP_GROUP_OWNER) ||
790*a1157835SDaniel Fojt 		     msg.persistent_dev) && msg.operating_channel) {
791*a1157835SDaniel Fojt 			struct p2p_channels intersect;
792*a1157835SDaniel Fojt 
793*a1157835SDaniel Fojt 			/*
794*a1157835SDaniel Fojt 			 * There are cases where only the operating channel is
795*a1157835SDaniel Fojt 			 * provided. This requires saving the channel as the
796*a1157835SDaniel Fojt 			 * supported channel list, and verifying that it is
797*a1157835SDaniel Fojt 			 * supported.
798*a1157835SDaniel Fojt 			 */
799*a1157835SDaniel Fojt 			if (dev->channels.reg_classes == 0 ||
800*a1157835SDaniel Fojt 			    !p2p_channels_includes(&dev->channels,
801*a1157835SDaniel Fojt 						   msg.operating_channel[3],
802*a1157835SDaniel Fojt 						   msg.operating_channel[4])) {
803*a1157835SDaniel Fojt 				struct p2p_channels *ch = &dev->channels;
804*a1157835SDaniel Fojt 
805*a1157835SDaniel Fojt 				os_memset(ch, 0, sizeof(*ch));
806*a1157835SDaniel Fojt 				ch->reg_class[0].reg_class =
807*a1157835SDaniel Fojt 					msg.operating_channel[3];
808*a1157835SDaniel Fojt 				ch->reg_class[0].channel[0] =
809*a1157835SDaniel Fojt 					msg.operating_channel[4];
810*a1157835SDaniel Fojt 				ch->reg_class[0].channels = 1;
811*a1157835SDaniel Fojt 				ch->reg_classes = 1;
812*a1157835SDaniel Fojt 			}
813*a1157835SDaniel Fojt 
814*a1157835SDaniel Fojt 			p2p_channels_intersect(&p2p->channels, &dev->channels,
815*a1157835SDaniel Fojt 					       &intersect);
816*a1157835SDaniel Fojt 
817*a1157835SDaniel Fojt 			if (intersect.reg_classes == 0) {
818*a1157835SDaniel Fojt 				p2p_dbg(p2p,
819*a1157835SDaniel Fojt 					"No common channels - force deferred flow");
820*a1157835SDaniel Fojt 				auto_accept = 0;
821*a1157835SDaniel Fojt 			}
822*a1157835SDaniel Fojt 		}
823*a1157835SDaniel Fojt 
824*a1157835SDaniel Fojt 		if (auto_accept || reject != P2P_SC_SUCCESS) {
825*a1157835SDaniel Fojt 			struct p2ps_provision *tmp;
826*a1157835SDaniel Fojt 
827*a1157835SDaniel Fojt 			if (p2ps_setup_p2ps_prov(p2p, adv_id, session_id,
828*a1157835SDaniel Fojt 						 msg.wps_config_methods,
829*a1157835SDaniel Fojt 						 session_mac, adv_mac) < 0) {
830*a1157835SDaniel Fojt 				reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
831*a1157835SDaniel Fojt 				goto out;
832*a1157835SDaniel Fojt 			}
833*a1157835SDaniel Fojt 
834*a1157835SDaniel Fojt 			tmp = p2p->p2ps_prov;
835*a1157835SDaniel Fojt 			tmp->force_freq = forced_freq;
836*a1157835SDaniel Fojt 			tmp->pref_freq = pref_freq;
837*a1157835SDaniel Fojt 			if (conncap) {
838*a1157835SDaniel Fojt 				tmp->conncap = conncap;
839*a1157835SDaniel Fojt 				tmp->status = P2P_SC_SUCCESS;
840*a1157835SDaniel Fojt 			} else {
841*a1157835SDaniel Fojt 				tmp->conncap = auto_accept;
842*a1157835SDaniel Fojt 				tmp->status = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
843*a1157835SDaniel Fojt 			}
844*a1157835SDaniel Fojt 
845*a1157835SDaniel Fojt 			if (reject != P2P_SC_SUCCESS)
846*a1157835SDaniel Fojt 				goto out;
847*a1157835SDaniel Fojt 		}
848*a1157835SDaniel Fojt 	}
849*a1157835SDaniel Fojt 
850*a1157835SDaniel Fojt 	if (!msg.status && !auto_accept &&
851*a1157835SDaniel Fojt 	    (!p2p->p2ps_prov || p2p->p2ps_prov->adv_id != adv_id)) {
852*a1157835SDaniel Fojt 		struct p2ps_provision *tmp;
853*a1157835SDaniel Fojt 
854*a1157835SDaniel Fojt 		if (!conncap) {
855*a1157835SDaniel Fojt 			reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
856*a1157835SDaniel Fojt 			goto out;
857*a1157835SDaniel Fojt 		}
858*a1157835SDaniel Fojt 
859*a1157835SDaniel Fojt 		if (p2ps_setup_p2ps_prov(p2p, adv_id, session_id,
860*a1157835SDaniel Fojt 					 msg.wps_config_methods,
861*a1157835SDaniel Fojt 					 session_mac, adv_mac) < 0) {
862*a1157835SDaniel Fojt 			reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
863*a1157835SDaniel Fojt 			goto out;
864*a1157835SDaniel Fojt 		}
865*a1157835SDaniel Fojt 		tmp = p2p->p2ps_prov;
866*a1157835SDaniel Fojt 		reject = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
867*a1157835SDaniel Fojt 		tmp->status = reject;
868*a1157835SDaniel Fojt 	}
869*a1157835SDaniel Fojt 
870*a1157835SDaniel Fojt 	/* Not a P2PS Follow-on PD */
871*a1157835SDaniel Fojt 	if (!msg.status)
872*a1157835SDaniel Fojt 		goto out;
873*a1157835SDaniel Fojt 
874*a1157835SDaniel Fojt 	if (*msg.status && *msg.status != P2P_SC_SUCCESS_DEFERRED) {
875*a1157835SDaniel Fojt 		reject = *msg.status;
876*a1157835SDaniel Fojt 		goto out;
877*a1157835SDaniel Fojt 	}
878*a1157835SDaniel Fojt 
879*a1157835SDaniel Fojt 	if (*msg.status != P2P_SC_SUCCESS_DEFERRED || !p2p->p2ps_prov)
880*a1157835SDaniel Fojt 		goto out;
881*a1157835SDaniel Fojt 
882*a1157835SDaniel Fojt 	if (p2p->p2ps_prov->adv_id != adv_id ||
883*a1157835SDaniel Fojt 	    os_memcmp(p2p->p2ps_prov->adv_mac, msg.adv_mac, ETH_ALEN)) {
884*a1157835SDaniel Fojt 		p2p_dbg(p2p,
885*a1157835SDaniel Fojt 			"P2PS Follow-on PD with mismatch Advertisement ID/MAC");
886*a1157835SDaniel Fojt 		goto out;
887*a1157835SDaniel Fojt 	}
888*a1157835SDaniel Fojt 
889*a1157835SDaniel Fojt 	if (p2p->p2ps_prov->session_id != session_id ||
890*a1157835SDaniel Fojt 	    os_memcmp(p2p->p2ps_prov->session_mac, msg.session_mac, ETH_ALEN)) {
891*a1157835SDaniel Fojt 		p2p_dbg(p2p, "P2PS Follow-on PD with mismatch Session ID/MAC");
892*a1157835SDaniel Fojt 		goto out;
893*a1157835SDaniel Fojt 	}
894*a1157835SDaniel Fojt 
895*a1157835SDaniel Fojt 	method = p2p->p2ps_prov->method;
896*a1157835SDaniel Fojt 
897*a1157835SDaniel Fojt 	conncap = p2p->cfg->p2ps_group_capability(p2p->cfg->cb_ctx,
898*a1157835SDaniel Fojt 						  remote_conncap,
899*a1157835SDaniel Fojt 						  p2p->p2ps_prov->conncap,
900*a1157835SDaniel Fojt 						  &p2p->p2ps_prov->force_freq,
901*a1157835SDaniel Fojt 						  &p2p->p2ps_prov->pref_freq);
902*a1157835SDaniel Fojt 
903*a1157835SDaniel Fojt 	resp_fcap.cpt = p2ps_own_preferred_cpt(p2p->p2ps_prov->cpt_priority,
904*a1157835SDaniel Fojt 					       req_fcap->cpt);
905*a1157835SDaniel Fojt 
906*a1157835SDaniel Fojt 	p2p_dbg(p2p, "cpt: local:0x%x remote:0x%x result:0x%x",
907*a1157835SDaniel Fojt 		p2p->p2ps_prov->cpt_mask, req_fcap->cpt, resp_fcap.cpt);
908*a1157835SDaniel Fojt 
909*a1157835SDaniel Fojt 	p2p_prepare_channel(p2p, dev, p2p->p2ps_prov->force_freq,
910*a1157835SDaniel Fojt 			    p2p->p2ps_prov->pref_freq, 0);
911*a1157835SDaniel Fojt 
912*a1157835SDaniel Fojt 	/*
913*a1157835SDaniel Fojt 	 * Ensure that if we asked for PIN originally, our method is consistent
914*a1157835SDaniel Fojt 	 * with original request.
915*a1157835SDaniel Fojt 	 */
916*a1157835SDaniel Fojt 	if (method & WPS_CONFIG_DISPLAY)
917*a1157835SDaniel Fojt 		method = WPS_CONFIG_KEYPAD;
918*a1157835SDaniel Fojt 	else if (method & WPS_CONFIG_KEYPAD)
919*a1157835SDaniel Fojt 		method = WPS_CONFIG_DISPLAY;
920*a1157835SDaniel Fojt 
921*a1157835SDaniel Fojt 	if (!conncap || !(msg.wps_config_methods & method)) {
922*a1157835SDaniel Fojt 		/*
923*a1157835SDaniel Fojt 		 * Reject this "Deferred Accept*
924*a1157835SDaniel Fojt 		 * if incompatible conncap or method
925*a1157835SDaniel Fojt 		 */
926*a1157835SDaniel Fojt 		reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
927*a1157835SDaniel Fojt 	} else if (!resp_fcap.cpt) {
928*a1157835SDaniel Fojt 		p2p_dbg(p2p,
929*a1157835SDaniel Fojt 			"Incompatible P2PS feature capability CPT bitmask");
930*a1157835SDaniel Fojt 		reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
931*a1157835SDaniel Fojt 	} else if ((remote_conncap & (P2PS_SETUP_NEW | P2PS_SETUP_CLIENT) ||
932*a1157835SDaniel Fojt 		    msg.persistent_dev) && conncap != P2PS_SETUP_NEW &&
933*a1157835SDaniel Fojt 		   msg.channel_list && msg.channel_list_len &&
934*a1157835SDaniel Fojt 		   p2p_peer_channels_check(p2p, &p2p->channels, dev,
935*a1157835SDaniel Fojt 					   msg.channel_list,
936*a1157835SDaniel Fojt 					   msg.channel_list_len) < 0) {
937*a1157835SDaniel Fojt 		p2p_dbg(p2p,
938*a1157835SDaniel Fojt 			"No common channels in Follow-On Provision Discovery Request");
939*a1157835SDaniel Fojt 		reject = P2P_SC_FAIL_NO_COMMON_CHANNELS;
940*a1157835SDaniel Fojt 	} else {
941*a1157835SDaniel Fojt 		reject = P2P_SC_SUCCESS;
942*a1157835SDaniel Fojt 	}
943*a1157835SDaniel Fojt 
944*a1157835SDaniel Fojt 	dev->oper_freq = 0;
945*a1157835SDaniel Fojt 	if (reject == P2P_SC_SUCCESS || reject == P2P_SC_SUCCESS_DEFERRED) {
946*a1157835SDaniel Fojt 		u8 tmp;
947*a1157835SDaniel Fojt 
948*a1157835SDaniel Fojt 		if (msg.operating_channel)
949*a1157835SDaniel Fojt 			dev->oper_freq =
950*a1157835SDaniel Fojt 				p2p_channel_to_freq(msg.operating_channel[3],
951*a1157835SDaniel Fojt 						    msg.operating_channel[4]);
952*a1157835SDaniel Fojt 
953*a1157835SDaniel Fojt 		if ((conncap & P2PS_SETUP_GROUP_OWNER) &&
954*a1157835SDaniel Fojt 		    p2p_go_select_channel(p2p, dev, &tmp) < 0)
955*a1157835SDaniel Fojt 			reject = P2P_SC_FAIL_NO_COMMON_CHANNELS;
956*a1157835SDaniel Fojt 	}
957*a1157835SDaniel Fojt 
958*a1157835SDaniel Fojt 	p2p->p2ps_prov->status = reject;
959*a1157835SDaniel Fojt 	p2p->p2ps_prov->conncap = conncap;
9603ff40c12SJohn Marino 
9613ff40c12SJohn Marino out:
962*a1157835SDaniel Fojt 	if (reject == P2P_SC_SUCCESS ||
963*a1157835SDaniel Fojt 	    reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE)
964*a1157835SDaniel Fojt 		config_methods = msg.wps_config_methods;
965*a1157835SDaniel Fojt 	else
966*a1157835SDaniel Fojt 		config_methods = 0;
967*a1157835SDaniel Fojt 
968*a1157835SDaniel Fojt 	/*
969*a1157835SDaniel Fojt 	 * Send PD Response for an initial PD Request or for follow-on
970*a1157835SDaniel Fojt 	 * PD Request with P2P_SC_SUCCESS_DEFERRED status.
971*a1157835SDaniel Fojt 	 */
972*a1157835SDaniel Fojt 	if (!msg.status || *msg.status == P2P_SC_SUCCESS_DEFERRED) {
973*a1157835SDaniel Fojt 		resp = p2p_build_prov_disc_resp(p2p, dev, msg.dialog_token,
974*a1157835SDaniel Fojt 						reject, config_methods, adv_id,
975*a1157835SDaniel Fojt 						msg.group_id, msg.group_id_len,
976*a1157835SDaniel Fojt 						msg.persistent_ssid,
977*a1157835SDaniel Fojt 						msg.persistent_ssid_len,
978*a1157835SDaniel Fojt 						(const u8 *) &resp_fcap,
979*a1157835SDaniel Fojt 						sizeof(resp_fcap));
980*a1157835SDaniel Fojt 		if (!resp) {
9813ff40c12SJohn Marino 			p2p_parse_free(&msg);
9823ff40c12SJohn Marino 			return;
9833ff40c12SJohn Marino 		}
9843ff40c12SJohn Marino 		p2p_dbg(p2p, "Sending Provision Discovery Response");
9853ff40c12SJohn Marino 		if (rx_freq > 0)
9863ff40c12SJohn Marino 			freq = rx_freq;
9873ff40c12SJohn Marino 		else
9883ff40c12SJohn Marino 			freq = p2p_channel_to_freq(p2p->cfg->reg_class,
9893ff40c12SJohn Marino 						   p2p->cfg->channel);
9903ff40c12SJohn Marino 		if (freq < 0) {
9913ff40c12SJohn Marino 			p2p_dbg(p2p, "Unknown regulatory class/channel");
9923ff40c12SJohn Marino 			wpabuf_free(resp);
9933ff40c12SJohn Marino 			p2p_parse_free(&msg);
9943ff40c12SJohn Marino 			return;
9953ff40c12SJohn Marino 		}
996*a1157835SDaniel Fojt 		p2p->pending_action_state = P2P_PENDING_PD_RESPONSE;
9973ff40c12SJohn Marino 		if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr,
9983ff40c12SJohn Marino 				    p2p->cfg->dev_addr,
999*a1157835SDaniel Fojt 				    wpabuf_head(resp), wpabuf_len(resp),
1000*a1157835SDaniel Fojt 				    50) < 0)
10013ff40c12SJohn Marino 			p2p_dbg(p2p, "Failed to send Action frame");
1002*a1157835SDaniel Fojt 		else
1003*a1157835SDaniel Fojt 			p2p->send_action_in_progress = 1;
10043ff40c12SJohn Marino 
10053ff40c12SJohn Marino 		wpabuf_free(resp);
1006*a1157835SDaniel Fojt 	}
10073ff40c12SJohn Marino 
1008*a1157835SDaniel Fojt 	if (!dev) {
1009*a1157835SDaniel Fojt 		p2p_parse_free(&msg);
1010*a1157835SDaniel Fojt 		return;
1011*a1157835SDaniel Fojt 	}
1012*a1157835SDaniel Fojt 
1013*a1157835SDaniel Fojt 	freq = 0;
1014*a1157835SDaniel Fojt 	if (reject == P2P_SC_SUCCESS && conncap == P2PS_SETUP_GROUP_OWNER) {
1015*a1157835SDaniel Fojt 		freq = p2p_channel_to_freq(p2p->op_reg_class,
1016*a1157835SDaniel Fojt 					   p2p->op_channel);
1017*a1157835SDaniel Fojt 		if (freq < 0)
1018*a1157835SDaniel Fojt 			freq = 0;
1019*a1157835SDaniel Fojt 	}
1020*a1157835SDaniel Fojt 
1021*a1157835SDaniel Fojt 	if (!p2p->cfg->p2ps_prov_complete) {
1022*a1157835SDaniel Fojt 		/* Don't emit anything */
1023*a1157835SDaniel Fojt 	} else if (msg.status && *msg.status != P2P_SC_SUCCESS &&
1024*a1157835SDaniel Fojt 		   *msg.status != P2P_SC_SUCCESS_DEFERRED) {
1025*a1157835SDaniel Fojt 		reject = *msg.status;
1026*a1157835SDaniel Fojt 		p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, reject,
1027*a1157835SDaniel Fojt 					     sa, adv_mac, session_mac,
1028*a1157835SDaniel Fojt 					     NULL, adv_id, session_id,
1029*a1157835SDaniel Fojt 					     0, 0, msg.persistent_ssid,
1030*a1157835SDaniel Fojt 					     msg.persistent_ssid_len,
1031*a1157835SDaniel Fojt 					     0, 0, NULL, NULL, 0, freq,
1032*a1157835SDaniel Fojt 					     NULL, 0);
1033*a1157835SDaniel Fojt 	} else if (msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED &&
1034*a1157835SDaniel Fojt 		   p2p->p2ps_prov) {
1035*a1157835SDaniel Fojt 		p2p->p2ps_prov->status = reject;
1036*a1157835SDaniel Fojt 		p2p->p2ps_prov->conncap = conncap;
1037*a1157835SDaniel Fojt 
1038*a1157835SDaniel Fojt 		if (reject != P2P_SC_SUCCESS)
1039*a1157835SDaniel Fojt 			p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, reject,
1040*a1157835SDaniel Fojt 						     sa, adv_mac, session_mac,
1041*a1157835SDaniel Fojt 						     NULL, adv_id,
1042*a1157835SDaniel Fojt 						     session_id, conncap, 0,
1043*a1157835SDaniel Fojt 						     msg.persistent_ssid,
1044*a1157835SDaniel Fojt 						     msg.persistent_ssid_len, 0,
1045*a1157835SDaniel Fojt 						     0, NULL, NULL, 0, freq,
1046*a1157835SDaniel Fojt 						     NULL, 0);
1047*a1157835SDaniel Fojt 		else
1048*a1157835SDaniel Fojt 			p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx,
1049*a1157835SDaniel Fojt 						     *msg.status,
1050*a1157835SDaniel Fojt 						     sa, adv_mac, session_mac,
1051*a1157835SDaniel Fojt 						     group_mac, adv_id,
1052*a1157835SDaniel Fojt 						     session_id, conncap,
1053*a1157835SDaniel Fojt 						     passwd_id,
1054*a1157835SDaniel Fojt 						     msg.persistent_ssid,
1055*a1157835SDaniel Fojt 						     msg.persistent_ssid_len, 0,
1056*a1157835SDaniel Fojt 						     0, NULL,
1057*a1157835SDaniel Fojt 						     (const u8 *) &resp_fcap,
1058*a1157835SDaniel Fojt 						     sizeof(resp_fcap), freq,
1059*a1157835SDaniel Fojt 						     NULL, 0);
1060*a1157835SDaniel Fojt 	} else if (msg.status && p2p->p2ps_prov) {
1061*a1157835SDaniel Fojt 		p2p->p2ps_prov->status = P2P_SC_SUCCESS;
1062*a1157835SDaniel Fojt 		p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, *msg.status, sa,
1063*a1157835SDaniel Fojt 					     adv_mac, session_mac, group_mac,
1064*a1157835SDaniel Fojt 					     adv_id, session_id, conncap,
1065*a1157835SDaniel Fojt 					     passwd_id,
1066*a1157835SDaniel Fojt 					     msg.persistent_ssid,
1067*a1157835SDaniel Fojt 					     msg.persistent_ssid_len,
1068*a1157835SDaniel Fojt 					     0, 0, NULL,
1069*a1157835SDaniel Fojt 					     (const u8 *) &resp_fcap,
1070*a1157835SDaniel Fojt 					     sizeof(resp_fcap), freq, NULL, 0);
1071*a1157835SDaniel Fojt 	} else if (msg.status) {
1072*a1157835SDaniel Fojt 	} else if (auto_accept && reject == P2P_SC_SUCCESS) {
1073*a1157835SDaniel Fojt 		p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS,
1074*a1157835SDaniel Fojt 					     sa, adv_mac, session_mac,
1075*a1157835SDaniel Fojt 					     group_mac, adv_id, session_id,
1076*a1157835SDaniel Fojt 					     conncap, passwd_id,
1077*a1157835SDaniel Fojt 					     msg.persistent_ssid,
1078*a1157835SDaniel Fojt 					     msg.persistent_ssid_len,
1079*a1157835SDaniel Fojt 					     0, 0, NULL,
1080*a1157835SDaniel Fojt 					     (const u8 *) &resp_fcap,
1081*a1157835SDaniel Fojt 					     sizeof(resp_fcap), freq,
1082*a1157835SDaniel Fojt 					     msg.group_id ?
1083*a1157835SDaniel Fojt 					     msg.group_id + ETH_ALEN : NULL,
1084*a1157835SDaniel Fojt 					     msg.group_id ?
1085*a1157835SDaniel Fojt 					     msg.group_id_len - ETH_ALEN : 0);
1086*a1157835SDaniel Fojt 	} else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
1087*a1157835SDaniel Fojt 		   (!msg.session_info || !msg.session_info_len)) {
1088*a1157835SDaniel Fojt 		p2p->p2ps_prov->method = msg.wps_config_methods;
1089*a1157835SDaniel Fojt 
1090*a1157835SDaniel Fojt 		p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS,
1091*a1157835SDaniel Fojt 					     sa, adv_mac, session_mac,
1092*a1157835SDaniel Fojt 					     group_mac, adv_id, session_id,
1093*a1157835SDaniel Fojt 					     conncap, passwd_id,
1094*a1157835SDaniel Fojt 					     msg.persistent_ssid,
1095*a1157835SDaniel Fojt 					     msg.persistent_ssid_len,
1096*a1157835SDaniel Fojt 					     0, 1, NULL,
1097*a1157835SDaniel Fojt 					     (const u8 *) &resp_fcap,
1098*a1157835SDaniel Fojt 					     sizeof(resp_fcap), freq, NULL, 0);
1099*a1157835SDaniel Fojt 	} else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
1100*a1157835SDaniel Fojt 		size_t buf_len = msg.session_info_len;
1101*a1157835SDaniel Fojt 		char *buf = os_malloc(2 * buf_len + 1);
1102*a1157835SDaniel Fojt 
1103*a1157835SDaniel Fojt 		if (buf) {
1104*a1157835SDaniel Fojt 			p2p->p2ps_prov->method = msg.wps_config_methods;
1105*a1157835SDaniel Fojt 
1106*a1157835SDaniel Fojt 			utf8_escape((char *) msg.session_info, buf_len,
1107*a1157835SDaniel Fojt 				    buf, 2 * buf_len + 1);
1108*a1157835SDaniel Fojt 
1109*a1157835SDaniel Fojt 			p2p->cfg->p2ps_prov_complete(
1110*a1157835SDaniel Fojt 				p2p->cfg->cb_ctx, P2P_SC_SUCCESS, sa,
1111*a1157835SDaniel Fojt 				adv_mac, session_mac, group_mac, adv_id,
1112*a1157835SDaniel Fojt 				session_id, conncap, passwd_id,
1113*a1157835SDaniel Fojt 				msg.persistent_ssid, msg.persistent_ssid_len,
1114*a1157835SDaniel Fojt 				0, 1, buf,
1115*a1157835SDaniel Fojt 				(const u8 *) &resp_fcap, sizeof(resp_fcap),
1116*a1157835SDaniel Fojt 				freq, NULL, 0);
1117*a1157835SDaniel Fojt 
1118*a1157835SDaniel Fojt 			os_free(buf);
1119*a1157835SDaniel Fojt 		}
1120*a1157835SDaniel Fojt 	}
1121*a1157835SDaniel Fojt 
1122*a1157835SDaniel Fojt 	/*
1123*a1157835SDaniel Fojt 	 * prov_disc_req callback is used to generate P2P-PROV-DISC-ENTER-PIN,
1124*a1157835SDaniel Fojt 	 * P2P-PROV-DISC-SHOW-PIN, and P2P-PROV-DISC-PBC-REQ events.
1125*a1157835SDaniel Fojt 	 * Call it either on legacy P2P PD or on P2PS PD only if we need to
1126*a1157835SDaniel Fojt 	 * enter/show PIN.
1127*a1157835SDaniel Fojt 	 *
1128*a1157835SDaniel Fojt 	 * The callback is called in the following cases:
1129*a1157835SDaniel Fojt 	 * 1. Legacy P2P PD request, response status SUCCESS
1130*a1157835SDaniel Fojt 	 * 2. P2PS advertiser, method: DISPLAY, autoaccept: TRUE,
1131*a1157835SDaniel Fojt 	 *    response status: SUCCESS
1132*a1157835SDaniel Fojt 	 * 3. P2PS advertiser, method  DISPLAY, autoaccept: FALSE,
1133*a1157835SDaniel Fojt 	 *    response status: INFO_CURRENTLY_UNAVAILABLE
1134*a1157835SDaniel Fojt 	 * 4. P2PS advertiser, method: KEYPAD, autoaccept==any,
1135*a1157835SDaniel Fojt 	 *    response status: INFO_CURRENTLY_UNAVAILABLE
1136*a1157835SDaniel Fojt 	 * 5. P2PS follow-on with SUCCESS_DEFERRED,
1137*a1157835SDaniel Fojt 	 *    advertiser role: DISPLAY, autoaccept: FALSE,
1138*a1157835SDaniel Fojt 	 *    seeker: KEYPAD, response status: SUCCESS
1139*a1157835SDaniel Fojt 	 */
1140*a1157835SDaniel Fojt 	if (p2p->cfg->prov_disc_req &&
1141*a1157835SDaniel Fojt 	    ((reject == P2P_SC_SUCCESS && !msg.adv_id) ||
1142*a1157835SDaniel Fojt 	     (!msg.status &&
1143*a1157835SDaniel Fojt 	     (reject == P2P_SC_SUCCESS ||
1144*a1157835SDaniel Fojt 	      reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) &&
1145*a1157835SDaniel Fojt 	      passwd_id == DEV_PW_USER_SPECIFIED) ||
1146*a1157835SDaniel Fojt 	     (!msg.status &&
1147*a1157835SDaniel Fojt 	      reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
1148*a1157835SDaniel Fojt 	      passwd_id == DEV_PW_REGISTRAR_SPECIFIED) ||
1149*a1157835SDaniel Fojt 	     (reject == P2P_SC_SUCCESS &&
1150*a1157835SDaniel Fojt 	      msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED &&
1151*a1157835SDaniel Fojt 	       passwd_id == DEV_PW_REGISTRAR_SPECIFIED))) {
11523ff40c12SJohn Marino 		const u8 *dev_addr = sa;
1153*a1157835SDaniel Fojt 
11543ff40c12SJohn Marino 		if (msg.p2p_device_addr)
11553ff40c12SJohn Marino 			dev_addr = msg.p2p_device_addr;
11563ff40c12SJohn Marino 		p2p->cfg->prov_disc_req(p2p->cfg->cb_ctx, sa,
11573ff40c12SJohn Marino 					msg.wps_config_methods,
11583ff40c12SJohn Marino 					dev_addr, msg.pri_dev_type,
11593ff40c12SJohn Marino 					msg.device_name, msg.config_methods,
11603ff40c12SJohn Marino 					msg.capability ? msg.capability[0] : 0,
11613ff40c12SJohn Marino 					msg.capability ? msg.capability[1] :
11623ff40c12SJohn Marino 					0,
11633ff40c12SJohn Marino 					msg.group_id, msg.group_id_len);
11643ff40c12SJohn Marino 	}
1165*a1157835SDaniel Fojt 
1166*a1157835SDaniel Fojt 	if (reject != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE)
1167*a1157835SDaniel Fojt 		p2ps_prov_free(p2p);
1168*a1157835SDaniel Fojt 
1169*a1157835SDaniel Fojt 	if (reject == P2P_SC_SUCCESS) {
1170*a1157835SDaniel Fojt 		switch (config_methods) {
1171*a1157835SDaniel Fojt 		case WPS_CONFIG_DISPLAY:
1172*a1157835SDaniel Fojt 			dev->wps_prov_info = WPS_CONFIG_KEYPAD;
1173*a1157835SDaniel Fojt 			break;
1174*a1157835SDaniel Fojt 		case WPS_CONFIG_KEYPAD:
1175*a1157835SDaniel Fojt 			dev->wps_prov_info = WPS_CONFIG_DISPLAY;
1176*a1157835SDaniel Fojt 			break;
1177*a1157835SDaniel Fojt 		case WPS_CONFIG_PUSHBUTTON:
1178*a1157835SDaniel Fojt 			dev->wps_prov_info = WPS_CONFIG_PUSHBUTTON;
1179*a1157835SDaniel Fojt 			break;
1180*a1157835SDaniel Fojt 		case WPS_CONFIG_P2PS:
1181*a1157835SDaniel Fojt 			dev->wps_prov_info = WPS_CONFIG_P2PS;
1182*a1157835SDaniel Fojt 			break;
1183*a1157835SDaniel Fojt 		default:
1184*a1157835SDaniel Fojt 			dev->wps_prov_info = 0;
1185*a1157835SDaniel Fojt 			break;
1186*a1157835SDaniel Fojt 		}
1187*a1157835SDaniel Fojt 
1188*a1157835SDaniel Fojt 		if (msg.intended_addr)
1189*a1157835SDaniel Fojt 			os_memcpy(dev->interface_addr, msg.intended_addr,
1190*a1157835SDaniel Fojt 				  ETH_ALEN);
1191*a1157835SDaniel Fojt 	}
11923ff40c12SJohn Marino 	p2p_parse_free(&msg);
11933ff40c12SJohn Marino }
11943ff40c12SJohn Marino 
11953ff40c12SJohn Marino 
p2p_validate_p2ps_pd_resp(struct p2p_data * p2p,struct p2p_message * msg)1196*a1157835SDaniel Fojt static int p2p_validate_p2ps_pd_resp(struct p2p_data *p2p,
1197*a1157835SDaniel Fojt 				     struct p2p_message *msg)
1198*a1157835SDaniel Fojt {
1199*a1157835SDaniel Fojt 	u8 conn_cap_go = 0;
1200*a1157835SDaniel Fojt 	u8 conn_cap_cli = 0;
1201*a1157835SDaniel Fojt 	u32 session_id;
1202*a1157835SDaniel Fojt 	u32 adv_id;
1203*a1157835SDaniel Fojt 
1204*a1157835SDaniel Fojt #define P2PS_PD_RESP_CHECK(_val, _attr) \
1205*a1157835SDaniel Fojt 	do { \
1206*a1157835SDaniel Fojt 		if ((_val) && !msg->_attr) { \
1207*a1157835SDaniel Fojt 			p2p_dbg(p2p, "P2PS PD Response missing " #_attr); \
1208*a1157835SDaniel Fojt 			return -1; \
1209*a1157835SDaniel Fojt 		} \
1210*a1157835SDaniel Fojt 	} while (0)
1211*a1157835SDaniel Fojt 
1212*a1157835SDaniel Fojt 	P2PS_PD_RESP_CHECK(1, status);
1213*a1157835SDaniel Fojt 	P2PS_PD_RESP_CHECK(1, adv_id);
1214*a1157835SDaniel Fojt 	P2PS_PD_RESP_CHECK(1, adv_mac);
1215*a1157835SDaniel Fojt 	P2PS_PD_RESP_CHECK(1, capability);
1216*a1157835SDaniel Fojt 	P2PS_PD_RESP_CHECK(1, p2p_device_info);
1217*a1157835SDaniel Fojt 	P2PS_PD_RESP_CHECK(1, session_id);
1218*a1157835SDaniel Fojt 	P2PS_PD_RESP_CHECK(1, session_mac);
1219*a1157835SDaniel Fojt 	P2PS_PD_RESP_CHECK(1, feature_cap);
1220*a1157835SDaniel Fojt 
1221*a1157835SDaniel Fojt 	session_id = WPA_GET_LE32(msg->session_id);
1222*a1157835SDaniel Fojt 	adv_id = WPA_GET_LE32(msg->adv_id);
1223*a1157835SDaniel Fojt 
1224*a1157835SDaniel Fojt 	if (p2p->p2ps_prov->session_id != session_id) {
1225*a1157835SDaniel Fojt 		p2p_dbg(p2p,
1226*a1157835SDaniel Fojt 			"Ignore PD Response with unexpected Session ID");
1227*a1157835SDaniel Fojt 		return -1;
1228*a1157835SDaniel Fojt 	}
1229*a1157835SDaniel Fojt 
1230*a1157835SDaniel Fojt 	if (os_memcmp(p2p->p2ps_prov->session_mac, msg->session_mac,
1231*a1157835SDaniel Fojt 		      ETH_ALEN)) {
1232*a1157835SDaniel Fojt 		p2p_dbg(p2p,
1233*a1157835SDaniel Fojt 			"Ignore PD Response with unexpected Session MAC");
1234*a1157835SDaniel Fojt 		return -1;
1235*a1157835SDaniel Fojt 	}
1236*a1157835SDaniel Fojt 
1237*a1157835SDaniel Fojt 	if (p2p->p2ps_prov->adv_id != adv_id) {
1238*a1157835SDaniel Fojt 		p2p_dbg(p2p,
1239*a1157835SDaniel Fojt 			"Ignore PD Response with unexpected Advertisement ID");
1240*a1157835SDaniel Fojt 		return -1;
1241*a1157835SDaniel Fojt 	}
1242*a1157835SDaniel Fojt 
1243*a1157835SDaniel Fojt 	if (os_memcmp(p2p->p2ps_prov->adv_mac, msg->adv_mac, ETH_ALEN) != 0) {
1244*a1157835SDaniel Fojt 		p2p_dbg(p2p,
1245*a1157835SDaniel Fojt 			"Ignore PD Response with unexpected Advertisement MAC");
1246*a1157835SDaniel Fojt 		return -1;
1247*a1157835SDaniel Fojt 	}
1248*a1157835SDaniel Fojt 
1249*a1157835SDaniel Fojt 	if (msg->listen_channel) {
1250*a1157835SDaniel Fojt 		p2p_dbg(p2p,
1251*a1157835SDaniel Fojt 			"Ignore malformed PD Response - unexpected Listen Channel");
1252*a1157835SDaniel Fojt 		return -1;
1253*a1157835SDaniel Fojt 	}
1254*a1157835SDaniel Fojt 
1255*a1157835SDaniel Fojt 	if (*msg->status == P2P_SC_SUCCESS &&
1256*a1157835SDaniel Fojt 	    !(!!msg->conn_cap ^ !!msg->persistent_dev)) {
1257*a1157835SDaniel Fojt 		p2p_dbg(p2p,
1258*a1157835SDaniel Fojt 			"Ignore malformed PD Response - either conn_cap or persistent group should be present");
1259*a1157835SDaniel Fojt 		return -1;
1260*a1157835SDaniel Fojt 	}
1261*a1157835SDaniel Fojt 
1262*a1157835SDaniel Fojt 	if (msg->persistent_dev && *msg->status != P2P_SC_SUCCESS) {
1263*a1157835SDaniel Fojt 		p2p_dbg(p2p,
1264*a1157835SDaniel Fojt 			"Ignore malformed PD Response - persistent group is present, but the status isn't success");
1265*a1157835SDaniel Fojt 		return -1;
1266*a1157835SDaniel Fojt 	}
1267*a1157835SDaniel Fojt 
1268*a1157835SDaniel Fojt 	if (msg->conn_cap) {
1269*a1157835SDaniel Fojt 		conn_cap_go = *msg->conn_cap == P2PS_SETUP_GROUP_OWNER;
1270*a1157835SDaniel Fojt 		conn_cap_cli = *msg->conn_cap == P2PS_SETUP_CLIENT;
1271*a1157835SDaniel Fojt 	}
1272*a1157835SDaniel Fojt 
1273*a1157835SDaniel Fojt 	P2PS_PD_RESP_CHECK(msg->persistent_dev || conn_cap_go || conn_cap_cli,
1274*a1157835SDaniel Fojt 			   channel_list);
1275*a1157835SDaniel Fojt 	P2PS_PD_RESP_CHECK(msg->persistent_dev || conn_cap_go || conn_cap_cli,
1276*a1157835SDaniel Fojt 			   config_timeout);
1277*a1157835SDaniel Fojt 
1278*a1157835SDaniel Fojt 	P2PS_PD_RESP_CHECK(conn_cap_go, group_id);
1279*a1157835SDaniel Fojt 	P2PS_PD_RESP_CHECK(conn_cap_go, intended_addr);
1280*a1157835SDaniel Fojt 	P2PS_PD_RESP_CHECK(conn_cap_go, operating_channel);
1281*a1157835SDaniel Fojt 	/*
1282*a1157835SDaniel Fojt 	 * TODO: Also validate that operating channel is present if the device
1283*a1157835SDaniel Fojt 	 * is a GO in a persistent group. We can't do it here since we don't
1284*a1157835SDaniel Fojt 	 * know what is the role of the peer. It should be probably done in
1285*a1157835SDaniel Fojt 	 * p2ps_prov_complete callback, but currently operating channel isn't
1286*a1157835SDaniel Fojt 	 * passed to it.
1287*a1157835SDaniel Fojt 	 */
1288*a1157835SDaniel Fojt 
1289*a1157835SDaniel Fojt #undef P2PS_PD_RESP_CHECK
1290*a1157835SDaniel Fojt 
1291*a1157835SDaniel Fojt 	return 0;
1292*a1157835SDaniel Fojt }
1293*a1157835SDaniel Fojt 
1294*a1157835SDaniel Fojt 
p2p_process_prov_disc_resp(struct p2p_data * p2p,const u8 * sa,const u8 * data,size_t len)12953ff40c12SJohn Marino void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa,
12963ff40c12SJohn Marino 				const u8 *data, size_t len)
12973ff40c12SJohn Marino {
12983ff40c12SJohn Marino 	struct p2p_message msg;
12993ff40c12SJohn Marino 	struct p2p_device *dev;
13003ff40c12SJohn Marino 	u16 report_config_methods = 0, req_config_methods;
1301*a1157835SDaniel Fojt 	u8 status = P2P_SC_SUCCESS;
1302*a1157835SDaniel Fojt 	u32 adv_id = 0;
1303*a1157835SDaniel Fojt 	u8 conncap = P2PS_SETUP_NEW;
1304*a1157835SDaniel Fojt 	u8 adv_mac[ETH_ALEN];
1305*a1157835SDaniel Fojt 	const u8 *group_mac;
1306*a1157835SDaniel Fojt 	int passwd_id = DEV_PW_DEFAULT;
1307*a1157835SDaniel Fojt 	int p2ps_seeker;
13083ff40c12SJohn Marino 
13093ff40c12SJohn Marino 	if (p2p_parse(data, len, &msg))
13103ff40c12SJohn Marino 		return;
13113ff40c12SJohn Marino 
1312*a1157835SDaniel Fojt 	if (p2p->p2ps_prov && p2p_validate_p2ps_pd_resp(p2p, &msg)) {
1313*a1157835SDaniel Fojt 		p2p_parse_free(&msg);
1314*a1157835SDaniel Fojt 		return;
1315*a1157835SDaniel Fojt 	}
1316*a1157835SDaniel Fojt 
1317*a1157835SDaniel Fojt 	/* Parse the P2PS members present */
1318*a1157835SDaniel Fojt 	if (msg.status)
1319*a1157835SDaniel Fojt 		status = *msg.status;
1320*a1157835SDaniel Fojt 
1321*a1157835SDaniel Fojt 	group_mac = msg.intended_addr;
1322*a1157835SDaniel Fojt 
1323*a1157835SDaniel Fojt 	if (msg.adv_mac)
1324*a1157835SDaniel Fojt 		os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN);
1325*a1157835SDaniel Fojt 	else
1326*a1157835SDaniel Fojt 		os_memset(adv_mac, 0, ETH_ALEN);
1327*a1157835SDaniel Fojt 
1328*a1157835SDaniel Fojt 	if (msg.adv_id)
1329*a1157835SDaniel Fojt 		adv_id = WPA_GET_LE32(msg.adv_id);
1330*a1157835SDaniel Fojt 
1331*a1157835SDaniel Fojt 	if (msg.conn_cap) {
1332*a1157835SDaniel Fojt 		conncap = *msg.conn_cap;
1333*a1157835SDaniel Fojt 
1334*a1157835SDaniel Fojt 		/* Switch bits to local relative */
1335*a1157835SDaniel Fojt 		switch (conncap) {
1336*a1157835SDaniel Fojt 		case P2PS_SETUP_GROUP_OWNER:
1337*a1157835SDaniel Fojt 			conncap = P2PS_SETUP_CLIENT;
1338*a1157835SDaniel Fojt 			break;
1339*a1157835SDaniel Fojt 		case P2PS_SETUP_CLIENT:
1340*a1157835SDaniel Fojt 			conncap = P2PS_SETUP_GROUP_OWNER;
1341*a1157835SDaniel Fojt 			break;
1342*a1157835SDaniel Fojt 		}
1343*a1157835SDaniel Fojt 	}
1344*a1157835SDaniel Fojt 
13453ff40c12SJohn Marino 	p2p_dbg(p2p, "Received Provision Discovery Response from " MACSTR
13463ff40c12SJohn Marino 		" with config methods 0x%x",
13473ff40c12SJohn Marino 		MAC2STR(sa), msg.wps_config_methods);
13483ff40c12SJohn Marino 
13493ff40c12SJohn Marino 	dev = p2p_get_device(p2p, sa);
13503ff40c12SJohn Marino 	if (dev == NULL || !dev->req_config_methods) {
13513ff40c12SJohn Marino 		p2p_dbg(p2p, "Ignore Provision Discovery Response from " MACSTR
13523ff40c12SJohn Marino 			" with no pending request", MAC2STR(sa));
13533ff40c12SJohn Marino 		p2p_parse_free(&msg);
13543ff40c12SJohn Marino 		return;
1355*a1157835SDaniel Fojt 	} else if (msg.wfd_subelems) {
1356*a1157835SDaniel Fojt 		wpabuf_free(dev->info.wfd_subelems);
1357*a1157835SDaniel Fojt 		dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
13583ff40c12SJohn Marino 	}
13593ff40c12SJohn Marino 
13603ff40c12SJohn Marino 	if (dev->dialog_token != msg.dialog_token) {
13613ff40c12SJohn Marino 		p2p_dbg(p2p, "Ignore Provision Discovery Response with unexpected Dialog Token %u (expected %u)",
13623ff40c12SJohn Marino 			msg.dialog_token, dev->dialog_token);
13633ff40c12SJohn Marino 		p2p_parse_free(&msg);
13643ff40c12SJohn Marino 		return;
13653ff40c12SJohn Marino 	}
13663ff40c12SJohn Marino 
13673ff40c12SJohn Marino 	if (p2p->pending_action_state == P2P_PENDING_PD) {
13683ff40c12SJohn Marino 		os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN);
13693ff40c12SJohn Marino 		p2p->pending_action_state = P2P_NO_PENDING_ACTION;
13703ff40c12SJohn Marino 	}
13713ff40c12SJohn Marino 
1372*a1157835SDaniel Fojt 	p2ps_seeker = p2p->p2ps_prov && p2p->p2ps_prov->pd_seeker;
1373*a1157835SDaniel Fojt 
13743ff40c12SJohn Marino 	/*
13753ff40c12SJohn Marino 	 * Use a local copy of the requested config methods since
13763ff40c12SJohn Marino 	 * p2p_reset_pending_pd() can clear this in the peer entry.
13773ff40c12SJohn Marino 	 */
13783ff40c12SJohn Marino 	req_config_methods = dev->req_config_methods;
13793ff40c12SJohn Marino 
13803ff40c12SJohn Marino 	/*
13813ff40c12SJohn Marino 	 * If the response is from the peer to whom a user initiated request
13823ff40c12SJohn Marino 	 * was sent earlier, we reset that state info here.
13833ff40c12SJohn Marino 	 */
13843ff40c12SJohn Marino 	if (p2p->user_initiated_pd &&
13853ff40c12SJohn Marino 	    os_memcmp(p2p->pending_pd_devaddr, sa, ETH_ALEN) == 0)
13863ff40c12SJohn Marino 		p2p_reset_pending_pd(p2p);
13873ff40c12SJohn Marino 
13883ff40c12SJohn Marino 	if (msg.wps_config_methods != req_config_methods) {
13893ff40c12SJohn Marino 		p2p_dbg(p2p, "Peer rejected our Provision Discovery Request (received config_methods 0x%x expected 0x%x",
13903ff40c12SJohn Marino 			msg.wps_config_methods, req_config_methods);
13913ff40c12SJohn Marino 		if (p2p->cfg->prov_disc_fail)
13923ff40c12SJohn Marino 			p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa,
1393*a1157835SDaniel Fojt 						 P2P_PROV_DISC_REJECTED,
1394*a1157835SDaniel Fojt 						 adv_id, adv_mac, NULL);
13953ff40c12SJohn Marino 		p2p_parse_free(&msg);
1396*a1157835SDaniel Fojt 		p2ps_prov_free(p2p);
13973ff40c12SJohn Marino 		goto out;
13983ff40c12SJohn Marino 	}
13993ff40c12SJohn Marino 
14003ff40c12SJohn Marino 	report_config_methods = req_config_methods;
14013ff40c12SJohn Marino 	dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
1402*a1157835SDaniel Fojt 			P2P_DEV_PD_PEER_KEYPAD |
1403*a1157835SDaniel Fojt 			P2P_DEV_PD_PEER_P2PS);
14043ff40c12SJohn Marino 	if (req_config_methods & WPS_CONFIG_DISPLAY) {
14053ff40c12SJohn Marino 		p2p_dbg(p2p, "Peer " MACSTR
14063ff40c12SJohn Marino 			" accepted to show a PIN on display", MAC2STR(sa));
14073ff40c12SJohn Marino 		dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
1408*a1157835SDaniel Fojt 		passwd_id = DEV_PW_REGISTRAR_SPECIFIED;
14093ff40c12SJohn Marino 	} else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
14103ff40c12SJohn Marino 		p2p_dbg(p2p, "Peer " MACSTR
14113ff40c12SJohn Marino 			" accepted to write our PIN using keypad",
14123ff40c12SJohn Marino 			MAC2STR(sa));
14133ff40c12SJohn Marino 		dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
1414*a1157835SDaniel Fojt 		passwd_id = DEV_PW_USER_SPECIFIED;
1415*a1157835SDaniel Fojt 	} else if (msg.wps_config_methods & WPS_CONFIG_P2PS) {
1416*a1157835SDaniel Fojt 		p2p_dbg(p2p, "Peer " MACSTR " accepted P2PS PIN",
1417*a1157835SDaniel Fojt 			MAC2STR(sa));
1418*a1157835SDaniel Fojt 		dev->flags |= P2P_DEV_PD_PEER_P2PS;
1419*a1157835SDaniel Fojt 		passwd_id = DEV_PW_P2PS_DEFAULT;
1420*a1157835SDaniel Fojt 	}
1421*a1157835SDaniel Fojt 
1422*a1157835SDaniel Fojt 	if ((status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED) &&
1423*a1157835SDaniel Fojt 	    p2p->p2ps_prov) {
1424*a1157835SDaniel Fojt 		dev->oper_freq = 0;
1425*a1157835SDaniel Fojt 
1426*a1157835SDaniel Fojt 		/*
1427*a1157835SDaniel Fojt 		 * Save the reported channel list and operating frequency.
1428*a1157835SDaniel Fojt 		 * Note that the specification mandates that the responder
1429*a1157835SDaniel Fojt 		 * should include in the channel list only channels reported by
1430*a1157835SDaniel Fojt 		 * the initiator, so this is only a sanity check, and if this
1431*a1157835SDaniel Fojt 		 * fails the flow would continue, although it would probably
1432*a1157835SDaniel Fojt 		 * fail. Same is true for the operating channel.
1433*a1157835SDaniel Fojt 		 */
1434*a1157835SDaniel Fojt 		if (msg.channel_list && msg.channel_list_len &&
1435*a1157835SDaniel Fojt 		    p2p_peer_channels_check(p2p, &p2p->channels, dev,
1436*a1157835SDaniel Fojt 					    msg.channel_list,
1437*a1157835SDaniel Fojt 					    msg.channel_list_len) < 0)
1438*a1157835SDaniel Fojt 			p2p_dbg(p2p, "P2PS PD Response - no common channels");
1439*a1157835SDaniel Fojt 
1440*a1157835SDaniel Fojt 		if (msg.operating_channel) {
1441*a1157835SDaniel Fojt 			if (p2p_channels_includes(&p2p->channels,
1442*a1157835SDaniel Fojt 						  msg.operating_channel[3],
1443*a1157835SDaniel Fojt 						  msg.operating_channel[4]) &&
1444*a1157835SDaniel Fojt 			    p2p_channels_includes(&dev->channels,
1445*a1157835SDaniel Fojt 						  msg.operating_channel[3],
1446*a1157835SDaniel Fojt 						  msg.operating_channel[4])) {
1447*a1157835SDaniel Fojt 				dev->oper_freq =
1448*a1157835SDaniel Fojt 					p2p_channel_to_freq(
1449*a1157835SDaniel Fojt 						msg.operating_channel[3],
1450*a1157835SDaniel Fojt 						msg.operating_channel[4]);
1451*a1157835SDaniel Fojt 			} else {
1452*a1157835SDaniel Fojt 				p2p_dbg(p2p,
1453*a1157835SDaniel Fojt 					"P2PS PD Response - invalid operating channel");
1454*a1157835SDaniel Fojt 			}
1455*a1157835SDaniel Fojt 		}
1456*a1157835SDaniel Fojt 
1457*a1157835SDaniel Fojt 		if (p2p->cfg->p2ps_prov_complete) {
1458*a1157835SDaniel Fojt 			int freq = 0;
1459*a1157835SDaniel Fojt 
1460*a1157835SDaniel Fojt 			if (conncap == P2PS_SETUP_GROUP_OWNER) {
1461*a1157835SDaniel Fojt 				u8 tmp;
1462*a1157835SDaniel Fojt 
1463*a1157835SDaniel Fojt 				/*
1464*a1157835SDaniel Fojt 				 * Re-select the operating channel as it is
1465*a1157835SDaniel Fojt 				 * possible that original channel is no longer
1466*a1157835SDaniel Fojt 				 * valid. This should not really fail.
1467*a1157835SDaniel Fojt 				 */
1468*a1157835SDaniel Fojt 				if (p2p_go_select_channel(p2p, dev, &tmp) < 0)
1469*a1157835SDaniel Fojt 					p2p_dbg(p2p,
1470*a1157835SDaniel Fojt 						"P2PS PD channel selection failed");
1471*a1157835SDaniel Fojt 
1472*a1157835SDaniel Fojt 				freq = p2p_channel_to_freq(p2p->op_reg_class,
1473*a1157835SDaniel Fojt 							   p2p->op_channel);
1474*a1157835SDaniel Fojt 				if (freq < 0)
1475*a1157835SDaniel Fojt 					freq = 0;
1476*a1157835SDaniel Fojt 			}
1477*a1157835SDaniel Fojt 
1478*a1157835SDaniel Fojt 			p2p->cfg->p2ps_prov_complete(
1479*a1157835SDaniel Fojt 				p2p->cfg->cb_ctx, status, sa, adv_mac,
1480*a1157835SDaniel Fojt 				p2p->p2ps_prov->session_mac,
1481*a1157835SDaniel Fojt 				group_mac, adv_id, p2p->p2ps_prov->session_id,
1482*a1157835SDaniel Fojt 				conncap, passwd_id, msg.persistent_ssid,
1483*a1157835SDaniel Fojt 				msg.persistent_ssid_len, 1, 0, NULL,
1484*a1157835SDaniel Fojt 				msg.feature_cap, msg.feature_cap_len, freq,
1485*a1157835SDaniel Fojt 				msg.group_id ? msg.group_id + ETH_ALEN : NULL,
1486*a1157835SDaniel Fojt 				msg.group_id ? msg.group_id_len - ETH_ALEN : 0);
1487*a1157835SDaniel Fojt 		}
1488*a1157835SDaniel Fojt 		p2ps_prov_free(p2p);
1489*a1157835SDaniel Fojt 	} else if (status != P2P_SC_SUCCESS &&
1490*a1157835SDaniel Fojt 		   status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
1491*a1157835SDaniel Fojt 		   status != P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) {
1492*a1157835SDaniel Fojt 		if (p2p->cfg->p2ps_prov_complete)
1493*a1157835SDaniel Fojt 			p2p->cfg->p2ps_prov_complete(
1494*a1157835SDaniel Fojt 				p2p->cfg->cb_ctx, status, sa, adv_mac,
1495*a1157835SDaniel Fojt 				p2p->p2ps_prov->session_mac,
1496*a1157835SDaniel Fojt 				group_mac, adv_id, p2p->p2ps_prov->session_id,
1497*a1157835SDaniel Fojt 				0, 0, NULL, 0, 1, 0, NULL, NULL, 0, 0, NULL, 0);
1498*a1157835SDaniel Fojt 		p2ps_prov_free(p2p);
1499*a1157835SDaniel Fojt 	}
1500*a1157835SDaniel Fojt 
1501*a1157835SDaniel Fojt 	if (status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
1502*a1157835SDaniel Fojt 		if (p2p->cfg->remove_stale_groups) {
1503*a1157835SDaniel Fojt 			p2p->cfg->remove_stale_groups(p2p->cfg->cb_ctx,
1504*a1157835SDaniel Fojt 						      dev->info.p2p_device_addr,
1505*a1157835SDaniel Fojt 						      NULL, NULL, 0);
1506*a1157835SDaniel Fojt 		}
1507*a1157835SDaniel Fojt 
1508*a1157835SDaniel Fojt 		if (msg.session_info && msg.session_info_len) {
1509*a1157835SDaniel Fojt 			size_t info_len = msg.session_info_len;
1510*a1157835SDaniel Fojt 			char *deferred_sess_resp = os_malloc(2 * info_len + 1);
1511*a1157835SDaniel Fojt 
1512*a1157835SDaniel Fojt 			if (!deferred_sess_resp) {
1513*a1157835SDaniel Fojt 				p2p_parse_free(&msg);
1514*a1157835SDaniel Fojt 				p2ps_prov_free(p2p);
1515*a1157835SDaniel Fojt 				goto out;
1516*a1157835SDaniel Fojt 			}
1517*a1157835SDaniel Fojt 			utf8_escape((char *) msg.session_info, info_len,
1518*a1157835SDaniel Fojt 				    deferred_sess_resp, 2 * info_len + 1);
1519*a1157835SDaniel Fojt 
1520*a1157835SDaniel Fojt 			if (p2p->cfg->prov_disc_fail)
1521*a1157835SDaniel Fojt 				p2p->cfg->prov_disc_fail(
1522*a1157835SDaniel Fojt 					p2p->cfg->cb_ctx, sa,
1523*a1157835SDaniel Fojt 					P2P_PROV_DISC_INFO_UNAVAILABLE,
1524*a1157835SDaniel Fojt 					adv_id, adv_mac,
1525*a1157835SDaniel Fojt 					deferred_sess_resp);
1526*a1157835SDaniel Fojt 			os_free(deferred_sess_resp);
1527*a1157835SDaniel Fojt 		} else
1528*a1157835SDaniel Fojt 			if (p2p->cfg->prov_disc_fail)
1529*a1157835SDaniel Fojt 				p2p->cfg->prov_disc_fail(
1530*a1157835SDaniel Fojt 					p2p->cfg->cb_ctx, sa,
1531*a1157835SDaniel Fojt 					P2P_PROV_DISC_INFO_UNAVAILABLE,
1532*a1157835SDaniel Fojt 					adv_id, adv_mac, NULL);
1533*a1157835SDaniel Fojt 	} else if (status != P2P_SC_SUCCESS) {
1534*a1157835SDaniel Fojt 		p2p_dbg(p2p, "Peer rejected our Provision Discovery Request");
1535*a1157835SDaniel Fojt 		if (p2p->cfg->prov_disc_fail)
1536*a1157835SDaniel Fojt 			p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa,
1537*a1157835SDaniel Fojt 						 P2P_PROV_DISC_REJECTED,
1538*a1157835SDaniel Fojt 						 adv_id, adv_mac, NULL);
1539*a1157835SDaniel Fojt 		p2p_parse_free(&msg);
1540*a1157835SDaniel Fojt 		p2ps_prov_free(p2p);
1541*a1157835SDaniel Fojt 		goto out;
15423ff40c12SJohn Marino 	}
15433ff40c12SJohn Marino 
15443ff40c12SJohn Marino 	/* Store the provisioning info */
15453ff40c12SJohn Marino 	dev->wps_prov_info = msg.wps_config_methods;
1546*a1157835SDaniel Fojt 	if (msg.intended_addr)
1547*a1157835SDaniel Fojt 		os_memcpy(dev->interface_addr, msg.intended_addr, ETH_ALEN);
15483ff40c12SJohn Marino 
15493ff40c12SJohn Marino 	p2p_parse_free(&msg);
15503ff40c12SJohn Marino 
15513ff40c12SJohn Marino out:
15523ff40c12SJohn Marino 	dev->req_config_methods = 0;
15533ff40c12SJohn Marino 	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
15543ff40c12SJohn Marino 	if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG) {
15553ff40c12SJohn Marino 		p2p_dbg(p2p, "Start GO Neg after the PD-before-GO-Neg workaround with "
15563ff40c12SJohn Marino 			MACSTR, MAC2STR(dev->info.p2p_device_addr));
15573ff40c12SJohn Marino 		dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG;
15583ff40c12SJohn Marino 		p2p_connect_send(p2p, dev);
15593ff40c12SJohn Marino 		return;
15603ff40c12SJohn Marino 	}
1561*a1157835SDaniel Fojt 
1562*a1157835SDaniel Fojt 	/*
1563*a1157835SDaniel Fojt 	 * prov_disc_resp callback is used to generate P2P-PROV-DISC-ENTER-PIN,
1564*a1157835SDaniel Fojt 	 * P2P-PROV-DISC-SHOW-PIN, and P2P-PROV-DISC-PBC-REQ events.
1565*a1157835SDaniel Fojt 	 * Call it only for a legacy P2P PD or for P2PS PD scenarios where
1566*a1157835SDaniel Fojt 	 * show/enter PIN events are needed.
1567*a1157835SDaniel Fojt 	 *
1568*a1157835SDaniel Fojt 	 * The callback is called in the following cases:
1569*a1157835SDaniel Fojt 	 * 1. Legacy P2P PD response with a status SUCCESS
1570*a1157835SDaniel Fojt 	 * 2. P2PS, advertiser method: DISPLAY, autoaccept: true,
1571*a1157835SDaniel Fojt 	 *    response status: SUCCESS, local method KEYPAD
1572*a1157835SDaniel Fojt 	 * 3. P2PS, advertiser method: KEYPAD,Seeker side,
1573*a1157835SDaniel Fojt 	 *    response status: INFO_CURRENTLY_UNAVAILABLE,
1574*a1157835SDaniel Fojt 	 *    local method: DISPLAY
1575*a1157835SDaniel Fojt 	 */
1576*a1157835SDaniel Fojt 	if (p2p->cfg->prov_disc_resp &&
1577*a1157835SDaniel Fojt 	    ((status == P2P_SC_SUCCESS && !adv_id) ||
1578*a1157835SDaniel Fojt 	     (p2ps_seeker && status == P2P_SC_SUCCESS &&
1579*a1157835SDaniel Fojt 	      passwd_id == DEV_PW_REGISTRAR_SPECIFIED) ||
1580*a1157835SDaniel Fojt 	     (p2ps_seeker &&
1581*a1157835SDaniel Fojt 	      status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
1582*a1157835SDaniel Fojt 	      passwd_id == DEV_PW_USER_SPECIFIED)))
15833ff40c12SJohn Marino 		p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa,
15843ff40c12SJohn Marino 					 report_config_methods);
15853ff40c12SJohn Marino 
15863ff40c12SJohn Marino 	if (p2p->state == P2P_PD_DURING_FIND) {
1587*a1157835SDaniel Fojt 		p2p_stop_listen_for_freq(p2p, 0);
15883ff40c12SJohn Marino 		p2p_continue_find(p2p);
15893ff40c12SJohn Marino 	}
15903ff40c12SJohn Marino }
15913ff40c12SJohn Marino 
15923ff40c12SJohn Marino 
p2p_send_prov_disc_req(struct p2p_data * p2p,struct p2p_device * dev,int join,int force_freq)15933ff40c12SJohn Marino int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev,
15943ff40c12SJohn Marino 			   int join, int force_freq)
15953ff40c12SJohn Marino {
15963ff40c12SJohn Marino 	struct wpabuf *req;
15973ff40c12SJohn Marino 	int freq;
15983ff40c12SJohn Marino 
15993ff40c12SJohn Marino 	if (force_freq > 0)
16003ff40c12SJohn Marino 		freq = force_freq;
16013ff40c12SJohn Marino 	else
16023ff40c12SJohn Marino 		freq = dev->listen_freq > 0 ? dev->listen_freq :
16033ff40c12SJohn Marino 			dev->oper_freq;
16043ff40c12SJohn Marino 	if (freq <= 0) {
16053ff40c12SJohn Marino 		p2p_dbg(p2p, "No Listen/Operating frequency known for the peer "
16063ff40c12SJohn Marino 			MACSTR " to send Provision Discovery Request",
16073ff40c12SJohn Marino 			MAC2STR(dev->info.p2p_device_addr));
16083ff40c12SJohn Marino 		return -1;
16093ff40c12SJohn Marino 	}
16103ff40c12SJohn Marino 
16113ff40c12SJohn Marino 	if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
16123ff40c12SJohn Marino 		if (!(dev->info.dev_capab &
16133ff40c12SJohn Marino 		      P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
16143ff40c12SJohn Marino 			p2p_dbg(p2p, "Cannot use PD with P2P Device " MACSTR
16153ff40c12SJohn Marino 				" that is in a group and is not discoverable",
16163ff40c12SJohn Marino 				MAC2STR(dev->info.p2p_device_addr));
16173ff40c12SJohn Marino 			return -1;
16183ff40c12SJohn Marino 		}
16193ff40c12SJohn Marino 		/* TODO: use device discoverability request through GO */
16203ff40c12SJohn Marino 	}
16213ff40c12SJohn Marino 
1622*a1157835SDaniel Fojt 	if (p2p->p2ps_prov) {
1623*a1157835SDaniel Fojt 		if (p2p->p2ps_prov->status == P2P_SC_SUCCESS_DEFERRED) {
1624*a1157835SDaniel Fojt 			if (p2p->p2ps_prov->method == WPS_CONFIG_DISPLAY)
1625*a1157835SDaniel Fojt 				dev->req_config_methods = WPS_CONFIG_KEYPAD;
1626*a1157835SDaniel Fojt 			else if (p2p->p2ps_prov->method == WPS_CONFIG_KEYPAD)
1627*a1157835SDaniel Fojt 				dev->req_config_methods = WPS_CONFIG_DISPLAY;
1628*a1157835SDaniel Fojt 			else
1629*a1157835SDaniel Fojt 				dev->req_config_methods = WPS_CONFIG_P2PS;
1630*a1157835SDaniel Fojt 		} else {
1631*a1157835SDaniel Fojt 			/* Order of preference, based on peer's capabilities */
1632*a1157835SDaniel Fojt 			if (p2p->p2ps_prov->method)
1633*a1157835SDaniel Fojt 				dev->req_config_methods =
1634*a1157835SDaniel Fojt 					p2p->p2ps_prov->method;
1635*a1157835SDaniel Fojt 			else if (dev->info.config_methods & WPS_CONFIG_P2PS)
1636*a1157835SDaniel Fojt 				dev->req_config_methods = WPS_CONFIG_P2PS;
1637*a1157835SDaniel Fojt 			else if (dev->info.config_methods & WPS_CONFIG_DISPLAY)
1638*a1157835SDaniel Fojt 				dev->req_config_methods = WPS_CONFIG_DISPLAY;
1639*a1157835SDaniel Fojt 			else
1640*a1157835SDaniel Fojt 				dev->req_config_methods = WPS_CONFIG_KEYPAD;
1641*a1157835SDaniel Fojt 		}
1642*a1157835SDaniel Fojt 		p2p_dbg(p2p,
1643*a1157835SDaniel Fojt 			"Building PD Request based on P2PS config method 0x%x status %d --> req_config_methods 0x%x",
1644*a1157835SDaniel Fojt 			p2p->p2ps_prov->method, p2p->p2ps_prov->status,
1645*a1157835SDaniel Fojt 			dev->req_config_methods);
1646*a1157835SDaniel Fojt 
1647*a1157835SDaniel Fojt 		if (p2p_prepare_channel(p2p, dev, p2p->p2ps_prov->force_freq,
1648*a1157835SDaniel Fojt 					p2p->p2ps_prov->pref_freq, 1) < 0)
1649*a1157835SDaniel Fojt 			return -1;
1650*a1157835SDaniel Fojt 	}
1651*a1157835SDaniel Fojt 
1652*a1157835SDaniel Fojt 	req = p2p_build_prov_disc_req(p2p, dev, join);
16533ff40c12SJohn Marino 	if (req == NULL)
16543ff40c12SJohn Marino 		return -1;
16553ff40c12SJohn Marino 
16563ff40c12SJohn Marino 	if (p2p->state != P2P_IDLE)
16573ff40c12SJohn Marino 		p2p_stop_listen_for_freq(p2p, freq);
16583ff40c12SJohn Marino 	p2p->pending_action_state = P2P_PENDING_PD;
16593ff40c12SJohn Marino 	if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
16603ff40c12SJohn Marino 			    p2p->cfg->dev_addr, dev->info.p2p_device_addr,
16613ff40c12SJohn Marino 			    wpabuf_head(req), wpabuf_len(req), 200) < 0) {
16623ff40c12SJohn Marino 		p2p_dbg(p2p, "Failed to send Action frame");
16633ff40c12SJohn Marino 		wpabuf_free(req);
16643ff40c12SJohn Marino 		return -1;
16653ff40c12SJohn Marino 	}
16663ff40c12SJohn Marino 
16673ff40c12SJohn Marino 	os_memcpy(p2p->pending_pd_devaddr, dev->info.p2p_device_addr, ETH_ALEN);
16683ff40c12SJohn Marino 
16693ff40c12SJohn Marino 	wpabuf_free(req);
16703ff40c12SJohn Marino 	return 0;
16713ff40c12SJohn Marino }
16723ff40c12SJohn Marino 
16733ff40c12SJohn Marino 
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)16743ff40c12SJohn Marino int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr,
1675*a1157835SDaniel Fojt 		      struct p2ps_provision *p2ps_prov,
16763ff40c12SJohn Marino 		      u16 config_methods, int join, int force_freq,
16773ff40c12SJohn Marino 		      int user_initiated_pd)
16783ff40c12SJohn Marino {
16793ff40c12SJohn Marino 	struct p2p_device *dev;
16803ff40c12SJohn Marino 
16813ff40c12SJohn Marino 	dev = p2p_get_device(p2p, peer_addr);
16823ff40c12SJohn Marino 	if (dev == NULL)
16833ff40c12SJohn Marino 		dev = p2p_get_device_interface(p2p, peer_addr);
16843ff40c12SJohn Marino 	if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
16853ff40c12SJohn Marino 		p2p_dbg(p2p, "Provision Discovery Request destination " MACSTR
16863ff40c12SJohn Marino 			" not yet known", MAC2STR(peer_addr));
1687*a1157835SDaniel Fojt 		os_free(p2ps_prov);
16883ff40c12SJohn Marino 		return -1;
16893ff40c12SJohn Marino 	}
16903ff40c12SJohn Marino 
16913ff40c12SJohn Marino 	p2p_dbg(p2p, "Provision Discovery Request with " MACSTR
16923ff40c12SJohn Marino 		" (config methods 0x%x)",
16933ff40c12SJohn Marino 		MAC2STR(peer_addr), config_methods);
1694*a1157835SDaniel Fojt 	if (config_methods == 0 && !p2ps_prov) {
1695*a1157835SDaniel Fojt 		os_free(p2ps_prov);
16963ff40c12SJohn Marino 		return -1;
1697*a1157835SDaniel Fojt 	}
1698*a1157835SDaniel Fojt 
1699*a1157835SDaniel Fojt 	if (p2ps_prov && p2ps_prov->status == P2P_SC_SUCCESS_DEFERRED &&
1700*a1157835SDaniel Fojt 	    p2p->p2ps_prov) {
1701*a1157835SDaniel Fojt 		/* Use cached method from deferred provisioning */
1702*a1157835SDaniel Fojt 		p2ps_prov->method = p2p->p2ps_prov->method;
1703*a1157835SDaniel Fojt 	}
17043ff40c12SJohn Marino 
17053ff40c12SJohn Marino 	/* Reset provisioning info */
17063ff40c12SJohn Marino 	dev->wps_prov_info = 0;
1707*a1157835SDaniel Fojt 	p2ps_prov_free(p2p);
1708*a1157835SDaniel Fojt 	p2p->p2ps_prov = p2ps_prov;
17093ff40c12SJohn Marino 
17103ff40c12SJohn Marino 	dev->req_config_methods = config_methods;
17113ff40c12SJohn Marino 	if (join)
17123ff40c12SJohn Marino 		dev->flags |= P2P_DEV_PD_FOR_JOIN;
17133ff40c12SJohn Marino 	else
17143ff40c12SJohn Marino 		dev->flags &= ~P2P_DEV_PD_FOR_JOIN;
17153ff40c12SJohn Marino 
17163ff40c12SJohn Marino 	if (p2p->state != P2P_IDLE && p2p->state != P2P_SEARCH &&
17173ff40c12SJohn Marino 	    p2p->state != P2P_LISTEN_ONLY) {
17183ff40c12SJohn Marino 		p2p_dbg(p2p, "Busy with other operations; postpone Provision Discovery Request with "
17193ff40c12SJohn Marino 			MACSTR " (config methods 0x%x)",
17203ff40c12SJohn Marino 			MAC2STR(peer_addr), config_methods);
17213ff40c12SJohn Marino 		return 0;
17223ff40c12SJohn Marino 	}
17233ff40c12SJohn Marino 
17243ff40c12SJohn Marino 	p2p->user_initiated_pd = user_initiated_pd;
17253ff40c12SJohn Marino 	p2p->pd_force_freq = force_freq;
17263ff40c12SJohn Marino 
17273ff40c12SJohn Marino 	if (p2p->user_initiated_pd)
17283ff40c12SJohn Marino 		p2p->pd_retries = MAX_PROV_DISC_REQ_RETRIES;
17293ff40c12SJohn Marino 
17303ff40c12SJohn Marino 	/*
17313ff40c12SJohn Marino 	 * Assign dialog token here to use the same value in each retry within
17323ff40c12SJohn Marino 	 * the same PD exchange.
17333ff40c12SJohn Marino 	 */
17343ff40c12SJohn Marino 	dev->dialog_token++;
17353ff40c12SJohn Marino 	if (dev->dialog_token == 0)
17363ff40c12SJohn Marino 		dev->dialog_token = 1;
17373ff40c12SJohn Marino 
17383ff40c12SJohn Marino 	return p2p_send_prov_disc_req(p2p, dev, join, force_freq);
17393ff40c12SJohn Marino }
17403ff40c12SJohn Marino 
17413ff40c12SJohn Marino 
p2p_reset_pending_pd(struct p2p_data * p2p)17423ff40c12SJohn Marino void p2p_reset_pending_pd(struct p2p_data *p2p)
17433ff40c12SJohn Marino {
17443ff40c12SJohn Marino 	struct p2p_device *dev;
17453ff40c12SJohn Marino 
17463ff40c12SJohn Marino 	dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
17473ff40c12SJohn Marino 		if (os_memcmp(p2p->pending_pd_devaddr,
17483ff40c12SJohn Marino 			      dev->info.p2p_device_addr, ETH_ALEN))
17493ff40c12SJohn Marino 			continue;
17503ff40c12SJohn Marino 		if (!dev->req_config_methods)
17513ff40c12SJohn Marino 			continue;
17523ff40c12SJohn Marino 		if (dev->flags & P2P_DEV_PD_FOR_JOIN)
17533ff40c12SJohn Marino 			continue;
17543ff40c12SJohn Marino 		/* Reset the config methods of the device */
17553ff40c12SJohn Marino 		dev->req_config_methods = 0;
17563ff40c12SJohn Marino 	}
17573ff40c12SJohn Marino 
17583ff40c12SJohn Marino 	p2p->user_initiated_pd = 0;
17593ff40c12SJohn Marino 	os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN);
17603ff40c12SJohn Marino 	p2p->pd_retries = 0;
17613ff40c12SJohn Marino 	p2p->pd_force_freq = 0;
17623ff40c12SJohn Marino }
1763*a1157835SDaniel Fojt 
1764*a1157835SDaniel Fojt 
p2ps_prov_free(struct p2p_data * p2p)1765*a1157835SDaniel Fojt void p2ps_prov_free(struct p2p_data *p2p)
1766*a1157835SDaniel Fojt {
1767*a1157835SDaniel Fojt 	os_free(p2p->p2ps_prov);
1768*a1157835SDaniel Fojt 	p2p->p2ps_prov = NULL;
1769*a1157835SDaniel Fojt }
1770