From f9c8c639593aa98723397a640f8d899b85c39fb7 Mon Sep 17 00:00:00 2001 From: Alexander V. Chernikov 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 is equivalent to export This is similar to the import keyword, except that it works in the direction from the routing table to the protocol. Default: import limit + 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: export limit + Specify limit action to be taken when export routes limit is hit. + Actions are the same as in import keyword except description " 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: route limit 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 + disable after error 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