in_mcast.c revision 194760
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: head/sys/netinet/in_mcast.c 194760 2009-06-23 20:19:09Z rwatson $"); 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> 48181803Sbz#include <sys/vimage.h> 49189592Sbms#include <sys/ktr.h> 50189592Sbms#include <sys/tree.h> 51170613Sbms 52170613Sbms#include <net/if.h> 53170613Sbms#include <net/if_dl.h> 54170613Sbms#include <net/route.h> 55185571Sbz#include <net/vnet.h> 56170613Sbms 57170613Sbms#include <netinet/in.h> 58170613Sbms#include <netinet/in_systm.h> 59170613Sbms#include <netinet/in_pcb.h> 60170613Sbms#include <netinet/in_var.h> 61170613Sbms#include <netinet/ip_var.h> 62170613Sbms#include <netinet/igmp_var.h> 63185571Sbz#include <netinet/vinet.h> 64170613Sbms 65189592Sbms#ifndef KTR_IGMPV3 66191659Sbms#define KTR_IGMPV3 KTR_INET 67189592Sbms#endif 68189592Sbms 69170613Sbms#ifndef __SOCKUNION_DECLARED 70170613Sbmsunion sockunion { 71170613Sbms struct sockaddr_storage ss; 72170613Sbms struct sockaddr sa; 73170613Sbms struct sockaddr_dl sdl; 74170613Sbms struct sockaddr_in sin; 75170613Sbms}; 76170613Sbmstypedef union sockunion sockunion_t; 77170613Sbms#define __SOCKUNION_DECLARED 78170613Sbms#endif /* __SOCKUNION_DECLARED */ 79170613Sbms 80189592Sbmsstatic MALLOC_DEFINE(M_INMFILTER, "in_mfilter", 81189592Sbms "IPv4 multicast PCB-layer source filter"); 82170613Sbmsstatic MALLOC_DEFINE(M_IPMADDR, "in_multi", "IPv4 multicast group"); 83170613Sbmsstatic MALLOC_DEFINE(M_IPMOPTS, "ip_moptions", "IPv4 multicast options"); 84189592Sbmsstatic MALLOC_DEFINE(M_IPMSOURCE, "ip_msource", 85189592Sbms "IPv4 multicast IGMP-layer source filter"); 86170613Sbms 87189592Sbms#ifdef VIMAGE_GLOBALS 88189592Sbmsstruct in_multihead in_multihead; /* XXX now unused; retain for ABI */ 89189592Sbms#endif 90189592Sbms 91170613Sbms/* 92189592Sbms * Locking: 93189592Sbms * - Lock order is: Giant, INP_WLOCK, IN_MULTI_LOCK, IGMP_LOCK, IF_ADDR_LOCK. 94189592Sbms * - The IF_ADDR_LOCK is implicitly taken by inm_lookup() earlier, however 95189592Sbms * it can be taken by code in net/if.c also. 96189592Sbms * - ip_moptions and in_mfilter are covered by the INP_WLOCK. 97189592Sbms * 98189592Sbms * struct in_multi is covered by IN_MULTI_LOCK. There isn't strictly 99189592Sbms * any need for in_multi itself to be virtualized -- it is bound to an ifp 100189592Sbms * anyway no matter what happens. 101170613Sbms */ 102170613Sbmsstruct mtx in_multi_mtx; 103189592SbmsMTX_SYSINIT(in_multi_mtx, &in_multi_mtx, "in_multi_mtx", MTX_DEF); 104170613Sbms 105170613Sbms/* 106170613Sbms * Functions with non-static linkage defined in this file should be 107170613Sbms * declared in in_var.h: 108189592Sbms * imo_multi_filter() 109170613Sbms * in_addmulti() 110170613Sbms * in_delmulti() 111189592Sbms * in_joingroup() 112189592Sbms * in_joingroup_locked() 113189592Sbms * in_leavegroup() 114189592Sbms * in_leavegroup_locked() 115170613Sbms * and ip_var.h: 116170613Sbms * inp_freemoptions() 117170613Sbms * inp_getmoptions() 118170613Sbms * inp_setmoptions() 119189592Sbms * 120189592Sbms * XXX: Both carp and pf need to use the legacy (*,G) KPIs in_addmulti() 121189592Sbms * and in_delmulti(). 122170613Sbms */ 123189592Sbmsstatic void imf_commit(struct in_mfilter *); 124189592Sbmsstatic int imf_get_source(struct in_mfilter *imf, 125189592Sbms const struct sockaddr_in *psin, 126189592Sbms struct in_msource **); 127189592Sbmsstatic struct in_msource * 128189592Sbms imf_graft(struct in_mfilter *, const uint8_t, 129189592Sbms const struct sockaddr_in *); 130189592Sbmsstatic void imf_leave(struct in_mfilter *); 131189592Sbmsstatic int imf_prune(struct in_mfilter *, const struct sockaddr_in *); 132189592Sbmsstatic void imf_purge(struct in_mfilter *); 133189592Sbmsstatic void imf_rollback(struct in_mfilter *); 134189592Sbmsstatic void imf_reap(struct in_mfilter *); 135170613Sbmsstatic int imo_grow(struct ip_moptions *); 136189592Sbmsstatic size_t imo_match_group(const struct ip_moptions *, 137189592Sbms const struct ifnet *, const struct sockaddr *); 138189592Sbmsstatic struct in_msource * 139189592Sbms imo_match_source(const struct ip_moptions *, const size_t, 140189592Sbms const struct sockaddr *); 141189592Sbmsstatic void ims_merge(struct ip_msource *ims, 142189592Sbms const struct in_msource *lims, const int rollback); 143189592Sbmsstatic int in_getmulti(struct ifnet *, const struct in_addr *, 144189592Sbms struct in_multi **); 145189592Sbmsstatic int inm_get_source(struct in_multi *inm, const in_addr_t haddr, 146189592Sbms const int noalloc, struct ip_msource **pims); 147189592Sbmsstatic int inm_is_ifp_detached(const struct in_multi *); 148189592Sbmsstatic int inm_merge(struct in_multi *, /*const*/ struct in_mfilter *); 149189592Sbmsstatic void inm_purge(struct in_multi *); 150189592Sbmsstatic void inm_reap(struct in_multi *); 151170613Sbmsstatic struct ip_moptions * 152170613Sbms inp_findmoptions(struct inpcb *); 153170613Sbmsstatic int inp_get_source_filters(struct inpcb *, struct sockopt *); 154170613Sbmsstatic int inp_join_group(struct inpcb *, struct sockopt *); 155170613Sbmsstatic int inp_leave_group(struct inpcb *, struct sockopt *); 156189592Sbmsstatic struct ifnet * 157189592Sbms inp_lookup_mcast_ifp(const struct inpcb *, 158189592Sbms const struct sockaddr_in *, const struct in_addr); 159189592Sbmsstatic int inp_block_unblock_source(struct inpcb *, struct sockopt *); 160170613Sbmsstatic int inp_set_multicast_if(struct inpcb *, struct sockopt *); 161170613Sbmsstatic int inp_set_source_filters(struct inpcb *, struct sockopt *); 162189592Sbmsstatic int sysctl_ip_mcast_filters(SYSCTL_HANDLER_ARGS); 163170613Sbms 164189357SbmsSYSCTL_NODE(_net_inet_ip, OID_AUTO, mcast, CTLFLAG_RW, 0, "IPv4 multicast"); 165189357Sbms 166189592Sbmsstatic u_long in_mcast_maxgrpsrc = IP_MAX_GROUP_SRC_FILTER; 167189592SbmsSYSCTL_ULONG(_net_inet_ip_mcast, OID_AUTO, maxgrpsrc, 168189592Sbms CTLFLAG_RW | CTLFLAG_TUN, &in_mcast_maxgrpsrc, 0, 169189592Sbms "Max source filters per group"); 170189592SbmsTUNABLE_ULONG("net.inet.ip.mcast.maxgrpsrc", &in_mcast_maxgrpsrc); 171189592Sbms 172189592Sbmsstatic u_long in_mcast_maxsocksrc = IP_MAX_SOCK_SRC_FILTER; 173189592SbmsSYSCTL_ULONG(_net_inet_ip_mcast, OID_AUTO, maxsocksrc, 174189592Sbms CTLFLAG_RW | CTLFLAG_TUN, &in_mcast_maxsocksrc, 0, 175189592Sbms "Max source filters per socket"); 176189592SbmsTUNABLE_ULONG("net.inet.ip.mcast.maxsocksrc", &in_mcast_maxsocksrc); 177189592Sbms 178189357Sbmsint in_mcast_loop = IP_DEFAULT_MULTICAST_LOOP; 179189357SbmsSYSCTL_INT(_net_inet_ip_mcast, OID_AUTO, loop, CTLFLAG_RW | CTLFLAG_TUN, 180189357Sbms &in_mcast_loop, 0, "Loopback multicast datagrams by default"); 181189357SbmsTUNABLE_INT("net.inet.ip.mcast.loop", &in_mcast_loop); 182189357Sbms 183189592SbmsSYSCTL_NODE(_net_inet_ip_mcast, OID_AUTO, filters, 184189592Sbms CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_ip_mcast_filters, 185189592Sbms "Per-interface stack-wide source filters"); 186189592Sbms 187170613Sbms/* 188189592Sbms * Inline function which wraps assertions for a valid ifp. 189189592Sbms * The ifnet layer will set the ifma's ifp pointer to NULL if the ifp 190189592Sbms * is detached. 191189592Sbms */ 192189592Sbmsstatic int __inline 193189592Sbmsinm_is_ifp_detached(const struct in_multi *inm) 194189592Sbms{ 195189592Sbms struct ifnet *ifp; 196189592Sbms 197189592Sbms KASSERT(inm->inm_ifma != NULL, ("%s: no ifma", __func__)); 198189592Sbms ifp = inm->inm_ifma->ifma_ifp; 199189592Sbms if (ifp != NULL) { 200189592Sbms /* 201189592Sbms * Sanity check that netinet's notion of ifp is the 202189592Sbms * same as net's. 203189592Sbms */ 204189592Sbms KASSERT(inm->inm_ifp == ifp, ("%s: bad ifp", __func__)); 205189592Sbms } 206189592Sbms 207189592Sbms return (ifp == NULL); 208189592Sbms} 209189592Sbms 210189592Sbms/* 211189592Sbms * Initialize an in_mfilter structure to a known state at t0, t1 212189592Sbms * with an empty source filter list. 213189592Sbms */ 214189592Sbmsstatic __inline void 215189592Sbmsimf_init(struct in_mfilter *imf, const int st0, const int st1) 216189592Sbms{ 217189592Sbms memset(imf, 0, sizeof(struct in_mfilter)); 218189592Sbms RB_INIT(&imf->imf_sources); 219189592Sbms imf->imf_st[0] = st0; 220189592Sbms imf->imf_st[1] = st1; 221189592Sbms} 222189592Sbms 223189592Sbms/* 224170613Sbms * Resize the ip_moptions vector to the next power-of-two minus 1. 225170613Sbms * May be called with locks held; do not sleep. 226170613Sbms */ 227170613Sbmsstatic int 228170613Sbmsimo_grow(struct ip_moptions *imo) 229170613Sbms{ 230170613Sbms struct in_multi **nmships; 231170613Sbms struct in_multi **omships; 232170613Sbms struct in_mfilter *nmfilters; 233170613Sbms struct in_mfilter *omfilters; 234170613Sbms size_t idx; 235170613Sbms size_t newmax; 236170613Sbms size_t oldmax; 237170613Sbms 238170613Sbms nmships = NULL; 239170613Sbms nmfilters = NULL; 240170613Sbms omships = imo->imo_membership; 241170613Sbms omfilters = imo->imo_mfilters; 242170613Sbms oldmax = imo->imo_max_memberships; 243170613Sbms newmax = ((oldmax + 1) * 2) - 1; 244170613Sbms 245170613Sbms if (newmax <= IP_MAX_MEMBERSHIPS) { 246170613Sbms nmships = (struct in_multi **)realloc(omships, 247170613Sbms sizeof(struct in_multi *) * newmax, M_IPMOPTS, M_NOWAIT); 248170613Sbms nmfilters = (struct in_mfilter *)realloc(omfilters, 249189592Sbms sizeof(struct in_mfilter) * newmax, M_INMFILTER, M_NOWAIT); 250170613Sbms if (nmships != NULL && nmfilters != NULL) { 251170613Sbms /* Initialize newly allocated source filter heads. */ 252170613Sbms for (idx = oldmax; idx < newmax; idx++) { 253189592Sbms imf_init(&nmfilters[idx], MCAST_UNDEFINED, 254189592Sbms MCAST_EXCLUDE); 255170613Sbms } 256170613Sbms imo->imo_max_memberships = newmax; 257170613Sbms imo->imo_membership = nmships; 258170613Sbms imo->imo_mfilters = nmfilters; 259170613Sbms } 260170613Sbms } 261170613Sbms 262170613Sbms if (nmships == NULL || nmfilters == NULL) { 263170613Sbms if (nmships != NULL) 264170613Sbms free(nmships, M_IPMOPTS); 265170613Sbms if (nmfilters != NULL) 266189592Sbms free(nmfilters, M_INMFILTER); 267170613Sbms return (ETOOMANYREFS); 268170613Sbms } 269170613Sbms 270170613Sbms return (0); 271170613Sbms} 272170613Sbms 273170613Sbms/* 274170613Sbms * Find an IPv4 multicast group entry for this ip_moptions instance 275170613Sbms * which matches the specified group, and optionally an interface. 276170613Sbms * Return its index into the array, or -1 if not found. 277170613Sbms */ 278189592Sbmsstatic size_t 279189592Sbmsimo_match_group(const struct ip_moptions *imo, const struct ifnet *ifp, 280189592Sbms const struct sockaddr *group) 281170613Sbms{ 282189592Sbms const struct sockaddr_in *gsin; 283170613Sbms struct in_multi **pinm; 284170613Sbms int idx; 285170613Sbms int nmships; 286170613Sbms 287189592Sbms gsin = (const struct sockaddr_in *)group; 288170613Sbms 289170613Sbms /* The imo_membership array may be lazy allocated. */ 290170613Sbms if (imo->imo_membership == NULL || imo->imo_num_memberships == 0) 291170613Sbms return (-1); 292170613Sbms 293170613Sbms nmships = imo->imo_num_memberships; 294170613Sbms pinm = &imo->imo_membership[0]; 295170613Sbms for (idx = 0; idx < nmships; idx++, pinm++) { 296170613Sbms if (*pinm == NULL) 297170613Sbms continue; 298170613Sbms if ((ifp == NULL || ((*pinm)->inm_ifp == ifp)) && 299189592Sbms in_hosteq((*pinm)->inm_addr, gsin->sin_addr)) { 300170613Sbms break; 301170613Sbms } 302170613Sbms } 303170613Sbms if (idx >= nmships) 304170613Sbms idx = -1; 305170613Sbms 306170613Sbms return (idx); 307170613Sbms} 308170613Sbms 309170613Sbms/* 310189592Sbms * Find an IPv4 multicast source entry for this imo which matches 311170613Sbms * the given group index for this socket, and source address. 312189592Sbms * 313189592Sbms * NOTE: This does not check if the entry is in-mode, merely if 314189592Sbms * it exists, which may not be the desired behaviour. 315170613Sbms */ 316189592Sbmsstatic struct in_msource * 317189592Sbmsimo_match_source(const struct ip_moptions *imo, const size_t gidx, 318189592Sbms const struct sockaddr *src) 319170613Sbms{ 320189592Sbms struct ip_msource find; 321170613Sbms struct in_mfilter *imf; 322189592Sbms struct ip_msource *ims; 323189592Sbms const sockunion_t *psa; 324170613Sbms 325170613Sbms KASSERT(src->sa_family == AF_INET, ("%s: !AF_INET", __func__)); 326170613Sbms KASSERT(gidx != -1 && gidx < imo->imo_num_memberships, 327170613Sbms ("%s: invalid index %d\n", __func__, (int)gidx)); 328170613Sbms 329170613Sbms /* The imo_mfilters array may be lazy allocated. */ 330170613Sbms if (imo->imo_mfilters == NULL) 331170613Sbms return (NULL); 332170613Sbms imf = &imo->imo_mfilters[gidx]; 333170613Sbms 334189592Sbms /* Source trees are keyed in host byte order. */ 335189592Sbms psa = (const sockunion_t *)src; 336189592Sbms find.ims_haddr = ntohl(psa->sin.sin_addr.s_addr); 337189592Sbms ims = RB_FIND(ip_msource_tree, &imf->imf_sources, &find); 338189592Sbms 339189592Sbms return ((struct in_msource *)ims); 340170613Sbms} 341170613Sbms 342170613Sbms/* 343189592Sbms * Perform filtering for multicast datagrams on a socket by group and source. 344189592Sbms * 345189592Sbms * Returns 0 if a datagram should be allowed through, or various error codes 346189592Sbms * if the socket was not a member of the group, or the source was muted, etc. 347170613Sbms */ 348189592Sbmsint 349189592Sbmsimo_multi_filter(const struct ip_moptions *imo, const struct ifnet *ifp, 350189592Sbms const struct sockaddr *group, const struct sockaddr *src) 351170613Sbms{ 352189592Sbms size_t gidx; 353189592Sbms struct in_msource *ims; 354189592Sbms int mode; 355189592Sbms 356189592Sbms KASSERT(ifp != NULL, ("%s: null ifp", __func__)); 357189592Sbms 358189592Sbms gidx = imo_match_group(imo, ifp, group); 359189592Sbms if (gidx == -1) 360189592Sbms return (MCAST_NOTGMEMBER); 361189592Sbms 362189592Sbms /* 363189592Sbms * Check if the source was included in an (S,G) join. 364189592Sbms * Allow reception on exclusive memberships by default, 365189592Sbms * reject reception on inclusive memberships by default. 366189592Sbms * Exclude source only if an in-mode exclude filter exists. 367189592Sbms * Include source only if an in-mode include filter exists. 368189592Sbms * NOTE: We are comparing group state here at IGMP t1 (now) 369189592Sbms * with socket-layer t0 (since last downcall). 370189592Sbms */ 371189592Sbms mode = imo->imo_mfilters[gidx].imf_st[1]; 372189592Sbms ims = imo_match_source(imo, gidx, src); 373189592Sbms 374189592Sbms if ((ims == NULL && mode == MCAST_INCLUDE) || 375189592Sbms (ims != NULL && ims->imsl_st[0] != mode)) 376189592Sbms return (MCAST_NOTSMEMBER); 377189592Sbms 378189592Sbms return (MCAST_PASS); 379189592Sbms} 380189592Sbms 381189592Sbms/* 382189592Sbms * Find and return a reference to an in_multi record for (ifp, group), 383189592Sbms * and bump its reference count. 384189592Sbms * If one does not exist, try to allocate it, and update link-layer multicast 385189592Sbms * filters on ifp to listen for group. 386189592Sbms * Assumes the IN_MULTI lock is held across the call. 387189592Sbms * Return 0 if successful, otherwise return an appropriate error code. 388189592Sbms */ 389189592Sbmsstatic int 390189592Sbmsin_getmulti(struct ifnet *ifp, const struct in_addr *group, 391189592Sbms struct in_multi **pinm) 392189592Sbms{ 393189592Sbms struct sockaddr_in gsin; 394189592Sbms struct ifmultiaddr *ifma; 395189592Sbms struct in_ifinfo *ii; 396189592Sbms struct in_multi *inm; 397189592Sbms int error; 398170613Sbms 399189592Sbms IN_MULTI_LOCK_ASSERT(); 400170613Sbms 401189592Sbms ii = (struct in_ifinfo *)ifp->if_afdata[AF_INET]; 402170613Sbms 403189592Sbms inm = inm_lookup(ifp, *group); 404170613Sbms if (inm != NULL) { 405170613Sbms /* 406170613Sbms * If we already joined this group, just bump the 407170613Sbms * refcount and return it. 408170613Sbms */ 409170613Sbms KASSERT(inm->inm_refcount >= 1, 410170613Sbms ("%s: bad refcount %d", __func__, inm->inm_refcount)); 411170613Sbms ++inm->inm_refcount; 412189592Sbms *pinm = inm; 413189592Sbms return (0); 414189592Sbms } 415170613Sbms 416189592Sbms memset(&gsin, 0, sizeof(gsin)); 417189592Sbms gsin.sin_family = AF_INET; 418189592Sbms gsin.sin_len = sizeof(struct sockaddr_in); 419189592Sbms gsin.sin_addr = *group; 420170613Sbms 421189592Sbms /* 422189592Sbms * Check if a link-layer group is already associated 423189592Sbms * with this network-layer group on the given ifnet. 424189592Sbms */ 425189592Sbms error = if_addmulti(ifp, (struct sockaddr *)&gsin, &ifma); 426189592Sbms if (error != 0) 427189592Sbms return (error); 428189592Sbms 429189931Sbms /* XXX ifma_protospec must be covered by IF_ADDR_LOCK */ 430189931Sbms IF_ADDR_LOCK(ifp); 431189931Sbms 432189592Sbms /* 433189592Sbms * If something other than netinet is occupying the link-layer 434189592Sbms * group, print a meaningful error message and back out of 435189592Sbms * the allocation. 436189592Sbms * Otherwise, bump the refcount on the existing network-layer 437189592Sbms * group association and return it. 438189592Sbms */ 439189592Sbms if (ifma->ifma_protospec != NULL) { 440189592Sbms inm = (struct in_multi *)ifma->ifma_protospec; 441170613Sbms#ifdef INVARIANTS 442189592Sbms KASSERT(ifma->ifma_addr != NULL, ("%s: no ifma_addr", 443189592Sbms __func__)); 444189592Sbms KASSERT(ifma->ifma_addr->sa_family == AF_INET, 445189592Sbms ("%s: ifma not AF_INET", __func__)); 446189592Sbms KASSERT(inm != NULL, ("%s: no ifma_protospec", __func__)); 447189592Sbms if (inm->inm_ifma != ifma || inm->inm_ifp != ifp || 448189592Sbms !in_hosteq(inm->inm_addr, *group)) 449189592Sbms panic("%s: ifma %p is inconsistent with %p (%s)", 450189592Sbms __func__, ifma, inm, inet_ntoa(*group)); 451170613Sbms#endif 452189592Sbms ++inm->inm_refcount; 453189592Sbms *pinm = inm; 454189931Sbms IF_ADDR_UNLOCK(ifp); 455189592Sbms return (0); 456189592Sbms } 457189592Sbms 458189931Sbms IF_ADDR_LOCK_ASSERT(ifp); 459189931Sbms 460189592Sbms /* 461189592Sbms * A new in_multi record is needed; allocate and initialize it. 462189592Sbms * We DO NOT perform an IGMP join as the in_ layer may need to 463189592Sbms * push an initial source list down to IGMP to support SSM. 464189592Sbms * 465189592Sbms * The initial source filter state is INCLUDE, {} as per the RFC. 466189592Sbms */ 467189592Sbms inm = malloc(sizeof(*inm), M_IPMADDR, M_NOWAIT | M_ZERO); 468189592Sbms if (inm == NULL) { 469189592Sbms if_delmulti_ifma(ifma); 470189931Sbms IF_ADDR_UNLOCK(ifp); 471189592Sbms return (ENOMEM); 472189592Sbms } 473189592Sbms inm->inm_addr = *group; 474189592Sbms inm->inm_ifp = ifp; 475189592Sbms inm->inm_igi = ii->ii_igmp; 476189592Sbms inm->inm_ifma = ifma; 477189592Sbms inm->inm_refcount = 1; 478189592Sbms inm->inm_state = IGMP_NOT_MEMBER; 479189592Sbms 480189592Sbms /* 481189592Sbms * Pending state-changes per group are subject to a bounds check. 482189592Sbms */ 483189592Sbms IFQ_SET_MAXLEN(&inm->inm_scq, IGMP_MAX_STATE_CHANGES); 484189592Sbms 485189592Sbms inm->inm_st[0].iss_fmode = MCAST_UNDEFINED; 486189592Sbms inm->inm_st[1].iss_fmode = MCAST_UNDEFINED; 487189592Sbms RB_INIT(&inm->inm_srcs); 488189592Sbms 489189592Sbms ifma->ifma_protospec = inm; 490189592Sbms 491189592Sbms *pinm = inm; 492189592Sbms 493189931Sbms IF_ADDR_UNLOCK(ifp); 494189592Sbms return (0); 495189592Sbms} 496189592Sbms 497189592Sbms/* 498189592Sbms * Drop a reference to an in_multi record. 499189592Sbms * 500189592Sbms * If the refcount drops to 0, free the in_multi record and 501189592Sbms * delete the underlying link-layer membership. 502189592Sbms */ 503189592Sbmsvoid 504189592Sbmsinm_release_locked(struct in_multi *inm) 505189592Sbms{ 506189592Sbms struct ifmultiaddr *ifma; 507189592Sbms 508189592Sbms IN_MULTI_LOCK_ASSERT(); 509189592Sbms 510189592Sbms CTR2(KTR_IGMPV3, "%s: refcount is %d", __func__, inm->inm_refcount); 511189592Sbms 512189592Sbms if (--inm->inm_refcount > 0) { 513189592Sbms CTR2(KTR_IGMPV3, "%s: refcount is now %d", __func__, 514189592Sbms inm->inm_refcount); 515189592Sbms return; 516189592Sbms } 517189592Sbms 518189592Sbms CTR2(KTR_IGMPV3, "%s: freeing inm %p", __func__, inm); 519189592Sbms 520189592Sbms ifma = inm->inm_ifma; 521189592Sbms 522189931Sbms /* XXX this access is not covered by IF_ADDR_LOCK */ 523189592Sbms CTR2(KTR_IGMPV3, "%s: purging ifma %p", __func__, ifma); 524189592Sbms KASSERT(ifma->ifma_protospec == inm, 525189592Sbms ("%s: ifma_protospec != inm", __func__)); 526189592Sbms ifma->ifma_protospec = NULL; 527189592Sbms 528189592Sbms inm_purge(inm); 529189592Sbms 530189592Sbms free(inm, M_IPMADDR); 531189592Sbms 532189592Sbms if_delmulti_ifma(ifma); 533189592Sbms} 534189592Sbms 535189592Sbms/* 536189592Sbms * Clear recorded source entries for a group. 537189592Sbms * Used by the IGMP code. Caller must hold the IN_MULTI lock. 538189592Sbms * FIXME: Should reap. 539189592Sbms */ 540189592Sbmsvoid 541189592Sbmsinm_clear_recorded(struct in_multi *inm) 542189592Sbms{ 543189592Sbms struct ip_msource *ims; 544189592Sbms 545189592Sbms IN_MULTI_LOCK_ASSERT(); 546189592Sbms 547189592Sbms RB_FOREACH(ims, ip_msource_tree, &inm->inm_srcs) { 548189592Sbms if (ims->ims_stp) { 549189592Sbms ims->ims_stp = 0; 550189592Sbms --inm->inm_st[1].iss_rec; 551170613Sbms } 552189592Sbms } 553189592Sbms KASSERT(inm->inm_st[1].iss_rec == 0, 554189592Sbms ("%s: iss_rec %d not 0", __func__, inm->inm_st[1].iss_rec)); 555189592Sbms} 556170613Sbms 557189592Sbms/* 558189592Sbms * Record a source as pending for a Source-Group IGMPv3 query. 559189592Sbms * This lives here as it modifies the shared tree. 560189592Sbms * 561189592Sbms * inm is the group descriptor. 562189592Sbms * naddr is the address of the source to record in network-byte order. 563189592Sbms * 564189592Sbms * If the net.inet.igmp.sgalloc sysctl is non-zero, we will 565189592Sbms * lazy-allocate a source node in response to an SG query. 566189592Sbms * Otherwise, no allocation is performed. This saves some memory 567189592Sbms * with the trade-off that the source will not be reported to the 568189592Sbms * router if joined in the window between the query response and 569189592Sbms * the group actually being joined on the local host. 570189592Sbms * 571189592Sbms * VIMAGE: XXX: Currently the igmp_sgalloc feature has been removed. 572189592Sbms * This turns off the allocation of a recorded source entry if 573189592Sbms * the group has not been joined. 574189592Sbms * 575189592Sbms * Return 0 if the source didn't exist or was already marked as recorded. 576189592Sbms * Return 1 if the source was marked as recorded by this function. 577189592Sbms * Return <0 if any error occured (negated errno code). 578189592Sbms */ 579189592Sbmsint 580189592Sbmsinm_record_source(struct in_multi *inm, const in_addr_t naddr) 581189592Sbms{ 582189592Sbms struct ip_msource find; 583189592Sbms struct ip_msource *ims, *nims; 584189592Sbms 585189592Sbms IN_MULTI_LOCK_ASSERT(); 586189592Sbms 587189592Sbms find.ims_haddr = ntohl(naddr); 588189592Sbms ims = RB_FIND(ip_msource_tree, &inm->inm_srcs, &find); 589189592Sbms if (ims && ims->ims_stp) 590189592Sbms return (0); 591189592Sbms if (ims == NULL) { 592189592Sbms if (inm->inm_nsrc == in_mcast_maxgrpsrc) 593189592Sbms return (-ENOSPC); 594189592Sbms nims = malloc(sizeof(struct ip_msource), M_IPMSOURCE, 595189592Sbms M_NOWAIT | M_ZERO); 596189592Sbms if (nims == NULL) 597189592Sbms return (-ENOMEM); 598189592Sbms nims->ims_haddr = find.ims_haddr; 599189592Sbms RB_INSERT(ip_msource_tree, &inm->inm_srcs, nims); 600189592Sbms ++inm->inm_nsrc; 601189592Sbms ims = nims; 602189592Sbms } 603189592Sbms 604189592Sbms /* 605189592Sbms * Mark the source as recorded and update the recorded 606189592Sbms * source count. 607189592Sbms */ 608189592Sbms ++ims->ims_stp; 609189592Sbms ++inm->inm_st[1].iss_rec; 610189592Sbms 611189592Sbms return (1); 612189592Sbms} 613189592Sbms 614189592Sbms/* 615189592Sbms * Return a pointer to an in_msource owned by an in_mfilter, 616189592Sbms * given its source address. 617189592Sbms * Lazy-allocate if needed. If this is a new entry its filter state is 618189592Sbms * undefined at t0. 619189592Sbms * 620189592Sbms * imf is the filter set being modified. 621189592Sbms * haddr is the source address in *host* byte-order. 622189592Sbms * 623189592Sbms * SMPng: May be called with locks held; malloc must not block. 624189592Sbms */ 625189592Sbmsstatic int 626189592Sbmsimf_get_source(struct in_mfilter *imf, const struct sockaddr_in *psin, 627189592Sbms struct in_msource **plims) 628189592Sbms{ 629189592Sbms struct ip_msource find; 630189592Sbms struct ip_msource *ims, *nims; 631189592Sbms struct in_msource *lims; 632189592Sbms int error; 633189592Sbms 634189592Sbms error = 0; 635189592Sbms ims = NULL; 636189592Sbms lims = NULL; 637189592Sbms 638189592Sbms /* key is host byte order */ 639189592Sbms find.ims_haddr = ntohl(psin->sin_addr.s_addr); 640189592Sbms ims = RB_FIND(ip_msource_tree, &imf->imf_sources, &find); 641189592Sbms lims = (struct in_msource *)ims; 642189592Sbms if (lims == NULL) { 643189592Sbms if (imf->imf_nsrc == in_mcast_maxsocksrc) 644189592Sbms return (ENOSPC); 645189592Sbms nims = malloc(sizeof(struct in_msource), M_INMFILTER, 646189592Sbms M_NOWAIT | M_ZERO); 647189592Sbms if (nims == NULL) 648189592Sbms return (ENOMEM); 649189592Sbms lims = (struct in_msource *)nims; 650189592Sbms lims->ims_haddr = find.ims_haddr; 651189592Sbms lims->imsl_st[0] = MCAST_UNDEFINED; 652189592Sbms RB_INSERT(ip_msource_tree, &imf->imf_sources, nims); 653189592Sbms ++imf->imf_nsrc; 654189592Sbms } 655189592Sbms 656189592Sbms *plims = lims; 657189592Sbms 658189592Sbms return (error); 659189592Sbms} 660189592Sbms 661189592Sbms/* 662189592Sbms * Graft a source entry into an existing socket-layer filter set, 663189592Sbms * maintaining any required invariants and checking allocations. 664189592Sbms * 665189592Sbms * The source is marked as being in the new filter mode at t1. 666189592Sbms * 667189592Sbms * Return the pointer to the new node, otherwise return NULL. 668189592Sbms */ 669189592Sbmsstatic struct in_msource * 670189592Sbmsimf_graft(struct in_mfilter *imf, const uint8_t st1, 671189592Sbms const struct sockaddr_in *psin) 672189592Sbms{ 673189592Sbms struct ip_msource *nims; 674189592Sbms struct in_msource *lims; 675189592Sbms 676189592Sbms nims = malloc(sizeof(struct in_msource), M_INMFILTER, 677189592Sbms M_NOWAIT | M_ZERO); 678189592Sbms if (nims == NULL) 679189592Sbms return (NULL); 680189592Sbms lims = (struct in_msource *)nims; 681189592Sbms lims->ims_haddr = ntohl(psin->sin_addr.s_addr); 682189592Sbms lims->imsl_st[0] = MCAST_UNDEFINED; 683189592Sbms lims->imsl_st[1] = st1; 684189592Sbms RB_INSERT(ip_msource_tree, &imf->imf_sources, nims); 685189592Sbms ++imf->imf_nsrc; 686189592Sbms 687189592Sbms return (lims); 688189592Sbms} 689189592Sbms 690189592Sbms/* 691189592Sbms * Prune a source entry from an existing socket-layer filter set, 692189592Sbms * maintaining any required invariants and checking allocations. 693189592Sbms * 694189592Sbms * The source is marked as being left at t1, it is not freed. 695189592Sbms * 696189592Sbms * Return 0 if no error occurred, otherwise return an errno value. 697189592Sbms */ 698189592Sbmsstatic int 699189592Sbmsimf_prune(struct in_mfilter *imf, const struct sockaddr_in *psin) 700189592Sbms{ 701189592Sbms struct ip_msource find; 702189592Sbms struct ip_msource *ims; 703189592Sbms struct in_msource *lims; 704189592Sbms 705189592Sbms /* key is host byte order */ 706189592Sbms find.ims_haddr = ntohl(psin->sin_addr.s_addr); 707189592Sbms ims = RB_FIND(ip_msource_tree, &imf->imf_sources, &find); 708189592Sbms if (ims == NULL) 709189592Sbms return (ENOENT); 710189592Sbms lims = (struct in_msource *)ims; 711189592Sbms lims->imsl_st[1] = MCAST_UNDEFINED; 712189592Sbms return (0); 713189592Sbms} 714189592Sbms 715189592Sbms/* 716189592Sbms * Revert socket-layer filter set deltas at t1 to t0 state. 717189592Sbms */ 718189592Sbmsstatic void 719189592Sbmsimf_rollback(struct in_mfilter *imf) 720189592Sbms{ 721189592Sbms struct ip_msource *ims, *tims; 722189592Sbms struct in_msource *lims; 723189592Sbms 724189592Sbms RB_FOREACH_SAFE(ims, ip_msource_tree, &imf->imf_sources, tims) { 725189592Sbms lims = (struct in_msource *)ims; 726189592Sbms if (lims->imsl_st[0] == lims->imsl_st[1]) { 727189592Sbms /* no change at t1 */ 728189592Sbms continue; 729189592Sbms } else if (lims->imsl_st[0] != MCAST_UNDEFINED) { 730189592Sbms /* revert change to existing source at t1 */ 731189592Sbms lims->imsl_st[1] = lims->imsl_st[0]; 732189592Sbms } else { 733189592Sbms /* revert source added t1 */ 734189592Sbms CTR2(KTR_IGMPV3, "%s: free ims %p", __func__, ims); 735189592Sbms RB_REMOVE(ip_msource_tree, &imf->imf_sources, ims); 736189592Sbms free(ims, M_INMFILTER); 737189592Sbms imf->imf_nsrc--; 738189592Sbms } 739189592Sbms } 740189592Sbms imf->imf_st[1] = imf->imf_st[0]; 741189592Sbms} 742189592Sbms 743189592Sbms/* 744189592Sbms * Mark socket-layer filter set as INCLUDE {} at t1. 745189592Sbms */ 746189592Sbmsstatic void 747189592Sbmsimf_leave(struct in_mfilter *imf) 748189592Sbms{ 749189592Sbms struct ip_msource *ims; 750189592Sbms struct in_msource *lims; 751189592Sbms 752189592Sbms RB_FOREACH(ims, ip_msource_tree, &imf->imf_sources) { 753189592Sbms lims = (struct in_msource *)ims; 754189592Sbms lims->imsl_st[1] = MCAST_UNDEFINED; 755189592Sbms } 756189592Sbms imf->imf_st[1] = MCAST_INCLUDE; 757189592Sbms} 758189592Sbms 759189592Sbms/* 760189592Sbms * Mark socket-layer filter set deltas as committed. 761189592Sbms */ 762189592Sbmsstatic void 763189592Sbmsimf_commit(struct in_mfilter *imf) 764189592Sbms{ 765189592Sbms struct ip_msource *ims; 766189592Sbms struct in_msource *lims; 767189592Sbms 768189592Sbms RB_FOREACH(ims, ip_msource_tree, &imf->imf_sources) { 769189592Sbms lims = (struct in_msource *)ims; 770189592Sbms lims->imsl_st[0] = lims->imsl_st[1]; 771189592Sbms } 772189592Sbms imf->imf_st[0] = imf->imf_st[1]; 773189592Sbms} 774189592Sbms 775189592Sbms/* 776189592Sbms * Reap unreferenced sources from socket-layer filter set. 777189592Sbms */ 778189592Sbmsstatic void 779189592Sbmsimf_reap(struct in_mfilter *imf) 780189592Sbms{ 781189592Sbms struct ip_msource *ims, *tims; 782189592Sbms struct in_msource *lims; 783189592Sbms 784189592Sbms RB_FOREACH_SAFE(ims, ip_msource_tree, &imf->imf_sources, tims) { 785189592Sbms lims = (struct in_msource *)ims; 786189592Sbms if ((lims->imsl_st[0] == MCAST_UNDEFINED) && 787189592Sbms (lims->imsl_st[1] == MCAST_UNDEFINED)) { 788189592Sbms CTR2(KTR_IGMPV3, "%s: free lims %p", __func__, ims); 789189592Sbms RB_REMOVE(ip_msource_tree, &imf->imf_sources, ims); 790189592Sbms free(ims, M_INMFILTER); 791189592Sbms imf->imf_nsrc--; 792189592Sbms } 793189592Sbms } 794189592Sbms} 795189592Sbms 796189592Sbms/* 797189592Sbms * Purge socket-layer filter set. 798189592Sbms */ 799189592Sbmsstatic void 800189592Sbmsimf_purge(struct in_mfilter *imf) 801189592Sbms{ 802189592Sbms struct ip_msource *ims, *tims; 803189592Sbms 804189592Sbms RB_FOREACH_SAFE(ims, ip_msource_tree, &imf->imf_sources, tims) { 805189592Sbms CTR2(KTR_IGMPV3, "%s: free ims %p", __func__, ims); 806189592Sbms RB_REMOVE(ip_msource_tree, &imf->imf_sources, ims); 807189592Sbms free(ims, M_INMFILTER); 808189592Sbms imf->imf_nsrc--; 809189592Sbms } 810189592Sbms imf->imf_st[0] = imf->imf_st[1] = MCAST_UNDEFINED; 811189592Sbms KASSERT(RB_EMPTY(&imf->imf_sources), 812189592Sbms ("%s: imf_sources not empty", __func__)); 813189592Sbms} 814189592Sbms 815189592Sbms/* 816189592Sbms * Look up a source filter entry for a multicast group. 817189592Sbms * 818189592Sbms * inm is the group descriptor to work with. 819189592Sbms * haddr is the host-byte-order IPv4 address to look up. 820189592Sbms * noalloc may be non-zero to suppress allocation of sources. 821189592Sbms * *pims will be set to the address of the retrieved or allocated source. 822189592Sbms * 823189592Sbms * SMPng: NOTE: may be called with locks held. 824189592Sbms * Return 0 if successful, otherwise return a non-zero error code. 825189592Sbms */ 826189592Sbmsstatic int 827189592Sbmsinm_get_source(struct in_multi *inm, const in_addr_t haddr, 828189592Sbms const int noalloc, struct ip_msource **pims) 829189592Sbms{ 830189592Sbms struct ip_msource find; 831189592Sbms struct ip_msource *ims, *nims; 832189592Sbms#ifdef KTR 833189592Sbms struct in_addr ia; 834189592Sbms#endif 835189592Sbms 836189592Sbms find.ims_haddr = haddr; 837189592Sbms ims = RB_FIND(ip_msource_tree, &inm->inm_srcs, &find); 838189592Sbms if (ims == NULL && !noalloc) { 839189592Sbms if (inm->inm_nsrc == in_mcast_maxgrpsrc) 840189592Sbms return (ENOSPC); 841189592Sbms nims = malloc(sizeof(struct ip_msource), M_IPMSOURCE, 842189592Sbms M_NOWAIT | M_ZERO); 843189592Sbms if (nims == NULL) 844189592Sbms return (ENOMEM); 845189592Sbms nims->ims_haddr = haddr; 846189592Sbms RB_INSERT(ip_msource_tree, &inm->inm_srcs, nims); 847189592Sbms ++inm->inm_nsrc; 848189592Sbms ims = nims; 849189592Sbms#ifdef KTR 850189592Sbms ia.s_addr = htonl(haddr); 851189592Sbms CTR3(KTR_IGMPV3, "%s: allocated %s as %p", __func__, 852189592Sbms inet_ntoa(ia), ims); 853189592Sbms#endif 854189592Sbms } 855189592Sbms 856189592Sbms *pims = ims; 857189592Sbms return (0); 858189592Sbms} 859189592Sbms 860189592Sbms/* 861189592Sbms * Merge socket-layer source into IGMP-layer source. 862189592Sbms * If rollback is non-zero, perform the inverse of the merge. 863189592Sbms */ 864189592Sbmsstatic void 865189592Sbmsims_merge(struct ip_msource *ims, const struct in_msource *lims, 866189592Sbms const int rollback) 867189592Sbms{ 868189592Sbms int n = rollback ? -1 : 1; 869189592Sbms#ifdef KTR 870189592Sbms struct in_addr ia; 871189592Sbms 872189592Sbms ia.s_addr = htonl(ims->ims_haddr); 873189592Sbms#endif 874189592Sbms 875189592Sbms if (lims->imsl_st[0] == MCAST_EXCLUDE) { 876189592Sbms CTR3(KTR_IGMPV3, "%s: t1 ex -= %d on %s", 877189592Sbms __func__, n, inet_ntoa(ia)); 878189592Sbms ims->ims_st[1].ex -= n; 879189592Sbms } else if (lims->imsl_st[0] == MCAST_INCLUDE) { 880189592Sbms CTR3(KTR_IGMPV3, "%s: t1 in -= %d on %s", 881189592Sbms __func__, n, inet_ntoa(ia)); 882189592Sbms ims->ims_st[1].in -= n; 883189592Sbms } 884189592Sbms 885189592Sbms if (lims->imsl_st[1] == MCAST_EXCLUDE) { 886189592Sbms CTR3(KTR_IGMPV3, "%s: t1 ex += %d on %s", 887189592Sbms __func__, n, inet_ntoa(ia)); 888189592Sbms ims->ims_st[1].ex += n; 889189592Sbms } else if (lims->imsl_st[1] == MCAST_INCLUDE) { 890189592Sbms CTR3(KTR_IGMPV3, "%s: t1 in += %d on %s", 891189592Sbms __func__, n, inet_ntoa(ia)); 892189592Sbms ims->ims_st[1].in += n; 893189592Sbms } 894189592Sbms} 895189592Sbms 896189592Sbms/* 897189592Sbms * Atomically update the global in_multi state, when a membership's 898189592Sbms * filter list is being updated in any way. 899189592Sbms * 900189592Sbms * imf is the per-inpcb-membership group filter pointer. 901189592Sbms * A fake imf may be passed for in-kernel consumers. 902189592Sbms * 903189592Sbms * XXX This is a candidate for a set-symmetric-difference style loop 904189592Sbms * which would eliminate the repeated lookup from root of ims nodes, 905189592Sbms * as they share the same key space. 906189592Sbms * 907189592Sbms * If any error occurred this function will back out of refcounts 908189592Sbms * and return a non-zero value. 909189592Sbms */ 910189592Sbmsstatic int 911189592Sbmsinm_merge(struct in_multi *inm, /*const*/ struct in_mfilter *imf) 912189592Sbms{ 913189592Sbms struct ip_msource *ims, *nims; 914189592Sbms struct in_msource *lims; 915189592Sbms int schanged, error; 916189592Sbms int nsrc0, nsrc1; 917189592Sbms 918189592Sbms schanged = 0; 919189592Sbms error = 0; 920189592Sbms nsrc1 = nsrc0 = 0; 921189592Sbms 922189592Sbms /* 923189592Sbms * Update the source filters first, as this may fail. 924189592Sbms * Maintain count of in-mode filters at t0, t1. These are 925189592Sbms * used to work out if we transition into ASM mode or not. 926189592Sbms * Maintain a count of source filters whose state was 927189592Sbms * actually modified by this operation. 928189592Sbms */ 929189592Sbms RB_FOREACH(ims, ip_msource_tree, &imf->imf_sources) { 930189592Sbms lims = (struct in_msource *)ims; 931189592Sbms if (lims->imsl_st[0] == imf->imf_st[0]) nsrc0++; 932189592Sbms if (lims->imsl_st[1] == imf->imf_st[1]) nsrc1++; 933189592Sbms if (lims->imsl_st[0] == lims->imsl_st[1]) continue; 934189592Sbms error = inm_get_source(inm, lims->ims_haddr, 0, &nims); 935189592Sbms ++schanged; 936189592Sbms if (error) 937170613Sbms break; 938189592Sbms ims_merge(nims, lims, 0); 939189592Sbms } 940189592Sbms if (error) { 941189592Sbms struct ip_msource *bims; 942189592Sbms 943189592Sbms RB_FOREACH_REVERSE_FROM(ims, ip_msource_tree, nims) { 944189592Sbms lims = (struct in_msource *)ims; 945189592Sbms if (lims->imsl_st[0] == lims->imsl_st[1]) 946189592Sbms continue; 947189592Sbms (void)inm_get_source(inm, lims->ims_haddr, 1, &bims); 948189592Sbms if (bims == NULL) 949189592Sbms continue; 950189592Sbms ims_merge(bims, lims, 1); 951170613Sbms } 952189592Sbms goto out_reap; 953189592Sbms } 954170613Sbms 955189592Sbms CTR3(KTR_IGMPV3, "%s: imf filters in-mode: %d at t0, %d at t1", 956189592Sbms __func__, nsrc0, nsrc1); 957170613Sbms 958189592Sbms /* Handle transition between INCLUDE {n} and INCLUDE {} on socket. */ 959189592Sbms if (imf->imf_st[0] == imf->imf_st[1] && 960189592Sbms imf->imf_st[1] == MCAST_INCLUDE) { 961189592Sbms if (nsrc1 == 0) { 962189592Sbms CTR1(KTR_IGMPV3, "%s: --in on inm at t1", __func__); 963189592Sbms --inm->inm_st[1].iss_in; 964189592Sbms } 965189592Sbms } 966170613Sbms 967189592Sbms /* Handle filter mode transition on socket. */ 968189592Sbms if (imf->imf_st[0] != imf->imf_st[1]) { 969189592Sbms CTR3(KTR_IGMPV3, "%s: imf transition %d to %d", 970189592Sbms __func__, imf->imf_st[0], imf->imf_st[1]); 971189592Sbms 972189592Sbms if (imf->imf_st[0] == MCAST_EXCLUDE) { 973189592Sbms CTR1(KTR_IGMPV3, "%s: --ex on inm at t1", __func__); 974189592Sbms --inm->inm_st[1].iss_ex; 975189592Sbms } else if (imf->imf_st[0] == MCAST_INCLUDE) { 976189592Sbms CTR1(KTR_IGMPV3, "%s: --in on inm at t1", __func__); 977189592Sbms --inm->inm_st[1].iss_in; 978189592Sbms } 979189592Sbms 980189592Sbms if (imf->imf_st[1] == MCAST_EXCLUDE) { 981189592Sbms CTR1(KTR_IGMPV3, "%s: ex++ on inm at t1", __func__); 982189592Sbms inm->inm_st[1].iss_ex++; 983189592Sbms } else if (imf->imf_st[1] == MCAST_INCLUDE && nsrc1 > 0) { 984189592Sbms CTR1(KTR_IGMPV3, "%s: in++ on inm at t1", __func__); 985189592Sbms inm->inm_st[1].iss_in++; 986189592Sbms } 987189592Sbms } 988189592Sbms 989189592Sbms /* 990189592Sbms * Track inm filter state in terms of listener counts. 991189592Sbms * If there are any exclusive listeners, stack-wide 992189592Sbms * membership is exclusive. 993189592Sbms * Otherwise, if only inclusive listeners, stack-wide is inclusive. 994189592Sbms * If no listeners remain, state is undefined at t1, 995189592Sbms * and the IGMP lifecycle for this group should finish. 996189592Sbms */ 997189592Sbms if (inm->inm_st[1].iss_ex > 0) { 998189592Sbms CTR1(KTR_IGMPV3, "%s: transition to EX", __func__); 999189592Sbms inm->inm_st[1].iss_fmode = MCAST_EXCLUDE; 1000189592Sbms } else if (inm->inm_st[1].iss_in > 0) { 1001189592Sbms CTR1(KTR_IGMPV3, "%s: transition to IN", __func__); 1002189592Sbms inm->inm_st[1].iss_fmode = MCAST_INCLUDE; 1003189592Sbms } else { 1004189592Sbms CTR1(KTR_IGMPV3, "%s: transition to UNDEF", __func__); 1005189592Sbms inm->inm_st[1].iss_fmode = MCAST_UNDEFINED; 1006189592Sbms } 1007189592Sbms 1008189592Sbms /* Decrement ASM listener count on transition out of ASM mode. */ 1009189592Sbms if (imf->imf_st[0] == MCAST_EXCLUDE && nsrc0 == 0) { 1010189592Sbms if ((imf->imf_st[1] != MCAST_EXCLUDE) || 1011189592Sbms (imf->imf_st[1] == MCAST_EXCLUDE && nsrc1 > 0)) 1012189592Sbms CTR1(KTR_IGMPV3, "%s: --asm on inm at t1", __func__); 1013189592Sbms --inm->inm_st[1].iss_asm; 1014189592Sbms } 1015189592Sbms 1016189592Sbms /* Increment ASM listener count on transition to ASM mode. */ 1017189592Sbms if (imf->imf_st[1] == MCAST_EXCLUDE && nsrc1 == 0) { 1018189592Sbms CTR1(KTR_IGMPV3, "%s: asm++ on inm at t1", __func__); 1019189592Sbms inm->inm_st[1].iss_asm++; 1020189592Sbms } 1021189592Sbms 1022189592Sbms CTR3(KTR_IGMPV3, "%s: merged imf %p to inm %p", __func__, imf, inm); 1023189592Sbms inm_print(inm); 1024189592Sbms 1025189592Sbmsout_reap: 1026189592Sbms if (schanged > 0) { 1027189592Sbms CTR1(KTR_IGMPV3, "%s: sources changed; reaping", __func__); 1028189592Sbms inm_reap(inm); 1029189592Sbms } 1030189592Sbms return (error); 1031189592Sbms} 1032189592Sbms 1033189592Sbms/* 1034189592Sbms * Mark an in_multi's filter set deltas as committed. 1035189592Sbms * Called by IGMP after a state change has been enqueued. 1036189592Sbms */ 1037189592Sbmsvoid 1038189592Sbmsinm_commit(struct in_multi *inm) 1039189592Sbms{ 1040189592Sbms struct ip_msource *ims; 1041189592Sbms 1042189592Sbms CTR2(KTR_IGMPV3, "%s: commit inm %p", __func__, inm); 1043189592Sbms CTR1(KTR_IGMPV3, "%s: pre commit:", __func__); 1044189592Sbms inm_print(inm); 1045189592Sbms 1046189592Sbms RB_FOREACH(ims, ip_msource_tree, &inm->inm_srcs) { 1047189592Sbms ims->ims_st[0] = ims->ims_st[1]; 1048189592Sbms } 1049189592Sbms inm->inm_st[0] = inm->inm_st[1]; 1050189592Sbms} 1051189592Sbms 1052189592Sbms/* 1053189592Sbms * Reap unreferenced nodes from an in_multi's filter set. 1054189592Sbms */ 1055189592Sbmsstatic void 1056189592Sbmsinm_reap(struct in_multi *inm) 1057189592Sbms{ 1058189592Sbms struct ip_msource *ims, *tims; 1059189592Sbms 1060189592Sbms RB_FOREACH_SAFE(ims, ip_msource_tree, &inm->inm_srcs, tims) { 1061189592Sbms if (ims->ims_st[0].ex > 0 || ims->ims_st[0].in > 0 || 1062189592Sbms ims->ims_st[1].ex > 0 || ims->ims_st[1].in > 0 || 1063189592Sbms ims->ims_stp != 0) 1064189592Sbms continue; 1065189592Sbms CTR2(KTR_IGMPV3, "%s: free ims %p", __func__, ims); 1066189592Sbms RB_REMOVE(ip_msource_tree, &inm->inm_srcs, ims); 1067189592Sbms free(ims, M_IPMSOURCE); 1068189592Sbms inm->inm_nsrc--; 1069189592Sbms } 1070189592Sbms} 1071189592Sbms 1072189592Sbms/* 1073189592Sbms * Purge all source nodes from an in_multi's filter set. 1074189592Sbms */ 1075189592Sbmsstatic void 1076189592Sbmsinm_purge(struct in_multi *inm) 1077189592Sbms{ 1078189592Sbms struct ip_msource *ims, *tims; 1079189592Sbms 1080189592Sbms RB_FOREACH_SAFE(ims, ip_msource_tree, &inm->inm_srcs, tims) { 1081189592Sbms CTR2(KTR_IGMPV3, "%s: free ims %p", __func__, ims); 1082189592Sbms RB_REMOVE(ip_msource_tree, &inm->inm_srcs, ims); 1083189592Sbms free(ims, M_IPMSOURCE); 1084189592Sbms inm->inm_nsrc--; 1085189592Sbms } 1086189592Sbms} 1087189592Sbms 1088189592Sbms/* 1089189592Sbms * Join a multicast group; unlocked entry point. 1090189592Sbms * 1091189592Sbms * SMPng: XXX: in_joingroup() is called from in_control() when Giant 1092189592Sbms * is not held. Fortunately, ifp is unlikely to have been detached 1093189592Sbms * at this point, so we assume it's OK to recurse. 1094189592Sbms */ 1095189592Sbmsint 1096189592Sbmsin_joingroup(struct ifnet *ifp, const struct in_addr *gina, 1097189592Sbms /*const*/ struct in_mfilter *imf, struct in_multi **pinm) 1098189592Sbms{ 1099189592Sbms int error; 1100189592Sbms 1101189592Sbms IN_MULTI_LOCK(); 1102189592Sbms error = in_joingroup_locked(ifp, gina, imf, pinm); 1103170613Sbms IN_MULTI_UNLOCK(); 1104170613Sbms 1105189592Sbms return (error); 1106170613Sbms} 1107170613Sbms 1108170613Sbms/* 1109189592Sbms * Join a multicast group; real entry point. 1110170613Sbms * 1111189592Sbms * Only preserves atomicity at inm level. 1112189592Sbms * NOTE: imf argument cannot be const due to sys/tree.h limitations. 1113170613Sbms * 1114189592Sbms * If the IGMP downcall fails, the group is not joined, and an error 1115189592Sbms * code is returned. 1116170613Sbms */ 1117189592Sbmsint 1118189592Sbmsin_joingroup_locked(struct ifnet *ifp, const struct in_addr *gina, 1119189592Sbms /*const*/ struct in_mfilter *imf, struct in_multi **pinm) 1120170613Sbms{ 1121189592Sbms struct in_mfilter timf; 1122189592Sbms struct in_multi *inm; 1123189592Sbms int error; 1124170613Sbms 1125189592Sbms IN_MULTI_LOCK_ASSERT(); 1126170613Sbms 1127189592Sbms CTR4(KTR_IGMPV3, "%s: join %s on %p(%s))", __func__, 1128189592Sbms inet_ntoa(*gina), ifp, ifp->if_xname); 1129189592Sbms 1130189592Sbms error = 0; 1131189592Sbms inm = NULL; 1132189592Sbms 1133189592Sbms /* 1134189592Sbms * If no imf was specified (i.e. kernel consumer), 1135189592Sbms * fake one up and assume it is an ASM join. 1136189592Sbms */ 1137189592Sbms if (imf == NULL) { 1138189592Sbms imf_init(&timf, MCAST_UNDEFINED, MCAST_EXCLUDE); 1139189592Sbms imf = &timf; 1140170613Sbms } 1141170613Sbms 1142189592Sbms error = in_getmulti(ifp, gina, &inm); 1143189592Sbms if (error) { 1144189592Sbms CTR1(KTR_IGMPV3, "%s: in_getmulti() failure", __func__); 1145189592Sbms return (error); 1146189592Sbms } 1147189592Sbms 1148189592Sbms CTR1(KTR_IGMPV3, "%s: merge inm state", __func__); 1149189592Sbms error = inm_merge(inm, imf); 1150189592Sbms if (error) { 1151189592Sbms CTR1(KTR_IGMPV3, "%s: failed to merge inm state", __func__); 1152189592Sbms goto out_inm_release; 1153189592Sbms } 1154189592Sbms 1155189592Sbms CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__); 1156189592Sbms error = igmp_change_state(inm); 1157189592Sbms if (error) { 1158189592Sbms CTR1(KTR_IGMPV3, "%s: failed to update source", __func__); 1159189592Sbms goto out_inm_release; 1160189592Sbms } 1161189592Sbms 1162189592Sbmsout_inm_release: 1163189592Sbms if (error) { 1164189592Sbms CTR2(KTR_IGMPV3, "%s: dropping ref on %p", __func__, inm); 1165189592Sbms inm_release_locked(inm); 1166189592Sbms } else { 1167189592Sbms *pinm = inm; 1168189592Sbms } 1169189592Sbms 1170189592Sbms return (error); 1171189592Sbms} 1172189592Sbms 1173189592Sbms/* 1174189592Sbms * Leave a multicast group; unlocked entry point. 1175189592Sbms */ 1176189592Sbmsint 1177189592Sbmsin_leavegroup(struct in_multi *inm, /*const*/ struct in_mfilter *imf) 1178189592Sbms{ 1179189592Sbms struct ifnet *ifp; 1180189851Srwatson int error; 1181189592Sbms 1182189592Sbms ifp = inm->inm_ifp; 1183189592Sbms 1184170613Sbms IN_MULTI_LOCK(); 1185189592Sbms error = in_leavegroup_locked(inm, imf); 1186170613Sbms IN_MULTI_UNLOCK(); 1187170613Sbms 1188189592Sbms return (error); 1189170613Sbms} 1190170613Sbms 1191170613Sbms/* 1192189592Sbms * Leave a multicast group; real entry point. 1193189592Sbms * All source filters will be expunged. 1194170613Sbms * 1195189592Sbms * Only preserves atomicity at inm level. 1196189592Sbms * 1197189592Sbms * Holding the write lock for the INP which contains imf 1198189592Sbms * is highly advisable. We can't assert for it as imf does not 1199189592Sbms * contain a back-pointer to the owning inp. 1200189592Sbms * 1201189592Sbms * Note: This is not the same as inm_release(*) as this function also 1202189592Sbms * makes a state change downcall into IGMP. 1203170613Sbms */ 1204189592Sbmsint 1205189592Sbmsin_leavegroup_locked(struct in_multi *inm, /*const*/ struct in_mfilter *imf) 1206170613Sbms{ 1207189592Sbms struct in_mfilter timf; 1208189592Sbms int error; 1209170613Sbms 1210189592Sbms error = 0; 1211189592Sbms 1212170613Sbms IN_MULTI_LOCK_ASSERT(); 1213170613Sbms 1214189592Sbms CTR5(KTR_IGMPV3, "%s: leave inm %p, %s/%s, imf %p", __func__, 1215189592Sbms inm, inet_ntoa(inm->inm_addr), 1216189592Sbms (inm_is_ifp_detached(inm) ? "null" : inm->inm_ifp->if_xname), 1217189592Sbms imf); 1218170613Sbms 1219189592Sbms /* 1220189592Sbms * If no imf was specified (i.e. kernel consumer), 1221189592Sbms * fake one up and assume it is an ASM join. 1222189592Sbms */ 1223189592Sbms if (imf == NULL) { 1224189592Sbms imf_init(&timf, MCAST_EXCLUDE, MCAST_UNDEFINED); 1225189592Sbms imf = &timf; 1226189592Sbms } 1227170613Sbms 1228189592Sbms /* 1229189592Sbms * Begin state merge transaction at IGMP layer. 1230189592Sbms * 1231189592Sbms * As this particular invocation should not cause any memory 1232189592Sbms * to be allocated, and there is no opportunity to roll back 1233189592Sbms * the transaction, it MUST NOT fail. 1234189592Sbms */ 1235189592Sbms CTR1(KTR_IGMPV3, "%s: merge inm state", __func__); 1236189592Sbms error = inm_merge(inm, imf); 1237189592Sbms KASSERT(error == 0, ("%s: failed to merge inm state", __func__)); 1238170613Sbms 1239189592Sbms CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__); 1240189592Sbms error = igmp_change_state(inm); 1241189592Sbms if (error) 1242189592Sbms CTR1(KTR_IGMPV3, "%s: failed igmp downcall", __func__); 1243189592Sbms 1244189592Sbms CTR2(KTR_IGMPV3, "%s: dropping ref on %p", __func__, inm); 1245189592Sbms inm_release_locked(inm); 1246189592Sbms 1247189592Sbms return (error); 1248170613Sbms} 1249170613Sbms 1250189592Sbms/*#ifndef BURN_BRIDGES*/ 1251170613Sbms/* 1252189592Sbms * Join an IPv4 multicast group in (*,G) exclusive mode. 1253189592Sbms * The group must be a 224.0.0.0/24 link-scope group. 1254189592Sbms * This KPI is for legacy kernel consumers only. 1255170613Sbms */ 1256189592Sbmsstruct in_multi * 1257189592Sbmsin_addmulti(struct in_addr *ap, struct ifnet *ifp) 1258189592Sbms{ 1259189592Sbms struct in_multi *pinm; 1260189592Sbms int error; 1261189592Sbms 1262189592Sbms KASSERT(IN_LOCAL_GROUP(ntohl(ap->s_addr)), 1263189592Sbms ("%s: %s not in 224.0.0.0/24", __func__, inet_ntoa(*ap))); 1264189592Sbms 1265189592Sbms error = in_joingroup(ifp, ap, NULL, &pinm); 1266189592Sbms if (error != 0) 1267189592Sbms pinm = NULL; 1268189592Sbms 1269189592Sbms return (pinm); 1270189592Sbms} 1271189592Sbms 1272189592Sbms/* 1273189592Sbms * Leave an IPv4 multicast group, assumed to be in exclusive (*,G) mode. 1274189592Sbms * This KPI is for legacy kernel consumers only. 1275189592Sbms */ 1276189592Sbmsvoid 1277189592Sbmsin_delmulti(struct in_multi *inm) 1278189592Sbms{ 1279189592Sbms 1280189592Sbms (void)in_leavegroup(inm, NULL); 1281189592Sbms} 1282189592Sbms/*#endif*/ 1283189592Sbms 1284189592Sbms/* 1285189592Sbms * Block or unblock an ASM multicast source on an inpcb. 1286189592Sbms * This implements the delta-based API described in RFC 3678. 1287189592Sbms * 1288189592Sbms * The delta-based API applies only to exclusive-mode memberships. 1289189592Sbms * An IGMP downcall will be performed. 1290189592Sbms * 1291189592Sbms * SMPng: NOTE: Must take Giant as a join may create a new ifma. 1292189592Sbms * 1293189592Sbms * Return 0 if successful, otherwise return an appropriate error code. 1294189592Sbms */ 1295170613Sbmsstatic int 1296189592Sbmsinp_block_unblock_source(struct inpcb *inp, struct sockopt *sopt) 1297170613Sbms{ 1298183550Szec INIT_VNET_NET(curvnet); 1299183550Szec INIT_VNET_INET(curvnet); 1300170613Sbms struct group_source_req gsr; 1301170613Sbms sockunion_t *gsa, *ssa; 1302170613Sbms struct ifnet *ifp; 1303170613Sbms struct in_mfilter *imf; 1304170613Sbms struct ip_moptions *imo; 1305170613Sbms struct in_msource *ims; 1306189592Sbms struct in_multi *inm; 1307170613Sbms size_t idx; 1308189592Sbms uint16_t fmode; 1309189592Sbms int error, doblock; 1310170613Sbms 1311170613Sbms ifp = NULL; 1312170613Sbms error = 0; 1313189592Sbms doblock = 0; 1314170613Sbms 1315170613Sbms memset(&gsr, 0, sizeof(struct group_source_req)); 1316170613Sbms gsa = (sockunion_t *)&gsr.gsr_group; 1317170613Sbms ssa = (sockunion_t *)&gsr.gsr_source; 1318170613Sbms 1319170613Sbms switch (sopt->sopt_name) { 1320170613Sbms case IP_BLOCK_SOURCE: 1321170613Sbms case IP_UNBLOCK_SOURCE: { 1322170613Sbms struct ip_mreq_source mreqs; 1323170613Sbms 1324170613Sbms error = sooptcopyin(sopt, &mreqs, 1325170613Sbms sizeof(struct ip_mreq_source), 1326170613Sbms sizeof(struct ip_mreq_source)); 1327170613Sbms if (error) 1328170613Sbms return (error); 1329170613Sbms 1330170613Sbms gsa->sin.sin_family = AF_INET; 1331170613Sbms gsa->sin.sin_len = sizeof(struct sockaddr_in); 1332170613Sbms gsa->sin.sin_addr = mreqs.imr_multiaddr; 1333170613Sbms 1334170613Sbms ssa->sin.sin_family = AF_INET; 1335170613Sbms ssa->sin.sin_len = sizeof(struct sockaddr_in); 1336170613Sbms ssa->sin.sin_addr = mreqs.imr_sourceaddr; 1337170613Sbms 1338189592Sbms if (!in_nullhost(mreqs.imr_interface)) 1339170613Sbms INADDR_TO_IFP(mreqs.imr_interface, ifp); 1340170613Sbms 1341170613Sbms if (sopt->sopt_name == IP_BLOCK_SOURCE) 1342189592Sbms doblock = 1; 1343170613Sbms 1344189592Sbms CTR3(KTR_IGMPV3, "%s: imr_interface = %s, ifp = %p", 1345189592Sbms __func__, inet_ntoa(mreqs.imr_interface), ifp); 1346170613Sbms break; 1347170613Sbms } 1348170613Sbms 1349170613Sbms case MCAST_BLOCK_SOURCE: 1350170613Sbms case MCAST_UNBLOCK_SOURCE: 1351170613Sbms error = sooptcopyin(sopt, &gsr, 1352170613Sbms sizeof(struct group_source_req), 1353170613Sbms sizeof(struct group_source_req)); 1354170613Sbms if (error) 1355170613Sbms return (error); 1356170613Sbms 1357170613Sbms if (gsa->sin.sin_family != AF_INET || 1358170613Sbms gsa->sin.sin_len != sizeof(struct sockaddr_in)) 1359170613Sbms return (EINVAL); 1360170613Sbms 1361170613Sbms if (ssa->sin.sin_family != AF_INET || 1362170613Sbms ssa->sin.sin_len != sizeof(struct sockaddr_in)) 1363170613Sbms return (EINVAL); 1364170613Sbms 1365181803Sbz if (gsr.gsr_interface == 0 || V_if_index < gsr.gsr_interface) 1366170613Sbms return (EADDRNOTAVAIL); 1367170613Sbms 1368170613Sbms ifp = ifnet_byindex(gsr.gsr_interface); 1369170613Sbms 1370170613Sbms if (sopt->sopt_name == MCAST_BLOCK_SOURCE) 1371189592Sbms doblock = 1; 1372170613Sbms break; 1373170613Sbms 1374170613Sbms default: 1375189592Sbms CTR2(KTR_IGMPV3, "%s: unknown sopt_name %d", 1376189592Sbms __func__, sopt->sopt_name); 1377170613Sbms return (EOPNOTSUPP); 1378170613Sbms break; 1379170613Sbms } 1380170613Sbms 1381170613Sbms if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr))) 1382170613Sbms return (EINVAL); 1383170613Sbms 1384170613Sbms /* 1385170613Sbms * Check if we are actually a member of this group. 1386170613Sbms */ 1387170613Sbms imo = inp_findmoptions(inp); 1388170613Sbms idx = imo_match_group(imo, ifp, &gsa->sa); 1389170613Sbms if (idx == -1 || imo->imo_mfilters == NULL) { 1390170613Sbms error = EADDRNOTAVAIL; 1391189592Sbms goto out_inp_locked; 1392170613Sbms } 1393170613Sbms 1394170613Sbms KASSERT(imo->imo_mfilters != NULL, 1395170613Sbms ("%s: imo_mfilters not allocated", __func__)); 1396170613Sbms imf = &imo->imo_mfilters[idx]; 1397189592Sbms inm = imo->imo_membership[idx]; 1398170613Sbms 1399170613Sbms /* 1400189592Sbms * Attempting to use the delta-based API on an 1401189592Sbms * non exclusive-mode membership is an error. 1402170613Sbms */ 1403189592Sbms fmode = imf->imf_st[0]; 1404189592Sbms if (fmode != MCAST_EXCLUDE) { 1405189592Sbms error = EINVAL; 1406189592Sbms goto out_inp_locked; 1407170613Sbms } 1408189592Sbms 1409189592Sbms /* 1410189592Sbms * Deal with error cases up-front: 1411189592Sbms * Asked to block, but already blocked; or 1412189592Sbms * Asked to unblock, but nothing to unblock. 1413189592Sbms * If adding a new block entry, allocate it. 1414189592Sbms */ 1415170613Sbms ims = imo_match_source(imo, idx, &ssa->sa); 1416189592Sbms if ((ims != NULL && doblock) || (ims == NULL && !doblock)) { 1417189592Sbms CTR3(KTR_IGMPV3, "%s: source %s %spresent", __func__, 1418189592Sbms inet_ntoa(ssa->sin.sin_addr), doblock ? "" : "not "); 1419189592Sbms error = EADDRNOTAVAIL; 1420189592Sbms goto out_inp_locked; 1421189592Sbms } 1422189592Sbms 1423189592Sbms INP_WLOCK_ASSERT(inp); 1424189592Sbms 1425189592Sbms /* 1426189592Sbms * Begin state merge transaction at socket layer. 1427189592Sbms */ 1428189592Sbms if (doblock) { 1429189592Sbms CTR2(KTR_IGMPV3, "%s: %s source", __func__, "block"); 1430189592Sbms ims = imf_graft(imf, fmode, &ssa->sin); 1431189592Sbms if (ims == NULL) 1432189592Sbms error = ENOMEM; 1433170613Sbms } else { 1434189592Sbms CTR2(KTR_IGMPV3, "%s: %s source", __func__, "allow"); 1435189592Sbms error = imf_prune(imf, &ssa->sin); 1436170613Sbms } 1437170613Sbms 1438189592Sbms if (error) { 1439189592Sbms CTR1(KTR_IGMPV3, "%s: merge imf state failed", __func__); 1440189592Sbms goto out_imf_rollback; 1441189592Sbms } 1442189592Sbms 1443189592Sbms /* 1444189592Sbms * Begin state merge transaction at IGMP layer. 1445189592Sbms */ 1446189592Sbms IN_MULTI_LOCK(); 1447189592Sbms 1448189592Sbms CTR1(KTR_IGMPV3, "%s: merge inm state", __func__); 1449189592Sbms error = inm_merge(inm, imf); 1450189592Sbms if (error) { 1451189592Sbms CTR1(KTR_IGMPV3, "%s: failed to merge inm state", __func__); 1452189592Sbms goto out_imf_rollback; 1453189592Sbms } 1454189592Sbms 1455189592Sbms CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__); 1456189592Sbms error = igmp_change_state(inm); 1457189592Sbms if (error) 1458189592Sbms CTR1(KTR_IGMPV3, "%s: failed igmp downcall", __func__); 1459189592Sbms 1460189592Sbms IN_MULTI_UNLOCK(); 1461189592Sbms 1462189592Sbmsout_imf_rollback: 1463189592Sbms if (error) 1464189592Sbms imf_rollback(imf); 1465189592Sbms else 1466189592Sbms imf_commit(imf); 1467189592Sbms 1468189592Sbms imf_reap(imf); 1469189592Sbms 1470189592Sbmsout_inp_locked: 1471178285Srwatson INP_WUNLOCK(inp); 1472170613Sbms return (error); 1473170613Sbms} 1474170613Sbms 1475170613Sbms/* 1476170613Sbms * Given an inpcb, return its multicast options structure pointer. Accepts 1477170613Sbms * an unlocked inpcb pointer, but will return it locked. May sleep. 1478189592Sbms * 1479189592Sbms * SMPng: NOTE: Potentially calls malloc(M_WAITOK) with Giant held. 1480189592Sbms * SMPng: NOTE: Returns with the INP write lock held. 1481170613Sbms */ 1482170613Sbmsstatic struct ip_moptions * 1483170613Sbmsinp_findmoptions(struct inpcb *inp) 1484170613Sbms{ 1485170613Sbms struct ip_moptions *imo; 1486170613Sbms struct in_multi **immp; 1487170613Sbms struct in_mfilter *imfp; 1488170613Sbms size_t idx; 1489170613Sbms 1490178285Srwatson INP_WLOCK(inp); 1491170613Sbms if (inp->inp_moptions != NULL) 1492170613Sbms return (inp->inp_moptions); 1493170613Sbms 1494178285Srwatson INP_WUNLOCK(inp); 1495170613Sbms 1496189592Sbms imo = malloc(sizeof(*imo), M_IPMOPTS, M_WAITOK); 1497189592Sbms immp = malloc(sizeof(*immp) * IP_MIN_MEMBERSHIPS, M_IPMOPTS, 1498189592Sbms M_WAITOK | M_ZERO); 1499189592Sbms imfp = malloc(sizeof(struct in_mfilter) * IP_MIN_MEMBERSHIPS, 1500189592Sbms M_INMFILTER, M_WAITOK); 1501170613Sbms 1502170613Sbms imo->imo_multicast_ifp = NULL; 1503170613Sbms imo->imo_multicast_addr.s_addr = INADDR_ANY; 1504170613Sbms imo->imo_multicast_vif = -1; 1505170613Sbms imo->imo_multicast_ttl = IP_DEFAULT_MULTICAST_TTL; 1506189357Sbms imo->imo_multicast_loop = in_mcast_loop; 1507170613Sbms imo->imo_num_memberships = 0; 1508170613Sbms imo->imo_max_memberships = IP_MIN_MEMBERSHIPS; 1509170613Sbms imo->imo_membership = immp; 1510170613Sbms 1511170613Sbms /* Initialize per-group source filters. */ 1512189592Sbms for (idx = 0; idx < IP_MIN_MEMBERSHIPS; idx++) 1513189592Sbms imf_init(&imfp[idx], MCAST_UNDEFINED, MCAST_EXCLUDE); 1514170613Sbms imo->imo_mfilters = imfp; 1515170613Sbms 1516178285Srwatson INP_WLOCK(inp); 1517170613Sbms if (inp->inp_moptions != NULL) { 1518189592Sbms free(imfp, M_INMFILTER); 1519170613Sbms free(immp, M_IPMOPTS); 1520170613Sbms free(imo, M_IPMOPTS); 1521170613Sbms return (inp->inp_moptions); 1522170613Sbms } 1523170613Sbms inp->inp_moptions = imo; 1524170613Sbms return (imo); 1525170613Sbms} 1526170613Sbms 1527170613Sbms/* 1528170613Sbms * Discard the IP multicast options (and source filters). 1529189592Sbms * 1530189592Sbms * SMPng: NOTE: assumes INP write lock is held. 1531170613Sbms */ 1532170613Sbmsvoid 1533170613Sbmsinp_freemoptions(struct ip_moptions *imo) 1534170613Sbms{ 1535170613Sbms struct in_mfilter *imf; 1536170613Sbms size_t idx, nmships; 1537170613Sbms 1538170613Sbms KASSERT(imo != NULL, ("%s: ip_moptions is NULL", __func__)); 1539170613Sbms 1540170613Sbms nmships = imo->imo_num_memberships; 1541170613Sbms for (idx = 0; idx < nmships; ++idx) { 1542189592Sbms imf = imo->imo_mfilters ? &imo->imo_mfilters[idx] : NULL; 1543189592Sbms if (imf) 1544189592Sbms imf_leave(imf); 1545189592Sbms (void)in_leavegroup(imo->imo_membership[idx], imf); 1546189592Sbms if (imf) 1547189592Sbms imf_purge(imf); 1548170613Sbms } 1549170613Sbms 1550189592Sbms if (imo->imo_mfilters) 1551189592Sbms free(imo->imo_mfilters, M_INMFILTER); 1552170613Sbms free(imo->imo_membership, M_IPMOPTS); 1553170613Sbms free(imo, M_IPMOPTS); 1554170613Sbms} 1555170613Sbms 1556170613Sbms/* 1557170613Sbms * Atomically get source filters on a socket for an IPv4 multicast group. 1558170613Sbms * Called with INP lock held; returns with lock released. 1559170613Sbms */ 1560170613Sbmsstatic int 1561170613Sbmsinp_get_source_filters(struct inpcb *inp, struct sockopt *sopt) 1562170613Sbms{ 1563183550Szec INIT_VNET_NET(curvnet); 1564170613Sbms struct __msfilterreq msfr; 1565170613Sbms sockunion_t *gsa; 1566170613Sbms struct ifnet *ifp; 1567170613Sbms struct ip_moptions *imo; 1568170613Sbms struct in_mfilter *imf; 1569189592Sbms struct ip_msource *ims; 1570189592Sbms struct in_msource *lims; 1571189592Sbms struct sockaddr_in *psin; 1572170613Sbms struct sockaddr_storage *ptss; 1573170613Sbms struct sockaddr_storage *tss; 1574170613Sbms int error; 1575189592Sbms size_t idx, nsrcs, ncsrcs; 1576170613Sbms 1577178285Srwatson INP_WLOCK_ASSERT(inp); 1578170613Sbms 1579170613Sbms imo = inp->inp_moptions; 1580170613Sbms KASSERT(imo != NULL, ("%s: null ip_moptions", __func__)); 1581170613Sbms 1582178285Srwatson INP_WUNLOCK(inp); 1583170613Sbms 1584170613Sbms error = sooptcopyin(sopt, &msfr, sizeof(struct __msfilterreq), 1585170613Sbms sizeof(struct __msfilterreq)); 1586170613Sbms if (error) 1587170613Sbms return (error); 1588170613Sbms 1589181803Sbz if (msfr.msfr_ifindex == 0 || V_if_index < msfr.msfr_ifindex) 1590170613Sbms return (EINVAL); 1591170613Sbms 1592170613Sbms ifp = ifnet_byindex(msfr.msfr_ifindex); 1593170613Sbms if (ifp == NULL) 1594170613Sbms return (EINVAL); 1595170613Sbms 1596178285Srwatson INP_WLOCK(inp); 1597170613Sbms 1598170613Sbms /* 1599170613Sbms * Lookup group on the socket. 1600170613Sbms */ 1601170613Sbms gsa = (sockunion_t *)&msfr.msfr_group; 1602170613Sbms idx = imo_match_group(imo, ifp, &gsa->sa); 1603170613Sbms if (idx == -1 || imo->imo_mfilters == NULL) { 1604178285Srwatson INP_WUNLOCK(inp); 1605170613Sbms return (EADDRNOTAVAIL); 1606170613Sbms } 1607170613Sbms imf = &imo->imo_mfilters[idx]; 1608170613Sbms 1609170613Sbms /* 1610189592Sbms * Ignore memberships which are in limbo. 1611189592Sbms */ 1612189592Sbms if (imf->imf_st[1] == MCAST_UNDEFINED) { 1613189592Sbms INP_WUNLOCK(inp); 1614189592Sbms return (EAGAIN); 1615189592Sbms } 1616189592Sbms msfr.msfr_fmode = imf->imf_st[1]; 1617189592Sbms 1618189592Sbms /* 1619170613Sbms * If the user specified a buffer, copy out the source filter 1620170613Sbms * entries to userland gracefully. 1621189592Sbms * We only copy out the number of entries which userland 1622189592Sbms * has asked for, but we always tell userland how big the 1623189592Sbms * buffer really needs to be. 1624170613Sbms */ 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{ 1681183550Szec INIT_VNET_INET(curvnet); 1682170613Sbms struct ip_mreqn mreqn; 1683170613Sbms struct ip_moptions *imo; 1684170613Sbms struct ifnet *ifp; 1685170613Sbms struct in_ifaddr *ia; 1686170613Sbms int error, optval; 1687170613Sbms u_char coptval; 1688170613Sbms 1689178285Srwatson INP_WLOCK(inp); 1690170613Sbms imo = inp->inp_moptions; 1691171746Scsjp /* 1692171746Scsjp * If socket is neither of type SOCK_RAW or SOCK_DGRAM, 1693171746Scsjp * or is a divert socket, reject it. 1694171746Scsjp */ 1695171746Scsjp if (inp->inp_socket->so_proto->pr_protocol == IPPROTO_DIVERT || 1696171746Scsjp (inp->inp_socket->so_proto->pr_type != SOCK_RAW && 1697171746Scsjp inp->inp_socket->so_proto->pr_type != SOCK_DGRAM)) { 1698178285Srwatson INP_WUNLOCK(inp); 1699171746Scsjp return (EOPNOTSUPP); 1700171746Scsjp } 1701170613Sbms 1702170613Sbms error = 0; 1703170613Sbms switch (sopt->sopt_name) { 1704170613Sbms case IP_MULTICAST_VIF: 1705170613Sbms if (imo != NULL) 1706170613Sbms optval = imo->imo_multicast_vif; 1707170613Sbms else 1708170613Sbms optval = -1; 1709178285Srwatson INP_WUNLOCK(inp); 1710170613Sbms error = sooptcopyout(sopt, &optval, sizeof(int)); 1711170613Sbms break; 1712170613Sbms 1713170613Sbms case IP_MULTICAST_IF: 1714170613Sbms memset(&mreqn, 0, sizeof(struct ip_mreqn)); 1715170613Sbms if (imo != NULL) { 1716170613Sbms ifp = imo->imo_multicast_ifp; 1717189592Sbms if (!in_nullhost(imo->imo_multicast_addr)) { 1718170613Sbms mreqn.imr_address = imo->imo_multicast_addr; 1719170613Sbms } else if (ifp != NULL) { 1720170613Sbms mreqn.imr_ifindex = ifp->if_index; 1721170613Sbms IFP_TO_IA(ifp, ia); 1722170613Sbms if (ia != NULL) { 1723170613Sbms mreqn.imr_address = 1724170613Sbms IA_SIN(ia)->sin_addr; 1725194760Srwatson ifa_free(&ia->ia_ifa); 1726170613Sbms } 1727170613Sbms } 1728170613Sbms } 1729178285Srwatson INP_WUNLOCK(inp); 1730170613Sbms if (sopt->sopt_valsize == sizeof(struct ip_mreqn)) { 1731170613Sbms error = sooptcopyout(sopt, &mreqn, 1732170613Sbms sizeof(struct ip_mreqn)); 1733170613Sbms } else { 1734170613Sbms error = sooptcopyout(sopt, &mreqn.imr_address, 1735170613Sbms sizeof(struct in_addr)); 1736170613Sbms } 1737170613Sbms break; 1738170613Sbms 1739170613Sbms case IP_MULTICAST_TTL: 1740170613Sbms if (imo == 0) 1741170613Sbms optval = coptval = IP_DEFAULT_MULTICAST_TTL; 1742170613Sbms else 1743170613Sbms optval = coptval = imo->imo_multicast_ttl; 1744178285Srwatson INP_WUNLOCK(inp); 1745170613Sbms if (sopt->sopt_valsize == sizeof(u_char)) 1746170613Sbms error = sooptcopyout(sopt, &coptval, sizeof(u_char)); 1747170613Sbms else 1748170613Sbms error = sooptcopyout(sopt, &optval, sizeof(int)); 1749170613Sbms break; 1750170613Sbms 1751170613Sbms case IP_MULTICAST_LOOP: 1752170613Sbms if (imo == 0) 1753170613Sbms optval = coptval = IP_DEFAULT_MULTICAST_LOOP; 1754170613Sbms else 1755170613Sbms optval = coptval = imo->imo_multicast_loop; 1756178285Srwatson INP_WUNLOCK(inp); 1757170613Sbms if (sopt->sopt_valsize == sizeof(u_char)) 1758170613Sbms error = sooptcopyout(sopt, &coptval, sizeof(u_char)); 1759170613Sbms else 1760170613Sbms error = sooptcopyout(sopt, &optval, sizeof(int)); 1761170613Sbms break; 1762170613Sbms 1763170613Sbms case IP_MSFILTER: 1764170613Sbms if (imo == NULL) { 1765170613Sbms error = EADDRNOTAVAIL; 1766178285Srwatson INP_WUNLOCK(inp); 1767170613Sbms } else { 1768170613Sbms error = inp_get_source_filters(inp, sopt); 1769170613Sbms } 1770170613Sbms break; 1771170613Sbms 1772170613Sbms default: 1773178285Srwatson INP_WUNLOCK(inp); 1774170613Sbms error = ENOPROTOOPT; 1775170613Sbms break; 1776170613Sbms } 1777170613Sbms 1778170613Sbms INP_UNLOCK_ASSERT(inp); 1779170613Sbms 1780170613Sbms return (error); 1781170613Sbms} 1782170613Sbms 1783170613Sbms/* 1784189592Sbms * Look up the ifnet to use for a multicast group membership, 1785189592Sbms * given the IPv4 address of an interface, and the IPv4 group address. 1786189592Sbms * 1787189592Sbms * This routine exists to support legacy multicast applications 1788189592Sbms * which do not understand that multicast memberships are scoped to 1789189592Sbms * specific physical links in the networking stack, or which need 1790189592Sbms * to join link-scope groups before IPv4 addresses are configured. 1791189592Sbms * 1792189592Sbms * If inp is non-NULL, use this socket's current FIB number for any 1793189592Sbms * required FIB lookup. 1794189592Sbms * If ina is INADDR_ANY, look up the group address in the unicast FIB, 1795189592Sbms * and use its ifp; usually, this points to the default next-hop. 1796189592Sbms * 1797189592Sbms * If the FIB lookup fails, attempt to use the first non-loopback 1798189592Sbms * interface with multicast capability in the system as a 1799189592Sbms * last resort. The legacy IPv4 ASM API requires that we do 1800189592Sbms * this in order to allow groups to be joined when the routing 1801189592Sbms * table has not yet been populated during boot. 1802189592Sbms * 1803189592Sbms * Returns NULL if no ifp could be found. 1804189592Sbms * 1805189592Sbms * SMPng: TODO: Acquire the appropriate locks for INADDR_TO_IFP. 1806189592Sbms * FUTURE: Implement IPv4 source-address selection. 1807189592Sbms */ 1808189592Sbmsstatic struct ifnet * 1809189592Sbmsinp_lookup_mcast_ifp(const struct inpcb *inp, 1810189592Sbms const struct sockaddr_in *gsin, const struct in_addr ina) 1811189592Sbms{ 1812191548Szec INIT_VNET_INET(curvnet); 1813189592Sbms struct ifnet *ifp; 1814189592Sbms 1815189592Sbms KASSERT(gsin->sin_family == AF_INET, ("%s: not AF_INET", __func__)); 1816189592Sbms KASSERT(IN_MULTICAST(ntohl(gsin->sin_addr.s_addr)), 1817189592Sbms ("%s: not multicast", __func__)); 1818189592Sbms 1819189592Sbms ifp = NULL; 1820189592Sbms if (!in_nullhost(ina)) { 1821189592Sbms INADDR_TO_IFP(ina, ifp); 1822189592Sbms } else { 1823189592Sbms struct route ro; 1824189592Sbms 1825189592Sbms ro.ro_rt = NULL; 1826189592Sbms memcpy(&ro.ro_dst, gsin, sizeof(struct sockaddr_in)); 1827189592Sbms in_rtalloc_ign(&ro, 0, inp ? inp->inp_inc.inc_fibnum : 0); 1828189592Sbms if (ro.ro_rt != NULL) { 1829189592Sbms ifp = ro.ro_rt->rt_ifp; 1830189592Sbms KASSERT(ifp != NULL, ("%s: null ifp", __func__)); 1831189592Sbms RTFREE(ro.ro_rt); 1832189592Sbms } else { 1833189592Sbms struct in_ifaddr *ia; 1834189592Sbms struct ifnet *mifp; 1835189592Sbms 1836189592Sbms mifp = NULL; 1837189592Sbms TAILQ_FOREACH(ia, &V_in_ifaddrhead, ia_link) { 1838189592Sbms mifp = ia->ia_ifp; 1839189592Sbms if (!(mifp->if_flags & IFF_LOOPBACK) && 1840189592Sbms (mifp->if_flags & IFF_MULTICAST)) { 1841189592Sbms ifp = mifp; 1842189592Sbms break; 1843189592Sbms } 1844189592Sbms } 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{ 1857183550Szec INIT_VNET_NET(curvnet); 1858170613Sbms struct group_source_req gsr; 1859170613Sbms sockunion_t *gsa, *ssa; 1860170613Sbms struct ifnet *ifp; 1861170613Sbms struct in_mfilter *imf; 1862170613Sbms struct ip_moptions *imo; 1863170613Sbms struct in_multi *inm; 1864189592Sbms struct in_msource *lims; 1865170613Sbms size_t idx; 1866189592Sbms int error, is_new; 1867170613Sbms 1868170613Sbms ifp = NULL; 1869189592Sbms imf = 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 1912189592Sbms ifp = inp_lookup_mcast_ifp(inp, &gsa->sin, 1913189592Sbms mreqs.imr_interface); 1914189592Sbms CTR3(KTR_IGMPV3, "%s: imr_interface = %s, ifp = %p", 1915189592Sbms __func__, inet_ntoa(mreqs.imr_interface), ifp); 1916170613Sbms break; 1917170613Sbms } 1918170613Sbms 1919170613Sbms case MCAST_JOIN_GROUP: 1920170613Sbms case MCAST_JOIN_SOURCE_GROUP: 1921170613Sbms if (sopt->sopt_name == MCAST_JOIN_GROUP) { 1922170613Sbms error = sooptcopyin(sopt, &gsr, 1923170613Sbms sizeof(struct group_req), 1924170613Sbms sizeof(struct group_req)); 1925170613Sbms } else if (sopt->sopt_name == MCAST_JOIN_SOURCE_GROUP) { 1926170613Sbms error = sooptcopyin(sopt, &gsr, 1927170613Sbms sizeof(struct group_source_req), 1928170613Sbms sizeof(struct group_source_req)); 1929170613Sbms } 1930170613Sbms if (error) 1931170613Sbms return (error); 1932170613Sbms 1933170613Sbms if (gsa->sin.sin_family != AF_INET || 1934170613Sbms gsa->sin.sin_len != sizeof(struct sockaddr_in)) 1935170613Sbms return (EINVAL); 1936170613Sbms 1937170613Sbms /* 1938170613Sbms * Overwrite the port field if present, as the sockaddr 1939170613Sbms * being copied in may be matched with a binary comparison. 1940170613Sbms */ 1941170613Sbms gsa->sin.sin_port = 0; 1942170613Sbms if (sopt->sopt_name == MCAST_JOIN_SOURCE_GROUP) { 1943170613Sbms if (ssa->sin.sin_family != AF_INET || 1944170613Sbms ssa->sin.sin_len != sizeof(struct sockaddr_in)) 1945170613Sbms return (EINVAL); 1946170613Sbms ssa->sin.sin_port = 0; 1947170613Sbms } 1948170613Sbms 1949181803Sbz if (gsr.gsr_interface == 0 || V_if_index < gsr.gsr_interface) 1950170613Sbms return (EADDRNOTAVAIL); 1951170613Sbms ifp = ifnet_byindex(gsr.gsr_interface); 1952170613Sbms break; 1953170613Sbms 1954170613Sbms default: 1955189592Sbms CTR2(KTR_IGMPV3, "%s: unknown sopt_name %d", 1956189592Sbms __func__, sopt->sopt_name); 1957170613Sbms return (EOPNOTSUPP); 1958170613Sbms break; 1959170613Sbms } 1960170613Sbms 1961170613Sbms if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr))) 1962170613Sbms return (EINVAL); 1963170613Sbms 1964170613Sbms if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) 1965170613Sbms return (EADDRNOTAVAIL); 1966170613Sbms 1967170613Sbms /* 1968189592Sbms * MCAST_JOIN_SOURCE on an exclusive membership is an error. 1969189592Sbms * On an existing inclusive membership, it just adds the 1970189592Sbms * source to the filter list. 1971170613Sbms */ 1972170613Sbms imo = inp_findmoptions(inp); 1973170613Sbms idx = imo_match_group(imo, ifp, &gsa->sa); 1974189592Sbms if (idx == -1) { 1975189592Sbms is_new = 1; 1976189592Sbms } else { 1977189592Sbms inm = imo->imo_membership[idx]; 1978189592Sbms imf = &imo->imo_mfilters[idx]; 1979189592Sbms if (ssa->ss.ss_family != AF_UNSPEC && 1980189592Sbms imf->imf_st[1] != MCAST_INCLUDE) { 1981189592Sbms error = EINVAL; 1982189592Sbms goto out_inp_locked; 1983189592Sbms } 1984189592Sbms lims = imo_match_source(imo, idx, &ssa->sa); 1985189592Sbms if (lims != NULL) { 1986170613Sbms error = EADDRNOTAVAIL; 1987189592Sbms goto out_inp_locked; 1988170613Sbms } 1989170613Sbms } 1990170613Sbms 1991170613Sbms /* 1992189592Sbms * Begin state merge transaction at socket layer. 1993170613Sbms */ 1994189592Sbms INP_WLOCK_ASSERT(inp); 1995189592Sbms 1996189592Sbms if (is_new) { 1997189592Sbms if (imo->imo_num_memberships == imo->imo_max_memberships) { 1998189592Sbms error = imo_grow(imo); 1999189592Sbms if (error) 2000189592Sbms goto out_inp_locked; 2001189592Sbms } 2002189592Sbms /* 2003189592Sbms * Allocate the new slot upfront so we can deal with 2004189592Sbms * grafting the new source filter in same code path 2005189592Sbms * as for join-source on existing membership. 2006189592Sbms */ 2007189592Sbms idx = imo->imo_num_memberships; 2008189592Sbms imo->imo_membership[idx] = NULL; 2009189592Sbms imo->imo_num_memberships++; 2010189592Sbms KASSERT(imo->imo_mfilters != NULL, 2011189592Sbms ("%s: imf_mfilters vector was not allocated", __func__)); 2012189592Sbms imf = &imo->imo_mfilters[idx]; 2013189592Sbms KASSERT(RB_EMPTY(&imf->imf_sources), 2014189592Sbms ("%s: imf_sources not empty", __func__)); 2015170613Sbms } 2016170613Sbms 2017170613Sbms /* 2018189592Sbms * Graft new source into filter list for this inpcb's 2019189592Sbms * membership of the group. The in_multi may not have 2020189592Sbms * been allocated yet if this is a new membership. 2021170613Sbms */ 2022189592Sbms if (ssa->ss.ss_family != AF_UNSPEC) { 2023189592Sbms /* Membership starts in IN mode */ 2024189592Sbms if (is_new) { 2025189592Sbms CTR1(KTR_IGMPV3, "%s: new join w/source", __func__); 2026189592Sbms imf_init(imf, MCAST_UNDEFINED, MCAST_INCLUDE); 2027189592Sbms } else { 2028189592Sbms CTR2(KTR_IGMPV3, "%s: %s source", __func__, "allow"); 2029189592Sbms } 2030189592Sbms lims = imf_graft(imf, MCAST_INCLUDE, &ssa->sin); 2031189592Sbms if (lims == NULL) { 2032189592Sbms CTR1(KTR_IGMPV3, "%s: merge imf state failed", 2033189592Sbms __func__); 2034189592Sbms error = ENOMEM; 2035189592Sbms goto out_imo_free; 2036189592Sbms } 2037170613Sbms } 2038170613Sbms 2039170613Sbms /* 2040189592Sbms * Begin state merge transaction at IGMP layer. 2041170613Sbms */ 2042189592Sbms IN_MULTI_LOCK(); 2043170613Sbms 2044189592Sbms if (is_new) { 2045189592Sbms error = in_joingroup_locked(ifp, &gsa->sin.sin_addr, imf, 2046189592Sbms &inm); 2047189592Sbms if (error) 2048189592Sbms goto out_imo_free; 2049189592Sbms imo->imo_membership[idx] = inm; 2050189592Sbms } else { 2051189592Sbms CTR1(KTR_IGMPV3, "%s: merge inm state", __func__); 2052189592Sbms error = inm_merge(inm, imf); 2053170613Sbms if (error) { 2054189592Sbms CTR1(KTR_IGMPV3, "%s: failed to merge inm state", 2055189592Sbms __func__); 2056189592Sbms goto out_imf_rollback; 2057170613Sbms } 2058189592Sbms CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__); 2059189592Sbms error = igmp_change_state(inm); 2060189592Sbms if (error) { 2061189592Sbms CTR1(KTR_IGMPV3, "%s: failed igmp downcall", 2062189592Sbms __func__); 2063189592Sbms goto out_imf_rollback; 2064189592Sbms } 2065170613Sbms } 2066170613Sbms 2067189592Sbms IN_MULTI_UNLOCK(); 2068189592Sbms 2069189592Sbmsout_imf_rollback: 2070189592Sbms INP_WLOCK_ASSERT(inp); 2071189592Sbms if (error) { 2072189592Sbms imf_rollback(imf); 2073189592Sbms if (is_new) 2074189592Sbms imf_purge(imf); 2075189592Sbms else 2076189592Sbms imf_reap(imf); 2077189592Sbms } else { 2078189592Sbms imf_commit(imf); 2079189592Sbms } 2080189592Sbms 2081189592Sbmsout_imo_free: 2082189592Sbms if (error && is_new) { 2083189592Sbms imo->imo_membership[idx] = NULL; 2084189592Sbms --imo->imo_num_memberships; 2085189592Sbms } 2086189592Sbms 2087189592Sbmsout_inp_locked: 2088178285Srwatson INP_WUNLOCK(inp); 2089170613Sbms return (error); 2090170613Sbms} 2091170613Sbms 2092170613Sbms/* 2093170613Sbms * Leave an IPv4 multicast group on an inpcb, possibly with a source. 2094170613Sbms */ 2095170613Sbmsstatic int 2096170613Sbmsinp_leave_group(struct inpcb *inp, struct sockopt *sopt) 2097170613Sbms{ 2098183550Szec INIT_VNET_NET(curvnet); 2099183550Szec INIT_VNET_INET(curvnet); 2100170613Sbms struct group_source_req gsr; 2101170613Sbms struct ip_mreq_source mreqs; 2102170613Sbms sockunion_t *gsa, *ssa; 2103170613Sbms struct ifnet *ifp; 2104170613Sbms struct in_mfilter *imf; 2105170613Sbms struct ip_moptions *imo; 2106189592Sbms struct in_msource *ims; 2107170613Sbms struct in_multi *inm; 2108170613Sbms size_t idx; 2109189592Sbms int error, is_final; 2110170613Sbms 2111170613Sbms ifp = NULL; 2112170613Sbms error = 0; 2113189592Sbms is_final = 1; 2114170613Sbms 2115170613Sbms memset(&gsr, 0, sizeof(struct group_source_req)); 2116170613Sbms gsa = (sockunion_t *)&gsr.gsr_group; 2117170613Sbms gsa->ss.ss_family = AF_UNSPEC; 2118170613Sbms ssa = (sockunion_t *)&gsr.gsr_source; 2119170613Sbms ssa->ss.ss_family = AF_UNSPEC; 2120170613Sbms 2121170613Sbms switch (sopt->sopt_name) { 2122170613Sbms case IP_DROP_MEMBERSHIP: 2123170613Sbms case IP_DROP_SOURCE_MEMBERSHIP: 2124170613Sbms if (sopt->sopt_name == IP_DROP_MEMBERSHIP) { 2125170613Sbms error = sooptcopyin(sopt, &mreqs, 2126170613Sbms sizeof(struct ip_mreq), 2127170613Sbms sizeof(struct ip_mreq)); 2128170613Sbms /* 2129170613Sbms * Swap interface and sourceaddr arguments, 2130170613Sbms * as ip_mreq and ip_mreq_source are laid 2131170613Sbms * out differently. 2132170613Sbms */ 2133170613Sbms mreqs.imr_interface = mreqs.imr_sourceaddr; 2134170613Sbms mreqs.imr_sourceaddr.s_addr = INADDR_ANY; 2135170613Sbms } else if (sopt->sopt_name == IP_DROP_SOURCE_MEMBERSHIP) { 2136170613Sbms error = sooptcopyin(sopt, &mreqs, 2137170613Sbms sizeof(struct ip_mreq_source), 2138170613Sbms sizeof(struct ip_mreq_source)); 2139170613Sbms } 2140170613Sbms if (error) 2141170613Sbms return (error); 2142170613Sbms 2143170613Sbms gsa->sin.sin_family = AF_INET; 2144170613Sbms gsa->sin.sin_len = sizeof(struct sockaddr_in); 2145170613Sbms gsa->sin.sin_addr = mreqs.imr_multiaddr; 2146170613Sbms 2147170613Sbms if (sopt->sopt_name == IP_DROP_SOURCE_MEMBERSHIP) { 2148170613Sbms ssa->sin.sin_family = AF_INET; 2149170613Sbms ssa->sin.sin_len = sizeof(struct sockaddr_in); 2150170613Sbms ssa->sin.sin_addr = mreqs.imr_sourceaddr; 2151170613Sbms } 2152170613Sbms 2153189592Sbms if (!in_nullhost(gsa->sin.sin_addr)) 2154170613Sbms INADDR_TO_IFP(mreqs.imr_interface, ifp); 2155170613Sbms 2156189592Sbms CTR3(KTR_IGMPV3, "%s: imr_interface = %s, ifp = %p", 2157189592Sbms __func__, inet_ntoa(mreqs.imr_interface), ifp); 2158189592Sbms 2159170613Sbms break; 2160170613Sbms 2161170613Sbms case MCAST_LEAVE_GROUP: 2162170613Sbms case MCAST_LEAVE_SOURCE_GROUP: 2163170613Sbms if (sopt->sopt_name == MCAST_LEAVE_GROUP) { 2164170613Sbms error = sooptcopyin(sopt, &gsr, 2165170613Sbms sizeof(struct group_req), 2166170613Sbms sizeof(struct group_req)); 2167170613Sbms } else if (sopt->sopt_name == MCAST_LEAVE_SOURCE_GROUP) { 2168170613Sbms error = sooptcopyin(sopt, &gsr, 2169170613Sbms sizeof(struct group_source_req), 2170170613Sbms sizeof(struct group_source_req)); 2171170613Sbms } 2172170613Sbms if (error) 2173170613Sbms return (error); 2174170613Sbms 2175170613Sbms if (gsa->sin.sin_family != AF_INET || 2176170613Sbms gsa->sin.sin_len != sizeof(struct sockaddr_in)) 2177170613Sbms return (EINVAL); 2178170613Sbms 2179170613Sbms if (sopt->sopt_name == MCAST_LEAVE_SOURCE_GROUP) { 2180170613Sbms if (ssa->sin.sin_family != AF_INET || 2181170613Sbms ssa->sin.sin_len != sizeof(struct sockaddr_in)) 2182170613Sbms return (EINVAL); 2183170613Sbms } 2184170613Sbms 2185181803Sbz if (gsr.gsr_interface == 0 || V_if_index < gsr.gsr_interface) 2186170613Sbms return (EADDRNOTAVAIL); 2187170613Sbms 2188170613Sbms ifp = ifnet_byindex(gsr.gsr_interface); 2189170613Sbms break; 2190170613Sbms 2191170613Sbms default: 2192189592Sbms CTR2(KTR_IGMPV3, "%s: unknown sopt_name %d", 2193189592Sbms __func__, sopt->sopt_name); 2194170613Sbms return (EOPNOTSUPP); 2195170613Sbms break; 2196170613Sbms } 2197170613Sbms 2198170613Sbms if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr))) 2199170613Sbms return (EINVAL); 2200170613Sbms 2201170613Sbms /* 2202170613Sbms * Find the membership in the membership array. 2203170613Sbms */ 2204170613Sbms imo = inp_findmoptions(inp); 2205170613Sbms idx = imo_match_group(imo, ifp, &gsa->sa); 2206170613Sbms if (idx == -1) { 2207170613Sbms error = EADDRNOTAVAIL; 2208189592Sbms goto out_inp_locked; 2209170613Sbms } 2210189592Sbms inm = imo->imo_membership[idx]; 2211170613Sbms imf = &imo->imo_mfilters[idx]; 2212170613Sbms 2213189592Sbms if (ssa->ss.ss_family != AF_UNSPEC) 2214189592Sbms is_final = 0; 2215189592Sbms 2216170613Sbms /* 2217189592Sbms * Begin state merge transaction at socket layer. 2218189592Sbms */ 2219189592Sbms INP_WLOCK_ASSERT(inp); 2220189592Sbms 2221189592Sbms /* 2222170613Sbms * If we were instructed only to leave a given source, do so. 2223189592Sbms * MCAST_LEAVE_SOURCE_GROUP is only valid for inclusive memberships. 2224170613Sbms */ 2225189592Sbms if (is_final) { 2226189592Sbms imf_leave(imf); 2227189592Sbms } else { 2228189592Sbms if (imf->imf_st[0] == MCAST_EXCLUDE) { 2229189592Sbms error = EADDRNOTAVAIL; 2230189592Sbms goto out_inp_locked; 2231170613Sbms } 2232189592Sbms ims = imo_match_source(imo, idx, &ssa->sa); 2233189592Sbms if (ims == NULL) { 2234189592Sbms CTR3(KTR_IGMPV3, "%s: source %s %spresent", __func__, 2235189592Sbms inet_ntoa(ssa->sin.sin_addr), "not "); 2236189592Sbms error = EADDRNOTAVAIL; 2237189592Sbms goto out_inp_locked; 2238189592Sbms } 2239189592Sbms CTR2(KTR_IGMPV3, "%s: %s source", __func__, "block"); 2240189592Sbms error = imf_prune(imf, &ssa->sin); 2241189592Sbms if (error) { 2242189592Sbms CTR1(KTR_IGMPV3, "%s: merge imf state failed", 2243189592Sbms __func__); 2244189592Sbms goto out_inp_locked; 2245189592Sbms } 2246170613Sbms } 2247170613Sbms 2248170613Sbms /* 2249189592Sbms * Begin state merge transaction at IGMP layer. 2250170613Sbms */ 2251189592Sbms IN_MULTI_LOCK(); 2252170613Sbms 2253189592Sbms if (is_final) { 2254189592Sbms /* 2255189592Sbms * Give up the multicast address record to which 2256189592Sbms * the membership points. 2257189592Sbms */ 2258189592Sbms (void)in_leavegroup_locked(inm, imf); 2259189592Sbms } else { 2260189592Sbms CTR1(KTR_IGMPV3, "%s: merge inm state", __func__); 2261189592Sbms error = inm_merge(inm, imf); 2262189592Sbms if (error) { 2263189592Sbms CTR1(KTR_IGMPV3, "%s: failed to merge inm state", 2264189592Sbms __func__); 2265189592Sbms goto out_imf_rollback; 2266170613Sbms } 2267189592Sbms 2268189592Sbms CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__); 2269189592Sbms error = igmp_change_state(inm); 2270189592Sbms if (error) { 2271189592Sbms CTR1(KTR_IGMPV3, "%s: failed igmp downcall", 2272189592Sbms __func__); 2273189592Sbms } 2274170613Sbms } 2275170613Sbms 2276189592Sbms IN_MULTI_UNLOCK(); 2277170613Sbms 2278189592Sbmsout_imf_rollback: 2279189592Sbms if (error) 2280189592Sbms imf_rollback(imf); 2281189592Sbms else 2282189592Sbms imf_commit(imf); 2283189592Sbms 2284189592Sbms imf_reap(imf); 2285189592Sbms 2286189592Sbms if (is_final) { 2287189592Sbms /* Remove the gap in the membership array. */ 2288189592Sbms for (++idx; idx < imo->imo_num_memberships; ++idx) 2289189592Sbms imo->imo_membership[idx-1] = imo->imo_membership[idx]; 2290189592Sbms imo->imo_num_memberships--; 2291189592Sbms } 2292189592Sbms 2293189592Sbmsout_inp_locked: 2294178285Srwatson INP_WUNLOCK(inp); 2295170613Sbms return (error); 2296170613Sbms} 2297170613Sbms 2298170613Sbms/* 2299170613Sbms * Select the interface for transmitting IPv4 multicast datagrams. 2300170613Sbms * 2301170613Sbms * Either an instance of struct in_addr or an instance of struct ip_mreqn 2302170613Sbms * may be passed to this socket option. An address of INADDR_ANY or an 2303170613Sbms * interface index of 0 is used to remove a previous selection. 2304170613Sbms * When no interface is selected, one is chosen for every send. 2305170613Sbms */ 2306170613Sbmsstatic int 2307170613Sbmsinp_set_multicast_if(struct inpcb *inp, struct sockopt *sopt) 2308170613Sbms{ 2309183550Szec INIT_VNET_NET(curvnet); 2310191548Szec INIT_VNET_INET(curvnet); 2311170613Sbms struct in_addr addr; 2312170613Sbms struct ip_mreqn mreqn; 2313170613Sbms struct ifnet *ifp; 2314170613Sbms struct ip_moptions *imo; 2315170613Sbms int error; 2316170613Sbms 2317170613Sbms if (sopt->sopt_valsize == sizeof(struct ip_mreqn)) { 2318170613Sbms /* 2319170613Sbms * An interface index was specified using the 2320170613Sbms * Linux-derived ip_mreqn structure. 2321170613Sbms */ 2322170613Sbms error = sooptcopyin(sopt, &mreqn, sizeof(struct ip_mreqn), 2323170613Sbms sizeof(struct ip_mreqn)); 2324170613Sbms if (error) 2325170613Sbms return (error); 2326170613Sbms 2327181803Sbz if (mreqn.imr_ifindex < 0 || V_if_index < mreqn.imr_ifindex) 2328170613Sbms return (EINVAL); 2329170613Sbms 2330170613Sbms if (mreqn.imr_ifindex == 0) { 2331170613Sbms ifp = NULL; 2332170613Sbms } else { 2333170613Sbms ifp = ifnet_byindex(mreqn.imr_ifindex); 2334170613Sbms if (ifp == NULL) 2335170613Sbms return (EADDRNOTAVAIL); 2336170613Sbms } 2337170613Sbms } else { 2338170613Sbms /* 2339170613Sbms * An interface was specified by IPv4 address. 2340170613Sbms * This is the traditional BSD usage. 2341170613Sbms */ 2342170613Sbms error = sooptcopyin(sopt, &addr, sizeof(struct in_addr), 2343170613Sbms sizeof(struct in_addr)); 2344170613Sbms if (error) 2345170613Sbms return (error); 2346189592Sbms if (in_nullhost(addr)) { 2347170613Sbms ifp = NULL; 2348170613Sbms } else { 2349170613Sbms INADDR_TO_IFP(addr, ifp); 2350170613Sbms if (ifp == NULL) 2351170613Sbms return (EADDRNOTAVAIL); 2352170613Sbms } 2353189592Sbms CTR3(KTR_IGMPV3, "%s: ifp = %p, addr = %s", __func__, ifp, 2354189592Sbms inet_ntoa(addr)); 2355170613Sbms } 2356170613Sbms 2357170613Sbms /* Reject interfaces which do not support multicast. */ 2358170613Sbms if (ifp != NULL && (ifp->if_flags & IFF_MULTICAST) == 0) 2359170613Sbms return (EOPNOTSUPP); 2360170613Sbms 2361170613Sbms imo = inp_findmoptions(inp); 2362170613Sbms imo->imo_multicast_ifp = ifp; 2363170613Sbms imo->imo_multicast_addr.s_addr = INADDR_ANY; 2364178285Srwatson INP_WUNLOCK(inp); 2365170613Sbms 2366170613Sbms return (0); 2367170613Sbms} 2368170613Sbms 2369170613Sbms/* 2370170613Sbms * Atomically set source filters on a socket for an IPv4 multicast group. 2371189592Sbms * 2372189592Sbms * SMPng: NOTE: Potentially calls malloc(M_WAITOK) with Giant held. 2373170613Sbms */ 2374170613Sbmsstatic int 2375170613Sbmsinp_set_source_filters(struct inpcb *inp, struct sockopt *sopt) 2376170613Sbms{ 2377183550Szec INIT_VNET_NET(curvnet); 2378170613Sbms struct __msfilterreq msfr; 2379170613Sbms sockunion_t *gsa; 2380170613Sbms struct ifnet *ifp; 2381170613Sbms struct in_mfilter *imf; 2382170613Sbms struct ip_moptions *imo; 2383189592Sbms struct in_multi *inm; 2384170613Sbms size_t idx; 2385170613Sbms int error; 2386170613Sbms 2387170613Sbms error = sooptcopyin(sopt, &msfr, sizeof(struct __msfilterreq), 2388170613Sbms sizeof(struct __msfilterreq)); 2389170613Sbms if (error) 2390170613Sbms return (error); 2391170613Sbms 2392189592Sbms if (msfr.msfr_nsrcs > in_mcast_maxsocksrc || 2393170613Sbms (msfr.msfr_fmode != MCAST_EXCLUDE && 2394170613Sbms msfr.msfr_fmode != MCAST_INCLUDE)) 2395170613Sbms return (EINVAL); 2396170613Sbms 2397170613Sbms if (msfr.msfr_group.ss_family != AF_INET || 2398170613Sbms msfr.msfr_group.ss_len != sizeof(struct sockaddr_in)) 2399170613Sbms return (EINVAL); 2400170613Sbms 2401170613Sbms gsa = (sockunion_t *)&msfr.msfr_group; 2402170613Sbms if (!IN_MULTICAST(ntohl(gsa->sin.sin_addr.s_addr))) 2403170613Sbms return (EINVAL); 2404170613Sbms 2405170613Sbms gsa->sin.sin_port = 0; /* ignore port */ 2406170613Sbms 2407181803Sbz if (msfr.msfr_ifindex == 0 || V_if_index < msfr.msfr_ifindex) 2408170613Sbms return (EADDRNOTAVAIL); 2409170613Sbms 2410170613Sbms ifp = ifnet_byindex(msfr.msfr_ifindex); 2411170613Sbms if (ifp == NULL) 2412170613Sbms return (EADDRNOTAVAIL); 2413170613Sbms 2414170613Sbms /* 2415189592Sbms * Take the INP write lock. 2416170613Sbms * Check if this socket is a member of this group. 2417170613Sbms */ 2418170613Sbms imo = inp_findmoptions(inp); 2419170613Sbms idx = imo_match_group(imo, ifp, &gsa->sa); 2420170613Sbms if (idx == -1 || imo->imo_mfilters == NULL) { 2421170613Sbms error = EADDRNOTAVAIL; 2422189592Sbms goto out_inp_locked; 2423170613Sbms } 2424189592Sbms inm = imo->imo_membership[idx]; 2425170613Sbms imf = &imo->imo_mfilters[idx]; 2426170613Sbms 2427170613Sbms /* 2428189592Sbms * Begin state merge transaction at socket layer. 2429170613Sbms */ 2430189592Sbms INP_WLOCK_ASSERT(inp); 2431170613Sbms 2432189592Sbms imf->imf_st[1] = msfr.msfr_fmode; 2433189592Sbms 2434170613Sbms /* 2435170613Sbms * Apply any new source filters, if present. 2436189592Sbms * Make a copy of the user-space source vector so 2437189592Sbms * that we may copy them with a single copyin. This 2438189592Sbms * allows us to deal with page faults up-front. 2439170613Sbms */ 2440170613Sbms if (msfr.msfr_nsrcs > 0) { 2441189592Sbms struct in_msource *lims; 2442189592Sbms struct sockaddr_in *psin; 2443189592Sbms struct sockaddr_storage *kss, *pkss; 2444189592Sbms int i; 2445170613Sbms 2446178285Srwatson INP_WUNLOCK(inp); 2447189592Sbms 2448189592Sbms CTR2(KTR_IGMPV3, "%s: loading %lu source list entries", 2449189592Sbms __func__, (unsigned long)msfr.msfr_nsrcs); 2450184214Sdes kss = malloc(sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs, 2451170613Sbms M_TEMP, M_WAITOK); 2452170613Sbms error = copyin(msfr.msfr_srcs, kss, 2453170613Sbms sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs); 2454170613Sbms if (error) { 2455184205Sdes free(kss, M_TEMP); 2456170613Sbms return (error); 2457170613Sbms } 2458170613Sbms 2459189592Sbms INP_WLOCK(inp); 2460189592Sbms 2461170613Sbms /* 2462189592Sbms * Mark all source filters as UNDEFINED at t1. 2463189592Sbms * Restore new group filter mode, as imf_leave() 2464189592Sbms * will set it to INCLUDE. 2465170613Sbms */ 2466189592Sbms imf_leave(imf); 2467189592Sbms imf->imf_st[1] = msfr.msfr_fmode; 2468189592Sbms 2469189592Sbms /* 2470189592Sbms * Update socket layer filters at t1, lazy-allocating 2471189592Sbms * new entries. This saves a bunch of memory at the 2472189592Sbms * cost of one RB_FIND() per source entry; duplicate 2473189592Sbms * entries in the msfr_nsrcs vector are ignored. 2474189592Sbms * If we encounter an error, rollback transaction. 2475189592Sbms * 2476189592Sbms * XXX This too could be replaced with a set-symmetric 2477189592Sbms * difference like loop to avoid walking from root 2478189592Sbms * every time, as the key space is common. 2479189592Sbms */ 2480189592Sbms for (i = 0, pkss = kss; i < msfr.msfr_nsrcs; i++, pkss++) { 2481189592Sbms psin = (struct sockaddr_in *)pkss; 2482189592Sbms if (psin->sin_family != AF_INET) { 2483170613Sbms error = EAFNOSUPPORT; 2484170613Sbms break; 2485170613Sbms } 2486189592Sbms if (psin->sin_len != sizeof(struct sockaddr_in)) { 2487189592Sbms error = EINVAL; 2488189592Sbms break; 2489189592Sbms } 2490189592Sbms error = imf_get_source(imf, psin, &lims); 2491170613Sbms if (error) 2492170613Sbms break; 2493189592Sbms lims->imsl_st[1] = imf->imf_st[1]; 2494170613Sbms } 2495189592Sbms free(kss, M_TEMP); 2496189592Sbms } 2497170613Sbms 2498189592Sbms if (error) 2499189592Sbms goto out_imf_rollback; 2500170613Sbms 2501189592Sbms INP_WLOCK_ASSERT(inp); 2502189592Sbms IN_MULTI_LOCK(); 2503170613Sbms 2504170613Sbms /* 2505189592Sbms * Begin state merge transaction at IGMP layer. 2506170613Sbms */ 2507189592Sbms CTR1(KTR_IGMPV3, "%s: merge inm state", __func__); 2508189592Sbms error = inm_merge(inm, imf); 2509189592Sbms if (error) { 2510189592Sbms CTR1(KTR_IGMPV3, "%s: failed to merge inm state", __func__); 2511189592Sbms goto out_imf_rollback; 2512189592Sbms } 2513170613Sbms 2514189592Sbms CTR1(KTR_IGMPV3, "%s: doing igmp downcall", __func__); 2515189592Sbms error = igmp_change_state(inm); 2516189592Sbms if (error) 2517189592Sbms CTR1(KTR_IGMPV3, "%s: failed igmp downcall", __func__); 2518189592Sbms 2519189592Sbms IN_MULTI_UNLOCK(); 2520189592Sbms 2521189592Sbmsout_imf_rollback: 2522189592Sbms if (error) 2523189592Sbms imf_rollback(imf); 2524189592Sbms else 2525189592Sbms imf_commit(imf); 2526189592Sbms 2527189592Sbms imf_reap(imf); 2528189592Sbms 2529189592Sbmsout_inp_locked: 2530178285Srwatson INP_WUNLOCK(inp); 2531170613Sbms return (error); 2532170613Sbms} 2533170613Sbms 2534170613Sbms/* 2535170613Sbms * Set the IP multicast options in response to user setsockopt(). 2536170613Sbms * 2537170613Sbms * Many of the socket options handled in this function duplicate the 2538170613Sbms * functionality of socket options in the regular unicast API. However, 2539170613Sbms * it is not possible to merge the duplicate code, because the idempotence 2540170613Sbms * of the IPv4 multicast part of the BSD Sockets API must be preserved; 2541170613Sbms * the effects of these options must be treated as separate and distinct. 2542189592Sbms * 2543189592Sbms * SMPng: XXX: Unlocked read of inp_socket believed OK. 2544189592Sbms * FUTURE: The IP_MULTICAST_VIF option may be eliminated if MROUTING 2545189592Sbms * is refactored to no longer use vifs. 2546170613Sbms */ 2547170613Sbmsint 2548170613Sbmsinp_setmoptions(struct inpcb *inp, struct sockopt *sopt) 2549170613Sbms{ 2550170613Sbms struct ip_moptions *imo; 2551170613Sbms int error; 2552170613Sbms 2553170613Sbms error = 0; 2554170613Sbms 2555171746Scsjp /* 2556171746Scsjp * If socket is neither of type SOCK_RAW or SOCK_DGRAM, 2557171746Scsjp * or is a divert socket, reject it. 2558171746Scsjp */ 2559171746Scsjp if (inp->inp_socket->so_proto->pr_protocol == IPPROTO_DIVERT || 2560171746Scsjp (inp->inp_socket->so_proto->pr_type != SOCK_RAW && 2561189592Sbms inp->inp_socket->so_proto->pr_type != SOCK_DGRAM)) 2562171746Scsjp return (EOPNOTSUPP); 2563171746Scsjp 2564170613Sbms switch (sopt->sopt_name) { 2565170613Sbms case IP_MULTICAST_VIF: { 2566170613Sbms int vifi; 2567170613Sbms /* 2568170613Sbms * Select a multicast VIF for transmission. 2569170613Sbms * Only useful if multicast forwarding is active. 2570170613Sbms */ 2571170613Sbms if (legal_vif_num == NULL) { 2572170613Sbms error = EOPNOTSUPP; 2573170613Sbms break; 2574170613Sbms } 2575170613Sbms error = sooptcopyin(sopt, &vifi, sizeof(int), sizeof(int)); 2576170613Sbms if (error) 2577170613Sbms break; 2578170613Sbms if (!legal_vif_num(vifi) && (vifi != -1)) { 2579170613Sbms error = EINVAL; 2580170613Sbms break; 2581170613Sbms } 2582170613Sbms imo = inp_findmoptions(inp); 2583170613Sbms imo->imo_multicast_vif = vifi; 2584178285Srwatson INP_WUNLOCK(inp); 2585170613Sbms break; 2586170613Sbms } 2587170613Sbms 2588170613Sbms case IP_MULTICAST_IF: 2589170613Sbms error = inp_set_multicast_if(inp, sopt); 2590170613Sbms break; 2591170613Sbms 2592170613Sbms case IP_MULTICAST_TTL: { 2593170613Sbms u_char ttl; 2594170613Sbms 2595170613Sbms /* 2596170613Sbms * Set the IP time-to-live for outgoing multicast packets. 2597170613Sbms * The original multicast API required a char argument, 2598170613Sbms * which is inconsistent with the rest of the socket API. 2599170613Sbms * We allow either a char or an int. 2600170613Sbms */ 2601170613Sbms if (sopt->sopt_valsize == sizeof(u_char)) { 2602170613Sbms error = sooptcopyin(sopt, &ttl, sizeof(u_char), 2603170613Sbms sizeof(u_char)); 2604170613Sbms if (error) 2605170613Sbms break; 2606170613Sbms } else { 2607170613Sbms u_int ittl; 2608170613Sbms 2609170613Sbms error = sooptcopyin(sopt, &ittl, sizeof(u_int), 2610170613Sbms sizeof(u_int)); 2611170613Sbms if (error) 2612170613Sbms break; 2613170613Sbms if (ittl > 255) { 2614170613Sbms error = EINVAL; 2615170613Sbms break; 2616170613Sbms } 2617170613Sbms ttl = (u_char)ittl; 2618170613Sbms } 2619170613Sbms imo = inp_findmoptions(inp); 2620170613Sbms imo->imo_multicast_ttl = ttl; 2621178285Srwatson INP_WUNLOCK(inp); 2622170613Sbms break; 2623170613Sbms } 2624170613Sbms 2625170613Sbms case IP_MULTICAST_LOOP: { 2626170613Sbms u_char loop; 2627170613Sbms 2628170613Sbms /* 2629170613Sbms * Set the loopback flag for outgoing multicast packets. 2630170613Sbms * Must be zero or one. The original multicast API required a 2631170613Sbms * char argument, which is inconsistent with the rest 2632170613Sbms * of the socket API. We allow either a char or an int. 2633170613Sbms */ 2634170613Sbms if (sopt->sopt_valsize == sizeof(u_char)) { 2635170613Sbms error = sooptcopyin(sopt, &loop, sizeof(u_char), 2636170613Sbms sizeof(u_char)); 2637170613Sbms if (error) 2638170613Sbms break; 2639170613Sbms } else { 2640170613Sbms u_int iloop; 2641170613Sbms 2642170613Sbms error = sooptcopyin(sopt, &iloop, sizeof(u_int), 2643170613Sbms sizeof(u_int)); 2644170613Sbms if (error) 2645170613Sbms break; 2646170613Sbms loop = (u_char)iloop; 2647170613Sbms } 2648170613Sbms imo = inp_findmoptions(inp); 2649170613Sbms imo->imo_multicast_loop = !!loop; 2650178285Srwatson INP_WUNLOCK(inp); 2651170613Sbms break; 2652170613Sbms } 2653170613Sbms 2654170613Sbms case IP_ADD_MEMBERSHIP: 2655170613Sbms case IP_ADD_SOURCE_MEMBERSHIP: 2656170613Sbms case MCAST_JOIN_GROUP: 2657170613Sbms case MCAST_JOIN_SOURCE_GROUP: 2658170613Sbms error = inp_join_group(inp, sopt); 2659170613Sbms break; 2660170613Sbms 2661170613Sbms case IP_DROP_MEMBERSHIP: 2662170613Sbms case IP_DROP_SOURCE_MEMBERSHIP: 2663170613Sbms case MCAST_LEAVE_GROUP: 2664170613Sbms case MCAST_LEAVE_SOURCE_GROUP: 2665170613Sbms error = inp_leave_group(inp, sopt); 2666170613Sbms break; 2667170613Sbms 2668170613Sbms case IP_BLOCK_SOURCE: 2669170613Sbms case IP_UNBLOCK_SOURCE: 2670170613Sbms case MCAST_BLOCK_SOURCE: 2671170613Sbms case MCAST_UNBLOCK_SOURCE: 2672189592Sbms error = inp_block_unblock_source(inp, sopt); 2673170613Sbms break; 2674170613Sbms 2675170613Sbms case IP_MSFILTER: 2676170613Sbms error = inp_set_source_filters(inp, sopt); 2677170613Sbms break; 2678170613Sbms 2679170613Sbms default: 2680170613Sbms error = EOPNOTSUPP; 2681170613Sbms break; 2682170613Sbms } 2683170613Sbms 2684170613Sbms INP_UNLOCK_ASSERT(inp); 2685170613Sbms 2686170613Sbms return (error); 2687170613Sbms} 2688189592Sbms 2689189592Sbms/* 2690189592Sbms * Expose IGMP's multicast filter mode and source list(s) to userland, 2691189592Sbms * keyed by (ifindex, group). 2692189592Sbms * The filter mode is written out as a uint32_t, followed by 2693189592Sbms * 0..n of struct in_addr. 2694189592Sbms * For use by ifmcstat(8). 2695189592Sbms * SMPng: NOTE: unlocked read of ifindex space. 2696189592Sbms */ 2697189592Sbmsstatic int 2698189592Sbmssysctl_ip_mcast_filters(SYSCTL_HANDLER_ARGS) 2699189592Sbms{ 2700189592Sbms INIT_VNET_NET(curvnet); 2701189592Sbms struct in_addr src, group; 2702189592Sbms struct ifnet *ifp; 2703189592Sbms struct ifmultiaddr *ifma; 2704189592Sbms struct in_multi *inm; 2705189592Sbms struct ip_msource *ims; 2706189592Sbms int *name; 2707189592Sbms int retval; 2708189592Sbms u_int namelen; 2709189592Sbms uint32_t fmode, ifindex; 2710189592Sbms 2711189592Sbms name = (int *)arg1; 2712189592Sbms namelen = arg2; 2713189592Sbms 2714189592Sbms if (req->newptr != NULL) 2715189592Sbms return (EPERM); 2716189592Sbms 2717189592Sbms if (namelen != 2) 2718189592Sbms return (EINVAL); 2719189592Sbms 2720189592Sbms ifindex = name[0]; 2721189592Sbms if (ifindex <= 0 || ifindex > V_if_index) { 2722189592Sbms CTR2(KTR_IGMPV3, "%s: ifindex %u out of range", 2723189592Sbms __func__, ifindex); 2724189592Sbms return (ENOENT); 2725189592Sbms } 2726189592Sbms 2727189592Sbms group.s_addr = name[1]; 2728189592Sbms if (!IN_MULTICAST(ntohl(group.s_addr))) { 2729189592Sbms CTR2(KTR_IGMPV3, "%s: group %s is not multicast", 2730189592Sbms __func__, inet_ntoa(group)); 2731189592Sbms return (EINVAL); 2732189592Sbms } 2733189592Sbms 2734189592Sbms ifp = ifnet_byindex(ifindex); 2735189592Sbms if (ifp == NULL) { 2736189592Sbms CTR2(KTR_IGMPV3, "%s: no ifp for ifindex %u", 2737189592Sbms __func__, ifindex); 2738189592Sbms return (ENOENT); 2739189592Sbms } 2740189592Sbms 2741189592Sbms retval = sysctl_wire_old_buffer(req, 2742189592Sbms sizeof(uint32_t) + (in_mcast_maxgrpsrc * sizeof(struct in_addr))); 2743189592Sbms if (retval) 2744189592Sbms return (retval); 2745189592Sbms 2746189592Sbms IN_MULTI_LOCK(); 2747189592Sbms 2748189592Sbms IF_ADDR_LOCK(ifp); 2749189592Sbms TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 2750189592Sbms if (ifma->ifma_addr->sa_family != AF_INET || 2751189592Sbms ifma->ifma_protospec == NULL) 2752189592Sbms continue; 2753189592Sbms inm = (struct in_multi *)ifma->ifma_protospec; 2754189592Sbms if (!in_hosteq(inm->inm_addr, group)) 2755189592Sbms continue; 2756189592Sbms fmode = inm->inm_st[1].iss_fmode; 2757189592Sbms retval = SYSCTL_OUT(req, &fmode, sizeof(uint32_t)); 2758189592Sbms if (retval != 0) 2759189592Sbms break; 2760189592Sbms RB_FOREACH(ims, ip_msource_tree, &inm->inm_srcs) { 2761189592Sbms#ifdef KTR 2762189592Sbms struct in_addr ina; 2763189592Sbms ina.s_addr = htonl(ims->ims_haddr); 2764189592Sbms CTR2(KTR_IGMPV3, "%s: visit node %s", __func__, 2765189592Sbms inet_ntoa(ina)); 2766189592Sbms#endif 2767189592Sbms /* 2768189592Sbms * Only copy-out sources which are in-mode. 2769189592Sbms */ 2770189592Sbms if (fmode != ims_get_mode(inm, ims, 1)) { 2771189592Sbms CTR1(KTR_IGMPV3, "%s: skip non-in-mode", 2772189592Sbms __func__); 2773189592Sbms continue; 2774189592Sbms } 2775189592Sbms src.s_addr = htonl(ims->ims_haddr); 2776189592Sbms retval = SYSCTL_OUT(req, &src, sizeof(struct in_addr)); 2777189592Sbms if (retval != 0) 2778189592Sbms break; 2779189592Sbms } 2780189592Sbms } 2781189592Sbms IF_ADDR_UNLOCK(ifp); 2782189592Sbms 2783189592Sbms IN_MULTI_UNLOCK(); 2784189592Sbms 2785189592Sbms return (retval); 2786189592Sbms} 2787189592Sbms 2788189592Sbms#ifdef KTR 2789189592Sbms 2790189592Sbmsstatic const char *inm_modestrs[] = { "un", "in", "ex" }; 2791189592Sbms 2792189592Sbmsstatic const char * 2793189592Sbmsinm_mode_str(const int mode) 2794189592Sbms{ 2795189592Sbms 2796189592Sbms if (mode >= MCAST_UNDEFINED && mode <= MCAST_EXCLUDE) 2797189592Sbms return (inm_modestrs[mode]); 2798189592Sbms return ("??"); 2799189592Sbms} 2800189592Sbms 2801189592Sbmsstatic const char *inm_statestrs[] = { 2802189592Sbms "not-member", 2803189592Sbms "silent", 2804189592Sbms "idle", 2805189592Sbms "lazy", 2806189592Sbms "sleeping", 2807189592Sbms "awakening", 2808189592Sbms "query-pending", 2809189592Sbms "sg-query-pending", 2810189592Sbms "leaving" 2811189592Sbms}; 2812189592Sbms 2813189592Sbmsstatic const char * 2814189592Sbmsinm_state_str(const int state) 2815189592Sbms{ 2816189592Sbms 2817189592Sbms if (state >= IGMP_NOT_MEMBER && state <= IGMP_LEAVING_MEMBER) 2818189592Sbms return (inm_statestrs[state]); 2819189592Sbms return ("??"); 2820189592Sbms} 2821189592Sbms 2822189592Sbms/* 2823189592Sbms * Dump an in_multi structure to the console. 2824189592Sbms */ 2825189592Sbmsvoid 2826189592Sbmsinm_print(const struct in_multi *inm) 2827189592Sbms{ 2828189592Sbms int t; 2829189592Sbms 2830190753Skan if ((ktr_mask & KTR_IGMPV3) == 0) 2831189635Sbms return; 2832189635Sbms 2833189592Sbms printf("%s: --- begin inm %p ---\n", __func__, inm); 2834189592Sbms printf("addr %s ifp %p(%s) ifma %p\n", 2835189592Sbms inet_ntoa(inm->inm_addr), 2836189592Sbms inm->inm_ifp, 2837189592Sbms inm->inm_ifp->if_xname, 2838189592Sbms inm->inm_ifma); 2839189592Sbms printf("timer %u state %s refcount %u scq.len %u\n", 2840189592Sbms inm->inm_timer, 2841189592Sbms inm_state_str(inm->inm_state), 2842189592Sbms inm->inm_refcount, 2843189592Sbms inm->inm_scq.ifq_len); 2844189592Sbms printf("igi %p nsrc %lu sctimer %u scrv %u\n", 2845189592Sbms inm->inm_igi, 2846189592Sbms inm->inm_nsrc, 2847189592Sbms inm->inm_sctimer, 2848189592Sbms inm->inm_scrv); 2849189592Sbms for (t = 0; t < 2; t++) { 2850189592Sbms printf("t%d: fmode %s asm %u ex %u in %u rec %u\n", t, 2851189592Sbms inm_mode_str(inm->inm_st[t].iss_fmode), 2852189592Sbms inm->inm_st[t].iss_asm, 2853189592Sbms inm->inm_st[t].iss_ex, 2854189592Sbms inm->inm_st[t].iss_in, 2855189592Sbms inm->inm_st[t].iss_rec); 2856189592Sbms } 2857189592Sbms printf("%s: --- end inm %p ---\n", __func__, inm); 2858189592Sbms} 2859189592Sbms 2860189592Sbms#else /* !KTR */ 2861189592Sbms 2862189592Sbmsvoid 2863189592Sbmsinm_print(const struct in_multi *inm) 2864189592Sbms{ 2865189592Sbms 2866189592Sbms} 2867189592Sbms 2868189592Sbms#endif /* KTR */ 2869189592Sbms 2870189592SbmsRB_GENERATE(ip_msource_tree, ip_msource, ims_link, ip_msource_cmp); 2871