1fe267a55SPedro F. Giffuni /*- 2fe267a55SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause 3fe267a55SPedro F. Giffuni * 48f002c6cSBruce M Simpson * Copyright (c) 2009 Bruce Simpson. 58f002c6cSBruce M Simpson * All rights reserved. 68f002c6cSBruce M Simpson * 78f002c6cSBruce M Simpson * Redistribution and use in source and binary forms, with or without 88f002c6cSBruce M Simpson * modification, are permitted provided that the following conditions 98f002c6cSBruce M Simpson * are met: 108f002c6cSBruce M Simpson * 1. Redistributions of source code must retain the above copyright 118f002c6cSBruce M Simpson * notice, this list of conditions and the following disclaimer. 128f002c6cSBruce M Simpson * 2. Redistributions in binary form must reproduce the above copyright 138f002c6cSBruce M Simpson * notice, this list of conditions and the following disclaimer in the 148f002c6cSBruce M Simpson * documentation and/or other materials provided with the distribution. 158f002c6cSBruce M Simpson * 3. The name of the author may not be used to endorse or promote 168f002c6cSBruce M Simpson * products derived from this software without specific prior written 178f002c6cSBruce M Simpson * permission. 188f002c6cSBruce M Simpson * 198f002c6cSBruce M Simpson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 208f002c6cSBruce M Simpson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 218f002c6cSBruce M Simpson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 228f002c6cSBruce M Simpson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 238f002c6cSBruce M Simpson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 248f002c6cSBruce M Simpson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 258f002c6cSBruce M Simpson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 268f002c6cSBruce M Simpson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 278f002c6cSBruce M Simpson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 288f002c6cSBruce M Simpson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 298f002c6cSBruce M Simpson * SUCH DAMAGE. 308f002c6cSBruce M Simpson */ 318f002c6cSBruce M Simpson 328f002c6cSBruce M Simpson /* 338f002c6cSBruce M Simpson * IPv6 multicast socket, group, and socket option processing module. 3433cde130SBruce M Simpson * Normative references: RFC 2292, RFC 3492, RFC 3542, RFC 3678, RFC 3810. 358f002c6cSBruce M Simpson */ 368f002c6cSBruce M Simpson 378f002c6cSBruce M Simpson #include <sys/cdefs.h> 388f002c6cSBruce M Simpson __FBSDID("$FreeBSD$"); 398f002c6cSBruce M Simpson 408f002c6cSBruce M Simpson #include "opt_inet6.h" 418f002c6cSBruce M Simpson 428f002c6cSBruce M Simpson #include <sys/param.h> 438f002c6cSBruce M Simpson #include <sys/systm.h> 448f002c6cSBruce M Simpson #include <sys/kernel.h> 459b1d850bSMark Johnston #include <sys/ktr.h> 468f002c6cSBruce M Simpson #include <sys/malloc.h> 478f002c6cSBruce M Simpson #include <sys/mbuf.h> 488f002c6cSBruce M Simpson #include <sys/protosw.h> 498f002c6cSBruce M Simpson #include <sys/socket.h> 508f002c6cSBruce M Simpson #include <sys/socketvar.h> 518f002c6cSBruce M Simpson #include <sys/sysctl.h> 528f002c6cSBruce M Simpson #include <sys/priv.h> 539b1d850bSMark Johnston #include <sys/taskqueue.h> 548f002c6cSBruce M Simpson #include <sys/tree.h> 558f002c6cSBruce M Simpson 568f002c6cSBruce M Simpson #include <net/if.h> 5776039bc8SGleb Smirnoff #include <net/if_var.h> 588f002c6cSBruce M Simpson #include <net/if_dl.h> 598f002c6cSBruce M Simpson #include <net/route.h> 606ad7446cSAlexander V. Chernikov #include <net/route/nhop.h> 618f002c6cSBruce M Simpson #include <net/vnet.h> 628f002c6cSBruce M Simpson 638f002c6cSBruce M Simpson #include <netinet/in.h> 64f3e1324bSStephen Hurd #include <netinet/udp.h> 658f002c6cSBruce M Simpson #include <netinet/in_var.h> 66f3e1324bSStephen Hurd #include <netinet/ip_var.h> 67f3e1324bSStephen Hurd #include <netinet/udp_var.h> 689977be4aSAlexander V. Chernikov #include <netinet6/in6_fib.h> 698f002c6cSBruce M Simpson #include <netinet6/in6_var.h> 708f002c6cSBruce M Simpson #include <netinet/ip6.h> 718f002c6cSBruce M Simpson #include <netinet/icmp6.h> 728f002c6cSBruce M Simpson #include <netinet6/ip6_var.h> 738f002c6cSBruce M Simpson #include <netinet/in_pcb.h> 748f002c6cSBruce M Simpson #include <netinet/tcp_var.h> 758f002c6cSBruce M Simpson #include <netinet6/nd6.h> 768f002c6cSBruce M Simpson #include <netinet6/mld6_var.h> 77eddfbb76SRobert Watson #include <netinet6/scope6_var.h> 788f002c6cSBruce M Simpson 798f002c6cSBruce M Simpson #ifndef KTR_MLD 808f002c6cSBruce M Simpson #define KTR_MLD KTR_INET6 818f002c6cSBruce M Simpson #endif 828f002c6cSBruce M Simpson 838f002c6cSBruce M Simpson #ifndef __SOCKUNION_DECLARED 848f002c6cSBruce M Simpson union sockunion { 858f002c6cSBruce M Simpson struct sockaddr_storage ss; 868f002c6cSBruce M Simpson struct sockaddr sa; 878f002c6cSBruce M Simpson struct sockaddr_dl sdl; 888f002c6cSBruce M Simpson struct sockaddr_in6 sin6; 898f002c6cSBruce M Simpson }; 908f002c6cSBruce M Simpson typedef union sockunion sockunion_t; 918f002c6cSBruce M Simpson #define __SOCKUNION_DECLARED 928f002c6cSBruce M Simpson #endif /* __SOCKUNION_DECLARED */ 938f002c6cSBruce M Simpson 948f002c6cSBruce M Simpson static MALLOC_DEFINE(M_IN6MFILTER, "in6_mfilter", 958f002c6cSBruce M Simpson "IPv6 multicast PCB-layer source filter"); 96f3e1324bSStephen Hurd MALLOC_DEFINE(M_IP6MADDR, "in6_multi", "IPv6 multicast group"); 978f002c6cSBruce M Simpson static MALLOC_DEFINE(M_IP6MOPTS, "ip6_moptions", "IPv6 multicast options"); 988f002c6cSBruce M Simpson static MALLOC_DEFINE(M_IP6MSOURCE, "ip6_msource", 998f002c6cSBruce M Simpson "IPv6 multicast MLD-layer source filter"); 1008f002c6cSBruce M Simpson 1018f002c6cSBruce M Simpson RB_GENERATE(ip6_msource_tree, ip6_msource, im6s_link, ip6_msource_cmp); 1028f002c6cSBruce M Simpson 1038f002c6cSBruce M Simpson /* 1048f002c6cSBruce M Simpson * Locking: 10559854ecfSHans Petter Selasky * - Lock order is: Giant, IN6_MULTI_LOCK, INP_WLOCK, 10659854ecfSHans Petter Selasky * IN6_MULTI_LIST_LOCK, MLD_LOCK, IF_ADDR_LOCK. 1078f002c6cSBruce M Simpson * - The IF_ADDR_LOCK is implicitly taken by in6m_lookup() earlier, however 1088f002c6cSBruce M Simpson * it can be taken by code in net/if.c also. 1098f002c6cSBruce M Simpson * - ip6_moptions and in6_mfilter are covered by the INP_WLOCK. 1108f002c6cSBruce M Simpson * 1118f002c6cSBruce M Simpson * struct in6_multi is covered by IN6_MULTI_LOCK. There isn't strictly 1128f002c6cSBruce M Simpson * any need for in6_multi itself to be virtualized -- it is bound to an ifp 1138f002c6cSBruce M Simpson * anyway no matter what happens. 1148f002c6cSBruce M Simpson */ 115f3e1324bSStephen Hurd struct mtx in6_multi_list_mtx; 116f3e1324bSStephen Hurd MTX_SYSINIT(in6_multi_mtx, &in6_multi_list_mtx, "in6_multi_list_mtx", MTX_DEF); 117f3e1324bSStephen Hurd 118f3e1324bSStephen Hurd struct mtx in6_multi_free_mtx; 119f3e1324bSStephen Hurd MTX_SYSINIT(in6_multi_free_mtx, &in6_multi_free_mtx, "in6_multi_free_mtx", MTX_DEF); 120f3e1324bSStephen Hurd 121f3e1324bSStephen Hurd struct sx in6_multi_sx; 122f3e1324bSStephen Hurd SX_SYSINIT(in6_multi_sx, &in6_multi_sx, "in6_multi_sx"); 123f3e1324bSStephen Hurd 1248f002c6cSBruce M Simpson static void im6f_commit(struct in6_mfilter *); 1258f002c6cSBruce M Simpson static int im6f_get_source(struct in6_mfilter *imf, 1268f002c6cSBruce M Simpson const struct sockaddr_in6 *psin, 1278f002c6cSBruce M Simpson struct in6_msource **); 1288f002c6cSBruce M Simpson static struct in6_msource * 1298f002c6cSBruce M Simpson im6f_graft(struct in6_mfilter *, const uint8_t, 1308f002c6cSBruce M Simpson const struct sockaddr_in6 *); 1318f002c6cSBruce M Simpson static void im6f_leave(struct in6_mfilter *); 1328f002c6cSBruce M Simpson static int im6f_prune(struct in6_mfilter *, const struct sockaddr_in6 *); 1338f002c6cSBruce M Simpson static void im6f_purge(struct in6_mfilter *); 1348f002c6cSBruce M Simpson static void im6f_rollback(struct in6_mfilter *); 1358f002c6cSBruce M Simpson static void im6f_reap(struct in6_mfilter *); 13659854ecfSHans Petter Selasky static struct in6_mfilter * 13759854ecfSHans Petter Selasky im6o_match_group(const struct ip6_moptions *, 1388f002c6cSBruce M Simpson const struct ifnet *, const struct sockaddr *); 1398f002c6cSBruce M Simpson static struct in6_msource * 14059854ecfSHans Petter Selasky im6o_match_source(struct in6_mfilter *, const struct sockaddr *); 1418f002c6cSBruce M Simpson static void im6s_merge(struct ip6_msource *ims, 1428f002c6cSBruce M Simpson const struct in6_msource *lims, const int rollback); 143f3e1324bSStephen Hurd static int in6_getmulti(struct ifnet *, const struct in6_addr *, 1448f002c6cSBruce M Simpson struct in6_multi **); 145e1891232SBjoern A. Zeeb static int in6_joingroup_locked(struct ifnet *, const struct in6_addr *, 146e1891232SBjoern A. Zeeb struct in6_mfilter *, struct in6_multi **, int); 1478f002c6cSBruce M Simpson static int in6m_get_source(struct in6_multi *inm, 1488f002c6cSBruce M Simpson const struct in6_addr *addr, const int noalloc, 1498f002c6cSBruce M Simpson struct ip6_msource **pims); 1506c5a340eSDimitry Andric #ifdef KTR 1518f002c6cSBruce M Simpson static int in6m_is_ifp_detached(const struct in6_multi *); 1526c5a340eSDimitry Andric #endif 1538f002c6cSBruce M Simpson static int in6m_merge(struct in6_multi *, /*const*/ struct in6_mfilter *); 1548f002c6cSBruce M Simpson static void in6m_purge(struct in6_multi *); 1558f002c6cSBruce M Simpson static void in6m_reap(struct in6_multi *); 1568f002c6cSBruce M Simpson static struct ip6_moptions * 1578f002c6cSBruce M Simpson in6p_findmoptions(struct inpcb *); 1588f002c6cSBruce M Simpson static int in6p_get_source_filters(struct inpcb *, struct sockopt *); 1598f002c6cSBruce M Simpson static int in6p_join_group(struct inpcb *, struct sockopt *); 1608f002c6cSBruce M Simpson static int in6p_leave_group(struct inpcb *, struct sockopt *); 16133cde130SBruce M Simpson static struct ifnet * 16233cde130SBruce M Simpson in6p_lookup_mcast_ifp(const struct inpcb *, 16333cde130SBruce M Simpson const struct sockaddr_in6 *); 1648f002c6cSBruce M Simpson static int in6p_block_unblock_source(struct inpcb *, struct sockopt *); 1658f002c6cSBruce M Simpson static int in6p_set_multicast_if(struct inpcb *, struct sockopt *); 1668f002c6cSBruce M Simpson static int in6p_set_source_filters(struct inpcb *, struct sockopt *); 1678f002c6cSBruce M Simpson static int sysctl_ip6_mcast_filters(SYSCTL_HANDLER_ARGS); 1688f002c6cSBruce M Simpson 1698f002c6cSBruce M Simpson SYSCTL_DECL(_net_inet6_ip6); /* XXX Not in any common header. */ 1708f002c6cSBruce M Simpson 1717029da5cSPawel Biernacki static SYSCTL_NODE(_net_inet6_ip6, OID_AUTO, mcast, 1727029da5cSPawel Biernacki CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 1736472ac3dSEd Schouten "IPv6 multicast"); 1748f002c6cSBruce M Simpson 1758f002c6cSBruce M Simpson static u_long in6_mcast_maxgrpsrc = IPV6_MAX_GROUP_SRC_FILTER; 1768f002c6cSBruce M Simpson SYSCTL_ULONG(_net_inet6_ip6_mcast, OID_AUTO, maxgrpsrc, 177af3b2549SHans Petter Selasky CTLFLAG_RWTUN, &in6_mcast_maxgrpsrc, 0, 1788f002c6cSBruce M Simpson "Max source filters per group"); 1798f002c6cSBruce M Simpson 1808f002c6cSBruce M Simpson static u_long in6_mcast_maxsocksrc = IPV6_MAX_SOCK_SRC_FILTER; 1818f002c6cSBruce M Simpson SYSCTL_ULONG(_net_inet6_ip6_mcast, OID_AUTO, maxsocksrc, 182af3b2549SHans Petter Selasky CTLFLAG_RWTUN, &in6_mcast_maxsocksrc, 0, 1838f002c6cSBruce M Simpson "Max source filters per socket"); 1848f002c6cSBruce M Simpson 1858f002c6cSBruce M Simpson /* TODO Virtualize this switch. */ 1868f002c6cSBruce M Simpson int in6_mcast_loop = IPV6_DEFAULT_MULTICAST_LOOP; 187af3b2549SHans Petter Selasky SYSCTL_INT(_net_inet6_ip6_mcast, OID_AUTO, loop, CTLFLAG_RWTUN, 1888f002c6cSBruce M Simpson &in6_mcast_loop, 0, "Loopback multicast datagrams by default"); 1898f002c6cSBruce M Simpson 1906472ac3dSEd Schouten static SYSCTL_NODE(_net_inet6_ip6_mcast, OID_AUTO, filters, 1918f002c6cSBruce M Simpson CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_ip6_mcast_filters, 1928f002c6cSBruce M Simpson "Per-interface stack-wide source filters"); 1938f002c6cSBruce M Simpson 1946c5a340eSDimitry Andric #ifdef KTR 1958f002c6cSBruce M Simpson /* 1968f002c6cSBruce M Simpson * Inline function which wraps assertions for a valid ifp. 1978f002c6cSBruce M Simpson * The ifnet layer will set the ifma's ifp pointer to NULL if the ifp 1988f002c6cSBruce M Simpson * is detached. 1998f002c6cSBruce M Simpson */ 2008f002c6cSBruce M Simpson static int __inline 2018f002c6cSBruce M Simpson in6m_is_ifp_detached(const struct in6_multi *inm) 2028f002c6cSBruce M Simpson { 2038f002c6cSBruce M Simpson struct ifnet *ifp; 2048f002c6cSBruce M Simpson 2058f002c6cSBruce M Simpson KASSERT(inm->in6m_ifma != NULL, ("%s: no ifma", __func__)); 2068f002c6cSBruce M Simpson ifp = inm->in6m_ifma->ifma_ifp; 2078f002c6cSBruce M Simpson if (ifp != NULL) { 2088f002c6cSBruce M Simpson /* 2098f002c6cSBruce M Simpson * Sanity check that network-layer notion of ifp is the 2108f002c6cSBruce M Simpson * same as that of link-layer. 2118f002c6cSBruce M Simpson */ 2128f002c6cSBruce M Simpson KASSERT(inm->in6m_ifp == ifp, ("%s: bad ifp", __func__)); 2138f002c6cSBruce M Simpson } 2148f002c6cSBruce M Simpson 2158f002c6cSBruce M Simpson return (ifp == NULL); 2168f002c6cSBruce M Simpson } 2176c5a340eSDimitry Andric #endif 2188f002c6cSBruce M Simpson 2198f002c6cSBruce M Simpson /* 2208f002c6cSBruce M Simpson * Initialize an in6_mfilter structure to a known state at t0, t1 2218f002c6cSBruce M Simpson * with an empty source filter list. 2228f002c6cSBruce M Simpson */ 2238f002c6cSBruce M Simpson static __inline void 2248f002c6cSBruce M Simpson im6f_init(struct in6_mfilter *imf, const int st0, const int st1) 2258f002c6cSBruce M Simpson { 2268f002c6cSBruce M Simpson memset(imf, 0, sizeof(struct in6_mfilter)); 2278f002c6cSBruce M Simpson RB_INIT(&imf->im6f_sources); 2288f002c6cSBruce M Simpson imf->im6f_st[0] = st0; 2298f002c6cSBruce M Simpson imf->im6f_st[1] = st1; 2308f002c6cSBruce M Simpson } 2318f002c6cSBruce M Simpson 23259854ecfSHans Petter Selasky struct in6_mfilter * 23359854ecfSHans Petter Selasky ip6_mfilter_alloc(const int mflags, const int st0, const int st1) 2348f002c6cSBruce M Simpson { 23559854ecfSHans Petter Selasky struct in6_mfilter *imf; 2368f002c6cSBruce M Simpson 23759854ecfSHans Petter Selasky imf = malloc(sizeof(*imf), M_IN6MFILTER, mflags); 2388f002c6cSBruce M Simpson 23959854ecfSHans Petter Selasky if (imf != NULL) 24059854ecfSHans Petter Selasky im6f_init(imf, st0, st1); 24159854ecfSHans Petter Selasky 24259854ecfSHans Petter Selasky return (imf); 2438f002c6cSBruce M Simpson } 2448f002c6cSBruce M Simpson 24559854ecfSHans Petter Selasky void 24659854ecfSHans Petter Selasky ip6_mfilter_free(struct in6_mfilter *imf) 24759854ecfSHans Petter Selasky { 2488f002c6cSBruce M Simpson 24959854ecfSHans Petter Selasky im6f_purge(imf); 25059854ecfSHans Petter Selasky free(imf, M_IN6MFILTER); 2518f002c6cSBruce M Simpson } 2528f002c6cSBruce M Simpson 2538f002c6cSBruce M Simpson /* 2548f002c6cSBruce M Simpson * Find an IPv6 multicast group entry for this ip6_moptions instance 2558f002c6cSBruce M Simpson * which matches the specified group, and optionally an interface. 2568f002c6cSBruce M Simpson * Return its index into the array, or -1 if not found. 2578f002c6cSBruce M Simpson */ 25859854ecfSHans Petter Selasky static struct in6_mfilter * 2598f002c6cSBruce M Simpson im6o_match_group(const struct ip6_moptions *imo, const struct ifnet *ifp, 2608f002c6cSBruce M Simpson const struct sockaddr *group) 2618f002c6cSBruce M Simpson { 2628f002c6cSBruce M Simpson const struct sockaddr_in6 *gsin6; 26359854ecfSHans Petter Selasky struct in6_mfilter *imf; 26459854ecfSHans Petter Selasky struct in6_multi *inm; 2658f002c6cSBruce M Simpson 2668f002c6cSBruce M Simpson gsin6 = (const struct sockaddr_in6 *)group; 2678f002c6cSBruce M Simpson 26859854ecfSHans Petter Selasky IP6_MFILTER_FOREACH(imf, &imo->im6o_head) { 26959854ecfSHans Petter Selasky inm = imf->im6f_in6m; 27059854ecfSHans Petter Selasky if (inm == NULL) 2718f002c6cSBruce M Simpson continue; 27259854ecfSHans Petter Selasky if ((ifp == NULL || (inm->in6m_ifp == ifp)) && 27359854ecfSHans Petter Selasky IN6_ARE_ADDR_EQUAL(&inm->in6m_addr, 2748f002c6cSBruce M Simpson &gsin6->sin6_addr)) { 2758f002c6cSBruce M Simpson break; 2768f002c6cSBruce M Simpson } 2778f002c6cSBruce M Simpson } 27859854ecfSHans Petter Selasky return (imf); 2798f002c6cSBruce M Simpson } 2808f002c6cSBruce M Simpson 2818f002c6cSBruce M Simpson /* 2828f002c6cSBruce M Simpson * Find an IPv6 multicast source entry for this imo which matches 2838f002c6cSBruce M Simpson * the given group index for this socket, and source address. 2848f002c6cSBruce M Simpson * 28529dc7bc6SBruce M Simpson * XXX TODO: The scope ID, if present in src, is stripped before 28629dc7bc6SBruce M Simpson * any comparison. We SHOULD enforce scope/zone checks where the source 28729dc7bc6SBruce M Simpson * filter entry has a link scope. 28829dc7bc6SBruce M Simpson * 2898f002c6cSBruce M Simpson * NOTE: This does not check if the entry is in-mode, merely if 2908f002c6cSBruce M Simpson * it exists, which may not be the desired behaviour. 2918f002c6cSBruce M Simpson */ 2928f002c6cSBruce M Simpson static struct in6_msource * 29359854ecfSHans Petter Selasky im6o_match_source(struct in6_mfilter *imf, const struct sockaddr *src) 2948f002c6cSBruce M Simpson { 2958f002c6cSBruce M Simpson struct ip6_msource find; 2968f002c6cSBruce M Simpson struct ip6_msource *ims; 2978f002c6cSBruce M Simpson const sockunion_t *psa; 2988f002c6cSBruce M Simpson 2998f002c6cSBruce M Simpson KASSERT(src->sa_family == AF_INET6, ("%s: !AF_INET6", __func__)); 3008f002c6cSBruce M Simpson 3018f002c6cSBruce M Simpson psa = (const sockunion_t *)src; 3028f002c6cSBruce M Simpson find.im6s_addr = psa->sin6.sin6_addr; 30329dc7bc6SBruce M Simpson in6_clearscope(&find.im6s_addr); /* XXX */ 3048f002c6cSBruce M Simpson ims = RB_FIND(ip6_msource_tree, &imf->im6f_sources, &find); 3058f002c6cSBruce M Simpson 3068f002c6cSBruce M Simpson return ((struct in6_msource *)ims); 3078f002c6cSBruce M Simpson } 3088f002c6cSBruce M Simpson 3098f002c6cSBruce M Simpson /* 3108f002c6cSBruce M Simpson * Perform filtering for multicast datagrams on a socket by group and source. 3118f002c6cSBruce M Simpson * 3128f002c6cSBruce M Simpson * Returns 0 if a datagram should be allowed through, or various error codes 3138f002c6cSBruce M Simpson * if the socket was not a member of the group, or the source was muted, etc. 3148f002c6cSBruce M Simpson */ 3158f002c6cSBruce M Simpson int 3168f002c6cSBruce M Simpson im6o_mc_filter(const struct ip6_moptions *imo, const struct ifnet *ifp, 3178f002c6cSBruce M Simpson const struct sockaddr *group, const struct sockaddr *src) 3188f002c6cSBruce M Simpson { 31959854ecfSHans Petter Selasky struct in6_mfilter *imf; 3208f002c6cSBruce M Simpson struct in6_msource *ims; 3218f002c6cSBruce M Simpson int mode; 3228f002c6cSBruce M Simpson 3238f002c6cSBruce M Simpson KASSERT(ifp != NULL, ("%s: null ifp", __func__)); 3248f002c6cSBruce M Simpson 32559854ecfSHans Petter Selasky imf = im6o_match_group(imo, ifp, group); 32659854ecfSHans Petter Selasky if (imf == NULL) 3278f002c6cSBruce M Simpson return (MCAST_NOTGMEMBER); 3288f002c6cSBruce M Simpson 3298f002c6cSBruce M Simpson /* 3308f002c6cSBruce M Simpson * Check if the source was included in an (S,G) join. 3318f002c6cSBruce M Simpson * Allow reception on exclusive memberships by default, 3328f002c6cSBruce M Simpson * reject reception on inclusive memberships by default. 3338f002c6cSBruce M Simpson * Exclude source only if an in-mode exclude filter exists. 3348f002c6cSBruce M Simpson * Include source only if an in-mode include filter exists. 3358f002c6cSBruce M Simpson * NOTE: We are comparing group state here at MLD t1 (now) 3368f002c6cSBruce M Simpson * with socket-layer t0 (since last downcall). 3378f002c6cSBruce M Simpson */ 33859854ecfSHans Petter Selasky mode = imf->im6f_st[1]; 33959854ecfSHans Petter Selasky ims = im6o_match_source(imf, src); 3408f002c6cSBruce M Simpson 3418f002c6cSBruce M Simpson if ((ims == NULL && mode == MCAST_INCLUDE) || 3428f002c6cSBruce M Simpson (ims != NULL && ims->im6sl_st[0] != mode)) 3438f002c6cSBruce M Simpson return (MCAST_NOTSMEMBER); 3448f002c6cSBruce M Simpson 3458f002c6cSBruce M Simpson return (MCAST_PASS); 3468f002c6cSBruce M Simpson } 3478f002c6cSBruce M Simpson 3488f002c6cSBruce M Simpson /* 3498f002c6cSBruce M Simpson * Find and return a reference to an in6_multi record for (ifp, group), 3508f002c6cSBruce M Simpson * and bump its reference count. 3518f002c6cSBruce M Simpson * If one does not exist, try to allocate it, and update link-layer multicast 3528f002c6cSBruce M Simpson * filters on ifp to listen for group. 3538f002c6cSBruce M Simpson * Assumes the IN6_MULTI lock is held across the call. 3548f002c6cSBruce M Simpson * Return 0 if successful, otherwise return an appropriate error code. 3558f002c6cSBruce M Simpson */ 3568f002c6cSBruce M Simpson static int 357f3e1324bSStephen Hurd in6_getmulti(struct ifnet *ifp, const struct in6_addr *group, 3588f002c6cSBruce M Simpson struct in6_multi **pinm) 3598f002c6cSBruce M Simpson { 3602cd6ad76SHans Petter Selasky struct epoch_tracker et; 3618f002c6cSBruce M Simpson struct sockaddr_in6 gsin6; 3628f002c6cSBruce M Simpson struct ifmultiaddr *ifma; 3638f002c6cSBruce M Simpson struct in6_multi *inm; 3648f002c6cSBruce M Simpson int error; 3658f002c6cSBruce M Simpson 3668f002c6cSBruce M Simpson error = 0; 3678f002c6cSBruce M Simpson 3688f002c6cSBruce M Simpson /* 3698f002c6cSBruce M Simpson * XXX: Accesses to ifma_protospec must be covered by IF_ADDR_LOCK; 3708f002c6cSBruce M Simpson * if_addmulti() takes this mutex itself, so we must drop and 3718f002c6cSBruce M Simpson * re-acquire around the call. 3728f002c6cSBruce M Simpson */ 3738f002c6cSBruce M Simpson IN6_MULTI_LOCK_ASSERT(); 374f3e1324bSStephen Hurd IN6_MULTI_LIST_LOCK(); 375137f91e8SJohn Baldwin IF_ADDR_WLOCK(ifp); 3762cd6ad76SHans Petter Selasky NET_EPOCH_ENTER(et); 377d477a7feSAndrey V. Elsukov /* 378d477a7feSAndrey V. Elsukov * Does ifp support IPv6 multicasts? 379d477a7feSAndrey V. Elsukov */ 380d477a7feSAndrey V. Elsukov if (ifp->if_afdata[AF_INET6] == NULL) 381d477a7feSAndrey V. Elsukov error = ENODEV; 382d477a7feSAndrey V. Elsukov else 3838f002c6cSBruce M Simpson inm = in6m_lookup_locked(ifp, group); 3842cd6ad76SHans Petter Selasky NET_EPOCH_EXIT(et); 3852cd6ad76SHans Petter Selasky 386d477a7feSAndrey V. Elsukov if (error != 0) 387d477a7feSAndrey V. Elsukov goto out_locked; 388d477a7feSAndrey V. Elsukov 3898f002c6cSBruce M Simpson if (inm != NULL) { 3908f002c6cSBruce M Simpson /* 3918f002c6cSBruce M Simpson * If we already joined this group, just bump the 3928f002c6cSBruce M Simpson * refcount and return it. 3938f002c6cSBruce M Simpson */ 3948f002c6cSBruce M Simpson KASSERT(inm->in6m_refcount >= 1, 3958f002c6cSBruce M Simpson ("%s: bad refcount %d", __func__, inm->in6m_refcount)); 396f3e1324bSStephen Hurd in6m_acquire_locked(inm); 3978f002c6cSBruce M Simpson *pinm = inm; 3988f002c6cSBruce M Simpson goto out_locked; 3998f002c6cSBruce M Simpson } 4008f002c6cSBruce M Simpson 4018f002c6cSBruce M Simpson memset(&gsin6, 0, sizeof(gsin6)); 4028f002c6cSBruce M Simpson gsin6.sin6_family = AF_INET6; 4038f002c6cSBruce M Simpson gsin6.sin6_len = sizeof(struct sockaddr_in6); 4048f002c6cSBruce M Simpson gsin6.sin6_addr = *group; 4058f002c6cSBruce M Simpson 4068f002c6cSBruce M Simpson /* 4078f002c6cSBruce M Simpson * Check if a link-layer group is already associated 4088f002c6cSBruce M Simpson * with this network-layer group on the given ifnet. 4098f002c6cSBruce M Simpson */ 410f3e1324bSStephen Hurd IN6_MULTI_LIST_UNLOCK(); 411137f91e8SJohn Baldwin IF_ADDR_WUNLOCK(ifp); 4128f002c6cSBruce M Simpson error = if_addmulti(ifp, (struct sockaddr *)&gsin6, &ifma); 4138f002c6cSBruce M Simpson if (error != 0) 4148f002c6cSBruce M Simpson return (error); 415f3e1324bSStephen Hurd IN6_MULTI_LIST_LOCK(); 416137f91e8SJohn Baldwin IF_ADDR_WLOCK(ifp); 4178f002c6cSBruce M Simpson 4188f002c6cSBruce M Simpson /* 4198f002c6cSBruce M Simpson * If something other than netinet6 is occupying the link-layer 4208f002c6cSBruce M Simpson * group, print a meaningful error message and back out of 4218f002c6cSBruce M Simpson * the allocation. 4228f002c6cSBruce M Simpson * Otherwise, bump the refcount on the existing network-layer 4238f002c6cSBruce M Simpson * group association and return it. 4248f002c6cSBruce M Simpson */ 4258f002c6cSBruce M Simpson if (ifma->ifma_protospec != NULL) { 4268f002c6cSBruce M Simpson inm = (struct in6_multi *)ifma->ifma_protospec; 4278f002c6cSBruce M Simpson #ifdef INVARIANTS 4288f002c6cSBruce M Simpson KASSERT(ifma->ifma_addr != NULL, ("%s: no ifma_addr", 4298f002c6cSBruce M Simpson __func__)); 4308f002c6cSBruce M Simpson KASSERT(ifma->ifma_addr->sa_family == AF_INET6, 4318f002c6cSBruce M Simpson ("%s: ifma not AF_INET6", __func__)); 4328f002c6cSBruce M Simpson KASSERT(inm != NULL, ("%s: no ifma_protospec", __func__)); 4338f002c6cSBruce M Simpson if (inm->in6m_ifma != ifma || inm->in6m_ifp != ifp || 4348f002c6cSBruce M Simpson !IN6_ARE_ADDR_EQUAL(&inm->in6m_addr, group)) 4358f002c6cSBruce M Simpson panic("%s: ifma %p is inconsistent with %p (%p)", 4368f002c6cSBruce M Simpson __func__, ifma, inm, group); 4378f002c6cSBruce M Simpson #endif 438f3e1324bSStephen Hurd in6m_acquire_locked(inm); 4398f002c6cSBruce M Simpson *pinm = inm; 4408f002c6cSBruce M Simpson goto out_locked; 4418f002c6cSBruce M Simpson } 4428f002c6cSBruce M Simpson 443137f91e8SJohn Baldwin IF_ADDR_WLOCK_ASSERT(ifp); 4448f002c6cSBruce M Simpson 4458f002c6cSBruce M Simpson /* 4468f002c6cSBruce M Simpson * A new in6_multi record is needed; allocate and initialize it. 4478f002c6cSBruce M Simpson * We DO NOT perform an MLD join as the in6_ layer may need to 4488f002c6cSBruce M Simpson * push an initial source list down to MLD to support SSM. 4498f002c6cSBruce M Simpson * 4508f002c6cSBruce M Simpson * The initial source filter state is INCLUDE, {} as per the RFC. 4518f002c6cSBruce M Simpson * Pending state-changes per group are subject to a bounds check. 4528f002c6cSBruce M Simpson */ 4538f002c6cSBruce M Simpson inm = malloc(sizeof(*inm), M_IP6MADDR, M_NOWAIT | M_ZERO); 4548f002c6cSBruce M Simpson if (inm == NULL) { 455f3e1324bSStephen Hurd IN6_MULTI_LIST_UNLOCK(); 45655faae77SConrad Meyer IF_ADDR_WUNLOCK(ifp); 4578f002c6cSBruce M Simpson if_delmulti_ifma(ifma); 45855faae77SConrad Meyer return (ENOMEM); 4598f002c6cSBruce M Simpson } 4608f002c6cSBruce M Simpson inm->in6m_addr = *group; 4618f002c6cSBruce M Simpson inm->in6m_ifp = ifp; 4628f002c6cSBruce M Simpson inm->in6m_mli = MLD_IFINFO(ifp); 4638f002c6cSBruce M Simpson inm->in6m_ifma = ifma; 4648f002c6cSBruce M Simpson inm->in6m_refcount = 1; 4658f002c6cSBruce M Simpson inm->in6m_state = MLD_NOT_MEMBER; 466a99c84d4SGleb Smirnoff mbufq_init(&inm->in6m_scq, MLD_MAX_STATE_CHANGES); 4678f002c6cSBruce M Simpson 4688f002c6cSBruce M Simpson inm->in6m_st[0].iss_fmode = MCAST_UNDEFINED; 4698f002c6cSBruce M Simpson inm->in6m_st[1].iss_fmode = MCAST_UNDEFINED; 4708f002c6cSBruce M Simpson RB_INIT(&inm->in6m_srcs); 4718f002c6cSBruce M Simpson 4728f002c6cSBruce M Simpson ifma->ifma_protospec = inm; 4738f002c6cSBruce M Simpson *pinm = inm; 4748f002c6cSBruce M Simpson 4758f002c6cSBruce M Simpson out_locked: 476f3e1324bSStephen Hurd IN6_MULTI_LIST_UNLOCK(); 477137f91e8SJohn Baldwin IF_ADDR_WUNLOCK(ifp); 4788f002c6cSBruce M Simpson return (error); 4798f002c6cSBruce M Simpson } 4808f002c6cSBruce M Simpson 4818f002c6cSBruce M Simpson /* 4828f002c6cSBruce M Simpson * Drop a reference to an in6_multi record. 4838f002c6cSBruce M Simpson * 4848f002c6cSBruce M Simpson * If the refcount drops to 0, free the in6_multi record and 4858f002c6cSBruce M Simpson * delete the underlying link-layer membership. 4868f002c6cSBruce M Simpson */ 487f3e1324bSStephen Hurd static void 488f3e1324bSStephen Hurd in6m_release(struct in6_multi *inm) 4898f002c6cSBruce M Simpson { 4908f002c6cSBruce M Simpson struct ifmultiaddr *ifma; 491f3e1324bSStephen Hurd struct ifnet *ifp; 4928f002c6cSBruce M Simpson 4938f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: refcount is %d", __func__, inm->in6m_refcount); 4948f002c6cSBruce M Simpson 495f3e1324bSStephen Hurd MPASS(inm->in6m_refcount == 0); 4968f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: freeing inm %p", __func__, inm); 4978f002c6cSBruce M Simpson 4988f002c6cSBruce M Simpson ifma = inm->in6m_ifma; 499f3e1324bSStephen Hurd ifp = inm->in6m_ifp; 500b6f6f880SMatt Macy MPASS(ifma->ifma_llifma == NULL); 5018f002c6cSBruce M Simpson 5028f002c6cSBruce M Simpson /* XXX this access is not covered by IF_ADDR_LOCK */ 5038f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: purging ifma %p", __func__, ifma); 504f3e1324bSStephen Hurd KASSERT(ifma->ifma_protospec == NULL, 505f3e1324bSStephen Hurd ("%s: ifma_protospec != NULL", __func__)); 506f9be0386SMatt Macy if (ifp == NULL) 507f9be0386SMatt Macy ifp = ifma->ifma_ifp; 5088f002c6cSBruce M Simpson 5093b9b6b17SEd Maste if (ifp != NULL) { 51015f8acc5SEd Maste CURVNET_SET(ifp->if_vnet); 5118f002c6cSBruce M Simpson in6m_purge(inm); 5128f002c6cSBruce M Simpson free(inm, M_IP6MADDR); 513b6f6f880SMatt Macy if_delmulti_ifma_flags(ifma, 1); 51415f8acc5SEd Maste CURVNET_RESTORE(); 515b6f6f880SMatt Macy if_rele(ifp); 5163b9b6b17SEd Maste } else { 5173b9b6b17SEd Maste in6m_purge(inm); 5183b9b6b17SEd Maste free(inm, M_IP6MADDR); 5193b9b6b17SEd Maste if_delmulti_ifma_flags(ifma, 1); 520b6f6f880SMatt Macy } 521f3e1324bSStephen Hurd } 522f3e1324bSStephen Hurd 523431f2b87SMark Johnston /* 524431f2b87SMark Johnston * Interface detach can happen in a taskqueue thread context, so we must use a 525431f2b87SMark Johnston * dedicated thread to avoid deadlocks when draining in6m_release tasks. 526431f2b87SMark Johnston */ 527431f2b87SMark Johnston TASKQUEUE_DEFINE_THREAD(in6m_free); 5289b1d850bSMark Johnston static struct in6_multi_head in6m_free_list = SLIST_HEAD_INITIALIZER(); 5299b1d850bSMark Johnston static void in6m_release_task(void *arg __unused, int pending __unused); 530b453d3d2SHans Petter Selasky static struct task in6m_free_task = TASK_INITIALIZER(0, in6m_release_task, NULL); 531f3e1324bSStephen Hurd 532f3e1324bSStephen Hurd void 533f3e1324bSStephen Hurd in6m_release_list_deferred(struct in6_multi_head *inmh) 534f3e1324bSStephen Hurd { 535f3e1324bSStephen Hurd if (SLIST_EMPTY(inmh)) 536f3e1324bSStephen Hurd return; 537f3e1324bSStephen Hurd mtx_lock(&in6_multi_free_mtx); 538f3e1324bSStephen Hurd SLIST_CONCAT(&in6m_free_list, inmh, in6_multi, in6m_nrele); 539f3e1324bSStephen Hurd mtx_unlock(&in6_multi_free_mtx); 540431f2b87SMark Johnston taskqueue_enqueue(taskqueue_in6m_free, &in6m_free_task); 541f3e1324bSStephen Hurd } 542f3e1324bSStephen Hurd 543f3e1324bSStephen Hurd void 5443689652cSHans Petter Selasky in6m_release_wait(void *arg __unused) 545dea72f06SHans Petter Selasky { 5463689652cSHans Petter Selasky 5473689652cSHans Petter Selasky /* 5483689652cSHans Petter Selasky * Make sure all pending multicast addresses are freed before 5493689652cSHans Petter Selasky * the VNET or network device is destroyed: 5503689652cSHans Petter Selasky */ 551431f2b87SMark Johnston taskqueue_drain_all(taskqueue_in6m_free); 552dea72f06SHans Petter Selasky } 5533689652cSHans Petter Selasky #ifdef VIMAGE 554f9461246SBjoern A. Zeeb /* XXX-BZ FIXME, see D24914. */ 5553689652cSHans Petter Selasky VNET_SYSUNINIT(in6m_release_wait, SI_SUB_PROTO_DOMAIN, SI_ORDER_FIRST, in6m_release_wait, NULL); 5563689652cSHans Petter Selasky #endif 557dea72f06SHans Petter Selasky 558dea72f06SHans Petter Selasky void 5592cd6ad76SHans Petter Selasky in6m_disconnect_locked(struct in6_multi_head *inmh, struct in6_multi *inm) 560f3e1324bSStephen Hurd { 561f3e1324bSStephen Hurd struct ifnet *ifp; 562f3e1324bSStephen Hurd struct ifaddr *ifa; 563f3e1324bSStephen Hurd struct in6_ifaddr *ifa6; 56466fe09d8SAndrew Gallatin struct in6_multi_mship *imm, *imm_tmp; 565b6f6f880SMatt Macy struct ifmultiaddr *ifma, *ll_ifma; 566f3e1324bSStephen Hurd 5672cd6ad76SHans Petter Selasky IN6_MULTI_LIST_LOCK_ASSERT(); 568f9be0386SMatt Macy 5692cd6ad76SHans Petter Selasky ifp = inm->in6m_ifp; 570f9be0386SMatt Macy if (ifp == NULL) 5712cd6ad76SHans Petter Selasky return; /* already called */ 5722cd6ad76SHans Petter Selasky 573f9be0386SMatt Macy inm->in6m_ifp = NULL; 574b6f6f880SMatt Macy IF_ADDR_WLOCK_ASSERT(ifp); 575b6f6f880SMatt Macy ifma = inm->in6m_ifma; 576f9be0386SMatt Macy if (ifma == NULL) 577f9be0386SMatt Macy return; 578b6f6f880SMatt Macy 579b6f6f880SMatt Macy if_ref(ifp); 580f9be0386SMatt Macy if (ifma->ifma_flags & IFMA_F_ENQUEUED) { 581d7c5a620SMatt Macy CK_STAILQ_REMOVE(&ifp->if_multiaddrs, ifma, ifmultiaddr, ifma_link); 582f9be0386SMatt Macy ifma->ifma_flags &= ~IFMA_F_ENQUEUED; 583f9be0386SMatt Macy } 584b6f6f880SMatt Macy MCDPRINTF("removed ifma: %p from %s\n", ifma, ifp->if_xname); 585b6f6f880SMatt Macy if ((ll_ifma = ifma->ifma_llifma) != NULL) { 586b6f6f880SMatt Macy MPASS(ifma != ll_ifma); 587b6f6f880SMatt Macy ifma->ifma_llifma = NULL; 588b6f6f880SMatt Macy MPASS(ll_ifma->ifma_llifma == NULL); 589b6f6f880SMatt Macy MPASS(ll_ifma->ifma_ifp == ifp); 590b6f6f880SMatt Macy if (--ll_ifma->ifma_refcount == 0) { 591f9be0386SMatt Macy if (ll_ifma->ifma_flags & IFMA_F_ENQUEUED) { 592d7c5a620SMatt Macy CK_STAILQ_REMOVE(&ifp->if_multiaddrs, ll_ifma, ifmultiaddr, ifma_link); 593d3878608SMatt Macy ll_ifma->ifma_flags &= ~IFMA_F_ENQUEUED; 594f9be0386SMatt Macy } 595b6f6f880SMatt Macy MCDPRINTF("removed ll_ifma: %p from %s\n", ll_ifma, ifp->if_xname); 596b6f6f880SMatt Macy if_freemulti(ll_ifma); 597b6f6f880SMatt Macy } 598b6f6f880SMatt Macy } 599d7c5a620SMatt Macy CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { 600f3e1324bSStephen Hurd if (ifa->ifa_addr->sa_family != AF_INET6) 601f3e1324bSStephen Hurd continue; 602f3e1324bSStephen Hurd ifa6 = (void *)ifa; 60366fe09d8SAndrew Gallatin LIST_FOREACH_SAFE(imm, &ifa6->ia6_memberships, 60466fe09d8SAndrew Gallatin i6mm_chain, imm_tmp) { 605b6f6f880SMatt Macy if (inm == imm->i6mm_maddr) { 606b6f6f880SMatt Macy LIST_REMOVE(imm, i6mm_chain); 607b6f6f880SMatt Macy free(imm, M_IP6MADDR); 6082cd6ad76SHans Petter Selasky in6m_rele_locked(inmh, inm); 609f3e1324bSStephen Hurd } 610f3e1324bSStephen Hurd } 611b6f6f880SMatt Macy } 612b6f6f880SMatt Macy } 613b6f6f880SMatt Macy 614f3e1324bSStephen Hurd static void 6159b1d850bSMark Johnston in6m_release_task(void *arg __unused, int pending __unused) 616f3e1324bSStephen Hurd { 617f3e1324bSStephen Hurd struct in6_multi_head in6m_free_tmp; 618f3e1324bSStephen Hurd struct in6_multi *inm, *tinm; 619f3e1324bSStephen Hurd 620f3e1324bSStephen Hurd SLIST_INIT(&in6m_free_tmp); 621f3e1324bSStephen Hurd mtx_lock(&in6_multi_free_mtx); 622f3e1324bSStephen Hurd SLIST_CONCAT(&in6m_free_tmp, &in6m_free_list, in6_multi, in6m_nrele); 623f3e1324bSStephen Hurd mtx_unlock(&in6_multi_free_mtx); 624f3e1324bSStephen Hurd IN6_MULTI_LOCK(); 625f3e1324bSStephen Hurd SLIST_FOREACH_SAFE(inm, &in6m_free_tmp, in6m_nrele, tinm) { 626f3e1324bSStephen Hurd SLIST_REMOVE_HEAD(&in6m_free_tmp, in6m_nrele); 627f3e1324bSStephen Hurd in6m_release(inm); 628f3e1324bSStephen Hurd } 629f3e1324bSStephen Hurd IN6_MULTI_UNLOCK(); 6308f002c6cSBruce M Simpson } 6318f002c6cSBruce M Simpson 6328f002c6cSBruce M Simpson /* 6338f002c6cSBruce M Simpson * Clear recorded source entries for a group. 6348f002c6cSBruce M Simpson * Used by the MLD code. Caller must hold the IN6_MULTI lock. 6358f002c6cSBruce M Simpson * FIXME: Should reap. 6368f002c6cSBruce M Simpson */ 6378f002c6cSBruce M Simpson void 6388f002c6cSBruce M Simpson in6m_clear_recorded(struct in6_multi *inm) 6398f002c6cSBruce M Simpson { 6408f002c6cSBruce M Simpson struct ip6_msource *ims; 6418f002c6cSBruce M Simpson 642f3e1324bSStephen Hurd IN6_MULTI_LIST_LOCK_ASSERT(); 6438f002c6cSBruce M Simpson 6448f002c6cSBruce M Simpson RB_FOREACH(ims, ip6_msource_tree, &inm->in6m_srcs) { 6458f002c6cSBruce M Simpson if (ims->im6s_stp) { 6468f002c6cSBruce M Simpson ims->im6s_stp = 0; 6478f002c6cSBruce M Simpson --inm->in6m_st[1].iss_rec; 6488f002c6cSBruce M Simpson } 6498f002c6cSBruce M Simpson } 6508f002c6cSBruce M Simpson KASSERT(inm->in6m_st[1].iss_rec == 0, 6518f002c6cSBruce M Simpson ("%s: iss_rec %d not 0", __func__, inm->in6m_st[1].iss_rec)); 6528f002c6cSBruce M Simpson } 6538f002c6cSBruce M Simpson 6548f002c6cSBruce M Simpson /* 6558f002c6cSBruce M Simpson * Record a source as pending for a Source-Group MLDv2 query. 6568f002c6cSBruce M Simpson * This lives here as it modifies the shared tree. 6578f002c6cSBruce M Simpson * 6588f002c6cSBruce M Simpson * inm is the group descriptor. 6598f002c6cSBruce M Simpson * naddr is the address of the source to record in network-byte order. 6608f002c6cSBruce M Simpson * 6618f002c6cSBruce M Simpson * If the net.inet6.mld.sgalloc sysctl is non-zero, we will 6628f002c6cSBruce M Simpson * lazy-allocate a source node in response to an SG query. 6638f002c6cSBruce M Simpson * Otherwise, no allocation is performed. This saves some memory 6648f002c6cSBruce M Simpson * with the trade-off that the source will not be reported to the 6658f002c6cSBruce M Simpson * router if joined in the window between the query response and 6668f002c6cSBruce M Simpson * the group actually being joined on the local host. 6678f002c6cSBruce M Simpson * 6688f002c6cSBruce M Simpson * VIMAGE: XXX: Currently the mld_sgalloc feature has been removed. 6698f002c6cSBruce M Simpson * This turns off the allocation of a recorded source entry if 6708f002c6cSBruce M Simpson * the group has not been joined. 6718f002c6cSBruce M Simpson * 6728f002c6cSBruce M Simpson * Return 0 if the source didn't exist or was already marked as recorded. 6738f002c6cSBruce M Simpson * Return 1 if the source was marked as recorded by this function. 674a4641f4eSPedro F. Giffuni * Return <0 if any error occurred (negated errno code). 6758f002c6cSBruce M Simpson */ 6768f002c6cSBruce M Simpson int 6778f002c6cSBruce M Simpson in6m_record_source(struct in6_multi *inm, const struct in6_addr *addr) 6788f002c6cSBruce M Simpson { 6798f002c6cSBruce M Simpson struct ip6_msource find; 6808f002c6cSBruce M Simpson struct ip6_msource *ims, *nims; 6818f002c6cSBruce M Simpson 682f3e1324bSStephen Hurd IN6_MULTI_LIST_LOCK_ASSERT(); 6838f002c6cSBruce M Simpson 6848f002c6cSBruce M Simpson find.im6s_addr = *addr; 6858f002c6cSBruce M Simpson ims = RB_FIND(ip6_msource_tree, &inm->in6m_srcs, &find); 6868f002c6cSBruce M Simpson if (ims && ims->im6s_stp) 6878f002c6cSBruce M Simpson return (0); 6888f002c6cSBruce M Simpson if (ims == NULL) { 6898f002c6cSBruce M Simpson if (inm->in6m_nsrc == in6_mcast_maxgrpsrc) 6908f002c6cSBruce M Simpson return (-ENOSPC); 6918f002c6cSBruce M Simpson nims = malloc(sizeof(struct ip6_msource), M_IP6MSOURCE, 6928f002c6cSBruce M Simpson M_NOWAIT | M_ZERO); 6938f002c6cSBruce M Simpson if (nims == NULL) 6948f002c6cSBruce M Simpson return (-ENOMEM); 6958f002c6cSBruce M Simpson nims->im6s_addr = find.im6s_addr; 6968f002c6cSBruce M Simpson RB_INSERT(ip6_msource_tree, &inm->in6m_srcs, nims); 6978f002c6cSBruce M Simpson ++inm->in6m_nsrc; 6988f002c6cSBruce M Simpson ims = nims; 6998f002c6cSBruce M Simpson } 7008f002c6cSBruce M Simpson 7018f002c6cSBruce M Simpson /* 7028f002c6cSBruce M Simpson * Mark the source as recorded and update the recorded 7038f002c6cSBruce M Simpson * source count. 7048f002c6cSBruce M Simpson */ 7058f002c6cSBruce M Simpson ++ims->im6s_stp; 7068f002c6cSBruce M Simpson ++inm->in6m_st[1].iss_rec; 7078f002c6cSBruce M Simpson 7088f002c6cSBruce M Simpson return (1); 7098f002c6cSBruce M Simpson } 7108f002c6cSBruce M Simpson 7118f002c6cSBruce M Simpson /* 7128f002c6cSBruce M Simpson * Return a pointer to an in6_msource owned by an in6_mfilter, 7138f002c6cSBruce M Simpson * given its source address. 7148f002c6cSBruce M Simpson * Lazy-allocate if needed. If this is a new entry its filter state is 7158f002c6cSBruce M Simpson * undefined at t0. 7168f002c6cSBruce M Simpson * 7178f002c6cSBruce M Simpson * imf is the filter set being modified. 7188f002c6cSBruce M Simpson * addr is the source address. 7198f002c6cSBruce M Simpson * 7208f002c6cSBruce M Simpson * SMPng: May be called with locks held; malloc must not block. 7218f002c6cSBruce M Simpson */ 7228f002c6cSBruce M Simpson static int 7238f002c6cSBruce M Simpson im6f_get_source(struct in6_mfilter *imf, const struct sockaddr_in6 *psin, 7248f002c6cSBruce M Simpson struct in6_msource **plims) 7258f002c6cSBruce M Simpson { 7268f002c6cSBruce M Simpson struct ip6_msource find; 7278f002c6cSBruce M Simpson struct ip6_msource *ims, *nims; 7288f002c6cSBruce M Simpson struct in6_msource *lims; 7298f002c6cSBruce M Simpson int error; 7308f002c6cSBruce M Simpson 7318f002c6cSBruce M Simpson error = 0; 7328f002c6cSBruce M Simpson ims = NULL; 7338f002c6cSBruce M Simpson lims = NULL; 7348f002c6cSBruce M Simpson 7358f002c6cSBruce M Simpson find.im6s_addr = psin->sin6_addr; 7368f002c6cSBruce M Simpson ims = RB_FIND(ip6_msource_tree, &imf->im6f_sources, &find); 7378f002c6cSBruce M Simpson lims = (struct in6_msource *)ims; 7388f002c6cSBruce M Simpson if (lims == NULL) { 7398f002c6cSBruce M Simpson if (imf->im6f_nsrc == in6_mcast_maxsocksrc) 7408f002c6cSBruce M Simpson return (ENOSPC); 7418f002c6cSBruce M Simpson nims = malloc(sizeof(struct in6_msource), M_IN6MFILTER, 7428f002c6cSBruce M Simpson M_NOWAIT | M_ZERO); 7438f002c6cSBruce M Simpson if (nims == NULL) 7448f002c6cSBruce M Simpson return (ENOMEM); 7458f002c6cSBruce M Simpson lims = (struct in6_msource *)nims; 7468f002c6cSBruce M Simpson lims->im6s_addr = find.im6s_addr; 7478f002c6cSBruce M Simpson lims->im6sl_st[0] = MCAST_UNDEFINED; 7488f002c6cSBruce M Simpson RB_INSERT(ip6_msource_tree, &imf->im6f_sources, nims); 7498f002c6cSBruce M Simpson ++imf->im6f_nsrc; 7508f002c6cSBruce M Simpson } 7518f002c6cSBruce M Simpson 7528f002c6cSBruce M Simpson *plims = lims; 7538f002c6cSBruce M Simpson 7548f002c6cSBruce M Simpson return (error); 7558f002c6cSBruce M Simpson } 7568f002c6cSBruce M Simpson 7578f002c6cSBruce M Simpson /* 7588f002c6cSBruce M Simpson * Graft a source entry into an existing socket-layer filter set, 7598f002c6cSBruce M Simpson * maintaining any required invariants and checking allocations. 7608f002c6cSBruce M Simpson * 7618f002c6cSBruce M Simpson * The source is marked as being in the new filter mode at t1. 7628f002c6cSBruce M Simpson * 7638f002c6cSBruce M Simpson * Return the pointer to the new node, otherwise return NULL. 7648f002c6cSBruce M Simpson */ 7658f002c6cSBruce M Simpson static struct in6_msource * 7668f002c6cSBruce M Simpson im6f_graft(struct in6_mfilter *imf, const uint8_t st1, 7678f002c6cSBruce M Simpson const struct sockaddr_in6 *psin) 7688f002c6cSBruce M Simpson { 7698f002c6cSBruce M Simpson struct ip6_msource *nims; 7708f002c6cSBruce M Simpson struct in6_msource *lims; 7718f002c6cSBruce M Simpson 7728f002c6cSBruce M Simpson nims = malloc(sizeof(struct in6_msource), M_IN6MFILTER, 7738f002c6cSBruce M Simpson M_NOWAIT | M_ZERO); 7748f002c6cSBruce M Simpson if (nims == NULL) 7758f002c6cSBruce M Simpson return (NULL); 7768f002c6cSBruce M Simpson lims = (struct in6_msource *)nims; 7778f002c6cSBruce M Simpson lims->im6s_addr = psin->sin6_addr; 7788f002c6cSBruce M Simpson lims->im6sl_st[0] = MCAST_UNDEFINED; 7798f002c6cSBruce M Simpson lims->im6sl_st[1] = st1; 7808f002c6cSBruce M Simpson RB_INSERT(ip6_msource_tree, &imf->im6f_sources, nims); 7818f002c6cSBruce M Simpson ++imf->im6f_nsrc; 7828f002c6cSBruce M Simpson 7838f002c6cSBruce M Simpson return (lims); 7848f002c6cSBruce M Simpson } 7858f002c6cSBruce M Simpson 7868f002c6cSBruce M Simpson /* 7878f002c6cSBruce M Simpson * Prune a source entry from an existing socket-layer filter set, 7888f002c6cSBruce M Simpson * maintaining any required invariants and checking allocations. 7898f002c6cSBruce M Simpson * 7908f002c6cSBruce M Simpson * The source is marked as being left at t1, it is not freed. 7918f002c6cSBruce M Simpson * 7928f002c6cSBruce M Simpson * Return 0 if no error occurred, otherwise return an errno value. 7938f002c6cSBruce M Simpson */ 7948f002c6cSBruce M Simpson static int 7958f002c6cSBruce M Simpson im6f_prune(struct in6_mfilter *imf, const struct sockaddr_in6 *psin) 7968f002c6cSBruce M Simpson { 7978f002c6cSBruce M Simpson struct ip6_msource find; 7988f002c6cSBruce M Simpson struct ip6_msource *ims; 7998f002c6cSBruce M Simpson struct in6_msource *lims; 8008f002c6cSBruce M Simpson 8018f002c6cSBruce M Simpson find.im6s_addr = psin->sin6_addr; 8028f002c6cSBruce M Simpson ims = RB_FIND(ip6_msource_tree, &imf->im6f_sources, &find); 8038f002c6cSBruce M Simpson if (ims == NULL) 8048f002c6cSBruce M Simpson return (ENOENT); 8058f002c6cSBruce M Simpson lims = (struct in6_msource *)ims; 8068f002c6cSBruce M Simpson lims->im6sl_st[1] = MCAST_UNDEFINED; 8078f002c6cSBruce M Simpson return (0); 8088f002c6cSBruce M Simpson } 8098f002c6cSBruce M Simpson 8108f002c6cSBruce M Simpson /* 8118f002c6cSBruce M Simpson * Revert socket-layer filter set deltas at t1 to t0 state. 8128f002c6cSBruce M Simpson */ 8138f002c6cSBruce M Simpson static void 8148f002c6cSBruce M Simpson im6f_rollback(struct in6_mfilter *imf) 8158f002c6cSBruce M Simpson { 8168f002c6cSBruce M Simpson struct ip6_msource *ims, *tims; 8178f002c6cSBruce M Simpson struct in6_msource *lims; 8188f002c6cSBruce M Simpson 8198f002c6cSBruce M Simpson RB_FOREACH_SAFE(ims, ip6_msource_tree, &imf->im6f_sources, tims) { 8208f002c6cSBruce M Simpson lims = (struct in6_msource *)ims; 8218f002c6cSBruce M Simpson if (lims->im6sl_st[0] == lims->im6sl_st[1]) { 8228f002c6cSBruce M Simpson /* no change at t1 */ 8238f002c6cSBruce M Simpson continue; 8248f002c6cSBruce M Simpson } else if (lims->im6sl_st[0] != MCAST_UNDEFINED) { 8258f002c6cSBruce M Simpson /* revert change to existing source at t1 */ 8268f002c6cSBruce M Simpson lims->im6sl_st[1] = lims->im6sl_st[0]; 8278f002c6cSBruce M Simpson } else { 8288f002c6cSBruce M Simpson /* revert source added t1 */ 8298f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: free ims %p", __func__, ims); 8308f002c6cSBruce M Simpson RB_REMOVE(ip6_msource_tree, &imf->im6f_sources, ims); 8318f002c6cSBruce M Simpson free(ims, M_IN6MFILTER); 8328f002c6cSBruce M Simpson imf->im6f_nsrc--; 8338f002c6cSBruce M Simpson } 8348f002c6cSBruce M Simpson } 8358f002c6cSBruce M Simpson imf->im6f_st[1] = imf->im6f_st[0]; 8368f002c6cSBruce M Simpson } 8378f002c6cSBruce M Simpson 8388f002c6cSBruce M Simpson /* 8398f002c6cSBruce M Simpson * Mark socket-layer filter set as INCLUDE {} at t1. 8408f002c6cSBruce M Simpson */ 8418f002c6cSBruce M Simpson static void 8428f002c6cSBruce M Simpson im6f_leave(struct in6_mfilter *imf) 8438f002c6cSBruce M Simpson { 8448f002c6cSBruce M Simpson struct ip6_msource *ims; 8458f002c6cSBruce M Simpson struct in6_msource *lims; 8468f002c6cSBruce M Simpson 8478f002c6cSBruce M Simpson RB_FOREACH(ims, ip6_msource_tree, &imf->im6f_sources) { 8488f002c6cSBruce M Simpson lims = (struct in6_msource *)ims; 8498f002c6cSBruce M Simpson lims->im6sl_st[1] = MCAST_UNDEFINED; 8508f002c6cSBruce M Simpson } 8518f002c6cSBruce M Simpson imf->im6f_st[1] = MCAST_INCLUDE; 8528f002c6cSBruce M Simpson } 8538f002c6cSBruce M Simpson 8548f002c6cSBruce M Simpson /* 8558f002c6cSBruce M Simpson * Mark socket-layer filter set deltas as committed. 8568f002c6cSBruce M Simpson */ 8578f002c6cSBruce M Simpson static void 8588f002c6cSBruce M Simpson im6f_commit(struct in6_mfilter *imf) 8598f002c6cSBruce M Simpson { 8608f002c6cSBruce M Simpson struct ip6_msource *ims; 8618f002c6cSBruce M Simpson struct in6_msource *lims; 8628f002c6cSBruce M Simpson 8638f002c6cSBruce M Simpson RB_FOREACH(ims, ip6_msource_tree, &imf->im6f_sources) { 8648f002c6cSBruce M Simpson lims = (struct in6_msource *)ims; 8658f002c6cSBruce M Simpson lims->im6sl_st[0] = lims->im6sl_st[1]; 8668f002c6cSBruce M Simpson } 8678f002c6cSBruce M Simpson imf->im6f_st[0] = imf->im6f_st[1]; 8688f002c6cSBruce M Simpson } 8698f002c6cSBruce M Simpson 8708f002c6cSBruce M Simpson /* 8718f002c6cSBruce M Simpson * Reap unreferenced sources from socket-layer filter set. 8728f002c6cSBruce M Simpson */ 8738f002c6cSBruce M Simpson static void 8748f002c6cSBruce M Simpson im6f_reap(struct in6_mfilter *imf) 8758f002c6cSBruce M Simpson { 8768f002c6cSBruce M Simpson struct ip6_msource *ims, *tims; 8778f002c6cSBruce M Simpson struct in6_msource *lims; 8788f002c6cSBruce M Simpson 8798f002c6cSBruce M Simpson RB_FOREACH_SAFE(ims, ip6_msource_tree, &imf->im6f_sources, tims) { 8808f002c6cSBruce M Simpson lims = (struct in6_msource *)ims; 8818f002c6cSBruce M Simpson if ((lims->im6sl_st[0] == MCAST_UNDEFINED) && 8828f002c6cSBruce M Simpson (lims->im6sl_st[1] == MCAST_UNDEFINED)) { 8838f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: free lims %p", __func__, ims); 8848f002c6cSBruce M Simpson RB_REMOVE(ip6_msource_tree, &imf->im6f_sources, ims); 8858f002c6cSBruce M Simpson free(ims, M_IN6MFILTER); 8868f002c6cSBruce M Simpson imf->im6f_nsrc--; 8878f002c6cSBruce M Simpson } 8888f002c6cSBruce M Simpson } 8898f002c6cSBruce M Simpson } 8908f002c6cSBruce M Simpson 8918f002c6cSBruce M Simpson /* 8928f002c6cSBruce M Simpson * Purge socket-layer filter set. 8938f002c6cSBruce M Simpson */ 8948f002c6cSBruce M Simpson static void 8958f002c6cSBruce M Simpson im6f_purge(struct in6_mfilter *imf) 8968f002c6cSBruce M Simpson { 8978f002c6cSBruce M Simpson struct ip6_msource *ims, *tims; 8988f002c6cSBruce M Simpson 8998f002c6cSBruce M Simpson RB_FOREACH_SAFE(ims, ip6_msource_tree, &imf->im6f_sources, tims) { 9008f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: free ims %p", __func__, ims); 9018f002c6cSBruce M Simpson RB_REMOVE(ip6_msource_tree, &imf->im6f_sources, ims); 9028f002c6cSBruce M Simpson free(ims, M_IN6MFILTER); 9038f002c6cSBruce M Simpson imf->im6f_nsrc--; 9048f002c6cSBruce M Simpson } 9058f002c6cSBruce M Simpson imf->im6f_st[0] = imf->im6f_st[1] = MCAST_UNDEFINED; 9068f002c6cSBruce M Simpson KASSERT(RB_EMPTY(&imf->im6f_sources), 9078f002c6cSBruce M Simpson ("%s: im6f_sources not empty", __func__)); 9088f002c6cSBruce M Simpson } 9098f002c6cSBruce M Simpson 9108f002c6cSBruce M Simpson /* 9118f002c6cSBruce M Simpson * Look up a source filter entry for a multicast group. 9128f002c6cSBruce M Simpson * 9138f002c6cSBruce M Simpson * inm is the group descriptor to work with. 9148f002c6cSBruce M Simpson * addr is the IPv6 address to look up. 9158f002c6cSBruce M Simpson * noalloc may be non-zero to suppress allocation of sources. 9168f002c6cSBruce M Simpson * *pims will be set to the address of the retrieved or allocated source. 9178f002c6cSBruce M Simpson * 9188f002c6cSBruce M Simpson * SMPng: NOTE: may be called with locks held. 9198f002c6cSBruce M Simpson * Return 0 if successful, otherwise return a non-zero error code. 9208f002c6cSBruce M Simpson */ 9218f002c6cSBruce M Simpson static int 9228f002c6cSBruce M Simpson in6m_get_source(struct in6_multi *inm, const struct in6_addr *addr, 9238f002c6cSBruce M Simpson const int noalloc, struct ip6_msource **pims) 9248f002c6cSBruce M Simpson { 9258f002c6cSBruce M Simpson struct ip6_msource find; 9268f002c6cSBruce M Simpson struct ip6_msource *ims, *nims; 9278f002c6cSBruce M Simpson #ifdef KTR 9288f002c6cSBruce M Simpson char ip6tbuf[INET6_ADDRSTRLEN]; 9298f002c6cSBruce M Simpson #endif 9308f002c6cSBruce M Simpson 9318f002c6cSBruce M Simpson find.im6s_addr = *addr; 9328f002c6cSBruce M Simpson ims = RB_FIND(ip6_msource_tree, &inm->in6m_srcs, &find); 9338f002c6cSBruce M Simpson if (ims == NULL && !noalloc) { 9348f002c6cSBruce M Simpson if (inm->in6m_nsrc == in6_mcast_maxgrpsrc) 9358f002c6cSBruce M Simpson return (ENOSPC); 9368f002c6cSBruce M Simpson nims = malloc(sizeof(struct ip6_msource), M_IP6MSOURCE, 9378f002c6cSBruce M Simpson M_NOWAIT | M_ZERO); 9388f002c6cSBruce M Simpson if (nims == NULL) 9398f002c6cSBruce M Simpson return (ENOMEM); 9408f002c6cSBruce M Simpson nims->im6s_addr = *addr; 9418f002c6cSBruce M Simpson RB_INSERT(ip6_msource_tree, &inm->in6m_srcs, nims); 9428f002c6cSBruce M Simpson ++inm->in6m_nsrc; 9438f002c6cSBruce M Simpson ims = nims; 9448f002c6cSBruce M Simpson CTR3(KTR_MLD, "%s: allocated %s as %p", __func__, 9458f002c6cSBruce M Simpson ip6_sprintf(ip6tbuf, addr), ims); 9468f002c6cSBruce M Simpson } 9478f002c6cSBruce M Simpson 9488f002c6cSBruce M Simpson *pims = ims; 9498f002c6cSBruce M Simpson return (0); 9508f002c6cSBruce M Simpson } 9518f002c6cSBruce M Simpson 9528f002c6cSBruce M Simpson /* 9538f002c6cSBruce M Simpson * Merge socket-layer source into MLD-layer source. 9548f002c6cSBruce M Simpson * If rollback is non-zero, perform the inverse of the merge. 9558f002c6cSBruce M Simpson */ 9568f002c6cSBruce M Simpson static void 9578f002c6cSBruce M Simpson im6s_merge(struct ip6_msource *ims, const struct in6_msource *lims, 9588f002c6cSBruce M Simpson const int rollback) 9598f002c6cSBruce M Simpson { 9608f002c6cSBruce M Simpson int n = rollback ? -1 : 1; 9618f002c6cSBruce M Simpson #ifdef KTR 9628f002c6cSBruce M Simpson char ip6tbuf[INET6_ADDRSTRLEN]; 9638f002c6cSBruce M Simpson 9648f002c6cSBruce M Simpson ip6_sprintf(ip6tbuf, &lims->im6s_addr); 9658f002c6cSBruce M Simpson #endif 9668f002c6cSBruce M Simpson 9678f002c6cSBruce M Simpson if (lims->im6sl_st[0] == MCAST_EXCLUDE) { 9688f002c6cSBruce M Simpson CTR3(KTR_MLD, "%s: t1 ex -= %d on %s", __func__, n, ip6tbuf); 9698f002c6cSBruce M Simpson ims->im6s_st[1].ex -= n; 9708f002c6cSBruce M Simpson } else if (lims->im6sl_st[0] == MCAST_INCLUDE) { 9718f002c6cSBruce M Simpson CTR3(KTR_MLD, "%s: t1 in -= %d on %s", __func__, n, ip6tbuf); 9728f002c6cSBruce M Simpson ims->im6s_st[1].in -= n; 9738f002c6cSBruce M Simpson } 9748f002c6cSBruce M Simpson 9758f002c6cSBruce M Simpson if (lims->im6sl_st[1] == MCAST_EXCLUDE) { 9768f002c6cSBruce M Simpson CTR3(KTR_MLD, "%s: t1 ex += %d on %s", __func__, n, ip6tbuf); 9778f002c6cSBruce M Simpson ims->im6s_st[1].ex += n; 9788f002c6cSBruce M Simpson } else if (lims->im6sl_st[1] == MCAST_INCLUDE) { 9798f002c6cSBruce M Simpson CTR3(KTR_MLD, "%s: t1 in += %d on %s", __func__, n, ip6tbuf); 9808f002c6cSBruce M Simpson ims->im6s_st[1].in += n; 9818f002c6cSBruce M Simpson } 9828f002c6cSBruce M Simpson } 9838f002c6cSBruce M Simpson 9848f002c6cSBruce M Simpson /* 9858f002c6cSBruce M Simpson * Atomically update the global in6_multi state, when a membership's 9868f002c6cSBruce M Simpson * filter list is being updated in any way. 9878f002c6cSBruce M Simpson * 9888f002c6cSBruce M Simpson * imf is the per-inpcb-membership group filter pointer. 9898f002c6cSBruce M Simpson * A fake imf may be passed for in-kernel consumers. 9908f002c6cSBruce M Simpson * 9918f002c6cSBruce M Simpson * XXX This is a candidate for a set-symmetric-difference style loop 9928f002c6cSBruce M Simpson * which would eliminate the repeated lookup from root of ims nodes, 9938f002c6cSBruce M Simpson * as they share the same key space. 9948f002c6cSBruce M Simpson * 9958f002c6cSBruce M Simpson * If any error occurred this function will back out of refcounts 9968f002c6cSBruce M Simpson * and return a non-zero value. 9978f002c6cSBruce M Simpson */ 9988f002c6cSBruce M Simpson static int 9998f002c6cSBruce M Simpson in6m_merge(struct in6_multi *inm, /*const*/ struct in6_mfilter *imf) 10008f002c6cSBruce M Simpson { 10018f002c6cSBruce M Simpson struct ip6_msource *ims, *nims; 10028f002c6cSBruce M Simpson struct in6_msource *lims; 10038f002c6cSBruce M Simpson int schanged, error; 10048f002c6cSBruce M Simpson int nsrc0, nsrc1; 10058f002c6cSBruce M Simpson 10068f002c6cSBruce M Simpson schanged = 0; 10078f002c6cSBruce M Simpson error = 0; 10088f002c6cSBruce M Simpson nsrc1 = nsrc0 = 0; 1009f3e1324bSStephen Hurd IN6_MULTI_LIST_LOCK_ASSERT(); 10108f002c6cSBruce M Simpson 10118f002c6cSBruce M Simpson /* 10128f002c6cSBruce M Simpson * Update the source filters first, as this may fail. 10138f002c6cSBruce M Simpson * Maintain count of in-mode filters at t0, t1. These are 10148f002c6cSBruce M Simpson * used to work out if we transition into ASM mode or not. 10158f002c6cSBruce M Simpson * Maintain a count of source filters whose state was 10168f002c6cSBruce M Simpson * actually modified by this operation. 10178f002c6cSBruce M Simpson */ 10188f002c6cSBruce M Simpson RB_FOREACH(ims, ip6_msource_tree, &imf->im6f_sources) { 10198f002c6cSBruce M Simpson lims = (struct in6_msource *)ims; 10208f002c6cSBruce M Simpson if (lims->im6sl_st[0] == imf->im6f_st[0]) nsrc0++; 10218f002c6cSBruce M Simpson if (lims->im6sl_st[1] == imf->im6f_st[1]) nsrc1++; 10228f002c6cSBruce M Simpson if (lims->im6sl_st[0] == lims->im6sl_st[1]) continue; 10238f002c6cSBruce M Simpson error = in6m_get_source(inm, &lims->im6s_addr, 0, &nims); 10248f002c6cSBruce M Simpson ++schanged; 10258f002c6cSBruce M Simpson if (error) 10268f002c6cSBruce M Simpson break; 10278f002c6cSBruce M Simpson im6s_merge(nims, lims, 0); 10288f002c6cSBruce M Simpson } 10298f002c6cSBruce M Simpson if (error) { 10308f002c6cSBruce M Simpson struct ip6_msource *bims; 10318f002c6cSBruce M Simpson 10328f002c6cSBruce M Simpson RB_FOREACH_REVERSE_FROM(ims, ip6_msource_tree, nims) { 10338f002c6cSBruce M Simpson lims = (struct in6_msource *)ims; 10348f002c6cSBruce M Simpson if (lims->im6sl_st[0] == lims->im6sl_st[1]) 10358f002c6cSBruce M Simpson continue; 10368f002c6cSBruce M Simpson (void)in6m_get_source(inm, &lims->im6s_addr, 1, &bims); 10378f002c6cSBruce M Simpson if (bims == NULL) 10388f002c6cSBruce M Simpson continue; 10398f002c6cSBruce M Simpson im6s_merge(bims, lims, 1); 10408f002c6cSBruce M Simpson } 10418f002c6cSBruce M Simpson goto out_reap; 10428f002c6cSBruce M Simpson } 10438f002c6cSBruce M Simpson 10448f002c6cSBruce M Simpson CTR3(KTR_MLD, "%s: imf filters in-mode: %d at t0, %d at t1", 10458f002c6cSBruce M Simpson __func__, nsrc0, nsrc1); 10468f002c6cSBruce M Simpson 10478f002c6cSBruce M Simpson /* Handle transition between INCLUDE {n} and INCLUDE {} on socket. */ 10488f002c6cSBruce M Simpson if (imf->im6f_st[0] == imf->im6f_st[1] && 10498f002c6cSBruce M Simpson imf->im6f_st[1] == MCAST_INCLUDE) { 10508f002c6cSBruce M Simpson if (nsrc1 == 0) { 10518f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: --in on inm at t1", __func__); 10528f002c6cSBruce M Simpson --inm->in6m_st[1].iss_in; 10538f002c6cSBruce M Simpson } 10548f002c6cSBruce M Simpson } 10558f002c6cSBruce M Simpson 10568f002c6cSBruce M Simpson /* Handle filter mode transition on socket. */ 10578f002c6cSBruce M Simpson if (imf->im6f_st[0] != imf->im6f_st[1]) { 10588f002c6cSBruce M Simpson CTR3(KTR_MLD, "%s: imf transition %d to %d", 10598f002c6cSBruce M Simpson __func__, imf->im6f_st[0], imf->im6f_st[1]); 10608f002c6cSBruce M Simpson 10618f002c6cSBruce M Simpson if (imf->im6f_st[0] == MCAST_EXCLUDE) { 10628f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: --ex on inm at t1", __func__); 10638f002c6cSBruce M Simpson --inm->in6m_st[1].iss_ex; 10648f002c6cSBruce M Simpson } else if (imf->im6f_st[0] == MCAST_INCLUDE) { 10658f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: --in on inm at t1", __func__); 10668f002c6cSBruce M Simpson --inm->in6m_st[1].iss_in; 10678f002c6cSBruce M Simpson } 10688f002c6cSBruce M Simpson 10698f002c6cSBruce M Simpson if (imf->im6f_st[1] == MCAST_EXCLUDE) { 10708f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: ex++ on inm at t1", __func__); 10718f002c6cSBruce M Simpson inm->in6m_st[1].iss_ex++; 10728f002c6cSBruce M Simpson } else if (imf->im6f_st[1] == MCAST_INCLUDE && nsrc1 > 0) { 10738f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: in++ on inm at t1", __func__); 10748f002c6cSBruce M Simpson inm->in6m_st[1].iss_in++; 10758f002c6cSBruce M Simpson } 10768f002c6cSBruce M Simpson } 10778f002c6cSBruce M Simpson 10788f002c6cSBruce M Simpson /* 10798f002c6cSBruce M Simpson * Track inm filter state in terms of listener counts. 10808f002c6cSBruce M Simpson * If there are any exclusive listeners, stack-wide 10818f002c6cSBruce M Simpson * membership is exclusive. 10828f002c6cSBruce M Simpson * Otherwise, if only inclusive listeners, stack-wide is inclusive. 10838f002c6cSBruce M Simpson * If no listeners remain, state is undefined at t1, 10848f002c6cSBruce M Simpson * and the MLD lifecycle for this group should finish. 10858f002c6cSBruce M Simpson */ 10868f002c6cSBruce M Simpson if (inm->in6m_st[1].iss_ex > 0) { 10878f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: transition to EX", __func__); 10888f002c6cSBruce M Simpson inm->in6m_st[1].iss_fmode = MCAST_EXCLUDE; 10898f002c6cSBruce M Simpson } else if (inm->in6m_st[1].iss_in > 0) { 10908f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: transition to IN", __func__); 10918f002c6cSBruce M Simpson inm->in6m_st[1].iss_fmode = MCAST_INCLUDE; 10928f002c6cSBruce M Simpson } else { 10938f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: transition to UNDEF", __func__); 10948f002c6cSBruce M Simpson inm->in6m_st[1].iss_fmode = MCAST_UNDEFINED; 10958f002c6cSBruce M Simpson } 10968f002c6cSBruce M Simpson 10978f002c6cSBruce M Simpson /* Decrement ASM listener count on transition out of ASM mode. */ 10988f002c6cSBruce M Simpson if (imf->im6f_st[0] == MCAST_EXCLUDE && nsrc0 == 0) { 10998f002c6cSBruce M Simpson if ((imf->im6f_st[1] != MCAST_EXCLUDE) || 1100bd745936SEnji Cooper (imf->im6f_st[1] == MCAST_EXCLUDE && nsrc1 > 0)) { 11018f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: --asm on inm at t1", __func__); 11028f002c6cSBruce M Simpson --inm->in6m_st[1].iss_asm; 11038f002c6cSBruce M Simpson } 1104bd745936SEnji Cooper } 11058f002c6cSBruce M Simpson 11068f002c6cSBruce M Simpson /* Increment ASM listener count on transition to ASM mode. */ 11078f002c6cSBruce M Simpson if (imf->im6f_st[1] == MCAST_EXCLUDE && nsrc1 == 0) { 11088f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: asm++ on inm at t1", __func__); 11098f002c6cSBruce M Simpson inm->in6m_st[1].iss_asm++; 11108f002c6cSBruce M Simpson } 11118f002c6cSBruce M Simpson 11128f002c6cSBruce M Simpson CTR3(KTR_MLD, "%s: merged imf %p to inm %p", __func__, imf, inm); 11138f002c6cSBruce M Simpson in6m_print(inm); 11148f002c6cSBruce M Simpson 11158f002c6cSBruce M Simpson out_reap: 11168f002c6cSBruce M Simpson if (schanged > 0) { 11178f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: sources changed; reaping", __func__); 11188f002c6cSBruce M Simpson in6m_reap(inm); 11198f002c6cSBruce M Simpson } 11208f002c6cSBruce M Simpson return (error); 11218f002c6cSBruce M Simpson } 11228f002c6cSBruce M Simpson 11238f002c6cSBruce M Simpson /* 11248f002c6cSBruce M Simpson * Mark an in6_multi's filter set deltas as committed. 11258f002c6cSBruce M Simpson * Called by MLD after a state change has been enqueued. 11268f002c6cSBruce M Simpson */ 11278f002c6cSBruce M Simpson void 11288f002c6cSBruce M Simpson in6m_commit(struct in6_multi *inm) 11298f002c6cSBruce M Simpson { 11308f002c6cSBruce M Simpson struct ip6_msource *ims; 11318f002c6cSBruce M Simpson 11328f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: commit inm %p", __func__, inm); 11338f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: pre commit:", __func__); 11348f002c6cSBruce M Simpson in6m_print(inm); 11358f002c6cSBruce M Simpson 11368f002c6cSBruce M Simpson RB_FOREACH(ims, ip6_msource_tree, &inm->in6m_srcs) { 11378f002c6cSBruce M Simpson ims->im6s_st[0] = ims->im6s_st[1]; 11388f002c6cSBruce M Simpson } 11398f002c6cSBruce M Simpson inm->in6m_st[0] = inm->in6m_st[1]; 11408f002c6cSBruce M Simpson } 11418f002c6cSBruce M Simpson 11428f002c6cSBruce M Simpson /* 11438f002c6cSBruce M Simpson * Reap unreferenced nodes from an in6_multi's filter set. 11448f002c6cSBruce M Simpson */ 11458f002c6cSBruce M Simpson static void 11468f002c6cSBruce M Simpson in6m_reap(struct in6_multi *inm) 11478f002c6cSBruce M Simpson { 11488f002c6cSBruce M Simpson struct ip6_msource *ims, *tims; 11498f002c6cSBruce M Simpson 11508f002c6cSBruce M Simpson RB_FOREACH_SAFE(ims, ip6_msource_tree, &inm->in6m_srcs, tims) { 11518f002c6cSBruce M Simpson if (ims->im6s_st[0].ex > 0 || ims->im6s_st[0].in > 0 || 11528f002c6cSBruce M Simpson ims->im6s_st[1].ex > 0 || ims->im6s_st[1].in > 0 || 11538f002c6cSBruce M Simpson ims->im6s_stp != 0) 11548f002c6cSBruce M Simpson continue; 11558f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: free ims %p", __func__, ims); 11568f002c6cSBruce M Simpson RB_REMOVE(ip6_msource_tree, &inm->in6m_srcs, ims); 11578f002c6cSBruce M Simpson free(ims, M_IP6MSOURCE); 11588f002c6cSBruce M Simpson inm->in6m_nsrc--; 11598f002c6cSBruce M Simpson } 11608f002c6cSBruce M Simpson } 11618f002c6cSBruce M Simpson 11628f002c6cSBruce M Simpson /* 11638f002c6cSBruce M Simpson * Purge all source nodes from an in6_multi's filter set. 11648f002c6cSBruce M Simpson */ 11658f002c6cSBruce M Simpson static void 11668f002c6cSBruce M Simpson in6m_purge(struct in6_multi *inm) 11678f002c6cSBruce M Simpson { 11688f002c6cSBruce M Simpson struct ip6_msource *ims, *tims; 11698f002c6cSBruce M Simpson 11708f002c6cSBruce M Simpson RB_FOREACH_SAFE(ims, ip6_msource_tree, &inm->in6m_srcs, tims) { 11718f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: free ims %p", __func__, ims); 11728f002c6cSBruce M Simpson RB_REMOVE(ip6_msource_tree, &inm->in6m_srcs, ims); 11738f002c6cSBruce M Simpson free(ims, M_IP6MSOURCE); 11748f002c6cSBruce M Simpson inm->in6m_nsrc--; 11758f002c6cSBruce M Simpson } 11763c268b3aSAndrey V. Elsukov /* Free state-change requests that might be queued. */ 1177a99c84d4SGleb Smirnoff mbufq_drain(&inm->in6m_scq); 11788f002c6cSBruce M Simpson } 11798f002c6cSBruce M Simpson 11808f002c6cSBruce M Simpson /* 11818f002c6cSBruce M Simpson * Join a multicast address w/o sources. 11828f002c6cSBruce M Simpson * KAME compatibility entry point. 11838f002c6cSBruce M Simpson * 11848f002c6cSBruce M Simpson * SMPng: Assume no mc locks held by caller. 11858f002c6cSBruce M Simpson */ 11868f002c6cSBruce M Simpson int 1187f3e1324bSStephen Hurd in6_joingroup(struct ifnet *ifp, const struct in6_addr *mcaddr, 11888f002c6cSBruce M Simpson /*const*/ struct in6_mfilter *imf, struct in6_multi **pinm, 11898f002c6cSBruce M Simpson const int delay) 11908f002c6cSBruce M Simpson { 11918f002c6cSBruce M Simpson int error; 11928f002c6cSBruce M Simpson 11938f002c6cSBruce M Simpson IN6_MULTI_LOCK(); 1194f3e1324bSStephen Hurd error = in6_joingroup_locked(ifp, mcaddr, NULL, pinm, delay); 11958f002c6cSBruce M Simpson IN6_MULTI_UNLOCK(); 11968f002c6cSBruce M Simpson return (error); 11978f002c6cSBruce M Simpson } 11988f002c6cSBruce M Simpson 11998f002c6cSBruce M Simpson /* 12008f002c6cSBruce M Simpson * Join a multicast group; real entry point. 12018f002c6cSBruce M Simpson * 12028f002c6cSBruce M Simpson * Only preserves atomicity at inm level. 12038f002c6cSBruce M Simpson * NOTE: imf argument cannot be const due to sys/tree.h limitations. 12048f002c6cSBruce M Simpson * 12058f002c6cSBruce M Simpson * If the MLD downcall fails, the group is not joined, and an error 12068f002c6cSBruce M Simpson * code is returned. 12078f002c6cSBruce M Simpson */ 1208e1891232SBjoern A. Zeeb static int 1209f3e1324bSStephen Hurd in6_joingroup_locked(struct ifnet *ifp, const struct in6_addr *mcaddr, 12108f002c6cSBruce M Simpson /*const*/ struct in6_mfilter *imf, struct in6_multi **pinm, 12118f002c6cSBruce M Simpson const int delay) 12128f002c6cSBruce M Simpson { 12132cd6ad76SHans Petter Selasky struct in6_multi_head inmh; 12148f002c6cSBruce M Simpson struct in6_mfilter timf; 12158f002c6cSBruce M Simpson struct in6_multi *inm; 1216f3e1324bSStephen Hurd struct ifmultiaddr *ifma; 12178f002c6cSBruce M Simpson int error; 12188f002c6cSBruce M Simpson #ifdef KTR 12198f002c6cSBruce M Simpson char ip6tbuf[INET6_ADDRSTRLEN]; 12208f002c6cSBruce M Simpson #endif 12218f002c6cSBruce M Simpson 122229dc7bc6SBruce M Simpson /* 122329dc7bc6SBruce M Simpson * Sanity: Check scope zone ID was set for ifp, if and 122429dc7bc6SBruce M Simpson * only if group is scoped to an interface. 122529dc7bc6SBruce M Simpson */ 122629dc7bc6SBruce M Simpson KASSERT(IN6_IS_ADDR_MULTICAST(mcaddr), 122729dc7bc6SBruce M Simpson ("%s: not a multicast address", __func__)); 122829dc7bc6SBruce M Simpson if (IN6_IS_ADDR_MC_LINKLOCAL(mcaddr) || 122929dc7bc6SBruce M Simpson IN6_IS_ADDR_MC_INTFACELOCAL(mcaddr)) { 123029dc7bc6SBruce M Simpson KASSERT(mcaddr->s6_addr16[1] != 0, 123129dc7bc6SBruce M Simpson ("%s: scope zone ID not set", __func__)); 123229dc7bc6SBruce M Simpson } 123329dc7bc6SBruce M Simpson 12348f002c6cSBruce M Simpson IN6_MULTI_LOCK_ASSERT(); 1235f3e1324bSStephen Hurd IN6_MULTI_LIST_UNLOCK_ASSERT(); 12368f002c6cSBruce M Simpson 12378f002c6cSBruce M Simpson CTR4(KTR_MLD, "%s: join %s on %p(%s))", __func__, 1238e74966f6SAndrey V. Elsukov ip6_sprintf(ip6tbuf, mcaddr), ifp, if_name(ifp)); 12398f002c6cSBruce M Simpson 12408f002c6cSBruce M Simpson error = 0; 12418f002c6cSBruce M Simpson inm = NULL; 12428f002c6cSBruce M Simpson 12438f002c6cSBruce M Simpson /* 12448f002c6cSBruce M Simpson * If no imf was specified (i.e. kernel consumer), 12458f002c6cSBruce M Simpson * fake one up and assume it is an ASM join. 12468f002c6cSBruce M Simpson */ 12478f002c6cSBruce M Simpson if (imf == NULL) { 12488f002c6cSBruce M Simpson im6f_init(&timf, MCAST_UNDEFINED, MCAST_EXCLUDE); 12498f002c6cSBruce M Simpson imf = &timf; 12508f002c6cSBruce M Simpson } 1251f3e1324bSStephen Hurd error = in6_getmulti(ifp, mcaddr, &inm); 12528f002c6cSBruce M Simpson if (error) { 1253f3e1324bSStephen Hurd CTR1(KTR_MLD, "%s: in6_getmulti() failure", __func__); 12548f002c6cSBruce M Simpson return (error); 12558f002c6cSBruce M Simpson } 12568f002c6cSBruce M Simpson 1257f3e1324bSStephen Hurd IN6_MULTI_LIST_LOCK(); 12588f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: merge inm state", __func__); 12598f002c6cSBruce M Simpson error = in6m_merge(inm, imf); 12608f002c6cSBruce M Simpson if (error) { 12618f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: failed to merge inm state", __func__); 12628f002c6cSBruce M Simpson goto out_in6m_release; 12638f002c6cSBruce M Simpson } 12648f002c6cSBruce M Simpson 12658f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: doing mld downcall", __func__); 12668f002c6cSBruce M Simpson error = mld_change_state(inm, delay); 12678f002c6cSBruce M Simpson if (error) { 12688f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: failed to update source", __func__); 12698f002c6cSBruce M Simpson goto out_in6m_release; 12708f002c6cSBruce M Simpson } 12718f002c6cSBruce M Simpson 12728f002c6cSBruce M Simpson out_in6m_release: 12732cd6ad76SHans Petter Selasky SLIST_INIT(&inmh); 12748f002c6cSBruce M Simpson if (error) { 1275a68cc388SGleb Smirnoff struct epoch_tracker et; 1276a68cc388SGleb Smirnoff 12778f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: dropping ref on %p", __func__, inm); 12786afe56f9SRyan Libby IF_ADDR_WLOCK(ifp); 1279a68cc388SGleb Smirnoff NET_EPOCH_ENTER(et); 1280d7c5a620SMatt Macy CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 1281f3e1324bSStephen Hurd if (ifma->ifma_protospec == inm) { 1282f3e1324bSStephen Hurd ifma->ifma_protospec = NULL; 1283f3e1324bSStephen Hurd break; 1284f3e1324bSStephen Hurd } 1285f3e1324bSStephen Hurd } 12862cd6ad76SHans Petter Selasky in6m_disconnect_locked(&inmh, inm); 12872cd6ad76SHans Petter Selasky in6m_rele_locked(&inmh, inm); 1288a68cc388SGleb Smirnoff NET_EPOCH_EXIT(et); 12896afe56f9SRyan Libby IF_ADDR_WUNLOCK(ifp); 12908f002c6cSBruce M Simpson } else { 12918f002c6cSBruce M Simpson *pinm = inm; 12928f002c6cSBruce M Simpson } 1293f3e1324bSStephen Hurd IN6_MULTI_LIST_UNLOCK(); 12942cd6ad76SHans Petter Selasky in6m_release_list_deferred(&inmh); 12958f002c6cSBruce M Simpson return (error); 12968f002c6cSBruce M Simpson } 12978f002c6cSBruce M Simpson 12988f002c6cSBruce M Simpson /* 12998f002c6cSBruce M Simpson * Leave a multicast group; unlocked entry point. 13008f002c6cSBruce M Simpson */ 13018f002c6cSBruce M Simpson int 1302f3e1324bSStephen Hurd in6_leavegroup(struct in6_multi *inm, /*const*/ struct in6_mfilter *imf) 13038f002c6cSBruce M Simpson { 13048f002c6cSBruce M Simpson int error; 13058f002c6cSBruce M Simpson 13068f002c6cSBruce M Simpson IN6_MULTI_LOCK(); 1307f3e1324bSStephen Hurd error = in6_leavegroup_locked(inm, imf); 13088f002c6cSBruce M Simpson IN6_MULTI_UNLOCK(); 13098f002c6cSBruce M Simpson return (error); 13108f002c6cSBruce M Simpson } 13118f002c6cSBruce M Simpson 13128f002c6cSBruce M Simpson /* 13138f002c6cSBruce M Simpson * Leave a multicast group; real entry point. 13148f002c6cSBruce M Simpson * All source filters will be expunged. 13158f002c6cSBruce M Simpson * 13168f002c6cSBruce M Simpson * Only preserves atomicity at inm level. 13178f002c6cSBruce M Simpson * 13188f002c6cSBruce M Simpson * Holding the write lock for the INP which contains imf 13198f002c6cSBruce M Simpson * is highly advisable. We can't assert for it as imf does not 13208f002c6cSBruce M Simpson * contain a back-pointer to the owning inp. 13218f002c6cSBruce M Simpson * 13228f002c6cSBruce M Simpson * Note: This is not the same as in6m_release(*) as this function also 13238f002c6cSBruce M Simpson * makes a state change downcall into MLD. 13248f002c6cSBruce M Simpson */ 13258f002c6cSBruce M Simpson int 1326f3e1324bSStephen Hurd in6_leavegroup_locked(struct in6_multi *inm, /*const*/ struct in6_mfilter *imf) 13278f002c6cSBruce M Simpson { 13282cd6ad76SHans Petter Selasky struct in6_multi_head inmh; 13298f002c6cSBruce M Simpson struct in6_mfilter timf; 1330f3e1324bSStephen Hurd struct ifnet *ifp; 13318f002c6cSBruce M Simpson int error; 13328f002c6cSBruce M Simpson #ifdef KTR 13338f002c6cSBruce M Simpson char ip6tbuf[INET6_ADDRSTRLEN]; 13348f002c6cSBruce M Simpson #endif 13358f002c6cSBruce M Simpson 13368f002c6cSBruce M Simpson error = 0; 13378f002c6cSBruce M Simpson 13388f002c6cSBruce M Simpson IN6_MULTI_LOCK_ASSERT(); 13398f002c6cSBruce M Simpson 13408f002c6cSBruce M Simpson CTR5(KTR_MLD, "%s: leave inm %p, %s/%s, imf %p", __func__, 13418f002c6cSBruce M Simpson inm, ip6_sprintf(ip6tbuf, &inm->in6m_addr), 1342e74966f6SAndrey V. Elsukov (in6m_is_ifp_detached(inm) ? "null" : if_name(inm->in6m_ifp)), 13438f002c6cSBruce M Simpson imf); 13448f002c6cSBruce M Simpson 13458f002c6cSBruce M Simpson /* 13468f002c6cSBruce M Simpson * If no imf was specified (i.e. kernel consumer), 13478f002c6cSBruce M Simpson * fake one up and assume it is an ASM join. 13488f002c6cSBruce M Simpson */ 13498f002c6cSBruce M Simpson if (imf == NULL) { 13508f002c6cSBruce M Simpson im6f_init(&timf, MCAST_EXCLUDE, MCAST_UNDEFINED); 13518f002c6cSBruce M Simpson imf = &timf; 13528f002c6cSBruce M Simpson } 13538f002c6cSBruce M Simpson 13548f002c6cSBruce M Simpson /* 13558f002c6cSBruce M Simpson * Begin state merge transaction at MLD layer. 13568f002c6cSBruce M Simpson * 13578f002c6cSBruce M Simpson * As this particular invocation should not cause any memory 13588f002c6cSBruce M Simpson * to be allocated, and there is no opportunity to roll back 13598f002c6cSBruce M Simpson * the transaction, it MUST NOT fail. 13608f002c6cSBruce M Simpson */ 1361f3e1324bSStephen Hurd 1362f3e1324bSStephen Hurd ifp = inm->in6m_ifp; 1363f3e1324bSStephen Hurd IN6_MULTI_LIST_LOCK(); 13648f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: merge inm state", __func__); 13658f002c6cSBruce M Simpson error = in6m_merge(inm, imf); 13668f002c6cSBruce M Simpson KASSERT(error == 0, ("%s: failed to merge inm state", __func__)); 13678f002c6cSBruce M Simpson 13688f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: doing mld downcall", __func__); 1369f9be0386SMatt Macy error = 0; 1370f9be0386SMatt Macy if (ifp) 13718f002c6cSBruce M Simpson error = mld_change_state(inm, 0); 13728f002c6cSBruce M Simpson if (error) 13738f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: failed mld downcall", __func__); 13748f002c6cSBruce M Simpson 13758f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: dropping ref on %p", __func__, inm); 1376f3e1324bSStephen Hurd if (ifp) 1377b6f6f880SMatt Macy IF_ADDR_WLOCK(ifp); 13782cd6ad76SHans Petter Selasky 13792cd6ad76SHans Petter Selasky SLIST_INIT(&inmh); 13802cd6ad76SHans Petter Selasky if (inm->in6m_refcount == 1) 13812cd6ad76SHans Petter Selasky in6m_disconnect_locked(&inmh, inm); 13822cd6ad76SHans Petter Selasky in6m_rele_locked(&inmh, inm); 1383f3e1324bSStephen Hurd if (ifp) 1384b6f6f880SMatt Macy IF_ADDR_WUNLOCK(ifp); 1385f3e1324bSStephen Hurd IN6_MULTI_LIST_UNLOCK(); 13862cd6ad76SHans Petter Selasky in6m_release_list_deferred(&inmh); 13878f002c6cSBruce M Simpson return (error); 13888f002c6cSBruce M Simpson } 13898f002c6cSBruce M Simpson 13908f002c6cSBruce M Simpson /* 13918f002c6cSBruce M Simpson * Block or unblock an ASM multicast source on an inpcb. 13928f002c6cSBruce M Simpson * This implements the delta-based API described in RFC 3678. 13938f002c6cSBruce M Simpson * 13948f002c6cSBruce M Simpson * The delta-based API applies only to exclusive-mode memberships. 13958f002c6cSBruce M Simpson * An MLD downcall will be performed. 13968f002c6cSBruce M Simpson * 13978f002c6cSBruce M Simpson * SMPng: NOTE: Must take Giant as a join may create a new ifma. 13988f002c6cSBruce M Simpson * 13998f002c6cSBruce M Simpson * Return 0 if successful, otherwise return an appropriate error code. 14008f002c6cSBruce M Simpson */ 14018f002c6cSBruce M Simpson static int 14028f002c6cSBruce M Simpson in6p_block_unblock_source(struct inpcb *inp, struct sockopt *sopt) 14038f002c6cSBruce M Simpson { 14048f002c6cSBruce M Simpson struct group_source_req gsr; 1405*d74b7baeSGleb Smirnoff struct epoch_tracker et; 14068f002c6cSBruce M Simpson sockunion_t *gsa, *ssa; 14078f002c6cSBruce M Simpson struct ifnet *ifp; 14088f002c6cSBruce M Simpson struct in6_mfilter *imf; 14098f002c6cSBruce M Simpson struct ip6_moptions *imo; 14108f002c6cSBruce M Simpson struct in6_msource *ims; 14118f002c6cSBruce M Simpson struct in6_multi *inm; 14128f002c6cSBruce M Simpson uint16_t fmode; 14138f002c6cSBruce M Simpson int error, doblock; 14148f002c6cSBruce M Simpson #ifdef KTR 14158f002c6cSBruce M Simpson char ip6tbuf[INET6_ADDRSTRLEN]; 14168f002c6cSBruce M Simpson #endif 14178f002c6cSBruce M Simpson 14188f002c6cSBruce M Simpson ifp = NULL; 14198f002c6cSBruce M Simpson error = 0; 14208f002c6cSBruce M Simpson doblock = 0; 14218f002c6cSBruce M Simpson 14228f002c6cSBruce M Simpson memset(&gsr, 0, sizeof(struct group_source_req)); 14238f002c6cSBruce M Simpson gsa = (sockunion_t *)&gsr.gsr_group; 14248f002c6cSBruce M Simpson ssa = (sockunion_t *)&gsr.gsr_source; 14258f002c6cSBruce M Simpson 14268f002c6cSBruce M Simpson switch (sopt->sopt_name) { 14278f002c6cSBruce M Simpson case MCAST_BLOCK_SOURCE: 14288f002c6cSBruce M Simpson case MCAST_UNBLOCK_SOURCE: 14298f002c6cSBruce M Simpson error = sooptcopyin(sopt, &gsr, 14308f002c6cSBruce M Simpson sizeof(struct group_source_req), 14318f002c6cSBruce M Simpson sizeof(struct group_source_req)); 14328f002c6cSBruce M Simpson if (error) 14338f002c6cSBruce M Simpson return (error); 14348f002c6cSBruce M Simpson 14358f002c6cSBruce M Simpson if (gsa->sin6.sin6_family != AF_INET6 || 14368f002c6cSBruce M Simpson gsa->sin6.sin6_len != sizeof(struct sockaddr_in6)) 14378f002c6cSBruce M Simpson return (EINVAL); 14388f002c6cSBruce M Simpson 14398f002c6cSBruce M Simpson if (ssa->sin6.sin6_family != AF_INET6 || 14408f002c6cSBruce M Simpson ssa->sin6.sin6_len != sizeof(struct sockaddr_in6)) 14418f002c6cSBruce M Simpson return (EINVAL); 14428f002c6cSBruce M Simpson 1443*d74b7baeSGleb Smirnoff /* 1444*d74b7baeSGleb Smirnoff * XXXGL: this function should use ifnet_byindex_ref, or 1445*d74b7baeSGleb Smirnoff * expand the epoch section all the way to where we put 1446*d74b7baeSGleb Smirnoff * the reference. 1447*d74b7baeSGleb Smirnoff */ 1448*d74b7baeSGleb Smirnoff NET_EPOCH_ENTER(et); 14498f002c6cSBruce M Simpson ifp = ifnet_byindex(gsr.gsr_interface); 1450*d74b7baeSGleb Smirnoff NET_EPOCH_EXIT(et); 1451*d74b7baeSGleb Smirnoff if (ifp == NULL) 1452*d74b7baeSGleb Smirnoff return (EADDRNOTAVAIL); 14538f002c6cSBruce M Simpson 14548f002c6cSBruce M Simpson if (sopt->sopt_name == MCAST_BLOCK_SOURCE) 14558f002c6cSBruce M Simpson doblock = 1; 14568f002c6cSBruce M Simpson break; 14578f002c6cSBruce M Simpson 14588f002c6cSBruce M Simpson default: 14598f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: unknown sopt_name %d", 14608f002c6cSBruce M Simpson __func__, sopt->sopt_name); 14618f002c6cSBruce M Simpson return (EOPNOTSUPP); 14628f002c6cSBruce M Simpson break; 14638f002c6cSBruce M Simpson } 14648f002c6cSBruce M Simpson 14658f002c6cSBruce M Simpson if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr)) 14668f002c6cSBruce M Simpson return (EINVAL); 14678f002c6cSBruce M Simpson 146829dc7bc6SBruce M Simpson (void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL); 146929dc7bc6SBruce M Simpson 14708f002c6cSBruce M Simpson /* 14718f002c6cSBruce M Simpson * Check if we are actually a member of this group. 14728f002c6cSBruce M Simpson */ 14738f002c6cSBruce M Simpson imo = in6p_findmoptions(inp); 147459854ecfSHans Petter Selasky imf = im6o_match_group(imo, ifp, &gsa->sa); 147559854ecfSHans Petter Selasky if (imf == NULL) { 14768f002c6cSBruce M Simpson error = EADDRNOTAVAIL; 14778f002c6cSBruce M Simpson goto out_in6p_locked; 14788f002c6cSBruce M Simpson } 147959854ecfSHans Petter Selasky inm = imf->im6f_in6m; 14808f002c6cSBruce M Simpson 14818f002c6cSBruce M Simpson /* 14828f002c6cSBruce M Simpson * Attempting to use the delta-based API on an 14838f002c6cSBruce M Simpson * non exclusive-mode membership is an error. 14848f002c6cSBruce M Simpson */ 14858f002c6cSBruce M Simpson fmode = imf->im6f_st[0]; 14868f002c6cSBruce M Simpson if (fmode != MCAST_EXCLUDE) { 14878f002c6cSBruce M Simpson error = EINVAL; 14888f002c6cSBruce M Simpson goto out_in6p_locked; 14898f002c6cSBruce M Simpson } 14908f002c6cSBruce M Simpson 14918f002c6cSBruce M Simpson /* 14928f002c6cSBruce M Simpson * Deal with error cases up-front: 14938f002c6cSBruce M Simpson * Asked to block, but already blocked; or 14948f002c6cSBruce M Simpson * Asked to unblock, but nothing to unblock. 14958f002c6cSBruce M Simpson * If adding a new block entry, allocate it. 14968f002c6cSBruce M Simpson */ 149759854ecfSHans Petter Selasky ims = im6o_match_source(imf, &ssa->sa); 14988f002c6cSBruce M Simpson if ((ims != NULL && doblock) || (ims == NULL && !doblock)) { 14998f002c6cSBruce M Simpson CTR3(KTR_MLD, "%s: source %s %spresent", __func__, 15008f002c6cSBruce M Simpson ip6_sprintf(ip6tbuf, &ssa->sin6.sin6_addr), 15018f002c6cSBruce M Simpson doblock ? "" : "not "); 15028f002c6cSBruce M Simpson error = EADDRNOTAVAIL; 15038f002c6cSBruce M Simpson goto out_in6p_locked; 15048f002c6cSBruce M Simpson } 15058f002c6cSBruce M Simpson 15068f002c6cSBruce M Simpson INP_WLOCK_ASSERT(inp); 15078f002c6cSBruce M Simpson 15088f002c6cSBruce M Simpson /* 15098f002c6cSBruce M Simpson * Begin state merge transaction at socket layer. 15108f002c6cSBruce M Simpson */ 15118f002c6cSBruce M Simpson if (doblock) { 15128f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: %s source", __func__, "block"); 15138f002c6cSBruce M Simpson ims = im6f_graft(imf, fmode, &ssa->sin6); 15148f002c6cSBruce M Simpson if (ims == NULL) 15158f002c6cSBruce M Simpson error = ENOMEM; 15168f002c6cSBruce M Simpson } else { 15178f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: %s source", __func__, "allow"); 15188f002c6cSBruce M Simpson error = im6f_prune(imf, &ssa->sin6); 15198f002c6cSBruce M Simpson } 15208f002c6cSBruce M Simpson 15218f002c6cSBruce M Simpson if (error) { 15228f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: merge imf state failed", __func__); 15238f002c6cSBruce M Simpson goto out_im6f_rollback; 15248f002c6cSBruce M Simpson } 15258f002c6cSBruce M Simpson 15268f002c6cSBruce M Simpson /* 15278f002c6cSBruce M Simpson * Begin state merge transaction at MLD layer. 15288f002c6cSBruce M Simpson */ 1529f3e1324bSStephen Hurd IN6_MULTI_LIST_LOCK(); 15308f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: merge inm state", __func__); 15318f002c6cSBruce M Simpson error = in6m_merge(inm, imf); 15324fd91336SAndrey V. Elsukov if (error) 15338f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: failed to merge inm state", __func__); 15344fd91336SAndrey V. Elsukov else { 15358f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: doing mld downcall", __func__); 15368f002c6cSBruce M Simpson error = mld_change_state(inm, 0); 15378f002c6cSBruce M Simpson if (error) 15388f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: failed mld downcall", __func__); 15394fd91336SAndrey V. Elsukov } 15408f002c6cSBruce M Simpson 1541f3e1324bSStephen Hurd IN6_MULTI_LIST_UNLOCK(); 15428f002c6cSBruce M Simpson 15438f002c6cSBruce M Simpson out_im6f_rollback: 15448f002c6cSBruce M Simpson if (error) 15458f002c6cSBruce M Simpson im6f_rollback(imf); 15468f002c6cSBruce M Simpson else 15478f002c6cSBruce M Simpson im6f_commit(imf); 15488f002c6cSBruce M Simpson 15498f002c6cSBruce M Simpson im6f_reap(imf); 15508f002c6cSBruce M Simpson 15518f002c6cSBruce M Simpson out_in6p_locked: 15528f002c6cSBruce M Simpson INP_WUNLOCK(inp); 15538f002c6cSBruce M Simpson return (error); 15548f002c6cSBruce M Simpson } 15558f002c6cSBruce M Simpson 15568f002c6cSBruce M Simpson /* 15578f002c6cSBruce M Simpson * Given an inpcb, return its multicast options structure pointer. Accepts 15588f002c6cSBruce M Simpson * an unlocked inpcb pointer, but will return it locked. May sleep. 15598f002c6cSBruce M Simpson * 15608f002c6cSBruce M Simpson * SMPng: NOTE: Potentially calls malloc(M_WAITOK) with Giant held. 15618f002c6cSBruce M Simpson * SMPng: NOTE: Returns with the INP write lock held. 15628f002c6cSBruce M Simpson */ 15638f002c6cSBruce M Simpson static struct ip6_moptions * 15648f002c6cSBruce M Simpson in6p_findmoptions(struct inpcb *inp) 15658f002c6cSBruce M Simpson { 15668f002c6cSBruce M Simpson struct ip6_moptions *imo; 15678f002c6cSBruce M Simpson 15688f002c6cSBruce M Simpson INP_WLOCK(inp); 15698f002c6cSBruce M Simpson if (inp->in6p_moptions != NULL) 15708f002c6cSBruce M Simpson return (inp->in6p_moptions); 15718f002c6cSBruce M Simpson 15728f002c6cSBruce M Simpson INP_WUNLOCK(inp); 15738f002c6cSBruce M Simpson 15748f002c6cSBruce M Simpson imo = malloc(sizeof(*imo), M_IP6MOPTS, M_WAITOK); 15758f002c6cSBruce M Simpson 15768f002c6cSBruce M Simpson imo->im6o_multicast_ifp = NULL; 15778f002c6cSBruce M Simpson imo->im6o_multicast_hlim = V_ip6_defmcasthlim; 15788f002c6cSBruce M Simpson imo->im6o_multicast_loop = in6_mcast_loop; 157959854ecfSHans Petter Selasky STAILQ_INIT(&imo->im6o_head); 15808f002c6cSBruce M Simpson 15818f002c6cSBruce M Simpson INP_WLOCK(inp); 15828f002c6cSBruce M Simpson if (inp->in6p_moptions != NULL) { 15838f002c6cSBruce M Simpson free(imo, M_IP6MOPTS); 15848f002c6cSBruce M Simpson return (inp->in6p_moptions); 15858f002c6cSBruce M Simpson } 15868f002c6cSBruce M Simpson inp->in6p_moptions = imo; 15878f002c6cSBruce M Simpson return (imo); 15888f002c6cSBruce M Simpson } 15898f002c6cSBruce M Simpson 15908f002c6cSBruce M Simpson /* 15918f002c6cSBruce M Simpson * Discard the IPv6 multicast options (and source filters). 15928f002c6cSBruce M Simpson * 15938f002c6cSBruce M Simpson * SMPng: NOTE: assumes INP write lock is held. 1594cb6bb230SMatt Macy * 1595cb6bb230SMatt Macy * XXX can all be safely deferred to epoch_call 1596cb6bb230SMatt Macy * 15978f002c6cSBruce M Simpson */ 1598cb6bb230SMatt Macy 1599cb6bb230SMatt Macy static void 1600f9be0386SMatt Macy inp_gcmoptions(struct ip6_moptions *imo) 16018f002c6cSBruce M Simpson { 16028f002c6cSBruce M Simpson struct in6_mfilter *imf; 1603b15253bbSMatt Macy struct in6_multi *inm; 1604b15253bbSMatt Macy struct ifnet *ifp; 16058f002c6cSBruce M Simpson 160659854ecfSHans Petter Selasky while ((imf = ip6_mfilter_first(&imo->im6o_head)) != NULL) { 160759854ecfSHans Petter Selasky ip6_mfilter_remove(&imo->im6o_head, imf); 160859854ecfSHans Petter Selasky 16098f002c6cSBruce M Simpson im6f_leave(imf); 161059854ecfSHans Petter Selasky if ((inm = imf->im6f_in6m) != NULL) { 161159854ecfSHans Petter Selasky if ((ifp = inm->in6m_ifp) != NULL) { 161215f8acc5SEd Maste CURVNET_SET(ifp->if_vnet); 1613b15253bbSMatt Macy (void)in6_leavegroup(inm, imf); 161415f8acc5SEd Maste CURVNET_RESTORE(); 16153b9b6b17SEd Maste } else { 16163b9b6b17SEd Maste (void)in6_leavegroup(inm, imf); 16173b9b6b17SEd Maste } 16188f002c6cSBruce M Simpson } 161959854ecfSHans Petter Selasky ip6_mfilter_free(imf); 162059854ecfSHans Petter Selasky } 16218f002c6cSBruce M Simpson free(imo, M_IP6MOPTS); 1622cb6bb230SMatt Macy } 1623cb6bb230SMatt Macy 1624cb6bb230SMatt Macy void 1625cb6bb230SMatt Macy ip6_freemoptions(struct ip6_moptions *imo) 1626cb6bb230SMatt Macy { 1627cb6bb230SMatt Macy if (imo == NULL) 1628cb6bb230SMatt Macy return; 1629f9be0386SMatt Macy inp_gcmoptions(imo); 16308f002c6cSBruce M Simpson } 16318f002c6cSBruce M Simpson 16328f002c6cSBruce M Simpson /* 16338f002c6cSBruce M Simpson * Atomically get source filters on a socket for an IPv6 multicast group. 16348f002c6cSBruce M Simpson * Called with INP lock held; returns with lock released. 16358f002c6cSBruce M Simpson */ 16368f002c6cSBruce M Simpson static int 16378f002c6cSBruce M Simpson in6p_get_source_filters(struct inpcb *inp, struct sockopt *sopt) 16388f002c6cSBruce M Simpson { 1639*d74b7baeSGleb Smirnoff struct epoch_tracker et; 16408f002c6cSBruce M Simpson struct __msfilterreq msfr; 16418f002c6cSBruce M Simpson sockunion_t *gsa; 16428f002c6cSBruce M Simpson struct ifnet *ifp; 16438f002c6cSBruce M Simpson struct ip6_moptions *imo; 16448f002c6cSBruce M Simpson struct in6_mfilter *imf; 16458f002c6cSBruce M Simpson struct ip6_msource *ims; 16468f002c6cSBruce M Simpson struct in6_msource *lims; 16478f002c6cSBruce M Simpson struct sockaddr_in6 *psin; 16488f002c6cSBruce M Simpson struct sockaddr_storage *ptss; 16498f002c6cSBruce M Simpson struct sockaddr_storage *tss; 16508f002c6cSBruce M Simpson int error; 165159854ecfSHans Petter Selasky size_t nsrcs, ncsrcs; 16528f002c6cSBruce M Simpson 16538f002c6cSBruce M Simpson INP_WLOCK_ASSERT(inp); 16548f002c6cSBruce M Simpson 16558f002c6cSBruce M Simpson imo = inp->in6p_moptions; 16568f002c6cSBruce M Simpson KASSERT(imo != NULL, ("%s: null ip6_moptions", __func__)); 16578f002c6cSBruce M Simpson 16588f002c6cSBruce M Simpson INP_WUNLOCK(inp); 16598f002c6cSBruce M Simpson 16608f002c6cSBruce M Simpson error = sooptcopyin(sopt, &msfr, sizeof(struct __msfilterreq), 16618f002c6cSBruce M Simpson sizeof(struct __msfilterreq)); 16628f002c6cSBruce M Simpson if (error) 16638f002c6cSBruce M Simpson return (error); 16648f002c6cSBruce M Simpson 166529dc7bc6SBruce M Simpson if (msfr.msfr_group.ss_family != AF_INET6 || 166629dc7bc6SBruce M Simpson msfr.msfr_group.ss_len != sizeof(struct sockaddr_in6)) 16678f002c6cSBruce M Simpson return (EINVAL); 16688f002c6cSBruce M Simpson 166929dc7bc6SBruce M Simpson gsa = (sockunion_t *)&msfr.msfr_group; 167029dc7bc6SBruce M Simpson if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr)) 167129dc7bc6SBruce M Simpson return (EINVAL); 167229dc7bc6SBruce M Simpson 1673*d74b7baeSGleb Smirnoff /* 1674*d74b7baeSGleb Smirnoff * XXXGL: this function should use ifnet_byindex_ref, or expand the 1675*d74b7baeSGleb Smirnoff * epoch section all the way to where the interface is referenced. 1676*d74b7baeSGleb Smirnoff */ 1677*d74b7baeSGleb Smirnoff NET_EPOCH_ENTER(et); 16788f002c6cSBruce M Simpson ifp = ifnet_byindex(msfr.msfr_ifindex); 1679*d74b7baeSGleb Smirnoff NET_EPOCH_EXIT(et); 16808f002c6cSBruce M Simpson if (ifp == NULL) 168129dc7bc6SBruce M Simpson return (EADDRNOTAVAIL); 168229dc7bc6SBruce M Simpson (void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL); 16838f002c6cSBruce M Simpson 16848f002c6cSBruce M Simpson INP_WLOCK(inp); 16858f002c6cSBruce M Simpson 16868f002c6cSBruce M Simpson /* 16878f002c6cSBruce M Simpson * Lookup group on the socket. 16888f002c6cSBruce M Simpson */ 168959854ecfSHans Petter Selasky imf = im6o_match_group(imo, ifp, &gsa->sa); 169059854ecfSHans Petter Selasky if (imf == NULL) { 16918f002c6cSBruce M Simpson INP_WUNLOCK(inp); 16928f002c6cSBruce M Simpson return (EADDRNOTAVAIL); 16938f002c6cSBruce M Simpson } 16948f002c6cSBruce M Simpson 16958f002c6cSBruce M Simpson /* 16968f002c6cSBruce M Simpson * Ignore memberships which are in limbo. 16978f002c6cSBruce M Simpson */ 16988f002c6cSBruce M Simpson if (imf->im6f_st[1] == MCAST_UNDEFINED) { 16998f002c6cSBruce M Simpson INP_WUNLOCK(inp); 17008f002c6cSBruce M Simpson return (EAGAIN); 17018f002c6cSBruce M Simpson } 17028f002c6cSBruce M Simpson msfr.msfr_fmode = imf->im6f_st[1]; 17038f002c6cSBruce M Simpson 17048f002c6cSBruce M Simpson /* 17058f002c6cSBruce M Simpson * If the user specified a buffer, copy out the source filter 17068f002c6cSBruce M Simpson * entries to userland gracefully. 17078f002c6cSBruce M Simpson * We only copy out the number of entries which userland 17088f002c6cSBruce M Simpson * has asked for, but we always tell userland how big the 17098f002c6cSBruce M Simpson * buffer really needs to be. 17108f002c6cSBruce M Simpson */ 1711acde2476SXin LI if (msfr.msfr_nsrcs > in6_mcast_maxsocksrc) 1712acde2476SXin LI msfr.msfr_nsrcs = in6_mcast_maxsocksrc; 17138f002c6cSBruce M Simpson tss = NULL; 17148f002c6cSBruce M Simpson if (msfr.msfr_srcs != NULL && msfr.msfr_nsrcs > 0) { 17158f002c6cSBruce M Simpson tss = malloc(sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs, 17168f002c6cSBruce M Simpson M_TEMP, M_NOWAIT | M_ZERO); 17178f002c6cSBruce M Simpson if (tss == NULL) { 17188f002c6cSBruce M Simpson INP_WUNLOCK(inp); 17198f002c6cSBruce M Simpson return (ENOBUFS); 17208f002c6cSBruce M Simpson } 17218f002c6cSBruce M Simpson } 17228f002c6cSBruce M Simpson 17238f002c6cSBruce M Simpson /* 17248f002c6cSBruce M Simpson * Count number of sources in-mode at t0. 17258f002c6cSBruce M Simpson * If buffer space exists and remains, copy out source entries. 17268f002c6cSBruce M Simpson */ 17278f002c6cSBruce M Simpson nsrcs = msfr.msfr_nsrcs; 17288f002c6cSBruce M Simpson ncsrcs = 0; 17298f002c6cSBruce M Simpson ptss = tss; 17308f002c6cSBruce M Simpson RB_FOREACH(ims, ip6_msource_tree, &imf->im6f_sources) { 17318f002c6cSBruce M Simpson lims = (struct in6_msource *)ims; 17328f002c6cSBruce M Simpson if (lims->im6sl_st[0] == MCAST_UNDEFINED || 17338f002c6cSBruce M Simpson lims->im6sl_st[0] != imf->im6f_st[0]) 17348f002c6cSBruce M Simpson continue; 17358f002c6cSBruce M Simpson ++ncsrcs; 17368f002c6cSBruce M Simpson if (tss != NULL && nsrcs > 0) { 17378f002c6cSBruce M Simpson psin = (struct sockaddr_in6 *)ptss; 17388f002c6cSBruce M Simpson psin->sin6_family = AF_INET6; 17398f002c6cSBruce M Simpson psin->sin6_len = sizeof(struct sockaddr_in6); 17408f002c6cSBruce M Simpson psin->sin6_addr = lims->im6s_addr; 17418f002c6cSBruce M Simpson psin->sin6_port = 0; 17428f002c6cSBruce M Simpson --nsrcs; 17438f002c6cSBruce M Simpson ++ptss; 17448f002c6cSBruce M Simpson } 17458f002c6cSBruce M Simpson } 17468f002c6cSBruce M Simpson 17478f002c6cSBruce M Simpson INP_WUNLOCK(inp); 17488f002c6cSBruce M Simpson 17498f002c6cSBruce M Simpson if (tss != NULL) { 17508f002c6cSBruce M Simpson error = copyout(tss, msfr.msfr_srcs, 17518f002c6cSBruce M Simpson sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs); 17528f002c6cSBruce M Simpson free(tss, M_TEMP); 17538f002c6cSBruce M Simpson if (error) 17548f002c6cSBruce M Simpson return (error); 17558f002c6cSBruce M Simpson } 17568f002c6cSBruce M Simpson 17578f002c6cSBruce M Simpson msfr.msfr_nsrcs = ncsrcs; 17588f002c6cSBruce M Simpson error = sooptcopyout(sopt, &msfr, sizeof(struct __msfilterreq)); 17598f002c6cSBruce M Simpson 17608f002c6cSBruce M Simpson return (error); 17618f002c6cSBruce M Simpson } 17628f002c6cSBruce M Simpson 17638f002c6cSBruce M Simpson /* 17648f002c6cSBruce M Simpson * Return the IP multicast options in response to user getsockopt(). 17658f002c6cSBruce M Simpson */ 17668f002c6cSBruce M Simpson int 17678f002c6cSBruce M Simpson ip6_getmoptions(struct inpcb *inp, struct sockopt *sopt) 17688f002c6cSBruce M Simpson { 176933cde130SBruce M Simpson struct ip6_moptions *im6o; 177033cde130SBruce M Simpson int error; 177133cde130SBruce M Simpson u_int optval; 17728f002c6cSBruce M Simpson 17738f002c6cSBruce M Simpson INP_WLOCK(inp); 177433cde130SBruce M Simpson im6o = inp->in6p_moptions; 17758f002c6cSBruce M Simpson /* 17768f002c6cSBruce M Simpson * If socket is neither of type SOCK_RAW or SOCK_DGRAM, 17778f002c6cSBruce M Simpson * or is a divert socket, reject it. 17788f002c6cSBruce M Simpson */ 17798f002c6cSBruce M Simpson if (inp->inp_socket->so_proto->pr_protocol == IPPROTO_DIVERT || 17808f002c6cSBruce M Simpson (inp->inp_socket->so_proto->pr_type != SOCK_RAW && 17818f002c6cSBruce M Simpson inp->inp_socket->so_proto->pr_type != SOCK_DGRAM)) { 17828f002c6cSBruce M Simpson INP_WUNLOCK(inp); 17838f002c6cSBruce M Simpson return (EOPNOTSUPP); 17848f002c6cSBruce M Simpson } 17858f002c6cSBruce M Simpson 17868f002c6cSBruce M Simpson error = 0; 17878f002c6cSBruce M Simpson switch (sopt->sopt_name) { 17888f002c6cSBruce M Simpson case IPV6_MULTICAST_IF: 178933cde130SBruce M Simpson if (im6o == NULL || im6o->im6o_multicast_ifp == NULL) { 17908f002c6cSBruce M Simpson optval = 0; 17918f002c6cSBruce M Simpson } else { 179233cde130SBruce M Simpson optval = im6o->im6o_multicast_ifp->if_index; 17938f002c6cSBruce M Simpson } 17948f002c6cSBruce M Simpson INP_WUNLOCK(inp); 179533cde130SBruce M Simpson error = sooptcopyout(sopt, &optval, sizeof(u_int)); 17968f002c6cSBruce M Simpson break; 17978f002c6cSBruce M Simpson 17988f002c6cSBruce M Simpson case IPV6_MULTICAST_HOPS: 179933cde130SBruce M Simpson if (im6o == NULL) 180033cde130SBruce M Simpson optval = V_ip6_defmcasthlim; 18018f002c6cSBruce M Simpson else 180208907004SBjoern A. Zeeb optval = im6o->im6o_multicast_hlim; 18038f002c6cSBruce M Simpson INP_WUNLOCK(inp); 18048f002c6cSBruce M Simpson error = sooptcopyout(sopt, &optval, sizeof(u_int)); 18058f002c6cSBruce M Simpson break; 18068f002c6cSBruce M Simpson 18078f002c6cSBruce M Simpson case IPV6_MULTICAST_LOOP: 180833cde130SBruce M Simpson if (im6o == NULL) 180933cde130SBruce M Simpson optval = in6_mcast_loop; /* XXX VIMAGE */ 18108f002c6cSBruce M Simpson else 181133cde130SBruce M Simpson optval = im6o->im6o_multicast_loop; 18128f002c6cSBruce M Simpson INP_WUNLOCK(inp); 18138f002c6cSBruce M Simpson error = sooptcopyout(sopt, &optval, sizeof(u_int)); 18148f002c6cSBruce M Simpson break; 18158f002c6cSBruce M Simpson 18168f002c6cSBruce M Simpson case IPV6_MSFILTER: 181733cde130SBruce M Simpson if (im6o == NULL) { 18188f002c6cSBruce M Simpson error = EADDRNOTAVAIL; 18198f002c6cSBruce M Simpson INP_WUNLOCK(inp); 18208f002c6cSBruce M Simpson } else { 18218f002c6cSBruce M Simpson error = in6p_get_source_filters(inp, sopt); 18228f002c6cSBruce M Simpson } 18238f002c6cSBruce M Simpson break; 18248f002c6cSBruce M Simpson 18258f002c6cSBruce M Simpson default: 18268f002c6cSBruce M Simpson INP_WUNLOCK(inp); 18278f002c6cSBruce M Simpson error = ENOPROTOOPT; 18288f002c6cSBruce M Simpson break; 18298f002c6cSBruce M Simpson } 18308f002c6cSBruce M Simpson 18318f002c6cSBruce M Simpson INP_UNLOCK_ASSERT(inp); 18328f002c6cSBruce M Simpson 18338f002c6cSBruce M Simpson return (error); 18348f002c6cSBruce M Simpson } 18358f002c6cSBruce M Simpson 18368f002c6cSBruce M Simpson /* 183733cde130SBruce M Simpson * Look up the ifnet to use for a multicast group membership, 183833cde130SBruce M Simpson * given the address of an IPv6 group. 183933cde130SBruce M Simpson * 184033cde130SBruce M Simpson * This routine exists to support legacy IPv6 multicast applications. 184133cde130SBruce M Simpson * 1842cfae6a92SMark Johnston * Use the socket's current FIB number for any required FIB lookup. Look up the 1843cfae6a92SMark Johnston * group address in the unicast FIB, and use its ifp; usually, this points to 1844cfae6a92SMark Johnston * the default next-hop. If the FIB lookup fails, return NULL. 184533cde130SBruce M Simpson * 184633cde130SBruce M Simpson * FUTURE: Support multiple forwarding tables for IPv6. 184733cde130SBruce M Simpson * 184833cde130SBruce M Simpson * Returns NULL if no ifp could be found. 184933cde130SBruce M Simpson */ 185033cde130SBruce M Simpson static struct ifnet * 1851cfae6a92SMark Johnston in6p_lookup_mcast_ifp(const struct inpcb *inp, const struct sockaddr_in6 *gsin6) 185233cde130SBruce M Simpson { 18536ad7446cSAlexander V. Chernikov struct nhop_object *nh; 18549977be4aSAlexander V. Chernikov struct in6_addr dst; 18559977be4aSAlexander V. Chernikov uint32_t scopeid; 18569977be4aSAlexander V. Chernikov uint32_t fibnum; 185733cde130SBruce M Simpson 185833cde130SBruce M Simpson KASSERT(gsin6->sin6_family == AF_INET6, 185933cde130SBruce M Simpson ("%s: not AF_INET6 group", __func__)); 186033cde130SBruce M Simpson 18619977be4aSAlexander V. Chernikov in6_splitscope(&gsin6->sin6_addr, &dst, &scopeid); 1862cfae6a92SMark Johnston fibnum = inp->inp_inc.inc_fibnum; 18636ad7446cSAlexander V. Chernikov nh = fib6_lookup(fibnum, &dst, scopeid, 0, 0); 186433cde130SBruce M Simpson 18656ad7446cSAlexander V. Chernikov return (nh ? nh->nh_ifp : NULL); 186633cde130SBruce M Simpson } 186733cde130SBruce M Simpson 186833cde130SBruce M Simpson /* 18698f002c6cSBruce M Simpson * Join an IPv6 multicast group, possibly with a source. 187033cde130SBruce M Simpson * 187133cde130SBruce M Simpson * FIXME: The KAME use of the unspecified address (::) 187233cde130SBruce M Simpson * to join *all* multicast groups is currently unsupported. 1873*d74b7baeSGleb Smirnoff * 1874*d74b7baeSGleb Smirnoff * XXXGL: this function multiple times uses ifnet_byindex() without 1875*d74b7baeSGleb Smirnoff * proper protection - staying in epoch, or putting reference on ifnet. 18768f002c6cSBruce M Simpson */ 18778f002c6cSBruce M Simpson static int 18788f002c6cSBruce M Simpson in6p_join_group(struct inpcb *inp, struct sockopt *sopt) 18798f002c6cSBruce M Simpson { 18802cd6ad76SHans Petter Selasky struct in6_multi_head inmh; 18818f002c6cSBruce M Simpson struct group_source_req gsr; 1882*d74b7baeSGleb Smirnoff struct epoch_tracker et; 18838f002c6cSBruce M Simpson sockunion_t *gsa, *ssa; 18848f002c6cSBruce M Simpson struct ifnet *ifp; 18858f002c6cSBruce M Simpson struct in6_mfilter *imf; 18868f002c6cSBruce M Simpson struct ip6_moptions *imo; 18878f002c6cSBruce M Simpson struct in6_multi *inm; 18888f002c6cSBruce M Simpson struct in6_msource *lims; 18898f002c6cSBruce M Simpson int error, is_new; 18908f002c6cSBruce M Simpson 18912cd6ad76SHans Petter Selasky SLIST_INIT(&inmh); 18928f002c6cSBruce M Simpson ifp = NULL; 18931f81c2b6SBruce M Simpson lims = NULL; 18948f002c6cSBruce M Simpson error = 0; 18958f002c6cSBruce M Simpson 18968f002c6cSBruce M Simpson memset(&gsr, 0, sizeof(struct group_source_req)); 18978f002c6cSBruce M Simpson gsa = (sockunion_t *)&gsr.gsr_group; 18988f002c6cSBruce M Simpson gsa->ss.ss_family = AF_UNSPEC; 18998f002c6cSBruce M Simpson ssa = (sockunion_t *)&gsr.gsr_source; 19008f002c6cSBruce M Simpson ssa->ss.ss_family = AF_UNSPEC; 19018f002c6cSBruce M Simpson 190229dc7bc6SBruce M Simpson /* 190329dc7bc6SBruce M Simpson * Chew everything into struct group_source_req. 190429dc7bc6SBruce M Simpson * Overwrite the port field if present, as the sockaddr 190529dc7bc6SBruce M Simpson * being copied in may be matched with a binary comparison. 190629dc7bc6SBruce M Simpson * Ignore passed-in scope ID. 190729dc7bc6SBruce M Simpson */ 19088f002c6cSBruce M Simpson switch (sopt->sopt_name) { 19098f002c6cSBruce M Simpson case IPV6_JOIN_GROUP: { 19108f002c6cSBruce M Simpson struct ipv6_mreq mreq; 19118f002c6cSBruce M Simpson 19128f002c6cSBruce M Simpson error = sooptcopyin(sopt, &mreq, sizeof(struct ipv6_mreq), 19138f002c6cSBruce M Simpson sizeof(struct ipv6_mreq)); 19148f002c6cSBruce M Simpson if (error) 19158f002c6cSBruce M Simpson return (error); 19168f002c6cSBruce M Simpson 19178f002c6cSBruce M Simpson gsa->sin6.sin6_family = AF_INET6; 19188f002c6cSBruce M Simpson gsa->sin6.sin6_len = sizeof(struct sockaddr_in6); 19198f002c6cSBruce M Simpson gsa->sin6.sin6_addr = mreq.ipv6mr_multiaddr; 19208f002c6cSBruce M Simpson 192133cde130SBruce M Simpson if (mreq.ipv6mr_interface == 0) { 192233cde130SBruce M Simpson ifp = in6p_lookup_mcast_ifp(inp, &gsa->sin6); 192333cde130SBruce M Simpson } else { 1924*d74b7baeSGleb Smirnoff NET_EPOCH_ENTER(et); 19258f002c6cSBruce M Simpson ifp = ifnet_byindex(mreq.ipv6mr_interface); 1926*d74b7baeSGleb Smirnoff NET_EPOCH_EXIT(et); 1927*d74b7baeSGleb Smirnoff if (ifp == NULL) 1928*d74b7baeSGleb Smirnoff return (EADDRNOTAVAIL); 192933cde130SBruce M Simpson } 19308f002c6cSBruce M Simpson CTR3(KTR_MLD, "%s: ipv6mr_interface = %d, ifp = %p", 19318f002c6cSBruce M Simpson __func__, mreq.ipv6mr_interface, ifp); 19328f002c6cSBruce M Simpson } break; 19338f002c6cSBruce M Simpson 19348f002c6cSBruce M Simpson case MCAST_JOIN_GROUP: 19358f002c6cSBruce M Simpson case MCAST_JOIN_SOURCE_GROUP: 19368f002c6cSBruce M Simpson if (sopt->sopt_name == MCAST_JOIN_GROUP) { 19378f002c6cSBruce M Simpson error = sooptcopyin(sopt, &gsr, 19388f002c6cSBruce M Simpson sizeof(struct group_req), 19398f002c6cSBruce M Simpson sizeof(struct group_req)); 19408f002c6cSBruce M Simpson } else if (sopt->sopt_name == MCAST_JOIN_SOURCE_GROUP) { 19418f002c6cSBruce M Simpson error = sooptcopyin(sopt, &gsr, 19428f002c6cSBruce M Simpson sizeof(struct group_source_req), 19438f002c6cSBruce M Simpson sizeof(struct group_source_req)); 19448f002c6cSBruce M Simpson } 19458f002c6cSBruce M Simpson if (error) 19468f002c6cSBruce M Simpson return (error); 19478f002c6cSBruce M Simpson 19488f002c6cSBruce M Simpson if (gsa->sin6.sin6_family != AF_INET6 || 19498f002c6cSBruce M Simpson gsa->sin6.sin6_len != sizeof(struct sockaddr_in6)) 19508f002c6cSBruce M Simpson return (EINVAL); 19518f002c6cSBruce M Simpson 19528f002c6cSBruce M Simpson if (sopt->sopt_name == MCAST_JOIN_SOURCE_GROUP) { 19538f002c6cSBruce M Simpson if (ssa->sin6.sin6_family != AF_INET6 || 19548f002c6cSBruce M Simpson ssa->sin6.sin6_len != sizeof(struct sockaddr_in6)) 19558f002c6cSBruce M Simpson return (EINVAL); 195629dc7bc6SBruce M Simpson if (IN6_IS_ADDR_MULTICAST(&ssa->sin6.sin6_addr)) 195729dc7bc6SBruce M Simpson return (EINVAL); 195829dc7bc6SBruce M Simpson /* 195929dc7bc6SBruce M Simpson * TODO: Validate embedded scope ID in source 196029dc7bc6SBruce M Simpson * list entry against passed-in ifp, if and only 196129dc7bc6SBruce M Simpson * if source list filter entry is iface or node local. 196229dc7bc6SBruce M Simpson */ 196329dc7bc6SBruce M Simpson in6_clearscope(&ssa->sin6.sin6_addr); 19648f002c6cSBruce M Simpson ssa->sin6.sin6_port = 0; 196529dc7bc6SBruce M Simpson ssa->sin6.sin6_scope_id = 0; 19668f002c6cSBruce M Simpson } 1967*d74b7baeSGleb Smirnoff NET_EPOCH_ENTER(et); 19688f002c6cSBruce M Simpson ifp = ifnet_byindex(gsr.gsr_interface); 1969*d74b7baeSGleb Smirnoff NET_EPOCH_EXIT(et); 1970*d74b7baeSGleb Smirnoff if (ifp == NULL) 1971*d74b7baeSGleb Smirnoff return (EADDRNOTAVAIL); 19728f002c6cSBruce M Simpson break; 19738f002c6cSBruce M Simpson 19748f002c6cSBruce M Simpson default: 19758f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: unknown sopt_name %d", 19768f002c6cSBruce M Simpson __func__, sopt->sopt_name); 19778f002c6cSBruce M Simpson return (EOPNOTSUPP); 19788f002c6cSBruce M Simpson break; 19798f002c6cSBruce M Simpson } 19808f002c6cSBruce M Simpson 19818f002c6cSBruce M Simpson if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr)) 19828f002c6cSBruce M Simpson return (EINVAL); 19838f002c6cSBruce M Simpson 19848f002c6cSBruce M Simpson if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) 19858f002c6cSBruce M Simpson return (EADDRNOTAVAIL); 19868f002c6cSBruce M Simpson 198729dc7bc6SBruce M Simpson gsa->sin6.sin6_port = 0; 198829dc7bc6SBruce M Simpson gsa->sin6.sin6_scope_id = 0; 198929dc7bc6SBruce M Simpson 199033cde130SBruce M Simpson /* 199129dc7bc6SBruce M Simpson * Always set the scope zone ID on memberships created from userland. 199229dc7bc6SBruce M Simpson * Use the passed-in ifp to do this. 199329dc7bc6SBruce M Simpson * XXX The in6_setscope() return value is meaningless. 199429dc7bc6SBruce M Simpson * XXX SCOPE6_LOCK() is taken by in6_setscope(). 199533cde130SBruce M Simpson */ 199629dc7bc6SBruce M Simpson (void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL); 199733cde130SBruce M Simpson 199859854ecfSHans Petter Selasky IN6_MULTI_LOCK(); 199959854ecfSHans Petter Selasky 200059854ecfSHans Petter Selasky /* 200159854ecfSHans Petter Selasky * Find the membership in the membership list. 200259854ecfSHans Petter Selasky */ 20038f002c6cSBruce M Simpson imo = in6p_findmoptions(inp); 200459854ecfSHans Petter Selasky imf = im6o_match_group(imo, ifp, &gsa->sa); 200559854ecfSHans Petter Selasky if (imf == NULL) { 20068f002c6cSBruce M Simpson is_new = 1; 200759854ecfSHans Petter Selasky inm = NULL; 200859854ecfSHans Petter Selasky 200959854ecfSHans Petter Selasky if (ip6_mfilter_count(&imo->im6o_head) >= IPV6_MAX_MEMBERSHIPS) { 201059854ecfSHans Petter Selasky error = ENOMEM; 201159854ecfSHans Petter Selasky goto out_in6p_locked; 201259854ecfSHans Petter Selasky } 20138f002c6cSBruce M Simpson } else { 201459854ecfSHans Petter Selasky is_new = 0; 201559854ecfSHans Petter Selasky inm = imf->im6f_in6m; 201659854ecfSHans Petter Selasky 20171ee6b058SBruce M Simpson if (ssa->ss.ss_family != AF_UNSPEC) { 20181ee6b058SBruce M Simpson /* 20191ee6b058SBruce M Simpson * MCAST_JOIN_SOURCE_GROUP on an exclusive membership 20201ee6b058SBruce M Simpson * is an error. On an existing inclusive membership, 20211ee6b058SBruce M Simpson * it just adds the source to the filter list. 20221ee6b058SBruce M Simpson */ 20231ee6b058SBruce M Simpson if (imf->im6f_st[1] != MCAST_INCLUDE) { 20248f002c6cSBruce M Simpson error = EINVAL; 20258f002c6cSBruce M Simpson goto out_in6p_locked; 20268f002c6cSBruce M Simpson } 20271f81c2b6SBruce M Simpson /* 20281f81c2b6SBruce M Simpson * Throw out duplicates. 20291f81c2b6SBruce M Simpson * 20301f81c2b6SBruce M Simpson * XXX FIXME: This makes a naive assumption that 20311f81c2b6SBruce M Simpson * even if entries exist for *ssa in this imf, 20321f81c2b6SBruce M Simpson * they will be rejected as dupes, even if they 20331f81c2b6SBruce M Simpson * are not valid in the current mode (in-mode). 20341f81c2b6SBruce M Simpson * 20351f81c2b6SBruce M Simpson * in6_msource is transactioned just as for anything 20361f81c2b6SBruce M Simpson * else in SSM -- but note naive use of in6m_graft() 20371f81c2b6SBruce M Simpson * below for allocating new filter entries. 20381f81c2b6SBruce M Simpson * 20391f81c2b6SBruce M Simpson * This is only an issue if someone mixes the 20401f81c2b6SBruce M Simpson * full-state SSM API with the delta-based API, 20411f81c2b6SBruce M Simpson * which is discouraged in the relevant RFCs. 20421f81c2b6SBruce M Simpson */ 204359854ecfSHans Petter Selasky lims = im6o_match_source(imf, &ssa->sa); 20441f81c2b6SBruce M Simpson if (lims != NULL /*&& 20451f81c2b6SBruce M Simpson lims->im6sl_st[1] == MCAST_INCLUDE*/) { 20468f002c6cSBruce M Simpson error = EADDRNOTAVAIL; 20478f002c6cSBruce M Simpson goto out_in6p_locked; 20488f002c6cSBruce M Simpson } 20491ee6b058SBruce M Simpson } else { 20501ee6b058SBruce M Simpson /* 2051604a60d1SBruce M Simpson * MCAST_JOIN_GROUP alone, on any existing membership, 2052604a60d1SBruce M Simpson * is rejected, to stop the same inpcb tying up 2053604a60d1SBruce M Simpson * multiple refs to the in_multi. 2054604a60d1SBruce M Simpson * On an existing inclusive membership, this is also 2055604a60d1SBruce M Simpson * an error; if you want to change filter mode, 2056604a60d1SBruce M Simpson * you must use the userland API setsourcefilter(). 2057604a60d1SBruce M Simpson * XXX We don't reject this for imf in UNDEFINED 2058604a60d1SBruce M Simpson * state at t1, because allocation of a filter 2059604a60d1SBruce M Simpson * is atomic with allocation of a membership. 20601ee6b058SBruce M Simpson */ 20612ef5d803SKristof Provost error = EADDRINUSE; 20621ee6b058SBruce M Simpson goto out_in6p_locked; 20631ee6b058SBruce M Simpson } 20641ee6b058SBruce M Simpson } 20658f002c6cSBruce M Simpson 20668f002c6cSBruce M Simpson /* 20678f002c6cSBruce M Simpson * Begin state merge transaction at socket layer. 20688f002c6cSBruce M Simpson */ 20698f002c6cSBruce M Simpson INP_WLOCK_ASSERT(inp); 20708f002c6cSBruce M Simpson 20718f002c6cSBruce M Simpson /* 20728f002c6cSBruce M Simpson * Graft new source into filter list for this inpcb's 20738f002c6cSBruce M Simpson * membership of the group. The in6_multi may not have 20741ee6b058SBruce M Simpson * been allocated yet if this is a new membership, however, 20751ee6b058SBruce M Simpson * the in_mfilter slot will be allocated and must be initialized. 2076604a60d1SBruce M Simpson * 2077604a60d1SBruce M Simpson * Note: Grafting of exclusive mode filters doesn't happen 2078604a60d1SBruce M Simpson * in this path. 20791f81c2b6SBruce M Simpson * XXX: Should check for non-NULL lims (node exists but may 20801f81c2b6SBruce M Simpson * not be in-mode) for interop with full-state API. 20818f002c6cSBruce M Simpson */ 20828f002c6cSBruce M Simpson if (ssa->ss.ss_family != AF_UNSPEC) { 20838f002c6cSBruce M Simpson /* Membership starts in IN mode */ 20848f002c6cSBruce M Simpson if (is_new) { 20858f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: new join w/source", __func__); 208659854ecfSHans Petter Selasky imf = ip6_mfilter_alloc(M_NOWAIT, MCAST_UNDEFINED, MCAST_INCLUDE); 208759854ecfSHans Petter Selasky if (imf == NULL) { 208859854ecfSHans Petter Selasky error = ENOMEM; 208959854ecfSHans Petter Selasky goto out_in6p_locked; 209059854ecfSHans Petter Selasky } 20918f002c6cSBruce M Simpson } else { 20928f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: %s source", __func__, "allow"); 20938f002c6cSBruce M Simpson } 20948f002c6cSBruce M Simpson lims = im6f_graft(imf, MCAST_INCLUDE, &ssa->sin6); 20958f002c6cSBruce M Simpson if (lims == NULL) { 20968f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: merge imf state failed", 20978f002c6cSBruce M Simpson __func__); 20988f002c6cSBruce M Simpson error = ENOMEM; 209959854ecfSHans Petter Selasky goto out_in6p_locked; 21008f002c6cSBruce M Simpson } 21011ee6b058SBruce M Simpson } else { 21021ee6b058SBruce M Simpson /* No address specified; Membership starts in EX mode */ 21031ee6b058SBruce M Simpson if (is_new) { 21041ee6b058SBruce M Simpson CTR1(KTR_MLD, "%s: new join w/o source", __func__); 210559854ecfSHans Petter Selasky imf = ip6_mfilter_alloc(M_NOWAIT, MCAST_UNDEFINED, MCAST_EXCLUDE); 210659854ecfSHans Petter Selasky if (imf == NULL) { 210759854ecfSHans Petter Selasky error = ENOMEM; 210859854ecfSHans Petter Selasky goto out_in6p_locked; 210959854ecfSHans Petter Selasky } 21101ee6b058SBruce M Simpson } 21118f002c6cSBruce M Simpson } 21128f002c6cSBruce M Simpson 21138f002c6cSBruce M Simpson /* 21148f002c6cSBruce M Simpson * Begin state merge transaction at MLD layer. 21158f002c6cSBruce M Simpson */ 211659854ecfSHans Petter Selasky if (is_new) { 2117f00876fbSMark Johnston in_pcbref(inp); 2118f00876fbSMark Johnston INP_WUNLOCK(inp); 2119f00876fbSMark Johnston 2120f3e1324bSStephen Hurd error = in6_joingroup_locked(ifp, &gsa->sin6.sin6_addr, imf, 212159854ecfSHans Petter Selasky &imf->im6f_in6m, 0); 212259854ecfSHans Petter Selasky 212359854ecfSHans Petter Selasky INP_WLOCK(inp); 212459854ecfSHans Petter Selasky if (in_pcbrele_wlocked(inp)) { 212559854ecfSHans Petter Selasky error = ENXIO; 212659854ecfSHans Petter Selasky goto out_in6p_unlocked; 212759854ecfSHans Petter Selasky } 21284fd91336SAndrey V. Elsukov if (error) { 212959854ecfSHans Petter Selasky goto out_in6p_locked; 21304fd91336SAndrey V. Elsukov } 2131130f575dSHans Petter Selasky /* 2132130f575dSHans Petter Selasky * NOTE: Refcount from in6_joingroup_locked() 2133130f575dSHans Petter Selasky * is protecting membership. 2134130f575dSHans Petter Selasky */ 21355b64b824SHans Petter Selasky ip6_mfilter_insert(&imo->im6o_head, imf); 21368f002c6cSBruce M Simpson } else { 21378f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: merge inm state", __func__); 2138f3e1324bSStephen Hurd IN6_MULTI_LIST_LOCK(); 21398f002c6cSBruce M Simpson error = in6m_merge(inm, imf); 214059854ecfSHans Petter Selasky if (error) { 21418f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: failed to merge inm state", 21428f002c6cSBruce M Simpson __func__); 214359854ecfSHans Petter Selasky IN6_MULTI_LIST_UNLOCK(); 214459854ecfSHans Petter Selasky im6f_rollback(imf); 214559854ecfSHans Petter Selasky im6f_reap(imf); 214659854ecfSHans Petter Selasky goto out_in6p_locked; 214759854ecfSHans Petter Selasky } 21488f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: doing mld downcall", __func__); 21498f002c6cSBruce M Simpson error = mld_change_state(inm, 0); 215059854ecfSHans Petter Selasky IN6_MULTI_LIST_UNLOCK(); 215159854ecfSHans Petter Selasky 215259854ecfSHans Petter Selasky if (error) { 21538f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: failed mld downcall", 21548f002c6cSBruce M Simpson __func__); 21558f002c6cSBruce M Simpson im6f_rollback(imf); 21568f002c6cSBruce M Simpson im6f_reap(imf); 215759854ecfSHans Petter Selasky goto out_in6p_locked; 215859854ecfSHans Petter Selasky } 21598f002c6cSBruce M Simpson } 21608f002c6cSBruce M Simpson 216159854ecfSHans Petter Selasky im6f_commit(imf); 216259854ecfSHans Petter Selasky imf = NULL; 21638f002c6cSBruce M Simpson 21648f002c6cSBruce M Simpson out_in6p_locked: 21658f002c6cSBruce M Simpson INP_WUNLOCK(inp); 216659854ecfSHans Petter Selasky out_in6p_unlocked: 216759854ecfSHans Petter Selasky IN6_MULTI_UNLOCK(); 216859854ecfSHans Petter Selasky 216959854ecfSHans Petter Selasky if (is_new && imf) { 217059854ecfSHans Petter Selasky if (imf->im6f_in6m != NULL) { 217159854ecfSHans Petter Selasky struct in6_multi_head inmh; 217259854ecfSHans Petter Selasky 217359854ecfSHans Petter Selasky SLIST_INIT(&inmh); 217459854ecfSHans Petter Selasky SLIST_INSERT_HEAD(&inmh, imf->im6f_in6m, in6m_defer); 21752cd6ad76SHans Petter Selasky in6m_release_list_deferred(&inmh); 217659854ecfSHans Petter Selasky } 217759854ecfSHans Petter Selasky ip6_mfilter_free(imf); 217859854ecfSHans Petter Selasky } 21798f002c6cSBruce M Simpson return (error); 21808f002c6cSBruce M Simpson } 21818f002c6cSBruce M Simpson 21828f002c6cSBruce M Simpson /* 21838f002c6cSBruce M Simpson * Leave an IPv6 multicast group on an inpcb, possibly with a source. 21848f002c6cSBruce M Simpson */ 21858f002c6cSBruce M Simpson static int 21868f002c6cSBruce M Simpson in6p_leave_group(struct inpcb *inp, struct sockopt *sopt) 21878f002c6cSBruce M Simpson { 218829dc7bc6SBruce M Simpson struct ipv6_mreq mreq; 21898f002c6cSBruce M Simpson struct group_source_req gsr; 2190*d74b7baeSGleb Smirnoff struct epoch_tracker et; 21918f002c6cSBruce M Simpson sockunion_t *gsa, *ssa; 21928f002c6cSBruce M Simpson struct ifnet *ifp; 21938f002c6cSBruce M Simpson struct in6_mfilter *imf; 21948f002c6cSBruce M Simpson struct ip6_moptions *imo; 21958f002c6cSBruce M Simpson struct in6_msource *ims; 21968f002c6cSBruce M Simpson struct in6_multi *inm; 219729dc7bc6SBruce M Simpson uint32_t ifindex; 219859854ecfSHans Petter Selasky int error; 219959854ecfSHans Petter Selasky bool is_final; 22008f002c6cSBruce M Simpson #ifdef KTR 22018f002c6cSBruce M Simpson char ip6tbuf[INET6_ADDRSTRLEN]; 22028f002c6cSBruce M Simpson #endif 22038f002c6cSBruce M Simpson 22048f002c6cSBruce M Simpson ifp = NULL; 220529dc7bc6SBruce M Simpson ifindex = 0; 22068f002c6cSBruce M Simpson error = 0; 220759854ecfSHans Petter Selasky is_final = true; 22088f002c6cSBruce M Simpson 22098f002c6cSBruce M Simpson memset(&gsr, 0, sizeof(struct group_source_req)); 22108f002c6cSBruce M Simpson gsa = (sockunion_t *)&gsr.gsr_group; 22118f002c6cSBruce M Simpson gsa->ss.ss_family = AF_UNSPEC; 22128f002c6cSBruce M Simpson ssa = (sockunion_t *)&gsr.gsr_source; 22138f002c6cSBruce M Simpson ssa->ss.ss_family = AF_UNSPEC; 22148f002c6cSBruce M Simpson 221529dc7bc6SBruce M Simpson /* 221629dc7bc6SBruce M Simpson * Chew everything passed in up into a struct group_source_req 221729dc7bc6SBruce M Simpson * as that is easier to process. 221829dc7bc6SBruce M Simpson * Note: Any embedded scope ID in the multicast group passed 221929dc7bc6SBruce M Simpson * in by userland is ignored, the interface index is the recommended 222029dc7bc6SBruce M Simpson * mechanism to specify an interface; see below. 222129dc7bc6SBruce M Simpson */ 22228f002c6cSBruce M Simpson switch (sopt->sopt_name) { 222329dc7bc6SBruce M Simpson case IPV6_LEAVE_GROUP: 22248f002c6cSBruce M Simpson error = sooptcopyin(sopt, &mreq, sizeof(struct ipv6_mreq), 22258f002c6cSBruce M Simpson sizeof(struct ipv6_mreq)); 22268f002c6cSBruce M Simpson if (error) 22278f002c6cSBruce M Simpson return (error); 22288f002c6cSBruce M Simpson gsa->sin6.sin6_family = AF_INET6; 22298f002c6cSBruce M Simpson gsa->sin6.sin6_len = sizeof(struct sockaddr_in6); 22308f002c6cSBruce M Simpson gsa->sin6.sin6_addr = mreq.ipv6mr_multiaddr; 223129dc7bc6SBruce M Simpson gsa->sin6.sin6_port = 0; 223229dc7bc6SBruce M Simpson gsa->sin6.sin6_scope_id = 0; 223329dc7bc6SBruce M Simpson ifindex = mreq.ipv6mr_interface; 223429dc7bc6SBruce M Simpson break; 22358f002c6cSBruce M Simpson 22368f002c6cSBruce M Simpson case MCAST_LEAVE_GROUP: 22378f002c6cSBruce M Simpson case MCAST_LEAVE_SOURCE_GROUP: 22388f002c6cSBruce M Simpson if (sopt->sopt_name == MCAST_LEAVE_GROUP) { 22398f002c6cSBruce M Simpson error = sooptcopyin(sopt, &gsr, 22408f002c6cSBruce M Simpson sizeof(struct group_req), 22418f002c6cSBruce M Simpson sizeof(struct group_req)); 22428f002c6cSBruce M Simpson } else if (sopt->sopt_name == MCAST_LEAVE_SOURCE_GROUP) { 22438f002c6cSBruce M Simpson error = sooptcopyin(sopt, &gsr, 22448f002c6cSBruce M Simpson sizeof(struct group_source_req), 22458f002c6cSBruce M Simpson sizeof(struct group_source_req)); 22468f002c6cSBruce M Simpson } 22478f002c6cSBruce M Simpson if (error) 22488f002c6cSBruce M Simpson return (error); 22498f002c6cSBruce M Simpson 22508f002c6cSBruce M Simpson if (gsa->sin6.sin6_family != AF_INET6 || 22518f002c6cSBruce M Simpson gsa->sin6.sin6_len != sizeof(struct sockaddr_in6)) 22528f002c6cSBruce M Simpson return (EINVAL); 22538f002c6cSBruce M Simpson if (sopt->sopt_name == MCAST_LEAVE_SOURCE_GROUP) { 22548f002c6cSBruce M Simpson if (ssa->sin6.sin6_family != AF_INET6 || 22558f002c6cSBruce M Simpson ssa->sin6.sin6_len != sizeof(struct sockaddr_in6)) 22568f002c6cSBruce M Simpson return (EINVAL); 225729dc7bc6SBruce M Simpson if (IN6_IS_ADDR_MULTICAST(&ssa->sin6.sin6_addr)) 225829dc7bc6SBruce M Simpson return (EINVAL); 225929dc7bc6SBruce M Simpson /* 226029dc7bc6SBruce M Simpson * TODO: Validate embedded scope ID in source 226129dc7bc6SBruce M Simpson * list entry against passed-in ifp, if and only 226229dc7bc6SBruce M Simpson * if source list filter entry is iface or node local. 226329dc7bc6SBruce M Simpson */ 226429dc7bc6SBruce M Simpson in6_clearscope(&ssa->sin6.sin6_addr); 22658f002c6cSBruce M Simpson } 226629dc7bc6SBruce M Simpson gsa->sin6.sin6_port = 0; 226729dc7bc6SBruce M Simpson gsa->sin6.sin6_scope_id = 0; 226829dc7bc6SBruce M Simpson ifindex = gsr.gsr_interface; 22698f002c6cSBruce M Simpson break; 22708f002c6cSBruce M Simpson 22718f002c6cSBruce M Simpson default: 22728f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: unknown sopt_name %d", 22738f002c6cSBruce M Simpson __func__, sopt->sopt_name); 22748f002c6cSBruce M Simpson return (EOPNOTSUPP); 22758f002c6cSBruce M Simpson break; 22768f002c6cSBruce M Simpson } 22778f002c6cSBruce M Simpson 22788f002c6cSBruce M Simpson if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr)) 22798f002c6cSBruce M Simpson return (EINVAL); 22808f002c6cSBruce M Simpson 228133cde130SBruce M Simpson /* 228229dc7bc6SBruce M Simpson * Validate interface index if provided. If no interface index 228329dc7bc6SBruce M Simpson * was provided separately, attempt to look the membership up 228429dc7bc6SBruce M Simpson * from the default scope as a last resort to disambiguate 228529dc7bc6SBruce M Simpson * the membership we are being asked to leave. 228629dc7bc6SBruce M Simpson * XXX SCOPE6 lock potentially taken here. 228733cde130SBruce M Simpson */ 228829dc7bc6SBruce M Simpson if (ifindex != 0) { 2289*d74b7baeSGleb Smirnoff NET_EPOCH_ENTER(et); 229029dc7bc6SBruce M Simpson ifp = ifnet_byindex(ifindex); 2291*d74b7baeSGleb Smirnoff NET_EPOCH_EXIT(et); /* XXXGL: unsafe ifp */ 229229dc7bc6SBruce M Simpson if (ifp == NULL) 229329dc7bc6SBruce M Simpson return (EADDRNOTAVAIL); 229429dc7bc6SBruce M Simpson (void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL); 229529dc7bc6SBruce M Simpson } else { 229629dc7bc6SBruce M Simpson error = sa6_embedscope(&gsa->sin6, V_ip6_use_defzone); 229729dc7bc6SBruce M Simpson if (error) 229829dc7bc6SBruce M Simpson return (EADDRNOTAVAIL); 229929dc7bc6SBruce M Simpson /* 2300b36c89e5SBruce M Simpson * Some badly behaved applications don't pass an ifindex 2301b36c89e5SBruce M Simpson * or a scope ID, which is an API violation. In this case, 2302b36c89e5SBruce M Simpson * perform a lookup as per a v6 join. 2303b36c89e5SBruce M Simpson * 230429dc7bc6SBruce M Simpson * XXX For now, stomp on zone ID for the corner case. 230529dc7bc6SBruce M Simpson * This is not the 'KAME way', but we need to see the ifp 230629dc7bc6SBruce M Simpson * directly until such time as this implementation is 230729dc7bc6SBruce M Simpson * refactored, assuming the scope IDs are the way to go. 230829dc7bc6SBruce M Simpson */ 230929dc7bc6SBruce M Simpson ifindex = ntohs(gsa->sin6.sin6_addr.s6_addr16[1]); 2310b36c89e5SBruce M Simpson if (ifindex == 0) { 2311b36c89e5SBruce M Simpson CTR2(KTR_MLD, "%s: warning: no ifindex, looking up " 2312b36c89e5SBruce M Simpson "ifp for group %s.", __func__, 2313b36c89e5SBruce M Simpson ip6_sprintf(ip6tbuf, &gsa->sin6.sin6_addr)); 2314b36c89e5SBruce M Simpson ifp = in6p_lookup_mcast_ifp(inp, &gsa->sin6); 2315b36c89e5SBruce M Simpson } else { 2316*d74b7baeSGleb Smirnoff NET_EPOCH_ENTER(et); 231729dc7bc6SBruce M Simpson ifp = ifnet_byindex(ifindex); 2318*d74b7baeSGleb Smirnoff NET_EPOCH_EXIT(et); /* XXXGL: unsafe ifp */ 2319b36c89e5SBruce M Simpson } 232029dc7bc6SBruce M Simpson if (ifp == NULL) 232129dc7bc6SBruce M Simpson return (EADDRNOTAVAIL); 232229dc7bc6SBruce M Simpson } 232329dc7bc6SBruce M Simpson 232429dc7bc6SBruce M Simpson CTR2(KTR_MLD, "%s: ifp = %p", __func__, ifp); 232529dc7bc6SBruce M Simpson KASSERT(ifp != NULL, ("%s: ifp did not resolve", __func__)); 232633cde130SBruce M Simpson 232759854ecfSHans Petter Selasky IN6_MULTI_LOCK(); 232859854ecfSHans Petter Selasky 23298f002c6cSBruce M Simpson /* 233059854ecfSHans Petter Selasky * Find the membership in the membership list. 23318f002c6cSBruce M Simpson */ 23328f002c6cSBruce M Simpson imo = in6p_findmoptions(inp); 233359854ecfSHans Petter Selasky imf = im6o_match_group(imo, ifp, &gsa->sa); 233459854ecfSHans Petter Selasky if (imf == NULL) { 23358f002c6cSBruce M Simpson error = EADDRNOTAVAIL; 23368f002c6cSBruce M Simpson goto out_in6p_locked; 23378f002c6cSBruce M Simpson } 233859854ecfSHans Petter Selasky inm = imf->im6f_in6m; 23398f002c6cSBruce M Simpson 23408f002c6cSBruce M Simpson if (ssa->ss.ss_family != AF_UNSPEC) 234159854ecfSHans Petter Selasky is_final = false; 23428f002c6cSBruce M Simpson 23438f002c6cSBruce M Simpson /* 23448f002c6cSBruce M Simpson * Begin state merge transaction at socket layer. 23458f002c6cSBruce M Simpson */ 23468f002c6cSBruce M Simpson INP_WLOCK_ASSERT(inp); 23478f002c6cSBruce M Simpson 23488f002c6cSBruce M Simpson /* 23498f002c6cSBruce M Simpson * If we were instructed only to leave a given source, do so. 23508f002c6cSBruce M Simpson * MCAST_LEAVE_SOURCE_GROUP is only valid for inclusive memberships. 23518f002c6cSBruce M Simpson */ 23528f002c6cSBruce M Simpson if (is_final) { 235359854ecfSHans Petter Selasky ip6_mfilter_remove(&imo->im6o_head, imf); 23548f002c6cSBruce M Simpson im6f_leave(imf); 2355a4c5668dSHans Petter Selasky 2356a4c5668dSHans Petter Selasky /* 2357a4c5668dSHans Petter Selasky * Give up the multicast address record to which 2358a4c5668dSHans Petter Selasky * the membership points. 2359a4c5668dSHans Petter Selasky */ 2360a4c5668dSHans Petter Selasky (void)in6_leavegroup_locked(inm, imf); 23618f002c6cSBruce M Simpson } else { 23628f002c6cSBruce M Simpson if (imf->im6f_st[0] == MCAST_EXCLUDE) { 23638f002c6cSBruce M Simpson error = EADDRNOTAVAIL; 23648f002c6cSBruce M Simpson goto out_in6p_locked; 23658f002c6cSBruce M Simpson } 236659854ecfSHans Petter Selasky ims = im6o_match_source(imf, &ssa->sa); 23678f002c6cSBruce M Simpson if (ims == NULL) { 23688f002c6cSBruce M Simpson CTR3(KTR_MLD, "%s: source %p %spresent", __func__, 23698f002c6cSBruce M Simpson ip6_sprintf(ip6tbuf, &ssa->sin6.sin6_addr), 23708f002c6cSBruce M Simpson "not "); 23718f002c6cSBruce M Simpson error = EADDRNOTAVAIL; 23728f002c6cSBruce M Simpson goto out_in6p_locked; 23738f002c6cSBruce M Simpson } 23748f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: %s source", __func__, "block"); 23758f002c6cSBruce M Simpson error = im6f_prune(imf, &ssa->sin6); 23768f002c6cSBruce M Simpson if (error) { 23778f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: merge imf state failed", 23788f002c6cSBruce M Simpson __func__); 23798f002c6cSBruce M Simpson goto out_in6p_locked; 23808f002c6cSBruce M Simpson } 23818f002c6cSBruce M Simpson } 23828f002c6cSBruce M Simpson 23838f002c6cSBruce M Simpson /* 23848f002c6cSBruce M Simpson * Begin state merge transaction at MLD layer. 23858f002c6cSBruce M Simpson */ 238659854ecfSHans Petter Selasky if (!is_final) { 238759854ecfSHans Petter Selasky CTR1(KTR_MLD, "%s: merge inm state", __func__); 238859854ecfSHans Petter Selasky IN6_MULTI_LIST_LOCK(); 238959854ecfSHans Petter Selasky error = in6m_merge(inm, imf); 239059854ecfSHans Petter Selasky if (error) { 239159854ecfSHans Petter Selasky CTR1(KTR_MLD, "%s: failed to merge inm state", 239259854ecfSHans Petter Selasky __func__); 239359854ecfSHans Petter Selasky IN6_MULTI_LIST_UNLOCK(); 239459854ecfSHans Petter Selasky im6f_rollback(imf); 239559854ecfSHans Petter Selasky im6f_reap(imf); 239659854ecfSHans Petter Selasky goto out_in6p_locked; 239759854ecfSHans Petter Selasky } 2398f00876fbSMark Johnston 239959854ecfSHans Petter Selasky CTR1(KTR_MLD, "%s: doing mld downcall", __func__); 240059854ecfSHans Petter Selasky error = mld_change_state(inm, 0); 240159854ecfSHans Petter Selasky IN6_MULTI_LIST_UNLOCK(); 240259854ecfSHans Petter Selasky if (error) { 240359854ecfSHans Petter Selasky CTR1(KTR_MLD, "%s: failed mld downcall", 240459854ecfSHans Petter Selasky __func__); 240559854ecfSHans Petter Selasky im6f_rollback(imf); 240659854ecfSHans Petter Selasky im6f_reap(imf); 240759854ecfSHans Petter Selasky goto out_in6p_locked; 240859854ecfSHans Petter Selasky } 240959854ecfSHans Petter Selasky } 241059854ecfSHans Petter Selasky 241159854ecfSHans Petter Selasky im6f_commit(imf); 241259854ecfSHans Petter Selasky im6f_reap(imf); 241359854ecfSHans Petter Selasky 241459854ecfSHans Petter Selasky out_in6p_locked: 241559854ecfSHans Petter Selasky INP_WUNLOCK(inp); 241659854ecfSHans Petter Selasky 2417a4c5668dSHans Petter Selasky if (is_final && imf) 241859854ecfSHans Petter Selasky ip6_mfilter_free(imf); 24198f002c6cSBruce M Simpson 2420f00876fbSMark Johnston IN6_MULTI_UNLOCK(); 24218f002c6cSBruce M Simpson return (error); 24228f002c6cSBruce M Simpson } 24238f002c6cSBruce M Simpson 24248f002c6cSBruce M Simpson /* 24258f002c6cSBruce M Simpson * Select the interface for transmitting IPv6 multicast datagrams. 24268f002c6cSBruce M Simpson * 24278f002c6cSBruce M Simpson * Either an instance of struct in6_addr or an instance of struct ipv6_mreqn 24288f002c6cSBruce M Simpson * may be passed to this socket option. An address of in6addr_any or an 24298f002c6cSBruce M Simpson * interface index of 0 is used to remove a previous selection. 24308f002c6cSBruce M Simpson * When no interface is selected, one is chosen for every send. 24318f002c6cSBruce M Simpson */ 24328f002c6cSBruce M Simpson static int 24338f002c6cSBruce M Simpson in6p_set_multicast_if(struct inpcb *inp, struct sockopt *sopt) 24348f002c6cSBruce M Simpson { 2435*d74b7baeSGleb Smirnoff struct epoch_tracker et; 24368f002c6cSBruce M Simpson struct ifnet *ifp; 24378f002c6cSBruce M Simpson struct ip6_moptions *imo; 24388f002c6cSBruce M Simpson u_int ifindex; 24398f002c6cSBruce M Simpson int error; 24408f002c6cSBruce M Simpson 24418f002c6cSBruce M Simpson if (sopt->sopt_valsize != sizeof(u_int)) 24428f002c6cSBruce M Simpson return (EINVAL); 24438f002c6cSBruce M Simpson 24448f002c6cSBruce M Simpson error = sooptcopyin(sopt, &ifindex, sizeof(u_int), sizeof(u_int)); 24458f002c6cSBruce M Simpson if (error) 24468f002c6cSBruce M Simpson return (error); 2447*d74b7baeSGleb Smirnoff NET_EPOCH_ENTER(et); 2448e2956804SAndrey V. Elsukov if (ifindex == 0) 2449e2956804SAndrey V. Elsukov ifp = NULL; 2450e2956804SAndrey V. Elsukov else { 24518f002c6cSBruce M Simpson ifp = ifnet_byindex(ifindex); 2452*d74b7baeSGleb Smirnoff if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) { 2453*d74b7baeSGleb Smirnoff NET_EPOCH_EXIT(et); 24548f002c6cSBruce M Simpson return (EADDRNOTAVAIL); 2455e2956804SAndrey V. Elsukov } 2456*d74b7baeSGleb Smirnoff } 24578f002c6cSBruce M Simpson imo = in6p_findmoptions(inp); 2458*d74b7baeSGleb Smirnoff imo->im6o_multicast_ifp = ifp; /* XXXGL: reference?! */ 2459*d74b7baeSGleb Smirnoff NET_EPOCH_EXIT(et); 24608f002c6cSBruce M Simpson INP_WUNLOCK(inp); 24618f002c6cSBruce M Simpson 24628f002c6cSBruce M Simpson return (0); 24638f002c6cSBruce M Simpson } 24648f002c6cSBruce M Simpson 24658f002c6cSBruce M Simpson /* 24668f002c6cSBruce M Simpson * Atomically set source filters on a socket for an IPv6 multicast group. 2467f00876fbSMark Johnston * 2468*d74b7baeSGleb Smirnoff * XXXGL: unsafely exits epoch with ifnet pointer 24698f002c6cSBruce M Simpson */ 24708f002c6cSBruce M Simpson static int 24718f002c6cSBruce M Simpson in6p_set_source_filters(struct inpcb *inp, struct sockopt *sopt) 24728f002c6cSBruce M Simpson { 24738f002c6cSBruce M Simpson struct __msfilterreq msfr; 2474*d74b7baeSGleb Smirnoff struct epoch_tracker et; 24758f002c6cSBruce M Simpson sockunion_t *gsa; 24768f002c6cSBruce M Simpson struct ifnet *ifp; 24778f002c6cSBruce M Simpson struct in6_mfilter *imf; 24788f002c6cSBruce M Simpson struct ip6_moptions *imo; 24798f002c6cSBruce M Simpson struct in6_multi *inm; 24808f002c6cSBruce M Simpson int error; 24818f002c6cSBruce M Simpson 24828f002c6cSBruce M Simpson error = sooptcopyin(sopt, &msfr, sizeof(struct __msfilterreq), 24838f002c6cSBruce M Simpson sizeof(struct __msfilterreq)); 24848f002c6cSBruce M Simpson if (error) 24858f002c6cSBruce M Simpson return (error); 24868f002c6cSBruce M Simpson 24870dc5893eSBruce M Simpson if (msfr.msfr_nsrcs > in6_mcast_maxsocksrc) 24880dc5893eSBruce M Simpson return (ENOBUFS); 24890dc5893eSBruce M Simpson 24900dc5893eSBruce M Simpson if (msfr.msfr_fmode != MCAST_EXCLUDE && 24910dc5893eSBruce M Simpson msfr.msfr_fmode != MCAST_INCLUDE) 24928f002c6cSBruce M Simpson return (EINVAL); 24938f002c6cSBruce M Simpson 24948f002c6cSBruce M Simpson if (msfr.msfr_group.ss_family != AF_INET6 || 24958f002c6cSBruce M Simpson msfr.msfr_group.ss_len != sizeof(struct sockaddr_in6)) 24968f002c6cSBruce M Simpson return (EINVAL); 24978f002c6cSBruce M Simpson 24988f002c6cSBruce M Simpson gsa = (sockunion_t *)&msfr.msfr_group; 24998f002c6cSBruce M Simpson if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr)) 25008f002c6cSBruce M Simpson return (EINVAL); 25018f002c6cSBruce M Simpson 25028f002c6cSBruce M Simpson gsa->sin6.sin6_port = 0; /* ignore port */ 25038f002c6cSBruce M Simpson 2504*d74b7baeSGleb Smirnoff NET_EPOCH_ENTER(et); 25058f002c6cSBruce M Simpson ifp = ifnet_byindex(msfr.msfr_ifindex); 2506*d74b7baeSGleb Smirnoff NET_EPOCH_EXIT(et); 25078f002c6cSBruce M Simpson if (ifp == NULL) 25088f002c6cSBruce M Simpson return (EADDRNOTAVAIL); 250929dc7bc6SBruce M Simpson (void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL); 25108f002c6cSBruce M Simpson 25118f002c6cSBruce M Simpson /* 25128f002c6cSBruce M Simpson * Take the INP write lock. 25138f002c6cSBruce M Simpson * Check if this socket is a member of this group. 25148f002c6cSBruce M Simpson */ 25158f002c6cSBruce M Simpson imo = in6p_findmoptions(inp); 251659854ecfSHans Petter Selasky imf = im6o_match_group(imo, ifp, &gsa->sa); 251759854ecfSHans Petter Selasky if (imf == NULL) { 25188f002c6cSBruce M Simpson error = EADDRNOTAVAIL; 25198f002c6cSBruce M Simpson goto out_in6p_locked; 25208f002c6cSBruce M Simpson } 252159854ecfSHans Petter Selasky inm = imf->im6f_in6m; 25228f002c6cSBruce M Simpson 25238f002c6cSBruce M Simpson /* 25248f002c6cSBruce M Simpson * Begin state merge transaction at socket layer. 25258f002c6cSBruce M Simpson */ 25268f002c6cSBruce M Simpson INP_WLOCK_ASSERT(inp); 25278f002c6cSBruce M Simpson 25288f002c6cSBruce M Simpson imf->im6f_st[1] = msfr.msfr_fmode; 25298f002c6cSBruce M Simpson 25308f002c6cSBruce M Simpson /* 25318f002c6cSBruce M Simpson * Apply any new source filters, if present. 25328f002c6cSBruce M Simpson * Make a copy of the user-space source vector so 25338f002c6cSBruce M Simpson * that we may copy them with a single copyin. This 25348f002c6cSBruce M Simpson * allows us to deal with page faults up-front. 25358f002c6cSBruce M Simpson */ 25368f002c6cSBruce M Simpson if (msfr.msfr_nsrcs > 0) { 25378f002c6cSBruce M Simpson struct in6_msource *lims; 25388f002c6cSBruce M Simpson struct sockaddr_in6 *psin; 25398f002c6cSBruce M Simpson struct sockaddr_storage *kss, *pkss; 25408f002c6cSBruce M Simpson int i; 25418f002c6cSBruce M Simpson 25428f002c6cSBruce M Simpson INP_WUNLOCK(inp); 25438f002c6cSBruce M Simpson 25448f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: loading %lu source list entries", 25458f002c6cSBruce M Simpson __func__, (unsigned long)msfr.msfr_nsrcs); 25468f002c6cSBruce M Simpson kss = malloc(sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs, 25478f002c6cSBruce M Simpson M_TEMP, M_WAITOK); 25488f002c6cSBruce M Simpson error = copyin(msfr.msfr_srcs, kss, 25498f002c6cSBruce M Simpson sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs); 25508f002c6cSBruce M Simpson if (error) { 25518f002c6cSBruce M Simpson free(kss, M_TEMP); 25528f002c6cSBruce M Simpson return (error); 25538f002c6cSBruce M Simpson } 25548f002c6cSBruce M Simpson 25558f002c6cSBruce M Simpson INP_WLOCK(inp); 25568f002c6cSBruce M Simpson 25578f002c6cSBruce M Simpson /* 25588f002c6cSBruce M Simpson * Mark all source filters as UNDEFINED at t1. 25598f002c6cSBruce M Simpson * Restore new group filter mode, as im6f_leave() 25608f002c6cSBruce M Simpson * will set it to INCLUDE. 25618f002c6cSBruce M Simpson */ 25628f002c6cSBruce M Simpson im6f_leave(imf); 25638f002c6cSBruce M Simpson imf->im6f_st[1] = msfr.msfr_fmode; 25648f002c6cSBruce M Simpson 25658f002c6cSBruce M Simpson /* 25668f002c6cSBruce M Simpson * Update socket layer filters at t1, lazy-allocating 25678f002c6cSBruce M Simpson * new entries. This saves a bunch of memory at the 25688f002c6cSBruce M Simpson * cost of one RB_FIND() per source entry; duplicate 25698f002c6cSBruce M Simpson * entries in the msfr_nsrcs vector are ignored. 25708f002c6cSBruce M Simpson * If we encounter an error, rollback transaction. 25718f002c6cSBruce M Simpson * 25728f002c6cSBruce M Simpson * XXX This too could be replaced with a set-symmetric 25738f002c6cSBruce M Simpson * difference like loop to avoid walking from root 25748f002c6cSBruce M Simpson * every time, as the key space is common. 25758f002c6cSBruce M Simpson */ 25768f002c6cSBruce M Simpson for (i = 0, pkss = kss; i < msfr.msfr_nsrcs; i++, pkss++) { 25778f002c6cSBruce M Simpson psin = (struct sockaddr_in6 *)pkss; 25788f002c6cSBruce M Simpson if (psin->sin6_family != AF_INET6) { 25798f002c6cSBruce M Simpson error = EAFNOSUPPORT; 25808f002c6cSBruce M Simpson break; 25818f002c6cSBruce M Simpson } 25828f002c6cSBruce M Simpson if (psin->sin6_len != sizeof(struct sockaddr_in6)) { 25838f002c6cSBruce M Simpson error = EINVAL; 25848f002c6cSBruce M Simpson break; 25858f002c6cSBruce M Simpson } 258629dc7bc6SBruce M Simpson if (IN6_IS_ADDR_MULTICAST(&psin->sin6_addr)) { 258729dc7bc6SBruce M Simpson error = EINVAL; 258829dc7bc6SBruce M Simpson break; 258929dc7bc6SBruce M Simpson } 259029dc7bc6SBruce M Simpson /* 259129dc7bc6SBruce M Simpson * TODO: Validate embedded scope ID in source 259229dc7bc6SBruce M Simpson * list entry against passed-in ifp, if and only 259329dc7bc6SBruce M Simpson * if source list filter entry is iface or node local. 259429dc7bc6SBruce M Simpson */ 259529dc7bc6SBruce M Simpson in6_clearscope(&psin->sin6_addr); 25968f002c6cSBruce M Simpson error = im6f_get_source(imf, psin, &lims); 25978f002c6cSBruce M Simpson if (error) 25988f002c6cSBruce M Simpson break; 25998f002c6cSBruce M Simpson lims->im6sl_st[1] = imf->im6f_st[1]; 26008f002c6cSBruce M Simpson } 26018f002c6cSBruce M Simpson free(kss, M_TEMP); 26028f002c6cSBruce M Simpson } 26038f002c6cSBruce M Simpson 26048f002c6cSBruce M Simpson if (error) 26058f002c6cSBruce M Simpson goto out_im6f_rollback; 26068f002c6cSBruce M Simpson 26078f002c6cSBruce M Simpson INP_WLOCK_ASSERT(inp); 2608f3e1324bSStephen Hurd IN6_MULTI_LIST_LOCK(); 26098f002c6cSBruce M Simpson 26108f002c6cSBruce M Simpson /* 26118f002c6cSBruce M Simpson * Begin state merge transaction at MLD layer. 26128f002c6cSBruce M Simpson */ 26138f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: merge inm state", __func__); 26148f002c6cSBruce M Simpson error = in6m_merge(inm, imf); 26154fd91336SAndrey V. Elsukov if (error) 26168f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: failed to merge inm state", __func__); 26174fd91336SAndrey V. Elsukov else { 26188f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: doing mld downcall", __func__); 26198f002c6cSBruce M Simpson error = mld_change_state(inm, 0); 26208f002c6cSBruce M Simpson if (error) 26218f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: failed mld downcall", __func__); 26224fd91336SAndrey V. Elsukov } 26238f002c6cSBruce M Simpson 2624f3e1324bSStephen Hurd IN6_MULTI_LIST_UNLOCK(); 26258f002c6cSBruce M Simpson 26268f002c6cSBruce M Simpson out_im6f_rollback: 26278f002c6cSBruce M Simpson if (error) 26288f002c6cSBruce M Simpson im6f_rollback(imf); 26298f002c6cSBruce M Simpson else 26308f002c6cSBruce M Simpson im6f_commit(imf); 26318f002c6cSBruce M Simpson 26328f002c6cSBruce M Simpson im6f_reap(imf); 26338f002c6cSBruce M Simpson 26348f002c6cSBruce M Simpson out_in6p_locked: 26358f002c6cSBruce M Simpson INP_WUNLOCK(inp); 26368f002c6cSBruce M Simpson return (error); 26378f002c6cSBruce M Simpson } 26388f002c6cSBruce M Simpson 26398f002c6cSBruce M Simpson /* 26408f002c6cSBruce M Simpson * Set the IP multicast options in response to user setsockopt(). 26418f002c6cSBruce M Simpson * 26428f002c6cSBruce M Simpson * Many of the socket options handled in this function duplicate the 26438f002c6cSBruce M Simpson * functionality of socket options in the regular unicast API. However, 26448f002c6cSBruce M Simpson * it is not possible to merge the duplicate code, because the idempotence 26458f002c6cSBruce M Simpson * of the IPv6 multicast part of the BSD Sockets API must be preserved; 26468f002c6cSBruce M Simpson * the effects of these options must be treated as separate and distinct. 26478f002c6cSBruce M Simpson * 26488f002c6cSBruce M Simpson * SMPng: XXX: Unlocked read of inp_socket believed OK. 26498f002c6cSBruce M Simpson */ 26508f002c6cSBruce M Simpson int 26518f002c6cSBruce M Simpson ip6_setmoptions(struct inpcb *inp, struct sockopt *sopt) 26528f002c6cSBruce M Simpson { 265333cde130SBruce M Simpson struct ip6_moptions *im6o; 26548f002c6cSBruce M Simpson int error; 26558f002c6cSBruce M Simpson 26568f002c6cSBruce M Simpson error = 0; 26578f002c6cSBruce M Simpson 26588f002c6cSBruce M Simpson /* 26598f002c6cSBruce M Simpson * If socket is neither of type SOCK_RAW or SOCK_DGRAM, 26608f002c6cSBruce M Simpson * or is a divert socket, reject it. 26618f002c6cSBruce M Simpson */ 26628f002c6cSBruce M Simpson if (inp->inp_socket->so_proto->pr_protocol == IPPROTO_DIVERT || 26638f002c6cSBruce M Simpson (inp->inp_socket->so_proto->pr_type != SOCK_RAW && 26648f002c6cSBruce M Simpson inp->inp_socket->so_proto->pr_type != SOCK_DGRAM)) 26658f002c6cSBruce M Simpson return (EOPNOTSUPP); 26668f002c6cSBruce M Simpson 26678f002c6cSBruce M Simpson switch (sopt->sopt_name) { 26688f002c6cSBruce M Simpson case IPV6_MULTICAST_IF: 26698f002c6cSBruce M Simpson error = in6p_set_multicast_if(inp, sopt); 26708f002c6cSBruce M Simpson break; 26718f002c6cSBruce M Simpson 26728f002c6cSBruce M Simpson case IPV6_MULTICAST_HOPS: { 26738f002c6cSBruce M Simpson int hlim; 26748f002c6cSBruce M Simpson 26758f002c6cSBruce M Simpson if (sopt->sopt_valsize != sizeof(int)) { 26768f002c6cSBruce M Simpson error = EINVAL; 26778f002c6cSBruce M Simpson break; 26788f002c6cSBruce M Simpson } 26798f002c6cSBruce M Simpson error = sooptcopyin(sopt, &hlim, sizeof(hlim), sizeof(int)); 26808f002c6cSBruce M Simpson if (error) 26818f002c6cSBruce M Simpson break; 26828f002c6cSBruce M Simpson if (hlim < -1 || hlim > 255) { 26838f002c6cSBruce M Simpson error = EINVAL; 26848f002c6cSBruce M Simpson break; 268533cde130SBruce M Simpson } else if (hlim == -1) { 268633cde130SBruce M Simpson hlim = V_ip6_defmcasthlim; 26878f002c6cSBruce M Simpson } 268833cde130SBruce M Simpson im6o = in6p_findmoptions(inp); 268933cde130SBruce M Simpson im6o->im6o_multicast_hlim = hlim; 26908f002c6cSBruce M Simpson INP_WUNLOCK(inp); 26918f002c6cSBruce M Simpson break; 26928f002c6cSBruce M Simpson } 26938f002c6cSBruce M Simpson 26948f002c6cSBruce M Simpson case IPV6_MULTICAST_LOOP: { 26958f002c6cSBruce M Simpson u_int loop; 26968f002c6cSBruce M Simpson 26978f002c6cSBruce M Simpson /* 26988f002c6cSBruce M Simpson * Set the loopback flag for outgoing multicast packets. 269933cde130SBruce M Simpson * Must be zero or one. 27008f002c6cSBruce M Simpson */ 27018f002c6cSBruce M Simpson if (sopt->sopt_valsize != sizeof(u_int)) { 27028f002c6cSBruce M Simpson error = EINVAL; 27038f002c6cSBruce M Simpson break; 27048f002c6cSBruce M Simpson } 27058f002c6cSBruce M Simpson error = sooptcopyin(sopt, &loop, sizeof(u_int), sizeof(u_int)); 27068f002c6cSBruce M Simpson if (error) 27078f002c6cSBruce M Simpson break; 270833cde130SBruce M Simpson if (loop > 1) { 270933cde130SBruce M Simpson error = EINVAL; 271033cde130SBruce M Simpson break; 271133cde130SBruce M Simpson } 271233cde130SBruce M Simpson im6o = in6p_findmoptions(inp); 271333cde130SBruce M Simpson im6o->im6o_multicast_loop = loop; 27148f002c6cSBruce M Simpson INP_WUNLOCK(inp); 27158f002c6cSBruce M Simpson break; 27168f002c6cSBruce M Simpson } 27178f002c6cSBruce M Simpson 27188f002c6cSBruce M Simpson case IPV6_JOIN_GROUP: 27198f002c6cSBruce M Simpson case MCAST_JOIN_GROUP: 27208f002c6cSBruce M Simpson case MCAST_JOIN_SOURCE_GROUP: 27218f002c6cSBruce M Simpson error = in6p_join_group(inp, sopt); 27228f002c6cSBruce M Simpson break; 27238f002c6cSBruce M Simpson 27248f002c6cSBruce M Simpson case IPV6_LEAVE_GROUP: 27258f002c6cSBruce M Simpson case MCAST_LEAVE_GROUP: 27268f002c6cSBruce M Simpson case MCAST_LEAVE_SOURCE_GROUP: 27278f002c6cSBruce M Simpson error = in6p_leave_group(inp, sopt); 27288f002c6cSBruce M Simpson break; 27298f002c6cSBruce M Simpson 27308f002c6cSBruce M Simpson case MCAST_BLOCK_SOURCE: 27318f002c6cSBruce M Simpson case MCAST_UNBLOCK_SOURCE: 27328f002c6cSBruce M Simpson error = in6p_block_unblock_source(inp, sopt); 27338f002c6cSBruce M Simpson break; 27348f002c6cSBruce M Simpson 27358f002c6cSBruce M Simpson case IPV6_MSFILTER: 27368f002c6cSBruce M Simpson error = in6p_set_source_filters(inp, sopt); 27378f002c6cSBruce M Simpson break; 27388f002c6cSBruce M Simpson 27398f002c6cSBruce M Simpson default: 27408f002c6cSBruce M Simpson error = EOPNOTSUPP; 27418f002c6cSBruce M Simpson break; 27428f002c6cSBruce M Simpson } 27438f002c6cSBruce M Simpson 27448f002c6cSBruce M Simpson INP_UNLOCK_ASSERT(inp); 27458f002c6cSBruce M Simpson 27468f002c6cSBruce M Simpson return (error); 27478f002c6cSBruce M Simpson } 27488f002c6cSBruce M Simpson 27498f002c6cSBruce M Simpson /* 27508f002c6cSBruce M Simpson * Expose MLD's multicast filter mode and source list(s) to userland, 27518f002c6cSBruce M Simpson * keyed by (ifindex, group). 27528f002c6cSBruce M Simpson * The filter mode is written out as a uint32_t, followed by 27538f002c6cSBruce M Simpson * 0..n of struct in6_addr. 27548f002c6cSBruce M Simpson * For use by ifmcstat(8). 27558f002c6cSBruce M Simpson * SMPng: NOTE: unlocked read of ifindex space. 27568f002c6cSBruce M Simpson */ 27578f002c6cSBruce M Simpson static int 27588f002c6cSBruce M Simpson sysctl_ip6_mcast_filters(SYSCTL_HANDLER_ARGS) 27598f002c6cSBruce M Simpson { 276029dc7bc6SBruce M Simpson struct in6_addr mcaddr; 27618f002c6cSBruce M Simpson struct in6_addr src; 2762a68cc388SGleb Smirnoff struct epoch_tracker et; 27638f002c6cSBruce M Simpson struct ifnet *ifp; 27648f002c6cSBruce M Simpson struct ifmultiaddr *ifma; 27658f002c6cSBruce M Simpson struct in6_multi *inm; 27668f002c6cSBruce M Simpson struct ip6_msource *ims; 27678f002c6cSBruce M Simpson int *name; 27688f002c6cSBruce M Simpson int retval; 27698f002c6cSBruce M Simpson u_int namelen; 27708f002c6cSBruce M Simpson uint32_t fmode, ifindex; 27718f002c6cSBruce M Simpson #ifdef KTR 27728f002c6cSBruce M Simpson char ip6tbuf[INET6_ADDRSTRLEN]; 27738f002c6cSBruce M Simpson #endif 27748f002c6cSBruce M Simpson 27758f002c6cSBruce M Simpson name = (int *)arg1; 27768f002c6cSBruce M Simpson namelen = arg2; 27778f002c6cSBruce M Simpson 27788f002c6cSBruce M Simpson if (req->newptr != NULL) 27798f002c6cSBruce M Simpson return (EPERM); 27808f002c6cSBruce M Simpson 27818f002c6cSBruce M Simpson /* int: ifindex + 4 * 32 bits of IPv6 address */ 27828f002c6cSBruce M Simpson if (namelen != 5) 27838f002c6cSBruce M Simpson return (EINVAL); 27848f002c6cSBruce M Simpson 278529dc7bc6SBruce M Simpson memcpy(&mcaddr, &name[1], sizeof(struct in6_addr)); 278629dc7bc6SBruce M Simpson if (!IN6_IS_ADDR_MULTICAST(&mcaddr)) { 27878f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: group %s is not multicast", 278829dc7bc6SBruce M Simpson __func__, ip6_sprintf(ip6tbuf, &mcaddr)); 27898f002c6cSBruce M Simpson return (EINVAL); 27908f002c6cSBruce M Simpson } 27918f002c6cSBruce M Simpson 2792*d74b7baeSGleb Smirnoff ifindex = name[0]; 2793b8a6e03fSGleb Smirnoff NET_EPOCH_ENTER(et); 27948f002c6cSBruce M Simpson ifp = ifnet_byindex(ifindex); 27958f002c6cSBruce M Simpson if (ifp == NULL) { 2796b8a6e03fSGleb Smirnoff NET_EPOCH_EXIT(et); 27978f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: no ifp for ifindex %u", 27988f002c6cSBruce M Simpson __func__, ifindex); 27998f002c6cSBruce M Simpson return (ENOENT); 28008f002c6cSBruce M Simpson } 280129dc7bc6SBruce M Simpson /* 280229dc7bc6SBruce M Simpson * Internal MLD lookups require that scope/zone ID is set. 280329dc7bc6SBruce M Simpson */ 280429dc7bc6SBruce M Simpson (void)in6_setscope(&mcaddr, ifp, NULL); 28058f002c6cSBruce M Simpson 28068f002c6cSBruce M Simpson retval = sysctl_wire_old_buffer(req, 28078f002c6cSBruce M Simpson sizeof(uint32_t) + (in6_mcast_maxgrpsrc * sizeof(struct in6_addr))); 2808b8a6e03fSGleb Smirnoff if (retval) { 2809b8a6e03fSGleb Smirnoff NET_EPOCH_EXIT(et); 28108f002c6cSBruce M Simpson return (retval); 2811b8a6e03fSGleb Smirnoff } 28128f002c6cSBruce M Simpson 28138f002c6cSBruce M Simpson IN6_MULTI_LOCK(); 2814f3e1324bSStephen Hurd IN6_MULTI_LIST_LOCK(); 2815d7c5a620SMatt Macy CK_STAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 28162cd6ad76SHans Petter Selasky inm = in6m_ifmultiaddr_get_inm(ifma); 28172cd6ad76SHans Petter Selasky if (inm == NULL) 28188f002c6cSBruce M Simpson continue; 281929dc7bc6SBruce M Simpson if (!IN6_ARE_ADDR_EQUAL(&inm->in6m_addr, &mcaddr)) 28208f002c6cSBruce M Simpson continue; 28218f002c6cSBruce M Simpson fmode = inm->in6m_st[1].iss_fmode; 28228f002c6cSBruce M Simpson retval = SYSCTL_OUT(req, &fmode, sizeof(uint32_t)); 28238f002c6cSBruce M Simpson if (retval != 0) 28248f002c6cSBruce M Simpson break; 28258f002c6cSBruce M Simpson RB_FOREACH(ims, ip6_msource_tree, &inm->in6m_srcs) { 28268f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: visit node %p", __func__, ims); 28278f002c6cSBruce M Simpson /* 28288f002c6cSBruce M Simpson * Only copy-out sources which are in-mode. 28298f002c6cSBruce M Simpson */ 28308f002c6cSBruce M Simpson if (fmode != im6s_get_mode(inm, ims, 1)) { 28318f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: skip non-in-mode", 28328f002c6cSBruce M Simpson __func__); 28338f002c6cSBruce M Simpson continue; 28348f002c6cSBruce M Simpson } 28358f002c6cSBruce M Simpson src = ims->im6s_addr; 28368f002c6cSBruce M Simpson retval = SYSCTL_OUT(req, &src, 28378f002c6cSBruce M Simpson sizeof(struct in6_addr)); 28388f002c6cSBruce M Simpson if (retval != 0) 28398f002c6cSBruce M Simpson break; 28408f002c6cSBruce M Simpson } 28418f002c6cSBruce M Simpson } 2842f3e1324bSStephen Hurd IN6_MULTI_LIST_UNLOCK(); 28438f002c6cSBruce M Simpson IN6_MULTI_UNLOCK(); 2844b8a6e03fSGleb Smirnoff NET_EPOCH_EXIT(et); 28458f002c6cSBruce M Simpson 28468f002c6cSBruce M Simpson return (retval); 28478f002c6cSBruce M Simpson } 28488f002c6cSBruce M Simpson 28498f002c6cSBruce M Simpson #ifdef KTR 28508f002c6cSBruce M Simpson 28518f002c6cSBruce M Simpson static const char *in6m_modestrs[] = { "un", "in", "ex" }; 28528f002c6cSBruce M Simpson 28538f002c6cSBruce M Simpson static const char * 28548f002c6cSBruce M Simpson in6m_mode_str(const int mode) 28558f002c6cSBruce M Simpson { 28568f002c6cSBruce M Simpson 28578f002c6cSBruce M Simpson if (mode >= MCAST_UNDEFINED && mode <= MCAST_EXCLUDE) 28588f002c6cSBruce M Simpson return (in6m_modestrs[mode]); 28598f002c6cSBruce M Simpson return ("??"); 28608f002c6cSBruce M Simpson } 28618f002c6cSBruce M Simpson 28628f002c6cSBruce M Simpson static const char *in6m_statestrs[] = { 28638f002c6cSBruce M Simpson "not-member", 28648f002c6cSBruce M Simpson "silent", 28658f002c6cSBruce M Simpson "idle", 28668f002c6cSBruce M Simpson "lazy", 28678f002c6cSBruce M Simpson "sleeping", 28688f002c6cSBruce M Simpson "awakening", 28698f002c6cSBruce M Simpson "query-pending", 28708f002c6cSBruce M Simpson "sg-query-pending", 28718f002c6cSBruce M Simpson "leaving" 28728f002c6cSBruce M Simpson }; 28738f002c6cSBruce M Simpson 28748f002c6cSBruce M Simpson static const char * 28758f002c6cSBruce M Simpson in6m_state_str(const int state) 28768f002c6cSBruce M Simpson { 28778f002c6cSBruce M Simpson 28788f002c6cSBruce M Simpson if (state >= MLD_NOT_MEMBER && state <= MLD_LEAVING_MEMBER) 28798f002c6cSBruce M Simpson return (in6m_statestrs[state]); 28808f002c6cSBruce M Simpson return ("??"); 28818f002c6cSBruce M Simpson } 28828f002c6cSBruce M Simpson 28838f002c6cSBruce M Simpson /* 28848f002c6cSBruce M Simpson * Dump an in6_multi structure to the console. 28858f002c6cSBruce M Simpson */ 28868f002c6cSBruce M Simpson void 28878f002c6cSBruce M Simpson in6m_print(const struct in6_multi *inm) 28888f002c6cSBruce M Simpson { 28898f002c6cSBruce M Simpson int t; 28908f002c6cSBruce M Simpson char ip6tbuf[INET6_ADDRSTRLEN]; 28918f002c6cSBruce M Simpson 28925b65b8bcSAlexander Kabaev if ((ktr_mask & KTR_MLD) == 0) 28938f002c6cSBruce M Simpson return; 28948f002c6cSBruce M Simpson 28958f002c6cSBruce M Simpson printf("%s: --- begin in6m %p ---\n", __func__, inm); 28968f002c6cSBruce M Simpson printf("addr %s ifp %p(%s) ifma %p\n", 28978f002c6cSBruce M Simpson ip6_sprintf(ip6tbuf, &inm->in6m_addr), 28988f002c6cSBruce M Simpson inm->in6m_ifp, 2899e74966f6SAndrey V. Elsukov if_name(inm->in6m_ifp), 29008f002c6cSBruce M Simpson inm->in6m_ifma); 29018f002c6cSBruce M Simpson printf("timer %u state %s refcount %u scq.len %u\n", 29028f002c6cSBruce M Simpson inm->in6m_timer, 29038f002c6cSBruce M Simpson in6m_state_str(inm->in6m_state), 29048f002c6cSBruce M Simpson inm->in6m_refcount, 2905a99c84d4SGleb Smirnoff mbufq_len(&inm->in6m_scq)); 29068f002c6cSBruce M Simpson printf("mli %p nsrc %lu sctimer %u scrv %u\n", 29078f002c6cSBruce M Simpson inm->in6m_mli, 29088f002c6cSBruce M Simpson inm->in6m_nsrc, 29098f002c6cSBruce M Simpson inm->in6m_sctimer, 29108f002c6cSBruce M Simpson inm->in6m_scrv); 29118f002c6cSBruce M Simpson for (t = 0; t < 2; t++) { 29128f002c6cSBruce M Simpson printf("t%d: fmode %s asm %u ex %u in %u rec %u\n", t, 29138f002c6cSBruce M Simpson in6m_mode_str(inm->in6m_st[t].iss_fmode), 29148f002c6cSBruce M Simpson inm->in6m_st[t].iss_asm, 29158f002c6cSBruce M Simpson inm->in6m_st[t].iss_ex, 29168f002c6cSBruce M Simpson inm->in6m_st[t].iss_in, 29178f002c6cSBruce M Simpson inm->in6m_st[t].iss_rec); 29188f002c6cSBruce M Simpson } 29198f002c6cSBruce M Simpson printf("%s: --- end in6m %p ---\n", __func__, inm); 29208f002c6cSBruce M Simpson } 29218f002c6cSBruce M Simpson 29228f002c6cSBruce M Simpson #else /* !KTR */ 29238f002c6cSBruce M Simpson 29248f002c6cSBruce M Simpson void 29258f002c6cSBruce M Simpson in6m_print(const struct in6_multi *inm) 29268f002c6cSBruce M Simpson { 29278f002c6cSBruce M Simpson 29288f002c6cSBruce M Simpson } 29298f002c6cSBruce M Simpson 29308f002c6cSBruce M Simpson #endif /* KTR */ 2931