]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Nest: Change nexthop merging order to prefer earlier nexthops
authorOndrej Zajicek (work) <santiago@crfreenet.org>
Thu, 27 Aug 2020 01:24:04 +0000 (03:24 +0200)
committerOndrej Zajicek (work) <santiago@crfreenet.org>
Thu, 27 Aug 2020 01:24:04 +0000 (03:24 +0200)
When nexthop merge limit was applied, the old code used first N nexthops
in the canonical ordering. Now the code prefers nexthops based on the
order or routes in the network.

nest/route.h
nest/rt-attr.c
nest/rt-table.c

index 1afad14ce91b689c1af4750171d2393c5f55f296..10874530f37db57fd541dd98e074b4f3c82876c1 100644 (file)
@@ -638,6 +638,7 @@ int nexthop__same(struct nexthop *x, struct nexthop *y); /* Compare multipath ne
 static inline int nexthop_same(struct nexthop *x, struct nexthop *y)
 { return (x == y) || nexthop__same(x, y); }
 struct nexthop *nexthop_merge(struct nexthop *x, struct nexthop *y, int rx, int ry, int max, linpool *lp);
+struct nexthop *nexthop_merge2(struct nexthop *x, struct nexthop *y, int ry, int *max, linpool *lp);
 static inline void nexthop_link(struct rta *a, struct nexthop *from)
 { memcpy(&a->nh, from, nexthop_size(from)); }
 void nexthop_insert(struct nexthop **n, struct nexthop *y);
index 3cbd25b021bb374e5644377844e7510b027915cf..4492ced9547bffda626eef711dab706a5b4052f2 100644 (file)
@@ -285,6 +285,57 @@ nexthop_merge(struct nexthop *x, struct nexthop *y, int rx, int ry, int max, lin
   return root;
 }
 
+/**
+ * nexthop_merge2 - merge nexthop lists
+ * @x: list 1
+ * @y: list 2
+ * @ry: reusability of list @y
+ * @max: max number of added nexthops
+ * @lp: linpool for allocating nexthops
+ *
+ * The nexthop_merge2() function takes two nexthop lists @x and @y and merges
+ * them, eliminating possible duplicates. It is a variant of nexthop_merge()
+ * function differing in how @max limit is handled, here it limits just number
+ * of nexthops added from the second list @y. The @max value is decreased with
+ * each such nexthop, this return remaining 'unused' limit, which can be used in
+ * subsequent calls. The list @x is expected to be an accumulator and its
+ * reusability is implied.  New nodes are allocated from linpool @lp.
+ */
+struct nexthop *
+nexthop_merge2(struct nexthop *x, struct nexthop *y, int ry, int *max, linpool *lp)
+{
+  struct nexthop *root = NULL;
+  struct nexthop **n = &root;
+
+  if (*max <= 0)
+    return x;
+
+  while (x || y)
+  {
+    int cmp = nexthop_compare_node(x, y);
+    if (cmp < 0)
+    {
+      *n = x;
+      x = x->next;
+    }
+    else if (cmp > 0)
+    {
+      *n = ry ? y : nexthop_copy_node(y, lp);
+      y = (--*max) ? y->next : NULL;
+    }
+    else
+    {
+      *n = x;
+      x = x->next;
+      y = y->next;
+    }
+    n = &((*n)->next);
+  }
+  *n = NULL;
+
+  return root;
+}
+
 void
 nexthop_insert(struct nexthop **n, struct nexthop *x)
 {
index 5422997eca60be5587ce9d1495f3131126ae6767..5f21de7523f77e79d9e46b24f785f5ead84f2c2a 100644 (file)
@@ -849,18 +849,13 @@ rt_notify_accepted(struct channel *c, net *net, rte *new_changed, rte *old_chang
 }
 
 
-static struct nexthop *
-nexthop_merge_rta(struct nexthop *nhs, rta *a, linpool *pool, int max)
-{
-  return nexthop_merge(nhs, &(a->nh), 1, 0, max, pool);
-}
-
 rte *
 rt_export_merged(struct channel *c, net *net, rte **rt_free, ea_list **tmpa, linpool *pool, int silent)
 {
   // struct proto *p = c->proto;
   struct nexthop *nhs = NULL;
   rte *best0, *best, *rt0, *rt, *tmp;
+  int max = c->merge_limit;
 
   best0 = net->routes;
   *rt_free = NULL;
@@ -870,10 +865,12 @@ rt_export_merged(struct channel *c, net *net, rte **rt_free, ea_list **tmpa, lin
 
   best = export_filter_(c, best0, rt_free, tmpa, pool, silent);
 
-  if (!best || !rte_is_reachable(best))
+  if (!best || !rte_is_reachable(best) || !best0->next)
     return best;
 
-  for (rt0 = best0->next; rt0; rt0 = rt0->next)
+  nhs = nexthop_merge2(NULL, &(best->attrs->nh), 0, &max, pool);
+
+  for (rt0 = best0->next; rt0 && max; rt0 = rt0->next)
   {
     if (!rte_mergable(best0, rt0))
       continue;
@@ -884,22 +881,14 @@ rt_export_merged(struct channel *c, net *net, rte **rt_free, ea_list **tmpa, lin
       continue;
 
     if (rte_is_reachable(rt))
-      nhs = nexthop_merge_rta(nhs, rt->attrs, pool, c->merge_limit);
+      nhs = nexthop_merge2(nhs, &(rt->attrs->nh), 0, &max, pool);
 
     if (tmp)
       rte_free(tmp);
   }
 
-  if (nhs)
-  {
-    nhs = nexthop_merge_rta(nhs, best->attrs, pool, c->merge_limit);
-
-    if (nhs->next)
-    {
-      best = rte_cow_rta(best, pool);
-      nexthop_link(best->attrs, nhs);
-    }
-  }
+  best = rte_cow_rta(best, pool);
+  nexthop_link(best->attrs, nhs);
 
   if (best != best0)
     *rt_free = best;