Coverage Report

Created: 2026-08-14 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/protocols/dhcpv6/decode.c
Line
Count
Source
1
/*
2
 *   This library is free software; you can redistribute it and/or
3
 *   modify it under the terms of the GNU Lesser General Public
4
 *   License as published by the Free Software Foundation; either
5
 *   version 2.1 of the License, or (at your option) any later version.
6
 *
7
 *   This library is distributed in the hope that it will be useful,
8
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10
 *   Lesser General Public License for more details.
11
 *
12
 *   You should have received a copy of the GNU Lesser General Public
13
 *   License along with this library; if not, write to the Free Software
14
 *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15
 */
16
17
/**
18
 * $Id: 85e1cdf59923173579798d0f0ce5826c3386c2d8 $
19
 *
20
 * @file protocols/dhcpv6/decode.c
21
 * @brief Functions to decode DHCP options.
22
 *
23
 * @author Arran Cudbard-Bell (a.cudbardb@freeradius.org)
24
 *
25
 * @copyright 2018 The FreeRADIUS server project
26
 * @copyright 2018 NetworkRADIUS SARL (legal@networkradius.com)
27
 */
28
#include <stdint.h>
29
#include <stddef.h>
30
31
#include <freeradius-devel/io/test_point.h>
32
#include <freeradius-devel/util/dns.h>
33
#include <freeradius-devel/util/proto.h>
34
#include <freeradius-devel/util/struct.h>
35
36
#include "dhcpv6.h"
37
#include "attrs.h"
38
39
/*
40
 *    RFC 8415 Section 21.7 defines option codes that MUST NOT appear
41
 *    in an Option Request option.  We convert the attributes to a
42
 *    64-bit number which can be easily checked.
43
 */
44
#define DHCPV6_ORO_FORBIDDEN          \
45
99.4k
  ((1ULL << 1)  |  /* OPTION_CLIENTID */      \
46
99.4k
   (1ULL << 2)  |  /* OPTION_SERVERID */      \
47
99.4k
   (1ULL << 3)  |  /* OPTION_IA_NA */     \
48
99.4k
   (1ULL << 4)  |  /* OPTION_IA_TA */     \
49
99.4k
   (1ULL << 5)  |  /* OPTION_IAADDR */      \
50
99.4k
   (1ULL << 6)  |  /* OPTION_ORO */     \
51
99.4k
   (1ULL << 7)  |  /* OPTION_PREFERENCE */    \
52
99.4k
   (1ULL << 8)  |  /* OPTION_ELAPSED_TIME */  \
53
99.4k
   (1ULL << 9)  |  /* OPTION_RELAY_MSG */     \
54
99.4k
   (1ULL << 11) |  /* OPTION_AUTH */      \
55
99.4k
   (1ULL << 12) |  /* OPTION_UNICAST */     \
56
99.4k
   (1ULL << 13) |  /* OPTION_STATUS_CODE */   \
57
99.4k
   (1ULL << 14) |  /* OPTION_RAPID_COMMIT */  \
58
99.4k
   (1ULL << 15) |  /* OPTION_USER_CLASS */    \
59
99.4k
   (1ULL << 16) |  /* OPTION_VENDOR_CLASS */  \
60
99.4k
   (1ULL << 18) |  /* OPTION_INTERFACE_ID */  \
61
99.4k
   (1ULL << 19) |  /* OPTION_RECONF_MSG */    \
62
99.4k
   (1ULL << 20) |  /* OPTION_RECONF_ACCEPT */ \
63
99.4k
   (1ULL << 25) |  /* OPTION_IA_PD */     \
64
99.4k
   (1ULL << 26))    /* OPTION_IAPREFIX */
65
66
static ssize_t decode_option(TALLOC_CTX *ctx, fr_pair_list_t *out,
67
           fr_dict_attr_t const *parent,
68
           uint8_t const *data, size_t const data_len, void *decode_ctx);
69
70
static ssize_t decode_tlv_trampoline(TALLOC_CTX *ctx, fr_pair_list_t *out,
71
             fr_dict_attr_t const *parent,
72
             uint8_t const *data, size_t const data_len, void *decode_ctx)
73
1.92k
{
74
1.92k
  return fr_pair_tlvs_from_network(ctx, out, parent, data, data_len, decode_ctx, decode_option, NULL, true);
75
1.92k
}
76
77
static ssize_t decode_value(TALLOC_CTX *ctx, fr_pair_list_t *out,
78
          fr_dict_attr_t const *parent,
79
          uint8_t const *data, size_t const data_len, void *decode_ctx);
80
81
/** Handle arrays of DNS labels for fr_struct_from_network()
82
 *
83
 */
84
static ssize_t decode_value_trampoline(TALLOC_CTX *ctx, fr_pair_list_t *out,
85
               fr_dict_attr_t const *parent,
86
               uint8_t const *data, size_t const data_len, void *decode_ctx)
87
125k
{
88
125k
  if ((parent->type == FR_TYPE_STRING) && fr_dhcpv6_flag_any_dns_label(parent)) {
89
26.9k
    return fr_pair_dns_labels_from_network(ctx, out, parent, data, data, data_len, NULL, false);
90
26.9k
  }
91
92
98.4k
  return decode_value(ctx, out, parent, data, data_len, decode_ctx);
93
125k
}
94
95
96
static ssize_t decode_value(TALLOC_CTX *ctx, fr_pair_list_t *out,
97
          fr_dict_attr_t const *parent,
98
          uint8_t const *data, size_t const data_len, void *decode_ctx)
99
549k
{
100
549k
  ssize_t     slen;
101
549k
  fr_pair_t   *vp = NULL;
102
549k
  fr_dict_attr_t const  *ref;
103
104
549k
  FR_PROTO_HEX_DUMP(data, data_len, "decode_value");
105
106
549k
  switch (parent->type) {
107
338k
  case FR_TYPE_ATTR:
108
    /*
109
     *  Force the length of the data to be two,
110
     *  otherwise the "from network" call complains.
111
     *  Because we pass in the enumv as the _parent_
112
     *  and not the da.  The da is marked as "array",
113
     *  but the parent is not.
114
     */
115
338k
    if (data_len < 2) goto raw;
116
117
    /*
118
     *  Run-time checks rather than assertions.
119
     */
120
338k
    if (!parent->parent->flags.is_root) return -1;
121
122
338k
    vp = fr_pair_afrom_da(ctx, parent);
123
338k
    if (!vp) return PAIR_DECODE_OOM;
124
338k
    PAIR_ALLOCED(vp);
125
126
338k
    slen = fr_value_box_from_network(vp, &vp->data, vp->vp_type, parent->parent,
127
338k
             &FR_DBUFF_TMP(data, 2), 2, true);
128
338k
    if (slen <= 0) {
129
0
      TALLOC_FREE(vp);
130
0
      goto raw;
131
0
    }
132
133
338k
    vp->vp_tainted = true;
134
338k
    fr_pair_append(out, vp);
135
338k
    return 2;
136
137
  /*
138
   *  Address MAY be shorter than 16 bytes.
139
   */
140
2.72k
  case FR_TYPE_IPV6_PREFIX:
141
2.72k
    if (data_len == 0) {
142
30.2k
    raw:
143
30.2k
      return fr_pair_raw_from_network(ctx, out, parent, data, data_len);
144
145
465
    }
146
147
2.25k
    vp = fr_pair_afrom_da(ctx, parent);
148
2.25k
    if (!vp) return PAIR_DECODE_OOM;
149
2.25k
    PAIR_ALLOCED(vp);
150
151
2.25k
    slen = fr_value_box_ipaddr_from_network(&vp->data, parent->type, parent,
152
2.25k
              data[0], data + 1, data_len - 1,
153
2.25k
              (parent->parent->type == FR_TYPE_STRUCT), true);
154
2.25k
    if (slen < 0) goto raw_free;
155
156
1.07k
    slen++;   /* account for the prefix */
157
1.07k
    break;
158
159
  /*
160
   *  A bool is encoded as an empty option if it's
161
   *  true.  A bool is omitted entirely if it's
162
   *  false.
163
   */
164
623
  case FR_TYPE_BOOL:
165
623
    if (data_len != 0) goto raw;
166
301
    vp = fr_pair_afrom_da(ctx, parent);
167
301
    if (!vp) return PAIR_DECODE_OOM;
168
301
    PAIR_ALLOCED(vp);
169
170
301
    vp->vp_bool = true;
171
301
    slen = 0;
172
301
    break;
173
174
  /*
175
   *  A standard 32bit integer, but unlike normal UNIX timestamps
176
   *  starts from the 1st of January 2000.
177
   *
178
   *  In the encoder we subtract 30 years to any values, so
179
   *  here we need to add that to the time here.
180
   */
181
1.62k
  case FR_TYPE_DATE:
182
1.62k
    vp = fr_pair_afrom_da(ctx, parent);
183
1.62k
    if (!vp) return PAIR_DECODE_OOM;
184
1.62k
    PAIR_ALLOCED(vp);
185
186
1.62k
    slen = fr_value_box_from_network(vp, &vp->data, vp->vp_type, vp->da,
187
1.62k
             &FR_DBUFF_TMP(data, data_len), data_len, true);
188
1.62k
    if (slen < 0) {
189
1.18k
      talloc_free(vp);
190
1.18k
      goto raw;
191
1.18k
    }
192
442
    vp->vp_date = fr_unix_time_add(vp->vp_date, fr_time_delta_from_sec(DHCPV6_DATE_OFFSET));
193
442
    break;
194
195
14.9k
  case FR_TYPE_STRUCT:
196
14.9k
    slen = fr_struct_from_network(ctx, out, parent, data, data_len,
197
14.9k
                decode_ctx, decode_value_trampoline, decode_tlv_trampoline);
198
14.9k
    if (slen < 0) goto raw;
199
200
8.77k
    if (parent->flags.array) return slen;
201
8.77k
    return data_len;
202
203
33.0k
  case FR_TYPE_GROUP:
204
33.0k
    vp = fr_pair_afrom_da(ctx, parent);
205
33.0k
    if (!vp) return PAIR_DECODE_OOM;
206
33.0k
    PAIR_ALLOCED(vp);
207
208
33.0k
    ref = fr_dict_attr_ref(parent);
209
33.0k
    if (ref && (ref->dict != dict_dhcpv6)) {
210
28.8k
      fr_dict_protocol_t const *proto;
211
212
28.8k
      proto = fr_dict_protocol(ref->dict);
213
28.8k
      fr_assert(proto != NULL);
214
215
28.8k
      if (!proto->decode) {
216
22.1k
      raw_free:
217
22.1k
        talloc_free(vp);
218
22.1k
        goto raw;
219
0
      }
220
221
28.8k
      slen = proto->decode(vp, &vp->vp_group, data, data_len);
222
28.8k
      if (slen < 0) goto raw_free;
223
224
18.4k
      vp->vp_tainted = true;
225
226
18.4k
    } else {
227
4.22k
      if (!ref) ref = fr_dict_root(dict_dhcpv6);
228
229
      /*
230
       *  Child VPs go into the child group, not in the main parent list.  BUT, we start
231
       *  decoding attributes from the ref, and not from the group parent.
232
       */
233
4.22k
      slen = fr_pair_tlvs_from_network(vp, &vp->vp_group, ref, data, data_len, decode_ctx, decode_option, NULL, false);
234
4.22k
      if (slen < 0) goto raw_free;
235
4.22k
    }
236
237
19.5k
    fr_pair_append(out, vp);
238
19.5k
    return data_len;
239
240
10.0k
  case FR_TYPE_IPV6_ADDR:
241
10.0k
    vp = fr_pair_afrom_da(ctx, parent);
242
10.0k
    if (!vp) return PAIR_DECODE_OOM;
243
10.0k
    PAIR_ALLOCED(vp);
244
245
10.0k
    slen = fr_value_box_ipaddr_from_network(&vp->data, parent->type, parent,
246
10.0k
              128, data, data_len,
247
10.0k
              true, true);
248
10.0k
    if (slen < 0) goto raw_free;
249
8.84k
    break;
250
251
148k
  default:
252
148k
    vp = fr_pair_afrom_da(ctx, parent);
253
148k
    if (!vp) return PAIR_DECODE_OOM;
254
148k
    PAIR_ALLOCED(vp);
255
256
148k
    slen = fr_value_box_from_network(vp, &vp->data, vp->vp_type, vp->da,
257
148k
             &FR_DBUFF_TMP(data, data_len), data_len, true);
258
148k
    if (slen < 0) goto raw_free;
259
141k
    break;
260
549k
  }
261
262
  /*
263
   *  The input is larger than the decoded value, re-do it as a raw attribute.
264
   */
265
152k
  if (!parent->flags.array && ((size_t) slen < data_len)) {
266
0
    talloc_free(vp);
267
0
    goto raw;
268
0
  }
269
270
152k
  fr_assert(vp != NULL);
271
272
152k
  vp->vp_tainted = true;
273
152k
  fr_pair_append(out, vp);
274
275
152k
  if (parent->flags.array) return slen;
276
277
33.4k
  return data_len;
278
152k
}
279
280
281
static ssize_t decode_option(TALLOC_CTX *ctx, fr_pair_list_t *out,
282
            fr_dict_attr_t const *parent,
283
            uint8_t const *data, size_t const data_len, void *decode_ctx)
284
89.8k
{
285
89.8k
  unsigned int      option;
286
89.8k
  size_t      len;
287
89.8k
  ssize_t     slen;
288
89.8k
  fr_dict_attr_t const  *da;
289
89.8k
  fr_dhcpv6_decode_ctx_t  *packet_ctx = decode_ctx;
290
291
#ifdef STATIC_ANALYZER
292
  if (!packet_ctx || !packet_ctx->tmp_ctx) return PAIR_DECODE_FATAL_ERROR;
293
#endif
294
295
  /*
296
   *  Must have at least an option header.
297
   */
298
89.8k
  if (data_len < 4) {
299
2.63k
    fr_strerror_printf("%s: Insufficient data", __FUNCTION__);
300
2.63k
    return -(data_len);
301
2.63k
  }
302
303
87.1k
  option = DHCPV6_GET_OPTION_NUM(data);
304
87.1k
  len = DHCPV6_GET_OPTION_LEN(data);
305
87.1k
  if (len > (data_len - 4)) {
306
4.23k
    fr_strerror_printf("%s: Option overflows input.  "
307
4.23k
           "Optional length must be less than %zu bytes, got %zu bytes",
308
4.23k
           __FUNCTION__, data_len - 4, len);
309
4.23k
    return PAIR_DECODE_FATAL_ERROR;
310
4.23k
  }
311
312
82.9k
  da = fr_dict_attr_child_by_num(parent, option);
313
82.9k
  if (!da) {
314
15.0k
    da = fr_dict_attr_unknown_raw_afrom_num(packet_ctx->tmp_ctx, parent, option);
315
15.0k
    if (!da) return PAIR_DECODE_FATAL_ERROR;
316
15.0k
  }
317
82.9k
  FR_PROTO_TRACE("decode context changed %s -> %s",da->parent->name, da->name);
318
319
  /*
320
   *  Relay messages are weird, and contain complete DHCPv6
321
   *  packets, copied verbatim from the DHCPv6 client.
322
   */
323
82.9k
  if (da == attr_relay_message) {
324
5.46k
    fr_pair_t *vp;
325
326
5.46k
    vp = fr_pair_afrom_da(ctx, attr_relay_message);
327
5.46k
    if (!vp) return PAIR_DECODE_FATAL_ERROR;
328
5.46k
    PAIR_ALLOCED(vp);
329
330
    /*
331
     *  Check if the relayed packet is OK.  If not, it's an error.
332
     */
333
5.46k
    if (!fr_dhcpv6_ok(data + 4, len, 200)) return -1;
334
335
2.66k
    slen = fr_dhcpv6_decode(vp, &vp->vp_group, data + 4, len);
336
2.66k
    if (slen < 0) {
337
1.39k
      talloc_free(vp);
338
3.97k
    raw:
339
3.97k
      slen = fr_pair_raw_from_network(ctx, out, da, data + 4, len);
340
3.97k
      if (slen < 0) return slen;
341
3.97k
      return 4 + slen;
342
3.97k
    }
343
344
1.27k
    fr_pair_append(out, vp);
345
346
77.4k
  } else if ((da->type == FR_TYPE_STRING) && fr_dhcpv6_flag_any_dns_label(da)) {
347
3.81k
    slen = fr_pair_dns_labels_from_network(ctx, out, da, data + 4, data + 4, len, NULL, true);
348
3.81k
    if (slen < 0) return slen;
349
350
73.6k
  } else if (da->flags.array) {
351
2.65k
    if (da == attr_option_request) {
352
907
      uint8_t const *p;
353
354
907
      if ((len < 2) || ((len & 0x01) != 0)) {
355
412
        fr_strerror_const("Invalid Option-Request, length is not a multiple of 2");
356
412
        return -1;
357
412
      }
358
359
      /*
360
       *  Check for the forbidden options.
361
       */
362
338k
      for (p = data + 4; p < (data + 4 + len); p += 2) {
363
338k
        if (*p) continue;
364
365
99.4k
        if (*p > 26) continue;
366
367
99.4k
        if ((1 << *p) & DHCPV6_ORO_FORBIDDEN) {
368
0
          fr_strerror_printf("Option %u is forbidden inside of Option-Request", *p);
369
0
          return -1;
370
0
        }
371
99.4k
      }
372
495
    }
373
374
2.24k
    slen = fr_pair_array_from_network(ctx, out, da, data + 4, len, decode_ctx, decode_value);
375
376
71.0k
  } else if (da->type == FR_TYPE_VSA) {
377
3.69k
    bool      append_vsa = false;
378
3.69k
    bool      append_vp = false;
379
3.69k
    uint32_t    pen;
380
3.69k
    fr_dict_attr_t const  *vendor;
381
3.69k
    fr_pair_t   *vsa, *vp;
382
3.69k
    fr_pair_list_t    list;
383
384
    /*
385
     *  Insufficient room for a 4-octet enterprise
386
     *  code, plus at least one option header.
387
     */
388
3.69k
    if (len < 8) goto raw;
389
390
3.27k
    pen = fr_nbo_to_uint32(data + 4);
391
392
    /*
393
     *  Verify that the VSA contains a vendor.
394
     *
395
     *  If it doesn't then this vendor is unknown, but we know
396
     *  vendor attributes have a standard format, so we can
397
     *  decode the data anyway.
398
     */
399
3.27k
    vendor = fr_dict_attr_child_by_num(da, pen);
400
3.27k
    if (!vendor) {
401
1.57k
      fr_dict_attr_t *n;
402
403
1.57k
      n = fr_dict_attr_unknown_vendor_afrom_num(packet_ctx->tmp_ctx, da, pen);
404
1.57k
      if (!n) return PAIR_DECODE_OOM;
405
1.57k
      vendor = n;
406
1.57k
    }
407
408
3.27k
    vsa = fr_pair_find_by_da(out, NULL, da);
409
3.27k
    if (!vsa) {
410
1.36k
      vsa = fr_pair_afrom_da(ctx, da);
411
1.36k
      if (!vsa) return PAIR_DECODE_FATAL_ERROR;
412
1.36k
      PAIR_ALLOCED(vsa);
413
414
1.36k
      append_vsa = true;
415
1.36k
    }
416
417
3.27k
    vp = NULL;
418
3.27k
    if (!append_vsa) vp = fr_pair_find_by_da(&vsa->vp_group, NULL, vendor);
419
420
3.27k
    if (!vp) {
421
2.30k
      vp = fr_pair_afrom_da(vsa, vendor);
422
2.30k
      if (!vp) return PAIR_DECODE_FATAL_ERROR;
423
2.30k
      PAIR_ALLOCED(vp);
424
425
2.30k
      append_vp = true;
426
2.30k
    }
427
428
3.27k
    fr_pair_list_init(&list);
429
3.27k
    slen = fr_pair_tlvs_from_network(vp, &vp->vp_group, vendor, data + 8, len - 4,
430
3.27k
             decode_ctx, decode_option, NULL, false);
431
432
3.27k
    if (slen < 0) {
433
1.80k
      fr_pair_list_free(&list);
434
1.80k
      if (append_vp) talloc_free(vp);
435
1.80k
      if (append_vsa) talloc_free(vsa);
436
1.80k
      goto raw;
437
1.80k
    }
438
439
    /*
440
     *  The child list contains vendors, and we have to merge the vendors 
441
     */
442
1.47k
    fr_pair_list_append(&vp->vp_group, &list);
443
1.47k
    if (append_vp) fr_pair_append(&vsa->vp_group, vp);
444
1.47k
    if (append_vsa) fr_pair_append(out, vsa);
445
446
67.3k
  } else if (da->type == FR_TYPE_TLV) {
447
833
    slen = fr_pair_tlvs_from_network(ctx, out, da, data + 4, len, decode_ctx, decode_option, NULL, true);
448
449
66.4k
  } else if (da == attr_auth) {
450
    /*
451
     *  See RFC 8415 Section 18 (option format), and Section 20 (functionality).
452
     *
453
     *  The Auth option can be sent from a server to client in a Reply message, and contains a
454
     *  128-bit secret key that is recorded somewhere on the server side.
455
     *
456
     *  The Auth option can be sent from a server to a client in a Reconfigure message, and
457
     *  contains an HMAC-MD5 of the packet and the secret key.
458
     *
459
     *  We are not currently a DHCPv6 client, so we do not support the Auth option, or
460
     *  Reconfigure messages.  See verify_to_client() in base.c.
461
     */
462
244
    fr_strerror_const("The 'Auth' option is not supported");
463
244
    return -1;
464
465
66.2k
  } else {
466
66.2k
    slen = decode_value(ctx, out, da, data + 4, len, decode_ctx);
467
66.2k
  }
468
469
75.8k
  if (slen < 0) goto raw;
470
471
75.5k
  return len + 4;
472
75.8k
}
473
474
475
/** Create a "normal" fr_pair_t from the given data
476
 *
477
 *    0                   1                   2                   3
478
 *    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
479
 *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
480
 *   |          option-code          |           option-len          |
481
 *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
482
 */
483
ssize_t fr_dhcpv6_decode_option(TALLOC_CTX *ctx, fr_pair_list_t *out,
484
        uint8_t const *data, size_t data_len, void *decode_ctx)
485
72.0k
{
486
72.0k
  FR_PROTO_HEX_DUMP(data, data_len, "fr_dhcpv6_decode_pair");
487
488
  /*
489
   *  The API changes, so we just bounce directly to the
490
   *  decode_option() function.
491
   *
492
   *  All options including VSAs in DHCPv6 MUST follow the
493
   *  standard format.
494
   */
495
72.0k
  return decode_option(ctx, out, fr_dict_root(dict_dhcpv6), data, data_len, decode_ctx);
496
72.0k
}
497
498
ssize_t fr_dhcpv6_decode_foreign(TALLOC_CTX *ctx, fr_pair_list_t *out,
499
         uint8_t const *data, size_t data_len)
500
4.52k
{
501
4.52k
  ssize_t slen;
502
4.52k
  uint8_t const *attr, *end;
503
504
4.52k
  fr_dhcpv6_decode_ctx_t decode_ctx = {};
505
506
4.52k
  fr_assert(dict_dhcpv6 != NULL);
507
508
4.52k
  decode_ctx.tmp_ctx = talloc(ctx, uint8_t);
509
510
4.52k
  attr = data;
511
4.52k
  end = data + data_len;
512
513
19.4k
  while (attr < end) {
514
18.3k
    slen = fr_dhcpv6_decode_option(ctx, out, attr, (end - attr), &decode_ctx);
515
18.3k
    if (slen < 0) {
516
3.46k
      talloc_free(decode_ctx.tmp_ctx);
517
3.46k
      return slen;
518
3.46k
    }
519
520
    /*
521
     *  If slen is larger than the room in the packet,
522
     *  all kinds of bad things happen.
523
     */
524
14.9k
     if (!fr_cond_assert(slen <= (end - attr))) {
525
0
      talloc_free(decode_ctx.tmp_ctx);
526
0
       return -slen - (attr - data);
527
0
     }
528
529
14.9k
    attr += slen;
530
14.9k
    talloc_free_children(decode_ctx.tmp_ctx);
531
14.9k
  }
532
533
4.52k
  talloc_free(decode_ctx.tmp_ctx);
534
1.06k
  return data_len;
535
4.52k
}
536
537
static int decode_test_ctx(void **out, TALLOC_CTX *ctx, UNUSED fr_dict_t const *dict,
538
         UNUSED fr_dict_attr_t const *root_da)
539
6.63k
{
540
6.63k
  fr_dhcpv6_decode_ctx_t  *test_ctx;
541
542
6.63k
  test_ctx = talloc_zero(ctx, fr_dhcpv6_decode_ctx_t);
543
6.63k
  if (!test_ctx) return -1;
544
545
6.63k
  test_ctx->tmp_ctx = talloc(test_ctx, uint8_t);
546
547
6.63k
  *out = test_ctx;
548
549
6.63k
  return 0;
550
6.63k
}
551
552
static ssize_t fr_dhcpv6_decode_proto(TALLOC_CTX *ctx, fr_pair_list_t *out, uint8_t const *data, size_t data_len, UNUSED void *proto_ctx)
553
5.68k
{
554
5.68k
  size_t packet_len = data_len;
555
//  fr_dhcpv6_decode_ctx_t  *test_ctx = talloc_get_type_abort(proto_ctx, fr_dhcpv6_decode_ctx_t);
556
557
5.68k
  if (!fr_dhcpv6_ok(data, packet_len, 200)) return -1;
558
559
5.57k
  return fr_dhcpv6_decode(ctx, out, data, packet_len);
560
5.68k
}
561
562
563
static ssize_t decode_pair(TALLOC_CTX *ctx, fr_pair_list_t *out, NDEBUG_UNUSED fr_dict_attr_t const *parent,
564
         uint8_t const *data, size_t data_len, void *decode_ctx)
565
0
{
566
0
  fr_assert(parent == fr_dict_root(dict_dhcpv6));
567
568
0
  return decode_option(ctx, out, fr_dict_root(dict_dhcpv6), data, data_len, decode_ctx);
569
0
}
570
571
/*
572
 *  Test points
573
 */
574
extern fr_test_point_pair_decode_t dhcpv6_tp_decode_pair;
575
fr_test_point_pair_decode_t dhcpv6_tp_decode_pair = {
576
  .test_ctx = decode_test_ctx,
577
  .func   = decode_pair,
578
};
579
580
extern fr_test_point_proto_decode_t dhcpv6_tp_decode_proto;
581
fr_test_point_proto_decode_t dhcpv6_tp_decode_proto = {
582
  .test_ctx = decode_test_ctx,
583
  .func   = fr_dhcpv6_decode_proto
584
};