From: Alexander Bluhm Subject: Re: unlock igmp and mld6 fast timer To: Vitaliy Makkoveev Cc: tech@openbsd.org Date: Fri, 6 Feb 2026 20:39:11 -0500 On Sun, Jan 04, 2026 at 11:36:44AM +0300, Vitaliy Makkoveev wrote: > On Sat, Jan 03, 2026 at 11:43:09PM +0100, Alexander Bluhm wrote: > > On Fri, Jan 02, 2026 at 01:58:09PM +0100, Alexander Bluhm wrote: > > > On Fri, Jan 02, 2026 at 06:32:31AM +0000, Vitaliy Makkoveev wrote: > > > > On Mon, Dec 29, 2025 at 01:20:05PM +0100, Alexander Bluhm wrote: > > > > > On Tue, Dec 02, 2025 at 11:01:36PM +0100, Alexander Bluhm wrote: > > > > > > Hi, > > > > > > > > > > > > After moving around code for a while, I think a rwlock to protect > > > > > > if_maddrlist is the next step. The malloc(M_WAITOK) in rti_fill() > > > > > > prevents us from using a mutex here. > > > > > > > > > > > > So protect the TAILQ if_maddrlist with rwlock if_maddrlock. Also > > > > > > struct in_multi and in6_multi use this lock for their state and > > > > > > timer. Sleeps in malloc and IP output are possible. > > > > > > > > > > > > This allows to run IGMP and MLD6 fast timeout with shared net lock. > > > > > > > > > > I would like to proceed with MP multicast. > > > > > > > > > > OK? objections? > > > > > > > > > > > > > Just for curiosity, why have you replaced mutex with rwlock? > > > > > > There is this call path, I found the sleeping point during testing. > > > > > > in_addmulti() > > > rw_enter_write(&ifp->if_maddrlock) > > > igmp_joingroup() > > > rti_fill() > > > malloc(M_WAITOK) > > > > > > Maybe I can pull the M_WAITOK out of the lock later. Sleeping for > > > memory with a rwlock held is not optimal. But moving the malloc > > > and replacing the lock with a mutex requires more refacotring. Not > > > sure if it is worth it. > > > > > > IPv6 is different, it uses M_NOWAIT. But that means a system call > > > can fail due to low memory situations. Also not good, sleeping > > > would be better. > > > > > > > in{,6}_var.h have missing locks description. Can you add something > > > > like of 'if_maddrlock rwlock of parent interface' for [m]? And > > > > description for [I] too. > > > > > > done > > > > > > > The rest looks good to me. > > > > The diff was not quite right. syzkaller found that in_addmulti -> > > igmp_joingroup -> igmp_sendpkt -> ip_output -> in_hasmulti is a bad > > idea. The lock was taken twice. I have backed it out. > > > > https://syzkaller.appspot.com/bug?extid=de6bcf8e746b8a631885 > > > > Fortunately I was working on a follow up diff. Idea is that > > igmp_sendpkt() is called after the lock has been released. Struct > > igmp_pktinfo contains everything that is needed to send an IGMP > > packet later. In case of timeout loops a bunch of such packets are > > queued. > > > > Diff below contains the previous locking diff with igmp_sendpkt() > > on top. MLD6 is analog to IGMP. If it helps review, I can also > > split it up. > > > > ok? > > > > Yes please. Not sure if that refers to the whole diff or the suggestion to split it up. Here it the part that delays igmp_sendpkt() so that it can run after the lock. This has to be commited first. ok? bluhm Index: netinet/igmp.c =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/igmp.c,v diff -u -p -r1.95 igmp.c --- netinet/igmp.c 3 Jan 2026 14:10:04 -0000 1.95 +++ netinet/igmp.c 6 Feb 2026 22:36:20 -0000 @@ -118,8 +118,7 @@ static LIST_HEAD(, router_info) rti_head static struct mbuf *router_alert; struct cpumem *igmpcounters; -int igmp_checktimer(struct ifnet *); -void igmp_sendpkt(struct ifnet *, struct in_multi *, int, in_addr_t); +int igmp_checktimer(struct ifnet *, struct igmp_pktlist *); int rti_fill(struct in_multi *); int rti_reset(struct ifnet *); int igmp_input_if(struct ifnet *, struct mbuf **, int *, int, int, @@ -538,16 +537,20 @@ igmp_input_if(struct ifnet *ifp, struct } void -igmp_joingroup(struct in_multi *inm, struct ifnet *ifp) +igmp_joingroup(struct in_multi *inm, struct ifnet *ifp, + struct igmp_pktinfo *pkt) { - int i, running = 0; + int running = 0; inm->inm_state = IGMP_IDLE_MEMBER; if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) && (ifp->if_flags & IFF_LOOPBACK) == 0) { - i = rti_fill(inm); - igmp_sendpkt(ifp, inm, i, 0); + pkt->ipi_addr = inm->inm_addr; + pkt->ipi_rdomain = ifp->if_rdomain; + pkt->ipi_ifidx = inm->inm_ifidx; + pkt->ipi_type = rti_fill(inm); + inm->inm_state = IGMP_DELAYING_MEMBER; inm->inm_timer = IGMP_RANDOM_DELAY( IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ); @@ -562,17 +565,20 @@ igmp_joingroup(struct in_multi *inm, str } void -igmp_leavegroup(struct in_multi *inm, struct ifnet *ifp) +igmp_leavegroup(struct in_multi *inm, struct ifnet *ifp, + struct igmp_pktinfo *pkt) { switch (inm->inm_state) { case IGMP_DELAYING_MEMBER: case IGMP_IDLE_MEMBER: if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) && (ifp->if_flags & IFF_LOOPBACK) == 0) - if (inm->inm_rti->rti_type != IGMP_v1_ROUTER) - igmp_sendpkt(ifp, inm, - IGMP_HOST_LEAVE_MESSAGE, - INADDR_ALLROUTERS_GROUP); + if (inm->inm_rti->rti_type != IGMP_v1_ROUTER) { + pkt->ipi_addr.s_addr = INADDR_ALLROUTERS_GROUP; + pkt->ipi_rdomain = ifp->if_rdomain; + pkt->ipi_ifidx = inm->inm_ifidx; + pkt->ipi_type = IGMP_HOST_LEAVE_MESSAGE; + } break; case IGMP_LAZY_MEMBER: case IGMP_AWAKENING_MEMBER: @@ -584,6 +590,7 @@ igmp_leavegroup(struct in_multi *inm, st void igmp_fasttimo(void) { + struct igmp_pktlist pktlist; struct ifnet *ifp; int running = 0; @@ -600,19 +607,29 @@ igmp_fasttimo(void) NET_LOCK(); + STAILQ_INIT(&pktlist); TAILQ_FOREACH(ifp, &ifnetlist, if_list) { - if (igmp_checktimer(ifp)) + if (igmp_checktimer(ifp, &pktlist)) running = 1; } membar_producer(); atomic_store_int(&igmp_timers_are_running, running); + while (!STAILQ_EMPTY(&pktlist)) { + struct igmp_pktinfo *pkt; + + pkt = STAILQ_FIRST(&pktlist); + STAILQ_REMOVE_HEAD(&pktlist, ipi_list); + igmp_sendpkt(pkt); + free(pkt, M_MRTABLE, sizeof(*pkt)); + } + NET_UNLOCK(); } int -igmp_checktimer(struct ifnet *ifp) +igmp_checktimer(struct ifnet *ifp, struct igmp_pktlist *pktlist) { struct in_multi *inm; struct ifmaddr *ifma; @@ -628,13 +645,20 @@ igmp_checktimer(struct ifnet *ifp) /* do nothing */ } else if (--inm->inm_timer == 0) { if (inm->inm_state == IGMP_DELAYING_MEMBER) { - if (inm->inm_rti->rti_type == IGMP_v1_ROUTER) - igmp_sendpkt(ifp, inm, - IGMP_v1_HOST_MEMBERSHIP_REPORT, 0); - else - igmp_sendpkt(ifp, inm, - IGMP_v2_HOST_MEMBERSHIP_REPORT, 0); + struct igmp_pktinfo *pkt; + inm->inm_state = IGMP_IDLE_MEMBER; + pkt = malloc(sizeof(*pkt), M_MRTABLE, M_NOWAIT); + if (pkt == NULL) + continue; + pkt->ipi_addr = inm->inm_addr; + pkt->ipi_rdomain = ifp->if_rdomain; + pkt->ipi_ifidx = inm->inm_ifidx; + pkt->ipi_type = + inm->inm_rti->rti_type == IGMP_v1_ROUTER ? + IGMP_v1_HOST_MEMBERSHIP_REPORT : + IGMP_v2_HOST_MEMBERSHIP_REPORT; + STAILQ_INSERT_TAIL(pktlist, pkt, ipi_list); } } else { running = 1; @@ -660,8 +684,7 @@ igmp_slowtimo(void) } void -igmp_sendpkt(struct ifnet *ifp, struct in_multi *inm, int type, - in_addr_t addr) +igmp_sendpkt(struct igmp_pktinfo *pkt) { struct mbuf *m; struct igmp *igmp; @@ -686,25 +709,21 @@ igmp_sendpkt(struct ifnet *ifp, struct i ip->ip_off = 0; ip->ip_p = IPPROTO_IGMP; ip->ip_src.s_addr = INADDR_ANY; - if (addr) { - ip->ip_dst.s_addr = addr; - } else { - ip->ip_dst = inm->inm_addr; - } + ip->ip_dst = pkt->ipi_addr; m->m_data += sizeof(struct ip); m->m_len -= sizeof(struct ip); igmp = mtod(m, struct igmp *); - igmp->igmp_type = type; + igmp->igmp_type = pkt->ipi_type; igmp->igmp_code = 0; - igmp->igmp_group = inm->inm_addr; + igmp->igmp_group = pkt->ipi_addr; igmp->igmp_cksum = 0; igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN); m->m_data -= sizeof(struct ip); m->m_len += sizeof(struct ip); - m->m_pkthdr.ph_rtableid = ifp->if_rdomain; - imo.imo_ifidx = inm->inm_ifidx; + m->m_pkthdr.ph_rtableid = pkt->ipi_rdomain; + imo.imo_ifidx = pkt->ipi_ifidx; imo.imo_ttl = 1; /* @@ -712,7 +731,7 @@ igmp_sendpkt(struct ifnet *ifp, struct i * router, so that the process-level routing daemon can hear it. */ #ifdef MROUTING - imo.imo_loop = (ip_mrouter[ifp->if_rdomain] != NULL); + imo.imo_loop = (ip_mrouter[pkt->ipi_rdomain] != NULL); #else imo.imo_loop = 0; #endif /* MROUTING */ Index: netinet/igmp_var.h =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/igmp_var.h,v diff -u -p -r1.16 igmp_var.h --- netinet/igmp_var.h 2 Mar 2025 21:28:32 -0000 1.16 +++ netinet/igmp_var.h 6 Feb 2026 22:35:48 -0000 @@ -105,12 +105,26 @@ igmpstat_inc(enum igmpstat_counters c) */ #define IGMP_RANDOM_DELAY(X) (arc4random_uniform(X) + 1) +struct igmp_pktinfo { + STAILQ_ENTRY(igmp_pktinfo) ipi_list; + struct in_addr ipi_addr; + unsigned int ipi_rdomain; + unsigned int ipi_ifidx; + int ipi_type; +}; +STAILQ_HEAD(igmp_pktlist, igmp_pktinfo); + void igmp_init(void); int igmp_input(struct mbuf **, int *, int, int, struct netstack *); -void igmp_joingroup(struct in_multi *, struct ifnet *); -void igmp_leavegroup(struct in_multi *, struct ifnet *); +void igmp_joingroup(struct in_multi *, struct ifnet *, + struct igmp_pktinfo *); +void igmp_leavegroup(struct in_multi *, struct ifnet *, + struct igmp_pktinfo *); void igmp_fasttimo(void); void igmp_slowtimo(void); int igmp_sysctl(int *, u_int, void *, size_t *, void *, size_t); +void igmp_sendpkt(struct igmp_pktinfo *); + #endif /* _KERNEL */ + #endif /* _NETINET_IGMP_VAR_H_ */ Index: netinet/in.c =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/in.c,v diff -u -p -r1.192 in.c --- netinet/in.c 3 Jan 2026 14:10:04 -0000 1.192 +++ netinet/in.c 6 Feb 2026 22:40:20 -0000 @@ -872,6 +872,7 @@ struct in_multi * in_addmulti(const struct in_addr *addr, struct ifnet *ifp) { struct in_multi *inm; + struct igmp_pktinfo pkt; struct ifreq ifr; /* @@ -916,7 +917,10 @@ in_addmulti(const struct in_addr *addr, /* * Let IGMP know that we have joined a new IP multicast group. */ - igmp_joingroup(inm, ifp); + pkt.ipi_ifidx = 0; + igmp_joingroup(inm, ifp, &pkt); + if (pkt.ipi_ifidx) + igmp_sendpkt(&pkt); } return (inm); @@ -928,6 +932,7 @@ in_addmulti(const struct in_addr *addr, void in_delmulti(struct in_multi *inm) { + struct igmp_pktinfo pkt; struct ifreq ifr; struct ifnet *ifp; @@ -942,7 +947,10 @@ in_delmulti(struct in_multi *inm) * No remaining claims to this record; let IGMP know that * we are leaving the multicast group. */ - igmp_leavegroup(inm, ifp); + pkt.ipi_ifidx = 0; + igmp_leavegroup(inm, ifp, &pkt); + if (pkt.ipi_ifidx) + igmp_sendpkt(&pkt); /* * Notify the network driver to update its multicast Index: netinet6/in6.c =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/in6.c,v diff -u -p -r1.277 in6.c --- netinet6/in6.c 3 Jan 2026 14:10:04 -0000 1.277 +++ netinet6/in6.c 6 Feb 2026 23:04:44 -0000 @@ -1026,6 +1026,7 @@ in6_lookupmulti(const struct in6_addr *a struct in6_multi * in6_addmulti(const struct in6_addr *addr, struct ifnet *ifp, int *errorp) { + struct mld6_pktinfo pkt; struct in6_ifreq ifr; struct in6_multi *in6m; @@ -1079,7 +1080,10 @@ in6_addmulti(const struct in6_addr *addr * Let MLD6 know that we have joined a new IP6 multicast * group. */ - mld6_start_listening(in6m); + pkt.mpi_ifidx = 0; + mld6_start_listening(in6m, ifp, &pkt); + if (pkt.mpi_ifidx) + mld6_sendpkt(&pkt); } return (in6m); @@ -1091,39 +1095,45 @@ in6_addmulti(const struct in6_addr *addr void in6_delmulti(struct in6_multi *in6m) { + struct mld6_pktinfo pkt; struct in6_ifreq ifr; struct ifnet *ifp; NET_ASSERT_LOCKED(); - if (refcnt_rele(&in6m->in6m_refcnt) != 0) { + if (refcnt_rele(&in6m->in6m_refcnt) == 0) + return; + + ifp = if_get(in6m->in6m_ifidx); + if (ifp != NULL) { /* * No remaining claims to this record; let MLD6 know * that we are leaving the multicast group. */ - mld6_stop_listening(in6m); - ifp = if_get(in6m->in6m_ifidx); + pkt.mpi_ifidx = 0; + mld6_stop_listening(in6m, ifp, &pkt); + if (pkt.mpi_ifidx) + mld6_sendpkt(&pkt); /* * Notify the network driver to update its multicast * reception filter. */ - if (ifp != NULL) { - bzero(&ifr.ifr_addr, sizeof(struct sockaddr_in6)); - ifr.ifr_addr.sin6_len = sizeof(struct sockaddr_in6); - ifr.ifr_addr.sin6_family = AF_INET6; - ifr.ifr_addr.sin6_addr = in6m->in6m_addr; - KERNEL_LOCK(); - (*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)&ifr); - KERNEL_UNLOCK(); + bzero(&ifr.ifr_addr, sizeof(struct sockaddr_in6)); + ifr.ifr_addr.sin6_len = sizeof(struct sockaddr_in6); + ifr.ifr_addr.sin6_family = AF_INET6; + ifr.ifr_addr.sin6_addr = in6m->in6m_addr; + KERNEL_LOCK(); + (*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)&ifr); + KERNEL_UNLOCK(); - TAILQ_REMOVE(&ifp->if_maddrlist, &in6m->in6m_ifma, - ifma_list); - } - if_put(ifp); + TAILQ_REMOVE(&ifp->if_maddrlist, &in6m->in6m_ifma, + ifma_list); - free(in6m, M_IPMADDR, sizeof(*in6m)); + if_put(ifp); } + + free(in6m, M_IPMADDR, sizeof(*in6m)); } /* Index: netinet6/mld6.c =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/mld6.c,v diff -u -p -r1.72 mld6.c --- netinet6/mld6.c 3 Jan 2026 14:10:04 -0000 1.72 +++ netinet6/mld6.c 6 Feb 2026 22:43:01 -0000 @@ -86,7 +86,6 @@ static struct ip6_pktopts ip6_opts; int mld6_timers_are_running; /* [a] shortcut for fast timer */ int mld6_checktimer(struct ifnet *); -static void mld6_sendpkt(struct in6_multi *, int, const struct in6_addr *); void mld6_init(void) @@ -112,7 +111,8 @@ mld6_init(void) } void -mld6_start_listening(struct in6_multi *in6m) +mld6_start_listening(struct in6_multi *in6m, struct ifnet *ifp, + struct mld6_pktinfo *pkt) { /* XXX: These are necessary for KAME's link-local hack */ struct in6_addr all_nodes = IN6ADDR_LINKLOCAL_ALLNODES_INIT; @@ -132,10 +132,12 @@ mld6_start_listening(struct in6_multi *i in6m->in6m_timer = 0; in6m->in6m_state = MLD_OTHERLISTENER; } else { - mld6_sendpkt(in6m, MLD_LISTENER_REPORT, NULL); + pkt->mpi_addr = in6m->in6m_addr; + pkt->mpi_rdomain = ifp->if_rdomain; + pkt->mpi_ifidx = in6m->in6m_ifidx; + pkt->mpi_type = MLD_LISTENER_REPORT; in6m->in6m_timer = - MLD_RANDOM_DELAY(MLD_V1_MAX_RI * - PR_FASTHZ); + MLD_RANDOM_DELAY(MLD_V1_MAX_RI * PR_FASTHZ); in6m->in6m_state = MLD_IREPORTEDLAST; running = 1; } @@ -147,7 +149,8 @@ mld6_start_listening(struct in6_multi *i } void -mld6_stop_listening(struct in6_multi *in6m) +mld6_stop_listening(struct in6_multi *in6m, struct ifnet *ifp, + struct mld6_pktinfo *pkt) { /* XXX: These are necessary for KAME's link-local hack */ struct in6_addr all_nodes = IN6ADDR_LINKLOCAL_ALLNODES_INIT; @@ -160,8 +163,12 @@ mld6_stop_listening(struct in6_multi *in if (in6m->in6m_state == MLD_IREPORTEDLAST && (!IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &all_nodes)) && __IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) > - __IPV6_ADDR_SCOPE_INTFACELOCAL) - mld6_sendpkt(in6m, MLD_LISTENER_DONE, &all_routers); + __IPV6_ADDR_SCOPE_INTFACELOCAL) { + pkt->mpi_addr = all_routers; + pkt->mpi_rdomain = ifp->if_rdomain; + pkt->mpi_ifidx = in6m->in6m_ifidx; + pkt->mpi_type = MLD_LISTENER_DONE; + } } void @@ -269,8 +276,13 @@ mld6_input(struct mbuf *m, int off) { if (timer == 0) { /* send a report immediately */ - mld6_sendpkt(in6m, MLD_LISTENER_REPORT, - NULL); + struct mld6_pktinfo pkt; + + pkt.mpi_addr = in6m->in6m_addr; + pkt.mpi_rdomain = ifp->if_rdomain; + pkt.mpi_ifidx = in6m->in6m_ifidx; + pkt.mpi_type = MLD_LISTENER_REPORT; + mld6_sendpkt(&pkt); in6m->in6m_timer = 0; /* reset timer */ in6m->in6m_state = MLD_IREPORTEDLAST; } else if (in6m->in6m_timer == 0 || /* idle */ @@ -373,10 +385,12 @@ mld6_checktimer(struct ifnet *ifp) { struct in6_multi *in6m; struct ifmaddr *ifma; + struct mld6_pktlist pktlist; int running = 0; NET_ASSERT_LOCKED(); + STAILQ_INIT(&pktlist); TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) { if (ifma->ifma_addr->sa_family != AF_INET6) continue; @@ -384,18 +398,36 @@ mld6_checktimer(struct ifnet *ifp) if (in6m->in6m_timer == 0) { /* do nothing */ } else if (--in6m->in6m_timer == 0) { - mld6_sendpkt(in6m, MLD_LISTENER_REPORT, NULL); + struct mld6_pktinfo *pkt; + in6m->in6m_state = MLD_IREPORTEDLAST; + pkt = malloc(sizeof(*pkt), M_MRTABLE, M_NOWAIT); + if (pkt == NULL) + continue; + pkt->mpi_addr = in6m->in6m_addr; + pkt->mpi_rdomain = ifp->if_rdomain; + pkt->mpi_ifidx = in6m->in6m_ifidx; + pkt->mpi_type = MLD_LISTENER_REPORT; + STAILQ_INSERT_TAIL(&pktlist, pkt, mpi_list); } else { running = 1; } } + while (!STAILQ_EMPTY(&pktlist)) { + struct mld6_pktinfo *pkt; + + pkt = STAILQ_FIRST(&pktlist); + STAILQ_REMOVE_HEAD(&pktlist, mpi_list); + mld6_sendpkt(pkt); + free(pkt, M_MRTABLE, sizeof(*pkt)); + } + return (running); } -static void -mld6_sendpkt(struct in6_multi *in6m, int type, const struct in6_addr *dst) +void +mld6_sendpkt(const struct mld6_pktinfo *pkt) { struct mbuf *mh, *md; struct mld_hdr *mldh; @@ -405,7 +437,7 @@ mld6_sendpkt(struct in6_multi *in6m, int struct ifnet *ifp; int ignflags; - ifp = if_get(in6m->in6m_ifidx); + ifp = if_get(pkt->mpi_ifidx); if (ifp == NULL) return; @@ -441,8 +473,7 @@ mld6_sendpkt(struct in6_multi *in6m, int } mh->m_next = md; - mh->m_pkthdr.ph_ifidx = 0; - mh->m_pkthdr.ph_rtableid = ifp->if_rdomain; + mh->m_pkthdr.ph_rtableid = pkt->mpi_rdomain; mh->m_pkthdr.len = sizeof(struct ip6_hdr) + sizeof(struct mld_hdr); mh->m_len = sizeof(struct ip6_hdr); m_align(mh, sizeof(struct ip6_hdr)); @@ -456,25 +487,25 @@ mld6_sendpkt(struct in6_multi *in6m, int ip6->ip6_nxt = IPPROTO_ICMPV6; /* ip6_hlim will be set by im6o.im6o_hlim */ ip6->ip6_src = ia6 ? ia6->ia_addr.sin6_addr : in6addr_any; - ip6->ip6_dst = dst ? *dst : in6m->in6m_addr; + ip6->ip6_dst = pkt->mpi_addr; /* fill in the MLD header */ md->m_len = sizeof(struct mld_hdr); mldh = mtod(md, struct mld_hdr *); - mldh->mld_type = type; + mldh->mld_type = pkt->mpi_type; mldh->mld_code = 0; mldh->mld_cksum = 0; /* XXX: we assume the function will not be called for query messages */ mldh->mld_maxdelay = 0; mldh->mld_reserved = 0; - mldh->mld_addr = in6m->in6m_addr; + mldh->mld_addr = pkt->mpi_addr; if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr)) mldh->mld_addr.s6_addr16[1] = 0; /* XXX */ mh->m_pkthdr.csum_flags |= M_ICMP_CSUM_OUT; /* construct multicast option */ bzero(&im6o, sizeof(im6o)); - im6o.im6o_ifidx = ifp->if_index; + im6o.im6o_ifidx = pkt->mpi_ifidx; im6o.im6o_hlim = 1; /* @@ -482,10 +513,10 @@ mld6_sendpkt(struct in6_multi *in6m, int * router, so that the process-level routing daemon can hear it. */ #ifdef MROUTING - im6o.im6o_loop = (ip6_mrouter[ifp->if_rdomain] != NULL); + im6o.im6o_loop = (ip6_mrouter[pkt->mpi_rdomain] != NULL); #endif if_put(ifp); - icmp6stat_inc(icp6s_outhist + type); + icmp6stat_inc(icp6s_outhist + pkt->mpi_type); ip6_output(mh, &ip6_opts, NULL, ia6 ? 0 : IPV6_UNSPECSRC, &im6o, NULL); } Index: netinet6/mld6_var.h =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/mld6_var.h,v diff -u -p -r1.9 mld6_var.h --- netinet6/mld6_var.h 3 Jan 2026 14:10:04 -0000 1.9 +++ netinet6/mld6_var.h 6 Feb 2026 22:43:14 -0000 @@ -43,11 +43,24 @@ #define MLD_OTHERLISTENER 0 #define MLD_IREPORTEDLAST 1 +struct mld6_pktinfo { + STAILQ_ENTRY(mld6_pktinfo) mpi_list; + struct in6_addr mpi_addr; + unsigned int mpi_rdomain; + unsigned int mpi_ifidx; + int mpi_type; +}; +STAILQ_HEAD(mld6_pktlist, mld6_pktinfo); + void mld6_init(void); void mld6_input(struct mbuf *, int); -void mld6_start_listening(struct in6_multi *); -void mld6_stop_listening(struct in6_multi *); +void mld6_start_listening(struct in6_multi *, struct ifnet *, + struct mld6_pktinfo *); +void mld6_stop_listening(struct in6_multi *, struct ifnet *, + struct mld6_pktinfo *); void mld6_fasttimo(void); +void mld6_sendpkt(const struct mld6_pktinfo *); + #endif /* _KERNEL */ #endif /* _NETINET6_MLD6_VAR_H_ */