summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOndrej Zajicek <santiago@crfreenet.org>2012-04-15 15:28:29 +0200
committerOndrej Zajicek <santiago@crfreenet.org>2012-04-15 15:28:29 +0200
commitebecb6f6a11bb418dd054cf12a2673ca0d9eac37 (patch)
tree9454aad3bd2e84fc65b518574aa9d112a0e5155c
parentae8b300164a975597f9b6caea0b205af2e4db30b (diff)
Implements generalized import hooks.
Thanks to Alexander V. Chernikov for the original patch.
-rw-r--r--doc/bird.sgml11
-rw-r--r--nest/config.Y21
-rw-r--r--nest/proto.c154
-rw-r--r--nest/protocol.h57
-rw-r--r--nest/rt-table.c19
-rw-r--r--proto/bgp/bgp.c63
-rw-r--r--proto/bgp/bgp.h1
-rw-r--r--proto/bgp/config.Y6
-rw-r--r--proto/bgp/packets.c3
-rw-r--r--proto/pipe/config.Y1
-rw-r--r--proto/pipe/pipe.c45
-rw-r--r--proto/pipe/pipe.h1
-rw-r--r--sysdep/unix/timer.h18
13 files changed, 328 insertions, 72 deletions
diff --git a/doc/bird.sgml b/doc/bird.sgml
index 20738be3..6f96b862 100644
--- a/doc/bird.sgml
+++ b/doc/bird.sgml
@@ -431,6 +431,14 @@ to zero to disable it. An empty <cf><m/switch/</cf> is equivalent to <cf/on/
<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>description "<m/text/"</tag> This is an optional
description of the protocol. It is displayed as a part of the
output of 'show route all' command.
@@ -1327,7 +1335,8 @@ for each neighbor using the following configuration parameters:
<tag>route limit <m/number/</tag> The maximal number of routes
that may be imported from the protocol. If the route limit is
- exceeded, the connection is closed with error. Default: no limit.
+ exceeded, the connection is closed with error. Limit is currently implemented as
+ <cf/import limit number exceed restart/. Default: no limit.
<tag>disable after error <m/switch/</tag> When an error is encountered (either
locally or by the other side), disable the instance automatically
diff --git a/nest/config.Y b/nest/config.Y
index f889828a..60b03278 100644
--- a/nest/config.Y
+++ b/nest/config.Y
@@ -44,6 +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(EXCEED, LIMIT, WARN, BLOCK, RESTART, DISABLE)
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)
@@ -64,8 +65,9 @@ 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
+%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
%type <ps> proto_patt proto_patt2
+%type <g> limit_spec
CF_GRAMMAR
@@ -176,6 +178,7 @@ proto_item:
| MRTDUMP mrtdump_mask { this_proto->mrtdump = $2; }
| IMPORT imexport { this_proto->in_filter = $2; }
| EXPORT imexport { this_proto->out_filter = $2; }
+ | IMPORT LIMIT limit_spec { this_proto->in_limit = $3; }
| TABLE rtable { this_proto->table = $2; }
| ROUTER ID idval { this_proto->router_id = $3; }
| DESCRIPTION TEXT { this_proto->dsc = $2; }
@@ -188,6 +191,22 @@ imexport:
| NONE { $$ = FILTER_REJECT; }
;
+limit_action:
+ WARN { $$ = PLA_WARN; }
+ | BLOCK { $$ = PLA_BLOCK; }
+ | RESTART { $$ = PLA_RESTART; }
+ | DISABLE { $$ = PLA_DISABLE; }
+ ;
+
+limit_spec:
+ expr EXCEED limit_action {
+ struct proto_limit *l = cfg_allocz(sizeof(struct proto_limit));
+ l->limit = $1;
+ l->action = $3;
+ $$ = l;
+ }
+ ;
+
rtable:
SYM {
if ($1->class != SYM_TABLE) cf_error("Table name expected");
diff --git a/nest/proto.c b/nest/proto.c
index 802d5238..418a7a61 100644
--- a/nest/proto.c
+++ b/nest/proto.c
@@ -34,11 +34,13 @@ static list flush_proto_list;
static struct proto *initial_device_proto;
static event *proto_flush_event;
+static timer *proto_shutdown_timer;
static char *p_states[] = { "DOWN", "START", "UP", "STOP" };
static char *c_states[] = { "HUNGRY", "FEEDING", "HAPPY", "FLUSHING" };
static void proto_flush_loop(void *);
+static void proto_shutdown_loop(struct timer *);
static void proto_rethink_goal(struct proto *p);
static char *proto_state_name(struct proto *p);
@@ -134,8 +136,6 @@ extern pool *rt_table_pool;
* proto_add_announce_hook - connect protocol to a routing table
* @p: protocol instance
* @t: routing table to connect to
- * @in: input filter
- * @out: output filter
* @stats: per-table protocol statistics
*
* This function creates a connection between the protocol instance @p
@@ -155,8 +155,7 @@ extern pool *rt_table_pool;
* automatically by the core code.
*/
struct announce_hook *
-proto_add_announce_hook(struct proto *p, struct rtable *t, struct filter *in,
- struct filter *out, struct proto_stats *stats)
+proto_add_announce_hook(struct proto *p, struct rtable *t, struct proto_stats *stats)
{
struct announce_hook *h;
@@ -166,8 +165,6 @@ proto_add_announce_hook(struct proto *p, struct rtable *t, struct filter *in,
h = mb_allocz(rt_table_pool, sizeof(struct announce_hook));
h->table = t;
h->proto = p;
- h->in_filter = in;
- h->out_filter = out;
h->stats = stats;
h->next = p->ahooks;
@@ -414,6 +411,8 @@ 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->in_limit = nc->in_limit;
+ // p->main_ahook->out_limit = nc->out_limit;
}
/* Update routes when filters changed. If the protocol in not UP,
@@ -438,6 +437,7 @@ proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config
and we have to do regular protocol restart. */
log(L_INFO "Restarting protocol %s", p->name);
p->disabled = 1;
+ p->down_code = PDC_CF_RESTART;
proto_rethink_goal(p);
p->disabled = 0;
proto_rethink_goal(p);
@@ -512,6 +512,7 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty
log(L_INFO "Disabling protocol %s", p->name);
PD(p, "Restarting");
+ p->down_code = nc->disabled ? PDC_CF_DISABLE : PDC_CF_RESTART;
p->cf_new = nc;
}
else
@@ -519,9 +520,11 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty
if (!shutting_down)
log(L_INFO "Removing protocol %s", p->name);
PD(p, "Unconfigured");
+ p->down_code = PDC_CF_REMOVE;
p->cf_new = NULL;
}
p->reconfiguring = 1;
+
config_add_obstacle(old);
proto_rethink_goal(p);
}
@@ -704,6 +707,8 @@ protos_build(void)
proto_pool = rp_new(&root_pool, "Protocols");
proto_flush_event = ev_new(proto_pool);
proto_flush_event->hook = proto_flush_loop;
+ proto_shutdown_timer = tm_new(proto_pool);
+ proto_shutdown_timer->hook = proto_shutdown_loop;
}
static void
@@ -775,8 +780,13 @@ proto_schedule_feed(struct proto *p, int initial)
/* Connect protocol to routing table */
if (initial && !p->proto->multitable)
- p->main_ahook = proto_add_announce_hook(p, p->table,
- p->cf->in_filter, p->cf->out_filter, &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->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_relink(p);
p->attn->hook = initial ? proto_feed_initial : proto_feed_more;
@@ -861,6 +871,42 @@ proto_schedule_flush(struct proto *p)
}
+static void
+proto_shutdown_loop(struct timer *t UNUSED)
+{
+ struct proto *p, *p_next;
+
+ WALK_LIST_DELSAFE(p, p_next, active_proto_list)
+ if (p->down_sched)
+ {
+ int restart = (p->down_sched == PDS_RESTART);
+
+ p->disabled = 1;
+ proto_rethink_goal(p);
+ if (restart)
+ {
+ p->disabled = 0;
+ proto_rethink_goal(p);
+ }
+ }
+}
+
+static inline void
+proto_schedule_down(struct proto *p, byte restart, byte code)
+{
+ /* Does not work for other states (even PS_START) */
+ ASSERT(p->proto_state == PS_UP);
+
+ /* Scheduled restart may change to shutdown, but not otherwise */
+ if (p->down_sched == PDS_DISABLE)
+ return;
+
+ p->down_sched = restart ? PDS_RESTART : PDS_DISABLE;
+ p->down_code = code;
+ tm_start_max(proto_shutdown_timer, restart ? 2 : 0);
+}
+
+
/**
* proto_request_feeding - request feeding routes to the protocol
* @p: given protocol
@@ -890,6 +936,62 @@ proto_request_feeding(struct proto *p)
proto_schedule_feed(p, 0);
}
+static const char *
+proto_limit_name(struct proto_limit *l)
+{
+ const char *actions[] = {
+ [PLA_WARN] = "warn",
+ [PLA_BLOCK] = "block",
+ [PLA_RESTART] = "restart",
+ [PLA_DISABLE] = "disable",
+ };
+
+ return actions[l->action];
+}
+
+/**
+ * proto_notify_limit: notify about limit hit and take appropriate action
+ * @ah: announce hook
+ * @l: limit being hit
+ *
+ * The function is called by the route processing core when limit @l
+ * is breached. It activates the limit and tooks appropriate action
+ * according to @l->action. It also says what should be done with the
+ * route that breached the limit.
+ *
+ * Returns 1 if the route should be freed, 0 otherwise.
+ */
+int
+proto_notify_limit(struct announce_hook *ah, struct proto_limit *l)
+{
+ struct proto *p = ah->proto;
+ int dir = (ah->in_limit == l);
+
+ if (l->active)
+ return (l->action != PLA_WARN);
+
+ l->active = 1;
+ log(L_WARN "Protocol %s hits route %s limit (%d), action: %s",
+ p->name, dir ? "import" : "export", l->limit, proto_limit_name(l));
+
+ switch (l->action)
+ {
+ case PLA_WARN:
+ return 0;
+
+ case PLA_BLOCK:
+ return 1;
+
+ case PLA_RESTART:
+ case PLA_DISABLE:
+ proto_schedule_down(p, l->action == PLA_RESTART,
+ dir ? PDC_IN_LIMIT_HIT : PDC_OUT_LIMIT_HIT);
+ return 1;
+ }
+
+ return 0;
+}
+
/**
* proto_notify_state - notify core about protocol state change
* @p: protocol the state of which has changed
@@ -919,6 +1021,8 @@ proto_notify_state(struct proto *p, unsigned ps)
switch (ps)
{
case PS_DOWN:
+ p->down_code = 0;
+ p->down_sched = 0;
if ((cs == FS_FEEDING) || (cs == FS_HAPPY))
proto_schedule_flush(p);
@@ -942,6 +1046,7 @@ proto_notify_state(struct proto *p, unsigned ps)
proto_schedule_feed(p, 1);
break;
case PS_STOP:
+ p->down_sched = 0;
if ((cs == FS_FEEDING) || (cs == FS_HAPPY))
proto_schedule_flush(p);
break;
@@ -994,6 +1099,14 @@ proto_show_stats(struct proto_stats *s)
}
void
+proto_show_limit(struct proto_limit *l, const char *dsc)
+{
+ if (l)
+ cli_msg(-1006, " %16s%d, action: %s%s", dsc, l->limit,
+ proto_limit_name(l), l->active ? " [HIT]" : "");
+}
+
+void
proto_show_basic_info(struct proto *p)
{
// cli_msg(-1006, " Table: %s", p->table->name);
@@ -1001,6 +1114,8 @@ 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->in_limit, "Import limit:");
+
if (p->proto_state != PS_DOWN)
proto_show_stats(&p->stats);
}
@@ -1052,6 +1167,7 @@ proto_cmd_disable(struct proto *p, unsigned int arg UNUSED, int cnt UNUSED)
log(L_INFO "Disabling protocol %s", p->name);
p->disabled = 1;
+ p->down_code = PDC_CMD_DISABLE;
proto_rethink_goal(p);
cli_msg(-9, "%s: disabled", p->name);
}
@@ -1082,6 +1198,7 @@ proto_cmd_restart(struct proto *p, unsigned int arg UNUSED, int cnt UNUSED)
log(L_INFO "Restarting protocol %s", p->name);
p->disabled = 1;
+ p->down_code = PDC_CMD_RESTART;
proto_rethink_goal(p);
p->disabled = 0;
proto_rethink_goal(p);
@@ -1105,12 +1222,21 @@ proto_cmd_reload(struct proto *p, unsigned int dir, int cnt UNUSED)
/* re-importing routes */
if (dir != CMD_RELOAD_OUT)
- if (! (p->reload_routes && p->reload_routes(p)))
- {
- cli_msg(-8006, "%s: reload failed", p->name);
- return;
- }
-
+ {
+ if (! (p->reload_routes && p->reload_routes(p)))
+ {
+ cli_msg(-8006, "%s: reload failed", p->name);
+ return;
+ }
+
+ /*
+ * Should be done before reload_routes() hook?
+ * Perhaps, but these hooks work asynchronously.
+ */
+ if (!p->proto->multitable && p->main_ahook->in_limit)
+ p->main_ahook->in_limit->active = 0;
+ }
+
/* re-exporting routes */
if (dir != CMD_RELOAD_IN)
proto_request_feeding(p);
diff --git a/nest/protocol.h b/nest/protocol.h
index 983ce75a..3dd7cf2f 100644
--- a/nest/protocol.h
+++ b/nest/protocol.h
@@ -94,13 +94,15 @@ 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 *in_limit; /* Limit for importing routes from protocol */
+ // struct proto_limit *out_limit; /* Limit for exporting routes to protocol */
/* Check proto_reconfigure() and proto_copy_config() after changing struct proto_config */
/* Protocol-specific data follow... */
};
- /* Protocol statistics */
+/* Protocol statistics */
struct proto_stats {
/* Import - from protocol to core */
u32 imp_routes; /* Number of routes successfully imported to the (adjacent) routing table */
@@ -138,14 +140,16 @@ struct proto {
u32 debug; /* Debugging flags */
u32 mrtdump; /* MRTDump flags */
unsigned preference; /* Default route preference */
- unsigned accept_ra_types; /* Which types of route announcements are accepted (RA_OPTIMAL or RA_ANY) */
- unsigned disabled; /* Manually disabled */
- unsigned proto_state; /* Protocol state machine (see below) */
- unsigned core_state; /* Core state machine (see below) */
- unsigned core_goal; /* State we want to reach (see below) */
- unsigned reconfiguring; /* We're shutting down due to reconfiguration */
- unsigned refeeding; /* We are refeeding (valid only if core_state == FS_FEEDING) */
- unsigned flushing; /* Protocol is flushed in current flush loop round */
+ byte accept_ra_types; /* Which types of route announcements are accepted (RA_OPTIMAL or RA_ANY) */
+ byte disabled; /* Manually disabled */
+ byte proto_state; /* Protocol state machine (PS_*, see below) */
+ byte core_state; /* Core state machine (FS_*, see below) */
+ byte core_goal; /* State we want to reach (FS_*, see below) */
+ byte reconfiguring; /* We're shutting down due to reconfiguration */
+ byte refeeding; /* We are refeeding (valid only if core_state == FS_FEEDING) */
+ byte flushing; /* Protocol is flushed in current flush loop round */
+ byte down_sched; /* Shutdown is scheduled for later (PDS_*) */
+ byte down_code; /* Reason for shutdown (PDC_* codes) */
u32 hash_key; /* Random key used for hashing of neighbors */
bird_clock_t last_state_change; /* Time of last state transition */
char *last_state_name_announced; /* Last state name we've announced to the user */
@@ -210,6 +214,18 @@ struct proto_spec {
};
+#define PDS_DISABLE 1 /* Proto disable scheduled */
+#define PDS_RESTART 2 /* Proto restart scheduled */
+
+#define PDC_CF_REMOVE 0x01 /* Removed in new config */
+#define PDC_CF_DISABLE 0x02 /* Disabled in new config */
+#define PDC_CF_RESTART 0x03 /* Restart due to reconfiguration */
+#define PDC_CMD_DISABLE 0x11 /* Result of disable command */
+#define PDC_CMD_RESTART 0x12 /* Result of restart command */
+#define PDC_IN_LIMIT_HIT 0x21 /* Route import limit reached */
+#define PDC_OUT_LIMIT_HIT 0x22 /* Route export limit reached - not implemented */
+
+
void *proto_new(struct proto_config *, unsigned size);
void *proto_config_new(struct protocol *, unsigned size, int class);
void proto_copy_config(struct proto_config *dest, struct proto_config *src);
@@ -220,6 +236,7 @@ proto_copy_rest(struct proto_config *dest, struct proto_config *src, unsigned si
{ memcpy(dest + 1, src + 1, size - sizeof(struct proto_config)); }
+void proto_show_limit(struct proto_limit *l, const char *dsc);
void proto_show_basic_info(struct proto *p);
void proto_cmd_show(struct proto *, unsigned int, int);
@@ -348,6 +365,24 @@ void proto_notify_state(struct proto *p, unsigned state);
extern struct proto_config *cf_dev_proto;
+
+/*
+ * Protocol limits
+ */
+
+#define PLA_WARN 1 /* Issue log warning */
+#define PLA_BLOCK 2 /* Block new routes */
+#define PLA_RESTART 4 /* Force protocol restart */
+#define PLA_DISABLE 5 /* Shutdown and disable protocol */
+
+struct proto_limit {
+ u32 limit; /* Maximum number of prefixes */
+ byte action; /* Action to take (PLA_*) */
+ byte active; /* Limit is active */
+};
+
+int proto_notify_limit(struct announce_hook *ah, struct proto_limit *l);
+
/*
* Route Announcement Hook
*/
@@ -358,11 +393,13 @@ struct announce_hook {
struct proto *proto;
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_stats *stats; /* Per-table protocol statistics */
struct announce_hook *next; /* Next hook for the same protocol */
};
-struct announce_hook *proto_add_announce_hook(struct proto *, struct rtable *, struct filter *, struct filter *, struct proto_stats *);
+struct announce_hook *proto_add_announce_hook(struct proto *p, struct rtable *t, struct proto_stats *stats);
struct announce_hook *proto_find_announce_hook(struct proto *p, struct rtable *t);
#endif
diff --git a/nest/rt-table.c b/nest/rt-table.c
index 310c1afd..6a28fd43 100644
--- a/nest/rt-table.c
+++ b/nest/rt-table.c
@@ -182,6 +182,16 @@ rte_trace_out(unsigned int flag, struct proto *p, rte *e, char *msg)
rte_trace(p, e, '<', msg);
}
+/**
+ * do_rte_announce - announce new rte to protocol
+ * @ah: pointer to announce hook
+ * @type: announce type (RA_ANY or RA_OPTIMAL)
+ * @net: pointer to announced network
+ * @new: new rte or NULL
+ * @old: previous rte or NULL
+ * @tmpa: new rte attributes (possibly modified by filter)
+ * @refeed: whether we are refeeding protocol
+ */
static inline void
do_rte_announce(struct announce_hook *ah, int type UNUSED, net *net, rte *new, rte *old, ea_list *tmpa, int refeed)
{
@@ -474,6 +484,15 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
return;
}
+ struct proto_limit *l = ah->in_limit;
+ if (l && !old && new && (stats->imp_routes >= l->limit) && proto_notify_limit(ah, l))
+ {
+ stats->imp_updates_ignored++;
+ rte_trace_in(D_FILTERS, p, new, "ignored [limit]");
+ rte_free_quick(new);
+ return;
+ }
+
if (new)
stats->imp_updates_accepted++;
else
diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c
index 4dd4b7be..cf743dff 100644
--- a/proto/bgp/bgp.c
+++ b/proto/bgp/bgp.c
@@ -542,22 +542,6 @@ bgp_active(struct bgp_proto *p)
bgp_start_timer(conn->connect_retry_timer, delay);
}
-int
-bgp_apply_limits(struct bgp_proto *p)
-{
- if (p->cf->route_limit && (p->p.stats.imp_routes > p->cf->route_limit))
- {
- log(L_WARN "%s: Route limit exceeded, shutting down", p->p.name);
- bgp_store_error(p, NULL, BE_AUTO_DOWN, BEA_ROUTE_LIMIT_EXCEEDED);
- bgp_update_startup_delay(p);
- bgp_stop(p, 1); // Errcode 6, 1 - max number of prefixes reached
- return -1;
- }
-
- return 0;
-}
-
-
/**
* bgp_connect - initiate an outgoing connection
* @p: BGP instance
@@ -868,24 +852,46 @@ static int
bgp_shutdown(struct proto *P)
{
struct bgp_proto *p = (struct bgp_proto *) P;
- unsigned subcode;
+ unsigned subcode = 0;
BGP_TRACE(D_EVENTS, "Shutdown requested");
- bgp_store_error(p, NULL, BE_MAN_DOWN, 0);
- if (P->reconfiguring)
+ switch (P->down_code)
{
- if (P->cf_new)
- subcode = 6; // Errcode 6, 6 - other configuration change
+ case PDC_CF_REMOVE:
+ case PDC_CF_DISABLE:
+ subcode = 3; // Errcode 6, 3 - peer de-configured
+ break;
+
+ case PDC_CF_RESTART:
+ subcode = 6; // Errcode 6, 6 - other configuration change
+ break;
+
+ case PDC_CMD_DISABLE:
+ subcode = 2; // Errcode 6, 2 - administrative shutdown
+ break;
+
+ case PDC_CMD_RESTART:
+ subcode = 4; // Errcode 6, 4 - administrative reset
+ break;
+
+ case PDC_IN_LIMIT_HIT:
+ subcode = 1; // Errcode 6, 1 - max number of prefixes reached
+ log(L_WARN "%s: Route limit exceeded, shutting down", p->p.name);
+
+ bgp_store_error(p, NULL, BE_AUTO_DOWN, BEA_ROUTE_LIMIT_EXCEEDED);
+ if (P->cf->in_limit->action == PLA_RESTART)
+ bgp_update_startup_delay(p);
else
- subcode = 3; // Errcode 6, 3 - peer de-configured
+ p->startup_delay = 0;
+ goto done;
}
- else
- subcode = 2; // Errcode 6, 2 - administrative shutdown
+ bgp_store_error(p, NULL, BE_MAN_DOWN, 0);
p->startup_delay = 0;
- bgp_stop(p, subcode);
+ done:
+ bgp_stop(p, subcode);
return p->p.proto_state;
}
@@ -969,6 +975,10 @@ bgp_check_config(struct bgp_config *c)
/* Different default for gw_mode */
if (!c->gw_mode)
c->gw_mode = (c->multihop || internal) ? GW_RECURSIVE : GW_DIRECT;
+
+ /* Disable after error incompatible with restart limit action */
+ if (c->c.in_limit && (c->c.in_limit->action == PLA_RESTART) && c->disable_after_error)
+ c->c.in_limit->action = PLA_DISABLE;
}
static int
@@ -1116,9 +1126,6 @@ bgp_get_status(struct proto *P, byte *buf)
bsprintf(buf, "%-14s%s%s", bgp_state_dsc(p), err1, err2);
}
-static inline bird_clock_t tm_remains(timer *t)
-{ return t->expires ? t->expires - now : 0; }
-
static void
bgp_show_proto_info(struct proto *P)
{
diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h
index a8c5818e..aa2db4b0 100644
--- a/proto/bgp/bgp.h
+++ b/proto/bgp/bgp.h
@@ -152,7 +152,6 @@ void bgp_conn_enter_established_state(struct bgp_conn *conn);
void bgp_conn_enter_close_state(struct bgp_conn *conn);
void bgp_conn_enter_idle_state(struct bgp_conn *conn);
void bgp_store_error(struct bgp_proto *p, struct bgp_conn *c, u8 class, u32 code);
-int bgp_apply_limits(struct bgp_proto *p);
void bgp_stop(struct bgp_proto *p, unsigned subcode);
diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y
index 78ca52de..5feaea0d 100644
--- a/proto/bgp/config.Y
+++ b/proto/bgp/config.Y
@@ -98,7 +98,11 @@ bgp_proto:
| bgp_proto CAPABILITIES bool ';' { BGP_CFG->capabilities = $3; }
| bgp_proto ADVERTISE IPV4 bool ';' { BGP_CFG->advertise_ipv4 = $4; }
| bgp_proto PASSWORD TEXT ';' { BGP_CFG->password = $3; }
- | bgp_proto ROUTE LIMIT expr ';' { BGP_CFG->route_limit = $4; }
+ | bgp_proto ROUTE LIMIT expr ';' {
+ this_proto->in_limit = cfg_allocz(sizeof(struct proto_limit));
+ this_proto->in_limit->limit = $4;
+ this_proto->in_limit->action = PLA_RESTART;
+ }
| bgp_proto PASSIVE bool ';' { BGP_CFG->passive = $3; }
| bgp_proto INTERPRET COMMUNITIES bool ';' { BGP_CFG->interpret_communities = $4; }
| bgp_proto IGP TABLE rtable ';' { BGP_CFG->igp_table = $4; }
diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c
index d3e9b6a1..168025d0 100644
--- a/proto/bgp/packets.c
+++ b/proto/bgp/packets.c
@@ -915,9 +915,6 @@ bgp_do_rx_update(struct bgp_conn *conn,
if (n = net_find(p->p.table, prefix, pxlen))
rte_update(p->p.table, n, &p->p, &p->p, NULL);
}
-
- if (bgp_apply_limits(p) < 0)
- goto done;
}
done:
diff --git a/proto/pipe/config.Y b/proto/pipe/config.Y
index 40637558..4fb2b499 100644
--- a/proto/pipe/config.Y
+++ b/proto/pipe/config.Y
@@ -36,6 +36,7 @@ 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 36b06d43..a5fcc6f6 100644
--- a/proto/pipe/pipe.c
+++ b/proto/pipe/pipe.c
@@ -24,9 +24,10 @@
* rte_update(), an import filter in ahook 2 is called. When a new
* route is announced in the peer table, an export filter in ahook2
* and an import filter in ahook 1 are used. Oviously, there is no
- * need in filtering the same route twice, so both import filters
- * are set to accept, while user configured 'import' and 'export'
- * filters are used as export filters in ahooks 2 and 1.
+ * need in filtering the same route twice, so both import filters are
+ * set to accept, while user configured 'import' and 'export' filters
+ * are used as export filters in ahooks 2 and 1. Route limits are
+ * handled similarly, but on the import side of ahooks.
*/
#undef LOCAL_DEBUG
@@ -116,6 +117,8 @@ pipe_import_control(struct proto *P, rte **ee, ea_list **ea UNUSED, struct linpo
static int
pipe_reload_routes(struct proto *P)
{
+ struct pipe_proto *p = (struct pipe_proto *) P;
+
/*
* Because the pipe protocol feeds routes from both routing tables
* together, both directions are reloaded during refeed and 'reload
@@ -123,6 +126,12 @@ pipe_reload_routes(struct proto *P)
* request refeed when 'reload in' command is used.
*/
proto_request_feeding(P);
+
+ if (P->main_ahook->in_limit)
+ P->main_ahook->in_limit->active = 0;
+ if (p->peer_ahook->in_limit)
+ p->peer_ahook->in_limit->active = 0;
+
return 1;
}
@@ -146,6 +155,7 @@ pipe_init(struct proto_config *C)
static int
pipe_start(struct proto *P)
{
+ struct pipe_config *cf = (struct pipe_config *) P->cf;
struct pipe_proto *p = (struct pipe_proto *) P;
/* Lock both tables, unlock is handled in pipe_cleanup() */
@@ -155,10 +165,13 @@ pipe_start(struct proto *P)
/* Going directly to PS_UP - prepare for feeding,
connect the protocol to both routing tables */
- P->main_ahook = proto_add_announce_hook(P, P->table,
- FILTER_ACCEPT, P->cf->out_filter, &P->stats);
- p->peer_ahook = proto_add_announce_hook(P, p->peer_table,
- FILTER_ACCEPT, P->cf->in_filter, &p->peer_stats);
+ P->main_ahook = proto_add_announce_hook(P, P->table, &P->stats);
+ P->main_ahook->out_filter = cf->c.out_filter;
+ P->main_ahook->in_limit = cf->c.in_limit;
+
+ 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;
return PS_UP;
}
@@ -204,10 +217,16 @@ pipe_reconfigure(struct proto *P, struct proto_config *new)
/* Update output filters in ahooks */
if (P->main_ahook)
- P->main_ahook->out_filter = new->out_filter;
+ {
+ P->main_ahook->out_filter = new->out_filter;
+ P->main_ahook->in_limit = new->in_limit;
+ }
if (p->peer_ahook)
- p->peer_ahook->out_filter = new->in_filter;
+ {
+ p->peer_ahook->out_filter = new->in_filter;
+ p->peer_ahook->in_limit = nc->out_limit;
+ }
if ((P->proto_state != PS_UP) || (proto_reconfig_type == RECONFIG_SOFT))
return 1;
@@ -283,12 +302,16 @@ static void
pipe_show_proto_info(struct proto *P)
{
struct pipe_proto *p = (struct pipe_proto *) P;
+ struct pipe_config *cf = (struct pipe_config *) P->cf;
// cli_msg(-1006, " Table: %s", P->table->name);
// cli_msg(-1006, " Peer table: %s", p->peer_table->name);
cli_msg(-1006, " Preference: %d", P->preference);
- 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, " Input filter: %s", filter_name(cf->c.in_filter));
+ 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:");
if (P->proto_state != PS_DOWN)
pipe_show_stats(p);
diff --git a/proto/pipe/pipe.h b/proto/pipe/pipe.h
index 50b31698..e777fb41 100644
--- a/proto/pipe/pipe.h
+++ b/proto/pipe/pipe.h
@@ -15,6 +15,7 @@
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 */
};
diff --git a/sysdep/unix/timer.h b/sysdep/unix/timer.h
index a20df483..a788ae27 100644
--- a/sysdep/unix/timer.h
+++ b/sysdep/unix/timer.h
@@ -30,6 +30,22 @@ void tm_start(timer *, unsigned after);
void tm_stop(timer *);
void tm_dump_all(void);
+extern bird_clock_t now; /* Relative, monotonic time in seconds */
+extern bird_clock_t now_real; /* Time in seconds since fixed known epoch */
+
+static inline bird_clock_t
+tm_remains(timer *t)
+{
+ return t->expires ? t->expires - now : 0;
+}
+
+static inline void
+tm_start_max(timer *t, unsigned after)
+{
+ bird_clock_t rem = tm_remains(t);
+ tm_start(t, (rem > after) ? rem : after);
+}
+
static inline timer *
tm_new_set(pool *p, void (*hook)(struct timer *), void *data, unsigned rand, unsigned rec)
{
@@ -41,8 +57,6 @@ tm_new_set(pool *p, void (*hook)(struct timer *), void *data, unsigned rand, uns
return t;
}
-extern bird_clock_t now; /* Relative, monotonic time in seconds */
-extern bird_clock_t now_real; /* Time in seconds since fixed known epoch */
struct timeformat {
char *fmt1, *fmt2;