Route Origin Authorization basics.

- ROA tables, which are used as a basic part for RPKI.
 - Commands for examining and modifying ROA tables.
 - Filter operators based on ROA tables consistent with RFC 6483.
This commit is contained in:
Ondrej Zajicek 2012-03-18 17:32:30 +01:00
parent fd087589f8
commit af582c4811
17 changed files with 773 additions and 12 deletions

View file

@ -536,6 +536,8 @@ cf_symbol_class_name(struct symbol *sym)
return "network address"; return "network address";
case SYM_TEMPLATE: case SYM_TEMPLATE:
return "protocol template"; return "protocol template";
case SYM_ROA:
return "ROA table";
default: default:
return "unknown type"; return "unknown type";
} }

View file

@ -112,6 +112,7 @@ config_parse(struct config *c)
sysdep_preconfig(c); sysdep_preconfig(c);
protos_preconfig(c); protos_preconfig(c);
rt_preconfig(c); rt_preconfig(c);
roa_preconfig(c);
cf_parse(); cf_parse();
protos_postconfig(c); protos_postconfig(c);
if (EMPTY_LIST(c->protos)) if (EMPTY_LIST(c->protos))
@ -210,6 +211,7 @@ config_do_commit(struct config *c, int type)
force_restart |= global_commit(c, old_config); force_restart |= global_commit(c, old_config);
DBG("rt_commit\n"); DBG("rt_commit\n");
rt_commit(c, old_config); rt_commit(c, old_config);
roa_commit(c, old_config);
DBG("protos_commit\n"); DBG("protos_commit\n");
protos_commit(c, old_config, force_restart, type); protos_commit(c, old_config, force_restart, type);
new_config = NULL; /* Just to be sure nobody uses that now */ new_config = NULL; /* Just to be sure nobody uses that now */

View file

@ -21,7 +21,9 @@ struct config {
linpool *mem; /* Linear pool containing configuration data */ linpool *mem; /* Linear pool containing configuration data */
list protos; /* Configured protocol instances (struct proto_config) */ list protos; /* Configured protocol instances (struct proto_config) */
list tables; /* Configured routing tables (struct rtable_config) */ list tables; /* Configured routing tables (struct rtable_config) */
list roa_tables; /* Configured ROA tables (struct roa_table_config) */
list logfiles; /* Configured log fils (sysdep) */ list logfiles; /* Configured log fils (sysdep) */
int mrtdump_file; /* Configured MRTDump file (sysdep, fd in unix) */ int mrtdump_file; /* Configured MRTDump file (sysdep, fd in unix) */
char *syslog_name; /* Name used for syslog (NULL -> no syslog) */ char *syslog_name; /* Name used for syslog (NULL -> no syslog) */
struct rtable_config *master_rtc; /* Configuration of master routing table */ struct rtable_config *master_rtc; /* Configuration of master routing table */
@ -110,6 +112,7 @@ struct symbol {
#define SYM_TABLE 5 #define SYM_TABLE 5
#define SYM_IPA 6 #define SYM_IPA 6
#define SYM_TEMPLATE 7 #define SYM_TEMPLATE 7
#define SYM_ROA 8
#define SYM_VARIABLE 0x100 /* 0x100-0x1ff are variable types */ #define SYM_VARIABLE 0x100 /* 0x100-0x1ff are variable types */

View file

@ -50,9 +50,11 @@ CF_DECLS
struct f_path_mask *h; struct f_path_mask *h;
struct password_item *p; struct password_item *p;
struct rt_show_data *ra; struct rt_show_data *ra;
struct roa_show_data *ro;
struct sym_show_data *sd; struct sym_show_data *sd;
struct lsadb_show_data *ld; struct lsadb_show_data *ld;
struct iface *iface; struct iface *iface;
struct roa_table *rot;
void *g; void *g;
bird_clock_t time; bird_clock_t time;
struct prefix px; struct prefix px;

View file

@ -358,6 +358,22 @@ protocol rip {
routing table is created implicitly, other routing tables have routing table is created implicitly, other routing tables have
to be added by this command. to be added by this command.
<tag>roa table [ { roa table options ... } ] <m/name/</tag>
Create a new ROA (Route Origin Authorization) table. ROA
tables can be used to validate route origination of BGP
routes. A ROA table contains ROA entries, each consist of a
network prefix, a max prefix length and an AS number. A ROA
entry specifies prefixes which could be originated by that AS
number. ROA tables could be filled with data from RPKI (RFC
6480) or from public databases like Whois. ROA tables are
examined by <cf/roa_check()/ operator in filters.
Currently, there is just one option,
<cf>roa <m/prefix/ max <m/num/ as <m/num/</cf>, which
can be used to populate the ROA table with static ROA
entries. The option may be used multiple times. Other entries
can be added dynamically by <cf/add roa/ command.
<tag>eval <m/expr/</tag> Evaluates given filter expression. It <tag>eval <m/expr/</tag> Evaluates given filter expression. It
is used by us for testing of filters. is used by us for testing of filters.
</descrip> </descrip>
@ -570,7 +586,7 @@ This argument can be omitted if there exists only a single instance.
<tag>show interfaces [summary]</tag> <tag>show interfaces [summary]</tag>
Show the list of interfaces. For each interface, print its type, state, MTU and addresses assigned. Show the list of interfaces. For each interface, print its type, state, MTU and addresses assigned.
<tag>show symbols [table|filter|function|protocol|template|<symbol>]</tag> <tag>show symbols [table|filter|function|protocol|template|roa|<symbol>]</tag>
Show the list of symbols defined in the configuration (names of protocols, routing tables etc.). Show the list of symbols defined in the configuration (names of protocols, routing tables etc.).
<tag>show route [[for] <m/prefix/|<m/IP/] [table <m/sym/] [filter <m/f/|where <m/c/] [(export|preexport) <m/p/] [protocol <m/p/] [<m/options/]</tag> <tag>show route [[for] <m/prefix/|<m/IP/] [table <m/sym/] [filter <m/f/|where <m/c/] [(export|preexport) <m/p/] [protocol <m/p/] [<m/options/]</tag>
@ -599,6 +615,29 @@ This argument can be omitted if there exists only a single instance.
number of networks, number of routes before and after filtering). If number of networks, number of routes before and after filtering). If
you use <cf/count/ instead, only the statistics will be printed. you use <cf/count/ instead, only the statistics will be printed.
<tag>show xroa [<m/prefix/ | in <m/prefix/ | for <m/prefix/] [as <m/num/] [table <m/t/>]</tag>
Show contents of a ROA table (by default of the first one).
You can specify a <m/prefix/ to print ROA entries for a
specific network. If you use <cf>for <m/prefix/</cf>, you'll
get all entries relevant for route validation of the network
prefix; i.e., ROA entries whose prefixes cover the network
prefix. Or you can use <cf>in <m/prefix/</cf> to get ROA entries
covered by the network prefix. You could also use <cf/as/ option
to show just entries for given AS.
<tag>add roa <m/prefix/ max <m/num/] as <m/num/ [table <m/t/>]</tag>
Add a new ROA entry to a ROA table. Such entry is called
<it/dynamic/ compared to <it/static/ entries specified in the
config file. These dynamic entries survive reconfiguration.
<tag>delete roa <m/prefix/ max <m/num/] as <m/num/ [table <m/t/>]</tag>
Delete the specified ROA entry from a ROA table. Only dynamic
ROA entries (i.e., the ones added by <cf/add roa/ command) can
be deleted.
<tag>flush roa [table <m/t/>]</tag>
Remove all dynamic ROA entries from a ROA table.
<tag>configure [soft] ["<m/config file/"]</tag> <tag>configure [soft] ["<m/config file/"]</tag>
Reload configuration from a given file. BIRD will smoothly Reload configuration from a given file. BIRD will smoothly
switch itself to the new configuration, protocols are switch itself to the new configuration, protocols are
@ -918,6 +957,17 @@ used on element and set of elements of the same type (returning true if element
on two strings (returning true if first string matches a shell-like pattern stored in second string) or on IP and prefix (returning true if IP is within the range defined by that prefix), or on on two strings (returning true if first string matches a shell-like pattern stored in second string) or on IP and prefix (returning true if IP is within the range defined by that prefix), or on
prefix and prefix (returning true if first prefix is more specific than second one) or on bgppath and bgpmask (returning true if the path matches the mask) or on number and bgppath (returning true if the number is in the path) or on pair/quad and clist (returning true if the pair/quad is element of the clist) or on clist and pair/quad set (returning true if there is an element of the clist that is also a member of the pair/quad set). prefix and prefix (returning true if first prefix is more specific than second one) or on bgppath and bgpmask (returning true if the path matches the mask) or on number and bgppath (returning true if the number is in the path) or on pair/quad and clist (returning true if the pair/quad is element of the clist) or on clist and pair/quad set (returning true if there is an element of the clist that is also a member of the pair/quad set).
<p>There is one operator related to ROA infrastructure -
<cf/roa_check()/. It examines a ROA table and does RFC 6483 route
origin validation for a given network prefix. The basic usage
is <cf>roa_check(<m/table/)</cf>, which checks current route (which
should be from BGP to have AS_PATH argument) in the specified ROA
table and returns ROA_UNKNOWN if there is no relevant ROA, ROA_VALID
if there is a matching ROA, or ROA_INVALID if there are some relevant
ROAs but none of them match. There is also an extended variant
<cf>roa_check(<m/table/, <m/prefix/, <m/asn/)</cf>, which allows to
specify a prefix and an ASN as arguments.
<sect>Control structures <sect>Control structures

View file

@ -259,6 +259,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
DEFINED, DEFINED,
ADD, DELETE, CONTAINS, RESET, ADD, DELETE, CONTAINS, RESET,
PREPEND, FIRST, LAST, MATCH, PREPEND, FIRST, LAST, MATCH,
ROA_CHECK,
EMPTY, EMPTY,
FILTER, WHERE, EVAL) FILTER, WHERE, EVAL)
@ -755,6 +756,9 @@ term:
| DELETE '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'd'; } | DELETE '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'd'; }
| FILTER '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'f'; } | FILTER '(' term ',' term ')' { $$ = f_new_inst(); $$->code = P('C','a'); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'f'; }
| ROA_CHECK '(' SYM ')' { $$ = f_generate_roa_check($3, NULL, NULL); }
| ROA_CHECK '(' SYM ',' term ',' term ')' { $$ = f_generate_roa_check($3, $5, $7); }
/* | term '.' LEN { $$->code = P('P','l'); } */ /* | term '.' LEN { $$->code = P('P','l'); } */
/* function_call is inlined here */ /* function_call is inlined here */
@ -801,7 +805,6 @@ print_list: /* EMPTY */ { $$ = NULL; }
$$ = $1; $$ = $1;
} else $$ = $3; } else $$ = $3;
} }
; ;
var_listn: term { var_listn: term {

View file

@ -54,6 +54,24 @@ f_generate_complex(int operation, int operation_aux, struct f_inst *dyn, struct
return set_dyn; return set_dyn;
} }
struct f_inst *
f_generate_roa_check(struct symbol *sym, struct f_inst *prefix, struct f_inst *asn)
{
struct f_inst_roa_check *ret = cfg_allocz(sizeof(struct f_inst_roa_check));
ret->i.code = P('R','C');
ret->i.lineno = ifs->conf_lino;
ret->i.arg1 = prefix;
ret->i.arg2 = asn;
/* prefix == NULL <-> asn == NULL */
if ((sym->class != SYM_ROA) || ! sym->def)
cf_error("%s is not a ROA table", sym->name);
ret->rtc = sym->def;
return &ret->i;
}
char * char *
filter_name(struct filter *filter) filter_name(struct filter *filter)
{ {

View file

@ -248,7 +248,7 @@ val_simple_in_range(struct f_val v1, struct f_val v2)
return ipa_in_net(v1.val.px.ip, v2.val.px.ip, v2.val.px.len); return ipa_in_net(v1.val.px.ip, v2.val.px.ip, v2.val.px.len);
if ((v1.type == T_PREFIX) && (v2.type == T_PREFIX)) if ((v1.type == T_PREFIX) && (v2.type == T_PREFIX))
return ipa_in_net(v1.val.px.ip, v2.val.px.ip, v2.val.px.len) && (v1.val.px.len >= v2.val.px.len); return net_in_net(v1.val.px.ip, v1.val.px.len, v2.val.px.ip, v2.val.px.len);
return CMP_ERROR; return CMP_ERROR;
} }
@ -1208,6 +1208,38 @@ interpret(struct f_inst *what)
break; break;
case P('R','C'): /* ROA Check */
if (what->arg1)
{
TWOARGS;
if ((v1.type != T_PREFIX) || (v2.type != T_INT))
runtime("Invalid argument to roa_check()");
as = v2.val.i;
}
else
{
v1.val.px.ip = (*f_rte)->net->n.prefix;
v1.val.px.len = (*f_rte)->net->n.pxlen;
/* We ignore temporary attributes, probably not a problem here */
/* 0x02 is a value of BA_AS_PATH, we don't want to include BGP headers */
eattr *e = ea_find((*f_rte)->attrs->eattrs, EA_CODE(EAP_BGP, 0x02));
if (!e || e->type != EAF_TYPE_AS_PATH)
runtime("Missing AS_PATH attribute");
as_path_get_last(e->u.ptr, &as);
}
struct roa_table_config *rtc = ((struct f_inst_roa_check *) what)->rtc;
if (!rtc->table)
runtime("Missing ROA table");
res.type = T_ENUM_ROA;
res.val.i = roa_check(rtc->table, v1.val.px.ip, v1.val.px.len, as);
break;
default: default:
bug( "Unknown instruction %d (%c)", what->code, what->code & 0xff); bug( "Unknown instruction %d (%c)", what->code, what->code & 0xff);
} }
@ -1332,6 +1364,13 @@ i_same(struct f_inst *f1, struct f_inst *f2)
case P('C','a'): TWOARGS; break; case P('C','a'): TWOARGS; break;
case P('a','f'): case P('a','f'):
case P('a','l'): ONEARG; break; case P('a','l'): ONEARG; break;
case P('R','C'):
TWOARGS;
/* Does not really make sense - ROA check resuls may change anyway */
if (strcmp(((struct f_inst_roa_check *) f1)->rtc->name,
((struct f_inst_roa_check *) f2)->rtc->name))
return 0;
break;
default: default:
bug( "Unknown instruction %d in same (%c)", f1->code, f1->code & 0xff); bug( "Unknown instruction %d in same (%c)", f1->code, f1->code & 0xff);
} }

View file

@ -32,6 +32,12 @@ struct f_inst { /* Instruction */
#define arg1 a1.p #define arg1 a1.p
#define arg2 a2.p #define arg2 a2.p
/* Not enough fields in f_inst for three args used by roa_check() */
struct f_inst_roa_check {
struct f_inst i;
struct roa_table_config *rtc;
};
struct f_prefix { struct f_prefix {
ip_addr ip; ip_addr ip;
int len; int len;
@ -66,6 +72,8 @@ struct f_inst *f_new_inst(void);
struct f_inst *f_new_dynamic_attr(int type, int f_type, int code); /* Type as core knows it, type as filters know it, and code of dynamic attribute */ struct f_inst *f_new_dynamic_attr(int type, int f_type, int code); /* Type as core knows it, type as filters know it, and code of dynamic attribute */
struct f_tree *f_new_tree(void); struct f_tree *f_new_tree(void);
struct f_inst *f_generate_complex(int operation, int operation_aux, struct f_inst *dyn, struct f_inst *argument); struct f_inst *f_generate_complex(int operation, int operation_aux, struct f_inst *dyn, struct f_inst *argument);
struct f_inst *f_generate_roa_check(struct symbol *sym, struct f_inst *prefix, struct f_inst *asn);
struct f_tree *build_tree(struct f_tree *); struct f_tree *build_tree(struct f_tree *);
struct f_tree *find_tree(struct f_tree *t, struct f_val val); struct f_tree *find_tree(struct f_tree *t, struct f_val val);
@ -141,6 +149,7 @@ int tree_compare(const void *p1, const void *p2);
#define T_ENUM_SCOPE 0x32 #define T_ENUM_SCOPE 0x32
#define T_ENUM_RTC 0x33 #define T_ENUM_RTC 0x33
#define T_ENUM_RTD 0x34 #define T_ENUM_RTD 0x34
#define T_ENUM_ROA 0x35
/* new enums go here */ /* new enums go here */
#define T_ENUM_EMPTY 0x3f /* Special hack for atomic_aggr */ #define T_ENUM_EMPTY 0x3f /* Special hack for atomic_aggr */

View file

@ -52,6 +52,32 @@ function fifteen()
return 15; return 15;
} }
roa table rl
{
roa 10.110.0.0/16 max 16 as 1000;
roa 10.120.0.0/16 max 24 as 1000;
roa 10.130.0.0/16 max 24 as 2000;
roa 10.130.128.0/18 max 24 as 3000;
}
function test_roa()
{
# cannot be tested in __startup(), sorry
print "Testing ROA";
print "Should be true: ", roa_check(rl, 10.10.0.0/16, 1000) = ROA_UNKNOWN,
" ", roa_check(rl, 10.0.0.0/8, 1000) = ROA_UNKNOWN,
" ", roa_check(rl, 10.110.0.0/16, 1000) = ROA_VALID,
" ", roa_check(rl, 10.110.0.0/16, 2000) = ROA_INVALID,
" ", roa_check(rl, 10.110.32.0/20, 1000) = ROA_INVALID,
" ", roa_check(rl, 10.120.32.0/20, 1000) = ROA_VALID;
print "Should be true: ", roa_check(rl, 10.120.32.0/20, 2000) = ROA_INVALID,
" ", roa_check(rl, 10.120.32.32/28, 1000) = ROA_INVALID,
" ", roa_check(rl, 10.130.130.0/24, 1000) = ROA_INVALID,
" ", roa_check(rl, 10.130.130.0/24, 2000) = ROA_VALID,
" ", roa_check(rl, 10.130.30.0/24, 3000) = ROA_INVALID,
" ", roa_check(rl, 10.130.130.0/24, 3000) = ROA_VALID;
}
function paths() function paths()
bgpmask pm1; bgpmask pm1;
bgpmask pm2; bgpmask pm2;
@ -163,7 +189,7 @@ eclist el2;
print "eclist A isect B: ", filter( el, el2 ); print "eclist A isect B: ", filter( el, el2 );
print "eclist A \ B: ", delete( el, el2 ); print "eclist A \ B: ", delete( el, el2 );
# test_roa();
} }
function bla() function bla()

View file

@ -15,7 +15,10 @@
#include "ipv6.h" #include "ipv6.h"
#endif #endif
#define ipa_in_net(x,n,p) (!ipa_nonzero(ipa_and(ipa_xor((n),(x)),ipa_mkmask(p)))) #define ipa_zero(x) (!ipa_nonzero(x))
#define ip_is_prefix(a,l) (!ipa_nonzero(ipa_and(a, ipa_not(ipa_mkmask(l)))))
#define ipa_in_net(x,n,p) (ipa_zero(ipa_and(ipa_xor((n),(x)),ipa_mkmask(p))))
#define net_in_net(n1,l1,n2,l2) (((l1) >= (l2)) && (ipa_zero(ipa_and(ipa_xor((n1),(n2)),ipa_mkmask(l2)))))
/* /*
* ip_classify() returns either a negative number for invalid addresses * ip_classify() returns either a negative number for invalid addresses
@ -50,9 +53,6 @@ struct prefix {
unsigned int len; unsigned int len;
}; };
#define ip_is_prefix(a,l) (!ipa_nonzero(ipa_and(a, ipa_not(ipa_mkmask(l)))))
#define ipa_zero(x) (!ipa_nonzero(x))
static inline int ipa_classify_net(ip_addr a) static inline int ipa_classify_net(ip_addr a)
{ return ipa_zero(a) ? (IADDR_HOST | SCOPE_UNIVERSE) : ipa_classify(a); } { return ipa_zero(a) ? (IADDR_HOST | SCOPE_UNIVERSE) : ipa_classify(a); }

View file

@ -1,4 +1,4 @@
source=rt-table.c rt-fib.c rt-attr.c proto.c iface.c rt-dev.c password.c cli.c locks.c cmds.c neighbor.c \ source=rt-table.c rt-fib.c rt-attr.c rt-roa.c proto.c iface.c rt-dev.c password.c cli.c locks.c cmds.c neighbor.c \
a-path.c a-set.c a-path.c a-set.c
root-rel=../ root-rel=../
dir-name=nest dir-name=nest

View file

@ -72,6 +72,7 @@ print_size(char *dsc, size_t val)
extern pool *rt_table_pool; extern pool *rt_table_pool;
extern pool *rta_pool; extern pool *rta_pool;
extern pool *roa_pool;
extern pool *proto_pool; extern pool *proto_pool;
void void
@ -80,6 +81,7 @@ cmd_show_memory(void)
cli_msg(-1018, "BIRD memory usage"); cli_msg(-1018, "BIRD memory usage");
print_size("Routing tables:", rmemsize(rt_table_pool)); print_size("Routing tables:", rmemsize(rt_table_pool));
print_size("Route attributes:", rmemsize(rta_pool)); print_size("Route attributes:", rmemsize(rta_pool));
print_size("ROA tables:", rmemsize(roa_pool));
print_size("Protocols:", rmemsize(proto_pool)); print_size("Protocols:", rmemsize(proto_pool));
print_size("Total:", rmemsize(&root_pool)); print_size("Total:", rmemsize(&root_pool));
cli_msg(0, ""); cli_msg(0, "");

View file

@ -19,6 +19,7 @@ CF_DEFINES
static struct proto_config *this_proto; static struct proto_config *this_proto;
static struct iface_patt *this_ipatt; static struct iface_patt *this_ipatt;
static struct iface_patt_node *this_ipn; static struct iface_patt_node *this_ipn;
static struct roa_table_config *this_roa_table;
static list *this_p_list; static list *this_p_list;
static struct password_item *this_p_item; static struct password_item *this_p_item;
static int password_id; static int password_id;
@ -44,7 +45,7 @@ CF_DECLS
CF_KEYWORDS(ROUTER, ID, PROTOCOL, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT) CF_KEYWORDS(ROUTER, ID, PROTOCOL, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT)
CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, TABLE, STATES, ROUTES, FILTERS) CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, TABLE, STATES, ROUTES, FILTERS)
CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES) CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES)
CF_KEYWORDS(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, GENERATE) CF_KEYWORDS(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, GENERATE, ROA, XROA, MAX, FLUSH)
CF_KEYWORDS(LISTEN, BGP, V6ONLY, DUAL, ADDRESS, PORT, PASSWORDS, DESCRIPTION) CF_KEYWORDS(LISTEN, BGP, V6ONLY, DUAL, ADDRESS, PORT, PASSWORDS, DESCRIPTION)
CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC) CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC)
@ -53,14 +54,17 @@ CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIREC
CF_ENUM(T_ENUM_SCOPE, SCOPE_, HOST, LINK, SITE, ORGANIZATION, UNIVERSE, UNDEFINED) CF_ENUM(T_ENUM_SCOPE, SCOPE_, HOST, LINK, SITE, ORGANIZATION, UNIVERSE, UNDEFINED)
CF_ENUM(T_ENUM_RTC, RTC_, UNICAST, BROADCAST, MULTICAST, ANYCAST) CF_ENUM(T_ENUM_RTC, RTC_, UNICAST, BROADCAST, MULTICAST, ANYCAST)
CF_ENUM(T_ENUM_RTD, RTD_, ROUTER, DEVICE, BLACKHOLE, UNREACHABLE, PROHIBIT, MULTIPATH) CF_ENUM(T_ENUM_RTD, RTD_, ROUTER, DEVICE, BLACKHOLE, UNREACHABLE, PROHIBIT, MULTIPATH)
CF_ENUM(T_ENUM_ROA, ROA_, UNKNOWN, VALID, INVALID)
%type <i32> idval %type <i32> idval
%type <f> imexport %type <f> imexport
%type <r> rtable %type <r> rtable
%type <s> optsym %type <s> optsym
%type <ra> r_args %type <ra> r_args
%type <ro> roa_args
%type <rot> roa_table_arg
%type <sd> sym_args %type <sd> sym_args
%type <i> proto_start echo_mask echo_size debug_mask debug_list debug_flag mrtdump_mask mrtdump_list mrtdump_flag export_or_preexport %type <i> proto_start echo_mask echo_size debug_mask debug_list debug_flag mrtdump_mask mrtdump_list mrtdump_flag export_or_preexport roa_mode
%type <ps> proto_patt proto_patt2 %type <ps> proto_patt proto_patt2
CF_GRAMMAR CF_GRAMMAR
@ -113,6 +117,24 @@ newtab: TABLE SYM {
} }
; ;
CF_ADDTO(conf, roa_table)
roa_table_start: ROA TABLE SYM {
this_roa_table = roa_new_table_config($3);
};
roa_table_opts:
/* empty */
| roa_table_opts ROA prefix MAX NUM AS NUM ';' {
roa_add_item_config(this_roa_table, $3.addr, $3.len, $5, $7);
}
;
roa_table:
roa_table_start
| roa_table_start '{' roa_table_opts '}'
;
/* Definition of protocols */ /* Definition of protocols */
CF_ADDTO(conf, proto) CF_ADDTO(conf, proto)
@ -433,7 +455,44 @@ export_or_preexport:
| EXPORT { $$ = 2; } | EXPORT { $$ = 2; }
; ;
CF_CLI(SHOW SYMBOLS, sym_args, [table|filter|function|protocol|template|<symbol>], [[Show all known symbolic names]])
CF_CLI(SHOW XROA, roa_args, [<prefix> | in <prefix> | for <prefix>] [as <num>] [table <t>], [[Show ROA table]])
{ roa_show($3); } ;
roa_args:
/* empty */ {
$$ = cfg_allocz(sizeof(struct roa_show_data));
$$->mode = ROA_SHOW_ALL;
$$->table = roa_table_default;
if (roa_table_default == NULL)
cf_error("No ROA table defined");
}
| roa_args roa_mode prefix {
$$ = $1;
if ($$->mode != ROA_SHOW_ALL) cf_error("Only one prefix expected");
$$->prefix = $3.addr;
$$->pxlen = $3.len;
$$->mode = $2;
}
| roa_args AS NUM {
$$ = $1;
$$->asn = $3;
}
| roa_args TABLE SYM {
$$ = $1;
if ($3->class != SYM_ROA) cf_error("%s is not a ROA table", $3->name);
$$->table = ((struct roa_table_config *)$3->def)->table;
}
;
roa_mode:
{ $$ = ROA_SHOW_PX; }
| IN { $$ = ROA_SHOW_IN; }
| FOR { $$ = ROA_SHOW_FOR; }
;
CF_CLI(SHOW SYMBOLS, sym_args, [table|filter|function|protocol|template|roa|<symbol>], [[Show all known symbolic names]])
{ cmd_show_symbols($3); } ; { cmd_show_symbols($3); } ;
sym_args: sym_args:
@ -445,9 +504,34 @@ sym_args:
| sym_args FILTER { $$ = $1; $$->type = SYM_FILTER; } | sym_args FILTER { $$ = $1; $$->type = SYM_FILTER; }
| sym_args PROTOCOL { $$ = $1; $$->type = SYM_PROTO; } | sym_args PROTOCOL { $$ = $1; $$->type = SYM_PROTO; }
| sym_args TEMPLATE { $$ = $1; $$->type = SYM_TEMPLATE; } | sym_args TEMPLATE { $$ = $1; $$->type = SYM_TEMPLATE; }
| sym_args ROA { $$ = $1; $$->type = SYM_ROA; }
| sym_args SYM { $$ = $1; $$->sym = $2; } | sym_args SYM { $$ = $1; $$->sym = $2; }
; ;
roa_table_arg:
/* empty */ {
if (roa_table_default == NULL)
cf_error("No ROA table defined");
$$ = roa_table_default;
}
| TABLE SYM {
if ($2->class != SYM_ROA)
cf_error("%s is not a ROA table", $2->name);
$$ = ((struct roa_table_config *)$2->def)->table;
}
;
CF_CLI(ADD ROA, prefix MAX NUM AS NUM roa_table_arg, <prefix> max <num> as <num> [table <name>], [[Add ROA record]])
{ roa_add_item($8, $3.addr, $3.len, $5, $7, ROA_SRC_DYNAMIC); cli_msg(0, ""); } ;
CF_CLI(DELETE ROA, prefix MAX NUM AS NUM roa_table_arg, <prefix> max <num> as <num> [table <name>], [[Delete ROA record]])
{ roa_delete_item($8, $3.addr, $3.len, $5, $7, ROA_SRC_DYNAMIC); cli_msg(0, ""); } ;
CF_CLI(FLUSH ROA, roa_table_arg, [table <name>], [[Removes all dynamic ROA records]])
{ roa_flush($3, ROA_SRC_DYNAMIC); cli_msg(0, ""); } ;
CF_CLI_HELP(DUMP, ..., [[Dump debugging information]]) CF_CLI_HELP(DUMP, ..., [[Dump debugging information]])
CF_CLI(DUMP RESOURCES,,, [[Dump all allocated resource]]) CF_CLI(DUMP RESOURCES,,, [[Dump all allocated resource]])
{ rdump(&root_pool); cli_msg(0, ""); } ; { rdump(&root_pool); cli_msg(0, ""); } ;

View file

@ -454,4 +454,84 @@ extern struct protocol *attr_class_to_protocol[EAP_MAX];
#define DEF_PREF_PIPE 70 /* Routes piped from other tables */ #define DEF_PREF_PIPE 70 /* Routes piped from other tables */
#define DEF_PREF_INHERITED 10 /* Routes inherited from other routing daemons */ #define DEF_PREF_INHERITED 10 /* Routes inherited from other routing daemons */
/*
* Route Origin Authorization
*/
struct roa_item {
u32 asn;
byte maxlen;
byte src;
struct roa_item *next;
};
struct roa_node {
struct fib_node n;
struct roa_item *items;
// u32 cached_asn;
};
struct roa_table {
node n; /* Node in roa_table_list */
struct fib fib;
char *name; /* Name of this ROA table */
struct roa_table_config *cf; /* Configuration of this ROA table */
};
struct roa_item_config {
ip_addr prefix;
byte pxlen, maxlen;
u32 asn;
struct roa_item_config *next;
};
struct roa_table_config {
node n; /* Node in config->rpa_tables */
char *name; /* Name of this ROA table */
struct roa_table *table;
struct roa_item_config *roa_items; /* Preconfigured ROA items */
// char *filename;
// int gc_max_ops; /* Maximum number of operations before GC is run */
// int gc_min_time; /* Minimum time between two consecutive GC runs */
};
struct roa_show_data {
struct fib_iterator fit;
struct roa_table *table;
ip_addr prefix;
byte pxlen;
byte mode; /* ROA_SHOW_* values */
u32 asn; /* Filter ASN, 0 -> all */
};
#define ROA_UNKNOWN 0
#define ROA_VALID 1
#define ROA_INVALID 2
#define ROA_SRC_ANY 0
#define ROA_SRC_CONFIG 1
#define ROA_SRC_DYNAMIC 2
#define ROA_SHOW_ALL 0
#define ROA_SHOW_PX 1
#define ROA_SHOW_IN 2
#define ROA_SHOW_FOR 3
extern struct roa_table *roa_table_default;
void roa_add_item(struct roa_table *t, ip_addr prefix, byte pxlen, byte maxlen, u32 asn, byte src);
void roa_delete_item(struct roa_table *t, ip_addr prefix, byte pxlen, byte maxlen, u32 asn, byte src);
void roa_flush(struct roa_table *t, byte src);
byte roa_check(struct roa_table *t, ip_addr prefix, byte pxlen, u32 asn);
struct roa_table_config * roa_new_table_config(struct symbol *s);
void roa_add_item_config(struct roa_table_config *rtc, ip_addr prefix, byte pxlen, byte maxlen, u32 asn);
void roa_init(void);
void roa_preconfig(struct config *c);
void roa_commit(struct config *new, struct config *old);
void roa_show(struct roa_show_data *d);
#endif #endif

440
nest/rt-roa.c Normal file
View file

@ -0,0 +1,440 @@
/*
* BIRD -- Route Origin Authorization
*
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#undef LOCAL_DEBUG
#include "nest/bird.h"
#include "nest/route.h"
#include "nest/cli.h"
#include "lib/lists.h"
#include "lib/resource.h"
#include "lib/event.h"
#include "lib/string.h"
#include "conf/conf.h"
pool *roa_pool;
static slab *roa_slab; /* Slab of struct roa_item */
static list roa_table_list; /* List of struct roa_table */
struct roa_table *roa_table_default; /* The first ROA table in the config */
static inline int
src_match(struct roa_item *it, byte src)
{ return !src || it->src == src; }
/**
* roa_add_item - add a ROA entry
* @t: ROA table
* @prefix: prefix of the ROA entry
* @pxlen: prefix length of the ROA entry
* @maxlen: max length field of the ROA entry
* @asn: AS number field of the ROA entry
* @src: source of the ROA entry (ROA_SRC_*)
*
* The function adds a new ROA entry to the ROA table. If the same ROA
* is already in the table, nothing is added. @src field is used to
* distinguish different sources of ROAs.
*/
void
roa_add_item(struct roa_table *t, ip_addr prefix, byte pxlen, byte maxlen, u32 asn, byte src)
{
struct roa_node *n = fib_get(&t->fib, &prefix, pxlen);
// if ((n->items == NULL) && (n->n.x0 != ROA_INVALID))
// t->cached_items--;
struct roa_item *it;
for (it = n->items; it; it = it->next)
if ((it->maxlen == maxlen) && (it->asn == asn) && src_match(it, src))
return;
it = sl_alloc(roa_slab);
it->asn = asn;
it->maxlen = maxlen;
it->src = src;
it->next = n->items;
n->items = it;
}
/**
* roa_delete_item - delete a ROA entry
* @t: ROA table
* @prefix: prefix of the ROA entry
* @pxlen: prefix length of the ROA entry
* @maxlen: max length field of the ROA entry
* @asn: AS number field of the ROA entry
* @src: source of the ROA entry (ROA_SRC_*)
*
* The function removes a specified ROA entry from the ROA table and
* frees it. If @src field is not ROA_SRC_ANY, only entries from
* that source are considered.
*/
void
roa_delete_item(struct roa_table *t, ip_addr prefix, byte pxlen, byte maxlen, u32 asn, byte src)
{
struct roa_node *n = fib_find(&t->fib, &prefix, pxlen);
if (!n)
return;
struct roa_item *it, **itp;
for (itp = &n->items; it = *itp; itp = &it->next)
if ((it->maxlen == maxlen) && (it->asn == asn) && src_match(it, src))
break;
if (!it)
return;
*itp = it->next;
sl_free(roa_slab, it);
// if ((n->items == NULL) && (n->n.x0 != ROA_INVALID))
// t->cached_items++;
}
/**
* roa_flush - flush a ROA table
* @t: ROA table
* @src: source of ROA entries (ROA_SRC_*)
*
* The function removes and frees ROA entries from the ROA table. If
* @src is ROA_SRC_ANY, all entries in the table are removed,
* otherwise only all entries from that source are removed.
*/
void
roa_flush(struct roa_table *t, byte src)
{
struct roa_item *it, **itp;
struct roa_node *n;
FIB_WALK(&t->fib, fn)
{
n = (struct roa_node *) fn;
itp = &n->items;
while (it = *itp)
if (src_match(it, src))
{
*itp = it->next;
sl_free(roa_slab, it);
}
else
itp = &it->next;
}
FIB_WALK_END;
// TODO add cleanup of roa_nodes
}
/*
byte
roa_check(struct roa_table *t, ip_addr prefix, byte pxlen, u32 asn)
{
struct roa_node *n = fib_find(&t->fib, &prefix, pxlen);
if (n && n->n.x0 == ROA_UNKNOWN)
return ROA_UNKNOWN;
if (n && n->n.x0 == ROA_VALID && asn == n->cached_asn)
return ROA_VALID;
byte rv = roa_match(t, n, prefix, pxlen, asn);
if (rv != ROA_INVALID)
{
if (!n)
{
if (t->cached_items >= t->cached_items_max)
n = fib_get(&t->fib, &prefix, pxlen);
t->cached_items++;
}
n->cached_asn = asn;
n->n.x0 = rv;
}
return rv;
}
*/
/**
* roa_check - check validity of route origination in a ROA table
* @t: ROA table
* @prefix: network prefix to check
* @pxlen: length of network prefix
* @asn: AS number of network prefix
*
* Implements RFC 6483 route validation for the given network
* prefix. The procedure is to find all candidate ROAs - ROAs whose
* prefixes cover the give network prefix. If there is no candidate
* ROA, return ROA_UNKNOWN. If there is a candidate ROA with matching
* ASN and maxlen field greater than or equal to the given prefix
* length, return ROA_VALID. Otherwise return ROA_INVALID. If caller
* cannot determine origin AS, 0 could be used (in that case ROA_VALID
* cannot happen).
*/
byte
roa_check(struct roa_table *t, ip_addr prefix, byte pxlen, u32 asn)
{
struct roa_node *n;
ip_addr px;
byte anything = 0;
int len;
for (len = pxlen; len >= 0; len--)
{
px = ipa_and(prefix, ipa_mkmask(len));
n = fib_find(&t->fib, &px, len);
if (!n)
continue;
struct roa_item *it;
for (it = n->items; it; it = it->next)
{
anything = 1;
if ((it->maxlen >= pxlen) && (it->asn == asn) && asn)
return ROA_VALID;
}
}
return anything ? ROA_INVALID : ROA_UNKNOWN;
}
static void
roa_node_init(struct fib_node *fn)
{
struct roa_node *n = (struct roa_node *) fn;
n->items = NULL;
}
static inline void
roa_populate(struct roa_table *t)
{
struct roa_item_config *ric;
for (ric = t->cf->roa_items; ric; ric = ric->next)
roa_add_item(t, ric->prefix, ric->pxlen, ric->maxlen, ric->asn, ROA_SRC_CONFIG);
}
static void
roa_new_table(struct roa_table_config *cf)
{
struct roa_table *t;
t = mb_allocz(roa_pool, sizeof(struct roa_table));
fib_init(&t->fib, roa_pool, sizeof(struct roa_node), 0, roa_node_init);
t->name = cf->name;
t->cf = cf;
cf->table = t;
add_tail(&roa_table_list, &t->n);
roa_populate(t);
}
struct roa_table_config *
roa_new_table_config(struct symbol *s)
{
struct roa_table_config *rtc = cfg_allocz(sizeof(struct roa_table_config));
cf_define_symbol(s, SYM_ROA, rtc);
rtc->name = s->name;
add_tail(&new_config->roa_tables, &rtc->n);
return rtc;
}
/**
* roa_add_item_config - add a static ROA entry to a ROA table configuration
*
* Arguments are self-explanatory. The first is the ROA table config, rest
* are specifying the ROA entry.
*/
void
roa_add_item_config(struct roa_table_config *rtc, ip_addr prefix, byte pxlen, byte maxlen, u32 asn)
{
struct roa_item_config *ric = cfg_allocz(sizeof(struct roa_item_config));
ric->prefix = prefix;
ric->pxlen = pxlen;
ric->maxlen = maxlen;
ric->asn = asn;
ric->next = rtc->roa_items;
rtc->roa_items = ric;
}
/**
* roa_init - initialize ROA tables
*
* This function is called during BIRD startup. It initializes
* the ROA table module.
*/
void
roa_init(void)
{
roa_pool = rp_new(&root_pool, "ROA tables");
roa_slab = sl_new(roa_pool, sizeof(struct roa_item));
init_list(&roa_table_list);
}
void
roa_preconfig(struct config *c)
{
init_list(&c->roa_tables);
}
/**
* roa_commit - commit new ROA table configuration
* @new: new configuration
* @old: original configuration or %NULL if it's boot time config
*
* Scan differences between @old and @new configuration and modify the
* ROA tables according to these changes. If @new defines a previously
* unknown table, create it, if it omits a table existing in @old,
* delete it (there are no references, only indirect through struct
* roa_table_config). If it exists in both configurations, update the
* configured ROA entries.
*/
void
roa_commit(struct config *new, struct config *old)
{
struct roa_table_config *cf;
struct roa_table *t;
if (old)
WALK_LIST(t, roa_table_list)
{
struct symbol *sym = cf_find_symbol(t->name);
if (sym && sym->class == SYM_ROA)
{
/* Found old table in new config */
cf = sym->def;
cf->table = t;
t->name = cf->name;
t->cf = cf;
/* Reconfigure it */
roa_flush(t, ROA_SRC_CONFIG);
roa_populate(t);
}
else
{
t->cf->table = NULL;
/* Free it now */
roa_flush(t, ROA_SRC_ANY);
rem_node(&t->n);
fib_free(&t->fib);
mb_free(t);
}
}
/* Add new tables */
WALK_LIST(cf, new->roa_tables)
if (! cf->table)
roa_new_table(cf);
roa_table_default = EMPTY_LIST(new->roa_tables) ? NULL :
((struct roa_table_config *) HEAD(new->roa_tables))->table;
}
static void
roa_show_node(struct cli *c, struct roa_node *rn, int len, u32 asn)
{
struct roa_item *ri;
for (ri = rn->items; ri; ri = ri->next)
if ((ri->maxlen >= len) && (!asn || (ri->asn == asn)))
cli_printf(c, -1111, "%I/%d max %d as %u", rn->n.prefix, rn->n.pxlen, ri->maxlen, ri->asn);
}
static void
roa_show_cont(struct cli *c)
{
struct roa_show_data *d = c->rover;
struct fib *fib = &d->table->fib;
struct fib_iterator *it = &d->fit;
struct roa_node *rn;
unsigned max = 32;
FIB_ITERATE_START(fib, it, f)
{
rn = (struct roa_node *) f;
if (!max--)
{
FIB_ITERATE_PUT(it, f);
return;
}
if ((d->mode == ROA_SHOW_ALL) ||
net_in_net(rn->n.prefix, rn->n.pxlen, d->prefix, d->pxlen))
roa_show_node(c, rn, 0, d->asn);
}
FIB_ITERATE_END(f);
cli_printf(c, 0, "");
c->cont = c->cleanup = NULL;
}
static void
roa_show_cleanup(struct cli *c)
{
struct roa_show_data *d = c->rover;
/* Unlink the iterator */
fit_get(&d->table->fib, &d->fit);
}
void
roa_show(struct roa_show_data *d)
{
struct roa_node *rn;
ip_addr px;
int len;
switch (d->mode)
{
case ROA_SHOW_ALL:
case ROA_SHOW_IN:
FIB_ITERATE_INIT(&d->fit, &d->table->fib);
this_cli->cont = roa_show_cont;
this_cli->cleanup = roa_show_cleanup;
this_cli->rover = d;
break;
case ROA_SHOW_PX:
rn = fib_find(&d->table->fib, &d->prefix, d->pxlen);
if (rn)
{
roa_show_node(this_cli, rn, 0, d->asn);
cli_msg(0, "");
}
else
cli_msg(-8001, "Network not in table");
break;
case ROA_SHOW_FOR:
for (len = d->pxlen; len >= 0; len--)
{
px = ipa_and(d->prefix, ipa_mkmask(len));
rn = fib_find(&d->table->fib, &px, len);
if (!rn)
continue;
roa_show_node(this_cli, rn, 0, d->asn);
}
cli_msg(0, "");
break;
}
}

View file

@ -643,6 +643,7 @@ main(int argc, char **argv)
io_init(); io_init();
rt_init(); rt_init();
if_init(); if_init();
roa_init();
uid_t use_uid = get_uid(use_user); uid_t use_uid = get_uid(use_user);
gid_t use_gid = get_gid(use_group); gid_t use_gid = get_gid(use_group);