The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
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: 9165b1c7fef56813e73bb0bad3de9c49c1e77cf5 $
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 case 0:
202 fr_strerror_const("Invalid Message type 0");
203 return 0;
204
205 default:
206 /*
207 * 8 bit code + 24 bits of transaction ID
208 */
209 if (packet_len < DHCPV6_HDR_LEN) {
210 fr_strerror_const("Packet is too small for DHCPv6 header");
211 return 0;
212 }
213
214 p = packet + DHCPV6_HDR_LEN;
215 allow_relay = false;
216 break;
217 }
218
219 attributes = fr_dhcpv6_options_ok(p, end, max_attributes, allow_relay, depth);
220 if (attributes < 0) return -(p - packet) + attributes;
221
222 return attributes;
223}
224
225
226/** See if the data pointed to by PTR is a valid DHCPv6 packet.
227 *
228 * @param[in] packet to check.
229 * @param[in] packet_len The size of the packet data.
230 * @param[in] max_attributes to allow in the packet.
231 * @return
232 * - True on success.
233 * - False on failure.
234 */
235bool fr_dhcpv6_ok(uint8_t const *packet, size_t packet_len, uint32_t max_attributes)
236{
237 ssize_t slen;
238
239 slen = fr_dhcpv6_ok_internal(packet, packet + packet_len, max_attributes, 0);
240 if (slen <= 0) {
241 fr_strerror_printf_push("Invalid DHCPv6 packet starting at offset %zd", -slen);
242 return false;
243 }
244
245 return true;
246}
247
248/*
249 * Return pointer to a particular option.
250 */
251uint8_t const *fr_dhcpv6_option_find(uint8_t const *start, uint8_t const *end, unsigned int option)
252{
253 uint8_t const *p = start;
254
255 while (p < end) {
256 uint16_t found;
257 uint16_t len;
258
259 if ((size_t)(end - p) < DHCPV6_OPT_HDR_LEN) return NULL;
260
261 found = DHCPV6_GET_OPTION_NUM(p);
262 len = DHCPV6_GET_OPTION_LEN(p);
263
264 if ((p + DHCPV6_OPT_HDR_LEN + len) > end) return NULL;
265
266 if (found == option) return p;
267
268 p += DHCPV6_OPT_HDR_LEN + len;
269 }
270
271 return NULL;
272}
273
274static bool duid_match(uint8_t const *option, fr_dhcpv6_decode_ctx_t const *packet_ctx)
275{
276 uint16_t len;
277
278 len = DHCPV6_GET_OPTION_LEN(option);
279 if (len != packet_ctx->duid_len) return false;
280 if (memcmp(option + 4, packet_ctx->duid, packet_ctx->duid_len) != 0) return false;
281
282 return true;
283}
284
285/** Verify a reply packet from a server to a client
286 *
287 */
288static bool verify_to_client(uint8_t const *packet, size_t packet_len, fr_dhcpv6_decode_ctx_t const *packet_ctx)
289{
290 uint32_t transaction_id;
291 uint8_t const *option;
292 uint8_t const *options = packet + 4;
293 uint8_t const *end = packet + packet_len;
294
295 switch (packet[0]) {
296 case FR_PACKET_TYPE_VALUE_ADVERTISE:
297 transaction_id = fr_nbo_to_uint24(&packet[1]);
298 if (transaction_id != packet_ctx->transaction_id) {
299 fail_tid:
300 fr_strerror_const("Transaction ID does not match");
301 return false;
302 }
303
304 if (!fr_dhcpv6_option_find(options, end, FR_SERVER_ID)) {
305 fail_sid:
306 fr_strerror_const("Packet does not contain a Server-Id option");
307 return false;
308 }
309
310 option = fr_dhcpv6_option_find(options, end, FR_CLIENT_ID);
311 if (!option) {
312 fail_cid:
313 fr_strerror_const("Packet does not contain a Client-Id option");
314 return false;
315 }
316
317 /*
318 * The DUID MUST exist.
319 */
320 if (!packet_ctx->duid) {
321 fail_duid:
322 fr_strerror_const("Packet context does not contain a DUID");
323 return false;
324 }
325
326 check_duid:
327 if (!duid_match(option, packet_ctx)) {
328 fail_match:
329 fr_strerror_const("DUID in packet does not match our DUID");
330 return false;
331 }
332 return true;
333
334 case FR_PACKET_TYPE_VALUE_REPLY:
335 transaction_id = fr_nbo_to_uint24(&packet[1]);
336 if (transaction_id != packet_ctx->transaction_id) goto fail_tid;
337
338 if (!fr_dhcpv6_option_find(options, end, FR_SERVER_ID)) goto fail_sid;
339
340 /*
341 * It's OK to not have a client ID in the reply if we didn't send one.
342 */
343 option = fr_dhcpv6_option_find(options, end, FR_CLIENT_ID);
344 if (!option) {
345 if (!packet_ctx->duid) return true;
346 goto fail_cid;
347 }
348 goto check_duid;
349
350 case FR_PACKET_TYPE_VALUE_RECONFIGURE:
351 if (!fr_dhcpv6_option_find(options, end, FR_SERVER_ID)) goto fail_sid;
352
353 option = fr_dhcpv6_option_find(options, end, FR_CLIENT_ID);
354 if (!option) goto fail_cid;
355
356 /*
357 * The DUID MUST exist.
358 */
359 if (!packet_ctx->duid) goto fail_duid;
360 if (!duid_match(option, packet_ctx)) goto fail_match;
361
362 option = fr_dhcpv6_option_find(options, end, FR_RECONF_MSG);
363 if (!option) {
364 fr_strerror_const("Packet does not contain a Reconf-Msg option");
365 return false;
366 }
367
368 /*
369 * Check reconfigure message type, and reject
370 * if it is not valid.
371 */
372 switch (option[4]) {
373 case FR_DHCPV6_RENEW:
374 case FR_DHCPV6_REBIND:
376 break;
377 default:
378 fr_strerror_const("Invalid Reconf-Msg option value");
379 return false;
380 }
381 /*
382 * @todo - check for authentication option and
383 * verify it.
384 */
385 break;
386
388 if (packet_len < DHCPV6_RELAY_HDR_LEN) {
389 fr_strerror_const("Relay-Reply message is too small");
390 return false;
391 }
392
393 options += (DHCPV6_RELAY_HDR_LEN - 4); /* we assumed it was a normal packet above */
394 option = fr_dhcpv6_option_find(options, end, FR_RELAY_MESSAGE);
395 if (!option) {
396 fr_strerror_const("Packet does not contain a Relay-Message option");
397 return false;
398 }
399 return verify_to_client(option + 4, DHCPV6_GET_OPTION_LEN(option), packet_ctx);
400
402 transaction_id = fr_nbo_to_uint24(&packet[1]);
403 if (transaction_id != packet_ctx->transaction_id) goto fail_tid;
404
405 if (!fr_dhcpv6_option_find(options, end, FR_SERVER_ID)) goto fail_sid;
406
407 option = fr_dhcpv6_option_find(options, end, FR_CLIENT_ID);
408 if (!option) goto fail_cid;
409
410 /*
411 * The DUID MUST exist.
412 */
413 if (!packet_ctx->duid) goto fail_duid;
414 if (!duid_match(option, packet_ctx)) goto fail_match;
415 break;
416
417 case FR_PACKET_TYPE_VALUE_REQUEST:
418 case FR_PACKET_TYPE_VALUE_CONFIRM:
419 case FR_PACKET_TYPE_VALUE_RENEW:
420 case FR_PACKET_TYPE_VALUE_REBIND:
421 case FR_PACKET_TYPE_VALUE_RELEASE:
422 case FR_PACKET_TYPE_VALUE_DECLINE:
423 case FR_PACKET_TYPE_VALUE_INFORMATION_REQUEST:
424 default:
425 fr_strerror_const("Invalid message type sent to client");
426 return false;
427 }
428
429 return true;
430}
431
432
433/** Verify a packet from a client to a server
434 *
435 */
436static bool verify_from_client(uint8_t const *packet, size_t packet_len, fr_dhcpv6_decode_ctx_t const *packet_ctx)
437{
438 uint8_t const *option;
439 uint8_t const *options = packet + 4;
440 uint8_t const *end = packet + packet_len;
441
442 /*
443 * Servers MUST have a DUID
444 */
445 if (!packet_ctx->duid) {
446 fr_strerror_const("Packet context does not contain a DUID");
447 return false;
448 }
449
450 switch (packet[0]) {
451 case FR_PACKET_TYPE_VALUE_SOLICIT:
452 case FR_PACKET_TYPE_VALUE_CONFIRM:
453 case FR_PACKET_TYPE_VALUE_REBIND:
454 if (!fr_dhcpv6_option_find(options, end, FR_CLIENT_ID)) {
455 fail_cid:
456 fr_strerror_const("Packet does not contain a Client-Id option");
457 return false;
458 }
459
460 if (fr_dhcpv6_option_find(options, end, FR_SERVER_ID)) {
461 fr_strerror_const("Packet contains a Server-Id option");
462 return false;
463 }
464 break;
465
466 case FR_PACKET_TYPE_VALUE_REQUEST:
467 case FR_PACKET_TYPE_VALUE_RENEW:
468 case FR_PACKET_TYPE_VALUE_DECLINE:
469 case FR_PACKET_TYPE_VALUE_RELEASE:
470 if (!fr_dhcpv6_option_find(options, end, FR_CLIENT_ID)) goto fail_cid;
471
472 option = fr_dhcpv6_option_find(options, end, FR_SERVER_ID);
473 if (!option) {
474 fr_strerror_const("Packet does not contain a Server-Id option");
475 return false;
476 }
477
478 if (!duid_match(option, packet_ctx)) {
479 fail_match:
480 fr_strerror_const("DUID in packet does not match our DUID");
481 return false;
482 }
483 break;
484
485 case FR_PACKET_TYPE_VALUE_INFORMATION_REQUEST:
486 option = fr_dhcpv6_option_find(options, end, FR_SERVER_ID);
487 if (option && !duid_match(option, packet_ctx)) goto fail_match;
488
489 /*
490 * IA options are forbidden.
491 */
492 if (fr_dhcpv6_option_find(options, end, FR_IA_NA)) {
493 fr_strerror_const("Packet contains an IA-NA option");
494 return false;
495 }
496 if (fr_dhcpv6_option_find(options, end, FR_IA_TA)) {
497 fr_strerror_const("Packet contains an IA-TA option");
498 return false;
499 }
500 if (fr_dhcpv6_option_find(options, end, FR_IA_ADDR)) {
501 fr_strerror_const("Packet contains an IA-Addr option");
502 return false;
503 }
504 break;
505
507 if (packet_len < DHCPV6_RELAY_HDR_LEN) {
508 fr_strerror_const("Relay-Forward message is too small");
509 return false;
510 }
511
512 options += (DHCPV6_RELAY_HDR_LEN - 4); /* we assumed it was a normal packet above */
513 option = fr_dhcpv6_option_find(options, end, FR_RELAY_MESSAGE);
514 if (!option) {
515 fr_strerror_const("Packet does not contain a Relay-Message option");
516 return false;
517 }
518
519 return verify_from_client(option + 4, DHCPV6_GET_OPTION_LEN(option), packet_ctx);
520
521 case FR_PACKET_TYPE_VALUE_LEASE_QUERY:
522 if (!fr_dhcpv6_option_find(options, end, FR_CLIENT_ID)) goto fail_cid;
523
524 /*
525 * Server-ID is a SHOULD, but if it exists, it
526 * MUST match.
527 */
528 option = fr_dhcpv6_option_find(options, end, FR_SERVER_ID);
529 if (option && !duid_match(option, packet_ctx)) goto fail_match;
530
531 option = fr_dhcpv6_option_find(options, end, FR_LEASE_QUERY);
532 if (!option) {
533 fr_strerror_const("Packet does not contain a Lease-Query option");
534 return false;
535 }
536 break;
537
538 case FR_PACKET_TYPE_VALUE_ADVERTISE:
539 case FR_PACKET_TYPE_VALUE_REPLY:
540 case FR_PACKET_TYPE_VALUE_RECONFIGURE:
541 default:
542 fr_strerror_const("Invalid message type sent to server");
543 return false;
544 }
545 return true;
546}
547
548/** Verify the packet under some various circumstances
549 *
550 * @param[in] packet to check.
551 * @param[in] packet_len The size of the packet data.
552 * @param[in] packet_ctx The expected packet_ctx
553 * @param[in] from_server true for packets from a server, false for packets from a client.
554 * @return
555 * - True on success.
556 * - False on failure.
557 *
558 * fr_dhcpv6_ok() SHOULD be called before calling this function.
559 */
560bool fr_dhcpv6_verify(uint8_t const *packet, size_t packet_len, fr_dhcpv6_decode_ctx_t const *packet_ctx,
561 bool from_server)
562{
563 if (packet_len < DHCPV6_HDR_LEN) return false;
564
565 /*
566 * We support up to lease query reply.
567 */
568 if ((packet[0] == 0) || (packet[0] > FR_DHCPV6_LEASE_QUERY_REPLY)) return false;
569
570 if (!packet_ctx->duid) return false;
571
572 if (from_server) return verify_to_client(packet, packet_len, packet_ctx);
573
574 return verify_from_client(packet, packet_len, packet_ctx);
575}
576
577/*
578
579 0 1 2 3
580 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
581 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
582 | msg-type | transaction-id |
583 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
584 | |
585 . options .
586 . (variable number and length) .
587 | |
588 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
589*/
590
591/** Decode a DHCPv6 packet
592 *
593 */
594ssize_t fr_dhcpv6_decode(TALLOC_CTX *ctx, fr_pair_list_t *out, uint8_t const *packet, size_t packet_len)
595{
596 ssize_t slen = -1;
597 uint8_t const *p, *end;
598 fr_dhcpv6_decode_ctx_t packet_ctx = {};
599 fr_pair_t *vp;
600 fr_pair_list_t tmp;
601
602 if (packet_len < DHCPV6_HDR_LEN) return 0; /* protect access to packet[0] */
603
604 /*
605 * Get the packet type.
606 */
608 if (!vp) return -1;
609
610 fr_pair_list_init(&tmp);
611 vp->vp_uint32 = packet[0];
612 fr_pair_append(&tmp, vp);
613
614 switch (packet[0]) {
617 /*
618 * Just for sanity check.
619 */
620 if (packet_len < DHCPV6_RELAY_HDR_LEN) return -1;
621
622 /*
623 * Decode the header fields.
624 */
626 if (!vp) goto fail;
627 if (fr_value_box_from_network(vp, &vp->data, vp->vp_type, NULL,
628 &FR_DBUFF_TMP(packet + 1, 1), 1, true) < 0) {
630 goto fail;
631 }
632 fr_pair_append(&tmp, vp);
633
635 if (!vp) goto fail;
636 if (fr_value_box_from_network(vp, &vp->data, vp->vp_type, NULL,
637 &FR_DBUFF_TMP(packet + 2, 16), 16, true) < 0) {
639 goto fail;
640 }
641 fr_pair_append(&tmp, vp);
642
644 if (!vp) goto fail;
645 if (fr_value_box_from_network(vp, &vp->data, vp->vp_type, NULL,
646 &FR_DBUFF_TMP(packet + 2 + 16, 16), 16, true) < 0) {
648 goto fail;
649 }
650
651 fr_pair_append(&tmp, vp);
652
653 p = packet + DHCPV6_RELAY_HDR_LEN;
654 goto decode_options;
655
656 default:
657 break;
658 }
659
660 /*
661 * And the transaction ID.
662 */
664 if (!vp) {
665 fail:
666 fr_pair_list_free(&tmp);
667 return slen;
668 }
669
670 /*
671 * Copy 3 octets over.
672 */
673 (void) fr_pair_value_memdup(vp, packet + 1, 3, false);
674
675 fr_pair_append(&tmp, vp);
676
677 p = packet + 4;
678
679decode_options:
680 end = packet + packet_len;
681 packet_ctx.tmp_ctx = talloc_init_const("tmp");
682
683 /*
684 * The caller MUST have called fr_dhcpv6_ok() first. If
685 * he doesn't, all hell breaks loose.
686 */
687 while (p < end) {
688 slen = fr_dhcpv6_decode_option(ctx, &tmp, p, (end - p), &packet_ctx);
689 if (slen < 0) {
690 talloc_free(packet_ctx.tmp_ctx);
691 goto fail;
692 }
693 /*
694 * If slen is larger than the room in the packet,
695 * all kinds of bad things happen.
696 */
697 if (!fr_cond_assert(slen <= (end - p))) {
698 talloc_free(packet_ctx.tmp_ctx);
699 goto fail;
700 }
701
702 p += slen;
703 talloc_free_children(packet_ctx.tmp_ctx);
704 }
706
707 /*
708 * We've parsed the whole packet, return that.
709 */
710 talloc_free(packet_ctx.tmp_ctx);
711 return packet_len;
712}
713
714/** DHCPV6-specific iterator
715 *
716 */
717void *fr_dhcpv6_next_encodable(fr_dcursor_t *cursor, void *current, void *uctx)
718{
719 fr_pair_t *c = current;
720 fr_dict_t *dict = talloc_get_type_abort(uctx, fr_dict_t);
721
722 while ((c = fr_dlist_next(cursor->dlist, c))) {
723 PAIR_VERIFY(c);
724 if (c->da->dict != dict || c->da->flags.internal) continue;
725 if (c->vp_type == FR_TYPE_BOOL && !c->vp_bool) continue;
726
727 break;
728 }
729
730 return c;
731}
732
733/** Encode a DHCPv6 packet
734 *
735 */
736ssize_t fr_dhcpv6_encode(fr_dbuff_t *dbuff, uint8_t const *original, size_t length, int msg_type, fr_pair_list_t *vps)
737{
738 fr_dbuff_t frame_dbuff = FR_DBUFF(dbuff);
739 fr_pair_t *vp;
740 fr_dict_attr_t const *root;
741 ssize_t slen;
742 fr_dcursor_t cursor;
743 fr_dhcpv6_encode_ctx_t packet_ctx;
744
746
747 if (!msg_type) {
749 if (vp) msg_type = vp->vp_uint32;
750 }
751
752 if ((msg_type <= 0) || (msg_type > UINT8_MAX)) {
753 fr_strerror_printf("Invalid message type %d", msg_type);
754 return -1;
755 }
756
757 FR_DBUFF_IN_RETURN(&frame_dbuff, (uint8_t)msg_type);
758
759 switch (msg_type) {
763 if (likely(vp != NULL)) {
764 FR_VALUE_BOX_TO_NETWORK_RETURN(&frame_dbuff, &vp->data);
765 } else {
767 }
768
770 if (likely(vp != NULL)) {
771 FR_VALUE_BOX_TO_NETWORK_RETURN(&frame_dbuff, &vp->data);
772 } else {
774 }
775
777 if (likely(vp != NULL)) {
778 FR_VALUE_BOX_TO_NETWORK_RETURN(&frame_dbuff, &vp->data);
779 } else {
781 }
782 break;
783
784 default:
785 /*
786 * We can set an XID, or we can pick a random one.
787 */
789 if (vp && (vp->vp_length >= DHCPV6_TRANSACTION_ID_LEN)) {
790 FR_DBUFF_IN_MEMCPY_RETURN(&frame_dbuff, vp->vp_octets, DHCPV6_TRANSACTION_ID_LEN);
791 } else {
794 FR_DBUFF_IN_MEMCPY_RETURN(&frame_dbuff, id, sizeof(id)); /* Need 24 bits of the 32bit integer */
795 }
796 break;
797 }
798
799 /*
800 * Encode options.
801 */
802 packet_ctx.root = root;
803 packet_ctx.original = original;
804 packet_ctx.original_length = length;
805
807 while ((fr_dbuff_extend(&frame_dbuff) > 0) && (fr_dcursor_current(&cursor) != NULL)) {
808 slen = fr_dhcpv6_encode_option(&frame_dbuff, &cursor, &packet_ctx);
809 if (slen < 0) return FR_DBUFF_ERROR_OFFSET(slen, fr_dbuff_used(&frame_dbuff));
810 }
811
812 return fr_dbuff_set(dbuff, &frame_dbuff);
813}
814
815
816static 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";
817
818static void print_hex_data(FILE *fp, uint8_t const *ptr, int attrlen, int depth)
819{
820 int i;
821
822 for (i = 0; i < attrlen; i++) {
823 if ((i > 0) && ((i & 0x0f) == 0x00))
824 fprintf(fp, "%.*s", depth, tabs);
825 fprintf(fp, "%02x ", ptr[i]);
826 if ((i & 0x0f) == 0x0f) fprintf(fp, "\n");
827 }
828 if ((i & 0x0f) != 0) fprintf(fp, "\n");
829}
830
831static void dhcpv6_print_hex(FILE *fp, uint8_t const *packet, size_t packet_len, int depth)
832{
833 uint8_t const *option, *end = packet + packet_len;
834
835 if (packet_len < 4) {
836 fprintf(fp, "%.*s", depth, tabs);
837 fprintf(fp, "???:\t");
838 print_hex_data(fp, packet, packet_len, depth + 1);
839 return;
840 }
841
842 fprintf(fp, "%.*s", depth, tabs);
843 if ((packet[0] > 0) && (packet[0] < FR_DHCPV6_CODE_MAX)) {
844 fprintf(fp, "packet: %s\n", fr_dhcpv6_packet_names[packet[0]]);
845 } else {
846 fprintf(fp, "packet: %02x\n", packet[0]);
847 }
848
849 if ((packet[0] == FR_PACKET_TYPE_VALUE_RELAY_FORWARD) ||
850 (packet[0] == FR_PACKET_TYPE_VALUE_RELAY_REPLY)) {
851 if (packet_len < 34) {
852 fprintf(fp, "%.*s", depth, tabs);
853 fprintf(fp, "???:\t");
854 print_hex_data(fp, packet + 1, packet_len - 1, depth + 1);
855 return;
856 }
857
858 fprintf(fp, "%.*s", depth, tabs);
859 fprintf(fp, "hops: %02x\n", packet[1]);
860 fprintf(fp, "%.*s", depth, tabs);
861 fprintf(fp, "relay link address: ");
862 print_hex_data(fp, packet + 2, 16, depth + 1);
863
864 fprintf(fp, "%.*s", depth, tabs);
865 fprintf(fp, "peer address: ");
866 print_hex_data(fp, packet + 18, 16, depth + 1);
867 option = packet + 34;
868 } else {
869 fprintf(fp, "%.*s", depth, tabs);
870 fprintf(fp, "transaction id: ");
871 print_hex_data(fp, packet + 1, 3, depth + 1);
872 option = packet + 4;
873 }
874
875 fprintf(fp, "%.*s", depth, tabs);
876 fprintf(fp, "options\n");
877 while (option < end) {
878 uint16_t length;
879
880 if ((end - option) < 4) {
881 fprintf(fp, "%.*s", depth + 1, tabs);
882 fprintf(fp, "???:\t");
883 print_hex_data(fp, option, end - option, depth + 2);
884 break;
885 }
886
887 length = fr_nbo_to_uint16(option + 2);
888 fprintf(fp, "%.*s", depth + 1, tabs);
889 fprintf(fp, "%04x %04x\t", fr_nbo_to_uint16(option), length);
890
891 if (length > end - (option + 4)) {
892 print_hex_data(fp, option + 4, end - (option + 4), depth + 3);
893 break;
894 }
895
896 print_hex_data(fp, option + 4, length, depth + 3);
897 if ((option[0] == 0) && (option[1] == attr_relay_message->attr)) {
898 dhcpv6_print_hex(fp, option + 4, length, depth + 2);
899 }
900
901 option += 4 + length;
902 }
903
904 fprintf(fp, "\n");
905}
906
907/** Print a raw DHCP packet as hex.
908 *
909 */
910void fr_dhcpv6_print_hex(FILE *fp, uint8_t const *packet, size_t packet_len)
911{
912 dhcpv6_print_hex(fp, packet, packet_len, 0);
913}
914
916{
917 if (instance_count > 0) {
919 return 0;
920 }
921
923
925 fail:
927 return -1;
928 }
929
932 goto fail;
933 }
934
935 instantiated = true;
936 return 0;
937}
938
940{
941 if (!instantiated) return;
942
944
945 if (--instance_count > 0) return;
946
948 instantiated = false;
949}
950
952{
953 /*
954 * DNS labels are strings, but are known width.
955 */
957 if (da->type != FR_TYPE_STRING) {
958 fr_strerror_const("The 'dns_label' flag can only be used with attributes of type 'string'");
959 return false;
960 }
961
962 da->flags.is_known_width = true;
963 da->flags.length = 0;
964 }
965
966 if (da->type == FR_TYPE_ATTR) {
967 da->flags.is_known_width = true;
968 da->flags.length = 2;
969 }
970
971 if (da_is_length_field8(da)) {
972 fr_strerror_const("The 'length=uint8' flag cannot be used for DHCPv6");
973 return false;
974 }
975
976 /*
977 * "arrays" of string/octets are encoded as a 16-bit
978 * length, followed by the actual data.
979 */
980 if (da->flags.array) {
981 if ((da->type == FR_TYPE_STRING) || (da->type == FR_TYPE_OCTETS)) {
982 if (da->flags.extra && !da_is_length_field16(da)) {
983 fr_strerror_const("Invalid flags");
984 return false;
985 }
986
987 da->flags.is_known_width = true;
988 da->flags.extra = true;
989 da->flags.subtype = FLAG_LENGTH_UINT16;
990 }
991
992 if (!da->flags.is_known_width) {
993 fr_strerror_const("DHCPv6 arrays require data types which have known width");
994 return false;
995 }
996 }
997
998 /*
999 * "extra" signifies that subtype is being used by the
1000 * dictionaries itself.
1001 */
1002 if (da->flags.extra || !da->flags.subtype) return true;
1003
1004 if ((da->type == FR_TYPE_ATTR) && !da->parent->flags.is_root) {
1005 fr_strerror_const("The 'attribute' data type can only be used at the dictionary root");
1006 return false;
1007 }
1008
1009 da->flags.is_known_width = true;
1010
1011 return true;
1012}
1013
1016 .name = "dhcpv6",
1017 .default_type_size = 2,
1018 .default_type_length = 2,
1019
1020 .attr = {
1021 .valid = attr_valid,
1022 .flags = {
1023 .table = dhcpv6_flags,
1024 .table_len = NUM_ELEMENTS(dhcpv6_flags),
1025 .len = sizeof(fr_dhcpv6_attr_flags_t)
1026 }
1027 },
1028
1029 .init = fr_dhcpv6_global_init,
1030 .free = fr_dhcpv6_global_free,
1031
1032 .encode = fr_dhcpv6_encode_foreign,
1033 .decode = fr_dhcpv6_decode_foreign,
1034};
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:228
#define NUM_ELEMENTS(_t)
Definition build.h:358
static const char * tabs
Definition command.c:1589
fr_dict_t * dict
Definition common.c:31
#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:131
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:2639
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
@ FLAG_LENGTH_UINT16
string / octets type is prefixed by uint16 of length
Definition dict.h:167
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:4372
#define fr_dict_autoload(_to_load)
Definition dict.h:912
#define DICT_AUTOLOAD_TERMINATOR
Definition dict.h:311
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:2962
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 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:274
void * fr_dhcpv6_next_encodable(fr_dcursor_t *cursor, void *current, void *uctx)
DHCPV6-specific iterator.
Definition base.c:717
void fr_dhcpv6_global_free(void)
Definition base.c:939
fr_dict_protocol_t libfreeradius_dhcpv6_dict_protocol
Definition base.c:1015
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:831
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:235
uint8_t const * fr_dhcpv6_option_find(uint8_t const *start, uint8_t const *end, unsigned int option)
Definition base.c:251
int fr_dhcpv6_global_init(void)
Definition base.c:915
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:594
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:560
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:288
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:436
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:910
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:736
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:414
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:429
static bool attr_valid(fr_dict_attr_t *da)
Definition base.c:484
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:127
#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:1892
#define FR_VALUE_BOX_TO_NETWORK_RETURN(_dbuff, _value)
Definition value.h:1040
static size_t char ** out
Definition value.h:1030