* group ref.
*/
case FR_TYPE_GROUP:
- parent = da->ref;
+ parent = fr_dict_attr_ref(da);
break;
case FR_TYPE_STRUCT:
/** Register an old style xlat function
+ *
+ * @note No new legacy xlat functions should be added to the server.
+ * Each one added creates additional work later for a member
+ * of the development team to fix the function to conform to
+ * the new API.
*
* @param[in] mod_inst Instance of module that's registering the xlat function.
* @param[in] name xlat name.
* @copyright 2020 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
*/
-RCSID("$Id:")
+RCSID("$Id$")
#include <freeradius-devel/util/dbuff.h>
extern const size_t dict_attr_sizes[FR_TYPE_MAX + 1][2];
+/** Extension identifier
+ *
+ */
+typedef enum {
+ FR_DICT_ATTR_EXT_CHILDREN = 0, //!< Attribute has children.
+ FR_DICT_ATTR_EXT_REF, //!< Attribute references another
+ ///< attribute and/or dictionary
+ FR_DICT_ATTR_EXT_VENDOR, //!< Cached vendor pointer.
+ FR_DICT_ATTR_EXT_DA_STACK, //!< Cached da stack.
+ FR_DICT_ATTR_EXT_PROTOCOL_SPECIFIC, //!< Protocol specific extensions
+ FR_DICT_ATTR_EXT_MAX
+} fr_dict_attr_ext_t;
+
+/** The alignment of object extension structures
+ *
+ */
+#define FR_DICT_ATTR_EXT_ALIGNMENT sizeof(uintptr_t)
+
+/** Attribute extension - Holds children for an attribute
+ *
+ * Children are possible for:
+ *
+ * #FR_TYPE_TLV, #FR_TYPE_VENDOR, #FR_TYPE_VSA, #FR_TYPE_STRUCT
+ *
+ * *or* where the parent->parent->type is
+ * #FR_TYPE_STRUCT, and "parent" is a "key"
+ * field. Note that these attributes therefore
+ * cannot have VALUEs, as the child defines their
+ * VALUE. See dict_attr_can_have_children() for details.
+ */
+typedef struct {
+ fr_dict_attr_t const **children; //!< Children of this attribute.
+} fr_dict_attr_ext_children_t;
+
+/** Attribute extension - Holds a reference to an attribute in another dictionary
+ *
+ */
+typedef struct {
+ fr_dict_attr_t const *ref; //!< reference, only for #FR_TYPE_GROUP
+} fr_dict_attr_ext_ref_t;
+
+/** Attribute extension - Cached vendor pointer
+ *
+ */
+typedef struct {
+ fr_dict_attr_t const *vendor; //!< ancestor which has type #FR_TYPE_VENDOR
+} fr_dict_attr_ext_vendor_t;
+
+/** Attribute extension - Stack of dictionary attributes that describe the path back to the root of the dictionary
+ *
+ */
+typedef struct {
+ fr_dict_attr_t const *da_stack[0]; //!< Stack of dictionary attributes
+} fr_dict_attr_ext_da_stack_t;
+
+/** Attribute extension - Protocol-specific
+ *
+ */
+typedef struct {
+ void *uctx; //!< Protocol specific extensions
+} fr_dict_attr_ext_protocol_specific_t;
+
/** Dictionary attribute
*/
struct dict_attr_s {
+ fr_dict_t _CONST* _CONST dict; //!< Dict attribute belongs to.
+
+ char const *name; //!< Attribute name.
+
unsigned int attr; //!< Attribute number.
unsigned int depth; //!< Depth of nesting for this attribute.
fr_type_t type; //!< Value type.
- char const *name; //!< Attribute name.
- fr_dict_t _CONST* _CONST dict; //!< Dict attribute belongs to.
fr_dict_attr_t const *parent; //!< Immediate parent of this attribute.
fr_dict_attr_t const *next; //!< Next child in bin.
fr_dict_attr_flags_t flags; //!< Flags.
- /*
- * The remaining fields vary depending on the attribute
- * properties.
- */
- fr_dict_attr_t const *vendor; //!< ancestor which has type #FR_TYPE_VENDOR
union {
/*
* Children are possible for:
fr_dict_attr_t const **children; //!< Children of this attribute.
fr_dict_attr_t const *ref; //!< reference, only for #FR_TYPE_GROUP
};
+ uint8_t ext[FR_DICT_ATTR_EXT_MAX]; //!< Extensions to the dictionary attribute.
- fr_dict_attr_t const *da_stack[0]; //!< load-time TLV stacks
-};
+} CC_HINT(aligned(FR_DICT_ATTR_EXT_ALIGNMENT));
/** Value of an enumerated attribute
*
*/
#define FR_DICT_TLV_NEST_MAX (24)
+/** Maximum level of da stack caching
+ */
+#define FR_DICT_DA_STACK_CACHE_MAX (5)
+
/** Maximum TLV stack size
*
* The additional attributes are to account for
extern bool const fr_dict_attr_allowed_chars[UINT8_MAX + 1];
extern bool const fr_dict_non_data_types[FR_TYPE_MAX + 1];
+/** @name Add extension structures to attributes
+ *
+ * @{
+ */
+
+/** Return a pointer to the specified extension structure
+ */
+#define DICT_EXT_OFFSET(_ptr, _ext) ((void *)(((_ptr)->ext[_ext] * FR_DICT_ATTR_EXT_ALIGNMENT) + ((uintptr_t)(_ptr))))
+
+/** Minimum extension lengths
+ */
+extern size_t const fr_dict_ext_length_min[];
+
+/* Retrieve an extension structure for a dictionary attribute
+ *
+ * @param[in] da to retrieve structure from.
+ * @param[in] ext to retrieve.
+ * @return
+ * - NULL if the extension wasn't found.
+ * - A pointer to the start of the extension.
+ */
+static inline void *fr_dict_attr_ext(fr_dict_attr_t const *da, fr_dict_attr_ext_t ext)
+{
+ if (!da->ext[ext]) return NULL;
+
+ return DICT_EXT_OFFSET(da, ext);
+}
+
+/** Return whether a da has a given extension or not
+ *
+ * @param[in] da to check for extensions.
+ * @param[in] ext to check.
+ * @return
+ * - true if the da has the specified extension.
+ * - false if the da does not have the specified extension
+ */
+static inline bool fr_dict_attr_has_ext(fr_dict_attr_t const *da, fr_dict_attr_ext_t ext)
+{
+ return (da->ext[ext] > 0);
+}
+
+/** Return the cached da stack (if any) associated with an attribute
+ *
+ * @param[in] da to return cached da stack for.
+ * @return
+ * - NULL if no da stack available.
+ * - The cached da stack on success.
+ */
+static inline fr_dict_attr_t const **fr_dict_attr_da_stack(fr_dict_attr_t const *da)
+{
+ fr_dict_attr_ext_da_stack_t *ext;
+
+ ext = fr_dict_attr_ext(da, FR_DICT_ATTR_EXT_DA_STACK);
+ if (!ext) return NULL;
+
+ return ext->da_stack;
+}
+
+/** Return the reference associated with a group type attribute
+ *
+ * @param[in] da to return the reference for.
+ * @return
+ * - NULL if no reference available.
+ * - A pointer to the attribute being referenced.
+ */
+static inline fr_dict_attr_t const *fr_dict_attr_ref(fr_dict_attr_t const *da)
+{
+ fr_dict_attr_ext_ref_t *ext;
+
+ ext = fr_dict_attr_ext(da, FR_DICT_ATTR_EXT_REF);
+ if (!ext) return NULL;
+
+ return ext->ref;
+}
+/** @} */
+
/** @name Programatically create dictionary attributes and values
*
* @{
*/
static inline uint32_t fr_dict_vendor_num_by_da(fr_dict_attr_t const *da)
{
+ fr_dict_attr_ext_vendor_t *ext;
+
if (da->type == FR_TYPE_VENDOR) return da->attr;
- if (!da->vendor) return 0;
+ ext = fr_dict_attr_ext(da, FR_DICT_ATTR_EXT_VENDOR);
+ if (!ext) return 0;
- return da->vendor->attr;
+ return ext->vendor->attr;
+}
+
+/** Return the vendor da for an attribute
+ *
+ * @param[in] da The dictionary attribute to find the
+ * vendor for.
+ * @return
+ * - 0 this isn't a vendor specific attribute.
+ * - The vendor PEN.
+ */
+static inline fr_dict_attr_t const *fr_dict_vendor_da_by_da(fr_dict_attr_t const *da)
+{
+ fr_dict_attr_ext_vendor_t *ext;
+
+ if (da->type == FR_TYPE_VENDOR) return da;
+
+ ext = fr_dict_attr_ext(da, FR_DICT_ATTR_EXT_VENDOR);
+ if (!ext) return NULL;
+
+ return ext->vendor;
}
fr_dict_vendor_t const *fr_dict_vendor_by_da(fr_dict_attr_t const *da);
fr_dict_vendor_t const *fr_dict_vendor_by_num(fr_dict_t const *dict, uint32_t vendor_pen);
-fr_dict_attr_t const *fr_dict_vendor_attr_by_da(fr_dict_attr_t const *da);
-
-fr_dict_attr_t const *fr_dict_vendor_attr_by_num(fr_dict_attr_t const *vendor_root, uint32_t vendor_pen);
+fr_dict_attr_t const *fr_dict_vendor_da_by_num(fr_dict_attr_t const *vendor_root, uint32_t vendor_pen);
ssize_t fr_dict_attr_by_name_substr(fr_dict_attr_err_t *err, fr_dict_attr_t const **out,
fr_dict_t const *dict, fr_sbuff_t *name) CC_HINT(nonnull(2,4));
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/** Extensions to dictionary structures
+ *
+ * @file src/lib/util/dict_attr_ext.c
+ *
+ * @copyright 2020 The FreeRADIUS server project
+ * @copyright 2020 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ */
+RCSID("$Id$")
+
+#include <freeradius-devel/util/dict_priv.h>
+
+/** Holds the minimum lengths of the extension structures
+ *
+ */
+size_t const fr_dict_ext_length_min[FR_DICT_ATTR_EXT_MAX] = {
+ [FR_DICT_ATTR_EXT_CHILDREN] = sizeof(fr_dict_attr_ext_children_t),
+ [FR_DICT_ATTR_EXT_REF] = sizeof(fr_dict_attr_ext_ref_t),
+ [FR_DICT_ATTR_EXT_VENDOR] = sizeof(fr_dict_attr_ext_vendor_t),
+ [FR_DICT_ATTR_EXT_DA_STACK] = sizeof(fr_dict_attr_ext_da_stack_t),
+};
+
+/** Add a variable length extension to a dictionary attribute
+ *
+ * Extensions are appended to the existing #fr_dict_attr_t memory chunk
+ * using realloc.
+ *
+ * When a new extension is allocated it will not be initialised.
+ *
+ * @param[in] ctx the dict attr was originally allocated in.
+ * @param[in,out] da_p The dictionary attribute to add an extension for.
+ * Under certain circumstances the value of *da_p will
+ * be changed to point to a new memory block.
+ * All cached copied of the previous pointer should be
+ * updated. This means that attributes that have
+ * already been added to a dictionary should not have
+ * extensions allocated unless care is taken to update
+ * all references.
+ * @param[in] ext to alloc.
+ * @param[in] ext_len The length of the extension.
+ * @return
+ * - NULL if we failed allocating an extension.
+ * - A pointer to the extension we allocated.
+ */
+void *dict_attr_ext_alloc_size(TALLOC_CTX *ctx, fr_dict_attr_t **da_p, fr_dict_attr_ext_t ext, size_t ext_len)
+{
+ size_t len;
+ size_t aligned = ROUND_UP(ext_len, FR_DICT_ATTR_EXT_ALIGNMENT);
+ size_t offset;
+
+ fr_dict_attr_t *n_da, *da = *da_p;
+
+ if (unlikely(da->dict && da->dict->read_only)) {
+ fr_strerror_printf("%s dictionary has been marked as read only", fr_dict_root(da->dict)->name);
+ return NULL;
+ }
+
+ if (da->ext[ext]) return fr_dict_attr_ext(da, ext);
+
+ len = talloc_array_length((uint8_t *)da);
+
+ offset = len / FR_DICT_ATTR_EXT_ALIGNMENT;
+ if (offset > UINT8_MAX) {
+ fr_strerror_printf("Insufficient space remaining for extensions");
+ return NULL;
+ }
+
+ n_da = talloc_realloc_size(ctx, da, len + aligned);
+ if (!n_da) return NULL;
+ talloc_set_type(n_da, fr_dict_attr_t);
+
+ *da_p = n_da;
+ da->ext[ext] = (uint8_t)offset;
+
+ return (void *)(((uintptr_t)n_da) + len);
+}
+
+/** Add a fixed length extension to a dictionary attribute
+ *
+ * Extensions are appended to the existing #fr_dict_attr_t memory chunk
+ * using realloc.
+ *
+ * When a new extension is allocated it will not be initialised.
+ * In the majority of instances this is OK as its value will be set
+ * immediately, but care should be taken to ensure it is initialised
+ * as some point.
+ *
+ * @param[in] ctx the dict attr was originally allocated in.
+ * @param[in,out] da_p The dictionary attribute to add an extension for.
+ * Under certain circumstances the value of *da_p will
+ * be changed to point to a new memory block.
+ * All cached copied of the previous pointer should be
+ * updated. This means that attributes that have
+ * already been added to a dictionary should not have
+ * extensions allocated unless care is taken to update
+ * all references.
+ * @param[in] ext to alloc.
+ * @return
+ * - NULL if we failed allocating an extension.
+ * - A pointer to the extension we allocated.
+ */
+void *dict_attr_ext_alloc(TALLOC_CTX *ctx, fr_dict_attr_t **da_p, fr_dict_attr_ext_t ext)
+{
+ return dict_attr_ext_alloc_size(ctx, da_p, ext, fr_dict_ext_length_min[ext]);
+}
+
+/** Return the length of an extension
+ *
+ * @param[in] da to return extension length for.
+ * @param[in] ext to return length for.
+ * @return
+ * - 0 if no extension exists.
+ * - >0 the length of the extension.
+ */
+size_t dict_attr_ext_len(fr_dict_attr_t const *da, fr_dict_attr_ext_t ext)
+{
+ uint8_t end = 0, start, i;
+ size_t len;
+
+ start = da->ext[ext];
+ if (!start) return 0;
+
+ len = talloc_array_length((uint8_t const *)da);
+ end = len / FR_DICT_ATTR_EXT_ALIGNMENT;
+
+ /*
+ * Figure out where the extension ends
+ */
+ for (i = 0; i < NUM_ELEMENTS(da->ext); i++) {
+ if ((da->ext[i] > start) && (da->ext[i] < end)) end = da->ext[i];
+ }
+
+ return (end - start) * FR_DICT_ATTR_EXT_ALIGNMENT;
+}
+
+/** Copy extension data from one attribute to another
+ *
+ * @param[in] ctx to realloc da_out in.
+ * @param[in] da_in to copy extension from.
+ * @param[in] ext to copy.
+ * @return
+ * - NULL if we failed to allocate an extension structure.
+ * - A pointer to the start of the extension in da_out.
+ */
+void *dict_attr_ext_copy(TALLOC_CTX *ctx,
+ fr_dict_attr_t **da_out_p, fr_dict_attr_t const *da_in, fr_dict_attr_ext_t ext)
+{
+ uint8_t start;
+ size_t ext_len;
+ void *ptr;
+
+ if (unlikely((*da_out_p)->dict && (*da_out_p)->dict->read_only)) {
+ fr_strerror_printf("%s dictionary has been marked as read only", fr_dict_root((*da_out_p)->dict)->name);
+ return NULL;
+ }
+
+ start = da_in->ext[ext];
+ if (!start) return NULL;
+
+ ext_len = dict_attr_ext_len(da_in, ext);
+ ptr = dict_attr_ext_alloc_size(ctx, da_out_p, ext, ext_len);
+ if (!ptr) return NULL;
+
+ /*
+ * Copy extension data over
+ */
+ memcpy(ptr, (void *)((uintptr_t)(da_in) + (start * FR_DICT_ATTR_EXT_ALIGNMENT)), ext_len);
+
+ return ptr;
+}
extern fr_dict_gctx_t *dict_gctx;
-extern fr_table_num_ordered_t const date_precision_table[];
-extern size_t date_precision_table_len;
+extern fr_table_num_ordered_t const date_precision_table[];
+extern size_t date_precision_table_len;
fr_dict_t *dict_alloc(TALLOC_CTX *ctx);
int dict_dlopen(fr_dict_t *dict, char const *name);
-/** Initialise fields in a dictionary attribute structure
+fr_dict_attr_t *dict_attr_alloc_null(TALLOC_CTX *ctx);
+
+int dict_attr_init(TALLOC_CTX *ctx, fr_dict_attr_t **da_p,
+ fr_dict_attr_t const *parent,
+ char const *name, int attr,
+ fr_type_t type, fr_dict_attr_flags_t const *flags);
+
+fr_dict_attr_t *dict_attr_alloc(TALLOC_CTX *ctx,
+ fr_dict_attr_t const *parent,
+ char const *name, int attr,
+ fr_type_t type, fr_dict_attr_flags_t const *flags);
+
+/** @name Add extension structures to attributes
*
- * @param[in] da to initialise.
- * @param[in] parent of the attribute, if none, should be
- * the dictionary root.
- * @param[in] attr number.
- * @param[in] type of the attribute.
- * @param[in] flags to assign.
+ * @{
*/
-static inline void dict_attr_init(fr_dict_attr_t *da,
- fr_dict_attr_t const *parent, int attr,
- fr_type_t type, fr_dict_attr_flags_t const *flags)
+void *dict_attr_ext_alloc_size(TALLOC_CTX *ctx, fr_dict_attr_t **da_p,
+ fr_dict_attr_ext_t ext, size_t ext_len);
+
+void *dict_attr_ext_alloc(TALLOC_CTX *ctx, fr_dict_attr_t **da_p, fr_dict_attr_ext_t ext);
+
+size_t dict_attr_ext_len(fr_dict_attr_t const *da, fr_dict_attr_ext_t ext);
+
+void *dict_attr_ext_copy(TALLOC_CTX *ctx,
+ fr_dict_attr_t **da_out_p, fr_dict_attr_t const *da_in,
+ fr_dict_attr_ext_t ext);
+
+static inline int dict_attr_ref_set(fr_dict_attr_t const *da, fr_dict_attr_t const *ref)
{
- da->attr = attr;
- da->type = type;
- da->flags = *flags;
- da->parent = parent;
-
- if (parent) {
- da->dict = parent->dict;
- da->depth = parent->depth + 1;
- } else {
- da->depth = 0;
+ fr_dict_attr_ext_ref_t *ext;
+
+ ext = fr_dict_attr_ext(da, FR_DICT_ATTR_EXT_REF);
+ if (unlikely(!ext)) {
+ fr_strerror_printf("%s contains no ref extension", da->name);
+ return -1;
}
+ ext->ref = ref;
- /*
- * Point to the vendor definition. Since ~90% of
- * attributes are VSAs, caching this pointer will help.
- */
- if (parent) {
- if (parent->type == FR_TYPE_VENDOR) {
- da->vendor = parent;
- } else if (parent->vendor) {
- da->vendor = parent->vendor;
- }
+ return 0;
+}
+
+
+static inline int dict_attr_children_set(fr_dict_attr_t const *da, fr_dict_attr_t const **children)
+{
+ fr_dict_attr_ext_children_t *ext;
+
+ ext = fr_dict_attr_ext(da, FR_DICT_ATTR_EXT_CHILDREN);
+ if (unlikely(!ext)) {
+ fr_strerror_printf("%s contains no children extension", da->name);
+ return -1;
}
+ ext->children = children;
+
+ return 0;
}
-fr_dict_attr_t *dict_attr_alloc_name(TALLOC_CTX *ctx, fr_dict_attr_t const *parent, char const *name);
+static inline fr_dict_attr_t const **dict_attr_children(fr_dict_attr_t const *da)
+{
+ fr_dict_attr_ext_children_t *ext;
-fr_dict_attr_t *dict_attr_alloc(TALLOC_CTX *ctx,
- fr_dict_attr_t const *parent,
- char const *name, int attr,
- fr_type_t type, fr_dict_attr_flags_t const *flags);
+ ext = fr_dict_attr_ext(da, FR_DICT_ATTR_EXT_CHILDREN);
+ if (unlikely(!ext)) {
+ fr_strerror_printf("%s contains no children extension", da->name);
+ return NULL;
+ }
+ return ext->children;
+}
+/** @} */
int dict_attr_child_add(fr_dict_attr_t *parent, fr_dict_attr_t *child);
*/
static int dict_root_set(fr_dict_t *dict, char const *name, unsigned int proto_number)
{
+ fr_dict_attr_t *da;
+
fr_dict_attr_flags_t flags = {
.is_root = 1,
.type_size = 1,
return -1;
}
- dict->root = dict_attr_alloc_name(dict, NULL, name);
- if (!dict->root) return -1;
+ da = dict_attr_alloc(dict, NULL, name, proto_number, FR_TYPE_TLV, &flags);
+ if (unlikely(!da)) return -1;
- dict_attr_init(dict->root, NULL, proto_number, FR_TYPE_TLV, &flags);
+ dict->root = da;
dict->root->dict = dict;
DA_VERIFY(dict->root);
static int dict_read_process_alias(dict_tokenize_ctx_t *ctx, char **argv, int argc)
{
fr_dict_attr_t const *da;
+ fr_dict_attr_t *new;
if (argc != 2) {
fr_strerror_printf("Invalid ALIAS syntax");
* second one is prioritized for printing. For ALIASes,
* we want the first one to be prioritized.
*/
- da = dict_attr_alloc(ctx->dict->pool, da->parent, argv[0], da->attr, da->type, &da->flags);
- if (!da) return -1;
+ new = dict_attr_alloc(ctx->dict->pool, da->parent, argv[0], da->attr, da->type, &da->flags);
+ if (unlikely(!new)) return -1;
- if (!fr_hash_table_insert(ctx->dict->attributes_by_name, da)) {
+ if (!fr_hash_table_insert(ctx->dict->attributes_by_name, new)) {
fr_strerror_printf("Internal error storing attribute");
talloc_const_free(da);
return -1;
talloc_free(ref);
self->dict = dict;
- self->ref = da;
+
+ dict_attr_ref_set(self, da);
}
}
}
talloc_free(this->ref);
- this->da->ref = da;
+ dict_attr_ref_set(this->da, da);
next = this->next;
}
}
}
+ new = dict_attr_alloc(ctx->dict->pool,
+ vsa_da, argv[1], vendor->pen, FR_TYPE_VENDOR, &flags);
+ if (unlikely(!new)) goto error;
+
memcpy(&mutable, &vsa_da, sizeof(mutable));
- new = dict_attr_alloc(mutable, vsa_da, argv[1],
- vendor->pen, FR_TYPE_VENDOR, &flags);
if (dict_attr_child_add(mutable, new) < 0) {
talloc_free(new);
goto error;
/*
* Allocate an attribute.
*/
- n = dict_attr_alloc_name(ctx, da->parent, da->name);
+ n = dict_attr_alloc_null(ctx);
if (!n) return NULL;
/*
/*
* Initialize the rest of the fields.
*/
- dict_attr_init(n, parent, da->attr, type, &flags);
+ dict_attr_init(ctx, &n, parent, da->name, da->attr, type, &flags);
DA_VERIFY(n);
if (dict_vendor_add(dict, old->name, old->attr) < 0) return NULL;
n = dict_attr_alloc(dict->pool, parent, old->name, old->attr, old->type, &flags);
+ if (unlikely(!n)) return NULL;
/*
* Setup parenting for the attribute
/*
* The config files may reference the unknown by name.
* If so, use the pre-defined name instead of an unknown
- * one.!
+ * one!
*/
da = fr_dict_attr_by_name(dict_by_da(parent), n->name);
if (da) {
* or more of the leading components may, in fact, be
* known.
*/
- n = dict_attr_alloc_name(ctx, parent, oid_str);
+ n = dict_attr_alloc_null(ctx);
/*
- * While the name of this attribu
+ * Parse the name of this attribute
*/
do {
unsigned int num;
* Leaf attribute
*/
case '\0':
- dict_attr_init(n, our_parent, num, FR_TYPE_OCTETS, &flags);
+ dict_attr_init(ctx, &n, our_parent, oid_str, num, FR_TYPE_OCTETS, &flags);
break;
}
p++;
return fr_value_box_cmp(a->value, b->value);
}
-/** Allocate a dictionary attribute and assign a name
+/** Set a dictionary attribute's name
*
- * @param[in] ctx to allocate attribute in.
- * @param[in] parent of this da
- * @param[in] name to set.
- * @return
- * - 0 on success.
- * - -1 on failure (memory allocation error).
+ * @note This function can only be used _before_ the attribute is inserted into the dictionary.
+ *
+ * @param[in] da to set name for.
+ * @param[in] name to set. If NULL a name will be automatically generated.
*/
-fr_dict_attr_t *dict_attr_alloc_name(TALLOC_CTX *ctx, fr_dict_attr_t const *parent, char const *name)
+static inline CC_HINT(always_inline) int dict_attr_name_set(fr_dict_attr_t *da, char const *name)
{
- int depth = 0;
- fr_dict_attr_t *da;
+ char buffer[FR_DICT_ATTR_MAX_NAME_LEN + 1];
+ size_t name_len;
+ /*
+ * Generate a name if non is specified
+ */
if (!name) {
- fr_strerror_printf("No attribute name provided");
- return NULL;
+ fr_sbuff_t unknown_name = FR_SBUFF_OUT(buffer, sizeof(buffer));
+
+ fr_sbuff_in_strcpy_literal(&unknown_name, "Attr-");
+ if (da->parent) {
+ if (fr_dict_print_attr_oid(&unknown_name, NULL, da->parent) > 0) {
+ fr_sbuff_in_char(&unknown_name, '.');
+ }
+ }
+ fr_sbuff_in_sprintf(&unknown_name, "%u", da->attr);
+
+ name = buffer;
+ name_len = fr_sbuff_used(&unknown_name);
+ } else {
+ name_len = strlen(name);
}
- if (parent) depth = parent->depth + 1;
+ da->name = talloc_bstrndup(da, name, name_len);
+ if (unlikely(!da->name)) {
+ fr_strerror_printf("Failed allocating space for attribute name");
+ return -1;
+ }
- da = talloc_zero_size(ctx, sizeof(fr_dict_attr_t) + sizeof(da->da_stack[0]) * (depth + 1));
- if (!da) return NULL;
+ return 0;
+}
- talloc_set_type(da, fr_dict_attr_t);
+/** Initialise an attribute's da stack from its parent
+ *
+ * @note This function can only be used _before_ the attribute is inserted into the dictionary.
+ *
+ * @param[in] ctx to realloc attribute in.
+ * @param[in] da_p to populate the da_stack for.
+ */
+static inline CC_HINT(always_inline) int dict_attr_da_stack_set(TALLOC_CTX *ctx, fr_dict_attr_t **da_p)
+{
+ fr_dict_attr_ext_da_stack_t *ext, *p_ext;
+ fr_dict_attr_t *da = *da_p;
+ fr_dict_attr_t const *parent = da->parent;
- da->name = talloc_typed_strdup(da, name);
- if (!da->name) {
- talloc_free(da);
- fr_strerror_printf("Out of memory");
- return NULL;
- }
+ if (!parent) return 1;
+ if (da->depth > FR_DICT_DA_STACK_CACHE_MAX) return 1;
+ if (fr_dict_attr_ext(da, FR_DICT_ATTR_EXT_DA_STACK)) return 1;
+
+ p_ext = fr_dict_attr_ext(parent, FR_DICT_ATTR_EXT_DA_STACK);
+ if (!p_ext) return 1;
+
+ ext = dict_attr_ext_alloc_size(ctx, da_p, FR_DICT_ATTR_EXT_DA_STACK, sizeof(ext->da_stack[0]) * (da->depth + 1));
+ if (unlikely(!ext)) return -1;
+
+ memcpy(ext->da_stack, p_ext->da_stack, sizeof(ext->da_stack[0]) * parent->depth);
/*
- * Set up parent / child relationship, and copy the
- * da_stack from the parent.
+ * Always set the last stack entry to ourselves.
+ */
+ ext->da_stack[da->depth] = da;
+
+ return 0;
+}
+
+/** Cache the vendor pointer for an attribute
+ *
+ * @note This function can only be used _before_ the attribute is inserted into the dictionary.
+ *
+ * @param[in] ctx to realloc attribute in.
+ * @param[in] da_p to set a group reference for.
+ * @param[in] vendor to set.
+ */
+static inline CC_HINT(always_inline) int dict_attr_vendor_set(TALLOC_CTX *ctx,
+ fr_dict_attr_t **da_p, fr_dict_attr_t const *vendor)
+{
+ fr_dict_attr_ext_vendor_t *ext;
+
+ ext = dict_attr_ext_alloc(ctx, da_p, FR_DICT_ATTR_EXT_VENDOR);
+ if (unlikely(!ext)) return -1;
+
+ ext->vendor = vendor;
+
+ return 0;
+}
+
+/** Set a reference for a grouping attribute
+ *
+ * @note This function can only be used _before_ the attribute is inserted into the dictionary.
+ *
+ * @param[in] ctx to realloc attribute in.
+ * @param[in] da_p to set a group reference for.
+ */
+static inline CC_HINT(always_inline) int dict_attr_ref_init(TALLOC_CTX *ctx, fr_dict_attr_t **da_p)
+{
+ fr_dict_attr_ext_ref_t *ext;
+
+ ext = dict_attr_ext_alloc(ctx, da_p, FR_DICT_ATTR_EXT_REF);
+ if (unlikely(!ext)) return -1;
+ memset(ext, 0, sizeof(*ext));
+
+ return 0;
+}
+
+/** Add a child/nesting extension to an attribute
+ *
+ * @note This function can only be used _before_ the attribute is inserted into the dictionary.
+ *
+ * @param[in] ctx to realloc attribute in.
+ * @param[in] da_p to set a group reference for.
+ */
+static inline CC_HINT(always_inline) int dict_attr_children_init(TALLOC_CTX *ctx, fr_dict_attr_t **da_p)
+{
+ fr_dict_attr_ext_ref_t *ext;
+
+ ext = dict_attr_ext_alloc(ctx, da_p, FR_DICT_ATTR_EXT_CHILDREN);
+ if (unlikely(!ext)) return -1;
+ memset(ext, 0, sizeof(*ext));
+
+ return 0;
+}
+
+/** Initialise fields in a dictionary attribute structure
+ *
+ * @note This function can only be used _before_ the attribute is inserted into the dictionary.
+ *
+ * @param[in] ctx To use if we need to re-alloc the da
+ * @param[in] da_p to initialise.
+ * @param[in] parent of the attribute, if none, should be
+ * the dictionary root.
+ * @param[in] name of attribute. Pass NULL for auto-generated name.
+ * @param[in] attr number.
+ * @param[in] type of the attribute.
+ * @param[in] flags to assign.
+ */
+int dict_attr_init(TALLOC_CTX *ctx, fr_dict_attr_t **da_p,
+ fr_dict_attr_t const *parent,
+ char const *name, int attr,
+ fr_type_t type, fr_dict_attr_flags_t const *flags)
+{
+ fr_dict_attr_t *da = *da_p;
+
+ *da = (fr_dict_attr_t) {
+ .attr = attr,
+ .type = type,
+ .flags = *flags,
+ .parent = parent,
+ };
+ *da_p = da;
+
+ /*
+ * Record the parent
*/
if (parent) {
- da->parent = parent;
- da->depth = depth;
+ da->dict = parent->dict;
+ da->depth = parent->depth + 1;
- memcpy(&da->da_stack[0], &parent->da_stack[0],
- sizeof(da->da_stack[0]) * depth);
+ /*
+ * Point to the vendor definition. Since ~90% of
+ * attributes are VSAs, caching this pointer will help.
+ */
+ if (parent->type == FR_TYPE_VENDOR) {
+ if (dict_attr_vendor_set(ctx, da_p, parent) < 0) return -1;
+ } else {
+ dict_attr_ext_copy(ctx, da_p, parent, FR_DICT_ATTR_EXT_VENDOR); /* Noop if no vendor extension */
+ }
+ } else {
+ da->depth = 0;
}
/*
- * Always set the last stack entry to ourselves.
+ * Cache the da_stack so we don't need
+ * to generate it at runtime.
*/
- da->da_stack[depth] = da;
+ dict_attr_da_stack_set(ctx, da_p);
+
+ /*
+ * Structural types can have children
+ * so add the extension for them.
+ */
+ switch (type) {
+ case FR_TYPE_STRUCTURAL:
+ if (dict_attr_children_init(ctx, da_p) < 0) return -1;
+ if ((type == FR_TYPE_TLV) || (type == FR_TYPE_GROUP)) {
+ if (dict_attr_ref_init(ctx, da_p) < 0) return -1;
+ }
+ break;
+
+ default:
+ break;
+ }
+ da = *da_p;
+
+ /*
+ * Name is a separate talloc chunk, so allocate it last
+ * so re-allocing for the extensions doesn't eat up
+ * pool space.
+ */
+ if (dict_attr_name_set(da, name) < 0) return -1;
+ DA_VERIFY(*da_p);
+
+ return 0;
+}
+
+/** Allocate a partially completed attribute
+ *
+ * This is useful in some instances where we need to pre-allocate the attribute
+ * for talloc hierarchy reasons, but want to finish initialising it
+ * with #fr_dict_init later.
+ *
+ * @param[in] ctx to allocate attribute in.
+ * @return
+ * - 0 on success.
+ * - -1 on failure (memory allocation error).
+ */
+fr_dict_attr_t *dict_attr_alloc_null(TALLOC_CTX *ctx)
+{
+ fr_dict_attr_t *da;
+
+ da = talloc(ctx, fr_dict_attr_t);
+ if (unlikely(!da)) return NULL;
+
+ talloc_set_type(da, fr_dict_attr_t);
return da;
}
{
fr_dict_attr_t *n;
- if (!fr_cond_assert(parent) || !fr_cond_assert(parent->dict)) return NULL;
+ n = dict_attr_alloc_null(ctx);
+ if (unlikely(!n)) return NULL;
- /*
- * Allocate a new attribute
- */
- if (!name) {
- char buffer[FR_DICT_ATTR_MAX_NAME_LEN + 1];
- fr_sbuff_t unknown_name = FR_SBUFF_OUT(buffer, sizeof(buffer));
-
- fr_sbuff_in_strcpy_literal(&unknown_name, "Attr-");
- if (parent) {
- if (fr_dict_print_attr_oid(&unknown_name, NULL, parent) > 0) {
- fr_sbuff_in_char(&unknown_name, '.');
- }
- }
- fr_sbuff_in_sprintf(&unknown_name, "%u", attr);
-
- n = dict_attr_alloc_name(ctx, parent, buffer);
- } else {
- n = dict_attr_alloc_name(ctx, parent, name);
- }
-
- dict_attr_init(n, parent, attr, type, flags);
- DA_VERIFY(n);
+ dict_attr_init(ctx, &n, parent, name, attr, type, flags);
return n;
}
*/
static fr_dict_attr_t *dict_attr_acopy(TALLOC_CTX *ctx, fr_dict_attr_t const *in)
{
- fr_dict_attr_t *n;
-
- n = dict_attr_alloc_name(ctx, in->parent, in->name);
- if (!n) return NULL;
+ fr_dict_attr_t *n;
+ uint8_t i;
- dict_attr_init(n, in->parent, in->attr, in->type, &in->flags);
+ n = dict_attr_alloc(ctx, in->parent, in->name, in->attr, in->type, &in->flags);
+ for (i = 0; i < NUM_ELEMENTS(in->ext); i++) if (in->ext[i]) {
+ if (unlikely(!dict_attr_ext_copy(ctx, &n, in, i))) {
+ talloc_free(n);
+ return NULL;
+ }
+ }
DA_VERIFY(n);
return n;
}
vendor = talloc_zero(dict, fr_dict_vendor_t);
+ if (!vendor) {
+ oom:
+ fr_strerror_printf("Out of memory");
+ return -1;
+ }
+
vendor->name = talloc_typed_strdup(vendor, name);
if (!vendor->name) {
talloc_free(vendor);
- fr_strerror_printf("Out of memory");
- return -1;
+ goto oom;
}
vendor->pen = num;
vendor->type = vendor->length = 1; /* defaults */
if (da_has_ref(parent)) {
fr_strerror_printf("Cannot add children to attribute '%s' which has 'ref=%s'",
- parent->name, parent->ref->name);
+ parent->name, fr_dict_attr_ref(parent)->name);
return false;
}
return fr_hash_table_finddata(dict->vendors_by_num, &(fr_dict_vendor_t) { .pen = vendor_pen });
}
-/** Return the vendor that parents this attribute
- *
- * @note Uses the dictionary hierachy to determine the parent
- *
- * @param[in] da The dictionary attribute to find parent for.
- * @return
- * - NULL if the attribute has no vendor.
- * - A fr_dict_attr_t representing this attribute's associated vendor.
- */
-fr_dict_attr_t const *fr_dict_vendor_attr_by_da(fr_dict_attr_t const *da)
-{
- DA_VERIFY(da);
-
- if (da->type == FR_TYPE_VENDOR) return da;
-
- if (!da->vendor) return NULL;
-
- return da->vendor;
-}
-
/** Return vendor attribute for the specified dictionary and pen
*
* @param[in] vendor_root of the vendor root attribute. Could be 26 (for example) in RADIUS.
* - NULL if vendor does not exist.
* - A fr_dict_attr_t representing the vendor in the dictionary hierarchy.
*/
-fr_dict_attr_t const *fr_dict_vendor_attr_by_num(fr_dict_attr_t const *vendor_root, uint32_t vendor_pen)
+fr_dict_attr_t const *fr_dict_vendor_da_by_num(fr_dict_attr_t const *vendor_root, uint32_t vendor_pen)
{
fr_dict_attr_t const *vendor;
DA_VERIFY(parent);
#endif
- if (da_has_ref(parent)) parent = parent->ref;
+ if (da_has_ref(parent)) parent = fr_dict_attr_ref(parent);
if (!dict_attr_can_have_children(parent) || !parent->children) return NULL;
/*
* Do any necessary dereferencing
*/
- if (da_has_ref(parent)) parent = parent->ref;
+ if (da_has_ref(parent)) parent = fr_dict_attr_ref(parent);
if (!dict_attr_can_have_children(parent) || !parent->children) {
return NULL;
/*
* Do any necessary dereferencing
*/
- if (da_has_ref(parent)) parent = parent->ref;
+ if (da_has_ref(parent)) parent = fr_dict_attr_ref(parent);
if (!dict_attr_can_have_children(parent)) {
fr_strerror_printf("Parent (%s) is a %s, it cannot contain nested attributes",
cursor.c \
dbuff.c \
debug.c \
+ dict_ext.c \
dict_print.c \
dict_tokenize.c \
dict_unknown.c \
* is a TLV attribute parented by a vendor, that's
* also fine...
*/
- vendor = fr_dict_vendor_attr_by_da(parent);
+ vendor = fr_dict_vendor_da_by_da(parent);
if (vendor) vendor_id = vendor->attr;
da = fr_dict_unknown_afrom_fields(ctx, parent,
*/
void fr_proto_da_stack_build(fr_da_stack_t *stack, fr_dict_attr_t const *da)
{
+ fr_dict_attr_t const **cached;
+
if (!da) return;
- if (!da->flags.is_unknown) {
+ /*
+ * See if we have a cached da stack available
+ */
+ cached = fr_dict_attr_da_stack(da);
+ if (cached) {
/*
* da->da_stack[0] is dict->root
*/
- memcpy(&stack->da[0], &da->da_stack[1], sizeof(stack->da[0]) * da->depth);
+ memcpy(&stack->da[0], &cached[1], sizeof(stack->da[0]) * da->depth);
} else {
fr_dict_attr_t const *da_p, **da_o;
p += 4;
value_len -= 4; /* -= 4 for the vendor ID field */
- our_parent = fr_dict_vendor_attr_by_num(attr_vendor_specific, vendor);
+ our_parent = fr_dict_vendor_da_by_num(attr_vendor_specific, vendor);
if (!our_parent) {
if (flags & FR_DIAMETER_AVP_FLAG_MANDATORY) {
fr_strerror_printf("Mandatory bit set and no vendor %u found", vendor);