diff options
-rw-r--r-- | doc/bird.sgml | 11 | ||||
-rw-r--r-- | nest/config.Y | 21 | ||||
-rw-r--r-- | nest/proto.c | 154 | ||||
-rw-r--r-- | nest/protocol.h | 57 | ||||
-rw-r--r-- | nest/rt-table.c | 19 | ||||
-rw-r--r-- | proto/bgp/bgp.c | 63 | ||||
-rw-r--r-- | proto/bgp/bgp.h | 1 | ||||
-rw-r--r-- | proto/bgp/config.Y | 6 | ||||
-rw-r--r-- | proto/bgp/packets.c | 3 | ||||
-rw-r--r-- | proto/pipe/config.Y | 1 | ||||
-rw-r--r-- | proto/pipe/pipe.c | 45 | ||||
-rw-r--r-- | proto/pipe/pipe.h | 1 | ||||
-rw-r--r-- | sysdep/unix/timer.h | 18 |
13 files changed, 328 insertions, 72 deletions
diff --git a/doc/bird.sgml b/doc/bird.sgml index 20738be3..6f96b862 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -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 diff --git a/nest/config.Y b/nest/config.Y index f889828a..60b03278 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -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"); diff --git a/nest/proto.c b/nest/proto.c index 802d5238..418a7a61 100644 --- a/nest/proto.c +++ b/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; @@ -994,6 +1099,14 @@ proto_show_stats(struct proto_stats *s) } 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) { // cli_msg(-1006, " Table: %s", p->table->name); @@ -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,12 +1222,21 @@ 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) proto_request_feeding(p); diff --git a/nest/protocol.h b/nest/protocol.h index 983ce75a..3dd7cf2f 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -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 diff --git a/nest/rt-table.c b/nest/rt-table.c index 310c1afd..6a28fd43 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -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 diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index 4dd4b7be..cf743dff 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -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) { diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index a8c5818e..aa2db4b0 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -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); diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y index 78ca52de..5feaea0d 100644 --- a/proto/bgp/config.Y +++ b/proto/bgp/config.Y @@ -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; } diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index d3e9b6a1..168025d0 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -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: diff --git a/proto/pipe/config.Y b/proto/pipe/config.Y index 40637558..4fb2b499 100644 --- a/proto/pipe/config.Y +++ b/proto/pipe/config.Y @@ -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; } ; diff --git a/proto/pipe/pipe.c b/proto/pipe/pipe.c index 36b06d43..a5fcc6f6 100644 --- a/proto/pipe/pipe.c +++ b/proto/pipe/pipe.c @@ -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); diff --git a/proto/pipe/pipe.h b/proto/pipe/pipe.h index 50b31698..e777fb41 100644 --- a/proto/pipe/pipe.h +++ b/proto/pipe/pipe.h @@ -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 */ }; diff --git a/sysdep/unix/timer.h b/sysdep/unix/timer.h index a20df483..a788ae27 100644 --- a/sysdep/unix/timer.h +++ b/sysdep/unix/timer.h @@ -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; |