]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Fix several issues in Flowspec handling master oz-test
authorOndrej Zajicek <santiago@crfreenet.org>
Fri, 5 Jun 2026 13:48:46 +0000 (15:48 +0200)
committerOndrej Zajicek <santiago@crfreenet.org>
Fri, 5 Jun 2026 15:31:04 +0000 (17:31 +0200)
The patch fixes several issues in Flowspec handling, namely:

 - Out-of-bounds read during flowspec validation
 - Rejection of NLRI for anomalies that MUST be ignored
 - Incorrect check of operand lengths
 - Broken label component construction
 - Broken formatting of IPv6 prefixes with specific offsets

The first issue was reported by multiple people in recent time.

The second issue found by Bronson Yen of Calif.io in collaboration
with Claude and Anthropic Research.

conf/flowspec.Y
doc/bird.sgml
lib/flowspec.c
lib/flowspec.h
lib/flowspec_test.c
proto/bgp/packets.c

index de496ecbf255c3d7bd9e52baaa61246f629f863e..cdb578ac4fe15f6a4499812146eb9b67c4c0ebdf 100644 (file)
@@ -73,11 +73,11 @@ flow_srcdst:
 
 flow_num_opts:
    flow_num_op expr {
 
 flow_num_opts:
    flow_num_op expr {
-     flow_check_cf_value_length(this_flow, $2);
+     flow_check_cf_numeric_arg(this_flow, $2);
      flow_builder_add_op_val(this_flow, $1, $2);
    }
  | flow_num_opts flow_logic_op flow_num_op expr {
      flow_builder_add_op_val(this_flow, $1, $2);
    }
  | flow_num_opts flow_logic_op flow_num_op expr {
-     flow_check_cf_value_length(this_flow, $4);
+     flow_check_cf_numeric_arg(this_flow, $4);
      flow_builder_add_op_val(this_flow, $2 | $3, $4);
    }
  | flow_num_opt_ext
      flow_builder_add_op_val(this_flow, $2 | $3, $4);
    }
  | flow_num_opt_ext
@@ -86,12 +86,12 @@ flow_num_opts:
 
 flow_num_opt_ext_expr:
    expr {
 
 flow_num_opt_ext_expr:
    expr {
-     flow_check_cf_value_length(this_flow, $1);
+     flow_check_cf_numeric_arg(this_flow, $1);
      flow_builder_add_op_val(this_flow, FLOW_OP_EQ, $1);
    }
  | expr DDOT expr {
      flow_builder_add_op_val(this_flow, FLOW_OP_EQ, $1);
    }
  | expr DDOT expr {
-     flow_check_cf_value_length(this_flow, $1);
-     flow_check_cf_value_length(this_flow, $3);
+     flow_check_cf_numeric_arg(this_flow, $1);
+     flow_check_cf_numeric_arg(this_flow, $3);
      flow_builder_add_op_val(this_flow, FLOW_OP_GEQ, $1);
      flow_builder_add_op_val(this_flow, FLOW_OP_AND | FLOW_OP_LEQ, $3);
    }
      flow_builder_add_op_val(this_flow, FLOW_OP_GEQ, $1);
      flow_builder_add_op_val(this_flow, FLOW_OP_AND | FLOW_OP_LEQ, $3);
    }
@@ -104,15 +104,15 @@ flow_num_opt_ext:
 
 flow_bmk_opts:
    flow_neg expr '/' expr {
 
 flow_bmk_opts:
    flow_neg expr '/' expr {
-     flow_check_cf_bmk_values(this_flow, $1, $2, $4);
+     flow_check_cf_bitmask_arg(this_flow, $1, $2, $4);
      flow_builder_add_val_mask(this_flow, $1, $2, $4);
    }
  | flow_bmk_opts flow_logic_op flow_neg expr '/' expr {
      flow_builder_add_val_mask(this_flow, $1, $2, $4);
    }
  | flow_bmk_opts flow_logic_op flow_neg expr '/' expr {
-     flow_check_cf_bmk_values(this_flow, $3, $4, $6);
+     flow_check_cf_bitmask_arg(this_flow, $3, $4, $6);
      flow_builder_add_val_mask(this_flow, $2 | $3, $4, $6);
    }
  | flow_bmk_opts ',' flow_neg expr '/' expr {
      flow_builder_add_val_mask(this_flow, $2 | $3, $4, $6);
    }
  | flow_bmk_opts ',' flow_neg expr '/' expr {
-     flow_check_cf_bmk_values(this_flow, $3, $4, $6);
+     flow_check_cf_bitmask_arg(this_flow, $3, $4, $6);
      flow_builder_add_val_mask(this_flow, 0x40 | $3, $4, $6); /* AND */
    }
  ;
      flow_builder_add_val_mask(this_flow, 0x40 | $3, $4, $6); /* AND */
    }
  ;
index 3ba6fe2fe3ae670f27e7ce6d2d09e56bf9cd0fae..51de0d97f89631035edadebcfd3fa4d52ce1fee3 100644 (file)
@@ -7100,7 +7100,6 @@ Note that for negated matches, value must be either zero or equal to bitmask
 <p><descrip>
        <tag><label id="flow-dst">dst <m/inet4/</tag>
        Set a matching destination prefix (e.g. <cf>dst 192.168.0.0/16</cf>).
 <p><descrip>
        <tag><label id="flow-dst">dst <m/inet4/</tag>
        Set a matching destination prefix (e.g. <cf>dst 192.168.0.0/16</cf>).
-       Only this option is mandatory in IPv4 Flowspec.
 
        <tag><label id="flow-src">src <m/inet4/</tag>
        Set a matching source prefix (e.g. <cf>src 10.0.0.0/8</cf>).
 
        <tag><label id="flow-src">src <m/inet4/</tag>
        Set a matching source prefix (e.g. <cf>src 10.0.0.0/8</cf>).
@@ -7168,7 +7167,6 @@ protocol static {
        but with prefix <cf/offset/ <m/number/ too (e.g.
        <cf>::1234:5678:9800:0000/101 offset 64</cf>). Offset means to don't
        care of <m/number/ first bits.
        but with prefix <cf/offset/ <m/number/ too (e.g.
        <cf>::1234:5678:9800:0000/101 offset 64</cf>). Offset means to don't
        care of <m/number/ first bits.
-       <item>IPv6 Flowspec hasn't mandatory any flowspec component.
        <item>In IPv6 packets, there is a matching the last next header value
        for a matching IP protocol number (e.g. <cf>next header 6</cf>).
        <item>It is not possible to set <cf>dont_fragment</cf> as a type of
        <item>In IPv6 packets, there is a matching the last next header value
        for a matching IP protocol number (e.g. <cf>next header 6</cf>).
        <item>It is not possible to set <cf>dont_fragment</cf> as a type of
index eaaffb148a26123ebe39ea2a43f2c06dee277bca..c534054faa3de5b254224cce1029df3b66d2c1b4 100644 (file)
@@ -170,16 +170,8 @@ flow_next_part(const byte *pos, const byte *end, int ipv6)
   case FLOW_TYPE_SRC_PREFIX:
   {
     uint pxlen = *pos++;
   case FLOW_TYPE_SRC_PREFIX:
   {
     uint pxlen = *pos++;
-    uint bytes = BYTES(pxlen);
-    if (ipv6)
-    {
-      uint offset = *pos++ / 8;
-      pos += bytes - offset;
-    }
-    else
-    {
-      pos += bytes;
-    }
+    uint offset = ipv6 ? *pos++ : 0;
+    pos += BYTES(pxlen - offset);
     break;
   }
 
     break;
   }
 
@@ -332,11 +324,11 @@ static const char* flow_validated_state_str_[] = {
   [FLOW_ST_EXCEED_MAX_PREFIX_OFFSET]   = "Exceed maximal prefix offset",
   [FLOW_ST_EXCEED_MAX_VALUE_LENGTH]    = "Exceed maximal value length",
   [FLOW_ST_BAD_TYPE_ORDER]             = "Bad component order",
   [FLOW_ST_EXCEED_MAX_PREFIX_OFFSET]   = "Exceed maximal prefix offset",
   [FLOW_ST_EXCEED_MAX_VALUE_LENGTH]    = "Exceed maximal value length",
   [FLOW_ST_BAD_TYPE_ORDER]             = "Bad component order",
-  [FLOW_ST_AND_BIT_SHOULD_BE_UNSET]    = "The AND-bit should be unset",
-  [FLOW_ST_ZERO_BIT_SHOULD_BE_UNSED]   = "The Zero-bit should be unset",
-  [FLOW_ST_DEST_PREFIX_REQUIRED]       = "Destination prefix is missing",
-  [FLOW_ST_INVALID_TCP_FLAGS]          = "TCP flags exceeding 0xfff",
-  [FLOW_ST_CANNOT_USE_DONT_FRAGMENT]    = "Cannot use Don't fragment flag in IPv6 flow"
+  [FLOW_ST_NONZERO_PADDING]            = "Nonzero prefix padding",
+  [FLOW_ST_FIRST_AND_BIT_SET]          = "The first AND-bit is set",
+  [FLOW_ST_ZERO_BIT_SET]               = "Zero-bit is set",
+  [FLOW_ST_INVALID_TCP_FLAGS]          = "Invalid TCP flags bitmask operand",
+  [FLOW_ST_INVALID_FRAGMENT]                   = "Invalid fragment bitmask operand",
 };
 
 /**
 };
 
 /**
@@ -351,45 +343,28 @@ flow_validated_state_str(enum flow_validated_state code)
   return flow_validated_state_str_[code];
 }
 
   return flow_validated_state_str_[code];
 }
 
-static const u8 flow4_max_value_length[] = {
-  [FLOW_TYPE_DST_PREFIX]       = 0,
-  [FLOW_TYPE_SRC_PREFIX]       = 0,
-  [FLOW_TYPE_IP_PROTOCOL]      = 1,
-  [FLOW_TYPE_PORT]             = 2,
-  [FLOW_TYPE_DST_PORT]         = 2,
-  [FLOW_TYPE_SRC_PORT]         = 2,
-  [FLOW_TYPE_ICMP_TYPE]                = 1,
-  [FLOW_TYPE_ICMP_CODE]                = 1,
+/* Maximum length of value fields, as mandated by RFC 8955 */
+static const u8 flow_max_value_length[FLOW_TYPE_MAX] = {
   [FLOW_TYPE_TCP_FLAGS]                = 2,
   [FLOW_TYPE_TCP_FLAGS]                = 2,
-  [FLOW_TYPE_PACKET_LENGTH]    = 2,
   [FLOW_TYPE_DSCP]             = 1,
   [FLOW_TYPE_DSCP]             = 1,
-  [FLOW_TYPE_FRAGMENT]         = 1     /* XXX */
+  [FLOW_TYPE_FRAGMENT]         = 1,
 };
 
 };
 
-static const u8 flow6_max_value_length[] = {
-  [FLOW_TYPE_DST_PREFIX]       = 0,
-  [FLOW_TYPE_SRC_PREFIX]       = 0,
-  [FLOW_TYPE_NEXT_HEADER]      = 1,
+/* Maximum valid numeric values (in bytes), semantically */
+static const u8 flow_max_valid_value[FLOW_TYPE_MAX] = {
+  [FLOW_TYPE_IP_PROTOCOL]      = 1,
   [FLOW_TYPE_PORT]             = 2,
   [FLOW_TYPE_DST_PORT]         = 2,
   [FLOW_TYPE_SRC_PORT]         = 2,
   [FLOW_TYPE_ICMP_TYPE]                = 1,
   [FLOW_TYPE_ICMP_CODE]                = 1,
   [FLOW_TYPE_PORT]             = 2,
   [FLOW_TYPE_DST_PORT]         = 2,
   [FLOW_TYPE_SRC_PORT]         = 2,
   [FLOW_TYPE_ICMP_TYPE]                = 1,
   [FLOW_TYPE_ICMP_CODE]                = 1,
-  [FLOW_TYPE_TCP_FLAGS]                = 2,
   [FLOW_TYPE_PACKET_LENGTH]    = 2,
   [FLOW_TYPE_DSCP]             = 1,
   [FLOW_TYPE_PACKET_LENGTH]    = 2,
   [FLOW_TYPE_DSCP]             = 1,
-  [FLOW_TYPE_FRAGMENT]         = 1,    /* XXX */
-  [FLOW_TYPE_LABEL]            = 4
+  [FLOW_TYPE_LABEL]            = 4,
 };
 
 };
 
-static u8
-flow_max_value_length(enum flow_type type, int ipv6)
-{
-  return ipv6 ? flow6_max_value_length[type] : flow4_max_value_length[type];
-}
-
 /**
 /**
- * flow_check_cf_bmk_values - check value/bitmask part of flowspec component
+ * flow_check_cf_bitmask_arg - check value/bitmask part of flowspec component
  * @fb: flow builder instance
  * @neg: negation operand
  * @val: value from value/mask pair
  * @fb: flow builder instance
  * @neg: negation operand
  * @val: value from value/mask pair
@@ -400,38 +375,37 @@ flow_max_value_length(enum flow_type type, int ipv6)
  * to failing of validation.
  */
 void
  * to failing of validation.
  */
 void
-flow_check_cf_bmk_values(struct flow_builder *fb, u8 neg, u32 val, u32 mask)
+flow_check_cf_bitmask_arg(struct flow_builder *fb, u8 neg, u32 val, u32 mask)
 {
 {
-  flow_check_cf_value_length(fb, val);
-  flow_check_cf_value_length(fb, mask);
-
   if (neg && !(val == 0 || val == mask))
     cf_error("For negation, value must be zero or bitmask");
 
   if (neg && !(val == 0 || val == mask))
     cf_error("For negation, value must be zero or bitmask");
 
-  if ((fb->this_type == FLOW_TYPE_TCP_FLAGS) && (mask & 0xf000))
+  if ((fb->this_type == FLOW_TYPE_TCP_FLAGS) && (mask & ~0xfff))
     cf_error("Invalid mask 0x%x, must not exceed 0xfff", mask);
 
     cf_error("Invalid mask 0x%x, must not exceed 0xfff", mask);
 
-  if ((fb->this_type == FLOW_TYPE_FRAGMENT) && fb->ipv6 && (mask & 0x01))
-    cf_error("Invalid mask 0x%x, bit 0 must be 0", mask);
+  u32 valid = fb->ipv6 ? 0x0e : 0x0f;
+  if ((fb->this_type == FLOW_TYPE_FRAGMENT) && (mask & ~valid))
+    cf_error("Invalid mask 0x%x, must not exceed 0x%x", mask, valid);
 
   if (val & ~mask)
     cf_error("Value 0x%x outside bitmask 0x%x", val, mask);
 }
 
 /**
 
   if (val & ~mask)
     cf_error("Value 0x%x outside bitmask 0x%x", val, mask);
 }
 
 /**
- * flow_check_cf_value_length - check value by flowspec component type
+ * flow_check_cf_numeric_arg - check numeric argument of flowspec component
  * @fb: flow builder instance
  * @val: value
  *
  * @fb: flow builder instance
  * @val: value
  *
- * This function checks if the value is in range of component's type support.
- * If some problem will appear, the function calls cf_error() function with
- * a textual description of reason to failing of validation.
+ * This function checks the value of numeric argument to see whether it is in
+ * the range of component's type.  If some problem will appear, the function
+ * calls cf_error() function with a textual description of reason to failing of
+ * validation.
  */
 void
  */
 void
-flow_check_cf_value_length(struct flow_builder *fb, u32 val)
+flow_check_cf_numeric_arg(struct flow_builder *fb, uint val)
 {
   enum flow_type t = fb->this_type;
 {
   enum flow_type t = fb->this_type;
-  u8 max = flow_max_value_length(t, fb->ipv6);
+  uint max = flow_max_valid_value[t];
 
   if (t == FLOW_TYPE_DSCP && val > 0x3f)
     cf_error("%s value %u out of range (0-63)", flow_type_str(t, fb->ipv6), val);
 
   if (t == FLOW_TYPE_DSCP && val > 0x3f)
     cf_error("%s value %u out of range (0-63)", flow_type_str(t, fb->ipv6), val);
@@ -443,18 +417,27 @@ flow_check_cf_value_length(struct flow_builder *fb, u32 val)
     cf_error("%s value %u out of range (0-65535)", flow_type_str(t, fb->ipv6), val);
 }
 
     cf_error("%s value %u out of range (0-65535)", flow_type_str(t, fb->ipv6), val);
 }
 
+/* Bitmask of padding bits in last byte of prefix */
+static inline u8
+flow_padding(uint n)
+{
+  ASSUME(n % 8 != 0);
+  return (1 << (8 - n % 8)) - 1;
+}
+
 static enum flow_validated_state
 static enum flow_validated_state
-flow_validate(const byte *nlri, uint len, int ipv6)
+flow_decode(byte *nlri, uint length, bool ipv6)
 {
   enum flow_type type = 0;
 {
   enum flow_type type = 0;
-  const byte *pos = nlri;
-  const byte *end = nlri + len;
+  byte *pos = nlri;
+  byte *end = nlri + length;
 
   while (pos < end)
   {
     /* Check increasing type ordering */
     if (*pos <= type)
       return FLOW_ST_BAD_TYPE_ORDER;
 
   while (pos < end)
   {
     /* Check increasing type ordering */
     if (*pos <= type)
       return FLOW_ST_BAD_TYPE_ORDER;
+
     type = *pos++;
 
     switch (type)
     type = *pos++;
 
     switch (type)
@@ -462,19 +445,42 @@ flow_validate(const byte *nlri, uint len, int ipv6)
     case FLOW_TYPE_DST_PREFIX:
     case FLOW_TYPE_SRC_PREFIX:
     {
     case FLOW_TYPE_DST_PREFIX:
     case FLOW_TYPE_SRC_PREFIX:
     {
-      uint pxlen = *pos++;
-      if (pxlen > (ipv6 ? IP6_MAX_PREFIX_LENGTH : IP4_MAX_PREFIX_LENGTH))
-       return FLOW_ST_EXCEED_MAX_PREFIX_LENGTH;
+      uint pxlen, offset = 0;
+
+      if (!ipv6)
+      {
+       if (pos + 1 > end)
+         return FLOW_ST_NOT_COMPLETE;
 
 
-      uint bytes = BYTES(pxlen);
-      if (ipv6)
+       pxlen = *pos++;
+
+       if (pxlen > IP4_MAX_PREFIX_LENGTH)
+         return FLOW_ST_EXCEED_MAX_PREFIX_LENGTH;
+      }
+      else
       {
       {
-        uint pxoffset = *pos++;
-        if (pxoffset > IP6_MAX_PREFIX_LENGTH || pxoffset > pxlen)
-          return FLOW_ST_EXCEED_MAX_PREFIX_OFFSET;
-        bytes = BYTES(pxlen - pxoffset);
+       if (pos + 2 > end)
+         return FLOW_ST_NOT_COMPLETE;
+
+       pxlen = *pos++;
+       offset = *pos++;
+
+       if (pxlen > IP6_MAX_PREFIX_LENGTH)
+         return FLOW_ST_EXCEED_MAX_PREFIX_LENGTH;
+
+       if (offset > pxlen)
+         return FLOW_ST_EXCEED_MAX_PREFIX_OFFSET;
       }
       }
-      pos += bytes;
+
+      uint bits = pxlen - offset;
+      pos += BYTES(bits);
+
+      if (pos > end)
+       return FLOW_ST_NOT_COMPLETE;
+
+      /* Padding bits in the last byte MUST be 0 */
+      if (bits % 8)
+       pos[-1] &= ~flow_padding(bits);
 
       break;
     }
 
       break;
     }
@@ -483,6 +489,7 @@ flow_validate(const byte *nlri, uint len, int ipv6)
       if (!ipv6)
        return FLOW_ST_UNKNOWN_COMPONENT;
       /* fall through */
       if (!ipv6)
        return FLOW_ST_UNKNOWN_COMPONENT;
       /* fall through */
+
     case FLOW_TYPE_IP_PROTOCOL: /* == FLOW_TYPE_NEXT_HEADER */
     case FLOW_TYPE_PORT:
     case FLOW_TYPE_DST_PORT:
     case FLOW_TYPE_IP_PROTOCOL: /* == FLOW_TYPE_NEXT_HEADER */
     case FLOW_TYPE_PORT:
     case FLOW_TYPE_DST_PORT:
@@ -494,70 +501,236 @@ flow_validate(const byte *nlri, uint len, int ipv6)
     case FLOW_TYPE_DSCP:
     case FLOW_TYPE_FRAGMENT:
     {
     case FLOW_TYPE_DSCP:
     case FLOW_TYPE_FRAGMENT:
     {
-      uint last = 0;
-      uint first = 1;
-
+      /*
+       *    0   1   2   3   4   5   6   7       0   1   2   3   4   5   6   7
+       *  +---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---+---+
+       *  | e | a |  len  | 0 |lt |gt |eq |   | e | a |  len  | 0 | 0 |not| m |
+       *  +---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---+---+
+       *
+       *           Numeric operator                    Bitmask operator
+       */
+
+      bool first = true;
+      bool last = false;
       while (!last)
       {
       while (!last)
       {
-       /*
-        *    0   1   2   3   4   5   6   7
-        *  +---+---+---+---+---+---+---+---+
-        *  | e | a |  len  | 0 |lt |gt |eq |
-        *  +---+---+---+---+---+---+---+---+
-        *
-        *           Numeric operator
-        */
+       if (pos + 1 > end)
+         return FLOW_ST_NOT_COMPLETE;
 
 
-       last = isset_end(pos);
+       uint len = get_value_length(pos);
 
 
-       /* The AND bit should in the first operator byte of a sequence */
-       if (first && isset_and(pos))
-         return FLOW_ST_AND_BIT_SHOULD_BE_UNSET;
+       /* Some component values MUST be encoded with limited length */
+       uint maxlen = flow_max_value_length[type];
+       if (maxlen && (len > maxlen))
+         return FLOW_ST_EXCEED_MAX_VALUE_LENGTH;
 
 
-       /* This bit should be zero */
-       if (*pos & 0x08)
-         return FLOW_ST_ZERO_BIT_SHOULD_BE_UNSED;
+       if (pos + 1 + len > end)
+         return FLOW_ST_NOT_COMPLETE;
 
 
-       if (type == FLOW_TYPE_TCP_FLAGS || type == FLOW_TYPE_FRAGMENT)
-       {
-         /*
-          *    0   1   2   3   4   5   6   7
-          *  +---+---+---+---+---+---+---+---+
-          *  | e | a |  len  | 0 | 0 |not| m |
-          *  +---+---+---+---+---+---+---+---+
-          *
-          *           Bitmask operand
-          */
-         if (*pos & 0x04)
-           return FLOW_ST_ZERO_BIT_SHOULD_BE_UNSED;
-       }
+       /* In first operator of sequence, AND bit ... MUST be ignored during decoding */
+       if (first)
+         pos[0] &= ~0x40;
 
 
-       /* Value length of operator */
-       uint len = get_value_length(pos);
-       if (len > flow_max_value_length(type, ipv6))
-         return FLOW_ST_EXCEED_MAX_VALUE_LENGTH;
+       /* Zero field MUST be set to 0 on encoding and MUST be ignored during decoding */
+       pos[0] &= ~0x08;
+
+       /* Bitmask operator has additional zero field */
+       if (type == FLOW_TYPE_TCP_FLAGS || type == FLOW_TYPE_FRAGMENT)
+         pos[0] &= ~0x04;
 
 
-       /* TCP Flags component must not check highest nibble (just 12 valid bits) */
-       if ((type == FLOW_TYPE_TCP_FLAGS) && (len == 2) && (pos[1] & 0xf0))
-         return FLOW_ST_INVALID_TCP_FLAGS;
+       /* Zero fields in highest nibble of TCP Flags bitmask operand */
+       if ((type == FLOW_TYPE_TCP_FLAGS) && (len == 2))
+         pos[1] &= ~0xf0;
 
 
-       /* Bit-7 must be 0 [draft-ietf-idr-flow-spec-v6] */
-       if ((type == FLOW_TYPE_FRAGMENT) && ipv6 && (pos[1] & 0x01))
-         return FLOW_ST_CANNOT_USE_DONT_FRAGMENT;
-       /* XXX: Could be a fragment component encoded in 2-bytes? */
+       /* Zero fields in fragment bitmask operand */
+       if ((type == FLOW_TYPE_FRAGMENT) && (len == 1))
+         pos[1] &= ipv6 ? ~0xf1 : ~0xf0;
 
 
+       /* Move to next operator */
+       first = false;
+       last = isset_end(pos);
        pos += 1+len;
        pos += 1+len;
+      }
+      break;
+    }
+
+    default:
+      return FLOW_ST_UNKNOWN_COMPONENT;
+    }
+  }
+
+  if (pos != end)
+    return FLOW_ST_NOT_COMPLETE;
+
+  return FLOW_ST_VALID;
+}
+
+/**
+ * flow4_decode - decode incoming BGP IPv4 flowspec data stream
+ * @nlri: flowspec data stream without length header
+ * @len: length of @nlri
+ *
+ * This function checks syntatic correctness of binary flowspec. It returns
+ * %FLOW_ST_VALID, or some other %FLOW_ST_xxx state if the NLRI is malformed.
+ * If the NLRI is valid but not normalized (e.g. some zero bits are set), the
+ * flow4_decode() modify the datastream to normalize it.
+ */
+inline enum flow_validated_state
+flow4_decode(byte *nlri, uint len)
+{
+  return flow_decode(nlri, len, false);
+}
+
+/**
+ * flow6_decode - decode incoming BGP IPv6 flowspec data stream
+ * @nlri: flowspec data stream without length header
+ * @len: length of @nlri
+ *
+ * This function checks syntatic correctness of binary flowspec. It returns
+ * %FLOW_ST_VALID, or some other %FLOW_ST_xxx state if the NLRI is malformed.
+ * If the NLRI is valid but not normalized (e.g. some zero bits are set), the
+ * flow6_decode() modify the datastream to normalize it.
+ */
+inline enum flow_validated_state
+flow6_decode(byte *nlri, uint len)
+{
+  return flow_decode(nlri, len, true);
+}
+
+static enum flow_validated_state
+flow_validate(const byte *nlri, uint len, bool ipv6)
+{
+  enum flow_type type = 0;
+  const byte *pos = nlri;
+  const byte *end = nlri + len;
+
+  while (pos < end)
+  {
+    /* Check increasing type ordering */
+    if (*pos <= type)
+      return FLOW_ST_BAD_TYPE_ORDER;
+
+    type = *pos++;
+
+    switch (type)
+    {
+    case FLOW_TYPE_DST_PREFIX:
+    case FLOW_TYPE_SRC_PREFIX:
+    {
+      uint pxlen, offset = 0;
 
 
-       if (pos > end && !last)
+      if (!ipv6)
+      {
+       if (pos + 1 > end)
          return FLOW_ST_NOT_COMPLETE;
 
          return FLOW_ST_NOT_COMPLETE;
 
-       if (pos > (end+1))
+       pxlen = *pos++;
+
+       if (pxlen > IP4_MAX_PREFIX_LENGTH)
+         return FLOW_ST_EXCEED_MAX_PREFIX_LENGTH;
+      }
+      else
+      {
+       if (pos + 2 > end)
          return FLOW_ST_NOT_COMPLETE;
 
          return FLOW_ST_NOT_COMPLETE;
 
-       first = 0;
+       pxlen = *pos++;
+       offset = *pos++;
+
+       if (pxlen > IP6_MAX_PREFIX_LENGTH)
+         return FLOW_ST_EXCEED_MAX_PREFIX_LENGTH;
+
+       if (offset > pxlen)
+         return FLOW_ST_EXCEED_MAX_PREFIX_OFFSET;
       }
       }
+
+      uint bits = pxlen - offset;
+      pos += BYTES(bits);
+
+      if (pos > end)
+       return FLOW_ST_NOT_COMPLETE;
+
+      /* Padding bits in the last byte MUST be 0 */
+      if (bits % 8)
+       if (pos[-1] & flow_padding(bits))
+         return FLOW_ST_NONZERO_PADDING;
+
       break;
     }
       break;
     }
+
+    case FLOW_TYPE_LABEL:
+      if (!ipv6)
+       return FLOW_ST_UNKNOWN_COMPONENT;
+      /* fall through */
+
+    case FLOW_TYPE_IP_PROTOCOL: /* == FLOW_TYPE_NEXT_HEADER */
+    case FLOW_TYPE_PORT:
+    case FLOW_TYPE_DST_PORT:
+    case FLOW_TYPE_SRC_PORT:
+    case FLOW_TYPE_ICMP_TYPE:
+    case FLOW_TYPE_ICMP_CODE:
+    case FLOW_TYPE_TCP_FLAGS:
+    case FLOW_TYPE_PACKET_LENGTH:
+    case FLOW_TYPE_DSCP:
+    case FLOW_TYPE_FRAGMENT:
+    {
+      /*
+       *    0   1   2   3   4   5   6   7       0   1   2   3   4   5   6   7
+       *  +---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---+---+
+       *  | e | a |  len  | 0 |lt |gt |eq |   | e | a |  len  | 0 | 0 |not| m |
+       *  +---+---+---+---+---+---+---+---+   +---+---+---+---+---+---+---+---+
+       *
+       *           Numeric operator                    Bitmask operator
+       */
+
+      bool first = true;
+      bool last = false;
+      while (!last)
+      {
+       if (pos + 1 > end)
+         return FLOW_ST_NOT_COMPLETE;
+
+       uint len = get_value_length(pos);
+
+       /* Some component values MUST be encoded with limited length */
+       uint maxlen = flow_max_value_length[type];
+       if (maxlen && (len > maxlen))
+         return FLOW_ST_EXCEED_MAX_VALUE_LENGTH;
+
+       if (pos + 1 + len > end)
+         return FLOW_ST_NOT_COMPLETE;
+
+       /* In first operator of sequence, AND bit ... MUST be ignored during decoding */
+       if (first && isset_and(pos))
+         return FLOW_ST_FIRST_AND_BIT_SET;
+
+       /* Zero field MUST be set to 0 on encoding and MUST be ignored during decoding */
+       if (pos[0] & 0x08)
+         return FLOW_ST_ZERO_BIT_SET;
+
+       /* Bitmask operator has additional zero field */
+       if (type == FLOW_TYPE_TCP_FLAGS || type == FLOW_TYPE_FRAGMENT)
+         if (pos[0] & 0x04)
+           return FLOW_ST_ZERO_BIT_SET;
+
+       /* Zero fields in highest nibble of TCP Flags bitmask operand */
+       if ((type == FLOW_TYPE_TCP_FLAGS) && (len == 2))
+         if (pos[1] & 0xf0)
+           return FLOW_ST_INVALID_TCP_FLAGS;
+
+       /* Zero fields in fragment bitmask operand */
+       if ((type == FLOW_TYPE_FRAGMENT) && (len == 1))
+         if (pos[1] & (ipv6 ? 0xf1 : 0xf0))
+           return FLOW_ST_INVALID_FRAGMENT;
+
+       /* Move to next operator */
+       first = false;
+       last = isset_end(pos);
+       pos += 1+len;
+      }
+      break;
+    }
+
     default:
       return FLOW_ST_UNKNOWN_COMPONENT;
     }
     default:
       return FLOW_ST_UNKNOWN_COMPONENT;
     }
@@ -571,32 +744,32 @@ flow_validate(const byte *nlri, uint len, int ipv6)
 
 /**
  * flow4_validate - check untrustworthy IPv4 flowspec data stream
 
 /**
  * flow4_validate - check untrustworthy IPv4 flowspec data stream
- * @nlri: flowspec data stream without compressed encoded length value
+ * @nlri: flowspec data stream without length header
  * @len: length of @nlri
  *
  * @len: length of @nlri
  *
- * This function checks meaningfulness of binary flowspec. It should return
- * %FLOW_ST_VALID or %FLOW_ST_UNKNOWN_COMPONENT. If some problem appears, it
- * returns some other %FLOW_ST_xxx state.
+ * This function checks syntactic correctness of binary flowspec. It returns
+ * %FLOW_ST_VALID, or some other %FLOW_ST_xxx state if the NLRI is malformed
+ * or non-normalized.
  */
 inline enum flow_validated_state
 flow4_validate(const byte *nlri, uint len)
 {
  */
 inline enum flow_validated_state
 flow4_validate(const byte *nlri, uint len)
 {
-  return flow_validate(nlri, len, 0);
+  return flow_validate(nlri, len, false);
 }
 
 /**
  * flow6_validate - check untrustworthy IPv6 flowspec data stream
 }
 
 /**
  * flow6_validate - check untrustworthy IPv6 flowspec data stream
- * @nlri: flowspec binary stream without encoded length value
+ * @nlri: flowspec binary stream without length header
  * @len: length of @nlri
  *
  * @len: length of @nlri
  *
- * This function checks meaningfulness of binary flowspec. It should return
- * %FLOW_ST_VALID or %FLOW_ST_UNKNOWN_COMPONENT. If some problem appears, it
- * returns some other %FLOW_ST_xxx state.
+ * This function checks syntactic correctness of binary flowspec. It returns
+ * %FLOW_ST_VALID, or some other %FLOW_ST_xxx state if the NLRI is malformed
+ * or non-normalized.
  */
 inline enum flow_validated_state
 flow6_validate(const byte *nlri, uint len)
 {
  */
 inline enum flow_validated_state
 flow6_validate(const byte *nlri, uint len)
 {
-  return flow_validate(nlri, len, 1);
+  return flow_validate(nlri, len, true);
 }
 
 /**
 }
 
 /**
@@ -788,7 +961,13 @@ flow_builder_add_op_val(struct flow_builder *fb, byte op, u32 value)
   /* Set the end-bit for operand-value pair of the component */
   op |= 0x80;
 
   /* Set the end-bit for operand-value pair of the component */
   op |= 0x80;
 
-  if (value & 0xff00)
+  /* Label component values SHOULD be encoded as 4-octet quantities */
+  if ((value > 0xffff) || (fb->this_type == FLOW_TYPE_LABEL))
+  {
+    BUFFER_PUSH(fb->data) = op | 0x20;
+    put_u32(BUFFER_INC(fb->data, 4), value);
+  }
+  else if (value > 0xff)
   {
     BUFFER_PUSH(fb->data) = op | 0x10;
     put_u16(BUFFER_INC(fb->data, 2), value);
   {
     BUFFER_PUSH(fb->data) = op | 0x10;
     put_u16(BUFFER_INC(fb->data, 2), value);
index 8ff968ed2a85d35136200890012e19500bc41fac..74b2288da77373c47ccac7c4b8faeed75cbf33c9 100644 (file)
@@ -137,18 +137,21 @@ enum flow_validated_state {
   FLOW_ST_EXCEED_MAX_PREFIX_OFFSET,
   FLOW_ST_EXCEED_MAX_VALUE_LENGTH,
   FLOW_ST_BAD_TYPE_ORDER,
   FLOW_ST_EXCEED_MAX_PREFIX_OFFSET,
   FLOW_ST_EXCEED_MAX_VALUE_LENGTH,
   FLOW_ST_BAD_TYPE_ORDER,
-  FLOW_ST_AND_BIT_SHOULD_BE_UNSET,
-  FLOW_ST_ZERO_BIT_SHOULD_BE_UNSED,
-  FLOW_ST_DEST_PREFIX_REQUIRED,
+  FLOW_ST_NONZERO_PADDING,
+  FLOW_ST_FIRST_AND_BIT_SET,
+  FLOW_ST_ZERO_BIT_SET,
   FLOW_ST_INVALID_TCP_FLAGS,
   FLOW_ST_INVALID_TCP_FLAGS,
-  FLOW_ST_CANNOT_USE_DONT_FRAGMENT
+  FLOW_ST_INVALID_FRAGMENT,
 };
 
 const char *flow_validated_state_str(enum flow_validated_state code);
 };
 
 const char *flow_validated_state_str(enum flow_validated_state code);
+enum flow_validated_state flow4_decode(byte *nlri, uint len);
+enum flow_validated_state flow6_decode(byte *nlri, uint len);
 enum flow_validated_state flow4_validate(const byte *nlri, uint len);
 enum flow_validated_state flow6_validate(const byte *nlri, uint len);
 enum flow_validated_state flow4_validate(const byte *nlri, uint len);
 enum flow_validated_state flow6_validate(const byte *nlri, uint len);
-void flow_check_cf_value_length(struct flow_builder *fb, u32 expr);
-void flow_check_cf_bmk_values(struct flow_builder *fb, u8 neg, u32 val, u32 mask);
+
+void flow_check_cf_bitmask_arg(struct flow_builder *fb, u8 neg, u32 val, u32 mask);
+void flow_check_cf_numeric_arg(struct flow_builder *fb, uint val);
 void flow4_validate_cf(net_addr_flow4 *f);
 void flow6_validate_cf(net_addr_flow6 *f);
 
 void flow4_validate_cf(net_addr_flow4 *f);
 void flow6_validate_cf(net_addr_flow6 *f);
 
index 1fd1a0b9912c3977d93dde8317efec94b86f90c2..13f1468b273cd77ea8297302421e4934dd395de9 100644 (file)
@@ -281,6 +281,13 @@ t_validation4(void)
        FLOW_TYPE_DST_PREFIX, 33, 5, 6, 7, 8, 9
       })
     ),
        FLOW_TYPE_DST_PREFIX, 33, 5, 6, 7, 8, 9
       })
     ),
+    TS(
+      FLOW_ST_EXCEED_MAX_VALUE_LENGTH,
+      "DSCP of length 2",
+      ((byte []) {
+       FLOW_TYPE_DSCP, 0x91, 00, 63,
+      })
+    ),
     TS(
       FLOW_ST_BAD_TYPE_ORDER,
       "Bad flowspec component type order",
     TS(
       FLOW_ST_BAD_TYPE_ORDER,
       "Bad flowspec component type order",
@@ -298,28 +305,65 @@ t_validation4(void)
       })
     ),
     TS(
       })
     ),
     TS(
-      FLOW_ST_AND_BIT_SHOULD_BE_UNSET,
+      FLOW_ST_UNKNOWN_COMPONENT,
+      "Unknown component of type number 14",
+      ((byte []) {
+       FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
+       FLOW_TYPE_TCP_FLAGS, 0x80, 0x55,
+       14 /*something new*/, 0x80, 0x55,
+      })
+    ),
+    TS(
+      FLOW_ST_UNKNOWN_COMPONENT,
+      "Label component in IPv4",
+      ((byte []) {
+       FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
+       FLOW_TYPE_LABEL, 0xa0, 0, 0, 0, 0x55,
+      })
+    ),
+    TS(
+      FLOW_ST_NONZERO_PADDING,
+      "Non-zero padding in prefix",
+      ((byte []) {
+       FLOW_TYPE_DST_PREFIX, 20, 5, 6, 7,
+      })
+    ),
+    TS(
+      FLOW_ST_FIRST_AND_BIT_SET,
       "The first numeric operator has set the AND bit",
       ((byte []) {
        FLOW_TYPE_PORT, 0x43, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
       })
     ),
     TS(
       "The first numeric operator has set the AND bit",
       ((byte []) {
        FLOW_TYPE_PORT, 0x43, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
       })
     ),
     TS(
-      FLOW_ST_ZERO_BIT_SHOULD_BE_UNSED,
-      "Set zero bit in operand to one",
+      FLOW_ST_ZERO_BIT_SET,
+      "Set zero bit in operator to one (0x08)",
       ((byte []) {
        FLOW_TYPE_IP_PROTOCOL, 0x89, 0x06,
       })
     ),
     TS(
       ((byte []) {
        FLOW_TYPE_IP_PROTOCOL, 0x89, 0x06,
       })
     ),
     TS(
-      FLOW_ST_UNKNOWN_COMPONENT,
-      "Unknown component of type number 13",
+      FLOW_ST_ZERO_BIT_SET,
+      "Set zero bit in operator to one (0x04)",
       ((byte []) {
       ((byte []) {
-       FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
-       FLOW_TYPE_TCP_FLAGS, 0x80, 0x55,
-       13 /*something new*/, 0x80, 0x55,
+       FLOW_TYPE_FRAGMENT, 0x84, 0x03,
       })
     ),
       })
     ),
+    TS(
+      FLOW_ST_INVALID_TCP_FLAGS,
+      "Set zero bit in operand to one (TCP flags)",
+      ((byte []) {
+       FLOW_TYPE_TCP_FLAGS, 0x91, 0xff, 0x3a,
+      })
+    ),
+    TS(
+      FLOW_ST_INVALID_FRAGMENT,
+      "Set zero bit in operand to one (fragment)",
+      ((byte []) {
+       FLOW_TYPE_FRAGMENT, 0x81, 0xff,
+      })
+    ),
+
   };
 #undef TS
 
   };
 #undef TS
 
@@ -338,7 +382,7 @@ t_validation6(void)
   enum flow_validated_state res;
 
   byte nlri1[] = {
   enum flow_validated_state res;
 
   byte nlri1[] = {
-    FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0x98,
+    FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0xc0,
     FLOW_TYPE_SRC_PREFIX, 8, 0, 0xc0,
     FLOW_TYPE_NEXT_HEADER, 0x81, 0x06,
     FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
     FLOW_TYPE_SRC_PREFIX, 8, 0, 0xc0,
     FLOW_TYPE_NEXT_HEADER, 0x81, 0x06,
     FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
@@ -392,6 +436,13 @@ t_validation6(void)
        FLOW_TYPE_DST_PREFIX, 48, 64, 0x40, 0x12, 0x34
       })
     ),
        FLOW_TYPE_DST_PREFIX, 48, 64, 0x40, 0x12, 0x34
       })
     ),
+    TS(
+      FLOW_ST_EXCEED_MAX_VALUE_LENGTH,
+      "Fragment of length 2",
+      ((byte []) {
+       FLOW_TYPE_FRAGMENT, 0x91, 00, 0x0f,
+      })
+    ),
     TS(
       FLOW_ST_BAD_TYPE_ORDER,
       "Bad flowspec component type order",
     TS(
       FLOW_ST_BAD_TYPE_ORDER,
       "Bad flowspec component type order",
@@ -404,46 +455,418 @@ t_validation6(void)
       FLOW_ST_BAD_TYPE_ORDER,
       "Doubled destination prefix component",
       ((byte []) {
       FLOW_ST_BAD_TYPE_ORDER,
       "Doubled destination prefix component",
       ((byte []) {
-       FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0x98,
-       FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0x98,
+       FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0xc0,
+       FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0xc0,
       })
     ),
     TS(
       })
     ),
     TS(
-      FLOW_ST_AND_BIT_SHOULD_BE_UNSET,
+      FLOW_ST_UNKNOWN_COMPONENT,
+      "Unknown component of type number 14",
+      ((byte []) {
+       FLOW_TYPE_TCP_FLAGS, 0x80, 0x55,
+       14 /*something new*/, 0x80, 0x55,
+      })
+      ),
+    TS(
+      FLOW_ST_VALID,
+      "Label component is well-known in IPv6",
+      ((byte []) {
+       FLOW_TYPE_LABEL, 0xa0, 0, 0, 0, 0x55,
+      })
+    ),
+    TS(
+      FLOW_ST_NONZERO_PADDING,
+      "Non-zero padding in prefix",
+      ((byte []) {
+       FLOW_TYPE_DST_PREFIX, 44, 16, 0x40, 0x12, 0x34, 0x56,
+      })
+    ),
+    TS(
+      FLOW_ST_FIRST_AND_BIT_SET,
       "The first numeric operator has set the AND bit",
       ((byte []) {
        FLOW_TYPE_PORT, 0x43, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90
       })
     ),
     TS(
       "The first numeric operator has set the AND bit",
       ((byte []) {
        FLOW_TYPE_PORT, 0x43, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90
       })
     ),
     TS(
-      FLOW_ST_ZERO_BIT_SHOULD_BE_UNSED,
-      "Set zero bit in operand to one",
+      FLOW_ST_ZERO_BIT_SET,
+      "Set zero bit in operator to one (0x08)",
       ((byte []) {
        FLOW_TYPE_NEXT_HEADER, 0x89, 0x06
       })
     ),
       ((byte []) {
        FLOW_TYPE_NEXT_HEADER, 0x89, 0x06
       })
     ),
+    TS(
+      FLOW_ST_ZERO_BIT_SET,
+      "Set zero bit in operator to one (0x04)",
+      ((byte []) {
+       FLOW_TYPE_FRAGMENT, 0x84, 0x03,
+      })
+    ),
+    TS(
+      FLOW_ST_INVALID_TCP_FLAGS,
+      "Set zero bit in operand to one (TCP flags)",
+      ((byte []) {
+       FLOW_TYPE_TCP_FLAGS, 0x91, 0xff, 0x3a,
+      })
+    ),
+    TS(
+      FLOW_ST_INVALID_FRAGMENT,
+      "Set zero bit in operand to one (fragment)",
+      ((byte []) {
+       FLOW_TYPE_FRAGMENT, 0x81, 0xff,
+      })
+    ),
+  };
+#undef TS
+
+  for (uint tcase = 0; tcase < ARRAY_SIZE(tset); tcase++)
+  {
+    res = flow6_validate(tset[tcase].nlri, tset[tcase].size);
+    bt_assert_msg(res == tset[tcase].expect, "Assertion (%s == %s) %s", flow_validated_state_str(res), flow_validated_state_str(tset[tcase].expect), tset[tcase].description);
+  }
+
+  return 1;
+}
+
+static int
+t_decoding4(void)
+{
+  enum flow_validated_state res;
+
+  byte nlri1[] = {
+    FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
+    FLOW_TYPE_SRC_PREFIX, 32, 10, 11, 12, 13,
+    FLOW_TYPE_IP_PROTOCOL, 0x81, 0x06,
+    FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
+    FLOW_TYPE_TCP_FLAGS, 0x80, 0x55,
+  };
+
+  /* Empty NLRI */
+  res = flow4_decode(nlri1, 0);
+  bt_assert(res == FLOW_ST_VALID);
+
+  /* Valid / Not Complete testing */
+  uint valid_sizes[] = {5, 11, 14, 22, 25, 0};
+  uint valid_idx = 0;
+  for (uint size = 1; size <= sizeof(nlri1); size++)
+  {
+    res = flow4_decode(nlri1, size);
+    bt_debug("size %u, result: %s\n", size, flow_validated_state_str(res));
+    if (size == valid_sizes[valid_idx])
+    {
+      valid_idx++;
+      bt_assert(res == FLOW_ST_VALID);
+    }
+    else
+    {
+      bt_assert(res == FLOW_ST_NOT_COMPLETE);
+    }
+  }
+
+  /* Misc err tests */
+
+  struct tset {
+    enum flow_validated_state expect;
+    char *description;
+    u16 size;
+    byte *nlri;
+    byte *result;
+  };
+
+#define TS(type, msg, data, result) ((struct tset) {type, msg, sizeof(data), (data), (result)})
+  struct tset tset[] = {
+    TS(
+      FLOW_ST_EXCEED_MAX_PREFIX_LENGTH,
+      "33-length IPv4 prefix",
+      ((byte []) {
+       FLOW_TYPE_DST_PREFIX, 33, 5, 6, 7, 8, 9
+      }),
+      NULL
+    ),
+    TS(
+      FLOW_ST_EXCEED_MAX_VALUE_LENGTH,
+      "DSCP of length 2",
+      ((byte []) {
+       FLOW_TYPE_DSCP, 0x91, 00, 63,
+      }),
+      NULL
+    ),
+    TS(
+      FLOW_ST_BAD_TYPE_ORDER,
+      "Bad flowspec component type order",
+      ((byte []) {
+       FLOW_TYPE_SRC_PREFIX, 32, 10, 11, 12, 13,
+       FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
+      }),
+      NULL
+    ),
+    TS(
+      FLOW_ST_BAD_TYPE_ORDER,
+      "Doubled destination prefix component",
+      ((byte []) {
+       FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
+       FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
+      }),
+      NULL
+    ),
+    TS(
+      FLOW_ST_UNKNOWN_COMPONENT,
+      "Unknown component of type number 14",
+      ((byte []) {
+       FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
+       FLOW_TYPE_TCP_FLAGS, 0x80, 0x55,
+       14 /*something new*/, 0x80, 0x55,
+      }),
+      NULL
+    ),
+    TS(
+      FLOW_ST_UNKNOWN_COMPONENT,
+      "Label component in IPv4",
+      ((byte []) {
+       FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
+       FLOW_TYPE_LABEL, 0xa0, 0, 0, 0, 0x55,
+      }),
+      NULL
+    ),
+    TS(
+      FLOW_ST_VALID,
+      "Non-zero padding in prefix",
+      ((byte []) {
+       FLOW_TYPE_DST_PREFIX, 21, 5, 6, 0x7f,
+       FLOW_TYPE_SRC_PREFIX, 24, 5, 6, 0x7f,
+      }),
+      ((byte []) {
+       FLOW_TYPE_DST_PREFIX, 21, 5, 6, 0x78,
+       FLOW_TYPE_SRC_PREFIX, 24, 5, 6, 0x7f,
+      })
+    ),
+    TS(
+      FLOW_ST_VALID,
+      "The first numeric operator has set the AND bit",
+      ((byte []) {
+       FLOW_TYPE_PORT, 0x43, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
+      }),
+      ((byte []) {
+       FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
+      })
+    ),
     TS(
       FLOW_ST_VALID,
     TS(
       FLOW_ST_VALID,
-      "Component of type number 13 (Label) is well-known in IPv6",
+      "Set zero bit in operator to one",
       ((byte []) {
       ((byte []) {
-       FLOW_TYPE_LABEL, 0x80, 0x55
+       FLOW_TYPE_IP_PROTOCOL, 0x89, 0x06,
+       FLOW_TYPE_FRAGMENT, 0x84, 0x03,
+      }),
+      ((byte []) {
+       FLOW_TYPE_IP_PROTOCOL, 0x81, 0x06,
+       FLOW_TYPE_FRAGMENT, 0x80, 0x03,
       })
     ),
       })
     ),
+    TS(
+      FLOW_ST_VALID,
+      "Set zero bit in operand to one",
+      ((byte []) {
+       FLOW_TYPE_TCP_FLAGS, 0x91, 0xff, 0x3a,
+       FLOW_TYPE_FRAGMENT, 0x81, 0xff,
+      }),
+      ((byte []) {
+       FLOW_TYPE_TCP_FLAGS, 0x91, 0x0f, 0x3a,
+       FLOW_TYPE_FRAGMENT, 0x81, 0x0f,
+      })
+    ),
+  };
+#undef TS
+
+  for (uint tcase = 0; tcase < ARRAY_SIZE(tset); tcase++)
+  {
+    res = flow4_decode(tset[tcase].nlri, tset[tcase].size);
+    bt_assert_msg(res == tset[tcase].expect, "Assertion (%s == %s) %s", flow_validated_state_str(res), flow_validated_state_str(tset[tcase].expect), tset[tcase].description);
+
+    if ((res == FLOW_ST_VALID) && (tset[tcase].expect == FLOW_ST_VALID))
+    {
+      int pos;
+      for (pos = 0; pos < tset[tcase].size; pos++)
+       if (tset[tcase].nlri[pos] != tset[tcase].result[pos])
+         break;
+
+      bool ok = (pos == tset[tcase].size);
+      bt_assert_msg(ok, "Assertion (pos %u: 0x%02x == 0x%02x) %s", pos, (ok ? 0 : tset[tcase].nlri[pos]), (ok ? 0 : tset[tcase].result[pos]), tset[tcase].description);
+    }
+  }
+
+  return 1;
+}
+
+static int
+t_decoding6(void)
+{
+  enum flow_validated_state res;
+
+  byte nlri1[] = {
+    FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0xc0,
+    FLOW_TYPE_SRC_PREFIX, 8, 0, 0xc0,
+    FLOW_TYPE_NEXT_HEADER, 0x81, 0x06,
+    FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
+    FLOW_TYPE_LABEL, 0x80, 0x55,
+  };
+
+  /* Isn't included destination prefix */
+  res = flow6_decode(nlri1, 0);
+  bt_assert(res == FLOW_ST_VALID);
+
+  /* Valid / Not Complete testing */
+  uint valid_sizes[] = {0, 9, 13, 16, 24, 27, 0};
+  uint valid_idx = 0;
+  for (uint size = 0; size <= sizeof(nlri1); size++)
+  {
+    res = flow6_decode(nlri1, size);
+    bt_debug("size %u, result: %s\n", size, flow_validated_state_str(res));
+    if (size == valid_sizes[valid_idx])
+    {
+      valid_idx++;
+      bt_assert(res == FLOW_ST_VALID);
+    }
+    else
+    {
+      bt_assert(res == FLOW_ST_NOT_COMPLETE);
+    }
+  }
+
+  /* Misc err tests */
+
+  struct tset {
+    enum flow_validated_state expect;
+    char *description;
+    u16 size;
+    byte *nlri;
+    byte *result;
+  };
+
+#define TS(type, msg, data, result) ((struct tset) {type, msg, sizeof(data), (data), (result)})
+  struct tset tset[] = {
+    TS(
+      FLOW_ST_EXCEED_MAX_PREFIX_LENGTH,
+      "129-length IPv6 prefix",
+      ((byte []) {
+       FLOW_TYPE_DST_PREFIX, 129, 64, 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x12
+      }),
+      NULL
+    ),
+    TS(
+      FLOW_ST_EXCEED_MAX_PREFIX_OFFSET,
+      "Prefix offset is higher than prefix length",
+      ((byte []) {
+       FLOW_TYPE_DST_PREFIX, 48, 64, 0x40, 0x12, 0x34
+      }),
+      NULL
+    ),
+    TS(
+      FLOW_ST_EXCEED_MAX_VALUE_LENGTH,
+      "Fragment of length 2",
+      ((byte []) {
+       FLOW_TYPE_FRAGMENT, 0x91, 00, 0x0f,
+      }),
+      NULL
+    ),
+    TS(
+      FLOW_ST_BAD_TYPE_ORDER,
+      "Bad flowspec component type order",
+      ((byte []) {
+       FLOW_TYPE_NEXT_HEADER, 0x81, 0x06,
+       FLOW_TYPE_SRC_PREFIX, 8, 0, 0xc0,
+      }),
+      NULL
+    ),
+    TS(
+      FLOW_ST_BAD_TYPE_ORDER,
+      "Doubled destination prefix component",
+      ((byte []) {
+       FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0xc0,
+       FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0xc0,
+      }),
+      NULL
+    ),
     TS(
       FLOW_ST_UNKNOWN_COMPONENT,
       "Unknown component of type number 14",
       ((byte []) {
     TS(
       FLOW_ST_UNKNOWN_COMPONENT,
       "Unknown component of type number 14",
       ((byte []) {
-       FLOW_TYPE_LABEL, 0x80, 0x55,
+       FLOW_TYPE_TCP_FLAGS, 0x80, 0x55,
        14 /*something new*/, 0x80, 0x55,
        14 /*something new*/, 0x80, 0x55,
+      }),
+      NULL
+      ),
+    TS(
+      FLOW_ST_VALID,
+      "Label component is well-known in IPv6",
+      ((byte []) {
+       FLOW_TYPE_LABEL, 0xa0, 0, 0, 0, 0x55,
+      }),
+      ((byte []) {
+       FLOW_TYPE_LABEL, 0xa0, 0, 0, 0, 0x55,
+      })
+    ),
+    TS(
+      FLOW_ST_VALID,
+      "Non-zero padding in prefix",
+      ((byte []) {
+       FLOW_TYPE_DST_PREFIX, 43, 16, 0x40, 0x12, 0x34, 0x7c,
+      }),
+      ((byte []) {
+       FLOW_TYPE_DST_PREFIX, 43, 16, 0x40, 0x12, 0x34, 0x60,
+      })
+    ),
+    TS(
+      FLOW_ST_VALID,
+      "The first numeric operator has set the AND bit",
+      ((byte []) {
+       FLOW_TYPE_PORT, 0x43, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90
+      }),
+      ((byte []) {
+       FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
+      })
+    ),
+    TS(
+      FLOW_ST_VALID,
+      "Set zero bit in operator to one",
+      ((byte []) {
+       FLOW_TYPE_NEXT_HEADER, 0x89, 0x06,
+       FLOW_TYPE_FRAGMENT, 0x84, 0x02,
+      }),
+      ((byte []) {
+       FLOW_TYPE_IP_PROTOCOL, 0x81, 0x06,
+       FLOW_TYPE_FRAGMENT, 0x80, 0x02,
       })
       })
-    )
+    ),
+    TS(
+      FLOW_ST_VALID,
+      "Set zero bit in operand to one",
+      ((byte []) {
+       FLOW_TYPE_TCP_FLAGS, 0x91, 0xff, 0x3a,
+       FLOW_TYPE_FRAGMENT, 0x81, 0xff,
+      }),
+      ((byte []) {
+       FLOW_TYPE_TCP_FLAGS, 0x91, 0x0f, 0x3a,
+       FLOW_TYPE_FRAGMENT, 0x81, 0x0e,
+      })
+    ),
   };
 #undef TS
 
   for (uint tcase = 0; tcase < ARRAY_SIZE(tset); tcase++)
   {
   };
 #undef TS
 
   for (uint tcase = 0; tcase < ARRAY_SIZE(tset); tcase++)
   {
-    res = flow6_validate(tset[tcase].nlri, tset[tcase].size);
+    res = flow6_decode(tset[tcase].nlri, tset[tcase].size);
     bt_assert_msg(res == tset[tcase].expect, "Assertion (%s == %s) %s", flow_validated_state_str(res), flow_validated_state_str(tset[tcase].expect), tset[tcase].description);
     bt_assert_msg(res == tset[tcase].expect, "Assertion (%s == %s) %s", flow_validated_state_str(res), flow_validated_state_str(tset[tcase].expect), tset[tcase].description);
+
+    if ((res == FLOW_ST_VALID) && (tset[tcase].expect == FLOW_ST_VALID))
+    {
+      int pos;
+      for (pos = 0; pos < tset[tcase].size; pos++)
+       if (tset[tcase].nlri[pos] != tset[tcase].result[pos])
+         break;
+
+      bool ok = (pos == tset[tcase].size);
+      bt_assert_msg(ok, "Assertion (pos %u: 0x%02x == 0x%02x) %s", pos, (ok ? 0 : tset[tcase].nlri[pos]), (ok ? 0 : tset[tcase].result[pos]), tset[tcase].description);
+    }
   }
 
   return 1;
   }
 
   return 1;
@@ -546,7 +969,7 @@ t_builder6(void)
     FLOW_TYPE_SRC_PREFIX, 8, 0, 0xc0,
     FLOW_TYPE_NEXT_HEADER, 0x80, 0x06,
     FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
     FLOW_TYPE_SRC_PREFIX, 8, 0, 0xc0,
     FLOW_TYPE_NEXT_HEADER, 0x80, 0x06,
     FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
-    FLOW_TYPE_LABEL, 0x80, 0x55,
+    FLOW_TYPE_LABEL, 0xa0, 0x00, 0x00, 0x00, 0x55,
   );
 
   /* Normal order */
   );
 
   /* Normal order */
@@ -729,6 +1152,8 @@ main(int argc, char *argv[])
   bt_test_suite(t_accessors6,   "Testing accessors (IPv6)");
   bt_test_suite(t_validation4,  "Testing validation (IPv4)");
   bt_test_suite(t_validation6,  "Testing validation (IPv6)");
   bt_test_suite(t_accessors6,   "Testing accessors (IPv6)");
   bt_test_suite(t_validation4,  "Testing validation (IPv4)");
   bt_test_suite(t_validation6,  "Testing validation (IPv6)");
+  bt_test_suite(t_decoding4,    "Testing decoding (IPv4)");
+  bt_test_suite(t_decoding6,    "Testing decoding (IPv6)");
   bt_test_suite(t_builder4,     "Inserting components into existing Flow Specification (IPv4)");
   bt_test_suite(t_builder6,     "Inserting components into existing Flow Specification (IPv6)");
   bt_test_suite(t_formatting4,  "Formatting Flow Specification (IPv4) into text representation");
   bt_test_suite(t_builder4,     "Inserting components into existing Flow Specification (IPv4)");
   bt_test_suite(t_builder6,     "Inserting components into existing Flow Specification (IPv6)");
   bt_test_suite(t_formatting4,  "Formatting Flow Specification (IPv4) into text representation");
index 522965d1059afb15dc21abc40303dd3fe395f30d..faa1558be974b5f32bdf193e79c0b35c572d5a5c 100644 (file)
@@ -2119,8 +2119,8 @@ bgp_decode_nlri_flow4(struct bgp_parse_state *s, byte *pos, uint len, rta *a)
     if (len < flen)
       bgp_parse_error(s, 1);
 
     if (len < flen)
       bgp_parse_error(s, 1);
 
-    /* Validate flow data */
-    enum flow_validated_state r = flow4_validate(data, dlen);
+    /* Decode flow data */
+    enum flow_validated_state r = flow4_decode(data, dlen);
     if (r != FLOW_ST_VALID)
     {
       log(L_REMOTE "%s: Invalid flow route: %s", s->proto->p.name, flow_validated_state_str(r));
     if (r != FLOW_ST_VALID)
     {
       log(L_REMOTE "%s: Invalid flow route: %s", s->proto->p.name, flow_validated_state_str(r));
@@ -2214,8 +2214,8 @@ bgp_decode_nlri_flow6(struct bgp_parse_state *s, byte *pos, uint len, rta *a)
     if (len < flen)
       bgp_parse_error(s, 1);
 
     if (len < flen)
       bgp_parse_error(s, 1);
 
-    /* Validate flow data */
-    enum flow_validated_state r = flow6_validate(data, dlen);
+    /* Decode flow data */
+    enum flow_validated_state r = flow6_decode(data, dlen);
     if (r != FLOW_ST_VALID)
     {
       log(L_REMOTE "%s: Invalid flow route: %s", s->proto->p.name, flow_validated_state_str(r));
     if (r != FLOW_ST_VALID)
     {
       log(L_REMOTE "%s: Invalid flow route: %s", s->proto->p.name, flow_validated_state_str(r));