18f002c6cSBruce M Simpson /* 28f002c6cSBruce M Simpson * Copyright (c) 2009 Bruce Simpson. 38f002c6cSBruce M Simpson * All rights reserved. 48f002c6cSBruce M Simpson * 58f002c6cSBruce M Simpson * Redistribution and use in source and binary forms, with or without 68f002c6cSBruce M Simpson * modification, are permitted provided that the following conditions 78f002c6cSBruce M Simpson * are met: 88f002c6cSBruce M Simpson * 1. Redistributions of source code must retain the above copyright 98f002c6cSBruce M Simpson * notice, this list of conditions and the following disclaimer. 108f002c6cSBruce M Simpson * 2. Redistributions in binary form must reproduce the above copyright 118f002c6cSBruce M Simpson * notice, this list of conditions and the following disclaimer in the 128f002c6cSBruce M Simpson * documentation and/or other materials provided with the distribution. 138f002c6cSBruce M Simpson * 3. The name of the author may not be used to endorse or promote 148f002c6cSBruce M Simpson * products derived from this software without specific prior written 158f002c6cSBruce M Simpson * permission. 168f002c6cSBruce M Simpson * 178f002c6cSBruce M Simpson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 188f002c6cSBruce M Simpson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 198f002c6cSBruce M Simpson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 208f002c6cSBruce M Simpson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 218f002c6cSBruce M Simpson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 228f002c6cSBruce M Simpson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 238f002c6cSBruce M Simpson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 248f002c6cSBruce M Simpson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 258f002c6cSBruce M Simpson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 268f002c6cSBruce M Simpson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 278f002c6cSBruce M Simpson * SUCH DAMAGE. 288f002c6cSBruce M Simpson */ 298f002c6cSBruce M Simpson 308f002c6cSBruce M Simpson /* 318f002c6cSBruce M Simpson * IPv6 multicast socket, group, and socket option processing module. 3233cde130SBruce M Simpson * Normative references: RFC 2292, RFC 3492, RFC 3542, RFC 3678, RFC 3810. 338f002c6cSBruce M Simpson */ 348f002c6cSBruce M Simpson 358f002c6cSBruce M Simpson #include <sys/cdefs.h> 368f002c6cSBruce M Simpson __FBSDID("$FreeBSD$"); 378f002c6cSBruce M Simpson 388f002c6cSBruce M Simpson #include "opt_inet6.h" 398f002c6cSBruce M Simpson 408f002c6cSBruce M Simpson #include <sys/param.h> 418f002c6cSBruce M Simpson #include <sys/systm.h> 428f002c6cSBruce M Simpson #include <sys/kernel.h> 438f002c6cSBruce M Simpson #include <sys/malloc.h> 448f002c6cSBruce M Simpson #include <sys/mbuf.h> 458f002c6cSBruce M Simpson #include <sys/protosw.h> 468f002c6cSBruce M Simpson #include <sys/socket.h> 478f002c6cSBruce M Simpson #include <sys/socketvar.h> 488f002c6cSBruce M Simpson #include <sys/protosw.h> 498f002c6cSBruce M Simpson #include <sys/sysctl.h> 508f002c6cSBruce M Simpson #include <sys/priv.h> 518f002c6cSBruce M Simpson #include <sys/ktr.h> 528f002c6cSBruce M Simpson #include <sys/tree.h> 538f002c6cSBruce M Simpson 548f002c6cSBruce M Simpson #include <net/if.h> 558f002c6cSBruce M Simpson #include <net/if_dl.h> 568f002c6cSBruce M Simpson #include <net/route.h> 578f002c6cSBruce M Simpson #include <net/vnet.h> 588f002c6cSBruce M Simpson 598f002c6cSBruce M Simpson #include <netinet/in.h> 608f002c6cSBruce M Simpson #include <netinet/in_var.h> 618f002c6cSBruce M Simpson #include <netinet6/in6_var.h> 628f002c6cSBruce M Simpson #include <netinet/ip6.h> 638f002c6cSBruce M Simpson #include <netinet/icmp6.h> 648f002c6cSBruce M Simpson #include <netinet6/ip6_var.h> 658f002c6cSBruce M Simpson #include <netinet/in_pcb.h> 668f002c6cSBruce M Simpson #include <netinet/tcp_var.h> 678f002c6cSBruce M Simpson #include <netinet6/nd6.h> 688f002c6cSBruce M Simpson #include <netinet6/mld6_var.h> 69eddfbb76SRobert Watson #include <netinet6/scope6_var.h> 708f002c6cSBruce M Simpson 718f002c6cSBruce M Simpson #ifndef KTR_MLD 728f002c6cSBruce M Simpson #define KTR_MLD KTR_INET6 738f002c6cSBruce M Simpson #endif 748f002c6cSBruce M Simpson 758f002c6cSBruce M Simpson #ifndef __SOCKUNION_DECLARED 768f002c6cSBruce M Simpson union sockunion { 778f002c6cSBruce M Simpson struct sockaddr_storage ss; 788f002c6cSBruce M Simpson struct sockaddr sa; 798f002c6cSBruce M Simpson struct sockaddr_dl sdl; 808f002c6cSBruce M Simpson struct sockaddr_in6 sin6; 818f002c6cSBruce M Simpson }; 828f002c6cSBruce M Simpson typedef union sockunion sockunion_t; 838f002c6cSBruce M Simpson #define __SOCKUNION_DECLARED 848f002c6cSBruce M Simpson #endif /* __SOCKUNION_DECLARED */ 858f002c6cSBruce M Simpson 868f002c6cSBruce M Simpson static MALLOC_DEFINE(M_IN6MFILTER, "in6_mfilter", 878f002c6cSBruce M Simpson "IPv6 multicast PCB-layer source filter"); 888f002c6cSBruce M Simpson static MALLOC_DEFINE(M_IP6MADDR, "in6_multi", "IPv6 multicast group"); 898f002c6cSBruce M Simpson static MALLOC_DEFINE(M_IP6MOPTS, "ip6_moptions", "IPv6 multicast options"); 908f002c6cSBruce M Simpson static MALLOC_DEFINE(M_IP6MSOURCE, "ip6_msource", 918f002c6cSBruce M Simpson "IPv6 multicast MLD-layer source filter"); 928f002c6cSBruce M Simpson 938f002c6cSBruce M Simpson RB_GENERATE(ip6_msource_tree, ip6_msource, im6s_link, ip6_msource_cmp); 948f002c6cSBruce M Simpson 958f002c6cSBruce M Simpson /* 968f002c6cSBruce M Simpson * Locking: 978f002c6cSBruce M Simpson * - Lock order is: Giant, INP_WLOCK, IN6_MULTI_LOCK, MLD_LOCK, IF_ADDR_LOCK. 988f002c6cSBruce M Simpson * - The IF_ADDR_LOCK is implicitly taken by in6m_lookup() earlier, however 998f002c6cSBruce M Simpson * it can be taken by code in net/if.c also. 1008f002c6cSBruce M Simpson * - ip6_moptions and in6_mfilter are covered by the INP_WLOCK. 1018f002c6cSBruce M Simpson * 1028f002c6cSBruce M Simpson * struct in6_multi is covered by IN6_MULTI_LOCK. There isn't strictly 1038f002c6cSBruce M Simpson * any need for in6_multi itself to be virtualized -- it is bound to an ifp 1048f002c6cSBruce M Simpson * anyway no matter what happens. 1058f002c6cSBruce M Simpson */ 1068f002c6cSBruce M Simpson struct mtx in6_multi_mtx; 1078f002c6cSBruce M Simpson MTX_SYSINIT(in6_multi_mtx, &in6_multi_mtx, "in6_multi_mtx", MTX_DEF); 1088f002c6cSBruce M Simpson 1098f002c6cSBruce M Simpson static void im6f_commit(struct in6_mfilter *); 1108f002c6cSBruce M Simpson static int im6f_get_source(struct in6_mfilter *imf, 1118f002c6cSBruce M Simpson const struct sockaddr_in6 *psin, 1128f002c6cSBruce M Simpson struct in6_msource **); 1138f002c6cSBruce M Simpson static struct in6_msource * 1148f002c6cSBruce M Simpson im6f_graft(struct in6_mfilter *, const uint8_t, 1158f002c6cSBruce M Simpson const struct sockaddr_in6 *); 1168f002c6cSBruce M Simpson static void im6f_leave(struct in6_mfilter *); 1178f002c6cSBruce M Simpson static int im6f_prune(struct in6_mfilter *, const struct sockaddr_in6 *); 1188f002c6cSBruce M Simpson static void im6f_purge(struct in6_mfilter *); 1198f002c6cSBruce M Simpson static void im6f_rollback(struct in6_mfilter *); 1208f002c6cSBruce M Simpson static void im6f_reap(struct in6_mfilter *); 1218f002c6cSBruce M Simpson static int im6o_grow(struct ip6_moptions *); 1228f002c6cSBruce M Simpson static size_t im6o_match_group(const struct ip6_moptions *, 1238f002c6cSBruce M Simpson const struct ifnet *, const struct sockaddr *); 1248f002c6cSBruce M Simpson static struct in6_msource * 1258f002c6cSBruce M Simpson im6o_match_source(const struct ip6_moptions *, const size_t, 1268f002c6cSBruce M Simpson const struct sockaddr *); 1278f002c6cSBruce M Simpson static void im6s_merge(struct ip6_msource *ims, 1288f002c6cSBruce M Simpson const struct in6_msource *lims, const int rollback); 1298f002c6cSBruce M Simpson static int in6_mc_get(struct ifnet *, const struct in6_addr *, 1308f002c6cSBruce M Simpson struct in6_multi **); 1318f002c6cSBruce M Simpson static int in6m_get_source(struct in6_multi *inm, 1328f002c6cSBruce M Simpson const struct in6_addr *addr, const int noalloc, 1338f002c6cSBruce M Simpson struct ip6_msource **pims); 1348f002c6cSBruce M Simpson static int in6m_is_ifp_detached(const struct in6_multi *); 1358f002c6cSBruce M Simpson static int in6m_merge(struct in6_multi *, /*const*/ struct in6_mfilter *); 1368f002c6cSBruce M Simpson static void in6m_purge(struct in6_multi *); 1378f002c6cSBruce M Simpson static void in6m_reap(struct in6_multi *); 1388f002c6cSBruce M Simpson static struct ip6_moptions * 1398f002c6cSBruce M Simpson in6p_findmoptions(struct inpcb *); 1408f002c6cSBruce M Simpson static int in6p_get_source_filters(struct inpcb *, struct sockopt *); 1418f002c6cSBruce M Simpson static int in6p_join_group(struct inpcb *, struct sockopt *); 1428f002c6cSBruce M Simpson static int in6p_leave_group(struct inpcb *, struct sockopt *); 14333cde130SBruce M Simpson static struct ifnet * 14433cde130SBruce M Simpson in6p_lookup_mcast_ifp(const struct inpcb *, 14533cde130SBruce M Simpson const struct sockaddr_in6 *); 1468f002c6cSBruce M Simpson static int in6p_block_unblock_source(struct inpcb *, struct sockopt *); 1478f002c6cSBruce M Simpson static int in6p_set_multicast_if(struct inpcb *, struct sockopt *); 1488f002c6cSBruce M Simpson static int in6p_set_source_filters(struct inpcb *, struct sockopt *); 1498f002c6cSBruce M Simpson static int sysctl_ip6_mcast_filters(SYSCTL_HANDLER_ARGS); 1508f002c6cSBruce M Simpson 1518f002c6cSBruce M Simpson SYSCTL_DECL(_net_inet6_ip6); /* XXX Not in any common header. */ 1528f002c6cSBruce M Simpson 1536472ac3dSEd Schouten static SYSCTL_NODE(_net_inet6_ip6, OID_AUTO, mcast, CTLFLAG_RW, 0, 1546472ac3dSEd Schouten "IPv6 multicast"); 1558f002c6cSBruce M Simpson 1568f002c6cSBruce M Simpson static u_long in6_mcast_maxgrpsrc = IPV6_MAX_GROUP_SRC_FILTER; 1578f002c6cSBruce M Simpson SYSCTL_ULONG(_net_inet6_ip6_mcast, OID_AUTO, maxgrpsrc, 1588f002c6cSBruce M Simpson CTLFLAG_RW | CTLFLAG_TUN, &in6_mcast_maxgrpsrc, 0, 1598f002c6cSBruce M Simpson "Max source filters per group"); 1608f002c6cSBruce M Simpson TUNABLE_ULONG("net.inet6.ip6.mcast.maxgrpsrc", &in6_mcast_maxgrpsrc); 1618f002c6cSBruce M Simpson 1628f002c6cSBruce M Simpson static u_long in6_mcast_maxsocksrc = IPV6_MAX_SOCK_SRC_FILTER; 1638f002c6cSBruce M Simpson SYSCTL_ULONG(_net_inet6_ip6_mcast, OID_AUTO, maxsocksrc, 1648f002c6cSBruce M Simpson CTLFLAG_RW | CTLFLAG_TUN, &in6_mcast_maxsocksrc, 0, 1658f002c6cSBruce M Simpson "Max source filters per socket"); 1668f002c6cSBruce M Simpson TUNABLE_ULONG("net.inet6.ip6.mcast.maxsocksrc", &in6_mcast_maxsocksrc); 1678f002c6cSBruce M Simpson 1688f002c6cSBruce M Simpson /* TODO Virtualize this switch. */ 1698f002c6cSBruce M Simpson int in6_mcast_loop = IPV6_DEFAULT_MULTICAST_LOOP; 1708f002c6cSBruce M Simpson SYSCTL_INT(_net_inet6_ip6_mcast, OID_AUTO, loop, CTLFLAG_RW | CTLFLAG_TUN, 1718f002c6cSBruce M Simpson &in6_mcast_loop, 0, "Loopback multicast datagrams by default"); 1728f002c6cSBruce M Simpson TUNABLE_INT("net.inet6.ip6.mcast.loop", &in6_mcast_loop); 1738f002c6cSBruce M Simpson 1746472ac3dSEd Schouten static SYSCTL_NODE(_net_inet6_ip6_mcast, OID_AUTO, filters, 1758f002c6cSBruce M Simpson CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_ip6_mcast_filters, 1768f002c6cSBruce M Simpson "Per-interface stack-wide source filters"); 1778f002c6cSBruce M Simpson 1788f002c6cSBruce M Simpson /* 1798f002c6cSBruce M Simpson * Inline function which wraps assertions for a valid ifp. 1808f002c6cSBruce M Simpson * The ifnet layer will set the ifma's ifp pointer to NULL if the ifp 1818f002c6cSBruce M Simpson * is detached. 1828f002c6cSBruce M Simpson */ 1838f002c6cSBruce M Simpson static int __inline 1848f002c6cSBruce M Simpson in6m_is_ifp_detached(const struct in6_multi *inm) 1858f002c6cSBruce M Simpson { 1868f002c6cSBruce M Simpson struct ifnet *ifp; 1878f002c6cSBruce M Simpson 1888f002c6cSBruce M Simpson KASSERT(inm->in6m_ifma != NULL, ("%s: no ifma", __func__)); 1898f002c6cSBruce M Simpson ifp = inm->in6m_ifma->ifma_ifp; 1908f002c6cSBruce M Simpson if (ifp != NULL) { 1918f002c6cSBruce M Simpson /* 1928f002c6cSBruce M Simpson * Sanity check that network-layer notion of ifp is the 1938f002c6cSBruce M Simpson * same as that of link-layer. 1948f002c6cSBruce M Simpson */ 1958f002c6cSBruce M Simpson KASSERT(inm->in6m_ifp == ifp, ("%s: bad ifp", __func__)); 1968f002c6cSBruce M Simpson } 1978f002c6cSBruce M Simpson 1988f002c6cSBruce M Simpson return (ifp == NULL); 1998f002c6cSBruce M Simpson } 2008f002c6cSBruce M Simpson 2018f002c6cSBruce M Simpson /* 2028f002c6cSBruce M Simpson * Initialize an in6_mfilter structure to a known state at t0, t1 2038f002c6cSBruce M Simpson * with an empty source filter list. 2048f002c6cSBruce M Simpson */ 2058f002c6cSBruce M Simpson static __inline void 2068f002c6cSBruce M Simpson im6f_init(struct in6_mfilter *imf, const int st0, const int st1) 2078f002c6cSBruce M Simpson { 2088f002c6cSBruce M Simpson memset(imf, 0, sizeof(struct in6_mfilter)); 2098f002c6cSBruce M Simpson RB_INIT(&imf->im6f_sources); 2108f002c6cSBruce M Simpson imf->im6f_st[0] = st0; 2118f002c6cSBruce M Simpson imf->im6f_st[1] = st1; 2128f002c6cSBruce M Simpson } 2138f002c6cSBruce M Simpson 2148f002c6cSBruce M Simpson /* 2158f002c6cSBruce M Simpson * Resize the ip6_moptions vector to the next power-of-two minus 1. 2168f002c6cSBruce M Simpson * May be called with locks held; do not sleep. 2178f002c6cSBruce M Simpson */ 2188f002c6cSBruce M Simpson static int 2198f002c6cSBruce M Simpson im6o_grow(struct ip6_moptions *imo) 2208f002c6cSBruce M Simpson { 2218f002c6cSBruce M Simpson struct in6_multi **nmships; 2228f002c6cSBruce M Simpson struct in6_multi **omships; 2238f002c6cSBruce M Simpson struct in6_mfilter *nmfilters; 2248f002c6cSBruce M Simpson struct in6_mfilter *omfilters; 2258f002c6cSBruce M Simpson size_t idx; 2268f002c6cSBruce M Simpson size_t newmax; 2278f002c6cSBruce M Simpson size_t oldmax; 2288f002c6cSBruce M Simpson 2298f002c6cSBruce M Simpson nmships = NULL; 2308f002c6cSBruce M Simpson nmfilters = NULL; 2318f002c6cSBruce M Simpson omships = imo->im6o_membership; 2328f002c6cSBruce M Simpson omfilters = imo->im6o_mfilters; 2338f002c6cSBruce M Simpson oldmax = imo->im6o_max_memberships; 2348f002c6cSBruce M Simpson newmax = ((oldmax + 1) * 2) - 1; 2358f002c6cSBruce M Simpson 2368f002c6cSBruce M Simpson if (newmax <= IPV6_MAX_MEMBERSHIPS) { 2378f002c6cSBruce M Simpson nmships = (struct in6_multi **)realloc(omships, 2388f002c6cSBruce M Simpson sizeof(struct in6_multi *) * newmax, M_IP6MOPTS, M_NOWAIT); 2398f002c6cSBruce M Simpson nmfilters = (struct in6_mfilter *)realloc(omfilters, 2408f002c6cSBruce M Simpson sizeof(struct in6_mfilter) * newmax, M_IN6MFILTER, 2418f002c6cSBruce M Simpson M_NOWAIT); 2428f002c6cSBruce M Simpson if (nmships != NULL && nmfilters != NULL) { 2438f002c6cSBruce M Simpson /* Initialize newly allocated source filter heads. */ 2448f002c6cSBruce M Simpson for (idx = oldmax; idx < newmax; idx++) { 2458f002c6cSBruce M Simpson im6f_init(&nmfilters[idx], MCAST_UNDEFINED, 2468f002c6cSBruce M Simpson MCAST_EXCLUDE); 2478f002c6cSBruce M Simpson } 2488f002c6cSBruce M Simpson imo->im6o_max_memberships = newmax; 2498f002c6cSBruce M Simpson imo->im6o_membership = nmships; 2508f002c6cSBruce M Simpson imo->im6o_mfilters = nmfilters; 2518f002c6cSBruce M Simpson } 2528f002c6cSBruce M Simpson } 2538f002c6cSBruce M Simpson 2548f002c6cSBruce M Simpson if (nmships == NULL || nmfilters == NULL) { 2558f002c6cSBruce M Simpson if (nmships != NULL) 2568f002c6cSBruce M Simpson free(nmships, M_IP6MOPTS); 2578f002c6cSBruce M Simpson if (nmfilters != NULL) 2588f002c6cSBruce M Simpson free(nmfilters, M_IN6MFILTER); 2598f002c6cSBruce M Simpson return (ETOOMANYREFS); 2608f002c6cSBruce M Simpson } 2618f002c6cSBruce M Simpson 2628f002c6cSBruce M Simpson return (0); 2638f002c6cSBruce M Simpson } 2648f002c6cSBruce M Simpson 2658f002c6cSBruce M Simpson /* 2668f002c6cSBruce M Simpson * Find an IPv6 multicast group entry for this ip6_moptions instance 2678f002c6cSBruce M Simpson * which matches the specified group, and optionally an interface. 2688f002c6cSBruce M Simpson * Return its index into the array, or -1 if not found. 2698f002c6cSBruce M Simpson */ 2708f002c6cSBruce M Simpson static size_t 2718f002c6cSBruce M Simpson im6o_match_group(const struct ip6_moptions *imo, const struct ifnet *ifp, 2728f002c6cSBruce M Simpson const struct sockaddr *group) 2738f002c6cSBruce M Simpson { 2748f002c6cSBruce M Simpson const struct sockaddr_in6 *gsin6; 2758f002c6cSBruce M Simpson struct in6_multi **pinm; 2768f002c6cSBruce M Simpson int idx; 2778f002c6cSBruce M Simpson int nmships; 2788f002c6cSBruce M Simpson 2798f002c6cSBruce M Simpson gsin6 = (const struct sockaddr_in6 *)group; 2808f002c6cSBruce M Simpson 2818f002c6cSBruce M Simpson /* The im6o_membership array may be lazy allocated. */ 2828f002c6cSBruce M Simpson if (imo->im6o_membership == NULL || imo->im6o_num_memberships == 0) 2838f002c6cSBruce M Simpson return (-1); 2848f002c6cSBruce M Simpson 2858f002c6cSBruce M Simpson nmships = imo->im6o_num_memberships; 2868f002c6cSBruce M Simpson pinm = &imo->im6o_membership[0]; 2878f002c6cSBruce M Simpson for (idx = 0; idx < nmships; idx++, pinm++) { 2888f002c6cSBruce M Simpson if (*pinm == NULL) 2898f002c6cSBruce M Simpson continue; 2908f002c6cSBruce M Simpson if ((ifp == NULL || ((*pinm)->in6m_ifp == ifp)) && 2918f002c6cSBruce M Simpson IN6_ARE_ADDR_EQUAL(&(*pinm)->in6m_addr, 2928f002c6cSBruce M Simpson &gsin6->sin6_addr)) { 2938f002c6cSBruce M Simpson break; 2948f002c6cSBruce M Simpson } 2958f002c6cSBruce M Simpson } 2968f002c6cSBruce M Simpson if (idx >= nmships) 2978f002c6cSBruce M Simpson idx = -1; 2988f002c6cSBruce M Simpson 2998f002c6cSBruce M Simpson return (idx); 3008f002c6cSBruce M Simpson } 3018f002c6cSBruce M Simpson 3028f002c6cSBruce M Simpson /* 3038f002c6cSBruce M Simpson * Find an IPv6 multicast source entry for this imo which matches 3048f002c6cSBruce M Simpson * the given group index for this socket, and source address. 3058f002c6cSBruce M Simpson * 30629dc7bc6SBruce M Simpson * XXX TODO: The scope ID, if present in src, is stripped before 30729dc7bc6SBruce M Simpson * any comparison. We SHOULD enforce scope/zone checks where the source 30829dc7bc6SBruce M Simpson * filter entry has a link scope. 30929dc7bc6SBruce M Simpson * 3108f002c6cSBruce M Simpson * NOTE: This does not check if the entry is in-mode, merely if 3118f002c6cSBruce M Simpson * it exists, which may not be the desired behaviour. 3128f002c6cSBruce M Simpson */ 3138f002c6cSBruce M Simpson static struct in6_msource * 3148f002c6cSBruce M Simpson im6o_match_source(const struct ip6_moptions *imo, const size_t gidx, 3158f002c6cSBruce M Simpson const struct sockaddr *src) 3168f002c6cSBruce M Simpson { 3178f002c6cSBruce M Simpson struct ip6_msource find; 3188f002c6cSBruce M Simpson struct in6_mfilter *imf; 3198f002c6cSBruce M Simpson struct ip6_msource *ims; 3208f002c6cSBruce M Simpson const sockunion_t *psa; 3218f002c6cSBruce M Simpson 3228f002c6cSBruce M Simpson KASSERT(src->sa_family == AF_INET6, ("%s: !AF_INET6", __func__)); 3238f002c6cSBruce M Simpson KASSERT(gidx != -1 && gidx < imo->im6o_num_memberships, 3248f002c6cSBruce M Simpson ("%s: invalid index %d\n", __func__, (int)gidx)); 3258f002c6cSBruce M Simpson 3268f002c6cSBruce M Simpson /* The im6o_mfilters array may be lazy allocated. */ 3278f002c6cSBruce M Simpson if (imo->im6o_mfilters == NULL) 3288f002c6cSBruce M Simpson return (NULL); 3298f002c6cSBruce M Simpson imf = &imo->im6o_mfilters[gidx]; 3308f002c6cSBruce M Simpson 3318f002c6cSBruce M Simpson psa = (const sockunion_t *)src; 3328f002c6cSBruce M Simpson find.im6s_addr = psa->sin6.sin6_addr; 33329dc7bc6SBruce M Simpson in6_clearscope(&find.im6s_addr); /* XXX */ 3348f002c6cSBruce M Simpson ims = RB_FIND(ip6_msource_tree, &imf->im6f_sources, &find); 3358f002c6cSBruce M Simpson 3368f002c6cSBruce M Simpson return ((struct in6_msource *)ims); 3378f002c6cSBruce M Simpson } 3388f002c6cSBruce M Simpson 3398f002c6cSBruce M Simpson /* 3408f002c6cSBruce M Simpson * Perform filtering for multicast datagrams on a socket by group and source. 3418f002c6cSBruce M Simpson * 3428f002c6cSBruce M Simpson * Returns 0 if a datagram should be allowed through, or various error codes 3438f002c6cSBruce M Simpson * if the socket was not a member of the group, or the source was muted, etc. 3448f002c6cSBruce M Simpson */ 3458f002c6cSBruce M Simpson int 3468f002c6cSBruce M Simpson im6o_mc_filter(const struct ip6_moptions *imo, const struct ifnet *ifp, 3478f002c6cSBruce M Simpson const struct sockaddr *group, const struct sockaddr *src) 3488f002c6cSBruce M Simpson { 3498f002c6cSBruce M Simpson size_t gidx; 3508f002c6cSBruce M Simpson struct in6_msource *ims; 3518f002c6cSBruce M Simpson int mode; 3528f002c6cSBruce M Simpson 3538f002c6cSBruce M Simpson KASSERT(ifp != NULL, ("%s: null ifp", __func__)); 3548f002c6cSBruce M Simpson 3558f002c6cSBruce M Simpson gidx = im6o_match_group(imo, ifp, group); 3568f002c6cSBruce M Simpson if (gidx == -1) 3578f002c6cSBruce M Simpson return (MCAST_NOTGMEMBER); 3588f002c6cSBruce M Simpson 3598f002c6cSBruce M Simpson /* 3608f002c6cSBruce M Simpson * Check if the source was included in an (S,G) join. 3618f002c6cSBruce M Simpson * Allow reception on exclusive memberships by default, 3628f002c6cSBruce M Simpson * reject reception on inclusive memberships by default. 3638f002c6cSBruce M Simpson * Exclude source only if an in-mode exclude filter exists. 3648f002c6cSBruce M Simpson * Include source only if an in-mode include filter exists. 3658f002c6cSBruce M Simpson * NOTE: We are comparing group state here at MLD t1 (now) 3668f002c6cSBruce M Simpson * with socket-layer t0 (since last downcall). 3678f002c6cSBruce M Simpson */ 3688f002c6cSBruce M Simpson mode = imo->im6o_mfilters[gidx].im6f_st[1]; 3698f002c6cSBruce M Simpson ims = im6o_match_source(imo, gidx, src); 3708f002c6cSBruce M Simpson 3718f002c6cSBruce M Simpson if ((ims == NULL && mode == MCAST_INCLUDE) || 3728f002c6cSBruce M Simpson (ims != NULL && ims->im6sl_st[0] != mode)) 3738f002c6cSBruce M Simpson return (MCAST_NOTSMEMBER); 3748f002c6cSBruce M Simpson 3758f002c6cSBruce M Simpson return (MCAST_PASS); 3768f002c6cSBruce M Simpson } 3778f002c6cSBruce M Simpson 3788f002c6cSBruce M Simpson /* 3798f002c6cSBruce M Simpson * Find and return a reference to an in6_multi record for (ifp, group), 3808f002c6cSBruce M Simpson * and bump its reference count. 3818f002c6cSBruce M Simpson * If one does not exist, try to allocate it, and update link-layer multicast 3828f002c6cSBruce M Simpson * filters on ifp to listen for group. 3838f002c6cSBruce M Simpson * Assumes the IN6_MULTI lock is held across the call. 3848f002c6cSBruce M Simpson * Return 0 if successful, otherwise return an appropriate error code. 3858f002c6cSBruce M Simpson */ 3868f002c6cSBruce M Simpson static int 3878f002c6cSBruce M Simpson in6_mc_get(struct ifnet *ifp, const struct in6_addr *group, 3888f002c6cSBruce M Simpson struct in6_multi **pinm) 3898f002c6cSBruce M Simpson { 3908f002c6cSBruce M Simpson struct sockaddr_in6 gsin6; 3918f002c6cSBruce M Simpson struct ifmultiaddr *ifma; 3928f002c6cSBruce M Simpson struct in6_multi *inm; 3938f002c6cSBruce M Simpson int error; 3948f002c6cSBruce M Simpson 3958f002c6cSBruce M Simpson error = 0; 3968f002c6cSBruce M Simpson 3978f002c6cSBruce M Simpson /* 3988f002c6cSBruce M Simpson * XXX: Accesses to ifma_protospec must be covered by IF_ADDR_LOCK; 3998f002c6cSBruce M Simpson * if_addmulti() takes this mutex itself, so we must drop and 4008f002c6cSBruce M Simpson * re-acquire around the call. 4018f002c6cSBruce M Simpson */ 4028f002c6cSBruce M Simpson IN6_MULTI_LOCK_ASSERT(); 4038f002c6cSBruce M Simpson IF_ADDR_LOCK(ifp); 4048f002c6cSBruce M Simpson 4058f002c6cSBruce M Simpson inm = in6m_lookup_locked(ifp, group); 4068f002c6cSBruce M Simpson if (inm != NULL) { 4078f002c6cSBruce M Simpson /* 4088f002c6cSBruce M Simpson * If we already joined this group, just bump the 4098f002c6cSBruce M Simpson * refcount and return it. 4108f002c6cSBruce M Simpson */ 4118f002c6cSBruce M Simpson KASSERT(inm->in6m_refcount >= 1, 4128f002c6cSBruce M Simpson ("%s: bad refcount %d", __func__, inm->in6m_refcount)); 4138f002c6cSBruce M Simpson ++inm->in6m_refcount; 4148f002c6cSBruce M Simpson *pinm = inm; 4158f002c6cSBruce M Simpson goto out_locked; 4168f002c6cSBruce M Simpson } 4178f002c6cSBruce M Simpson 4188f002c6cSBruce M Simpson memset(&gsin6, 0, sizeof(gsin6)); 4198f002c6cSBruce M Simpson gsin6.sin6_family = AF_INET6; 4208f002c6cSBruce M Simpson gsin6.sin6_len = sizeof(struct sockaddr_in6); 4218f002c6cSBruce M Simpson gsin6.sin6_addr = *group; 4228f002c6cSBruce M Simpson 4238f002c6cSBruce M Simpson /* 4248f002c6cSBruce M Simpson * Check if a link-layer group is already associated 4258f002c6cSBruce M Simpson * with this network-layer group on the given ifnet. 4268f002c6cSBruce M Simpson */ 4278f002c6cSBruce M Simpson IF_ADDR_UNLOCK(ifp); 4288f002c6cSBruce M Simpson error = if_addmulti(ifp, (struct sockaddr *)&gsin6, &ifma); 4298f002c6cSBruce M Simpson if (error != 0) 4308f002c6cSBruce M Simpson return (error); 4318f002c6cSBruce M Simpson IF_ADDR_LOCK(ifp); 4328f002c6cSBruce M Simpson 4338f002c6cSBruce M Simpson /* 4348f002c6cSBruce M Simpson * If something other than netinet6 is occupying the link-layer 4358f002c6cSBruce M Simpson * group, print a meaningful error message and back out of 4368f002c6cSBruce M Simpson * the allocation. 4378f002c6cSBruce M Simpson * Otherwise, bump the refcount on the existing network-layer 4388f002c6cSBruce M Simpson * group association and return it. 4398f002c6cSBruce M Simpson */ 4408f002c6cSBruce M Simpson if (ifma->ifma_protospec != NULL) { 4418f002c6cSBruce M Simpson inm = (struct in6_multi *)ifma->ifma_protospec; 4428f002c6cSBruce M Simpson #ifdef INVARIANTS 4438f002c6cSBruce M Simpson KASSERT(ifma->ifma_addr != NULL, ("%s: no ifma_addr", 4448f002c6cSBruce M Simpson __func__)); 4458f002c6cSBruce M Simpson KASSERT(ifma->ifma_addr->sa_family == AF_INET6, 4468f002c6cSBruce M Simpson ("%s: ifma not AF_INET6", __func__)); 4478f002c6cSBruce M Simpson KASSERT(inm != NULL, ("%s: no ifma_protospec", __func__)); 4488f002c6cSBruce M Simpson if (inm->in6m_ifma != ifma || inm->in6m_ifp != ifp || 4498f002c6cSBruce M Simpson !IN6_ARE_ADDR_EQUAL(&inm->in6m_addr, group)) 4508f002c6cSBruce M Simpson panic("%s: ifma %p is inconsistent with %p (%p)", 4518f002c6cSBruce M Simpson __func__, ifma, inm, group); 4528f002c6cSBruce M Simpson #endif 4538f002c6cSBruce M Simpson ++inm->in6m_refcount; 4548f002c6cSBruce M Simpson *pinm = inm; 4558f002c6cSBruce M Simpson goto out_locked; 4568f002c6cSBruce M Simpson } 4578f002c6cSBruce M Simpson 4588f002c6cSBruce M Simpson IF_ADDR_LOCK_ASSERT(ifp); 4598f002c6cSBruce M Simpson 4608f002c6cSBruce M Simpson /* 4618f002c6cSBruce M Simpson * A new in6_multi record is needed; allocate and initialize it. 4628f002c6cSBruce M Simpson * We DO NOT perform an MLD join as the in6_ layer may need to 4638f002c6cSBruce M Simpson * push an initial source list down to MLD to support SSM. 4648f002c6cSBruce M Simpson * 4658f002c6cSBruce M Simpson * The initial source filter state is INCLUDE, {} as per the RFC. 4668f002c6cSBruce M Simpson * Pending state-changes per group are subject to a bounds check. 4678f002c6cSBruce M Simpson */ 4688f002c6cSBruce M Simpson inm = malloc(sizeof(*inm), M_IP6MADDR, M_NOWAIT | M_ZERO); 4698f002c6cSBruce M Simpson if (inm == NULL) { 4708f002c6cSBruce M Simpson if_delmulti_ifma(ifma); 4718f002c6cSBruce M Simpson error = ENOMEM; 4728f002c6cSBruce M Simpson goto out_locked; 4738f002c6cSBruce M Simpson } 4748f002c6cSBruce M Simpson inm->in6m_addr = *group; 4758f002c6cSBruce M Simpson inm->in6m_ifp = ifp; 4768f002c6cSBruce M Simpson inm->in6m_mli = MLD_IFINFO(ifp); 4778f002c6cSBruce M Simpson inm->in6m_ifma = ifma; 4788f002c6cSBruce M Simpson inm->in6m_refcount = 1; 4798f002c6cSBruce M Simpson inm->in6m_state = MLD_NOT_MEMBER; 4808f002c6cSBruce M Simpson IFQ_SET_MAXLEN(&inm->in6m_scq, MLD_MAX_STATE_CHANGES); 4818f002c6cSBruce M Simpson 4828f002c6cSBruce M Simpson inm->in6m_st[0].iss_fmode = MCAST_UNDEFINED; 4838f002c6cSBruce M Simpson inm->in6m_st[1].iss_fmode = MCAST_UNDEFINED; 4848f002c6cSBruce M Simpson RB_INIT(&inm->in6m_srcs); 4858f002c6cSBruce M Simpson 4868f002c6cSBruce M Simpson ifma->ifma_protospec = inm; 4878f002c6cSBruce M Simpson *pinm = inm; 4888f002c6cSBruce M Simpson 4898f002c6cSBruce M Simpson out_locked: 4908f002c6cSBruce M Simpson IF_ADDR_UNLOCK(ifp); 4918f002c6cSBruce M Simpson return (error); 4928f002c6cSBruce M Simpson } 4938f002c6cSBruce M Simpson 4948f002c6cSBruce M Simpson /* 4958f002c6cSBruce M Simpson * Drop a reference to an in6_multi record. 4968f002c6cSBruce M Simpson * 4978f002c6cSBruce M Simpson * If the refcount drops to 0, free the in6_multi record and 4988f002c6cSBruce M Simpson * delete the underlying link-layer membership. 4998f002c6cSBruce M Simpson */ 5008f002c6cSBruce M Simpson void 5018f002c6cSBruce M Simpson in6m_release_locked(struct in6_multi *inm) 5028f002c6cSBruce M Simpson { 5038f002c6cSBruce M Simpson struct ifmultiaddr *ifma; 5048f002c6cSBruce M Simpson 5058f002c6cSBruce M Simpson IN6_MULTI_LOCK_ASSERT(); 5068f002c6cSBruce M Simpson 5078f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: refcount is %d", __func__, inm->in6m_refcount); 5088f002c6cSBruce M Simpson 5098f002c6cSBruce M Simpson if (--inm->in6m_refcount > 0) { 5108f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: refcount is now %d", __func__, 5118f002c6cSBruce M Simpson inm->in6m_refcount); 5128f002c6cSBruce M Simpson return; 5138f002c6cSBruce M Simpson } 5148f002c6cSBruce M Simpson 5158f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: freeing inm %p", __func__, inm); 5168f002c6cSBruce M Simpson 5178f002c6cSBruce M Simpson ifma = inm->in6m_ifma; 5188f002c6cSBruce M Simpson 5198f002c6cSBruce M Simpson /* XXX this access is not covered by IF_ADDR_LOCK */ 5208f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: purging ifma %p", __func__, ifma); 5218f002c6cSBruce M Simpson KASSERT(ifma->ifma_protospec == inm, 5228f002c6cSBruce M Simpson ("%s: ifma_protospec != inm", __func__)); 5238f002c6cSBruce M Simpson ifma->ifma_protospec = NULL; 5248f002c6cSBruce M Simpson 5258f002c6cSBruce M Simpson in6m_purge(inm); 5268f002c6cSBruce M Simpson 5278f002c6cSBruce M Simpson free(inm, M_IP6MADDR); 5288f002c6cSBruce M Simpson 5298f002c6cSBruce M Simpson if_delmulti_ifma(ifma); 5308f002c6cSBruce M Simpson } 5318f002c6cSBruce M Simpson 5328f002c6cSBruce M Simpson /* 5338f002c6cSBruce M Simpson * Clear recorded source entries for a group. 5348f002c6cSBruce M Simpson * Used by the MLD code. Caller must hold the IN6_MULTI lock. 5358f002c6cSBruce M Simpson * FIXME: Should reap. 5368f002c6cSBruce M Simpson */ 5378f002c6cSBruce M Simpson void 5388f002c6cSBruce M Simpson in6m_clear_recorded(struct in6_multi *inm) 5398f002c6cSBruce M Simpson { 5408f002c6cSBruce M Simpson struct ip6_msource *ims; 5418f002c6cSBruce M Simpson 5428f002c6cSBruce M Simpson IN6_MULTI_LOCK_ASSERT(); 5438f002c6cSBruce M Simpson 5448f002c6cSBruce M Simpson RB_FOREACH(ims, ip6_msource_tree, &inm->in6m_srcs) { 5458f002c6cSBruce M Simpson if (ims->im6s_stp) { 5468f002c6cSBruce M Simpson ims->im6s_stp = 0; 5478f002c6cSBruce M Simpson --inm->in6m_st[1].iss_rec; 5488f002c6cSBruce M Simpson } 5498f002c6cSBruce M Simpson } 5508f002c6cSBruce M Simpson KASSERT(inm->in6m_st[1].iss_rec == 0, 5518f002c6cSBruce M Simpson ("%s: iss_rec %d not 0", __func__, inm->in6m_st[1].iss_rec)); 5528f002c6cSBruce M Simpson } 5538f002c6cSBruce M Simpson 5548f002c6cSBruce M Simpson /* 5558f002c6cSBruce M Simpson * Record a source as pending for a Source-Group MLDv2 query. 5568f002c6cSBruce M Simpson * This lives here as it modifies the shared tree. 5578f002c6cSBruce M Simpson * 5588f002c6cSBruce M Simpson * inm is the group descriptor. 5598f002c6cSBruce M Simpson * naddr is the address of the source to record in network-byte order. 5608f002c6cSBruce M Simpson * 5618f002c6cSBruce M Simpson * If the net.inet6.mld.sgalloc sysctl is non-zero, we will 5628f002c6cSBruce M Simpson * lazy-allocate a source node in response to an SG query. 5638f002c6cSBruce M Simpson * Otherwise, no allocation is performed. This saves some memory 5648f002c6cSBruce M Simpson * with the trade-off that the source will not be reported to the 5658f002c6cSBruce M Simpson * router if joined in the window between the query response and 5668f002c6cSBruce M Simpson * the group actually being joined on the local host. 5678f002c6cSBruce M Simpson * 5688f002c6cSBruce M Simpson * VIMAGE: XXX: Currently the mld_sgalloc feature has been removed. 5698f002c6cSBruce M Simpson * This turns off the allocation of a recorded source entry if 5708f002c6cSBruce M Simpson * the group has not been joined. 5718f002c6cSBruce M Simpson * 5728f002c6cSBruce M Simpson * Return 0 if the source didn't exist or was already marked as recorded. 5738f002c6cSBruce M Simpson * Return 1 if the source was marked as recorded by this function. 5748f002c6cSBruce M Simpson * Return <0 if any error occured (negated errno code). 5758f002c6cSBruce M Simpson */ 5768f002c6cSBruce M Simpson int 5778f002c6cSBruce M Simpson in6m_record_source(struct in6_multi *inm, const struct in6_addr *addr) 5788f002c6cSBruce M Simpson { 5798f002c6cSBruce M Simpson struct ip6_msource find; 5808f002c6cSBruce M Simpson struct ip6_msource *ims, *nims; 5818f002c6cSBruce M Simpson 5828f002c6cSBruce M Simpson IN6_MULTI_LOCK_ASSERT(); 5838f002c6cSBruce M Simpson 5848f002c6cSBruce M Simpson find.im6s_addr = *addr; 5858f002c6cSBruce M Simpson ims = RB_FIND(ip6_msource_tree, &inm->in6m_srcs, &find); 5868f002c6cSBruce M Simpson if (ims && ims->im6s_stp) 5878f002c6cSBruce M Simpson return (0); 5888f002c6cSBruce M Simpson if (ims == NULL) { 5898f002c6cSBruce M Simpson if (inm->in6m_nsrc == in6_mcast_maxgrpsrc) 5908f002c6cSBruce M Simpson return (-ENOSPC); 5918f002c6cSBruce M Simpson nims = malloc(sizeof(struct ip6_msource), M_IP6MSOURCE, 5928f002c6cSBruce M Simpson M_NOWAIT | M_ZERO); 5938f002c6cSBruce M Simpson if (nims == NULL) 5948f002c6cSBruce M Simpson return (-ENOMEM); 5958f002c6cSBruce M Simpson nims->im6s_addr = find.im6s_addr; 5968f002c6cSBruce M Simpson RB_INSERT(ip6_msource_tree, &inm->in6m_srcs, nims); 5978f002c6cSBruce M Simpson ++inm->in6m_nsrc; 5988f002c6cSBruce M Simpson ims = nims; 5998f002c6cSBruce M Simpson } 6008f002c6cSBruce M Simpson 6018f002c6cSBruce M Simpson /* 6028f002c6cSBruce M Simpson * Mark the source as recorded and update the recorded 6038f002c6cSBruce M Simpson * source count. 6048f002c6cSBruce M Simpson */ 6058f002c6cSBruce M Simpson ++ims->im6s_stp; 6068f002c6cSBruce M Simpson ++inm->in6m_st[1].iss_rec; 6078f002c6cSBruce M Simpson 6088f002c6cSBruce M Simpson return (1); 6098f002c6cSBruce M Simpson } 6108f002c6cSBruce M Simpson 6118f002c6cSBruce M Simpson /* 6128f002c6cSBruce M Simpson * Return a pointer to an in6_msource owned by an in6_mfilter, 6138f002c6cSBruce M Simpson * given its source address. 6148f002c6cSBruce M Simpson * Lazy-allocate if needed. If this is a new entry its filter state is 6158f002c6cSBruce M Simpson * undefined at t0. 6168f002c6cSBruce M Simpson * 6178f002c6cSBruce M Simpson * imf is the filter set being modified. 6188f002c6cSBruce M Simpson * addr is the source address. 6198f002c6cSBruce M Simpson * 6208f002c6cSBruce M Simpson * SMPng: May be called with locks held; malloc must not block. 6218f002c6cSBruce M Simpson */ 6228f002c6cSBruce M Simpson static int 6238f002c6cSBruce M Simpson im6f_get_source(struct in6_mfilter *imf, const struct sockaddr_in6 *psin, 6248f002c6cSBruce M Simpson struct in6_msource **plims) 6258f002c6cSBruce M Simpson { 6268f002c6cSBruce M Simpson struct ip6_msource find; 6278f002c6cSBruce M Simpson struct ip6_msource *ims, *nims; 6288f002c6cSBruce M Simpson struct in6_msource *lims; 6298f002c6cSBruce M Simpson int error; 6308f002c6cSBruce M Simpson 6318f002c6cSBruce M Simpson error = 0; 6328f002c6cSBruce M Simpson ims = NULL; 6338f002c6cSBruce M Simpson lims = NULL; 6348f002c6cSBruce M Simpson 6358f002c6cSBruce M Simpson find.im6s_addr = psin->sin6_addr; 6368f002c6cSBruce M Simpson ims = RB_FIND(ip6_msource_tree, &imf->im6f_sources, &find); 6378f002c6cSBruce M Simpson lims = (struct in6_msource *)ims; 6388f002c6cSBruce M Simpson if (lims == NULL) { 6398f002c6cSBruce M Simpson if (imf->im6f_nsrc == in6_mcast_maxsocksrc) 6408f002c6cSBruce M Simpson return (ENOSPC); 6418f002c6cSBruce M Simpson nims = malloc(sizeof(struct in6_msource), M_IN6MFILTER, 6428f002c6cSBruce M Simpson M_NOWAIT | M_ZERO); 6438f002c6cSBruce M Simpson if (nims == NULL) 6448f002c6cSBruce M Simpson return (ENOMEM); 6458f002c6cSBruce M Simpson lims = (struct in6_msource *)nims; 6468f002c6cSBruce M Simpson lims->im6s_addr = find.im6s_addr; 6478f002c6cSBruce M Simpson lims->im6sl_st[0] = MCAST_UNDEFINED; 6488f002c6cSBruce M Simpson RB_INSERT(ip6_msource_tree, &imf->im6f_sources, nims); 6498f002c6cSBruce M Simpson ++imf->im6f_nsrc; 6508f002c6cSBruce M Simpson } 6518f002c6cSBruce M Simpson 6528f002c6cSBruce M Simpson *plims = lims; 6538f002c6cSBruce M Simpson 6548f002c6cSBruce M Simpson return (error); 6558f002c6cSBruce M Simpson } 6568f002c6cSBruce M Simpson 6578f002c6cSBruce M Simpson /* 6588f002c6cSBruce M Simpson * Graft a source entry into an existing socket-layer filter set, 6598f002c6cSBruce M Simpson * maintaining any required invariants and checking allocations. 6608f002c6cSBruce M Simpson * 6618f002c6cSBruce M Simpson * The source is marked as being in the new filter mode at t1. 6628f002c6cSBruce M Simpson * 6638f002c6cSBruce M Simpson * Return the pointer to the new node, otherwise return NULL. 6648f002c6cSBruce M Simpson */ 6658f002c6cSBruce M Simpson static struct in6_msource * 6668f002c6cSBruce M Simpson im6f_graft(struct in6_mfilter *imf, const uint8_t st1, 6678f002c6cSBruce M Simpson const struct sockaddr_in6 *psin) 6688f002c6cSBruce M Simpson { 6698f002c6cSBruce M Simpson struct ip6_msource *nims; 6708f002c6cSBruce M Simpson struct in6_msource *lims; 6718f002c6cSBruce M Simpson 6728f002c6cSBruce M Simpson nims = malloc(sizeof(struct in6_msource), M_IN6MFILTER, 6738f002c6cSBruce M Simpson M_NOWAIT | M_ZERO); 6748f002c6cSBruce M Simpson if (nims == NULL) 6758f002c6cSBruce M Simpson return (NULL); 6768f002c6cSBruce M Simpson lims = (struct in6_msource *)nims; 6778f002c6cSBruce M Simpson lims->im6s_addr = psin->sin6_addr; 6788f002c6cSBruce M Simpson lims->im6sl_st[0] = MCAST_UNDEFINED; 6798f002c6cSBruce M Simpson lims->im6sl_st[1] = st1; 6808f002c6cSBruce M Simpson RB_INSERT(ip6_msource_tree, &imf->im6f_sources, nims); 6818f002c6cSBruce M Simpson ++imf->im6f_nsrc; 6828f002c6cSBruce M Simpson 6838f002c6cSBruce M Simpson return (lims); 6848f002c6cSBruce M Simpson } 6858f002c6cSBruce M Simpson 6868f002c6cSBruce M Simpson /* 6878f002c6cSBruce M Simpson * Prune a source entry from an existing socket-layer filter set, 6888f002c6cSBruce M Simpson * maintaining any required invariants and checking allocations. 6898f002c6cSBruce M Simpson * 6908f002c6cSBruce M Simpson * The source is marked as being left at t1, it is not freed. 6918f002c6cSBruce M Simpson * 6928f002c6cSBruce M Simpson * Return 0 if no error occurred, otherwise return an errno value. 6938f002c6cSBruce M Simpson */ 6948f002c6cSBruce M Simpson static int 6958f002c6cSBruce M Simpson im6f_prune(struct in6_mfilter *imf, const struct sockaddr_in6 *psin) 6968f002c6cSBruce M Simpson { 6978f002c6cSBruce M Simpson struct ip6_msource find; 6988f002c6cSBruce M Simpson struct ip6_msource *ims; 6998f002c6cSBruce M Simpson struct in6_msource *lims; 7008f002c6cSBruce M Simpson 7018f002c6cSBruce M Simpson find.im6s_addr = psin->sin6_addr; 7028f002c6cSBruce M Simpson ims = RB_FIND(ip6_msource_tree, &imf->im6f_sources, &find); 7038f002c6cSBruce M Simpson if (ims == NULL) 7048f002c6cSBruce M Simpson return (ENOENT); 7058f002c6cSBruce M Simpson lims = (struct in6_msource *)ims; 7068f002c6cSBruce M Simpson lims->im6sl_st[1] = MCAST_UNDEFINED; 7078f002c6cSBruce M Simpson return (0); 7088f002c6cSBruce M Simpson } 7098f002c6cSBruce M Simpson 7108f002c6cSBruce M Simpson /* 7118f002c6cSBruce M Simpson * Revert socket-layer filter set deltas at t1 to t0 state. 7128f002c6cSBruce M Simpson */ 7138f002c6cSBruce M Simpson static void 7148f002c6cSBruce M Simpson im6f_rollback(struct in6_mfilter *imf) 7158f002c6cSBruce M Simpson { 7168f002c6cSBruce M Simpson struct ip6_msource *ims, *tims; 7178f002c6cSBruce M Simpson struct in6_msource *lims; 7188f002c6cSBruce M Simpson 7198f002c6cSBruce M Simpson RB_FOREACH_SAFE(ims, ip6_msource_tree, &imf->im6f_sources, tims) { 7208f002c6cSBruce M Simpson lims = (struct in6_msource *)ims; 7218f002c6cSBruce M Simpson if (lims->im6sl_st[0] == lims->im6sl_st[1]) { 7228f002c6cSBruce M Simpson /* no change at t1 */ 7238f002c6cSBruce M Simpson continue; 7248f002c6cSBruce M Simpson } else if (lims->im6sl_st[0] != MCAST_UNDEFINED) { 7258f002c6cSBruce M Simpson /* revert change to existing source at t1 */ 7268f002c6cSBruce M Simpson lims->im6sl_st[1] = lims->im6sl_st[0]; 7278f002c6cSBruce M Simpson } else { 7288f002c6cSBruce M Simpson /* revert source added t1 */ 7298f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: free ims %p", __func__, ims); 7308f002c6cSBruce M Simpson RB_REMOVE(ip6_msource_tree, &imf->im6f_sources, ims); 7318f002c6cSBruce M Simpson free(ims, M_IN6MFILTER); 7328f002c6cSBruce M Simpson imf->im6f_nsrc--; 7338f002c6cSBruce M Simpson } 7348f002c6cSBruce M Simpson } 7358f002c6cSBruce M Simpson imf->im6f_st[1] = imf->im6f_st[0]; 7368f002c6cSBruce M Simpson } 7378f002c6cSBruce M Simpson 7388f002c6cSBruce M Simpson /* 7398f002c6cSBruce M Simpson * Mark socket-layer filter set as INCLUDE {} at t1. 7408f002c6cSBruce M Simpson */ 7418f002c6cSBruce M Simpson static void 7428f002c6cSBruce M Simpson im6f_leave(struct in6_mfilter *imf) 7438f002c6cSBruce M Simpson { 7448f002c6cSBruce M Simpson struct ip6_msource *ims; 7458f002c6cSBruce M Simpson struct in6_msource *lims; 7468f002c6cSBruce M Simpson 7478f002c6cSBruce M Simpson RB_FOREACH(ims, ip6_msource_tree, &imf->im6f_sources) { 7488f002c6cSBruce M Simpson lims = (struct in6_msource *)ims; 7498f002c6cSBruce M Simpson lims->im6sl_st[1] = MCAST_UNDEFINED; 7508f002c6cSBruce M Simpson } 7518f002c6cSBruce M Simpson imf->im6f_st[1] = MCAST_INCLUDE; 7528f002c6cSBruce M Simpson } 7538f002c6cSBruce M Simpson 7548f002c6cSBruce M Simpson /* 7558f002c6cSBruce M Simpson * Mark socket-layer filter set deltas as committed. 7568f002c6cSBruce M Simpson */ 7578f002c6cSBruce M Simpson static void 7588f002c6cSBruce M Simpson im6f_commit(struct in6_mfilter *imf) 7598f002c6cSBruce M Simpson { 7608f002c6cSBruce M Simpson struct ip6_msource *ims; 7618f002c6cSBruce M Simpson struct in6_msource *lims; 7628f002c6cSBruce M Simpson 7638f002c6cSBruce M Simpson RB_FOREACH(ims, ip6_msource_tree, &imf->im6f_sources) { 7648f002c6cSBruce M Simpson lims = (struct in6_msource *)ims; 7658f002c6cSBruce M Simpson lims->im6sl_st[0] = lims->im6sl_st[1]; 7668f002c6cSBruce M Simpson } 7678f002c6cSBruce M Simpson imf->im6f_st[0] = imf->im6f_st[1]; 7688f002c6cSBruce M Simpson } 7698f002c6cSBruce M Simpson 7708f002c6cSBruce M Simpson /* 7718f002c6cSBruce M Simpson * Reap unreferenced sources from socket-layer filter set. 7728f002c6cSBruce M Simpson */ 7738f002c6cSBruce M Simpson static void 7748f002c6cSBruce M Simpson im6f_reap(struct in6_mfilter *imf) 7758f002c6cSBruce M Simpson { 7768f002c6cSBruce M Simpson struct ip6_msource *ims, *tims; 7778f002c6cSBruce M Simpson struct in6_msource *lims; 7788f002c6cSBruce M Simpson 7798f002c6cSBruce M Simpson RB_FOREACH_SAFE(ims, ip6_msource_tree, &imf->im6f_sources, tims) { 7808f002c6cSBruce M Simpson lims = (struct in6_msource *)ims; 7818f002c6cSBruce M Simpson if ((lims->im6sl_st[0] == MCAST_UNDEFINED) && 7828f002c6cSBruce M Simpson (lims->im6sl_st[1] == MCAST_UNDEFINED)) { 7838f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: free lims %p", __func__, ims); 7848f002c6cSBruce M Simpson RB_REMOVE(ip6_msource_tree, &imf->im6f_sources, ims); 7858f002c6cSBruce M Simpson free(ims, M_IN6MFILTER); 7868f002c6cSBruce M Simpson imf->im6f_nsrc--; 7878f002c6cSBruce M Simpson } 7888f002c6cSBruce M Simpson } 7898f002c6cSBruce M Simpson } 7908f002c6cSBruce M Simpson 7918f002c6cSBruce M Simpson /* 7928f002c6cSBruce M Simpson * Purge socket-layer filter set. 7938f002c6cSBruce M Simpson */ 7948f002c6cSBruce M Simpson static void 7958f002c6cSBruce M Simpson im6f_purge(struct in6_mfilter *imf) 7968f002c6cSBruce M Simpson { 7978f002c6cSBruce M Simpson struct ip6_msource *ims, *tims; 7988f002c6cSBruce M Simpson 7998f002c6cSBruce M Simpson RB_FOREACH_SAFE(ims, ip6_msource_tree, &imf->im6f_sources, tims) { 8008f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: free ims %p", __func__, ims); 8018f002c6cSBruce M Simpson RB_REMOVE(ip6_msource_tree, &imf->im6f_sources, ims); 8028f002c6cSBruce M Simpson free(ims, M_IN6MFILTER); 8038f002c6cSBruce M Simpson imf->im6f_nsrc--; 8048f002c6cSBruce M Simpson } 8058f002c6cSBruce M Simpson imf->im6f_st[0] = imf->im6f_st[1] = MCAST_UNDEFINED; 8068f002c6cSBruce M Simpson KASSERT(RB_EMPTY(&imf->im6f_sources), 8078f002c6cSBruce M Simpson ("%s: im6f_sources not empty", __func__)); 8088f002c6cSBruce M Simpson } 8098f002c6cSBruce M Simpson 8108f002c6cSBruce M Simpson /* 8118f002c6cSBruce M Simpson * Look up a source filter entry for a multicast group. 8128f002c6cSBruce M Simpson * 8138f002c6cSBruce M Simpson * inm is the group descriptor to work with. 8148f002c6cSBruce M Simpson * addr is the IPv6 address to look up. 8158f002c6cSBruce M Simpson * noalloc may be non-zero to suppress allocation of sources. 8168f002c6cSBruce M Simpson * *pims will be set to the address of the retrieved or allocated source. 8178f002c6cSBruce M Simpson * 8188f002c6cSBruce M Simpson * SMPng: NOTE: may be called with locks held. 8198f002c6cSBruce M Simpson * Return 0 if successful, otherwise return a non-zero error code. 8208f002c6cSBruce M Simpson */ 8218f002c6cSBruce M Simpson static int 8228f002c6cSBruce M Simpson in6m_get_source(struct in6_multi *inm, const struct in6_addr *addr, 8238f002c6cSBruce M Simpson const int noalloc, struct ip6_msource **pims) 8248f002c6cSBruce M Simpson { 8258f002c6cSBruce M Simpson struct ip6_msource find; 8268f002c6cSBruce M Simpson struct ip6_msource *ims, *nims; 8278f002c6cSBruce M Simpson #ifdef KTR 8288f002c6cSBruce M Simpson char ip6tbuf[INET6_ADDRSTRLEN]; 8298f002c6cSBruce M Simpson #endif 8308f002c6cSBruce M Simpson 8318f002c6cSBruce M Simpson find.im6s_addr = *addr; 8328f002c6cSBruce M Simpson ims = RB_FIND(ip6_msource_tree, &inm->in6m_srcs, &find); 8338f002c6cSBruce M Simpson if (ims == NULL && !noalloc) { 8348f002c6cSBruce M Simpson if (inm->in6m_nsrc == in6_mcast_maxgrpsrc) 8358f002c6cSBruce M Simpson return (ENOSPC); 8368f002c6cSBruce M Simpson nims = malloc(sizeof(struct ip6_msource), M_IP6MSOURCE, 8378f002c6cSBruce M Simpson M_NOWAIT | M_ZERO); 8388f002c6cSBruce M Simpson if (nims == NULL) 8398f002c6cSBruce M Simpson return (ENOMEM); 8408f002c6cSBruce M Simpson nims->im6s_addr = *addr; 8418f002c6cSBruce M Simpson RB_INSERT(ip6_msource_tree, &inm->in6m_srcs, nims); 8428f002c6cSBruce M Simpson ++inm->in6m_nsrc; 8438f002c6cSBruce M Simpson ims = nims; 8448f002c6cSBruce M Simpson CTR3(KTR_MLD, "%s: allocated %s as %p", __func__, 8458f002c6cSBruce M Simpson ip6_sprintf(ip6tbuf, addr), ims); 8468f002c6cSBruce M Simpson } 8478f002c6cSBruce M Simpson 8488f002c6cSBruce M Simpson *pims = ims; 8498f002c6cSBruce M Simpson return (0); 8508f002c6cSBruce M Simpson } 8518f002c6cSBruce M Simpson 8528f002c6cSBruce M Simpson /* 8538f002c6cSBruce M Simpson * Merge socket-layer source into MLD-layer source. 8548f002c6cSBruce M Simpson * If rollback is non-zero, perform the inverse of the merge. 8558f002c6cSBruce M Simpson */ 8568f002c6cSBruce M Simpson static void 8578f002c6cSBruce M Simpson im6s_merge(struct ip6_msource *ims, const struct in6_msource *lims, 8588f002c6cSBruce M Simpson const int rollback) 8598f002c6cSBruce M Simpson { 8608f002c6cSBruce M Simpson int n = rollback ? -1 : 1; 8618f002c6cSBruce M Simpson #ifdef KTR 8628f002c6cSBruce M Simpson char ip6tbuf[INET6_ADDRSTRLEN]; 8638f002c6cSBruce M Simpson 8648f002c6cSBruce M Simpson ip6_sprintf(ip6tbuf, &lims->im6s_addr); 8658f002c6cSBruce M Simpson #endif 8668f002c6cSBruce M Simpson 8678f002c6cSBruce M Simpson if (lims->im6sl_st[0] == MCAST_EXCLUDE) { 8688f002c6cSBruce M Simpson CTR3(KTR_MLD, "%s: t1 ex -= %d on %s", __func__, n, ip6tbuf); 8698f002c6cSBruce M Simpson ims->im6s_st[1].ex -= n; 8708f002c6cSBruce M Simpson } else if (lims->im6sl_st[0] == MCAST_INCLUDE) { 8718f002c6cSBruce M Simpson CTR3(KTR_MLD, "%s: t1 in -= %d on %s", __func__, n, ip6tbuf); 8728f002c6cSBruce M Simpson ims->im6s_st[1].in -= n; 8738f002c6cSBruce M Simpson } 8748f002c6cSBruce M Simpson 8758f002c6cSBruce M Simpson if (lims->im6sl_st[1] == MCAST_EXCLUDE) { 8768f002c6cSBruce M Simpson CTR3(KTR_MLD, "%s: t1 ex += %d on %s", __func__, n, ip6tbuf); 8778f002c6cSBruce M Simpson ims->im6s_st[1].ex += n; 8788f002c6cSBruce M Simpson } else if (lims->im6sl_st[1] == MCAST_INCLUDE) { 8798f002c6cSBruce M Simpson CTR3(KTR_MLD, "%s: t1 in += %d on %s", __func__, n, ip6tbuf); 8808f002c6cSBruce M Simpson ims->im6s_st[1].in += n; 8818f002c6cSBruce M Simpson } 8828f002c6cSBruce M Simpson } 8838f002c6cSBruce M Simpson 8848f002c6cSBruce M Simpson /* 8858f002c6cSBruce M Simpson * Atomically update the global in6_multi state, when a membership's 8868f002c6cSBruce M Simpson * filter list is being updated in any way. 8878f002c6cSBruce M Simpson * 8888f002c6cSBruce M Simpson * imf is the per-inpcb-membership group filter pointer. 8898f002c6cSBruce M Simpson * A fake imf may be passed for in-kernel consumers. 8908f002c6cSBruce M Simpson * 8918f002c6cSBruce M Simpson * XXX This is a candidate for a set-symmetric-difference style loop 8928f002c6cSBruce M Simpson * which would eliminate the repeated lookup from root of ims nodes, 8938f002c6cSBruce M Simpson * as they share the same key space. 8948f002c6cSBruce M Simpson * 8958f002c6cSBruce M Simpson * If any error occurred this function will back out of refcounts 8968f002c6cSBruce M Simpson * and return a non-zero value. 8978f002c6cSBruce M Simpson */ 8988f002c6cSBruce M Simpson static int 8998f002c6cSBruce M Simpson in6m_merge(struct in6_multi *inm, /*const*/ struct in6_mfilter *imf) 9008f002c6cSBruce M Simpson { 9018f002c6cSBruce M Simpson struct ip6_msource *ims, *nims; 9028f002c6cSBruce M Simpson struct in6_msource *lims; 9038f002c6cSBruce M Simpson int schanged, error; 9048f002c6cSBruce M Simpson int nsrc0, nsrc1; 9058f002c6cSBruce M Simpson 9068f002c6cSBruce M Simpson schanged = 0; 9078f002c6cSBruce M Simpson error = 0; 9088f002c6cSBruce M Simpson nsrc1 = nsrc0 = 0; 9098f002c6cSBruce M Simpson 9108f002c6cSBruce M Simpson /* 9118f002c6cSBruce M Simpson * Update the source filters first, as this may fail. 9128f002c6cSBruce M Simpson * Maintain count of in-mode filters at t0, t1. These are 9138f002c6cSBruce M Simpson * used to work out if we transition into ASM mode or not. 9148f002c6cSBruce M Simpson * Maintain a count of source filters whose state was 9158f002c6cSBruce M Simpson * actually modified by this operation. 9168f002c6cSBruce M Simpson */ 9178f002c6cSBruce M Simpson RB_FOREACH(ims, ip6_msource_tree, &imf->im6f_sources) { 9188f002c6cSBruce M Simpson lims = (struct in6_msource *)ims; 9198f002c6cSBruce M Simpson if (lims->im6sl_st[0] == imf->im6f_st[0]) nsrc0++; 9208f002c6cSBruce M Simpson if (lims->im6sl_st[1] == imf->im6f_st[1]) nsrc1++; 9218f002c6cSBruce M Simpson if (lims->im6sl_st[0] == lims->im6sl_st[1]) continue; 9228f002c6cSBruce M Simpson error = in6m_get_source(inm, &lims->im6s_addr, 0, &nims); 9238f002c6cSBruce M Simpson ++schanged; 9248f002c6cSBruce M Simpson if (error) 9258f002c6cSBruce M Simpson break; 9268f002c6cSBruce M Simpson im6s_merge(nims, lims, 0); 9278f002c6cSBruce M Simpson } 9288f002c6cSBruce M Simpson if (error) { 9298f002c6cSBruce M Simpson struct ip6_msource *bims; 9308f002c6cSBruce M Simpson 9318f002c6cSBruce M Simpson RB_FOREACH_REVERSE_FROM(ims, ip6_msource_tree, nims) { 9328f002c6cSBruce M Simpson lims = (struct in6_msource *)ims; 9338f002c6cSBruce M Simpson if (lims->im6sl_st[0] == lims->im6sl_st[1]) 9348f002c6cSBruce M Simpson continue; 9358f002c6cSBruce M Simpson (void)in6m_get_source(inm, &lims->im6s_addr, 1, &bims); 9368f002c6cSBruce M Simpson if (bims == NULL) 9378f002c6cSBruce M Simpson continue; 9388f002c6cSBruce M Simpson im6s_merge(bims, lims, 1); 9398f002c6cSBruce M Simpson } 9408f002c6cSBruce M Simpson goto out_reap; 9418f002c6cSBruce M Simpson } 9428f002c6cSBruce M Simpson 9438f002c6cSBruce M Simpson CTR3(KTR_MLD, "%s: imf filters in-mode: %d at t0, %d at t1", 9448f002c6cSBruce M Simpson __func__, nsrc0, nsrc1); 9458f002c6cSBruce M Simpson 9468f002c6cSBruce M Simpson /* Handle transition between INCLUDE {n} and INCLUDE {} on socket. */ 9478f002c6cSBruce M Simpson if (imf->im6f_st[0] == imf->im6f_st[1] && 9488f002c6cSBruce M Simpson imf->im6f_st[1] == MCAST_INCLUDE) { 9498f002c6cSBruce M Simpson if (nsrc1 == 0) { 9508f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: --in on inm at t1", __func__); 9518f002c6cSBruce M Simpson --inm->in6m_st[1].iss_in; 9528f002c6cSBruce M Simpson } 9538f002c6cSBruce M Simpson } 9548f002c6cSBruce M Simpson 9558f002c6cSBruce M Simpson /* Handle filter mode transition on socket. */ 9568f002c6cSBruce M Simpson if (imf->im6f_st[0] != imf->im6f_st[1]) { 9578f002c6cSBruce M Simpson CTR3(KTR_MLD, "%s: imf transition %d to %d", 9588f002c6cSBruce M Simpson __func__, imf->im6f_st[0], imf->im6f_st[1]); 9598f002c6cSBruce M Simpson 9608f002c6cSBruce M Simpson if (imf->im6f_st[0] == MCAST_EXCLUDE) { 9618f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: --ex on inm at t1", __func__); 9628f002c6cSBruce M Simpson --inm->in6m_st[1].iss_ex; 9638f002c6cSBruce M Simpson } else if (imf->im6f_st[0] == MCAST_INCLUDE) { 9648f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: --in on inm at t1", __func__); 9658f002c6cSBruce M Simpson --inm->in6m_st[1].iss_in; 9668f002c6cSBruce M Simpson } 9678f002c6cSBruce M Simpson 9688f002c6cSBruce M Simpson if (imf->im6f_st[1] == MCAST_EXCLUDE) { 9698f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: ex++ on inm at t1", __func__); 9708f002c6cSBruce M Simpson inm->in6m_st[1].iss_ex++; 9718f002c6cSBruce M Simpson } else if (imf->im6f_st[1] == MCAST_INCLUDE && nsrc1 > 0) { 9728f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: in++ on inm at t1", __func__); 9738f002c6cSBruce M Simpson inm->in6m_st[1].iss_in++; 9748f002c6cSBruce M Simpson } 9758f002c6cSBruce M Simpson } 9768f002c6cSBruce M Simpson 9778f002c6cSBruce M Simpson /* 9788f002c6cSBruce M Simpson * Track inm filter state in terms of listener counts. 9798f002c6cSBruce M Simpson * If there are any exclusive listeners, stack-wide 9808f002c6cSBruce M Simpson * membership is exclusive. 9818f002c6cSBruce M Simpson * Otherwise, if only inclusive listeners, stack-wide is inclusive. 9828f002c6cSBruce M Simpson * If no listeners remain, state is undefined at t1, 9838f002c6cSBruce M Simpson * and the MLD lifecycle for this group should finish. 9848f002c6cSBruce M Simpson */ 9858f002c6cSBruce M Simpson if (inm->in6m_st[1].iss_ex > 0) { 9868f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: transition to EX", __func__); 9878f002c6cSBruce M Simpson inm->in6m_st[1].iss_fmode = MCAST_EXCLUDE; 9888f002c6cSBruce M Simpson } else if (inm->in6m_st[1].iss_in > 0) { 9898f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: transition to IN", __func__); 9908f002c6cSBruce M Simpson inm->in6m_st[1].iss_fmode = MCAST_INCLUDE; 9918f002c6cSBruce M Simpson } else { 9928f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: transition to UNDEF", __func__); 9938f002c6cSBruce M Simpson inm->in6m_st[1].iss_fmode = MCAST_UNDEFINED; 9948f002c6cSBruce M Simpson } 9958f002c6cSBruce M Simpson 9968f002c6cSBruce M Simpson /* Decrement ASM listener count on transition out of ASM mode. */ 9978f002c6cSBruce M Simpson if (imf->im6f_st[0] == MCAST_EXCLUDE && nsrc0 == 0) { 9988f002c6cSBruce M Simpson if ((imf->im6f_st[1] != MCAST_EXCLUDE) || 9998f002c6cSBruce M Simpson (imf->im6f_st[1] == MCAST_EXCLUDE && nsrc1 > 0)) 10008f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: --asm on inm at t1", __func__); 10018f002c6cSBruce M Simpson --inm->in6m_st[1].iss_asm; 10028f002c6cSBruce M Simpson } 10038f002c6cSBruce M Simpson 10048f002c6cSBruce M Simpson /* Increment ASM listener count on transition to ASM mode. */ 10058f002c6cSBruce M Simpson if (imf->im6f_st[1] == MCAST_EXCLUDE && nsrc1 == 0) { 10068f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: asm++ on inm at t1", __func__); 10078f002c6cSBruce M Simpson inm->in6m_st[1].iss_asm++; 10088f002c6cSBruce M Simpson } 10098f002c6cSBruce M Simpson 10108f002c6cSBruce M Simpson CTR3(KTR_MLD, "%s: merged imf %p to inm %p", __func__, imf, inm); 10118f002c6cSBruce M Simpson in6m_print(inm); 10128f002c6cSBruce M Simpson 10138f002c6cSBruce M Simpson out_reap: 10148f002c6cSBruce M Simpson if (schanged > 0) { 10158f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: sources changed; reaping", __func__); 10168f002c6cSBruce M Simpson in6m_reap(inm); 10178f002c6cSBruce M Simpson } 10188f002c6cSBruce M Simpson return (error); 10198f002c6cSBruce M Simpson } 10208f002c6cSBruce M Simpson 10218f002c6cSBruce M Simpson /* 10228f002c6cSBruce M Simpson * Mark an in6_multi's filter set deltas as committed. 10238f002c6cSBruce M Simpson * Called by MLD after a state change has been enqueued. 10248f002c6cSBruce M Simpson */ 10258f002c6cSBruce M Simpson void 10268f002c6cSBruce M Simpson in6m_commit(struct in6_multi *inm) 10278f002c6cSBruce M Simpson { 10288f002c6cSBruce M Simpson struct ip6_msource *ims; 10298f002c6cSBruce M Simpson 10308f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: commit inm %p", __func__, inm); 10318f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: pre commit:", __func__); 10328f002c6cSBruce M Simpson in6m_print(inm); 10338f002c6cSBruce M Simpson 10348f002c6cSBruce M Simpson RB_FOREACH(ims, ip6_msource_tree, &inm->in6m_srcs) { 10358f002c6cSBruce M Simpson ims->im6s_st[0] = ims->im6s_st[1]; 10368f002c6cSBruce M Simpson } 10378f002c6cSBruce M Simpson inm->in6m_st[0] = inm->in6m_st[1]; 10388f002c6cSBruce M Simpson } 10398f002c6cSBruce M Simpson 10408f002c6cSBruce M Simpson /* 10418f002c6cSBruce M Simpson * Reap unreferenced nodes from an in6_multi's filter set. 10428f002c6cSBruce M Simpson */ 10438f002c6cSBruce M Simpson static void 10448f002c6cSBruce M Simpson in6m_reap(struct in6_multi *inm) 10458f002c6cSBruce M Simpson { 10468f002c6cSBruce M Simpson struct ip6_msource *ims, *tims; 10478f002c6cSBruce M Simpson 10488f002c6cSBruce M Simpson RB_FOREACH_SAFE(ims, ip6_msource_tree, &inm->in6m_srcs, tims) { 10498f002c6cSBruce M Simpson if (ims->im6s_st[0].ex > 0 || ims->im6s_st[0].in > 0 || 10508f002c6cSBruce M Simpson ims->im6s_st[1].ex > 0 || ims->im6s_st[1].in > 0 || 10518f002c6cSBruce M Simpson ims->im6s_stp != 0) 10528f002c6cSBruce M Simpson continue; 10538f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: free ims %p", __func__, ims); 10548f002c6cSBruce M Simpson RB_REMOVE(ip6_msource_tree, &inm->in6m_srcs, ims); 10558f002c6cSBruce M Simpson free(ims, M_IP6MSOURCE); 10568f002c6cSBruce M Simpson inm->in6m_nsrc--; 10578f002c6cSBruce M Simpson } 10588f002c6cSBruce M Simpson } 10598f002c6cSBruce M Simpson 10608f002c6cSBruce M Simpson /* 10618f002c6cSBruce M Simpson * Purge all source nodes from an in6_multi's filter set. 10628f002c6cSBruce M Simpson */ 10638f002c6cSBruce M Simpson static void 10648f002c6cSBruce M Simpson in6m_purge(struct in6_multi *inm) 10658f002c6cSBruce M Simpson { 10668f002c6cSBruce M Simpson struct ip6_msource *ims, *tims; 10678f002c6cSBruce M Simpson 10688f002c6cSBruce M Simpson RB_FOREACH_SAFE(ims, ip6_msource_tree, &inm->in6m_srcs, tims) { 10698f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: free ims %p", __func__, ims); 10708f002c6cSBruce M Simpson RB_REMOVE(ip6_msource_tree, &inm->in6m_srcs, ims); 10718f002c6cSBruce M Simpson free(ims, M_IP6MSOURCE); 10728f002c6cSBruce M Simpson inm->in6m_nsrc--; 10738f002c6cSBruce M Simpson } 10748f002c6cSBruce M Simpson } 10758f002c6cSBruce M Simpson 10768f002c6cSBruce M Simpson /* 10778f002c6cSBruce M Simpson * Join a multicast address w/o sources. 10788f002c6cSBruce M Simpson * KAME compatibility entry point. 10798f002c6cSBruce M Simpson * 10808f002c6cSBruce M Simpson * SMPng: Assume no mc locks held by caller. 10818f002c6cSBruce M Simpson */ 10828f002c6cSBruce M Simpson struct in6_multi_mship * 10838f002c6cSBruce M Simpson in6_joingroup(struct ifnet *ifp, struct in6_addr *mcaddr, 10848f002c6cSBruce M Simpson int *errorp, int delay) 10858f002c6cSBruce M Simpson { 10868f002c6cSBruce M Simpson struct in6_multi_mship *imm; 10878f002c6cSBruce M Simpson int error; 10888f002c6cSBruce M Simpson 10898f002c6cSBruce M Simpson imm = malloc(sizeof(*imm), M_IP6MADDR, M_NOWAIT); 10908f002c6cSBruce M Simpson if (imm == NULL) { 10918f002c6cSBruce M Simpson *errorp = ENOBUFS; 10928f002c6cSBruce M Simpson return (NULL); 10938f002c6cSBruce M Simpson } 10948f002c6cSBruce M Simpson 10958f002c6cSBruce M Simpson delay = (delay * PR_FASTHZ) / hz; 10968f002c6cSBruce M Simpson 10978f002c6cSBruce M Simpson error = in6_mc_join(ifp, mcaddr, NULL, &imm->i6mm_maddr, delay); 10988f002c6cSBruce M Simpson if (error) { 10998f002c6cSBruce M Simpson *errorp = error; 11008f002c6cSBruce M Simpson free(imm, M_IP6MADDR); 11018f002c6cSBruce M Simpson return (NULL); 11028f002c6cSBruce M Simpson } 11038f002c6cSBruce M Simpson 11048f002c6cSBruce M Simpson return (imm); 11058f002c6cSBruce M Simpson } 11068f002c6cSBruce M Simpson 11078f002c6cSBruce M Simpson /* 11088f002c6cSBruce M Simpson * Leave a multicast address w/o sources. 11098f002c6cSBruce M Simpson * KAME compatibility entry point. 11108f002c6cSBruce M Simpson * 11118f002c6cSBruce M Simpson * SMPng: Assume no mc locks held by caller. 11128f002c6cSBruce M Simpson */ 11138f002c6cSBruce M Simpson int 11148f002c6cSBruce M Simpson in6_leavegroup(struct in6_multi_mship *imm) 11158f002c6cSBruce M Simpson { 11168f002c6cSBruce M Simpson 11178f002c6cSBruce M Simpson if (imm->i6mm_maddr != NULL) 11188f002c6cSBruce M Simpson in6_mc_leave(imm->i6mm_maddr, NULL); 11198f002c6cSBruce M Simpson free(imm, M_IP6MADDR); 11208f002c6cSBruce M Simpson return 0; 11218f002c6cSBruce M Simpson } 11228f002c6cSBruce M Simpson 11238f002c6cSBruce M Simpson /* 11248f002c6cSBruce M Simpson * Join a multicast group; unlocked entry point. 11258f002c6cSBruce M Simpson * 11268f002c6cSBruce M Simpson * SMPng: XXX: in6_mc_join() is called from in6_control() when upper 11278f002c6cSBruce M Simpson * locks are not held. Fortunately, ifp is unlikely to have been detached 11288f002c6cSBruce M Simpson * at this point, so we assume it's OK to recurse. 11298f002c6cSBruce M Simpson */ 11308f002c6cSBruce M Simpson int 11318f002c6cSBruce M Simpson in6_mc_join(struct ifnet *ifp, const struct in6_addr *mcaddr, 11328f002c6cSBruce M Simpson /*const*/ struct in6_mfilter *imf, struct in6_multi **pinm, 11338f002c6cSBruce M Simpson const int delay) 11348f002c6cSBruce M Simpson { 11358f002c6cSBruce M Simpson int error; 11368f002c6cSBruce M Simpson 11378f002c6cSBruce M Simpson IN6_MULTI_LOCK(); 11388f002c6cSBruce M Simpson error = in6_mc_join_locked(ifp, mcaddr, imf, pinm, delay); 11398f002c6cSBruce M Simpson IN6_MULTI_UNLOCK(); 11408f002c6cSBruce M Simpson 11418f002c6cSBruce M Simpson return (error); 11428f002c6cSBruce M Simpson } 11438f002c6cSBruce M Simpson 11448f002c6cSBruce M Simpson /* 11458f002c6cSBruce M Simpson * Join a multicast group; real entry point. 11468f002c6cSBruce M Simpson * 11478f002c6cSBruce M Simpson * Only preserves atomicity at inm level. 11488f002c6cSBruce M Simpson * NOTE: imf argument cannot be const due to sys/tree.h limitations. 11498f002c6cSBruce M Simpson * 11508f002c6cSBruce M Simpson * If the MLD downcall fails, the group is not joined, and an error 11518f002c6cSBruce M Simpson * code is returned. 11528f002c6cSBruce M Simpson */ 11538f002c6cSBruce M Simpson int 11548f002c6cSBruce M Simpson in6_mc_join_locked(struct ifnet *ifp, const struct in6_addr *mcaddr, 11558f002c6cSBruce M Simpson /*const*/ struct in6_mfilter *imf, struct in6_multi **pinm, 11568f002c6cSBruce M Simpson const int delay) 11578f002c6cSBruce M Simpson { 11588f002c6cSBruce M Simpson struct in6_mfilter timf; 11598f002c6cSBruce M Simpson struct in6_multi *inm; 11608f002c6cSBruce M Simpson int error; 11618f002c6cSBruce M Simpson #ifdef KTR 11628f002c6cSBruce M Simpson char ip6tbuf[INET6_ADDRSTRLEN]; 11638f002c6cSBruce M Simpson #endif 11648f002c6cSBruce M Simpson 116529dc7bc6SBruce M Simpson #ifdef INVARIANTS 116629dc7bc6SBruce M Simpson /* 116729dc7bc6SBruce M Simpson * Sanity: Check scope zone ID was set for ifp, if and 116829dc7bc6SBruce M Simpson * only if group is scoped to an interface. 116929dc7bc6SBruce M Simpson */ 117029dc7bc6SBruce M Simpson KASSERT(IN6_IS_ADDR_MULTICAST(mcaddr), 117129dc7bc6SBruce M Simpson ("%s: not a multicast address", __func__)); 117229dc7bc6SBruce M Simpson if (IN6_IS_ADDR_MC_LINKLOCAL(mcaddr) || 117329dc7bc6SBruce M Simpson IN6_IS_ADDR_MC_INTFACELOCAL(mcaddr)) { 117429dc7bc6SBruce M Simpson KASSERT(mcaddr->s6_addr16[1] != 0, 117529dc7bc6SBruce M Simpson ("%s: scope zone ID not set", __func__)); 117629dc7bc6SBruce M Simpson } 117729dc7bc6SBruce M Simpson #endif 117829dc7bc6SBruce M Simpson 11798f002c6cSBruce M Simpson IN6_MULTI_LOCK_ASSERT(); 11808f002c6cSBruce M Simpson 11818f002c6cSBruce M Simpson CTR4(KTR_MLD, "%s: join %s on %p(%s))", __func__, 11828f002c6cSBruce M Simpson ip6_sprintf(ip6tbuf, mcaddr), ifp, ifp->if_xname); 11838f002c6cSBruce M Simpson 11848f002c6cSBruce M Simpson error = 0; 11858f002c6cSBruce M Simpson inm = NULL; 11868f002c6cSBruce M Simpson 11878f002c6cSBruce M Simpson /* 11888f002c6cSBruce M Simpson * If no imf was specified (i.e. kernel consumer), 11898f002c6cSBruce M Simpson * fake one up and assume it is an ASM join. 11908f002c6cSBruce M Simpson */ 11918f002c6cSBruce M Simpson if (imf == NULL) { 11928f002c6cSBruce M Simpson im6f_init(&timf, MCAST_UNDEFINED, MCAST_EXCLUDE); 11938f002c6cSBruce M Simpson imf = &timf; 11948f002c6cSBruce M Simpson } 11958f002c6cSBruce M Simpson 11968f002c6cSBruce M Simpson error = in6_mc_get(ifp, mcaddr, &inm); 11978f002c6cSBruce M Simpson if (error) { 11988f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: in6_mc_get() failure", __func__); 11998f002c6cSBruce M Simpson return (error); 12008f002c6cSBruce M Simpson } 12018f002c6cSBruce M Simpson 12028f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: merge inm state", __func__); 12038f002c6cSBruce M Simpson error = in6m_merge(inm, imf); 12048f002c6cSBruce M Simpson if (error) { 12058f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: failed to merge inm state", __func__); 12068f002c6cSBruce M Simpson goto out_in6m_release; 12078f002c6cSBruce M Simpson } 12088f002c6cSBruce M Simpson 12098f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: doing mld downcall", __func__); 12108f002c6cSBruce M Simpson error = mld_change_state(inm, delay); 12118f002c6cSBruce M Simpson if (error) { 12128f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: failed to update source", __func__); 12138f002c6cSBruce M Simpson goto out_in6m_release; 12148f002c6cSBruce M Simpson } 12158f002c6cSBruce M Simpson 12168f002c6cSBruce M Simpson out_in6m_release: 12178f002c6cSBruce M Simpson if (error) { 12188f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: dropping ref on %p", __func__, inm); 12198f002c6cSBruce M Simpson in6m_release_locked(inm); 12208f002c6cSBruce M Simpson } else { 12218f002c6cSBruce M Simpson *pinm = inm; 12228f002c6cSBruce M Simpson } 12238f002c6cSBruce M Simpson 12248f002c6cSBruce M Simpson return (error); 12258f002c6cSBruce M Simpson } 12268f002c6cSBruce M Simpson 12278f002c6cSBruce M Simpson /* 12288f002c6cSBruce M Simpson * Leave a multicast group; unlocked entry point. 12298f002c6cSBruce M Simpson */ 12308f002c6cSBruce M Simpson int 12318f002c6cSBruce M Simpson in6_mc_leave(struct in6_multi *inm, /*const*/ struct in6_mfilter *imf) 12328f002c6cSBruce M Simpson { 12338f002c6cSBruce M Simpson struct ifnet *ifp; 12348f002c6cSBruce M Simpson int error; 12358f002c6cSBruce M Simpson 12368f002c6cSBruce M Simpson ifp = inm->in6m_ifp; 12378f002c6cSBruce M Simpson 12388f002c6cSBruce M Simpson IN6_MULTI_LOCK(); 12398f002c6cSBruce M Simpson error = in6_mc_leave_locked(inm, imf); 12408f002c6cSBruce M Simpson IN6_MULTI_UNLOCK(); 12418f002c6cSBruce M Simpson 12428f002c6cSBruce M Simpson return (error); 12438f002c6cSBruce M Simpson } 12448f002c6cSBruce M Simpson 12458f002c6cSBruce M Simpson /* 12468f002c6cSBruce M Simpson * Leave a multicast group; real entry point. 12478f002c6cSBruce M Simpson * All source filters will be expunged. 12488f002c6cSBruce M Simpson * 12498f002c6cSBruce M Simpson * Only preserves atomicity at inm level. 12508f002c6cSBruce M Simpson * 12518f002c6cSBruce M Simpson * Holding the write lock for the INP which contains imf 12528f002c6cSBruce M Simpson * is highly advisable. We can't assert for it as imf does not 12538f002c6cSBruce M Simpson * contain a back-pointer to the owning inp. 12548f002c6cSBruce M Simpson * 12558f002c6cSBruce M Simpson * Note: This is not the same as in6m_release(*) as this function also 12568f002c6cSBruce M Simpson * makes a state change downcall into MLD. 12578f002c6cSBruce M Simpson */ 12588f002c6cSBruce M Simpson int 12598f002c6cSBruce M Simpson in6_mc_leave_locked(struct in6_multi *inm, /*const*/ struct in6_mfilter *imf) 12608f002c6cSBruce M Simpson { 12618f002c6cSBruce M Simpson struct in6_mfilter timf; 12628f002c6cSBruce M Simpson int error; 12638f002c6cSBruce M Simpson #ifdef KTR 12648f002c6cSBruce M Simpson char ip6tbuf[INET6_ADDRSTRLEN]; 12658f002c6cSBruce M Simpson #endif 12668f002c6cSBruce M Simpson 12678f002c6cSBruce M Simpson error = 0; 12688f002c6cSBruce M Simpson 12698f002c6cSBruce M Simpson IN6_MULTI_LOCK_ASSERT(); 12708f002c6cSBruce M Simpson 12718f002c6cSBruce M Simpson CTR5(KTR_MLD, "%s: leave inm %p, %s/%s, imf %p", __func__, 12728f002c6cSBruce M Simpson inm, ip6_sprintf(ip6tbuf, &inm->in6m_addr), 12738f002c6cSBruce M Simpson (in6m_is_ifp_detached(inm) ? "null" : inm->in6m_ifp->if_xname), 12748f002c6cSBruce M Simpson imf); 12758f002c6cSBruce M Simpson 12768f002c6cSBruce M Simpson /* 12778f002c6cSBruce M Simpson * If no imf was specified (i.e. kernel consumer), 12788f002c6cSBruce M Simpson * fake one up and assume it is an ASM join. 12798f002c6cSBruce M Simpson */ 12808f002c6cSBruce M Simpson if (imf == NULL) { 12818f002c6cSBruce M Simpson im6f_init(&timf, MCAST_EXCLUDE, MCAST_UNDEFINED); 12828f002c6cSBruce M Simpson imf = &timf; 12838f002c6cSBruce M Simpson } 12848f002c6cSBruce M Simpson 12858f002c6cSBruce M Simpson /* 12868f002c6cSBruce M Simpson * Begin state merge transaction at MLD layer. 12878f002c6cSBruce M Simpson * 12888f002c6cSBruce M Simpson * As this particular invocation should not cause any memory 12898f002c6cSBruce M Simpson * to be allocated, and there is no opportunity to roll back 12908f002c6cSBruce M Simpson * the transaction, it MUST NOT fail. 12918f002c6cSBruce M Simpson */ 12928f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: merge inm state", __func__); 12938f002c6cSBruce M Simpson error = in6m_merge(inm, imf); 12948f002c6cSBruce M Simpson KASSERT(error == 0, ("%s: failed to merge inm state", __func__)); 12958f002c6cSBruce M Simpson 12968f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: doing mld downcall", __func__); 12978f002c6cSBruce M Simpson error = mld_change_state(inm, 0); 12988f002c6cSBruce M Simpson if (error) 12998f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: failed mld downcall", __func__); 13008f002c6cSBruce M Simpson 13018f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: dropping ref on %p", __func__, inm); 13028f002c6cSBruce M Simpson in6m_release_locked(inm); 13038f002c6cSBruce M Simpson 13048f002c6cSBruce M Simpson return (error); 13058f002c6cSBruce M Simpson } 13068f002c6cSBruce M Simpson 13078f002c6cSBruce M Simpson /* 13088f002c6cSBruce M Simpson * Block or unblock an ASM multicast source on an inpcb. 13098f002c6cSBruce M Simpson * This implements the delta-based API described in RFC 3678. 13108f002c6cSBruce M Simpson * 13118f002c6cSBruce M Simpson * The delta-based API applies only to exclusive-mode memberships. 13128f002c6cSBruce M Simpson * An MLD downcall will be performed. 13138f002c6cSBruce M Simpson * 13148f002c6cSBruce M Simpson * SMPng: NOTE: Must take Giant as a join may create a new ifma. 13158f002c6cSBruce M Simpson * 13168f002c6cSBruce M Simpson * Return 0 if successful, otherwise return an appropriate error code. 13178f002c6cSBruce M Simpson */ 13188f002c6cSBruce M Simpson static int 13198f002c6cSBruce M Simpson in6p_block_unblock_source(struct inpcb *inp, struct sockopt *sopt) 13208f002c6cSBruce M Simpson { 13218f002c6cSBruce M Simpson struct group_source_req gsr; 13228f002c6cSBruce M Simpson sockunion_t *gsa, *ssa; 13238f002c6cSBruce M Simpson struct ifnet *ifp; 13248f002c6cSBruce M Simpson struct in6_mfilter *imf; 13258f002c6cSBruce M Simpson struct ip6_moptions *imo; 13268f002c6cSBruce M Simpson struct in6_msource *ims; 13278f002c6cSBruce M Simpson struct in6_multi *inm; 13288f002c6cSBruce M Simpson size_t idx; 13298f002c6cSBruce M Simpson uint16_t fmode; 13308f002c6cSBruce M Simpson int error, doblock; 13318f002c6cSBruce M Simpson #ifdef KTR 13328f002c6cSBruce M Simpson char ip6tbuf[INET6_ADDRSTRLEN]; 13338f002c6cSBruce M Simpson #endif 13348f002c6cSBruce M Simpson 13358f002c6cSBruce M Simpson ifp = NULL; 13368f002c6cSBruce M Simpson error = 0; 13378f002c6cSBruce M Simpson doblock = 0; 13388f002c6cSBruce M Simpson 13398f002c6cSBruce M Simpson memset(&gsr, 0, sizeof(struct group_source_req)); 13408f002c6cSBruce M Simpson gsa = (sockunion_t *)&gsr.gsr_group; 13418f002c6cSBruce M Simpson ssa = (sockunion_t *)&gsr.gsr_source; 13428f002c6cSBruce M Simpson 13438f002c6cSBruce M Simpson switch (sopt->sopt_name) { 13448f002c6cSBruce M Simpson case MCAST_BLOCK_SOURCE: 13458f002c6cSBruce M Simpson case MCAST_UNBLOCK_SOURCE: 13468f002c6cSBruce M Simpson error = sooptcopyin(sopt, &gsr, 13478f002c6cSBruce M Simpson sizeof(struct group_source_req), 13488f002c6cSBruce M Simpson sizeof(struct group_source_req)); 13498f002c6cSBruce M Simpson if (error) 13508f002c6cSBruce M Simpson return (error); 13518f002c6cSBruce M Simpson 13528f002c6cSBruce M Simpson if (gsa->sin6.sin6_family != AF_INET6 || 13538f002c6cSBruce M Simpson gsa->sin6.sin6_len != sizeof(struct sockaddr_in6)) 13548f002c6cSBruce M Simpson return (EINVAL); 13558f002c6cSBruce M Simpson 13568f002c6cSBruce M Simpson if (ssa->sin6.sin6_family != AF_INET6 || 13578f002c6cSBruce M Simpson ssa->sin6.sin6_len != sizeof(struct sockaddr_in6)) 13588f002c6cSBruce M Simpson return (EINVAL); 13598f002c6cSBruce M Simpson 13608f002c6cSBruce M Simpson if (gsr.gsr_interface == 0 || V_if_index < gsr.gsr_interface) 13618f002c6cSBruce M Simpson return (EADDRNOTAVAIL); 13628f002c6cSBruce M Simpson 13638f002c6cSBruce M Simpson ifp = ifnet_byindex(gsr.gsr_interface); 13648f002c6cSBruce M Simpson 13658f002c6cSBruce M Simpson if (sopt->sopt_name == MCAST_BLOCK_SOURCE) 13668f002c6cSBruce M Simpson doblock = 1; 13678f002c6cSBruce M Simpson break; 13688f002c6cSBruce M Simpson 13698f002c6cSBruce M Simpson default: 13708f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: unknown sopt_name %d", 13718f002c6cSBruce M Simpson __func__, sopt->sopt_name); 13728f002c6cSBruce M Simpson return (EOPNOTSUPP); 13738f002c6cSBruce M Simpson break; 13748f002c6cSBruce M Simpson } 13758f002c6cSBruce M Simpson 13768f002c6cSBruce M Simpson if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr)) 13778f002c6cSBruce M Simpson return (EINVAL); 13788f002c6cSBruce M Simpson 137929dc7bc6SBruce M Simpson (void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL); 138029dc7bc6SBruce M Simpson 13818f002c6cSBruce M Simpson /* 13828f002c6cSBruce M Simpson * Check if we are actually a member of this group. 13838f002c6cSBruce M Simpson */ 13848f002c6cSBruce M Simpson imo = in6p_findmoptions(inp); 13858f002c6cSBruce M Simpson idx = im6o_match_group(imo, ifp, &gsa->sa); 13868f002c6cSBruce M Simpson if (idx == -1 || imo->im6o_mfilters == NULL) { 13878f002c6cSBruce M Simpson error = EADDRNOTAVAIL; 13888f002c6cSBruce M Simpson goto out_in6p_locked; 13898f002c6cSBruce M Simpson } 13908f002c6cSBruce M Simpson 13918f002c6cSBruce M Simpson KASSERT(imo->im6o_mfilters != NULL, 13928f002c6cSBruce M Simpson ("%s: im6o_mfilters not allocated", __func__)); 13938f002c6cSBruce M Simpson imf = &imo->im6o_mfilters[idx]; 13948f002c6cSBruce M Simpson inm = imo->im6o_membership[idx]; 13958f002c6cSBruce M Simpson 13968f002c6cSBruce M Simpson /* 13978f002c6cSBruce M Simpson * Attempting to use the delta-based API on an 13988f002c6cSBruce M Simpson * non exclusive-mode membership is an error. 13998f002c6cSBruce M Simpson */ 14008f002c6cSBruce M Simpson fmode = imf->im6f_st[0]; 14018f002c6cSBruce M Simpson if (fmode != MCAST_EXCLUDE) { 14028f002c6cSBruce M Simpson error = EINVAL; 14038f002c6cSBruce M Simpson goto out_in6p_locked; 14048f002c6cSBruce M Simpson } 14058f002c6cSBruce M Simpson 14068f002c6cSBruce M Simpson /* 14078f002c6cSBruce M Simpson * Deal with error cases up-front: 14088f002c6cSBruce M Simpson * Asked to block, but already blocked; or 14098f002c6cSBruce M Simpson * Asked to unblock, but nothing to unblock. 14108f002c6cSBruce M Simpson * If adding a new block entry, allocate it. 14118f002c6cSBruce M Simpson */ 14128f002c6cSBruce M Simpson ims = im6o_match_source(imo, idx, &ssa->sa); 14138f002c6cSBruce M Simpson if ((ims != NULL && doblock) || (ims == NULL && !doblock)) { 14148f002c6cSBruce M Simpson CTR3(KTR_MLD, "%s: source %s %spresent", __func__, 14158f002c6cSBruce M Simpson ip6_sprintf(ip6tbuf, &ssa->sin6.sin6_addr), 14168f002c6cSBruce M Simpson doblock ? "" : "not "); 14178f002c6cSBruce M Simpson error = EADDRNOTAVAIL; 14188f002c6cSBruce M Simpson goto out_in6p_locked; 14198f002c6cSBruce M Simpson } 14208f002c6cSBruce M Simpson 14218f002c6cSBruce M Simpson INP_WLOCK_ASSERT(inp); 14228f002c6cSBruce M Simpson 14238f002c6cSBruce M Simpson /* 14248f002c6cSBruce M Simpson * Begin state merge transaction at socket layer. 14258f002c6cSBruce M Simpson */ 14268f002c6cSBruce M Simpson if (doblock) { 14278f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: %s source", __func__, "block"); 14288f002c6cSBruce M Simpson ims = im6f_graft(imf, fmode, &ssa->sin6); 14298f002c6cSBruce M Simpson if (ims == NULL) 14308f002c6cSBruce M Simpson error = ENOMEM; 14318f002c6cSBruce M Simpson } else { 14328f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: %s source", __func__, "allow"); 14338f002c6cSBruce M Simpson error = im6f_prune(imf, &ssa->sin6); 14348f002c6cSBruce M Simpson } 14358f002c6cSBruce M Simpson 14368f002c6cSBruce M Simpson if (error) { 14378f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: merge imf state failed", __func__); 14388f002c6cSBruce M Simpson goto out_im6f_rollback; 14398f002c6cSBruce M Simpson } 14408f002c6cSBruce M Simpson 14418f002c6cSBruce M Simpson /* 14428f002c6cSBruce M Simpson * Begin state merge transaction at MLD layer. 14438f002c6cSBruce M Simpson */ 14448f002c6cSBruce M Simpson IN6_MULTI_LOCK(); 14458f002c6cSBruce M Simpson 14468f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: merge inm state", __func__); 14478f002c6cSBruce M Simpson error = in6m_merge(inm, imf); 14488f002c6cSBruce M Simpson if (error) { 14498f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: failed to merge inm state", __func__); 14508f002c6cSBruce M Simpson goto out_im6f_rollback; 14518f002c6cSBruce M Simpson } 14528f002c6cSBruce M Simpson 14538f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: doing mld downcall", __func__); 14548f002c6cSBruce M Simpson error = mld_change_state(inm, 0); 14558f002c6cSBruce M Simpson if (error) 14568f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: failed mld downcall", __func__); 14578f002c6cSBruce M Simpson 14588f002c6cSBruce M Simpson IN6_MULTI_UNLOCK(); 14598f002c6cSBruce M Simpson 14608f002c6cSBruce M Simpson out_im6f_rollback: 14618f002c6cSBruce M Simpson if (error) 14628f002c6cSBruce M Simpson im6f_rollback(imf); 14638f002c6cSBruce M Simpson else 14648f002c6cSBruce M Simpson im6f_commit(imf); 14658f002c6cSBruce M Simpson 14668f002c6cSBruce M Simpson im6f_reap(imf); 14678f002c6cSBruce M Simpson 14688f002c6cSBruce M Simpson out_in6p_locked: 14698f002c6cSBruce M Simpson INP_WUNLOCK(inp); 14708f002c6cSBruce M Simpson return (error); 14718f002c6cSBruce M Simpson } 14728f002c6cSBruce M Simpson 14738f002c6cSBruce M Simpson /* 14748f002c6cSBruce M Simpson * Given an inpcb, return its multicast options structure pointer. Accepts 14758f002c6cSBruce M Simpson * an unlocked inpcb pointer, but will return it locked. May sleep. 14768f002c6cSBruce M Simpson * 14778f002c6cSBruce M Simpson * SMPng: NOTE: Potentially calls malloc(M_WAITOK) with Giant held. 14788f002c6cSBruce M Simpson * SMPng: NOTE: Returns with the INP write lock held. 14798f002c6cSBruce M Simpson */ 14808f002c6cSBruce M Simpson static struct ip6_moptions * 14818f002c6cSBruce M Simpson in6p_findmoptions(struct inpcb *inp) 14828f002c6cSBruce M Simpson { 14838f002c6cSBruce M Simpson struct ip6_moptions *imo; 14848f002c6cSBruce M Simpson struct in6_multi **immp; 14858f002c6cSBruce M Simpson struct in6_mfilter *imfp; 14868f002c6cSBruce M Simpson size_t idx; 14878f002c6cSBruce M Simpson 14888f002c6cSBruce M Simpson INP_WLOCK(inp); 14898f002c6cSBruce M Simpson if (inp->in6p_moptions != NULL) 14908f002c6cSBruce M Simpson return (inp->in6p_moptions); 14918f002c6cSBruce M Simpson 14928f002c6cSBruce M Simpson INP_WUNLOCK(inp); 14938f002c6cSBruce M Simpson 14948f002c6cSBruce M Simpson imo = malloc(sizeof(*imo), M_IP6MOPTS, M_WAITOK); 14958f002c6cSBruce M Simpson immp = malloc(sizeof(*immp) * IPV6_MIN_MEMBERSHIPS, M_IP6MOPTS, 14968f002c6cSBruce M Simpson M_WAITOK | M_ZERO); 14978f002c6cSBruce M Simpson imfp = malloc(sizeof(struct in6_mfilter) * IPV6_MIN_MEMBERSHIPS, 14988f002c6cSBruce M Simpson M_IN6MFILTER, M_WAITOK); 14998f002c6cSBruce M Simpson 15008f002c6cSBruce M Simpson imo->im6o_multicast_ifp = NULL; 15018f002c6cSBruce M Simpson imo->im6o_multicast_hlim = V_ip6_defmcasthlim; 15028f002c6cSBruce M Simpson imo->im6o_multicast_loop = in6_mcast_loop; 15038f002c6cSBruce M Simpson imo->im6o_num_memberships = 0; 15048f002c6cSBruce M Simpson imo->im6o_max_memberships = IPV6_MIN_MEMBERSHIPS; 15058f002c6cSBruce M Simpson imo->im6o_membership = immp; 15068f002c6cSBruce M Simpson 15078f002c6cSBruce M Simpson /* Initialize per-group source filters. */ 15088f002c6cSBruce M Simpson for (idx = 0; idx < IPV6_MIN_MEMBERSHIPS; idx++) 15098f002c6cSBruce M Simpson im6f_init(&imfp[idx], MCAST_UNDEFINED, MCAST_EXCLUDE); 15108f002c6cSBruce M Simpson imo->im6o_mfilters = imfp; 15118f002c6cSBruce M Simpson 15128f002c6cSBruce M Simpson INP_WLOCK(inp); 15138f002c6cSBruce M Simpson if (inp->in6p_moptions != NULL) { 15148f002c6cSBruce M Simpson free(imfp, M_IN6MFILTER); 15158f002c6cSBruce M Simpson free(immp, M_IP6MOPTS); 15168f002c6cSBruce M Simpson free(imo, M_IP6MOPTS); 15178f002c6cSBruce M Simpson return (inp->in6p_moptions); 15188f002c6cSBruce M Simpson } 15198f002c6cSBruce M Simpson inp->in6p_moptions = imo; 15208f002c6cSBruce M Simpson return (imo); 15218f002c6cSBruce M Simpson } 15228f002c6cSBruce M Simpson 15238f002c6cSBruce M Simpson /* 15248f002c6cSBruce M Simpson * Discard the IPv6 multicast options (and source filters). 15258f002c6cSBruce M Simpson * 15268f002c6cSBruce M Simpson * SMPng: NOTE: assumes INP write lock is held. 15278f002c6cSBruce M Simpson */ 15288f002c6cSBruce M Simpson void 15298f002c6cSBruce M Simpson ip6_freemoptions(struct ip6_moptions *imo) 15308f002c6cSBruce M Simpson { 15318f002c6cSBruce M Simpson struct in6_mfilter *imf; 15328f002c6cSBruce M Simpson size_t idx, nmships; 15338f002c6cSBruce M Simpson 15348f002c6cSBruce M Simpson KASSERT(imo != NULL, ("%s: ip6_moptions is NULL", __func__)); 15358f002c6cSBruce M Simpson 15368f002c6cSBruce M Simpson nmships = imo->im6o_num_memberships; 15378f002c6cSBruce M Simpson for (idx = 0; idx < nmships; ++idx) { 15388f002c6cSBruce M Simpson imf = imo->im6o_mfilters ? &imo->im6o_mfilters[idx] : NULL; 15398f002c6cSBruce M Simpson if (imf) 15408f002c6cSBruce M Simpson im6f_leave(imf); 15418f002c6cSBruce M Simpson /* XXX this will thrash the lock(s) */ 15428f002c6cSBruce M Simpson (void)in6_mc_leave(imo->im6o_membership[idx], imf); 15438f002c6cSBruce M Simpson if (imf) 15448f002c6cSBruce M Simpson im6f_purge(imf); 15458f002c6cSBruce M Simpson } 15468f002c6cSBruce M Simpson 15478f002c6cSBruce M Simpson if (imo->im6o_mfilters) 15488f002c6cSBruce M Simpson free(imo->im6o_mfilters, M_IN6MFILTER); 15498f002c6cSBruce M Simpson free(imo->im6o_membership, M_IP6MOPTS); 15508f002c6cSBruce M Simpson free(imo, M_IP6MOPTS); 15518f002c6cSBruce M Simpson } 15528f002c6cSBruce M Simpson 15538f002c6cSBruce M Simpson /* 15548f002c6cSBruce M Simpson * Atomically get source filters on a socket for an IPv6 multicast group. 15558f002c6cSBruce M Simpson * Called with INP lock held; returns with lock released. 15568f002c6cSBruce M Simpson */ 15578f002c6cSBruce M Simpson static int 15588f002c6cSBruce M Simpson in6p_get_source_filters(struct inpcb *inp, struct sockopt *sopt) 15598f002c6cSBruce M Simpson { 15608f002c6cSBruce M Simpson struct __msfilterreq msfr; 15618f002c6cSBruce M Simpson sockunion_t *gsa; 15628f002c6cSBruce M Simpson struct ifnet *ifp; 15638f002c6cSBruce M Simpson struct ip6_moptions *imo; 15648f002c6cSBruce M Simpson struct in6_mfilter *imf; 15658f002c6cSBruce M Simpson struct ip6_msource *ims; 15668f002c6cSBruce M Simpson struct in6_msource *lims; 15678f002c6cSBruce M Simpson struct sockaddr_in6 *psin; 15688f002c6cSBruce M Simpson struct sockaddr_storage *ptss; 15698f002c6cSBruce M Simpson struct sockaddr_storage *tss; 15708f002c6cSBruce M Simpson int error; 15718f002c6cSBruce M Simpson size_t idx, nsrcs, ncsrcs; 15728f002c6cSBruce M Simpson 15738f002c6cSBruce M Simpson INP_WLOCK_ASSERT(inp); 15748f002c6cSBruce M Simpson 15758f002c6cSBruce M Simpson imo = inp->in6p_moptions; 15768f002c6cSBruce M Simpson KASSERT(imo != NULL, ("%s: null ip6_moptions", __func__)); 15778f002c6cSBruce M Simpson 15788f002c6cSBruce M Simpson INP_WUNLOCK(inp); 15798f002c6cSBruce M Simpson 15808f002c6cSBruce M Simpson error = sooptcopyin(sopt, &msfr, sizeof(struct __msfilterreq), 15818f002c6cSBruce M Simpson sizeof(struct __msfilterreq)); 15828f002c6cSBruce M Simpson if (error) 15838f002c6cSBruce M Simpson return (error); 15848f002c6cSBruce M Simpson 158529dc7bc6SBruce M Simpson if (msfr.msfr_group.ss_family != AF_INET6 || 158629dc7bc6SBruce M Simpson msfr.msfr_group.ss_len != sizeof(struct sockaddr_in6)) 15878f002c6cSBruce M Simpson return (EINVAL); 15888f002c6cSBruce M Simpson 158929dc7bc6SBruce M Simpson gsa = (sockunion_t *)&msfr.msfr_group; 159029dc7bc6SBruce M Simpson if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr)) 159129dc7bc6SBruce M Simpson return (EINVAL); 159229dc7bc6SBruce M Simpson 159329dc7bc6SBruce M Simpson if (msfr.msfr_ifindex == 0 || V_if_index < msfr.msfr_ifindex) 159429dc7bc6SBruce M Simpson return (EADDRNOTAVAIL); 15958f002c6cSBruce M Simpson ifp = ifnet_byindex(msfr.msfr_ifindex); 15968f002c6cSBruce M Simpson if (ifp == NULL) 159729dc7bc6SBruce M Simpson return (EADDRNOTAVAIL); 159829dc7bc6SBruce M Simpson (void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL); 15998f002c6cSBruce M Simpson 16008f002c6cSBruce M Simpson INP_WLOCK(inp); 16018f002c6cSBruce M Simpson 16028f002c6cSBruce M Simpson /* 16038f002c6cSBruce M Simpson * Lookup group on the socket. 16048f002c6cSBruce M Simpson */ 16058f002c6cSBruce M Simpson idx = im6o_match_group(imo, ifp, &gsa->sa); 16068f002c6cSBruce M Simpson if (idx == -1 || imo->im6o_mfilters == NULL) { 16078f002c6cSBruce M Simpson INP_WUNLOCK(inp); 16088f002c6cSBruce M Simpson return (EADDRNOTAVAIL); 16098f002c6cSBruce M Simpson } 16108f002c6cSBruce M Simpson imf = &imo->im6o_mfilters[idx]; 16118f002c6cSBruce M Simpson 16128f002c6cSBruce M Simpson /* 16138f002c6cSBruce M Simpson * Ignore memberships which are in limbo. 16148f002c6cSBruce M Simpson */ 16158f002c6cSBruce M Simpson if (imf->im6f_st[1] == MCAST_UNDEFINED) { 16168f002c6cSBruce M Simpson INP_WUNLOCK(inp); 16178f002c6cSBruce M Simpson return (EAGAIN); 16188f002c6cSBruce M Simpson } 16198f002c6cSBruce M Simpson msfr.msfr_fmode = imf->im6f_st[1]; 16208f002c6cSBruce M Simpson 16218f002c6cSBruce M Simpson /* 16228f002c6cSBruce M Simpson * If the user specified a buffer, copy out the source filter 16238f002c6cSBruce M Simpson * entries to userland gracefully. 16248f002c6cSBruce M Simpson * We only copy out the number of entries which userland 16258f002c6cSBruce M Simpson * has asked for, but we always tell userland how big the 16268f002c6cSBruce M Simpson * buffer really needs to be. 16278f002c6cSBruce M Simpson */ 16288f002c6cSBruce M Simpson tss = NULL; 16298f002c6cSBruce M Simpson if (msfr.msfr_srcs != NULL && msfr.msfr_nsrcs > 0) { 16308f002c6cSBruce M Simpson tss = malloc(sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs, 16318f002c6cSBruce M Simpson M_TEMP, M_NOWAIT | M_ZERO); 16328f002c6cSBruce M Simpson if (tss == NULL) { 16338f002c6cSBruce M Simpson INP_WUNLOCK(inp); 16348f002c6cSBruce M Simpson return (ENOBUFS); 16358f002c6cSBruce M Simpson } 16368f002c6cSBruce M Simpson } 16378f002c6cSBruce M Simpson 16388f002c6cSBruce M Simpson /* 16398f002c6cSBruce M Simpson * Count number of sources in-mode at t0. 16408f002c6cSBruce M Simpson * If buffer space exists and remains, copy out source entries. 16418f002c6cSBruce M Simpson */ 16428f002c6cSBruce M Simpson nsrcs = msfr.msfr_nsrcs; 16438f002c6cSBruce M Simpson ncsrcs = 0; 16448f002c6cSBruce M Simpson ptss = tss; 16458f002c6cSBruce M Simpson RB_FOREACH(ims, ip6_msource_tree, &imf->im6f_sources) { 16468f002c6cSBruce M Simpson lims = (struct in6_msource *)ims; 16478f002c6cSBruce M Simpson if (lims->im6sl_st[0] == MCAST_UNDEFINED || 16488f002c6cSBruce M Simpson lims->im6sl_st[0] != imf->im6f_st[0]) 16498f002c6cSBruce M Simpson continue; 16508f002c6cSBruce M Simpson ++ncsrcs; 16518f002c6cSBruce M Simpson if (tss != NULL && nsrcs > 0) { 16528f002c6cSBruce M Simpson psin = (struct sockaddr_in6 *)ptss; 16538f002c6cSBruce M Simpson psin->sin6_family = AF_INET6; 16548f002c6cSBruce M Simpson psin->sin6_len = sizeof(struct sockaddr_in6); 16558f002c6cSBruce M Simpson psin->sin6_addr = lims->im6s_addr; 16568f002c6cSBruce M Simpson psin->sin6_port = 0; 16578f002c6cSBruce M Simpson --nsrcs; 16588f002c6cSBruce M Simpson ++ptss; 16598f002c6cSBruce M Simpson } 16608f002c6cSBruce M Simpson } 16618f002c6cSBruce M Simpson 16628f002c6cSBruce M Simpson INP_WUNLOCK(inp); 16638f002c6cSBruce M Simpson 16648f002c6cSBruce M Simpson if (tss != NULL) { 16658f002c6cSBruce M Simpson error = copyout(tss, msfr.msfr_srcs, 16668f002c6cSBruce M Simpson sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs); 16678f002c6cSBruce M Simpson free(tss, M_TEMP); 16688f002c6cSBruce M Simpson if (error) 16698f002c6cSBruce M Simpson return (error); 16708f002c6cSBruce M Simpson } 16718f002c6cSBruce M Simpson 16728f002c6cSBruce M Simpson msfr.msfr_nsrcs = ncsrcs; 16738f002c6cSBruce M Simpson error = sooptcopyout(sopt, &msfr, sizeof(struct __msfilterreq)); 16748f002c6cSBruce M Simpson 16758f002c6cSBruce M Simpson return (error); 16768f002c6cSBruce M Simpson } 16778f002c6cSBruce M Simpson 16788f002c6cSBruce M Simpson /* 16798f002c6cSBruce M Simpson * Return the IP multicast options in response to user getsockopt(). 16808f002c6cSBruce M Simpson */ 16818f002c6cSBruce M Simpson int 16828f002c6cSBruce M Simpson ip6_getmoptions(struct inpcb *inp, struct sockopt *sopt) 16838f002c6cSBruce M Simpson { 168433cde130SBruce M Simpson struct ip6_moptions *im6o; 168533cde130SBruce M Simpson int error; 168633cde130SBruce M Simpson u_int optval; 16878f002c6cSBruce M Simpson 16888f002c6cSBruce M Simpson INP_WLOCK(inp); 168933cde130SBruce M Simpson im6o = inp->in6p_moptions; 16908f002c6cSBruce M Simpson /* 16918f002c6cSBruce M Simpson * If socket is neither of type SOCK_RAW or SOCK_DGRAM, 16928f002c6cSBruce M Simpson * or is a divert socket, reject it. 16938f002c6cSBruce M Simpson */ 16948f002c6cSBruce M Simpson if (inp->inp_socket->so_proto->pr_protocol == IPPROTO_DIVERT || 16958f002c6cSBruce M Simpson (inp->inp_socket->so_proto->pr_type != SOCK_RAW && 16968f002c6cSBruce M Simpson inp->inp_socket->so_proto->pr_type != SOCK_DGRAM)) { 16978f002c6cSBruce M Simpson INP_WUNLOCK(inp); 16988f002c6cSBruce M Simpson return (EOPNOTSUPP); 16998f002c6cSBruce M Simpson } 17008f002c6cSBruce M Simpson 17018f002c6cSBruce M Simpson error = 0; 17028f002c6cSBruce M Simpson switch (sopt->sopt_name) { 17038f002c6cSBruce M Simpson case IPV6_MULTICAST_IF: 170433cde130SBruce M Simpson if (im6o == NULL || im6o->im6o_multicast_ifp == NULL) { 17058f002c6cSBruce M Simpson optval = 0; 17068f002c6cSBruce M Simpson } else { 170733cde130SBruce M Simpson optval = im6o->im6o_multicast_ifp->if_index; 17088f002c6cSBruce M Simpson } 17098f002c6cSBruce M Simpson INP_WUNLOCK(inp); 171033cde130SBruce M Simpson error = sooptcopyout(sopt, &optval, sizeof(u_int)); 17118f002c6cSBruce M Simpson break; 17128f002c6cSBruce M Simpson 17138f002c6cSBruce M Simpson case IPV6_MULTICAST_HOPS: 171433cde130SBruce M Simpson if (im6o == NULL) 171533cde130SBruce M Simpson optval = V_ip6_defmcasthlim; 17168f002c6cSBruce M Simpson else 171708907004SBjoern A. Zeeb optval = im6o->im6o_multicast_hlim; 17188f002c6cSBruce M Simpson INP_WUNLOCK(inp); 17198f002c6cSBruce M Simpson error = sooptcopyout(sopt, &optval, sizeof(u_int)); 17208f002c6cSBruce M Simpson break; 17218f002c6cSBruce M Simpson 17228f002c6cSBruce M Simpson case IPV6_MULTICAST_LOOP: 172333cde130SBruce M Simpson if (im6o == NULL) 172433cde130SBruce M Simpson optval = in6_mcast_loop; /* XXX VIMAGE */ 17258f002c6cSBruce M Simpson else 172633cde130SBruce M Simpson optval = im6o->im6o_multicast_loop; 17278f002c6cSBruce M Simpson INP_WUNLOCK(inp); 17288f002c6cSBruce M Simpson error = sooptcopyout(sopt, &optval, sizeof(u_int)); 17298f002c6cSBruce M Simpson break; 17308f002c6cSBruce M Simpson 17318f002c6cSBruce M Simpson case IPV6_MSFILTER: 173233cde130SBruce M Simpson if (im6o == NULL) { 17338f002c6cSBruce M Simpson error = EADDRNOTAVAIL; 17348f002c6cSBruce M Simpson INP_WUNLOCK(inp); 17358f002c6cSBruce M Simpson } else { 17368f002c6cSBruce M Simpson error = in6p_get_source_filters(inp, sopt); 17378f002c6cSBruce M Simpson } 17388f002c6cSBruce M Simpson break; 17398f002c6cSBruce M Simpson 17408f002c6cSBruce M Simpson default: 17418f002c6cSBruce M Simpson INP_WUNLOCK(inp); 17428f002c6cSBruce M Simpson error = ENOPROTOOPT; 17438f002c6cSBruce M Simpson break; 17448f002c6cSBruce M Simpson } 17458f002c6cSBruce M Simpson 17468f002c6cSBruce M Simpson INP_UNLOCK_ASSERT(inp); 17478f002c6cSBruce M Simpson 17488f002c6cSBruce M Simpson return (error); 17498f002c6cSBruce M Simpson } 17508f002c6cSBruce M Simpson 17518f002c6cSBruce M Simpson /* 175233cde130SBruce M Simpson * Look up the ifnet to use for a multicast group membership, 175333cde130SBruce M Simpson * given the address of an IPv6 group. 175433cde130SBruce M Simpson * 175533cde130SBruce M Simpson * This routine exists to support legacy IPv6 multicast applications. 175633cde130SBruce M Simpson * 175733cde130SBruce M Simpson * If inp is non-NULL, use this socket's current FIB number for any 175833cde130SBruce M Simpson * required FIB lookup. Look up the group address in the unicast FIB, 175933cde130SBruce M Simpson * and use its ifp; usually, this points to the default next-hop. 176033cde130SBruce M Simpson * If the FIB lookup fails, return NULL. 176133cde130SBruce M Simpson * 176233cde130SBruce M Simpson * FUTURE: Support multiple forwarding tables for IPv6. 176333cde130SBruce M Simpson * 176433cde130SBruce M Simpson * Returns NULL if no ifp could be found. 176533cde130SBruce M Simpson */ 176633cde130SBruce M Simpson static struct ifnet * 176733cde130SBruce M Simpson in6p_lookup_mcast_ifp(const struct inpcb *in6p __unused, 176833cde130SBruce M Simpson const struct sockaddr_in6 *gsin6) 176933cde130SBruce M Simpson { 177033cde130SBruce M Simpson struct route_in6 ro6; 177133cde130SBruce M Simpson struct ifnet *ifp; 177233cde130SBruce M Simpson 177333cde130SBruce M Simpson KASSERT(in6p->inp_vflag & INP_IPV6, 177433cde130SBruce M Simpson ("%s: not INP_IPV6 inpcb", __func__)); 177533cde130SBruce M Simpson KASSERT(gsin6->sin6_family == AF_INET6, 177633cde130SBruce M Simpson ("%s: not AF_INET6 group", __func__)); 177733cde130SBruce M Simpson KASSERT(IN6_IS_ADDR_MULTICAST(&gsin6->sin6_addr), 177833cde130SBruce M Simpson ("%s: not multicast", __func__)); 177933cde130SBruce M Simpson 178033cde130SBruce M Simpson ifp = NULL; 178133cde130SBruce M Simpson memset(&ro6, 0, sizeof(struct route_in6)); 178233cde130SBruce M Simpson memcpy(&ro6.ro_dst, gsin6, sizeof(struct sockaddr_in6)); 178333cde130SBruce M Simpson #ifdef notyet 178433cde130SBruce M Simpson rtalloc_ign_fib(&ro6, 0, inp ? inp->inp_inc.inc_fibnum : 0); 178533cde130SBruce M Simpson #else 178633cde130SBruce M Simpson rtalloc_ign((struct route *)&ro6, 0); 178733cde130SBruce M Simpson #endif 178833cde130SBruce M Simpson if (ro6.ro_rt != NULL) { 178933cde130SBruce M Simpson ifp = ro6.ro_rt->rt_ifp; 179033cde130SBruce M Simpson KASSERT(ifp != NULL, ("%s: null ifp", __func__)); 179133cde130SBruce M Simpson RTFREE(ro6.ro_rt); 179233cde130SBruce M Simpson } 179333cde130SBruce M Simpson 179433cde130SBruce M Simpson return (ifp); 179533cde130SBruce M Simpson } 179633cde130SBruce M Simpson 179733cde130SBruce M Simpson /* 17988f002c6cSBruce M Simpson * Join an IPv6 multicast group, possibly with a source. 179933cde130SBruce M Simpson * 180033cde130SBruce M Simpson * FIXME: The KAME use of the unspecified address (::) 180133cde130SBruce M Simpson * to join *all* multicast groups is currently unsupported. 18028f002c6cSBruce M Simpson */ 18038f002c6cSBruce M Simpson static int 18048f002c6cSBruce M Simpson in6p_join_group(struct inpcb *inp, struct sockopt *sopt) 18058f002c6cSBruce M Simpson { 18068f002c6cSBruce M Simpson struct group_source_req gsr; 18078f002c6cSBruce M Simpson sockunion_t *gsa, *ssa; 18088f002c6cSBruce M Simpson struct ifnet *ifp; 18098f002c6cSBruce M Simpson struct in6_mfilter *imf; 18108f002c6cSBruce M Simpson struct ip6_moptions *imo; 18118f002c6cSBruce M Simpson struct in6_multi *inm; 18128f002c6cSBruce M Simpson struct in6_msource *lims; 18138f002c6cSBruce M Simpson size_t idx; 18148f002c6cSBruce M Simpson int error, is_new; 18158f002c6cSBruce M Simpson 18168f002c6cSBruce M Simpson ifp = NULL; 18178f002c6cSBruce M Simpson imf = NULL; 18181f81c2b6SBruce M Simpson lims = NULL; 18198f002c6cSBruce M Simpson error = 0; 18208f002c6cSBruce M Simpson is_new = 0; 18218f002c6cSBruce M Simpson 18228f002c6cSBruce M Simpson memset(&gsr, 0, sizeof(struct group_source_req)); 18238f002c6cSBruce M Simpson gsa = (sockunion_t *)&gsr.gsr_group; 18248f002c6cSBruce M Simpson gsa->ss.ss_family = AF_UNSPEC; 18258f002c6cSBruce M Simpson ssa = (sockunion_t *)&gsr.gsr_source; 18268f002c6cSBruce M Simpson ssa->ss.ss_family = AF_UNSPEC; 18278f002c6cSBruce M Simpson 182829dc7bc6SBruce M Simpson /* 182929dc7bc6SBruce M Simpson * Chew everything into struct group_source_req. 183029dc7bc6SBruce M Simpson * Overwrite the port field if present, as the sockaddr 183129dc7bc6SBruce M Simpson * being copied in may be matched with a binary comparison. 183229dc7bc6SBruce M Simpson * Ignore passed-in scope ID. 183329dc7bc6SBruce M Simpson */ 18348f002c6cSBruce M Simpson switch (sopt->sopt_name) { 18358f002c6cSBruce M Simpson case IPV6_JOIN_GROUP: { 18368f002c6cSBruce M Simpson struct ipv6_mreq mreq; 18378f002c6cSBruce M Simpson 18388f002c6cSBruce M Simpson error = sooptcopyin(sopt, &mreq, sizeof(struct ipv6_mreq), 18398f002c6cSBruce M Simpson sizeof(struct ipv6_mreq)); 18408f002c6cSBruce M Simpson if (error) 18418f002c6cSBruce M Simpson return (error); 18428f002c6cSBruce M Simpson 18438f002c6cSBruce M Simpson gsa->sin6.sin6_family = AF_INET6; 18448f002c6cSBruce M Simpson gsa->sin6.sin6_len = sizeof(struct sockaddr_in6); 18458f002c6cSBruce M Simpson gsa->sin6.sin6_addr = mreq.ipv6mr_multiaddr; 18468f002c6cSBruce M Simpson 184733cde130SBruce M Simpson if (mreq.ipv6mr_interface == 0) { 184833cde130SBruce M Simpson ifp = in6p_lookup_mcast_ifp(inp, &gsa->sin6); 184933cde130SBruce M Simpson } else { 185033cde130SBruce M Simpson if (mreq.ipv6mr_interface < 0 || 185133cde130SBruce M Simpson V_if_index < mreq.ipv6mr_interface) 185233cde130SBruce M Simpson return (EADDRNOTAVAIL); 18538f002c6cSBruce M Simpson ifp = ifnet_byindex(mreq.ipv6mr_interface); 185433cde130SBruce M Simpson } 18558f002c6cSBruce M Simpson CTR3(KTR_MLD, "%s: ipv6mr_interface = %d, ifp = %p", 18568f002c6cSBruce M Simpson __func__, mreq.ipv6mr_interface, ifp); 18578f002c6cSBruce M Simpson } break; 18588f002c6cSBruce M Simpson 18598f002c6cSBruce M Simpson case MCAST_JOIN_GROUP: 18608f002c6cSBruce M Simpson case MCAST_JOIN_SOURCE_GROUP: 18618f002c6cSBruce M Simpson if (sopt->sopt_name == MCAST_JOIN_GROUP) { 18628f002c6cSBruce M Simpson error = sooptcopyin(sopt, &gsr, 18638f002c6cSBruce M Simpson sizeof(struct group_req), 18648f002c6cSBruce M Simpson sizeof(struct group_req)); 18658f002c6cSBruce M Simpson } else if (sopt->sopt_name == MCAST_JOIN_SOURCE_GROUP) { 18668f002c6cSBruce M Simpson error = sooptcopyin(sopt, &gsr, 18678f002c6cSBruce M Simpson sizeof(struct group_source_req), 18688f002c6cSBruce M Simpson sizeof(struct group_source_req)); 18698f002c6cSBruce M Simpson } 18708f002c6cSBruce M Simpson if (error) 18718f002c6cSBruce M Simpson return (error); 18728f002c6cSBruce M Simpson 18738f002c6cSBruce M Simpson if (gsa->sin6.sin6_family != AF_INET6 || 18748f002c6cSBruce M Simpson gsa->sin6.sin6_len != sizeof(struct sockaddr_in6)) 18758f002c6cSBruce M Simpson return (EINVAL); 18768f002c6cSBruce M Simpson 18778f002c6cSBruce M Simpson if (sopt->sopt_name == MCAST_JOIN_SOURCE_GROUP) { 18788f002c6cSBruce M Simpson if (ssa->sin6.sin6_family != AF_INET6 || 18798f002c6cSBruce M Simpson ssa->sin6.sin6_len != sizeof(struct sockaddr_in6)) 18808f002c6cSBruce M Simpson return (EINVAL); 188129dc7bc6SBruce M Simpson if (IN6_IS_ADDR_MULTICAST(&ssa->sin6.sin6_addr)) 188229dc7bc6SBruce M Simpson return (EINVAL); 188329dc7bc6SBruce M Simpson /* 188429dc7bc6SBruce M Simpson * TODO: Validate embedded scope ID in source 188529dc7bc6SBruce M Simpson * list entry against passed-in ifp, if and only 188629dc7bc6SBruce M Simpson * if source list filter entry is iface or node local. 188729dc7bc6SBruce M Simpson */ 188829dc7bc6SBruce M Simpson in6_clearscope(&ssa->sin6.sin6_addr); 18898f002c6cSBruce M Simpson ssa->sin6.sin6_port = 0; 189029dc7bc6SBruce M Simpson ssa->sin6.sin6_scope_id = 0; 18918f002c6cSBruce M Simpson } 18928f002c6cSBruce M Simpson 18938f002c6cSBruce M Simpson if (gsr.gsr_interface == 0 || V_if_index < gsr.gsr_interface) 18948f002c6cSBruce M Simpson return (EADDRNOTAVAIL); 18958f002c6cSBruce M Simpson ifp = ifnet_byindex(gsr.gsr_interface); 18968f002c6cSBruce M Simpson break; 18978f002c6cSBruce M Simpson 18988f002c6cSBruce M Simpson default: 18998f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: unknown sopt_name %d", 19008f002c6cSBruce M Simpson __func__, sopt->sopt_name); 19018f002c6cSBruce M Simpson return (EOPNOTSUPP); 19028f002c6cSBruce M Simpson break; 19038f002c6cSBruce M Simpson } 19048f002c6cSBruce M Simpson 19058f002c6cSBruce M Simpson if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr)) 19068f002c6cSBruce M Simpson return (EINVAL); 19078f002c6cSBruce M Simpson 19088f002c6cSBruce M Simpson if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) 19098f002c6cSBruce M Simpson return (EADDRNOTAVAIL); 19108f002c6cSBruce M Simpson 191129dc7bc6SBruce M Simpson gsa->sin6.sin6_port = 0; 191229dc7bc6SBruce M Simpson gsa->sin6.sin6_scope_id = 0; 191329dc7bc6SBruce M Simpson 191433cde130SBruce M Simpson /* 191529dc7bc6SBruce M Simpson * Always set the scope zone ID on memberships created from userland. 191629dc7bc6SBruce M Simpson * Use the passed-in ifp to do this. 191729dc7bc6SBruce M Simpson * XXX The in6_setscope() return value is meaningless. 191829dc7bc6SBruce M Simpson * XXX SCOPE6_LOCK() is taken by in6_setscope(). 191933cde130SBruce M Simpson */ 192029dc7bc6SBruce M Simpson (void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL); 192133cde130SBruce M Simpson 19228f002c6cSBruce M Simpson imo = in6p_findmoptions(inp); 19238f002c6cSBruce M Simpson idx = im6o_match_group(imo, ifp, &gsa->sa); 19248f002c6cSBruce M Simpson if (idx == -1) { 19258f002c6cSBruce M Simpson is_new = 1; 19268f002c6cSBruce M Simpson } else { 19278f002c6cSBruce M Simpson inm = imo->im6o_membership[idx]; 19288f002c6cSBruce M Simpson imf = &imo->im6o_mfilters[idx]; 19291ee6b058SBruce M Simpson if (ssa->ss.ss_family != AF_UNSPEC) { 19301ee6b058SBruce M Simpson /* 19311ee6b058SBruce M Simpson * MCAST_JOIN_SOURCE_GROUP on an exclusive membership 19321ee6b058SBruce M Simpson * is an error. On an existing inclusive membership, 19331ee6b058SBruce M Simpson * it just adds the source to the filter list. 19341ee6b058SBruce M Simpson */ 19351ee6b058SBruce M Simpson if (imf->im6f_st[1] != MCAST_INCLUDE) { 19368f002c6cSBruce M Simpson error = EINVAL; 19378f002c6cSBruce M Simpson goto out_in6p_locked; 19388f002c6cSBruce M Simpson } 19391f81c2b6SBruce M Simpson /* 19401f81c2b6SBruce M Simpson * Throw out duplicates. 19411f81c2b6SBruce M Simpson * 19421f81c2b6SBruce M Simpson * XXX FIXME: This makes a naive assumption that 19431f81c2b6SBruce M Simpson * even if entries exist for *ssa in this imf, 19441f81c2b6SBruce M Simpson * they will be rejected as dupes, even if they 19451f81c2b6SBruce M Simpson * are not valid in the current mode (in-mode). 19461f81c2b6SBruce M Simpson * 19471f81c2b6SBruce M Simpson * in6_msource is transactioned just as for anything 19481f81c2b6SBruce M Simpson * else in SSM -- but note naive use of in6m_graft() 19491f81c2b6SBruce M Simpson * below for allocating new filter entries. 19501f81c2b6SBruce M Simpson * 19511f81c2b6SBruce M Simpson * This is only an issue if someone mixes the 19521f81c2b6SBruce M Simpson * full-state SSM API with the delta-based API, 19531f81c2b6SBruce M Simpson * which is discouraged in the relevant RFCs. 19541f81c2b6SBruce M Simpson */ 19558f002c6cSBruce M Simpson lims = im6o_match_source(imo, idx, &ssa->sa); 19561f81c2b6SBruce M Simpson if (lims != NULL /*&& 19571f81c2b6SBruce M Simpson lims->im6sl_st[1] == MCAST_INCLUDE*/) { 19588f002c6cSBruce M Simpson error = EADDRNOTAVAIL; 19598f002c6cSBruce M Simpson goto out_in6p_locked; 19608f002c6cSBruce M Simpson } 19611ee6b058SBruce M Simpson } else { 19621ee6b058SBruce M Simpson /* 1963604a60d1SBruce M Simpson * MCAST_JOIN_GROUP alone, on any existing membership, 1964604a60d1SBruce M Simpson * is rejected, to stop the same inpcb tying up 1965604a60d1SBruce M Simpson * multiple refs to the in_multi. 1966604a60d1SBruce M Simpson * On an existing inclusive membership, this is also 1967604a60d1SBruce M Simpson * an error; if you want to change filter mode, 1968604a60d1SBruce M Simpson * you must use the userland API setsourcefilter(). 1969604a60d1SBruce M Simpson * XXX We don't reject this for imf in UNDEFINED 1970604a60d1SBruce M Simpson * state at t1, because allocation of a filter 1971604a60d1SBruce M Simpson * is atomic with allocation of a membership. 19721ee6b058SBruce M Simpson */ 19731ee6b058SBruce M Simpson error = EINVAL; 19741ee6b058SBruce M Simpson goto out_in6p_locked; 19751ee6b058SBruce M Simpson } 19761ee6b058SBruce M Simpson } 19778f002c6cSBruce M Simpson 19788f002c6cSBruce M Simpson /* 19798f002c6cSBruce M Simpson * Begin state merge transaction at socket layer. 19808f002c6cSBruce M Simpson */ 19818f002c6cSBruce M Simpson INP_WLOCK_ASSERT(inp); 19828f002c6cSBruce M Simpson 19838f002c6cSBruce M Simpson if (is_new) { 19848f002c6cSBruce M Simpson if (imo->im6o_num_memberships == imo->im6o_max_memberships) { 19858f002c6cSBruce M Simpson error = im6o_grow(imo); 19868f002c6cSBruce M Simpson if (error) 19878f002c6cSBruce M Simpson goto out_in6p_locked; 19888f002c6cSBruce M Simpson } 19898f002c6cSBruce M Simpson /* 19908f002c6cSBruce M Simpson * Allocate the new slot upfront so we can deal with 19918f002c6cSBruce M Simpson * grafting the new source filter in same code path 19928f002c6cSBruce M Simpson * as for join-source on existing membership. 19938f002c6cSBruce M Simpson */ 19948f002c6cSBruce M Simpson idx = imo->im6o_num_memberships; 19958f002c6cSBruce M Simpson imo->im6o_membership[idx] = NULL; 19968f002c6cSBruce M Simpson imo->im6o_num_memberships++; 19978f002c6cSBruce M Simpson KASSERT(imo->im6o_mfilters != NULL, 19988f002c6cSBruce M Simpson ("%s: im6f_mfilters vector was not allocated", __func__)); 19998f002c6cSBruce M Simpson imf = &imo->im6o_mfilters[idx]; 20008f002c6cSBruce M Simpson KASSERT(RB_EMPTY(&imf->im6f_sources), 20018f002c6cSBruce M Simpson ("%s: im6f_sources not empty", __func__)); 20028f002c6cSBruce M Simpson } 20038f002c6cSBruce M Simpson 20048f002c6cSBruce M Simpson /* 20058f002c6cSBruce M Simpson * Graft new source into filter list for this inpcb's 20068f002c6cSBruce M Simpson * membership of the group. The in6_multi may not have 20071ee6b058SBruce M Simpson * been allocated yet if this is a new membership, however, 20081ee6b058SBruce M Simpson * the in_mfilter slot will be allocated and must be initialized. 2009604a60d1SBruce M Simpson * 2010604a60d1SBruce M Simpson * Note: Grafting of exclusive mode filters doesn't happen 2011604a60d1SBruce M Simpson * in this path. 20121f81c2b6SBruce M Simpson * XXX: Should check for non-NULL lims (node exists but may 20131f81c2b6SBruce M Simpson * not be in-mode) for interop with full-state API. 20148f002c6cSBruce M Simpson */ 20158f002c6cSBruce M Simpson if (ssa->ss.ss_family != AF_UNSPEC) { 20168f002c6cSBruce M Simpson /* Membership starts in IN mode */ 20178f002c6cSBruce M Simpson if (is_new) { 20188f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: new join w/source", __func__); 20198f002c6cSBruce M Simpson im6f_init(imf, MCAST_UNDEFINED, MCAST_INCLUDE); 20208f002c6cSBruce M Simpson } else { 20218f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: %s source", __func__, "allow"); 20228f002c6cSBruce M Simpson } 20238f002c6cSBruce M Simpson lims = im6f_graft(imf, MCAST_INCLUDE, &ssa->sin6); 20248f002c6cSBruce M Simpson if (lims == NULL) { 20258f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: merge imf state failed", 20268f002c6cSBruce M Simpson __func__); 20278f002c6cSBruce M Simpson error = ENOMEM; 20288f002c6cSBruce M Simpson goto out_im6o_free; 20298f002c6cSBruce M Simpson } 20301ee6b058SBruce M Simpson } else { 20311ee6b058SBruce M Simpson /* No address specified; Membership starts in EX mode */ 20321ee6b058SBruce M Simpson if (is_new) { 20331ee6b058SBruce M Simpson CTR1(KTR_MLD, "%s: new join w/o source", __func__); 20341ee6b058SBruce M Simpson im6f_init(imf, MCAST_UNDEFINED, MCAST_EXCLUDE); 20351ee6b058SBruce M Simpson } 20368f002c6cSBruce M Simpson } 20378f002c6cSBruce M Simpson 20388f002c6cSBruce M Simpson /* 20398f002c6cSBruce M Simpson * Begin state merge transaction at MLD layer. 20408f002c6cSBruce M Simpson */ 20418f002c6cSBruce M Simpson IN6_MULTI_LOCK(); 20428f002c6cSBruce M Simpson 20438f002c6cSBruce M Simpson if (is_new) { 20448f002c6cSBruce M Simpson error = in6_mc_join_locked(ifp, &gsa->sin6.sin6_addr, imf, 20458f002c6cSBruce M Simpson &inm, 0); 20468f002c6cSBruce M Simpson if (error) 20478f002c6cSBruce M Simpson goto out_im6o_free; 20488f002c6cSBruce M Simpson imo->im6o_membership[idx] = inm; 20498f002c6cSBruce M Simpson } else { 20508f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: merge inm state", __func__); 20518f002c6cSBruce M Simpson error = in6m_merge(inm, imf); 20528f002c6cSBruce M Simpson if (error) { 20538f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: failed to merge inm state", 20548f002c6cSBruce M Simpson __func__); 20558f002c6cSBruce M Simpson goto out_im6f_rollback; 20568f002c6cSBruce M Simpson } 20578f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: doing mld downcall", __func__); 20588f002c6cSBruce M Simpson error = mld_change_state(inm, 0); 20598f002c6cSBruce M Simpson if (error) { 20608f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: failed mld downcall", 20618f002c6cSBruce M Simpson __func__); 20628f002c6cSBruce M Simpson goto out_im6f_rollback; 20638f002c6cSBruce M Simpson } 20648f002c6cSBruce M Simpson } 20658f002c6cSBruce M Simpson 20668f002c6cSBruce M Simpson IN6_MULTI_UNLOCK(); 20678f002c6cSBruce M Simpson 20688f002c6cSBruce M Simpson out_im6f_rollback: 20698f002c6cSBruce M Simpson INP_WLOCK_ASSERT(inp); 20708f002c6cSBruce M Simpson if (error) { 20718f002c6cSBruce M Simpson im6f_rollback(imf); 20728f002c6cSBruce M Simpson if (is_new) 20738f002c6cSBruce M Simpson im6f_purge(imf); 20748f002c6cSBruce M Simpson else 20758f002c6cSBruce M Simpson im6f_reap(imf); 20768f002c6cSBruce M Simpson } else { 20778f002c6cSBruce M Simpson im6f_commit(imf); 20788f002c6cSBruce M Simpson } 20798f002c6cSBruce M Simpson 20808f002c6cSBruce M Simpson out_im6o_free: 20818f002c6cSBruce M Simpson if (error && is_new) { 20828f002c6cSBruce M Simpson imo->im6o_membership[idx] = NULL; 20838f002c6cSBruce M Simpson --imo->im6o_num_memberships; 20848f002c6cSBruce M Simpson } 20858f002c6cSBruce M Simpson 20868f002c6cSBruce M Simpson out_in6p_locked: 20878f002c6cSBruce M Simpson INP_WUNLOCK(inp); 20888f002c6cSBruce M Simpson return (error); 20898f002c6cSBruce M Simpson } 20908f002c6cSBruce M Simpson 20918f002c6cSBruce M Simpson /* 20928f002c6cSBruce M Simpson * Leave an IPv6 multicast group on an inpcb, possibly with a source. 20938f002c6cSBruce M Simpson */ 20948f002c6cSBruce M Simpson static int 20958f002c6cSBruce M Simpson in6p_leave_group(struct inpcb *inp, struct sockopt *sopt) 20968f002c6cSBruce M Simpson { 209729dc7bc6SBruce M Simpson struct ipv6_mreq mreq; 20988f002c6cSBruce M Simpson struct group_source_req gsr; 20998f002c6cSBruce M Simpson sockunion_t *gsa, *ssa; 21008f002c6cSBruce M Simpson struct ifnet *ifp; 21018f002c6cSBruce M Simpson struct in6_mfilter *imf; 21028f002c6cSBruce M Simpson struct ip6_moptions *imo; 21038f002c6cSBruce M Simpson struct in6_msource *ims; 21048f002c6cSBruce M Simpson struct in6_multi *inm; 210529dc7bc6SBruce M Simpson uint32_t ifindex; 21068f002c6cSBruce M Simpson size_t idx; 21078f002c6cSBruce M Simpson int error, is_final; 21088f002c6cSBruce M Simpson #ifdef KTR 21098f002c6cSBruce M Simpson char ip6tbuf[INET6_ADDRSTRLEN]; 21108f002c6cSBruce M Simpson #endif 21118f002c6cSBruce M Simpson 21128f002c6cSBruce M Simpson ifp = NULL; 211329dc7bc6SBruce M Simpson ifindex = 0; 21148f002c6cSBruce M Simpson error = 0; 21158f002c6cSBruce M Simpson is_final = 1; 21168f002c6cSBruce M Simpson 21178f002c6cSBruce M Simpson memset(&gsr, 0, sizeof(struct group_source_req)); 21188f002c6cSBruce M Simpson gsa = (sockunion_t *)&gsr.gsr_group; 21198f002c6cSBruce M Simpson gsa->ss.ss_family = AF_UNSPEC; 21208f002c6cSBruce M Simpson ssa = (sockunion_t *)&gsr.gsr_source; 21218f002c6cSBruce M Simpson ssa->ss.ss_family = AF_UNSPEC; 21228f002c6cSBruce M Simpson 212329dc7bc6SBruce M Simpson /* 212429dc7bc6SBruce M Simpson * Chew everything passed in up into a struct group_source_req 212529dc7bc6SBruce M Simpson * as that is easier to process. 212629dc7bc6SBruce M Simpson * Note: Any embedded scope ID in the multicast group passed 212729dc7bc6SBruce M Simpson * in by userland is ignored, the interface index is the recommended 212829dc7bc6SBruce M Simpson * mechanism to specify an interface; see below. 212929dc7bc6SBruce M Simpson */ 21308f002c6cSBruce M Simpson switch (sopt->sopt_name) { 213129dc7bc6SBruce M Simpson case IPV6_LEAVE_GROUP: 21328f002c6cSBruce M Simpson error = sooptcopyin(sopt, &mreq, sizeof(struct ipv6_mreq), 21338f002c6cSBruce M Simpson sizeof(struct ipv6_mreq)); 21348f002c6cSBruce M Simpson if (error) 21358f002c6cSBruce M Simpson return (error); 21368f002c6cSBruce M Simpson gsa->sin6.sin6_family = AF_INET6; 21378f002c6cSBruce M Simpson gsa->sin6.sin6_len = sizeof(struct sockaddr_in6); 21388f002c6cSBruce M Simpson gsa->sin6.sin6_addr = mreq.ipv6mr_multiaddr; 213929dc7bc6SBruce M Simpson gsa->sin6.sin6_port = 0; 214029dc7bc6SBruce M Simpson gsa->sin6.sin6_scope_id = 0; 214129dc7bc6SBruce M Simpson ifindex = mreq.ipv6mr_interface; 214229dc7bc6SBruce M Simpson break; 21438f002c6cSBruce M Simpson 21448f002c6cSBruce M Simpson case MCAST_LEAVE_GROUP: 21458f002c6cSBruce M Simpson case MCAST_LEAVE_SOURCE_GROUP: 21468f002c6cSBruce M Simpson if (sopt->sopt_name == MCAST_LEAVE_GROUP) { 21478f002c6cSBruce M Simpson error = sooptcopyin(sopt, &gsr, 21488f002c6cSBruce M Simpson sizeof(struct group_req), 21498f002c6cSBruce M Simpson sizeof(struct group_req)); 21508f002c6cSBruce M Simpson } else if (sopt->sopt_name == MCAST_LEAVE_SOURCE_GROUP) { 21518f002c6cSBruce M Simpson error = sooptcopyin(sopt, &gsr, 21528f002c6cSBruce M Simpson sizeof(struct group_source_req), 21538f002c6cSBruce M Simpson sizeof(struct group_source_req)); 21548f002c6cSBruce M Simpson } 21558f002c6cSBruce M Simpson if (error) 21568f002c6cSBruce M Simpson return (error); 21578f002c6cSBruce M Simpson 21588f002c6cSBruce M Simpson if (gsa->sin6.sin6_family != AF_INET6 || 21598f002c6cSBruce M Simpson gsa->sin6.sin6_len != sizeof(struct sockaddr_in6)) 21608f002c6cSBruce M Simpson return (EINVAL); 21618f002c6cSBruce M Simpson if (sopt->sopt_name == MCAST_LEAVE_SOURCE_GROUP) { 21628f002c6cSBruce M Simpson if (ssa->sin6.sin6_family != AF_INET6 || 21638f002c6cSBruce M Simpson ssa->sin6.sin6_len != sizeof(struct sockaddr_in6)) 21648f002c6cSBruce M Simpson return (EINVAL); 216529dc7bc6SBruce M Simpson if (IN6_IS_ADDR_MULTICAST(&ssa->sin6.sin6_addr)) 216629dc7bc6SBruce M Simpson return (EINVAL); 216729dc7bc6SBruce M Simpson /* 216829dc7bc6SBruce M Simpson * TODO: Validate embedded scope ID in source 216929dc7bc6SBruce M Simpson * list entry against passed-in ifp, if and only 217029dc7bc6SBruce M Simpson * if source list filter entry is iface or node local. 217129dc7bc6SBruce M Simpson */ 217229dc7bc6SBruce M Simpson in6_clearscope(&ssa->sin6.sin6_addr); 21738f002c6cSBruce M Simpson } 217429dc7bc6SBruce M Simpson gsa->sin6.sin6_port = 0; 217529dc7bc6SBruce M Simpson gsa->sin6.sin6_scope_id = 0; 217629dc7bc6SBruce M Simpson ifindex = gsr.gsr_interface; 21778f002c6cSBruce M Simpson break; 21788f002c6cSBruce M Simpson 21798f002c6cSBruce M Simpson default: 21808f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: unknown sopt_name %d", 21818f002c6cSBruce M Simpson __func__, sopt->sopt_name); 21828f002c6cSBruce M Simpson return (EOPNOTSUPP); 21838f002c6cSBruce M Simpson break; 21848f002c6cSBruce M Simpson } 21858f002c6cSBruce M Simpson 21868f002c6cSBruce M Simpson if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr)) 21878f002c6cSBruce M Simpson return (EINVAL); 21888f002c6cSBruce M Simpson 218933cde130SBruce M Simpson /* 219029dc7bc6SBruce M Simpson * Validate interface index if provided. If no interface index 219129dc7bc6SBruce M Simpson * was provided separately, attempt to look the membership up 219229dc7bc6SBruce M Simpson * from the default scope as a last resort to disambiguate 219329dc7bc6SBruce M Simpson * the membership we are being asked to leave. 219429dc7bc6SBruce M Simpson * XXX SCOPE6 lock potentially taken here. 219533cde130SBruce M Simpson */ 219629dc7bc6SBruce M Simpson if (ifindex != 0) { 219729dc7bc6SBruce M Simpson if (ifindex < 0 || V_if_index < ifindex) 219829dc7bc6SBruce M Simpson return (EADDRNOTAVAIL); 219929dc7bc6SBruce M Simpson ifp = ifnet_byindex(ifindex); 220029dc7bc6SBruce M Simpson if (ifp == NULL) 220129dc7bc6SBruce M Simpson return (EADDRNOTAVAIL); 220229dc7bc6SBruce M Simpson (void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL); 220329dc7bc6SBruce M Simpson } else { 220429dc7bc6SBruce M Simpson error = sa6_embedscope(&gsa->sin6, V_ip6_use_defzone); 220529dc7bc6SBruce M Simpson if (error) 220629dc7bc6SBruce M Simpson return (EADDRNOTAVAIL); 220729dc7bc6SBruce M Simpson /* 2208b36c89e5SBruce M Simpson * Some badly behaved applications don't pass an ifindex 2209b36c89e5SBruce M Simpson * or a scope ID, which is an API violation. In this case, 2210b36c89e5SBruce M Simpson * perform a lookup as per a v6 join. 2211b36c89e5SBruce M Simpson * 221229dc7bc6SBruce M Simpson * XXX For now, stomp on zone ID for the corner case. 221329dc7bc6SBruce M Simpson * This is not the 'KAME way', but we need to see the ifp 221429dc7bc6SBruce M Simpson * directly until such time as this implementation is 221529dc7bc6SBruce M Simpson * refactored, assuming the scope IDs are the way to go. 221629dc7bc6SBruce M Simpson */ 221729dc7bc6SBruce M Simpson ifindex = ntohs(gsa->sin6.sin6_addr.s6_addr16[1]); 2218b36c89e5SBruce M Simpson if (ifindex == 0) { 2219b36c89e5SBruce M Simpson CTR2(KTR_MLD, "%s: warning: no ifindex, looking up " 2220b36c89e5SBruce M Simpson "ifp for group %s.", __func__, 2221b36c89e5SBruce M Simpson ip6_sprintf(ip6tbuf, &gsa->sin6.sin6_addr)); 2222b36c89e5SBruce M Simpson ifp = in6p_lookup_mcast_ifp(inp, &gsa->sin6); 2223b36c89e5SBruce M Simpson } else { 222429dc7bc6SBruce M Simpson ifp = ifnet_byindex(ifindex); 2225b36c89e5SBruce M Simpson } 222629dc7bc6SBruce M Simpson if (ifp == NULL) 222729dc7bc6SBruce M Simpson return (EADDRNOTAVAIL); 222829dc7bc6SBruce M Simpson } 222929dc7bc6SBruce M Simpson 223029dc7bc6SBruce M Simpson CTR2(KTR_MLD, "%s: ifp = %p", __func__, ifp); 223129dc7bc6SBruce M Simpson KASSERT(ifp != NULL, ("%s: ifp did not resolve", __func__)); 223233cde130SBruce M Simpson 22338f002c6cSBruce M Simpson /* 22348f002c6cSBruce M Simpson * Find the membership in the membership array. 22358f002c6cSBruce M Simpson */ 22368f002c6cSBruce M Simpson imo = in6p_findmoptions(inp); 22378f002c6cSBruce M Simpson idx = im6o_match_group(imo, ifp, &gsa->sa); 22388f002c6cSBruce M Simpson if (idx == -1) { 22398f002c6cSBruce M Simpson error = EADDRNOTAVAIL; 22408f002c6cSBruce M Simpson goto out_in6p_locked; 22418f002c6cSBruce M Simpson } 22428f002c6cSBruce M Simpson inm = imo->im6o_membership[idx]; 22438f002c6cSBruce M Simpson imf = &imo->im6o_mfilters[idx]; 22448f002c6cSBruce M Simpson 22458f002c6cSBruce M Simpson if (ssa->ss.ss_family != AF_UNSPEC) 22468f002c6cSBruce M Simpson is_final = 0; 22478f002c6cSBruce M Simpson 22488f002c6cSBruce M Simpson /* 22498f002c6cSBruce M Simpson * Begin state merge transaction at socket layer. 22508f002c6cSBruce M Simpson */ 22518f002c6cSBruce M Simpson INP_WLOCK_ASSERT(inp); 22528f002c6cSBruce M Simpson 22538f002c6cSBruce M Simpson /* 22548f002c6cSBruce M Simpson * If we were instructed only to leave a given source, do so. 22558f002c6cSBruce M Simpson * MCAST_LEAVE_SOURCE_GROUP is only valid for inclusive memberships. 22568f002c6cSBruce M Simpson */ 22578f002c6cSBruce M Simpson if (is_final) { 22588f002c6cSBruce M Simpson im6f_leave(imf); 22598f002c6cSBruce M Simpson } else { 22608f002c6cSBruce M Simpson if (imf->im6f_st[0] == MCAST_EXCLUDE) { 22618f002c6cSBruce M Simpson error = EADDRNOTAVAIL; 22628f002c6cSBruce M Simpson goto out_in6p_locked; 22638f002c6cSBruce M Simpson } 22648f002c6cSBruce M Simpson ims = im6o_match_source(imo, idx, &ssa->sa); 22658f002c6cSBruce M Simpson if (ims == NULL) { 22668f002c6cSBruce M Simpson CTR3(KTR_MLD, "%s: source %p %spresent", __func__, 22678f002c6cSBruce M Simpson ip6_sprintf(ip6tbuf, &ssa->sin6.sin6_addr), 22688f002c6cSBruce M Simpson "not "); 22698f002c6cSBruce M Simpson error = EADDRNOTAVAIL; 22708f002c6cSBruce M Simpson goto out_in6p_locked; 22718f002c6cSBruce M Simpson } 22728f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: %s source", __func__, "block"); 22738f002c6cSBruce M Simpson error = im6f_prune(imf, &ssa->sin6); 22748f002c6cSBruce M Simpson if (error) { 22758f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: merge imf state failed", 22768f002c6cSBruce M Simpson __func__); 22778f002c6cSBruce M Simpson goto out_in6p_locked; 22788f002c6cSBruce M Simpson } 22798f002c6cSBruce M Simpson } 22808f002c6cSBruce M Simpson 22818f002c6cSBruce M Simpson /* 22828f002c6cSBruce M Simpson * Begin state merge transaction at MLD layer. 22838f002c6cSBruce M Simpson */ 22848f002c6cSBruce M Simpson IN6_MULTI_LOCK(); 22858f002c6cSBruce M Simpson 22868f002c6cSBruce M Simpson if (is_final) { 22878f002c6cSBruce M Simpson /* 22888f002c6cSBruce M Simpson * Give up the multicast address record to which 22898f002c6cSBruce M Simpson * the membership points. 22908f002c6cSBruce M Simpson */ 22918f002c6cSBruce M Simpson (void)in6_mc_leave_locked(inm, imf); 22928f002c6cSBruce M Simpson } else { 22938f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: merge inm state", __func__); 22948f002c6cSBruce M Simpson error = in6m_merge(inm, imf); 22958f002c6cSBruce M Simpson if (error) { 22968f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: failed to merge inm state", 22978f002c6cSBruce M Simpson __func__); 22988f002c6cSBruce M Simpson goto out_im6f_rollback; 22998f002c6cSBruce M Simpson } 23008f002c6cSBruce M Simpson 23018f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: doing mld downcall", __func__); 23028f002c6cSBruce M Simpson error = mld_change_state(inm, 0); 23038f002c6cSBruce M Simpson if (error) { 23048f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: failed mld downcall", 23058f002c6cSBruce M Simpson __func__); 23068f002c6cSBruce M Simpson } 23078f002c6cSBruce M Simpson } 23088f002c6cSBruce M Simpson 23098f002c6cSBruce M Simpson IN6_MULTI_UNLOCK(); 23108f002c6cSBruce M Simpson 23118f002c6cSBruce M Simpson out_im6f_rollback: 23128f002c6cSBruce M Simpson if (error) 23138f002c6cSBruce M Simpson im6f_rollback(imf); 23148f002c6cSBruce M Simpson else 23158f002c6cSBruce M Simpson im6f_commit(imf); 23168f002c6cSBruce M Simpson 23178f002c6cSBruce M Simpson im6f_reap(imf); 23188f002c6cSBruce M Simpson 23198f002c6cSBruce M Simpson if (is_final) { 23208f002c6cSBruce M Simpson /* Remove the gap in the membership array. */ 232157a9feeaSBruce M Simpson for (++idx; idx < imo->im6o_num_memberships; ++idx) { 23228f002c6cSBruce M Simpson imo->im6o_membership[idx-1] = imo->im6o_membership[idx]; 232357a9feeaSBruce M Simpson imo->im6o_mfilters[idx-1] = imo->im6o_mfilters[idx]; 232457a9feeaSBruce M Simpson } 23258f002c6cSBruce M Simpson imo->im6o_num_memberships--; 23268f002c6cSBruce M Simpson } 23278f002c6cSBruce M Simpson 23288f002c6cSBruce M Simpson out_in6p_locked: 23298f002c6cSBruce M Simpson INP_WUNLOCK(inp); 23308f002c6cSBruce M Simpson return (error); 23318f002c6cSBruce M Simpson } 23328f002c6cSBruce M Simpson 23338f002c6cSBruce M Simpson /* 23348f002c6cSBruce M Simpson * Select the interface for transmitting IPv6 multicast datagrams. 23358f002c6cSBruce M Simpson * 23368f002c6cSBruce M Simpson * Either an instance of struct in6_addr or an instance of struct ipv6_mreqn 23378f002c6cSBruce M Simpson * may be passed to this socket option. An address of in6addr_any or an 23388f002c6cSBruce M Simpson * interface index of 0 is used to remove a previous selection. 23398f002c6cSBruce M Simpson * When no interface is selected, one is chosen for every send. 23408f002c6cSBruce M Simpson */ 23418f002c6cSBruce M Simpson static int 23428f002c6cSBruce M Simpson in6p_set_multicast_if(struct inpcb *inp, struct sockopt *sopt) 23438f002c6cSBruce M Simpson { 23448f002c6cSBruce M Simpson struct ifnet *ifp; 23458f002c6cSBruce M Simpson struct ip6_moptions *imo; 23468f002c6cSBruce M Simpson u_int ifindex; 23478f002c6cSBruce M Simpson int error; 23488f002c6cSBruce M Simpson 23498f002c6cSBruce M Simpson if (sopt->sopt_valsize != sizeof(u_int)) 23508f002c6cSBruce M Simpson return (EINVAL); 23518f002c6cSBruce M Simpson 23528f002c6cSBruce M Simpson error = sooptcopyin(sopt, &ifindex, sizeof(u_int), sizeof(u_int)); 23538f002c6cSBruce M Simpson if (error) 23548f002c6cSBruce M Simpson return (error); 23558f002c6cSBruce M Simpson if (ifindex < 0 || V_if_index < ifindex) 23568f002c6cSBruce M Simpson return (EINVAL); 23578f002c6cSBruce M Simpson 23588f002c6cSBruce M Simpson ifp = ifnet_byindex(ifindex); 23598f002c6cSBruce M Simpson if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) 23608f002c6cSBruce M Simpson return (EADDRNOTAVAIL); 23618f002c6cSBruce M Simpson 23628f002c6cSBruce M Simpson imo = in6p_findmoptions(inp); 23638f002c6cSBruce M Simpson imo->im6o_multicast_ifp = ifp; 23648f002c6cSBruce M Simpson INP_WUNLOCK(inp); 23658f002c6cSBruce M Simpson 23668f002c6cSBruce M Simpson return (0); 23678f002c6cSBruce M Simpson } 23688f002c6cSBruce M Simpson 23698f002c6cSBruce M Simpson /* 23708f002c6cSBruce M Simpson * Atomically set source filters on a socket for an IPv6 multicast group. 23718f002c6cSBruce M Simpson * 23728f002c6cSBruce M Simpson * SMPng: NOTE: Potentially calls malloc(M_WAITOK) with Giant held. 23738f002c6cSBruce M Simpson */ 23748f002c6cSBruce M Simpson static int 23758f002c6cSBruce M Simpson in6p_set_source_filters(struct inpcb *inp, struct sockopt *sopt) 23768f002c6cSBruce M Simpson { 23778f002c6cSBruce M Simpson struct __msfilterreq msfr; 23788f002c6cSBruce M Simpson sockunion_t *gsa; 23798f002c6cSBruce M Simpson struct ifnet *ifp; 23808f002c6cSBruce M Simpson struct in6_mfilter *imf; 23818f002c6cSBruce M Simpson struct ip6_moptions *imo; 23828f002c6cSBruce M Simpson struct in6_multi *inm; 23838f002c6cSBruce M Simpson size_t idx; 23848f002c6cSBruce M Simpson int error; 23858f002c6cSBruce M Simpson 23868f002c6cSBruce M Simpson error = sooptcopyin(sopt, &msfr, sizeof(struct __msfilterreq), 23878f002c6cSBruce M Simpson sizeof(struct __msfilterreq)); 23888f002c6cSBruce M Simpson if (error) 23898f002c6cSBruce M Simpson return (error); 23908f002c6cSBruce M Simpson 23910dc5893eSBruce M Simpson if (msfr.msfr_nsrcs > in6_mcast_maxsocksrc) 23920dc5893eSBruce M Simpson return (ENOBUFS); 23930dc5893eSBruce M Simpson 23940dc5893eSBruce M Simpson if (msfr.msfr_fmode != MCAST_EXCLUDE && 23950dc5893eSBruce M Simpson msfr.msfr_fmode != MCAST_INCLUDE) 23968f002c6cSBruce M Simpson return (EINVAL); 23978f002c6cSBruce M Simpson 23988f002c6cSBruce M Simpson if (msfr.msfr_group.ss_family != AF_INET6 || 23998f002c6cSBruce M Simpson msfr.msfr_group.ss_len != sizeof(struct sockaddr_in6)) 24008f002c6cSBruce M Simpson return (EINVAL); 24018f002c6cSBruce M Simpson 24028f002c6cSBruce M Simpson gsa = (sockunion_t *)&msfr.msfr_group; 24038f002c6cSBruce M Simpson if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr)) 24048f002c6cSBruce M Simpson return (EINVAL); 24058f002c6cSBruce M Simpson 24068f002c6cSBruce M Simpson gsa->sin6.sin6_port = 0; /* ignore port */ 24078f002c6cSBruce M Simpson 24088f002c6cSBruce M Simpson if (msfr.msfr_ifindex == 0 || V_if_index < msfr.msfr_ifindex) 24098f002c6cSBruce M Simpson return (EADDRNOTAVAIL); 24108f002c6cSBruce M Simpson ifp = ifnet_byindex(msfr.msfr_ifindex); 24118f002c6cSBruce M Simpson if (ifp == NULL) 24128f002c6cSBruce M Simpson return (EADDRNOTAVAIL); 241329dc7bc6SBruce M Simpson (void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL); 24148f002c6cSBruce M Simpson 24158f002c6cSBruce M Simpson /* 24168f002c6cSBruce M Simpson * Take the INP write lock. 24178f002c6cSBruce M Simpson * Check if this socket is a member of this group. 24188f002c6cSBruce M Simpson */ 24198f002c6cSBruce M Simpson imo = in6p_findmoptions(inp); 24208f002c6cSBruce M Simpson idx = im6o_match_group(imo, ifp, &gsa->sa); 24218f002c6cSBruce M Simpson if (idx == -1 || imo->im6o_mfilters == NULL) { 24228f002c6cSBruce M Simpson error = EADDRNOTAVAIL; 24238f002c6cSBruce M Simpson goto out_in6p_locked; 24248f002c6cSBruce M Simpson } 24258f002c6cSBruce M Simpson inm = imo->im6o_membership[idx]; 24268f002c6cSBruce M Simpson imf = &imo->im6o_mfilters[idx]; 24278f002c6cSBruce M Simpson 24288f002c6cSBruce M Simpson /* 24298f002c6cSBruce M Simpson * Begin state merge transaction at socket layer. 24308f002c6cSBruce M Simpson */ 24318f002c6cSBruce M Simpson INP_WLOCK_ASSERT(inp); 24328f002c6cSBruce M Simpson 24338f002c6cSBruce M Simpson imf->im6f_st[1] = msfr.msfr_fmode; 24348f002c6cSBruce M Simpson 24358f002c6cSBruce M Simpson /* 24368f002c6cSBruce M Simpson * Apply any new source filters, if present. 24378f002c6cSBruce M Simpson * Make a copy of the user-space source vector so 24388f002c6cSBruce M Simpson * that we may copy them with a single copyin. This 24398f002c6cSBruce M Simpson * allows us to deal with page faults up-front. 24408f002c6cSBruce M Simpson */ 24418f002c6cSBruce M Simpson if (msfr.msfr_nsrcs > 0) { 24428f002c6cSBruce M Simpson struct in6_msource *lims; 24438f002c6cSBruce M Simpson struct sockaddr_in6 *psin; 24448f002c6cSBruce M Simpson struct sockaddr_storage *kss, *pkss; 24458f002c6cSBruce M Simpson int i; 24468f002c6cSBruce M Simpson 24478f002c6cSBruce M Simpson INP_WUNLOCK(inp); 24488f002c6cSBruce M Simpson 24498f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: loading %lu source list entries", 24508f002c6cSBruce M Simpson __func__, (unsigned long)msfr.msfr_nsrcs); 24518f002c6cSBruce M Simpson kss = malloc(sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs, 24528f002c6cSBruce M Simpson M_TEMP, M_WAITOK); 24538f002c6cSBruce M Simpson error = copyin(msfr.msfr_srcs, kss, 24548f002c6cSBruce M Simpson sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs); 24558f002c6cSBruce M Simpson if (error) { 24568f002c6cSBruce M Simpson free(kss, M_TEMP); 24578f002c6cSBruce M Simpson return (error); 24588f002c6cSBruce M Simpson } 24598f002c6cSBruce M Simpson 24608f002c6cSBruce M Simpson INP_WLOCK(inp); 24618f002c6cSBruce M Simpson 24628f002c6cSBruce M Simpson /* 24638f002c6cSBruce M Simpson * Mark all source filters as UNDEFINED at t1. 24648f002c6cSBruce M Simpson * Restore new group filter mode, as im6f_leave() 24658f002c6cSBruce M Simpson * will set it to INCLUDE. 24668f002c6cSBruce M Simpson */ 24678f002c6cSBruce M Simpson im6f_leave(imf); 24688f002c6cSBruce M Simpson imf->im6f_st[1] = msfr.msfr_fmode; 24698f002c6cSBruce M Simpson 24708f002c6cSBruce M Simpson /* 24718f002c6cSBruce M Simpson * Update socket layer filters at t1, lazy-allocating 24728f002c6cSBruce M Simpson * new entries. This saves a bunch of memory at the 24738f002c6cSBruce M Simpson * cost of one RB_FIND() per source entry; duplicate 24748f002c6cSBruce M Simpson * entries in the msfr_nsrcs vector are ignored. 24758f002c6cSBruce M Simpson * If we encounter an error, rollback transaction. 24768f002c6cSBruce M Simpson * 24778f002c6cSBruce M Simpson * XXX This too could be replaced with a set-symmetric 24788f002c6cSBruce M Simpson * difference like loop to avoid walking from root 24798f002c6cSBruce M Simpson * every time, as the key space is common. 24808f002c6cSBruce M Simpson */ 24818f002c6cSBruce M Simpson for (i = 0, pkss = kss; i < msfr.msfr_nsrcs; i++, pkss++) { 24828f002c6cSBruce M Simpson psin = (struct sockaddr_in6 *)pkss; 24838f002c6cSBruce M Simpson if (psin->sin6_family != AF_INET6) { 24848f002c6cSBruce M Simpson error = EAFNOSUPPORT; 24858f002c6cSBruce M Simpson break; 24868f002c6cSBruce M Simpson } 24878f002c6cSBruce M Simpson if (psin->sin6_len != sizeof(struct sockaddr_in6)) { 24888f002c6cSBruce M Simpson error = EINVAL; 24898f002c6cSBruce M Simpson break; 24908f002c6cSBruce M Simpson } 249129dc7bc6SBruce M Simpson if (IN6_IS_ADDR_MULTICAST(&psin->sin6_addr)) { 249229dc7bc6SBruce M Simpson error = EINVAL; 249329dc7bc6SBruce M Simpson break; 249429dc7bc6SBruce M Simpson } 249529dc7bc6SBruce M Simpson /* 249629dc7bc6SBruce M Simpson * TODO: Validate embedded scope ID in source 249729dc7bc6SBruce M Simpson * list entry against passed-in ifp, if and only 249829dc7bc6SBruce M Simpson * if source list filter entry is iface or node local. 249929dc7bc6SBruce M Simpson */ 250029dc7bc6SBruce M Simpson in6_clearscope(&psin->sin6_addr); 25018f002c6cSBruce M Simpson error = im6f_get_source(imf, psin, &lims); 25028f002c6cSBruce M Simpson if (error) 25038f002c6cSBruce M Simpson break; 25048f002c6cSBruce M Simpson lims->im6sl_st[1] = imf->im6f_st[1]; 25058f002c6cSBruce M Simpson } 25068f002c6cSBruce M Simpson free(kss, M_TEMP); 25078f002c6cSBruce M Simpson } 25088f002c6cSBruce M Simpson 25098f002c6cSBruce M Simpson if (error) 25108f002c6cSBruce M Simpson goto out_im6f_rollback; 25118f002c6cSBruce M Simpson 25128f002c6cSBruce M Simpson INP_WLOCK_ASSERT(inp); 25138f002c6cSBruce M Simpson IN6_MULTI_LOCK(); 25148f002c6cSBruce M Simpson 25158f002c6cSBruce M Simpson /* 25168f002c6cSBruce M Simpson * Begin state merge transaction at MLD layer. 25178f002c6cSBruce M Simpson */ 25188f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: merge inm state", __func__); 25198f002c6cSBruce M Simpson error = in6m_merge(inm, imf); 25208f002c6cSBruce M Simpson if (error) { 25218f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: failed to merge inm state", __func__); 25228f002c6cSBruce M Simpson goto out_im6f_rollback; 25238f002c6cSBruce M Simpson } 25248f002c6cSBruce M Simpson 25258f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: doing mld downcall", __func__); 25268f002c6cSBruce M Simpson error = mld_change_state(inm, 0); 25278f002c6cSBruce M Simpson if (error) 25288f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: failed mld downcall", __func__); 25298f002c6cSBruce M Simpson 25308f002c6cSBruce M Simpson IN6_MULTI_UNLOCK(); 25318f002c6cSBruce M Simpson 25328f002c6cSBruce M Simpson out_im6f_rollback: 25338f002c6cSBruce M Simpson if (error) 25348f002c6cSBruce M Simpson im6f_rollback(imf); 25358f002c6cSBruce M Simpson else 25368f002c6cSBruce M Simpson im6f_commit(imf); 25378f002c6cSBruce M Simpson 25388f002c6cSBruce M Simpson im6f_reap(imf); 25398f002c6cSBruce M Simpson 25408f002c6cSBruce M Simpson out_in6p_locked: 25418f002c6cSBruce M Simpson INP_WUNLOCK(inp); 25428f002c6cSBruce M Simpson return (error); 25438f002c6cSBruce M Simpson } 25448f002c6cSBruce M Simpson 25458f002c6cSBruce M Simpson /* 25468f002c6cSBruce M Simpson * Set the IP multicast options in response to user setsockopt(). 25478f002c6cSBruce M Simpson * 25488f002c6cSBruce M Simpson * Many of the socket options handled in this function duplicate the 25498f002c6cSBruce M Simpson * functionality of socket options in the regular unicast API. However, 25508f002c6cSBruce M Simpson * it is not possible to merge the duplicate code, because the idempotence 25518f002c6cSBruce M Simpson * of the IPv6 multicast part of the BSD Sockets API must be preserved; 25528f002c6cSBruce M Simpson * the effects of these options must be treated as separate and distinct. 25538f002c6cSBruce M Simpson * 25548f002c6cSBruce M Simpson * SMPng: XXX: Unlocked read of inp_socket believed OK. 25558f002c6cSBruce M Simpson */ 25568f002c6cSBruce M Simpson int 25578f002c6cSBruce M Simpson ip6_setmoptions(struct inpcb *inp, struct sockopt *sopt) 25588f002c6cSBruce M Simpson { 255933cde130SBruce M Simpson struct ip6_moptions *im6o; 25608f002c6cSBruce M Simpson int error; 25618f002c6cSBruce M Simpson 25628f002c6cSBruce M Simpson error = 0; 25638f002c6cSBruce M Simpson 25648f002c6cSBruce M Simpson /* 25658f002c6cSBruce M Simpson * If socket is neither of type SOCK_RAW or SOCK_DGRAM, 25668f002c6cSBruce M Simpson * or is a divert socket, reject it. 25678f002c6cSBruce M Simpson */ 25688f002c6cSBruce M Simpson if (inp->inp_socket->so_proto->pr_protocol == IPPROTO_DIVERT || 25698f002c6cSBruce M Simpson (inp->inp_socket->so_proto->pr_type != SOCK_RAW && 25708f002c6cSBruce M Simpson inp->inp_socket->so_proto->pr_type != SOCK_DGRAM)) 25718f002c6cSBruce M Simpson return (EOPNOTSUPP); 25728f002c6cSBruce M Simpson 25738f002c6cSBruce M Simpson switch (sopt->sopt_name) { 25748f002c6cSBruce M Simpson case IPV6_MULTICAST_IF: 25758f002c6cSBruce M Simpson error = in6p_set_multicast_if(inp, sopt); 25768f002c6cSBruce M Simpson break; 25778f002c6cSBruce M Simpson 25788f002c6cSBruce M Simpson case IPV6_MULTICAST_HOPS: { 25798f002c6cSBruce M Simpson int hlim; 25808f002c6cSBruce M Simpson 25818f002c6cSBruce M Simpson if (sopt->sopt_valsize != sizeof(int)) { 25828f002c6cSBruce M Simpson error = EINVAL; 25838f002c6cSBruce M Simpson break; 25848f002c6cSBruce M Simpson } 25858f002c6cSBruce M Simpson error = sooptcopyin(sopt, &hlim, sizeof(hlim), sizeof(int)); 25868f002c6cSBruce M Simpson if (error) 25878f002c6cSBruce M Simpson break; 25888f002c6cSBruce M Simpson if (hlim < -1 || hlim > 255) { 25898f002c6cSBruce M Simpson error = EINVAL; 25908f002c6cSBruce M Simpson break; 259133cde130SBruce M Simpson } else if (hlim == -1) { 259233cde130SBruce M Simpson hlim = V_ip6_defmcasthlim; 25938f002c6cSBruce M Simpson } 259433cde130SBruce M Simpson im6o = in6p_findmoptions(inp); 259533cde130SBruce M Simpson im6o->im6o_multicast_hlim = hlim; 25968f002c6cSBruce M Simpson INP_WUNLOCK(inp); 25978f002c6cSBruce M Simpson break; 25988f002c6cSBruce M Simpson } 25998f002c6cSBruce M Simpson 26008f002c6cSBruce M Simpson case IPV6_MULTICAST_LOOP: { 26018f002c6cSBruce M Simpson u_int loop; 26028f002c6cSBruce M Simpson 26038f002c6cSBruce M Simpson /* 26048f002c6cSBruce M Simpson * Set the loopback flag for outgoing multicast packets. 260533cde130SBruce M Simpson * Must be zero or one. 26068f002c6cSBruce M Simpson */ 26078f002c6cSBruce M Simpson if (sopt->sopt_valsize != sizeof(u_int)) { 26088f002c6cSBruce M Simpson error = EINVAL; 26098f002c6cSBruce M Simpson break; 26108f002c6cSBruce M Simpson } 26118f002c6cSBruce M Simpson error = sooptcopyin(sopt, &loop, sizeof(u_int), sizeof(u_int)); 26128f002c6cSBruce M Simpson if (error) 26138f002c6cSBruce M Simpson break; 261433cde130SBruce M Simpson if (loop > 1) { 261533cde130SBruce M Simpson error = EINVAL; 261633cde130SBruce M Simpson break; 261733cde130SBruce M Simpson } 261833cde130SBruce M Simpson im6o = in6p_findmoptions(inp); 261933cde130SBruce M Simpson im6o->im6o_multicast_loop = loop; 26208f002c6cSBruce M Simpson INP_WUNLOCK(inp); 26218f002c6cSBruce M Simpson break; 26228f002c6cSBruce M Simpson } 26238f002c6cSBruce M Simpson 26248f002c6cSBruce M Simpson case IPV6_JOIN_GROUP: 26258f002c6cSBruce M Simpson case MCAST_JOIN_GROUP: 26268f002c6cSBruce M Simpson case MCAST_JOIN_SOURCE_GROUP: 26278f002c6cSBruce M Simpson error = in6p_join_group(inp, sopt); 26288f002c6cSBruce M Simpson break; 26298f002c6cSBruce M Simpson 26308f002c6cSBruce M Simpson case IPV6_LEAVE_GROUP: 26318f002c6cSBruce M Simpson case MCAST_LEAVE_GROUP: 26328f002c6cSBruce M Simpson case MCAST_LEAVE_SOURCE_GROUP: 26338f002c6cSBruce M Simpson error = in6p_leave_group(inp, sopt); 26348f002c6cSBruce M Simpson break; 26358f002c6cSBruce M Simpson 26368f002c6cSBruce M Simpson case MCAST_BLOCK_SOURCE: 26378f002c6cSBruce M Simpson case MCAST_UNBLOCK_SOURCE: 26388f002c6cSBruce M Simpson error = in6p_block_unblock_source(inp, sopt); 26398f002c6cSBruce M Simpson break; 26408f002c6cSBruce M Simpson 26418f002c6cSBruce M Simpson case IPV6_MSFILTER: 26428f002c6cSBruce M Simpson error = in6p_set_source_filters(inp, sopt); 26438f002c6cSBruce M Simpson break; 26448f002c6cSBruce M Simpson 26458f002c6cSBruce M Simpson default: 26468f002c6cSBruce M Simpson error = EOPNOTSUPP; 26478f002c6cSBruce M Simpson break; 26488f002c6cSBruce M Simpson } 26498f002c6cSBruce M Simpson 26508f002c6cSBruce M Simpson INP_UNLOCK_ASSERT(inp); 26518f002c6cSBruce M Simpson 26528f002c6cSBruce M Simpson return (error); 26538f002c6cSBruce M Simpson } 26548f002c6cSBruce M Simpson 26558f002c6cSBruce M Simpson /* 26568f002c6cSBruce M Simpson * Expose MLD's multicast filter mode and source list(s) to userland, 26578f002c6cSBruce M Simpson * keyed by (ifindex, group). 26588f002c6cSBruce M Simpson * The filter mode is written out as a uint32_t, followed by 26598f002c6cSBruce M Simpson * 0..n of struct in6_addr. 26608f002c6cSBruce M Simpson * For use by ifmcstat(8). 26618f002c6cSBruce M Simpson * SMPng: NOTE: unlocked read of ifindex space. 26628f002c6cSBruce M Simpson */ 26638f002c6cSBruce M Simpson static int 26648f002c6cSBruce M Simpson sysctl_ip6_mcast_filters(SYSCTL_HANDLER_ARGS) 26658f002c6cSBruce M Simpson { 266629dc7bc6SBruce M Simpson struct in6_addr mcaddr; 26678f002c6cSBruce M Simpson struct in6_addr src; 26688f002c6cSBruce M Simpson struct ifnet *ifp; 26698f002c6cSBruce M Simpson struct ifmultiaddr *ifma; 26708f002c6cSBruce M Simpson struct in6_multi *inm; 26718f002c6cSBruce M Simpson struct ip6_msource *ims; 26728f002c6cSBruce M Simpson int *name; 26738f002c6cSBruce M Simpson int retval; 26748f002c6cSBruce M Simpson u_int namelen; 26758f002c6cSBruce M Simpson uint32_t fmode, ifindex; 26768f002c6cSBruce M Simpson #ifdef KTR 26778f002c6cSBruce M Simpson char ip6tbuf[INET6_ADDRSTRLEN]; 26788f002c6cSBruce M Simpson #endif 26798f002c6cSBruce M Simpson 26808f002c6cSBruce M Simpson name = (int *)arg1; 26818f002c6cSBruce M Simpson namelen = arg2; 26828f002c6cSBruce M Simpson 26838f002c6cSBruce M Simpson if (req->newptr != NULL) 26848f002c6cSBruce M Simpson return (EPERM); 26858f002c6cSBruce M Simpson 26868f002c6cSBruce M Simpson /* int: ifindex + 4 * 32 bits of IPv6 address */ 26878f002c6cSBruce M Simpson if (namelen != 5) 26888f002c6cSBruce M Simpson return (EINVAL); 26898f002c6cSBruce M Simpson 26908f002c6cSBruce M Simpson ifindex = name[0]; 26918f002c6cSBruce M Simpson if (ifindex <= 0 || ifindex > V_if_index) { 26928f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: ifindex %u out of range", 26938f002c6cSBruce M Simpson __func__, ifindex); 26948f002c6cSBruce M Simpson return (ENOENT); 26958f002c6cSBruce M Simpson } 26968f002c6cSBruce M Simpson 269729dc7bc6SBruce M Simpson memcpy(&mcaddr, &name[1], sizeof(struct in6_addr)); 269829dc7bc6SBruce M Simpson if (!IN6_IS_ADDR_MULTICAST(&mcaddr)) { 26998f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: group %s is not multicast", 270029dc7bc6SBruce M Simpson __func__, ip6_sprintf(ip6tbuf, &mcaddr)); 27018f002c6cSBruce M Simpson return (EINVAL); 27028f002c6cSBruce M Simpson } 27038f002c6cSBruce M Simpson 27048f002c6cSBruce M Simpson ifp = ifnet_byindex(ifindex); 27058f002c6cSBruce M Simpson if (ifp == NULL) { 27068f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: no ifp for ifindex %u", 27078f002c6cSBruce M Simpson __func__, ifindex); 27088f002c6cSBruce M Simpson return (ENOENT); 27098f002c6cSBruce M Simpson } 271029dc7bc6SBruce M Simpson /* 271129dc7bc6SBruce M Simpson * Internal MLD lookups require that scope/zone ID is set. 271229dc7bc6SBruce M Simpson */ 271329dc7bc6SBruce M Simpson (void)in6_setscope(&mcaddr, ifp, NULL); 27148f002c6cSBruce M Simpson 27158f002c6cSBruce M Simpson retval = sysctl_wire_old_buffer(req, 27168f002c6cSBruce M Simpson sizeof(uint32_t) + (in6_mcast_maxgrpsrc * sizeof(struct in6_addr))); 27178f002c6cSBruce M Simpson if (retval) 27188f002c6cSBruce M Simpson return (retval); 27198f002c6cSBruce M Simpson 27208f002c6cSBruce M Simpson IN6_MULTI_LOCK(); 27218f002c6cSBruce M Simpson 27228f002c6cSBruce M Simpson IF_ADDR_LOCK(ifp); 27238f002c6cSBruce M Simpson TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 27248f002c6cSBruce M Simpson if (ifma->ifma_addr->sa_family != AF_INET6 || 27258f002c6cSBruce M Simpson ifma->ifma_protospec == NULL) 27268f002c6cSBruce M Simpson continue; 27278f002c6cSBruce M Simpson inm = (struct in6_multi *)ifma->ifma_protospec; 272829dc7bc6SBruce M Simpson if (!IN6_ARE_ADDR_EQUAL(&inm->in6m_addr, &mcaddr)) 27298f002c6cSBruce M Simpson continue; 27308f002c6cSBruce M Simpson fmode = inm->in6m_st[1].iss_fmode; 27318f002c6cSBruce M Simpson retval = SYSCTL_OUT(req, &fmode, sizeof(uint32_t)); 27328f002c6cSBruce M Simpson if (retval != 0) 27338f002c6cSBruce M Simpson break; 27348f002c6cSBruce M Simpson RB_FOREACH(ims, ip6_msource_tree, &inm->in6m_srcs) { 27358f002c6cSBruce M Simpson CTR2(KTR_MLD, "%s: visit node %p", __func__, ims); 27368f002c6cSBruce M Simpson /* 27378f002c6cSBruce M Simpson * Only copy-out sources which are in-mode. 27388f002c6cSBruce M Simpson */ 27398f002c6cSBruce M Simpson if (fmode != im6s_get_mode(inm, ims, 1)) { 27408f002c6cSBruce M Simpson CTR1(KTR_MLD, "%s: skip non-in-mode", 27418f002c6cSBruce M Simpson __func__); 27428f002c6cSBruce M Simpson continue; 27438f002c6cSBruce M Simpson } 27448f002c6cSBruce M Simpson src = ims->im6s_addr; 27458f002c6cSBruce M Simpson retval = SYSCTL_OUT(req, &src, 27468f002c6cSBruce M Simpson sizeof(struct in6_addr)); 27478f002c6cSBruce M Simpson if (retval != 0) 27488f002c6cSBruce M Simpson break; 27498f002c6cSBruce M Simpson } 27508f002c6cSBruce M Simpson } 27518f002c6cSBruce M Simpson IF_ADDR_UNLOCK(ifp); 27528f002c6cSBruce M Simpson 27538f002c6cSBruce M Simpson IN6_MULTI_UNLOCK(); 27548f002c6cSBruce M Simpson 27558f002c6cSBruce M Simpson return (retval); 27568f002c6cSBruce M Simpson } 27578f002c6cSBruce M Simpson 27588f002c6cSBruce M Simpson #ifdef KTR 27598f002c6cSBruce M Simpson 27608f002c6cSBruce M Simpson static const char *in6m_modestrs[] = { "un", "in", "ex" }; 27618f002c6cSBruce M Simpson 27628f002c6cSBruce M Simpson static const char * 27638f002c6cSBruce M Simpson in6m_mode_str(const int mode) 27648f002c6cSBruce M Simpson { 27658f002c6cSBruce M Simpson 27668f002c6cSBruce M Simpson if (mode >= MCAST_UNDEFINED && mode <= MCAST_EXCLUDE) 27678f002c6cSBruce M Simpson return (in6m_modestrs[mode]); 27688f002c6cSBruce M Simpson return ("??"); 27698f002c6cSBruce M Simpson } 27708f002c6cSBruce M Simpson 27718f002c6cSBruce M Simpson static const char *in6m_statestrs[] = { 27728f002c6cSBruce M Simpson "not-member", 27738f002c6cSBruce M Simpson "silent", 27748f002c6cSBruce M Simpson "idle", 27758f002c6cSBruce M Simpson "lazy", 27768f002c6cSBruce M Simpson "sleeping", 27778f002c6cSBruce M Simpson "awakening", 27788f002c6cSBruce M Simpson "query-pending", 27798f002c6cSBruce M Simpson "sg-query-pending", 27808f002c6cSBruce M Simpson "leaving" 27818f002c6cSBruce M Simpson }; 27828f002c6cSBruce M Simpson 27838f002c6cSBruce M Simpson static const char * 27848f002c6cSBruce M Simpson in6m_state_str(const int state) 27858f002c6cSBruce M Simpson { 27868f002c6cSBruce M Simpson 27878f002c6cSBruce M Simpson if (state >= MLD_NOT_MEMBER && state <= MLD_LEAVING_MEMBER) 27888f002c6cSBruce M Simpson return (in6m_statestrs[state]); 27898f002c6cSBruce M Simpson return ("??"); 27908f002c6cSBruce M Simpson } 27918f002c6cSBruce M Simpson 27928f002c6cSBruce M Simpson /* 27938f002c6cSBruce M Simpson * Dump an in6_multi structure to the console. 27948f002c6cSBruce M Simpson */ 27958f002c6cSBruce M Simpson void 27968f002c6cSBruce M Simpson in6m_print(const struct in6_multi *inm) 27978f002c6cSBruce M Simpson { 27988f002c6cSBruce M Simpson int t; 27998f002c6cSBruce M Simpson char ip6tbuf[INET6_ADDRSTRLEN]; 28008f002c6cSBruce M Simpson 28015b65b8bcSAlexander Kabaev if ((ktr_mask & KTR_MLD) == 0) 28028f002c6cSBruce M Simpson return; 28038f002c6cSBruce M Simpson 28048f002c6cSBruce M Simpson printf("%s: --- begin in6m %p ---\n", __func__, inm); 28058f002c6cSBruce M Simpson printf("addr %s ifp %p(%s) ifma %p\n", 28068f002c6cSBruce M Simpson ip6_sprintf(ip6tbuf, &inm->in6m_addr), 28078f002c6cSBruce M Simpson inm->in6m_ifp, 28088f002c6cSBruce M Simpson inm->in6m_ifp->if_xname, 28098f002c6cSBruce M Simpson inm->in6m_ifma); 28108f002c6cSBruce M Simpson printf("timer %u state %s refcount %u scq.len %u\n", 28118f002c6cSBruce M Simpson inm->in6m_timer, 28128f002c6cSBruce M Simpson in6m_state_str(inm->in6m_state), 28138f002c6cSBruce M Simpson inm->in6m_refcount, 28148f002c6cSBruce M Simpson inm->in6m_scq.ifq_len); 28158f002c6cSBruce M Simpson printf("mli %p nsrc %lu sctimer %u scrv %u\n", 28168f002c6cSBruce M Simpson inm->in6m_mli, 28178f002c6cSBruce M Simpson inm->in6m_nsrc, 28188f002c6cSBruce M Simpson inm->in6m_sctimer, 28198f002c6cSBruce M Simpson inm->in6m_scrv); 28208f002c6cSBruce M Simpson for (t = 0; t < 2; t++) { 28218f002c6cSBruce M Simpson printf("t%d: fmode %s asm %u ex %u in %u rec %u\n", t, 28228f002c6cSBruce M Simpson in6m_mode_str(inm->in6m_st[t].iss_fmode), 28238f002c6cSBruce M Simpson inm->in6m_st[t].iss_asm, 28248f002c6cSBruce M Simpson inm->in6m_st[t].iss_ex, 28258f002c6cSBruce M Simpson inm->in6m_st[t].iss_in, 28268f002c6cSBruce M Simpson inm->in6m_st[t].iss_rec); 28278f002c6cSBruce M Simpson } 28288f002c6cSBruce M Simpson printf("%s: --- end in6m %p ---\n", __func__, inm); 28298f002c6cSBruce M Simpson } 28308f002c6cSBruce M Simpson 28318f002c6cSBruce M Simpson #else /* !KTR */ 28328f002c6cSBruce M Simpson 28338f002c6cSBruce M Simpson void 28348f002c6cSBruce M Simpson in6m_print(const struct in6_multi *inm) 28358f002c6cSBruce M Simpson { 28368f002c6cSBruce M Simpson 28378f002c6cSBruce M Simpson } 28388f002c6cSBruce M Simpson 28398f002c6cSBruce M Simpson #endif /* KTR */ 2840