Implements generalized import hooks.

Thanks to Alexander V. Chernikov for the original patch.
This commit is contained in:
Ondrej Zajicek 2012-04-15 15:28:29 +02:00
parent ae8b300164
commit ebecb6f6a1
13 changed files with 328 additions and 72 deletions

View file

@ -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 <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/. 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 <tag>description "<m/text/"</tag> This is an optional
description of the protocol. It is displayed as a part of the description of the protocol. It is displayed as a part of the
output of 'show route all' command. 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 <tag>route limit <m/number/</tag> The maximal number of routes
that may be imported from the protocol. If the route limit is 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 <tag>disable after error <m/switch/</tag> When an error is encountered (either
locally or by the other side), disable the instance automatically locally or by the other side), disable the instance automatically

View file

@ -44,6 +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(EXCEED, LIMIT, WARN, BLOCK, RESTART, DISABLE)
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) 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 <ro> roa_args
%type <rot> roa_table_arg %type <rot> roa_table_arg
%type <sd> sym_args %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 <ps> proto_patt proto_patt2
%type <g> limit_spec
CF_GRAMMAR CF_GRAMMAR
@ -176,6 +178,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; }
| IMPORT LIMIT limit_spec { this_proto->in_limit = $3; }
| TABLE rtable { this_proto->table = $2; } | TABLE rtable { this_proto->table = $2; }
| ROUTER ID idval { this_proto->router_id = $3; } | ROUTER ID idval { this_proto->router_id = $3; }
| DESCRIPTION TEXT { this_proto->dsc = $2; } | DESCRIPTION TEXT { this_proto->dsc = $2; }
@ -188,6 +191,22 @@ imexport:
| NONE { $$ = FILTER_REJECT; } | 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: rtable:
SYM { SYM {
if ($1->class != SYM_TABLE) cf_error("Table name expected"); if ($1->class != SYM_TABLE) cf_error("Table name expected");

View file

@ -34,11 +34,13 @@ static list flush_proto_list;
static struct proto *initial_device_proto; static struct proto *initial_device_proto;
static event *proto_flush_event; static event *proto_flush_event;
static timer *proto_shutdown_timer;
static char *p_states[] = { "DOWN", "START", "UP", "STOP" }; static char *p_states[] = { "DOWN", "START", "UP", "STOP" };
static char *c_states[] = { "HUNGRY", "FEEDING", "HAPPY", "FLUSHING" }; static char *c_states[] = { "HUNGRY", "FEEDING", "HAPPY", "FLUSHING" };
static void proto_flush_loop(void *); static void proto_flush_loop(void *);
static void proto_shutdown_loop(struct timer *);
static void proto_rethink_goal(struct proto *p); static void proto_rethink_goal(struct proto *p);
static char *proto_state_name(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 * proto_add_announce_hook - connect protocol to a routing table
* @p: protocol instance * @p: protocol instance
* @t: routing table to connect to * @t: routing table to connect to
* @in: input filter
* @out: output filter
* @stats: per-table protocol statistics * @stats: per-table protocol statistics
* *
* This function creates a connection between the protocol instance @p * This function creates a connection between the protocol instance @p
@ -155,8 +155,7 @@ extern pool *rt_table_pool;
* automatically by the core code. * automatically by the core code.
*/ */
struct announce_hook * struct announce_hook *
proto_add_announce_hook(struct proto *p, struct rtable *t, struct filter *in, proto_add_announce_hook(struct proto *p, struct rtable *t, struct proto_stats *stats)
struct filter *out, struct proto_stats *stats)
{ {
struct announce_hook *h; 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 = mb_allocz(rt_table_pool, sizeof(struct announce_hook));
h->table = t; h->table = t;
h->proto = p; h->proto = p;
h->in_filter = in;
h->out_filter = out;
h->stats = stats; h->stats = stats;
h->next = p->ahooks; 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->in_filter = nc->in_filter;
p->main_ahook->out_filter = nc->out_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, /* 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. */ and we have to do regular protocol restart. */
log(L_INFO "Restarting protocol %s", p->name); log(L_INFO "Restarting protocol %s", p->name);
p->disabled = 1; p->disabled = 1;
p->down_code = PDC_CF_RESTART;
proto_rethink_goal(p); proto_rethink_goal(p);
p->disabled = 0; p->disabled = 0;
proto_rethink_goal(p); 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); log(L_INFO "Disabling protocol %s", p->name);
PD(p, "Restarting"); PD(p, "Restarting");
p->down_code = nc->disabled ? PDC_CF_DISABLE : PDC_CF_RESTART;
p->cf_new = nc; p->cf_new = nc;
} }
else else
@ -519,9 +520,11 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty
if (!shutting_down) if (!shutting_down)
log(L_INFO "Removing protocol %s", p->name); log(L_INFO "Removing protocol %s", p->name);
PD(p, "Unconfigured"); PD(p, "Unconfigured");
p->down_code = PDC_CF_REMOVE;
p->cf_new = NULL; p->cf_new = NULL;
} }
p->reconfiguring = 1; p->reconfiguring = 1;
config_add_obstacle(old); config_add_obstacle(old);
proto_rethink_goal(p); proto_rethink_goal(p);
} }
@ -704,6 +707,8 @@ protos_build(void)
proto_pool = rp_new(&root_pool, "Protocols"); proto_pool = rp_new(&root_pool, "Protocols");
proto_flush_event = ev_new(proto_pool); proto_flush_event = ev_new(proto_pool);
proto_flush_event->hook = proto_flush_loop; proto_flush_event->hook = proto_flush_loop;
proto_shutdown_timer = tm_new(proto_pool);
proto_shutdown_timer->hook = proto_shutdown_loop;
} }
static void static void
@ -775,8 +780,13 @@ proto_schedule_feed(struct proto *p, int initial)
/* Connect protocol to routing table */ /* Connect protocol to routing table */
if (initial && !p->proto->multitable) 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); proto_relink(p);
p->attn->hook = initial ? proto_feed_initial : proto_feed_more; 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 * proto_request_feeding - request feeding routes to the protocol
* @p: given protocol * @p: given protocol
@ -890,6 +936,62 @@ proto_request_feeding(struct proto *p)
proto_schedule_feed(p, 0); 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 * proto_notify_state - notify core about protocol state change
* @p: protocol the state of which has changed * @p: protocol the state of which has changed
@ -919,6 +1021,8 @@ proto_notify_state(struct proto *p, unsigned ps)
switch (ps) switch (ps)
{ {
case PS_DOWN: case PS_DOWN:
p->down_code = 0;
p->down_sched = 0;
if ((cs == FS_FEEDING) || (cs == FS_HAPPY)) if ((cs == FS_FEEDING) || (cs == FS_HAPPY))
proto_schedule_flush(p); proto_schedule_flush(p);
@ -942,6 +1046,7 @@ proto_notify_state(struct proto *p, unsigned ps)
proto_schedule_feed(p, 1); proto_schedule_feed(p, 1);
break; break;
case PS_STOP: case PS_STOP:
p->down_sched = 0;
if ((cs == FS_FEEDING) || (cs == FS_HAPPY)) if ((cs == FS_FEEDING) || (cs == FS_HAPPY))
proto_schedule_flush(p); proto_schedule_flush(p);
break; break;
@ -993,6 +1098,14 @@ proto_show_stats(struct proto_stats *s)
s->exp_withdraws_received, s->exp_withdraws_accepted); s->exp_withdraws_received, s->exp_withdraws_accepted);
} }
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 void
proto_show_basic_info(struct proto *p) proto_show_basic_info(struct proto *p)
{ {
@ -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, " 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->in_limit, "Import limit:");
if (p->proto_state != PS_DOWN) if (p->proto_state != PS_DOWN)
proto_show_stats(&p->stats); 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); log(L_INFO "Disabling protocol %s", p->name);
p->disabled = 1; p->disabled = 1;
p->down_code = PDC_CMD_DISABLE;
proto_rethink_goal(p); proto_rethink_goal(p);
cli_msg(-9, "%s: disabled", p->name); 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); log(L_INFO "Restarting protocol %s", p->name);
p->disabled = 1; p->disabled = 1;
p->down_code = PDC_CMD_RESTART;
proto_rethink_goal(p); proto_rethink_goal(p);
p->disabled = 0; p->disabled = 0;
proto_rethink_goal(p); proto_rethink_goal(p);
@ -1105,11 +1222,20 @@ proto_cmd_reload(struct proto *p, unsigned int dir, int cnt UNUSED)
/* re-importing routes */ /* re-importing routes */
if (dir != CMD_RELOAD_OUT) if (dir != CMD_RELOAD_OUT)
if (! (p->reload_routes && p->reload_routes(p))) {
{ if (! (p->reload_routes && p->reload_routes(p)))
cli_msg(-8006, "%s: reload failed", p->name); {
return; 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 */ /* re-exporting routes */
if (dir != CMD_RELOAD_IN) if (dir != CMD_RELOAD_IN)

View file

@ -94,13 +94,15 @@ 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 *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 */ /* Check proto_reconfigure() and proto_copy_config() after changing struct proto_config */
/* Protocol-specific data follow... */ /* Protocol-specific data follow... */
}; };
/* Protocol statistics */ /* Protocol statistics */
struct proto_stats { struct proto_stats {
/* Import - from protocol to core */ /* Import - from protocol to core */
u32 imp_routes; /* Number of routes successfully imported to the (adjacent) routing table */ u32 imp_routes; /* Number of routes successfully imported to the (adjacent) routing table */
@ -138,14 +140,16 @@ struct proto {
u32 debug; /* Debugging flags */ u32 debug; /* Debugging flags */
u32 mrtdump; /* MRTDump flags */ u32 mrtdump; /* MRTDump flags */
unsigned preference; /* Default route preference */ unsigned preference; /* Default route preference */
unsigned accept_ra_types; /* Which types of route announcements are accepted (RA_OPTIMAL or RA_ANY) */ byte accept_ra_types; /* Which types of route announcements are accepted (RA_OPTIMAL or RA_ANY) */
unsigned disabled; /* Manually disabled */ byte disabled; /* Manually disabled */
unsigned proto_state; /* Protocol state machine (see below) */ byte proto_state; /* Protocol state machine (PS_*, see below) */
unsigned core_state; /* Core state machine (see below) */ byte core_state; /* Core state machine (FS_*, see below) */
unsigned core_goal; /* State we want to reach (see below) */ byte core_goal; /* State we want to reach (FS_*, see below) */
unsigned reconfiguring; /* We're shutting down due to reconfiguration */ byte reconfiguring; /* We're shutting down due to reconfiguration */
unsigned refeeding; /* We are refeeding (valid only if core_state == FS_FEEDING) */ byte refeeding; /* We are refeeding (valid only if core_state == FS_FEEDING) */
unsigned flushing; /* Protocol is flushed in current flush loop round */ 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 */ u32 hash_key; /* Random key used for hashing of neighbors */
bird_clock_t last_state_change; /* Time of last state transition */ 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 */ 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_new(struct proto_config *, unsigned size);
void *proto_config_new(struct protocol *, unsigned size, int class); void *proto_config_new(struct protocol *, unsigned size, int class);
void proto_copy_config(struct proto_config *dest, struct proto_config *src); 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)); } { 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_show_basic_info(struct proto *p);
void proto_cmd_show(struct proto *, unsigned int, int); 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; 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 * Route Announcement Hook
*/ */
@ -358,11 +393,13 @@ 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 *in_limit; /* Input limit */
// struct proto_limit *out_limit; /* Output limit */
struct proto_stats *stats; /* Per-table protocol statistics */ struct proto_stats *stats; /* Per-table protocol statistics */
struct announce_hook *next; /* Next hook for the same protocol */ 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); struct announce_hook *proto_find_announce_hook(struct proto *p, struct rtable *t);
#endif #endif

View file

@ -182,6 +182,16 @@ rte_trace_out(unsigned int flag, struct proto *p, rte *e, char *msg)
rte_trace(p, e, '<', 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 static inline void
do_rte_announce(struct announce_hook *ah, int type UNUSED, net *net, rte *new, rte *old, ea_list *tmpa, int refeed) 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; 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) if (new)
stats->imp_updates_accepted++; stats->imp_updates_accepted++;
else else

View file

@ -542,22 +542,6 @@ bgp_active(struct bgp_proto *p)
bgp_start_timer(conn->connect_retry_timer, delay); 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 * bgp_connect - initiate an outgoing connection
* @p: BGP instance * @p: BGP instance
@ -868,24 +852,46 @@ static int
bgp_shutdown(struct proto *P) bgp_shutdown(struct proto *P)
{ {
struct bgp_proto *p = (struct bgp_proto *) P; struct bgp_proto *p = (struct bgp_proto *) P;
unsigned subcode; unsigned subcode = 0;
BGP_TRACE(D_EVENTS, "Shutdown requested"); 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) case PDC_CF_REMOVE:
subcode = 6; // Errcode 6, 6 - other configuration change 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 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; p->startup_delay = 0;
bgp_stop(p, subcode);
done:
bgp_stop(p, subcode);
return p->p.proto_state; return p->p.proto_state;
} }
@ -969,6 +975,10 @@ bgp_check_config(struct bgp_config *c)
/* Different default for gw_mode */ /* Different default for gw_mode */
if (!c->gw_mode) if (!c->gw_mode)
c->gw_mode = (c->multihop || internal) ? GW_RECURSIVE : GW_DIRECT; 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 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); 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 static void
bgp_show_proto_info(struct proto *P) bgp_show_proto_info(struct proto *P)
{ {

View file

@ -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_close_state(struct bgp_conn *conn);
void bgp_conn_enter_idle_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); 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); void bgp_stop(struct bgp_proto *p, unsigned subcode);

View file

@ -98,7 +98,11 @@ bgp_proto:
| bgp_proto CAPABILITIES bool ';' { BGP_CFG->capabilities = $3; } | bgp_proto CAPABILITIES bool ';' { BGP_CFG->capabilities = $3; }
| bgp_proto ADVERTISE IPV4 bool ';' { BGP_CFG->advertise_ipv4 = $4; } | bgp_proto ADVERTISE IPV4 bool ';' { BGP_CFG->advertise_ipv4 = $4; }
| bgp_proto PASSWORD TEXT ';' { BGP_CFG->password = $3; } | 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 PASSIVE bool ';' { BGP_CFG->passive = $3; }
| bgp_proto INTERPRET COMMUNITIES bool ';' { BGP_CFG->interpret_communities = $4; } | bgp_proto INTERPRET COMMUNITIES bool ';' { BGP_CFG->interpret_communities = $4; }
| bgp_proto IGP TABLE rtable ';' { BGP_CFG->igp_table = $4; } | bgp_proto IGP TABLE rtable ';' { BGP_CFG->igp_table = $4; }

View file

@ -915,9 +915,6 @@ bgp_do_rx_update(struct bgp_conn *conn,
if (n = net_find(p->p.table, prefix, pxlen)) if (n = net_find(p->p.table, prefix, pxlen))
rte_update(p->p.table, n, &p->p, &p->p, NULL); rte_update(p->p.table, n, &p->p, &p->p, NULL);
} }
if (bgp_apply_limits(p) < 0)
goto done;
} }
done: done:

View file

@ -36,6 +36,7 @@ pipe_proto:
cf_error("Routing table name expected"); cf_error("Routing table name expected");
PIPE_CFG->peer = $4->def; 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 OPAQUE ';' { PIPE_CFG->mode = PIPE_OPAQUE; }
| pipe_proto MODE TRANSPARENT ';' { PIPE_CFG->mode = PIPE_TRANSPARENT; } | pipe_proto MODE TRANSPARENT ';' { PIPE_CFG->mode = PIPE_TRANSPARENT; }
; ;

View file

@ -24,9 +24,10 @@
* rte_update(), an import filter in ahook 2 is called. When a new * 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 * 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 * and an import filter in ahook 1 are used. Oviously, there is no
* need in filtering the same route twice, so both import filters * need in filtering the same route twice, so both import filters are
* are set to accept, while user configured 'import' and 'export' * set to accept, while user configured 'import' and 'export' filters
* filters are used as export filters in ahooks 2 and 1. * 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 #undef LOCAL_DEBUG
@ -116,6 +117,8 @@ pipe_import_control(struct proto *P, rte **ee, ea_list **ea UNUSED, struct linpo
static int static int
pipe_reload_routes(struct proto *P) pipe_reload_routes(struct proto *P)
{ {
struct pipe_proto *p = (struct pipe_proto *) P;
/* /*
* Because the pipe protocol feeds routes from both routing tables * Because the pipe protocol feeds routes from both routing tables
* together, both directions are reloaded during refeed and 'reload * 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. * request refeed when 'reload in' command is used.
*/ */
proto_request_feeding(P); 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; return 1;
} }
@ -146,6 +155,7 @@ pipe_init(struct proto_config *C)
static int static int
pipe_start(struct proto *P) pipe_start(struct proto *P)
{ {
struct pipe_config *cf = (struct pipe_config *) P->cf;
struct pipe_proto *p = (struct pipe_proto *) P; struct pipe_proto *p = (struct pipe_proto *) P;
/* Lock both tables, unlock is handled in pipe_cleanup() */ /* 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, /* Going directly to PS_UP - prepare for feeding,
connect the protocol to both routing tables */ connect the protocol to both routing tables */
P->main_ahook = proto_add_announce_hook(P, P->table, P->main_ahook = proto_add_announce_hook(P, P->table, &P->stats);
FILTER_ACCEPT, P->cf->out_filter, &P->stats); P->main_ahook->out_filter = cf->c.out_filter;
p->peer_ahook = proto_add_announce_hook(P, p->peer_table, P->main_ahook->in_limit = cf->c.in_limit;
FILTER_ACCEPT, P->cf->in_filter, &p->peer_stats);
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; return PS_UP;
} }
@ -204,10 +217,16 @@ pipe_reconfigure(struct proto *P, struct proto_config *new)
/* Update output filters in ahooks */ /* Update output filters in ahooks */
if (P->main_ahook) 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) 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)) if ((P->proto_state != PS_UP) || (proto_reconfig_type == RECONFIG_SOFT))
return 1; return 1;
@ -283,12 +302,16 @@ static void
pipe_show_proto_info(struct proto *P) pipe_show_proto_info(struct proto *P)
{ {
struct pipe_proto *p = (struct pipe_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, " Table: %s", P->table->name);
// cli_msg(-1006, " Peer table: %s", p->peer_table->name); // cli_msg(-1006, " Peer table: %s", p->peer_table->name);
cli_msg(-1006, " Preference: %d", P->preference); cli_msg(-1006, " Preference: %d", P->preference);
cli_msg(-1006, " Input filter: %s", filter_name(P->cf->in_filter)); cli_msg(-1006, " Input filter: %s", filter_name(cf->c.in_filter));
cli_msg(-1006, " Output filter: %s", filter_name(P->cf->out_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) if (P->proto_state != PS_DOWN)
pipe_show_stats(p); pipe_show_stats(p);

View file

@ -15,6 +15,7 @@
struct pipe_config { struct pipe_config {
struct proto_config c; struct proto_config c;
struct rtable_config *peer; /* Table we're connected to */ struct rtable_config *peer; /* Table we're connected to */
struct proto_limit *out_limit; /* Export route limit */
int mode; /* PIPE_OPAQUE or PIPE_TRANSPARENT */ int mode; /* PIPE_OPAQUE or PIPE_TRANSPARENT */
}; };

View file

@ -30,6 +30,22 @@ void tm_start(timer *, unsigned after);
void tm_stop(timer *); void tm_stop(timer *);
void tm_dump_all(void); 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 * static inline timer *
tm_new_set(pool *p, void (*hook)(struct timer *), void *data, unsigned rand, unsigned rec) 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; 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 { struct timeformat {
char *fmt1, *fmt2; char *fmt1, *fmt2;