More tests
authorAlan T. DeKok <aland@freeradius.org>
Sun, 21 Nov 2010 09:59:24 +0000 (10:59 +0100)
committerAlan T. DeKok <aland@freeradius.org>
Sun, 21 Nov 2010 16:19:51 +0000 (17:19 +0100)
- zero length attributes
- more extended attrs
- "raw" attributes get a new format: Attr-{OID}
- vendor IDs of more than FR_MAX_VENDOR are supported
- attribute OIDs are printed (e.g. 24.1.2)
- raw attributes are parsed

src/include/libradius.h
src/lib/dict.c
src/lib/print.c
src/lib/valuepair.c
src/main/radattr.c
src/tests/errors.txt
src/tests/extended.txt
src/tests/lucent.txt [new file with mode: 0644]
src/tests/rfc.txt
src/tests/wimax.txt

index 73eb907..c3f9b69 100644 (file)
@@ -255,7 +255,8 @@ void                fr_print_string(const char *in, size_t inlen,
                                 char *out, size_t outlen);
 int            vp_prints_value(char *out, size_t outlen,
                                VALUE_PAIR *vp, int delimitst);
-const char     *vp_print_name(char *buffer, size_t bufsize, int attr, int vendor);
+const char     *vp_print_name(char *buffer, size_t bufsize,
+                              unsigned int attr, unsigned int vendor);
 int            vp_prints(char *out, size_t outlen, VALUE_PAIR *vp);
 void           vp_print(FILE *, VALUE_PAIR *);
 void           vp_printlist(FILE *, VALUE_PAIR *);
@@ -264,6 +265,8 @@ void                vp_printlist(FILE *, VALUE_PAIR *);
 /*
  *     Dictionary functions.
  */
+int            dict_str2oid(const char *ptr, unsigned int *pattr,
+                            int vendor, int tlv_depth);
 int            dict_addvendor(const char *name, unsigned int value);
 int            dict_addattr(const char *name, int attr, unsigned int vendor, int type, ATTR_FLAGS flags);
 int            dict_addvalue(const char *namestr, const char *attrstr, int value);
index 8b5e083..85a73bb 100644 (file)
@@ -119,7 +119,7 @@ const int fr_attr_shift[MAX_TLV_NEST + 1] = {
 };
 
 const int fr_attr_mask[MAX_TLV_NEST + 1] = {
-  0, 0xff, 0xff, 0x1f, 0x07
+  0xff, 0xff, 0xff, 0x1f, 0x07
 };
 
 
@@ -887,7 +887,7 @@ int dict_addvalue(const char *namestr, const char *attrstr, int value)
        return 0;
 }
 
-static int sscanf_i(const char *str, int *pvalue)
+static int sscanf_i(const char *str, unsigned int *pvalue)
 {
        int rcode = 0;
        int base = 10;
@@ -904,6 +904,8 @@ static int sscanf_i(const char *str, int *pvalue)
        while (*str) {
                const char *c;
 
+               if (*str == '.') break;
+
                c = memchr(tab, tolower((int) *str), base);
                if (!c) return 0;
 
@@ -917,10 +919,10 @@ static int sscanf_i(const char *str, int *pvalue)
 }
 
 
-static int sscanf_oid(char *ptr, int *pvalue, int vendor, int tlv_depth)
+int dict_str2oid(const char *ptr, unsigned int *pvalue, int vendor, int tlv_depth)
 {
-       char *p;
-       int value;
+       const char *p;
+       unsigned int value;
        DICT_ATTR *da;
 
        if (tlv_depth > fr_attr_max_tlv) {
@@ -928,24 +930,25 @@ static int sscanf_oid(char *ptr, int *pvalue, int vendor, int tlv_depth)
                return 0;
        }
 
-       if (vendor) {
-               da = dict_attrbyvalue(*pvalue, vendor);
-       } else {
-               da = dict_attrbyvalue(*pvalue, VENDORPEC_EXTENDED);
-       }
-       if (!da) {
-               fr_strerror_printf("Unknown parent attribute");
-               return 0;
-       }
+       if (*pvalue) {
+               if (vendor) {
+                       da = dict_attrbyvalue(*pvalue, vendor);
+               } else {
+                       da = dict_attrbyvalue(*pvalue, VENDORPEC_EXTENDED);
+               }
+               if (!da) {
+                       fr_strerror_printf("Unknown parent attribute");
+                       return 0;
+               }
        
-       if (!(da->flags.has_tlv || da->flags.extended || da->flags.extended_flags)) {
-               fr_strerror_printf("Parent attribute %s cannot have sub-tlvs",
-                                  da->name);
-               return 0;
+               if (!(da->flags.has_tlv || da->flags.extended || da->flags.extended_flags)) {
+                       fr_strerror_printf("Parent attribute %s cannot have sub-tlvs",
+                                          da->name);
+                       return 0;
+               }
        }
 
        p = strchr(ptr, '.');
-       if (p) *p = '\0';
 
        if (!sscanf_i(ptr, &value)) {
                fr_strerror_printf("Failed parsing attribute identifier %s",
@@ -956,8 +959,7 @@ static int sscanf_oid(char *ptr, int *pvalue, int vendor, int tlv_depth)
        *pvalue |= (value & fr_attr_mask[tlv_depth]) << fr_attr_shift[tlv_depth];
 
        if (p) {
-               *p = '.';
-               return sscanf_oid(p + 1, pvalue, vendor, tlv_depth + 1);
+               return dict_str2oid(p + 1, pvalue, vendor, tlv_depth + 1);
        }
 
        return 1;
@@ -972,9 +974,9 @@ static int process_attribute(const char* fn, const int line,
                             int tlv_depth, char **argv, int argc)
 {
        unsigned int    vendor = 0;
-       int             value;
+       unsigned int    value;
        int             type;
-       int             length = 0;
+       unsigned int    length = 0;
        ATTR_FLAGS      flags;
        char            *p;
 
@@ -1027,7 +1029,7 @@ static int process_attribute(const char* fn, const int line,
                        return -1;
                }
 
-               if (!sscanf_oid(p + 1, &value, block_vendor, tlv_depth + 1)) {
+               if (!dict_str2oid(p + 1, &value, block_vendor, tlv_depth + 1)) {
                        char buffer[256];
 
                        strlcpy(buffer, fr_strerror(), sizeof(buffer));
@@ -1282,7 +1284,7 @@ static int process_attribute(const char* fn, const int line,
 static int process_value(const char* fn, const int line, char **argv,
                         int argc)
 {
-       int     value;
+       unsigned int    value;
 
        if (argc != 3) {
                fr_strerror_printf("dict_init: %s[%d]: invalid VALUE line",
index c01117e..679a9c6 100644 (file)
@@ -398,14 +398,14 @@ static size_t vp_print_attr_oid(char *buffer, size_t size, unsigned int attr,
 
        switch (dv_type) {
        case 4:
-               return snprintf(buffer, size, "Attr-%u", attr);
+               return snprintf(buffer, size, "%u", attr);
 
        case 2:
-               return snprintf(buffer, size, "Attr-%u", attr & 0xffff);
+               return snprintf(buffer, size, "%u", attr & 0xffff);
 
        default:
        case 1:
-               len = snprintf(buffer, size, "Attr-%u", attr & 0xff);
+               len = snprintf(buffer, size, "%u", attr & 0xff);
                break;
        }
 
@@ -429,36 +429,42 @@ static size_t vp_print_attr_oid(char *buffer, size_t size, unsigned int attr,
        return outlen;
 }
 
-const char *vp_print_name(char *buffer, size_t bufsize, int attr, int vendor)
+const char *vp_print_name(char *buffer, size_t bufsize,
+                         unsigned int attr, unsigned int vendor)
 {
+       char *p = buffer;
        int dv_type = 1;
        size_t len = 0;
 
        if (!buffer) return NULL;
+       
+       len = snprintf(p, bufsize, "Attr-");
+       p += len;
+       bufsize -= len;
 
        if (vendor) {
-               DICT_VENDOR *v;
-               
-               v = dict_vendorbyvalue(vendor);
-               if (v) {
-                       snprintf(buffer, bufsize, "%s-", v->name);
-                       dv_type = v->type;
-               } else {
-                       snprintf(buffer, bufsize, "Vendor-%u-", vendor);
+               DICT_VENDOR *dv;
+
+               if (vendor >= FR_MAX_VENDOR) {
+                       len = snprintf(p, bufsize, "%u.",
+                                      vendor / FR_MAX_VENDOR);
+                       p += len;
+                       bufsize -= len;
+                       vendor &= (FR_MAX_VENDOR) - 1;
                }
 
-               len = strlen(buffer);
-               if (len == bufsize) {
-                       return NULL;
+               dv = dict_vendorbyvalue(vendor);
+               if (dv) {
+                       dv_type = dv->type;
                }
-       }
+               len = snprintf(p, bufsize, "26.%u.", vendor);
 
-       len = vp_print_attr_oid(buffer + len, bufsize - len, attr, dv_type);
-       len += strlen(buffer + len);
-       if (len == bufsize) {
-               return NULL;
+               p += len;
+               bufsize -= len;
        }
 
+       len = vp_print_attr_oid(p, bufsize , attr, dv_type);
+
        return buffer;
 }
 
index 2258957..e7d158b 100644 (file)
@@ -45,18 +45,18 @@ static const char *months[] = {
  *     code accesses vp->name directly, rather than through an
  *     accessor function.
  *
- *     The name padding only has to large enough for:
+ *     The name padding has to be large enough for:
  *
- *             Vendor-65535-Attr-65535
+ *             Attr-{3}.{8}.{3}.{3}.{3}.{3}
  *
- *     i.e. 23 characters, plus a zero.  We add another 8 bytes for
+ *     i.e. 28 characters, plus a zero.  We add some more bytes for
  *     padding, because the VALUE_PAIR structure may be un-aligned.
  *
  *     The result is that for the normal case, the server uses a less
  *     memory (36 bytes * number of VALUE_PAIRs).
  */
 #define FR_VP_NAME_PAD (32)
-#define FR_VP_NAME_LEN (24)
+#define FR_VP_NAME_LEN (30)
 
 VALUE_PAIR *pairalloc(DICT_ATTR *da)
 {
@@ -1292,8 +1292,10 @@ VALUE_PAIR *pairparsevalue(VALUE_PAIR *vp, const char *value)
  *     where the attribute name is in the form:
  *
  *     Attr-%d
+ *     Attr-%d.%d.%d...
  *     Vendor-%d-Attr-%d
  *     VendorName-Attr-%d
+ *
  */
 static VALUE_PAIR *pairmake_any(const char *attribute, const char *value,
                                int operator)
@@ -1372,9 +1374,9 @@ static VALUE_PAIR *pairmake_any(const char *attribute, const char *value,
        attr = strtol(p + 5, &q, 10);
 
        /*
-        *      Invalid, or trailing text after number.
+        *      Invalid attribute.
         */
-       if ((attr == 0) || *q) {
+       if (attr == 0) {
                fr_strerror_printf("Invalid value in attribute name \"%s\"", attribute);
                return NULL;
        }
@@ -1408,6 +1410,59 @@ static VALUE_PAIR *pairmake_any(const char *attribute, const char *value,
                                fr_strerror_printf("Internal sanity check failed");
                                return NULL;
                }
+
+       } else {                /* RADIUS attrs can be 1..255 */
+               if (attr > 255) goto attr_error;
+       }
+
+       /*
+        *      Look for OIDs.
+        */
+       if (*q == '.') {
+               int my_attr;
+               DICT_ATTR *da;
+
+               if (vendor != 0) {
+                       fr_strerror_printf("Unknown VSAs cannot use OIDs");
+                       return NULL;
+                       
+               }
+
+               da = dict_attrbyvalue(attr, vendor);
+               if (!da) {
+                       fr_strerror_printf("Cannot parse attributes without dictionaries");
+                       return NULL;
+               }               
+               
+               if ((attr != PW_VENDOR_SPECIFIC) &&
+                   !(da->flags.extended || da->flags.extended_flags)) {
+                       fr_strerror_printf("Standard attributes cannot use attribute OIDs");
+                       return NULL;
+               }
+
+               p = q + 1;
+               if ((attr == PW_VENDOR_SPECIFIC) || da->flags.evs) {
+                       vendor = strtol(p, &q, 10);
+                       if ((vendor == 0) || (vendor > FR_MAX_VENDOR)) {
+                               fr_strerror_printf("Invalid vendor");
+                               return NULL;
+                       }
+
+                       if (*q != '.') {
+                               fr_strerror_printf("Invalid text after vendor");
+                               return NULL;
+                       }
+
+                       p = q + 1;
+               }
+
+               my_attr = 0;
+               if (!dict_str2oid(p, &my_attr, vendor, 0)) {
+                       return NULL;
+               }
+
+               if (da->flags.evs) vendor |= attr * FR_MAX_VENDOR;
+               attr = my_attr;
        }
 
        /*
@@ -1622,12 +1677,12 @@ VALUE_PAIR *pairmake(const char *attribute, const char *value, int operator)
 
 
 /*
- *     [a-zA-Z0-9_-:]+
+ *     [a-zA-Z0-9_-:.]+
  */
 static const int valid_attr_name[256] = {
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
        0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
index d4affb4..31b06c2 100644 (file)
@@ -615,7 +615,7 @@ static void process_file(const char *filename)
                                        fprintf(stderr, "Internal sanity check failed at %d\n", __LINE__);
                                        exit(1);
                                }
-                               
+
                                *tail = vp;
                                while (vp) {
                                        tail = &(vp->next);
@@ -642,8 +642,11 @@ static void process_file(const char *filename)
                                }
                                
                                pairfree(&head);
-                       } else {
+                       } else if (my_len < 0) {
                                strcpy(output, fr_strerror());
+
+                       } else { /* zero-length attribute */
+                               *output = '\0';
                        }
                        continue;
                }
index 4f07b99..cc432e7 100644 (file)
@@ -1,3 +1,6 @@
+#
+#  Bad attributes
+#
 decode 01 04 00
 data rad_attr2vp: Insufficient data
 
index 773cdc0..43f950b 100644 (file)
@@ -53,3 +53,28 @@ data f5 ff 04 80 aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa
 
 raw 245.26.1.6 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccc13456789
 data f5 ff 1a 80 00 00 00 01 06 aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ab bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb f5 17 1a 00 bb bb bb bb bb cc cc cc cc cc cc cc cc cc cc 13 45 67 89
+
+# Same as above, but the first attribute doesn't have
+# the "continuation" bit set.
+decode f5 ff 1a 00 00 00 00 01 06 aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ab bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb f5 17 1a 00 bb bb bb bb bb cc cc cc cc cc cc cc cc cc cc 13 45 67 89
+data Attr-245.26.1.6 = 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, Attr-245 = 0x1a00bbbbbbbbbbcccccccccccccccccccc13456789
+
+# again, but the second one attr is not an extended attr
+decode f5 ff 1a 80 00 00 00 01 06 aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa ab bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb bb 01 05 62 6f 62
+data Attr-245 = 0x1a800000000106aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, User-Name = "bob"
+
+# No data means that the attribute is ignored.
+decode f5 04 01 00
+data 
+
+decode f5 09 1a 00 00 00 00 01 06
+data 
+
+decode f5 0a 1a 00 00 00 00 01 06 01
+data Attr-245.26.1.6 = 0x01
+
+decode f5 09 1a 80 00 00 00 01 06 f5 05 1a 80 01
+data Attr-245.26.1.6 = 0x01
+
+decode f5 0a 1a 80 00 00 00 01 06 01 f5 05 1a 80 01
+data Attr-245.26.1.6 = 0x0101
diff --git a/src/tests/lucent.txt b/src/tests/lucent.txt
new file mode 100644 (file)
index 0000000..2bdd515
--- /dev/null
@@ -0,0 +1,5 @@
+encode Lucent-Max-Shared-Users = 1
+data 1a 0d 00 00 12 ee 00 02 07 00 00 00 01
+
+decode -
+data Lucent-Max-Shared-Users = 1
index e2a9fae..0866967 100644 (file)
@@ -83,8 +83,25 @@ data User-Name = "bob"
 decode 01 05 62 6f 62
 data User-Name = "bob"
 
-$INCLUDE errors.txt
+#
+#  The Type/Length is OK, but the attribute data is of the wrong size.
+#
+decode 04 04 ab cd
+data Attr-4 = 0xabcd
+
+#  Zero-length attributes
+decode 01 02
+data 
 
+# except for CUI.  Thank you, WiMAX!
+decode 59 02
+data Chargeable-User-Identity = ""
+
+# Hah! Thought you had it figured out, didn't you?
+encode -
+data 59 02
+
+$INCLUDE errors.txt
 $INCLUDE extended.txt
 $INCLUDE lucent.txt
 $INCLUDE wimax.txt
index 8b7f012..af3cadf 100644 (file)
@@ -18,3 +18,91 @@ data 1a 11 00 00 60 b5 01 0b 00 01 05 31 2e 30 02 03 01
 
 decode -
 data WiMAX-Release = "1.0", WiMAX-Accounting-Capabilities = IP-Session-Based
+
+encode -
+data 1a 11 00 00 60 b5 01 0b 00 01 05 31 2e 30 02 03 01
+
+encode WiMAX-PFDv2-Classifier-Direction = 1
+data 1a 0e 00 00 60 b5 54 08 00 09 05 04 03 01
+
+encode WiMAX-PFDv2-Classifier-Direction = 1, WiMAX-PFDv2-Src-Port = 6809
+data 1a 14 00 00 60 b5 54 0e 00 09 0b 04 03 01 05 06 04 04 1a 99
+
+decode -
+data WiMAX-PFDv2-Classifier-Direction = 1, WiMAX-PFDv2-Src-Port = 6809
+
+# 26.24757.89.9.4 has the correct length.
+# 26.24757.89.9.5 has the correct length.
+# 26.24757.89.9.5.4 has the wrong length.
+decode 1a 14 00 00 60 b5 54 0e 00 09 0b 04 03 01 05 06 04 05 1a 99
+data WiMAX-PFDv2-Classifier-Direction = 1, Attr-26.24757.84.9.5 = 0x04051a99
+
+# The 26.24757.1 has the wrong length
+decode 1a 11 00 00 60 b5 01 0a 00 01 05 31 2e 30 02 03 01
+data Attr-26 = 0x000060b5010a000105312e30020301
+
+encode -
+data 1a 11 00 00 60 b5 01 0a 00 01 05 31 2e 30 02 03 01
+
+decode 1a 11 00 00 60 b5 01 0c 00 01 05 31 2e 30 02 03 01
+data Attr-26 = 0x000060b5010c000105312e30020301
+
+encode -
+data 1a 11 00 00 60 b5 01 0c 00 01 05 31 2e 30 02 03 01
+
+# 26.24757.1.1 has the wrong length
+decode 1a 11 00 00 60 b5 01 0b 00 01 04 31 2e 30 02 03 01
+data Attr-26.24757.1 = 0x0104312e30020301
+
+decode 1a 11 00 00 60 b5 01 0b 00 01 06 31 2e 30 02 03 01
+data Attr-26.24757.1 = 0x0106312e30020301
+
+encode -
+data 1a 11 00 00 60 b5 01 0b 00 01 06 31 2e 30 02 03 01
+
+# 26.24757.1.2 has the wrong length
+decode 1a 11 00 00 60 b5 01 0b 00 01 05 31 2e 30 02 02 01
+data Attr-26.24757.1 = 0x0105312e30020201
+
+encode -
+data 1a 11 00 00 60 b5 01 0b 00 01 05 31 2e 30 02 02 01
+
+# 26.24757.1.1 has the correct length
+# 26.24757.1.2 has the wrong length
+# This means that 26.24757.1 is malformed, and we create a raw attribute.
+decode 1a 11 00 00 60 b5 01 0b 00 01 05 31 2e 30 02 04 01
+data Attr-26.24757.1 = 0x0105312e30020401
+
+encode -
+data 1a 11 00 00 60 b5 01 0b 00 01 05 31 2e 30 02 04 01
+
+encode WiMAX-PFDv2-Eth-Priority-Range-Low = 55
+data 1a 12 00 00 60 b5 54 0c 00 09 09 09 07 03 05 01 03 37
+
+encode WiMAX-PFDv2-Eth-Priority-Range-Low = 55, WiMAX-PFDv2-Eth-Priority-Range-High = 84
+data 1a 15 00 00 60 b5 54 0f 00 09 0c 09 0a 03 08 01 03 37 02 03 54
+
+decode -
+data WiMAX-PFDv2-Eth-Priority-Range-Low = 55, WiMAX-PFDv2-Eth-Priority-Range-High = 84
+
+#  A less efficient encoding of the above data
+decode 1a 17 00 00 60 b5 54 11 00 09 0e 09 0c 03 05 01 03 37 03 05 02 03 54
+data WiMAX-PFDv2-Eth-Priority-Range-Low = 55, WiMAX-PFDv2-Eth-Priority-Range-High = 84
+
+# 26.24757.84.9.9.3.1 has the wrong length
+decode 1a 15 00 00 60 b5 54 0f 00 09 0c 09 0a 03 08 01 04 37 02 03 54
+data Attr-26.24757.84.9.9.3 = 0x010437020354
+
+# 26.24757.84.9.9.3.2 has the wrong length
+decode 1a 15 00 00 60 b5 54 0f 00 09 0c 09 0a 03 08 01 03 37 02 04 54
+data Attr-26.24757.84.9.9.3 = 0x010337020454
+
+# 26.24757.84.9.9.3.2 has the wrong length
+# This means that the SECOND 26.24757.84.9.9.3 is malformed.
+decode 1a 17 00 00 60 b5 54 11 00 09 0e 09 0c 03 05 01 03 37 03 05 02 04 54
+data WiMAX-PFDv2-Eth-Priority-Range-Low = 55, Attr-26.24757.84.9.9.3 = 0x020454
+
+# 26.24757.84.9.9.3.1 has the wrong length
+# This means that 26.24757.84.9.9.3 is malformed.
+decode 1a 17 00 00 60 b5 54 11 00 09 0e 09 0c 03 05 01 02 37 03 05 02 03 54
+data Attr-26.24757.84.9.9.3 = 0x010237, WiMAX-PFDv2-Eth-Priority-Range-High = 84