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