]> git.ipfire.org Git - thirdparty/bird.git/blobdiff - sysdep/unix/krt.c
KRT: Improve syncer code to avoid using temporary data in rtable
[thirdparty/bird.git] / sysdep / unix / krt.c
index d4cb964eee5ca84e2682656c9686228a41c90b66..42dd12f6e22f250835f4a9ac14b54ebde5ca0d58 100644 (file)
@@ -56,9 +56,9 @@
 #include "nest/route.h"
 #include "nest/protocol.h"
 #include "filter/filter.h"
-#include "sysdep/unix/timer.h"
 #include "conf/conf.h"
 #include "lib/string.h"
+#include "lib/timer.h"
 
 #include "unix.h"
 #include "krt.h"
@@ -75,7 +75,7 @@ void
 krt_io_init(void)
 {
   krt_pool = rp_new(&root_pool, "Kernel Syncer");
-  krt_filter_lp = lp_new(krt_pool, 4080);
+  krt_filter_lp = lp_new_default(krt_pool);
   init_list(&krt_proto_list);
   krt_sys_io_init();
 }
@@ -87,7 +87,17 @@ krt_io_init(void)
 struct kif_proto *kif_proto;
 static struct kif_config *kif_cf;
 static timer *kif_scan_timer;
-static bird_clock_t kif_last_shot;
+static btime kif_last_shot;
+
+static struct kif_iface_config kif_default_iface = {};
+
+struct kif_iface_config *
+kif_get_iface_config(struct iface *iface)
+{
+  struct kif_config *cf = (void *) (kif_proto->p.cf);
+  struct kif_iface_config *ic = (void *) iface_patt_find(&cf->iface_list, iface, NULL);
+  return ic ?: &kif_default_iface;
+}
 
 static void
 kif_scan(timer *t)
@@ -95,14 +105,14 @@ kif_scan(timer *t)
   struct kif_proto *p = t->data;
 
   KRT_TRACE(p, D_EVENTS, "Scanning interfaces");
-  kif_last_shot = now;
+  kif_last_shot = current_time();
   kif_do_scan(p);
 }
 
 static void
 kif_force_scan(void)
 {
-  if (kif_proto && kif_last_shot + 2 < now)
+  if (kif_proto && ((kif_last_shot + 2 S) < current_time()))
     {
       kif_scan(kif_scan_timer);
       tm_start(kif_scan_timer, ((struct kif_config *) kif_proto->p.cf)->scan_time);
@@ -112,61 +122,10 @@ kif_force_scan(void)
 void
 kif_request_scan(void)
 {
-  if (kif_proto && kif_scan_timer->expires > now)
-    tm_start(kif_scan_timer, 1);
-}
-
-static inline int
-prefer_addr(struct ifa *a, struct ifa *b)
-{
-  int sa = a->scope > SCOPE_LINK;
-  int sb = b->scope > SCOPE_LINK;
-
-  if (sa < sb)
-    return 0;
-  else if (sa > sb)
-    return 1;
-  else
-    return ipa_compare(a->ip, b->ip) < 0;
-}
-
-static inline struct ifa *
-find_preferred_ifa(struct iface *i, const net_addr *n)
-{
-  struct ifa *a, *b = NULL;
-
-  WALK_LIST(a, i->addrs)
-    {
-      if (!(a->flags & IA_SECONDARY) &&
-         (!n || ipa_in_netX(a->ip, n)) &&
-         (!b || prefer_addr(a, b)))
-       b = a;
-    }
-
-  return b;
-}
-
-struct ifa *
-kif_choose_primary(struct iface *i)
-{
-  struct kif_config *cf = (struct kif_config *) (kif_proto->p.cf);
-  struct kif_primary_item *it;
-  struct ifa *a;
-
-  WALK_LIST(it, cf->primary)
-    {
-      if (!it->pattern || patmatch(it->pattern, i->name))
-       if (a = find_preferred_ifa(i, &it->addr))
-         return a;
-    }
-
-  if (a = kif_get_primary_ip(i))
-    return a;
-
-  return find_preferred_ifa(i, NULL);
+  if (kif_proto && (kif_scan_timer->expires > (current_time() + 1 S)))
+    tm_start(kif_scan_timer, 1 S);
 }
 
-
 static struct proto *
 kif_init(struct proto_config *c)
 {
@@ -185,10 +144,7 @@ kif_start(struct proto *P)
   kif_sys_start(p);
 
   /* Start periodic interface scanning */
-  kif_scan_timer = tm_new(P->pool);
-  kif_scan_timer->hook = kif_scan;
-  kif_scan_timer->data = p;
-  kif_scan_timer->recurrent = KIF_CF->scan_time;
+  kif_scan_timer = tm_new_init(P->pool, kif_scan, p, KIF_CF->scan_time, 0);
   kif_scan(kif_scan_timer);
   tm_start(kif_scan_timer, KIF_CF->scan_time);
 
@@ -224,15 +180,15 @@ kif_reconfigure(struct proto *p, struct proto_config *new)
       tm_start(kif_scan_timer, n->scan_time);
     }
 
-  if (!EMPTY_LIST(o->primary) || !EMPTY_LIST(n->primary))
+  if (!EMPTY_LIST(o->iface_list) || !EMPTY_LIST(n->iface_list))
     {
       /* This is hack, we have to update a configuration
        * to the new value just now, because it is used
-       * for recalculation of primary addresses.
+       * for recalculation of preferred addresses.
        */
       p->cf = new;
 
-      ifa_recalc_all_primary_addresses();
+      if_recalc_all_preferred_addresses();
     }
 
   return 1;
@@ -253,8 +209,8 @@ kif_init_config(int class)
     cf_error("Kernel device protocol already defined");
 
   kif_cf = (struct kif_config *) proto_config_new(&proto_unix_iface, class);
-  kif_cf->scan_time = 60;
-  init_list(&kif_cf->primary);
+  kif_cf->scan_time = 60 S;
+  init_list(&kif_cf->iface_list);
 
   kif_sys_init_config(kif_cf);
   return (struct proto_config *) kif_cf;
@@ -266,17 +222,17 @@ kif_copy_config(struct proto_config *dest, struct proto_config *src)
   struct kif_config *d = (struct kif_config *) dest;
   struct kif_config *s = (struct kif_config *) src;
 
-  /* Copy primary addr list */
-  cfg_copy_list(&d->primary, &s->primary, sizeof(struct kif_primary_item));
+  /* Copy interface config list */
+  cfg_copy_list(&d->iface_list, &s->iface_list, sizeof(struct kif_iface_config));
 
   /* Fix sysdep parts */
   kif_sys_copy_config(d, s);
 }
 
-
 struct protocol proto_unix_iface = {
   .name =              "Device",
   .template =          "device%d",
+  .class =             PROTOCOL_DEVICE,
   .proto_size =                sizeof(struct kif_proto),
   .config_size =       sizeof(struct kif_config),
   .preconfig =         kif_preconfig,
@@ -345,7 +301,7 @@ krt_learn_announce_update(struct krt_proto *p, rte *e)
   net *n = e->net;
   rta *aa = rta_clone(e->attrs);
   rte *ee = rte_get_temp(aa);
-  ee->pflags = 0;
+  ee->pflags = EA_ID_FLAG(EA_KRT_SOURCE) | EA_ID_FLAG(EA_KRT_METRIC);
   ee->u.krt = e->u.krt;
   rte_update(&p->p, n->n.addr, ee);
 }
@@ -551,7 +507,13 @@ static void
 krt_learn_init(struct krt_proto *p)
 {
   if (KRT_CF->learn)
-    rt_setup(p->p.pool, &p->krt_table, "Inherited", NULL);
+  {
+    struct rtable_config *cf = mb_allocz(p->p.pool, sizeof(struct rtable_config));
+    cf->name = "Inherited";
+    cf->addr_type = p->p.net_type;
+
+    rt_setup(p->p.pool, &p->krt_table, cf);
+  }
 }
 
 static void
@@ -577,6 +539,12 @@ krt_dump_attrs(rte *e)
  *     Routes
  */
 
+static inline int
+krt_is_installed(struct krt_proto *p, net *n)
+{
+  return n->routes && bmap_test(&p->p.main_channel->export_map, n->routes->id);
+}
+
 static void
 krt_flush_routes(struct krt_proto *p)
 {
@@ -585,26 +553,24 @@ krt_flush_routes(struct krt_proto *p)
   KRT_TRACE(p, D_EVENTS, "Flushing kernel routes");
   FIB_WALK(&t->fib, net, n)
     {
-      rte *e = n->routes;
-      if (rte_is_valid(e) && (n->n.flags & KRF_INSTALLED))
+      if (krt_is_installed(p, n))
        {
          /* FIXME: this does not work if gw is changed in export filter */
-         krt_replace_rte(p, e->net, NULL, e, NULL);
-         n->n.flags &= ~KRF_INSTALLED;
+         krt_replace_rte(p, n, NULL, n->routes);
        }
     }
   FIB_WALK_END;
 }
 
 static struct rte *
-krt_export_net(struct krt_proto *p, net *net, rte **rt_free, ea_list **tmpa)
+krt_export_net(struct krt_proto *p, net *net, rte **rt_free)
 {
   struct channel *c = p->p.main_channel;
-  struct filter *filter = c->out_filter;
+  const struct filter *filter = c->out_filter;
   rte *rt;
 
   if (c->ra_mode == RA_MERGED)
-    return rt_export_merged(c, net, rt_free, tmpa, krt_filter_lp, 1);
+    return rt_export_merged(c, net, rt_free, krt_filter_lp, 1);
 
   rt = net->routes;
   *rt_free = NULL;
@@ -615,15 +581,14 @@ krt_export_net(struct krt_proto *p, net *net, rte **rt_free, ea_list **tmpa)
   if (filter == FILTER_REJECT)
     return NULL;
 
-  struct proto *src = rt->attrs->src->proto;
-  *tmpa = src->make_tmp_attrs ? src->make_tmp_attrs(rt, krt_filter_lp) : NULL;
+  rte_make_tmp_attrs(&rt, krt_filter_lp, NULL);
 
-  /* We could run krt_import_control() here, but it is already handled by KRF_INSTALLED */
+  /* We could run krt_preexport() here, but it is already handled by krt_is_installed() */
 
   if (filter == FILTER_ACCEPT)
     goto accept;
 
-  if (f_run(filter, &rt, tmpa, krt_filter_lp, FF_FORCE_TMPATTR) > F_ACCEPT)
+  if (f_run(filter, &rt, krt_filter_lp, FF_SILENT) > F_ACCEPT)
     goto reject;
 
 
@@ -645,17 +610,11 @@ krt_same_dest(rte *k, rte *e)
 
   if (ka->dest != ea->dest)
     return 0;
-  switch (ka->dest)
-    {
-    case RTD_ROUTER:
-      return ipa_equal(ka->gw, ea->gw);
-    case RTD_DEVICE:
-      return !strcmp(ka->iface->name, ea->iface->name);
-    case RTD_MULTIPATH:
-      return mpnh_same(ka->nexthops, ea->nexthops);
-    default:
-      return 1;
-    }
+
+  if (ka->dest == RTD_UNICAST)
+    return nexthop_same(&(ka->nh), &(ea->nh));
+
+  return 1;
 }
 
 /*
@@ -666,19 +625,17 @@ krt_same_dest(rte *k, rte *e)
 void
 krt_got_route(struct krt_proto *p, rte *e)
 {
-  net *net = e->net;
-  int verdict;
+  rte *new = NULL, *rt_free = NULL;
+  net *n = e->net;
 
 #ifdef KRT_ALLOW_LEARN
   switch (e->u.krt.src)
     {
     case KRT_SRC_KERNEL:
-      verdict = KRF_IGNORE;
-      goto sentenced;
+      goto ignore;
 
     case KRT_SRC_REDIRECT:
-      verdict = KRF_DELETE;
-      goto sentenced;
+      goto delete;
 
     case  KRT_SRC_ALIEN:
       if (KRT_CF->learn)
@@ -693,59 +650,68 @@ krt_got_route(struct krt_proto *p, rte *e)
 #endif
   /* The rest is for KRT_SRC_BIRD (or KRT_SRC_UNKNOWN) */
 
-  if (net->n.flags & KRF_VERDICT_MASK)
-    {
-      /* Route to this destination was already seen. Strange, but it happens... */
-      krt_trace_in(p, e, "already seen");
-      rte_free(e);
-      return;
-    }
 
+  /* We wait for the initial feed to have correct installed state */
   if (!p->ready)
-    {
-      /* We wait for the initial feed to have correct KRF_INSTALLED flag */
-      verdict = KRF_IGNORE;
-      goto sentenced;
-    }
+    goto ignore;
 
-  if (net->n.flags & KRF_INSTALLED)
-    {
-      rte *new, *rt_free;
-      ea_list *tmpa;
+  if (!krt_is_installed(p, n))
+    goto delete;
 
-      new = krt_export_net(p, net, &rt_free, &tmpa);
+  new = krt_export_net(p, n, &rt_free);
 
-      /* TODO: There also may be changes in route eattrs, we ignore that for now. */
+  /* Rejected by filters */
+  if (!new)
+    goto delete;
 
-      if (!new)
-       verdict = KRF_DELETE;
-      else if ((net->n.flags & KRF_SYNC_ERROR) || !krt_same_dest(e, new))
-       verdict = KRF_UPDATE;
-      else
-       verdict = KRF_SEEN;
+  /* Route to this destination was already seen. Strange, but it happens... */
+  if (bmap_test(&p->seen_map, new->id))
+    goto aseen;
 
-      if (rt_free)
-       rte_free(rt_free);
+  /* Mark route as seen */
+  bmap_set(&p->seen_map, new->id);
 
-      lp_flush(krt_filter_lp);
-    }
-  else
-    verdict = KRF_DELETE;
+  /* 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))
+    goto update;
 
- sentenced:
-  krt_trace_in(p, e, ((char *[]) { "?", "seen", "will be updated", "will be removed", "ignored" }) [verdict]);
-  net->n.flags = (net->n.flags & ~KRF_VERDICT_MASK) | verdict;
-  if (verdict == KRF_UPDATE || verdict == KRF_DELETE)
-    {
-      /* Get a cached copy of attributes and temporarily link the route */
-      rta *a = e->attrs;
-      a->source = RTS_DUMMY;
-      e->attrs = rta_lookup(a);
-      e->next = net->routes;
-      net->routes = e;
-    }
-  else
-    rte_free(e);
+  goto seen;
+
+seen:
+  krt_trace_in(p, e, "seen");
+  goto done;
+
+aseen:
+  krt_trace_in(p, e, "already seen");
+  goto done;
+
+ignore:
+  krt_trace_in(p, e, "ignored");
+  goto done;
+
+update:
+  krt_trace_in(p, new, "updating");
+  krt_replace_rte(p, n, new, e);
+  goto done;
+
+delete:
+  krt_trace_in(p, e, "deleting");
+  krt_replace_rte(p, n, NULL, e);
+  goto done;
+
+done:
+  rte_free(e);
+
+  if (rt_free)
+    rte_free(rt_free);
+
+  lp_flush(krt_filter_lp);
+}
+
+static void
+krt_init_scan(struct krt_proto *p)
+{
+  bmap_reset(&p->seen_map, 1024);
 }
 
 static void
@@ -755,65 +721,24 @@ krt_prune(struct krt_proto *p)
 
   KRT_TRACE(p, D_EVENTS, "Pruning table %s", t->name);
   FIB_WALK(&t->fib, net, n)
+  {
+    if (p->ready && krt_is_installed(p, n) && !bmap_test(&p->seen_map, n->routes->id))
     {
-      int verdict = n->n.flags & KRF_VERDICT_MASK;
-      rte *new, *old, *rt_free = NULL;
-      ea_list *tmpa = NULL;
-
-      if (verdict == KRF_UPDATE || verdict == KRF_DELETE)
-       {
-         /* Get a dummy route from krt_got_route() */
-         old = n->routes;
-         n->routes = old->next;
-       }
-      else
-       old = NULL;
-
-      if (verdict == KRF_CREATE || verdict == KRF_UPDATE)
-       {
-         /* We have to run export filter to get proper 'new' route */
-         new = krt_export_net(p, n, &rt_free, &tmpa);
+      rte *rt_free = NULL;
+      rte *new = krt_export_net(p, n, &rt_free);
 
-         if (!new)
-           verdict = (verdict == KRF_CREATE) ? KRF_IGNORE : KRF_DELETE;
-         else
-           tmpa = ea_append(tmpa, new->attrs->eattrs);
-       }
-      else
-       new = NULL;
-
-      switch (verdict)
-       {
-       case KRF_CREATE:
-         if (new && (n->n.flags & KRF_INSTALLED))
-           {
-             krt_trace_in(p, new, "reinstalling");
-             krt_replace_rte(p, n, new, NULL, tmpa);
-           }
-         break;
-       case KRF_SEEN:
-       case KRF_IGNORE:
-         /* Nothing happens */
-         break;
-       case KRF_UPDATE:
-         krt_trace_in(p, new, "updating");
-         krt_replace_rte(p, n, new, old, tmpa);
-         break;
-       case KRF_DELETE:
-         krt_trace_in(p, old, "deleting");
-         krt_replace_rte(p, n, NULL, old, NULL);
-         break;
-       default:
-         bug("krt_prune: invalid route status");
-       }
+      if (new)
+      {
+       krt_trace_in(p, new, "installing");
+       krt_replace_rte(p, n, new, NULL);
+      }
 
-      if (old)
-       rte_free(old);
       if (rt_free)
        rte_free(rt_free);
+
       lp_flush(krt_filter_lp);
-      n->n.flags &= ~KRF_VERDICT_MASK;
     }
+  }
   FIB_WALK_END;
 
 #ifdef KRT_ALLOW_LEARN
@@ -833,13 +758,14 @@ krt_got_route_async(struct krt_proto *p, rte *e, int new)
   switch (e->u.krt.src)
     {
     case KRT_SRC_BIRD:
-      ASSERT(0);                       /* Should be filtered by the back end */
+      /* Should be filtered by the back end */
+      bug("BIRD originated routes should not get here.");
 
     case KRT_SRC_REDIRECT:
       if (new)
        {
          krt_trace_in(p, e, "[redirect] deleting");
-         krt_replace_rte(p, net, NULL, e, NULL);
+         krt_replace_rte(p, net, NULL, e);
        }
       /* If !new, it is probably echo of our deletion */
       break;
@@ -870,6 +796,7 @@ static void
 krt_scan(timer *t UNUSED)
 {
   struct krt_proto *p;
+  node *n;
 
   kif_force_scan();
 
@@ -877,29 +804,28 @@ krt_scan(timer *t UNUSED)
   p = SKIP_BACK(struct krt_proto, krt_node, HEAD(krt_proto_list));
   KRT_TRACE(p, D_EVENTS, "Scanning routing table");
 
+  WALK_LIST2(p, n, krt_proto_list, krt_node)
+    krt_init_scan(p);
+
   krt_do_scan(NULL);
 
-  void *q;
-  WALK_LIST(q, krt_proto_list)
-  {
-    p = SKIP_BACK(struct krt_proto, krt_node, q);
+  WALK_LIST2(p, n, krt_proto_list, krt_node)
     krt_prune(p);
-  }
 }
 
 static void
 krt_scan_timer_start(struct krt_proto *p)
 {
   if (!krt_scan_count)
-    krt_scan_timer = tm_new_set(krt_pool, krt_scan, NULL, 0, KRT_CF->scan_time);
+    krt_scan_timer = tm_new_init(krt_pool, krt_scan, NULL, KRT_CF->scan_time, 0);
 
   krt_scan_count++;
 
-  tm_start(krt_scan_timer, 1);
+  tm_start(krt_scan_timer, 1 S);
 }
 
 static void
-krt_scan_timer_stop(struct krt_proto *p)
+krt_scan_timer_stop(struct krt_proto *p UNUSED)
 {
   krt_scan_count--;
 
@@ -926,6 +852,7 @@ krt_scan(timer *t)
   kif_force_scan();
 
   KRT_TRACE(p, D_EVENTS, "Scanning routing table");
+  krt_init_scan(p);
   krt_do_scan(p);
   krt_prune(p);
 }
@@ -933,8 +860,8 @@ krt_scan(timer *t)
 static void
 krt_scan_timer_start(struct krt_proto *p)
 {
-  p->scan_timer = tm_new_set(p->p.pool, krt_scan, p, 0, KRT_CF->scan_time);
-  tm_start(p->scan_timer, 1);
+  p->scan_timer = tm_new_init(p->p.pool, krt_scan, p, KRT_CF->scan_time, 0);
+  tm_start(p->scan_timer, 1 S);
 }
 
 static void
@@ -958,62 +885,29 @@ krt_scan_timer_kick(struct krt_proto *p)
  *     Updates
  */
 
-static struct ea_list *
-krt_make_tmp_attrs(rte *rt, struct linpool *pool)
+static void
+krt_make_tmp_attrs(struct rte *rt, struct linpool *pool)
 {
-  struct ea_list *l = lp_alloc(pool, sizeof(struct ea_list) + 2 * sizeof(eattr));
-
-  l->next = NULL;
-  l->flags = EALF_SORTED;
-  l->count = 2;
-
-  l->attrs[0].id = EA_KRT_SOURCE;
-  l->attrs[0].flags = 0;
-  l->attrs[0].type = EAF_TYPE_INT | EAF_TEMP;
-  l->attrs[0].u.data = rt->u.krt.proto;
-
-  l->attrs[1].id = EA_KRT_METRIC;
-  l->attrs[1].flags = 0;
-  l->attrs[1].type = EAF_TYPE_INT | EAF_TEMP;
-  l->attrs[1].u.data = rt->u.krt.metric;
-
-  return l;
+  rte_init_tmp_attrs(rt, pool, 2);
+  rte_make_tmp_attr(rt, EA_KRT_SOURCE, EAF_TYPE_INT, rt->u.krt.proto);
+  rte_make_tmp_attr(rt, EA_KRT_METRIC, EAF_TYPE_INT, rt->u.krt.metric);
 }
 
 static void
-krt_store_tmp_attrs(rte *rt, struct ea_list *attrs)
+krt_store_tmp_attrs(struct rte *rt, struct linpool *pool)
 {
-  /* EA_KRT_SOURCE is read-only */
-  rt->u.krt.metric = ea_get_int(attrs, EA_KRT_METRIC, 0);
+  rte_init_tmp_attrs(rt, pool, 2);
+  rt->u.krt.proto = rte_store_tmp_attr(rt, EA_KRT_SOURCE);
+  rt->u.krt.metric = rte_store_tmp_attr(rt, EA_KRT_METRIC);
 }
 
 static int
-krt_import_control(struct proto *P, rte **new, ea_list **attrs, struct linpool *pool)
+krt_preexport(struct proto *P, rte **new, struct linpool *pool UNUSED)
 {
-  struct krt_proto *p = (struct krt_proto *) P;
+  // struct krt_proto *p = (struct krt_proto *) P;
   rte *e = *new;
 
   if (e->attrs->src->proto == P)
-  {
-#ifdef CONFIG_SINGLE_ROUTE
-    /*
-     * Implicit withdraw - when the imported kernel route becomes the best one,
-     * we know that the previous one exported to the kernel was already removed,
-     * but if we processed the update as usual, we would send withdraw to the
-     * kernel, which would remove the new imported route instead.
-     *
-     * We will remove KRT_INSTALLED flag, which stops such withdraw to be
-     * processed in krt_rt_notify() and krt_replace_rte().
-     */
-    if (e == e->net->routes)
-      e->net->n.flags &= ~KRF_INSTALLED;
-#endif
-    return -1;
-  }
-
-  if (!KRT_CF->devroutes &&
-      (e->attrs->dest == RTD_DEVICE) &&
-      (e->attrs->source != RTS_STATIC_DEVICE))
     return -1;
 
   if (!krt_capable(e))
@@ -1024,20 +918,27 @@ krt_import_control(struct proto *P, rte **new, ea_list **attrs, struct linpool *
 
 static void
 krt_rt_notify(struct proto *P, struct channel *ch UNUSED, net *net,
-             rte *new, rte *old, struct ea_list *eattrs)
+             rte *new, rte *old)
 {
   struct krt_proto *p = (struct krt_proto *) P;
 
   if (config->shutdown)
     return;
-  if (!(net->n.flags & KRF_INSTALLED))
-    old = NULL;
-  if (new)
-    net->n.flags |= KRF_INSTALLED;
-  else
-    net->n.flags &= ~KRF_INSTALLED;
+
+#ifdef CONFIG_SINGLE_ROUTE
+  /*
+   * Implicit withdraw - when the imported kernel route becomes the best one,
+   * we know that the previous one exported to the kernel was already removed,
+   * but if we processed the update as usual, we would send withdraw to the
+   * kernel, which would remove the new imported route instead.
+   */
+  rte *best = net->routes;
+  if (!new && best && (best->attrs->src->proto == P))
+    return;
+#endif
+
   if (p->initialized)          /* Before first scan we don't touch the routes */
-    krt_replace_rte(p, net, new, old, eattrs);
+    krt_replace_rte(p, net, new, old);
 }
 
 static void
@@ -1115,11 +1016,18 @@ krt_postconfig(struct proto_config *CF)
     cf_error("All kernel syncers must use the same table scan interval");
 #endif
 
-  struct rtable_config *tab = proto_cf_main_channel(CF)->table;
+  struct channel_config *cc = proto_cf_main_channel(CF);
+  struct rtable_config *tab = cc->table;
   if (tab->krt_attached)
     cf_error("Kernel syncer (%s) already attached to table %s", tab->krt_attached->name, tab->name);
   tab->krt_attached = CF;
 
+  if (cf->merge_paths)
+  {
+    cc->ra_mode = RA_MERGED;
+    cc->merge_limit = cf->merge_paths;
+  }
+
   krt_sys_postconfig(cf);
 }
 
@@ -1131,7 +1039,7 @@ krt_init(struct proto_config *CF)
 
   p->p.main_channel = proto_add_channel(&p->p, proto_cf_main_channel(CF));
 
-  p->p.import_control = krt_import_control;
+  p->p.preexport = krt_preexport;
   p->p.rt_notify = krt_rt_notify;
   p->p.if_notify = krt_if_notify;
   p->p.reload_routes = krt_reload_routes;
@@ -1151,11 +1059,17 @@ krt_start(struct proto *P)
 
   switch (p->p.net_type)
   {
-  case NET_IP4:        p->af = AF_INET; break;
-  case NET_IP6:        p->af = AF_INET6; break;
-  default:     ASSERT(0);
+  case NET_IP4:                p->af = AF_INET; break;
+  case NET_IP6:                p->af = AF_INET6; break;
+  case NET_IP6_SADR:   p->af = AF_INET6; break;
+#ifdef AF_MPLS
+  case NET_MPLS:       p->af = AF_MPLS; break;
+#endif
+  default: log(L_ERR "KRT: Tried to start with strange net type: %d", p->p.net_type); return PS_START; break;
   }
 
+  bmap_init(&p->sync_map, p->p.pool, 1024);
+  bmap_init(&p->seen_map, p->p.pool, 1024);
   add_tail(&krt_proto_list, &p->krt_node);
 
 #ifdef KRT_ALLOW_LEARN
@@ -1184,7 +1098,7 @@ krt_shutdown(struct proto *P)
   krt_scan_timer_stop(p);
 
   /* FIXME we should flush routes even when persist during reconfiguration */
-  if (p->initialized && !KRT_CF->persist)
+  if (p->initialized && !KRT_CF->persist && (P->down_code != PDC_CMD_GR_DOWN))
     krt_flush_routes(p);
 
   p->ready = 0;
@@ -1195,6 +1109,7 @@ krt_shutdown(struct proto *P)
 
   krt_sys_shutdown(p);
   rem_node(&p->krt_node);
+  bmap_free(&p->sync_map);
 
   return PS_DOWN;
 }
@@ -1212,7 +1127,7 @@ krt_reconfigure(struct proto *p, struct proto_config *CF)
     return 0;
 
   /* persist, graceful restart need not be the same */
-  return o->scan_time == n->scan_time && o->learn == n->learn && o->devroutes == n->devroutes;
+  return o->scan_time == n->scan_time && o->learn == n->learn;
 }
 
 struct proto_config *
@@ -1224,7 +1139,7 @@ krt_init_config(int class)
 #endif
 
   krt_cf = (struct krt_config *) proto_config_new(&proto_unix_kernel, class);
-  krt_cf->scan_time = 60;
+  krt_cf->scan_time = 60 S;
 
   krt_sys_init_config(krt_cf);
   return (struct proto_config *) krt_cf;
@@ -1259,12 +1174,24 @@ krt_get_attr(eattr *a, byte *buf, int buflen)
 }
 
 
+#ifdef CONFIG_IP6_SADR_KERNEL
+#define MAYBE_IP6_SADR NB_IP6_SADR
+#else
+#define MAYBE_IP6_SADR 0
+#endif
+
+#ifdef HAVE_MPLS_KERNEL
+#define MAYBE_MPLS     NB_MPLS
+#else
+#define MAYBE_MPLS     0
+#endif
+
 struct protocol proto_unix_kernel = {
   .name =              "Kernel",
   .template =          "kernel%d",
-  .attr_class =                EAP_KRT,
+  .class =             PROTOCOL_KERNEL,
   .preference =                DEF_PREF_INHERITED,
-  .channel_mask =      NB_IP,
+  .channel_mask =      NB_IP | MAYBE_IP6_SADR | MAYBE_MPLS,
   .proto_size =                sizeof(struct krt_proto),
   .config_size =       sizeof(struct krt_config),
   .preconfig =         krt_preconfig,