xref: /freebsd/contrib/wpa/src/p2p/p2p_build.c (revision a90b9d0159070121c221b966469c3e36d912bf82)
1f05cddf9SRui Paulo /*
2f05cddf9SRui Paulo  * P2P - IE builder
3f05cddf9SRui Paulo  * Copyright (c) 2009-2010, Atheros Communications
4f05cddf9SRui Paulo  *
5f05cddf9SRui Paulo  * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo  * See README for more details.
7f05cddf9SRui Paulo  */
8f05cddf9SRui Paulo 
9f05cddf9SRui Paulo #include "includes.h"
10f05cddf9SRui Paulo 
11f05cddf9SRui Paulo #include "common.h"
12f05cddf9SRui Paulo #include "common/ieee802_11_defs.h"
13*a90b9d01SCy Schubert #include "common/ieee802_11_common.h"
14325151a3SRui Paulo #include "common/qca-vendor.h"
15f05cddf9SRui Paulo #include "wps/wps_i.h"
16f05cddf9SRui Paulo #include "p2p_i.h"
17f05cddf9SRui Paulo 
18f05cddf9SRui Paulo 
p2p_buf_add_action_hdr(struct wpabuf * buf,u8 subtype,u8 dialog_token)19f05cddf9SRui Paulo void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token)
20f05cddf9SRui Paulo {
21f05cddf9SRui Paulo 	wpabuf_put_u8(buf, WLAN_ACTION_VENDOR_SPECIFIC);
225b9c547cSRui Paulo 	wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
23f05cddf9SRui Paulo 
24f05cddf9SRui Paulo 	wpabuf_put_u8(buf, subtype); /* OUI Subtype */
25f05cddf9SRui Paulo 	wpabuf_put_u8(buf, dialog_token);
26f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "P2P: * Dialog Token: %d", dialog_token);
27f05cddf9SRui Paulo }
28f05cddf9SRui Paulo 
29f05cddf9SRui Paulo 
p2p_buf_add_public_action_hdr(struct wpabuf * buf,u8 subtype,u8 dialog_token)30f05cddf9SRui Paulo void p2p_buf_add_public_action_hdr(struct wpabuf *buf, u8 subtype,
31f05cddf9SRui Paulo 				   u8 dialog_token)
32f05cddf9SRui Paulo {
33f05cddf9SRui Paulo 	wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
34f05cddf9SRui Paulo 	wpabuf_put_u8(buf, WLAN_PA_VENDOR_SPECIFIC);
355b9c547cSRui Paulo 	wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
36f05cddf9SRui Paulo 
37f05cddf9SRui Paulo 	wpabuf_put_u8(buf, subtype); /* OUI Subtype */
38f05cddf9SRui Paulo 	wpabuf_put_u8(buf, dialog_token);
39f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "P2P: * Dialog Token: %d", dialog_token);
40f05cddf9SRui Paulo }
41f05cddf9SRui Paulo 
42f05cddf9SRui Paulo 
p2p_buf_add_ie_hdr(struct wpabuf * buf)43f05cddf9SRui Paulo u8 * p2p_buf_add_ie_hdr(struct wpabuf *buf)
44f05cddf9SRui Paulo {
45f05cddf9SRui Paulo 	u8 *len;
46f05cddf9SRui Paulo 
47f05cddf9SRui Paulo 	/* P2P IE header */
48f05cddf9SRui Paulo 	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
49f05cddf9SRui Paulo 	len = wpabuf_put(buf, 1); /* IE length to be filled */
505b9c547cSRui Paulo 	wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
51f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "P2P: * P2P IE header");
52f05cddf9SRui Paulo 	return len;
53f05cddf9SRui Paulo }
54f05cddf9SRui Paulo 
55f05cddf9SRui Paulo 
p2p_buf_update_ie_hdr(struct wpabuf * buf,u8 * len)56f05cddf9SRui Paulo void p2p_buf_update_ie_hdr(struct wpabuf *buf, u8 *len)
57f05cddf9SRui Paulo {
58f05cddf9SRui Paulo 	/* Update P2P IE Length */
59f05cddf9SRui Paulo 	*len = (u8 *) wpabuf_put(buf, 0) - len - 1;
60f05cddf9SRui Paulo }
61f05cddf9SRui Paulo 
62f05cddf9SRui Paulo 
p2p_buf_add_capability(struct wpabuf * buf,u8 dev_capab,u8 group_capab)63f05cddf9SRui Paulo void p2p_buf_add_capability(struct wpabuf *buf, u8 dev_capab, u8 group_capab)
64f05cddf9SRui Paulo {
65f05cddf9SRui Paulo 	/* P2P Capability */
66f05cddf9SRui Paulo 	wpabuf_put_u8(buf, P2P_ATTR_CAPABILITY);
67f05cddf9SRui Paulo 	wpabuf_put_le16(buf, 2);
68f05cddf9SRui Paulo 	wpabuf_put_u8(buf, dev_capab); /* Device Capabilities */
69f05cddf9SRui Paulo 	wpabuf_put_u8(buf, group_capab); /* Group Capabilities */
70f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "P2P: * Capability dev=%02x group=%02x",
71f05cddf9SRui Paulo 		   dev_capab, group_capab);
72f05cddf9SRui Paulo }
73f05cddf9SRui Paulo 
74f05cddf9SRui Paulo 
p2p_buf_add_go_intent(struct wpabuf * buf,u8 go_intent)75f05cddf9SRui Paulo void p2p_buf_add_go_intent(struct wpabuf *buf, u8 go_intent)
76f05cddf9SRui Paulo {
77f05cddf9SRui Paulo 	/* Group Owner Intent */
78f05cddf9SRui Paulo 	wpabuf_put_u8(buf, P2P_ATTR_GROUP_OWNER_INTENT);
79f05cddf9SRui Paulo 	wpabuf_put_le16(buf, 1);
80f05cddf9SRui Paulo 	wpabuf_put_u8(buf, go_intent);
81f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "P2P: * GO Intent: Intent %u Tie breaker %u",
82f05cddf9SRui Paulo 		   go_intent >> 1, go_intent & 0x01);
83f05cddf9SRui Paulo }
84f05cddf9SRui Paulo 
85f05cddf9SRui Paulo 
p2p_buf_add_listen_channel(struct wpabuf * buf,const char * country,u8 reg_class,u8 channel)86f05cddf9SRui Paulo void p2p_buf_add_listen_channel(struct wpabuf *buf, const char *country,
87f05cddf9SRui Paulo 				u8 reg_class, u8 channel)
88f05cddf9SRui Paulo {
89f05cddf9SRui Paulo 	/* Listen Channel */
90f05cddf9SRui Paulo 	wpabuf_put_u8(buf, P2P_ATTR_LISTEN_CHANNEL);
91f05cddf9SRui Paulo 	wpabuf_put_le16(buf, 5);
92f05cddf9SRui Paulo 	wpabuf_put_data(buf, country, 3);
93f05cddf9SRui Paulo 	wpabuf_put_u8(buf, reg_class); /* Regulatory Class */
94f05cddf9SRui Paulo 	wpabuf_put_u8(buf, channel); /* Channel Number */
95f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "P2P: * Listen Channel: Regulatory Class %u "
96f05cddf9SRui Paulo 		   "Channel %u", reg_class, channel);
97f05cddf9SRui Paulo }
98f05cddf9SRui Paulo 
99f05cddf9SRui Paulo 
p2p_buf_add_operating_channel(struct wpabuf * buf,const char * country,u8 reg_class,u8 channel)100f05cddf9SRui Paulo void p2p_buf_add_operating_channel(struct wpabuf *buf, const char *country,
101f05cddf9SRui Paulo 				   u8 reg_class, u8 channel)
102f05cddf9SRui Paulo {
103f05cddf9SRui Paulo 	/* Operating Channel */
104f05cddf9SRui Paulo 	wpabuf_put_u8(buf, P2P_ATTR_OPERATING_CHANNEL);
105f05cddf9SRui Paulo 	wpabuf_put_le16(buf, 5);
106f05cddf9SRui Paulo 	wpabuf_put_data(buf, country, 3);
107f05cddf9SRui Paulo 	wpabuf_put_u8(buf, reg_class); /* Regulatory Class */
108f05cddf9SRui Paulo 	wpabuf_put_u8(buf, channel); /* Channel Number */
109f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "P2P: * Operating Channel: Regulatory Class %u "
110f05cddf9SRui Paulo 		   "Channel %u", reg_class, channel);
111f05cddf9SRui Paulo }
112f05cddf9SRui Paulo 
113f05cddf9SRui Paulo 
p2p_buf_add_pref_channel_list(struct wpabuf * buf,const struct weighted_pcl * pref_freq_list,unsigned int size)114325151a3SRui Paulo void p2p_buf_add_pref_channel_list(struct wpabuf *buf,
115*a90b9d01SCy Schubert 				   const struct weighted_pcl *pref_freq_list,
116325151a3SRui Paulo 				   unsigned int size)
117325151a3SRui Paulo {
118325151a3SRui Paulo 	unsigned int i, count = 0;
119325151a3SRui Paulo 	u8 op_class, op_channel;
120325151a3SRui Paulo 
121325151a3SRui Paulo 	if (!size)
122325151a3SRui Paulo 		return;
123325151a3SRui Paulo 
124325151a3SRui Paulo 	/*
125325151a3SRui Paulo 	 * First, determine the number of P2P supported channels in the
126325151a3SRui Paulo 	 * pref_freq_list returned from driver. This is needed for calculations
127325151a3SRui Paulo 	 * of the vendor IE size.
128325151a3SRui Paulo 	 */
129325151a3SRui Paulo 	for (i = 0; i < size; i++) {
130*a90b9d01SCy Schubert 		if (p2p_freq_to_channel(pref_freq_list[i].freq, &op_class,
131*a90b9d01SCy Schubert 					&op_channel) == 0 &&
132*a90b9d01SCy Schubert 		    !(pref_freq_list[i].flag & WEIGHTED_PCL_EXCLUDE))
133325151a3SRui Paulo 			count++;
134325151a3SRui Paulo 	}
135325151a3SRui Paulo 
136325151a3SRui Paulo 	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
137325151a3SRui Paulo 	wpabuf_put_u8(buf, 4 + count * sizeof(u16));
138325151a3SRui Paulo 	wpabuf_put_be24(buf, OUI_QCA);
139325151a3SRui Paulo 	wpabuf_put_u8(buf, QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST);
140325151a3SRui Paulo 	for (i = 0; i < size; i++) {
141*a90b9d01SCy Schubert 		if (p2p_freq_to_channel(pref_freq_list[i].freq, &op_class,
142*a90b9d01SCy Schubert 					&op_channel) < 0 ||
143*a90b9d01SCy Schubert 		    (pref_freq_list[i].flag & WEIGHTED_PCL_EXCLUDE)) {
144325151a3SRui Paulo 			wpa_printf(MSG_DEBUG, "Unsupported frequency %u MHz",
145*a90b9d01SCy Schubert 				   pref_freq_list[i].freq);
146325151a3SRui Paulo 			continue;
147325151a3SRui Paulo 		}
148325151a3SRui Paulo 		wpabuf_put_u8(buf, op_class);
149325151a3SRui Paulo 		wpabuf_put_u8(buf, op_channel);
150325151a3SRui Paulo 	}
151325151a3SRui Paulo }
152325151a3SRui Paulo 
153325151a3SRui Paulo 
p2p_buf_add_channel_list(struct wpabuf * buf,const char * country,struct p2p_channels * chan,bool is_6ghz_capab)154f05cddf9SRui Paulo void p2p_buf_add_channel_list(struct wpabuf *buf, const char *country,
155*a90b9d01SCy Schubert 			      struct p2p_channels *chan, bool is_6ghz_capab)
156f05cddf9SRui Paulo {
157f05cddf9SRui Paulo 	u8 *len;
158f05cddf9SRui Paulo 	size_t i;
159f05cddf9SRui Paulo 
160f05cddf9SRui Paulo 	/* Channel List */
161f05cddf9SRui Paulo 	wpabuf_put_u8(buf, P2P_ATTR_CHANNEL_LIST);
162f05cddf9SRui Paulo 	len = wpabuf_put(buf, 2); /* IE length to be filled */
163f05cddf9SRui Paulo 	wpabuf_put_data(buf, country, 3); /* Country String */
164f05cddf9SRui Paulo 
165f05cddf9SRui Paulo 	for (i = 0; i < chan->reg_classes; i++) {
166f05cddf9SRui Paulo 		struct p2p_reg_class *c = &chan->reg_class[i];
167*a90b9d01SCy Schubert 
168*a90b9d01SCy Schubert 		if (is_6ghz_op_class(c->reg_class) && !is_6ghz_capab)
169*a90b9d01SCy Schubert 			continue;
170f05cddf9SRui Paulo 		wpabuf_put_u8(buf, c->reg_class);
171f05cddf9SRui Paulo 		wpabuf_put_u8(buf, c->channels);
172f05cddf9SRui Paulo 		wpabuf_put_data(buf, c->channel, c->channels);
173f05cddf9SRui Paulo 	}
174f05cddf9SRui Paulo 
175f05cddf9SRui Paulo 	/* Update attribute length */
176f05cddf9SRui Paulo 	WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
177f05cddf9SRui Paulo 	wpa_hexdump(MSG_DEBUG, "P2P: * Channel List",
178f05cddf9SRui Paulo 		    len + 2, (u8 *) wpabuf_put(buf, 0) - len - 2);
179f05cddf9SRui Paulo }
180f05cddf9SRui Paulo 
181f05cddf9SRui Paulo 
p2p_buf_add_status(struct wpabuf * buf,u8 status)182f05cddf9SRui Paulo void p2p_buf_add_status(struct wpabuf *buf, u8 status)
183f05cddf9SRui Paulo {
184f05cddf9SRui Paulo 	/* Status */
185f05cddf9SRui Paulo 	wpabuf_put_u8(buf, P2P_ATTR_STATUS);
186f05cddf9SRui Paulo 	wpabuf_put_le16(buf, 1);
187f05cddf9SRui Paulo 	wpabuf_put_u8(buf, status);
188f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "P2P: * Status: %d", status);
189f05cddf9SRui Paulo }
190f05cddf9SRui Paulo 
191f05cddf9SRui Paulo 
p2p_buf_add_device_info(struct wpabuf * buf,struct p2p_data * p2p,struct p2p_device * peer)192f05cddf9SRui Paulo void p2p_buf_add_device_info(struct wpabuf *buf, struct p2p_data *p2p,
193f05cddf9SRui Paulo 			     struct p2p_device *peer)
194f05cddf9SRui Paulo {
195f05cddf9SRui Paulo 	u8 *len;
196f05cddf9SRui Paulo 	u16 methods;
197f05cddf9SRui Paulo 	size_t nlen, i;
198f05cddf9SRui Paulo 
199f05cddf9SRui Paulo 	/* P2P Device Info */
200f05cddf9SRui Paulo 	wpabuf_put_u8(buf, P2P_ATTR_DEVICE_INFO);
201f05cddf9SRui Paulo 	len = wpabuf_put(buf, 2); /* IE length to be filled */
202f05cddf9SRui Paulo 
203f05cddf9SRui Paulo 	/* P2P Device address */
204f05cddf9SRui Paulo 	wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN);
205f05cddf9SRui Paulo 
206f05cddf9SRui Paulo 	/* Config Methods */
207f05cddf9SRui Paulo 	methods = 0;
208f05cddf9SRui Paulo 	if (peer && peer->wps_method != WPS_NOT_READY) {
209f05cddf9SRui Paulo 		if (peer->wps_method == WPS_PBC)
210f05cddf9SRui Paulo 			methods |= WPS_CONFIG_PUSHBUTTON;
211780fb4a2SCy Schubert 		else if (peer->wps_method == WPS_P2PS)
2125b9c547cSRui Paulo 			methods |= WPS_CONFIG_P2PS;
213780fb4a2SCy Schubert 		else if (peer->wps_method == WPS_PIN_DISPLAY ||
214780fb4a2SCy Schubert 			 peer->wps_method == WPS_PIN_KEYPAD)
215780fb4a2SCy Schubert 			methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
216f05cddf9SRui Paulo 	} else if (p2p->cfg->config_methods) {
217f05cddf9SRui Paulo 		methods |= p2p->cfg->config_methods &
218f05cddf9SRui Paulo 			(WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_DISPLAY |
2195b9c547cSRui Paulo 			 WPS_CONFIG_KEYPAD | WPS_CONFIG_P2PS);
220f05cddf9SRui Paulo 	} else {
221f05cddf9SRui Paulo 		methods |= WPS_CONFIG_PUSHBUTTON;
222f05cddf9SRui Paulo 		methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
2235b9c547cSRui Paulo 		methods |= WPS_CONFIG_P2PS;
224f05cddf9SRui Paulo 	}
225f05cddf9SRui Paulo 	wpabuf_put_be16(buf, methods);
226f05cddf9SRui Paulo 
227f05cddf9SRui Paulo 	/* Primary Device Type */
228f05cddf9SRui Paulo 	wpabuf_put_data(buf, p2p->cfg->pri_dev_type,
229f05cddf9SRui Paulo 			sizeof(p2p->cfg->pri_dev_type));
230f05cddf9SRui Paulo 
231f05cddf9SRui Paulo 	/* Number of Secondary Device Types */
232f05cddf9SRui Paulo 	wpabuf_put_u8(buf, p2p->cfg->num_sec_dev_types);
233f05cddf9SRui Paulo 
234f05cddf9SRui Paulo 	/* Secondary Device Type List */
235f05cddf9SRui Paulo 	for (i = 0; i < p2p->cfg->num_sec_dev_types; i++)
236f05cddf9SRui Paulo 		wpabuf_put_data(buf, p2p->cfg->sec_dev_type[i],
237f05cddf9SRui Paulo 				WPS_DEV_TYPE_LEN);
238f05cddf9SRui Paulo 
239f05cddf9SRui Paulo 	/* Device Name */
240f05cddf9SRui Paulo 	nlen = p2p->cfg->dev_name ? os_strlen(p2p->cfg->dev_name) : 0;
241f05cddf9SRui Paulo 	wpabuf_put_be16(buf, ATTR_DEV_NAME);
242f05cddf9SRui Paulo 	wpabuf_put_be16(buf, nlen);
243f05cddf9SRui Paulo 	wpabuf_put_data(buf, p2p->cfg->dev_name, nlen);
244f05cddf9SRui Paulo 
245f05cddf9SRui Paulo 	/* Update attribute length */
246f05cddf9SRui Paulo 	WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
247f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "P2P: * Device Info");
248f05cddf9SRui Paulo }
249f05cddf9SRui Paulo 
250f05cddf9SRui Paulo 
p2p_buf_add_device_id(struct wpabuf * buf,const u8 * dev_addr)251f05cddf9SRui Paulo void p2p_buf_add_device_id(struct wpabuf *buf, const u8 *dev_addr)
252f05cddf9SRui Paulo {
253f05cddf9SRui Paulo 	/* P2P Device ID */
254f05cddf9SRui Paulo 	wpabuf_put_u8(buf, P2P_ATTR_DEVICE_ID);
255f05cddf9SRui Paulo 	wpabuf_put_le16(buf, ETH_ALEN);
256f05cddf9SRui Paulo 	wpabuf_put_data(buf, dev_addr, ETH_ALEN);
257f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "P2P: * Device ID: " MACSTR, MAC2STR(dev_addr));
258f05cddf9SRui Paulo }
259f05cddf9SRui Paulo 
260f05cddf9SRui Paulo 
p2p_buf_add_config_timeout(struct wpabuf * buf,u8 go_timeout,u8 client_timeout)261f05cddf9SRui Paulo void p2p_buf_add_config_timeout(struct wpabuf *buf, u8 go_timeout,
262f05cddf9SRui Paulo 				u8 client_timeout)
263f05cddf9SRui Paulo {
264f05cddf9SRui Paulo 	/* Configuration Timeout */
265f05cddf9SRui Paulo 	wpabuf_put_u8(buf, P2P_ATTR_CONFIGURATION_TIMEOUT);
266f05cddf9SRui Paulo 	wpabuf_put_le16(buf, 2);
267f05cddf9SRui Paulo 	wpabuf_put_u8(buf, go_timeout);
268f05cddf9SRui Paulo 	wpabuf_put_u8(buf, client_timeout);
269f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "P2P: * Configuration Timeout: GO %d (*10ms)  "
270f05cddf9SRui Paulo 		   "client %d (*10ms)", go_timeout, client_timeout);
271f05cddf9SRui Paulo }
272f05cddf9SRui Paulo 
273f05cddf9SRui Paulo 
p2p_buf_add_intended_addr(struct wpabuf * buf,const u8 * interface_addr)274f05cddf9SRui Paulo void p2p_buf_add_intended_addr(struct wpabuf *buf, const u8 *interface_addr)
275f05cddf9SRui Paulo {
276f05cddf9SRui Paulo 	/* Intended P2P Interface Address */
277f05cddf9SRui Paulo 	wpabuf_put_u8(buf, P2P_ATTR_INTENDED_INTERFACE_ADDR);
278f05cddf9SRui Paulo 	wpabuf_put_le16(buf, ETH_ALEN);
279f05cddf9SRui Paulo 	wpabuf_put_data(buf, interface_addr, ETH_ALEN);
280f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "P2P: * Intended P2P Interface Address " MACSTR,
281f05cddf9SRui Paulo 		   MAC2STR(interface_addr));
282f05cddf9SRui Paulo }
283f05cddf9SRui Paulo 
284f05cddf9SRui Paulo 
p2p_buf_add_group_bssid(struct wpabuf * buf,const u8 * bssid)285f05cddf9SRui Paulo void p2p_buf_add_group_bssid(struct wpabuf *buf, const u8 *bssid)
286f05cddf9SRui Paulo {
287f05cddf9SRui Paulo 	/* P2P Group BSSID */
288f05cddf9SRui Paulo 	wpabuf_put_u8(buf, P2P_ATTR_GROUP_BSSID);
289f05cddf9SRui Paulo 	wpabuf_put_le16(buf, ETH_ALEN);
290f05cddf9SRui Paulo 	wpabuf_put_data(buf, bssid, ETH_ALEN);
291f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "P2P: * P2P Group BSSID " MACSTR,
292f05cddf9SRui Paulo 		   MAC2STR(bssid));
293f05cddf9SRui Paulo }
294f05cddf9SRui Paulo 
295f05cddf9SRui Paulo 
p2p_buf_add_group_id(struct wpabuf * buf,const u8 * dev_addr,const u8 * ssid,size_t ssid_len)296f05cddf9SRui Paulo void p2p_buf_add_group_id(struct wpabuf *buf, const u8 *dev_addr,
297f05cddf9SRui Paulo 			  const u8 *ssid, size_t ssid_len)
298f05cddf9SRui Paulo {
299f05cddf9SRui Paulo 	/* P2P Group ID */
300f05cddf9SRui Paulo 	wpabuf_put_u8(buf, P2P_ATTR_GROUP_ID);
301f05cddf9SRui Paulo 	wpabuf_put_le16(buf, ETH_ALEN + ssid_len);
302f05cddf9SRui Paulo 	wpabuf_put_data(buf, dev_addr, ETH_ALEN);
303f05cddf9SRui Paulo 	wpabuf_put_data(buf, ssid, ssid_len);
304f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "P2P: * P2P Group ID " MACSTR,
305f05cddf9SRui Paulo 		   MAC2STR(dev_addr));
3065b9c547cSRui Paulo 	wpa_hexdump_ascii(MSG_DEBUG, "P2P: P2P Group ID SSID", ssid, ssid_len);
307f05cddf9SRui Paulo }
308f05cddf9SRui Paulo 
309f05cddf9SRui Paulo 
p2p_buf_add_invitation_flags(struct wpabuf * buf,u8 flags)310f05cddf9SRui Paulo void p2p_buf_add_invitation_flags(struct wpabuf *buf, u8 flags)
311f05cddf9SRui Paulo {
312f05cddf9SRui Paulo 	/* Invitation Flags */
313f05cddf9SRui Paulo 	wpabuf_put_u8(buf, P2P_ATTR_INVITATION_FLAGS);
314f05cddf9SRui Paulo 	wpabuf_put_le16(buf, 1);
315f05cddf9SRui Paulo 	wpabuf_put_u8(buf, flags);
316f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "P2P: * Invitation Flags: bitmap 0x%x", flags);
317f05cddf9SRui Paulo }
318f05cddf9SRui Paulo 
319f05cddf9SRui Paulo 
p2p_buf_add_noa_desc(struct wpabuf * buf,struct p2p_noa_desc * desc)320f05cddf9SRui Paulo static void p2p_buf_add_noa_desc(struct wpabuf *buf, struct p2p_noa_desc *desc)
321f05cddf9SRui Paulo {
322f05cddf9SRui Paulo 	if (desc == NULL)
323f05cddf9SRui Paulo 		return;
324f05cddf9SRui Paulo 
325f05cddf9SRui Paulo 	wpabuf_put_u8(buf, desc->count_type);
326f05cddf9SRui Paulo 	wpabuf_put_le32(buf, desc->duration);
327f05cddf9SRui Paulo 	wpabuf_put_le32(buf, desc->interval);
328f05cddf9SRui Paulo 	wpabuf_put_le32(buf, desc->start_time);
329f05cddf9SRui Paulo }
330f05cddf9SRui Paulo 
331f05cddf9SRui Paulo 
p2p_buf_add_noa(struct wpabuf * buf,u8 noa_index,u8 opp_ps,u8 ctwindow,struct p2p_noa_desc * desc1,struct p2p_noa_desc * desc2)332f05cddf9SRui Paulo void p2p_buf_add_noa(struct wpabuf *buf, u8 noa_index, u8 opp_ps, u8 ctwindow,
333f05cddf9SRui Paulo 		     struct p2p_noa_desc *desc1, struct p2p_noa_desc *desc2)
334f05cddf9SRui Paulo {
335f05cddf9SRui Paulo 	/* Notice of Absence */
336f05cddf9SRui Paulo 	wpabuf_put_u8(buf, P2P_ATTR_NOTICE_OF_ABSENCE);
337f05cddf9SRui Paulo 	wpabuf_put_le16(buf, 2 + (desc1 ? 13 : 0) + (desc2 ? 13 : 0));
338f05cddf9SRui Paulo 	wpabuf_put_u8(buf, noa_index);
339f05cddf9SRui Paulo 	wpabuf_put_u8(buf, (opp_ps ? 0x80 : 0) | (ctwindow & 0x7f));
340f05cddf9SRui Paulo 	p2p_buf_add_noa_desc(buf, desc1);
341f05cddf9SRui Paulo 	p2p_buf_add_noa_desc(buf, desc2);
342f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "P2P: * Notice of Absence");
343f05cddf9SRui Paulo }
344f05cddf9SRui Paulo 
345f05cddf9SRui Paulo 
p2p_buf_add_ext_listen_timing(struct wpabuf * buf,u16 period,u16 interval)346f05cddf9SRui Paulo void p2p_buf_add_ext_listen_timing(struct wpabuf *buf, u16 period,
347f05cddf9SRui Paulo 				   u16 interval)
348f05cddf9SRui Paulo {
349f05cddf9SRui Paulo 	/* Extended Listen Timing */
350f05cddf9SRui Paulo 	wpabuf_put_u8(buf, P2P_ATTR_EXT_LISTEN_TIMING);
351f05cddf9SRui Paulo 	wpabuf_put_le16(buf, 4);
352f05cddf9SRui Paulo 	wpabuf_put_le16(buf, period);
353f05cddf9SRui Paulo 	wpabuf_put_le16(buf, interval);
354f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "P2P: * Extended Listen Timing (period %u msec  "
355f05cddf9SRui Paulo 		   "interval %u msec)", period, interval);
356f05cddf9SRui Paulo }
357f05cddf9SRui Paulo 
358f05cddf9SRui Paulo 
p2p_buf_add_p2p_interface(struct wpabuf * buf,struct p2p_data * p2p)359f05cddf9SRui Paulo void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p)
360f05cddf9SRui Paulo {
361f05cddf9SRui Paulo 	/* P2P Interface */
362f05cddf9SRui Paulo 	wpabuf_put_u8(buf, P2P_ATTR_INTERFACE);
363f05cddf9SRui Paulo 	wpabuf_put_le16(buf, ETH_ALEN + 1 + ETH_ALEN);
364f05cddf9SRui Paulo 	/* P2P Device address */
365f05cddf9SRui Paulo 	wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN);
366f05cddf9SRui Paulo 	/*
367f05cddf9SRui Paulo 	 * FIX: Fetch interface address list from driver. Do not include
368f05cddf9SRui Paulo 	 * the P2P Device address if it is never used as interface address.
369f05cddf9SRui Paulo 	 */
370f05cddf9SRui Paulo 	/* P2P Interface Address Count */
371f05cddf9SRui Paulo 	wpabuf_put_u8(buf, 1);
372f05cddf9SRui Paulo 	wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN);
373f05cddf9SRui Paulo }
374f05cddf9SRui Paulo 
375f05cddf9SRui Paulo 
p2p_buf_add_oob_go_neg_channel(struct wpabuf * buf,const char * country,u8 oper_class,u8 channel,enum p2p_role_indication role)3765b9c547cSRui Paulo void p2p_buf_add_oob_go_neg_channel(struct wpabuf *buf, const char *country,
3775b9c547cSRui Paulo 				    u8 oper_class, u8 channel,
3785b9c547cSRui Paulo 				    enum p2p_role_indication role)
3795b9c547cSRui Paulo {
3805b9c547cSRui Paulo 	/* OOB Group Owner Negotiation Channel */
3815b9c547cSRui Paulo 	wpabuf_put_u8(buf, P2P_ATTR_OOB_GO_NEG_CHANNEL);
3825b9c547cSRui Paulo 	wpabuf_put_le16(buf, 6);
3835b9c547cSRui Paulo 	wpabuf_put_data(buf, country, 3);
3845b9c547cSRui Paulo 	wpabuf_put_u8(buf, oper_class); /* Operating Class */
3855b9c547cSRui Paulo 	wpabuf_put_u8(buf, channel); /* Channel Number */
3865b9c547cSRui Paulo 	wpabuf_put_u8(buf, (u8) role); /* Role indication */
3875b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "P2P: * OOB GO Negotiation Channel: Operating "
3885b9c547cSRui Paulo 		   "Class %u Channel %u Role %d",
3895b9c547cSRui Paulo 		   oper_class, channel, role);
3905b9c547cSRui Paulo }
3915b9c547cSRui Paulo 
3925b9c547cSRui Paulo 
p2p_buf_add_service_hash(struct wpabuf * buf,struct p2p_data * p2p)3935b9c547cSRui Paulo void p2p_buf_add_service_hash(struct wpabuf *buf, struct p2p_data *p2p)
3945b9c547cSRui Paulo {
3955b9c547cSRui Paulo 	if (!p2p)
3965b9c547cSRui Paulo 		return;
3975b9c547cSRui Paulo 
3985b9c547cSRui Paulo 	/* Service Hash */
3995b9c547cSRui Paulo 	wpabuf_put_u8(buf, P2P_ATTR_SERVICE_HASH);
4005b9c547cSRui Paulo 	wpabuf_put_le16(buf, p2p->p2ps_seek_count * P2PS_HASH_LEN);
401325151a3SRui Paulo 	wpabuf_put_data(buf, p2p->p2ps_seek_hash,
4025b9c547cSRui Paulo 			p2p->p2ps_seek_count * P2PS_HASH_LEN);
4035b9c547cSRui Paulo 	wpa_hexdump(MSG_DEBUG, "P2P: * Service Hash",
404325151a3SRui Paulo 		    p2p->p2ps_seek_hash, p2p->p2ps_seek_count * P2PS_HASH_LEN);
4055b9c547cSRui Paulo }
4065b9c547cSRui Paulo 
4075b9c547cSRui Paulo 
p2p_buf_add_session_info(struct wpabuf * buf,const char * info)4085b9c547cSRui Paulo void p2p_buf_add_session_info(struct wpabuf *buf, const char *info)
4095b9c547cSRui Paulo {
4105b9c547cSRui Paulo 	size_t info_len = 0;
4115b9c547cSRui Paulo 
4125b9c547cSRui Paulo 	if (info && info[0])
4135b9c547cSRui Paulo 		info_len = os_strlen(info);
4145b9c547cSRui Paulo 
4155b9c547cSRui Paulo 	/* Session Information Data Info */
4165b9c547cSRui Paulo 	wpabuf_put_u8(buf, P2P_ATTR_SESSION_INFORMATION_DATA);
4175b9c547cSRui Paulo 	wpabuf_put_le16(buf, (u16) info_len);
4185b9c547cSRui Paulo 
4195b9c547cSRui Paulo 	if (info) {
4205b9c547cSRui Paulo 		wpabuf_put_data(buf, info, info_len);
4215b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "P2P: * Session Info Data (%s)", info);
4225b9c547cSRui Paulo 	}
4235b9c547cSRui Paulo }
4245b9c547cSRui Paulo 
4255b9c547cSRui Paulo 
p2p_buf_add_connection_capability(struct wpabuf * buf,u8 connection_cap)4265b9c547cSRui Paulo void p2p_buf_add_connection_capability(struct wpabuf *buf, u8 connection_cap)
4275b9c547cSRui Paulo {
4285b9c547cSRui Paulo 	/* Connection Capability Info */
4295b9c547cSRui Paulo 	wpabuf_put_u8(buf, P2P_ATTR_CONNECTION_CAPABILITY);
4305b9c547cSRui Paulo 	wpabuf_put_le16(buf, 1);
4315b9c547cSRui Paulo 	wpabuf_put_u8(buf, connection_cap);
4325b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "P2P: * Connection Capability: 0x%x",
4335b9c547cSRui Paulo 		   connection_cap);
4345b9c547cSRui Paulo }
4355b9c547cSRui Paulo 
4365b9c547cSRui Paulo 
p2p_buf_add_advertisement_id(struct wpabuf * buf,u32 id,const u8 * mac)4375b9c547cSRui Paulo void p2p_buf_add_advertisement_id(struct wpabuf *buf, u32 id, const u8 *mac)
4385b9c547cSRui Paulo {
4395b9c547cSRui Paulo 	if (!buf || !mac)
4405b9c547cSRui Paulo 		return;
4415b9c547cSRui Paulo 
4425b9c547cSRui Paulo 	/* Advertisement ID Info */
4435b9c547cSRui Paulo 	wpabuf_put_u8(buf, P2P_ATTR_ADVERTISEMENT_ID);
4445b9c547cSRui Paulo 	wpabuf_put_le16(buf, (u16) (sizeof(u32) + ETH_ALEN));
4455b9c547cSRui Paulo 	wpabuf_put_le32(buf, id);
4465b9c547cSRui Paulo 	wpabuf_put_data(buf, mac, ETH_ALEN);
4475b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "P2P: * Advertisement ID (%x) " MACSTR,
4485b9c547cSRui Paulo 		   id, MAC2STR(mac));
4495b9c547cSRui Paulo }
4505b9c547cSRui Paulo 
4515b9c547cSRui Paulo 
p2ps_wildcard_hash(struct p2p_data * p2p,const u8 * hash,u8 hash_count)452325151a3SRui Paulo static int p2ps_wildcard_hash(struct p2p_data *p2p,
453325151a3SRui Paulo 			      const u8 *hash, u8 hash_count)
454325151a3SRui Paulo {
455325151a3SRui Paulo 	u8 i;
456325151a3SRui Paulo 	const u8 *test = hash;
457325151a3SRui Paulo 
458325151a3SRui Paulo 	for (i = 0; i < hash_count; i++) {
459325151a3SRui Paulo 		if (os_memcmp(test, p2p->wild_card_hash, P2PS_HASH_LEN) == 0)
460325151a3SRui Paulo 			return 1;
461325151a3SRui Paulo 		test += P2PS_HASH_LEN;
462325151a3SRui Paulo 	}
463325151a3SRui Paulo 
464325151a3SRui Paulo 	return 0;
465325151a3SRui Paulo }
466325151a3SRui Paulo 
467325151a3SRui Paulo 
p2p_wfa_service_adv(struct p2p_data * p2p)468325151a3SRui Paulo static int p2p_wfa_service_adv(struct p2p_data *p2p)
469325151a3SRui Paulo {
470325151a3SRui Paulo 	struct p2ps_advertisement *adv;
471325151a3SRui Paulo 
472325151a3SRui Paulo 	for (adv = p2p->p2ps_adv_list; adv; adv = adv->next) {
473325151a3SRui Paulo 		if (os_strncmp(adv->svc_name, P2PS_WILD_HASH_STR,
474325151a3SRui Paulo 			       os_strlen(P2PS_WILD_HASH_STR)) == 0)
475325151a3SRui Paulo 			return 1;
476325151a3SRui Paulo 	}
477325151a3SRui Paulo 
478325151a3SRui Paulo 	return 0;
479325151a3SRui Paulo }
480325151a3SRui Paulo 
481325151a3SRui Paulo 
p2p_buf_add_service_info(struct wpabuf * buf,struct p2p_data * p2p,u32 adv_id,u16 config_methods,const char * svc_name,u8 ** ie_len,u8 ** pos,size_t * total_len,u8 * attr_len)482325151a3SRui Paulo static int p2p_buf_add_service_info(struct wpabuf *buf, struct p2p_data *p2p,
483325151a3SRui Paulo 				    u32 adv_id, u16 config_methods,
484325151a3SRui Paulo 				    const char *svc_name, u8 **ie_len, u8 **pos,
485325151a3SRui Paulo 				    size_t *total_len, u8 *attr_len)
486325151a3SRui Paulo {
487325151a3SRui Paulo 	size_t svc_len;
488325151a3SRui Paulo 	size_t remaining;
489325151a3SRui Paulo 	size_t info_len;
490325151a3SRui Paulo 
491325151a3SRui Paulo 	p2p_dbg(p2p, "Add service info for %s (adv_id=%u)", svc_name, adv_id);
492325151a3SRui Paulo 	svc_len = os_strlen(svc_name);
493325151a3SRui Paulo 	info_len = sizeof(adv_id) + sizeof(config_methods) + sizeof(u8) +
494325151a3SRui Paulo 		svc_len;
495325151a3SRui Paulo 
496325151a3SRui Paulo 	if (info_len + *total_len > MAX_SVC_ADV_LEN) {
497325151a3SRui Paulo 		p2p_dbg(p2p,
498325151a3SRui Paulo 			"Unsufficient buffer, failed to add advertised service info");
499325151a3SRui Paulo 		return -1;
500325151a3SRui Paulo 	}
501325151a3SRui Paulo 
502325151a3SRui Paulo 	if (svc_len > 255) {
503325151a3SRui Paulo 		p2p_dbg(p2p,
504325151a3SRui Paulo 			"Invalid service name length (%u bytes), failed to add advertised service info",
505325151a3SRui Paulo 			(unsigned int) svc_len);
506325151a3SRui Paulo 		return -1;
507325151a3SRui Paulo 	}
508325151a3SRui Paulo 
509325151a3SRui Paulo 	if (*ie_len) {
510325151a3SRui Paulo 		int ie_data_len = (*pos - *ie_len) - 1;
511325151a3SRui Paulo 
512325151a3SRui Paulo 		if (ie_data_len < 0 || ie_data_len > 255) {
513325151a3SRui Paulo 			p2p_dbg(p2p,
514325151a3SRui Paulo 				"Invalid IE length, failed to add advertised service info");
515325151a3SRui Paulo 			return -1;
516325151a3SRui Paulo 		}
517325151a3SRui Paulo 		remaining = 255 - ie_data_len;
518325151a3SRui Paulo 	} else {
519325151a3SRui Paulo 		/*
520325151a3SRui Paulo 		 * Adding new P2P IE header takes 6 extra bytes:
521325151a3SRui Paulo 		 * - 2 byte IE header (1 byte IE id and 1 byte length)
522325151a3SRui Paulo 		 * - 4 bytes of IE_VENDOR_TYPE are reduced from 255 below
523325151a3SRui Paulo 		 */
524325151a3SRui Paulo 		*ie_len = p2p_buf_add_ie_hdr(buf);
525325151a3SRui Paulo 		remaining = 255 - 4;
526325151a3SRui Paulo 	}
527325151a3SRui Paulo 
528325151a3SRui Paulo 	if (remaining < sizeof(u32) + sizeof(u16) + sizeof(u8)) {
529325151a3SRui Paulo 		/*
530325151a3SRui Paulo 		 * Split adv_id, config_methods, and svc_name_len between two
531325151a3SRui Paulo 		 * IEs.
532325151a3SRui Paulo 		 */
533325151a3SRui Paulo 		size_t front = remaining;
534325151a3SRui Paulo 		size_t back = sizeof(u32) + sizeof(u16) + sizeof(u8) - front;
535325151a3SRui Paulo 		u8 holder[sizeof(u32) + sizeof(u16) + sizeof(u8)];
536325151a3SRui Paulo 
537325151a3SRui Paulo 		WPA_PUT_LE32(holder, adv_id);
538325151a3SRui Paulo 		WPA_PUT_BE16(&holder[sizeof(u32)], config_methods);
539325151a3SRui Paulo 		holder[sizeof(u32) + sizeof(u16)] = svc_len;
540325151a3SRui Paulo 
541325151a3SRui Paulo 		if (front)
542325151a3SRui Paulo 			wpabuf_put_data(buf, holder, front);
543325151a3SRui Paulo 
544325151a3SRui Paulo 		p2p_buf_update_ie_hdr(buf, *ie_len);
545325151a3SRui Paulo 		*ie_len = p2p_buf_add_ie_hdr(buf);
546325151a3SRui Paulo 
547325151a3SRui Paulo 		wpabuf_put_data(buf, &holder[front], back);
548325151a3SRui Paulo 		remaining = 255 - 4 - (sizeof(u32) + sizeof(u16) + sizeof(u8)) -
549325151a3SRui Paulo 			back;
550325151a3SRui Paulo 	} else {
551325151a3SRui Paulo 		wpabuf_put_le32(buf, adv_id);
552325151a3SRui Paulo 		wpabuf_put_be16(buf, config_methods);
553325151a3SRui Paulo 		wpabuf_put_u8(buf, svc_len);
554325151a3SRui Paulo 		remaining -= sizeof(adv_id) + sizeof(config_methods) +
555325151a3SRui Paulo 			sizeof(u8);
556325151a3SRui Paulo 	}
557325151a3SRui Paulo 
558325151a3SRui Paulo 	if (remaining < svc_len) {
559325151a3SRui Paulo 		/* split svc_name between two or three IEs */
560325151a3SRui Paulo 		size_t front = remaining;
561325151a3SRui Paulo 		size_t back = svc_len - front;
562325151a3SRui Paulo 
563325151a3SRui Paulo 		if (front)
564325151a3SRui Paulo 			wpabuf_put_data(buf, svc_name, front);
565325151a3SRui Paulo 
566325151a3SRui Paulo 		p2p_buf_update_ie_hdr(buf, *ie_len);
567325151a3SRui Paulo 		*ie_len = p2p_buf_add_ie_hdr(buf);
568325151a3SRui Paulo 
569325151a3SRui Paulo 		/* In rare cases, we must split across 3 attributes */
570325151a3SRui Paulo 		if (back > 255 - 4) {
571325151a3SRui Paulo 			wpabuf_put_data(buf, &svc_name[front], 255 - 4);
572325151a3SRui Paulo 			back -= 255 - 4;
573325151a3SRui Paulo 			front += 255 - 4;
574325151a3SRui Paulo 			p2p_buf_update_ie_hdr(buf, *ie_len);
575325151a3SRui Paulo 			*ie_len = p2p_buf_add_ie_hdr(buf);
576325151a3SRui Paulo 		}
577325151a3SRui Paulo 
578325151a3SRui Paulo 		wpabuf_put_data(buf, &svc_name[front], back);
579325151a3SRui Paulo 		remaining = 255 - 4 - back;
580325151a3SRui Paulo 	} else {
581325151a3SRui Paulo 		wpabuf_put_data(buf, svc_name, svc_len);
582325151a3SRui Paulo 		remaining -= svc_len;
583325151a3SRui Paulo 	}
584325151a3SRui Paulo 
585325151a3SRui Paulo 	p2p_buf_update_ie_hdr(buf, *ie_len);
586325151a3SRui Paulo 
587325151a3SRui Paulo 	/* set *ie_len to NULL if a new IE has to be added on the next call */
588325151a3SRui Paulo 	if (!remaining)
589325151a3SRui Paulo 		*ie_len = NULL;
590325151a3SRui Paulo 
591325151a3SRui Paulo 	/* set *pos to point to the next byte to update */
592325151a3SRui Paulo 	*pos = wpabuf_put(buf, 0);
593325151a3SRui Paulo 
594325151a3SRui Paulo 	*total_len += info_len;
595325151a3SRui Paulo 	WPA_PUT_LE16(attr_len, (u16) *total_len);
596325151a3SRui Paulo 	return 0;
597325151a3SRui Paulo }
598325151a3SRui Paulo 
599325151a3SRui Paulo 
p2p_buf_add_service_instance(struct wpabuf * buf,struct p2p_data * p2p,u8 hash_count,const u8 * hash,struct p2ps_advertisement * adv_list)6005b9c547cSRui Paulo void p2p_buf_add_service_instance(struct wpabuf *buf, struct p2p_data *p2p,
6015b9c547cSRui Paulo 				  u8 hash_count, const u8 *hash,
6025b9c547cSRui Paulo 				  struct p2ps_advertisement *adv_list)
6035b9c547cSRui Paulo {
6045b9c547cSRui Paulo 	struct p2ps_advertisement *adv;
605325151a3SRui Paulo 	int p2ps_wildcard;
606325151a3SRui Paulo 	size_t total_len;
607325151a3SRui Paulo 	struct wpabuf *tmp_buf = NULL;
608325151a3SRui Paulo 	u8 *pos, *attr_len, *ie_len = NULL;
6095b9c547cSRui Paulo 
610325151a3SRui Paulo 	if (!adv_list || !hash || !hash_count)
6115b9c547cSRui Paulo 		return;
6125b9c547cSRui Paulo 
613325151a3SRui Paulo 	wpa_hexdump(MSG_DEBUG, "P2PS: Probe Request service hash values",
614325151a3SRui Paulo 		    hash, hash_count * P2PS_HASH_LEN);
615325151a3SRui Paulo 	p2ps_wildcard = p2ps_wildcard_hash(p2p, hash, hash_count) &&
616325151a3SRui Paulo 		p2p_wfa_service_adv(p2p);
617325151a3SRui Paulo 
6185b9c547cSRui Paulo 	/* Allocate temp buffer, allowing for overflow of 1 instance */
6195b9c547cSRui Paulo 	tmp_buf = wpabuf_alloc(MAX_SVC_ADV_IE_LEN + 256 + P2PS_HASH_LEN);
6205b9c547cSRui Paulo 	if (!tmp_buf)
6215b9c547cSRui Paulo 		return;
6225b9c547cSRui Paulo 
623325151a3SRui Paulo 	/*
624325151a3SRui Paulo 	 * Attribute data can be split into a number of IEs. Start with the
625325151a3SRui Paulo 	 * first IE and the attribute headers here.
626325151a3SRui Paulo 	 */
627325151a3SRui Paulo 	ie_len = p2p_buf_add_ie_hdr(tmp_buf);
6285b9c547cSRui Paulo 
629325151a3SRui Paulo 	total_len = 0;
630325151a3SRui Paulo 
631325151a3SRui Paulo 	wpabuf_put_u8(tmp_buf, P2P_ATTR_ADVERTISED_SERVICE);
632325151a3SRui Paulo 	attr_len = wpabuf_put(tmp_buf, sizeof(u16));
633325151a3SRui Paulo 	WPA_PUT_LE16(attr_len, (u16) total_len);
634325151a3SRui Paulo 	p2p_buf_update_ie_hdr(tmp_buf, ie_len);
635325151a3SRui Paulo 	pos = wpabuf_put(tmp_buf, 0);
636325151a3SRui Paulo 
637325151a3SRui Paulo 	if (p2ps_wildcard) {
638325151a3SRui Paulo 		/* org.wi-fi.wfds match found */
639325151a3SRui Paulo 		p2p_buf_add_service_info(tmp_buf, p2p, 0, 0, P2PS_WILD_HASH_STR,
640325151a3SRui Paulo 					 &ie_len, &pos, &total_len, attr_len);
6415b9c547cSRui Paulo 	}
6425b9c547cSRui Paulo 
643325151a3SRui Paulo 	/* add advertised service info of matching services */
644325151a3SRui Paulo 	for (adv = adv_list; adv && total_len <= MAX_SVC_ADV_LEN;
645325151a3SRui Paulo 	     adv = adv->next) {
646325151a3SRui Paulo 		const u8 *test = hash;
647325151a3SRui Paulo 		u8 i;
648325151a3SRui Paulo 
649325151a3SRui Paulo 		for (i = 0; i < hash_count; i++) {
650325151a3SRui Paulo 			/* exact name hash match */
651325151a3SRui Paulo 			if (os_memcmp(test, adv->hash, P2PS_HASH_LEN) == 0 &&
652325151a3SRui Paulo 			    p2p_buf_add_service_info(tmp_buf, p2p,
653325151a3SRui Paulo 						     adv->id,
654325151a3SRui Paulo 						     adv->config_methods,
655325151a3SRui Paulo 						     adv->svc_name,
656325151a3SRui Paulo 						     &ie_len, &pos,
657325151a3SRui Paulo 						     &total_len,
658325151a3SRui Paulo 						     attr_len))
659325151a3SRui Paulo 				break;
6605b9c547cSRui Paulo 
6615b9c547cSRui Paulo 			test += P2PS_HASH_LEN;
6625b9c547cSRui Paulo 		}
6635b9c547cSRui Paulo 	}
6645b9c547cSRui Paulo 
665325151a3SRui Paulo 	if (total_len)
6665b9c547cSRui Paulo 		wpabuf_put_buf(buf, tmp_buf);
6675b9c547cSRui Paulo 	wpabuf_free(tmp_buf);
6685b9c547cSRui Paulo }
6695b9c547cSRui Paulo 
6705b9c547cSRui Paulo 
p2p_buf_add_session_id(struct wpabuf * buf,u32 id,const u8 * mac)6715b9c547cSRui Paulo void p2p_buf_add_session_id(struct wpabuf *buf, u32 id, const u8 *mac)
6725b9c547cSRui Paulo {
6735b9c547cSRui Paulo 	if (!buf || !mac)
6745b9c547cSRui Paulo 		return;
6755b9c547cSRui Paulo 
6765b9c547cSRui Paulo 	/* Session ID Info */
6775b9c547cSRui Paulo 	wpabuf_put_u8(buf, P2P_ATTR_SESSION_ID);
6785b9c547cSRui Paulo 	wpabuf_put_le16(buf, (u16) (sizeof(u32) + ETH_ALEN));
6795b9c547cSRui Paulo 	wpabuf_put_le32(buf, id);
6805b9c547cSRui Paulo 	wpabuf_put_data(buf, mac, ETH_ALEN);
6815b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "P2P: * Session ID Info (%x) " MACSTR,
6825b9c547cSRui Paulo 		   id, MAC2STR(mac));
6835b9c547cSRui Paulo }
6845b9c547cSRui Paulo 
6855b9c547cSRui Paulo 
p2p_buf_add_feature_capability(struct wpabuf * buf,u16 len,const u8 * mask)6865b9c547cSRui Paulo void p2p_buf_add_feature_capability(struct wpabuf *buf, u16 len, const u8 *mask)
6875b9c547cSRui Paulo {
6885b9c547cSRui Paulo 	if (!buf || !len || !mask)
6895b9c547cSRui Paulo 		return;
6905b9c547cSRui Paulo 
6915b9c547cSRui Paulo 	/* Feature Capability */
6925b9c547cSRui Paulo 	wpabuf_put_u8(buf, P2P_ATTR_FEATURE_CAPABILITY);
6935b9c547cSRui Paulo 	wpabuf_put_le16(buf, len);
6945b9c547cSRui Paulo 	wpabuf_put_data(buf, mask, len);
6955b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "P2P: * Feature Capability (%d)", len);
6965b9c547cSRui Paulo }
6975b9c547cSRui Paulo 
6985b9c547cSRui Paulo 
p2p_buf_add_persistent_group_info(struct wpabuf * buf,const u8 * dev_addr,const u8 * ssid,size_t ssid_len)6995b9c547cSRui Paulo void p2p_buf_add_persistent_group_info(struct wpabuf *buf, const u8 *dev_addr,
7005b9c547cSRui Paulo 				       const u8 *ssid, size_t ssid_len)
7015b9c547cSRui Paulo {
7025b9c547cSRui Paulo 	/* P2P Group ID */
7035b9c547cSRui Paulo 	wpabuf_put_u8(buf, P2P_ATTR_PERSISTENT_GROUP);
7045b9c547cSRui Paulo 	wpabuf_put_le16(buf, ETH_ALEN + ssid_len);
7055b9c547cSRui Paulo 	wpabuf_put_data(buf, dev_addr, ETH_ALEN);
7065b9c547cSRui Paulo 	wpabuf_put_data(buf, ssid, ssid_len);
7075b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "P2P: * P2P Group ID " MACSTR,
7085b9c547cSRui Paulo 		   MAC2STR(dev_addr));
7095b9c547cSRui Paulo }
7105b9c547cSRui Paulo 
7115b9c547cSRui Paulo 
p2p_add_wps_string(struct wpabuf * buf,enum wps_attribute attr,const char * val)7125b9c547cSRui Paulo static int p2p_add_wps_string(struct wpabuf *buf, enum wps_attribute attr,
713f05cddf9SRui Paulo 			      const char *val)
714f05cddf9SRui Paulo {
715f05cddf9SRui Paulo 	size_t len;
716f05cddf9SRui Paulo 
717f05cddf9SRui Paulo 	len = val ? os_strlen(val) : 0;
7185b9c547cSRui Paulo 	if (wpabuf_tailroom(buf) < 4 + len)
7195b9c547cSRui Paulo 		return -1;
7205b9c547cSRui Paulo 	wpabuf_put_be16(buf, attr);
721f05cddf9SRui Paulo #ifndef CONFIG_WPS_STRICT
722f05cddf9SRui Paulo 	if (len == 0) {
723f05cddf9SRui Paulo 		/*
724f05cddf9SRui Paulo 		 * Some deployed WPS implementations fail to parse zeor-length
725f05cddf9SRui Paulo 		 * attributes. As a workaround, send a space character if the
726f05cddf9SRui Paulo 		 * device attribute string is empty.
727f05cddf9SRui Paulo 		 */
7285b9c547cSRui Paulo 		if (wpabuf_tailroom(buf) < 3)
7295b9c547cSRui Paulo 			return -1;
730f05cddf9SRui Paulo 		wpabuf_put_be16(buf, 1);
731f05cddf9SRui Paulo 		wpabuf_put_u8(buf, ' ');
7325b9c547cSRui Paulo 		return 0;
733f05cddf9SRui Paulo 	}
734f05cddf9SRui Paulo #endif /* CONFIG_WPS_STRICT */
735f05cddf9SRui Paulo 	wpabuf_put_be16(buf, len);
736f05cddf9SRui Paulo 	if (val)
737f05cddf9SRui Paulo 		wpabuf_put_data(buf, val, len);
7385b9c547cSRui Paulo 	return 0;
739f05cddf9SRui Paulo }
740f05cddf9SRui Paulo 
741f05cddf9SRui Paulo 
p2p_build_wps_ie(struct p2p_data * p2p,struct wpabuf * buf,int pw_id,int all_attr)7425b9c547cSRui Paulo int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
743f05cddf9SRui Paulo 		     int all_attr)
744f05cddf9SRui Paulo {
745f05cddf9SRui Paulo 	u8 *len;
746f05cddf9SRui Paulo 	int i;
747f05cddf9SRui Paulo 
7485b9c547cSRui Paulo 	if (wpabuf_tailroom(buf) < 6)
7495b9c547cSRui Paulo 		return -1;
750f05cddf9SRui Paulo 	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
751f05cddf9SRui Paulo 	len = wpabuf_put(buf, 1);
752f05cddf9SRui Paulo 	wpabuf_put_be32(buf, WPS_DEV_OUI_WFA);
753f05cddf9SRui Paulo 
7545b9c547cSRui Paulo 	if (wps_build_version(buf) < 0)
7555b9c547cSRui Paulo 		return -1;
756f05cddf9SRui Paulo 
757f05cddf9SRui Paulo 	if (all_attr) {
7585b9c547cSRui Paulo 		if (wpabuf_tailroom(buf) < 5)
7595b9c547cSRui Paulo 			return -1;
760f05cddf9SRui Paulo 		wpabuf_put_be16(buf, ATTR_WPS_STATE);
761f05cddf9SRui Paulo 		wpabuf_put_be16(buf, 1);
762f05cddf9SRui Paulo 		wpabuf_put_u8(buf, WPS_STATE_NOT_CONFIGURED);
763f05cddf9SRui Paulo 	}
764f05cddf9SRui Paulo 
765f05cddf9SRui Paulo 	if (pw_id >= 0) {
7665b9c547cSRui Paulo 		if (wpabuf_tailroom(buf) < 6)
7675b9c547cSRui Paulo 			return -1;
768f05cddf9SRui Paulo 		/* Device Password ID */
769f05cddf9SRui Paulo 		wpabuf_put_be16(buf, ATTR_DEV_PASSWORD_ID);
770f05cddf9SRui Paulo 		wpabuf_put_be16(buf, 2);
771f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "P2P: WPS IE Device Password ID: %d",
772f05cddf9SRui Paulo 			   pw_id);
773f05cddf9SRui Paulo 		wpabuf_put_be16(buf, pw_id);
774f05cddf9SRui Paulo 	}
775f05cddf9SRui Paulo 
776f05cddf9SRui Paulo 	if (all_attr) {
7775b9c547cSRui Paulo 		if (wpabuf_tailroom(buf) < 5)
7785b9c547cSRui Paulo 			return -1;
779f05cddf9SRui Paulo 		wpabuf_put_be16(buf, ATTR_RESPONSE_TYPE);
780f05cddf9SRui Paulo 		wpabuf_put_be16(buf, 1);
781f05cddf9SRui Paulo 		wpabuf_put_u8(buf, WPS_RESP_ENROLLEE_INFO);
782f05cddf9SRui Paulo 
7835b9c547cSRui Paulo 		if (wps_build_uuid_e(buf, p2p->cfg->uuid) < 0 ||
784f05cddf9SRui Paulo 		    p2p_add_wps_string(buf, ATTR_MANUFACTURER,
7855b9c547cSRui Paulo 				       p2p->cfg->manufacturer) < 0 ||
7865b9c547cSRui Paulo 		    p2p_add_wps_string(buf, ATTR_MODEL_NAME,
7875b9c547cSRui Paulo 				       p2p->cfg->model_name) < 0 ||
788f05cddf9SRui Paulo 		    p2p_add_wps_string(buf, ATTR_MODEL_NUMBER,
7895b9c547cSRui Paulo 				       p2p->cfg->model_number) < 0 ||
790f05cddf9SRui Paulo 		    p2p_add_wps_string(buf, ATTR_SERIAL_NUMBER,
7915b9c547cSRui Paulo 				       p2p->cfg->serial_number) < 0)
7925b9c547cSRui Paulo 			return -1;
793f05cddf9SRui Paulo 
7945b9c547cSRui Paulo 		if (wpabuf_tailroom(buf) < 4 + WPS_DEV_TYPE_LEN)
7955b9c547cSRui Paulo 			return -1;
796f05cddf9SRui Paulo 		wpabuf_put_be16(buf, ATTR_PRIMARY_DEV_TYPE);
797f05cddf9SRui Paulo 		wpabuf_put_be16(buf, WPS_DEV_TYPE_LEN);
798f05cddf9SRui Paulo 		wpabuf_put_data(buf, p2p->cfg->pri_dev_type, WPS_DEV_TYPE_LEN);
799f05cddf9SRui Paulo 
8005b9c547cSRui Paulo 		if (p2p_add_wps_string(buf, ATTR_DEV_NAME, p2p->cfg->dev_name)
8015b9c547cSRui Paulo 		    < 0)
8025b9c547cSRui Paulo 			return -1;
803f05cddf9SRui Paulo 
8045b9c547cSRui Paulo 		if (wpabuf_tailroom(buf) < 6)
8055b9c547cSRui Paulo 			return -1;
806f05cddf9SRui Paulo 		wpabuf_put_be16(buf, ATTR_CONFIG_METHODS);
807f05cddf9SRui Paulo 		wpabuf_put_be16(buf, 2);
808f05cddf9SRui Paulo 		wpabuf_put_be16(buf, p2p->cfg->config_methods);
809f05cddf9SRui Paulo 	}
810f05cddf9SRui Paulo 
8114bc52338SCy Schubert 	if (wps_build_wfa_ext(buf, 0, NULL, 0, 0) < 0)
8125b9c547cSRui Paulo 		return -1;
813f05cddf9SRui Paulo 
814f05cddf9SRui Paulo 	if (all_attr && p2p->cfg->num_sec_dev_types) {
8155b9c547cSRui Paulo 		if (wpabuf_tailroom(buf) <
8165b9c547cSRui Paulo 		    4 + WPS_DEV_TYPE_LEN * p2p->cfg->num_sec_dev_types)
8175b9c547cSRui Paulo 			return -1;
818f05cddf9SRui Paulo 		wpabuf_put_be16(buf, ATTR_SECONDARY_DEV_TYPE_LIST);
819f05cddf9SRui Paulo 		wpabuf_put_be16(buf, WPS_DEV_TYPE_LEN *
820f05cddf9SRui Paulo 				p2p->cfg->num_sec_dev_types);
821f05cddf9SRui Paulo 		wpabuf_put_data(buf, p2p->cfg->sec_dev_type,
822f05cddf9SRui Paulo 				WPS_DEV_TYPE_LEN *
823f05cddf9SRui Paulo 				p2p->cfg->num_sec_dev_types);
824f05cddf9SRui Paulo 	}
825f05cddf9SRui Paulo 
826f05cddf9SRui Paulo 	/* Add the WPS vendor extensions */
827f05cddf9SRui Paulo 	for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
828f05cddf9SRui Paulo 		if (p2p->wps_vendor_ext[i] == NULL)
829f05cddf9SRui Paulo 			break;
830f05cddf9SRui Paulo 		if (wpabuf_tailroom(buf) <
831f05cddf9SRui Paulo 		    4 + wpabuf_len(p2p->wps_vendor_ext[i]))
832f05cddf9SRui Paulo 			continue;
833f05cddf9SRui Paulo 		wpabuf_put_be16(buf, ATTR_VENDOR_EXT);
834f05cddf9SRui Paulo 		wpabuf_put_be16(buf, wpabuf_len(p2p->wps_vendor_ext[i]));
835f05cddf9SRui Paulo 		wpabuf_put_buf(buf, p2p->wps_vendor_ext[i]);
836f05cddf9SRui Paulo 	}
837f05cddf9SRui Paulo 
838f05cddf9SRui Paulo 	p2p_buf_update_ie_hdr(buf, len);
8395b9c547cSRui Paulo 
8405b9c547cSRui Paulo 	return 0;
841f05cddf9SRui Paulo }
842