The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
base.c
Go to the documentation of this file.
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: e823e74049804eb9a1b839bcaaa8d38c0d637d13 $
19 *
20 * @file protocols/dhcpv6/base.c
21 * @brief Functions to encode 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 <freeradius-devel/io/pair.h>
29#include <freeradius-devel/protocol/dhcpv6/freeradius.internal.h>
30#include <freeradius-devel/protocol/dhcpv6/rfc3315.h>
31#include <freeradius-devel/protocol/dhcpv6/rfc5007.h>
32#include <freeradius-devel/util/proto.h>
33#include <freeradius-devel/util/rand.h>
34
35#include "dhcpv6.h"
36#include "attrs.h"
37
39static bool instantiated = false;
40
42
45 { .out = &dict_dhcpv6, .proto = "dhcpv6" },
46 { NULL }
47};
48
56
59 { .out = &attr_packet_type, .name = "Packet-Type", .type = FR_TYPE_UINT32, .dict = &dict_dhcpv6 },
60 { .out = &attr_transaction_id, .name = "Transaction-Id", .type = FR_TYPE_OCTETS, .dict = &dict_dhcpv6 },
61 { .out = &attr_hop_count, .name = "Hop-Count", .type = FR_TYPE_UINT8, .dict = &dict_dhcpv6 },
62 { .out = &attr_relay_link_address, .name = "Relay-Link-Address", .type = FR_TYPE_IPV6_ADDR, .dict = &dict_dhcpv6 },
63 { .out = &attr_relay_peer_address, .name = "Relay-Peer-Address", .type = FR_TYPE_IPV6_ADDR, .dict = &dict_dhcpv6 },
64 { .out = &attr_relay_message, .name = "Relay-Message", .type = FR_TYPE_GROUP, .dict = &dict_dhcpv6 },
65 { .out = &attr_option_request, .name = "Option-Request", .type = FR_TYPE_ATTR, .dict = &dict_dhcpv6 },
66 { NULL }
67};
68
69/*
70 * grep VALUE share/dictionary/dhcpv6/dictionary.freeradius.internal | awk '{print "[" $4 "] = \"" $3 "\"," }'
71 */
73 [0] = "invalid",
74 [FR_PACKET_TYPE_VALUE_SOLICIT] = "Solicit",
75 [FR_PACKET_TYPE_VALUE_ADVERTISE] = "Advertise",
76 [FR_PACKET_TYPE_VALUE_REQUEST] = "Request",
77 [FR_PACKET_TYPE_VALUE_CONFIRM] = "Confirm",
78 [FR_PACKET_TYPE_VALUE_RENEW] = "Renew",
79 [FR_PACKET_TYPE_VALUE_REBIND] = "Rebind",
80 [FR_PACKET_TYPE_VALUE_REPLY] = "Reply",
81 [FR_PACKET_TYPE_VALUE_RELEASE] = "Release",
82 [FR_PACKET_TYPE_VALUE_DECLINE] = "Decline",
83 [FR_PACKET_TYPE_VALUE_RECONFIGURE] = "Reconfigure",
84 [FR_PACKET_TYPE_VALUE_INFORMATION_REQUEST] = "Information-Request",
85 [FR_PACKET_TYPE_VALUE_RELAY_FORWARD] = "Relay-Forward",
86 [FR_PACKET_TYPE_VALUE_RELAY_REPLY] = "Relay-Reply",
87 [FR_PACKET_TYPE_VALUE_LEASE_QUERY] = "Lease-Query",
88 [FR_PACKET_TYPE_VALUE_LEASE_QUERY_REPLY] = "Lease-Query-Reply",
89 [FR_PACKET_TYPE_VALUE_LEASE_QUERY_DONE] = "Lease-Query-Done",
90 [FR_PACKET_TYPE_VALUE_LEASE_QUERY_DATA] = "Lease-Query-Data",
91 [FR_PACKET_TYPE_VALUE_RECONFIGURE_REQUEST] = "Reconfigure-Request",
92 [FR_PACKET_TYPE_VALUE_RECONFIGURE_REPLY] = "Reconfigure-Reply",
93 [FR_PACKET_TYPE_VALUE_DHCPV4_QUERY] = "DHCPv4-Query",
94 [FR_PACKET_TYPE_VALUE_DHCPV4_RESPONSE] = "DHCPv4-Response",
95 [FR_PACKET_TYPE_VALUE_ACTIVE_LEASE_QUERY] = "Active-Lease-Query",
96 [FR_PACKET_TYPE_VALUE_START_TLS] = "Start-TLS",
97 [FR_PACKET_TYPE_VALUE_BIND_UPDATE] = "Bind-Update",
98 [FR_PACKET_TYPE_VALUE_BIND_REPLY] = "Bind-Reply",
99 [FR_PACKET_TYPE_VALUE_POOL_REQUEST] = "Pool-Request",
100 [FR_PACKET_TYPE_VALUE_POOL_RESPONSE] = "Pool-Response",
101 [FR_PACKET_TYPE_VALUE_UPDATE_REQUEST] = "Update-Request",
102 [FR_PACKET_TYPE_VALUE_UPDATE_REQUEST_ALL] = "Update-Request-All",
103 [FR_PACKET_TYPE_VALUE_UPDATE_DONE] = "Update-Done",
104 [FR_PACKET_TYPE_VALUE_CONNECT] = "Connect",
105 [FR_PACKET_TYPE_VALUE_CONNECT_REPLY] = "Connect-Reply",
106 [FR_PACKET_TYPE_VALUE_DISCONNECT] = "Disconnect",
107 [FR_PACKET_TYPE_VALUE_STATE] = "State",
108 [FR_PACKET_TYPE_VALUE_CONTACT] = "Contact"
109};
110
113
115 { L("dns_label"), { .func = dict_flag_dns_label } },
116 { L("partial_dns_label"), { .func = dict_flag_partial_dns_label } }
117};
118
119static ssize_t fr_dhcpv6_ok_internal(uint8_t const *packet, uint8_t const *end, size_t max_attributes, int depth);
120
121static ssize_t fr_dhcpv6_options_ok(uint8_t const *packet, uint8_t const *end, size_t max_attributes,
122 bool allow_relay, int depth)
123{
124 size_t attributes;
125 uint8_t const *p;
126
127 attributes = 0;
128 p = packet;
129
130 while (p < end) {
131 uint16_t len;
132
133 if ((size_t)(end - p) < DHCPV6_OPT_HDR_LEN) {
134 fr_strerror_const("Not enough room for option header");
135 return -(p - packet);
136 }
137
138 len = DHCPV6_GET_OPTION_LEN(p);
139 if ((size_t)(end - p) < (DHCPV6_OPT_HDR_LEN + len)) {
140 fr_strerror_const("Option length overflows the packet");
141 return -(p - packet);
142 }
143
144 attributes++;
145 if (attributes > (size_t) max_attributes) {
146 fr_strerror_const("Too many attributes");
147 return -(p - packet);
148 }
149
150 /*
151 * Recurse into the Relay-Message attribute, but
152 * only if the outer packet was a relayed message.
153 */
154 if (allow_relay && (p[0] == 0) && (p[1] == attr_relay_message->attr)) {
155 ssize_t child;
156
157 /*
158 * Recurse to check the encapsulated packet.
159 */
160 child = fr_dhcpv6_ok_internal(p + 4, p + 4 + len, max_attributes - attributes, depth + 1);
161 if (child <= 0) return -((p + 4) - packet) + child;
162
163 attributes += child;
164 }
165
166 p += DHCPV6_OPT_HDR_LEN + len;
167 }
168
169 return attributes;
170}
171
172static ssize_t fr_dhcpv6_ok_internal(uint8_t const *packet, uint8_t const *end, size_t max_attributes, int depth)
173{
174 uint8_t const *p;
175 ssize_t attributes;
176 bool allow_relay;
177 size_t packet_len = end - packet;
178
179 if (end == packet) {
180 fr_strerror_const("Packet is empty");
181 return 0;
182 }
183
185 fr_strerror_const("Too many layers forwarded packets");
186 return 0;
187 }
188
189 switch (packet[0]) {
192 if (packet_len < DHCPV6_RELAY_HDR_LEN) {
193 fr_strerror_const("Packet is too small for relay header");
194 return 0;
195 }
196
197 p = packet + DHCPV6_RELAY_HDR_LEN;
198 allow_relay = true;
199 break;
200
201 default:
202 /*
203 * 8 bit code + 24 bits of transaction ID
204 */
205 if (packet_len < DHCPV6_HDR_LEN) {
206 fr_strerror_const("Packet is too small for DHCPv6 header");
207 return 0;
208 }
209
210 p = packet + DHCPV6_HDR_LEN;
211 allow_relay = false;
212 break;
213 }
214
215 attributes = fr_dhcpv6_options_ok(p, end, max_attributes, allow_relay, depth);
216 if (attributes < 0) return -(p - packet) + attributes;
217
218 return attributes;
219}
220
221
222/** See if the data pointed to by PTR is a valid DHCPv6 packet.
223 *
224 * @param[in] packet to check.
225 * @param[in] packet_len The size of the packet data.
226 * @param[in] max_attributes to allow in the packet.
227 * @return
228 * - True on success.
229 * - False on failure.
230 */
231bool fr_dhcpv6_ok(uint8_t const *packet, size_t packet_len, uint32_t max_attributes)
232{
233 ssize_t slen;
234
235 slen = fr_dhcpv6_ok_internal(packet, packet + packet_len, max_attributes, 0);
236 if (slen <= 0) {
237 fr_strerror_printf_push("Invalid DHCPv6 packet starting at offset %zd", -slen);
238 return false;
239 }
240
241 return true;
242}
243
244/*
245 * Return pointer to a particular option.
246 */
247uint8_t const *fr_dhcpv6_option_find(uint8_t const *start, uint8_t const *end, unsigned int option)
248{
249 uint8_t const *p = start;
250
251 while (p < end) {
252 uint16_t found;
253 uint16_t len;
254
255 if ((size_t)(end - p) < DHCPV6_OPT_HDR_LEN) return NULL;
256
257 found = DHCPV6_GET_OPTION_NUM(p);
258 len = DHCPV6_GET_OPTION_LEN(p);
259
260 if ((p + DHCPV6_OPT_HDR_LEN + len) > end) return NULL;
261
262 if (found == option) return p;
263
264 p += DHCPV6_OPT_HDR_LEN + len;
265 }
266
267 return NULL;
268}
269
270static bool duid_match(uint8_t const *option, fr_dhcpv6_decode_ctx_t const *packet_ctx)
271{
272 uint16_t len;
273
274 len = DHCPV6_GET_OPTION_LEN(option);
275 if (len != packet_ctx->duid_len) return false;
276 if (memcmp(option + 4, packet_ctx->duid, packet_ctx->duid_len) != 0) return false;
277
278 return true;
279}
280
281/** Verify a reply packet from a server to a client
282 *
283 */
284static bool verify_to_client(uint8_t const *packet, size_t packet_len, fr_dhcpv6_decode_ctx_t const *packet_ctx)
285{
286 uint32_t transaction_id;
287 uint8_t const *option;
288 uint8_t const *options = packet + 4;
289 uint8_t const *end = packet + packet_len;
290
291 switch (packet[0]) {
292 case FR_PACKET_TYPE_VALUE_ADVERTISE:
293 transaction_id = fr_nbo_to_uint24(&packet[1]);
294 if (transaction_id != packet_ctx->transaction_id) {
295 fail_tid:
296 fr_strerror_const("Transaction ID does not match");
297 return false;
298 }
299
300 if (!fr_dhcpv6_option_find(options, end, FR_SERVER_ID)) {
301 fail_sid:
302 fr_strerror_const("Packet does not contain a Server-Id option");
303 return false;
304 }
305
306 option = fr_dhcpv6_option_find(options, end, FR_CLIENT_ID);
307 if (!option) {
308 fail_cid:
309 fr_strerror_const("Packet does not contain a Client-Id option");
310 return false;
311 }
312
313 /*
314 * The DUID MUST exist.
315 */
316 if (!packet_ctx->duid) {
317 fail_duid:
318 fr_strerror_const("Packet context does not contain a DUID");
319 return false;
320 }
321
322 check_duid:
323 if (!duid_match(option, packet_ctx)) {
324 fail_match:
325 fr_strerror_const("DUID in packet does not match our DUID");
326 return false;
327 }
328 return true;
329
330 case FR_PACKET_TYPE_VALUE_REPLY:
331 transaction_id = fr_nbo_to_uint24(&packet[1]);
332 if (transaction_id != packet_ctx->transaction_id) goto fail_tid;
333
334 if (!fr_dhcpv6_option_find(options, end, FR_SERVER_ID)) goto fail_sid;
335
336 /*
337 * It's OK to not have a client ID in the reply if we didn't send one.
338 */
339 option = fr_dhcpv6_option_find(options, end, FR_CLIENT_ID);
340 if (!option) {
341 if (!packet_ctx->duid) return true;
342 goto fail_cid;
343 }
344 goto check_duid;
345
346 case FR_PACKET_TYPE_VALUE_RECONFIGURE:
347 if (!fr_dhcpv6_option_find(options, end, FR_SERVER_ID)) goto fail_sid;
348
349 option = fr_dhcpv6_option_find(options, end, FR_CLIENT_ID);
350 if (!option) goto fail_cid;
351
352 /*
353 * The DUID MUST exist.
354 */
355 if (!packet_ctx->duid) goto fail_duid;
356 if (!duid_match(option, packet_ctx)) goto fail_match;
357
358 option = fr_dhcpv6_option_find(options, end, FR_RECONF_MSG);
359 if (!option) {
360 fr_strerror_const("Packet does not contain a Reconf-Msg option");
361 return false;
362 }
363
364 /*
365 * @todo - check reconfigure message type, and
366 * reject if it doesn't match.
367 */
368
369 /*
370 * @todo - check for authentication option and
371 * verify it.
372 */
373 break;
374
376 if (packet_len < DHCPV6_RELAY_HDR_LEN) {
377 fr_strerror_const("Relay-Reply message is too small");
378 return false;
379 }
380
381 options += (DHCPV6_RELAY_HDR_LEN - 4); /* we assumed it was a normal packet above */
382 option = fr_dhcpv6_option_find(options, end, FR_RELAY_MESSAGE);
383 if (!option) {
384 fr_strerror_const("Packet does not contain a Relay-Message option");
385 return false;
386 }
387 return verify_to_client(option + 4, DHCPV6_GET_OPTION_LEN(option), packet_ctx);
388
390 transaction_id = fr_nbo_to_uint24(&packet[1]);
391 if (transaction_id != packet_ctx->transaction_id) goto fail_tid;
392
393 if (!fr_dhcpv6_option_find(options, end, FR_SERVER_ID)) goto fail_sid;
394
395 option = fr_dhcpv6_option_find(options, end, FR_CLIENT_ID);
396 if (!option) goto fail_cid;
397
398 /*
399 * The DUID MUST exist.
400 */
401 if (!packet_ctx->duid) goto fail_duid;
402 if (!duid_match(option, packet_ctx)) goto fail_match;
403 break;
404
405 case FR_PACKET_TYPE_VALUE_REQUEST:
406 case FR_PACKET_TYPE_VALUE_CONFIRM:
407 case FR_PACKET_TYPE_VALUE_RENEW:
408 case FR_PACKET_TYPE_VALUE_REBIND:
409 case FR_PACKET_TYPE_VALUE_RELEASE:
410 case FR_PACKET_TYPE_VALUE_DECLINE:
411 case FR_PACKET_TYPE_VALUE_INFORMATION_REQUEST:
412 default:
413 fr_strerror_const("Invalid message type sent to client");
414 return false;
415 }
416
417 return true;
418}
419
420
421/** Verify a packet from a client to a server
422 *
423 */
424static bool verify_from_client(uint8_t const *packet, size_t packet_len, fr_dhcpv6_decode_ctx_t const *packet_ctx)
425{
426 uint8_t const *option;
427 uint8_t const *options = packet + 4;
428 uint8_t const *end = packet + packet_len;
429
430 /*
431 * Servers MUST have a DUID
432 */
433 if (!packet_ctx->duid) {
434 fr_strerror_const("Packet context does not contain a DUID");
435 return false;
436 }
437
438 switch (packet[0]) {
439 case FR_PACKET_TYPE_VALUE_SOLICIT:
440 case FR_PACKET_TYPE_VALUE_CONFIRM:
441 case FR_PACKET_TYPE_VALUE_REBIND:
442 if (!fr_dhcpv6_option_find(options, end, FR_CLIENT_ID)) {
443 fail_cid:
444 fr_strerror_const("Packet does not contain a Client-Id option");
445 return false;
446 }
447
448 if (!fr_dhcpv6_option_find(options, end, FR_SERVER_ID)) {
449 fail_sid:
450 fr_strerror_const("Packet does not contain a Server-Id option");
451 return false;
452 }
453 break;
454
455 case FR_PACKET_TYPE_VALUE_REQUEST:
456 case FR_PACKET_TYPE_VALUE_RENEW:
457 case FR_PACKET_TYPE_VALUE_DECLINE:
458 case FR_PACKET_TYPE_VALUE_RELEASE:
459 if (!fr_dhcpv6_option_find(options, end, FR_CLIENT_ID)) goto fail_cid;
460
461 option = fr_dhcpv6_option_find(options, end, FR_SERVER_ID);
462 if (!option) goto fail_sid;
463
464 if (!duid_match(option, packet_ctx)) {
465 fail_match:
466 fr_strerror_const("DUID in packet does not match our DUID");
467 return false;
468 }
469 break;
470
471 case FR_PACKET_TYPE_VALUE_INFORMATION_REQUEST:
472 option = fr_dhcpv6_option_find(options, end, FR_SERVER_ID);
473 if (!option) goto fail_sid;
474
475 if (!duid_match(option, packet_ctx)) goto fail_match;
476
477 /*
478 * IA options are forbidden.
479 */
480 if (fr_dhcpv6_option_find(options, end, FR_IA_NA)) {
481 fr_strerror_const("Packet contains an IA-NA option");
482 return false;
483 }
484 if (fr_dhcpv6_option_find(options, end, FR_IA_TA)) {
485 fr_strerror_const("Packet contains an IA-TA option");
486 return false;
487 }
488 if (fr_dhcpv6_option_find(options, end, FR_IA_ADDR)) {
489 fr_strerror_const("Packet contains an IA-Addr option");
490 return false;
491 }
492 break;
493
495 if (packet_len < DHCPV6_RELAY_HDR_LEN) {
496 fr_strerror_const("Relay-Forward message is too small");
497 return false;
498 }
499
500 options += (DHCPV6_RELAY_HDR_LEN - 4); /* we assumed it was a normal packet above */
501 option = fr_dhcpv6_option_find(options, end, FR_RELAY_MESSAGE);
502 if (!option) {
503 fr_strerror_const("Packet does not contain a Relay-Message option");
504 return false;
505 }
506
507 return verify_from_client(option + 4, DHCPV6_GET_OPTION_LEN(option), packet_ctx);
508
509 case FR_PACKET_TYPE_VALUE_LEASE_QUERY:
510 if (!fr_dhcpv6_option_find(options, end, FR_CLIENT_ID)) goto fail_cid;
511
512 /*
513 * Server-ID is a SHOULD, but if it exists, it
514 * MUST match.
515 */
516 option = fr_dhcpv6_option_find(options, end, FR_SERVER_ID);
517 if (option && !duid_match(option, packet_ctx)) goto fail_match;
518
519 option = fr_dhcpv6_option_find(options, end, FR_LEASE_QUERY);
520 if (!option) {
521 fr_strerror_const("Packet does not contain a Lease-Query option");
522 return false;
523 }
524 break;
525
526 case FR_PACKET_TYPE_VALUE_ADVERTISE:
527 case FR_PACKET_TYPE_VALUE_REPLY:
528 case FR_PACKET_TYPE_VALUE_RECONFIGURE:
529 default:
530 fr_strerror_const("Invalid message type sent to server");
531 return false;
532 }
533 return true;
534}
535
536/** Verify the packet under some various circumstances
537 *
538 * @param[in] packet to check.
539 * @param[in] packet_len The size of the packet data.
540 * @param[in] packet_ctx The expected packet_ctx
541 * @param[in] from_server true for packets from a server, false for packets from a client.
542 * @return
543 * - True on success.
544 * - False on failure.
545 *
546 * fr_dhcpv6_ok() SHOULD be called before calling this function.
547 */
548bool fr_dhcpv6_verify(uint8_t const *packet, size_t packet_len, fr_dhcpv6_decode_ctx_t const *packet_ctx,
549 bool from_server)
550{
551 if (packet_len < DHCPV6_HDR_LEN) return false;
552
553 /*
554 * We support up to relaying.
555 */
556 if ((packet[0] == 0) || (packet[0] > FR_PACKET_TYPE_VALUE_RELAY_REPLY)) return false;
557
558 if (!packet_ctx->duid) return false;
559
560 if (from_server) return verify_to_client(packet, packet_len, packet_ctx);
561
562 return verify_from_client(packet, packet_len, packet_ctx);
563}
564
565/*
566
567 0 1 2 3
568 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
569 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
570 | msg-type | transaction-id |
571 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
572 | |
573 . options .
574 . (variable number and length) .
575 | |
576 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
577*/
578
579/** Decode a DHCPv6 packet
580 *
581 */
582ssize_t fr_dhcpv6_decode(TALLOC_CTX *ctx, fr_pair_list_t *out, uint8_t const *packet, size_t packet_len)
583{
584 ssize_t slen = -1;
585 uint8_t const *p, *end;
586 fr_dhcpv6_decode_ctx_t packet_ctx = {};
587 fr_pair_t *vp;
588 fr_pair_list_t tmp;
589
590 if (packet_len < DHCPV6_HDR_LEN) return 0; /* protect access to packet[0] */
591
592 /*
593 * Get the packet type.
594 */
596 if (!vp) return -1;
597
598 fr_pair_list_init(&tmp);
599 vp->vp_uint32 = packet[0];
600 fr_pair_append(&tmp, vp);
601
602 switch (packet[0]) {
605 /*
606 * Just for sanity check.
607 */
608 if (packet_len < DHCPV6_RELAY_HDR_LEN) return -1;
609
610 /*
611 * Decode the header fields.
612 */
614 if (!vp) goto fail;
615 if (fr_value_box_from_network(vp, &vp->data, vp->vp_type, NULL,
616 &FR_DBUFF_TMP(packet + 1, 1), 1, true) < 0) {
617 goto fail;
618 }
619 fr_pair_append(&tmp, vp);
620
622 if (!vp) goto fail;
623 if (fr_value_box_from_network(vp, &vp->data, vp->vp_type, NULL,
624 &FR_DBUFF_TMP(packet + 2, 16), 16, true) < 0) {
625 goto fail;
626 }
627 fr_pair_append(&tmp, vp);
628
630 if (!vp) goto fail;
631 if (fr_value_box_from_network(vp, &vp->data, vp->vp_type, NULL,
632 &FR_DBUFF_TMP(packet + 2 + 16, 16), 16, true) < 0) {
633 goto fail;
634 }
635
636 fr_pair_append(&tmp, vp);
637
638 p = packet + DHCPV6_RELAY_HDR_LEN;
639 goto decode_options;
640
641 default:
642 break;
643 }
644
645 /*
646 * And the transaction ID.
647 */
649 if (!vp) {
650 fail:
651 fr_pair_list_free(&tmp);
652 return slen;
653 }
654
655 /*
656 * Copy 3 octets over.
657 */
658 (void) fr_pair_value_memdup(vp, packet + 1, 3, false);
659
660 fr_pair_append(&tmp, vp);
661
662 p = packet + 4;
663
664decode_options:
665 end = packet + packet_len;
666 packet_ctx.tmp_ctx = talloc_init_const("tmp");
667
668 /*
669 * The caller MUST have called fr_dhcpv6_ok() first. If
670 * he doesn't, all hell breaks loose.
671 */
672 while (p < end) {
673 slen = fr_dhcpv6_decode_option(ctx, &tmp, p, (end - p), &packet_ctx);
674 if (slen < 0) {
675 talloc_free(packet_ctx.tmp_ctx);
676 goto fail;
677 }
678 /*
679 * If slen is larger than the room in the packet,
680 * all kinds of bad things happen.
681 */
682 if (!fr_cond_assert(slen <= (end - p))) {
683 talloc_free(packet_ctx.tmp_ctx);
684 goto fail;
685 }
686
687 p += slen;
688 talloc_free_children(packet_ctx.tmp_ctx);
689 }
691
692 /*
693 * We've parsed the whole packet, return that.
694 */
695 talloc_free(packet_ctx.tmp_ctx);
696 return packet_len;
697}
698
699/** DHCPV6-specific iterator
700 *
701 */
702void *fr_dhcpv6_next_encodable(fr_dcursor_t *cursor, void *current, void *uctx)
703{
704 fr_pair_t *c = current;
705 fr_dict_t *dict = talloc_get_type_abort(uctx, fr_dict_t);
706
707 while ((c = fr_dlist_next(cursor->dlist, c))) {
708 PAIR_VERIFY(c);
709 if (c->da->dict != dict || c->da->flags.internal) continue;
710 if (c->vp_type == FR_TYPE_BOOL && !c->vp_bool) continue;
711
712 break;
713 }
714
715 return c;
716}
717
718/** Encode a DHCPv6 packet
719 *
720 */
721ssize_t fr_dhcpv6_encode(fr_dbuff_t *dbuff, uint8_t const *original, size_t length, int msg_type, fr_pair_list_t *vps)
722{
723 fr_dbuff_t frame_dbuff = FR_DBUFF(dbuff);
724 fr_pair_t *vp;
725 fr_dict_attr_t const *root;
726 ssize_t slen;
727 fr_dcursor_t cursor;
728 fr_dhcpv6_encode_ctx_t packet_ctx;
729
731
732 if (!msg_type) {
734 if (vp) msg_type = vp->vp_uint32;
735 }
736
737 if ((msg_type <= 0) || (msg_type > UINT8_MAX)) {
738 fr_strerror_printf("Invalid message type %d", msg_type);
739 return -1;
740 }
741
742 FR_DBUFF_IN_RETURN(&frame_dbuff, (uint8_t)msg_type);
743
744 switch (msg_type) {
748 if (likely(vp != NULL)) {
749 FR_VALUE_BOX_TO_NETWORK_RETURN(&frame_dbuff, &vp->data);
750 } else {
752 }
753
755 if (likely(vp != NULL)) {
756 FR_VALUE_BOX_TO_NETWORK_RETURN(&frame_dbuff, &vp->data);
757 } else {
759 }
760
762 if (likely(vp != NULL)) {
763 FR_VALUE_BOX_TO_NETWORK_RETURN(&frame_dbuff, &vp->data);
764 } else {
766 }
767 break;
768
769 default:
770 /*
771 * We can set an XID, or we can pick a random one.
772 */
774 if (vp && (vp->vp_length >= DHCPV6_TRANSACTION_ID_LEN)) {
775 FR_DBUFF_IN_MEMCPY_RETURN(&frame_dbuff, vp->vp_octets, DHCPV6_TRANSACTION_ID_LEN);
776 } else {
779 FR_DBUFF_IN_MEMCPY_RETURN(&frame_dbuff, id, sizeof(id)); /* Need 24 bits of the 32bit integer */
780 }
781 break;
782 }
783
784 /*
785 * Encode options.
786 */
787 packet_ctx.root = root;
788 packet_ctx.original = original;
789 packet_ctx.original_length = length;
790
792 while ((fr_dbuff_extend(&frame_dbuff) > 0) && (fr_dcursor_current(&cursor) != NULL)) {
793 slen = fr_dhcpv6_encode_option(&frame_dbuff, &cursor, &packet_ctx);
794 if (slen < 0) return slen - fr_dbuff_used(&frame_dbuff);
795 }
796
797 return fr_dbuff_set(dbuff, &frame_dbuff);
798}
799
800
801static char const tabs[] = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
802
803static void print_hex_data(FILE *fp, uint8_t const *ptr, int attrlen, int depth)
804{
805 int i;
806
807 for (i = 0; i < attrlen; i++) {
808 if ((i > 0) && ((i & 0x0f) == 0x00))
809 fprintf(fp, "%.*s", depth, tabs);
810 fprintf(fp, "%02x ", ptr[i]);
811 if ((i & 0x0f) == 0x0f) fprintf(fp, "\n");
812 }
813 if ((i & 0x0f) != 0) fprintf(fp, "\n");
814}
815
816static void dhcpv6_print_hex(FILE *fp, uint8_t const *packet, size_t packet_len, int depth)
817{
818 uint8_t const *option, *end = packet + packet_len;
819
820 if (packet_len < 4) {
821 fprintf(fp, "%.*s", depth, tabs);
822 fprintf(fp, "???:\t");
823 print_hex_data(fp, packet, packet_len, depth + 1);
824 return;
825 }
826
827 fprintf(fp, "%.*s", depth, tabs);
828 if ((packet[0] > 0) && (packet[0] < FR_DHCPV6_CODE_MAX)) {
829 fprintf(fp, "packet: %s\n", fr_dhcpv6_packet_names[packet[0]]);
830 } else {
831 fprintf(fp, "packet: %02x\n", packet[0]);
832 }
833
834 if ((packet[0] == FR_PACKET_TYPE_VALUE_RELAY_FORWARD) ||
835 (packet[0] == FR_PACKET_TYPE_VALUE_RELAY_REPLY)) {
836 if (packet_len < 34) {
837 fprintf(fp, "%.*s", depth, tabs);
838 fprintf(fp, "???:\t");
839 print_hex_data(fp, packet + 1, packet_len - 1, depth + 1);
840 return;
841 }
842
843 fprintf(fp, "%.*s", depth, tabs);
844 fprintf(fp, "hops: %02x\n", packet[1]);
845 fprintf(fp, "%.*s", depth, tabs);
846 fprintf(fp, "relay link address: ");
847 print_hex_data(fp, packet + 2, 16, depth + 1);
848
849 fprintf(fp, "%.*s", depth, tabs);
850 fprintf(fp, "peer address: ");
851 print_hex_data(fp, packet + 18, 16, depth + 1);
852 option = packet + 34;
853 } else {
854 fprintf(fp, "%.*s", depth, tabs);
855 fprintf(fp, "transaction id: ");
856 print_hex_data(fp, packet + 1, 3, depth + 1);
857 option = packet + 4;
858 }
859
860 fprintf(fp, "%.*s", depth, tabs);
861 fprintf(fp, "options\n");
862 while (option < end) {
863 uint16_t length;
864
865 if ((end - option) < 4) {
866 fprintf(fp, "%.*s", depth + 1, tabs);
867 fprintf(fp, "???:\t");
868 print_hex_data(fp, option, end - option, depth + 2);
869 break;
870 }
871
872 length = fr_nbo_to_uint16(option + 2);
873 fprintf(fp, "%.*s", depth + 1, tabs);
874 fprintf(fp, "%04x %04x\t", fr_nbo_to_uint16(option), length);
875
876 if (length > end - (option + 4)) {
877 print_hex_data(fp, option + 4, end - (option + 4), depth + 3);
878 break;
879 }
880
881 print_hex_data(fp, option + 4, length, depth + 3);
882 if ((option[0] == 0) && (option[1] == attr_relay_message->attr)) {
883 dhcpv6_print_hex(fp, option + 4, length, depth + 2);
884 }
885
886 option += 4 + length;
887 }
888
889 fprintf(fp, "\n");
890}
891
892/** Print a raw DHCP packet as hex.
893 *
894 */
895void fr_dhcpv6_print_hex(FILE *fp, uint8_t const *packet, size_t packet_len)
896{
897 dhcpv6_print_hex(fp, packet, packet_len, 0);
898}
899
901{
902 if (instance_count > 0) {
904 return 0;
905 }
906
908
910 fail:
912 return -1;
913 }
914
917 goto fail;
918 }
919
920 instantiated = true;
921 return 0;
922}
923
925{
926 if (!instantiated) return;
927
929
930 if (--instance_count > 0) return;
931
933 instantiated = false;
934}
935
937{
938 /*
939 * "arrays" of string/octets are encoded as a 16-bit
940 * length, followed by the actual data.
941 */
942 if (da->flags.array && ((da->type == FR_TYPE_STRING) || (da->type == FR_TYPE_OCTETS))) {
943 da->flags.is_known_width = true;
944
945 if (da->flags.extra && (da->flags.subtype != FLAG_LENGTH_UINT16)) {
946 fr_strerror_const("string/octets arrays require the 'length=uint16' flag");
947 return false;
948 }
949 }
950
951 if (da->flags.extra && (da->flags.subtype == FLAG_LENGTH_UINT8)) {
952 fr_strerror_const("The 'length=uint8' flag cannot be used for DHCPv6");
953 return false;
954 }
955
956 /*
957 * "extra" signifies that subtype is being used by the
958 * dictionaries itself.
959 */
960 if (da->flags.extra || !da->flags.subtype) return true;
961
962 if ((da->type != FR_TYPE_STRING) && fr_dhcpv6_flag_any_dns_label(da)) {
963 fr_strerror_const("The 'dns_label' flag can only be used with attributes of type 'string'");
964 return false;
965 }
966
967 if ((da->type == FR_TYPE_ATTR) && !da->parent->flags.is_root) {
968 fr_strerror_const("Data type 'attr' can only exist at the dictionary root");
969 return false;
970 }
971
972 da->flags.is_known_width = true;
973
974 return true;
975}
976
979 .name = "dhcpv6",
980 .default_type_size = 2,
981 .default_type_length = 2,
982
983 .attr = {
984 .valid = attr_valid,
985 .flags = {
986 .table = dhcpv6_flags,
987 .table_len = NUM_ELEMENTS(dhcpv6_flags),
988 .len = sizeof(fr_dhcpv6_attr_flags_t)
989 }
990 },
991
992 .init = fr_dhcpv6_global_init,
993 .free = fr_dhcpv6_global_free,
994
995 .encode = fr_dhcpv6_encode_foreign,
996 .decode = fr_dhcpv6_decode_foreign,
997};
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:209
#define NUM_ELEMENTS(_t)
Definition build.h:339
static const char * tabs
Definition command.c:1589
#define fr_dbuff_used(_dbuff_or_marker)
Return the number of bytes remaining between the start of the dbuff or marker and the current positio...
Definition dbuff.h:767
#define fr_dbuff_set(_dst, _src)
Set the 'current' position in a dbuff or marker using another dbuff or marker, a char pointer,...
Definition dbuff.h:1004
#define FR_DBUFF_MEMSET_RETURN(_dbuff_or_marker, _c, _inlen)
Set _inlen bytes of a dbuff or marker to _c returning if there is insufficient space.
Definition dbuff.h:1508
#define fr_dbuff_extend(_dbuff)
Extend if no space remains.
Definition dbuff.h:705
#define FR_DBUFF_IN_MEMCPY_RETURN(_dbuff_or_marker, _in, _inlen)
Copy exactly _inlen bytes into dbuff or marker returning if there's insufficient space.
Definition dbuff.h:1382
#define FR_DBUFF_IN_RETURN(_dbuff_or_marker, _in)
Copy data from a fixed sized C type into a dbuff returning if there is insufficient space.
Definition dbuff.h:1585
#define FR_DBUFF(_dbuff_or_marker)
Create a new dbuff pointing to the same underlying buffer.
Definition dbuff.h:222
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
Definition dbuff.h:514
static void * fr_dcursor_current(fr_dcursor_t *cursor)
Return the item the cursor current points to.
Definition dcursor.h:339
fr_dlist_head_t * dlist
Head of the doubly linked list being iterated over.
Definition dcursor.h:94
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:131
Implementation of the DHCPv6 protocol.
uint32_t transaction_id
previous transaction ID
Definition dhcpv6.h:134
@ FR_DHCPV6_CODE_MAX
Definition dhcpv6.h:103
@ FR_DHCPV6_RELAY_FORWARD
Definition dhcpv6.h:79
@ FR_DHCPV6_RELAY_REPLY
Definition dhcpv6.h:80
@ FR_DHCPV6_LEASE_QUERY_REPLY
Definition dhcpv6.h:82
fr_dict_attr_t const * root
Root attribute of the dictionary.
Definition dhcpv6.h:127
#define DHCPV6_HDR_LEN
Definition dhcpv6.h:43
#define DHCPV6_GET_OPTION_NUM(_x)
Definition dhcpv6.h:47
uint8_t * duid
the expected DUID, in wire format
Definition dhcpv6.h:135
#define DHCPV6_RELAY_HDR_LEN
Definition dhcpv6.h:44
#define DHCPV6_LINK_ADDRESS_LEN
Definition dhcpv6.h:40
#define DHCPV6_PEER_ADDRESS_LEN
Definition dhcpv6.h:41
#define DHCPV6_GET_OPTION_LEN(_x)
Definition dhcpv6.h:48
static bool fr_dhcpv6_flag_any_dns_label(fr_dict_attr_t const *da)
Definition dhcpv6.h:152
uint8_t const * original
original packet
Definition dhcpv6.h:128
ssize_t fr_dhcpv6_encode_option(fr_dbuff_t *dbuff, fr_dcursor_t *cursor, void *encode_ctx)
Encode a DHCPv6 option and any sub-options.
Definition encode.c:679
#define DHCPV6_OPT_HDR_LEN
Definition dhcpv6.h:45
#define DHCPV6_TRANSACTION_ID_LEN
Definition dhcpv6.h:37
size_t original_length
length of the original packet
Definition dhcpv6.h:129
#define DHCPV6_HOP_COUNT_LEN
Definition dhcpv6.h:39
size_t duid_len
length of the expected DUID
Definition dhcpv6.h:136
#define DHCPV6_MAX_RELAY_NESTING
Definition dhcpv6.h:50
TALLOC_CTX * tmp_ctx
for temporary things cleaned up during decoding
Definition dhcpv6.h:133
ssize_t fr_dhcpv6_encode_foreign(fr_dbuff_t *dbuff, fr_pair_list_t const *list)
Definition encode.c:730
#define fr_dict_autofree(_to_free)
Definition dict.h:879
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2475
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:274
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:287
@ FLAG_LENGTH_UINT8
string / octets type is prefixed by uint8 of length
Definition dict.h:155
@ FLAG_LENGTH_UINT16
string / octets type is prefixed by uint16 of length
Definition dict.h:156
int fr_dict_attr_autoload(fr_dict_attr_autoload_t const *to_load)
Process a dict_attr_autoload element to load/verify a dictionary attribute.
Definition dict_util.c:4206
#define fr_dict_autoload(_to_load)
Definition dict.h:876
char const * name
name of this protocol
Definition dict.h:435
#define FR_DICT_ATTR_FLAG_FUNC(_struct, _name)
Define a flag setting function, which sets one bit in a fr_dict_attr_flags_t.
Definition dict.h:414
Specifies an attribute which must be present for the module to function.
Definition dict.h:273
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:286
Protocol specific custom flag definitnion.
Definition dict.h:404
Protocol-specific callbacks in libfreeradius-PROTOCOL.
Definition dict.h:434
static void * fr_dlist_next(fr_dlist_head_t const *list_head, void const *ptr)
Get the next item in a list.
Definition dlist.h:555
fr_dict_attr_t const * attr_packet_type
Definition base.c:93
static uint32_t instance_count
Definition base.c:44
talloc_free(reap)
unsigned short uint16_t
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_UINT8
8 Bit unsigned integer.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_IPV6_ADDR
128 Bit IPv6 Address.
@ FR_TYPE_BOOL
A truth value.
@ FR_TYPE_OCTETS
Raw octets.
@ FR_TYPE_GROUP
A grouping of other attributes.
unsigned int uint32_t
long int ssize_t
unsigned char uint8_t
#define UINT8_MAX
static uint8_t depth(fr_minmax_heap_index_t i)
Definition minmax_heap.c:83
static void fr_nbo_from_uint24(uint8_t out[static 3], uint32_t num)
Write out an unsigned 24bit integer in wire format (big endian)
Definition nbo.h:49
static uint16_t fr_nbo_to_uint16(uint8_t const data[static sizeof(uint16_t)])
Read an unsigned 16bit integer from wire format (big endian)
Definition nbo.h:146
static uint32_t fr_nbo_to_uint24(uint8_t const data[static 3])
Read an unsigned 24bit integer from wire format (big endian)
Definition nbo.h:157
int fr_pair_value_memdup(fr_pair_t *vp, uint8_t const *src, size_t len, bool tainted)
Copy data into an "octets" data type.
Definition pair.c:2938
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition pair.c:698
int fr_pair_append(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the end of the list.
Definition pair.c:1343
fr_pair_t * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute and assign a fr_dict_attr_t.
Definition pair.c:287
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
Definition pair.c:46
static fr_dict_attr_t const * attr_hop_count
Definition base.c:130
static fr_dict_attr_t const * attr_relay_link_address
Definition base.c:133
static fr_dict_t const * dict_dhcpv6
Definition base.c:118
static fr_dict_attr_t const * attr_transaction_id
Definition base.c:135
static fr_dict_attr_t const * attr_relay_peer_address
Definition base.c:134
static void print_hex_data(FILE *fp, uint8_t const *ptr, int attrlen, int depth)
Definition base.c:644
static bool instantiated
Definition base.c:34
fr_dict_attr_t const * attr_option_request
Definition base.c:55
fr_dict_autoload_t libfreeradius_dhcpv6_dict[]
Definition base.c:44
static bool duid_match(uint8_t const *option, fr_dhcpv6_decode_ctx_t const *packet_ctx)
Definition base.c:270
void * fr_dhcpv6_next_encodable(fr_dcursor_t *cursor, void *current, void *uctx)
DHCPV6-specific iterator.
Definition base.c:702
void fr_dhcpv6_global_free(void)
Definition base.c:924
fr_dict_protocol_t libfreeradius_dhcpv6_dict_protocol
Definition base.c:978
static ssize_t fr_dhcpv6_ok_internal(uint8_t const *packet, uint8_t const *end, size_t max_attributes, int depth)
Definition base.c:172
static void dhcpv6_print_hex(FILE *fp, uint8_t const *packet, size_t packet_len, int depth)
Definition base.c:816
bool fr_dhcpv6_ok(uint8_t const *packet, size_t packet_len, uint32_t max_attributes)
See if the data pointed to by PTR is a valid DHCPv6 packet.
Definition base.c:231
uint8_t const * fr_dhcpv6_option_find(uint8_t const *start, uint8_t const *end, unsigned int option)
Definition base.c:247
int fr_dhcpv6_global_init(void)
Definition base.c:900
static ssize_t fr_dhcpv6_options_ok(uint8_t const *packet, uint8_t const *end, size_t max_attributes, bool allow_relay, int depth)
Definition base.c:121
ssize_t fr_dhcpv6_decode(TALLOC_CTX *ctx, fr_pair_list_t *out, uint8_t const *packet, size_t packet_len)
Decode a DHCPv6 packet.
Definition base.c:582
char const * fr_dhcpv6_packet_names[FR_DHCPV6_CODE_MAX]
Definition base.c:72
fr_dict_attr_t const * attr_relay_message
Definition base.c:54
fr_dict_attr_autoload_t libfreeradius_dhcpv6_dict_attr[]
Definition base.c:58
static fr_dict_flag_parser_t const dhcpv6_flags[]
Definition base.c:114
bool fr_dhcpv6_verify(uint8_t const *packet, size_t packet_len, fr_dhcpv6_decode_ctx_t const *packet_ctx, bool from_server)
Verify the packet under some various circumstances.
Definition base.c:548
static bool verify_to_client(uint8_t const *packet, size_t packet_len, fr_dhcpv6_decode_ctx_t const *packet_ctx)
Verify a reply packet from a server to a client.
Definition base.c:284
static bool verify_from_client(uint8_t const *packet, size_t packet_len, fr_dhcpv6_decode_ctx_t const *packet_ctx)
Verify a packet from a client to a server.
Definition base.c:424
void fr_dhcpv6_print_hex(FILE *fp, uint8_t const *packet, size_t packet_len)
Print a raw DHCP packet as hex.
Definition base.c:895
ssize_t fr_dhcpv6_encode(fr_dbuff_t *dbuff, uint8_t const *original, size_t length, int msg_type, fr_pair_list_t *vps)
Encode a DHCPv6 packet.
Definition base.c:721
ssize_t fr_dhcpv6_decode_option(TALLOC_CTX *ctx, fr_pair_list_t *out, uint8_t const *data, size_t data_len, void *decode_ctx)
Create a "normal" fr_pair_t from the given data.
Definition decode.c:449
ssize_t fr_dhcpv6_decode_foreign(TALLOC_CTX *ctx, fr_pair_list_t *out, uint8_t const *data, size_t data_len)
Definition decode.c:464
static bool attr_valid(fr_dict_attr_t *da)
Definition base.c:443
VQP attributes.
#define fr_assert(_expr)
Definition rad_assert.h:38
static rc_request_t * current
uint32_t fr_rand(void)
Return a 32-bit random number.
Definition rand.c:105
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
fr_dict_attr_t const *_CONST da
Dictionary attribute defines the attribute number, vendor and type of the pair.
Definition pair.h:69
static TALLOC_CTX * talloc_init_const(char const *name)
Allocate a top level chunk with a constant name.
Definition talloc.h:112
#define fr_pair_dcursor_iter_init(_cursor, _list, _iter, _uctx)
Initialises a special dcursor with callbacks that will maintain the attr sublists correctly.
Definition pair.h:569
#define PAIR_VERIFY(_x)
Definition pair.h:193
void fr_pair_list_free(fr_pair_list_t *list)
Free memory used by a valuepair list.
void fr_pair_list_append(fr_pair_list_t *dst, fr_pair_list_t *src)
Appends a list of fr_pair_t from a temporary list to a destination list.
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_printf_push(_fmt,...)
Add a message to an existing stack of messages at the tail.
Definition strerror.h:84
#define fr_strerror_const(_msg)
Definition strerror.h:223
@ FR_TYPE_ATTR
A contains an attribute reference.
Definition types.h:83
ssize_t fr_value_box_from_network(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t type, fr_dict_attr_t const *enumv, fr_dbuff_t *dbuff, size_t len, bool tainted)
Decode a fr_value_box_t from serialized binary data.
Definition value.c:1917
#define FR_VALUE_BOX_TO_NETWORK_RETURN(_dbuff, _value)
Definition value.h:1033
static size_t char ** out
Definition value.h:1023