git: be42ac4dceac - main - routing: Replace unreachable nhops in nhgrp
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Sat, 11 Jul 2026 19:26:23 UTC
The branch main has been updated by pouria:
URL: https://cgit.FreeBSD.org/src/commit/?id=be42ac4dceacedb0e4f676a0614e14d125fab36b
commit be42ac4dceacedb0e4f676a0614e14d125fab36b
Author: Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org>
AuthorDate: 2026-06-01 20:07:27 +0000
Commit: Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org>
CommitDate: 2026-07-11 18:23:07 +0000
routing: Replace unreachable nhops in nhgrp
If a nhop gets an interface event, revalidate the nhops and
immediately try to recompile existing nexthop groups by
replacing unreachable nexthops with reachable ones.
If none are available, recompile them back to
their normal position in nexthop group slots.
Reviewed by: glebius
Discussed with: markj
Relnotes: yes
Differential Revision: https://reviews.freebsd.org/D57389
---
sys/net/route/nhgrp_ctl.c | 66 ++++++++++++++++++++++++++++++++++++++++++-----
sys/net/route/nhop_ctl.c | 15 +++++++++--
sys/net/route/route_var.h | 1 +
3 files changed, 74 insertions(+), 8 deletions(-)
diff --git a/sys/net/route/nhgrp_ctl.c b/sys/net/route/nhgrp_ctl.c
index d684cd698284..ea1d3b089d9e 100644
--- a/sys/net/route/nhgrp_ctl.c
+++ b/sys/net/route/nhgrp_ctl.c
@@ -251,14 +251,29 @@ compile_nhgrp(struct nhgrp_priv *dst_priv, const struct weightened_nhop *x,
struct nhgrp_object *dst;
int i, slot_idx, remaining_slots;
uint64_t remaining_sum, nh_weight, nh_slots;
+ bool one_reachable = true;
slot_idx = 0;
dst = dst_priv->nhg;
/* Calculate sum of all weights with lowest metric */
- remaining_sum = 0;
+ remaining_sum = nh_weight = 0;
for (i = 0; i < dst_priv->nhg_nh_count; i++) {
- if (nhop_get_metric(x[i].nh) == metric)
- remaining_sum += x[i].weight;
+ if (nhop_get_metric(x[i].nh) == metric) {
+ /*
+ * Temporary store weight of unreachable nhops in nh_weight
+ * to ensure we have at least one reachable nexthop.
+ */
+ if (NH_IS_VALID(x[i].nh))
+ remaining_sum += x[i].weight;
+ else
+ nh_weight += x[i].weight;
+ }
+ }
+
+ /* If no reachable nhops exist, include all */
+ if (remaining_sum == 0) {
+ remaining_sum = nh_weight;
+ one_reachable = false;
}
remaining_slots = num_slots;
@@ -268,14 +283,18 @@ compile_nhgrp(struct nhgrp_priv *dst_priv, const struct weightened_nhop *x,
if (nhop_get_metric(x[i].nh) != metric)
continue;
- /* Calculate number of slots for the current nexthop */
- if (remaining_sum > 0) {
+ /*
+ * Calculate number of slots for the current nexthop.
+ * Exclude unreachable nexthops if there is one reachable.
+ */
+ if (remaining_sum > 0 &&
+ (NH_IS_VALID(x[i].nh) || !one_reachable)) {
nh_weight = (uint64_t)x[i].weight;
nh_slots = (nh_weight * remaining_slots / remaining_sum);
+ remaining_sum -= x[i].weight;
} else
nh_slots = 0;
- remaining_sum -= x[i].weight;
remaining_slots -= nh_slots;
FIB_NH_LOG(LOG_DEBUG3, x[0].nh,
@@ -1066,6 +1085,41 @@ nhgrp_get_count(struct rib_head *rh)
return (count);
}
+/*
+ * Recompile nexthop group without changing the slots.
+ * Since a nexthop might become reachable again soon,
+ * this avoids unnecessary memory allocation.
+ */
+static void
+nhgrp_recompile_one(struct nhgrp_priv *nhg_priv)
+{
+ struct nhgrp_object *nhg = nhg_priv->nhg;
+ const struct weightened_nhop *wn;
+ uint32_t num_nhops, min_metric;
+
+ wn = nhgrp_get_nhops(nhg, &num_nhops);
+
+ /* dataplane only has the lowest metric nhops, just pick one */
+ min_metric = nhop_get_metric(nhg->nhops[0]);
+ compile_nhgrp(nhg_priv, wn, nhg->nhg_size, min_metric);
+}
+
+void
+nhgrp_recompile(struct rib_head *rh)
+{
+ struct nh_control *ctl = rh->nh_control;
+ struct nhgrp_priv *nhg_priv;
+
+ NHOPS_WLOCK_ASSERT(ctl);
+
+ if (ctl->gr_head.items_count == 0)
+ return;
+
+ CHT_SLIST_FOREACH(&ctl->gr_head, mpath, nhg_priv) {
+ nhgrp_recompile_one(nhg_priv);
+ } CHT_SLIST_FOREACH_END;
+}
+
int
nhgrp_dump_sysctl(struct rib_head *rh, struct sysctl_req *w)
{
diff --git a/sys/net/route/nhop_ctl.c b/sys/net/route/nhop_ctl.c
index 4f013908b338..d978e624e65d 100644
--- a/sys/net/route/nhop_ctl.c
+++ b/sys/net/route/nhop_ctl.c
@@ -1335,19 +1335,30 @@ nhops_ifnet_state_changed(struct ifnet *ifp, bool status)
{
struct nhop_object *nh;
struct nhop_iter iter = { .fibnum = ifp->if_fib, .wlock = true };
+ bool nhops_changed;
for (iter.family = 1; iter.family <= AF_MAX; iter.family++) {
+ /*
+ * By tracking nhop changes, we avoid redundant nhgrp
+ * recompilation triggered by multiple events.
+ */
+ nhops_changed = false;
iter.rh = rt_tables_get_rnh_safe(iter.fibnum, iter.family);
for (nh = nhops_iter_start(&iter); nh != NULL;
nh = nhops_iter_next(&iter)) {
if (nh->nh_ifp != ifp)
continue;
- if (status)
+ if (status && !NH_IS_VALID(nh)) {
nh->nh_flags &= ~NHF_INVALID;
- else
+ nhops_changed = true;
+ } else if (!status && NH_IS_VALID(nh)) {
nh->nh_flags |= NHF_INVALID;
+ nhops_changed = true;
+ }
}
+ if (iter.rh != NULL && nhops_changed)
+ nhgrp_recompile(iter.rh);
nhops_iter_stop(&iter);
}
}
diff --git a/sys/net/route/route_var.h b/sys/net/route/route_var.h
index df528c93262a..c91002e6c005 100644
--- a/sys/net/route/route_var.h
+++ b/sys/net/route/route_var.h
@@ -312,6 +312,7 @@ int nhgrp_get_addition_group(struct rib_head *rnh,
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_recompile(struct rib_head *rh);
void nhgrp_ref_object(struct nhgrp_object *nhg);
uint32_t nhgrp_get_idx(const struct nhgrp_object *nhg);