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: 440c45bcf9131b1c865bac6cc7282e46a8f8d26a $
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
57
60 { .out = &attr_packet_type, .name = "Packet-Type", .type = FR_TYPE_UINT32, .dict = &dict_dhcpv6 },
61 { .out = &attr_transaction_id, .name = "Transaction-Id", .type = FR_TYPE_OCTETS, .dict = &dict_dhcpv6 },
62 { .out = &attr_hop_count, .name = "Hop-Count", .type = FR_TYPE_UINT8, .dict = &dict_dhcpv6 },
63 { .out = &attr_relay_link_address, .name = "Relay-Link-Address", .type = FR_TYPE_IPV6_ADDR, .dict = &dict_dhcpv6 },
64 { .out = &attr_relay_peer_address, .name = "Relay-Peer-Address", .type = FR_TYPE_IPV6_ADDR, .dict = &dict_dhcpv6 },
65 { .out = &attr_relay_message, .name = "Relay-Message", .type = FR_TYPE_GROUP, .dict = &dict_dhcpv6 },
66 { .out = &attr_option_request, .name = "Option-Request", .type = FR_TYPE_ATTR, .dict = &dict_dhcpv6 },
67 { .out = &attr_auth, .name = "Auth", .type = FR_TYPE_STRUCT, .dict = &dict_dhcpv6 },
69};
70
71/*
72 * grep VALUE share/dictionary/dhcpv6/dictionary.freeradius.internal | awk '{print "[" $4 "] = \"" $3 "\"," }'
73 */
75 [0] = "invalid",
76 [FR_PACKET_TYPE_VALUE_SOLICIT] = "Solicit",
77 [FR_PACKET_TYPE_VALUE_ADVERTISE] = "Advertise",
78 [FR_PACKET_TYPE_VALUE_REQUEST] = "Request",
79 [FR_PACKET_TYPE_VALUE_CONFIRM] = "Confirm",
80 [FR_PACKET_TYPE_VALUE_RENEW] = "Renew",
81 [FR_PACKET_TYPE_VALUE_REBIND] = "Rebind",
82 [FR_PACKET_TYPE_VALUE_REPLY] = "Reply",
83 [FR_PACKET_TYPE_VALUE_RELEASE] = "Release",
84 [FR_PACKET_TYPE_VALUE_DECLINE] = "Decline",
85 [FR_PACKET_TYPE_VALUE_RECONFIGURE] = "Reconfigure",
86 [FR_PACKET_TYPE_VALUE_INFORMATION_REQUEST] = "Information-Request",
87 [FR_PACKET_TYPE_VALUE_RELAY_FORWARD] = "Relay-Forward",
88 [FR_PACKET_TYPE_VALUE_RELAY_REPLY] = "Relay-Reply",
89 [FR_PACKET_TYPE_VALUE_LEASE_QUERY] = "Lease-Query",
90 [FR_PACKET_TYPE_VALUE_LEASE_QUERY_REPLY] = "Lease-Query-Reply",
91 [FR_PACKET_TYPE_VALUE_LEASE_QUERY_DONE] = "Lease-Query-Done",
92 [FR_PACKET_TYPE_VALUE_LEASE_QUERY_DATA] = "Lease-Query-Data",
93 [FR_PACKET_TYPE_VALUE_RECONFIGURE_REQUEST] = "Reconfigure-Request",
94 [FR_PACKET_TYPE_VALUE_RECONFIGURE_REPLY] = "Reconfigure-Reply",
95 [FR_PACKET_TYPE_VALUE_DHCPV4_QUERY] = "DHCPv4-Query",
96 [FR_PACKET_TYPE_VALUE_DHCPV4_RESPONSE] = "DHCPv4-Response",
97 [FR_PACKET_TYPE_VALUE_ACTIVE_LEASE_QUERY] = "Active-Lease-Query",
98 [FR_PACKET_TYPE_VALUE_START_TLS] = "Start-TLS",
99 [FR_PACKET_TYPE_VALUE_BIND_UPDATE] = "Bind-Update",
100 [FR_PACKET_TYPE_VALUE_BIND_REPLY] = "Bind-Reply",
101 [FR_PACKET_TYPE_VALUE_POOL_REQUEST] = "Pool-Request",
102 [FR_PACKET_TYPE_VALUE_POOL_RESPONSE] = "Pool-Response",
103 [FR_PACKET_TYPE_VALUE_UPDATE_REQUEST] = "Update-Request",
104 [FR_PACKET_TYPE_VALUE_UPDATE_REQUEST_ALL] = "Update-Request-All",
105 [FR_PACKET_TYPE_VALUE_UPDATE_DONE] = "Update-Done",
106 [FR_PACKET_TYPE_VALUE_CONNECT] = "Connect",
107 [FR_PACKET_TYPE_VALUE_CONNECT_REPLY] = "Connect-Reply",
108 [FR_PACKET_TYPE_VALUE_DISCONNECT] = "Disconnect",
109 [FR_PACKET_TYPE_VALUE_STATE] = "State",
110 [FR_PACKET_TYPE_VALUE_CONTACT] = "Contact"
111};
112
115
117 { L("dns_label"), { .func = dict_flag_dns_label } },
118 { L("partial_dns_label"), { .func = dict_flag_partial_dns_label } }
119};
120
121static ssize_t fr_dhcpv6_ok_internal(uint8_t const *packet, uint8_t const *end, size_t max_attributes, int depth);
122
123static ssize_t fr_dhcpv6_options_ok(uint8_t const *packet, uint8_t const *end, size_t max_attributes,
124 bool allow_relay, int depth)
125{
126 size_t attributes;
127 uint8_t const *p;
128
129 attributes = 0;
130 p = packet;
131
132 while (p < end) {
133 uint16_t len;
134
135 if ((size_t)(end - p) < DHCPV6_OPT_HDR_LEN) {
136 fr_strerror_const("Not enough room for option header");
137 return -(p - packet);
138 }
139
140 len = DHCPV6_GET_OPTION_LEN(p);
141 if ((size_t)(end - p) < (DHCPV6_OPT_HDR_LEN + len)) {
142 fr_strerror_const("Option length overflows the packet");
143 return -(p - packet);
144 }
145
146 attributes++;
147 if (attributes > (size_t) max_attributes) {
148 fr_strerror_const("Too many attributes");
149 return -(p - packet);
150 }
151
152 /*
153 * Recurse into the Relay-Message attribute, but
154 * only if the outer packet was a relayed message.
155 */
156 if (allow_relay && (p[0] == 0) && (p[1] == attr_relay_message->attr)) {
157 ssize_t child;
158
159 /*
160 * Recurse to check the encapsulated packet.
161 */
162 child = fr_dhcpv6_ok_internal(p + 4, p + 4 + len, max_attributes - attributes, depth + 1);
163 if (child <= 0) return -((p + 4) - packet) + child;
164
165 attributes += child;
166 }
167
168 p += DHCPV6_OPT_HDR_LEN + len;
169 }
170
171 return attributes;
172}
173
174static ssize_t fr_dhcpv6_ok_internal(uint8_t const *packet, uint8_t const *end, size_t max_attributes, int depth)
175{
176 uint8_t const *p;
177 ssize_t attributes;
178 bool allow_relay;
179 size_t packet_len = end - packet;
180
181 if (end == packet) {
182 fr_strerror_const("Packet is empty");
183 return 0;
184 }
185
187 fr_strerror_const("Too many layers forwarded packets");
188 return 0;
189 }
190
191 switch (packet[0]) {
194 if (packet_len < DHCPV6_RELAY_HDR_LEN) {
195 fr_strerror_const("Packet is too small for relay header");
196 return 0;
197 }
198
199 p = packet + DHCPV6_RELAY_HDR_LEN;
200 allow_relay = true;
201 break;
202
203 case 0:
204 fr_strerror_const("Invalid Message type 0");
205 return 0;
206
207 default:
208 /*
209 * 8 bit code + 24 bits of transaction ID
210 */
211 if (packet_len < DHCPV6_HDR_LEN) {
212 fr_strerror_const("Packet is too small for DHCPv6 header");
213 return 0;
214 }
215
216 p = packet + DHCPV6_HDR_LEN;
217 allow_relay = false;
218 break;
219 }
220
221 attributes = fr_dhcpv6_options_ok(p, end, max_attributes, allow_relay, depth);
222 if (attributes < 0) return -(p - packet) + attributes;
223
224 return attributes;
225}
226
227
228/** See if the data pointed to by PTR is a valid DHCPv6 packet.
229 *
230 * @param[in] packet to check.
231 * @param[in] packet_len The size of the packet data.
232 * @param[in] max_attributes to allow in the packet.
233 * @return
234 * - True on success.
235 * - False on failure.
236 */
237bool fr_dhcpv6_ok(uint8_t const *packet, size_t packet_len, uint32_t max_attributes)
238{
239 ssize_t slen;
240
241 slen = fr_dhcpv6_ok_internal(packet, packet + packet_len, max_attributes, 0);
242 if (slen <= 0) {
243 fr_strerror_printf_push("Invalid DHCPv6 packet starting at offset %zd", -slen);
244 return false;
245 }
246
247 return true;
248}
249
250/*
251 * Return pointer to a particular option.
252 */
253uint8_t const *fr_dhcpv6_option_find(uint8_t const *start, uint8_t const *end, unsigned int option)
254{
255 uint8_t const *p = start;
256
257 while (p < end) {
258 uint16_t found;
259 uint16_t len;
260
261 if ((size_t)(end - p) < DHCPV6_OPT_HDR_LEN) return NULL;
262
263 found = DHCPV6_GET_OPTION_NUM(p);
264 len = DHCPV6_GET_OPTION_LEN(p);
265
266 if ((p + DHCPV6_OPT_HDR_LEN + len) > end) return NULL;
267
268 if (found == option) return p;
269
270 p += DHCPV6_OPT_HDR_LEN + len;
271 }
272
273 return NULL;
274}
275
276static bool duid_match(uint8_t const *option, fr_dhcpv6_decode_ctx_t const *packet_ctx)
277{
278 uint16_t len;
279
280 len = DHCPV6_GET_OPTION_LEN(option);
281 if (len != packet_ctx->duid_len) return false;
282 if (memcmp(option + 4, packet_ctx->duid, packet_ctx->duid_len) != 0) return false;
283
284 return true;
285}
286
287/** Verify a reply packet from a server to a client
288 *
289 */
290static bool verify_to_client(uint8_t const *packet, size_t packet_len, fr_dhcpv6_decode_ctx_t const *packet_ctx)
291{
292 uint32_t transaction_id;
293 uint8_t const *option;
294 uint8_t const *options = packet + 4;
295 uint8_t const *end = packet + packet_len;
296
297 switch (packet[0]) {
298 case FR_PACKET_TYPE_VALUE_ADVERTISE:
299 transaction_id = fr_nbo_to_uint24(&packet[1]);
300 if (transaction_id != packet_ctx->transaction_id) {
301 fail_tid:
302 fr_strerror_const("Transaction ID does not match");
303 return false;
304 }
305
306 if (!fr_dhcpv6_option_find(options, end, FR_SERVER_ID)) {
307 fail_sid:
308 fr_strerror_const("Packet does not contain a Server-Id option");
309 return false;
310 }
311
312 option = fr_dhcpv6_option_find(options, end, FR_CLIENT_ID);
313 if (!option) {
314 fail_cid:
315 fr_strerror_const("Packet does not contain a Client-Id option");
316 return false;
317 }
318
319 /*
320 * The DUID MUST exist.
321 */
322 if (!packet_ctx->duid) {
323 fail_duid:
324 fr_strerror_const("Packet context does not contain a DUID");
325 return false;
326 }
327
328 check_duid:
329 if (!duid_match(option, packet_ctx)) {
330 fail_match:
331 fr_strerror_const("DUID in packet does not match our DUID");
332 return false;
333 }
334 return true;
335
336 case FR_PACKET_TYPE_VALUE_REPLY:
337 transaction_id = fr_nbo_to_uint24(&packet[1]);
338 if (transaction_id != packet_ctx->transaction_id) goto fail_tid;
339
340 if (!fr_dhcpv6_option_find(options, end, FR_SERVER_ID)) goto fail_sid;
341
342 /*
343 * It's OK to not have a client ID in the reply if we didn't send one.
344 */
345 option = fr_dhcpv6_option_find(options, end, FR_CLIENT_ID);
346 if (!option) {
347 if (!packet_ctx->duid) return true;
348 goto fail_cid;
349 }
350 goto check_duid;
351
352 case FR_PACKET_TYPE_VALUE_RECONFIGURE:
353#if 1
354 /*
355 * See RFC 8415 Section 18 (option format), and Section 20 (functionality).
356 *
357 * In order to support sending Reconfigure messages, we have to support the Auth option.
358 * Except that common clients don't support it:
359 *
360 * https://www.cisco.com/c/en/us/td/docs/routers/asr920/configuration/guide/ipaddr-dhcp/17-1-1/b-dhcp-xe-17-1-asr920/b-dhcp-xe-17-1-asr920_chapter_0101.html
361 *
362 * "DHCPv6 Reconfigure message type is not be supported on the Cisco ASR920 Series routers."
363 *
364 * So we don't support it, either. The rest of the comments here are notes about what we
365 * would need to do in order to support Reconfigure, and the Auth option.
366 *
367 * If auth.type == 1, the contents of the attribute are just a reconfigure key, which is
368 * sent over the network in the clear. The server has to get the key from somewhere, OR
369 * create it from a CSRNG and then store it. The client just stores it somewhere.
370 * Except that the key is sent over the network in the clear, which make is pretty much
371 * useless for security.
372 *
373 * If auth.type == 2, then we need to check the encode/decode context for a key, and if
374 * not fail.
375 *
376 * For encode, encode the structure and remember in the encode context where we encoded
377 * it. Then when we are finished encoding the entire packet, go back and do an HMAC-MD5
378 * over the packet and update the auth.value field.
379 *
380 * We also have to ignore any supplied auth.rdm and auth.replay-detection field, and then
381 * force auth.rdm=0, and set auth.reply-detection to the current 64-bit NTP time. The
382 * replay-detection field is a monotonically increasing 64-bit value which is saved
383 * across server restarts. So an NTP time is the easiest way to manage that.
384 *
385 * For decode, we have to check the reply-detection field, and fail if it's smaller than
386 * what we expect. Then, remember where the Auth option is in the packet, go back and do
387 * an HMAC-MD5 over the packet.
388 *
389 * See also references to attr_auth in encode.c and decode.c.
390 */
391 fr_strerror_const("Sending Reconfigure to a client is not supported");
392 return false;
393#else
394 if (!fr_dhcpv6_option_find(options, end, FR_SERVER_ID)) goto fail_sid;
395
396 option = fr_dhcpv6_option_find(options, end, FR_CLIENT_ID);
397 if (!option) goto fail_cid;
398
399 /*
400 * The DUID MUST exist.
401 */
402 if (!packet_ctx->duid) goto fail_duid;
403 if (!duid_match(option, packet_ctx)) goto fail_match;
404
405 option = fr_dhcpv6_option_find(options, end, FR_RECONF_MSG);
406 if (!option) {
407 fr_strerror_const("Packet does not contain a Reconf-Msg option");
408 return false;
409 }
410
411 /*
412 * Check reconfigure message type, and reject
413 * if it is not valid.
414 */
415 switch (option[4]) {
416 case FR_DHCPV6_RENEW:
417 case FR_DHCPV6_REBIND:
419 break;
420 default:
421 fr_strerror_const("Invalid Reconf-Msg option value");
422 return false;
423 }
424
425 /*
426 * @todo - Do an HMAC-MD5 of the Auth option.
427 */
428 break;
429#endif
430
432 if (packet_len < DHCPV6_RELAY_HDR_LEN) {
433 fr_strerror_const("Relay-Reply message is too small");
434 return false;
435 }
436
437 options += (DHCPV6_RELAY_HDR_LEN - 4); /* we assumed it was a normal packet above */
438 option = fr_dhcpv6_option_find(options, end, FR_RELAY_MESSAGE);
439 if (!option) {
440 fr_strerror_const("Packet does not contain a Relay-Message option");
441 return false;
442 }
443 return verify_to_client(option + 4, DHCPV6_GET_OPTION_LEN(option), packet_ctx);
444
446 transaction_id = fr_nbo_to_uint24(&packet[1]);
447 if (transaction_id != packet_ctx->transaction_id) goto fail_tid;
448
449 if (!fr_dhcpv6_option_find(options, end, FR_SERVER_ID)) goto fail_sid;
450
451 option = fr_dhcpv6_option_find(options, end, FR_CLIENT_ID);
452 if (!option) goto fail_cid;
453
454 /*
455 * The DUID MUST exist.
456 */
457 if (!packet_ctx->duid) goto fail_duid;
458 if (!duid_match(option, packet_ctx)) goto fail_match;
459 break;
460
461 case FR_PACKET_TYPE_VALUE_REQUEST:
462 case FR_PACKET_TYPE_VALUE_CONFIRM:
463 case FR_PACKET_TYPE_VALUE_RENEW:
464 case FR_PACKET_TYPE_VALUE_REBIND:
465 case FR_PACKET_TYPE_VALUE_RELEASE:
466 case FR_PACKET_TYPE_VALUE_DECLINE:
467 case FR_PACKET_TYPE_VALUE_INFORMATION_REQUEST:
468 default:
469 fr_strerror_const("Invalid message type sent to client");
470 return false;
471 }
472
473 return true;
474}
475
476
477/** Verify a packet from a client to a server
478 *
479 */
480static bool verify_from_client(uint8_t const *packet, size_t packet_len, fr_dhcpv6_decode_ctx_t const *packet_ctx)
481{
482 uint8_t const *option;
483 uint8_t const *options = packet + 4;
484 uint8_t const *end = packet + packet_len;
485
486 /*
487 * Servers MUST have a DUID
488 */
489 if (!packet_ctx->duid) {
490 fr_strerror_const("Packet context does not contain a DUID");
491 return false;
492 }
493
494 switch (packet[0]) {
495 case FR_PACKET_TYPE_VALUE_SOLICIT:
496 case FR_PACKET_TYPE_VALUE_CONFIRM:
497 case FR_PACKET_TYPE_VALUE_REBIND:
498 if (!fr_dhcpv6_option_find(options, end, FR_CLIENT_ID)) {
499 fail_cid:
500 fr_strerror_const("Packet does not contain a Client-Id option");
501 return false;
502 }
503
504 if (fr_dhcpv6_option_find(options, end, FR_SERVER_ID)) {
505 fr_strerror_const("Packet contains a Server-Id option");
506 return false;
507 }
508 break;
509
510 case FR_PACKET_TYPE_VALUE_REQUEST:
511 case FR_PACKET_TYPE_VALUE_RENEW:
512 case FR_PACKET_TYPE_VALUE_DECLINE:
513 case FR_PACKET_TYPE_VALUE_RELEASE:
514 if (!fr_dhcpv6_option_find(options, end, FR_CLIENT_ID)) goto fail_cid;
515
516 option = fr_dhcpv6_option_find(options, end, FR_SERVER_ID);
517 if (!option) {
518 fr_strerror_const("Packet does not contain a Server-Id option");
519 return false;
520 }
521
522 if (!duid_match(option, packet_ctx)) {
523 fail_match:
524 fr_strerror_const("DUID in packet does not match our DUID");
525 return false;
526 }
527 break;
528
529 case FR_PACKET_TYPE_VALUE_INFORMATION_REQUEST:
530 option = fr_dhcpv6_option_find(options, end, FR_SERVER_ID);
531 if (option && !duid_match(option, packet_ctx)) goto fail_match;
532
533 /*
534 * IA options are forbidden.
535 */
536 if (fr_dhcpv6_option_find(options, end, FR_IA_NA)) {
537 fr_strerror_const("Packet contains an IA-NA option");
538 return false;
539 }
540 if (fr_dhcpv6_option_find(options, end, FR_IA_TA)) {
541 fr_strerror_const("Packet contains an IA-TA option");
542 return false;
543 }
544 if (fr_dhcpv6_option_find(options, end, FR_IA_ADDR)) {
545 fr_strerror_const("Packet contains an IA-Addr option");
546 return false;
547 }
548 break;
549
551 if (packet_len < DHCPV6_RELAY_HDR_LEN) {
552 fr_strerror_const("Relay-Forward message is too small");
553 return false;
554 }
555
556 options += (DHCPV6_RELAY_HDR_LEN - 4); /* we assumed it was a normal packet above */
557 option = fr_dhcpv6_option_find(options, end, FR_RELAY_MESSAGE);
558 if (!option) {
559 fr_strerror_const("Packet does not contain a Relay-Message option");
560 return false;
561 }
562
563 return verify_from_client(option + 4, DHCPV6_GET_OPTION_LEN(option), packet_ctx);
564
565 case FR_PACKET_TYPE_VALUE_LEASE_QUERY:
566 if (!fr_dhcpv6_option_find(options, end, FR_CLIENT_ID)) goto fail_cid;
567
568 /*
569 * Server-ID is a SHOULD, but if it exists, it
570 * MUST match.
571 */
572 option = fr_dhcpv6_option_find(options, end, FR_SERVER_ID);
573 if (option && !duid_match(option, packet_ctx)) goto fail_match;
574
575 option = fr_dhcpv6_option_find(options, end, FR_LEASE_QUERY);
576 if (!option) {
577 fr_strerror_const("Packet does not contain a Lease-Query option");
578 return false;
579 }
580 break;
581
582 case FR_PACKET_TYPE_VALUE_ADVERTISE:
583 case FR_PACKET_TYPE_VALUE_REPLY:
584 case FR_PACKET_TYPE_VALUE_RECONFIGURE:
585 default:
586 fr_strerror_const("Invalid message type sent to server");
587 return false;
588 }
589 return true;
590}
591
592/** Verify the packet under some various circumstances
593 *
594 * @param[in] packet to check.
595 * @param[in] packet_len The size of the packet data.
596 * @param[in] packet_ctx The expected packet_ctx
597 * @param[in] from_server true for packets from a server, false for packets from a client.
598 * @return
599 * - True on success.
600 * - False on failure.
601 *
602 * fr_dhcpv6_ok() SHOULD be called before calling this function.
603 */
604bool fr_dhcpv6_verify(uint8_t const *packet, size_t packet_len, fr_dhcpv6_decode_ctx_t const *packet_ctx,
605 bool from_server)
606{
607 if (packet_len < DHCPV6_HDR_LEN) return false;
608
609 /*
610 * We support up to lease query reply.
611 */
612 if ((packet[0] == 0) || (packet[0] > FR_DHCPV6_LEASE_QUERY_REPLY)) return false;
613
614 if (from_server) return verify_to_client(packet, packet_len, packet_ctx);
615
616 return verify_from_client(packet, packet_len, packet_ctx);
617}
618
619/*
620
621 0 1 2 3
622 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
623 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
624 | msg-type | transaction-id |
625 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
626 | |
627 . options .
628 . (variable number and length) .
629 | |
630 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
631*/
632
633/** Decode a DHCPv6 packet
634 *
635 */
636ssize_t fr_dhcpv6_decode(TALLOC_CTX *ctx, fr_pair_list_t *out, uint8_t const *packet, size_t packet_len)
637{
638 ssize_t slen = -1;
639 uint8_t const *p, *end;
640 fr_dhcpv6_decode_ctx_t packet_ctx = {};
641 fr_pair_t *vp;
642 fr_pair_list_t tmp;
643
644 if (packet_len < DHCPV6_HDR_LEN) return 0; /* protect access to packet[0] */
645
646 /*
647 * Get the packet type.
648 */
650 if (!vp) return -1;
651
652 fr_pair_list_init(&tmp);
653 vp->vp_uint32 = packet[0];
654 fr_pair_append(&tmp, vp);
655
656 switch (packet[0]) {
659 /*
660 * Just for sanity check.
661 */
662 if (packet_len < DHCPV6_RELAY_HDR_LEN) return -1;
663
664 /*
665 * Decode the header fields.
666 */
668 if (!vp) goto fail;
669 if (fr_value_box_from_network(vp, &vp->data, vp->vp_type, NULL,
670 &FR_DBUFF_TMP(packet + 1, 1), 1, true) < 0) {
672 goto fail;
673 }
674 fr_pair_append(&tmp, vp);
675
677 if (!vp) goto fail;
678 if (fr_value_box_from_network(vp, &vp->data, vp->vp_type, NULL,
679 &FR_DBUFF_TMP(packet + 2, 16), 16, true) < 0) {
681 goto fail;
682 }
683 fr_pair_append(&tmp, vp);
684
686 if (!vp) goto fail;
687 if (fr_value_box_from_network(vp, &vp->data, vp->vp_type, NULL,
688 &FR_DBUFF_TMP(packet + 2 + 16, 16), 16, true) < 0) {
690 goto fail;
691 }
692
693 fr_pair_append(&tmp, vp);
694
695 p = packet + DHCPV6_RELAY_HDR_LEN;
696 goto decode_options;
697
698 default:
699 break;
700 }
701
702 /*
703 * And the transaction ID.
704 */
706 if (!vp) {
707 fail:
708 fr_pair_list_free(&tmp);
709 return slen;
710 }
711
712 /*
713 * Copy 3 octets over.
714 */
715 (void) fr_pair_value_memdup(vp, packet + 1, 3, false);
716
717 fr_pair_append(&tmp, vp);
718
719 p = packet + 4;
720
721decode_options:
722 end = packet + packet_len;
723 packet_ctx.tmp_ctx = talloc_init_const("tmp");
724
725 /*
726 * The caller MUST have called fr_dhcpv6_ok() first. If
727 * he doesn't, all hell breaks loose.
728 */
729 while (p < end) {
730 slen = fr_dhcpv6_decode_option(ctx, &tmp, p, (end - p), &packet_ctx);
731 if (slen < 0) {
732 talloc_free(packet_ctx.tmp_ctx);
733 goto fail;
734 }
735 /*
736 * If slen is larger than the room in the packet,
737 * all kinds of bad things happen.
738 */
739 if (!fr_cond_assert(slen <= (end - p))) {
740 talloc_free(packet_ctx.tmp_ctx);
741 goto fail;
742 }
743
744 p += slen;
745 talloc_free_children(packet_ctx.tmp_ctx);
746 }
748
749 /*
750 * We've parsed the whole packet, return that.
751 */
752 talloc_free(packet_ctx.tmp_ctx);
753 return packet_len;
754}
755
756/** DHCPV6-specific iterator
757 *
758 */
759void *fr_dhcpv6_next_encodable(fr_dcursor_t *cursor, void *current, void *uctx)
760{
761 fr_pair_t *c = current;
762 fr_dict_t *dict = talloc_get_type_abort(uctx, fr_dict_t);
763
764 while ((c = fr_dlist_next(cursor->dlist, c))) {
765 PAIR_VERIFY(c);
766 if (c->da->dict != dict || c->da->flags.internal) continue;
767 if (c->vp_type == FR_TYPE_BOOL && !c->vp_bool) continue;
768
769 break;
770 }
771
772 return c;
773}
774
775/** Encode a DHCPv6 packet
776 *
777 */
778ssize_t fr_dhcpv6_encode(fr_dbuff_t *dbuff, uint8_t const *original, size_t length, int msg_type, fr_pair_list_t *vps)
779{
780 fr_dbuff_t frame_dbuff = FR_DBUFF(dbuff);
781 fr_pair_t *vp;
782 fr_dict_attr_t const *root;
783 ssize_t slen;
784 fr_dcursor_t cursor;
785 fr_dhcpv6_encode_ctx_t packet_ctx;
786
788
789 if (!msg_type) {
791 if (vp) msg_type = vp->vp_uint32;
792 }
793
794 if ((msg_type <= 0) || (msg_type > UINT8_MAX)) {
795 fr_strerror_printf("Invalid message type %d", msg_type);
796 return -1;
797 }
798
799 FR_DBUFF_IN_RETURN(&frame_dbuff, (uint8_t)msg_type);
800
801 switch (msg_type) {
805 if (likely(vp != NULL)) {
806 FR_VALUE_BOX_TO_NETWORK_RETURN(&frame_dbuff, &vp->data);
807 } else {
809 }
810
812 if (likely(vp != NULL)) {
813 FR_VALUE_BOX_TO_NETWORK_RETURN(&frame_dbuff, &vp->data);
814 } else {
816 }
817
819 if (likely(vp != NULL)) {
820 FR_VALUE_BOX_TO_NETWORK_RETURN(&frame_dbuff, &vp->data);
821 } else {
823 }
824 break;
825
826 default:
827 /*
828 * We can set an XID, or we can pick a random one.
829 */
831 if (vp && (vp->vp_length >= DHCPV6_TRANSACTION_ID_LEN)) {
832 FR_DBUFF_IN_MEMCPY_RETURN(&frame_dbuff, vp->vp_octets, DHCPV6_TRANSACTION_ID_LEN);
833 } else {
836 FR_DBUFF_IN_MEMCPY_RETURN(&frame_dbuff, id, sizeof(id)); /* Need 24 bits of the 32bit integer */
837 }
838 break;
839 }
840
841 /*
842 * Encode options.
843 */
844 packet_ctx.root = root;
845 packet_ctx.original = original;
846 packet_ctx.original_length = length;
847
849 while ((fr_dbuff_extend(&frame_dbuff) > 0) && (fr_dcursor_current(&cursor) != NULL)) {
850 slen = fr_dhcpv6_encode_option(&frame_dbuff, &cursor, &packet_ctx);
851 if (slen < 0) return FR_DBUFF_ERROR_OFFSET(slen, fr_dbuff_used(&frame_dbuff));
852 }
853
854 return fr_dbuff_set(dbuff, &frame_dbuff);
855}
856
857
858static 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";
859
860static void print_hex_data(FILE *fp, uint8_t const *ptr, int attrlen, int depth)
861{
862 int i;
863
864 for (i = 0; i < attrlen; i++) {
865 if ((i > 0) && ((i & 0x0f) == 0x00))
866 fprintf(fp, "%.*s", depth, tabs);
867 fprintf(fp, "%02x ", ptr[i]);
868 if ((i & 0x0f) == 0x0f) fprintf(fp, "\n");
869 }
870 if ((i & 0x0f) != 0) fprintf(fp, "\n");
871}
872
873static void dhcpv6_print_hex(FILE *fp, uint8_t const *packet, size_t packet_len, int depth)
874{
875 uint8_t const *option, *end = packet + packet_len;
876
877 if (packet_len < 4) {
878 fprintf(fp, "%.*s", depth, tabs);
879 fprintf(fp, "???:\t");
880 print_hex_data(fp, packet, packet_len, depth + 1);
881 return;
882 }
883
884 fprintf(fp, "%.*s", depth, tabs);
885 if ((packet[0] > 0) && (packet[0] < FR_DHCPV6_CODE_MAX)) {
886 fprintf(fp, "packet: %s\n", fr_dhcpv6_packet_names[packet[0]]);
887 } else {
888 fprintf(fp, "packet: %02x\n", packet[0]);
889 }
890
891 if ((packet[0] == FR_PACKET_TYPE_VALUE_RELAY_FORWARD) ||
892 (packet[0] == FR_PACKET_TYPE_VALUE_RELAY_REPLY)) {
893 if (packet_len < 34) {
894 fprintf(fp, "%.*s", depth, tabs);
895 fprintf(fp, "???:\t");
896 print_hex_data(fp, packet + 1, packet_len - 1, depth + 1);
897 return;
898 }
899
900 fprintf(fp, "%.*s", depth, tabs);
901 fprintf(fp, "hops: %02x\n", packet[1]);
902 fprintf(fp, "%.*s", depth, tabs);
903 fprintf(fp, "relay link address: ");
904 print_hex_data(fp, packet + 2, 16, depth + 1);
905
906 fprintf(fp, "%.*s", depth, tabs);
907 fprintf(fp, "peer address: ");
908 print_hex_data(fp, packet + 18, 16, depth + 1);
909 option = packet + 34;
910 } else {
911 fprintf(fp, "%.*s", depth, tabs);
912 fprintf(fp, "transaction id: ");
913 print_hex_data(fp, packet + 1, 3, depth + 1);
914 option = packet + 4;
915 }
916
917 fprintf(fp, "%.*s", depth, tabs);
918 fprintf(fp, "options\n");
919 while (option < end) {
920 uint16_t length;
921
922 if ((end - option) < 4) {
923 fprintf(fp, "%.*s", depth + 1, tabs);
924 fprintf(fp, "???:\t");
925 print_hex_data(fp, option, end - option, depth + 2);
926 break;
927 }
928
929 length = fr_nbo_to_uint16(option + 2);
930 fprintf(fp, "%.*s", depth + 1, tabs);
931 fprintf(fp, "%04x %04x\t", fr_nbo_to_uint16(option), length);
932
933 if (length > end - (option + 4)) {
934 print_hex_data(fp, option + 4, end - (option + 4), depth + 3);
935 break;
936 }
937
938 print_hex_data(fp, option + 4, length, depth + 3);
939 if ((option[0] == 0) && (option[1] == attr_relay_message->attr)) {
940 dhcpv6_print_hex(fp, option + 4, length, depth + 2);
941 }
942
943 option += 4 + length;
944 }
945
946 fprintf(fp, "\n");
947}
948
949/** Print a raw DHCP packet as hex.
950 *
951 */
952void fr_dhcpv6_print_hex(FILE *fp, uint8_t const *packet, size_t packet_len)
953{
954 dhcpv6_print_hex(fp, packet, packet_len, 0);
955}
956
958{
959 if (instance_count > 0) {
961 return 0;
962 }
963
965
967 fail:
969 return -1;
970 }
971
974 goto fail;
975 }
976
977 instantiated = true;
978 return 0;
979}
980
982{
983 if (!instantiated) return;
984
986
987 if (--instance_count > 0) return;
988
990 instantiated = false;
991}
992
994{
995 /*
996 * DNS labels are strings, but are known width.
997 */
999 if (da->type != FR_TYPE_STRING) {
1000 fr_strerror_const("The 'dns_label' flag can only be used with attributes of type 'string'");
1001 return false;
1002 }
1003
1004 da->flags.is_known_width = true;
1005 da->flags.length = 0;
1006 }
1007
1008 if (da->type == FR_TYPE_ATTR) {
1009 if (!da->parent->flags.is_root) {
1010 fr_strerror_const("Data type 'attribute' can only be used at the dictionary root");
1011 return false;
1012 }
1013
1014 da->flags.is_known_width = true;
1015 da->flags.length = 2;
1016 }
1017
1018 if (da_is_length_field8(da)) {
1019 fr_strerror_const("The 'length=uint8' flag cannot be used for DHCPv6");
1020 return false;
1021 }
1022
1023 /*
1024 * "arrays" of string/octets are encoded as a 16-bit
1025 * length, followed by the actual data.
1026 */
1027 if (da->flags.array) {
1028 if ((da->type == FR_TYPE_STRING) || (da->type == FR_TYPE_OCTETS)) {
1029 if (da->flags.extra && !da_is_length_field16(da)) {
1030 fr_strerror_const("Invalid flags");
1031 return false;
1032 }
1033
1034 da->flags.is_known_width = true;
1035 da->flags.extra = true;
1036 da->flags.subtype = FLAG_LENGTH_UINT16;
1037 }
1038
1039 if (!da->flags.is_known_width) {
1040 fr_strerror_const("DHCPv6 arrays require data types which have known width");
1041 return false;
1042 }
1043 }
1044
1045 /*
1046 * "extra" signifies that subtype is being used by the
1047 * dictionaries itself.
1048 */
1049 if (da->flags.extra || !da->flags.subtype) return true;
1050
1051 if ((da->type == FR_TYPE_ATTR) && !da->parent->flags.is_root) {
1052 fr_strerror_const("The 'attribute' data type can only be used at the dictionary root");
1053 return false;
1054 }
1055
1056 da->flags.is_known_width = true;
1057
1058 return true;
1059}
1060
1063 .name = "dhcpv6",
1064 .default_type_size = 2,
1065 .default_type_length = 2,
1066
1067 .attr = {
1068 .valid = attr_valid,
1069 .flags = {
1070 .table = dhcpv6_flags,
1071 .table_len = NUM_ELEMENTS(dhcpv6_flags),
1072 .len = sizeof(fr_dhcpv6_attr_flags_t)
1073 }
1074 },
1075
1076 .init = fr_dhcpv6_global_init,
1077 .free = fr_dhcpv6_global_free,
1078
1079 .encode = fr_dhcpv6_encode_foreign,
1080 .decode = fr_dhcpv6_decode_foreign,
1081};
#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
#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
uint8_t const * duid
the expected DUID, in wire format
Definition dhcpv6.h:135
#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:749
#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_STRUCT
like TLV, but without T or L, and fixed-width children
@ 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:276
void * fr_dhcpv6_next_encodable(fr_dcursor_t *cursor, void *current, void *uctx)
DHCPV6-specific iterator.
Definition base.c:759
void fr_dhcpv6_global_free(void)
Definition base.c:981
fr_dict_protocol_t libfreeradius_dhcpv6_dict_protocol
Definition base.c:1062
static ssize_t fr_dhcpv6_ok_internal(uint8_t const *packet, uint8_t const *end, size_t max_attributes, int depth)
Definition base.c:174
static void dhcpv6_print_hex(FILE *fp, uint8_t const *packet, size_t packet_len, int depth)
Definition base.c:873
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:237
uint8_t const * fr_dhcpv6_option_find(uint8_t const *start, uint8_t const *end, unsigned int option)
Definition base.c:253
int fr_dhcpv6_global_init(void)
Definition base.c:957
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:123
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:636
char const * fr_dhcpv6_packet_names[FR_DHCPV6_CODE_MAX]
Definition base.c:74
fr_dict_attr_t const * attr_relay_message
Definition base.c:54
fr_dict_attr_autoload_t libfreeradius_dhcpv6_dict_attr[]
Definition base.c:59
fr_dict_attr_t const * attr_auth
Definition base.c:56
static fr_dict_flag_parser_t const dhcpv6_flags[]
Definition base.c:116
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:604
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:290
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:480
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:952
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:778
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:483
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:498
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