[PATCH 1/1] * Implement general protocol limititng, v2
--- doc/bird.sgml | 17 +++++- nest/config.Y | 26 ++++++++- nest/proto-hooks.c | 11 ++++ nest/proto.c | 166 +++++++++++++++++++++++++++++++++++++++++++++++++-- nest/protocol.h | 40 ++++++++++++- nest/rt-table.c | 84 ++++++++++++++++++++++++-- proto/bgp/bgp.c | 33 +++++++--- proto/bgp/bgp.h | 1 - proto/bgp/config.Y | 9 +++- proto/bgp/packets.c | 3 - 10 files changed, 361 insertions(+), 29 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 7f53f02..ef9901d 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -415,6 +415,20 @@ 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 limit action to be taken when import routes limit is hit. Warn action + does nothing more than printing error log message. Block action ignores + new routes (and converts route updates to withdraws) coming from protocol. + Restart action shuts protocol down (and core immediately restarts it). + Disable action takes protocol down and restrict automatic protocol restart + until protocol is explicitly enabled from CLI. + Default: <cf/none/. + + <tag>export limit <m/number/ exceed warn | block | disable</tag> + Specify limit action to be taken when export routes limit is hit. + Actions are the same as in <cf>import</cf> keyword except <cf/restart/. + 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. @@ -1271,7 +1285,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 a6baf4e..aa08661 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -43,6 +43,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) CF_KEYWORDS(LISTEN, BGP, V6ONLY, DUAL, ADDRESS, PORT, PASSWORDS, DESCRIPTION) @@ -59,7 +60,7 @@ CF_ENUM(T_ENUM_RTD, RTD_, ROUTER, DEVICE, BLACKHOLE, UNREACHABLE, PROHIBIT, MULT %type <r> rtable %type <s> optsym %type <ra> r_args -%type <i> proto_start echo_mask echo_size debug_mask debug_list debug_flag mrtdump_mask mrtdump_list mrtdump_flag export_or_preexport +%type <i> proto_start echo_mask echo_size debug_mask debug_list debug_flag mrtdump_mask mrtdump_list mrtdump_flag export_or_preexport limit_action %type <ps> proto_patt proto_patt2 CF_GRAMMAR @@ -153,6 +154,22 @@ 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 expr EXCEED limit_action { + if (!this_proto->in_limit) + this_proto->in_limit = cfg_allocz(sizeof(struct proto_limit)); + this_proto->in_limit->direction = PL_IMPORT; + this_proto->in_limit->limit = $3; + this_proto->in_limit->action = $5; + } + | EXPORT LIMIT expr EXCEED limit_action { + if (!this_proto->out_limit) + this_proto->out_limit = cfg_allocz(sizeof(struct proto_limit)); + this_proto->out_limit->direction = PL_EXPORT; + this_proto->out_limit->limit = $3; + this_proto->out_limit->action = $5; + if ($5 == PL_ACTION_RESTART) + cf_error("RESTART action can't be used in export limiter. Use DISABLE action instead"); + } | TABLE rtable { this_proto->table = $2; } | ROUTER ID idval { this_proto->router_id = $3; } | DESCRIPTION TEXT { this_proto->dsc = $2; } @@ -165,6 +182,13 @@ imexport: | NONE { $$ = FILTER_REJECT; } ; +limit_action: + WARN { $$ = PL_ACTION_WARN; } + | BLOCK { $$ = PL_ACTION_BLOCK; } + | RESTART { $$ = PL_ACTION_RESTART; } + | DISABLE { $$ = PL_ACTION_DISABLE; } + ; + rtable: SYM { if ($1->class != SYM_TABLE) cf_error("Table name expected"); diff --git a/nest/proto-hooks.c b/nest/proto-hooks.c index f026192..c901e9d 100644 --- a/nest/proto-hooks.c +++ b/nest/proto-hooks.c @@ -205,6 +205,17 @@ void rt_notify(struct proto *p, net *net, rte *new, rte *old, ea_list *attrs) { DUMMY; } /** + * limit_notify - notify instance about limit hit + * @p: protocol instance + * @l: linit structure + * @table: table where route limit occurs + * + * Function should return 0 unless it shuts protocol down + */ +int limit_notify(struct proto *, struct proto_limit *l, struct rtable *table) +{ DUMMY; } + +/** * neigh_notify - notify instance about neighbor status change * @neigh: a neighbor cache entry * diff --git a/nest/proto.c b/nest/proto.c index d55c348..56ca6af 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -33,6 +33,10 @@ static list initial_proto_list; static list flush_proto_list; static struct proto *initial_device_proto; +/* protocol limiting variables */ +static struct rate_limit rl_rt_limit; +static event *proto_shut_event; + static event *proto_flush_event; static char *p_states[] = { "DOWN", "START", "UP", "STOP" }; @@ -41,6 +45,8 @@ static char *c_states[] = { "HUNGRY", "FEEDING", "HAPPY", "FLUSHING" }; static void proto_flush_all(void *); static void proto_rethink_goal(struct proto *p); static char *proto_state_name(struct proto *p); +static char *proto_limit_name(struct proto_limit *l); +static void proto_shutdown_abusers(void *unused UNUSED); static void proto_enqueue(list *l, struct proto *p) @@ -114,6 +120,8 @@ proto_new(struct proto_config *c, unsigned size) p->table = c->table->table; p->in_filter = c->in_filter; p->out_filter = c->out_filter; + p->in_limit = c->in_limit; + p->out_limit = c->out_limit; p->hash_key = random_u32(); c->proto = p; return p; @@ -363,6 +371,8 @@ proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config p->name = nc->name; p->in_filter = nc->in_filter; p->out_filter = nc->out_filter; + p->in_limit = nc->in_limit; + p->out_limit = nc->out_limit; p->preference = nc->preference; if (import_changed || export_changed) @@ -457,7 +467,7 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty PD(p, "Unconfigured"); p->cf_new = NULL; } - p->reconfiguring = 1; + p->flags |= PFLAG_RECONFIGURING; config_add_obstacle(old); proto_rethink_goal(p); } @@ -490,7 +500,7 @@ proto_rethink_goal(struct proto *p) { struct protocol *q; - if (p->reconfiguring && p->core_state == FS_HUNGRY && p->proto_state == PS_DOWN) + if (PROTO_IS_RECONFIGURING(p) && p->core_state == FS_HUNGRY && p->proto_state == PS_DOWN) { struct proto_config *nc = p->cf_new; DBG("%s has shut down for reconfiguration\n", p->name); @@ -504,7 +514,7 @@ proto_rethink_goal(struct proto *p) } /* Determine what state we want to reach */ - if (p->disabled || p->reconfiguring) + if (p->disabled || PROTO_IS_RECONFIGURING(p)) { p->core_goal = FS_HUNGRY; if (p->core_state == FS_HUNGRY && p->proto_state == PS_DOWN) @@ -525,6 +535,8 @@ proto_rethink_goal(struct proto *p) DBG("Kicking %s up\n", p->name); PD(p, "Starting"); proto_init_instance(p); + /* Zeroing limit flags */ + p->flags &= ~PLIMIT_FLAGS; proto_notify_state(p, (q->start ? q->start(p) : PS_UP)); } } @@ -635,6 +647,8 @@ protos_build(void) proto_pool = rp_new(&root_pool, "Protocols"); proto_flush_event = ev_new(proto_pool); proto_flush_event->hook = proto_flush_all; + proto_shut_event = ev_new(proto_pool); + proto_shut_event->hook = proto_shutdown_abusers; } static void @@ -715,6 +729,20 @@ proto_schedule_feed(struct proto *p, int initial) if (!initial) p->stats.exp_routes = 0; + /* + * Remove export limit if set. + * We assume something is changed (protocol limit or filter or + * other host announces) so refeeding protocol will not cause + * export limit to hit again. + */ + p->flags &= ~(PFLAG_ELIMIT|PFLAG_ELIMIT_BLOCK); +#ifdef CONFIG_PIPE + /* feedind is requested for every pipe hook e.g. both ends */ + if (proto_is_pipe(p)) + { + p->flags &= ~(PFLAG_ILIMIT|PFLAG_ILIMIT_BLOCK); + } +#endif proto_relink(p); p->attn->hook = initial ? proto_feed_initial : proto_feed_more; ev_schedule(p->attn); @@ -749,6 +777,80 @@ proto_request_feeding(struct proto *p) proto_schedule_feed(p, 0); } +static void +proto_shutdown_abusers(void *unused UNUSED) +{ + struct proto *p, *p_next; + int disable; + + WALK_LIST_DELSAFE(p, p_next, active_proto_list) + { + if (!(p->flags & (PFLAG_RESTART|PFLAG_DISABLE))) + continue; + + disable = (p->flags & PFLAG_DISABLE) ? 1 : 0; + + p->disabled = 1; + proto_rethink_goal(p); + if (!disable) + { + p->disabled = 0; + proto_rethink_goal(p); + } + } +} + +/** + * proto_notify_limit: notify protocol instance about limit hit and take appropriate action + * @p: given protocol + * @l: limit being hit + * + * If limit hook exists it is called and returned value is examined. + * If non-zero value returned, processing stops. Overwise action is taken + * depending on l->action configured in limit instance. Non-zero value should be + * used for proper protocol shutdown only. Setting one of PFLAG_ILIMIT|PFLAG_ELIMIT is required + * if protocol is not going down. + * + * Returns 1 if processing must be stopped, 0 overwise. + */ +int +proto_notify_limit(struct proto *p, struct proto_limit *l, struct rtable *table) +{ + int ret = 1, flag; + + log_rl(&rl_rt_limit, L_ERR "Protocol %s hits route %s limit (%d), action: %s", p->name, + (l->direction == PL_IMPORT) ? "import" : "export", l->limit, proto_limit_name(l)); + + flag = (l->direction == PL_IMPORT) ? PFLAG_ILIMIT : PFLAG_ELIMIT; + + /* Skip multiple filter hit invocations */ + if ((l->action != PL_ACTION_WARN) && (p->flags & flag)) + return 1; + + p->flags |= flag; + + if (p->limit_notify && (p->limit_notify(p, l, table))) + return 1; + + switch (l->action) + { + case PL_ACTION_WARN: + ret = 0; + break; + case PL_ACTION_BLOCK: + p->flags |= (l->direction == PL_IMPORT) ? PFLAG_ILIMIT_BLOCK : PFLAG_ELIMIT_BLOCK; + break; + case PL_ACTION_RESTART: + case PL_ACTION_DISABLE: + /* Schedule instance shutdown */ + p->flags |= (l->action == PL_ACTION_DISABLE) ? PFLAG_DISABLE : PFLAG_RESTART; + ev_schedule(proto_shut_event); + break; + } + + return ret; +} + /** * proto_notify_state - notify core about protocol state change * @p: protocol the state of which has changed @@ -853,6 +955,19 @@ proto_state_name(struct proto *p) #undef P } +static char * +proto_limit_name(struct proto_limit *l) +{ + switch (l->action) + { + case PL_ACTION_WARN: return "WARN"; + case PL_ACTION_BLOCK: return "BLOCK"; + case PL_ACTION_RESTART: return "RESTART"; + case PL_ACTION_DISABLE: return "DISABLE"; + } + return "unknown"; +} + static void proto_do_show_stats(struct proto *p) { @@ -919,7 +1034,8 @@ proto_do_show_pipe_stats(struct proto *p) void proto_cmd_show(struct proto *p, unsigned int verbose, int cnt) { - byte buf[256], tbuf[TM_DATETIME_BUFFER_SIZE]; + byte buf[256], tbuf[TM_DATETIME_BUFFER_SIZE], lbuf[25]; + struct proto_limit *l; /* First protocol - show header */ if (!cnt) @@ -928,12 +1044,16 @@ proto_cmd_show(struct proto *p, unsigned int verbose, int cnt) buf[0] = 0; if (p->proto->get_status) p->proto->get_status(p, buf); + if (p->flags & (PFLAG_ILIMIT|PFLAG_ELIMIT)) + bsnprintf(lbuf, sizeof(lbuf), "%s/%s", proto_state_name(p), "LI"); + else + strcpy(lbuf, proto_state_name(p)); tm_format_datetime(tbuf, &config->tf_proto, p->last_state_change); cli_msg(-1002, "%-8s %-8s %-8s %-5s %-10s %s", p->name, p->proto->name, p->table->name, - proto_state_name(p), + lbuf, tbuf, buf); if (verbose) @@ -945,6 +1065,20 @@ proto_cmd_show(struct proto *p, unsigned int verbose, int cnt) cli_msg(-1006, " Preference: %d", p->preference); cli_msg(-1006, " Input filter: %s", filter_name(p->in_filter)); cli_msg(-1006, " Output filter: %s", filter_name(p->out_filter)); + if (p->in_limit) + { + l = p->in_limit; + cli_msg(-1006, " Import limit: %4d, action: %7s%s", l->limit, + proto_limit_name(l), (p->flags & PFLAG_ILIMIT) ? " [ LIMIT HIT ]" : ""); + } else if (p->flags & PFLAG_ILIMIT) + cli_msg(-1006, " Old Import limit: [HIT][reload/restart required]"); + if (p->out_limit) + { + l = p->out_limit; + cli_msg(-1006, " Export limit: %4d, action: %7s%s", l->limit, + proto_limit_name(l), (p->flags & PFLAG_ELIMIT) ? " [ LIMIT HIT ]" : ""); + } else if (p->flags & PFLAG_ELIMIT) + cli_msg(-1006, " Old export limit: [HIT][reload/restart required]"); if (p->proto_state != PS_DOWN) { @@ -1013,6 +1147,8 @@ proto_cmd_restart(struct proto *p, unsigned int arg UNUSED, int cnt UNUSED) void proto_cmd_reload(struct proto *p, unsigned int dir, int cnt UNUSED) { + u32 flags; + if (p->disabled) { cli_msg(-8, "%s: already disabled", p->name); @@ -1027,13 +1163,31 @@ proto_cmd_reload(struct proto *p, unsigned int dir, int cnt UNUSED) /* re-importing routes */ if (dir != CMD_RELOAD_OUT) + { + /* + * Removing limit hit flags should be safe because: + * current (and planned) limiting actions block + * new route import only. Route withdrawal is not blocked. + * At this moment core has a small (possibly zero) subset of + * routes which are announced by protocol. Same route announce + * from the same protocol are ignored by core so we can safely + * re-import all routes. + */ + flags = p->flags & (PFLAG_ILIMIT|PFLAG_ILIMIT_BLOCK); + p->flags &= ~flags; if (! (p->reload_routes && p->reload_routes(p))) { cli_msg(-8006, "%s: reload failed", p->name); + /* reload failed, adding removed flags back */ + p->flags |= flags; return; } + } - /* re-exporting routes */ + /* + * re-exporting routes. + * Export limit flags are dispatched in proto_request_feeding() + */ if (dir != CMD_RELOAD_IN) proto_request_feeding(p); diff --git a/nest/protocol.h b/nest/protocol.h index a7518c2..82b20cb 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -68,6 +68,25 @@ void protos_dump_all(void); #define GA_FULL 2 /* Result = both name and value */ /* + * Protocol limits + */ +#define PL_IMPORT 1 /* Import limit*/ +#define PL_EXPORT 2 /* Export limit */ + +#define PL_ACTION_WARN 1 /* Issue log warning */ +#define PL_ACTION_BLOCK 2 /* Block new routes */ +#define PL_ACTION_RESTART 4 /* Force protocol restart */ +#define PL_ACTION_DISABLE 5 /* Shutdown and disable protocol */ + +struct proto_limit { + int direction; /* PL_IMPORT|PL_EXPORT */ + int limit; /* maximum prefix number */ + int action; /* action to take */ +}; + +int proto_notify_limit(struct proto *p, struct proto_limit *l, struct rtable *table); + +/* * Known protocols */ @@ -92,12 +111,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 */ struct proto_stats { /* Import - from protocol to core */ @@ -141,7 +163,7 @@ struct proto { 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 flags; /* Various protocol flags */ unsigned refeeding; /* We are refeeding (valid only if core_state == FS_FEEDING) */ u32 hash_key; /* Random key used for hashing of neighbors */ bird_clock_t last_state_change; /* Time of last state transition */ @@ -154,6 +176,7 @@ struct proto { * if_notify Notify protocol about interface state changes. * ifa_notify Notify protocol about interface address changes. * rt_notify Notify protocol about routing table updates. + * limit_notify Notify protocol about import/export limit hit. * neigh_notify Notify protocol about neighbor cache events. * make_tmp_attrs Construct ea_list from private attrs stored in rte. * store_tmp_attrs Store private attrs back to the rte. @@ -169,6 +192,7 @@ struct proto { void (*if_notify)(struct proto *, unsigned flags, struct iface *i); void (*ifa_notify)(struct proto *, unsigned flags, struct ifa *a); void (*rt_notify)(struct proto *, struct rtable *table, struct network *net, struct rte *new, struct rte *old, struct ea_list *attrs); + int (*limit_notify)(struct proto *, struct proto_limit *l, struct rtable *table); void (*neigh_notify)(struct neighbor *neigh); struct ea_list *(*make_tmp_attrs)(struct rte *rt, struct linpool *pool); void (*store_tmp_attrs)(struct rte *rt, struct ea_list *attrs); @@ -192,6 +216,8 @@ struct proto { struct rtable *table; /* Our primary routing table */ struct filter *in_filter; /* Input filter */ struct filter *out_filter; /* Output filter */ + struct proto_limit *in_limit; /* Limit for importing routes from protocol */ + struct proto_limit *out_limit; /* Limit for exporting routes to protocol */ struct announce_hook *ahooks; /* Announcement hooks for this protocol */ struct fib_iterator *feed_iterator; /* Routing table iterator used during protocol feeding */ @@ -200,6 +226,18 @@ struct proto { /* Hic sunt protocol-specific data */ }; +#define PFLAG_RECONFIGURING 0x01 /* We're shutting down due to reconfiguration */ +#define PFLAG_ILIMIT 0x02 /* Import route limit reached */ +#define PFLAG_ELIMIT 0x04 /* Export route limit reached */ +#define PFLAG_ILIMIT_BLOCK 0x08 /* Block route imports */ +#define PFLAG_ELIMIT_BLOCK 0x10 /* Block route exports */ +#define PFLAG_RESTART 0x20 /* Protocol needs restart */ +#define PFLAG_DISABLE 0x40 /* Protocol needs disabling */ + +#define PLIMIT_FLAGS (PFLAG_ILIMIT|PFLAG_ELIMIT|PFLAG_ILIMIT_BLOCK|PFLAG_ELIMIT_BLOCK|PFLAG_DISABLE) + +#define PROTO_IS_RECONFIGURING(x) ((x)->flags & PFLAG_RECONFIGURING) + struct proto_spec { void *ptr; int patt; diff --git a/nest/rt-table.c b/nest/rt-table.c index e20d2f6..92fb245 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -183,25 +183,44 @@ rte_trace_out(unsigned int flag, struct proto *p, rte *e, char *msg) rte_trace(p, e, '<', msg); } -static inline void +/** + * do_rte_announce - announce new rte to protocol + * @a: 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: are we refeeding protocol + * + * Returns 1 if feeding can continue, 0 overwise + * + */ +static inline int do_rte_announce(struct announce_hook *a, int type UNUSED, net *net, rte *new, rte *old, ea_list *tmpa, int refeed) { struct proto *p = a->proto; struct filter *filter = p->out_filter; + struct proto_limit *l = p->out_limit; struct proto_stats *stats = &p->stats; rte *new0 = new; rte *old0 = old; - int ok; + int ok, check_limit = 1; #ifdef CONFIG_PIPE /* The secondary direction of the pipe */ if (proto_is_pipe(p) && (p->table != a->table)) { filter = p->in_filter; + l = p->in_limit; stats = pipe_get_peer_stats(p); } #endif + /* Check if we're called on non-default protocol table */ + if ((!proto_is_pipe(p)) && (p->table != a->table)) + check_limit = 0; + if (new) { stats->exp_updates_received++; @@ -270,7 +289,35 @@ do_rte_announce(struct announce_hook *a, int type UNUSED, net *net, rte *new, rt /* FIXME - This is broken because of incorrect 'old' value (see above) */ if (!new && !old) - return; + return 1; + + /* + * XXX: there is (currently) no way to determine if rte was announced to the protocol, + * especially in case of route limiting active. As a result if block flag is set we're + * 1) ignoring new routes + * 2) convert updates to withdrawals + * + */ + if (new && (p->flags & PFLAG_ELIMIT_BLOCK) && check_limit) + { + if (new != new0) + rte_free(new); + /* New route announce, simply ignore */ + if (!old) + return 0; + /* Route update, change to withdraw */ + new = NULL; + } + + /* Check route limits for new routes */ + if (new && l && !old && check_limit && (stats->exp_routes + 1 > l->limit) && (proto_notify_limit(p, l, a->table) == 1)) + { + /* free allocated data and return */ + if (new != new0) + rte_free(new); + + return 0; + } if (new) stats->exp_updates_accepted++; @@ -310,6 +357,8 @@ do_rte_announce(struct announce_hook *a, int type UNUSED, net *net, rte *new, rt rte_free(new); if (old && old != old0) rte_free(old); + + return 1; } /** @@ -426,13 +475,21 @@ static void rte_recalculate(rtable *table, net *net, struct proto *p, struct proto *src, rte *new, ea_list *tmpa) { struct proto_stats *stats = &p->stats; + struct proto_limit *l; rte *old_best = net->routes; rte *old = NULL; rte **k, *r, *s; + int check_limit = 1; #ifdef CONFIG_PIPE if (proto_is_pipe(p) && (p->table == table)) stats = pipe_get_peer_stats(p); + /* Check if we're called on non-default protocol table */ + if ((!proto_is_pipe(p)) && (p->table != table)) + check_limit = 0; +#else + if (p->table != a->table) + check_limit = 0; #endif k = &net->routes; /* Find and remove original route from the same protocol */ @@ -486,6 +543,17 @@ rte_recalculate(rtable *table, net *net, struct proto *p, struct proto *src, rte return; } + /* Check limit for imported routes */ + if (new && !old && check_limit) + { + if (p->flags & PFLAG_ILIMIT_BLOCK) + return; + + if ((l = p->in_limit) && (!proto_is_pipe(p)) && + (stats->imp_routes + 1 > l->limit) && (proto_notify_limit(p, l, table) == 1)) + return; + } + if (new) stats->imp_updates_accepted++; else @@ -1207,16 +1275,19 @@ rt_commit(struct config *new, struct config *old) DBG("\tdone\n"); } -static inline void +static inline int do_feed_baby(struct proto *p, int type, struct announce_hook *h, net *n, rte *e) { struct proto *q = e->attrs->proto; + int res; ea_list *tmpa; rte_update_lock(); tmpa = q->make_tmp_attrs ? q->make_tmp_attrs(e, rte_update_pool) : NULL; - do_rte_announce(h, type, n, e, p->refeeding ? e : NULL, tmpa, p->refeeding); + res = do_rte_announce(h, type, n, e, p->refeeding ? e : NULL, tmpa, p->refeeding); rte_update_unlock(); + + return res; } /** @@ -1263,8 +1334,9 @@ again: { if (p->core_state != FS_FEEDING) return 1; /* In the meantime, the protocol fell down. */ - do_feed_baby(p, RA_OPTIMAL, h, n, e); max_feed--; + if (do_feed_baby(p, RA_OPTIMAL, h, n, e) == 0) + break; } if (p->accept_ra_types == RA_ANY) diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index 675342d..0c5516b 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -542,19 +542,33 @@ bgp_active(struct bgp_proto *p) bgp_start_timer(conn->connect_retry_timer, delay); } -int -bgp_apply_limits(struct bgp_proto *p) +static int +bgp_limit_notify(struct proto *P, struct proto_limit *l, struct rtable *table) { - if (p->cf->route_limit && (p->p.stats.imp_routes > p->cf->route_limit)) + struct bgp_proto *p = (struct bgp_proto *) P; + int subcode; + if ((l->direction != PL_IMPORT) && (l->action != PL_ACTION_DISABLE)) + return 0; + + switch (l->action) { - log(L_WARN "%s: Route limit exceeded, shutting down", p->p.name); + case PL_ACTION_RESTART: + case PL_ACTION_DISABLE: + if (l->action == PL_ACTION_DISABLE) + P->disabled = 1; + log(L_WARN "%s: Route limit exceeded, shutting down", 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; + /* + * Send 6,2 (Administrative Shutdown) for export limit + * Send 6,1 (Maximum Number of Prefixes Reached) overwise + */ + subcode = (P->disabled && (l->direction == PL_EXPORT)) ? 2 : 1; + bgp_stop(p, subcode); + return 1; + default: + return 0; } - - return 0; } @@ -866,7 +880,7 @@ bgp_shutdown(struct proto *P) BGP_TRACE(D_EVENTS, "Shutdown requested"); bgp_store_error(p, NULL, BE_MAN_DOWN, 0); - if (P->reconfiguring) + if (P->flags & PFLAG_RECONFIGURING) { if (P->cf_new) subcode = 6; // Errcode 6, 6 - other configuration change @@ -907,6 +921,7 @@ bgp_init(struct proto_config *C) P->rte_better = bgp_rte_better; P->import_control = bgp_import_control; P->neigh_notify = bgp_neigh_notify; + P->limit_notify = bgp_limit_notify; P->reload_routes = bgp_reload_routes; p->cf = c; p->local_as = c->local_as; diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index 437ba33..f271ef3 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -150,7 +150,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 03c233d..de3b254 100644 --- a/proto/bgp/config.Y +++ b/proto/bgp/config.Y @@ -94,7 +94,14 @@ 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 ';' { + if (!this_proto->in_limit) + this_proto->in_limit = cfg_allocz(sizeof(struct proto_limit)); + struct proto_limit *l = this_proto->in_limit; + l->direction = PL_IMPORT; + l->limit = $4; + l->action = PL_ACTION_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 c3a8673..8eb6483 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -883,9 +883,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: -- 1.7.3.2 --------------010908080505070307040008--
participants (1)
-
Alexander V. Chernikov