diff options
author | Ondrej Zajicek <santiago@crfreenet.org> | 2012-03-18 17:32:30 +0100 |
---|---|---|
committer | Ondrej Zajicek <santiago@crfreenet.org> | 2012-03-18 17:32:30 +0100 |
commit | af582c4811175d9a27ed5d08a4f6d5eaa69ecec7 (patch) | |
tree | 3b2793cb9db3c67efddfb379e6c8adc16b143604 /nest | |
parent | fd087589f80a435a42cedb87b917c71363b11860 (diff) |
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.
Diffstat (limited to 'nest')
-rw-r--r-- | nest/Makefile | 2 | ||||
-rw-r--r-- | nest/cmds.c | 2 | ||||
-rw-r--r-- | nest/config.Y | 90 | ||||
-rw-r--r-- | nest/route.h | 80 | ||||
-rw-r--r-- | nest/rt-roa.c | 440 |
5 files changed, 610 insertions, 4 deletions
diff --git a/nest/Makefile b/nest/Makefile index 478a82b7..e6928668 100644 --- a/nest/Makefile +++ b/nest/Makefile @@ -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 root-rel=../ dir-name=nest diff --git a/nest/cmds.c b/nest/cmds.c index 62d7c9b4..2a803930 100644 --- a/nest/cmds.c +++ b/nest/cmds.c @@ -72,6 +72,7 @@ print_size(char *dsc, size_t val) extern pool *rt_table_pool; extern pool *rta_pool; +extern pool *roa_pool; extern pool *proto_pool; void @@ -80,6 +81,7 @@ cmd_show_memory(void) cli_msg(-1018, "BIRD memory usage"); print_size("Routing tables:", rmemsize(rt_table_pool)); print_size("Route attributes:", rmemsize(rta_pool)); + print_size("ROA tables:", rmemsize(roa_pool)); print_size("Protocols:", rmemsize(proto_pool)); print_size("Total:", rmemsize(&root_pool)); cli_msg(0, ""); diff --git a/nest/config.Y b/nest/config.Y index f6795df4..24ef58d0 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -19,6 +19,7 @@ CF_DEFINES static struct proto_config *this_proto; static struct iface_patt *this_ipatt; static struct iface_patt_node *this_ipn; +static struct roa_table_config *this_roa_table; static list *this_p_list; static struct password_item *this_p_item; static int password_id; @@ -44,7 +45,7 @@ CF_DECLS 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(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(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_RTC, RTC_, UNICAST, BROADCAST, MULTICAST, ANYCAST) 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 <f> imexport %type <r> rtable %type <s> optsym %type <ra> r_args +%type <ro> roa_args +%type <rot> roa_table_arg %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 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 */ CF_ADDTO(conf, proto) @@ -433,7 +455,44 @@ export_or_preexport: | 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); } ; sym_args: @@ -445,9 +504,34 @@ sym_args: | sym_args FILTER { $$ = $1; $$->type = SYM_FILTER; } | sym_args PROTOCOL { $$ = $1; $$->type = SYM_PROTO; } | sym_args TEMPLATE { $$ = $1; $$->type = SYM_TEMPLATE; } + | sym_args ROA { $$ = $1; $$->type = SYM_ROA; } | 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(DUMP RESOURCES,,, [[Dump all allocated resource]]) { rdump(&root_pool); cli_msg(0, ""); } ; diff --git a/nest/route.h b/nest/route.h index e6712c64..ea948838 100644 --- a/nest/route.h +++ b/nest/route.h @@ -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_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 diff --git a/nest/rt-roa.c b/nest/rt-roa.c new file mode 100644 index 00000000..f94842c4 --- /dev/null +++ b/nest/rt-roa.c @@ -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; + } +} |