]> git.saurik.com Git - apple/xnu.git/blob - bsd/netinet6/in6_mcast.c
715403cf82fd7100389d0ced5c98c3cc9af2ec0c
[apple/xnu.git] / bsd / netinet6 / in6_mcast.c
1 /*
2 * Copyright (c) 2010-2012 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28 /*
29 * Copyright (c) 2009 Bruce Simpson.
30 * All rights reserved.
31 *
32 * Redistribution and use in source and binary forms, with or without
33 * modification, are permitted provided that the following conditions
34 * are met:
35 * 1. Redistributions of source code must retain the above copyright
36 * notice, this list of conditions and the following disclaimer.
37 * 2. Redistributions in binary form must reproduce the above copyright
38 * notice, this list of conditions and the following disclaimer in the
39 * documentation and/or other materials provided with the distribution.
40 * 3. The name of the author may not be used to endorse or promote
41 * products derived from this software without specific prior written
42 * permission.
43 *
44 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
45 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
46 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
47 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
48 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
49 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
50 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
51 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
52 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
53 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
54 * SUCH DAMAGE.
55 */
56
57 /*
58 * IPv6 multicast socket, group, and socket option processing module.
59 * Normative references: RFC 2292, RFC 3492, RFC 3542, RFC 3678, RFC 3810.
60 */
61
62 #include <sys/cdefs.h>
63
64 #include <sys/param.h>
65 #include <sys/systm.h>
66 #include <sys/kernel.h>
67 #include <sys/malloc.h>
68 #include <sys/mbuf.h>
69 #include <sys/protosw.h>
70 #include <sys/socket.h>
71 #include <sys/socketvar.h>
72 #include <sys/protosw.h>
73 #include <sys/sysctl.h>
74 #include <sys/tree.h>
75 #include <sys/mcache.h>
76
77 #include <kern/zalloc.h>
78
79 #include <pexpert/pexpert.h>
80
81 #include <net/if.h>
82 #include <net/if_dl.h>
83 #include <net/route.h>
84
85 #include <netinet/in.h>
86 #include <netinet/in_var.h>
87 #include <netinet6/in6_var.h>
88 #include <netinet/ip6.h>
89 #include <netinet/icmp6.h>
90 #include <netinet6/ip6_var.h>
91 #include <netinet/in_pcb.h>
92 #include <netinet/tcp.h>
93 #include <netinet/tcp_seq.h>
94 #include <netinet/tcp_var.h>
95 #include <netinet6/nd6.h>
96 #include <netinet6/mld6_var.h>
97 #include <netinet6/scope6_var.h>
98
99 #ifndef __SOCKUNION_DECLARED
100 union sockunion {
101 struct sockaddr_storage ss;
102 struct sockaddr sa;
103 struct sockaddr_dl sdl;
104 struct sockaddr_in6 sin6;
105 };
106 typedef union sockunion sockunion_t;
107 #define __SOCKUNION_DECLARED
108 #endif /* __SOCKUNION_DECLARED */
109
110 static void im6f_commit(struct in6_mfilter *);
111 static int im6f_get_source(struct in6_mfilter *imf,
112 const struct sockaddr_in6 *psin,
113 struct in6_msource **);
114 static struct in6_msource *
115 im6f_graft(struct in6_mfilter *, const uint8_t,
116 const struct sockaddr_in6 *);
117 static int im6f_prune(struct in6_mfilter *, const struct sockaddr_in6 *);
118 static void im6f_rollback(struct in6_mfilter *);
119 static void im6f_reap(struct in6_mfilter *);
120 static int im6o_grow(struct ip6_moptions *, size_t);
121 static size_t im6o_match_group(const struct ip6_moptions *,
122 const struct ifnet *, const struct sockaddr *);
123 static struct in6_msource *
124 im6o_match_source(const struct ip6_moptions *, const size_t,
125 const struct sockaddr *);
126 static void im6s_merge(struct ip6_msource *ims,
127 const struct in6_msource *lims, const int rollback);
128 static int in6_mc_get(struct ifnet *, const struct in6_addr *,
129 struct in6_multi **);
130 static int in6m_get_source(struct in6_multi *inm,
131 const struct in6_addr *addr, const int noalloc,
132 struct ip6_msource **pims);
133 static int in6m_is_ifp_detached(const struct in6_multi *);
134 static int in6m_merge(struct in6_multi *, /*const*/ struct in6_mfilter *);
135 static void in6m_reap(struct in6_multi *);
136 static struct ip6_moptions *
137 in6p_findmoptions(struct inpcb *);
138 static int in6p_get_source_filters(struct inpcb *, struct sockopt *);
139 static int in6p_lookup_v4addr(struct ipv6_mreq *, struct ip_mreq *);
140 static int in6p_join_group(struct inpcb *, struct sockopt *);
141 static int in6p_leave_group(struct inpcb *, struct sockopt *);
142 static struct ifnet *
143 in6p_lookup_mcast_ifp(const struct inpcb *,
144 const struct sockaddr_in6 *);
145 static int in6p_block_unblock_source(struct inpcb *, struct sockopt *);
146 static int in6p_set_multicast_if(struct inpcb *, struct sockopt *);
147 static int in6p_set_source_filters(struct inpcb *, struct sockopt *);
148 static int sysctl_ip6_mcast_filters SYSCTL_HANDLER_ARGS;
149 static __inline__ int ip6_msource_cmp(const struct ip6_msource *,
150 const struct ip6_msource *);
151
152 SYSCTL_DECL(_net_inet6_ip6); /* XXX Not in any common header. */
153
154 SYSCTL_NODE(_net_inet6_ip6, OID_AUTO, mcast, CTLFLAG_RW | CTLFLAG_LOCKED, 0, "IPv6 multicast");
155
156 static unsigned long in6_mcast_maxgrpsrc = IPV6_MAX_GROUP_SRC_FILTER;
157 SYSCTL_LONG(_net_inet6_ip6_mcast, OID_AUTO, maxgrpsrc,
158 CTLFLAG_RW | CTLFLAG_LOCKED, &in6_mcast_maxgrpsrc,
159 "Max source filters per group");
160
161 static unsigned long in6_mcast_maxsocksrc = IPV6_MAX_SOCK_SRC_FILTER;
162 SYSCTL_LONG(_net_inet6_ip6_mcast, OID_AUTO, maxsocksrc,
163 CTLFLAG_RW | CTLFLAG_LOCKED, &in6_mcast_maxsocksrc,
164 "Max source filters per socket");
165
166 int in6_mcast_loop = IPV6_DEFAULT_MULTICAST_LOOP;
167 SYSCTL_INT(_net_inet6_ip6_mcast, OID_AUTO, loop, CTLFLAG_RW | CTLFLAG_LOCKED,
168 &in6_mcast_loop, 0, "Loopback multicast datagrams by default");
169
170 SYSCTL_NODE(_net_inet6_ip6_mcast, OID_AUTO, filters,
171 CTLFLAG_RD | CTLFLAG_LOCKED, sysctl_ip6_mcast_filters,
172 "Per-interface stack-wide source filters");
173
174 RB_GENERATE_PREV(ip6_msource_tree, ip6_msource, im6s_link, ip6_msource_cmp);
175
176 #define IN6M_TRACE_HIST_SIZE 32 /* size of trace history */
177
178 /* For gdb */
179 __private_extern__ unsigned int in6m_trace_hist_size = IN6M_TRACE_HIST_SIZE;
180
181 struct in6_multi_dbg {
182 struct in6_multi in6m; /* in6_multi */
183 u_int16_t in6m_refhold_cnt; /* # of ref */
184 u_int16_t in6m_refrele_cnt; /* # of rele */
185 /*
186 * Circular lists of in6m_addref and in6m_remref callers.
187 */
188 ctrace_t in6m_refhold[IN6M_TRACE_HIST_SIZE];
189 ctrace_t in6m_refrele[IN6M_TRACE_HIST_SIZE];
190 /*
191 * Trash list linkage
192 */
193 TAILQ_ENTRY(in6_multi_dbg) in6m_trash_link;
194 };
195
196 /* List of trash in6_multi entries protected by in6m_trash_lock */
197 static TAILQ_HEAD(, in6_multi_dbg) in6m_trash_head;
198 static decl_lck_mtx_data(, in6m_trash_lock);
199
200 #if DEBUG
201 static unsigned int in6m_debug = 1; /* debugging (enabled) */
202 #else
203 static unsigned int in6m_debug; /* debugging (disabled) */
204 #endif /* !DEBUG */
205 static unsigned int in6m_size; /* size of zone element */
206 static struct zone *in6m_zone; /* zone for in6_multi */
207
208 #define IN6M_ZONE_MAX 64 /* maximum elements in zone */
209 #define IN6M_ZONE_NAME "in6_multi" /* zone name */
210
211 static unsigned int imm_size; /* size of zone element */
212 static struct zone *imm_zone; /* zone for in6_multi_mship */
213
214 #define IMM_ZONE_MAX 64 /* maximum elements in zone */
215 #define IMM_ZONE_NAME "in6_multi_mship" /* zone name */
216
217 #define IP6MS_ZONE_MAX 64 /* maximum elements in zone */
218 #define IP6MS_ZONE_NAME "ip6_msource" /* zone name */
219
220 static unsigned int ip6ms_size; /* size of zone element */
221 static struct zone *ip6ms_zone; /* zone for ip6_msource */
222
223 #define IN6MS_ZONE_MAX 64 /* maximum elements in zone */
224 #define IN6MS_ZONE_NAME "in6_msource" /* zone name */
225
226 static unsigned int in6ms_size; /* size of zone element */
227 static struct zone *in6ms_zone; /* zone for in6_msource */
228
229 /* Lock group and attribute for in6_multihead_lock lock */
230 static lck_attr_t *in6_multihead_lock_attr;
231 static lck_grp_t *in6_multihead_lock_grp;
232 static lck_grp_attr_t *in6_multihead_lock_grp_attr;
233
234 static decl_lck_rw_data(, in6_multihead_lock);
235 struct in6_multihead in6_multihead;
236
237 static struct in6_multi *in6_multi_alloc(int);
238 static void in6_multi_free(struct in6_multi *);
239 static void in6_multi_attach(struct in6_multi *);
240 static struct in6_multi_mship *in6_multi_mship_alloc(int);
241 static void in6_multi_mship_free(struct in6_multi_mship *);
242 static void in6m_trace(struct in6_multi *, int);
243
244 static struct ip6_msource *ip6ms_alloc(int);
245 static void ip6ms_free(struct ip6_msource *);
246 static struct in6_msource *in6ms_alloc(int);
247 static void in6ms_free(struct in6_msource *);
248
249 #define IM6O_CAST_TO_NONCONST(x) ((struct ip6_moptions *)(void *)(uintptr_t)x)
250 #define IN6M_CAST_TO_NONCONST(x) ((struct in6_multi *)(void *)(uintptr_t)x)
251
252 /*
253 * IPv6 source tree comparison function.
254 *
255 * An ordered predicate is necessary; bcmp() is not documented to return
256 * an indication of order, memcmp() is, and is an ISO C99 requirement.
257 */
258 static __inline int
259 ip6_msource_cmp(const struct ip6_msource *a, const struct ip6_msource *b)
260 {
261 return (memcmp(&a->im6s_addr, &b->im6s_addr, sizeof(struct in6_addr)));
262 }
263
264 /*
265 * Inline function which wraps assertions for a valid ifp.
266 */
267 static __inline__ int
268 in6m_is_ifp_detached(const struct in6_multi *inm)
269 {
270 VERIFY(inm->in6m_ifma != NULL);
271 VERIFY(inm->in6m_ifp == inm->in6m_ifma->ifma_ifp);
272
273 return (!ifnet_is_attached(inm->in6m_ifp, 0));
274 }
275
276 /*
277 * Initialize an in6_mfilter structure to a known state at t0, t1
278 * with an empty source filter list.
279 */
280 static __inline__ void
281 im6f_init(struct in6_mfilter *imf, const int st0, const int st1)
282 {
283 memset(imf, 0, sizeof(struct in6_mfilter));
284 RB_INIT(&imf->im6f_sources);
285 imf->im6f_st[0] = st0;
286 imf->im6f_st[1] = st1;
287 }
288
289 /*
290 * Resize the ip6_moptions vector to the next power-of-two minus 1.
291 */
292 static int
293 im6o_grow(struct ip6_moptions *imo, size_t newmax)
294 {
295 struct in6_multi **nmships;
296 struct in6_multi **omships;
297 struct in6_mfilter *nmfilters;
298 struct in6_mfilter *omfilters;
299 size_t idx;
300 size_t oldmax;
301
302 IM6O_LOCK_ASSERT_HELD(imo);
303
304 nmships = NULL;
305 nmfilters = NULL;
306 omships = imo->im6o_membership;
307 omfilters = imo->im6o_mfilters;
308 oldmax = imo->im6o_max_memberships;
309 if (newmax == 0)
310 newmax = ((oldmax + 1) * 2) - 1;
311
312 if (newmax > IPV6_MAX_MEMBERSHIPS)
313 return (ETOOMANYREFS);
314
315 if ((nmships = (struct in6_multi **)_REALLOC(omships,
316 sizeof (struct in6_multi *) * newmax, M_IP6MOPTS,
317 M_WAITOK | M_ZERO)) == NULL)
318 return (ENOMEM);
319
320 imo->im6o_membership = nmships;
321
322 if ((nmfilters = (struct in6_mfilter *)_REALLOC(omfilters,
323 sizeof (struct in6_mfilter) * newmax, M_IN6MFILTER,
324 M_WAITOK | M_ZERO)) == NULL)
325 return (ENOMEM);
326
327 imo->im6o_mfilters = nmfilters;
328
329 /* Initialize newly allocated source filter heads. */
330 for (idx = oldmax; idx < newmax; idx++)
331 im6f_init(&nmfilters[idx], MCAST_UNDEFINED, MCAST_EXCLUDE);
332
333 imo->im6o_max_memberships = newmax;
334
335 return (0);
336 }
337
338 /*
339 * Find an IPv6 multicast group entry for this ip6_moptions instance
340 * which matches the specified group, and optionally an interface.
341 * Return its index into the array, or -1 if not found.
342 */
343 static size_t
344 im6o_match_group(const struct ip6_moptions *imo, const struct ifnet *ifp,
345 const struct sockaddr *group)
346 {
347 const struct sockaddr_in6 *gsin6;
348 struct in6_multi *pinm;
349 int idx;
350 int nmships;
351
352 IM6O_LOCK_ASSERT_HELD(IM6O_CAST_TO_NONCONST(imo));
353
354 gsin6 = (struct sockaddr_in6 *)(uintptr_t)(size_t)group;
355
356 /* The im6o_membership array may be lazy allocated. */
357 if (imo->im6o_membership == NULL || imo->im6o_num_memberships == 0)
358 return (-1);
359
360 nmships = imo->im6o_num_memberships;
361 for (idx = 0; idx < nmships; idx++) {
362 pinm = imo->im6o_membership[idx];
363 if (pinm == NULL)
364 continue;
365 IN6M_LOCK(pinm);
366 if ((ifp == NULL || (pinm->in6m_ifp == ifp)) &&
367 IN6_ARE_ADDR_EQUAL(&pinm->in6m_addr,
368 &gsin6->sin6_addr)) {
369 IN6M_UNLOCK(pinm);
370 break;
371 }
372 IN6M_UNLOCK(pinm);
373 }
374 if (idx >= nmships)
375 idx = -1;
376
377 return (idx);
378 }
379
380 /*
381 * Find an IPv6 multicast source entry for this imo which matches
382 * the given group index for this socket, and source address.
383 *
384 * XXX TODO: The scope ID, if present in src, is stripped before
385 * any comparison. We SHOULD enforce scope/zone checks where the source
386 * filter entry has a link scope.
387 *
388 * NOTE: This does not check if the entry is in-mode, merely if
389 * it exists, which may not be the desired behaviour.
390 */
391 static struct in6_msource *
392 im6o_match_source(const struct ip6_moptions *imo, const size_t gidx,
393 const struct sockaddr *src)
394 {
395 struct ip6_msource find;
396 struct in6_mfilter *imf;
397 struct ip6_msource *ims;
398 const sockunion_t *psa;
399
400 IM6O_LOCK_ASSERT_HELD(IM6O_CAST_TO_NONCONST(imo));
401
402 VERIFY(src->sa_family == AF_INET6);
403 VERIFY(gidx != (size_t)-1 && gidx < imo->im6o_num_memberships);
404
405 /* The im6o_mfilters array may be lazy allocated. */
406 if (imo->im6o_mfilters == NULL)
407 return (NULL);
408 imf = &imo->im6o_mfilters[gidx];
409
410 psa = (sockunion_t *)(uintptr_t)(size_t)src;
411 find.im6s_addr = psa->sin6.sin6_addr;
412 in6_clearscope(&find.im6s_addr); /* XXX */
413 ims = RB_FIND(ip6_msource_tree, &imf->im6f_sources, &find);
414
415 return ((struct in6_msource *)ims);
416 }
417
418 /*
419 * Perform filtering for multicast datagrams on a socket by group and source.
420 *
421 * Returns 0 if a datagram should be allowed through, or various error codes
422 * if the socket was not a member of the group, or the source was muted, etc.
423 */
424 int
425 im6o_mc_filter(const struct ip6_moptions *imo, const struct ifnet *ifp,
426 const struct sockaddr *group, const struct sockaddr *src)
427 {
428 size_t gidx;
429 struct in6_msource *ims;
430 int mode;
431
432 IM6O_LOCK_ASSERT_HELD(IM6O_CAST_TO_NONCONST(imo));
433 VERIFY(ifp != NULL);
434
435 gidx = im6o_match_group(imo, ifp, group);
436 if (gidx == (size_t)-1)
437 return (MCAST_NOTGMEMBER);
438
439 /*
440 * Check if the source was included in an (S,G) join.
441 * Allow reception on exclusive memberships by default,
442 * reject reception on inclusive memberships by default.
443 * Exclude source only if an in-mode exclude filter exists.
444 * Include source only if an in-mode include filter exists.
445 * NOTE: We are comparing group state here at MLD t1 (now)
446 * with socket-layer t0 (since last downcall).
447 */
448 mode = imo->im6o_mfilters[gidx].im6f_st[1];
449 ims = im6o_match_source(imo, gidx, src);
450
451 if ((ims == NULL && mode == MCAST_INCLUDE) ||
452 (ims != NULL && ims->im6sl_st[0] != mode))
453 return (MCAST_NOTSMEMBER);
454
455 return (MCAST_PASS);
456 }
457
458 /*
459 * Find and return a reference to an in6_multi record for (ifp, group),
460 * and bump its reference count.
461 * If one does not exist, try to allocate it, and update link-layer multicast
462 * filters on ifp to listen for group.
463 * Assumes the IN6_MULTI lock is held across the call.
464 * Return 0 if successful, otherwise return an appropriate error code.
465 */
466 static int
467 in6_mc_get(struct ifnet *ifp, const struct in6_addr *group,
468 struct in6_multi **pinm)
469 {
470 struct sockaddr_in6 gsin6;
471 struct ifmultiaddr *ifma;
472 struct in6_multi *inm;
473 int error;
474
475 *pinm = NULL;
476
477 in6_multihead_lock_shared();
478 IN6_LOOKUP_MULTI(group, ifp, inm);
479 if (inm != NULL) {
480 IN6M_LOCK(inm);
481 VERIFY(inm->in6m_reqcnt >= 1);
482 inm->in6m_reqcnt++;
483 VERIFY(inm->in6m_reqcnt != 0);
484 *pinm = inm;
485 IN6M_UNLOCK(inm);
486 in6_multihead_lock_done();
487 /*
488 * We already joined this group; return the in6m
489 * with a refcount held (via lookup) for caller.
490 */
491 return (0);
492 }
493 in6_multihead_lock_done();
494
495 memset(&gsin6, 0, sizeof(gsin6));
496 gsin6.sin6_family = AF_INET6;
497 gsin6.sin6_len = sizeof(struct sockaddr_in6);
498 gsin6.sin6_addr = *group;
499
500 /*
501 * Check if a link-layer group is already associated
502 * with this network-layer group on the given ifnet.
503 */
504 error = if_addmulti(ifp, (struct sockaddr *)&gsin6, &ifma);
505 if (error != 0)
506 return (error);
507
508 /*
509 * See comments in in6m_remref() for access to ifma_protospec.
510 */
511 in6_multihead_lock_exclusive();
512 IFMA_LOCK(ifma);
513 if ((inm = ifma->ifma_protospec) != NULL) {
514 VERIFY(ifma->ifma_addr != NULL);
515 VERIFY(ifma->ifma_addr->sa_family == AF_INET6);
516 IN6M_ADDREF(inm); /* for caller */
517 IFMA_UNLOCK(ifma);
518 IN6M_LOCK(inm);
519 VERIFY(inm->in6m_ifma == ifma);
520 VERIFY(inm->in6m_ifp == ifp);
521 VERIFY(IN6_ARE_ADDR_EQUAL(&inm->in6m_addr, group));
522 if (inm->in6m_debug & IFD_ATTACHED) {
523 VERIFY(inm->in6m_reqcnt >= 1);
524 inm->in6m_reqcnt++;
525 VERIFY(inm->in6m_reqcnt != 0);
526 *pinm = inm;
527 IN6M_UNLOCK(inm);
528 in6_multihead_lock_done();
529 IFMA_REMREF(ifma);
530 /*
531 * We lost the race with another thread doing
532 * in6_mc_get(); since this group has already
533 * been joined; return the inm with a refcount
534 * held for caller.
535 */
536 return (0);
537 }
538 /*
539 * We lost the race with another thread doing in6_delmulti();
540 * the inm referring to the ifma has been detached, thus we
541 * reattach it back to the in6_multihead list, and return the
542 * inm with a refcount held for the caller.
543 */
544 in6_multi_attach(inm);
545 VERIFY((inm->in6m_debug &
546 (IFD_ATTACHED | IFD_TRASHED)) == IFD_ATTACHED);
547 *pinm = inm;
548 IN6M_UNLOCK(inm);
549 in6_multihead_lock_done();
550 IFMA_REMREF(ifma);
551 return (0);
552 }
553 IFMA_UNLOCK(ifma);
554
555 /*
556 * A new in6_multi record is needed; allocate and initialize it.
557 * We DO NOT perform an MLD join as the in6_ layer may need to
558 * push an initial source list down to MLD to support SSM.
559 *
560 * The initial source filter state is INCLUDE, {} as per the RFC.
561 * Pending state-changes per group are subject to a bounds check.
562 */
563 inm = in6_multi_alloc(M_WAITOK);
564 if (inm == NULL) {
565 in6_multihead_lock_done();
566 IFMA_REMREF(ifma);
567 return (ENOMEM);
568 }
569 IN6M_LOCK(inm);
570 inm->in6m_addr = *group;
571 inm->in6m_ifp = ifp;
572 inm->in6m_mli = MLD_IFINFO(ifp);
573 VERIFY(inm->in6m_mli != NULL);
574 MLI_ADDREF(inm->in6m_mli);
575 inm->in6m_ifma = ifma; /* keep refcount from if_addmulti() */
576 inm->in6m_state = MLD_NOT_MEMBER;
577 /*
578 * Pending state-changes per group are subject to a bounds check.
579 */
580 inm->in6m_scq.ifq_maxlen = MLD_MAX_STATE_CHANGES;
581 inm->in6m_st[0].iss_fmode = MCAST_UNDEFINED;
582 inm->in6m_st[1].iss_fmode = MCAST_UNDEFINED;
583 RB_INIT(&inm->in6m_srcs);
584 *pinm = inm;
585 in6_multi_attach(inm);
586 VERIFY((inm->in6m_debug &
587 (IFD_ATTACHED | IFD_TRASHED)) == IFD_ATTACHED);
588 IN6M_ADDREF_LOCKED(inm); /* for caller */
589 IN6M_UNLOCK(inm);
590
591 IFMA_LOCK(ifma);
592 VERIFY(ifma->ifma_protospec == NULL);
593 ifma->ifma_protospec = inm;
594 IFMA_UNLOCK(ifma);
595 in6_multihead_lock_done();
596
597 return (0);
598 }
599
600 /*
601 * Clear recorded source entries for a group.
602 * Used by the MLD code. Caller must hold the IN6_MULTI lock.
603 * FIXME: Should reap.
604 */
605 void
606 in6m_clear_recorded(struct in6_multi *inm)
607 {
608 struct ip6_msource *ims;
609
610 IN6M_LOCK_ASSERT_HELD(inm);
611
612 RB_FOREACH(ims, ip6_msource_tree, &inm->in6m_srcs) {
613 if (ims->im6s_stp) {
614 ims->im6s_stp = 0;
615 --inm->in6m_st[1].iss_rec;
616 }
617 }
618 VERIFY(inm->in6m_st[1].iss_rec == 0);
619 }
620
621 /*
622 * Record a source as pending for a Source-Group MLDv2 query.
623 * This lives here as it modifies the shared tree.
624 *
625 * inm is the group descriptor.
626 * naddr is the address of the source to record in network-byte order.
627 *
628 * If the net.inet6.mld.sgalloc sysctl is non-zero, we will
629 * lazy-allocate a source node in response to an SG query.
630 * Otherwise, no allocation is performed. This saves some memory
631 * with the trade-off that the source will not be reported to the
632 * router if joined in the window between the query response and
633 * the group actually being joined on the local host.
634 *
635 * VIMAGE: XXX: Currently the mld_sgalloc feature has been removed.
636 * This turns off the allocation of a recorded source entry if
637 * the group has not been joined.
638 *
639 * Return 0 if the source didn't exist or was already marked as recorded.
640 * Return 1 if the source was marked as recorded by this function.
641 * Return <0 if any error occured (negated errno code).
642 */
643 int
644 in6m_record_source(struct in6_multi *inm, const struct in6_addr *addr)
645 {
646 struct ip6_msource find;
647 struct ip6_msource *ims, *nims;
648
649 IN6M_LOCK_ASSERT_HELD(inm);
650
651 find.im6s_addr = *addr;
652 ims = RB_FIND(ip6_msource_tree, &inm->in6m_srcs, &find);
653 if (ims && ims->im6s_stp)
654 return (0);
655 if (ims == NULL) {
656 if (inm->in6m_nsrc == in6_mcast_maxgrpsrc)
657 return (-ENOSPC);
658 nims = ip6ms_alloc(M_WAITOK);
659 if (nims == NULL)
660 return (-ENOMEM);
661 nims->im6s_addr = find.im6s_addr;
662 RB_INSERT(ip6_msource_tree, &inm->in6m_srcs, nims);
663 ++inm->in6m_nsrc;
664 ims = nims;
665 }
666
667 /*
668 * Mark the source as recorded and update the recorded
669 * source count.
670 */
671 ++ims->im6s_stp;
672 ++inm->in6m_st[1].iss_rec;
673
674 return (1);
675 }
676
677 /*
678 * Return a pointer to an in6_msource owned by an in6_mfilter,
679 * given its source address.
680 * Lazy-allocate if needed. If this is a new entry its filter state is
681 * undefined at t0.
682 *
683 * imf is the filter set being modified.
684 * addr is the source address.
685 *
686 * Caller is expected to be holding im6o_lock.
687 */
688 static int
689 im6f_get_source(struct in6_mfilter *imf, const struct sockaddr_in6 *psin,
690 struct in6_msource **plims)
691 {
692 struct ip6_msource find;
693 struct ip6_msource *ims;
694 struct in6_msource *lims;
695 int error;
696
697 error = 0;
698 ims = NULL;
699 lims = NULL;
700
701 find.im6s_addr = psin->sin6_addr;
702 ims = RB_FIND(ip6_msource_tree, &imf->im6f_sources, &find);
703 lims = (struct in6_msource *)ims;
704 if (lims == NULL) {
705 if (imf->im6f_nsrc == in6_mcast_maxsocksrc)
706 return (ENOSPC);
707 lims = in6ms_alloc(M_WAITOK);
708 if (lims == NULL)
709 return (ENOMEM);
710 lims->im6s_addr = find.im6s_addr;
711 lims->im6sl_st[0] = MCAST_UNDEFINED;
712 RB_INSERT(ip6_msource_tree, &imf->im6f_sources,
713 (struct ip6_msource *)lims);
714 ++imf->im6f_nsrc;
715 }
716
717 *plims = lims;
718
719 return (error);
720 }
721
722 /*
723 * Graft a source entry into an existing socket-layer filter set,
724 * maintaining any required invariants and checking allocations.
725 *
726 * The source is marked as being in the new filter mode at t1.
727 *
728 * Return the pointer to the new node, otherwise return NULL.
729 *
730 * Caller is expected to be holding im6o_lock.
731 */
732 static struct in6_msource *
733 im6f_graft(struct in6_mfilter *imf, const uint8_t st1,
734 const struct sockaddr_in6 *psin)
735 {
736 struct in6_msource *lims;
737
738 lims = in6ms_alloc(M_WAITOK);
739 if (lims == NULL)
740 return (NULL);
741 lims->im6s_addr = psin->sin6_addr;
742 lims->im6sl_st[0] = MCAST_UNDEFINED;
743 lims->im6sl_st[1] = st1;
744 RB_INSERT(ip6_msource_tree, &imf->im6f_sources,
745 (struct ip6_msource *)lims);
746 ++imf->im6f_nsrc;
747
748 return (lims);
749 }
750
751 /*
752 * Prune a source entry from an existing socket-layer filter set,
753 * maintaining any required invariants and checking allocations.
754 *
755 * The source is marked as being left at t1, it is not freed.
756 *
757 * Return 0 if no error occurred, otherwise return an errno value.
758 *
759 * Caller is expected to be holding im6o_lock.
760 */
761 static int
762 im6f_prune(struct in6_mfilter *imf, const struct sockaddr_in6 *psin)
763 {
764 struct ip6_msource find;
765 struct ip6_msource *ims;
766 struct in6_msource *lims;
767
768 find.im6s_addr = psin->sin6_addr;
769 ims = RB_FIND(ip6_msource_tree, &imf->im6f_sources, &find);
770 if (ims == NULL)
771 return (ENOENT);
772 lims = (struct in6_msource *)ims;
773 lims->im6sl_st[1] = MCAST_UNDEFINED;
774 return (0);
775 }
776
777 /*
778 * Revert socket-layer filter set deltas at t1 to t0 state.
779 *
780 * Caller is expected to be holding im6o_lock.
781 */
782 static void
783 im6f_rollback(struct in6_mfilter *imf)
784 {
785 struct ip6_msource *ims, *tims;
786 struct in6_msource *lims;
787
788 RB_FOREACH_SAFE(ims, ip6_msource_tree, &imf->im6f_sources, tims) {
789 lims = (struct in6_msource *)ims;
790 if (lims->im6sl_st[0] == lims->im6sl_st[1]) {
791 /* no change at t1 */
792 continue;
793 } else if (lims->im6sl_st[0] != MCAST_UNDEFINED) {
794 /* revert change to existing source at t1 */
795 lims->im6sl_st[1] = lims->im6sl_st[0];
796 } else {
797 /* revert source added t1 */
798 MLD_PRINTF(("%s: free in6ms %p\n", __func__, lims));
799 RB_REMOVE(ip6_msource_tree, &imf->im6f_sources, ims);
800 in6ms_free(lims);
801 imf->im6f_nsrc--;
802 }
803 }
804 imf->im6f_st[1] = imf->im6f_st[0];
805 }
806
807 /*
808 * Mark socket-layer filter set as INCLUDE {} at t1.
809 *
810 * Caller is expected to be holding im6o_lock.
811 */
812 void
813 im6f_leave(struct in6_mfilter *imf)
814 {
815 struct ip6_msource *ims;
816 struct in6_msource *lims;
817
818 RB_FOREACH(ims, ip6_msource_tree, &imf->im6f_sources) {
819 lims = (struct in6_msource *)ims;
820 lims->im6sl_st[1] = MCAST_UNDEFINED;
821 }
822 imf->im6f_st[1] = MCAST_INCLUDE;
823 }
824
825 /*
826 * Mark socket-layer filter set deltas as committed.
827 *
828 * Caller is expected to be holding im6o_lock.
829 */
830 static void
831 im6f_commit(struct in6_mfilter *imf)
832 {
833 struct ip6_msource *ims;
834 struct in6_msource *lims;
835
836 RB_FOREACH(ims, ip6_msource_tree, &imf->im6f_sources) {
837 lims = (struct in6_msource *)ims;
838 lims->im6sl_st[0] = lims->im6sl_st[1];
839 }
840 imf->im6f_st[0] = imf->im6f_st[1];
841 }
842
843 /*
844 * Reap unreferenced sources from socket-layer filter set.
845 *
846 * Caller is expected to be holding im6o_lock.
847 */
848 static void
849 im6f_reap(struct in6_mfilter *imf)
850 {
851 struct ip6_msource *ims, *tims;
852 struct in6_msource *lims;
853
854 RB_FOREACH_SAFE(ims, ip6_msource_tree, &imf->im6f_sources, tims) {
855 lims = (struct in6_msource *)ims;
856 if ((lims->im6sl_st[0] == MCAST_UNDEFINED) &&
857 (lims->im6sl_st[1] == MCAST_UNDEFINED)) {
858 MLD_PRINTF(("%s: free in6ms %p\n", __func__, lims));
859 RB_REMOVE(ip6_msource_tree, &imf->im6f_sources, ims);
860 in6ms_free(lims);
861 imf->im6f_nsrc--;
862 }
863 }
864 }
865
866 /*
867 * Purge socket-layer filter set.
868 *
869 * Caller is expected to be holding im6o_lock.
870 */
871 void
872 im6f_purge(struct in6_mfilter *imf)
873 {
874 struct ip6_msource *ims, *tims;
875 struct in6_msource *lims;
876
877 RB_FOREACH_SAFE(ims, ip6_msource_tree, &imf->im6f_sources, tims) {
878 lims = (struct in6_msource *)ims;
879 MLD_PRINTF(("%s: free in6ms %p\n", __func__, lims));
880 RB_REMOVE(ip6_msource_tree, &imf->im6f_sources, ims);
881 in6ms_free(lims);
882 imf->im6f_nsrc--;
883 }
884 imf->im6f_st[0] = imf->im6f_st[1] = MCAST_UNDEFINED;
885 VERIFY(RB_EMPTY(&imf->im6f_sources));
886 }
887
888 /*
889 * Look up a source filter entry for a multicast group.
890 *
891 * inm is the group descriptor to work with.
892 * addr is the IPv6 address to look up.
893 * noalloc may be non-zero to suppress allocation of sources.
894 * *pims will be set to the address of the retrieved or allocated source.
895 *
896 * Return 0 if successful, otherwise return a non-zero error code.
897 */
898 static int
899 in6m_get_source(struct in6_multi *inm, const struct in6_addr *addr,
900 const int noalloc, struct ip6_msource **pims)
901 {
902 struct ip6_msource find;
903 struct ip6_msource *ims, *nims;
904
905 IN6M_LOCK_ASSERT_HELD(inm);
906
907 find.im6s_addr = *addr;
908 ims = RB_FIND(ip6_msource_tree, &inm->in6m_srcs, &find);
909 if (ims == NULL && !noalloc) {
910 if (inm->in6m_nsrc == in6_mcast_maxgrpsrc)
911 return (ENOSPC);
912 nims = ip6ms_alloc(M_WAITOK);
913 if (nims == NULL)
914 return (ENOMEM);
915 nims->im6s_addr = *addr;
916 RB_INSERT(ip6_msource_tree, &inm->in6m_srcs, nims);
917 ++inm->in6m_nsrc;
918 ims = nims;
919 MLD_PRINTF(("%s: allocated %s as %p\n", __func__,
920 ip6_sprintf(addr), ims));
921 }
922
923 *pims = ims;
924 return (0);
925 }
926
927 /*
928 * Helper function to derive the filter mode on a source entry
929 * from its internal counters. Predicates are:
930 * A source is only excluded if all listeners exclude it.
931 * A source is only included if no listeners exclude it,
932 * and at least one listener includes it.
933 * May be used by ifmcstat(8).
934 */
935 uint8_t
936 im6s_get_mode(const struct in6_multi *inm, const struct ip6_msource *ims,
937 uint8_t t)
938 {
939 IN6M_LOCK_ASSERT_HELD(IN6M_CAST_TO_NONCONST(inm));
940
941 t = !!t;
942 if (inm->in6m_st[t].iss_ex > 0 &&
943 inm->in6m_st[t].iss_ex == ims->im6s_st[t].ex)
944 return (MCAST_EXCLUDE);
945 else if (ims->im6s_st[t].in > 0 && ims->im6s_st[t].ex == 0)
946 return (MCAST_INCLUDE);
947 return (MCAST_UNDEFINED);
948 }
949
950 /*
951 * Merge socket-layer source into MLD-layer source.
952 * If rollback is non-zero, perform the inverse of the merge.
953 */
954 static void
955 im6s_merge(struct ip6_msource *ims, const struct in6_msource *lims,
956 const int rollback)
957 {
958 int n = rollback ? -1 : 1;
959
960 if (lims->im6sl_st[0] == MCAST_EXCLUDE) {
961 MLD_PRINTF(("%s: t1 ex -= %d on %s\n", __func__, n,
962 ip6_sprintf(&lims->im6s_addr)));
963 ims->im6s_st[1].ex -= n;
964 } else if (lims->im6sl_st[0] == MCAST_INCLUDE) {
965 MLD_PRINTF(("%s: t1 in -= %d on %s\n", __func__, n,
966 ip6_sprintf(&lims->im6s_addr)));
967 ims->im6s_st[1].in -= n;
968 }
969
970 if (lims->im6sl_st[1] == MCAST_EXCLUDE) {
971 MLD_PRINTF(("%s: t1 ex += %d on %s\n", __func__, n,
972 ip6_sprintf(&lims->im6s_addr)));
973 ims->im6s_st[1].ex += n;
974 } else if (lims->im6sl_st[1] == MCAST_INCLUDE) {
975 MLD_PRINTF(("%s: t1 in += %d on %s\n", __func__, n,
976 ip6_sprintf(&lims->im6s_addr)));
977 ims->im6s_st[1].in += n;
978 }
979 }
980
981 /*
982 * Atomically update the global in6_multi state, when a membership's
983 * filter list is being updated in any way.
984 *
985 * imf is the per-inpcb-membership group filter pointer.
986 * A fake imf may be passed for in-kernel consumers.
987 *
988 * XXX This is a candidate for a set-symmetric-difference style loop
989 * which would eliminate the repeated lookup from root of ims nodes,
990 * as they share the same key space.
991 *
992 * If any error occurred this function will back out of refcounts
993 * and return a non-zero value.
994 */
995 static int
996 in6m_merge(struct in6_multi *inm, /*const*/ struct in6_mfilter *imf)
997 {
998 struct ip6_msource *ims, *nims;
999 struct in6_msource *lims;
1000 int schanged, error;
1001 int nsrc0, nsrc1;
1002
1003 IN6M_LOCK_ASSERT_HELD(inm);
1004
1005 schanged = 0;
1006 error = 0;
1007 nsrc1 = nsrc0 = 0;
1008
1009 /*
1010 * Update the source filters first, as this may fail.
1011 * Maintain count of in-mode filters at t0, t1. These are
1012 * used to work out if we transition into ASM mode or not.
1013 * Maintain a count of source filters whose state was
1014 * actually modified by this operation.
1015 */
1016 RB_FOREACH(ims, ip6_msource_tree, &imf->im6f_sources) {
1017 lims = (struct in6_msource *)ims;
1018 if (lims->im6sl_st[0] == imf->im6f_st[0]) nsrc0++;
1019 if (lims->im6sl_st[1] == imf->im6f_st[1]) nsrc1++;
1020 if (lims->im6sl_st[0] == lims->im6sl_st[1]) continue;
1021 error = in6m_get_source(inm, &lims->im6s_addr, 0, &nims);
1022 ++schanged;
1023 if (error)
1024 break;
1025 im6s_merge(nims, lims, 0);
1026 }
1027 if (error) {
1028 struct ip6_msource *bims;
1029
1030 RB_FOREACH_REVERSE_FROM(ims, ip6_msource_tree, nims) {
1031 lims = (struct in6_msource *)ims;
1032 if (lims->im6sl_st[0] == lims->im6sl_st[1])
1033 continue;
1034 (void) in6m_get_source(inm, &lims->im6s_addr, 1, &bims);
1035 if (bims == NULL)
1036 continue;
1037 im6s_merge(bims, lims, 1);
1038 }
1039 goto out_reap;
1040 }
1041
1042 MLD_PRINTF(("%s: imf filters in-mode: %d at t0, %d at t1\n",
1043 __func__, nsrc0, nsrc1));
1044
1045 /* Handle transition between INCLUDE {n} and INCLUDE {} on socket. */
1046 if (imf->im6f_st[0] == imf->im6f_st[1] &&
1047 imf->im6f_st[1] == MCAST_INCLUDE) {
1048 if (nsrc1 == 0) {
1049 MLD_PRINTF(("%s: --in on inm at t1\n", __func__));
1050 --inm->in6m_st[1].iss_in;
1051 }
1052 }
1053
1054 /* Handle filter mode transition on socket. */
1055 if (imf->im6f_st[0] != imf->im6f_st[1]) {
1056 MLD_PRINTF(("%s: imf transition %d to %d\n",
1057 __func__, imf->im6f_st[0], imf->im6f_st[1]));
1058
1059 if (imf->im6f_st[0] == MCAST_EXCLUDE) {
1060 MLD_PRINTF(("%s: --ex on inm at t1\n", __func__));
1061 --inm->in6m_st[1].iss_ex;
1062 } else if (imf->im6f_st[0] == MCAST_INCLUDE) {
1063 MLD_PRINTF(("%s: --in on inm at t1\n", __func__));
1064 --inm->in6m_st[1].iss_in;
1065 }
1066
1067 if (imf->im6f_st[1] == MCAST_EXCLUDE) {
1068 MLD_PRINTF(("%s: ex++ on inm at t1\n", __func__));
1069 inm->in6m_st[1].iss_ex++;
1070 } else if (imf->im6f_st[1] == MCAST_INCLUDE && nsrc1 > 0) {
1071 MLD_PRINTF(("%s: in++ on inm at t1\n", __func__));
1072 inm->in6m_st[1].iss_in++;
1073 }
1074 }
1075
1076 /*
1077 * Track inm filter state in terms of listener counts.
1078 * If there are any exclusive listeners, stack-wide
1079 * membership is exclusive.
1080 * Otherwise, if only inclusive listeners, stack-wide is inclusive.
1081 * If no listeners remain, state is undefined at t1,
1082 * and the MLD lifecycle for this group should finish.
1083 */
1084 if (inm->in6m_st[1].iss_ex > 0) {
1085 MLD_PRINTF(("%s: transition to EX\n", __func__));
1086 inm->in6m_st[1].iss_fmode = MCAST_EXCLUDE;
1087 } else if (inm->in6m_st[1].iss_in > 0) {
1088 MLD_PRINTF(("%s: transition to IN\n", __func__));
1089 inm->in6m_st[1].iss_fmode = MCAST_INCLUDE;
1090 } else {
1091 MLD_PRINTF(("%s: transition to UNDEF\n", __func__));
1092 inm->in6m_st[1].iss_fmode = MCAST_UNDEFINED;
1093 }
1094
1095 /* Decrement ASM listener count on transition out of ASM mode. */
1096 if (imf->im6f_st[0] == MCAST_EXCLUDE && nsrc0 == 0) {
1097 if ((imf->im6f_st[1] != MCAST_EXCLUDE) ||
1098 (imf->im6f_st[1] == MCAST_EXCLUDE && nsrc1 > 0)) {
1099 MLD_PRINTF(("%s: --asm on inm at t1\n", __func__));
1100 --inm->in6m_st[1].iss_asm;
1101 }
1102 }
1103
1104 /* Increment ASM listener count on transition to ASM mode. */
1105 if (imf->im6f_st[1] == MCAST_EXCLUDE && nsrc1 == 0) {
1106 MLD_PRINTF(("%s: asm++ on inm at t1\n", __func__));
1107 inm->in6m_st[1].iss_asm++;
1108 }
1109
1110 MLD_PRINTF(("%s: merged imf %p to inm %p\n", __func__, imf, inm));
1111 in6m_print(inm);
1112
1113 out_reap:
1114 if (schanged > 0) {
1115 MLD_PRINTF(("%s: sources changed; reaping\n", __func__));
1116 in6m_reap(inm);
1117 }
1118 return (error);
1119 }
1120
1121 /*
1122 * Mark an in6_multi's filter set deltas as committed.
1123 * Called by MLD after a state change has been enqueued.
1124 */
1125 void
1126 in6m_commit(struct in6_multi *inm)
1127 {
1128 struct ip6_msource *ims;
1129
1130 IN6M_LOCK_ASSERT_HELD(inm);
1131
1132 MLD_PRINTF(("%s: commit inm %p\n", __func__, inm));
1133 MLD_PRINTF(("%s: pre commit:\n", __func__));
1134 in6m_print(inm);
1135
1136 RB_FOREACH(ims, ip6_msource_tree, &inm->in6m_srcs) {
1137 ims->im6s_st[0] = ims->im6s_st[1];
1138 }
1139 inm->in6m_st[0] = inm->in6m_st[1];
1140 }
1141
1142 /*
1143 * Reap unreferenced nodes from an in6_multi's filter set.
1144 */
1145 static void
1146 in6m_reap(struct in6_multi *inm)
1147 {
1148 struct ip6_msource *ims, *tims;
1149
1150 IN6M_LOCK_ASSERT_HELD(inm);
1151
1152 RB_FOREACH_SAFE(ims, ip6_msource_tree, &inm->in6m_srcs, tims) {
1153 if (ims->im6s_st[0].ex > 0 || ims->im6s_st[0].in > 0 ||
1154 ims->im6s_st[1].ex > 0 || ims->im6s_st[1].in > 0 ||
1155 ims->im6s_stp != 0)
1156 continue;
1157 MLD_PRINTF(("%s: free ims %p\n", __func__, ims));
1158 RB_REMOVE(ip6_msource_tree, &inm->in6m_srcs, ims);
1159 ip6ms_free(ims);
1160 inm->in6m_nsrc--;
1161 }
1162 }
1163
1164 /*
1165 * Purge all source nodes from an in6_multi's filter set.
1166 */
1167 void
1168 in6m_purge(struct in6_multi *inm)
1169 {
1170 struct ip6_msource *ims, *tims;
1171
1172 IN6M_LOCK_ASSERT_HELD(inm);
1173
1174 RB_FOREACH_SAFE(ims, ip6_msource_tree, &inm->in6m_srcs, tims) {
1175 MLD_PRINTF(("%s: free ims %p\n", __func__, ims));
1176 RB_REMOVE(ip6_msource_tree, &inm->in6m_srcs, ims);
1177 ip6ms_free(ims);
1178 inm->in6m_nsrc--;
1179 }
1180 }
1181
1182 /*
1183 * Join a multicast address w/o sources.
1184 * KAME compatibility entry point.
1185 *
1186 */
1187 struct in6_multi_mship *
1188 in6_joingroup(struct ifnet *ifp, struct in6_addr *mcaddr,
1189 int *errorp, int delay)
1190 {
1191 struct in6_multi_mship *imm;
1192 int error;
1193
1194 *errorp = 0;
1195
1196 imm = in6_multi_mship_alloc(M_WAITOK);
1197 if (imm == NULL) {
1198 *errorp = ENOBUFS;
1199 return (NULL);
1200 }
1201
1202 delay = (delay * PR_SLOWHZ) / hz;
1203
1204 error = in6_mc_join(ifp, mcaddr, NULL, &imm->i6mm_maddr, delay);
1205 if (error) {
1206 *errorp = error;
1207 in6_multi_mship_free(imm);
1208 return (NULL);
1209 }
1210
1211 return (imm);
1212 }
1213
1214 /*
1215 * Leave a multicast address w/o sources.
1216 * KAME compatibility entry point.
1217 */
1218 int
1219 in6_leavegroup(struct in6_multi_mship *imm)
1220 {
1221 if (imm->i6mm_maddr != NULL) {
1222 in6_mc_leave(imm->i6mm_maddr, NULL);
1223 IN6M_REMREF(imm->i6mm_maddr);
1224 imm->i6mm_maddr = NULL;
1225 }
1226 in6_multi_mship_free(imm);
1227 return 0;
1228 }
1229
1230 /*
1231 * Join a multicast group; real entry point.
1232 *
1233 * Only preserves atomicity at inm level.
1234 * NOTE: imf argument cannot be const due to sys/tree.h limitations.
1235 *
1236 * If the MLD downcall fails, the group is not joined, and an error
1237 * code is returned.
1238 */
1239 int
1240 in6_mc_join(struct ifnet *ifp, const struct in6_addr *mcaddr,
1241 /*const*/ struct in6_mfilter *imf, struct in6_multi **pinm,
1242 const int delay)
1243 {
1244 struct in6_mfilter timf;
1245 struct in6_multi *inm = NULL;
1246 int error = 0;
1247
1248 /*
1249 * Sanity: Check scope zone ID was set for ifp, if and
1250 * only if group is scoped to an interface.
1251 */
1252 VERIFY(IN6_IS_ADDR_MULTICAST(mcaddr));
1253 if (IN6_IS_ADDR_MC_LINKLOCAL(mcaddr) ||
1254 IN6_IS_ADDR_MC_INTFACELOCAL(mcaddr)) {
1255 VERIFY(mcaddr->s6_addr16[1] != 0);
1256 }
1257
1258 MLD_PRINTF(("%s: join %s on %p(%s%d))\n", __func__,
1259 ip6_sprintf(mcaddr), ifp, ifp->if_name, ifp->if_unit));
1260
1261 *pinm = NULL;
1262
1263 /*
1264 * If no imf was specified (i.e. kernel consumer),
1265 * fake one up and assume it is an ASM join.
1266 */
1267 if (imf == NULL) {
1268 im6f_init(&timf, MCAST_UNDEFINED, MCAST_EXCLUDE);
1269 imf = &timf;
1270 }
1271
1272 error = in6_mc_get(ifp, mcaddr, &inm);
1273 if (error) {
1274 MLD_PRINTF(("%s: in6_mc_get() failure\n", __func__));
1275 return (error);
1276 }
1277
1278 MLD_PRINTF(("%s: merge inm state\n", __func__));
1279
1280 IN6M_LOCK(inm);
1281 error = in6m_merge(inm, imf);
1282 if (error) {
1283 MLD_PRINTF(("%s: failed to merge inm state\n", __func__));
1284 goto out_in6m_release;
1285 }
1286
1287 MLD_PRINTF(("%s: doing mld downcall\n", __func__));
1288 error = mld_change_state(inm, delay);
1289 if (error) {
1290 MLD_PRINTF(("%s: failed to update source\n", __func__));
1291 goto out_in6m_release;
1292 }
1293
1294 out_in6m_release:
1295 if (error) {
1296 MLD_PRINTF(("%s: dropping ref on %p\n", __func__, inm));
1297 IN6M_UNLOCK(inm);
1298 IN6M_REMREF(inm);
1299 } else {
1300 IN6M_UNLOCK(inm);
1301 *pinm = inm; /* keep refcount from in6_mc_get() */
1302 }
1303
1304 return (error);
1305 }
1306
1307 /*
1308 * Leave a multicast group; real entry point.
1309 * All source filters will be expunged.
1310 *
1311 * Only preserves atomicity at inm level.
1312 *
1313 * Holding the write lock for the INP which contains imf
1314 * is highly advisable. We can't assert for it as imf does not
1315 * contain a back-pointer to the owning inp.
1316 *
1317 * Note: This is not the same as in6m_release(*) as this function also
1318 * makes a state change downcall into MLD.
1319 */
1320 int
1321 in6_mc_leave(struct in6_multi *inm, /*const*/ struct in6_mfilter *imf)
1322 {
1323 struct in6_mfilter timf;
1324 int error, lastref;
1325
1326 error = 0;
1327
1328 IN6M_LOCK_ASSERT_NOTHELD(inm);
1329
1330 in6_multihead_lock_exclusive();
1331 IN6M_LOCK(inm);
1332
1333 MLD_PRINTF(("%s: leave inm %p, %s/%s%d, imf %p\n", __func__,
1334 inm, ip6_sprintf(&inm->in6m_addr),
1335 (in6m_is_ifp_detached(inm) ? "null" : inm->in6m_ifp->if_name),
1336 inm->in6m_ifp->if_unit, imf));
1337
1338 /*
1339 * If no imf was specified (i.e. kernel consumer),
1340 * fake one up and assume it is an ASM join.
1341 */
1342 if (imf == NULL) {
1343 im6f_init(&timf, MCAST_EXCLUDE, MCAST_UNDEFINED);
1344 imf = &timf;
1345 }
1346
1347 /*
1348 * Begin state merge transaction at MLD layer.
1349 *
1350 * As this particular invocation should not cause any memory
1351 * to be allocated, and there is no opportunity to roll back
1352 * the transaction, it MUST NOT fail.
1353 */
1354 MLD_PRINTF(("%s: merge inm state\n", __func__));
1355
1356 error = in6m_merge(inm, imf);
1357 KASSERT(error == 0, ("%s: failed to merge inm state\n", __func__));
1358
1359 MLD_PRINTF(("%s: doing mld downcall\n", __func__));
1360 error = mld_change_state(inm, 0);
1361 #if MLD_DEBUG
1362 if (error)
1363 MLD_PRINTF(("%s: failed mld downcall\n", __func__));
1364 #endif
1365 lastref = in6_multi_detach(inm);
1366 VERIFY(!lastref || (!(inm->in6m_debug & IFD_ATTACHED) &&
1367 inm->in6m_reqcnt == 0));
1368 IN6M_UNLOCK(inm);
1369 in6_multihead_lock_done();
1370
1371 if (lastref)
1372 IN6M_REMREF(inm); /* for in6_multihead list */
1373
1374 return (error);
1375 }
1376
1377 /*
1378 * Block or unblock an ASM multicast source on an inpcb.
1379 * This implements the delta-based API described in RFC 3678.
1380 *
1381 * The delta-based API applies only to exclusive-mode memberships.
1382 * An MLD downcall will be performed.
1383 *
1384 * Return 0 if successful, otherwise return an appropriate error code.
1385 */
1386 static int
1387 in6p_block_unblock_source(struct inpcb *inp, struct sockopt *sopt)
1388 {
1389 struct group_source_req gsr;
1390 sockunion_t *gsa, *ssa;
1391 struct ifnet *ifp;
1392 struct in6_mfilter *imf;
1393 struct ip6_moptions *imo;
1394 struct in6_msource *ims;
1395 struct in6_multi *inm;
1396 size_t idx;
1397 uint16_t fmode;
1398 int error, doblock;
1399
1400 ifp = NULL;
1401 error = 0;
1402 doblock = 0;
1403
1404 memset(&gsr, 0, sizeof(struct group_source_req));
1405 gsa = (sockunion_t *)&gsr.gsr_group;
1406 ssa = (sockunion_t *)&gsr.gsr_source;
1407
1408 switch (sopt->sopt_name) {
1409 case MCAST_BLOCK_SOURCE:
1410 case MCAST_UNBLOCK_SOURCE:
1411 error = sooptcopyin(sopt, &gsr,
1412 sizeof(struct group_source_req),
1413 sizeof(struct group_source_req));
1414 if (error)
1415 return (error);
1416
1417 if (gsa->sin6.sin6_family != AF_INET6 ||
1418 gsa->sin6.sin6_len != sizeof(struct sockaddr_in6))
1419 return (EINVAL);
1420
1421 if (ssa->sin6.sin6_family != AF_INET6 ||
1422 ssa->sin6.sin6_len != sizeof(struct sockaddr_in6))
1423 return (EINVAL);
1424
1425 ifnet_head_lock_shared();
1426 if (gsr.gsr_interface == 0 ||
1427 (u_int)if_index < gsr.gsr_interface) {
1428 ifnet_head_done();
1429 return (EADDRNOTAVAIL);
1430 }
1431
1432 ifp = ifindex2ifnet[gsr.gsr_interface];
1433 ifnet_head_done();
1434
1435 if (ifp == NULL)
1436 return (EADDRNOTAVAIL);
1437
1438 if (sopt->sopt_name == MCAST_BLOCK_SOURCE)
1439 doblock = 1;
1440 break;
1441
1442 default:
1443 MLD_PRINTF(("%s: unknown sopt_name %d\n",
1444 __func__, sopt->sopt_name));
1445 return (EOPNOTSUPP);
1446 break;
1447 }
1448
1449 if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr))
1450 return (EINVAL);
1451
1452 (void) in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL);
1453
1454 /*
1455 * Check if we are actually a member of this group.
1456 */
1457 imo = in6p_findmoptions(inp);
1458 if (imo == NULL)
1459 return (ENOMEM);
1460
1461 IM6O_LOCK(imo);
1462 idx = im6o_match_group(imo, ifp, &gsa->sa);
1463 if (idx == (size_t)-1 || imo->im6o_mfilters == NULL) {
1464 error = EADDRNOTAVAIL;
1465 goto out_imo_locked;
1466 }
1467
1468 VERIFY(imo->im6o_mfilters != NULL);
1469 imf = &imo->im6o_mfilters[idx];
1470 inm = imo->im6o_membership[idx];
1471
1472 /*
1473 * Attempting to use the delta-based API on an
1474 * non exclusive-mode membership is an error.
1475 */
1476 fmode = imf->im6f_st[0];
1477 if (fmode != MCAST_EXCLUDE) {
1478 error = EINVAL;
1479 goto out_imo_locked;
1480 }
1481
1482 /*
1483 * Deal with error cases up-front:
1484 * Asked to block, but already blocked; or
1485 * Asked to unblock, but nothing to unblock.
1486 * If adding a new block entry, allocate it.
1487 */
1488 ims = im6o_match_source(imo, idx, &ssa->sa);
1489 if ((ims != NULL && doblock) || (ims == NULL && !doblock)) {
1490 MLD_PRINTF(("%s: source %s %spresent\n", __func__,
1491 ip6_sprintf(&ssa->sin6.sin6_addr),
1492 doblock ? "" : "not "));
1493 error = EADDRNOTAVAIL;
1494 goto out_imo_locked;
1495 }
1496
1497 /*
1498 * Begin state merge transaction at socket layer.
1499 */
1500 if (doblock) {
1501 MLD_PRINTF(("%s: %s source\n", __func__, "block"));
1502 ims = im6f_graft(imf, fmode, &ssa->sin6);
1503 if (ims == NULL)
1504 error = ENOMEM;
1505 } else {
1506 MLD_PRINTF(("%s: %s source\n", __func__, "allow"));
1507 error = im6f_prune(imf, &ssa->sin6);
1508 }
1509
1510 if (error) {
1511 MLD_PRINTF(("%s: merge imf state failed\n", __func__));
1512 goto out_im6f_rollback;
1513 }
1514
1515 /*
1516 * Begin state merge transaction at MLD layer.
1517 */
1518 IN6M_LOCK(inm);
1519 MLD_PRINTF(("%s: merge inm state\n", __func__));
1520 error = in6m_merge(inm, imf);
1521 if (error) {
1522 MLD_PRINTF(("%s: failed to merge inm state\n", __func__));
1523 IN6M_UNLOCK(inm);
1524 goto out_im6f_rollback;
1525 }
1526
1527 MLD_PRINTF(("%s: doing mld downcall\n", __func__));
1528 error = mld_change_state(inm, 0);
1529 IN6M_UNLOCK(inm);
1530 #if MLD_DEBUG
1531 if (error)
1532 MLD_PRINTF(("%s: failed mld downcall\n", __func__));
1533 #endif
1534
1535 out_im6f_rollback:
1536 if (error)
1537 im6f_rollback(imf);
1538 else
1539 im6f_commit(imf);
1540
1541 im6f_reap(imf);
1542
1543 out_imo_locked:
1544 IM6O_UNLOCK(imo);
1545 IM6O_REMREF(imo); /* from in6p_findmoptions() */
1546 return (error);
1547 }
1548
1549 /*
1550 * Given an inpcb, return its multicast options structure pointer. Accepts
1551 * an unlocked inpcb pointer, but will return it locked. May sleep.
1552 *
1553 */
1554 static struct ip6_moptions *
1555 in6p_findmoptions(struct inpcb *inp)
1556 {
1557 struct ip6_moptions *imo;
1558 struct in6_multi **immp;
1559 struct in6_mfilter *imfp;
1560 size_t idx;
1561
1562 if ((imo = inp->in6p_moptions) != NULL) {
1563 IM6O_ADDREF(imo); /* for caller */
1564 return (imo);
1565 }
1566
1567 imo = ip6_allocmoptions(M_WAITOK);
1568 if (imo == NULL)
1569 return (NULL);
1570
1571 immp = _MALLOC(sizeof (*immp) * IPV6_MIN_MEMBERSHIPS, M_IP6MOPTS,
1572 M_WAITOK | M_ZERO);
1573 if (immp == NULL) {
1574 IM6O_REMREF(imo);
1575 return (NULL);
1576 }
1577
1578 imfp = _MALLOC(sizeof (struct in6_mfilter) * IPV6_MIN_MEMBERSHIPS,
1579 M_IN6MFILTER, M_WAITOK | M_ZERO);
1580 if (imfp == NULL) {
1581 _FREE(immp, M_IP6MOPTS);
1582 IM6O_REMREF(imo);
1583 return (NULL);
1584 }
1585
1586 imo->im6o_multicast_ifp = NULL;
1587 imo->im6o_multicast_hlim = ip6_defmcasthlim;
1588 imo->im6o_multicast_loop = in6_mcast_loop;
1589 imo->im6o_num_memberships = 0;
1590 imo->im6o_max_memberships = IPV6_MIN_MEMBERSHIPS;
1591 imo->im6o_membership = immp;
1592
1593 /* Initialize per-group source filters. */
1594 for (idx = 0; idx < IPV6_MIN_MEMBERSHIPS; idx++)
1595 im6f_init(&imfp[idx], MCAST_UNDEFINED, MCAST_EXCLUDE);
1596
1597 imo->im6o_mfilters = imfp;
1598 inp->in6p_moptions = imo; /* keep reference from ip6_allocmoptions() */
1599 IM6O_ADDREF(imo); /* for caller */
1600
1601 return (imo);
1602 }
1603
1604 /*
1605 * Atomically get source filters on a socket for an IPv6 multicast group.
1606 * Called with INP lock held; returns with lock released.
1607 */
1608 static int
1609 in6p_get_source_filters(struct inpcb *inp, struct sockopt *sopt)
1610 {
1611 struct __msfilterreq64 msfr, msfr64;
1612 struct __msfilterreq32 msfr32;
1613 sockunion_t *gsa;
1614 struct ifnet *ifp;
1615 struct ip6_moptions *imo;
1616 struct in6_mfilter *imf;
1617 struct ip6_msource *ims;
1618 struct in6_msource *lims;
1619 struct sockaddr_in6 *psin;
1620 struct sockaddr_storage *ptss;
1621 struct sockaddr_storage *tss;
1622 int error;
1623 size_t idx, nsrcs, ncsrcs;
1624 user_addr_t tmp_ptr;
1625
1626 imo = inp->in6p_moptions;
1627 VERIFY(imo != NULL);
1628
1629 if (IS_64BIT_PROCESS(current_proc())) {
1630 error = sooptcopyin(sopt, &msfr64,
1631 sizeof(struct __msfilterreq64),
1632 sizeof(struct __msfilterreq64));
1633 if (error)
1634 return (error);
1635 /* we never use msfr.msfr_srcs; */
1636 memcpy(&msfr, &msfr64, sizeof(msfr));
1637 } else {
1638 error = sooptcopyin(sopt, &msfr32,
1639 sizeof(struct __msfilterreq32),
1640 sizeof(struct __msfilterreq32));
1641 if (error)
1642 return (error);
1643 /* we never use msfr.msfr_srcs; */
1644 memcpy(&msfr, &msfr32, sizeof(msfr));
1645 }
1646
1647 if (msfr.msfr_group.ss_family != AF_INET6 ||
1648 msfr.msfr_group.ss_len != sizeof(struct sockaddr_in6))
1649 return (EINVAL);
1650
1651 gsa = (sockunion_t *)&msfr.msfr_group;
1652 if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr))
1653 return (EINVAL);
1654
1655 ifnet_head_lock_shared();
1656 if (msfr.msfr_ifindex == 0 || (u_int)if_index < msfr.msfr_ifindex) {
1657 ifnet_head_done();
1658 return (EADDRNOTAVAIL);
1659 }
1660 ifp = ifindex2ifnet[msfr.msfr_ifindex];
1661 ifnet_head_done();
1662
1663 if (ifp == NULL)
1664 return (EADDRNOTAVAIL);
1665
1666 if ((size_t) msfr.msfr_nsrcs >
1667 SIZE_MAX / sizeof(struct sockaddr_storage))
1668 msfr.msfr_nsrcs = SIZE_MAX / sizeof(struct sockaddr_storage);
1669
1670 if (msfr.msfr_nsrcs > in6_mcast_maxsocksrc)
1671 msfr.msfr_nsrcs = in6_mcast_maxsocksrc;
1672
1673 (void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL);
1674
1675 IM6O_LOCK(imo);
1676 /*
1677 * Lookup group on the socket.
1678 */
1679 idx = im6o_match_group(imo, ifp, &gsa->sa);
1680 if (idx == (size_t)-1 || imo->im6o_mfilters == NULL) {
1681 IM6O_UNLOCK(imo);
1682 return (EADDRNOTAVAIL);
1683 }
1684 imf = &imo->im6o_mfilters[idx];
1685
1686 /*
1687 * Ignore memberships which are in limbo.
1688 */
1689 if (imf->im6f_st[1] == MCAST_UNDEFINED) {
1690 IM6O_UNLOCK(imo);
1691 return (EAGAIN);
1692 }
1693 msfr.msfr_fmode = imf->im6f_st[1];
1694
1695 /*
1696 * If the user specified a buffer, copy out the source filter
1697 * entries to userland gracefully.
1698 * We only copy out the number of entries which userland
1699 * has asked for, but we always tell userland how big the
1700 * buffer really needs to be.
1701 */
1702 tss = NULL;
1703
1704 if (IS_64BIT_PROCESS(current_proc()))
1705 tmp_ptr = msfr64.msfr_srcs;
1706 else
1707 tmp_ptr = CAST_USER_ADDR_T(msfr32.msfr_srcs);
1708
1709 if (tmp_ptr != USER_ADDR_NULL && msfr.msfr_nsrcs > 0) {
1710 tss = _MALLOC((size_t) msfr.msfr_nsrcs * sizeof(*tss),
1711 M_TEMP, M_WAITOK | M_ZERO);
1712 if (tss == NULL) {
1713 IM6O_UNLOCK(imo);
1714 return (ENOBUFS);
1715 }
1716 bzero(tss, (size_t) msfr.msfr_nsrcs * sizeof(*tss));
1717 }
1718
1719 /*
1720 * Count number of sources in-mode at t0.
1721 * If buffer space exists and remains, copy out source entries.
1722 */
1723 nsrcs = msfr.msfr_nsrcs;
1724 ncsrcs = 0;
1725 ptss = tss;
1726 RB_FOREACH(ims, ip6_msource_tree, &imf->im6f_sources) {
1727 lims = (struct in6_msource *)ims;
1728 if (lims->im6sl_st[0] == MCAST_UNDEFINED ||
1729 lims->im6sl_st[0] != imf->im6f_st[0])
1730 continue;
1731 if (tss != NULL && nsrcs > 0) {
1732 psin = (struct sockaddr_in6 *)ptss;
1733 psin->sin6_family = AF_INET6;
1734 psin->sin6_len = sizeof(struct sockaddr_in6);
1735 psin->sin6_addr = lims->im6s_addr;
1736 psin->sin6_port = 0;
1737 --nsrcs;
1738 ++ptss;
1739 ++ncsrcs;
1740 }
1741 }
1742
1743 IM6O_UNLOCK(imo);
1744
1745 if (tss != NULL) {
1746 error = copyout(tss, tmp_ptr, ncsrcs * sizeof(*tss));
1747 FREE(tss, M_TEMP);
1748 if (error)
1749 return (error);
1750 }
1751
1752 msfr.msfr_nsrcs = ncsrcs;
1753 if (IS_64BIT_PROCESS(current_proc())) {
1754 msfr64.msfr_ifindex = msfr.msfr_ifindex;
1755 msfr64.msfr_fmode = msfr.msfr_fmode;
1756 msfr64.msfr_nsrcs = msfr.msfr_nsrcs;
1757 memcpy(&msfr64.msfr_group, &msfr.msfr_group,
1758 sizeof(struct sockaddr_storage));
1759 error = sooptcopyout(sopt, &msfr64,
1760 sizeof(struct __msfilterreq64));
1761 } else {
1762 msfr32.msfr_ifindex = msfr.msfr_ifindex;
1763 msfr32.msfr_fmode = msfr.msfr_fmode;
1764 msfr32.msfr_nsrcs = msfr.msfr_nsrcs;
1765 memcpy(&msfr64.msfr_group, &msfr.msfr_group,
1766 sizeof(struct sockaddr_storage));
1767 error = sooptcopyout(sopt, &msfr32,
1768 sizeof(struct __msfilterreq32));
1769 }
1770
1771 return (error);
1772 }
1773
1774 /*
1775 * Return the IP multicast options in response to user getsockopt().
1776 */
1777 int
1778 ip6_getmoptions(struct inpcb *inp, struct sockopt *sopt)
1779 {
1780 struct ip6_moptions *im6o;
1781 int error;
1782 u_int optval;
1783
1784 im6o = inp->in6p_moptions;
1785 /*
1786 * If socket is neither of type SOCK_RAW or SOCK_DGRAM,
1787 * or is a divert socket, reject it.
1788 */
1789 if (inp->inp_socket->so_proto->pr_protocol == IPPROTO_DIVERT ||
1790 (inp->inp_socket->so_proto->pr_type != SOCK_RAW &&
1791 inp->inp_socket->so_proto->pr_type != SOCK_DGRAM)) {
1792 return (EOPNOTSUPP);
1793 }
1794
1795 error = 0;
1796 switch (sopt->sopt_name) {
1797 case IPV6_MULTICAST_IF:
1798 if (im6o != NULL)
1799 IM6O_LOCK(im6o);
1800 if (im6o == NULL || im6o->im6o_multicast_ifp == NULL) {
1801 optval = 0;
1802 } else {
1803 optval = im6o->im6o_multicast_ifp->if_index;
1804 }
1805 if (im6o != NULL)
1806 IM6O_UNLOCK(im6o);
1807 error = sooptcopyout(sopt, &optval, sizeof(u_int));
1808 break;
1809
1810 case IPV6_MULTICAST_HOPS:
1811 if (im6o == NULL) {
1812 optval = ip6_defmcasthlim;
1813 } else {
1814 IM6O_LOCK(im6o);
1815 optval = im6o->im6o_multicast_hlim;
1816 IM6O_UNLOCK(im6o);
1817 }
1818 error = sooptcopyout(sopt, &optval, sizeof(u_int));
1819 break;
1820
1821 case IPV6_MULTICAST_LOOP:
1822 if (im6o == NULL) {
1823 optval = in6_mcast_loop; /* XXX VIMAGE */
1824 } else {
1825 IM6O_LOCK(im6o);
1826 optval = im6o->im6o_multicast_loop;
1827 IM6O_UNLOCK(im6o);
1828 }
1829 error = sooptcopyout(sopt, &optval, sizeof(u_int));
1830 break;
1831
1832 case IPV6_MSFILTER:
1833 if (im6o == NULL) {
1834 error = EADDRNOTAVAIL;
1835 } else {
1836 error = in6p_get_source_filters(inp, sopt);
1837 }
1838 break;
1839
1840 default:
1841 error = ENOPROTOOPT;
1842 break;
1843 }
1844
1845 return (error);
1846 }
1847
1848 /*
1849 * Look up the ifnet to use for a multicast group membership,
1850 * given the address of an IPv6 group.
1851 *
1852 * This routine exists to support legacy IPv6 multicast applications.
1853 *
1854 * If inp is non-NULL and is bound to an interface, use this socket's
1855 * inp_boundif for any required routing table lookup.
1856 *
1857 * If the route lookup fails, return NULL.
1858 *
1859 * FUTURE: Support multiple forwarding tables for IPv6.
1860 *
1861 * Returns NULL if no ifp could be found.
1862 */
1863 static struct ifnet *
1864 in6p_lookup_mcast_ifp(const struct inpcb *in6p,
1865 const struct sockaddr_in6 *gsin6)
1866 {
1867 struct route_in6 ro6;
1868 struct ifnet *ifp;
1869 unsigned int ifscope = IFSCOPE_NONE;
1870
1871 VERIFY(in6p == NULL || (in6p->inp_vflag & INP_IPV6));
1872 VERIFY(gsin6->sin6_family == AF_INET6);
1873 if (IN6_IS_ADDR_MULTICAST(&gsin6->sin6_addr) == 0)
1874 return NULL;
1875
1876 if (in6p != NULL && (in6p->inp_flags & INP_BOUND_IF))
1877 ifscope = in6p->inp_boundifp->if_index;
1878
1879 ifp = NULL;
1880 memset(&ro6, 0, sizeof(struct route_in6));
1881 memcpy(&ro6.ro_dst, gsin6, sizeof(struct sockaddr_in6));
1882 rtalloc_scoped_ign((struct route *)&ro6, 0, ifscope);
1883 if (ro6.ro_rt != NULL) {
1884 ifp = ro6.ro_rt->rt_ifp;
1885 VERIFY(ifp != NULL);
1886 rtfree(ro6.ro_rt);
1887 }
1888
1889 return (ifp);
1890 }
1891
1892 /*
1893 * Since ipv6_mreq contains an ifindex and ip_mreq contains an AF_INET
1894 * address, we need to lookup the AF_INET address when translating an
1895 * ipv6_mreq structure into an ipmreq structure.
1896 * This is used when userland performs multicast setsockopt() on AF_INET6
1897 * sockets with AF_INET multicast addresses (IPv6 v4 mapped addresses).
1898 */
1899 static int
1900 in6p_lookup_v4addr(struct ipv6_mreq *mreq, struct ip_mreq *v4mreq)
1901 {
1902 struct ifnet *ifp;
1903 struct ifaddr *ifa;
1904 struct sockaddr_in *sin;
1905
1906 ifnet_head_lock_shared();
1907 if (mreq->ipv6mr_interface > (unsigned int)if_index) {
1908 ifnet_head_done();
1909 return (EADDRNOTAVAIL);
1910 } else
1911 ifp = ifindex2ifnet[mreq->ipv6mr_interface];
1912 ifnet_head_done();
1913 if (ifp == NULL)
1914 return (EADDRNOTAVAIL);
1915 ifa = ifa_ifpgetprimary(ifp, AF_INET);
1916 if (ifa == NULL)
1917 return (EADDRNOTAVAIL);
1918 sin = (struct sockaddr_in *)(uintptr_t)(size_t)ifa->ifa_addr;
1919 v4mreq->imr_interface.s_addr = sin->sin_addr.s_addr;
1920 IFA_REMREF(ifa);
1921
1922 return (0);
1923 }
1924
1925 /*
1926 * Join an IPv6 multicast group, possibly with a source.
1927 *
1928 * FIXME: The KAME use of the unspecified address (::)
1929 * to join *all* multicast groups is currently unsupported.
1930 */
1931 static int
1932 in6p_join_group(struct inpcb *inp, struct sockopt *sopt)
1933 {
1934 struct group_source_req gsr;
1935 sockunion_t *gsa, *ssa;
1936 struct ifnet *ifp;
1937 struct in6_mfilter *imf;
1938 struct ip6_moptions *imo;
1939 struct in6_multi *inm = NULL;
1940 struct in6_msource *lims = NULL;
1941 size_t idx;
1942 int error, is_new;
1943 uint32_t scopeid = 0;
1944
1945 ifp = NULL;
1946 imf = NULL;
1947 error = 0;
1948 is_new = 0;
1949
1950 memset(&gsr, 0, sizeof(struct group_source_req));
1951 gsa = (sockunion_t *)&gsr.gsr_group;
1952 gsa->ss.ss_family = AF_UNSPEC;
1953 ssa = (sockunion_t *)&gsr.gsr_source;
1954 ssa->ss.ss_family = AF_UNSPEC;
1955
1956 /*
1957 * Chew everything into struct group_source_req.
1958 * Overwrite the port field if present, as the sockaddr
1959 * being copied in may be matched with a binary comparison.
1960 * Ignore passed-in scope ID.
1961 */
1962 switch (sopt->sopt_name) {
1963 case IPV6_JOIN_GROUP: {
1964 struct ipv6_mreq mreq;
1965 struct sockaddr_in6 *gsin6;
1966
1967 error = sooptcopyin(sopt, &mreq, sizeof(struct ipv6_mreq),
1968 sizeof(struct ipv6_mreq));
1969 if (error)
1970 return (error);
1971 if (IN6_IS_ADDR_V4MAPPED(&mreq.ipv6mr_multiaddr)) {
1972 struct ip_mreq v4mreq;
1973 struct sockopt v4sopt;
1974
1975 v4mreq.imr_multiaddr.s_addr =
1976 mreq.ipv6mr_multiaddr.s6_addr32[3];
1977 if (mreq.ipv6mr_interface == 0)
1978 v4mreq.imr_interface.s_addr = INADDR_ANY;
1979 else
1980 error = in6p_lookup_v4addr(&mreq, &v4mreq);
1981 if (error)
1982 return (error);
1983 v4sopt.sopt_dir = SOPT_SET;
1984 v4sopt.sopt_level = sopt->sopt_level;
1985 v4sopt.sopt_name = IP_ADD_MEMBERSHIP;
1986 v4sopt.sopt_val = CAST_USER_ADDR_T(&v4mreq);
1987 v4sopt.sopt_valsize = sizeof(v4mreq);
1988 v4sopt.sopt_p = kernproc;
1989
1990 return (inp_join_group(inp, &v4sopt));
1991 }
1992 gsa->sin6.sin6_family = AF_INET6;
1993 gsa->sin6.sin6_len = sizeof(struct sockaddr_in6);
1994 gsa->sin6.sin6_addr = mreq.ipv6mr_multiaddr;
1995
1996 gsin6 = &gsa->sin6;
1997
1998 /* Only allow IPv6 multicast addresses */
1999 if (IN6_IS_ADDR_MULTICAST(&gsin6->sin6_addr) == 0) {
2000 return (EINVAL);
2001 }
2002
2003 if (mreq.ipv6mr_interface == 0) {
2004 ifp = in6p_lookup_mcast_ifp(inp, gsin6);
2005 } else {
2006 ifnet_head_lock_shared();
2007 if ((u_int)if_index < mreq.ipv6mr_interface) {
2008 ifnet_head_done();
2009 return (EADDRNOTAVAIL);
2010 }
2011 ifp = ifindex2ifnet[mreq.ipv6mr_interface];
2012 ifnet_head_done();
2013 }
2014 MLD_PRINTF(("%s: ipv6mr_interface = %d, ifp = %p\n",
2015 __func__, mreq.ipv6mr_interface, ifp));
2016 break;
2017 }
2018
2019 case MCAST_JOIN_GROUP:
2020 case MCAST_JOIN_SOURCE_GROUP:
2021 if (sopt->sopt_name == MCAST_JOIN_GROUP) {
2022 error = sooptcopyin(sopt, &gsr,
2023 sizeof(struct group_req),
2024 sizeof(struct group_req));
2025 } else if (sopt->sopt_name == MCAST_JOIN_SOURCE_GROUP) {
2026 error = sooptcopyin(sopt, &gsr,
2027 sizeof(struct group_source_req),
2028 sizeof(struct group_source_req));
2029 }
2030 if (error)
2031 return (error);
2032
2033 if (gsa->sin6.sin6_family != AF_INET6 ||
2034 gsa->sin6.sin6_len != sizeof(struct sockaddr_in6))
2035 return (EINVAL);
2036
2037 if (sopt->sopt_name == MCAST_JOIN_SOURCE_GROUP) {
2038 if (ssa->sin6.sin6_family != AF_INET6 ||
2039 ssa->sin6.sin6_len != sizeof(struct sockaddr_in6))
2040 return (EINVAL);
2041 if (IN6_IS_ADDR_MULTICAST(&ssa->sin6.sin6_addr))
2042 return (EINVAL);
2043 /*
2044 * TODO: Validate embedded scope ID in source
2045 * list entry against passed-in ifp, if and only
2046 * if source list filter entry is iface or node local.
2047 */
2048 in6_clearscope(&ssa->sin6.sin6_addr);
2049 ssa->sin6.sin6_port = 0;
2050 ssa->sin6.sin6_scope_id = 0;
2051 }
2052
2053 ifnet_head_lock_shared();
2054 if (gsr.gsr_interface == 0 ||
2055 (u_int)if_index < gsr.gsr_interface) {
2056 ifnet_head_done();
2057 return (EADDRNOTAVAIL);
2058 }
2059 ifp = ifindex2ifnet[gsr.gsr_interface];
2060 ifnet_head_done();
2061 break;
2062
2063 default:
2064 MLD_PRINTF(("%s: unknown sopt_name %d\n",
2065 __func__, sopt->sopt_name));
2066 return (EOPNOTSUPP);
2067 break;
2068 }
2069
2070 if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr))
2071 return (EINVAL);
2072
2073 if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0)
2074 return (EADDRNOTAVAIL);
2075
2076 gsa->sin6.sin6_port = 0;
2077 gsa->sin6.sin6_scope_id = 0;
2078
2079 /*
2080 * Always set the scope zone ID on memberships created from userland.
2081 * Use the passed-in ifp to do this.
2082 */
2083 (void)in6_setscope(&gsa->sin6.sin6_addr, ifp, &scopeid);
2084 /*
2085 * Some addresses are not valid without an embedded scopeid.
2086 * This check must be present because otherwise we will later hit
2087 * a VERIFY() in in6_mc_join().
2088 */
2089 if ((IN6_IS_ADDR_MC_LINKLOCAL(&gsa->sin6.sin6_addr) ||
2090 IN6_IS_ADDR_MC_INTFACELOCAL(&gsa->sin6.sin6_addr)) &&
2091 (scopeid == 0 || gsa->sin6.sin6_addr.s6_addr16[1] == 0))
2092 return (EINVAL);
2093
2094 imo = in6p_findmoptions(inp);
2095 if (imo == NULL)
2096 return (ENOMEM);
2097
2098 IM6O_LOCK(imo);
2099 idx = im6o_match_group(imo, ifp, &gsa->sa);
2100 if (idx == (size_t)-1) {
2101 is_new = 1;
2102 } else {
2103 inm = imo->im6o_membership[idx];
2104 imf = &imo->im6o_mfilters[idx];
2105 if (ssa->ss.ss_family != AF_UNSPEC) {
2106 /*
2107 * MCAST_JOIN_SOURCE_GROUP on an exclusive membership
2108 * is an error. On an existing inclusive membership,
2109 * it just adds the source to the filter list.
2110 */
2111 if (imf->im6f_st[1] != MCAST_INCLUDE) {
2112 error = EINVAL;
2113 goto out_imo_locked;
2114 }
2115 /*
2116 * Throw out duplicates.
2117 *
2118 * XXX FIXME: This makes a naive assumption that
2119 * even if entries exist for *ssa in this imf,
2120 * they will be rejected as dupes, even if they
2121 * are not valid in the current mode (in-mode).
2122 *
2123 * in6_msource is transactioned just as for anything
2124 * else in SSM -- but note naive use of in6m_graft()
2125 * below for allocating new filter entries.
2126 *
2127 * This is only an issue if someone mixes the
2128 * full-state SSM API with the delta-based API,
2129 * which is discouraged in the relevant RFCs.
2130 */
2131 lims = im6o_match_source(imo, idx, &ssa->sa);
2132 if (lims != NULL /*&&
2133 lims->im6sl_st[1] == MCAST_INCLUDE*/) {
2134 error = EADDRNOTAVAIL;
2135 goto out_imo_locked;
2136 }
2137 } else {
2138 /*
2139 * MCAST_JOIN_GROUP on an existing exclusive
2140 * membership is an error; return EADDRINUSE
2141 * to preserve 4.4BSD API idempotence, and
2142 * avoid tedious detour to code below.
2143 * NOTE: This is bending RFC 3678 a bit.
2144 *
2145 * On an existing inclusive membership, this is also
2146 * an error; if you want to change filter mode,
2147 * you must use the userland API setsourcefilter().
2148 * XXX We don't reject this for imf in UNDEFINED
2149 * state at t1, because allocation of a filter
2150 * is atomic with allocation of a membership.
2151 */
2152 error = EINVAL;
2153 /* See comments above for EADDRINUSE */
2154 if (imf->im6f_st[1] == MCAST_EXCLUDE)
2155 error = EADDRINUSE;
2156 goto out_imo_locked;
2157 }
2158 }
2159
2160 /*
2161 * Begin state merge transaction at socket layer.
2162 */
2163
2164 if (is_new) {
2165 if (imo->im6o_num_memberships == imo->im6o_max_memberships) {
2166 error = im6o_grow(imo, 0);
2167 if (error)
2168 goto out_imo_locked;
2169 }
2170 /*
2171 * Allocate the new slot upfront so we can deal with
2172 * grafting the new source filter in same code path
2173 * as for join-source on existing membership.
2174 */
2175 idx = imo->im6o_num_memberships;
2176 imo->im6o_membership[idx] = NULL;
2177 imo->im6o_num_memberships++;
2178 VERIFY(imo->im6o_mfilters != NULL);
2179 imf = &imo->im6o_mfilters[idx];
2180 VERIFY(RB_EMPTY(&imf->im6f_sources));
2181 }
2182
2183 /*
2184 * Graft new source into filter list for this inpcb's
2185 * membership of the group. The in6_multi may not have
2186 * been allocated yet if this is a new membership, however,
2187 * the in_mfilter slot will be allocated and must be initialized.
2188 *
2189 * Note: Grafting of exclusive mode filters doesn't happen
2190 * in this path.
2191 * XXX: Should check for non-NULL lims (node exists but may
2192 * not be in-mode) for interop with full-state API.
2193 */
2194 if (ssa->ss.ss_family != AF_UNSPEC) {
2195 /* Membership starts in IN mode */
2196 if (is_new) {
2197 MLD_PRINTF(("%s: new join w/source\n", __func__);
2198 im6f_init(imf, MCAST_UNDEFINED, MCAST_INCLUDE));
2199 } else {
2200 MLD_PRINTF(("%s: %s source\n", __func__, "allow"));
2201 }
2202 lims = im6f_graft(imf, MCAST_INCLUDE, &ssa->sin6);
2203 if (lims == NULL) {
2204 MLD_PRINTF(("%s: merge imf state failed\n",
2205 __func__));
2206 error = ENOMEM;
2207 goto out_im6o_free;
2208 }
2209 } else {
2210 /* No address specified; Membership starts in EX mode */
2211 if (is_new) {
2212 MLD_PRINTF(("%s: new join w/o source", __func__));
2213 im6f_init(imf, MCAST_UNDEFINED, MCAST_EXCLUDE);
2214 }
2215 }
2216
2217 /*
2218 * Begin state merge transaction at MLD layer.
2219 */
2220
2221 if (is_new) {
2222 VERIFY(inm == NULL);
2223 error = in6_mc_join(ifp, &gsa->sin6.sin6_addr, imf, &inm, 0);
2224 VERIFY(inm != NULL || error != 0);
2225 if (error)
2226 goto out_im6o_free;
2227 imo->im6o_membership[idx] = inm; /* from in6_mc_join() */
2228 } else {
2229 MLD_PRINTF(("%s: merge inm state\n", __func__));
2230 IN6M_LOCK(inm);
2231 error = in6m_merge(inm, imf);
2232 if (error) {
2233 MLD_PRINTF(("%s: failed to merge inm state\n",
2234 __func__));
2235 IN6M_UNLOCK(inm);
2236 goto out_im6f_rollback;
2237 }
2238 MLD_PRINTF(("%s: doing mld downcall\n", __func__));
2239 error = mld_change_state(inm, 0);
2240 IN6M_UNLOCK(inm);
2241 if (error) {
2242 MLD_PRINTF(("%s: failed mld downcall\n",
2243 __func__));
2244 goto out_im6f_rollback;
2245 }
2246 }
2247
2248 out_im6f_rollback:
2249 if (error) {
2250 im6f_rollback(imf);
2251 if (is_new)
2252 im6f_purge(imf);
2253 else
2254 im6f_reap(imf);
2255 } else {
2256 im6f_commit(imf);
2257 }
2258
2259 out_im6o_free:
2260 if (error && is_new) {
2261 VERIFY(inm == NULL);
2262 imo->im6o_membership[idx] = NULL;
2263 --imo->im6o_num_memberships;
2264 }
2265
2266 out_imo_locked:
2267 IM6O_UNLOCK(imo);
2268 IM6O_REMREF(imo); /* from in6p_findmoptions() */
2269 return (error);
2270 }
2271
2272 /*
2273 * Leave an IPv6 multicast group on an inpcb, possibly with a source.
2274 */
2275 static int
2276 in6p_leave_group(struct inpcb *inp, struct sockopt *sopt)
2277 {
2278 struct ipv6_mreq mreq;
2279 struct group_source_req gsr;
2280 sockunion_t *gsa, *ssa;
2281 struct ifnet *ifp;
2282 struct in6_mfilter *imf;
2283 struct ip6_moptions *imo;
2284 struct in6_msource *ims;
2285 struct in6_multi *inm = NULL;
2286 uint32_t ifindex = 0;
2287 size_t idx;
2288 int error, is_final;
2289
2290 ifp = NULL;
2291 error = 0;
2292 is_final = 1;
2293
2294 memset(&gsr, 0, sizeof(struct group_source_req));
2295 gsa = (sockunion_t *)&gsr.gsr_group;
2296 gsa->ss.ss_family = AF_UNSPEC;
2297 ssa = (sockunion_t *)&gsr.gsr_source;
2298 ssa->ss.ss_family = AF_UNSPEC;
2299
2300 /*
2301 * Chew everything passed in up into a struct group_source_req
2302 * as that is easier to process.
2303 * Note: Any embedded scope ID in the multicast group passed
2304 * in by userland is ignored, the interface index is the recommended
2305 * mechanism to specify an interface; see below.
2306 */
2307 switch (sopt->sopt_name) {
2308 case IPV6_LEAVE_GROUP: {
2309 struct sockaddr_in6 *gsin6;
2310
2311 error = sooptcopyin(sopt, &mreq, sizeof(struct ipv6_mreq),
2312 sizeof(struct ipv6_mreq));
2313 if (error)
2314 return (error);
2315 if (IN6_IS_ADDR_V4MAPPED(&mreq.ipv6mr_multiaddr)) {
2316 struct ip_mreq v4mreq;
2317 struct sockopt v4sopt;
2318
2319 v4mreq.imr_multiaddr.s_addr =
2320 mreq.ipv6mr_multiaddr.s6_addr32[3];
2321 if (mreq.ipv6mr_interface == 0)
2322 v4mreq.imr_interface.s_addr = INADDR_ANY;
2323 else
2324 error = in6p_lookup_v4addr(&mreq, &v4mreq);
2325 if (error)
2326 return (error);
2327 v4sopt.sopt_dir = SOPT_SET;
2328 v4sopt.sopt_level = sopt->sopt_level;
2329 v4sopt.sopt_name = IP_DROP_MEMBERSHIP;
2330 v4sopt.sopt_val = CAST_USER_ADDR_T(&v4mreq);
2331 v4sopt.sopt_valsize = sizeof(v4mreq);
2332 v4sopt.sopt_p = kernproc;
2333
2334 return (inp_leave_group(inp, &v4sopt));
2335 }
2336 gsa->sin6.sin6_family = AF_INET6;
2337 gsa->sin6.sin6_len = sizeof(struct sockaddr_in6);
2338 gsa->sin6.sin6_addr = mreq.ipv6mr_multiaddr;
2339 gsa->sin6.sin6_port = 0;
2340 gsa->sin6.sin6_scope_id = 0;
2341 ifindex = mreq.ipv6mr_interface;
2342 gsin6 = &gsa->sin6;
2343 /* Only allow IPv6 multicast addresses */
2344 if (IN6_IS_ADDR_MULTICAST(&gsin6->sin6_addr) == 0) {
2345 return (EINVAL);
2346 }
2347 break;
2348 }
2349
2350 case MCAST_LEAVE_GROUP:
2351 case MCAST_LEAVE_SOURCE_GROUP:
2352 if (sopt->sopt_name == MCAST_LEAVE_GROUP) {
2353 error = sooptcopyin(sopt, &gsr,
2354 sizeof(struct group_req),
2355 sizeof(struct group_req));
2356 } else if (sopt->sopt_name == MCAST_LEAVE_SOURCE_GROUP) {
2357 error = sooptcopyin(sopt, &gsr,
2358 sizeof(struct group_source_req),
2359 sizeof(struct group_source_req));
2360 }
2361 if (error)
2362 return (error);
2363
2364 if (gsa->sin6.sin6_family != AF_INET6 ||
2365 gsa->sin6.sin6_len != sizeof(struct sockaddr_in6))
2366 return (EINVAL);
2367 if (sopt->sopt_name == MCAST_LEAVE_SOURCE_GROUP) {
2368 if (ssa->sin6.sin6_family != AF_INET6 ||
2369 ssa->sin6.sin6_len != sizeof(struct sockaddr_in6))
2370 return (EINVAL);
2371 if (IN6_IS_ADDR_MULTICAST(&ssa->sin6.sin6_addr))
2372 return (EINVAL);
2373 /*
2374 * TODO: Validate embedded scope ID in source
2375 * list entry against passed-in ifp, if and only
2376 * if source list filter entry is iface or node local.
2377 */
2378 in6_clearscope(&ssa->sin6.sin6_addr);
2379 }
2380 gsa->sin6.sin6_port = 0;
2381 gsa->sin6.sin6_scope_id = 0;
2382 ifindex = gsr.gsr_interface;
2383 break;
2384
2385 default:
2386 MLD_PRINTF(("%s: unknown sopt_name %d\n",
2387 __func__, sopt->sopt_name));
2388 return (EOPNOTSUPP);
2389 break;
2390 }
2391
2392 if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr))
2393 return (EINVAL);
2394
2395 /*
2396 * Validate interface index if provided. If no interface index
2397 * was provided separately, attempt to look the membership up
2398 * from the default scope as a last resort to disambiguate
2399 * the membership we are being asked to leave.
2400 * XXX SCOPE6 lock potentially taken here.
2401 */
2402 if (ifindex != 0) {
2403 ifnet_head_lock_shared();
2404 if ((u_int)if_index < ifindex) {
2405 ifnet_head_done();
2406 return (EADDRNOTAVAIL);
2407 }
2408 ifp = ifindex2ifnet[ifindex];
2409 ifnet_head_done();
2410 if (ifp == NULL)
2411 return (EADDRNOTAVAIL);
2412 (void) in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL);
2413 } else {
2414 error = sa6_embedscope(&gsa->sin6, ip6_use_defzone);
2415 if (error)
2416 return (EADDRNOTAVAIL);
2417 /*
2418 * Some badly behaved applications don't pass an ifindex
2419 * or a scope ID, which is an API violation. In this case,
2420 * perform a lookup as per a v6 join.
2421 *
2422 * XXX For now, stomp on zone ID for the corner case.
2423 * This is not the 'KAME way', but we need to see the ifp
2424 * directly until such time as this implementation is
2425 * refactored, assuming the scope IDs are the way to go.
2426 */
2427 ifindex = ntohs(gsa->sin6.sin6_addr.s6_addr16[1]);
2428 if (ifindex == 0) {
2429 MLD_PRINTF(("%s: warning: no ifindex, looking up "
2430 "ifp for group %s.\n", __func__,
2431 ip6_sprintf(&gsa->sin6.sin6_addr)));
2432 ifp = in6p_lookup_mcast_ifp(inp, &gsa->sin6);
2433 } else {
2434 ifnet_head_lock_shared();
2435 ifp = ifindex2ifnet[ifindex];
2436 ifnet_head_done();
2437 }
2438 if (ifp == NULL)
2439 return (EADDRNOTAVAIL);
2440 }
2441
2442 VERIFY(ifp != NULL);
2443 MLD_PRINTF(("%s: ifp = %p\n", __func__, ifp));
2444
2445 /*
2446 * Find the membership in the membership array.
2447 */
2448 imo = in6p_findmoptions(inp);
2449 if (imo == NULL)
2450 return (ENOMEM);
2451
2452 IM6O_LOCK(imo);
2453 idx = im6o_match_group(imo, ifp, &gsa->sa);
2454 if (idx == (size_t)-1) {
2455 error = EADDRNOTAVAIL;
2456 goto out_locked;
2457 }
2458 inm = imo->im6o_membership[idx];
2459 imf = &imo->im6o_mfilters[idx];
2460
2461 if (ssa->ss.ss_family != AF_UNSPEC)
2462 is_final = 0;
2463
2464 /*
2465 * Begin state merge transaction at socket layer.
2466 */
2467
2468 /*
2469 * If we were instructed only to leave a given source, do so.
2470 * MCAST_LEAVE_SOURCE_GROUP is only valid for inclusive memberships.
2471 */
2472 if (is_final) {
2473 im6f_leave(imf);
2474 } else {
2475 if (imf->im6f_st[0] == MCAST_EXCLUDE) {
2476 error = EADDRNOTAVAIL;
2477 goto out_locked;
2478 }
2479 ims = im6o_match_source(imo, idx, &ssa->sa);
2480 if (ims == NULL) {
2481 MLD_PRINTF(("%s: source %p %spresent\n", __func__,
2482 ip6_sprintf(&ssa->sin6.sin6_addr),
2483 "not "));
2484 error = EADDRNOTAVAIL;
2485 goto out_locked;
2486 }
2487 MLD_PRINTF(("%s: %s source\n", __func__, "block"));
2488 error = im6f_prune(imf, &ssa->sin6);
2489 if (error) {
2490 MLD_PRINTF(("%s: merge imf state failed\n",
2491 __func__));
2492 goto out_locked;
2493 }
2494 }
2495
2496 /*
2497 * Begin state merge transaction at MLD layer.
2498 */
2499
2500 if (is_final) {
2501 /*
2502 * Give up the multicast address record to which
2503 * the membership points. Reference held in im6o
2504 * will be released below.
2505 */
2506 (void) in6_mc_leave(inm, imf);
2507 } else {
2508 MLD_PRINTF(("%s: merge inm state\n", __func__));
2509 IN6M_LOCK(inm);
2510 error = in6m_merge(inm, imf);
2511 if (error) {
2512 MLD_PRINTF(("%s: failed to merge inm state\n",
2513 __func__));
2514 IN6M_UNLOCK(inm);
2515 goto out_im6f_rollback;
2516 }
2517
2518 MLD_PRINTF(("%s: doing mld downcall\n", __func__));
2519 error = mld_change_state(inm, 0);
2520 if (error) {
2521 MLD_PRINTF(("%s: failed mld downcall\n", __func__));
2522 }
2523 IN6M_UNLOCK(inm);
2524 }
2525
2526 out_im6f_rollback:
2527 if (error)
2528 im6f_rollback(imf);
2529 else
2530 im6f_commit(imf);
2531
2532 im6f_reap(imf);
2533
2534 if (is_final) {
2535 /* Remove the gap in the membership array. */
2536 VERIFY(inm == imo->im6o_membership[idx]);
2537 imo->im6o_membership[idx] = NULL;
2538 IN6M_REMREF(inm);
2539 for (++idx; idx < imo->im6o_num_memberships; ++idx) {
2540 imo->im6o_membership[idx-1] = imo->im6o_membership[idx];
2541 imo->im6o_mfilters[idx-1] = imo->im6o_mfilters[idx];
2542 }
2543 imo->im6o_num_memberships--;
2544 }
2545
2546 out_locked:
2547 IM6O_UNLOCK(imo);
2548 IM6O_REMREF(imo); /* from in6p_findmoptions() */
2549 return (error);
2550 }
2551
2552 /*
2553 * Select the interface for transmitting IPv6 multicast datagrams.
2554 *
2555 * Either an instance of struct in6_addr or an instance of struct ipv6_mreqn
2556 * may be passed to this socket option. An address of in6addr_any or an
2557 * interface index of 0 is used to remove a previous selection.
2558 * When no interface is selected, one is chosen for every send.
2559 */
2560 static int
2561 in6p_set_multicast_if(struct inpcb *inp, struct sockopt *sopt)
2562 {
2563 struct ifnet *ifp;
2564 struct ip6_moptions *imo;
2565 u_int ifindex;
2566 int error;
2567
2568 if (sopt->sopt_valsize != sizeof(u_int))
2569 return (EINVAL);
2570
2571 error = sooptcopyin(sopt, &ifindex, sizeof(u_int), sizeof(u_int));
2572 if (error)
2573 return (error);
2574
2575 ifnet_head_lock_shared();
2576 if ((u_int)if_index < ifindex) {
2577 ifnet_head_done();
2578 return (EINVAL);
2579 }
2580
2581 ifp = ifindex2ifnet[ifindex];
2582 ifnet_head_done();
2583 if (ifp == NULL || (ifp->if_flags & IFF_MULTICAST) == 0)
2584 return (EADDRNOTAVAIL);
2585
2586 imo = in6p_findmoptions(inp);
2587 if (imo == NULL)
2588 return (ENOMEM);
2589
2590 IM6O_LOCK(imo);
2591 imo->im6o_multicast_ifp = ifp;
2592 IM6O_UNLOCK(imo);
2593 IM6O_REMREF(imo); /* from in6p_findmoptions() */
2594
2595 return (0);
2596 }
2597
2598 /*
2599 * Atomically set source filters on a socket for an IPv6 multicast group.
2600 *
2601 */
2602 static int
2603 in6p_set_source_filters(struct inpcb *inp, struct sockopt *sopt)
2604 {
2605 struct __msfilterreq64 msfr, msfr64;
2606 struct __msfilterreq32 msfr32;
2607 sockunion_t *gsa;
2608 struct ifnet *ifp;
2609 struct in6_mfilter *imf;
2610 struct ip6_moptions *imo;
2611 struct in6_multi *inm;
2612 size_t idx;
2613 int error;
2614 user_addr_t tmp_ptr;
2615
2616 if (IS_64BIT_PROCESS(current_proc())) {
2617 error = sooptcopyin(sopt, &msfr64,
2618 sizeof(struct __msfilterreq64),
2619 sizeof(struct __msfilterreq64));
2620 if (error)
2621 return (error);
2622 /* we never use msfr.msfr_srcs; */
2623 memcpy(&msfr, &msfr64, sizeof(msfr));
2624 } else {
2625 error = sooptcopyin(sopt, &msfr32,
2626 sizeof(struct __msfilterreq32),
2627 sizeof(struct __msfilterreq32));
2628 if (error)
2629 return (error);
2630 /* we never use msfr.msfr_srcs; */
2631 memcpy(&msfr, &msfr32, sizeof(msfr));
2632 }
2633
2634 if ((size_t) msfr.msfr_nsrcs >
2635 SIZE_MAX / sizeof(struct sockaddr_storage))
2636 msfr.msfr_nsrcs = SIZE_MAX / sizeof(struct sockaddr_storage);
2637
2638 if (msfr.msfr_nsrcs > in6_mcast_maxsocksrc)
2639 return (ENOBUFS);
2640
2641 if (msfr.msfr_fmode != MCAST_EXCLUDE &&
2642 msfr.msfr_fmode != MCAST_INCLUDE)
2643 return (EINVAL);
2644
2645 if (msfr.msfr_group.ss_family != AF_INET6 ||
2646 msfr.msfr_group.ss_len != sizeof(struct sockaddr_in6))
2647 return (EINVAL);
2648
2649 gsa = (sockunion_t *)&msfr.msfr_group;
2650 if (!IN6_IS_ADDR_MULTICAST(&gsa->sin6.sin6_addr))
2651 return (EINVAL);
2652
2653 gsa->sin6.sin6_port = 0; /* ignore port */
2654
2655 ifnet_head_lock_shared();
2656 if (msfr.msfr_ifindex == 0 || (u_int)if_index < msfr.msfr_ifindex) {
2657 ifnet_head_done();
2658 return (EADDRNOTAVAIL);
2659 }
2660 ifp = ifindex2ifnet[msfr.msfr_ifindex];
2661 ifnet_head_done();
2662 if (ifp == NULL)
2663 return (EADDRNOTAVAIL);
2664
2665 (void)in6_setscope(&gsa->sin6.sin6_addr, ifp, NULL);
2666
2667 /*
2668 * Take the INP write lock.
2669 * Check if this socket is a member of this group.
2670 */
2671 imo = in6p_findmoptions(inp);
2672 if (imo == NULL)
2673 return (ENOMEM);
2674
2675 IM6O_LOCK(imo);
2676 idx = im6o_match_group(imo, ifp, &gsa->sa);
2677 if (idx == (size_t)-1 || imo->im6o_mfilters == NULL) {
2678 error = EADDRNOTAVAIL;
2679 goto out_imo_locked;
2680 }
2681 inm = imo->im6o_membership[idx];
2682 imf = &imo->im6o_mfilters[idx];
2683
2684 /*
2685 * Begin state merge transaction at socket layer.
2686 */
2687
2688 imf->im6f_st[1] = msfr.msfr_fmode;
2689
2690 /*
2691 * Apply any new source filters, if present.
2692 * Make a copy of the user-space source vector so
2693 * that we may copy them with a single copyin. This
2694 * allows us to deal with page faults up-front.
2695 */
2696 if (msfr.msfr_nsrcs > 0) {
2697 struct in6_msource *lims;
2698 struct sockaddr_in6 *psin;
2699 struct sockaddr_storage *kss, *pkss;
2700 unsigned int i;
2701
2702 if (IS_64BIT_PROCESS(current_proc()))
2703 tmp_ptr = msfr64.msfr_srcs;
2704 else
2705 tmp_ptr = CAST_USER_ADDR_T(msfr32.msfr_srcs);
2706
2707 MLD_PRINTF(("%s: loading %lu source list entries\n",
2708 __func__, (unsigned long)msfr.msfr_nsrcs));
2709 kss = _MALLOC((size_t) msfr.msfr_nsrcs * sizeof(*kss),
2710 M_TEMP, M_WAITOK);
2711 if (kss == NULL) {
2712 error = ENOMEM;
2713 goto out_imo_locked;
2714 }
2715
2716 error = copyin(tmp_ptr, kss,
2717 (size_t) msfr.msfr_nsrcs * sizeof(*kss));
2718 if (error) {
2719 FREE(kss, M_TEMP);
2720 goto out_imo_locked;
2721 }
2722
2723 /*
2724 * Mark all source filters as UNDEFINED at t1.
2725 * Restore new group filter mode, as im6f_leave()
2726 * will set it to INCLUDE.
2727 */
2728 im6f_leave(imf);
2729 imf->im6f_st[1] = msfr.msfr_fmode;
2730
2731 /*
2732 * Update socket layer filters at t1, lazy-allocating
2733 * new entries. This saves a bunch of memory at the
2734 * cost of one RB_FIND() per source entry; duplicate
2735 * entries in the msfr_nsrcs vector are ignored.
2736 * If we encounter an error, rollback transaction.
2737 *
2738 * XXX This too could be replaced with a set-symmetric
2739 * difference like loop to avoid walking from root
2740 * every time, as the key space is common.
2741 */
2742 for (i = 0, pkss = kss; i < msfr.msfr_nsrcs; i++, pkss++) {
2743 psin = (struct sockaddr_in6 *)pkss;
2744 if (psin->sin6_family != AF_INET6) {
2745 error = EAFNOSUPPORT;
2746 break;
2747 }
2748 if (psin->sin6_len != sizeof(struct sockaddr_in6)) {
2749 error = EINVAL;
2750 break;
2751 }
2752 if (IN6_IS_ADDR_MULTICAST(&psin->sin6_addr)) {
2753 error = EINVAL;
2754 break;
2755 }
2756 /*
2757 * TODO: Validate embedded scope ID in source
2758 * list entry against passed-in ifp, if and only
2759 * if source list filter entry is iface or node local.
2760 */
2761 in6_clearscope(&psin->sin6_addr);
2762 error = im6f_get_source(imf, psin, &lims);
2763 if (error)
2764 break;
2765 lims->im6sl_st[1] = imf->im6f_st[1];
2766 }
2767 FREE(kss, M_TEMP);
2768 }
2769
2770 if (error)
2771 goto out_im6f_rollback;
2772
2773 /*
2774 * Begin state merge transaction at MLD layer.
2775 */
2776 IN6M_LOCK(inm);
2777 MLD_PRINTF(("%s: merge inm state\n", __func__));
2778 error = in6m_merge(inm, imf);
2779 if (error) {
2780 MLD_PRINTF(("%s: failed to merge inm state\n", __func__));
2781 IN6M_UNLOCK(inm);
2782 goto out_im6f_rollback;
2783 }
2784
2785 MLD_PRINTF(("%s: doing mld downcall\n", __func__));
2786 error = mld_change_state(inm, 0);
2787 IN6M_UNLOCK(inm);
2788 #if MLD_DEBUG
2789 if (error)
2790 MLD_PRINTF(("%s: failed mld downcall\n", __func__));
2791 #endif
2792
2793 out_im6f_rollback:
2794 if (error)
2795 im6f_rollback(imf);
2796 else
2797 im6f_commit(imf);
2798
2799 im6f_reap(imf);
2800
2801 out_imo_locked:
2802 IM6O_UNLOCK(imo);
2803 IM6O_REMREF(imo); /* from in6p_findmoptions() */
2804
2805 return (error);
2806 }
2807
2808 /*
2809 * Set the IP multicast options in response to user setsockopt().
2810 *
2811 * Many of the socket options handled in this function duplicate the
2812 * functionality of socket options in the regular unicast API. However,
2813 * it is not possible to merge the duplicate code, because the idempotence
2814 * of the IPv6 multicast part of the BSD Sockets API must be preserved;
2815 * the effects of these options must be treated as separate and distinct.
2816 *
2817 */
2818 int
2819 ip6_setmoptions(struct inpcb *inp, struct sockopt *sopt)
2820 {
2821 struct ip6_moptions *im6o;
2822 int error;
2823
2824 error = 0;
2825
2826 /*
2827 * If socket is neither of type SOCK_RAW or SOCK_DGRAM,
2828 * or is a divert socket, reject it.
2829 */
2830 if (inp->inp_socket->so_proto->pr_protocol == IPPROTO_DIVERT ||
2831 (inp->inp_socket->so_proto->pr_type != SOCK_RAW &&
2832 inp->inp_socket->so_proto->pr_type != SOCK_DGRAM))
2833 return (EOPNOTSUPP);
2834
2835 switch (sopt->sopt_name) {
2836 case IPV6_MULTICAST_IF:
2837 error = in6p_set_multicast_if(inp, sopt);
2838 break;
2839
2840 case IPV6_MULTICAST_HOPS: {
2841 int hlim;
2842
2843 if (sopt->sopt_valsize != sizeof(int)) {
2844 error = EINVAL;
2845 break;
2846 }
2847 error = sooptcopyin(sopt, &hlim, sizeof(hlim), sizeof(int));
2848 if (error)
2849 break;
2850 if (hlim < -1 || hlim > 255) {
2851 error = EINVAL;
2852 break;
2853 } else if (hlim == -1) {
2854 hlim = ip6_defmcasthlim;
2855 }
2856 im6o = in6p_findmoptions(inp);
2857 if (im6o == NULL) {
2858 error = ENOMEM;
2859 break;
2860 }
2861 IM6O_LOCK(im6o);
2862 im6o->im6o_multicast_hlim = hlim;
2863 IM6O_UNLOCK(im6o);
2864 IM6O_REMREF(im6o); /* from in6p_findmoptions() */
2865 break;
2866 }
2867
2868 case IPV6_MULTICAST_LOOP: {
2869 u_int loop;
2870
2871 /*
2872 * Set the loopback flag for outgoing multicast packets.
2873 * Must be zero or one.
2874 */
2875 if (sopt->sopt_valsize != sizeof(u_int)) {
2876 error = EINVAL;
2877 break;
2878 }
2879 error = sooptcopyin(sopt, &loop, sizeof(u_int), sizeof(u_int));
2880 if (error)
2881 break;
2882 if (loop > 1) {
2883 error = EINVAL;
2884 break;
2885 }
2886 im6o = in6p_findmoptions(inp);
2887 if (im6o == NULL) {
2888 error = ENOMEM;
2889 break;
2890 }
2891 IM6O_LOCK(im6o);
2892 im6o->im6o_multicast_loop = loop;
2893 IM6O_UNLOCK(im6o);
2894 IM6O_REMREF(im6o); /* from in6p_findmoptions() */
2895 break;
2896 }
2897
2898 case IPV6_JOIN_GROUP:
2899 case MCAST_JOIN_GROUP:
2900 case MCAST_JOIN_SOURCE_GROUP:
2901 error = in6p_join_group(inp, sopt);
2902 break;
2903
2904 case IPV6_LEAVE_GROUP:
2905 case MCAST_LEAVE_GROUP:
2906 case MCAST_LEAVE_SOURCE_GROUP:
2907 error = in6p_leave_group(inp, sopt);
2908 break;
2909
2910 case MCAST_BLOCK_SOURCE:
2911 case MCAST_UNBLOCK_SOURCE:
2912 error = in6p_block_unblock_source(inp, sopt);
2913 break;
2914
2915 case IPV6_MSFILTER:
2916 error = in6p_set_source_filters(inp, sopt);
2917 break;
2918
2919 default:
2920 error = EOPNOTSUPP;
2921 break;
2922 }
2923
2924 return (error);
2925 }
2926 /*
2927 * Expose MLD's multicast filter mode and source list(s) to userland,
2928 * keyed by (ifindex, group).
2929 * The filter mode is written out as a uint32_t, followed by
2930 * 0..n of struct in6_addr.
2931 * For use by ifmcstat(8).
2932 */
2933 static int
2934 sysctl_ip6_mcast_filters SYSCTL_HANDLER_ARGS
2935 {
2936 #pragma unused(oidp)
2937
2938 struct in6_addr mcaddr;
2939 struct in6_addr src;
2940 struct ifnet *ifp;
2941 struct in6_multi *inm;
2942 struct in6_multistep step;
2943 struct ip6_msource *ims;
2944 int *name;
2945 int retval = 0;
2946 u_int namelen;
2947 uint32_t fmode, ifindex;
2948
2949 name = (int *)arg1;
2950 namelen = arg2;
2951
2952 if (req->newptr != USER_ADDR_NULL)
2953 return (EPERM);
2954
2955 /* int: ifindex + 4 * 32 bits of IPv6 address */
2956 if (namelen != 5)
2957 return (EINVAL);
2958
2959 ifindex = name[0];
2960 ifnet_head_lock_shared();
2961 if (ifindex <= 0 || ifindex > (u_int)if_index) {
2962 MLD_PRINTF(("%s: ifindex %u out of range\n",
2963 __func__, ifindex));
2964 ifnet_head_done();
2965 return (ENOENT);
2966 }
2967
2968 memcpy(&mcaddr, &name[1], sizeof(struct in6_addr));
2969 if (!IN6_IS_ADDR_MULTICAST(&mcaddr)) {
2970 MLD_PRINTF(("%s: group %s is not multicast\n",
2971 __func__, ip6_sprintf(&mcaddr)));
2972 ifnet_head_done();
2973 return (EINVAL);
2974 }
2975
2976 ifp = ifindex2ifnet[ifindex];
2977 ifnet_head_done();
2978 if (ifp == NULL) {
2979 MLD_PRINTF(("%s: no ifp for ifindex %u\n", __func__, ifindex));
2980 return (ENOENT);
2981 }
2982 /*
2983 * Internal MLD lookups require that scope/zone ID is set.
2984 */
2985 (void)in6_setscope(&mcaddr, ifp, NULL);
2986
2987 in6_multihead_lock_shared();
2988 IN6_FIRST_MULTI(step, inm);
2989 while (inm != NULL) {
2990 IN6M_LOCK(inm);
2991 if (inm->in6m_ifp != ifp)
2992 goto next;
2993
2994 if (!IN6_ARE_ADDR_EQUAL(&inm->in6m_addr, &mcaddr))
2995 goto next;
2996
2997 fmode = inm->in6m_st[1].iss_fmode;
2998 retval = SYSCTL_OUT(req, &fmode, sizeof(uint32_t));
2999 if (retval != 0) {
3000 IN6M_UNLOCK(inm);
3001 break; /* abort */
3002 }
3003 RB_FOREACH(ims, ip6_msource_tree, &inm->in6m_srcs) {
3004 MLD_PRINTF(("%s: visit node %p\n", __func__, ims));
3005 /*
3006 * Only copy-out sources which are in-mode.
3007 */
3008 if (fmode != im6s_get_mode(inm, ims, 1)) {
3009 MLD_PRINTF(("%s: skip non-in-mode\n",
3010 __func__));
3011 continue; /* process next source */
3012 }
3013 src = ims->im6s_addr;
3014 retval = SYSCTL_OUT(req, &src, sizeof(struct in6_addr));
3015 if (retval != 0)
3016 break; /* process next inm */
3017 }
3018 next:
3019 IN6M_UNLOCK(inm);
3020 IN6_NEXT_MULTI(step, inm);
3021 }
3022 in6_multihead_lock_done();
3023
3024 return (retval);
3025 }
3026
3027 void
3028 in6_multi_init(void)
3029 {
3030 PE_parse_boot_argn("ifa_debug", &in6m_debug, sizeof (in6m_debug));
3031
3032 /* Setup lock group and attribute for in6_multihead */
3033 in6_multihead_lock_grp_attr = lck_grp_attr_alloc_init();
3034 in6_multihead_lock_grp = lck_grp_alloc_init("in6_multihead",
3035 in6_multihead_lock_grp_attr);
3036 in6_multihead_lock_attr = lck_attr_alloc_init();
3037 lck_rw_init(&in6_multihead_lock, in6_multihead_lock_grp,
3038 in6_multihead_lock_attr);
3039
3040 lck_mtx_init(&in6m_trash_lock, in6_multihead_lock_grp,
3041 in6_multihead_lock_attr);
3042 TAILQ_INIT(&in6m_trash_head);
3043
3044 in6m_size = (in6m_debug == 0) ? sizeof (struct in6_multi) :
3045 sizeof (struct in6_multi_dbg);
3046 in6m_zone = zinit(in6m_size, IN6M_ZONE_MAX * in6m_size,
3047 0, IN6M_ZONE_NAME);
3048 if (in6m_zone == NULL) {
3049 panic("%s: failed allocating %s", __func__, IN6M_ZONE_NAME);
3050 /* NOTREACHED */
3051 }
3052 zone_change(in6m_zone, Z_EXPAND, TRUE);
3053
3054 imm_size = sizeof (struct in6_multi_mship);
3055 imm_zone = zinit(imm_size, IMM_ZONE_MAX * imm_size, 0, IMM_ZONE_NAME);
3056 if (imm_zone == NULL) {
3057 panic("%s: failed allocating %s", __func__, IMM_ZONE_NAME);
3058 /* NOTREACHED */
3059 }
3060 zone_change(imm_zone, Z_EXPAND, TRUE);
3061
3062 ip6ms_size = sizeof (struct ip6_msource);
3063 ip6ms_zone = zinit(ip6ms_size, IP6MS_ZONE_MAX * ip6ms_size,
3064 0, IP6MS_ZONE_NAME);
3065 if (ip6ms_zone == NULL) {
3066 panic("%s: failed allocating %s", __func__, IP6MS_ZONE_NAME);
3067 /* NOTREACHED */
3068 }
3069 zone_change(ip6ms_zone, Z_EXPAND, TRUE);
3070
3071 in6ms_size = sizeof (struct in6_msource);
3072 in6ms_zone = zinit(in6ms_size, IN6MS_ZONE_MAX * in6ms_size,
3073 0, IN6MS_ZONE_NAME);
3074 if (in6ms_zone == NULL) {
3075 panic("%s: failed allocating %s", __func__, IN6MS_ZONE_NAME);
3076 /* NOTREACHED */
3077 }
3078 zone_change(in6ms_zone, Z_EXPAND, TRUE);
3079 }
3080
3081 static struct in6_multi *
3082 in6_multi_alloc(int how)
3083 {
3084 struct in6_multi *in6m;
3085
3086 in6m = (how == M_WAITOK) ? zalloc(in6m_zone) :
3087 zalloc_noblock(in6m_zone);
3088 if (in6m != NULL) {
3089 bzero(in6m, in6m_size);
3090 lck_mtx_init(&in6m->in6m_lock, in6_multihead_lock_grp,
3091 in6_multihead_lock_attr);
3092 in6m->in6m_debug |= IFD_ALLOC;
3093 if (in6m_debug != 0) {
3094 in6m->in6m_debug |= IFD_DEBUG;
3095 in6m->in6m_trace = in6m_trace;
3096 }
3097 }
3098 return (in6m);
3099 }
3100
3101 static void
3102 in6_multi_free(struct in6_multi *in6m)
3103 {
3104 IN6M_LOCK(in6m);
3105 if (in6m->in6m_debug & IFD_ATTACHED) {
3106 panic("%s: attached in6m=%p is being freed", __func__, in6m);
3107 /* NOTREACHED */
3108 } else if (in6m->in6m_ifma != NULL) {
3109 panic("%s: ifma not NULL for in6m=%p", __func__, in6m);
3110 /* NOTREACHED */
3111 } else if (!(in6m->in6m_debug & IFD_ALLOC)) {
3112 panic("%s: in6m %p cannot be freed", __func__, in6m);
3113 /* NOTREACHED */
3114 } else if (in6m->in6m_refcount != 0) {
3115 panic("%s: non-zero refcount in6m=%p", __func__, in6m);
3116 /* NOTREACHED */
3117 } else if (in6m->in6m_reqcnt != 0) {
3118 panic("%s: non-zero reqcnt in6m=%p", __func__, in6m);
3119 /* NOTREACHED */
3120 }
3121
3122 /* Free any pending MLDv2 state-change records */
3123 IF_DRAIN(&in6m->in6m_scq);
3124
3125 in6m->in6m_debug &= ~IFD_ALLOC;
3126 if ((in6m->in6m_debug & (IFD_DEBUG | IFD_TRASHED)) ==
3127 (IFD_DEBUG | IFD_TRASHED)) {
3128 lck_mtx_lock(&in6m_trash_lock);
3129 TAILQ_REMOVE(&in6m_trash_head, (struct in6_multi_dbg *)in6m,
3130 in6m_trash_link);
3131 lck_mtx_unlock(&in6m_trash_lock);
3132 in6m->in6m_debug &= ~IFD_TRASHED;
3133 }
3134 IN6M_UNLOCK(in6m);
3135
3136 lck_mtx_destroy(&in6m->in6m_lock, in6_multihead_lock_grp);
3137 zfree(in6m_zone, in6m);
3138 }
3139
3140 static void
3141 in6_multi_attach(struct in6_multi *in6m)
3142 {
3143 in6_multihead_lock_assert(LCK_RW_ASSERT_EXCLUSIVE);
3144 IN6M_LOCK_ASSERT_HELD(in6m);
3145
3146 if (in6m->in6m_debug & IFD_ATTACHED) {
3147 panic("%s: Attempt to attach an already attached in6m=%p",
3148 __func__, in6m);
3149 /* NOTREACHED */
3150 } else if (in6m->in6m_debug & IFD_TRASHED) {
3151 panic("%s: Attempt to reattach a detached in6m=%p",
3152 __func__, in6m);
3153 /* NOTREACHED */
3154 }
3155
3156 in6m->in6m_reqcnt++;
3157 VERIFY(in6m->in6m_reqcnt == 1);
3158 IN6M_ADDREF_LOCKED(in6m);
3159 in6m->in6m_debug |= IFD_ATTACHED;
3160 /*
3161 * Reattach case: If debugging is enabled, take it
3162 * out of the trash list and clear IFD_TRASHED.
3163 */
3164 if ((in6m->in6m_debug & (IFD_DEBUG | IFD_TRASHED)) ==
3165 (IFD_DEBUG | IFD_TRASHED)) {
3166 /* Become a regular mutex, just in case */
3167 IN6M_CONVERT_LOCK(in6m);
3168 lck_mtx_lock(&in6m_trash_lock);
3169 TAILQ_REMOVE(&in6m_trash_head, (struct in6_multi_dbg *)in6m,
3170 in6m_trash_link);
3171 lck_mtx_unlock(&in6m_trash_lock);
3172 in6m->in6m_debug &= ~IFD_TRASHED;
3173 }
3174
3175 LIST_INSERT_HEAD(&in6_multihead, in6m, in6m_entry);
3176 }
3177
3178 int
3179 in6_multi_detach(struct in6_multi *in6m)
3180 {
3181 in6_multihead_lock_assert(LCK_RW_ASSERT_EXCLUSIVE);
3182 IN6M_LOCK_ASSERT_HELD(in6m);
3183
3184 if (in6m->in6m_reqcnt == 0) {
3185 panic("%s: in6m=%p negative reqcnt", __func__, in6m);
3186 /* NOTREACHED */
3187 }
3188
3189 --in6m->in6m_reqcnt;
3190 if (in6m->in6m_reqcnt > 0)
3191 return (0);
3192
3193 if (!(in6m->in6m_debug & IFD_ATTACHED)) {
3194 panic("%s: Attempt to detach an unattached record in6m=%p",
3195 __func__, in6m);
3196 /* NOTREACHED */
3197 } else if (in6m->in6m_debug & IFD_TRASHED) {
3198 panic("%s: in6m %p is already in trash list", __func__, in6m);
3199 /* NOTREACHED */
3200 }
3201
3202 /*
3203 * NOTE: Caller calls IFMA_REMREF
3204 */
3205 in6m->in6m_debug &= ~IFD_ATTACHED;
3206 LIST_REMOVE(in6m, in6m_entry);
3207
3208 if (in6m->in6m_debug & IFD_DEBUG) {
3209 /* Become a regular mutex, just in case */
3210 IN6M_CONVERT_LOCK(in6m);
3211 lck_mtx_lock(&in6m_trash_lock);
3212 TAILQ_INSERT_TAIL(&in6m_trash_head,
3213 (struct in6_multi_dbg *)in6m, in6m_trash_link);
3214 lck_mtx_unlock(&in6m_trash_lock);
3215 in6m->in6m_debug |= IFD_TRASHED;
3216 }
3217
3218 return (1);
3219 }
3220
3221 void
3222 in6m_addref(struct in6_multi *in6m, int locked)
3223 {
3224 if (!locked)
3225 IN6M_LOCK_SPIN(in6m);
3226 else
3227 IN6M_LOCK_ASSERT_HELD(in6m);
3228
3229 if (++in6m->in6m_refcount == 0) {
3230 panic("%s: in6m=%p wraparound refcnt", __func__, in6m);
3231 /* NOTREACHED */
3232 } else if (in6m->in6m_trace != NULL) {
3233 (*in6m->in6m_trace)(in6m, TRUE);
3234 }
3235 if (!locked)
3236 IN6M_UNLOCK(in6m);
3237 }
3238
3239 void
3240 in6m_remref(struct in6_multi *in6m, int locked)
3241 {
3242 struct ifmultiaddr *ifma;
3243 struct mld_ifinfo *mli;
3244
3245 if (!locked)
3246 IN6M_LOCK_SPIN(in6m);
3247 else
3248 IN6M_LOCK_ASSERT_HELD(in6m);
3249
3250 if (in6m->in6m_refcount == 0 || (in6m->in6m_refcount == 1 && locked)) {
3251 panic("%s: in6m=%p negative refcnt", __func__, in6m);
3252 /* NOTREACHED */
3253 } else if (in6m->in6m_trace != NULL) {
3254 (*in6m->in6m_trace)(in6m, FALSE);
3255 }
3256
3257 --in6m->in6m_refcount;
3258 if (in6m->in6m_refcount > 0) {
3259 if (!locked)
3260 IN6M_UNLOCK(in6m);
3261 return;
3262 }
3263
3264 /*
3265 * Synchronization with in6_mc_get(). In the event the in6m has been
3266 * detached, the underlying ifma would still be in the if_multiaddrs
3267 * list, and thus can be looked up via if_addmulti(). At that point,
3268 * the only way to find this in6m is via ifma_protospec. To avoid
3269 * race conditions between the last in6m_remref() of that in6m and its
3270 * use via ifma_protospec, in6_multihead lock is used for serialization.
3271 * In order to avoid violating the lock order, we must drop in6m_lock
3272 * before acquiring in6_multihead lock. To prevent the in6m from being
3273 * freed prematurely, we hold an extra reference.
3274 */
3275 ++in6m->in6m_refcount;
3276 IN6M_UNLOCK(in6m);
3277 in6_multihead_lock_shared();
3278 IN6M_LOCK_SPIN(in6m);
3279 --in6m->in6m_refcount;
3280 if (in6m->in6m_refcount > 0) {
3281 /* We've lost the race, so abort since in6m is still in use */
3282 IN6M_UNLOCK(in6m);
3283 in6_multihead_lock_done();
3284 /* If it was locked, return it as such */
3285 if (locked)
3286 IN6M_LOCK(in6m);
3287 return;
3288 }
3289 in6m_purge(in6m);
3290 ifma = in6m->in6m_ifma;
3291 in6m->in6m_ifma = NULL;
3292 in6m->in6m_ifp = NULL;
3293 mli = in6m->in6m_mli;
3294 in6m->in6m_mli = NULL;
3295 IN6M_UNLOCK(in6m);
3296 IFMA_LOCK_SPIN(ifma);
3297 ifma->ifma_protospec = NULL;
3298 IFMA_UNLOCK(ifma);
3299 in6_multihead_lock_done();
3300
3301 in6_multi_free(in6m);
3302 if_delmulti_ifma(ifma);
3303 /* Release reference held to the underlying ifmultiaddr */
3304 IFMA_REMREF(ifma);
3305
3306 if (mli != NULL)
3307 MLI_REMREF(mli);
3308 }
3309
3310 static void
3311 in6m_trace(struct in6_multi *in6m, int refhold)
3312 {
3313 struct in6_multi_dbg *in6m_dbg = (struct in6_multi_dbg *)in6m;
3314 ctrace_t *tr;
3315 u_int32_t idx;
3316 u_int16_t *cnt;
3317
3318 if (!(in6m->in6m_debug & IFD_DEBUG)) {
3319 panic("%s: in6m %p has no debug structure", __func__, in6m);
3320 /* NOTREACHED */
3321 }
3322 if (refhold) {
3323 cnt = &in6m_dbg->in6m_refhold_cnt;
3324 tr = in6m_dbg->in6m_refhold;
3325 } else {
3326 cnt = &in6m_dbg->in6m_refrele_cnt;
3327 tr = in6m_dbg->in6m_refrele;
3328 }
3329
3330 idx = atomic_add_16_ov(cnt, 1) % IN6M_TRACE_HIST_SIZE;
3331 ctrace_record(&tr[idx]);
3332 }
3333
3334 static struct in6_multi_mship *
3335 in6_multi_mship_alloc(int how)
3336 {
3337 struct in6_multi_mship *imm;
3338
3339 imm = (how == M_WAITOK) ? zalloc(imm_zone) : zalloc_noblock(imm_zone);
3340 if (imm != NULL)
3341 bzero(imm, imm_size);
3342
3343 return (imm);
3344 }
3345
3346 static void
3347 in6_multi_mship_free(struct in6_multi_mship *imm)
3348 {
3349 if (imm->i6mm_maddr != NULL) {
3350 panic("%s: i6mm_maddr not NULL for imm=%p", __func__, imm);
3351 /* NOTREACHED */
3352 }
3353 zfree(imm_zone, imm);
3354 }
3355
3356 void
3357 in6_multihead_lock_exclusive(void)
3358 {
3359 lck_rw_lock_exclusive(&in6_multihead_lock);
3360 }
3361
3362 void
3363 in6_multihead_lock_shared(void)
3364 {
3365 lck_rw_lock_shared(&in6_multihead_lock);
3366 }
3367
3368 void
3369 in6_multihead_lock_assert(int what)
3370 {
3371 lck_rw_assert(&in6_multihead_lock, what);
3372 }
3373
3374 void
3375 in6_multihead_lock_done(void)
3376 {
3377 lck_rw_done(&in6_multihead_lock);
3378 }
3379
3380 static struct ip6_msource *
3381 ip6ms_alloc(int how)
3382 {
3383 struct ip6_msource *i6ms;
3384
3385 i6ms = (how == M_WAITOK) ? zalloc(ip6ms_zone) :
3386 zalloc_noblock(ip6ms_zone);
3387 if (i6ms != NULL)
3388 bzero(i6ms, ip6ms_size);
3389
3390 return (i6ms);
3391 }
3392
3393 static void
3394 ip6ms_free(struct ip6_msource *i6ms)
3395 {
3396 zfree(ip6ms_zone, i6ms);
3397 }
3398
3399 static struct in6_msource *
3400 in6ms_alloc(int how)
3401 {
3402 struct in6_msource *in6ms;
3403
3404 in6ms = (how == M_WAITOK) ? zalloc(in6ms_zone) :
3405 zalloc_noblock(in6ms_zone);
3406 if (in6ms != NULL)
3407 bzero(in6ms, in6ms_size);
3408
3409 return (in6ms);
3410 }
3411
3412 static void
3413 in6ms_free(struct in6_msource *in6ms)
3414 {
3415 zfree(in6ms_zone, in6ms);
3416 }
3417
3418 #ifdef MLD_DEBUG
3419
3420 static const char *in6m_modestrs[] = { "un\n", "in", "ex" };
3421
3422 static const char *
3423 in6m_mode_str(const int mode)
3424 {
3425 if (mode >= MCAST_UNDEFINED && mode <= MCAST_EXCLUDE)
3426 return (in6m_modestrs[mode]);
3427 return ("??");
3428 }
3429
3430 static const char *in6m_statestrs[] = {
3431 "not-member\n",
3432 "silent\n",
3433 "idle\n",
3434 "lazy\n",
3435 "sleeping\n",
3436 "awakening\n",
3437 "query-pending\n",
3438 "sg-query-pending\n",
3439 "leaving"
3440 };
3441
3442 static const char *
3443 in6m_state_str(const int state)
3444 {
3445 if (state >= MLD_NOT_MEMBER && state <= MLD_LEAVING_MEMBER)
3446 return (in6m_statestrs[state]);
3447 return ("??");
3448 }
3449
3450 /*
3451 * Dump an in6_multi structure to the console.
3452 */
3453 void
3454 in6m_print(const struct in6_multi *inm)
3455 {
3456 int t;
3457
3458 IN6M_LOCK_ASSERT_HELD(IN6M_CAST_TO_NONCONST(inm));
3459
3460 if (mld_debug == 0)
3461 return;
3462
3463 printf("%s: --- begin in6m %p ---\n", __func__, inm);
3464 printf("addr %s ifp %p(%s%d) ifma %p\n",
3465 ip6_sprintf(&inm->in6m_addr),
3466 inm->in6m_ifp,
3467 inm->in6m_ifp->if_name,
3468 inm->in6m_ifp->if_unit,
3469 inm->in6m_ifma);
3470 printf("timer %u state %s refcount %u scq.len %u\n",
3471 inm->in6m_timer,
3472 in6m_state_str(inm->in6m_state),
3473 inm->in6m_refcount,
3474 inm->in6m_scq.ifq_len);
3475 printf("mli %p nsrc %lu sctimer %u scrv %u\n",
3476 inm->in6m_mli,
3477 inm->in6m_nsrc,
3478 inm->in6m_sctimer,
3479 inm->in6m_scrv);
3480 for (t = 0; t < 2; t++) {
3481 printf("t%d: fmode %s asm %u ex %u in %u rec %u\n", t,
3482 in6m_mode_str(inm->in6m_st[t].iss_fmode),
3483 inm->in6m_st[t].iss_asm,
3484 inm->in6m_st[t].iss_ex,
3485 inm->in6m_st[t].iss_in,
3486 inm->in6m_st[t].iss_rec);
3487 }
3488 printf("%s: --- end in6m %p ---\n", __func__, inm);
3489 }
3490
3491 #else
3492
3493 void
3494 in6m_print(__unused const struct in6_multi *inm)
3495 {
3496
3497 }
3498
3499 #endif