xref: /netbsd-src/external/bsd/wpa/dist/wpa_supplicant/wifi_display.c (revision 0a73ee0a32b4208ab171f89f408b38fd4c664291)
1e604d861Schristos /*
2e604d861Schristos  * wpa_supplicant - Wi-Fi Display
3e604d861Schristos  * Copyright (c) 2011, Atheros Communications, Inc.
4e604d861Schristos  * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
5e604d861Schristos  *
6e604d861Schristos  * This software may be distributed under the terms of the BSD license.
7e604d861Schristos  * See README for more details.
8e604d861Schristos  */
9e604d861Schristos 
10e604d861Schristos #include "includes.h"
11e604d861Schristos 
12e604d861Schristos #include "common.h"
13e604d861Schristos #include "p2p/p2p.h"
14e604d861Schristos #include "common/ieee802_11_defs.h"
15e604d861Schristos #include "wpa_supplicant_i.h"
16e604d861Schristos #include "wifi_display.h"
17e604d861Schristos 
18e604d861Schristos 
193c260e60Schristos #define WIFI_DISPLAY_SUBELEM_HEADER_LEN 3
203c260e60Schristos 
213c260e60Schristos 
wifi_display_init(struct wpa_global * global)22e604d861Schristos int wifi_display_init(struct wpa_global *global)
23e604d861Schristos {
24e604d861Schristos 	global->wifi_display = 1;
25e604d861Schristos 	return 0;
26e604d861Schristos }
27e604d861Schristos 
28e604d861Schristos 
wifi_display_deinit(struct wpa_global * global)29e604d861Schristos void wifi_display_deinit(struct wpa_global *global)
30e604d861Schristos {
31e604d861Schristos 	int i;
32e604d861Schristos 	for (i = 0; i < MAX_WFD_SUBELEMS; i++) {
33e604d861Schristos 		wpabuf_free(global->wfd_subelem[i]);
34e604d861Schristos 		global->wfd_subelem[i] = NULL;
35e604d861Schristos 	}
36e604d861Schristos }
37e604d861Schristos 
38e604d861Schristos 
wifi_display_get_wfd_ie(struct wpa_global * global)393c260e60Schristos struct wpabuf * wifi_display_get_wfd_ie(struct wpa_global *global)
403c260e60Schristos {
413c260e60Schristos 	struct wpabuf *ie;
423c260e60Schristos 	size_t len;
433c260e60Schristos 	int i;
443c260e60Schristos 
453c260e60Schristos 	if (global->p2p == NULL)
463c260e60Schristos 		return NULL;
473c260e60Schristos 
483c260e60Schristos 	len = 0;
493c260e60Schristos 	for (i = 0; i < MAX_WFD_SUBELEMS; i++) {
503c260e60Schristos 		if (global->wfd_subelem[i])
513c260e60Schristos 			len += wpabuf_len(global->wfd_subelem[i]);
523c260e60Schristos 	}
533c260e60Schristos 
543c260e60Schristos 	ie = wpabuf_alloc(len);
553c260e60Schristos 	if (ie == NULL)
563c260e60Schristos 		return NULL;
573c260e60Schristos 
583c260e60Schristos 	for (i = 0; i < MAX_WFD_SUBELEMS; i++) {
593c260e60Schristos 		if (global->wfd_subelem[i])
603c260e60Schristos 			wpabuf_put_buf(ie, global->wfd_subelem[i]);
613c260e60Schristos 	}
623c260e60Schristos 
633c260e60Schristos 	return ie;
643c260e60Schristos }
653c260e60Schristos 
663c260e60Schristos 
wifi_display_update_wfd_ie(struct wpa_global * global)67e604d861Schristos static int wifi_display_update_wfd_ie(struct wpa_global *global)
68e604d861Schristos {
69e604d861Schristos 	struct wpabuf *ie, *buf;
70e604d861Schristos 	size_t len, plen;
71e604d861Schristos 
723c260e60Schristos 	if (global->p2p == NULL)
733c260e60Schristos 		return 0;
743c260e60Schristos 
75e604d861Schristos 	wpa_printf(MSG_DEBUG, "WFD: Update WFD IE");
76e604d861Schristos 
77e604d861Schristos 	if (!global->wifi_display) {
78e604d861Schristos 		wpa_printf(MSG_DEBUG, "WFD: Wi-Fi Display disabled - do not "
79e604d861Schristos 			   "include WFD IE");
80e604d861Schristos 		p2p_set_wfd_ie_beacon(global->p2p, NULL);
81e604d861Schristos 		p2p_set_wfd_ie_probe_req(global->p2p, NULL);
82e604d861Schristos 		p2p_set_wfd_ie_probe_resp(global->p2p, NULL);
83e604d861Schristos 		p2p_set_wfd_ie_assoc_req(global->p2p, NULL);
84e604d861Schristos 		p2p_set_wfd_ie_invitation(global->p2p, NULL);
85e604d861Schristos 		p2p_set_wfd_ie_prov_disc_req(global->p2p, NULL);
86e604d861Schristos 		p2p_set_wfd_ie_prov_disc_resp(global->p2p, NULL);
87e604d861Schristos 		p2p_set_wfd_ie_go_neg(global->p2p, NULL);
88e604d861Schristos 		p2p_set_wfd_dev_info(global->p2p, NULL);
89*0a73ee0aSchristos 		p2p_set_wfd_r2_dev_info(global->p2p, NULL);
90e604d861Schristos 		p2p_set_wfd_assoc_bssid(global->p2p, NULL);
91e604d861Schristos 		p2p_set_wfd_coupled_sink_info(global->p2p, NULL);
92e604d861Schristos 		return 0;
93e604d861Schristos 	}
94e604d861Schristos 
95e604d861Schristos 	p2p_set_wfd_dev_info(global->p2p,
96e604d861Schristos 			     global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]);
97*0a73ee0aSchristos 	p2p_set_wfd_r2_dev_info(
98*0a73ee0aSchristos 		global->p2p, global->wfd_subelem[WFD_SUBELEM_R2_DEVICE_INFO]);
99e604d861Schristos 	p2p_set_wfd_assoc_bssid(
100e604d861Schristos 		global->p2p,
101e604d861Schristos 		global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID]);
102e604d861Schristos 	p2p_set_wfd_coupled_sink_info(
103e604d861Schristos 		global->p2p, global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK]);
104e604d861Schristos 
105e604d861Schristos 	/*
106e604d861Schristos 	 * WFD IE is included in number of management frames. Two different
107e604d861Schristos 	 * sets of subelements are included depending on the frame:
108e604d861Schristos 	 *
109e604d861Schristos 	 * Beacon, (Re)Association Request, GO Negotiation Req/Resp/Conf,
110e604d861Schristos 	 * Provision Discovery Req:
111e604d861Schristos 	 * WFD Device Info
112e604d861Schristos 	 * [Associated BSSID]
113e604d861Schristos 	 * [Coupled Sink Info]
114e604d861Schristos 	 *
115e604d861Schristos 	 * Probe Request:
116e604d861Schristos 	 * WFD Device Info
117e604d861Schristos 	 * [Associated BSSID]
118e604d861Schristos 	 * [Coupled Sink Info]
119e604d861Schristos 	 * [WFD Extended Capability]
120e604d861Schristos 	 *
121e604d861Schristos 	 * Probe Response:
122e604d861Schristos 	 * WFD Device Info
123e604d861Schristos 	 * [Associated BSSID]
124e604d861Schristos 	 * [Coupled Sink Info]
125e604d861Schristos 	 * [WFD Extended Capability]
126e604d861Schristos 	 * [WFD Session Info]
127e604d861Schristos 	 *
128e604d861Schristos 	 * (Re)Association Response, P2P Invitation Req/Resp,
129e604d861Schristos 	 * Provision Discovery Resp:
130e604d861Schristos 	 * WFD Device Info
131e604d861Schristos 	 * [Associated BSSID]
132e604d861Schristos 	 * [Coupled Sink Info]
133e604d861Schristos 	 * [WFD Session Info]
134e604d861Schristos 	 */
135e604d861Schristos 	len = 0;
136e604d861Schristos 	if (global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO])
137e604d861Schristos 		len += wpabuf_len(global->wfd_subelem[
138e604d861Schristos 					  WFD_SUBELEM_DEVICE_INFO]);
139*0a73ee0aSchristos 
140*0a73ee0aSchristos 	if (global->wfd_subelem[WFD_SUBELEM_R2_DEVICE_INFO])
141*0a73ee0aSchristos 		len += wpabuf_len(global->wfd_subelem[
142*0a73ee0aSchristos 					  WFD_SUBELEM_R2_DEVICE_INFO]);
143*0a73ee0aSchristos 
144e604d861Schristos 	if (global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID])
145e604d861Schristos 		len += wpabuf_len(global->wfd_subelem[
146e604d861Schristos 					  WFD_SUBELEM_ASSOCIATED_BSSID]);
147e604d861Schristos 	if (global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK])
148e604d861Schristos 		len += wpabuf_len(global->wfd_subelem[
149e604d861Schristos 					  WFD_SUBELEM_COUPLED_SINK]);
150e604d861Schristos 	if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO])
151e604d861Schristos 		len += wpabuf_len(global->wfd_subelem[
152e604d861Schristos 					  WFD_SUBELEM_SESSION_INFO]);
153e604d861Schristos 	if (global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB])
154e604d861Schristos 		len += wpabuf_len(global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB]);
155e604d861Schristos 	buf = wpabuf_alloc(len);
156e604d861Schristos 	if (buf == NULL)
157e604d861Schristos 		return -1;
158e604d861Schristos 
159e604d861Schristos 	if (global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO])
160e604d861Schristos 		wpabuf_put_buf(buf,
161e604d861Schristos 			       global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]);
162*0a73ee0aSchristos 
163*0a73ee0aSchristos 	if (global->wfd_subelem[WFD_SUBELEM_R2_DEVICE_INFO])
164*0a73ee0aSchristos 		wpabuf_put_buf(buf,
165*0a73ee0aSchristos 			       global->wfd_subelem[WFD_SUBELEM_R2_DEVICE_INFO]);
166*0a73ee0aSchristos 
167e604d861Schristos 	if (global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID])
168e604d861Schristos 		wpabuf_put_buf(buf, global->wfd_subelem[
169e604d861Schristos 				       WFD_SUBELEM_ASSOCIATED_BSSID]);
170e604d861Schristos 	if (global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK])
171e604d861Schristos 		wpabuf_put_buf(buf,
172e604d861Schristos 			       global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK]);
173e604d861Schristos 
174e604d861Schristos 	ie = wifi_display_encaps(buf);
175e604d861Schristos 	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Beacon", ie);
176e604d861Schristos 	p2p_set_wfd_ie_beacon(global->p2p, ie);
177e604d861Schristos 
178e604d861Schristos 	ie = wifi_display_encaps(buf);
179e604d861Schristos 	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for (Re)Association Request",
180e604d861Schristos 			ie);
181e604d861Schristos 	p2p_set_wfd_ie_assoc_req(global->p2p, ie);
182e604d861Schristos 
183e604d861Schristos 	ie = wifi_display_encaps(buf);
184e604d861Schristos 	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for GO Negotiation", ie);
185e604d861Schristos 	p2p_set_wfd_ie_go_neg(global->p2p, ie);
186e604d861Schristos 
187e604d861Schristos 	ie = wifi_display_encaps(buf);
188e604d861Schristos 	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Provision Discovery "
189e604d861Schristos 			"Request", ie);
190e604d861Schristos 	p2p_set_wfd_ie_prov_disc_req(global->p2p, ie);
191e604d861Schristos 
192e604d861Schristos 	plen = buf->used;
193e604d861Schristos 	if (global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB])
194e604d861Schristos 		wpabuf_put_buf(buf,
195e604d861Schristos 			       global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB]);
196e604d861Schristos 
197e604d861Schristos 	ie = wifi_display_encaps(buf);
198e604d861Schristos 	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Probe Request", ie);
199e604d861Schristos 	p2p_set_wfd_ie_probe_req(global->p2p, ie);
200e604d861Schristos 
201e604d861Schristos 	if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO])
202e604d861Schristos 		wpabuf_put_buf(buf,
203e604d861Schristos 			       global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]);
204e604d861Schristos 	ie = wifi_display_encaps(buf);
205e604d861Schristos 	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Probe Response", ie);
206e604d861Schristos 	p2p_set_wfd_ie_probe_resp(global->p2p, ie);
207e604d861Schristos 
208e604d861Schristos 	/* Remove WFD Extended Capability from buffer */
209e604d861Schristos 	buf->used = plen;
210e604d861Schristos 	if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO])
211e604d861Schristos 		wpabuf_put_buf(buf,
212e604d861Schristos 			       global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]);
213e604d861Schristos 
214e604d861Schristos 	ie = wifi_display_encaps(buf);
215e604d861Schristos 	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for P2P Invitation", ie);
216e604d861Schristos 	p2p_set_wfd_ie_invitation(global->p2p, ie);
217e604d861Schristos 
218e604d861Schristos 	ie = wifi_display_encaps(buf);
219e604d861Schristos 	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Provision Discovery "
220e604d861Schristos 			"Response", ie);
221e604d861Schristos 	p2p_set_wfd_ie_prov_disc_resp(global->p2p, ie);
222e604d861Schristos 
223e604d861Schristos 	wpabuf_free(buf);
224e604d861Schristos 
225e604d861Schristos 	return 0;
226e604d861Schristos }
227e604d861Schristos 
228e604d861Schristos 
wifi_display_enable(struct wpa_global * global,int enabled)229e604d861Schristos void wifi_display_enable(struct wpa_global *global, int enabled)
230e604d861Schristos {
231e604d861Schristos 	wpa_printf(MSG_DEBUG, "WFD: Wi-Fi Display %s",
232e604d861Schristos 		   enabled ? "enabled" : "disabled");
233e604d861Schristos 	global->wifi_display = enabled;
234e604d861Schristos 	wifi_display_update_wfd_ie(global);
235e604d861Schristos }
236e604d861Schristos 
237e604d861Schristos 
wifi_display_subelem_set(struct wpa_global * global,char * cmd)238e604d861Schristos int wifi_display_subelem_set(struct wpa_global *global, char *cmd)
239e604d861Schristos {
240e604d861Schristos 	char *pos;
241e604d861Schristos 	int subelem;
242e604d861Schristos 	size_t len;
243e604d861Schristos 	struct wpabuf *e;
244e604d861Schristos 
245e604d861Schristos 	pos = os_strchr(cmd, ' ');
246e604d861Schristos 	if (pos == NULL)
247e604d861Schristos 		return -1;
248e604d861Schristos 	*pos++ = '\0';
249e604d861Schristos 
250e604d861Schristos 	len = os_strlen(pos);
251e604d861Schristos 	if (len & 1)
252e604d861Schristos 		return -1;
253e604d861Schristos 	len /= 2;
254e604d861Schristos 
255bb610346Schristos 	if (os_strcmp(cmd, "all") == 0) {
256bb610346Schristos 		int res;
257bb610346Schristos 
258bb610346Schristos 		e = wpabuf_alloc(len);
259bb610346Schristos 		if (e == NULL)
260bb610346Schristos 			return -1;
261bb610346Schristos 		if (hexstr2bin(pos, wpabuf_put(e, len), len) < 0) {
262bb610346Schristos 			wpabuf_free(e);
263bb610346Schristos 			return -1;
264bb610346Schristos 		}
265bb610346Schristos 		res = wifi_display_subelem_set_from_ies(global, e);
266bb610346Schristos 		wpabuf_free(e);
267bb610346Schristos 		return res;
268bb610346Schristos 	}
269bb610346Schristos 
270bb610346Schristos 	subelem = atoi(cmd);
271bb610346Schristos 	if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS)
272bb610346Schristos 		return -1;
273bb610346Schristos 
274e604d861Schristos 	if (len == 0) {
275e604d861Schristos 		/* Clear subelement */
276e604d861Schristos 		e = NULL;
277e604d861Schristos 		wpa_printf(MSG_DEBUG, "WFD: Clear subelement %d", subelem);
278e604d861Schristos 	} else {
279e604d861Schristos 		e = wpabuf_alloc(1 + len);
280e604d861Schristos 		if (e == NULL)
281e604d861Schristos 			return -1;
282e604d861Schristos 		wpabuf_put_u8(e, subelem);
283e604d861Schristos 		if (hexstr2bin(pos, wpabuf_put(e, len), len) < 0) {
284e604d861Schristos 			wpabuf_free(e);
285e604d861Schristos 			return -1;
286e604d861Schristos 		}
287e604d861Schristos 		wpa_printf(MSG_DEBUG, "WFD: Set subelement %d", subelem);
288e604d861Schristos 	}
289e604d861Schristos 
290e604d861Schristos 	wpabuf_free(global->wfd_subelem[subelem]);
291e604d861Schristos 	global->wfd_subelem[subelem] = e;
292e604d861Schristos 	wifi_display_update_wfd_ie(global);
293e604d861Schristos 
294e604d861Schristos 	return 0;
295e604d861Schristos }
296e604d861Schristos 
297e604d861Schristos 
wifi_display_subelem_set_from_ies(struct wpa_global * global,struct wpabuf * ie)2983c260e60Schristos int wifi_display_subelem_set_from_ies(struct wpa_global *global,
2993c260e60Schristos 				      struct wpabuf *ie)
3003c260e60Schristos {
3013c260e60Schristos 	int subelements[MAX_WFD_SUBELEMS] = {};
3023c260e60Schristos 	const u8 *pos, *end;
303bb610346Schristos 	unsigned int len, subelem;
3043c260e60Schristos 	struct wpabuf *e;
3053c260e60Schristos 
3063c260e60Schristos 	wpa_printf(MSG_DEBUG, "WFD IEs set: %p - %lu",
3073c260e60Schristos 		   ie, ie ? (unsigned long) wpabuf_len(ie) : 0);
3083c260e60Schristos 
3093c260e60Schristos 	if (ie == NULL || wpabuf_len(ie) < 6)
3103c260e60Schristos 		return -1;
3113c260e60Schristos 
3123c260e60Schristos 	pos = wpabuf_head(ie);
3133c260e60Schristos 	end = pos + wpabuf_len(ie);
3143c260e60Schristos 
3153c260e60Schristos 	while (end > pos) {
3163c260e60Schristos 		if (pos + 3 > end)
3173c260e60Schristos 			break;
3183c260e60Schristos 
3193c260e60Schristos 		len = WPA_GET_BE16(pos + 1) + 3;
3203c260e60Schristos 
3213c260e60Schristos 		wpa_printf(MSG_DEBUG, "WFD Sub-Element ID %d - len %d",
3223c260e60Schristos 			   *pos, len - 3);
3233c260e60Schristos 
324bb610346Schristos 		if (len > (unsigned int) (end - pos))
3253c260e60Schristos 			break;
3263c260e60Schristos 
3273c260e60Schristos 		subelem = *pos;
3283c260e60Schristos 		if (subelem < MAX_WFD_SUBELEMS && subelements[subelem] == 0) {
3293c260e60Schristos 			e = wpabuf_alloc_copy(pos, len);
3303c260e60Schristos 			if (e == NULL)
3313c260e60Schristos 				return -1;
3323c260e60Schristos 
3333c260e60Schristos 			wpabuf_free(global->wfd_subelem[subelem]);
3343c260e60Schristos 			global->wfd_subelem[subelem] = e;
3353c260e60Schristos 			subelements[subelem] = 1;
3363c260e60Schristos 		}
3373c260e60Schristos 
3383c260e60Schristos 		pos += len;
3393c260e60Schristos 	}
3403c260e60Schristos 
3413c260e60Schristos 	for (subelem = 0; subelem < MAX_WFD_SUBELEMS; subelem++) {
3423c260e60Schristos 		if (subelements[subelem] == 0) {
3433c260e60Schristos 			wpabuf_free(global->wfd_subelem[subelem]);
3443c260e60Schristos 			global->wfd_subelem[subelem] = NULL;
3453c260e60Schristos 		}
3463c260e60Schristos 	}
3473c260e60Schristos 
3483c260e60Schristos 	return wifi_display_update_wfd_ie(global);
3493c260e60Schristos }
3503c260e60Schristos 
3513c260e60Schristos 
wifi_display_subelem_get(struct wpa_global * global,char * cmd,char * buf,size_t buflen)352e604d861Schristos int wifi_display_subelem_get(struct wpa_global *global, char *cmd,
353e604d861Schristos 			     char *buf, size_t buflen)
354e604d861Schristos {
355e604d861Schristos 	int subelem;
356e604d861Schristos 
357bb610346Schristos 	if (os_strcmp(cmd, "all") == 0) {
358bb610346Schristos 		struct wpabuf *ie;
359bb610346Schristos 		int res;
360bb610346Schristos 
361bb610346Schristos 		ie = wifi_display_get_wfd_ie(global);
362bb610346Schristos 		if (ie == NULL)
363bb610346Schristos 			return 0;
364bb610346Schristos 		res = wpa_snprintf_hex(buf, buflen, wpabuf_head(ie),
365bb610346Schristos 				       wpabuf_len(ie));
366bb610346Schristos 		wpabuf_free(ie);
367bb610346Schristos 		return res;
368bb610346Schristos 	}
369bb610346Schristos 
370e604d861Schristos 	subelem = atoi(cmd);
371e604d861Schristos 	if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS)
372e604d861Schristos 		return -1;
373e604d861Schristos 
374e604d861Schristos 	if (global->wfd_subelem[subelem] == NULL)
375e604d861Schristos 		return 0;
376e604d861Schristos 
377e604d861Schristos 	return wpa_snprintf_hex(buf, buflen,
378e604d861Schristos 				wpabuf_head_u8(global->wfd_subelem[subelem]) +
379e604d861Schristos 				1,
380e604d861Schristos 				wpabuf_len(global->wfd_subelem[subelem]) - 1);
381e604d861Schristos }
3823c260e60Schristos 
3833c260e60Schristos 
wifi_display_subelem_hex(const struct wpabuf * wfd_subelems,u8 id)3843c260e60Schristos char * wifi_display_subelem_hex(const struct wpabuf *wfd_subelems, u8 id)
3853c260e60Schristos {
3863c260e60Schristos 	char *subelem = NULL;
3873c260e60Schristos 	const u8 *buf;
3883c260e60Schristos 	size_t buflen;
3893c260e60Schristos 	size_t i = 0;
3903c260e60Schristos 	u16 elen;
3913c260e60Schristos 
3923c260e60Schristos 	if (!wfd_subelems)
3933c260e60Schristos 		return NULL;
3943c260e60Schristos 
3953c260e60Schristos 	buf = wpabuf_head_u8(wfd_subelems);
3963c260e60Schristos 	if (!buf)
3973c260e60Schristos 		return NULL;
3983c260e60Schristos 
3993c260e60Schristos 	buflen = wpabuf_len(wfd_subelems);
4003c260e60Schristos 
4013c260e60Schristos 	while (i + WIFI_DISPLAY_SUBELEM_HEADER_LEN < buflen) {
4023c260e60Schristos 		elen = WPA_GET_BE16(buf + i + 1);
4033c260e60Schristos 		if (i + WIFI_DISPLAY_SUBELEM_HEADER_LEN + elen > buflen)
4043c260e60Schristos 			break; /* truncated subelement */
4053c260e60Schristos 
4063c260e60Schristos 		if (buf[i] == id) {
4073c260e60Schristos 			/*
4083c260e60Schristos 			 * Limit explicitly to an arbitrary length to avoid
4093c260e60Schristos 			 * unnecessarily large allocations. In practice, this
4103c260e60Schristos 			 * is limited to maximum frame length anyway, so the
4113c260e60Schristos 			 * maximum memory allocation here is not really that
4123c260e60Schristos 			 * large. Anyway, the Wi-Fi Display subelements that
4133c260e60Schristos 			 * are fetched with this function are even shorter.
4143c260e60Schristos 			 */
4153c260e60Schristos 			if (elen > 1000)
4163c260e60Schristos 				break;
4173c260e60Schristos 			subelem = os_zalloc(2 * elen + 1);
4183c260e60Schristos 			if (!subelem)
4193c260e60Schristos 				return NULL;
4203c260e60Schristos 			wpa_snprintf_hex(subelem, 2 * elen + 1,
4213c260e60Schristos 					 buf + i +
4223c260e60Schristos 					 WIFI_DISPLAY_SUBELEM_HEADER_LEN,
4233c260e60Schristos 					 elen);
4243c260e60Schristos 			break;
4253c260e60Schristos 		}
4263c260e60Schristos 
4273c260e60Schristos 		i += elen + WIFI_DISPLAY_SUBELEM_HEADER_LEN;
4283c260e60Schristos 	}
4293c260e60Schristos 
4303c260e60Schristos 	return subelem;
4313c260e60Schristos }
432