mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-12-22 01:31:55 +00:00
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:
parent
b94e5e58db
commit
77234bbbde
@ -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
|
||||
|
@ -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_
|
||||
;
|
||||
|
||||
|
||||
|
219
conf/flowspec.Y
Normal file
219
conf/flowspec.Y
Normal 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
|
131
doc/bird.sgml
131
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">
|
||||
|
||||
|
@ -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
|
||||
* -------------
|
||||
|
1
lib/Doc
1
lib/Doc
@ -3,6 +3,7 @@ S ip.c
|
||||
S lists.c
|
||||
S checksum.c bitops.c patmatch.c printf.c xmalloc.c tbf.c
|
||||
S mac.c
|
||||
S flowspec.c
|
||||
D resource.sgml
|
||||
S resource.c
|
||||
S mempool.c
|
||||
|
@ -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)
|
||||
|
@ -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; })
|
||||
|
||||
|
@ -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_ */
|
||||
|
1148
lib/flowspec.c
Normal file
1148
lib/flowspec.c
Normal file
File diff suppressed because it is too large
Load Diff
134
lib/flowspec.h
Normal file
134
lib/flowspec.h
Normal 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
644
lib/flowspec_test.c
Normal 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();
|
||||
}
|
33
lib/net.c
33
lib/net.c
@ -2,6 +2,7 @@
|
||||
#include "nest/bird.h"
|
||||
#include "lib/ip.h"
|
||||
#include "lib/net.h"
|
||||
#include "lib/flowspec.h"
|
||||
|
||||
|
||||
const char * const net_label[] = {
|
||||
@ -11,6 +12,8 @@ const char * const net_label[] = {
|
||||
[NET_VPN6] = "vpn6",
|
||||
[NET_ROA4] = "roa4",
|
||||
[NET_ROA6] = "roa6",
|
||||
[NET_FLOW4] = "flow4",
|
||||
[NET_FLOW6] = "flow6"
|
||||
};
|
||||
|
||||
const u16 net_addr_length[] = {
|
||||
@ -19,7 +22,9 @@ const u16 net_addr_length[] = {
|
||||
[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_ROA6] = sizeof(net_addr_roa6),
|
||||
[NET_FLOW4] = 0,
|
||||
[NET_FLOW6] = 0
|
||||
};
|
||||
|
||||
const u8 net_max_prefix_length[] = {
|
||||
@ -28,7 +33,9 @@ const u8 net_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_ROA6] = IP6_MAX_PREFIX_LENGTH,
|
||||
[NET_FLOW4] = IP4_MAX_PREFIX_LENGTH,
|
||||
[NET_FLOW6] = IP6_MAX_PREFIX_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_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))));
|
||||
|
79
lib/net.h
79
lib/net.h
@ -19,7 +19,9 @@
|
||||
#define NET_VPN6 4
|
||||
#define NET_ROA4 5
|
||||
#define NET_ROA6 6
|
||||
#define NET_MAX 7
|
||||
#define NET_FLOW4 7
|
||||
#define NET_FLOW6 8
|
||||
#define NET_MAX 9
|
||||
|
||||
#define NB_IP4 (1 << NET_IP4)
|
||||
#define NB_IP6 (1 << NET_IP6)
|
||||
@ -27,6 +29,8 @@
|
||||
#define NB_VPN6 (1 << NET_VPN6)
|
||||
#define NB_ROA4 (1 << NET_ROA4)
|
||||
#define NB_ROA6 (1 << NET_ROA6)
|
||||
#define NB_FLOW4 (1 << NET_FLOW4)
|
||||
#define NB_FLOW6 (1 << NET_FLOW6)
|
||||
|
||||
#define NB_IP (NB_IP4 | NB_IP6)
|
||||
#define NB_ANY 0xffffffff
|
||||
@ -88,6 +92,22 @@ typedef struct net_addr_roa6 {
|
||||
u32 asn;
|
||||
} net_addr_roa6;
|
||||
|
||||
typedef struct net_addr_flow4 {
|
||||
u8 type;
|
||||
u8 pxlen;
|
||||
u16 length;
|
||||
ip4_addr prefix;
|
||||
byte data[0];
|
||||
} net_addr_flow4;
|
||||
|
||||
typedef struct net_addr_flow6 {
|
||||
u8 type;
|
||||
u8 pxlen;
|
||||
u16 length;
|
||||
ip6_addr prefix;
|
||||
byte data[0];
|
||||
} net_addr_flow6;
|
||||
|
||||
typedef union net_addr_union {
|
||||
net_addr n;
|
||||
net_addr_ip4 ip4;
|
||||
@ -96,6 +116,8 @@ typedef union net_addr_union {
|
||||
net_addr_vpn6 vpn6;
|
||||
net_addr_roa4 roa4;
|
||||
net_addr_roa6 roa6;
|
||||
net_addr_flow4 flow4;
|
||||
net_addr_flow6 flow6;
|
||||
} net_addr_union;
|
||||
|
||||
|
||||
@ -104,7 +126,7 @@ extern const u16 net_addr_length[];
|
||||
extern const u8 net_max_prefix_length[];
|
||||
extern const u16 net_max_text_length[];
|
||||
|
||||
#define NET_MAX_TEXT_LENGTH 65
|
||||
#define NET_MAX_TEXT_LENGTH 256
|
||||
|
||||
|
||||
#define NET_ADDR_IP4(prefix,pxlen) \
|
||||
@ -125,6 +147,12 @@ extern const u16 net_max_text_length[];
|
||||
#define NET_ADDR_ROA6(prefix,pxlen,max_pxlen,asn) \
|
||||
((net_addr_roa6) { NET_ROA6, pxlen, sizeof(net_addr_roa6), prefix, max_pxlen, asn })
|
||||
|
||||
#define NET_ADDR_FLOW4(prefix,pxlen,dlen) \
|
||||
((net_addr_flow4) { NET_FLOW4, pxlen, sizeof(net_addr_ip4) + dlen, prefix })
|
||||
|
||||
#define NET_ADDR_FLOW6(prefix,pxlen,dlen) \
|
||||
((net_addr_flow6) { NET_FLOW6, pxlen, sizeof(net_addr_ip6) + dlen, prefix })
|
||||
|
||||
|
||||
|
||||
static inline void net_fill_ip4(net_addr *a, ip4_addr prefix, uint pxlen)
|
||||
@ -161,6 +189,19 @@ static inline void net_fill_ip_host(net_addr *a, ip_addr prefix)
|
||||
net_fill_ip6(a, ipa_to_ip6(prefix), IP6_MAX_PREFIX_LENGTH);
|
||||
}
|
||||
|
||||
static inline void net_fill_flow4(net_addr *a, ip4_addr prefix, uint pxlen, byte *data, uint dlen)
|
||||
{
|
||||
net_addr_flow4 *f = (void *) a;
|
||||
*f = NET_ADDR_FLOW4(prefix, pxlen, dlen);
|
||||
memcpy(f->data, data, dlen);
|
||||
}
|
||||
|
||||
static inline void net_fill_flow6(net_addr *a, ip6_addr prefix, uint pxlen, byte *data, uint dlen)
|
||||
{
|
||||
net_addr_flow6 *f = (void *) a;
|
||||
*f = NET_ADDR_FLOW6(prefix, pxlen, dlen);
|
||||
memcpy(f->data, data, dlen);
|
||||
}
|
||||
|
||||
static inline int net_val_match(u8 type, u32 mask)
|
||||
{ return !!((1 << type) & mask); }
|
||||
@ -188,11 +229,13 @@ static inline ip_addr net_prefix(const net_addr *a)
|
||||
case NET_IP4:
|
||||
case NET_VPN4:
|
||||
case NET_ROA4:
|
||||
case NET_FLOW4:
|
||||
return ipa_from_ip4(net4_prefix(a));
|
||||
|
||||
case NET_IP6:
|
||||
case NET_VPN6:
|
||||
case NET_ROA6:
|
||||
case NET_FLOW6:
|
||||
return ipa_from_ip6(net6_prefix(a));
|
||||
|
||||
default:
|
||||
@ -233,6 +276,13 @@ static inline int net_equal_roa4(const net_addr_roa4 *a, const net_addr_roa4 *b)
|
||||
static inline int net_equal_roa6(const net_addr_roa6 *a, const net_addr_roa6 *b)
|
||||
{ return !memcmp(a, b, sizeof(net_addr_roa6)); }
|
||||
|
||||
static inline int net_equal_flow4(const net_addr_flow4 *a, const net_addr_flow4 *b)
|
||||
{ return net_equal((const net_addr *) a, (const net_addr *) b); }
|
||||
|
||||
static inline int net_equal_flow6(const net_addr_flow6 *a, const net_addr_flow6 *b)
|
||||
{ return net_equal((const net_addr *) a, (const net_addr *) b); }
|
||||
|
||||
|
||||
static inline int net_equal_prefix_roa4(const net_addr_roa4 *a, const net_addr_roa4 *b)
|
||||
{ return ip4_equal(a->prefix, b->prefix) && (a->pxlen == b->pxlen); }
|
||||
|
||||
@ -258,6 +308,13 @@ static inline int net_zero_roa4(const net_addr_roa4 *a)
|
||||
static inline int net_zero_roa6(const net_addr_roa6 *a)
|
||||
{ return !a->pxlen && ip6_zero(a->prefix) && !a->max_pxlen && !a->asn; }
|
||||
|
||||
static inline int net_zero_flow4(const net_addr_flow4 *a)
|
||||
{ return !a->pxlen && ip4_zero(a->prefix) && !a->data; }
|
||||
|
||||
static inline int net_zero_flow6(const net_addr_flow6 *a)
|
||||
{ return !a->pxlen && ip6_zero(a->prefix) && !a->data; }
|
||||
|
||||
|
||||
|
||||
static inline int net_compare_ip4(const net_addr_ip4 *a, const net_addr_ip4 *b)
|
||||
{ return ip4_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen); }
|
||||
@ -277,6 +334,12 @@ static inline int net_compare_roa4(const net_addr_roa4 *a, const net_addr_roa4 *
|
||||
static inline int net_compare_roa6(const net_addr_roa6 *a, const net_addr_roa6 *b)
|
||||
{ return ip6_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen) ?: uint_cmp(a->max_pxlen, b->max_pxlen) ?: uint_cmp(a->asn, b->asn); }
|
||||
|
||||
static inline int net_compare_flow4(const net_addr_flow4 *a, const net_addr_flow4 *b)
|
||||
{ return ip4_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen) ?: uint_cmp(a->length, b->length) ?: memcmp(a->data, b->data, a->length - sizeof(net_addr_flow4)); }
|
||||
|
||||
static inline int net_compare_flow6(const net_addr_flow6 *a, const net_addr_flow6 *b)
|
||||
{ return ip6_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen) ?: uint_cmp(a->length, b->length) ?: memcmp(a->data, b->data, a->length - sizeof(net_addr_flow6)); }
|
||||
|
||||
int net_compare(const net_addr *a, const net_addr *b);
|
||||
|
||||
|
||||
@ -301,6 +364,12 @@ static inline void net_copy_roa4(net_addr_roa4 *dst, const net_addr_roa4 *src)
|
||||
static inline void net_copy_roa6(net_addr_roa6 *dst, const net_addr_roa6 *src)
|
||||
{ memcpy(dst, src, sizeof(net_addr_roa6)); }
|
||||
|
||||
static inline void net_copy_flow4(net_addr_flow4 *dst, const net_addr_flow4 *src)
|
||||
{ memcpy(dst, src, src->length); }
|
||||
|
||||
static inline void net_copy_flow6(net_addr_flow6 *dst, const net_addr_flow6 *src)
|
||||
{ memcpy(dst, src, src->length); }
|
||||
|
||||
|
||||
static inline u32 net_hash_ip4(const net_addr_ip4 *n)
|
||||
{ return ip4_hash(n->prefix) ^ ((u32) n->pxlen << 26); }
|
||||
@ -324,6 +393,12 @@ static inline u32 net_hash_roa4(const net_addr_roa4 *n)
|
||||
static inline u32 net_hash_roa6(const net_addr_roa6 *n)
|
||||
{ return ip6_hash(n->prefix) ^ ((u32) n->pxlen << 26); }
|
||||
|
||||
static inline u32 net_hash_flow4(const net_addr_flow4 *n)
|
||||
{ return ip4_hash(n->prefix) ^ ((u32) n->pxlen << 26); }
|
||||
|
||||
static inline u32 net_hash_flow6(const net_addr_flow6 *n)
|
||||
{ return ip6_hash(n->prefix) ^ ((u32) n->pxlen << 26); }
|
||||
|
||||
u32 net_hash(const net_addr *a);
|
||||
|
||||
|
||||
|
13
lib/printf.c
13
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;
|
||||
}
|
||||
@ -481,6 +485,9 @@ buffer_print(buffer *buf, const char *fmt, ...)
|
||||
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;
|
||||
}
|
||||
|
@ -150,6 +150,8 @@ net_type:
|
||||
| VPN6 { $$ = NET_VPN6; }
|
||||
| ROA4 { $$ = NET_ROA4; }
|
||||
| ROA6 { $$ = NET_ROA6; }
|
||||
| FLOW4{ $$ = NET_FLOW4; }
|
||||
| FLOW6{ $$ = NET_FLOW6; }
|
||||
;
|
||||
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user