Re: multicast router lock

Claudio Jeker <[email protected]> Thu, 16 Jul 2026 09:29:51 +0200
Newsgroups gmane.os.openbsd.tech
Message-ID <[email protected]>
On Wed, Jul 15, 2026 at 09:09:35PM +0200, Alexander Bluhm wrote:
> On Thu, Jun 25, 2026 at 09:14:11PM +0200, Alexander Bluhm wrote:
> > When we want to go MP for multicast forward, we have to proect the
> > multicast router structure.
> > 
> > I have introduced a rwlock for mrt_mrouter and mrt_api_config.
> > Since we support one router per route table, we should also have
> > an api version per router.  The existing code was inconsistent.
> > Ref count the sockets in mrt_mrouter.
> > 
> > Also fixed the name space a bit by using mrt_ prefix.
> > 
> > ok?
> 
> Add little fixes in mrt_count accounting and removed kassert in
> diff.
> 
> anyone?

I have a WIP diff that unfucks the rdomain handling in this code and
in rtable.c. I did a lot of similar changes to your code. Will be a pain
to fix all the conflicts.

I'm a bit surprised about the lock gymnastics in mrt_ioctl(), I guess the
KERNEL_LOCK bits are still there because you have not yet fully unlocked
ip_mforward, right?
 
I can't test this. If you tested it then throw it in.

> bluhm
> 
> Index: netinet/igmp.c
> ===================================================================
> RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/igmp.c,v
> diff -u -p -r1.98 igmp.c
> --- netinet/igmp.c	29 Mar 2026 18:08:07 -0000	1.98
> +++ netinet/igmp.c	15 Jul 2026 10:35:37 -0000
> @@ -741,7 +741,7 @@ igmp_sendpkt(struct igmp_pktinfo *pkt)
>  	 * router, so that the process-level routing daemon can hear it.
>  	 */
>  #ifdef MROUTING
> -	imo.imo_loop = (ip_mrouter[pkt->ipi_rdomain] != NULL);
> +	imo.imo_loop = ip_mrouter_active(pkt->ipi_rdomain);
>  #else
>  	imo.imo_loop = 0;
>  #endif /* MROUTING */
> Index: netinet/ip_input.c
> ===================================================================
> RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/ip_input.c,v
> diff -u -p -r1.430 ip_input.c
> --- netinet/ip_input.c	22 Jun 2026 10:58:34 -0000	1.430
> +++ netinet/ip_input.c	15 Jul 2026 10:35:37 -0000
> @@ -532,7 +532,7 @@ ip_input_if(struct mbuf **mp, int *offp,
>  
>  #ifdef MROUTING
>  		if (atomic_load_int(&ipmforwarding) &&
> -		    ip_mrouter[ifp->if_rdomain]) {
> +		    ip_mrouter_active(ifp->if_rdomain)) {
>  			int error;
>  
>  			if (m->m_flags & M_EXT) {
> Index: netinet/ip_mroute.c
> ===================================================================
> RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/ip_mroute.c,v
> diff -u -p -r1.153 ip_mroute.c
> --- netinet/ip_mroute.c	24 Jun 2026 12:33:49 -0000	1.153
> +++ netinet/ip_mroute.c	15 Jul 2026 10:44:34 -0000
> @@ -79,6 +79,8 @@
>  /*
>   * Locks used to protect data:
>   *	I	immutable after creation
> + *	N	net lock
> + *	R	multicast router lock
>   */
>  
>  /* #define MCAST_DEBUG */
> @@ -96,14 +98,21 @@ int mcast_debug = 1;
>  	do { } while (0)
>  #endif
>  
> +struct rwlock mrt_routerlock = RWLOCK_INITIALIZER("mrouter");
> +
>  /*
> - * Globals.  All but ip_mrouter and ip_mrtproto could be static,
> - * except for netstat or debugging purposes.
> + * Kernel multicast routing API capabilities and setup.
> + * If more API capabilities are added to the kernel, they should be
> + * recorded in `mrt_api_support'.
>   */
> -struct socket	*ip_mrouter[RT_TABLEID_MAX + 1];
> -struct rttimer_queue ip_mrouterq;
> -uint64_t	 mrt_count[RT_TABLEID_MAX + 1];
> -int		ip_mrtproto = IGMP_DVMRP;    /* [I] for netstat only */
> +static const u_int32_t mrt_api_support =
> +    MRT_MFC_FLAGS_DISABLE_WRONGVIF | MRT_MFC_RP;
> +
> +struct rttimer_queue mrt_timer;
> +struct socket	*mrt_mrouter[RT_TABLEID_MAX + 1];	/* [R] */
> +uint32_t	 mrt_api_config[RT_TABLEID_MAX + 1];	/* [R] */
> +uint64_t	 mrt_count[RT_TABLEID_MAX + 1];		/* [N] */
> +int		 ip_mrtproto = IGMP_DVMRP;	/* [I] for netstat only */
>  
>  struct cpumem *mrtcounters;
>  
> @@ -124,9 +133,8 @@ int add_mfc(struct socket *, struct mbuf
>  int del_mfc(struct socket *, struct mbuf *);
>  int set_api_config(struct socket *, struct mbuf *); /* chose API capabilities */
>  int get_api_support(struct mbuf *);
> -int get_api_config(struct mbuf *);
> -int socket_send(struct socket *, struct mbuf *,
> -			    struct sockaddr_in *);
> +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);
>  struct ifnet *if_lookupbyvif(vifi_t, unsigned int);
>  struct rtentry *rt_mcast_add(struct ifnet *, struct sockaddr *,
> @@ -134,15 +142,6 @@ struct rtentry *rt_mcast_add(struct ifne
>  void mrt_mcast_del(struct rtentry *, unsigned int);
>  
>  /*
> - * Kernel multicast routing API capabilities and setup.
> - * If more API capabilities are added to the kernel, they should be
> - * recorded in `mrt_api_support'.
> - */
> -static const u_int32_t mrt_api_support = (MRT_MFC_FLAGS_DISABLE_WRONGVIF |
> -					  MRT_MFC_RP);
> -static u_int32_t mrt_api_config = 0;
> -
> -/*
>   * Find a route for a given Multicast group address.
>   * Type of service parameter to be added in the future!!!
>   * Statistics are updated by the caller if needed (mrts_mfc_lookups and
> @@ -185,39 +184,34 @@ mfc_find(struct ifnet *ifp, struct in_ad
>  int
>  ip_mrouter_set(struct socket *so, int optname, struct mbuf *m)
>  {
> -	struct inpcb *inp = sotoinpcb(so);
>  	int error;
>  
> -	if (optname != MRT_INIT &&
> -	    so != ip_mrouter[inp->inp_rtableid])
> +	switch (optname) {
> +	case MRT_INIT:
> +		error = ip_mrouter_init(so, m);
> +		break;
> +	case MRT_DONE:
> +		error = ip_mrouter_done(so);
> +		break;
> +	case MRT_ADD_VIF:
> +		error = add_vif(so, m);
> +		break;
> +	case MRT_DEL_VIF:
> +		error = del_vif(so, m);
> +		break;
> +	case MRT_ADD_MFC:
> +		error = add_mfc(so, m);
> +		break;
> +	case MRT_DEL_MFC:
> +		error = del_mfc(so, m);
> +		break;
> +	case MRT_API_CONFIG:
> +		error = set_api_config(so, m);
> +		break;
> +	default:
>  		error = ENOPROTOOPT;
> -	else
> -		switch (optname) {
> -		case MRT_INIT:
> -			error = ip_mrouter_init(so, m);
> -			break;
> -		case MRT_DONE:
> -			error = ip_mrouter_done(so);
> -			break;
> -		case MRT_ADD_VIF:
> -			error = add_vif(so, m);
> -			break;
> -		case MRT_DEL_VIF:
> -			error = del_vif(so, m);
> -			break;
> -		case MRT_ADD_MFC:
> -			error = add_mfc(so, m);
> -			break;
> -		case MRT_DEL_MFC:
> -			error = del_mfc(so, m);
> -			break;
> -		case MRT_API_CONFIG:
> -			error = set_api_config(so, m);
> -			break;
> -		default:
> -			error = ENOPROTOOPT;
> -			break;
> -		}
> +		break;
> +	}
>  
>  	return (error);
>  }
> @@ -228,37 +222,38 @@ 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);
>  	int error;
>  
> -	if (so != ip_mrouter[inp->inp_rtableid])
> +	switch (optname) {
> +	case MRT_VERSION:
> +		error = get_version(m);
> +		break;
> +	case MRT_API_SUPPORT:
> +		error = get_api_support(m);
> +		break;
> +	case MRT_API_CONFIG:
> +		error = get_api_config(so, m);
> +		break;
> +	default:
>  		error = ENOPROTOOPT;
> -	else {
> -		switch (optname) {
> -		case MRT_VERSION:
> -			error = get_version(m);
> -			break;
> -		case MRT_API_SUPPORT:
> -			error = get_api_support(m);
> -			break;
> -		case MRT_API_CONFIG:
> -			error = get_api_config(m);
> -			break;
> -		default:
> -			error = ENOPROTOOPT;
> -			break;
> -		}
> +		break;
>  	}
>  
>  	return (error);
>  }
>  
> +int
> +ip_mrouter_active(u_int rtableid)
> +{
> +	return (READ_ONCE(mrt_mrouter[rtableid]) != NULL);
> +}
> +
>  void
>  mrt_init(void)
>  {
>  	mrtcounters = counters_alloc(mrts_ncounters);
>  
> -	rt_timer_queue_init(&ip_mrouterq, MCAST_EXPIRE_FREQUENCY,
> +	rt_timer_queue_init(&mrt_timer, MCAST_EXPIRE_FREQUENCY,
>  	    &mfc_expire_route);
>  }
>  
> @@ -274,30 +269,37 @@ mrt_ioctl(struct socket *so, u_long cmd,
>  	if (inp == NULL)
>  		return (ENOTCONN);
>  
> -	KERNEL_LOCK();
>  
> -	if (so != ip_mrouter[inp->inp_rtableid])
> -		error = EINVAL;
> -	else
> -		switch (cmd) {
> -		case SIOCGETVIFCNT:
> -			NET_LOCK_SHARED();
> -			error = get_vif_cnt(inp->inp_rtableid,
> -			    (struct sioc_vif_req *)data);
> -			NET_UNLOCK_SHARED();
> -			break;
> -		case SIOCGETSGCNT:
> -			NET_LOCK_SHARED();
> -			error = get_sg_cnt(inp->inp_rtableid,
> -			    (struct sioc_sg_req *)data);
> -			NET_UNLOCK_SHARED();
> -			break;
> -		default:
> -			error = ENOTTY;
> -			break;
> -		}
> +	NET_LOCK_SHARED();
> +	rw_enter_read(&mrt_routerlock);
> +
> +	if (so != mrt_mrouter[inp->inp_rtableid]) {
> +		error =  EPROTONOSUPPORT;
> +		goto out;
> +	}
> +
> +	switch (cmd) {
> +	case SIOCGETVIFCNT:
> +		KERNEL_LOCK();
> +		error = get_vif_cnt(inp->inp_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);
> +		KERNEL_UNLOCK();
> +		break;
> +	default:
> +		error = ENOTTY;
> +		break;
> +	}
> +
> + out:
> +	rw_exit_read(&mrt_routerlock);
> +	NET_UNLOCK_SHARED();
>  
> -	KERNEL_UNLOCK();
>  	return (error);
>  }
>  
> @@ -593,10 +595,15 @@ ip_mrouter_init(struct socket *so, struc
>  	if (*v != 1)
>  		return (EINVAL);
>  
> -	if (ip_mrouter[rtableid] != NULL)
> +	rw_enter_write(&mrt_routerlock);
> +
> +	if (mrt_mrouter[rtableid] != NULL) {
> +		rw_exit_write(&mrt_routerlock);
>  		return (EADDRINUSE);
> +	}
> +	mrt_mrouter[rtableid] = soref(so);
>  
> -	ip_mrouter[rtableid] = so;
> +	rw_exit_write(&mrt_routerlock);
>  
>  	return (0);
>  }
> @@ -625,6 +632,13 @@ ip_mrouter_done(struct socket *so)
>  
>  	NET_ASSERT_LOCKED();
>  
> +	rw_enter_write(&mrt_routerlock);
> +
> +	if (so != mrt_mrouter[inp->inp_rtableid]) {
> +		rw_exit_write(&mrt_routerlock);
> +		return (EPROTONOSUPPORT);
> +	}
> +
>  	/* Delete all remaining installed multicast routes. */
>  	do {
>  		struct rtentry *rt = NULL;
> @@ -638,6 +652,12 @@ ip_mrouter_done(struct socket *so)
>  		rtfree(rt);
>  	} while (error == EAGAIN);
>  
> +	/*
> +	 * XXX Should be KASSERT, but there is a leak.  If an interface
> +	 * is destroyed, the route is deleted, but mrt_mcast_del() not run.
> +	 */
> +	mrt_count[rtableid] = 0;
> +
>  	/* Unregister all interfaces in the domain. */
>  	TAILQ_FOREACH(ifp, &ifnetlist, if_list) {
>  		if (ifp->if_rdomain != rtableid)
> @@ -646,10 +666,11 @@ ip_mrouter_done(struct socket *so)
>  		vif_delete(ifp);
>  	}
>  
> -	mrt_api_config = 0;
> +	mrt_api_config[rtableid] = 0;
> +	mrt_mrouter[rtableid] = NULL;
> +	sorele(so);
>  
> -	ip_mrouter[rtableid] = NULL;
> -	mrt_count[rtableid] = 0;
> +	rw_exit_write(&mrt_routerlock);
>  
>  	return (0);
>  }
> @@ -672,7 +693,7 @@ set_api_config(struct socket *so, struct
>  {
>  	struct inpcb *inp = sotoinpcb(so);
>  	struct ifnet *ifp;
> -	u_int32_t *apival;
> +	uint32_t *apival;
>  	unsigned int rtableid = inp->inp_rtableid;
>  
>  	if (m == NULL || m->m_len < sizeof(u_int32_t))
> @@ -680,6 +701,13 @@ set_api_config(struct socket *so, struct
>  
>  	apival = mtod(m, u_int32_t *);
>  
> +	rw_enter_write(&mrt_routerlock);
> +
> +	if (so != mrt_mrouter[inp->inp_rtableid]) {
> +		rw_exit_write(&mrt_routerlock);
> +		return (EPROTONOSUPPORT);
> +	}
> +
>  	/*
>  	 * We can set the API capabilities only if it is the first operation
>  	 * after MRT_INIT. I.e.:
> @@ -692,16 +720,20 @@ set_api_config(struct socket *so, struct
>  		if (ifp->if_mcast == NULL)
>  			continue;
>  
> +		rw_exit_write(&mrt_routerlock);
>  		*apival = 0;
>  		return (EPERM);
>  	}
>  	if (mrt_count[rtableid] > 0) {
> +		rw_exit_write(&mrt_routerlock);
>  		*apival = 0;
>  		return (EPERM);
>  	}
>  
> -	mrt_api_config = *apival & mrt_api_support;
> -	*apival = mrt_api_config;
> +	*apival &= mrt_api_support;
> +	mrt_api_config[rtableid] = *apival;
> +
> +	rw_exit_write(&mrt_routerlock);
>  
>  	return (0);
>  }
> @@ -712,12 +744,12 @@ set_api_config(struct socket *so, struct
>  int
>  get_api_support(struct mbuf *m)
>  {
> -	u_int32_t *apival;
> +	uint32_t *apival;
>  
>  	if (m == NULL || m->m_len < sizeof(u_int32_t))
>  		return (EINVAL);
>  
> -	apival = mtod(m, u_int32_t *);
> +	apival = mtod(m, uint32_t *);
>  
>  	*apival = mrt_api_support;
>  
> @@ -728,16 +760,24 @@ get_api_support(struct mbuf *m)
>   * Get API configured capabilities
>   */
>  int
> -get_api_config(struct mbuf *m)
> +get_api_config(struct socket *so, struct mbuf *m)
>  {
> -	u_int32_t *apival;
> +	struct inpcb *inp = sotoinpcb(so);
> +	uint32_t *apival;
> +	unsigned int rtableid = inp->inp_rtableid;
>  
>  	if (m == NULL || m->m_len < sizeof(u_int32_t))
>  		return (EINVAL);
>  
> -	apival = mtod(m, u_int32_t *);
> +	apival = mtod(m, uint32_t *);
>  
> -	*apival = mrt_api_config;
> +	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);
>  }
> @@ -759,6 +799,13 @@ add_vif(struct socket *so, struct mbuf *
>  	if (m == NULL || m->m_len < sizeof(struct vifctl))
>  		return (EINVAL);
>  
> +	rw_enter_read(&mrt_routerlock);
> +	if (so != mrt_mrouter[inp->inp_rtableid]) {
> +		rw_exit_read(&mrt_routerlock);
> +		return (EPROTONOSUPPORT);
> +	}
> +	rw_exit_read(&mrt_routerlock);
> +
>  	vifcp = mtod(m, struct vifctl *);
>  	if (vifcp->vifc_vifi >= MAXVIFS)
>  		return (EINVAL);
> @@ -826,6 +873,13 @@ del_vif(struct socket *so, struct mbuf *
>  	if (m == NULL || m->m_len < sizeof(vifi_t))
>  		return (EINVAL);
>  
> +	rw_enter_read(&mrt_routerlock);
> +	if (so != mrt_mrouter[inp->inp_rtableid]) {
> +		rw_exit_read(&mrt_routerlock);
> +		return (EPROTONOSUPPORT);
> +	}
> +	rw_exit_read(&mrt_routerlock);
> +
>  	vifip = mtod(m, vifi_t *);
>  	if ((ifp = if_lookupbyvif(*vifip, rtableid)) == NULL)
>  		return (EADDRNOTAVAIL);
> @@ -873,7 +927,7 @@ mfc_expire_route(struct rtentry *rt, u_i
>  	/* Not expired, add it back to the queue. */
>  	if (mfc->mfc_expire == 0) {
>  		mfc->mfc_expire = 1;
> -		rt_timer_add(rt, &ip_mrouterq, rtableid);
> +		rt_timer_add(rt, &mrt_timer, rtableid);
>  		return;
>  	}
>  
> @@ -887,6 +941,7 @@ mfc_add_route(struct ifnet *ifp, struct 
>  	struct vif		*v = ifp->if_mcast;
>  	struct rtentry		*rt;
>  	struct mfc		*mfc;
> +	uint32_t		 api_config;
>  	unsigned int		 rtableid = ifp->if_rdomain;
>  
>  	rt = rt_mcast_add(ifp, origin, group);
> @@ -907,19 +962,23 @@ mfc_add_route(struct ifnet *ifp, struct 
>  
>  	rt->rt_llinfo = (caddr_t)mfc;
>  
> -	rt_timer_add(rt, &ip_mrouterq, rtableid);
> +	rt_timer_add(rt, &mrt_timer, rtableid);
> +
> +	rw_enter_read(&mrt_routerlock);
> +	api_config = mrt_api_config[rtableid];
> +	rw_exit_read(&mrt_routerlock);
>  
>  	mfc->mfc_parent = mfccp->mfcc_parent;
>  	mfc->mfc_pkt_cnt = 0;
>  	mfc->mfc_byte_cnt = 0;
>  	mfc->mfc_wrong_if = 0;
>  	mfc->mfc_ttl = mfccp->mfcc_ttls[v->v_id];
> -	mfc->mfc_flags = mfccp->mfcc_flags[v->v_id] & mrt_api_config &
> +	mfc->mfc_flags = mfccp->mfcc_flags[v->v_id] & api_config &
>  	    MRT_MFC_FLAGS_ALL;
>  	mfc->mfc_expire = 0;
>  
>  	/* set the RP address */
> -	if (mrt_api_config & MRT_MFC_RP)
> +	if (api_config & MRT_MFC_RP)
>  		mfc->mfc_rp = mfccp->mfcc_rp;
>  	else
>  		mfc->mfc_rp = zeroin_addr;
> @@ -1053,11 +1112,20 @@ add_mfc(struct socket *so, struct mbuf *
>  	struct inpcb *inp = sotoinpcb(so);
>  	struct mfcctl2 mfcctl2;
>  	int mfcctl_size = sizeof(struct mfcctl);
> +	uint32_t api_config;
>  	unsigned int rtableid = inp->inp_rtableid;
>  
>  	NET_ASSERT_LOCKED();
>  
> -	if (mrt_api_config & MRT_API_FLAGS_ALL)
> +	rw_enter_read(&mrt_routerlock);
> +	if (so != mrt_mrouter[inp->inp_rtableid]) {
> +		rw_exit_read(&mrt_routerlock);
> +		return (EPROTONOSUPPORT);
> +	}
> +	api_config = mrt_api_config[rtableid];
> +	rw_exit_read(&mrt_routerlock);
> +
> +	if (api_config & MRT_API_FLAGS_ALL)
>  		mfcctl_size = sizeof(struct mfcctl2);
>  
>  	if (m == NULL || m->m_len < mfcctl_size)
> @@ -1066,7 +1134,7 @@ add_mfc(struct socket *so, struct mbuf *
>  	/*
>  	 * select data size depending on API version.
>  	 */
> -	if (mrt_api_config & MRT_API_FLAGS_ALL) {
> +	if (api_config & MRT_API_FLAGS_ALL) {
>  		struct mfcctl2 *mp2 = mtod(m, struct mfcctl2 *);
>  		memcpy((caddr_t)&mfcctl2, mp2, sizeof(*mp2));
>  	} else {
> @@ -1103,6 +1171,13 @@ del_mfc(struct socket *so, struct mbuf *
>  	if (m == NULL || m->m_len < mfcctl_size)
>  		return (EINVAL);
>  
> +	rw_enter_read(&mrt_routerlock);
> +	if (so != mrt_mrouter[inp->inp_rtableid]) {
> +		rw_exit_read(&mrt_routerlock);
> +		return (EPROTONOSUPPORT);
> +	}
> +	rw_exit_read(&mrt_routerlock);
> +
>  	mp = mtod(m, struct mfcctl *);
>  
>  	memcpy((caddr_t)&mfcctl2, mp, sizeof(*mp));
> @@ -1238,12 +1313,15 @@ ip_mforward(struct mbuf *m, struct ifnet
>  			mrtstat_inc(mrts_upcalls);
>  
>  			sin.sin_addr = ip->ip_src;
> -			if (socket_send(ip_mrouter[rtableid], mm, &sin) < 0) {
> +			rw_enter_read(&mrt_routerlock);
> +			if (socket_send(mrt_mrouter[rtableid], mm, &sin) < 0) {
> +				rw_exit_read(&mrt_routerlock);
>  				log(LOG_WARNING, "ip_mforward: ip_mrouter "
>  				    "socket queue full\n");
>  				mrtstat_inc(mrts_upq_sockfull);
>  				return (ENOBUFS);
>  			}
> +			rw_exit_read(&mrt_routerlock);
>  
>  			mfc_add(NULL, &ip->ip_src, &ip->ip_dst, v->v_id,
>  			    rtableid, M_NOWAIT);
> @@ -1422,6 +1500,8 @@ mrt_mcast_del(struct rtentry *rt, unsign
>  	free(rt->rt_llinfo, M_MRTABLE, sizeof(struct mfc));
>  	rt->rt_llinfo = NULL;
>  
> +	mrt_count[rtableid]--;
> +
>  	ifp = if_get(rt->rt_ifidx);
>  	if (ifp == NULL)
>  		return;
> @@ -1430,6 +1510,4 @@ mrt_mcast_del(struct rtentry *rt, unsign
>  
>  	if (error)
>  		DPRINTF("delete route error %d\n", error);
> -
> -	mrt_count[rtableid]--;
>  }
> Index: netinet/ip_output.c
> ===================================================================
> RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/ip_output.c,v
> diff -u -p -r1.417 ip_output.c
> --- netinet/ip_output.c	6 May 2026 11:36:13 -0000	1.417
> +++ netinet/ip_output.c	15 Jul 2026 10:35:37 -0000
> @@ -325,7 +325,7 @@ reroute:
>  			 * if necessary.
>  			 */
>  			if (atomic_load_int(&ipmforwarding) &&
> -			    ip_mrouter[ifp->if_rdomain] &&
> +			    ip_mrouter_active(ifp->if_rdomain) &&
>  			    (flags & IP_FORWARDING) == 0) {
>  				int rv;
>  
> Index: netinet/ip_var.h
> ===================================================================
> RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/ip_var.h,v
> diff -u -p -r1.124 ip_var.h
> --- netinet/ip_var.h	21 Jun 2026 21:17:07 -0000	1.124
> +++ netinet/ip_var.h	15 Jul 2026 10:35:37 -0000
> @@ -247,6 +247,7 @@ int	 ip_getmoptions(int, struct ip_mopti
>  void	 ip_init(void);
>  struct mbuf*
>  	 ip_insertoptions(struct mbuf *, struct mbuf *, int *);
> +int	 ip_mrouter_active(u_int);
>  int	 ip_mforward(struct mbuf *, struct ifnet *, int);
>  int	 ip_optcopy(struct ip *, struct ip *);
>  int	 ip_output(struct mbuf *, struct mbuf *, struct route *, int,
> @@ -282,9 +283,6 @@ int	 rip_disconnect(struct socket *);
>  int	 rip_shutdown(struct socket *);
>  int	 rip_send(struct socket *, struct mbuf *, struct mbuf *,
>  	     struct mbuf *);
> -#ifdef MROUTING
> -extern struct socket *ip_mrouter[];	/* multicast routing daemon */
> -#endif
>  
>  #endif /* _KERNEL */
>  #endif /* _NETINET_IP_VAR_H_ */
> Index: netinet/raw_ip.c
> ===================================================================
> RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/raw_ip.c,v
> diff -u -p -r1.167 raw_ip.c
> --- netinet/raw_ip.c	8 Jul 2025 00:47:41 -0000	1.167
> +++ netinet/raw_ip.c	15 Jul 2026 10:35:37 -0000
> @@ -504,8 +504,7 @@ rip_detach(struct socket *so)
>  		return (EINVAL);
>  
>  #ifdef MROUTING
> -	if (so == ip_mrouter[inp->inp_rtableid])
> -		ip_mrouter_done(so);
> +	ip_mrouter_done(so);
>  #endif
>  	in_pcbdetach(inp);
>  
> 

-- 
:wq Claudio