blob: ddadd34bc5a1128c05abca3c5b9f08aae5717767 [file] [log] [blame]
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001/*
2 * P2P - IE builder
3 * Copyright (c) 2009-2010, Atheros Communications
4 *
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -08005 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007 */
8
9#include "includes.h"
10
11#include "common.h"
12#include "common/ieee802_11_defs.h"
Sunil8cd6f4d2022-06-28 18:40:46 +000013#include "common/ieee802_11_common.h"
Dmitry Shmidtd80a4012015-11-05 16:35:40 -080014#include "common/qca-vendor.h"
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070015#include "wps/wps_i.h"
16#include "p2p_i.h"
17
18
19void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token)
20{
21 wpabuf_put_u8(buf, WLAN_ACTION_VENDOR_SPECIFIC);
Dmitry Shmidta38abf92014-03-06 13:38:44 -080022 wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070023
24 wpabuf_put_u8(buf, subtype); /* OUI Subtype */
25 wpabuf_put_u8(buf, dialog_token);
26 wpa_printf(MSG_DEBUG, "P2P: * Dialog Token: %d", dialog_token);
27}
28
29
30void p2p_buf_add_public_action_hdr(struct wpabuf *buf, u8 subtype,
31 u8 dialog_token)
32{
33 wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
34 wpabuf_put_u8(buf, WLAN_PA_VENDOR_SPECIFIC);
Dmitry Shmidta38abf92014-03-06 13:38:44 -080035 wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070036
37 wpabuf_put_u8(buf, subtype); /* OUI Subtype */
38 wpabuf_put_u8(buf, dialog_token);
39 wpa_printf(MSG_DEBUG, "P2P: * Dialog Token: %d", dialog_token);
40}
41
42
43u8 * p2p_buf_add_ie_hdr(struct wpabuf *buf)
44{
45 u8 *len;
46
47 /* P2P IE header */
48 wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
49 len = wpabuf_put(buf, 1); /* IE length to be filled */
Dmitry Shmidta38abf92014-03-06 13:38:44 -080050 wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070051 wpa_printf(MSG_DEBUG, "P2P: * P2P IE header");
52 return len;
53}
54
55
56void p2p_buf_update_ie_hdr(struct wpabuf *buf, u8 *len)
57{
Sunil Ravic0f5d412024-09-11 22:12:49 +000058 /* Update P2P/P2P2 IE Length */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070059 *len = (u8 *) wpabuf_put(buf, 0) - len - 1;
60}
61
62
Sunil Ravic0f5d412024-09-11 22:12:49 +000063u8 * p2p_buf_add_p2p2_ie_hdr(struct wpabuf *buf)
64{
65 u8 *len;
66
67 /* P2P2 IE header */
68 wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
69 len = wpabuf_put(buf, 1); /* IE length to be filled */
70 wpabuf_put_be32(buf, P2P2_IE_VENDOR_TYPE);
71 wpa_printf(MSG_DEBUG, "P2P: * P2P2 IE header");
72 return len;
73}
74
75
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070076void p2p_buf_add_capability(struct wpabuf *buf, u8 dev_capab, u8 group_capab)
77{
78 /* P2P Capability */
79 wpabuf_put_u8(buf, P2P_ATTR_CAPABILITY);
80 wpabuf_put_le16(buf, 2);
81 wpabuf_put_u8(buf, dev_capab); /* Device Capabilities */
82 wpabuf_put_u8(buf, group_capab); /* Group Capabilities */
83 wpa_printf(MSG_DEBUG, "P2P: * Capability dev=%02x group=%02x",
84 dev_capab, group_capab);
85}
86
87
88void p2p_buf_add_go_intent(struct wpabuf *buf, u8 go_intent)
89{
90 /* Group Owner Intent */
91 wpabuf_put_u8(buf, P2P_ATTR_GROUP_OWNER_INTENT);
92 wpabuf_put_le16(buf, 1);
93 wpabuf_put_u8(buf, go_intent);
94 wpa_printf(MSG_DEBUG, "P2P: * GO Intent: Intent %u Tie breaker %u",
95 go_intent >> 1, go_intent & 0x01);
96}
97
98
99void p2p_buf_add_listen_channel(struct wpabuf *buf, const char *country,
100 u8 reg_class, u8 channel)
101{
102 /* Listen Channel */
103 wpabuf_put_u8(buf, P2P_ATTR_LISTEN_CHANNEL);
104 wpabuf_put_le16(buf, 5);
105 wpabuf_put_data(buf, country, 3);
106 wpabuf_put_u8(buf, reg_class); /* Regulatory Class */
107 wpabuf_put_u8(buf, channel); /* Channel Number */
108 wpa_printf(MSG_DEBUG, "P2P: * Listen Channel: Regulatory Class %u "
109 "Channel %u", reg_class, channel);
110}
111
112
113void p2p_buf_add_operating_channel(struct wpabuf *buf, const char *country,
114 u8 reg_class, u8 channel)
115{
116 /* Operating Channel */
117 wpabuf_put_u8(buf, P2P_ATTR_OPERATING_CHANNEL);
118 wpabuf_put_le16(buf, 5);
119 wpabuf_put_data(buf, country, 3);
120 wpabuf_put_u8(buf, reg_class); /* Regulatory Class */
121 wpabuf_put_u8(buf, channel); /* Channel Number */
122 wpa_printf(MSG_DEBUG, "P2P: * Operating Channel: Regulatory Class %u "
123 "Channel %u", reg_class, channel);
124}
125
126
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800127void p2p_buf_add_pref_channel_list(struct wpabuf *buf,
Sunil8cd6f4d2022-06-28 18:40:46 +0000128 const struct weighted_pcl *pref_freq_list,
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800129 unsigned int size)
130{
131 unsigned int i, count = 0;
132 u8 op_class, op_channel;
133
134 if (!size)
135 return;
136
137 /*
138 * First, determine the number of P2P supported channels in the
139 * pref_freq_list returned from driver. This is needed for calculations
140 * of the vendor IE size.
141 */
142 for (i = 0; i < size; i++) {
Sunil8cd6f4d2022-06-28 18:40:46 +0000143 if (p2p_freq_to_channel(pref_freq_list[i].freq, &op_class,
144 &op_channel) == 0 &&
145 !(pref_freq_list[i].flag & WEIGHTED_PCL_EXCLUDE))
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800146 count++;
147 }
148
149 wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
150 wpabuf_put_u8(buf, 4 + count * sizeof(u16));
151 wpabuf_put_be24(buf, OUI_QCA);
152 wpabuf_put_u8(buf, QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST);
153 for (i = 0; i < size; i++) {
Sunil8cd6f4d2022-06-28 18:40:46 +0000154 if (p2p_freq_to_channel(pref_freq_list[i].freq, &op_class,
155 &op_channel) < 0 ||
156 (pref_freq_list[i].flag & WEIGHTED_PCL_EXCLUDE)) {
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800157 wpa_printf(MSG_DEBUG, "Unsupported frequency %u MHz",
Sunil8cd6f4d2022-06-28 18:40:46 +0000158 pref_freq_list[i].freq);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800159 continue;
160 }
161 wpabuf_put_u8(buf, op_class);
162 wpabuf_put_u8(buf, op_channel);
163 }
164}
165
166
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700167void p2p_buf_add_channel_list(struct wpabuf *buf, const char *country,
Sunil8cd6f4d2022-06-28 18:40:46 +0000168 struct p2p_channels *chan, bool is_6ghz_capab)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700169{
170 u8 *len;
171 size_t i;
172
173 /* Channel List */
174 wpabuf_put_u8(buf, P2P_ATTR_CHANNEL_LIST);
175 len = wpabuf_put(buf, 2); /* IE length to be filled */
176 wpabuf_put_data(buf, country, 3); /* Country String */
177
178 for (i = 0; i < chan->reg_classes; i++) {
179 struct p2p_reg_class *c = &chan->reg_class[i];
Sunil8cd6f4d2022-06-28 18:40:46 +0000180
181 if (is_6ghz_op_class(c->reg_class) && !is_6ghz_capab)
182 continue;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700183 wpabuf_put_u8(buf, c->reg_class);
184 wpabuf_put_u8(buf, c->channels);
185 wpabuf_put_data(buf, c->channel, c->channels);
186 }
187
188 /* Update attribute length */
189 WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
Dmitry Shmidt91c40cd2012-09-25 14:23:53 -0700190 wpa_hexdump(MSG_DEBUG, "P2P: * Channel List",
191 len + 2, (u8 *) wpabuf_put(buf, 0) - len - 2);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700192}
193
Dmitry Shmidtd5e49232012-12-03 15:08:10 -0800194
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700195void p2p_buf_add_status(struct wpabuf *buf, u8 status)
196{
197 /* Status */
198 wpabuf_put_u8(buf, P2P_ATTR_STATUS);
199 wpabuf_put_le16(buf, 1);
200 wpabuf_put_u8(buf, status);
201 wpa_printf(MSG_DEBUG, "P2P: * Status: %d", status);
202}
203
204
205void p2p_buf_add_device_info(struct wpabuf *buf, struct p2p_data *p2p,
206 struct p2p_device *peer)
207{
208 u8 *len;
209 u16 methods;
210 size_t nlen, i;
211
212 /* P2P Device Info */
213 wpabuf_put_u8(buf, P2P_ATTR_DEVICE_INFO);
214 len = wpabuf_put(buf, 2); /* IE length to be filled */
215
216 /* P2P Device address */
217 wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700218
219 /* Config Methods */
220 methods = 0;
221 if (peer && peer->wps_method != WPS_NOT_READY) {
222 if (peer->wps_method == WPS_PBC)
223 methods |= WPS_CONFIG_PUSHBUTTON;
Dmitry Shmidt849734c2016-05-27 09:59:01 -0700224 else if (peer->wps_method == WPS_P2PS)
Dmitry Shmidtaff761d2015-02-06 10:50:36 -0800225 methods |= WPS_CONFIG_P2PS;
Dmitry Shmidt849734c2016-05-27 09:59:01 -0700226 else if (peer->wps_method == WPS_PIN_DISPLAY ||
227 peer->wps_method == WPS_PIN_KEYPAD)
228 methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800229 } else if (p2p->cfg->config_methods) {
230 methods |= p2p->cfg->config_methods &
231 (WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_DISPLAY |
Dmitry Shmidtaff761d2015-02-06 10:50:36 -0800232 WPS_CONFIG_KEYPAD | WPS_CONFIG_P2PS);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700233 } else {
234 methods |= WPS_CONFIG_PUSHBUTTON;
235 methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
Dmitry Shmidtaff761d2015-02-06 10:50:36 -0800236 methods |= WPS_CONFIG_P2PS;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700237 }
238 wpabuf_put_be16(buf, methods);
239
240 /* Primary Device Type */
241 wpabuf_put_data(buf, p2p->cfg->pri_dev_type,
242 sizeof(p2p->cfg->pri_dev_type));
243
244 /* Number of Secondary Device Types */
245 wpabuf_put_u8(buf, p2p->cfg->num_sec_dev_types);
246
247 /* Secondary Device Type List */
248 for (i = 0; i < p2p->cfg->num_sec_dev_types; i++)
249 wpabuf_put_data(buf, p2p->cfg->sec_dev_type[i],
250 WPS_DEV_TYPE_LEN);
251
252 /* Device Name */
253 nlen = p2p->cfg->dev_name ? os_strlen(p2p->cfg->dev_name) : 0;
254 wpabuf_put_be16(buf, ATTR_DEV_NAME);
255 wpabuf_put_be16(buf, nlen);
256 wpabuf_put_data(buf, p2p->cfg->dev_name, nlen);
257
258 /* Update attribute length */
259 WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
260 wpa_printf(MSG_DEBUG, "P2P: * Device Info");
261}
262
263
264void p2p_buf_add_device_id(struct wpabuf *buf, const u8 *dev_addr)
265{
266 /* P2P Device ID */
267 wpabuf_put_u8(buf, P2P_ATTR_DEVICE_ID);
268 wpabuf_put_le16(buf, ETH_ALEN);
269 wpabuf_put_data(buf, dev_addr, ETH_ALEN);
270 wpa_printf(MSG_DEBUG, "P2P: * Device ID: " MACSTR, MAC2STR(dev_addr));
271}
272
273
274void p2p_buf_add_config_timeout(struct wpabuf *buf, u8 go_timeout,
275 u8 client_timeout)
276{
277 /* Configuration Timeout */
278 wpabuf_put_u8(buf, P2P_ATTR_CONFIGURATION_TIMEOUT);
279 wpabuf_put_le16(buf, 2);
280 wpabuf_put_u8(buf, go_timeout);
281 wpabuf_put_u8(buf, client_timeout);
282 wpa_printf(MSG_DEBUG, "P2P: * Configuration Timeout: GO %d (*10ms) "
283 "client %d (*10ms)", go_timeout, client_timeout);
284}
285
286
287void p2p_buf_add_intended_addr(struct wpabuf *buf, const u8 *interface_addr)
288{
289 /* Intended P2P Interface Address */
290 wpabuf_put_u8(buf, P2P_ATTR_INTENDED_INTERFACE_ADDR);
291 wpabuf_put_le16(buf, ETH_ALEN);
292 wpabuf_put_data(buf, interface_addr, ETH_ALEN);
293 wpa_printf(MSG_DEBUG, "P2P: * Intended P2P Interface Address " MACSTR,
294 MAC2STR(interface_addr));
295}
296
297
298void p2p_buf_add_group_bssid(struct wpabuf *buf, const u8 *bssid)
299{
300 /* P2P Group BSSID */
301 wpabuf_put_u8(buf, P2P_ATTR_GROUP_BSSID);
302 wpabuf_put_le16(buf, ETH_ALEN);
303 wpabuf_put_data(buf, bssid, ETH_ALEN);
304 wpa_printf(MSG_DEBUG, "P2P: * P2P Group BSSID " MACSTR,
305 MAC2STR(bssid));
306}
307
308
309void p2p_buf_add_group_id(struct wpabuf *buf, const u8 *dev_addr,
310 const u8 *ssid, size_t ssid_len)
311{
312 /* P2P Group ID */
313 wpabuf_put_u8(buf, P2P_ATTR_GROUP_ID);
314 wpabuf_put_le16(buf, ETH_ALEN + ssid_len);
315 wpabuf_put_data(buf, dev_addr, ETH_ALEN);
316 wpabuf_put_data(buf, ssid, ssid_len);
317 wpa_printf(MSG_DEBUG, "P2P: * P2P Group ID " MACSTR,
318 MAC2STR(dev_addr));
Dmitry Shmidt54605472013-11-08 11:10:19 -0800319 wpa_hexdump_ascii(MSG_DEBUG, "P2P: P2P Group ID SSID", ssid, ssid_len);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700320}
321
322
323void p2p_buf_add_invitation_flags(struct wpabuf *buf, u8 flags)
324{
325 /* Invitation Flags */
326 wpabuf_put_u8(buf, P2P_ATTR_INVITATION_FLAGS);
327 wpabuf_put_le16(buf, 1);
328 wpabuf_put_u8(buf, flags);
329 wpa_printf(MSG_DEBUG, "P2P: * Invitation Flags: bitmap 0x%x", flags);
330}
331
332
333static void p2p_buf_add_noa_desc(struct wpabuf *buf, struct p2p_noa_desc *desc)
334{
335 if (desc == NULL)
336 return;
337
338 wpabuf_put_u8(buf, desc->count_type);
339 wpabuf_put_le32(buf, desc->duration);
340 wpabuf_put_le32(buf, desc->interval);
341 wpabuf_put_le32(buf, desc->start_time);
342}
343
344
345void p2p_buf_add_noa(struct wpabuf *buf, u8 noa_index, u8 opp_ps, u8 ctwindow,
346 struct p2p_noa_desc *desc1, struct p2p_noa_desc *desc2)
347{
348 /* Notice of Absence */
349 wpabuf_put_u8(buf, P2P_ATTR_NOTICE_OF_ABSENCE);
350 wpabuf_put_le16(buf, 2 + (desc1 ? 13 : 0) + (desc2 ? 13 : 0));
351 wpabuf_put_u8(buf, noa_index);
352 wpabuf_put_u8(buf, (opp_ps ? 0x80 : 0) | (ctwindow & 0x7f));
353 p2p_buf_add_noa_desc(buf, desc1);
354 p2p_buf_add_noa_desc(buf, desc2);
355 wpa_printf(MSG_DEBUG, "P2P: * Notice of Absence");
356}
357
358
359void p2p_buf_add_ext_listen_timing(struct wpabuf *buf, u16 period,
360 u16 interval)
361{
362 /* Extended Listen Timing */
363 wpabuf_put_u8(buf, P2P_ATTR_EXT_LISTEN_TIMING);
364 wpabuf_put_le16(buf, 4);
365 wpabuf_put_le16(buf, period);
366 wpabuf_put_le16(buf, interval);
367 wpa_printf(MSG_DEBUG, "P2P: * Extended Listen Timing (period %u msec "
368 "interval %u msec)", period, interval);
369}
370
371
372void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p)
373{
374 /* P2P Interface */
375 wpabuf_put_u8(buf, P2P_ATTR_INTERFACE);
376 wpabuf_put_le16(buf, ETH_ALEN + 1 + ETH_ALEN);
377 /* P2P Device address */
378 wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN);
379 /*
380 * FIX: Fetch interface address list from driver. Do not include
381 * the P2P Device address if it is never used as interface address.
382 */
383 /* P2P Interface Address Count */
384 wpabuf_put_u8(buf, 1);
385 wpabuf_put_data(buf, p2p->cfg->dev_addr, ETH_ALEN);
386}
387
388
Dmitry Shmidtcf32e602014-01-28 10:57:39 -0800389void p2p_buf_add_oob_go_neg_channel(struct wpabuf *buf, const char *country,
390 u8 oper_class, u8 channel,
391 enum p2p_role_indication role)
392{
393 /* OOB Group Owner Negotiation Channel */
394 wpabuf_put_u8(buf, P2P_ATTR_OOB_GO_NEG_CHANNEL);
395 wpabuf_put_le16(buf, 6);
396 wpabuf_put_data(buf, country, 3);
397 wpabuf_put_u8(buf, oper_class); /* Operating Class */
398 wpabuf_put_u8(buf, channel); /* Channel Number */
399 wpabuf_put_u8(buf, (u8) role); /* Role indication */
400 wpa_printf(MSG_DEBUG, "P2P: * OOB GO Negotiation Channel: Operating "
401 "Class %u Channel %u Role %d",
402 oper_class, channel, role);
403}
404
405
Dmitry Shmidtaff761d2015-02-06 10:50:36 -0800406void p2p_buf_add_service_hash(struct wpabuf *buf, struct p2p_data *p2p)
407{
408 if (!p2p)
409 return;
410
411 /* Service Hash */
412 wpabuf_put_u8(buf, P2P_ATTR_SERVICE_HASH);
413 wpabuf_put_le16(buf, p2p->p2ps_seek_count * P2PS_HASH_LEN);
Dmitry Shmidta3dc3092015-06-23 11:21:28 -0700414 wpabuf_put_data(buf, p2p->p2ps_seek_hash,
Dmitry Shmidtaff761d2015-02-06 10:50:36 -0800415 p2p->p2ps_seek_count * P2PS_HASH_LEN);
416 wpa_hexdump(MSG_DEBUG, "P2P: * Service Hash",
Dmitry Shmidta3dc3092015-06-23 11:21:28 -0700417 p2p->p2ps_seek_hash, p2p->p2ps_seek_count * P2PS_HASH_LEN);
Dmitry Shmidtaff761d2015-02-06 10:50:36 -0800418}
419
420
421void p2p_buf_add_session_info(struct wpabuf *buf, const char *info)
422{
423 size_t info_len = 0;
424
425 if (info && info[0])
426 info_len = os_strlen(info);
427
428 /* Session Information Data Info */
429 wpabuf_put_u8(buf, P2P_ATTR_SESSION_INFORMATION_DATA);
430 wpabuf_put_le16(buf, (u16) info_len);
431
432 if (info) {
433 wpabuf_put_data(buf, info, info_len);
434 wpa_printf(MSG_DEBUG, "P2P: * Session Info Data (%s)", info);
435 }
436}
437
438
439void p2p_buf_add_connection_capability(struct wpabuf *buf, u8 connection_cap)
440{
441 /* Connection Capability Info */
442 wpabuf_put_u8(buf, P2P_ATTR_CONNECTION_CAPABILITY);
443 wpabuf_put_le16(buf, 1);
444 wpabuf_put_u8(buf, connection_cap);
445 wpa_printf(MSG_DEBUG, "P2P: * Connection Capability: 0x%x",
446 connection_cap);
447}
448
449
450void p2p_buf_add_advertisement_id(struct wpabuf *buf, u32 id, const u8 *mac)
451{
452 if (!buf || !mac)
453 return;
454
455 /* Advertisement ID Info */
456 wpabuf_put_u8(buf, P2P_ATTR_ADVERTISEMENT_ID);
457 wpabuf_put_le16(buf, (u16) (sizeof(u32) + ETH_ALEN));
458 wpabuf_put_le32(buf, id);
459 wpabuf_put_data(buf, mac, ETH_ALEN);
460 wpa_printf(MSG_DEBUG, "P2P: * Advertisement ID (%x) " MACSTR,
461 id, MAC2STR(mac));
462}
463
464
Dmitry Shmidta3dc3092015-06-23 11:21:28 -0700465static int p2ps_wildcard_hash(struct p2p_data *p2p,
466 const u8 *hash, u8 hash_count)
467{
468 u8 i;
469 const u8 *test = hash;
470
471 for (i = 0; i < hash_count; i++) {
472 if (os_memcmp(test, p2p->wild_card_hash, P2PS_HASH_LEN) == 0)
473 return 1;
474 test += P2PS_HASH_LEN;
475 }
476
477 return 0;
478}
479
480
481static int p2p_wfa_service_adv(struct p2p_data *p2p)
482{
483 struct p2ps_advertisement *adv;
484
485 for (adv = p2p->p2ps_adv_list; adv; adv = adv->next) {
486 if (os_strncmp(adv->svc_name, P2PS_WILD_HASH_STR,
487 os_strlen(P2PS_WILD_HASH_STR)) == 0)
488 return 1;
489 }
490
491 return 0;
492}
493
494
495static int p2p_buf_add_service_info(struct wpabuf *buf, struct p2p_data *p2p,
496 u32 adv_id, u16 config_methods,
497 const char *svc_name, u8 **ie_len, u8 **pos,
498 size_t *total_len, u8 *attr_len)
499{
500 size_t svc_len;
501 size_t remaining;
502 size_t info_len;
503
504 p2p_dbg(p2p, "Add service info for %s (adv_id=%u)", svc_name, adv_id);
505 svc_len = os_strlen(svc_name);
506 info_len = sizeof(adv_id) + sizeof(config_methods) + sizeof(u8) +
507 svc_len;
508
509 if (info_len + *total_len > MAX_SVC_ADV_LEN) {
510 p2p_dbg(p2p,
511 "Unsufficient buffer, failed to add advertised service info");
512 return -1;
513 }
514
515 if (svc_len > 255) {
516 p2p_dbg(p2p,
517 "Invalid service name length (%u bytes), failed to add advertised service info",
518 (unsigned int) svc_len);
519 return -1;
520 }
521
522 if (*ie_len) {
523 int ie_data_len = (*pos - *ie_len) - 1;
524
525 if (ie_data_len < 0 || ie_data_len > 255) {
526 p2p_dbg(p2p,
527 "Invalid IE length, failed to add advertised service info");
528 return -1;
529 }
530 remaining = 255 - ie_data_len;
531 } else {
532 /*
533 * Adding new P2P IE header takes 6 extra bytes:
534 * - 2 byte IE header (1 byte IE id and 1 byte length)
535 * - 4 bytes of IE_VENDOR_TYPE are reduced from 255 below
536 */
537 *ie_len = p2p_buf_add_ie_hdr(buf);
538 remaining = 255 - 4;
539 }
540
541 if (remaining < sizeof(u32) + sizeof(u16) + sizeof(u8)) {
542 /*
543 * Split adv_id, config_methods, and svc_name_len between two
544 * IEs.
545 */
546 size_t front = remaining;
547 size_t back = sizeof(u32) + sizeof(u16) + sizeof(u8) - front;
548 u8 holder[sizeof(u32) + sizeof(u16) + sizeof(u8)];
549
550 WPA_PUT_LE32(holder, adv_id);
551 WPA_PUT_BE16(&holder[sizeof(u32)], config_methods);
552 holder[sizeof(u32) + sizeof(u16)] = svc_len;
553
554 if (front)
555 wpabuf_put_data(buf, holder, front);
556
557 p2p_buf_update_ie_hdr(buf, *ie_len);
558 *ie_len = p2p_buf_add_ie_hdr(buf);
559
560 wpabuf_put_data(buf, &holder[front], back);
561 remaining = 255 - 4 - (sizeof(u32) + sizeof(u16) + sizeof(u8)) -
562 back;
563 } else {
564 wpabuf_put_le32(buf, adv_id);
565 wpabuf_put_be16(buf, config_methods);
566 wpabuf_put_u8(buf, svc_len);
567 remaining -= sizeof(adv_id) + sizeof(config_methods) +
568 sizeof(u8);
569 }
570
571 if (remaining < svc_len) {
572 /* split svc_name between two or three IEs */
573 size_t front = remaining;
574 size_t back = svc_len - front;
575
576 if (front)
577 wpabuf_put_data(buf, svc_name, front);
578
579 p2p_buf_update_ie_hdr(buf, *ie_len);
580 *ie_len = p2p_buf_add_ie_hdr(buf);
581
582 /* In rare cases, we must split across 3 attributes */
583 if (back > 255 - 4) {
584 wpabuf_put_data(buf, &svc_name[front], 255 - 4);
585 back -= 255 - 4;
586 front += 255 - 4;
587 p2p_buf_update_ie_hdr(buf, *ie_len);
588 *ie_len = p2p_buf_add_ie_hdr(buf);
589 }
590
591 wpabuf_put_data(buf, &svc_name[front], back);
592 remaining = 255 - 4 - back;
593 } else {
594 wpabuf_put_data(buf, svc_name, svc_len);
595 remaining -= svc_len;
596 }
597
598 p2p_buf_update_ie_hdr(buf, *ie_len);
599
600 /* set *ie_len to NULL if a new IE has to be added on the next call */
601 if (!remaining)
602 *ie_len = NULL;
603
604 /* set *pos to point to the next byte to update */
605 *pos = wpabuf_put(buf, 0);
606
607 *total_len += info_len;
608 WPA_PUT_LE16(attr_len, (u16) *total_len);
609 return 0;
610}
611
612
Dmitry Shmidtaff761d2015-02-06 10:50:36 -0800613void p2p_buf_add_service_instance(struct wpabuf *buf, struct p2p_data *p2p,
614 u8 hash_count, const u8 *hash,
615 struct p2ps_advertisement *adv_list)
616{
617 struct p2ps_advertisement *adv;
Dmitry Shmidt41712582015-06-29 11:02:15 -0700618 int p2ps_wildcard;
Dmitry Shmidta3dc3092015-06-23 11:21:28 -0700619 size_t total_len;
620 struct wpabuf *tmp_buf = NULL;
621 u8 *pos, *attr_len, *ie_len = NULL;
Dmitry Shmidtaff761d2015-02-06 10:50:36 -0800622
Dmitry Shmidta3dc3092015-06-23 11:21:28 -0700623 if (!adv_list || !hash || !hash_count)
Dmitry Shmidtaff761d2015-02-06 10:50:36 -0800624 return;
625
Dmitry Shmidta3dc3092015-06-23 11:21:28 -0700626 wpa_hexdump(MSG_DEBUG, "P2PS: Probe Request service hash values",
627 hash, hash_count * P2PS_HASH_LEN);
628 p2ps_wildcard = p2ps_wildcard_hash(p2p, hash, hash_count) &&
629 p2p_wfa_service_adv(p2p);
630
Dmitry Shmidtaff761d2015-02-06 10:50:36 -0800631 /* Allocate temp buffer, allowing for overflow of 1 instance */
632 tmp_buf = wpabuf_alloc(MAX_SVC_ADV_IE_LEN + 256 + P2PS_HASH_LEN);
633 if (!tmp_buf)
634 return;
635
Dmitry Shmidta3dc3092015-06-23 11:21:28 -0700636 /*
637 * Attribute data can be split into a number of IEs. Start with the
638 * first IE and the attribute headers here.
639 */
640 ie_len = p2p_buf_add_ie_hdr(tmp_buf);
641
642 total_len = 0;
643
644 wpabuf_put_u8(tmp_buf, P2P_ATTR_ADVERTISED_SERVICE);
645 attr_len = wpabuf_put(tmp_buf, sizeof(u16));
646 WPA_PUT_LE16(attr_len, (u16) total_len);
647 p2p_buf_update_ie_hdr(tmp_buf, ie_len);
648 pos = wpabuf_put(tmp_buf, 0);
649
650 if (p2ps_wildcard) {
651 /* org.wi-fi.wfds match found */
652 p2p_buf_add_service_info(tmp_buf, p2p, 0, 0, P2PS_WILD_HASH_STR,
653 &ie_len, &pos, &total_len, attr_len);
Dmitry Shmidta3dc3092015-06-23 11:21:28 -0700654 }
655
656 /* add advertised service info of matching services */
Dmitry Shmidtaff761d2015-02-06 10:50:36 -0800657 for (adv = adv_list; adv && total_len <= MAX_SVC_ADV_LEN;
658 adv = adv->next) {
Dmitry Shmidtaff761d2015-02-06 10:50:36 -0800659 const u8 *test = hash;
Dmitry Shmidta3dc3092015-06-23 11:21:28 -0700660 u8 i;
Dmitry Shmidtaff761d2015-02-06 10:50:36 -0800661
Dmitry Shmidta3dc3092015-06-23 11:21:28 -0700662 for (i = 0; i < hash_count; i++) {
663 /* exact name hash match */
664 if (os_memcmp(test, adv->hash, P2PS_HASH_LEN) == 0 &&
665 p2p_buf_add_service_info(tmp_buf, p2p,
666 adv->id,
667 adv->config_methods,
668 adv->svc_name,
669 &ie_len, &pos,
670 &total_len,
671 attr_len))
672 break;
Dmitry Shmidt41712582015-06-29 11:02:15 -0700673
Dmitry Shmidtaff761d2015-02-06 10:50:36 -0800674 test += P2PS_HASH_LEN;
675 }
Dmitry Shmidtaff761d2015-02-06 10:50:36 -0800676 }
677
Dmitry Shmidt41712582015-06-29 11:02:15 -0700678 if (total_len)
Dmitry Shmidtaff761d2015-02-06 10:50:36 -0800679 wpabuf_put_buf(buf, tmp_buf);
Dmitry Shmidtaff761d2015-02-06 10:50:36 -0800680 wpabuf_free(tmp_buf);
681}
682
683
684void p2p_buf_add_session_id(struct wpabuf *buf, u32 id, const u8 *mac)
685{
686 if (!buf || !mac)
687 return;
688
689 /* Session ID Info */
690 wpabuf_put_u8(buf, P2P_ATTR_SESSION_ID);
691 wpabuf_put_le16(buf, (u16) (sizeof(u32) + ETH_ALEN));
692 wpabuf_put_le32(buf, id);
693 wpabuf_put_data(buf, mac, ETH_ALEN);
694 wpa_printf(MSG_DEBUG, "P2P: * Session ID Info (%x) " MACSTR,
695 id, MAC2STR(mac));
696}
697
698
699void p2p_buf_add_feature_capability(struct wpabuf *buf, u16 len, const u8 *mask)
700{
701 if (!buf || !len || !mask)
702 return;
703
704 /* Feature Capability */
705 wpabuf_put_u8(buf, P2P_ATTR_FEATURE_CAPABILITY);
706 wpabuf_put_le16(buf, len);
707 wpabuf_put_data(buf, mask, len);
708 wpa_printf(MSG_DEBUG, "P2P: * Feature Capability (%d)", len);
709}
710
711
712void p2p_buf_add_persistent_group_info(struct wpabuf *buf, const u8 *dev_addr,
713 const u8 *ssid, size_t ssid_len)
714{
715 /* P2P Group ID */
716 wpabuf_put_u8(buf, P2P_ATTR_PERSISTENT_GROUP);
717 wpabuf_put_le16(buf, ETH_ALEN + ssid_len);
718 wpabuf_put_data(buf, dev_addr, ETH_ALEN);
719 wpabuf_put_data(buf, ssid, ssid_len);
720 wpa_printf(MSG_DEBUG, "P2P: * P2P Group ID " MACSTR,
721 MAC2STR(dev_addr));
722}
723
724
Sunil Ravic0f5d412024-09-11 22:12:49 +0000725void p2p_buf_add_pcea(struct wpabuf *buf, struct p2p_data *p2p)
726{
727 u8 *len;
728 u16 capability_info = 0;
729
730 /* P2P Capability Extension */
731 wpabuf_put_u8(buf, P2P_ATTR_CAPABILITY_EXTENSION);
732 /* Length to be filled */
733 len = wpabuf_put(buf, 2);
734
735 if (!p2p->cfg->p2p_6ghz_disable)
736 capability_info |= P2P_PCEA_6GHZ;
737
738 if (p2p->cfg->reg_info)
739 capability_info |= P2P_PCEA_REG_INFO;
740
741 if (p2p->cfg->dfs_owner)
742 capability_info |= P2P_PCEA_DFS_OWNER;
743
744 if (p2p->cfg->pairing_config.pairing_capable)
745 capability_info |= P2P_PCEA_PAIRING_CAPABLE;
746
747 if (p2p->cfg->pairing_config.enable_pairing_setup)
748 capability_info |= P2P_PCEA_PAIRING_SETUP_ENABLED;
749
750 if (p2p->cfg->pairing_config.enable_pairing_cache)
751 capability_info |= P2P_PCEA_PMK_CACHING;
752
753 if (p2p->cfg->pairing_config.pasn_type)
754 capability_info |= P2P_PCEA_PASN_TYPE;
755
756 if (p2p->cfg->twt_power_mgmt)
757 capability_info |= P2P_PCEA_TWT_POWER_MGMT;
758
759 /* Field length is (n-1), n in octets */
760 capability_info |= (2 - 1) & P2P_PCEA_LEN_MASK;
761 wpabuf_put_le16(buf, capability_info);
762
763 if (capability_info & P2P_PCEA_REG_INFO)
764 wpabuf_put_u8(buf, p2p->cfg->reg_info);
765
766 if (capability_info & P2P_PCEA_PASN_TYPE)
767 wpabuf_put_u8(buf, p2p->cfg->pairing_config.pasn_type);
768
769 /* Update attribute length */
770 WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
771
772 wpa_printf(MSG_DEBUG, "P2P: * Capability Extension info=0x%x",
773 capability_info);
774}
775
776
777void p2p_buf_add_pbma(struct wpabuf *buf, u16 bootstrap, const u8 *cookie,
778 size_t cookie_len, int comeback_after)
779{
780 u8 *len;
781
782 /* P2P Pairing and Bootstrapping methods */
783 wpabuf_put_u8(buf, P2P_ATTR_PAIRING_AND_BOOTSTRAPPING);
784 /* Length to be filled */
785 len = wpabuf_put(buf, 2);
786
787 if (cookie && cookie_len) {
788 if (comeback_after)
789 wpabuf_put_le16(buf, comeback_after);
790 wpabuf_put_u8(buf, cookie_len);
791 wpabuf_put_data(buf, cookie, cookie_len);
792 }
793 wpabuf_put_le16(buf, bootstrap);
794
795 /* Update attribute length */
796 WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
797
798 wpa_printf(MSG_DEBUG, "P2P: * Bootstrapping method=0x%x",
799 bootstrap);
800}
801
802
803void p2p_buf_add_dira(struct wpabuf *buf, struct p2p_data *p2p)
804{
805 u8 *len;
806 struct p2p_id_key *dev_ik;
807
808 if (!p2p->cfg->pairing_config.pairing_capable ||
809 !p2p->cfg->pairing_config.enable_pairing_cache ||
810 !p2p->cfg->pairing_config.enable_pairing_verification)
811 return;
812
813 dev_ik = &p2p->pairing_info->dev_ik;
814 /* P2P DIRA */
815 wpabuf_put_u8(buf, P2P_ATTR_DEVICE_IDENTITY_RESOLUTION);
816 /* Length to be filled */
817 len = wpabuf_put(buf, 2);
818
819 wpabuf_put_u8(buf, dev_ik->cipher_version);
820 wpabuf_put_data(buf, dev_ik->dira_nonce, dev_ik->dira_nonce_len);
821 wpabuf_put_data(buf, dev_ik->dira_tag, dev_ik->dira_tag_len);
822
823 /* Update attribute length */
824 WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
825
826 wpa_printf(MSG_DEBUG, "P2P: * DIRA");
827}
828
829
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -0700830static int p2p_add_wps_string(struct wpabuf *buf, enum wps_attribute attr,
831 const char *val)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700832{
833 size_t len;
834
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700835 len = val ? os_strlen(val) : 0;
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -0700836 if (wpabuf_tailroom(buf) < 4 + len)
837 return -1;
838 wpabuf_put_be16(buf, attr);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700839#ifndef CONFIG_WPS_STRICT
840 if (len == 0) {
841 /*
842 * Some deployed WPS implementations fail to parse zeor-length
843 * attributes. As a workaround, send a space character if the
844 * device attribute string is empty.
845 */
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -0700846 if (wpabuf_tailroom(buf) < 3)
847 return -1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700848 wpabuf_put_be16(buf, 1);
849 wpabuf_put_u8(buf, ' ');
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -0700850 return 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700851 }
852#endif /* CONFIG_WPS_STRICT */
853 wpabuf_put_be16(buf, len);
854 if (val)
855 wpabuf_put_data(buf, val, len);
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -0700856 return 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700857}
858
859
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -0700860int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
861 int all_attr)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700862{
863 u8 *len;
864 int i;
865
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -0700866 if (wpabuf_tailroom(buf) < 6)
867 return -1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700868 wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
869 len = wpabuf_put(buf, 1);
870 wpabuf_put_be32(buf, WPS_DEV_OUI_WFA);
871
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -0700872 if (wps_build_version(buf) < 0)
873 return -1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700874
875 if (all_attr) {
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -0700876 if (wpabuf_tailroom(buf) < 5)
877 return -1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700878 wpabuf_put_be16(buf, ATTR_WPS_STATE);
879 wpabuf_put_be16(buf, 1);
880 wpabuf_put_u8(buf, WPS_STATE_NOT_CONFIGURED);
881 }
882
Dmitry Shmidt04949592012-07-19 12:16:46 -0700883 if (pw_id >= 0) {
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -0700884 if (wpabuf_tailroom(buf) < 6)
885 return -1;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700886 /* Device Password ID */
887 wpabuf_put_be16(buf, ATTR_DEV_PASSWORD_ID);
888 wpabuf_put_be16(buf, 2);
889 wpa_printf(MSG_DEBUG, "P2P: WPS IE Device Password ID: %d",
890 pw_id);
891 wpabuf_put_be16(buf, pw_id);
892 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700893
894 if (all_attr) {
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -0700895 if (wpabuf_tailroom(buf) < 5)
896 return -1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700897 wpabuf_put_be16(buf, ATTR_RESPONSE_TYPE);
898 wpabuf_put_be16(buf, 1);
899 wpabuf_put_u8(buf, WPS_RESP_ENROLLEE_INFO);
900
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -0700901 if (wps_build_uuid_e(buf, p2p->cfg->uuid) < 0 ||
902 p2p_add_wps_string(buf, ATTR_MANUFACTURER,
903 p2p->cfg->manufacturer) < 0 ||
904 p2p_add_wps_string(buf, ATTR_MODEL_NAME,
905 p2p->cfg->model_name) < 0 ||
906 p2p_add_wps_string(buf, ATTR_MODEL_NUMBER,
907 p2p->cfg->model_number) < 0 ||
908 p2p_add_wps_string(buf, ATTR_SERIAL_NUMBER,
909 p2p->cfg->serial_number) < 0)
910 return -1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700911
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -0700912 if (wpabuf_tailroom(buf) < 4 + WPS_DEV_TYPE_LEN)
913 return -1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700914 wpabuf_put_be16(buf, ATTR_PRIMARY_DEV_TYPE);
915 wpabuf_put_be16(buf, WPS_DEV_TYPE_LEN);
916 wpabuf_put_data(buf, p2p->cfg->pri_dev_type, WPS_DEV_TYPE_LEN);
917
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -0700918 if (p2p_add_wps_string(buf, ATTR_DEV_NAME, p2p->cfg->dev_name)
919 < 0)
920 return -1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700921
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -0700922 if (wpabuf_tailroom(buf) < 6)
923 return -1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700924 wpabuf_put_be16(buf, ATTR_CONFIG_METHODS);
925 wpabuf_put_be16(buf, 2);
926 wpabuf_put_be16(buf, p2p->cfg->config_methods);
927 }
928
Hai Shalom021b0b52019-04-10 11:17:58 -0700929 if (wps_build_wfa_ext(buf, 0, NULL, 0, 0) < 0)
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -0700930 return -1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700931
932 if (all_attr && p2p->cfg->num_sec_dev_types) {
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -0700933 if (wpabuf_tailroom(buf) <
934 4 + WPS_DEV_TYPE_LEN * p2p->cfg->num_sec_dev_types)
935 return -1;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700936 wpabuf_put_be16(buf, ATTR_SECONDARY_DEV_TYPE_LIST);
937 wpabuf_put_be16(buf, WPS_DEV_TYPE_LEN *
938 p2p->cfg->num_sec_dev_types);
939 wpabuf_put_data(buf, p2p->cfg->sec_dev_type,
940 WPS_DEV_TYPE_LEN *
941 p2p->cfg->num_sec_dev_types);
942 }
943
944 /* Add the WPS vendor extensions */
945 for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
946 if (p2p->wps_vendor_ext[i] == NULL)
947 break;
948 if (wpabuf_tailroom(buf) <
949 4 + wpabuf_len(p2p->wps_vendor_ext[i]))
950 continue;
951 wpabuf_put_be16(buf, ATTR_VENDOR_EXT);
952 wpabuf_put_be16(buf, wpabuf_len(p2p->wps_vendor_ext[i]));
953 wpabuf_put_buf(buf, p2p->wps_vendor_ext[i]);
954 }
955
956 p2p_buf_update_ie_hdr(buf, len);
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -0700957
958 return 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700959}
Sunil Ravic0f5d412024-09-11 22:12:49 +0000960
961
962struct wpabuf * p2p_encaps_ie(const struct wpabuf *subelems, u32 ie_type)
963{
964 struct wpabuf *ie;
965 const u8 *pos, *end;
966 size_t len;
967
968 if (!subelems)
969 return NULL;
970
971 len = wpabuf_len(subelems) + 1000;
972
973 ie = wpabuf_alloc(len);
974 if (!ie)
975 return NULL;
976
977 pos = wpabuf_head(subelems);
978 end = pos + wpabuf_len(subelems);
979
980 while (end > pos) {
981 size_t frag_len = end - pos;
982
983 if (frag_len > 251)
984 frag_len = 251;
985 wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
986 wpabuf_put_u8(ie, 4 + frag_len);
987 wpabuf_put_be32(ie, ie_type);
988 wpabuf_put_data(ie, pos, frag_len);
989 pos += frag_len;
990 }
991
992 return ie;
993}