And also fixes some minor bugs in limits.
<tag>export <m/filter/</tag> This is similar to the <cf>import</cf> keyword, except that it
works in the direction from the routing table to the protocol. Default: <cf/none/.
- <tag>import limit <m/number/ exceed warn | block | restart | disable</tag>
- 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: <cf/none/.
+ <tag>import limit <m/number/ [exceed warn | block | restart | disable]</tag>
+ 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: <cf/none/.
+
+ <tag>export limit <m/number/ [exceed warn | block | restart | disable]</tag>
+ Specify an export route limit, works similarly to
+ the <cf>import limit</cf> option, but for the routes exported
+ to the protocol. Default: <cf/none/.
<tag>description "<m/text/"</tag> This is an optional
description of the protocol. It is displayed as a part of the
| 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; }
;
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;
}
;
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,
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)
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);
proto_schedule_flush_loop();
}
+/* Temporary hack to propagate restart to BGP */
+int proto_restart;
static void
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);
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));
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);
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 */
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 */
};
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
}
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)
return PS_START;
}
+extern int proto_restart;
+
static int
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;
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; }
;
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;
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))
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);
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 */
};