On Sat, 11 Jul 2026, Pouria Mousavizadeh Tehrani wrote:

The branch main has been updated by pouria:

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

commit d05d1f25608230edd300d59b96da6521b409d4f3
Author:     Pouria Mousavizadeh Tehrani <[email protected]>
AuthorDate: 2026-07-08 19:55:46 +0000
Commit:     Pouria Mousavizadeh Tehrani <[email protected]>
CommitDate: 2026-07-11 18:23:05 +0000

   routing: Subscribe nhops to ifnet link events

   Update nexthop flags with interface link status events and
   instead of checking link status of interface for every packet
   only check the reachability flag of the final nexthop.

   Reviewed by:    glebius
   Discussed with: markj
   Differential Revision: https://reviews.freebsd.org/D57375
---
sys/net/route.h           |  4 +--
sys/net/route/nhop.h      |  8 +-----
sys/net/route/nhop_ctl.c  | 68 +++++++++++++++++++++++++++++++++++++++++++++--
sys/net/route/route_ctl.h |  1 +
sys/netinet/in_fib.c      |  6 ++---
sys/netinet6/in6_fib.c    |  6 ++---
6 files changed, 75 insertions(+), 18 deletions(-)

diff --git a/sys/net/route.h b/sys/net/route.h
index d8cc12a39c61..b4440de19d96 100644
--- a/sys/net/route.h
+++ b/sys/net/route.h
@@ -205,6 +205,7 @@ EVENTHANDLER_DECLARE(rtnumfibs_change, rtnumfibs_change_t);
#define NHF_BROADCAST           0x0100  /* RTF_BROADCAST */
#define NHF_GATEWAY             0x0200  /* RTF_GATEWAY */
#define NHF_HOST                0x0400  /* RTF_HOST */
+#define        NHF_INVALID             0x0800  /* Nexthop is unreachable */

/* Nexthop request flags */
#define NHR_NONE                0x00    /* empty flags field */
@@ -368,9 +369,6 @@ struct rt_addrinfo {

#ifdef _KERNEL

-#define RT_LINK_IS_UP(ifp)     (!((ifp)->if_capabilities & IFCAP_LINKSTATE) \
-                                || (ifp)->if_link_state == LINK_STATE_UP)
-
#define RO_NHFREE(_ro) do {                                     \
        if ((_ro)->ro_nh) {                                  \
                NH_FREE((_ro)->ro_nh);                               \
diff --git a/sys/net/route/nhop.h b/sys/net/route/nhop.h
index 6c62ae2f2f5f..0ae41cbf8292 100644
--- a/sys/net/route/nhop.h
+++ b/sys/net/route/nhop.h
@@ -145,14 +145,8 @@ struct nhop_object {

/*
 * Nhop validness.
- *
- * Currently we verify whether link is up or not on every packet, which can be
- *   quite costy.
- * TODO: subscribe for the interface notifications and update the nexthops
- *  with NHF_INVALID flag.
 */
-
-#define        NH_IS_VALID(_nh)        RT_LINK_IS_UP((_nh)->nh_ifp)
+#define        NH_IS_VALID(_nh)        (!((_nh)->nh_flags & NHF_INVALID))
#define NH_IS_NHGRP(_nh)        ((_nh)->nh_flags & NHF_MULTIPATH)

#define NH_FREE(_nh) do {                                       \
diff --git a/sys/net/route/nhop_ctl.c b/sys/net/route/nhop_ctl.c
index 9ef5bbc74a92..4f013908b338 100644
--- a/sys/net/route/nhop_ctl.c
+++ b/sys/net/route/nhop_ctl.c

You need to include eventhandler.h or what it is (as it's currently only leaked 
in through vnet code):

     24 /sys/net/route/nhop_ctl.c:114:2: error: call to undeclared function 
'EVENTHANDLER_REGISTER'; ISO C99 and later do not support implicit function 
declarations [-Werror,-Wimplicit-function-declaration]
     25   114 |         EVENTHANDLER_REGISTER(ifnet_event, nhops_ifnet_event, 
NULL,
     26       |         ^
     27 /sys/net/route/nhop_ctl.c:114:24: error: use of undeclared identifier 
'ifnet_event'
     28   114 |         EVENTHANDLER_REGISTER(ifnet_event, nhops_ifnet_event, 
NULL,
     29       |                               ^~~~~~~~~~~
     30 /sys/net/route/nhop_ctl.c:115:6: error: use of undeclared identifier 
'EVENTHANDLER_PRI_ANY'
     31   115 |             EVENTHANDLER_PRI_ANY);
     32       |             ^~~~~~~~~~~~~~~~~~~~
     33 /sys/net/route/nhop_ctl.c:116:24: error: use of undeclared identifier 
'ifnet_link_event'
     34   116 |         EVENTHANDLER_REGISTER(ifnet_link_event, 
nhops_ifnet_link_event, NULL,
     35       |                               ^~~~~~~~~~~~~~~~
     36 /sys/net/route/nhop_ctl.c:117:6: error: use of undeclared identifier 
'EVENTHANDLER_PRI_ANY'
     37   117 |             EVENTHANDLER_PRI_ANY);
     38       |             ^~~~~~~~~~~~~~~~~~~~
     39 5 errors generated.
     40 --- nhop_ctl.o ---
     41 *** [nhop_ctl.o] Error code 1



@@ -90,6 +90,8 @@ static void fill_sdl_from_ifp(struct sockaddr_dl_short *sdl, 
const struct ifnet

static void destroy_nhop_epoch(epoch_context_t ctx);
static void destroy_nhop(struct nhop_object *nh);
+static void nhops_ifnet_event(void *arg, struct ifnet *ifp, int state);
+static void nhops_ifnet_link_event(void *arg, struct ifnet *ifp, int state);

_Static_assert(__offsetof(struct nhop_object, nh_ifp) == 32,
    "nhop_object: wrong nh_ifp offset");
@@ -109,6 +111,10 @@ nhops_init(void)
        nhops_zone = uma_zcreate("routing nhops",
            NHOP_OBJECT_ALIGNED_SIZE + NHOP_PRIV_ALIGNED_SIZE,
            NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
+       EVENTHANDLER_REGISTER(ifnet_event, nhops_ifnet_event, NULL,
+           EVENTHANDLER_PRI_ANY);
+       EVENTHANDLER_REGISTER(ifnet_link_event, nhops_ifnet_link_event, NULL,
+           EVENTHANDLER_PRI_ANY);
}

/*
@@ -1109,7 +1115,10 @@ nhops_iter_start(struct nhop_iter *iter)
        if (iter->rh != NULL) {
                struct nh_control *ctl = iter->rh->nh_control;

-               NHOPS_RLOCK(ctl);
+               if (iter->wlock)
+                       NHOPS_WLOCK(ctl);
+               else
+                       NHOPS_RLOCK(ctl);

                iter->_i = 0;
                iter->_next = CHT_FIRST(&ctl->nh_head, iter->_i);
@@ -1147,7 +1156,10 @@ nhops_iter_stop(struct nhop_iter *iter)
        if (iter->rh != NULL) {
                struct nh_control *ctl = iter->rh->nh_control;

-               NHOPS_RUNLOCK(ctl);
+               if (iter->wlock)
+                       NHOPS_WUNLOCK(ctl);
+               else
+                       NHOPS_RUNLOCK(ctl);
        }
}

@@ -1317,3 +1329,55 @@ nhops_dump_sysctl(struct rib_head *rh, struct sysctl_req 
*w)

        return (0);
}
+
+static void
+nhops_ifnet_state_changed(struct ifnet *ifp, bool status)
+{
+       struct nhop_object *nh;
+       struct nhop_iter iter = { .fibnum = ifp->if_fib, .wlock = true };
+
+       for (iter.family = 1; iter.family <= AF_MAX; iter.family++) {
+               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)
+                               nh->nh_flags &= ~NHF_INVALID;
+                       else
+                               nh->nh_flags |= NHF_INVALID;
+               }
+               nhops_iter_stop(&iter);
+       }
+}
+
+static void
+nhops_ifnet_event(void *arg __unused, struct ifnet *ifp, int state)
+{
+
+       if ((ifp->if_flags & IFF_DYING) != 0 ||
+           (state != IFNET_EVENT_UP && state != IFNET_EVENT_DOWN))
+               return;
+
+       nhops_ifnet_state_changed(ifp, state == IFNET_EVENT_UP);
+}
+
+static void
+nhops_ifnet_link_event(void *arg __unused, struct ifnet *ifp, int state)
+{
+#ifdef VIMAGE
+       /*
+        * rib_head will be calculated from V_tables in rt_tables_get_rnh
+        * and the VNET is destroyed.
+        */
+       if (VNET_IS_SHUTTING_DOWN(ifp->if_vnet))
+               return;
+#endif
+
+       if ((ifp->if_flags & IFF_DYING) != 0 ||
+           (state != LINK_STATE_UP && state != LINK_STATE_DOWN))
+               return;
+
+       nhops_ifnet_state_changed(ifp, state == LINK_STATE_UP);
+}
diff --git a/sys/net/route/route_ctl.h b/sys/net/route/route_ctl.h
index 845df8ce1fbe..2e34b2a35c25 100644
--- a/sys/net/route/route_ctl.h
+++ b/sys/net/route/route_ctl.h
@@ -164,6 +164,7 @@ struct nhop_iter {
        struct rib_head         *rh;
        int                     _i;
        struct nhop_priv        *_next;
+       const bool              wlock;
};

struct nhop_object *nhops_iter_start(struct nhop_iter *iter);
diff --git a/sys/netinet/in_fib.c b/sys/netinet/in_fib.c
index d8e373bf3dc2..d864bce07315 100644
--- a/sys/netinet/in_fib.c
+++ b/sys/netinet/in_fib.c
@@ -117,7 +117,7 @@ fib4_lookup(uint32_t fibnum, struct in_addr dst, uint32_t 
scopeid,
        if (nh != NULL) {
                nh = nhop_select(nh, flowid);
                /* Ensure route & ifp is UP */
-               if (RT_LINK_IS_UP(nh->nh_ifp)) {
+               if (NH_IS_VALID(nh)) {
                        if (flags & NHR_REF)
                                nhop_ref_object(nh);
                        return (nh);
@@ -154,7 +154,7 @@ fib4_lookup(uint32_t fibnum, struct in_addr dst, uint32_t 
scopeid,
        if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) {
                nh = nhop_select((RNTORT(rn))->rt_nhop, flowid);
                /* Ensure route & ifp is UP */
-               if (RT_LINK_IS_UP(nh->nh_ifp)) {
+               if (NH_IS_VALID(nh)) {
                        if (flags & NHR_REF)
                                nhop_ref_object(nh);
                        RIB_RUNLOCK(rh);
@@ -320,7 +320,7 @@ fib4_lookup_debugnet(uint32_t fibnum, struct in_addr dst, 
uint32_t scopeid,
        if (rt != NULL) {
                struct nhop_object *nh = nhop_select(rnd.rnd_nhop, 0);
                /* Ensure route & ifp is UP */
-               if (RT_LINK_IS_UP(nh->nh_ifp))
+               if (NH_IS_VALID(nh))
                        return (nh);
        }

diff --git a/sys/netinet6/in6_fib.c b/sys/netinet6/in6_fib.c
index c851a5bd14c5..594a9dc0a24e 100644
--- a/sys/netinet6/in6_fib.c
+++ b/sys/netinet6/in6_fib.c
@@ -125,7 +125,7 @@ fib6_lookup(uint32_t fibnum, const struct in6_addr *dst6,
        if (nh != NULL) {
                nh = nhop_select(nh, flowid);
                /* Ensure route & ifp is UP */
-               if (RT_LINK_IS_UP(nh->nh_ifp)) {
+               if (NH_IS_VALID(nh)) {
                        if (flags & NHR_REF)
                                nhop_ref_object(nh);
                        return (nh);
@@ -163,7 +163,7 @@ fib6_lookup(uint32_t fibnum, const struct in6_addr *dst6,
        if (rn != NULL && ((rn->rn_flags & RNF_ROOT) == 0)) {
                nh = nhop_select((RNTORT(rn))->rt_nhop, flowid);
                /* Ensure route & ifp is UP */
-               if (RT_LINK_IS_UP(nh->nh_ifp)) {
+               if (NH_IS_VALID(nh)) {
                        if (flags & NHR_REF)
                                nhop_ref_object(nh);
                        RIB_RUNLOCK(rh);
@@ -335,7 +335,7 @@ fib6_lookup_debugnet(uint32_t fibnum, const struct in6_addr 
*dst6,
        if (rt != NULL) {
                struct nhop_object *nh = nhop_select(rnd.rnd_nhop, 0);
                /* Ensure route & ifp is UP */
-               if (RT_LINK_IS_UP(nh->nh_ifp))
+               if (NH_IS_VALID(nh))
                        return (nh);
        }




--
Bjoern A. Zeeb                                                     r15:7

Reply via email to