]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Split route data structure to storage (ro) / manipulation (rw) structures.
authorMaria Matejka <mq@jmq.cz>
Fri, 10 Apr 2020 22:35:52 +0000 (00:35 +0200)
committerMaria Matejka <mq@ucw.cz>
Sat, 20 Mar 2021 23:09:17 +0000 (00:09 +0100)
This should help a lot with keeping the route machinery and allocations
clean. This commit also changes behaviour of rte_update() with cached
rta. Newly, calling rte_update() keeps the number of rta references.

28 files changed:
filter/f-inst.c
filter/filter.c
filter/filter.h
lib/birdlib.h
nest/proto.c
nest/protocol.h
nest/route.h
nest/rt-dev.c
nest/rt-show.c
nest/rt-table.c
proto/babel/babel.c
proto/bgp/attrs.c
proto/bgp/bgp.h
proto/bgp/packets.c
proto/mrt/mrt.c
proto/ospf/ospf.c
proto/ospf/rt.c
proto/ospf/topology.c
proto/perf/perf.c
proto/pipe/pipe.c
proto/radv/radv.c
proto/rip/rip.c
proto/rpki/rpki.c
proto/static/static.c
sysdep/bsd/krt-sock.c
sysdep/linux/netlink.c
sysdep/unix/krt.c
sysdep/unix/krt.h

index d2f411881e8b4ba960147c46f8323931ea1cc4d7..419421d8235d1618e3773988c3c027d3a0151ae8 100644 (file)
     {
       STATIC_ATTR;
       ACCESS_RTE;
-      struct rta *rta = (*fs->rte)->attrs;
+      struct rta *rta = fs->rte->attrs;
 
       switch (sa.sa_code)
       {
       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->n.addr); break;
-      case SA_PROTO:   RESULT(sa.f_type, s, (*fs->rte)->src->proto->name); 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_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;
 
     f_rta_cow(fs);
     {
-      struct rta *rta = (*fs->rte)->attrs;
+      struct rta *rta = fs->rte->attrs;
 
       switch (sa.sa_code)
       {
        {
          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);
+         neighbor *n = neigh_find(fs->rte->src->proto, ip, ifa, 0);
          if (!n || (n->scope == SCOPE_HOST))
            runtime( "Invalid gw address" );
 
     struct rtable *table = rtc->table;
     ACCESS_RTE;
     ACCESS_EATTRS;
-    const net_addr *net = (*fs->rte)->net->n.addr;
+    const net_addr *net = fs->rte->net;
 
     /* We ignore temporary attributes, probably not a problem here */
     /* 0x02 is a value of BA_AS_PATH, we don't want to include BGP headers */
index e505d570daf6e0898a7754ba9b8c42870d9e3dd8..ca2bfc14edce5bda2883024b425538d9e76c6cad 100644 (file)
@@ -76,10 +76,7 @@ struct filter_state {
   struct filter_stack *stack;
 
   /* The route we are processing. This may be NULL to indicate no route available. */
-  struct rte **rte;
-
-  /* The old rta to be freed after filters are done. */
-  struct rta *old_rta;
+  struct rte *rte;
 
   /* Cached pointer to ea_list */
   struct ea_list **eattrs;
@@ -87,11 +84,11 @@ struct filter_state {
   /* Linpool for adata allocation */
   struct linpool *pool;
 
-  /* Buffer for log output */
-  struct buffer buf;
-
   /* Filter execution flags */
   int flags;
+
+  /* Buffer for log output */
+  struct buffer buf;
 };
 
 _Thread_local static struct filter_state filter_state;
@@ -101,15 +98,7 @@ void (*bt_assert_hook)(int result, const struct f_line_item *assert);
 
 static inline void f_cache_eattrs(struct filter_state *fs)
 {
-  fs->eattrs = &((*fs->rte)->attrs->eattrs);
-}
-
-static inline void f_rte_cow(struct filter_state *fs)
-{
-  if (!((*fs->rte)->flags & REF_COW))
-    return;
-
-  *fs->rte = rte_cow(*fs->rte);
+  fs->eattrs = &(fs->rte->attrs->eattrs);
 }
 
 /*
@@ -118,22 +107,16 @@ static inline void f_rte_cow(struct filter_state *fs)
 static void
 f_rta_cow(struct filter_state *fs)
 {
-  if (!rta_is_cached((*fs->rte)->attrs))
+  if (!rta_is_cached(fs->rte->attrs))
     return;
 
-  /* Prepare to modify rte */
-  f_rte_cow(fs);
-
-  /* Store old rta to free it later, it stores reference from rte_cow() */
-  fs->old_rta = (*fs->rte)->attrs;
-
   /*
    * Get shallow copy of rta. Fields eattrs and nexthops of rta are shared
    * with fs->old_rta (they will be copied when the cached rta will be obtained
    * at the end of f_run()), also the lock of hostentry is inherited (we
    * suppose hostentry is not changed by filters).
    */
-  (*fs->rte)->attrs = rta_do_cow((*fs->rte)->attrs, fs->pool);
+  fs->rte->attrs = rta_do_cow(fs->rte->attrs, fs->pool);
 
   /* Re-cache the ea_list */
   f_cache_eattrs(fs);
@@ -241,29 +224,15 @@ interpret(struct filter_state *fs, const struct f_line *line, struct f_val *val)
 /**
  * f_run - run a filter for a route
  * @filter: filter to run
- * @rte: route being filtered, may be modified
+ * @rte: route being filtered; must be writable
  * @tmp_pool: all filter allocations go from this pool
  * @flags: flags
  *
- * If filter needs to modify the route, there are several
- * posibilities. @rte might be read-only (with REF_COW flag), in that
- * case rw copy is obtained by rte_cow() and @rte is replaced. If
- * @rte is originally rw, it may be directly modified (and it is never
- * copied).
- *
- * The returned rte may reuse the (possibly cached, cloned) rta, or
- * (if rta was modified) contains a modified uncached rta, which
- * uses parts allocated from @tmp_pool and parts shared from original
- * rta. There is one exception - if @rte is rw but contains a cached
- * rta and that is modified, rta in returned rte is also cached.
- *
- * Ownership of cached rtas is consistent with rte, i.e.
- * if a new rte is returned, it has its own clone of cached rta
- * (and cached rta of read-only source rte is intact), if rte is
- * modified in place, old cached rta is possibly freed.
+ * If filter needs to modify the attributes, it allocates a local
+ * shallow copy of them on @tmp_pool.
  */
 enum filter_return
-f_run(const struct filter *filter, struct rte **rte, struct linpool *tmp_pool, int flags)
+f_run(const struct filter *filter, struct rte *rte, struct linpool *tmp_pool, int flags)
 {
   if (filter == FILTER_ACCEPT)
     return F_ACCEPT;
@@ -271,7 +240,6 @@ f_run(const struct filter *filter, struct rte **rte, struct linpool *tmp_pool, i
   if (filter == FILTER_REJECT)
     return F_REJECT;
 
-  int rte_cow = ((*rte)->flags & REF_COW);
   DBG( "Running filter `%s'...", filter->name );
 
   /* Initialize the filter state */
@@ -287,32 +255,6 @@ f_run(const struct filter *filter, struct rte **rte, struct linpool *tmp_pool, i
   /* Run the interpreter itself */
   enum filter_return fret = interpret(&filter_state, filter->root, NULL);
 
-  if (filter_state.old_rta) {
-    /*
-     * Cached rta was modified and filter_state->rte contains now an uncached one,
-     * sharing some part with the cached one. The cached rta should
-     * be freed (if rte was originally COW, filter_state->old_rta is a clone
-     * obtained during rte_cow()).
-     *
-     * This also implements the exception mentioned in f_run()
-     * description. The reason for this is that rta reuses parts of
-     * filter_state->old_rta, and these may be freed during rta_free(filter_state->old_rta).
-     * This is not the problem if rte was COW, because original rte
-     * also holds the same rta.
-     */
-    if (!rte_cow) {
-      /* Cache the new attrs */
-      (*filter_state.rte)->attrs = rta_lookup((*filter_state.rte)->attrs);
-
-      /* Drop cached ea_list pointer */
-      filter_state.eattrs = NULL;
-    }
-
-    /* Uncache the old attrs and drop the pointer as it is invalid now. */
-    rta_free(filter_state.old_rta);
-    filter_state.old_rta = NULL;
-  }
-
   /* Process the filter output, log it and return */
   if (fret < F_ACCEPT) {
     if (!(filter_state.flags & FF_SILENT))
@@ -337,7 +279,7 @@ f_run(const struct filter *filter, struct rte **rte, struct linpool *tmp_pool, i
  */
 
 enum filter_return
-f_eval_rte(const struct f_line *expr, struct rte **rte, struct linpool *tmp_pool)
+f_eval_rte(const struct f_line *expr, struct rte *rte, struct linpool *tmp_pool)
 {
   filter_state = (struct filter_state) {
     .stack = &filter_stack,
@@ -347,8 +289,7 @@ f_eval_rte(const struct f_line *expr, struct rte **rte, struct linpool *tmp_pool
 
   LOG_BUFFER_INIT(filter_state.buf);
 
-  ASSERT(!((*rte)->flags & REF_COW));
-  ASSERT(!rta_is_cached((*rte)->attrs));
+  ASSERT(!rta_is_cached(rte->attrs));
 
   return interpret(&filter_state, expr, NULL);
 }
index 77d39bbff1aed29a13167746aba1279650f1d070..ffc6af2b1643a1350effed18f1bf33f886616d1d 100644 (file)
@@ -51,8 +51,8 @@ struct filter {
 
 struct rte;
 
-enum filter_return f_run(const struct filter *filter, struct rte **rte, struct linpool *tmp_pool, int flags);
-enum filter_return f_eval_rte(const struct f_line *expr, struct rte **rte, struct linpool *tmp_pool);
+enum filter_return f_run(const struct filter *filter, struct rte *rte, struct linpool *tmp_pool, int flags);
+enum filter_return f_eval_rte(const struct f_line *expr, struct rte *rte, struct linpool *tmp_pool);
 uint f_eval_int(const struct f_line *expr);
 enum filter_return f_eval_buf(const struct f_line *expr, struct linpool *tmp_pool, buffer *buf);
 
index e8adb8d649016cadb3df197a959ef7e14d726e2a..bfef608db50893ea5b8b1973a97f2f7381884dd4 100644 (file)
@@ -72,7 +72,7 @@ static inline int u64_cmp(u64 i1, u64 i2)
 #define NORET __attribute__((noreturn))
 #define UNUSED __attribute__((unused))
 #define PACKED __attribute__((packed))
-#define NONNULL(...) __attribute__((nonnull((__VA_ARGS__))))
+#define NONNULL(...) __attribute__((nonnull(__VA_ARGS__)))
 #define USE_RESULT __attribute__((warn_unused_result))
 
 #ifndef HAVE_THREAD_LOCAL
index 5ecbe9a22fa598af726d73f44d85f3a039567d8c..2ffb897c75cd07e93031bf63795204ecbf2a76b6 100644 (file)
@@ -892,8 +892,8 @@ channel_reconfigure(struct channel *c, struct channel_config *cf)
   c->out_limit = cf->out_limit;
 
   // c->ra_mode = cf->ra_mode;
-  c->merge_limit = cf->merge_limit;
   c->preference = cf->preference;
+  c->merge_limit = cf->merge_limit;
   c->debug = cf->debug;
   c->in_keep_filtered = cf->in_keep_filtered;
   c->rpki_reload = cf->rpki_reload;
index 216cd954411129ef52e080385875417be9b9c931..cb368ac315336697ff65af28efee1ac067cd1966 100644 (file)
@@ -78,7 +78,7 @@ struct protocol {
   int (*shutdown)(struct proto *);             /* Stop the instance */
   void (*cleanup)(struct proto *);             /* Called after shutdown when protocol became hungry/down */
   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) */
+  void (*get_route_info)(struct rte *, struct rte_storage *, byte *); /* 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 */
@@ -213,7 +213,7 @@ struct proto {
   void (*ifa_notify)(struct proto *, unsigned flags, struct ifa *a);
   void (*rt_notify)(struct channel *, struct rte_export *);
   void (*neigh_notify)(struct neighbor *neigh);
-  int (*preexport)(struct proto *, struct rte *rt);
+  int (*preexport)(struct channel *, struct rte *rt);
   void (*reload_routes)(struct channel *);
   void (*feed_begin)(struct channel *, int initial);
   void (*feed_end)(struct channel *);
@@ -228,12 +228,12 @@ struct proto {
    *      rte_remove   Called whenever a rte is removed from the routing table.
    */
 
-  int (*rte_recalculate)(struct rtable *, struct network *, struct rte *, struct rte *, struct rte *);
-  int (*rte_better)(struct rte *, struct rte *);
-  int (*rte_mergable)(struct rte *, struct rte *);
-  struct rte * (*rte_modify)(struct rte *, struct linpool *);
-  void (*rte_insert)(struct network *, struct rte *);
-  void (*rte_remove)(struct network *, struct rte *);
+  int (*rte_recalculate)(struct rtable *, struct network *, struct rte_storage *, struct rte_storage *, struct rte_storage *);
+  int (*rte_better)(struct rte_storage *, struct rte_storage *);
+  int (*rte_mergable)(struct rte_storage *, struct rte_storage *);
+  struct rta *(*rte_modify)(struct rte_storage *, struct linpool *);
+  void (*rte_insert)(struct network *, struct rte_storage *);
+  void (*rte_remove)(struct network *, struct rte_storage *);
 
   /* Hic sunt protocol-specific data */
 };
@@ -541,7 +541,7 @@ struct channel {
   struct rtable *in_table;             /* Internal table for received routes */
   struct event *reload_event;          /* Event responsible for reloading from in_table */
   struct fib_iterator reload_fit;      /* FIB iterator in in_table used during reloading */
-  struct rte *reload_next_rte;         /* Route iterator in in_table used during reloading */
+  struct rte_storage *reload_next_rte; /* Route iterator in in_table used during reloading */
   u8 reload_active;                    /* Iterator reload_fit is linked */
   u8 reload_pending;                   /* Reloading and another reload is scheduled */
   u8 refeed_pending;                   /* Refeeding and another refeed is scheduled */
index 5d77f5c42e255cc6f4344bac888222dbb96eefc9..41765638a29cd9b99da1deea406a688f72995af6 100644 (file)
@@ -196,7 +196,7 @@ struct rt_subscription {
 #define NHU_DIRTY      3
 
 typedef struct network {
-  struct rte *routes;                  /* Available routes for this network */
+  struct rte_storage *routes;                  /* Available routes for this network */
   struct fib_node n;                   /* FIB flags reserved for kernel syncer */
 } net;
 
@@ -228,40 +228,60 @@ struct hostentry {
 };
 
 typedef struct rte {
-  struct rte *next;
+  struct rta *attrs;                   /* Attributes of this route */
+  const net_addr *net;                 /* Network this RTE belongs to */
+  struct rte_src *src;                 /* Route source that created the route */
+} rte;
+
+struct rte_storage {
+  struct rte_storage *next;            /* Next in chain */
+
+  struct rta *attrs;                   /* Attributes of this route */
   net *net;                            /* Network this RTE belongs to */
   struct rte_src *src;                 /* Route source that created the route */
+
   struct channel *sender;              /* Channel used to send the route to the routing table */
-  struct rta *attrs;                   /* Attributes of this route */
   u32 id;                              /* Table specific route id */
   byte flags;                          /* Flags (REF_...) */
   byte pflags;                         /* Protocol-specific flags */
   btime lastmod;                       /* Last modified */
-} rte;
-
-/* Route export structure. Protocols get this structure as an information about
- * new routes on the channel. */
-
-struct rte_export {
-  net_addr *net;                       /* Network information */
-  rte *new;                            /* New route (NULL for withdraw) */
-  struct rte_src *new_src;             /* New route src (kept if route is rejected by preexport or filter) */
-  rte *old;                            /* Old route (only if export table is on) */
-  struct rte_src *old_src;             /* Old route src */
 };
 
-#define REF_COW                1               /* Copy this rte on write */
 #define REF_FILTERED   2               /* Route is rejected by import filter */
 #define REF_STALE      4               /* Route is stale in a refresh cycle */
 #define REF_DISCARD    8               /* Route is scheduled for discard */
 #define REF_MODIFY     16              /* Route is scheduled for modify */
 
 /* Route is valid for propagation (may depend on other flags in the future), accepts NULL */
-static inline int rte_is_valid(rte *r) { return r && !(r->flags & REF_FILTERED); }
+static inline int rte_is_valid(const struct rte_storage *r) { return r && !(r->flags & REF_FILTERED); }
 
 /* Route just has REF_FILTERED flag */
-static inline int rte_is_filtered(rte *r) { return !!(r->flags & REF_FILTERED); }
+static inline int rte_is_filtered(const struct rte_storage *r) { return !!(r->flags & REF_FILTERED); }
 
+/* Route export structure. Protocols get this structure as an information about
+ * new routes on the channel.
+ *
+ * In new:
+ *    if all fields are set then the route is to be exported,
+ *    if all fields are set but attrs == NULL then this route has been filtered out,
+ *    if all fields are NULL then there is no route at all to announce.
+ *
+ * In old:
+ *    if all fields are set then this is the complete previously exported route,
+ *    if all fields are set but attrs == NULL then this route was exported last time
+ *      but we don't run filters on it once more to get the modified version,
+ *    if all fields are NULL then there was no route at all to withdraw.
+ */
+
+struct rte_export {
+  rte new;                             /* New route */
+  rte old;                             /* Old route */
+  u32 new_id, old_id;                  /* Table specific route id for channel-private use */
+};
+
+#define RTE_EXPORT_NEW_OK(ep)  (!!ep->new.attrs)
+#define RTE_EXPORT_OLD_OK(ep)  (!!ep->old.net)
+#define RTE_EXPORT_IS_OK(ep)   (RTE_EXPORT_NEW_OK(ep) || RTE_EXPORT_OLD_OK(ep))
 
 /* Types of route announcement, also used as flags */
 #define RA_UNDEF       0               /* Undefined RA type */
@@ -279,19 +299,18 @@ static inline int rte_is_filtered(rte *r) { return !!(r->flags & REF_FILTERED);
 /**
  * rte_update - enter a new update to a routing table
  * @c: channel doing the update
- * @net: network address
  * @rte: a &rte representing the new route
  *
  * This function imports a new route to the appropriate table (via the channel).
- * Table keys are @net (obligatory) and @rte->attrs->src.
- * Both the @net and @rte pointers can be local.
- *
- * The route attributes (@rte->attrs) are obligatory. They can be also allocated
- * locally. Anyway, if you use an already-cached attribute object, you shall
- * call rta_clone() on that object yourself. (This semantics may change in future.)
+ * Table keys are @rte->net (obligatory) and @rte->src.
+ * The @rte pointer can be local as well as @rte->net. The @rte->src must be
+ * either the protocol's main_source, or looked-up by rt_get_source().
+ * The @rte pointer must be writable.
  *
- * If the route attributes are local, you may set @rte->attrs->src to NULL, then
- * the protocol's default route source will be supplied.
+ * For an update, the route attributes (@rte->attrs) are obligatory.
+ * They can be also allocated locally. If you use an already-cached
+ * attribute object, this function returns keeping your reference
+ * for yourself. No attributes means withdraw.
  *
  * When rte_update() gets a route, it automatically validates it. This includes
  * checking for validity of the given network and next hop addresses and also
@@ -305,21 +324,11 @@ static inline int rte_is_filtered(rte *r) { return !!(r->flags & REF_FILTERED);
  * All memory used for temporary allocations is taken from a special linpool
  * @rte_update_pool and freed when rte_update() finishes.
  */
-void rte_update(struct channel *c, const net_addr *net, struct rte *rte);
-
-/**
- * rte_withdraw - withdraw a route from a routing table
- * @c: channel doing the withdraw
- * @net: network address
- * @src: the route source identifier
- *
- * This function withdraws a previously announced route from the table.
- * No import filter is called. This function is idempotent. If no route
- * is found under the given key, it does nothing.
- *
- * If @src is NULL, the protocol's default route source is used.
- */
-void rte_withdraw(struct channel *c, const net_addr *net, struct rte_src *src);
+void rte_update(struct channel *c, struct rte *rte) NONNULL(1,2);
+static inline void rte_withdraw(struct channel *c, const net_addr *net, struct rte_src *src)
+{
+  rte e = { .net = net, .src = src}; rte_update(c, &e);
+}
 
 extern list routing_tables;
 struct config;
@@ -338,18 +347,18 @@ static inline net *net_find_valid(rtable *tab, const net_addr *addr)
 static inline net *net_get(rtable *tab, const net_addr *addr) { return (net *) fib_get(&tab->fib, addr); }
 void *net_route(rtable *tab, const net_addr *n);
 int net_roa_check(rtable *tab, const net_addr *n, u32 asn);
-rte *rte_find(net *net, struct rte_src *src);
-rte *rt_export_merged(struct channel *c, net *net, rte **rt_free, linpool *pool, int silent);
+struct rte_storage *rte_find(net *net, struct rte_src *src);
+_Bool rt_export_merged(struct channel *c, net *net, rte *best, linpool *pool, int silent);
 void rt_refresh_begin(rtable *t, struct channel *c);
 void rt_refresh_end(rtable *t, struct channel *c);
 void rt_modify_stale(rtable *t, struct channel *c);
 void rt_schedule_prune(rtable *t);
-void rte_dump(rte *);
-void rte_free(rte *);
-rte *rte_do_cow(rte *);
-rte *rte_store(rte *);
-static inline rte * rte_cow(rte *r) { return (r->flags & REF_COW) ? rte_do_cow(r) : r; }
-rte *rte_cow_rta(rte *r, linpool *lp);
+void rte_dump(struct rte_storage *);
+void rte_free(struct rte_storage *);
+struct rte_storage *rte_store(const rte *, net *n);
+void rte_copy_metadata(struct rte_storage *dest, struct rte_storage *src);
+static inline rte rte_copy(const struct rte_storage *r)
+{ return (rte) { .attrs = r->attrs, .net = r->net->n.addr, .src = r->src }; }
 void rt_dump(rtable *);
 void rt_dump_all(void);
 int rt_feed_channel(struct channel *c);
@@ -358,7 +367,7 @@ void rt_feed_channel_abort(struct channel *c);
 int rt_reload_channel(struct channel *c);
 void rt_reload_channel_abort(struct channel *c);
 void rt_prune_sync(rtable *t, int all);
-int rte_update_out(struct channel *c, const net_addr *n, struct rte_src *src, rte *new, rte **old_exported, int refeed);
+int rte_update_out(struct channel *c, rte *new, rte *old, struct rte_storage **old_stored, int refeed);
 struct rtable_config *rt_new_table(struct symbol *s, uint addr_type);
 
 
@@ -679,7 +688,7 @@ void rta_dump(rta *);
 void rta_dump_all(void);
 void rta_show(struct cli *, rta *);
 
-u32 rt_get_igp_metric(rte *rt);
+u32 rt_get_igp_metric(struct rta *);
 struct hostentry * rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep);
 void rta_apply_hostentry(rta *a, struct hostentry *he, mpls_label_stack *mls);
 
index dae1223b4d0b76d07a7f8280662cfda05d1a9db9..f40c588ed8b561b699e5860a9e1aab6442ed2882 100644 (file)
@@ -85,9 +85,10 @@ dev_ifa_notify(struct proto *P, uint flags, struct ifa *ad)
       };
       rte e0 = {
        .src = rt_get_source(P, ad->iface->index),
+       .net = net,
        .attrs = &a0,
       };
-      rte_update(c, net, &e0);
+      rte_update(c, &e0);
     }
 }
 
index 4a96ad3cab2fab0b97be3520d3e1be639c170004..1b56d5b997ac78468f0c796ef591522626da2367 100644 (file)
@@ -37,16 +37,16 @@ rt_show_get_kernel(struct rt_show_data *d)
 }
 
 static void
-rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, int primary)
+rt_show_rte(struct cli *c, byte *ia, struct rte *e, struct rte_storage *er, struct rt_show_data *d)
 {
   byte from[IPA_MAX_TEXT_LENGTH+8];
   byte tm[TM_DATETIME_BUFFER_SIZE], info[256];
   rta *a = e->attrs;
-  int sync_error = d->kernel ? krt_get_sync_error(d->kernel, e) : 0;
-  void (*get_route_info)(struct rte *, byte *buf);
+  int sync_error = d->kernel ? krt_get_sync_error(d->kernel, er->id) : 0;
+  void (*get_route_info)(struct rte *, struct rte_storage *, byte *);
   struct nexthop *nh;
 
-  tm_format_time(tm, &config->tf_route, e->lastmod);
+  tm_format_time(tm, &config->tf_route, er->lastmod);
   if (ipa_nonzero(a->from) && !ipa_equal(a->from, a->nh.gw))
     bsprintf(from, " from %I", a->from);
   else
@@ -58,7 +58,7 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, int primary
 
   get_route_info = e->src->proto->proto->get_route_info;
   if (get_route_info)
-    get_route_info(e, info);
+    get_route_info(e, er, info);
   else
     bsprintf(info, " (%d)", a->pref);
 
@@ -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->proto->name, tm, from, (er == er->net->routes) ? (sync_error ? " !" : " *") : "", info);
 
   if (a->dest == RTD_UNICAST)
     for (nh = &(a->nh); nh; nh = nh->next)
@@ -101,7 +101,6 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, int primary
 static void
 rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
 {
-  rte *e, *ee;
   byte ia[NET_MAX_TEXT_LENGTH+1];
   struct channel *ec = d->tab->export_channel;
 
@@ -114,9 +113,9 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
 
   bsnprintf(ia, sizeof(ia), "%N", n->n.addr);
 
-  for (e = n->routes; e; e = e->next)
+  for (struct rte_storage *er = n->routes; er; er = er->next)
     {
-      if (rte_is_filtered(e) != d->filtered)
+      if (rte_is_filtered(er) != d->filtered)
        continue;
 
       d->rt_counter++;
@@ -126,7 +125,7 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
       if (pass)
        continue;
 
-      ee = e;
+      struct rte e = rte_copy(er);
 
       /* Export channel is down, do not try to export routes to it */
       if (ec && (ec->export_state == ES_DOWN))
@@ -134,7 +133,7 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
 
       if (d->export_mode == RSEM_EXPORTED)
         {
-         if (!bmap_test(&ec->export_map, ee->id))
+         if (!bmap_test(&ec->export_map, er->id))
            goto skip;
 
          // if (ec->ra_mode != RA_ANY)
@@ -143,17 +142,14 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
       else if ((d->export_mode == RSEM_EXPORT) && (ec->ra_mode == RA_MERGED))
        {
          /* Special case for merged export */
-         rte *rt_free;
-         e = rt_export_merged(ec, n, &rt_free, c->show_pool, 1);
          pass = 1;
-
-         if (!e)
-         { e = ee; goto skip; }
+         if (!rt_export_merged(ec, n, &e, c->show_pool, 1))
+           goto skip;
        }
       else if (d->export_mode)
        {
          struct proto *ep = ec->proto;
-         int ic = ep->preexport ? ep->preexport(ep, e) : 0;
+         int ic = ep->preexport ? ep->preexport(ec, &e) : 0;
 
          if (ec->ra_mode == RA_OPTIMAL || ec->ra_mode == RA_MERGED)
            pass = 1;
@@ -179,24 +175,19 @@ 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 != er->src->proto))
        goto skip;
 
       if (f_run(d->filter, &e, c->show_pool, 0) > F_ACCEPT)
        goto skip;
 
       if (d->stats < 2)
-       rt_show_rte(c, ia, e, d, (e->net->routes == ee));
+       rt_show_rte(c, ia, &e, er, d);
 
       d->show_counter++;
       ia[0] = 0;
 
     skip:
-      if (e != ee)
-      {
-       rte_free(e);
-       e = ee;
-      }
       lp_flush(c->show_pool);
 
       if (d->primary_only)
index 21acad7708f3f93802c32d5529aa9b193795f6d5..76d7171400c38d23257a69d401d45aa6c6fe3ab0 100644 (file)
@@ -271,72 +271,66 @@ net_roa_check(rtable *tab, const net_addr *n, u32 asn)
  * The rte_find() function returns a route for destination @net
  * which is from route source @src.
  */
-rte *
+struct rte_storage *
 rte_find(net *net, struct rte_src *src)
 {
-  rte *e = net->routes;
+  struct rte_storage *e = net->routes;
 
   while (e && e->src != src)
     e = e->next;
   return e;
 }
 
-rte *
-rte_do_cow(rte *r)
+struct rte_storage *
+rte_store(const rte *r, net *n)
 {
-  rte *e = sl_alloc(rte_slab);
+  struct rte_storage *e = sl_alloc(rte_slab);
+  *e = (struct rte_storage) {
+    .attrs = r->attrs,
+    .net = n,
+    .src = r->src,
+  };
 
-  memcpy(e, r, sizeof(rte));
-  e->attrs = rta_clone(r->attrs);
-  e->flags = 0;
   rt_lock_source(e->src);
-  return e;
-}
 
-rte *
-rte_store(rte *r)
-{
-  rte *e = sl_alloc(rte_slab);
-  memcpy(e, r, sizeof(rte));
-
-  rt_lock_source(e->src);
   if (e->attrs->cached)
     e->attrs = rta_clone(r->attrs);
   else
     e->attrs = rta_lookup(r->attrs);
+
   return e;
 }
 
+void
+rte_copy_metadata(struct rte_storage *dest, struct rte_storage *src)
+{
+  dest->sender = src->sender;
+  dest->flags = src->flags & REF_FILTERED;
+  dest->pflags = src->pflags;
+  dest->lastmod = src->lastmod;
+}
+
 /**
  * rte_cow_rta - get a private writable copy of &rte with writable &rta
  * @r: a route entry to be copied
  * @lp: a linpool from which to allocate &rta
  *
- * rte_cow_rta() takes a &rte and prepares it and associated &rta for
- * modification. There are three possibilities: First, both &rte and &rta are
- * private copies, in that case they are returned unchanged.  Second, &rte is
- * private copy, but &rta is cached, in that case &rta is duplicated using
- * rta_do_cow(). Third, both &rte is shared and &rta is cached, in that case
- * both structures are duplicated by rte_do_cow() and rta_do_cow().
+ * rte_cow_rta() returns directly a &rte struct; the route attributes are
+ * a shallow copy made on the given linpool, src is not locked.
  *
- * Note that in the second case, cached &rta loses one reference, while private
- * copy created by rta_do_cow() is a shallow copy sharing indirect data (eattrs,
- * nexthops, ...) with it. To work properly, original shared &rta should have
- * another reference during the life of created private copy.
+ * To work properly, the caller must own the original rte_storage whole the
+ * time this route is being used.
  *
- * Result: a pointer to the new writable &rte with writable &rta.
+ * Result: a new &rte with writable &rta.
  */
-rte *
-rte_cow_rta(rte *r, linpool *lp)
+rte
+rte_cow_rta(const struct rte_storage *r, linpool *lp)
 {
-  if (!rta_is_cached(r->attrs))
-    return r;
-
-  r = rte_cow(r);
-  rta *a = rta_do_cow(r->attrs, lp);
-  rta_free(r->attrs);
-  r->attrs = a;
-  return r;
+  return (rte) {
+    .attrs = rta_do_cow(r->attrs, lp),
+    .net = r->net->n.addr,
+    .src = r->src,
+  };
 }
 
 /**
@@ -346,34 +340,34 @@ rte_cow_rta(rte *r, linpool *lp)
  * rte_free() deletes the given &rte from the routing table it's linked to.
  */
 void
-rte_free(rte *e)
+rte_free(struct rte_storage *e)
 {
   rt_unlock_source(e->src);
-  if (rta_is_cached(e->attrs))
-    rta_free(e->attrs);
+  rta_free(e->attrs);
   sl_free(rte_slab, e);
 }
 
-static inline void
-rte_free_quick(rte *e)
+enum export_filter_result
 {
-  rt_unlock_source(e->src);
-  rta_free(e->attrs);
-  sl_free(rte_slab, e);
-}
+  EFR_PREEXPORT_ACCEPT = 1,
+  EFR_FILTER_ACCEPT,
+  EFR_FILTER_REJECT,
+  EFR_PREEXPORT_REJECT,
+  EFR_CACHED_REJECT,
+} new_efr;
 
 struct rte_export_internal {
   net *net;
-  rte *new, *old, *new_best, *old_best;
-  rte *rt_free;
+  struct rte_storage *new, *old, *new_best, *old_best;
   struct rte_export pub;
   _Bool refeed;
+  _Bool accepted;
 };
 
 static int                             /* Actually better or at least as good as */
-rte_better(rte *new, rte *old)
+rte_better(struct rte_storage *new, struct rte_storage *old)
 {
-  int (*better)(rte *, rte *);
+  int (*better)(struct rte_storage *, struct rte_storage *);
 
   if (!rte_is_valid(old))
     return 1;
@@ -399,9 +393,9 @@ rte_better(rte *new, rte *old)
 }
 
 static int
-rte_mergable(rte *pri, rte *sec)
+rte_mergable(struct rte_storage *pri, struct rte_storage *sec)
 {
-  int (*mergable)(rte *, rte *);
+  int (*mergable)(struct rte_storage *, struct rte_storage *);
 
   if (!rte_is_valid(pri) || !rte_is_valid(sec))
     return 0;
@@ -422,8 +416,8 @@ static void
 rte_trace(struct channel *c, rte *e, int dir, char *msg)
 {
   log(L_TRACE "%s.%s %c %s %N %s",
-      c->proto->name, c->name ?: "?", dir, msg, e->net->n.addr,
-      rta_dest_name(e->attrs->dest));
+      c->proto->name, c->name ?: "?", dir, msg, e->net,
+      e->attrs ? rta_dest_name(e->attrs->dest) : "-");
 }
 
 static inline void
@@ -440,25 +434,23 @@ rte_trace_out(uint flag, struct channel *c, rte *e, char *msg)
     rte_trace(c, e, '<', msg);
 }
 
-static rte *
-export_filter_(struct channel *c, rte *rt0, rte **rt_free, linpool *pool, int silent)
+enum export_filter_result
+export_filter_(struct channel *c, struct rte *rt, u32 id, linpool *pool, int silent)
 {
   struct proto *p = c->proto;
   const struct filter *filter = c->out_filter;
   struct proto_stats *stats = &c->stats;
-  rte *rt;
   int v;
-
-  rt = rt0;
-  *rt_free = NULL;
+  enum export_filter_result efr = 0;
 
   /* Do nothing if we have already rejected the route */
-  if (silent && bmap_test(&c->export_reject_map, rt0->id))
-    return NULL;
+  if (silent && bmap_test(&c->export_reject_map, id))
+    return EFR_CACHED_REJECT;
 
-  v = p->preexport ? p->preexport(p, rt) : 0;
+  v = p->preexport ? p->preexport(c, rt) : 0;
   if (v < 0)
     {
+      efr = EFR_PREEXPORT_REJECT;
       if (silent)
        goto reject;
 
@@ -469,16 +461,18 @@ export_filter_(struct channel *c, rte *rt0, rte **rt_free, linpool *pool, int si
     }
   if (v > 0)
     {
+      efr = EFR_PREEXPORT_ACCEPT;
       if (!silent)
        rte_trace_out(D_FILTERS, c, rt, "forced accept by protocol");
       goto accept;
     }
 
   v = filter && ((filter == FILTER_REJECT) ||
-                (f_run(filter, &rt, pool,
+                (f_run(filter, rt, pool,
                        (silent ? FF_SILENT : 0)) > F_ACCEPT));
   if (v)
     {
+      efr = EFR_FILTER_REJECT;
       if (silent)
        goto reject;
 
@@ -487,29 +481,24 @@ export_filter_(struct channel *c, rte *rt0, rte **rt_free, linpool *pool, int si
       goto reject;
     }
 
+  efr = EFR_FILTER_ACCEPT;
+
  accept:
   /* We have accepted the route */
-  bmap_clear(&c->export_reject_map, rt0->id);
-
-  /* Discard temporary rte */
-  if (rt != rt0)
-    *rt_free = rt;
-  return rt;
+  bmap_clear(&c->export_reject_map, id);
+  return efr;
 
  reject:
   /* We have rejected the route */
-  bmap_set(&c->export_reject_map, rt0->id);
-
-  /* Discard temporary rte */
-  if (rt != rt0)
-    rte_free(rt);
-  return NULL;
+  bmap_set(&c->export_reject_map, id);
+  rt->attrs = NULL;
+  return efr;
 }
 
-static inline rte *
-export_filter(struct channel *c, rte *rt0, rte **rt_free, int silent)
+enum export_filter_result
+export_filter(struct channel *c, rte *rt, u32 id, int silent)
 {
-  return export_filter_(c, rt0, rt_free, rte_update_pool, silent);
+  return export_filter_(c, rt, id, rte_update_pool, silent);
 }
 
 USE_RESULT static _Bool
@@ -524,26 +513,28 @@ rt_notify_basic(struct channel *c, struct rte_export_internal *e)
 
   if (e->new)
   {
-    ep->new = export_filter(c, e->new, &e->rt_free, 0);
-    ep->new_src = e->new->src;
+    ep->new = rte_copy(e->new);
+    ep->new_id = e->new->id;
+
+    if (export_filter(c, &ep->new, ep->new_id, 0) >= EFR_FILTER_REJECT)
+      /* If reject, send a withdraw */
+      ep->new.attrs = NULL;
   }
 
   if (e->old && bmap_test(&c->export_map, e->old->id))
   {
-    ep->old = e->old;
-    ep->old_src = e->old->src;
+    ep->old = rte_copy(e->old);
+    ep->old_id = e->old->id;
   }
 
-  return (ep->new || ep->old);
+  return RTE_EXPORT_IS_OK(ep);
 }
 
 USE_RESULT static _Bool
 rt_notify_accepted(struct channel *c, struct rte_export_internal *e)
 {
   // struct proto *p = c->proto;
-  rte *new_best = NULL;
-  rte *old_best = NULL;
-  rte *new_filtered = NULL;
+  struct rte_storage *old_best = NULL;
   int new_first = 0;
 
   /*
@@ -572,7 +563,7 @@ rt_notify_accepted(struct channel *c, struct rte_export_internal *e)
     old_best = e->old;
   else
   {
-    for (rte *r = e->net->routes; rte_is_valid(r); r = r->next)
+    for (struct rte_storage *r = e->net->routes; rte_is_valid(r); r = r->next)
     {
       if (bmap_test(&c->export_map, r->id))
       {
@@ -586,34 +577,38 @@ rt_notify_accepted(struct channel *c, struct rte_export_internal *e)
     }
   }
 
+  struct rte_export *ep = &e->pub;
+
   /* Find new_best */
   if ((e->new == e->old) || (old_best == e->old))
   {
     /* Feed or old_best changed -> find first accepted by filters */
-    for (rte *r = e->net->routes; rte_is_valid(r); r = r->next)
-      if (new_best = export_filter(c, r, &e->rt_free, 0))
+    for (struct rte_storage *r = e->net->routes; rte_is_valid(r); r = r->next)
+    {
+      ep->new = rte_copy(r);
+      if (export_filter(c, &ep->new, ep->new_id = r->id, 0) <= EFR_FILTER_ACCEPT)
        break;
+    }
   }
   else
   {
-    /* Other cases -> either new_changed, or old_best (and nothing changed) */
-    if (new_first && (new_filtered = export_filter(c, e->new, &e->rt_free, 0)))
-      new_best = new_filtered;
-    else
-      return 0;
-  }
+    if (!new_first) /* old_best is still best, nothing has changed */
+      return NULL;
 
-  if (!new_best && !old_best)
-    return 0;
+    ep->new = rte_copy(e->new);
+    if (export_filter(c, &ep->new, ep->new_id = e->new->id, 0) >= EFR_FILTER_REJECT)
+      /* This route is better than old_best but doesn't pass */
+      return NULL;
+  }
 
-  e->pub = (struct rte_export) {
-    .new = new_best,
-    .new_src = new_best ? new_best->src : NULL,
-    .old = old_best,
-    .old_src = old_best ? old_best->src : NULL,
-  };
+  if (old_best)
+  {
+    /* Store the old_best route */
+    ep->old = rte_copy(old_best);
+    ep->old_id = old_best->id;
+  }
 
-  return 1;
+  return RTE_EXPORT_IS_OK(ep);
 }
 
 
@@ -623,39 +618,38 @@ nexthop_merge_rta(struct nexthop *nhs, rta *a, linpool *pool, int max)
   return nexthop_merge(nhs, &(a->nh), 1, 0, max, pool);
 }
 
-rte *
-rt_export_merged(struct channel *c, net *net, rte **rt_free, linpool *pool, int silent)
+_Bool
+rt_export_merged(struct channel *c, net *net, rte *best, linpool *pool, int silent)
 {
   // struct proto *p = c->proto;
   struct nexthop *nhs = NULL;
-  rte *best0, *best, *rt0, *rt, *tmp;
-
-  best0 = net->routes;
-  *rt_free = NULL;
+  struct rte_storage *best0 = net->routes;
 
   if (!rte_is_valid(best0))
-    return NULL;
+    return 0;
 
-  best = export_filter_(c, best0, rt_free, pool, silent);
+  *best = rte_copy(best0);
+  if (export_filter_(c, best, best0->id, pool, silent) >= EFR_FILTER_REJECT)
+    /* Best route doesn't pass the filter */
+    return 0;
 
-  if (!best || !rte_is_reachable(best))
-    return best;
+  if (!rte_is_reachable(best))
+    /* Unreachable routes can't be merged */
+    return 1;
 
-  for (rt0 = best0->next; rt0; rt0 = rt0->next)
+  for (struct rte_storage *rt0 = best0->next; rt0; rt0 = rt0->next)
   {
     if (!rte_mergable(best0, rt0))
       continue;
 
-    rt = export_filter_(c, rt0, &tmp, pool, 1);
-
-    if (!rt)
+    struct rte tmp = rte_copy(rt0);
+    if (export_filter_(c, &tmp, rt0->id, pool, 1) >= EFR_FILTER_REJECT)
       continue;
 
-    if (rte_is_reachable(rt))
-      nhs = nexthop_merge_rta(nhs, rt->attrs, pool, c->merge_limit);
+    if (!rte_is_reachable(&tmp))
+      continue;
 
-    if (tmp)
-      rte_free(tmp);
+    nhs = nexthop_merge_rta(nhs, tmp.attrs, pool, c->merge_limit);
   }
 
   if (nhs)
@@ -663,16 +657,12 @@ rt_export_merged(struct channel *c, net *net, rte **rt_free, linpool *pool, int
     nhs = nexthop_merge_rta(nhs, best->attrs, pool, c->merge_limit);
 
     if (nhs->next)
-    {
-      best = rte_cow_rta(best, pool);
-      nexthop_link(best->attrs, nhs);
-    }
-  }
+      best->attrs = rta_cow(best->attrs, pool);
 
-  if (best != best0)
-    *rt_free = best;
+    nexthop_link(best->attrs, nhs);
+  }
 
-  return best;
+  return 1;
 }
 
 
@@ -702,19 +692,19 @@ rt_notify_merged(struct channel *c, struct rte_export_internal *e)
   /* Prepare new merged route */
   if (e->new_best)
   {
-    ep->new = rt_export_merged(c, e->net, &(e->rt_free), rte_update_pool, 0);
-    ep->new_src = e->net->routes->src;
+    ep->new_id = e->new->id;
+    if (!rt_export_merged(c, e->net, &ep->new, rte_update_pool, 0))
+      ep->new.attrs = NULL;
   }
 
   /* Check old merged route */
-  if (e->old_best)
-    ep->old = bmap_test(&c->export_map, e->old_best->id) ? e->old_best : NULL;
-
-  if (!ep->new && !ep->old)
-    return 0;
+  if (e->old_best && bmap_test(&c->export_map, e->old_best->id))
+  {
+    ep->old_id = e->old_best->id;
+    ep->old = rte_copy(e->old_best);
+  }
 
-  ep->old_src = ep->old ? ep->old->src : NULL;
-  return 1;
+  return RTE_EXPORT_IS_OK(ep);
 }
 
 static void
@@ -749,20 +739,22 @@ rte_export(struct channel *c, struct rte_export_internal *e)
   }
 
   if (!accepted)
+  {
+    DBG("Idempotent export.\n");
     goto cleanup;
+  }
 
   struct rte_export *ep = &(e->pub);
-  ep->net = e->net->n.addr;
 
   struct proto *p = c->proto;
   struct proto_stats *stats = &c->stats;
 
-  if (e->refeed && e->pub.new)
+  if (e->refeed && ep->new.attrs)
     c->refeed_count++;
 
   /* Apply export limit */
   struct channel_limit *l = &c->out_limit;
-  if (l->action && !ep->old && ep->new)
+  if (l->action && !ep->old.attrs && ep->new.attrs)
   {
     if (stats->exp_routes >= l->limit)
       channel_notify_limit(c, l, PLD_OUT, stats->exp_routes);
@@ -770,59 +762,57 @@ rte_export(struct channel *c, struct rte_export_internal *e)
     if (l->state == PLS_BLOCKED)
     {
       stats->exp_updates_rejected++;
-      rte_trace_out(D_FILTERS, c, ep->new, "rejected [limit]");
+      rte_trace_out(D_FILTERS, c, &ep->new, "rejected [limit]");
       goto cleanup;
     }
   }
 
+  struct rte_storage *old_stored = NULL;
   /* Apply export table */
-  rte *old_exported = NULL;
   if (c->out_table)
   {
-    if (!rte_update_out(c, ep->net, ep->old_src, ep->new, &(old_exported), e->refeed))
+    if (!rte_update_out(c, &(ep->new), &(ep->old), &old_stored, e->refeed))
       goto cleanup;
   }
-  else if (c->out_filter == FILTER_ACCEPT)
-    old_exported = ep->old;
+  else if (c->out_filter != FILTER_ACCEPT)
+    /* We aren't sure about the old route attributes */
+    ep->old.attrs = NULL;
 
-  if (ep->new)
+  if (ep->new.attrs)
     stats->exp_updates_accepted++;
   else
     stats->exp_withdraws_accepted++;
 
-  if (ep->old)
+  if (ep->old.attrs)
   {
-    bmap_clear(&c->export_map, ep->old->id);
+    bmap_clear(&c->export_map, ep->old_id);
     stats->exp_routes--;
   }
 
-  if (ep->new)
+  if (ep->new.attrs)
   {
-    bmap_set(&c->export_map, ep->new->id);
+    bmap_set(&c->export_map, ep->new_id);
     stats->exp_routes++;
   }
 
   if (p->debug & D_ROUTES)
   {
-    if (ep->new && ep->old)
-      rte_trace_out(D_ROUTES, c, ep->new, "replaced");
-    else if (ep->new)
-      rte_trace_out(D_ROUTES, c, ep->new, "added");
-    else if (ep->old)
-      rte_trace_out(D_ROUTES, c, ep->old, "removed");
+    if (RTE_EXPORT_NEW_OK(ep) && RTE_EXPORT_OLD_OK(ep))
+      rte_trace_out(D_ROUTES, c, &ep->new, "replaced");
+    else if (RTE_EXPORT_NEW_OK(ep))
+      rte_trace_out(D_ROUTES, c, &ep->new, "added");
+    else if (RTE_EXPORT_OLD_OK(ep))
+      rte_trace_out(D_ROUTES, c, &ep->old, "removed");
+    else
+      bug("Idempotent exports should have been ignored by now");
   }
 
-  ep->old = old_exported;
-
   p->rt_notify(c, ep);
 
-  if (c->out_table && old_exported)
-    rte_free_quick(old_exported);
+  if (old_stored)
+    rte_free(old_stored);
 
 cleanup:
-  if (e->rt_free)
-    rte_free(e->rt_free);
-
   if (e->old && (!e->new || (e->new->id != e->old->id)))
     bmap_clear(&c->export_reject_map, e->old->id);
 }
@@ -868,8 +858,8 @@ cleanup:
  * done outside of scope of rte_announce().
  */
 static void
-rte_announce(rtable *tab, uint type, net *net, rte *new, rte *old,
-            rte *new_best, rte *old_best)
+rte_announce(rtable *tab, uint type, net *net, struct rte_storage *new, struct rte_storage *old,
+            struct rte_storage *new_best, struct rte_storage *old_best)
 {
   if (!rte_is_valid(new))
     new = NULL;
@@ -927,134 +917,80 @@ rte_announce(rtable *tab, uint type, net *net, rte *new, rte *old,
   }
 }
 
-static inline int
-rte_validate(rte *e)
-{
-  int c;
-  net *n = e->net;
-
-  if (!net_validate(n->n.addr))
-  {
-    log(L_WARN "Ignoring bogus prefix %N received via %s",
-       n->n.addr, e->sender->proto->name);
-    return 0;
-  }
-
-  /* FIXME: better handling different nettypes */
-  c = !net_is_flow(n->n.addr) ?
-    net_classify(n->n.addr): (IADDR_HOST | SCOPE_UNIVERSE);
-  if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK))
-  {
-    log(L_WARN "Ignoring bogus route %N received via %s",
-       n->n.addr, e->sender->proto->name);
-    return 0;
-  }
-
-  if (net_type_match(n->n.addr, NB_DEST) == !e->attrs->dest)
-  {
-    log(L_WARN "Ignoring route %N with invalid dest %d received via %s",
-       n->n.addr, e->attrs->dest, e->sender->proto->name);
-    return 0;
-  }
-
-  if ((e->attrs->dest == RTD_UNICAST) && !nexthop_is_sorted(&(e->attrs->nh)))
-  {
-    log(L_WARN "Ignoring unsorted multipath route %N received via %s",
-       n->n.addr, e->sender->proto->name);
-    return 0;
-  }
-
-  return 1;
-}
-
 static int
-rte_same(rte *x, rte *y)
+rte_same(struct rte_storage *x, rte *y, _Bool fy)
 {
   /* rte.flags are not checked, as they are mostly internal to rtable */
   return
     x->attrs == y->attrs &&
-    rte_is_filtered(x) == rte_is_filtered(y);
+    x->src == y->src &&
+    rte_is_filtered(x) == fy;
 }
 
-static inline int rte_is_ok(rte *e) { return e && !rte_is_filtered(e); }
-
-static void
-rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src)
+static void NONNULL(1,2,3)
+rte_recalculate(struct channel *c, net *net, rte *new, _Bool filtered)
 {
   struct proto *p = c->proto;
   struct rtable *table = c->table;
   struct proto_stats *stats = &c->stats;
   static struct tbf rl_pipe = TBF_DEFAULT_LOG_LIMITS;
-  rte *before_old = NULL;
-  rte *old_best = net->routes;
-  rte *old = NULL;
-  rte **k;
+  struct rte_storage *old_best = net->routes;
+  struct rte_storage *old = NULL, **before_old = NULL;
 
-  k = &net->routes;                    /* Find and remove original route from the same protocol */
-  while (old = *k)
+  /* Find and remove original route from the same protocol */
+  for (before_old = &net->routes; old = *before_old; before_old = &(old->next))
     {
-      if (old->src == src)
-       {
-         /* If there is the same route in the routing table but from
-          * a different sender, then there are two paths from the
-          * source protocol to this routing table through transparent
-          * pipes, which is not allowed.
-          *
-          * We log that and ignore the route. If it is withdraw, we
-          * ignore it completely (there might be 'spurious withdraws',
-          * see FIXME in do_rte_announce())
-          */
-         if (old->sender->proto != p)
-           {
-             if (new)
-               {
-                 log_rl(&rl_pipe, L_ERR "Pipe collision detected when sending %N to table %s",
-                     net->n.addr, table->name);
-                 rte_free_quick(new);
-               }
-             return;
-           }
+      /* Another route */
+      if (old->src != new->src)
+       continue;
 
-         if (new && rte_same(old, new))
-           {
-             /* No changes, ignore the new route and refresh the old one */
+      /* If there is the same route in the routing table but from
+       * a different sender, then there are two paths from the
+       * source protocol to this routing table through transparent
+       * pipes, which is not allowed.
+       *
+       * We log that and ignore the route. If it is withdraw, we
+       * ignore it completely (there might be 'spurious withdraws',
+       * see FIXME in do_rte_announce())
+       */
+      if (old->sender->proto != p)
+       {
+         if (new->attrs)
+             log_rl(&rl_pipe, L_ERR "Pipe collision detected when sending %N to table %s",
+                 net->n.addr, table->name);
+         return;
+       }
 
-             old->flags &= ~(REF_STALE | REF_DISCARD | REF_MODIFY);
+      if (new->attrs && rte_same(old, new, filtered))
+       {
+         /* No changes, ignore the new route and refresh the old one */
 
-             if (!rte_is_filtered(new))
-               {
-                 stats->imp_updates_ignored++;
-                 rte_trace_in(D_ROUTES, c, new, "ignored");
-               }
+         old->flags &= ~(REF_STALE | REF_DISCARD | REF_MODIFY);
 
-             rte_free_quick(new);
-             return;
+         if (!filtered)
+           {
+             stats->imp_updates_ignored++;
+             rte_trace_in(D_ROUTES, c, new, "ignored");
            }
-         *k = old->next;
-         table->rt_count--;
-         break;
+
+         return;
        }
-      k = &old->next;
-      before_old = old;
+      *before_old = old->next;
+      table->rt_count--;
+      break;
     }
 
-  /* Save the last accessed position */
-  rte **pos = k;
-
-  if (!old)
-    before_old = NULL;
-
-  if (!old && !new)
+  if (!old && !new->attrs)
     {
       stats->imp_withdraws_ignored++;
       return;
     }
 
-  int new_ok = rte_is_ok(new);
-  int old_ok = rte_is_ok(old);
+  _Bool new_ok = new->attrs && !filtered;
+  _Bool old_ok = old && !rte_is_filtered(old);
 
   struct channel_limit *l = &c->rx_limit;
-  if (l->action && !old && new && !c->in_table)
+  if (l->action && !old && new->attrs && !c->in_table)
     {
       u32 all_routes = stats->imp_routes + stats->filt_routes;
 
@@ -1068,7 +1004,6 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src)
 
          stats->imp_updates_ignored++;
          rte_trace_in(D_FILTERS, c, new, "ignored [limit]");
-         rte_free_quick(new);
          return;
        }
     }
@@ -1092,14 +1027,14 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src)
          rte_trace_in(D_FILTERS, c, new, "ignored [limit]");
 
          if (c->in_keep_filtered)
-           new->flags |= REF_FILTERED;
+           filtered = 1;
          else
-           { rte_free_quick(new); new = NULL; }
+           new->attrs = NULL;
 
          /* Note that old && !new could be possible when
             c->in_keep_filtered changed in the recent past. */
 
-         if (!old && !new)
+         if (!old && !new->attrs)
            return;
 
          new_ok = 0;
@@ -1119,27 +1054,36 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src)
 
  skip_stats1:
 
-  if (new)
-    rte_is_filtered(new) ? stats->filt_routes++ : stats->imp_routes++;
+  if (new->attrs)
+    filtered ? stats->filt_routes++ : stats->imp_routes++;
   if (old)
     rte_is_filtered(old) ? stats->filt_routes-- : stats->imp_routes--;
 
+  /* Store the new route now, it is going to be inserted. */
+  struct rte_storage *new_stored = NULL;
+
+  if (new->attrs) {
+    new_stored = rte_store(new, net);
+    new_stored->sender = c;
+
+    if (filtered)
+      new_stored->flags |= REF_FILTERED;
+  }
+
   if (table->config->sorted)
     {
       /* If routes are sorted, just insert new route to appropriate position */
-      if (new)
+      if (new_stored)
        {
-         if (before_old && !rte_better(new, before_old))
-           k = &before_old->next;
-         else
-           k = &net->routes;
+         if (*before_old && rte_better(new_stored, *before_old))
+           before_old = &net->routes;
 
-         for (; *k; k=&(*k)->next)
-           if (rte_better(new, *k))
+         for (; *before_old; before_old = &(*before_old)->next)
+           if (rte_better(new_stored, *before_old))
              break;
 
-         new->next = *k;
-         *k = new;
+         new_stored->next = *before_old;
+         *before_old = new_stored;
 
          table->rt_count++;
        }
@@ -1149,16 +1093,16 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src)
       /* 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, old, old_best))
+      if (new->src->proto->rte_recalculate && new->src->proto->rte_recalculate(table, net, new_stored, old, old_best))
        goto do_recalculate;
 
-      if (new && rte_better(new, old_best))
+      if (new_stored && rte_better(new_stored, old_best))
        {
          /* The first case - the new route is cleary optimal,
             we link it at the first position */
 
-         new->next = net->routes;
-         net->routes = new;
+         new_stored->next = net->routes;
+         net->routes = new_stored;
 
          table->rt_count++;
        }
@@ -1172,75 +1116,75 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src)
 
        do_recalculate:
          /* Add the new route to the list */
-         if (new)
+         if (new_stored)
            {
-             new->next = *pos;
-             *pos = new;
-
+             new_stored->next = *before_old;
+             *before_old = new_stored;
              table->rt_count++;
            }
 
          /* Find a new optimal route (if there is any) */
          if (net->routes)
            {
-             rte **bp = &net->routes;
-             for (k=&(*bp)->next; *k; k=&(*k)->next)
+             struct rte_storage **bp = &net->routes;
+             for (struct rte_storage **k=&(*bp)->next; *k; k=&(*k)->next)
                if (rte_better(*k, *bp))
                  bp = k;
 
              /* And relink it */
-             rte *best = *bp;
+             struct rte_storage *best = *bp;
              *bp = best->next;
              best->next = net->routes;
              net->routes = best;
            }
        }
-      else if (new)
+      else if (new_stored)
        {
          /* The third case - the new route is not better than the old
             best route (therefore old_best != NULL) and the old best
             route was not removed (therefore old_best == net->routes).
             We just link the new route to the old/last position. */
 
-         new->next = *pos;
-         *pos = new;
+         new_stored->next = *before_old;
+         *before_old = new_stored;
 
          table->rt_count++;
        }
       /* The fourth (empty) case - suboptimal route was removed, nothing to do */
     }
 
-  if (new)
+  if (new_stored)
     {
-      new->lastmod = current_time();
+      new_stored->lastmod = current_time();
 
       if (!old)
         {
-         new->id = hmap_first_zero(&table->id_map);
-         hmap_set(&table->id_map, new->id);
+         new_stored->id = hmap_first_zero(&table->id_map);
+         hmap_set(&table->id_map, new_stored->id);
        }
       else
-       new->id = old->id;
+       new_stored->id = old->id;
     }
 
   /* Log the route change */
   if ((c->debug & D_ROUTES) || (p->debug & D_ROUTES))
     {
       if (new_ok)
-       rte_trace(c, new, '>', new == net->routes ? "added [best]" : "added");
+       rte_trace(c, new, '>', new_stored == net->routes ? "added [best]" : "added");
       else if (old_ok)
        {
+         rte old_copy = rte_copy(old);
          if (old != old_best)
-           rte_trace(c, old, '>', "removed");
-         else if (rte_is_ok(net->routes))
-           rte_trace(c, old, '>', "removed [replaced]");
+           rte_trace(c, &old_copy, '>', "removed");
+         else if (net->routes && !rte_is_filtered(net->routes))
+           rte_trace(c, &old_copy, '>', "removed [replaced]");
          else
-           rte_trace(c, old, '>', "removed [sole]");
+           rte_trace(c, &old_copy, '>', "removed [sole]");
        }
     }
 
   /* Propagate the route change */
-  rte_announce(table, RA_UNDEF, net, new, old, net->routes, old_best);
+  rte_announce(table, RA_UNDEF, net, new_stored, old, net->routes, old_best);
 
   if (!net->routes &&
       (table->gc_counter++ >= table->config->gc_max_ops) &&
@@ -1250,14 +1194,14 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src)
   if (old_ok && p->rte_remove)
     p->rte_remove(net, old);
   if (new_ok && p->rte_insert)
-    p->rte_insert(net, new);
+    p->rte_insert(net, new_stored);
 
   if (old)
     {
-      if (!new)
+      if (!new_stored)
        hmap_clear(&table->id_map, old->id);
 
-      rte_free_quick(old);
+      rte_free(old);
     }
 }
 
@@ -1276,165 +1220,153 @@ rte_update_unlock(void)
     lp_flush(rte_update_pool);
 }
 
-static void
-rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
+static int NONNULL(1,2) rte_update_in(struct channel *c, rte *new);
+static void NONNULL(1,2) rte_update2(struct channel *c, rte *new);
+
+void NONNULL(1,2)
+rte_update(struct channel *c, rte *new)
+{
+  ASSERT(c->channel_state == CS_UP);
+  ASSERT(new->net);
+  ASSERT(new->src);
+
+  if (new->attrs && !new->attrs->pref)
+  {
+    ASSERT(!new->attrs->cached);
+    new->attrs->pref = c->preference;
+  }
+
+  if (c->in_table && !rte_update_in(c, new))
+    return;
+
+  rte_update2(c, new);
+}
+
+static void NONNULL(1,2)
+rte_update2(struct channel *c, rte *new)
 {
   // struct proto *p = c->proto;
   struct proto_stats *stats = &c->stats;
   const struct filter *filter = c->in_filter;
-  net *nn;
 
-  ASSERT(c->channel_state == CS_UP);
+  _Bool filtered = 0;
+
+  if (new->attrs)
+    stats->imp_updates_received++;
+  else
+    stats->imp_withdraws_received++;
 
   rte_update_lock();
-  if (new)
-    {
-      /* Create a temporary table node */
-      nn = alloca(sizeof(net) + n->length);
-      memset(nn, 0, sizeof(net) + n->length);
-      net_copy(nn->n.addr, n);
 
-      new->net = nn;
-      new->sender = c;
+  if (!net_validate(new->net))
+  {
+    log(L_WARN "Ignoring bogus prefix %N received via %s.%s",
+       new->net, c->proto->name, c->name);
+    goto invalid;
+  }
 
-      stats->imp_updates_received++;
-      if (!rte_validate(new))
-       {
-         rte_trace_in(D_FILTERS, c, new, "invalid");
-         stats->imp_updates_invalid++;
-         goto drop;
-       }
+  /* FIXME: better handling different nettypes */
+  int cl = !net_is_flow(new->net) ?
+    net_classify(new->net): (IADDR_HOST | SCOPE_UNIVERSE);
+  if ((cl < 0) || !(cl & IADDR_HOST) || ((cl & IADDR_SCOPE_MASK) <= SCOPE_LINK))
+  {
+    log(L_WARN "Ignoring bogus route %N received via %s.%s",
+       new->net, c->proto->name, c->name);
+    goto invalid;
+  }
+
+  if (new->attrs)
+    {
+      if (net_type_match(new->net, NB_DEST) == !new->attrs->dest)
+      {
+       log(L_WARN "Ignoring route %N with invalid dest %d received via %s.%s",
+           new->net, new->attrs->dest, c->proto->name, c->name);
+       goto invalid;
+      }
+
+      if ((new->attrs->dest == RTD_UNICAST) && !nexthop_is_sorted(&(new->attrs->nh)))
+      {
+       log(L_WARN "Ignoring unsorted multipath route %N received via %s.%s",
+           new->net, c->proto->name, c->name);
+       goto invalid;
+      }
 
-      if (filter == FILTER_REJECT)
+      if ((filter == FILTER_REJECT) || (filter && (f_run(filter, new, rte_update_pool, 0) > F_ACCEPT)))
        {
          stats->imp_updates_filtered++;
          rte_trace_in(D_FILTERS, c, new, "filtered out");
-
-         if (! c->in_keep_filtered)
-           goto drop;
-
-         /* new is a private copy, i could modify it */
-         new->flags |= REF_FILTERED;
+         filtered = 1;
        }
-      else if (filter)
-       {
-         int fr = f_run(filter, &new, rte_update_pool, 0);
-         if (fr > F_ACCEPT)
-         {
-           stats->imp_updates_filtered++;
-           rte_trace_in(D_FILTERS, c, new, "filtered out");
-
-           if (! c->in_keep_filtered)
-             goto drop;
+    }
 
-           new->flags |= REF_FILTERED;
-         }
-       }
-      if (!rta_is_cached(new->attrs)) /* Need to copy attributes */
-       new->attrs = rta_lookup(new->attrs);
-      new->flags |= REF_COW;
+  /* Find a table record */
+  net *nn;
 
-      /* Use the actual struct network, not the dummy one */
-      nn = net_get(c->table, n);
-      new->net = nn;
-    }
+  if (new->attrs && (!filtered || c->in_keep_filtered))
+    /* This is an update and it shall pass to the table */
+    nn = net_get(c->table, new->net);
   else
+  {
+    /* This is a withdraw and it need not be in the table */
+    nn = net_find(c->table, new->net);
+
+    if (!nn) /* No previous table record found */
     {
-      stats->imp_withdraws_received++;
+      if (!new->attrs) /* Regular withdraw */
+       stats->imp_withdraws_ignored++;
 
-      if (!(nn = net_find(c->table, n)) || !src)
-       {
-         stats->imp_withdraws_ignored++;
-         rte_update_unlock();
-         return;
-       }
+      rte_update_unlock();
+      return;
     }
 
- recalc:
-  /* And recalculate the best route */
-  rte_recalculate(c, nn, new, src);
+    /* Drop the attributes as they aren't for anything now. */
+    new->attrs = NULL;
+  }
 
+  /* And recalculate the best route */
+  rte_recalculate(c, nn, new, filtered);
   rte_update_unlock();
   return;
 
- drop:
-  rte_free(new);
-  new = NULL;
-  if (nn = net_find(c->table, n))
-    goto recalc;
-
-  rte_update_unlock();
-}
-
-static int rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *src);
-
-void
-rte_withdraw(struct channel *c, const net_addr *n, struct rte_src *src)
-{
-  ASSERT(src);
-  if (!c->in_table || rte_update_in(c, n, NULL, src))
-    rte_update2(c, n, NULL, src);
-}
-
-void
-rte_update(struct channel *c, const net_addr *n, struct rte *new)
-{
-  ASSERT(new);
-  ASSERT(new->attrs);
-  ASSERT(new->src);
-
-  if (!new->attrs->pref)
+ invalid:
+  if (new->attrs)
   {
-    ASSERT(!new->attrs->cached);
-    new->attrs->pref = c->preference;
+    stats->imp_updates_invalid++;
+    rte_trace_in(D_FILTERS, c, new, "invalid");
   }
+  else
+    stats->imp_withdraws_invalid++;
 
-  rte *e = sl_alloc(rte_slab);
-  *e = *new;
-
-  rt_lock_source(e->src);
-
-  if (!c->in_table || rte_update_in(c, n, e, e->src))
-    rte_update2(c, n, e, e->src);
+  rte_update_unlock();
+  return;
 }
 
 /* Independent call to rte_announce(), used from next hop
    recalculation, outside of rte_update(). new must be non-NULL */
 static inline void
-rte_announce_i(rtable *tab, uint type, net *net, rte *new, rte *old,
-              rte *new_best, rte *old_best)
+rte_announce_i(rtable *tab, uint type, net *net,
+    struct rte_storage *new, struct rte_storage *old,
+    struct rte_storage *new_best, struct rte_storage *old_best)
 {
   rte_update_lock();
   rte_announce(tab, type, net, new, old, new_best, old_best);
   rte_update_unlock();
 }
 
-static inline void
-rte_discard(rte *old)  /* Non-filtered route deletion, used during garbage collection */
-{
-  rte_update_lock();
-  rte_recalculate(old->sender, old->net, NULL, old->src);
-  rte_update_unlock();
-}
-
 /* Modify existing route by protocol hook, used for long-lived graceful restart */
 static inline void
-rte_modify(rte *old)
+rte_modify(struct rte_storage *old)
 {
   rte_update_lock();
 
-  rte *new = old->sender->proto->rte_modify(old, rte_update_pool);
-  if (new != old)
-  {
-    if (new)
-    {
-      if (!rta_is_cached(new->attrs))
-       new->attrs = rta_lookup(new->attrs);
-      new->flags = (old->flags & ~REF_MODIFY) | REF_COW;
-    }
+  rte new = {
+    .net = old->net->n.addr,
+    .src = old->src,
+    .attrs = old->sender->proto->rte_modify(old, rte_update_pool),
+  };
 
-    rte_recalculate(old->sender, old->net, new, old->src);
-  }
+  if (new.attrs != old->attrs)
+    rte_recalculate(old->sender, old->net, &new, old->src);
 
   rte_update_unlock();
 }
@@ -1458,8 +1390,7 @@ rt_refresh_begin(rtable *t, struct channel *c)
 {
   FIB_WALK(&t->fib, net, n)
     {
-      rte *e;
-      for (e = n->routes; e; e = e->next)
+      for (struct rte_storage *e = n->routes; e; e = e->next)
        if (e->sender == c)
          e->flags |= REF_STALE;
     }
@@ -1481,8 +1412,7 @@ rt_refresh_end(rtable *t, struct channel *c)
 
   FIB_WALK(&t->fib, net, n)
     {
-      rte *e;
-      for (e = n->routes; e; e = e->next)
+      for (struct rte_storage *e = n->routes; e; e = e->next)
        if ((e->sender == c) && (e->flags & REF_STALE))
          {
            e->flags |= REF_DISCARD;
@@ -1502,8 +1432,7 @@ rt_modify_stale(rtable *t, struct channel *c)
 
   FIB_WALK(&t->fib, net, n)
     {
-      rte *e;
-      for (e = n->routes; e; e = e->next)
+      for (struct rte_storage *e = n->routes; e; e = e->next)
        if ((e->sender == c) && (e->flags & REF_STALE) && !(e->flags & REF_FILTERED))
          {
            e->flags |= REF_MODIFY;
@@ -1523,10 +1452,11 @@ rt_modify_stale(rtable *t, struct channel *c)
  * This functions dumps contents of a &rte to debug output.
  */
 void
-rte_dump(rte *e)
+rte_dump(struct rte_storage *e)
 {
   net *n = e->net;
   debug("%-1N ", n->n.addr);
+  debug("p=%s src=(%u/%u) ", e->src->proto->name, e->src->private_id, e->src->global_id);
   debug("PF=%02x ", e->pflags);
   rta_dump(e->attrs);
   debug("\n");
@@ -1547,8 +1477,7 @@ rt_dump(rtable *t)
 #endif
   FIB_WALK(&t->fib, net, n)
     {
-      rte *e;
-      for(e=n->routes; e; e=e->next)
+      for(struct rte_storage *e=n->routes; e; e=e->next)
        rte_dump(e);
     }
   FIB_WALK_END;
@@ -1725,7 +1654,7 @@ rt_init(void)
   rta_init();
   rt_table_pool = rp_new(&root_pool, "Routing tables");
   rte_update_pool = lp_new_default(rt_table_pool);
-  rte_slab = sl_new(rt_table_pool, sizeof(rte));
+  rte_slab = sl_new(rt_table_pool, sizeof(struct rte_storage));
   init_list(&routing_tables);
 }
 
@@ -1775,10 +1704,8 @@ rt_prune_table(rtable *tab)
 again:
   FIB_ITERATE_START(&tab->fib, fit, net, n)
     {
-      rte *e;
-
     rescan:
-      for (e=n->routes; e; e=e->next)
+      for (struct rte_storage *e=n->routes; e; e=e->next)
       {
        if (e->sender->flush_active || (e->flags & REF_DISCARD))
          {
@@ -1789,7 +1716,12 @@ again:
                return;
              }
 
-           rte_discard(e);
+           /* Discard the route */
+           rte_update_lock();
+           rte ew = { .net = e->net->n.addr, .src = e->src };
+           rte_recalculate(e->sender, e->net, &ew, 0);
+           rte_update_unlock();
+
            limit--;
 
            goto rescan;
@@ -1968,8 +1900,8 @@ no_nexthop:
     }
 }
 
-static inline rte *
-rt_next_hop_update_rte(rtable *tab UNUSED, rte *old)
+static inline struct rte_storage *
+rt_next_hop_update_rte(struct rte_storage *old)
 {
   rta *a = alloca(RTA_MAX_SIZE);
   memcpy(a, old->attrs, rta_size(old->attrs));
@@ -1980,32 +1912,37 @@ rt_next_hop_update_rte(rtable *tab UNUSED, rte *old)
   rta_apply_hostentry(a, old->attrs->hostentry, &mls);
   a->cached = 0;
 
-  rte *e = sl_alloc(rte_slab);
-  memcpy(e, old, sizeof(rte));
-  e->attrs = rta_lookup(a);
-  rt_lock_source(e->src);
+  rte e = {
+    .attrs = a,
+    .net = old->net->n.addr,
+    .src = old->src,
+  };
 
-  return e;
+  rte_trace_in(D_ROUTES, old->sender, &e, "updated");
+
+  struct rte_storage *new = rte_store(&e, old->net);
+  rte_copy_metadata(new, old);
+  return new;
 }
 
 static inline int
 rt_next_hop_update_net(rtable *tab, net *n)
 {
-  rte **k, *e, *new, *old_best, **new_best;
+  struct rte_storage *new, **new_best;
   int count = 0;
   int free_old_best = 0;
 
-  old_best = n->routes;
+  struct rte_storage *old_best = n->routes;
   if (!old_best)
     return 0;
 
-  for (k = &n->routes; e = *k; k = &e->next)
+  for (struct rte_storage **k = &n->routes, *e; e = *k; k = &e->next)
     if (rta_next_hop_outdated(e->attrs))
       {
-       new = rt_next_hop_update_rte(tab, e);
+       new = rt_next_hop_update_rte(e);
+       new->next = e->next;
        *k = new;
 
-       rte_trace_in(D_ROUTES, new->sender, new, "updated");
        rte_announce_i(tab, RA_ANY, n, new, e, NULL, NULL);
 
        /* Call a pre-comparison hook */
@@ -2014,7 +1951,7 @@ rt_next_hop_update_net(rtable *tab, net *n)
          e->src->proto->rte_recalculate(tab, n, new, e, NULL);
 
        if (e != old_best)
-         rte_free_quick(e);
+         rte_free(e);
        else /* Freeing of the old best rte is postponed */
          free_old_best = 1;
 
@@ -2027,7 +1964,7 @@ rt_next_hop_update_net(rtable *tab, net *n)
 
   /* Find the new best route */
   new_best = NULL;
-  for (k = &n->routes; e = *k; k = &e->next)
+  for (struct rte_storage **k = &n->routes, *e; e = *k; k = &e->next)
     {
       if (!new_best || rte_better(e, *new_best))
        new_best = k;
@@ -2044,13 +1981,16 @@ rt_next_hop_update_net(rtable *tab, net *n)
 
   /* Announce the new best route */
   if (new != old_best)
-    rte_trace_in(D_ROUTES, new->sender, new, "updated [best]");
+  {
+    rte nloc = rte_copy(new);
+    rte_trace_in(D_ROUTES, new->sender, &nloc, "updated [best]");
+  }
 
   /* Propagate changes */
   rte_announce_i(tab, RA_UNDEF, n, NULL, NULL, n->routes, old_best);
 
   if (free_old_best)
-    rte_free_quick(old_best);
+    rte_free(old_best);
 
   return count;
 }
@@ -2329,22 +2269,22 @@ rt_feed_channel_abort(struct channel *c)
  */
 
 static int
-rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
+rte_update_in(struct channel *c, rte *new)
 {
   struct rtable *tab = c->in_table;
-  rte *old, **pos;
+  struct rte_storage *old, **pos;
   net *net;
 
-  if (new)
+  if (new->attrs)
   {
-    net = net_get(tab, n);
+    net = net_get(tab, new->net);
 
     if (!rta_is_cached(new->attrs))
       new->attrs = rta_lookup(new->attrs);
   }
   else
   {
-    net = net_find(tab, n);
+    net = net_find(tab, new->net);
 
     if (!net)
       goto drop_withdraw;
@@ -2352,9 +2292,9 @@ rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *sr
 
   /* Find the old rte */
   for (pos = &net->routes; old = *pos; pos = &old->next)
-    if (old->src == src)
+    if (old->src == new->src)
     {
-      if (new && rte_same(old, new))
+      if (new->attrs && rte_same(old, new, 0))
       {
        /* Refresh the old rte, continue with update to main rtable */
        if (old->flags & (REF_STALE | REF_DISCARD | REF_MODIFY))
@@ -2372,13 +2312,13 @@ rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *sr
 
       /* Remove the old rte */
       *pos = old->next;
-      rte_free_quick(old);
+      rte_free(old);
       tab->rt_count--;
 
       break;
     }
 
-  if (!new)
+  if (!new->attrs)
   {
     if (!old)
       goto drop_withdraw;
@@ -2394,18 +2334,13 @@ rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *sr
 
     if (l->state == PLS_BLOCKED)
     {
-      /* Required by rte_trace_in() */
-      new->net = net;
-
       rte_trace_in(D_FILTERS, c, new, "ignored [limit]");
       goto drop_update;
     }
   }
 
   /* Insert the new rte */
-  rte *e = rte_do_cow(new);
-  e->flags |= REF_COW;
-  e->net = net;
+  struct rte_storage *e = rte_store(new, net);
   e->sender = c;
   e->lastmod = current_time();
   e->next = *pos;
@@ -2416,7 +2351,6 @@ rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *sr
 drop_update:
   c->stats.imp_updates_received++;
   c->stats.imp_updates_ignored++;
-  rte_free(new);
   return 0;
 
 drop_withdraw:
@@ -2441,7 +2375,7 @@ rt_reload_channel(struct channel *c)
   }
 
   do {
-    for (rte *e = c->reload_next_rte; e; e = e->next)
+    for (struct rte_storage *e = c->reload_next_rte; e; e = e->next)
     {
       if (max_feed-- <= 0)
       {
@@ -2450,7 +2384,8 @@ rt_reload_channel(struct channel *c)
        return 0;
       }
 
-      rte_update2(c, e->net->n.addr, rte_do_cow(e), e->src);
+      rte eloc = rte_copy(e);
+      rte_update2(c, &eloc);
     }
 
     c->reload_next_rte = NULL;
@@ -2488,13 +2423,13 @@ rt_prune_sync(rtable *t, int all)
 {
   FIB_WALK(&t->fib, net, n)
   {
-    rte *e, **ee = &n->routes;
+    struct rte_storage *e, **ee = &n->routes;
     while (e = *ee)
     {
       if (all || (e->flags & (REF_STALE | REF_DISCARD)))
       {
        *ee = e->next;
-       rte_free_quick(e);
+       rte_free(e);
        t->rt_count--;
       }
       else
@@ -2510,32 +2445,32 @@ rt_prune_sync(rtable *t, int all)
  */
 
 int
-rte_update_out(struct channel *c, const net_addr *n, struct rte_src *src, rte *new, rte **old_exported, int refeed)
+rte_update_out(struct channel *c, rte *new, rte *old, struct rte_storage **old_stored, int refeed)
 {
   struct rtable *tab = c->out_table;
-  rte *old, **pos;
+  struct rte_storage **pos;
   net *net;
 
-  if (new)
+  if (new->attrs)
   {
-    net = net_get(tab, n);
+    net = net_get(tab, new->net);
 
     if (!rta_is_cached(new->attrs))
       new->attrs = rta_lookup(new->attrs);
   }
   else
   {
-    net = net_find(tab, n);
+    net = net_find(tab, old->net);
 
     if (!net)
       goto drop_withdraw;
   }
 
   /* Find the old rte */
-  for (pos = &net->routes; old = *pos; pos = &old->next)
-    if ((c->ra_mode != RA_ANY) || (old->src == src))
+  for (pos = &net->routes; *pos; pos = &(*pos)->next)
+    if ((c->ra_mode != RA_ANY) || ((*pos)->src == old->src))
     {
-      if (new && rte_same(old, new))
+      if (new && rte_same(*pos, new, 0))
       {
        /* REF_STALE / REF_DISCARD not used in export table */
        /*
@@ -2549,26 +2484,27 @@ rte_update_out(struct channel *c, const net_addr *n, struct rte_src *src, rte *n
        goto drop_update;
       }
 
-      /* Remove the old rte */
-      *pos = old->next;
-      *old_exported = old;
+      /* Keep the old rte */
+      *old_stored = *pos;
+      *old = rte_copy(*pos);
+
+      /* Remove the old rte from the list */
+      *pos = (*pos)->next;
       tab->rt_count--;
 
       break;
     }
 
-  if (!new)
+  if (!new->attrs)
   {
-    if (!old)
+    if (!*old_stored)
       goto drop_withdraw;
 
     return 1;
   }
 
   /* Insert the new rte */
-  rte *e = rte_do_cow(new);
-  e->flags |= REF_COW;
-  e->net = net;
+  struct rte_storage *e = rte_store(new, net);
   e->sender = c;
   e->lastmod = current_time();
   e->next = *pos;
@@ -2746,29 +2682,27 @@ if_local_addr(ip_addr a, struct iface *i)
 }
 
 u32
-rt_get_igp_metric(rte *rt)
+rt_get_igp_metric(rta *a)
 {
-  eattr *ea = ea_find(rt->attrs->eattrs, EA_GEN_IGP_METRIC);
+  eattr *ea;
 
-  if (ea)
+  if (ea = ea_find(a->eattrs, EA_GEN_IGP_METRIC))
     return ea->u.data;
 
-  rta *a = rt->attrs;
-
   switch (a->source)
   {
 #ifdef CONFIG_OSPF
     case RTS_OSPF:
     case RTS_OSPF_IA:
     case RTS_OSPF_EXT1:
-      if (ea = ea_find(rt->attrs->eattrs, EA_OSPF_METRIC1))
+      if (ea = ea_find(a->eattrs, EA_OSPF_METRIC1))
        return ea->u.data;
       break;
 #endif
 
 #ifdef CONFIG_RIP
     case RTS_RIP:
-      if (ea = ea_find(rt->attrs->eattrs, EA_RIP_METRIC))
+      if (ea = ea_find(a->eattrs, EA_RIP_METRIC))
        return ea->u.data;
       break;
 #endif
@@ -2776,14 +2710,14 @@ rt_get_igp_metric(rte *rt)
 #ifdef CONFIG_BGP
     case RTS_BGP:
       {
-       u64 metric = bgp_total_aigp_metric(rt);
+       u64 metric = bgp_total_aigp_metric(a);
        return (u32) MIN(metric, (u64) IGP_METRIC_UNKNOWN);
       }
 #endif
 
 #ifdef CONFIG_BABEL
     case RTS_BABEL:
-      if (ea = ea_find(rt->attrs->eattrs, EA_BABEL_METRIC))
+      if (ea = ea_find(a->eattrs, EA_BABEL_METRIC))
        return ea->u.data;
       break;
 #endif
@@ -2810,11 +2744,10 @@ rt_update_hostentry(rtable *tab, struct hostentry *he)
 
   net_addr he_addr;
   net_fill_ip_host(&he_addr, he->addr);
-  net *n = net_route(tab, &he_addr);
+  net *n = net_route(tab, &he_addr); /* This always returns a valid route or NULL */
   if (n)
     {
-      rte *e = n->routes;
-      rta *a = e->attrs;
+      rta *a = n->routes->attrs;
       pxlen = n->n.addr->pxlen;
 
       if (a->hostentry)
@@ -2845,7 +2778,7 @@ rt_update_hostentry(rtable *tab, struct hostentry *he)
       he->src = rta_clone(a);
       he->dest = a->dest;
       he->nexthop_linkable = !direct;
-      he->igp_metric = rt_get_igp_metric(e);
+      he->igp_metric = rt_get_igp_metric(a);
     }
 
 done:
index 8b0859b40ba06767d1c5677cc7bdbd3af73bd4d7..95e9e1ee22ad2568d39bf193f1036aaca0b996a1 100644 (file)
@@ -671,12 +671,13 @@ babel_announce_rte(struct babel_proto *p, struct babel_entry *e)
       a0.nh.flags = RNF_ONLINK;
 
     rte e0 = {
+      .net = e->n.addr,
       .src = p->p.main_source,
       .attrs = &a0,
     };
 
     e->unreachable = 0;
-    rte_update(c, e->n.addr, &e0);
+    rte_update(c, &e0);
   }
   else if (e->valid && (e->router_id != p->router_id))
   {
@@ -689,12 +690,13 @@ babel_announce_rte(struct babel_proto *p, struct babel_entry *e)
     };
 
     rte e0 = {
+      .net = e->n.addr,
       .src = p->p.main_source,
       .attrs = &a0,
     };
 
     e->unreachable = 1;
-    rte_update(c, e->n.addr, &e0);
+    rte_update(c, &e0);
   }
   else
   {
@@ -1873,12 +1875,12 @@ babel_dump(struct proto *P)
 }
 
 static void
-babel_get_route_info(rte *rte, byte *buf)
+babel_get_route_info(struct rte *e, struct rte_storage *er UNUSED, byte *buf)
 {
   u64 rid;
-  memcpy(&rid, ea_find(rte->attrs->eattrs, EA_BABEL_ROUTER_ID)->u.ptr->data, sizeof(u64));
-  buf += bsprintf(buf, " (%d/%d) [%lR]", rte->attrs->pref,
-      ea_find(rte->attrs->eattrs, EA_BABEL_METRIC)->u.data, rid);
+  memcpy(&rid, ea_find(e->attrs->eattrs, EA_BABEL_ROUTER_ID)->u.ptr->data, sizeof(u64));
+  buf += bsprintf(buf, " (%d/%d) [%lR]", e->attrs->pref,
+      ea_find(e->attrs->eattrs, EA_BABEL_METRIC)->u.data, rid);
 }
 
 static int
@@ -2100,10 +2102,10 @@ babel_kick_timer(struct babel_proto *p)
 
 
 static int
-babel_preexport(struct proto *P, struct rte *new)
+babel_preexport(struct channel *c, struct rte *new)
 {
   /* Reject our own unreachable routes */
-  if ((new->attrs->dest == RTD_UNREACHABLE) && (new->src->proto == P))
+  if ((new->attrs->dest == RTD_UNREACHABLE) && (new->src->proto == c->proto))
     return -1;
 
   return 0;
@@ -2119,15 +2121,15 @@ babel_rt_notify(struct channel *c, struct rte_export *export)
   struct babel_proto *p = (void *) c->proto;
   struct babel_entry *e;
 
-  if (export->new)
+  if (export->new.attrs)
   {
     /* Update */
-    uint rt_metric = ea_get_int(export->new->attrs->eattrs, EA_BABEL_METRIC, 0);
-    uint rt_seqno = ea_get_int(export->new->attrs->eattrs, EA_BABEL_SEQNO, p->update_seqno);
+    uint rt_metric = ea_get_int(export->new.attrs->eattrs, EA_BABEL_METRIC, 0);
+    uint rt_seqno = ea_get_int(export->new.attrs->eattrs, EA_BABEL_SEQNO, p->update_seqno);
     u64 rt_router_id;
 
     eattr *ea;
-    if (ea = ea_find(export->new->attrs->eattrs, EA_BABEL_ROUTER_ID))
+    if (ea = ea_find(export->new.attrs->eattrs, EA_BABEL_ROUTER_ID))
       memcpy(&rt_router_id, ea->u.ptr->data, sizeof(u64));
     else
       rt_router_id = p->router_id; 
@@ -2135,11 +2137,11 @@ babel_rt_notify(struct channel *c, struct rte_export *export)
     if (rt_metric > BABEL_INFINITY)
     {
       log(L_WARN "%s: Invalid babel_metric value %u for route %N",
-         p->p.name, rt_metric, export->net);
+         p->p.name, rt_metric, export->new.net);
       rt_metric = BABEL_INFINITY;
     }
 
-    e = babel_get_entry(p, export->net);
+    e = babel_get_entry(p, export->new.net);
 
     /* Activate triggered updates */
     if ((e->valid != BABEL_ENTRY_VALID) ||
@@ -2157,7 +2159,7 @@ babel_rt_notify(struct channel *c, struct rte_export *export)
   else
   {
     /* Withdraw */
-    e = babel_find_entry(p, export->net);
+    e = babel_find_entry(p, export->old.net);
 
     if (!e || e->valid != BABEL_ENTRY_VALID)
       return;
@@ -2171,7 +2173,7 @@ babel_rt_notify(struct channel *c, struct rte_export *export)
 }
 
 static int
-babel_rte_better(struct rte *new, struct rte *old)
+babel_rte_better(struct rte_storage *new, struct rte_storage *old)
 {
   uint new_metric = ea_find(new->attrs->eattrs, EA_BABEL_SEQNO)->u.data;
   uint old_metric = ea_find(old->attrs->eattrs, EA_BABEL_SEQNO)->u.data;
index 11a64c1bb4070f61f1c7d016b90e4c3557dd91e1..be16c81e25c5636eeeb1d3c52f1fec0c5f215ee4 100644 (file)
@@ -333,26 +333,26 @@ bgp_aigp_set_metric(struct linpool *pool, const struct adata *ad, u64 metric)
 }
 
 int
-bgp_total_aigp_metric_(rte *e, u64 *metric, const struct adata **ad)
+bgp_total_aigp_metric_(struct rta *a, u64 *metric, const struct adata **ad)
 {
-  eattr *a = ea_find(e->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_AIGP));
-  if (!a)
+  eattr *ea = ea_find(a->eattrs, EA_CODE(PROTOCOL_BGP, BA_AIGP));
+  if (!ea)
     return 0;
 
-  const byte *b = bgp_aigp_get_tlv(a->u.ptr, BGP_AIGP_METRIC);
+  const byte *b = bgp_aigp_get_tlv(ea->u.ptr, BGP_AIGP_METRIC);
   if (!b)
     return 0;
 
   u64 aigp = get_u64(b + 3);
-  u64 step = e->attrs->igp_metric;
+  u64 step = a->igp_metric;
 
-  if (!rte_resolvable(e) || (step >= IGP_METRIC_UNKNOWN))
+  if (!rta_resolvable(a) || (step >= IGP_METRIC_UNKNOWN))
     step = BGP_AIGP_MAX;
 
   if (!step)
     step = 1;
 
-  *ad = a->u.ptr;
+  *ad = ea->u.ptr;
   *metric = aigp + step;
   if (*metric < aigp)
     *metric = BGP_AIGP_MAX;
@@ -361,12 +361,12 @@ bgp_total_aigp_metric_(rte *e, u64 *metric, const struct adata **ad)
 }
 
 static inline int
-bgp_init_aigp_metric(rte *e, u64 *metric, const struct adata **ad)
+bgp_init_aigp_metric(rta *a, u64 *metric, const struct adata **ad)
 {
-  if (e->attrs->source == RTS_BGP)
+  if (a->source == RTS_BGP)
     return 0;
 
-  *metric = rt_get_igp_metric(e);
+  *metric = rt_get_igp_metric(a);
   *ad = NULL;
   return *metric < IGP_METRIC_UNKNOWN;
 }
@@ -896,7 +896,7 @@ bgp_decode_large_community(struct bgp_parse_state *s, uint code UNUSED, uint fla
 static void
 bgp_export_mpls_label_stack(struct bgp_export_state *s, eattr *a)
 {
-  net_addr *n = s->route->net->n.addr;
+  const net_addr *n = s->route->net;
   u32 *labels = (u32 *) a->u.ptr->data;
   uint lnum = a->u.ptr->length / 4;
 
@@ -1617,7 +1617,7 @@ bgp_free_prefix_table(struct bgp_channel *c)
 }
 
 static struct bgp_prefix *
-bgp_get_prefix(struct bgp_channel *c, net_addr *net, u32 path_id)
+bgp_get_prefix(struct bgp_channel *c, const net_addr *net, u32 path_id)
 {
   u32 hash = net_hash(net) ^ u32_hash(path_id);
   struct bgp_prefix *px = HASH_FIND(c->prefix_hash, PXH, net, path_id, hash);
@@ -1661,10 +1661,10 @@ bgp_free_prefix(struct bgp_channel *c, struct bgp_prefix *px)
  */
 
 int
-bgp_preexport(struct proto *P, rte *e)
+bgp_preexport(struct channel *c, rte *e)
 {
   struct proto *SRC = e->src->proto;
-  struct bgp_proto *p = (struct bgp_proto *) P;
+  struct bgp_proto *p = (struct bgp_proto *) (c->proto);
   struct bgp_proto *src = (SRC->proto == &proto_bgp) ? (struct bgp_proto *) SRC : NULL;
 
   /* Reject our routes */
@@ -1689,11 +1689,11 @@ bgp_preexport(struct proto *P, rte *e)
   }
 
   /* Handle well-known communities, RFC 1997 */
-  struct eattr *c;
+  struct eattr *com;
   if (p->cf->interpret_communities &&
-      (c = ea_find(e->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_COMMUNITY))))
+      (com = ea_find(e->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_COMMUNITY))))
   {
-    const struct adata *d = c->u.ptr;
+    const struct adata *d = com->u.ptr;
 
     /* Do not export anywhere */
     if (int_set_contains(d, BGP_COMM_NO_ADVERTISE))
@@ -1773,8 +1773,8 @@ bgp_update_attrs(struct bgp_proto *p, struct bgp_channel *c, rte *e, ea_list *at
   /* AIGP attribute - accumulate local metric or originate new one */
   u64 metric;
   if (s.local_next_hop &&
-      (bgp_total_aigp_metric_(e, &metric, &ad) ||
-       (c->cf->aigp_originate && bgp_init_aigp_metric(e, &metric, &ad))))
+      (bgp_total_aigp_metric_(e->attrs, &metric, &ad) ||
+       (c->cf->aigp_originate && bgp_init_aigp_metric(e->attrs, &metric, &ad))))
   {
     ad = bgp_aigp_set_metric(pool, ad, metric);
     bgp_set_attr_ptr(&attrs, pool, BA_AIGP, 0, ad);
@@ -1840,23 +1840,28 @@ bgp_rt_notify(struct channel *C, struct rte_export *e)
   struct bgp_prefix *px;
   u32 path;
 
-  if (e->new)
+  const net_addr *n;
+
+  if (e->new.attrs)
   {
-    struct ea_list *attrs = bgp_update_attrs(p, c, e->new, e->new->attrs->eattrs, bgp_linpool2);
+    struct ea_list *attrs = bgp_update_attrs(p, c, &(e->new), e->new.attrs->eattrs, bgp_linpool2);
 
     /* If attributes are invalid, we fail back to withdraw */
     buck = attrs ? bgp_get_bucket(c, attrs) : bgp_get_withdraw_bucket(c);
-    path = e->new_src->global_id;
+    path = e->new.src->global_id;
 
     lp_flush(bgp_linpool2);
+
+    n = e->new.net;
   }
   else
   {
     buck = bgp_get_withdraw_bucket(c);
-    path = e->old_src->global_id;
+    path = e->old.src->global_id;
+    n = e->old.net;
   }
 
-  px = bgp_get_prefix(c, e->net, c->add_path_tx ? path : 0);
+  px = bgp_get_prefix(c, n, c->add_path_tx ? path : 0);
   add_tail(&buck->prefixes, &px->buck_node);
 
   bgp_schedule_packet(p->conn, c, PKT_UPDATE);
@@ -1864,7 +1869,7 @@ bgp_rt_notify(struct channel *C, struct rte_export *e)
 
 
 static inline u32
-bgp_get_neighbor(rte *r)
+bgp_get_neighbor(struct rte_storage *r)
 {
   eattr *e = ea_find(r->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_AS_PATH));
   u32 as;
@@ -1878,7 +1883,7 @@ bgp_get_neighbor(rte *r)
 }
 
 static inline int
-rte_stale(rte *r)
+rte_stale(struct rte_storage *r)
 {
   if (r->pflags & BGP_REF_STALE)
     return 1;
@@ -1901,7 +1906,7 @@ rte_stale(rte *r)
 }
 
 int
-bgp_rte_better(rte *new, rte *old)
+bgp_rte_better(struct rte_storage *new, struct rte_storage *old)
 {
   struct bgp_proto *new_bgp = (struct bgp_proto *) new->src->proto;
   struct bgp_proto *old_bgp = (struct bgp_proto *) old->src->proto;
@@ -1917,8 +1922,8 @@ bgp_rte_better(rte *new, rte *old)
     return 1;
 
   /* RFC 4271 9.1.2.1. Route resolvability test */
-  n = rte_resolvable(new);
-  o = rte_resolvable(old);
+  n = rta_resolvable(new->attrs);
+  o = rta_resolvable(old->attrs);
   if (n > o)
     return 1;
   if (n < o)
@@ -1943,8 +1948,8 @@ bgp_rte_better(rte *new, rte *old)
     return 0;
 
   /* RFC 7311 4.1 - Apply AIGP metric */
-  u64 n2 = bgp_total_aigp_metric(new);
-  u64 o2 = bgp_total_aigp_metric(old);
+  u64 n2 = bgp_total_aigp_metric(new->attrs);
+  u64 o2 = bgp_total_aigp_metric(old->attrs);
   if (n2 < o2)
     return 1;
   if (n2 > o2)
@@ -2046,7 +2051,7 @@ bgp_rte_better(rte *new, rte *old)
 
 
 int
-bgp_rte_mergable(rte *pri, rte *sec)
+bgp_rte_mergable(struct rte_storage *pri, struct rte_storage *sec)
 {
   struct bgp_proto *pri_bgp = (struct bgp_proto *) pri->src->proto;
   struct bgp_proto *sec_bgp = (struct bgp_proto *) sec->src->proto;
@@ -2059,7 +2064,7 @@ bgp_rte_mergable(rte *pri, rte *sec)
     return 0;
 
   /* RFC 4271 9.1.2.1. Route resolvability test */
-  if (rte_resolvable(pri) != rte_resolvable(sec))
+  if (rta_resolvable(pri->attrs) != rta_resolvable(sec->attrs))
     return 0;
 
   /* Start with local preferences */
@@ -2122,23 +2127,23 @@ bgp_rte_mergable(rte *pri, rte *sec)
 
 
 static inline int
-same_group(rte *r, u32 lpref, u32 lasn)
+same_group(struct rte_storage *r, u32 lpref, u32 lasn)
 {
   return (r->attrs->pref == lpref) && (bgp_get_neighbor(r) == lasn);
 }
 
 static inline int
-use_deterministic_med(rte *r)
+use_deterministic_med(struct rte_storage *r)
 {
   struct proto *P = r->src->proto;
   return (P->proto == &proto_bgp) && ((struct bgp_proto *) P)->cf->deterministic_med;
 }
 
 int
-bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best)
+bgp_rte_recalculate(rtable *table, net *net, struct rte_storage *new, struct rte_storage *old, struct rte_storage *old_best)
 {
-  rte *r, *s;
-  rte *key = new ? new : old;
+  struct rte_storage *r, *s;
+  struct rte_storage *key = new ? new : old;
   u32 lpref = key->attrs->pref;
   u32 lasn = bgp_get_neighbor(key);
   int old_suppressed = old ? !!(old->pflags & BGP_REF_SUPPRESSED) : 0;
@@ -2261,25 +2266,31 @@ bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best)
     return !old_suppressed;
 }
 
-struct rte *
-bgp_rte_modify_stale(struct rte *r, struct linpool *pool)
+struct rta *
+bgp_rte_modify_stale(struct rte_storage *r, struct linpool *pool)
 {
-  eattr *a = ea_find(r->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_COMMUNITY));
-  const struct adata *ad = a ? a->u.ptr : NULL;
-  uint flags = a ? a->flags : BAF_PARTIAL;
+  eattr *ea = ea_find(r->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_COMMUNITY));
+  const struct adata *ad = ea ? ea->u.ptr : NULL;
+  uint flags = ea ? ea->flags : BAF_PARTIAL;
 
   if (ad && int_set_contains(ad, BGP_COMM_NO_LLGR))
     return NULL;
 
   if (ad && int_set_contains(ad, BGP_COMM_LLGR_STALE))
-    return r;
+    return r->attrs;
+
+  _Thread_local static struct {
+    rta a;
+    u32 labels[MPLS_MAX_LABEL_STACK];
+  } aloc;
+  
+  struct rta *a = &(aloc.a);
+  memcpy(a, r->attrs, rta_size(r->attrs));
 
-  r = rte_cow_rta(r, pool);
-  bgp_set_attr_ptr(&(r->attrs->eattrs), pool, BA_COMMUNITY, flags,
+  bgp_set_attr_ptr(&(a->eattrs), pool, BA_COMMUNITY, flags,
                   int_set_add(pool, ad, BGP_COMM_LLGR_STALE));
-  r->pflags |= BGP_REF_STALE;
 
-  return r;
+  return a;
 }
 
 
@@ -2356,7 +2367,7 @@ bgp_get_attr(const eattr *a, byte *buf, int buflen)
 }
 
 void
-bgp_get_route_info(rte *e, byte *buf)
+bgp_get_route_info(struct rte *e, struct rte_storage *er, byte *buf)
 {
   eattr *p = ea_find(e->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_AS_PATH));
   eattr *o = ea_find(e->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_ORIGIN));
@@ -2364,20 +2375,20 @@ bgp_get_route_info(rte *e, byte *buf)
 
   buf += bsprintf(buf, " (%d", e->attrs->pref);
 
-  if (e->pflags & BGP_REF_SUPPRESSED)
+  if (er->pflags & BGP_REF_SUPPRESSED)
     buf += bsprintf(buf, "-");
 
-  if (rte_stale(e))
+  if (rte_stale(er))
     buf += bsprintf(buf, "s");
 
-  u64 metric = bgp_total_aigp_metric(e);
+  u64 metric = bgp_total_aigp_metric(e->attrs);
   if (metric < BGP_AIGP_MAX)
   {
     buf += bsprintf(buf, "/%lu", metric);
   }
   else if (e->attrs->igp_metric)
   {
-    if (!rte_resolvable(e))
+    if (!rta_resolvable(e->attrs))
       buf += bsprintf(buf, "/-");
     else if (e->attrs->igp_metric >= IGP_METRIC_UNKNOWN)
       buf += bsprintf(buf, "/?");
index 70faf1de0b2fdf99bd1a6101194b623e1760eee0..520c55f691a9d04f3f37104e6106682a12783b42 100644 (file)
@@ -517,9 +517,9 @@ struct rte_source *bgp_find_source(struct bgp_proto *p, u32 path_id);
 struct rte_source *bgp_get_source(struct bgp_proto *p, u32 path_id);
 
 static inline int
-rte_resolvable(rte *rt)
+rta_resolvable(rta *a)
 {
-  return rt->attrs->dest == RTD_UNICAST;
+  return a->dest == RTD_UNICAST;
 }
 
 
@@ -582,26 +582,26 @@ void bgp_init_prefix_table(struct bgp_channel *c);
 void bgp_free_prefix_table(struct bgp_channel *c);
 void bgp_free_prefix(struct bgp_channel *c, struct bgp_prefix *bp);
 
-int bgp_rte_better(struct rte *, struct rte *);
-int bgp_rte_mergable(rte *pri, rte *sec);
-int bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best);
-struct rte *bgp_rte_modify_stale(struct rte *r, struct linpool *pool);
+int bgp_rte_better(struct rte_storage *, struct rte_storage *);
+int bgp_rte_mergable(struct rte_storage *pri, struct rte_storage *sec);
+int bgp_rte_recalculate(rtable *table, net *net, struct rte_storage *new, struct rte_storage *old, struct rte_storage *old_best);
+struct rta *bgp_rte_modify_stale(struct rte_storage *r, struct linpool *pool);
 void bgp_rt_notify(struct channel *C, struct rte_export *e);
-int bgp_preexport(struct proto *, struct rte *);
+int bgp_preexport(struct channel *, struct rte *);
 int bgp_get_attr(const struct eattr *e, byte *buf, int buflen);
-void bgp_get_route_info(struct rte *, byte *buf);
-int bgp_total_aigp_metric_(rte *e, u64 *metric, const struct adata **ad);
+void bgp_get_route_info(struct rte *, struct rte_storage *, byte *);
+int bgp_total_aigp_metric_(rta *a, u64 *metric, const struct adata **ad);
 
 #define BGP_AIGP_METRIC                1
 #define BGP_AIGP_MAX           U64(0xffffffffffffffff)
 
 static inline u64
-bgp_total_aigp_metric(rte *r)
+bgp_total_aigp_metric(rta *a)
 {
   u64 metric = BGP_AIGP_MAX;
   const struct adata *ad;
 
-  bgp_total_aigp_metric_(r, &metric, &ad);
+  bgp_total_aigp_metric_(a, &metric, &ad);
   return metric;
 }
 
index 83e09955067ed6c8ea3dc9485d2702fe65705699..f5411f9f7ca8057e516ecd7e5134962d0b671990 100644 (file)
@@ -1359,11 +1359,12 @@ bgp_rte_update(struct bgp_parse_state *s, net_addr *n, u32 path_id, rta *a0)
   }
 
   rte e0 = {
-    .attrs = rta_clone(s->cached_rta),
+    .net = n,
     .src = s->last_src,
+    .attrs = s->cached_rta,
   };
 
-  rte_update(&(s->channel->c), n, &e0);
+  rte_update(&(s->channel->c), &e0);
 }
 
 static void
index e28fe4d22da681c5d04285fb5b29a76f3746a2f1..cdfe492d74806d7d118973695cff678a60937604 100644 (file)
@@ -457,12 +457,12 @@ mrt_rib_table_entry_bgp_attrs(struct mrt_table_dump_state *s, rte *r)
   return;
 
 fail:
-  mrt_log(s, "Attribute list too long for %N", r->net->n.addr);
+  mrt_log(s, "Attribute list too long for %N", r->net);
 }
 #endif
 
 static void
-mrt_rib_table_entry(struct mrt_table_dump_state *s, rte *r)
+mrt_rib_table_entry(struct mrt_table_dump_state *s, rte *r, struct rte_storage *er)
 {
   buffer *b = &s->buf;
   uint peer = 0;
@@ -481,7 +481,7 @@ mrt_rib_table_entry(struct mrt_table_dump_state *s, rte *r)
 
   /* Peer Index and Originated Time */
   mrt_put_u16(b, peer);
-  mrt_put_u32(b, (r->lastmod + s->time_offset) TO_S);
+  mrt_put_u32(b, (er->lastmod + s->time_offset) TO_S);
 
   /* Path Identifier */
   if (s->add_path)
@@ -509,8 +509,7 @@ mrt_rib_table_dump(struct mrt_table_dump_state *s, net *n, int add_path)
   mrt_init_message(&s->buf, MRT_TABLE_DUMP_V2, subtype);
   mrt_rib_table_header(s, n->n.addr);
 
-  rte *rt, *rt0;
-  for (rt0 = n->routes; rt = rt0; rt0 = rt0->next)
+  for (struct rte_storage *rt = n->routes; rt; rt = rt->next)
   {
     if (rte_is_filtered(rt))
       continue;
@@ -522,11 +521,10 @@ mrt_rib_table_dump(struct mrt_table_dump_state *s, net *n, int add_path)
       continue;
     }
 
-    if (f_run(s->filter, &rt, s->linpool, 0) <= F_ACCEPT)
-      mrt_rib_table_entry(s, rt);
+    rte e = rte_copy(rt);
 
-    if (rt != rt0)
-      rte_free(rt);
+    if (f_run(s->filter, &e, s->linpool, 0) <= F_ACCEPT)
+      mrt_rib_table_entry(s, &e, rt);
 
     lp_flush(s->linpool);
   }
index f8d0115668b502e3f3aab029c6af361c5cb93b8c..32f2045ca1ffc4b38c365bfb0798af98d4000886 100644 (file)
 #include <stdlib.h>
 #include "ospf.h"
 
-static int ospf_preexport(struct proto *P, rte *new);
+static int ospf_preexport(struct channel *c, rte *new);
 static void ospf_reload_routes(struct channel *C);
-static int ospf_rte_better(struct rte *new, struct rte *old);
+static int ospf_rte_better(struct rte_storage *new, struct rte_storage *old);
 static void ospf_disp(timer *timer);
 
 
@@ -382,7 +382,7 @@ ospf_init(struct proto_config *CF)
 
 /* If new is better return 1 */
 static int
-ospf_rte_better(struct rte *new, struct rte *old)
+ospf_rte_better(struct rte_storage *new, struct rte_storage *old)
 {
   u32 new_metric1 = ea_get_int(new->attrs->eattrs, EA_OSPF_METRIC1, LSINFINITY);
 
@@ -471,13 +471,13 @@ ospf_disp(timer * timer)
  * import to the filters.
  */
 static int
-ospf_preexport(struct proto *P, rte *e)
+ospf_preexport(struct channel *c, rte *e)
 {
-  struct ospf_proto *p = (struct ospf_proto *) P;
+  struct ospf_proto *p = (struct ospf_proto *) (c->proto);
   struct ospf_area *oa = ospf_main_area(p);
 
   /* Reject our own routes */
-  if (e->src->proto == P)
+  if (e->src->proto == c->proto)
     return -1;
 
   /* Do not export routes to stub areas */
@@ -552,7 +552,7 @@ ospf_get_status(struct proto *P, byte * buf)
 }
 
 static void
-ospf_get_route_info(rte * rte, byte * buf)
+ospf_get_route_info(rte * rte, struct rte_storage * er UNUSED, byte * buf)
 {
   char *type = "<bug>";
 
index a754d3b8bb331c3417a99c4381ce75d78be1501f..f3b7e20bc4695339d8fd9a97303364363a5f00a6 100644 (file)
@@ -2098,15 +2098,16 @@ again1:
        rte e0 = {
          .attrs = rta_lookup(&a0),
          .src = p->p.main_source,
+         .net = nf->fn.addr, 
        };
 
        rta_free(nf->old_rta);
-       nf->old_rta = rta_clone(e0.attrs);
+       nf->old_rta = e0.attrs;
 
        DBG("Mod rte type %d - %N via %I on iface %s, met %d\n",
            a0.source, nf->fn.addr, a0.gw, a0.iface ? a0.iface->name : "(none)", nf->n.metric1);
 
-       rte_update(p->p.main_channel, nf->fn.addr, &e0);
+       rte_update(p->p.main_channel, &e0);
       }
     }
     else if (nf->old_rta)
index e1ddc106383bbe4ee23576b68d613de6429c8421..81d04c0f1d263edb5eb10f6dbd18555541325e65 100644 (file)
@@ -1317,9 +1317,9 @@ ospf_rt_notify(struct channel *ch, struct rte_export *e)
   if ((p->areano == 1) && oa_is_nssa(HEAD(p->area_list)))
     oa = HEAD(p->area_list);
 
-  if (!e->new)
+  if (!e->new.attrs)
   {
-    nf = fib_find(&p->rtf, e->net);
+    nf = fib_find(&p->rtf, e->old.net);
 
     if (!nf || !nf->external_rte)
       return;
@@ -1337,7 +1337,7 @@ ospf_rt_notify(struct channel *ch, struct rte_export *e)
   ASSERT(p->asbr);
 
   /* Get route attributes */
-  rta *a = e->new->attrs;
+  rta *a = e->new.attrs;
   eattr *m1a = ea_find(a->eattrs, EA_OSPF_METRIC1);
   eattr *m2a = ea_find(a->eattrs, EA_OSPF_METRIC2);
   uint m1 = m1a ? m1a->u.data : 0;
@@ -1346,14 +1346,14 @@ ospf_rt_notify(struct channel *ch, struct rte_export *e)
   if (m1 > LSINFINITY)
   {
     log(L_WARN "%s: Invalid ospf_metric1 value %u for route %N",
-       p->p.name, m1, e->net);
+       p->p.name, m1, e->new.net);
     m1 = LSINFINITY;
   }
 
   if (m2 > LSINFINITY)
   {
     log(L_WARN "%s: Invalid ospf_metric2 value %u for route %N",
-       p->p.name, m2, e->net);
+       p->p.name, m2, e->new.net);
     m2 = LSINFINITY;
   }
 
@@ -1377,12 +1377,12 @@ ospf_rt_notify(struct channel *ch, struct rte_export *e)
     if (ipa_zero(fwd))
     {
       log(L_ERR "%s: Cannot find forwarding address for NSSA-LSA %N",
-         p->p.name, e->net);
+         p->p.name, e->new.net);
       return;
     }
   }
 
-  nf = fib_get(&p->rtf, e->net);
+  nf = fib_get(&p->rtf, e->new.net);
   ospf_originate_ext_lsa(p, oa, nf, LSA_M_EXPORT, metric, ebit, fwd, tag, 1, p->vpn_pe);
   nf->external_rte = 1;
 }
index 0989b9febc3ad90ce166907b9eb589bd0878fbb3..15ffd109c8c5ad0b890f12722e76f00003068d53 100644 (file)
@@ -165,8 +165,9 @@ perf_loop(void *data)
     rte e0 = {
       .attrs = p->data[i].a,
       .src = p->p.main_source,
+      .net = &(p->data[i].net),
     };
-    rte_update(P->main_channel, &(p->data[i].net), &e0);
+    rte_update(P->main_channel, &e0);
   }
 
   clock_gettime(CLOCK_MONOTONIC, &ts_update);
@@ -177,6 +178,9 @@ perf_loop(void *data)
 
   clock_gettime(CLOCK_MONOTONIC, &ts_withdraw);
 
+  for (uint i=0; i<N; i++)
+    rta_free(p->data[i].a);
+
   s64 gentime = timediff(&ts_begin, &ts_generated);
   s64 updatetime = timediff(&ts_generated, &ts_update);
   s64 withdrawtime = timediff(&ts_update, &ts_withdraw);
index 0e541eb0e8c0c7ee94767b19436536d3482f65ba..f6fe7e697af4955206848682d3691296b4bbf6d7 100644 (file)
@@ -53,48 +53,54 @@ pipe_rt_notify(struct channel *src_ch, struct rte_export *export)
   struct pipe_proto *p = (void *) src_ch->proto;
   struct channel *dst = (src_ch == p->pri) ? p->sec : p->pri;
 
-  if (!export->new && !export->old)
+  if (!RTE_EXPORT_IS_OK(export))
     return;
 
+  const net_addr *net = export->new.attrs ? export->new.net : export->old.net;
+
   if (dst->table->pipe_busy)
     {
       log(L_ERR "Pipe loop detected when sending %N to table %s",
-         export->net, dst->table->name);
+         net, dst->table->name);
       return;
     }
 
-  if (export->new)
+  if (export->new.attrs)
     {
-      rta *a = alloca(rta_size(export->new->attrs));
-      memcpy(a, export->new->attrs, rta_size(export->new->attrs));
+      rta *a = alloca(rta_size(export->new.attrs));
+      memcpy(a, export->new.attrs, rta_size(export->new.attrs));
 
       a->cached = 0;
       a->uc = 0;
       a->hostentry = NULL;
 
       rte e0 = {
-       .attrs = rta_lookup(a),
+       .attrs = a,
+       .src = export->new.src,
+       .net = net,
       };
 
       src_ch->table->pipe_busy = 1;
-      rte_update(dst, export->net, &e0);
+      rte_update(dst, &e0);
       src_ch->table->pipe_busy = 0;
     }
   else
     {
       src_ch->table->pipe_busy = 1;
-      rte_withdraw(dst, export->net, export->old_src);
+      rte_withdraw(dst, net, export->old.src);
       src_ch->table->pipe_busy = 0;
     }
 }
 
 static int
-pipe_preexport(struct proto *P, rte *e)
+pipe_preexport(struct channel *src_ch, rte *e UNUSED)
 {
-  struct proto *pp = e->sender->proto;
+  struct pipe_proto *p = (void *) src_ch->proto;
+  struct channel *dst = (src_ch == p->pri) ? p->sec : p->pri;
 
-  if (pp == P)
-    return -1; /* Avoid local loops automatically */
+  /* Avoid pipe loops */
+  if (dst->table->pipe_busy)
+    return -1;
 
   return 0;
 }
index 524ff5af4c5b7be68acb6176e6abc7da2ea7d198..e3b49f83aab1e01b97408d75f035483118736dd5 100644 (file)
@@ -385,18 +385,17 @@ radv_trigger_valid(struct radv_config *cf)
 }
 
 static inline int
-radv_net_match_trigger(struct radv_config *cf, net_addr *n)
+radv_net_match_trigger(struct radv_config *cf, const net_addr *n)
 {
   return radv_trigger_valid(cf) && net_equal(n, &cf->trigger);
 }
 
 int
-radv_preexport(struct proto *P, rte *new)
+radv_preexport(struct channel *c, rte *new)
 {
-  // struct radv_proto *p = (struct radv_proto *) P;
-  struct radv_config *cf = (struct radv_config *) (P->cf);
+  struct radv_config *cf = (struct radv_config *) (c->proto->cf);
 
-  if (radv_net_match_trigger(cf, new->net->n.addr))
+  if (radv_net_match_trigger(cf, new->net))
     return RIC_PROCESS;
 
   if (cf->propagate_routes)
@@ -413,10 +412,12 @@ radv_rt_notify(struct channel *ch, struct rte_export *e)
   struct radv_route *rt;
   eattr *ea;
 
-  if (radv_net_match_trigger(cf, e->net))
+  const net_addr *net = e->new.attrs ? e->new.net : e->old.net;
+
+  if (radv_net_match_trigger(cf, net))
   {
     u8 old_active = p->active;
-    p->active = !!e->new;
+    p->active = !!e->new.attrs;
 
     if (p->active == old_active)
       return;
@@ -440,15 +441,15 @@ radv_rt_notify(struct channel *ch, struct rte_export *e)
    * And yes, we exclude the trigger route on purpose.
    */
 
-  if (e->new)
+  if (e->new.attrs)
   {
     /* Update */
 
-    ea = ea_find(e->new->attrs->eattrs, EA_RA_PREFERENCE);
+    ea = ea_find(e->new.attrs->eattrs, EA_RA_PREFERENCE);
     uint preference = ea ? ea->u.data : RA_PREF_MEDIUM;
     uint preference_set = !!ea;
 
-    ea = ea_find(e->new->attrs->eattrs, EA_RA_LIFETIME);
+    ea = ea_find(e->new.attrs->eattrs, EA_RA_LIFETIME);
     uint lifetime = ea ? ea->u.data : 0;
     uint lifetime_set = !!ea;
 
@@ -457,14 +458,14 @@ radv_rt_notify(struct channel *ch, struct rte_export *e)
        (preference != RA_PREF_HIGH))
     {
       log(L_WARN "%s: Invalid ra_preference value %u on route %N",
-         p->p.name, preference, e->net);
+         p->p.name, preference, e->new.net);
       preference = RA_PREF_MEDIUM;
       preference_set = 1;
       lifetime = 0;
       lifetime_set = 1;
     }
 
-    rt = fib_get(&p->routes, e->net);
+    rt = fib_get(&p->routes, e->new.net);
 
     /* Ignore update if nothing changed */
     if (rt->valid &&
@@ -487,7 +488,7 @@ radv_rt_notify(struct channel *ch, struct rte_export *e)
   else
   {
     /* Withdraw */
-    rt = fib_find(&p->routes, e->net);
+    rt = fib_find(&p->routes, e->old.net);
 
     if (!rt || !rt->valid)
       return;
index ad62ca0ab845d5c56567f0a7763c8a889c713b90..53696af0383af57952a9ae967039102a852ac24f 100644 (file)
@@ -210,9 +210,10 @@ rip_announce_rte(struct rip_proto *p, struct rip_entry *en)
     rte e0 = {
       .attrs = &a0,
       .src = p->p.main_source,
+      .net = en->n.addr, 
     };
 
-    rte_update(p->p.main_channel, en->n.addr, &e0);
+    rte_update(p->p.main_channel, &e0);
   }
   else
     rte_withdraw(p->p.main_channel, en->n.addr, p->p.main_source);
@@ -316,10 +317,10 @@ rip_rt_notify(struct channel *ch, struct rte_export *e)
   struct rip_entry *en;
   int old_metric;
 
-  if (e->new)
+  if (e->new.attrs)
   {
     /* Update */
-    rta *a = e->new->attrs;
+    rta *a = e->new.attrs;
     u32 rt_tag = ea_get_int(a->eattrs, EA_RIP_TAG, 0);
     u32 rt_metric = ea_get_int(a->eattrs, EA_RIP_METRIC, 1);
     struct iface *rt_from = (struct iface *) ea_get_int(a->eattrs, EA_RIP_FROM, 0);
@@ -327,14 +328,14 @@ rip_rt_notify(struct channel *ch, struct rte_export *e)
     if (rt_metric > p->infinity)
     {
       log(L_WARN "%s: Invalid rip_metric value %u for route %N",
-         p->p.name, rt_metric, e->net);
+         p->p.name, rt_metric, e->new.net);
       rt_metric = p->infinity;
     }
 
     if (rt_tag > 0xffff)
     {
       log(L_WARN "%s: Invalid rip_tag value %u for route %N",
-         p->p.name, rt_tag, e->net);
+         p->p.name, rt_tag, e->new.net);
       rt_metric = p->infinity;
       rt_tag = 0;
     }
@@ -346,7 +347,7 @@ rip_rt_notify(struct channel *ch, struct rte_export *e)
      * collection.
      */
 
-    en = fib_get(&p->rtable, e->net);
+    en = fib_get(&p->rtable, e->new.net);
 
     old_metric = en->valid ? en->metric : -1;
 
@@ -360,7 +361,7 @@ rip_rt_notify(struct channel *ch, struct rte_export *e)
   else
   {
     /* Withdraw */
-    en = fib_find(&p->rtable, e->net);
+    en = fib_find(&p->rtable, e->old.net);
 
     if (!en || en->valid != RIP_ENTRY_VALID)
       return;
@@ -1083,7 +1084,7 @@ rip_reload_routes(struct channel *C)
 }
 
 static int
-rip_rte_better(struct rte *new, struct rte *old)
+rip_rte_better(struct rte_storage *new, struct rte_storage *old)
 {
   u32 new_metric = ea_get_int(new->attrs->eattrs, EA_RIP_METRIC, 1);
   u32 old_metric = ea_get_int(old->attrs->eattrs, EA_RIP_METRIC, 1);
@@ -1185,7 +1186,7 @@ rip_reconfigure(struct proto *P, struct proto_config *CF)
 }
 
 static void
-rip_get_route_info(rte *rte, byte *buf)
+rip_get_route_info(rte *rte, struct rte_storage *er UNUSED, byte *buf)
 {
   u32 rt_metric = ea_get_int(rte->attrs->eattrs, EA_RIP_METRIC, 1);
   u32 rt_tag = ea_get_int(rte->attrs->eattrs, EA_RIP_TAG, 0);
index df974acca3dda04afdddc33af878f8ef93003535..79204387e4f9f785ba4d12d171263ccd3fa30f19 100644 (file)
@@ -130,9 +130,10 @@ rpki_table_add_roa(struct rpki_cache *cache, struct channel *channel, const net_
   rte e0 = {
     .attrs = &a0,
     .src = p->p.main_source,
+    .net = &pfxr->n, 
   };
 
-  rte_update(channel, &pfxr->n, &e0);
+  rte_update(channel, &e0);
 }
 
 void
index db805a66f50e8c4b96b81eda004d6f722f52658b..20edcf1d5475f30cb233ea52f8b296c6b16d2d14 100644 (file)
@@ -101,27 +101,16 @@ static_announce_rte(struct static_proto *p, struct static_route *r)
   if (r->state == SRS_CLEAN)
     return;
 
-  /* We skip rta_lookup() here */
   rte e0 = {
     .attrs = a,
     .src = static_get_source(p, r->index),
-  }, *e = &e0;
+    .net = r->net,
+  };
 
   if (r->cmds)
-  {
-    /* Create a temporary table node */
-    e->net = alloca(sizeof(net) + r->net->length);
-    memset(e->net, 0, sizeof(net) + r->net->length);
-    net_copy(e->net->n.addr, r->net);
-
-    /* Evaluate the filter */
-    f_eval_rte(r->cmds, &e, static_lp);
-
-    /* Remove the temporary node */
-    e->net = NULL;
-  }
+    f_eval_rte(r->cmds, &e0, static_lp);
 
-  rte_update(p->p.main_channel, r->net, e);
+  rte_update(p->p.main_channel, &e0);
   r->state = SRS_CLEAN;
 
   if (r->cmds)
@@ -133,7 +122,7 @@ withdraw:
   if (r->state == SRS_DOWN)
     return;
 
-  rte_withdraw(p->p.main_channel, r->net, p->p.main_source);
+  rte_withdraw(p->p.main_channel, r->net, static_get_source(p, r->index));
   r->state = SRS_DOWN;
 }
 
@@ -413,7 +402,7 @@ static_reload_routes(struct channel *C)
 }
 
 static int
-static_rte_better(rte *new, rte *old)
+static_rte_better(struct rte_storage *new, struct rte_storage *old)
 {
   u32 n = ea_get_int(new->attrs->eattrs, EA_GEN_IGP_METRIC, IGP_METRIC_UNKNOWN);
   u32 o = ea_get_int(old->attrs->eattrs, EA_GEN_IGP_METRIC, IGP_METRIC_UNKNOWN);
@@ -421,7 +410,7 @@ static_rte_better(rte *new, rte *old)
 }
 
 static int
-static_rte_mergable(rte *pri, rte *sec)
+static_rte_mergable(struct rte_storage *pri, struct rte_storage *sec)
 {
   u32 a = ea_get_int(pri->attrs->eattrs, EA_GEN_IGP_METRIC, IGP_METRIC_UNKNOWN);
   u32 b = ea_get_int(sec->attrs->eattrs, EA_GEN_IGP_METRIC, IGP_METRIC_UNKNOWN);
@@ -719,7 +708,7 @@ static_copy_config(struct proto_config *dest, struct proto_config *src)
 }
 
 static void
-static_get_route_info(rte *rte, byte *buf)
+static_get_route_info(rte *rte, struct rte_storage *es UNUSED, byte *buf)
 {
   eattr *a = ea_find(rte->attrs->eattrs, EA_GEN_IGP_METRIC);
   if (a)
index ec928480891857d922c1492c03720d1124adc7d3..66ed6250b29bd06ebead603ab0cce12d15a02677 100644 (file)
@@ -199,7 +199,6 @@ sockaddr_fill_dl(struct sockaddr_dl *sa, struct iface *ifa)
 static int
 krt_send_route(struct krt_proto *p, int cmd, rte *e)
 {
-  net *net = e->net;
   rta *a = e->attrs;
   static int msg_seq;
   struct iface *j, *i = a->nh.iface;
@@ -208,7 +207,7 @@ krt_send_route(struct krt_proto *p, int cmd, rte *e)
   char *body = (char *)msg.buf;
   sockaddr gate, mask, dst;
 
-  DBG("krt-sock: send %I/%d via %I\n", net->n.prefix, net->n.pxlen, a->gw);
+  DBG("krt-sock: send %N via %I\n", e->net, a->gw);
 
   bzero(&msg,sizeof (struct rt_msghdr));
   msg.rtm.rtm_version = RTM_VERSION;
@@ -218,7 +217,7 @@ krt_send_route(struct krt_proto *p, int cmd, rte *e)
   msg.rtm.rtm_flags = RTF_UP | RTF_PROTO1;
 
   /* XXXX */
-  if (net_pxlen(net->n.addr) == net_max_prefix_length[net->n.addr->type])
+  if (net_pxlen(e->net) == net_max_prefix_length[e->net->type])
     msg.rtm.rtm_flags |= RTF_HOST;
   else
     msg.rtm.rtm_addrs |= RTA_NETMASK;
@@ -260,7 +259,7 @@ krt_send_route(struct krt_proto *p, int cmd, rte *e)
 
   int af = AF_UNSPEC;
 
-  switch (net->n.addr->type) {
+  switch (e->net->type) {
     case NET_IP4:
       af = AF_INET;
       break;
@@ -268,12 +267,12 @@ krt_send_route(struct krt_proto *p, int cmd, rte *e)
       af = AF_INET6;
       break;
     default:
-      log(L_ERR "KRT: Not sending route %N to kernel", net->n.addr);
+      log(L_ERR "KRT: Not sending route %N to kernel", e->net);
       return -1;
   }
 
-  sockaddr_fill(&dst,  af, net_prefix(net->n.addr), NULL, 0);
-  sockaddr_fill(&mask, af, net_pxmask(net->n.addr), NULL, 0);
+  sockaddr_fill(&dst,  af, net_prefix(e->net), NULL, 0);
+  sockaddr_fill(&mask, af, net_pxmask(e->net), NULL, 0);
 
   switch (a->dest)
   {
@@ -303,7 +302,7 @@ krt_send_route(struct krt_proto *p, int cmd, rte *e)
 
 #if __OpenBSD__
     /* Keeping temporarily old code for OpenBSD */
-    struct ifa *addr = (net->n.addr->type == NET_IP4) ? i->addr4 : (i->addr6 ?: i->llv6);
+    struct ifa *addr = (e->net->type == NET_IP4) ? i->addr4 : (i->addr6 ?: i->llv6);
 
     if (!addr)
     {
@@ -339,7 +338,7 @@ krt_send_route(struct krt_proto *p, int cmd, rte *e)
   msg.rtm.rtm_msglen = l;
 
   if ((l = write(p->sys.sk->fd, (char *)&msg, l)) < 0) {
-    log(L_ERR "KRT: Error sending route %N to kernel: %m", net->n.addr);
+    log(L_ERR "KRT: Error sending route %N to kernel: %m", e->net);
     return -1;
   }
 
@@ -347,22 +346,24 @@ krt_send_route(struct krt_proto *p, int cmd, rte *e)
 }
 
 void
-krt_replace_rte(struct krt_proto *p, rte *new, rte *old)
+krt_replace_rte(struct krt_proto *p, struct rte_export *e)
 {
   int err = 0;
 
-  if (old)
-    krt_send_route(p, RTM_DELETE, old);
+  if (RTE_EXPORT_OLD_OK(e))
+    krt_send_route(p, RTM_DELETE, &e->old);
 
-  if (new)
-    err = krt_send_route(p, RTM_ADD, new);
+  if (RTE_EXPORT_NEW_OK(e))
+    err = krt_send_route(p, RTM_ADD, &e->new);
 
-  if (new)
+  /* There is no need to update this bit for the old route. It is used solely together
+   * with the bit in export map in channel. */
+  if (RTE_EXPORT_NEW_OK(e))
   {
     if (err < 0)
-      bmap_clear(&p->sync_map, new->id);
+      bmap_clear(&p->sync_map, e->new_id);
     else
-      bmap_set(&p->sync_map, new->id);
+      bmap_set(&p->sync_map, e->new_id);
   }
 }
 
@@ -374,7 +375,6 @@ krt_read_route(struct ks_msg *msg, struct krt_proto *p, int scan)
   /* p is NULL iff KRT_SHARED_SOCKET and !scan */
 
   int ipv6;
-  net *net;
   sockaddr dst, gate, mask;
   ip_addr idst, igate, imask;
   net_addr ndst;
@@ -491,8 +491,6 @@ krt_read_route(struct ks_msg *msg, struct krt_proto *p, int scan)
   else
     src = KRT_SRC_KERNEL;
 
-  net = net_get(p->p.main_channel->table, &ndst);
-
   rta a = {
     .source = RTS_INHERIT,
     .scope = SCOPE_UNIVERSE,
@@ -519,7 +517,7 @@ krt_read_route(struct ks_msg *msg, struct krt_proto *p, int scan)
   if (!a.nh.iface)
     {
       log(L_ERR "KRT: Received route %N with unknown ifindex %u",
-         net->n.addr, msg->rtm.rtm_index);
+         &ndst, msg->rtm.rtm_index);
       return;
     }
 
@@ -542,19 +540,20 @@ krt_read_route(struct ks_msg *msg, struct krt_proto *p, int scan)
           return;
 
        log(L_ERR "KRT: Received route %N with strange next-hop %I",
-           net->n.addr, a.nh.gw);
+           &ndst, a.nh.gw);
        return;
       }
   }
 
  done:;
-  rte e0 = {}, *e = &e0;
-  e->attrs = &a;
-  e->net = net;
+  rte e0 = {
+    .attrs = &a,
+    .net = &ndst,
+  };
 
   ea_list *ea = alloca(sizeof(ea_list) + 1 * sizeof(eattr));
-  *ea = (ea_list) { .count = 1, .next = e->attrs->eattrs };
-  e->attrs->eattrs = ea;
+  *ea = (ea_list) { .count = 1, .next = a.eattrs };
+  a.eattrs = ea;
 
   ea->attrs[0] = (eattr) {
     .id = EA_KRT_SOURCE,
@@ -563,9 +562,9 @@ krt_read_route(struct ks_msg *msg, struct krt_proto *p, int scan)
   };
 
   if (scan)
-    krt_got_route(p, e, src);
+    krt_got_route(p, &e0, src);
   else
-    krt_got_route_async(p, e, new, src);
+    krt_got_route_async(p, &e0, new, src);
 }
 
 static void
index 6a5d6487237d8225ab756e9f83a4e17a0324ed40..baf2331ae70111e1ef43178410ad019ba5108519 100644 (file)
@@ -105,7 +105,7 @@ struct nl_parse_state
   int scan;
   int merge;
 
-  net *net;
+  net_addr *net;
   rta *attrs;
   struct krt_proto *proto;
   s8 new;
@@ -1236,7 +1236,7 @@ static int
 nl_send_route(struct krt_proto *p, rte *e, int op, int dest, struct nexthop *nh)
 {
   eattr *ea;
-  net *net = e->net;
+  const net_addr *net = e->net;
   rta *a = e->attrs;
   ea_list *eattrs = a->eattrs;
   int bufsize = 128 + KRT_METRICS_MAX*8 + nh_bufsize(&(a->nh));
@@ -1251,7 +1251,7 @@ nl_send_route(struct krt_proto *p, rte *e, int op, int dest, struct nexthop *nh)
   int rsize = sizeof(*r) + bufsize;
   r = alloca(rsize);
 
-  DBG("nl_send_route(%N,op=%x)\n", net->n.addr, op);
+  DBG("nl_send_route(%N,op=%x)\n", net, op);
 
   bzero(&r->h, sizeof(r->h));
   bzero(&r->r, sizeof(r->r));
@@ -1260,7 +1260,7 @@ nl_send_route(struct krt_proto *p, rte *e, int op, int dest, struct nexthop *nh)
   r->h.nlmsg_flags = op | NLM_F_REQUEST | NLM_F_ACK;
 
   r->r.rtm_family = p->af;
-  r->r.rtm_dst_len = net_pxlen(net->n.addr);
+  r->r.rtm_dst_len = net_pxlen(net);
   r->r.rtm_protocol = RTPROT_BIRD;
   r->r.rtm_scope = RT_SCOPE_NOWHERE;
 #ifdef HAVE_MPLS_KERNEL
@@ -1272,7 +1272,7 @@ nl_send_route(struct krt_proto *p, rte *e, int op, int dest, struct nexthop *nh)
      * 2) Never use RTA_PRIORITY
      */
 
-    u32 label = net_mpls(net->n.addr);
+    u32 label = net_mpls(net);
     nl_add_attr_mpls(&r->h, rsize, RTA_DST, 1, &label);
     r->r.rtm_scope = RT_SCOPE_UNIVERSE;
     r->r.rtm_type = RTN_UNICAST;
@@ -1280,12 +1280,12 @@ nl_send_route(struct krt_proto *p, rte *e, int op, int dest, struct nexthop *nh)
   else
 #endif
   {
-    nl_add_attr_ipa(&r->h, rsize, RTA_DST, net_prefix(net->n.addr));
+    nl_add_attr_ipa(&r->h, rsize, RTA_DST, net_prefix(net));
 
     /* Add source address for IPv6 SADR routes */
-    if (net->n.addr->type == NET_IP6_SADR)
+    if (net->type == NET_IP6_SADR)
     {
-      net_addr_ip6_sadr *a = (void *) &net->n.addr;
+      net_addr_ip6_sadr *a = (void *) &net;
       nl_add_attr_ip6(&r->h, rsize, RTA_SRC, a->src_prefix);
       r->r.rtm_src_len = a->src_pxlen;
     }
@@ -1427,7 +1427,7 @@ nl_replace_rte(struct krt_proto *p, rte *e)
 
 
 void
-krt_replace_rte(struct krt_proto *p, rte *new, rte *old)
+krt_replace_rte(struct krt_proto *p, struct rte_export *e)
 {
   int err = 0;
 
@@ -1443,30 +1443,32 @@ krt_replace_rte(struct krt_proto *p, rte *new, rte *old)
    * old route value, so we do not try to optimize IPv6 ECMP reconfigurations.
    */
 
-  if (krt_ipv4(p) && old && new)
+  if (krt_ipv4(p) && RTE_EXPORT_NEW_OK(e) && RTE_EXPORT_OLD_OK(e))
   {
-    err = nl_replace_rte(p, new);
+    err = nl_replace_rte(p, &e->new);
   }
   else
   {
-    if (old)
-      nl_delete_rte(p, old);
+    if (RTE_EXPORT_OLD_OK(e))
+      nl_delete_rte(p, &e->old);
 
-    if (new)
-      err = nl_add_rte(p, new);
+    if (RTE_EXPORT_NEW_OK(e))
+      err = nl_add_rte(p, &e->new);
   }
 
-  if (new)
+  /* There is no need to update this bit for the old route. It is used solely together
+   * with the bit in export map in channel. */
+  if (RTE_EXPORT_NEW_OK(e))
   {
     if (err < 0)
-      bmap_clear(&p->sync_map, new->id);
+      bmap_clear(&p->sync_map, e->new_id);
     else
-      bmap_set(&p->sync_map, new->id);
+      bmap_set(&p->sync_map, e->new_id);
   }
 }
 
 static int
-nl_mergable_route(struct nl_parse_state *s, net *net, struct krt_proto *p, uint priority, uint krt_type, uint rtm_family)
+nl_mergable_route(struct nl_parse_state *s, const net_addr *net, struct krt_proto *p, uint priority, uint krt_type, uint rtm_family)
 {
   /* Route merging is used for IPv6 scans */
   if (!s->scan || (rtm_family != AF_INET6))
@@ -1662,9 +1664,7 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
                      net6_prefix(&src), net6_pxlen(&src));
   }
 
-  net *net = net_get(p->p.main_channel->table, n);
-
-  if (s->net && !nl_mergable_route(s, net, p, priority, i->rtm_type, i->rtm_family))
+  if (s->net && !nl_mergable_route(s, n, p, priority, i->rtm_type, i->rtm_family))
     nl_announce_route(s);
 
   rta *ra = lp_allocz(s->pool, RTA_MAX_SIZE);
@@ -1686,7 +1686,7 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
          struct nexthop *nh = nl_parse_multipath(s, p, a[RTA_MULTIPATH], i->rtm_family);
          if (!nh)
            {
-             log(L_ERR "KRT: Received strange multipath route %N", net->n.addr);
+             log(L_ERR "KRT: Received strange multipath route %N", n);
              return;
            }
 
@@ -1700,7 +1700,7 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
       ra->nh.iface = if_find_by_index(oif);
       if (!ra->nh.iface)
        {
-         log(L_ERR "KRT: Received route %N with unknown ifindex %u", net->n.addr, oif);
+         log(L_ERR "KRT: Received route %N with unknown ifindex %u", n, oif);
          return;
        }
 
@@ -1727,7 +1727,7 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
                           (ra->nh.flags & RNF_ONLINK) ? NEF_ONLINK : 0);
          if (!nbr || (nbr->scope == SCOPE_HOST))
            {
-             log(L_ERR "KRT: Received route %N with strange next-hop %I", net->n.addr,
+             log(L_ERR "KRT: Received route %N with strange next-hop %I", n,
                   ra->nh.gw);
              return;
            }
@@ -1823,29 +1823,29 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
     {
       u32 metrics[KRT_METRICS_MAX];
       ea_list *ea = lp_alloc(s->pool, sizeof(ea_list) + KRT_METRICS_MAX * sizeof(eattr));
-      int t, n = 0;
+      int t, na = 0;
 
       if (nl_parse_metrics(a[RTA_METRICS], metrics, ARRAY_SIZE(metrics)) < 0)
         {
-         log(L_ERR "KRT: Received route %N with strange RTA_METRICS attribute", net->n.addr);
+         log(L_ERR "KRT: Received route %N with strange RTA_METRICS attribute", n);
          return;
        }
 
       for (t = 1; t < KRT_METRICS_MAX; t++)
        if (metrics[0] & (1 << t))
          {
-           ea->attrs[n].id = EA_CODE(PROTOCOL_KERNEL, KRT_METRICS_OFFSET + t);
-           ea->attrs[n].flags = 0;
-           ea->attrs[n].type = EAF_TYPE_INT; /* FIXME: Some are EAF_TYPE_BITFIELD */
-           ea->attrs[n].u.data = metrics[t];
-           n++;
+           ea->attrs[na].id = EA_CODE(PROTOCOL_KERNEL, KRT_METRICS_OFFSET + t);
+           ea->attrs[na].flags = 0;
+           ea->attrs[na].type = EAF_TYPE_INT; /* FIXME: Some are EAF_TYPE_BITFIELD */
+           ea->attrs[na].u.data = metrics[t];
+           na++;
          }
 
-      if (n > 0)
+      if (na > 0)
         {
          ea->next = ra->eattrs;
          ea->flags = EALF_SORTED;
-         ea->count = n;
+         ea->count = na;
          ra->eattrs = ea;
        }
     }
@@ -1861,7 +1861,9 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
   if (!s->net)
   {
     /* Store the new route */
-    s->net = net;
+    s->net = lp_alloc(s->pool, n->length);
+    net_copy(s->net, n);
+
     s->attrs = ra;
     s->proto = p;
     s->new = new;
index f39f6f10b82ca3d1f69fa494a8e5f9d3810641e7..c4c74e13c53b68d202b369be7df0ad151163be98 100644 (file)
@@ -251,14 +251,14 @@ static inline void
 krt_trace_in(struct krt_proto *p, rte *e, char *msg)
 {
   if (p->p.debug & D_PACKETS)
-    log(L_TRACE "%s: %N: %s", p->p.name, e->net->n.addr, msg);
+    log(L_TRACE "%s: %N: %s", p->p.name, e->net, msg);
 }
 
 static inline void
 krt_trace_in_rl(struct tbf *f, struct krt_proto *p, rte *e, char *msg)
 {
   if (p->p.debug & D_PACKETS)
-    log_rl(f, L_TRACE "%s: %N: %s", p->p.name, e->net->n.addr, msg);
+    log_rl(f, L_TRACE "%s: %N: %s", p->p.name, e->net, msg);
 }
 
 /*
@@ -278,33 +278,34 @@ static struct tbf rl_alien = TBF_DEFAULT_LOG_LIMITS;
  */
 
 static inline u32
-krt_metric(rte *a)
+krt_metric(struct rte_storage *a)
 {
   eattr *ea = ea_find(a->attrs->eattrs, EA_KRT_METRIC);
   return ea ? ea->u.data : 0;
 }
 
 static inline int
-krt_same_key(rte *a, rte *b)
+krt_same_key(struct rte_storage *a, struct rte_storage *b)
 {
   return (krt_metric(a) == krt_metric(b));
 }
 
 static inline int
-krt_uptodate(rte *a, rte *b)
+krt_uptodate(struct rte_storage *a, struct rte_storage *b)
 {
   return (a->attrs == b->attrs);
 }
 
 static void
-krt_learn_announce_update(struct krt_proto *p, rte *e)
+krt_learn_announce_update(struct krt_proto *p, struct rte_storage *e)
 {
   rte e0 = {
-    .attrs = rta_clone(e->attrs),
+    .attrs = e->attrs,
     .src = p->p.main_source,
+    .net = e->net->n.addr,
   };
 
-  rte_update(p->p.main_channel, e->net->n.addr, &e0);
+  rte_update(p->p.main_channel, &e0);
 }
 
 static void
@@ -317,21 +318,20 @@ krt_learn_announce_delete(struct krt_proto *p, net_addr *n)
 static void
 krt_learn_scan(struct krt_proto *p, rte *e)
 {
-  net *n0 = e->net;
-  net *n = net_get(&p->krt_table, n0->n.addr);
-  rte *m, **mm;
-
-  e = rte_store(e);
+  net *n = net_get(&p->krt_table, e->net);
+  struct rte_storage *er = rte_store(e, n);
+  struct rte_storage *m, **mm;
 
   for(mm=&n->routes; m = *mm; mm=&m->next)
-    if (krt_same_key(m, e))
+    if (krt_same_key(m, er))
       break;
+
   if (m)
     {
-      if (krt_uptodate(m, e))
+      if (krt_uptodate(m, er))
        {
          krt_trace_in_rl(&rl_alien, p, e, "[alien] seen");
-         rte_free(e);
+         rte_free(er);
          m->pflags |= KRT_REF_SEEN;
        }
       else
@@ -344,11 +344,12 @@ krt_learn_scan(struct krt_proto *p, rte *e)
     }
   else
     krt_trace_in(p, e, "[alien] created");
+
   if (!m)
     {
-      e->next = n->routes;
-      n->routes = e;
-      e->pflags |= KRT_REF_SEEN;
+      er->next = n->routes;
+      n->routes = er;
+      er->pflags |= KRT_REF_SEEN;
     }
 }
 
@@ -364,7 +365,7 @@ krt_learn_prune(struct krt_proto *p)
 again:
   FIB_ITERATE_START(fib, &fit, net, n)
     {
-      rte *e, **ee, *best, **pbest, *old_best;
+      struct rte_storage *e, **ee, *best, **pbest, *old_best;
 
       /*
        * Note that old_best may be NULL even if there was an old best route in
@@ -429,27 +430,28 @@ again:
 static void
 krt_learn_async(struct krt_proto *p, rte *e, int new)
 {
-  net *n0 = e->net;
-  net *n = net_get(&p->krt_table, n0->n.addr);
-  rte *g, **gg, *best, **bestp, *old_best;
+  net *n = net_get(&p->krt_table, e->net);
+  struct rte_storage *g, **gg, *best, **bestp, *old_best;
 
   ASSERT(!e->attrs->cached);
   e->attrs->pref = p->p.main_channel->preference;
 
-  e = rte_store(e);
+  struct rte_storage *er = rte_store(e, n);
 
   old_best = n->routes;
+
   for(gg=&n->routes; g = *gg; gg = &g->next)
-    if (krt_same_key(g, e))
+    if (krt_same_key(g, er))
       break;
+
   if (new)
     {
       if (g)
        {
-         if (krt_uptodate(g, e))
+         if (krt_uptodate(g, er))
            {
              krt_trace_in(p, e, "[alien async] same");
-             rte_free(e);
+             rte_free(er);
              return;
            }
          krt_trace_in(p, e, "[alien async] updated");
@@ -459,20 +461,20 @@ krt_learn_async(struct krt_proto *p, rte *e, int new)
       else
        krt_trace_in(p, e, "[alien async] created");
 
-      e->next = n->routes;
-      n->routes = e;
+      er->next = n->routes;
+      n->routes = er;
     }
   else if (!g)
     {
       krt_trace_in(p, e, "[alien async] delete failed");
-      rte_free(e);
+      rte_free(er);
       return;
     }
   else
     {
       krt_trace_in(p, e, "[alien async] removed");
       *gg = g->next;
-      rte_free(e);
+      rte_free(er);
       rte_free(g);
     }
   best = n->routes;
@@ -552,50 +554,43 @@ krt_flush_routes(struct krt_proto *p)
     {
       if (krt_is_installed(p, n))
        {
+         struct rte_export e = {
+           .old = rte_copy(n->routes),
+           .old_id = n->routes->id,
+         };
+
          /* FIXME: this does not work if gw is changed in export filter */
-         krt_replace_rte(p, NULL, n->routes);
+         krt_replace_rte(p, &e);
        }
     }
   FIB_WALK_END;
 }
 
-static struct rte *
-krt_export_net(struct krt_proto *p, net *net, rte **rt_free)
+static _Bool
+krt_export_net(struct krt_proto *p, net *net, rte *rt)
 {
   struct channel *c = p->p.main_channel;
   const struct filter *filter = c->out_filter;
-  rte *rt;
 
   if (c->ra_mode == RA_MERGED)
-    return rt_export_merged(c, net, rt_free, krt_filter_lp, 1);
-
-  rt = net->routes;
-  *rt_free = NULL;
+    return rt_export_merged(c, net, rt, krt_filter_lp, 1);
 
-  if (!rte_is_valid(rt))
-    return NULL;
+  if (!rte_is_valid(net->routes))
+    return 0;
 
   if (filter == FILTER_REJECT)
-    return NULL;
+    return 0;
 
   /* We could run krt_preexport() here, but it is already handled by krt_is_installed() */
+  *rt = rte_copy(net->routes);
 
   if (filter == FILTER_ACCEPT)
-    goto accept;
-
-  if (f_run(filter, &rt, krt_filter_lp, FF_SILENT) > F_ACCEPT)
-    goto reject;
-
+    return 1;
 
-accept:
-  if (rt != net->routes)
-    *rt_free = rt;
-  return rt;
+  if (f_run(filter, rt, krt_filter_lp, FF_SILENT) > F_ACCEPT)
+    return 0;
 
-reject:
-  if (rt != net->routes)
-    rte_free(rt);
-  return NULL;
+  return 1;
 }
 
 static int
@@ -620,8 +615,10 @@ krt_same_dest(rte *k, rte *e)
 void
 krt_got_route(struct krt_proto *p, rte *e, s8 src)
 {
-  rte *new = NULL, *rt_free = NULL;
-  net *n = e->net;
+
+  struct rte_export ex = {
+    .old = *e,
+  };
 
 #ifdef KRT_ALLOW_LEARN
   switch (src)
@@ -647,24 +644,24 @@ krt_got_route(struct krt_proto *p, rte *e, s8 src)
   if (!p->ready)
     goto ignore;
 
-  if (!krt_is_installed(p, n))
-    goto delete;
+  net *n = net_find(p->p.main_channel->table, e->net);
 
-  new = krt_export_net(p, n, &rt_free);
+  if (!n || !krt_is_installed(p, n))
+    goto delete;
 
   /* Rejected by filters */
-  if (!new)
+  if (!krt_export_net(p, n, &ex.new))
     goto delete;
 
   /* Route to this destination was already seen. Strange, but it happens... */
-  if (bmap_test(&p->seen_map, new->id))
+  if (bmap_test(&p->seen_map, n->routes->id))
     goto aseen;
 
   /* Mark route as seen */
-  bmap_set(&p->seen_map, new->id);
+  bmap_set(&p->seen_map, n->routes->id);
 
   /* TODO: There also may be changes in route eattrs, we ignore that for now. */
-  if (!bmap_test(&p->sync_map, new->id) || !krt_same_dest(e, new))
+  if (!bmap_test(&p->sync_map, n->routes->id) || !krt_same_dest(e, &ex.new))
     goto update;
 
   goto seen;
@@ -682,19 +679,17 @@ ignore:
   goto done;
 
 update:
-  krt_trace_in(p, new, "updating");
-  krt_replace_rte(p, new, e);
+  krt_trace_in(p, &ex.new, "updating");
+  ex.new_id = n->routes->id;
+  krt_replace_rte(p, &ex);
   goto done;
 
 delete:
   krt_trace_in(p, e, "deleting");
-  krt_replace_rte(p, NULL, e);
+  krt_replace_rte(p, &ex);
   goto done;
 
 done:
-  if (rt_free)
-    rte_free(rt_free);
-
   lp_flush(krt_filter_lp);
 }
 
@@ -714,18 +709,16 @@ krt_prune(struct krt_proto *p)
   {
     if (p->ready && krt_is_installed(p, n) && !bmap_test(&p->seen_map, n->routes->id))
     {
-      rte *rt_free = NULL;
-      rte *new = krt_export_net(p, n, &rt_free);
+      struct rte_export ex = {
+       .new_id = n->routes->id
+      };
 
-      if (new)
+      if (krt_export_net(p, n, &ex.new))
       {
-       krt_trace_in(p, new, "installing");
-       krt_replace_rte(p, new, NULL);
+       krt_trace_in(p, &ex.new, "installing");
+       krt_replace_rte(p, &ex);
       }
 
-      if (rt_free)
-       rte_free(rt_free);
-
       lp_flush(krt_filter_lp);
     }
   }
@@ -753,7 +746,8 @@ krt_got_route_async(struct krt_proto *p, rte *e, int new, s8 src)
       if (new)
        {
          krt_trace_in(p, e, "[redirect] deleting");
-         krt_replace_rte(p, NULL, e);
+         struct rte_export ex = { .old = *e };
+         krt_replace_rte(p, &ex);
        }
       /* If !new, it is probably echo of our deletion */
       break;
@@ -873,10 +867,9 @@ krt_scan_timer_kick(struct krt_proto *p)
  */
 
 static int
-krt_preexport(struct proto *P, rte *e)
+krt_preexport(struct channel *c, rte *e)
 {
-  // struct krt_proto *p = (struct krt_proto *) P;
-  if (e->src->proto == P)
+  if (e->src->proto == c->proto)
     return -1;
 
   if (!krt_capable(e))
@@ -900,12 +893,12 @@ krt_rt_notify(struct channel *ch, struct rte_export *e)
    * but if we processed the update as usual, we would send withdraw to the
    * kernel, which would remove the new imported route instead.
    */
-  if (!e->new && (e->new_src->proto == ch->proto))
+  if (!e->new.attrs && (e->new.src->proto == ch->proto))
     return;
 #endif
 
   if (p->initialized)          /* Before first scan we don't touch the routes */
-    krt_replace_rte(p, e->new, e->old);
+    krt_replace_rte(p, e);
 }
 
 static void
index 72659a3a6813193ca2361f83847cd73b8963c5dd..a0c60d8024add0f051182f2fe07d1bb7fccce372 100644 (file)
@@ -83,11 +83,11 @@ void krt_got_route(struct krt_proto *p, struct rte *e, s8 src);
 void krt_got_route_async(struct krt_proto *p, struct rte *e, int new, s8 src);
 
 static inline int
-krt_get_sync_error(struct krt_proto *p, struct rte *e)
+krt_get_sync_error(struct krt_proto *p, u32 id)
 {
   return (p->p.proto_state == PS_UP) &&
-    bmap_test(&p->p.main_channel->export_map, e->id) &&
-    !bmap_test(&p->sync_map, e->id);
+    bmap_test(&p->p.main_channel->export_map, id) &&
+    !bmap_test(&p->sync_map, id);
 }
 
 /* Values for rte->u.krt_sync.src */
@@ -143,7 +143,7 @@ void krt_sys_copy_config(struct krt_config *, struct krt_config *);
 
 int  krt_capable(rte *e);
 void krt_do_scan(struct krt_proto *);
-void krt_replace_rte(struct krt_proto *p, rte *new, rte *old);
+void krt_replace_rte(struct krt_proto *p, struct rte_export *e);
 int krt_sys_get_attr(const eattr *a, byte *buf, int buflen);