Separate import and receive limits.

They have different behavior w.r.t. filtered routes that are kept.
This commit is contained in:
Ondrej Zajicek 2013-01-10 13:07:33 +01:00
parent 79b4e12e60
commit b662290f40
8 changed files with 94 additions and 22 deletions

2
README
View file

@ -3,7 +3,7 @@
(c) 1998--2008 Martin Mares <mj@ucw.cz> (c) 1998--2008 Martin Mares <mj@ucw.cz>
(c) 1998--2000 Pavel Machek <pavel@ucw.cz> (c) 1998--2000 Pavel Machek <pavel@ucw.cz>
(c) 1998--2008 Ondrej Filip <feela@network.cz> (c) 1998--2008 Ondrej Filip <feela@network.cz>
(c) 2009--2011 CZ.NIC z.s.p.o. (c) 2009--2013 CZ.NIC z.s.p.o.
================================================================================ ================================================================================

View file

@ -482,15 +482,23 @@ to zero to disable it. An empty <cf><m/switch/</cf> is equivalent to <cf/on/
Specify an import route limit (a maximum number of routes Specify an import route limit (a maximum number of routes
imported from the protocol) and optionally the action to be imported from the protocol) and optionally the action to be
taken when the limit is hit. Warn action just prints warning taken when the limit is hit. Warn action just prints warning
log message. Block action ignores new routes coming from the log message. Block action discards new routes coming from the
protocol. Restart and disable actions shut the protocol down protocol. Restart and disable actions shut the protocol down
like appropriate commands. Disable is the default action if an like appropriate commands. Disable is the default action if an
action is not explicitly specified. Note that limits are reset action is not explicitly specified. Note that limits are reset
during protocol reconfigure, reload or restart. Also note that during protocol reconfigure, reload or restart. Default: <cf/none/.
if <cf/import keep filtered/ is active, filtered routes are
counted towards the limit and blocked routes are forgotten, as <tag>receive limit <m/number/ [action warn | block | restart | disable]</tag>
the main purpose of the import limit is to protect routing Specify an receive route limit (a maximum number of routes
tables from overflow. Default: <cf/none/. received from the protocol and remembered). It works almost
identically to <cf>import limit</cf> option, the only
difference is that if <cf/import keep filtered/ option is
active, filtered routes are counted towards the limit and
blocked routes are forgotten, as the main purpose of the
receive limit is to protect routing tables from
overflow. Import limit, on the contrary, counts accepted
routes only and routes blocked by the limit are handled like
filtered routes. Default: <cf/none/.
<tag>export limit <m/number/ [action warn | block | restart | disable]</tag> <tag>export limit <m/number/ [action warn | block | restart | disable]</tag>
Specify an export route limit, works similarly to Specify an export route limit, works similarly to

View file

@ -44,7 +44,7 @@ CF_DECLS
CF_KEYWORDS(ROUTER, ID, PROTOCOL, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT) 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(INTERFACE, IMPORT, EXPORT, FILTER, NONE, TABLE, STATES, ROUTES, FILTERS)
CF_KEYWORDS(LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, FILTERED) CF_KEYWORDS(RECEIVE, LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, FILTERED)
CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES) 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)
CF_KEYWORDS(LISTEN, BGP, V6ONLY, DUAL, ADDRESS, PORT, PASSWORDS, DESCRIPTION, SORTED) CF_KEYWORDS(LISTEN, BGP, V6ONLY, DUAL, ADDRESS, PORT, PASSWORDS, DESCRIPTION, SORTED)
@ -185,6 +185,7 @@ proto_item:
| MRTDUMP mrtdump_mask { this_proto->mrtdump = $2; } | MRTDUMP mrtdump_mask { this_proto->mrtdump = $2; }
| IMPORT imexport { this_proto->in_filter = $2; } | IMPORT imexport { this_proto->in_filter = $2; }
| EXPORT imexport { this_proto->out_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; } | IMPORT LIMIT limit_spec { this_proto->in_limit = $3; }
| EXPORT LIMIT limit_spec { this_proto->out_limit = $3; } | EXPORT LIMIT limit_spec { this_proto->out_limit = $3; }
| IMPORT KEEP FILTERED bool { this_proto->in_keep_filtered = $4; } | IMPORT KEEP FILTERED bool { this_proto->in_keep_filtered = $4; }

View file

@ -344,6 +344,7 @@ protos_postconfig(struct config *c)
WALK_LIST(x, c->protos) WALK_LIST(x, c->protos)
{ {
DBG(" %s", x->name); DBG(" %s", x->name);
p = x->protocol; p = x->protocol;
if (p->postconfig) if (p->postconfig)
p->postconfig(x); p->postconfig(x);
@ -410,6 +411,7 @@ proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config
{ {
p->main_ahook->in_filter = nc->in_filter; p->main_ahook->in_filter = nc->in_filter;
p->main_ahook->out_filter = nc->out_filter; p->main_ahook->out_filter = nc->out_filter;
p->main_ahook->rx_limit = nc->rx_limit;
p->main_ahook->in_limit = nc->in_limit; p->main_ahook->in_limit = nc->in_limit;
p->main_ahook->out_limit = nc->out_limit; p->main_ahook->out_limit = nc->out_limit;
p->main_ahook->in_keep_filtered = nc->in_keep_filtered; p->main_ahook->in_keep_filtered = nc->in_keep_filtered;
@ -804,9 +806,11 @@ proto_schedule_feed(struct proto *p, int initial)
p->main_ahook = proto_add_announce_hook(p, p->table, &p->stats); p->main_ahook = proto_add_announce_hook(p, p->table, &p->stats);
p->main_ahook->in_filter = p->cf->in_filter; p->main_ahook->in_filter = p->cf->in_filter;
p->main_ahook->out_filter = p->cf->out_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->in_limit = p->cf->in_limit;
p->main_ahook->out_limit = p->cf->out_limit; p->main_ahook->out_limit = p->cf->out_limit;
p->main_ahook->in_keep_filtered = p->cf->in_keep_filtered; 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->in_limit);
proto_reset_limit(p->main_ahook->out_limit); proto_reset_limit(p->main_ahook->out_limit);
} }
@ -978,6 +982,7 @@ proto_limit_name(struct proto_limit *l)
* proto_notify_limit: notify about limit hit and take appropriate action * proto_notify_limit: notify about limit hit and take appropriate action
* @ah: announce hook * @ah: announce hook
* @l: limit being hit * @l: limit being hit
* @dir: limit direction (PLD_*)
* @rt_count: the number of routes * @rt_count: the number of routes
* *
* The function is called by the route processing core when limit @l * The function is called by the route processing core when limit @l
@ -985,10 +990,11 @@ proto_limit_name(struct proto_limit *l)
* according to @l->action. * according to @l->action.
*/ */
void 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; struct proto *p = ah->proto;
int dir = (ah->in_limit == l);
if (l->state == PLS_BLOCKED) if (l->state == PLS_BLOCKED)
return; return;
@ -996,7 +1002,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 */ /* 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))) if (!l->state || ((l->action == PLA_WARN) && (rt_count == l->limit)))
log(L_WARN "Protocol %s hits route %s limit (%d), action: %s", 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) switch (l->action)
{ {
@ -1011,8 +1017,7 @@ proto_notify_limit(struct announce_hook *ah, struct proto_limit *l, u32 rt_count
case PLA_RESTART: case PLA_RESTART:
case PLA_DISABLE: case PLA_DISABLE:
l->state = PLS_BLOCKED; l->state = PLS_BLOCKED;
proto_schedule_down(p, l->action == PLA_RESTART, proto_schedule_down(p, l->action == PLA_RESTART, dir_down[dir]);
dir ? PDC_IN_LIMIT_HIT : PDC_OUT_LIMIT_HIT);
break; break;
} }
} }
@ -1146,6 +1151,7 @@ proto_show_basic_info(struct proto *p)
cli_msg(-1006, " Input filter: %s", filter_name(p->cf->in_filter)); cli_msg(-1006, " Input filter: %s", filter_name(p->cf->in_filter));
cli_msg(-1006, " Output filter: %s", filter_name(p->cf->out_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->in_limit, "Import limit:");
proto_show_limit(p->cf->out_limit, "Export limit:"); proto_show_limit(p->cf->out_limit, "Export limit:");
@ -1267,7 +1273,10 @@ proto_cmd_reload(struct proto *p, unsigned int dir, int cnt UNUSED)
* Perhaps, but these hooks work asynchronously. * Perhaps, but these hooks work asynchronously.
*/ */
if (!p->proto->multitable) 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 */ /* re-exporting routes */

View file

@ -95,6 +95,8 @@ struct proto_config {
u32 router_id; /* Protocol specific router ID */ u32 router_id; /* Protocol specific router ID */
struct rtable_config *table; /* Table we're attached to */ struct rtable_config *table; /* Table we're attached to */
struct filter *in_filter, *out_filter; /* Attached filters */ 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 *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 */
@ -225,8 +227,9 @@ struct proto_spec {
#define PDC_CMD_DISABLE 0x11 /* Result of disable command */ #define PDC_CMD_DISABLE 0x11 /* Result of disable command */
#define PDC_CMD_RESTART 0x12 /* Result of restart command */ #define PDC_CMD_RESTART 0x12 /* Result of restart command */
#define PDC_CMD_SHUTDOWN 0x13 /* Result of global shutdown */ #define PDC_CMD_SHUTDOWN 0x13 /* Result of global shutdown */
#define PDC_IN_LIMIT_HIT 0x21 /* Route import limit reached */ #define PDC_RX_LIMIT_HIT 0x21 /* Route receive limit reached */
#define PDC_OUT_LIMIT_HIT 0x22 /* Route export 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); void *proto_new(struct proto_config *, unsigned size);
@ -373,6 +376,11 @@ extern struct proto_config *cf_dev_proto;
* Protocol limits * 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_WARN 1 /* Issue log warning */
#define PLA_BLOCK 2 /* Block new routes */ #define PLA_BLOCK 2 /* Block new routes */
#define PLA_RESTART 4 /* Force protocol restart */ #define PLA_RESTART 4 /* Force protocol restart */
@ -388,7 +396,7 @@ struct proto_limit {
byte state; /* State of limit (PLS_*) */ 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 static inline void
proto_reset_limit(struct proto_limit *l) proto_reset_limit(struct proto_limit *l)
@ -408,6 +416,7 @@ struct announce_hook {
struct proto *proto; struct proto *proto;
struct filter *in_filter; /* Input filter */ struct filter *in_filter; /* Input filter */
struct filter *out_filter; /* Output 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 *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 proto_stats *stats; /* Per-table protocol statistics */

View file

@ -285,7 +285,7 @@ do_rt_notify(struct announce_hook *ah, net *net, rte *new, rte *old, ea_list *tm
if (l && new) if (l && new)
{ {
if ((!old || refeed) && (stats->exp_routes >= l->limit)) 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) if (l->state == PLS_BLOCKED)
{ {
@ -700,16 +700,22 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
return; 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 (l && !old && new)
{ {
u32 all_routes = stats->imp_routes + stats->filt_routes; u32 all_routes = stats->imp_routes + stats->filt_routes;
if (all_routes >= l->limit) if (all_routes >= l->limit)
proto_notify_limit(ah, l, all_routes); proto_notify_limit(ah, l, PLD_RX, all_routes);
if (l->state == PLS_BLOCKED) 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++; stats->imp_updates_ignored++;
rte_trace_in(D_FILTERS, p, new, "ignored [limit]"); rte_trace_in(D_FILTERS, p, new, "ignored [limit]");
rte_free_quick(new); rte_free_quick(new);
@ -717,8 +723,39 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
} }
} }
int new_ok = rte_is_ok(new); l = ah->in_limit;
int old_ok = rte_is_ok(old); 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) if (new_ok)
stats->imp_updates_accepted++; stats->imp_updates_accepted++;
@ -727,6 +764,8 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
else else
stats->imp_withdraws_ignored++; stats->imp_withdraws_ignored++;
skip_stats1:
if (new) if (new)
rte_is_filtered(new) ? stats->filt_routes++ : stats->imp_routes++; rte_is_filtered(new) ? stats->filt_routes++ : stats->imp_routes++;
if (old) if (old)

View file

@ -878,6 +878,7 @@ bgp_shutdown(struct proto *P)
subcode = 4; // Errcode 6, 4 - administrative reset subcode = 4; // Errcode 6, 4 - administrative reset
break; break;
case PDC_RX_LIMIT_HIT:
case PDC_IN_LIMIT_HIT: case PDC_IN_LIMIT_HIT:
subcode = 1; // Errcode 6, 1 - max number of prefixes reached subcode = 1; // Errcode 6, 1 - max number of prefixes reached
/* log message for compatibility */ /* log message for compatibility */

View file

@ -200,6 +200,11 @@ pipe_postconfig(struct proto_config *C)
cf_error("Name of peer routing table not specified"); cf_error("Name of peer routing table not specified");
if (c->peer == C->table) if (c->peer == C->table)
cf_error("Primary table and peer table must be different"); cf_error("Primary table and peer table must be different");
if (C->in_keep_filtered)
cf_error("Pipe protocol prohibits keeping filtered routes");
if (C->rx_limit)
cf_error("Pipe protocol does not support receive limits");
} }
extern int proto_reconfig_type; extern int proto_reconfig_type;