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