From f0507f05ce57398e135651896dace4cb68eeed54 Mon Sep 17 00:00:00 2001 From: Maria Matejka Date: Mon, 27 Sep 2021 16:40:28 +0200 Subject: [PATCH] Route sources have an explicit owner This commit prevents use-after-free of routes belonging to protocols which have been already destroyed, delaying also all the protocols' shutdown until all of their routes have been finally propagated through all the pipes down to the appropriate exports. The use-after-free was somehow hypothetic yet theoretically possible in rare conditions, when one BGP protocol authors a lot of routes and the user deletes that protocol by reconfiguring in the same time as next hop update is requested, causing rte_better() to be called on a not-yet-pruned network prefix while the owner protocol has been already freed. In parallel execution environments, this would happen an inter-thread use-after-free, causing possible heisenbugs or other nasty problems. --- filter/f-inst.c | 5 +- lib/locking.h | 1 + nest/proto.c | 12 +++- nest/protocol.h | 4 +- nest/route.h | 62 ++++++++++++++++++--- nest/rt-attr.c | 125 ++++++++++++++++++++++++++++++++++-------- nest/rt-dev.c | 2 + nest/rt-show.c | 6 +- nest/rt-table.c | 37 +++++-------- proto/babel/babel.c | 16 ++++-- proto/bgp/attrs.c | 20 +++---- proto/bgp/bgp.c | 15 +++-- proto/bgp/bgp.h | 7 +++ proto/bgp/packets.c | 5 ++ proto/mrt/mrt.c | 4 +- proto/ospf/ospf.c | 13 +++-- proto/ospf/ospf.h | 2 + proto/pipe/pipe.c | 2 +- proto/rip/rip.c | 25 +++++++-- proto/static/static.c | 27 +++++++-- sysdep/unix/krt.c | 5 +- 21 files changed, 290 insertions(+), 105 deletions(-) diff --git a/filter/f-inst.c b/filter/f-inst.c index 00e22383..706eb684 100644 --- a/filter/f-inst.c +++ b/filter/f-inst.c @@ -526,7 +526,7 @@ case SA_FROM: RESULT(sa.f_type, ip, rta->from); break; case SA_GW: RESULT(sa.f_type, ip, rta->nh.gw); break; case SA_NET: RESULT(sa.f_type, net, fs->rte->net); break; - case SA_PROTO: RESULT(sa.f_type, s, fs->rte->src->proto->name); break; + case SA_PROTO: RESULT(sa.f_type, s, fs->rte->src->owner->name); break; case SA_SOURCE: RESULT(sa.f_type, i, rta->source); break; case SA_SCOPE: RESULT(sa.f_type, i, rta->scope); break; case SA_DEST: RESULT(sa.f_type, i, rta->dest); break; @@ -562,7 +562,8 @@ { ip_addr ip = v1.val.ip; struct iface *ifa = ipa_is_link_local(ip) ? rta->nh.iface : NULL; - neighbor *n = neigh_find(fs->rte->src->proto, ip, ifa, 0); + /* XXX this code supposes that every owner is a protocol XXX */ + neighbor *n = neigh_find(SKIP_BACK(struct proto, sources, fs->rte->src->owner), ip, ifa, 0); if (!n || (n->scope == SCOPE_HOST)) runtime( "Invalid gw address" ); diff --git a/lib/locking.h b/lib/locking.h index 0cbdead8..0a69f50f 100644 --- a/lib/locking.h +++ b/lib/locking.h @@ -16,6 +16,7 @@ struct lock_order { struct domain_generic *the_bird; struct domain_generic *proto; struct domain_generic *rtable; + struct domain_generic *attrs; struct domain_generic *cork; struct domain_generic *event; }; diff --git a/nest/proto.c b/nest/proto.c index f8e1ba31..930fad1d 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -2329,10 +2329,17 @@ channel_reset_limit(struct channel *c, struct limit *l, int dir) c->limit_active &= ~(1 << dir); } +static struct rte_owner_class default_rte_owner_class; + static inline void proto_do_start(struct proto *p) { p->active = 1; + + rt_init_sources(&p->sources, p->name, proto_event_list(p)); + if (!p->sources.class) + p->sources.class = &default_rte_owner_class; + if (!p->cf->late_if_feed) if_feed_baby(p); } @@ -2341,10 +2348,8 @@ static void proto_do_up(struct proto *p) { if (!p->main_source) - { p->main_source = rt_get_source(p, 0); - rt_lock_source(p->main_source); - } + // Locked automaticaly proto_start_channels(p); @@ -2371,6 +2376,7 @@ proto_do_stop(struct proto *p) } proto_stop_channels(p); + rt_destroy_sources(&p->sources, p->event); p->do_stop = 1; proto_send_event(p); diff --git a/nest/protocol.h b/nest/protocol.h index 981ca96a..440297a1 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -78,7 +78,6 @@ struct protocol { int (*start)(struct proto *); /* Start the instance */ int (*shutdown)(struct proto *); /* Stop the instance */ void (*get_status)(struct proto *, byte *buf); /* Get instance status (for `show protocols' command) */ - void (*get_route_info)(struct rte *, byte *buf); /* Get route information (for `show route' command) */ int (*get_attr)(const struct eattr *, byte *buf, int buflen); /* ASCIIfy dynamic attribute (returns GA_*) */ void (*show_proto_info)(struct proto *); /* Show protocol info (for `show protocols all' command) */ void (*copy_config)(struct proto_config *, struct proto_config *); /* Copy config from given protocol instance */ @@ -146,6 +145,7 @@ struct proto { list channels; /* List of channels to rtables (struct channel) */ struct channel *main_channel; /* Primary channel */ struct rte_src *main_source; /* Primary route source */ + struct rte_owner sources; /* Route source owner structure */ struct iface *vrf; /* Related VRF instance, NULL if global */ const char *name; /* Name of this instance (== cf->name) */ @@ -360,7 +360,7 @@ void proto_notify_state(struct proto *p, unsigned state); */ static inline int proto_is_inactive(struct proto *p) -{ return (p->active_channels == 0) && (p->active_coroutines == 0); } +{ return (p->active_channels == 0) && (p->active_coroutines == 0) && (p->sources.uc == 0); } /* diff --git a/nest/route.h b/nest/route.h index 310cea92..a01eff1a 100644 --- a/nest/route.h +++ b/nest/route.h @@ -15,6 +15,8 @@ #include "lib/bitmap.h" #include "lib/resource.h" #include "lib/net.h" +#include "lib/hash.h" +#include "lib/event.h" #include @@ -579,10 +581,10 @@ struct nexthop { struct rte_src { struct rte_src *next; /* Hash chain */ - struct proto *proto; /* Protocol the source is based on */ + struct rte_owner *owner; /* Route source owner */ u32 private_id; /* Private ID, assigned by the protocol */ u32 global_id; /* Globally unique ID of the source */ - unsigned uc; /* Use count */ + _Atomic u64 uc; /* Use count */ }; @@ -720,11 +722,57 @@ typedef struct ea_list { #define EALF_BISECT 2 /* Use interval bisection for searching */ #define EALF_CACHED 4 /* Attributes belonging to cached rta */ -struct rte_src *rt_find_source(struct proto *p, u32 id); -struct rte_src *rt_get_source(struct proto *p, u32 id); -static inline void rt_lock_source(struct rte_src *src) { src->uc++; } -static inline void rt_unlock_source(struct rte_src *src) { src->uc--; } -void rt_prune_sources(void); +struct rte_owner_class { + void (*get_route_info)(struct rte *, byte *buf); /* Get route information (for `show route' command) */ + int (*rte_better)(struct rte *, struct rte *); + int (*rte_mergable)(struct rte *, struct rte *); + u32 (*rte_igp_metric)(struct rte *); +}; + +struct rte_owner { + struct rte_owner_class *class; + int (*rte_recalculate)(struct rtable *, struct network *, struct rte *, struct rte *, struct rte *); + HASH(struct rte_src) hash; + const char *name; + u32 hash_key; + u32 uc; + event_list *list; + event *prune; + event *stop; +}; + +DEFINE_DOMAIN(attrs); +extern DOMAIN(attrs) attrs_domain; + +#define RTA_LOCK LOCK_DOMAIN(attrs, attrs_domain) +#define RTA_UNLOCK UNLOCK_DOMAIN(attrs, attrs_domain) + +#define RTE_SRC_PU_SHIFT 44 +#define RTE_SRC_IN_PROGRESS (1ULL << RTE_SRC_PU_SHIFT) + +struct rte_src *rt_get_source_o(struct rte_owner *o, u32 id); +#define rt_get_source(p, id) rt_get_source_o(&(p)->sources, (id)) +static inline void rt_lock_source(struct rte_src *src) +{ + u64 uc = atomic_fetch_add_explicit(&src->uc, 1, memory_order_acq_rel); + ASSERT_DIE(uc > 0); +} + +static inline void rt_unlock_source(struct rte_src *src) +{ + u64 uc = atomic_fetch_add_explicit(&src->uc, RTE_SRC_IN_PROGRESS, memory_order_acq_rel); + u64 pending = uc >> RTE_SRC_PU_SHIFT; + uc &= RTE_SRC_IN_PROGRESS - 1; + + ASSERT_DIE(uc > pending); + if (uc == pending + 1) + ev_send(src->owner->list, src->owner->prune); + + atomic_fetch_sub_explicit(&src->uc, RTE_SRC_IN_PROGRESS + 1, memory_order_acq_rel); +} + +void rt_init_sources(struct rte_owner *, const char *name, event_list *list); +void rt_destroy_sources(struct rte_owner *, event *); struct ea_walk_state { ea_list *eattrs; /* Ccurrent ea_list, initially set by caller */ diff --git a/nest/rt-attr.c b/nest/rt-attr.c index 77fd3c3b..f7e33d72 100644 --- a/nest/rt-attr.c +++ b/nest/rt-attr.c @@ -85,6 +85,8 @@ const char * rta_dest_names[RTD_MAX] = { [RTD_PROHIBIT] = "prohibited", }; +DOMAIN(attrs) attrs_domain; + pool *rta_pool; static slab *rta_slab_[4]; @@ -96,16 +98,14 @@ static struct idm src_ids; /* rte source hash */ -#define RSH_KEY(n) n->proto, n->private_id +#define RSH_KEY(n) n->private_id #define RSH_NEXT(n) n->next -#define RSH_EQ(p1,n1,p2,n2) p1 == p2 && n1 == n2 -#define RSH_FN(p,n) p->hash_key ^ u32_hash(n) +#define RSH_EQ(n1,n2) n1 == n2 +#define RSH_FN(n) u32_hash(n) #define RSH_REHASH rte_src_rehash #define RSH_PARAMS /2, *2, 1, 1, 8, 20 -#define RSH_INIT_ORDER 6 - -static HASH(struct rte_src) src_hash; +#define RSH_INIT_ORDER 2 static void rte_src_init(void) @@ -113,55 +113,134 @@ rte_src_init(void) rte_src_slab = sl_new(rta_pool, sizeof(struct rte_src)); idm_init(&src_ids, rta_pool, SRC_ID_INIT_SIZE); - - HASH_INIT(src_hash, rta_pool, RSH_INIT_ORDER); } - HASH_DEFINE_REHASH_FN(RSH, struct rte_src) -struct rte_src * -rt_find_source(struct proto *p, u32 id) +static struct rte_src * +rt_find_source(struct rte_owner *p, u32 id) { - return HASH_FIND(src_hash, RSH, p, id); + return HASH_FIND(p->hash, RSH, id); } struct rte_src * -rt_get_source(struct proto *p, u32 id) +rt_get_source_o(struct rte_owner *p, u32 id) { + if (p->stop) + bug("Stopping route owner asked for another source."); + struct rte_src *src = rt_find_source(p, id); if (src) + { + UNUSED u64 uc = atomic_fetch_add_explicit(&src->uc, 1, memory_order_acq_rel); return src; + } + RTA_LOCK; src = sl_allocz(rte_src_slab); - src->proto = p; + src->owner = p; src->private_id = id; src->global_id = idm_alloc(&src_ids); - src->uc = 0; - HASH_INSERT2(src_hash, RSH, rta_pool, src); + atomic_store_explicit(&src->uc, 1, memory_order_release); + p->uc++; + + HASH_INSERT2(p->hash, RSH, rta_pool, src); + if (config->table_debug) + log(L_TRACE "Allocated new rte_src for %s, ID %uL %uG, have %u sources now", + p->name, src->private_id, src->global_id, p->uc); + + RTA_UNLOCK; return src; } -void -rt_prune_sources(void) +static inline void +rt_done_sources(struct rte_owner *o) { - HASH_WALK_FILTER(src_hash, next, src, sp) + if (o->stop->list) + ev_send(o->stop->list, o->stop); + else + ev_send(o->list, o->stop); +} + +void +rt_prune_sources(void *data) +{ + struct rte_owner *o = data; + + HASH_WALK_FILTER(o->hash, next, src, sp) { - if (src->uc == 0) + u64 uc; + while ((uc = atomic_load_explicit(&src->uc, memory_order_acquire)) >> RTE_SRC_PU_SHIFT) + ; + + if (uc == 0) { - HASH_DO_REMOVE(src_hash, RSH, sp); + o->uc--; + + HASH_DO_REMOVE(o->hash, RSH, sp); + + RTA_LOCK; idm_free(&src_ids, src->global_id); sl_free(rte_src_slab, src); + RTA_UNLOCK; } } HASH_WALK_FILTER_END; - HASH_MAY_RESIZE_DOWN(src_hash, RSH, rta_pool); + RTA_LOCK; + HASH_MAY_RESIZE_DOWN(o->hash, RSH, rta_pool); + + if (o->stop && !o->uc) + { + rfree(o->prune); + RTA_UNLOCK; + + if (config->table_debug) + log(L_TRACE "All rte_src's for %s pruned, scheduling stop event", o->name); + + rt_done_sources(o); + } + else + RTA_UNLOCK; } +void +rt_init_sources(struct rte_owner *o, const char *name, event_list *list) +{ + RTA_LOCK; + HASH_INIT(o->hash, rta_pool, RSH_INIT_ORDER); + o->hash_key = random_u32(); + o->uc = 0; + o->name = name; + o->prune = ev_new_init(rta_pool, rt_prune_sources, o); + o->stop = NULL; + o->list = list; + RTA_UNLOCK; +} + +void +rt_destroy_sources(struct rte_owner *o, event *done) +{ + o->stop = done; + + if (!o->uc) + { + if (config->table_debug) + log(L_TRACE "Source owner %s destroy requested. All rte_src's already pruned, scheduling stop event", o->name); + + RTA_LOCK; + rfree(o->prune); + RTA_UNLOCK; + + rt_done_sources(o); + } + else + if (config->table_debug) + log(L_TRACE "Source owner %s destroy requested. Remaining %u rte_src's to prune.", o->name, o->uc); +} /* * Multipath Next Hop @@ -1328,6 +1407,8 @@ rta_show(struct cli *c, rta *a) void rta_init(void) { + attrs_domain = DOMAIN_NEW(attrs, "Attributes"); + rta_pool = rp_new(&root_pool, "Attributes"); rta_slab_[0] = sl_new(rta_pool, sizeof(rta)); diff --git a/nest/rt-dev.c b/nest/rt-dev.c index 5d1e57b3..c1251675 100644 --- a/nest/rt-dev.c +++ b/nest/rt-dev.c @@ -68,6 +68,7 @@ dev_ifa_notify(struct proto *P, uint flags, struct ifa *ad) /* Use iface ID as local source ID */ struct rte_src *src = rt_get_source(P, ad->iface->index); rte_update(c, net, NULL, src); + rt_unlock_source(src); } else if (flags & IF_CHANGE_UP) { @@ -93,6 +94,7 @@ dev_ifa_notify(struct proto *P, uint flags, struct ifa *ad) }; rte_update(c, net, &e0, src); + rt_unlock_source(src); } } diff --git a/nest/rt-show.c b/nest/rt-show.c index d942b8e1..8196903d 100644 --- a/nest/rt-show.c +++ b/nest/rt-show.c @@ -56,7 +56,7 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, int primary if (d->verbose && !rta_is_cached(a) && a->eattrs) ea_normalize(a->eattrs); - get_route_info = e->src->proto->proto->get_route_info; + get_route_info = e->src->owner->class ? e->src->owner->class->get_route_info : NULL; if (get_route_info) get_route_info(e, info); else @@ -66,7 +66,7 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, int primary rt_show_table(c, d); cli_printf(c, -1007, "%-20s %s [%s %s%s]%s%s", ia, rta_dest_name(a->dest), - e->src->proto->name, tm, from, primary ? (sync_error ? " !" : " *") : "", info); + e->src->owner->name, tm, from, primary ? (sync_error ? " !" : " *") : "", info); if (a->dest == RTD_UNICAST) for (nh = &(a->nh); nh; nh = nh->next) @@ -211,7 +211,7 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d) } } - if (d->show_protocol && (d->show_protocol != e.src->proto)) + if (d->show_protocol && (&d->show_protocol->sources != e.src->owner)) goto skip; if (f_run(d->filter, &e, c->show_pool, 0) > F_ACCEPT) diff --git a/nest/rt-table.c b/nest/rt-table.c index b4cd0448..c67f5bf8 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -367,16 +367,16 @@ rte_better(rte *new, rte *old) return 1; if (new->attrs->pref < old->attrs->pref) return 0; - if (new->src->proto->proto != old->src->proto->proto) + if (new->src->owner->class != old->src->owner->class) { /* * If the user has configured protocol preferences, so that two different protocols * have the same preference, try to break the tie by comparing addresses. Not too * useful, but keeps the ordering of routes unambiguous. */ - return new->src->proto->proto > old->src->proto->proto; + return new->src->owner->class > old->src->owner->class; } - if (better = new->src->proto->rte_better) + if (better = new->src->owner->class->rte_better) return better(new, old); return 0; } @@ -392,10 +392,10 @@ rte_mergable(rte *pri, rte *sec) if (pri->attrs->pref != sec->attrs->pref) return 0; - if (pri->src->proto->proto != sec->src->proto->proto) + if (pri->src->owner->class != sec->src->owner->class) return 0; - if (mergable = pri->src->proto->rte_mergable) + if (mergable = pri->src->owner->class->rte_mergable) return mergable(pri, sec); return 0; @@ -1269,10 +1269,10 @@ rte_recalculate(struct rt_import_hook *c, net *net, rte *new, struct rte_src *sr { if (!old->generation && !new->generation) bug("Two protocols claim to author a route with the same rte_src in table %s: %N %s/%u:%u", - c->table->name, net->n.addr, old->src->proto->name, old->src->private_id, old->src->global_id); + c->table->name, net->n.addr, old->src->owner->name, old->src->private_id, old->src->global_id); log_rl(&table->rl_pipe, L_ERR "Route source collision in table %s: %N %s/%u:%u", - c->table->name, net->n.addr, old->src->proto->name, old->src->private_id, old->src->global_id); + c->table->name, net->n.addr, old->src->owner->name, old->src->private_id, old->src->global_id); } if (new && rte_same(old, new)) @@ -1341,8 +1341,8 @@ rte_recalculate(struct rt_import_hook *c, net *net, rte *new, struct rte_src *sr /* If routes are not sorted, find the best route and move it on the first position. There are several optimized cases. */ - if (src->proto->rte_recalculate && - src->proto->rte_recalculate(table, net, new_stored ? &new_stored->rte : NULL, old, old_best)) + if (src->owner->rte_recalculate && + src->owner->rte_recalculate(table, net, new_stored ? &new_stored->rte : NULL, old, old_best)) goto do_recalculate; if (new_stored && rte_better(&new_stored->rte, old_best)) @@ -2237,8 +2237,6 @@ again: /* state change 2->0, 3->1 */ tab->prune_state &= 1; - rt_prune_sources(); - uint flushed_channels = 0; /* Close flushed channels */ @@ -2409,7 +2407,6 @@ rt_export_cleanup(void *data) done:; struct rt_import_hook *ih; node *x; - _Bool imports_stopped = 0; WALK_LIST2_DELSAFE(ih, n, x, tab->imports, n) if (ih->import_state == TIS_WAITING) if (!first_export || (first_export->seq >= ih->flush_seq)) @@ -2419,16 +2416,8 @@ done:; rem_node(&ih->n); mb_free(ih); rt_unlock_table(tab); - imports_stopped = 1; } - if (imports_stopped) - { - if (config->table_debug) - log(L_TRACE "%s: Sources pruning routine requested", tab->name); - - rt_prune_sources(); - } if (EMPTY_LIST(tab->pending_exports) && tm_active(tab->export_timer)) tm_stop(tab->export_timer); @@ -2610,8 +2599,8 @@ rt_next_hop_update_net(rtable *tab, net *n) /* Call a pre-comparison hook */ /* Not really an efficient way to compute this */ - if (e->rte.src->proto->rte_recalculate) - e->rte.src->proto->rte_recalculate(tab, n, &new->rte, &e->rte, &old_best->rte); + if (e->rte.src->owner->rte_recalculate) + e->rte.src->owner->rte_recalculate(tab, n, &new->rte, &e->rte, &old_best->rte); updates[pos++] = (struct rte_multiupdate) { .old = e, @@ -3083,8 +3072,8 @@ rt_get_igp_metric(rte *rt) if (rt->attrs->source == RTS_DEVICE) return 0; - if (rt->src->proto->rte_igp_metric) - return rt->src->proto->rte_igp_metric(rt); + if (rt->src->owner->class->rte_igp_metric) + return rt->src->owner->class->rte_igp_metric(rt); return IGP_METRIC_UNKNOWN; } diff --git a/proto/babel/babel.c b/proto/babel/babel.c index 03c85b7d..40e85a16 100644 --- a/proto/babel/babel.c +++ b/proto/babel/babel.c @@ -2264,7 +2264,7 @@ babel_preexport(struct channel *c, struct rte *new) { struct rta *a = new->attrs; /* Reject our own unreachable routes */ - if ((a->dest == RTD_UNREACHABLE) && (new->src->proto == c->proto)) + if ((a->dest == RTD_UNREACHABLE) && (new->src->owner == &c->proto->sources)) return -1; return 0; @@ -2288,7 +2288,7 @@ babel_rt_notify(struct proto *P, struct channel *c UNUSED, const net_addr *net, uint rt_metric = ea_get_int(new->attrs->eattrs, EA_BABEL_METRIC, 0); u64 rt_router_id = 0; - if (new->src->proto == P) + if (new->src->owner == &P->sources) { rt_seqno = ea_find(new->attrs->eattrs, EA_BABEL_SEQNO)->u.data; eattr *e = ea_find(new->attrs->eattrs, EA_BABEL_ROUTER_ID); @@ -2372,6 +2372,12 @@ babel_postconfig(struct proto_config *CF) cf->ip6_channel = ip6 ?: ip6_sadr; } +static struct rte_owner_class babel_rte_owner_class = { + .get_route_info = babel_get_route_info, + .rte_better = babel_rte_better, + .rte_igp_metric = babel_rte_igp_metric, +}; + static struct proto * babel_init(struct proto_config *CF) { @@ -2385,8 +2391,8 @@ babel_init(struct proto_config *CF) P->if_notify = babel_if_notify; P->rt_notify = babel_rt_notify; P->preexport = babel_preexport; - P->rte_better = babel_rte_better; - P->rte_igp_metric = babel_rte_igp_metric; + + P->sources.class = &babel_rte_owner_class; return P; } @@ -2479,7 +2485,6 @@ babel_reconfigure(struct proto *P, struct proto_config *CF) return 1; } - struct protocol proto_babel = { .name = "Babel", .template = "babel%d", @@ -2494,6 +2499,5 @@ struct protocol proto_babel = { .start = babel_start, .shutdown = babel_shutdown, .reconfigure = babel_reconfigure, - .get_route_info = babel_get_route_info, .get_attr = babel_get_attr }; diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c index 892b26e3..9b9013f9 100644 --- a/proto/bgp/attrs.c +++ b/proto/bgp/attrs.c @@ -1670,9 +1670,8 @@ bgp_free_prefix(struct bgp_channel *c, struct bgp_prefix *px) int bgp_preexport(struct channel *c, rte *e) { - struct proto *SRC = e->src->proto; struct bgp_proto *p = (struct bgp_proto *) (c->proto); - struct bgp_proto *src = (SRC->proto == &proto_bgp) ? (struct bgp_proto *) SRC : NULL; + struct bgp_proto *src = bgp_rte_proto(e); /* Reject our routes */ if (src == p) @@ -1725,8 +1724,7 @@ bgp_preexport(struct channel *c, rte *e) static ea_list * bgp_update_attrs(struct bgp_proto *p, struct bgp_channel *c, rte *e, ea_list *attrs0, struct linpool *pool) { - struct proto *SRC = e->src->proto; - struct bgp_proto *src = (SRC->proto == &proto_bgp) ? (void *) SRC : NULL; + struct bgp_proto *src = bgp_rte_proto(e); struct bgp_export_state s = { .proto = p, .channel = c, .pool = pool, .src = src, .route = e, .mpls = c->desc->mpls }; ea_list *attrs = attrs0; eattr *a; @@ -1880,7 +1878,7 @@ bgp_get_neighbor(rte *r) return as; /* If AS_PATH is not defined, we treat rte as locally originated */ - struct bgp_proto *p = (void *) r->src->proto; + struct bgp_proto *p = bgp_rte_proto(r); return p->cf->confederation ?: p->local_as; } @@ -1910,8 +1908,8 @@ rte_stale(rte *r) int bgp_rte_better(rte *new, rte *old) { - struct bgp_proto *new_bgp = (struct bgp_proto *) new->src->proto; - struct bgp_proto *old_bgp = (struct bgp_proto *) old->src->proto; + struct bgp_proto *new_bgp = bgp_rte_proto(new); + struct bgp_proto *old_bgp = bgp_rte_proto(old); eattr *x, *y; u32 n, o; @@ -2055,8 +2053,8 @@ bgp_rte_better(rte *new, rte *old) int bgp_rte_mergable(rte *pri, rte *sec) { - struct bgp_proto *pri_bgp = (struct bgp_proto *) pri->src->proto; - struct bgp_proto *sec_bgp = (struct bgp_proto *) sec->src->proto; + struct bgp_proto *pri_bgp = bgp_rte_proto(pri); + struct bgp_proto *sec_bgp = bgp_rte_proto(sec); eattr *x, *y; u32 p, s; @@ -2137,8 +2135,8 @@ same_group(rte *r, u32 lpref, u32 lasn) static inline int use_deterministic_med(struct rte_storage *r) { - struct proto *P = r->rte.src->proto; - return (P->proto == &proto_bgp) && ((struct bgp_proto *) P)->cf->deterministic_med; + struct bgp_proto *p = bgp_rte_proto(&r->rte); + return p && p->cf->deterministic_med; } int diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index 5f2e7dfd..dc845550 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -1726,6 +1726,13 @@ done: return p->p.proto_state; } +struct rte_owner_class bgp_rte_owner_class = { + .get_route_info = bgp_get_route_info, + .rte_better = bgp_rte_better, + .rte_mergable = bgp_rte_mergable, + .rte_igp_metric = bgp_rte_igp_metric, +}; + static struct proto * bgp_init(struct proto_config *CF) { @@ -1739,10 +1746,9 @@ bgp_init(struct proto_config *CF) P->reload_routes = bgp_reload_routes; P->feed_begin = bgp_feed_begin; P->feed_end = bgp_feed_end; - P->rte_better = bgp_rte_better; - P->rte_mergable = bgp_rte_mergable; - P->rte_recalculate = cf->deterministic_med ? bgp_rte_recalculate : NULL; - P->rte_igp_metric = bgp_rte_igp_metric; + + P->sources.class = &bgp_rte_owner_class; + P->sources.rte_recalculate = cf->deterministic_med ? bgp_rte_recalculate : NULL; p->cf = cf; p->is_internal = (cf->local_as == cf->remote_as); @@ -2605,6 +2611,5 @@ struct protocol proto_bgp = { .copy_config = bgp_copy_config, .get_status = bgp_get_status, .get_attr = bgp_get_attr, - .get_route_info = bgp_get_route_info, .show_proto_info = bgp_show_proto_info }; diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index 342dc023..7cb4df1f 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -523,6 +523,7 @@ rta_resolvable(rta *a) return a->dest == RTD_UNICAST; } +extern struct rte_owner_class bgp_rte_owner_class; #ifdef LOCAL_DEBUG #define BGP_FORCE_DEBUG 1 @@ -594,6 +595,12 @@ int bgp_get_attr(const struct eattr *e, byte *buf, int buflen); void bgp_get_route_info(struct rte *, byte *); int bgp_total_aigp_metric_(rta *a, u64 *metric, const struct adata **ad); +static inline struct bgp_proto *bgp_rte_proto(struct rte *rte) +{ + return (rte->src->owner->class == &bgp_rte_owner_class) ? + SKIP_BACK(struct bgp_proto, p.sources, rte->src->owner) : NULL; +} + #define BGP_AIGP_METRIC 1 #define BGP_AIGP_MAX U64(0xffffffffffffffff) diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index 647551e5..d2d5b174 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -1339,6 +1339,8 @@ bgp_rte_update(struct bgp_parse_state *s, net_addr *n, u32 path_id, rta *a0) { if (path_id != s->last_id) { + rt_unlock_source(s->last_src); + s->last_src = rt_get_source(&s->proto->p, path_id); s->last_id = path_id; @@ -2421,6 +2423,7 @@ bgp_decode_nlri(struct bgp_parse_state *s, u32 afi, byte *nlri, uint len, ea_lis s->last_id = 0; s->last_src = s->proto->p.main_source; + rt_lock_source(s->last_src); /* * IPv4 BGP and MP-BGP may be used together in one update, therefore we do not @@ -2451,6 +2454,8 @@ bgp_decode_nlri(struct bgp_parse_state *s, u32 afi, byte *nlri, uint len, ea_lis rta_free(s->cached_rta); s->cached_rta = NULL; + + rt_unlock_source(s->last_src); } static void diff --git a/proto/mrt/mrt.c b/proto/mrt/mrt.c index 5da3c7c6..9d78438d 100644 --- a/proto/mrt/mrt.c +++ b/proto/mrt/mrt.c @@ -472,9 +472,9 @@ mrt_rib_table_entry(struct mrt_table_dump_state *s, rte *r) #ifdef CONFIG_BGP /* Find peer index */ - if (r->src->proto->proto == &proto_bgp) + struct bgp_proto *p = bgp_rte_proto(r); + if (p) { - struct bgp_proto *p = (void *) r->src->proto; struct mrt_peer_entry *n = HASH_FIND(s->peer_hash, PEER, p->remote_id, p->remote_as, p->remote_ip); diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c index 22150590..16774df6 100644 --- a/proto/ospf/ospf.c +++ b/proto/ospf/ospf.c @@ -376,8 +376,8 @@ ospf_init(struct proto_config *CF) P->reload_routes = ospf_reload_routes; P->feed_begin = ospf_feed_begin; P->feed_end = ospf_feed_end; - P->rte_better = ospf_rte_better; - P->rte_igp_metric = ospf_rte_igp_metric; + + P->sources.class = &ospf_rte_owner_class; return P; } @@ -488,7 +488,7 @@ ospf_preexport(struct channel *c, rte *e) struct ospf_area *oa = ospf_main_area(p); /* Reject our own routes */ - if (e->src->proto == c->proto) + if (e->sender == c->in_req.hook) return -1; /* Do not export routes to stub areas */ @@ -1517,6 +1517,12 @@ ospf_sh_lsadb(struct lsadb_show_data *ld) } +struct rte_owner_class ospf_rte_owner_class = { + .get_route_info = ospf_get_route_info, + .rte_better = ospf_rte_better, + .rte_igp_metric = ospf_rte_igp_metric, +}; + struct protocol proto_ospf = { .name = "OSPF", .template = "ospf%d", @@ -1532,5 +1538,4 @@ struct protocol proto_ospf = { .reconfigure = ospf_reconfigure, .get_status = ospf_get_status, .get_attr = ospf_get_attr, - .get_route_info = ospf_get_route_info }; diff --git a/proto/ospf/ospf.h b/proto/ospf/ospf.h index 3e704ae8..a5f83e79 100644 --- a/proto/ospf/ospf.h +++ b/proto/ospf/ospf.h @@ -1007,6 +1007,8 @@ void ospf_sh_state(struct proto *P, int verbose, int reachable); void ospf_sh_lsadb(struct lsadb_show_data *ld); +extern struct rte_owner_class ospf_rte_owner_class; + /* iface.c */ void ospf_iface_chstate(struct ospf_iface *ifa, u8 state); void ospf_iface_sm(struct ospf_iface *ifa, int event); diff --git a/proto/pipe/pipe.c b/proto/pipe/pipe.c index bbcd8f0d..a30da0e2 100644 --- a/proto/pipe/pipe.c +++ b/proto/pipe/pipe.c @@ -91,7 +91,7 @@ pipe_preexport(struct channel *c, rte *e) { log_rl(&p->rl_gen, L_ERR "Route overpiped (%u hops of %u configured in %s) in table %s: %N %s/%u:%u", e->generation, max_generation, c->proto->name, - c->table->name, e->net, e->src->proto->name, e->src->private_id, e->src->global_id); + c->table->name, e->net, e->src->owner->name, e->src->private_id, e->src->global_id); return -1; } diff --git a/proto/rip/rip.c b/proto/rip/rip.c index 0a9844f3..ee05f058 100644 --- a/proto/rip/rip.c +++ b/proto/rip/rip.c @@ -353,7 +353,7 @@ rip_rt_notify(struct proto *P, struct channel *ch UNUSED, const net_addr *net, s en->valid = RIP_ENTRY_VALID; en->metric = rt_metric; en->tag = rt_tag; - en->from = (new->src->proto == P) ? rt_from : NULL; + en->from = (new->src->owner == &P->sources) ? rt_from : NULL; en->iface = new->attrs->nh.iface; en->next_hop = new->attrs->nh.gw; } @@ -1082,11 +1082,20 @@ rip_reload_routes(struct channel *C) rip_kick_timer(p); } +static struct rte_owner_class rip_rte_owner_class; + +static inline struct rip_proto * +rip_rte_proto(struct rte *rte) +{ + return (rte->src->owner->class == &rip_rte_owner_class) ? + SKIP_BACK(struct rip_proto, p.sources, rte->src->owner) : NULL; +} + static int rip_rte_better(struct rte *new, struct rte *old) { ASSERT_DIE(new->src == old->src); - struct rip_proto *p = (struct rip_proto *) new->src->proto; + struct rip_proto *p = rip_rte_proto(new); u32 new_metric = ea_get_int(new->attrs->eattrs, EA_RIP_METRIC, p->infinity); u32 old_metric = ea_get_int(old->attrs->eattrs, EA_RIP_METRIC, p->infinity); @@ -1121,8 +1130,7 @@ rip_init(struct proto_config *CF) P->rt_notify = rip_rt_notify; P->neigh_notify = rip_neigh_notify; P->reload_routes = rip_reload_routes; - P->rte_better = rip_rte_better; - P->rte_igp_metric = rip_rte_igp_metric; + P->sources.class = &rip_rte_owner_class; return P; } @@ -1197,7 +1205,7 @@ rip_reconfigure(struct proto *P, struct proto_config *CF) static void rip_get_route_info(rte *rte, byte *buf) { - struct rip_proto *p = (struct rip_proto *) rte->src->proto; + struct rip_proto *p = rip_rte_proto(rte); u32 rt_metric = ea_get_int(rte->attrs->eattrs, EA_RIP_METRIC, p->infinity); u32 rt_tag = ea_get_int(rte->attrs->eattrs, EA_RIP_TAG, 0); @@ -1324,6 +1332,12 @@ rip_dump(struct proto *P) } +static struct rte_owner_class rip_rte_owner_class = { + .get_route_info = rip_get_route_info, + .rte_better = rip_rte_better, + .rte_igp_metric = rip_rte_igp_metric, +}; + struct protocol proto_rip = { .name = "RIP", .template = "rip%d", @@ -1338,6 +1352,5 @@ struct protocol proto_rip = { .start = rip_start, .shutdown = rip_shutdown, .reconfigure = rip_reconfigure, - .get_route_info = rip_get_route_info, .get_attr = rip_get_attr }; diff --git a/proto/static/static.c b/proto/static/static.c index f8ac9c81..b0c9d640 100644 --- a/proto/static/static.c +++ b/proto/static/static.c @@ -52,11 +52,14 @@ static linpool *static_lp; static inline struct rte_src * static_get_source(struct static_proto *p, uint i) { return i ? rt_get_source(&p->p, i) : p->p.main_source; } +static inline void static_free_source(struct rte_src *src, uint i) +{ if (i) rt_unlock_source(src); } + static void static_announce_rte(struct static_proto *p, struct static_route *r) { + struct rte_src *src; rta *a = allocz(RTA_MAX_SIZE); - struct rte_src *src = static_get_source(p, r->index); a->source = RTS_STATIC; a->scope = SCOPE_UNIVERSE; a->dest = r->dest; @@ -103,6 +106,7 @@ static_announce_rte(struct static_proto *p, struct static_route *r) return; /* We skip rta_lookup() here */ + src = static_get_source(p, r->index); rte e0 = { .attrs = a, .src = src, .net = r->net, }, *e = &e0; /* Evaluate the filter */ @@ -110,6 +114,8 @@ static_announce_rte(struct static_proto *p, struct static_route *r) f_eval_rte(r->cmds, e, static_lp); rte_update(p->p.main_channel, r->net, e, src); + static_free_source(src, r->index); + r->state = SRS_CLEAN; if (r->cmds) @@ -121,7 +127,9 @@ withdraw: if (r->state == SRS_DOWN) return; + src = static_get_source(p, r->index); rte_update(p->p.main_channel, r->net, NULL, src); + static_free_source(src, r->index); r->state = SRS_DOWN; } @@ -287,7 +295,11 @@ static void static_remove_rte(struct static_proto *p, struct static_route *r) { if (r->state) - rte_update(p->p.main_channel, r->net, NULL, static_get_source(p, r->index)); + { + struct rte_src *src = static_get_source(p, r->index); + rte_update(p->p.main_channel, r->net, NULL, src); + static_free_source(src, r->index); + } static_reset_rte(p, r); } @@ -444,6 +456,8 @@ static_postconfig(struct proto_config *CF) static_index_routes(cf); } +static struct rte_owner_class static_rte_owner_class; + static struct proto * static_init(struct proto_config *CF) { @@ -455,8 +469,7 @@ static_init(struct proto_config *CF) P->neigh_notify = static_neigh_notify; P->reload_routes = static_reload_routes; - P->rte_better = static_rte_better; - P->rte_mergable = static_rte_mergable; + P->sources.class = &static_rte_owner_class; if (cf->igp_table_ip4) p->igp_table_ip4 = cf->igp_table_ip4->table; @@ -757,6 +770,11 @@ static_show(struct proto *P) static_show_rt(r); } +static struct rte_owner_class static_rte_owner_class = { + .get_route_info = static_get_route_info, + .rte_better = static_rte_better, + .rte_mergable = static_rte_mergable, +}; struct protocol proto_static = { .name = "Static", @@ -773,5 +791,4 @@ struct protocol proto_static = { .shutdown = static_shutdown, .reconfigure = static_reconfigure, .copy_config = static_copy_config, - .get_route_info = static_get_route_info, }; diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c index 609ee921..5431bebe 100644 --- a/sysdep/unix/krt.c +++ b/sysdep/unix/krt.c @@ -294,8 +294,9 @@ krt_rte_better(rte *a, rte *b) static void krt_learn_rte(struct krt_proto *p, rte *e) { - e->src = rt_get_source(&p->p, krt_metric(e)); + struct rte_src *src = e->src = rt_get_source(&p->p, krt_metric(e)); rte_update(p->p.main_channel, e->net, e, e->src); + rt_unlock_source(src); } static void @@ -674,7 +675,7 @@ krt_scan_timer_kick(struct krt_proto *p) static int krt_preexport(struct channel *c, rte *e) { - if (e->src->proto == c->proto) + if (e->src->owner == &c->proto->sources) return -1; if (!krt_capable(e))