mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-12-22 09:41:54 +00:00
Implements generalized import hooks.
Thanks to Alexander V. Chernikov for the original patch.
This commit is contained in:
parent
ae8b300164
commit
ebecb6f6a1
@ -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
|
||||
|
@ -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");
|
||||
|
152
nest/proto.c
152
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;
|
||||
@ -993,6 +1098,14 @@ proto_show_stats(struct proto_stats *s)
|
||||
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
|
||||
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, " 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,11 +1222,20 @@ 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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
||||
|
||||
|
@ -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; }
|
||||
|
@ -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:
|
||||
|
@ -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; }
|
||||
;
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user