]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Merge commit '938742decc6e1d6d3a0375dd012b75172e747bbc' into haugesund
authorMaria Matejka <mq@ucw.cz>
Wed, 8 Jun 2022 13:31:28 +0000 (15:31 +0200)
committerMaria Matejka <mq@ucw.cz>
Wed, 8 Jun 2022 13:31:28 +0000 (15:31 +0200)
26 files changed:
1  2 
filter/f-inst.c
filter/filter.c
lib/route.h
nest/config.Y
nest/rt-attr.c
nest/rt-dev.c
nest/rt-show.c
nest/rt-table.c
nest/rt.h
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/linux/netlink.c
sysdep/unix/krt.c
sysdep/unix/main.c

diff --cc filter/f-inst.c
index 1690c9f64f441fe21b073bcd38a05a0eb27e9464,65a0b011ca480c16f5027221ae617749928f3821..9ff5f79ab29f23ee4205993cdaee80b70573bf5c
   *    m4_dnl    NEVER_CONSTANT-> don't generate pre-interpretation code at all
   *    m4_dnl    ACCESS_RTE    -> check that route is available, also NEVER_CONSTANT
   *    m4_dnl    ACCESS_EATTRS -> pre-cache the eattrs; use only with ACCESS_RTE
-  *    m4_dnl    f_rta_cow(fs) -> function to call before any change to route should be done
 - *    m4_dnl    f_rte_cow(fs) -> function to call before any change to route should be done
   *
   *    m4_dnl  If you are stymied, see FI_CALL or FI_CONSTANT or just search for
   *    m4_dnl  the mentioned macros in this file to see what is happening there in wild.
      ARG_ANY(1);
      STATIC_ATTR;
      ARG_TYPE(1, sa.type);
--
-     f_rta_cow(fs);
 -    f_rte_cow(fs);
      {
        union {
        struct nexthop_adata nha;
        if (da->type >= EAF_TYPE__MAX)
        bug("Unsupported attribute type");
  
-       f_rta_cow(fs);
 -      f_rte_cow(fs);
--
        switch (da->type) {
        case T_OPAQUE:
        case T_IFACE:
      ACCESS_RTE;
      ACCESS_EATTRS;
  
-     f_rta_cow(fs);
 -    f_rte_cow(fs);
      ea_unset_attr(fs->eattrs, 1, da);
    }
  
diff --cc filter/filter.c
index 124c99324189971af9c62c7a8ac6f87d34867926,ff6a34199ead6affa8c6d62e787ec955194b875d..ad2fafe215f71aaa1833921f80510f65bbab7bcf
@@@ -96,28 -96,15 +96,7 @@@ void (*bt_assert_hook)(int result, cons
  
  static inline void f_cache_eattrs(struct filter_state *fs)
  {
-   fs->eattrs = &(fs->rte->attrs->eattrs);
 -  fs->eattrs = &((*fs->rte)->attrs);
--}
--
- /*
-  * rta_cow - prepare rta for modification by filter
-  */
- static void
- f_rta_cow(struct filter_state *fs)
 -static inline void f_rte_cow(struct filter_state *fs)
--{
-   if (!rta_is_cached(fs->rte->attrs))
 -  if (!((*fs->rte)->flags & REF_COW))
--    return;
-   /*
-    * 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, tmp_linpool);
--
-   /* Re-cache the ea_list */
-   f_cache_eattrs(fs);
 -  *fs->rte = rte_cow(*fs->rte);
++  fs->eattrs = &(fs->rte->attrs);
  }
  
  static struct tbf rl_runtime_err = TBF_DEFAULT_LOG_LIMITS;
diff --cc lib/route.h
index 283a3760db47c807de8ec1549332014982a59bb3,3f6fc5a9c0155695c3730f0140bd81521cf0f93a..1b2f4de6e03b12317e47839e9ad6a24e2516da19
@@@ -16,21 -16,19 +16,21 @@@ struct network
  struct proto;
  struct cli;
  
 +
  typedef struct rte {
-   struct rta *attrs;                  /* Attributes of this route */
 -  struct rte *next;
 -  struct network *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 ea_list *attrs;              /* Attributes of this route */
 +  const net_addr *net;                        /* Network this RTE belongs to */
 +  struct rte_src *src;                        /* Route source that created the route */
 +  struct rt_import_hook *sender;      /* Import hook used to send the route to the routing table */
 +  btime lastmod;                      /* Last modified (set by table) */
    u32 id;                             /* Table specific route id */
 -  byte flags;                         /* Flags (REF_...) */
 +  byte flags;                         /* Table-specific flags */
    byte pflags;                                /* Protocol-specific flags */
 -  btime lastmod;                      /* Last modified */
 +  u8 generation;                      /* If this route import is based on other previously exported route,
 +                                         this value should be 1 + MAX(generation of the parent routes).
 +                                         Otherwise the route is independent and this value is zero. */
  } rte;
  
 -#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 */
@@@ -333,24 -327,17 +329,24 @@@ extern struct ea_class ea_gen_from
  /* Source: An old method to devise the route source protocol and kind.
   * To be superseded in a near future by something more informative. */
  extern struct ea_class ea_gen_source;
 -static inline u32 rt_get_source_attr(rte *rt)
 +static inline u32 rt_get_source_attr(const rte *rt)
- { return ea_get_int(rt->attrs->eattrs, &ea_gen_source, 0); }
+ { return ea_get_int(rt->attrs, &ea_gen_source, 0); }
  
  /* Flowspec validation result */
 -#define FLOWSPEC_UNKNOWN      0
 -#define FLOWSPEC_VALID                1
 -#define FLOWSPEC_INVALID      2
 +enum flowspec_valid {
 +  FLOWSPEC_UNKNOWN    = 0,
 +  FLOWSPEC_VALID      = 1,
 +  FLOWSPEC_INVALID    = 2,
 +  FLOWSPEC__MAX,
 +};
 +
 +extern const char * flowspec_valid_names[FLOWSPEC__MAX];
 +static inline const char *flowspec_valid_name(enum flowspec_valid v)
 +{ return (v < FLOWSPEC__MAX) ? flowspec_valid_names[v] : "???"; }
  
  extern struct ea_class ea_gen_flowspec_valid;
 -static inline u32 rt_get_flowspec_valid(rte *rt)
 +static inline enum flowspec_valid rt_get_flowspec_valid(rte *rt)
- { return ea_get_int(rt->attrs->eattrs, &ea_gen_flowspec_valid, FLOWSPEC_UNKNOWN); }
+ { return ea_get_int(rt->attrs, &ea_gen_flowspec_valid, FLOWSPEC_UNKNOWN); }
  
  /* Next hop: For now, stored as adata */
  extern struct ea_class ea_gen_nexthop;
@@@ -411,9 -398,9 +407,9 @@@ static inline int nhea_dest(eattr *nhea
      return nhad->dest;
  }
  
 -static inline int rte_dest(rte *r)
 +static inline int rte_dest(const rte *r)
  {
-   return nhea_dest(ea_find(r->attrs->eattrs, &ea_gen_nexthop));
+   return nhea_dest(ea_find(r->attrs, &ea_gen_nexthop));
  }
  
  void rta_init(void);
diff --cc nest/config.Y
index 7c68a09ad5d5048932b9db77135f0d3a1a02c2a4,773673e8936d921aea9245d66023e7d90d5547a6..2d73b4c739046fe524490e3bf18dcc61d389a3ab
@@@ -850,11 -849,9 +850,11 @@@ CF_CLI(DUMP INTERFACES,,, [[Dump interf
  CF_CLI(DUMP NEIGHBORS,,, [[Dump neighbor cache]])
  { neigh_dump_all(); cli_msg(0, ""); } ;
  CF_CLI(DUMP ATTRIBUTES,,, [[Dump attribute cache]])
- { rta_dump_all(); cli_msg(0, ""); } ;
+ { ea_dump_all(); cli_msg(0, ""); } ;
 -CF_CLI(DUMP ROUTES,,, [[Dump routing table]])
 +CF_CLI(DUMP ROUTES,,, [[Dump routes]])
  { rt_dump_all(); cli_msg(0, ""); } ;
 +CF_CLI(DUMP TABLES,,, [[Dump table connections]])
 +{ rt_dump_hooks_all(); cli_msg(0, ""); } ;
  CF_CLI(DUMP PROTOCOLS,,, [[Dump protocol information]])
  { protos_dump_all(); cli_msg(0, ""); } ;
  CF_CLI(DUMP FILTER ALL,,, [[Dump all filters in linearized form]])
diff --cc nest/rt-attr.c
index f548a575bb58b75467a9533c38e79fab325a95b3,6927751fe99fa2e05dca3ef37d47bbe589e20ce1..31e2057e2adfa1aff90a795f72be7d96e1845418
@@@ -172,15 -172,8 +172,14 @@@ struct ea_class ea_gen_flowspec_valid 
    .readonly = 1,
  };
  
 +const char * flowspec_valid_names[FLOWSPEC__MAX] = {
 +  [FLOWSPEC_UNKNOWN]  = "unknown",
 +  [FLOWSPEC_VALID]    = "",
 +  [FLOWSPEC_INVALID]  = "invalid",
 +};
 +
  pool *rta_pool;
  
- static slab *rta_slab;
  static slab *rte_src_slab;
  
  static struct idm src_ids;
diff --cc nest/rt-dev.c
index fa224f9a2823708ba676219e4978cafed01dda68,63f8a26b1c0698cab3358bbe6c5b32154cc9bbd5..7f45985ff30ec5c047fed6ba6318f80b4ea9fa9a
@@@ -85,16 -87,13 +85,16 @@@ dev_ifa_notify(struct proto *P, uint fl
        .ad = { .length = (void *) NEXTHOP_NEXT(&nhad.nh) - (void *) nhad.ad.data, },
        };
  
-       ea_set_attr_u32(&a0.eattrs, &ea_gen_preference, 0, c->preference);
-       ea_set_attr_u32(&a0.eattrs, &ea_gen_source, 0, RTS_DEVICE);
-       ea_set_attr_data(&a0.eattrs, &ea_gen_nexthop, 0, nhad.ad.data, nhad.ad.length);
+       ea_set_attr_u32(&ea, &ea_gen_preference, 0, c->preference);
+       ea_set_attr_u32(&ea, &ea_gen_source, 0, RTS_DEVICE);
+       ea_set_attr_data(&ea, &ea_gen_nexthop, 0, nhad.ad.data, nhad.ad.length);
  
 -      e = rte_get_temp(rta_lookup(ea), src);
 -      e->pflags = 0;
 -      rte_update2(c, net, e, src);
 +      rte e0 = {
-       .attrs = rta_lookup(&a0),
++      .attrs = ea,
 +      .src = src,
 +      };
 +
 +      rte_update(c, net, &e0, src);
      }
  }
  
diff --cc nest/rt-show.c
index cc5a9a101c8c57f084dad1bc6976823a55a74249,2cbc500ac9f5feda1cb3da8ce4391fd50a35cf3a..f3852d170d5537a314d1564b3d2fd6ba6402657c
@@@ -42,17 -42,15 +42,17 @@@ rt_show_rte(struct cli *c, byte *ia, rt
  {
    byte from[IPA_MAX_TEXT_LENGTH+8];
    byte tm[TM_DATETIME_BUFFER_SIZE], info[256];
-   rta *a = e->attrs;
+   ea_list *a = e->attrs;
    int sync_error = d->kernel ? krt_get_sync_error(d->kernel, e) : 0;
    void (*get_route_info)(struct rte *, byte *buf);
 -  eattr *nhea = ea_find(a, &ea_gen_nexthop);
 +  eattr *nhea = net_type_match(e->net, NB_DEST) ?
-     ea_find(a->eattrs, &ea_gen_nexthop) : NULL;
++    ea_find(a, &ea_gen_nexthop) : NULL;
    struct nexthop_adata *nhad = nhea ? (struct nexthop_adata *) nhea->u.ptr : NULL;
 -  int dest = NEXTHOP_IS_REACHABLE(nhad) ? RTD_UNICAST : nhad->dest;
 +  int dest = nhad ? (NEXTHOP_IS_REACHABLE(nhad) ? RTD_UNICAST : nhad->dest) : RTD_NONE;
 +  int flowspec_valid = net_is_flow(e->net) ? rt_get_flowspec_valid(e) : FLOWSPEC_UNKNOWN;
  
    tm_format_time(tm, &config->tf_route, e->lastmod);
-   ip_addr a_from = ea_get_ip(a->eattrs, &ea_gen_from, IPA_NONE);
+   ip_addr a_from = ea_get_ip(a, &ea_gen_from, IPA_NONE);
    if (ipa_nonzero(a_from) && (!nhad || !ipa_equal(a_from, nhad->nh.gw)))
      bsprintf(from, " from %I", a_from);
    else
      }
  
    if (d->verbose)
-     rta_show(c, a);
+     ea_show_list(c, a);
  }
  
 +static uint
 +rte_feed_count(net *n)
 +{
 +  uint count = 0;
 +  for (struct rte_storage *e = n->routes; e; e = e->next)
 +    if (rte_is_valid(RTE_OR_NULL(e)))
 +      count++;
 +  return count;
 +}
 +
 +static void
 +rte_feed_obtain(net *n, rte **feed, uint count)
 +{
 +  uint i = 0;
 +  for (struct rte_storage *e = n->routes; e; e = e->next)
 +    if (rte_is_valid(RTE_OR_NULL(e)))
 +    {
 +      ASSERT_DIE(i < count);
 +      feed[i++] = &e->rte;
 +    }
 +  ASSERT_DIE(i == count);
 +}
 +
  static void
  rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
  {
diff --cc nest/rt-table.c
index 946f4021c94cadebe403c7ed416e2786884ecc0b,e3dab120c57122faa6f50daa33e1b7ea636b3dbf..1631e00f5efb3204a23cff966a3d67dc568c6119
@@@ -592,23 -571,53 +592,23 @@@ rte_find(net *net, struct rte_src *src
    return e;
  }
  
 -rte *
 -rte_do_cow(rte *r)
 -{
 -  rte *e = sl_alloc(rte_slab);
  
 -  memcpy(e, r, sizeof(rte));
 +struct rte_storage *
 +rte_store(const rte *r, net *net, rtable *tab)
 +{
 +  struct rte_storage *e = sl_alloc(tab->rte_slab);
  
 -  rt_lock_source(e->src);
 -  e->attrs = rta_clone(r->attrs);
 -  e->flags = 0;
 -  return e;
 -}
 +  e->rte = *r;
 +  e->rte.net = net->n.addr;
  
 -/**
 - * 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().
 - *
 - * 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.
 - *
 - * Result: a pointer to the new writable &rte with writable &rta.
 - */
 -rte *
 -rte_cow_rta(rte *r)
 -{
 -  if (!rta_is_cached(r->attrs))
 -    return r;
 +  rt_lock_source(e->rte.src);
  
-   if (e->rte.attrs->cached)
 -  r = rte_cow(r);
++  if (ea_is_cached(e->rte.attrs))
 +    e->rte.attrs = rta_clone(e->rte.attrs);
 +  else
 +    e->rte.attrs = rta_lookup(e->rte.attrs);
  
 -  /* This looks stupid but should DWIW. */
 -  rta_free(r->attrs);
 -  return r;
 +  return e;
  }
  
  /**
@@@ -960,50 -954,46 +960,49 @@@ rt_export_merged(struct channel *c, str
    if (!rte_is_valid(best0))
      return NULL;
  
 -  best = export_filter(c, best0, rt_free, silent);
 +  /* Already rejected, no need to re-run the filter */
 +  if (!c->refeeding && bmap_test(&c->export_reject_map, best0->id))
 +    return NULL;
 +
 +  rloc = *best0;
 +  best = export_filter(c, &rloc, silent);
  
 -  if (!best || !rte_is_reachable(best))
 +  if (!best)
 +    /* Best route doesn't pass the filter */
 +    return NULL;
 +
 +  if (!rte_is_reachable(best))
 +    /* Unreachable routes can't be merged */
      return best;
  
 -  for (rt0 = best0->next; rt0; rt0 = rt0->next)
 +  for (uint i = 1; i < count; i++)
    {
 -    if (!rte_mergable(best0, rt0))
 +    if (!rte_mergable(best0, feed[i]))
        continue;
  
 -    rt = export_filter(c, rt0, &tmp, 1);
 +    rte tmp0 = *feed[i];
 +    rte *tmp = export_filter(c, &tmp0, 1);
  
 -    if (!rt)
 +    if (!tmp || !rte_is_reachable(tmp))
        continue;
  
-     eattr *nhea = ea_find(tmp->attrs->eattrs, &ea_gen_nexthop);
 -    if (rte_is_reachable(rt))
 -    {
 -      eattr *nhea = ea_find(rt->attrs, &ea_gen_nexthop);
 -      ASSERT_DIE(nhea);
 -
 -      if (nhs)
 -      nhs = nexthop_merge(nhs, (struct nexthop_adata *) nhea->u.ptr, c->merge_limit, pool);
 -      else
 -      nhs = (struct nexthop_adata *) nhea->u.ptr;
 -    }
++    eattr *nhea = ea_find(tmp->attrs, &ea_gen_nexthop);
 +    ASSERT_DIE(nhea);
  
 -    if (tmp)
 -      rte_free(tmp);
 +    if (nhs)
 +      nhs = nexthop_merge(nhs, (struct nexthop_adata *) nhea->u.ptr, c->merge_limit, pool);
 +    else
 +      nhs = (struct nexthop_adata *) nhea->u.ptr;
    }
  
 -
    if (nhs)
    {
-     eattr *nhea = ea_find(best->attrs->eattrs, &ea_gen_nexthop);
+     eattr *nhea = ea_find(best->attrs, &ea_gen_nexthop);
      ASSERT_DIE(nhea);
  
      nhs = nexthop_merge(nhs, (struct nexthop_adata *) nhea->u.ptr, c->merge_limit, pool);
  
-     best->attrs = rta_cow(best->attrs, pool);
-     ea_set_attr(&best->attrs->eattrs,
 -    best = rte_cow_rta(best);
+     ea_set_attr(&best->attrs,
        EA_LITERAL_DIRECT_ADATA(&ea_gen_nexthop, 0, &nhs->ad));
    }
  
@@@ -1226,30 -1177,21 +1225,30 @@@ rte_validate(struct channel *ch, rte *e
      return 0;
    }
  
 -  eattr *nhea = ea_find(e->attrs, &ea_gen_nexthop);
 -  int dest = nhea_dest(nhea);
 -
 -  if (net_type_match(n->n.addr, NB_DEST) == !dest)
 +  if (net_type_match(n, NB_DEST))
    {
-     eattr *nhea = ea_find(e->attrs->eattrs, &ea_gen_nexthop);
 -    log(L_WARN "Ignoring route %N with invalid dest %d received via %s",
 -      n->n.addr, dest, e->sender->proto->name);
 -    return 0;
 -  }
++    eattr *nhea = ea_find(e->attrs, &ea_gen_nexthop);
 +    int dest = nhea_dest(nhea);
 +
 +    if (dest == RTD_NONE)
 +    {
 +      log(L_WARN "Ignoring route %N with no destination received via %s",
 +        n, ch->proto->name);
 +      return 0;
 +    }
  
 -  if ((dest == RTD_UNICAST) &&
 -      !nexthop_is_sorted((struct nexthop_adata *) nhea->u.ptr))
 +    if ((dest == RTD_UNICAST) &&
 +      !nexthop_is_sorted((struct nexthop_adata *) nhea->u.ptr))
 +    {
 +      log(L_WARN "Ignoring unsorted multipath route %N received via %s",
 +        n, ch->proto->name);
 +      return 0;
 +    }
 +  }
-   else if (ea_find(e->attrs->eattrs, &ea_gen_nexthop))
++  else if (ea_find(e->attrs, &ea_gen_nexthop))
    {
 -    log(L_WARN "Ignoring unsorted multipath route %N received via %s",
 -      n->n.addr, e->sender->proto->name);
 +    log(L_WARN "Ignoring route %N having a nexthop attribute received via %s",
 +      n, ch->proto->name);
      return 0;
    }
  
@@@ -1880,11 -1803,12 +1879,11 @@@ rt_modify_stale(rtable *t, struct rt_im
   * 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("PF=%02x ", e->pflags);
 -  ea_dump(e->attrs);
 +  debug("%-1N ", e->rte.net);
 +  debug("PF=%02x ", e->rte.pflags);
-   rta_dump(e->rte.attrs);
++  ea_dump(e->rte.attrs);
    debug("\n");
  }
  
@@@ -2668,35 -2543,17 +2667,23 @@@ rt_next_hop_update_rte(rtable *tab, ne
    if (!head)
      return NULL;
  
-   rta a = *old->attrs;
-   a.cached = 0;
-   rta_apply_hostentry(&a, head);
 -  ea_list *ea = old->attrs;
 -  rta_apply_hostentry(&ea, head);
 +  rte e0 = *old;
-   e0.attrs = &a;
++  rta_apply_hostentry(&e0.attrs, head);
  
 -  rte *e = sl_alloc(rte_slab);
 -  memcpy(e, old, sizeof(rte));
 -  e->attrs = rta_lookup(ea);
 -  rt_lock_source(e->src);
 -
 -  return e;
 +  return rte_store(&e0, n, tab);
  }
  
-   eattr *heea = ea_find(r->attrs->eattrs, &ea_gen_hostentry);
 +static inline void
 +rt_next_hop_resolve_rte(rte *r)
 +{
-   if (r->attrs->cached)
-   {
-     rta *a = tmp_alloc(RTA_MAX_SIZE);
-     *a = *r->attrs;
-     a->cached = 0;
-     r->attrs = a;
-   }
-   rta_apply_hostentry(r->attrs, head);
++  eattr *heea = ea_find(r->attrs, &ea_gen_hostentry);
 +  if (!heea)
 +    return;
 +
 +  struct hostentry_adata *head = (struct hostentry_adata *) heea->u.ptr;
 +
++  rta_apply_hostentry(&r->attrs, head);
 +}
  
  #ifdef CONFIG_BGP
  
@@@ -2736,8 -2593,8 +2723,8 @@@ rta_get_first_asn(ea_list *a
    return (e && as_path_get_first_regular(e->u.ptr, &asn)) ? asn : 0;
  }
  
 -int
 +static inline enum flowspec_valid
- rt_flowspec_check(rtable *tab_ip, rtable *tab_flow, const net_addr *n, rta *a, int interior)
+ rt_flowspec_check(rtable *tab_ip, rtable *tab_flow, const net_addr *n, ea_list *a, int interior)
  {
    ASSERT(rt_is_ip(tab_ip));
    ASSERT(rt_is_flow(tab_flow));
  
    /* No best-match BGP route -> no flowspec */
    if (!rb || (rt_get_source_attr(rb) != RTS_BGP))
 -    return 0;
 +    return FLOWSPEC_INVALID;
  
    /* Find ORIGINATOR_ID values */
-   u32 orig_a = ea_get_int(a->eattrs, "bgp_originator_id", 0);
-   u32 orig_b = ea_get_int(rb->attrs->eattrs, "bgp_originator_id", 0);
+   u32 orig_a = ea_get_int(a, "bgp_originator_id", 0);
+   u32 orig_b = ea_get_int(rb->attrs, "bgp_originator_id", 0);
  
    /* Originator is either ORIGINATOR_ID (if present), or BGP neighbor address (if not) */
    if ((orig_a != orig_b) || (!orig_a && !orig_b && !ipa_equal(
-         ea_get_ip(a->eattrs, &ea_gen_from, IPA_NONE),
-         ea_get_ip(rb->attrs->eattrs, &ea_gen_from, IPA_NONE)
+         ea_get_ip(a, &ea_gen_from, IPA_NONE),
+         ea_get_ip(rb->attrs, &ea_gen_from, IPA_NONE)
          )))
 -    return 0;
 +    return FLOWSPEC_INVALID;
  
  
    /* Find ASN of the best-match route, for use in next checks */
@@@ -2834,59 -2690,19 +2821,44 @@@ rt_flowspec_update_rte(rtable *tab, ne
    if (old == valid)
      return NULL;
  
-   rta *a = alloca(RTA_MAX_SIZE);
-   *a = *r->attrs;
-   a->cached = 0;
-   ea_set_attr_u32(&a->eattrs, &ea_gen_flowspec_valid, 0, valid);
 -  ea_list *a = r->attrs;
 -  ea_set_attr_u32(&a, &ea_gen_flowspec_valid, 0, valid);
--
-   rte new;
-   memcpy(&new, r, sizeof(rte));
-   new.attrs = a;
 -  rte *new = sl_alloc(rte_slab);
 -  memcpy(new, r, sizeof(rte));
 -  new->attrs = ea_lookup(a);
++  rte new = *r;
++  ea_set_attr_u32(&new.attrs, &ea_gen_flowspec_valid, 0, valid);
  
 -  return new;
 +  return rte_store(&new, n, tab);
  #else
    return NULL;
  #endif
  }
  
-   if (r->attrs->cached)
-   {
-     rta *a = tmp_alloc(RTA_MAX_SIZE);
-     *a = *r->attrs;
-     a->cached = 0;
-     r->attrs = a;
-   }
 +static inline void
 +rt_flowspec_resolve_rte(rte *r, struct channel *c)
 +{
 +#ifdef CONFIG_BGP
 +  enum flowspec_valid valid, old = rt_get_flowspec_valid(r);
 +  struct bgp_channel *bc = (struct bgp_channel *) c;
 +
 +  if (        (rt_get_source_attr(r) == RTS_BGP)
 +     && (c->channel == &channel_bgp)
 +     && (bc->base_table))
 +  {
 +    struct bgp_proto *p = SKIP_BACK(struct bgp_proto, p, bc->c.proto);
 +    valid = rt_flowspec_check(
 +      bc->base_table,
 +      c->in_req.hook->table,
 +      r->net, r->attrs, p->is_interior);
 +  }
 +  else
 +    valid = FLOWSPEC_UNKNOWN;
 +
 +  if (valid == old)
 +    return;
 +
-     ea_unset_attr(&r->attrs->eattrs, 0, &ea_gen_flowspec_valid);
 +  if (valid == FLOWSPEC_UNKNOWN)
-     ea_set_attr_u32(&r->attrs->eattrs, &ea_gen_flowspec_valid, 0, valid);
++    ea_unset_attr(&r->attrs, 0, &ea_gen_flowspec_valid);
 +  else
++    ea_set_attr_u32(&r->attrs, &ea_gen_flowspec_valid, 0, valid);
 +#endif
 +}
  
  static inline int
  rt_next_hop_update_net(rtable *tab, net *n)
@@@ -3650,9 -3500,9 +3622,9 @@@ if_local_addr(ip_addr a, struct iface *
  }
  
  u32
 -rt_get_igp_metric(rte *rt)
 +rt_get_igp_metric(const rte *rt)
  {
-   eattr *ea = ea_find(rt->attrs->eattrs, "igp_metric");
+   eattr *ea = ea_find(rt->attrs, "igp_metric");
  
    if (ea)
      return ea->u.data;
@@@ -3683,11 -3533,11 +3655,11 @@@ rt_update_hostentry(rtable *tab, struc
    net *n = net_route(tab, &he_addr);
    if (n)
      {
 -      rte *e = n->routes;
 -      ea_list *a = e->attrs;
 +      struct rte_storage *e = n->routes;
-       rta *a = e->rte.attrs;
++      ea_list *a = e->rte.attrs;
        pxlen = n->n.addr->pxlen;
  
-       if (ea_find(a->eattrs, &ea_gen_hostentry))
+       if (ea_find(a, &ea_gen_hostentry))
        {
          /* Recursive route should not depend on another recursive route */
          log(L_WARN "Next hop address %I resolvable through recursive route for %N",
diff --cc nest/rt.h
index eb868aa76e1ec21077bd4bc5384a1e8eee1b8518,f7cc164dfe6266327246c83184a9428d8217f539..32bba6a6c075b25372bca9c95c48c82c497855ce
+++ b/nest/rt.h
@@@ -448,16 -290,8 +448,6 @@@ struct hostentry_adata 
  void
  ea_set_hostentry(ea_list **to, struct rtable *dep, struct rtable *tab, ip_addr gw, ip_addr ll, u32 lnum, u32 labels[lnum]);
  
- /*
- struct hostentry * rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep);
- void rta_apply_hostentry(rta *a, struct hostentry *he, u32 lnum, u32 labels[lnum]);
 -int rt_flowspec_check(rtable *tab_ip, rtable *tab_flow, const net_addr *n, ea_list *a, int interior);
--
- static inline void
- rta_set_recursive_next_hop(rtable *dep, rta *a, rtable *tab, ip_addr gw, ip_addr ll, u32 lnum, u32 labels[lnum])
- {
-   rta_apply_hostentry(a, rt_get_hostentry(tab, gw, ll, dep), lnum, labels);
- }
- */
  
  /*
   *    Default protocol preferences
index 65d2567e614347daa096807d05c3a5efef8b1b26,e3903c6795f0e50a02c0484bb18f5cd1068e7a55..4939619f948c51f93087a4ad3a9d354882780e3e
@@@ -677,32 -677,25 +677,30 @@@ babel_announce_rte(struct babel_proto *
        }
      };
  
-     rta a0 = { .eattrs = &eattrs.l, };
 -    rte *rte = rte_get_temp(rta_lookup(&eattrs.l), p->p.main_source);
 +    rte e0 = {
-       .attrs = &a0,
++      .attrs = &eattrs.l,
 +      .src = p->p.main_source,
 +    };
  
      e->unreachable = 0;
 -    rte_update2(c, e->n.addr, rte, p->p.main_source);
 +    rte_update(c, e->n.addr, &e0, p->p.main_source);
    }
    else if (e->valid && (e->router_id != p->router_id))
    {
      /* Unreachable */
-     rta a0 = {};
+     ea_list *ea = NULL;
  
-     ea_set_attr_u32(&a0.eattrs, &ea_gen_preference, 0, 1);
-     ea_set_attr_u32(&a0.eattrs, &ea_gen_source, 0, RTS_BABEL);
-     ea_set_dest(&a0.eattrs, 0, RTD_UNREACHABLE);
+     ea_set_attr_u32(&ea, &ea_gen_preference, 0, 1);
+     ea_set_attr_u32(&ea, &ea_gen_source, 0, RTS_BABEL);
+     ea_set_dest(&ea, 0, RTD_UNREACHABLE);
  
 -    rte *rte = rte_get_temp(rta_lookup(ea), p->p.main_source);
 -    rte->pflags = 0;
 +    rte e0 = {
-       .attrs = &a0,
++      .attrs = ea,
 +      .src = p->p.main_source,
 +    };
  
      e->unreachable = 1;
 -    rte_update2(c, e->n.addr, rte, p->p.main_source);
 +    rte_update(c, e->n.addr, &e0, p->p.main_source);
    }
    else
    {
@@@ -2353,9 -2346,9 +2351,9 @@@ babel_rte_better(struct rte *new, struc
  }
  
  static u32
 -babel_rte_igp_metric(struct rte *rt)
 +babel_rte_igp_metric(const rte *rt)
  {
-   return ea_get_int(rt->attrs->eattrs, &ea_babel_metric, BABEL_INFINITY);
+   return ea_get_int(rt->attrs, &ea_babel_metric, BABEL_INFINITY);
  }
  
  
index 0b715eaa60fb080de7444c472f731e4cceb22ca9,c605bb07666d31c70bffba9472d62b1832200d2e..6d33ef2e407ffacb0bf7fc52809fb3cd8a2ab90d
@@@ -372,15 -372,13 +372,13 @@@ bgp_aigp_set_metric(struct linpool *poo
  }
  
  int
 -bgp_total_aigp_metric_(rte *e, u64 *metric, const struct adata **ad)
 +bgp_total_aigp_metric_(const rte *e, u64 *metric, const struct adata **ad)
  {
-   rta *a = e->attrs;
-   eattr *ea = ea_find(a->eattrs, BGP_EA_ID(BA_AIGP));
-   if (!ea)
+   eattr *a = ea_find(e->attrs, BGP_EA_ID(BA_AIGP));
+   if (!a)
      return 0;
  
-   const byte *b = bgp_aigp_get_tlv(ea->u.ptr, BGP_AIGP_METRIC);
+   const byte *b = bgp_aigp_get_tlv(a->u.ptr, BGP_AIGP_METRIC);
    if (!b)
      return 0;
  
@@@ -1744,11 -1740,11 +1742,11 @@@ bgp_preexport(struct channel *c, rte *e
    }
  
    /* Handle well-known communities, RFC 1997 */
 -  struct eattr *c;
 +  struct eattr *com;
    if (p->cf->interpret_communities &&
-       (com = ea_find(e->attrs->eattrs, BGP_EA_ID(BA_COMMUNITY))))
 -      (c = ea_find(e->attrs, BGP_EA_ID(BA_COMMUNITY))))
++      (com = ea_find(e->attrs, BGP_EA_ID(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))
@@@ -2320,12 -2317,12 +2318,12 @@@ bgp_rte_recalculate(rtable *table, net 
      return !old_suppressed;
  }
  
 -struct rte *
 +rte *
  bgp_rte_modify_stale(struct rte *r, struct linpool *pool)
  {
-   eattr *ea = ea_find(r->attrs->eattrs, BGP_EA_ID(BA_COMMUNITY));
 -  eattr *a = ea_find(r->attrs, BGP_EA_ID(BA_COMMUNITY));
 -  const struct adata *ad = a ? a->u.ptr : NULL;
 -  uint flags = a ? a->flags : BAF_PARTIAL;
++  eattr *ea = ea_find(r->attrs, BGP_EA_ID(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;
  
-   rta *a = rta_do_cow(r->attrs, pool);
-   
 -  r = rte_cow_rta(r);
 -  bgp_set_attr_ptr(&(r->attrs), BA_COMMUNITY, flags,
 +  _Thread_local static rte e0;
 +  e0 = *r;
-   e0.attrs = a;
 +
-   bgp_set_attr_ptr(&(a->eattrs), BA_COMMUNITY, flags,
++  bgp_set_attr_ptr(&e0.attrs, BA_COMMUNITY, flags,
                   int_set_add(pool, ad, BGP_COMM_LLGR_STALE));
 -  r->pflags |= BGP_REF_STALE;
 +  e0.pflags |= BGP_REF_STALE;
  
 -  return r;
 +  return &e0;
  }
  
  
@@@ -2402,28 -2394,25 +2397,28 @@@ bgp_get_route_info(rte *e, byte *buf
  
    buf += bsprintf(buf, " (%d", rt_get_preference(e));
  
 -  if (e->pflags & BGP_REF_SUPPRESSED)
 -    buf += bsprintf(buf, "-");
 +  if (!net_is_flow(e->net))
 +  {
 +    if (e->pflags & BGP_REF_SUPPRESSED)
 +      buf += bsprintf(buf, "-");
  
 -  if (rte_stale(e))
 -    buf += bsprintf(buf, "s");
 +    if (rte_stale(e))
 +      buf += bsprintf(buf, "s");
  
 -  u64 metric = bgp_total_aigp_metric(e);
 -  if (metric < BGP_AIGP_MAX)
 -  {
 -    buf += bsprintf(buf, "/%lu", metric);
 -  }
 -  else if (metric = rt_get_igp_metric(e))
 -  {
 -    if (!rte_resolvable(e))
 -      buf += bsprintf(buf, "/-");
 -    else if (metric >= IGP_METRIC_UNKNOWN)
 -      buf += bsprintf(buf, "/?");
 -    else
 -      buf += bsprintf(buf, "/%d", metric);
 +    u64 metric = bgp_total_aigp_metric(e);
 +    if (metric < BGP_AIGP_MAX)
 +    {
 +      buf += bsprintf(buf, "/%lu", metric);
 +    }
 +    else if (metric = rt_get_igp_metric(e))
 +    {
-       if (!rta_resolvable(e->attrs))
++      if (!rte_resolvable(e))
 +      buf += bsprintf(buf, "/-");
 +      else if (metric >= IGP_METRIC_UNKNOWN)
 +      buf += bsprintf(buf, "/?");
 +      else
 +      buf += bsprintf(buf, "/%d", metric);
 +    }
    }
    buf += bsprintf(buf, ") [");
  
diff --cc proto/bgp/bgp.h
index 662d9d48a3422acc4c1c7f5e0541794f053ec201,376b463f5b22cdc7c79ed9d1c6a0ac40f4cae85f..b3966bc3db9abaca690f9d6864d6fca82a9b3439
@@@ -517,9 -517,9 +517,9 @@@ struct rte_source *bgp_find_source(stru
  struct rte_source *bgp_get_source(struct bgp_proto *p, u32 path_id);
  
  static inline int
- rta_resolvable(rta *a)
 -rte_resolvable(rte *rt)
++rte_resolvable(const rte *rt)
  {
-   eattr *nhea = ea_find(a->eattrs, &ea_gen_nexthop);
+   eattr *nhea = ea_find(rt->attrs, &ea_gen_nexthop);
    struct nexthop_adata *nhad = (void *) nhea->u.ptr;
    return NEXTHOP_IS_REACHABLE(nhad) || (nhad->dest != RTD_UNREACHABLE);
  }
index 9911738d34a4cb1ccbd298229d686420a990125f,e72ec22217798486b582ea43d39f7113e0c158a3..45ee4ed2349d717d461d6165523726ee767b10e9
@@@ -1391,20 -1412,13 +1391,15 @@@ bgp_rte_update(struct bgp_parse_state *
    }
  
    /* Prepare cached route attributes */
-   if (s->cached_rta == NULL)
-   {
-     /* Workaround for rta_lookup() breaking eattrs */
-     ea_list *ea = a0->eattrs;
-     s->cached_rta = rta_lookup(a0);
-     a0->eattrs = ea;
-   }
+   if (s->cached_ea == NULL)
+     s->cached_ea = ea_lookup(a0);
  
 -  rte *e = rte_get_temp(rta_clone(s->cached_ea), s->last_src);
 +  rte e0 = {
-     .attrs = s->cached_rta,
++    .attrs = s->cached_ea,
 +    .src = s->last_src,
 +  };
  
 -  e->pflags = 0;
 -  rte_update3(&s->channel->c, n, e, s->last_src);
 +  rte_update(&s->channel->c, n, &e0, s->last_src);
  }
  
  static void
diff --cc proto/mrt/mrt.c
Simple merge
Simple merge
diff --cc proto/ospf/rt.c
index 1c76aee7888a3b4db3a3cd80139faf63390b24b4,3889d63498fec44019f1eaa42c5101cdec04379b..aedf3df6f690170f2438329b8cfd32eaa33b30a0
@@@ -2088,29 -2085,27 +2085,31 @@@ again1
          EA_LITERAL_EMBEDDED(&ea_ospf_router_id, 0, nf->n.rid);
  
        ASSERT_DIE(ARRAY_SIZE(eattrs.a) >= eattrs.l.count);
-       a0.eattrs = &eattrs.l;
  
-       rta_free(nf->old_rta);
-       nf->old_rta = rta_lookup(&a0);
 -      ea_list *a = rta_lookup(&eattrs.l);
 -      rte *e = rte_get_temp(a, p->p.main_source);
++      ea_list *eal = ea_lookup(&eattrs.l);
++      ea_free(nf->old_ea);
++      nf->old_ea = eal;
  
 -      rta_free(nf->old_ea);
 -      nf->old_ea = rta_clone(a);
 +      rte e0 = {
-         .attrs = nf->old_rta,
++        .attrs = eal,
 +        .src = p->p.main_source,
 +      };
  
+       /*
        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, nf->fn.addr, e);
+           */
 +
 +      rte_update(p->p.main_channel, nf->fn.addr, &e0, p->p.main_source);
        }
      }
-     else if (nf->old_rta)
+     else if (nf->old_ea)
      {
        /* Remove the route */
-       rta_free(nf->old_rta);
-       nf->old_rta = NULL;
+       rta_free(nf->old_ea);
+       nf->old_ea = NULL;
  
 -      rte_update(&p->p, nf->fn.addr, NULL);
 +      rte_update(p->p.main_channel, nf->fn.addr, NULL, p->p.main_source);
      }
  
      /* Remove unused rt entry, some special entries are persistent */
index 6ff6a74577569798a020229859915923c11d02fb,e3dd0885978aa85a11d1fa94fce4642aadd8446b..85bce03ddcd636d005020c278dcbc9898d29faae
@@@ -1363,14 -1363,16 +1363,14 @@@ ospf_rt_notify(struct proto *P, struct 
  
    uint ebit = m2a || !m1a;
    uint metric = ebit ? m2 : m1;
-   uint tag = ea_get_int(a->eattrs, &ea_ospf_tag, 0);
+   uint tag = ea_get_int(a, &ea_ospf_tag, 0);
  
    ip_addr fwd = IPA_NONE;
-   eattr *nhea = ea_find(a->eattrs, &ea_gen_nexthop);
+   eattr *nhea = ea_find(a, &ea_gen_nexthop);
 -  if (nhea)
 -  {
 -    struct nexthop_adata *nhad = (struct nexthop_adata *) nhea->u.ptr;
 +  struct nexthop_adata *nhad = (struct nexthop_adata *) nhea->u.ptr;
 +  if (NEXTHOP_IS_REACHABLE(nhad))
      if (use_gw_for_fwaddr(p, nhad->nh.gw, nhad->nh.iface))
        fwd = nhad->nh.gw;
 -  }
  
    /* NSSA-LSA with P-bit set must have non-zero forwarding address */
    if (oa && ipa_zero(fwd))
Simple merge
index e122d77155e5152008752742475c4a11f091c24e,bdffed3be94705144a364f137f909c30c093a2c1..99d4b737ed56e08c9426f1a9b1b07329259b345d
@@@ -56,25 -59,38 +56,20 @@@ pipe_rt_notify(struct proto *P, struct 
    if (!new && !old)
      return;
  
 -  if (dst->table->pipe_busy)
 -    {
 -      log(L_ERR "Pipe loop detected when sending %N to table %s",
 -        n->n.addr, dst->table->name);
 -      return;
 -    }
 -
    if (new)
      {
-       rta *a = alloca(rta_size(new->attrs));
-       memcpy(a, new->attrs, rta_size(new->attrs));
-       a->cached = 0;
-       ea_unset_attr(&a->eattrs, 0, &ea_gen_hostentry);
 -      src = new->src;
--
 -      ea_list *a = new->attrs;
 -      ea_unset_attr(&a, 0, &ea_gen_hostentry);
 +      rte e0 = {
-       .attrs = a,
++      .attrs = new->attrs,
 +      .src = new->src,
 +      .generation = new->generation + 1,
 +      };
  
 -      e = rte_get_temp(a, src);
 -      e->pflags = new->pflags;
++      ea_unset_attr(&e0.attrs, 0, &ea_gen_hostentry);
 -#ifdef CONFIG_BGP
 -      /* Hack to cleanup cached value */
 -      if (e->src->proto->proto == &proto_bgp)
 -      e->pflags &= ~(BGP_REF_STALE | BGP_REF_NOT_STALE);
 -#endif
 +      rte_update(dst, n, &e0, new->src);
      }
    else
 -    {
 -      e = NULL;
 -      src = old->src;
 -    }
 -
 -  src_ch->table->pipe_busy = 1;
 -  rte_update2(dst, n->n.addr, e, src);
 -  src_ch->table->pipe_busy = 0;
 +    rte_update(dst, n, NULL, old->src);
  }
  
  static int
Simple merge
diff --cc proto/rip/rip.c
index cc8b57ebb51e78d665b6e4ee9f9d7f0bcca2d01b,ce452ac70ce187992698fd6e423bbdbbaa85208e..d6edac14873497df07a60f140857e78caa8d1cd9
@@@ -228,18 -227,18 +227,18 @@@ rip_announce_rte(struct rip_proto *p, s
        .ad = { .length = sizeof(struct rip_iface_adata) - sizeof(struct adata) },
        .iface = rt_from,
      };
-     ea_set_attr(&a0.eattrs,
+     ea_set_attr(&ea,
        EA_LITERAL_DIRECT_ADATA(&ea_rip_from, 0, &riad.ad));
  
 -    rte *e = rte_get_temp(rta_lookup(ea), p->p.main_source);
 +    rte e0 = {
-       .attrs = &a0,
++      .attrs = ea,
 +      .src = p->p.main_source,
 +    };
  
 -    rte_update(&p->p, en->n.addr, e);
 +    rte_update(p->p.main_channel, en->n.addr, &e0, p->p.main_source);
    }
    else
 -  {
 -    /* Withdraw */
 -    rte_update(&p->p, en->n.addr, NULL);
 -  }
 +    rte_update(p->p.main_channel, en->n.addr, NULL, p->p.main_source);
  }
  
  /**
@@@ -1126,9 -1125,9 +1125,9 @@@ rip_rte_better(struct rte *new, struct 
  }
  
  static u32
 -rip_rte_igp_metric(struct rte *rt)
 +rip_rte_igp_metric(const rte *rt)
  {
-   return ea_get_int(rt->attrs->eattrs, &ea_rip_metric, IGP_METRIC_UNKNOWN);
+   return ea_get_int(rt->attrs, &ea_rip_metric, IGP_METRIC_UNKNOWN);
  }
  
  static void
index c8b0ff67d5cf287c088eab216eb026efc02485ad,2d36f1fb005530e20a6860dbd8c71c75163f92d7..56615e36ba5518443ed7837981748e6595fa5005
@@@ -120,14 -120,15 +120,13 @@@ rpki_table_add_roa(struct rpki_cache *c
  {
    struct rpki_proto *p = cache->p;
  
-   rta a0 = {};
+   ea_list *ea = NULL;
+   ea_set_attr_u32(&ea, &ea_gen_preference, 0, channel->preference);
+   ea_set_attr_u32(&ea, &ea_gen_source, 0, RTS_RPKI);
  
-   ea_set_attr_u32(&a0.eattrs, &ea_gen_preference, 0, channel->preference);
-   ea_set_attr_u32(&a0.eattrs, &ea_gen_source, 0, RTS_RPKI);
-   rte e0 = { .attrs = &a0, .src = p->p.main_source, };
 -  rte *e = rte_get_temp(rta_lookup(ea), p->p.main_source);
++  rte e0 = { .attrs = ea, .src = p->p.main_source, };
  
 -  e->pflags = 0;
 -
 -  rte_update2(channel, &pfxr->n, e, e->src);
 +  rte_update(channel, &pfxr->n, &e0, p->p.main_source);
  }
  
  void
index 6369fea53bfaa476a4620a09a444af0acf26c62b,29285d371efce1bc7aa70046527b361d5dc224fd..d1d5b92babca8d1b2a518853fea51ee378e310c3
@@@ -114,13 -114,24 +114,13 @@@ static_announce_rte(struct static_prot
      return;
  
    /* We skip rta_lookup() here */
-   rte e0 = { .attrs = a, .src = src, .net = r->net, }, *e = &e0;
 -  rte *e = rte_get_temp(ea, src);
 -  e->pflags = 0;
++  rte e0 = { .attrs = ea, .src = src, .net = r->net, }, *e = &e0;
  
 +  /* Evaluate the filter */
    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);
 -
 -    /* Remove the temporary node */
 -    e->net = NULL;
 -  }
 +    f_eval_rte(r->cmds, e);
  
 -  rte_update2(p->p.main_channel, r->net, e, src);
 +  rte_update(p->p.main_channel, r->net, e, src);
    r->state = SRS_CLEAN;
    return;
  
index a4172a6d4c7064cabc79df548ba93460d8575d34,4cfd7c800d2438aa54f9febc0f6715b52389c60d..d802d3e072f1f09c8fa439cd072d429d01bff5a4
@@@ -110,8 -110,8 +110,8 @@@ struct nl_parse_stat
    int scan;
    int merge;
  
 -  net *net;
 +  net_addr *net;
-   rta *attrs;
+   ea_list *attrs;
    struct krt_proto *proto;
    s8 new;
    s8 krt_src;
@@@ -1437,11 -1437,11 +1437,10 @@@ nh_bufsize(struct nexthop_adata *nhad
  }
  
  static int
 -nl_send_route(struct krt_proto *p, rte *e, int op, int dest, struct nexthop_adata *nh)
 +nl_send_route(struct krt_proto *p, const rte *e, int op, int dest, struct nexthop_adata *nh)
  {
    eattr *ea;
-   rta *a = e->attrs;
-   ea_list *eattrs = a->eattrs;
 -  net *net = e->net;
+   ea_list *eattrs = e->attrs;
  
    int bufsize = 128 + KRT_METRICS_MAX*8 + (nh ? nh_bufsize(nh) : 0);
    u32 priority = 0;
@@@ -1708,25 -1707,23 +1706,25 @@@ nl_mergable_route(struct nl_parse_stat
  static void
  nl_announce_route(struct nl_parse_state *s)
  {
 -  rte *e = rte_get_temp(s->attrs, s->proto->p.main_source);
 -  e->net = s->net;
 +  rte e0 = {
 +    .attrs = s->attrs,
 +    .net = s->net,
 +  };
  
    EA_LOCAL_LIST(2) ea = {
-     .l = { .count = 2, .next = e0.attrs->eattrs },
 -    .l = { .count = 2, .next = e->attrs },
++    .l = { .count = 2, .next = e0.attrs },
      .a = {
        EA_LITERAL_EMBEDDED(&ea_krt_source, 0, s->krt_proto),
        EA_LITERAL_EMBEDDED(&ea_krt_metric, 0, s->krt_metric),
      },
    };
  
-   e0.attrs->eattrs = &ea.l;
 -  e->attrs = &ea.l;
++  e0.attrs = &ea.l;
  
    if (s->scan)
 -    krt_got_route(s->proto, e, s->krt_src);
 +    krt_got_route(s->proto, &e0, s->krt_src);
    else
 -    krt_got_route_async(s->proto, e, s->new, s->krt_src);
 +    krt_got_route_async(s->proto, &e0, s->new, s->krt_src);
  
    s->net = NULL;
    s->attrs = NULL;
@@@ -2041,12 -2040,10 +2039,12 @@@ nl_parse_route(struct nl_parse_state *s
    if (!s->net)
    {
      /* Store the new route */
 -    s->net = net;
 +    s->net = lp_alloc(s->pool, net->length);
 +    net_copy(s->net, net);
 +
      s->attrs = ra;
  
-     ea_set_attr_data(&ra->eattrs, &ea_gen_nexthop, 0,
+     ea_set_attr_data(&ra, &ea_gen_nexthop, 0,
        nhad.ad.data, nhad.ad.length);
  
      s->proto = p;
index 6e55d8f5e23fbd08362adb5cdc42a68599a70e19,3edbdfa2f2549801901b1e18cd2f93ced7fce7a0..a37d3186664de92d8c3587ba7a5b8e1833250e88
@@@ -305,12 -305,9 +305,12 @@@ krt_uptodate(rte *a, rte *b
  static void
  krt_learn_announce_update(struct krt_proto *p, rte *e)
  {
 -  net *n = e->net;
 -  rte *ee = rte_get_temp(ea_clone(e->attrs), p->p.main_source);
 -  rte_update(&p->p, n->n.addr, ee);
 +  rte e0 = {
-     .attrs = rta_clone(e->attrs),
++    .attrs = ea_clone(e->attrs),
 +    .src = p->p.main_source,
 +  };
 +
 +  rte_update(p->p.main_channel, e->net, &e0, p->p.main_source);
  }
  
  static void
@@@ -435,13 -432,13 +435,11 @@@ 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);
-   ea_set_attr_u32(&e->attrs->eattrs, &ea_gen_preference, 0, p->p.main_channel->preference);
 -  ea_list *ea = e->attrs;
 -  ea_set_attr_u32(&ea, &ea_gen_preference, 0, p->p.main_channel->preference);
 -  e->attrs = rta_lookup(ea);
++  ea_set_attr_u32(&e->attrs, &ea_gen_preference, 0, p->p.main_channel->preference);
 +  struct rte_storage *ee = rte_store(e, n, p->krt_table);
  
    old_best = n->routes;
    for(gg=&n->routes; g = *gg; gg = &g->next)
index 8fdad4e6f249363840d776590de08e5fe2d2cecf,8fdad4e6f249363840d776590de08e5fe2d2cecf..fd4934d999e72e81f85a759405bd9f059d1ec11e
@@@ -56,7 -56,7 +56,7 @@@ async_dump(void
    // XXXX tm_dump_all();
    if_dump_all();
    neigh_dump_all();
--  rta_dump_all();
++  ea_dump_all();
    rt_dump_all();
    protos_dump_all();