1 /*
2 * Driver O/S-independent utility routines
3 *
4 * Copyright (C) 2020, Broadcom.
5 *
6 * Unless you and Broadcom execute a separate written software license
7 * agreement governing use of this software, this software is licensed to you
8 * under the terms of the GNU General Public License version 2 (the "GPL"),
9 * available at http://www.broadcom.com/licenses/GPLv2.php, with the
10 * following added to such license:
11 *
12 * As a special exception, the copyright holders of this software give you
13 * permission to link this software with independent modules, and to copy and
14 * distribute the resulting executable under terms of your choice, provided that
15 * you also meet, for each linked independent module, the terms and conditions of
16 * the license of that module. An independent module is a module which is not
17 * derived from this software. The special exception does not apply to any
18 * modifications of the software.
19 *
20 *
21 * <<Broadcom-WL-IPTag/Dual:>>
22 */
23
24 #include <typedefs.h>
25 #include <bcmdefs.h>
26
27 #if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 16, 0))
28 #include <stdarg.h>
29 #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(5, 16, 0) */
30
31 #ifdef BCMDRIVER
32 #include <osl.h>
33 #else /* !BCMDRIVER */
34 #include <stdio.h>
35 #include <string.h>
36 #include <stdlib.h>
37 #ifndef ASSERT
38 #define ASSERT(exp)
39 #endif
40 #endif /* !BCMDRIVER */
41
42 #include <bcmtlv.h>
43 #include <bcmendian.h>
44 #include <bcmutils.h>
45
46 int
BCMPOSTTRAPFN(bcm_xtlv_hdr_size)47 BCMPOSTTRAPFN(bcm_xtlv_hdr_size)(bcm_xtlv_opts_t opts)
48 {
49 int len = (int)OFFSETOF(bcm_xtlv_t, data); /* nominal */
50 if (opts & BCM_XTLV_OPTION_LENU8) --len;
51 if (opts & BCM_XTLV_OPTION_IDU8) --len;
52
53 return len;
54 }
55
56 bool
bcm_valid_xtlv(const bcm_xtlv_t * elt,int buf_len,bcm_xtlv_opts_t opts)57 bcm_valid_xtlv(const bcm_xtlv_t *elt, int buf_len, bcm_xtlv_opts_t opts)
58 {
59 return elt != NULL &&
60 buf_len >= bcm_xtlv_hdr_size(opts) &&
61 buf_len >= bcm_xtlv_size(elt, opts);
62 }
63
64 int
BCMPOSTTRAPFN(bcm_xtlv_size_for_data)65 BCMPOSTTRAPFN(bcm_xtlv_size_for_data)(int dlen, bcm_xtlv_opts_t opts)
66 {
67 int hsz;
68
69 hsz = bcm_xtlv_hdr_size(opts);
70 return ((opts & BCM_XTLV_OPTION_ALIGN32) ? ALIGN_SIZE(dlen + hsz, 4)
71 : (dlen + hsz));
72 }
73
74 int
bcm_xtlv_size(const bcm_xtlv_t * elt,bcm_xtlv_opts_t opts)75 bcm_xtlv_size(const bcm_xtlv_t *elt, bcm_xtlv_opts_t opts)
76 {
77 int size; /* size including header, data, and any pad */
78 int len; /* length wthout padding */
79
80 len = BCM_XTLV_LEN_EX(elt, opts);
81 size = bcm_xtlv_size_for_data(len, opts);
82 return size;
83 }
84
85 int
bcm_xtlv_len(const bcm_xtlv_t * elt,bcm_xtlv_opts_t opts)86 bcm_xtlv_len(const bcm_xtlv_t *elt, bcm_xtlv_opts_t opts)
87 {
88 const uint8 *lenp;
89 int len;
90
91 lenp = (const uint8 *)elt + OFFSETOF(bcm_xtlv_t, len); /* nominal */
92 if (opts & BCM_XTLV_OPTION_IDU8) {
93 --lenp;
94 }
95
96 if (opts & BCM_XTLV_OPTION_LENU8) {
97 len = *lenp;
98 } else if (opts & BCM_XTLV_OPTION_LENBE) {
99 len = (uint32)hton16(elt->len);
100 } else {
101 len = ltoh16_ua(lenp);
102 }
103
104 return len;
105 }
106
107 int
bcm_xtlv_id(const bcm_xtlv_t * elt,bcm_xtlv_opts_t opts)108 bcm_xtlv_id(const bcm_xtlv_t *elt, bcm_xtlv_opts_t opts)
109 {
110 int id = 0;
111 if (opts & BCM_XTLV_OPTION_IDU8) {
112 id = *(const uint8 *)elt;
113 } else if (opts & BCM_XTLV_OPTION_IDBE) {
114 id = (uint32)hton16(elt->id);
115 } else {
116 id = ltoh16_ua((const uint8 *)elt);
117 }
118
119 return id;
120 }
121
122 bcm_xtlv_t *
bcm_next_xtlv(const bcm_xtlv_t * elt,int * buflen,bcm_xtlv_opts_t opts)123 bcm_next_xtlv(const bcm_xtlv_t *elt, int *buflen, bcm_xtlv_opts_t opts)
124 {
125 uint sz;
126
127 COV_TAINTED_DATA_SINK(buflen);
128 COV_NEG_SINK(buflen);
129
130 /* validate current elt */
131 if (!bcm_valid_xtlv(elt, *buflen, opts))
132 return NULL;
133
134 /* advance to next elt */
135 sz = BCM_XTLV_SIZE_EX(elt, opts);
136 elt = (const bcm_xtlv_t*)((const uint8 *)elt + sz);
137
138 #if defined(__COVERITY__)
139 /* The 'sz' value is tainted in Coverity because it is read from the tainted data pointed
140 * to by 'elt'. However, bcm_valid_xtlv() verifies that the elt pointer is a valid element,
141 * so its size, sz = BCM_XTLV_SIZE_EX(elt, opts), is in the bounds of the buffer.
142 * Clearing the tainted attribute of 'sz' for Coverity.
143 */
144 __coverity_tainted_data_sanitize__(sz);
145 if (sz > *buflen) {
146 return NULL;
147 }
148 #endif /* __COVERITY__ */
149
150 *buflen -= sz;
151
152 /* validate next elt */
153 if (!bcm_valid_xtlv(elt, *buflen, opts))
154 return NULL;
155
156 COV_TAINTED_DATA_ARG(elt);
157
158 GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST()
159 return (bcm_xtlv_t *)(elt);
160 GCC_DIAGNOSTIC_POP()
161 }
162
163 int
bcm_xtlv_buf_init(bcm_xtlvbuf_t * tlv_buf,uint8 * buf,uint16 len,bcm_xtlv_opts_t opts)164 bcm_xtlv_buf_init(bcm_xtlvbuf_t *tlv_buf, uint8 *buf, uint16 len, bcm_xtlv_opts_t opts)
165 {
166 if (!tlv_buf || !buf || !len)
167 return BCME_BADARG;
168
169 tlv_buf->opts = opts;
170 tlv_buf->size = len;
171 tlv_buf->head = buf;
172 tlv_buf->buf = buf;
173 return BCME_OK;
174 }
175
176 uint16
bcm_xtlv_buf_len(bcm_xtlvbuf_t * tbuf)177 bcm_xtlv_buf_len(bcm_xtlvbuf_t *tbuf)
178 {
179 uint16 len;
180
181 if (tbuf)
182 len = (uint16)(tbuf->buf - tbuf->head);
183 else
184 len = 0;
185
186 return len;
187 }
188
189 uint16
bcm_xtlv_buf_rlen(bcm_xtlvbuf_t * tbuf)190 bcm_xtlv_buf_rlen(bcm_xtlvbuf_t *tbuf)
191 {
192 uint16 rlen;
193 if (tbuf)
194 rlen = tbuf->size - bcm_xtlv_buf_len(tbuf);
195 else
196 rlen = 0;
197
198 return rlen;
199 }
200
201 uint8 *
bcm_xtlv_buf(bcm_xtlvbuf_t * tbuf)202 bcm_xtlv_buf(bcm_xtlvbuf_t *tbuf)
203 {
204 return tbuf ? tbuf->buf : NULL;
205 }
206
207 uint8 *
bcm_xtlv_head(bcm_xtlvbuf_t * tbuf)208 bcm_xtlv_head(bcm_xtlvbuf_t *tbuf)
209 {
210 return tbuf ? tbuf->head : NULL;
211 }
212
213 void
BCMPOSTTRAPFN(bcm_xtlv_pack_xtlv)214 BCMPOSTTRAPFN(bcm_xtlv_pack_xtlv)(bcm_xtlv_t *xtlv, uint16 type, uint16 len, const uint8 *data,
215 bcm_xtlv_opts_t opts)
216 {
217 uint8 *data_buf;
218 bcm_xtlv_opts_t mask = BCM_XTLV_OPTION_IDU8 | BCM_XTLV_OPTION_LENU8;
219
220 if (!(opts & mask)) { /* default */
221 uint8 *idp = (uint8 *)xtlv;
222 uint8 *lenp = idp + sizeof(xtlv->id);
223 htol16_ua_store(type, idp);
224 htol16_ua_store(len, lenp);
225 data_buf = lenp + sizeof(uint16);
226 } else if ((opts & mask) == mask) { /* u8 id and u8 len */
227 uint8 *idp = (uint8 *)xtlv;
228 uint8 *lenp = idp + 1;
229 *idp = (uint8)type;
230 *lenp = (uint8)len;
231 data_buf = lenp + sizeof(uint8);
232 } else if (opts & BCM_XTLV_OPTION_IDU8) { /* u8 id, u16 len */
233 uint8 *idp = (uint8 *)xtlv;
234 uint8 *lenp = idp + 1;
235 *idp = (uint8)type;
236 htol16_ua_store(len, lenp);
237 data_buf = lenp + sizeof(uint16);
238 } else if (opts & BCM_XTLV_OPTION_LENU8) { /* u16 id, u8 len */
239 uint8 *idp = (uint8 *)xtlv;
240 uint8 *lenp = idp + sizeof(uint16);
241 htol16_ua_store(type, idp);
242 *lenp = (uint8)len;
243 data_buf = lenp + sizeof(uint8);
244 } else {
245 ASSERT(!"Unexpected xtlv option");
246 return;
247 }
248
249 if (opts & BCM_XTLV_OPTION_LENU8) {
250 ASSERT(len <= 0x00ff);
251 len &= 0xff;
252 }
253
254 if (data != NULL) {
255 memcpy(data_buf, data, len);
256 }
257 }
258
259 /* xtlv header is always packed in LE order */
260 void
bcm_xtlv_unpack_xtlv(const bcm_xtlv_t * xtlv,uint16 * type,uint16 * len,const uint8 ** data,bcm_xtlv_opts_t opts)261 bcm_xtlv_unpack_xtlv(const bcm_xtlv_t *xtlv, uint16 *type, uint16 *len,
262 const uint8 **data, bcm_xtlv_opts_t opts)
263 {
264 if (type)
265 *type = (uint16)bcm_xtlv_id(xtlv, opts);
266 if (len)
267 *len = (uint16)bcm_xtlv_len(xtlv, opts);
268 if (data)
269 *data = (const uint8 *)xtlv + BCM_XTLV_HDR_SIZE_EX(opts);
270 }
271
272 int
bcm_xtlv_put_data(bcm_xtlvbuf_t * tbuf,uint16 type,const uint8 * data,int n)273 bcm_xtlv_put_data(bcm_xtlvbuf_t *tbuf, uint16 type, const uint8 *data, int n)
274 {
275 bcm_xtlv_t *xtlv;
276 int size;
277
278 if (tbuf == NULL)
279 return BCME_BADARG;
280
281 size = bcm_xtlv_size_for_data(n, tbuf->opts);
282 if (bcm_xtlv_buf_rlen(tbuf) < size)
283 return BCME_NOMEM;
284
285 xtlv = (bcm_xtlv_t *)bcm_xtlv_buf(tbuf);
286 bcm_xtlv_pack_xtlv(xtlv, type, (uint16)n, data, tbuf->opts);
287 tbuf->buf += size; /* note: data may be NULL, reserves space */
288 return BCME_OK;
289 }
290
291 static int
bcm_xtlv_put_int(bcm_xtlvbuf_t * tbuf,uint16 type,const uint8 * data,int n,int int_sz)292 bcm_xtlv_put_int(bcm_xtlvbuf_t *tbuf, uint16 type, const uint8 *data, int n, int int_sz)
293 {
294 bcm_xtlv_t *xtlv;
295 int xtlv_len;
296 uint8 *xtlv_data;
297 int err = BCME_OK;
298
299 if (tbuf == NULL) {
300 err = BCME_BADARG;
301 goto done;
302 }
303
304 xtlv = (bcm_xtlv_t *)bcm_xtlv_buf(tbuf);
305
306 /* put type and length in xtlv and reserve data space */
307 xtlv_len = n * int_sz;
308 err = bcm_xtlv_put_data(tbuf, type, NULL, xtlv_len);
309 if (err != BCME_OK)
310 goto done;
311
312 xtlv_data = (uint8 *)xtlv + bcm_xtlv_hdr_size(tbuf->opts);
313
314 /* write data w/ little-endianness into buffer - single loop, aligned access */
315 for (; n != 0; --n, xtlv_data += int_sz, data += int_sz) {
316 switch (int_sz) {
317 case sizeof(uint8):
318 break;
319 case sizeof(uint16):
320 {
321 uint16 v = load16_ua(data);
322 htol16_ua_store(v, xtlv_data);
323 break;
324 }
325 case sizeof(uint32):
326 {
327 uint32 v = load32_ua(data);
328 htol32_ua_store(v, xtlv_data);
329 break;
330 }
331 case sizeof(uint64):
332 {
333 uint64 v = load64_ua(data);
334 htol64_ua_store(v, xtlv_data);
335 break;
336 }
337 default:
338 err = BCME_UNSUPPORTED;
339 goto done;
340 }
341 }
342
343 done:
344 return err;
345 }
346
347 int
bcm_xtlv_put16(bcm_xtlvbuf_t * tbuf,uint16 type,const uint16 * data,int n)348 bcm_xtlv_put16(bcm_xtlvbuf_t *tbuf, uint16 type, const uint16 *data, int n)
349 {
350 return bcm_xtlv_put_int(tbuf, type, (const uint8 *)data, n, sizeof(uint16));
351 }
352
353 int
bcm_xtlv_put32(bcm_xtlvbuf_t * tbuf,uint16 type,const uint32 * data,int n)354 bcm_xtlv_put32(bcm_xtlvbuf_t *tbuf, uint16 type, const uint32 *data, int n)
355 {
356 return bcm_xtlv_put_int(tbuf, type, (const uint8 *)data, n, sizeof(uint32));
357 }
358
359 int
bcm_xtlv_put64(bcm_xtlvbuf_t * tbuf,uint16 type,const uint64 * data,int n)360 bcm_xtlv_put64(bcm_xtlvbuf_t *tbuf, uint16 type, const uint64 *data, int n)
361 {
362 return bcm_xtlv_put_int(tbuf, type, (const uint8 *)data, n, sizeof(uint64));
363 }
364
365 /*
366 * upacks xtlv record from buf checks the type
367 * copies data to callers buffer
368 * advances tlv pointer to next record
369 * caller's resposible for dst space check
370 */
371 int
bcm_unpack_xtlv_entry(const uint8 ** tlv_buf,uint16 xpct_type,uint16 xpct_len,uint8 * dst_data,bcm_xtlv_opts_t opts)372 bcm_unpack_xtlv_entry(const uint8 **tlv_buf, uint16 xpct_type, uint16 xpct_len,
373 uint8 *dst_data, bcm_xtlv_opts_t opts)
374 {
375 const bcm_xtlv_t *ptlv = (const bcm_xtlv_t *)*tlv_buf;
376 uint16 len;
377 uint16 type;
378 const uint8 *data;
379
380 ASSERT(ptlv);
381
382 bcm_xtlv_unpack_xtlv(ptlv, &type, &len, &data, opts);
383 if (len) {
384 if ((type != xpct_type) || (len > xpct_len))
385 return BCME_BADARG;
386 if (dst_data && data)
387 memcpy(dst_data, data, len); /* copy data to dst */
388 }
389
390 *tlv_buf += BCM_XTLV_SIZE_EX(ptlv, opts);
391 return BCME_OK;
392 }
393
394 /*
395 * packs user data into tlv record and advances tlv pointer to next xtlv slot
396 * buflen is used for tlv_buf space check
397 */
398 int
bcm_pack_xtlv_entry(uint8 ** tlv_buf,uint16 * buflen,uint16 type,uint16 len,const uint8 * src_data,bcm_xtlv_opts_t opts)399 bcm_pack_xtlv_entry(uint8 **tlv_buf, uint16 *buflen, uint16 type, uint16 len,
400 const uint8 *src_data, bcm_xtlv_opts_t opts)
401 {
402 bcm_xtlv_t *ptlv = (bcm_xtlv_t *)*tlv_buf;
403 int size;
404
405 ASSERT(ptlv);
406
407 size = bcm_xtlv_size_for_data(len, opts);
408
409 /* copy data from tlv buffer to dst provided by user */
410 if (size > *buflen) {
411 return BCME_BADLEN;
412 }
413
414 bcm_xtlv_pack_xtlv(ptlv, type, len, src_data, opts);
415
416 /* advance callers pointer to tlv buff */
417 *tlv_buf = (uint8*)(*tlv_buf) + size;
418 /* decrement the len */
419 *buflen -= (uint16)size;
420 return BCME_OK;
421 }
422
423 /*
424 * unpack all xtlv records from the issue a callback
425 * to set function one call per found tlv record
426 */
427 int
bcm_unpack_xtlv_buf(void * ctx,const uint8 * tlv_buf,uint16 buflen,bcm_xtlv_opts_t opts,bcm_xtlv_unpack_cbfn_t * cbfn)428 bcm_unpack_xtlv_buf(void *ctx, const uint8 *tlv_buf, uint16 buflen, bcm_xtlv_opts_t opts,
429 bcm_xtlv_unpack_cbfn_t *cbfn)
430 {
431 uint16 len;
432 uint16 type;
433 int res = BCME_OK;
434 int size;
435 const bcm_xtlv_t *ptlv;
436 int sbuflen = buflen;
437 const uint8 *data;
438 int hdr_size;
439
440 ASSERT(!buflen || tlv_buf);
441 ASSERT(!buflen || cbfn);
442
443 hdr_size = BCM_XTLV_HDR_SIZE_EX(opts);
444 while (sbuflen >= hdr_size) {
445 ptlv = (const bcm_xtlv_t *)tlv_buf;
446
447 bcm_xtlv_unpack_xtlv(ptlv, &type, &len, &data, opts);
448 size = bcm_xtlv_size_for_data(len, opts);
449
450 sbuflen -= size;
451
452 /* check for buffer overrun */
453 if (sbuflen < 0) {
454 break;
455 }
456
457 if ((res = cbfn(ctx, data, type, len)) != BCME_OK) {
458 break;
459 }
460 tlv_buf += size;
461 }
462 return res;
463 }
464
465 int
bcm_pack_xtlv_buf(void * ctx,uint8 * tlv_buf,uint16 buflen,bcm_xtlv_opts_t opts,bcm_pack_xtlv_next_info_cbfn_t get_next,bcm_pack_xtlv_pack_next_cbfn_t pack_next,int * outlen)466 bcm_pack_xtlv_buf(void *ctx, uint8 *tlv_buf, uint16 buflen, bcm_xtlv_opts_t opts,
467 bcm_pack_xtlv_next_info_cbfn_t get_next, bcm_pack_xtlv_pack_next_cbfn_t pack_next,
468 int *outlen)
469 {
470 int res = BCME_OK;
471 uint16 tlv_id;
472 uint16 tlv_len;
473 uint8 *startp;
474 uint8 *endp;
475 uint8 *buf;
476 bool more;
477 int size;
478 int hdr_size;
479
480 ASSERT(get_next && pack_next);
481
482 buf = tlv_buf;
483 startp = buf;
484 endp = (uint8 *)buf + buflen;
485 more = TRUE;
486 hdr_size = BCM_XTLV_HDR_SIZE_EX(opts);
487
488 while (more && (buf < endp)) {
489 more = get_next(ctx, &tlv_id, &tlv_len);
490 size = bcm_xtlv_size_for_data(tlv_len, opts);
491 if ((buf + size) > endp) {
492 res = BCME_BUFTOOSHORT;
493 goto done;
494 }
495
496 bcm_xtlv_pack_xtlv((bcm_xtlv_t *)buf, tlv_id, tlv_len, NULL, opts);
497 pack_next(ctx, tlv_id, tlv_len, buf + hdr_size);
498 buf += size;
499 }
500
501 if (more)
502 res = BCME_BUFTOOSHORT;
503
504 done:
505 if (outlen) {
506 *outlen = (int)(buf - startp);
507 }
508 return res;
509 }
510
511 /*
512 * pack xtlv buffer from memory according to xtlv_desc_t
513 */
514 int
bcm_pack_xtlv_buf_from_mem(uint8 ** tlv_buf,uint16 * buflen,const xtlv_desc_t * items,bcm_xtlv_opts_t opts)515 bcm_pack_xtlv_buf_from_mem(uint8 **tlv_buf, uint16 *buflen, const xtlv_desc_t *items,
516 bcm_xtlv_opts_t opts)
517 {
518 int res = BCME_OK;
519 uint8 *ptlv = *tlv_buf;
520
521 while (items->type != 0) {
522 if (items->len && items->ptr) {
523 res = bcm_pack_xtlv_entry(&ptlv, buflen, items->type,
524 items->len, items->ptr, opts);
525 if (res != BCME_OK)
526 break;
527 }
528 items++;
529 }
530
531 *tlv_buf = ptlv; /* update the external pointer */
532 return res;
533 }
534
535 /*
536 * unpack xtlv buffer to memory according to xtlv_desc_t
537 *
538 */
539 int
bcm_unpack_xtlv_buf_to_mem(const uint8 * tlv_buf,int * buflen,xtlv_desc_t * items,bcm_xtlv_opts_t opts)540 bcm_unpack_xtlv_buf_to_mem(const uint8 *tlv_buf, int *buflen, xtlv_desc_t *items,
541 bcm_xtlv_opts_t opts)
542 {
543 int res = BCME_OK;
544 const bcm_xtlv_t *elt;
545
546 elt = bcm_valid_xtlv((const bcm_xtlv_t *)tlv_buf, *buflen, opts) ?
547 (const bcm_xtlv_t *)tlv_buf : NULL;
548 if (!elt || !items) {
549 res = BCME_BADARG;
550 return res;
551 }
552
553 for (; elt != NULL && res == BCME_OK; elt = bcm_next_xtlv(elt, buflen, opts)) {
554 /* find matches in desc_t items */
555 xtlv_desc_t *dst_desc = items;
556 uint16 len, type;
557 const uint8 *data;
558
559 bcm_xtlv_unpack_xtlv(elt, &type, &len, &data, opts);
560 while (dst_desc->type != 0) {
561 if (type == dst_desc->type) {
562 if (len != dst_desc->len) {
563 res = BCME_BADLEN;
564 } else {
565 memcpy(dst_desc->ptr, data, len);
566 }
567 break;
568 }
569 dst_desc++;
570 }
571 }
572
573 if (res == BCME_OK && *buflen != 0) /* this does not look right */
574 res = BCME_BUFTOOSHORT;
575
576 return res;
577 }
578
579 /*
580 * return data pointer of a given ID from xtlv buffer.
581 * If the specified xTLV ID is found, on return *datalen will contain
582 * the the data length of the xTLV ID.
583 */
584 const uint8*
bcm_get_data_from_xtlv_buf(const uint8 * tlv_buf,uint16 buflen,uint16 id,uint16 * datalen,bcm_xtlv_opts_t opts)585 bcm_get_data_from_xtlv_buf(const uint8 *tlv_buf, uint16 buflen, uint16 id,
586 uint16 *datalen, bcm_xtlv_opts_t opts)
587 {
588 const uint8 *retptr = NULL;
589 uint16 type, len;
590 int size;
591 const bcm_xtlv_t *ptlv;
592 int sbuflen = buflen;
593 const uint8 *data;
594 int hdr_size;
595
596 COV_TAINTED_DATA_SINK(buflen);
597 COV_NEG_SINK(buflen);
598
599 hdr_size = BCM_XTLV_HDR_SIZE_EX(opts);
600
601 /* Init the datalength */
602 if (datalen) {
603 *datalen = 0;
604 }
605 while (sbuflen >= hdr_size) {
606 ptlv = (const bcm_xtlv_t *)tlv_buf;
607 bcm_xtlv_unpack_xtlv(ptlv, &type, &len, &data, opts);
608
609 size = bcm_xtlv_size_for_data(len, opts);
610 sbuflen -= size;
611 if (sbuflen < 0) /* buffer overrun? */
612 break;
613
614 if (id == type) {
615 retptr = data;
616 if (datalen)
617 *datalen = len;
618 break;
619 }
620
621 tlv_buf += size;
622 }
623
624 COV_TAINTED_DATA_ARG(retptr);
625
626 return retptr;
627 }
628
629 bcm_xtlv_t*
bcm_xtlv_bcopy(const bcm_xtlv_t * src,bcm_xtlv_t * dst,int src_buf_len,int dst_buf_len,bcm_xtlv_opts_t opts)630 bcm_xtlv_bcopy(const bcm_xtlv_t *src, bcm_xtlv_t *dst,
631 int src_buf_len, int dst_buf_len, bcm_xtlv_opts_t opts)
632 {
633 bcm_xtlv_t *dst_next = NULL;
634 src = (src && bcm_valid_xtlv(src, src_buf_len, opts)) ? src : NULL;
635 if (src && dst) {
636 uint16 type;
637 uint16 len;
638 const uint8 *data;
639 int size;
640 bcm_xtlv_unpack_xtlv(src, &type, &len, &data, opts);
641 size = bcm_xtlv_size_for_data(len, opts);
642 if (size <= dst_buf_len) {
643 bcm_xtlv_pack_xtlv(dst, type, len, data, opts);
644 dst_next = (bcm_xtlv_t *)((uint8 *)dst + size);
645 }
646 }
647
648 return dst_next;
649 }
650