Hello list! At the moment bird has route limits implemented for BGP only (route limit XXX).
This seems to be not enough in various BGP+IGP configurations. Vendors implement the following approaches: Cisco: * warning-only (OSPF, redistribute maximum-prefix) - print warning message * withdraw (OSPF, redistribute maximum-prefix) - Prevents additional redistribution when the number of routes defined by the maximum argument have been redistributed. * clear (IS-IS) - clears all redistributed prefixes See http://www.cisco.com/en/US/docs/ios/12_0s/feature/guide/fsoredis.htmlfor more information Juniper: * log-only (general, maximum-prefixes) - print warning message only * withdraw (general, maximum-prefixes) - any additional routes are rejected * clear (OSPF, ISIS, prefix-export-limit) - clear database from all extrnal routes This patch introduces general limiting functionality for any protocol. Import/export limits can be configured with the following actions: * warn (prints warning message) * block (blocks new import/exports from/to the protocol) * shutdown (restart the protocol, import only) * disable (shutdown and disable protocol) If any protocol limit is hit and block action is taken, protocol can be returned to 'normal' state by using reload [in|out] protocol (or restaring it).
From f9c8c639593aa98723397a640f8d899b85c39fb7 Mon Sep 17 00:00:00 2001 From: Alexander V. Chernikov <[email protected]> Date: Mon, 14 Nov 2011 15:33:27 +0000 Subject: [PATCH 1/1] * Implement general protocol limiting --- doc/bird.sgml | 16 +++++- nest/config.Y | 32 +++++++++ nest/proto.c | 175 +++++++++++++++++++++++++++++++++++++++++++++++++-- nest/protocol.h | 42 ++++++++++++- nest/rt-table.c | 49 ++++++++++++++- proto/bgp/bgp.c | 28 +++++--- proto/bgp/bgp.h | 1 - proto/bgp/config.Y | 9 +++- proto/bgp/packets.c | 3 - 9 files changed, 329 insertions(+), 26 deletions(-) diff --git a/doc/bird.sgml b/doc/bird.sgml index 7f53f02..4060f05 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -415,6 +415,19 @@ 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 | shutdown | 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 (not route updates) coming from protocol. Shutdown 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/shutdown/. + 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 +1284,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 shutdown/. 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..d02b1f7 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -21,6 +21,7 @@ static struct iface_patt *this_ipatt; static struct iface_patt_node *this_ipn; static list *this_p_list; static struct password_item *this_p_item; +static struct proto_limit *this_limit; static int password_id; static inline void @@ -43,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, SHUTDOWN, 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) @@ -153,6 +155,25 @@ 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 { + if ((!strcmp(this_proto->protocol->name, "Device")) || + (!strcmp(this_proto->protocol->name, "Static"))) + cf_error("%s protocol does not support import limits", this_proto->protocol->name); + if (!this_proto->in_limit) + this_proto->in_limit = cfg_allocz(sizeof(struct proto_limit)); + this_limit = this_proto->in_limit; + this_limit->direction = PL_IMPORT; + this_limit->limit = $3; + } + EXCEED limit_action + | EXPORT LIMIT expr { + if (!this_proto->out_limit) + this_proto->out_limit = cfg_allocz(sizeof(struct proto_limit)); + this_limit = this_proto->out_limit; + this_limit->direction = PL_EXPORT; + this_limit->limit = $3; + } + EXCEED limit_action | TABLE rtable { this_proto->table = $2; } | ROUTER ID idval { this_proto->router_id = $3; } | DESCRIPTION TEXT { this_proto->dsc = $2; } @@ -165,6 +186,17 @@ imexport: | NONE { $$ = FILTER_REJECT; } ; +limit_action: + WARN { this_limit->action = PL_ACTION_WARN; } + | BLOCK { this_limit->action = PL_ACTION_BLOCK; } + | SHUTDOWN { + if (this_limit->direction == PL_EXPORT) + cf_error("SHUTDOWN action can't be used in export limiter. Use DISABLE action instead"); + this_limit->action = PL_ACTION_SHUTDOWN; + } + | DISABLE { this_limit->action = PL_ACTION_DISABLE; } + ; + rtable: SYM { if ($1->class != SYM_TABLE) cf_error("Table name expected"); diff --git a/nest/proto.c b/nest/proto.c index d55c348..f022dc2 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -33,6 +33,11 @@ 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 list abuse_proto_list; + static event *proto_flush_event; static char *p_states[] = { "DOWN", "START", "UP", "STOP" }; @@ -41,6 +46,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 +121,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 +372,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 +468,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); } @@ -489,8 +500,9 @@ static void proto_rethink_goal(struct proto *p) { struct protocol *q; + struct proto *ap, *ap_next; - if (p->reconfiguring && p->core_state == FS_HUNGRY && p->proto_state == PS_DOWN) + if ((p->flags & PFLAG_RECONFIGURING) && 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 +516,7 @@ proto_rethink_goal(struct proto *p) } /* Determine what state we want to reach */ - if (p->disabled || p->reconfiguring) + if (p->disabled || (p->flags & PFLAG_RECONFIGURING)) { p->core_goal = FS_HUNGRY; if (p->core_state == FS_HUNGRY && p->proto_state == PS_DOWN) @@ -525,6 +537,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)); } } @@ -534,6 +548,14 @@ proto_rethink_goal(struct proto *p) { DBG("Kicking %s down\n", p->name); PD(p, "Shutting down"); + /* Remove from pre-shutdown list if exists */ + WALK_LIST_DELSAFE(ap, ap_next, abuse_proto_list) + { + if ((ap = SKIP_BACK(struct proto, shutdown_node, ap)) != p) + continue; + rem_node(&ap->shutdown_node); + break; + } proto_notify_state(p, (q->shutdown ? q->shutdown(p) : PS_DOWN)); } } @@ -613,6 +635,7 @@ protos_build(void) init_list(&inactive_proto_list); init_list(&initial_proto_list); init_list(&flush_proto_list); + init_list(&abuse_proto_list); proto_build(&proto_device); #ifdef CONFIG_RADV proto_build(&proto_radv); @@ -635,6 +658,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 +740,9 @@ proto_schedule_feed(struct proto *p, int initial) if (!initial) p->stats.exp_routes = 0; + /* Remove export limit flags from protocol */ + p->flags &= ~(PFLAG_ELIMIT|PFLAG_ELIMIT_BLOCK); + proto_relink(p); p->attn->hook = initial ? proto_feed_initial : proto_feed_more; ev_schedule(p->attn); @@ -746,9 +774,92 @@ proto_request_feeding(struct proto *p) rt_feed_baby_abort(p); } + /* + * 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); 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, abuse_proto_list) + { + p = SKIP_BACK(struct proto, shutdown_node, p); + + rem_node(&p->shutdown_node); + 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 PL_HANDLED is returned, processing stops. Overwise action is taken + * depending on l->action configured in limit instance. PL_HANDLED 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) == PL_HANDLED)) + 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_SHUTDOWN: + case PL_ACTION_DISABLE: + /* Schedule instance shutdown */ + add_tail(&abuse_proto_list, &p->shutdown_node); + if (l->action == PL_ACTION_DISABLE) + p->flags |= PFLAG_DISABLE; + 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 +964,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_SHUTDOWN: return "SHUTDOWN"; + case PL_ACTION_DISABLE: return "DISABLE"; + } + return "unknown"; +} + static void proto_do_show_stats(struct proto *p) { @@ -919,7 +1043,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 +1053,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 +1074,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 +1156,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 +1172,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..f9e6d92 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -68,6 +68,29 @@ 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_HANDLED 1 /* Limit action is handled by the protocol hook */ +#define PL_DEFAULT 2 /* Core has to execute default action */ + +#define PL_ACTION_WARN 1 /* Issue log warning */ +#define PL_ACTION_BLOCK 2 /* Block new routes */ +#define PL_ACTION_CLEAR 3 /* Clear exported routes */ +#define PL_ACTION_SHUTDOWN 4 /* Force protocol shutdown */ +#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 +115,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 */ @@ -126,6 +152,7 @@ struct proto_stats { struct proto { node n; /* Node in *_proto_list */ node glob_node; /* Node in global proto_list */ + node shutdown_node; /* Node in limiter shutdown list */ struct protocol *proto; /* Protocol */ struct proto_config *cf; /* Configuration data */ struct proto_config *cf_new; /* Configuration we want to switch to after shutdown (NULL=delete) */ @@ -141,7 +168,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 +181,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 +197,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 +221,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 +231,15 @@ 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_DISABLE 0x20 /* Protocol needs disabling */ + +#define PLIMIT_FLAGS (PFLAG_ILIMIT|PFLAG_ELIMIT|PFLAG_ILIMIT_BLOCK|PFLAG_ELIMIT_BLOCK|PFLAG_DISABLE) + struct proto_spec { void *ptr; int patt; diff --git a/nest/rt-table.c b/nest/rt-table.c index e20d2f6..83c0345 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -188,20 +188,26 @@ do_rte_announce(struct announce_hook *a, int type UNUSED, net *net, rte *new, rt { 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, wrong_table = 0; #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)) + wrong_table = 1; + if (new) { stats->exp_updates_received++; @@ -272,6 +278,22 @@ do_rte_announce(struct announce_hook *a, int type UNUSED, net *net, rte *new, rt if (!new && !old) return; + /* Check if we can export new route / exceed export limit */ + if (new && l && !old) + { + if (p->flags & PFLAG_ELIMIT_BLOCK) + return; + + if ((l = p->out_limit) && (p->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; + } + } + if (new) stats->exp_updates_accepted++; else @@ -426,6 +448,7 @@ 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; @@ -486,6 +509,16 @@ rte_recalculate(rtable *table, net *net, struct proto *p, struct proto *src, rte return; } + /* Check limit for imported routes */ + if (new && !old) + { + if (p->flags & PFLAG_ILIMIT_BLOCK) + return; + + if ((l = p->in_limit) && (p->stats.imp_routes + 1 > l->limit) && (proto_notify_limit(p, l, table) == 1)) + return; + } + if (new) stats->imp_updates_accepted++; else @@ -1233,7 +1266,11 @@ rt_feed_baby(struct proto *p) { struct announce_hook *h; struct fib_iterator *fit; - int max_feed = 256; + struct proto_limit *l = p->out_limit; + int max_feed = 256, need_check, do_check; + + /* Do we need to filter route updates? */ + need_check = (!(p->flags & PFLAG_ELIMIT_BLOCK) && l) ? 1 : 0; if (!p->feed_ahook) /* Need to initialize first */ { @@ -1248,6 +1285,8 @@ rt_feed_baby(struct proto *p) again: h = p->feed_ahook; + /* Do limit check for base protocol table only */ + do_check = ((p->table == h->table) && need_check)? 1 : 0; FIB_ITERATE_START(&h->table->fib, fit, fn) { net *n = (net *) fn; @@ -1263,6 +1302,12 @@ again: { if (p->core_state != FS_FEEDING) return 1; /* In the meantime, the protocol fell down. */ + if (do_check && (p->stats.exp_routes + 1 > l->limit)) + if (proto_notify_limit(p, l, h->table)) + { + /* limit action forbids new exports, end feed */ + break; + } do_feed_baby(p, RA_OPTIMAL, h, n, e); max_feed--; } diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index 675342d..483a2d0 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -542,19 +542,27 @@ 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; + if (l->direction != PL_IMPORT) + return PL_DEFAULT; + + switch (l->action) { - log(L_WARN "%s: Route limit exceeded, shutting down", p->p.name); + case PL_ACTION_SHUTDOWN: + 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; + return PL_HANDLED; + default: + return PL_DEFAULT; } - - return 0; } @@ -866,7 +874,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 +915,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; @@ -1141,9 +1150,6 @@ bgp_show_proto_info(struct proto *P) p->rs_client ? " route-server" : "", p->as4_session ? " AS4" : ""); cli_msg(-1006, " Source address: %I", p->source_addr); - if (p->cf->route_limit) - cli_msg(-1006, " Route limit: %d/%d", - p->p.stats.imp_routes, p->cf->route_limit); cli_msg(-1006, " Hold timer: %d/%d", tm_remains(c->hold_timer), c->hold_time); cli_msg(-1006, " Keepalive timer: %d/%d", 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..3267ffd 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_SHUTDOWN; + } | 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
signature.asc
Description: OpenPGP digital signature
