The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
encode.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: 8a502a4c80fa073f94718e2c32bb71fd9c88663e $
19  *
20  * @file protocols/dhcpv6/encode.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 <stdint.h>
29 #include <stddef.h>
30 #include <freeradius-devel/io/test_point.h>
31 #include <freeradius-devel/util/dbuff.h>
32 #include <freeradius-devel/util/dns.h>
33 #include <freeradius-devel/util/proto.h>
34 #include <freeradius-devel/util/struct.h>
35 #include <freeradius-devel/util/encode.h>
36 
37 #include "dhcpv6.h"
38 #include "attrs.h"
39 
40 static ssize_t encode_value(fr_dbuff_t *dbuff,
41  fr_da_stack_t *da_stack, unsigned int depth,
42  fr_dcursor_t *cursor, void *encode_ctx);
43 
44 static ssize_t encode_rfc(fr_dbuff_t *dbuff,
45  fr_da_stack_t *da_stack, unsigned int depth,
46  fr_dcursor_t *cursor, void *encode_ctx);
47 
48 static ssize_t encode_tlv(fr_dbuff_t *dbuff,
49  fr_da_stack_t *da_stack, unsigned int depth,
50  fr_dcursor_t *cursor, void *encode_ctx);
51 
52 static ssize_t encode_child(fr_dbuff_t *dbuff,
53  fr_da_stack_t *da_stack, unsigned int depth,
54  fr_dcursor_t *cursor, void *encode_ctx);
55 
56 /** Macro-like function for encoding an option header
57  *
58  * 0 1 2 3
59  * 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
60  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
61  * | option-code | option-len |
62  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
63  *
64  * @param[out] m Where to write the 4 byte option header.
65  * @param[in] option The option number (host byte order).
66  * @param[in] data_len The length of the option (host byte order).
67  * @return
68  * - <0 How much data would have been required as a negative value.
69  * - 4 The length of data written.
70  */
71 static inline ssize_t encode_option_hdr(fr_dbuff_marker_t *m, uint16_t option, size_t data_len)
72 {
73  FR_DBUFF_IN_RETURN(m, option);
74  FR_DBUFF_IN_RETURN(m, (uint16_t) data_len);
75 
76  return sizeof(option) + sizeof(uint16_t);
77 }
78 
79 
81  fr_da_stack_t *da_stack, unsigned int depth,
82  fr_dcursor_t *cursor, void *encode_ctx)
83 {
84  ssize_t slen;
85  fr_dbuff_t work_dbuff = FR_DBUFF(dbuff);
86  fr_pair_t const *vp = fr_dcursor_current(cursor);
87  fr_dict_attr_t const *da = da_stack->da[depth];
88 
89  PAIR_VERIFY(vp);
90  FR_PROTO_STACK_PRINT(da_stack, depth);
91 
92  /*
93  * Pack multiple attributes into into a single option
94  */
95  if ((vp->vp_type == FR_TYPE_STRUCT) || (da->type == FR_TYPE_STRUCT)) {
96  slen = fr_struct_to_network(&work_dbuff, da_stack, depth, cursor, encode_ctx, encode_value, encode_child);
97  if (slen <= 0) return slen;
98 
99  vp = fr_dcursor_current(cursor);
100  fr_proto_da_stack_build(da_stack, vp ? vp->da : NULL);
101  return fr_dbuff_set(dbuff, &work_dbuff);
102  }
103 
104  /*
105  * If it's not a TLV, it should be a value type RFC
106  * attribute make sure that it is.
107  */
108  if (da_stack->da[depth + 1] != NULL) {
109  fr_strerror_printf("%s: Encoding value but not at top of stack", __FUNCTION__);
111  }
112 
113  if (vp->da != da) {
114  fr_strerror_printf("%s: Top of stack does not match vp->da", __FUNCTION__);
116  }
117 
118  switch (vp->vp_type) {
119  case FR_TYPE_TLV:
120  case FR_TYPE_VENDOR:
121  case FR_TYPE_VSA:
122  fr_strerror_printf("%s: Called with structural type %s", __FUNCTION__,
123  fr_type_to_str(da->type));
125 
126  /*
127  * 0 1 2 3
128  * 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
129  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
130  * | option-code | option-len |
131  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
132  * . String .
133  * | ... |
134  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
135  */
136  case FR_TYPE_STRING:
137  /*
138  * DNS labels get a special encoder. DNS labels
139  * MUST NOT be compressed in DHCP.
140  *
141  * https://tools.ietf.org/html/rfc8415#section-10
142  */
143  if (da_is_dns_label(da)) {
144  fr_dbuff_marker_t last_byte, src;
145 
146  fr_dbuff_marker(&last_byte, &work_dbuff);
147  fr_dbuff_marker(&src, &work_dbuff);
148  slen = fr_dns_label_from_value_box_dbuff(&work_dbuff, false, &vp->data, NULL);
149  if (slen < 0) return slen;
150 
151  /*
152  * RFC 4704 says "FQDN", unless it's a
153  * single label, in which case it's a
154  * partial name, and we omit the trailing
155  * zero.
156  */
157  if ((da->flags.subtype == FLAG_ENCODE_PARTIAL_DNS_LABEL) && slen > 0) {
158  uint8_t c = 0;
159 
160  fr_dbuff_advance(&last_byte, (size_t)(slen - 1));
161  fr_dbuff_set(&src, &last_byte);
162  fr_dbuff_out(&c, &src);
163  if (!c) fr_dbuff_set(&work_dbuff, &last_byte);
164  }
165  break;
166  }
167  goto to_network;
168 
169  /*
170  * Common encoder might add scope byte, so we just copy the address portion
171  *
172  * 0 1 2 3
173  * 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
174  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
175  * | option-code | option-len |
176  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
177  * | |
178  * | ipv6-address |
179  * | |
180  * | |
181  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
182  */
183  case FR_TYPE_IPV6_ADDR:
184  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, vp->vp_ipv6addr, sizeof(vp->vp_ipv6addr));
185  break;
186 
187  /*
188  * Common encoder doesn't add a reserved byte after prefix, but it also
189  * doesn't do the variable length encoding required.
190  *
191  * 0 1 2 3
192  * 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
193  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
194  * | option-code | option-length |
195  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
196  * | prefix6len | ipv6-prefix |
197  * +-+-+-+-+-+-+-+-+ (variable length) |
198  * . .
199  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
200  */
201  case FR_TYPE_IPV6_PREFIX:
202  {
203  size_t prefix_len;
204 
205  /*
206  * Structs have fixed length value fields.
207  */
208  if (da->parent->type == FR_TYPE_STRUCT) {
209  prefix_len = sizeof(vp->vp_ipv6addr);
210  } else {
211  prefix_len = fr_bytes_from_bits(vp->vp_ip.prefix);
212  }
213 
214  FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, vp->vp_ip.prefix);
215  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, (uint8_t const *)&vp->vp_ipv6addr, prefix_len); /* Only copy the minimum address bytes required */
216  }
217  break;
218 
219  /*
220  * Not actually specified by the DHCPv6 RFC, but will probably come
221  * in handy at some point if we need to have the DHCPv6 server
222  * hand out v4 prefixes.
223  */
224  case FR_TYPE_IPV4_PREFIX:
225  {
226  size_t prefix_len;
227 
228  /*
229  * Structs have fixed length value fields.
230  */
231  if (da->parent->type == FR_TYPE_STRUCT) {
232  prefix_len = sizeof(vp->vp_ipv4addr);
233  } else {
234  prefix_len = fr_bytes_from_bits(vp->vp_ip.prefix);
235  }
236 
237  FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, vp->vp_ip.prefix);
238  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, (uint8_t const *)&vp->vp_ipv4addr, prefix_len); /* Only copy the minimum address bytes required */
239  }
240  break;
241 
242  /*
243  * 0 1 2 3
244  * 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
245  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
246  * | option-code | option-len |
247  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
248  */
249  case FR_TYPE_BOOL:
250  /*
251  * Don't encode anything! The mere existence of
252  * the attribute signifies a "true" value.
253  */
254  break;
255 
256  /*
257  * A standard 32bit integer, but unlike normal UNIX timestamps
258  * starts from the 1st of January 2000.
259  *
260  * In the decoder we add 30 years to any values, so here
261  * we need to subtract that time, or if the value is less
262  * than that time, just encode a 0x0000000000
263  * value.
264  */
265  case FR_TYPE_DATE:
266  {
267  uint64_t date = fr_unix_time_to_sec(vp->vp_date);
268 
269  if (date < DHCPV6_DATE_OFFSET) { /* 30 years */
270  FR_DBUFF_IN_RETURN(&work_dbuff, (uint32_t) 0);
271  break;
272  }
273 
274  FR_DBUFF_IN_RETURN(&work_dbuff, (uint32_t)(date - DHCPV6_DATE_OFFSET));
275  }
276  break;
277 
278  case FR_TYPE_GROUP:
279  {
280  fr_dcursor_t child_cursor;
281  fr_dict_attr_t const *ref = fr_dict_attr_ref(vp->da);
282 
283  if (ref && (ref->dict != dict_dhcpv6)) {
284  slen = fr_pair_ref_to_network(&work_dbuff, da_stack, depth, cursor);
285  if (slen < 0) return PAIR_ENCODE_FATAL_ERROR;
286  break;
287  }
288 
289  /*
290  * Encode the child options.
291  */
292  if (!fr_pair_list_empty(&vp->vp_group)) {
293  (void) fr_pair_dcursor_child_iter_init(&child_cursor, &vp->vp_group, cursor);
294 
295  while (fr_dcursor_current(&child_cursor) != NULL) {
296  slen = fr_dhcpv6_encode_option(&work_dbuff, &child_cursor, encode_ctx);
297 
298  if (slen < 0) return PAIR_ENCODE_FATAL_ERROR;
299  }
300  }
301  }
302  break;
303 
304  /*
305  * The value_box functions will take care of fixed-width
306  * "string" and "octets" options.
307  */
308  to_network:
309  case FR_TYPE_OCTETS:
310  /*
311  * Hack until we find all places that don't set data.enumv
312  */
313  if (vp->da->flags.length && (vp->data.enumv != vp->da)) {
314  fr_dict_attr_t const * const *c = &vp->data.enumv;
315  fr_dict_attr_t **u;
316 
317  memcpy(&u, &c, sizeof(c)); /* const issues */
318  memcpy(u, &vp->da, sizeof(vp->da));
319  }
320  FALL_THROUGH;
321 
322  /*
323  * 0 1 2 3
324  * 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
325  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
326  * | option-code | option-len |
327  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
328  * | 8-bit-integer |
329  * +-+-+-+-+-+-+-+-+
330  */
331  case FR_TYPE_UINT8:
332 
333  /*
334  * 0 1 2 3
335  * 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
336  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
337  * | option-code | option-len |
338  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
339  * | 16-bit-integer |
340  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
341  */
342  case FR_TYPE_UINT16:
343  /*
344  * 0 1 2 3
345  * 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
346  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
347  * | option-code | option-len |
348  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
349  * | 32-bit-integer |
350  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
351  */
352  case FR_TYPE_UINT32:
353  default:
354  slen = fr_value_box_to_network(&work_dbuff, &vp->data);
355  if (slen < 0) return PAIR_ENCODE_FATAL_ERROR;
356  break;
357  }
358 
359  /*
360  * Rebuilds the TLV stack for encoding the next attribute
361  */
362  vp = fr_dcursor_next(cursor);
363  fr_proto_da_stack_build(da_stack, vp ? vp->da : NULL);
364 
365  return fr_dbuff_set(dbuff, &work_dbuff);
366 }
367 
368 
369 static ssize_t encode_vsio(fr_dbuff_t *dbuff,
370  fr_da_stack_t *da_stack, unsigned int depth,
371  fr_dcursor_t *cursor, void *encode_ctx);
372 
374  fr_da_stack_t *da_stack, unsigned int depth,
375  fr_dcursor_t *cursor, void *encode_ctx)
376 {
377  ssize_t len;
378  fr_pair_t *vp = fr_dcursor_current(cursor);
379  fr_dcursor_t child_cursor;
380  fr_dbuff_t work_dbuff;
381 
382  if (da_stack->da[depth]) {
383  /*
384  * Determine the nested type and call the appropriate encoder
385  */
386  switch (da_stack->da[depth]->type) {
387  case FR_TYPE_TLV:
388  if (!da_stack->da[depth + 1]) break;
389 
390  return encode_tlv(dbuff, da_stack, depth, cursor, encode_ctx);
391 
392  case FR_TYPE_VSA:
393  if (!da_stack->da[depth + 1]) break;
394 
395  return encode_vsio(dbuff, da_stack, depth, cursor, encode_ctx);
396 
397  case FR_TYPE_GROUP:
398  if (!da_stack->da[depth + 1]) break;
399  FALL_THROUGH;
400 
401  default:
402  return encode_rfc(dbuff, da_stack, depth, cursor, encode_ctx);
403  }
404  }
405 
407 
408  fr_pair_dcursor_child_iter_init(&child_cursor, &vp->vp_group, cursor);
409  work_dbuff = FR_DBUFF(dbuff);
410 
411  while ((vp = fr_dcursor_current(&child_cursor)) != NULL) {
412  fr_proto_da_stack_build(da_stack, vp->da);
413 
414  switch (da_stack->da[depth]->type) {
415  case FR_TYPE_VSA:
416  len = encode_vsio(&work_dbuff, da_stack, depth, &child_cursor, encode_ctx);
417  break;
418 
419  case FR_TYPE_TLV:
420  len = encode_tlv(&work_dbuff, da_stack, depth, &child_cursor, encode_ctx);
421  break;
422 
423  default:
424  len = encode_rfc(&work_dbuff, da_stack, depth, &child_cursor, encode_ctx);
425  break;
426  }
427 
428  if (len <= 0) return len;
429  }
430 
431  /*
432  * Skip over the attribute we just encoded.
433  */
434  vp = fr_dcursor_next(cursor);
435  fr_proto_da_stack_build(da_stack, vp ? vp->da : NULL);
436 
437  return fr_dbuff_set(dbuff, &work_dbuff);
438 }
439 
440 /** Encode an RFC format TLV.
441  *
442  * This could be a standard attribute, or a TLV data type.
443  * If it's a standard attribute, then vp->da->attr == attribute.
444  * Otherwise, attribute may be something else.
445  */
447  fr_da_stack_t *da_stack, unsigned int depth,
448  fr_dcursor_t *cursor, void *encode_ctx)
449 {
450  fr_dbuff_t work_dbuff = FR_DBUFF(dbuff);
451  fr_dbuff_marker_t hdr;
452  fr_dict_attr_t const *da = da_stack->da[depth];
453  ssize_t slen;
454 
455  FR_PROTO_STACK_PRINT(da_stack, depth);
456  fr_dbuff_marker(&hdr, &work_dbuff);
457 
458  /*
459  * Make space for the header...
460  */
462  fr_dbuff_advance(&work_dbuff, DHCPV6_OPT_HDR_LEN);
463 
464  /*
465  * Write out the option's value
466  */
467  if (da->flags.array) {
468  slen = fr_pair_array_to_network(&work_dbuff, da_stack, depth, cursor, encode_ctx, encode_value);
469  } else {
470  slen = encode_value(&work_dbuff, da_stack, depth, cursor, encode_ctx);
471  }
472  if (slen < 0) return slen;
473 
474  /*
475  * Write out the option number and length (before the value we just wrote)
476  */
477  (void) encode_option_hdr(&hdr, (uint16_t)da->attr, (uint16_t) (fr_dbuff_used(&work_dbuff) - DHCPV6_OPT_HDR_LEN));
478 
479  FR_PROTO_HEX_DUMP(fr_dbuff_start(&work_dbuff), fr_dbuff_used(&work_dbuff), "Done RFC header");
480 
481  return fr_dbuff_set(dbuff, &work_dbuff);
482 }
483 
485  fr_da_stack_t *da_stack, unsigned int depth,
486  fr_dcursor_t *cursor, void *encode_ctx)
487 {
488  fr_dbuff_t work_dbuff = FR_DBUFF(dbuff);
489  fr_dbuff_marker_t hdr;
490  fr_dict_attr_t const *da = da_stack->da[depth];
491  ssize_t len;
492 
493  fr_dbuff_marker(&hdr, &work_dbuff);
495  FR_PROTO_STACK_PRINT(da_stack, depth);
496 
497  if (da_stack->da[depth]->type != FR_TYPE_TLV) {
498  fr_strerror_printf("%s: Expected type \"tlv\" got \"%s\"", __FUNCTION__,
499  fr_type_to_str(da_stack->da[depth]->type));
501  }
502 
503  if (!da_stack->da[depth + 1]) {
504  fr_strerror_printf("%s: Can't encode empty TLV", __FUNCTION__);
506  }
507 
508  FR_DBUFF_ADVANCE_RETURN(&work_dbuff, DHCPV6_OPT_HDR_LEN); /* Make room for option header */
509 
510  len = fr_pair_cursor_to_network(&work_dbuff, da_stack, depth, cursor, encode_ctx, encode_child);
511  if (len < 0) return len;
512 
513  /*
514  * 0 1 2 3
515  * 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
516  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
517  * | option-code | option-len |
518  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
519  */
520  (void) encode_option_hdr(&hdr, (uint16_t)da->attr, (uint16_t) (fr_dbuff_used(&work_dbuff) - DHCPV6_OPT_HDR_LEN));
521 
522  FR_PROTO_HEX_DUMP(fr_dbuff_start(&work_dbuff), fr_dbuff_used(&work_dbuff), "Done TLV header");
523 
524  return fr_dbuff_set(dbuff, &work_dbuff);
525 }
526 
527 /** Encode a Vendor-Specific Information Option
528  *
529  * 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
530  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
531  * | OPTION_VENDOR_OPTS | option-len |
532  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
533  * | enterprise-number |
534  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
535  * . .
536  * . option-data .
537  * . .
538  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
539  */
541  fr_da_stack_t *da_stack, unsigned int depth,
542  fr_dcursor_t *cursor, void *encode_ctx)
543 {
544  fr_dbuff_t work_dbuff = FR_DBUFF(dbuff);
545  fr_dbuff_marker_t hdr;
546  fr_dict_attr_t const *da = da_stack->da[depth];
547  fr_dict_attr_t const *dv;
548  ssize_t len;
549 
550  fr_dbuff_marker(&hdr, &work_dbuff);
551  FR_PROTO_STACK_PRINT(da_stack, depth);
552 
553  /*
554  * DA should be a VSA type with the value of OPTION_VENDOR_OPTS.
555  */
556  if (da->type != FR_TYPE_VSA) {
557  fr_strerror_printf("%s: Expected type \"vsa\" got \"%s\"", __FUNCTION__,
558  fr_type_to_str(da->type));
560  }
561 
562  /*
563  * Now process the vendor ID part (which is one attribute deeper)
564  */
565  dv = da_stack->da[++depth];
566  FR_PROTO_STACK_PRINT(da_stack, depth);
567 
568  if (dv->type != FR_TYPE_VENDOR) {
569  fr_strerror_printf("%s: Expected type \"vsa\" got \"%s\"", __FUNCTION__,
570  fr_type_to_str(dv->type));
572  }
573 
575  fr_dbuff_advance(&work_dbuff, DHCPV6_OPT_HDR_LEN);
576  FR_DBUFF_IN_RETURN(&work_dbuff, dv->attr);
577 
578  /*
579  * https://tools.ietf.org/html/rfc8415#section-21.17 says:
580  *
581  * The vendor-option-data field MUST be encoded as a sequence of
582  * code/length/value fields of format identical to the DHCP options (see
583  * Section 21.1). The sub-option codes are defined by the vendor
584  * identified in the enterprise-number field and are not managed by
585  * IANA. Each of the sub-options is formatted as follows:
586  *
587  * 0 1 2 3
588  * 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
589  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
590  * | sub-opt-code | sub-option-len |
591  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
592  * . .
593  * . sub-option-data .
594  * . .
595  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
596  */
597 
598  /*
599  * Encode the different data types
600  */
601  len = encode_child(&work_dbuff, da_stack, depth + 1, cursor, encode_ctx);
602  if (len < 0) return len;
603 
604  (void) encode_option_hdr(&hdr, da->attr, fr_dbuff_used(&work_dbuff) - DHCPV6_OPT_HDR_LEN);
605 
606  FR_PROTO_HEX_DUMP(fr_dbuff_start(&work_dbuff), fr_dbuff_used(&work_dbuff), "Done VSIO header");
607 
608  return fr_dbuff_set(dbuff, &work_dbuff);
609 }
610 
611 /** Encode a Relay-Message
612  *
613  * Header + stuff
614  */
616  fr_da_stack_t *da_stack, unsigned int depth,
617  fr_dcursor_t *cursor, UNUSED void *encode_ctx)
618 {
619  fr_dbuff_t work_dbuff = FR_DBUFF(dbuff);
620  fr_dbuff_marker_t len_m;
621  ssize_t slen;
622 
623  fr_dict_attr_t const *da = da_stack->da[depth];
624  fr_pair_t *vp;
625 
626  FR_PROTO_STACK_PRINT(da_stack, depth);
627 
628  /*
629  * Skip empty relay messages...
630  * This shouldn't really happen.
631  */
632  vp = fr_dcursor_current(cursor);
633  if (fr_pair_list_empty(&vp->vp_group)) {
634  vp = fr_dcursor_next(cursor);
635  fr_proto_da_stack_build(da_stack, vp ? vp->da : NULL);
636  return 0;
637  }
638 
639  /*
640  * Write out the header
641  */
642  FR_DBUFF_IN_RETURN(&work_dbuff, (uint16_t)da->attr); /* Write out the option header */
643  fr_dbuff_marker(&len_m, &work_dbuff); /* Mark where we'll need to put the length field */
644  FR_DBUFF_ADVANCE_RETURN(&work_dbuff, 2); /* Advanced past the length field */
645 
646  vp = fr_dcursor_current(cursor);
647  slen = fr_dhcpv6_encode(&work_dbuff, NULL, 0, 0, &vp->vp_group);
648  if (slen <= 0) return slen;
649 
650  fr_dbuff_in(&len_m, (uint16_t)slen); /* Write out the length value */
651 
652  FR_PROTO_HEX_DUMP(fr_dbuff_start(&work_dbuff), fr_dbuff_used(&work_dbuff), "Done Relay-Message header");
653 
654  vp = fr_dcursor_next(cursor);
655  fr_proto_da_stack_build(da_stack, vp ? vp->da : NULL);
656 
657  return fr_dbuff_set(dbuff, &work_dbuff);
658 }
659 
660 /** Encode a DHCPv6 option and any sub-options.
661  *
662  * @param[out] dbuff Where to write encoded DHCP attributes.
663  * @param[in] cursor with current VP set to the option to be encoded.
664  * Will be advanced to the next option to encode.
665  * @param[in] encode_ctx containing parameters for the encoder.
666  * @return
667  * - > 0 length of data written.
668  * - < 0 error.
669  */
671 {
672  fr_pair_t *vp;
673  unsigned int depth = 0;
674  fr_da_stack_t da_stack;
675  fr_dbuff_t work_dbuff = FR_DBUFF_MAX(dbuff, DHCPV6_OPT_HDR_LEN + UINT16_MAX);
676  ssize_t slen;
677 
678  vp = fr_dcursor_current(cursor);
679  if (!vp) return 0;
680 
681  FR_PROTO_TRACE("encoding option %s", vp->da->name);
682 
683  if (vp->da->flags.internal) {
684  fr_strerror_printf("Attribute \"%s\" is not a DHCPv6 option", vp->da->name);
685  fr_dcursor_next(cursor);
686  return 0;
687  }
688 
689  fr_proto_da_stack_build(&da_stack, vp->da);
690 
691  FR_PROTO_STACK_PRINT(&da_stack, depth);
692 
693  /*
694  * Deal with nested options
695  */
696  switch (da_stack.da[depth]->type) {
697  case FR_TYPE_GROUP:
698  /*
699  * Relay-Message has a special format, it's an entire packet. :(
700  */
701  if (da_stack.da[depth] == attr_relay_message) {
702  slen = encode_relay_message(&work_dbuff, &da_stack, depth, cursor, encode_ctx);
703  break;
704  }
705 
706  slen = encode_rfc(&work_dbuff, &da_stack, depth, cursor, encode_ctx);
707  break;
708 
709  default:
710  slen = encode_child(&work_dbuff, &da_stack, depth, cursor, encode_ctx);
711  break;
712  }
713  if (slen < 0) return slen;
714 
715  FR_PROTO_TRACE("Complete option is %zu byte(s)", fr_dbuff_used(&work_dbuff));
716  FR_PROTO_HEX_DUMP(fr_dbuff_start(&work_dbuff), fr_dbuff_used(&work_dbuff), NULL);
717 
718  return fr_dbuff_set(dbuff, &work_dbuff);
719 }
720 
722 {
723  ssize_t slen;
724  fr_dcursor_t cursor;
725  fr_dbuff_t work_dbuff = FR_DBUFF(dbuff);
726 
727  fr_assert(dict_dhcpv6 != NULL);
728 
730 
731  while (fr_dcursor_current(&cursor) != NULL) {
732  slen = fr_dhcpv6_encode_option(&work_dbuff, &cursor, &(fr_dhcpv6_encode_ctx_t){ .root = fr_dict_root(dict_dhcpv6) });
733  if (slen < 0) return slen;
734  }
735 
736  FR_PROTO_TRACE("Foreign option is %zu byte(s)", fr_dbuff_used(&work_dbuff));
737  FR_PROTO_HEX_DUMP(dbuff->p, fr_dbuff_used(&work_dbuff), NULL);
738 
739  return fr_dbuff_set(dbuff, &work_dbuff);
740 }
741 
742 
743 static int encode_test_ctx(void **out, TALLOC_CTX *ctx)
744 {
745  fr_dhcpv6_encode_ctx_t *test_ctx;
746 
747  test_ctx = talloc_zero(ctx, fr_dhcpv6_encode_ctx_t);
748  if (!test_ctx) return -1;
749 
750  test_ctx->root = fr_dict_root(dict_dhcpv6);
751 
752  *out = test_ctx;
753 
754  return 0;
755 }
756 
757 static ssize_t fr_dhcpv6_encode_proto(UNUSED TALLOC_CTX *ctx, fr_pair_list_t *vps, uint8_t *data, size_t data_len, UNUSED void *proto_ctx)
758 {
759  ssize_t slen;
760 
761  slen = fr_dhcpv6_encode(&FR_DBUFF_TMP(data, data_len), NULL, 0, 0, vps);
762 
763 #ifndef NDEBUG
764  if (slen <= 0) return slen;
765 
766  if (fr_debug_lvl > 2) {
767  fr_dhcpv6_print_hex(stdout, data, slen);
768  }
769 #endif
770 
771  return slen;
772 }
773 
774 /*
775  * Test points
776  */
780  .func = fr_dhcpv6_encode_option,
781  .next_encodable = fr_dhcpv6_next_encodable,
782 };
783 
787  .func = fr_dhcpv6_encode_proto
788 };
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition: build.h:320
#define UNUSED
Definition: build.h:313
#define fr_dbuff_advance(_dbuff_or_marker, _len)
Advance 'current' position in dbuff or marker by _len bytes.
Definition: dbuff.h:1067
#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:762
#define FR_DBUFF_ADVANCE_RETURN(_dbuff_or_marker, _len)
Advance the 'current' position in dbuff or marker by _len bytes returning if _len is out of range.
Definition: dbuff.h:1083
#define FR_DBUFF_EXTEND_LOWAT_OR_RETURN(_dbuff_or_marker, _lowat)
Extend if we're below _lowat and return if we can't extend above _lowat.
Definition: dbuff.h:668
struct fr_dbuff_marker_s fr_dbuff_marker_t
A position marker associated with a dbuff.
Definition: dbuff.h:81
#define fr_dbuff_start(_dbuff_or_marker)
Return the 'start' position of a dbuff or marker.
Definition: dbuff.h:893
#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:1377
#define fr_dbuff_in(_dbuff_or_marker, _in)
Copy data from a fixed sized C type into a dbuff or marker.
Definition: dbuff.h:1562
#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:1580
#define FR_DBUFF(_dbuff_or_marker)
Create a new dbuff pointing to the same underlying buffer.
Definition: dbuff.h:222
#define FR_DBUFF_MAX(_dbuff_or_marker, _max)
Limit the maximum number of bytes available in the dbuff when passing it to another function.
Definition: dbuff.h:301
static uint8_t * fr_dbuff_marker(fr_dbuff_marker_t *m, fr_dbuff_t *dbuff)
Initialises a new marker pointing to the 'current' position of the dbuff.
Definition: dbuff.h:1187
#define fr_dbuff_out(_out, _dbuff_or_marker)
Copy data from a dbuff or marker to a fixed sized C type.
Definition: dbuff.h:1779
#define FR_DBUFF_IN_BYTES_RETURN(_dbuff_or_marker,...)
Copy a byte sequence into a dbuff or marker returning if there's insufficient space.
Definition: dbuff.h:1467
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
Definition: dbuff.h:509
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition: dcursor.h:287
static void * fr_dcursor_current(fr_dcursor_t *cursor)
Return the item the cursor current points to.
Definition: dcursor.h:336
#define da_is_dns_label(_da)
Definition: dhcpv4.h:77
Implementation of the DHCPv6 protocol.
fr_dict_attr_t const * root
Root attribute of the dictionary.
Definition: dhcpv6.h:135
#define DHCPV6_DATE_OFFSET
Definition: dhcpv6.h:132
#define DHCPV6_OPT_HDR_LEN
Definition: dhcpv6.h:45
@ FLAG_ENCODE_PARTIAL_DNS_LABEL
encode as a partial DNS label
Definition: dhcpv6.h:117
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition: dict_util.c:1997
static fr_dict_attr_t const * fr_dict_attr_ref(fr_dict_attr_t const *da)
Return the reference associated with a group type attribute.
Definition: dict_ext.h:164
ssize_t fr_dns_label_from_value_box_dbuff(fr_dbuff_t *dbuff, bool compression, fr_value_box_t const *value, fr_dns_labels_t *lb)
Encode a single value box of type string, serializing its contents to a dns label in a dbuff.
Definition: dns.c:604
#define PAIR_ENCODE_FATAL_ERROR
Fatal encoding error.
Definition: pair.h:37
ssize_t fr_pair_array_to_network(fr_dbuff_t *dbuff, fr_da_stack_t *da_stack, int depth, fr_dcursor_t *cursor, void *encode_ctx, fr_encode_dbuff_t encode_value)
Encode an array of values from the network.
Definition: encode.c:42
ssize_t fr_pair_ref_to_network(fr_dbuff_t *dbuff, fr_da_stack_t *da_stack, unsigned int depth, fr_dcursor_t *cursor)
Encode a foreign reference to the network.
Definition: encode.c:123
ssize_t fr_pair_cursor_to_network(fr_dbuff_t *dbuff, fr_da_stack_t *da_stack, unsigned int depth, fr_dcursor_t *cursor, void *encode_ctx, fr_encode_dbuff_t encode_pair)
Definition: encode.c:71
int fr_debug_lvl
Definition: log.c:42
unsigned short uint16_t
Definition: merged_model.c:31
@ FR_TYPE_TLV
Contains nested attributes.
Definition: merged_model.c:118
@ FR_TYPE_IPV6_PREFIX
IPv6 Prefix.
Definition: merged_model.c:89
@ 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_DATE
Unix time stamp, always has value >2^31.
Definition: merged_model.c:111
@ 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_STRUCT
like TLV, but without T or L, and fixed-width children
Definition: merged_model.c:119
@ FR_TYPE_VENDOR
Attribute that represents a vendor in the attribute tree.
Definition: merged_model.c:122
@ FR_TYPE_IPV6_ADDR
128 Bit IPv6 Address.
Definition: merged_model.c:88
@ FR_TYPE_IPV4_PREFIX
IPv4 Prefix.
Definition: merged_model.c:87
@ FR_TYPE_BOOL
A truth value.
Definition: merged_model.c:95
@ FR_TYPE_VSA
Vendor-Specific, for RADIUS attribute 26.
Definition: merged_model.c:121
@ FR_TYPE_OCTETS
Raw octets.
Definition: merged_model.c:84
@ FR_TYPE_GROUP
A grouping of other attributes.
Definition: merged_model.c:124
uint8_t * p
Definition: merged_model.c:42
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
static uint8_t depth(fr_minmax_heap_index_t i)
Definition: minmax_heap.c:83
static unsigned int fr_bytes_from_bits(unsigned int bits)
Convert bits (as in prefix length) to bytes, rounding up.
Definition: nbo.h:228
void fr_proto_da_stack_build(fr_da_stack_t *stack, fr_dict_attr_t const *da)
Build a complete DA stack from the da back to the root.
Definition: proto.c:118
static fr_dict_t const * dict_dhcpv6
Definition: proto_dhcpv6.c:86
static fr_dict_attr_t const * attr_relay_message
static fr_internal_encode_ctx_t encode_ctx
void * fr_dhcpv6_next_encodable(fr_dlist_head_t *list, void *current, void *uctx)
DHCPV6-specific iterator.
Definition: base.c:698
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:891
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:717
fr_test_point_pair_encode_t dhcpv6_tp_encode_pair
Definition: encode.c:778
static ssize_t fr_dhcpv6_encode_proto(UNUSED TALLOC_CTX *ctx, fr_pair_list_t *vps, uint8_t *data, size_t data_len, UNUSED void *proto_ctx)
Definition: encode.c:757
static ssize_t encode_relay_message(fr_dbuff_t *dbuff, fr_da_stack_t *da_stack, unsigned int depth, fr_dcursor_t *cursor, UNUSED void *encode_ctx)
Encode a Relay-Message.
Definition: encode.c:615
static ssize_t encode_option_hdr(fr_dbuff_marker_t *m, uint16_t option, size_t data_len)
Macro-like function for encoding an option header.
Definition: encode.c:71
static ssize_t encode_value(fr_dbuff_t *dbuff, fr_da_stack_t *da_stack, unsigned int depth, fr_dcursor_t *cursor, void *encode_ctx)
Definition: encode.c:80
static ssize_t encode_child(fr_dbuff_t *dbuff, fr_da_stack_t *da_stack, unsigned int depth, fr_dcursor_t *cursor, void *encode_ctx)
Definition: encode.c:373
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
static ssize_t encode_tlv(fr_dbuff_t *dbuff, fr_da_stack_t *da_stack, unsigned int depth, fr_dcursor_t *cursor, void *encode_ctx)
Definition: encode.c:484
fr_test_point_proto_encode_t dhcpv6_tp_encode_proto
Definition: encode.c:785
static int encode_test_ctx(void **out, TALLOC_CTX *ctx)
Definition: encode.c:743
static ssize_t encode_rfc(fr_dbuff_t *dbuff, fr_da_stack_t *da_stack, unsigned int depth, fr_dcursor_t *cursor, void *encode_ctx)
Encode an RFC format TLV.
Definition: encode.c:446
ssize_t fr_dhcpv6_encode_foreign(fr_dbuff_t *dbuff, fr_pair_list_t const *list)
Definition: encode.c:721
static ssize_t encode_vsio(fr_dbuff_t *dbuff, fr_da_stack_t *da_stack, unsigned int depth, fr_dcursor_t *cursor, void *encode_ctx)
Encode a Vendor-Specific Information Option.
Definition: encode.c:540
VQP attributes.
fr_assert(0)
fr_pair_t * vp
ssize_t fr_struct_to_network(fr_dbuff_t *dbuff, fr_da_stack_t *da_stack, unsigned int depth, fr_dcursor_t *parent_cursor, void *encode_ctx, fr_encode_dbuff_t encode_value, fr_encode_dbuff_t encode_pair)
Definition: struct.c:478
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
fr_test_point_ctx_alloc_t test_ctx
Allocate a test ctx for the encoder.
Definition: test_point.h:92
fr_test_point_ctx_alloc_t test_ctx
Allocate a test ctx for the encoder.
Definition: test_point.h:74
Entry point for pair encoders.
Definition: test_point.h:91
Entry point for protocol encoders.
Definition: test_point.h:73
static int64_t fr_unix_time_to_sec(fr_unix_time_t delta)
Definition: time.h:504
#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:568
static fr_pair_t * fr_pair_dcursor_child_iter_init(fr_dcursor_t *cursor, fr_pair_list_t const *list, fr_dcursor_t const *parent)
Initializes a child dcursor from a parent cursor, with an iteration function.
Definition: pair.h:610
bool fr_pair_list_empty(fr_pair_list_t const *list)
Is a valuepair list empty.
Definition: pair_inline.c:125
#define PAIR_VERIFY(_x)
Definition: pair.h:190
#define FR_PROTO_HEX_DUMP(_data, _data_len, _fmt,...)
Definition: proto.h:41
#define FR_PROTO_TRACE(_fmt,...)
Definition: proto.h:40
#define FR_PROTO_STACK_PRINT(_stack, _depth)
Definition: proto.h:43
fr_dict_attr_t const * da[FR_DICT_MAX_TLV_STACK+1]
The stack.
Definition: proto.h:56
Structure for holding the stack of dictionary attributes being encoded.
Definition: proto.h:54
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition: strerror.h:64
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition: types.h:433
#define fr_type_is_structural(_x)
Definition: types.h:371
return fr_dbuff_set(dbuff, &our_dbuff)
ssize_t fr_value_box_to_network(fr_dbuff_t *dbuff, fr_value_box_t const *value)
Encode a single value box, serializing its contents in generic network format.
Definition: value.c:1359
static fr_slen_t data
Definition: value.h:1259
static size_t char ** out
Definition: value.h:984