xref: /freebsd-src/sys/netinet/in_mcast.c (revision bbf86c65d04d6013fd3f7b6d74a341256c4e7336)
171498f30SBruce M Simpson /*-
2fe267a55SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
3fe267a55SPedro F. Giffuni  *
4d10910e6SBruce M Simpson  * Copyright (c) 2007-2009 Bruce Simpson.
571498f30SBruce M Simpson  * Copyright (c) 2005 Robert N. M. Watson.
671498f30SBruce M Simpson  * All rights reserved.
771498f30SBruce M Simpson  *
871498f30SBruce M Simpson  * Redistribution and use in source and binary forms, with or without
971498f30SBruce M Simpson  * modification, are permitted provided that the following conditions
1071498f30SBruce M Simpson  * are met:
1171498f30SBruce M Simpson  * 1. Redistributions of source code must retain the above copyright
1271498f30SBruce M Simpson  *    notice, this list of conditions and the following disclaimer.
1371498f30SBruce M Simpson  * 2. Redistributions in binary form must reproduce the above copyright
1471498f30SBruce M Simpson  *    notice, this list of conditions and the following disclaimer in the
1571498f30SBruce M Simpson  *    documentation and/or other materials provided with the distribution.
1671498f30SBruce M Simpson  * 3. The name of the author may not be used to endorse or promote
1771498f30SBruce M Simpson  *    products derived from this software without specific prior written
1871498f30SBruce M Simpson  *    permission.
1971498f30SBruce M Simpson  *
2071498f30SBruce M Simpson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2171498f30SBruce M Simpson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2271498f30SBruce M Simpson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2371498f30SBruce M Simpson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2471498f30SBruce M Simpson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2571498f30SBruce M Simpson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2671498f30SBruce M Simpson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2771498f30SBruce M Simpson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2871498f30SBruce M Simpson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2971498f30SBruce M Simpson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3071498f30SBruce M Simpson  * SUCH DAMAGE.
3171498f30SBruce M Simpson  */
3271498f30SBruce M Simpson 
3371498f30SBruce M Simpson /*
3471498f30SBruce M Simpson  * IPv4 multicast socket, group, and socket option processing module.
3571498f30SBruce M Simpson  */
3671498f30SBruce M Simpson 
3771498f30SBruce M Simpson #include <sys/param.h>
3871498f30SBruce M Simpson #include <sys/systm.h>
3971498f30SBruce M Simpson #include <sys/kernel.h>
40cc0a3c8cSAndrey V. Elsukov #include <sys/lock.h>
4171498f30SBruce M Simpson #include <sys/malloc.h>
4271498f30SBruce M Simpson #include <sys/mbuf.h>
43b244c8adSChristian S.J. Peron #include <sys/protosw.h>
4471498f30SBruce M Simpson #include <sys/socket.h>
4571498f30SBruce M Simpson #include <sys/socketvar.h>
46d10910e6SBruce M Simpson #include <sys/protosw.h>
4771498f30SBruce M Simpson #include <sys/sysctl.h>
48d10910e6SBruce M Simpson #include <sys/ktr.h>
49c23de1f4SJohn Baldwin #include <sys/taskqueue.h>
50d10910e6SBruce M Simpson #include <sys/tree.h>
5171498f30SBruce M Simpson 
5271498f30SBruce M Simpson #include <net/if.h>
5376039bc8SGleb Smirnoff #include <net/if_var.h>
5471498f30SBruce M Simpson #include <net/if_dl.h>
5571498f30SBruce M Simpson #include <net/route.h>
566ad7446cSAlexander V. Chernikov #include <net/route/nhop.h>
574b79449eSBjoern A. Zeeb #include <net/vnet.h>
5871498f30SBruce M Simpson 
59f3e1324bSStephen Hurd #include <net/ethernet.h>
60f3e1324bSStephen Hurd 
6171498f30SBruce M Simpson #include <netinet/in.h>
6271498f30SBruce M Simpson #include <netinet/in_systm.h>
639977be4aSAlexander V. Chernikov #include <netinet/in_fib.h>
6471498f30SBruce M Simpson #include <netinet/in_pcb.h>
6571498f30SBruce M Simpson #include <netinet/in_var.h>
663d0d5b21SJustin Hibbits #include <net/if_private.h>
6771498f30SBruce M Simpson #include <netinet/ip_var.h>
6871498f30SBruce M Simpson #include <netinet/igmp_var.h>
6971498f30SBruce M Simpson 
70d10910e6SBruce M Simpson #ifndef KTR_IGMPV3
71c566b476SBruce M Simpson #define KTR_IGMPV3 KTR_INET
72d10910e6SBruce M Simpson #endif
73d10910e6SBruce M Simpson 
7471498f30SBruce M Simpson #ifndef __SOCKUNION_DECLARED
7571498f30SBruce M Simpson union sockunion {
7671498f30SBruce M Simpson 	struct sockaddr_storage	ss;
7771498f30SBruce M Simpson 	struct sockaddr		sa;
7871498f30SBruce M Simpson 	struct sockaddr_dl	sdl;
7971498f30SBruce M Simpson 	struct sockaddr_in	sin;
8071498f30SBruce M Simpson };
8171498f30SBruce M Simpson typedef union sockunion sockunion_t;
8271498f30SBruce M Simpson #define __SOCKUNION_DECLARED
8371498f30SBruce M Simpson #endif /* __SOCKUNION_DECLARED */
8471498f30SBruce M Simpson 
85d10910e6SBruce M Simpson static MALLOC_DEFINE(M_INMFILTER, "in_mfilter",
86d10910e6SBruce M Simpson     "IPv4 multicast PCB-layer source filter");
8771498f30SBruce M Simpson static MALLOC_DEFINE(M_IPMADDR, "in_multi", "IPv4 multicast group");
8871498f30SBruce M Simpson static MALLOC_DEFINE(M_IPMOPTS, "ip_moptions", "IPv4 multicast options");
89d10910e6SBruce M Simpson static MALLOC_DEFINE(M_IPMSOURCE, "ip_msource",
90d10910e6SBruce M Simpson     "IPv4 multicast IGMP-layer source filter");
91d10910e6SBruce M Simpson 
9271498f30SBruce M Simpson /*
93d10910e6SBruce M Simpson  * Locking:
9459854ecfSHans Petter Selasky  *
95*bbf86c65SMark Johnston  * - Lock order is: IN_MULTI_LOCK, INP_WLOCK, IN_MULTI_LIST_LOCK, IGMP_LOCK,
96*bbf86c65SMark Johnston  *                  IF_ADDR_LOCK.
97d10910e6SBruce M Simpson  * - The IF_ADDR_LOCK is implicitly taken by inm_lookup() earlier, however
98d10910e6SBruce M Simpson  *   it can be taken by code in net/if.c also.
99d10910e6SBruce M Simpson  * - ip_moptions and in_mfilter are covered by the INP_WLOCK.
100d10910e6SBruce M Simpson  *
101f3e1324bSStephen Hurd  * struct in_multi is covered by IN_MULTI_LIST_LOCK. There isn't strictly
102d10910e6SBruce M Simpson  * any need for in_multi itself to be virtualized -- it is bound to an ifp
103d10910e6SBruce M Simpson  * anyway no matter what happens.
10471498f30SBruce M Simpson  */
105f3e1324bSStephen Hurd struct mtx in_multi_list_mtx;
106f3e1324bSStephen Hurd MTX_SYSINIT(in_multi_mtx, &in_multi_list_mtx, "in_multi_list_mtx", MTX_DEF);
107f3e1324bSStephen Hurd 
108f3e1324bSStephen Hurd struct mtx in_multi_free_mtx;
109f3e1324bSStephen Hurd MTX_SYSINIT(in_multi_free_mtx, &in_multi_free_mtx, "in_multi_free_mtx", MTX_DEF);
110f3e1324bSStephen Hurd 
111f3e1324bSStephen Hurd struct sx in_multi_sx;
112f3e1324bSStephen Hurd SX_SYSINIT(in_multi_sx, &in_multi_sx, "in_multi_sx");
11371498f30SBruce M Simpson 
11471498f30SBruce M Simpson /*
11571498f30SBruce M Simpson  * Functions with non-static linkage defined in this file should be
11671498f30SBruce M Simpson  * declared in in_var.h:
117d10910e6SBruce M Simpson  *  imo_multi_filter()
118d10910e6SBruce M Simpson  *  in_joingroup()
119d10910e6SBruce M Simpson  *  in_joingroup_locked()
120d10910e6SBruce M Simpson  *  in_leavegroup()
121d10910e6SBruce M Simpson  *  in_leavegroup_locked()
12271498f30SBruce M Simpson  * and ip_var.h:
12371498f30SBruce M Simpson  *  inp_freemoptions()
12471498f30SBruce M Simpson  *  inp_getmoptions()
12571498f30SBruce M Simpson  *  inp_setmoptions()
12671498f30SBruce M Simpson  */
127d10910e6SBruce M Simpson static void	imf_commit(struct in_mfilter *);
128d10910e6SBruce M Simpson static int	imf_get_source(struct in_mfilter *imf,
129d10910e6SBruce M Simpson 		    const struct sockaddr_in *psin,
130d10910e6SBruce M Simpson 		    struct in_msource **);
131d10910e6SBruce M Simpson static struct in_msource *
132d10910e6SBruce M Simpson 		imf_graft(struct in_mfilter *, const uint8_t,
133d10910e6SBruce M Simpson 		    const struct sockaddr_in *);
134d10910e6SBruce M Simpson static void	imf_leave(struct in_mfilter *);
135d10910e6SBruce M Simpson static int	imf_prune(struct in_mfilter *, const struct sockaddr_in *);
136d10910e6SBruce M Simpson static void	imf_purge(struct in_mfilter *);
137d10910e6SBruce M Simpson static void	imf_rollback(struct in_mfilter *);
138d10910e6SBruce M Simpson static void	imf_reap(struct in_mfilter *);
13959854ecfSHans Petter Selasky static struct in_mfilter *
14059854ecfSHans Petter Selasky 		imo_match_group(const struct ip_moptions *,
141d10910e6SBruce M Simpson 		    const struct ifnet *, const struct sockaddr *);
142d10910e6SBruce M Simpson static struct in_msource *
14359854ecfSHans Petter Selasky 		imo_match_source(struct in_mfilter *, const struct sockaddr *);
144d10910e6SBruce M Simpson static void	ims_merge(struct ip_msource *ims,
145d10910e6SBruce M Simpson 		    const struct in_msource *lims, const int rollback);
146d10910e6SBruce M Simpson static int	in_getmulti(struct ifnet *, const struct in_addr *,
147d10910e6SBruce M Simpson 		    struct in_multi **);
148d10910e6SBruce M Simpson static int	inm_get_source(struct in_multi *inm, const in_addr_t haddr,
149d10910e6SBruce M Simpson 		    const int noalloc, struct ip_msource **pims);
15036f54f0aSDimitry Andric #ifdef KTR
151d10910e6SBruce M Simpson static int	inm_is_ifp_detached(const struct in_multi *);
15236f54f0aSDimitry Andric #endif
153d10910e6SBruce M Simpson static int	inm_merge(struct in_multi *, /*const*/ struct in_mfilter *);
154d10910e6SBruce M Simpson static void	inm_purge(struct in_multi *);
155d10910e6SBruce M Simpson static void	inm_reap(struct in_multi *);
156f3e1324bSStephen Hurd static void inm_release(struct in_multi *);
15771498f30SBruce M Simpson static struct ip_moptions *
15871498f30SBruce M Simpson 		inp_findmoptions(struct inpcb *);
15971498f30SBruce M Simpson static int	inp_get_source_filters(struct inpcb *, struct sockopt *);
16071498f30SBruce M Simpson static int	inp_join_group(struct inpcb *, struct sockopt *);
16171498f30SBruce M Simpson static int	inp_leave_group(struct inpcb *, struct sockopt *);
162d10910e6SBruce M Simpson static struct ifnet *
163d10910e6SBruce M Simpson 		inp_lookup_mcast_ifp(const struct inpcb *,
164d10910e6SBruce M Simpson 		    const struct sockaddr_in *, const struct in_addr);
165d10910e6SBruce M Simpson static int	inp_block_unblock_source(struct inpcb *, struct sockopt *);
16671498f30SBruce M Simpson static int	inp_set_multicast_if(struct inpcb *, struct sockopt *);
16771498f30SBruce M Simpson static int	inp_set_source_filters(struct inpcb *, struct sockopt *);
168d10910e6SBruce M Simpson static int	sysctl_ip_mcast_filters(SYSCTL_HANDLER_ARGS);
16971498f30SBruce M Simpson 
1707029da5cSPawel Biernacki static SYSCTL_NODE(_net_inet_ip, OID_AUTO, mcast,
1717029da5cSPawel Biernacki     CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
1726472ac3dSEd Schouten     "IPv4 multicast");
173dd7fd7c0SBruce M Simpson 
174d10910e6SBruce M Simpson static u_long in_mcast_maxgrpsrc = IP_MAX_GROUP_SRC_FILTER;
175d10910e6SBruce M Simpson SYSCTL_ULONG(_net_inet_ip_mcast, OID_AUTO, maxgrpsrc,
176af3b2549SHans Petter Selasky     CTLFLAG_RWTUN, &in_mcast_maxgrpsrc, 0,
177d10910e6SBruce M Simpson     "Max source filters per group");
178d10910e6SBruce M Simpson 
179d10910e6SBruce M Simpson static u_long in_mcast_maxsocksrc = IP_MAX_SOCK_SRC_FILTER;
180d10910e6SBruce M Simpson SYSCTL_ULONG(_net_inet_ip_mcast, OID_AUTO, maxsocksrc,
181af3b2549SHans Petter Selasky     CTLFLAG_RWTUN, &in_mcast_maxsocksrc, 0,
182d10910e6SBruce M Simpson     "Max source filters per socket");
183d10910e6SBruce M Simpson 
184dd7fd7c0SBruce M Simpson int in_mcast_loop = IP_DEFAULT_MULTICAST_LOOP;
185af3b2549SHans Petter Selasky SYSCTL_INT(_net_inet_ip_mcast, OID_AUTO, loop, CTLFLAG_RWTUN,
186dd7fd7c0SBruce M Simpson     &in_mcast_loop, 0, "Loopback multicast datagrams by default");
187dd7fd7c0SBruce M Simpson 
1886472ac3dSEd Schouten static SYSCTL_NODE(_net_inet_ip_mcast, OID_AUTO, filters,
189d10910e6SBruce M Simpson     CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_ip_mcast_filters,
190d10910e6SBruce M Simpson     "Per-interface stack-wide source filters");
191d10910e6SBruce M Simpson 
19236f54f0aSDimitry Andric #ifdef KTR
193d10910e6SBruce M Simpson /*
194d10910e6SBruce M Simpson  * Inline function which wraps assertions for a valid ifp.
195d10910e6SBruce M Simpson  * The ifnet layer will set the ifma's ifp pointer to NULL if the ifp
196d10910e6SBruce M Simpson  * is detached.
197d10910e6SBruce M Simpson  */
198d10910e6SBruce M Simpson static int __inline
inm_is_ifp_detached(const struct in_multi * inm)199d10910e6SBruce M Simpson inm_is_ifp_detached(const struct in_multi *inm)
200d10910e6SBruce M Simpson {
201d10910e6SBruce M Simpson 	struct ifnet *ifp;
202d10910e6SBruce M Simpson 
203d10910e6SBruce M Simpson 	KASSERT(inm->inm_ifma != NULL, ("%s: no ifma", __func__));
204d10910e6SBruce M Simpson 	ifp = inm->inm_ifma->ifma_ifp;
205d10910e6SBruce M Simpson 	if (ifp != NULL) {
206d10910e6SBruce M Simpson 		/*
207d10910e6SBruce M Simpson 		 * Sanity check that netinet's notion of ifp is the
208d10910e6SBruce M Simpson 		 * same as net's.
209d10910e6SBruce M Simpson 		 */
210d10910e6SBruce M Simpson 		KASSERT(inm->inm_ifp == ifp, ("%s: bad ifp", __func__));
211d10910e6SBruce M Simpson 	}
212d10910e6SBruce M Simpson 
213d10910e6SBruce M Simpson 	return (ifp == NULL);
214d10910e6SBruce M Simpson }
21536f54f0aSDimitry Andric #endif
216d10910e6SBruce M Simpson 
2173689652cSHans Petter Selasky /*
2183689652cSHans Petter Selasky  * Interface detach can happen in a taskqueue thread context, so we must use a
2193689652cSHans Petter Selasky  * dedicated thread to avoid deadlocks when draining inm_release tasks.
2203689652cSHans Petter Selasky  */
2213689652cSHans Petter Selasky TASKQUEUE_DEFINE_THREAD(inm_free);
2229b1d850bSMark Johnston static struct in_multi_head inm_free_list = SLIST_HEAD_INITIALIZER();
2239b1d850bSMark Johnston static void inm_release_task(void *arg __unused, int pending __unused);
224b453d3d2SHans Petter Selasky static struct task inm_free_task = TASK_INITIALIZER(0, inm_release_task, NULL);
225f3e1324bSStephen Hurd 
226f3e1324bSStephen Hurd void
inm_release_wait(void * arg __unused)2273689652cSHans Petter Selasky inm_release_wait(void *arg __unused)
2283689652cSHans Petter Selasky {
2293689652cSHans Petter Selasky 
2303689652cSHans Petter Selasky 	/*
2313689652cSHans Petter Selasky 	 * Make sure all pending multicast addresses are freed before
2323689652cSHans Petter Selasky 	 * the VNET or network device is destroyed:
2333689652cSHans Petter Selasky 	 */
2343689652cSHans Petter Selasky 	taskqueue_drain(taskqueue_inm_free, &inm_free_task);
2353689652cSHans Petter Selasky }
2363689652cSHans Petter Selasky #ifdef VIMAGE
237f9461246SBjoern A. Zeeb /* XXX-BZ FIXME, see D24914. */
2383689652cSHans Petter Selasky VNET_SYSUNINIT(inm_release_wait, SI_SUB_PROTO_DOMAIN, SI_ORDER_FIRST, inm_release_wait, NULL);
2393689652cSHans Petter Selasky #endif
2403689652cSHans Petter Selasky 
2413689652cSHans Petter Selasky void
inm_release_list_deferred(struct in_multi_head * inmh)242f3e1324bSStephen Hurd inm_release_list_deferred(struct in_multi_head *inmh)
243f3e1324bSStephen Hurd {
244f3e1324bSStephen Hurd 
245f3e1324bSStephen Hurd 	if (SLIST_EMPTY(inmh))
246f3e1324bSStephen Hurd 		return;
247f3e1324bSStephen Hurd 	mtx_lock(&in_multi_free_mtx);
248f3e1324bSStephen Hurd 	SLIST_CONCAT(&inm_free_list, inmh, in_multi, inm_nrele);
249f3e1324bSStephen Hurd 	mtx_unlock(&in_multi_free_mtx);
2503689652cSHans Petter Selasky 	taskqueue_enqueue(taskqueue_inm_free, &inm_free_task);
251f3e1324bSStephen Hurd }
252f3e1324bSStephen Hurd 
253f3e1324bSStephen Hurd void
inm_disconnect(struct in_multi * inm)254b6f6f880SMatt Macy inm_disconnect(struct in_multi *inm)
255b6f6f880SMatt Macy {
256b6f6f880SMatt Macy 	struct ifnet *ifp;
257b6f6f880SMatt Macy 	struct ifmultiaddr *ifma, *ll_ifma;
258b6f6f880SMatt Macy 
259b6f6f880SMatt Macy 	ifp = inm->inm_ifp;
260b6f6f880SMatt Macy 	IF_ADDR_WLOCK_ASSERT(ifp);
261b6f6f880SMatt Macy 	ifma = inm->inm_ifma;
262b6f6f880SMatt Macy 
263b6f6f880SMatt Macy 	if_ref(ifp);
264f9be0386SMatt Macy 	if (ifma->ifma_flags & IFMA_F_ENQUEUED) {
265d7c5a620SMatt Macy 		CK_STAILQ_REMOVE(&ifp->if_multiaddrs, ifma, ifmultiaddr, ifma_link);
266f9be0386SMatt Macy 		ifma->ifma_flags &= ~IFMA_F_ENQUEUED;
267f9be0386SMatt Macy 	}
268b6f6f880SMatt Macy 	MCDPRINTF("removed ifma: %p from %s\n", ifma, ifp->if_xname);
269b6f6f880SMatt Macy 	if ((ll_ifma = ifma->ifma_llifma) != NULL) {
270b6f6f880SMatt Macy 		MPASS(ifma != ll_ifma);
271b6f6f880SMatt Macy 		ifma->ifma_llifma = NULL;
272b6f6f880SMatt Macy 		MPASS(ll_ifma->ifma_llifma == NULL);
273b6f6f880SMatt Macy 		MPASS(ll_ifma->ifma_ifp == ifp);
274b6f6f880SMatt Macy 		if (--ll_ifma->ifma_refcount == 0) {
275f9be0386SMatt Macy 			if (ll_ifma->ifma_flags & IFMA_F_ENQUEUED) {
276d7c5a620SMatt Macy 				CK_STAILQ_REMOVE(&ifp->if_multiaddrs, ll_ifma, ifmultiaddr, ifma_link);
277d3878608SMatt Macy 				ll_ifma->ifma_flags &= ~IFMA_F_ENQUEUED;
278f9be0386SMatt Macy 			}
279b6f6f880SMatt Macy 			MCDPRINTF("removed ll_ifma: %p from %s\n", ll_ifma, ifp->if_xname);
280b6f6f880SMatt Macy 			if_freemulti(ll_ifma);
281b6f6f880SMatt Macy 		}
282b6f6f880SMatt Macy 	}
283b6f6f880SMatt Macy }
284b6f6f880SMatt Macy 
285b6f6f880SMatt Macy void
inm_release_deferred(struct in_multi * inm)286f3e1324bSStephen Hurd inm_release_deferred(struct in_multi *inm)
287f3e1324bSStephen Hurd {
288f3e1324bSStephen Hurd 	struct in_multi_head tmp;
289f3e1324bSStephen Hurd 
290f3e1324bSStephen Hurd 	IN_MULTI_LIST_LOCK_ASSERT();
291f3e1324bSStephen Hurd 	MPASS(inm->inm_refcount > 0);
292f3e1324bSStephen Hurd 	if (--inm->inm_refcount == 0) {
293f3e1324bSStephen Hurd 		SLIST_INIT(&tmp);
294b6f6f880SMatt Macy 		inm_disconnect(inm);
295f3e1324bSStephen Hurd 		inm->inm_ifma->ifma_protospec = NULL;
296f3e1324bSStephen Hurd 		SLIST_INSERT_HEAD(&tmp, inm, inm_nrele);
297f3e1324bSStephen Hurd 		inm_release_list_deferred(&tmp);
298f3e1324bSStephen Hurd 	}
299f3e1324bSStephen Hurd }
300f3e1324bSStephen Hurd 
301f3e1324bSStephen Hurd static void
inm_release_task(void * arg __unused,int pending __unused)3029b1d850bSMark Johnston inm_release_task(void *arg __unused, int pending __unused)
303f3e1324bSStephen Hurd {
304f3e1324bSStephen Hurd 	struct in_multi_head inm_free_tmp;
305f3e1324bSStephen Hurd 	struct in_multi *inm, *tinm;
306f3e1324bSStephen Hurd 
307f3e1324bSStephen Hurd 	SLIST_INIT(&inm_free_tmp);
308f3e1324bSStephen Hurd 	mtx_lock(&in_multi_free_mtx);
309f3e1324bSStephen Hurd 	SLIST_CONCAT(&inm_free_tmp, &inm_free_list, in_multi, inm_nrele);
310f3e1324bSStephen Hurd 	mtx_unlock(&in_multi_free_mtx);
311f3e1324bSStephen Hurd 	IN_MULTI_LOCK();
312f3e1324bSStephen Hurd 	SLIST_FOREACH_SAFE(inm, &inm_free_tmp, inm_nrele, tinm) {
313f3e1324bSStephen Hurd 		SLIST_REMOVE_HEAD(&inm_free_tmp, inm_nrele);
314f3e1324bSStephen Hurd 		MPASS(inm);
315f3e1324bSStephen Hurd 		inm_release(inm);
316f3e1324bSStephen Hurd 	}
317f3e1324bSStephen Hurd 	IN_MULTI_UNLOCK();
318f3e1324bSStephen Hurd }
319f3e1324bSStephen Hurd 
320d10910e6SBruce M Simpson /*
321d10910e6SBruce M Simpson  * Initialize an in_mfilter structure to a known state at t0, t1
322d10910e6SBruce M Simpson  * with an empty source filter list.
323d10910e6SBruce M Simpson  */
324d10910e6SBruce M Simpson static __inline void
imf_init(struct in_mfilter * imf,const int st0,const int st1)325d10910e6SBruce M Simpson imf_init(struct in_mfilter *imf, const int st0, const int st1)
326d10910e6SBruce M Simpson {
327d10910e6SBruce M Simpson 	memset(imf, 0, sizeof(struct in_mfilter));
328d10910e6SBruce M Simpson 	RB_INIT(&imf->imf_sources);
329d10910e6SBruce M Simpson 	imf->imf_st[0] = st0;
330d10910e6SBruce M Simpson 	imf->imf_st[1] = st1;
331d10910e6SBruce M Simpson }
332d10910e6SBruce M Simpson 
33359854ecfSHans Petter Selasky struct in_mfilter *
ip_mfilter_alloc(const int mflags,const int st0,const int st1)33459854ecfSHans Petter Selasky ip_mfilter_alloc(const int mflags, const int st0, const int st1)
33559854ecfSHans Petter Selasky {
33659854ecfSHans Petter Selasky 	struct in_mfilter *imf;
33759854ecfSHans Petter Selasky 
33859854ecfSHans Petter Selasky 	imf = malloc(sizeof(*imf), M_INMFILTER, mflags);
33959854ecfSHans Petter Selasky 	if (imf != NULL)
34059854ecfSHans Petter Selasky 		imf_init(imf, st0, st1);
34159854ecfSHans Petter Selasky 
34259854ecfSHans Petter Selasky 	return (imf);
34359854ecfSHans Petter Selasky }
34459854ecfSHans Petter Selasky 
34559854ecfSHans Petter Selasky void
ip_mfilter_free(struct in_mfilter * imf)34659854ecfSHans Petter Selasky ip_mfilter_free(struct in_mfilter *imf)
34759854ecfSHans Petter Selasky {
34859854ecfSHans Petter Selasky 
34959854ecfSHans Petter Selasky 	imf_purge(imf);
35059854ecfSHans Petter Selasky 	free(imf, M_INMFILTER);
35159854ecfSHans Petter Selasky }
35259854ecfSHans Petter Selasky 
35371498f30SBruce M Simpson /*
3548d7cf9b5SGleb Smirnoff  * Function for looking up an in_multi record for an IPv4 multicast address
3558d7cf9b5SGleb Smirnoff  * on a given interface. ifp must be valid. If no record found, return NULL.
356f3e1324bSStephen Hurd  * The IN_MULTI_LIST_LOCK and IF_ADDR_LOCK on ifp must be held.
3578d7cf9b5SGleb Smirnoff  */
3588d7cf9b5SGleb Smirnoff struct in_multi *
inm_lookup_locked(struct ifnet * ifp,const struct in_addr ina)3598d7cf9b5SGleb Smirnoff inm_lookup_locked(struct ifnet *ifp, const struct in_addr ina)
3608d7cf9b5SGleb Smirnoff {
3618d7cf9b5SGleb Smirnoff 	struct ifmultiaddr *ifma;
3628d7cf9b5SGleb Smirnoff 	struct in_multi *inm;
3638d7cf9b5SGleb Smirnoff 
364f3e1324bSStephen Hurd 	IN_MULTI_LIST_LOCK_ASSERT();
3658d7cf9b5SGleb Smirnoff 	IF_ADDR_LOCK_ASSERT(ifp);
3668d7cf9b5SGleb Smirnoff 
367d7c5a620SMatt Macy 	CK_STAILQ_FOREACH(ifma, &((ifp)->if_multiaddrs), ifma_link) {
3681e9482f4SAlexander Motin 		inm = inm_ifmultiaddr_get_inm(ifma);
3691e9482f4SAlexander Motin 		if (inm == NULL)
370f2cf90e2SStephen Hurd 			continue;
3718d7cf9b5SGleb Smirnoff 		if (inm->inm_addr.s_addr == ina.s_addr)
3728d7cf9b5SGleb Smirnoff 			return (inm);
3738d7cf9b5SGleb Smirnoff 	}
3741e9482f4SAlexander Motin 	return (NULL);
3751e9482f4SAlexander Motin }
3768d7cf9b5SGleb Smirnoff 
3778d7cf9b5SGleb Smirnoff /*
3788d7cf9b5SGleb Smirnoff  * Wrapper for inm_lookup_locked().
3798d7cf9b5SGleb Smirnoff  * The IF_ADDR_LOCK will be taken on ifp and released on return.
3808d7cf9b5SGleb Smirnoff  */
3818d7cf9b5SGleb Smirnoff struct in_multi *
inm_lookup(struct ifnet * ifp,const struct in_addr ina)3828d7cf9b5SGleb Smirnoff inm_lookup(struct ifnet *ifp, const struct in_addr ina)
3838d7cf9b5SGleb Smirnoff {
3840732ac0eSGleb Smirnoff 	struct epoch_tracker et;
3858d7cf9b5SGleb Smirnoff 	struct in_multi *inm;
3868d7cf9b5SGleb Smirnoff 
387f3e1324bSStephen Hurd 	IN_MULTI_LIST_LOCK_ASSERT();
3880732ac0eSGleb Smirnoff 	NET_EPOCH_ENTER(et);
389b8a6e03fSGleb Smirnoff 
3908d7cf9b5SGleb Smirnoff 	inm = inm_lookup_locked(ifp, ina);
3910732ac0eSGleb Smirnoff 	NET_EPOCH_EXIT(et);
3928d7cf9b5SGleb Smirnoff 
3938d7cf9b5SGleb Smirnoff 	return (inm);
3948d7cf9b5SGleb Smirnoff }
3958d7cf9b5SGleb Smirnoff 
3968d7cf9b5SGleb Smirnoff /*
39771498f30SBruce M Simpson  * Find an IPv4 multicast group entry for this ip_moptions instance
39871498f30SBruce M Simpson  * which matches the specified group, and optionally an interface.
39971498f30SBruce M Simpson  * Return its index into the array, or -1 if not found.
40071498f30SBruce M Simpson  */
40159854ecfSHans Petter Selasky static struct in_mfilter *
imo_match_group(const struct ip_moptions * imo,const struct ifnet * ifp,const struct sockaddr * group)402d10910e6SBruce M Simpson imo_match_group(const struct ip_moptions *imo, const struct ifnet *ifp,
403d10910e6SBruce M Simpson     const struct sockaddr *group)
40471498f30SBruce M Simpson {
405d10910e6SBruce M Simpson 	const struct sockaddr_in *gsin;
40659854ecfSHans Petter Selasky 	struct in_mfilter *imf;
40759854ecfSHans Petter Selasky 	struct in_multi	*inm;
40871498f30SBruce M Simpson 
409d10910e6SBruce M Simpson 	gsin = (const struct sockaddr_in *)group;
41071498f30SBruce M Simpson 
41159854ecfSHans Petter Selasky 	IP_MFILTER_FOREACH(imf, &imo->imo_head) {
41259854ecfSHans Petter Selasky 		inm = imf->imf_inm;
41359854ecfSHans Petter Selasky 		if (inm == NULL)
41471498f30SBruce M Simpson 			continue;
41559854ecfSHans Petter Selasky 		if ((ifp == NULL || (inm->inm_ifp == ifp)) &&
41659854ecfSHans Petter Selasky 		    in_hosteq(inm->inm_addr, gsin->sin_addr)) {
41771498f30SBruce M Simpson 			break;
41871498f30SBruce M Simpson 		}
41971498f30SBruce M Simpson 	}
42059854ecfSHans Petter Selasky 	return (imf);
42171498f30SBruce M Simpson }
42271498f30SBruce M Simpson 
42371498f30SBruce M Simpson /*
424d10910e6SBruce M Simpson  * Find an IPv4 multicast source entry for this imo which matches
42571498f30SBruce M Simpson  * the given group index for this socket, and source address.
426d10910e6SBruce M Simpson  *
427d10910e6SBruce M Simpson  * NOTE: This does not check if the entry is in-mode, merely if
428d10910e6SBruce M Simpson  * it exists, which may not be the desired behaviour.
42971498f30SBruce M Simpson  */
430d10910e6SBruce M Simpson static struct in_msource *
imo_match_source(struct in_mfilter * imf,const struct sockaddr * src)43159854ecfSHans Petter Selasky imo_match_source(struct in_mfilter *imf, const struct sockaddr *src)
43271498f30SBruce M Simpson {
433d10910e6SBruce M Simpson 	struct ip_msource	 find;
434d10910e6SBruce M Simpson 	struct ip_msource	*ims;
435d10910e6SBruce M Simpson 	const sockunion_t	*psa;
43671498f30SBruce M Simpson 
43771498f30SBruce M Simpson 	KASSERT(src->sa_family == AF_INET, ("%s: !AF_INET", __func__));
43871498f30SBruce M Simpson 
439d10910e6SBruce M Simpson 	/* Source trees are keyed in host byte order. */
440d10910e6SBruce M Simpson 	psa = (const sockunion_t *)src;
441d10910e6SBruce M Simpson 	find.ims_haddr = ntohl(psa->sin.sin_addr.s_addr);
442d10910e6SBruce M Simpson 	ims = RB_FIND(ip_msource_tree, &imf->imf_sources, &find);
443d10910e6SBruce M Simpson 
444d10910e6SBruce M Simpson 	return ((struct in_msource *)ims);
44571498f30SBruce M Simpson }
44671498f30SBruce M Simpson 
44771498f30SBruce M Simpson /*
448d10910e6SBruce M Simpson  * Perform filtering for multicast datagrams on a socket by group and source.
449d10910e6SBruce M Simpson  *
450d10910e6SBruce M Simpson  * Returns 0 if a datagram should be allowed through, or various error codes
451d10910e6SBruce M Simpson  * if the socket was not a member of the group, or the source was muted, etc.
45271498f30SBruce M Simpson  */
453d10910e6SBruce M Simpson int
imo_multi_filter(const struct ip_moptions * imo,const struct ifnet * ifp,const struct sockaddr * group,const struct sockaddr * src)454d10910e6SBruce M Simpson imo_multi_filter(const struct ip_moptions *imo, const struct ifnet *ifp,
455d10910e6SBruce M Simpson     const struct sockaddr *group, const struct sockaddr *src)
456d10910e6SBruce M Simpson {
45759854ecfSHans Petter Selasky 	struct in_mfilter *imf;
458d10910e6SBruce M Simpson 	struct in_msource *ims;
459d10910e6SBruce M Simpson 	int mode;
460d10910e6SBruce M Simpson 
461d10910e6SBruce M Simpson 	KASSERT(ifp != NULL, ("%s: null ifp", __func__));
462d10910e6SBruce M Simpson 
46359854ecfSHans Petter Selasky 	imf = imo_match_group(imo, ifp, group);
46459854ecfSHans Petter Selasky 	if (imf == NULL)
465d10910e6SBruce M Simpson 		return (MCAST_NOTGMEMBER);
466d10910e6SBruce M Simpson 
467d10910e6SBruce M Simpson 	/*
468d10910e6SBruce M Simpson 	 * Check if the source was included in an (S,G) join.
469d10910e6SBruce M Simpson 	 * Allow reception on exclusive memberships by default,
470d10910e6SBruce M Simpson 	 * reject reception on inclusive memberships by default.
471d10910e6SBruce M Simpson 	 * Exclude source only if an in-mode exclude filter exists.
472d10910e6SBruce M Simpson 	 * Include source only if an in-mode include filter exists.
473d10910e6SBruce M Simpson 	 * NOTE: We are comparing group state here at IGMP t1 (now)
474d10910e6SBruce M Simpson 	 * with socket-layer t0 (since last downcall).
475d10910e6SBruce M Simpson 	 */
47659854ecfSHans Petter Selasky 	mode = imf->imf_st[1];
47759854ecfSHans Petter Selasky 	ims = imo_match_source(imf, src);
478d10910e6SBruce M Simpson 
479d10910e6SBruce M Simpson 	if ((ims == NULL && mode == MCAST_INCLUDE) ||
480290f7f4aSFidaullah Noonari 	    (ims != NULL && ims->imsl_st[0] == MCAST_EXCLUDE))
481d10910e6SBruce M Simpson 		return (MCAST_NOTSMEMBER);
482d10910e6SBruce M Simpson 
483d10910e6SBruce M Simpson 	return (MCAST_PASS);
484d10910e6SBruce M Simpson }
485d10910e6SBruce M Simpson 
486d10910e6SBruce M Simpson /*
487d10910e6SBruce M Simpson  * Find and return a reference to an in_multi record for (ifp, group),
488d10910e6SBruce M Simpson  * and bump its reference count.
489d10910e6SBruce M Simpson  * If one does not exist, try to allocate it, and update link-layer multicast
490d10910e6SBruce M Simpson  * filters on ifp to listen for group.
491d10910e6SBruce M Simpson  * Assumes the IN_MULTI lock is held across the call.
492d10910e6SBruce M Simpson  * Return 0 if successful, otherwise return an appropriate error code.
493d10910e6SBruce M Simpson  */
494d10910e6SBruce M Simpson static int
in_getmulti(struct ifnet * ifp,const struct in_addr * group,struct in_multi ** pinm)495d10910e6SBruce M Simpson in_getmulti(struct ifnet *ifp, const struct in_addr *group,
496d10910e6SBruce M Simpson     struct in_multi **pinm)
49771498f30SBruce M Simpson {
498d10910e6SBruce M Simpson 	struct sockaddr_in	 gsin;
499d10910e6SBruce M Simpson 	struct ifmultiaddr	*ifma;
500d10910e6SBruce M Simpson 	struct in_ifinfo	*ii;
50171498f30SBruce M Simpson 	struct in_multi		*inm;
502d10910e6SBruce M Simpson 	int error;
50371498f30SBruce M Simpson 
504d10910e6SBruce M Simpson 	IN_MULTI_LOCK_ASSERT();
50571498f30SBruce M Simpson 
506d10910e6SBruce M Simpson 	ii = (struct in_ifinfo *)ifp->if_afdata[AF_INET];
507f3e1324bSStephen Hurd 	IN_MULTI_LIST_LOCK();
508d10910e6SBruce M Simpson 	inm = inm_lookup(ifp, *group);
50971498f30SBruce M Simpson 	if (inm != NULL) {
51071498f30SBruce M Simpson 		/*
51171498f30SBruce M Simpson 		 * If we already joined this group, just bump the
51271498f30SBruce M Simpson 		 * refcount and return it.
51371498f30SBruce M Simpson 		 */
51471498f30SBruce M Simpson 		KASSERT(inm->inm_refcount >= 1,
51571498f30SBruce M Simpson 		    ("%s: bad refcount %d", __func__, inm->inm_refcount));
516f3e1324bSStephen Hurd 		inm_acquire_locked(inm);
517d10910e6SBruce M Simpson 		*pinm = inm;
518d10910e6SBruce M Simpson 	}
519f3e1324bSStephen Hurd 	IN_MULTI_LIST_UNLOCK();
520f3e1324bSStephen Hurd 	if (inm != NULL)
521f3e1324bSStephen Hurd 		return (0);
52271498f30SBruce M Simpson 
523d10910e6SBruce M Simpson 	memset(&gsin, 0, sizeof(gsin));
524d10910e6SBruce M Simpson 	gsin.sin_family = AF_INET;
525d10910e6SBruce M Simpson 	gsin.sin_len = sizeof(struct sockaddr_in);
526d10910e6SBruce M Simpson 	gsin.sin_addr = *group;
52771498f30SBruce M Simpson 
52871498f30SBruce M Simpson 	/*
52971498f30SBruce M Simpson 	 * Check if a link-layer group is already associated
53071498f30SBruce M Simpson 	 * with this network-layer group on the given ifnet.
531d10910e6SBruce M Simpson 	 */
532d10910e6SBruce M Simpson 	error = if_addmulti(ifp, (struct sockaddr *)&gsin, &ifma);
533d10910e6SBruce M Simpson 	if (error != 0)
534d10910e6SBruce M Simpson 		return (error);
535d10910e6SBruce M Simpson 
53656663a40SBruce M Simpson 	/* XXX ifma_protospec must be covered by IF_ADDR_LOCK */
537f3e1324bSStephen Hurd 	IN_MULTI_LIST_LOCK();
538137f91e8SJohn Baldwin 	IF_ADDR_WLOCK(ifp);
53956663a40SBruce M Simpson 
540d10910e6SBruce M Simpson 	/*
541d10910e6SBruce M Simpson 	 * If something other than netinet is occupying the link-layer
542d10910e6SBruce M Simpson 	 * group, print a meaningful error message and back out of
543d10910e6SBruce M Simpson 	 * the allocation.
544d10910e6SBruce M Simpson 	 * Otherwise, bump the refcount on the existing network-layer
54571498f30SBruce M Simpson 	 * group association and return it.
54671498f30SBruce M Simpson 	 */
54771498f30SBruce M Simpson 	if (ifma->ifma_protospec != NULL) {
54871498f30SBruce M Simpson 		inm = (struct in_multi *)ifma->ifma_protospec;
54971498f30SBruce M Simpson #ifdef INVARIANTS
550d10910e6SBruce M Simpson 		KASSERT(ifma->ifma_addr != NULL, ("%s: no ifma_addr",
551d10910e6SBruce M Simpson 		    __func__));
552d10910e6SBruce M Simpson 		KASSERT(ifma->ifma_addr->sa_family == AF_INET,
553d10910e6SBruce M Simpson 		    ("%s: ifma not AF_INET", __func__));
554d10910e6SBruce M Simpson 		KASSERT(inm != NULL, ("%s: no ifma_protospec", __func__));
55571498f30SBruce M Simpson 		if (inm->inm_ifma != ifma || inm->inm_ifp != ifp ||
5568144690aSEric van Gyzen 		    !in_hosteq(inm->inm_addr, *group)) {
5578144690aSEric van Gyzen 			char addrbuf[INET_ADDRSTRLEN];
5588144690aSEric van Gyzen 
559d10910e6SBruce M Simpson 			panic("%s: ifma %p is inconsistent with %p (%s)",
5608144690aSEric van Gyzen 			    __func__, ifma, inm, inet_ntoa_r(*group, addrbuf));
5618144690aSEric van Gyzen 		}
56271498f30SBruce M Simpson #endif
563f3e1324bSStephen Hurd 		inm_acquire_locked(inm);
564d10910e6SBruce M Simpson 		*pinm = inm;
565f3e1324bSStephen Hurd 		goto out_locked;
56671498f30SBruce M Simpson 	}
56771498f30SBruce M Simpson 
568137f91e8SJohn Baldwin 	IF_ADDR_WLOCK_ASSERT(ifp);
56956663a40SBruce M Simpson 
57071498f30SBruce M Simpson 	/*
571d10910e6SBruce M Simpson 	 * A new in_multi record is needed; allocate and initialize it.
572d10910e6SBruce M Simpson 	 * We DO NOT perform an IGMP join as the in_ layer may need to
573d10910e6SBruce M Simpson 	 * push an initial source list down to IGMP to support SSM.
574d10910e6SBruce M Simpson 	 *
575d10910e6SBruce M Simpson 	 * The initial source filter state is INCLUDE, {} as per the RFC.
57671498f30SBruce M Simpson 	 */
577d10910e6SBruce M Simpson 	inm = malloc(sizeof(*inm), M_IPMADDR, M_NOWAIT | M_ZERO);
578d10910e6SBruce M Simpson 	if (inm == NULL) {
579137f91e8SJohn Baldwin 		IF_ADDR_WUNLOCK(ifp);
580f3e1324bSStephen Hurd 		IN_MULTI_LIST_UNLOCK();
5813fbd30d4SConrad Meyer 		if_delmulti_ifma(ifma);
582d10910e6SBruce M Simpson 		return (ENOMEM);
58371498f30SBruce M Simpson 	}
584d10910e6SBruce M Simpson 	inm->inm_addr = *group;
585d10910e6SBruce M Simpson 	inm->inm_ifp = ifp;
586d10910e6SBruce M Simpson 	inm->inm_igi = ii->ii_igmp;
587d10910e6SBruce M Simpson 	inm->inm_ifma = ifma;
588d10910e6SBruce M Simpson 	inm->inm_refcount = 1;
589d10910e6SBruce M Simpson 	inm->inm_state = IGMP_NOT_MEMBER;
590058e08beSGleb Smirnoff 	mbufq_init(&inm->inm_scq, IGMP_MAX_STATE_CHANGES);
591d10910e6SBruce M Simpson 	inm->inm_st[0].iss_fmode = MCAST_UNDEFINED;
592d10910e6SBruce M Simpson 	inm->inm_st[1].iss_fmode = MCAST_UNDEFINED;
593d10910e6SBruce M Simpson 	RB_INIT(&inm->inm_srcs);
59471498f30SBruce M Simpson 
595d10910e6SBruce M Simpson 	ifma->ifma_protospec = inm;
59671498f30SBruce M Simpson 
597d10910e6SBruce M Simpson 	*pinm = inm;
598f3e1324bSStephen Hurd  out_locked:
599137f91e8SJohn Baldwin 	IF_ADDR_WUNLOCK(ifp);
600f3e1324bSStephen Hurd 	IN_MULTI_LIST_UNLOCK();
601d10910e6SBruce M Simpson 	return (0);
60271498f30SBruce M Simpson }
60371498f30SBruce M Simpson 
60471498f30SBruce M Simpson /*
605d10910e6SBruce M Simpson  * Drop a reference to an in_multi record.
60671498f30SBruce M Simpson  *
607d10910e6SBruce M Simpson  * If the refcount drops to 0, free the in_multi record and
608d10910e6SBruce M Simpson  * delete the underlying link-layer membership.
60971498f30SBruce M Simpson  */
610f3e1324bSStephen Hurd static void
inm_release(struct in_multi * inm)611f3e1324bSStephen Hurd inm_release(struct in_multi *inm)
61271498f30SBruce M Simpson {
61371498f30SBruce M Simpson 	struct ifmultiaddr *ifma;
614f3e1324bSStephen Hurd 	struct ifnet *ifp;
615d10910e6SBruce M Simpson 
616d10910e6SBruce M Simpson 	CTR2(KTR_IGMPV3, "%s: refcount is %d", __func__, inm->inm_refcount);
617f3e1324bSStephen Hurd 	MPASS(inm->inm_refcount == 0);
618d10910e6SBruce M Simpson 	CTR2(KTR_IGMPV3, "%s: freeing inm %p", __func__, inm);
61971498f30SBruce M Simpson 
62071498f30SBruce M Simpson 	ifma = inm->inm_ifma;
621f3e1324bSStephen Hurd 	ifp = inm->inm_ifp;
622d10910e6SBruce M Simpson 
62356663a40SBruce M Simpson 	/* XXX this access is not covered by IF_ADDR_LOCK */
624d10910e6SBruce M Simpson 	CTR2(KTR_IGMPV3, "%s: purging ifma %p", __func__, ifma);
6253b9b6b17SEd Maste 	if (ifp != NULL) {
62615f8acc5SEd Maste 		CURVNET_SET(ifp->if_vnet);
627d10910e6SBruce M Simpson 		inm_purge(inm);
62871498f30SBruce M Simpson 		free(inm, M_IPMADDR);
629b6f6f880SMatt Macy 		if_delmulti_ifma_flags(ifma, 1);
63015f8acc5SEd Maste 		CURVNET_RESTORE();
631b6f6f880SMatt Macy 		if_rele(ifp);
6323b9b6b17SEd Maste 	} else {
6333b9b6b17SEd Maste 		inm_purge(inm);
6343b9b6b17SEd Maste 		free(inm, M_IPMADDR);
6353b9b6b17SEd Maste 		if_delmulti_ifma_flags(ifma, 1);
636b6f6f880SMatt Macy 	}
63771498f30SBruce M Simpson }
638d10910e6SBruce M Simpson 
639d10910e6SBruce M Simpson /*
640d10910e6SBruce M Simpson  * Clear recorded source entries for a group.
641d10910e6SBruce M Simpson  * Used by the IGMP code. Caller must hold the IN_MULTI lock.
642d10910e6SBruce M Simpson  * FIXME: Should reap.
643d10910e6SBruce M Simpson  */
644d10910e6SBruce M Simpson void
inm_clear_recorded(struct in_multi * inm)645d10910e6SBruce M Simpson inm_clear_recorded(struct in_multi *inm)
646d10910e6SBruce M Simpson {
647d10910e6SBruce M Simpson 	struct ip_msource	*ims;
648d10910e6SBruce M Simpson 
649f3e1324bSStephen Hurd 	IN_MULTI_LIST_LOCK_ASSERT();
650d10910e6SBruce M Simpson 
651d10910e6SBruce M Simpson 	RB_FOREACH(ims, ip_msource_tree, &inm->inm_srcs) {
652d10910e6SBruce M Simpson 		if (ims->ims_stp) {
653d10910e6SBruce M Simpson 			ims->ims_stp = 0;
654d10910e6SBruce M Simpson 			--inm->inm_st[1].iss_rec;
655d10910e6SBruce M Simpson 		}
656d10910e6SBruce M Simpson 	}
657d10910e6SBruce M Simpson 	KASSERT(inm->inm_st[1].iss_rec == 0,
658d10910e6SBruce M Simpson 	    ("%s: iss_rec %d not 0", __func__, inm->inm_st[1].iss_rec));
65971498f30SBruce M Simpson }
66071498f30SBruce M Simpson 
66171498f30SBruce M Simpson /*
662d10910e6SBruce M Simpson  * Record a source as pending for a Source-Group IGMPv3 query.
663d10910e6SBruce M Simpson  * This lives here as it modifies the shared tree.
664d10910e6SBruce M Simpson  *
665d10910e6SBruce M Simpson  * inm is the group descriptor.
666d10910e6SBruce M Simpson  * naddr is the address of the source to record in network-byte order.
667d10910e6SBruce M Simpson  *
668d10910e6SBruce M Simpson  * If the net.inet.igmp.sgalloc sysctl is non-zero, we will
669d10910e6SBruce M Simpson  * lazy-allocate a source node in response to an SG query.
670d10910e6SBruce M Simpson  * Otherwise, no allocation is performed. This saves some memory
671d10910e6SBruce M Simpson  * with the trade-off that the source will not be reported to the
672d10910e6SBruce M Simpson  * router if joined in the window between the query response and
673d10910e6SBruce M Simpson  * the group actually being joined on the local host.
674d10910e6SBruce M Simpson  *
675d10910e6SBruce M Simpson  * VIMAGE: XXX: Currently the igmp_sgalloc feature has been removed.
676d10910e6SBruce M Simpson  * This turns off the allocation of a recorded source entry if
677d10910e6SBruce M Simpson  * the group has not been joined.
678d10910e6SBruce M Simpson  *
679d10910e6SBruce M Simpson  * Return 0 if the source didn't exist or was already marked as recorded.
680d10910e6SBruce M Simpson  * Return 1 if the source was marked as recorded by this function.
681a4641f4eSPedro F. Giffuni  * Return <0 if any error occurred (negated errno code).
682d10910e6SBruce M Simpson  */
683d10910e6SBruce M Simpson int
inm_record_source(struct in_multi * inm,const in_addr_t naddr)684d10910e6SBruce M Simpson inm_record_source(struct in_multi *inm, const in_addr_t naddr)
685d10910e6SBruce M Simpson {
686d10910e6SBruce M Simpson 	struct ip_msource	 find;
687d10910e6SBruce M Simpson 	struct ip_msource	*ims, *nims;
688d10910e6SBruce M Simpson 
689f3e1324bSStephen Hurd 	IN_MULTI_LIST_LOCK_ASSERT();
690d10910e6SBruce M Simpson 
691d10910e6SBruce M Simpson 	find.ims_haddr = ntohl(naddr);
692d10910e6SBruce M Simpson 	ims = RB_FIND(ip_msource_tree, &inm->inm_srcs, &find);
693d10910e6SBruce M Simpson 	if (ims && ims->ims_stp)
694d10910e6SBruce M Simpson 		return (0);
695d10910e6SBruce M Simpson 	if (ims == NULL) {
696d10910e6SBruce M Simpson 		if (inm->inm_nsrc == in_mcast_maxgrpsrc)
697d10910e6SBruce M Simpson 			return (-ENOSPC);
698d10910e6SBruce M Simpson 		nims = malloc(sizeof(struct ip_msource), M_IPMSOURCE,
699d10910e6SBruce M Simpson 		    M_NOWAIT | M_ZERO);
700d10910e6SBruce M Simpson 		if (nims == NULL)
701d10910e6SBruce M Simpson 			return (-ENOMEM);
702d10910e6SBruce M Simpson 		nims->ims_haddr = find.ims_haddr;
703d10910e6SBruce M Simpson 		RB_INSERT(ip_msource_tree, &inm->inm_srcs, nims);
704d10910e6SBruce M Simpson 		++inm->inm_nsrc;
705d10910e6SBruce M Simpson 		ims = nims;
706d10910e6SBruce M Simpson 	}
707d10910e6SBruce M Simpson 
708d10910e6SBruce M Simpson 	/*
709d10910e6SBruce M Simpson 	 * Mark the source as recorded and update the recorded
710d10910e6SBruce M Simpson 	 * source count.
711d10910e6SBruce M Simpson 	 */
712d10910e6SBruce M Simpson 	++ims->ims_stp;
713d10910e6SBruce M Simpson 	++inm->inm_st[1].iss_rec;
714d10910e6SBruce M Simpson 
715d10910e6SBruce M Simpson 	return (1);
716d10910e6SBruce M Simpson }
717d10910e6SBruce M Simpson 
718d10910e6SBruce M Simpson /*
719d10910e6SBruce M Simpson  * Return a pointer to an in_msource owned by an in_mfilter,
720d10910e6SBruce M Simpson  * given its source address.
721d10910e6SBruce M Simpson  * Lazy-allocate if needed. If this is a new entry its filter state is
722d10910e6SBruce M Simpson  * undefined at t0.
723d10910e6SBruce M Simpson  *
724d10910e6SBruce M Simpson  * imf is the filter set being modified.
725d10910e6SBruce M Simpson  * haddr is the source address in *host* byte-order.
726d10910e6SBruce M Simpson  *
727d10910e6SBruce M Simpson  * SMPng: May be called with locks held; malloc must not block.
72871498f30SBruce M Simpson  */
72971498f30SBruce M Simpson static int
imf_get_source(struct in_mfilter * imf,const struct sockaddr_in * psin,struct in_msource ** plims)730d10910e6SBruce M Simpson imf_get_source(struct in_mfilter *imf, const struct sockaddr_in *psin,
731d10910e6SBruce M Simpson     struct in_msource **plims)
732d10910e6SBruce M Simpson {
733d10910e6SBruce M Simpson 	struct ip_msource	 find;
734d10910e6SBruce M Simpson 	struct ip_msource	*ims, *nims;
735d10910e6SBruce M Simpson 	struct in_msource	*lims;
736d10910e6SBruce M Simpson 	int			 error;
737d10910e6SBruce M Simpson 
738d10910e6SBruce M Simpson 	error = 0;
739d10910e6SBruce M Simpson 	ims = NULL;
740d10910e6SBruce M Simpson 	lims = NULL;
741d10910e6SBruce M Simpson 
742d10910e6SBruce M Simpson 	/* key is host byte order */
743d10910e6SBruce M Simpson 	find.ims_haddr = ntohl(psin->sin_addr.s_addr);
744d10910e6SBruce M Simpson 	ims = RB_FIND(ip_msource_tree, &imf->imf_sources, &find);
745d10910e6SBruce M Simpson 	lims = (struct in_msource *)ims;
746d10910e6SBruce M Simpson 	if (lims == NULL) {
747d10910e6SBruce M Simpson 		if (imf->imf_nsrc == in_mcast_maxsocksrc)
748d10910e6SBruce M Simpson 			return (ENOSPC);
749d10910e6SBruce M Simpson 		nims = malloc(sizeof(struct in_msource), M_INMFILTER,
750d10910e6SBruce M Simpson 		    M_NOWAIT | M_ZERO);
751d10910e6SBruce M Simpson 		if (nims == NULL)
752d10910e6SBruce M Simpson 			return (ENOMEM);
753d10910e6SBruce M Simpson 		lims = (struct in_msource *)nims;
754d10910e6SBruce M Simpson 		lims->ims_haddr = find.ims_haddr;
755d10910e6SBruce M Simpson 		lims->imsl_st[0] = MCAST_UNDEFINED;
756d10910e6SBruce M Simpson 		RB_INSERT(ip_msource_tree, &imf->imf_sources, nims);
757d10910e6SBruce M Simpson 		++imf->imf_nsrc;
758d10910e6SBruce M Simpson 	}
759d10910e6SBruce M Simpson 
760d10910e6SBruce M Simpson 	*plims = lims;
761d10910e6SBruce M Simpson 
762d10910e6SBruce M Simpson 	return (error);
763d10910e6SBruce M Simpson }
764d10910e6SBruce M Simpson 
765d10910e6SBruce M Simpson /*
766d10910e6SBruce M Simpson  * Graft a source entry into an existing socket-layer filter set,
767d10910e6SBruce M Simpson  * maintaining any required invariants and checking allocations.
768d10910e6SBruce M Simpson  *
769d10910e6SBruce M Simpson  * The source is marked as being in the new filter mode at t1.
770d10910e6SBruce M Simpson  *
771d10910e6SBruce M Simpson  * Return the pointer to the new node, otherwise return NULL.
772d10910e6SBruce M Simpson  */
773d10910e6SBruce M Simpson static struct in_msource *
imf_graft(struct in_mfilter * imf,const uint8_t st1,const struct sockaddr_in * psin)774d10910e6SBruce M Simpson imf_graft(struct in_mfilter *imf, const uint8_t st1,
775d10910e6SBruce M Simpson     const struct sockaddr_in *psin)
776d10910e6SBruce M Simpson {
777d10910e6SBruce M Simpson 	struct ip_msource	*nims;
778d10910e6SBruce M Simpson 	struct in_msource	*lims;
779d10910e6SBruce M Simpson 
780d10910e6SBruce M Simpson 	nims = malloc(sizeof(struct in_msource), M_INMFILTER,
781d10910e6SBruce M Simpson 	    M_NOWAIT | M_ZERO);
782d10910e6SBruce M Simpson 	if (nims == NULL)
783d10910e6SBruce M Simpson 		return (NULL);
784d10910e6SBruce M Simpson 	lims = (struct in_msource *)nims;
785d10910e6SBruce M Simpson 	lims->ims_haddr = ntohl(psin->sin_addr.s_addr);
786d10910e6SBruce M Simpson 	lims->imsl_st[0] = MCAST_UNDEFINED;
787d10910e6SBruce M Simpson 	lims->imsl_st[1] = st1;
788d10910e6SBruce M Simpson 	RB_INSERT(ip_msource_tree, &imf->imf_sources, nims);
789d10910e6SBruce M Simpson 	++imf->imf_nsrc;
790d10910e6SBruce M Simpson 
791d10910e6SBruce M Simpson 	return (lims);
792d10910e6SBruce M Simpson }
793d10910e6SBruce M Simpson 
794d10910e6SBruce M Simpson /*
795d10910e6SBruce M Simpson  * Prune a source entry from an existing socket-layer filter set,
796d10910e6SBruce M Simpson  * maintaining any required invariants and checking allocations.
797d10910e6SBruce M Simpson  *
798d10910e6SBruce M Simpson  * The source is marked as being left at t1, it is not freed.
799d10910e6SBruce M Simpson  *
800d10910e6SBruce M Simpson  * Return 0 if no error occurred, otherwise return an errno value.
801d10910e6SBruce M Simpson  */
802d10910e6SBruce M Simpson static int
imf_prune(struct in_mfilter * imf,const struct sockaddr_in * psin)803d10910e6SBruce M Simpson imf_prune(struct in_mfilter *imf, const struct sockaddr_in *psin)
804d10910e6SBruce M Simpson {
805d10910e6SBruce M Simpson 	struct ip_msource	 find;
806d10910e6SBruce M Simpson 	struct ip_msource	*ims;
807d10910e6SBruce M Simpson 	struct in_msource	*lims;
808d10910e6SBruce M Simpson 
809d10910e6SBruce M Simpson 	/* key is host byte order */
810d10910e6SBruce M Simpson 	find.ims_haddr = ntohl(psin->sin_addr.s_addr);
811d10910e6SBruce M Simpson 	ims = RB_FIND(ip_msource_tree, &imf->imf_sources, &find);
812d10910e6SBruce M Simpson 	if (ims == NULL)
813d10910e6SBruce M Simpson 		return (ENOENT);
814d10910e6SBruce M Simpson 	lims = (struct in_msource *)ims;
815d10910e6SBruce M Simpson 	lims->imsl_st[1] = MCAST_UNDEFINED;
816d10910e6SBruce M Simpson 	return (0);
817d10910e6SBruce M Simpson }
818d10910e6SBruce M Simpson 
819d10910e6SBruce M Simpson /*
820d10910e6SBruce M Simpson  * Revert socket-layer filter set deltas at t1 to t0 state.
821d10910e6SBruce M Simpson  */
822d10910e6SBruce M Simpson static void
imf_rollback(struct in_mfilter * imf)823d10910e6SBruce M Simpson imf_rollback(struct in_mfilter *imf)
824d10910e6SBruce M Simpson {
825d10910e6SBruce M Simpson 	struct ip_msource	*ims, *tims;
826d10910e6SBruce M Simpson 	struct in_msource	*lims;
827d10910e6SBruce M Simpson 
828d10910e6SBruce M Simpson 	RB_FOREACH_SAFE(ims, ip_msource_tree, &imf->imf_sources, tims) {
829d10910e6SBruce M Simpson 		lims = (struct in_msource *)ims;
830d10910e6SBruce M Simpson 		if (lims->imsl_st[0] == lims->imsl_st[1]) {
831d10910e6SBruce M Simpson 			/* no change at t1 */
832d10910e6SBruce M Simpson 			continue;
833d10910e6SBruce M Simpson 		} else if (lims->imsl_st[0] != MCAST_UNDEFINED) {
834d10910e6SBruce M Simpson 			/* revert change to existing source at t1 */
835d10910e6SBruce M Simpson 			lims->imsl_st[1] = lims->imsl_st[0];
836d10910e6SBruce M Simpson 		} else {
837d10910e6SBruce M Simpson 			/* revert source added t1 */
838d10910e6SBruce M Simpson 			CTR2(KTR_IGMPV3, "%s: free ims %p", __func__, ims);
839d10910e6SBruce M Simpson 			RB_REMOVE(ip_msource_tree, &imf->imf_sources, ims);
840d10910e6SBruce M Simpson 			free(ims, M_INMFILTER);
841d10910e6SBruce M Simpson 			imf->imf_nsrc--;
842d10910e6SBruce M Simpson 		}
843d10910e6SBruce M Simpson 	}
844d10910e6SBruce M Simpson 	imf->imf_st[1] = imf->imf_st[0];
845d10910e6SBruce M Simpson }
846d10910e6SBruce M Simpson 
847d10910e6SBruce M Simpson /*
848d10910e6SBruce M Simpson  * Mark socket-layer filter set as INCLUDE {} at t1.
849d10910e6SBruce M Simpson  */
850d10910e6SBruce M Simpson static void
imf_leave(struct in_mfilter * imf)851d10910e6SBruce M Simpson imf_leave(struct in_mfilter *imf)
852d10910e6SBruce M Simpson {
853d10910e6SBruce M Simpson 	struct ip_msource	*ims;
854d10910e6SBruce M Simpson 	struct in_msource	*lims;
855d10910e6SBruce M Simpson 
856d10910e6SBruce M Simpson 	RB_FOREACH(ims, ip_msource_tree, &imf->imf_sources) {
857d10910e6SBruce M Simpson 		lims = (struct in_msource *)ims;
858d10910e6SBruce M Simpson 		lims->imsl_st[1] = MCAST_UNDEFINED;
859d10910e6SBruce M Simpson 	}
860d10910e6SBruce M Simpson 	imf->imf_st[1] = MCAST_INCLUDE;
861d10910e6SBruce M Simpson }
862d10910e6SBruce M Simpson 
863d10910e6SBruce M Simpson /*
864d10910e6SBruce M Simpson  * Mark socket-layer filter set deltas as committed.
865d10910e6SBruce M Simpson  */
866d10910e6SBruce M Simpson static void
imf_commit(struct in_mfilter * imf)867d10910e6SBruce M Simpson imf_commit(struct in_mfilter *imf)
868d10910e6SBruce M Simpson {
869d10910e6SBruce M Simpson 	struct ip_msource	*ims;
870d10910e6SBruce M Simpson 	struct in_msource	*lims;
871d10910e6SBruce M Simpson 
872d10910e6SBruce M Simpson 	RB_FOREACH(ims, ip_msource_tree, &imf->imf_sources) {
873d10910e6SBruce M Simpson 		lims = (struct in_msource *)ims;
874d10910e6SBruce M Simpson 		lims->imsl_st[0] = lims->imsl_st[1];
875d10910e6SBruce M Simpson 	}
876d10910e6SBruce M Simpson 	imf->imf_st[0] = imf->imf_st[1];
877d10910e6SBruce M Simpson }
878d10910e6SBruce M Simpson 
879d10910e6SBruce M Simpson /*
880d10910e6SBruce M Simpson  * Reap unreferenced sources from socket-layer filter set.
881d10910e6SBruce M Simpson  */
882d10910e6SBruce M Simpson static void
imf_reap(struct in_mfilter * imf)883d10910e6SBruce M Simpson imf_reap(struct in_mfilter *imf)
884d10910e6SBruce M Simpson {
885d10910e6SBruce M Simpson 	struct ip_msource	*ims, *tims;
886d10910e6SBruce M Simpson 	struct in_msource	*lims;
887d10910e6SBruce M Simpson 
888d10910e6SBruce M Simpson 	RB_FOREACH_SAFE(ims, ip_msource_tree, &imf->imf_sources, tims) {
889d10910e6SBruce M Simpson 		lims = (struct in_msource *)ims;
890d10910e6SBruce M Simpson 		if ((lims->imsl_st[0] == MCAST_UNDEFINED) &&
891d10910e6SBruce M Simpson 		    (lims->imsl_st[1] == MCAST_UNDEFINED)) {
892d10910e6SBruce M Simpson 			CTR2(KTR_IGMPV3, "%s: free lims %p", __func__, ims);
893d10910e6SBruce M Simpson 			RB_REMOVE(ip_msource_tree, &imf->imf_sources, ims);
894d10910e6SBruce M Simpson 			free(ims, M_INMFILTER);
895d10910e6SBruce M Simpson 			imf->imf_nsrc--;
896d10910e6SBruce M Simpson 		}
897d10910e6SBruce M Simpson 	}
898d10910e6SBruce M Simpson }
899d10910e6SBruce M Simpson 
900d10910e6SBruce M Simpson /*
901d10910e6SBruce M Simpson  * Purge socket-layer filter set.
902d10910e6SBruce M Simpson  */
903d10910e6SBruce M Simpson static void
imf_purge(struct in_mfilter * imf)904d10910e6SBruce M Simpson imf_purge(struct in_mfilter *imf)
905d10910e6SBruce M Simpson {
906d10910e6SBruce M Simpson 	struct ip_msource	*ims, *tims;
907d10910e6SBruce M Simpson 
908d10910e6SBruce M Simpson 	RB_FOREACH_SAFE(ims, ip_msource_tree, &imf->imf_sources, tims) {
909d10910e6SBruce M Simpson 		CTR2(KTR_IGMPV3, "%s: free ims %p", __func__, ims);
910d10910e6SBruce M Simpson 		RB_REMOVE(ip_msource_tree, &imf->imf_sources, ims);
911d10910e6SBruce M Simpson 		free(ims, M_INMFILTER);
912d10910e6SBruce M Simpson 		imf->imf_nsrc--;
913d10910e6SBruce M Simpson 	}
914d10910e6SBruce M Simpson 	imf->imf_st[0] = imf->imf_st[1] = MCAST_UNDEFINED;
915d10910e6SBruce M Simpson 	KASSERT(RB_EMPTY(&imf->imf_sources),
916d10910e6SBruce M Simpson 	    ("%s: imf_sources not empty", __func__));
917d10910e6SBruce M Simpson }
918d10910e6SBruce M Simpson 
919d10910e6SBruce M Simpson /*
920d10910e6SBruce M Simpson  * Look up a source filter entry for a multicast group.
921d10910e6SBruce M Simpson  *
922d10910e6SBruce M Simpson  * inm is the group descriptor to work with.
923d10910e6SBruce M Simpson  * haddr is the host-byte-order IPv4 address to look up.
924d10910e6SBruce M Simpson  * noalloc may be non-zero to suppress allocation of sources.
925d10910e6SBruce M Simpson  * *pims will be set to the address of the retrieved or allocated source.
926d10910e6SBruce M Simpson  *
927d10910e6SBruce M Simpson  * SMPng: NOTE: may be called with locks held.
928d10910e6SBruce M Simpson  * Return 0 if successful, otherwise return a non-zero error code.
929d10910e6SBruce M Simpson  */
930d10910e6SBruce M Simpson static int
inm_get_source(struct in_multi * inm,const in_addr_t haddr,const int noalloc,struct ip_msource ** pims)931d10910e6SBruce M Simpson inm_get_source(struct in_multi *inm, const in_addr_t haddr,
932d10910e6SBruce M Simpson     const int noalloc, struct ip_msource **pims)
933d10910e6SBruce M Simpson {
934d10910e6SBruce M Simpson 	struct ip_msource	 find;
935d10910e6SBruce M Simpson 	struct ip_msource	*ims, *nims;
936d10910e6SBruce M Simpson 
937d10910e6SBruce M Simpson 	find.ims_haddr = haddr;
938d10910e6SBruce M Simpson 	ims = RB_FIND(ip_msource_tree, &inm->inm_srcs, &find);
939d10910e6SBruce M Simpson 	if (ims == NULL && !noalloc) {
940d10910e6SBruce M Simpson 		if (inm->inm_nsrc == in_mcast_maxgrpsrc)
941d10910e6SBruce M Simpson 			return (ENOSPC);
942d10910e6SBruce M Simpson 		nims = malloc(sizeof(struct ip_msource), M_IPMSOURCE,
943d10910e6SBruce M Simpson 		    M_NOWAIT | M_ZERO);
944d10910e6SBruce M Simpson 		if (nims == NULL)
945d10910e6SBruce M Simpson 			return (ENOMEM);
946d10910e6SBruce M Simpson 		nims->ims_haddr = haddr;
947d10910e6SBruce M Simpson 		RB_INSERT(ip_msource_tree, &inm->inm_srcs, nims);
948d10910e6SBruce M Simpson 		++inm->inm_nsrc;
949d10910e6SBruce M Simpson 		ims = nims;
950d10910e6SBruce M Simpson #ifdef KTR
95147d803eaSEric van Gyzen 		CTR3(KTR_IGMPV3, "%s: allocated 0x%08x as %p", __func__,
95240769242SEric van Gyzen 		    haddr, ims);
953d10910e6SBruce M Simpson #endif
954d10910e6SBruce M Simpson 	}
955d10910e6SBruce M Simpson 
956d10910e6SBruce M Simpson 	*pims = ims;
957d10910e6SBruce M Simpson 	return (0);
958d10910e6SBruce M Simpson }
959d10910e6SBruce M Simpson 
960d10910e6SBruce M Simpson /*
961d10910e6SBruce M Simpson  * Merge socket-layer source into IGMP-layer source.
962d10910e6SBruce M Simpson  * If rollback is non-zero, perform the inverse of the merge.
963d10910e6SBruce M Simpson  */
964d10910e6SBruce M Simpson static void
ims_merge(struct ip_msource * ims,const struct in_msource * lims,const int rollback)965d10910e6SBruce M Simpson ims_merge(struct ip_msource *ims, const struct in_msource *lims,
966d10910e6SBruce M Simpson     const int rollback)
967d10910e6SBruce M Simpson {
968d10910e6SBruce M Simpson 	int n = rollback ? -1 : 1;
969d10910e6SBruce M Simpson 
970d10910e6SBruce M Simpson 	if (lims->imsl_st[0] == MCAST_EXCLUDE) {
97147d803eaSEric van Gyzen 		CTR3(KTR_IGMPV3, "%s: t1 ex -= %d on 0x%08x",
97240769242SEric van Gyzen 		    __func__, n, ims->ims_haddr);
973d10910e6SBruce M Simpson 		ims->ims_st[1].ex -= n;
974d10910e6SBruce M Simpson 	} else if (lims->imsl_st[0] == MCAST_INCLUDE) {
97547d803eaSEric van Gyzen 		CTR3(KTR_IGMPV3, "%s: t1 in -= %d on 0x%08x",
97640769242SEric van Gyzen 		    __func__, n, ims->ims_haddr);
977d10910e6SBruce M Simpson 		ims->ims_st[1].in -= n;
978d10910e6SBruce M Simpson 	}
979d10910e6SBruce M Simpson 
980d10910e6SBruce M Simpson 	if (lims->imsl_st[1] == MCAST_EXCLUDE) {
98147d803eaSEric van Gyzen 		CTR3(KTR_IGMPV3, "%s: t1 ex += %d on 0x%08x",
98240769242SEric van Gyzen 		    __func__, n, ims->ims_haddr);
983d10910e6SBruce M Simpson 		ims->ims_st[1].ex += n;
984d10910e6SBruce M Simpson 	} else if (lims->imsl_st[1] == MCAST_INCLUDE) {
98547d803eaSEric van Gyzen 		CTR3(KTR_IGMPV3, "%s: t1 in += %d on 0x%08x",
98640769242SEric van Gyzen 		    __func__, n, ims->ims_haddr);
987d10910e6SBruce M Simpson 		ims->ims_st[1].in += n;
988d10910e6SBruce M Simpson 	}
989d10910e6SBruce M Simpson }
990d10910e6SBruce M Simpson 
991d10910e6SBruce M Simpson /*
992d10910e6SBruce M Simpson  * Atomically update the global in_multi state, when a membership's
993d10910e6SBruce M Simpson  * filter list is being updated in any way.
994d10910e6SBruce M Simpson  *
995d10910e6SBruce M Simpson  * imf is the per-inpcb-membership group filter pointer.
996d10910e6SBruce M Simpson  * A fake imf may be passed for in-kernel consumers.
997d10910e6SBruce M Simpson  *
998d10910e6SBruce M Simpson  * XXX This is a candidate for a set-symmetric-difference style loop
999d10910e6SBruce M Simpson  * which would eliminate the repeated lookup from root of ims nodes,
1000d10910e6SBruce M Simpson  * as they share the same key space.
1001d10910e6SBruce M Simpson  *
1002d10910e6SBruce M Simpson  * If any error occurred this function will back out of refcounts
1003d10910e6SBruce M Simpson  * and return a non-zero value.
1004d10910e6SBruce M Simpson  */
1005d10910e6SBruce M Simpson static int
inm_merge(struct in_multi * inm,struct in_mfilter * imf)1006d10910e6SBruce M Simpson inm_merge(struct in_multi *inm, /*const*/ struct in_mfilter *imf)
1007d10910e6SBruce M Simpson {
1008d10910e6SBruce M Simpson 	struct ip_msource	*ims, *nims;
1009d10910e6SBruce M Simpson 	struct in_msource	*lims;
1010d10910e6SBruce M Simpson 	int			 schanged, error;
1011d10910e6SBruce M Simpson 	int			 nsrc0, nsrc1;
1012d10910e6SBruce M Simpson 
1013d10910e6SBruce M Simpson 	schanged = 0;
1014d10910e6SBruce M Simpson 	error = 0;
1015d10910e6SBruce M Simpson 	nsrc1 = nsrc0 = 0;
1016f3e1324bSStephen Hurd 	IN_MULTI_LIST_LOCK_ASSERT();
1017d10910e6SBruce M Simpson 
1018d10910e6SBruce M Simpson 	/*
1019d10910e6SBruce M Simpson 	 * Update the source filters first, as this may fail.
1020d10910e6SBruce M Simpson 	 * Maintain count of in-mode filters at t0, t1. These are
1021d10910e6SBruce M Simpson 	 * used to work out if we transition into ASM mode or not.
1022d10910e6SBruce M Simpson 	 * Maintain a count of source filters whose state was
1023d10910e6SBruce M Simpson 	 * actually modified by this operation.
1024d10910e6SBruce M Simpson 	 */
1025d10910e6SBruce M Simpson 	RB_FOREACH(ims, ip_msource_tree, &imf->imf_sources) {
1026d10910e6SBruce M Simpson 		lims = (struct in_msource *)ims;
1027d10910e6SBruce M Simpson 		if (lims->imsl_st[0] == imf->imf_st[0]) nsrc0++;
1028d10910e6SBruce M Simpson 		if (lims->imsl_st[1] == imf->imf_st[1]) nsrc1++;
1029d10910e6SBruce M Simpson 		if (lims->imsl_st[0] == lims->imsl_st[1]) continue;
1030d10910e6SBruce M Simpson 		error = inm_get_source(inm, lims->ims_haddr, 0, &nims);
1031d10910e6SBruce M Simpson 		++schanged;
1032d10910e6SBruce M Simpson 		if (error)
1033d10910e6SBruce M Simpson 			break;
1034d10910e6SBruce M Simpson 		ims_merge(nims, lims, 0);
1035d10910e6SBruce M Simpson 	}
1036d10910e6SBruce M Simpson 	if (error) {
1037d10910e6SBruce M Simpson 		struct ip_msource *bims;
1038d10910e6SBruce M Simpson 
1039d10910e6SBruce M Simpson 		RB_FOREACH_REVERSE_FROM(ims, ip_msource_tree, nims) {
1040d10910e6SBruce M Simpson 			lims = (struct in_msource *)ims;
1041d10910e6SBruce M Simpson 			if (lims->imsl_st[0] == lims->imsl_st[1])
1042d10910e6SBruce M Simpson 				continue;
1043d10910e6SBruce M Simpson 			(void)inm_get_source(inm, lims->ims_haddr, 1, &bims);
1044d10910e6SBruce M Simpson 			if (bims == NULL)
1045d10910e6SBruce M Simpson 				continue;
1046d10910e6SBruce M Simpson 			ims_merge(bims, lims, 1);
1047d10910e6SBruce M Simpson 		}
1048d10910e6SBruce M Simpson 		goto out_reap;
1049d10910e6SBruce M Simpson 	}
1050d10910e6SBruce M Simpson 
1051d10910e6SBruce M Simpson 	CTR3(KTR_IGMPV3, "%s: imf filters in-mode: %d at t0, %d at t1",
1052d10910e6SBruce M Simpson 	    __func__, nsrc0, nsrc1);
1053d10910e6SBruce M Simpson 
1054d10910e6SBruce M Simpson 	/* Handle transition between INCLUDE {n} and INCLUDE {} on socket. */
1055d10910e6SBruce M Simpson 	if (imf->imf_st[0] == imf->imf_st[1] &&
1056d10910e6SBruce M Simpson 	    imf->imf_st[1] == MCAST_INCLUDE) {
1057d10910e6SBruce M Simpson 		if (nsrc1 == 0) {
1058d10910e6SBruce M Simpson 			CTR1(KTR_IGMPV3, "%s: --in on inm at t1", __func__);
1059d10910e6SBruce M Simpson 			--inm->inm_st[1].iss_in;
1060d10910e6SBruce M Simpson 		}
1061d10910e6SBruce M Simpson 	}
1062d10910e6SBruce M Simpson 
1063d10910e6SBruce M Simpson 	/* Handle filter mode transition on socket. */
1064d10910e6SBruce M Simpson 	if (imf->imf_st[0] != imf->imf_st[1]) {
1065d10910e6SBruce M Simpson 		CTR3(KTR_IGMPV3, "%s: imf transition %d to %d",
1066d10910e6SBruce M Simpson 		    __func__, imf->imf_st[0], imf->imf_st[1]);
1067d10910e6SBruce M Simpson 
1068d10910e6SBruce M Simpson 		if (imf->imf_st[0] == MCAST_EXCLUDE) {
1069d10910e6SBruce M Simpson 			CTR1(KTR_IGMPV3, "%s: --ex on inm at t1", __func__);
1070d10910e6SBruce M Simpson 			--inm->inm_st[1].iss_ex;
1071d10910e6SBruce M Simpson 		} else if (imf->imf_st[0] == MCAST_INCLUDE) {
1072d10910e6SBruce M Simpson 			CTR1(KTR_IGMPV3, "%s: --in on inm at t1", __func__);
1073d10910e6SBruce M Simpson 			--inm->inm_st[1].iss_in;
1074d10910e6SBruce M Simpson 		}
1075d10910e6SBruce M Simpson 
1076d10910e6SBruce M Simpson 		if (imf->imf_st[1] == MCAST_EXCLUDE) {
1077d10910e6SBruce M Simpson 			CTR1(KTR_IGMPV3, "%s: ex++ on inm at t1", __func__);
1078d10910e6SBruce M Simpson 			inm->inm_st[1].iss_ex++;
1079d10910e6SBruce M Simpson 		} else if (imf->imf_st[1] == MCAST_INCLUDE && nsrc1 > 0) {
1080d10910e6SBruce M Simpson 			CTR1(KTR_IGMPV3, "%s: in++ on inm at t1", __func__);
1081d10910e6SBruce M Simpson 			inm->inm_st[1].iss_in++;
1082d10910e6SBruce M Simpson 		}
1083d10910e6SBruce M Simpson 	}
1084d10910e6SBruce M Simpson 
1085d10910e6SBruce M Simpson 	/*
1086d10910e6SBruce M Simpson 	 * Track inm filter state in terms of listener counts.
1087d10910e6SBruce M Simpson 	 * If there are any exclusive listeners, stack-wide
1088d10910e6SBruce M Simpson 	 * membership is exclusive.
1089d10910e6SBruce M Simpson 	 * Otherwise, if only inclusive listeners, stack-wide is inclusive.
1090d10910e6SBruce M Simpson 	 * If no listeners remain, state is undefined at t1,
1091d10910e6SBruce M Simpson 	 * and the IGMP lifecycle for this group should finish.
1092d10910e6SBruce M Simpson 	 */
1093d10910e6SBruce M Simpson 	if (inm->inm_st[1].iss_ex > 0) {
1094d10910e6SBruce M Simpson 		CTR1(KTR_IGMPV3, "%s: transition to EX", __func__);
1095d10910e6SBruce M Simpson 		inm->inm_st[1].iss_fmode = MCAST_EXCLUDE;
1096d10910e6SBruce M Simpson 	} else if (inm->inm_st[1].iss_in > 0) {
1097d10910e6SBruce M Simpson 		CTR1(KTR_IGMPV3, "%s: transition to IN", __func__);
1098d10910e6SBruce M Simpson 		inm->inm_st[1].iss_fmode = MCAST_INCLUDE;
1099d10910e6SBruce M Simpson 	} else {
1100d10910e6SBruce M Simpson 		CTR1(KTR_IGMPV3, "%s: transition to UNDEF", __func__);
1101d10910e6SBruce M Simpson 		inm->inm_st[1].iss_fmode = MCAST_UNDEFINED;
1102d10910e6SBruce M Simpson 	}
1103d10910e6SBruce M Simpson 
1104d10910e6SBruce M Simpson 	/* Decrement ASM listener count on transition out of ASM mode. */
1105d10910e6SBruce M Simpson 	if (imf->imf_st[0] == MCAST_EXCLUDE && nsrc0 == 0) {
1106d10910e6SBruce M Simpson 		if ((imf->imf_st[1] != MCAST_EXCLUDE) ||
1107bd745936SEnji Cooper 		    (imf->imf_st[1] == MCAST_EXCLUDE && nsrc1 > 0)) {
1108d10910e6SBruce M Simpson 			CTR1(KTR_IGMPV3, "%s: --asm on inm at t1", __func__);
1109d10910e6SBruce M Simpson 			--inm->inm_st[1].iss_asm;
1110d10910e6SBruce M Simpson 		}
1111bd745936SEnji Cooper 	}
1112d10910e6SBruce M Simpson 
1113d10910e6SBruce M Simpson 	/* Increment ASM listener count on transition to ASM mode. */
1114d10910e6SBruce M Simpson 	if (imf->imf_st[1] == MCAST_EXCLUDE && nsrc1 == 0) {
1115d10910e6SBruce M Simpson 		CTR1(KTR_IGMPV3, "%s: asm++ on inm at t1", __func__);
1116d10910e6SBruce M Simpson 		inm->inm_st[1].iss_asm++;
1117d10910e6SBruce M Simpson 	}
1118d10910e6SBruce M Simpson 
1119d10910e6SBruce M Simpson 	CTR3(KTR_IGMPV3, "%s: merged imf %p to inm %p", __func__, imf, inm);
1120d10910e6SBruce M Simpson 	inm_print(inm);
1121d10910e6SBruce M Simpson 
1122d10910e6SBruce M Simpson out_reap:
1123d10910e6SBruce M Simpson 	if (schanged > 0) {
1124d10910e6SBruce M Simpson 		CTR1(KTR_IGMPV3, "%s: sources changed; reaping", __func__);
1125d10910e6SBruce M Simpson 		inm_reap(inm);
1126d10910e6SBruce M Simpson 	}
1127d10910e6SBruce M Simpson 	return (error);
1128d10910e6SBruce M Simpson }
1129d10910e6SBruce M Simpson 
1130d10910e6SBruce M Simpson /*
1131d10910e6SBruce M Simpson  * Mark an in_multi's filter set deltas as committed.
1132d10910e6SBruce M Simpson  * Called by IGMP after a state change has been enqueued.
1133d10910e6SBruce M Simpson  */
1134d10910e6SBruce M Simpson void
inm_commit(struct in_multi * inm)1135d10910e6SBruce M Simpson inm_commit(struct in_multi *inm)
1136d10910e6SBruce M Simpson {
1137d10910e6SBruce M Simpson 	struct ip_msource	*ims;
1138d10910e6SBruce M Simpson 
1139d10910e6SBruce M Simpson 	CTR2(KTR_IGMPV3, "%s: commit inm %p", __func__, inm);
1140d10910e6SBruce M Simpson 	CTR1(KTR_IGMPV3, "%s: pre commit:", __func__);
1141d10910e6SBruce M Simpson 	inm_print(inm);
1142d10910e6SBruce M Simpson 
1143d10910e6SBruce M Simpson 	RB_FOREACH(ims, ip_msource_tree, &inm->inm_srcs) {
1144d10910e6SBruce M Simpson 		ims->ims_st[0] = ims->ims_st[1];
1145d10910e6SBruce M Simpson 	}
1146d10910e6SBruce M Simpson 	inm->inm_st[0] = inm->inm_st[1];
1147d10910e6SBruce M Simpson }
1148d10910e6SBruce M Simpson 
1149d10910e6SBruce M Simpson /*
1150d10910e6SBruce M Simpson  * Reap unreferenced nodes from an in_multi's filter set.
1151d10910e6SBruce M Simpson  */
1152d10910e6SBruce M Simpson static void
inm_reap(struct in_multi * inm)1153d10910e6SBruce M Simpson inm_reap(struct in_multi *inm)
1154d10910e6SBruce M Simpson {
1155d10910e6SBruce M Simpson 	struct ip_msource	*ims, *tims;
1156d10910e6SBruce M Simpson 
1157d10910e6SBruce M Simpson 	RB_FOREACH_SAFE(ims, ip_msource_tree, &inm->inm_srcs, tims) {
1158d10910e6SBruce M Simpson 		if (ims->ims_st[0].ex > 0 || ims->ims_st[0].in > 0 ||
1159d10910e6SBruce M Simpson 		    ims->ims_st[1].ex > 0 || ims->ims_st[1].in > 0 ||
1160d10910e6SBruce M Simpson 		    ims->ims_stp != 0)
1161d10910e6SBruce M Simpson 			continue;
1162d10910e6SBruce M Simpson 		CTR2(KTR_IGMPV3, "%s: free ims %p", __func__, ims);
1163d10910e6SBruce M Simpson 		RB_REMOVE(ip_msource_tree, &inm->inm_srcs, ims);
1164d10910e6SBruce M Simpson 		free(ims, M_IPMSOURCE);
1165d10910e6SBruce M Simpson 		inm->inm_nsrc--;
1166d10910e6SBruce M Simpson 	}
1167d10910e6SBruce M Simpson }
1168d10910e6SBruce M Simpson 
1169d10910e6SBruce M Simpson /*
1170d10910e6SBruce M Simpson  * Purge all source nodes from an in_multi's filter set.
1171d10910e6SBruce M Simpson  */
1172d10910e6SBruce M Simpson static void
inm_purge(struct in_multi * inm)1173d10910e6SBruce M Simpson inm_purge(struct in_multi *inm)
1174d10910e6SBruce M Simpson {
1175d10910e6SBruce M Simpson 	struct ip_msource	*ims, *tims;
1176d10910e6SBruce M Simpson 
1177d10910e6SBruce M Simpson 	RB_FOREACH_SAFE(ims, ip_msource_tree, &inm->inm_srcs, tims) {
1178d10910e6SBruce M Simpson 		CTR2(KTR_IGMPV3, "%s: free ims %p", __func__, ims);
1179d10910e6SBruce M Simpson 		RB_REMOVE(ip_msource_tree, &inm->inm_srcs, ims);
1180d10910e6SBruce M Simpson 		free(ims, M_IPMSOURCE);
1181d10910e6SBruce M Simpson 		inm->inm_nsrc--;
1182d10910e6SBruce M Simpson 	}
1183c2e34045SKristof Provost 	mbufq_drain(&inm->inm_scq);
1184d10910e6SBruce M Simpson }
1185d10910e6SBruce M Simpson 
1186d10910e6SBruce M Simpson /*
1187d10910e6SBruce M Simpson  * Join a multicast group; unlocked entry point.
1188d10910e6SBruce M Simpson  *
1189*bbf86c65SMark Johnston  * SMPng: XXX: in_joingroup() is called from in_control().  Fortunately,
1190*bbf86c65SMark Johnston  * ifp is unlikely to have been detached at this point, so we assume
1191*bbf86c65SMark Johnston  * it's OK to recurse.
1192d10910e6SBruce M Simpson  */
1193d10910e6SBruce M Simpson int
in_joingroup(struct ifnet * ifp,const struct in_addr * gina,struct in_mfilter * imf,struct in_multi ** pinm)1194d10910e6SBruce M Simpson in_joingroup(struct ifnet *ifp, const struct in_addr *gina,
1195d10910e6SBruce M Simpson     /*const*/ struct in_mfilter *imf, struct in_multi **pinm)
1196d10910e6SBruce M Simpson {
1197d10910e6SBruce M Simpson 	int error;
1198d10910e6SBruce M Simpson 
1199d10910e6SBruce M Simpson 	IN_MULTI_LOCK();
1200d10910e6SBruce M Simpson 	error = in_joingroup_locked(ifp, gina, imf, pinm);
1201d10910e6SBruce M Simpson 	IN_MULTI_UNLOCK();
1202d10910e6SBruce M Simpson 
1203d10910e6SBruce M Simpson 	return (error);
1204d10910e6SBruce M Simpson }
1205d10910e6SBruce M Simpson 
1206d10910e6SBruce M Simpson /*
1207d10910e6SBruce M Simpson  * Join a multicast group; real entry point.
1208d10910e6SBruce M Simpson  *
1209d10910e6SBruce M Simpson  * Only preserves atomicity at inm level.
1210d10910e6SBruce M Simpson  * NOTE: imf argument cannot be const due to sys/tree.h limitations.
1211d10910e6SBruce M Simpson  *
1212d10910e6SBruce M Simpson  * If the IGMP downcall fails, the group is not joined, and an error
1213d10910e6SBruce M Simpson  * code is returned.
1214d10910e6SBruce M Simpson  */
1215d10910e6SBruce M Simpson int
in_joingroup_locked(struct ifnet * ifp,const struct in_addr * gina,struct in_mfilter * imf,struct in_multi ** pinm)1216d10910e6SBruce M Simpson in_joingroup_locked(struct ifnet *ifp, const struct in_addr *gina,
1217d10910e6SBruce M Simpson     /*const*/ struct in_mfilter *imf, struct in_multi **pinm)
1218d10910e6SBruce M Simpson {
1219d10910e6SBruce M Simpson 	struct in_mfilter	 timf;
1220d10910e6SBruce M Simpson 	struct in_multi		*inm;
1221d10910e6SBruce M Simpson 	int			 error;
1222d10910e6SBruce M Simpson 
1223d10910e6SBruce M Simpson 	IN_MULTI_LOCK_ASSERT();
1224f3e1324bSStephen Hurd 	IN_MULTI_LIST_UNLOCK_ASSERT();
1225d10910e6SBruce M Simpson 
122647d803eaSEric van Gyzen 	CTR4(KTR_IGMPV3, "%s: join 0x%08x on %p(%s))", __func__,
122740769242SEric van Gyzen 	    ntohl(gina->s_addr), ifp, ifp->if_xname);
1228d10910e6SBruce M Simpson 
1229d10910e6SBruce M Simpson 	error = 0;
1230d10910e6SBruce M Simpson 	inm = NULL;
1231d10910e6SBruce M Simpson 
1232d10910e6SBruce M Simpson 	/*
1233d10910e6SBruce M Simpson 	 * If no imf was specified (i.e. kernel consumer),
1234d10910e6SBruce M Simpson 	 * fake one up and assume it is an ASM join.
1235d10910e6SBruce M Simpson 	 */
1236d10910e6SBruce M Simpson 	if (imf == NULL) {
1237d10910e6SBruce M Simpson 		imf_init(&timf, MCAST_UNDEFINED, MCAST_EXCLUDE);
1238d10910e6SBruce M Simpson 		imf = &timf;
1239d10910e6SBruce M Simpson 	}
1240d10910e6SBruce M Simpson 
1241d10910e6SBruce M Simpson 	error = in_getmulti(ifp, gina, &inm);
1242d10910e6SBruce M Simpson 	if (error) {
1243d10910e6SBruce M Simpson 		CTR1(KTR_IGMPV3, "%s: in_getmulti() failure", __func__);
1244d10910e6SBruce M Simpson 		return (error);
1245d10910e6SBruce M Simpson 	}
1246f3e1324bSStephen Hurd 	IN_MULTI_LIST_LOCK();
1247d10910e6SBruce M Simpson 	CTR1(KTR_IGMPV3, "%s: merge inm state", __func__);
1248d10910e6SBruce M Simpson 	error = inm_merge(inm, imf);
1249d10910e6SBruce M Simpson 	if (error) {
1250d10910e6SBruce M Simpson 		CTR1(KTR_IGMPV3, "%s: failed to merge inm state", __func__);
1251d10910e6SBruce M Simpson 		goto out_inm_release;
1252d10910e6SBruce M Simpson 	}
1253d10910e6SBruce M Simpson 
1254d10910e6SBruce M Simpson 	CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__);
1255d10910e6SBruce M Simpson 	error = igmp_change_state(inm);
1256d10910e6SBruce M Simpson 	if (error) {
1257d10910e6SBruce M Simpson 		CTR1(KTR_IGMPV3, "%s: failed to update source", __func__);
1258d10910e6SBruce M Simpson 		goto out_inm_release;
1259d10910e6SBruce M Simpson 	}
1260d10910e6SBruce M Simpson 
1261d10910e6SBruce M Simpson  out_inm_release:
1262d10910e6SBruce M Simpson 	if (error) {
1263d10910e6SBruce M Simpson 		CTR2(KTR_IGMPV3, "%s: dropping ref on %p", __func__, inm);
1264200f3ac6SRyan Libby 		IF_ADDR_WLOCK(ifp);
1265f3e1324bSStephen Hurd 		inm_release_deferred(inm);
1266200f3ac6SRyan Libby 		IF_ADDR_WUNLOCK(ifp);
1267d10910e6SBruce M Simpson 	} else {
1268d10910e6SBruce M Simpson 		*pinm = inm;
1269d10910e6SBruce M Simpson 	}
1270b6f6f880SMatt Macy 	IN_MULTI_LIST_UNLOCK();
1271d10910e6SBruce M Simpson 
1272d10910e6SBruce M Simpson 	return (error);
1273d10910e6SBruce M Simpson }
1274d10910e6SBruce M Simpson 
1275d10910e6SBruce M Simpson /*
1276d10910e6SBruce M Simpson  * Leave a multicast group; unlocked entry point.
1277d10910e6SBruce M Simpson  */
1278d10910e6SBruce M Simpson int
in_leavegroup(struct in_multi * inm,struct in_mfilter * imf)1279d10910e6SBruce M Simpson in_leavegroup(struct in_multi *inm, /*const*/ struct in_mfilter *imf)
1280d10910e6SBruce M Simpson {
1281e5adda3dSRobert Watson 	int error;
1282d10910e6SBruce M Simpson 
1283d10910e6SBruce M Simpson 	IN_MULTI_LOCK();
1284d10910e6SBruce M Simpson 	error = in_leavegroup_locked(inm, imf);
1285d10910e6SBruce M Simpson 	IN_MULTI_UNLOCK();
1286d10910e6SBruce M Simpson 
1287d10910e6SBruce M Simpson 	return (error);
1288d10910e6SBruce M Simpson }
1289d10910e6SBruce M Simpson 
1290d10910e6SBruce M Simpson /*
1291d10910e6SBruce M Simpson  * Leave a multicast group; real entry point.
1292d10910e6SBruce M Simpson  * All source filters will be expunged.
1293d10910e6SBruce M Simpson  *
1294d10910e6SBruce M Simpson  * Only preserves atomicity at inm level.
1295d10910e6SBruce M Simpson  *
1296d10910e6SBruce M Simpson  * Holding the write lock for the INP which contains imf
1297d10910e6SBruce M Simpson  * is highly advisable. We can't assert for it as imf does not
1298d10910e6SBruce M Simpson  * contain a back-pointer to the owning inp.
1299d10910e6SBruce M Simpson  *
1300d10910e6SBruce M Simpson  * Note: This is not the same as inm_release(*) as this function also
1301d10910e6SBruce M Simpson  * makes a state change downcall into IGMP.
1302d10910e6SBruce M Simpson  */
1303d10910e6SBruce M Simpson int
in_leavegroup_locked(struct in_multi * inm,struct in_mfilter * imf)1304d10910e6SBruce M Simpson in_leavegroup_locked(struct in_multi *inm, /*const*/ struct in_mfilter *imf)
1305d10910e6SBruce M Simpson {
1306d10910e6SBruce M Simpson 	struct in_mfilter	 timf;
1307d10910e6SBruce M Simpson 	int			 error;
1308d10910e6SBruce M Simpson 
1309d10910e6SBruce M Simpson 	IN_MULTI_LOCK_ASSERT();
1310f3e1324bSStephen Hurd 	IN_MULTI_LIST_UNLOCK_ASSERT();
1311d10910e6SBruce M Simpson 
1312b8a6e03fSGleb Smirnoff 	error = 0;
1313b8a6e03fSGleb Smirnoff 
131447d803eaSEric van Gyzen 	CTR5(KTR_IGMPV3, "%s: leave inm %p, 0x%08x/%s, imf %p", __func__,
131540769242SEric van Gyzen 	    inm, ntohl(inm->inm_addr.s_addr),
1316d10910e6SBruce M Simpson 	    (inm_is_ifp_detached(inm) ? "null" : inm->inm_ifp->if_xname),
1317d10910e6SBruce M Simpson 	    imf);
1318d10910e6SBruce M Simpson 
1319d10910e6SBruce M Simpson 	/*
1320d10910e6SBruce M Simpson 	 * If no imf was specified (i.e. kernel consumer),
1321d10910e6SBruce M Simpson 	 * fake one up and assume it is an ASM join.
1322d10910e6SBruce M Simpson 	 */
1323d10910e6SBruce M Simpson 	if (imf == NULL) {
1324d10910e6SBruce M Simpson 		imf_init(&timf, MCAST_EXCLUDE, MCAST_UNDEFINED);
1325d10910e6SBruce M Simpson 		imf = &timf;
1326d10910e6SBruce M Simpson 	}
1327d10910e6SBruce M Simpson 
1328d10910e6SBruce M Simpson 	/*
1329d10910e6SBruce M Simpson 	 * Begin state merge transaction at IGMP layer.
1330d10910e6SBruce M Simpson 	 *
1331d10910e6SBruce M Simpson 	 * As this particular invocation should not cause any memory
1332d10910e6SBruce M Simpson 	 * to be allocated, and there is no opportunity to roll back
1333d10910e6SBruce M Simpson 	 * the transaction, it MUST NOT fail.
1334d10910e6SBruce M Simpson 	 */
1335d10910e6SBruce M Simpson 	CTR1(KTR_IGMPV3, "%s: merge inm state", __func__);
1336f3e1324bSStephen Hurd 	IN_MULTI_LIST_LOCK();
1337d10910e6SBruce M Simpson 	error = inm_merge(inm, imf);
1338d10910e6SBruce M Simpson 	KASSERT(error == 0, ("%s: failed to merge inm state", __func__));
1339d10910e6SBruce M Simpson 
1340d10910e6SBruce M Simpson 	CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__);
1341e32d9395SHiroki Sato 	CURVNET_SET(inm->inm_ifp->if_vnet);
1342d10910e6SBruce M Simpson 	error = igmp_change_state(inm);
1343b6f6f880SMatt Macy 	IF_ADDR_WLOCK(inm->inm_ifp);
1344f3e1324bSStephen Hurd 	inm_release_deferred(inm);
1345b6f6f880SMatt Macy 	IF_ADDR_WUNLOCK(inm->inm_ifp);
1346f3e1324bSStephen Hurd 	IN_MULTI_LIST_UNLOCK();
1347e32d9395SHiroki Sato 	CURVNET_RESTORE();
1348d10910e6SBruce M Simpson 	if (error)
1349d10910e6SBruce M Simpson 		CTR1(KTR_IGMPV3, "%s: failed igmp downcall", __func__);
1350d10910e6SBruce M Simpson 
1351d10910e6SBruce M Simpson 	CTR2(KTR_IGMPV3, "%s: dropping ref on %p", __func__, inm);
1352d10910e6SBruce M Simpson 
1353d10910e6SBruce M Simpson 	return (error);
1354d10910e6SBruce M Simpson }
1355d10910e6SBruce M Simpson 
1356d10910e6SBruce M Simpson /*#ifndef BURN_BRIDGES*/
1357d10910e6SBruce M Simpson 
1358d10910e6SBruce M Simpson /*
1359d10910e6SBruce M Simpson  * Block or unblock an ASM multicast source on an inpcb.
1360d10910e6SBruce M Simpson  * This implements the delta-based API described in RFC 3678.
1361d10910e6SBruce M Simpson  *
1362d10910e6SBruce M Simpson  * The delta-based API applies only to exclusive-mode memberships.
1363d10910e6SBruce M Simpson  * An IGMP downcall will be performed.
1364d10910e6SBruce M Simpson  *
1365d10910e6SBruce M Simpson  * Return 0 if successful, otherwise return an appropriate error code.
1366d10910e6SBruce M Simpson  */
1367d10910e6SBruce M Simpson static int
inp_block_unblock_source(struct inpcb * inp,struct sockopt * sopt)1368d10910e6SBruce M Simpson inp_block_unblock_source(struct inpcb *inp, struct sockopt *sopt)
136971498f30SBruce M Simpson {
1370d74b7baeSGleb Smirnoff 	struct epoch_tracker		 et;
137171498f30SBruce M Simpson 	struct group_source_req		 gsr;
137271498f30SBruce M Simpson 	sockunion_t			*gsa, *ssa;
137371498f30SBruce M Simpson 	struct ifnet			*ifp;
137471498f30SBruce M Simpson 	struct in_mfilter		*imf;
137571498f30SBruce M Simpson 	struct ip_moptions		*imo;
137671498f30SBruce M Simpson 	struct in_msource		*ims;
1377d10910e6SBruce M Simpson 	struct in_multi			*inm;
1378d10910e6SBruce M Simpson 	uint16_t			 fmode;
1379d10910e6SBruce M Simpson 	int				 error, doblock;
138071498f30SBruce M Simpson 
138171498f30SBruce M Simpson 	ifp = NULL;
138271498f30SBruce M Simpson 	error = 0;
1383d10910e6SBruce M Simpson 	doblock = 0;
138471498f30SBruce M Simpson 
138571498f30SBruce M Simpson 	memset(&gsr, 0, sizeof(struct group_source_req));
138671498f30SBruce M Simpson 	gsa = (sockunion_t *)&gsr.gsr_group;
138771498f30SBruce M Simpson 	ssa = (sockunion_t *)&gsr.gsr_source;
138871498f30SBruce M Simpson 
138971498f30SBruce M Simpson 	switch (sopt->sopt_name) {
139071498f30SBruce M Simpson 	case IP_BLOCK_SOURCE:
139171498f30SBruce M Simpson 	case IP_UNBLOCK_SOURCE: {
139271498f30SBruce M Simpson 		struct ip_mreq_source	 mreqs;
139371498f30SBruce M Simpson 
139471498f30SBruce M Simpson 		error = sooptcopyin(sopt, &mreqs,
139571498f30SBruce M Simpson 		    sizeof(struct ip_mreq_source),
139671498f30SBruce M Simpson 		    sizeof(struct ip_mreq_source));
139771498f30SBruce M Simpson 		if (error)
139871498f30SBruce M Simpson 			return (error);
139971498f30SBruce M Simpson 
140071498f30SBruce M Simpson 		gsa->sin.sin_family = AF_INET;
140171498f30SBruce M Simpson 		gsa->sin.sin_len = sizeof(struct sockaddr_in);
140271498f30SBruce M Simpson 		gsa->sin.sin_addr = mreqs.imr_multiaddr;
140371498f30SBruce M Simpson 
140471498f30SBruce M Simpson 		ssa->sin.sin_family = AF_INET;
140571498f30SBruce M Simpson 		ssa->sin.sin_len = sizeof(struct sockaddr_in);
140671498f30SBruce M Simpson 		ssa->sin.sin_addr = mreqs.imr_sourceaddr;
140771498f30SBruce M Simpson 
14084f1e3122SEugene Grosbein 		if (!in_nullhost(mreqs.imr_interface)) {
1409c8ee75f2SGleb Smirnoff 			NET_EPOCH_ENTER(et);
141071498f30SBruce M Simpson 			INADDR_TO_IFP(mreqs.imr_interface, ifp);
1411c8ee75f2SGleb Smirnoff 			/* XXXGL: ifref? */
1412c8ee75f2SGleb Smirnoff 			NET_EPOCH_EXIT(et);
14134f1e3122SEugene Grosbein 		}
141471498f30SBruce M Simpson 		if (sopt->sopt_name == IP_BLOCK_SOURCE)
1415d10910e6SBruce M Simpson 			doblock = 1;
141671498f30SBruce M Simpson 
141747d803eaSEric van Gyzen 		CTR3(KTR_IGMPV3, "%s: imr_interface = 0x%08x, ifp = %p",
141840769242SEric van Gyzen 		    __func__, ntohl(mreqs.imr_interface.s_addr), ifp);
141971498f30SBruce M Simpson 		break;
142071498f30SBruce M Simpson 	    }
142171498f30SBruce M Simpson 
142271498f30SBruce M Simpson 	case MCAST_BLOCK_SOURCE:
142371498f30SBruce M Simpson 	case MCAST_UNBLOCK_SOURCE:
142471498f30SBruce M Simpson 		error = sooptcopyin(sopt, &gsr,
142571498f30SBruce M Simpson 		    sizeof(struct group_source_req),
142671498f30SBruce M Simpson 		    sizeof(struct group_source_req));
142771498f30SBruce M Simpson 		if (error)
142871498f30SBruce M Simpson 			return (error);
142971498f30SBruce M Simpson 
143071498f30SBruce M Simpson 		if (gsa->sin.sin_family != AF_INET ||
143171498f30SBruce M Simpson 		    gsa->sin.sin_len != sizeof(struct sockaddr_in))
143271498f30SBruce M Simpson 			return (EINVAL);
143371498f30SBruce M Simpson 
143471498f30SBruce M Simpson 		if (ssa->sin.sin_family != AF_INET ||
143571498f30SBruce M Simpson 		    ssa->sin.sin_len != sizeof(struct sockaddr_in))
143671498f30SBruce M Simpson 			return (EINVAL);
143771498f30SBruce M Simpson 
1438d74b7baeSGleb Smirnoff 		NET_EPOCH_ENTER(et);
143971498f30SBruce M Simpson 		ifp = ifnet_byindex(gsr.gsr_interface);
1440d74b7baeSGleb Smirnoff 		NET_EPOCH_EXIT(et);
1441d74b7baeSGleb Smirnoff 		if (ifp == NULL)
1442d74b7baeSGleb Smirnoff 			return (EADDRNOTAVAIL);
144371498f30SBruce M Simpson 
144471498f30SBruce M Simpson 		if (sopt->sopt_name == MCAST_BLOCK_SOURCE)
1445d10910e6SBruce M Simpson 			doblock = 1;
144671498f30SBruce M Simpson 		break;
144771498f30SBruce M Simpson 
144871498f30SBruce M Simpson 	default:
1449d10910e6SBruce M Simpson 		CTR2(KTR_IGMPV3, "%s: unknown sopt_name %d",
1450d10910e6SBruce M Simpson 		    __func__, sopt->sopt_name);
145171498f30SBruce M Simpson 		return (EOPNOTSUPP);
145271498f30SBruce M Simpson 		break;
145371498f30SBruce M Simpson 	}
145471498f30SBruce M Simpson 
145571498f30SBruce M Simpson 	if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr)))
145671498f30SBruce M Simpson 		return (EINVAL);
145771498f30SBruce M Simpson 
145859854ecfSHans Petter Selasky 	IN_MULTI_LOCK();
145959854ecfSHans Petter Selasky 
146071498f30SBruce M Simpson 	/*
146171498f30SBruce M Simpson 	 * Check if we are actually a member of this group.
146271498f30SBruce M Simpson 	 */
146371498f30SBruce M Simpson 	imo = inp_findmoptions(inp);
146459854ecfSHans Petter Selasky 	imf = imo_match_group(imo, ifp, &gsa->sa);
146559854ecfSHans Petter Selasky 	if (imf == NULL) {
146671498f30SBruce M Simpson 		error = EADDRNOTAVAIL;
1467d10910e6SBruce M Simpson 		goto out_inp_locked;
146871498f30SBruce M Simpson 	}
146959854ecfSHans Petter Selasky 	inm = imf->imf_inm;
147071498f30SBruce M Simpson 
147171498f30SBruce M Simpson 	/*
1472d10910e6SBruce M Simpson 	 * Attempting to use the delta-based API on an
1473d10910e6SBruce M Simpson 	 * non exclusive-mode membership is an error.
147471498f30SBruce M Simpson 	 */
1475d10910e6SBruce M Simpson 	fmode = imf->imf_st[0];
1476d10910e6SBruce M Simpson 	if (fmode != MCAST_EXCLUDE) {
147771498f30SBruce M Simpson 		error = EINVAL;
1478d10910e6SBruce M Simpson 		goto out_inp_locked;
147971498f30SBruce M Simpson 	}
148071498f30SBruce M Simpson 
1481d10910e6SBruce M Simpson 	/*
1482d10910e6SBruce M Simpson 	 * Deal with error cases up-front:
1483d10910e6SBruce M Simpson 	 *  Asked to block, but already blocked; or
1484d10910e6SBruce M Simpson 	 *  Asked to unblock, but nothing to unblock.
1485d10910e6SBruce M Simpson 	 * If adding a new block entry, allocate it.
1486d10910e6SBruce M Simpson 	 */
148759854ecfSHans Petter Selasky 	ims = imo_match_source(imf, &ssa->sa);
1488d10910e6SBruce M Simpson 	if ((ims != NULL && doblock) || (ims == NULL && !doblock)) {
148947d803eaSEric van Gyzen 		CTR3(KTR_IGMPV3, "%s: source 0x%08x %spresent", __func__,
149040769242SEric van Gyzen 		    ntohl(ssa->sin.sin_addr.s_addr), doblock ? "" : "not ");
1491d10910e6SBruce M Simpson 		error = EADDRNOTAVAIL;
1492d10910e6SBruce M Simpson 		goto out_inp_locked;
1493d10910e6SBruce M Simpson 	}
1494d10910e6SBruce M Simpson 
1495d10910e6SBruce M Simpson 	INP_WLOCK_ASSERT(inp);
1496d10910e6SBruce M Simpson 
1497d10910e6SBruce M Simpson 	/*
1498d10910e6SBruce M Simpson 	 * Begin state merge transaction at socket layer.
1499d10910e6SBruce M Simpson 	 */
1500d10910e6SBruce M Simpson 	if (doblock) {
1501d10910e6SBruce M Simpson 		CTR2(KTR_IGMPV3, "%s: %s source", __func__, "block");
1502d10910e6SBruce M Simpson 		ims = imf_graft(imf, fmode, &ssa->sin);
1503d10910e6SBruce M Simpson 		if (ims == NULL)
1504d10910e6SBruce M Simpson 			error = ENOMEM;
1505d10910e6SBruce M Simpson 	} else {
1506d10910e6SBruce M Simpson 		CTR2(KTR_IGMPV3, "%s: %s source", __func__, "allow");
1507d10910e6SBruce M Simpson 		error = imf_prune(imf, &ssa->sin);
1508d10910e6SBruce M Simpson 	}
1509d10910e6SBruce M Simpson 
1510d10910e6SBruce M Simpson 	if (error) {
1511d10910e6SBruce M Simpson 		CTR1(KTR_IGMPV3, "%s: merge imf state failed", __func__);
1512d10910e6SBruce M Simpson 		goto out_imf_rollback;
1513d10910e6SBruce M Simpson 	}
1514d10910e6SBruce M Simpson 
1515d10910e6SBruce M Simpson 	/*
1516d10910e6SBruce M Simpson 	 * Begin state merge transaction at IGMP layer.
1517d10910e6SBruce M Simpson 	 */
1518d10910e6SBruce M Simpson 	CTR1(KTR_IGMPV3, "%s: merge inm state", __func__);
15195ba05d3dSHiroki Sato 	IN_MULTI_LIST_LOCK();
1520d10910e6SBruce M Simpson 	error = inm_merge(inm, imf);
1521d10910e6SBruce M Simpson 	if (error) {
1522d10910e6SBruce M Simpson 		CTR1(KTR_IGMPV3, "%s: failed to merge inm state", __func__);
15235ba05d3dSHiroki Sato 		IN_MULTI_LIST_UNLOCK();
152459854ecfSHans Petter Selasky 		goto out_imf_rollback;
1525d10910e6SBruce M Simpson 	}
1526d10910e6SBruce M Simpson 
1527d10910e6SBruce M Simpson 	CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__);
1528d10910e6SBruce M Simpson 	error = igmp_change_state(inm);
15295ba05d3dSHiroki Sato 	IN_MULTI_LIST_UNLOCK();
1530d10910e6SBruce M Simpson 	if (error)
1531d10910e6SBruce M Simpson 		CTR1(KTR_IGMPV3, "%s: failed igmp downcall", __func__);
1532d10910e6SBruce M Simpson 
1533d10910e6SBruce M Simpson out_imf_rollback:
1534d10910e6SBruce M Simpson 	if (error)
1535d10910e6SBruce M Simpson 		imf_rollback(imf);
1536d10910e6SBruce M Simpson 	else
1537d10910e6SBruce M Simpson 		imf_commit(imf);
1538d10910e6SBruce M Simpson 
1539d10910e6SBruce M Simpson 	imf_reap(imf);
1540d10910e6SBruce M Simpson 
1541d10910e6SBruce M Simpson out_inp_locked:
15428501a69cSRobert Watson 	INP_WUNLOCK(inp);
154359854ecfSHans Petter Selasky 	IN_MULTI_UNLOCK();
154471498f30SBruce M Simpson 	return (error);
154571498f30SBruce M Simpson }
154671498f30SBruce M Simpson 
154771498f30SBruce M Simpson /*
154871498f30SBruce M Simpson  * Given an inpcb, return its multicast options structure pointer.  Accepts
154971498f30SBruce M Simpson  * an unlocked inpcb pointer, but will return it locked.  May sleep.
1550d10910e6SBruce M Simpson  *
1551d10910e6SBruce M Simpson  * SMPng: NOTE: Returns with the INP write lock held.
155271498f30SBruce M Simpson  */
155371498f30SBruce M Simpson static struct ip_moptions *
inp_findmoptions(struct inpcb * inp)155471498f30SBruce M Simpson inp_findmoptions(struct inpcb *inp)
155571498f30SBruce M Simpson {
155671498f30SBruce M Simpson 	struct ip_moptions	 *imo;
155771498f30SBruce M Simpson 
15588501a69cSRobert Watson 	INP_WLOCK(inp);
155971498f30SBruce M Simpson 	if (inp->inp_moptions != NULL)
156071498f30SBruce M Simpson 		return (inp->inp_moptions);
156171498f30SBruce M Simpson 
15628501a69cSRobert Watson 	INP_WUNLOCK(inp);
156371498f30SBruce M Simpson 
1564d10910e6SBruce M Simpson 	imo = malloc(sizeof(*imo), M_IPMOPTS, M_WAITOK);
156571498f30SBruce M Simpson 
156671498f30SBruce M Simpson 	imo->imo_multicast_ifp = NULL;
156771498f30SBruce M Simpson 	imo->imo_multicast_addr.s_addr = INADDR_ANY;
156871498f30SBruce M Simpson 	imo->imo_multicast_vif = -1;
156971498f30SBruce M Simpson 	imo->imo_multicast_ttl = IP_DEFAULT_MULTICAST_TTL;
1570dd7fd7c0SBruce M Simpson 	imo->imo_multicast_loop = in_mcast_loop;
157159854ecfSHans Petter Selasky 	STAILQ_INIT(&imo->imo_head);
157271498f30SBruce M Simpson 
15738501a69cSRobert Watson 	INP_WLOCK(inp);
157471498f30SBruce M Simpson 	if (inp->inp_moptions != NULL) {
157571498f30SBruce M Simpson 		free(imo, M_IPMOPTS);
157671498f30SBruce M Simpson 		return (inp->inp_moptions);
157771498f30SBruce M Simpson 	}
157871498f30SBruce M Simpson 	inp->inp_moptions = imo;
157971498f30SBruce M Simpson 	return (imo);
158071498f30SBruce M Simpson }
158171498f30SBruce M Simpson 
15829b7501e7SGleb Smirnoff void
inp_freemoptions(struct ip_moptions * imo)15839b7501e7SGleb Smirnoff inp_freemoptions(struct ip_moptions *imo)
1584c23de1f4SJohn Baldwin {
1585c23de1f4SJohn Baldwin 	struct in_mfilter *imf;
158606b15160SMatt Macy 	struct in_multi *inm;
158706b15160SMatt Macy 	struct ifnet *ifp;
158871498f30SBruce M Simpson 
15899b7501e7SGleb Smirnoff 	if (imo == NULL)
15909b7501e7SGleb Smirnoff 		return;
15919b7501e7SGleb Smirnoff 
159259854ecfSHans Petter Selasky 	while ((imf = ip_mfilter_first(&imo->imo_head)) != NULL) {
159359854ecfSHans Petter Selasky 		ip_mfilter_remove(&imo->imo_head, imf);
159459854ecfSHans Petter Selasky 
1595d10910e6SBruce M Simpson 		imf_leave(imf);
159659854ecfSHans Petter Selasky 		if ((inm = imf->imf_inm) != NULL) {
159759854ecfSHans Petter Selasky 			if ((ifp = inm->inm_ifp) != NULL) {
159815f8acc5SEd Maste 				CURVNET_SET(ifp->if_vnet);
159906b15160SMatt Macy 				(void)in_leavegroup(inm, imf);
160015f8acc5SEd Maste 				CURVNET_RESTORE();
16013b9b6b17SEd Maste 			} else {
16023b9b6b17SEd Maste 				(void)in_leavegroup(inm, imf);
16033b9b6b17SEd Maste 			}
160471498f30SBruce M Simpson 		}
160559854ecfSHans Petter Selasky 		ip_mfilter_free(imf);
160659854ecfSHans Petter Selasky 	}
160771498f30SBruce M Simpson 	free(imo, M_IPMOPTS);
160871498f30SBruce M Simpson }
160971498f30SBruce M Simpson 
1610cb6bb230SMatt Macy /*
161171498f30SBruce M Simpson  * Atomically get source filters on a socket for an IPv4 multicast group.
161271498f30SBruce M Simpson  * Called with INP lock held; returns with lock released.
161371498f30SBruce M Simpson  */
161471498f30SBruce M Simpson static int
inp_get_source_filters(struct inpcb * inp,struct sockopt * sopt)161571498f30SBruce M Simpson inp_get_source_filters(struct inpcb *inp, struct sockopt *sopt)
161671498f30SBruce M Simpson {
1617d74b7baeSGleb Smirnoff 	struct epoch_tracker	 et;
161871498f30SBruce M Simpson 	struct __msfilterreq	 msfr;
161971498f30SBruce M Simpson 	sockunion_t		*gsa;
162071498f30SBruce M Simpson 	struct ifnet		*ifp;
162171498f30SBruce M Simpson 	struct ip_moptions	*imo;
162271498f30SBruce M Simpson 	struct in_mfilter	*imf;
1623d10910e6SBruce M Simpson 	struct ip_msource	*ims;
1624d10910e6SBruce M Simpson 	struct in_msource	*lims;
1625d10910e6SBruce M Simpson 	struct sockaddr_in	*psin;
162671498f30SBruce M Simpson 	struct sockaddr_storage	*ptss;
162771498f30SBruce M Simpson 	struct sockaddr_storage	*tss;
162871498f30SBruce M Simpson 	int			 error;
162959854ecfSHans Petter Selasky 	size_t			 nsrcs, ncsrcs;
163071498f30SBruce M Simpson 
16318501a69cSRobert Watson 	INP_WLOCK_ASSERT(inp);
163271498f30SBruce M Simpson 
163371498f30SBruce M Simpson 	imo = inp->inp_moptions;
163471498f30SBruce M Simpson 	KASSERT(imo != NULL, ("%s: null ip_moptions", __func__));
163571498f30SBruce M Simpson 
16368501a69cSRobert Watson 	INP_WUNLOCK(inp);
163771498f30SBruce M Simpson 
163871498f30SBruce M Simpson 	error = sooptcopyin(sopt, &msfr, sizeof(struct __msfilterreq),
163971498f30SBruce M Simpson 	    sizeof(struct __msfilterreq));
164071498f30SBruce M Simpson 	if (error)
164171498f30SBruce M Simpson 		return (error);
164271498f30SBruce M Simpson 
1643d74b7baeSGleb Smirnoff 	NET_EPOCH_ENTER(et);
164471498f30SBruce M Simpson 	ifp = ifnet_byindex(msfr.msfr_ifindex);
1645d74b7baeSGleb Smirnoff 	NET_EPOCH_EXIT(et);	/* XXXGL: unsafe ifnet pointer left */
164671498f30SBruce M Simpson 	if (ifp == NULL)
164771498f30SBruce M Simpson 		return (EINVAL);
164871498f30SBruce M Simpson 
16498501a69cSRobert Watson 	INP_WLOCK(inp);
165071498f30SBruce M Simpson 
165171498f30SBruce M Simpson 	/*
165271498f30SBruce M Simpson 	 * Lookup group on the socket.
165371498f30SBruce M Simpson 	 */
165471498f30SBruce M Simpson 	gsa = (sockunion_t *)&msfr.msfr_group;
165559854ecfSHans Petter Selasky 	imf = imo_match_group(imo, ifp, &gsa->sa);
165659854ecfSHans Petter Selasky 	if (imf == NULL) {
16578501a69cSRobert Watson 		INP_WUNLOCK(inp);
165871498f30SBruce M Simpson 		return (EADDRNOTAVAIL);
165971498f30SBruce M Simpson 	}
1660d10910e6SBruce M Simpson 
1661d10910e6SBruce M Simpson 	/*
1662d10910e6SBruce M Simpson 	 * Ignore memberships which are in limbo.
1663d10910e6SBruce M Simpson 	 */
1664d10910e6SBruce M Simpson 	if (imf->imf_st[1] == MCAST_UNDEFINED) {
1665d10910e6SBruce M Simpson 		INP_WUNLOCK(inp);
1666d10910e6SBruce M Simpson 		return (EAGAIN);
1667d10910e6SBruce M Simpson 	}
1668d10910e6SBruce M Simpson 	msfr.msfr_fmode = imf->imf_st[1];
166971498f30SBruce M Simpson 
167071498f30SBruce M Simpson 	/*
167171498f30SBruce M Simpson 	 * If the user specified a buffer, copy out the source filter
167271498f30SBruce M Simpson 	 * entries to userland gracefully.
167371498f30SBruce M Simpson 	 * We only copy out the number of entries which userland
167471498f30SBruce M Simpson 	 * has asked for, but we always tell userland how big the
167571498f30SBruce M Simpson 	 * buffer really needs to be.
167671498f30SBruce M Simpson 	 */
1677acde2476SXin LI 	if (msfr.msfr_nsrcs > in_mcast_maxsocksrc)
1678acde2476SXin LI 		msfr.msfr_nsrcs = in_mcast_maxsocksrc;
1679d10910e6SBruce M Simpson 	tss = NULL;
1680d10910e6SBruce M Simpson 	if (msfr.msfr_srcs != NULL && msfr.msfr_nsrcs > 0) {
16811ede983cSDag-Erling Smørgrav 		tss = malloc(sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs,
1682d10910e6SBruce M Simpson 		    M_TEMP, M_NOWAIT | M_ZERO);
168371498f30SBruce M Simpson 		if (tss == NULL) {
1684d10910e6SBruce M Simpson 			INP_WUNLOCK(inp);
1685d10910e6SBruce M Simpson 			return (ENOBUFS);
168671498f30SBruce M Simpson 		}
168771498f30SBruce M Simpson 	}
1688d10910e6SBruce M Simpson 
1689d10910e6SBruce M Simpson 	/*
1690d10910e6SBruce M Simpson 	 * Count number of sources in-mode at t0.
1691d10910e6SBruce M Simpson 	 * If buffer space exists and remains, copy out source entries.
1692d10910e6SBruce M Simpson 	 */
1693d10910e6SBruce M Simpson 	nsrcs = msfr.msfr_nsrcs;
1694d10910e6SBruce M Simpson 	ncsrcs = 0;
1695d10910e6SBruce M Simpson 	ptss = tss;
1696d10910e6SBruce M Simpson 	RB_FOREACH(ims, ip_msource_tree, &imf->imf_sources) {
1697d10910e6SBruce M Simpson 		lims = (struct in_msource *)ims;
1698d10910e6SBruce M Simpson 		if (lims->imsl_st[0] == MCAST_UNDEFINED ||
1699d10910e6SBruce M Simpson 		    lims->imsl_st[0] != imf->imf_st[0])
1700d10910e6SBruce M Simpson 			continue;
1701d10910e6SBruce M Simpson 		++ncsrcs;
1702c566b476SBruce M Simpson 		if (tss != NULL && nsrcs > 0) {
1703c566b476SBruce M Simpson 			psin = (struct sockaddr_in *)ptss;
1704d10910e6SBruce M Simpson 			psin->sin_family = AF_INET;
1705d10910e6SBruce M Simpson 			psin->sin_len = sizeof(struct sockaddr_in);
1706d10910e6SBruce M Simpson 			psin->sin_addr.s_addr = htonl(lims->ims_haddr);
1707c566b476SBruce M Simpson 			psin->sin_port = 0;
1708c566b476SBruce M Simpson 			++ptss;
1709c566b476SBruce M Simpson 			--nsrcs;
1710d10910e6SBruce M Simpson 		}
171171498f30SBruce M Simpson 	}
171271498f30SBruce M Simpson 
17138501a69cSRobert Watson 	INP_WUNLOCK(inp);
171471498f30SBruce M Simpson 
171571498f30SBruce M Simpson 	if (tss != NULL) {
171671498f30SBruce M Simpson 		error = copyout(tss, msfr.msfr_srcs,
171771498f30SBruce M Simpson 		    sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs);
17181ede983cSDag-Erling Smørgrav 		free(tss, M_TEMP);
171971498f30SBruce M Simpson 		if (error)
172071498f30SBruce M Simpson 			return (error);
1721d10910e6SBruce M Simpson 	}
172271498f30SBruce M Simpson 
1723d10910e6SBruce M Simpson 	msfr.msfr_nsrcs = ncsrcs;
172471498f30SBruce M Simpson 	error = sooptcopyout(sopt, &msfr, sizeof(struct __msfilterreq));
172571498f30SBruce M Simpson 
172671498f30SBruce M Simpson 	return (error);
172771498f30SBruce M Simpson }
172871498f30SBruce M Simpson 
172971498f30SBruce M Simpson /*
173071498f30SBruce M Simpson  * Return the IP multicast options in response to user getsockopt().
173171498f30SBruce M Simpson  */
173271498f30SBruce M Simpson int
inp_getmoptions(struct inpcb * inp,struct sockopt * sopt)173371498f30SBruce M Simpson inp_getmoptions(struct inpcb *inp, struct sockopt *sopt)
173471498f30SBruce M Simpson {
173571498f30SBruce M Simpson 	struct ip_mreqn		 mreqn;
173671498f30SBruce M Simpson 	struct ip_moptions	*imo;
173771498f30SBruce M Simpson 	struct ifnet		*ifp;
173871498f30SBruce M Simpson 	struct in_ifaddr	*ia;
173971498f30SBruce M Simpson 	int			 error, optval;
174071498f30SBruce M Simpson 	u_char			 coptval;
174171498f30SBruce M Simpson 
17428501a69cSRobert Watson 	INP_WLOCK(inp);
174371498f30SBruce M Simpson 	imo = inp->inp_moptions;
17448624f434SGleb Smirnoff 	/* If socket is neither of type SOCK_RAW or SOCK_DGRAM reject it. */
17458624f434SGleb Smirnoff 	if (inp->inp_socket->so_proto->pr_type != SOCK_RAW &&
17468624f434SGleb Smirnoff 	    inp->inp_socket->so_proto->pr_type != SOCK_DGRAM) {
17478501a69cSRobert Watson 		INP_WUNLOCK(inp);
1748b244c8adSChristian S.J. Peron 		return (EOPNOTSUPP);
1749b244c8adSChristian S.J. Peron 	}
175071498f30SBruce M Simpson 
175171498f30SBruce M Simpson 	error = 0;
175271498f30SBruce M Simpson 	switch (sopt->sopt_name) {
175371498f30SBruce M Simpson 	case IP_MULTICAST_VIF:
175471498f30SBruce M Simpson 		if (imo != NULL)
175571498f30SBruce M Simpson 			optval = imo->imo_multicast_vif;
175671498f30SBruce M Simpson 		else
175771498f30SBruce M Simpson 			optval = -1;
17588501a69cSRobert Watson 		INP_WUNLOCK(inp);
175971498f30SBruce M Simpson 		error = sooptcopyout(sopt, &optval, sizeof(int));
176071498f30SBruce M Simpson 		break;
176171498f30SBruce M Simpson 
176271498f30SBruce M Simpson 	case IP_MULTICAST_IF:
176371498f30SBruce M Simpson 		memset(&mreqn, 0, sizeof(struct ip_mreqn));
176471498f30SBruce M Simpson 		if (imo != NULL) {
176571498f30SBruce M Simpson 			ifp = imo->imo_multicast_ifp;
1766d10910e6SBruce M Simpson 			if (!in_nullhost(imo->imo_multicast_addr)) {
176771498f30SBruce M Simpson 				mreqn.imr_address = imo->imo_multicast_addr;
176871498f30SBruce M Simpson 			} else if (ifp != NULL) {
17690732ac0eSGleb Smirnoff 				struct epoch_tracker et;
17700732ac0eSGleb Smirnoff 
177171498f30SBruce M Simpson 				mreqn.imr_ifindex = ifp->if_index;
17720732ac0eSGleb Smirnoff 				NET_EPOCH_ENTER(et);
17732144431cSGleb Smirnoff 				IFP_TO_IA(ifp, ia);
17744f6c66ccSMatt Macy 				if (ia != NULL)
177571498f30SBruce M Simpson 					mreqn.imr_address =
177671498f30SBruce M Simpson 					    IA_SIN(ia)->sin_addr;
17770732ac0eSGleb Smirnoff 				NET_EPOCH_EXIT(et);
177871498f30SBruce M Simpson 			}
177971498f30SBruce M Simpson 		}
17808501a69cSRobert Watson 		INP_WUNLOCK(inp);
178171498f30SBruce M Simpson 		if (sopt->sopt_valsize == sizeof(struct ip_mreqn)) {
178271498f30SBruce M Simpson 			error = sooptcopyout(sopt, &mreqn,
178371498f30SBruce M Simpson 			    sizeof(struct ip_mreqn));
178471498f30SBruce M Simpson 		} else {
178571498f30SBruce M Simpson 			error = sooptcopyout(sopt, &mreqn.imr_address,
178671498f30SBruce M Simpson 			    sizeof(struct in_addr));
178771498f30SBruce M Simpson 		}
178871498f30SBruce M Simpson 		break;
178971498f30SBruce M Simpson 
179071498f30SBruce M Simpson 	case IP_MULTICAST_TTL:
179199d628d5SPedro F. Giffuni 		if (imo == NULL)
179271498f30SBruce M Simpson 			optval = coptval = IP_DEFAULT_MULTICAST_TTL;
179371498f30SBruce M Simpson 		else
179471498f30SBruce M Simpson 			optval = coptval = imo->imo_multicast_ttl;
17958501a69cSRobert Watson 		INP_WUNLOCK(inp);
179671498f30SBruce M Simpson 		if (sopt->sopt_valsize == sizeof(u_char))
179771498f30SBruce M Simpson 			error = sooptcopyout(sopt, &coptval, sizeof(u_char));
179871498f30SBruce M Simpson 		else
179971498f30SBruce M Simpson 			error = sooptcopyout(sopt, &optval, sizeof(int));
180071498f30SBruce M Simpson 		break;
180171498f30SBruce M Simpson 
180271498f30SBruce M Simpson 	case IP_MULTICAST_LOOP:
180399d628d5SPedro F. Giffuni 		if (imo == NULL)
180471498f30SBruce M Simpson 			optval = coptval = IP_DEFAULT_MULTICAST_LOOP;
180571498f30SBruce M Simpson 		else
180671498f30SBruce M Simpson 			optval = coptval = imo->imo_multicast_loop;
18078501a69cSRobert Watson 		INP_WUNLOCK(inp);
180871498f30SBruce M Simpson 		if (sopt->sopt_valsize == sizeof(u_char))
180971498f30SBruce M Simpson 			error = sooptcopyout(sopt, &coptval, sizeof(u_char));
181071498f30SBruce M Simpson 		else
181171498f30SBruce M Simpson 			error = sooptcopyout(sopt, &optval, sizeof(int));
181271498f30SBruce M Simpson 		break;
181371498f30SBruce M Simpson 
181471498f30SBruce M Simpson 	case IP_MSFILTER:
181571498f30SBruce M Simpson 		if (imo == NULL) {
181671498f30SBruce M Simpson 			error = EADDRNOTAVAIL;
18178501a69cSRobert Watson 			INP_WUNLOCK(inp);
181871498f30SBruce M Simpson 		} else {
181971498f30SBruce M Simpson 			error = inp_get_source_filters(inp, sopt);
182071498f30SBruce M Simpson 		}
182171498f30SBruce M Simpson 		break;
182271498f30SBruce M Simpson 
182371498f30SBruce M Simpson 	default:
18248501a69cSRobert Watson 		INP_WUNLOCK(inp);
182571498f30SBruce M Simpson 		error = ENOPROTOOPT;
182671498f30SBruce M Simpson 		break;
182771498f30SBruce M Simpson 	}
182871498f30SBruce M Simpson 
182971498f30SBruce M Simpson 	INP_UNLOCK_ASSERT(inp);
183071498f30SBruce M Simpson 
183171498f30SBruce M Simpson 	return (error);
183271498f30SBruce M Simpson }
183371498f30SBruce M Simpson 
183471498f30SBruce M Simpson /*
1835d10910e6SBruce M Simpson  * Look up the ifnet to use for a multicast group membership,
1836d10910e6SBruce M Simpson  * given the IPv4 address of an interface, and the IPv4 group address.
1837d10910e6SBruce M Simpson  *
1838d10910e6SBruce M Simpson  * This routine exists to support legacy multicast applications
1839d10910e6SBruce M Simpson  * which do not understand that multicast memberships are scoped to
1840d10910e6SBruce M Simpson  * specific physical links in the networking stack, or which need
1841d10910e6SBruce M Simpson  * to join link-scope groups before IPv4 addresses are configured.
1842d10910e6SBruce M Simpson  *
1843c3a456deSAlexander V. Chernikov  * Use this socket's current FIB number for any required FIB lookup.
1844d10910e6SBruce M Simpson  * If ina is INADDR_ANY, look up the group address in the unicast FIB,
1845d10910e6SBruce M Simpson  * and use its ifp; usually, this points to the default next-hop.
1846d10910e6SBruce M Simpson  *
1847d10910e6SBruce M Simpson  * If the FIB lookup fails, attempt to use the first non-loopback
1848d10910e6SBruce M Simpson  * interface with multicast capability in the system as a
1849d10910e6SBruce M Simpson  * last resort. The legacy IPv4 ASM API requires that we do
1850d10910e6SBruce M Simpson  * this in order to allow groups to be joined when the routing
1851d10910e6SBruce M Simpson  * table has not yet been populated during boot.
1852d10910e6SBruce M Simpson  *
1853337418adSAlexander V. Chernikov  * Returns NULL if no ifp could be found, otherwise return referenced ifp.
1854d10910e6SBruce M Simpson  *
1855d10910e6SBruce M Simpson  * FUTURE: Implement IPv4 source-address selection.
1856d10910e6SBruce M Simpson  */
1857d10910e6SBruce M Simpson static struct ifnet *
inp_lookup_mcast_ifp(const struct inpcb * inp,const struct sockaddr_in * gsin,const struct in_addr ina)1858d10910e6SBruce M Simpson inp_lookup_mcast_ifp(const struct inpcb *inp,
1859d10910e6SBruce M Simpson     const struct sockaddr_in *gsin, const struct in_addr ina)
1860d10910e6SBruce M Simpson {
1861d10910e6SBruce M Simpson 	struct ifnet *ifp;
18626ad7446cSAlexander V. Chernikov 	struct nhop_object *nh;
1863d10910e6SBruce M Simpson 
18642144431cSGleb Smirnoff 	NET_EPOCH_ASSERT();
1865c3a456deSAlexander V. Chernikov 	KASSERT(inp != NULL, ("%s: inp must not be NULL", __func__));
1866d10910e6SBruce M Simpson 	KASSERT(gsin->sin_family == AF_INET, ("%s: not AF_INET", __func__));
1867d10910e6SBruce M Simpson 	KASSERT(IN_MULTICAST(ntohl(gsin->sin_addr.s_addr)),
1868d10910e6SBruce M Simpson 	    ("%s: not multicast", __func__));
1869d10910e6SBruce M Simpson 
1870d10910e6SBruce M Simpson 	ifp = NULL;
1871d10910e6SBruce M Simpson 	if (!in_nullhost(ina)) {
1872d10910e6SBruce M Simpson 		INADDR_TO_IFP(ina, ifp);
1873337418adSAlexander V. Chernikov 		if (ifp != NULL)
1874337418adSAlexander V. Chernikov 			if_ref(ifp);
1875d10910e6SBruce M Simpson 	} else {
1876c3a456deSAlexander V. Chernikov 		nh = fib4_lookup(inp->inp_inc.inc_fibnum, gsin->sin_addr, 0, NHR_NONE, 0);
1877337418adSAlexander V. Chernikov 		if (nh != NULL) {
18786ad7446cSAlexander V. Chernikov 			ifp = nh->nh_ifp;
1879337418adSAlexander V. Chernikov 			if_ref(ifp);
1880337418adSAlexander V. Chernikov 		} else {
1881d10910e6SBruce M Simpson 			struct in_ifaddr *ia;
1882d10910e6SBruce M Simpson 			struct ifnet *mifp;
1883d10910e6SBruce M Simpson 
1884d10910e6SBruce M Simpson 			mifp = NULL;
1885d7c5a620SMatt Macy 			CK_STAILQ_FOREACH(ia, &V_in_ifaddrhead, ia_link) {
1886d10910e6SBruce M Simpson 				mifp = ia->ia_ifp;
1887d10910e6SBruce M Simpson 				if (!(mifp->if_flags & IFF_LOOPBACK) &&
1888d10910e6SBruce M Simpson 				     (mifp->if_flags & IFF_MULTICAST)) {
1889d10910e6SBruce M Simpson 					ifp = mifp;
1890337418adSAlexander V. Chernikov 					if_ref(ifp);
1891d10910e6SBruce M Simpson 					break;
1892d10910e6SBruce M Simpson 				}
1893d10910e6SBruce M Simpson 			}
1894d10910e6SBruce M Simpson 		}
1895d10910e6SBruce M Simpson 	}
1896d10910e6SBruce M Simpson 
1897d10910e6SBruce M Simpson 	return (ifp);
1898d10910e6SBruce M Simpson }
1899d10910e6SBruce M Simpson 
1900d10910e6SBruce M Simpson /*
190171498f30SBruce M Simpson  * Join an IPv4 multicast group, possibly with a source.
190271498f30SBruce M Simpson  */
190371498f30SBruce M Simpson static int
inp_join_group(struct inpcb * inp,struct sockopt * sopt)190471498f30SBruce M Simpson inp_join_group(struct inpcb *inp, struct sockopt *sopt)
190571498f30SBruce M Simpson {
190671498f30SBruce M Simpson 	struct group_source_req		 gsr;
190771498f30SBruce M Simpson 	sockunion_t			*gsa, *ssa;
190871498f30SBruce M Simpson 	struct ifnet			*ifp;
190971498f30SBruce M Simpson 	struct in_mfilter		*imf;
191071498f30SBruce M Simpson 	struct ip_moptions		*imo;
191171498f30SBruce M Simpson 	struct in_multi			*inm;
1912d10910e6SBruce M Simpson 	struct in_msource		*lims;
1913337418adSAlexander V. Chernikov 	struct epoch_tracker		 et;
1914d10910e6SBruce M Simpson 	int				 error, is_new;
191571498f30SBruce M Simpson 
191671498f30SBruce M Simpson 	ifp = NULL;
1917fa2eebfcSBruce M Simpson 	lims = NULL;
191871498f30SBruce M Simpson 	error = 0;
191971498f30SBruce M Simpson 
192071498f30SBruce M Simpson 	memset(&gsr, 0, sizeof(struct group_source_req));
192171498f30SBruce M Simpson 	gsa = (sockunion_t *)&gsr.gsr_group;
192271498f30SBruce M Simpson 	gsa->ss.ss_family = AF_UNSPEC;
192371498f30SBruce M Simpson 	ssa = (sockunion_t *)&gsr.gsr_source;
192471498f30SBruce M Simpson 	ssa->ss.ss_family = AF_UNSPEC;
192571498f30SBruce M Simpson 
192671498f30SBruce M Simpson 	switch (sopt->sopt_name) {
19270dfc145aSGleb Smirnoff 	case IP_ADD_MEMBERSHIP: {
19280dfc145aSGleb Smirnoff 		struct ip_mreqn mreqn;
192971498f30SBruce M Simpson 
19300dfc145aSGleb Smirnoff 		if (sopt->sopt_valsize == sizeof(struct ip_mreqn))
19310dfc145aSGleb Smirnoff 			error = sooptcopyin(sopt, &mreqn,
19320dfc145aSGleb Smirnoff 			    sizeof(struct ip_mreqn), sizeof(struct ip_mreqn));
19330dfc145aSGleb Smirnoff 		else
19340dfc145aSGleb Smirnoff 			error = sooptcopyin(sopt, &mreqn,
19350dfc145aSGleb Smirnoff 			    sizeof(struct ip_mreq), sizeof(struct ip_mreq));
193671498f30SBruce M Simpson 		if (error)
193771498f30SBruce M Simpson 			return (error);
193871498f30SBruce M Simpson 
193971498f30SBruce M Simpson 		gsa->sin.sin_family = AF_INET;
194071498f30SBruce M Simpson 		gsa->sin.sin_len = sizeof(struct sockaddr_in);
19410dfc145aSGleb Smirnoff 		gsa->sin.sin_addr = mreqn.imr_multiaddr;
1942e72ae6eaSShteryana Shopova 		if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr)))
1943e72ae6eaSShteryana Shopova 			return (EINVAL);
1944e72ae6eaSShteryana Shopova 
1945337418adSAlexander V. Chernikov 		NET_EPOCH_ENTER(et);
19460dfc145aSGleb Smirnoff 		if (sopt->sopt_valsize == sizeof(struct ip_mreqn) &&
19470dfc145aSGleb Smirnoff 		    mreqn.imr_ifindex != 0)
1948337418adSAlexander V. Chernikov 			ifp = ifnet_byindex_ref(mreqn.imr_ifindex);
19490dfc145aSGleb Smirnoff 		else
19500dfc145aSGleb Smirnoff 			ifp = inp_lookup_mcast_ifp(inp, &gsa->sin,
19510dfc145aSGleb Smirnoff 			    mreqn.imr_address);
1952fa8b3fcbSAlexander V. Chernikov 		NET_EPOCH_EXIT(et);
19530dfc145aSGleb Smirnoff 		break;
19540dfc145aSGleb Smirnoff 	}
19550dfc145aSGleb Smirnoff 	case IP_ADD_SOURCE_MEMBERSHIP: {
19560dfc145aSGleb Smirnoff 		struct ip_mreq_source	 mreqs;
19570dfc145aSGleb Smirnoff 
19580dfc145aSGleb Smirnoff 		error = sooptcopyin(sopt, &mreqs, sizeof(struct ip_mreq_source),
19590dfc145aSGleb Smirnoff 			    sizeof(struct ip_mreq_source));
19600dfc145aSGleb Smirnoff 		if (error)
19610dfc145aSGleb Smirnoff 			return (error);
19620dfc145aSGleb Smirnoff 
19630dfc145aSGleb Smirnoff 		gsa->sin.sin_family = ssa->sin.sin_family = AF_INET;
19640dfc145aSGleb Smirnoff 		gsa->sin.sin_len = ssa->sin.sin_len =
19650dfc145aSGleb Smirnoff 		    sizeof(struct sockaddr_in);
19660dfc145aSGleb Smirnoff 
19670dfc145aSGleb Smirnoff 		gsa->sin.sin_addr = mreqs.imr_multiaddr;
19680dfc145aSGleb Smirnoff 		if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr)))
19690dfc145aSGleb Smirnoff 			return (EINVAL);
19700dfc145aSGleb Smirnoff 
19710dfc145aSGleb Smirnoff 		ssa->sin.sin_addr = mreqs.imr_sourceaddr;
19720dfc145aSGleb Smirnoff 
1973337418adSAlexander V. Chernikov 		NET_EPOCH_ENTER(et);
1974d10910e6SBruce M Simpson 		ifp = inp_lookup_mcast_ifp(inp, &gsa->sin,
1975d10910e6SBruce M Simpson 		    mreqs.imr_interface);
1976fa8b3fcbSAlexander V. Chernikov 		NET_EPOCH_EXIT(et);
197747d803eaSEric van Gyzen 		CTR3(KTR_IGMPV3, "%s: imr_interface = 0x%08x, ifp = %p",
197840769242SEric van Gyzen 		    __func__, ntohl(mreqs.imr_interface.s_addr), ifp);
197971498f30SBruce M Simpson 		break;
198071498f30SBruce M Simpson 	}
198171498f30SBruce M Simpson 
198271498f30SBruce M Simpson 	case MCAST_JOIN_GROUP:
198371498f30SBruce M Simpson 	case MCAST_JOIN_SOURCE_GROUP:
198471498f30SBruce M Simpson 		if (sopt->sopt_name == MCAST_JOIN_GROUP) {
198571498f30SBruce M Simpson 			error = sooptcopyin(sopt, &gsr,
198671498f30SBruce M Simpson 			    sizeof(struct group_req),
198771498f30SBruce M Simpson 			    sizeof(struct group_req));
198871498f30SBruce M Simpson 		} else if (sopt->sopt_name == MCAST_JOIN_SOURCE_GROUP) {
198971498f30SBruce M Simpson 			error = sooptcopyin(sopt, &gsr,
199071498f30SBruce M Simpson 			    sizeof(struct group_source_req),
199171498f30SBruce M Simpson 			    sizeof(struct group_source_req));
199271498f30SBruce M Simpson 		}
199371498f30SBruce M Simpson 		if (error)
199471498f30SBruce M Simpson 			return (error);
199571498f30SBruce M Simpson 
199671498f30SBruce M Simpson 		if (gsa->sin.sin_family != AF_INET ||
199771498f30SBruce M Simpson 		    gsa->sin.sin_len != sizeof(struct sockaddr_in))
199871498f30SBruce M Simpson 			return (EINVAL);
199971498f30SBruce M Simpson 
200071498f30SBruce M Simpson 		/*
200171498f30SBruce M Simpson 		 * Overwrite the port field if present, as the sockaddr
200271498f30SBruce M Simpson 		 * being copied in may be matched with a binary comparison.
200371498f30SBruce M Simpson 		 */
200471498f30SBruce M Simpson 		gsa->sin.sin_port = 0;
200571498f30SBruce M Simpson 		if (sopt->sopt_name == MCAST_JOIN_SOURCE_GROUP) {
200671498f30SBruce M Simpson 			if (ssa->sin.sin_family != AF_INET ||
200771498f30SBruce M Simpson 			    ssa->sin.sin_len != sizeof(struct sockaddr_in))
200871498f30SBruce M Simpson 				return (EINVAL);
200971498f30SBruce M Simpson 			ssa->sin.sin_port = 0;
201071498f30SBruce M Simpson 		}
201171498f30SBruce M Simpson 
2012e72ae6eaSShteryana Shopova 		if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr)))
2013e72ae6eaSShteryana Shopova 			return (EINVAL);
2014e72ae6eaSShteryana Shopova 
2015337418adSAlexander V. Chernikov 		NET_EPOCH_ENTER(et);
2016337418adSAlexander V. Chernikov 		ifp = ifnet_byindex_ref(gsr.gsr_interface);
2017fa8b3fcbSAlexander V. Chernikov 		NET_EPOCH_EXIT(et);
2018d74b7baeSGleb Smirnoff 		if (ifp == NULL)
2019d74b7baeSGleb Smirnoff 			return (EADDRNOTAVAIL);
202071498f30SBruce M Simpson 		break;
202171498f30SBruce M Simpson 
202271498f30SBruce M Simpson 	default:
2023d10910e6SBruce M Simpson 		CTR2(KTR_IGMPV3, "%s: unknown sopt_name %d",
2024d10910e6SBruce M Simpson 		    __func__, sopt->sopt_name);
202571498f30SBruce M Simpson 		return (EOPNOTSUPP);
2026f00876fbSMark Johnston 		break;
202771498f30SBruce M Simpson 	}
202871498f30SBruce M Simpson 
2029337418adSAlexander V. Chernikov 	if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) {
2030337418adSAlexander V. Chernikov 		if (ifp != NULL)
2031337418adSAlexander V. Chernikov 			if_rele(ifp);
203271498f30SBruce M Simpson 		return (EADDRNOTAVAIL);
2033337418adSAlexander V. Chernikov 	}
203471498f30SBruce M Simpson 
203559854ecfSHans Petter Selasky 	IN_MULTI_LOCK();
203659854ecfSHans Petter Selasky 
203759854ecfSHans Petter Selasky 	/*
203859854ecfSHans Petter Selasky 	 * Find the membership in the membership list.
203959854ecfSHans Petter Selasky 	 */
204071498f30SBruce M Simpson 	imo = inp_findmoptions(inp);
204159854ecfSHans Petter Selasky 	imf = imo_match_group(imo, ifp, &gsa->sa);
204259854ecfSHans Petter Selasky 	if (imf == NULL) {
2043d10910e6SBruce M Simpson 		is_new = 1;
204459854ecfSHans Petter Selasky 		inm = NULL;
204559854ecfSHans Petter Selasky 
204659854ecfSHans Petter Selasky 		if (ip_mfilter_count(&imo->imo_head) >= IP_MAX_MEMBERSHIPS) {
204759854ecfSHans Petter Selasky 			error = ENOMEM;
204859854ecfSHans Petter Selasky 			goto out_inp_locked;
204959854ecfSHans Petter Selasky 		}
205071498f30SBruce M Simpson 	} else {
205159854ecfSHans Petter Selasky 		is_new = 0;
205259854ecfSHans Petter Selasky 		inm = imf->imf_inm;
205359854ecfSHans Petter Selasky 
20541fc39d54SBruce M Simpson 		if (ssa->ss.ss_family != AF_UNSPEC) {
20551fc39d54SBruce M Simpson 			/*
2056a8cf681dSBruce M Simpson 			 * MCAST_JOIN_SOURCE_GROUP on an exclusive membership
20571fc39d54SBruce M Simpson 			 * is an error. On an existing inclusive membership,
20581fc39d54SBruce M Simpson 			 * it just adds the source to the filter list.
20591fc39d54SBruce M Simpson 			 */
20601fc39d54SBruce M Simpson 			if (imf->imf_st[1] != MCAST_INCLUDE) {
206171498f30SBruce M Simpson 				error = EINVAL;
2062d10910e6SBruce M Simpson 				goto out_inp_locked;
206371498f30SBruce M Simpson 			}
2064fa2eebfcSBruce M Simpson 			/*
2065fa2eebfcSBruce M Simpson 			 * Throw out duplicates.
2066fa2eebfcSBruce M Simpson 			 *
2067fa2eebfcSBruce M Simpson 			 * XXX FIXME: This makes a naive assumption that
2068fa2eebfcSBruce M Simpson 			 * even if entries exist for *ssa in this imf,
2069fa2eebfcSBruce M Simpson 			 * they will be rejected as dupes, even if they
2070fa2eebfcSBruce M Simpson 			 * are not valid in the current mode (in-mode).
2071fa2eebfcSBruce M Simpson 			 *
2072fa2eebfcSBruce M Simpson 			 * in_msource is transactioned just as for anything
2073fa2eebfcSBruce M Simpson 			 * else in SSM -- but note naive use of inm_graft()
2074fa2eebfcSBruce M Simpson 			 * below for allocating new filter entries.
2075fa2eebfcSBruce M Simpson 			 *
2076fa2eebfcSBruce M Simpson 			 * This is only an issue if someone mixes the
2077fa2eebfcSBruce M Simpson 			 * full-state SSM API with the delta-based API,
2078fa2eebfcSBruce M Simpson 			 * which is discouraged in the relevant RFCs.
2079fa2eebfcSBruce M Simpson 			 */
208059854ecfSHans Petter Selasky 			lims = imo_match_source(imf, &ssa->sa);
2081fa2eebfcSBruce M Simpson 			if (lims != NULL /*&&
2082fa2eebfcSBruce M Simpson 			    lims->imsl_st[1] == MCAST_INCLUDE*/) {
2083d10910e6SBruce M Simpson 				error = EADDRNOTAVAIL;
2084d10910e6SBruce M Simpson 				goto out_inp_locked;
208571498f30SBruce M Simpson 			}
20861fc39d54SBruce M Simpson 		} else {
20871fc39d54SBruce M Simpson 			/*
2088933fc4ddSBruce M Simpson 			 * MCAST_JOIN_GROUP on an existing exclusive
2089933fc4ddSBruce M Simpson 			 * membership is an error; return EADDRINUSE
2090933fc4ddSBruce M Simpson 			 * to preserve 4.4BSD API idempotence, and
2091933fc4ddSBruce M Simpson 			 * avoid tedious detour to code below.
2092933fc4ddSBruce M Simpson 			 * NOTE: This is bending RFC 3678 a bit.
2093933fc4ddSBruce M Simpson 			 *
20940eebc0d7SBruce M Simpson 			 * On an existing inclusive membership, this is also
20950eebc0d7SBruce M Simpson 			 * an error; if you want to change filter mode,
20960eebc0d7SBruce M Simpson 			 * you must use the userland API setsourcefilter().
20970eebc0d7SBruce M Simpson 			 * XXX We don't reject this for imf in UNDEFINED
20980eebc0d7SBruce M Simpson 			 * state at t1, because allocation of a filter
20990eebc0d7SBruce M Simpson 			 * is atomic with allocation of a membership.
21001fc39d54SBruce M Simpson 			 */
21011fc39d54SBruce M Simpson 			error = EINVAL;
2102933fc4ddSBruce M Simpson 			if (imf->imf_st[1] == MCAST_EXCLUDE)
2103933fc4ddSBruce M Simpson 				error = EADDRINUSE;
21041fc39d54SBruce M Simpson 			goto out_inp_locked;
21051fc39d54SBruce M Simpson 		}
21061fc39d54SBruce M Simpson 	}
210771498f30SBruce M Simpson 
210871498f30SBruce M Simpson 	/*
2109d10910e6SBruce M Simpson 	 * Begin state merge transaction at socket layer.
211071498f30SBruce M Simpson 	 */
2111d10910e6SBruce M Simpson 	INP_WLOCK_ASSERT(inp);
2112d10910e6SBruce M Simpson 
211371498f30SBruce M Simpson 	/*
2114d10910e6SBruce M Simpson 	 * Graft new source into filter list for this inpcb's
2115d10910e6SBruce M Simpson 	 * membership of the group. The in_multi may not have
21161fc39d54SBruce M Simpson 	 * been allocated yet if this is a new membership, however,
21171fc39d54SBruce M Simpson 	 * the in_mfilter slot will be allocated and must be initialized.
21180eebc0d7SBruce M Simpson 	 *
21190eebc0d7SBruce M Simpson 	 * Note: Grafting of exclusive mode filters doesn't happen
21200eebc0d7SBruce M Simpson 	 * in this path.
2121fa2eebfcSBruce M Simpson 	 * XXX: Should check for non-NULL lims (node exists but may
2122fa2eebfcSBruce M Simpson 	 * not be in-mode) for interop with full-state API.
212371498f30SBruce M Simpson 	 */
212471498f30SBruce M Simpson 	if (ssa->ss.ss_family != AF_UNSPEC) {
2125d10910e6SBruce M Simpson 		/* Membership starts in IN mode */
2126d10910e6SBruce M Simpson 		if (is_new) {
2127d10910e6SBruce M Simpson 			CTR1(KTR_IGMPV3, "%s: new join w/source", __func__);
212859854ecfSHans Petter Selasky 			imf = ip_mfilter_alloc(M_NOWAIT, MCAST_UNDEFINED, MCAST_INCLUDE);
212959854ecfSHans Petter Selasky 			if (imf == NULL) {
213059854ecfSHans Petter Selasky 				error = ENOMEM;
213159854ecfSHans Petter Selasky 				goto out_inp_locked;
213259854ecfSHans Petter Selasky 			}
2133d10910e6SBruce M Simpson 		} else {
2134d10910e6SBruce M Simpson 			CTR2(KTR_IGMPV3, "%s: %s source", __func__, "allow");
2135d10910e6SBruce M Simpson 		}
2136d10910e6SBruce M Simpson 		lims = imf_graft(imf, MCAST_INCLUDE, &ssa->sin);
2137d10910e6SBruce M Simpson 		if (lims == NULL) {
2138d10910e6SBruce M Simpson 			CTR1(KTR_IGMPV3, "%s: merge imf state failed",
2139d10910e6SBruce M Simpson 			    __func__);
2140d10910e6SBruce M Simpson 			error = ENOMEM;
214159854ecfSHans Petter Selasky 			goto out_inp_locked;
2142d10910e6SBruce M Simpson 		}
21431fc39d54SBruce M Simpson 	} else {
21441fc39d54SBruce M Simpson 		/* No address specified; Membership starts in EX mode */
21451fc39d54SBruce M Simpson 		if (is_new) {
21461fc39d54SBruce M Simpson 			CTR1(KTR_IGMPV3, "%s: new join w/o source", __func__);
214759854ecfSHans Petter Selasky 			imf = ip_mfilter_alloc(M_NOWAIT, MCAST_UNDEFINED, MCAST_EXCLUDE);
214859854ecfSHans Petter Selasky 			if (imf == NULL) {
214959854ecfSHans Petter Selasky 				error = ENOMEM;
215059854ecfSHans Petter Selasky 				goto out_inp_locked;
215159854ecfSHans Petter Selasky 			}
21521fc39d54SBruce M Simpson 		}
2153d10910e6SBruce M Simpson 	}
215471498f30SBruce M Simpson 
2155d10910e6SBruce M Simpson 	/*
2156d10910e6SBruce M Simpson 	 * Begin state merge transaction at IGMP layer.
2157d10910e6SBruce M Simpson 	 */
215859854ecfSHans Petter Selasky 	if (is_new) {
2159f00876fbSMark Johnston 		in_pcbref(inp);
2160f00876fbSMark Johnston 		INP_WUNLOCK(inp);
21615df91cbeSGleb Smirnoff 
2162d10910e6SBruce M Simpson 		error = in_joingroup_locked(ifp, &gsa->sin.sin_addr, imf,
216359854ecfSHans Petter Selasky 		    &imf->imf_inm);
21645df91cbeSGleb Smirnoff 
216559854ecfSHans Petter Selasky 		INP_WLOCK(inp);
216659854ecfSHans Petter Selasky 		if (in_pcbrele_wlocked(inp)) {
216759854ecfSHans Petter Selasky 			error = ENXIO;
216859854ecfSHans Petter Selasky 			goto out_inp_unlocked;
216959854ecfSHans Petter Selasky 		}
2170d9e1bc4fSGeorge V. Neville-Neil 		if (error) {
2171d9e1bc4fSGeorge V. Neville-Neil                         CTR1(KTR_IGMPV3, "%s: in_joingroup_locked failed",
2172d9e1bc4fSGeorge V. Neville-Neil                             __func__);
217359854ecfSHans Petter Selasky 			goto out_inp_locked;
2174d9e1bc4fSGeorge V. Neville-Neil 		}
21755b64b824SHans Petter Selasky 		/*
21765b64b824SHans Petter Selasky 		 * NOTE: Refcount from in_joingroup_locked()
21775b64b824SHans Petter Selasky 		 * is protecting membership.
21785b64b824SHans Petter Selasky 		 */
21795b64b824SHans Petter Selasky 		ip_mfilter_insert(&imo->imo_head, imf);
2180d10910e6SBruce M Simpson 	} else {
2181d10910e6SBruce M Simpson 		CTR1(KTR_IGMPV3, "%s: merge inm state", __func__);
2182f3e1324bSStephen Hurd 		IN_MULTI_LIST_LOCK();
2183d10910e6SBruce M Simpson 		error = inm_merge(inm, imf);
218471498f30SBruce M Simpson 		if (error) {
2185d10910e6SBruce M Simpson 			CTR1(KTR_IGMPV3, "%s: failed to merge inm state",
2186d10910e6SBruce M Simpson 				 __func__);
2187f3e1324bSStephen Hurd 			IN_MULTI_LIST_UNLOCK();
218859854ecfSHans Petter Selasky 			imf_rollback(imf);
218959854ecfSHans Petter Selasky 			imf_reap(imf);
219059854ecfSHans Petter Selasky 			goto out_inp_locked;
2191d10910e6SBruce M Simpson 		}
2192d10910e6SBruce M Simpson 		CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__);
2193d10910e6SBruce M Simpson 		error = igmp_change_state(inm);
2194f3e1324bSStephen Hurd 		IN_MULTI_LIST_UNLOCK();
2195d10910e6SBruce M Simpson 		if (error) {
2196d10910e6SBruce M Simpson 			CTR1(KTR_IGMPV3, "%s: failed igmp downcall",
2197d10910e6SBruce M Simpson 			    __func__);
2198d10910e6SBruce M Simpson 			imf_rollback(imf);
2199d10910e6SBruce M Simpson 			imf_reap(imf);
220059854ecfSHans Petter Selasky 			goto out_inp_locked;
2201d10910e6SBruce M Simpson 		}
220259854ecfSHans Petter Selasky 	}
2203d10910e6SBruce M Simpson 
220459854ecfSHans Petter Selasky 	imf_commit(imf);
220559854ecfSHans Petter Selasky 	imf = NULL;
2206d10910e6SBruce M Simpson 
2207d10910e6SBruce M Simpson out_inp_locked:
22088501a69cSRobert Watson 	INP_WUNLOCK(inp);
220959854ecfSHans Petter Selasky out_inp_unlocked:
221059854ecfSHans Petter Selasky 	IN_MULTI_UNLOCK();
221159854ecfSHans Petter Selasky 
221259854ecfSHans Petter Selasky 	if (is_new && imf) {
221359854ecfSHans Petter Selasky 		if (imf->imf_inm != NULL) {
221459854ecfSHans Petter Selasky 			IN_MULTI_LIST_LOCK();
2215200f3ac6SRyan Libby 			IF_ADDR_WLOCK(ifp);
221659854ecfSHans Petter Selasky 			inm_release_deferred(imf->imf_inm);
2217200f3ac6SRyan Libby 			IF_ADDR_WUNLOCK(ifp);
221859854ecfSHans Petter Selasky 			IN_MULTI_LIST_UNLOCK();
221959854ecfSHans Petter Selasky 		}
222059854ecfSHans Petter Selasky 		ip_mfilter_free(imf);
222159854ecfSHans Petter Selasky 	}
2222337418adSAlexander V. Chernikov 	if_rele(ifp);
222371498f30SBruce M Simpson 	return (error);
222471498f30SBruce M Simpson }
222571498f30SBruce M Simpson 
222671498f30SBruce M Simpson /*
222771498f30SBruce M Simpson  * Leave an IPv4 multicast group on an inpcb, possibly with a source.
222871498f30SBruce M Simpson  */
222971498f30SBruce M Simpson static int
inp_leave_group(struct inpcb * inp,struct sockopt * sopt)223071498f30SBruce M Simpson inp_leave_group(struct inpcb *inp, struct sockopt *sopt)
223171498f30SBruce M Simpson {
2232d74b7baeSGleb Smirnoff 	struct epoch_tracker		 et;
223371498f30SBruce M Simpson 	struct group_source_req		 gsr;
223471498f30SBruce M Simpson 	struct ip_mreq_source		 mreqs;
223571498f30SBruce M Simpson 	sockunion_t			*gsa, *ssa;
223671498f30SBruce M Simpson 	struct ifnet			*ifp;
223771498f30SBruce M Simpson 	struct in_mfilter		*imf;
223871498f30SBruce M Simpson 	struct ip_moptions		*imo;
2239d10910e6SBruce M Simpson 	struct in_msource		*ims;
224071498f30SBruce M Simpson 	struct in_multi			*inm;
224159854ecfSHans Petter Selasky 	int				 error;
224259854ecfSHans Petter Selasky 	bool				 is_final;
224371498f30SBruce M Simpson 
224471498f30SBruce M Simpson 	ifp = NULL;
224571498f30SBruce M Simpson 	error = 0;
224659854ecfSHans Petter Selasky 	is_final = true;
224771498f30SBruce M Simpson 
224871498f30SBruce M Simpson 	memset(&gsr, 0, sizeof(struct group_source_req));
224971498f30SBruce M Simpson 	gsa = (sockunion_t *)&gsr.gsr_group;
225071498f30SBruce M Simpson 	gsa->ss.ss_family = AF_UNSPEC;
225171498f30SBruce M Simpson 	ssa = (sockunion_t *)&gsr.gsr_source;
225271498f30SBruce M Simpson 	ssa->ss.ss_family = AF_UNSPEC;
225371498f30SBruce M Simpson 
225471498f30SBruce M Simpson 	switch (sopt->sopt_name) {
225571498f30SBruce M Simpson 	case IP_DROP_MEMBERSHIP:
225671498f30SBruce M Simpson 	case IP_DROP_SOURCE_MEMBERSHIP:
225771498f30SBruce M Simpson 		if (sopt->sopt_name == IP_DROP_MEMBERSHIP) {
225871498f30SBruce M Simpson 			error = sooptcopyin(sopt, &mreqs,
225971498f30SBruce M Simpson 			    sizeof(struct ip_mreq),
226071498f30SBruce M Simpson 			    sizeof(struct ip_mreq));
226171498f30SBruce M Simpson 			/*
226271498f30SBruce M Simpson 			 * Swap interface and sourceaddr arguments,
226371498f30SBruce M Simpson 			 * as ip_mreq and ip_mreq_source are laid
226471498f30SBruce M Simpson 			 * out differently.
226571498f30SBruce M Simpson 			 */
226671498f30SBruce M Simpson 			mreqs.imr_interface = mreqs.imr_sourceaddr;
226771498f30SBruce M Simpson 			mreqs.imr_sourceaddr.s_addr = INADDR_ANY;
226871498f30SBruce M Simpson 		} else if (sopt->sopt_name == IP_DROP_SOURCE_MEMBERSHIP) {
226971498f30SBruce M Simpson 			error = sooptcopyin(sopt, &mreqs,
227071498f30SBruce M Simpson 			    sizeof(struct ip_mreq_source),
227171498f30SBruce M Simpson 			    sizeof(struct ip_mreq_source));
227271498f30SBruce M Simpson 		}
227371498f30SBruce M Simpson 		if (error)
227471498f30SBruce M Simpson 			return (error);
227571498f30SBruce M Simpson 
227671498f30SBruce M Simpson 		gsa->sin.sin_family = AF_INET;
227771498f30SBruce M Simpson 		gsa->sin.sin_len = sizeof(struct sockaddr_in);
227871498f30SBruce M Simpson 		gsa->sin.sin_addr = mreqs.imr_multiaddr;
227971498f30SBruce M Simpson 
228071498f30SBruce M Simpson 		if (sopt->sopt_name == IP_DROP_SOURCE_MEMBERSHIP) {
228171498f30SBruce M Simpson 			ssa->sin.sin_family = AF_INET;
228271498f30SBruce M Simpson 			ssa->sin.sin_len = sizeof(struct sockaddr_in);
228371498f30SBruce M Simpson 			ssa->sin.sin_addr = mreqs.imr_sourceaddr;
228471498f30SBruce M Simpson 		}
228571498f30SBruce M Simpson 
2286933fc4ddSBruce M Simpson 		/*
2287933fc4ddSBruce M Simpson 		 * Attempt to look up hinted ifp from interface address.
2288933fc4ddSBruce M Simpson 		 * Fallthrough with null ifp iff lookup fails, to
2289933fc4ddSBruce M Simpson 		 * preserve 4.4BSD mcast API idempotence.
2290933fc4ddSBruce M Simpson 		 * XXX NOTE WELL: The RFC 3678 API is preferred because
2291933fc4ddSBruce M Simpson 		 * using an IPv4 address as a key is racy.
2292933fc4ddSBruce M Simpson 		 */
22934f1e3122SEugene Grosbein 		if (!in_nullhost(mreqs.imr_interface)) {
2294c8ee75f2SGleb Smirnoff 			NET_EPOCH_ENTER(et);
229571498f30SBruce M Simpson 			INADDR_TO_IFP(mreqs.imr_interface, ifp);
2296c8ee75f2SGleb Smirnoff 			/* XXXGL ifref? */
2297c8ee75f2SGleb Smirnoff 			NET_EPOCH_EXIT(et);
22984f1e3122SEugene Grosbein 		}
229947d803eaSEric van Gyzen 		CTR3(KTR_IGMPV3, "%s: imr_interface = 0x%08x, ifp = %p",
230040769242SEric van Gyzen 		    __func__, ntohl(mreqs.imr_interface.s_addr), ifp);
2301d10910e6SBruce M Simpson 
230271498f30SBruce M Simpson 		break;
230371498f30SBruce M Simpson 
230471498f30SBruce M Simpson 	case MCAST_LEAVE_GROUP:
230571498f30SBruce M Simpson 	case MCAST_LEAVE_SOURCE_GROUP:
230671498f30SBruce M Simpson 		if (sopt->sopt_name == MCAST_LEAVE_GROUP) {
230771498f30SBruce M Simpson 			error = sooptcopyin(sopt, &gsr,
230871498f30SBruce M Simpson 			    sizeof(struct group_req),
230971498f30SBruce M Simpson 			    sizeof(struct group_req));
231071498f30SBruce M Simpson 		} else if (sopt->sopt_name == MCAST_LEAVE_SOURCE_GROUP) {
231171498f30SBruce M Simpson 			error = sooptcopyin(sopt, &gsr,
231271498f30SBruce M Simpson 			    sizeof(struct group_source_req),
231371498f30SBruce M Simpson 			    sizeof(struct group_source_req));
231471498f30SBruce M Simpson 		}
231571498f30SBruce M Simpson 		if (error)
231671498f30SBruce M Simpson 			return (error);
231771498f30SBruce M Simpson 
231871498f30SBruce M Simpson 		if (gsa->sin.sin_family != AF_INET ||
231971498f30SBruce M Simpson 		    gsa->sin.sin_len != sizeof(struct sockaddr_in))
232071498f30SBruce M Simpson 			return (EINVAL);
232171498f30SBruce M Simpson 
232271498f30SBruce M Simpson 		if (sopt->sopt_name == MCAST_LEAVE_SOURCE_GROUP) {
232371498f30SBruce M Simpson 			if (ssa->sin.sin_family != AF_INET ||
232471498f30SBruce M Simpson 			    ssa->sin.sin_len != sizeof(struct sockaddr_in))
232571498f30SBruce M Simpson 				return (EINVAL);
232671498f30SBruce M Simpson 		}
232771498f30SBruce M Simpson 
2328d74b7baeSGleb Smirnoff 		NET_EPOCH_ENTER(et);
232971498f30SBruce M Simpson 		ifp = ifnet_byindex(gsr.gsr_interface);
2330d74b7baeSGleb Smirnoff 		NET_EPOCH_EXIT(et);	/* XXXGL: unsafe ifp */
2331933fc4ddSBruce M Simpson 		if (ifp == NULL)
2332933fc4ddSBruce M Simpson 			return (EADDRNOTAVAIL);
233371498f30SBruce M Simpson 		break;
233471498f30SBruce M Simpson 
233571498f30SBruce M Simpson 	default:
2336d10910e6SBruce M Simpson 		CTR2(KTR_IGMPV3, "%s: unknown sopt_name %d",
2337d10910e6SBruce M Simpson 		    __func__, sopt->sopt_name);
233871498f30SBruce M Simpson 		return (EOPNOTSUPP);
233971498f30SBruce M Simpson 		break;
234071498f30SBruce M Simpson 	}
234171498f30SBruce M Simpson 
234271498f30SBruce M Simpson 	if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr)))
234371498f30SBruce M Simpson 		return (EINVAL);
234471498f30SBruce M Simpson 
234559854ecfSHans Petter Selasky 	IN_MULTI_LOCK();
234659854ecfSHans Petter Selasky 
234771498f30SBruce M Simpson 	/*
234859854ecfSHans Petter Selasky 	 * Find the membership in the membership list.
234971498f30SBruce M Simpson 	 */
235071498f30SBruce M Simpson 	imo = inp_findmoptions(inp);
235159854ecfSHans Petter Selasky 	imf = imo_match_group(imo, ifp, &gsa->sa);
235259854ecfSHans Petter Selasky 	if (imf == NULL) {
235371498f30SBruce M Simpson 		error = EADDRNOTAVAIL;
2354d10910e6SBruce M Simpson 		goto out_inp_locked;
235571498f30SBruce M Simpson 	}
235659854ecfSHans Petter Selasky 	inm = imf->imf_inm;
235771498f30SBruce M Simpson 
2358d10910e6SBruce M Simpson 	if (ssa->ss.ss_family != AF_UNSPEC)
235959854ecfSHans Petter Selasky 		is_final = false;
2360d10910e6SBruce M Simpson 
2361d10910e6SBruce M Simpson 	/*
2362d10910e6SBruce M Simpson 	 * Begin state merge transaction at socket layer.
2363d10910e6SBruce M Simpson 	 */
2364d10910e6SBruce M Simpson 	INP_WLOCK_ASSERT(inp);
2365d10910e6SBruce M Simpson 
236671498f30SBruce M Simpson 	/*
236771498f30SBruce M Simpson 	 * If we were instructed only to leave a given source, do so.
2368d10910e6SBruce M Simpson 	 * MCAST_LEAVE_SOURCE_GROUP is only valid for inclusive memberships.
236971498f30SBruce M Simpson 	 */
2370d10910e6SBruce M Simpson 	if (is_final) {
237159854ecfSHans Petter Selasky 		ip_mfilter_remove(&imo->imo_head, imf);
2372d10910e6SBruce M Simpson 		imf_leave(imf);
2373a4c5668dSHans Petter Selasky 
2374a4c5668dSHans Petter Selasky 		/*
2375a4c5668dSHans Petter Selasky 		 * Give up the multicast address record to which
2376a4c5668dSHans Petter Selasky 		 * the membership points.
2377a4c5668dSHans Petter Selasky 		 */
2378a4c5668dSHans Petter Selasky 		(void) in_leavegroup_locked(imf->imf_inm, imf);
237971498f30SBruce M Simpson 	} else {
2380d10910e6SBruce M Simpson 		if (imf->imf_st[0] == MCAST_EXCLUDE) {
2381d10910e6SBruce M Simpson 			error = EADDRNOTAVAIL;
2382d10910e6SBruce M Simpson 			goto out_inp_locked;
238371498f30SBruce M Simpson 		}
238459854ecfSHans Petter Selasky 		ims = imo_match_source(imf, &ssa->sa);
2385d10910e6SBruce M Simpson 		if (ims == NULL) {
238647d803eaSEric van Gyzen 			CTR3(KTR_IGMPV3, "%s: source 0x%08x %spresent",
238740769242SEric van Gyzen 			    __func__, ntohl(ssa->sin.sin_addr.s_addr), "not ");
2388d10910e6SBruce M Simpson 			error = EADDRNOTAVAIL;
2389d10910e6SBruce M Simpson 			goto out_inp_locked;
2390d10910e6SBruce M Simpson 		}
2391d10910e6SBruce M Simpson 		CTR2(KTR_IGMPV3, "%s: %s source", __func__, "block");
2392d10910e6SBruce M Simpson 		error = imf_prune(imf, &ssa->sin);
2393d10910e6SBruce M Simpson 		if (error) {
2394d10910e6SBruce M Simpson 			CTR1(KTR_IGMPV3, "%s: merge imf state failed",
2395d10910e6SBruce M Simpson 			    __func__);
2396d10910e6SBruce M Simpson 			goto out_inp_locked;
2397d10910e6SBruce M Simpson 		}
239871498f30SBruce M Simpson 	}
239971498f30SBruce M Simpson 
240071498f30SBruce M Simpson 	/*
2401d10910e6SBruce M Simpson 	 * Begin state merge transaction at IGMP layer.
240271498f30SBruce M Simpson 	 */
240359854ecfSHans Petter Selasky 	if (!is_final) {
2404d10910e6SBruce M Simpson 		CTR1(KTR_IGMPV3, "%s: merge inm state", __func__);
2405f3e1324bSStephen Hurd 		IN_MULTI_LIST_LOCK();
2406d10910e6SBruce M Simpson 		error = inm_merge(inm, imf);
2407d10910e6SBruce M Simpson 		if (error) {
2408d10910e6SBruce M Simpson 			CTR1(KTR_IGMPV3, "%s: failed to merge inm state",
2409d10910e6SBruce M Simpson 			    __func__);
24105ba05d3dSHiroki Sato 			IN_MULTI_LIST_UNLOCK();
241159854ecfSHans Petter Selasky 			imf_rollback(imf);
241259854ecfSHans Petter Selasky 			imf_reap(imf);
241359854ecfSHans Petter Selasky 			goto out_inp_locked;
241471498f30SBruce M Simpson 		}
241571498f30SBruce M Simpson 
2416d10910e6SBruce M Simpson 		CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__);
2417d10910e6SBruce M Simpson 		error = igmp_change_state(inm);
2418f3e1324bSStephen Hurd 		IN_MULTI_LIST_UNLOCK();
2419d10910e6SBruce M Simpson 		if (error) {
2420d10910e6SBruce M Simpson 			CTR1(KTR_IGMPV3, "%s: failed igmp downcall",
2421d10910e6SBruce M Simpson 			    __func__);
2422d10910e6SBruce M Simpson 			imf_rollback(imf);
2423d10910e6SBruce M Simpson 			imf_reap(imf);
242459854ecfSHans Petter Selasky 			goto out_inp_locked;
2425cc5776b2SBruce M Simpson 		}
2426d10910e6SBruce M Simpson 	}
242759854ecfSHans Petter Selasky 	imf_commit(imf);
242859854ecfSHans Petter Selasky 	imf_reap(imf);
242971498f30SBruce M Simpson 
2430d10910e6SBruce M Simpson out_inp_locked:
24318501a69cSRobert Watson 	INP_WUNLOCK(inp);
243259854ecfSHans Petter Selasky 
2433a4c5668dSHans Petter Selasky 	if (is_final && imf)
243459854ecfSHans Petter Selasky 		ip_mfilter_free(imf);
243559854ecfSHans Petter Selasky 
243659854ecfSHans Petter Selasky 	IN_MULTI_UNLOCK();
243771498f30SBruce M Simpson 	return (error);
243871498f30SBruce M Simpson }
243971498f30SBruce M Simpson 
244071498f30SBruce M Simpson /*
244171498f30SBruce M Simpson  * Select the interface for transmitting IPv4 multicast datagrams.
244271498f30SBruce M Simpson  *
244371498f30SBruce M Simpson  * Either an instance of struct in_addr or an instance of struct ip_mreqn
244471498f30SBruce M Simpson  * may be passed to this socket option. An address of INADDR_ANY or an
244571498f30SBruce M Simpson  * interface index of 0 is used to remove a previous selection.
244671498f30SBruce M Simpson  * When no interface is selected, one is chosen for every send.
244771498f30SBruce M Simpson  */
244871498f30SBruce M Simpson static int
inp_set_multicast_if(struct inpcb * inp,struct sockopt * sopt)244971498f30SBruce M Simpson inp_set_multicast_if(struct inpcb *inp, struct sockopt *sopt)
245071498f30SBruce M Simpson {
245171498f30SBruce M Simpson 	struct in_addr		 addr;
245271498f30SBruce M Simpson 	struct ip_mreqn		 mreqn;
245371498f30SBruce M Simpson 	struct ifnet		*ifp;
245471498f30SBruce M Simpson 	struct ip_moptions	*imo;
245571498f30SBruce M Simpson 	int			 error;
245671498f30SBruce M Simpson 
245771498f30SBruce M Simpson 	if (sopt->sopt_valsize == sizeof(struct ip_mreqn)) {
245871498f30SBruce M Simpson 		/*
245971498f30SBruce M Simpson 		 * An interface index was specified using the
246071498f30SBruce M Simpson 		 * Linux-derived ip_mreqn structure.
246171498f30SBruce M Simpson 		 */
246271498f30SBruce M Simpson 		error = sooptcopyin(sopt, &mreqn, sizeof(struct ip_mreqn),
246371498f30SBruce M Simpson 		    sizeof(struct ip_mreqn));
246471498f30SBruce M Simpson 		if (error)
246571498f30SBruce M Simpson 			return (error);
246671498f30SBruce M Simpson 
2467d74b7baeSGleb Smirnoff 		if (mreqn.imr_ifindex < 0)
246871498f30SBruce M Simpson 			return (EINVAL);
246971498f30SBruce M Simpson 
247071498f30SBruce M Simpson 		if (mreqn.imr_ifindex == 0) {
247171498f30SBruce M Simpson 			ifp = NULL;
247271498f30SBruce M Simpson 		} else {
2473d74b7baeSGleb Smirnoff 			struct epoch_tracker et;
2474d74b7baeSGleb Smirnoff 
2475d74b7baeSGleb Smirnoff 			NET_EPOCH_ENTER(et);
247671498f30SBruce M Simpson 			ifp = ifnet_byindex(mreqn.imr_ifindex);
2477d74b7baeSGleb Smirnoff 			NET_EPOCH_EXIT(et);	/* XXXGL: unsafe ifp */
247871498f30SBruce M Simpson 			if (ifp == NULL)
247971498f30SBruce M Simpson 				return (EADDRNOTAVAIL);
248071498f30SBruce M Simpson 		}
248171498f30SBruce M Simpson 	} else {
248271498f30SBruce M Simpson 		/*
248371498f30SBruce M Simpson 		 * An interface was specified by IPv4 address.
248471498f30SBruce M Simpson 		 * This is the traditional BSD usage.
248571498f30SBruce M Simpson 		 */
248671498f30SBruce M Simpson 		error = sooptcopyin(sopt, &addr, sizeof(struct in_addr),
248771498f30SBruce M Simpson 		    sizeof(struct in_addr));
248871498f30SBruce M Simpson 		if (error)
248971498f30SBruce M Simpson 			return (error);
2490d10910e6SBruce M Simpson 		if (in_nullhost(addr)) {
249171498f30SBruce M Simpson 			ifp = NULL;
249271498f30SBruce M Simpson 		} else {
2493c8ee75f2SGleb Smirnoff 			struct epoch_tracker et;
2494c8ee75f2SGleb Smirnoff 
2495c8ee75f2SGleb Smirnoff 			NET_EPOCH_ENTER(et);
249671498f30SBruce M Simpson 			INADDR_TO_IFP(addr, ifp);
2497c8ee75f2SGleb Smirnoff 			/* XXXGL ifref? */
2498c8ee75f2SGleb Smirnoff 			NET_EPOCH_EXIT(et);
249971498f30SBruce M Simpson 			if (ifp == NULL)
250071498f30SBruce M Simpson 				return (EADDRNOTAVAIL);
250171498f30SBruce M Simpson 		}
250247d803eaSEric van Gyzen 		CTR3(KTR_IGMPV3, "%s: ifp = %p, addr = 0x%08x", __func__, ifp,
250340769242SEric van Gyzen 		    ntohl(addr.s_addr));
250471498f30SBruce M Simpson 	}
250571498f30SBruce M Simpson 
250671498f30SBruce M Simpson 	/* Reject interfaces which do not support multicast. */
250771498f30SBruce M Simpson 	if (ifp != NULL && (ifp->if_flags & IFF_MULTICAST) == 0)
250871498f30SBruce M Simpson 		return (EOPNOTSUPP);
250971498f30SBruce M Simpson 
251071498f30SBruce M Simpson 	imo = inp_findmoptions(inp);
251171498f30SBruce M Simpson 	imo->imo_multicast_ifp = ifp;
251271498f30SBruce M Simpson 	imo->imo_multicast_addr.s_addr = INADDR_ANY;
25138501a69cSRobert Watson 	INP_WUNLOCK(inp);
251471498f30SBruce M Simpson 
251571498f30SBruce M Simpson 	return (0);
251671498f30SBruce M Simpson }
251771498f30SBruce M Simpson 
251871498f30SBruce M Simpson /*
251971498f30SBruce M Simpson  * Atomically set source filters on a socket for an IPv4 multicast group.
252071498f30SBruce M Simpson  */
252171498f30SBruce M Simpson static int
inp_set_source_filters(struct inpcb * inp,struct sockopt * sopt)252271498f30SBruce M Simpson inp_set_source_filters(struct inpcb *inp, struct sockopt *sopt)
252371498f30SBruce M Simpson {
2524d74b7baeSGleb Smirnoff 	struct epoch_tracker	 et;
252571498f30SBruce M Simpson 	struct __msfilterreq	 msfr;
252671498f30SBruce M Simpson 	sockunion_t		*gsa;
252771498f30SBruce M Simpson 	struct ifnet		*ifp;
252871498f30SBruce M Simpson 	struct in_mfilter	*imf;
252971498f30SBruce M Simpson 	struct ip_moptions	*imo;
2530d10910e6SBruce M Simpson 	struct in_multi		*inm;
253171498f30SBruce M Simpson 	int			 error;
253271498f30SBruce M Simpson 
253371498f30SBruce M Simpson 	error = sooptcopyin(sopt, &msfr, sizeof(struct __msfilterreq),
253471498f30SBruce M Simpson 	    sizeof(struct __msfilterreq));
253571498f30SBruce M Simpson 	if (error)
253671498f30SBruce M Simpson 		return (error);
253771498f30SBruce M Simpson 
253899bf30cfSBruce M Simpson 	if (msfr.msfr_nsrcs > in_mcast_maxsocksrc)
253999bf30cfSBruce M Simpson 		return (ENOBUFS);
254099bf30cfSBruce M Simpson 
254199bf30cfSBruce M Simpson 	if ((msfr.msfr_fmode != MCAST_EXCLUDE &&
254271498f30SBruce M Simpson 	     msfr.msfr_fmode != MCAST_INCLUDE))
254371498f30SBruce M Simpson 		return (EINVAL);
254471498f30SBruce M Simpson 
254571498f30SBruce M Simpson 	if (msfr.msfr_group.ss_family != AF_INET ||
254671498f30SBruce M Simpson 	    msfr.msfr_group.ss_len != sizeof(struct sockaddr_in))
254771498f30SBruce M Simpson 		return (EINVAL);
254871498f30SBruce M Simpson 
254971498f30SBruce M Simpson 	gsa = (sockunion_t *)&msfr.msfr_group;
255071498f30SBruce M Simpson 	if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr)))
255171498f30SBruce M Simpson 		return (EINVAL);
255271498f30SBruce M Simpson 
255371498f30SBruce M Simpson 	gsa->sin.sin_port = 0;	/* ignore port */
255471498f30SBruce M Simpson 
2555d74b7baeSGleb Smirnoff 	NET_EPOCH_ENTER(et);
255671498f30SBruce M Simpson 	ifp = ifnet_byindex(msfr.msfr_ifindex);
2557d74b7baeSGleb Smirnoff 	NET_EPOCH_EXIT(et);	/* XXXGL: unsafe ifp */
255871498f30SBruce M Simpson 	if (ifp == NULL)
255971498f30SBruce M Simpson 		return (EADDRNOTAVAIL);
256071498f30SBruce M Simpson 
256159854ecfSHans Petter Selasky 	IN_MULTI_LOCK();
256259854ecfSHans Petter Selasky 
256371498f30SBruce M Simpson 	/*
2564d10910e6SBruce M Simpson 	 * Take the INP write lock.
256571498f30SBruce M Simpson 	 * Check if this socket is a member of this group.
256671498f30SBruce M Simpson 	 */
256771498f30SBruce M Simpson 	imo = inp_findmoptions(inp);
256859854ecfSHans Petter Selasky 	imf = imo_match_group(imo, ifp, &gsa->sa);
256959854ecfSHans Petter Selasky 	if (imf == NULL) {
257071498f30SBruce M Simpson 		error = EADDRNOTAVAIL;
2571d10910e6SBruce M Simpson 		goto out_inp_locked;
257271498f30SBruce M Simpson 	}
257359854ecfSHans Petter Selasky 	inm = imf->imf_inm;
257471498f30SBruce M Simpson 
257571498f30SBruce M Simpson 	/*
2576d10910e6SBruce M Simpson 	 * Begin state merge transaction at socket layer.
257771498f30SBruce M Simpson 	 */
2578d10910e6SBruce M Simpson 	INP_WLOCK_ASSERT(inp);
2579d10910e6SBruce M Simpson 
2580d10910e6SBruce M Simpson 	imf->imf_st[1] = msfr.msfr_fmode;
258171498f30SBruce M Simpson 
258271498f30SBruce M Simpson 	/*
258371498f30SBruce M Simpson 	 * Apply any new source filters, if present.
258471498f30SBruce M Simpson 	 * Make a copy of the user-space source vector so
258571498f30SBruce M Simpson 	 * that we may copy them with a single copyin. This
258671498f30SBruce M Simpson 	 * allows us to deal with page faults up-front.
258771498f30SBruce M Simpson 	 */
2588d10910e6SBruce M Simpson 	if (msfr.msfr_nsrcs > 0) {
2589d10910e6SBruce M Simpson 		struct in_msource	*lims;
2590d10910e6SBruce M Simpson 		struct sockaddr_in	*psin;
2591d10910e6SBruce M Simpson 		struct sockaddr_storage	*kss, *pkss;
2592d10910e6SBruce M Simpson 		int			 i;
2593d10910e6SBruce M Simpson 
2594d10910e6SBruce M Simpson 		INP_WUNLOCK(inp);
2595d10910e6SBruce M Simpson 
2596d10910e6SBruce M Simpson 		CTR2(KTR_IGMPV3, "%s: loading %lu source list entries",
2597d10910e6SBruce M Simpson 		    __func__, (unsigned long)msfr.msfr_nsrcs);
25981ede983cSDag-Erling Smørgrav 		kss = malloc(sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs,
259971498f30SBruce M Simpson 		    M_TEMP, M_WAITOK);
260071498f30SBruce M Simpson 		error = copyin(msfr.msfr_srcs, kss,
260171498f30SBruce M Simpson 		    sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs);
260271498f30SBruce M Simpson 		if (error) {
26031ede983cSDag-Erling Smørgrav 			free(kss, M_TEMP);
260471498f30SBruce M Simpson 			return (error);
260571498f30SBruce M Simpson 		}
260671498f30SBruce M Simpson 
2607d10910e6SBruce M Simpson 		INP_WLOCK(inp);
2608d10910e6SBruce M Simpson 
260971498f30SBruce M Simpson 		/*
2610d10910e6SBruce M Simpson 		 * Mark all source filters as UNDEFINED at t1.
2611d10910e6SBruce M Simpson 		 * Restore new group filter mode, as imf_leave()
2612d10910e6SBruce M Simpson 		 * will set it to INCLUDE.
261371498f30SBruce M Simpson 		 */
2614d10910e6SBruce M Simpson 		imf_leave(imf);
2615d10910e6SBruce M Simpson 		imf->imf_st[1] = msfr.msfr_fmode;
2616d10910e6SBruce M Simpson 
2617d10910e6SBruce M Simpson 		/*
2618d10910e6SBruce M Simpson 		 * Update socket layer filters at t1, lazy-allocating
2619d10910e6SBruce M Simpson 		 * new entries. This saves a bunch of memory at the
2620d10910e6SBruce M Simpson 		 * cost of one RB_FIND() per source entry; duplicate
2621d10910e6SBruce M Simpson 		 * entries in the msfr_nsrcs vector are ignored.
2622d10910e6SBruce M Simpson 		 * If we encounter an error, rollback transaction.
2623d10910e6SBruce M Simpson 		 *
2624d10910e6SBruce M Simpson 		 * XXX This too could be replaced with a set-symmetric
2625d10910e6SBruce M Simpson 		 * difference like loop to avoid walking from root
2626d10910e6SBruce M Simpson 		 * every time, as the key space is common.
2627d10910e6SBruce M Simpson 		 */
2628d10910e6SBruce M Simpson 		for (i = 0, pkss = kss; i < msfr.msfr_nsrcs; i++, pkss++) {
2629d10910e6SBruce M Simpson 			psin = (struct sockaddr_in *)pkss;
2630d10910e6SBruce M Simpson 			if (psin->sin_family != AF_INET) {
263171498f30SBruce M Simpson 				error = EAFNOSUPPORT;
263271498f30SBruce M Simpson 				break;
263371498f30SBruce M Simpson 			}
2634d10910e6SBruce M Simpson 			if (psin->sin_len != sizeof(struct sockaddr_in)) {
2635d10910e6SBruce M Simpson 				error = EINVAL;
263671498f30SBruce M Simpson 				break;
263771498f30SBruce M Simpson 			}
2638d10910e6SBruce M Simpson 			error = imf_get_source(imf, psin, &lims);
2639d10910e6SBruce M Simpson 			if (error)
2640d10910e6SBruce M Simpson 				break;
2641d10910e6SBruce M Simpson 			lims->imsl_st[1] = imf->imf_st[1];
264271498f30SBruce M Simpson 		}
26431ede983cSDag-Erling Smørgrav 		free(kss, M_TEMP);
264471498f30SBruce M Simpson 	}
264571498f30SBruce M Simpson 
2646d10910e6SBruce M Simpson 	if (error)
2647d10910e6SBruce M Simpson 		goto out_imf_rollback;
2648d10910e6SBruce M Simpson 
26498501a69cSRobert Watson 	INP_WLOCK_ASSERT(inp);
265071498f30SBruce M Simpson 
2651d10910e6SBruce M Simpson 	/*
2652d10910e6SBruce M Simpson 	 * Begin state merge transaction at IGMP layer.
2653d10910e6SBruce M Simpson 	 */
2654d10910e6SBruce M Simpson 	CTR1(KTR_IGMPV3, "%s: merge inm state", __func__);
26555ba05d3dSHiroki Sato 	IN_MULTI_LIST_LOCK();
2656d10910e6SBruce M Simpson 	error = inm_merge(inm, imf);
2657d10910e6SBruce M Simpson 	if (error) {
2658d10910e6SBruce M Simpson 		CTR1(KTR_IGMPV3, "%s: failed to merge inm state", __func__);
2659f3e1324bSStephen Hurd 		IN_MULTI_LIST_UNLOCK();
266059854ecfSHans Petter Selasky 		goto out_imf_rollback;
2661d10910e6SBruce M Simpson 	}
2662d10910e6SBruce M Simpson 
2663d10910e6SBruce M Simpson 	CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__);
2664d10910e6SBruce M Simpson 	error = igmp_change_state(inm);
2665f3e1324bSStephen Hurd 	IN_MULTI_LIST_UNLOCK();
2666d10910e6SBruce M Simpson 	if (error)
2667d10910e6SBruce M Simpson 		CTR1(KTR_IGMPV3, "%s: failed igmp downcall", __func__);
2668d10910e6SBruce M Simpson 
2669d10910e6SBruce M Simpson out_imf_rollback:
2670d10910e6SBruce M Simpson 	if (error)
2671d10910e6SBruce M Simpson 		imf_rollback(imf);
2672d10910e6SBruce M Simpson 	else
2673d10910e6SBruce M Simpson 		imf_commit(imf);
2674d10910e6SBruce M Simpson 
2675d10910e6SBruce M Simpson 	imf_reap(imf);
2676d10910e6SBruce M Simpson 
2677d10910e6SBruce M Simpson out_inp_locked:
26788501a69cSRobert Watson 	INP_WUNLOCK(inp);
267959854ecfSHans Petter Selasky 	IN_MULTI_UNLOCK();
268071498f30SBruce M Simpson 	return (error);
268171498f30SBruce M Simpson }
268271498f30SBruce M Simpson 
268371498f30SBruce M Simpson /*
268471498f30SBruce M Simpson  * Set the IP multicast options in response to user setsockopt().
268571498f30SBruce M Simpson  *
268671498f30SBruce M Simpson  * Many of the socket options handled in this function duplicate the
268771498f30SBruce M Simpson  * functionality of socket options in the regular unicast API. However,
268871498f30SBruce M Simpson  * it is not possible to merge the duplicate code, because the idempotence
268971498f30SBruce M Simpson  * of the IPv4 multicast part of the BSD Sockets API must be preserved;
269071498f30SBruce M Simpson  * the effects of these options must be treated as separate and distinct.
2691d10910e6SBruce M Simpson  *
2692d10910e6SBruce M Simpson  * SMPng: XXX: Unlocked read of inp_socket believed OK.
2693d10910e6SBruce M Simpson  * FUTURE: The IP_MULTICAST_VIF option may be eliminated if MROUTING
2694d10910e6SBruce M Simpson  * is refactored to no longer use vifs.
269571498f30SBruce M Simpson  */
269671498f30SBruce M Simpson int
inp_setmoptions(struct inpcb * inp,struct sockopt * sopt)269771498f30SBruce M Simpson inp_setmoptions(struct inpcb *inp, struct sockopt *sopt)
269871498f30SBruce M Simpson {
269971498f30SBruce M Simpson 	struct ip_moptions	*imo;
270071498f30SBruce M Simpson 	int			 error;
270171498f30SBruce M Simpson 
270271498f30SBruce M Simpson 	error = 0;
270371498f30SBruce M Simpson 
27048624f434SGleb Smirnoff 	/* If socket is neither of type SOCK_RAW or SOCK_DGRAM, reject it. */
27058624f434SGleb Smirnoff 	if (inp->inp_socket->so_proto->pr_type != SOCK_RAW &&
27068624f434SGleb Smirnoff 	     inp->inp_socket->so_proto->pr_type != SOCK_DGRAM)
2707b244c8adSChristian S.J. Peron 		return (EOPNOTSUPP);
2708b244c8adSChristian S.J. Peron 
270971498f30SBruce M Simpson 	switch (sopt->sopt_name) {
271071498f30SBruce M Simpson 	case IP_MULTICAST_VIF: {
271171498f30SBruce M Simpson 		int vifi;
271271498f30SBruce M Simpson 		/*
271371498f30SBruce M Simpson 		 * Select a multicast VIF for transmission.
271471498f30SBruce M Simpson 		 * Only useful if multicast forwarding is active.
271571498f30SBruce M Simpson 		 */
271671498f30SBruce M Simpson 		if (legal_vif_num == NULL) {
271771498f30SBruce M Simpson 			error = EOPNOTSUPP;
271871498f30SBruce M Simpson 			break;
271971498f30SBruce M Simpson 		}
272071498f30SBruce M Simpson 		error = sooptcopyin(sopt, &vifi, sizeof(int), sizeof(int));
272171498f30SBruce M Simpson 		if (error)
272271498f30SBruce M Simpson 			break;
272371498f30SBruce M Simpson 		if (!legal_vif_num(vifi) && (vifi != -1)) {
272471498f30SBruce M Simpson 			error = EINVAL;
272571498f30SBruce M Simpson 			break;
272671498f30SBruce M Simpson 		}
272771498f30SBruce M Simpson 		imo = inp_findmoptions(inp);
272871498f30SBruce M Simpson 		imo->imo_multicast_vif = vifi;
27298501a69cSRobert Watson 		INP_WUNLOCK(inp);
273071498f30SBruce M Simpson 		break;
273171498f30SBruce M Simpson 	}
273271498f30SBruce M Simpson 
273371498f30SBruce M Simpson 	case IP_MULTICAST_IF:
273471498f30SBruce M Simpson 		error = inp_set_multicast_if(inp, sopt);
273571498f30SBruce M Simpson 		break;
273671498f30SBruce M Simpson 
273771498f30SBruce M Simpson 	case IP_MULTICAST_TTL: {
273871498f30SBruce M Simpson 		u_char ttl;
273971498f30SBruce M Simpson 
274071498f30SBruce M Simpson 		/*
274171498f30SBruce M Simpson 		 * Set the IP time-to-live for outgoing multicast packets.
274271498f30SBruce M Simpson 		 * The original multicast API required a char argument,
274371498f30SBruce M Simpson 		 * which is inconsistent with the rest of the socket API.
274471498f30SBruce M Simpson 		 * We allow either a char or an int.
274571498f30SBruce M Simpson 		 */
274671498f30SBruce M Simpson 		if (sopt->sopt_valsize == sizeof(u_char)) {
274771498f30SBruce M Simpson 			error = sooptcopyin(sopt, &ttl, sizeof(u_char),
274871498f30SBruce M Simpson 			    sizeof(u_char));
274971498f30SBruce M Simpson 			if (error)
275071498f30SBruce M Simpson 				break;
275171498f30SBruce M Simpson 		} else {
275271498f30SBruce M Simpson 			u_int ittl;
275371498f30SBruce M Simpson 
275471498f30SBruce M Simpson 			error = sooptcopyin(sopt, &ittl, sizeof(u_int),
275571498f30SBruce M Simpson 			    sizeof(u_int));
275671498f30SBruce M Simpson 			if (error)
275771498f30SBruce M Simpson 				break;
275871498f30SBruce M Simpson 			if (ittl > 255) {
275971498f30SBruce M Simpson 				error = EINVAL;
276071498f30SBruce M Simpson 				break;
276171498f30SBruce M Simpson 			}
276271498f30SBruce M Simpson 			ttl = (u_char)ittl;
276371498f30SBruce M Simpson 		}
276471498f30SBruce M Simpson 		imo = inp_findmoptions(inp);
276571498f30SBruce M Simpson 		imo->imo_multicast_ttl = ttl;
27668501a69cSRobert Watson 		INP_WUNLOCK(inp);
276771498f30SBruce M Simpson 		break;
276871498f30SBruce M Simpson 	}
276971498f30SBruce M Simpson 
277071498f30SBruce M Simpson 	case IP_MULTICAST_LOOP: {
277171498f30SBruce M Simpson 		u_char loop;
277271498f30SBruce M Simpson 
277371498f30SBruce M Simpson 		/*
277471498f30SBruce M Simpson 		 * Set the loopback flag for outgoing multicast packets.
277571498f30SBruce M Simpson 		 * Must be zero or one.  The original multicast API required a
277671498f30SBruce M Simpson 		 * char argument, which is inconsistent with the rest
277771498f30SBruce M Simpson 		 * of the socket API.  We allow either a char or an int.
277871498f30SBruce M Simpson 		 */
277971498f30SBruce M Simpson 		if (sopt->sopt_valsize == sizeof(u_char)) {
278071498f30SBruce M Simpson 			error = sooptcopyin(sopt, &loop, sizeof(u_char),
278171498f30SBruce M Simpson 			    sizeof(u_char));
278271498f30SBruce M Simpson 			if (error)
278371498f30SBruce M Simpson 				break;
278471498f30SBruce M Simpson 		} else {
278571498f30SBruce M Simpson 			u_int iloop;
278671498f30SBruce M Simpson 
278771498f30SBruce M Simpson 			error = sooptcopyin(sopt, &iloop, sizeof(u_int),
278871498f30SBruce M Simpson 					    sizeof(u_int));
278971498f30SBruce M Simpson 			if (error)
279071498f30SBruce M Simpson 				break;
279171498f30SBruce M Simpson 			loop = (u_char)iloop;
279271498f30SBruce M Simpson 		}
279371498f30SBruce M Simpson 		imo = inp_findmoptions(inp);
279471498f30SBruce M Simpson 		imo->imo_multicast_loop = !!loop;
27958501a69cSRobert Watson 		INP_WUNLOCK(inp);
279671498f30SBruce M Simpson 		break;
279771498f30SBruce M Simpson 	}
279871498f30SBruce M Simpson 
279971498f30SBruce M Simpson 	case IP_ADD_MEMBERSHIP:
280071498f30SBruce M Simpson 	case IP_ADD_SOURCE_MEMBERSHIP:
280171498f30SBruce M Simpson 	case MCAST_JOIN_GROUP:
280271498f30SBruce M Simpson 	case MCAST_JOIN_SOURCE_GROUP:
280371498f30SBruce M Simpson 		error = inp_join_group(inp, sopt);
280471498f30SBruce M Simpson 		break;
280571498f30SBruce M Simpson 
280671498f30SBruce M Simpson 	case IP_DROP_MEMBERSHIP:
280771498f30SBruce M Simpson 	case IP_DROP_SOURCE_MEMBERSHIP:
280871498f30SBruce M Simpson 	case MCAST_LEAVE_GROUP:
280971498f30SBruce M Simpson 	case MCAST_LEAVE_SOURCE_GROUP:
281071498f30SBruce M Simpson 		error = inp_leave_group(inp, sopt);
281171498f30SBruce M Simpson 		break;
281271498f30SBruce M Simpson 
281371498f30SBruce M Simpson 	case IP_BLOCK_SOURCE:
281471498f30SBruce M Simpson 	case IP_UNBLOCK_SOURCE:
281571498f30SBruce M Simpson 	case MCAST_BLOCK_SOURCE:
281671498f30SBruce M Simpson 	case MCAST_UNBLOCK_SOURCE:
2817d10910e6SBruce M Simpson 		error = inp_block_unblock_source(inp, sopt);
281871498f30SBruce M Simpson 		break;
281971498f30SBruce M Simpson 
282071498f30SBruce M Simpson 	case IP_MSFILTER:
282171498f30SBruce M Simpson 		error = inp_set_source_filters(inp, sopt);
282271498f30SBruce M Simpson 		break;
282371498f30SBruce M Simpson 
282471498f30SBruce M Simpson 	default:
282571498f30SBruce M Simpson 		error = EOPNOTSUPP;
282671498f30SBruce M Simpson 		break;
282771498f30SBruce M Simpson 	}
282871498f30SBruce M Simpson 
282971498f30SBruce M Simpson 	INP_UNLOCK_ASSERT(inp);
283071498f30SBruce M Simpson 
283171498f30SBruce M Simpson 	return (error);
283271498f30SBruce M Simpson }
2833d10910e6SBruce M Simpson 
2834d10910e6SBruce M Simpson /*
2835d10910e6SBruce M Simpson  * Expose IGMP's multicast filter mode and source list(s) to userland,
2836d10910e6SBruce M Simpson  * keyed by (ifindex, group).
2837d10910e6SBruce M Simpson  * The filter mode is written out as a uint32_t, followed by
2838d10910e6SBruce M Simpson  * 0..n of struct in_addr.
2839d10910e6SBruce M Simpson  * For use by ifmcstat(8).
2840d10910e6SBruce M Simpson  * SMPng: NOTE: unlocked read of ifindex space.
2841d10910e6SBruce M Simpson  */
2842d10910e6SBruce M Simpson static int
sysctl_ip_mcast_filters(SYSCTL_HANDLER_ARGS)2843d10910e6SBruce M Simpson sysctl_ip_mcast_filters(SYSCTL_HANDLER_ARGS)
2844d10910e6SBruce M Simpson {
2845d10910e6SBruce M Simpson 	struct in_addr			 src, group;
2846a68cc388SGleb Smirnoff 	struct epoch_tracker		 et;
2847d10910e6SBruce M Simpson 	struct ifnet			*ifp;
2848d10910e6SBruce M Simpson 	struct ifmultiaddr		*ifma;
2849d10910e6SBruce M Simpson 	struct in_multi			*inm;
2850d10910e6SBruce M Simpson 	struct ip_msource		*ims;
2851d10910e6SBruce M Simpson 	int				*name;
2852d10910e6SBruce M Simpson 	int				 retval;
2853d10910e6SBruce M Simpson 	u_int				 namelen;
2854d10910e6SBruce M Simpson 	uint32_t			 fmode, ifindex;
2855d10910e6SBruce M Simpson 
2856d10910e6SBruce M Simpson 	name = (int *)arg1;
2857d10910e6SBruce M Simpson 	namelen = arg2;
2858d10910e6SBruce M Simpson 
2859d10910e6SBruce M Simpson 	if (req->newptr != NULL)
2860d10910e6SBruce M Simpson 		return (EPERM);
2861d10910e6SBruce M Simpson 
2862d10910e6SBruce M Simpson 	if (namelen != 2)
2863d10910e6SBruce M Simpson 		return (EINVAL);
2864d10910e6SBruce M Simpson 
2865d10910e6SBruce M Simpson 	group.s_addr = name[1];
2866d10910e6SBruce M Simpson 	if (!IN_MULTICAST(ntohl(group.s_addr))) {
286747d803eaSEric van Gyzen 		CTR2(KTR_IGMPV3, "%s: group 0x%08x is not multicast",
286840769242SEric van Gyzen 		    __func__, ntohl(group.s_addr));
2869d10910e6SBruce M Simpson 		return (EINVAL);
2870d10910e6SBruce M Simpson 	}
2871d10910e6SBruce M Simpson 
2872d74b7baeSGleb Smirnoff 	ifindex = name[0];
2873b8a6e03fSGleb Smirnoff 	NET_EPOCH_ENTER(et);
2874d10910e6SBruce M Simpson 	ifp = ifnet_byindex(ifindex);
2875d10910e6SBruce M Simpson 	if (ifp == NULL) {
2876b8a6e03fSGleb Smirnoff 		NET_EPOCH_EXIT(et);
2877d10910e6SBruce M Simpson 		CTR2(KTR_IGMPV3, "%s: no ifp for ifindex %u",
2878d10910e6SBruce M Simpson 		    __func__, ifindex);
2879d10910e6SBruce M Simpson 		return (ENOENT);
2880d10910e6SBruce M Simpson 	}
2881d10910e6SBruce M Simpson 
2882d10910e6SBruce M Simpson 	retval = sysctl_wire_old_buffer(req,
2883d10910e6SBruce M Simpson 	    sizeof(uint32_t) + (in_mcast_maxgrpsrc * sizeof(struct in_addr)));
2884b8a6e03fSGleb Smirnoff 	if (retval) {
2885b8a6e03fSGleb Smirnoff 		NET_EPOCH_EXIT(et);
2886d10910e6SBruce M Simpson 		return (retval);
2887b8a6e03fSGleb Smirnoff 	}
2888d10910e6SBruce M Simpson 
2889f3e1324bSStephen Hurd 	IN_MULTI_LIST_LOCK();
2890d10910e6SBruce M Simpson 
2891d7c5a620SMatt Macy 	CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
28921e9482f4SAlexander Motin 		inm = inm_ifmultiaddr_get_inm(ifma);
28931e9482f4SAlexander Motin 		if (inm == NULL)
2894d10910e6SBruce M Simpson 			continue;
2895d10910e6SBruce M Simpson 		if (!in_hosteq(inm->inm_addr, group))
2896d10910e6SBruce M Simpson 			continue;
2897d10910e6SBruce M Simpson 		fmode = inm->inm_st[1].iss_fmode;
2898d10910e6SBruce M Simpson 		retval = SYSCTL_OUT(req, &fmode, sizeof(uint32_t));
2899d10910e6SBruce M Simpson 		if (retval != 0)
2900d10910e6SBruce M Simpson 			break;
2901d10910e6SBruce M Simpson 		RB_FOREACH(ims, ip_msource_tree, &inm->inm_srcs) {
290247d803eaSEric van Gyzen 			CTR2(KTR_IGMPV3, "%s: visit node 0x%08x", __func__,
290340769242SEric van Gyzen 			    ims->ims_haddr);
2904d10910e6SBruce M Simpson 			/*
2905d10910e6SBruce M Simpson 			 * Only copy-out sources which are in-mode.
2906d10910e6SBruce M Simpson 			 */
2907d10910e6SBruce M Simpson 			if (fmode != ims_get_mode(inm, ims, 1)) {
2908d10910e6SBruce M Simpson 				CTR1(KTR_IGMPV3, "%s: skip non-in-mode",
2909d10910e6SBruce M Simpson 				    __func__);
2910d10910e6SBruce M Simpson 				continue;
2911d10910e6SBruce M Simpson 			}
2912d10910e6SBruce M Simpson 			src.s_addr = htonl(ims->ims_haddr);
2913d10910e6SBruce M Simpson 			retval = SYSCTL_OUT(req, &src, sizeof(struct in_addr));
2914d10910e6SBruce M Simpson 			if (retval != 0)
2915d10910e6SBruce M Simpson 				break;
2916d10910e6SBruce M Simpson 		}
2917d10910e6SBruce M Simpson 	}
2918d10910e6SBruce M Simpson 
2919f3e1324bSStephen Hurd 	IN_MULTI_LIST_UNLOCK();
2920b8a6e03fSGleb Smirnoff 	NET_EPOCH_EXIT(et);
2921d10910e6SBruce M Simpson 
2922d10910e6SBruce M Simpson 	return (retval);
2923d10910e6SBruce M Simpson }
2924d10910e6SBruce M Simpson 
2925a9456c08SJohn Baldwin #if defined(KTR) && (KTR_COMPILE & KTR_IGMPV3)
2926d10910e6SBruce M Simpson 
2927fb6a8447SAlexander Motin static const char *inm_modestrs[] = {
2928fb6a8447SAlexander Motin 	[MCAST_UNDEFINED] = "un",
2929fb6a8447SAlexander Motin 	[MCAST_INCLUDE] = "in",
2930fb6a8447SAlexander Motin 	[MCAST_EXCLUDE] = "ex",
2931fb6a8447SAlexander Motin };
2932fb6a8447SAlexander Motin _Static_assert(MCAST_UNDEFINED == 0 &&
2933fb6a8447SAlexander Motin 	       MCAST_EXCLUDE + 1 == nitems(inm_modestrs),
2934fb6a8447SAlexander Motin 	       "inm_modestrs: no longer matches #defines");
2935d10910e6SBruce M Simpson 
2936d10910e6SBruce M Simpson static const char *
inm_mode_str(const int mode)2937d10910e6SBruce M Simpson inm_mode_str(const int mode)
2938d10910e6SBruce M Simpson {
2939d10910e6SBruce M Simpson 
2940d10910e6SBruce M Simpson 	if (mode >= MCAST_UNDEFINED && mode <= MCAST_EXCLUDE)
2941d10910e6SBruce M Simpson 		return (inm_modestrs[mode]);
2942d10910e6SBruce M Simpson 	return ("??");
2943d10910e6SBruce M Simpson }
2944d10910e6SBruce M Simpson 
2945d10910e6SBruce M Simpson static const char *inm_statestrs[] = {
2946fb6a8447SAlexander Motin 	[IGMP_NOT_MEMBER] = "not-member",
2947fb6a8447SAlexander Motin 	[IGMP_SILENT_MEMBER] = "silent",
2948fb6a8447SAlexander Motin 	[IGMP_REPORTING_MEMBER] = "reporting",
2949fb6a8447SAlexander Motin 	[IGMP_IDLE_MEMBER] = "idle",
2950fb6a8447SAlexander Motin 	[IGMP_LAZY_MEMBER] = "lazy",
2951fb6a8447SAlexander Motin 	[IGMP_SLEEPING_MEMBER] = "sleeping",
2952fb6a8447SAlexander Motin 	[IGMP_AWAKENING_MEMBER] = "awakening",
2953fb6a8447SAlexander Motin 	[IGMP_G_QUERY_PENDING_MEMBER] = "query-pending",
2954fb6a8447SAlexander Motin 	[IGMP_SG_QUERY_PENDING_MEMBER] = "sg-query-pending",
2955fb6a8447SAlexander Motin 	[IGMP_LEAVING_MEMBER] = "leaving",
2956d10910e6SBruce M Simpson };
2957fb6a8447SAlexander Motin _Static_assert(IGMP_NOT_MEMBER == 0 &&
2958fb6a8447SAlexander Motin 	       IGMP_LEAVING_MEMBER + 1 == nitems(inm_statestrs),
2959fb6a8447SAlexander Motin 	       "inm_statetrs: no longer matches #defines");
2960d10910e6SBruce M Simpson 
2961d10910e6SBruce M Simpson static const char *
inm_state_str(const int state)2962d10910e6SBruce M Simpson inm_state_str(const int state)
2963d10910e6SBruce M Simpson {
2964d10910e6SBruce M Simpson 
2965d10910e6SBruce M Simpson 	if (state >= IGMP_NOT_MEMBER && state <= IGMP_LEAVING_MEMBER)
2966d10910e6SBruce M Simpson 		return (inm_statestrs[state]);
2967d10910e6SBruce M Simpson 	return ("??");
2968d10910e6SBruce M Simpson }
2969d10910e6SBruce M Simpson 
2970d10910e6SBruce M Simpson /*
2971d10910e6SBruce M Simpson  * Dump an in_multi structure to the console.
2972d10910e6SBruce M Simpson  */
2973d10910e6SBruce M Simpson void
inm_print(const struct in_multi * inm)2974d10910e6SBruce M Simpson inm_print(const struct in_multi *inm)
2975d10910e6SBruce M Simpson {
2976d10910e6SBruce M Simpson 	int t;
29778144690aSEric van Gyzen 	char addrbuf[INET_ADDRSTRLEN];
2978d10910e6SBruce M Simpson 
2979024a4bd6SAlexander Kabaev 	if ((ktr_mask & KTR_IGMPV3) == 0)
298030e239feSBruce M Simpson 		return;
298130e239feSBruce M Simpson 
2982d10910e6SBruce M Simpson 	printf("%s: --- begin inm %p ---\n", __func__, inm);
2983d10910e6SBruce M Simpson 	printf("addr %s ifp %p(%s) ifma %p\n",
29848144690aSEric van Gyzen 	    inet_ntoa_r(inm->inm_addr, addrbuf),
2985d10910e6SBruce M Simpson 	    inm->inm_ifp,
2986d10910e6SBruce M Simpson 	    inm->inm_ifp->if_xname,
2987d10910e6SBruce M Simpson 	    inm->inm_ifma);
2988d10910e6SBruce M Simpson 	printf("timer %u state %s refcount %u scq.len %u\n",
2989d10910e6SBruce M Simpson 	    inm->inm_timer,
2990d10910e6SBruce M Simpson 	    inm_state_str(inm->inm_state),
2991d10910e6SBruce M Simpson 	    inm->inm_refcount,
29920c6dcac3SKonstantin Belousov 	    inm->inm_scq.mq_len);
2993d10910e6SBruce M Simpson 	printf("igi %p nsrc %lu sctimer %u scrv %u\n",
2994d10910e6SBruce M Simpson 	    inm->inm_igi,
2995d10910e6SBruce M Simpson 	    inm->inm_nsrc,
2996d10910e6SBruce M Simpson 	    inm->inm_sctimer,
2997d10910e6SBruce M Simpson 	    inm->inm_scrv);
2998d10910e6SBruce M Simpson 	for (t = 0; t < 2; t++) {
2999d10910e6SBruce M Simpson 		printf("t%d: fmode %s asm %u ex %u in %u rec %u\n", t,
3000d10910e6SBruce M Simpson 		    inm_mode_str(inm->inm_st[t].iss_fmode),
3001d10910e6SBruce M Simpson 		    inm->inm_st[t].iss_asm,
3002d10910e6SBruce M Simpson 		    inm->inm_st[t].iss_ex,
3003d10910e6SBruce M Simpson 		    inm->inm_st[t].iss_in,
3004d10910e6SBruce M Simpson 		    inm->inm_st[t].iss_rec);
3005d10910e6SBruce M Simpson 	}
3006d10910e6SBruce M Simpson 	printf("%s: --- end inm %p ---\n", __func__, inm);
3007d10910e6SBruce M Simpson }
3008d10910e6SBruce M Simpson 
3009a9456c08SJohn Baldwin #else /* !KTR || !(KTR_COMPILE & KTR_IGMPV3) */
3010d10910e6SBruce M Simpson 
3011d10910e6SBruce M Simpson void
inm_print(const struct in_multi * inm)3012d10910e6SBruce M Simpson inm_print(const struct in_multi *inm)
3013d10910e6SBruce M Simpson {
3014d10910e6SBruce M Simpson 
3015d10910e6SBruce M Simpson }
3016d10910e6SBruce M Simpson 
3017a9456c08SJohn Baldwin #endif /* KTR && (KTR_COMPILE & KTR_IGMPV3) */
3018d10910e6SBruce M Simpson 
3019d10910e6SBruce M Simpson RB_GENERATE(ip_msource_tree, ip_msource, ims_link, ip_msource_cmp);
3020