mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-11-18 09:08:42 +00:00
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.
This commit is contained in:
parent
3b20722a1f
commit
f0507f05ce
@ -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" );
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
12
nest/proto.c
12
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);
|
||||
|
@ -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); }
|
||||
|
||||
|
||||
/*
|
||||
|
62
nest/route.h
62
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 <stdatomic.h>
|
||||
|
||||
@ -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 */
|
||||
|
125
nest/rt-attr.c
125
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));
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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))
|
||||
|
Loading…
Reference in New Issue
Block a user