1 /* 2 * Copyright (c) 2009 Bruce Simpson. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote 14 * products derived from this software without specific prior written 15 * permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 /* 31 * IPv6 multicast socket, group, and socket option processing module. 32 * Normative references: RFC 2292, RFC 3492, RFC 3542, RFC 3678, RFC 3810. 33 */ 34 35 #include <sys/cdefs.h> 36 __FBSDID("$FreeBSD$"); 37 38 #include "opt_inet6.h" 39 #include "opt_route.h" 40 41 #include <sys/param.h> 42 #include <sys/systm.h> 43 #include <sys/kernel.h> 44 #include <sys/malloc.h> 45 #include <sys/mbuf.h> 46 #include <sys/protosw.h> 47 #include <sys/socket.h> 48 #include <sys/socketvar.h> 49 #include <sys/protosw.h> 50 #include <sys/sysctl.h> 51 #include <sys/priv.h> 52 #include <sys/ktr.h> 53 #include <sys/tree.h> 54 #include <sys/vimage.h> 55 56 #include <net/if.h> 57 #include <net/if_dl.h> 58 #include <net/route.h> 59 #include <net/vnet.h> 60 61 #include <netinet/in.h> 62 #include <netinet/in_var.h> 63 #include <netinet6/in6_var.h> 64 #include <netinet/ip6.h> 65 #include <netinet/icmp6.h> 66 #include <netinet6/ip6_var.h> 67 #include <netinet/in_pcb.h> 68 #include <netinet/tcp_var.h> 69 #include <netinet6/nd6.h> 70 #include <netinet/vinet.h> 71 #include <netinet6/vinet6.h> 72 #include <netinet6/mld6_var.h> 73 74 #ifndef KTR_MLD 75 #define KTR_MLD KTR_INET6 76 #endif 77 78 #ifndef __SOCKUNION_DECLARED 79 union sockunion { 80 struct sockaddr_storage ss; 81 struct sockaddr sa; 82 struct sockaddr_dl sdl; 83 struct sockaddr_in6 sin6; 84 }; 85 typedef union sockunion sockunion_t; 86 #define __SOCKUNION_DECLARED 87 #endif /* __SOCKUNION_DECLARED */ 88 89 static MALLOC_DEFINE(M_IN6MFILTER, "in6_mfilter", 90 "IPv6 multicast PCB-layer source filter"); 91 static MALLOC_DEFINE(M_IP6MADDR, "in6_multi", "IPv6 multicast group"); 92 static MALLOC_DEFINE(M_IP6MOPTS, "ip6_moptions", "IPv6 multicast options"); 93 static MALLOC_DEFINE(M_IP6MSOURCE, "ip6_msource", 94 "IPv6 multicast MLD-layer source filter"); 95 96 RB_GENERATE(ip6_msource_tree, ip6_msource, im6s_link, ip6_msource_cmp); 97 98 /* 99 * Locking: 100 * - Lock order is: Giant, INP_WLOCK, IN6_MULTI_LOCK, MLD_LOCK, IF_ADDR_LOCK. 101 * - The IF_ADDR_LOCK is implicitly taken by in6m_lookup() earlier, however 102 * it can be taken by code in net/if.c also. 103 * - ip6_moptions and in6_mfilter are covered by the INP_WLOCK. 104 * 105 * struct in6_multi is covered by IN6_MULTI_LOCK. There isn't strictly 106 * any need for in6_multi itself to be virtualized -- it is bound to an ifp 107 * anyway no matter what happens. 108 */ 109 struct mtx in6_multi_mtx; 110 MTX_SYSINIT(in6_multi_mtx, &in6_multi_mtx, "in6_multi_mtx", MTX_DEF); 111 112 static void im6f_commit(struct in6_mfilter *); 113 static int im6f_get_source(struct in6_mfilter *imf, 114 const struct sockaddr_in6 *psin, 115 struct in6_msource **); 116 static struct in6_msource * 117 im6f_graft(struct in6_mfilter *, const uint8_t, 118 const struct sockaddr_in6 *); 119 static void im6f_leave(struct in6_mfilter *); 120 static int im6f_prune(struct in6_mfilter *, const struct sockaddr_in6 *); 121 static void im6f_purge(struct in6_mfilter *); 122 static void im6f_rollback(struct in6_mfilter *); 123 static void im6f_reap(struct in6_mfilter *); 124 static int im6o_grow(struct ip6_moptions *); 125 static size_t im6o_match_group(const struct ip6_moptions *, 126 const struct ifnet *, const struct sockaddr *); 127 static struct in6_msource * 128 im6o_match_source(const struct ip6_moptions *, const size_t, 129 const struct sockaddr *); 130 static void im6s_merge(struct ip6_msource *ims, 131 const struct in6_msource *lims, const int rollback); 132 static int in6_mc_get(struct ifnet *, const struct in6_addr *, 133 struct in6_multi **); 134 static int in6m_get_source(struct in6_multi *inm, 135 const struct in6_addr *addr, const int noalloc, 136 struct ip6_msource **pims); 137 static int in6m_is_ifp_detached(const struct in6_multi *); 138 static int in6m_merge(struct in6_multi *, /*const*/ struct in6_mfilter *); 139 static void in6m_purge(struct in6_multi *); 140 static void in6m_reap(struct in6_multi *); 141 static struct ip6_moptions * 142 in6p_findmoptions(struct inpcb *); 143 static int in6p_get_source_filters(struct inpcb *, struct sockopt *); 144 static int in6p_join_group(struct inpcb *, struct sockopt *); 145 static int in6p_leave_group(struct inpcb *, struct sockopt *); 146 static struct ifnet * 147 in6p_lookup_mcast_ifp(const struct inpcb *, 148 const struct sockaddr_in6 *); 149 static int in6p_block_unblock_source(struct inpcb *, struct sockopt *); 150 static int in6p_set_multicast_if(struct inpcb *, struct sockopt *); 151 static int in6p_set_source_filters(struct inpcb *, struct sockopt *); 152 static int sysctl_ip6_mcast_filters(SYSCTL_HANDLER_ARGS); 153 154 SYSCTL_DECL(_net_inet6_ip6); /* XXX Not in any common header. */ 155 156 SYSCTL_NODE(_net_inet6_ip6, OID_AUTO, mcast, CTLFLAG_RW, 0, "IPv6 multicast"); 157 158 static u_long in6_mcast_maxgrpsrc = IPV6_MAX_GROUP_SRC_FILTER; 159 SYSCTL_ULONG(_net_inet6_ip6_mcast, OID_AUTO, maxgrpsrc, 160 CTLFLAG_RW | CTLFLAG_TUN, &in6_mcast_maxgrpsrc, 0, 161 "Max source filters per group"); 162 TUNABLE_ULONG("net.inet6.ip6.mcast.maxgrpsrc", &in6_mcast_maxgrpsrc); 163 164 static u_long in6_mcast_maxsocksrc = IPV6_MAX_SOCK_SRC_FILTER; 165 SYSCTL_ULONG(_net_inet6_ip6_mcast, OID_AUTO, maxsocksrc, 166 CTLFLAG_RW | CTLFLAG_TUN, &in6_mcast_maxsocksrc, 0, 167 "Max source filters per socket"); 168 TUNABLE_ULONG("net.inet6.ip6.mcast.maxsocksrc", &in6_mcast_maxsocksrc); 169 170 /* TODO Virtualize this switch. */ 171 int in6_mcast_loop = IPV6_DEFAULT_MULTICAST_LOOP; 172 SYSCTL_INT(_net_inet6_ip6_mcast, OID_AUTO, loop, CTLFLAG_RW | CTLFLAG_TUN, 173 &in6_mcast_loop, 0, "Loopback multicast datagrams by default"); 174 TUNABLE_INT("net.inet6.ip6.mcast.loop", &in6_mcast_loop); 175 176 SYSCTL_NODE(_net_inet6_ip6_mcast, OID_AUTO, filters, 177 CTLFLAG_RD | CTLFLAG_MPSAFE, sysctl_ip6_mcast_filters, 178 "Per-interface stack-wide source filters"); 179 180 /* 181 * Inline function which wraps assertions for a valid ifp. 182 * The ifnet layer will set the ifma's ifp pointer to NULL if the ifp 183 * is detached. 184 */ 185 static int __inline 186 in6m_is_ifp_detached(const struct in6_multi *inm) 187 { 188 struct ifnet *ifp; 189 190 KASSERT(inm->in6m_ifma != NULL, ("%s: no ifma", __func__)); 191 ifp = inm->in6m_ifma->ifma_ifp; 192 if (ifp != NULL) { 193 /* 194 * Sanity check that network-layer notion of ifp is the 195 * same as that of link-layer. 196 */ 197 KASSERT(inm->in6m_ifp == ifp, ("%s: bad ifp", __func__)); 198 } 199 200 return (ifp == NULL); 201 } 202 203 /* 204 * Initialize an in6_mfilter structure to a known state at t0, t1 205 * with an empty source filter list. 206 */ 207 static __inline void 208 im6f_init(struct in6_mfilter *imf, const int st0, const int st1) 209 { 210 memset(imf, 0, sizeof(struct in6_mfilter)); 211 RB_INIT(&imf->im6f_sources); 212 imf->im6f_st[0] = st0; 213 imf->im6f_st[1] = st1; 214 } 215 216 /* 217 * Resize the ip6_moptions vector to the next power-of-two minus 1. 218 * May be called with locks held; do not sleep. 219 */ 220 static int 221 im6o_grow(struct ip6_moptions *imo) 222 { 223 struct in6_multi **nmships; 224 struct in6_multi **omships; 225 struct in6_mfilter *nmfilters; 226 struct in6_mfilter *omfilters; 227 size_t idx; 228 size_t newmax; 229 size_t oldmax; 230 231 nmships = NULL; 232 nmfilters = NULL; 233 omships = imo->im6o_membership; 234 omfilters = imo->im6o_mfilters; 235 oldmax = imo->im6o_max_memberships; 236 newmax = ((oldmax + 1) * 2) - 1; 237 238 if (newmax <= IPV6_MAX_MEMBERSHIPS) { 239 nmships = (struct in6_multi **)realloc(omships, 240 sizeof(struct in6_multi *) * newmax, M_IP6MOPTS, M_NOWAIT); 241 nmfilters = (struct in6_mfilter *)realloc(omfilters, 242 sizeof(struct in6_mfilter) * newmax, M_IN6MFILTER, 243 M_NOWAIT); 244 if (nmships != NULL && nmfilters != NULL) { 245 /* Initialize newly allocated source filter heads. */ 246 for (idx = oldmax; idx < newmax; idx++) { 247 im6f_init(&nmfilters[idx], MCAST_UNDEFINED, 248 MCAST_EXCLUDE); 249 } 250 imo->im6o_max_memberships = newmax; 251 imo->im6o_membership = nmships; 252 imo->im6o_mfilters = nmfilters; 253 } 254 } 255 256 if (nmships == NULL || nmfilters == NULL) { 257 if (nmships != NULL) 258 free(nmships, M_IP6MOPTS); 259 if (nmfilters != NULL) 260 free(nmfilters, M_IN6MFILTER); 261 return (ETOOMANYREFS); 262 } 263 264 return (0); 265 } 266 267 /* 268 * Find an IPv6 multicast group entry for this ip6_moptions instance 269 * which matches the specified group, and optionally an interface. 270 * Return its index into the array, or -1 if not found. 271 */ 272 static size_t 273 im6o_match_group(const struct ip6_moptions *imo, const struct ifnet *ifp, 274 const struct sockaddr *group) 275 { 276 const struct sockaddr_in6 *gsin6; 277 struct in6_multi **pinm; 278 int idx; 279 int nmships; 280 281 gsin6 = (const struct sockaddr_in6 *)group; 282 283 /* The im6o_membership array may be lazy allocated. */ 284 if (imo->im6o_membership == NULL || imo->im6o_num_memberships == 0) 285 return (-1); 286 287 nmships = imo->im6o_num_memberships; 288 pinm = &imo->im6o_membership[0]; 289 for (idx = 0; idx < nmships; idx++, pinm++) { 290 if (*pinm == NULL) 291 continue; 292 if ((ifp == NULL || ((*pinm)->in6m_ifp == ifp)) && 293 IN6_ARE_ADDR_EQUAL(&(*pinm)->in6m_addr, 294 &gsin6->sin6_addr)) { 295 break; 296 } 297 } 298 if (idx >= nmships) 299 idx = -1; 300 301 return (idx); 302 } 303 304 /* 305 * Find an IPv6 multicast source entry for this imo which matches 306 * the given group index for this socket, and source address. 307 * 308 * NOTE: This does not check if the entry is in-mode, merely if 309 * it exists, which may not be the desired behaviour. 310 */ 311 static struct in6_msource * 312 im6o_match_source(const struct ip6_moptions *imo, const size_t gidx, 313 const struct sockaddr *src) 314 { 315 struct ip6_msource find; 316 struct in6_mfilter *imf; 317 struct ip6_msource *ims; 318 const sockunion_t *psa; 319 320 KASSERT(src->sa_family == AF_INET6, ("%s: !AF_INET6", __func__)); 321 KASSERT(gidx != -1 && gidx < imo->im6o_num_memberships, 322 ("%s: invalid index %d\n", __func__, (int)gidx)); 323 324 /* The im6o_mfilters array may be lazy allocated. */ 325 if (imo->im6o_mfilters == NULL) 326 return (NULL); 327 imf = &imo->im6o_mfilters[gidx]; 328 329 psa = (const sockunion_t *)src; 330 find.im6s_addr = psa->sin6.sin6_addr; 331 ims = RB_FIND(ip6_msource_tree, &imf->im6f_sources, &find); 332 333 return ((struct in6_msource *)ims); 334 } 335 336 /* 337 * Perform filtering for multicast datagrams on a socket by group and source. 338 * 339 * Returns 0 if a datagram should be allowed through, or various error codes 340 * if the socket was not a member of the group, or the source was muted, etc. 341 */ 342 int 343 im6o_mc_filter(const struct ip6_moptions *imo, const struct ifnet *ifp, 344 const struct sockaddr *group, const struct sockaddr *src) 345 { 346 size_t gidx; 347 struct in6_msource *ims; 348 int mode; 349 350 KASSERT(ifp != NULL, ("%s: null ifp", __func__)); 351 352 gidx = im6o_match_group(imo, ifp, group); 353 if (gidx == -1) 354 return (MCAST_NOTGMEMBER); 355 356 /* 357 * Check if the source was included in an (S,G) join. 358 * Allow reception on exclusive memberships by default, 359 * reject reception on inclusive memberships by default. 360 * Exclude source only if an in-mode exclude filter exists. 361 * Include source only if an in-mode include filter exists. 362 * NOTE: We are comparing group state here at MLD t1 (now) 363 * with socket-layer t0 (since last downcall). 364 */ 365 mode = imo->im6o_mfilters[gidx].im6f_st[1]; 366 ims = im6o_match_source(imo, gidx, src); 367 368 if ((ims == NULL && mode == MCAST_INCLUDE) || 369 (ims != NULL && ims->im6sl_st[0] != mode)) 370 return (MCAST_NOTSMEMBER); 371 372 return (MCAST_PASS); 373 } 374 375 /* 376 * Find and return a reference to an in6_multi record for (ifp, group), 377 * and bump its reference count. 378 * If one does not exist, try to allocate it, and update link-layer multicast 379 * filters on ifp to listen for group. 380 * Assumes the IN6_MULTI lock is held across the call. 381 * Return 0 if successful, otherwise return an appropriate error code. 382 */ 383 static int 384 in6_mc_get(struct ifnet *ifp, const struct in6_addr *group, 385 struct in6_multi **pinm) 386 { 387 struct sockaddr_in6 gsin6; 388 struct ifmultiaddr *ifma; 389 struct in6_multi *inm; 390 int error; 391 392 error = 0; 393 394 /* 395 * XXX: Accesses to ifma_protospec must be covered by IF_ADDR_LOCK; 396 * if_addmulti() takes this mutex itself, so we must drop and 397 * re-acquire around the call. 398 */ 399 IN6_MULTI_LOCK_ASSERT(); 400 IF_ADDR_LOCK(ifp); 401 402 inm = in6m_lookup_locked(ifp, group); 403 if (inm != NULL) { 404 /* 405 * If we already joined this group, just bump the 406 * refcount and return it. 407 */ 408 KASSERT(inm->in6m_refcount >= 1, 409 ("%s: bad refcount %d", __func__, inm->in6m_refcount)); 410 ++inm->in6m_refcount; 411 *pinm = inm; 412 goto out_locked; 413 } 414 415 memset(&gsin6, 0, sizeof(gsin6)); 416 gsin6.sin6_family = AF_INET6; 417 gsin6.sin6_len = sizeof(struct sockaddr_in6); 418 gsin6.sin6_addr = *group; 419 420 /* 421 * Check if a link-layer group is already associated 422 * with this network-layer group on the given ifnet. 423 */ 424 IF_ADDR_UNLOCK(ifp); 425 error = if_addmulti(ifp, (struct sockaddr *)&gsin6, &ifma); 426 if (error != 0) 427 return (error); 428 IF_ADDR_LOCK(ifp); 429 430 /* 431 * If something other than netinet6 is occupying the link-layer 432 * group, print a meaningful error message and back out of 433 * the allocation. 434 * Otherwise, bump the refcount on the existing network-layer 435 * group association and return it. 436 */ 437 if (ifma->ifma_protospec != NULL) { 438 inm = (struct in6_multi *)ifma->ifma_protospec; 439 #ifdef INVARIANTS 440 KASSERT(ifma->ifma_addr != NULL, ("%s: no ifma_addr", 441 __func__)); 442 KASSERT(ifma->ifma_addr->sa_family == AF_INET6, 443 ("%s: ifma not AF_INET6", __func__)); 444 KASSERT(inm != NULL, ("%s: no ifma_protospec", __func__)); 445 if (inm->in6m_ifma != ifma || inm->in6m_ifp != ifp || 446 !IN6_ARE_ADDR_EQUAL(&inm->in6m_addr, group)) 447 panic("%s: ifma %p is inconsistent with %p (%p)", 448 __func__, ifma, inm, group); 449 #endif 450 ++inm->in6m_refcount; 451 *pinm = inm; 452 goto out_locked; 453 } 454 455 IF_ADDR_LOCK_ASSERT(ifp); 456 457 /* 458 * A new in6_multi record is needed; allocate and initialize it. 459 * We DO NOT perform an MLD join as the in6_ layer may need to 460 * push an initial source list down to MLD to support SSM. 461 * 462 * The initial source filter state is INCLUDE, {} as per the RFC. 463 * Pending state-changes per group are subject to a bounds check. 464 */ 465 inm = malloc(sizeof(*inm), M_IP6MADDR, M_NOWAIT | M_ZERO); 466 if (inm == NULL) { 467 if_delmulti_ifma(ifma); 468 error = ENOMEM; 469 goto out_locked; 470 } 471 inm->in6m_addr = *group; 472 inm->in6m_ifp = ifp; 473 inm->in6m_mli = MLD_IFINFO(ifp); 474 inm->in6m_ifma = ifma; 475 inm->in6m_refcount = 1; 476 inm->in6m_state = MLD_NOT_MEMBER; 477 IFQ_SET_MAXLEN(&inm->in6m_scq, MLD_MAX_STATE_CHANGES); 478 479 inm->in6m_st[0].iss_fmode = MCAST_UNDEFINED; 480 inm->in6m_st[1].iss_fmode = MCAST_UNDEFINED; 481 RB_INIT(&inm->in6m_srcs); 482 483 ifma->ifma_protospec = inm; 484 *pinm = inm; 485 486 out_locked: 487 IF_ADDR_UNLOCK(ifp); 488 return (error); 489 } 490 491 /* 492 * Drop a reference to an in6_multi record. 493 * 494 * If the refcount drops to 0, free the in6_multi record and 495 * delete the underlying link-layer membership. 496 */ 497 void 498 in6m_release_locked(struct in6_multi *inm) 499 { 500 struct ifmultiaddr *ifma; 501 502 IN6_MULTI_LOCK_ASSERT(); 503 504 CTR2(KTR_MLD, "%s: refcount is %d", __func__, inm->in6m_refcount); 505 506 if (--inm->in6m_refcount > 0) { 507 CTR2(KTR_MLD, "%s: refcount is now %d", __func__, 508 inm->in6m_refcount); 509 return; 510 } 511 512 CTR2(KTR_MLD, "%s: freeing inm %p", __func__, inm); 513 514 ifma = inm->in6m_ifma; 515 516 /* XXX this access is not covered by IF_ADDR_LOCK */ 517 CTR2(KTR_MLD, "%s: purging ifma %p", __func__, ifma); 518 KASSERT(ifma->ifma_protospec == inm, 519 ("%s: ifma_protospec != inm", __func__)); 520 ifma->ifma_protospec = NULL; 521 522 in6m_purge(inm); 523 524 free(inm, M_IP6MADDR); 525 526 if_delmulti_ifma(ifma); 527 } 528 529 /* 530 * Clear recorded source entries for a group. 531 * Used by the MLD code. Caller must hold the IN6_MULTI lock. 532 * FIXME: Should reap. 533 */ 534 void 535 in6m_clear_recorded(struct in6_multi *inm) 536 { 537 struct ip6_msource *ims; 538 539 IN6_MULTI_LOCK_ASSERT(); 540 541 RB_FOREACH(ims, ip6_msource_tree, &inm->in6m_srcs) { 542 if (ims->im6s_stp) { 543 ims->im6s_stp = 0; 544 --inm->in6m_st[1].iss_rec; 545 } 546 } 547 KASSERT(inm->in6m_st[1].iss_rec == 0, 548 ("%s: iss_rec %d not 0", __func__, inm->in6m_st[1].iss_rec)); 549 } 550 551 /* 552 * Record a source as pending for a Source-Group MLDv2 query. 553 * This lives here as it modifies the shared tree. 554 * 555 * inm is the group descriptor. 556 * naddr is the address of the source to record in network-byte order. 557 * 558 * If the net.inet6.mld.sgalloc sysctl is non-zero, we will 559 * lazy-allocate a source node in response to an SG query. 560 * Otherwise, no allocation is performed. This saves some memory 561 * with the trade-off that the source will not be reported to the 562 * router if joined in the window between the query response and 563 * the group actually being joined on the local host. 564 * 565 * VIMAGE: XXX: Currently the mld_sgalloc feature has been removed. 566 * This turns off the allocation of a recorded source entry if 567 * the group has not been joined. 568 * 569 * Return 0 if the source didn't exist or was already marked as recorded. 570 * Return 1 if the source was marked as recorded by this function. 571 * Return <0 if any error occured (negated errno code). 572 */ 573 int 574 in6m_record_source(struct in6_multi *inm, const struct in6_addr *addr) 575 { 576 struct ip6_msource find; 577 struct ip6_msource *ims, *nims; 578 579 IN6_MULTI_LOCK_ASSERT(); 580 581 find.im6s_addr = *addr; 582 ims = RB_FIND(ip6_msource_tree, &inm->in6m_srcs, &find); 583 if (ims && ims->im6s_stp) 584 return (0); 585 if (ims == NULL) { 586 if (inm->in6m_nsrc == in6_mcast_maxgrpsrc) 587 return (-ENOSPC); 588 nims = malloc(sizeof(struct ip6_msource), M_IP6MSOURCE, 589 M_NOWAIT | M_ZERO); 590 if (nims == NULL) 591 return (-ENOMEM); 592 nims->im6s_addr = find.im6s_addr; 593 RB_INSERT(ip6_msource_tree, &inm->in6m_srcs, nims); 594 ++inm->in6m_nsrc; 595 ims = nims; 596 } 597 598 /* 599 * Mark the source as recorded and update the recorded 600 * source count. 601 */ 602 ++ims->im6s_stp; 603 ++inm->in6m_st[1].iss_rec; 604 605 return (1); 606 } 607 608 /* 609 * Return a pointer to an in6_msource owned by an in6_mfilter, 610 * given its source address. 611 * Lazy-allocate if needed. If this is a new entry its filter state is 612 * undefined at t0. 613 * 614 * imf is the filter set being modified. 615 * addr is the source address. 616 * 617 * SMPng: May be called with locks held; malloc must not block. 618 */ 619 static int 620 im6f_get_source(struct in6_mfilter *imf, const struct sockaddr_in6 *psin, 621 struct in6_msource **plims) 622 { 623 struct ip6_msource find; 624 struct ip6_msource *ims, *nims; 625 struct in6_msource *lims; 626 int error; 627 628 error = 0; 629 ims = NULL; 630 lims = NULL; 631 632 find.im6s_addr = psin->sin6_addr; 633 ims = RB_FIND(ip6_msource_tree, &imf->im6f_sources, &find); 634 lims = (struct in6_msource *)ims; 635 if (lims == NULL) { 636 if (imf->im6f_nsrc == in6_mcast_maxsocksrc) 637 return (ENOSPC); 638 nims = malloc(sizeof(struct in6_msource), M_IN6MFILTER, 639 M_NOWAIT | M_ZERO); 640 if (nims == NULL) 641 return (ENOMEM); 642 lims = (struct in6_msource *)nims; 643 lims->im6s_addr = find.im6s_addr; 644 lims->im6sl_st[0] = MCAST_UNDEFINED; 645 RB_INSERT(ip6_msource_tree, &imf->im6f_sources, nims); 646 ++imf->im6f_nsrc; 647 } 648 649 *plims = lims; 650 651 return (error); 652 } 653 654 /* 655 * Graft a source entry into an existing socket-layer filter set, 656 * maintaining any required invariants and checking allocations. 657 * 658 * The source is marked as being in the new filter mode at t1. 659 * 660 * Return the pointer to the new node, otherwise return NULL. 661 */ 662 static struct in6_msource * 663 im6f_graft(struct in6_mfilter *imf, const uint8_t st1, 664 const struct sockaddr_in6 *psin) 665 { 666 struct ip6_msource *nims; 667 struct in6_msource *lims; 668 669 nims = malloc(sizeof(struct in6_msource), M_IN6MFILTER, 670 M_NOWAIT | M_ZERO); 671 if (nims == NULL) 672 return (NULL); 673 lims = (struct in6_msource *)nims; 674 lims->im6s_addr = psin->sin6_addr; 675 lims->im6sl_st[0] = MCAST_UNDEFINED; 676 lims->im6sl_st[1] = st1; 677 RB_INSERT(ip6_msource_tree, &imf->im6f_sources, nims); 678 ++imf->im6f_nsrc; 679 680 return (lims); 681 } 682 683 /* 684 * Prune a source entry from an existing socket-layer filter set, 685 * maintaining any required invariants and checking allocations. 686 * 687 * The source is marked as being left at t1, it is not freed. 688 * 689 * Return 0 if no error occurred, otherwise return an errno value. 690 */ 691 static int 692 im6f_prune(struct in6_mfilter *imf, const struct sockaddr_in6 *psin) 693 { 694 struct ip6_msource find; 695 struct ip6_msource *ims; 696 struct in6_msource *lims; 697 698 find.im6s_addr = psin->sin6_addr; 699 ims = RB_FIND(ip6_msource_tree, &imf->im6f_sources, &find); 700 if (ims == NULL) 701 return (ENOENT); 702 lims = (struct in6_msource *)ims; 703 lims->im6sl_st[1] = MCAST_UNDEFINED; 704 return (0); 705 } 706 707 /* 708 * Revert socket-layer filter set deltas at t1 to t0 state. 709 */ 710 static void 711 im6f_rollback(struct in6_mfilter *imf) 712 { 713 struct ip6_msource *ims, *tims; 714 struct in6_msource *lims; 715 716 RB_FOREACH_SAFE(ims, ip6_msource_tree, &imf->im6f_sources, tims) { 717 lims = (struct in6_msource *)ims; 718 if (lims->im6sl_st[0] == lims->im6sl_st[1]) { 719 /* no change at t1 */ 720 continue; 721 } else if (lims->im6sl_st[0] != MCAST_UNDEFINED) { 722 /* revert change to existing source at t1 */ 723 lims->im6sl_st[1] = lims->im6sl_st[0]; 724 } else { 725 /* revert source added t1 */ 726 CTR2(KTR_MLD, "%s: free ims %p", __func__, ims); 727 RB_REMOVE(ip6_msource_tree, &imf->im6f_sources, ims); 728 free(ims, M_IN6MFILTER); 729 imf->im6f_nsrc--; 730 } 731 } 732 imf->im6f_st[1] = imf->im6f_st[0]; 733 } 734 735 /* 736 * Mark socket-layer filter set as INCLUDE {} at t1. 737 */ 738 static void 739 im6f_leave(struct in6_mfilter *imf) 740 { 741 struct ip6_msource *ims; 742 struct in6_msource *lims; 743 744 RB_FOREACH(ims, ip6_msource_tree, &imf->im6f_sources) { 745 lims = (struct in6_msource *)ims; 746 lims->im6sl_st[1] = MCAST_UNDEFINED; 747 } 748 imf->im6f_st[1] = MCAST_INCLUDE; 749 } 750 751 /* 752 * Mark socket-layer filter set deltas as committed. 753 */ 754 static void 755 im6f_commit(struct in6_mfilter *imf) 756 { 757 struct ip6_msource *ims; 758 struct in6_msource *lims; 759 760 RB_FOREACH(ims, ip6_msource_tree, &imf->im6f_sources) { 761 lims = (struct in6_msource *)ims; 762 lims->im6sl_st[0] = lims->im6sl_st[1]; 763 } 764 imf->im6f_st[0] = imf->im6f_st[1]; 765 } 766 767 /* 768 * Reap unreferenced sources from socket-layer filter set. 769 */ 770 static void 771 im6f_reap(struct in6_mfilter *imf) 772 { 773 struct ip6_msource *ims, *tims; 774 struct in6_msource *lims; 775 776 RB_FOREACH_SAFE(ims, ip6_msource_tree, &imf->im6f_sources, tims) { 777 lims = (struct in6_msource *)ims; 778 if ((lims->im6sl_st[0] == MCAST_UNDEFINED) && 779 (lims->im6sl_st[1] == MCAST_UNDEFINED)) { 780 CTR2(KTR_MLD, "%s: free lims %p", __func__, ims); 781 RB_REMOVE(ip6_msource_tree, &imf->im6f_sources, ims); 782 free(ims, M_IN6MFILTER); 783 imf->im6f_nsrc--; 784 } 785 } 786 } 787 788 /* 789 * Purge socket-layer filter set. 790 */ 791 static void 792 im6f_purge(struct in6_mfilter *imf) 793 { 794 struct ip6_msource *ims, *tims; 795 796 RB_FOREACH_SAFE(ims, ip6_msource_tree, &imf->im6f_sources, tims) { 797 CTR2(KTR_MLD, "%s: free ims %p", __func__, ims); 798 RB_REMOVE(ip6_msource_tree, &imf->im6f_sources, ims); 799 free(ims, M_IN6MFILTER); 800 imf->im6f_nsrc--; 801 } 802 imf->im6f_st[0] = imf->im6f_st[1] = MCAST_UNDEFINED; 803 KASSERT(RB_EMPTY(&imf->im6f_sources), 804 ("%s: im6f_sources not empty", __func__)); 805 } 806 807 /* 808 * Look up a source filter entry for a multicast group. 809 * 810 * inm is the group descriptor to work with. 811 * addr is the IPv6 address to look up. 812 * noalloc may be non-zero to suppress allocation of sources. 813 * *pims will be set to the address of the retrieved or allocated source. 814 * 815 * SMPng: NOTE: may be called with locks held. 816 * Return 0 if successful, otherwise return a non-zero error code. 817 */ 818 static int 819 in6m_get_source(struct in6_multi *inm, const struct in6_addr *addr, 820 const int noalloc, struct ip6_msource **pims) 821 { 822 struct ip6_msource find; 823 struct ip6_msource *ims, *nims; 824 #ifdef KTR 825 char ip6tbuf[INET6_ADDRSTRLEN]; 826 #endif 827 828 find.im6s_addr = *addr; 829 ims = RB_FIND(ip6_msource_tree, &inm->in6m_srcs, &find); 830 if (ims == NULL && !noalloc) { 831 if (inm->in6m_nsrc == in6_mcast_maxgrpsrc) 832 return (ENOSPC); 833 nims = malloc(sizeof(struct ip6_msource), M_IP6MSOURCE, 834 M_NOWAIT | M_ZERO); 835 if (nims == NULL) 836 return (ENOMEM); 837 nims->im6s_addr = *addr; 838 RB_INSERT(ip6_msource_tree, &inm->in6m_srcs, nims); 839 ++inm->in6m_nsrc; 840 ims = nims; 841 CTR3(KTR_MLD, "%s: allocated %s as %p", __func__, 842 ip6_sprintf(ip6tbuf, addr), ims); 843 } 844 845 *pims = ims; 846 return (0); 847 } 848 849 /* 850 * Merge socket-layer source into MLD-layer source. 851 * If rollback is non-zero, perform the inverse of the merge. 852 */ 853 static void 854 im6s_merge(struct ip6_msource *ims, const struct in6_msource *lims, 855 const int rollback) 856 { 857 int n = rollback ? -1 : 1; 858 #ifdef KTR 859 char ip6tbuf[INET6_ADDRSTRLEN]; 860 861 ip6_sprintf(ip6tbuf, &lims->im6s_addr); 862 #endif 863 864 if (lims->im6sl_st[0] == MCAST_EXCLUDE) { 865 CTR3(KTR_MLD, "%s: t1 ex -= %d on %s", __func__, n, ip6tbuf); 866 ims->im6s_st[1].ex -= n; 867 } else if (lims->im6sl_st[0] == MCAST_INCLUDE) { 868 CTR3(KTR_MLD, "%s: t1 in -= %d on %s", __func__, n, ip6tbuf); 869 ims->im6s_st[1].in -= n; 870 } 871 872 if (lims->im6sl_st[1] == MCAST_EXCLUDE) { 873 CTR3(KTR_MLD, "%s: t1 ex += %d on %s", __func__, n, ip6tbuf); 874 ims->im6s_st[1].ex += n; 875 } else if (lims->im6sl_st[1] == MCAST_INCLUDE) { 876 CTR3(KTR_MLD, "%s: t1 in += %d on %s", __func__, n, ip6tbuf); 877 ims->im6s_st[1].in += n; 878 } 879 } 880 881 /* 882 * Atomically update the global in6_multi state, when a membership's 883 * filter list is being updated in any way. 884 * 885 * imf is the per-inpcb-membership group filter pointer. 886 * A fake imf may be passed for in-kernel consumers. 887 * 888 * XXX This is a candidate for a set-symmetric-difference style loop 889 * which would eliminate the repeated lookup from root of ims nodes, 890 * as they share the same key space. 891 * 892 * If any error occurred this function will back out of refcounts 893 * and return a non-zero value. 894 */ 895 static int 896 in6m_merge(struct in6_multi *inm, /*const*/ struct in6_mfilter *imf) 897 { 898 struct ip6_msource *ims, *nims; 899 struct in6_msource *lims; 900 int schanged, error; 901 int nsrc0, nsrc1; 902 903 schanged = 0; 904 error = 0; 905 nsrc1 = nsrc0 = 0; 906 907 /* 908 * Update the source filters first, as this may fail. 909 * Maintain count of in-mode filters at t0, t1. These are 910 * used to work out if we transition into ASM mode or not. 911 * Maintain a count of source filters whose state was 912 * actually modified by this operation. 913 */ 914 RB_FOREACH(ims, ip6_msource_tree, &imf->im6f_sources) { 915 lims = (struct in6_msource *)ims; 916 if (lims->im6sl_st[0] == imf->im6f_st[0]) nsrc0++; 917 if (lims->im6sl_st[1] == imf->im6f_st[1]) nsrc1++; 918 if (lims->im6sl_st[0] == lims->im6sl_st[1]) continue; 919 error = in6m_get_source(inm, &lims->im6s_addr, 0, &nims); 920 ++schanged; 921 if (error) 922 break; 923 im6s_merge(nims, lims, 0); 924 } 925 if (error) { 926 struct ip6_msource *bims; 927 928 RB_FOREACH_REVERSE_FROM(ims, ip6_msource_tree, nims) { 929 lims = (struct in6_msource *)ims; 930 if (lims->im6sl_st[0] == lims->im6sl_st[1]) 931 continue; 932 (void)in6m_get_source(inm, &lims->im6s_addr, 1, &bims); 933 if (bims == NULL) 934 continue; 935 im6s_merge(bims, lims, 1); 936 } 937 goto out_reap; 938 } 939 940 CTR3(KTR_MLD, "%s: imf filters in-mode: %d at t0, %d at t1", 941 __func__, nsrc0, nsrc1); 942 943 /* Handle transition between INCLUDE {n} and INCLUDE {} on socket. */ 944 if (imf->im6f_st[0] == imf->im6f_st[1] && 945 imf->im6f_st[1] == MCAST_INCLUDE) { 946 if (nsrc1 == 0) { 947 CTR1(KTR_MLD, "%s: --in on inm at t1", __func__); 948 --inm->in6m_st[1].iss_in; 949 } 950 } 951 952 /* Handle filter mode transition on socket. */ 953 if (imf->im6f_st[0] != imf->im6f_st[1]) { 954 CTR3(KTR_MLD, "%s: imf transition %d to %d", 955 __func__, imf->im6f_st[0], imf->im6f_st[1]); 956 957 if (imf->im6f_st[0] == MCAST_EXCLUDE) { 958 CTR1(KTR_MLD, "%s: --ex on inm at t1", __func__); 959 --inm->in6m_st[1].iss_ex; 960 } else if (imf->im6f_st[0] == MCAST_INCLUDE) { 961 CTR1(KTR_MLD, "%s: --in on inm at t1", __func__); 962 --inm->in6m_st[1].iss_in; 963 } 964 965 if (imf->im6f_st[1] == MCAST_EXCLUDE) { 966 CTR1(KTR_MLD, "%s: ex++ on inm at t1", __func__); 967 inm->in6m_st[1].iss_ex++; 968 } else if (imf->im6f_st[1] == MCAST_INCLUDE && nsrc1 > 0) { 969 CTR1(KTR_MLD, "%s: in++ on inm at t1", __func__); 970 inm->in6m_st[1].iss_in++; 971 } 972 } 973 974 /* 975 * Track inm filter state in terms of listener counts. 976 * If there are any exclusive listeners, stack-wide 977 * membership is exclusive. 978 * Otherwise, if only inclusive listeners, stack-wide is inclusive. 979 * If no listeners remain, state is undefined at t1, 980 * and the MLD lifecycle for this group should finish. 981 */ 982 if (inm->in6m_st[1].iss_ex > 0) { 983 CTR1(KTR_MLD, "%s: transition to EX", __func__); 984 inm->in6m_st[1].iss_fmode = MCAST_EXCLUDE; 985 } else if (inm->in6m_st[1].iss_in > 0) { 986 CTR1(KTR_MLD, "%s: transition to IN", __func__); 987 inm->in6m_st[1].iss_fmode = MCAST_INCLUDE; 988 } else { 989 CTR1(KTR_MLD, "%s: transition to UNDEF", __func__); 990 inm->in6m_st[1].iss_fmode = MCAST_UNDEFINED; 991 } 992 993 /* Decrement ASM listener count on transition out of ASM mode. */ 994 if (imf->im6f_st[0] == MCAST_EXCLUDE && nsrc0 == 0) { 995 if ((imf->im6f_st[1] != MCAST_EXCLUDE) || 996 (imf->im6f_st[1] == MCAST_EXCLUDE && nsrc1 > 0)) 997 CTR1(KTR_MLD, "%s: --asm on inm at t1", __func__); 998 --inm->in6m_st[1].iss_asm; 999 } 1000 1001 /* Increment ASM listener count on transition to ASM mode. */ 1002 if (imf->im6f_st[1] == MCAST_EXCLUDE && nsrc1 == 0) { 1003 CTR1(KTR_MLD, "%s: asm++ on inm at t1", __func__); 1004 inm->in6m_st[1].iss_asm++; 1005 } 1006 1007 CTR3(KTR_MLD, "%s: merged imf %p to inm %p", __func__, imf, inm); 1008 in6m_print(inm); 1009 1010 out_reap: 1011 if (schanged > 0) { 1012 CTR1(KTR_MLD, "%s: sources changed; reaping", __func__); 1013 in6m_reap(inm); 1014 } 1015 return (error); 1016 } 1017 1018 /* 1019 * Mark an in6_multi's filter set deltas as committed. 1020 * Called by MLD after a state change has been enqueued. 1021 */ 1022 void 1023 in6m_commit(struct in6_multi *inm) 1024 { 1025 struct ip6_msource *ims; 1026 1027 CTR2(KTR_MLD, "%s: commit inm %p", __func__, inm); 1028 CTR1(KTR_MLD, "%s: pre commit:", __func__); 1029 in6m_print(inm); 1030 1031 RB_FOREACH(ims, ip6_msource_tree, &inm->in6m_srcs) { 1032 ims->im6s_st[0] = ims->im6s_st[1]; 1033 } 1034 inm->in6m_st[0] = inm->in6m_st[1]; 1035 } 1036 1037 /* 1038 * Reap unreferenced nodes from an in6_multi's filter set. 1039 */ 1040 static void 1041 in6m_reap(struct in6_multi *inm) 1042 { 1043 struct ip6_msource *ims, *tims; 1044 1045 RB_FOREACH_SAFE(ims, ip6_msource_tree, &inm->in6m_srcs, tims) { 1046 if (ims->im6s_st[0].ex > 0 || ims->im6s_st[0].in > 0 || 1047 ims->im6s_st[1].ex > 0 || ims->im6s_st[1].in > 0 || 1048 ims->im6s_stp != 0) 1049 continue; 1050 CTR2(KTR_MLD, "%s: free ims %p", __func__, ims); 1051 RB_REMOVE(ip6_msource_tree, &inm->in6m_srcs, ims); 1052 free(ims, M_IP6MSOURCE); 1053 inm->in6m_nsrc--; 1054 } 1055 } 1056 1057 /* 1058 * Purge all source nodes from an in6_multi's filter set. 1059 */ 1060 static void 1061 in6m_purge(struct in6_multi *inm) 1062 { 1063 struct ip6_msource *ims, *tims; 1064 1065 RB_FOREACH_SAFE(ims, ip6_msource_tree, &inm->in6m_srcs, tims) { 1066 CTR2(KTR_MLD, "%s: free ims %p", __func__, ims); 1067 RB_REMOVE(ip6_msource_tree, &inm->in6m_srcs, ims); 1068 free(ims, M_IP6MSOURCE); 1069 inm->in6m_nsrc--; 1070 } 1071 } 1072 1073 /* 1074 * Join a multicast address w/o sources. 1075 * KAME compatibility entry point. 1076 * 1077 * SMPng: Assume no mc locks held by caller. 1078 */ 1079 struct in6_multi_mship * 1080 in6_joingroup(struct ifnet *ifp, struct in6_addr *mcaddr, 1081 int *errorp, int delay) 1082 { 1083 struct in6_multi_mship *imm; 1084 int error; 1085 1086 imm = malloc(sizeof(*imm), M_IP6MADDR, M_NOWAIT); 1087 if (imm == NULL) { 1088 *errorp = ENOBUFS; 1089 return (NULL); 1090 } 1091 1092 delay = (delay * PR_FASTHZ) / hz; 1093 1094 error = in6_mc_join(ifp, mcaddr, NULL, &imm->i6mm_maddr, delay); 1095 if (error) { 1096 *errorp = error; 1097 free(imm, M_IP6MADDR); 1098 return (NULL); 1099 } 1100 1101 return (imm); 1102 } 1103 1104 /* 1105 * Leave a multicast address w/o sources. 1106 * KAME compatibility entry point. 1107 * 1108 * SMPng: Assume no mc locks held by caller. 1109 */ 1110 int 1111 in6_leavegroup(struct in6_multi_mship *imm) 1112 { 1113 1114 if (imm->i6mm_maddr != NULL) 1115 in6_mc_leave(imm->i6mm_maddr, NULL); 1116 free(imm, M_IP6MADDR); 1117 return 0; 1118 } 1119 1120 /* 1121 * Join a multicast group; unlocked entry point. 1122 * 1123 * SMPng: XXX: in6_mc_join() is called from in6_control() when upper 1124 * locks are not held. Fortunately, ifp is unlikely to have been detached 1125 * at this point, so we assume it's OK to recurse. 1126 */ 1127 int 1128 in6_mc_join(struct ifnet *ifp, const struct in6_addr *mcaddr, 1129 /*const*/ struct in6_mfilter *imf, struct in6_multi **pinm, 1130 const int delay) 1131 { 1132 int error; 1133 1134 IN6_MULTI_LOCK(); 1135 error = in6_mc_join_locked(ifp, mcaddr, imf, pinm, delay); 1136 IN6_MULTI_UNLOCK(); 1137 1138 return (error); 1139 } 1140 1141 /* 1142 * Join a multicast group; real entry point. 1143 * 1144 * Only preserves atomicity at inm level. 1145 * NOTE: imf argument cannot be const due to sys/tree.h limitations. 1146 * 1147 * If the MLD downcall fails, the group is not joined, and an error 1148 * code is returned. 1149 */ 1150 int 1151 in6_mc_join_locked(struct ifnet *ifp, const struct in6_addr *mcaddr, 1152 /*const*/ struct in6_mfilter *imf, struct in6_multi **pinm, 1153 const int delay) 1154 { 1155 struct in6_mfilter timf; 1156 struct in6_multi *inm; 1157 int error; 1158 #ifdef KTR 1159 char ip6tbuf[INET6_ADDRSTRLEN]; 1160 #endif 1161 1162 IN6_MULTI_LOCK_ASSERT(); 1163 1164 CTR4(KTR_MLD, "%s: join %s on %p(%s))", __func__, 1165 ip6_sprintf(ip6tbuf, mcaddr), ifp, ifp->if_xname); 1166 1167 error = 0; 1168 inm = NULL; 1169 1170 /* 1171 * If no imf was specified (i.e. kernel consumer), 1172 * fake one up and assume it is an ASM join. 1173 */ 1174 if (imf == NULL) { 1175 im6f_init(&timf, MCAST_UNDEFINED, MCAST_EXCLUDE); 1176 imf = &timf; 1177 } 1178 1179 error = in6_mc_get(ifp, mcaddr, &inm); 1180 if (error) { 1181 CTR1(KTR_MLD, "%s: in6_mc_get() failure", __func__); 1182 return (error); 1183 } 1184 1185 CTR1(KTR_MLD, "%s: merge inm state", __func__); 1186 error = in6m_merge(inm, imf); 1187 if (error) { 1188 CTR1(KTR_MLD, "%s: failed to merge inm state", __func__); 1189 goto out_in6m_release; 1190 } 1191 1192 CTR1(KTR_MLD, "%s: doing mld downcall", __func__); 1193 error = mld_change_state(inm, delay); 1194 if (error) { 1195 CTR1(KTR_MLD, "%s: failed to update source", __func__); 1196 goto out_in6m_release; 1197 } 1198 1199 out_in6m_release: 1200 if (error) { 1201 CTR2(KTR_MLD, "%s: dropping ref on %p", __func__, inm); 1202 in6m_release_locked(inm); 1203 } else { 1204 *pinm = inm; 1205 } 1206 1207 return (error); 1208 } 1209 1210 /* 1211 * Leave a multicast group; unlocked entry point. 1212 */ 1213 int 1214 in6_mc_leave(struct in6_multi *inm, /*const*/ struct in6_mfilter *imf) 1215 { 1216 struct ifnet *ifp; 1217 int error; 1218 1219 ifp = inm->in6m_ifp; 1220 1221 IN6_MULTI_LOCK(); 1222 error = in6_mc_leave_locked(inm, imf); 1223 IN6_MULTI_UNLOCK(); 1224 1225 return (error); 1226 } 1227 1228 /* 1229 * Leave a multicast group; real entry point. 1230 * All source filters will be expunged. 1231 * 1232 * Only preserves atomicity at inm level. 1233 * 1234 * Holding the write lock for the INP which contains imf 1235 * is highly advisable. We can't assert for it as imf does not 1236 * contain a back-pointer to the owning inp. 1237 * 1238 * Note: This is not the same as in6m_release(*) as this function also 1239 * makes a state change downcall into MLD. 1240 */ 1241 int 1242 in6_mc_leave_locked(struct in6_multi *inm, /*const*/ struct in6_mfilter *imf) 1243 { 1244 struct in6_mfilter timf; 1245 int error; 1246 #ifdef KTR 1247 char ip6tbuf[INET6_ADDRSTRLEN]; 1248 #endif 1249 1250 error = 0; 1251 1252 IN6_MULTI_LOCK_ASSERT(); 1253 1254 CTR5(KTR_MLD, "%s: leave inm %p, %s/%s, imf %p", __func__, 1255 inm, ip6_sprintf(ip6tbuf, &inm->in6m_addr), 1256 (in6m_is_ifp_detached(inm) ? "null" : inm->in6m_ifp->if_xname), 1257 imf); 1258 1259 /* 1260 * If no imf was specified (i.e. kernel consumer), 1261 * fake one up and assume it is an ASM join. 1262 */ 1263 if (imf == NULL) { 1264 im6f_init(&timf, MCAST_EXCLUDE, MCAST_UNDEFINED); 1265 imf = &timf; 1266 } 1267 1268 /* 1269 * Begin state merge transaction at MLD layer. 1270 * 1271 * As this particular invocation should not cause any memory 1272 * to be allocated, and there is no opportunity to roll back 1273 * the transaction, it MUST NOT fail. 1274 */ 1275 CTR1(KTR_MLD, "%s: merge inm state", __func__); 1276 error = in6m_merge(inm, imf); 1277 KASSERT(error == 0, ("%s: failed to merge inm state", __func__)); 1278 1279 CTR1(KTR_MLD, "%s: doing mld downcall", __func__); 1280 error = mld_change_state(inm, 0); 1281 if (error) 1282 CTR1(KTR_MLD, "%s: failed mld downcall", __func__); 1283 1284 CTR2(KTR_MLD, "%s: dropping ref on %p", __func__, inm); 1285 in6m_release_locked(inm); 1286 1287 return (error); 1288 } 1289 1290 /* 1291 * Block or unblock an ASM multicast source on an inpcb. 1292 * This implements the delta-based API described in RFC 3678. 1293 * 1294 * The delta-based API applies only to exclusive-mode memberships. 1295 * An MLD downcall will be performed. 1296 * 1297 * SMPng: NOTE: Must take Giant as a join may create a new ifma. 1298 * 1299 * Return 0 if successful, otherwise return an appropriate error code. 1300 */ 1301 static int 1302 in6p_block_unblock_source(struct inpcb *inp, struct sockopt *sopt) 1303 { 1304 INIT_VNET_NET(curvnet); 1305 struct group_source_req gsr; 1306 sockunion_t *gsa, *ssa; 1307 struct ifnet *ifp; 1308 struct in6_mfilter *imf; 1309 struct ip6_moptions *imo; 1310 struct in6_msource *ims; 1311 struct in6_multi *inm; 1312 size_t idx; 1313 uint16_t fmode; 1314 int error, doblock; 1315 #ifdef KTR 1316 char ip6tbuf[INET6_ADDRSTRLEN]; 1317 #endif 1318 1319 ifp = NULL; 1320 error = 0; 1321 doblock = 0; 1322 1323 memset(&gsr, 0, sizeof(struct group_source_req)); 1324 gsa = (sockunion_t *)&gsr.gsr_group; 1325 ssa = (sockunion_t *)&gsr.gsr_source; 1326 1327 switch (sopt->sopt_name) { 1328 case MCAST_BLOCK_SOURCE: 1329 case MCAST_UNBLOCK_SOURCE: 1330 error = sooptcopyin(sopt, &gsr, 1331 sizeof(struct group_source_req), 1332 sizeof(struct group_source_req)); 1333 if (error) 1334 return (error); 1335 1336 if (gsa->sin6.sin6_family != AF_INET6 || 1337 gsa->sin6.sin6_len != sizeof(struct sockaddr_in6)) 1338 return (EINVAL); 1339 1340 if (ssa->sin6.sin6_family != AF_INET6 || 1341 ssa->sin6.sin6_len != sizeof(struct sockaddr_in6)) 1342 return (EINVAL); 1343 1344 if (gsr.gsr_interface == 0 || V_if_index < gsr.gsr_interface) 1345 return (EADDRNOTAVAIL); 1346 1347 ifp = ifnet_byindex(gsr.gsr_interface); 1348 1349 if (sopt->sopt_name == MCAST_BLOCK_SOURCE) 1350 doblock = 1; 1351 break; 1352 1353 default: 1354 CTR2(KTR_MLD, "%s: unknown sopt_name %d", 1355 __func__, sopt->sopt_name); 1356 return (EOPNOTSUPP); 1357 break; 1358 } 1359 1360 if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr)) 1361 return (EINVAL); 1362 1363 /* 1364 * Check if we are actually a member of this group. 1365 */ 1366 imo = in6p_findmoptions(inp); 1367 idx = im6o_match_group(imo, ifp, &gsa->sa); 1368 if (idx == -1 || imo->im6o_mfilters == NULL) { 1369 error = EADDRNOTAVAIL; 1370 goto out_in6p_locked; 1371 } 1372 1373 KASSERT(imo->im6o_mfilters != NULL, 1374 ("%s: im6o_mfilters not allocated", __func__)); 1375 imf = &imo->im6o_mfilters[idx]; 1376 inm = imo->im6o_membership[idx]; 1377 1378 /* 1379 * Attempting to use the delta-based API on an 1380 * non exclusive-mode membership is an error. 1381 */ 1382 fmode = imf->im6f_st[0]; 1383 if (fmode != MCAST_EXCLUDE) { 1384 error = EINVAL; 1385 goto out_in6p_locked; 1386 } 1387 1388 /* 1389 * Deal with error cases up-front: 1390 * Asked to block, but already blocked; or 1391 * Asked to unblock, but nothing to unblock. 1392 * If adding a new block entry, allocate it. 1393 */ 1394 ims = im6o_match_source(imo, idx, &ssa->sa); 1395 if ((ims != NULL && doblock) || (ims == NULL && !doblock)) { 1396 CTR3(KTR_MLD, "%s: source %s %spresent", __func__, 1397 ip6_sprintf(ip6tbuf, &ssa->sin6.sin6_addr), 1398 doblock ? "" : "not "); 1399 error = EADDRNOTAVAIL; 1400 goto out_in6p_locked; 1401 } 1402 1403 INP_WLOCK_ASSERT(inp); 1404 1405 /* 1406 * Begin state merge transaction at socket layer. 1407 */ 1408 if (doblock) { 1409 CTR2(KTR_MLD, "%s: %s source", __func__, "block"); 1410 ims = im6f_graft(imf, fmode, &ssa->sin6); 1411 if (ims == NULL) 1412 error = ENOMEM; 1413 } else { 1414 CTR2(KTR_MLD, "%s: %s source", __func__, "allow"); 1415 error = im6f_prune(imf, &ssa->sin6); 1416 } 1417 1418 if (error) { 1419 CTR1(KTR_MLD, "%s: merge imf state failed", __func__); 1420 goto out_im6f_rollback; 1421 } 1422 1423 /* 1424 * Begin state merge transaction at MLD layer. 1425 */ 1426 IN6_MULTI_LOCK(); 1427 1428 CTR1(KTR_MLD, "%s: merge inm state", __func__); 1429 error = in6m_merge(inm, imf); 1430 if (error) { 1431 CTR1(KTR_MLD, "%s: failed to merge inm state", __func__); 1432 goto out_im6f_rollback; 1433 } 1434 1435 CTR1(KTR_MLD, "%s: doing mld downcall", __func__); 1436 error = mld_change_state(inm, 0); 1437 if (error) 1438 CTR1(KTR_MLD, "%s: failed mld downcall", __func__); 1439 1440 IN6_MULTI_UNLOCK(); 1441 1442 out_im6f_rollback: 1443 if (error) 1444 im6f_rollback(imf); 1445 else 1446 im6f_commit(imf); 1447 1448 im6f_reap(imf); 1449 1450 out_in6p_locked: 1451 INP_WUNLOCK(inp); 1452 return (error); 1453 } 1454 1455 /* 1456 * Given an inpcb, return its multicast options structure pointer. Accepts 1457 * an unlocked inpcb pointer, but will return it locked. May sleep. 1458 * 1459 * SMPng: NOTE: Potentially calls malloc(M_WAITOK) with Giant held. 1460 * SMPng: NOTE: Returns with the INP write lock held. 1461 */ 1462 static struct ip6_moptions * 1463 in6p_findmoptions(struct inpcb *inp) 1464 { 1465 INIT_VNET_INET6(curvnet); 1466 struct ip6_moptions *imo; 1467 struct in6_multi **immp; 1468 struct in6_mfilter *imfp; 1469 size_t idx; 1470 1471 INP_WLOCK(inp); 1472 if (inp->in6p_moptions != NULL) 1473 return (inp->in6p_moptions); 1474 1475 INP_WUNLOCK(inp); 1476 1477 imo = malloc(sizeof(*imo), M_IP6MOPTS, M_WAITOK); 1478 immp = malloc(sizeof(*immp) * IPV6_MIN_MEMBERSHIPS, M_IP6MOPTS, 1479 M_WAITOK | M_ZERO); 1480 imfp = malloc(sizeof(struct in6_mfilter) * IPV6_MIN_MEMBERSHIPS, 1481 M_IN6MFILTER, M_WAITOK); 1482 1483 imo->im6o_multicast_ifp = NULL; 1484 imo->im6o_multicast_hlim = V_ip6_defmcasthlim; 1485 imo->im6o_multicast_loop = in6_mcast_loop; 1486 imo->im6o_num_memberships = 0; 1487 imo->im6o_max_memberships = IPV6_MIN_MEMBERSHIPS; 1488 imo->im6o_membership = immp; 1489 1490 /* Initialize per-group source filters. */ 1491 for (idx = 0; idx < IPV6_MIN_MEMBERSHIPS; idx++) 1492 im6f_init(&imfp[idx], MCAST_UNDEFINED, MCAST_EXCLUDE); 1493 imo->im6o_mfilters = imfp; 1494 1495 INP_WLOCK(inp); 1496 if (inp->in6p_moptions != NULL) { 1497 free(imfp, M_IN6MFILTER); 1498 free(immp, M_IP6MOPTS); 1499 free(imo, M_IP6MOPTS); 1500 return (inp->in6p_moptions); 1501 } 1502 inp->in6p_moptions = imo; 1503 return (imo); 1504 } 1505 1506 /* 1507 * Discard the IPv6 multicast options (and source filters). 1508 * 1509 * SMPng: NOTE: assumes INP write lock is held. 1510 */ 1511 void 1512 ip6_freemoptions(struct ip6_moptions *imo) 1513 { 1514 struct in6_mfilter *imf; 1515 size_t idx, nmships; 1516 1517 KASSERT(imo != NULL, ("%s: ip6_moptions is NULL", __func__)); 1518 1519 nmships = imo->im6o_num_memberships; 1520 for (idx = 0; idx < nmships; ++idx) { 1521 imf = imo->im6o_mfilters ? &imo->im6o_mfilters[idx] : NULL; 1522 if (imf) 1523 im6f_leave(imf); 1524 /* XXX this will thrash the lock(s) */ 1525 (void)in6_mc_leave(imo->im6o_membership[idx], imf); 1526 if (imf) 1527 im6f_purge(imf); 1528 } 1529 1530 if (imo->im6o_mfilters) 1531 free(imo->im6o_mfilters, M_IN6MFILTER); 1532 free(imo->im6o_membership, M_IP6MOPTS); 1533 free(imo, M_IP6MOPTS); 1534 } 1535 1536 /* 1537 * Atomically get source filters on a socket for an IPv6 multicast group. 1538 * Called with INP lock held; returns with lock released. 1539 */ 1540 static int 1541 in6p_get_source_filters(struct inpcb *inp, struct sockopt *sopt) 1542 { 1543 INIT_VNET_NET(curvnet); 1544 struct __msfilterreq msfr; 1545 sockunion_t *gsa; 1546 struct ifnet *ifp; 1547 struct ip6_moptions *imo; 1548 struct in6_mfilter *imf; 1549 struct ip6_msource *ims; 1550 struct in6_msource *lims; 1551 struct sockaddr_in6 *psin; 1552 struct sockaddr_storage *ptss; 1553 struct sockaddr_storage *tss; 1554 int error; 1555 size_t idx, nsrcs, ncsrcs; 1556 1557 INP_WLOCK_ASSERT(inp); 1558 1559 imo = inp->in6p_moptions; 1560 KASSERT(imo != NULL, ("%s: null ip6_moptions", __func__)); 1561 1562 INP_WUNLOCK(inp); 1563 1564 error = sooptcopyin(sopt, &msfr, sizeof(struct __msfilterreq), 1565 sizeof(struct __msfilterreq)); 1566 if (error) 1567 return (error); 1568 1569 if (msfr.msfr_ifindex == 0 || V_if_index < msfr.msfr_ifindex) 1570 return (EINVAL); 1571 1572 ifp = ifnet_byindex(msfr.msfr_ifindex); 1573 if (ifp == NULL) 1574 return (EINVAL); 1575 1576 INP_WLOCK(inp); 1577 1578 /* 1579 * Lookup group on the socket. 1580 */ 1581 gsa = (sockunion_t *)&msfr.msfr_group; 1582 idx = im6o_match_group(imo, ifp, &gsa->sa); 1583 if (idx == -1 || imo->im6o_mfilters == NULL) { 1584 INP_WUNLOCK(inp); 1585 return (EADDRNOTAVAIL); 1586 } 1587 imf = &imo->im6o_mfilters[idx]; 1588 1589 /* 1590 * Ignore memberships which are in limbo. 1591 */ 1592 if (imf->im6f_st[1] == MCAST_UNDEFINED) { 1593 INP_WUNLOCK(inp); 1594 return (EAGAIN); 1595 } 1596 msfr.msfr_fmode = imf->im6f_st[1]; 1597 1598 /* 1599 * If the user specified a buffer, copy out the source filter 1600 * entries to userland gracefully. 1601 * We only copy out the number of entries which userland 1602 * has asked for, but we always tell userland how big the 1603 * buffer really needs to be. 1604 */ 1605 tss = NULL; 1606 if (msfr.msfr_srcs != NULL && msfr.msfr_nsrcs > 0) { 1607 tss = malloc(sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs, 1608 M_TEMP, M_NOWAIT | M_ZERO); 1609 if (tss == NULL) { 1610 INP_WUNLOCK(inp); 1611 return (ENOBUFS); 1612 } 1613 } 1614 1615 /* 1616 * Count number of sources in-mode at t0. 1617 * If buffer space exists and remains, copy out source entries. 1618 */ 1619 nsrcs = msfr.msfr_nsrcs; 1620 ncsrcs = 0; 1621 ptss = tss; 1622 RB_FOREACH(ims, ip6_msource_tree, &imf->im6f_sources) { 1623 lims = (struct in6_msource *)ims; 1624 if (lims->im6sl_st[0] == MCAST_UNDEFINED || 1625 lims->im6sl_st[0] != imf->im6f_st[0]) 1626 continue; 1627 ++ncsrcs; 1628 if (tss != NULL && nsrcs > 0) { 1629 psin = (struct sockaddr_in6 *)ptss; 1630 psin->sin6_family = AF_INET6; 1631 psin->sin6_len = sizeof(struct sockaddr_in6); 1632 psin->sin6_addr = lims->im6s_addr; 1633 psin->sin6_port = 0; 1634 --nsrcs; 1635 ++ptss; 1636 } 1637 } 1638 1639 INP_WUNLOCK(inp); 1640 1641 if (tss != NULL) { 1642 error = copyout(tss, msfr.msfr_srcs, 1643 sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs); 1644 free(tss, M_TEMP); 1645 if (error) 1646 return (error); 1647 } 1648 1649 msfr.msfr_nsrcs = ncsrcs; 1650 error = sooptcopyout(sopt, &msfr, sizeof(struct __msfilterreq)); 1651 1652 return (error); 1653 } 1654 1655 /* 1656 * Return the IP multicast options in response to user getsockopt(). 1657 */ 1658 int 1659 ip6_getmoptions(struct inpcb *inp, struct sockopt *sopt) 1660 { 1661 INIT_VNET_INET6(curvnet); 1662 struct ip6_moptions *im6o; 1663 int error; 1664 u_int optval; 1665 1666 INP_WLOCK(inp); 1667 im6o = inp->in6p_moptions; 1668 /* 1669 * If socket is neither of type SOCK_RAW or SOCK_DGRAM, 1670 * or is a divert socket, reject it. 1671 */ 1672 if (inp->inp_socket->so_proto->pr_protocol == IPPROTO_DIVERT || 1673 (inp->inp_socket->so_proto->pr_type != SOCK_RAW && 1674 inp->inp_socket->so_proto->pr_type != SOCK_DGRAM)) { 1675 INP_WUNLOCK(inp); 1676 return (EOPNOTSUPP); 1677 } 1678 1679 error = 0; 1680 switch (sopt->sopt_name) { 1681 case IPV6_MULTICAST_IF: 1682 if (im6o == NULL || im6o->im6o_multicast_ifp == NULL) { 1683 optval = 0; 1684 } else { 1685 optval = im6o->im6o_multicast_ifp->if_index; 1686 } 1687 INP_WUNLOCK(inp); 1688 error = sooptcopyout(sopt, &optval, sizeof(u_int)); 1689 break; 1690 1691 case IPV6_MULTICAST_HOPS: 1692 if (im6o == NULL) 1693 optval = V_ip6_defmcasthlim; 1694 else 1695 optval = im6o->im6o_multicast_loop; 1696 INP_WUNLOCK(inp); 1697 error = sooptcopyout(sopt, &optval, sizeof(u_int)); 1698 break; 1699 1700 case IPV6_MULTICAST_LOOP: 1701 if (im6o == NULL) 1702 optval = in6_mcast_loop; /* XXX VIMAGE */ 1703 else 1704 optval = im6o->im6o_multicast_loop; 1705 INP_WUNLOCK(inp); 1706 error = sooptcopyout(sopt, &optval, sizeof(u_int)); 1707 break; 1708 1709 case IPV6_MSFILTER: 1710 if (im6o == NULL) { 1711 error = EADDRNOTAVAIL; 1712 INP_WUNLOCK(inp); 1713 } else { 1714 error = in6p_get_source_filters(inp, sopt); 1715 } 1716 break; 1717 1718 default: 1719 INP_WUNLOCK(inp); 1720 error = ENOPROTOOPT; 1721 break; 1722 } 1723 1724 INP_UNLOCK_ASSERT(inp); 1725 1726 return (error); 1727 } 1728 1729 /* 1730 * Look up the ifnet to use for a multicast group membership, 1731 * given the address of an IPv6 group. 1732 * 1733 * This routine exists to support legacy IPv6 multicast applications. 1734 * 1735 * If inp is non-NULL, use this socket's current FIB number for any 1736 * required FIB lookup. Look up the group address in the unicast FIB, 1737 * and use its ifp; usually, this points to the default next-hop. 1738 * If the FIB lookup fails, return NULL. 1739 * 1740 * FUTURE: Support multiple forwarding tables for IPv6. 1741 * 1742 * Returns NULL if no ifp could be found. 1743 */ 1744 static struct ifnet * 1745 in6p_lookup_mcast_ifp(const struct inpcb *in6p __unused, 1746 const struct sockaddr_in6 *gsin6) 1747 { 1748 struct route_in6 ro6; 1749 struct ifnet *ifp; 1750 1751 KASSERT(in6p->inp_vflag & INP_IPV6, 1752 ("%s: not INP_IPV6 inpcb", __func__)); 1753 KASSERT(gsin6->sin6_family == AF_INET6, 1754 ("%s: not AF_INET6 group", __func__)); 1755 KASSERT(IN6_IS_ADDR_MULTICAST(&gsin6->sin6_addr), 1756 ("%s: not multicast", __func__)); 1757 1758 ifp = NULL; 1759 memset(&ro6, 0, sizeof(struct route_in6)); 1760 memcpy(&ro6.ro_dst, gsin6, sizeof(struct sockaddr_in6)); 1761 #ifdef notyet 1762 rtalloc_ign_fib(&ro6, 0, inp ? inp->inp_inc.inc_fibnum : 0); 1763 #else 1764 rtalloc_ign((struct route *)&ro6, 0); 1765 #endif 1766 if (ro6.ro_rt != NULL) { 1767 ifp = ro6.ro_rt->rt_ifp; 1768 KASSERT(ifp != NULL, ("%s: null ifp", __func__)); 1769 RTFREE(ro6.ro_rt); 1770 } 1771 1772 return (ifp); 1773 } 1774 1775 /* 1776 * Join an IPv6 multicast group, possibly with a source. 1777 * 1778 * FIXME: The KAME use of the unspecified address (::) 1779 * to join *all* multicast groups is currently unsupported. 1780 */ 1781 static int 1782 in6p_join_group(struct inpcb *inp, struct sockopt *sopt) 1783 { 1784 INIT_VNET_NET(curvnet); 1785 struct group_source_req gsr; 1786 sockunion_t *gsa, *ssa; 1787 struct ifnet *ifp; 1788 struct in6_mfilter *imf; 1789 struct ip6_moptions *imo; 1790 struct in6_multi *inm; 1791 struct in6_msource *lims; 1792 size_t idx; 1793 int error, is_new; 1794 1795 ifp = NULL; 1796 imf = NULL; 1797 error = 0; 1798 is_new = 0; 1799 1800 memset(&gsr, 0, sizeof(struct group_source_req)); 1801 gsa = (sockunion_t *)&gsr.gsr_group; 1802 gsa->ss.ss_family = AF_UNSPEC; 1803 ssa = (sockunion_t *)&gsr.gsr_source; 1804 ssa->ss.ss_family = AF_UNSPEC; 1805 1806 switch (sopt->sopt_name) { 1807 case IPV6_JOIN_GROUP: { 1808 struct ipv6_mreq mreq; 1809 1810 error = sooptcopyin(sopt, &mreq, sizeof(struct ipv6_mreq), 1811 sizeof(struct ipv6_mreq)); 1812 if (error) 1813 return (error); 1814 1815 gsa->sin6.sin6_family = AF_INET6; 1816 gsa->sin6.sin6_len = sizeof(struct sockaddr_in6); 1817 gsa->sin6.sin6_addr = mreq.ipv6mr_multiaddr; 1818 1819 if (mreq.ipv6mr_interface == 0) { 1820 ifp = in6p_lookup_mcast_ifp(inp, &gsa->sin6); 1821 } else { 1822 if (mreq.ipv6mr_interface < 0 || 1823 V_if_index < mreq.ipv6mr_interface) 1824 return (EADDRNOTAVAIL); 1825 ifp = ifnet_byindex(mreq.ipv6mr_interface); 1826 } 1827 CTR3(KTR_MLD, "%s: ipv6mr_interface = %d, ifp = %p", 1828 __func__, mreq.ipv6mr_interface, ifp); 1829 } break; 1830 1831 case MCAST_JOIN_GROUP: 1832 case MCAST_JOIN_SOURCE_GROUP: 1833 if (sopt->sopt_name == MCAST_JOIN_GROUP) { 1834 error = sooptcopyin(sopt, &gsr, 1835 sizeof(struct group_req), 1836 sizeof(struct group_req)); 1837 } else if (sopt->sopt_name == MCAST_JOIN_SOURCE_GROUP) { 1838 error = sooptcopyin(sopt, &gsr, 1839 sizeof(struct group_source_req), 1840 sizeof(struct group_source_req)); 1841 } 1842 if (error) 1843 return (error); 1844 1845 if (gsa->sin6.sin6_family != AF_INET6 || 1846 gsa->sin6.sin6_len != sizeof(struct sockaddr_in6)) 1847 return (EINVAL); 1848 1849 /* 1850 * Overwrite the port field if present, as the sockaddr 1851 * being copied in may be matched with a binary comparison. 1852 */ 1853 gsa->sin6.sin6_port = 0; 1854 if (sopt->sopt_name == MCAST_JOIN_SOURCE_GROUP) { 1855 if (ssa->sin6.sin6_family != AF_INET6 || 1856 ssa->sin6.sin6_len != sizeof(struct sockaddr_in6)) 1857 return (EINVAL); 1858 ssa->sin6.sin6_port = 0; 1859 } 1860 1861 if (gsr.gsr_interface == 0 || V_if_index < gsr.gsr_interface) 1862 return (EADDRNOTAVAIL); 1863 ifp = ifnet_byindex(gsr.gsr_interface); 1864 break; 1865 1866 default: 1867 CTR2(KTR_MLD, "%s: unknown sopt_name %d", 1868 __func__, sopt->sopt_name); 1869 return (EOPNOTSUPP); 1870 break; 1871 } 1872 1873 #ifdef notyet 1874 /* 1875 * FIXME: Check for unspecified address (all groups). 1876 * Do we have a normative reference for this 'feature'? 1877 * 1878 * We use the unspecified address to specify to accept 1879 * all multicast addresses. Only super user is allowed 1880 * to do this. 1881 * XXX-BZ might need a better PRIV_NETINET_x for this 1882 */ 1883 if (IN6_IS_ADDR_UNSPECIFIED(&gsa->sin6.sin6_addr)) { 1884 error = priv_check(curthread, PRIV_NETINET_MROUTE); 1885 if (error) 1886 break; 1887 } else 1888 #endif 1889 if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr)) 1890 return (EINVAL); 1891 1892 if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) 1893 return (EADDRNOTAVAIL); 1894 1895 #ifdef notyet 1896 /* 1897 * FIXME: Set interface scope in group address. 1898 */ 1899 (void)in6_setscope(&gsa->sin6.sin_addr, ifp, NULL); 1900 #endif 1901 1902 /* 1903 * MCAST_JOIN_SOURCE on an exclusive membership is an error. 1904 * On an existing inclusive membership, it just adds the 1905 * source to the filter list. 1906 */ 1907 imo = in6p_findmoptions(inp); 1908 idx = im6o_match_group(imo, ifp, &gsa->sa); 1909 if (idx == -1) { 1910 is_new = 1; 1911 } else { 1912 inm = imo->im6o_membership[idx]; 1913 imf = &imo->im6o_mfilters[idx]; 1914 if (ssa->ss.ss_family != AF_UNSPEC && 1915 imf->im6f_st[1] != MCAST_INCLUDE) { 1916 error = EINVAL; 1917 goto out_in6p_locked; 1918 } 1919 lims = im6o_match_source(imo, idx, &ssa->sa); 1920 if (lims != NULL) { 1921 error = EADDRNOTAVAIL; 1922 goto out_in6p_locked; 1923 } 1924 } 1925 1926 /* 1927 * Begin state merge transaction at socket layer. 1928 */ 1929 INP_WLOCK_ASSERT(inp); 1930 1931 if (is_new) { 1932 if (imo->im6o_num_memberships == imo->im6o_max_memberships) { 1933 error = im6o_grow(imo); 1934 if (error) 1935 goto out_in6p_locked; 1936 } 1937 /* 1938 * Allocate the new slot upfront so we can deal with 1939 * grafting the new source filter in same code path 1940 * as for join-source on existing membership. 1941 */ 1942 idx = imo->im6o_num_memberships; 1943 imo->im6o_membership[idx] = NULL; 1944 imo->im6o_num_memberships++; 1945 KASSERT(imo->im6o_mfilters != NULL, 1946 ("%s: im6f_mfilters vector was not allocated", __func__)); 1947 imf = &imo->im6o_mfilters[idx]; 1948 KASSERT(RB_EMPTY(&imf->im6f_sources), 1949 ("%s: im6f_sources not empty", __func__)); 1950 } 1951 1952 /* 1953 * Graft new source into filter list for this inpcb's 1954 * membership of the group. The in6_multi may not have 1955 * been allocated yet if this is a new membership. 1956 */ 1957 if (ssa->ss.ss_family != AF_UNSPEC) { 1958 /* Membership starts in IN mode */ 1959 if (is_new) { 1960 CTR1(KTR_MLD, "%s: new join w/source", __func__); 1961 im6f_init(imf, MCAST_UNDEFINED, MCAST_INCLUDE); 1962 } else { 1963 CTR2(KTR_MLD, "%s: %s source", __func__, "allow"); 1964 } 1965 lims = im6f_graft(imf, MCAST_INCLUDE, &ssa->sin6); 1966 if (lims == NULL) { 1967 CTR1(KTR_MLD, "%s: merge imf state failed", 1968 __func__); 1969 error = ENOMEM; 1970 goto out_im6o_free; 1971 } 1972 } 1973 1974 /* 1975 * Begin state merge transaction at MLD layer. 1976 */ 1977 IN6_MULTI_LOCK(); 1978 1979 if (is_new) { 1980 error = in6_mc_join_locked(ifp, &gsa->sin6.sin6_addr, imf, 1981 &inm, 0); 1982 if (error) 1983 goto out_im6o_free; 1984 imo->im6o_membership[idx] = inm; 1985 } else { 1986 CTR1(KTR_MLD, "%s: merge inm state", __func__); 1987 error = in6m_merge(inm, imf); 1988 if (error) { 1989 CTR1(KTR_MLD, "%s: failed to merge inm state", 1990 __func__); 1991 goto out_im6f_rollback; 1992 } 1993 CTR1(KTR_MLD, "%s: doing mld downcall", __func__); 1994 error = mld_change_state(inm, 0); 1995 if (error) { 1996 CTR1(KTR_MLD, "%s: failed mld downcall", 1997 __func__); 1998 goto out_im6f_rollback; 1999 } 2000 } 2001 2002 IN6_MULTI_UNLOCK(); 2003 2004 out_im6f_rollback: 2005 INP_WLOCK_ASSERT(inp); 2006 if (error) { 2007 im6f_rollback(imf); 2008 if (is_new) 2009 im6f_purge(imf); 2010 else 2011 im6f_reap(imf); 2012 } else { 2013 im6f_commit(imf); 2014 } 2015 2016 out_im6o_free: 2017 if (error && is_new) { 2018 imo->im6o_membership[idx] = NULL; 2019 --imo->im6o_num_memberships; 2020 } 2021 2022 out_in6p_locked: 2023 INP_WUNLOCK(inp); 2024 return (error); 2025 } 2026 2027 /* 2028 * Leave an IPv6 multicast group on an inpcb, possibly with a source. 2029 */ 2030 static int 2031 in6p_leave_group(struct inpcb *inp, struct sockopt *sopt) 2032 { 2033 INIT_VNET_NET(curvnet); 2034 struct group_source_req gsr; 2035 sockunion_t *gsa, *ssa; 2036 struct ifnet *ifp; 2037 struct in6_mfilter *imf; 2038 struct ip6_moptions *imo; 2039 struct in6_msource *ims; 2040 struct in6_multi *inm; 2041 size_t idx; 2042 int error, is_final; 2043 #ifdef KTR 2044 char ip6tbuf[INET6_ADDRSTRLEN]; 2045 #endif 2046 2047 ifp = NULL; 2048 error = 0; 2049 is_final = 1; 2050 2051 memset(&gsr, 0, sizeof(struct group_source_req)); 2052 gsa = (sockunion_t *)&gsr.gsr_group; 2053 gsa->ss.ss_family = AF_UNSPEC; 2054 ssa = (sockunion_t *)&gsr.gsr_source; 2055 ssa->ss.ss_family = AF_UNSPEC; 2056 2057 switch (sopt->sopt_name) { 2058 case IPV6_LEAVE_GROUP: { 2059 struct ipv6_mreq mreq; 2060 2061 error = sooptcopyin(sopt, &mreq, sizeof(struct ipv6_mreq), 2062 sizeof(struct ipv6_mreq)); 2063 if (error) 2064 return (error); 2065 2066 gsa->sin6.sin6_family = AF_INET6; 2067 gsa->sin6.sin6_len = sizeof(struct sockaddr_in6); 2068 gsa->sin6.sin6_addr = mreq.ipv6mr_multiaddr; 2069 2070 if (mreq.ipv6mr_interface == 0) { 2071 #ifdef notyet 2072 /* 2073 * FIXME: Resolve scope ambiguity when interface 2074 * index is unspecified. 2075 */ 2076 ifp = in6p_lookup_mcast_ifp(inp, &gsa->sin6); 2077 #else 2078 return (EADDRNOTAVAIL); 2079 #endif 2080 } else { 2081 if (mreq.ipv6mr_interface < 0 || 2082 V_if_index < mreq.ipv6mr_interface) 2083 return (EADDRNOTAVAIL); 2084 ifp = ifnet_byindex(mreq.ipv6mr_interface); 2085 } 2086 2087 CTR3(KTR_MLD, "%s: ipv6mr_interface = %d, ifp = %p", 2088 __func__, mreq.ipv6mr_interface, ifp); 2089 } break; 2090 2091 case MCAST_LEAVE_GROUP: 2092 case MCAST_LEAVE_SOURCE_GROUP: 2093 if (sopt->sopt_name == MCAST_LEAVE_GROUP) { 2094 error = sooptcopyin(sopt, &gsr, 2095 sizeof(struct group_req), 2096 sizeof(struct group_req)); 2097 } else if (sopt->sopt_name == MCAST_LEAVE_SOURCE_GROUP) { 2098 error = sooptcopyin(sopt, &gsr, 2099 sizeof(struct group_source_req), 2100 sizeof(struct group_source_req)); 2101 } 2102 if (error) 2103 return (error); 2104 2105 if (gsa->sin6.sin6_family != AF_INET6 || 2106 gsa->sin6.sin6_len != sizeof(struct sockaddr_in6)) 2107 return (EINVAL); 2108 2109 if (sopt->sopt_name == MCAST_LEAVE_SOURCE_GROUP) { 2110 if (ssa->sin6.sin6_family != AF_INET6 || 2111 ssa->sin6.sin6_len != sizeof(struct sockaddr_in6)) 2112 return (EINVAL); 2113 } 2114 2115 if (gsr.gsr_interface == 0 || V_if_index < gsr.gsr_interface) 2116 return (EADDRNOTAVAIL); 2117 2118 ifp = ifnet_byindex(gsr.gsr_interface); 2119 break; 2120 2121 default: 2122 CTR2(KTR_MLD, "%s: unknown sopt_name %d", 2123 __func__, sopt->sopt_name); 2124 return (EOPNOTSUPP); 2125 break; 2126 } 2127 2128 if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr)) 2129 return (EINVAL); 2130 2131 #ifdef notyet 2132 /* 2133 * FIXME: Need to embed ifp's scope ID in the address 2134 * handed down to MLD. 2135 * See KAME IPV6_LEAVE_GROUP implementation. 2136 */ 2137 (void)in6_setscope(&mreq->ipv6mr_multiaddr, ifp, NULL); 2138 #endif 2139 2140 /* 2141 * Find the membership in the membership array. 2142 */ 2143 imo = in6p_findmoptions(inp); 2144 idx = im6o_match_group(imo, ifp, &gsa->sa); 2145 if (idx == -1) { 2146 error = EADDRNOTAVAIL; 2147 goto out_in6p_locked; 2148 } 2149 inm = imo->im6o_membership[idx]; 2150 imf = &imo->im6o_mfilters[idx]; 2151 2152 if (ssa->ss.ss_family != AF_UNSPEC) 2153 is_final = 0; 2154 2155 /* 2156 * Begin state merge transaction at socket layer. 2157 */ 2158 INP_WLOCK_ASSERT(inp); 2159 2160 /* 2161 * If we were instructed only to leave a given source, do so. 2162 * MCAST_LEAVE_SOURCE_GROUP is only valid for inclusive memberships. 2163 */ 2164 if (is_final) { 2165 im6f_leave(imf); 2166 } else { 2167 if (imf->im6f_st[0] == MCAST_EXCLUDE) { 2168 error = EADDRNOTAVAIL; 2169 goto out_in6p_locked; 2170 } 2171 ims = im6o_match_source(imo, idx, &ssa->sa); 2172 if (ims == NULL) { 2173 CTR3(KTR_MLD, "%s: source %p %spresent", __func__, 2174 ip6_sprintf(ip6tbuf, &ssa->sin6.sin6_addr), 2175 "not "); 2176 error = EADDRNOTAVAIL; 2177 goto out_in6p_locked; 2178 } 2179 CTR2(KTR_MLD, "%s: %s source", __func__, "block"); 2180 error = im6f_prune(imf, &ssa->sin6); 2181 if (error) { 2182 CTR1(KTR_MLD, "%s: merge imf state failed", 2183 __func__); 2184 goto out_in6p_locked; 2185 } 2186 } 2187 2188 /* 2189 * Begin state merge transaction at MLD layer. 2190 */ 2191 IN6_MULTI_LOCK(); 2192 2193 if (is_final) { 2194 /* 2195 * Give up the multicast address record to which 2196 * the membership points. 2197 */ 2198 (void)in6_mc_leave_locked(inm, imf); 2199 } else { 2200 CTR1(KTR_MLD, "%s: merge inm state", __func__); 2201 error = in6m_merge(inm, imf); 2202 if (error) { 2203 CTR1(KTR_MLD, "%s: failed to merge inm state", 2204 __func__); 2205 goto out_im6f_rollback; 2206 } 2207 2208 CTR1(KTR_MLD, "%s: doing mld downcall", __func__); 2209 error = mld_change_state(inm, 0); 2210 if (error) { 2211 CTR1(KTR_MLD, "%s: failed mld downcall", 2212 __func__); 2213 } 2214 } 2215 2216 IN6_MULTI_UNLOCK(); 2217 2218 out_im6f_rollback: 2219 if (error) 2220 im6f_rollback(imf); 2221 else 2222 im6f_commit(imf); 2223 2224 im6f_reap(imf); 2225 2226 if (is_final) { 2227 /* Remove the gap in the membership array. */ 2228 for (++idx; idx < imo->im6o_num_memberships; ++idx) 2229 imo->im6o_membership[idx-1] = imo->im6o_membership[idx]; 2230 imo->im6o_num_memberships--; 2231 } 2232 2233 out_in6p_locked: 2234 INP_WUNLOCK(inp); 2235 return (error); 2236 } 2237 2238 /* 2239 * Select the interface for transmitting IPv6 multicast datagrams. 2240 * 2241 * Either an instance of struct in6_addr or an instance of struct ipv6_mreqn 2242 * may be passed to this socket option. An address of in6addr_any or an 2243 * interface index of 0 is used to remove a previous selection. 2244 * When no interface is selected, one is chosen for every send. 2245 */ 2246 static int 2247 in6p_set_multicast_if(struct inpcb *inp, struct sockopt *sopt) 2248 { 2249 INIT_VNET_NET(curvnet); 2250 struct ifnet *ifp; 2251 struct ip6_moptions *imo; 2252 u_int ifindex; 2253 int error; 2254 2255 if (sopt->sopt_valsize != sizeof(u_int)) 2256 return (EINVAL); 2257 2258 error = sooptcopyin(sopt, &ifindex, sizeof(u_int), sizeof(u_int)); 2259 if (error) 2260 return (error); 2261 if (ifindex < 0 || V_if_index < ifindex) 2262 return (EINVAL); 2263 2264 ifp = ifnet_byindex(ifindex); 2265 if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0) 2266 return (EADDRNOTAVAIL); 2267 2268 imo = in6p_findmoptions(inp); 2269 imo->im6o_multicast_ifp = ifp; 2270 INP_WUNLOCK(inp); 2271 2272 return (0); 2273 } 2274 2275 /* 2276 * Atomically set source filters on a socket for an IPv6 multicast group. 2277 * 2278 * SMPng: NOTE: Potentially calls malloc(M_WAITOK) with Giant held. 2279 */ 2280 static int 2281 in6p_set_source_filters(struct inpcb *inp, struct sockopt *sopt) 2282 { 2283 INIT_VNET_NET(curvnet); 2284 struct __msfilterreq msfr; 2285 sockunion_t *gsa; 2286 struct ifnet *ifp; 2287 struct in6_mfilter *imf; 2288 struct ip6_moptions *imo; 2289 struct in6_multi *inm; 2290 size_t idx; 2291 int error; 2292 2293 error = sooptcopyin(sopt, &msfr, sizeof(struct __msfilterreq), 2294 sizeof(struct __msfilterreq)); 2295 if (error) 2296 return (error); 2297 2298 if (msfr.msfr_nsrcs > in6_mcast_maxsocksrc || 2299 (msfr.msfr_fmode != MCAST_EXCLUDE && 2300 msfr.msfr_fmode != MCAST_INCLUDE)) 2301 return (EINVAL); 2302 2303 if (msfr.msfr_group.ss_family != AF_INET6 || 2304 msfr.msfr_group.ss_len != sizeof(struct sockaddr_in6)) 2305 return (EINVAL); 2306 2307 gsa = (sockunion_t *)&msfr.msfr_group; 2308 if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr)) 2309 return (EINVAL); 2310 2311 gsa->sin6.sin6_port = 0; /* ignore port */ 2312 2313 if (msfr.msfr_ifindex == 0 || V_if_index < msfr.msfr_ifindex) 2314 return (EADDRNOTAVAIL); 2315 2316 ifp = ifnet_byindex(msfr.msfr_ifindex); 2317 if (ifp == NULL) 2318 return (EADDRNOTAVAIL); 2319 2320 /* 2321 * Take the INP write lock. 2322 * Check if this socket is a member of this group. 2323 */ 2324 imo = in6p_findmoptions(inp); 2325 idx = im6o_match_group(imo, ifp, &gsa->sa); 2326 if (idx == -1 || imo->im6o_mfilters == NULL) { 2327 error = EADDRNOTAVAIL; 2328 goto out_in6p_locked; 2329 } 2330 inm = imo->im6o_membership[idx]; 2331 imf = &imo->im6o_mfilters[idx]; 2332 2333 /* 2334 * Begin state merge transaction at socket layer. 2335 */ 2336 INP_WLOCK_ASSERT(inp); 2337 2338 imf->im6f_st[1] = msfr.msfr_fmode; 2339 2340 /* 2341 * Apply any new source filters, if present. 2342 * Make a copy of the user-space source vector so 2343 * that we may copy them with a single copyin. This 2344 * allows us to deal with page faults up-front. 2345 */ 2346 if (msfr.msfr_nsrcs > 0) { 2347 struct in6_msource *lims; 2348 struct sockaddr_in6 *psin; 2349 struct sockaddr_storage *kss, *pkss; 2350 int i; 2351 2352 INP_WUNLOCK(inp); 2353 2354 CTR2(KTR_MLD, "%s: loading %lu source list entries", 2355 __func__, (unsigned long)msfr.msfr_nsrcs); 2356 kss = malloc(sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs, 2357 M_TEMP, M_WAITOK); 2358 error = copyin(msfr.msfr_srcs, kss, 2359 sizeof(struct sockaddr_storage) * msfr.msfr_nsrcs); 2360 if (error) { 2361 free(kss, M_TEMP); 2362 return (error); 2363 } 2364 2365 INP_WLOCK(inp); 2366 2367 /* 2368 * Mark all source filters as UNDEFINED at t1. 2369 * Restore new group filter mode, as im6f_leave() 2370 * will set it to INCLUDE. 2371 */ 2372 im6f_leave(imf); 2373 imf->im6f_st[1] = msfr.msfr_fmode; 2374 2375 /* 2376 * Update socket layer filters at t1, lazy-allocating 2377 * new entries. This saves a bunch of memory at the 2378 * cost of one RB_FIND() per source entry; duplicate 2379 * entries in the msfr_nsrcs vector are ignored. 2380 * If we encounter an error, rollback transaction. 2381 * 2382 * XXX This too could be replaced with a set-symmetric 2383 * difference like loop to avoid walking from root 2384 * every time, as the key space is common. 2385 */ 2386 for (i = 0, pkss = kss; i < msfr.msfr_nsrcs; i++, pkss++) { 2387 psin = (struct sockaddr_in6 *)pkss; 2388 if (psin->sin6_family != AF_INET6) { 2389 error = EAFNOSUPPORT; 2390 break; 2391 } 2392 if (psin->sin6_len != sizeof(struct sockaddr_in6)) { 2393 error = EINVAL; 2394 break; 2395 } 2396 error = im6f_get_source(imf, psin, &lims); 2397 if (error) 2398 break; 2399 lims->im6sl_st[1] = imf->im6f_st[1]; 2400 } 2401 free(kss, M_TEMP); 2402 } 2403 2404 if (error) 2405 goto out_im6f_rollback; 2406 2407 INP_WLOCK_ASSERT(inp); 2408 IN6_MULTI_LOCK(); 2409 2410 /* 2411 * Begin state merge transaction at MLD layer. 2412 */ 2413 CTR1(KTR_MLD, "%s: merge inm state", __func__); 2414 error = in6m_merge(inm, imf); 2415 if (error) { 2416 CTR1(KTR_MLD, "%s: failed to merge inm state", __func__); 2417 goto out_im6f_rollback; 2418 } 2419 2420 CTR1(KTR_MLD, "%s: doing mld downcall", __func__); 2421 error = mld_change_state(inm, 0); 2422 if (error) 2423 CTR1(KTR_MLD, "%s: failed mld downcall", __func__); 2424 2425 IN6_MULTI_UNLOCK(); 2426 2427 out_im6f_rollback: 2428 if (error) 2429 im6f_rollback(imf); 2430 else 2431 im6f_commit(imf); 2432 2433 im6f_reap(imf); 2434 2435 out_in6p_locked: 2436 INP_WUNLOCK(inp); 2437 return (error); 2438 } 2439 2440 /* 2441 * Set the IP multicast options in response to user setsockopt(). 2442 * 2443 * Many of the socket options handled in this function duplicate the 2444 * functionality of socket options in the regular unicast API. However, 2445 * it is not possible to merge the duplicate code, because the idempotence 2446 * of the IPv6 multicast part of the BSD Sockets API must be preserved; 2447 * the effects of these options must be treated as separate and distinct. 2448 * 2449 * SMPng: XXX: Unlocked read of inp_socket believed OK. 2450 */ 2451 int 2452 ip6_setmoptions(struct inpcb *inp, struct sockopt *sopt) 2453 { 2454 INIT_VNET_INET6(curvnet); 2455 struct ip6_moptions *im6o; 2456 int error; 2457 2458 error = 0; 2459 2460 /* 2461 * If socket is neither of type SOCK_RAW or SOCK_DGRAM, 2462 * or is a divert socket, reject it. 2463 */ 2464 if (inp->inp_socket->so_proto->pr_protocol == IPPROTO_DIVERT || 2465 (inp->inp_socket->so_proto->pr_type != SOCK_RAW && 2466 inp->inp_socket->so_proto->pr_type != SOCK_DGRAM)) 2467 return (EOPNOTSUPP); 2468 2469 switch (sopt->sopt_name) { 2470 case IPV6_MULTICAST_IF: 2471 error = in6p_set_multicast_if(inp, sopt); 2472 break; 2473 2474 case IPV6_MULTICAST_HOPS: { 2475 int hlim; 2476 2477 if (sopt->sopt_valsize != sizeof(int)) { 2478 error = EINVAL; 2479 break; 2480 } 2481 error = sooptcopyin(sopt, &hlim, sizeof(hlim), sizeof(int)); 2482 if (error) 2483 break; 2484 if (hlim < -1 || hlim > 255) { 2485 error = EINVAL; 2486 break; 2487 } else if (hlim == -1) { 2488 hlim = V_ip6_defmcasthlim; 2489 } 2490 im6o = in6p_findmoptions(inp); 2491 im6o->im6o_multicast_hlim = hlim; 2492 INP_WUNLOCK(inp); 2493 break; 2494 } 2495 2496 case IPV6_MULTICAST_LOOP: { 2497 u_int loop; 2498 2499 /* 2500 * Set the loopback flag for outgoing multicast packets. 2501 * Must be zero or one. 2502 */ 2503 if (sopt->sopt_valsize != sizeof(u_int)) { 2504 error = EINVAL; 2505 break; 2506 } 2507 error = sooptcopyin(sopt, &loop, sizeof(u_int), sizeof(u_int)); 2508 if (error) 2509 break; 2510 if (loop > 1) { 2511 error = EINVAL; 2512 break; 2513 } 2514 im6o = in6p_findmoptions(inp); 2515 im6o->im6o_multicast_loop = loop; 2516 INP_WUNLOCK(inp); 2517 break; 2518 } 2519 2520 case IPV6_JOIN_GROUP: 2521 case MCAST_JOIN_GROUP: 2522 case MCAST_JOIN_SOURCE_GROUP: 2523 error = in6p_join_group(inp, sopt); 2524 break; 2525 2526 case IPV6_LEAVE_GROUP: 2527 case MCAST_LEAVE_GROUP: 2528 case MCAST_LEAVE_SOURCE_GROUP: 2529 error = in6p_leave_group(inp, sopt); 2530 break; 2531 2532 case MCAST_BLOCK_SOURCE: 2533 case MCAST_UNBLOCK_SOURCE: 2534 error = in6p_block_unblock_source(inp, sopt); 2535 break; 2536 2537 case IPV6_MSFILTER: 2538 error = in6p_set_source_filters(inp, sopt); 2539 break; 2540 2541 default: 2542 error = EOPNOTSUPP; 2543 break; 2544 } 2545 2546 INP_UNLOCK_ASSERT(inp); 2547 2548 return (error); 2549 } 2550 2551 /* 2552 * Expose MLD's multicast filter mode and source list(s) to userland, 2553 * keyed by (ifindex, group). 2554 * The filter mode is written out as a uint32_t, followed by 2555 * 0..n of struct in6_addr. 2556 * For use by ifmcstat(8). 2557 * SMPng: NOTE: unlocked read of ifindex space. 2558 */ 2559 static int 2560 sysctl_ip6_mcast_filters(SYSCTL_HANDLER_ARGS) 2561 { 2562 INIT_VNET_NET(curvnet); 2563 struct in6_addr *pgina; 2564 struct in6_addr src; 2565 struct ifnet *ifp; 2566 struct ifmultiaddr *ifma; 2567 struct in6_multi *inm; 2568 struct ip6_msource *ims; 2569 int *name; 2570 int retval; 2571 u_int namelen; 2572 uint32_t fmode, ifindex; 2573 #ifdef KTR 2574 char ip6tbuf[INET6_ADDRSTRLEN]; 2575 #endif 2576 2577 name = (int *)arg1; 2578 namelen = arg2; 2579 2580 if (req->newptr != NULL) 2581 return (EPERM); 2582 2583 /* int: ifindex + 4 * 32 bits of IPv6 address */ 2584 if (namelen != 5) 2585 return (EINVAL); 2586 2587 ifindex = name[0]; 2588 if (ifindex <= 0 || ifindex > V_if_index) { 2589 CTR2(KTR_MLD, "%s: ifindex %u out of range", 2590 __func__, ifindex); 2591 return (ENOENT); 2592 } 2593 2594 pgina = (struct in6_addr *)&name[1]; 2595 if (!IN6_IS_ADDR_MULTICAST(pgina)) { 2596 CTR2(KTR_MLD, "%s: group %s is not multicast", 2597 __func__, ip6_sprintf(ip6tbuf, pgina)); 2598 return (EINVAL); 2599 } 2600 2601 ifp = ifnet_byindex(ifindex); 2602 if (ifp == NULL) { 2603 CTR2(KTR_MLD, "%s: no ifp for ifindex %u", 2604 __func__, ifindex); 2605 return (ENOENT); 2606 } 2607 2608 retval = sysctl_wire_old_buffer(req, 2609 sizeof(uint32_t) + (in6_mcast_maxgrpsrc * sizeof(struct in6_addr))); 2610 if (retval) 2611 return (retval); 2612 2613 IN6_MULTI_LOCK(); 2614 2615 IF_ADDR_LOCK(ifp); 2616 TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 2617 if (ifma->ifma_addr->sa_family != AF_INET6 || 2618 ifma->ifma_protospec == NULL) 2619 continue; 2620 inm = (struct in6_multi *)ifma->ifma_protospec; 2621 if (!IN6_ARE_ADDR_EQUAL(&inm->in6m_addr, pgina)) 2622 continue; 2623 fmode = inm->in6m_st[1].iss_fmode; 2624 retval = SYSCTL_OUT(req, &fmode, sizeof(uint32_t)); 2625 if (retval != 0) 2626 break; 2627 RB_FOREACH(ims, ip6_msource_tree, &inm->in6m_srcs) { 2628 CTR2(KTR_MLD, "%s: visit node %p", __func__, ims); 2629 /* 2630 * Only copy-out sources which are in-mode. 2631 */ 2632 if (fmode != im6s_get_mode(inm, ims, 1)) { 2633 CTR1(KTR_MLD, "%s: skip non-in-mode", 2634 __func__); 2635 continue; 2636 } 2637 src = ims->im6s_addr; 2638 retval = SYSCTL_OUT(req, &src, 2639 sizeof(struct in6_addr)); 2640 if (retval != 0) 2641 break; 2642 } 2643 } 2644 IF_ADDR_UNLOCK(ifp); 2645 2646 IN6_MULTI_UNLOCK(); 2647 2648 return (retval); 2649 } 2650 2651 #ifdef KTR 2652 2653 static const char *in6m_modestrs[] = { "un", "in", "ex" }; 2654 2655 static const char * 2656 in6m_mode_str(const int mode) 2657 { 2658 2659 if (mode >= MCAST_UNDEFINED && mode <= MCAST_EXCLUDE) 2660 return (in6m_modestrs[mode]); 2661 return ("??"); 2662 } 2663 2664 static const char *in6m_statestrs[] = { 2665 "not-member", 2666 "silent", 2667 "idle", 2668 "lazy", 2669 "sleeping", 2670 "awakening", 2671 "query-pending", 2672 "sg-query-pending", 2673 "leaving" 2674 }; 2675 2676 static const char * 2677 in6m_state_str(const int state) 2678 { 2679 2680 if (state >= MLD_NOT_MEMBER && state <= MLD_LEAVING_MEMBER) 2681 return (in6m_statestrs[state]); 2682 return ("??"); 2683 } 2684 2685 /* 2686 * Dump an in6_multi structure to the console. 2687 */ 2688 void 2689 in6m_print(const struct in6_multi *inm) 2690 { 2691 int t; 2692 char ip6tbuf[INET6_ADDRSTRLEN]; 2693 2694 if ((ktr_mask & KTR_MLD) == 0) 2695 return; 2696 2697 printf("%s: --- begin in6m %p ---\n", __func__, inm); 2698 printf("addr %s ifp %p(%s) ifma %p\n", 2699 ip6_sprintf(ip6tbuf, &inm->in6m_addr), 2700 inm->in6m_ifp, 2701 inm->in6m_ifp->if_xname, 2702 inm->in6m_ifma); 2703 printf("timer %u state %s refcount %u scq.len %u\n", 2704 inm->in6m_timer, 2705 in6m_state_str(inm->in6m_state), 2706 inm->in6m_refcount, 2707 inm->in6m_scq.ifq_len); 2708 printf("mli %p nsrc %lu sctimer %u scrv %u\n", 2709 inm->in6m_mli, 2710 inm->in6m_nsrc, 2711 inm->in6m_sctimer, 2712 inm->in6m_scrv); 2713 for (t = 0; t < 2; t++) { 2714 printf("t%d: fmode %s asm %u ex %u in %u rec %u\n", t, 2715 in6m_mode_str(inm->in6m_st[t].iss_fmode), 2716 inm->in6m_st[t].iss_asm, 2717 inm->in6m_st[t].iss_ex, 2718 inm->in6m_st[t].iss_in, 2719 inm->in6m_st[t].iss_rec); 2720 } 2721 printf("%s: --- end in6m %p ---\n", __func__, inm); 2722 } 2723 2724 #else /* !KTR */ 2725 2726 void 2727 in6m_print(const struct in6_multi *inm) 2728 { 2729 2730 } 2731 2732 #endif /* KTR */ 2733