]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Basic flow specification support (RFC 5575)
authorOndrej Zajicek (work) <santiago@crfreenet.org>
Wed, 7 Dec 2016 14:36:15 +0000 (15:36 +0100)
committerOndrej Zajicek (work) <santiago@crfreenet.org>
Wed, 7 Dec 2016 14:54:19 +0000 (15:54 +0100)
Add flow4/flow6 network and rt-table type and operations, config grammar
and static protocol support.

Squashed flowspec branch from Pavel Tvrdik.

18 files changed:
conf/Makefile
conf/confbase.Y
conf/flowspec.Y [new file with mode: 0644]
doc/bird.sgml
filter/test.conf
lib/Doc
lib/Makefile
lib/birdlib.h
lib/buffer.h
lib/flowspec.c [new file with mode: 0644]
lib/flowspec.h [new file with mode: 0644]
lib/flowspec_test.c [new file with mode: 0644]
lib/net.c
lib/net.h
lib/printf.c
nest/config.Y
nest/rt-fib.c
nest/rt-table.c

index cc2b13c6e0fbeac2f81bffcc9218a4601de55ccc..fb3dd0525ebb76144b957cc540234530374afc02 100644 (file)
@@ -10,7 +10,7 @@ BISON_DEBUG=-t
 #FLEX_DEBUG=-d
 endif
 
-$(conf-y-targets): $(s)confbase.Y
+$(conf-y-targets): $(s)confbase.Y $(s)flowspec.Y
        $(M4) -P $| $^ >$@
 
 $(o)cf-parse.y: | $(s)gen_parser.m4
index 094c81b55cf3b3b04485262e3e3b27ed124d0c3f..aec4aeb47b93db5383e8a728185816f845e96db9 100644 (file)
@@ -138,8 +138,6 @@ expr_us:
  | expr US { $$ = (u32) $1 * 1; }
  ;
 
-/* expr_u16: expr { check_u16($1); $$ = $1; }; */
-
 /* Switches */
 
 bool:
@@ -220,6 +218,7 @@ net_roa_: net_roa4_ | net_roa6_ ;
 net_:
    net_ip_ { $$ = cfg_alloc($1.length); net_copy($$, &($1)); }
  | net_roa_
+ | net_flow_
  ;
 
 
diff --git a/conf/flowspec.Y b/conf/flowspec.Y
new file mode 100644 (file)
index 0000000..a47d453
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ *     BIRD -- Flow specification (RFC 5575) grammar
+ *
+ *     (c) 2016 CZ.NIC z.s.p.o.
+ *
+ *     Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+CF_HDR
+
+#define PARSER 1
+
+#include "nest/bird.h"
+#include "conf/conf.h"
+#include "lib/resource.h"
+#include "lib/socket.h"
+#include "sysdep/unix/timer.h"
+#include "lib/string.h"
+#include "nest/protocol.h"
+#include "nest/iface.h"
+#include "nest/route.h"
+#include "nest/cli.h"
+#include "filter/filter.h"
+#include "lib/flowspec.h"
+
+
+CF_DEFINES
+
+struct flow_builder *this_flow;
+
+
+CF_DECLS
+
+%type <i32> flow_num_op flow_srcdst flow_logic_op flow_num_type_ flow_frag_val flow_neg
+%type <net_ptr> net_flow4_ net_flow6_ net_flow_
+
+CF_KEYWORDS(FLOW4, FLOW6, DST, SRC, PROTO, NEXT, HEADER, DPORT, SPORT, ICMP,
+           TYPE, CODE, TCP, FLAGS, LENGTH, DSCP, DONT_FRAGMENT, IS_FRAGMENT,
+           FIRST_FRAGMENT, LAST_FRAGMENT, FRAGMENT, LABEL, OFFSET)
+
+
+CF_GRAMMAR
+
+/* Network Flow Specification */
+
+flow_num_op:
+   TRUE                { $$ = 0b000; }
+ | '='         { $$ = 0b001; }
+ | NEQ         { $$ = 0b110; }
+ | '<'         { $$ = 0b100; }
+ | LEQ         { $$ = 0b101; }
+ | '>'         { $$ = 0b010; }
+ | GEQ         { $$ = 0b011; }
+ | FALSE       { $$ = 0b111; }
+ ;
+
+flow_logic_op:
+   OR          { $$ = 0x00; }
+ | AND         { $$ = 0x40; }
+ ;
+
+flow_num_type_:
+   PROTO       { $$ = FLOW_TYPE_IP_PROTOCOL; }
+ | NEXT HEADER  { $$ = FLOW_TYPE_NEXT_HEADER; }
+ | PORT                { $$ = FLOW_TYPE_PORT; }
+ | DPORT       { $$ = FLOW_TYPE_DST_PORT; }
+ | SPORT       { $$ = FLOW_TYPE_SRC_PORT; }
+ | ICMP TYPE   { $$ = FLOW_TYPE_ICMP_TYPE; }
+ | ICMP CODE   { $$ = FLOW_TYPE_ICMP_CODE; }
+ | LENGTH      { $$ = FLOW_TYPE_PACKET_LENGTH; }
+ | DSCP                { $$ = FLOW_TYPE_DSCP; }
+ ;
+
+flow_num_type: flow_num_type_{ flow_builder_set_type(this_flow, $1); };
+flow_flag_type: TCP FLAGS    { flow_builder_set_type(this_flow, FLOW_TYPE_TCP_FLAGS); };
+flow_frag_type: FRAGMENT     { flow_builder_set_type(this_flow, FLOW_TYPE_FRAGMENT); };
+flow_label_type: LABEL       { flow_builder_set_type(this_flow, FLOW_TYPE_LABEL); };
+
+flow_srcdst:
+   DST         { $$ = FLOW_TYPE_DST_PREFIX; }
+ | SRC         { $$ = FLOW_TYPE_SRC_PREFIX; }
+ ;
+
+flow_num_opts:
+   flow_num_op expr {
+     flow_check_cf_value_length(this_flow, $2);
+     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_builder_add_op_val(this_flow, $2 | $3, $4);
+   }
+ | flow_num_opt_ext
+ | flow_num_opts OR flow_num_opt_ext
+ ;
+
+flow_num_opt_ext_expr:
+   expr {
+     flow_check_cf_value_length(this_flow, $1);
+     flow_builder_add_op_val(this_flow, 0b001, $1);
+   }
+ | expr DDOT expr {
+     flow_check_cf_value_length(this_flow, $1);
+     flow_check_cf_value_length(this_flow, $3);
+     flow_builder_add_op_val(this_flow, 0b011, $1); /* >= */
+     flow_builder_add_op_val(this_flow, 0x40 | 0b101, $3); /* AND <= */
+   }
+ ;
+
+flow_num_opt_ext:
+   flow_num_opt_ext_expr
+ | flow_num_opt_ext ',' flow_num_opt_ext_expr
+ ;
+
+flow_bmk_opts:
+   flow_neg expr '/' expr {
+     flow_check_cf_bmk_values(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_check_cf_bmk_values(this_flow, $3, $4, $6);
+     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_builder_add_val_mask(this_flow, 0x40 | $3, $4, $6); /* AND */
+   }
+ ;
+
+flow_neg:
+   /* empty */         { $$ = 0x00; }
+ | '!'         { $$ = 0x02; }
+ ;
+
+flow_frag_val:
+   DONT_FRAGMENT  { $$ = 1; }
+ | IS_FRAGMENT   { $$ = 2; }
+ | FIRST_FRAGMENT { $$ = 4; }
+ | LAST_FRAGMENT  { $$ = 8; }
+ ;
+
+flow_frag_opts:
+   flow_neg flow_frag_val {
+     flow_builder_add_val_mask(this_flow, 0, ($1 ? 0 : $2), $2);
+   }
+ | flow_frag_opts flow_logic_op flow_neg flow_frag_val {
+     flow_builder_add_val_mask(this_flow, $2, ($3 ? 0 : $4), $4);
+   }
+ | flow_frag_opts ',' flow_neg flow_frag_val {
+     flow_builder_add_val_mask(this_flow, 0x40, ($3 ? 0 : $4), $4); /* AND */
+   }
+ ;
+
+flow4_item:
+   flow_srcdst net_ip {
+     flow_builder_set_type(this_flow, $1);
+     flow_builder4_add_pfx(this_flow, (net_addr_ip4 *) &($2));
+   }
+ | flow_num_type flow_num_opts
+ | flow_flag_type flow_bmk_opts
+ | flow_frag_type flow_frag_opts
+ ;
+
+flow6_item:
+   flow_srcdst net_ip6 {
+     flow_builder_set_type(this_flow, $1);
+     flow_builder6_add_pfx(this_flow, (net_addr_ip6 *) &($2), 0);
+   }
+ | flow_srcdst net_ip6 OFFSET NUM {
+     if ($4 > $2.pxlen)
+       cf_error("Prefix offset is higher than prefix length");
+     flow_builder_set_type(this_flow, $1);
+     flow_builder6_add_pfx(this_flow, (net_addr_ip6 *) &($2), $4);
+   }
+ | flow_num_type flow_num_opts
+ | flow_flag_type flow_bmk_opts
+ | flow_frag_type flow_frag_opts
+ | flow_label_type flow_bmk_opts
+ ;
+
+flow4_opts:
+   /* empty */
+ | flow4_opts flow4_item ';'
+ ;
+
+flow6_opts:
+ /* empty */
+ | flow6_opts flow6_item ';'
+ ;
+
+flow_builder_init:
+{
+  if (this_flow == NULL)
+    this_flow = flow_builder_init(&root_pool);
+  else
+    flow_builder_clear(this_flow);
+};
+
+flow_builder_set_ipv4: { this_flow->ipv6 = 0; };
+flow_builder_set_ipv6: { this_flow->ipv6 = 1; };
+
+net_flow4_: FLOW4 '{' flow_builder_init flow_builder_set_ipv4 flow4_opts '}'
+{
+  $$ = (net_addr *) flow_builder4_finalize(this_flow, cfg_mem);
+  flow4_validate_cf((net_addr_flow4 *) $$);
+};
+
+net_flow6_: FLOW6 '{' flow_builder_init flow_builder_set_ipv6 flow6_opts '}'
+{
+  $$ = (net_addr *) flow_builder6_finalize(this_flow, cfg_mem);
+  flow6_validate_cf((net_addr_flow6 *) $$);
+};
+
+net_flow_: net_flow4_ | net_flow6_ ;
+
+
+CF_CODE
+
+CF_END
index a734b2ff57e122ff3a1a9f8040a0149b9520bd59..999fa294740c37cfd8a69f62488844e9e2256235 100644 (file)
@@ -715,6 +715,137 @@ agreement").
 
 </descrip>
 
+
+<sect>Flowspec network type
+<label id="flowspec-network-type">
+
+<p>The flow specification are rules for routers and firewalls for filtering
+purpose. It is described by <rfc id="5575">. There are 3 types of arguments:
+<m/inet4/ or <m/inet6/ prefixes, bitmasks matching expressions and numbers
+matching expressions.
+
+Bitmasks matching is written using <m/value/<cf>/</cf><m/mask/ or
+<cf/!/<m/value/<cf>/</cf><m/mask/ pairs. It means that <cf/(/<m/data/ <cf/&/
+<m/mask/<cf/)/ is or is not equal to <m/value/.
+
+Numbers matching is a matching sequence of numbers and ranges separeted by a
+commas (<cf/,/) (e.g. <cf/10,20,30/). Ranges can be written using double dots
+<cf/../ notation (e.g. <cf/80..90,120..124/). An alternative notation are
+sequence of one or more pairs of relational operators and values separated by
+logical operators <cf/&&/ or <cf/||/. Allowed relational operators are <cf/=/,
+<cf/!=/, <cf/</, <cf/<=/, <cf/>/, <cf/>=/, <cf/true/ and <cf/false/.
+
+<sect1>IPv4 Flowspec
+
+<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-proto">proto <m/numbers-match/</tag>
+       Set a matching IP protocol numbers (e.g.  <cf/proto 6/).
+
+       <tag><label id="flow-port">port <m/numbers-match/</tag>
+       Set a matching source or destination TCP/UDP port numbers (e.g.
+       <cf>port 1..1023,1194,3306</cf>).
+
+       <tag><label id="flow-dport">dport <m/numbers-match/</tag>
+       Set a mating destination port numbers (e.g. <cf>dport 49151</cf>).
+
+       <tag><label id="flow-sport">sport <m/numbers-match/</tag>
+       Set a matching source port numbers (e.g. <cf>sport = 0</cf>).
+
+       <tag><label id="flow-icmp-type">icmp type <m/numbers-match/</tag>
+       Set a matching type field number of an ICMP packet (e.g. <cf>icmp type
+       3</cf>)
+
+       <tag><label id="flow-icmp-code">icmp code <m/numbers-match/</tag>
+       Set a matching code field number of an ICMP packet (e.g. <cf>icmp code
+       1</cf>)
+
+       <tag><label id="flow-tcp-flags">tcp flags <m/bitmask-match/</tag>
+       Set a matching bitmask for TCP header flags (aka control bits) (e.g.
+       <cf>tcp flags 0x03/0x0f;</cf>).
+
+       <tag><label id="flow-length">length <m/numbers-match/</tag>
+       Set a matching packet length (e.g. <cf>length > 1500;</cf>)
+
+       <tag><label id="flow-dscp">dscp <m/numbers-match/</tag>
+       Set a matching DiffServ Code Point number (e.g. <cf>length > 1500;</cf>).
+
+       <tag><label id="flow-fragment">fragment <m/fragmentation-type/</tag>
+       Set a matching type of packet fragmentation. Allowed fragmentation
+       types are <cf/dont_fragment/, <cf/is_fragment/, <cf/first_fragment/,
+       <cf/last_fragment/ (e.g. <cf>fragment is_fragment &&
+       !dont_fragment</cf>).
+</descrip>
+
+<p><code>
+protocol static {
+       flow4;
+
+       route flow4 {
+               dst 10.0.0.0/8;
+               port > 24 && < 30 || 40..50,60..70,80 && >= 90;
+               tcp flags 0x03/0x0f;
+               length > 1024;
+               dscp = 63;
+               fragment dont_fragment, is_fragment || !first_fragment;
+       } drop;
+}
+</code>
+
+<sect1>Differences for IPv6 Flowspec
+
+<p>Flowspec IPv6 are same as Flowspec IPv4 with a few exceptions.
+<itemize>
+       <item>Prefixes <m/inet6/ can be specified not only with prefix length,
+       but with prefix <cf/offset/ <m/num/ too (e.g.
+       <cf>::1234:5678:9800:0000/101 offset 64</cf>). Offset means to don't
+       care of <m/num/ 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
+       packet fragmentation.
+</itemize>
+
+<p><descrip>
+       <tag><label id="flow6-dst">dst <m/inet6/ [offset <m/num/]</tag>
+       Set a matching destination IPv6 prefix (e.g. <cf>dst
+       ::1c77:3769:27ad:a11a/128 offset 64</cf>).
+
+       <tag><label id="flow6-src">src <m/inet6/ [offset <m/num/]</tag>
+       Set a matching source IPv6 prefix (e.g. <cf>src fe80::/64</cf>).
+
+       <tag><label id="flow6-next-header">next header <m/numbers-match/</tag>
+       Set a matching IP protocol numbers (e.g. <cf>next header != 6</cf>).
+
+       <tag><label id="flow6-label">label <m/bitmask-match/</tag>
+       Set a 20-bit bitmask for matching Flow Label field in IPv6 packets
+       (e.g. <cf>label 0x8e5/0x8e5</cf>).
+</descrip>
+
+<p><code>
+protocol static {
+       flow6;
+
+       route flow6 {
+               dst fec0:1122:3344:5566:7788:99aa:bbcc:ddee/128;
+               src 0000:0000:0000:0001:1234:5678:9800:0000/101 offset 63;
+               next header = 23;
+               sport > 24 && < 30 || = 40 || 50,60,70..80;
+               dport = 50;
+               tcp flags 0x03/0x0f, !0/0xff || 0x33/0x33;
+               fragment !is_fragment || !first_fragment;
+               label 0xaaaa/0xaaaa && 0x33/0x33;
+       } drop;
+}
+</code>
+
 <chapt>Remote control
 <label id="remote-control">
 
index 18aeaae1f3861a56f1d1ced94013d50364971130..7915e627577a61578c930916bdb3cb48526c9a1c 100644 (file)
@@ -548,6 +548,32 @@ bt_test_suite(t_prefix6_set, "Testing prefix IPv6 sets");
 
 
 
+function t_flowspec()
+prefix p;
+{
+       p = flow4 { dst 10.0.0.0/8; };
+       bt_assert(p !~ [ 10.0.0.0/8 ] );
+
+       bt_assert(format(flow4 { dst 10.0.0.0/8; proto = 23; }) = "flow4 { dst 10.0.0.0/8; proto 23; }");
+       bt_assert(format(flow6 { dst ::1/128; src ::2/127; }) = "flow6 { dst ::1/128; src ::2/127; }");
+       bt_assert(format(flow6 { next header false 42; }) = "flow6 { next header false 42; }");
+       bt_assert(format(flow6 { port 80; }) = "flow6 { port 80; }");
+       bt_assert(format(flow6 { dport > 24 && < 30 || 40..50,60..70,80 && >= 90; }) = "flow6 { dport > 24 && < 30 || 40..50,60..70,80 && >= 90; }");
+       bt_assert(format(flow6 { sport 0..0x400; }) = "flow6 { sport 0..1024; }");
+       bt_assert(format(flow6 { icmp type 80; }) = "flow6 { icmp type 80; }");
+       bt_assert(format(flow6 { icmp code 90; }) = "flow6 { icmp code 90; }");
+       bt_assert(format(flow6 { tcp flags 0x03/0x0f; }) = "flow6 { tcp flags 0x3/0x3,0x0/0xc; }");
+       bt_assert(format(flow6 { length 0..65535; }) = "flow6 { length 0..65535; }");
+       bt_assert(format(flow6 { dscp = 63; }) = "flow6 { dscp 63; }");
+       bt_assert(format(flow6 { fragment is_fragment || !first_fragment; }) = "flow6 { fragment is_fragment || !first_fragment; }");
+       bt_assert(format(flow6 { }) = "flow6 { }");
+}
+
+bt_test_suite(t_flowspec, "Testing flowspec routes");
+
+
+
+
 /*
  *     Testing Paths
  *     -------------
diff --git a/lib/Doc b/lib/Doc
index 632bf1f69712ef6d8e125da99d6ec03c81cae88d..3877f3a38c05e551f09de1458a433d11fea4fc1a 100644 (file)
--- a/lib/Doc
+++ b/lib/Doc
@@ -3,6 +3,7 @@ S ip.c
 S lists.c
 S checksum.c bitops.c patmatch.c printf.c xmalloc.c tbf.c
 S mac.c
+S flowspec.c
 D resource.sgml
 S resource.c
 S mempool.c
index a7da980259b46cb94d4d43141d6a90e95dfa2eb5..acfe81aca16ba53ddd1c942f5a086b01a3966404 100644 (file)
@@ -2,10 +2,10 @@ src := bitops.c checksum.c ip.c lists.c md5.c net.c patmatch.c printf.c sha1.c s
 obj := $(src-o-files)
 $(all-client)
 
-src := bitops.c checksum.c event.c idm.c ip.c lists.c mac.c md5.c mempool.c net.c patmatch.c printf.c resource.c sha1.c sha256.c sha512.c slab.c slists.c tbf.c xmalloc.c
+src := bitops.c checksum.c event.c flowspec.c idm.c ip.c lists.c mac.c md5.c mempool.c net.c patmatch.c printf.c resource.c sha1.c sha256.c sha512.c slab.c slists.c tbf.c xmalloc.c
 obj := $(src-o-files)
 $(all-daemon)
 
-tests_src := heap_test.c buffer_test.c event_test.c bitops_test.c patmatch_test.c fletcher16_test.c slist_test.c checksum_test.c lists_test.c mac_test.c ip_test.c hash_test.c printf_test.c
+tests_src := heap_test.c buffer_test.c event_test.c flowspec_test.c bitops_test.c patmatch_test.c fletcher16_test.c slist_test.c checksum_test.c lists_test.c mac_test.c ip_test.c hash_test.c printf_test.c
 tests_targets := $(tests_targets) $(tests-target-files)
 tests_objs := $(tests_objs) $(src-o-files)
index bb19df548e9769e48d87d873c76b1fbf55ee71b6..c82082c1f83900dd580f085603a8fd8846b4072e 100644 (file)
@@ -34,6 +34,7 @@
 #define ABS(a)   ((a)>=0 ? (a) : -(a))
 #define DELTA(a,b) (((a)>=(b))?(a)-(b):(b)-(a))
 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(*(a)))
+#define BYTES(n) ((((uint) (n)) + 7) / 8)
 #define CALL(fn, args...) ({ if (fn) fn(args); })
 #define ADVANCE(w, r, l) ({ r -= l; w += l; })
 
index 2a53f2118b8882ff089bdcca7a67fad6574d4568..a8b1195162a61f433c422ab549ef8779696ab32c 100644 (file)
 
 #define BUFFER_FLUSH(v)                ({ (v).used = 0; })
 
+#define BUFFER_SHALLOW_COPY(dst, src)                                  \
+  ({                                                                   \
+    (dst).used = (src).used;                                           \
+    (dst).size = (src).size;                                           \
+    (dst).data = (src).data;                                           \
+  })
+
 #endif /* _BIRD_BUFFER_H_ */
diff --git a/lib/flowspec.c b/lib/flowspec.c
new file mode 100644 (file)
index 0000000..b72bc7f
--- /dev/null
@@ -0,0 +1,1148 @@
+/*
+ *     BIRD Library -- Flow specification (RFC 5575)
+ *
+ *     (c) 2016 CZ.NIC z.s.p.o.
+ *
+ *     Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+/**
+ * DOC: Flow specification (flowspec)
+ *
+ * Flowspec are rules (RFC 5575) for firewalls disseminated using BGP protocol.
+ * The |flowspec.c| is a library for handling flowspec binary streams and
+ * flowspec data structures. You will find there functions for validation
+ * incoming flowspec binary streams, iterators for jumping over components,
+ * functions for handling a length and functions for formatting flowspec data
+ * structure into user-friendly text representation.
+ *
+ * In this library, you will find also flowspec builder. In |confbase.Y|, there
+ * are grammar's rules for parsing and building new flowspec data structure
+ * from BIRD's configuration files and from BIRD's command line interface.
+ * Finalize function will assemble final &net_addr_flow4 or &net_addr_flow6
+ * data structure.
+ *
+ * The data structures &net_addr_flow4 and &net_addr_flow6 are defined in
+ * |net.h| file. The attribute length is size of whole data structure plus
+ * binary stream representation of flowspec including a compressed encoded
+ * length of flowspec.
+ *
+ * Sometimes in code, it is used expression flowspec type, it should mean
+ * flowspec component type.
+ */
+
+#include "nest/bird.h"
+#include "lib/flowspec.h"
+#include "conf/conf.h"
+
+
+static const char* flow4_type_str[] = {
+  [FLOW_TYPE_DST_PREFIX]       = "dst",
+  [FLOW_TYPE_SRC_PREFIX]       = "src",
+  [FLOW_TYPE_IP_PROTOCOL]      = "proto",
+  [FLOW_TYPE_PORT]             = "port",
+  [FLOW_TYPE_DST_PORT]         = "dport",
+  [FLOW_TYPE_SRC_PORT]         = "sport",
+  [FLOW_TYPE_ICMP_TYPE]                = "icmp type",
+  [FLOW_TYPE_ICMP_CODE]                = "icmp code",
+  [FLOW_TYPE_TCP_FLAGS]                = "tcp flags",
+  [FLOW_TYPE_PACKET_LENGTH]    = "length",
+  [FLOW_TYPE_DSCP]             = "dscp",
+  [FLOW_TYPE_FRAGMENT]         = "fragment"
+};
+
+static const char* flow6_type_str[] = {
+  [FLOW_TYPE_DST_PREFIX]       = "dst",
+  [FLOW_TYPE_SRC_PREFIX]       = "src",
+  [FLOW_TYPE_NEXT_HEADER]      = "next header",
+  [FLOW_TYPE_PORT]             = "port",
+  [FLOW_TYPE_DST_PORT]         = "dport",
+  [FLOW_TYPE_SRC_PORT]         = "sport",
+  [FLOW_TYPE_ICMP_TYPE]                = "icmp type",
+  [FLOW_TYPE_ICMP_CODE]                = "icmp code",
+  [FLOW_TYPE_TCP_FLAGS]                = "tcp flags",
+  [FLOW_TYPE_PACKET_LENGTH]    = "length",
+  [FLOW_TYPE_DSCP]             = "dscp",
+  [FLOW_TYPE_FRAGMENT]         = "fragment",
+  [FLOW_TYPE_LABEL]            = "label"
+};
+
+/**
+ * flow_type_str - get stringified flowspec name of component
+ * @type: flowspec component type
+ * @ipv6: IPv4/IPv6 decide flag, use zero for IPv4 and one for IPv6
+ *
+ * This function returns flowspec name of component @type in string.
+ */
+const char *
+flow_type_str(enum flow_type type, int ipv6)
+{
+  return ipv6 ? flow6_type_str[type] : flow4_type_str[type];
+}
+
+/*
+ *     Length
+ */
+
+/**
+ * flow_write_length - write compressed length value
+ * @data: destination buffer to write
+ * @len: the value of the length (0 to 0xfff) for writing
+ *
+ * This function writes appropriate as (1- or 2-bytes) the value of @len into
+ * buffer @data. The function returns number of written bytes, thus 1 or 2 bytes.
+ */
+uint
+flow_write_length(byte *data, u16 len)
+{
+  if (len >= 0xf0)
+  {
+    put_u16(data, len | 0xf000);
+    return 2;
+  }
+
+  *data = len;
+  return 1;
+}
+
+inline static uint
+get_value_length(const byte *op)
+{
+  return (1 << ((*op & 0x30) >> 4));
+}
+
+
+
+/*
+ *     Flowspec iterators
+ */
+
+static inline u8  num_op(const byte *op)    { return  (*op & 0x07); }
+static inline int isset_and(const byte *op) { return ((*op & 0x40) == 0x40); }
+static inline int isset_end(const byte *op) { return ((*op & 0x80) == 0x80); }
+
+static const byte *
+flow_first_part(const byte *data)
+{
+  if (!data || flow_read_length(data) == 0)
+    return NULL;
+
+  /* It is allowed to encode the value of length less then 240 into 2-bytes too */
+  if ((data[0] & 0xf0) == 0xf0)
+    return data + 2;
+
+  return data + 1;
+}
+
+/**
+ * flow4_first_part - get position of the first flowspec component
+ * @f: flowspec data structure &net_addr_flow4
+ *
+ * This function return a position to the beginning of the first flowspec
+ * component in IPv4 flowspec @f.
+ */
+inline const byte *
+flow4_first_part(const net_addr_flow4 *f)
+{
+  return f ? flow_first_part(f->data) : NULL;
+}
+
+/**
+ * flow6_first_part - get position of the first flowspec component
+ * @f: flowspec data structure &net_addr_flow6
+ *
+ * This function return a position to the beginning of the first flowspec
+ * component in IPv6 flowspec @f.
+ */
+inline const byte *
+flow6_first_part(const net_addr_flow6 *f)
+{
+  return f ? flow_first_part(f->data) : NULL;
+}
+
+static const byte *
+flow_next_part(const byte *pos, const byte *end, int ipv6)
+{
+  switch (*pos++)
+  {
+  case FLOW_TYPE_DST_PREFIX:
+  case FLOW_TYPE_SRC_PREFIX:
+  {
+    uint pxlen = *pos++;
+    uint bytes = BYTES(pxlen);
+    if (ipv6)
+    {
+      uint offset = *pos++ / 8;
+      pos += bytes - offset;
+    }
+    else
+    {
+      pos += bytes;
+    }
+    break;
+  }
+
+  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:
+  case FLOW_TYPE_LABEL:
+  {
+    /* Is this the end of list operator-value pair? */
+    uint last = 0;
+
+    while (!last)
+    {
+      last = isset_end(pos);
+
+      /* Value length of operator */
+      uint len = get_value_length(pos);
+      pos += 1+len;
+    }
+    break;
+  }
+  default:
+    return NULL;
+  }
+
+  return (pos < end) ? pos : NULL;
+}
+
+/**
+ * flow4_next_part - an iterator over flowspec components in flowspec binary stream
+ * @pos: the beginning of a previous or the first component in flowspec binary
+ *       stream
+ * @end: the last valid byte in scanned flowspec binary stream
+ *
+ * This function returns a position to the beginning of the next component
+ * (to a component type byte) in flowspec binary stream or %NULL for the end.
+ */
+inline const byte *
+flow4_next_part(const byte *pos, const byte *end)
+{
+  return flow_next_part(pos, end, 0);
+}
+
+/**
+ * flow6_next_part - an iterator over flowspec components in flowspec binary stream
+ * @pos: the beginning of a previous or the first component in flowspec binary
+ *       stream
+ * @end: the last valid byte in scanned flowspec binary stream
+ *
+ * This function returns a position to the beginning of the next component
+ * (to a component type byte) in flowspec binary stream or %NULL for the end.
+ */
+inline const byte *
+flow6_next_part(const byte *pos, const byte *end)
+{
+  return flow_next_part(pos, end, 1);
+}
+
+
+/*
+ *     Flowspec validation
+ */
+
+static const char* flow_validated_state_str_[] = {
+  [FLOW_ST_UNKNOWN_COMPONENT]          = "Unknown component",
+  [FLOW_ST_VALID]                      = "Valid",
+  [FLOW_ST_NOT_COMPLETE]               = "Not complete",
+  [FLOW_ST_EXCEED_MAX_PREFIX_LENGTH]   = "Exceed maximal prefix length",
+  [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 required to define",
+  [FLOW_ST_CANNOT_USE_DONT_FRAGMENT]    = "Cannot use Don't fragment flag in IPv6 flow"
+};
+
+/**
+ * flow_validated_state_str - return a textual description of validation process
+ * @code: validation result
+ *
+ * This function return well described validation state in string.
+ */
+const char *
+flow_validated_state_str(enum flow_validated_state 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,
+  [FLOW_TYPE_TCP_FLAGS]                = 2,
+  [FLOW_TYPE_PACKET_LENGTH]    = 2,
+  [FLOW_TYPE_DSCP]             = 1,
+  [FLOW_TYPE_FRAGMENT]         = 1     /* XXX */
+};
+
+static const u8 flow6_max_value_length[] = {
+  [FLOW_TYPE_DST_PREFIX]       = 0,
+  [FLOW_TYPE_SRC_PREFIX]       = 0,
+  [FLOW_TYPE_NEXT_HEADER]      = 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_FRAGMENT]         = 1,    /* XXX */
+  [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
+ * @fb: flow builder instance
+ * @neg: negation operand
+ * @val: value from value/mask pair
+ * @mask: bitmap mask from value/mask pair
+ *
+ * This function checks value/bitmask pair. If some problem will appear, the
+ * function calls cf_error() function with a textual description of reason
+ * to failing of validation.
+ */
+void
+flow_check_cf_bmk_values(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 (fb->this_type == FLOW_TYPE_FRAGMENT && fb->ipv6 && (mask & 0x01))
+    cf_error("Invalid mask 0x%x. Bit 0 must be 0", 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
+ * @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.
+ */
+void
+flow_check_cf_value_length(struct flow_builder *fb, u32 val)
+{
+  enum flow_type t = fb->this_type;
+  u8 max = flow_max_value_length(t, fb->ipv6);
+
+  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 (max == 1 && (val > 0xff))
+    cf_error("%s value %u out of range (0-255)", flow_type_str(t, fb->ipv6), val);
+
+  if (max == 2 && (val > 0xffff))
+    cf_error("%s value %u out of range (0-65535)", flow_type_str(t, fb->ipv6), val);
+}
+
+static enum flow_validated_state
+flow_validate(const byte *nlri, uint len, int ipv6)
+{
+  enum flow_type type = 0;
+  const byte *pos = nlri;
+  const byte *end = nlri + len;
+  int met_dst_pfx = 0;
+
+  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:
+      met_dst_pfx = 1;
+      /* Fall through */
+    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 bytes = BYTES(pxlen);
+      if (ipv6)
+      {
+        uint pxoffset = *pos++;
+        if (pxoffset > IP6_MAX_PREFIX_LENGTH || pxoffset > pxlen)
+          return FLOW_ST_EXCEED_MAX_PREFIX_OFFSET;
+        bytes -= pxoffset / 8;
+      }
+      pos += bytes;
+
+      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:
+    {
+      uint last = 0;
+      uint first = 1;
+
+      while (!last)
+      {
+       /*
+        *    0   1   2   3   4   5   6   7
+        *  +---+---+---+---+---+---+---+---+
+        *  | e | a |  len  | 0 |lt |gt |eq |
+        *  +---+---+---+---+---+---+---+---+
+        *
+        *           Numeric operator
+        */
+
+       last = isset_end(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;
+
+       /* This bit should be zero */
+       if (*pos & 0x08)
+         return FLOW_ST_ZERO_BIT_SHOULD_BE_UNSED;
+
+       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;
+       }
+
+       /* Bit-7 must be 0 [draft-ietf-idr-flow-spec-v6] */
+       if (ipv6 && type == FLOW_TYPE_FRAGMENT && (*(pos+1) & 0x01))
+         return FLOW_ST_CANNOT_USE_DONT_FRAGMENT;
+       /* XXX: Could be a fragment component encoded in 2-bytes? */
+
+       /* 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;
+       pos += 1+len;
+
+       if (pos > end && !last)
+         return FLOW_ST_NOT_COMPLETE;
+
+       if (pos > (end+1))
+         return FLOW_ST_NOT_COMPLETE;
+
+       first = 0;
+      }
+      break;
+    }
+    default:
+      return FLOW_ST_UNKNOWN_COMPONENT;
+    }
+  }
+
+  if (pos != end)
+    return FLOW_ST_NOT_COMPLETE;
+
+  if (!ipv6 && !met_dst_pfx)
+    return FLOW_ST_DEST_PREFIX_REQUIRED;
+
+  return FLOW_ST_VALID;
+}
+
+/**
+ * flow4_validate - check untrustworthy IPv4 flowspec data stream
+ * @nlri: flowspec data stream without compressed encoded length value
+ * @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.
+ */
+inline enum flow_validated_state
+flow4_validate(const byte *nlri, uint len)
+{
+  return flow_validate(nlri, len, 0);
+}
+
+/**
+ * flow6_validate - check untrustworthy IPv6 flowspec data stream
+ * @nlri: flowspec binary stream without encoded length value
+ * @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.
+ */
+inline enum flow_validated_state
+flow6_validate(const byte *nlri, uint len)
+{
+  return flow_validate(nlri, len, 1);
+}
+
+/**
+ * flow4_validate_cf - validate flowspec data structure &net_addr_flow4 in parsing time
+ * @f: flowspec data structure &net_addr_flow4
+ *
+ * Check if @f is valid flowspec data structure. Can call cf_error() function
+ * with a textual description of reason to failing of validation.
+ */
+void
+flow4_validate_cf(net_addr_flow4 *f)
+{
+  enum flow_validated_state r = flow4_validate(flow4_first_part(f), flow_read_length(f->data));
+
+  if (r != FLOW_ST_VALID)
+    cf_error("Invalid flow route: %s", flow_validated_state_str(r));
+}
+
+/**
+ * flow6_validate_cf - validate flowspec data structure &net_addr_flow6 in parsing time
+ * @f: flowspec data structure &net_addr_flow6
+ *
+ * Check if @f is valid flowspec data structure. Can call cf_error() function
+ * with a textual description of reason to failing of validation.
+ */
+void
+flow6_validate_cf(net_addr_flow6 *f)
+{
+  enum flow_validated_state r = flow6_validate(flow6_first_part(f), flow_read_length(f->data));
+
+  if (r != FLOW_ST_VALID)
+    cf_error("Invalid flow route: %s", flow_validated_state_str(r));
+}
+
+
+/*
+ *     Flowspec Builder
+ */
+
+/**
+ * flow_builder_init - constructor for flowspec builder instance
+ * @pool: memory pool
+ *
+ * This function prepares flowspec builder instance using memory pool @pool.
+ */
+struct flow_builder *
+flow_builder_init(pool *pool)
+{
+  struct flow_builder *fb = mb_allocz(pool, sizeof(struct flow_builder));
+  BUFFER_INIT(fb->data, pool, 4);
+  return fb;
+}
+
+static int
+is_stackable_type(enum flow_type type)
+{
+  switch (type)
+  {
+  case FLOW_TYPE_IP_PROTOCOL:
+  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:
+  case FLOW_TYPE_LABEL:
+    return 1;
+
+  default:
+    /* The unknown components are not stack-able in default */
+    return 0;
+  }
+}
+
+static int
+builder_add_prepare(struct flow_builder *fb)
+{
+  if (fb->parts[fb->this_type].length)
+  {
+    if (fb->last_type != fb->this_type)
+      return 0;
+
+    if (!is_stackable_type(fb->this_type))
+      return 0;
+  }
+  else
+  {
+    fb->parts[fb->this_type].offset = fb->data.used;
+  }
+
+  return 1;
+}
+
+static void
+builder_add_finish(struct flow_builder *fb)
+{
+  fb->parts[fb->this_type].length = fb->data.used - fb->parts[fb->this_type].offset;
+  flow_builder_set_type(fb, fb->this_type);
+}
+
+static void
+push_pfx_to_buffer(struct flow_builder *fb, u8 pxlen_bytes, byte *ip)
+{
+  for (int i = 0; i < pxlen_bytes; i++)
+    BUFFER_PUSH(fb->data) = *ip++;
+}
+
+/**
+ * flow_builder4_add_pfx - add IPv4 prefix
+ * @fb: flowspec builder instance
+ * @n4: net address of type IPv4
+ *
+ * This function add IPv4 prefix into flowspec builder instance.
+ */
+int
+flow_builder4_add_pfx(struct flow_builder *fb, const net_addr_ip4 *n4)
+{
+  if (!builder_add_prepare(fb))
+    return 0;
+
+  ip4_addr ip4 = ip4_hton(n4->prefix);
+
+  BUFFER_PUSH(fb->data) = fb->this_type;
+  BUFFER_PUSH(fb->data) = n4->pxlen;
+  push_pfx_to_buffer(fb, BYTES(n4->pxlen), (byte *) &ip4);
+
+  builder_add_finish(fb);
+  return 1;
+}
+
+/**
+ * flow_builder6_add_pfx - add IPv6 prefix
+ * @fb: flowspec builder instance
+ * @n6: net address of type IPv4
+ * @pxoffset: prefix offset for @n6
+ *
+ * This function add IPv4 prefix into flowspec builder instance. This function
+ * should return 1 for successful adding, otherwise returns %0.
+ */
+int
+flow_builder6_add_pfx(struct flow_builder *fb, const net_addr_ip6 *n6, u32 pxoffset)
+{
+  if (!builder_add_prepare(fb))
+    return 0;
+
+  ip6_addr ip6 = ip6_hton(n6->prefix);
+
+  BUFFER_PUSH(fb->data) = fb->this_type;
+  BUFFER_PUSH(fb->data) = n6->pxlen;
+  BUFFER_PUSH(fb->data) = pxoffset;
+  push_pfx_to_buffer(fb, BYTES(n6->pxlen) - (pxoffset / 8), ((byte *) &ip6) + (pxoffset / 8));
+
+  builder_add_finish(fb);
+  return 1;
+}
+
+/**
+ * flow_builder_add_op_val - add operator/value pair
+ * @fb: flowspec builder instance
+ * @op: operator
+ * @value: value
+ *
+ * This function add operator/value pair as a part of a flowspec component. It
+ * is required to set appropriate flowspec component type using function
+ * flow_builder_set_type(). This function should return 1 for successful
+ * adding, otherwise returns 0.
+ */
+int
+flow_builder_add_op_val(struct flow_builder *fb, byte op, u32 value)
+{
+  if (!builder_add_prepare(fb))
+    return 0;
+
+  if (fb->this_type == fb->last_type)
+  {
+    /* Remove the end-bit from last operand-value pair of the component */
+    fb->data.data[fb->last_op_offset] &= 0x7f;
+  }
+  else
+  {
+    BUFFER_PUSH(fb->data) = fb->this_type;
+  }
+
+  fb->last_op_offset = fb->data.used;
+
+  /* Set the end-bit for operand-value pair of the component */
+  op |= 0x80;
+
+  if (value & 0xff00)
+  {
+    BUFFER_PUSH(fb->data) = op | 0x10;
+    put_u16(BUFFER_INC(fb->data, 2), value);
+  }
+  else
+  {
+    BUFFER_PUSH(fb->data) = op;
+    BUFFER_PUSH(fb->data) = (u8) value;
+  }
+
+  builder_add_finish(fb);
+  return 1;
+}
+
+/**
+ * flow_builder_add_val_mask - add value/bitmask pair
+ * @fb: flowspec builder instance
+ * @op: operator
+ * @value: value
+ * @mask: bitmask
+ *
+ * It is required to set appropriate flowspec component type using function
+ * flow_builder_set_type(). This function should return 1 for successful adding,
+ * otherwise returns 0.
+ */
+int
+flow_builder_add_val_mask(struct flow_builder *fb, byte op, u32 value, u32 mask)
+{
+  u32 a =  value & mask;
+  u32 b = ~value & mask;
+
+  if (a)
+  {
+    flow_builder_add_op_val(fb, op ^ 0x01, a);
+    op |= 0x40;
+  }
+
+  if (b)
+    flow_builder_add_op_val(fb, op ^ 0x02, b);
+
+  return 1;
+}
+
+
+/**
+ * flow_builder_set_type - set type of next flowspec component
+ * @fb: flowspec builder instance
+ * @type: flowspec component type
+ *
+ * This function sets type of next flowspec component. It is necessary to call
+ * this function before each changing of adding flowspec component.
+ */
+void
+flow_builder_set_type(struct flow_builder *fb, enum flow_type type)
+{
+  fb->last_type = fb->this_type;
+  fb->this_type = type;
+}
+
+static ip4_addr
+flow_read_ip4(const byte *px, uint pxlen)
+{
+  ip4_addr ip = IP4_NONE;
+  memcpy(&ip, px, BYTES(pxlen));
+  return ip4_ntoh(ip);
+}
+
+static ip6_addr
+flow_read_ip6(const byte *px, uint pxlen, uint pxoffset)
+{
+  uint floor_offset = BYTES(pxoffset - (pxoffset % 8));
+  uint ceil_len = BYTES(pxlen);
+  ip6_addr ip = IP6_NONE;
+
+  memcpy(((byte *) &ip) + floor_offset, px, ceil_len - floor_offset);
+
+  return ip6_ntoh(ip);
+}
+
+static void
+builder_write_parts(struct flow_builder *fb, byte *buf)
+{
+  for (int i = 1; i < FLOW_TYPE_MAX; i++)
+  {
+    if (fb->parts[i].length)
+    {
+      memcpy(buf, fb->data.data + fb->parts[i].offset, fb->parts[i].length);
+      buf += fb->parts[i].length;
+    }
+  }
+}
+
+/**
+ * flow_builder4_finalize - assemble final flowspec data structure &net_addr_flow4
+ * @fb: flowspec builder instance
+ * @lpool: linear memory pool
+ *
+ * This function returns final flowspec data structure &net_addr_flow4 allocated
+ * onto @lpool linear memory pool.
+ */
+net_addr_flow4 *
+flow_builder4_finalize(struct flow_builder *fb, linpool *lpool)
+{
+  uint data_len = fb->data.used + (fb->data.used < 0xf0 ? 1 : 2);
+  net_addr_flow4 *f = lp_alloc(lpool, sizeof(struct net_addr_flow4) + data_len);
+
+  ip4_addr prefix = IP4_NONE;
+  uint pxlen = 0;
+
+  if (fb->parts[FLOW_TYPE_DST_PREFIX].length)
+  {
+    byte *p = fb->data.data + fb->parts[FLOW_TYPE_DST_PREFIX].offset + 1;
+    pxlen = *p++;
+    prefix = flow_read_ip4(p, pxlen);
+  }
+  *f = NET_ADDR_FLOW4(prefix, pxlen, data_len);
+
+  builder_write_parts(fb, f->data + flow_write_length(f->data, fb->data.used));
+
+  return f;
+}
+
+/**
+ * flow_builder6_finalize - assemble final flowspec data structure &net_addr_flow6
+ * @fb: flowspec builder instance
+ * @lpool: linear memory pool for allocation of
+ *
+ * This function returns final flowspec data structure &net_addr_flow6 allocated
+ * onto @lpool linear memory pool.
+ */
+net_addr_flow6 *
+flow_builder6_finalize(struct flow_builder *fb, linpool *lpool)
+{
+  uint data_len =  fb->data.used + (fb->data.used < 0xf0 ? 1 : 2);
+  net_addr_flow6 *n = lp_alloc(lpool, sizeof(net_addr_flow6) + data_len);
+
+  ip6_addr prefix = IP6_NONE;
+  uint pxlen = 0;
+
+  if (fb->parts[FLOW_TYPE_DST_PREFIX].length)
+  {
+    byte *p = fb->data.data + fb->parts[FLOW_TYPE_DST_PREFIX].offset + 1;
+    pxlen = *p++;
+    uint pxoffset = *p++;
+    prefix = flow_read_ip6(p, pxlen, pxoffset);
+  }
+  *n = NET_ADDR_FLOW6(prefix, pxlen, data_len);
+
+  builder_write_parts(fb, n->data + flow_write_length(n->data, fb->data.used));
+
+  return n;
+}
+
+/**
+ * flow_builder_clear - flush flowspec builder instance for another flowspec creation
+ * @fb: flowspec builder instance
+ *
+ * This function flushes all data from builder but it maintains pre-allocated
+ * buffer space.
+ */
+void
+flow_builder_clear(struct flow_builder *fb)
+{
+  BUFFER(byte) data;
+  BUFFER_FLUSH(fb->data);
+
+  BUFFER_SHALLOW_COPY(data, fb->data);
+  memset(fb, 0, sizeof(struct flow_builder));
+  BUFFER_SHALLOW_COPY(fb->data, data);
+}
+
+
+/*
+ *     Net Formatting
+ */
+
+/* Flowspec operators for [op, value]+ pairs */
+#define FLOW_TRUE      0b000
+#define FLOW_EQ                0b001
+#define FLOW_GT                0b010
+#define FLOW_GTE       0b011
+#define FLOW_LT                0b100
+#define FLOW_LTE       0b101
+#define FLOW_NEQ       0b110
+#define FLOW_FALSE     0b111
+
+static const char *
+num_op_str(const byte *op)
+{
+  switch (*op & 0x07)
+  {
+  case FLOW_TRUE:      return "true";
+  case FLOW_EQ:        return "=";
+  case FLOW_GT:        return ">";
+  case FLOW_GTE:       return ">=";
+  case FLOW_LT:        return "<";
+  case FLOW_LTE:       return "<=";
+  case FLOW_NEQ:       return "!=";
+  case FLOW_FALSE:     return "false";
+  }
+
+  return NULL;
+}
+
+static u64
+get_value(const byte *val, u8 len)
+{
+  switch (len)
+  {
+  case 1: return *val;
+  case 2: return get_u16(val);
+  case 4: return get_u32(val);
+  case 8: return get_u64(val);
+  }
+
+  return 0;
+}
+
+static int
+is_bitmask(enum flow_type type)
+{
+  switch (type)
+  {
+  case FLOW_TYPE_TCP_FLAGS:
+  case FLOW_TYPE_FRAGMENT:
+  case FLOW_TYPE_LABEL:
+    return 1;
+
+  default:
+    return 0;
+  }
+}
+
+static const char *
+fragment_val_str(u8 val)
+{
+  switch (val)
+  {
+  case 1: return "dont_fragment";
+  case 2: return "is_fragment";
+  case 4: return "first_fragment";
+  case 8: return "last_fragment";
+  }
+  return "???";
+}
+
+static int
+net_format_flow(char *buf, uint blen, const byte *data, uint dlen, int ipv6)
+{
+  buffer b = {
+    .start = buf,
+    .pos = buf,
+    .end = buf + blen,
+  };
+
+  const byte *part = flow_first_part(data);
+  *buf = 0;
+
+  if (ipv6)
+    buffer_puts(&b, "flow6 { ");
+  else
+    buffer_puts(&b, "flow4 { ");
+
+  while (part)
+  {
+    buffer_print(&b, "%s ", flow_type_str(*part, ipv6));
+
+    switch (*part)
+    {
+    case FLOW_TYPE_DST_PREFIX:
+    case FLOW_TYPE_SRC_PREFIX:
+    {
+      uint pxlen = *(part+1);
+      if (ipv6)
+      {
+       uint pxoffset = *(part+2);
+       if (pxoffset)
+         buffer_print(&b, "%I6/%u offset %u; ", flow_read_ip6(part+3,pxlen,pxoffset), pxlen, pxoffset);
+       else
+         buffer_print(&b, "%I6/%u; ", flow_read_ip6(part+3,pxlen,0), pxlen);
+      }
+      else
+      {
+       buffer_print(&b, "%I4/%u; ", flow_read_ip4(part+2,pxlen), pxlen);
+      }
+      break;
+    }
+
+    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:
+    case FLOW_TYPE_LABEL:
+    {
+      const byte *last_op = NULL;
+      const byte *op = part+1;
+      u64 val;
+      uint len;
+      uint first = 1;
+
+      while (1)
+      {
+       if (!first)
+       {
+         /* XXX: I don't like this so complicated if-tree */
+         if (!isset_and(op) && !is_bitmask(*part) &&
+             ((num_op(     op) == FLOW_EQ) || (num_op(     op) == FLOW_GTE)) &&
+             ((num_op(last_op) == FLOW_EQ) || (num_op(last_op) == FLOW_LTE)))
+         {
+           b.pos--; /* Remove last char (it is a space) */
+           buffer_puts(&b, ",");
+         }
+         else if (isset_and(op) && is_bitmask(*part))
+         {
+           b.pos--; /* Remove last char (it is a space) */
+           buffer_puts(&b, ",");
+         }
+         else
+         {
+           buffer_puts(&b, isset_and(op) ? "&& " : "|| ");
+         }
+       }
+       first = 0;
+
+       len = get_value_length(op);
+       val = get_value(op+1, len);
+
+       if (is_bitmask(*part))
+       {
+         /*
+          *   Not Match  Show
+          *  ------------------
+          *    0    0    !0/B
+          *    0    1     B/B
+          *    1    0     0/B
+          *    1    1    !B/B
+          */
+
+         if ((*op & 0x3) == 0x3 || (*op & 0x3) == 0)
+           buffer_puts(&b, "!");
+
+         if (*part == FLOW_TYPE_FRAGMENT && (val == 1 || val == 2 || val == 4 || val == 8))
+           buffer_print(&b, "%s%s", ((*op & 0x1) ? "" : "!"), fragment_val_str(val));
+         else
+           buffer_print(&b, "0x%x/0x%x", ((*op & 0x1) ? val : 0), val);
+       }
+       else
+       {
+         if (!isset_end(op) && !isset_and(op) && isset_and(op+1+len) &&
+             (num_op(op) == FLOW_GTE) && (num_op(op+1+len) == FLOW_LTE))
+         {
+           /* Display interval */
+           buffer_print(&b, "%u..", val);
+           op += 1 + len;
+           len = get_value_length(op);
+           val = get_value(op+1, len);
+           buffer_print(&b, "%u", val);
+         }
+         else if (num_op(op) == FLOW_EQ)
+         {
+           buffer_print(&b, "%u", val);
+         }
+         else
+         {
+           buffer_print(&b, "%s %u", num_op_str(op), val);
+         }
+       }
+
+       if (isset_end(op))
+       {
+         buffer_puts(&b, "; ");
+         break;
+       }
+       else
+       {
+         buffer_puts(&b, " ");
+       }
+
+       last_op = op;
+       op += 1 + len;
+      }
+    }
+    }
+
+    part = flow_next_part(part, data+dlen, ipv6);
+  }
+
+  buffer_puts(&b, "}");
+
+  if (b.pos == b.end)
+  {
+    b.pos = b.start + MIN(blen - 6, strlen(b.start));
+    buffer_puts(&b, " ...}");
+  }
+
+  return b.pos - b.start;
+}
+
+/**
+ * flow4_net_format - stringify flowspec data structure &net_addr_flow4
+ * @buf: pre-allocated buffer for writing a stringify net address flowspec
+ * @blen: free allocated space in @buf
+ * @f: flowspec data structure &net_addr_flow4 for stringify
+ *
+ * This function writes stringified @f into @buf. The function returns number
+ * of written chars. If final string is too large, the string will ends the with
+ * ' ...}' sequence and zero-terminator.
+ */
+int
+flow4_net_format(char *buf, uint blen, const net_addr_flow4 *f)
+{
+  return net_format_flow(buf, blen, f->data, f->length - sizeof(net_addr_flow4), 0);
+}
+
+/**
+ * flow6_net_format - stringify flowspec data structure &net_addr_flow6
+ * @buf: pre-allocated buffer for writing a stringify net address flowspec
+ * @blen: free allocated space in @buf
+ * @f: flowspec data structure &net_addr_flow4 for stringify
+ *
+ * This function writes stringified @f into @buf. The function returns number
+ * of written chars. If final string is too large, the string will ends the with
+ * ' ...}' sequence and zero-terminator.
+ */
+int
+flow6_net_format(char *buf, uint blen, const net_addr_flow6 *f)
+{
+  return net_format_flow(buf, blen, f->data, f->length - sizeof(net_addr_flow6), 1);
+}
diff --git a/lib/flowspec.h b/lib/flowspec.h
new file mode 100644 (file)
index 0000000..57809be
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ *     BIRD Library -- Flow specification (RFC 5575)
+ *
+ *     (c) 2016 CZ.NIC z.s.p.o.
+ *
+ *     Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#ifndef _BIRD_FLOWSPEC_H_
+#define _BIRD_FLOWSPEC_H_
+
+#include "nest/bird.h"
+#include "lib/buffer.h"
+#include "lib/net.h"
+
+
+/* Types of components in flowspec */
+enum flow_type {
+  FLOW_TYPE_DST_PREFIX                 =  1,
+  FLOW_TYPE_SRC_PREFIX                 =  2,
+  FLOW_TYPE_IP_PROTOCOL        =  3,
+  FLOW_TYPE_NEXT_HEADER        =  3,   /* IPv6 */
+  FLOW_TYPE_PORT               =  4,
+  FLOW_TYPE_DST_PORT           =  5,
+  FLOW_TYPE_SRC_PORT           =  6,
+  FLOW_TYPE_ICMP_TYPE          =  7,
+  FLOW_TYPE_ICMP_CODE          =  8,
+  FLOW_TYPE_TCP_FLAGS          =  9,
+  FLOW_TYPE_PACKET_LENGTH      = 10,
+  FLOW_TYPE_DSCP               = 11,   /* DiffServ Code Point */
+  FLOW_TYPE_FRAGMENT           = 12,
+  FLOW_TYPE_LABEL              = 13,   /* IPv6 */
+  FLOW_TYPE_MAX
+};
+
+const char *flow_type_str(enum flow_type type, int ipv6);
+
+
+/*
+ *     Length
+ */
+
+uint flow_write_length(byte *data, u16 len);
+
+static inline u16 flow_read_length(const byte *data)
+{ return ((*data & 0xf0) == 0xf0) ? get_u16(data) & 0x0fff : *data; }
+
+static inline u16 flow4_get_length(const net_addr_flow4 *f)
+{ return f->length - sizeof(net_addr_flow4); }
+
+static inline u16 flow6_get_length(const net_addr_flow6 *f)
+{ return f->length - sizeof(net_addr_flow6); }
+
+static inline void flow4_set_length(net_addr_flow4 *f, u16 len)
+{ f->length = sizeof(net_addr_flow4) + flow_write_length(f->data, len) + len; }
+
+static inline void flow6_set_length(net_addr_flow6 *f, u16 len)
+{ f->length = sizeof(net_addr_flow6) + flow_write_length(f->data, len) + len; }
+
+
+/*
+ *     Iterators
+ */
+
+const byte *flow4_first_part(const net_addr_flow4 *f);
+const byte *flow6_first_part(const net_addr_flow6 *f);
+const byte *flow4_next_part(const byte *pos, const byte *end);
+const byte *flow6_next_part(const byte *pos, const byte *end);
+
+
+/*
+ *     Flowspec Builder
+ */
+
+/* A data structure for keep a state of flow builder */
+struct flow_builder {
+  BUFFER(byte) data;
+  enum flow_type this_type;
+  enum flow_type last_type;
+  u16 last_op_offset;                  /* Position of last operator in data.data */
+  int ipv6;
+  struct {
+    u16 offset;                                /* Beginning of a component */
+    u16 length;                                /* Length of a component */
+  } parts[FLOW_TYPE_MAX];              /* Indexing all components */
+};
+
+struct flow_builder *flow_builder_init(pool *pool);
+void flow_builder_clear(struct flow_builder *fb);
+void flow_builder_set_type(struct flow_builder *fb, enum flow_type p);
+int flow_builder4_add_pfx(struct flow_builder *fb, const net_addr_ip4 *n4);
+int flow_builder6_add_pfx(struct flow_builder *fb, const net_addr_ip6 *n6, u32 offset);
+int flow_builder_add_op_val(struct flow_builder *fb, byte op, u32 value);
+int flow_builder_add_val_mask(struct flow_builder *fb, byte op, u32 value, u32 mask);
+net_addr_flow4 *flow_builder4_finalize(struct flow_builder *fb, linpool *lpool);
+net_addr_flow6 *flow_builder6_finalize(struct flow_builder *fb, linpool *lpool);
+
+
+/*
+ *     Validation
+ */
+
+/* Results of validation Flow specification */
+enum flow_validated_state {
+  FLOW_ST_UNKNOWN_COMPONENT,
+  FLOW_ST_VALID,
+  FLOW_ST_NOT_COMPLETE,
+  FLOW_ST_EXCEED_MAX_PREFIX_LENGTH,
+  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_CANNOT_USE_DONT_FRAGMENT
+};
+
+const char *flow_validated_state_str(enum flow_validated_state code);
+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 flow4_validate_cf(net_addr_flow4 *f);
+void flow6_validate_cf(net_addr_flow6 *f);
+
+
+/*
+ *     Net Formatting
+ */
+
+int flow4_net_format(char *buf, uint blen, const net_addr_flow4 *f);
+int flow6_net_format(char *buf, uint blen, const net_addr_flow6 *f);
+
+#endif /* _BIRD_FLOWSPEC_H_ */
diff --git a/lib/flowspec_test.c b/lib/flowspec_test.c
new file mode 100644 (file)
index 0000000..93364df
--- /dev/null
@@ -0,0 +1,644 @@
+/*
+ *     BIRD Library -- Flow specification (RFC 5575) Tests
+ *
+ *     (c) 2016 CZ.NIC z.s.p.o.
+ *
+ *     Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include "test/birdtest.h"
+#include "lib/flowspec.h"
+
+#define NET_ADDR_FLOW4_(what,prefix,pxlen,data_)       \
+  do                                                   \
+  {                                                    \
+    what = alloca(sizeof(net_addr_flow4) + 128);       \
+    *what = NET_ADDR_FLOW4(prefix, pxlen, sizeof(data_)); \
+    memcpy(what->data, &(data_), sizeof(data_));       \
+  } while(0)
+
+#define NET_ADDR_FLOW6_(what,prefix,pxlen,data_)       \
+  do                                                   \
+  {                                                    \
+    what = alloca(sizeof(net_addr_flow6) + 128);       \
+    *what = NET_ADDR_FLOW6(prefix, pxlen, sizeof(data_)); \
+    memcpy(what->data, &(data_), sizeof(data_));       \
+  } while(0)
+
+static int
+t_read_length(void)
+{
+  byte data[] = { 0xcc, 0xcc, 0xcc };
+
+  u16 get;
+  u16 expect;
+
+  for (uint expect = 0; expect < 0xf0; expect++)
+  {
+    *data = expect;
+    get = flow_read_length(data);
+    bt_assert_msg(get == expect, "Testing get length 0x%02x (get 0x%02x)", expect, get);
+  }
+
+  for (uint expect = 0; expect <= 0xfff; expect++)
+  {
+    put_u16(data, expect | 0xf000);
+    get = flow_read_length(data);
+    bt_assert_msg(get == expect, "Testing get length 0x%03x (get 0x%03x)", expect, get);
+  }
+
+  return 1;
+}
+
+static int
+t_write_length(void)
+{
+  byte data[] = { 0xcc, 0xcc, 0xcc };
+  uint offset;
+  byte *c;
+
+  for (uint expect = 0; expect <= 0xfff; expect++)
+  {
+    offset = flow_write_length(data, expect);
+
+    uint set = (expect < 0xf0) ? *data : (get_u16(data) & 0x0fff);
+    bt_assert_msg(set == expect, "Testing set length 0x%03x (set 0x%03x)", expect, set);
+    bt_assert(offset == (expect < 0xf0 ? 1 : 2));
+  }
+
+  return 1;
+}
+
+static int
+t_first_part(void)
+{
+  net_addr_flow4 *f;
+  NET_ADDR_FLOW4_(f, ip4_build(10,0,0,1), 24, ((byte[]) { 0x00, 0x00, 0xab }));
+
+  const byte const *under240 = &f->data[1];
+  const byte const *above240 = &f->data[2];
+
+  /* Case 0x00 0x00 */
+  bt_assert(flow4_first_part(f) == NULL);
+
+  /* Case 0x01 0x00 */
+  f->data[0] = 0x01;
+  bt_assert(flow4_first_part(f) == under240);
+
+  /* Case 0xef 0x00 */
+  f->data[0] = 0xef;
+  bt_assert(flow4_first_part(f) == under240);
+
+  /* Case 0xf0 0x00 */
+  f->data[0] = 0xf0;
+  bt_assert(flow4_first_part(f) == NULL);
+
+  /* Case 0xf0 0x01 */
+  f->data[1] = 0x01;
+  bt_assert(flow4_first_part(f) == above240);
+
+  /* Case 0xff 0xff */
+  f->data[0] = 0xff;
+  f->data[1] = 0xff;
+  bt_assert(flow4_first_part(f) == above240);
+
+  return 1;
+}
+
+static int
+t_iterators4(void)
+{
+  net_addr_flow4 *f;
+  NET_ADDR_FLOW4_(f, ip4_build(5,6,7,0), 24, ((byte[]) {
+    25, /* Length */
+    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,
+  }));
+
+  const byte *start            = f->data;
+  const byte *p1_dst_pfx       = &f->data[1];
+  const byte *p2_src_pfx       = &f->data[6];
+  const byte *p3_ip_proto      = &f->data[12];
+  const byte *p4_port          = &f->data[15];
+  const byte *p5_tcp_flags     = &f->data[23];
+  const byte *end              = &f->data[25];
+
+  bt_assert(flow_read_length(f->data) == (end-start));
+  bt_assert(flow4_first_part(f) == p1_dst_pfx);
+
+  bt_assert(flow4_next_part(p1_dst_pfx, end) == p2_src_pfx);
+  bt_assert(flow4_next_part(p2_src_pfx, end) == p3_ip_proto);
+  bt_assert(flow4_next_part(p3_ip_proto, end) == p4_port);
+  bt_assert(flow4_next_part(p4_port, end) == p5_tcp_flags);
+  bt_assert(flow4_next_part(p5_tcp_flags, end) == NULL);
+
+  return 1;
+}
+
+static int
+t_iterators6(void)
+{
+  net_addr_flow6 *f;
+  NET_ADDR_FLOW6_(f, ip6_build(0,0,0x12345678,0x9a000000), 64, ((byte[]) {
+    26, /* Length */
+    FLOW_TYPE_DST_PREFIX, 0x68, 0x40, 0x12, 0x34, 0x56, 0x78, 0x9a,
+    FLOW_TYPE_SRC_PREFIX, 0x08, 0x0, 0xc0,
+    FLOW_TYPE_NEXT_HEADER, 0x81, 0x06,
+    FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
+    FLOW_TYPE_LABEL, 0x80, 0x55,
+  }));
+
+  const byte *start            = f->data;
+  const byte *p1_dst_pfx       = &f->data[1];
+  const byte *p2_src_pfx       = &f->data[9];
+  const byte *p3_next_header   = &f->data[13];
+  const byte *p4_port          = &f->data[16];
+  const byte *p5_label         = &f->data[24];
+  const byte *end              = &f->data[26];
+
+  bt_assert(flow_read_length(f->data) == (end-start));
+  bt_assert(flow6_first_part(f) == p1_dst_pfx);
+
+  bt_assert(flow6_next_part(p1_dst_pfx, end) == p2_src_pfx);
+  bt_assert(flow6_next_part(p2_src_pfx, end) == p3_next_header);
+  bt_assert(flow6_next_part(p3_next_header, end) == p4_port);
+  bt_assert(flow6_next_part(p4_port, end) == p5_label);
+  bt_assert(flow6_next_part(p5_label, end) == NULL);
+
+  return 1;
+}
+
+static int
+t_validation4(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,
+  };
+
+  /* Isn't included destination prefix */
+  res = flow4_validate(nlri1, 0);
+  bt_assert(res == FLOW_ST_DEST_PREFIX_REQUIRED);
+  res = flow4_validate(&nlri1[5], sizeof(nlri1)-5);
+  bt_assert(res == FLOW_ST_DEST_PREFIX_REQUIRED);
+
+  /* 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_validate(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;
+  };
+
+#define TS(type, msg, data) ((struct tset) {type, msg, sizeof(data), (data)})
+  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
+      })
+    ),
+    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,
+      })
+    ),
+    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,
+      })
+    ),
+    TS(
+      FLOW_ST_AND_BIT_SHOULD_BE_UNSET,
+      "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",
+      ((byte []) {
+       FLOW_TYPE_IP_PROTOCOL, 0x89, 0x06,
+      })
+    ),
+    TS(
+      FLOW_ST_UNKNOWN_COMPONENT,
+      "Unknown component of type number 13",
+      ((byte []) {
+       FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
+       FLOW_TYPE_TCP_FLAGS, 0x80, 0x55,
+       13 /*something new*/, 0x80, 0x55,
+      })
+    ),
+  };
+#undef TS
+
+  for (uint tcase = 0; tcase < ARRAY_SIZE(tset); tcase++)
+  {
+    res = flow4_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_validation6(void)
+{
+  enum flow_validated_state res;
+
+  byte nlri1[] = {
+    FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0x98,
+    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_validate(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_validate(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;
+  };
+
+#define TS(type, msg, data) ((struct tset) {type, msg, sizeof(data), (data)})
+  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
+      })
+    ),
+    TS(
+      FLOW_ST_EXCEED_MAX_PREFIX_OFFSET,
+      "Prefix offset is higher than prefix length",
+      ((byte []) {
+       FLOW_TYPE_DST_PREFIX, 48, 64, 0x40, 0x12, 0x34
+      })
+    ),
+    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,
+      })
+    ),
+    TS(
+      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,
+      })
+    ),
+    TS(
+      FLOW_ST_AND_BIT_SHOULD_BE_UNSET,
+      "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",
+      ((byte []) {
+       FLOW_TYPE_NEXT_HEADER, 0x89, 0x06
+      })
+    ),
+    TS(
+      FLOW_ST_VALID,
+      "Component of type number 13 (Label) is well-known in IPv6",
+      ((byte []) {
+       FLOW_TYPE_LABEL, 0x80, 0x55
+      })
+    ),
+    TS(
+      FLOW_ST_UNKNOWN_COMPONENT,
+      "Unknown component of type number 14",
+      ((byte []) {
+       FLOW_TYPE_LABEL, 0x80, 0x55,
+       14 /*something new*/, 0x80, 0x55,
+      })
+    )
+  };
+#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;
+}
+
+
+
+/*
+ *     Builder tests
+ */
+
+static int
+t_builder4(void)
+{
+  resource_init();
+
+  struct flow_builder *fb = flow_builder_init(&root_pool);
+  linpool *lp = lp_new(&root_pool, 4096);
+
+  /* Expectation */
+
+  static byte nlri[] = {
+    25,
+    FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
+    FLOW_TYPE_SRC_PREFIX, 32, 10, 11, 12, 13,
+    FLOW_TYPE_IP_PROTOCOL, 0x80, 0x06,
+    FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
+    FLOW_TYPE_TCP_FLAGS, 0x80, 0x55
+  };
+
+  net_addr_flow4 *expect;
+  NET_ADDR_FLOW4_(expect, ip4_build(5, 6, 7, 0), 24, nlri);
+
+  /* Normal order */
+
+  net_addr_ip4 n1;
+  net_fill_ip4((net_addr *) &n1, ip4_build(5,6,7,0), 24);
+  flow_builder_set_type(fb, FLOW_TYPE_DST_PREFIX);
+  flow_builder4_add_pfx(fb, &n1);
+
+  net_addr_ip4 n2;
+  net_fill_ip4((net_addr *) &n2, ip4_build(10,11,12,13), 32);
+  flow_builder_set_type(fb, FLOW_TYPE_SRC_PREFIX);
+  flow_builder4_add_pfx(fb, &n2);
+
+  flow_builder_set_type(fb, FLOW_TYPE_IP_PROTOCOL);
+  flow_builder_add_op_val(fb, 0, 0x06);
+
+  flow_builder_set_type(fb, FLOW_TYPE_PORT);
+  flow_builder_add_op_val(fb, 0x03, 0x89);
+  flow_builder_add_op_val(fb, 0x45, 0x8b);
+  flow_builder_add_op_val(fb, 0x01, 0x1f90);
+
+  /* Try put a component twice time */
+  flow_builder_set_type(fb, FLOW_TYPE_IP_PROTOCOL);
+  flow_builder_add_op_val(fb, 0, 0x06);
+
+  flow_builder_set_type(fb, FLOW_TYPE_TCP_FLAGS);
+  flow_builder_add_op_val(fb, 0, 0x55);
+
+  net_addr_flow4 *res = flow_builder4_finalize(fb, lp);
+
+  bt_assert(memcmp(res, expect, expect->length) == 0);
+
+  /* Reverse order */
+
+  flow_builder_clear(fb);
+
+  flow_builder_set_type(fb, FLOW_TYPE_TCP_FLAGS);
+  flow_builder_add_op_val(fb, 0, 0x55);
+
+  flow_builder_set_type(fb, FLOW_TYPE_PORT);
+  flow_builder_add_op_val(fb, 0x03, 0x89);
+  flow_builder_add_op_val(fb, 0x45, 0x8b);
+  flow_builder_add_op_val(fb, 0x01, 0x1f90);
+
+  flow_builder_set_type(fb, FLOW_TYPE_IP_PROTOCOL);
+  flow_builder_add_op_val(fb, 0, 0x06);
+
+  net_fill_ip4((net_addr *) &n2, ip4_build(10,11,12,13), 32);
+  flow_builder_set_type(fb, FLOW_TYPE_SRC_PREFIX);
+  flow_builder4_add_pfx(fb, &n2);
+
+  net_fill_ip4((net_addr *) &n1, ip4_build(5,6,7,0), 24);
+  flow_builder_set_type(fb, FLOW_TYPE_DST_PREFIX);
+  flow_builder4_add_pfx(fb, &n1);
+
+  bt_assert(memcmp(res, expect, expect->length) == 0);
+
+  return 1;
+}
+
+static int
+t_builder6(void)
+{
+  net_addr_ip6 ip;
+
+  resource_init();
+  linpool *lp = lp_new(&root_pool, 4096);
+  struct flow_builder *fb = flow_builder_init(&root_pool);
+  fb->ipv6 = 1;
+
+  /* Expectation */
+
+  byte nlri[] = {
+    27,
+    FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0x98,
+    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,
+  };
+
+  net_addr_flow6 *expect;
+  NET_ADDR_FLOW6_(expect, ip6_build(0, 1, 0x12345678, 0x98000000), 103, nlri);
+
+  /* Normal order */
+
+  net_fill_ip6((net_addr *) &ip, ip6_build(0, 1, 0x12345678, 0x98000000), 103);
+  flow_builder_set_type(fb, FLOW_TYPE_DST_PREFIX);
+  flow_builder6_add_pfx(fb, &ip, 61);
+
+  /* Try put a component twice time */
+  net_fill_ip6((net_addr *) &ip, ip6_build(0, 1, 0x12345678, 0x98000000), 103);
+  flow_builder_set_type(fb, FLOW_TYPE_DST_PREFIX);
+  bt_assert(flow_builder6_add_pfx(fb, &ip, 61) == 0);
+
+  net_fill_ip6((net_addr *) &ip, ip6_build(0xc0000000,0,0,0), 8);
+  flow_builder_set_type(fb, FLOW_TYPE_SRC_PREFIX);
+  flow_builder6_add_pfx(fb, &ip, 0);
+
+  flow_builder_set_type(fb, FLOW_TYPE_NEXT_HEADER);
+  flow_builder_add_op_val(fb, 0, 0x06);
+
+  flow_builder_set_type(fb, FLOW_TYPE_PORT);
+  flow_builder_add_op_val(fb, 0x03, 0x89);
+  flow_builder_add_op_val(fb, 0x45, 0x8b);
+  flow_builder_add_op_val(fb, 0x01, 0x1f90);
+
+  flow_builder_set_type(fb, FLOW_TYPE_LABEL);
+  flow_builder_add_op_val(fb, 0, 0x55);
+
+  net_addr_flow6 *res = flow_builder6_finalize(fb, lp);
+  bt_assert(memcmp(res, expect, expect->length) == 0);
+
+  /* Reverse order */
+
+  flow_builder_clear(fb);
+  fb->ipv6 = 1;
+
+  flow_builder_set_type(fb, FLOW_TYPE_LABEL);
+  flow_builder_add_op_val(fb, 0, 0x55);
+
+  flow_builder_set_type(fb, FLOW_TYPE_PORT);
+  flow_builder_add_op_val(fb, 0x03, 0x89);
+  flow_builder_add_op_val(fb, 0x45, 0x8b);
+  flow_builder_add_op_val(fb, 0x01, 0x1f90);
+
+  flow_builder_set_type(fb, FLOW_TYPE_NEXT_HEADER);
+  flow_builder_add_op_val(fb, 0, 0x06);
+
+  net_fill_ip6((net_addr *) &ip, ip6_build(0xc0000000,0,0,0), 8);
+  flow_builder_set_type(fb, FLOW_TYPE_SRC_PREFIX);
+  flow_builder6_add_pfx(fb, &ip, 0);
+
+  net_fill_ip6((net_addr *) &ip, ip6_build(0, 1, 0x12345678, 0x98000000), 103);
+  flow_builder_set_type(fb, FLOW_TYPE_DST_PREFIX);
+  flow_builder6_add_pfx(fb, &ip, 61);
+
+  res = flow_builder6_finalize(fb, lp);
+  bt_assert(memcmp(res, expect, expect->length) == 0);
+
+  return 1;
+}
+
+static int
+t_formatting4(void)
+{
+  char b[1024];
+
+  byte nlri[] = {
+    0,
+    FLOW_TYPE_DST_PREFIX, 0x08, 10,
+    FLOW_TYPE_IP_PROTOCOL, 0x81, 23,
+    FLOW_TYPE_DST_PORT, 0x02, 24, 0x44, 30, 0x03, 40, 0x45, 50, 0x03, 60, 0x45, 70, 0x01, 80, 0xc3, 90,
+    FLOW_TYPE_SRC_PORT, 0x02, 24, 0x44, 0x1e, 0x01, 0x28, 0x01, 0x32, 0x03, 0x3c, 0x45, 0x46, 0x81, 0x50,
+    FLOW_TYPE_ICMP_TYPE, 0x81, 0x50,
+    FLOW_TYPE_ICMP_CODE, 0x81, 0x5a,
+    FLOW_TYPE_TCP_FLAGS, 0x01, 0x03, 0xc2, 0x0c,
+    FLOW_TYPE_PACKET_LENGTH, 0x03, 0, 0xd5, 0xff, 0xff,
+    FLOW_TYPE_DSCP, 0x81, 63,
+    FLOW_TYPE_FRAGMENT, 0x01, 0x01, 0x82, 0x02
+  };
+  *nlri = (u8) sizeof(nlri);
+
+  net_addr_flow4 *input;
+  NET_ADDR_FLOW4_(input, ip4_build(5, 6, 7, 0), 24, nlri);
+
+  const char *expect = "flow4 { dst 10.0.0.0/8; proto 23; dport > 24 && < 30 || 40..50,60..70,80 && >= 90; sport > 24 && < 30 || 40,50,60..70,80; icmp type 80; icmp code 90; tcp flags 0x3/0x3,0x0/0xc; length 0..65535; dscp 63; fragment dont_fragment || !is_fragment; }";
+
+  bt_assert(flow4_net_format(b, sizeof(b), input) == strlen(expect));
+  bt_debug(" expect: '%s',\n output: '%s'\n", expect, b);
+  bt_assert(strcmp(b, expect) == 0);
+
+  return 1;
+}
+
+static int
+t_formatting6(void)
+{
+  char b[1024];
+
+  byte nlri[] = {
+    0,
+    FLOW_TYPE_DST_PREFIX, 103, 61, 0x01, 0x12, 0x34, 0x56, 0x78, 0x98,
+    FLOW_TYPE_SRC_PREFIX, 8, 0, 0xc0,
+    FLOW_TYPE_NEXT_HEADER, 0x81, 0x06,
+    FLOW_TYPE_PORT, 0x03, 20, 0x45, 40, 0x91, 0x01, 0x11,
+    FLOW_TYPE_LABEL, 0xa0, 0x12, 0x34, 0x56, 0x78,
+  };
+  *nlri = (u8) sizeof(nlri);
+
+  net_addr_flow6 *input;
+  NET_ADDR_FLOW6_(input, ip6_build(0, 1, 0x12345678, 0x98000000), 103, nlri);
+
+  const char *expect = "flow6 { dst ::1:1234:5678:9800:0/103 offset 61; src c000::/8; next header 6; port 20..40,273; label !0x0/0x12345678; }";
+
+  bt_assert(flow6_net_format(b, sizeof(b), input) == strlen(expect));
+  bt_debug(" expect: '%s',\n output: '%s'\n", expect, b);
+  bt_assert(strcmp(b, expect) == 0);
+
+  return 1;
+}
+
+int
+main(int argc, char *argv[])
+{
+  bt_init(argc, argv);
+
+  bt_test_suite(t_read_length,  "Testing get NLRI length");
+  bt_test_suite(t_write_length, "Testing set NLRI length");
+  bt_test_suite(t_first_part,   "Searching first part in net_addr_flow");
+  bt_test_suite(t_iterators4,   "Testing iterators (IPv4)");
+  bt_test_suite(t_iterators6,   "Testing iterators (IPv6)");
+  bt_test_suite(t_validation4,  "Testing validation (IPv4)");
+  bt_test_suite(t_validation6,  "Testing validation (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_formatting6,  "Formatting Flow Specification (IPv6) into text representation");
+
+  return bt_exit_value();
+}
index 53991b4cb9be3cb860f5996149187b775d22b66a..f283e79f4536d0da727427edce0ab61729f71f5a 100644 (file)
--- a/lib/net.c
+++ b/lib/net.c
@@ -2,42 +2,51 @@
 #include "nest/bird.h"
 #include "lib/ip.h"
 #include "lib/net.h"
+#include "lib/flowspec.h"
 
 
 const char * const net_label[] = {
-  [NET_IP4] = "ipv4",
-  [NET_IP6] = "ipv6",
-  [NET_VPN4] = "vpn4",
-  [NET_VPN6] = "vpn6",
-  [NET_ROA4] = "roa4",
-  [NET_ROA6] = "roa6",
+  [NET_IP4]    = "ipv4",
+  [NET_IP6]    = "ipv6",
+  [NET_VPN4]   = "vpn4",
+  [NET_VPN6]   = "vpn6",
+  [NET_ROA4]   = "roa4",
+  [NET_ROA6]   = "roa6",
+  [NET_FLOW4]  = "flow4",
+  [NET_FLOW6]  = "flow6"
 };
 
 const u16 net_addr_length[] = {
-  [NET_IP4] = sizeof(net_addr_ip4),
-  [NET_IP6] = sizeof(net_addr_ip6),
-  [NET_VPN4] = sizeof(net_addr_vpn4),
-  [NET_VPN6] = sizeof(net_addr_vpn6),
-  [NET_ROA4] = sizeof(net_addr_roa4),
-  [NET_ROA6] = sizeof(net_addr_roa6)
+  [NET_IP4]    = sizeof(net_addr_ip4),
+  [NET_IP6]    = sizeof(net_addr_ip6),
+  [NET_VPN4]   = sizeof(net_addr_vpn4),
+  [NET_VPN6]   = sizeof(net_addr_vpn6),
+  [NET_ROA4]   = sizeof(net_addr_roa4),
+  [NET_ROA6]   = sizeof(net_addr_roa6),
+  [NET_FLOW4]  = 0,
+  [NET_FLOW6]  = 0
 };
 
 const u8 net_max_prefix_length[] = {
-  [NET_IP4] = IP4_MAX_PREFIX_LENGTH,
-  [NET_IP6] = IP6_MAX_PREFIX_LENGTH,
-  [NET_VPN4] = IP4_MAX_PREFIX_LENGTH,
-  [NET_VPN6] = IP6_MAX_PREFIX_LENGTH,
-  [NET_ROA4] = IP4_MAX_PREFIX_LENGTH,
-  [NET_ROA6] = IP6_MAX_PREFIX_LENGTH
+  [NET_IP4]    = IP4_MAX_PREFIX_LENGTH,
+  [NET_IP6]    = IP6_MAX_PREFIX_LENGTH,
+  [NET_VPN4]   = IP4_MAX_PREFIX_LENGTH,
+  [NET_VPN6]   = IP6_MAX_PREFIX_LENGTH,
+  [NET_ROA4]   = IP4_MAX_PREFIX_LENGTH,
+  [NET_ROA6]   = IP6_MAX_PREFIX_LENGTH,
+  [NET_FLOW4]  = IP4_MAX_PREFIX_LENGTH,
+  [NET_FLOW6]  = IP6_MAX_PREFIX_LENGTH
 };
 
 const u16 net_max_text_length[] = {
-  [NET_IP4] = 18,      /* "255.255.255.255/32" */
-  [NET_IP6] = 43,      /* "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" */
-  [NET_VPN4] = 40,     /* "4294967296:4294967296 255.255.255.255/32" */
-  [NET_VPN6] = 65,     /* "4294967296:4294967296 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" */
-  [NET_ROA4] = 34,      /* "255.255.255.255/32-32 AS4294967295" */
-  [NET_ROA6] = 60,      /* "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128-128 AS4294967295" */
+  [NET_IP4]    = 18,   /* "255.255.255.255/32" */
+  [NET_IP6]    = 43,   /* "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" */
+  [NET_VPN4]   = 40,   /* "4294967296:4294967296 255.255.255.255/32" */
+  [NET_VPN6]   = 65,   /* "4294967296:4294967296 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" */
+  [NET_ROA4]   = 34,   /* "255.255.255.255/32-32 AS4294967295" */
+  [NET_ROA6]   = 60,   /* "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128-128 AS4294967295" */
+  [NET_FLOW4]  = 0,    /* "flow4 { ... }" */
+  [NET_FLOW6]  = 0     /* "flow6 { ... }" */
 };
 
 
@@ -60,6 +69,10 @@ net_format(const net_addr *N, char *buf, int buflen)
     return bsnprintf(buf, buflen, "%I4/%u-%u AS%u",  n->roa4.prefix, n->roa4.pxlen, n->roa4.max_pxlen, n->roa4.asn);
   case NET_ROA6:
     return bsnprintf(buf, buflen, "%I6/%u-%u AS%u",  n->roa6.prefix, n->roa6.pxlen, n->roa6.max_pxlen, n->roa6.asn);
+  case NET_FLOW4:
+    return flow4_net_format(buf, buflen, &n->flow4);
+  case NET_FLOW6:
+    return flow6_net_format(buf, buflen, &n->flow6);
   }
 
   return 0;
@@ -73,11 +86,13 @@ net_pxmask(const net_addr *a)
   case NET_IP4:
   case NET_VPN4:
   case NET_ROA4:
+  case NET_FLOW4:
     return ipa_from_ip4(ip4_mkmask(net4_pxlen(a)));
 
   case NET_IP6:
   case NET_VPN6:
   case NET_ROA6:
+  case NET_FLOW6:
     return ipa_from_ip6(ip6_mkmask(net6_pxlen(a)));
 
   default:
@@ -105,6 +120,10 @@ net_compare(const net_addr *a, const net_addr *b)
     return net_compare_roa4((const net_addr_roa4 *) a, (const net_addr_roa4 *) b);
   case NET_ROA6:
     return net_compare_roa6((const net_addr_roa6 *) a, (const net_addr_roa6 *) b);
+  case NET_FLOW4:
+    return net_compare_flow4((const net_addr_flow4 *) a, (const net_addr_flow4 *) b);
+  case NET_FLOW6:
+    return net_compare_flow6((const net_addr_flow6 *) a, (const net_addr_flow6 *) b);
   }
   return 0;
 }
@@ -122,6 +141,8 @@ net_hash(const net_addr *n)
   case NET_VPN6: return NET_HASH(n, vpn6);
   case NET_ROA4: return NET_HASH(n, roa4);
   case NET_ROA6: return NET_HASH(n, roa6);
+  case NET_FLOW4: return NET_HASH(n, flow4);
+  case NET_FLOW6: return NET_HASH(n, flow6);
   default: bug("invalid type");
   }
 }
@@ -135,11 +156,13 @@ net_validate(const net_addr *N)
   case NET_IP4:
   case NET_VPN4:
   case NET_ROA4:
+  case NET_FLOW4:
     return net_validate_ip4((net_addr_ip4 *) N);
 
   case NET_IP6:
   case NET_VPN6:
   case NET_ROA6:
+  case NET_FLOW6:
     return net_validate_ip6((net_addr_ip6 *) N);
 
   default:
@@ -157,11 +180,13 @@ net_normalize(net_addr *N)
   case NET_IP4:
   case NET_VPN4:
   case NET_ROA4:
+  case NET_FLOW4:
     return net_normalize_ip4(&n->ip4);
 
   case NET_IP6:
   case NET_VPN6:
   case NET_ROA6:
+  case NET_FLOW6:
     return net_normalize_ip6(&n->ip6);
   }
 }
@@ -176,11 +201,13 @@ net_classify(const net_addr *N)
   case NET_IP4:
   case NET_VPN4:
   case NET_ROA4:
+  case NET_FLOW4:
     return ip4_zero(n->ip4.prefix) ? (IADDR_HOST | SCOPE_UNIVERSE) : ip4_classify(n->ip4.prefix);
 
   case NET_IP6:
   case NET_VPN6:
   case NET_ROA6:
+  case NET_FLOW6:
     return ip6_zero(n->ip6.prefix) ? (IADDR_HOST | SCOPE_UNIVERSE) : ip6_classify(&n->ip6.prefix);
   }
 
@@ -195,6 +222,7 @@ ipa_in_netX(const ip_addr a, const net_addr *n)
   case NET_IP4:
   case NET_VPN4:
   case NET_ROA4:
+  case NET_FLOW4:
     if (!ipa_is_ip4(a)) return 0;
     return ip4_zero(ip4_and(ip4_xor(ipa_to_ip4(a), net4_prefix(n)),
                            ip4_mkmask(net4_pxlen(n))));
@@ -202,6 +230,7 @@ ipa_in_netX(const ip_addr a, const net_addr *n)
   case NET_IP6:
   case NET_VPN6:
   case NET_ROA6:
+  case NET_FLOW6:
     if (ipa_is_ip4(a)) return 0;
     return ip6_zero(ip6_and(ip6_xor(ipa_to_ip6(a), net6_prefix(n)),
                            ip6_mkmask(net6_pxlen(n))));
index e8310894e5c4b2637add128497a8b56752ef575a..f07e6b240f205b99b2c8df18ef09a5639050675e 100644 (file)
--- a/lib/net.h
+++ b/lib/net.h
@@ -19,7 +19,9 @@
 #define NET_VPN6       4
 #define NET_ROA4       5
 #define NET_ROA6       6
-#define NET_MAX                7
+#define NET_FLOW4      7
+#define NET_FLOW6      8
+#define NET_MAX                9
 
 #define NB_IP4         (1 << NET_IP4)
 #define NB_IP6         (1 << NET_IP6)
@@ -27,6 +29,8 @@
 #define NB_VPN6                (1 << NET_VPN6)
 #define NB_ROA4                (1 << NET_ROA4)
 #define NB_ROA6                (1 << NET_ROA6)
+#define NB_FLOW4       (1 << NET_FLOW4)
+#define NB_FLOW6       (1 << NET_FLOW6)
 
 #define NB_IP          (NB_IP4 | NB_IP6)
 #define NB_ANY         0xffffffff
@@ -88,6 +92,22 @@ typedef struct net_addr_roa6 {
   u32 asn;
 } net_addr_roa6;
 
+typedef struct net_addr_flow4 {
+  u8 type;
+  u8 pxlen;
+  u16 length;
+  ip4_addr prefix;
+  byte data[0];
+} net_addr_flow4;
+
+typedef struct net_addr_flow6 {
+  u8 type;
+  u8 pxlen;
+  u16 length;
+  ip6_addr prefix;
+  byte data[0];
+} net_addr_flow6;
+
 typedef union net_addr_union {
   net_addr n;
   net_addr_ip4 ip4;
@@ -96,6 +116,8 @@ typedef union net_addr_union {
   net_addr_vpn6 vpn6;
   net_addr_roa4 roa4;
   net_addr_roa6 roa6;
+  net_addr_flow4 flow4;
+  net_addr_flow6 flow6;
 } net_addr_union;
 
 
@@ -104,7 +126,7 @@ extern const u16 net_addr_length[];
 extern const u8  net_max_prefix_length[];
 extern const u16 net_max_text_length[];
 
-#define NET_MAX_TEXT_LENGTH    65
+#define NET_MAX_TEXT_LENGTH    256
 
 
 #define NET_ADDR_IP4(prefix,pxlen) \
@@ -125,6 +147,12 @@ extern const u16 net_max_text_length[];
 #define NET_ADDR_ROA6(prefix,pxlen,max_pxlen,asn) \
   ((net_addr_roa6) { NET_ROA6, pxlen, sizeof(net_addr_roa6), prefix, max_pxlen, asn })
 
+#define NET_ADDR_FLOW4(prefix,pxlen,dlen) \
+  ((net_addr_flow4) { NET_FLOW4, pxlen, sizeof(net_addr_ip4) + dlen, prefix })
+
+#define NET_ADDR_FLOW6(prefix,pxlen,dlen) \
+  ((net_addr_flow6) { NET_FLOW6, pxlen, sizeof(net_addr_ip6) + dlen, prefix })
+
 
 
 static inline void net_fill_ip4(net_addr *a, ip4_addr prefix, uint pxlen)
@@ -161,6 +189,19 @@ static inline void net_fill_ip_host(net_addr *a, ip_addr prefix)
     net_fill_ip6(a, ipa_to_ip6(prefix), IP6_MAX_PREFIX_LENGTH);
 }
 
+static inline void net_fill_flow4(net_addr *a, ip4_addr prefix, uint pxlen, byte *data, uint dlen)
+{
+  net_addr_flow4 *f = (void *) a;
+  *f = NET_ADDR_FLOW4(prefix, pxlen, dlen);
+  memcpy(f->data, data, dlen);
+}
+
+static inline void net_fill_flow6(net_addr *a, ip6_addr prefix, uint pxlen, byte *data, uint dlen)
+{
+  net_addr_flow6 *f = (void *) a;
+  *f = NET_ADDR_FLOW6(prefix, pxlen, dlen);
+  memcpy(f->data, data, dlen);
+}
 
 static inline int net_val_match(u8 type, u32 mask)
 { return !!((1 << type) & mask); }
@@ -188,11 +229,13 @@ static inline ip_addr net_prefix(const net_addr *a)
   case NET_IP4:
   case NET_VPN4:
   case NET_ROA4:
+  case NET_FLOW4:
     return ipa_from_ip4(net4_prefix(a));
 
   case NET_IP6:
   case NET_VPN6:
   case NET_ROA6:
+  case NET_FLOW6:
     return ipa_from_ip6(net6_prefix(a));
 
   default:
@@ -233,6 +276,13 @@ static inline int net_equal_roa4(const net_addr_roa4 *a, const net_addr_roa4 *b)
 static inline int net_equal_roa6(const net_addr_roa6 *a, const net_addr_roa6 *b)
 { return !memcmp(a, b, sizeof(net_addr_roa6)); }
 
+static inline int net_equal_flow4(const net_addr_flow4 *a, const net_addr_flow4 *b)
+{ return net_equal((const net_addr *) a, (const net_addr *) b); }
+
+static inline int net_equal_flow6(const net_addr_flow6 *a, const net_addr_flow6 *b)
+{ return net_equal((const net_addr *) a, (const net_addr *) b); }
+
+
 static inline int net_equal_prefix_roa4(const net_addr_roa4 *a, const net_addr_roa4 *b)
 { return ip4_equal(a->prefix, b->prefix) && (a->pxlen == b->pxlen); }
 
@@ -258,6 +308,13 @@ static inline int net_zero_roa4(const net_addr_roa4 *a)
 static inline int net_zero_roa6(const net_addr_roa6 *a)
 { return !a->pxlen && ip6_zero(a->prefix) && !a->max_pxlen && !a->asn; }
 
+static inline int net_zero_flow4(const net_addr_flow4 *a)
+{ return !a->pxlen && ip4_zero(a->prefix) && !a->data; }
+
+static inline int net_zero_flow6(const net_addr_flow6 *a)
+{ return !a->pxlen && ip6_zero(a->prefix) && !a->data; }
+
+
 
 static inline int net_compare_ip4(const net_addr_ip4 *a, const net_addr_ip4 *b)
 { return ip4_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen); }
@@ -277,6 +334,12 @@ static inline int net_compare_roa4(const net_addr_roa4 *a, const net_addr_roa4 *
 static inline int net_compare_roa6(const net_addr_roa6 *a, const net_addr_roa6 *b)
 { return ip6_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen) ?: uint_cmp(a->max_pxlen, b->max_pxlen) ?: uint_cmp(a->asn, b->asn); }
 
+static inline int net_compare_flow4(const net_addr_flow4 *a, const net_addr_flow4 *b)
+{ return ip4_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen) ?: uint_cmp(a->length, b->length) ?: memcmp(a->data, b->data, a->length - sizeof(net_addr_flow4)); }
+
+static inline int net_compare_flow6(const net_addr_flow6 *a, const net_addr_flow6 *b)
+{ return ip6_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen) ?: uint_cmp(a->length, b->length) ?: memcmp(a->data, b->data, a->length - sizeof(net_addr_flow6)); }
+
 int net_compare(const net_addr *a, const net_addr *b);
 
 
@@ -301,6 +364,12 @@ static inline void net_copy_roa4(net_addr_roa4 *dst, const net_addr_roa4 *src)
 static inline void net_copy_roa6(net_addr_roa6 *dst, const net_addr_roa6 *src)
 { memcpy(dst, src, sizeof(net_addr_roa6)); }
 
+static inline void net_copy_flow4(net_addr_flow4 *dst, const net_addr_flow4 *src)
+{ memcpy(dst, src, src->length); }
+
+static inline void net_copy_flow6(net_addr_flow6 *dst, const net_addr_flow6 *src)
+{ memcpy(dst, src, src->length); }
+
 
 static inline u32 net_hash_ip4(const net_addr_ip4 *n)
 { return ip4_hash(n->prefix) ^ ((u32) n->pxlen << 26); }
@@ -324,6 +393,12 @@ static inline u32 net_hash_roa4(const net_addr_roa4 *n)
 static inline u32 net_hash_roa6(const net_addr_roa6 *n)
 { return ip6_hash(n->prefix) ^ ((u32) n->pxlen << 26); }
 
+static inline u32 net_hash_flow4(const net_addr_flow4 *n)
+{ return ip4_hash(n->prefix) ^ ((u32) n->pxlen << 26); }
+
+static inline u32 net_hash_flow6(const net_addr_flow6 *n)
+{ return ip6_hash(n->prefix) ^ ((u32) n->pxlen << 26); }
+
 u32 net_hash(const net_addr *a);
 
 
index 1632b5f3feec12a0be0d5114f7688737f3096a3f..8e3cbbcfcc0c3125a5dd9c7945d905c6bb3d5d12 100644 (file)
@@ -467,6 +467,10 @@ int
 buffer_vprint(buffer *buf, const char *fmt, va_list args)
 {
   int i = bvsnprintf((char *) buf->pos, buf->end - buf->pos, fmt, args);
+
+  if ((i < 0) && (buf->pos < buf->end))
+    *buf->pos = 0;
+
   buf->pos = (i >= 0) ? (buf->pos + i) : buf->end;
   return i;
 }
@@ -478,9 +482,12 @@ buffer_print(buffer *buf, const char *fmt, ...)
   int i;
 
   va_start(args, fmt);
-  i=bvsnprintf((char *) buf->pos, buf->end - buf->pos, fmt, args);
+  i = bvsnprintf((char *) buf->pos, buf->end - buf->pos, fmt, args);
   va_end(args);
 
+  if ((i < 0) && (buf->pos < buf->end))
+    *buf->pos = 0;
+
   buf->pos = (i >= 0) ? (buf->pos + i) : buf->end;
   return i;
 }
@@ -489,13 +496,13 @@ void
 buffer_puts(buffer *buf, const char *str)
 {
   byte *bp = buf->pos;
-  byte *be = buf->end;
+  byte *be = buf->end - 1;
 
   while (bp < be && *str)
     *bp++ = *str++;
 
-  if (bp < be)
+  if (bp <= be)
     *bp = 0;
 
-  buf->pos = bp;
+  buf->pos = (bp < be) ? bp : buf->end;
 }
index 776e5d161610bcaf7dc038f8ce2abb3343610bde..23d6a45281c7404b153a1f125e3dd5f7a0e0244f 100644 (file)
@@ -150,6 +150,8 @@ net_type:
  | VPN6 { $$ = NET_VPN6; }
  | ROA4 { $$ = NET_ROA4; }
  | ROA6 { $$ = NET_ROA6; }
+ | FLOW4{ $$ = NET_FLOW4; }
+ | FLOW6{ $$ = NET_FLOW6; }
  ;
 
 
index 8021ea24cd314176131697f3e3d6f7f424a70339..11c31d0d8110a8e340145fe51f54fdae252995bc 100644 (file)
@@ -193,6 +193,8 @@ fib_hash(struct fib *f, const net_addr *a)
   case NET_VPN6: return FIB_HASH(f, a, vpn6);
   case NET_ROA4: return FIB_HASH(f, a, roa4);
   case NET_ROA6: return FIB_HASH(f, a, roa6);
+  case NET_FLOW4: return FIB_HASH(f, a, flow4);
+  case NET_FLOW6: return FIB_HASH(f, a, flow6);
   default: bug("invalid type");
   }
 }
@@ -227,6 +229,8 @@ fib_find(struct fib *f, const net_addr *a)
   case NET_VPN6: return FIB_FIND(f, a, vpn6);
   case NET_ROA4: return FIB_FIND(f, a, roa4);
   case NET_ROA6: return FIB_FIND(f, a, roa6);
+  case NET_FLOW4: return FIB_FIND(f, a, flow4);
+  case NET_FLOW6: return FIB_FIND(f, a, flow6);
   default: bug("invalid type");
   }
 }
@@ -244,6 +248,8 @@ fib_insert(struct fib *f, const net_addr *a, struct fib_node *e)
   case NET_VPN6: FIB_INSERT(f, a, e, vpn6); return;
   case NET_ROA4: FIB_INSERT(f, a, e, roa4); return;
   case NET_ROA6: FIB_INSERT(f, a, e, roa6); return;
+  case NET_FLOW4: FIB_INSERT(f, a, e, flow4); return;
+  case NET_FLOW6: FIB_INSERT(f, a, e, flow6); return;
   default: bug("invalid type");
   }
 }
@@ -334,11 +340,13 @@ fib_route(struct fib *f, const net_addr *n)
   case NET_IP4:
   case NET_VPN4:
   case NET_ROA4:
+  case NET_FLOW4:
     return fib_route_ip4(f, (net_addr_ip4 *) n0);
 
   case NET_IP6:
   case NET_VPN6:
   case NET_ROA6:
+  case NET_FLOW6:
     return fib_route_ip6(f, (net_addr_ip6 *) n0);
 
   default:
index 6bf6c2fea2bd7160e5693bb58e95d7628f66248e..8c429874034487822911790830b32a5d435b7b8b 100644 (file)
@@ -2506,8 +2506,7 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
   int first = 1;
   int pass = 0;
 
-  bsprintf(ia, "%N", n->n.addr);
-
+  bsnprintf(ia, sizeof(ia), "%N", n->n.addr);
 
   for (e = n->routes; e; e = e->next)
     {