diff --git a/nest/proto-hooks.c b/nest/proto-hooks.c index 82df5cb7..9af6ef5f 100644 --- a/nest/proto-hooks.c +++ b/nest/proto-hooks.c @@ -188,6 +188,8 @@ void ifa_notify(struct proto *p, unsigned flags, struct ifa *a) * belonging to network @net being replaced by a new route @new with * extended attributes @attrs. Either @new or @old or both can be %NULL * if the corresponding route doesn't exist. + * + * FIXME documentation */ void rt_notify(struct proto *p, net *net, rte *new, rte *old, ea_list *attrs) { DUMMY; } diff --git a/nest/protocol.h b/nest/protocol.h index d681ae68..865b2b38 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -100,6 +100,7 @@ struct proto { unsigned debug; /* Debugging flags */ unsigned preference; /* Default route preference */ int min_scope; /* Minimal route scope accepted */ + 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) */ diff --git a/nest/route.h b/nest/route.h index 43cfa9dd..e55ae1d1 100644 --- a/nest/route.h +++ b/nest/route.h @@ -146,6 +146,7 @@ typedef struct network { typedef struct rte { struct rte *next; net *net; /* Network this RTE belongs to */ + struct proto *sender; /* Protocol instance that sent the route to the routing table */ struct rta *attrs; /* Attributes of this route */ byte flags; /* Flags (REF_...) */ byte pflags; /* Protocol-specific flags */ @@ -178,6 +179,10 @@ typedef struct rte { #define REF_COW 1 /* Copy this rte on write */ +/* Types of route announcement, also used as flags */ +#define RA_OPTIMAL 1 /* Announcement of optimal route change */ +#define RA_ANY 2 /* Announcement of any route change */ + struct config; void rt_init(void); @@ -191,6 +196,7 @@ static inline net *net_get(rtable *tab, ip_addr addr, unsigned len) { return (ne rte *rte_find(net *net, struct proto *p); rte *rte_get_temp(struct rta *); void rte_update(rtable *tab, net *net, struct proto *p, rte *new); +void rte_update2(rtable *tab, net *net, struct proto *p, struct proto *src, rte *new); void rte_discard(rtable *tab, rte *old); void rte_dump(rte *); void rte_free(rte *); @@ -230,7 +236,7 @@ void rt_show(struct rt_show_data *); typedef struct rta { struct rta *next, **pprev; /* Hash chain */ - struct proto *proto; /* Protocol instance */ + struct proto *proto; /* Protocol instance that originally created the route */ unsigned uc; /* Use count */ byte source; /* Route source (RTS_...) */ byte scope; /* Route scope (SCOPE_... -- see ip.h) */ diff --git a/nest/rt-table.c b/nest/rt-table.c index b0781a33..176e2dee 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -158,7 +158,7 @@ rte_trace_out(unsigned int flag, struct proto *p, rte *e, char *msg) } static inline void -do_rte_announce(struct announce_hook *a, 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) { struct proto *p = a->proto; rte *new0 = new; @@ -234,14 +234,17 @@ do_rte_announce(struct announce_hook *a, net *net, rte *new, rte *old, ea_list * /** * rte_announce - announce a routing table change * @tab: table the route has been added to + * @type: type of route announcement (RA_OPTIMAL or RA_ANY) * @net: network in question * @new: the new route to be announced - * @old: previous optimal route for the same network + * @old: the previous route for the same network * @tmpa: a list of temporary attributes belonging to the new route * * This function gets a routing table update and announces it * to all protocols connected to the same table by their announcement hooks. * + * previous optimal route for the same network FIXME + * * For each such protocol, we first call its import_control() hook which * performs basic checks on the route (each protocol has a right to veto * or force accept of the route before any filter is asked) and adds default @@ -250,7 +253,7 @@ do_rte_announce(struct announce_hook *a, net *net, rte *new, rte *old, ea_list * * route, the rt_notify() hook of the protocol gets called. */ static void -rte_announce(rtable *tab, net *net, rte *new, rte *old, ea_list *tmpa) +rte_announce(rtable *tab, int type, net *net, rte *new, rte *old, ea_list *tmpa) { struct announce_hook *a; int class = ipa_classify(net->n.prefix); @@ -258,7 +261,8 @@ rte_announce(rtable *tab, net *net, rte *new, rte *old, ea_list *tmpa) WALK_LIST(a, tab->hooks) { ASSERT(a->proto->core_state == FS_HAPPY || a->proto->core_state == FS_FEEDING); - do_rte_announce(a, net, new, old, tmpa, class); + if (a->proto->accept_ra_types == type) + do_rte_announce(a, type, net, new, old, tmpa, class); } } @@ -271,7 +275,7 @@ rte_validate(rte *e) if (ipa_nonzero(ipa_and(n->n.prefix, ipa_not(ipa_mkmask(n->n.pxlen))))) { log(L_BUG "Ignoring bogus prefix %I/%d received via %s", - n->n.prefix, n->n.pxlen, e->attrs->proto->name); + n->n.prefix, n->n.pxlen, e->sender->name); return 0; } if (n->n.pxlen) @@ -290,14 +294,14 @@ rte_validate(rte *e) return 1; } log(L_WARN "Ignoring bogus route %I/%d received via %s", - n->n.prefix, n->n.pxlen, e->attrs->proto->name); + n->n.prefix, n->n.pxlen, e->sender->name); return 0; } - if ((c & IADDR_SCOPE_MASK) < e->attrs->proto->min_scope) + if ((c & IADDR_SCOPE_MASK) < e->sender->min_scope) { log(L_WARN "Ignoring %s scope route %I/%d received from %I via %s", ip_scope_text(c & IADDR_SCOPE_MASK), - n->n.prefix, n->n.pxlen, e->attrs->from, e->attrs->proto->name); + n->n.prefix, n->n.pxlen, e->attrs->from, e->sender->name); return 0; } } @@ -337,7 +341,7 @@ rte_same(rte *x, rte *y) } static void -rte_recalculate(rtable *table, net *net, struct proto *p, rte *new, ea_list *tmpa) +rte_recalculate(rtable *table, net *net, struct proto *p, struct proto *src, rte *new, ea_list *tmpa) { rte *old_best = net->routes; rte *old = NULL; @@ -346,7 +350,7 @@ rte_recalculate(rtable *table, net *net, struct proto *p, rte *new, ea_list *tmp k = &net->routes; /* Find and remove original route from the same protocol */ while (old = *k) { - if (old->attrs->proto == p) + if (old->attrs->proto == src) { if (new && rte_same(old, new)) { @@ -362,10 +366,14 @@ rte_recalculate(rtable *table, net *net, struct proto *p, rte *new, ea_list *tmp k = &old->next; } + log(L_WARN "ANNOUNCE %I/%d from %s (%s) %p", net->n.prefix, net->n.pxlen, p->name, src->name, old_best); + + rte_announce(table, RA_ANY, net, new, old, tmpa); + if (new && rte_better(new, old_best)) /* It's a new optimal route => announce and relink it */ { rte_trace_in(D_ROUTES, p, new, "added [best]"); - rte_announce(table, net, new, old_best, tmpa); + rte_announce(table, RA_OPTIMAL, net, new, old_best, tmpa); new->next = net->routes; net->routes = new; } @@ -377,7 +385,7 @@ rte_recalculate(rtable *table, net *net, struct proto *p, rte *new, ea_list *tmp for(s=net->routes; s; s=s->next) if (rte_better(s, r)) r = s; - rte_announce(table, net, r, old_best, tmpa); + rte_announce(table, RA_OPTIMAL, net, r, old_best, tmpa); if (r) /* Re-link the new optimal route */ { k = &net->routes; @@ -477,12 +485,19 @@ rte_update_unlock(void) */ void rte_update(rtable *table, net *net, struct proto *p, rte *new) +{ + rte_update2(table, net, p, p, new); +} + +void +rte_update2(rtable *table, net *net, struct proto *p, struct proto *src, rte *new) { ea_list *tmpa = NULL; rte_update_lock(); if (new) { + new->sender = p; struct filter *filter = p->in_filter; /* Do not filter routes going to the secondary side of the pipe, @@ -501,8 +516,8 @@ rte_update(rtable *table, net *net, struct proto *p, rte *new) rte_trace_in(D_FILTERS, p, new, "filtered out"); goto drop; } - if (p->make_tmp_attrs) - tmpa = p->make_tmp_attrs(new, rte_update_pool); + if (src->make_tmp_attrs) + tmpa = src->make_tmp_attrs(new, rte_update_pool); if (filter) { ea_list *old_tmpa = tmpa; @@ -512,14 +527,14 @@ rte_update(rtable *table, net *net, struct proto *p, rte *new) rte_trace_in(D_FILTERS, p, new, "filtered out"); goto drop; } - if (tmpa != old_tmpa && p->store_tmp_attrs) - p->store_tmp_attrs(new, tmpa); + if (tmpa != old_tmpa && src->store_tmp_attrs) + src->store_tmp_attrs(new, tmpa); } if (!(new->attrs->aflags & RTAF_CACHED)) /* Need to copy attributes */ new->attrs = rta_lookup(new->attrs); new->flags |= REF_COW; } - rte_recalculate(table, net, p, new, tmpa); + rte_recalculate(table, net, p, src, new, tmpa); rte_update_unlock(); return; @@ -531,11 +546,8 @@ drop: void rte_discard(rtable *t, rte *old) /* Non-filtered route deletion, used during garbage collection */ { - net *n = old->net; - struct proto *p = old->attrs->proto; - rte_update_lock(); - rte_recalculate(t, n, p, NULL, NULL); + rte_recalculate(t, old->net, old->sender, old->attrs->proto, NULL, NULL); rte_update_unlock(); } @@ -673,8 +685,8 @@ again: ncnt++; rescan: for (e=n->routes; e; e=e->next, rcnt++) - if (e->attrs->proto->core_state != FS_HAPPY && - e->attrs->proto->core_state != FS_FEEDING) + if (e->sender->core_state != FS_HAPPY && + e->sender->core_state != FS_FEEDING) { rte_discard(tab, e); rdel++; @@ -827,6 +839,18 @@ rt_commit(struct config *new, struct config *old) DBG("\tdone\n"); } +static inline void +do_feed_baby(struct proto *p, int type, struct announce_hook *h, net *n, rte *e) +{ + struct proto *q = e->attrs->proto; + ea_list *tmpa; + + 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)); + rte_update_unlock(); +} + /** * rt_feed_baby - advertise routes to a new protocol * @p: protocol to be fed @@ -865,19 +889,24 @@ again: FIB_ITERATE_PUT(fit, fn); return 0; } - if (e) - { - struct proto *q = e->attrs->proto; - ea_list *tmpa; - if (p->core_state != FS_FEEDING) - return 1; /* In the meantime, the protocol fell down. */ - rte_update_lock(); - tmpa = q->make_tmp_attrs ? q->make_tmp_attrs(e, rte_update_pool) : NULL; - do_rte_announce(h, n, e, NULL, tmpa, ipa_classify(n->n.prefix)); - rte_update_unlock(); - max_feed--; - } + if (p->accept_ra_types == RA_OPTIMAL) + if (e) + { + if (p->core_state != FS_FEEDING) + return 1; /* In the meantime, the protocol fell down. */ + do_feed_baby(p, RA_OPTIMAL, h, n, e); + max_feed--; + } + + if (p->accept_ra_types == RA_ANY) + for(e = n->routes; e != NULL; e = e->next) + { + if (p->core_state != FS_FEEDING) + return 1; /* In the meantime, the protocol fell down. */ + do_feed_baby(p, RA_ANY, h, n, e); + max_feed--; + } } FIB_ITERATE_END(fn); p->feed_ahook = h->next; diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index cbc699bb..b42ea302 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -753,6 +753,7 @@ bgp_init(struct proto_config *C) struct proto *P = proto_new(C, sizeof(struct bgp_proto)); struct bgp_proto *p = (struct bgp_proto *) P; + P->accept_ra_types = RA_OPTIMAL; P->rt_notify = bgp_rt_notify; P->rte_better = bgp_rte_better; P->import_control = bgp_import_control; diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c index 0cab1d7b..69d37241 100644 --- a/proto/ospf/ospf.c +++ b/proto/ospf/ospf.c @@ -224,6 +224,7 @@ ospf_init(struct proto_config *c) p->import_control = ospf_import_control; p->make_tmp_attrs = ospf_make_tmp_attrs; p->store_tmp_attrs = ospf_store_tmp_attrs; + p->accept_ra_types = RA_OPTIMAL; p->rt_notify = ospf_rt_notify; p->if_notify = ospf_iface_notify; p->rte_better = ospf_rte_better; diff --git a/proto/pipe/pipe.c b/proto/pipe/pipe.c index d1d6bba9..0240d16e 100644 --- a/proto/pipe/pipe.c +++ b/proto/pipe/pipe.c @@ -31,12 +31,16 @@ #include "pipe.h" static void -pipe_send(struct pipe_proto *p, rtable *dest, net *n, rte *new, rte *old UNUSED, ea_list *attrs) +pipe_send(struct pipe_proto *p, rtable *dest, net *n, rte *new, rte *old, ea_list *attrs) { + struct proto *src; net *nn; rte *e; rta a; + if (!new && !old) + return; + if (dest->pipe_busy) { log(L_ERR "Pipe loop detected when sending %I/%d to table %s", @@ -47,17 +51,24 @@ pipe_send(struct pipe_proto *p, rtable *dest, net *n, rte *new, rte *old UNUSED, if (new) { memcpy(&a, new->attrs, sizeof(rta)); - a.proto = &p->p; - a.source = RTS_PIPE; a.aflags = 0; a.eattrs = attrs; e = rte_get_temp(&a); e->net = nn; + + /* Copy protocol specific embedded attributes. */ + memcpy(&(e->u), &(new->u), sizeof(e->u)); + + src = new->attrs->proto; } else - e = NULL; + { + e = NULL; + src = old->attrs->proto; + } + dest->pipe_busy = 1; - rte_update(dest, nn, &p->p, e); + rte_update2(dest, nn, &p->p, src, e); dest->pipe_busy = 0; } @@ -82,7 +93,7 @@ pipe_rt_notify_sec(struct proto *P, net *net, rte *new, rte *old, ea_list *attrs static int pipe_import_control(struct proto *P, rte **ee, ea_list **ea UNUSED, struct linpool *p UNUSED) { - struct proto *pp = (*ee)->attrs->proto; + struct proto *pp = (*ee)->sender; if (pp == P || pp == &((struct pipe_proto *) P)->phantom->p) return -1; /* Avoid local loops automatically */ @@ -106,6 +117,7 @@ pipe_start(struct proto *P) memcpy(ph, p, sizeof(struct pipe_proto)); p->phantom = ph; ph->phantom = p; + ph->p.accept_ra_types = RA_ANY; ph->p.rt_notify = pipe_rt_notify_sec; ph->p.proto_state = PS_UP; ph->p.core_state = ph->p.core_goal = FS_HAPPY; @@ -141,6 +153,7 @@ pipe_init(struct proto_config *C) struct pipe_proto *p = (struct pipe_proto *) P; p->peer = c->peer->table; + P->accept_ra_types = RA_ANY; P->rt_notify = pipe_rt_notify_pri; P->import_control = pipe_import_control; return P; diff --git a/proto/rip/rip.c b/proto/rip/rip.c index 12cc8783..ab417f01 100644 --- a/proto/rip/rip.c +++ b/proto/rip/rip.c @@ -946,6 +946,7 @@ rip_rte_remove(net *net UNUSED, rte *rte) void rip_init_instance(struct proto *p) { + p->accept_ra_types = RA_OPTIMAL; p->if_notify = rip_if_notify; p->rt_notify = rip_rt_notify; p->import_control = rip_import_control; diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c index a60e7f91..0b55b40b 100644 --- a/sysdep/unix/krt.c +++ b/sysdep/unix/krt.c @@ -875,6 +875,7 @@ krt_init(struct proto_config *c) { struct krt_proto *p = proto_new(c, sizeof(struct krt_proto)); + p->p.accept_ra_types = RA_OPTIMAL; p->p.rt_notify = krt_notify; p->p.min_scope = SCOPE_HOST; return &p->p;