]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
add "clone" functionality, and tests
authorAlan T. DeKok <aland@freeradius.org>
Thu, 3 Dec 2020 21:04:03 +0000 (16:04 -0500)
committerAlan T. DeKok <aland@freeradius.org>
Thu, 3 Dec 2020 21:09:06 +0000 (16:09 -0500)
We can now say that one TLV is a "clone" of another.  In which
case the children of the source TLV are copied to become
children of the destination TLV

share/dictionary/radius/dictionary.rfc8045
share/dictionary/radius/dictionary.wimax
src/lib/util/dict_tokenize.c
src/tests/unit/protocols/radius/rfc8045.txt [new file with mode: 0644]
src/tests/unit/protocols/radius/wimax.txt

index 00ab781a5045e78b7dfbb36284c4455340afb3fe..8e84330829fd9ae04eda98ed3d6a18624580f745 100644 (file)
@@ -8,11 +8,6 @@
 #
 
 ATTRIBUTE      IP-Port-Limit-Info                      241.5   tlv
-#
-#  These attributes can appear in all of the above TLVs.  There is
-#  currently no way to describe this using the existing dictionary
-#  format.
-#
 ATTRIBUTE      Type                                    .1      integer
 ATTRIBUTE      Limit                                   .2      integer
 ATTRIBUTE      Ext-IPv4-Addr                           .3      ipaddr
@@ -30,38 +25,9 @@ ATTRIBUTE    Range-Start                             .9      integer
 ATTRIBUTE      Range-End                               .10     integer
 ATTRIBUTE      Local-Id                                .11     string
 
-ATTRIBUTE      IP-Port-Range                           241.6   tlv
-ATTRIBUTE      Type                                    .1      integer
-ATTRIBUTE      Limit                                   .2      integer
-ATTRIBUTE      Ext-IPv4-Addr                           .3      ipaddr
-ATTRIBUTE      Int-IPv4-Addr                           .4      ipaddr
-ATTRIBUTE      Int-IPv6-Addr                           .5      ipv6addr
-ATTRIBUTE      Int-Port                                .6      integer
-ATTRIBUTE      Ext-Port                                .7      integer
-ATTRIBUTE      Alloc                                   .8      integer
-
-VALUE  Alloc                           Reserved                0
-VALUE  Alloc                           Allocation              1
-VALUE  Alloc                           Deallocation            2
-
+ATTRIBUTE      IP-Port-Range                           241.6   tlv clone=Extended-Attribute-1.IP-Port-Limit-Info
 ATTRIBUTE      Range-Start                             .9      integer
 ATTRIBUTE      Range-End                               .10     integer
 ATTRIBUTE      Local-Id                                .11     string
 
-ATTRIBUTE      IP-Port-Forwarding-Map                  241.7   tlv
-ATTRIBUTE      Type                                    .1      integer
-ATTRIBUTE      Limit                                   .2      integer
-ATTRIBUTE      Ext-IPv4-Addr                           .3      ipaddr
-ATTRIBUTE      Int-IPv4-Addr                           .4      ipaddr
-ATTRIBUTE      Int-IPv6-Addr                           .5      ipv6addr
-ATTRIBUTE      Int-Port                                .6      integer
-ATTRIBUTE      Ext-Port                                .7      integer
-ATTRIBUTE      Alloc                                   .8      integer
-
-VALUE  Alloc                           Reserved                0
-VALUE  Alloc                           Allocation              1
-VALUE  Alloc                           Deallocation            2
-
-ATTRIBUTE      Range-Start                             .9      integer
-ATTRIBUTE      Range-End                               .10     integer
-ATTRIBUTE      Local-Id                                .11     string
+ATTRIBUTE      IP-Port-Forwarding-Map                  241.7   tlv clone=Extended-Attribute-1.IP-Port-Range
index 00383d049300255eee1c277553bc51bf69695853..41e56e710be252319f8035a67f460aeb39d26f67 100644 (file)
@@ -189,16 +189,16 @@ ATTRIBUTE Downlink-QOS-Id                         28.8    byte
 ATTRIBUTE      Uplink-Classifier                       28.9    string
 ATTRIBUTE      Downlink-Classifier                     28.10   string
 ATTRIBUTE      Classifier                              28.11   tlv
-ATTRIBUTE      ClassifierID                            .1      integer
-ATTRIBUTE      Classifer-Priority                      .2      integer
-ATTRIBUTE      Classifer-Protocol                      .3      integer
-ATTRIBUTE      Classifer-Direction                     .4      byte
+ATTRIBUTE      ID                                      .1      integer
+ATTRIBUTE      Priority                                .2      integer
+ATTRIBUTE      Protocol                                .3      integer
+ATTRIBUTE      Direction                               .4      byte
 
-VALUE  Classifer-Direction             Reserved-0              0
-VALUE  Classifer-Direction             IN                      1
-VALUE  Classifer-Direction             OUT                     2
-VALUE  Classifer-Direction             Bi-Directional          3
-VALUE  Classifer-Direction             FF                      4
+VALUE  Direction                       Reserved-0              0
+VALUE  Direction                       IN                      1
+VALUE  Direction                       OUT                     2
+VALUE  Direction                       Bi-Directional          3
+VALUE  Direction                       FF                      4
 
 ATTRIBUTE      Src-Spec                                28.11.5 tlv
 
@@ -215,21 +215,7 @@ VALUE      Assigned                        Src-Assigned            1
 VALUE  Assigned                        Dst-Assigned            2
 VALUE  Assigned                        Src-Dst-Assigned        3
 
-ATTRIBUTE      Dst-Spec                                28.11.6 tlv
-
-# Copied from 28.11.5
-ATTRIBUTE      IP-Address                              .1      combo-ip
-# Really "combo-ip start, end"
-ATTRIBUTE      IP-Address-Range                        .2      octets
-ATTRIBUTE      IP-Address-Mask                         .3      octets
-ATTRIBUTE      Port                                    .4      short
-# Really "uint16 start, end"
-ATTRIBUTE      Port-Range                              .5      integer
-ATTRIBUTE      Inverted                                .6      byte
-ATTRIBUTE      Assigned                                .7      byte
-VALUE  Assigned                        Src-Assigned            1
-VALUE  Assigned                        Dst-Assigned            2
-VALUE  Assigned                        Src-Dst-Assigned        3
+ATTRIBUTE      Dst-Spec                                28.11.6 tlv clone=Vendor-Specific.WiMAX.Packet-Flow-Descriptor.Classifier.Src-Spec
 
 ATTRIBUTE      IP-TOS/DSCP-Range-and-Mask              28.11.7 octets
 ATTRIBUTE      VLAN-ID                                 28.11.8 integer
@@ -478,47 +464,21 @@ ATTRIBUTE Downlink-QoS-Id                         .8      byte
 #
 #  Classifiers
 #
+#  Almost the same as Packet-Flow-Descriptor.Classifier, but (of course)
+#  with some small changes.
+#
 ATTRIBUTE      Classifier                              84.9    tlv
 ATTRIBUTE      Id                                      .1      byte
 ATTRIBUTE      Priority                                .2      byte
 ATTRIBUTE      Protocol                                .3      byte
 ATTRIBUTE      Direction                               .4      byte
 
-ATTRIBUTE      Src-Spec                                84.9.5  tlv
-# Copied from 28.11.5
-ATTRIBUTE      IP-Address                              .1      combo-ip
-# Really "combo-ip start, end"
-ATTRIBUTE      IP-Address-Range                        .2      octets
-ATTRIBUTE      IP-Address-Mask                         .3      octets
-ATTRIBUTE      Port                                    .4      short
-# Really "uint16 start, end"
-ATTRIBUTE      Port-Range                              .5      integer
-ATTRIBUTE      Inverted                                .6      byte
-ATTRIBUTE      Assigned                                .7      byte
-VALUE  Assigned                        Src-Assigned            1
-VALUE  Assigned                        Dst-Assigned            2
-VALUE  Assigned                        Src-Dst-Assigned        3
-
+ATTRIBUTE      Src-Spec                                84.9.5  tlv  clone=Vendor-Specific.WiMAX.Packet-Flow-Descriptor.Classifier.Src-Spec
+#  Add these two, also
 ATTRIBUTE      MAC-Address                             .8      ether
 ATTRIBUTE      MAC-Mask                                .9      ether
 
-ATTRIBUTE      Dest-Spec                               84.9.6  tlv
-# Copied from 28.11.5
-ATTRIBUTE      IP-Address                              .1      combo-ip
-# Really "combo-ip start, end"
-ATTRIBUTE      IP-Address-Range                        .2      octets
-ATTRIBUTE      IP-Address-Mask                         .3      octets
-ATTRIBUTE      Port                                    .4      short
-# Really "uint16 start, end"
-ATTRIBUTE      Port-Range                              .5      integer
-ATTRIBUTE      Inverted                                .6      byte
-ATTRIBUTE      Assigned                                .7      byte
-VALUE  Assigned                        Src-Assigned            1
-VALUE  Assigned                        Dst-Assigned            2
-VALUE  Assigned                        Src-Dst-Assigned        3
-
-ATTRIBUTE      MAC-Address                             .8      ether
-ATTRIBUTE      MAC-Mask                                .9      ether
+ATTRIBUTE      Dest-Spec                               84.9.6  tlv clone=Vendor-Specific.WiMAX.Packet-Flow-Descriptor-v2.Classifier.Src-Spec
 
 ATTRIBUTE      Classifier-IP-ToS-DSCP                  84.9.7  byte
 
index be71a7bbdeb298d72ad950b93592f15705e46bff..cd05053741fecd547b4a34a1f2a329d5ab41928a 100644 (file)
@@ -64,6 +64,20 @@ struct dict_group_fixup_s {
        dict_group_fixup_t      *next;                  //!< Next in the linked list of fixups.
 };
 
+typedef struct dict_clone_fixup_s dict_clone_fixup_t;
+
+/** A clone call, which we'll resolve later
+ *
+ */
+struct dict_clone_fixup_s {
+       char                    *filename;              //!< where the "group" was defined
+       int                     line;                   //!< ditto
+       fr_dict_attr_t          *parent;                //!< parent where we add the clone
+       fr_dict_attr_t          *da;                    //!< FR_TYPE_TLV to clone
+       char                    *ref;                   //!< the target attribute to clone
+       dict_clone_fixup_t      *next;                  //!< Next in the linked list of fixups.
+};
+
 /** Parser context for dict_from_file
  *
  * Allows vendor and TLV context to persist across $INCLUDEs
@@ -94,6 +108,7 @@ typedef struct {
 
        dict_enum_fixup_t       *enum_fixup;
        dict_group_fixup_t      *group_fixup;
+       dict_clone_fixup_t      *clone_fixup;
 
        fr_dict_attr_t          *ext_fixup;             //!< Head of a list of attributes to apply fixups to.
 } dict_tokenize_ctx_t;
@@ -477,7 +492,7 @@ static int dict_process_flag_field(dict_tokenize_ctx_t *ctx, char *name, fr_type
                                return -1;
                        }
 
-                       if ((type != FR_TYPE_GROUP) && (type != FR_TYPE_TLV) && (type != FR_TYPE_STRUCT)) {
+                       if (type != FR_TYPE_GROUP) {
                                fr_strerror_printf("The 'ref' flag cannot be used for type '%s'",
                                                   fr_table_str_by_value(fr_value_box_type_table, type, "<UNKNOWN>"));
                                return -1;
@@ -487,6 +502,20 @@ static int dict_process_flag_field(dict_tokenize_ctx_t *ctx, char *name, fr_type
                        flags->extra = true;
                        flags->subtype = FLAG_HAS_REF;
 
+               } else if (strcmp(key, "clone") == 0) {
+                       if (!value) {
+                               fr_strerror_printf("Missing attribute name for 'clone=...'");
+                               return -1;
+                       }
+
+                       if (type != FR_TYPE_TLV) {
+                               fr_strerror_printf("The 'clone' flag cannot be used for type '%s'",
+                                                  fr_table_str_by_value(fr_value_box_type_table, type, "<UNKNOWN>"));
+                               return -1;
+                       }
+
+                       *ref = talloc_strdup(ctx->dict->pool, value);
+
                } else if (ctx->dict->subtype_table) {
                        int subtype;
 
@@ -757,14 +786,19 @@ static int dict_read_process_attribute(dict_tokenize_ctx_t *ctx, char **argv, in
                return -1;
        }
 
+#ifndef NDEBUG
+       if (!dict_attr_by_name(NULL, parent, argv[0])) {
+               fr_strerror_printf("Failed to find attribute '%s' we just added.", argv[0]);
+               return -1;
+       }
+#endif
+
        if (set_relative_attr) ctx->relative_attr = da;
 
        /*
-        *      Update 'ref'.  GROUPs always have refs.  For TLVs and
-        *      STRUCTs, we add the ref after the attribute has been
-        *      created.
+        *      Groups can refer to other dictionaries, or other attributes.
         */
-       if (ref || (type == FR_TYPE_GROUP)) {
+       if (type == FR_TYPE_GROUP) {
                fr_dict_attr_t          *self;
                fr_dict_t               *dict;
                char                    *p;
@@ -775,7 +809,6 @@ static int dict_read_process_attribute(dict_tokenize_ctx_t *ctx, char **argv, in
                 *      No qualifiers, just point it to the root of the current dictionary.
                 */
                if (!ref) {
-                       fr_assert(type == FR_TYPE_GROUP);
                        dict = ctx->dict;
                        da = ctx->dict->root;
                        goto check;
@@ -833,6 +866,7 @@ static int dict_read_process_attribute(dict_tokenize_ctx_t *ctx, char **argv, in
                         */
                        fixup->next = ctx->group_fixup;
                        ctx->group_fixup = fixup;
+                       return 0;
 
                } else if (ref[slen] == '\0') {
                        da = dict->root;
@@ -850,17 +884,6 @@ static int dict_read_process_attribute(dict_tokenize_ctx_t *ctx, char **argv, in
                        }
 
                check:
-                       if ((self->type == FR_TYPE_STRUCT) && (da->type != FR_TYPE_STRUCT)) {
-                               fr_strerror_printf("References MUST be to attributes of type 'struct'");
-                               talloc_free(ref);
-                               return -1;
-
-                       } else if (da->type != FR_TYPE_TLV) {
-                               fr_strerror_printf("References MUST be to attributes of type 'tlv'");
-                               talloc_free(ref);
-                               return -1;
-                       }
-
                        if (fr_dict_attr_ref(da)) {
                                fr_strerror_printf("References MUST NOT refer to an ATTRIBUTE which also has 'ref=...'");
                                talloc_free(ref);
@@ -874,6 +897,34 @@ static int dict_read_process_attribute(dict_tokenize_ctx_t *ctx, char **argv, in
                }
        }
 
+       /*
+        *      It's a "clone" thing.
+        */
+       if (ref) {
+               dict_clone_fixup_t      *fixup;
+               fr_dict_attr_t          *self;
+
+               memcpy(&self, &da, sizeof(self)); /* const issues */
+
+               fixup = talloc_zero(ctx->fixup_pool, dict_clone_fixup_t);
+               if (!fixup) goto oom;
+
+               fixup->filename = talloc_strdup(fixup, ctx->stack[ctx->stack_depth].filename);
+               if (!fixup->filename) goto oom;
+               fixup->line = ctx->stack[ctx->stack_depth].line;
+
+               memcpy(&fixup->parent, &parent, sizeof(parent)); /* const issues */
+               fixup->da = self;
+               fixup->ref = ref;
+
+               /*
+                *      Insert to the head of the list.
+                */
+               fixup->next = ctx->clone_fixup;
+               ctx->clone_fixup = fixup;
+               return 0;
+       }
+
        /*
         *      Adding an attribute of type 'struct' is an implicit
         *      BEGIN-STRUCT.
@@ -1756,6 +1807,149 @@ static int fr_dict_finalise(dict_tokenize_ctx_t *ctx)
                }
        }
 
+       if (ctx->clone_fixup) {
+               dict_clone_fixup_t *mine, *this, *next;
+
+               mine = ctx->clone_fixup;
+               ctx->clone_fixup = NULL;
+
+               /*
+                *      Loop over references, cloning the da.
+                */
+               for (this = mine; this != NULL; this = next) {
+                       fr_dict_t               *dict;
+                       fr_dict_attr_t const    *da;
+                       fr_dict_attr_t          *cloned;
+                       char                    *p;
+                       ssize_t                 slen;
+
+                       da = fr_dict_attr_by_oid(NULL, fr_dict_root(ctx->dict), this->ref);
+                       if (da) {
+                               dict = ctx->dict;
+                               goto clone_check;
+                       }
+
+                       /*
+                        *      The attribute doesn't exist, and the reference
+                        *      isn't in a "PROTO.ATTR" format, die.
+                        */
+                       p = strchr(this->ref, '.');
+
+                       /*
+                        *      Get / skip protocol name.
+                        */
+                       slen = dict_by_protocol_substr(NULL,
+                                                      &dict, &FR_SBUFF_IN(this->ref, strlen(this->ref)),
+                                                      ctx->dict);
+                       if (slen <= 0) {
+                               fr_dict_t *other;
+
+                               if (p) *p = '\0';
+
+                               if (fr_dict_protocol_afrom_file(&other, this->ref, NULL) < 0) {
+                                       return -1;
+                               }
+
+                               if (p) *p = '.';
+
+                               /*
+                                *      Grab the protocol name again
+                                */
+                               dict = other;
+                               if (!p) {
+                                       dict = other;
+                                       da = other->root;
+                                       goto clone_check;
+                               }
+
+                               slen = p - this->ref;
+                       }
+
+                       if (slen < 0) {
+                       clone_invalid_reference:
+                               fr_strerror_printf("Invalid reference '%s' at %s[%d]",
+                                                  this->ref,
+                                                  fr_cwd_strip(this->filename), this->line);
+                       clone_error:
+                               /*
+                                *      Just so we don't lose track of things.
+                                */
+                               // @todo - don't leak clone_fixup stuff? things?
+                               return -1;
+                       }
+
+                       /*
+                        *      No known dictionary, so we're asked to just
+                        *      use the whole string.  Which we did above.  So
+                        *      either it's a bad ref, OR it's a ref to a
+                        *      dictionary which doesn't exist.
+                        */
+                       if (slen == 0) goto clone_invalid_reference;
+
+                       /*
+                        *      Look up the attribute.
+                        */
+                       da = fr_dict_attr_by_oid(NULL, fr_dict_root(dict), this->ref + slen + 1);
+                       if (!da) {
+                               fr_strerror_printf("No such attribute '%s' in reference at %s[%d]",
+                                                  this->ref + slen + 1, fr_cwd_strip(this->filename), this->line);
+                               goto clone_error;
+                       }
+
+               clone_check:
+                       if (da->type != FR_TYPE_TLV) {
+                               fr_strerror_printf("Clone references MUST be to attributes of type 'tlv' at %s[%d]",
+                                                  fr_cwd_strip(this->filename), this->line);
+                               goto clone_error;
+                       }
+
+                       if (fr_dict_attr_ref(da)) {
+                               fr_strerror_printf("Clone references MUST NOT refer to an ATTRIBUTE which has 'ref=...' at %s[%d]",
+                                                  fr_cwd_strip(this->filename), this->line);
+                               goto clone_error;
+                       }
+
+                       /*
+                        *      Copy the source attribute, but with a
+                        *      new name and a new attribute number.
+                        */
+                       cloned = dict_attr_acopy(dict->pool, da, this->da->name);
+                       if (!cloned) {
+                               fr_strerror_printf("Failed cloning attribute '%s' to %s", da->name, this->ref);
+                               goto clone_error;
+                       }
+
+                       cloned->attr = this->da->attr;
+                       cloned->parent = this->parent;
+
+                       /*
+                        *      Copy any pre-existing children over.
+                        */
+                       if (dict_attr_children(this->da)) {
+                               if (dict_attr_acopy_children(dict, cloned, this->da) < 0) {
+                                       fr_strerror_printf("Failed cloned attribute '%s' from children of %s", da->name, this->ref);
+                                       goto clone_error;
+                               }
+                       }
+
+                       if (dict_attr_acopy_children(dict, cloned, da) < 0) {
+                               fr_strerror_printf("Failed cloned attribute '%s' from children of %s", da->name, this->ref);
+                               goto clone_error;
+                       }
+
+                       if (dict_attr_child_add(this->parent, cloned) < 0) {
+                               fr_strerror_printf("Failed adding cloned attribute %s", da->name);
+                               talloc_free(cloned);
+                               goto clone_error;
+                       }
+
+                       if (dict_attr_add_to_namespace(dict, this->parent, cloned) < 0) goto clone_error;
+
+                       talloc_free(this->ref);
+                       next = this->next;
+               }
+       }
+
        TALLOC_FREE(ctx->fixup_pool);
 
        /*
diff --git a/src/tests/unit/protocols/radius/rfc8045.txt b/src/tests/unit/protocols/radius/rfc8045.txt
new file mode 100644 (file)
index 0000000..a9e3659
--- /dev/null
@@ -0,0 +1,32 @@
+proto radius
+proto-dictionary radius
+
+#
+#  RADIUS does not (yet) support nested TLVs.
+#
+#encode-pair IP-Port-Limit-Info = { IP-Port-Type = 1, IP-Port-Limit = 2 }
+#match fo
+
+encode-pair Extended-Attribute-1.IP-Port-Limit-Info.Type = 1
+match f1 09 05 01 06 00 00 00 01
+
+encode-pair Extended-Attribute-1.IP-Port-Limit-Info.Limit = 2
+match f1 09 05 02 06 00 00 00 02
+
+encode-pair Extended-Attribute-1.IP-Port-Limit-Info.Type = 1, Extended-Attribute-1.IP-Port-Limit-Info.Limit = 2
+match f1 0f 05 01 06 00 00 00 01 02 06 00 00 00 02
+
+decode-pair -
+match Extended-Attribute-1.IP-Port-Limit-Info.Type = 1, Extended-Attribute-1.IP-Port-Limit-Info.Limit = 2
+
+#
+#  And for the other attributes
+#
+encode-pair Extended-Attribute-1.IP-Port-Range.Type = 1, Extended-Attribute-1.IP-Port-Range.Limit = 2
+match f1 0f 06 01 06 00 00 00 01 02 06 00 00 00 02
+
+decode-pair -
+match Extended-Attribute-1.IP-Port-Range.Type = 1, Extended-Attribute-1.IP-Port-Range.Limit = 2
+
+count
+match 14
index 3aec28d8c5b5182b65e219724763b2904e5fb7ff..b32b43443b41f07184e287432c372cee97f4adc8 100644 (file)
@@ -25,6 +25,49 @@ match Vendor-Specific.WiMAX.Capability.Release = "1.0", Vendor-Specific.WiMAX.Ca
 encode-pair -
 match 1a 11 00 00 60 b5 01 0b 00 01 05 31 2e 30 02 03 01
 
+#
+#  Testing cloned TLVs
+#
+encode-pair Vendor-Specific.WiMAX.Packet-Flow-Descriptor.Classifier.Src-Spec.IP-Address = 127.0.0.1, Vendor-Specific.WiMAX.Packet-Flow-Descriptor.Classifier.Src-Spec.Port = 2112
+match 1a 17 00 00 60 b5 1c 11 00 0b 0e 05 0c 01 06 7f 00 00 01 04 04 08 40
+
+encode-pair Vendor-Specific.WiMAX.Packet-Flow-Descriptor.Classifier.Src-Spec.Port = 2112
+match 1a 11 00 00 60 b5 1c 0b 00 0b 08 05 06 04 04 08 40
+
+#
+#  Dst-Spec is copied from Src-Spec
+#
+encode-pair Vendor-Specific.WiMAX.Packet-Flow-Descriptor.Classifier.Dst-Spec.Port = 2112
+match 1a 11 00 00 60 b5 1c 0b 00 0b 08 06 06 04 04 08 40
+
+#
+#  Src-Spec is copied from the other Src-Spec, but with more attributes added
+#
+encode-pair Vendor-Specific.WiMAX.Packet-Flow-Descriptor-v2.Classifier.Src-Spec.MAC-Address = 00:01:02:03:04:05
+match 1a 15 00 00 60 b5 54 0f 00 09 0c 05 0a 08 08 00 01 02 03 04 05
+
+#
+# Dest-Spec is copied from the most recent Src-Spec
+#
+encode-pair Vendor-Specific.WiMAX.Packet-Flow-Descriptor-v2.Classifier.Dest-Spec.MAC-Address = 00:01:02:03:04:05
+match 1a 15 00 00 60 b5 54 0f 00 09 0c 06 0a 08 08 00 01 02 03 04 05
+
+#
+#  We copied enums, too
+# 
+encode-pair Vendor-Specific.WiMAX.Packet-Flow-Descriptor.Classifier.Src-Spec.Assigned = Src-Assigned
+match 1a 10 00 00 60 b5 1c 0a 00 0b 07 05 05 07 03 01
+
+#
+#  Enum is copied!
+#
+encode-pair Vendor-Specific.WiMAX.Packet-Flow-Descriptor.Classifier.Dst-Spec.Assigned = Src-Assigned
+match 1a 10 00 00 60 b5 1c 0a 00 0b 07 06 05 07 03 01
+
+
+#
+#  More complex tests
+#
 encode-pair Vendor-Specific.WiMAX.Packet-Flow-Descriptor-v2.Classifier.Direction = 1
 match 1a 0e 00 00 60 b5 54 08 00 09 05 04 03 01
 
@@ -189,4 +232,4 @@ match 1a 17 00 00 60 b5 1c 11 00 01 04 00 20 02 04 00 20 03 06 00 00 00 20 1b 06
 # data Vendor-Specific.WiMAX.Packet-Flow-Descriptor.Release = "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE", Vendor-Specific.WiMAX.Packet-Flow-Descriptor.Idle-Mode-Notification-Cap = Supported
 
 count
-match 94
+match 108