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