*** xref:dictionary/member.adoc[MEMBER]
*** xref:dictionary/protocol.adoc[PROTOCOL]
*** xref:dictionary/reference.adoc[References]
-*** xref:dictionary/struct.adoc[STRUCT]
*** xref:dictionary/value.adoc[VALUE]
*** xref:dictionary/vendor.adoc[VENDOR]
*** xref:dictionary/begin-protocol.adoc[BEGIN-PROTOCOL]
Names occur in many places, such as in
xref:dictionary/attribute.adoc[ATTRIBUTE] definitions,
xref:dictionary/define.adoc[DEFINE],
-xref:dictionary/member.adoc[MEMBER],
-xref:dictionary/struct.adoc[STRUCT], etc. We generally refer to all
+xref:dictionary/member.adoc[MEMBER], etc. We generally refer to all
of these entities as being 'attributes', no matter how the name was
defined.
| xref:dictionary/include.adoc[$INCLUDE] | Include another dictionary file
| xref:dictionary/member.adoc[MEMBER] | Define a member of a `STRUCT`
| xref:dictionary/protocol.adoc[PROTOCOL] | Define a protocol like `RADIUS` or `DHCPv4`
-| xref:dictionary/struct.adoc[STRUCT] | Define a structure which can contain ``MEMBER``s
| xref:dictionary/value.adoc[VALUE] | Define a name for a particular value of an `ATTRIBUTE`
| xref:dictionary/vendor.adoc[VENDOR] | Define a name and number for a vendor
|=====
.Description
-The `MEMBER` keyword defines a name and data type mapping for members
-of a parent xref:dictionary/struct.adoc[STRUCT] definition. The definitions
-preceding a `MEMBER` must be either another `MEMBER`, or be a `STRUCT`.
+The `MEMBER` keyword defines a name and data type mapping for fields
+of a `struct` data type.
<name>:: The name of the attribute. The name can contain alphanumeric
characters, `-`, and `_`. The name should be short and descriptive.
+
-The name of this `MEMBER` is defined only within the context of its parent `STRUCT`.
+The name of this `MEMBER` is defined only within the context of its parent `struct`.
<type>:: A xref:type/index.adoc[data type], or the special type `bit`.
+
| Name | Description
| `array` | For fixed-size types, declare that the contents of the packet can have an array of this value.
| `enum=...` | For "leaf" types, copy xref:dictionary/value.adoc[VALUE]s from an `xref:dictionary/enum.adoc[ENUM].
-| `clone=...` | For `tlv` or 'struct' types, clone (or copy) child definitions from another attribute of the same type
-| `key` | This member is a "key" type. The struct can have different xref:dictionary/struct.adoc[STRUCT] children depending on the value of the key.
+| `clone=...` | For `tlv` types, clone (or copy) child definitions from another attribute of the same type
+| `key` | This member is a "key" type. The structure can have different children of type `union`, depending on the value of the key.
|=====
See the the xref:dictionary/reference.adoc[reference] page for the syntax of references in the dictionary.
.Examples
----
-STRUCT Foo
+ATTRIBUTE Foo 1 struct
MEMBER Bar bit[3]
MEMBER Baz bit[5]
MEMBER Name string
| `string[n]` | Declare that this attribute uses `n` bytes of `string` data
|=====
-
-// Copyright (C) 2023 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Copyright (C) 2025 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
// This documentation was developed by Network RADIUS SAS.
+++ /dev/null
-= The STRUCT keyword
-
-.Syntax
-----
-STRUCT <name> <field> <value> [<flags>]
-----
-
-.Description
-The `STRUCT` keyword defines a child structure of a previous `struct`,
-which is keyed by a particular xref:dictionary/member.adoc[MEMBER].
-
-Many protocols are defined as a _header_ which contains information
-about the protocol, and a _data_ portion which contains data carried
-by that _header_. The _header_ usually includes a _type_ field, which
-defines how the _data_ portion is interpreted. The `STRUCT` keyword
-allows the server to automatically decode these protocols, and all of
-their contents.
-
-<name>:: The name of the attribute. The name can contain alphanumeric
-characters, `-`, and `_`. The name should be short and descriptive.
-+
-As the names are hierarchical, the name is scoped to its parent. So
-the name `Counter` can mean different things, depending on its
-context.
-
-<field>:: The name of a previous xref:dictionary/member.adoc[MEMBER]
-which has been marked up with the word `key` in the `flags` field.
-+
-In most cases, the _<field>_ reference is simply the name of a field
-in the attribute of type `struct` which was defined immediately before
-this definition.
-+
-The _<field>_ reference can also be an OID-style name, as in
-`Client-Id.Type`. The name is always looked up in the current dictionary.
-References cannot be to fields in other dictionaries.
-
-<value>:: When the _<field>_ has this value, the data is interpreted
-as this `struct`. Numbers can be specified as decimal (`19`), or as
-hex (`0xffee`).
-
-<flags>:: Can only be `length=uint8` or `length=uint16`. When
-encoding or decoding the structure, it is prefixed by a `uint8` or `uint16` field
-containing the length of the structure.
-
-Common flags and meanings
-[options="header"]
-[cols="30%,70%"]
-|=====
-| Name | Description
-| `length=uint8` | When encoding or decoding the structure, it is prefixed by a `uint8` field containing the length of the structure.
-| `length=uint16` | When encoding or decoding the structure, it is prefixed by a `uint16` field containing the length of the structure.
-| `offset=<number>` | When encoding or decoding the structure, add `number` to the value in the `length` field.
-|=====
-
-
-The following example shows how one structure can include another one. In this example, the `Information` structure has two fields: `Type` and `Other`. It has two possible sub-structures which can appear after the `Other` field. Which sub-structure to be decoded is defined by the `key` field: `Type`.
-
-.Example of a Key field
-----
-ATTRIBUTE Information 1 struct
-MEMBER Type uint8 key
-MEMBER Other uint32
-
-STRUCT Foo Type 1
-MEMBER Bar uint16
-MEMBER Baz uint16
-
-STRUCT Blag Type 2
-MEMBER Whoops uint32
-MEMBER Stuff uint8
-----
-
-== Caveats
-
-Variable-sized fields such as `tlv`, `struct`, `string`, or `octets` can only be placed at the end of the `struct`.
-
-// Copyright (C) 2023 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
-// This documentation was developed by Network RADIUS SAS.
| group | generic grouping
| struct | structure which contains fixed-width fields
| tlv | type-length-value which contains other attributes
+| union | Selects a child based on the value of a `key` field.
| vendor | Encapsulation of a vendor within data type `vsa`
| vsa | Encapsulation of vendor-specific attributes
|=====
types have different behaviors about what kind of children they can
contain, and how the data type is sent in a packet over the network.
-struct:: A `struct` contains fixed-sized data types, in a pre-defined order.
-+
-The `struct` can only contain a fixed and pre-defined list of child
-attributes. These attributes are the fields, or members, of the structure.
-+
-The `struct` always encodes all of its children. If a child is
-missing, then the relevant field is filled with zeros.
-
group: A `group` contains an arbitrary collection of children, in any order.
+
A `group` is really a reference to some other attribute elsewhere in
The `group` only encodes the child attributes which have been created
and stored within the `group`. The order of children does not matter.
+struct:: A `struct` contains fixed-sized data types, in a pre-defined order.
++
+The `struct` can only contain a fixed and pre-defined list of child
+attributes. These attributes are the fields, or members, of the structure.
++
+The `struct` always encodes all of its children. If a child is
+missing, then the relevant field is filled with zeros.
+
tlv:: A `tlv` defines a hierarchy of children, which can only be contained in the `tlv`.
+
The `tlv` can only contain child attributes which have been defined as
The `tlv` only encodes the child attributes which have been created
and stored within the `tlv`. The order of children does not matter.
+union: A `union` contains one out of set of children.
++
+The `union` can contains any child attributes, so long as they are
+within the same protocol namespace. See the
+dictionary/attribute.adoc[ATTRIBUTE] documentation for more
+information.
++
+The `union` only encodes one child attribute which has been defined
+within the `union`.
+
vendor:: A `vendor` is a group which has a limited subset of children:
attributes which have been defined by that vendor.
+
A `vsa` data type can only be a contain children of the `vendor` data type.
-// Copyright (C) 2021 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
+// Copyright (C) 2025 Network RADIUS SAS. Licenced under CC-by-NC 4.0.
// This documentation was developed by Network RADIUS SAS
return dict_set_value_attr(dctx, da);
}
-/** Process a STRUCT name attr value
- *
- * Define struct 'name' when key 'attr' has 'value'.
- *
- * Which MUST be a sub-structure of another struct
- */
-static int dict_read_process_struct(dict_tokenize_ctx_t *dctx, char **argv, int argc,
- UNUSED fr_dict_attr_flags_t *base_flags)
-{
- fr_value_box_t box = FR_VALUE_BOX_INITIALISER_NULL(box);
- int i;
- fr_dict_attr_t const *parent = NULL;
- fr_dict_attr_t const *key = NULL;
- unsigned int attr;
- char const *name = argv[0];
- char const *value;
- char *flags = NULL;
- fr_dict_attr_t *da;
-
- fr_assert(dctx->stack_depth > 0);
-
- /*
- * Old-stle: unwind the stack until we find a parent which has a child named for key_attr.
- */
- parent = CURRENT_FRAME(dctx)->da;
- if (parent->type != FR_TYPE_UNION) {
- char const *key_attr;
-
- if ((argc < 3) || (argc > 4)) {
- fr_strerror_const("Invalid STRUCT syntax");
- return -1;
- }
-
- key_attr = argv[1];
- value = argv[2];
- if (argc == 4) flags = argv[3];
-
- parent = NULL;
- for (i = dctx->stack_depth; i > 0; i--) {
- key = dict_attr_by_name(NULL, dctx->stack[i].da, key_attr);
- if (key) {
- parent = key;
- dctx->stack_depth = i;
- break;
- }
- }
-
- /*
- * No parent was found, maybe the reference is a fully qualified name from the root.
- */
- if (!parent) {
- parent = fr_dict_attr_by_oid(NULL, CURRENT_FRAME(dctx)->da->dict->root, key_attr);
-
- if (!parent) {
- fr_strerror_printf("Invalid STRUCT definition, unknown key attribute %s",
- key_attr);
- return -1;
- }
-
- /*
- * @todo - remove after migration_union_key is deleted
- */
- if (!fr_dict_attr_is_key_field(parent)) {
- fr_strerror_printf("Attribute '%s' is not a 'key' attribute", key_attr);
- return -1;
- }
-
- key = parent;
- }
-
- } else {
- fr_dict_attr_ext_ref_t *ext;
-
- /*
- * STRUCT inside of a UNION doesn't need to specify the name of the key.
- *
- * STRUCT name value [flags]
- */
- if ((argc < 2) || (argc > 3)) {
- fr_strerror_const("Invalid STRUCT syntax");
- return -1;
- }
-
- value = argv[1];
- if (argc == 3) flags = argv[2];
-
- /*
- * The parent is a union. Get and verify the key ref.
- */
- ext = fr_dict_attr_ext(parent, FR_DICT_ATTR_EXT_KEY);
- fr_assert(ext != NULL);
-
- /*
- * Double-check names against the reference.
- */
- key = ext->ref;
- fr_assert(key);
- fr_assert(fr_dict_attr_is_key_field(key));
- }
-
- /*
- * Rely on dict_attr_flags_valid() to ensure that
- * da->type is an unsigned integer, AND that da->parent->type == struct
- */
- if (!fr_cond_assert(parent->parent->type == FR_TYPE_STRUCT)) return -1;
-
- /*
- * Parse the value, which should be a small integer.
- */
- if (fr_value_box_from_str(NULL, &box, key->type, NULL, value, strlen(value), NULL) < 0) {
- fr_strerror_printf_push("Invalid value for STRUCT \"%s\"", value);
- return -1;
- }
-
- /*
- * Allocate the attribute here, and then fill in the fields
- * as we start parsing the various elements of the definition.
- */
- da = dict_attr_alloc_null(dctx->dict->pool, dctx->dict->proto);
- if (unlikely(da == NULL)) return -1;
- dict_attr_location_set(dctx, da);
- da->dict = dctx->dict;
-
- if (unlikely(dict_attr_type_init(&da, FR_TYPE_STRUCT) < 0)) {
- error:
- talloc_free(da);
- return -1;
- }
-
- /*
- * Structs can be prefixed with 16-bit lengths, but not
- * with any other type of length.
- */
- if (flags) {
- if (dict_process_flag_field(dctx, flags, &da) < 0) goto error;
- }
-
- /*
- * Create a unique number for the child attribute, based on the value of the key.
- */
- switch (key->type) {
- case FR_TYPE_UINT8:
- attr = box.vb_uint8;
- break;
-
- case FR_TYPE_UINT16:
- attr = box.vb_uint16;
- break;
-
- case FR_TYPE_UINT32:
- attr = box.vb_uint32;
- break;
-
- default:
- fr_assert(0); /* should have been checked earlier when the key attribute was defined */
- return -1;
- }
-
- if (unlikely(dict_attr_num_init(da, attr) < 0)) goto error;
- if (unlikely(dict_attr_parent_init(&da, parent) < 0)) goto error;
- if (unlikely(dict_attr_finalise(&da, name) < 0)) goto error;
-
- /*
- * Check to see if this is a duplicate attribute
- * and whether we should ignore it or error out...
- */
- switch (dict_attr_allow_dup(da)) {
- case 1:
- break;
-
- case 0:
- talloc_free(da);
- return 0;
-
- default:
- goto error;
- }
-
- /*
- * Add the STRUCT to the global namespace, and as a child of "parent".
- */
- switch (dict_attr_add_or_fixup(&dctx->fixup, &da)) {
- default:
- goto error;
-
- /* FIXME: Should dict_attr_enum_add_name also be called in the fixup code? */
- case 0:
- da = dict_attr_by_name(NULL, parent, name);
- if (!da) return -1;
-
- /*
- * A STRUCT definition is an implicit BEGIN-STRUCT.
- */
- dctx->relative_attr = NULL;
- if (dict_dctx_push(dctx, da, NEST_NONE) < 0) return -1;
-
- /*
- * Add the VALUE to the key attribute, and ensure that
- * the VALUE also contains a pointer to the child struct.
- */
- if (dict_attr_enum_add_name(fr_dict_attr_unconst(key), name, &box, false, true, da) < 0) {
- fr_value_box_clear(&box);
- return -1;
- }
- fr_value_box_clear(&box);
- break;
-
- case 1:
- break;
- }
-
- return 0;
-}
/** Process a value alias
*
{ L("FLAGS"), { .parse = dict_read_process_flags } },
{ L("MEMBER"), { .parse = dict_read_process_member } },
{ L("PROTOCOL"), { .parse = dict_read_process_protocol, .begin = dict_begin_protocol }},
- { L("STRUCT"), { .parse = dict_read_process_struct } },
{ L("VALUE"), { .parse = dict_read_process_value } },
{ L("VENDOR"), { .parse = dict_read_process_vendor } },
};