The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
dict_validate.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program 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
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/** Validation framework to allow protocols to set custom validation rules
18 *
19 * @file src/lib/util/dict_validate.c
20 *
21 * @copyright 2019 The FreeRADIUS server project
22 * @copyright 2024 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23 */
24RCSID("$Id: 2b0443724818f20cb60fd6bc186789e5468eca08 $")
25
26#include <freeradius-devel/util/dict_priv.h>
27
28/** Validate a set of flags
29 *
30 * @param[in] da to check.
31 * @return
32 * - true if attribute definition is valid.
33 * - false if attribute definition is not valid.
34 */
36{
37 int bit;
38 uint32_t all_flags;
39 uint32_t shift_is_root, shift_internal;
40 uint32_t shift_array, shift_has_value;
41 uint32_t shift_subtype, shift_extra;
42 uint32_t shift_counter;
43 fr_dict_t *dict = da->dict;
44 fr_dict_attr_t const *parent = da->parent;
45 char const *name = da->name;
46 int attr = da->attr;
47 fr_type_t type = da->type;
48 fr_dict_attr_flags_t *flags = &da->flags;
49
50 /*
51 * Convert the 1-bit fields into bits numbers, so that we
52 * can check them in parallel.
53 */
54 all_flags = 0;
55 bit = -1;
56
57#define SET_FLAG(_flag) do { shift_ ## _flag = 1 << ++bit; if (flags->_flag) all_flags |= (1 << bit); } while (0)
58 SET_FLAG(is_root);
59 SET_FLAG(internal);
60 SET_FLAG(array);
61 SET_FLAG(has_value);
62 SET_FLAG(extra);
63 SET_FLAG(counter);
64 SET_FLAG(subtype);
65
66#define FORBID_OTHER_FLAGS(_flag, _allowed) \
67 do { \
68 if (all_flags & ~shift_ ## _flag & ~(_allowed)) { \
69 fr_strerror_printf("The '" STRINGIFY(_flag) "' flag cannot be used with any other flag (%u) %s[%d]", all_flags, da->filename, da->line); \
70 return false; \
71 } \
72 } while (0)
73
74#define ALLOW_FLAG(_flag) do { all_flags &= ~shift_ ## _flag; } while (0)
75
76 // is_root
77 // is_unknown
78 // internal
79 // array
80 // has_value
81 // extra
82 // encrypt
83 // length
84 // type_size
85
86 if (flags->is_root) {
87 FORBID_OTHER_FLAGS(is_root, 0);
88 }
89
90 if (flags->is_unknown) {
91 fr_strerror_const("The 'unknown' flag cannot be set for attributes in the dictionary.");
92 return false;
93 }
94
95 if (flags->local != parent->flags.local) {
96 fr_strerror_const("Cannot mix local variables with non-local attributes");
97 return false;
98 }
99
100 /*
101 * Only some data types can be in arrays, because we need
102 * to be able to decode the various array members.
103 */
104 if (flags->array) {
105 if (!flags->is_known_width) switch (type) {
106 default:
107 fr_strerror_printf("The 'array' flag cannot be used with attributes of type '%s'",
109 return false;
110
115 case FR_TYPE_UINT8:
116 case FR_TYPE_UINT16:
117 case FR_TYPE_UINT32:
118 case FR_TYPE_DATE:
120 break;
121
122 case FR_TYPE_ATTR:
123 flags->is_known_width = 1;
124 break;
125
126 case FR_TYPE_STRING:
127 case FR_TYPE_OCTETS:
128 if (!flags->length) {
129 fr_strerror_const("Variable length attributes cannot be marked as 'array'");
130 return false;
131 }
132
133 flags->is_known_width = 1;
134 break;
135
136 case FR_TYPE_STRUCT:
137 /*
138 * If we have arrays of structs, then the structure MUST be known width.
139 */
140 flags->is_known_width = 1;
141 break;
142 }
143
144 /*
145 * DHCPv6 has arrays of string / octets, prefixed
146 * with a uint16 field of "length". Also, arrays of dns_labels.
147 */
148 ALLOW_FLAG(extra);
149 ALLOW_FLAG(subtype);
150
151 FORBID_OTHER_FLAGS(array, 0);
152 }
153
154 /*
155 * 'has_value' should only be set internally. If the
156 * caller sets it, we still sanity check it.
157 */
158 if (flags->has_value) {
159 if (type != FR_TYPE_UINT32) {
160 fr_strerror_printf("The 'has_value' flag can only be used with attributes "
161 "of type 'integer'");
162 return false;
163 }
164
165 FORBID_OTHER_FLAGS(has_value, shift_internal);
166 }
167
168 /*
169 * Sanity check aliases.
170 */
171 if (flags->is_alias) {
173
175 if (!ext) {
176 fr_strerror_const("ALIAS is missing extension");
177 return false;
178 }
179
180 if (!ext->ref) {
181 fr_strerror_const("ALIAS is missing ref");
182 return false;
183 }
184
185 if (da->parent->type == FR_TYPE_STRUCT) {
186 fr_strerror_const("ALIAS cannot be added to a data type 'struct'");
187 return false;
188 }
189
190 fr_assert(!da->flags.is_unknown);
191 fr_assert(!da->flags.is_raw);
192 fr_assert(!da->flags.array);
193 fr_assert(!da->flags.is_known_width);
194 fr_assert(!da->flags.has_value);
195 fr_assert(!da->flags.counter);
196 fr_assert(!da->flags.secret);
197 fr_assert(!da->flags.unsafe);
198 fr_assert(!da->flags.is_ref_target);
199 fr_assert(!da->flags.local);
200 fr_assert(!da->flags.has_fixup);
201 }
202
203 /*
204 * The "extra" flag is a grab-bag of stuff, depending on
205 * the data type.
206 */
207 if (flags->extra) {
209 fr_strerror_const("The 'key' and 'length' flags cannot be used with any other flags.");
210 return false;
211 }
212
213 switch (type) {
214 case FR_TYPE_BOOL:
215 case FR_TYPE_UINT8:
216 case FR_TYPE_UINT16:
217 case FR_TYPE_UINT32:
218 case FR_TYPE_UINT64:
219 if ((flags->subtype != FLAG_KEY_FIELD) && (flags->subtype != FLAG_BIT_FIELD)) {
220 fr_strerror_const("Invalid type (not 'key' field or 'bit' field) for extra flag.");
221 return false;
222 }
223
224 if (parent->type != FR_TYPE_STRUCT) {
225 fr_strerror_const("The 'key' flag can only be used inside of a 'struct'.");
226 return false;
227 }
228
229 ALLOW_FLAG(extra);
230 ALLOW_FLAG(subtype);
231 break;
232
233 case FR_TYPE_OCTETS:
234 case FR_TYPE_STRING:
235 if (flags->length != 0) {
236 fr_strerror_const("Cannot use [..] and length=uint...");
237 return false;
238 }
239
240 /*
241 * We can do arrays of variable-length types, so long as they have a "length="
242 * modifier.
243 *
244 * But any other modifier is foridden, including the use of "length=" outside of
245 * the context of arrays.
246 */
247 if (flags->array) {
248 ALLOW_FLAG(array);
249
250 if (!da_is_length_field(da)) {
251 fr_assert(0);
252 goto invalid_extra;
253 }
254
255 } else if (flags->subtype) {
256 invalid_extra:
257 fr_strerror_const("Invalid type (not 'length=...') for extra flag.");
258 return false;
259 }
260
261 ALLOW_FLAG(extra);
262 ALLOW_FLAG(subtype);
263 break;
264
265 case FR_TYPE_STRUCT:
266 if (!da_is_length_field(da)) {
267 fr_strerror_const("Invalid type (not 'length=...') for extra flag.");
268 return false;
269 }
270
271 ALLOW_FLAG(extra);
272 ALLOW_FLAG(subtype);
273 ALLOW_FLAG(array);
274 break;
275
276 case FR_TYPE_TLV:
277 ALLOW_FLAG(extra);
278 /* @todo - allow arrays of struct? */
279 ALLOW_FLAG(subtype);
280 break;
281
282 default:
283 fr_strerror_printf("Type %s cannot hold extra flags",
285 return false;
286 }
287
288 if (da_is_length_field(da) &&
289 ((type != FR_TYPE_STRING) && (type != FR_TYPE_OCTETS) && (type != FR_TYPE_STRUCT))) {
290 fr_strerror_printf("The 'length' flag cannot be used used with type %s",
292 return false;
293 }
294
295 FORBID_OTHER_FLAGS(extra, 0);
296 }
297
298 /*
299 * Force "length" for fixed-size data types which aren't
300 * bit fields. Check / set "length" and "type_size" for
301 * other types.
302 */
303 if (!flags->extra || (flags->subtype != FLAG_BIT_FIELD)) switch (type) {
304 case FR_TYPE_INT8:
305 case FR_TYPE_UINT8:
306 case FR_TYPE_BOOL:
307 flags->length = 1;
308 break;
309
310 case FR_TYPE_INT16:
311 case FR_TYPE_UINT16:
312 flags->length = 2;
313 break;
314
315 case FR_TYPE_DATE:
317 if (!flags->length) flags->length = 4;
318
319 if ((flags->length != 2) && (flags->length != 4) && (flags->length != 8)) {
320 fr_strerror_printf("Invalid length %u for attribute of type '%s'",
321 flags->length, fr_type_to_str(type));
322 return false;
323 }
324 break;
325
327 case FR_TYPE_INT32:
328 case FR_TYPE_UINT32:
329 case FR_TYPE_FLOAT32:
330 flags->length = 4;
331 break;
332
333 case FR_TYPE_INT64:
334 case FR_TYPE_UINT64:
335 case FR_TYPE_FLOAT64:
336 flags->length = 8;
337 break;
338
339 case FR_TYPE_SIZE:
340 flags->length = sizeof(size_t);
341 break;
342
343 case FR_TYPE_ETHERNET:
344 flags->length = 6;
345 break;
346
347 case FR_TYPE_IFID:
348 flags->length = 8;
349 break;
350
353 flags->length = 16;
354 break;
355
358 flags->length = 17;
359 break;
360
361 case FR_TYPE_STRUCT:
362 ALLOW_FLAG(internal);
363 ALLOW_FLAG(array);
364 if (all_flags) {
365 fr_strerror_const("Invalid flag for attribute of type 'struct'");
366 return false;
367 }
368 break;
369
370 case FR_TYPE_VENDOR:
371 if (dict->string_based) break;
372
373 if (parent->type != FR_TYPE_VSA) {
374 fr_strerror_printf("Attributes of type 'vendor' MUST have a parent of type 'vsa' "
375 "instead of '%s'",
376 fr_type_to_str(parent->type));
377 return false;
378 }
379
380 if ((flags->length != 1) &&
381 (flags->length != 2) &&
382 (flags->length != 4)) {
383 fr_strerror_const("The 'length' flag can only be used for attributes of type 'vendor' with lengths of 1,2 or 4");
384 return false;
385 }
386 break;
387
388 case FR_TYPE_TLV:
389 if ((flags->length != 1) &&
390 (flags->length != 2) &&
391 (flags->length != 4)) {
392 fr_strerror_const("The 'length' flag can only be used for attributes of type 'tlv' with lengths of 1,2 or 4");
393 return false;
394 }
395 break;
396
397 /*
398 * 'octets[n]' can only be used in a few limited situations.
399 */
400 case FR_TYPE_OCTETS:
401 if (flags->length) {
402 /*
403 * Internal attributes can use octets[n]
404 * MS-MPPE-Keys use octets[18],encrypt=User-Password
405 * EAP-SIM-RAND uses array
406 */
407 ALLOW_FLAG(internal);
408 ALLOW_FLAG(subtype);
409 ALLOW_FLAG(array);
410
411 if (all_flags) {
412 fr_strerror_const("The 'octets[...]' syntax cannot be used any other flag");
413 return false;
414 }
415
416 if (flags->length > 253) {
417 fr_strerror_printf("Invalid length %d", flags->length);
418 return false;
419 }
420 }
421 break;
422
423 case FR_TYPE_UNION:
424 if (parent->type != FR_TYPE_STRUCT) {
425 fr_strerror_printf("Attributes of type 'union' must have a parent of type 'struct', not of type '%s'",
426 fr_type_to_str(parent->type));
427 return false;
428 }
429
430 /*
431 * If the UNION is missing a key extension, then the children of the UNION cannot find
432 * the key field in the parent STRUCT.
433 */
435 fr_strerror_const("Attribute of type 'union' is missing 'key=...'");
436 return false;
437 }
438 break;
439
440 case FR_TYPE_NULL:
441 case FR_TYPE_INTERNAL:
442 fr_strerror_printf("Attributes of type '%s' cannot be used in dictionaries",
444 return false;
445
446 /*
447 * These types are encoded differently in each protocol.
448 */
450 case FR_TYPE_ATTR:
451 case FR_TYPE_STRING:
452 case FR_TYPE_VSA:
453 case FR_TYPE_GROUP:
454 break;
455 }
456
457 /*
458 * type_size is used to limit the maximum attribute number, so it's checked first.
459 */
460 if (flags->type_size) {
461 if ((type == FR_TYPE_DATE) || (type == FR_TYPE_TIME_DELTA)) {
462 /*
463 * Allow all time res here
464 */
465 } else if (!flags->extra) {
466 if ((type != FR_TYPE_TLV) && (type != FR_TYPE_VENDOR)) {
467 fr_strerror_printf("The 'format=' flag can only be used with attributes of type 'tlv', and not type '%s'", fr_type_to_str(type));
468 return false;
469 }
470
471 if ((flags->type_size != 1) &&
472 (flags->type_size != 2) &&
473 (flags->type_size != 4)) {
474 fr_strerror_printf("The 'format=' flag can only be used with attributes of type size 1,2 or 4, not %i", flags->type_size);
475 return false;
476 }
477 }
478 }
479
480 /*
481 * Counters can be time deltas, or unsigned integers.
482 * For other data types, we don't know how to
483 * automatically add two counters.
484 */
485 if (flags->counter) {
487 ALLOW_FLAG(counter);
488 } else {
489 fr_strerror_printf("The 'counter' flag cannot be used with '%s'", fr_type_to_str(type));
490 return false;
491 }
492 }
493
494 /*
495 * Check flags against the parent attribute.
496 */
497 switch (parent->type) {
498 case FR_TYPE_STRUCT:
499 ALLOW_FLAG(extra);
500 ALLOW_FLAG(subtype);
501
502 /*
503 * If our parent is known width, then the children have to be known width, UNLESS
504 * either this child or its parent has a "length" prefix.
505 */
506 if (parent->flags.is_known_width && !flags->is_known_width && !flags->length &&
508 fr_strerror_const("Variable-sized fields cannot be used within a 'struct' which is 'array'");
509 return false;
510 }
511
512 if (flags->array) {
513 switch (type) {
515 ALLOW_FLAG(array);
516 break;
517
518 default:
519 if (flags->is_known_width) ALLOW_FLAG(array);
520 break;
521 }
522 }
523
524 if (all_flags) {
525 fr_strerror_const("Invalid flag for attribute inside of a 'struct'");
526 return false;
527 }
528
529 if (!attr) break;
530
531 /*
532 * If we have keyed structs, then the first
533 * member can be variable length.
534 *
535 * For subsequent children, have each one check
536 * the previous child.
537 */
538 if (attr != 1) {
539 int i;
540 fr_dict_attr_t const *sibling;
541
542 sibling = fr_dict_attr_child_by_num(parent, (attr) - 1);
543
544 /*
545 * sibling might not exist, if it's a deferred 'tlv clone=...'
546 */
547
548 /*
549 * Variable sized elements cannot have anything after them in a struct.
550 */
551 if (sibling && !sibling->flags.length && !sibling->flags.is_known_width) {
552 fr_strerror_const("No other field can follow a struct MEMBER which is variable sized");
553 return false;
554 }
555
556 /*
557 * The same goes for arrays.
558 */
559 if (sibling && sibling->flags.array) {
560 fr_strerror_const("No other field can follow a struct MEMBER which is 'array'");
561 return false;
562 }
563
564 /*
565 * Check for bad key fields, or multiple
566 * key fields. Yes, this is O(N^2), but
567 * the structs are small.
568 */
570 for (i = 1; i < attr; i++) {
571 sibling = fr_dict_attr_child_by_num(parent, i);
572 if (!sibling) {
573 fr_strerror_printf("Child %d of 'struct' type attribute %s does not exist.",
574 i, parent->name);
575 return false;
576 }
577
578 if (!fr_dict_attr_is_key_field(sibling)) continue;
579
580 fr_strerror_printf("Duplicate key attributes '%s' and '%s' in 'struct' type attribute %s are forbidden",
581 name, sibling->name, parent->name);
582 return false;
583 }
584 }
585 }
586 break;
587
588 case FR_TYPE_VSA:
589 if ((type != FR_TYPE_VENDOR) && !flags->internal) {
590 fr_strerror_printf("Attributes of type '%s' cannot be children of the 'vsa' type",
592 return false;
593 }
594 break;
595
596 case FR_TYPE_TLV:
597 case FR_TYPE_VENDOR:
598 break;
599
600 case FR_TYPE_UNION:
601 if (!((da->type == FR_TYPE_STRUCT) || (da->type == FR_TYPE_TLV) || fr_type_is_leaf(da->type))) {
602 fr_strerror_printf("Attributes of type '%s' cannot be children of the 'union' type",
604 return false;
605 }
606 break;
607
608 default:
609 fr_strerror_printf("Attributes of type '%s' cannot have child attributes",
610 fr_type_to_str(parent->type));
611 return false;
612 }
613
614 return true;
615}
616
617
618/** Validate a new attribute definition
619 *
620 * @todo we need to check length of none vendor attributes.
621 *
622 * @param[in] da to validate.
623 * @return
624 * - true if attribute definition is valid.
625 * - false if attribute definition is not valid.
626 */
628{
629 if (!fr_cond_assert(da->parent)) return false;
630
631 if (fr_dict_valid_name(da->name, -1) <= 0) return false;
632
633 /*
634 * Run protocol-specific validation functions, BEFORE we
635 * do the rest of the checks.
636 */
637 if (da->dict->proto->attr.valid && !da->dict->proto->attr.valid(da)) return false;
638
639 /*
640 * Check the flags, data types, and parent data types and flags.
641 */
642 if (!dict_attr_flags_valid(da)) return false;
643
644 return true;
645}
#define RCSID(id)
Definition build.h:485
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:131
unsigned int has_value
Has a value.
Definition dict.h:96
unsigned int is_root
Is root of a dictionary.
Definition dict.h:77
unsigned int array
Pack multiples into 1 attr.
Definition dict.h:92
unsigned int extra
really "subtype is used by dict, not by protocol"
Definition dict.h:120
unsigned int internal
Internal attribute, should not be received in protocol packets, should not be encoded.
Definition dict.h:90
#define da_is_bit_field(_da)
Definition dict.h:171
@ FLAG_KEY_FIELD
this is a key field for a subsequent struct
Definition dict.h:164
@ FLAG_BIT_FIELD
bit field inside of a struct
Definition dict.h:165
#define da_is_length_field(_da)
Definition dict.h:172
uint8_t type_size
Type size for TLVs.
Definition dict.h:144
uint16_t length
length of the attribute
Definition dict.h:153
unsigned int local
is a local variable
Definition dict.h:122
@ FR_DICT_ATTR_EXT_REF
Attribute references another attribute and/or dictionary.
Definition dict.h:184
@ FR_DICT_ATTR_EXT_KEY
UNION attribute references a key.
Definition dict.h:186
unsigned int is_known_width
is treated as if it has a known width for structs
Definition dict.h:94
ssize_t fr_dict_valid_name(char const *name, ssize_t len)
Definition dict_util.c:4958
fr_dict_attr_t const * fr_dict_attr_child_by_num(fr_dict_attr_t const *parent, unsigned int attr)
Check if a child attribute exists in a parent using an attribute number.
Definition dict_util.c:3623
#define fr_dict_attr_is_key_field(_da)
Definition dict.h:170
uint8_t subtype
needs a fixup during dictionary parsing
Definition dict.h:131
unsigned int is_alias
This isn't a real attribute, it's a reference to to one.
Definition dict.h:87
unsigned int counter
integer attribute is actually an impulse / counter
Definition dict.h:100
unsigned int is_unknown
This dictionary attribute is ephemeral and not part of the main dictionary.
Definition dict.h:79
Values of the encryption flags.
static void * fr_dict_attr_ext(fr_dict_attr_t const *da, fr_dict_attr_ext_t ext)
Definition dict_ext.h:134
Attribute extension - Holds a reference to an attribute in another dictionary.
Definition dict_ext.h:77
#define FORBID_OTHER_FLAGS(_flag, _allowed)
bool dict_attr_flags_valid(fr_dict_attr_t *da)
Validate a set of flags.
bool dict_attr_valid(fr_dict_attr_t *da)
Validate a new attribute definition.
#define ALLOW_FLAG(_flag)
#define SET_FLAG(_flag)
fr_type_t
@ FR_TYPE_TIME_DELTA
A period of time measured in nanoseconds.
@ FR_TYPE_FLOAT32
Single precision floating point.
@ FR_TYPE_IPV4_ADDR
32 Bit IPv4 Address.
@ FR_TYPE_INT8
8 Bit signed integer.
@ FR_TYPE_TLV
Contains nested attributes.
@ FR_TYPE_ETHERNET
48 Bit Mac-Address.
@ FR_TYPE_IPV6_PREFIX
IPv6 Prefix.
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
@ FR_TYPE_UINT16
16 Bit unsigned integer.
@ FR_TYPE_INT64
64 Bit signed integer.
@ FR_TYPE_INT16
16 Bit signed integer.
@ FR_TYPE_DATE
Unix time stamp, always has value >2^31.
@ FR_TYPE_COMBO_IP_PREFIX
IPv4 or IPv6 address prefix depending on length.
@ FR_TYPE_UINT8
8 Bit unsigned integer.
@ FR_TYPE_UINT32
32 Bit unsigned integer.
@ FR_TYPE_STRUCT
like TLV, but without T or L, and fixed-width children
@ FR_TYPE_INT32
32 Bit signed integer.
@ FR_TYPE_VENDOR
Attribute that represents a vendor in the attribute tree.
@ FR_TYPE_UINT64
64 Bit unsigned integer.
@ FR_TYPE_IPV6_ADDR
128 Bit IPv6 Address.
@ FR_TYPE_IPV4_PREFIX
IPv4 Prefix.
@ FR_TYPE_BOOL
A truth value.
@ FR_TYPE_SIZE
Unsigned integer capable of representing any memory address on the local system.
@ FR_TYPE_VSA
Vendor-Specific, for RADIUS attribute 26.
@ FR_TYPE_COMBO_IP_ADDR
IPv4 or IPv6 address depending on length.
@ FR_TYPE_IFID
Interface ID.
@ FR_TYPE_OCTETS
Raw octets.
@ FR_TYPE_GROUP
A grouping of other attributes.
@ FR_TYPE_FLOAT64
Double precision floating point.
unsigned int uint32_t
unsigned long int size_t
#define fr_assert(_expr)
Definition rad_assert.h:38
static char const * name
fr_aka_sim_id_type_t type
static fr_slen_t parent
Definition pair.h:857
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_const(_msg)
Definition strerror.h:223
@ FR_TYPE_UNION
A union of limited children.
Definition types.h:82
@ FR_TYPE_ATTR
A contains an attribute reference.
Definition types.h:84
#define FR_TYPE_INTERNAL
Definition types.h:320
#define fr_type_is_signed(_x)
Definition types.h:384
#define fr_type_is_leaf(_x)
Definition types.h:394
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:455
#define fr_type_is_integer(_x)
Definition types.h:382
#define FR_TYPE_FIXED_SIZE
Definition types.h:311