0
0
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:
Maria Matejka 2021-09-27 16:40:28 +02:00
parent 3b20722a1f
commit f0507f05ce
21 changed files with 290 additions and 105 deletions

View File

@ -526,7 +526,7 @@
case SA_FROM: RESULT(sa.f_type, ip, rta->from); break; 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_GW: RESULT(sa.f_type, ip, rta->nh.gw); break;
case SA_NET: RESULT(sa.f_type, net, fs->rte->net); 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_SOURCE: RESULT(sa.f_type, i, rta->source); break;
case SA_SCOPE: RESULT(sa.f_type, i, rta->scope); break; case SA_SCOPE: RESULT(sa.f_type, i, rta->scope); break;
case SA_DEST: RESULT(sa.f_type, i, rta->dest); break; case SA_DEST: RESULT(sa.f_type, i, rta->dest); break;
@ -562,7 +562,8 @@
{ {
ip_addr ip = v1.val.ip; ip_addr ip = v1.val.ip;
struct iface *ifa = ipa_is_link_local(ip) ? rta->nh.iface : NULL; 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)) if (!n || (n->scope == SCOPE_HOST))
runtime( "Invalid gw address" ); runtime( "Invalid gw address" );

View File

@ -16,6 +16,7 @@ struct lock_order {
struct domain_generic *the_bird; struct domain_generic *the_bird;
struct domain_generic *proto; struct domain_generic *proto;
struct domain_generic *rtable; struct domain_generic *rtable;
struct domain_generic *attrs;
struct domain_generic *cork; struct domain_generic *cork;
struct domain_generic *event; struct domain_generic *event;
}; };

View File

@ -2329,10 +2329,17 @@ channel_reset_limit(struct channel *c, struct limit *l, int dir)
c->limit_active &= ~(1 << dir); c->limit_active &= ~(1 << dir);
} }
static struct rte_owner_class default_rte_owner_class;
static inline void static inline void
proto_do_start(struct proto *p) proto_do_start(struct proto *p)
{ {
p->active = 1; 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 (!p->cf->late_if_feed)
if_feed_baby(p); if_feed_baby(p);
} }
@ -2341,10 +2348,8 @@ static void
proto_do_up(struct proto *p) proto_do_up(struct proto *p)
{ {
if (!p->main_source) if (!p->main_source)
{
p->main_source = rt_get_source(p, 0); p->main_source = rt_get_source(p, 0);
rt_lock_source(p->main_source); // Locked automaticaly
}
proto_start_channels(p); proto_start_channels(p);
@ -2371,6 +2376,7 @@ proto_do_stop(struct proto *p)
} }
proto_stop_channels(p); proto_stop_channels(p);
rt_destroy_sources(&p->sources, p->event);
p->do_stop = 1; p->do_stop = 1;
proto_send_event(p); proto_send_event(p);

View File

@ -78,7 +78,6 @@ struct protocol {
int (*start)(struct proto *); /* Start the instance */ int (*start)(struct proto *); /* Start the instance */
int (*shutdown)(struct proto *); /* Stop 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_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_*) */ 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 (*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 */ 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) */ list channels; /* List of channels to rtables (struct channel) */
struct channel *main_channel; /* Primary channel */ struct channel *main_channel; /* Primary channel */
struct rte_src *main_source; /* Primary route source */ 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 */ struct iface *vrf; /* Related VRF instance, NULL if global */
const char *name; /* Name of this instance (== cf->name) */ 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) 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); }
/* /*

View File

@ -15,6 +15,8 @@
#include "lib/bitmap.h" #include "lib/bitmap.h"
#include "lib/resource.h" #include "lib/resource.h"
#include "lib/net.h" #include "lib/net.h"
#include "lib/hash.h"
#include "lib/event.h"
#include <stdatomic.h> #include <stdatomic.h>
@ -579,10 +581,10 @@ struct nexthop {
struct rte_src { struct rte_src {
struct rte_src *next; /* Hash chain */ 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 private_id; /* Private ID, assigned by the protocol */
u32 global_id; /* Globally unique ID of the source */ 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_BISECT 2 /* Use interval bisection for searching */
#define EALF_CACHED 4 /* Attributes belonging to cached rta */ #define EALF_CACHED 4 /* Attributes belonging to cached rta */
struct rte_src *rt_find_source(struct proto *p, u32 id); struct rte_owner_class {
struct rte_src *rt_get_source(struct proto *p, u32 id); void (*get_route_info)(struct rte *, byte *buf); /* Get route information (for `show route' command) */
static inline void rt_lock_source(struct rte_src *src) { src->uc++; } int (*rte_better)(struct rte *, struct rte *);
static inline void rt_unlock_source(struct rte_src *src) { src->uc--; } int (*rte_mergable)(struct rte *, struct rte *);
void rt_prune_sources(void); 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 { struct ea_walk_state {
ea_list *eattrs; /* Ccurrent ea_list, initially set by caller */ ea_list *eattrs; /* Ccurrent ea_list, initially set by caller */

View File

@ -85,6 +85,8 @@ const char * rta_dest_names[RTD_MAX] = {
[RTD_PROHIBIT] = "prohibited", [RTD_PROHIBIT] = "prohibited",
}; };
DOMAIN(attrs) attrs_domain;
pool *rta_pool; pool *rta_pool;
static slab *rta_slab_[4]; static slab *rta_slab_[4];
@ -96,16 +98,14 @@ static struct idm src_ids;
/* rte source hash */ /* 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_NEXT(n) n->next
#define RSH_EQ(p1,n1,p2,n2) p1 == p2 && n1 == n2 #define RSH_EQ(n1,n2) n1 == n2
#define RSH_FN(p,n) p->hash_key ^ u32_hash(n) #define RSH_FN(n) u32_hash(n)
#define RSH_REHASH rte_src_rehash #define RSH_REHASH rte_src_rehash
#define RSH_PARAMS /2, *2, 1, 1, 8, 20 #define RSH_PARAMS /2, *2, 1, 1, 8, 20
#define RSH_INIT_ORDER 6 #define RSH_INIT_ORDER 2
static HASH(struct rte_src) src_hash;
static void static void
rte_src_init(void) rte_src_init(void)
@ -113,55 +113,134 @@ rte_src_init(void)
rte_src_slab = sl_new(rta_pool, sizeof(struct rte_src)); rte_src_slab = sl_new(rta_pool, sizeof(struct rte_src));
idm_init(&src_ids, rta_pool, SRC_ID_INIT_SIZE); 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) HASH_DEFINE_REHASH_FN(RSH, struct rte_src)
struct rte_src * static struct rte_src *
rt_find_source(struct proto *p, u32 id) 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 * 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); struct rte_src *src = rt_find_source(p, id);
if (src) if (src)
{
UNUSED u64 uc = atomic_fetch_add_explicit(&src->uc, 1, memory_order_acq_rel);
return src; return src;
}
RTA_LOCK;
src = sl_allocz(rte_src_slab); src = sl_allocz(rte_src_slab);
src->proto = p; src->owner = p;
src->private_id = id; src->private_id = id;
src->global_id = idm_alloc(&src_ids); 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; return src;
} }
static inline void
rt_done_sources(struct rte_owner *o)
{
if (o->stop->list)
ev_send(o->stop->list, o->stop);
else
ev_send(o->list, o->stop);
}
void void
rt_prune_sources(void) rt_prune_sources(void *data)
{ {
HASH_WALK_FILTER(src_hash, next, src, sp) 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); idm_free(&src_ids, src->global_id);
sl_free(rte_src_slab, src); sl_free(rte_src_slab, src);
RTA_UNLOCK;
} }
} }
HASH_WALK_FILTER_END; 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 * Multipath Next Hop
@ -1328,6 +1407,8 @@ rta_show(struct cli *c, rta *a)
void void
rta_init(void) rta_init(void)
{ {
attrs_domain = DOMAIN_NEW(attrs, "Attributes");
rta_pool = rp_new(&root_pool, "Attributes"); rta_pool = rp_new(&root_pool, "Attributes");
rta_slab_[0] = sl_new(rta_pool, sizeof(rta)); rta_slab_[0] = sl_new(rta_pool, sizeof(rta));

View File

@ -68,6 +68,7 @@ dev_ifa_notify(struct proto *P, uint flags, struct ifa *ad)
/* Use iface ID as local source ID */ /* Use iface ID as local source ID */
struct rte_src *src = rt_get_source(P, ad->iface->index); struct rte_src *src = rt_get_source(P, ad->iface->index);
rte_update(c, net, NULL, src); rte_update(c, net, NULL, src);
rt_unlock_source(src);
} }
else if (flags & IF_CHANGE_UP) 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); rte_update(c, net, &e0, src);
rt_unlock_source(src);
} }
} }

View File

@ -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) if (d->verbose && !rta_is_cached(a) && a->eattrs)
ea_normalize(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) if (get_route_info)
get_route_info(e, info); get_route_info(e, info);
else 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); rt_show_table(c, d);
cli_printf(c, -1007, "%-20s %s [%s %s%s]%s%s", ia, rta_dest_name(a->dest), 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) if (a->dest == RTD_UNICAST)
for (nh = &(a->nh); nh; nh = nh->next) 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; goto skip;
if (f_run(d->filter, &e, c->show_pool, 0) > F_ACCEPT) if (f_run(d->filter, &e, c->show_pool, 0) > F_ACCEPT)

View File

@ -367,16 +367,16 @@ rte_better(rte *new, rte *old)
return 1; return 1;
if (new->attrs->pref < old->attrs->pref) if (new->attrs->pref < old->attrs->pref)
return 0; 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 * 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 * have the same preference, try to break the tie by comparing addresses. Not too
* useful, but keeps the ordering of routes unambiguous. * 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 better(new, old);
return 0; return 0;
} }
@ -392,10 +392,10 @@ rte_mergable(rte *pri, rte *sec)
if (pri->attrs->pref != sec->attrs->pref) if (pri->attrs->pref != sec->attrs->pref)
return 0; return 0;
if (pri->src->proto->proto != sec->src->proto->proto) if (pri->src->owner->class != sec->src->owner->class)
return 0; return 0;
if (mergable = pri->src->proto->rte_mergable) if (mergable = pri->src->owner->class->rte_mergable)
return mergable(pri, sec); return mergable(pri, sec);
return 0; 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) 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", 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", 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)) 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 /* If routes are not sorted, find the best route and move it on
the first position. There are several optimized cases. */ the first position. There are several optimized cases. */
if (src->proto->rte_recalculate && if (src->owner->rte_recalculate &&
src->proto->rte_recalculate(table, net, new_stored ? &new_stored->rte : NULL, old, old_best)) src->owner->rte_recalculate(table, net, new_stored ? &new_stored->rte : NULL, old, old_best))
goto do_recalculate; goto do_recalculate;
if (new_stored && rte_better(&new_stored->rte, old_best)) if (new_stored && rte_better(&new_stored->rte, old_best))
@ -2237,8 +2237,6 @@ again:
/* state change 2->0, 3->1 */ /* state change 2->0, 3->1 */
tab->prune_state &= 1; tab->prune_state &= 1;
rt_prune_sources();
uint flushed_channels = 0; uint flushed_channels = 0;
/* Close flushed channels */ /* Close flushed channels */
@ -2409,7 +2407,6 @@ rt_export_cleanup(void *data)
done:; done:;
struct rt_import_hook *ih; node *x; struct rt_import_hook *ih; node *x;
_Bool imports_stopped = 0;
WALK_LIST2_DELSAFE(ih, n, x, tab->imports, n) WALK_LIST2_DELSAFE(ih, n, x, tab->imports, n)
if (ih->import_state == TIS_WAITING) if (ih->import_state == TIS_WAITING)
if (!first_export || (first_export->seq >= ih->flush_seq)) if (!first_export || (first_export->seq >= ih->flush_seq))
@ -2419,16 +2416,8 @@ done:;
rem_node(&ih->n); rem_node(&ih->n);
mb_free(ih); mb_free(ih);
rt_unlock_table(tab); 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)) if (EMPTY_LIST(tab->pending_exports) && tm_active(tab->export_timer))
tm_stop(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 */ /* Call a pre-comparison hook */
/* Not really an efficient way to compute this */ /* Not really an efficient way to compute this */
if (e->rte.src->proto->rte_recalculate) if (e->rte.src->owner->rte_recalculate)
e->rte.src->proto->rte_recalculate(tab, n, &new->rte, &e->rte, &old_best->rte); e->rte.src->owner->rte_recalculate(tab, n, &new->rte, &e->rte, &old_best->rte);
updates[pos++] = (struct rte_multiupdate) { updates[pos++] = (struct rte_multiupdate) {
.old = e, .old = e,
@ -3083,8 +3072,8 @@ rt_get_igp_metric(rte *rt)
if (rt->attrs->source == RTS_DEVICE) if (rt->attrs->source == RTS_DEVICE)
return 0; return 0;
if (rt->src->proto->rte_igp_metric) if (rt->src->owner->class->rte_igp_metric)
return rt->src->proto->rte_igp_metric(rt); return rt->src->owner->class->rte_igp_metric(rt);
return IGP_METRIC_UNKNOWN; return IGP_METRIC_UNKNOWN;
} }

View File

@ -2264,7 +2264,7 @@ babel_preexport(struct channel *c, struct rte *new)
{ {
struct rta *a = new->attrs; struct rta *a = new->attrs;
/* Reject our own unreachable routes */ /* 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 -1;
return 0; 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); uint rt_metric = ea_get_int(new->attrs->eattrs, EA_BABEL_METRIC, 0);
u64 rt_router_id = 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; rt_seqno = ea_find(new->attrs->eattrs, EA_BABEL_SEQNO)->u.data;
eattr *e = ea_find(new->attrs->eattrs, EA_BABEL_ROUTER_ID); 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; 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 * static struct proto *
babel_init(struct proto_config *CF) babel_init(struct proto_config *CF)
{ {
@ -2385,8 +2391,8 @@ babel_init(struct proto_config *CF)
P->if_notify = babel_if_notify; P->if_notify = babel_if_notify;
P->rt_notify = babel_rt_notify; P->rt_notify = babel_rt_notify;
P->preexport = babel_preexport; 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; return P;
} }
@ -2479,7 +2485,6 @@ babel_reconfigure(struct proto *P, struct proto_config *CF)
return 1; return 1;
} }
struct protocol proto_babel = { struct protocol proto_babel = {
.name = "Babel", .name = "Babel",
.template = "babel%d", .template = "babel%d",
@ -2494,6 +2499,5 @@ struct protocol proto_babel = {
.start = babel_start, .start = babel_start,
.shutdown = babel_shutdown, .shutdown = babel_shutdown,
.reconfigure = babel_reconfigure, .reconfigure = babel_reconfigure,
.get_route_info = babel_get_route_info,
.get_attr = babel_get_attr .get_attr = babel_get_attr
}; };

View File

@ -1670,9 +1670,8 @@ bgp_free_prefix(struct bgp_channel *c, struct bgp_prefix *px)
int int
bgp_preexport(struct channel *c, rte *e) 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 *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 */ /* Reject our routes */
if (src == p) if (src == p)
@ -1725,8 +1724,7 @@ bgp_preexport(struct channel *c, rte *e)
static ea_list * static ea_list *
bgp_update_attrs(struct bgp_proto *p, struct bgp_channel *c, rte *e, ea_list *attrs0, struct linpool *pool) 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 = bgp_rte_proto(e);
struct bgp_proto *src = (SRC->proto == &proto_bgp) ? (void *) SRC : NULL;
struct bgp_export_state s = { .proto = p, .channel = c, .pool = pool, .src = src, .route = e, .mpls = c->desc->mpls }; struct bgp_export_state s = { .proto = p, .channel = c, .pool = pool, .src = src, .route = e, .mpls = c->desc->mpls };
ea_list *attrs = attrs0; ea_list *attrs = attrs0;
eattr *a; eattr *a;
@ -1880,7 +1878,7 @@ bgp_get_neighbor(rte *r)
return as; return as;
/* If AS_PATH is not defined, we treat rte as locally originated */ /* 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; return p->cf->confederation ?: p->local_as;
} }
@ -1910,8 +1908,8 @@ rte_stale(rte *r)
int int
bgp_rte_better(rte *new, rte *old) bgp_rte_better(rte *new, rte *old)
{ {
struct bgp_proto *new_bgp = (struct bgp_proto *) new->src->proto; struct bgp_proto *new_bgp = bgp_rte_proto(new);
struct bgp_proto *old_bgp = (struct bgp_proto *) old->src->proto; struct bgp_proto *old_bgp = bgp_rte_proto(old);
eattr *x, *y; eattr *x, *y;
u32 n, o; u32 n, o;
@ -2055,8 +2053,8 @@ bgp_rte_better(rte *new, rte *old)
int int
bgp_rte_mergable(rte *pri, rte *sec) bgp_rte_mergable(rte *pri, rte *sec)
{ {
struct bgp_proto *pri_bgp = (struct bgp_proto *) pri->src->proto; struct bgp_proto *pri_bgp = bgp_rte_proto(pri);
struct bgp_proto *sec_bgp = (struct bgp_proto *) sec->src->proto; struct bgp_proto *sec_bgp = bgp_rte_proto(sec);
eattr *x, *y; eattr *x, *y;
u32 p, s; u32 p, s;
@ -2137,8 +2135,8 @@ same_group(rte *r, u32 lpref, u32 lasn)
static inline int static inline int
use_deterministic_med(struct rte_storage *r) use_deterministic_med(struct rte_storage *r)
{ {
struct proto *P = r->rte.src->proto; struct bgp_proto *p = bgp_rte_proto(&r->rte);
return (P->proto == &proto_bgp) && ((struct bgp_proto *) P)->cf->deterministic_med; return p && p->cf->deterministic_med;
} }
int int

View File

@ -1726,6 +1726,13 @@ done:
return p->p.proto_state; 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 * static struct proto *
bgp_init(struct proto_config *CF) bgp_init(struct proto_config *CF)
{ {
@ -1739,10 +1746,9 @@ bgp_init(struct proto_config *CF)
P->reload_routes = bgp_reload_routes; P->reload_routes = bgp_reload_routes;
P->feed_begin = bgp_feed_begin; P->feed_begin = bgp_feed_begin;
P->feed_end = bgp_feed_end; P->feed_end = bgp_feed_end;
P->rte_better = bgp_rte_better;
P->rte_mergable = bgp_rte_mergable; P->sources.class = &bgp_rte_owner_class;
P->rte_recalculate = cf->deterministic_med ? bgp_rte_recalculate : NULL; P->sources.rte_recalculate = cf->deterministic_med ? bgp_rte_recalculate : NULL;
P->rte_igp_metric = bgp_rte_igp_metric;
p->cf = cf; p->cf = cf;
p->is_internal = (cf->local_as == cf->remote_as); p->is_internal = (cf->local_as == cf->remote_as);
@ -2605,6 +2611,5 @@ struct protocol proto_bgp = {
.copy_config = bgp_copy_config, .copy_config = bgp_copy_config,
.get_status = bgp_get_status, .get_status = bgp_get_status,
.get_attr = bgp_get_attr, .get_attr = bgp_get_attr,
.get_route_info = bgp_get_route_info,
.show_proto_info = bgp_show_proto_info .show_proto_info = bgp_show_proto_info
}; };

View File

@ -523,6 +523,7 @@ rta_resolvable(rta *a)
return a->dest == RTD_UNICAST; return a->dest == RTD_UNICAST;
} }
extern struct rte_owner_class bgp_rte_owner_class;
#ifdef LOCAL_DEBUG #ifdef LOCAL_DEBUG
#define BGP_FORCE_DEBUG 1 #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 *); void bgp_get_route_info(struct rte *, byte *);
int bgp_total_aigp_metric_(rta *a, u64 *metric, const struct adata **ad); 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_METRIC 1
#define BGP_AIGP_MAX U64(0xffffffffffffffff) #define BGP_AIGP_MAX U64(0xffffffffffffffff)

View File

@ -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) 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_src = rt_get_source(&s->proto->p, path_id);
s->last_id = 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_id = 0;
s->last_src = s->proto->p.main_source; 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 * 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); rta_free(s->cached_rta);
s->cached_rta = NULL; s->cached_rta = NULL;
rt_unlock_source(s->last_src);
} }
static void static void

View File

@ -472,9 +472,9 @@ mrt_rib_table_entry(struct mrt_table_dump_state *s, rte *r)
#ifdef CONFIG_BGP #ifdef CONFIG_BGP
/* Find peer index */ /* 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 = struct mrt_peer_entry *n =
HASH_FIND(s->peer_hash, PEER, p->remote_id, p->remote_as, p->remote_ip); HASH_FIND(s->peer_hash, PEER, p->remote_id, p->remote_as, p->remote_ip);

View File

@ -376,8 +376,8 @@ ospf_init(struct proto_config *CF)
P->reload_routes = ospf_reload_routes; P->reload_routes = ospf_reload_routes;
P->feed_begin = ospf_feed_begin; P->feed_begin = ospf_feed_begin;
P->feed_end = ospf_feed_end; 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; return P;
} }
@ -488,7 +488,7 @@ ospf_preexport(struct channel *c, rte *e)
struct ospf_area *oa = ospf_main_area(p); struct ospf_area *oa = ospf_main_area(p);
/* Reject our own routes */ /* Reject our own routes */
if (e->src->proto == c->proto) if (e->sender == c->in_req.hook)
return -1; return -1;
/* Do not export routes to stub areas */ /* 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 = { struct protocol proto_ospf = {
.name = "OSPF", .name = "OSPF",
.template = "ospf%d", .template = "ospf%d",
@ -1532,5 +1538,4 @@ struct protocol proto_ospf = {
.reconfigure = ospf_reconfigure, .reconfigure = ospf_reconfigure,
.get_status = ospf_get_status, .get_status = ospf_get_status,
.get_attr = ospf_get_attr, .get_attr = ospf_get_attr,
.get_route_info = ospf_get_route_info
}; };

View File

@ -1007,6 +1007,8 @@ void ospf_sh_state(struct proto *P, int verbose, int reachable);
void ospf_sh_lsadb(struct lsadb_show_data *ld); void ospf_sh_lsadb(struct lsadb_show_data *ld);
extern struct rte_owner_class ospf_rte_owner_class;
/* iface.c */ /* iface.c */
void ospf_iface_chstate(struct ospf_iface *ifa, u8 state); void ospf_iface_chstate(struct ospf_iface *ifa, u8 state);
void ospf_iface_sm(struct ospf_iface *ifa, int event); void ospf_iface_sm(struct ospf_iface *ifa, int event);

View File

@ -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", 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, 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; return -1;
} }

View File

@ -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->valid = RIP_ENTRY_VALID;
en->metric = rt_metric; en->metric = rt_metric;
en->tag = rt_tag; 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->iface = new->attrs->nh.iface;
en->next_hop = new->attrs->nh.gw; en->next_hop = new->attrs->nh.gw;
} }
@ -1082,11 +1082,20 @@ rip_reload_routes(struct channel *C)
rip_kick_timer(p); 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 static int
rip_rte_better(struct rte *new, struct rte *old) rip_rte_better(struct rte *new, struct rte *old)
{ {
ASSERT_DIE(new->src == old->src); 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 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); 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->rt_notify = rip_rt_notify;
P->neigh_notify = rip_neigh_notify; P->neigh_notify = rip_neigh_notify;
P->reload_routes = rip_reload_routes; P->reload_routes = rip_reload_routes;
P->rte_better = rip_rte_better; P->sources.class = &rip_rte_owner_class;
P->rte_igp_metric = rip_rte_igp_metric;
return P; return P;
} }
@ -1197,7 +1205,7 @@ rip_reconfigure(struct proto *P, struct proto_config *CF)
static void static void
rip_get_route_info(rte *rte, byte *buf) 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_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); 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 = { struct protocol proto_rip = {
.name = "RIP", .name = "RIP",
.template = "rip%d", .template = "rip%d",
@ -1338,6 +1352,5 @@ struct protocol proto_rip = {
.start = rip_start, .start = rip_start,
.shutdown = rip_shutdown, .shutdown = rip_shutdown,
.reconfigure = rip_reconfigure, .reconfigure = rip_reconfigure,
.get_route_info = rip_get_route_info,
.get_attr = rip_get_attr .get_attr = rip_get_attr
}; };

View File

@ -52,11 +52,14 @@ static linpool *static_lp;
static inline struct rte_src * static_get_source(struct static_proto *p, uint i) 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; } { 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 void
static_announce_rte(struct static_proto *p, struct static_route *r) static_announce_rte(struct static_proto *p, struct static_route *r)
{ {
struct rte_src *src;
rta *a = allocz(RTA_MAX_SIZE); rta *a = allocz(RTA_MAX_SIZE);
struct rte_src *src = static_get_source(p, r->index);
a->source = RTS_STATIC; a->source = RTS_STATIC;
a->scope = SCOPE_UNIVERSE; a->scope = SCOPE_UNIVERSE;
a->dest = r->dest; a->dest = r->dest;
@ -103,6 +106,7 @@ static_announce_rte(struct static_proto *p, struct static_route *r)
return; return;
/* We skip rta_lookup() here */ /* We skip rta_lookup() here */
src = static_get_source(p, r->index);
rte e0 = { .attrs = a, .src = src, .net = r->net, }, *e = &e0; rte e0 = { .attrs = a, .src = src, .net = r->net, }, *e = &e0;
/* Evaluate the filter */ /* 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); f_eval_rte(r->cmds, e, static_lp);
rte_update(p->p.main_channel, r->net, e, src); rte_update(p->p.main_channel, r->net, e, src);
static_free_source(src, r->index);
r->state = SRS_CLEAN; r->state = SRS_CLEAN;
if (r->cmds) if (r->cmds)
@ -121,7 +127,9 @@ withdraw:
if (r->state == SRS_DOWN) if (r->state == SRS_DOWN)
return; return;
src = static_get_source(p, r->index);
rte_update(p->p.main_channel, r->net, NULL, src); rte_update(p->p.main_channel, r->net, NULL, src);
static_free_source(src, r->index);
r->state = SRS_DOWN; r->state = SRS_DOWN;
} }
@ -287,7 +295,11 @@ static void
static_remove_rte(struct static_proto *p, struct static_route *r) static_remove_rte(struct static_proto *p, struct static_route *r)
{ {
if (r->state) 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); static_reset_rte(p, r);
} }
@ -444,6 +456,8 @@ static_postconfig(struct proto_config *CF)
static_index_routes(cf); static_index_routes(cf);
} }
static struct rte_owner_class static_rte_owner_class;
static struct proto * static struct proto *
static_init(struct proto_config *CF) static_init(struct proto_config *CF)
{ {
@ -455,8 +469,7 @@ static_init(struct proto_config *CF)
P->neigh_notify = static_neigh_notify; P->neigh_notify = static_neigh_notify;
P->reload_routes = static_reload_routes; P->reload_routes = static_reload_routes;
P->rte_better = static_rte_better; P->sources.class = &static_rte_owner_class;
P->rte_mergable = static_rte_mergable;
if (cf->igp_table_ip4) if (cf->igp_table_ip4)
p->igp_table_ip4 = cf->igp_table_ip4->table; p->igp_table_ip4 = cf->igp_table_ip4->table;
@ -757,6 +770,11 @@ static_show(struct proto *P)
static_show_rt(r); 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 = { struct protocol proto_static = {
.name = "Static", .name = "Static",
@ -773,5 +791,4 @@ struct protocol proto_static = {
.shutdown = static_shutdown, .shutdown = static_shutdown,
.reconfigure = static_reconfigure, .reconfigure = static_reconfigure,
.copy_config = static_copy_config, .copy_config = static_copy_config,
.get_route_info = static_get_route_info,
}; };

View File

@ -294,8 +294,9 @@ krt_rte_better(rte *a, rte *b)
static void static void
krt_learn_rte(struct krt_proto *p, rte *e) 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); rte_update(p->p.main_channel, e->net, e, e->src);
rt_unlock_source(src);
} }
static void static void
@ -674,7 +675,7 @@ krt_scan_timer_kick(struct krt_proto *p)
static int static int
krt_preexport(struct channel *c, rte *e) krt_preexport(struct channel *c, rte *e)
{ {
if (e->src->proto == c->proto) if (e->src->owner == &c->proto->sources)
return -1; return -1;
if (!krt_capable(e)) if (!krt_capable(e))