diff options
author | Ondrej Zajicek <santiago@crfreenet.org> | 2013-11-23 11:50:34 +0100 |
---|---|---|
committer | Ondrej Zajicek <santiago@crfreenet.org> | 2013-11-23 11:50:34 +0100 |
commit | 736e143fa50607fcd88132291e96089b899af979 (patch) | |
tree | c0fcd5fb3174bae8a39b3a32dfe582b2ccb6df17 /nest | |
parent | 094d2bdb79e1ffa0a02761fd651aa0f0b6b0c585 (diff) | |
parent | 2b3d52aa421ae1c31e30107beefd82fddbb42854 (diff) |
Merge branch 'master' into add-path
Conflicts:
filter/filter.c
nest/proto.c
nest/rt-table.c
proto/bgp/bgp.h
proto/bgp/config.Y
Diffstat (limited to 'nest')
-rw-r--r-- | nest/a-path.c | 90 | ||||
-rw-r--r-- | nest/attrs.h | 10 | ||||
-rw-r--r-- | nest/bfd.h | 51 | ||||
-rw-r--r-- | nest/cli.c | 20 | ||||
-rw-r--r-- | nest/cli.h | 2 | ||||
-rw-r--r-- | nest/cmds.c | 24 | ||||
-rw-r--r-- | nest/cmds.h | 3 | ||||
-rw-r--r-- | nest/config.Y | 47 | ||||
-rw-r--r-- | nest/iface.c | 70 | ||||
-rw-r--r-- | nest/iface.h | 4 | ||||
-rw-r--r-- | nest/neighbor.c | 20 | ||||
-rw-r--r-- | nest/proto.c | 101 | ||||
-rw-r--r-- | nest/protocol.h | 28 | ||||
-rw-r--r-- | nest/route.h | 24 | ||||
-rw-r--r-- | nest/rt-attr.c | 5 | ||||
-rw-r--r-- | nest/rt-dev.c | 3 | ||||
-rw-r--r-- | nest/rt-table.c | 326 |
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_ */ @@ -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); } @@ -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); |