diff --git a/doc/bird.sgml b/doc/bird.sgml index 6f96b862..df6e2610 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -431,13 +431,19 @@ to zero to disable it. An empty is equivalent to export This is similar to the import keyword, except that it works in the direction from the routing table to the protocol. Default: import limit - Specify an import route limit and the action to be taken when - the limit is hit. Warn action just prints warning log - message. Block action ignores new routes (and converts route - updates to withdraws) coming from the protocol. Restart and - disable actions shut the protocol down like appropriate - commands. Default: import limit + Specify an import route limit (a maximum number of routes + imported from the protocol) and optionally the action to be + taken when the limit is hit. Warn action just prints warning + log message. Block action ignores new routes coming from the + protocol. Restart and disable actions shut the protocol down + like appropriate commands. Disable is the default action if an + action is not explicitly specified. Default: export limit + Specify an export route limit, works similarly to + the import limit option, but for the routes exported + to the protocol. Default: description " This is an optional description of the protocol. It is displayed as a part of the diff --git a/nest/config.Y b/nest/config.Y index 60b03278..c59319cb 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -179,6 +179,7 @@ proto_item: | IMPORT imexport { this_proto->in_filter = $2; } | EXPORT imexport { this_proto->out_filter = $2; } | IMPORT LIMIT limit_spec { this_proto->in_limit = $3; } + | EXPORT LIMIT limit_spec { this_proto->out_limit = $3; } | TABLE rtable { this_proto->table = $2; } | ROUTER ID idval { this_proto->router_id = $3; } | DESCRIPTION TEXT { this_proto->dsc = $2; } @@ -192,17 +193,18 @@ imexport: ; limit_action: - WARN { $$ = PLA_WARN; } - | BLOCK { $$ = PLA_BLOCK; } - | RESTART { $$ = PLA_RESTART; } - | DISABLE { $$ = PLA_DISABLE; } + /* default */ { $$ = PLA_DISABLE; } + | EXCEED WARN { $$ = PLA_WARN; } + | EXCEED BLOCK { $$ = PLA_BLOCK; } + | EXCEED RESTART { $$ = PLA_RESTART; } + | EXCEED DISABLE { $$ = PLA_DISABLE; } ; limit_spec: - expr EXCEED limit_action { + expr limit_action { struct proto_limit *l = cfg_allocz(sizeof(struct proto_limit)); l->limit = $1; - l->action = $3; + l->action = $2; $$ = l; } ; diff --git a/nest/proto.c b/nest/proto.c index cf81573f..887d3e5e 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -406,13 +406,14 @@ proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config if (p->proto->multitable) return 1; - /* Update filters in the main announce hook */ + /* Update filters and limits in the main announce hook + Note that this also resets limit state */ if (p->main_ahook) { 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; + p->main_ahook->out_limit = nc->out_limit; } /* Update routes when filters changed. If the protocol in not UP, @@ -774,9 +775,16 @@ proto_schedule_feed(struct proto *p, int initial) p->core_state = FS_FEEDING; p->refeeding = !initial; - /* Hack: reset exp_routes during refeed, and do not decrease it later */ + /* FIXME: This should be changed for better support of multitable protos */ if (!initial) - p->stats.exp_routes = 0; + { + struct announce_hook *ah; + for (ah = p->ahooks; ah; ah = ah->next) + proto_reset_limit(ah->out_limit); + + /* Hack: reset exp_routes during refeed, and do not decrease it later */ + p->stats.exp_routes = 0; + } /* Connect protocol to routing table */ if (initial && !p->proto->multitable) @@ -785,9 +793,9 @@ proto_schedule_feed(struct proto *p, int initial) p->main_ahook->in_filter = p->cf->in_filter; p->main_ahook->out_filter = p->cf->out_filter; p->main_ahook->in_limit = p->cf->in_limit; + p->main_ahook->out_limit = p->cf->out_limit; proto_reset_limit(p->main_ahook->in_limit); - // p->main_ahook->out_limit = p->cf->out_limit; - // proto_reset_limit(p->main_ahook->out_limit); + proto_reset_limit(p->main_ahook->out_limit); } proto_relink(p); @@ -872,6 +880,8 @@ proto_schedule_flush(struct proto *p) proto_schedule_flush_loop(); } +/* Temporary hack to propagate restart to BGP */ +int proto_restart; static void proto_shutdown_loop(struct timer *t UNUSED) @@ -881,11 +891,11 @@ proto_shutdown_loop(struct timer *t UNUSED) WALK_LIST_DELSAFE(p, p_next, active_proto_list) if (p->down_sched) { - int restart = (p->down_sched == PDS_RESTART); + proto_restart = (p->down_sched == PDS_RESTART); p->disabled = 1; proto_rethink_goal(p); - if (restart) + if (proto_restart) { p->disabled = 0; proto_rethink_goal(p); @@ -970,7 +980,8 @@ proto_notify_limit(struct announce_hook *ah, struct proto_limit *l, u32 rt_count if (l->state == PLS_BLOCKED) return; - if (rt_count == l->limit) + /* 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)); @@ -1118,6 +1129,7 @@ proto_show_basic_info(struct proto *p) cli_msg(-1006, " Output filter: %s", filter_name(p->cf->out_filter)); 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); diff --git a/nest/protocol.h b/nest/protocol.h index d8442acb..3f9ed96e 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -95,7 +95,7 @@ struct proto_config { struct rtable_config *table; /* Table we're attached to */ struct filter *in_filter, *out_filter; /* Attached filters */ struct proto_limit *in_limit; /* Limit for importing routes from protocol */ - // struct proto_limit *out_limit; /* Limit for exporting routes to protocol */ + struct proto_limit *out_limit; /* Limit for exporting routes to protocol */ /* Check proto_reconfigure() and proto_copy_config() after changing struct proto_config */ @@ -405,7 +405,7 @@ struct announce_hook { struct filter *in_filter; /* Input filter */ struct filter *out_filter; /* Output filter */ struct proto_limit *in_limit; /* Input limit */ - // struct proto_limit *out_limit; /* Output 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 */ }; diff --git a/nest/rt-table.c b/nest/rt-table.c index 6d82e1d3..06121ea3 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -273,6 +273,23 @@ do_rte_announce(struct announce_hook *ah, int type UNUSED, net *net, rte *new, r if (!new && !old) return; + struct proto_limit *l = ah->out_limit; + if (l && new && (!old || refeed)) + { + if (stats->exp_routes >= l->limit) + proto_notify_limit(ah, l, stats->exp_routes); + + if (l->state == PLS_BLOCKED) + { + /* Exported route counter ignores whether the route was + blocked by limit, to be consistent when limits change */ + stats->exp_routes++; + stats->exp_updates_rejected++; + rte_trace_out(D_FILTERS, p, new, "rejected [limit]"); + goto done; + } + } + if (new) stats->exp_updates_accepted++; else @@ -307,6 +324,8 @@ do_rte_announce(struct announce_hook *ah, int type UNUSED, net *net, rte *new, r } else p->rt_notify(p, ah->table, net, new, old, new->attrs->eattrs); + + done: if (new && new != new0) /* Discard temporary rte's */ rte_free(new); if (old && old != old0) diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index 95dbe477..3b9f7cc5 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -848,6 +848,8 @@ bgp_start(struct proto *P) return PS_START; } +extern int proto_restart; + static int bgp_shutdown(struct proto *P) { @@ -877,10 +879,16 @@ bgp_shutdown(struct proto *P) case PDC_IN_LIMIT_HIT: subcode = 1; // Errcode 6, 1 - max number of prefixes reached + /* log message for compatibility */ log(L_WARN "%s: Route limit exceeded, shutting down", p->p.name); + goto limit; + case PDC_OUT_LIMIT_HIT: + subcode = proto_restart ? 4 : 2; // Administrative reset or shutdown + + limit: bgp_store_error(p, NULL, BE_AUTO_DOWN, BEA_ROUTE_LIMIT_EXCEEDED); - if (P->cf->in_limit->action == PLA_RESTART) + if (proto_restart) bgp_update_startup_delay(p); else p->startup_delay = 0; diff --git a/proto/pipe/config.Y b/proto/pipe/config.Y index 4fb2b499..40637558 100644 --- a/proto/pipe/config.Y +++ b/proto/pipe/config.Y @@ -36,7 +36,6 @@ pipe_proto: cf_error("Routing table name expected"); PIPE_CFG->peer = $4->def; } - | pipe_proto EXPORT LIMIT limit_spec ';' { PIPE_CFG->out_limit = $4; } | pipe_proto MODE OPAQUE ';' { PIPE_CFG->mode = PIPE_OPAQUE; } | pipe_proto MODE TRANSPARENT ';' { PIPE_CFG->mode = PIPE_TRANSPARENT; } ; diff --git a/proto/pipe/pipe.c b/proto/pipe/pipe.c index 41bac474..6099d284 100644 --- a/proto/pipe/pipe.c +++ b/proto/pipe/pipe.c @@ -170,7 +170,7 @@ pipe_start(struct proto *P) p->peer_ahook = proto_add_announce_hook(P, p->peer_table, &p->peer_stats); p->peer_ahook->out_filter = cf->c.in_filter; - p->peer_ahook->in_limit = cf->out_limit; + p->peer_ahook->in_limit = cf->c.out_limit; proto_reset_limit(p->peer_ahook->in_limit); return PS_UP; @@ -225,7 +225,7 @@ pipe_reconfigure(struct proto *P, struct proto_config *new) if (p->peer_ahook) { p->peer_ahook->out_filter = new->in_filter; - p->peer_ahook->in_limit = nc->out_limit; + p->peer_ahook->in_limit = new->out_limit; } if ((P->proto_state != PS_UP) || (proto_reconfig_type == RECONFIG_SOFT)) @@ -311,7 +311,7 @@ pipe_show_proto_info(struct proto *P) cli_msg(-1006, " Output filter: %s", filter_name(cf->c.out_filter)); proto_show_limit(cf->c.in_limit, "Import limit:"); - proto_show_limit(cf->out_limit, "Export limit:"); + proto_show_limit(cf->c.out_limit, "Export limit:"); if (P->proto_state != PS_DOWN) pipe_show_stats(p); diff --git a/proto/pipe/pipe.h b/proto/pipe/pipe.h index e777fb41..50b31698 100644 --- a/proto/pipe/pipe.h +++ b/proto/pipe/pipe.h @@ -15,7 +15,6 @@ struct pipe_config { struct proto_config c; struct rtable_config *peer; /* Table we're connected to */ - struct proto_limit *out_limit; /* Export route limit */ int mode; /* PIPE_OPAQUE or PIPE_TRANSPARENT */ };