From: Arran Cudbard-Bell Date: Tue, 13 Oct 2020 22:25:23 +0000 (-0500) Subject: Add mechanism for adding type specific dictionary extensions X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=473133641d3dc42796da67d2f43f7cc2986ee884;p=thirdparty%2Ffreeradius-server.git Add mechanism for adding type specific dictionary extensions --- diff --git a/src/lib/server/tmpl_tokenize.c b/src/lib/server/tmpl_tokenize.c index 915f6d22700..9d40f577349 100644 --- a/src/lib/server/tmpl_tokenize.c +++ b/src/lib/server/tmpl_tokenize.c @@ -1570,7 +1570,7 @@ do_suffix: * group ref. */ case FR_TYPE_GROUP: - parent = da->ref; + parent = fr_dict_attr_ref(da); break; case FR_TYPE_STRUCT: diff --git a/src/lib/unlang/xlat_builtin.c b/src/lib/unlang/xlat_builtin.c index 4627b40cc1e..0c94835e95f 100644 --- a/src/lib/unlang/xlat_builtin.c +++ b/src/lib/unlang/xlat_builtin.c @@ -259,6 +259,11 @@ static void _xlat_func_tree_free(void *xlat) /** 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. diff --git a/src/lib/util/dbuff.c b/src/lib/util/dbuff.c index fcc29a2deb1..516b37da871 100644 --- a/src/lib/util/dbuff.c +++ b/src/lib/util/dbuff.c @@ -23,7 +23,7 @@ * @copyright 2020 Arran Cudbard-Bell */ -RCSID("$Id:") +RCSID("$Id$") #include diff --git a/src/lib/util/dict.h b/src/lib/util/dict.h index f320a080a09..267a2f5e618 100644 --- a/src/lib/util/dict.h +++ b/src/lib/util/dict.h @@ -111,26 +111,85 @@ enum { 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: @@ -146,9 +205,9 @@ struct dict_attr_s { 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 * @@ -248,6 +307,10 @@ typedef struct fr_dict_gctx_s fr_dict_gctx_t; */ #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 @@ -270,6 +333,82 @@ typedef struct fr_dict_gctx_s fr_dict_gctx_t; 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 * * @{ @@ -407,11 +546,34 @@ static inline bool fr_dict_attr_is_top_level(fr_dict_attr_t const *da) */ 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); @@ -420,9 +582,7 @@ fr_dict_vendor_t const *fr_dict_vendor_by_name(fr_dict_t const *dict, char const 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)); diff --git a/src/lib/util/dict_ext.c b/src/lib/util/dict_ext.c new file mode 100644 index 00000000000..8c93a95a300 --- /dev/null +++ b/src/lib/util/dict_ext.c @@ -0,0 +1,185 @@ +/* + * 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 + */ +RCSID("$Id$") + +#include + +/** 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; +} diff --git a/src/lib/util/dict_priv.h b/src/lib/util/dict_priv.h index 4d451cdec5c..886495d2b34 100644 --- a/src/lib/util/dict_priv.h +++ b/src/lib/util/dict_priv.h @@ -126,57 +126,81 @@ struct fr_dict_gctx_s { 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); diff --git a/src/lib/util/dict_tokenize.c b/src/lib/util/dict_tokenize.c index 88de9dda1c4..ee9fc13bbfb 100644 --- a/src/lib/util/dict_tokenize.c +++ b/src/lib/util/dict_tokenize.c @@ -180,6 +180,8 @@ static int dict_read_sscanf_i(unsigned int *pvalue, char const *str) */ 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, @@ -191,10 +193,10 @@ static int dict_root_set(fr_dict_t *dict, char const *name, unsigned int proto_n 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); @@ -535,6 +537,7 @@ static fr_dict_attr_t const *dict_gctx_unwind(dict_tokenize_ctx_t *ctx) 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"); @@ -571,10 +574,10 @@ static int dict_read_process_alias(dict_tokenize_ctx_t *ctx, char **argv, int ar * 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; @@ -836,7 +839,8 @@ static int dict_read_process_attribute(dict_tokenize_ctx_t *ctx, char **argv, in talloc_free(ref); self->dict = dict; - self->ref = da; + + dict_attr_ref_set(self, da); } } @@ -1677,7 +1681,7 @@ static int fr_dict_finalise(dict_tokenize_ctx_t *ctx) } talloc_free(this->ref); - this->da->ref = da; + dict_attr_ref_set(this->da, da); next = this->next; } @@ -2337,9 +2341,11 @@ static int _dict_from_file(dict_tokenize_ctx_t *ctx, } } + 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; diff --git a/src/lib/util/dict_unknown.c b/src/lib/util/dict_unknown.c index e3653cde6f4..a4d974a241c 100644 --- a/src/lib/util/dict_unknown.c +++ b/src/lib/util/dict_unknown.c @@ -46,7 +46,7 @@ fr_dict_attr_t *fr_dict_unknown_acopy(TALLOC_CTX *ctx, fr_dict_attr_t const *da) /* * Allocate an attribute. */ - n = dict_attr_alloc_name(ctx, da->parent, da->name); + n = dict_attr_alloc_null(ctx); if (!n) return NULL; /* @@ -85,7 +85,7 @@ fr_dict_attr_t *fr_dict_unknown_acopy(TALLOC_CTX *ctx, fr_dict_attr_t const *da) /* * 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); @@ -148,6 +148,7 @@ fr_dict_attr_t const *fr_dict_unknown_add(fr_dict_t *dict, fr_dict_attr_t const 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 @@ -342,7 +343,7 @@ fr_dict_attr_t const *fr_dict_unknown_afrom_fields(TALLOC_CTX *ctx, fr_dict_attr /* * 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) { @@ -464,10 +465,10 @@ ssize_t fr_dict_unknown_afrom_oid_str(TALLOC_CTX *ctx, fr_dict_attr_t **out, * 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; @@ -530,7 +531,7 @@ ssize_t fr_dict_unknown_afrom_oid_str(TALLOC_CTX *ctx, fr_dict_attr_t **out, * 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++; diff --git a/src/lib/util/dict_util.c b/src/lib/util/dict_util.c index c015d43e3ad..eefdc378829 100644 --- a/src/lib/util/dict_util.c +++ b/src/lib/util/dict_util.c @@ -359,55 +359,238 @@ static int dict_enum_value_cmp(void const *one, void const *two) 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; } @@ -433,30 +616,10 @@ fr_dict_attr_t *dict_attr_alloc(TALLOC_CTX *ctx, { 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; } @@ -471,12 +634,16 @@ fr_dict_attr_t *dict_attr_alloc(TALLOC_CTX *ctx, */ 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; @@ -551,11 +718,16 @@ int dict_vendor_add(fr_dict_t *dict, char const *name, unsigned int num) } 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 */ @@ -663,7 +835,7 @@ int dict_attr_child_add(fr_dict_attr_t *parent, fr_dict_attr_t *child) 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; } @@ -1705,26 +1877,6 @@ fr_dict_vendor_t const *fr_dict_vendor_by_num(fr_dict_t const *dict, uint32_t ve 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. @@ -1733,7 +1885,7 @@ fr_dict_attr_t const *fr_dict_vendor_attr_by_da(fr_dict_attr_t const *da) * - 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; @@ -2057,7 +2209,7 @@ fr_dict_attr_t const *fr_dict_attr_child_by_da(fr_dict_attr_t const *parent, fr_ 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; @@ -2100,7 +2252,7 @@ inline fr_dict_attr_t *dict_attr_child_by_num(fr_dict_attr_t const *parent, unsi /* * 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; @@ -2156,7 +2308,7 @@ ssize_t fr_dict_attr_child_by_name_substr(fr_dict_attr_err_t *err, /* * 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", diff --git a/src/lib/util/libfreeradius-util.mk b/src/lib/util/libfreeradius-util.mk index f471aa95b53..0febfb05aed 100644 --- a/src/lib/util/libfreeradius-util.mk +++ b/src/lib/util/libfreeradius-util.mk @@ -12,6 +12,7 @@ SOURCES := \ cursor.c \ dbuff.c \ debug.c \ + dict_ext.c \ dict_print.c \ dict_tokenize.c \ dict_unknown.c \ diff --git a/src/lib/util/pair.c b/src/lib/util/pair.c index 71dc37cb75c..f376d1a8234 100644 --- a/src/lib/util/pair.c +++ b/src/lib/util/pair.c @@ -165,7 +165,7 @@ VALUE_PAIR *fr_pair_afrom_child_num(TALLOC_CTX *ctx, fr_dict_attr_t const *paren * 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, diff --git a/src/lib/util/proto.c b/src/lib/util/proto.c index e8c0e0bb8a1..91e5454aa45 100644 --- a/src/lib/util/proto.c +++ b/src/lib/util/proto.c @@ -123,13 +123,19 @@ void *fr_proto_next_encodable(void **prev, void *to_eval, void *uctx) */ 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; diff --git a/src/modules/rlm_eap/types/rlm_eap_ttls/ttls.c b/src/modules/rlm_eap/types/rlm_eap_ttls/ttls.c index 41fc2c0d546..da5de1c9039 100644 --- a/src/modules/rlm_eap/types/rlm_eap_ttls/ttls.c +++ b/src/modules/rlm_eap/types/rlm_eap_ttls/ttls.c @@ -201,7 +201,7 @@ static ssize_t eap_ttls_decode_pair(TALLOC_CTX *ctx, fr_cursor_t *cursor, fr_dic 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);