summaryrefslogtreecommitdiff
path: root/nest/proto.c
diff options
context:
space:
mode:
Diffstat (limited to 'nest/proto.c')
-rw-r--r--nest/proto.c175
1 files changed, 158 insertions, 17 deletions
diff --git a/nest/proto.c b/nest/proto.c
index 802d5238..887d3e5e 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;
@@ -409,11 +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;
}
/* Update routes when filters changed. If the protocol in not UP,
@@ -438,6 +438,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 +513,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 +521,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 +708,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
@@ -769,14 +775,28 @@ 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)
- 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_reset_limit(p->main_ahook->in_limit);
+ proto_reset_limit(p->main_ahook->out_limit);
+ }
proto_relink(p);
p->attn->hook = initial ? proto_feed_initial : proto_feed_more;
@@ -860,6 +880,44 @@ 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)
+{
+ struct proto *p, *p_next;
+
+ WALK_LIST_DELSAFE(p, p_next, active_proto_list)
+ if (p->down_sched)
+ {
+ proto_restart = (p->down_sched == PDS_RESTART);
+
+ p->disabled = 1;
+ proto_rethink_goal(p);
+ if (proto_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
@@ -890,6 +948,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
+ * @rt_count: the number of routes
+ *
+ * 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.
+ */
+void
+proto_notify_limit(struct announce_hook *ah, struct proto_limit *l, u32 rt_count)
+{
+ struct proto *p = ah->proto;
+ int dir = (ah->in_limit == l);
+
+ if (l->state == PLS_BLOCKED)
+ return;
+
+ /* 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));
+
+ switch (l->action)
+ {
+ case PLA_WARN:
+ l->state = PLS_ACTIVE;
+ break;
+
+ case PLA_BLOCK:
+ l->state = PLS_BLOCKED;
+ break;
+
+ 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);
+ break;
+ }
+}
+
/**
* proto_notify_state - notify core about protocol state change
* @p: protocol the state of which has changed
@@ -919,6 +1033,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 +1058,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 +1111,16 @@ proto_show_stats(struct proto_stats *s)
}
void
+proto_show_limit(struct proto_limit *l, const char *dsc)
+{
+ if (!l)
+ return;
+
+ cli_msg(-1006, " %-16s%d%s", dsc, l->limit, l->state ? " [HIT]" : "");
+ cli_msg(-1006, " Action: %s", proto_limit_name(l));
+}
+
+void
proto_show_basic_info(struct proto *p)
{
// cli_msg(-1006, " Table: %s", p->table->name);
@@ -1001,6 +1128,9 @@ 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:");
+ proto_show_limit(p->cf->out_limit, "Export limit:");
+
if (p->proto_state != PS_DOWN)
proto_show_stats(&p->stats);
}
@@ -1052,6 +1182,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 +1213,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 +1237,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)
+ proto_reset_limit(p->main_ahook->in_limit);
+ }
+
/* re-exporting routes */
if (dir != CMD_RELOAD_IN)
proto_request_feeding(p);