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: 1d5a58b67e0b4e5b288596357a9b25dddb635998 $
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
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 },
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 * Check reconfigure message type, and reject
366 * if it is not valid.
367 */
368 switch (option[4]) {
369 case FR_DHCPV6_RENEW:
370 case FR_DHCPV6_REBIND:
372 break;
373 default:
374 fr_strerror_const("Invalid Reconf-Msg option value");
375 return false;
376 }
377 /*
378 * @todo - check for authentication option and
379 * verify it.
380 */
381 break;
382
384 if (packet_len < DHCPV6_RELAY_HDR_LEN) {
385 fr_strerror_const("Relay-Reply message is too small");
386 return false;
387 }
388
389 options += (DHCPV6_RELAY_HDR_LEN - 4); /* we assumed it was a normal packet above */
390 option = fr_dhcpv6_option_find(options, end, FR_RELAY_MESSAGE);
391 if (!option) {
392 fr_strerror_const("Packet does not contain a Relay-Message option");
393 return false;
394 }
395 return verify_to_client(option + 4, DHCPV6_GET_OPTION_LEN(option), packet_ctx);
396
398 transaction_id = fr_nbo_to_uint24(&packet[1]);
399 if (transaction_id != packet_ctx->transaction_id) goto fail_tid;
400
401 if (!fr_dhcpv6_option_find(options, end, FR_SERVER_ID)) goto fail_sid;
402
403 option = fr_dhcpv6_option_find(options, end, FR_CLIENT_ID);
404 if (!option) goto fail_cid;
405
406 /*
407 * The DUID MUST exist.
408 */
409 if (!packet_ctx->duid) goto fail_duid;
410 if (!duid_match(option, packet_ctx)) goto fail_match;
411 break;
412
413 case FR_PACKET_TYPE_VALUE_REQUEST:
414 case FR_PACKET_TYPE_VALUE_CONFIRM:
415 case FR_PACKET_TYPE_VALUE_RENEW:
416 case FR_PACKET_TYPE_VALUE_REBIND:
417 case FR_PACKET_TYPE_VALUE_RELEASE:
418 case FR_PACKET_TYPE_VALUE_DECLINE:
419 case FR_PACKET_TYPE_VALUE_INFORMATION_REQUEST:
420 default:
421 fr_strerror_const("Invalid message type sent to client");
422 return false;
423 }
424
425 return true;
426}
427
428
429/** Verify a packet from a client to a server
430 *
431 */
432static bool verify_from_client(uint8_t const *packet, size_t packet_len, fr_dhcpv6_decode_ctx_t const *packet_ctx)
433{
434 uint8_t const *option;
435 uint8_t const *options = packet + 4;
436 uint8_t const *end = packet + packet_len;
437
438 /*
439 * Servers MUST have a DUID
440 */
441 if (!packet_ctx->duid) {
442 fr_strerror_const("Packet context does not contain a DUID");
443 return false;
444 }
445
446 switch (packet[0]) {
447 case FR_PACKET_TYPE_VALUE_SOLICIT:
448 case FR_PACKET_TYPE_VALUE_CONFIRM:
449 case FR_PACKET_TYPE_VALUE_REBIND:
450 if (!fr_dhcpv6_option_find(options, end, FR_CLIENT_ID)) {
451 fail_cid:
452 fr_strerror_const("Packet does not contain a Client-Id option");
453 return false;
454 }
455
456 if (fr_dhcpv6_option_find(options, end, FR_SERVER_ID)) {
457 fr_strerror_const("Packet contains a Server-Id option");
458 return false;
459 }
460 break;
461
462 case FR_PACKET_TYPE_VALUE_REQUEST:
463 case FR_PACKET_TYPE_VALUE_RENEW:
464 case FR_PACKET_TYPE_VALUE_DECLINE:
465 case FR_PACKET_TYPE_VALUE_RELEASE:
466 if (!fr_dhcpv6_option_find(options, end, FR_CLIENT_ID)) goto fail_cid;
467
468 option = fr_dhcpv6_option_find(options, end, FR_SERVER_ID);
469 if (!option) {
470 fr_strerror_const("Packet does not contain a Server-Id option");
471 return false;
472 }
473
474 if (!duid_match(option, packet_ctx)) {
475 fail_match:
476 fr_strerror_const("DUID in packet does not match our DUID");
477 return false;
478 }
479 break;
480
481 case FR_PACKET_TYPE_VALUE_INFORMATION_REQUEST:
482 option = fr_dhcpv6_option_find(options, end, FR_SERVER_ID);
483 if (option && !duid_match(option, packet_ctx)) goto fail_match;
484
485 /*
486 * IA options are forbidden.
487 */
488 if (fr_dhcpv6_option_find(options, end, FR_IA_NA)) {
489 fr_strerror_const("Packet contains an IA-NA option");
490 return false;
491 }
492 if (fr_dhcpv6_option_find(options, end, FR_IA_TA)) {
493 fr_strerror_const("Packet contains an IA-TA option");
494 return false;
495 }
496 if (fr_dhcpv6_option_find(options, end, FR_IA_ADDR)) {
497 fr_strerror_const("Packet contains an IA-Addr option");
498 return false;
499 }
500 break;
501
503 if (packet_len < DHCPV6_RELAY_HDR_LEN) {
504 fr_strerror_const("Relay-Forward message is too small");
505 return false;
506 }
507
508 options += (DHCPV6_RELAY_HDR_LEN - 4); /* we assumed it was a normal packet above */
509 option = fr_dhcpv6_option_find(options, end, FR_RELAY_MESSAGE);
510 if (!option) {
511 fr_strerror_const("Packet does not contain a Relay-Message option");
512 return false;
513 }
514
515 return verify_from_client(option + 4, DHCPV6_GET_OPTION_LEN(option), packet_ctx);
516
517 case FR_PACKET_TYPE_VALUE_LEASE_QUERY:
518 if (!fr_dhcpv6_option_find(options, end, FR_CLIENT_ID)) goto fail_cid;
519
520 /*
521 * Server-ID is a SHOULD, but if it exists, it
522 * MUST match.
523 */
524 option = fr_dhcpv6_option_find(options, end, FR_SERVER_ID);
525 if (option && !duid_match(option, packet_ctx)) goto fail_match;
526
527 option = fr_dhcpv6_option_find(options, end, FR_LEASE_QUERY);
528 if (!option) {
529 fr_strerror_const("Packet does not contain a Lease-Query option");
530 return false;
531 }
532 break;
533
534 case FR_PACKET_TYPE_VALUE_ADVERTISE:
535 case FR_PACKET_TYPE_VALUE_REPLY:
536 case FR_PACKET_TYPE_VALUE_RECONFIGURE:
537 default:
538 fr_strerror_const("Invalid message type sent to server");
539 return false;
540 }
541 return true;
542}
543
544/** Verify the packet under some various circumstances
545 *
546 * @param[in] packet to check.
547 * @param[in] packet_len The size of the packet data.
548 * @param[in] packet_ctx The expected packet_ctx
549 * @param[in] from_server true for packets from a server, false for packets from a client.
550 * @return
551 * - True on success.
552 * - False on failure.
553 *
554 * fr_dhcpv6_ok() SHOULD be called before calling this function.
555 */
556bool fr_dhcpv6_verify(uint8_t const *packet, size_t packet_len, fr_dhcpv6_decode_ctx_t const *packet_ctx,
557 bool from_server)
558{
559 if (packet_len < DHCPV6_HDR_LEN) return false;
560
561 /*
562 * We support up to lease query reply.
563 */
564 if ((packet[0] == 0) || (packet[0] > FR_DHCPV6_LEASE_QUERY_REPLY)) return false;
565
566 if (!packet_ctx->duid) return false;
567
568 if (from_server) return verify_to_client(packet, packet_len, packet_ctx);
569
570 return verify_from_client(packet, packet_len, packet_ctx);
571}
572
573/*
574
575 0 1 2 3
576 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
577 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
578 | msg-type | transaction-id |
579 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
580 | |
581 . options .
582 . (variable number and length) .
583 | |
584 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
585*/
586
587/** Decode a DHCPv6 packet
588 *
589 */
590ssize_t fr_dhcpv6_decode(TALLOC_CTX *ctx, fr_pair_list_t *out, uint8_t const *packet, size_t packet_len)
591{
592 ssize_t slen = -1;
593 uint8_t const *p, *end;
594 fr_dhcpv6_decode_ctx_t packet_ctx = {};
595 fr_pair_t *vp;
596 fr_pair_list_t tmp;
597
598 if (packet_len < DHCPV6_HDR_LEN) return 0; /* protect access to packet[0] */
599
600 /*
601 * Get the packet type.
602 */
604 if (!vp) return -1;
605
606 fr_pair_list_init(&tmp);
607 vp->vp_uint32 = packet[0];
608 fr_pair_append(&tmp, vp);
609
610 switch (packet[0]) {
613 /*
614 * Just for sanity check.
615 */
616 if (packet_len < DHCPV6_RELAY_HDR_LEN) return -1;
617
618 /*
619 * Decode the header fields.
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 + 1, 1), 1, true) < 0) {
626 goto fail;
627 }
628 fr_pair_append(&tmp, vp);
629
631 if (!vp) goto fail;
632 if (fr_value_box_from_network(vp, &vp->data, vp->vp_type, NULL,
633 &FR_DBUFF_TMP(packet + 2, 16), 16, true) < 0) {
635 goto fail;
636 }
637 fr_pair_append(&tmp, vp);
638
640 if (!vp) goto fail;
641 if (fr_value_box_from_network(vp, &vp->data, vp->vp_type, NULL,
642 &FR_DBUFF_TMP(packet + 2 + 16, 16), 16, true) < 0) {
644 goto fail;
645 }
646
647 fr_pair_append(&tmp, vp);
648
649 p = packet + DHCPV6_RELAY_HDR_LEN;
650 goto decode_options;
651
652 default:
653 break;
654 }
655
656 /*
657 * And the transaction ID.
658 */
660 if (!vp) {
661 fail:
662 fr_pair_list_free(&tmp);
663 return slen;
664 }
665
666 /*
667 * Copy 3 octets over.
668 */
669 (void) fr_pair_value_memdup(vp, packet + 1, 3, false);
670
671 fr_pair_append(&tmp, vp);
672
673 p = packet + 4;
674
675decode_options:
676 end = packet + packet_len;
677 packet_ctx.tmp_ctx = talloc_init_const("tmp");
678
679 /*
680 * The caller MUST have called fr_dhcpv6_ok() first. If
681 * he doesn't, all hell breaks loose.
682 */
683 while (p < end) {
684 slen = fr_dhcpv6_decode_option(ctx, &tmp, p, (end - p), &packet_ctx);
685 if (slen < 0) {
686 talloc_free(packet_ctx.tmp_ctx);
687 goto fail;
688 }
689 /*
690 * If slen is larger than the room in the packet,
691 * all kinds of bad things happen.
692 */
693 if (!fr_cond_assert(slen <= (end - p))) {
694 talloc_free(packet_ctx.tmp_ctx);
695 goto fail;
696 }
697
698 p += slen;
699 talloc_free_children(packet_ctx.tmp_ctx);
700 }
702
703 /*
704 * We've parsed the whole packet, return that.
705 */
706 talloc_free(packet_ctx.tmp_ctx);
707 return packet_len;
708}
709
710/** DHCPV6-specific iterator
711 *
712 */
713void *fr_dhcpv6_next_encodable(fr_dcursor_t *cursor, void *current, void *uctx)
714{
715 fr_pair_t *c = current;
716 fr_dict_t *dict = talloc_get_type_abort(uctx, fr_dict_t);
717
718 while ((c = fr_dlist_next(cursor->dlist, c))) {
719 PAIR_VERIFY(c);
720 if (c->da->dict != dict || c->da->flags.internal) continue;
721 if (c->vp_type == FR_TYPE_BOOL && !c->vp_bool) continue;
722
723 break;
724 }
725
726 return c;
727}
728
729/** Encode a DHCPv6 packet
730 *
731 */
732ssize_t fr_dhcpv6_encode(fr_dbuff_t *dbuff, uint8_t const *original, size_t length, int msg_type, fr_pair_list_t *vps)
733{
734 fr_dbuff_t frame_dbuff = FR_DBUFF(dbuff);
735 fr_pair_t *vp;
736 fr_dict_attr_t const *root;
737 ssize_t slen;
738 fr_dcursor_t cursor;
739 fr_dhcpv6_encode_ctx_t packet_ctx;
740
742
743 if (!msg_type) {
745 if (vp) msg_type = vp->vp_uint32;
746 }
747
748 if ((msg_type <= 0) || (msg_type > UINT8_MAX)) {
749 fr_strerror_printf("Invalid message type %d", msg_type);
750 return -1;
751 }
752
753 FR_DBUFF_IN_RETURN(&frame_dbuff, (uint8_t)msg_type);
754
755 switch (msg_type) {
759 if (likely(vp != NULL)) {
760 FR_VALUE_BOX_TO_NETWORK_RETURN(&frame_dbuff, &vp->data);
761 } else {
763 }
764
766 if (likely(vp != NULL)) {
767 FR_VALUE_BOX_TO_NETWORK_RETURN(&frame_dbuff, &vp->data);
768 } else {
770 }
771
773 if (likely(vp != NULL)) {
774 FR_VALUE_BOX_TO_NETWORK_RETURN(&frame_dbuff, &vp->data);
775 } else {
777 }
778 break;
779
780 default:
781 /*
782 * We can set an XID, or we can pick a random one.
783 */
785 if (vp && (vp->vp_length >= DHCPV6_TRANSACTION_ID_LEN)) {
786 FR_DBUFF_IN_MEMCPY_RETURN(&frame_dbuff, vp->vp_octets, DHCPV6_TRANSACTION_ID_LEN);
787 } else {
790 FR_DBUFF_IN_MEMCPY_RETURN(&frame_dbuff, id, sizeof(id)); /* Need 24 bits of the 32bit integer */
791 }
792 break;
793 }
794
795 /*
796 * Encode options.
797 */
798 packet_ctx.root = root;
799 packet_ctx.original = original;
800 packet_ctx.original_length = length;
801
803 while ((fr_dbuff_extend(&frame_dbuff) > 0) && (fr_dcursor_current(&cursor) != NULL)) {
804 slen = fr_dhcpv6_encode_option(&frame_dbuff, &cursor, &packet_ctx);
805 if (slen < 0) return FR_DBUFF_ERROR_OFFSET(slen, fr_dbuff_used(&frame_dbuff));
806 }
807
808 return fr_dbuff_set(dbuff, &frame_dbuff);
809}
810
811
812static 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";
813
814static void print_hex_data(FILE *fp, uint8_t const *ptr, int attrlen, int depth)
815{
816 int i;
817
818 for (i = 0; i < attrlen; i++) {
819 if ((i > 0) && ((i & 0x0f) == 0x00))
820 fprintf(fp, "%.*s", depth, tabs);
821 fprintf(fp, "%02x ", ptr[i]);
822 if ((i & 0x0f) == 0x0f) fprintf(fp, "\n");
823 }
824 if ((i & 0x0f) != 0) fprintf(fp, "\n");
825}
826
827static void dhcpv6_print_hex(FILE *fp, uint8_t const *packet, size_t packet_len, int depth)
828{
829 uint8_t const *option, *end = packet + packet_len;
830
831 if (packet_len < 4) {
832 fprintf(fp, "%.*s", depth, tabs);
833 fprintf(fp, "???:\t");
834 print_hex_data(fp, packet, packet_len, depth + 1);
835 return;
836 }
837
838 fprintf(fp, "%.*s", depth, tabs);
839 if ((packet[0] > 0) && (packet[0] < FR_DHCPV6_CODE_MAX)) {
840 fprintf(fp, "packet: %s\n", fr_dhcpv6_packet_names[packet[0]]);
841 } else {
842 fprintf(fp, "packet: %02x\n", packet[0]);
843 }
844
845 if ((packet[0] == FR_PACKET_TYPE_VALUE_RELAY_FORWARD) ||
846 (packet[0] == FR_PACKET_TYPE_VALUE_RELAY_REPLY)) {
847 if (packet_len < 34) {
848 fprintf(fp, "%.*s", depth, tabs);
849 fprintf(fp, "???:\t");
850 print_hex_data(fp, packet + 1, packet_len - 1, depth + 1);
851 return;
852 }
853
854 fprintf(fp, "%.*s", depth, tabs);
855 fprintf(fp, "hops: %02x\n", packet[1]);
856 fprintf(fp, "%.*s", depth, tabs);
857 fprintf(fp, "relay link address: ");
858 print_hex_data(fp, packet + 2, 16, depth + 1);
859
860 fprintf(fp, "%.*s", depth, tabs);
861 fprintf(fp, "peer address: ");
862 print_hex_data(fp, packet + 18, 16, depth + 1);
863 option = packet + 34;
864 } else {
865 fprintf(fp, "%.*s", depth, tabs);
866 fprintf(fp, "transaction id: ");
867 print_hex_data(fp, packet + 1, 3, depth + 1);
868 option = packet + 4;
869 }
870
871 fprintf(fp, "%.*s", depth, tabs);
872 fprintf(fp, "options\n");
873 while (option < end) {
874 uint16_t length;
875
876 if ((end - option) < 4) {
877 fprintf(fp, "%.*s", depth + 1, tabs);
878 fprintf(fp, "???:\t");
879 print_hex_data(fp, option, end - option, depth + 2);
880 break;
881 }
882
883 length = fr_nbo_to_uint16(option + 2);
884 fprintf(fp, "%.*s", depth + 1, tabs);
885 fprintf(fp, "%04x %04x\t", fr_nbo_to_uint16(option), length);
886
887 if (length > end - (option + 4)) {
888 print_hex_data(fp, option + 4, end - (option + 4), depth + 3);
889 break;
890 }
891
892 print_hex_data(fp, option + 4, length, depth + 3);
893 if ((option[0] == 0) && (option[1] == attr_relay_message->attr)) {
894 dhcpv6_print_hex(fp, option + 4, length, depth + 2);
895 }
896
897 option += 4 + length;
898 }
899
900 fprintf(fp, "\n");
901}
902
903/** Print a raw DHCP packet as hex.
904 *
905 */
906void fr_dhcpv6_print_hex(FILE *fp, uint8_t const *packet, size_t packet_len)
907{
908 dhcpv6_print_hex(fp, packet, packet_len, 0);
909}
910
912{
913 if (instance_count > 0) {
915 return 0;
916 }
917
919
921 fail:
923 return -1;
924 }
925
928 goto fail;
929 }
930
931 instantiated = true;
932 return 0;
933}
934
936{
937 if (!instantiated) return;
938
940
941 if (--instance_count > 0) return;
942
944 instantiated = false;
945}
946
948{
949 /*
950 * DNS labels are strings, but are known width.
951 */
953 if (da->type != FR_TYPE_STRING) {
954 fr_strerror_const("The 'dns_label' flag can only be used with attributes of type 'string'");
955 return false;
956 }
957
958 da->flags.is_known_width = true;
959 da->flags.length = 0;
960 }
961
962 if (da->type == FR_TYPE_ATTR) {
963 da->flags.is_known_width = true;
964 da->flags.length = 2;
965 }
966
967 if (da_is_length_field8(da)) {
968 fr_strerror_const("The 'length=uint8' flag cannot be used for DHCPv6");
969 return false;
970 }
971
972 /*
973 * "arrays" of string/octets are encoded as a 16-bit
974 * length, followed by the actual data.
975 */
976 if (da->flags.array) {
977 if ((da->type == FR_TYPE_STRING) || (da->type == FR_TYPE_OCTETS)) {
978 if (da->flags.extra && !da_is_length_field16(da)) {
979 fr_strerror_const("Invalid flags");
980 return false;
981 }
982
983 da->flags.is_known_width = true;
984 da->flags.extra = true;
985 da->flags.subtype = FLAG_LENGTH_UINT16;
986 }
987
988 if (!da->flags.is_known_width) {
989 fr_strerror_const("DHCPv6 arrays require data types which have known width");
990 return false;
991 }
992 }
993
994 /*
995 * "extra" signifies that subtype is being used by the
996 * dictionaries itself.
997 */
998 if (da->flags.extra || !da->flags.subtype) return true;
999
1000 if ((da->type == FR_TYPE_ATTR) && !da->parent->flags.is_root) {
1001 fr_strerror_const("The 'attribute' data type can only be used at the dictionary root");
1002 return false;
1003 }
1004
1005 da->flags.is_known_width = true;
1006
1007 return true;
1008}
1009
1012 .name = "dhcpv6",
1013 .default_type_size = 2,
1014 .default_type_length = 2,
1015
1016 .attr = {
1017 .valid = attr_valid,
1018 .flags = {
1019 .table = dhcpv6_flags,
1020 .table_len = NUM_ELEMENTS(dhcpv6_flags),
1021 .len = sizeof(fr_dhcpv6_attr_flags_t)
1022 }
1023 },
1024
1025 .init = fr_dhcpv6_global_init,
1026 .free = fr_dhcpv6_global_free,
1027
1028 .encode = fr_dhcpv6_encode_foreign,
1029 .decode = fr_dhcpv6_decode_foreign,
1030};
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:210
#define NUM_ELEMENTS(_t)
Definition build.h:340
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:775
#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:1012
#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:1517
#define fr_dbuff_extend(_dbuff)
Extend if no space remains.
Definition dbuff.h:713
#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:1391
#define FR_DBUFF_ERROR_OFFSET(_slen, _offset)
Generic wrapper to return an error and an offset from encoding.
Definition dbuff.h:196
#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:1594
#define FR_DBUFF(_dbuff_or_marker)
Create a new dbuff pointing to the same underlying buffer.
Definition dbuff.h:230
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
Definition dbuff.h:522
static void * fr_dcursor_current(fr_dcursor_t *cursor)
Return the item the cursor current points to.
Definition dcursor.h:337
fr_dlist_head_t * dlist
Head of the doubly linked list being iterated over.
Definition dcursor.h:92
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:141
Implementation of the DHCPv6 protocol.
uint32_t transaction_id
previous transaction ID
Definition dhcpv6.h:134
@ FR_DHCPV6_REBIND
Definition dhcpv6.h:73
@ FR_DHCPV6_RENEW
Definition dhcpv6.h:72
@ FR_DHCPV6_INFORMATION_REQUEST
Definition dhcpv6.h:78
@ 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 da_is_length_field16(_da)
Definition dict.h:174
#define fr_dict_autofree(_to_free)
Definition dict.h:915
#define da_is_length_field8(_da)
Definition dict.h:173
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2665
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:292
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:305
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:4395
#define fr_dict_autoload(_to_load)
Definition dict.h:912
#define DICT_AUTOLOAD_TERMINATOR
Definition dict.h:311
@ FLAG_LENGTH_UINT16
string / octets type is prefixed by uint16 of length
Definition dict.h:167
char const * name
name of this protocol
Definition dict.h:456
#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:435
Specifies an attribute which must be present for the module to function.
Definition dict.h:291
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:304
Protocol specific custom flag definitnion.
Definition dict.h:425
Protocol-specific callbacks in libfreeradius-PROTOCOL.
Definition dict.h:455
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:537
talloc_free(hp)
fr_dict_attr_t const * attr_packet_type
Definition base.c:91
static uint32_t instance_count
Definition base.c:44
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:2966
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:707
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:1352
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:290
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:131
static fr_dict_attr_t const * attr_relay_link_address
Definition base.c:134
static fr_dict_t const * dict_dhcpv6
Definition base.c:119
static fr_dict_attr_t const * attr_transaction_id
Definition base.c:136
static fr_dict_attr_t const * attr_relay_peer_address
Definition base.c:135
static bool instantiated
Definition base.c:39
static bool attr_valid(fr_dict_attr_t *da)
Definition base.c:897
static void print_hex_data(FILE *fp, uint8_t const *ptr, int attrlen, int depth)
Definition base.c:660
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:713
void fr_dhcpv6_global_free(void)
Definition base.c:935
fr_dict_protocol_t libfreeradius_dhcpv6_dict_protocol
Definition base.c:1011
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:827
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:911
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:590
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:556
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:432
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:906
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:732
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:409
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:424
VQP attributes.
#define fr_assert(_expr)
Definition rad_assert.h:37
uint32_t fr_rand(void)
Return a 32-bit random number.
Definition rand.c:104
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:120
#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:584
#define PAIR_VERIFY(_x)
Definition pair.h:204
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:1921
#define FR_VALUE_BOX_TO_NETWORK_RETURN(_dbuff, _value)
Definition value.h:1040
static size_t char ** out
Definition value.h:1030