diff --git a/nest/route.h b/nest/route.h index 1afad14c..10874530 100644 --- a/nest/route.h +++ b/nest/route.h @@ -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); diff --git a/nest/rt-attr.c b/nest/rt-attr.c index 3cbd25b0..4492ced9 100644 --- a/nest/rt-attr.c +++ b/nest/rt-attr.c @@ -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) { diff --git a/nest/rt-table.c b/nest/rt-table.c index 5422997e..5f21de75 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -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;