git: d19fd2f34922 - main - ip_mroute: Make the routing socket private

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Fri, 13 Feb 2026 19:35:50 UTC
The branch main has been updated by markj:

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

commit d19fd2f349226116f7effb281baa1eb32b8292e7
Author:     Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2026-02-13 16:50:18 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2026-02-13 19:35:18 +0000

    ip_mroute: Make the routing socket private
    
    I have some patches which make ip_mroute and ip6_mroute multi-FIB-aware.
    This enables running per-FIB routing daemons, each of which has a
    separate routing socket.
    
    Several places in the network stack check whether multicast routing is
    configured by checking whether the multicast routing socket is non-NULL.
    This doesn't directly translate in my proposed scheme, as each FIB would
    have its own socket.  I'd like to modify the ip(6)_mroute code to store
    all state, including the socket, in a per-FIB structure.  So, take a
    step towards that and 1) hide the socket, 2) add a boolean flag which
    indicates whether a multicast router is registered.
    
    Reviewed by:    pouria, zlei, glebius, adrian
    MFC after:      2 weeks
    Sponsored by:   Stormshield
    Sponsored by:   Klara, Inc.
    Differential Revision:  https://reviews.freebsd.org/D55236
---
 sys/netinet/igmp.c        |  2 +-
 sys/netinet/ip_input.c    |  3 ++-
 sys/netinet/ip_mroute.c   | 29 +++++++++++++++--------------
 sys/netinet/ip_mroute.h   |  6 +++---
 sys/netinet/ip_output.c   |  3 ++-
 sys/netinet/raw_ip.c      | 10 +++++-----
 sys/netinet6/ip6_input.c  |  2 +-
 sys/netinet6/ip6_mroute.c | 35 ++++++++++++++++++++---------------
 sys/netinet6/ip6_mroute.h |  6 +++---
 sys/netinet6/ip6_output.c |  3 ++-
 sys/netinet6/mld6.c       |  2 +-
 sys/netinet6/raw_ip6.c    |  8 ++++----
 12 files changed, 59 insertions(+), 50 deletions(-)

diff --git a/sys/netinet/igmp.c b/sys/netinet/igmp.c
index 35128dadffe6..00288356cb1f 100644
--- a/sys/netinet/igmp.c
+++ b/sys/netinet/igmp.c
@@ -3489,7 +3489,7 @@ igmp_intr(struct mbuf *m)
 
 	imo.imo_multicast_ttl  = 1;
 	imo.imo_multicast_vif  = -1;
-	imo.imo_multicast_loop = (V_ip_mrouter != NULL);
+	imo.imo_multicast_loop = V_ip_mrouting_enabled;
 
 	/*
 	 * If the user requested that IGMP traffic be explicitly
diff --git a/sys/netinet/ip_input.c b/sys/netinet/ip_input.c
index 4b8294c93967..7de3dc24dc53 100644
--- a/sys/netinet/ip_input.c
+++ b/sys/netinet/ip_input.c
@@ -772,7 +772,8 @@ passin:
 		 * RFC 3927 2.7: Do not forward multicast packets from
 		 * IN_LINKLOCAL.
 		 */
-		if (V_ip_mrouter && !IN_LINKLOCAL(ntohl(ip->ip_src.s_addr))) {
+		if (V_ip_mrouting_enabled &&
+		    !IN_LINKLOCAL(ntohl(ip->ip_src.s_addr))) {
 			/*
 			 * If we are acting as a multicast router, all
 			 * incoming multicast packets are passed to the
diff --git a/sys/netinet/ip_mroute.c b/sys/netinet/ip_mroute.c
index fb42c04548e7..7a197e20a62f 100644
--- a/sys/netinet/ip_mroute.c
+++ b/sys/netinet/ip_mroute.c
@@ -169,6 +169,9 @@ SYSCTL_VNET_PCPUSTAT(_net_inet_ip, OID_AUTO, mrtstat, struct mrtstat,
     mrtstat, "IPv4 Multicast Forwarding Statistics (struct mrtstat, "
     "netinet/ip_mroute.h)");
 
+VNET_DEFINE_STATIC(struct socket *, ip_mrouter);
+#define	V_ip_mrouter		VNET(ip_mrouter)
+
 VNET_DEFINE_STATIC(u_long, mfchash);
 #define	V_mfchash		VNET(mfchash)
 #define	MFCHASH(a, g)							\
@@ -305,7 +308,7 @@ VNET_DEFINE_STATIC(struct ifnet *, multicast_register_if);
 static u_long	X_ip_mcast_src(int);
 static int	X_ip_mforward(struct ip *, struct ifnet *, struct mbuf *,
 		    struct ip_moptions *);
-static int	X_ip_mrouter_done(void);
+static void	X_ip_mrouter_done(struct socket *);
 static int	X_ip_mrouter_get(struct socket *, struct sockopt *);
 static int	X_ip_mrouter_set(struct socket *, struct sockopt *);
 static int	X_legal_vif_num(int);
@@ -435,7 +438,7 @@ X_ip_mrouter_set(struct socket *so, struct sockopt *sopt)
 		error = ip_mrouter_init(so, optval);
 		break;
 	case MRT_DONE:
-		error = ip_mrouter_done();
+		ip_mrouter_done(so);
 		break;
 	case MRT_ADD_VIF:
 		error = sooptcopyin(sopt, &vifc, sizeof vifc, sizeof vifc);
@@ -624,8 +627,7 @@ if_detached_event(void *arg __unused, struct ifnet *ifp)
 	struct ifnet *free_ptr, *multi_leave;
 
 	MRW_WLOCK();
-
-	if (V_ip_mrouter == NULL) {
+	if (!V_ip_mrouting_enabled) {
 		MRW_WUNLOCK();
 		return;
 	}
@@ -740,6 +742,7 @@ ip_mrouter_init(struct socket *so, int version)
 	    curvnet);
 
 	V_ip_mrouter = so;
+	V_ip_mrouting_enabled = true;
 	atomic_add_int(&ip_mrouter_cnt, 1);
 
 	/* This is a mutex required by buf_ring init, but not used internally */
@@ -756,8 +759,8 @@ ip_mrouter_init(struct socket *so, int version)
 /*
  * Disable multicast forwarding.
  */
-static int
-X_ip_mrouter_done(void)
+static void
+X_ip_mrouter_done(struct socket *so)
 {
 	struct ifnet **ifps;
 	int nifp;
@@ -766,22 +769,22 @@ X_ip_mrouter_done(void)
 	struct bw_upcall *bu;
 
 	MRW_TEARDOWN_WLOCK();
-
-	if (V_ip_mrouter == NULL) {
+	if (so != V_ip_mrouter) {
 		MRW_TEARDOWN_WUNLOCK();
-		return (EINVAL);
+		return;
 	}
 
 	/*
 	 * Detach/disable hooks to the reset of the system.
 	 */
 	V_ip_mrouter = NULL;
+	V_ip_mrouting_enabled = false;
 	atomic_subtract_int(&ip_mrouter_cnt, 1);
 	V_mrt_api_config = 0;
 
 	/*
-	 * Wait for all epoch sections to complete to ensure
-	 * V_ip_mrouter = NULL is visible to others.
+	 * Wait for all epoch sections to complete to ensure the new value of
+	 * V_ip_mrouting_enabled is visible to others.
 	 */
 	NET_EPOCH_WAIT();
 
@@ -856,8 +859,6 @@ X_ip_mrouter_done(void)
 	free(ifps, M_TEMP);
 
 	CTR1(KTR_IPMF, "%s: done", __func__);
-
-	return 0;
 }
 
 /*
@@ -2872,7 +2873,7 @@ ip_mroute_modevent(module_t mod, int type, void *unused)
 		MRW_WLOCK();
 		if (ip_mrouter_cnt != 0) {
 			MRW_WUNLOCK();
-			return (EINVAL);
+			return (EBUSY);
 		}
 		ip_mrouter_unloading = 1;
 		MRW_WUNLOCK();
diff --git a/sys/netinet/ip_mroute.h b/sys/netinet/ip_mroute.h
index 6f09006ec9e2..5c2527ea64e5 100644
--- a/sys/netinet/ip_mroute.h
+++ b/sys/netinet/ip_mroute.h
@@ -356,8 +356,8 @@ struct bw_meter {
 };
 
 #ifdef _KERNEL
-VNET_DECLARE(struct socket *, ip_mrouter);	/* multicast routing daemon */
-#define	V_ip_mrouter		VNET(ip_mrouter)
+VNET_DECLARE(bool, ip_mrouting_enabled);
+#define	V_ip_mrouting_enabled	VNET(ip_mrouting_enabled)
 
 struct ifnet;
 struct ip;
@@ -369,7 +369,7 @@ struct sockopt;
 extern u_long	(*ip_mcast_src)(int);
 extern int	(*ip_mforward)(struct ip *, struct ifnet *, struct mbuf *,
 		    struct ip_moptions *);
-extern int	(*ip_mrouter_done)(void);
+extern void	(*ip_mrouter_done)(struct socket *);
 extern int	(*ip_mrouter_get)(struct socket *, struct sockopt *);
 extern int	(*ip_mrouter_set)(struct socket *, struct sockopt *);
 
diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c
index 1edc4906b542..8af44c6a200d 100644
--- a/sys/netinet/ip_output.c
+++ b/sys/netinet/ip_output.c
@@ -610,7 +610,8 @@ again:
 			 * above, will be forwarded by the ip_input() routine,
 			 * if necessary.
 			 */
-			if (V_ip_mrouter && (flags & IP_FORWARDING) == 0) {
+			if (V_ip_mrouting_enabled &&
+			    (flags & IP_FORWARDING) == 0) {
 				/*
 				 * If rsvp daemon is not running, do not
 				 * set ip_moptions. This ensures that the packet
diff --git a/sys/netinet/raw_ip.c b/sys/netinet/raw_ip.c
index bfe608be6b36..e0e7aed04cd0 100644
--- a/sys/netinet/raw_ip.c
+++ b/sys/netinet/raw_ip.c
@@ -106,16 +106,16 @@ int	(*ng_ipfw_input_p)(struct mbuf **, struct ip_fw_args *, bool);
  */
 
 /*
- * The socket used to communicate with the multicast routing daemon.
+ * A per-VNET flag indicating whether multicast routing is enabled.
  */
-VNET_DEFINE(struct socket *, ip_mrouter);
+VNET_DEFINE(bool, ip_mrouting_enabled);
 
 /*
  * The various mrouter and rsvp functions.
  */
 int (*ip_mrouter_set)(struct socket *, struct sockopt *);
 int (*ip_mrouter_get)(struct socket *, struct sockopt *);
-int (*ip_mrouter_done)(void);
+void (*ip_mrouter_done)(struct socket *);
 int (*ip_mforward)(struct ip *, struct ifnet *, struct mbuf *,
 		   struct ip_moptions *);
 int (*mrt_ioctl)(u_long, caddr_t, int);
@@ -860,8 +860,8 @@ rip_detach(struct socket *so)
 	    ("rip_detach: not closed"));
 
 	/* Disable mrouter first */
-	if (so == V_ip_mrouter && ip_mrouter_done)
-		ip_mrouter_done();
+	if (ip_mrouter_done != NULL)
+		ip_mrouter_done(so);
 
 	INP_WLOCK(inp);
 	INP_HASH_WLOCK(&V_ripcbinfo);
diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c
index 656b3b996f90..acb690c2033d 100644
--- a/sys/netinet6/ip6_input.c
+++ b/sys/netinet6/ip6_input.c
@@ -891,7 +891,7 @@ passin:
 	/*
 	 * Forward if desirable.
 	 */
-	if (V_ip6_mrouter &&
+	if (V_ip6_mrouting_enabled &&
 	    IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
 		/*
 		 * If we are acting as a multicast router, all
diff --git a/sys/netinet6/ip6_mroute.c b/sys/netinet6/ip6_mroute.c
index 46378cca5e21..a8313e1753a6 100644
--- a/sys/netinet6/ip6_mroute.c
+++ b/sys/netinet6/ip6_mroute.c
@@ -172,6 +172,9 @@ static struct sx mrouter6_mtx;
 #define	MROUTER6_LOCK_INIT()	sx_init(MROUTER6_LOCKPTR(), "mrouter6")
 #define	MROUTER6_LOCK_DESTROY()	sx_destroy(MROUTER6_LOCKPTR())
 
+VNET_DEFINE_STATIC(struct socket *, ip6_mrouter);
+#define	V_ip6_mrouter		VNET(ip6_mrouter)
+
 static struct mf6c *mf6ctable[MF6CTBLSIZ];
 SYSCTL_OPAQUE(_net_inet6_ip6, OID_AUTO, mf6ctable, CTLFLAG_RD,
     &mf6ctable, sizeof(mf6ctable), "S,*mf6ctable[MF6CTBLSIZ]",
@@ -335,7 +338,7 @@ static int get_sg_cnt(struct sioc_sg_req6 *);
 static struct callout expire_upcalls_ch;
 
 static int X_ip6_mforward(struct ip6_hdr *, struct ifnet *, struct mbuf *);
-static int X_ip6_mrouter_done(void);
+static void X_ip6_mrouter_done(struct socket *);
 static int X_ip6_mrouter_set(struct socket *, struct sockopt *);
 static int X_ip6_mrouter_get(struct socket *, struct sockopt *);
 static int X_mrt6_ioctl(u_long, caddr_t);
@@ -383,7 +386,7 @@ X_ip6_mrouter_set(struct socket *so, struct sockopt *sopt)
 		error = ip6_mrouter_init(so, optval, sopt->sopt_name);
 		break;
 	case MRT6_DONE:
-		error = X_ip6_mrouter_done();
+		X_ip6_mrouter_done(so);
 		break;
 	case MRT6_ADD_MIF:
 		error = sooptcopyin(sopt, &mifc, sizeof(mifc), sizeof(mifc));
@@ -556,6 +559,8 @@ ip6_mrouter_init(struct socket *so, int v, int cmd)
 		return (EADDRINUSE);
 	}
 
+	MFC6_LOCK();
+	V_ip6_mrouting_enabled = true;
 	V_ip6_mrouter = so;
 	V_ip6_mrouter_ver = cmd;
 
@@ -568,6 +573,7 @@ ip6_mrouter_init(struct socket *so, int v, int cmd)
 	callout_reset(&expire_upcalls_ch, EXPIRE_TIMEOUT,
 	    expire_upcalls, NULL);
 
+	MFC6_UNLOCK();
 	MROUTER6_UNLOCK();
 
 	MRT6_DLOG(DEBUG_ANY, "finished");
@@ -578,8 +584,8 @@ ip6_mrouter_init(struct socket *so, int v, int cmd)
 /*
  * Disable IPv6 multicast forwarding.
  */
-static int
-X_ip6_mrouter_done(void)
+static void
+X_ip6_mrouter_done(struct socket *so)
 {
 	mifi_t mifi;
 	u_long i;
@@ -588,9 +594,9 @@ X_ip6_mrouter_done(void)
 
 	MROUTER6_LOCK();
 
-	if (V_ip6_mrouter == NULL) {
+	if (V_ip6_mrouter != so) {
 		MROUTER6_UNLOCK();
-		return (EINVAL);
+		return;
 	}
 
 	/*
@@ -603,6 +609,7 @@ X_ip6_mrouter_done(void)
 			if_allmulti(mif6table[mifi].m6_ifp, 0);
 		}
 	}
+	MFC6_LOCK();
 	bzero((caddr_t)mif6table, sizeof(mif6table));
 	nummifs = 0;
 
@@ -611,7 +618,6 @@ X_ip6_mrouter_done(void)
 	/*
 	 * Free all multicast forwarding cache entries.
 	 */
-	MFC6_LOCK();
 	for (i = 0; i < MF6CTBLSIZ; i++) {
 		rt = mf6ctable[i];
 		while (rt) {
@@ -630,6 +636,10 @@ X_ip6_mrouter_done(void)
 		}
 	}
 	bzero((caddr_t)mf6ctable, sizeof(mf6ctable));
+
+	V_ip6_mrouter = NULL;
+	V_ip6_mrouting_enabled = false;
+	V_ip6_mrouter_ver = 0;
 	MFC6_UNLOCK();
 
 	callout_drain(&expire_upcalls_ch);
@@ -644,13 +654,8 @@ X_ip6_mrouter_done(void)
 		multicast_register_if6 = NULL;
 	}
 
-	V_ip6_mrouter = NULL;
-	V_ip6_mrouter_ver = 0;
-
 	MROUTER6_UNLOCK();
 	MRT6_DLOG(DEBUG_ANY, "finished");
-
-	return (0);
 }
 
 static struct sockaddr_in6 sin6 = { sizeof(sin6), AF_INET6 };
@@ -1903,14 +1908,14 @@ ip6_mroute_modevent(module_t mod, int type, void *unused)
 		break;
 
 	case MOD_UNLOAD:
-		if (V_ip6_mrouter != NULL)
-			return EINVAL;
+		if (V_ip6_mrouting_enabled)
+			return (EBUSY);
 
 		if (pim6_encap_cookie) {
 			ip6_encap_detach(pim6_encap_cookie);
 			pim6_encap_cookie = NULL;
 		}
-		X_ip6_mrouter_done();
+
 		ip6_mforward = NULL;
 		ip6_mrouter_done = NULL;
 		ip6_mrouter_get = NULL;
diff --git a/sys/netinet6/ip6_mroute.h b/sys/netinet6/ip6_mroute.h
index 2f298df6a1ce..fda8c4308e86 100644
--- a/sys/netinet6/ip6_mroute.h
+++ b/sys/netinet6/ip6_mroute.h
@@ -273,8 +273,8 @@ struct rtdetq {		/* XXX: rtdetq is also defined in ip_mroute.h */
 #endif /* _KERNEL || KERNEL */
 
 #ifdef _KERNEL
-VNET_DECLARE(struct socket *, ip6_mrouter);	/* multicast routing daemon */
-#define	V_ip6_mrouter			VNET(ip6_mrouter)
+VNET_DECLARE(bool, ip6_mrouting_enabled);
+#define	V_ip6_mrouting_enabled	VNET(ip6_mrouting_enabled)
 
 struct ifnet;
 struct ip6_hdr;
@@ -286,7 +286,7 @@ extern int	(*ip6_mforward)(struct ip6_hdr *, struct ifnet *,
 		    struct mbuf *);
 extern int	(*ip6_mrouter_set)(struct socket *so, struct sockopt *sopt);
 extern int	(*ip6_mrouter_get)(struct socket *so, struct sockopt *sopt);
-extern int	(*ip6_mrouter_done)(void);
+extern void	(*ip6_mrouter_done)(struct socket *so);
 
 extern int	(*mrt6_ioctl)(u_long, caddr_t);
 #endif /* _KERNEL */
diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c
index 4b0c08b4e493..b7ba3c4c080c 100644
--- a/sys/netinet6/ip6_output.c
+++ b/sys/netinet6/ip6_output.c
@@ -890,7 +890,8 @@ nonh6lookup:
 			 * above, will be forwarded by the ip6_input() routine,
 			 * if necessary.
 			 */
-			if (V_ip6_mrouter && (flags & IPV6_FORWARDING) == 0) {
+			if (V_ip6_mrouting_enabled &&
+			    (flags & IPV6_FORWARDING) == 0) {
 				/*
 				 * XXX: ip6_mforward expects that rcvif is NULL
 				 * when it is called from the originating path.
diff --git a/sys/netinet6/mld6.c b/sys/netinet6/mld6.c
index d00a9c13ec8d..2b6cd3437d36 100644
--- a/sys/netinet6/mld6.c
+++ b/sys/netinet6/mld6.c
@@ -3063,7 +3063,7 @@ mld_dispatch_packet(struct mbuf *m)
 	}
 
 	im6o.im6o_multicast_hlim  = 1;
-	im6o.im6o_multicast_loop = (V_ip6_mrouter != NULL);
+	im6o.im6o_multicast_loop = V_ip6_mrouting_enabled;
 	im6o.im6o_multicast_ifp = ifp;
 
 	if (m->m_flags & M_MLDV1) {
diff --git a/sys/netinet6/raw_ip6.c b/sys/netinet6/raw_ip6.c
index 7deb605c07a2..f3153b5435e2 100644
--- a/sys/netinet6/raw_ip6.c
+++ b/sys/netinet6/raw_ip6.c
@@ -137,14 +137,14 @@ VNET_PCPUSTAT_SYSUNINIT(rip6stat);
 /*
  * The socket used to communicate with the multicast routing daemon.
  */
-VNET_DEFINE(struct socket *, ip6_mrouter);
+VNET_DEFINE(bool, ip6_mrouting_enabled);
 
 /*
  * The various mrouter functions.
  */
 int (*ip6_mrouter_set)(struct socket *, struct sockopt *);
 int (*ip6_mrouter_get)(struct socket *, struct sockopt *);
-int (*ip6_mrouter_done)(void);
+void (*ip6_mrouter_done)(struct socket *);
 int (*ip6_mforward)(struct ip6_hdr *, struct ifnet *, struct mbuf *);
 int (*mrt6_ioctl)(u_long, caddr_t);
 
@@ -694,8 +694,8 @@ rip6_detach(struct socket *so)
 	inp = sotoinpcb(so);
 	KASSERT(inp != NULL, ("rip6_detach: inp == NULL"));
 
-	if (so == V_ip6_mrouter && ip6_mrouter_done)
-		ip6_mrouter_done();
+	if (ip6_mrouter_done != NULL)
+		ip6_mrouter_done(so);
 	/* xxx: RSVP */
 	INP_WLOCK(inp);
 	free(inp->in6p_icmp6filt, M_PCB);