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: 804aa699298e3636eb78960daa48967705ff1408 $
19  *
20  * @file protocols/dhcpv4/encode.c
21  * @brief Functions to encode DHCP options.
22  *
23  * @copyright 2008,2017 The FreeRADIUS server project
24  * @copyright 2008 Alan DeKok (aland@deployingradius.com)
25  * @copyright 2015,2017 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
26  */
27 #include <freeradius-devel/io/test_point.h>
28 #include <freeradius-devel/util/dbuff.h>
29 #include <freeradius-devel/util/proto.h>
30 #include <freeradius-devel/util/struct.h>
31 #include <freeradius-devel/util/dns.h>
32 #include <freeradius-devel/util/encode.h>
33 
34 #include "dhcpv4.h"
35 #include "attrs.h"
36 
37 static ssize_t encode_value(fr_dbuff_t *dbuff,
38  fr_da_stack_t *da_stack, unsigned int depth,
39  fr_dcursor_t *cursor, void *encode_ctx);
40 
41 static ssize_t encode_child(fr_dbuff_t *dbuff,
42  fr_da_stack_t *da_stack, unsigned int depth,
43  fr_dcursor_t *cursor, void *encode_ctx);
44 
45 /** Write DHCP option value into buffer
46  *
47  * Does not include DHCP option length or number.
48  *
49  * @param[out] dbuff buffer to write the option to.
50  * @param[in] da_stack Describing nesting of options.
51  * @param[in] depth in da_stack.
52  * @param[in,out] cursor Current attribute we're encoding.
53  * @param[in] encode_ctx Containing DHCPv4 dictionary.
54  * @return
55  * - The length of data written, may return 0 for bools
56  * < 0 if there's not enough space or option type is unsupported
57  */
59  fr_da_stack_t *da_stack, unsigned int depth,
60  fr_dcursor_t *cursor, void *encode_ctx)
61 {
62  fr_pair_t *vp = fr_dcursor_current(cursor);
63  fr_dbuff_t work_dbuff = FR_DBUFF(dbuff);
64  fr_dict_attr_t const *da = da_stack->da[depth];
65  ssize_t slen;
66 
67 
68  FR_PROTO_STACK_PRINT(da_stack, depth);
69  FR_PROTO_TRACE("%zu byte(s) available for value", fr_dbuff_remaining(dbuff));
70 
71  /*
72  * Structures are special.
73  */
74  if ((vp->vp_type == FR_TYPE_STRUCT) || (da->type == FR_TYPE_STRUCT)) {
75  slen = fr_struct_to_network(&work_dbuff, da_stack, depth, cursor, encode_ctx, encode_value, encode_child);
76  if (slen <= 0) return slen;
77 
78  /*
79  * Rebuild the da_stack for the next option.
80  */
81  vp = fr_dcursor_current(cursor);
82  fr_proto_da_stack_build(da_stack, vp ? vp->da : NULL);
83  return fr_dbuff_set(dbuff, &work_dbuff);
84  }
85 
86  switch (da_stack->da[depth]->type) {
88  FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, vp->vp_ip.prefix);
89  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, (uint8_t const *)&vp->vp_ipv6addr, sizeof(vp->vp_ipv6addr));
90  break;
91 
92  case FR_TYPE_IPV6_ADDR:
93  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff, (uint8_t const *)&vp->vp_ipv6addr, sizeof(vp->vp_ipv6addr));
94  break;
95 
96  /*
97  * "option exists" == true.
98  * "option does not exist" == false
99  *
100  * fr_dhcpv4_next_encodable() takes care of skipping bools which are false.
101  *
102  * Rapid-Commit does this. Options 19/20 require encoding as one byte of 0/1.
103  */
104  case FR_TYPE_BOOL:
105  if (fr_dhcpv4_flag_exists(vp->da)) {
106  break;
107  }
108  FR_DBUFF_IN_RETURN(&work_dbuff, (uint8_t) (vp->vp_bool == true));
109  break;
110 
111  case FR_TYPE_IPV4_PREFIX:
113  uint32_t mask;
114 
115  mask = ~((~(uint32_t) 0) >> vp->vp_ip.prefix);
116 
117  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff,
118  (uint8_t const *)&vp->vp_ipv4addr,
119  sizeof(vp->vp_ipv4addr));
120  FR_DBUFF_IN_RETURN(&work_dbuff, mask);
121  break;
122  }
123 
125  size_t num_bytes = (vp->vp_ip.prefix + 0x07) >> 3;
126 
127  FR_DBUFF_IN_RETURN(&work_dbuff, (uint8_t) vp->vp_ip.prefix);
128 
129  if (num_bytes) {
130  FR_DBUFF_IN_MEMCPY_RETURN(&work_dbuff,
131  (uint8_t const *)&vp->vp_ipv4addr,
132  num_bytes);
133  }
134 
135  break;
136  }
137 
138  goto from_network;
139 
140  case FR_TYPE_STRING:
141  /*
142  * DNS labels get a special encoder. DNS labels
143  * MUST NOT be compressed in DHCP.
144  *
145  * https://tools.ietf.org/html/rfc8415#section-10
146  */
147  if (fr_dhcpv4_flag_dns_label(da)) {
148  fr_dbuff_marker_t last_byte, src;
149 
150  fr_dbuff_marker(&last_byte, &work_dbuff);
151  fr_dbuff_marker(&src, &work_dbuff);
152  slen = fr_dns_label_from_value_box_dbuff(&work_dbuff, false, &vp->data, NULL);
153  if (slen < 0) return slen;
154  break;
155  }
156  FALL_THROUGH;
157 
158  default:
159  from_network:
160  slen = fr_value_box_to_network(&work_dbuff, &vp->data);
161  if (slen < 0) return slen;
162  break;
163  }
164 
165  vp = fr_dcursor_next(cursor); /* We encoded a leaf, advance the cursor */
166  fr_proto_da_stack_build(da_stack, vp ? vp->da : NULL);
167 
168  FR_PROTO_STACK_PRINT(da_stack, depth);
169  FR_PROTO_HEX_DUMP(dbuff->p, fr_dbuff_used(&work_dbuff), "Value");
170 
171  return fr_dbuff_set(dbuff, &work_dbuff);
172 }
173 
174 
175 /** Extend an encoded option in-place.
176  *
177  * @param[in] dbuff buffer containing the option
178  * @param[in] hdr marker (with dbuff as parent) set to where the option starts
179  * @param[in] len length of the data being written
180  * @return
181  * - <0 if we can't extend the option
182  * - >0 if we can, with hdr set to where the next option should start
183  * @note The option starts with a two-byte (type, length) header, where
184  * the length does *not* include the two bytes for the header.
185  * The starting length may be non-zero, hence its counting towards
186  * the header_byte calculation and its inclusion in sublen calculation.
187  * (All those following start out empty, hence the initialization
188  * of their lengths to zero.)
189  */
190 static ssize_t extend_option(fr_dbuff_t *dbuff, fr_dbuff_marker_t *hdr, size_t len)
191 {
192  size_t header_bytes;
193  uint8_t type = 0, option_len = 0;
194  fr_dbuff_marker_t dst, tmp;
195 
196  /*
197  * This can't follow the convention of operating on
198  * a chlld dbuff because it must work on and amidst
199  * already-written data.
200  */
201 
202  fr_dbuff_marker(&dst, dbuff);
203  fr_dbuff_marker(&tmp, dbuff);
204 
205  fr_dbuff_set(&tmp, hdr);
206 
207  /*
208  * Read the current header.
209  */
210  if (fr_dbuff_out(&type, &tmp) < 0 || fr_dbuff_out(&option_len, &tmp) < 0) {
211  error:
214  return -1;
215  }
216 
217  len += option_len;
218 
219  /*
220  * How many bytes we will need to add for all headers.
221  */
222  header_bytes = (option_len / 255) * 2;
223 
224  /*
225  * No room for the new headers and data, we're done.
226  */
227  if (fr_dbuff_extend_lowat(NULL, dbuff, header_bytes) < header_bytes) goto error;
228 
229  /*
230  * Moving the same data repeatedly in a loop is simpler
231  * and less error-prone than anything smarter.
232  */
233  while (true) {
234  uint8_t sublen;
235 
236  sublen = (len > 255) ? 255 : len;
237 
238  /*
239  * Write the new header, including the (possibly partial) length.
240  */
241  fr_dbuff_set(&tmp, fr_dbuff_current(hdr));
242  FR_DBUFF_IN_BYTES_RETURN(&tmp, type, sublen);
243 
244  /*
245  * The data is already where it's supposed to be, and the length is in the header, and
246  * the length is small. We're done.
247  */
248  len -= sublen;
249  if (!len) {
250  fr_dbuff_set(dbuff, fr_dbuff_current(hdr) + sublen + 2);
251  len = sublen;
252  break;
253  }
254 
255  /*
256  * Take the current header, skip it, and then skip the data we just encoded. That is the
257  * location of the "next" header.
258  */
259  fr_dbuff_set(&tmp, fr_dbuff_current(hdr) + 2 + 255);
260  fr_dbuff_set(hdr, &tmp);
261 
262  /*
263  * The data is currently overlapping with the next header. We have to move it two bytes forward to
264  * make room for the header.
265  */
266  fr_dbuff_set(&dst, fr_dbuff_current(&tmp) + 2);
267  fr_dbuff_move(&dst, &tmp, len);
268  }
269 
272  return len;
273 }
274 
275 #define DHCPV4_OPT_HDR_LEN (2)
276 
277 /** Write out an RFC option header and option data
278  *
279  * @note May coalesce options with fixed width values
280  *
281  * @param[out] dbuff buffer to write the TLV to.
282  * @param[in] da_stack Describing nesting of options.
283  * @param[in] depth in the da_stack.
284  * @param[in,out] cursor Current attribute we're encoding.
285  * @param[in] encode_ctx Containing DHCPv4 dictionary.
286  * @return
287  * - >0 length of data encoded.
288  * - 0 if we ran out of space.
289  * - < 0 on error.
290  */
292  fr_da_stack_t *da_stack, unsigned int depth,
293  fr_dcursor_t *cursor, void *encode_ctx)
294 {
295  ssize_t len;
296  fr_dbuff_marker_t hdr;
297  fr_dict_attr_t const *da = da_stack->da[depth];
298  fr_dbuff_t work_dbuff = FR_DBUFF(dbuff);
299 
300  FR_PROTO_STACK_PRINT(da_stack, depth);
301 
302  /*
303  * Write out the option number and length (which, unlike RADIUS,
304  * is just the length of the value and hence starts out as zero).
305  */
306  fr_dbuff_marker(&hdr, &work_dbuff);
307  FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, (uint8_t)da->attr, (uint8_t) 0);
308 
309  /*
310  * Write out the option's value
311  */
312  if (da->flags.array) {
313  len = fr_pair_array_to_network(&work_dbuff, da_stack, depth, cursor, encode_ctx, encode_value);
314  if (len < 0) return -1;
315 
316  } else if (da->parent && (da->parent->type != FR_TYPE_VENDOR)) {
317  fr_pair_t *vp;
318 
319  do {
320  len = encode_value(&work_dbuff, da_stack, depth, cursor, encode_ctx);
321  if (len < 0) return len; /* @todo return the correct offset, but whatever */
322 
323  vp = fr_dcursor_current(cursor);
324  } while (vp && (vp->da == da));
325 
326  } else {
327  /*
328  * For VSAs, each vendor value is prefixed by an 8-bit length, so we don't loop over the
329  * input pairs.
330  */
331  len = encode_value(&work_dbuff, da_stack, depth, cursor, encode_ctx);
332  if (len < 0) return len; /* @todo return the correct offset, but whatever */
333  }
334 
335  len = fr_dbuff_used(&work_dbuff) - 2;
336 
337  if (len <= UINT8_MAX) {
338  fr_dbuff_advance(&hdr, 1);
339  FR_DBUFF_IN_RETURN(&hdr, (uint8_t) len);
340 
341  } else if (extend_option(&work_dbuff, &hdr, len) < 0) {
343  }
344 
345  FR_PROTO_HEX_DUMP(fr_dbuff_start(&work_dbuff), fr_dbuff_used(&work_dbuff), "Done RFC header");
346 
347  return fr_dbuff_set(dbuff, &work_dbuff);
348 }
349 
350 static ssize_t encode_vsio(fr_dbuff_t *dbuff,
351  fr_da_stack_t *da_stack, unsigned int depth,
352  fr_dcursor_t *cursor, void *encode_ctx);
353 
354 static ssize_t encode_tlv(fr_dbuff_t *dbuff,
355  fr_da_stack_t *da_stack, unsigned int depth,
356  fr_dcursor_t *cursor, void *encode_ctx);
357 
359  fr_da_stack_t *da_stack, unsigned int depth,
360  fr_dcursor_t *cursor, void *encode_ctx)
361 {
362  ssize_t len;
363  fr_pair_t *vp = fr_dcursor_current(cursor);
364  fr_dcursor_t child_cursor;
365  fr_dbuff_t work_dbuff;
366 
367  if (da_stack->da[depth]) {
368  /*
369  * Determine the nested type and call the appropriate encoder
370  */
371  switch (da_stack->da[depth]->type) {
372  case FR_TYPE_TLV:
373  if (!da_stack->da[depth + 1]) break;
374 
375  return encode_tlv(dbuff, da_stack, depth, cursor, encode_ctx);
376 
377  case FR_TYPE_VSA:
378  if (!da_stack->da[depth + 1]) break;
379 
380  return encode_vsio(dbuff, da_stack, depth, cursor, encode_ctx);
381 
382  default:
383  return encode_rfc(dbuff, da_stack, depth, cursor, encode_ctx);
384  }
385  }
386 
388 
389  fr_pair_dcursor_child_iter_init(&child_cursor, &vp->vp_group, cursor);
390  work_dbuff = FR_DBUFF(dbuff);
391 
392  while ((vp = fr_dcursor_current(&child_cursor)) != NULL) {
393  fr_proto_da_stack_build(da_stack, vp->da);
394 
395  switch (da_stack->da[depth]->type) {
396  case FR_TYPE_VSA:
397  len = encode_vsio(&work_dbuff, da_stack, depth, &child_cursor, encode_ctx);
398  break;
399 
400  case FR_TYPE_TLV:
401  len = encode_tlv(&work_dbuff, da_stack, depth, &child_cursor, encode_ctx);
402  break;
403 
404  default:
405  len = encode_rfc(&work_dbuff, da_stack, depth, &child_cursor, encode_ctx);
406  break;
407  }
408 
409  if (len <= 0) return len;
410  }
411 
412  /*
413  * Skip over the attribute we just encoded.
414  */
415  vp = fr_dcursor_next(cursor);
416  fr_proto_da_stack_build(da_stack, vp ? vp->da : NULL);
417 
418  return fr_dbuff_set(dbuff, &work_dbuff);
419 }
420 
421 
422 
423 /** Write out a TLV header (and any sub TLVs or values)
424  *
425  * @param[out] dbuff buffer to write the TLV to.
426  * @param[in] da_stack Describing nesting of options.
427  * @param[in] depth in the da_stack.
428  * @param[in,out] cursor Current attribute we're encoding.
429  * @param[in] encode_ctx Containing DHCPv4 dictionary.
430  * @return
431  * - >0 length of data encoded.
432  * - 0 if we ran out of space.
433  * - < 0 on error.
434  */
436  fr_da_stack_t *da_stack, unsigned int depth,
437  fr_dcursor_t *cursor, void *encode_ctx)
438 {
439  ssize_t len, option_len;
440  fr_dbuff_t work_dbuff = FR_DBUFF(dbuff);
441  fr_dbuff_marker_t hdr, dst, tmp;
442  fr_pair_t const *vp = fr_dcursor_current(cursor);
443  fr_dict_attr_t const *da = da_stack->da[depth];
444  uint8_t option_number;
445 
446  FR_PROTO_STACK_PRINT(da_stack, depth);
447 
448  /*
449  * Where the TLV header starts.
450  */
451  fr_dbuff_marker(&hdr, &work_dbuff);
452 
453  /*
454  * These are set before use; their initial value doesn't matter.
455  */
456  fr_dbuff_marker(&dst, &work_dbuff);
457  fr_dbuff_marker(&tmp, &work_dbuff);
458 
459  /*
460  * Write out the option number and length (which, unlike RADIUS,
461  * is just the length of the value and hence starts out as zero).
462  */
463  option_number = (uint8_t)da->attr;
464  option_len = 0;
465  FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, option_number, option_len);
466 
467  /*
468  * Encode any sub TLVs or values
469  */
470  while (fr_dbuff_extend_lowat(NULL, &work_dbuff, 3) >= 3) {
471  len = encode_child(&work_dbuff, da_stack, depth + 1, cursor, encode_ctx);
472  if (len < 0) return len;
473  if (len == 0) break; /* Insufficient space */
474 
475  /*
476  * If the newly added data fits within the current option, then
477  * update the header, and go to the next option.
478  */
479  if ((option_len + len) <= 255) {
480  option_len += len;
481 
482  fr_dbuff_set(&tmp, fr_dbuff_current(&hdr) + 1);
483  FR_DBUFF_IN_BYTES_RETURN(&tmp, (uint8_t) option_len);
484 
485  } else if ((len = extend_option(&work_dbuff, &hdr, len)) < 0) {
487 
488  } else {
489  option_len = len;
490  }
491 
492  FR_PROTO_STACK_PRINT(da_stack, depth);
493  FR_PROTO_HEX_DUMP(fr_dbuff_start(&work_dbuff), fr_dbuff_used(&work_dbuff), "TLV header and sub TLVs");
494 
495  /*
496  * If nothing updated the attribute, stop
497  */
498  if (!fr_dcursor_current(cursor) || (vp == fr_dcursor_current(cursor))) break;
499 
500  /*
501  * We can encode multiple sub TLVs, if after
502  * rebuilding the TLV Stack, the attribute
503  * at this depth is the same.
504  */
505  if ((da != da_stack->da[depth]) || (da_stack->depth < da->depth)) break;
506  vp = fr_dcursor_current(cursor);
507  }
508 
509  return fr_dbuff_set(dbuff, &work_dbuff);
510 }
511 
513  fr_da_stack_t *da_stack, unsigned int depth,
514  fr_dcursor_t *cursor, void *encode_ctx)
515 {
516  fr_dbuff_t work_dbuff = FR_DBUFF_MAX(dbuff, 255 - 4 - 1 - 2);
517  fr_dbuff_marker_t hdr;
518  fr_dict_attr_t const *da;
519  fr_dict_attr_t const *dv = da_stack->da[depth - 1];
520  ssize_t len;
521  fr_pair_t *vp;
522 
523  FR_PROTO_STACK_PRINT(da_stack, depth);
524 
525  if (dv->type != FR_TYPE_VENDOR) {
526  fr_strerror_printf("%s: Expected type \"vendor\" got \"%s\"", __FUNCTION__,
527  fr_type_to_str(dv->type));
529  }
530 
531  /*
532  * Check if we have enough the enterprise-number,
533  * plus the data length, plus at least one option header.
534  */
535  FR_DBUFF_REMAINING_RETURN(&work_dbuff, sizeof(uint32_t) + 3);
536 
537  fr_dbuff_marker(&hdr, &work_dbuff);
538 
539  /*
540  * Copy in the 32bit PEN (Private Enterprise Number)
541  *
542  * And leave room for data-len1
543  */
544  FR_DBUFF_IN_RETURN(&work_dbuff, dv->attr);
545  FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, (uint8_t) 0x00);
546 
547  /*
548  * https://tools.ietf.org/html/rfc3925#section-4
549  *
550  * 1 1 1 1 1 1
551  * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
552  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
553  * | option-code | option-len |
554  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
555  * | enterprise-number1 |
556  * | |
557  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
558  * | data-len1 | |
559  * +-+-+-+-+-+-+-+-+ option-data1 |
560  * / /
561  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
562  */
563  da = da_stack->da[depth];
564 
565  /*
566  * RFC 3925 Section 4 says:
567  *
568  * Multiple instances of this option may be present and MUST be concatenated in accordance with
569  * RFC 3396.
570  *
571  * @todo - we don't currently allow encoding more data as per extend_option() or encode_tlv().
572  * We probably want to do that. We probably also want to update the decoder so that it
573  * concatenates options before decoding, too.
574  */
575  while (true) {
576  len = encode_child(&work_dbuff, da_stack, depth, cursor, encode_ctx);
577  if (len == 0) break; /* insufficient space */
578  if (len < 0) return len;
579 
580  vp = fr_dcursor_current(cursor);
581  if (!vp) break;
582 
583  /*
584  * Encode all attributes which match this vendor.
585  */
586  if (vp->da->parent != da->parent) break;
587  }
588 
589  /*
590  * Write out "data-len1" for this vendor
591  */
592  fr_dbuff_advance(&hdr, 4);
593  FR_DBUFF_IN_RETURN(&hdr, (uint8_t)(fr_dbuff_used(&work_dbuff) - 4 - 1));
594 
595 #ifndef NDEBUG
596  FR_PROTO_HEX_DUMP(dbuff->p, fr_dbuff_used(&work_dbuff), "Done VSIO Data");
597 #endif
598 
599  return fr_dbuff_set(dbuff, &work_dbuff);
600 }
601 
603  fr_da_stack_t *da_stack, unsigned int depth,
604  fr_dcursor_t *cursor, void *encode_ctx)
605 {
606  fr_dict_attr_t const *da = da_stack->da[depth];
607  fr_pair_t *vp;
608  fr_dcursor_t vendor_cursor;
609  fr_dbuff_t work_dbuff;
610  fr_dbuff_marker_t hdr;
611 
612  FR_PROTO_STACK_PRINT(da_stack, depth);
613 
614  /*
615  * DA should be a VSA type with the value of OPTION_VENDOR_OPTS.
616  */
617  if (da->type != FR_TYPE_VSA) {
618  fr_strerror_printf("%s: Expected type \"vsa\" got \"%s\"", __FUNCTION__,
619  fr_type_to_str(da->type));
621  }
622 
623  work_dbuff = FR_DBUFF(dbuff);
624  fr_dbuff_marker(&hdr, &work_dbuff);
625 
626  /*
627  * Copy in the option code
628  * And leave room for data-len1
629  */
630  FR_DBUFF_IN_BYTES_RETURN(&work_dbuff, (uint8_t) da->attr, 0x00);
631 
632  /*
633  * We are at the VSA. The next entry in the stack is the vendor. The entry after that is the vendor data.
634  */
635  if (da_stack->da[depth + 1]) {
636  ssize_t len;
637  fr_dcursor_t vsa_cursor;
638 
639  if (da_stack->da[depth + 2]) {
640  len = encode_vsio_data(&work_dbuff, da_stack, depth + 2, cursor, encode_ctx);
641  if (len <= 0) return len;
642  goto done;
643  }
644 
645  vp = fr_dcursor_current(cursor);
646  fr_assert(vp->vp_type == FR_TYPE_VENDOR);
647 
648  /*
649  * Copied from below.
650  */
651  fr_pair_dcursor_init(&vsa_cursor, &vp->vp_group);
652  work_dbuff = FR_DBUFF(dbuff);
653 
654  while ((vp = fr_dcursor_current(&vsa_cursor)) != NULL) {
655  fr_proto_da_stack_build(da_stack, vp->da);
656  len = encode_vsio_data(&work_dbuff, da_stack, depth + 2, &vsa_cursor, encode_ctx);
657  if (len <= 0) return len;
658  }
659  goto done;
660  }
661 
662  vp = fr_dcursor_current(cursor);
663  fr_assert(vp->da == da);
664 
665  fr_pair_dcursor_init(&vendor_cursor, &vp->vp_group);
666 
667  /*
668  * Loop over all vendors, and inside of that, loop over all VSA attributes.
669  */
670  while ((vp = fr_dcursor_current(&vendor_cursor)) != NULL) {
671  ssize_t len;
672  fr_dcursor_t vsa_cursor;
673 
674  if (vp->vp_type != FR_TYPE_VENDOR) continue;
675 
676  fr_pair_dcursor_init(&vsa_cursor, &vp->vp_group);
677 
678  while ((vp = fr_dcursor_current(&vsa_cursor)) != NULL) {
679  /*
680  * RFC 3925 Section 4 says:
681  *
682  * "An Enterprise Number SHOULD only occur once
683  * among all instances of this option. Behavior
684  * is undefined if an Enterprise Number occurs
685  * multiple times."
686  *
687  * The function encode_vsio_data() builds
688  * one header, and then loops over all
689  * children of the vsa_cursor.
690  */
691  fr_proto_da_stack_build(da_stack, vp->da);
692  len = encode_vsio_data(&work_dbuff, da_stack, depth + 2, &vsa_cursor, encode_ctx);
693  if (len <= 0) return len;
694  }
695 
696  (void) fr_dcursor_next(&vendor_cursor);
697  }
698 
699  /*
700  * Write out length for whole option
701  */
702 done:
703  fr_dbuff_advance(&hdr, 1);
705 
706  /*
707  * Skip over the attribute we just encoded.
708  */
709  vp = fr_dcursor_next(cursor);
710  fr_proto_da_stack_build(da_stack, vp ? vp->da : NULL);
711 
712  return fr_dbuff_set(dbuff, &work_dbuff);
713 }
714 
715 /** Encode a DHCP option and any sub-options.
716  *
717  * @param[out] dbuff Where to write encoded DHCP attributes.
718  * @param[in] cursor with current VP set to the option to be encoded.
719  * Will be advanced to the next option to encode.
720  * @param[in] encode_ctx Containing DHCPv4 dictionary.
721  * @return
722  * - > 0 length of data written.
723  * - < 0 error.
724  * - 0 not valid option for DHCP (skipping).
725  */
727 {
728  fr_pair_t *vp;
729  unsigned int depth = 0;
730  fr_da_stack_t da_stack;
731  ssize_t len;
732  fr_dbuff_t work_dbuff = FR_DBUFF(dbuff);
733 
734  vp = fr_dcursor_current(cursor);
735  if (!vp) return -1;
736 
737  if (vp->da == attr_dhcp_message_type) goto next; /* already done */
738  if (vp->da->attr > 255) {
739  fr_strerror_printf("Attribute \"%s\" is not a DHCP option", vp->da->name);
740  next:
741  (void)fr_dcursor_next(cursor);
742  return 0;
743  }
744 
745  fr_proto_da_stack_build(&da_stack, vp->da);
746 
747  FR_PROTO_STACK_PRINT(&da_stack, depth);
748 
749  /*
750  * We only have two types of options in DHCPv4
751  */
752  switch (da_stack.da[depth]->type) {
753  case FR_TYPE_VSA:
754  len = encode_vsio(&work_dbuff, &da_stack, depth, cursor, encode_ctx);
755  break;
756 
757  case FR_TYPE_TLV:
758  len = encode_tlv(&work_dbuff, &da_stack, depth, cursor, encode_ctx);
759  break;
760 
761  default:
762  len = encode_rfc(&work_dbuff, &da_stack, depth, cursor, encode_ctx);
763  break;
764  }
765 
766  if (len <= 0) return len;
767 
768  FR_PROTO_TRACE("Complete option is %zu byte(s)", fr_dbuff_used(&work_dbuff));
769  FR_PROTO_HEX_DUMP(dbuff->p, fr_dbuff_used(&work_dbuff), NULL);
770 
771  return fr_dbuff_set(dbuff, &work_dbuff);
772 }
773 
775 {
776  ssize_t slen;
777  fr_dcursor_t cursor;
778  fr_dbuff_t work_dbuff = FR_DBUFF(dbuff);
779 
780  fr_assert(dict_dhcpv4 != NULL);
781 
783 
784  /*
785  * Loop over all DHCPv4 options.
786  *
787  * Unlike fr_dhcpv4_encode_dbuff(), we don't sort the options. If that causes problems, we will
788  * deal with it later.
789  */
790  while (fr_dcursor_current(&cursor) != NULL) {
791  slen = fr_dhcpv4_encode_option(&work_dbuff, &cursor, &(fr_dhcpv4_ctx_t){ .root = fr_dict_root(dict_dhcpv4) });
792  if (slen < 0) return slen;
793  }
794 
795  FR_PROTO_TRACE("Foreign option is %zu byte(s)", fr_dbuff_used(&work_dbuff));
796  FR_PROTO_HEX_DUMP(dbuff->p, fr_dbuff_used(&work_dbuff), NULL);
797 
798  return fr_dbuff_set(dbuff, &work_dbuff);
799 }
800 
801 static ssize_t fr_dhcpv4_encode_proto(UNUSED TALLOC_CTX *ctx, fr_pair_list_t *vps, uint8_t *data, size_t data_len, UNUSED void *proto_ctx)
802 {
803  return fr_dhcpv4_encode_dbuff(&FR_DBUFF_TMP(data, data_len), NULL, 0, 0, vps);
804 }
805 
806 static int encode_test_ctx(void **out, TALLOC_CTX *ctx)
807 {
808  fr_dhcpv4_ctx_t *test_ctx;
809 
810  test_ctx = talloc_zero(ctx, fr_dhcpv4_ctx_t);
811  if (!test_ctx) return -1;
812  test_ctx->root = fr_dict_root(dict_dhcpv4);
813 
814  *out = test_ctx;
815 
816  return 0;
817 }
818 
819 /*
820  * Test points
821  */
825  .func = fr_dhcpv4_encode_option,
826  .next_encodable = fr_dhcpv4_next_encodable,
827 };
828 
829 
830 
834  .func = fr_dhcpv4_encode_proto
835 };
#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:1072
#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
struct fr_dbuff_marker_s fr_dbuff_marker_t
A position marker associated with a dbuff.
Definition: dbuff.h:81
#define fr_dbuff_current(_dbuff_or_marker)
Return the 'current' position of a dbuff or marker.
Definition: dbuff.h:911
#define fr_dbuff_start(_dbuff_or_marker)
Return the 'start' position of a dbuff or marker.
Definition: dbuff.h:898
#define fr_dbuff_extend_lowat(_status, _dbuff_or_marker, _lowat)
Extend if we're below _lowat.
Definition: dbuff.h:660
#define fr_dbuff_remaining(_dbuff_or_marker)
Return the number of bytes remaining between the dbuff or marker and the end of the buffer.
Definition: dbuff.h:743
#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_REMAINING_RETURN(_dbuff_or_marker, _len)
Check if _len bytes are available in the dbuff and if not return the number of bytes we'd need.
Definition: dbuff.h:761
#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
static void fr_dbuff_marker_release(fr_dbuff_marker_t *m)
Releases the specified marker and any markers added before it.
Definition: dbuff.h:1210
#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:1192
#define fr_dbuff_move(_out, _in, _len)
Copy in as many bytes as possible from one dbuff or marker to another.
Definition: dbuff.h:1656
#define fr_dbuff_out(_out, _dbuff_or_marker)
Copy data from a dbuff or marker to a fixed sized C type.
Definition: dbuff.h:1799
#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:1472
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
Definition: dbuff.h:514
next
Definition: dcursor.h:178
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition: dcursor.h:288
static void * fr_dcursor_current(fr_dcursor_t *cursor)
Return the item the cursor current points to.
Definition: dcursor.h:337
static fr_dict_attr_t const * attr_dhcp_message_type
Definition: dhcpclient.c:90
static fr_dict_t const * dict_dhcpv4
Definition: dhcpclient.c:80
Implementation of the DHCPv4 protocol.
#define fr_dhcpv4_flag_prefix_bits(_da)
Definition: dhcpv4.h:161
#define fr_dhcpv4_flag_dns_label(_da)
Definition: dhcpv4.h:157
#define fr_dhcpv4_flag_prefix_split(_da)
Definition: dhcpv4.h:162
#define fr_dhcpv4_flag_exists(_da)
Definition: dhcpv4.h:158
fr_dict_attr_t const * root
Definition: dhcpv4.h:135
Used as the decoder ctx.
Definition: dhcpv4.h:134
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
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:36
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
@ 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_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
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
#define UINT8_MAX
Definition: merged_model.c:32
static uint8_t depth(fr_minmax_heap_index_t i)
Definition: minmax_heap.c:83
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_internal_encode_ctx_t encode_ctx
ssize_t fr_dhcpv4_encode_dbuff(fr_dbuff_t *dbuff, dhcp_packet_t *original, int code, uint32_t xid, fr_pair_list_t *vps)
Definition: base.c:341
void * fr_dhcpv4_next_encodable(fr_dlist_head_t *list, void *current, void *uctx)
DHCPV4-specific iterator.
Definition: base.c:319
ssize_t fr_dhcpv4_encode_foreign(fr_dbuff_t *dbuff, fr_pair_list_t const *list)
Definition: encode.c:774
static ssize_t extend_option(fr_dbuff_t *dbuff, fr_dbuff_marker_t *hdr, size_t len)
Extend an encoded option in-place.
Definition: encode.c:190
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)
Write DHCP option value into buffer.
Definition: encode.c:58
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:358
fr_test_point_pair_encode_t dhcpv4_tp_encode_pair
Definition: encode.c:823
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)
Write out a TLV header (and any sub TLVs or values)
Definition: encode.c:435
static ssize_t fr_dhcpv4_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:801
#define DHCPV4_OPT_HDR_LEN
Definition: encode.c:275
static int encode_test_ctx(void **out, TALLOC_CTX *ctx)
Definition: encode.c:806
fr_test_point_proto_encode_t dhcpv4_tp_encode_proto
Definition: encode.c:832
static ssize_t encode_vsio_data(fr_dbuff_t *dbuff, fr_da_stack_t *da_stack, unsigned int depth, fr_dcursor_t *cursor, void *encode_ctx)
Definition: encode.c:512
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)
Write out an RFC option header and option data.
Definition: encode.c:291
ssize_t fr_dhcpv4_encode_option(fr_dbuff_t *dbuff, fr_dcursor_t *cursor, void *encode_ctx)
Encode a DHCP option and any sub-options.
Definition: encode.c:726
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)
Definition: encode.c:602
VQP attributes.
static bool done
Definition: radclient.c:80
static uint32_t mask
Definition: rbmonkey.c:39
fr_assert(0)
fr_aka_sim_id_type_t type
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:470
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:112
fr_test_point_ctx_alloc_t test_ctx
Allocate a test ctx for the encoder.
Definition: test_point.h:94
Entry point for pair encoders.
Definition: test_point.h:111
Entry point for protocol encoders.
Definition: test_point.h:93
#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
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:611
#define fr_pair_dcursor_init(_cursor, _list)
Initialises a special dcursor with callbacks that will maintain the attr sublists correctly.
Definition: pair.h:591
#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
uint8_t depth
Deepest attribute in the stack.
Definition: proto.h:55
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:1404
static fr_slen_t data
Definition: value.h:1265
static size_t char ** out
Definition: value.h:997