diff options
Diffstat (limited to 'nest')
-rw-r--r-- | nest/config.Y | 16 | ||||
-rw-r--r-- | nest/proto.c | 93 | ||||
-rw-r--r-- | nest/protocol.h | 18 | ||||
-rw-r--r-- | nest/rt-table.c | 83 |
4 files changed, 182 insertions, 28 deletions
diff --git a/nest/config.Y b/nest/config.Y index 4721112..dbb10ad 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -44,7 +44,8 @@ CF_KEYWORDS(ROUTER, ID, PROTOCOL, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT) CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, TABLE, STATES, ROUTES, FILTERS) 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, ADDRESS, PORT, PASSWORDS) +CF_KEYWORDS(LISTEN, BGP, V6ONLY, ADDRESS, PORT, PASSWORDS, DESCRIPTION) +CF_KEYWORDS(RELOAD, IN, OUT) CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT, RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE) @@ -141,6 +142,7 @@ proto_item: | EXPORT imexport { this_proto->out_filter = $2; } | TABLE rtable { this_proto->table = $2; } | ROUTER ID idval { this_proto->router_id = $3; } + | DESCRIPTION TEXT { this_proto->dsc = $2; } ; imexport: @@ -436,11 +438,17 @@ echo_size: ; CF_CLI(DISABLE, proto_patt, <protocol> | \"<pattern>\" | all, [[Disable protocol]]) -{ proto_xxable($2, 0); } ; +{ proto_xxable($2, XX_DISABLE); } ; CF_CLI(ENABLE, proto_patt, <protocol> | \"<pattern>\" | all, [[Enable protocol]]) -{ proto_xxable($2, 1); } ; +{ proto_xxable($2, XX_ENABLE); } ; CF_CLI(RESTART, proto_patt, <protocol> | \"<pattern>\" | all, [[Restart protocol]]) -{ proto_xxable($2, 2); } ; +{ proto_xxable($2, XX_RESTART); } ; +CF_CLI(RELOAD, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol]]) +{ proto_xxable($2, XX_RELOAD); } ; +CF_CLI(RELOAD IN, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol (just imported routes)]]) +{ proto_xxable($3, XX_RELOAD_IN); } ; +CF_CLI(RELOAD OUT, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protocol (just exported routes)]]) +{ proto_xxable($3, XX_RELOAD_OUT); } ; CF_CLI_HELP(DEBUG, ..., [[Control protocol debugging]]) CF_CLI(DEBUG, proto_patt debug_mask, (<protocol> | <pattern> | all) (all | off | { states | routes | filters | events | packets }), [[Control protocol debugging]]) diff --git a/nest/proto.c b/nest/proto.c index 7bb1286..4f352a6 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -73,6 +73,10 @@ proto_relink(struct proto *p) rem_node(&p->n); switch (p->core_state) { + case FS_HUNGRY: + l = &inactive_proto_list; + break; + case FS_FEEDING: case FS_HAPPY: l = &active_proto_list; break; @@ -80,7 +84,7 @@ proto_relink(struct proto *p) l = &flush_proto_list; break; default: - l = &inactive_proto_list; + ASSERT(0); } proto_enqueue(l, p); } @@ -307,6 +311,7 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty if (sym && sym->class == SYM_PROTO && !new->shutdown) { /* Found match, let's check if we can smoothly switch to new configuration */ + /* No need to check description */ nc = sym->def; if (!force_reconfig && nc->protocol == oc->protocol @@ -548,7 +553,7 @@ proto_feed_more(void *P) } static void -proto_feed(void *P) +proto_feed_initial(void *P) { struct proto *p = P; @@ -576,16 +581,51 @@ proto_schedule_flush(struct proto *p) } static void -proto_schedule_feed(struct proto *p) +proto_schedule_feed(struct proto *p, int initial) { DBG("%s: Scheduling meal\n", p->name); p->core_state = FS_FEEDING; + p->refeeding = !initial; + + /* Hack: reset exp_routes during refeed, and do not decrease it later */ + if (!initial) + p->stats.exp_routes = 0; + proto_relink(p); - p->attn->hook = proto_feed; + p->attn->hook = initial ? proto_feed_initial : proto_feed_more; ev_schedule(p->attn); } /** + * proto_request_feeding - request feeding routes to the protocol + * @p: given protocol + * + * Sometimes it is needed to send again all routes to the + * protocol. This is called feeding and can be requested by this + * function. This would cause protocol core state transition + * to FS_FEEDING (during feeding) and when completed, it will + * switch back to FS_HAPPY. This function can be called even + * when feeding is already running, in that case it is restarted. + */ +void +proto_request_feeding(struct proto *p) +{ + ASSERT(p->proto_state == PS_UP); + + /* If we are already feeding, we want to restart it */ + if (p->core_state == FS_FEEDING) + { + /* Unless feeding is in initial state */ + if (p->attn->hook == proto_feed_initial) + return; + + rt_feed_baby_abort(p); + } + + proto_schedule_feed(p, 0); +} + +/** * proto_notify_state - notify core about protocol state change * @p: protocol the state of which has changed * @ps: the new status @@ -614,7 +654,7 @@ proto_notify_state(struct proto *p, unsigned ps) switch (ps) { case PS_DOWN: - if ((cs = FS_FEEDING) || (cs == FS_HAPPY)) + if ((cs == FS_FEEDING) || (cs == FS_HAPPY)) proto_schedule_flush(p); neigh_prune(); // FIXME convert neighbors to resource? @@ -634,10 +674,10 @@ proto_notify_state(struct proto *p, unsigned ps) case PS_UP: ASSERT(ops == PS_DOWN || ops == PS_START); ASSERT(cs == FS_HUNGRY); - proto_schedule_feed(p); + proto_schedule_feed(p, 1); break; case PS_STOP: - if ((cs = FS_FEEDING) || (cs == FS_HAPPY)) + if ((cs == FS_FEEDING) || (cs == FS_HAPPY)) proto_schedule_flush(p); break; default: @@ -702,6 +742,8 @@ proto_do_show(struct proto *p, int verbose) buf); if (verbose) { + if (p->cf->dsc) + cli_msg(-1006, " Description: %s", p->cf->dsc); 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)); @@ -788,25 +830,29 @@ proto_xxable(char *pattern, int xx) cnt++; switch (xx) { - case 0: + case XX_DISABLE: if (p->disabled) cli_msg(-8, "%s: already disabled", p->name); else { cli_msg(-9, "%s: disabled", p->name); p->disabled = 1; + proto_rethink_goal(p); } break; - case 1: + + case XX_ENABLE: if (!p->disabled) cli_msg(-10, "%s: already enabled", p->name); else { cli_msg(-11, "%s: enabled", p->name); p->disabled = 0; + proto_rethink_goal(p); } break; - case 2: + + case XX_RESTART: if (p->disabled) cli_msg(-8, "%s: already disabled", p->name); else @@ -814,13 +860,38 @@ proto_xxable(char *pattern, int xx) p->disabled = 1; proto_rethink_goal(p); p->disabled = 0; + proto_rethink_goal(p); cli_msg(-12, "%s: restarted", p->name); } break; + + case XX_RELOAD: + case XX_RELOAD_IN: + case XX_RELOAD_OUT: + if (p->disabled) + { + cli_msg(-8, "%s: already disabled", p->name); + break; + } + + /* re-importing routes */ + if (xx != XX_RELOAD_OUT) + if (! (p->reload_routes && p->reload_routes(p))) + { + cli_msg(-8006, "%s: reload failed", p->name); + break; + } + + /* re-exporting routes */ + if (xx != XX_RELOAD_IN) + proto_request_feeding(p); + + cli_msg(-15, "%s: reloading", p->name); + break; + default: ASSERT(0); } - proto_rethink_goal(p); } WALK_PROTO_LIST_END; if (!cnt) diff --git a/nest/protocol.h b/nest/protocol.h index 484df84..21a1c1b 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -81,6 +81,7 @@ struct proto_config { struct protocol *protocol; /* Protocol */ struct proto *proto; /* Instance we've created */ char *name; + char *dsc; unsigned debug, preference, disabled; /* Generic parameters */ u32 router_id; /* Protocol specific router ID */ struct rtable_config *table; /* Table we're attached to */ @@ -133,6 +134,7 @@ struct proto { 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) */ 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 */ @@ -151,6 +153,9 @@ struct proto { * It can construct a new rte, add private attributes and * decide whether the route shall be imported: 1=yes, -1=no, * 0=process it through the import filter set by the user. + * reload_routes Request protocol to reload all its routes to the core + * (using rte_update()). Returns: 0=reload cannot be done, + * 1= reload is scheduled and will happen (asynchronously). */ void (*if_notify)(struct proto *, unsigned flags, struct iface *i); @@ -160,6 +165,7 @@ struct proto { struct ea_list *(*make_tmp_attrs)(struct rte *rt, struct linpool *pool); void (*store_tmp_attrs)(struct rte *rt, struct ea_list *attrs); int (*import_control)(struct proto *, struct rte **rt, struct ea_list **attrs, struct linpool *pool); + int (*reload_routes)(struct proto *); /* * Routing entry hooks (called only for rte's belonging to this protocol): @@ -189,11 +195,19 @@ struct proto { void *proto_new(struct proto_config *, unsigned size); void *proto_config_new(struct protocol *, unsigned size); +void proto_request_feeding(struct proto *p); void proto_show(struct symbol *, int); struct proto *proto_get_named(struct symbol *, struct protocol *); void proto_xxable(char *, int); void proto_debug(char *, unsigned int); +#define XX_DISABLE 0 +#define XX_ENABLE 1 +#define XX_RESTART 2 +#define XX_RELOAD 3 +#define XX_RELOAD_IN 4 +#define XX_RELOAD_OUT 5 + static inline u32 proto_get_router_id(struct proto_config *pc) { @@ -270,6 +284,10 @@ void proto_notify_state(struct proto *p, unsigned state); * HUNGRY/DOWN --> HUNGRY/START --> HUNGRY/UP --> * FEEDING/UP --> HAPPY/UP --> FLUSHING/STOP|DOWN --> * HUNGRY/STOP|DOWN --> HUNGRY/DOWN + * + * Sometimes, protocol might switch from HAPPY/UP to FEEDING/UP + * if it wants to refeed the routes (for example BGP does so + * as a result of received ROUTE-REFRESH request). */ #define FS_HUNGRY 0 diff --git a/nest/rt-table.c b/nest/rt-table.c index 87bf0dc..df2834a 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -158,13 +158,15 @@ rte_trace_out(unsigned int flag, struct proto *p, rte *e, char *msg) } static inline void -do_rte_announce(struct announce_hook *a, int type, net *net, rte *new, rte *old, ea_list *tmpa, int class) +do_rte_announce(struct announce_hook *a, int type, net *net, rte *new, rte *old, ea_list *tmpa, int class, int refeed) { struct proto *p = a->proto; rte *new0 = new; rte *old0 = old; int ok; + int fast_exit_hack = 0; + if (new) { p->stats.exp_updates_received++; @@ -174,6 +176,7 @@ do_rte_announce(struct announce_hook *a, int type, net *net, rte *new, rte *old, { p->stats.exp_updates_rejected++; drop_reason = "out of scope"; + fast_exit_hack = 1; } else if ((ok = p->import_control ? p->import_control(p, &new, &tmpa, rte_update_pool) : 0) < 0) { @@ -199,7 +202,32 @@ do_rte_announce(struct announce_hook *a, int type, net *net, rte *new, rte *old, else p->stats.exp_withdraws_received++; - if (old) + /* Hack: This is here to prevent 'spurious withdraws' + for loopback addresses during reload. */ + if (fast_exit_hack) + return; + + /* + * This is a tricky part - we don't know whether route 'old' was + * exported to protocol 'p' or was filtered by the export filter. + * We try tu run the export filter to know this to have a correct + * value in 'old' argument of rt_update (and proper filter value) + * + * FIXME - this is broken because 'configure soft' may change + * filters but keep routes. Refeed is expected to be called after + * change of the filters and with old == new, therefore we do not + * even try to run the filter on an old route, This may lead to + * 'spurious withdraws' but ensure that there are no 'missing + * withdraws'. + * + * This is not completely safe as there is a window between + * reconfiguration and the end of refeed - if a newly filtered + * route disappears during this period, proper withdraw is not + * sent (because old would be also filtered) and the route is + * not refeeded (because it disappeared before that). + */ + + if (old && !refeed) { if (p->out_filter == FILTER_REJECT) old = NULL; @@ -216,6 +244,7 @@ do_rte_announce(struct announce_hook *a, int type, net *net, rte *new, rte *old, } } + /* FIXME - This is broken because of incorrect 'old' value (see above) */ if (!new && !old) return; @@ -224,9 +253,11 @@ do_rte_announce(struct announce_hook *a, int type, net *net, rte *new, rte *old, else p->stats.exp_withdraws_accepted++; + /* Hack: We do not decrease exp_routes during refeed, we instead + reset exp_routes at the start of refeed. */ if (new) p->stats.exp_routes++; - if (old) + if (old && !refeed) p->stats.exp_routes--; if (p->debug & D_ROUTES) @@ -304,7 +335,7 @@ rte_announce(rtable *tab, int type, net *net, rte *new, rte *old, ea_list *tmpa) { ASSERT(a->proto->core_state == FS_HAPPY || a->proto->core_state == FS_FEEDING); if (a->proto->accept_ra_types == type) - do_rte_announce(a, type, net, new, old, tmpa, class); + do_rte_announce(a, type, net, new, old, tmpa, class, 0); } } @@ -394,6 +425,26 @@ rte_recalculate(rtable *table, net *net, struct proto *p, struct proto *src, rte { if (old->attrs->proto == src) { + /* If there is the same route in the routing table but from + * a different sender, then there are two paths from the + * source protocol to this routing table through transparent + * pipes, which is not allowed. + * + * We log that and ignore the route. If it is withdraw, we + * ignore it completely (there might be 'spurious withdraws', + * see FIXME in do_rte_announce()) + */ + if (old->sender != p) + { + if (new) + { + log(L_ERR "Pipe collision detected when sending %I/%d to table %s", + net->n.prefix, net->n.pxlen, table->name); + rte_free_quick(new); + } + return; + } + if (new && rte_same(old, new)) { /* No changes, ignore the new route */ @@ -495,11 +546,10 @@ rte_recalculate(rtable *table, net *net, struct proto *p, struct proto *src, rte net->routes->next = new; rte_trace_in(D_ROUTES, p, new, "added"); } - else if (old && (p->debug & D_ROUTES)) - { - /* Not really a case - the list of routes is correct, we just - log the route removal */ + /* Log the route removal */ + if (!new && old && (p->debug & D_ROUTES)) + { if (old != old_best) rte_trace_in(D_ROUTES, p, old, "removed"); else if (net->routes) @@ -589,11 +639,12 @@ rte_update(rtable *table, net *net, struct proto *p, struct proto *src, rte *new new->sender = p; struct filter *filter = p->in_filter; - /* Do not filter routes going to the secondary side of the pipe, - that should only go through export filter. - FIXME Make a better check whether p is really a pipe. */ - if (p->table != table) + /* Do not filter routes going through the pipe, + they are filtered in the export filter only. */ +#ifdef CONFIG_PIPE + if (p->proto == &proto_pipe) filter = FILTER_ACCEPT; +#endif p->stats.imp_updates_received++; if (!rte_validate(new)) @@ -636,6 +687,7 @@ rte_update(rtable *table, net *net, struct proto *p, struct proto *src, rte *new drop: rte_free(new); + rte_recalculate(table, net, p, src, NULL, NULL); rte_update_unlock(); } @@ -943,7 +995,7 @@ do_feed_baby(struct proto *p, int type, struct announce_hook *h, net *n, rte *e) rte_update_lock(); tmpa = q->make_tmp_attrs ? q->make_tmp_attrs(e, rte_update_pool) : NULL; - do_rte_announce(h, type, n, e, NULL, tmpa, ipa_classify(n->n.prefix)); + do_rte_announce(h, type, n, e, p->refeeding ? e : NULL, tmpa, ipa_classify(n->n.prefix), p->refeeding); rte_update_unlock(); } @@ -1121,6 +1173,11 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d) ok = 0; else if (!ic && d->export_mode > 1) { + /* FIXME - this shows what should be exported according + to current filters, but not what was really exported. + 'configure soft' command may change the export filter + and do not update routes */ + if (p1->out_filter == FILTER_REJECT || p1->out_filter && f_run(p1->out_filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT) ok = 0; |