Download raw body.
unlock igmp and mld6
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 <sys/queue.h>
#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);
}
unlock igmp and mld6