Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 30 Apr 2026 22:07:48 +0000
From:      Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: dd945c6ba4ff - main - routing: Implement merge of nhgrp in new multipath route
Message-ID:  <69f3d2b4.1c025.47f28c2e@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch main has been updated by pouria:

URL: https://cgit.FreeBSD.org/src/commit/?id=dd945c6ba4ff8d444c4cb90a911d96c66b6fc4aa

commit dd945c6ba4ff8d444c4cb90a911d96c66b6fc4aa
Author:     Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org>
AuthorDate: 2026-03-31 19:13:48 +0000
Commit:     Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org>
CommitDate: 2026-04-30 22:05:32 +0000

    routing: Implement merge of nhgrp in new multipath route
    
    Routing subsystem allows creating new multipath routes by
    nexthop groups (e.g RTA_MULTIPATH in netlink), in case of
    a second nexthop group on the same route, don't panic and
    merge the existing nhgrp with new one.
    
    Reviewed by: melifaro (except one comment)
    Differential Revision: https://reviews.freebsd.org/D56187
---
 sys/net/route/nhgrp_ctl.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++
 sys/net/route/route_ctl.c |  5 ++-
 sys/net/route/route_var.h |  3 ++
 3 files changed, 106 insertions(+), 1 deletion(-)

diff --git a/sys/net/route/nhgrp_ctl.c b/sys/net/route/nhgrp_ctl.c
index 8a1fa2113a6c..7230e02195ee 100644
--- a/sys/net/route/nhgrp_ctl.c
+++ b/sys/net/route/nhgrp_ctl.c
@@ -632,6 +632,63 @@ append_nhops(struct nh_control *ctl, const struct nhgrp_object *gr_orig,
 	return (nhg_priv);
 }
 
+/*
+ * Merge nexthop group denoted by @gr_add with the nexthop group @gr_orig.
+ *
+ * Returns referenced nexthop group or NULL. In the latter case, @perror is
+ *  filled with an error code.
+ * Note that function does NOT care if the next nexthops already exists
+ * in the @gr_orig. As a result, they will be added, resulting in the
+ * same nexthop being present multiple times in the new group.
+ */
+static struct nhgrp_priv *
+merge_nhgrps(struct nh_control *ctl, const struct nhgrp_object *gr_orig,
+     const struct nhgrp_object *gr_add, int *perror)
+{
+	char storage[64];
+	struct weightened_nhop *pnhops;
+	struct nhgrp_priv *nhg_priv;
+	const struct nhgrp_priv *orig_priv, *add_priv;
+	size_t sz;
+	int curr_nhops;
+
+	orig_priv = NHGRP_PRIV_CONST(gr_orig);
+	add_priv = NHGRP_PRIV_CONST(gr_add);
+	curr_nhops = orig_priv->nhg_nh_count;
+
+	*perror = 0;
+
+	sz = (orig_priv->nhg_nh_count + orig_priv->nhg_nh_count) *
+		sizeof(struct weightened_nhop);
+	/* optimize for <= 4 paths, each path=16 bytes */
+	if (sz <= sizeof(storage))
+		pnhops = (struct weightened_nhop *)&storage[0];
+	else {
+		pnhops = malloc(sz, M_TEMP, M_NOWAIT);
+		if (pnhops == NULL) {
+			*perror = ENOMEM;
+			return (NULL);
+		}
+	}
+
+	/* First, copy nhops from first group */
+	memcpy(pnhops, orig_priv->nhg_nh_weights,
+	   orig_priv->nhg_nh_count * sizeof(struct weightened_nhop));
+	memcpy(&pnhops[curr_nhops], add_priv->nhg_nh_weights,
+	   add_priv->nhg_nh_count * sizeof(struct weightened_nhop));
+	curr_nhops += add_priv->nhg_nh_count;
+
+	nhg_priv = get_nhgrp(ctl, pnhops, curr_nhops, 0, perror);
+
+	if (pnhops != (struct weightened_nhop *)&storage[0])
+		free(pnhops, M_TEMP);
+
+	if (nhg_priv == NULL)
+		return (NULL);
+
+	return (nhg_priv);
+}
+
 
 /*
  * Creates/finds nexthop group based on @wn and @num_nhops.
@@ -728,6 +785,8 @@ nhgrp_get_addition_group(struct rib_head *rh, struct route_nhop_data *rnd_orig,
 	struct weightened_nhop wn[2] = {};
 	int error;
 
+	MPASS((!NH_IS_NHGRP(rnd_add->rnd_nhop)));
+
 	if (rnd_orig->rnd_nhop == NULL) {
 		/* No paths to add to, just reference current nhop */
 		*rnd_new = *rnd_add;
@@ -758,6 +817,46 @@ nhgrp_get_addition_group(struct rib_head *rh, struct route_nhop_data *rnd_orig,
 	return (0);
 }
 
+/*
+ * Creates new multipath group based on existing group/nhop in @rnd_orig and
+ *  to-be-merged nhgrp @wn_add.
+ * Returns 0 on success and stores result in @rnd_new.
+ */
+int
+nhgrp_get_merge_group(struct rib_head *rh, struct route_nhop_data *rnd_orig,
+    struct route_nhop_data *rnd_add, struct route_nhop_data *rnd_new)
+{
+	struct nh_control *ctl = rh->nh_control;
+	struct nhgrp_priv *nhg_priv;
+	struct weightened_nhop wn = {};
+	int error;
+
+	MPASS((NH_IS_NHGRP(rnd_add->rnd_nhop)));
+
+	/* No paths to add to, Just give up */
+	if (rnd_orig->rnd_nhop == NULL)
+		return (EINVAL);
+
+	if (!NH_IS_NHGRP(rnd_orig->rnd_nhop)) {
+		wn.nh = rnd_orig->rnd_nhop;
+		wn.weight = rnd_orig->rnd_weight;
+		/* Get new nhop group with addition of nhops in nhgrp */
+		nhg_priv = append_nhops(ctl, rnd_add->rnd_nhgrp, &wn, 1,
+		    &error);
+	} else {
+		/* Get new nhop group with addition of nhops in nhgrp */
+		nhg_priv = merge_nhgrps(ctl, rnd_orig->rnd_nhgrp, rnd_add->rnd_nhgrp,
+		    &error);
+	}
+
+	if (nhg_priv == NULL)
+		return (error);
+	rnd_new->rnd_nhgrp = nhg_priv->nhg;
+	rnd_new->rnd_weight = 0;
+
+	return (0);
+}
+
 /*
  * Returns pointer to array of nexthops with weights for
  * given @nhg. Stores number of items in the array into @pnum_nhops.
diff --git a/sys/net/route/route_ctl.c b/sys/net/route/route_ctl.c
index c6d8d43a73f4..fe00c762905d 100644
--- a/sys/net/route/route_ctl.c
+++ b/sys/net/route/route_ctl.c
@@ -858,7 +858,10 @@ add_route_flags_mpath(struct rib_head *rnh, struct rtentry *rt,
 	struct route_nhop_data rnd_new;
 	int error = 0;
 
-	error = nhgrp_get_addition_group(rnh, rnd_orig, rnd_add, &rnd_new);
+	if (!NH_IS_NHGRP(rnd_add->rnd_nhop))
+		error = nhgrp_get_addition_group(rnh, rnd_orig, rnd_add, &rnd_new);
+	else
+		error = nhgrp_get_merge_group(rnh, rnd_orig, rnd_add, &rnd_new);
 	if (error != 0) {
 		if (error == EAGAIN) {
 			/*
diff --git a/sys/net/route/route_var.h b/sys/net/route/route_var.h
index 40433f1b37c0..df528c93262a 100644
--- a/sys/net/route/route_var.h
+++ b/sys/net/route/route_var.h
@@ -309,6 +309,9 @@ int nhgrp_get_filtered_group(struct rib_head *rh, const struct rtentry *rt,
 int nhgrp_get_addition_group(struct rib_head *rnh,
     struct route_nhop_data *rnd_orig, struct route_nhop_data *rnd_add,
     struct route_nhop_data *rnd_new);
+int nhgrp_get_merge_group(struct rib_head *rnh,
+    struct route_nhop_data *rnd_orig, struct route_nhop_data *rnd_add,
+    struct route_nhop_data *rnd_new);
 
 void nhgrp_ref_object(struct nhgrp_object *nhg);
 uint32_t nhgrp_get_idx(const struct nhgrp_object *nhg);


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?69f3d2b4.1c025.47f28c2e>