summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOndrej Zajicek <santiago@crfreenet.org>2013-01-10 13:07:33 +0100
committerOndrej Zajicek <santiago@crfreenet.org>2013-01-10 13:07:33 +0100
commitb662290f40ea0fa0b1a1ba283e50e833724f2050 (patch)
tree92c2e42f231e8e8a4294117faa087f96de65631e
parent79b4e12e6032faf6bb1f3feac385bd36ee53019e (diff)
Separate import and receive limits.
They have different behavior w.r.t. filtered routes that are kept.
-rw-r--r--README2
-rw-r--r--doc/bird.sgml20
-rw-r--r--nest/config.Y3
-rw-r--r--nest/proto.c21
-rw-r--r--nest/protocol.h15
-rw-r--r--nest/rt-table.c49
-rw-r--r--proto/bgp/bgp.c1
-rw-r--r--proto/pipe/pipe.c5
8 files changed, 94 insertions, 22 deletions
diff --git a/README b/README
index 5c2ef076..daeb18bd 100644
--- a/README
+++ b/README
@@ -3,7 +3,7 @@
(c) 1998--2008 Martin Mares <mj@ucw.cz>
(c) 1998--2000 Pavel Machek <pavel@ucw.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.
================================================================================
diff --git a/doc/bird.sgml b/doc/bird.sgml
index 4e04a138..b0d4e6a1 100644
--- a/doc/bird.sgml
+++ b/doc/bird.sgml
@@ -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
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
+ log message. Block action discards 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. Note that limits are reset
- during protocol reconfigure, reload or restart. Also note that
- if <cf/import keep filtered/ is active, filtered routes are
- counted towards the limit and blocked routes are forgotten, as
- the main purpose of the import limit is to protect routing
- tables from overflow. Default: <cf/none/.
+ during protocol reconfigure, reload or restart. Default: <cf/none/.
+
+ <tag>receive limit <m/number/ [action warn | block | restart | disable]</tag>
+ Specify an receive route limit (a maximum number of routes
+ 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>
Specify an export route limit, works similarly to
diff --git a/nest/config.Y b/nest/config.Y
index dbd72055..e46b5fb5 100644
--- a/nest/config.Y
+++ b/nest/config.Y
@@ -44,7 +44,7 @@ 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, 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(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, GENERATE, ROA, MAX, FLUSH)
CF_KEYWORDS(LISTEN, BGP, V6ONLY, DUAL, ADDRESS, PORT, PASSWORDS, DESCRIPTION, SORTED)
@@ -185,6 +185,7 @@ 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; }
diff --git a/nest/proto.c b/nest/proto.c
index b976a6cb..7e7fb7fa 100644
--- a/nest/proto.c
+++ b/nest/proto.c
@@ -344,6 +344,7 @@ protos_postconfig(struct config *c)
WALK_LIST(x, c->protos)
{
DBG(" %s", x->name);
+
p = x->protocol;
if (p->postconfig)
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->out_filter = nc->out_filter;
+ p->main_ahook->rx_limit = nc->rx_limit;
p->main_ahook->in_limit = nc->in_limit;
p->main_ahook->out_limit = nc->out_limit;
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->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);
}
@@ -978,6 +982,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
@@ -985,10 +990,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;
@@ -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 */
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)
{
@@ -1011,8 +1017,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;
}
}
@@ -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, " 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:");
@@ -1267,7 +1273,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 cf2ca0a4..033a0ede 100644
--- a/nest/protocol.h
+++ b/nest/protocol.h
@@ -95,6 +95,8 @@ struct proto_config {
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 */
@@ -225,8 +227,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);
@@ -373,6 +376,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 */
@@ -388,7 +396,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)
@@ -408,6 +416,7 @@ 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 */
diff --git a/nest/rt-table.c b/nest/rt-table.c
index 2f0840f0..99175448 100644
--- a/nest/rt-table.c
+++ b/nest/rt-table.c
@@ -285,7 +285,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)
{
@@ -700,16 +700,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)
{
u32 all_routes = stats->imp_routes + stats->filt_routes;
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)
{
+ /* 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);
@@ -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);
- int old_ok = rte_is_ok(old);
+ 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++;
@@ -727,6 +764,8 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
else
stats->imp_withdraws_ignored++;
+ skip_stats1:
+
if (new)
rte_is_filtered(new) ? stats->filt_routes++ : stats->imp_routes++;
if (old)
diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c
index 249d2e07..0f351b44 100644
--- a/proto/bgp/bgp.c
+++ b/proto/bgp/bgp.c
@@ -878,6 +878,7 @@ bgp_shutdown(struct proto *P)
subcode = 4; // Errcode 6, 4 - administrative reset
break;
+ case PDC_RX_LIMIT_HIT:
case PDC_IN_LIMIT_HIT:
subcode = 1; // Errcode 6, 1 - max number of prefixes reached
/* log message for compatibility */
diff --git a/proto/pipe/pipe.c b/proto/pipe/pipe.c
index 6099d284..51be3c7d 100644
--- a/proto/pipe/pipe.c
+++ b/proto/pipe/pipe.c
@@ -200,6 +200,11 @@ pipe_postconfig(struct proto_config *C)
cf_error("Name of peer routing table not specified");
if (c->peer == C->table)
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;