summaryrefslogtreecommitdiff
path: root/nest
diff options
context:
space:
mode:
Diffstat (limited to 'nest')
-rw-r--r--nest/config.Y16
-rw-r--r--nest/proto.c93
-rw-r--r--nest/protocol.h18
-rw-r--r--nest/rt-table.c83
4 files changed, 182 insertions, 28 deletions
diff --git a/nest/config.Y b/nest/config.Y
index 4721112a..dbb10ada 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 7bb1286d..4f352a6f 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 484df846..21a1c1b4 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 87bf0dc7..df2834a6 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;