Commit 77234bbb authored by Ondrej Zajicek (work)'s avatar Ondrej Zajicek (work)
Browse files

Basic flow specification support (RFC 5575)

Add flow4/flow6 network and rt-table type and operations, config grammar
and static protocol support.

Squashed flowspec branch from Pavel Tvrdik.
parent b94e5e58
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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
+1 −2
Original line number Diff line number Diff line
@@ -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_
 ;


conf/flowspec.Y

0 → 100644
+219 −0
Original line number Diff line number Diff line
/*
 *	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
+131 −0
Original line number Diff line number Diff line
@@ -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">

+26 −0
Original line number Diff line number Diff line
@@ -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
 * 	-------------
Loading