From: Alexander Bluhm Subject: unlock igmp and mld6 To: tech@openbsd.org Cc: Vitaliy Makkoveev Date: Sun, 1 Mar 2026 18:38:16 +0100 Hi, After moving the code sending IGMP and ICMP6 packets out of the critical section, I think we an introduve a mutlicast lock. This allows us to run igmp and mld6 timers with shared netlock. ok? bluhm Index: net/if.c =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/net/if.c,v diff -u -p -r1.762 if.c --- net/if.c 3 Jan 2026 14:10:04 -0000 1.762 +++ net/if.c 1 Mar 2026 17:01:05 -0000 @@ -662,6 +662,7 @@ if_attach_common(struct ifnet *ifp) TAILQ_INIT(&ifp->if_addrlist); TAILQ_INIT(&ifp->if_maddrlist); TAILQ_INIT(&ifp->if_groups); + rw_init(&ifp->if_maddrlock, "maddr"); if (!ISSET(ifp->if_xflags, IFXF_MPSAFE)) { KASSERTMSG(ifp->if_qstart == NULL, Index: net/if_var.h =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/net/if_var.h,v diff -u -p -r1.147 if_var.h --- net/if_var.h 3 Jan 2026 14:10:04 -0000 1.147 +++ net/if_var.h 1 Mar 2026 17:01:05 -0000 @@ -81,6 +81,7 @@ * K kernel lock * N net lock * T if_tmplist_lock + * m interface multicast rwlock if_maddrlock * * For SRP related structures that allow lock-free reads, the write lock * is indicated below. @@ -153,8 +154,9 @@ struct ifnet { /* and the entries */ TAILQ_ENTRY(ifnet) if_list; /* [NK] all struct ifnets are chained */ TAILQ_ENTRY(ifnet) if_tmplist; /* [T] temporary list */ TAILQ_HEAD(, ifaddr) if_addrlist; /* [N] list of addresses per if */ - TAILQ_HEAD(, ifmaddr) if_maddrlist; /* [N] list of multicast records */ + TAILQ_HEAD(, ifmaddr) if_maddrlist; /* [m] list of multicast records */ TAILQ_HEAD(, ifg_list) if_groups; /* [N] list of groups per if */ + struct rwlock if_maddrlock; struct task_list if_addrhooks; /* [I] address change callbacks */ struct task_list if_linkstatehooks; /* [I] link change callbacks*/ struct task_list if_detachhooks; /* [I] detach callbacks */ @@ -273,10 +275,10 @@ struct ifaddr { * Interface multicast address. */ struct ifmaddr { - struct sockaddr *ifma_addr; /* Protocol address */ - unsigned int ifma_ifidx; /* Index of the interface */ + TAILQ_ENTRY(ifmaddr) ifma_list; /* [m] Per-interface list */ + struct sockaddr *ifma_addr; /* [I] Protocol address */ struct refcnt ifma_refcnt; /* Count of references */ - TAILQ_ENTRY(ifmaddr) ifma_list; /* Per-interface list */ + unsigned int ifma_ifidx; /* [I] Index of the interface */ }; /* Index: netinet/igmp.c =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/igmp.c,v diff -u -p -r1.96 igmp.c --- netinet/igmp.c 26 Feb 2026 00:53:18 -0000 1.96 +++ netinet/igmp.c 1 Mar 2026 17:22:24 -0000 @@ -340,6 +340,7 @@ igmp_input_if(struct ifnet *ifp, struct * except those that are already running and those * that belong to a "local" group (224.0.0.X). */ + rw_enter_write(&ifp->if_maddrlock); TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) { if (ifma->ifma_addr->sa_family != AF_INET) continue; @@ -352,6 +353,7 @@ igmp_input_if(struct ifnet *ifp, struct running = 1; } } + rw_exit_write(&ifp->if_maddrlock); } else { if (!IN_MULTICAST(ip->ip_dst.s_addr)) { igmpstat_inc(igps_rcv_badqueries); @@ -371,6 +373,7 @@ igmp_input_if(struct ifnet *ifp, struct * timers already running, check if they need to be * reset. */ + rw_enter_write(&ifp->if_maddrlock); TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) { if (ifma->ifma_addr->sa_family != AF_INET) continue; @@ -399,6 +402,7 @@ igmp_input_if(struct ifnet *ifp, struct } } } + rw_exit_write(&ifp->if_maddrlock); } break; @@ -435,6 +439,7 @@ igmp_input_if(struct ifnet *ifp, struct * If we belong to the group being reported, stop * our timer for that group. */ + rw_enter_write(&ifp->if_maddrlock); inm = in_lookupmulti(&igmp->igmp_group, ifp); if (inm != NULL) { inm->inm_timer = 0; @@ -455,6 +460,7 @@ igmp_input_if(struct ifnet *ifp, struct break; } } + rw_exit_write(&ifp->if_maddrlock); break; @@ -503,6 +509,7 @@ igmp_input_if(struct ifnet *ifp, struct * If we belong to the group being reported, stop * our timer for that group. */ + rw_enter_write(&ifp->if_maddrlock); inm = in_lookupmulti(&igmp->igmp_group, ifp); if (inm != NULL) { inm->inm_timer = 0; @@ -519,6 +526,7 @@ igmp_input_if(struct ifnet *ifp, struct break; } } + rw_exit_write(&ifp->if_maddrlock); break; @@ -542,18 +550,19 @@ igmp_joingroup(struct in_multi *inm, str { int running = 0; + rw_assert_wrlock(&ifp->if_maddrlock); + inm->inm_state = IGMP_IDLE_MEMBER; if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) && (ifp->if_flags & IFF_LOOPBACK) == 0) { + inm->inm_state = IGMP_DELAYING_MEMBER; + inm->inm_timer = IGMP_RANDOM_DELAY( + IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ); 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); running = 1; } else inm->inm_timer = 0; @@ -568,6 +577,8 @@ void igmp_leavegroup(struct in_multi *inm, struct ifnet *ifp, struct igmp_pktinfo *pkt) { + rw_assert_anylock(&ifp->if_maddrlock); + switch (inm->inm_state) { case IGMP_DELAYING_MEMBER: case IGMP_IDLE_MEMBER: @@ -605,7 +616,7 @@ igmp_fasttimo(void) return; membar_consumer(); - NET_LOCK(); + NET_LOCK_SHARED(); STAILQ_INIT(&pktlist); TAILQ_FOREACH(ifp, &ifnetlist, if_list) { @@ -625,7 +636,7 @@ igmp_fasttimo(void) free(pkt, M_MRTABLE, sizeof(*pkt)); } - NET_UNLOCK(); + NET_UNLOCK_SHARED(); } int @@ -635,8 +646,7 @@ igmp_checktimer(struct ifnet *ifp, struc struct ifmaddr *ifma; int running = 0; - NET_ASSERT_LOCKED(); - + rw_enter_write(&ifp->if_maddrlock); TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) { if (ifma->ifma_addr->sa_family != AF_INET) continue; @@ -664,6 +674,7 @@ igmp_checktimer(struct ifnet *ifp, struc running = 1; } } + rw_exit_write(&ifp->if_maddrlock); return (running); } Index: netinet/in.c =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/in.c,v diff -u -p -r1.193 in.c --- netinet/in.c 26 Feb 2026 00:53:18 -0000 1.193 +++ netinet/in.c 1 Mar 2026 17:01:05 -0000 @@ -853,7 +853,7 @@ in_lookupmulti(const struct in_addr *add struct in_multi *inm = NULL; struct ifmaddr *ifma; - NET_ASSERT_LOCKED(); + rw_assert_anylock(&ifp->if_maddrlock); TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) { if (ifma->ifma_addr->sa_family == AF_INET && @@ -871,58 +871,70 @@ in_lookupmulti(const struct in_addr *add struct in_multi * in_addmulti(const struct in_addr *addr, struct ifnet *ifp) { - struct in_multi *inm; + struct in_multi *inm, *new_inm = NULL; struct igmp_pktinfo pkt; struct ifreq ifr; /* * See if address already in list. */ + rw_enter_write(&ifp->if_maddrlock); inm = in_lookupmulti(addr, ifp); - if (inm != NULL) { - /* - * Found it; just increment the reference count. - */ - refcnt_take(&inm->inm_refcnt); - } else { - /* - * New address; allocate a new multicast record - * and link it into the interface's multicast list. - */ - inm = malloc(sizeof(*inm), M_IPMADDR, M_WAITOK | M_ZERO); - inm->inm_sin.sin_len = sizeof(struct sockaddr_in); - inm->inm_sin.sin_family = AF_INET; - inm->inm_sin.sin_addr = *addr; - refcnt_init_trace(&inm->inm_refcnt, DT_REFCNT_IDX_IFMADDR); - inm->inm_ifidx = ifp->if_index; - inm->inm_ifma.ifma_addr = sintosa(&inm->inm_sin); + if (inm != NULL) + goto found; + rw_exit_write(&ifp->if_maddrlock); - /* - * Ask the network driver to update its multicast reception - * filter appropriately for the new address. - */ - memset(&ifr, 0, sizeof(ifr)); - memcpy(&ifr.ifr_addr, &inm->inm_sin, sizeof(inm->inm_sin)); - KERNEL_LOCK(); - if ((*ifp->if_ioctl)(ifp, SIOCADDMULTI,(caddr_t)&ifr) != 0) { - KERNEL_UNLOCK(); - free(inm, M_IPMADDR, sizeof(*inm)); - return (NULL); - } + /* + * New address; allocate a new multicast record + * and link it into the interface's multicast list. + */ + new_inm = malloc(sizeof(*inm), M_IPMADDR, M_WAITOK | M_ZERO); + + /* + * Ask the network driver to update its multicast reception + * filter appropriately for the new address. + */ + memset(&ifr, 0, sizeof(ifr)); + satosin(&ifr.ifr_addr)->sin_len = sizeof(struct sockaddr_in); + satosin(&ifr.ifr_addr)->sin_family = AF_INET; + satosin(&ifr.ifr_addr)->sin_addr = *addr; + KERNEL_LOCK(); + if ((*ifp->if_ioctl)(ifp, SIOCADDMULTI,(caddr_t)&ifr) != 0) { KERNEL_UNLOCK(); + goto out; + } + KERNEL_UNLOCK(); - TAILQ_INSERT_HEAD(&ifp->if_maddrlist, &inm->inm_ifma, - ifma_list); + rw_enter_write(&ifp->if_maddrlock); + /* check again after unlock and lock */ + inm = in_lookupmulti(addr, ifp); + if (inm != NULL) + goto found; + inm = new_inm; + inm->inm_sin.sin_len = sizeof(struct sockaddr_in); + inm->inm_sin.sin_family = AF_INET; + inm->inm_sin.sin_addr = *addr; + refcnt_init_trace(&inm->inm_refcnt, DT_REFCNT_IDX_IFMADDR); + inm->inm_ifidx = ifp->if_index; + inm->inm_ifma.ifma_addr = sintosa(&inm->inm_sin); - /* - * Let IGMP know that we have joined a new IP multicast group. - */ - pkt.ipi_ifidx = 0; - igmp_joingroup(inm, ifp, &pkt); - if (pkt.ipi_ifidx) - igmp_sendpkt(&pkt); - } + /* + * Let IGMP know that we have joined a new IP multicast group. + */ + TAILQ_INSERT_HEAD(&ifp->if_maddrlist, &inm->inm_ifma, ifma_list); + pkt.ipi_ifidx = 0; + igmp_joingroup(inm, ifp, &pkt); + rw_exit_write(&ifp->if_maddrlock); + + if (pkt.ipi_ifidx) + igmp_sendpkt(&pkt); + return (inm); + found: + refcnt_take(&inm->inm_refcnt); + rw_exit_write(&ifp->if_maddrlock); + out: + free(new_inm, M_IPMADDR, sizeof(*inm)); return (inm); } @@ -936,19 +948,21 @@ in_delmulti(struct in_multi *inm) struct ifreq ifr; struct ifnet *ifp; - NET_ASSERT_LOCKED(); - if (refcnt_rele(&inm->inm_refcnt) == 0) return; ifp = if_get(inm->inm_ifidx); if (ifp != NULL) { + rw_enter_write(&ifp->if_maddrlock); /* * No remaining claims to this record; let IGMP know that * we are leaving the multicast group. */ pkt.ipi_ifidx = 0; igmp_leavegroup(inm, ifp, &pkt); + TAILQ_REMOVE(&ifp->if_maddrlist, &inm->inm_ifma, ifma_list); + rw_exit_write(&ifp->if_maddrlock); + if (pkt.ipi_ifidx) igmp_sendpkt(&pkt); @@ -964,9 +978,8 @@ in_delmulti(struct in_multi *inm) (*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)&ifr); KERNEL_UNLOCK(); - TAILQ_REMOVE(&ifp->if_maddrlist, &inm->inm_ifma, ifma_list); + if_put(ifp); } - if_put(ifp); free(inm, M_IPMADDR, sizeof(*inm)); } @@ -981,8 +994,10 @@ in_hasmulti(const struct in_addr *addr, struct in_multi *inm; int joined; + rw_enter_read(&ifp->if_maddrlock); inm = in_lookupmulti(addr, ifp); joined = (inm != NULL); + rw_exit_read(&ifp->if_maddrlock); return (joined); } Index: netinet/in_var.h =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/in_var.h,v diff -u -p -r1.46 in_var.h --- netinet/in_var.h 3 Jan 2026 14:10:04 -0000 1.46 +++ netinet/in_var.h 1 Mar 2026 17:01:05 -0000 @@ -35,6 +35,12 @@ #ifndef _NETINET_IN_VAR_H_ #define _NETINET_IN_VAR_H_ +/* + * Locks used to protect struct members in this file: + * I immutable after creation + * m multicast if_maddrlock rwlock of parent interface + */ + #include #ifdef _KERNEL @@ -87,11 +93,11 @@ struct in_multi { #define inm_refcnt inm_ifma.ifma_refcnt #define inm_ifidx inm_ifma.ifma_ifidx - struct sockaddr_in inm_sin; /* IPv4 multicast address */ + struct sockaddr_in inm_sin; /* [I] IPv4 multicast address */ #define inm_addr inm_sin.sin_addr - u_int inm_state; /* state of membership */ - u_int inm_timer; /* IGMP membership report timer */ + u_int inm_state; /* [m] state of membership */ + u_int inm_timer; /* [m] IGMP membership report */ struct router_info *inm_rti; /* router version info */ }; Index: netinet6/in6.c =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/in6.c,v diff -u -p -r1.278 in6.c --- netinet6/in6.c 26 Feb 2026 00:53:18 -0000 1.278 +++ netinet6/in6.c 1 Mar 2026 17:01:05 -0000 @@ -1007,7 +1007,7 @@ in6_lookupmulti(const struct in6_addr *a struct in6_multi *in6m = NULL; struct ifmaddr *ifma; - NET_ASSERT_LOCKED(); + rw_assert_anylock(&ifp->if_maddrlock); TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) { if (ifma->ifma_addr->sa_family == AF_INET6 && @@ -1026,66 +1026,75 @@ in6_lookupmulti(const struct in6_addr *a struct in6_multi * in6_addmulti(const struct in6_addr *addr, struct ifnet *ifp, int *errorp) { + struct in6_multi *in6m, *new_in6m = NULL; struct mld6_pktinfo pkt; struct in6_ifreq ifr; - struct in6_multi *in6m; - - NET_ASSERT_LOCKED(); *errorp = 0; /* * See if address already in list. */ + rw_enter_write(&ifp->if_maddrlock); in6m = in6_lookupmulti(addr, ifp); - if (in6m != NULL) { - /* - * Found it; just increment the reference count. - */ - refcnt_take(&in6m->in6m_refcnt); - } else { - /* - * New address; allocate a new multicast record - * and link it into the interface's multicast list. - */ - in6m = malloc(sizeof(*in6m), M_IPMADDR, M_NOWAIT | M_ZERO); - if (in6m == NULL) { - *errorp = ENOBUFS; - return (NULL); - } + if (in6m != NULL) + goto found; + rw_exit_write(&ifp->if_maddrlock); - in6m->in6m_sin.sin6_len = sizeof(struct sockaddr_in6); - in6m->in6m_sin.sin6_family = AF_INET6; - in6m->in6m_sin.sin6_addr = *addr; - refcnt_init_trace(&in6m->in6m_refcnt, DT_REFCNT_IDX_IFMADDR); - in6m->in6m_ifidx = ifp->if_index; - in6m->in6m_ifma.ifma_addr = sin6tosa(&in6m->in6m_sin); + /* + * New address; allocate a new multicast record + * and link it into the interface's multicast list. + */ + new_in6m = malloc(sizeof(*in6m), M_IPMADDR, M_NOWAIT | M_ZERO); + if (new_in6m == NULL) { + *errorp = ENOBUFS; + return (NULL); + } - /* - * Ask the network driver to update its multicast reception - * filter appropriately for the new address. - */ - memcpy(&ifr.ifr_addr, &in6m->in6m_sin, sizeof(in6m->in6m_sin)); - KERNEL_LOCK(); - *errorp = (*ifp->if_ioctl)(ifp, SIOCADDMULTI, (caddr_t)&ifr); - KERNEL_UNLOCK(); - if (*errorp) { - free(in6m, M_IPMADDR, sizeof(*in6m)); - return (NULL); - } + /* + * Ask the network driver to update its multicast reception + * filter appropriately for the new address. + */ + 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 = *addr; + KERNEL_LOCK(); + *errorp = (*ifp->if_ioctl)(ifp, SIOCADDMULTI, (caddr_t)&ifr); + KERNEL_UNLOCK(); + if (*errorp) + goto out; - TAILQ_INSERT_HEAD(&ifp->if_maddrlist, &in6m->in6m_ifma, - ifma_list); + rw_enter_write(&ifp->if_maddrlock); + /* check again after unlock and lock */ + in6m = in6_lookupmulti(addr, ifp); + if (in6m != NULL) + goto found; + in6m = new_in6m; + in6m->in6m_sin.sin6_len = sizeof(struct sockaddr_in6); + in6m->in6m_sin.sin6_family = AF_INET6; + in6m->in6m_sin.sin6_addr = *addr; + refcnt_init_trace(&in6m->in6m_refcnt, DT_REFCNT_IDX_IFMADDR); + in6m->in6m_ifidx = ifp->if_index; + in6m->in6m_ifma.ifma_addr = sin6tosa(&in6m->in6m_sin); - /* - * Let MLD6 know that we have joined a new IP6 multicast - * group. - */ - pkt.mpi_ifidx = 0; - mld6_start_listening(in6m, ifp, &pkt); - if (pkt.mpi_ifidx) - mld6_sendpkt(&pkt); - } + /* + * Let MLD6 know that we have joined a new IP6 multicast group. + */ + TAILQ_INSERT_HEAD(&ifp->if_maddrlist, &in6m->in6m_ifma, ifma_list); + pkt.mpi_ifidx = 0; + mld6_start_listening(in6m, ifp, &pkt); + rw_exit_write(&ifp->if_maddrlock); + + if (pkt.mpi_ifidx) + mld6_sendpkt(&pkt); + + return (in6m); + found: + refcnt_take(&in6m->in6m_refcnt); + rw_exit_write(&ifp->if_maddrlock); + out: + free(new_in6m, M_IPMADDR, sizeof(*in6m)); return (in6m); } @@ -1099,19 +1108,21 @@ in6_delmulti(struct in6_multi *in6m) struct in6_ifreq ifr; struct ifnet *ifp; - NET_ASSERT_LOCKED(); - if (refcnt_rele(&in6m->in6m_refcnt) == 0) return; ifp = if_get(in6m->in6m_ifidx); if (ifp != NULL) { + rw_enter_write(&ifp->if_maddrlock); /* * No remaining claims to this record; let MLD6 know * that we are leaving the multicast group. */ pkt.mpi_ifidx = 0; mld6_stop_listening(in6m, ifp, &pkt); + TAILQ_REMOVE(&ifp->if_maddrlist, &in6m->in6m_ifma, ifma_list); + rw_exit_write(&ifp->if_maddrlock); + if (pkt.mpi_ifidx) mld6_sendpkt(&pkt); @@ -1119,7 +1130,7 @@ in6_delmulti(struct in6_multi *in6m) * Notify the network driver to update its multicast * reception filter. */ - bzero(&ifr.ifr_addr, sizeof(struct sockaddr_in6)); + 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 = in6m->in6m_addr; @@ -1127,9 +1138,6 @@ in6_delmulti(struct in6_multi *in6m) (*ifp->if_ioctl)(ifp, SIOCDELMULTI, (caddr_t)&ifr); KERNEL_UNLOCK(); - TAILQ_REMOVE(&ifp->if_maddrlist, &in6m->in6m_ifma, - ifma_list); - if_put(ifp); } @@ -1146,8 +1154,10 @@ in6_hasmulti(const struct in6_addr *addr struct in6_multi *in6m; int joined; + rw_enter_read(&ifp->if_maddrlock); in6m = in6_lookupmulti(addr, ifp); joined = (in6m != NULL); + rw_exit_read(&ifp->if_maddrlock); return (joined); } Index: netinet6/in6_var.h =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/in6_var.h,v diff -u -p -r1.84 in6_var.h --- netinet6/in6_var.h 3 Jan 2026 14:10:04 -0000 1.84 +++ netinet6/in6_var.h 1 Mar 2026 17:01:05 -0000 @@ -65,6 +65,12 @@ #define _NETINET6_IN6_VAR_H_ /* + * Locks used to protect struct members in this file: + * I immutable after creation + * m multicast if_maddrlock rwlock of parent interface + */ + +/* * Interface address, Internet version. One of these structures * is allocated for each interface with an Internet address. * The ifaddr structure contains the protocol-independent part @@ -316,11 +322,11 @@ struct in6_multi { #define in6m_refcnt in6m_ifma.ifma_refcnt #define in6m_ifidx in6m_ifma.ifma_ifidx - struct sockaddr_in6 in6m_sin; /* IPv6 multicast address */ + struct sockaddr_in6 in6m_sin; /* [I] IPv6 multicast address */ #define in6m_addr in6m_sin.sin6_addr - u_int in6m_state; /* state of membership */ - u_int in6m_timer; /* MLD6 membership report timer */ + u_int in6m_state; /* [m] state of membership */ + u_int in6m_timer; /* [m] MLD6 membership report */ }; static __inline struct in6_multi * Index: netinet6/mld6.c =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/mld6.c,v diff -u -p -r1.73 mld6.c --- netinet6/mld6.c 26 Feb 2026 00:53:18 -0000 1.73 +++ netinet6/mld6.c 1 Mar 2026 17:22:23 -0000 @@ -85,7 +85,7 @@ static struct ip6_pktopts ip6_opts; int mld6_timers_are_running; /* [a] shortcut for fast timer */ -int mld6_checktimer(struct ifnet *); +int mld6_checktimer(struct ifnet *, struct mld6_pktlist *); void mld6_init(void) @@ -118,6 +118,8 @@ mld6_start_listening(struct in6_multi *i struct in6_addr all_nodes = IN6ADDR_LINKLOCAL_ALLNODES_INIT; int running = 0; + rw_assert_wrlock(&ifp->if_maddrlock); + /* * RFC2710 page 10: * The node never sends a Report or Done for the link-scope all-nodes @@ -129,16 +131,16 @@ mld6_start_listening(struct in6_multi *i if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &all_nodes) || __IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) < __IPV6_ADDR_SCOPE_LINKLOCAL) { - in6m->in6m_timer = 0; in6m->in6m_state = MLD_OTHERLISTENER; + in6m->in6m_timer = 0; } else { + in6m->in6m_state = MLD_IREPORTEDLAST; + in6m->in6m_timer = + MLD_RANDOM_DELAY(MLD_V1_MAX_RI * PR_FASTHZ); 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); - in6m->in6m_state = MLD_IREPORTEDLAST; running = 1; } @@ -156,6 +158,8 @@ mld6_stop_listening(struct in6_multi *in struct in6_addr all_nodes = IN6ADDR_LINKLOCAL_ALLNODES_INIT; struct in6_addr all_routers = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT; + rw_assert_anylock(&ifp->if_maddrlock); + all_nodes.s6_addr16[1] = htons(in6m->in6m_ifidx); /* XXX: necessary when mrouting */ all_routers.s6_addr16[1] = htons(in6m->in6m_ifidx); @@ -228,7 +232,9 @@ mld6_input(struct mbuf *m, int off) * if we sent the last report. */ switch(mldh->mld_type) { - case MLD_LISTENER_QUERY: + case MLD_LISTENER_QUERY: { + struct mld6_pktlist pktlist; + if (ifp->if_flags & IFF_LOOPBACK) break; @@ -261,6 +267,8 @@ mld6_input(struct mbuf *m, int off) timer = 1; all_nodes.s6_addr16[1] = htons(ifp->if_index); + rw_enter_write(&ifp->if_maddrlock); + STAILQ_INIT(&pktlist); TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) { if (ifma->ifma_addr->sa_family != AF_INET6) continue; @@ -276,15 +284,20 @@ mld6_input(struct mbuf *m, int off) { if (timer == 0) { /* send a report immediately */ - struct mld6_pktinfo pkt; + 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; + in6m->in6m_timer = 0; /* reset timer */ + 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 if (in6m->in6m_timer == 0 || /* idle */ in6m->in6m_timer > timer) { in6m->in6m_timer = @@ -293,10 +306,21 @@ mld6_input(struct mbuf *m, int off) } } } + rw_exit_write(&ifp->if_maddrlock); + + 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)); + } if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr)) mldh->mld_addr.s6_addr16[1] = 0; /* XXX */ break; + } case MLD_LISTENER_REPORT: /* * For fast leave to work, we have to know that we are the @@ -320,11 +344,13 @@ mld6_input(struct mbuf *m, int off) * If we belong to the group being reported, stop * our timer for that group. */ + rw_enter_write(&ifp->if_maddrlock); in6m = in6_lookupmulti(&mldh->mld_addr, ifp); if (in6m) { - in6m->in6m_timer = 0; /* transit to idle state */ in6m->in6m_state = MLD_OTHERLISTENER; /* clear flag */ + in6m->in6m_timer = 0; /* transit to idle state */ } + rw_exit_write(&ifp->if_maddrlock); if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr)) mldh->mld_addr.s6_addr16[1] = 0; /* XXX */ @@ -353,6 +379,7 @@ mld6_input(struct mbuf *m, int off) void mld6_fasttimo(void) { + struct mld6_pktlist pktlist; struct ifnet *ifp; int running = 0; @@ -367,30 +394,37 @@ mld6_fasttimo(void) return; membar_consumer(); - NET_LOCK(); + NET_LOCK_SHARED(); + STAILQ_INIT(&pktlist); TAILQ_FOREACH(ifp, &ifnetlist, if_list) { - if (mld6_checktimer(ifp)) + if (mld6_checktimer(ifp, &pktlist)) running = 1; } membar_producer(); atomic_store_int(&mld6_timers_are_running, running); - NET_UNLOCK(); + 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)); + } + + NET_UNLOCK_SHARED(); } int -mld6_checktimer(struct ifnet *ifp) +mld6_checktimer(struct ifnet *ifp, struct mld6_pktlist *pktlist) { struct in6_multi *in6m; struct ifmaddr *ifma; - struct mld6_pktlist pktlist; int running = 0; - NET_ASSERT_LOCKED(); - - STAILQ_INIT(&pktlist); + rw_enter_write(&ifp->if_maddrlock); TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) { if (ifma->ifma_addr->sa_family != AF_INET6) continue; @@ -408,20 +442,12 @@ mld6_checktimer(struct ifnet *ifp) 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); + 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)); - } + rw_exit_write(&ifp->if_maddrlock); return (running); }