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