]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Merged export moved after out_table
authorMaria Matejka <mq@ucw.cz>
Wed, 24 Feb 2021 00:51:00 +0000 (01:51 +0100)
committerMaria Matejka <mq@ucw.cz>
Sat, 20 Mar 2021 23:28:17 +0000 (00:28 +0100)
nest/protocol.h
nest/route.h
nest/rt-show.c
nest/rt-table.c
proto/bgp/attrs.c
proto/bgp/bgp.h
proto/static/static.c
sysdep/unix/krt.c

index fd7d532dd040bf4b09d26de8654bcdb7f26c7dc1..2d180aa0ae42ae3bb8ed8b583c5746f72e121aba 100644 (file)
@@ -230,7 +230,7 @@ struct proto {
 
   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 *);
+  int (*rte_mergable)(struct rte, struct rte);
   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 *);
@@ -491,7 +491,7 @@ struct channel_config {
   u8 ra_mode;                          /* Mode of received route advertisements (RA_*) */
   u16 preference;                      /* Default route preference */
   u32 debug;                           /* Debugging flags (D_*) */
-  u8 merge_limit;                      /* Maximal number of nexthops for RA_MERGED */
+  u8 merge_limit;                      /* Maximal number of nexthops for merging */
   u8 in_keep_filtered;                 /* Routes rejected in import filter are kept */
   u8 rpki_reload;                      /* RPKI changes trigger channel reload */
 };
@@ -523,7 +523,7 @@ struct channel {
   u8 ra_mode;                          /* Mode of received route advertisements (RA_*) */
   u16 preference;                      /* Default route preference */
   u32 debug;                           /* Debugging flags (D_*) */
-  u8 merge_limit;                      /* Maximal number of nexthops for RA_MERGED */
+  u8 merge_limit;                      /* Maximal number of nexthops for merging */
   u8 in_keep_filtered;                 /* Routes rejected in import filter are kept */
   u8 disabled;
   u8 stale;                            /* Used in reconfiguration */
index d89b2f59a0961a21a2c378481304e19d60032b67..92b9cb3a7b6d827f899110736c31329340861611 100644 (file)
@@ -231,6 +231,8 @@ typedef struct rte {
   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 */
+  byte flags;                          /* Flags (REF_...) */
+  byte pflags;                         /* Protocol-specific flags */
 } rte;
 
 struct rte_storage {
@@ -251,6 +253,7 @@ struct rte_storage {
 #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 */
+#define REF_E_MERGED   32              /* Route has been merged on export */
 
 /* Route is valid for propagation (may depend on other flags in the future), accepts NULL */
 static inline int rte_is_valid(const struct rte_storage *r) { return r && !(r->flags & REF_FILTERED); }
@@ -288,7 +291,6 @@ struct rte_export {
 #define RA_OPTIMAL     1               /* Announcement of optimal route change */
 #define RA_ACCEPTED    2               /* Announcement of first accepted route */
 #define RA_ANY         3               /* Announcement of any route change */
-#define RA_MERGED      4               /* Announcement of optimal route merged with next ones */
 
 /* Return value of preexport() callback */
 #define RIC_ACCEPT     1               /* Accepted by protocol */
@@ -348,7 +350,7 @@ static inline net *net_get(rtable *tab, const net_addr *addr) { return (net *) f
 void *net_route(rtable *tab, const net_addr *n);
 int net_roa_check(rtable *tab, const net_addr *n, u32 asn);
 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);
+rte rte_get_merged(net *n, linpool *p, u32 limit);
 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);
@@ -358,7 +360,15 @@ 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 }; }
+{
+  return r ? (rte) {
+    .attrs = r->attrs,
+    .net = r->net->n.addr,
+    .src = r->src,
+    .flags = r->flags,
+    .pflags = r->pflags
+  } : (rte) {};
+}
 void rt_dump(rtable *);
 void rt_dump_all(void);
 int rt_feed_channel(struct channel *c);
@@ -367,10 +377,9 @@ 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, rte *new, rte *old, struct rte_storage **old_stored, u32 id, int refeed);
 struct rtable_config *rt_new_table(struct symbol *s, uint addr_type);
 void rt_out_sync_start(struct channel *c);
-_Bool rt_out_sync_mark(struct channel *c, struct rte_export *e);
+_Bool rt_out_sync_mark(struct channel *c, struct rte_export *e, linpool *p);
 void rt_out_sync_finish(struct channel *c);
 void rt_out_flush(struct channel *c);
 
index 1b56d5b997ac78468f0c796ef591522626da2367..846e63f4132250e627d777ab05b58d61e87864fb 100644 (file)
@@ -133,25 +133,26 @@ 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, er->id))
+         if (ec->merge_limit > 1)
+         {
+           pass = 1;
+           e = rte_get_merged(n, c->show_pool, ec->merge_limit);
+           if (!e.attrs)
+             goto skip;
+         }
+         else if (!bmap_test(&ec->export_map, er->id))
            goto skip;
 
+
          // if (ec->ra_mode != RA_ANY)
          //   pass = 1;
         }
-      else if ((d->export_mode == RSEM_EXPORT) && (ec->ra_mode == RA_MERGED))
-       {
-         /* Special case for merged export */
-         pass = 1;
-         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(ec, &e) : 0;
 
-         if (ec->ra_mode == RA_OPTIMAL || ec->ra_mode == RA_MERGED)
+         if (ec->ra_mode == RA_OPTIMAL)
            pass = 1;
 
          if (ic < 0)
@@ -230,7 +231,15 @@ rt_show_cont(struct cli *c)
 
   if (!d->table_open)
   {
-    FIB_ITERATE_INIT(&d->fit, &d->tab->table->fib);
+    if (d->export_mode == RSEM_EXPORTED &&
+       d->tab->export_channel &&
+       d->tab->export_channel->out_table)
+      fib = &d->tab->export_channel->out_table->fib;
+    else
+      fib = &d->tab->table->fib;
+
+    FIB_ITERATE_INIT(&d->fit, fib);
+
     d->table_open = 1;
     d->table_counter++;
     d->kernel = rt_show_get_kernel(d);
@@ -410,10 +419,12 @@ rt_show(struct rt_show_data *d)
       d->tab = tab;
       d->kernel = rt_show_get_kernel(d);
 
+      struct rtable *rt = (d->export_mode == RSEM_EXPORTED) ? tab->export_channel->out_table : tab->table;
+
       if (d->show_for)
-       n = net_route(tab->table, d->addr);
+       n = net_route(rt, d->addr);
       else
-       n = net_find(tab->table, d->addr);
+       n = net_find(rt, d->addr);
 
       if (n)
        rt_show_net(this_cli, n, d);
index 3ef052ccee44f0fbf8cfbe456f72cb4a04cca7e2..e0898bc6b4aecaf1c67d52a4f8d0d2fbe0db0349 100644 (file)
@@ -68,13 +68,15 @@ static linpool *rte_update_pool;
 
 list routing_tables;
 
+struct rte_export_internal;
+
 static void rt_free_hostcache(rtable *tab);
 static void rt_notify_hostcache(rtable *tab, net *net);
 static void rt_update_hostcache(rtable *tab);
 static void rt_next_hop_update(rtable *tab);
 static inline void rt_prune_table(rtable *tab);
 static inline void rt_schedule_notify(rtable *tab);
-
+static int rte_update_out(struct channel *c, struct rte_export_internal *e);
 
 /* Like fib_route(), but skips empty net entries */
 static inline void *
@@ -392,20 +394,20 @@ rte_better(struct rte_storage *new, struct rte_storage *old)
 }
 
 static int
-rte_mergable(struct rte_storage *pri, struct rte_storage *sec)
+rte_mergable(struct rte pri, struct rte sec)
 {
-  int (*mergable)(struct rte_storage *, struct rte_storage *);
+  int (*mergable)(struct rte, struct rte);
 
-  if (!rte_is_valid(pri) || !rte_is_valid(sec))
-    return 0;
+  ASSERT_DIE(pri.attrs);
+  ASSERT_DIE(sec.attrs);
 
-  if (pri->attrs->pref != sec->attrs->pref)
+  if (pri.attrs->pref != sec.attrs->pref)
     return 0;
 
-  if (pri->src->proto->proto != sec->src->proto->proto)
+  if (pri.src->proto->proto != sec.src->proto->proto)
     return 0;
 
-  if (mergable = pri->src->proto->rte_mergable)
+  if (mergable = pri.src->proto->rte_mergable)
     return mergable(pri, sec);
 
   return 0;
@@ -617,93 +619,33 @@ nexthop_merge_rta(struct nexthop *nhs, rta *a, linpool *pool, int max)
   return nexthop_merge(nhs, &(a->nh), 1, 0, max, pool);
 }
 
-_Bool
-rt_export_merged(struct channel *c, net *net, rte *best, linpool *pool, int silent)
+rte
+rte_get_merged(net *net, linpool *pool, uint limit)
 {
-  // struct proto *p = c->proto;
-  struct nexthop *nhs = NULL;
-  struct rte_storage *best0 = net->routes;
+  if (!net->routes || !(net->routes->flags & REF_E_MERGED))
+    return (rte) {};
 
-  if (!rte_is_valid(best0))
-    return 0;
-
-  *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 (!rte_is_reachable(best))
-    /* Unreachable routes can't be merged */
-    return 1;
+  struct nexthop *nhs = NULL;
+  struct rte e = rte_copy(net->routes);
 
-  for (struct rte_storage *rt0 = best0->next; rt0; rt0 = rt0->next)
+  for (struct rte_storage *rt0 = net->routes->next; rt0; rt0 = rt0->next)
   {
-    if (!rte_mergable(best0, rt0))
+    if (!(rt0->flags & REF_E_MERGED))
       continue;
 
-    struct rte tmp = rte_copy(rt0);
-    if (export_filter_(c, &tmp, rt0->id, pool, 1) >= EFR_FILTER_REJECT)
-      continue;
-
-    if (!rte_is_reachable(&tmp))
-      continue;
-
-    nhs = nexthop_merge_rta(nhs, tmp.attrs, pool, c->merge_limit);
+    nhs = nexthop_merge_rta(nhs, rte_copy(rt0).attrs, pool, limit);
   }
 
   if (nhs)
   {
-    nhs = nexthop_merge_rta(nhs, best->attrs, pool, c->merge_limit);
-
+    nhs = nexthop_merge_rta(nhs, e.attrs, pool, limit);
     if (nhs->next)
-      best->attrs = rta_cow(best->attrs, pool);
-
-    nexthop_link(best->attrs, nhs);
-  }
-
-  return 1;
-}
-
-
-USE_RESULT static _Bool
-rt_notify_merged(struct channel *c, struct rte_export_internal *e)
-{
-  /* We assume that all rte arguments are either NULL or rte_is_valid() */
+      e.attrs = rta_cow(e.attrs, pool);
 
-  /* This check should be done by the caller */
-  if (!e->new_best && !e->old_best)
-    return 0;
-
-  /* Check whether the change is relevant to the merged route */
-  if ((e->new_best == e->old_best) &&
-      (e->new != e->old) &&
-      !rte_mergable(e->new_best, e->new) &&
-      !rte_mergable(e->old_best, e->old))
-    return 0;
-
-  if (e->new_best)
-    c->stats.exp_updates_received++;
-  else
-    c->stats.exp_withdraws_received++;
-
-  struct rte_export *ep = &e->pub;
-
-  /* Prepare new merged route */
-  if (e->new_best)
-  {
-    ep->new_id = e->new_best->id;
-    if (!rt_export_merged(c, e->net, &ep->new, rte_update_pool, 0))
-      ep->new.attrs = NULL;
+    nexthop_link(e.attrs, nhs);
   }
 
-  /* Check old merged route */
-  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);
-  }
-
-  return RTE_EXPORT_IS_OK(ep);
+  return e;
 }
 
 static struct rte_export *
@@ -729,10 +671,6 @@ rte_export_obtain(struct channel *c, struct rte_export_internal *e)
       accepted = rt_notify_accepted(c, e);
       break;
 
-    case RA_MERGED:
-      accepted = rt_notify_merged(c, e);
-      break;
-
     default:
       bug("Strange channel route announcement mode");
   }
@@ -774,7 +712,7 @@ rte_export_store(struct channel *c, struct rte_export_internal *e)
   /* Apply export table */
   if (c->out_table)
   {
-    if (!rte_update_out(c, &(e->pub.new), &(e->pub.old), &(e->old_stored), e->pub.new_id, e->refeed))
+    if (!rte_update_out(c, e))
       return 0;
   }
   else if (c->out_filter != FILTER_ACCEPT)
@@ -938,7 +876,7 @@ rte_announce(rtable *tab, uint type, net *net, struct rte_storage *new, struct r
       .new = new, .old = old,
       .new_best = new_best, .old_best = old_best,
     };
-    
+
     rte_export(c, &rei);
   }
 }
@@ -2477,106 +2415,164 @@ rt_prune_sync(rtable *t, int all)
  *     Export table
  */
 
-int
-rte_update_out(struct channel *c, rte *new, rte *old, struct rte_storage **old_stored, u32 id, int refeed)
+static int
+rte_update_out(struct channel *c, struct rte_export_internal *e)
 {
   struct rtable *tab = c->out_table;
   struct rte_storage **pos;
   net *net;
 
+  rte *new = &e->pub.new;
+  rte *old = &e->pub.old;
+
+  _Bool new_is_best = (e->new == e->new_best);
+  _Bool old_is_best = (e->old == e->old_best);
+  _Bool merging = c->merge_limit > 1;
+
   if (new->attrs)
   {
-    net = net_get(tab, new->net);
-
     if (!rta_is_cached(new->attrs))
       new->attrs = rta_lookup(new->attrs);
-  }
-  else
-  {
-    net = net_find(tab, old->net);
 
-    if (!net)
-      goto drop_withdraw;
+    net = net_get(tab, new->net);
   }
+  else if (!(net = net_find(tab, old->net)))
+    return 0;
 
-  /* Find the old rte */
+  /* Find the old rte if exists */
   for (pos = &net->routes; *pos; pos = &(*pos)->next)
-    if ((c->ra_mode != RA_ANY) || ((*pos)->src == old->src))
-    {
-      if (new && rte_same(*pos, new, 0))
-      {
-       /* REF_STALE / REF_DISCARD not used in export table */
-       /*
-       if (old->flags & (REF_STALE | REF_DISCARD | REF_MODIFY))
-       {
-         old->flags &= ~(REF_STALE | REF_DISCARD | REF_MODIFY);
-         return 1;
-       }
-       */
+    if ((c->ra_mode != RA_ANY) || ((*pos)->src == new->src))
+      break;
 
-       goto drop_update;
-      }
+  /* Found exactly the same route, no update needed */
+  if ((*pos) && new->attrs && rte_same(*pos, new, 0))
+    return 0;
 
-      /* Keep the old rte */
-      *old_stored = *pos;
-      *old = rte_copy(*pos);
+  /* Are the new/old routes mergable with the old_best? */
+  struct rte_storage *best_stored =
+    (!merging || net->routes && (net->routes->flags & REF_E_MERGED))
+    ? net->routes : NULL;
+
+  _Bool best_changed = new_is_best || old_is_best;
+
+  _Bool new_mergable_with_best_stored =
+    !best_changed &&
+    new->attrs && best_stored &&
+    rte_mergable(rte_copy(best_stored), *new);
+
+  _Bool gen_old = merging ?
+    best_changed ||
+    (*pos) && ((*pos)->flags & REF_E_MERGED) ||
+    new_mergable_with_best_stored
+    : 0;
+
+  if (gen_old)
+    /* Reconstruct the old merged rte completely. It's gonna change */
+    *old = rte_get_merged(net, rte_update_pool, c->merge_limit);
+  else if (!merging && *pos)
+    /* A plain old route */
+    *old = rte_copy(*pos);
+
+  /* Keep the old_stored but remove it from the list */
+  tab->rt_count--;
+  e->old_stored = *pos;
+  if (*pos)
+    *pos = (*pos)->next;
+
+  /* Idempotent withdraw */
+  if (!e->old_stored && !new->attrs)
+    return 0;
 
-      /* Remove the old rte from the list */
-      *pos = (*pos)->next;
-      tab->rt_count--;
+  /* Best route must be inserted to the beginning */
+  if (merging)
+  {
+    if (new_is_best)
+      pos = &net->routes;
+    else if (old_is_best && (c->ra_mode == RA_ANY))
+    {
+      /* We have to find the new best route and put it first */
+      for (pos = &net->routes; *pos; pos = &(*pos)->next)
+       if ((c->ra_mode != RA_ANY) || ((*pos)->src == e->new_best->src))
+         break;
 
-      break;
+      if (best_stored = *pos)
+      {
+       *pos = best_stored->next;
+       best_stored->next = net->routes;
+       net->routes = best_stored;
+      }
     }
+  }
 
-  if (!new->attrs)
+  /* Store the new rte */
+  if (new->attrs)
   {
-    if (!*old_stored)
-      goto drop_withdraw;
-
-    return 1;
+    struct rte_storage *es = rte_store(new, net);
+    es->sender = c;
+    es->lastmod = current_time();
+    es->id = e->new->id;
+    es->next = *pos;
+    *pos = es;
+    tab->rt_count++;
+
+    if (new_is_best)
+      best_stored = es;
+    else if (!best_changed && new_mergable_with_best_stored)
+      es->flags |= REF_E_MERGED;
   }
 
-  /* Insert the new rte */
-  struct rte_storage *e = rte_store(new, net);
-  e->sender = c;
-  e->lastmod = current_time();
-  e->id = id;
-  e->next = *pos;
-  *pos = e;
-  tab->rt_count++;
-  return 1;
+  if (merging)
+  {
+    if (best_stored)
+      best_stored->flags |= REF_E_MERGED;
 
-drop_update:
-  return refeed;
+    ASSERT_DIE(best_stored == net->routes);
 
-drop_withdraw:
-  return 0;
+    /* Recalculate REF_E_MERGED for other routes */
+    if (best_changed && net->routes)
+      for (struct rte_storage *rt0 = best_stored ? net->routes->next : net->routes; rt0; rt0 = rt0->next)
+       if (best_stored && rte_mergable(rte_copy(best_stored), rte_copy(rt0)))
+         rt0->flags |= REF_E_MERGED;
+       else
+         rt0->flags &= ~REF_E_MERGED;
+
+    if (!gen_old)
+      /* Not mergable before nor after, no update generated at all. */
+      return 0;
+
+    *new = rte_get_merged(net, rte_update_pool, c->merge_limit);
+  }
+
+  return 1;
 }
 
 void
 rt_out_sync_start(struct channel *c)
 {
   ASSERT_DIE(c->out_table);
-  ASSERT_DIE(c->ra_mode != RA_ANY);
+  ASSERT_DIE((c->ra_mode != RA_ANY) || (c->merge_limit > 1));
   bmap_reset(&c->out_seen_map, 1024);
 }
 
 _Bool
-rt_out_sync_mark(struct channel *c, struct rte_export *e)
+rt_out_sync_mark(struct channel *c, struct rte_export *e, linpool *p)
 {
   ASSERT_DIE(c->out_table);
-  ASSERT_DIE(c->ra_mode != RA_ANY);
+  ASSERT_DIE((c->ra_mode != RA_ANY) || (c->merge_limit > 1));
 
   net *n = net_find(c->out_table, e->old.net);
   if (!n || !n->routes)
     return 1;
 
-  e->new = rte_copy(n->routes);
   e->new_id = n->routes->id;
-
   if (bmap_test(&c->out_seen_map, n->routes->id))
     return 0;
 
+  if (c->merge_limit > 1)
+    e->new = rte_get_merged(n, p, c->merge_limit);
+  else
+    e->new = rte_copy(n->routes);
+
   bmap_set(&c->out_seen_map, n->routes->id);
   return 1;
 }
@@ -2585,7 +2581,7 @@ void
 rt_out_sync_finish(struct channel *c)
 {
   ASSERT_DIE(c->out_table);
-  ASSERT_DIE(c->ra_mode != RA_ANY);
+  ASSERT_DIE((c->ra_mode != RA_ANY) || (c->merge_limit > 1));
 
   FIB_WALK(&c->out_table->fib, net, n)
   {
@@ -2594,12 +2590,14 @@ rt_out_sync_finish(struct channel *c)
 
     if (!bmap_test(&c->out_seen_map, n->routes->id))
     {
+      rte_update_lock();
       struct rte_export ex = {
        .new_id = n->routes->id,
-       .new = rte_copy(n->routes),
+       .new = ((c->merge_limit > 1) ? rte_get_merged(n, rte_update_pool, c->merge_limit) : rte_copy(n->routes)),
       };
 
       c->proto->rt_notify(c, &ex);
+      rte_update_unlock();
     }
   }
   FIB_WALK_END;
@@ -2610,19 +2608,21 @@ void
 rt_out_flush(struct channel *c)
 {
   ASSERT_DIE(c->out_table);
-  ASSERT_DIE(c->ra_mode != RA_ANY);
+  ASSERT_DIE((c->ra_mode != RA_ANY) || (c->merge_limit > 1));
 
   FIB_WALK(&c->out_table->fib, net, n)
   {
     if (!n->routes)
       continue;
 
+    rte_update_lock();
     struct rte_export ex = {
       .old_id = n->routes->id,
-      .old = rte_copy(n->routes),
+      .old = ((c->merge_limit > 1) ? rte_get_merged(n, rte_update_pool, c->merge_limit) : rte_copy(n->routes)),
     };
 
     c->proto->rt_notify(c, &ex);
+    rte_update_unlock();
   }
   FIB_WALK_END;
 }
index be16c81e25c5636eeeb1d3c52f1fec0c5f215ee4..a3dd08f34c25b3da44bbabcb959ef00d478adcbe 100644 (file)
@@ -1869,19 +1869,27 @@ bgp_rt_notify(struct channel *C, struct rte_export *e)
 
 
 static inline u32
-bgp_get_neighbor(struct rte_storage *r)
+bgp_get_neighbor(const struct rte r)
 {
-  eattr *e = ea_find(r->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_AS_PATH));
+  eattr *e = ea_find(r.attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_AS_PATH));
   u32 as;
 
   if (e && as_path_get_first_regular(e->u.ptr, &as))
     return as;
 
   /* If AS_PATH is not defined, we treat rte as locally originated */
-  struct bgp_proto *p = (void *) r->src->proto;
+  struct bgp_proto *p = (void *) r.src->proto;
   return p->cf->confederation ?: p->local_as;
 }
 
+static inline int
+rte_stale_get(rta *r)
+{
+  /* If staleness is unknown, compute and cache it */
+  eattr *a = ea_find(r->eattrs, EA_CODE(PROTOCOL_BGP, BA_COMMUNITY));
+  return (a && int_set_contains(a->u.ptr, BGP_COMM_LLGR_STALE));
+}
+
 static inline int
 rte_stale(struct rte_storage *r)
 {
@@ -1891,9 +1899,7 @@ rte_stale(struct rte_storage *r)
   if (r->pflags & BGP_REF_NOT_STALE)
     return 0;
 
-  /* If staleness is unknown, compute and cache it */
-  eattr *a = ea_find(r->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_COMMUNITY));
-  if (a && int_set_contains(a->u.ptr, BGP_COMM_LLGR_STALE))
+  if (rte_stale_get(r->attrs))
   {
     r->pflags |= BGP_REF_STALE;
     return 1;
@@ -1990,7 +1996,7 @@ bgp_rte_better(struct rte_storage *new, struct rte_storage *old)
    * probably not a big issue.
    */
   if (new_bgp->cf->med_metric || old_bgp->cf->med_metric ||
-      (bgp_get_neighbor(new) == bgp_get_neighbor(old)))
+      (bgp_get_neighbor(rte_copy(new)) == bgp_get_neighbor(rte_copy(old))))
   {
     x = ea_find(new->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_MULTI_EXIT_DISC));
     y = ea_find(old->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_MULTI_EXIT_DISC));
@@ -2049,9 +2055,8 @@ bgp_rte_better(struct rte_storage *new, struct rte_storage *old)
   return ipa_compare(new_bgp->remote_ip, old_bgp->remote_ip) < 0;
 }
 
-
-int
-bgp_rte_mergable(struct rte_storage *pri, struct rte_storage *sec)
+static int
+bgp_rte_mergable_internal(struct rte *pri, struct rte *sec)
 {
   struct bgp_proto *pri_bgp = (struct bgp_proto *) pri->src->proto;
   struct bgp_proto *sec_bgp = (struct bgp_proto *) sec->src->proto;
@@ -2059,14 +2064,17 @@ bgp_rte_mergable(struct rte_storage *pri, struct rte_storage *sec)
   u32 p, s;
 
   /* Skip suppressed routes (see bgp_rte_recalculate()) */
-  /* LLGR draft - depreference stale routes */
-  if (pri->pflags != sec->pflags)
+  if ((pri->pflags ^ sec->pflags) & BGP_REF_SUPPRESSED)
     return 0;
 
   /* RFC 4271 9.1.2.1. Route resolvability test */
   if (rta_resolvable(pri->attrs) != rta_resolvable(sec->attrs))
     return 0;
 
+  /* LLGR draft - depreference stale routes */
+  if (rte_stale_get(pri->attrs) != rte_stale_get(sec->attrs))
+    return 0;
+
   /* Start with local preferences */
   x = ea_find(pri->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_LOCAL_PREF));
   y = ea_find(sec->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_LOCAL_PREF));
@@ -2100,7 +2108,7 @@ bgp_rte_mergable(struct rte_storage *pri, struct rte_storage *sec)
 
   /* RFC 4271 9.1.2.2. c) Compare MED's */
   if (pri_bgp->cf->med_metric || sec_bgp->cf->med_metric ||
-      (bgp_get_neighbor(pri) == bgp_get_neighbor(sec)))
+      (bgp_get_neighbor(*pri) == bgp_get_neighbor(*sec)))
   {
     x = ea_find(pri->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_MULTI_EXIT_DISC));
     y = ea_find(sec->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_MULTI_EXIT_DISC));
@@ -2125,11 +2133,17 @@ bgp_rte_mergable(struct rte_storage *pri, struct rte_storage *sec)
   return 1;
 }
 
+int
+bgp_rte_mergable(struct rte pri, struct rte sec)
+{
+  return bgp_rte_mergable_internal(&pri, &sec);
+}
+
 
 static inline int
 same_group(struct rte_storage *r, u32 lpref, u32 lasn)
 {
-  return (r->attrs->pref == lpref) && (bgp_get_neighbor(r) == lasn);
+  return (r->attrs->pref == lpref) && (bgp_get_neighbor(rte_copy(r)) == lasn);
 }
 
 static inline int
@@ -2145,7 +2159,7 @@ bgp_rte_recalculate(rtable *table, net *net, struct rte_storage *new, struct rte
   struct rte_storage *r, *s;
   struct rte_storage *key = new ? new : old;
   u32 lpref = key->attrs->pref;
-  u32 lasn = bgp_get_neighbor(key);
+  u32 lasn = bgp_get_neighbor(rte_copy(key));
   int old_suppressed = old ? !!(old->pflags & BGP_REF_SUPPRESSED) : 0;
 
   /*
@@ -2224,13 +2238,13 @@ bgp_rte_recalculate(rtable *table, net *net, struct rte_storage *new, struct rte
     return 0;
 
   /* Found if new is mergable with best-in-group */
-  if (new && (new != r) && bgp_rte_mergable(r, new))
+  if (new && (new != r) && bgp_rte_mergable(rte_copy(r), rte_copy(new)))
     new->pflags &= ~BGP_REF_SUPPRESSED;
 
   /* Found all existing routes mergable with best-in-group */
   for (s=net->routes; rte_is_valid(s); s=s->next)
     if (use_deterministic_med(s) && same_group(s, lpref, lasn))
-      if ((s != r) && bgp_rte_mergable(r, s))
+      if ((s != r) && bgp_rte_mergable(rte_copy(r), rte_copy(s)))
        s->pflags &= ~BGP_REF_SUPPRESSED;
 
   /* Found best-in-group */
index 520c55f691a9d04f3f37104e6106682a12783b42..352b19ae10494789eb434f1562b2b8a494ea276e 100644 (file)
@@ -583,7 +583,7 @@ 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_storage *, struct rte_storage *);
-int bgp_rte_mergable(struct rte_storage *pri, struct rte_storage *sec);
+int bgp_rte_mergable(struct rte pri, struct rte 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);
index 20edcf1d5475f30cb233ea52f8b296c6b16d2d14..9c5886d5558cb2d2c32c6af528c3cd98721c46ce 100644 (file)
@@ -410,10 +410,10 @@ static_rte_better(struct rte_storage *new, struct rte_storage *old)
 }
 
 static int
-static_rte_mergable(struct rte_storage *pri, struct rte_storage *sec)
+static_rte_mergable(struct rte pri, struct rte 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);
+  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);
   return a == b;
 }
 
index 38d874441a37aa7f7fefae1487cc79a7ff262e71..e3421ce429d05d001fc61344a851928af09990a9 100644 (file)
  */
 
 pool *krt_pool;
+static linpool *krt_filter_lp;
 static list krt_proto_list;
 
 void
 krt_io_init(void)
 {
   krt_pool = rp_new(&root_pool, "Kernel Syncer");
+  krt_filter_lp = lp_new_default(krt_pool);
   init_list(&krt_proto_list);
   krt_sys_io_init();
 }
@@ -558,7 +560,6 @@ krt_same_dest(rte *k, rte *e)
 void
 krt_got_route(struct krt_proto *p, rte *e, s8 src)
 {
-
   struct rte_export ex = {
     .old = *e,
   };
@@ -587,7 +588,7 @@ krt_got_route(struct krt_proto *p, rte *e, s8 src)
   if (!p->ready)
     goto ignore;
 
-  if (!rt_out_sync_mark(p->p.main_channel, &ex))
+  if (!rt_out_sync_mark(p->p.main_channel, &ex, krt_filter_lp))
     goto aseen;
 
   if (!ex.new.attrs)
@@ -622,6 +623,7 @@ delete:
   goto done;
 
 done:
+  lp_flush(krt_filter_lp);
   return;
 }
 
@@ -896,7 +898,7 @@ krt_postconfig(struct proto_config *CF)
 
   if (cf->merge_paths)
   {
-    cc->ra_mode = RA_MERGED;
+    cc->ra_mode = RA_ANY;
     cc->merge_limit = cf->merge_paths;
   }