diff options
Diffstat (limited to 'nest')
-rw-r--r-- | nest/Makefile | 2 | ||||
-rw-r--r-- | nest/a-set.c | 64 | ||||
-rw-r--r-- | nest/attrs.h | 2 | ||||
-rw-r--r-- | nest/cmds.c | 15 | ||||
-rw-r--r-- | nest/cmds.h | 7 | ||||
-rw-r--r-- | nest/config.Y | 118 | ||||
-rw-r--r-- | nest/proto.c | 117 | ||||
-rw-r--r-- | nest/protocol.h | 1 | ||||
-rw-r--r-- | nest/route.h | 85 | ||||
-rw-r--r-- | nest/rt-roa.c | 440 | ||||
-rw-r--r-- | nest/rt-table.c | 125 |
11 files changed, 896 insertions, 80 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/a-set.c b/nest/a-set.c index 020d0978..42ef9b06 100644 --- a/nest/a-set.c +++ b/nest/a-set.c @@ -264,3 +264,67 @@ ec_set_del(struct linpool *pool, struct adata *list, u64 val) return res; } + + +struct adata * +int_set_union(struct linpool *pool, struct adata *l1, struct adata *l2) +{ + if (!l1) + return l2; + if (!l2) + return l1; + + struct adata *res; + int len = int_set_get_size(l2); + u32 *l = int_set_get_data(l2); + u32 tmp[len]; + u32 *k = tmp; + int i; + + for (i = 0; i < len; i++) + if (!int_set_contains(l1, l[i])) + *k++ = l[i]; + + if (k == tmp) + return l1; + + len = (k - tmp) * 4; + res = lp_alloc(pool, sizeof(struct adata) + l1->length + len); + res->length = l1->length + len; + memcpy(res->data, l1->data, l1->length); + memcpy(res->data + l1->length, tmp, len); + return res; +} + +struct adata * +ec_set_union(struct linpool *pool, struct adata *l1, struct adata *l2) +{ + if (!l1) + return l2; + if (!l2) + return l1; + + struct adata *res; + int len = int_set_get_size(l2); + u32 *l = int_set_get_data(l2); + u32 tmp[len]; + u32 *k = tmp; + int i; + + for (i = 0; i < len; i += 2) + if (!ec_set_contains(l1, ec_get(l, i))) + { + *k++ = l[i]; + *k++ = l[i+1]; + } + + if (k == tmp) + return l1; + + len = (k - tmp) * 4; + res = lp_alloc(pool, sizeof(struct adata) + l1->length + len); + res->length = l1->length + len; + memcpy(res->data, l1->data, l1->length); + memcpy(res->data + l1->length, tmp, len); + return res; +} diff --git a/nest/attrs.h b/nest/attrs.h index 85e4e59a..42f81a10 100644 --- a/nest/attrs.h +++ b/nest/attrs.h @@ -96,6 +96,8 @@ struct adata *int_set_add(struct linpool *pool, struct adata *list, u32 val); struct adata *ec_set_add(struct linpool *pool, struct adata *list, u64 val); struct adata *int_set_del(struct linpool *pool, struct adata *list, u32 val); struct adata *ec_set_del(struct linpool *pool, struct adata *list, u64 val); +struct adata *int_set_union(struct linpool *pool, struct adata *l1, struct adata *l2); +struct adata *ec_set_union(struct linpool *pool, struct adata *l1, struct adata *l2); #endif diff --git a/nest/cmds.c b/nest/cmds.c index 8ac32096..2a803930 100644 --- a/nest/cmds.c +++ b/nest/cmds.c @@ -7,6 +7,7 @@ */ #include "nest/bird.h" +#include "nest/route.h" #include "nest/cli.h" #include "conf/conf.h" #include "nest/cmds.h" @@ -35,16 +36,22 @@ cmd_show_status(void) } void -cmd_show_symbols(struct symbol *sym) +cmd_show_symbols(struct sym_show_data *sd) { int pos = 0; + struct symbol *sym = sd->sym; if (sym) - cli_msg(1010, "%s\t%s", sym->name, cf_symbol_class_name(sym)); + cli_msg(1010, "%-8s\t%s", sym->name, cf_symbol_class_name(sym)); else { while (sym = cf_walk_symbols(config, sym, &pos)) - cli_msg(-1010, "%s\t%s", sym->name, cf_symbol_class_name(sym)); + { + if (sd->type && (sym->class != sd->type)) + continue; + + cli_msg(-1010, "%-8s\t%s", sym->name, cf_symbol_class_name(sym)); + } cli_msg(0, ""); } } @@ -65,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 @@ -73,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/cmds.h b/nest/cmds.h index 3b86a924..8b0bff7e 100644 --- a/nest/cmds.h +++ b/nest/cmds.h @@ -6,6 +6,11 @@ * Can be freely distributed and used under the terms of the GNU GPL. */ +struct sym_show_data { + int type; /* Symbols type to show */ + struct symbol *sym; +}; + void cmd_show_status(void); -void cmd_show_symbols(struct symbol *sym); +void cmd_show_symbols(struct sym_show_data *sym); void cmd_show_memory(void); diff --git a/nest/config.Y b/nest/config.Y index 3fcfa528..f889828a 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, MAX, FLUSH) CF_KEYWORDS(LISTEN, BGP, V6ONLY, DUAL, ADDRESS, PORT, PASSWORDS, DESCRIPTION) CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC) @@ -53,13 +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 <i> proto_start echo_mask echo_size debug_mask debug_list debug_flag mrtdump_mask mrtdump_list mrtdump_flag export_or_preexport +%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 roa_mode %type <ps> proto_patt proto_patt2 CF_GRAMMAR @@ -112,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) @@ -353,6 +376,7 @@ CF_CLI(SHOW INTERFACES,,, [[Show network interfaces]]) CF_CLI(SHOW INTERFACES SUMMARY,,, [[Show summary of network interfaces]]) { if_show_summary(); } ; +CF_CLI_HELP(SHOW ROUTE, ..., [[Show routing table]]) CF_CLI(SHOW ROUTE, r_args, [[[<prefix>|for <prefix>|for <ip>] [table <t>] [filter <f>|where <cond>] [all] [primary] [(export|preexport) <p>] [protocol <p>] [stats|count]]], [[Show routing table]]) { rt_show($3); } ; @@ -432,9 +456,97 @@ export_or_preexport: | EXPORT { $$ = 2; } ; -CF_CLI(SHOW SYMBOLS, optsym, [<symbol>], [[Show all known symbolic names]]) + +CF_CLI_HELP(SHOW ROA, ..., [[Show ROA table]]) +CF_CLI(SHOW ROA, 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_HELP(SHOW SYMBOLS, ..., [[Show all known symbolic names]]) +CF_CLI(SHOW SYMBOLS, sym_args, [table|filter|function|protocol|template|roa|<symbol>], [[Show all known symbolic names]]) { cmd_show_symbols($3); } ; +sym_args: + /* empty */ { + $$ = cfg_allocz(sizeof(struct sym_show_data)); + } + | sym_args TABLE { $$ = $1; $$->type = SYM_TABLE; } + | sym_args FUNCTION { $$ = $1; $$->type = SYM_FUNCTION; } + | 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_HELP(ADD, roa ..., [[Add ROA record]]) +CF_CLI(ADD ROA, prefix MAX NUM AS NUM roa_table_arg, <prefix> max <num> as <num> [table <name>], [[Add ROA record]]) +{ + if (! cli_access_restricted()) + { roa_add_item($8, $3.addr, $3.len, $5, $7, ROA_SRC_DYNAMIC); cli_msg(0, ""); } +}; + +CF_CLI_HELP(DELETE, roa ..., [[Delete ROA record]]) +CF_CLI(DELETE ROA, prefix MAX NUM AS NUM roa_table_arg, <prefix> max <num> as <num> [table <name>], [[Delete ROA record]]) +{ + if (! cli_access_restricted()) + { roa_delete_item($8, $3.addr, $3.len, $5, $7, ROA_SRC_DYNAMIC); cli_msg(0, ""); } +}; + +CF_CLI_HELP(FLUSH, roa [table <name>], [[Removes all dynamic ROA records]]) +CF_CLI(FLUSH ROA, roa_table_arg, [table <name>], [[Removes all dynamic ROA records]]) +{ + if (! cli_access_restricted()) + { 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/proto.c b/nest/proto.c index 9a7f123e..802d5238 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -38,7 +38,7 @@ static event *proto_flush_event; static char *p_states[] = { "DOWN", "START", "UP", "STOP" }; static char *c_states[] = { "HUNGRY", "FEEDING", "HAPPY", "FLUSHING" }; -static void proto_flush_all(void *); +static void proto_flush_loop(void *); static void proto_rethink_goal(struct proto *p); static char *proto_state_name(struct proto *p); @@ -703,7 +703,7 @@ protos_build(void) #endif proto_pool = rp_new(&root_pool, "Protocols"); proto_flush_event = ev_new(proto_pool); - proto_flush_event->hook = proto_flush_all; + proto_flush_event->hook = proto_flush_loop; } static void @@ -763,20 +763,6 @@ proto_feed_initial(void *P) } static void -proto_schedule_flush(struct proto *p) -{ - /* Need to abort feeding */ - if (p->core_state == FS_FEEDING) - rt_feed_baby_abort(p); - - DBG("%s: Scheduling flush\n", p->name); - p->core_state = FS_FLUSHING; - proto_relink(p); - proto_unlink_ahooks(p); - ev_schedule(proto_flush_event); -} - -static void proto_schedule_feed(struct proto *p, int initial) { DBG("%s: Scheduling meal\n", p->name); @@ -797,6 +783,84 @@ proto_schedule_feed(struct proto *p, int initial) ev_schedule(p->attn); } +/* + * Flushing loop is responsible for flushing routes and protocols + * after they went down. It runs in proto_flush_event. At the start of + * one round, protocols waiting to flush are marked in + * proto_schedule_flush_loop(). At the end of the round (when routing + * table flush is complete), marked protocols are flushed and a next + * round may start. + */ + +static int flush_loop_state; /* 1 -> running */ + +static void +proto_schedule_flush_loop(void) +{ + struct proto *p; + + if (flush_loop_state) + return; + flush_loop_state = 1; + + rt_schedule_prune_all(); + WALK_LIST(p, flush_proto_list) + p->flushing = 1; + + ev_schedule(proto_flush_event); +} + +static void +proto_flush_loop(void *unused UNUSED) +{ + struct proto *p; + + if (! rt_prune_loop()) + { + /* Rtable pruning is not finished */ + ev_schedule(proto_flush_event); + return; + } + + again: + WALK_LIST(p, flush_proto_list) + if (p->flushing) + { + /* This will flush interfaces in the same manner + like rt_prune_all() flushes routes */ + if (p->proto == &proto_unix_iface) + if_flush_ifaces(p); + + DBG("Flushing protocol %s\n", p->name); + p->flushing = 0; + p->core_state = FS_HUNGRY; + proto_relink(p); + if (p->proto_state == PS_DOWN) + proto_fell_down(p); + goto again; + } + + /* This round finished, perhaps there will be another one */ + flush_loop_state = 0; + if (!EMPTY_LIST(flush_proto_list)) + proto_schedule_flush_loop(); +} + +static void +proto_schedule_flush(struct proto *p) +{ + /* Need to abort feeding */ + if (p->core_state == FS_FEEDING) + rt_feed_baby_abort(p); + + DBG("%s: Scheduling flush\n", p->name); + p->core_state = FS_FLUSHING; + proto_relink(p); + proto_unlink_ahooks(p); + proto_schedule_flush_loop(); +} + + /** * proto_request_feeding - request feeding routes to the protocol * @p: given protocol @@ -886,27 +950,6 @@ proto_notify_state(struct proto *p, unsigned ps) } } -static void -proto_flush_all(void *unused UNUSED) -{ - struct proto *p; - - rt_prune_all(); - while ((p = HEAD(flush_proto_list))->n.next) - { - /* This will flush interfaces in the same manner - like rt_prune_all() flushes routes */ - if (p->proto == &proto_unix_iface) - if_flush_ifaces(p); - - DBG("Flushing protocol %s\n", p->name); - p->core_state = FS_HUNGRY; - proto_relink(p); - if (p->proto_state == PS_DOWN) - proto_fell_down(p); - } -} - /* * CLI Commands */ diff --git a/nest/protocol.h b/nest/protocol.h index 187b8712..983ce75a 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -145,6 +145,7 @@ struct proto { unsigned core_goal; /* State we want to reach (see below) */ unsigned reconfiguring; /* We're shutting down due to reconfiguration */ unsigned refeeding; /* We are refeeding (valid only if core_state == FS_FEEDING) */ + unsigned flushing; /* Protocol is flushed in current flush loop round */ u32 hash_key; /* Random key used for hashing of neighbors */ bird_clock_t last_state_change; /* Time of last state transition */ char *last_state_name_announced; /* Last state name we've announced to the user */ diff --git a/nest/route.h b/nest/route.h index fcb4f283..34536609 100644 --- a/nest/route.h +++ b/nest/route.h @@ -140,8 +140,10 @@ typedef struct rtable { int gc_counter; /* Number of operations since last GC */ bird_clock_t gc_time; /* Time of last GC */ byte gc_scheduled; /* GC is scheduled */ + byte prune_state; /* Table prune state, 1 -> prune is running */ byte hcu_scheduled; /* Hostcache update is scheduled */ byte nhu_state; /* Next Hop Update state */ + struct fib_iterator prune_fit; /* Rtable prune FIB iterator */ struct fib_iterator nhu_fit; /* Next Hop Update FIB iterator */ } rtable; @@ -246,7 +248,8 @@ void rt_dump(rtable *); void rt_dump_all(void); int rt_feed_baby(struct proto *p); void rt_feed_baby_abort(struct proto *p); -void rt_prune_all(void); +void rt_schedule_prune_all(void); +int rt_prune_loop(void); struct rtable_config *rt_new_table(struct symbol *s); struct rt_show_data { @@ -456,4 +459,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..aa453f16 --- /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, -1019, "%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; + } +} diff --git a/nest/rt-table.c b/nest/rt-table.c index 3807ef8d..310c1afd 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -55,7 +55,6 @@ static void rt_free_hostcache(rtable *tab); static void rt_notify_hostcache(rtable *tab, net *net); static void rt_update_hostcache(rtable *tab); static void rt_next_hop_update(rtable *tab); -static void rt_prune(rtable *tab); static inline void rt_schedule_gc(rtable *tab); @@ -817,6 +816,38 @@ rt_schedule_nhu(rtable *tab) } static void +rt_prune_nets(rtable *tab) +{ + struct fib_iterator fit; + int ncnt = 0, ndel = 0; + +#ifdef DEBUGGING + fib_check(&tab->fib); +#endif + + FIB_ITERATE_INIT(&fit, &tab->fib); +again: + FIB_ITERATE_START(&tab->fib, &fit, f) + { + net *n = (net *) f; + ncnt++; + if (!n->routes) /* Orphaned FIB entry */ + { + FIB_ITERATE_PUT(&fit, f); + fib_delete(&tab->fib, f); + ndel++; + goto again; + } + } + FIB_ITERATE_END(f); + DBG("Pruned %d of %d networks\n", ndel, ncnt); + + tab->gc_counter = 0; + tab->gc_time = now; + tab->gc_scheduled = 0; +} + +static void rt_event(void *ptr) { rtable *tab = ptr; @@ -828,7 +859,7 @@ rt_event(void *ptr) rt_next_hop_update(tab); if (tab->gc_scheduled) - rt_prune(tab); + rt_prune_nets(tab); } void @@ -864,70 +895,96 @@ rt_init(void) init_list(&routing_tables); } -/** - * rt_prune - prune a routing table - * @tab: routing table to be pruned - * - * This function is called whenever a protocol shuts down. It scans - * the routing table and removes all routes belonging to inactive - * protocols and also stale network entries. - */ -static void -rt_prune(rtable *tab) + +/* Called from proto_schedule_flush_loop() only, + ensuring that all prune states are zero */ +void +rt_schedule_prune_all(void) { - struct fib_iterator fit; - int rcnt = 0, rdel = 0, ncnt = 0, ndel = 0; + rtable *t; + + WALK_LIST(t, routing_tables) + t->prune_state = 1; +} + +static inline int +rt_prune_step(rtable *tab, int *max_feed) +{ + struct fib_iterator *fit = &tab->prune_fit; DBG("Pruning route table %s\n", tab->name); #ifdef DEBUGGING fib_check(&tab->fib); #endif - FIB_ITERATE_INIT(&fit, &tab->fib); + + if (tab->prune_state == 0) + return 1; + + if (tab->prune_state == 1) + { + FIB_ITERATE_INIT(fit, &tab->fib); + tab->prune_state = 2; + } + again: - FIB_ITERATE_START(&tab->fib, &fit, f) + FIB_ITERATE_START(&tab->fib, fit, fn) { - net *n = (net *) f; + net *n = (net *) fn; rte *e; - ncnt++; + rescan: - for (e=n->routes; e; e=e->next, rcnt++) + for (e=n->routes; e; e=e->next) if (e->sender->proto->core_state != FS_HAPPY && e->sender->proto->core_state != FS_FEEDING) { + if (*max_feed <= 0) + { + FIB_ITERATE_PUT(fit, fn); + return 0; + } + rte_discard(tab, e); - rdel++; + (*max_feed)--; + goto rescan; } - if (!n->routes) /* Orphaned FIB entry? */ + if (!n->routes) /* Orphaned FIB entry */ { - FIB_ITERATE_PUT(&fit, f); - fib_delete(&tab->fib, f); - ndel++; + FIB_ITERATE_PUT(fit, fn); + fib_delete(&tab->fib, fn); goto again; } } - FIB_ITERATE_END(f); - DBG("Pruned %d of %d routes and %d of %d networks\n", rdel, rcnt, ndel, ncnt); + FIB_ITERATE_END(fn); + #ifdef DEBUGGING fib_check(&tab->fib); #endif - tab->gc_counter = 0; - tab->gc_time = now; - tab->gc_scheduled = 0; + + tab->prune_state = 0; + return 1; } /** - * rt_prune_all - prune all routing tables + * rt_prune_loop - prune routing tables + * @tab: routing table to be pruned * - * This function calls rt_prune() for all known routing tables. + * The prune loop scans routing tables and removes routes belonging to + * inactive protocols and also stale network entries. Returns 1 when + * all such routes are pruned. It is a part of the protocol flushing + * loop. */ -void -rt_prune_all(void) +int +rt_prune_loop(void) { rtable *t; + int max_feed = 512; WALK_LIST(t, routing_tables) - rt_prune(t); + if (! rt_prune_step(t, &max_feed)) + return 0; + + return 1; } void |