summaryrefslogtreecommitdiff
path: root/nest
diff options
context:
space:
mode:
Diffstat (limited to 'nest')
-rw-r--r--nest/Makefile2
-rw-r--r--nest/a-set.c64
-rw-r--r--nest/attrs.h2
-rw-r--r--nest/cmds.c15
-rw-r--r--nest/cmds.h7
-rw-r--r--nest/config.Y118
-rw-r--r--nest/proto.c117
-rw-r--r--nest/protocol.h1
-rw-r--r--nest/route.h85
-rw-r--r--nest/rt-roa.c440
-rw-r--r--nest/rt-table.c125
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