diff options
-rw-r--r-- | conf/Makefile | 2 | ||||
-rw-r--r-- | conf/confbase.Y | 3 | ||||
-rw-r--r-- | conf/flowspec.Y | 219 | ||||
-rw-r--r-- | doc/bird.sgml | 131 | ||||
-rw-r--r-- | filter/test.conf | 26 | ||||
-rw-r--r-- | lib/Doc | 1 | ||||
-rw-r--r-- | lib/Makefile | 4 | ||||
-rw-r--r-- | lib/birdlib.h | 1 | ||||
-rw-r--r-- | lib/buffer.h | 7 | ||||
-rw-r--r-- | lib/flowspec.c | 1148 | ||||
-rw-r--r-- | lib/flowspec.h | 134 | ||||
-rw-r--r-- | lib/flowspec_test.c | 644 | ||||
-rw-r--r-- | lib/net.c | 77 | ||||
-rw-r--r-- | lib/net.h | 79 | ||||
-rw-r--r-- | lib/printf.c | 15 | ||||
-rw-r--r-- | nest/config.Y | 2 | ||||
-rw-r--r-- | nest/rt-fib.c | 8 | ||||
-rw-r--r-- | nest/rt-table.c | 3 |
18 files changed, 2467 insertions, 37 deletions
diff --git a/conf/Makefile b/conf/Makefile index cc2b13c6..fb3dd052 100644 --- a/conf/Makefile +++ b/conf/Makefile @@ -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 diff --git a/conf/confbase.Y b/conf/confbase.Y index 094c81b5..aec4aeb4 100644 --- a/conf/confbase.Y +++ b/conf/confbase.Y @@ -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 index 00000000..a47d453b --- /dev/null +++ b/conf/flowspec.Y @@ -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 diff --git a/doc/bird.sgml b/doc/bird.sgml index a734b2ff..999fa294 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -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"> diff --git a/filter/test.conf b/filter/test.conf index 18aeaae1..7915e627 100644 --- a/filter/test.conf +++ b/filter/test.conf @@ -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 * ------------- @@ -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 diff --git a/lib/Makefile b/lib/Makefile index a7da9802..acfe81ac 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -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) diff --git a/lib/birdlib.h b/lib/birdlib.h index bb19df54..c82082c1 100644 --- a/lib/birdlib.h +++ b/lib/birdlib.h @@ -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; }) diff --git a/lib/buffer.h b/lib/buffer.h index 2a53f211..a8b11951 100644 --- a/lib/buffer.h +++ b/lib/buffer.h @@ -46,4 +46,11 @@ #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 index 00000000..b72bc7fc --- /dev/null +++ b/lib/flowspec.c @@ -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 index 00000000..57809bec --- /dev/null +++ b/lib/flowspec.h @@ -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 index 00000000..93364dfe --- /dev/null +++ b/lib/flowspec_test.c @@ -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(); +} @@ -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)))); @@ -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); diff --git a/lib/printf.c b/lib/printf.c index 1632b5f3..8e3cbbcf 100644 --- a/lib/printf.c +++ b/lib/printf.c @@ -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; } diff --git a/nest/config.Y b/nest/config.Y index 776e5d16..23d6a452 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -150,6 +150,8 @@ net_type: | VPN6 { $$ = NET_VPN6; } | ROA4 { $$ = NET_ROA4; } | ROA6 { $$ = NET_ROA6; } + | FLOW4{ $$ = NET_FLOW4; } + | FLOW6{ $$ = NET_FLOW6; } ; diff --git a/nest/rt-fib.c b/nest/rt-fib.c index 8021ea24..11c31d0d 100644 --- a/nest/rt-fib.c +++ b/nest/rt-fib.c @@ -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: diff --git a/nest/rt-table.c b/nest/rt-table.c index 6bf6c2fe..8c429874 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -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) { |