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.
This commit is contained in:
Ondrej Zajicek (work) 2016-12-07 15:36:15 +01:00
parent b94e5e58db
commit 77234bbbde
18 changed files with 2467 additions and 37 deletions

View file

@ -10,7 +10,7 @@ BISON_DEBUG=-t
#FLEX_DEBUG=-d #FLEX_DEBUG=-d
endif endif
$(conf-y-targets): $(s)confbase.Y $(conf-y-targets): $(s)confbase.Y $(s)flowspec.Y
$(M4) -P $| $^ >$@ $(M4) -P $| $^ >$@
$(o)cf-parse.y: | $(s)gen_parser.m4 $(o)cf-parse.y: | $(s)gen_parser.m4

View file

@ -138,8 +138,6 @@ expr_us:
| expr US { $$ = (u32) $1 * 1; } | expr US { $$ = (u32) $1 * 1; }
; ;
/* expr_u16: expr { check_u16($1); $$ = $1; }; */
/* Switches */ /* Switches */
bool: bool:
@ -220,6 +218,7 @@ net_roa_: net_roa4_ | net_roa6_ ;
net_: net_:
net_ip_ { $$ = cfg_alloc($1.length); net_copy($$, &($1)); } net_ip_ { $$ = cfg_alloc($1.length); net_copy($$, &($1)); }
| net_roa_ | net_roa_
| net_flow_
; ;

219
conf/flowspec.Y Normal file
View file

@ -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

View file

@ -715,6 +715,137 @@ agreement").
</descrip> </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 <chapt>Remote control
<label id="remote-control"> <label id="remote-control">

View file

@ -548,6 +548,32 @@ bt_test_suite(t_prefix6_set, "Testing prefix IPv6 sets");
function t_flowspec()
prefix p;
{
p = flow4 { dst 10.0.0.0/8; };
bt_assert(p !~ [ 10.0.0.0/8 ] );
bt_assert(format(flow4 { dst 10.0.0.0/8; proto = 23; }) = "flow4 { dst 10.0.0.0/8; proto 23; }");
bt_assert(format(flow6 { dst ::1/128; src ::2/127; }) = "flow6 { dst ::1/128; src ::2/127; }");
bt_assert(format(flow6 { next header false 42; }) = "flow6 { next header false 42; }");
bt_assert(format(flow6 { port 80; }) = "flow6 { port 80; }");
bt_assert(format(flow6 { dport > 24 && < 30 || 40..50,60..70,80 && >= 90; }) = "flow6 { dport > 24 && < 30 || 40..50,60..70,80 && >= 90; }");
bt_assert(format(flow6 { sport 0..0x400; }) = "flow6 { sport 0..1024; }");
bt_assert(format(flow6 { icmp type 80; }) = "flow6 { icmp type 80; }");
bt_assert(format(flow6 { icmp code 90; }) = "flow6 { icmp code 90; }");
bt_assert(format(flow6 { tcp flags 0x03/0x0f; }) = "flow6 { tcp flags 0x3/0x3,0x0/0xc; }");
bt_assert(format(flow6 { length 0..65535; }) = "flow6 { length 0..65535; }");
bt_assert(format(flow6 { dscp = 63; }) = "flow6 { dscp 63; }");
bt_assert(format(flow6 { fragment is_fragment || !first_fragment; }) = "flow6 { fragment is_fragment || !first_fragment; }");
bt_assert(format(flow6 { }) = "flow6 { }");
}
bt_test_suite(t_flowspec, "Testing flowspec routes");
/* /*
* Testing Paths * Testing Paths
* ------------- * -------------

View file

@ -3,6 +3,7 @@ S ip.c
S lists.c S lists.c
S checksum.c bitops.c patmatch.c printf.c xmalloc.c tbf.c S checksum.c bitops.c patmatch.c printf.c xmalloc.c tbf.c
S mac.c S mac.c
S flowspec.c
D resource.sgml D resource.sgml
S resource.c S resource.c
S mempool.c S mempool.c

View file

@ -2,10 +2,10 @@ src := bitops.c checksum.c ip.c lists.c md5.c net.c patmatch.c printf.c sha1.c s
obj := $(src-o-files) obj := $(src-o-files)
$(all-client) $(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) obj := $(src-o-files)
$(all-daemon) $(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_targets := $(tests_targets) $(tests-target-files)
tests_objs := $(tests_objs) $(src-o-files) tests_objs := $(tests_objs) $(src-o-files)

View file

@ -34,6 +34,7 @@
#define ABS(a) ((a)>=0 ? (a) : -(a)) #define ABS(a) ((a)>=0 ? (a) : -(a))
#define DELTA(a,b) (((a)>=(b))?(a)-(b):(b)-(a)) #define DELTA(a,b) (((a)>=(b))?(a)-(b):(b)-(a))
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(*(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 CALL(fn, args...) ({ if (fn) fn(args); })
#define ADVANCE(w, r, l) ({ r -= l; w += l; }) #define ADVANCE(w, r, l) ({ r -= l; w += l; })

View file

@ -46,4 +46,11 @@
#define BUFFER_FLUSH(v) ({ (v).used = 0; }) #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_ */ #endif /* _BIRD_BUFFER_H_ */

1148
lib/flowspec.c Normal file

File diff suppressed because it is too large Load diff

134
lib/flowspec.h Normal file
View file

@ -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_ */

644
lib/flowspec_test.c Normal file
View file

@ -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();
}

View file

@ -2,6 +2,7 @@
#include "nest/bird.h" #include "nest/bird.h"
#include "lib/ip.h" #include "lib/ip.h"
#include "lib/net.h" #include "lib/net.h"
#include "lib/flowspec.h"
const char * const net_label[] = { const char * const net_label[] = {
@ -11,6 +12,8 @@ const char * const net_label[] = {
[NET_VPN6] = "vpn6", [NET_VPN6] = "vpn6",
[NET_ROA4] = "roa4", [NET_ROA4] = "roa4",
[NET_ROA6] = "roa6", [NET_ROA6] = "roa6",
[NET_FLOW4] = "flow4",
[NET_FLOW6] = "flow6"
}; };
const u16 net_addr_length[] = { const u16 net_addr_length[] = {
@ -19,7 +22,9 @@ const u16 net_addr_length[] = {
[NET_VPN4] = sizeof(net_addr_vpn4), [NET_VPN4] = sizeof(net_addr_vpn4),
[NET_VPN6] = sizeof(net_addr_vpn6), [NET_VPN6] = sizeof(net_addr_vpn6),
[NET_ROA4] = sizeof(net_addr_roa4), [NET_ROA4] = sizeof(net_addr_roa4),
[NET_ROA6] = sizeof(net_addr_roa6) [NET_ROA6] = sizeof(net_addr_roa6),
[NET_FLOW4] = 0,
[NET_FLOW6] = 0
}; };
const u8 net_max_prefix_length[] = { const u8 net_max_prefix_length[] = {
@ -28,7 +33,9 @@ const u8 net_max_prefix_length[] = {
[NET_VPN4] = IP4_MAX_PREFIX_LENGTH, [NET_VPN4] = IP4_MAX_PREFIX_LENGTH,
[NET_VPN6] = IP6_MAX_PREFIX_LENGTH, [NET_VPN6] = IP6_MAX_PREFIX_LENGTH,
[NET_ROA4] = IP4_MAX_PREFIX_LENGTH, [NET_ROA4] = IP4_MAX_PREFIX_LENGTH,
[NET_ROA6] = IP6_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[] = { const u16 net_max_text_length[] = {
@ -38,6 +45,8 @@ const u16 net_max_text_length[] = {
[NET_VPN6] = 65, /* "4294967296:4294967296 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" */ [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_ROA4] = 34, /* "255.255.255.255/32-32 AS4294967295" */
[NET_ROA6] = 60, /* "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128-128 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); 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: 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); 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; return 0;
@ -73,11 +86,13 @@ net_pxmask(const net_addr *a)
case NET_IP4: case NET_IP4:
case NET_VPN4: case NET_VPN4:
case NET_ROA4: case NET_ROA4:
case NET_FLOW4:
return ipa_from_ip4(ip4_mkmask(net4_pxlen(a))); return ipa_from_ip4(ip4_mkmask(net4_pxlen(a)));
case NET_IP6: case NET_IP6:
case NET_VPN6: case NET_VPN6:
case NET_ROA6: case NET_ROA6:
case NET_FLOW6:
return ipa_from_ip6(ip6_mkmask(net6_pxlen(a))); return ipa_from_ip6(ip6_mkmask(net6_pxlen(a)));
default: 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); return net_compare_roa4((const net_addr_roa4 *) a, (const net_addr_roa4 *) b);
case NET_ROA6: case NET_ROA6:
return net_compare_roa6((const net_addr_roa6 *) a, (const net_addr_roa6 *) b); 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; return 0;
} }
@ -122,6 +141,8 @@ net_hash(const net_addr *n)
case NET_VPN6: return NET_HASH(n, vpn6); case NET_VPN6: return NET_HASH(n, vpn6);
case NET_ROA4: return NET_HASH(n, roa4); case NET_ROA4: return NET_HASH(n, roa4);
case NET_ROA6: return NET_HASH(n, roa6); 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"); default: bug("invalid type");
} }
} }
@ -135,11 +156,13 @@ net_validate(const net_addr *N)
case NET_IP4: case NET_IP4:
case NET_VPN4: case NET_VPN4:
case NET_ROA4: case NET_ROA4:
case NET_FLOW4:
return net_validate_ip4((net_addr_ip4 *) N); return net_validate_ip4((net_addr_ip4 *) N);
case NET_IP6: case NET_IP6:
case NET_VPN6: case NET_VPN6:
case NET_ROA6: case NET_ROA6:
case NET_FLOW6:
return net_validate_ip6((net_addr_ip6 *) N); return net_validate_ip6((net_addr_ip6 *) N);
default: default:
@ -157,11 +180,13 @@ net_normalize(net_addr *N)
case NET_IP4: case NET_IP4:
case NET_VPN4: case NET_VPN4:
case NET_ROA4: case NET_ROA4:
case NET_FLOW4:
return net_normalize_ip4(&n->ip4); return net_normalize_ip4(&n->ip4);
case NET_IP6: case NET_IP6:
case NET_VPN6: case NET_VPN6:
case NET_ROA6: case NET_ROA6:
case NET_FLOW6:
return net_normalize_ip6(&n->ip6); return net_normalize_ip6(&n->ip6);
} }
} }
@ -176,11 +201,13 @@ net_classify(const net_addr *N)
case NET_IP4: case NET_IP4:
case NET_VPN4: case NET_VPN4:
case NET_ROA4: case NET_ROA4:
case NET_FLOW4:
return ip4_zero(n->ip4.prefix) ? (IADDR_HOST | SCOPE_UNIVERSE) : ip4_classify(n->ip4.prefix); return ip4_zero(n->ip4.prefix) ? (IADDR_HOST | SCOPE_UNIVERSE) : ip4_classify(n->ip4.prefix);
case NET_IP6: case NET_IP6:
case NET_VPN6: case NET_VPN6:
case NET_ROA6: case NET_ROA6:
case NET_FLOW6:
return ip6_zero(n->ip6.prefix) ? (IADDR_HOST | SCOPE_UNIVERSE) : ip6_classify(&n->ip6.prefix); 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_IP4:
case NET_VPN4: case NET_VPN4:
case NET_ROA4: case NET_ROA4:
case NET_FLOW4:
if (!ipa_is_ip4(a)) return 0; if (!ipa_is_ip4(a)) return 0;
return ip4_zero(ip4_and(ip4_xor(ipa_to_ip4(a), net4_prefix(n)), return ip4_zero(ip4_and(ip4_xor(ipa_to_ip4(a), net4_prefix(n)),
ip4_mkmask(net4_pxlen(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_IP6:
case NET_VPN6: case NET_VPN6:
case NET_ROA6: case NET_ROA6:
case NET_FLOW6:
if (ipa_is_ip4(a)) return 0; if (ipa_is_ip4(a)) return 0;
return ip6_zero(ip6_and(ip6_xor(ipa_to_ip6(a), net6_prefix(n)), return ip6_zero(ip6_and(ip6_xor(ipa_to_ip6(a), net6_prefix(n)),
ip6_mkmask(net6_pxlen(n)))); ip6_mkmask(net6_pxlen(n))));

View file

@ -19,7 +19,9 @@
#define NET_VPN6 4 #define NET_VPN6 4
#define NET_ROA4 5 #define NET_ROA4 5
#define NET_ROA6 6 #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_IP4 (1 << NET_IP4)
#define NB_IP6 (1 << NET_IP6) #define NB_IP6 (1 << NET_IP6)
@ -27,6 +29,8 @@
#define NB_VPN6 (1 << NET_VPN6) #define NB_VPN6 (1 << NET_VPN6)
#define NB_ROA4 (1 << NET_ROA4) #define NB_ROA4 (1 << NET_ROA4)
#define NB_ROA6 (1 << NET_ROA6) #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_IP (NB_IP4 | NB_IP6)
#define NB_ANY 0xffffffff #define NB_ANY 0xffffffff
@ -88,6 +92,22 @@ typedef struct net_addr_roa6 {
u32 asn; u32 asn;
} net_addr_roa6; } 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 { typedef union net_addr_union {
net_addr n; net_addr n;
net_addr_ip4 ip4; net_addr_ip4 ip4;
@ -96,6 +116,8 @@ typedef union net_addr_union {
net_addr_vpn6 vpn6; net_addr_vpn6 vpn6;
net_addr_roa4 roa4; net_addr_roa4 roa4;
net_addr_roa6 roa6; net_addr_roa6 roa6;
net_addr_flow4 flow4;
net_addr_flow6 flow6;
} net_addr_union; } net_addr_union;
@ -104,7 +126,7 @@ extern const u16 net_addr_length[];
extern const u8 net_max_prefix_length[]; extern const u8 net_max_prefix_length[];
extern const u16 net_max_text_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) \ #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) \ #define NET_ADDR_ROA6(prefix,pxlen,max_pxlen,asn) \
((net_addr_roa6) { NET_ROA6, pxlen, sizeof(net_addr_roa6), prefix, 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) 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); 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) static inline int net_val_match(u8 type, u32 mask)
{ return !!((1 << type) & mask); } { return !!((1 << type) & mask); }
@ -188,11 +229,13 @@ static inline ip_addr net_prefix(const net_addr *a)
case NET_IP4: case NET_IP4:
case NET_VPN4: case NET_VPN4:
case NET_ROA4: case NET_ROA4:
case NET_FLOW4:
return ipa_from_ip4(net4_prefix(a)); return ipa_from_ip4(net4_prefix(a));
case NET_IP6: case NET_IP6:
case NET_VPN6: case NET_VPN6:
case NET_ROA6: case NET_ROA6:
case NET_FLOW6:
return ipa_from_ip6(net6_prefix(a)); return ipa_from_ip6(net6_prefix(a));
default: 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) static inline int net_equal_roa6(const net_addr_roa6 *a, const net_addr_roa6 *b)
{ return !memcmp(a, b, sizeof(net_addr_roa6)); } { 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) 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); } { 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) static inline int net_zero_roa6(const net_addr_roa6 *a)
{ return !a->pxlen && ip6_zero(a->prefix) && !a->max_pxlen && !a->asn; } { 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) 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); } { 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) 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); } { 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); 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) static inline void net_copy_roa6(net_addr_roa6 *dst, const net_addr_roa6 *src)
{ memcpy(dst, src, sizeof(net_addr_roa6)); } { 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) static inline u32 net_hash_ip4(const net_addr_ip4 *n)
{ return ip4_hash(n->prefix) ^ ((u32) n->pxlen << 26); } { 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) static inline u32 net_hash_roa6(const net_addr_roa6 *n)
{ return ip6_hash(n->prefix) ^ ((u32) n->pxlen << 26); } { 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); u32 net_hash(const net_addr *a);

View file

@ -467,6 +467,10 @@ int
buffer_vprint(buffer *buf, const char *fmt, va_list args) buffer_vprint(buffer *buf, const char *fmt, va_list args)
{ {
int i = bvsnprintf((char *) buf->pos, buf->end - buf->pos, fmt, 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; buf->pos = (i >= 0) ? (buf->pos + i) : buf->end;
return i; return i;
} }
@ -481,6 +485,9 @@ buffer_print(buffer *buf, const char *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); va_end(args);
if ((i < 0) && (buf->pos < buf->end))
*buf->pos = 0;
buf->pos = (i >= 0) ? (buf->pos + i) : buf->end; buf->pos = (i >= 0) ? (buf->pos + i) : buf->end;
return i; return i;
} }
@ -489,13 +496,13 @@ void
buffer_puts(buffer *buf, const char *str) buffer_puts(buffer *buf, const char *str)
{ {
byte *bp = buf->pos; byte *bp = buf->pos;
byte *be = buf->end; byte *be = buf->end - 1;
while (bp < be && *str) while (bp < be && *str)
*bp++ = *str++; *bp++ = *str++;
if (bp < be) if (bp <= be)
*bp = 0; *bp = 0;
buf->pos = bp; buf->pos = (bp < be) ? bp : buf->end;
} }

View file

@ -150,6 +150,8 @@ net_type:
| VPN6 { $$ = NET_VPN6; } | VPN6 { $$ = NET_VPN6; }
| ROA4 { $$ = NET_ROA4; } | ROA4 { $$ = NET_ROA4; }
| ROA6 { $$ = NET_ROA6; } | ROA6 { $$ = NET_ROA6; }
| FLOW4{ $$ = NET_FLOW4; }
| FLOW6{ $$ = NET_FLOW6; }
; ;

View file

@ -193,6 +193,8 @@ fib_hash(struct fib *f, const net_addr *a)
case NET_VPN6: return FIB_HASH(f, a, vpn6); case NET_VPN6: return FIB_HASH(f, a, vpn6);
case NET_ROA4: return FIB_HASH(f, a, roa4); case NET_ROA4: return FIB_HASH(f, a, roa4);
case NET_ROA6: return FIB_HASH(f, a, roa6); 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"); 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_VPN6: return FIB_FIND(f, a, vpn6);
case NET_ROA4: return FIB_FIND(f, a, roa4); case NET_ROA4: return FIB_FIND(f, a, roa4);
case NET_ROA6: return FIB_FIND(f, a, roa6); 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"); 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_VPN6: FIB_INSERT(f, a, e, vpn6); return;
case NET_ROA4: FIB_INSERT(f, a, e, roa4); return; case NET_ROA4: FIB_INSERT(f, a, e, roa4); return;
case NET_ROA6: FIB_INSERT(f, a, e, roa6); 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"); default: bug("invalid type");
} }
} }
@ -334,11 +340,13 @@ fib_route(struct fib *f, const net_addr *n)
case NET_IP4: case NET_IP4:
case NET_VPN4: case NET_VPN4:
case NET_ROA4: case NET_ROA4:
case NET_FLOW4:
return fib_route_ip4(f, (net_addr_ip4 *) n0); return fib_route_ip4(f, (net_addr_ip4 *) n0);
case NET_IP6: case NET_IP6:
case NET_VPN6: case NET_VPN6:
case NET_ROA6: case NET_ROA6:
case NET_FLOW6:
return fib_route_ip6(f, (net_addr_ip6 *) n0); return fib_route_ip6(f, (net_addr_ip6 *) n0);
default: default:

View file

@ -2506,8 +2506,7 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
int first = 1; int first = 1;
int pass = 0; 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) for (e = n->routes; e; e = e->next)
{ {