Re: ip6 multicast router lock

Alexander Bluhm <[email protected]> Wed, 5 Aug 2026 23:52:03 +0200
Newsgroups gmane.os.openbsd.tech
Message-ID <[email protected]>
On Thu, Jul 16, 2026 at 11:28:05PM +0200, Alexander Bluhm wrote:
> Hi,
> 
> This is the IPv6 counter part of my multicast router locking diff.
> The IPv4 part tries to make both families similar.
> 
> Also fix checking the return code of mfc_add().
> 
> Unlocking is not completed yet, netlock and kernel lock stay.
> 
> ok?

anyone?

Index: netinet/ip_mroute.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/ip_mroute.c,v
diff -u -p -r1.154 ip_mroute.c
--- netinet/ip_mroute.c	16 Jul 2026 12:21:40 -0000	1.154
+++ netinet/ip_mroute.c	5 Aug 2026 13:42:56 -0000
@@ -116,7 +116,6 @@ int		 ip_mrtproto = IGMP_DVMRP;	/* [I] f
 
 struct cpumem *mrtcounters;
 
-struct rtentry	*mfc_find(struct ifnet *, struct in_addr *, unsigned int);
 int get_sg_cnt(unsigned int, struct sioc_sg_req *);
 int get_vif_cnt(unsigned int, struct sioc_vif_req *);
 int mrt_rtwalk_mfcsysctl(struct rtentry *, void *, unsigned int);
@@ -126,7 +125,6 @@ int get_version(struct mbuf *);
 int add_vif(struct socket *, struct mbuf *);
 int del_vif(struct socket *, struct mbuf *);
 void update_mfc_params(struct mfcctl2 *, int, unsigned int);
-void mfc_expire_route(struct rtentry *, u_int);
 int mfc_add(struct mfcctl2 *, struct in_addr *, struct in_addr *,
     int, unsigned int, int);
 int add_mfc(struct socket *, struct mbuf *);
@@ -136,7 +134,9 @@ int get_api_support(struct mbuf *);
 int get_api_config(struct socket *, struct mbuf *);
 int socket_send(struct socket *, struct mbuf *, struct sockaddr_in *);
 int ip_mdq(struct mbuf *, struct ifnet *, struct rtentry *, int);
+void mfc_expire_route(struct rtentry *, u_int);
 struct ifnet *if_lookupbyvif(vifi_t, unsigned int);
+struct rtentry *mfc_find(struct ifnet *, struct in_addr *, unsigned int);
 struct rtentry *rt_mcast_add(struct ifnet *, struct sockaddr *,
     struct sockaddr *);
 void mrt_mcast_del(struct rtentry *, unsigned int);
@@ -222,8 +222,19 @@ ip_mrouter_set(struct socket *so, int op
 int
 ip_mrouter_get(struct socket *so, int optname, struct mbuf *m)
 {
+	struct inpcb *inp = sotoinpcb(so);
+	unsigned int rtableid = inp->inp_rtableid;
 	int error;
 
+	soassertlocked(so);
+
+	rw_enter_read(&mrt_routerlock);
+
+	if (so != mrt_mrouter[rtableid]) {
+		error = EPROTONOSUPPORT;
+		goto out;
+	}
+
 	switch (optname) {
 	case MRT_VERSION:
 		error = get_version(m);
@@ -238,6 +249,8 @@ ip_mrouter_get(struct socket *so, int op
 		error = ENOPROTOOPT;
 		break;
 	}
+ out:
+	rw_exit_read(&mrt_routerlock);
 
 	return (error);
 }
@@ -263,17 +276,22 @@ mrt_init(void)
 int
 mrt_ioctl(struct socket *so, u_long cmd, caddr_t data)
 {
-	struct inpcb *inp = sotoinpcb(so);
+	struct inpcb *inp;
+	unsigned int rtableid;
 	int error;
 
-	if (inp == NULL)
-		return (ENOTCONN);
+	solock_shared(so);
 
+	inp = sotoinpcb(so);
+	if (inp == NULL) {
+		error = ENOTCONN;
+		goto sounlock;
+	}
+	rtableid = inp->inp_rtableid;
 
-	NET_LOCK_SHARED();
 	rw_enter_read(&mrt_routerlock);
 
-	if (so != mrt_mrouter[inp->inp_rtableid]) {
+	if (so != mrt_mrouter[rtableid]) {
 		error =  EPROTONOSUPPORT;
 		goto out;
 	}
@@ -281,24 +299,22 @@ mrt_ioctl(struct socket *so, u_long cmd,
 	switch (cmd) {
 	case SIOCGETVIFCNT:
 		KERNEL_LOCK();
-		error = get_vif_cnt(inp->inp_rtableid,
-		    (struct sioc_vif_req *)data);
+		error = get_vif_cnt(rtableid, (struct sioc_vif_req *)data);
 		KERNEL_UNLOCK();
 		break;
 	case SIOCGETSGCNT:
 		KERNEL_LOCK();
-		error = get_sg_cnt(inp->inp_rtableid,
-		    (struct sioc_sg_req *)data);
+		error = get_sg_cnt(rtableid, (struct sioc_sg_req *)data);
 		KERNEL_UNLOCK();
 		break;
 	default:
 		error = ENOTTY;
 		break;
 	}
-
  out:
 	rw_exit_read(&mrt_routerlock);
-	NET_UNLOCK_SHARED();
+ sounlock:
+	sounlock_shared(so);
 
 	return (error);
 }
@@ -584,6 +600,8 @@ ip_mrouter_init(struct socket *so, struc
 	unsigned int rtableid = inp->inp_rtableid;
 	int *v;
 
+	soassertlocked(so);
+
 	if (so->so_type != SOCK_RAW ||
 	    so->so_proto->pr_protocol != IPPROTO_IGMP)
 		return (EOPNOTSUPP);
@@ -602,6 +620,7 @@ ip_mrouter_init(struct socket *so, struc
 		return (EADDRINUSE);
 	}
 	mrt_mrouter[rtableid] = soref(so);
+	mrt_api_config[rtableid] = 0;
 
 	rw_exit_write(&mrt_routerlock);
 
@@ -630,11 +649,11 @@ ip_mrouter_done(struct socket *so)
 	unsigned int rtableid = inp->inp_rtableid;
 	int error;
 
-	NET_ASSERT_LOCKED();
+	soassertlocked(so);
 
 	rw_enter_write(&mrt_routerlock);
 
-	if (so != mrt_mrouter[inp->inp_rtableid]) {
+	if (so != mrt_mrouter[rtableid]) {
 		rw_exit_write(&mrt_routerlock);
 		return (EPROTONOSUPPORT);
 	}
@@ -666,8 +685,8 @@ ip_mrouter_done(struct socket *so)
 		vif_delete(ifp);
 	}
 
-	mrt_api_config[rtableid] = 0;
 	mrt_mrouter[rtableid] = NULL;
+	mrt_api_config[rtableid] = 0;
 	sorele(so);
 
 	rw_exit_write(&mrt_routerlock);
@@ -682,6 +701,7 @@ get_version(struct mbuf *m)
 
 	*v = 0x0305;	/* XXX !!!! */
 	m->m_len = sizeof(int);
+
 	return (0);
 }
 
@@ -696,6 +716,8 @@ set_api_config(struct socket *so, struct
 	uint32_t *apival;
 	unsigned int rtableid = inp->inp_rtableid;
 
+	NET_ASSERT_LOCKED();
+
 	if (m == NULL || m->m_len < sizeof(u_int32_t))
 		return (EINVAL);
 
@@ -750,7 +772,6 @@ get_api_support(struct mbuf *m)
 		return (EINVAL);
 
 	apival = mtod(m, uint32_t *);
-
 	*apival = mrt_api_support;
 
 	return (0);
@@ -766,18 +787,13 @@ get_api_config(struct socket *so, struct
 	uint32_t *apival;
 	unsigned int rtableid = inp->inp_rtableid;
 
+	rw_assert_rdlock(&mrt_routerlock);
+
 	if (m == NULL || m->m_len < sizeof(u_int32_t))
 		return (EINVAL);
 
 	apival = mtod(m, uint32_t *);
-
-	rw_enter_read(&mrt_routerlock);
-	if (so != mrt_mrouter[inp->inp_rtableid]) {
-		rw_exit_read(&mrt_routerlock);
-		return (EPROTONOSUPPORT);
-	}
 	*apival = mrt_api_config[rtableid];
-	rw_exit_read(&mrt_routerlock);
 
 	return (0);
 }
@@ -794,13 +810,13 @@ add_vif(struct socket *so, struct mbuf *
 	int error;
 	unsigned int rtableid = inp->inp_rtableid;
 
-	NET_ASSERT_LOCKED();
+	soassertlocked(so);
 
 	if (m == NULL || m->m_len < sizeof(struct vifctl))
 		return (EINVAL);
 
 	rw_enter_read(&mrt_routerlock);
-	if (so != mrt_mrouter[inp->inp_rtableid]) {
+	if (so != mrt_mrouter[rtableid]) {
 		rw_exit_read(&mrt_routerlock);
 		return (EPROTONOSUPPORT);
 	}
@@ -868,13 +884,13 @@ del_vif(struct socket *so, struct mbuf *
 	vifi_t *vifip;
 	unsigned int rtableid = inp->inp_rtableid;
 
-	NET_ASSERT_LOCKED();
+	soassertlocked(so);
 
 	if (m == NULL || m->m_len < sizeof(vifi_t))
 		return (EINVAL);
 
 	rw_enter_read(&mrt_routerlock);
-	if (so != mrt_mrouter[inp->inp_rtableid]) {
+	if (so != mrt_mrouter[rtableid]) {
 		rw_exit_read(&mrt_routerlock);
 		return (EPROTONOSUPPORT);
 	}
@@ -885,16 +901,17 @@ del_vif(struct socket *so, struct mbuf *
 		return (EADDRNOTAVAIL);
 
 	vif_delete(ifp);
+
 	return (0);
 }
 
 void
 vif_delete(struct ifnet *ifp)
 {
-	struct vif	*v;
+	struct vif	*v = ifp->if_mcast;
 	struct ifreq	 ifr;
 
-	if ((v = ifp->if_mcast) == NULL)
+	if (v == NULL)
 		return;
 
 	ifp->if_mcast = NULL;
@@ -1115,10 +1132,10 @@ add_mfc(struct socket *so, struct mbuf *
 	uint32_t api_config;
 	unsigned int rtableid = inp->inp_rtableid;
 
-	NET_ASSERT_LOCKED();
+	soassertlocked(so);
 
 	rw_enter_read(&mrt_routerlock);
-	if (so != mrt_mrouter[inp->inp_rtableid]) {
+	if (so != mrt_mrouter[rtableid]) {
 		rw_exit_read(&mrt_routerlock);
 		return (EPROTONOSUPPORT);
 	}
@@ -1144,11 +1161,8 @@ add_mfc(struct socket *so, struct mbuf *
 		    sizeof(mfcctl2) - sizeof(struct mfcctl));
 	}
 
-	if (mfc_add(&mfcctl2, &mfcctl2.mfcc_origin, &mfcctl2.mfcc_mcastgrp,
-	    mfcctl2.mfcc_parent, rtableid, M_WAITOK) == -1)
-		return (EINVAL);
-
-	return (0);
+	return (mfc_add(&mfcctl2, &mfcctl2.mfcc_origin, &mfcctl2.mfcc_mcastgrp,
+	    mfcctl2.mfcc_parent, rtableid, M_WAITOK));
 }
 
 int
@@ -1161,7 +1175,7 @@ del_mfc(struct socket *so, struct mbuf *
 	struct mfcctl *mp;
 	unsigned int rtableid = inp->inp_rtableid;
 
-	NET_ASSERT_LOCKED();
+	soassertlocked(so);
 
 	/*
 	 * XXX: for deleting MFC entries the information in entries
@@ -1172,7 +1186,7 @@ del_mfc(struct socket *so, struct mbuf *
 		return (EINVAL);
 
 	rw_enter_read(&mrt_routerlock);
-	if (so != mrt_mrouter[inp->inp_rtableid]) {
+	if (so != mrt_mrouter[rtableid]) {
 		rw_exit_read(&mrt_routerlock);
 		return (EPROTONOSUPPORT);
 	}
@@ -1187,7 +1201,8 @@ del_mfc(struct socket *so, struct mbuf *
 	DPRINTF("origin %#08X group %#08X rtableid %d",
 	    mfcctl2.mfcc_origin.s_addr, mfcctl2.mfcc_mcastgrp.s_addr, rtableid);
 
-	while ((rt = mfc_find(NULL, &mfcctl2.mfcc_mcastgrp, rtableid)) != NULL) {
+	while ((rt = mfc_find(NULL, &mfcctl2.mfcc_mcastgrp,
+	    rtableid)) != NULL) {
 		mrt_mcast_del(rt, rtableid);
 		rtfree(rt);
 	}
@@ -1237,6 +1252,8 @@ ip_mforward(struct mbuf *m, struct ifnet
 	struct mbuf *mm;
 	unsigned int rtableid = ifp->if_rdomain;
 
+	NET_ASSERT_LOCKED();
+
 	if (ip->ip_hl < (IP_HDR_LEN + TUNNEL_LEN) >> 2 ||
 	    ((u_char *)(ip + 1))[1] != IPOPT_LSRR) {
 		/*
@@ -1310,8 +1327,6 @@ ip_mforward(struct mbuf *m, struct ifnet
 			im->im_mbz = 0;
 			im->im_vif = v->v_id;
 
-			mrtstat_inc(mrts_upcalls);
-
 			sin.sin_addr = ip->ip_src;
 			rw_enter_read(&mrt_routerlock);
 			if (socket_send(mrt_mrouter[rtableid], mm, &sin) < 0) {
@@ -1323,6 +1338,8 @@ ip_mforward(struct mbuf *m, struct ifnet
 			}
 			rw_exit_read(&mrt_routerlock);
 
+			mrtstat_inc(mrts_upcalls);
+
 			mfc_add(NULL, &ip->ip_src, &ip->ip_dst, v->v_id,
 			    rtableid, M_NOWAIT);
 		}
@@ -1338,22 +1355,23 @@ int
 ip_mdq(struct mbuf *m, struct ifnet *ifp0, struct rtentry *rt, int flags)
 {
 	struct ip  *ip = mtod(m, struct ip *);
-	struct mfc *mfc = (struct mfc *)rt->rt_llinfo;
 	struct vif *v = ifp0->if_mcast;
+	struct mfc *mfc = (struct mfc *)rt->rt_llinfo;
 	struct ifnet *ifp;
 	struct mbuf *mc;
 	struct ip_moptions imo;
+	int plen = m->m_pkthdr.len;
 
-	/* Sanity check: we have all promised pointers. */
 	if (v == NULL || mfc == NULL) {
 		rtfree(rt);
 		return (EHOSTUNREACH);
 	}
 
 	/*
-	 * Don't forward if it didn't arrive from the parent vif for its origin.
+	 * Don't forward if it didn't arrive from the parent vif
+	 * for its origin.
 	 */
-	if (mfc->mfc_parent != v->v_id) {
+	if (v->v_id != mfc->mfc_parent) {
 		/* came in the wrong interface */
 		mrtstat_inc(mrts_wrong_if);
 		mfc->mfc_wrong_if++;
@@ -1364,10 +1382,10 @@ ip_mdq(struct mbuf *m, struct ifnet *ifp
 	/* If I sourced this packet, it counts as output, else it was input. */
 	if (in_hosteq(ip->ip_src, v->v_lcl_addr)) {
 		v->v_pkt_out++;
-		v->v_bytes_out += m->m_pkthdr.len;
+		v->v_bytes_out += plen;
 	} else {
 		v->v_pkt_in++;
-		v->v_bytes_in += m->m_pkthdr.len;
+		v->v_bytes_in += plen;
 	}
 
 	/*
@@ -1387,7 +1405,7 @@ ip_mdq(struct mbuf *m, struct ifnet *ifp
 			continue;
 
 		mfc->mfc_pkt_cnt++;
-		mfc->mfc_byte_cnt += m->m_pkthdr.len;
+		mfc->mfc_byte_cnt += plen;
 
 		/* Don't let this route expire. */
 		mfc->mfc_expire = 0;
@@ -1410,7 +1428,7 @@ ip_mdq(struct mbuf *m, struct ifnet *ifp
 		}
 
 		v->v_pkt_out++;
-		v->v_bytes_out += m->m_pkthdr.len;
+		v->v_bytes_out += plen;
 
 		/*
 		 * Make a new reference to the packet; make sure
@@ -1446,6 +1464,8 @@ if_lookupbyvif(vifi_t vifi, unsigned int
 	struct vif	*v;
 	struct ifnet	*ifp;
 
+	NET_ASSERT_LOCKED();
+
 	TAILQ_FOREACH(ifp, &ifnetlist, if_list) {
 		if (ifp->if_rdomain != rtableid)
 			continue;
@@ -1466,6 +1486,8 @@ rt_mcast_add(struct ifnet *ifp, struct s
 	struct ifaddr		*ifa;
 	int			 rv;
 	unsigned int		 rtableid = ifp->if_rdomain;
+
+	NET_ASSERT_LOCKED();
 
 	TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
 		if (ifa->ifa_addr->sa_family == AF_INET)
Index: netinet6/ip6_input.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/ip6_input.c,v
diff -u -p -r1.300 ip6_input.c
--- netinet6/ip6_input.c	16 Sep 2025 09:19:16 -0000	1.300
+++ netinet6/ip6_input.c	5 Aug 2026 13:42:56 -0000
@@ -475,7 +475,7 @@ ip6_input_if(struct mbuf **mp, int *offp
 
 #ifdef MROUTING
 		if (atomic_load_int(&ip6_mforwarding) &&
-		    ip6_mrouter[ifp->if_rdomain]) {
+		    ip6_mrouter_active(ifp->if_rdomain)) {
 			int error;
 
 			nxt = ip6_hbhchcheck(&m, offp, &ours, flags);
@@ -1432,10 +1432,6 @@ const u_char inet6ctlerrmap[PRC_NCMDS] =
 	0,		0,		0,		0,
 	ENOPROTOOPT
 };
-
-#ifdef MROUTING
-extern int ip6_mrtproto;
-#endif
 
 #ifndef SMALL_KERNEL
 const struct sysctl_bounded_args ipv6ctl_vars[] = {
Index: netinet6/ip6_mroute.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/ip6_mroute.c,v
diff -u -p -r1.158 ip6_mroute.c
--- netinet6/ip6_mroute.c	24 Jun 2026 12:33:49 -0000	1.158
+++ netinet6/ip6_mroute.c	5 Aug 2026 13:42:56 -0000
@@ -107,6 +107,8 @@
 /*
  * Locks used to protect data:
  *	I	immutable after creation
+ *	N	net lock
+ *	R	multicast router lock
  */
 
 /* #define MCAST_DEBUG */
@@ -127,14 +129,13 @@ int mcast6_debug = 1;
 int ip6_mdq(struct mbuf *, struct ifnet *, struct rtentry *, int);
 void phyint_send6(struct ifnet *, struct ip6_hdr *, struct mbuf *, int, int);
 
-/*
- * Globals.  All but ip6_mrouter, ip6_mrtproto and mrt6stat could be static,
- * except for netstat or debugging purposes.
- */
-struct socket  *ip6_mrouter[RT_TABLEID_MAX + 1];
-struct rttimer_queue ip6_mrouterq;
-int		ip6_mrouter_ver = 0;
-int		ip6_mrtproto;    /* [I] for netstat only */
+struct rwlock mrt6_routerlock = RWLOCK_INITIALIZER("mrouter6");
+
+struct rttimer_queue mrt6_timer;
+struct socket	*mrt6_mrouter[RT_TABLEID_MAX + 1];	/* [R] */
+int		 mrt6_mrouter_ver[RT_TABLEID_MAX + 1];	/* [R] */
+int		 ip6_mrtproto;    		/* [I] for netstat only */
+
 struct cpumem *mrt6counters;
 
 int get_sg6_cnt(struct sioc_sg_req6 *, unsigned int);
@@ -144,6 +145,7 @@ int add_m6if(struct socket *, struct mif
 int del_m6if(struct socket *, mifi_t *);
 int add_m6fc(struct socket *, struct mf6cctl *);
 int del_m6fc(struct socket *, struct mf6cctl *);
+int socket6_send(struct socket *, struct mbuf *, struct sockaddr_in6 *);
 void mf6c_expire_route(struct rtentry *, u_int);
 struct ifnet *mrt6_iflookupbymif(mifi_t, unsigned int);
 struct rtentry *mf6c_find(struct ifnet *, struct in6_addr *, unsigned int);
@@ -156,11 +158,6 @@ void mrt6_mcast_del(struct rtentry *, un
 int
 ip6_mrouter_set(int cmd, struct socket *so, struct mbuf *m)
 {
-	struct inpcb	*inp = sotoinpcb(so);
-
-	if (cmd != MRT6_INIT && so != ip6_mrouter[inp->inp_rtableid])
-		return (EPERM);
-
 	switch (cmd) {
 	case MRT6_INIT:
 		if (m == NULL || m->m_len < sizeof(int))
@@ -195,15 +192,34 @@ ip6_mrouter_set(int cmd, struct socket *
 int
 ip6_mrouter_get(int cmd, struct socket *so, struct mbuf *m)
 {
-	struct inpcb	*inp = sotoinpcb(so);
+	struct inpcb *inp = sotoinpcb(so);
+	unsigned int rtableid = inp->inp_rtableid;
+	int error;
+
+	soassertlocked(so);
 
-	if (so != ip6_mrouter[inp->inp_rtableid])
-		return (EPERM);
+	rw_enter_read(&mrt6_routerlock);
+
+	if (so != mrt6_mrouter[rtableid]) {
+		error = EPROTONOSUPPORT;
+		goto out;
+	}
 
 	switch (cmd) {
 	default:
-		return EOPNOTSUPP;
+		error = EOPNOTSUPP;
+		break;
 	}
+ out:
+	rw_exit_read(&mrt6_routerlock);
+
+	return (error);
+}
+
+int
+ip6_mrouter_active(u_int rtableid)
+{
+	return (READ_ONCE(mrt6_mrouter[rtableid]) != NULL);
 }
 
 void
@@ -211,7 +227,7 @@ mrt6_init(void)
 {
 	mrt6counters = counters_alloc(mrt6s_ncounters);
 
-	rt_timer_queue_init(&ip6_mrouterq, MCAST_EXPIRE_TIMEOUT,
+	rt_timer_queue_init(&mrt6_timer, MCAST_EXPIRE_TIMEOUT,
 	    &mf6c_expire_route);
 }
 
@@ -221,33 +237,46 @@ mrt6_init(void)
 int
 mrt6_ioctl(struct socket *so, u_long cmd, caddr_t data)
 {
-	struct inpcb *inp = sotoinpcb(so);
+	struct inpcb *inp;
+	unsigned int rtableid;
 	int error;
 
-	if (inp == NULL)
-		return (ENOTCONN);
+	solock_shared(so);
 
-	KERNEL_LOCK();
+	inp = sotoinpcb(so);
+	if (inp == NULL) {
+		error = ENOTCONN;
+		goto sounlock;
+	}
+	rtableid = inp->inp_rtableid;
+
+	rw_enter_read(&mrt6_routerlock);
+
+	if (so != mrt6_mrouter[rtableid]) {
+		error =  EPROTONOSUPPORT;
+		goto out;
+	}
 
 	switch (cmd) {
 	case SIOCGETSGCNT_IN6:
-		NET_LOCK_SHARED();
-		error = get_sg6_cnt((struct sioc_sg_req6 *)data,
-		    inp->inp_rtableid);
-		NET_UNLOCK_SHARED();
+		KERNEL_LOCK();
+		error = get_sg6_cnt((struct sioc_sg_req6 *)data, rtableid);
+		KERNEL_UNLOCK();
 		break;
 	case SIOCGETMIFCNT_IN6:
-		NET_LOCK_SHARED();
-		error = get_mif6_cnt((struct sioc_mif_req6 *)data,
-		    inp->inp_rtableid);
-		NET_UNLOCK_SHARED();
+		KERNEL_LOCK();
+		error = get_mif6_cnt((struct sioc_mif_req6 *)data, rtableid);
+		KERNEL_UNLOCK();
 		break;
 	default:
 		error = ENOTTY;
 		break;
 	}
+ out:
+	rw_exit_read(&mrt6_routerlock);
+ sounlock:
+	sounlock_shared(so);
 
-	KERNEL_UNLOCK();
 	return error;
 }
 
@@ -526,6 +555,8 @@ ip6_mrouter_init(struct socket *so, int 
 	struct inpcb *inp = sotoinpcb(so);
 	unsigned int rtableid = inp->inp_rtableid;
 
+	soassertlocked(so);
+
 	if (so->so_type != SOCK_RAW ||
 	    so->so_proto->pr_protocol != IPPROTO_ICMPV6)
 		return (EOPNOTSUPP);
@@ -533,11 +564,16 @@ ip6_mrouter_init(struct socket *so, int 
 	if (v != 1)
 		return (ENOPROTOOPT);
 
-	if (ip6_mrouter[rtableid] != NULL)
+	rw_enter_write(&mrt6_routerlock);
+
+	if (mrt6_mrouter[rtableid] != NULL) {
+		rw_exit_write(&mrt6_routerlock);
 		return (EADDRINUSE);
+	}
+	mrt6_mrouter[rtableid] = soref(so);
+	mrt6_mrouter_ver[rtableid] = cmd;
 
-	ip6_mrouter[rtableid] = so;
-	ip6_mrouter_ver = cmd;
+	rw_exit_write(&mrt6_routerlock);
 
 	return (0);
 }
@@ -564,7 +600,14 @@ ip6_mrouter_done(struct socket *so)
 	unsigned int rtableid = inp->inp_rtableid;
 	int error;
 
-	NET_ASSERT_LOCKED();
+	soassertlocked(so);
+
+	rw_enter_write(&mrt6_routerlock);
+
+	if (so != mrt6_mrouter[rtableid]) {
+		rw_exit_write(&mrt6_routerlock);
+		return (EPROTONOSUPPORT);
+	}
 
 	/* Delete all remaining installed multicast routes. */
 	do {
@@ -587,8 +630,11 @@ ip6_mrouter_done(struct socket *so)
 		ip6_mrouter_detach(ifp);
 	}
 
-	ip6_mrouter[inp->inp_rtableid] = NULL;
-	ip6_mrouter_ver = 0;
+	mrt6_mrouter[rtableid] = NULL;
+	mrt6_mrouter_ver[rtableid] = 0;
+	sorele(so);
+
+	rw_exit_write(&mrt6_routerlock);
 
 	return 0;
 }
@@ -605,6 +651,7 @@ ip6_mrouter_detach(struct ifnet *ifp)
 	ifp->if_mcast6 = NULL;
 
 	memset(&ifr, 0, sizeof(ifr));
+	ifr.ifr_addr.sin6_len = sizeof(struct sockaddr_in6);
 	ifr.ifr_addr.sin6_family = AF_INET6;
 	ifr.ifr_addr.sin6_addr = in6addr_any;
 	KERNEL_LOCK();
@@ -627,23 +674,29 @@ add_m6if(struct socket *so, struct mif6c
 	int error;
 	unsigned int rtableid = inp->inp_rtableid;
 
-	NET_ASSERT_LOCKED();
+	soassertlocked(so);
 
-	if (mifcp->mif6c_mifi >= MAXMIFS)
-		return EINVAL;
+	rw_enter_read(&mrt6_routerlock);
+	if (so != mrt6_mrouter[rtableid]) {
+		rw_exit_read(&mrt6_routerlock);
+		return (EPROTONOSUPPORT);
+	}
+	rw_exit_read(&mrt6_routerlock);
 
+	if (mifcp->mif6c_mifi >= MAXMIFS)
+		return (EINVAL);
 	if (mrt6_iflookupbymif(mifcp->mif6c_mifi, rtableid) != NULL)
-		return EADDRINUSE; /* XXX: is it appropriate? */
+		return (EADDRINUSE);
 
 	{
 		ifp = if_get(mifcp->mif6c_pifi);
 		if (ifp == NULL)
-			return ENXIO;
+			return (ENXIO);
 
 		/* Make sure the interface supports multicast */
 		if ((ifp->if_flags & IFF_MULTICAST) == 0) {
 			if_put(ifp);
-			return EOPNOTSUPP;
+			return (EOPNOTSUPP);
 		}
 
 		/*
@@ -651,15 +704,15 @@ add_m6if(struct socket *so, struct mif6c
 		 * from the interface.
 		 */
 		memset(&ifr, 0, sizeof(ifr));
+		ifr.ifr_addr.sin6_len = sizeof(struct sockaddr_in6);
 		ifr.ifr_addr.sin6_family = AF_INET6;
 		ifr.ifr_addr.sin6_addr = in6addr_any;
 		KERNEL_LOCK();
 		error = (*ifp->if_ioctl)(ifp, SIOCADDMULTI, (caddr_t)&ifr);
 		KERNEL_UNLOCK();
-
 		if (error) {
 			if_put(ifp);
-			return error;
+			return (error);
 		}
 	}
 
@@ -675,7 +728,7 @@ add_m6if(struct socket *so, struct mif6c
 
 	if_put(ifp);
 
-	return 0;
+	return (0);
 }
 
 /*
@@ -686,17 +739,25 @@ del_m6if(struct socket *so, mifi_t *mifi
 {
 	struct inpcb *inp = sotoinpcb(so);
 	struct ifnet *ifp;
+	unsigned int rtableid = inp->inp_rtableid;
 
-	NET_ASSERT_LOCKED();
+	soassertlocked(so);
+
+	rw_enter_read(&mrt6_routerlock);
+	if (so != mrt6_mrouter[rtableid]) {
+		rw_exit_read(&mrt6_routerlock);
+		return (EPROTONOSUPPORT);
+	}
+	rw_exit_read(&mrt6_routerlock);
 
 	if (*mifip >= MAXMIFS)
-		return EINVAL;
-	if ((ifp = mrt6_iflookupbymif(*mifip, inp->inp_rtableid)) == NULL)
-		return EINVAL;
+		return (EINVAL);
+	if ((ifp = mrt6_iflookupbymif(*mifip, rtableid)) == NULL)
+		return (EADDRNOTAVAIL);
 
 	ip6_mrouter_detach(ifp);
 
-	return 0;
+	return (0);
 }
 
 int
@@ -726,7 +787,7 @@ mf6c_add_route(struct ifnet *ifp, struct
 	}
 
 	rt->rt_llinfo = (caddr_t)mf6c;
-	rt_timer_add(rt, &ip6_mrouterq, rtableid);
+	rt_timer_add(rt, &mrt6_timer, rtableid);
 	mf6c->mf6c_parent = mf6cc->mf6cc_parent;
 	rtfree(rt);
 
@@ -870,7 +931,14 @@ add_m6fc(struct socket *so, struct mf6cc
 	struct inpcb *inp = sotoinpcb(so);
 	unsigned int rtableid = inp->inp_rtableid;
 
-	NET_ASSERT_LOCKED();
+	soassertlocked(so);
+
+	rw_enter_read(&mrt6_routerlock);
+	if (so != mrt6_mrouter[rtableid]) {
+		rw_exit_read(&mrt6_routerlock);
+		return (EPROTONOSUPPORT);
+	}
+	rw_exit_read(&mrt6_routerlock);
 
 	return mf6c_add(mfccp, &mfccp->mf6cc_origin.sin6_addr,
 	    &mfccp->mf6cc_mcastgrp.sin6_addr, mfccp->mf6cc_parent,
@@ -884,7 +952,14 @@ del_m6fc(struct socket *so, struct mf6cc
 	struct rtentry *rt;
 	unsigned int rtableid = inp->inp_rtableid;
 
-	NET_ASSERT_LOCKED();
+	soassertlocked(so);
+
+	rw_enter_read(&mrt6_routerlock);
+	if (so != mrt6_mrouter[rtableid]) {
+		rw_exit_read(&mrt6_routerlock);
+		return (EPROTONOSUPPORT);
+	}
+	rw_exit_read(&mrt6_routerlock);
 
 	while ((rt = mf6c_find(NULL, &mfccp->mf6cc_mcastgrp.sin6_addr,
 	    rtableid)) != NULL) {
@@ -892,7 +967,7 @@ del_m6fc(struct socket *so, struct mf6cc
 		rtfree(rt);
 	}
 
-	return 0;
+	return (0);
 }
 
 int
@@ -930,7 +1005,6 @@ ip6_mforward(struct ip6_hdr *ip6, struct
 	struct rtentry *rt;
 	struct mif6 *mifp;
 	struct mbuf *mm;
-	struct sockaddr_in6 sin6;
 	unsigned int rtableid = ifp->if_rdomain;
 
 	NET_ASSERT_LOCKED();
@@ -978,6 +1052,7 @@ ip6_mforward(struct ip6_hdr *ip6, struct
 		mrt6stat_inc(mrt6s_no_route);
 
 		{
+			struct sockaddr_in6 sin6 = { sizeof(sin6), AF_INET6 };
 			struct mrt6msg *im;
 
 			if ((mifp = ifp->if_mcast6) == NULL)
@@ -989,18 +1064,15 @@ ip6_mforward(struct ip6_hdr *ip6, struct
 			 */
 			mm = m_copym(m, 0, sizeof(struct ip6_hdr), M_NOWAIT);
 			if (mm == NULL)
-				return ENOBUFS;
+				return (ENOBUFS);
 
 			/*
 			 * Send message to routing daemon
 			 */
-			(void)memset(&sin6, 0, sizeof(sin6));
-			sin6.sin6_len = sizeof(sin6);
-			sin6.sin6_family = AF_INET6;
-			sin6.sin6_addr = ip6->ip6_src;
 
-			im = NULL;
-			switch (ip6_mrouter_ver) {
+			rw_enter_read(&mrt6_routerlock);
+
+			switch (mrt6_mrouter_ver[rtableid]) {
 			case MRT6_INIT:
 				im = mtod(mm, struct mrt6msg *);
 				im->im6_msgtype = MRT6MSG_NOCACHE;
@@ -1008,17 +1080,21 @@ ip6_mforward(struct ip6_hdr *ip6, struct
 				im->im6_mif = mifp->m6_mifi;
 				break;
 			default:
+				rw_exit_read(&mrt6_routerlock);
 				m_freem(mm);
-				return EINVAL;
+				return (EINVAL);
 			}
 
-			if (socket6_send(ip6_mrouter[rtableid], mm,
+			sin6.sin6_addr = ip6->ip6_src;
+			if (socket6_send(mrt6_mrouter[rtableid], mm,
 			    &sin6) < 0) {
-				log(LOG_WARNING, "ip6_mforward: ip6_mrouter "
+				rw_exit_read(&mrt6_routerlock);
+				log(LOG_WARNING, "ip6_mforward: mrt6_mrouter "
 				    "socket queue full\n");
 				mrt6stat_inc(mrt6s_upq_sockfull);
-				return ENOBUFS;
+				return (ENOBUFS);
 			}
+			rw_exit_read(&mrt6_routerlock);
 
 			mrt6stat_inc(mrt6s_upcalls);
 
@@ -1026,7 +1102,7 @@ ip6_mforward(struct ip6_hdr *ip6, struct
 			    mifp->m6_mifi, rtableid, M_NOWAIT);
 		}
 
-		return 0;
+		return (0);
 	}
 }
 
@@ -1051,7 +1127,7 @@ mf6c_expire_route(struct rtentry *rt, u_
 
 	if (mf6c->mf6c_expire == 0) {
 		mf6c->mf6c_expire = 1;
-		rt_timer_add(rt, &ip6_mrouterq, rtableid);
+		rt_timer_add(rt, &mrt6_timer, rtableid);
 		return;
 	}
 
@@ -1072,7 +1148,7 @@ ip6_mdq(struct mbuf *m, struct ifnet *if
 
 	if (mifp == NULL || mf6c == NULL) {
 		rtfree(rt);
-		return EHOSTUNREACH;
+		return (EHOSTUNREACH);
 	}
 
 	/*
@@ -1114,7 +1190,7 @@ ip6_mdq(struct mbuf *m, struct ifnet *if
 			continue;
 
 		mf6c->mf6c_pkt_cnt++;
-		mf6c->mf6c_byte_cnt += m->m_pkthdr.len;
+		mf6c->mf6c_byte_cnt += plen;
 
 		/* Don't let this route expire. */
 		mf6c->mf6c_expire = 0;
@@ -1246,10 +1322,10 @@ mrt6_iflookupbymif(mifi_t mifi, unsigned
 		if (m6->m6_mifi != mifi)
 			continue;
 
-		return ifp;
+		return (ifp);
 	}
 
-	return NULL;
+	return (NULL);
 }
 
 struct rtentry *
@@ -1259,8 +1335,8 @@ mf6c_find(struct ifnet *ifp, struct in6_
 	struct sockaddr_in6 msin6;
 
 	memset(&msin6, 0, sizeof(msin6));
-	msin6.sin6_family = AF_INET6;
 	msin6.sin6_len = sizeof(msin6);
+	msin6.sin6_family = AF_INET6;
 	msin6.sin6_addr = *group;
 
 	rt = rtalloc(sin6tosa(&msin6), 0, rtableid);
@@ -1288,6 +1364,8 @@ mrt6_mcast_add(struct ifnet *ifp, struct
 	struct ifaddr *ifa;
 	int rv;
 	unsigned int rtableid = ifp->if_rdomain;
+
+	NET_ASSERT_LOCKED();
 
 	TAILQ_FOREACH(ifa, &ifp->if_addrlist, ifa_list) {
 		if (ifa->ifa_addr->sa_family == AF_INET6)
Index: netinet6/ip6_mroute.h
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/ip6_mroute.h,v
diff -u -p -r1.26 ip6_mroute.h
--- netinet6/ip6_mroute.h	24 Jun 2026 12:33:49 -0000	1.26
+++ netinet6/ip6_mroute.h	5 Aug 2026 13:42:56 -0000
@@ -222,6 +222,8 @@ mrt6stat_inc(enum mrt6stat_counters c)
 /* How frequent should we look for expired entries (in seconds). */
 #define	MCAST_EXPIRE_TIMEOUT	30
 
+extern int ip6_mrtproto;
+
 /*
  * The kernel's multicast-interface structure.
  */
Index: netinet6/ip6_output.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/ip6_output.c,v
diff -u -p -r1.308 ip6_output.c
--- netinet6/ip6_output.c	5 Aug 2026 09:43:19 -0000	1.308
+++ netinet6/ip6_output.c	5 Aug 2026 13:42:56 -0000
@@ -490,7 +490,7 @@ reroute:
 			 * if necessary.
 			 */
 			if (atomic_load_int(&ip6_mforwarding) &&
-			    ip6_mrouter[ifp->if_rdomain] &&
+			    ip6_mrouter_active(ifp->if_rdomain) &&
 			    (flags & IPV6_FORWARDING) == 0) {
 				int rv;
 
Index: netinet6/ip6_var.h
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/ip6_var.h,v
diff -u -p -r1.130 ip6_var.h
--- netinet6/ip6_var.h	23 Jun 2026 15:45:00 -0000	1.130
+++ netinet6/ip6_var.h	5 Aug 2026 13:42:56 -0000
@@ -299,6 +299,7 @@ int	ip6_unknown_opt(struct mbuf **, u_in
 int	ip6_get_prevhdr(struct mbuf *, int);
 int	ip6_nexthdr(struct mbuf *, int, int, int *);
 int	ip6_lasthdr(struct mbuf *, int, int, int *);
+int	ip6_mrouter_active(u_int);
 int	ip6_mforward(struct ip6_hdr *, struct ifnet *, struct mbuf *, int);
 int	ip6_process_hopopts(struct mbuf **, u_int8_t *, int, u_int32_t *,
 	     u_int32_t *);
Index: netinet6/mld6.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/mld6.c,v
diff -u -p -r1.75 mld6.c
--- netinet6/mld6.c	29 Mar 2026 18:08:07 -0000	1.75
+++ netinet6/mld6.c	5 Aug 2026 13:42:56 -0000
@@ -535,7 +535,7 @@ mld6_sendpkt(const struct mld6_pktinfo *
 	 * router, so that the process-level routing daemon can hear it.
 	 */
 #ifdef MROUTING
-	im6o.im6o_loop = (ip6_mrouter[pkt->mpi_rdomain] != NULL);
+	im6o.im6o_loop = ip6_mrouter_active(pkt->mpi_rdomain);
 #endif
 	if_put(ifp);
 
Index: netinet6/raw_ip6.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/raw_ip6.c,v
diff -u -p -r1.194 raw_ip6.c
--- netinet6/raw_ip6.c	8 Jul 2025 00:47:41 -0000	1.194
+++ netinet6/raw_ip6.c	5 Aug 2026 13:42:56 -0000
@@ -627,8 +627,7 @@ rip6_detach(struct socket *so)
 	if (inp == NULL)
 		panic("%s", __func__);
 #ifdef MROUTING
-	if (so == ip6_mrouter[inp->inp_rtableid])
-		ip6_mrouter_done(so);
+	ip6_mrouter_done(so);
 #endif
 	free(inp->inp_icmp6filt, M_PCB, sizeof(struct icmp6_filter));
 	inp->inp_icmp6filt = NULL;