summaryrefslogtreecommitdiff
path: root/nest
diff options
context:
space:
mode:
Diffstat (limited to 'nest')
-rw-r--r--nest/a-path.c90
-rw-r--r--nest/attrs.h10
-rw-r--r--nest/bfd.h51
-rw-r--r--nest/cli.c20
-rw-r--r--nest/cli.h2
-rw-r--r--nest/cmds.c24
-rw-r--r--nest/cmds.h3
-rw-r--r--nest/config.Y47
-rw-r--r--nest/iface.c70
-rw-r--r--nest/iface.h4
-rw-r--r--nest/neighbor.c20
-rw-r--r--nest/proto.c101
-rw-r--r--nest/protocol.h28
-rw-r--r--nest/route.h24
-rw-r--r--nest/rt-attr.c5
-rw-r--r--nest/rt-dev.c3
-rw-r--r--nest/rt-table.c326
17 files changed, 672 insertions, 156 deletions
diff --git a/nest/a-path.c b/nest/a-path.c
index 63ac402e..dc36e653 100644
--- a/nest/a-path.c
+++ b/nest/a-path.c
@@ -244,10 +244,11 @@ as_path_get_first(struct adata *path, u32 *last_as)
}
int
-as_path_is_member(struct adata *path, u32 as)
+as_path_contains(struct adata *path, u32 as, int min)
{
u8 *p = path->data;
u8 *q = p+path->length;
+ int num = 0;
int i, n;
while (p<q)
@@ -257,13 +258,100 @@ as_path_is_member(struct adata *path, u32 as)
for(i=0; i<n; i++)
{
if (get_as(p) == as)
+ if (++num == min)
+ return 1;
+ p += BS;
+ }
+ }
+ return 0;
+}
+
+int
+as_path_match_set(struct adata *path, struct f_tree *set)
+{
+ u8 *p = path->data;
+ u8 *q = p+path->length;
+ int i, n;
+
+ while (p<q)
+ {
+ n = p[1];
+ p += 2;
+ for (i=0; i<n; i++)
+ {
+ struct f_val v = {T_INT, .val.i = get_as(p)};
+ if (find_tree(set, v))
return 1;
p += BS;
}
}
+
return 0;
}
+struct adata *
+as_path_filter(struct linpool *pool, struct adata *path, struct f_tree *set, u32 key, int pos)
+{
+ if (!path)
+ return NULL;
+
+ int len = path->length;
+ u8 *p = path->data;
+ u8 *q = path->data + len;
+ u8 *d, *d2;
+ int i, bt, sn, dn;
+ u8 buf[len];
+
+ d = buf;
+ while (p<q)
+ {
+ /* Read block header (type and length) */
+ bt = p[0];
+ sn = p[1];
+ dn = 0;
+ p += 2;
+ d2 = d + 2;
+
+ for (i = 0; i < sn; i++)
+ {
+ u32 as = get_as(p);
+ int match;
+
+ if (set)
+ match = !!find_tree(set, (struct f_val){T_INT, .val.i = as});
+ else
+ match = (as == key);
+
+ if (match == pos)
+ {
+ put_as(d2, as);
+ d2 += BS;
+ dn++;
+ }
+
+ p += BS;
+ }
+
+ if (dn > 0)
+ {
+ /* Nonempty block, set block header and advance */
+ d[0] = bt;
+ d[1] = dn;
+ d = d2;
+ }
+ }
+
+ int nl = d - buf;
+ if (nl == path->length)
+ return path;
+
+ struct adata *res = lp_alloc(pool, sizeof(struct adata) + nl);
+ res->length = nl;
+ memcpy(res->data, buf, nl);
+
+ return res;
+}
+
struct pm_pos
{
diff --git a/nest/attrs.h b/nest/attrs.h
index 42f81a10..b6e067cb 100644
--- a/nest/attrs.h
+++ b/nest/attrs.h
@@ -25,6 +25,8 @@
* to 16bit slot (like in 16bit AS_PATH). See RFC 4893 for details
*/
+struct f_tree;
+
struct adata *as_path_prepend(struct linpool *pool, struct adata *olda, u32 as);
int as_path_convert_to_old(struct adata *path, byte *dst, int *new_used);
int as_path_convert_to_new(struct adata *path, byte *dst, int req_as);
@@ -33,7 +35,10 @@ int as_path_getlen(struct adata *path);
int as_path_getlen_int(struct adata *path, int bs);
int as_path_get_first(struct adata *path, u32 *orig_as);
int as_path_get_last(struct adata *path, u32 *last_as);
-int as_path_is_member(struct adata *path, u32 as);
+int as_path_contains(struct adata *path, u32 as, int min);
+int as_path_match_set(struct adata *path, struct f_tree *set);
+struct adata *as_path_filter(struct linpool *pool, struct adata *path, struct f_tree *set, u32 key, int pos);
+
#define PM_ASN 0
#define PM_QUESTION 1
@@ -64,6 +69,9 @@ int as_path_match(struct adata *path, struct f_path_mask *mask);
static inline int int_set_get_size(struct adata *list)
{ return list->length / 4; }
+static inline int ec_set_get_size(struct adata *list)
+{ return list->length / 8; }
+
static inline u32 *int_set_get_data(struct adata *list)
{ return (u32 *) list->data; }
diff --git a/nest/bfd.h b/nest/bfd.h
new file mode 100644
index 00000000..79c3c921
--- /dev/null
+++ b/nest/bfd.h
@@ -0,0 +1,51 @@
+/*
+ * BIRD -- Bidirectional Forwarding Detection (BFD)
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#ifndef _BIRD_NBFD_H_
+#define _BIRD_NBFD_H_
+
+#include "lib/lists.h"
+#include "lib/resource.h"
+
+struct bfd_session;
+
+struct bfd_request {
+ resource r;
+ node n;
+
+ ip_addr addr;
+ ip_addr local;
+ struct iface *iface;
+
+ void (*hook)(struct bfd_request *);
+ void *data;
+
+ struct bfd_session *session;
+
+ u8 state;
+ u8 diag;
+ u8 old_state;
+ u8 down;
+};
+
+
+#ifdef CONFIG_BFD
+
+struct bfd_request * bfd_request_session(pool *p, ip_addr addr, ip_addr local, struct iface *iface, void (*hook)(struct bfd_request *), void *data);
+
+static inline void cf_check_bfd(int use) { }
+
+#else
+
+static inline struct bfd_request * bfd_request_session(pool *p, ip_addr addr, ip_addr local, struct iface *iface, void (*hook)(struct bfd_request *), void *data) { return NULL; }
+
+static inline void cf_check_bfd(int use) { if (use) cf_error("BFD not available"); }
+
+#endif /* CONFIG_BFD */
+
+
+
+#endif /* _BIRD_NBFD_H_ */
diff --git a/nest/cli.c b/nest/cli.c
index d245790b..11f98794 100644
--- a/nest/cli.c
+++ b/nest/cli.c
@@ -122,6 +122,7 @@ cli_printf(cli *c, int code, char *msg, ...)
va_list args;
byte buf[CLI_LINE_SIZE];
int cd = code;
+ int errcode;
int size, cnt;
if (cd < 0)
@@ -131,16 +132,26 @@ cli_printf(cli *c, int code, char *msg, ...)
size = bsprintf(buf, " ");
else
size = bsprintf(buf, "%04d-", cd);
+ errcode = -8000;
+ }
+ else if (cd == CLI_ASYNC_CODE)
+ {
+ size = 1; buf[0] = '+';
+ errcode = cd;
}
else
- size = bsprintf(buf, "%04d ", cd);
+ {
+ size = bsprintf(buf, "%04d ", cd);
+ errcode = 8000;
+ }
+
c->last_reply = cd;
va_start(args, msg);
cnt = bvsnprintf(buf+size, sizeof(buf)-size-1, msg, args);
va_end(args);
if (cnt < 0)
{
- cli_printf(c, code < 0 ? -8000 : 8000, "<line overflow>");
+ cli_printf(c, errcode, "<line overflow>");
return;
}
size += cnt;
@@ -385,12 +396,17 @@ cli_echo(unsigned int class, byte *msg)
}
}
+/* Hack for scheduled undo notification */
+extern cli *cmd_reconfig_stored_cli;
+
void
cli_free(cli *c)
{
cli_set_log_echo(c, 0, 0);
if (c->cleanup)
c->cleanup(c);
+ if (c == cmd_reconfig_stored_cli)
+ cmd_reconfig_stored_cli = NULL;
rfree(c->pool);
}
diff --git a/nest/cli.h b/nest/cli.h
index ea64680a..396656e8 100644
--- a/nest/cli.h
+++ b/nest/cli.h
@@ -49,6 +49,8 @@ typedef struct cli {
extern pool *cli_pool;
extern struct cli *this_cli; /* Used during parsing */
+#define CLI_ASYNC_CODE 10000
+
/* Functions to be called by command handlers */
void cli_printf(cli *, int, char *, ...);
diff --git a/nest/cmds.c b/nest/cmds.c
index 2a803930..ec6bc762 100644
--- a/nest/cmds.c
+++ b/nest/cmds.c
@@ -13,6 +13,10 @@
#include "nest/cmds.h"
#include "lib/string.h"
#include "lib/resource.h"
+#include "filter/filter.h"
+
+extern int shutting_down;
+extern int configuring;
void
cmd_show_status(void)
@@ -27,9 +31,10 @@ cmd_show_status(void)
cli_msg(-1011, "Last reboot on %s", tim);
tm_format_datetime(tim, &config->tf_base, config->load_time);
cli_msg(-1011, "Last reconfiguration on %s", tim);
+
if (shutting_down)
cli_msg(13, "Shutdown in progress");
- else if (old_config)
+ else if (configuring)
cli_msg(13, "Reconfiguration in progress");
else
cli_msg(13, "Daemon is up and running");
@@ -86,3 +91,20 @@ cmd_show_memory(void)
print_size("Total:", rmemsize(&root_pool));
cli_msg(0, "");
}
+
+void
+cmd_eval(struct f_inst *expr)
+{
+ struct f_val v = f_eval(expr, this_cli->parser_pool);
+
+ if (v.type == T_RETURN)
+ {
+ cli_msg(8008, "runtime error");
+ return;
+ }
+
+ buffer buf;
+ LOG_BUFFER_INIT(buf);
+ val_format(v, &buf);
+ cli_msg(23, "%s", buf.start);
+}
diff --git a/nest/cmds.h b/nest/cmds.h
index 8b0bff7e..4cf8fb1b 100644
--- a/nest/cmds.h
+++ b/nest/cmds.h
@@ -11,6 +11,9 @@ struct sym_show_data {
struct symbol *sym;
};
+struct f_inst;
+
void cmd_show_status(void);
void cmd_show_symbols(struct sym_show_data *sym);
void cmd_show_memory(void);
+void cmd_eval(struct f_inst *expr);
diff --git a/nest/config.Y b/nest/config.Y
index a75dd0c3..e9b8a21b 100644
--- a/nest/config.Y
+++ b/nest/config.Y
@@ -44,11 +44,11 @@ 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(LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE)
+CF_KEYWORDS(RECEIVE, LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, FILTERED)
CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES)
-CF_KEYWORDS(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, GENERATE, ROA, MAX, FLUSH)
+CF_KEYWORDS(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, GENERATE, ROA, MAX, FLUSH, AS)
CF_KEYWORDS(LISTEN, BGP, V6ONLY, DUAL, ADDRESS, PORT, PASSWORDS, DESCRIPTION, SORTED)
-CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC)
+CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC, CLASS, DSCP)
CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT,
RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE)
@@ -65,7 +65,7 @@ CF_ENUM(T_ENUM_ROA, ROA_, UNKNOWN, VALID, INVALID)
%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 limit_action tab_sorted
+%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 limit_action tab_sorted tos
%type <ps> proto_patt proto_patt2
%type <g> limit_spec
@@ -75,9 +75,9 @@ CF_GRAMMAR
CF_ADDTO(conf, rtrid)
-rtrid: ROUTER ID idval ';' {
- new_config->router_id = $3;
- }
+rtrid:
+ ROUTER ID idval ';' { new_config->router_id = $3; }
+ | ROUTER ID FROM iface_patt ';' { new_config->router_id_from = this_ipatt; }
;
idval:
@@ -185,8 +185,10 @@ proto_item:
| MRTDUMP mrtdump_mask { this_proto->mrtdump = $2; }
| IMPORT imexport { this_proto->in_filter = $2; }
| EXPORT imexport { this_proto->out_filter = $2; }
+ | RECEIVE LIMIT limit_spec { this_proto->rx_limit = $3; }
| IMPORT LIMIT limit_spec { this_proto->in_limit = $3; }
| EXPORT LIMIT limit_spec { this_proto->out_limit = $3; }
+ | IMPORT KEEP FILTERED bool { this_proto->in_keep_filtered = $4; }
| TABLE rtable { this_proto->table = $2; }
| ROUTER ID idval { this_proto->router_id = $3; }
| DESCRIPTION TEXT { this_proto->dsc = $2; }
@@ -214,6 +216,7 @@ limit_spec:
l->action = $2;
$$ = l;
}
+ | OFF { $$ = NULL; }
;
rtable:
@@ -263,6 +266,21 @@ iface_patt_list:
| iface_patt_list ',' iface_patt_node
;
+iface_patt_init: {
+ /* Generic this_ipatt init */
+ this_ipatt = cfg_allocz(sizeof(struct iface_patt));
+ init_list(&this_ipatt->ipn_list);
+ }
+ ;
+
+iface_patt:
+ iface_patt_init iface_patt_list
+ ;
+
+tos:
+ CLASS expr { $$ = $2 & 0xfc; if (($2 < 0) || ($2 > 255)) cf_error("TX class must be in range 0-255"); }
+ | DSCP expr { $$ = ($2 & 0x3f) << 2; if (($2 < 0) || ($2 > 63)) cf_error("TX DSCP must be in range 0-63"); }
+ ;
/* Direct device route protocol */
@@ -405,7 +423,7 @@ 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]])
+CF_CLI(SHOW ROUTE, r_args, [[[<prefix>|for <prefix>|for <ip>] [table <t>] [filter <f>|where <cond>] [all] [primary] [filtered] [(export|preexport) <p>] [protocol <p>] [stats|count]]], [[Show routing table]])
{ rt_show($3); } ;
r_args:
@@ -413,7 +431,6 @@ r_args:
$$ = cfg_allocz(sizeof(struct rt_show_data));
$$->pxlen = 256;
$$->filter = FILTER_ACCEPT;
- $$->table = config->master_rtc->table;
}
| r_args prefix {
$$ = $1;
@@ -451,6 +468,10 @@ r_args:
$$ = $1;
$$->primary_only = 1;
}
+ | r_args FILTERED {
+ $$ = $1;
+ $$->filtered = 1;
+ }
| r_args export_or_preexport SYM {
struct proto_config *c = (struct proto_config *) $3->def;
$$ = $1;
@@ -591,7 +612,11 @@ CF_CLI(DUMP ROUTES,,, [[Dump routing table]])
CF_CLI(DUMP PROTOCOLS,,, [[Dump protocol information]])
{ protos_dump_all(); cli_msg(0, ""); } ;
-CF_CLI(ECHO, echo_mask echo_size, [all | off | <mask>] [<buffer-size>], [[Configure echoing of log messages]]) {
+CF_CLI(EVAL, term, <expr>, [[Evaluate an expression]])
+{ cmd_eval($2); } ;
+
+CF_CLI_HELP(ECHO, ..., [[Control echoing of log messages]])
+CF_CLI(ECHO, echo_mask echo_size, (all | off | { debug | trace | info | remote | warning | error | auth }) [<buffer-size>], [[Control echoing of log messages]]) {
cli_set_log_echo(this_cli, $2, $3);
cli_msg(0, "");
} ;
@@ -599,7 +624,7 @@ CF_CLI(ECHO, echo_mask echo_size, [all | off | <mask>] [<buffer-size>], [[Config
echo_mask:
ALL { $$ = ~0; }
| OFF { $$ = 0; }
- | NUM
+ | '{' log_mask_list '}' { $$ = $2; }
;
echo_size:
diff --git a/nest/iface.c b/nest/iface.c
index eea3d3b1..b4ab70c3 100644
--- a/nest/iface.c
+++ b/nest/iface.c
@@ -35,8 +35,6 @@
static pool *if_pool;
-static void auto_router_id(void);
-
list iface_list;
/**
@@ -354,9 +352,6 @@ if_end_update(void)
struct iface *i;
struct ifa *a, *b;
- if (!config->router_id)
- auto_router_id();
-
WALK_LIST(i, iface_list)
{
if (!(i->flags & IF_UPDATED))
@@ -583,24 +578,61 @@ ifa_delete(struct ifa *a)
}
}
-static void
-auto_router_id(void)
+u32
+if_choose_router_id(struct iface_patt *mask, u32 old_id)
{
#ifndef IPV6
- struct iface *i, *j;
+ struct iface *i;
+ struct ifa *a, *b;
- j = NULL;
+ b = NULL;
WALK_LIST(i, iface_list)
- if ((i->flags & IF_ADMIN_UP) &&
- !(i->flags & (IF_IGNORE | IF_SHUTDOWN)) &&
- i->addr &&
- !(i->addr->flags & IA_PEER) &&
- (!j || ipa_to_u32(i->addr->ip) < ipa_to_u32(j->addr->ip)))
- j = i;
- if (!j)
- die("Cannot determine router ID (no suitable network interface found), please configure it manually");
- log(L_INFO "Guessed router ID %I according to interface %s", j->addr->ip, j->name);
- config->router_id = ipa_to_u32(j->addr->ip);
+ {
+ if (!(i->flags & IF_ADMIN_UP) ||
+ (i->flags & IF_SHUTDOWN))
+ continue;
+
+ WALK_LIST(a, i->addrs)
+ {
+ if (a->flags & IA_SECONDARY)
+ continue;
+
+ if (a->scope <= SCOPE_LINK)
+ continue;
+
+ /* FIXME: This should go away */
+ if (a->flags & IA_PEER)
+ continue;
+
+ /* FIXME: This should go away too */
+ if (!mask && (a != i->addr))
+ continue;
+
+ /* Check pattern if specified */
+ if (mask && !iface_patt_match(mask, i, a))
+ continue;
+
+ /* FIXME: This should go away too */
+ if ((i->flags & IF_IGNORE) && !mask)
+ continue;
+
+ /* No pattern or pattern matched */
+ if (!b || ipa_to_u32(a->ip) < ipa_to_u32(b->ip))
+ b = a;
+ }
+ }
+
+ if (!b)
+ return 0;
+
+ u32 id = ipa_to_u32(b->ip);
+ if (id != old_id)
+ log(L_INFO "Chosen router ID %R according to interface %s", id, b->iface->name);
+
+ return id;
+
+#else
+ return 0;
#endif
}
diff --git a/nest/iface.h b/nest/iface.h
index 2416f82f..697ea543 100644
--- a/nest/iface.h
+++ b/nest/iface.h
@@ -101,6 +101,7 @@ struct iface *if_find_by_name(char *);
struct iface *if_get_by_name(char *);
void ifa_recalc_all_primary_addresses(void);
+
/* The Neighbor Cache */
typedef struct neighbor {
@@ -161,4 +162,7 @@ int iface_patt_match(struct iface_patt *ifp, struct iface *i, struct ifa *a);
struct iface_patt *iface_patt_find(list *l, struct iface *i, struct ifa *a);
int iface_patts_equal(list *, list *, int (*)(struct iface_patt *, struct iface_patt *));
+
+u32 if_choose_router_id(struct iface_patt *mask, u32 old_id);
+
#endif
diff --git a/nest/neighbor.c b/nest/neighbor.c
index 506d9bde..11a980b2 100644
--- a/nest/neighbor.c
+++ b/nest/neighbor.c
@@ -114,7 +114,7 @@ neighbor *
neigh_find2(struct proto *p, ip_addr *a, struct iface *ifa, unsigned flags)
{
neighbor *n;
- int class, scope = -1; ;
+ int class, scope = -1;
unsigned int h = neigh_hash(p, a);
struct iface *i;
@@ -231,7 +231,7 @@ neigh_up(neighbor *n, struct iface *i, int scope)
static void
neigh_down(neighbor *n)
{
- DBG("Flushing neighbor %I on %s\n", n->addr, i->name);
+ DBG("Flushing neighbor %I on %s\n", n->addr, n->iface->name);
rem_node(&n->if_n);
if (! (n->flags & NEF_BIND))
n->iface = NULL;
@@ -240,7 +240,21 @@ neigh_down(neighbor *n)
n->proto->neigh_notify(n);
rem_node(&n->n);
if (n->flags & NEF_STICKY)
- add_tail(&sticky_neigh_list, &n->n);
+ {
+ add_tail(&sticky_neigh_list, &n->n);
+
+ /* Respawn neighbor if there is another matching prefix */
+ struct iface *i;
+ int scope;
+
+ if (!n->iface)
+ WALK_LIST(i, iface_list)
+ if ((scope = if_connected(&n->addr, i)) >= 0)
+ {
+ neigh_up(n, i, scope);
+ return;
+ }
+ }
else
sl_free(neigh_slab, n);
}
diff --git a/nest/proto.c b/nest/proto.c
index 399c02e3..019b846e 100644
--- a/nest/proto.c
+++ b/nest/proto.c
@@ -345,6 +345,7 @@ protos_postconfig(struct config *c)
WALK_LIST(x, c->protos)
{
DBG(" %s", x->name);
+
p = x->protocol;
if (p->postconfig)
p->postconfig(x);
@@ -376,6 +377,7 @@ int proto_reconfig_type; /* Hack to propagate type info to pipe reconfigure hoo
static int
proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config *nc, int type)
{
+ struct announce_hook *ah = p->main_ahook;
/* If the protocol is DOWN, we just restart it */
if (p->proto_state == PS_DOWN)
return 0;
@@ -383,11 +385,9 @@ proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config
/* If there is a too big change in core attributes, ... */
if ((nc->protocol != oc->protocol) ||
(nc->disabled != p->disabled) ||
- (nc->table->table != oc->table->table) ||
- (proto_get_router_id(nc) != proto_get_router_id(oc)))
+ (nc->table->table != oc->table->table))
return 0;
-
p->debug = nc->debug;
p->mrtdump = nc->mrtdump;
proto_reconfig_type = type;
@@ -409,12 +409,31 @@ proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config
/* Update filters and limits in the main announce hook
Note that this also resets limit state */
- if (p->main_ahook)
+ if (ah)
{
- p->main_ahook->in_filter = nc->in_filter;
- p->main_ahook->out_filter = nc->out_filter;
- p->main_ahook->in_limit = nc->in_limit;
- p->main_ahook->out_limit = nc->out_limit;
+ ah->in_filter = nc->in_filter;
+ ah->out_filter = nc->out_filter;
+ ah->rx_limit = nc->rx_limit;
+ ah->in_limit = nc->in_limit;
+ ah->out_limit = nc->out_limit;
+ ah->in_keep_filtered = nc->in_keep_filtered;
+
+ if (p->proto_state == PS_UP) /* Recheck export/import/receive limit */
+ {
+ struct proto_stats *stats = ah->stats;
+ struct proto_limit *l = ah->in_limit;
+ u32 all_routes = stats->imp_routes + stats->filt_routes;
+
+ if (l && (stats->imp_routes >= l->limit)) proto_notify_limit(ah, l, PLD_IN, stats->imp_routes);
+
+ l = ah->rx_limit;
+
+ if (l && ( all_routes >= l->limit)) proto_notify_limit(ah, l, PLD_RX, all_routes );
+
+ l = ah->out_limit;
+
+ if (l && ( stats->exp_routes >= l->limit)) proto_notify_limit(ah, l, PLD_OUT, stats->exp_routes);
+ }
}
/* Update routes when filters changed. If the protocol in not UP,
@@ -516,7 +535,7 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty
p->down_code = nc->disabled ? PDC_CF_DISABLE : PDC_CF_RESTART;
p->cf_new = nc;
}
- else if (!shutting_down)
+ else if (!new->shutdown)
{
log(L_INFO "Removing protocol %s", p->name);
p->down_code = PDC_CF_REMOVE;
@@ -537,7 +556,7 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty
WALK_LIST(nc, new->protos)
if (!nc->proto)
{
- if (old_config) /* Not a first-time configuration */
+ if (old) /* Not a first-time configuration */
log(L_INFO "Adding protocol %s", nc->name);
proto_init(nc);
}
@@ -552,6 +571,16 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty
initial_device_proto = NULL;
}
+ /* Determine router ID for the first time - it has to be here and not in
+ global_commit() because it is postponed after start of device protocol */
+ if (!config->router_id)
+ {
+ config->router_id = if_choose_router_id(config->router_id_from, 0);
+ if (!config->router_id)
+ die("Cannot determine router ID, please configure it manually");
+ }
+
+ /* Start all other protocols */
WALK_LIST_DELSAFE(p, n, initial_proto_list)
proto_rethink_goal(p);
}
@@ -671,6 +700,9 @@ proto_build(struct protocol *p)
}
}
+/* FIXME: convert this call to some protocol hook */
+extern void bfd_init_all(void);
+
/**
* protos_build - build a protocol list
*
@@ -708,6 +740,11 @@ protos_build(void)
#ifdef CONFIG_BGP
proto_build(&proto_bgp);
#endif
+#ifdef CONFIG_BFD
+ proto_build(&proto_bfd);
+ bfd_init_all();
+#endif
+
proto_pool = rp_new(&root_pool, "Protocols");
proto_flush_event = ev_new(proto_pool);
proto_flush_event->hook = proto_flush_loop;
@@ -720,8 +757,9 @@ proto_fell_down(struct proto *p)
{
DBG("Protocol %s down\n", p->name);
- if (p->stats.imp_routes != 0)
- log(L_ERR "Protocol %s is down but still has %d routes", p->name, p->stats.imp_routes);
+ u32 all_routes = p->stats.imp_routes + p->stats.filt_routes;
+ if (all_routes != 0)
+ log(L_ERR "Protocol %s is down but still has %d routes", p->name, all_routes);
bzero(&p->stats, sizeof(struct proto_stats));
proto_free_ahooks(p);
@@ -798,9 +836,12 @@ proto_schedule_feed(struct proto *p, int initial)
p->main_ahook = proto_add_announce_hook(p, p->table, &p->stats);
p->main_ahook->in_filter = p->cf->in_filter;
p->main_ahook->out_filter = p->cf->out_filter;
+ p->main_ahook->rx_limit = p->cf->rx_limit;
p->main_ahook->in_limit = p->cf->in_limit;
p->main_ahook->out_limit = p->cf->out_limit;
+ p->main_ahook->in_keep_filtered = p->cf->in_keep_filtered;
+ proto_reset_limit(p->main_ahook->rx_limit);
proto_reset_limit(p->main_ahook->in_limit);
proto_reset_limit(p->main_ahook->out_limit);
}
@@ -825,14 +866,18 @@ static void
proto_schedule_flush_loop(void)
{
struct proto *p;
+ struct announce_hook *h;
if (flush_loop_state)
return;
flush_loop_state = 1;
- rt_schedule_prune_all();
WALK_LIST(p, flush_proto_list)
+ {
p->flushing = 1;
+ for (h=p->ahooks; h; h=h->next)
+ h->table->prune_state = 1;
+ }
ev_schedule(proto_flush_event);
}
@@ -974,6 +1019,7 @@ proto_limit_name(struct proto_limit *l)
* proto_notify_limit: notify about limit hit and take appropriate action
* @ah: announce hook
* @l: limit being hit
+ * @dir: limit direction (PLD_*)
* @rt_count: the number of routes
*
* The function is called by the route processing core when limit @l
@@ -981,10 +1027,11 @@ proto_limit_name(struct proto_limit *l)
* according to @l->action.
*/
void
-proto_notify_limit(struct announce_hook *ah, struct proto_limit *l, u32 rt_count)
+proto_notify_limit(struct announce_hook *ah, struct proto_limit *l, int dir, u32 rt_count)
{
+ const char *dir_name[PLD_MAX] = { "receive", "import" , "export" };
+ const byte dir_down[PLD_MAX] = { PDC_RX_LIMIT_HIT, PDC_IN_LIMIT_HIT, PDC_OUT_LIMIT_HIT };
struct proto *p = ah->proto;
- int dir = (ah->in_limit == l);
if (l->state == PLS_BLOCKED)
return;
@@ -992,7 +1039,7 @@ proto_notify_limit(struct announce_hook *ah, struct proto_limit *l, u32 rt_count
/* For warning action, we want the log message every time we hit the limit */
if (!l->state || ((l->action == PLA_WARN) && (rt_count == l->limit)))
log(L_WARN "Protocol %s hits route %s limit (%d), action: %s",
- p->name, dir ? "import" : "export", l->limit, proto_limit_name(l));
+ p->name, dir_name[dir], l->limit, proto_limit_name(l));
switch (l->action)
{
@@ -1007,8 +1054,7 @@ proto_notify_limit(struct announce_hook *ah, struct proto_limit *l, u32 rt_count
case PLA_RESTART:
case PLA_DISABLE:
l->state = PLS_BLOCKED;
- proto_schedule_down(p, l->action == PLA_RESTART,
- dir ? PDC_IN_LIMIT_HIT : PDC_OUT_LIMIT_HIT);
+ proto_schedule_down(p, l->action == PLA_RESTART, dir_down[dir]);
break;
}
}
@@ -1106,10 +1152,15 @@ proto_state_name(struct proto *p)
}
static void
-proto_show_stats(struct proto_stats *s)
+proto_show_stats(struct proto_stats *s, int in_keep_filtered)
{
- cli_msg(-1006, " Routes: %u imported, %u exported, %u preferred",
- s->imp_routes, s->exp_routes, s->pref_routes);
+ if (in_keep_filtered)
+ cli_msg(-1006, " Routes: %u imported, %u filtered, %u exported, %u preferred",
+ s->imp_routes, s->filt_routes, s->exp_routes, s->pref_routes);
+ else
+ cli_msg(-1006, " Routes: %u imported, %u exported, %u preferred",
+ s->imp_routes, s->exp_routes, s->pref_routes);
+
cli_msg(-1006, " Route change stats: received rejected filtered ignored accepted");
cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u",
s->imp_updates_received, s->imp_updates_invalid,
@@ -1143,11 +1194,12 @@ proto_show_basic_info(struct proto *p)
cli_msg(-1006, " Input filter: %s", filter_name(p->cf->in_filter));
cli_msg(-1006, " Output filter: %s", filter_name(p->cf->out_filter));
+ proto_show_limit(p->cf->rx_limit, "Receive limit:");
proto_show_limit(p->cf->in_limit, "Import limit:");
proto_show_limit(p->cf->out_limit, "Export limit:");
if (p->proto_state != PS_DOWN)
- proto_show_stats(&p->stats);
+ proto_show_stats(&p->stats, p->cf->in_keep_filtered);
}
void
@@ -1264,7 +1316,10 @@ proto_cmd_reload(struct proto *p, unsigned int dir, int cnt UNUSED)
* Perhaps, but these hooks work asynchronously.
*/
if (!p->proto->multitable)
- proto_reset_limit(p->main_ahook->in_limit);
+ {
+ proto_reset_limit(p->main_ahook->rx_limit);
+ proto_reset_limit(p->main_ahook->in_limit);
+ }
}
/* re-exporting routes */
diff --git a/nest/protocol.h b/nest/protocol.h
index d80201f3..b58f9e67 100644
--- a/nest/protocol.h
+++ b/nest/protocol.h
@@ -75,7 +75,7 @@ void protos_dump_all(void);
extern struct protocol
proto_device, proto_radv, proto_rip, proto_static,
- proto_ospf, proto_pipe, proto_bgp;
+ proto_ospf, proto_pipe, proto_bgp, proto_bfd;
/*
* Routing Protocol Instance
@@ -91,9 +91,12 @@ struct proto_config {
int class; /* SYM_PROTO or SYM_TEMPLATE */
u32 debug, mrtdump; /* Debugging bitfields, both use D_* constants */
unsigned preference, disabled; /* Generic parameters */
+ int in_keep_filtered; /* Routes rejected in import filter are kept */
u32 router_id; /* Protocol specific router ID */
struct rtable_config *table; /* Table we're attached to */
struct filter *in_filter, *out_filter; /* Attached filters */
+ struct proto_limit *rx_limit; /* Limit for receiving routes from protocol
+ (relevant when in_keep_filtered is active) */
struct proto_limit *in_limit; /* Limit for importing routes from protocol */
struct proto_limit *out_limit; /* Limit for exporting routes to protocol */
@@ -106,7 +109,8 @@ struct proto_config {
struct proto_stats {
/* Import - from protocol to core */
u32 imp_routes; /* Number of routes successfully imported to the (adjacent) routing table */
- u32 pref_routes; /* Number of routes that are preferred, sum over all routing table */
+ u32 filt_routes; /* Number of routes rejected in import filter but kept in the routing table */
+ u32 pref_routes; /* Number of routes that are preferred, sum over all routing tables */
u32 imp_updates_received; /* Number of route updates received */
u32 imp_updates_invalid; /* Number of route updates rejected as invalid */
u32 imp_updates_filtered; /* Number of route updates rejected by filters */
@@ -224,8 +228,9 @@ struct proto_spec {
#define PDC_CMD_DISABLE 0x11 /* Result of disable command */
#define PDC_CMD_RESTART 0x12 /* Result of restart command */
#define PDC_CMD_SHUTDOWN 0x13 /* Result of global shutdown */
-#define PDC_IN_LIMIT_HIT 0x21 /* Route import limit reached */
-#define PDC_OUT_LIMIT_HIT 0x22 /* Route export limit reached */
+#define PDC_RX_LIMIT_HIT 0x21 /* Route receive limit reached */
+#define PDC_IN_LIMIT_HIT 0x22 /* Route import limit reached */
+#define PDC_OUT_LIMIT_HIT 0x23 /* Route export limit reached */
void *proto_new(struct proto_config *, unsigned size);
@@ -354,6 +359,12 @@ void proto_notify_state(struct proto *p, unsigned state);
#define D_EVENTS 16 /* Protocol events */
#define D_PACKETS 32 /* Packets sent/received */
+#ifndef PARSER
+#define TRACE(flags, msg, args...) \
+ do { if (p->p.debug & flags) log(L_TRACE "%s: " msg, p->p.name , ## args ); } while(0)
+#endif
+
+
/*
* MRTDump flags
*/
@@ -372,6 +383,11 @@ extern struct proto_config *cf_dev_proto;
* Protocol limits
*/
+#define PLD_RX 0 /* Receive limit */
+#define PLD_IN 1 /* Import limit */
+#define PLD_OUT 2 /* Export limit */
+#define PLD_MAX 3
+
#define PLA_WARN 1 /* Issue log warning */
#define PLA_BLOCK 2 /* Block new routes */
#define PLA_RESTART 4 /* Force protocol restart */
@@ -387,7 +403,7 @@ struct proto_limit {
byte state; /* State of limit (PLS_*) */
};
-void proto_notify_limit(struct announce_hook *ah, struct proto_limit *l, u32 rt_count);
+void proto_notify_limit(struct announce_hook *ah, struct proto_limit *l, int dir, u32 rt_count);
static inline void
proto_reset_limit(struct proto_limit *l)
@@ -407,10 +423,12 @@ struct announce_hook {
struct proto *proto;
struct filter *in_filter; /* Input filter */
struct filter *out_filter; /* Output filter */
+ struct proto_limit *rx_limit; /* Receive limit (for in_keep_filtered) */
struct proto_limit *in_limit; /* Input limit */
struct proto_limit *out_limit; /* Output limit */
struct proto_stats *stats; /* Per-table protocol statistics */
struct announce_hook *next; /* Next hook for the same protocol */
+ int in_keep_filtered; /* Routes rejected in import filter are kept */
};
struct announce_hook *proto_add_announce_hook(struct proto *p, struct rtable *t, struct proto_stats *stats);
diff --git a/nest/route.h b/nest/route.h
index 3b65a855..f00f8b2b 100644
--- a/nest/route.h
+++ b/nest/route.h
@@ -141,7 +141,7 @@ 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 prune_state; /* Table prune state, 1 -> scheduled, 2-> running */
byte hcu_scheduled; /* Hostcache update is scheduled */
byte nhu_state; /* Next Hop Update state */
struct fib_iterator prune_fit; /* Rtable prune FIB iterator */
@@ -221,12 +221,26 @@ typedef struct rte {
} rte;
#define REF_COW 1 /* Copy this rte on write */
+#define REF_FILTERED 2 /* Route is rejected by import filter */
+
+/* Route is valid for propagation (may depend on other flags in the future), accepts NULL */
+static inline int rte_is_valid(rte *r) { return r && !(r->flags & REF_FILTERED); }
+
+/* Route just has REF_FILTERED flag */
+static inline int rte_is_filtered(rte *r) { return !!(r->flags & REF_FILTERED); }
+
/* Types of route announcement, also used as flags */
#define RA_OPTIMAL 1 /* Announcement of optimal route change */
#define RA_ACCEPTED 2 /* Announcement of first accepted route */
#define RA_ANY 3 /* Announcement of any route change */
+/* Return value of import_control() callback */
+#define RIC_ACCEPT 1 /* Accepted by protocol */
+#define RIC_PROCESS 0 /* Process it through import filter */
+#define RIC_REJECT -1 /* Rejected by protocol */
+#define RIC_DROP -2 /* Silently dropped by protocol */
+
struct config;
void rt_init(void);
@@ -242,6 +256,7 @@ rte *rte_get_temp(struct rta *);
void rte_update2(struct announce_hook *ah, net *net, rte *new, struct rte_src *src);
static inline void rte_update(struct proto *p, net *net, rte *new) { rte_update2(p->main_ahook, net, new, p->main_source); }
void rte_discard(rtable *tab, rte *old);
+int rt_examine(rtable *t, ip_addr prefix, int pxlen, struct proto *p, struct filter *filter);
void rte_dump(rte *);
void rte_free(rte *);
rte *rte_do_cow(rte *);
@@ -250,7 +265,6 @@ 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_schedule_prune_all(void);
int rt_prune_loop(void);
struct rtable_config *rt_new_table(struct symbol *s);
@@ -263,7 +277,7 @@ struct rt_show_data {
struct fib_iterator fit;
struct proto *show_protocol;
struct proto *export_protocol;
- int export_mode, primary_only;
+ int export_mode, primary_only, filtered;
struct config *running_on_config;
int net_counter, rt_counter, show_counter;
int stats, show_for;
@@ -400,6 +414,10 @@ struct adata {
byte data[0];
};
+static inline int adata_same(struct adata *a, struct adata *b)
+{ return (a->length == b->length && !memcmp(a->data, b->data, a->length)); }
+
+
typedef struct ea_list {
struct ea_list *next; /* In case we have an override list */
byte flags; /* Flags: EALF_... */
diff --git a/nest/rt-attr.c b/nest/rt-attr.c
index b2bb152f..0fb7c820 100644
--- a/nest/rt-attr.c
+++ b/nest/rt-attr.c
@@ -108,7 +108,7 @@ rte_src_alloc_id(void)
if (src_id_used > (src_id_size * 28))
{
src_id_size *= 2;
- src_ids = mb_realloc(rta_pool, src_ids, src_id_size * sizeof(u32));
+ src_ids = mb_realloc(src_ids, src_id_size * sizeof(u32));
bzero(src_ids + i, (src_id_size - i) * sizeof(u32));
goto found;
}
@@ -551,8 +551,7 @@ ea_same(ea_list *x, ea_list *y)
if (a->id != b->id ||
a->flags != b->flags ||
a->type != b->type ||
- ((a->type & EAF_EMBEDDED) ? a->u.data != b->u.data :
- (a->u.ptr->length != b->u.ptr->length || memcmp(a->u.ptr->data, b->u.ptr->data, a->u.ptr->length))))
+ ((a->type & EAF_EMBEDDED) ? a->u.data != b->u.data : !adata_same(a->u.ptr, b->u.ptr)))
return 0;
}
return 1;
diff --git a/nest/rt-dev.c b/nest/rt-dev.c
index 7319018f..1a859dac 100644
--- a/nest/rt-dev.c
+++ b/nest/rt-dev.c
@@ -34,6 +34,9 @@ dev_ifa_notify(struct proto *p, unsigned c, struct ifa *ad)
/* Empty list is automagically treated as "*" */
return;
+ if (ad->flags & IA_SECONDARY)
+ return;
+
if (ad->scope <= SCOPE_LINK)
return;
diff --git a/nest/rt-table.c b/nest/rt-table.c
index ecd6e324..8c91ea0a 100644
--- a/nest/rt-table.c
+++ b/nest/rt-table.c
@@ -77,7 +77,7 @@ net_route(rtable *tab, ip_addr a, int len)
{
a0 = ipa_and(a, ipa_mkmask(len));
n = fib_find(&tab->fib, &a0, len);
- if (n && n->routes)
+ if (n && rte_is_valid(n->routes))
return n;
len--;
}
@@ -147,8 +147,11 @@ rte_better(rte *new, rte *old)
{
int (*better)(rte *, rte *);
- if (!old)
+ if (!rte_is_valid(old))
return 1;
+ if (!rte_is_valid(new))
+ return 0;
+
if (new->pref > old->pref)
return 1;
if (new->pref < old->pref)
@@ -217,7 +220,8 @@ export_filter(struct announce_hook *ah, rte *rt0, rte **rt_free, ea_list **tmpa,
goto reject;
stats->exp_updates_rejected++;
- rte_trace_out(D_FILTERS, p, rt, "rejected by protocol");
+ if (v == RIC_REJECT)
+ rte_trace_out(D_FILTERS, p, rt, "rejected by protocol");
goto reject;
}
if (v > 0)
@@ -289,7 +293,7 @@ do_rt_notify(struct announce_hook *ah, net *net, rte *new, rte *old, ea_list *tm
if (l && new)
{
if ((!old || refeed) && (stats->exp_routes >= l->limit))
- proto_notify_limit(ah, l, stats->exp_routes);
+ proto_notify_limit(ah, l, PLD_OUT, stats->exp_routes);
if (l->state == PLS_BLOCKED)
{
@@ -406,9 +410,13 @@ rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *ol
rte *old_free = NULL;
rte *r;
- /* Used to track whether we met old_changed position. If it is NULL
- it was the first and met it implicitly before current best route. */
- int old_meet = (old_changed && !before_old) ? 1 : 0;
+ /* Used to track whether we met old_changed position. If before_old is NULL
+ old_changed was the first and we met it implicitly before current best route. */
+ int old_meet = old_changed && !before_old;
+
+ /* Note that before_old is either NULL or valid (not rejected) route.
+ If old_changed is valid, before_old have to be too. If old changed route
+ was not valid, caller must use NULL for both old_changed and before_old. */
if (new_changed)
stats->exp_updates_received++;
@@ -416,7 +424,7 @@ rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *ol
stats->exp_withdraws_received++;
/* First, find the new_best route - first accepted by filters */
- for (r=net->routes; r; r=r->next)
+ for (r=net->routes; rte_is_valid(r); r=r->next)
{
if (new_best = export_filter(ah, r, &new_free, &tmpa, 0))
break;
@@ -435,7 +443,8 @@ rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *ol
if (feed)
{
if (feed == 2) /* refeed */
- old_best = new_best ? new_best : net->routes;
+ old_best = new_best ? new_best :
+ (rte_is_valid(net->routes) ? net->routes : NULL);
else
old_best = NULL;
@@ -484,7 +493,7 @@ rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *ol
}
/* Fourth case */
- for (r=r->next; r; r=r->next)
+ for (r=r->next; rte_is_valid(r); r=r->next)
{
if (old_best = export_filter(ah, r, &old_free, NULL, 1))
goto found;
@@ -538,7 +547,14 @@ rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *ol
static void
rte_announce(rtable *tab, unsigned type, net *net, rte *new, rte *old, rte *before_old, ea_list *tmpa)
{
- struct announce_hook *a;
+ if (!rte_is_valid(old))
+ old = before_old = NULL;
+
+ if (!rte_is_valid(new))
+ new = NULL;
+
+ if (!old && !new)
+ return;
if (type == RA_OPTIMAL)
{
@@ -551,6 +567,7 @@ rte_announce(rtable *tab, unsigned type, net *net, rte *new, rte *old, rte *befo
rt_notify_hostcache(tab, net);
}
+ struct announce_hook *a;
WALK_LIST(a, tab->hooks)
{
ASSERT(a->proto->core_state == FS_HAPPY || a->proto->core_state == FS_FEEDING);
@@ -618,12 +635,15 @@ rte_same(rte *x, rte *y)
(!x->attrs->src->proto->rte_same || x->attrs->src->proto->rte_same(x, y));
}
+static inline int rte_is_ok(rte *e) { return e && !rte_is_filtered(e); }
+
static void
rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, struct rte_src *src)
{
struct proto *p = ah->proto;
struct rtable *table = ah->table;
struct proto_stats *stats = ah->stats;
+ static struct rate_limit rl_pipe;
rte *before_old = NULL;
rte *old_best = net->routes;
rte *old = NULL;
@@ -647,7 +667,7 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
{
if (new)
{
- log(L_ERR "Pipe collision detected when sending %I/%d to table %s",
+ log_rl(&rl_pipe, L_ERR "Pipe collision detected when sending %I/%d to table %s",
net->n.prefix, net->n.pxlen, table->name);
rte_free_quick(new);
}
@@ -657,8 +677,13 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
if (new && rte_same(old, new))
{
/* No changes, ignore the new route */
- stats->imp_updates_ignored++;
- rte_trace_in(D_ROUTES, p, new, "ignored");
+
+ if (!rte_is_filtered(new))
+ {
+ stats->imp_updates_ignored++;
+ rte_trace_in(D_ROUTES, p, new, "ignored");
+ }
+
rte_free_quick(new);
#ifdef CONFIG_RIP
/* lastmod is used internally by RIP as the last time
@@ -684,14 +709,22 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
return;
}
- struct proto_limit *l = ah->in_limit;
+ int new_ok = rte_is_ok(new);
+ int old_ok = rte_is_ok(old);
+
+ struct proto_limit *l = ah->rx_limit;
if (l && !old && new)
{
- if (stats->imp_routes >= l->limit)
- proto_notify_limit(ah, l, stats->imp_routes);
+ u32 all_routes = stats->imp_routes + stats->filt_routes;
+
+ if (all_routes >= l->limit)
+ proto_notify_limit(ah, l, PLD_RX, all_routes);
if (l->state == PLS_BLOCKED)
{
+ /* In receive limit the situation is simple, old is NULL so
+ we just free new and exit like nothing happened */
+
stats->imp_updates_ignored++;
rte_trace_in(D_FILTERS, p, new, "ignored [limit]");
rte_free_quick(new);
@@ -699,15 +732,53 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
}
}
- if (new)
+ l = ah->in_limit;
+ if (l && !old_ok && new_ok)
+ {
+ if (stats->imp_routes >= l->limit)
+ proto_notify_limit(ah, l, PLD_IN, stats->imp_routes);
+
+ if (l->state == PLS_BLOCKED)
+ {
+ /* In import limit the situation is more complicated. We
+ shouldn't just drop the route, we should handle it like
+ it was filtered. We also have to continue the route
+ processing if old or new is non-NULL, but we should exit
+ if both are NULL as this case is probably assumed to be
+ already handled. */
+
+ stats->imp_updates_ignored++;
+ rte_trace_in(D_FILTERS, p, new, "ignored [limit]");
+
+ if (ah->in_keep_filtered)
+ new->flags |= REF_FILTERED;
+ else
+ { rte_free_quick(new); new = NULL; }
+
+ /* Note that old && !new could be possible when
+ ah->in_keep_filtered changed in the recent past. */
+
+ if (!old && !new)
+ return;
+
+ new_ok = 0;
+ goto skip_stats1;
+ }
+ }
+
+ if (new_ok)
stats->imp_updates_accepted++;
- else
+ else if (old_ok)
stats->imp_withdraws_accepted++;
+ else
+ stats->imp_withdraws_ignored++;
+
+ skip_stats1:
if (new)
- stats->imp_routes++;
+ rte_is_filtered(new) ? stats->filt_routes++ : stats->imp_routes++;
if (old)
- stats->imp_routes--;
+ rte_is_filtered(old) ? stats->filt_routes-- : stats->imp_routes--;
if (table->config->sorted)
{
@@ -792,17 +863,19 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
new->lastmod = now;
/* Log the route change */
- if (new)
- rte_trace_in(D_ROUTES, p, new, net->routes == new ? "added [best]" : "added");
-
- if (!new && (p->debug & D_ROUTES))
+ if (p->debug & D_ROUTES)
{
- if (old != old_best)
- rte_trace_in(D_ROUTES, p, old, "removed");
- else if (net->routes)
- rte_trace_in(D_ROUTES, p, old, "removed [replaced]");
- else
- rte_trace_in(D_ROUTES, p, old, "removed [sole]");
+ if (new_ok)
+ rte_trace(p, new, '>', new == net->routes ? "added [best]" : "added");
+ else if (old_ok)
+ {
+ if (old != old_best)
+ rte_trace(p, old, '>', "removed");
+ else if (rte_is_ok(net->routes))
+ rte_trace(p, old, '>', "removed [replaced]");
+ else
+ rte_trace(p, old, '>', "removed [sole]");
+ }
}
/* Propagate the route change */
@@ -817,17 +890,13 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
(table->gc_time + table->config->gc_min_time <= now))
rt_schedule_gc(table);
+ if (old_ok && p->rte_remove)
+ p->rte_remove(net, old);
+ if (new_ok && p->rte_insert)
+ p->rte_insert(net, new);
+
if (old)
- {
- if (p->rte_remove)
- p->rte_remove(net, old);
- rte_free_quick(old);
- }
- if (new)
- {
- if (p->rte_insert)
- p->rte_insert(net, new);
- }
+ rte_free_quick(old);
}
static int rte_update_nest_cnt; /* Nesting counter to allow recursive updates */
@@ -845,6 +914,26 @@ rte_update_unlock(void)
lp_flush(rte_update_pool);
}
+static inline void
+rte_hide_dummy_routes(net *net, rte **dummy)
+{
+ if (net->routes && net->routes->attrs->source == RTS_DUMMY)
+ {
+ *dummy = net->routes;
+ net->routes = (*dummy)->next;
+ }
+}
+
+static inline void
+rte_unhide_dummy_routes(net *net, rte **dummy)
+{
+ if (*dummy)
+ {
+ (*dummy)->next = net->routes;
+ net->routes = *dummy;
+ }
+}
+
/**
* rte_update - enter a new update to a routing table
* @table: table to be updated
@@ -894,6 +983,7 @@ rte_update2(struct announce_hook *ah, net *net, rte *new, struct rte_src *src)
struct proto_stats *stats = ah->stats;
struct filter *filter = ah->in_filter;
ea_list *tmpa = NULL;
+ rte *dummy = NULL;
rte_update_lock();
if (new)
@@ -907,28 +997,39 @@ rte_update2(struct announce_hook *ah, net *net, rte *new, struct rte_src *src)
stats->imp_updates_invalid++;
goto drop;
}
+
if (filter == FILTER_REJECT)
{
stats->imp_updates_filtered++;
rte_trace_in(D_FILTERS, p, new, "filtered out");
- goto drop;
- }
- tmpa = make_tmp_attrs(new, rte_update_pool);
- if (filter)
+ if (! ah->in_keep_filtered)
+ goto drop;
+
+ /* new is a private copy, i could modify it */
+ new->flags |= REF_FILTERED;
+ }
+ else
{
- ea_list *old_tmpa = tmpa;
- int fr = f_run(filter, &new, &tmpa, rte_update_pool, 0);
- if (fr > F_ACCEPT)
+ tmpa = make_tmp_attrs(new, rte_update_pool);
+ if (filter && (filter != FILTER_REJECT))
{
- stats->imp_updates_filtered++;
- rte_trace_in(D_FILTERS, p, new, "filtered out");
- goto drop;
+ ea_list *old_tmpa = tmpa;
+ int fr = f_run(filter, &new, &tmpa, rte_update_pool, 0);
+ if (fr > F_ACCEPT)
+ {
+ stats->imp_updates_filtered++;
+ rte_trace_in(D_FILTERS, p, new, "filtered out");
+
+ if (! ah->in_keep_filtered)
+ goto drop;
+
+ new->flags |= REF_FILTERED;
+ }
+ if (tmpa != old_tmpa && src->proto->store_tmp_attrs)
+ src->proto->store_tmp_attrs(new, tmpa);
}
- if (tmpa != old_tmpa && src->proto->store_tmp_attrs)
- src->proto->store_tmp_attrs(new, tmpa);
}
-
if (!rta_is_cached(new->attrs)) /* Need to copy attributes */
new->attrs = rta_lookup(new->attrs);
new->flags |= REF_COW;
@@ -945,14 +1046,18 @@ rte_update2(struct announce_hook *ah, net *net, rte *new, struct rte_src *src)
}
}
+ recalc:
+ rte_hide_dummy_routes(net, &dummy);
rte_recalculate(ah, net, new, tmpa, src);
+ rte_unhide_dummy_routes(net, &dummy);
rte_update_unlock();
return;
-drop:
+ drop:
rte_free(new);
- rte_recalculate(ah, net, NULL, NULL, src);
- rte_update_unlock();
+ new = NULL;
+ tmpa = NULL;
+ goto recalc;
}
/* Independent call to rte_announce(), used from next hop
@@ -976,6 +1081,33 @@ rte_discard(rtable *t, rte *old) /* Non-filtered route deletion, used during gar
rte_update_unlock();
}
+/* Check rtable for best route to given net whether it would be exported do p */
+int
+rt_examine(rtable *t, ip_addr prefix, int pxlen, struct proto *p, struct filter *filter)
+{
+ net *n = net_find(t, prefix, pxlen);
+ rte *rt = n ? n->routes : NULL;
+
+ if (!rte_is_valid(rt))
+ return 0;
+
+ rte_update_lock();
+
+ /* Rest is stripped down export_filter() */
+ ea_list *tmpa = make_tmp_attrs(rt, rte_update_pool);
+ int v = p->import_control ? p->import_control(p, &rt, &tmpa, rte_update_pool) : 0;
+ if (v == RIC_PROCESS)
+ v = (f_run(filter, &rt, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) <= F_ACCEPT);
+
+ /* Discard temporary rte */
+ if (rt != n->routes)
+ rte_free(rt);
+
+ rte_update_unlock();
+
+ return v > 0;
+}
+
/**
* rte_dump - dump a route
* @e: &rte to be dumped
@@ -1151,20 +1283,10 @@ rt_init(void)
}
-/* Called from proto_schedule_flush_loop() only,
- ensuring that all prune states are zero */
-void
-rt_schedule_prune_all(void)
-{
- rtable *t;
-
- WALK_LIST(t, routing_tables)
- t->prune_state = 1;
-}
-
static inline int
-rt_prune_step(rtable *tab, int *max_feed)
+rt_prune_step(rtable *tab, int step, int *max_feed)
{
+ static struct rate_limit rl_flush;
struct fib_iterator *fit = &tab->prune_fit;
DBG("Pruning route table %s\n", tab->name);
@@ -1189,8 +1311,8 @@ again:
rescan:
for (e=n->routes; e; e=e->next)
- if (e->sender->proto->core_state != FS_HAPPY &&
- e->sender->proto->core_state != FS_FEEDING)
+ if (e->sender->proto->flushing ||
+ (step && e->attrs->src->proto->flushing))
{
if (*max_feed <= 0)
{
@@ -1198,6 +1320,10 @@ again:
return 0;
}
+ if (step)
+ log_rl(&rl_flush, L_WARN "Route %I/%d from %s still in %s after flush",
+ n->n.prefix, n->n.pxlen, e->attrs->src->proto->name, tab->name);
+
rte_discard(tab, e);
(*max_feed)--;
@@ -1222,23 +1348,42 @@ again:
/**
* rt_prune_loop - prune routing tables
- * @tab: routing table to be pruned
*
* The prune loop scans routing tables and removes routes belonging to
- * inactive protocols and also stale network entries. Returns 1 when
+ * flushing protocols and also stale network entries. Returns 1 when
* all such routes are pruned. It is a part of the protocol flushing
* loop.
+ *
+ * The prune loop runs in two steps. In the first step it prunes just
+ * the routes with flushing senders (in explicitly marked tables) so
+ * the route removal is propagated as usual. In the second step, all
+ * remaining relevant routes are removed. Ideally, there shouldn't be
+ * any, but it happens when pipe filters are changed.
*/
int
rt_prune_loop(void)
{
- rtable *t;
+ static int step = 0;
int max_feed = 512;
+ rtable *t;
+ again:
WALK_LIST(t, routing_tables)
- if (! rt_prune_step(t, &max_feed))
+ if (! rt_prune_step(t, step, &max_feed))
return 0;
+ if (step == 0)
+ {
+ /* Prepare for the second step */
+ WALK_LIST(t, routing_tables)
+ t->prune_state = 1;
+
+ step = 1;
+ goto again;
+ }
+
+ /* Done */
+ step = 0;
return 1;
}
@@ -1570,9 +1715,11 @@ again:
return 0;
}
+ /* XXXX perhaps we should change feed for RA_ACCEPTED to not use 'new' */
+
if ((p->accept_ra_types == RA_OPTIMAL) ||
(p->accept_ra_types == RA_ACCEPTED))
- if (e)
+ if (rte_is_valid(e))
{
if (p->core_state != FS_FEEDING)
return 1; /* In the meantime, the protocol fell down. */
@@ -1581,7 +1728,7 @@ again:
}
if (p->accept_ra_types == RA_ANY)
- for(e = n->routes; e != NULL; e = e->next)
+ for(e = n->routes; rte_is_valid(e); e = e->next)
{
if (p->core_state != FS_FEEDING)
return 1; /* In the meantime, the protocol fell down. */
@@ -1834,7 +1981,8 @@ rt_update_hostentry(rtable *tab, struct hostentry *he)
net *n = net_route(tab, he->addr, MAX_PREFIX_LENGTH);
if (n)
{
- rta *a = n->routes->attrs;
+ rte *e = n->routes;
+ rta *a = e->attrs;
pxlen = n->n.pxlen;
if (a->hostentry)
@@ -1867,7 +2015,7 @@ rt_update_hostentry(rtable *tab, struct hostentry *he)
}
he->src = rta_clone(a);
- he->igp_metric = rt_get_igp_metric(n->routes);
+ he->igp_metric = rt_get_igp_metric(e);
}
done:
@@ -2001,19 +2149,24 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
int ok;
bsprintf(ia, "%I/%d", n->n.prefix, n->n.pxlen);
- if (n->routes)
- d->net_counter++;
+
for(e=n->routes; e; e=e->next)
{
+ if (rte_is_filtered(e) != d->filtered)
+ continue;
+
struct ea_list *tmpa;
struct rte_src *src = e->attrs->src;
struct proto *p1 = d->export_protocol;
struct proto *p2 = d->show_protocol;
+
+ if (ia[0])
+ d->net_counter++;
d->rt_counter++;
ee = e;
rte_update_lock(); /* We use the update buffer for filtering */
tmpa = make_tmp_attrs(e, rte_update_pool);
- ok = (d->filter == FILTER_ACCEPT || f_run(d->filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) <= F_ACCEPT);
+ ok = f_run(d->filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) <= F_ACCEPT;
if (p2 && p2 != src->proto) ok = 0;
if (ok && d->export_mode)
{
@@ -2027,8 +2180,8 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
'configure soft' command may change the export filter
and do not update routes */
- if ((a = proto_find_announce_hook(p1, d->table)) && ((a->out_filter == FILTER_REJECT) ||
- (a->out_filter && f_run(a->out_filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT)))
+ if ((a = proto_find_announce_hook(p1, d->table)) &&
+ (f_run(a->out_filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT))
ok = 0;
}
}
@@ -2107,6 +2260,11 @@ rt_show(struct rt_show_data *d)
{
net *n;
+ /* Default is either a master table or a table related to a respective protocol */
+ if ((!d->table) && d->export_protocol) d->table = d->export_protocol->table;
+ if ((!d->table) && d->show_protocol) d->table = d->show_protocol->table;
+ if (!d->table) d->table = config->master_rtc->table;
+
if (d->pxlen == 256)
{
FIB_ITERATE_INIT(&d->fit, &d->table->fib);