]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Route sources have an explicit owner
authorMaria Matejka <mq@ucw.cz>
Mon, 27 Sep 2021 14:40:28 +0000 (16:40 +0200)
committerMaria Matejka <mq@ucw.cz>
Mon, 22 Nov 2021 18:05:44 +0000 (19:05 +0100)
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.

21 files changed:
filter/f-inst.c
lib/locking.h
nest/proto.c
nest/protocol.h
nest/route.h
nest/rt-attr.c
nest/rt-dev.c
nest/rt-show.c
nest/rt-table.c
proto/babel/babel.c
proto/bgp/attrs.c
proto/bgp/bgp.c
proto/bgp/bgp.h
proto/bgp/packets.c
proto/mrt/mrt.c
proto/ospf/ospf.c
proto/ospf/ospf.h
proto/pipe/pipe.c
proto/rip/rip.c
proto/static/static.c
sysdep/unix/krt.c

index 00e22383d6b01b6e1ca5454f8bcb353e00160f53..706eb6841e354286de86691ed564e605cb664fc6 100644 (file)
       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;
        {
          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" );
 
index 0cbdead85f052a78cafebbd8b14a3709662b6824..0a69f50f0305221bda134e05b96dd4fdc07549a3 100644 (file)
@@ -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;
 };
index f8e1ba310a19b311ff96ccd8660eb4ae93fd860c..930fad1d4ad31e6b9db049a6fc9da73d29ed458c 100644 (file)
@@ -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);
index 981ca96abf0f89ee6b4b2b647852df1cdca333c9..440297a12f56839dacd5159b5744bb1981d751bd 100644 (file)
@@ -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); }
 
 
 /*
index 310cea92e43d946dc074a920bf74fd0647dcf481..a01eff1a1319ca26f6775771276036a35cdaceb9 100644 (file)
@@ -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 */
index 77fd3c3bfcba596176167d86440eecb2f7cdb12f..f7e33d72752c79968b0c083951fa326f3618554f 100644 (file)
@@ -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;
 }
 
+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
-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);
       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));
index 5d1e57b3960492a6d8147a9e9d0a28452067080a..c1251675a811ba3ecc8667eafe0e66155932e982 100644 (file)
@@ -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);
     }
 }
 
index d942b8e182251fc6d8520572bc6c7a09e3cffabf..8196903dc64577d9e39b2cb8eec94c165a9ab249 100644 (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)
     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)
index b4cd0448eb3583c745513b13562191da4541e2d9..c67f5bf8a0b5db1c15b73751ed1120327637ce7d 100644 (file)
@@ -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;
 }
index 03c85b7d5d59bf501a706e32528d168da1da9f1f..40e85a1643c16aeaf1c8b949b00bb2865b69e032 100644 (file)
@@ -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
 };
index 892b26e3cf2ca6965acec94b4e1670b5348e3cc9..9b9013f9e6a8809bbe4006589302aaf00554a431 100644 (file)
@@ -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
index 5f2e7dfdffd6b9988c2e15b57a2d7f640f59102a..dc84555093e98768c6d8ea3abded9f3ae49835cc 100644 (file)
@@ -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
 };
index 342dc023fb46e60a8e440bfa114e0314c301409a..7cb4df1f800a125dd80e34a979f54289fb44d26e 100644 (file)
@@ -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)
 
index 647551e5f99c6e373fda547777a0ddb4354e5bb9..d2d5b174ad833e59fd38363af9e2d05609880e1e 100644 (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)
   {
+    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
index 5da3c7c6bbc12d34dd578ce6bd72fede7fc4e3b4..9d78438d74d0a06e9897ba2b82a6b3331dac5bad 100644 (file)
@@ -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);
 
index 221505900b7b0600c5227a762a7efc6a3c03d07c..16774df69fd61b3b17c54eed05c899b191c3ad8e 100644 (file)
@@ -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
 };
index 3e704ae82e8dc95328ee55fb18c63ec8b59da7de..a5f83e79bba1cf44d97159d76136c7fdcd6859bd 100644 (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);
 
+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);
index bbcd8f0d8cc1550d85183b56df7b4bf60525f5b3..a30da0e25d6629f63be2062cf53b2d98b0e24725 100644 (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",
        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;
   }
index 0a9844f30117e1b181b5f173004ba5ae0117b979..ee05f0580f0dc45f81e18e33be4cf58f12c2f81e 100644 (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->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
 };
index f8ac9c81d8f4abe5694ed225f561f44a358ee863..b0c9d640183710a493f98efdc98505366d72995d 100644 (file)
@@ -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,
 };
index 609ee921716dd6c7b4f9c4a897d90ce74c245588..5431bebea02443f951077ca1870588ba2e23f4d6 100644 (file)
@@ -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))