The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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: 8df8f68d6751ca2d2d73413b08b06ee8be8e3268 $
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
37static 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
41static 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{
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
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:
106 break;
107 }
108 FR_DBUFF_IN_RETURN(&work_dbuff, (uint8_t) (vp->vp_bool == true));
109 break;
110
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 }
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 */
190static 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;
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
350static 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
354static 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;
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);
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;
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 */
702done:
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
801static 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
806static int encode_test_ctx(void **out, TALLOC_CTX *ctx, UNUSED fr_dict_t const *dict)
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 */
828
829
830
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:322
#define UNUSED
Definition build.h:315
#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_set(_dst, _src)
Set the 'current' position in a dbuff or marker using another dbuff or marker, a char pointer,...
Definition dbuff.h:1004
#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
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_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
#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
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
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:272
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:736
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 attribute header.
Definition encode.c:587
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.
@ 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.
uint8_t * p
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)
Definition encode.c:165
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_dbuff(fr_dbuff_t *dbuff, dhcp_packet_t *original, int code, uint32_t xid, fr_pair_list_t *vps)
Definition base.c:341
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_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 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
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
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.
#define fr_assert(_expr)
Definition rad_assert.h:38
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: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:93
fr_test_point_ctx_alloc_t test_ctx
Allocate a test ctx for the encoder.
Definition test_point.h:75
Entry point for pair encoders.
Definition test_point.h:92
Entry point for protocol encoders.
Definition test_point.h:74
#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
#define fr_type_is_structural(_x)
Definition types.h:371
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:433
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