diff options
Diffstat (limited to 'nest/rt-roa.c')
-rw-r--r-- | nest/rt-roa.c | 440 |
1 files changed, 440 insertions, 0 deletions
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; + } +} |