1170613Sbms/*- 2189592Sbms * Copyright (c) 2007-2009 Bruce Simpson. 3170613Sbms * Copyright (c) 2005 Robert N. M. Watson. 4170613Sbms * All rights reserved. 5170613Sbms * 6170613Sbms * Redistribution and use in source and binary forms, with or without 7170613Sbms * modification, are permitted provided that the following conditions 8170613Sbms * are met: 9170613Sbms * 1. Redistributions of source code must retain the above copyright 10170613Sbms * notice, this list of conditions and the following disclaimer. 11170613Sbms * 2. Redistributions in binary form must reproduce the above copyright 12170613Sbms * notice, this list of conditions and the following disclaimer in the 13170613Sbms * documentation and/or other materials provided with the distribution. 14170613Sbms * 3. The name of the author may not be used to endorse or promote 15170613Sbms * products derived from this software without specific prior written 16170613Sbms * permission. 17170613Sbms * 18170613Sbms * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19170613Sbms * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20170613Sbms * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21170613Sbms * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22170613Sbms * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23170613Sbms * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24170613Sbms * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25170613Sbms * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26170613Sbms * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27170613Sbms * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28170613Sbms * SUCH DAMAGE. 29170613Sbms */ 30170613Sbms 31170613Sbms/* 32170613Sbms * IPv4 multicast socket, group, and socket option processing module. 33170613Sbms */ 34170613Sbms 35170613Sbms#include <sys/cdefs.h> 36170613Sbms__FBSDID("$FreeBSD$"); 37170613Sbms 38170613Sbms#include <sys/param.h> 39170613Sbms#include <sys/systm.h> 40170613Sbms#include <sys/kernel.h> 41170613Sbms#include <sys/malloc.h> 42170613Sbms#include <sys/mbuf.h> 43171746Scsjp#include <sys/protosw.h> 44170613Sbms#include <sys/socket.h> 45170613Sbms#include <sys/socketvar.h> 46189592Sbms#include <sys/protosw.h> 47170613Sbms#include <sys/sysctl.h> 48189592Sbms#include <sys/ktr.h> 49189592Sbms#include <sys/tree.h> 50170613Sbms 51170613Sbms#include <net/if.h> 52170613Sbms#include <net/if_dl.h> 53170613Sbms#include <net/route.h> 54185571Sbz#include <net/vnet.h> 55170613Sbms 56170613Sbms#include <netinet/in.h> 57170613Sbms#include <netinet/in_systm.h> 58170613Sbms#include <netinet/in_pcb.h> 59170613Sbms#include <netinet/in_var.h> 60170613Sbms#include <netinet/ip_var.h> 61170613Sbms#include <netinet/igmp_var.h> 62170613Sbms 63189592Sbms#ifndef KTR_IGMPV3 64191659Sbms#define KTR_IGMPV3 KTR_INET 65189592Sbms#endif 66189592Sbms 67170613Sbms#ifndef __SOCKUNION_DECLARED 68170613Sbmsunion sockunion { 69170613Sbms struct sockaddr_storage ss; 70170613Sbms struct sockaddr sa; 71170613Sbms struct sockaddr_dl sdl; 72170613Sbms struct sockaddr_in sin; 73170613Sbms}; 74170613Sbmstypedef union sockunion sockunion_t; 75170613Sbms#define __SOCKUNION_DECLARED 76170613Sbms#endif /* __SOCKUNION_DECLARED */ 77170613Sbms 78189592Sbmsstatic MALLOC_DEFINE(M_INMFILTER, "in_mfilter", 79189592Sbms "IPv4 multicast PCB-layer source filter"); 80170613Sbmsstatic MALLOC_DEFINE(M_IPMADDR, "in_multi", "IPv4 multicast group"); 81170613Sbmsstatic MALLOC_DEFINE(M_IPMOPTS, "ip_moptions", "IPv4 multicast options"); 82189592Sbmsstatic MALLOC_DEFINE(M_IPMSOURCE, "ip_msource", 83189592Sbms "IPv4 multicast IGMP-layer source filter"); 84170613Sbms 85170613Sbms/* 86189592Sbms * Locking: 87189592Sbms * - Lock order is: Giant, INP_WLOCK, IN_MULTI_LOCK, IGMP_LOCK, IF_ADDR_LOCK. 88189592Sbms * - The IF_ADDR_LOCK is implicitly taken by inm_lookup() earlier, however 89189592Sbms * it can be taken by code in net/if.c also. 90189592Sbms * - ip_moptions and in_mfilter are covered by the INP_WLOCK. 91189592Sbms * 92189592Sbms * struct in_multi is covered by IN_MULTI_LOCK. There isn't strictly 93189592Sbms * any need for in_multi itself to be virtualized -- it is bound to an ifp 94189592Sbms * anyway no matter what happens. 95170613Sbms */ 96170613Sbmsstruct mtx in_multi_mtx; 97189592SbmsMTX_SYSINIT(in_multi_mtx, &in_multi_mtx, "in_multi_mtx", MTX_DEF); 98170613Sbms 99170613Sbms/* 100170613Sbms * Functions with non-static linkage defined in this file should be 101170613Sbms * declared in in_var.h: 102189592Sbms * imo_multi_filter() 103170613Sbms * in_addmulti() 104170613Sbms * in_delmulti() 105189592Sbms * in_joingroup() 106189592Sbms * in_joingroup_locked() 107189592Sbms * in_leavegroup() 108189592Sbms * in_leavegroup_locked() 109170613Sbms * and ip_var.h: 110170613Sbms * inp_freemoptions() 111170613Sbms * inp_getmoptions() 112170613Sbms * inp_setmoptions() 113189592Sbms * 114189592Sbms * XXX: Both carp and pf need to use the legacy (*,G) KPIs in_addmulti() 115189592Sbms * and in_delmulti(). 116170613Sbms */ 117189592Sbmsstatic void imf_commit(struct in_mfilter *); 118189592Sbmsstatic int imf_get_source(struct in_mfilter *imf, 119189592Sbms const struct sockaddr_in *psin, 120189592Sbms struct in_msource **); 121189592Sbmsstatic struct in_msource * 122189592Sbms imf_graft(struct in_mfilter *, const uint8_t, 123189592Sbms const struct sockaddr_in *); 124189592Sbmsstatic void imf_leave(struct in_mfilter *); 125189592Sbmsstatic int imf_prune(struct in_mfilter *, const struct sockaddr_in *); 126189592Sbmsstatic void imf_purge(struct in_mfilter *); 127189592Sbmsstatic void imf_rollback(struct in_mfilter *); 128189592Sbmsstatic void imf_reap(struct in_mfilter *); 129170613Sbmsstatic int imo_grow(struct ip_moptions *); 130189592Sbmsstatic size_t imo_match_group(const struct ip_moptions *, 131189592Sbms const struct ifnet *, const struct sockaddr *); 132189592Sbmsstatic struct in_msource * 133189592Sbms imo_match_source(const struct ip_moptions *, const size_t, 134189592Sbms const struct sockaddr *); 135189592Sbmsstatic void ims_merge(struct ip_msource *ims, 136189592Sbms const struct in_msource *lims, const int rollback); 137189592Sbmsstatic int in_getmulti(struct ifnet *, const struct in_addr *, 138189592Sbms struct in_multi **); 139189592Sbmsstatic int inm_get_source(struct in_multi *inm, const in_addr_t haddr, 140189592Sbms const int noalloc, struct ip_msource **pims); 141259982Sdim#ifdef KTR 142189592Sbmsstatic int inm_is_ifp_detached(const struct in_multi *); 143259982Sdim#endif 144189592Sbmsstatic int inm_merge(struct in_multi *, /*const*/ struct in_mfilter *); 145189592Sbmsstatic void inm_purge(struct in_multi *); 146189592Sbmsstatic void inm_reap(struct in_multi *); 147170613Sbmsstatic struct ip_moptions * 148170613Sbms inp_findmoptions(struct inpcb *); 149170613Sbmsstatic int inp_get_source_filters(struct inpcb *, struct sockopt *); 150170613Sbmsstatic int inp_join_group(struct inpcb *, struct sockopt *); 151170613Sbmsstatic int inp_leave_group(struct inpcb *, struct sockopt *); 152189592Sbmsstatic struct ifnet * 153189592Sbms inp_lookup_mcast_ifp(const struct inpcb *, 154189592Sbms const struct sockaddr_in *, const struct in_addr); 155189592Sbmsstatic int inp_block_unblock_source(struct inpcb *, struct sockopt *); 156170613Sbmsstatic int inp_set_multicast_if(struct inpcb *, struct sockopt *); 157170613Sbmsstatic int inp_set_source_filters(struct inpcb *, struct sockopt *); 158189592Sbmsstatic int sysctl_ip_mcast_filters(SYSCTL_HANDLER_ARGS); 159170613Sbms 160248085Smariusstatic SYSCTL_NODE(_net_inet_ip, OID_AUTO, mcast, CTLFLAG_RW, 0, 161248085Smarius "IPv4 multicast"); 162189357Sbms 163189592Sbmsstatic u_long in_mcast_maxgrpsrc = IP_MAX_GROUP_SRC_FILTER; 164189592SbmsSYSCTL_ULONG(_net_inet_ip_mcast, OID_AUTO, maxgrpsrc, 165189592Sbms CTLFLAG_RW | CTLFLAG_TUN, &in_mcast_maxgrpsrc, 0, 166189592Sbms "Max source filters per group"); 167189592SbmsTUNABLE_ULONG("net.inet.ip.mcast.maxgrpsrc", &in_mcast_maxgrpsrc); 168189592Sbms 169189592Sbmsstatic u_long in_mcast_maxsocksrc = IP_MAX_SOCK_SRC_FILTER; 170189592SbmsSYSCTL_ULONG(_net_inet_ip_mcast, OID_AUTO, maxsocksrc, 171189592Sbms CTLFLAG_RW | CTLFLAG_TUN, &in_mcast_maxsocksrc, 0, 172189592Sbms "Max source filters per socket"); 173189592SbmsTUNABLE_ULONG("net.inet.ip.mcast.maxsocksrc", &in_mcast_maxsocksrc); 174189592Sbms 175189357Sbmsint in_mcast_loop = IP_DEFAULT_MULTICAST_LOOP; 176189357SbmsSYSCTL_INT(_net_inet_ip_mcast, OID_AUTO, loop, CTLFLAG_RW | CTLFLAG_TUN, 177189357Sbms &in_mcast_loop, 0, "Loopback multicast datagrams by default"); 178189357SbmsTUNABLE_INT("net.inet.ip.mcast.loop", &in_mcast_loop); 179189357Sbms 180248085Smariusstatic SYSCTL_NODE(_net_inet_ip_mcast, OID_AUTO, filters, 181189592Sbms CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_ip_mcast_filters, 182189592Sbms "Per-interface stack-wide source filters"); 183189592Sbms 184259982Sdim#ifdef KTR 185170613Sbms/* 186189592Sbms * Inline function which wraps assertions for a valid ifp. 187189592Sbms * The ifnet layer will set the ifma's ifp pointer to NULL if the ifp 188189592Sbms * is detached. 189189592Sbms */ 190189592Sbmsstatic int __inline 191189592Sbmsinm_is_ifp_detached(const struct in_multi *inm) 192189592Sbms{ 193189592Sbms struct ifnet *ifp; 194189592Sbms 195189592Sbms KASSERT(inm->inm_ifma != NULL, ("%s: no ifma", __func__)); 196189592Sbms ifp = inm->inm_ifma->ifma_ifp; 197189592Sbms if (ifp != NULL) { 198189592Sbms /* 199189592Sbms * Sanity check that netinet's notion of ifp is the 200189592Sbms * same as net's. 201189592Sbms */ 202189592Sbms KASSERT(inm->inm_ifp == ifp, ("%s: bad ifp", __func__)); 203189592Sbms } 204189592Sbms 205189592Sbms return (ifp == NULL); 206189592Sbms} 207259982Sdim#endif 208189592Sbms 209189592Sbms/* 210189592Sbms * Initialize an in_mfilter structure to a known state at t0, t1 211189592Sbms * with an empty source filter list. 212189592Sbms */ 213189592Sbmsstatic __inline void 214189592Sbmsimf_init(struct in_mfilter *imf, const int st0, const int st1) 215189592Sbms{ 216189592Sbms memset(imf, 0, sizeof(struct in_mfilter)); 217189592Sbms RB_INIT(&imf->imf_sources); 218189592Sbms imf->imf_st[0] = st0; 219189592Sbms imf->imf_st[1] = st1; 220189592Sbms} 221189592Sbms 222189592Sbms/* 223170613Sbms * Resize the ip_moptions vector to the next power-of-two minus 1. 224170613Sbms * May be called with locks held; do not sleep. 225170613Sbms */ 226170613Sbmsstatic int 227170613Sbmsimo_grow(struct ip_moptions *imo) 228170613Sbms{ 229170613Sbms struct in_multi **nmships; 230170613Sbms struct in_multi **omships; 231170613Sbms struct in_mfilter *nmfilters; 232170613Sbms struct in_mfilter *omfilters; 233170613Sbms size_t idx; 234170613Sbms size_t newmax; 235170613Sbms size_t oldmax; 236170613Sbms 237170613Sbms nmships = NULL; 238170613Sbms nmfilters = NULL; 239170613Sbms omships = imo->imo_membership; 240170613Sbms omfilters = imo->imo_mfilters; 241170613Sbms oldmax = imo->imo_max_memberships; 242170613Sbms newmax = ((oldmax + 1) * 2) - 1; 243170613Sbms 244170613Sbms if (newmax <= IP_MAX_MEMBERSHIPS) { 245170613Sbms nmships = (struct in_multi **)realloc(omships, 246170613Sbms sizeof(struct in_multi *) * newmax, M_IPMOPTS, M_NOWAIT); 247170613Sbms nmfilters = (struct in_mfilter *)realloc(omfilters, 248189592Sbms sizeof(struct in_mfilter) * newmax, M_INMFILTER, M_NOWAIT); 249170613Sbms if (nmships != NULL && nmfilters != NULL) { 250170613Sbms /* Initialize newly allocated source filter heads. */ 251170613Sbms for (idx = oldmax; idx < newmax; idx++) { 252189592Sbms imf_init(&nmfilters[idx], MCAST_UNDEFINED, 253189592Sbms MCAST_EXCLUDE); 254170613Sbms } 255170613Sbms imo->imo_max_memberships = newmax; 256170613Sbms imo->imo_membership = nmships; 257170613Sbms imo->imo_mfilters = nmfilters; 258170613Sbms } 259170613Sbms } 260170613Sbms 261170613Sbms if (nmships == NULL || nmfilters == NULL) { 262170613Sbms if (nmships != NULL) 263170613Sbms free(nmships, M_IPMOPTS); 264170613Sbms if (nmfilters != NULL) 265189592Sbms free(nmfilters, M_INMFILTER); 266170613Sbms return (ETOOMANYREFS); 267170613Sbms } 268170613Sbms 269170613Sbms return (0); 270170613Sbms} 271170613Sbms 272170613Sbms/* 273170613Sbms * Find an IPv4 multicast group entry for this ip_moptions instance 274170613Sbms * which matches the specified group, and optionally an interface. 275170613Sbms * Return its index into the array, or -1 if not found. 276170613Sbms */ 277189592Sbmsstatic size_t 278189592Sbmsimo_match_group(const struct ip_moptions *imo, const struct ifnet *ifp, 279189592Sbms const struct sockaddr *group) 280170613Sbms{ 281189592Sbms const struct sockaddr_in *gsin; 282170613Sbms struct in_multi **pinm; 283170613Sbms int idx; 284170613Sbms int nmships; 285170613Sbms 286189592Sbms gsin = (const struct sockaddr_in *)group; 287170613Sbms 288170613Sbms /* The imo_membership array may be lazy allocated. */ 289170613Sbms if (imo->imo_membership == NULL || imo->imo_num_memberships == 0) 290170613Sbms return (-1); 291170613Sbms 292170613Sbms nmships = imo->imo_num_memberships; 293170613Sbms pinm = &imo->imo_membership[0]; 294170613Sbms for (idx = 0; idx < nmships; idx++, pinm++) { 295170613Sbms if (*pinm == NULL) 296170613Sbms continue; 297170613Sbms if ((ifp == NULL || ((*pinm)->inm_ifp == ifp)) && 298189592Sbms in_hosteq((*pinm)->inm_addr, gsin->sin_addr)) { 299170613Sbms break; 300170613Sbms } 301170613Sbms } 302170613Sbms if (idx >= nmships) 303170613Sbms idx = -1; 304170613Sbms 305170613Sbms return (idx); 306170613Sbms} 307170613Sbms 308170613Sbms/* 309189592Sbms * Find an IPv4 multicast source entry for this imo which matches 310170613Sbms * the given group index for this socket, and source address. 311189592Sbms * 312189592Sbms * NOTE: This does not check if the entry is in-mode, merely if 313189592Sbms * it exists, which may not be the desired behaviour. 314170613Sbms */ 315189592Sbmsstatic struct in_msource * 316189592Sbmsimo_match_source(const struct ip_moptions *imo, const size_t gidx, 317189592Sbms const struct sockaddr *src) 318170613Sbms{ 319189592Sbms struct ip_msource find; 320170613Sbms struct in_mfilter *imf; 321189592Sbms struct ip_msource *ims; 322189592Sbms const sockunion_t *psa; 323170613Sbms 324170613Sbms KASSERT(src->sa_family == AF_INET, ("%s: !AF_INET", __func__)); 325170613Sbms KASSERT(gidx != -1 && gidx < imo->imo_num_memberships, 326170613Sbms ("%s: invalid index %d\n", __func__, (int)gidx)); 327170613Sbms 328170613Sbms /* The imo_mfilters array may be lazy allocated. */ 329170613Sbms if (imo->imo_mfilters == NULL) 330170613Sbms return (NULL); 331170613Sbms imf = &imo->imo_mfilters[gidx]; 332170613Sbms 333189592Sbms /* Source trees are keyed in host byte order. */ 334189592Sbms psa = (const sockunion_t *)src; 335189592Sbms find.ims_haddr = ntohl(psa->sin.sin_addr.s_addr); 336189592Sbms ims = RB_FIND(ip_msource_tree, &imf->imf_sources, &find); 337189592Sbms 338189592Sbms return ((struct in_msource *)ims); 339170613Sbms} 340170613Sbms 341170613Sbms/* 342189592Sbms * Perform filtering for multicast datagrams on a socket by group and source. 343189592Sbms * 344189592Sbms * Returns 0 if a datagram should be allowed through, or various error codes 345189592Sbms * if the socket was not a member of the group, or the source was muted, etc. 346170613Sbms */ 347189592Sbmsint 348189592Sbmsimo_multi_filter(const struct ip_moptions *imo, const struct ifnet *ifp, 349189592Sbms const struct sockaddr *group, const struct sockaddr *src) 350170613Sbms{ 351189592Sbms size_t gidx; 352189592Sbms struct in_msource *ims; 353189592Sbms int mode; 354189592Sbms 355189592Sbms KASSERT(ifp != NULL, ("%s: null ifp", __func__)); 356189592Sbms 357189592Sbms gidx = imo_match_group(imo, ifp, group); 358189592Sbms if (gidx == -1) 359189592Sbms return (MCAST_NOTGMEMBER); 360189592Sbms 361189592Sbms /* 362189592Sbms * Check if the source was included in an (S,G) join. 363189592Sbms * Allow reception on exclusive memberships by default, 364189592Sbms * reject reception on inclusive memberships by default. 365189592Sbms * Exclude source only if an in-mode exclude filter exists. 366189592Sbms * Include source only if an in-mode include filter exists. 367189592Sbms * NOTE: We are comparing group state here at IGMP t1 (now) 368189592Sbms * with socket-layer t0 (since last downcall). 369189592Sbms */ 370189592Sbms mode = imo->imo_mfilters[gidx].imf_st[1]; 371189592Sbms ims = imo_match_source(imo, gidx, src); 372189592Sbms 373189592Sbms if ((ims == NULL && mode == MCAST_INCLUDE) || 374189592Sbms (ims != NULL && ims->imsl_st[0] != mode)) 375189592Sbms return (MCAST_NOTSMEMBER); 376189592Sbms 377189592Sbms return (MCAST_PASS); 378189592Sbms} 379189592Sbms 380189592Sbms/* 381189592Sbms * Find and return a reference to an in_multi record for (ifp, group), 382189592Sbms * and bump its reference count. 383189592Sbms * If one does not exist, try to allocate it, and update link-layer multicast 384189592Sbms * filters on ifp to listen for group. 385189592Sbms * Assumes the IN_MULTI lock is held across the call. 386189592Sbms * Return 0 if successful, otherwise return an appropriate error code. 387189592Sbms */ 388189592Sbmsstatic int 389189592Sbmsin_getmulti(struct ifnet *ifp, const struct in_addr *group, 390189592Sbms struct in_multi **pinm) 391189592Sbms{ 392189592Sbms struct sockaddr_in gsin; 393189592Sbms struct ifmultiaddr *ifma; 394189592Sbms struct in_ifinfo *ii; 395189592Sbms struct in_multi *inm; 396189592Sbms int error; 397170613Sbms 398189592Sbms IN_MULTI_LOCK_ASSERT(); 399170613Sbms 400189592Sbms ii = (struct in_ifinfo *)ifp->if_afdata[AF_INET]; 401170613Sbms 402189592Sbms inm = inm_lookup(ifp, *group); 403170613Sbms if (inm != NULL) { 404170613Sbms /* 405170613Sbms * If we already joined this group, just bump the 406170613Sbms * refcount and return it. 407170613Sbms */ 408170613Sbms KASSERT(inm->inm_refcount >= 1, 409170613Sbms ("%s: bad refcount %d", __func__, inm->inm_refcount)); 410170613Sbms ++inm->inm_refcount; 411189592Sbms *pinm = inm; 412189592Sbms return (0); 413189592Sbms } 414170613Sbms 415189592Sbms memset(&gsin, 0, sizeof(gsin)); 416189592Sbms gsin.sin_family = AF_INET; 417189592Sbms gsin.sin_len = sizeof(struct sockaddr_in); 418189592Sbms gsin.sin_addr = *group; 419170613Sbms 420189592Sbms /* 421189592Sbms * Check if a link-layer group is already associated 422189592Sbms * with this network-layer group on the given ifnet. 423189592Sbms */ 424189592Sbms error = if_addmulti(ifp, (struct sockaddr *)&gsin, &ifma); 425189592Sbms if (error != 0) 426189592Sbms return (error); 427189592Sbms 428189931Sbms /* XXX ifma_protospec must be covered by IF_ADDR_LOCK */ 429233200Sjhb IF_ADDR_WLOCK(ifp); 430189931Sbms 431189592Sbms /* 432189592Sbms * If something other than netinet is occupying the link-layer 433189592Sbms * group, print a meaningful error message and back out of 434189592Sbms * the allocation. 435189592Sbms * Otherwise, bump the refcount on the existing network-layer 436189592Sbms * group association and return it. 437189592Sbms */ 438189592Sbms if (ifma->ifma_protospec != NULL) { 439189592Sbms inm = (struct in_multi *)ifma->ifma_protospec; 440170613Sbms#ifdef INVARIANTS 441189592Sbms KASSERT(ifma->ifma_addr != NULL, ("%s: no ifma_addr", 442189592Sbms __func__)); 443189592Sbms KASSERT(ifma->ifma_addr->sa_family == AF_INET, 444189592Sbms ("%s: ifma not AF_INET", __func__)); 445189592Sbms KASSERT(inm != NULL, ("%s: no ifma_protospec", __func__)); 446189592Sbms if (inm->inm_ifma != ifma || inm->inm_ifp != ifp || 447189592Sbms !in_hosteq(inm->inm_addr, *group)) 448189592Sbms panic("%s: ifma %p is inconsistent with %p (%s)", 449189592Sbms __func__, ifma, inm, inet_ntoa(*group)); 450170613Sbms#endif 451189592Sbms ++inm->inm_refcount; 452189592Sbms *pinm = inm; 453233200Sjhb IF_ADDR_WUNLOCK(ifp); 454189592Sbms return (0); 455189592Sbms } 456189592Sbms 457233200Sjhb IF_ADDR_WLOCK_ASSERT(ifp); 458189931Sbms 459189592Sbms /* 460189592Sbms * A new in_multi record is needed; allocate and initialize it. 461189592Sbms * We DO NOT perform an IGMP join as the in_ layer may need to 462189592Sbms * push an initial source list down to IGMP to support SSM. 463189592Sbms * 464189592Sbms * The initial source filter state is INCLUDE, {} as per the RFC. 465189592Sbms */ 466189592Sbms inm = malloc(sizeof(*inm), M_IPMADDR, M_NOWAIT | M_ZERO); 467189592Sbms if (inm == NULL) { 468189592Sbms if_delmulti_ifma(ifma); 469233200Sjhb IF_ADDR_WUNLOCK(ifp); 470189592Sbms return (ENOMEM); 471189592Sbms } 472189592Sbms inm->inm_addr = *group; 473189592Sbms inm->inm_ifp = ifp; 474189592Sbms inm->inm_igi = ii->ii_igmp; 475189592Sbms inm->inm_ifma = ifma; 476189592Sbms inm->inm_refcount = 1; 477189592Sbms inm->inm_state = IGMP_NOT_MEMBER; 478189592Sbms 479189592Sbms /* 480189592Sbms * Pending state-changes per group are subject to a bounds check. 481189592Sbms */ 482189592Sbms IFQ_SET_MAXLEN(&inm->inm_scq, IGMP_MAX_STATE_CHANGES); 483189592Sbms 484189592Sbms inm->inm_st[0].iss_fmode = MCAST_UNDEFINED; 485189592Sbms inm->inm_st[1].iss_fmode = MCAST_UNDEFINED; 486189592Sbms RB_INIT(&inm->inm_srcs); 487189592Sbms 488189592Sbms ifma->ifma_protospec = inm; 489189592Sbms 490189592Sbms *pinm = inm; 491189592Sbms 492233200Sjhb IF_ADDR_WUNLOCK(ifp); 493189592Sbms return (0); 494189592Sbms} 495189592Sbms 496189592Sbms/* 497189592Sbms * Drop a reference to an in_multi record. 498189592Sbms * 499189592Sbms * If the refcount drops to 0, free the in_multi record and 500189592Sbms * delete the underlying link-layer membership. 501189592Sbms */ 502189592Sbmsvoid 503189592Sbmsinm_release_locked(struct in_multi *inm) 504189592Sbms{ 505189592Sbms struct ifmultiaddr *ifma; 506189592Sbms 507189592Sbms IN_MULTI_LOCK_ASSERT(); 508189592Sbms 509189592Sbms CTR2(KTR_IGMPV3, "%s: refcount is %d", __func__, inm->inm_refcount); 510189592Sbms 511189592Sbms if (--inm->inm_refcount > 0) { 512189592Sbms CTR2(KTR_IGMPV3, "%s: refcount is now %d", __func__, 513189592Sbms inm->inm_refcount); 514189592Sbms return; 515189592Sbms } 516189592Sbms 517189592Sbms CTR2(KTR_IGMPV3, "%s: freeing inm %p", __func__, inm); 518189592Sbms 519189592Sbms ifma = inm->inm_ifma; 520189592Sbms 521189931Sbms /* XXX this access is not covered by IF_ADDR_LOCK */ 522189592Sbms CTR2(KTR_IGMPV3, "%s: purging ifma %p", __func__, ifma); 523189592Sbms KASSERT(ifma->ifma_protospec == inm, 524189592Sbms ("%s: ifma_protospec != inm", __func__)); 525189592Sbms ifma->ifma_protospec = NULL; 526189592Sbms 527189592Sbms inm_purge(inm); 528189592Sbms 529189592Sbms free(inm, M_IPMADDR); 530189592Sbms 531189592Sbms if_delmulti_ifma(ifma); 532189592Sbms} 533189592Sbms 534189592Sbms/* 535189592Sbms * Clear recorded source entries for a group. 536189592Sbms * Used by the IGMP code. Caller must hold the IN_MULTI lock. 537189592Sbms * FIXME: Should reap. 538189592Sbms */ 539189592Sbmsvoid 540189592Sbmsinm_clear_recorded(struct in_multi *inm) 541189592Sbms{ 542189592Sbms struct ip_msource *ims; 543189592Sbms 544189592Sbms IN_MULTI_LOCK_ASSERT(); 545189592Sbms 546189592Sbms RB_FOREACH(ims, ip_msource_tree, &inm->inm_srcs) { 547189592Sbms if (ims->ims_stp) { 548189592Sbms ims->ims_stp = 0; 549189592Sbms --inm->inm_st[1].iss_rec; 550170613Sbms } 551189592Sbms } 552189592Sbms KASSERT(inm->inm_st[1].iss_rec == 0, 553189592Sbms ("%s: iss_rec %d not 0", __func__, inm->inm_st[1].iss_rec)); 554189592Sbms} 555170613Sbms 556189592Sbms/* 557189592Sbms * Record a source as pending for a Source-Group IGMPv3 query. 558189592Sbms * This lives here as it modifies the shared tree. 559189592Sbms * 560189592Sbms * inm is the group descriptor. 561189592Sbms * naddr is the address of the source to record in network-byte order. 562189592Sbms * 563189592Sbms * If the net.inet.igmp.sgalloc sysctl is non-zero, we will 564189592Sbms * lazy-allocate a source node in response to an SG query. 565189592Sbms * Otherwise, no allocation is performed. This saves some memory 566189592Sbms * with the trade-off that the source will not be reported to the 567189592Sbms * router if joined in the window between the query response and 568189592Sbms * the group actually being joined on the local host. 569189592Sbms * 570189592Sbms * VIMAGE: XXX: Currently the igmp_sgalloc feature has been removed. 571189592Sbms * This turns off the allocation of a recorded source entry if 572189592Sbms * the group has not been joined. 573189592Sbms * 574189592Sbms * Return 0 if the source didn't exist or was already marked as recorded. 575189592Sbms * Return 1 if the source was marked as recorded by this function. 576189592Sbms * Return <0 if any error occured (negated errno code). 577189592Sbms */ 578189592Sbmsint 579189592Sbmsinm_record_source(struct in_multi *inm, const in_addr_t naddr) 580189592Sbms{ 581189592Sbms struct ip_msource find; 582189592Sbms struct ip_msource *ims, *nims; 583189592Sbms 584189592Sbms IN_MULTI_LOCK_ASSERT(); 585189592Sbms 586189592Sbms find.ims_haddr = ntohl(naddr); 587189592Sbms ims = RB_FIND(ip_msource_tree, &inm->inm_srcs, &find); 588189592Sbms if (ims && ims->ims_stp) 589189592Sbms return (0); 590189592Sbms if (ims == NULL) { 591189592Sbms if (inm->inm_nsrc == in_mcast_maxgrpsrc) 592189592Sbms return (-ENOSPC); 593189592Sbms nims = malloc(sizeof(struct ip_msource), M_IPMSOURCE, 594189592Sbms M_NOWAIT | M_ZERO); 595189592Sbms if (nims == NULL) 596189592Sbms return (-ENOMEM); 597189592Sbms nims->ims_haddr = find.ims_haddr; 598189592Sbms RB_INSERT(ip_msource_tree, &inm->inm_srcs, nims); 599189592Sbms ++inm->inm_nsrc; 600189592Sbms ims = nims; 601189592Sbms } 602189592Sbms 603189592Sbms /* 604189592Sbms * Mark the source as recorded and update the recorded 605189592Sbms * source count. 606189592Sbms */ 607189592Sbms ++ims->ims_stp; 608189592Sbms ++inm->inm_st[1].iss_rec; 609189592Sbms 610189592Sbms return (1); 611189592Sbms} 612189592Sbms 613189592Sbms/* 614189592Sbms * Return a pointer to an in_msource owned by an in_mfilter, 615189592Sbms * given its source address. 616189592Sbms * Lazy-allocate if needed. If this is a new entry its filter state is 617189592Sbms * undefined at t0. 618189592Sbms * 619189592Sbms * imf is the filter set being modified. 620189592Sbms * haddr is the source address in *host* byte-order. 621189592Sbms * 622189592Sbms * SMPng: May be called with locks held; malloc must not block. 623189592Sbms */ 624189592Sbmsstatic int 625189592Sbmsimf_get_source(struct in_mfilter *imf, const struct sockaddr_in *psin, 626189592Sbms struct in_msource **plims) 627189592Sbms{ 628189592Sbms struct ip_msource find; 629189592Sbms struct ip_msource *ims, *nims; 630189592Sbms struct in_msource *lims; 631189592Sbms int error; 632189592Sbms 633189592Sbms error = 0; 634189592Sbms ims = NULL; 635189592Sbms lims = NULL; 636189592Sbms 637189592Sbms /* key is host byte order */ 638189592Sbms find.ims_haddr = ntohl(psin->sin_addr.s_addr); 639189592Sbms ims = RB_FIND(ip_msource_tree, &imf->imf_sources, &find); 640189592Sbms lims = (struct in_msource *)ims; 641189592Sbms if (lims == NULL) { 642189592Sbms if (imf->imf_nsrc == in_mcast_maxsocksrc) 643189592Sbms return (ENOSPC); 644189592Sbms nims = malloc(sizeof(struct in_msource), M_INMFILTER, 645189592Sbms M_NOWAIT | M_ZERO); 646189592Sbms if (nims == NULL) 647189592Sbms return (ENOMEM); 648189592Sbms lims = (struct in_msource *)nims; 649189592Sbms lims->ims_haddr = find.ims_haddr; 650189592Sbms lims->imsl_st[0] = MCAST_UNDEFINED; 651189592Sbms RB_INSERT(ip_msource_tree, &imf->imf_sources, nims); 652189592Sbms ++imf->imf_nsrc; 653189592Sbms } 654189592Sbms 655189592Sbms *plims = lims; 656189592Sbms 657189592Sbms return (error); 658189592Sbms} 659189592Sbms 660189592Sbms/* 661189592Sbms * Graft a source entry into an existing socket-layer filter set, 662189592Sbms * maintaining any required invariants and checking allocations. 663189592Sbms * 664189592Sbms * The source is marked as being in the new filter mode at t1. 665189592Sbms * 666189592Sbms * Return the pointer to the new node, otherwise return NULL. 667189592Sbms */ 668189592Sbmsstatic struct in_msource * 669189592Sbmsimf_graft(struct in_mfilter *imf, const uint8_t st1, 670189592Sbms const struct sockaddr_in *psin) 671189592Sbms{ 672189592Sbms struct ip_msource *nims; 673189592Sbms struct in_msource *lims; 674189592Sbms 675189592Sbms nims = malloc(sizeof(struct in_msource), M_INMFILTER, 676189592Sbms M_NOWAIT | M_ZERO); 677189592Sbms if (nims == NULL) 678189592Sbms return (NULL); 679189592Sbms lims = (struct in_msource *)nims; 680189592Sbms lims->ims_haddr = ntohl(psin->sin_addr.s_addr); 681189592Sbms lims->imsl_st[0] = MCAST_UNDEFINED; 682189592Sbms lims->imsl_st[1] = st1; 683189592Sbms RB_INSERT(ip_msource_tree, &imf->imf_sources, nims); 684189592Sbms ++imf->imf_nsrc; 685189592Sbms 686189592Sbms return (lims); 687189592Sbms} 688189592Sbms 689189592Sbms/* 690189592Sbms * Prune a source entry from an existing socket-layer filter set, 691189592Sbms * maintaining any required invariants and checking allocations. 692189592Sbms * 693189592Sbms * The source is marked as being left at t1, it is not freed. 694189592Sbms * 695189592Sbms * Return 0 if no error occurred, otherwise return an errno value. 696189592Sbms */ 697189592Sbmsstatic int 698189592Sbmsimf_prune(struct in_mfilter *imf, const struct sockaddr_in *psin) 699189592Sbms{ 700189592Sbms struct ip_msource find; 701189592Sbms struct ip_msource *ims; 702189592Sbms struct in_msource *lims; 703189592Sbms 704189592Sbms /* key is host byte order */ 705189592Sbms find.ims_haddr = ntohl(psin->sin_addr.s_addr); 706189592Sbms ims = RB_FIND(ip_msource_tree, &imf->imf_sources, &find); 707189592Sbms if (ims == NULL) 708189592Sbms return (ENOENT); 709189592Sbms lims = (struct in_msource *)ims; 710189592Sbms lims->imsl_st[1] = MCAST_UNDEFINED; 711189592Sbms return (0); 712189592Sbms} 713189592Sbms 714189592Sbms/* 715189592Sbms * Revert socket-layer filter set deltas at t1 to t0 state. 716189592Sbms */ 717189592Sbmsstatic void 718189592Sbmsimf_rollback(struct in_mfilter *imf) 719189592Sbms{ 720189592Sbms struct ip_msource *ims, *tims; 721189592Sbms struct in_msource *lims; 722189592Sbms 723189592Sbms RB_FOREACH_SAFE(ims, ip_msource_tree, &imf->imf_sources, tims) { 724189592Sbms lims = (struct in_msource *)ims; 725189592Sbms if (lims->imsl_st[0] == lims->imsl_st[1]) { 726189592Sbms /* no change at t1 */ 727189592Sbms continue; 728189592Sbms } else if (lims->imsl_st[0] != MCAST_UNDEFINED) { 729189592Sbms /* revert change to existing source at t1 */ 730189592Sbms lims->imsl_st[1] = lims->imsl_st[0]; 731189592Sbms } else { 732189592Sbms /* revert source added t1 */ 733189592Sbms CTR2(KTR_IGMPV3, "%s: free ims %p", __func__, ims); 734189592Sbms RB_REMOVE(ip_msource_tree, &imf->imf_sources, ims); 735189592Sbms free(ims, M_INMFILTER); 736189592Sbms imf->imf_nsrc--; 737189592Sbms } 738189592Sbms } 739189592Sbms imf->imf_st[1] = imf->imf_st[0]; 740189592Sbms} 741189592Sbms 742189592Sbms/* 743189592Sbms * Mark socket-layer filter set as INCLUDE {} at t1. 744189592Sbms */ 745189592Sbmsstatic void 746189592Sbmsimf_leave(struct in_mfilter *imf) 747189592Sbms{ 748189592Sbms struct ip_msource *ims; 749189592Sbms struct in_msource *lims; 750189592Sbms 751189592Sbms RB_FOREACH(ims, ip_msource_tree, &imf->imf_sources) { 752189592Sbms lims = (struct in_msource *)ims; 753189592Sbms lims->imsl_st[1] = MCAST_UNDEFINED; 754189592Sbms } 755189592Sbms imf->imf_st[1] = MCAST_INCLUDE; 756189592Sbms} 757189592Sbms 758189592Sbms/* 759189592Sbms * Mark socket-layer filter set deltas as committed. 760189592Sbms */ 761189592Sbmsstatic void 762189592Sbmsimf_commit(struct in_mfilter *imf) 763189592Sbms{ 764189592Sbms struct ip_msource *ims; 765189592Sbms struct in_msource *lims; 766189592Sbms 767189592Sbms RB_FOREACH(ims, ip_msource_tree, &imf->imf_sources) { 768189592Sbms lims = (struct in_msource *)ims; 769189592Sbms lims->imsl_st[0] = lims->imsl_st[1]; 770189592Sbms } 771189592Sbms imf->imf_st[0] = imf->imf_st[1]; 772189592Sbms} 773189592Sbms 774189592Sbms/* 775189592Sbms * Reap unreferenced sources from socket-layer filter set. 776189592Sbms */ 777189592Sbmsstatic void 778189592Sbmsimf_reap(struct in_mfilter *imf) 779189592Sbms{ 780189592Sbms struct ip_msource *ims, *tims; 781189592Sbms struct in_msource *lims; 782189592Sbms 783189592Sbms RB_FOREACH_SAFE(ims, ip_msource_tree, &imf->imf_sources, tims) { 784189592Sbms lims = (struct in_msource *)ims; 785189592Sbms if ((lims->imsl_st[0] == MCAST_UNDEFINED) && 786189592Sbms (lims->imsl_st[1] == MCAST_UNDEFINED)) { 787189592Sbms CTR2(KTR_IGMPV3, "%s: free lims %p", __func__, ims); 788189592Sbms RB_REMOVE(ip_msource_tree, &imf->imf_sources, ims); 789189592Sbms free(ims, M_INMFILTER); 790189592Sbms imf->imf_nsrc--; 791189592Sbms } 792189592Sbms } 793189592Sbms} 794189592Sbms 795189592Sbms/* 796189592Sbms * Purge socket-layer filter set. 797189592Sbms */ 798189592Sbmsstatic void 799189592Sbmsimf_purge(struct in_mfilter *imf) 800189592Sbms{ 801189592Sbms struct ip_msource *ims, *tims; 802189592Sbms 803189592Sbms RB_FOREACH_SAFE(ims, ip_msource_tree, &imf->imf_sources, tims) { 804189592Sbms CTR2(KTR_IGMPV3, "%s: free ims %p", __func__, ims); 805189592Sbms RB_REMOVE(ip_msource_tree, &imf->imf_sources, ims); 806189592Sbms free(ims, M_INMFILTER); 807189592Sbms imf->imf_nsrc--; 808189592Sbms } 809189592Sbms imf->imf_st[0] = imf->imf_st[1] = MCAST_UNDEFINED; 810189592Sbms KASSERT(RB_EMPTY(&imf->imf_sources), 811189592Sbms ("%s: imf_sources not empty", __func__)); 812189592Sbms} 813189592Sbms 814189592Sbms/* 815189592Sbms * Look up a source filter entry for a multicast group. 816189592Sbms * 817189592Sbms * inm is the group descriptor to work with. 818189592Sbms * haddr is the host-byte-order IPv4 address to look up. 819189592Sbms * noalloc may be non-zero to suppress allocation of sources. 820189592Sbms * *pims will be set to the address of the retrieved or allocated source. 821189592Sbms * 822189592Sbms * SMPng: NOTE: may be called with locks held. 823189592Sbms * Return 0 if successful, otherwise return a non-zero error code. 824189592Sbms */ 825189592Sbmsstatic int 826189592Sbmsinm_get_source(struct in_multi *inm, const in_addr_t haddr, 827189592Sbms const int noalloc, struct ip_msource **pims) 828189592Sbms{ 829189592Sbms struct ip_msource find; 830189592Sbms struct ip_msource *ims, *nims; 831189592Sbms#ifdef KTR 832189592Sbms struct in_addr ia; 833189592Sbms#endif 834189592Sbms 835189592Sbms find.ims_haddr = haddr; 836189592Sbms ims = RB_FIND(ip_msource_tree, &inm->inm_srcs, &find); 837189592Sbms if (ims == NULL && !noalloc) { 838189592Sbms if (inm->inm_nsrc == in_mcast_maxgrpsrc) 839189592Sbms return (ENOSPC); 840189592Sbms nims = malloc(sizeof(struct ip_msource), M_IPMSOURCE, 841189592Sbms M_NOWAIT | M_ZERO); 842189592Sbms if (nims == NULL) 843189592Sbms return (ENOMEM); 844189592Sbms nims->ims_haddr = haddr; 845189592Sbms RB_INSERT(ip_msource_tree, &inm->inm_srcs, nims); 846189592Sbms ++inm->inm_nsrc; 847189592Sbms ims = nims; 848189592Sbms#ifdef KTR 849189592Sbms ia.s_addr = htonl(haddr); 850189592Sbms CTR3(KTR_IGMPV3, "%s: allocated %s as %p", __func__, 851189592Sbms inet_ntoa(ia), ims); 852189592Sbms#endif 853189592Sbms } 854189592Sbms 855189592Sbms *pims = ims; 856189592Sbms return (0); 857189592Sbms} 858189592Sbms 859189592Sbms/* 860189592Sbms * Merge socket-layer source into IGMP-layer source. 861189592Sbms * If rollback is non-zero, perform the inverse of the merge. 862189592Sbms */ 863189592Sbmsstatic void 864189592Sbmsims_merge(struct ip_msource *ims, const struct in_msource *lims, 865189592Sbms const int rollback) 866189592Sbms{ 867189592Sbms int n = rollback ? -1 : 1; 868189592Sbms#ifdef KTR 869189592Sbms struct in_addr ia; 870189592Sbms 871189592Sbms ia.s_addr = htonl(ims->ims_haddr); 872189592Sbms#endif 873189592Sbms 874189592Sbms if (lims->imsl_st[0] == MCAST_EXCLUDE) { 875189592Sbms CTR3(KTR_IGMPV3, "%s: t1 ex -= %d on %s", 876189592Sbms __func__, n, inet_ntoa(ia)); 877189592Sbms ims->ims_st[1].ex -= n; 878189592Sbms } else if (lims->imsl_st[0] == MCAST_INCLUDE) { 879189592Sbms CTR3(KTR_IGMPV3, "%s: t1 in -= %d on %s", 880189592Sbms __func__, n, inet_ntoa(ia)); 881189592Sbms ims->ims_st[1].in -= n; 882189592Sbms } 883189592Sbms 884189592Sbms if (lims->imsl_st[1] == MCAST_EXCLUDE) { 885189592Sbms CTR3(KTR_IGMPV3, "%s: t1 ex += %d on %s", 886189592Sbms __func__, n, inet_ntoa(ia)); 887189592Sbms ims->ims_st[1].ex += n; 888189592Sbms } else if (lims->imsl_st[1] == MCAST_INCLUDE) { 889189592Sbms CTR3(KTR_IGMPV3, "%s: t1 in += %d on %s", 890189592Sbms __func__, n, inet_ntoa(ia)); 891189592Sbms ims->ims_st[1].in += n; 892189592Sbms } 893189592Sbms} 894189592Sbms 895189592Sbms/* 896189592Sbms * Atomically update the global in_multi state, when a membership's 897189592Sbms * filter list is being updated in any way. 898189592Sbms * 899189592Sbms * imf is the per-inpcb-membership group filter pointer. 900189592Sbms * A fake imf may be passed for in-kernel consumers. 901189592Sbms * 902189592Sbms * XXX This is a candidate for a set-symmetric-difference style loop 903189592Sbms * which would eliminate the repeated lookup from root of ims nodes, 904189592Sbms * as they share the same key space. 905189592Sbms * 906189592Sbms * If any error occurred this function will back out of refcounts 907189592Sbms * and return a non-zero value. 908189592Sbms */ 909189592Sbmsstatic int 910189592Sbmsinm_merge(struct in_multi *inm, /*const*/ struct in_mfilter *imf) 911189592Sbms{ 912189592Sbms struct ip_msource *ims, *nims; 913189592Sbms struct in_msource *lims; 914189592Sbms int schanged, error; 915189592Sbms int nsrc0, nsrc1; 916189592Sbms 917189592Sbms schanged = 0; 918189592Sbms error = 0; 919189592Sbms nsrc1 = nsrc0 = 0; 920189592Sbms 921189592Sbms /* 922189592Sbms * Update the source filters first, as this may fail. 923189592Sbms * Maintain count of in-mode filters at t0, t1. These are 924189592Sbms * used to work out if we transition into ASM mode or not. 925189592Sbms * Maintain a count of source filters whose state was 926189592Sbms * actually modified by this operation. 927189592Sbms */ 928189592Sbms RB_FOREACH(ims, ip_msource_tree, &imf->imf_sources) { 929189592Sbms lims = (struct in_msource *)ims; 930189592Sbms if (lims->imsl_st[0] == imf->imf_st[0]) nsrc0++; 931189592Sbms if (lims->imsl_st[1] == imf->imf_st[1]) nsrc1++; 932189592Sbms if (lims->imsl_st[0] == lims->imsl_st[1]) continue; 933189592Sbms error = inm_get_source(inm, lims->ims_haddr, 0, &nims); 934189592Sbms ++schanged; 935189592Sbms if (error) 936170613Sbms break; 937189592Sbms ims_merge(nims, lims, 0); 938189592Sbms } 939189592Sbms if (error) { 940189592Sbms struct ip_msource *bims; 941189592Sbms 942189592Sbms RB_FOREACH_REVERSE_FROM(ims, ip_msource_tree, nims) { 943189592Sbms lims = (struct in_msource *)ims; 944189592Sbms if (lims->imsl_st[0] == lims->imsl_st[1]) 945189592Sbms continue; 946189592Sbms (void)inm_get_source(inm, lims->ims_haddr, 1, &bims); 947189592Sbms if (bims == NULL) 948189592Sbms continue; 949189592Sbms ims_merge(bims, lims, 1); 950170613Sbms } 951189592Sbms goto out_reap; 952189592Sbms } 953170613Sbms 954189592Sbms CTR3(KTR_IGMPV3, "%s: imf filters in-mode: %d at t0, %d at t1", 955189592Sbms __func__, nsrc0, nsrc1); 956170613Sbms 957189592Sbms /* Handle transition between INCLUDE {n} and INCLUDE {} on socket. */ 958189592Sbms if (imf->imf_st[0] == imf->imf_st[1] && 959189592Sbms imf->imf_st[1] == MCAST_INCLUDE) { 960189592Sbms if (nsrc1 == 0) { 961189592Sbms CTR1(KTR_IGMPV3, "%s: --in on inm at t1", __func__); 962189592Sbms --inm->inm_st[1].iss_in; 963189592Sbms } 964189592Sbms } 965170613Sbms 966189592Sbms /* Handle filter mode transition on socket. */ 967189592Sbms if (imf->imf_st[0] != imf->imf_st[1]) { 968189592Sbms CTR3(KTR_IGMPV3, "%s: imf transition %d to %d", 969189592Sbms __func__, imf->imf_st[0], imf->imf_st[1]); 970189592Sbms 971189592Sbms if (imf->imf_st[0] == MCAST_EXCLUDE) { 972189592Sbms CTR1(KTR_IGMPV3, "%s: --ex on inm at t1", __func__); 973189592Sbms --inm->inm_st[1].iss_ex; 974189592Sbms } else if (imf->imf_st[0] == MCAST_INCLUDE) { 975189592Sbms CTR1(KTR_IGMPV3, "%s: --in on inm at t1", __func__); 976189592Sbms --inm->inm_st[1].iss_in; 977189592Sbms } 978189592Sbms 979189592Sbms if (imf->imf_st[1] == MCAST_EXCLUDE) { 980189592Sbms CTR1(KTR_IGMPV3, "%s: ex++ on inm at t1", __func__); 981189592Sbms inm->inm_st[1].iss_ex++; 982189592Sbms } else if (imf->imf_st[1] == MCAST_INCLUDE && nsrc1 > 0) { 983189592Sbms CTR1(KTR_IGMPV3, "%s: in++ on inm at t1", __func__); 984189592Sbms inm->inm_st[1].iss_in++; 985189592Sbms } 986189592Sbms } 987189592Sbms 988189592Sbms /* 989189592Sbms * Track inm filter state in terms of listener counts. 990189592Sbms * If there are any exclusive listeners, stack-wide 991189592Sbms * membership is exclusive. 992189592Sbms * Otherwise, if only inclusive listeners, stack-wide is inclusive. 993189592Sbms * If no listeners remain, state is undefined at t1, 994189592Sbms * and the IGMP lifecycle for this group should finish. 995189592Sbms */ 996189592Sbms if (inm->inm_st[1].iss_ex > 0) { 997189592Sbms CTR1(KTR_IGMPV3, "%s: transition to EX", __func__); 998189592Sbms inm->inm_st[1].iss_fmode = MCAST_EXCLUDE; 999189592Sbms } else if (inm->inm_st[1].iss_in > 0) { 1000189592Sbms CTR1(KTR_IGMPV3, "%s: transition to IN", __func__); 1001189592Sbms inm->inm_st[1].iss_fmode = MCAST_INCLUDE; 1002189592Sbms } else { 1003189592Sbms CTR1(KTR_IGMPV3, "%s: transition to UNDEF", __func__); 1004189592Sbms inm->inm_st[1].iss_fmode = MCAST_UNDEFINED; 1005189592Sbms } 1006189592Sbms 1007189592Sbms /* Decrement ASM listener count on transition out of ASM mode. */ 1008189592Sbms if (imf->imf_st[0] == MCAST_EXCLUDE && nsrc0 == 0) { 1009189592Sbms if ((imf->imf_st[1] != MCAST_EXCLUDE) || 1010189592Sbms (imf->imf_st[1] == MCAST_EXCLUDE && nsrc1 > 0)) 1011189592Sbms CTR1(KTR_IGMPV3, "%s: --asm on inm at t1", __func__); 1012189592Sbms --inm->inm_st[1].iss_asm; 1013189592Sbms } 1014189592Sbms 1015189592Sbms /* Increment ASM listener count on transition to ASM mode. */ 1016189592Sbms if (imf->imf_st[1] == MCAST_EXCLUDE && nsrc1 == 0) { 1017189592Sbms CTR1(KTR_IGMPV3, "%s: asm++ on inm at t1", __func__); 1018189592Sbms inm->inm_st[1].iss_asm++; 1019189592Sbms } 1020189592Sbms 1021189592Sbms CTR3(KTR_IGMPV3, "%s: merged imf %p to inm %p", __func__, imf, inm); 1022189592Sbms inm_print(inm); 1023189592Sbms 1024189592Sbmsout_reap: 1025189592Sbms if (schanged > 0) { 1026189592Sbms CTR1(KTR_IGMPV3, "%s: sources changed; reaping", __func__); 1027189592Sbms inm_reap(inm); 1028189592Sbms } 1029189592Sbms return (error); 1030189592Sbms} 1031189592Sbms 1032189592Sbms/* 1033189592Sbms * Mark an in_multi's filter set deltas as committed. 1034189592Sbms * Called by IGMP after a state change has been enqueued. 1035189592Sbms */ 1036189592Sbmsvoid 1037189592Sbmsinm_commit(struct in_multi *inm) 1038189592Sbms{ 1039189592Sbms struct ip_msource *ims; 1040189592Sbms 1041189592Sbms CTR2(KTR_IGMPV3, "%s: commit inm %p", __func__, inm); 1042189592Sbms CTR1(KTR_IGMPV3, "%s: pre commit:", __func__); 1043189592Sbms inm_print(inm); 1044189592Sbms 1045189592Sbms RB_FOREACH(ims, ip_msource_tree, &inm->inm_srcs) { 1046189592Sbms ims->ims_st[0] = ims->ims_st[1]; 1047189592Sbms } 1048189592Sbms inm->inm_st[0] = inm->inm_st[1]; 1049189592Sbms} 1050189592Sbms 1051189592Sbms/* 1052189592Sbms * Reap unreferenced nodes from an in_multi's filter set. 1053189592Sbms */ 1054189592Sbmsstatic void 1055189592Sbmsinm_reap(struct in_multi *inm) 1056189592Sbms{ 1057189592Sbms struct ip_msource *ims, *tims; 1058189592Sbms 1059189592Sbms RB_FOREACH_SAFE(ims, ip_msource_tree, &inm->inm_srcs, tims) { 1060189592Sbms if (ims->ims_st[0].ex > 0 || ims->ims_st[0].in > 0 || 1061189592Sbms ims->ims_st[1].ex > 0 || ims->ims_st[1].in > 0 || 1062189592Sbms ims->ims_stp != 0) 1063189592Sbms continue; 1064189592Sbms CTR2(KTR_IGMPV3, "%s: free ims %p", __func__, ims); 1065189592Sbms RB_REMOVE(ip_msource_tree, &inm->inm_srcs, ims); 1066189592Sbms free(ims, M_IPMSOURCE); 1067189592Sbms inm->inm_nsrc--; 1068189592Sbms } 1069189592Sbms} 1070189592Sbms 1071189592Sbms/* 1072189592Sbms * Purge all source nodes from an in_multi's filter set. 1073189592Sbms */ 1074189592Sbmsstatic void 1075189592Sbmsinm_purge(struct in_multi *inm) 1076189592Sbms{ 1077189592Sbms struct ip_msource *ims, *tims; 1078189592Sbms 1079189592Sbms RB_FOREACH_SAFE(ims, ip_msource_tree, &inm->inm_srcs, tims) { 1080189592Sbms CTR2(KTR_IGMPV3, "%s: free ims %p", __func__, ims); 1081189592Sbms RB_REMOVE(ip_msource_tree, &inm->inm_srcs, ims); 1082189592Sbms free(ims, M_IPMSOURCE); 1083189592Sbms inm->inm_nsrc--; 1084189592Sbms } 1085189592Sbms} 1086189592Sbms 1087189592Sbms/* 1088189592Sbms * Join a multicast group; unlocked entry point. 1089189592Sbms * 1090189592Sbms * SMPng: XXX: in_joingroup() is called from in_control() when Giant 1091189592Sbms * is not held. Fortunately, ifp is unlikely to have been detached 1092189592Sbms * at this point, so we assume it's OK to recurse. 1093189592Sbms */ 1094189592Sbmsint 1095189592Sbmsin_joingroup(struct ifnet *ifp, const struct in_addr *gina, 1096189592Sbms /*const*/ struct in_mfilter *imf, struct in_multi **pinm) 1097189592Sbms{ 1098189592Sbms int error; 1099189592Sbms 1100189592Sbms IN_MULTI_LOCK(); 1101189592Sbms error = in_joingroup_locked(ifp, gina, imf, pinm); 1102170613Sbms IN_MULTI_UNLOCK(); 1103170613Sbms 1104189592Sbms return (error); 1105170613Sbms} 1106170613Sbms 1107170613Sbms/* 1108189592Sbms * Join a multicast group; real entry point. 1109170613Sbms * 1110189592Sbms * Only preserves atomicity at inm level. 1111189592Sbms * NOTE: imf argument cannot be const due to sys/tree.h limitations. 1112170613Sbms * 1113189592Sbms * If the IGMP downcall fails, the group is not joined, and an error 1114189592Sbms * code is returned. 1115170613Sbms */ 1116189592Sbmsint 1117189592Sbmsin_joingroup_locked(struct ifnet *ifp, const struct in_addr *gina, 1118189592Sbms /*const*/ struct in_mfilter *imf, struct in_multi **pinm) 1119170613Sbms{ 1120189592Sbms struct in_mfilter timf; 1121189592Sbms struct in_multi *inm; 1122189592Sbms int error; 1123170613Sbms 1124189592Sbms IN_MULTI_LOCK_ASSERT(); 1125170613Sbms 1126189592Sbms CTR4(KTR_IGMPV3, "%s: join %s on %p(%s))", __func__, 1127189592Sbms inet_ntoa(*gina), ifp, ifp->if_xname); 1128189592Sbms 1129189592Sbms error = 0; 1130189592Sbms inm = NULL; 1131189592Sbms 1132189592Sbms /* 1133189592Sbms * If no imf was specified (i.e. kernel consumer), 1134189592Sbms * fake one up and assume it is an ASM join. 1135189592Sbms */ 1136189592Sbms if (imf == NULL) { 1137189592Sbms imf_init(&timf, MCAST_UNDEFINED, MCAST_EXCLUDE); 1138189592Sbms imf = &timf; 1139170613Sbms } 1140170613Sbms 1141189592Sbms error = in_getmulti(ifp, gina, &inm); 1142189592Sbms if (error) { 1143189592Sbms CTR1(KTR_IGMPV3, "%s: in_getmulti() failure", __func__); 1144189592Sbms return (error); 1145189592Sbms } 1146189592Sbms 1147189592Sbms CTR1(KTR_IGMPV3, "%s: merge inm state", __func__); 1148189592Sbms error = inm_merge(inm, imf); 1149189592Sbms if (error) { 1150189592Sbms CTR1(KTR_IGMPV3, "%s: failed to merge inm state", __func__); 1151189592Sbms goto out_inm_release; 1152189592Sbms } 1153189592Sbms 1154189592Sbms CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__); 1155189592Sbms error = igmp_change_state(inm); 1156189592Sbms if (error) { 1157189592Sbms CTR1(KTR_IGMPV3, "%s: failed to update source", __func__); 1158189592Sbms goto out_inm_release; 1159189592Sbms } 1160189592Sbms 1161189592Sbmsout_inm_release: 1162189592Sbms if (error) { 1163189592Sbms CTR2(KTR_IGMPV3, "%s: dropping ref on %p", __func__, inm); 1164189592Sbms inm_release_locked(inm); 1165189592Sbms } else { 1166189592Sbms *pinm = inm; 1167189592Sbms } 1168189592Sbms 1169189592Sbms return (error); 1170189592Sbms} 1171189592Sbms 1172189592Sbms/* 1173189592Sbms * Leave a multicast group; unlocked entry point. 1174189592Sbms */ 1175189592Sbmsint 1176189592Sbmsin_leavegroup(struct in_multi *inm, /*const*/ struct in_mfilter *imf) 1177189592Sbms{ 1178189592Sbms struct ifnet *ifp; 1179189851Srwatson int error; 1180189592Sbms 1181189592Sbms ifp = inm->inm_ifp; 1182189592Sbms 1183170613Sbms IN_MULTI_LOCK(); 1184189592Sbms error = in_leavegroup_locked(inm, imf); 1185170613Sbms IN_MULTI_UNLOCK(); 1186170613Sbms 1187189592Sbms return (error); 1188170613Sbms} 1189170613Sbms 1190170613Sbms/* 1191189592Sbms * Leave a multicast group; real entry point. 1192189592Sbms * All source filters will be expunged. 1193170613Sbms * 1194189592Sbms * Only preserves atomicity at inm level. 1195189592Sbms * 1196189592Sbms * Holding the write lock for the INP which contains imf 1197189592Sbms * is highly advisable. We can't assert for it as imf does not 1198189592Sbms * contain a back-pointer to the owning inp. 1199189592Sbms * 1200189592Sbms * Note: This is not the same as inm_release(*) as this function also 1201189592Sbms * makes a state change downcall into IGMP. 1202170613Sbms */ 1203189592Sbmsint 1204189592Sbmsin_leavegroup_locked(struct in_multi *inm, /*const*/ struct in_mfilter *imf) 1205170613Sbms{ 1206189592Sbms struct in_mfilter timf; 1207189592Sbms int error; 1208170613Sbms 1209189592Sbms error = 0; 1210189592Sbms 1211170613Sbms IN_MULTI_LOCK_ASSERT(); 1212170613Sbms 1213189592Sbms CTR5(KTR_IGMPV3, "%s: leave inm %p, %s/%s, imf %p", __func__, 1214189592Sbms inm, inet_ntoa(inm->inm_addr), 1215189592Sbms (inm_is_ifp_detached(inm) ? "null" : inm->inm_ifp->if_xname), 1216189592Sbms imf); 1217170613Sbms 1218189592Sbms /* 1219189592Sbms * If no imf was specified (i.e. kernel consumer), 1220189592Sbms * fake one up and assume it is an ASM join. 1221189592Sbms */ 1222189592Sbms if (imf == NULL) { 1223189592Sbms imf_init(&timf, MCAST_EXCLUDE, MCAST_UNDEFINED); 1224189592Sbms imf = &timf; 1225189592Sbms } 1226170613Sbms 1227189592Sbms /* 1228189592Sbms * Begin state merge transaction at IGMP layer. 1229189592Sbms * 1230189592Sbms * As this particular invocation should not cause any memory 1231189592Sbms * to be allocated, and there is no opportunity to roll back 1232189592Sbms * the transaction, it MUST NOT fail. 1233189592Sbms */ 1234189592Sbms CTR1(KTR_IGMPV3, "%s: merge inm state", __func__); 1235189592Sbms error = inm_merge(inm, imf); 1236189592Sbms KASSERT(error == 0, ("%s: failed to merge inm state", __func__)); 1237170613Sbms 1238189592Sbms CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__); 1239189592Sbms error = igmp_change_state(inm); 1240189592Sbms if (error) 1241189592Sbms CTR1(KTR_IGMPV3, "%s: failed igmp downcall", __func__); 1242189592Sbms 1243189592Sbms CTR2(KTR_IGMPV3, "%s: dropping ref on %p", __func__, inm); 1244189592Sbms inm_release_locked(inm); 1245189592Sbms 1246189592Sbms return (error); 1247170613Sbms} 1248170613Sbms 1249189592Sbms/*#ifndef BURN_BRIDGES*/ 1250170613Sbms/* 1251189592Sbms * Join an IPv4 multicast group in (*,G) exclusive mode. 1252189592Sbms * The group must be a 224.0.0.0/24 link-scope group. 1253189592Sbms * This KPI is for legacy kernel consumers only. 1254170613Sbms */ 1255189592Sbmsstruct in_multi * 1256189592Sbmsin_addmulti(struct in_addr *ap, struct ifnet *ifp) 1257189592Sbms{ 1258189592Sbms struct in_multi *pinm; 1259189592Sbms int error; 1260189592Sbms 1261189592Sbms KASSERT(IN_LOCAL_GROUP(ntohl(ap->s_addr)), 1262189592Sbms ("%s: %s not in 224.0.0.0/24", __func__, inet_ntoa(*ap))); 1263189592Sbms 1264189592Sbms error = in_joingroup(ifp, ap, NULL, &pinm); 1265189592Sbms if (error != 0) 1266189592Sbms pinm = NULL; 1267189592Sbms 1268189592Sbms return (pinm); 1269189592Sbms} 1270189592Sbms 1271189592Sbms/* 1272189592Sbms * Leave an IPv4 multicast group, assumed to be in exclusive (*,G) mode. 1273189592Sbms * This KPI is for legacy kernel consumers only. 1274189592Sbms */ 1275189592Sbmsvoid 1276189592Sbmsin_delmulti(struct in_multi *inm) 1277189592Sbms{ 1278189592Sbms 1279189592Sbms (void)in_leavegroup(inm, NULL); 1280189592Sbms} 1281189592Sbms/*#endif*/ 1282189592Sbms 1283189592Sbms/* 1284189592Sbms * Block or unblock an ASM multicast source on an inpcb. 1285189592Sbms * This implements the delta-based API described in RFC 3678. 1286189592Sbms * 1287189592Sbms * The delta-based API applies only to exclusive-mode memberships. 1288189592Sbms * An IGMP downcall will be performed. 1289189592Sbms * 1290189592Sbms * SMPng: NOTE: Must take Giant as a join may create a new ifma. 1291189592Sbms * 1292189592Sbms * Return 0 if successful, otherwise return an appropriate error code. 1293189592Sbms */ 1294170613Sbmsstatic int 1295189592Sbmsinp_block_unblock_source(struct inpcb *inp, struct sockopt *sopt) 1296170613Sbms{ 1297170613Sbms struct group_source_req gsr; 1298170613Sbms sockunion_t *gsa, *ssa; 1299170613Sbms struct ifnet *ifp; 1300170613Sbms struct in_mfilter *imf; 1301170613Sbms struct ip_moptions *imo; 1302170613Sbms struct in_msource *ims; 1303189592Sbms struct in_multi *inm; 1304170613Sbms size_t idx; 1305189592Sbms uint16_t fmode; 1306189592Sbms int error, doblock; 1307170613Sbms 1308170613Sbms ifp = NULL; 1309170613Sbms error = 0; 1310189592Sbms doblock = 0; 1311170613Sbms 1312170613Sbms memset(&gsr, 0, sizeof(struct group_source_req)); 1313170613Sbms gsa = (sockunion_t *)&gsr.gsr_group; 1314170613Sbms ssa = (sockunion_t *)&gsr.gsr_source; 1315170613Sbms 1316170613Sbms switch (sopt->sopt_name) { 1317170613Sbms case IP_BLOCK_SOURCE: 1318170613Sbms case IP_UNBLOCK_SOURCE: { 1319170613Sbms struct ip_mreq_source mreqs; 1320170613Sbms 1321170613Sbms error = sooptcopyin(sopt, &mreqs, 1322170613Sbms sizeof(struct ip_mreq_source), 1323170613Sbms sizeof(struct ip_mreq_source)); 1324170613Sbms if (error) 1325170613Sbms return (error); 1326170613Sbms 1327170613Sbms gsa->sin.sin_family = AF_INET; 1328170613Sbms gsa->sin.sin_len = sizeof(struct sockaddr_in); 1329170613Sbms gsa->sin.sin_addr = mreqs.imr_multiaddr; 1330170613Sbms 1331170613Sbms ssa->sin.sin_family = AF_INET; 1332170613Sbms ssa->sin.sin_len = sizeof(struct sockaddr_in); 1333170613Sbms ssa->sin.sin_addr = mreqs.imr_sourceaddr; 1334170613Sbms 1335189592Sbms if (!in_nullhost(mreqs.imr_interface)) 1336170613Sbms INADDR_TO_IFP(mreqs.imr_interface, ifp); 1337170613Sbms 1338170613Sbms if (sopt->sopt_name == IP_BLOCK_SOURCE) 1339189592Sbms doblock = 1; 1340170613Sbms 1341189592Sbms CTR3(KTR_IGMPV3, "%s: imr_interface = %s, ifp = %p", 1342189592Sbms __func__, inet_ntoa(mreqs.imr_interface), ifp); 1343170613Sbms break; 1344170613Sbms } 1345170613Sbms 1346170613Sbms case MCAST_BLOCK_SOURCE: 1347170613Sbms case MCAST_UNBLOCK_SOURCE: 1348170613Sbms error = sooptcopyin(sopt, &gsr, 1349170613Sbms sizeof(struct group_source_req), 1350170613Sbms sizeof(struct group_source_req)); 1351170613Sbms if (error) 1352170613Sbms return (error); 1353170613Sbms 1354170613Sbms if (gsa->sin.sin_family != AF_INET || 1355170613Sbms gsa->sin.sin_len != sizeof(struct sockaddr_in)) 1356170613Sbms return (EINVAL); 1357170613Sbms 1358170613Sbms if (ssa->sin.sin_family != AF_INET || 1359170613Sbms ssa->sin.sin_len != sizeof(struct sockaddr_in)) 1360170613Sbms return (EINVAL); 1361170613Sbms 1362181803Sbz if (gsr.gsr_interface == 0 || V_if_index < gsr.gsr_interface) 1363170613Sbms return (EADDRNOTAVAIL); 1364170613Sbms 1365170613Sbms ifp = ifnet_byindex(gsr.gsr_interface); 1366170613Sbms 1367170613Sbms if (sopt->sopt_name == MCAST_BLOCK_SOURCE) 1368189592Sbms doblock = 1; 1369170613Sbms break; 1370170613Sbms 1371170613Sbms default: 1372189592Sbms CTR2(KTR_IGMPV3, "%s: unknown sopt_name %d", 1373189592Sbms __func__, sopt->sopt_name); 1374170613Sbms return (EOPNOTSUPP); 1375170613Sbms break; 1376170613Sbms } 1377170613Sbms 1378170613Sbms if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr))) 1379170613Sbms return (EINVAL); 1380170613Sbms 1381170613Sbms /* 1382170613Sbms * Check if we are actually a member of this group. 1383170613Sbms */ 1384170613Sbms imo = inp_findmoptions(inp); 1385170613Sbms idx = imo_match_group(imo, ifp, &gsa->sa); 1386170613Sbms if (idx == -1 || imo->imo_mfilters == NULL) { 1387170613Sbms error = EADDRNOTAVAIL; 1388189592Sbms goto out_inp_locked; 1389170613Sbms } 1390170613Sbms 1391170613Sbms KASSERT(imo->imo_mfilters != NULL, 1392170613Sbms ("%s: imo_mfilters not allocated", __func__)); 1393170613Sbms imf = &imo->imo_mfilters[idx]; 1394189592Sbms inm = imo->imo_membership[idx]; 1395170613Sbms 1396170613Sbms /* 1397189592Sbms * Attempting to use the delta-based API on an 1398189592Sbms * non exclusive-mode membership is an error. 1399170613Sbms */ 1400189592Sbms fmode = imf->imf_st[0]; 1401189592Sbms if (fmode != MCAST_EXCLUDE) { 1402189592Sbms error = EINVAL; 1403189592Sbms goto out_inp_locked; 1404170613Sbms } 1405189592Sbms 1406189592Sbms /* 1407189592Sbms * Deal with error cases up-front: 1408189592Sbms * Asked to block, but already blocked; or 1409189592Sbms * Asked to unblock, but nothing to unblock. 1410189592Sbms * If adding a new block entry, allocate it. 1411189592Sbms */ 1412170613Sbms ims = imo_match_source(imo, idx, &ssa->sa); 1413189592Sbms if ((ims != NULL && doblock) || (ims == NULL && !doblock)) { 1414189592Sbms CTR3(KTR_IGMPV3, "%s: source %s %spresent", __func__, 1415189592Sbms inet_ntoa(ssa->sin.sin_addr), doblock ? "" : "not "); 1416189592Sbms error = EADDRNOTAVAIL; 1417189592Sbms goto out_inp_locked; 1418189592Sbms } 1419189592Sbms 1420189592Sbms INP_WLOCK_ASSERT(inp); 1421189592Sbms 1422189592Sbms /* 1423189592Sbms * Begin state merge transaction at socket layer. 1424189592Sbms */ 1425189592Sbms if (doblock) { 1426189592Sbms CTR2(KTR_IGMPV3, "%s: %s source", __func__, "block"); 1427189592Sbms ims = imf_graft(imf, fmode, &ssa->sin); 1428189592Sbms if (ims == NULL) 1429189592Sbms error = ENOMEM; 1430170613Sbms } else { 1431189592Sbms CTR2(KTR_IGMPV3, "%s: %s source", __func__, "allow"); 1432189592Sbms error = imf_prune(imf, &ssa->sin); 1433170613Sbms } 1434170613Sbms 1435189592Sbms if (error) { 1436189592Sbms CTR1(KTR_IGMPV3, "%s: merge imf state failed", __func__); 1437189592Sbms goto out_imf_rollback; 1438189592Sbms } 1439189592Sbms 1440189592Sbms /* 1441189592Sbms * Begin state merge transaction at IGMP layer. 1442189592Sbms */ 1443189592Sbms IN_MULTI_LOCK(); 1444189592Sbms 1445189592Sbms CTR1(KTR_IGMPV3, "%s: merge inm state", __func__); 1446189592Sbms error = inm_merge(inm, imf); 1447189592Sbms if (error) { 1448189592Sbms CTR1(KTR_IGMPV3, "%s: failed to merge inm state", __func__); 1449261694Sgnn goto out_in_multi_locked; 1450189592Sbms } 1451189592Sbms 1452189592Sbms CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__); 1453189592Sbms error = igmp_change_state(inm); 1454189592Sbms if (error) 1455189592Sbms CTR1(KTR_IGMPV3, "%s: failed igmp downcall", __func__); 1456189592Sbms 1457261694Sgnnout_in_multi_locked: 1458261694Sgnn 1459189592Sbms IN_MULTI_UNLOCK(); 1460189592Sbms 1461189592Sbmsout_imf_rollback: 1462189592Sbms if (error) 1463189592Sbms imf_rollback(imf); 1464189592Sbms else 1465189592Sbms imf_commit(imf); 1466189592Sbms 1467189592Sbms imf_reap(imf); 1468189592Sbms 1469189592Sbmsout_inp_locked: 1470178285Srwatson INP_WUNLOCK(inp); 1471170613Sbms return (error); 1472170613Sbms} 1473170613Sbms 1474170613Sbms/* 1475170613Sbms * Given an inpcb, return its multicast options structure pointer. Accepts 1476170613Sbms * an unlocked inpcb pointer, but will return it locked. May sleep. 1477189592Sbms * 1478189592Sbms * SMPng: NOTE: Potentially calls malloc(M_WAITOK) with Giant held. 1479189592Sbms * SMPng: NOTE: Returns with the INP write lock held. 1480170613Sbms */ 1481170613Sbmsstatic struct ip_moptions * 1482170613Sbmsinp_findmoptions(struct inpcb *inp) 1483170613Sbms{ 1484170613Sbms struct ip_moptions *imo; 1485170613Sbms struct in_multi **immp; 1486170613Sbms struct in_mfilter *imfp; 1487170613Sbms size_t idx; 1488170613Sbms 1489178285Srwatson INP_WLOCK(inp); 1490170613Sbms if (inp->inp_moptions != NULL) 1491170613Sbms return (inp->inp_moptions); 1492170613Sbms 1493178285Srwatson INP_WUNLOCK(inp); 1494170613Sbms 1495189592Sbms imo = malloc(sizeof(*imo), M_IPMOPTS, M_WAITOK); 1496189592Sbms immp = malloc(sizeof(*immp) * IP_MIN_MEMBERSHIPS, M_IPMOPTS, 1497189592Sbms M_WAITOK | M_ZERO); 1498189592Sbms imfp = malloc(sizeof(struct in_mfilter) * IP_MIN_MEMBERSHIPS, 1499189592Sbms M_INMFILTER, M_WAITOK); 1500170613Sbms 1501170613Sbms imo->imo_multicast_ifp = NULL; 1502170613Sbms imo->imo_multicast_addr.s_addr = INADDR_ANY; 1503170613Sbms imo->imo_multicast_vif = -1; 1504170613Sbms imo->imo_multicast_ttl = IP_DEFAULT_MULTICAST_TTL; 1505189357Sbms imo->imo_multicast_loop = in_mcast_loop; 1506170613Sbms imo->imo_num_memberships = 0; 1507170613Sbms imo->imo_max_memberships = IP_MIN_MEMBERSHIPS; 1508170613Sbms imo->imo_membership = immp; 1509170613Sbms 1510170613Sbms /* Initialize per-group source filters. */ 1511189592Sbms for (idx = 0; idx < IP_MIN_MEMBERSHIPS; idx++) 1512189592Sbms imf_init(&imfp[idx], MCAST_UNDEFINED, MCAST_EXCLUDE); 1513170613Sbms imo->imo_mfilters = imfp; 1514170613Sbms 1515178285Srwatson INP_WLOCK(inp); 1516170613Sbms if (inp->inp_moptions != NULL) { 1517189592Sbms free(imfp, M_INMFILTER); 1518170613Sbms free(immp, M_IPMOPTS); 1519170613Sbms free(imo, M_IPMOPTS); 1520170613Sbms return (inp->inp_moptions); 1521170613Sbms } 1522170613Sbms inp->inp_moptions = imo; 1523170613Sbms return (imo); 1524170613Sbms} 1525170613Sbms 1526170613Sbms/* 1527170613Sbms * Discard the IP multicast options (and source filters). 1528189592Sbms * 1529189592Sbms * SMPng: NOTE: assumes INP write lock is held. 1530170613Sbms */ 1531170613Sbmsvoid 1532170613Sbmsinp_freemoptions(struct ip_moptions *imo) 1533170613Sbms{ 1534170613Sbms struct in_mfilter *imf; 1535170613Sbms size_t idx, nmships; 1536170613Sbms 1537170613Sbms KASSERT(imo != NULL, ("%s: ip_moptions is NULL", __func__)); 1538170613Sbms 1539170613Sbms nmships = imo->imo_num_memberships; 1540170613Sbms for (idx = 0; idx < nmships; ++idx) { 1541189592Sbms imf = imo->imo_mfilters ? &imo->imo_mfilters[idx] : NULL; 1542189592Sbms if (imf) 1543189592Sbms imf_leave(imf); 1544189592Sbms (void)in_leavegroup(imo->imo_membership[idx], imf); 1545189592Sbms if (imf) 1546189592Sbms imf_purge(imf); 1547170613Sbms } 1548170613Sbms 1549189592Sbms if (imo->imo_mfilters) 1550189592Sbms free(imo->imo_mfilters, M_INMFILTER); 1551170613Sbms free(imo->imo_membership, M_IPMOPTS); 1552170613Sbms free(imo, M_IPMOPTS); 1553170613Sbms} 1554170613Sbms 1555170613Sbms/* 1556170613Sbms * Atomically get source filters on a socket for an IPv4 multicast group. 1557170613Sbms * Called with INP lock held; returns with lock released. 1558170613Sbms */ 1559170613Sbmsstatic int 1560170613Sbmsinp_get_source_filters(struct inpcb *inp, struct sockopt *sopt) 1561170613Sbms{ 1562170613Sbms struct __msfilterreq msfr; 1563170613Sbms sockunion_t *gsa; 1564170613Sbms struct ifnet *ifp; 1565170613Sbms struct ip_moptions *imo; 1566170613Sbms struct in_mfilter *imf; 1567189592Sbms struct ip_msource *ims; 1568189592Sbms struct in_msource *lims; 1569189592Sbms struct sockaddr_in *psin; 1570170613Sbms struct sockaddr_storage *ptss; 1571170613Sbms struct sockaddr_storage *tss; 1572170613Sbms int error; 1573189592Sbms size_t idx, nsrcs, ncsrcs; 1574170613Sbms 1575178285Srwatson INP_WLOCK_ASSERT(inp); 1576170613Sbms 1577170613Sbms imo = inp->inp_moptions; 1578170613Sbms KASSERT(imo != NULL, ("%s: null ip_moptions", __func__)); 1579170613Sbms 1580178285Srwatson INP_WUNLOCK(inp); 1581170613Sbms 1582170613Sbms error = sooptcopyin(sopt, &msfr, sizeof(struct __msfilterreq), 1583170613Sbms sizeof(struct __msfilterreq)); 1584170613Sbms if (error) 1585170613Sbms return (error); 1586170613Sbms 1587181803Sbz if (msfr.msfr_ifindex == 0 || V_if_index < msfr.msfr_ifindex) 1588170613Sbms return (EINVAL); 1589170613Sbms 1590170613Sbms ifp = ifnet_byindex(msfr.msfr_ifindex); 1591170613Sbms if (ifp == NULL) 1592170613Sbms return (EINVAL); 1593170613Sbms 1594178285Srwatson INP_WLOCK(inp); 1595170613Sbms 1596170613Sbms /* 1597170613Sbms * Lookup group on the socket. 1598170613Sbms */ 1599170613Sbms gsa = (sockunion_t *)&msfr.msfr_group; 1600170613Sbms idx = imo_match_group(imo, ifp, &gsa->sa); 1601170613Sbms if (idx == -1 || imo->imo_mfilters == NULL) { 1602178285Srwatson INP_WUNLOCK(inp); 1603170613Sbms return (EADDRNOTAVAIL); 1604170613Sbms } 1605170613Sbms imf = &imo->imo_mfilters[idx]; 1606170613Sbms 1607170613Sbms /* 1608189592Sbms * Ignore memberships which are in limbo. 1609189592Sbms */ 1610189592Sbms if (imf->imf_st[1] == MCAST_UNDEFINED) { 1611189592Sbms INP_WUNLOCK(inp); 1612189592Sbms return (EAGAIN); 1613189592Sbms } 1614189592Sbms msfr.msfr_fmode = imf->imf_st[1]; 1615189592Sbms 1616189592Sbms /* 1617170613Sbms * If the user specified a buffer, copy out the source filter 1618170613Sbms * entries to userland gracefully. 1619189592Sbms * We only copy out the number of entries which userland 1620189592Sbms * has asked for, but we always tell userland how big the 1621189592Sbms * buffer really needs to be. 1622170613Sbms */ 1623254629Sdelphij if (msfr.msfr_nsrcs > in_mcast_maxsocksrc) 1624254629Sdelphij msfr.msfr_nsrcs = in_mcast_maxsocksrc; 1625170613Sbms tss = NULL; 1626170613Sbms if (msfr.msfr_srcs != NULL && msfr.msfr_nsrcs > 0) { 1627184214Sdes tss = malloc(sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs, 1628189592Sbms M_TEMP, M_NOWAIT | M_ZERO); 1629170613Sbms if (tss == NULL) { 1630189592Sbms INP_WUNLOCK(inp); 1631189592Sbms return (ENOBUFS); 1632170613Sbms } 1633170613Sbms } 1634170613Sbms 1635189592Sbms /* 1636189592Sbms * Count number of sources in-mode at t0. 1637189592Sbms * If buffer space exists and remains, copy out source entries. 1638189592Sbms */ 1639189592Sbms nsrcs = msfr.msfr_nsrcs; 1640189592Sbms ncsrcs = 0; 1641189592Sbms ptss = tss; 1642189592Sbms RB_FOREACH(ims, ip_msource_tree, &imf->imf_sources) { 1643189592Sbms lims = (struct in_msource *)ims; 1644189592Sbms if (lims->imsl_st[0] == MCAST_UNDEFINED || 1645189592Sbms lims->imsl_st[0] != imf->imf_st[0]) 1646189592Sbms continue; 1647189592Sbms ++ncsrcs; 1648191659Sbms if (tss != NULL && nsrcs > 0) { 1649191659Sbms psin = (struct sockaddr_in *)ptss; 1650189592Sbms psin->sin_family = AF_INET; 1651189592Sbms psin->sin_len = sizeof(struct sockaddr_in); 1652189592Sbms psin->sin_addr.s_addr = htonl(lims->ims_haddr); 1653191659Sbms psin->sin_port = 0; 1654191659Sbms ++ptss; 1655191659Sbms --nsrcs; 1656189592Sbms } 1657189592Sbms } 1658189592Sbms 1659178285Srwatson INP_WUNLOCK(inp); 1660170613Sbms 1661170613Sbms if (tss != NULL) { 1662170613Sbms error = copyout(tss, msfr.msfr_srcs, 1663170613Sbms sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs); 1664184205Sdes free(tss, M_TEMP); 1665189592Sbms if (error) 1666189592Sbms return (error); 1667170613Sbms } 1668170613Sbms 1669189592Sbms msfr.msfr_nsrcs = ncsrcs; 1670170613Sbms error = sooptcopyout(sopt, &msfr, sizeof(struct __msfilterreq)); 1671170613Sbms 1672170613Sbms return (error); 1673170613Sbms} 1674170613Sbms 1675170613Sbms/* 1676170613Sbms * Return the IP multicast options in response to user getsockopt(). 1677170613Sbms */ 1678170613Sbmsint 1679170613Sbmsinp_getmoptions(struct inpcb *inp, struct sockopt *sopt) 1680170613Sbms{ 1681170613Sbms struct ip_mreqn mreqn; 1682170613Sbms struct ip_moptions *imo; 1683170613Sbms struct ifnet *ifp; 1684170613Sbms struct in_ifaddr *ia; 1685170613Sbms int error, optval; 1686170613Sbms u_char coptval; 1687170613Sbms 1688178285Srwatson INP_WLOCK(inp); 1689170613Sbms imo = inp->inp_moptions; 1690171746Scsjp /* 1691171746Scsjp * If socket is neither of type SOCK_RAW or SOCK_DGRAM, 1692171746Scsjp * or is a divert socket, reject it. 1693171746Scsjp */ 1694171746Scsjp if (inp->inp_socket->so_proto->pr_protocol == IPPROTO_DIVERT || 1695171746Scsjp (inp->inp_socket->so_proto->pr_type != SOCK_RAW && 1696171746Scsjp inp->inp_socket->so_proto->pr_type != SOCK_DGRAM)) { 1697178285Srwatson INP_WUNLOCK(inp); 1698171746Scsjp return (EOPNOTSUPP); 1699171746Scsjp } 1700170613Sbms 1701170613Sbms error = 0; 1702170613Sbms switch (sopt->sopt_name) { 1703170613Sbms case IP_MULTICAST_VIF: 1704170613Sbms if (imo != NULL) 1705170613Sbms optval = imo->imo_multicast_vif; 1706170613Sbms else 1707170613Sbms optval = -1; 1708178285Srwatson INP_WUNLOCK(inp); 1709170613Sbms error = sooptcopyout(sopt, &optval, sizeof(int)); 1710170613Sbms break; 1711170613Sbms 1712170613Sbms case IP_MULTICAST_IF: 1713170613Sbms memset(&mreqn, 0, sizeof(struct ip_mreqn)); 1714170613Sbms if (imo != NULL) { 1715170613Sbms ifp = imo->imo_multicast_ifp; 1716189592Sbms if (!in_nullhost(imo->imo_multicast_addr)) { 1717170613Sbms mreqn.imr_address = imo->imo_multicast_addr; 1718170613Sbms } else if (ifp != NULL) { 1719170613Sbms mreqn.imr_ifindex = ifp->if_index; 1720170613Sbms IFP_TO_IA(ifp, ia); 1721170613Sbms if (ia != NULL) { 1722170613Sbms mreqn.imr_address = 1723170613Sbms IA_SIN(ia)->sin_addr; 1724194760Srwatson ifa_free(&ia->ia_ifa); 1725170613Sbms } 1726170613Sbms } 1727170613Sbms } 1728178285Srwatson INP_WUNLOCK(inp); 1729170613Sbms if (sopt->sopt_valsize == sizeof(struct ip_mreqn)) { 1730170613Sbms error = sooptcopyout(sopt, &mreqn, 1731170613Sbms sizeof(struct ip_mreqn)); 1732170613Sbms } else { 1733170613Sbms error = sooptcopyout(sopt, &mreqn.imr_address, 1734170613Sbms sizeof(struct in_addr)); 1735170613Sbms } 1736170613Sbms break; 1737170613Sbms 1738170613Sbms case IP_MULTICAST_TTL: 1739170613Sbms if (imo == 0) 1740170613Sbms optval = coptval = IP_DEFAULT_MULTICAST_TTL; 1741170613Sbms else 1742170613Sbms optval = coptval = imo->imo_multicast_ttl; 1743178285Srwatson INP_WUNLOCK(inp); 1744170613Sbms if (sopt->sopt_valsize == sizeof(u_char)) 1745170613Sbms error = sooptcopyout(sopt, &coptval, sizeof(u_char)); 1746170613Sbms else 1747170613Sbms error = sooptcopyout(sopt, &optval, sizeof(int)); 1748170613Sbms break; 1749170613Sbms 1750170613Sbms case IP_MULTICAST_LOOP: 1751170613Sbms if (imo == 0) 1752170613Sbms optval = coptval = IP_DEFAULT_MULTICAST_LOOP; 1753170613Sbms else 1754170613Sbms optval = coptval = imo->imo_multicast_loop; 1755178285Srwatson INP_WUNLOCK(inp); 1756170613Sbms if (sopt->sopt_valsize == sizeof(u_char)) 1757170613Sbms error = sooptcopyout(sopt, &coptval, sizeof(u_char)); 1758170613Sbms else 1759170613Sbms error = sooptcopyout(sopt, &optval, sizeof(int)); 1760170613Sbms break; 1761170613Sbms 1762170613Sbms case IP_MSFILTER: 1763170613Sbms if (imo == NULL) { 1764170613Sbms error = EADDRNOTAVAIL; 1765178285Srwatson INP_WUNLOCK(inp); 1766170613Sbms } else { 1767170613Sbms error = inp_get_source_filters(inp, sopt); 1768170613Sbms } 1769170613Sbms break; 1770170613Sbms 1771170613Sbms default: 1772178285Srwatson INP_WUNLOCK(inp); 1773170613Sbms error = ENOPROTOOPT; 1774170613Sbms break; 1775170613Sbms } 1776170613Sbms 1777170613Sbms INP_UNLOCK_ASSERT(inp); 1778170613Sbms 1779170613Sbms return (error); 1780170613Sbms} 1781170613Sbms 1782170613Sbms/* 1783189592Sbms * Look up the ifnet to use for a multicast group membership, 1784189592Sbms * given the IPv4 address of an interface, and the IPv4 group address. 1785189592Sbms * 1786189592Sbms * This routine exists to support legacy multicast applications 1787189592Sbms * which do not understand that multicast memberships are scoped to 1788189592Sbms * specific physical links in the networking stack, or which need 1789189592Sbms * to join link-scope groups before IPv4 addresses are configured. 1790189592Sbms * 1791189592Sbms * If inp is non-NULL, use this socket's current FIB number for any 1792189592Sbms * required FIB lookup. 1793189592Sbms * If ina is INADDR_ANY, look up the group address in the unicast FIB, 1794189592Sbms * and use its ifp; usually, this points to the default next-hop. 1795189592Sbms * 1796189592Sbms * If the FIB lookup fails, attempt to use the first non-loopback 1797189592Sbms * interface with multicast capability in the system as a 1798189592Sbms * last resort. The legacy IPv4 ASM API requires that we do 1799189592Sbms * this in order to allow groups to be joined when the routing 1800189592Sbms * table has not yet been populated during boot. 1801189592Sbms * 1802189592Sbms * Returns NULL if no ifp could be found. 1803189592Sbms * 1804189592Sbms * SMPng: TODO: Acquire the appropriate locks for INADDR_TO_IFP. 1805189592Sbms * FUTURE: Implement IPv4 source-address selection. 1806189592Sbms */ 1807189592Sbmsstatic struct ifnet * 1808189592Sbmsinp_lookup_mcast_ifp(const struct inpcb *inp, 1809189592Sbms const struct sockaddr_in *gsin, const struct in_addr ina) 1810189592Sbms{ 1811189592Sbms struct ifnet *ifp; 1812189592Sbms 1813189592Sbms KASSERT(gsin->sin_family == AF_INET, ("%s: not AF_INET", __func__)); 1814189592Sbms KASSERT(IN_MULTICAST(ntohl(gsin->sin_addr.s_addr)), 1815189592Sbms ("%s: not multicast", __func__)); 1816189592Sbms 1817189592Sbms ifp = NULL; 1818189592Sbms if (!in_nullhost(ina)) { 1819189592Sbms INADDR_TO_IFP(ina, ifp); 1820189592Sbms } else { 1821189592Sbms struct route ro; 1822189592Sbms 1823189592Sbms ro.ro_rt = NULL; 1824189592Sbms memcpy(&ro.ro_dst, gsin, sizeof(struct sockaddr_in)); 1825189592Sbms in_rtalloc_ign(&ro, 0, inp ? inp->inp_inc.inc_fibnum : 0); 1826189592Sbms if (ro.ro_rt != NULL) { 1827189592Sbms ifp = ro.ro_rt->rt_ifp; 1828189592Sbms KASSERT(ifp != NULL, ("%s: null ifp", __func__)); 1829189592Sbms RTFREE(ro.ro_rt); 1830189592Sbms } else { 1831189592Sbms struct in_ifaddr *ia; 1832189592Sbms struct ifnet *mifp; 1833189592Sbms 1834189592Sbms mifp = NULL; 1835194951Srwatson IN_IFADDR_RLOCK(); 1836189592Sbms TAILQ_FOREACH(ia, &V_in_ifaddrhead, ia_link) { 1837189592Sbms mifp = ia->ia_ifp; 1838189592Sbms if (!(mifp->if_flags & IFF_LOOPBACK) && 1839189592Sbms (mifp->if_flags & IFF_MULTICAST)) { 1840189592Sbms ifp = mifp; 1841189592Sbms break; 1842189592Sbms } 1843189592Sbms } 1844194951Srwatson IN_IFADDR_RUNLOCK(); 1845189592Sbms } 1846189592Sbms } 1847189592Sbms 1848189592Sbms return (ifp); 1849189592Sbms} 1850189592Sbms 1851189592Sbms/* 1852170613Sbms * Join an IPv4 multicast group, possibly with a source. 1853170613Sbms */ 1854170613Sbmsstatic int 1855170613Sbmsinp_join_group(struct inpcb *inp, struct sockopt *sopt) 1856170613Sbms{ 1857170613Sbms struct group_source_req gsr; 1858170613Sbms sockunion_t *gsa, *ssa; 1859170613Sbms struct ifnet *ifp; 1860170613Sbms struct in_mfilter *imf; 1861170613Sbms struct ip_moptions *imo; 1862170613Sbms struct in_multi *inm; 1863189592Sbms struct in_msource *lims; 1864170613Sbms size_t idx; 1865189592Sbms int error, is_new; 1866170613Sbms 1867170613Sbms ifp = NULL; 1868189592Sbms imf = NULL; 1869197136Sbms lims = NULL; 1870170613Sbms error = 0; 1871189592Sbms is_new = 0; 1872170613Sbms 1873170613Sbms memset(&gsr, 0, sizeof(struct group_source_req)); 1874170613Sbms gsa = (sockunion_t *)&gsr.gsr_group; 1875170613Sbms gsa->ss.ss_family = AF_UNSPEC; 1876170613Sbms ssa = (sockunion_t *)&gsr.gsr_source; 1877170613Sbms ssa->ss.ss_family = AF_UNSPEC; 1878170613Sbms 1879170613Sbms switch (sopt->sopt_name) { 1880170613Sbms case IP_ADD_MEMBERSHIP: 1881170613Sbms case IP_ADD_SOURCE_MEMBERSHIP: { 1882170613Sbms struct ip_mreq_source mreqs; 1883170613Sbms 1884170613Sbms if (sopt->sopt_name == IP_ADD_MEMBERSHIP) { 1885170613Sbms error = sooptcopyin(sopt, &mreqs, 1886170613Sbms sizeof(struct ip_mreq), 1887170613Sbms sizeof(struct ip_mreq)); 1888170613Sbms /* 1889170613Sbms * Do argument switcharoo from ip_mreq into 1890170613Sbms * ip_mreq_source to avoid using two instances. 1891170613Sbms */ 1892170613Sbms mreqs.imr_interface = mreqs.imr_sourceaddr; 1893170613Sbms mreqs.imr_sourceaddr.s_addr = INADDR_ANY; 1894170613Sbms } else if (sopt->sopt_name == IP_ADD_SOURCE_MEMBERSHIP) { 1895170613Sbms error = sooptcopyin(sopt, &mreqs, 1896170613Sbms sizeof(struct ip_mreq_source), 1897170613Sbms sizeof(struct ip_mreq_source)); 1898170613Sbms } 1899170613Sbms if (error) 1900170613Sbms return (error); 1901170613Sbms 1902170613Sbms gsa->sin.sin_family = AF_INET; 1903170613Sbms gsa->sin.sin_len = sizeof(struct sockaddr_in); 1904170613Sbms gsa->sin.sin_addr = mreqs.imr_multiaddr; 1905170613Sbms 1906170613Sbms if (sopt->sopt_name == IP_ADD_SOURCE_MEMBERSHIP) { 1907170613Sbms ssa->sin.sin_family = AF_INET; 1908170613Sbms ssa->sin.sin_len = sizeof(struct sockaddr_in); 1909170613Sbms ssa->sin.sin_addr = mreqs.imr_sourceaddr; 1910170613Sbms } 1911170613Sbms 1912196932Ssyrinx if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr))) 1913196932Ssyrinx return (EINVAL); 1914196932Ssyrinx 1915189592Sbms ifp = inp_lookup_mcast_ifp(inp, &gsa->sin, 1916189592Sbms mreqs.imr_interface); 1917189592Sbms CTR3(KTR_IGMPV3, "%s: imr_interface = %s, ifp = %p", 1918189592Sbms __func__, inet_ntoa(mreqs.imr_interface), ifp); 1919170613Sbms break; 1920170613Sbms } 1921170613Sbms 1922170613Sbms case MCAST_JOIN_GROUP: 1923170613Sbms case MCAST_JOIN_SOURCE_GROUP: 1924170613Sbms if (sopt->sopt_name == MCAST_JOIN_GROUP) { 1925170613Sbms error = sooptcopyin(sopt, &gsr, 1926170613Sbms sizeof(struct group_req), 1927170613Sbms sizeof(struct group_req)); 1928170613Sbms } else if (sopt->sopt_name == MCAST_JOIN_SOURCE_GROUP) { 1929170613Sbms error = sooptcopyin(sopt, &gsr, 1930170613Sbms sizeof(struct group_source_req), 1931170613Sbms sizeof(struct group_source_req)); 1932170613Sbms } 1933170613Sbms if (error) 1934170613Sbms return (error); 1935170613Sbms 1936170613Sbms if (gsa->sin.sin_family != AF_INET || 1937170613Sbms gsa->sin.sin_len != sizeof(struct sockaddr_in)) 1938170613Sbms return (EINVAL); 1939170613Sbms 1940170613Sbms /* 1941170613Sbms * Overwrite the port field if present, as the sockaddr 1942170613Sbms * being copied in may be matched with a binary comparison. 1943170613Sbms */ 1944170613Sbms gsa->sin.sin_port = 0; 1945170613Sbms if (sopt->sopt_name == MCAST_JOIN_SOURCE_GROUP) { 1946170613Sbms if (ssa->sin.sin_family != AF_INET || 1947170613Sbms ssa->sin.sin_len != sizeof(struct sockaddr_in)) 1948170613Sbms return (EINVAL); 1949170613Sbms ssa->sin.sin_port = 0; 1950170613Sbms } 1951170613Sbms 1952196932Ssyrinx if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr))) 1953196932Ssyrinx return (EINVAL); 1954196932Ssyrinx 1955181803Sbz if (gsr.gsr_interface == 0 || V_if_index < gsr.gsr_interface) 1956170613Sbms return (EADDRNOTAVAIL); 1957170613Sbms ifp = ifnet_byindex(gsr.gsr_interface); 1958170613Sbms break; 1959170613Sbms 1960170613Sbms default: 1961189592Sbms CTR2(KTR_IGMPV3, "%s: unknown sopt_name %d", 1962189592Sbms __func__, sopt->sopt_name); 1963170613Sbms return (EOPNOTSUPP); 1964170613Sbms break; 1965170613Sbms } 1966170613Sbms 1967170613Sbms if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) 1968170613Sbms return (EADDRNOTAVAIL); 1969170613Sbms 1970170613Sbms imo = inp_findmoptions(inp); 1971170613Sbms idx = imo_match_group(imo, ifp, &gsa->sa); 1972189592Sbms if (idx == -1) { 1973189592Sbms is_new = 1; 1974189592Sbms } else { 1975189592Sbms inm = imo->imo_membership[idx]; 1976189592Sbms imf = &imo->imo_mfilters[idx]; 1977197132Sbms if (ssa->ss.ss_family != AF_UNSPEC) { 1978197132Sbms /* 1979199525Sbms * MCAST_JOIN_SOURCE_GROUP on an exclusive membership 1980197132Sbms * is an error. On an existing inclusive membership, 1981197132Sbms * it just adds the source to the filter list. 1982197132Sbms */ 1983197132Sbms if (imf->imf_st[1] != MCAST_INCLUDE) { 1984197132Sbms error = EINVAL; 1985197132Sbms goto out_inp_locked; 1986197132Sbms } 1987197136Sbms /* 1988197136Sbms * Throw out duplicates. 1989197136Sbms * 1990197136Sbms * XXX FIXME: This makes a naive assumption that 1991197136Sbms * even if entries exist for *ssa in this imf, 1992197136Sbms * they will be rejected as dupes, even if they 1993197136Sbms * are not valid in the current mode (in-mode). 1994197136Sbms * 1995197136Sbms * in_msource is transactioned just as for anything 1996197136Sbms * else in SSM -- but note naive use of inm_graft() 1997197136Sbms * below for allocating new filter entries. 1998197136Sbms * 1999197136Sbms * This is only an issue if someone mixes the 2000197136Sbms * full-state SSM API with the delta-based API, 2001197136Sbms * which is discouraged in the relevant RFCs. 2002197136Sbms */ 2003197132Sbms lims = imo_match_source(imo, idx, &ssa->sa); 2004197136Sbms if (lims != NULL /*&& 2005197136Sbms lims->imsl_st[1] == MCAST_INCLUDE*/) { 2006197132Sbms error = EADDRNOTAVAIL; 2007197132Sbms goto out_inp_locked; 2008197132Sbms } 2009197132Sbms } else { 2010197132Sbms /* 2011206452Sbms * MCAST_JOIN_GROUP on an existing exclusive 2012206452Sbms * membership is an error; return EADDRINUSE 2013206452Sbms * to preserve 4.4BSD API idempotence, and 2014206452Sbms * avoid tedious detour to code below. 2015206452Sbms * NOTE: This is bending RFC 3678 a bit. 2016206452Sbms * 2017197135Sbms * On an existing inclusive membership, this is also 2018197135Sbms * an error; if you want to change filter mode, 2019197135Sbms * you must use the userland API setsourcefilter(). 2020197135Sbms * XXX We don't reject this for imf in UNDEFINED 2021197135Sbms * state at t1, because allocation of a filter 2022197135Sbms * is atomic with allocation of a membership. 2023197132Sbms */ 2024197135Sbms error = EINVAL; 2025206452Sbms if (imf->imf_st[1] == MCAST_EXCLUDE) 2026206452Sbms error = EADDRINUSE; 2027197135Sbms goto out_inp_locked; 2028189592Sbms } 2029170613Sbms } 2030170613Sbms 2031170613Sbms /* 2032189592Sbms * Begin state merge transaction at socket layer. 2033170613Sbms */ 2034189592Sbms INP_WLOCK_ASSERT(inp); 2035189592Sbms 2036189592Sbms if (is_new) { 2037189592Sbms if (imo->imo_num_memberships == imo->imo_max_memberships) { 2038189592Sbms error = imo_grow(imo); 2039189592Sbms if (error) 2040189592Sbms goto out_inp_locked; 2041189592Sbms } 2042189592Sbms /* 2043189592Sbms * Allocate the new slot upfront so we can deal with 2044189592Sbms * grafting the new source filter in same code path 2045189592Sbms * as for join-source on existing membership. 2046189592Sbms */ 2047189592Sbms idx = imo->imo_num_memberships; 2048189592Sbms imo->imo_membership[idx] = NULL; 2049189592Sbms imo->imo_num_memberships++; 2050189592Sbms KASSERT(imo->imo_mfilters != NULL, 2051189592Sbms ("%s: imf_mfilters vector was not allocated", __func__)); 2052189592Sbms imf = &imo->imo_mfilters[idx]; 2053189592Sbms KASSERT(RB_EMPTY(&imf->imf_sources), 2054189592Sbms ("%s: imf_sources not empty", __func__)); 2055170613Sbms } 2056170613Sbms 2057170613Sbms /* 2058189592Sbms * Graft new source into filter list for this inpcb's 2059189592Sbms * membership of the group. The in_multi may not have 2060197132Sbms * been allocated yet if this is a new membership, however, 2061197132Sbms * the in_mfilter slot will be allocated and must be initialized. 2062197135Sbms * 2063197135Sbms * Note: Grafting of exclusive mode filters doesn't happen 2064197135Sbms * in this path. 2065197136Sbms * XXX: Should check for non-NULL lims (node exists but may 2066197136Sbms * not be in-mode) for interop with full-state API. 2067170613Sbms */ 2068189592Sbms if (ssa->ss.ss_family != AF_UNSPEC) { 2069189592Sbms /* Membership starts in IN mode */ 2070189592Sbms if (is_new) { 2071189592Sbms CTR1(KTR_IGMPV3, "%s: new join w/source", __func__); 2072189592Sbms imf_init(imf, MCAST_UNDEFINED, MCAST_INCLUDE); 2073189592Sbms } else { 2074189592Sbms CTR2(KTR_IGMPV3, "%s: %s source", __func__, "allow"); 2075189592Sbms } 2076189592Sbms lims = imf_graft(imf, MCAST_INCLUDE, &ssa->sin); 2077189592Sbms if (lims == NULL) { 2078189592Sbms CTR1(KTR_IGMPV3, "%s: merge imf state failed", 2079189592Sbms __func__); 2080189592Sbms error = ENOMEM; 2081189592Sbms goto out_imo_free; 2082189592Sbms } 2083197132Sbms } else { 2084197132Sbms /* No address specified; Membership starts in EX mode */ 2085197132Sbms if (is_new) { 2086197132Sbms CTR1(KTR_IGMPV3, "%s: new join w/o source", __func__); 2087197132Sbms imf_init(imf, MCAST_UNDEFINED, MCAST_EXCLUDE); 2088197132Sbms } 2089170613Sbms } 2090170613Sbms 2091170613Sbms /* 2092189592Sbms * Begin state merge transaction at IGMP layer. 2093170613Sbms */ 2094189592Sbms IN_MULTI_LOCK(); 2095170613Sbms 2096189592Sbms if (is_new) { 2097189592Sbms error = in_joingroup_locked(ifp, &gsa->sin.sin_addr, imf, 2098189592Sbms &inm); 2099261694Sgnn if (error) { 2100261694Sgnn CTR1(KTR_IGMPV3, "%s: in_joingroup_locked failed", 2101261694Sgnn __func__); 2102261694Sgnn IN_MULTI_UNLOCK(); 2103189592Sbms goto out_imo_free; 2104261694Sgnn } 2105189592Sbms imo->imo_membership[idx] = inm; 2106189592Sbms } else { 2107189592Sbms CTR1(KTR_IGMPV3, "%s: merge inm state", __func__); 2108189592Sbms error = inm_merge(inm, imf); 2109170613Sbms if (error) { 2110189592Sbms CTR1(KTR_IGMPV3, "%s: failed to merge inm state", 2111189592Sbms __func__); 2112261694Sgnn goto out_in_multi_locked; 2113170613Sbms } 2114189592Sbms CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__); 2115189592Sbms error = igmp_change_state(inm); 2116189592Sbms if (error) { 2117189592Sbms CTR1(KTR_IGMPV3, "%s: failed igmp downcall", 2118189592Sbms __func__); 2119261694Sgnn goto out_in_multi_locked; 2120189592Sbms } 2121170613Sbms } 2122170613Sbms 2123261694Sgnnout_in_multi_locked: 2124261694Sgnn 2125189592Sbms IN_MULTI_UNLOCK(); 2126189592Sbms 2127189592Sbms INP_WLOCK_ASSERT(inp); 2128189592Sbms if (error) { 2129189592Sbms imf_rollback(imf); 2130189592Sbms if (is_new) 2131189592Sbms imf_purge(imf); 2132189592Sbms else 2133189592Sbms imf_reap(imf); 2134189592Sbms } else { 2135189592Sbms imf_commit(imf); 2136189592Sbms } 2137189592Sbms 2138189592Sbmsout_imo_free: 2139189592Sbms if (error && is_new) { 2140189592Sbms imo->imo_membership[idx] = NULL; 2141189592Sbms --imo->imo_num_memberships; 2142189592Sbms } 2143189592Sbms 2144189592Sbmsout_inp_locked: 2145178285Srwatson INP_WUNLOCK(inp); 2146170613Sbms return (error); 2147170613Sbms} 2148170613Sbms 2149170613Sbms/* 2150170613Sbms * Leave an IPv4 multicast group on an inpcb, possibly with a source. 2151170613Sbms */ 2152170613Sbmsstatic int 2153170613Sbmsinp_leave_group(struct inpcb *inp, struct sockopt *sopt) 2154170613Sbms{ 2155170613Sbms struct group_source_req gsr; 2156170613Sbms struct ip_mreq_source mreqs; 2157170613Sbms sockunion_t *gsa, *ssa; 2158170613Sbms struct ifnet *ifp; 2159170613Sbms struct in_mfilter *imf; 2160170613Sbms struct ip_moptions *imo; 2161189592Sbms struct in_msource *ims; 2162170613Sbms struct in_multi *inm; 2163170613Sbms size_t idx; 2164189592Sbms int error, is_final; 2165170613Sbms 2166170613Sbms ifp = NULL; 2167170613Sbms error = 0; 2168189592Sbms is_final = 1; 2169170613Sbms 2170170613Sbms memset(&gsr, 0, sizeof(struct group_source_req)); 2171170613Sbms gsa = (sockunion_t *)&gsr.gsr_group; 2172170613Sbms gsa->ss.ss_family = AF_UNSPEC; 2173170613Sbms ssa = (sockunion_t *)&gsr.gsr_source; 2174170613Sbms ssa->ss.ss_family = AF_UNSPEC; 2175170613Sbms 2176170613Sbms switch (sopt->sopt_name) { 2177170613Sbms case IP_DROP_MEMBERSHIP: 2178170613Sbms case IP_DROP_SOURCE_MEMBERSHIP: 2179170613Sbms if (sopt->sopt_name == IP_DROP_MEMBERSHIP) { 2180170613Sbms error = sooptcopyin(sopt, &mreqs, 2181170613Sbms sizeof(struct ip_mreq), 2182170613Sbms sizeof(struct ip_mreq)); 2183170613Sbms /* 2184170613Sbms * Swap interface and sourceaddr arguments, 2185170613Sbms * as ip_mreq and ip_mreq_source are laid 2186170613Sbms * out differently. 2187170613Sbms */ 2188170613Sbms mreqs.imr_interface = mreqs.imr_sourceaddr; 2189170613Sbms mreqs.imr_sourceaddr.s_addr = INADDR_ANY; 2190170613Sbms } else if (sopt->sopt_name == IP_DROP_SOURCE_MEMBERSHIP) { 2191170613Sbms error = sooptcopyin(sopt, &mreqs, 2192170613Sbms sizeof(struct ip_mreq_source), 2193170613Sbms sizeof(struct ip_mreq_source)); 2194170613Sbms } 2195170613Sbms if (error) 2196170613Sbms return (error); 2197170613Sbms 2198170613Sbms gsa->sin.sin_family = AF_INET; 2199170613Sbms gsa->sin.sin_len = sizeof(struct sockaddr_in); 2200170613Sbms gsa->sin.sin_addr = mreqs.imr_multiaddr; 2201170613Sbms 2202170613Sbms if (sopt->sopt_name == IP_DROP_SOURCE_MEMBERSHIP) { 2203170613Sbms ssa->sin.sin_family = AF_INET; 2204170613Sbms ssa->sin.sin_len = sizeof(struct sockaddr_in); 2205170613Sbms ssa->sin.sin_addr = mreqs.imr_sourceaddr; 2206170613Sbms } 2207170613Sbms 2208206452Sbms /* 2209206452Sbms * Attempt to look up hinted ifp from interface address. 2210206452Sbms * Fallthrough with null ifp iff lookup fails, to 2211206452Sbms * preserve 4.4BSD mcast API idempotence. 2212206452Sbms * XXX NOTE WELL: The RFC 3678 API is preferred because 2213206452Sbms * using an IPv4 address as a key is racy. 2214206452Sbms */ 2215206452Sbms if (!in_nullhost(mreqs.imr_interface)) 2216170613Sbms INADDR_TO_IFP(mreqs.imr_interface, ifp); 2217170613Sbms 2218189592Sbms CTR3(KTR_IGMPV3, "%s: imr_interface = %s, ifp = %p", 2219189592Sbms __func__, inet_ntoa(mreqs.imr_interface), ifp); 2220189592Sbms 2221170613Sbms break; 2222170613Sbms 2223170613Sbms case MCAST_LEAVE_GROUP: 2224170613Sbms case MCAST_LEAVE_SOURCE_GROUP: 2225170613Sbms if (sopt->sopt_name == MCAST_LEAVE_GROUP) { 2226170613Sbms error = sooptcopyin(sopt, &gsr, 2227170613Sbms sizeof(struct group_req), 2228170613Sbms sizeof(struct group_req)); 2229170613Sbms } else if (sopt->sopt_name == MCAST_LEAVE_SOURCE_GROUP) { 2230170613Sbms error = sooptcopyin(sopt, &gsr, 2231170613Sbms sizeof(struct group_source_req), 2232170613Sbms sizeof(struct group_source_req)); 2233170613Sbms } 2234170613Sbms if (error) 2235170613Sbms return (error); 2236170613Sbms 2237170613Sbms if (gsa->sin.sin_family != AF_INET || 2238170613Sbms gsa->sin.sin_len != sizeof(struct sockaddr_in)) 2239170613Sbms return (EINVAL); 2240170613Sbms 2241170613Sbms if (sopt->sopt_name == MCAST_LEAVE_SOURCE_GROUP) { 2242170613Sbms if (ssa->sin.sin_family != AF_INET || 2243170613Sbms ssa->sin.sin_len != sizeof(struct sockaddr_in)) 2244170613Sbms return (EINVAL); 2245170613Sbms } 2246170613Sbms 2247181803Sbz if (gsr.gsr_interface == 0 || V_if_index < gsr.gsr_interface) 2248170613Sbms return (EADDRNOTAVAIL); 2249170613Sbms 2250170613Sbms ifp = ifnet_byindex(gsr.gsr_interface); 2251206452Sbms 2252206452Sbms if (ifp == NULL) 2253206452Sbms return (EADDRNOTAVAIL); 2254170613Sbms break; 2255170613Sbms 2256170613Sbms default: 2257189592Sbms CTR2(KTR_IGMPV3, "%s: unknown sopt_name %d", 2258189592Sbms __func__, sopt->sopt_name); 2259170613Sbms return (EOPNOTSUPP); 2260170613Sbms break; 2261170613Sbms } 2262170613Sbms 2263170613Sbms if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr))) 2264170613Sbms return (EINVAL); 2265170613Sbms 2266170613Sbms /* 2267170613Sbms * Find the membership in the membership array. 2268170613Sbms */ 2269170613Sbms imo = inp_findmoptions(inp); 2270170613Sbms idx = imo_match_group(imo, ifp, &gsa->sa); 2271170613Sbms if (idx == -1) { 2272170613Sbms error = EADDRNOTAVAIL; 2273189592Sbms goto out_inp_locked; 2274170613Sbms } 2275189592Sbms inm = imo->imo_membership[idx]; 2276170613Sbms imf = &imo->imo_mfilters[idx]; 2277170613Sbms 2278189592Sbms if (ssa->ss.ss_family != AF_UNSPEC) 2279189592Sbms is_final = 0; 2280189592Sbms 2281170613Sbms /* 2282189592Sbms * Begin state merge transaction at socket layer. 2283189592Sbms */ 2284189592Sbms INP_WLOCK_ASSERT(inp); 2285189592Sbms 2286189592Sbms /* 2287170613Sbms * If we were instructed only to leave a given source, do so. 2288189592Sbms * MCAST_LEAVE_SOURCE_GROUP is only valid for inclusive memberships. 2289170613Sbms */ 2290189592Sbms if (is_final) { 2291189592Sbms imf_leave(imf); 2292189592Sbms } else { 2293189592Sbms if (imf->imf_st[0] == MCAST_EXCLUDE) { 2294189592Sbms error = EADDRNOTAVAIL; 2295189592Sbms goto out_inp_locked; 2296170613Sbms } 2297189592Sbms ims = imo_match_source(imo, idx, &ssa->sa); 2298189592Sbms if (ims == NULL) { 2299189592Sbms CTR3(KTR_IGMPV3, "%s: source %s %spresent", __func__, 2300189592Sbms inet_ntoa(ssa->sin.sin_addr), "not "); 2301189592Sbms error = EADDRNOTAVAIL; 2302189592Sbms goto out_inp_locked; 2303189592Sbms } 2304189592Sbms CTR2(KTR_IGMPV3, "%s: %s source", __func__, "block"); 2305189592Sbms error = imf_prune(imf, &ssa->sin); 2306189592Sbms if (error) { 2307189592Sbms CTR1(KTR_IGMPV3, "%s: merge imf state failed", 2308189592Sbms __func__); 2309189592Sbms goto out_inp_locked; 2310189592Sbms } 2311170613Sbms } 2312170613Sbms 2313170613Sbms /* 2314189592Sbms * Begin state merge transaction at IGMP layer. 2315170613Sbms */ 2316189592Sbms IN_MULTI_LOCK(); 2317170613Sbms 2318189592Sbms if (is_final) { 2319189592Sbms /* 2320189592Sbms * Give up the multicast address record to which 2321189592Sbms * the membership points. 2322189592Sbms */ 2323189592Sbms (void)in_leavegroup_locked(inm, imf); 2324189592Sbms } else { 2325189592Sbms CTR1(KTR_IGMPV3, "%s: merge inm state", __func__); 2326189592Sbms error = inm_merge(inm, imf); 2327189592Sbms if (error) { 2328189592Sbms CTR1(KTR_IGMPV3, "%s: failed to merge inm state", 2329189592Sbms __func__); 2330261694Sgnn goto out_in_multi_locked; 2331170613Sbms } 2332189592Sbms 2333189592Sbms CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__); 2334189592Sbms error = igmp_change_state(inm); 2335189592Sbms if (error) { 2336189592Sbms CTR1(KTR_IGMPV3, "%s: failed igmp downcall", 2337189592Sbms __func__); 2338189592Sbms } 2339170613Sbms } 2340170613Sbms 2341261694Sgnnout_in_multi_locked: 2342261694Sgnn 2343189592Sbms IN_MULTI_UNLOCK(); 2344170613Sbms 2345189592Sbms if (error) 2346189592Sbms imf_rollback(imf); 2347189592Sbms else 2348189592Sbms imf_commit(imf); 2349189592Sbms 2350189592Sbms imf_reap(imf); 2351189592Sbms 2352189592Sbms if (is_final) { 2353197130Sbms /* Remove the gap in the membership and filter array. */ 2354197130Sbms for (++idx; idx < imo->imo_num_memberships; ++idx) { 2355189592Sbms imo->imo_membership[idx-1] = imo->imo_membership[idx]; 2356197130Sbms imo->imo_mfilters[idx-1] = imo->imo_mfilters[idx]; 2357197130Sbms } 2358189592Sbms imo->imo_num_memberships--; 2359189592Sbms } 2360189592Sbms 2361189592Sbmsout_inp_locked: 2362178285Srwatson INP_WUNLOCK(inp); 2363170613Sbms return (error); 2364170613Sbms} 2365170613Sbms 2366170613Sbms/* 2367170613Sbms * Select the interface for transmitting IPv4 multicast datagrams. 2368170613Sbms * 2369170613Sbms * Either an instance of struct in_addr or an instance of struct ip_mreqn 2370170613Sbms * may be passed to this socket option. An address of INADDR_ANY or an 2371170613Sbms * interface index of 0 is used to remove a previous selection. 2372170613Sbms * When no interface is selected, one is chosen for every send. 2373170613Sbms */ 2374170613Sbmsstatic int 2375170613Sbmsinp_set_multicast_if(struct inpcb *inp, struct sockopt *sopt) 2376170613Sbms{ 2377170613Sbms struct in_addr addr; 2378170613Sbms struct ip_mreqn mreqn; 2379170613Sbms struct ifnet *ifp; 2380170613Sbms struct ip_moptions *imo; 2381170613Sbms int error; 2382170613Sbms 2383170613Sbms if (sopt->sopt_valsize == sizeof(struct ip_mreqn)) { 2384170613Sbms /* 2385170613Sbms * An interface index was specified using the 2386170613Sbms * Linux-derived ip_mreqn structure. 2387170613Sbms */ 2388170613Sbms error = sooptcopyin(sopt, &mreqn, sizeof(struct ip_mreqn), 2389170613Sbms sizeof(struct ip_mreqn)); 2390170613Sbms if (error) 2391170613Sbms return (error); 2392170613Sbms 2393181803Sbz if (mreqn.imr_ifindex < 0 || V_if_index < mreqn.imr_ifindex) 2394170613Sbms return (EINVAL); 2395170613Sbms 2396170613Sbms if (mreqn.imr_ifindex == 0) { 2397170613Sbms ifp = NULL; 2398170613Sbms } else { 2399170613Sbms ifp = ifnet_byindex(mreqn.imr_ifindex); 2400170613Sbms if (ifp == NULL) 2401170613Sbms return (EADDRNOTAVAIL); 2402170613Sbms } 2403170613Sbms } else { 2404170613Sbms /* 2405170613Sbms * An interface was specified by IPv4 address. 2406170613Sbms * This is the traditional BSD usage. 2407170613Sbms */ 2408170613Sbms error = sooptcopyin(sopt, &addr, sizeof(struct in_addr), 2409170613Sbms sizeof(struct in_addr)); 2410170613Sbms if (error) 2411170613Sbms return (error); 2412189592Sbms if (in_nullhost(addr)) { 2413170613Sbms ifp = NULL; 2414170613Sbms } else { 2415170613Sbms INADDR_TO_IFP(addr, ifp); 2416170613Sbms if (ifp == NULL) 2417170613Sbms return (EADDRNOTAVAIL); 2418170613Sbms } 2419189592Sbms CTR3(KTR_IGMPV3, "%s: ifp = %p, addr = %s", __func__, ifp, 2420189592Sbms inet_ntoa(addr)); 2421170613Sbms } 2422170613Sbms 2423170613Sbms /* Reject interfaces which do not support multicast. */ 2424170613Sbms if (ifp != NULL && (ifp->if_flags & IFF_MULTICAST) == 0) 2425170613Sbms return (EOPNOTSUPP); 2426170613Sbms 2427170613Sbms imo = inp_findmoptions(inp); 2428170613Sbms imo->imo_multicast_ifp = ifp; 2429170613Sbms imo->imo_multicast_addr.s_addr = INADDR_ANY; 2430178285Srwatson INP_WUNLOCK(inp); 2431170613Sbms 2432170613Sbms return (0); 2433170613Sbms} 2434170613Sbms 2435170613Sbms/* 2436170613Sbms * Atomically set source filters on a socket for an IPv4 multicast group. 2437189592Sbms * 2438189592Sbms * SMPng: NOTE: Potentially calls malloc(M_WAITOK) with Giant held. 2439170613Sbms */ 2440170613Sbmsstatic int 2441170613Sbmsinp_set_source_filters(struct inpcb *inp, struct sockopt *sopt) 2442170613Sbms{ 2443170613Sbms struct __msfilterreq msfr; 2444170613Sbms sockunion_t *gsa; 2445170613Sbms struct ifnet *ifp; 2446170613Sbms struct in_mfilter *imf; 2447170613Sbms struct ip_moptions *imo; 2448189592Sbms struct in_multi *inm; 2449170613Sbms size_t idx; 2450170613Sbms int error; 2451170613Sbms 2452170613Sbms error = sooptcopyin(sopt, &msfr, sizeof(struct __msfilterreq), 2453170613Sbms sizeof(struct __msfilterreq)); 2454170613Sbms if (error) 2455170613Sbms return (error); 2456170613Sbms 2457197314Sbms if (msfr.msfr_nsrcs > in_mcast_maxsocksrc) 2458197314Sbms return (ENOBUFS); 2459197314Sbms 2460197314Sbms if ((msfr.msfr_fmode != MCAST_EXCLUDE && 2461170613Sbms msfr.msfr_fmode != MCAST_INCLUDE)) 2462170613Sbms return (EINVAL); 2463170613Sbms 2464170613Sbms if (msfr.msfr_group.ss_family != AF_INET || 2465170613Sbms msfr.msfr_group.ss_len != sizeof(struct sockaddr_in)) 2466170613Sbms return (EINVAL); 2467170613Sbms 2468170613Sbms gsa = (sockunion_t *)&msfr.msfr_group; 2469170613Sbms if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr))) 2470170613Sbms return (EINVAL); 2471170613Sbms 2472170613Sbms gsa->sin.sin_port = 0; /* ignore port */ 2473170613Sbms 2474181803Sbz if (msfr.msfr_ifindex == 0 || V_if_index < msfr.msfr_ifindex) 2475170613Sbms return (EADDRNOTAVAIL); 2476170613Sbms 2477170613Sbms ifp = ifnet_byindex(msfr.msfr_ifindex); 2478170613Sbms if (ifp == NULL) 2479170613Sbms return (EADDRNOTAVAIL); 2480170613Sbms 2481170613Sbms /* 2482189592Sbms * Take the INP write lock. 2483170613Sbms * Check if this socket is a member of this group. 2484170613Sbms */ 2485170613Sbms imo = inp_findmoptions(inp); 2486170613Sbms idx = imo_match_group(imo, ifp, &gsa->sa); 2487170613Sbms if (idx == -1 || imo->imo_mfilters == NULL) { 2488170613Sbms error = EADDRNOTAVAIL; 2489189592Sbms goto out_inp_locked; 2490170613Sbms } 2491189592Sbms inm = imo->imo_membership[idx]; 2492170613Sbms imf = &imo->imo_mfilters[idx]; 2493170613Sbms 2494170613Sbms /* 2495189592Sbms * Begin state merge transaction at socket layer. 2496170613Sbms */ 2497189592Sbms INP_WLOCK_ASSERT(inp); 2498170613Sbms 2499189592Sbms imf->imf_st[1] = msfr.msfr_fmode; 2500189592Sbms 2501170613Sbms /* 2502170613Sbms * Apply any new source filters, if present. 2503189592Sbms * Make a copy of the user-space source vector so 2504189592Sbms * that we may copy them with a single copyin. This 2505189592Sbms * allows us to deal with page faults up-front. 2506170613Sbms */ 2507170613Sbms if (msfr.msfr_nsrcs > 0) { 2508189592Sbms struct in_msource *lims; 2509189592Sbms struct sockaddr_in *psin; 2510189592Sbms struct sockaddr_storage *kss, *pkss; 2511189592Sbms int i; 2512170613Sbms 2513178285Srwatson INP_WUNLOCK(inp); 2514189592Sbms 2515189592Sbms CTR2(KTR_IGMPV3, "%s: loading %lu source list entries", 2516189592Sbms __func__, (unsigned long)msfr.msfr_nsrcs); 2517184214Sdes kss = malloc(sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs, 2518170613Sbms M_TEMP, M_WAITOK); 2519170613Sbms error = copyin(msfr.msfr_srcs, kss, 2520170613Sbms sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs); 2521170613Sbms if (error) { 2522184205Sdes free(kss, M_TEMP); 2523170613Sbms return (error); 2524170613Sbms } 2525170613Sbms 2526189592Sbms INP_WLOCK(inp); 2527189592Sbms 2528170613Sbms /* 2529189592Sbms * Mark all source filters as UNDEFINED at t1. 2530189592Sbms * Restore new group filter mode, as imf_leave() 2531189592Sbms * will set it to INCLUDE. 2532170613Sbms */ 2533189592Sbms imf_leave(imf); 2534189592Sbms imf->imf_st[1] = msfr.msfr_fmode; 2535189592Sbms 2536189592Sbms /* 2537189592Sbms * Update socket layer filters at t1, lazy-allocating 2538189592Sbms * new entries. This saves a bunch of memory at the 2539189592Sbms * cost of one RB_FIND() per source entry; duplicate 2540189592Sbms * entries in the msfr_nsrcs vector are ignored. 2541189592Sbms * If we encounter an error, rollback transaction. 2542189592Sbms * 2543189592Sbms * XXX This too could be replaced with a set-symmetric 2544189592Sbms * difference like loop to avoid walking from root 2545189592Sbms * every time, as the key space is common. 2546189592Sbms */ 2547189592Sbms for (i = 0, pkss = kss; i < msfr.msfr_nsrcs; i++, pkss++) { 2548189592Sbms psin = (struct sockaddr_in *)pkss; 2549189592Sbms if (psin->sin_family != AF_INET) { 2550170613Sbms error = EAFNOSUPPORT; 2551170613Sbms break; 2552170613Sbms } 2553189592Sbms if (psin->sin_len != sizeof(struct sockaddr_in)) { 2554189592Sbms error = EINVAL; 2555189592Sbms break; 2556189592Sbms } 2557189592Sbms error = imf_get_source(imf, psin, &lims); 2558170613Sbms if (error) 2559170613Sbms break; 2560189592Sbms lims->imsl_st[1] = imf->imf_st[1]; 2561170613Sbms } 2562189592Sbms free(kss, M_TEMP); 2563189592Sbms } 2564170613Sbms 2565189592Sbms if (error) 2566189592Sbms goto out_imf_rollback; 2567170613Sbms 2568189592Sbms INP_WLOCK_ASSERT(inp); 2569189592Sbms IN_MULTI_LOCK(); 2570170613Sbms 2571170613Sbms /* 2572189592Sbms * Begin state merge transaction at IGMP layer. 2573170613Sbms */ 2574189592Sbms CTR1(KTR_IGMPV3, "%s: merge inm state", __func__); 2575189592Sbms error = inm_merge(inm, imf); 2576189592Sbms if (error) { 2577189592Sbms CTR1(KTR_IGMPV3, "%s: failed to merge inm state", __func__); 2578261694Sgnn goto out_in_multi_locked; 2579189592Sbms } 2580170613Sbms 2581189592Sbms CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__); 2582189592Sbms error = igmp_change_state(inm); 2583189592Sbms if (error) 2584189592Sbms CTR1(KTR_IGMPV3, "%s: failed igmp downcall", __func__); 2585189592Sbms 2586261694Sgnnout_in_multi_locked: 2587261694Sgnn 2588189592Sbms IN_MULTI_UNLOCK(); 2589189592Sbms 2590189592Sbmsout_imf_rollback: 2591189592Sbms if (error) 2592189592Sbms imf_rollback(imf); 2593189592Sbms else 2594189592Sbms imf_commit(imf); 2595189592Sbms 2596189592Sbms imf_reap(imf); 2597189592Sbms 2598189592Sbmsout_inp_locked: 2599178285Srwatson INP_WUNLOCK(inp); 2600170613Sbms return (error); 2601170613Sbms} 2602170613Sbms 2603170613Sbms/* 2604170613Sbms * Set the IP multicast options in response to user setsockopt(). 2605170613Sbms * 2606170613Sbms * Many of the socket options handled in this function duplicate the 2607170613Sbms * functionality of socket options in the regular unicast API. However, 2608170613Sbms * it is not possible to merge the duplicate code, because the idempotence 2609170613Sbms * of the IPv4 multicast part of the BSD Sockets API must be preserved; 2610170613Sbms * the effects of these options must be treated as separate and distinct. 2611189592Sbms * 2612189592Sbms * SMPng: XXX: Unlocked read of inp_socket believed OK. 2613189592Sbms * FUTURE: The IP_MULTICAST_VIF option may be eliminated if MROUTING 2614189592Sbms * is refactored to no longer use vifs. 2615170613Sbms */ 2616170613Sbmsint 2617170613Sbmsinp_setmoptions(struct inpcb *inp, struct sockopt *sopt) 2618170613Sbms{ 2619170613Sbms struct ip_moptions *imo; 2620170613Sbms int error; 2621170613Sbms 2622170613Sbms error = 0; 2623170613Sbms 2624171746Scsjp /* 2625171746Scsjp * If socket is neither of type SOCK_RAW or SOCK_DGRAM, 2626171746Scsjp * or is a divert socket, reject it. 2627171746Scsjp */ 2628171746Scsjp if (inp->inp_socket->so_proto->pr_protocol == IPPROTO_DIVERT || 2629171746Scsjp (inp->inp_socket->so_proto->pr_type != SOCK_RAW && 2630189592Sbms inp->inp_socket->so_proto->pr_type != SOCK_DGRAM)) 2631171746Scsjp return (EOPNOTSUPP); 2632171746Scsjp 2633170613Sbms switch (sopt->sopt_name) { 2634170613Sbms case IP_MULTICAST_VIF: { 2635170613Sbms int vifi; 2636170613Sbms /* 2637170613Sbms * Select a multicast VIF for transmission. 2638170613Sbms * Only useful if multicast forwarding is active. 2639170613Sbms */ 2640170613Sbms if (legal_vif_num == NULL) { 2641170613Sbms error = EOPNOTSUPP; 2642170613Sbms break; 2643170613Sbms } 2644170613Sbms error = sooptcopyin(sopt, &vifi, sizeof(int), sizeof(int)); 2645170613Sbms if (error) 2646170613Sbms break; 2647170613Sbms if (!legal_vif_num(vifi) && (vifi != -1)) { 2648170613Sbms error = EINVAL; 2649170613Sbms break; 2650170613Sbms } 2651170613Sbms imo = inp_findmoptions(inp); 2652170613Sbms imo->imo_multicast_vif = vifi; 2653178285Srwatson INP_WUNLOCK(inp); 2654170613Sbms break; 2655170613Sbms } 2656170613Sbms 2657170613Sbms case IP_MULTICAST_IF: 2658170613Sbms error = inp_set_multicast_if(inp, sopt); 2659170613Sbms break; 2660170613Sbms 2661170613Sbms case IP_MULTICAST_TTL: { 2662170613Sbms u_char ttl; 2663170613Sbms 2664170613Sbms /* 2665170613Sbms * Set the IP time-to-live for outgoing multicast packets. 2666170613Sbms * The original multicast API required a char argument, 2667170613Sbms * which is inconsistent with the rest of the socket API. 2668170613Sbms * We allow either a char or an int. 2669170613Sbms */ 2670170613Sbms if (sopt->sopt_valsize == sizeof(u_char)) { 2671170613Sbms error = sooptcopyin(sopt, &ttl, sizeof(u_char), 2672170613Sbms sizeof(u_char)); 2673170613Sbms if (error) 2674170613Sbms break; 2675170613Sbms } else { 2676170613Sbms u_int ittl; 2677170613Sbms 2678170613Sbms error = sooptcopyin(sopt, &ittl, sizeof(u_int), 2679170613Sbms sizeof(u_int)); 2680170613Sbms if (error) 2681170613Sbms break; 2682170613Sbms if (ittl > 255) { 2683170613Sbms error = EINVAL; 2684170613Sbms break; 2685170613Sbms } 2686170613Sbms ttl = (u_char)ittl; 2687170613Sbms } 2688170613Sbms imo = inp_findmoptions(inp); 2689170613Sbms imo->imo_multicast_ttl = ttl; 2690178285Srwatson INP_WUNLOCK(inp); 2691170613Sbms break; 2692170613Sbms } 2693170613Sbms 2694170613Sbms case IP_MULTICAST_LOOP: { 2695170613Sbms u_char loop; 2696170613Sbms 2697170613Sbms /* 2698170613Sbms * Set the loopback flag for outgoing multicast packets. 2699170613Sbms * Must be zero or one. The original multicast API required a 2700170613Sbms * char argument, which is inconsistent with the rest 2701170613Sbms * of the socket API. We allow either a char or an int. 2702170613Sbms */ 2703170613Sbms if (sopt->sopt_valsize == sizeof(u_char)) { 2704170613Sbms error = sooptcopyin(sopt, &loop, sizeof(u_char), 2705170613Sbms sizeof(u_char)); 2706170613Sbms if (error) 2707170613Sbms break; 2708170613Sbms } else { 2709170613Sbms u_int iloop; 2710170613Sbms 2711170613Sbms error = sooptcopyin(sopt, &iloop, sizeof(u_int), 2712170613Sbms sizeof(u_int)); 2713170613Sbms if (error) 2714170613Sbms break; 2715170613Sbms loop = (u_char)iloop; 2716170613Sbms } 2717170613Sbms imo = inp_findmoptions(inp); 2718170613Sbms imo->imo_multicast_loop = !!loop; 2719178285Srwatson INP_WUNLOCK(inp); 2720170613Sbms break; 2721170613Sbms } 2722170613Sbms 2723170613Sbms case IP_ADD_MEMBERSHIP: 2724170613Sbms case IP_ADD_SOURCE_MEMBERSHIP: 2725170613Sbms case MCAST_JOIN_GROUP: 2726170613Sbms case MCAST_JOIN_SOURCE_GROUP: 2727170613Sbms error = inp_join_group(inp, sopt); 2728170613Sbms break; 2729170613Sbms 2730170613Sbms case IP_DROP_MEMBERSHIP: 2731170613Sbms case IP_DROP_SOURCE_MEMBERSHIP: 2732170613Sbms case MCAST_LEAVE_GROUP: 2733170613Sbms case MCAST_LEAVE_SOURCE_GROUP: 2734170613Sbms error = inp_leave_group(inp, sopt); 2735170613Sbms break; 2736170613Sbms 2737170613Sbms case IP_BLOCK_SOURCE: 2738170613Sbms case IP_UNBLOCK_SOURCE: 2739170613Sbms case MCAST_BLOCK_SOURCE: 2740170613Sbms case MCAST_UNBLOCK_SOURCE: 2741189592Sbms error = inp_block_unblock_source(inp, sopt); 2742170613Sbms break; 2743170613Sbms 2744170613Sbms case IP_MSFILTER: 2745170613Sbms error = inp_set_source_filters(inp, sopt); 2746170613Sbms break; 2747170613Sbms 2748170613Sbms default: 2749170613Sbms error = EOPNOTSUPP; 2750170613Sbms break; 2751170613Sbms } 2752170613Sbms 2753170613Sbms INP_UNLOCK_ASSERT(inp); 2754170613Sbms 2755170613Sbms return (error); 2756170613Sbms} 2757189592Sbms 2758189592Sbms/* 2759189592Sbms * Expose IGMP's multicast filter mode and source list(s) to userland, 2760189592Sbms * keyed by (ifindex, group). 2761189592Sbms * The filter mode is written out as a uint32_t, followed by 2762189592Sbms * 0..n of struct in_addr. 2763189592Sbms * For use by ifmcstat(8). 2764189592Sbms * SMPng: NOTE: unlocked read of ifindex space. 2765189592Sbms */ 2766189592Sbmsstatic int 2767189592Sbmssysctl_ip_mcast_filters(SYSCTL_HANDLER_ARGS) 2768189592Sbms{ 2769189592Sbms struct in_addr src, group; 2770189592Sbms struct ifnet *ifp; 2771189592Sbms struct ifmultiaddr *ifma; 2772189592Sbms struct in_multi *inm; 2773189592Sbms struct ip_msource *ims; 2774189592Sbms int *name; 2775189592Sbms int retval; 2776189592Sbms u_int namelen; 2777189592Sbms uint32_t fmode, ifindex; 2778189592Sbms 2779189592Sbms name = (int *)arg1; 2780189592Sbms namelen = arg2; 2781189592Sbms 2782189592Sbms if (req->newptr != NULL) 2783189592Sbms return (EPERM); 2784189592Sbms 2785189592Sbms if (namelen != 2) 2786189592Sbms return (EINVAL); 2787189592Sbms 2788189592Sbms ifindex = name[0]; 2789189592Sbms if (ifindex <= 0 || ifindex > V_if_index) { 2790189592Sbms CTR2(KTR_IGMPV3, "%s: ifindex %u out of range", 2791189592Sbms __func__, ifindex); 2792189592Sbms return (ENOENT); 2793189592Sbms } 2794189592Sbms 2795189592Sbms group.s_addr = name[1]; 2796189592Sbms if (!IN_MULTICAST(ntohl(group.s_addr))) { 2797189592Sbms CTR2(KTR_IGMPV3, "%s: group %s is not multicast", 2798189592Sbms __func__, inet_ntoa(group)); 2799189592Sbms return (EINVAL); 2800189592Sbms } 2801189592Sbms 2802189592Sbms ifp = ifnet_byindex(ifindex); 2803189592Sbms if (ifp == NULL) { 2804189592Sbms CTR2(KTR_IGMPV3, "%s: no ifp for ifindex %u", 2805189592Sbms __func__, ifindex); 2806189592Sbms return (ENOENT); 2807189592Sbms } 2808189592Sbms 2809189592Sbms retval = sysctl_wire_old_buffer(req, 2810189592Sbms sizeof(uint32_t) + (in_mcast_maxgrpsrc * sizeof(struct in_addr))); 2811189592Sbms if (retval) 2812189592Sbms return (retval); 2813189592Sbms 2814189592Sbms IN_MULTI_LOCK(); 2815189592Sbms 2816233200Sjhb IF_ADDR_RLOCK(ifp); 2817189592Sbms TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 2818189592Sbms if (ifma->ifma_addr->sa_family != AF_INET || 2819189592Sbms ifma->ifma_protospec == NULL) 2820189592Sbms continue; 2821189592Sbms inm = (struct in_multi *)ifma->ifma_protospec; 2822189592Sbms if (!in_hosteq(inm->inm_addr, group)) 2823189592Sbms continue; 2824189592Sbms fmode = inm->inm_st[1].iss_fmode; 2825189592Sbms retval = SYSCTL_OUT(req, &fmode, sizeof(uint32_t)); 2826189592Sbms if (retval != 0) 2827189592Sbms break; 2828189592Sbms RB_FOREACH(ims, ip_msource_tree, &inm->inm_srcs) { 2829189592Sbms#ifdef KTR 2830189592Sbms struct in_addr ina; 2831189592Sbms ina.s_addr = htonl(ims->ims_haddr); 2832189592Sbms CTR2(KTR_IGMPV3, "%s: visit node %s", __func__, 2833189592Sbms inet_ntoa(ina)); 2834189592Sbms#endif 2835189592Sbms /* 2836189592Sbms * Only copy-out sources which are in-mode. 2837189592Sbms */ 2838189592Sbms if (fmode != ims_get_mode(inm, ims, 1)) { 2839189592Sbms CTR1(KTR_IGMPV3, "%s: skip non-in-mode", 2840189592Sbms __func__); 2841189592Sbms continue; 2842189592Sbms } 2843189592Sbms src.s_addr = htonl(ims->ims_haddr); 2844189592Sbms retval = SYSCTL_OUT(req, &src, sizeof(struct in_addr)); 2845189592Sbms if (retval != 0) 2846189592Sbms break; 2847189592Sbms } 2848189592Sbms } 2849233200Sjhb IF_ADDR_RUNLOCK(ifp); 2850189592Sbms 2851189592Sbms IN_MULTI_UNLOCK(); 2852189592Sbms 2853189592Sbms return (retval); 2854189592Sbms} 2855189592Sbms 2856189592Sbms#ifdef KTR 2857189592Sbms 2858189592Sbmsstatic const char *inm_modestrs[] = { "un", "in", "ex" }; 2859189592Sbms 2860189592Sbmsstatic const char * 2861189592Sbmsinm_mode_str(const int mode) 2862189592Sbms{ 2863189592Sbms 2864189592Sbms if (mode >= MCAST_UNDEFINED && mode <= MCAST_EXCLUDE) 2865189592Sbms return (inm_modestrs[mode]); 2866189592Sbms return ("??"); 2867189592Sbms} 2868189592Sbms 2869189592Sbmsstatic const char *inm_statestrs[] = { 2870189592Sbms "not-member", 2871189592Sbms "silent", 2872189592Sbms "idle", 2873189592Sbms "lazy", 2874189592Sbms "sleeping", 2875189592Sbms "awakening", 2876189592Sbms "query-pending", 2877189592Sbms "sg-query-pending", 2878189592Sbms "leaving" 2879189592Sbms}; 2880189592Sbms 2881189592Sbmsstatic const char * 2882189592Sbmsinm_state_str(const int state) 2883189592Sbms{ 2884189592Sbms 2885189592Sbms if (state >= IGMP_NOT_MEMBER && state <= IGMP_LEAVING_MEMBER) 2886189592Sbms return (inm_statestrs[state]); 2887189592Sbms return ("??"); 2888189592Sbms} 2889189592Sbms 2890189592Sbms/* 2891189592Sbms * Dump an in_multi structure to the console. 2892189592Sbms */ 2893189592Sbmsvoid 2894189592Sbmsinm_print(const struct in_multi *inm) 2895189592Sbms{ 2896189592Sbms int t; 2897189592Sbms 2898190753Skan if ((ktr_mask & KTR_IGMPV3) == 0) 2899189635Sbms return; 2900189635Sbms 2901189592Sbms printf("%s: --- begin inm %p ---\n", __func__, inm); 2902189592Sbms printf("addr %s ifp %p(%s) ifma %p\n", 2903189592Sbms inet_ntoa(inm->inm_addr), 2904189592Sbms inm->inm_ifp, 2905189592Sbms inm->inm_ifp->if_xname, 2906189592Sbms inm->inm_ifma); 2907189592Sbms printf("timer %u state %s refcount %u scq.len %u\n", 2908189592Sbms inm->inm_timer, 2909189592Sbms inm_state_str(inm->inm_state), 2910189592Sbms inm->inm_refcount, 2911189592Sbms inm->inm_scq.ifq_len); 2912189592Sbms printf("igi %p nsrc %lu sctimer %u scrv %u\n", 2913189592Sbms inm->inm_igi, 2914189592Sbms inm->inm_nsrc, 2915189592Sbms inm->inm_sctimer, 2916189592Sbms inm->inm_scrv); 2917189592Sbms for (t = 0; t < 2; t++) { 2918189592Sbms printf("t%d: fmode %s asm %u ex %u in %u rec %u\n", t, 2919189592Sbms inm_mode_str(inm->inm_st[t].iss_fmode), 2920189592Sbms inm->inm_st[t].iss_asm, 2921189592Sbms inm->inm_st[t].iss_ex, 2922189592Sbms inm->inm_st[t].iss_in, 2923189592Sbms inm->inm_st[t].iss_rec); 2924189592Sbms } 2925189592Sbms printf("%s: --- end inm %p ---\n", __func__, inm); 2926189592Sbms} 2927189592Sbms 2928189592Sbms#else /* !KTR */ 2929189592Sbms 2930189592Sbmsvoid 2931189592Sbmsinm_print(const struct in_multi *inm) 2932189592Sbms{ 2933189592Sbms 2934189592Sbms} 2935189592Sbms 2936189592Sbms#endif /* KTR */ 2937189592Sbms 2938189592SbmsRB_GENERATE(ip_msource_tree, ip_msource, ims_link, ip_msource_cmp); 2939