From: Arran Cudbard-Bell Date: Mon, 19 Oct 2020 18:07:35 +0000 (-0500) Subject: Add generic extensions for different dictionary structures X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=dc2eb3dfb56869427cb1f34f7475c200327ef4d5;p=thirdparty%2Ffreeradius-server.git Add generic extensions for different dictionary structures --- diff --git a/src/lib/util/dict.h b/src/lib/util/dict.h index 68c0726749a..0d9b7029254 100644 --- a/src/lib/util/dict.h +++ b/src/lib/util/dict.h @@ -21,6 +21,7 @@ * * @copyright 2015 The FreeRADIUS server project */ +RCSIDH(dict_h, "$Id$") #ifdef __cplusplus extern "C" { @@ -29,6 +30,7 @@ extern "C" { #include #include #include +#include #include #include #include @@ -37,7 +39,6 @@ extern "C" { #include #include #include -#include /* * Avoid circular type references. @@ -114,7 +115,7 @@ extern const size_t dict_attr_sizes[FR_TYPE_MAX + 1][2]; /** Extension identifier * - * @note New extension structures should also be added to the #fr_dict_ext_length_min table in dict_ext.c + * @note New extension structures should also be added to the #fr_dict_attr_ext_info table in dict_ext.c */ typedef enum { FR_DICT_ATTR_EXT_NAME = 0, //!< Name of the attribute. @@ -128,72 +129,6 @@ typedef enum { FR_DICT_ATTR_EXT_MAX } fr_dict_attr_ext_t; -/** The alignment of object extension structures - * - */ -#ifdef __WORD_SIZE -# if __WORD_SIZE < 4 -# define FR_DICT_ATTR_EXT_ALIGNMENT sizeof(uint32_t) -# else -# define FR_DICT_ATTR_EXT_ALIGNMENT __WORD_SIZE /* From limits.h */ -# endif -#else -# define FR_DICT_ATTR_EXT_ALIGNMENT sizeof(uint64_t) -#endif - -/** 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_hash_table_t *child_by_name; //!< Namespace at this level in the hierarchy. - 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 - Holds enumeration values - * - */ -typedef struct { - fr_hash_table_t *value_by_name; //!< Lookup an enumeration value by name - fr_hash_table_t *name_by_value; //!< Lookup a name by value -} fr_dict_attr_ext_enumv_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 { @@ -214,7 +149,16 @@ struct dict_attr_s { fr_dict_attr_flags_t flags; //!< Flags. uint8_t ext[FR_DICT_ATTR_EXT_MAX]; //!< Extensions to the dictionary attribute. -} CC_HINT(aligned(FR_DICT_ATTR_EXT_ALIGNMENT)); +} CC_HINT(aligned(FR_EXT_ALIGNMENT)); + +/** Extension identifier + * + * @note New extension structures should also be added to the #fr_dict_ext_length_min table in dict_ext.c + */ +typedef enum { + FR_DICT_ENUM_EXT_UNION_REF = 0, //!< Reference to a union/subs-struct. + FR_DICT_ENUM_EXT_MAX +} fr_dict_enum_ext_t; /** Value of an enumerated attribute * @@ -226,8 +170,10 @@ typedef struct { ///< on partial buffers. fr_value_box_t const *value; //!< Enum value (what name maps to). + uint8_t ext[FR_DICT_ENUM_EXT_MAX]; //!< Extensions to the dictionary attribute. + fr_dict_attr_t const *child_struct[0]; //!< for key fields -} fr_dict_enum_t; +} fr_dict_enum_t CC_HINT(aligned(FR_EXT_ALIGNMENT)); /** Private enterprise * @@ -335,76 +281,11 @@ 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 +/** @name Dictionary structure extensions * * @{ */ - -/** 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)))) - -/* 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; -} +#include /** @} */ /** @name Programatically create dictionary attributes and values @@ -421,6 +302,7 @@ int fr_dict_attr_enum_add_name_next(fr_dict_attr_t *da, char const *name) CC_H int fr_dict_str_to_argv(char *str, char **argv, int max_argc); /** @} */ + /** @name Unknown ephemeral attributes * * @{ @@ -508,6 +390,8 @@ ssize_t fr_dict_attr_by_oid(fr_dict_t const *dict, fr_dict_attr_t const **pare */ fr_dict_attr_t const *fr_dict_root(fr_dict_t const *dict); +bool fr_dict_is_read_only(fr_dict_t const *dict); + ssize_t fr_dict_by_protocol_substr(fr_dict_attr_err_t *err, fr_dict_t const **out, fr_sbuff_t *name, fr_dict_t const *dict_def); @@ -533,46 +417,6 @@ static inline bool fr_dict_attr_is_top_level(fr_dict_attr_t const *da) return true; } -/** Return the vendor number 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 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; - - ext = fr_dict_attr_ext(da, FR_DICT_ATTR_EXT_VENDOR); - if (!ext || !ext->vendor) return 0; - - 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_name(fr_dict_t const *dict, char const *name); diff --git a/src/lib/util/dict_ext.c b/src/lib/util/dict_ext.c index 4aa586e6439..cea3a39a105 100644 --- a/src/lib/util/dict_ext.c +++ b/src/lib/util/dict_ext.c @@ -24,33 +24,11 @@ RCSID("$Id$") #include +#include #include #include #include -/** Copy function for fixing up extensions after they're copy - * - */ -typedef void *(* fr_dict_attr_ext_copy_t)(TALLOC_CTX *ctx, fr_dict_attr_t **da_out_p, - fr_dict_attr_ext_t ext, void *ext_ptr, size_t ext_len); - -/** Additional information for a given extension - */ -typedef struct { - size_t min; //!< Minimum size of extension. - bool has_hdr; //!< Has additional metadata allocated before - ///< the extension data. - bool can_copy; //!< Copying this extension between attributes is allowed. - fr_dict_attr_ext_copy_t copy; //!< Override the normal copy operation with a callback. -} fr_dict_attr_ext_info_t; - -static void *fr_dict_attr_ext_enumv_copy(TALLOC_CTX *ctx, fr_dict_attr_t **da_p, - fr_dict_attr_ext_t ext, void *ext_ptr, size_t ext_len); - - -static void *fr_dict_attr_ext_vendor_copy(TALLOC_CTX *ctx, fr_dict_attr_t **da_p, - fr_dict_attr_ext_t ext, void *ext_ptr, UNUSED size_t ext_len); - static fr_table_num_ordered_t const dict_attr_ext_table[] = { { L("name"), FR_DICT_ATTR_EXT_NAME }, { L("children"), FR_DICT_ATTR_EXT_CHILDREN }, @@ -61,61 +39,18 @@ static fr_table_num_ordered_t const dict_attr_ext_table[] = { }; static size_t dict_attr_ext_table_len = NUM_ELEMENTS(dict_attr_ext_table); -/** Holds additional information about extension structures - * - */ -static fr_dict_attr_ext_info_t const fr_dict_ext_info[] = { - [FR_DICT_ATTR_EXT_NAME] = { - .min = sizeof(char), - .has_hdr = true, - .can_copy = false, /* Name may change, and we can only set it once */ - }, - [FR_DICT_ATTR_EXT_CHILDREN] = { - .min = sizeof(fr_dict_attr_ext_children_t), - .can_copy = false, /* Limitation in hashing scheme we use */ - }, - [FR_DICT_ATTR_EXT_REF] = { - .min = sizeof(fr_dict_attr_ext_ref_t), - .can_copy = true, - }, - [FR_DICT_ATTR_EXT_VENDOR] = { - .min = sizeof(fr_dict_attr_ext_vendor_t), - .can_copy = true, - .copy = fr_dict_attr_ext_vendor_copy - }, - [FR_DICT_ATTR_EXT_ENUMV] = { - .min = sizeof(fr_dict_attr_ext_enumv_t), - .can_copy = true, - .copy = fr_dict_attr_ext_enumv_copy - }, - [FR_DICT_ATTR_EXT_DA_STACK] = { - .min = sizeof(fr_dict_attr_ext_da_stack_t), - .has_hdr = true, - .can_copy = false /* Reinitialised for each new attribute */ - }, - [FR_DICT_ATTR_EXT_MAX] = {} -}; - -/** Optional extension header struct - * - */ -typedef struct { - size_t len; //!< Length of extension data. - uint8_t data[]; //!< Extension data -} CC_HINT(aligned(FR_DICT_ATTR_EXT_ALIGNMENT)) dict_attr_ext_hdr_t; - /** Copy all enumeration values from one attribute to another * */ -static void *fr_dict_attr_ext_enumv_copy(TALLOC_CTX *ctx, fr_dict_attr_t **da_p, - fr_dict_attr_ext_t ext, void *ext_ptr, UNUSED size_t ext_len) +static void *fr_dict_attr_ext_enumv_copy(void **chunk_p, int ext, void *ext_ptr, UNUSED size_t ext_len) { + fr_dict_attr_t **da_p = (fr_dict_attr_t **)chunk_p; fr_dict_attr_ext_enumv_t *new_ext, *old_ext; fr_hash_iter_t iter; fr_dict_enum_t *enumv; bool has_child = da_is_key_field(*da_p); - new_ext = dict_attr_ext_alloc(ctx, da_p, ext); + new_ext = dict_attr_ext_alloc(da_p, ext); if (!new_ext) return NULL; old_ext = ext_ptr; @@ -154,9 +89,9 @@ static void *fr_dict_attr_ext_enumv_copy(TALLOC_CTX *ctx, fr_dict_attr_t **da_p, /** Rediscover the parent of this attribute, and cache it * */ -static void *fr_dict_attr_ext_vendor_copy(TALLOC_CTX *ctx, fr_dict_attr_t **da_p, - fr_dict_attr_ext_t ext, void *ext_ptr, UNUSED size_t ext_len) +static void *fr_dict_attr_ext_vendor_copy(void **chunk_p, int ext, void *ext_ptr, UNUSED size_t ext_len) { + fr_dict_attr_t **da_p = (fr_dict_attr_t **)chunk_p; fr_dict_attr_ext_vendor_t *new_ext, *old_ext = ext_ptr; fr_dict_attr_t const **da_stack; fr_dict_attr_t const *old_vendor = old_ext->vendor; @@ -171,7 +106,7 @@ static void *fr_dict_attr_ext_vendor_copy(TALLOC_CTX *ctx, fr_dict_attr_t **da_p if (da_stack) { new_vendor = da_stack[old_vendor->depth]; if ((new_vendor->type == old_vendor->type) && (new_vendor->attr == old_vendor->attr)) { - new_ext = dict_attr_ext_alloc(ctx, da_p, ext); + new_ext = dict_attr_ext_alloc(da_p, ext); if (!new_ext) return NULL; new_ext->vendor = new_vendor; @@ -188,7 +123,7 @@ static void *fr_dict_attr_ext_vendor_copy(TALLOC_CTX *ctx, fr_dict_attr_t **da_p */ for (da = (*da_p)->parent; da; da = da->parent) { if ((da->type == old_vendor->type) && (da->attr == old_vendor->attr)) { - new_ext = dict_attr_ext_alloc(ctx, da_p, ext); + new_ext = dict_attr_ext_alloc(da_p, ext); if (!new_ext) return NULL; new_ext->vendor = da; @@ -199,231 +134,70 @@ static void *fr_dict_attr_ext_vendor_copy(TALLOC_CTX *ctx, fr_dict_attr_t **da_p return NULL; } -/** 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 aligned_len = ROUND_UP_POW2(ext_len, FR_DICT_ATTR_EXT_ALIGNMENT); - size_t da_len; - size_t hdr_len = 0; - - size_t offset; - - fr_dict_attr_ext_info_t const *info; - fr_dict_attr_t *n_da, *da = *da_p; - uint8_t *ext_ptr; - - (void)talloc_get_type_abort(da, fr_dict_attr_t); - - if (da->ext[ext]) return fr_dict_attr_ext(da, ext); - - 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; - } - - info = &fr_dict_ext_info[ext]; - if (info->has_hdr) hdr_len = sizeof(dict_attr_ext_hdr_t); /* Add space for a length prefix */ - - /* - * Packing the offsets into a uint8_t means - * the offset address of the final extension - * must be less than or equal to - * UINT8_MAX * FR_DICT_ATTR_EXT_ALIGNMENT - */ - da_len = talloc_length(da); - offset = (da_len + hdr_len) / FR_DICT_ATTR_EXT_ALIGNMENT; - if (unlikely(offset > UINT8_MAX)) { - fr_strerror_printf("Insufficient space remaining for extensions"); - return NULL; - } - - n_da = talloc_realloc_size(ctx, da, da_len + hdr_len + aligned_len); - if (!n_da) { - fr_strerror_printf("Failed in realloc for dictionary extension '%s'. " - "Tried to realloc %zu bytes -> %zu bytes", - fr_table_str_by_value(dict_attr_ext_table, ext, ""), - da_len, da_len + aligned_len); - return NULL; - } - talloc_set_type(n_da, fr_dict_attr_t); - *da_p = n_da; - - n_da->ext[ext] = (uint8_t)offset; - ext_ptr = ((uint8_t *)n_da) + da_len; - - if (info->has_hdr) { - dict_attr_ext_hdr_t *ext_hdr = (dict_attr_ext_hdr_t *)ext_ptr; - - ext_hdr->len = ext_len; /* Record the real size */ - return &ext_hdr->data; /* Pointer to the data portion */ - } - - return ext_ptr; -} - -/** 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_info[ext].min); -} - -/** 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 offset; - fr_dict_attr_ext_info_t const *info; - dict_attr_ext_hdr_t *ext_hdr; - - offset = da->ext[ext]; - if (!offset) return 0; - - info = &fr_dict_ext_info[ext]; - if (!info->has_hdr) return info->min; /* Fixed size */ - - ext_hdr = (dict_attr_ext_hdr_t *)((uintptr_t)da) + ((offset * FR_DICT_ATTR_EXT_ALIGNMENT) - sizeof(dict_attr_ext_hdr_t)); - return ext_hdr->len; -} - -/** Copy extension data from one attribute to another +/** Holds additional information about extension structures * - * @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 offset 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) -{ - void *ext_ptr, *new_ext_ptr; - size_t ext_len; - - fr_dict_attr_ext_info_t const *info; - - 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; +fr_ext_t const fr_dict_attr_ext_def = { + .offset_of_exts = __offsetof(fr_dict_attr_t, ext), + .name_table = dict_attr_ext_table, + .name_table_len = &dict_attr_ext_table_len, + .max = FR_DICT_ATTR_EXT_MAX, + .info = { + [FR_DICT_ATTR_EXT_NAME] = { + .min = sizeof(char), + .has_hdr = true, + .can_copy = false, /* Name may change, and we can only set it once */ + }, + [FR_DICT_ATTR_EXT_CHILDREN] = { + .min = sizeof(fr_dict_attr_ext_children_t), + .can_copy = false, /* Limitation in hashing scheme we use */ + }, + [FR_DICT_ATTR_EXT_REF] = { + .min = sizeof(fr_dict_attr_ext_ref_t), + .can_copy = true, + }, + [FR_DICT_ATTR_EXT_VENDOR] = { + .min = sizeof(fr_dict_attr_ext_vendor_t), + .can_copy = true, + .copy = fr_dict_attr_ext_vendor_copy + }, + [FR_DICT_ATTR_EXT_ENUMV] = { + .min = sizeof(fr_dict_attr_ext_enumv_t), + .can_copy = true, + .copy = fr_dict_attr_ext_enumv_copy + }, + [FR_DICT_ATTR_EXT_DA_STACK] = { + .min = sizeof(fr_dict_attr_ext_da_stack_t), + .has_hdr = true, + .can_copy = false /* Reinitialised for each new attribute */ + }, + [FR_DICT_ATTR_EXT_PROTOCOL_SPECIFIC] = { + .min = sizeof(fr_dict_ext_protocol_specific_t), + .can_copy = true + }, + [FR_DICT_ATTR_EXT_MAX] = {} } +}; - info = &fr_dict_ext_info[ext]; - if (!info->can_copy) { - fr_strerror_printf("Extension cannot be copied"); - return NULL; - } - - ext_len = dict_attr_ext_len(da_in, ext); - ext_ptr = fr_dict_attr_ext(da_in, ext); - if (!ext_ptr) return NULL; - - /* - * Use the special copy function. - * Its responsible for allocating the extension in the - * destination attribute. - */ - if (info->copy) return info->copy(ctx, da_out_p, ext, ext_ptr, ext_len); - - /* - * If there's no special function - * just memcpy the data over. - */ - new_ext_ptr = dict_attr_ext_alloc_size(ctx, da_out_p, ext, ext_len); - if (!new_ext_ptr) return NULL; - memcpy(new_ext_ptr, ext_ptr, ext_len); - - return new_ext_ptr; -} +static fr_table_num_ordered_t const dict_enum_ext_table[] = { + { L("union_ref"), FR_DICT_ENUM_EXT_UNION_REF } +}; +static size_t dict_enum_ext_table_len = NUM_ELEMENTS(dict_enum_ext_table); -/** Copy all the extensions from one attribute to another +/** Holds additional information about extension structures * */ -int dict_attr_ext_copy_all(TALLOC_CTX *ctx, - fr_dict_attr_t **da_out_p, fr_dict_attr_t const *da_in) -{ - fr_dict_attr_ext_t i; - - for (i = 0; i < NUM_ELEMENTS(da_in->ext); i++) { - if (!da_in->ext[i] || !fr_dict_ext_info[i].can_copy) continue; - if (!dict_attr_ext_copy(ctx, da_out_p, da_in, i)) return -1; +fr_ext_t const fr_dict_enum_ext_def = { + .offset_of_exts = __offsetof(fr_dict_enum_t, ext), + .name_table = dict_enum_ext_table, + .name_table_len = &dict_enum_ext_table_len, + .max = FR_DICT_ENUM_EXT_MAX, + .info = { + [FR_DICT_ENUM_EXT_UNION_REF] = { + .min = sizeof(fr_dict_enum_ext_union_ref_t), + .can_copy = true + }, + [FR_DICT_ENUM_EXT_MAX] = {} } +}; - return 0; -} - -void dict_attr_ext_debug(fr_dict_attr_t const *da) -{ - fr_dict_attr_ext_t i; - - FR_FAULT_LOG("ext: %s - total len = %zu, ext len = %zu", - da->name, talloc_length(da), talloc_length(da) - sizeof(fr_dict_attr_t)); - - for (i = 0; i < NUM_ELEMENTS(da->ext); i++) { - if (fr_dict_attr_has_ext(da, i)) { - void *ext = fr_dict_attr_ext(da, i); - size_t ext_len = dict_attr_ext_len(da, i); - - if (ext_len > 1024) { - FR_FAULT_LOG("ext: %s - bad length %zu - limiting to 1024", - fr_table_str_by_value(dict_attr_ext_table, i, ""), - ext_len); - ext_len = 1024; - } - - FR_FAULT_LOG("ext: %s - start=%p end=%p len=%zu", - fr_table_str_by_value(dict_attr_ext_table, i, ""), - ext, ((uint8_t *)ext) + ext_len, ext_len); - FR_FAULT_LOG_HEX(ext, ext_len); - } - } -} diff --git a/src/lib/util/dict_ext.h b/src/lib/util/dict_ext.h new file mode 100644 index 00000000000..a1efb9f1221 --- /dev/null +++ b/src/lib/util/dict_ext.h @@ -0,0 +1,207 @@ +#pragma once +/* + * 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 + */ + +/** Multi-protocol AVP dictionary API + * + * @file src/lib/util/dict_ext.h + * + * @copyright 2020 The FreeRADIUS server project + * @copyright 2020 Arran Cudbard-Bell + */ +RCSIDH(dict_ext_h, "$Id$") + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** 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_hash_table_t *child_by_name; //!< Namespace at this level in the hierarchy. + 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 - Holds enumeration values + * + */ +typedef struct { + fr_hash_table_t *value_by_name; //!< Lookup an enumeration value by name + fr_hash_table_t *name_by_value; //!< Lookup a name by value +} fr_dict_attr_ext_enumv_t; + +/** Enum extension - Sub-struct or union pointer + * + */ +typedef struct { + fr_dict_attr_t const *union_ref; //!< The union da this value points into. +} fr_dict_enum_ext_union_ref_t; + +/** Attribute extension - Protocol-specific + * + */ +typedef struct { + void *uctx; //!< Protocol specific extensions +} fr_dict_ext_protocol_specific_t; + +/** @name Add extension structures to attributes + * + * @{ + */ + +/* 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 FR_EXT_PTR(da, ext, 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; +} + +/** Return the vendor number 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 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; + + ext = fr_dict_attr_ext(da, FR_DICT_ATTR_EXT_VENDOR); + if (!ext || !ext->vendor) return 0; + + 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; +} + +/** @} */ + +#ifdef __cplusplus +} +#endif diff --git a/src/lib/util/dict_ext_priv.h b/src/lib/util/dict_ext_priv.h new file mode 100644 index 00000000000..977126209e5 --- /dev/null +++ b/src/lib/util/dict_ext_priv.h @@ -0,0 +1,161 @@ +#pragma once +/* + * 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 for dictionary definitions + * + * @file src/lib/util/dict_ext_priv.h + * + * @copyright 2020 The FreeRADIUS server project + * @copyright 2020 Arran Cudbard-Bell + */ +RCSIDH(dict_ext_priv_h, "$Id$") + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern fr_ext_t const fr_dict_attr_ext_def; +extern fr_ext_t const fr_dict_enum_ext_def; + +/** @name Add extension structures to attributes + * + * @{ + */ + +/** Allocate an attribute extension of a particular size + * + */ +static inline void *dict_attr_ext_alloc_size(fr_dict_attr_t **da_p, fr_dict_attr_ext_t ext, size_t ext_len) +{ + if (unlikely((*da_p)->dict && fr_dict_is_read_only((*da_p)->dict))) { + fr_strerror_printf("%s dictionary has been marked as read only", fr_dict_root((*da_p)->dict)->name); + return NULL; + } + + return fr_ext_alloc_size(&fr_dict_attr_ext_def, (void **)da_p, ext, ext_len); +} + +/** Allocate an attribute extension + * + */ +static inline void *dict_attr_ext_alloc(fr_dict_attr_t **da_p, fr_dict_attr_ext_t ext) +{ + if (unlikely((*da_p)->dict && fr_dict_is_read_only((*da_p)->dict))) { + fr_strerror_printf("%s dictionary has been marked as read only", fr_dict_root((*da_p)->dict)->name); + return NULL; + } + + return fr_ext_alloc_size(&fr_dict_attr_ext_def, (void **)da_p, ext, fr_dict_attr_ext_def.info[ext].min); +} + +/** Return the length of an attribute extension + * + */ +static inline size_t dict_attr_ext_len(fr_dict_attr_t const *da, fr_dict_attr_ext_t ext) +{ + return fr_ext_len(&fr_dict_attr_ext_def, (void const *)da, ext); +} + +/** Copy a single attribute extension from one attribute to another + * + */ +static inline void *dict_attr_ext_copy(fr_dict_attr_t **da_out_p, fr_dict_attr_t const *da_in, fr_dict_attr_ext_t ext) +{ + if (unlikely((*da_out_p)->dict && fr_dict_is_read_only((*da_out_p)->dict))) { + fr_strerror_printf("%s dictionary has been marked as read only", fr_dict_root((*da_out_p)->dict)->name); + return NULL; + } + + return fr_ext_copy(&fr_dict_attr_ext_def, (void **)da_out_p, (void const *)da_in, ext); +} + +/** Copy all attribute extensions from one attribute to another + * + */ +static inline int dict_attr_ext_copy_all(fr_dict_attr_t **da_out_p, fr_dict_attr_t const *da_in) +{ + if (unlikely((*da_out_p)->dict && fr_dict_is_read_only((*da_out_p)->dict))) { + fr_strerror_printf("%s dictionary has been marked as read only", fr_dict_root((*da_out_p)->dict)->name); + return -1; + } + + return fr_ext_copy_all(&fr_dict_attr_ext_def, (void **)da_out_p, (void const *)da_in); +} + +/** Print extension debug information for attributes + * + */ +static inline void dict_attr_ext_debug(fr_dict_attr_t const *da) +{ + fr_ext_debug(&fr_dict_attr_ext_def, da->name, da); +} +/** @} */ + +/** @name Convenience functions for populating attribute extensions + * + * @{ + */ +static inline int dict_attr_ref_set(fr_dict_attr_t const *da, fr_dict_attr_t const *ref) +{ + 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 (%s) contains no 'ref' extension", da->name, + fr_table_str_by_value(fr_value_box_type_table, da->type, "")); + return -1; + } + ext->ref = ref; + + 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 (%s) contains no 'children' extension", da->name, + fr_table_str_by_value(fr_value_box_type_table, da->type, "")); + return -1; + } + ext->children = children; + + return 0; +} + +static inline fr_dict_attr_t const **dict_attr_children(fr_dict_attr_t const *da) +{ + 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 (%s) contains no 'children' extension", da->name, + fr_table_str_by_value(fr_value_box_type_table, da->type, "")); + return NULL; + } + return ext->children; +} +/** @} */ + +#ifdef __cplusplus +} +#endif diff --git a/src/lib/util/dict_priv.h b/src/lib/util/dict_priv.h index 3e1e6a20bf2..8b257a798ac 100644 --- a/src/lib/util/dict_priv.h +++ b/src/lib/util/dict_priv.h @@ -27,10 +27,11 @@ extern "C" { #define _DICT_PRIVATE 1 +#include #include -#include +#include #include -#include +#include #define DICT_POOL_SIZE (1024 * 1024 * 2) #define DICT_FIXUP_POOL_SIZE (1024) @@ -135,7 +136,7 @@ int dict_dlopen(fr_dict_t *dict, char const *name); fr_dict_attr_t *dict_attr_alloc_null(TALLOC_CTX *ctx); -int dict_attr_init(TALLOC_CTX *ctx, fr_dict_attr_t **da_p, +int dict_attr_init(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); @@ -147,70 +148,6 @@ fr_dict_attr_t *dict_attr_alloc(TALLOC_CTX *ctx, fr_dict_attr_t *dict_attr_acopy(TALLOC_CTX *ctx, fr_dict_attr_t const *in, char const *new_name); -/** @name Add extension structures to attributes - * - * @{ - */ -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); - -int dict_attr_ext_copy_all(TALLOC_CTX *ctx, - fr_dict_attr_t **da_out_p, fr_dict_attr_t const *da_in); - -void dict_attr_ext_debug(fr_dict_attr_t const *da); - -static inline int dict_attr_ref_set(fr_dict_attr_t const *da, fr_dict_attr_t const *ref) -{ - 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 (%s) contains no 'ref' extension", da->name, - fr_table_str_by_value(fr_value_box_type_table, da->type, "")); - return -1; - } - ext->ref = ref; - - 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 (%s) contains no 'children' extension", da->name, - fr_table_str_by_value(fr_value_box_type_table, da->type, "")); - return -1; - } - ext->children = children; - - return 0; -} - -static inline fr_dict_attr_t const **dict_attr_children(fr_dict_attr_t const *da) -{ - 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 (%s) contains no 'children' extension", da->name, - fr_table_str_by_value(fr_value_box_type_table, da->type, "")); - return NULL; - } - return ext->children; -} -/** @} */ - int dict_attr_child_add(fr_dict_attr_t *parent, fr_dict_attr_t *child); int dict_protocol_add(fr_dict_t *dict); diff --git a/src/lib/util/dict_unknown.c b/src/lib/util/dict_unknown.c index bbb7ebfdb7c..5ae98bfda8b 100644 --- a/src/lib/util/dict_unknown.c +++ b/src/lib/util/dict_unknown.c @@ -85,8 +85,8 @@ 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(ctx, &n, parent, new_name ? new_name : da->name, da->attr, type, &flags); - dict_attr_ext_copy_all(ctx, &n, da); + dict_attr_init(&n, parent, new_name ? new_name : da->name, da->attr, type, &flags); + dict_attr_ext_copy_all(&n, da); DA_VERIFY(n); return n; @@ -533,7 +533,7 @@ ssize_t fr_dict_unknown_afrom_oid_str(TALLOC_CTX *ctx, fr_dict_attr_t **out, * Leaf attribute */ case '\0': - dict_attr_init(ctx, &n, our_parent, oid_str, num, FR_TYPE_OCTETS, &flags); + dict_attr_init(&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 dc81855d7cf..f13f2f9005c 100644 --- a/src/lib/util/dict_util.c +++ b/src/lib/util/dict_util.c @@ -350,11 +350,10 @@ static int dict_enum_value_cmp(void const *one, void const *two) * * @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 name for. * @param[in] name to set. If NULL a name will be automatically generated. */ -static inline CC_HINT(always_inline) int dict_attr_name_set(TALLOC_CTX *ctx, fr_dict_attr_t **da_p, char const *name) +static inline CC_HINT(always_inline) int dict_attr_name_set(fr_dict_attr_t **da_p, char const *name) { char buffer[FR_DICT_ATTR_MAX_NAME_LEN + 1]; size_t name_len; @@ -392,7 +391,7 @@ static inline CC_HINT(always_inline) int dict_attr_name_set(TALLOC_CTX *ctx, fr_ * We do still need to fixup the da->name pointer * though. */ - name_start = dict_attr_ext_alloc_size(ctx, da_p, FR_DICT_ATTR_EXT_NAME, name_len + 1); + name_start = dict_attr_ext_alloc_size(da_p, FR_DICT_ATTR_EXT_NAME, name_len + 1); if (!name_start) return -1; name_end = name_start + name_len; @@ -410,14 +409,13 @@ static inline CC_HINT(always_inline) int dict_attr_name_set(TALLOC_CTX *ctx, fr_ * * @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) +static inline CC_HINT(always_inline) int dict_attr_children_init(fr_dict_attr_t **da_p) { fr_dict_attr_ext_children_t *ext; - ext = dict_attr_ext_alloc(ctx, da_p, FR_DICT_ATTR_EXT_CHILDREN); + ext = dict_attr_ext_alloc(da_p, FR_DICT_ATTR_EXT_CHILDREN); if (unlikely(!ext)) return -1; memset(ext, 0, sizeof(*ext)); @@ -428,14 +426,13 @@ static inline CC_HINT(always_inline) int dict_attr_children_init(TALLOC_CTX *ctx * * @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) +static inline CC_HINT(always_inline) int dict_attr_ref_init(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); + ext = dict_attr_ext_alloc(da_p, FR_DICT_ATTR_EXT_REF); if (unlikely(!ext)) return -1; memset(ext, 0, sizeof(*ext)); @@ -446,16 +443,14 @@ static inline CC_HINT(always_inline) int dict_attr_ref_init(TALLOC_CTX *ctx, fr_ * * @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) +static inline CC_HINT(always_inline) int dict_attr_vendor_set(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); + ext = dict_attr_ext_alloc(da_p, FR_DICT_ATTR_EXT_VENDOR); if (unlikely(!ext)) return -1; ext->vendor = vendor; @@ -467,10 +462,9 @@ static inline CC_HINT(always_inline) int dict_attr_vendor_set(TALLOC_CTX *ctx, * * @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) +static inline CC_HINT(always_inline) int dict_attr_da_stack_set(fr_dict_attr_t **da_p) { fr_dict_attr_ext_da_stack_t *ext, *p_ext; fr_dict_attr_t *da = *da_p; @@ -483,7 +477,7 @@ static inline CC_HINT(always_inline) int dict_attr_da_stack_set(TALLOC_CTX *ctx, 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)); + ext = dict_attr_ext_alloc_size(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); @@ -500,14 +494,13 @@ static inline CC_HINT(always_inline) int dict_attr_da_stack_set(TALLOC_CTX *ctx, * * @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_enumv_init(TALLOC_CTX *ctx, fr_dict_attr_t **da_p) +static inline CC_HINT(always_inline) int dict_attr_enumv_init(fr_dict_attr_t **da_p) { fr_dict_attr_ext_enumv_t *ext; - ext = dict_attr_ext_alloc(ctx, da_p, FR_DICT_ATTR_EXT_ENUMV); + ext = dict_attr_ext_alloc(da_p, FR_DICT_ATTR_EXT_ENUMV); if (unlikely(!ext)) return -1; memset(ext, 0, sizeof(*ext)); @@ -518,7 +511,6 @@ static inline CC_HINT(always_inline) int dict_attr_enumv_init(TALLOC_CTX *ctx, f * * @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. @@ -527,7 +519,7 @@ static inline CC_HINT(always_inline) int dict_attr_enumv_init(TALLOC_CTX *ctx, f * @param[in] type of the attribute. * @param[in] flags to assign. */ -int dict_attr_init(TALLOC_CTX *ctx, fr_dict_attr_t **da_p, +int dict_attr_init(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) @@ -551,9 +543,9 @@ int dict_attr_init(TALLOC_CTX *ctx, fr_dict_attr_t **da_p, * 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; + if (dict_attr_vendor_set(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 */ + dict_attr_ext_copy(da_p, parent, FR_DICT_ATTR_EXT_VENDOR); /* Noop if no vendor extension */ } } else { (*da_p)->depth = 0; @@ -563,7 +555,7 @@ int dict_attr_init(TALLOC_CTX *ctx, fr_dict_attr_t **da_p, * Cache the da_stack so we don't need * to generate it at runtime. */ - dict_attr_da_stack_set(ctx, da_p); + dict_attr_da_stack_set(da_p); /* * Structural types can have children @@ -573,15 +565,15 @@ int dict_attr_init(TALLOC_CTX *ctx, fr_dict_attr_t **da_p, case FR_TYPE_STRUCTURAL: structural: if (type == FR_TYPE_GROUP) { - if (dict_attr_ref_init(ctx, da_p) < 0) return -1; + if (dict_attr_ref_init(da_p) < 0) return -1; break; } if (type == FR_TYPE_TLV) { - if (dict_attr_ref_init(ctx, da_p) < 0) return -1; + if (dict_attr_ref_init(da_p) < 0) return -1; } - if (dict_attr_children_init(ctx, da_p) < 0) return -1; + if (dict_attr_children_init(da_p) < 0) return -1; break; /* @@ -589,14 +581,14 @@ int dict_attr_init(TALLOC_CTX *ctx, fr_dict_attr_t **da_p, */ case FR_TYPE_UINT8: /* Hopefully temporary until unions are done properly */ case FR_TYPE_UINT16: /* Same here */ - if (dict_attr_enumv_init(ctx, da_p) < 0) return -1; + if (dict_attr_enumv_init(da_p) < 0) return -1; goto structural; /* * Leaf types */ default: - if (dict_attr_enumv_init(ctx, da_p) < 0) return -1; + if (dict_attr_enumv_init(da_p) < 0) return -1; break; } @@ -604,7 +596,7 @@ int dict_attr_init(TALLOC_CTX *ctx, fr_dict_attr_t **da_p, * Name is a separate talloc chunk. We allocate * it last because we cache the pointer value. */ - if (dict_attr_name_set(ctx, da_p, name) < 0) return -1; + if (dict_attr_name_set(da_p, name) < 0) return -1; DA_VERIFY(*da_p); @@ -658,7 +650,7 @@ fr_dict_attr_t *dict_attr_alloc(TALLOC_CTX *ctx, n = dict_attr_alloc_null(ctx); if (unlikely(!n)) return NULL; - if (dict_attr_init(ctx, &n, parent, name, attr, type, flags) < 0) { + if (dict_attr_init(&n, parent, name, attr, type, flags) < 0) { talloc_free(n); return NULL; } @@ -683,7 +675,7 @@ fr_dict_attr_t *dict_attr_acopy(TALLOC_CTX *ctx, fr_dict_attr_t const *in, char n = dict_attr_alloc(ctx, in->parent, new_name ? new_name : in->name, in->attr, in->type, &in->flags); if (unlikely(!n)) return NULL; - if (dict_attr_ext_copy_all(ctx, &n, in) < 0) { + if (dict_attr_ext_copy_all(&n, in) < 0) { talloc_free(n); return NULL; } @@ -1637,6 +1629,11 @@ fr_dict_attr_t const *fr_dict_root(fr_dict_t const *dict) return dict->root; } +bool fr_dict_is_read_only(fr_dict_t const *dict) +{ + return dict->read_only; +} + ssize_t dict_by_protocol_substr(fr_dict_attr_err_t *err, fr_dict_t **out, fr_sbuff_t *name, fr_dict_t const *dict_def) { diff --git a/src/lib/util/ext.c b/src/lib/util/ext.c new file mode 100644 index 00000000000..d9d97e94d23 --- /dev/null +++ b/src/lib/util/ext.c @@ -0,0 +1,232 @@ +/* + * 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 talloced structures + * + * @file src/lib/util/ext.c + * + * @copyright 2020 The FreeRADIUS server project + * @copyright 2020 Arran Cudbard-Bell + */ +RCSID("$Id$") + +#include +#include +#include +#include +#include + +#define CHUNK_EXT_PTR(_chunk, _chunk_ext, _ext) ((void *)((chunk_ext[_ext] * FR_EXT_ALIGNMENT) + ((uintptr_t)(_chunk)))) +#define CHUNK_EXT(_chunk, _offset) ((uint8_t *)(((uintptr_t)_chunk) + (_offset))) + +/** 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] def Extension definitions. + * @param[in,out] chunk_p The chunk to add an extension for. + * Under certain circumstances the value of *chunk_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 *fr_ext_alloc_size(fr_ext_t const *def, void **chunk_p, int ext, size_t ext_len) +{ + size_t aligned_len = ROUND_UP_POW2(ext_len, FR_EXT_ALIGNMENT); + size_t chunk_len; + size_t hdr_len = 0; + + size_t offset; + + fr_ext_info_t const *info; + void *n_chunk, *chunk = *chunk_p; + uint8_t *chunk_ext; + uint8_t *ext_ptr; + char const *type; + + chunk_ext = CHUNK_EXT(*chunk_p, def->offset_of_exts); + if (chunk_ext[ext]) return CHUNK_EXT_PTR(*chunk_p, chunk_ext, ext); + + info = &def->info[ext]; + if (info->has_hdr) hdr_len = sizeof(dict_ext_hdr_t); /* Add space for a length prefix */ + + /* + * Packing the offsets into a uint8_t means + * the offset address of the final extension + * must be less than or equal to + * UINT8_MAX * FR_EXT_ALIGNMENT + */ + chunk_len = talloc_get_size(chunk); + offset = (chunk_len + hdr_len) / FR_EXT_ALIGNMENT; + if (unlikely(offset > UINT8_MAX)) { + fr_strerror_printf("Insufficient space remaining for extensions"); + return NULL; + } + + type = talloc_get_name(chunk); + n_chunk = talloc_realloc_size(NULL, chunk, chunk_len + hdr_len + aligned_len); + if (!n_chunk) { + fr_strerror_printf("Failed reallocing %s. Tried to realloc %zu bytes -> %zu bytes", + type, chunk_len, chunk_len + aligned_len); + return NULL; + } + talloc_set_name_const(n_chunk, type); + *chunk_p = n_chunk; + + chunk_ext = CHUNK_EXT(*chunk_p, def->offset_of_exts); + chunk_ext[ext] = (uint8_t)offset; + + ext_ptr = ((uint8_t *)n_chunk) + chunk_len; + + if (info->has_hdr) { + dict_ext_hdr_t *ext_hdr = (dict_ext_hdr_t *)ext_ptr; + + ext_hdr->len = ext_len; /* Record the real size */ + return &ext_hdr->data; /* Pointer to the data portion */ + } + + return ext_ptr; +} + +/** Return the length of an extension + * + * @param[in] def Extension definitions. + * @param[in] chunk 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 fr_ext_len(fr_ext_t const *def, void const *chunk, int ext) +{ + uint8_t offset; + fr_ext_info_t const *info; + dict_ext_hdr_t *ext_hdr; + uint8_t *chunk_ext; + + chunk_ext = CHUNK_EXT(chunk, def->offset_of_exts); + offset = chunk_ext[ext]; + if (!offset) return 0; + + info = &def->info[ext]; + if (!info->has_hdr) return info->min; /* Fixed size */ + + ext_hdr = (dict_ext_hdr_t *)((uintptr_t)chunk) + ((offset * FR_EXT_ALIGNMENT) - sizeof(dict_ext_hdr_t)); + return ext_hdr->len; +} + +/** Copy extension data from one attribute to another + * + * @param[in] def Extension definitions. + * @param[in,out] chunk_out to copy extension to. + * @param[in] chunk_in to copy extension from. + * @param[in] ext to copy. + * @return + * - NULL if we failed to allocate an extension structure. + * - A pointer to the offset of the extension in da_out. + */ +void *fr_ext_copy(fr_ext_t const *def, void **chunk_out, void const *chunk_in, int ext) +{ + void *ext_ptr, *new_ext_ptr; + uint8_t *chunk_ext; + size_t ext_len; + + fr_ext_info_t const *info; + + info = &def->info[ext]; + if (!info->can_copy) { + fr_strerror_printf("Extension cannot be copied"); + return NULL; + } + + chunk_ext = CHUNK_EXT(chunk_in, def->offset_of_exts); + ext_ptr = CHUNK_EXT_PTR(chunk_in, chunk_ext, ext); + if (!ext_ptr) return NULL; + + ext_len = fr_ext_len(def, chunk_in, ext); + + /* + * Use the special copy function. + * Its responsible for allocating the extension in the + * destination attribute. + */ + if (info->copy) return info->copy(chunk_out, ext, ext_ptr, ext_len); + + /* + * If there's no special function + * just memcpy the data over. + */ + new_ext_ptr = fr_ext_alloc_size(def, chunk_out, ext, ext_len); + if (!new_ext_ptr) return NULL; + memcpy(new_ext_ptr, ext_ptr, ext_len); + + return new_ext_ptr; +} + +/** Copy all the extensions from one attribute to another + * + */ +int fr_ext_copy_all(fr_ext_t const *def, void **chunk_out, void const *chunk_in) +{ + int i; + uint8_t *ext_in = CHUNK_EXT(chunk_in, def->offset_of_exts); + + for (i = 0; i < def->max; i++) { + if (!ext_in[i] || !def->info[i].can_copy) continue; + if (!fr_ext_copy(def, chunk_out, chunk_in, i)) return -1; + } + + return 0; +} + +void fr_ext_debug(fr_ext_t const *def, char const *name, void const *chunk) +{ + int i; + + FR_FAULT_LOG("ext: %s - total len = %zu", name, talloc_get_size(chunk)); + for (i = 0; i < (int)def->max; i++) { + uint8_t *chunk_ext = CHUNK_EXT(def->info, def->offset_of_exts); + if (chunk_ext[i]) { + void *ext = CHUNK_EXT_PTR(chunk, chunk_ext, i); + size_t ext_len = fr_ext_len(def, chunk, i); + + char const *ext_name = fr_table_ordered_str_by_num(def->name_table, + *def->name_table_len, + i, ""); + + if (ext_len > 1024) { + FR_FAULT_LOG("ext: %s - possibly bad length %zu - limiting dump to 1024", + ext_name, ext_len); + ext_len = 1024; + } + + FR_FAULT_LOG("ext: %s - start=%p end=%p len=%zu", + ext_name, ext, ((uint8_t *)ext) + ext_len, ext_len); + FR_FAULT_LOG_HEX(ext, ext_len); + } + } +} diff --git a/src/lib/util/ext.h b/src/lib/util/ext.h new file mode 100644 index 00000000000..5b84fccb6bd --- /dev/null +++ b/src/lib/util/ext.h @@ -0,0 +1,116 @@ +#pragma once +/* + * 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 talloced structures + * + * These allow multiple variable length chunks to be appended to talloced + * structures. Extensions can either contain a header in which case the + * exact length is recorded, or they can be of a fixed size. + * + * The structure being extended must be padded to a multiple of FR_EXT_ALIGNMENT. + * + * It is strongly recommended that extended structures are allocated in a + * talloc_pool() to avoid the overhead of multiple reallocs. + * + * @file src/lib/util/ext.h + * + * @copyright 2020 The FreeRADIUS server project + * @copyright 2020 Arran Cudbard-Bell + */ +RCSIDH(ext_h, "$Id$") + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** The alignment of object extension structures + * + */ +#ifdef __WORD_SIZE +# if __WORD_SIZE < 4 +# define FR_EXT_ALIGNMENT sizeof(uint32_t) +# else +# define FR_EXT_ALIGNMENT __WORD_SIZE /* From limits.h */ +# endif +#else +# define FR_EXT_ALIGNMENT sizeof(uint64_t) +#endif + +/** Function for fixing up extensions after they're copied + * + */ +typedef void *(* fr_ext_copy_t)(void **chunk_p, int ext, void *ext_ptr, size_t ext_len); + +/** Additional information for a given extension + */ +typedef struct { + size_t min; //!< Minimum size of extension. + bool has_hdr; //!< Has additional metadata allocated before + ///< the extension data. + bool can_copy; //!< Copying this extension between attributes is allowed. + fr_ext_copy_t copy; //!< Override the normal copy operation with a callback. +} fr_ext_info_t; + +/** Structure to define a set of extensions + * + */ +typedef struct { + size_t offset_of_exts; //!< Where in the extended struct the extensions array starts. + fr_table_num_ordered_t const *name_table; //!< String identifiers for the extensions. + size_t *name_table_len; //!< How many extensions there are in the table. + int max; //!< The highest extension value. + fr_ext_info_t const info[]; //!< Additional information about each extension. +} fr_ext_t; + +/** Optional extension header struct + * + */ +typedef struct { + size_t len; //!< Length of extension data. + uint8_t data[]; //!< Extension data +} CC_HINT(aligned(FR_EXT_ALIGNMENT)) dict_ext_hdr_t; + +/** @name Generic extension manipulation functions that can be used with any talloced chunk + * + * @{ + */ + +/** Return a pointer to the specified extension structure + * + * @param[in] _ptr to fetch extension for. + * @param[in] _field Array of extensions. + * @param[in] _ext to retrieve. + */ +#define FR_EXT_PTR(_ptr, _field, _ext) ((void *)(((_ptr)->_field[_ext] * FR_EXT_ALIGNMENT) + ((uintptr_t)(_ptr)))) + +void *fr_ext_alloc_size(fr_ext_t const *def, void **chunk_p, int ext, size_t ext_len); + +size_t fr_ext_len(fr_ext_t const *def, void const *chunk_in, int ext); + +void *fr_ext_copy(fr_ext_t const *def, void **chunk_out, void const *chunk_in, int ext); + +int fr_ext_copy_all(fr_ext_t const *def, void **chunk_out, void const *chunk_in); + +void fr_ext_debug(fr_ext_t const *def, char const *name, void const *chunk); +/** @} */ + +#ifdef __cplusplus +} +#endif diff --git a/src/lib/util/libfreeradius-util.mk b/src/lib/util/libfreeradius-util.mk index 0febfb05aed..8ae65bada10 100644 --- a/src/lib/util/libfreeradius-util.mk +++ b/src/lib/util/libfreeradius-util.mk @@ -21,6 +21,7 @@ SOURCES := \ dl.c \ dns.c \ event.c \ + ext.c \ fifo.c \ file.c \ fopencookie.c \ diff --git a/src/lib/util/talloc.h b/src/lib/util/talloc.h index e41cefcabef..8a216b1c11b 100644 --- a/src/lib/util/talloc.h +++ b/src/lib/util/talloc.h @@ -63,11 +63,6 @@ talloc_foreach(vpt_m, vpt) { (_p < _end) && (_iter = *((void **)(_p))); \ _p = (__typeof__(_p))((__typeof__(_array))_p) + 1) -/** Get the number of bytes in a given talloc chunk - * - */ -#define talloc_length(_ptr) talloc_array_length((uint8_t const *)(_ptr)) - typedef int(* fr_talloc_free_func_t)(void *fire_ctx, void *uctx); typedef struct fr_talloc_destructor_s fr_talloc_destructor_t;