xref: /onnv-gate/usr/src/uts/common/inet/ip/ip_multi.c (revision 12811:dec8caad2567)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51676Sjpk  * Common Development and Distribution License (the "License").
61676Sjpk  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*12811SRavichandra.Nallan@Sun.COM  * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
23*12811SRavichandra.Nallan@Sun.COM  * Copyright (c) 1990 Mentat Inc.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #include <sys/types.h>
270Sstevel@tonic-gate #include <sys/stream.h>
280Sstevel@tonic-gate #include <sys/dlpi.h>
290Sstevel@tonic-gate #include <sys/stropts.h>
300Sstevel@tonic-gate #include <sys/strsun.h>
310Sstevel@tonic-gate #include <sys/ddi.h>
320Sstevel@tonic-gate #include <sys/cmn_err.h>
332958Sdr146992 #include <sys/sdt.h>
340Sstevel@tonic-gate #include <sys/zone.h>
350Sstevel@tonic-gate 
360Sstevel@tonic-gate #include <sys/param.h>
370Sstevel@tonic-gate #include <sys/socket.h>
381676Sjpk #include <sys/sockio.h>
390Sstevel@tonic-gate #include <net/if.h>
400Sstevel@tonic-gate #include <sys/systm.h>
415758Sjarrett #include <sys/strsubr.h>
420Sstevel@tonic-gate #include <net/route.h>
430Sstevel@tonic-gate #include <netinet/in.h>
440Sstevel@tonic-gate #include <net/if_dl.h>
450Sstevel@tonic-gate #include <netinet/ip6.h>
460Sstevel@tonic-gate #include <netinet/icmp6.h>
470Sstevel@tonic-gate 
480Sstevel@tonic-gate #include <inet/common.h>
490Sstevel@tonic-gate #include <inet/mi.h>
500Sstevel@tonic-gate #include <inet/nd.h>
510Sstevel@tonic-gate #include <inet/arp.h>
520Sstevel@tonic-gate #include <inet/ip.h>
530Sstevel@tonic-gate #include <inet/ip6.h>
540Sstevel@tonic-gate #include <inet/ip_if.h>
550Sstevel@tonic-gate #include <inet/ip_ndp.h>
560Sstevel@tonic-gate #include <inet/ip_multi.h>
570Sstevel@tonic-gate #include <inet/ipclassifier.h>
580Sstevel@tonic-gate #include <inet/ipsec_impl.h>
590Sstevel@tonic-gate #include <inet/sctp_ip.h>
600Sstevel@tonic-gate #include <inet/ip_listutils.h>
61741Smasputra #include <inet/udp_impl.h>
620Sstevel@tonic-gate 
630Sstevel@tonic-gate /* igmpv3/mldv2 source filter manipulation */
640Sstevel@tonic-gate static void	ilm_bld_flists(conn_t *conn, void *arg);
650Sstevel@tonic-gate static void	ilm_gen_filter(ilm_t *ilm, mcast_record_t *fmode,
660Sstevel@tonic-gate     slist_t *flist);
670Sstevel@tonic-gate 
6811042SErik.Nordmark@Sun.COM static ilm_t	*ilm_add(ill_t *ill, const in6_addr_t *group,
690Sstevel@tonic-gate     ilg_stat_t ilgstat, mcast_record_t ilg_fmode, slist_t *ilg_flist,
708485SPeter.Memishian@Sun.COM     zoneid_t zoneid);
710Sstevel@tonic-gate static void	ilm_delete(ilm_t *ilm);
7211042SErik.Nordmark@Sun.COM static int	ilm_numentries(ill_t *, const in6_addr_t *);
7311042SErik.Nordmark@Sun.COM 
7411042SErik.Nordmark@Sun.COM static ilm_t	*ip_addmulti_serial(const in6_addr_t *, ill_t *, zoneid_t,
7511042SErik.Nordmark@Sun.COM     ilg_stat_t, mcast_record_t, slist_t *, int *);
7611042SErik.Nordmark@Sun.COM static ilm_t	*ip_addmulti_impl(const in6_addr_t *, ill_t *,
7711042SErik.Nordmark@Sun.COM     zoneid_t, ilg_stat_t, mcast_record_t, slist_t *, int *);
7811042SErik.Nordmark@Sun.COM static int	ip_delmulti_serial(ilm_t *, boolean_t, boolean_t);
7911042SErik.Nordmark@Sun.COM static int	ip_delmulti_impl(ilm_t *, boolean_t, boolean_t);
8011042SErik.Nordmark@Sun.COM 
8111042SErik.Nordmark@Sun.COM static int	ip_ll_multireq(ill_t *ill, const in6_addr_t *group,
8211042SErik.Nordmark@Sun.COM     t_uscalar_t);
8311042SErik.Nordmark@Sun.COM static ilg_t	*ilg_lookup(conn_t *, const in6_addr_t *, ipaddr_t ifaddr,
8411042SErik.Nordmark@Sun.COM     uint_t ifindex);
8511042SErik.Nordmark@Sun.COM 
8611042SErik.Nordmark@Sun.COM static int	ilg_add(conn_t *connp, const in6_addr_t *group,
8711042SErik.Nordmark@Sun.COM     ipaddr_t ifaddr, uint_t ifindex, ill_t *ill, mcast_record_t fmode,
8811042SErik.Nordmark@Sun.COM     const in6_addr_t *v6src);
890Sstevel@tonic-gate static void	ilg_delete(conn_t *connp, ilg_t *ilg, const in6_addr_t *src);
900Sstevel@tonic-gate static mblk_t	*ill_create_dl(ill_t *ill, uint32_t dl_primitive,
9111042SErik.Nordmark@Sun.COM     uint32_t *addr_lenp, uint32_t *addr_offp);
9211042SErik.Nordmark@Sun.COM static int	ip_opt_delete_group_excl(conn_t *connp,
9311042SErik.Nordmark@Sun.COM     const in6_addr_t *v6group, ipaddr_t ifaddr, uint_t ifindex,
9411042SErik.Nordmark@Sun.COM     mcast_record_t fmode, const in6_addr_t *v6src);
9511042SErik.Nordmark@Sun.COM 
9611042SErik.Nordmark@Sun.COM static	ilm_t	*ilm_lookup(ill_t *, const in6_addr_t *, zoneid_t);
9711042SErik.Nordmark@Sun.COM 
9811042SErik.Nordmark@Sun.COM static int	ip_msfilter_ill(conn_t *, mblk_t *, const ip_ioctl_cmd_t *,
9911042SErik.Nordmark@Sun.COM     ill_t **);
10011042SErik.Nordmark@Sun.COM 
10111042SErik.Nordmark@Sun.COM static void	ilg_check_detach(conn_t *, ill_t *);
10211463SSowmini.Varadhan@Sun.COM static void	ilg_check_reattach(conn_t *, ill_t *);
1030Sstevel@tonic-gate 
1040Sstevel@tonic-gate /*
1050Sstevel@tonic-gate  * MT notes:
1060Sstevel@tonic-gate  *
1070Sstevel@tonic-gate  * Multicast joins operate on both the ilg and ilm structures. Multiple
1080Sstevel@tonic-gate  * threads operating on an conn (socket) trying to do multicast joins
1098485SPeter.Memishian@Sun.COM  * need to synchronize when operating on the ilg. Multiple threads
1100Sstevel@tonic-gate  * potentially operating on different conn (socket endpoints) trying to
1110Sstevel@tonic-gate  * do multicast joins could eventually end up trying to manipulate the
11211042SErik.Nordmark@Sun.COM  * ilm simulatenously and need to synchronize on the access to the ilm.
11311042SErik.Nordmark@Sun.COM  * The access and lookup of the ilm, as well as other ill multicast state,
11411042SErik.Nordmark@Sun.COM  * is under ill_mcast_lock.
11511042SErik.Nordmark@Sun.COM  * The modifications and lookup of ilg entries is serialized using conn_ilg_lock
11611042SErik.Nordmark@Sun.COM  * rwlock. An ilg will not be freed until ilg_refcnt drops to zero.
11711042SErik.Nordmark@Sun.COM  *
11811042SErik.Nordmark@Sun.COM  * In some cases we hold ill_mcast_lock and then acquire conn_ilg_lock, but
11911042SErik.Nordmark@Sun.COM  * never the other way around.
1200Sstevel@tonic-gate  *
1210Sstevel@tonic-gate  * An ilm is an IP data structure used to track multicast join/leave.
1220Sstevel@tonic-gate  * An ilm is associated with a <multicast group, ipif> tuple in IPv4 and
1230Sstevel@tonic-gate  * with just <multicast group> in IPv6. ilm_refcnt is the number of ilg's
12411042SErik.Nordmark@Sun.COM  * referencing the ilm.
12511042SErik.Nordmark@Sun.COM  * The modifications and lookup of ilm entries is serialized using the
12611042SErik.Nordmark@Sun.COM  * ill_mcast_lock rwlock; that lock handles all the igmp/mld modifications
12711042SErik.Nordmark@Sun.COM  * of the ilm state.
12811042SErik.Nordmark@Sun.COM  * ilms are created / destroyed only as writer. ilms
12911042SErik.Nordmark@Sun.COM  * are not passed around. The datapath (anything outside of this file
13011042SErik.Nordmark@Sun.COM  * and igmp.c) use functions that do not return ilms - just the number
13111042SErik.Nordmark@Sun.COM  * of members. So we don't need a dynamic refcount of the number
1320Sstevel@tonic-gate  * of threads holding reference to an ilm.
1330Sstevel@tonic-gate  *
13411042SErik.Nordmark@Sun.COM  * In the cases where we serially access the ilg and ilm, which happens when
13511042SErik.Nordmark@Sun.COM  * we handle the applications requests to join or leave groups and sources,
13611042SErik.Nordmark@Sun.COM  * we use the ill_mcast_serializer mutex to ensure that a multithreaded
13711042SErik.Nordmark@Sun.COM  * application which does concurrent joins and/or leaves on the same group on
13811042SErik.Nordmark@Sun.COM  * the same socket always results in a consistent order for the ilg and ilm
13911042SErik.Nordmark@Sun.COM  * modifications.
1400Sstevel@tonic-gate  *
14111042SErik.Nordmark@Sun.COM  * When a multicast operation results in needing to send a message to
14211042SErik.Nordmark@Sun.COM  * the driver (to join/leave a L2 multicast address), we use ill_dlpi_queue()
14311042SErik.Nordmark@Sun.COM  * which serialized the DLPI requests. The IGMP/MLD code uses ill_mcast_queue()
14411042SErik.Nordmark@Sun.COM  * to send IGMP/MLD IP packet to avoid dropping the lock just to send a packet.
1450Sstevel@tonic-gate  */
1460Sstevel@tonic-gate 
1470Sstevel@tonic-gate #define	GETSTRUCT(structure, number)	\
1480Sstevel@tonic-gate 	((structure *)mi_zalloc(sizeof (structure) * (number)))
1490Sstevel@tonic-gate 
15011042SErik.Nordmark@Sun.COM /*
15111042SErik.Nordmark@Sun.COM  * Caller must ensure that the ilg has not been condemned
15211042SErik.Nordmark@Sun.COM  * The condemned flag is only set in ilg_delete under conn_ilg_lock.
15311042SErik.Nordmark@Sun.COM  *
15411042SErik.Nordmark@Sun.COM  * The caller must hold conn_ilg_lock as writer.
15511042SErik.Nordmark@Sun.COM  */
15611042SErik.Nordmark@Sun.COM static void
ilg_refhold(ilg_t * ilg)15711042SErik.Nordmark@Sun.COM ilg_refhold(ilg_t *ilg)
15811042SErik.Nordmark@Sun.COM {
15911042SErik.Nordmark@Sun.COM 	ASSERT(ilg->ilg_refcnt != 0);
16011042SErik.Nordmark@Sun.COM 	ASSERT(!ilg->ilg_condemned);
16111042SErik.Nordmark@Sun.COM 	ASSERT(RW_WRITE_HELD(&ilg->ilg_connp->conn_ilg_lock));
16211042SErik.Nordmark@Sun.COM 
16311042SErik.Nordmark@Sun.COM 	ilg->ilg_refcnt++;
16411042SErik.Nordmark@Sun.COM }
16511042SErik.Nordmark@Sun.COM 
16611042SErik.Nordmark@Sun.COM static void
ilg_inactive(ilg_t * ilg)16711042SErik.Nordmark@Sun.COM ilg_inactive(ilg_t *ilg)
16811042SErik.Nordmark@Sun.COM {
16911042SErik.Nordmark@Sun.COM 	ASSERT(ilg->ilg_ill == NULL);
17011042SErik.Nordmark@Sun.COM 	ASSERT(ilg->ilg_ilm == NULL);
17111042SErik.Nordmark@Sun.COM 	ASSERT(ilg->ilg_filter == NULL);
17211042SErik.Nordmark@Sun.COM 	ASSERT(ilg->ilg_condemned);
17311042SErik.Nordmark@Sun.COM 
17411042SErik.Nordmark@Sun.COM 	/* Unlink from list */
17511042SErik.Nordmark@Sun.COM 	*ilg->ilg_ptpn = ilg->ilg_next;
17611042SErik.Nordmark@Sun.COM 	if (ilg->ilg_next != NULL)
17711042SErik.Nordmark@Sun.COM 		ilg->ilg_next->ilg_ptpn = ilg->ilg_ptpn;
17811042SErik.Nordmark@Sun.COM 	ilg->ilg_next = NULL;
17911042SErik.Nordmark@Sun.COM 	ilg->ilg_ptpn = NULL;
18011042SErik.Nordmark@Sun.COM 
18111042SErik.Nordmark@Sun.COM 	ilg->ilg_connp = NULL;
18211042SErik.Nordmark@Sun.COM 	kmem_free(ilg, sizeof (*ilg));
18311042SErik.Nordmark@Sun.COM }
1840Sstevel@tonic-gate 
1850Sstevel@tonic-gate /*
18611042SErik.Nordmark@Sun.COM  * The caller must hold conn_ilg_lock as writer.
18711042SErik.Nordmark@Sun.COM  */
18811042SErik.Nordmark@Sun.COM static void
ilg_refrele(ilg_t * ilg)18911042SErik.Nordmark@Sun.COM ilg_refrele(ilg_t *ilg)
19011042SErik.Nordmark@Sun.COM {
19111042SErik.Nordmark@Sun.COM 	ASSERT(RW_WRITE_HELD(&ilg->ilg_connp->conn_ilg_lock));
19211042SErik.Nordmark@Sun.COM 	ASSERT(ilg->ilg_refcnt != 0);
19311042SErik.Nordmark@Sun.COM 	if (--ilg->ilg_refcnt == 0)
19411042SErik.Nordmark@Sun.COM 		ilg_inactive(ilg);
19511042SErik.Nordmark@Sun.COM }
19611042SErik.Nordmark@Sun.COM 
19711042SErik.Nordmark@Sun.COM /*
19811042SErik.Nordmark@Sun.COM  * Acquire reference on ilg and drop reference on held_ilg.
19911042SErik.Nordmark@Sun.COM  * In the case when held_ilg is the same as ilg we already have
20011042SErik.Nordmark@Sun.COM  * a reference, but the held_ilg might be condemned. In that case
20111042SErik.Nordmark@Sun.COM  * we avoid the ilg_refhold/rele so that we can assert in ire_refhold
20211042SErik.Nordmark@Sun.COM  * that the ilg isn't condemned.
20311042SErik.Nordmark@Sun.COM  */
20411042SErik.Nordmark@Sun.COM static void
ilg_transfer_hold(ilg_t * held_ilg,ilg_t * ilg)20511042SErik.Nordmark@Sun.COM ilg_transfer_hold(ilg_t *held_ilg, ilg_t *ilg)
20611042SErik.Nordmark@Sun.COM {
20711042SErik.Nordmark@Sun.COM 	if (held_ilg == ilg)
20811042SErik.Nordmark@Sun.COM 		return;
20911042SErik.Nordmark@Sun.COM 
21011042SErik.Nordmark@Sun.COM 	ilg_refhold(ilg);
21111042SErik.Nordmark@Sun.COM 	if (held_ilg != NULL)
21211042SErik.Nordmark@Sun.COM 		ilg_refrele(held_ilg);
21311042SErik.Nordmark@Sun.COM }
21411042SErik.Nordmark@Sun.COM 
21511042SErik.Nordmark@Sun.COM /*
21611042SErik.Nordmark@Sun.COM  * Allocate a new ilg_t and links it into conn_ilg.
21711042SErik.Nordmark@Sun.COM  * Returns NULL on failure, in which case `*errp' will be
2188485SPeter.Memishian@Sun.COM  * filled in with the reason.
2190Sstevel@tonic-gate  *
22011042SErik.Nordmark@Sun.COM  * Assumes connp->conn_ilg_lock is held.
2210Sstevel@tonic-gate  */
2220Sstevel@tonic-gate static ilg_t *
conn_ilg_alloc(conn_t * connp,int * errp)2238485SPeter.Memishian@Sun.COM conn_ilg_alloc(conn_t *connp, int *errp)
2240Sstevel@tonic-gate {
22511042SErik.Nordmark@Sun.COM 	ilg_t *ilg;
22611042SErik.Nordmark@Sun.COM 
22711042SErik.Nordmark@Sun.COM 	ASSERT(RW_WRITE_HELD(&connp->conn_ilg_lock));
2280Sstevel@tonic-gate 
2298485SPeter.Memishian@Sun.COM 	/*
2308485SPeter.Memishian@Sun.COM 	 * If CONN_CLOSING is set, conn_ilg cleanup has begun and we must not
2318485SPeter.Memishian@Sun.COM 	 * create any ilgs.
2328485SPeter.Memishian@Sun.COM 	 */
2338485SPeter.Memishian@Sun.COM 	if (connp->conn_state_flags & CONN_CLOSING) {
2348485SPeter.Memishian@Sun.COM 		*errp = EINVAL;
2358485SPeter.Memishian@Sun.COM 		return (NULL);
2368485SPeter.Memishian@Sun.COM 	}
2378485SPeter.Memishian@Sun.COM 
23811042SErik.Nordmark@Sun.COM 	ilg = kmem_zalloc(sizeof (ilg_t), KM_NOSLEEP);
23911042SErik.Nordmark@Sun.COM 	if (ilg == NULL) {
24011042SErik.Nordmark@Sun.COM 		*errp = ENOMEM;
24111042SErik.Nordmark@Sun.COM 		return (NULL);
2420Sstevel@tonic-gate 	}
24311042SErik.Nordmark@Sun.COM 
24411042SErik.Nordmark@Sun.COM 	ilg->ilg_refcnt = 1;
24511042SErik.Nordmark@Sun.COM 
24611042SErik.Nordmark@Sun.COM 	/* Insert at head */
24711042SErik.Nordmark@Sun.COM 	if (connp->conn_ilg != NULL)
24811042SErik.Nordmark@Sun.COM 		connp->conn_ilg->ilg_ptpn = &ilg->ilg_next;
24911042SErik.Nordmark@Sun.COM 	ilg->ilg_next = connp->conn_ilg;
25011042SErik.Nordmark@Sun.COM 	ilg->ilg_ptpn = &connp->conn_ilg;
25111042SErik.Nordmark@Sun.COM 	connp->conn_ilg = ilg;
25211042SErik.Nordmark@Sun.COM 
25311042SErik.Nordmark@Sun.COM 	ilg->ilg_connp = connp;
25411042SErik.Nordmark@Sun.COM 	return (ilg);
2550Sstevel@tonic-gate }
2560Sstevel@tonic-gate 
2570Sstevel@tonic-gate typedef struct ilm_fbld_s {
2580Sstevel@tonic-gate 	ilm_t		*fbld_ilm;
2590Sstevel@tonic-gate 	int		fbld_in_cnt;
2600Sstevel@tonic-gate 	int		fbld_ex_cnt;
2610Sstevel@tonic-gate 	slist_t		fbld_in;
2620Sstevel@tonic-gate 	slist_t		fbld_ex;
2630Sstevel@tonic-gate 	boolean_t	fbld_in_overflow;
2640Sstevel@tonic-gate } ilm_fbld_t;
2650Sstevel@tonic-gate 
26611042SErik.Nordmark@Sun.COM /*
26711042SErik.Nordmark@Sun.COM  * Caller must hold ill_mcast_lock
26811042SErik.Nordmark@Sun.COM  */
2690Sstevel@tonic-gate static void
ilm_bld_flists(conn_t * connp,void * arg)27011042SErik.Nordmark@Sun.COM ilm_bld_flists(conn_t *connp, void *arg)
2710Sstevel@tonic-gate {
27211042SErik.Nordmark@Sun.COM 	ilg_t *ilg;
2730Sstevel@tonic-gate 	ilm_fbld_t *fbld = (ilm_fbld_t *)(arg);
2740Sstevel@tonic-gate 	ilm_t *ilm = fbld->fbld_ilm;
2750Sstevel@tonic-gate 	in6_addr_t *v6group = &ilm->ilm_v6addr;
2760Sstevel@tonic-gate 
27711042SErik.Nordmark@Sun.COM 	if (connp->conn_ilg == NULL)
2780Sstevel@tonic-gate 		return;
2790Sstevel@tonic-gate 
2800Sstevel@tonic-gate 	/*
2810Sstevel@tonic-gate 	 * Since we can't break out of the ipcl_walk once started, we still
2820Sstevel@tonic-gate 	 * have to look at every conn.  But if we've already found one
2830Sstevel@tonic-gate 	 * (EXCLUDE, NULL) list, there's no need to keep checking individual
2840Sstevel@tonic-gate 	 * ilgs--that will be our state.
2850Sstevel@tonic-gate 	 */
2860Sstevel@tonic-gate 	if (fbld->fbld_ex_cnt > 0 && fbld->fbld_ex.sl_numsrc == 0)
2870Sstevel@tonic-gate 		return;
2880Sstevel@tonic-gate 
2890Sstevel@tonic-gate 	/*
2900Sstevel@tonic-gate 	 * Check this conn's ilgs to see if any are interested in our
2910Sstevel@tonic-gate 	 * ilm (group, interface match).  If so, update the master
2920Sstevel@tonic-gate 	 * include and exclude lists we're building in the fbld struct
2930Sstevel@tonic-gate 	 * with this ilg's filter info.
29411042SErik.Nordmark@Sun.COM 	 *
29511042SErik.Nordmark@Sun.COM 	 * Note that the caller has already serialized on the ill we care
29611042SErik.Nordmark@Sun.COM 	 * about.
2970Sstevel@tonic-gate 	 */
29811042SErik.Nordmark@Sun.COM 	ASSERT(MUTEX_HELD(&ilm->ilm_ill->ill_mcast_serializer));
29911042SErik.Nordmark@Sun.COM 
30011042SErik.Nordmark@Sun.COM 	rw_enter(&connp->conn_ilg_lock, RW_READER);
30111042SErik.Nordmark@Sun.COM 	for (ilg = connp->conn_ilg; ilg != NULL; ilg = ilg->ilg_next) {
30211042SErik.Nordmark@Sun.COM 		if (ilg->ilg_condemned)
30311042SErik.Nordmark@Sun.COM 			continue;
30411042SErik.Nordmark@Sun.COM 
30511042SErik.Nordmark@Sun.COM 		/*
30611042SErik.Nordmark@Sun.COM 		 * Since we are under the ill_mcast_serializer we know
30711042SErik.Nordmark@Sun.COM 		 * that any ilg+ilm operations on this ilm have either
30811042SErik.Nordmark@Sun.COM 		 * not started or completed, except for the last ilg
30911042SErik.Nordmark@Sun.COM 		 * (the one that caused us to be called) which doesn't
31011042SErik.Nordmark@Sun.COM 		 * have ilg_ilm set yet. Hence we compare using ilg_ill
31111042SErik.Nordmark@Sun.COM 		 * and the address.
31211042SErik.Nordmark@Sun.COM 		 */
3130Sstevel@tonic-gate 		if ((ilg->ilg_ill == ilm->ilm_ill) &&
3140Sstevel@tonic-gate 		    IN6_ARE_ADDR_EQUAL(&ilg->ilg_v6group, v6group)) {
3150Sstevel@tonic-gate 			if (ilg->ilg_fmode == MODE_IS_INCLUDE) {
3160Sstevel@tonic-gate 				fbld->fbld_in_cnt++;
3170Sstevel@tonic-gate 				if (!fbld->fbld_in_overflow)
3180Sstevel@tonic-gate 					l_union_in_a(&fbld->fbld_in,
3190Sstevel@tonic-gate 					    ilg->ilg_filter,
3200Sstevel@tonic-gate 					    &fbld->fbld_in_overflow);
3210Sstevel@tonic-gate 			} else {
3220Sstevel@tonic-gate 				fbld->fbld_ex_cnt++;
3230Sstevel@tonic-gate 				/*
3240Sstevel@tonic-gate 				 * On the first exclude list, don't try to do
3250Sstevel@tonic-gate 				 * an intersection, as the master exclude list
3260Sstevel@tonic-gate 				 * is intentionally empty.  If the master list
3270Sstevel@tonic-gate 				 * is still empty on later iterations, that
3280Sstevel@tonic-gate 				 * means we have at least one ilg with an empty
3290Sstevel@tonic-gate 				 * exclude list, so that should be reflected
3300Sstevel@tonic-gate 				 * when we take the intersection.
3310Sstevel@tonic-gate 				 */
3320Sstevel@tonic-gate 				if (fbld->fbld_ex_cnt == 1) {
3330Sstevel@tonic-gate 					if (ilg->ilg_filter != NULL)
3340Sstevel@tonic-gate 						l_copy(ilg->ilg_filter,
3350Sstevel@tonic-gate 						    &fbld->fbld_ex);
3360Sstevel@tonic-gate 				} else {
3370Sstevel@tonic-gate 					l_intersection_in_a(&fbld->fbld_ex,
3380Sstevel@tonic-gate 					    ilg->ilg_filter);
3390Sstevel@tonic-gate 				}
3400Sstevel@tonic-gate 			}
3410Sstevel@tonic-gate 			/* there will only be one match, so break now. */
3420Sstevel@tonic-gate 			break;
3430Sstevel@tonic-gate 		}
3440Sstevel@tonic-gate 	}
34511042SErik.Nordmark@Sun.COM 	rw_exit(&connp->conn_ilg_lock);
3460Sstevel@tonic-gate }
3470Sstevel@tonic-gate 
34811042SErik.Nordmark@Sun.COM /*
34911042SErik.Nordmark@Sun.COM  * Caller must hold ill_mcast_lock
35011042SErik.Nordmark@Sun.COM  */
3510Sstevel@tonic-gate static void
ilm_gen_filter(ilm_t * ilm,mcast_record_t * fmode,slist_t * flist)3520Sstevel@tonic-gate ilm_gen_filter(ilm_t *ilm, mcast_record_t *fmode, slist_t *flist)
3530Sstevel@tonic-gate {
3540Sstevel@tonic-gate 	ilm_fbld_t fbld;
3553448Sdh155122 	ip_stack_t *ipst = ilm->ilm_ipst;
3560Sstevel@tonic-gate 
3570Sstevel@tonic-gate 	fbld.fbld_ilm = ilm;
3580Sstevel@tonic-gate 	fbld.fbld_in_cnt = fbld.fbld_ex_cnt = 0;
3590Sstevel@tonic-gate 	fbld.fbld_in.sl_numsrc = fbld.fbld_ex.sl_numsrc = 0;
3600Sstevel@tonic-gate 	fbld.fbld_in_overflow = B_FALSE;
3610Sstevel@tonic-gate 
3620Sstevel@tonic-gate 	/* first, construct our master include and exclude lists */
3633448Sdh155122 	ipcl_walk(ilm_bld_flists, (caddr_t)&fbld, ipst);
3640Sstevel@tonic-gate 
3650Sstevel@tonic-gate 	/* now use those master lists to generate the interface filter */
3660Sstevel@tonic-gate 
3670Sstevel@tonic-gate 	/* if include list overflowed, filter is (EXCLUDE, NULL) */
3680Sstevel@tonic-gate 	if (fbld.fbld_in_overflow) {
3690Sstevel@tonic-gate 		*fmode = MODE_IS_EXCLUDE;
3700Sstevel@tonic-gate 		flist->sl_numsrc = 0;
3710Sstevel@tonic-gate 		return;
3720Sstevel@tonic-gate 	}
3730Sstevel@tonic-gate 
3740Sstevel@tonic-gate 	/* if nobody interested, interface filter is (INCLUDE, NULL) */
3750Sstevel@tonic-gate 	if (fbld.fbld_in_cnt == 0 && fbld.fbld_ex_cnt == 0) {
3760Sstevel@tonic-gate 		*fmode = MODE_IS_INCLUDE;
3770Sstevel@tonic-gate 		flist->sl_numsrc = 0;
3780Sstevel@tonic-gate 		return;
3790Sstevel@tonic-gate 	}
3800Sstevel@tonic-gate 
3810Sstevel@tonic-gate 	/*
3820Sstevel@tonic-gate 	 * If there are no exclude lists, then the interface filter
3830Sstevel@tonic-gate 	 * is INCLUDE, with its filter list equal to fbld_in.  A single
3840Sstevel@tonic-gate 	 * exclude list makes the interface filter EXCLUDE, with its
3850Sstevel@tonic-gate 	 * filter list equal to (fbld_ex - fbld_in).
3860Sstevel@tonic-gate 	 */
3870Sstevel@tonic-gate 	if (fbld.fbld_ex_cnt == 0) {
3880Sstevel@tonic-gate 		*fmode = MODE_IS_INCLUDE;
3890Sstevel@tonic-gate 		l_copy(&fbld.fbld_in, flist);
3900Sstevel@tonic-gate 	} else {
3910Sstevel@tonic-gate 		*fmode = MODE_IS_EXCLUDE;
3920Sstevel@tonic-gate 		l_difference(&fbld.fbld_ex, &fbld.fbld_in, flist);
3930Sstevel@tonic-gate 	}
3940Sstevel@tonic-gate }
3950Sstevel@tonic-gate 
39611042SErik.Nordmark@Sun.COM /*
39711042SErik.Nordmark@Sun.COM  * Caller must hold ill_mcast_lock
39811042SErik.Nordmark@Sun.COM  */
3990Sstevel@tonic-gate static int
ilm_update_add(ilm_t * ilm,ilg_stat_t ilgstat,slist_t * ilg_flist)40011042SErik.Nordmark@Sun.COM ilm_update_add(ilm_t *ilm, ilg_stat_t ilgstat, slist_t *ilg_flist)
4010Sstevel@tonic-gate {
4020Sstevel@tonic-gate 	mcast_record_t fmode;
4030Sstevel@tonic-gate 	slist_t *flist;
4040Sstevel@tonic-gate 	boolean_t fdefault;
4050Sstevel@tonic-gate 	char buf[INET6_ADDRSTRLEN];
40611042SErik.Nordmark@Sun.COM 	ill_t *ill = ilm->ilm_ill;
4070Sstevel@tonic-gate 
4080Sstevel@tonic-gate 	/*
4090Sstevel@tonic-gate 	 * There are several cases where the ilm's filter state
4100Sstevel@tonic-gate 	 * defaults to (EXCLUDE, NULL):
4110Sstevel@tonic-gate 	 *	- we've had previous joins without associated ilgs
4120Sstevel@tonic-gate 	 *	- this join has no associated ilg
4130Sstevel@tonic-gate 	 *	- the ilg's filter state is (EXCLUDE, NULL)
4140Sstevel@tonic-gate 	 */
4150Sstevel@tonic-gate 	fdefault = (ilm->ilm_no_ilg_cnt > 0) ||
4160Sstevel@tonic-gate 	    (ilgstat == ILGSTAT_NONE) || SLIST_IS_EMPTY(ilg_flist);
4170Sstevel@tonic-gate 
4180Sstevel@tonic-gate 	/* attempt mallocs (if needed) before doing anything else */
4190Sstevel@tonic-gate 	if ((flist = l_alloc()) == NULL)
4200Sstevel@tonic-gate 		return (ENOMEM);
4210Sstevel@tonic-gate 	if (!fdefault && ilm->ilm_filter == NULL) {
4220Sstevel@tonic-gate 		ilm->ilm_filter = l_alloc();
4230Sstevel@tonic-gate 		if (ilm->ilm_filter == NULL) {
4240Sstevel@tonic-gate 			l_free(flist);
4250Sstevel@tonic-gate 			return (ENOMEM);
4260Sstevel@tonic-gate 		}
4270Sstevel@tonic-gate 	}
4280Sstevel@tonic-gate 
4290Sstevel@tonic-gate 	if (ilgstat != ILGSTAT_CHANGE)
4300Sstevel@tonic-gate 		ilm->ilm_refcnt++;
4310Sstevel@tonic-gate 
4320Sstevel@tonic-gate 	if (ilgstat == ILGSTAT_NONE)
4330Sstevel@tonic-gate 		ilm->ilm_no_ilg_cnt++;
4340Sstevel@tonic-gate 
4350Sstevel@tonic-gate 	/*
4360Sstevel@tonic-gate 	 * Determine new filter state.  If it's not the default
4370Sstevel@tonic-gate 	 * (EXCLUDE, NULL), we must walk the conn list to find
4380Sstevel@tonic-gate 	 * any ilgs interested in this group, and re-build the
4390Sstevel@tonic-gate 	 * ilm filter.
4400Sstevel@tonic-gate 	 */
4410Sstevel@tonic-gate 	if (fdefault) {
4420Sstevel@tonic-gate 		fmode = MODE_IS_EXCLUDE;
4430Sstevel@tonic-gate 		flist->sl_numsrc = 0;
4440Sstevel@tonic-gate 	} else {
4450Sstevel@tonic-gate 		ilm_gen_filter(ilm, &fmode, flist);
4460Sstevel@tonic-gate 	}
4470Sstevel@tonic-gate 
4480Sstevel@tonic-gate 	/* make sure state actually changed; nothing to do if not. */
4490Sstevel@tonic-gate 	if ((ilm->ilm_fmode == fmode) &&
4500Sstevel@tonic-gate 	    !lists_are_different(ilm->ilm_filter, flist)) {
4510Sstevel@tonic-gate 		l_free(flist);
4520Sstevel@tonic-gate 		return (0);
4530Sstevel@tonic-gate 	}
4540Sstevel@tonic-gate 
4550Sstevel@tonic-gate 	/* send the state change report */
4564459Skcpoon 	if (!IS_LOOPBACK(ill)) {
45711042SErik.Nordmark@Sun.COM 		if (ill->ill_isv6)
4580Sstevel@tonic-gate 			mld_statechange(ilm, fmode, flist);
4590Sstevel@tonic-gate 		else
4600Sstevel@tonic-gate 			igmp_statechange(ilm, fmode, flist);
4610Sstevel@tonic-gate 	}
4620Sstevel@tonic-gate 
4630Sstevel@tonic-gate 	/* update the ilm state */
4640Sstevel@tonic-gate 	ilm->ilm_fmode = fmode;
4650Sstevel@tonic-gate 	if (flist->sl_numsrc > 0)
4660Sstevel@tonic-gate 		l_copy(flist, ilm->ilm_filter);
4670Sstevel@tonic-gate 	else
4680Sstevel@tonic-gate 		CLEAR_SLIST(ilm->ilm_filter);
4690Sstevel@tonic-gate 
4700Sstevel@tonic-gate 	ip1dbg(("ilm_update: new if filter mode %d, group %s\n", ilm->ilm_fmode,
4710Sstevel@tonic-gate 	    inet_ntop(AF_INET6, &ilm->ilm_v6addr, buf, sizeof (buf))));
4720Sstevel@tonic-gate 
4730Sstevel@tonic-gate 	l_free(flist);
4740Sstevel@tonic-gate 	return (0);
4750Sstevel@tonic-gate }
4760Sstevel@tonic-gate 
47711042SErik.Nordmark@Sun.COM /*
47811042SErik.Nordmark@Sun.COM  * Caller must hold ill_mcast_lock
47911042SErik.Nordmark@Sun.COM  */
4800Sstevel@tonic-gate static int
ilm_update_del(ilm_t * ilm)48111042SErik.Nordmark@Sun.COM ilm_update_del(ilm_t *ilm)
4820Sstevel@tonic-gate {
4830Sstevel@tonic-gate 	mcast_record_t fmode;
4840Sstevel@tonic-gate 	slist_t *flist;
48511042SErik.Nordmark@Sun.COM 	ill_t *ill = ilm->ilm_ill;
4860Sstevel@tonic-gate 
4870Sstevel@tonic-gate 	ip1dbg(("ilm_update_del: still %d left; updating state\n",
4880Sstevel@tonic-gate 	    ilm->ilm_refcnt));
4890Sstevel@tonic-gate 
4900Sstevel@tonic-gate 	if ((flist = l_alloc()) == NULL)
4910Sstevel@tonic-gate 		return (ENOMEM);
4920Sstevel@tonic-gate 
4930Sstevel@tonic-gate 	/*
4940Sstevel@tonic-gate 	 * If present, the ilg in question has already either been
4950Sstevel@tonic-gate 	 * updated or removed from our list; so all we need to do
4960Sstevel@tonic-gate 	 * now is walk the list to update the ilm filter state.
4970Sstevel@tonic-gate 	 *
4980Sstevel@tonic-gate 	 * Skip the list walk if we have any no-ilg joins, which
4990Sstevel@tonic-gate 	 * cause the filter state to revert to (EXCLUDE, NULL).
5000Sstevel@tonic-gate 	 */
5010Sstevel@tonic-gate 	if (ilm->ilm_no_ilg_cnt != 0) {
5020Sstevel@tonic-gate 		fmode = MODE_IS_EXCLUDE;
5030Sstevel@tonic-gate 		flist->sl_numsrc = 0;
5040Sstevel@tonic-gate 	} else {
5050Sstevel@tonic-gate 		ilm_gen_filter(ilm, &fmode, flist);
5060Sstevel@tonic-gate 	}
5070Sstevel@tonic-gate 
5080Sstevel@tonic-gate 	/* check to see if state needs to be updated */
5090Sstevel@tonic-gate 	if ((ilm->ilm_fmode == fmode) &&
5100Sstevel@tonic-gate 	    (!lists_are_different(ilm->ilm_filter, flist))) {
5110Sstevel@tonic-gate 		l_free(flist);
5120Sstevel@tonic-gate 		return (0);
5130Sstevel@tonic-gate 	}
5140Sstevel@tonic-gate 
5154459Skcpoon 	if (!IS_LOOPBACK(ill)) {
51611042SErik.Nordmark@Sun.COM 		if (ill->ill_isv6)
5170Sstevel@tonic-gate 			mld_statechange(ilm, fmode, flist);
5180Sstevel@tonic-gate 		else
5190Sstevel@tonic-gate 			igmp_statechange(ilm, fmode, flist);
5200Sstevel@tonic-gate 	}
5210Sstevel@tonic-gate 
5220Sstevel@tonic-gate 	ilm->ilm_fmode = fmode;
5230Sstevel@tonic-gate 	if (flist->sl_numsrc > 0) {
5240Sstevel@tonic-gate 		if (ilm->ilm_filter == NULL) {
5250Sstevel@tonic-gate 			ilm->ilm_filter = l_alloc();
5260Sstevel@tonic-gate 			if (ilm->ilm_filter == NULL) {
5270Sstevel@tonic-gate 				char buf[INET6_ADDRSTRLEN];
5280Sstevel@tonic-gate 				ip1dbg(("ilm_update_del: failed to alloc ilm "
5290Sstevel@tonic-gate 				    "filter; no source filtering for %s on %s",
5300Sstevel@tonic-gate 				    inet_ntop(AF_INET6, &ilm->ilm_v6addr,
5310Sstevel@tonic-gate 				    buf, sizeof (buf)), ill->ill_name));
5320Sstevel@tonic-gate 				ilm->ilm_fmode = MODE_IS_EXCLUDE;
5330Sstevel@tonic-gate 				l_free(flist);
5340Sstevel@tonic-gate 				return (0);
5350Sstevel@tonic-gate 			}
5360Sstevel@tonic-gate 		}
5370Sstevel@tonic-gate 		l_copy(flist, ilm->ilm_filter);
5380Sstevel@tonic-gate 	} else {
5390Sstevel@tonic-gate 		CLEAR_SLIST(ilm->ilm_filter);
5400Sstevel@tonic-gate 	}
5410Sstevel@tonic-gate 
5420Sstevel@tonic-gate 	l_free(flist);
5430Sstevel@tonic-gate 	return (0);
5440Sstevel@tonic-gate }
5450Sstevel@tonic-gate 
5460Sstevel@tonic-gate /*
54711042SErik.Nordmark@Sun.COM  * Create/update the ilm for the group/ill. Used by other parts of IP to
54811042SErik.Nordmark@Sun.COM  * do the ILGSTAT_NONE (no ilg), MODE_IS_EXCLUDE, with no slist join.
54911042SErik.Nordmark@Sun.COM  * Returns with a refhold on the ilm.
55011042SErik.Nordmark@Sun.COM  *
55111042SErik.Nordmark@Sun.COM  * The unspecified address means all multicast addresses for in both the
55211042SErik.Nordmark@Sun.COM  * case of IPv4 and IPv6.
55311042SErik.Nordmark@Sun.COM  *
55411042SErik.Nordmark@Sun.COM  * The caller should have already mapped an IPMP under ill to the upper.
5550Sstevel@tonic-gate  */
55611042SErik.Nordmark@Sun.COM ilm_t *
ip_addmulti(const in6_addr_t * v6group,ill_t * ill,zoneid_t zoneid,int * errorp)55711042SErik.Nordmark@Sun.COM ip_addmulti(const in6_addr_t *v6group, ill_t *ill, zoneid_t zoneid,
55811042SErik.Nordmark@Sun.COM     int *errorp)
5590Sstevel@tonic-gate {
56011042SErik.Nordmark@Sun.COM 	ilm_t *ilm;
56111042SErik.Nordmark@Sun.COM 
56211042SErik.Nordmark@Sun.COM 	/* Acquire serializer to keep assert in ilm_bld_flists happy */
56311042SErik.Nordmark@Sun.COM 	mutex_enter(&ill->ill_mcast_serializer);
56411042SErik.Nordmark@Sun.COM 	ilm = ip_addmulti_serial(v6group, ill, zoneid, ILGSTAT_NONE,
56511042SErik.Nordmark@Sun.COM 	    MODE_IS_EXCLUDE, NULL, errorp);
56611042SErik.Nordmark@Sun.COM 	mutex_exit(&ill->ill_mcast_serializer);
56711463SSowmini.Varadhan@Sun.COM 	/*
56811463SSowmini.Varadhan@Sun.COM 	 * Now that all locks have been dropped, we can send any
56911463SSowmini.Varadhan@Sun.COM 	 * deferred/queued DLPI or IP packets
57011463SSowmini.Varadhan@Sun.COM 	 */
57111463SSowmini.Varadhan@Sun.COM 	ill_mcast_send_queued(ill);
57211463SSowmini.Varadhan@Sun.COM 	ill_dlpi_send_queued(ill);
57311042SErik.Nordmark@Sun.COM 	return (ilm);
5740Sstevel@tonic-gate }
5750Sstevel@tonic-gate 
5760Sstevel@tonic-gate /*
57711042SErik.Nordmark@Sun.COM  * Create/update the ilm for the group/ill. If ILGSTAT_CHANGE is not set
57811042SErik.Nordmark@Sun.COM  * then this returns with a refhold on the ilm.
5790Sstevel@tonic-gate  *
58011042SErik.Nordmark@Sun.COM  * Internal routine which assumes the caller has already acquired
58111463SSowmini.Varadhan@Sun.COM  * ill_mcast_serializer. It is the caller's responsibility to send out
58211463SSowmini.Varadhan@Sun.COM  * queued DLPI/multicast packets after all locks are dropped.
58311042SErik.Nordmark@Sun.COM  *
58411042SErik.Nordmark@Sun.COM  * The unspecified address means all multicast addresses for in both the
58511042SErik.Nordmark@Sun.COM  * case of IPv4 and IPv6.
5860Sstevel@tonic-gate  *
5870Sstevel@tonic-gate  * ilgstat tells us if there's an ilg associated with this join,
5880Sstevel@tonic-gate  * and if so, if it's a new ilg or a change to an existing one.
5890Sstevel@tonic-gate  * ilg_fmode and ilg_flist give us the current filter state of
5900Sstevel@tonic-gate  * the ilg (and will be EXCLUDE {NULL} in the case of no ilg).
59111042SErik.Nordmark@Sun.COM  *
59211042SErik.Nordmark@Sun.COM  * The caller should have already mapped an IPMP under ill to the upper.
5930Sstevel@tonic-gate  */
59411042SErik.Nordmark@Sun.COM static ilm_t *
ip_addmulti_serial(const in6_addr_t * v6group,ill_t * ill,zoneid_t zoneid,ilg_stat_t ilgstat,mcast_record_t ilg_fmode,slist_t * ilg_flist,int * errorp)59511042SErik.Nordmark@Sun.COM ip_addmulti_serial(const in6_addr_t *v6group, ill_t *ill, zoneid_t zoneid,
59611042SErik.Nordmark@Sun.COM     ilg_stat_t ilgstat, mcast_record_t ilg_fmode, slist_t *ilg_flist,
59711042SErik.Nordmark@Sun.COM     int *errorp)
59811042SErik.Nordmark@Sun.COM {
59911042SErik.Nordmark@Sun.COM 	ilm_t *ilm;
60011042SErik.Nordmark@Sun.COM 
60111042SErik.Nordmark@Sun.COM 	ASSERT(MUTEX_HELD(&ill->ill_mcast_serializer));
60211042SErik.Nordmark@Sun.COM 
60311042SErik.Nordmark@Sun.COM 	if (ill->ill_isv6) {
60411042SErik.Nordmark@Sun.COM 		if (!IN6_IS_ADDR_MULTICAST(v6group) &&
60511042SErik.Nordmark@Sun.COM 		    !IN6_IS_ADDR_UNSPECIFIED(v6group)) {
60611042SErik.Nordmark@Sun.COM 			*errorp = EINVAL;
60711042SErik.Nordmark@Sun.COM 			return (NULL);
60811042SErik.Nordmark@Sun.COM 		}
60911042SErik.Nordmark@Sun.COM 	} else {
61011042SErik.Nordmark@Sun.COM 		if (IN6_IS_ADDR_V4MAPPED(v6group)) {
61111042SErik.Nordmark@Sun.COM 			ipaddr_t v4group;
61211042SErik.Nordmark@Sun.COM 
61311042SErik.Nordmark@Sun.COM 			IN6_V4MAPPED_TO_IPADDR(v6group, v4group);
61411463SSowmini.Varadhan@Sun.COM 			ASSERT(!IS_UNDER_IPMP(ill));
61511042SErik.Nordmark@Sun.COM 			if (!CLASSD(v4group)) {
61611042SErik.Nordmark@Sun.COM 				*errorp = EINVAL;
61711042SErik.Nordmark@Sun.COM 				return (NULL);
61811042SErik.Nordmark@Sun.COM 			}
61911042SErik.Nordmark@Sun.COM 		} else if (!IN6_IS_ADDR_UNSPECIFIED(v6group)) {
62011042SErik.Nordmark@Sun.COM 			*errorp = EINVAL;
62111042SErik.Nordmark@Sun.COM 			return (NULL);
62211042SErik.Nordmark@Sun.COM 		}
62311042SErik.Nordmark@Sun.COM 	}
62411042SErik.Nordmark@Sun.COM 
62511042SErik.Nordmark@Sun.COM 	if (IS_UNDER_IPMP(ill)) {
62611042SErik.Nordmark@Sun.COM 		*errorp = EINVAL;
62711042SErik.Nordmark@Sun.COM 		return (NULL);
62811042SErik.Nordmark@Sun.COM 	}
62911042SErik.Nordmark@Sun.COM 
63011042SErik.Nordmark@Sun.COM 	rw_enter(&ill->ill_mcast_lock, RW_WRITER);
63111042SErik.Nordmark@Sun.COM 	/*
63211042SErik.Nordmark@Sun.COM 	 * We do the equivalent of a lookup by checking after we get the lock
63311042SErik.Nordmark@Sun.COM 	 * This is needed since the ill could have been condemned after
63411042SErik.Nordmark@Sun.COM 	 * we looked it up, and we need to check condemned after we hold
63511042SErik.Nordmark@Sun.COM 	 * ill_mcast_lock to synchronize with the unplumb code.
63611042SErik.Nordmark@Sun.COM 	 */
63711042SErik.Nordmark@Sun.COM 	if (ill->ill_state_flags & ILL_CONDEMNED) {
63811042SErik.Nordmark@Sun.COM 		rw_exit(&ill->ill_mcast_lock);
63911042SErik.Nordmark@Sun.COM 		*errorp = ENXIO;
64011042SErik.Nordmark@Sun.COM 		return (NULL);
64111042SErik.Nordmark@Sun.COM 	}
64211042SErik.Nordmark@Sun.COM 	ilm = ip_addmulti_impl(v6group, ill, zoneid, ilgstat, ilg_fmode,
64311042SErik.Nordmark@Sun.COM 	    ilg_flist, errorp);
64411042SErik.Nordmark@Sun.COM 	rw_exit(&ill->ill_mcast_lock);
64511042SErik.Nordmark@Sun.COM 
64611042SErik.Nordmark@Sun.COM 	ill_mcast_timer_start(ill->ill_ipst);
64711042SErik.Nordmark@Sun.COM 	return (ilm);
64811042SErik.Nordmark@Sun.COM }
64911042SErik.Nordmark@Sun.COM 
65011042SErik.Nordmark@Sun.COM static ilm_t *
ip_addmulti_impl(const in6_addr_t * v6group,ill_t * ill,zoneid_t zoneid,ilg_stat_t ilgstat,mcast_record_t ilg_fmode,slist_t * ilg_flist,int * errorp)65111042SErik.Nordmark@Sun.COM ip_addmulti_impl(const in6_addr_t *v6group, ill_t *ill, zoneid_t zoneid,
65211042SErik.Nordmark@Sun.COM     ilg_stat_t ilgstat, mcast_record_t ilg_fmode, slist_t *ilg_flist,
65311042SErik.Nordmark@Sun.COM     int *errorp)
6540Sstevel@tonic-gate {
6550Sstevel@tonic-gate 	ilm_t	*ilm;
65611042SErik.Nordmark@Sun.COM 	int	ret = 0;
65711042SErik.Nordmark@Sun.COM 
65811042SErik.Nordmark@Sun.COM 	ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock));
65911042SErik.Nordmark@Sun.COM 	*errorp = 0;
6608485SPeter.Memishian@Sun.COM 
6610Sstevel@tonic-gate 	/*
6628485SPeter.Memishian@Sun.COM 	 * An ilm is uniquely identified by the tuple of (group, ill) where
6638485SPeter.Memishian@Sun.COM 	 * `group' is the multicast group address, and `ill' is the interface
6648485SPeter.Memishian@Sun.COM 	 * on which it is currently joined.
6650Sstevel@tonic-gate 	 */
66611042SErik.Nordmark@Sun.COM 
66711042SErik.Nordmark@Sun.COM 	ilm = ilm_lookup(ill, v6group, zoneid);
66811042SErik.Nordmark@Sun.COM 	if (ilm != NULL) {
66911042SErik.Nordmark@Sun.COM 		/* ilm_update_add bumps ilm_refcnt unless ILGSTAT_CHANGE */
67011042SErik.Nordmark@Sun.COM 		ret = ilm_update_add(ilm, ilgstat, ilg_flist);
67111042SErik.Nordmark@Sun.COM 		if (ret == 0)
67211042SErik.Nordmark@Sun.COM 			return (ilm);
67311042SErik.Nordmark@Sun.COM 
67411042SErik.Nordmark@Sun.COM 		*errorp = ret;
67511042SErik.Nordmark@Sun.COM 		return (NULL);
67611042SErik.Nordmark@Sun.COM 	}
67711042SErik.Nordmark@Sun.COM 
67811042SErik.Nordmark@Sun.COM 	/*
67911042SErik.Nordmark@Sun.COM 	 * The callers checks on the ilg and the ilg+ilm consistency under
68011042SErik.Nordmark@Sun.COM 	 * ill_mcast_serializer ensures that we can not have ILGSTAT_CHANGE
68111042SErik.Nordmark@Sun.COM 	 * and no ilm.
68211042SErik.Nordmark@Sun.COM 	 */
68311042SErik.Nordmark@Sun.COM 	ASSERT(ilgstat != ILGSTAT_CHANGE);
68411042SErik.Nordmark@Sun.COM 	ilm = ilm_add(ill, v6group, ilgstat, ilg_fmode, ilg_flist, zoneid);
68511042SErik.Nordmark@Sun.COM 	if (ilm == NULL) {
68611042SErik.Nordmark@Sun.COM 		*errorp = ENOMEM;
68711042SErik.Nordmark@Sun.COM 		return (NULL);
68811042SErik.Nordmark@Sun.COM 	}
6890Sstevel@tonic-gate 
6900Sstevel@tonic-gate 	if (IN6_IS_ADDR_UNSPECIFIED(v6group)) {
6910Sstevel@tonic-gate 		/*
69211042SErik.Nordmark@Sun.COM 		 * If we have more then one we should not tell the driver
69311042SErik.Nordmark@Sun.COM 		 * to join this time.
6940Sstevel@tonic-gate 		 */
69511042SErik.Nordmark@Sun.COM 		if (ilm_numentries(ill, v6group) == 1) {
69611042SErik.Nordmark@Sun.COM 			ret = ill_join_allmulti(ill);
69711042SErik.Nordmark@Sun.COM 		}
69811042SErik.Nordmark@Sun.COM 	} else {
69911042SErik.Nordmark@Sun.COM 		if (!IS_LOOPBACK(ill)) {
70011042SErik.Nordmark@Sun.COM 			if (ill->ill_isv6)
70111042SErik.Nordmark@Sun.COM 				mld_joingroup(ilm);
70211042SErik.Nordmark@Sun.COM 			else
70311042SErik.Nordmark@Sun.COM 				igmp_joingroup(ilm);
70411042SErik.Nordmark@Sun.COM 		}
70511042SErik.Nordmark@Sun.COM 
70611042SErik.Nordmark@Sun.COM 		/*
70711042SErik.Nordmark@Sun.COM 		 * If we have more then one we should not tell the driver
70811042SErik.Nordmark@Sun.COM 		 * to join this time.
70911042SErik.Nordmark@Sun.COM 		 */
71011042SErik.Nordmark@Sun.COM 		if (ilm_numentries(ill, v6group) == 1) {
71111042SErik.Nordmark@Sun.COM 			ret = ip_ll_multireq(ill, v6group, DL_ENABMULTI_REQ);
71211042SErik.Nordmark@Sun.COM 		}
7130Sstevel@tonic-gate 	}
71411042SErik.Nordmark@Sun.COM 	if (ret != 0) {
71511042SErik.Nordmark@Sun.COM 		if (ret == ENETDOWN) {
71611042SErik.Nordmark@Sun.COM 			char buf[INET6_ADDRSTRLEN];
71711042SErik.Nordmark@Sun.COM 
71811042SErik.Nordmark@Sun.COM 			ip0dbg(("ip_addmulti: ENETDOWN for %s on %s",
71911042SErik.Nordmark@Sun.COM 			    inet_ntop(AF_INET6, &ilm->ilm_v6addr,
72011042SErik.Nordmark@Sun.COM 			    buf, sizeof (buf)), ill->ill_name));
72111042SErik.Nordmark@Sun.COM 		}
7220Sstevel@tonic-gate 		ilm_delete(ilm);
72311042SErik.Nordmark@Sun.COM 		*errorp = ret;
72411042SErik.Nordmark@Sun.COM 		return (NULL);
72511042SErik.Nordmark@Sun.COM 	} else {
72611042SErik.Nordmark@Sun.COM 		return (ilm);
72711042SErik.Nordmark@Sun.COM 	}
7280Sstevel@tonic-gate }
7290Sstevel@tonic-gate 
7300Sstevel@tonic-gate /*
731*12811SRavichandra.Nallan@Sun.COM  * Looks up the list of multicast physical addresses this interface
732*12811SRavichandra.Nallan@Sun.COM  * listens to. Add to the list if not present already.
733*12811SRavichandra.Nallan@Sun.COM  */
734*12811SRavichandra.Nallan@Sun.COM boolean_t
ip_mphysaddr_add(ill_t * ill,uchar_t * hw_addr)735*12811SRavichandra.Nallan@Sun.COM ip_mphysaddr_add(ill_t *ill, uchar_t *hw_addr)
736*12811SRavichandra.Nallan@Sun.COM {
737*12811SRavichandra.Nallan@Sun.COM 	multiphysaddr_t *mpa = NULL;
738*12811SRavichandra.Nallan@Sun.COM 	int	hw_addr_length = ill->ill_phys_addr_length;
739*12811SRavichandra.Nallan@Sun.COM 
740*12811SRavichandra.Nallan@Sun.COM 	mutex_enter(&ill->ill_lock);
741*12811SRavichandra.Nallan@Sun.COM 	for (mpa = ill->ill_mphysaddr_list; mpa != NULL; mpa = mpa->mpa_next) {
742*12811SRavichandra.Nallan@Sun.COM 		if (bcmp(hw_addr, &(mpa->mpa_addr[0]), hw_addr_length) == 0) {
743*12811SRavichandra.Nallan@Sun.COM 			mpa->mpa_refcnt++;
744*12811SRavichandra.Nallan@Sun.COM 			mutex_exit(&ill->ill_lock);
745*12811SRavichandra.Nallan@Sun.COM 			return (B_FALSE);
746*12811SRavichandra.Nallan@Sun.COM 		}
747*12811SRavichandra.Nallan@Sun.COM 	}
748*12811SRavichandra.Nallan@Sun.COM 
749*12811SRavichandra.Nallan@Sun.COM 	mpa = kmem_zalloc(sizeof (multiphysaddr_t), KM_NOSLEEP);
750*12811SRavichandra.Nallan@Sun.COM 	if (mpa == NULL) {
751*12811SRavichandra.Nallan@Sun.COM 		/*
752*12811SRavichandra.Nallan@Sun.COM 		 * We risk not having the multiphysadd structure. At this
753*12811SRavichandra.Nallan@Sun.COM 		 * point we can't fail. We can't afford to not send a
754*12811SRavichandra.Nallan@Sun.COM 		 * DL_ENABMULTI_REQ also. It is better than pre-allocating
755*12811SRavichandra.Nallan@Sun.COM 		 * the structure and having the code to track it also.
756*12811SRavichandra.Nallan@Sun.COM 		 */
757*12811SRavichandra.Nallan@Sun.COM 		ip0dbg(("ip_mphysaddr_add: ENOMEM. Some multicast apps"
758*12811SRavichandra.Nallan@Sun.COM 		    " may have issues. hw_addr: %p ill_name: %s\n",
759*12811SRavichandra.Nallan@Sun.COM 		    (void *)hw_addr, ill->ill_name));
760*12811SRavichandra.Nallan@Sun.COM 		mutex_exit(&ill->ill_lock);
761*12811SRavichandra.Nallan@Sun.COM 		return (B_TRUE);
762*12811SRavichandra.Nallan@Sun.COM 	}
763*12811SRavichandra.Nallan@Sun.COM 	bcopy(hw_addr, &(mpa->mpa_addr[0]), hw_addr_length);
764*12811SRavichandra.Nallan@Sun.COM 	mpa->mpa_refcnt = 1;
765*12811SRavichandra.Nallan@Sun.COM 	mpa->mpa_next = ill->ill_mphysaddr_list;
766*12811SRavichandra.Nallan@Sun.COM 	ill->ill_mphysaddr_list = mpa;
767*12811SRavichandra.Nallan@Sun.COM 	mutex_exit(&ill->ill_lock);
768*12811SRavichandra.Nallan@Sun.COM 	return (B_TRUE);
769*12811SRavichandra.Nallan@Sun.COM }
770*12811SRavichandra.Nallan@Sun.COM 
771*12811SRavichandra.Nallan@Sun.COM /*
772*12811SRavichandra.Nallan@Sun.COM  * Look up hw_addr from the list of physical multicast addresses this interface
773*12811SRavichandra.Nallan@Sun.COM  * listens to.
774*12811SRavichandra.Nallan@Sun.COM  * Remove the entry if the refcnt is 0
775*12811SRavichandra.Nallan@Sun.COM  */
776*12811SRavichandra.Nallan@Sun.COM boolean_t
ip_mphysaddr_del(ill_t * ill,uchar_t * hw_addr)777*12811SRavichandra.Nallan@Sun.COM ip_mphysaddr_del(ill_t *ill, uchar_t *hw_addr)
778*12811SRavichandra.Nallan@Sun.COM {
779*12811SRavichandra.Nallan@Sun.COM 	multiphysaddr_t *mpap = NULL, **mpapp = NULL;
780*12811SRavichandra.Nallan@Sun.COM 	int hw_addr_length = ill->ill_phys_addr_length;
781*12811SRavichandra.Nallan@Sun.COM 	boolean_t ret = B_FALSE;
782*12811SRavichandra.Nallan@Sun.COM 
783*12811SRavichandra.Nallan@Sun.COM 	mutex_enter(&ill->ill_lock);
784*12811SRavichandra.Nallan@Sun.COM 	for (mpapp = &ill->ill_mphysaddr_list; (mpap = *mpapp) != NULL;
785*12811SRavichandra.Nallan@Sun.COM 	    mpapp = &(mpap->mpa_next)) {
786*12811SRavichandra.Nallan@Sun.COM 		if (bcmp(hw_addr, &(mpap->mpa_addr[0]), hw_addr_length) == 0)
787*12811SRavichandra.Nallan@Sun.COM 			break;
788*12811SRavichandra.Nallan@Sun.COM 	}
789*12811SRavichandra.Nallan@Sun.COM 	if (mpap == NULL) {
790*12811SRavichandra.Nallan@Sun.COM 		/*
791*12811SRavichandra.Nallan@Sun.COM 		 * Should be coming here only when there was a memory
792*12811SRavichandra.Nallan@Sun.COM 		 * exhaustion and we were not able to allocate
793*12811SRavichandra.Nallan@Sun.COM 		 * a multiphysaddr_t. We still send a DL_DISABMULTI_REQ down.
794*12811SRavichandra.Nallan@Sun.COM 		 */
795*12811SRavichandra.Nallan@Sun.COM 
796*12811SRavichandra.Nallan@Sun.COM 		ip0dbg(("ip_mphysaddr_del: No entry for this addr. Some "
797*12811SRavichandra.Nallan@Sun.COM 		    "multicast apps might have had issues. hw_addr: %p "
798*12811SRavichandra.Nallan@Sun.COM 		    " ill_name: %s\n", (void *)hw_addr, ill->ill_name));
799*12811SRavichandra.Nallan@Sun.COM 		ret = B_TRUE;
800*12811SRavichandra.Nallan@Sun.COM 	} else if (--mpap->mpa_refcnt == 0) {
801*12811SRavichandra.Nallan@Sun.COM 		*mpapp = mpap->mpa_next;
802*12811SRavichandra.Nallan@Sun.COM 		kmem_free(mpap, sizeof (multiphysaddr_t));
803*12811SRavichandra.Nallan@Sun.COM 		ret = B_TRUE;
804*12811SRavichandra.Nallan@Sun.COM 	}
805*12811SRavichandra.Nallan@Sun.COM 	mutex_exit(&ill->ill_lock);
806*12811SRavichandra.Nallan@Sun.COM 	return (ret);
807*12811SRavichandra.Nallan@Sun.COM }
808*12811SRavichandra.Nallan@Sun.COM 
809*12811SRavichandra.Nallan@Sun.COM /*
81011042SErik.Nordmark@Sun.COM  * Send a multicast request to the driver for enabling or disabling
81111042SErik.Nordmark@Sun.COM  * multicast reception for v6groupp address. The caller has already
81211042SErik.Nordmark@Sun.COM  * checked whether it is appropriate to send one or not.
81311042SErik.Nordmark@Sun.COM  *
81411042SErik.Nordmark@Sun.COM  * For IPMP we switch to the cast_ill since it has the right hardware
81511042SErik.Nordmark@Sun.COM  * information.
8169073SCathy.Zhou@Sun.COM  */
81711042SErik.Nordmark@Sun.COM static int
ip_ll_send_multireq(ill_t * ill,const in6_addr_t * v6groupp,t_uscalar_t prim)81811042SErik.Nordmark@Sun.COM ip_ll_send_multireq(ill_t *ill, const in6_addr_t *v6groupp, t_uscalar_t prim)
8190Sstevel@tonic-gate {
8200Sstevel@tonic-gate 	mblk_t	*mp;
8210Sstevel@tonic-gate 	uint32_t addrlen, addroff;
82211042SErik.Nordmark@Sun.COM 	ill_t *release_ill = NULL;
823*12811SRavichandra.Nallan@Sun.COM 	uchar_t *cp;
82411042SErik.Nordmark@Sun.COM 	int err = 0;
82511042SErik.Nordmark@Sun.COM 
82611042SErik.Nordmark@Sun.COM 	ASSERT(RW_LOCK_HELD(&ill->ill_mcast_lock));
82711042SErik.Nordmark@Sun.COM 
82811042SErik.Nordmark@Sun.COM 	if (IS_IPMP(ill)) {
82911042SErik.Nordmark@Sun.COM 		/* On the upper IPMP ill. */
83011042SErik.Nordmark@Sun.COM 		release_ill = ipmp_illgrp_hold_cast_ill(ill->ill_grp);
83111042SErik.Nordmark@Sun.COM 		if (release_ill == NULL) {
83211042SErik.Nordmark@Sun.COM 			/*
83311042SErik.Nordmark@Sun.COM 			 * Avoid sending it down to the ipmpstub.
83411042SErik.Nordmark@Sun.COM 			 * We will be called again once the members of the
83511042SErik.Nordmark@Sun.COM 			 * group are in place
83611042SErik.Nordmark@Sun.COM 			 */
83711042SErik.Nordmark@Sun.COM 			ip1dbg(("ip_ll_send_multireq: no cast_ill for %s %d\n",
83811042SErik.Nordmark@Sun.COM 			    ill->ill_name, ill->ill_isv6));
83911042SErik.Nordmark@Sun.COM 			return (0);
84011042SErik.Nordmark@Sun.COM 		}
84111042SErik.Nordmark@Sun.COM 		ill = release_ill;
84211042SErik.Nordmark@Sun.COM 	}
84311042SErik.Nordmark@Sun.COM 	/* Create a DL_ENABMULTI_REQ or DL_DISABMULTI_REQ message. */
84411042SErik.Nordmark@Sun.COM 	mp = ill_create_dl(ill, prim, &addrlen, &addroff);
84511042SErik.Nordmark@Sun.COM 	if (mp == NULL) {
84611042SErik.Nordmark@Sun.COM 		err = ENOMEM;
84711042SErik.Nordmark@Sun.COM 		goto done;
84811042SErik.Nordmark@Sun.COM 	}
84911042SErik.Nordmark@Sun.COM 
85011042SErik.Nordmark@Sun.COM 	mp = ndp_mcastreq(ill, v6groupp, addrlen, addroff, mp);
85111042SErik.Nordmark@Sun.COM 	if (mp == NULL) {
85211042SErik.Nordmark@Sun.COM 		ip0dbg(("null from ndp_mcastreq(ill %s)\n", ill->ill_name));
85311042SErik.Nordmark@Sun.COM 		err = ENOMEM;
85411042SErik.Nordmark@Sun.COM 		goto done;
85511042SErik.Nordmark@Sun.COM 	}
856*12811SRavichandra.Nallan@Sun.COM 	cp = mp->b_rptr;
857*12811SRavichandra.Nallan@Sun.COM 
858*12811SRavichandra.Nallan@Sun.COM 	switch (((union DL_primitives *)cp)->dl_primitive) {
85911042SErik.Nordmark@Sun.COM 	case DL_ENABMULTI_REQ:
860*12811SRavichandra.Nallan@Sun.COM 		cp += ((dl_enabmulti_req_t *)cp)->dl_addr_offset;
861*12811SRavichandra.Nallan@Sun.COM 		if (!ip_mphysaddr_add(ill, cp)) {
862*12811SRavichandra.Nallan@Sun.COM 			freemsg(mp);
863*12811SRavichandra.Nallan@Sun.COM 			err = 0;
864*12811SRavichandra.Nallan@Sun.COM 			goto done;
865*12811SRavichandra.Nallan@Sun.COM 		}
86611042SErik.Nordmark@Sun.COM 		mutex_enter(&ill->ill_lock);
8679073SCathy.Zhou@Sun.COM 		/* Track the state if this is the first enabmulti */
8689073SCathy.Zhou@Sun.COM 		if (ill->ill_dlpi_multicast_state == IDS_UNKNOWN)
8699073SCathy.Zhou@Sun.COM 			ill->ill_dlpi_multicast_state = IDS_INPROGRESS;
87011042SErik.Nordmark@Sun.COM 		mutex_exit(&ill->ill_lock);
87111042SErik.Nordmark@Sun.COM 		break;
872*12811SRavichandra.Nallan@Sun.COM 	case DL_DISABMULTI_REQ:
873*12811SRavichandra.Nallan@Sun.COM 		cp += ((dl_disabmulti_req_t *)cp)->dl_addr_offset;
874*12811SRavichandra.Nallan@Sun.COM 		if (!ip_mphysaddr_del(ill, cp)) {
875*12811SRavichandra.Nallan@Sun.COM 			freemsg(mp);
876*12811SRavichandra.Nallan@Sun.COM 			err = 0;
877*12811SRavichandra.Nallan@Sun.COM 			goto done;
878*12811SRavichandra.Nallan@Sun.COM 		}
8790Sstevel@tonic-gate 	}
88011042SErik.Nordmark@Sun.COM 	ill_dlpi_queue(ill, mp);
88111042SErik.Nordmark@Sun.COM done:
88211042SErik.Nordmark@Sun.COM 	if (release_ill != NULL)
88311042SErik.Nordmark@Sun.COM 		ill_refrele(release_ill);
88411042SErik.Nordmark@Sun.COM 	return (err);
8850Sstevel@tonic-gate }
8860Sstevel@tonic-gate 
8870Sstevel@tonic-gate /*
8880Sstevel@tonic-gate  * Send a multicast request to the driver for enabling multicast
8890Sstevel@tonic-gate  * membership for v6group if appropriate.
8900Sstevel@tonic-gate  */
8910Sstevel@tonic-gate static int
ip_ll_multireq(ill_t * ill,const in6_addr_t * v6groupp,t_uscalar_t prim)89211042SErik.Nordmark@Sun.COM ip_ll_multireq(ill_t *ill, const in6_addr_t *v6groupp, t_uscalar_t prim)
8930Sstevel@tonic-gate {
8940Sstevel@tonic-gate 	if (ill->ill_net_type != IRE_IF_RESOLVER ||
89511042SErik.Nordmark@Sun.COM 	    ill->ill_ipif->ipif_flags & IPIF_POINTOPOINT) {
89611042SErik.Nordmark@Sun.COM 		ip1dbg(("ip_ll_multireq: not resolver\n"));
8970Sstevel@tonic-gate 		return (0);	/* Must be IRE_IF_NORESOLVER */
8980Sstevel@tonic-gate 	}
8990Sstevel@tonic-gate 
9000Sstevel@tonic-gate 	if (ill->ill_phyint->phyint_flags & PHYI_MULTI_BCAST) {
90111042SErik.Nordmark@Sun.COM 		ip1dbg(("ip_ll_multireq: MULTI_BCAST\n"));
9020Sstevel@tonic-gate 		return (0);
9030Sstevel@tonic-gate 	}
90411042SErik.Nordmark@Sun.COM 	return (ip_ll_send_multireq(ill, v6groupp, prim));
9050Sstevel@tonic-gate }
9060Sstevel@tonic-gate 
9070Sstevel@tonic-gate /*
90811042SErik.Nordmark@Sun.COM  * Delete the ilm. Used by other parts of IP for the case of no_ilg/leaving
90911042SErik.Nordmark@Sun.COM  * being true.
9100Sstevel@tonic-gate  */
9110Sstevel@tonic-gate int
ip_delmulti(ilm_t * ilm)91211042SErik.Nordmark@Sun.COM ip_delmulti(ilm_t *ilm)
91311042SErik.Nordmark@Sun.COM {
91411042SErik.Nordmark@Sun.COM 	ill_t *ill = ilm->ilm_ill;
91511042SErik.Nordmark@Sun.COM 	int error;
91611042SErik.Nordmark@Sun.COM 
91711042SErik.Nordmark@Sun.COM 	/* Acquire serializer to keep assert in ilm_bld_flists happy */
91811042SErik.Nordmark@Sun.COM 	mutex_enter(&ill->ill_mcast_serializer);
91911042SErik.Nordmark@Sun.COM 	error = ip_delmulti_serial(ilm, B_TRUE, B_TRUE);
92011042SErik.Nordmark@Sun.COM 	mutex_exit(&ill->ill_mcast_serializer);
92111463SSowmini.Varadhan@Sun.COM 	/*
92211463SSowmini.Varadhan@Sun.COM 	 * Now that all locks have been dropped, we can send any
92311463SSowmini.Varadhan@Sun.COM 	 * deferred/queued DLPI or IP packets
92411463SSowmini.Varadhan@Sun.COM 	 */
92511463SSowmini.Varadhan@Sun.COM 	ill_mcast_send_queued(ill);
92611463SSowmini.Varadhan@Sun.COM 	ill_dlpi_send_queued(ill);
92711042SErik.Nordmark@Sun.COM 	return (error);
92811042SErik.Nordmark@Sun.COM }
92911042SErik.Nordmark@Sun.COM 
93011042SErik.Nordmark@Sun.COM 
93111042SErik.Nordmark@Sun.COM /*
93211042SErik.Nordmark@Sun.COM  * Delete the ilm.
93311463SSowmini.Varadhan@Sun.COM  * Assumes ill_mcast_serializer is held by the caller.
93411463SSowmini.Varadhan@Sun.COM  * Caller must send out queued dlpi/multicast packets after dropping
93511463SSowmini.Varadhan@Sun.COM  * all locks.
93611042SErik.Nordmark@Sun.COM  */
93711042SErik.Nordmark@Sun.COM static int
ip_delmulti_serial(ilm_t * ilm,boolean_t no_ilg,boolean_t leaving)93811042SErik.Nordmark@Sun.COM ip_delmulti_serial(ilm_t *ilm, boolean_t no_ilg, boolean_t leaving)
9390Sstevel@tonic-gate {
94011042SErik.Nordmark@Sun.COM 	ill_t *ill = ilm->ilm_ill;
94111042SErik.Nordmark@Sun.COM 	int ret;
94211042SErik.Nordmark@Sun.COM 
94311042SErik.Nordmark@Sun.COM 	ASSERT(MUTEX_HELD(&ill->ill_mcast_serializer));
94411042SErik.Nordmark@Sun.COM 	ASSERT(!(IS_UNDER_IPMP(ill)));
94511042SErik.Nordmark@Sun.COM 
94611042SErik.Nordmark@Sun.COM 	rw_enter(&ill->ill_mcast_lock, RW_WRITER);
94711042SErik.Nordmark@Sun.COM 	ret = ip_delmulti_impl(ilm, no_ilg, leaving);
94811042SErik.Nordmark@Sun.COM 	rw_exit(&ill->ill_mcast_lock);
94911042SErik.Nordmark@Sun.COM 	ill_mcast_timer_start(ill->ill_ipst);
95011042SErik.Nordmark@Sun.COM 	return (ret);
95111042SErik.Nordmark@Sun.COM }
95211042SErik.Nordmark@Sun.COM 
95311042SErik.Nordmark@Sun.COM static int
ip_delmulti_impl(ilm_t * ilm,boolean_t no_ilg,boolean_t leaving)95411042SErik.Nordmark@Sun.COM ip_delmulti_impl(ilm_t *ilm, boolean_t no_ilg, boolean_t leaving)
95511042SErik.Nordmark@Sun.COM {
95611042SErik.Nordmark@Sun.COM 	ill_t *ill = ilm->ilm_ill;
95711042SErik.Nordmark@Sun.COM 	int error;
9580Sstevel@tonic-gate 	in6_addr_t v6group;
9590Sstevel@tonic-gate 
96011042SErik.Nordmark@Sun.COM 	ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock));
9610Sstevel@tonic-gate 
9620Sstevel@tonic-gate 	/* Update counters */
9630Sstevel@tonic-gate 	if (no_ilg)
9640Sstevel@tonic-gate 		ilm->ilm_no_ilg_cnt--;
9650Sstevel@tonic-gate 
9660Sstevel@tonic-gate 	if (leaving)
9670Sstevel@tonic-gate 		ilm->ilm_refcnt--;
9680Sstevel@tonic-gate 
9690Sstevel@tonic-gate 	if (ilm->ilm_refcnt > 0)
97011042SErik.Nordmark@Sun.COM 		return (ilm_update_del(ilm));
97111042SErik.Nordmark@Sun.COM 
97211042SErik.Nordmark@Sun.COM 	v6group = ilm->ilm_v6addr;
97311042SErik.Nordmark@Sun.COM 
97411042SErik.Nordmark@Sun.COM 	if (IN6_IS_ADDR_UNSPECIFIED(&ilm->ilm_v6addr)) {
9750Sstevel@tonic-gate 		ilm_delete(ilm);
9760Sstevel@tonic-gate 		/*
97711042SErik.Nordmark@Sun.COM 		 * If we have some left then one we should not tell the driver
97811042SErik.Nordmark@Sun.COM 		 * to leave.
9790Sstevel@tonic-gate 		 */
98011042SErik.Nordmark@Sun.COM 		if (ilm_numentries(ill, &v6group) != 0)
9810Sstevel@tonic-gate 			return (0);
9820Sstevel@tonic-gate 
98311042SErik.Nordmark@Sun.COM 		ill_leave_allmulti(ill);
9848485SPeter.Memishian@Sun.COM 
9858023SPhil.Kirk@Sun.COM 		return (0);
9860Sstevel@tonic-gate 	}
9870Sstevel@tonic-gate 
98811042SErik.Nordmark@Sun.COM 	if (!IS_LOOPBACK(ill)) {
98911042SErik.Nordmark@Sun.COM 		if (ill->ill_isv6)
99011042SErik.Nordmark@Sun.COM 			mld_leavegroup(ilm);
99111042SErik.Nordmark@Sun.COM 		else
99211042SErik.Nordmark@Sun.COM 			igmp_leavegroup(ilm);
9930Sstevel@tonic-gate 	}
9940Sstevel@tonic-gate 
9950Sstevel@tonic-gate 	ilm_delete(ilm);
9960Sstevel@tonic-gate 	/*
99711042SErik.Nordmark@Sun.COM 	 * If we have some left then one we should not tell the driver
99811042SErik.Nordmark@Sun.COM 	 * to leave.
9990Sstevel@tonic-gate 	 */
100011042SErik.Nordmark@Sun.COM 	if (ilm_numentries(ill, &v6group) != 0)
10010Sstevel@tonic-gate 		return (0);
100211042SErik.Nordmark@Sun.COM 
100311042SErik.Nordmark@Sun.COM 	error = ip_ll_multireq(ill, &v6group, DL_DISABMULTI_REQ);
100411042SErik.Nordmark@Sun.COM 	/* We ignore the case when ill_dl_up is not set */
100511042SErik.Nordmark@Sun.COM 	if (error == ENETDOWN) {
100611042SErik.Nordmark@Sun.COM 		char buf[INET6_ADDRSTRLEN];
100711042SErik.Nordmark@Sun.COM 
100811042SErik.Nordmark@Sun.COM 		ip0dbg(("ip_delmulti: ENETDOWN for %s on %s",
100911042SErik.Nordmark@Sun.COM 		    inet_ntop(AF_INET6, &v6group, buf, sizeof (buf)),
101011042SErik.Nordmark@Sun.COM 		    ill->ill_name));
101111042SErik.Nordmark@Sun.COM 	}
101211042SErik.Nordmark@Sun.COM 	return (error);
10130Sstevel@tonic-gate }
10140Sstevel@tonic-gate 
10150Sstevel@tonic-gate /*
101611042SErik.Nordmark@Sun.COM  * Make the driver pass up all multicast packets.
10170Sstevel@tonic-gate  */
10180Sstevel@tonic-gate int
ill_join_allmulti(ill_t * ill)10198023SPhil.Kirk@Sun.COM ill_join_allmulti(ill_t *ill)
10200Sstevel@tonic-gate {
102111042SErik.Nordmark@Sun.COM 	mblk_t		*promiscon_mp, *promiscoff_mp = NULL;
10220Sstevel@tonic-gate 	uint32_t	addrlen, addroff;
102311042SErik.Nordmark@Sun.COM 	ill_t		*release_ill = NULL;
102411042SErik.Nordmark@Sun.COM 
102511042SErik.Nordmark@Sun.COM 	ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock));
10260Sstevel@tonic-gate 
102711077SErik.Nordmark@Sun.COM 	if (IS_LOOPBACK(ill))
102811077SErik.Nordmark@Sun.COM 		return (0);
102911077SErik.Nordmark@Sun.COM 
10304770Smeem 	if (!ill->ill_dl_up) {
10310Sstevel@tonic-gate 		/*
10320Sstevel@tonic-gate 		 * Nobody there. All multicast addresses will be re-joined
10330Sstevel@tonic-gate 		 * when we get the DL_BIND_ACK bringing the interface up.
10340Sstevel@tonic-gate 		 */
103511042SErik.Nordmark@Sun.COM 		return (ENETDOWN);
10360Sstevel@tonic-gate 	}
10370Sstevel@tonic-gate 
103811042SErik.Nordmark@Sun.COM 	if (IS_IPMP(ill)) {
103911042SErik.Nordmark@Sun.COM 		/* On the upper IPMP ill. */
104011042SErik.Nordmark@Sun.COM 		release_ill = ipmp_illgrp_hold_cast_ill(ill->ill_grp);
104111042SErik.Nordmark@Sun.COM 		if (release_ill == NULL) {
104211042SErik.Nordmark@Sun.COM 			/*
104311042SErik.Nordmark@Sun.COM 			 * Avoid sending it down to the ipmpstub.
104411042SErik.Nordmark@Sun.COM 			 * We will be called again once the members of the
104511042SErik.Nordmark@Sun.COM 			 * group are in place
104611042SErik.Nordmark@Sun.COM 			 */
104711042SErik.Nordmark@Sun.COM 			ip1dbg(("ill_join_allmulti: no cast_ill for %s %d\n",
104811042SErik.Nordmark@Sun.COM 			    ill->ill_name, ill->ill_isv6));
104911042SErik.Nordmark@Sun.COM 			return (0);
105011042SErik.Nordmark@Sun.COM 		}
105111042SErik.Nordmark@Sun.COM 		ill = release_ill;
105211042SErik.Nordmark@Sun.COM 		if (!ill->ill_dl_up) {
105311042SErik.Nordmark@Sun.COM 			ill_refrele(ill);
105411042SErik.Nordmark@Sun.COM 			return (ENETDOWN);
105511042SErik.Nordmark@Sun.COM 		}
105611042SErik.Nordmark@Sun.COM 	}
10570Sstevel@tonic-gate 
10580Sstevel@tonic-gate 	/*
10598023SPhil.Kirk@Sun.COM 	 * Create a DL_PROMISCON_REQ message and send it directly to the DLPI
10608023SPhil.Kirk@Sun.COM 	 * provider.  We don't need to do this for certain media types for
10618023SPhil.Kirk@Sun.COM 	 * which we never need to turn promiscuous mode on.  While we're here,
10628023SPhil.Kirk@Sun.COM 	 * pre-allocate a DL_PROMISCOFF_REQ message to make sure that
10638023SPhil.Kirk@Sun.COM 	 * ill_leave_allmulti() will not fail due to low memory conditions.
10640Sstevel@tonic-gate 	 */
10650Sstevel@tonic-gate 	if ((ill->ill_net_type == IRE_IF_RESOLVER) &&
10660Sstevel@tonic-gate 	    !(ill->ill_phyint->phyint_flags & PHYI_MULTI_BCAST)) {
10678023SPhil.Kirk@Sun.COM 		promiscon_mp = ill_create_dl(ill, DL_PROMISCON_REQ,
106811042SErik.Nordmark@Sun.COM 		    &addrlen, &addroff);
106911042SErik.Nordmark@Sun.COM 		if (ill->ill_promiscoff_mp == NULL)
107011042SErik.Nordmark@Sun.COM 			promiscoff_mp = ill_create_dl(ill, DL_PROMISCOFF_REQ,
107111042SErik.Nordmark@Sun.COM 			    &addrlen, &addroff);
107211042SErik.Nordmark@Sun.COM 		if (promiscon_mp == NULL ||
107311042SErik.Nordmark@Sun.COM 		    (ill->ill_promiscoff_mp == NULL && promiscoff_mp == NULL)) {
10748023SPhil.Kirk@Sun.COM 			freemsg(promiscon_mp);
10758023SPhil.Kirk@Sun.COM 			freemsg(promiscoff_mp);
107611042SErik.Nordmark@Sun.COM 			if (release_ill != NULL)
107711042SErik.Nordmark@Sun.COM 				ill_refrele(release_ill);
10780Sstevel@tonic-gate 			return (ENOMEM);
10798023SPhil.Kirk@Sun.COM 		}
108011042SErik.Nordmark@Sun.COM 		if (ill->ill_promiscoff_mp == NULL)
108111042SErik.Nordmark@Sun.COM 			ill->ill_promiscoff_mp = promiscoff_mp;
108211042SErik.Nordmark@Sun.COM 		ill_dlpi_queue(ill, promiscon_mp);
10830Sstevel@tonic-gate 	}
108411042SErik.Nordmark@Sun.COM 	if (release_ill != NULL)
108511042SErik.Nordmark@Sun.COM 		ill_refrele(release_ill);
10860Sstevel@tonic-gate 	return (0);
10870Sstevel@tonic-gate }
10880Sstevel@tonic-gate 
10890Sstevel@tonic-gate /*
10900Sstevel@tonic-gate  * Make the driver stop passing up all multicast packets
10910Sstevel@tonic-gate  */
10928023SPhil.Kirk@Sun.COM void
ill_leave_allmulti(ill_t * ill)10938023SPhil.Kirk@Sun.COM ill_leave_allmulti(ill_t *ill)
10940Sstevel@tonic-gate {
10958485SPeter.Memishian@Sun.COM 	mblk_t	*promiscoff_mp;
109611042SErik.Nordmark@Sun.COM 	ill_t	*release_ill = NULL;
109711042SErik.Nordmark@Sun.COM 
109811042SErik.Nordmark@Sun.COM 	ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock));
10990Sstevel@tonic-gate 
110011077SErik.Nordmark@Sun.COM 	if (IS_LOOPBACK(ill))
110111077SErik.Nordmark@Sun.COM 		return;
110211077SErik.Nordmark@Sun.COM 
11034770Smeem 	if (!ill->ill_dl_up) {
11040Sstevel@tonic-gate 		/*
11050Sstevel@tonic-gate 		 * Nobody there. All multicast addresses will be re-joined
11060Sstevel@tonic-gate 		 * when we get the DL_BIND_ACK bringing the interface up.
11070Sstevel@tonic-gate 		 */
11088023SPhil.Kirk@Sun.COM 		return;
11090Sstevel@tonic-gate 	}
11100Sstevel@tonic-gate 
111111042SErik.Nordmark@Sun.COM 	if (IS_IPMP(ill)) {
111211042SErik.Nordmark@Sun.COM 		/* On the upper IPMP ill. */
111311042SErik.Nordmark@Sun.COM 		release_ill = ipmp_illgrp_hold_cast_ill(ill->ill_grp);
111411042SErik.Nordmark@Sun.COM 		if (release_ill == NULL) {
111511042SErik.Nordmark@Sun.COM 			/*
111611042SErik.Nordmark@Sun.COM 			 * Avoid sending it down to the ipmpstub.
111711042SErik.Nordmark@Sun.COM 			 * We will be called again once the members of the
111811042SErik.Nordmark@Sun.COM 			 * group are in place
111911042SErik.Nordmark@Sun.COM 			 */
112011042SErik.Nordmark@Sun.COM 			ip1dbg(("ill_leave_allmulti: no cast_ill on %s %d\n",
112111042SErik.Nordmark@Sun.COM 			    ill->ill_name, ill->ill_isv6));
112211042SErik.Nordmark@Sun.COM 			return;
112311042SErik.Nordmark@Sun.COM 		}
112411042SErik.Nordmark@Sun.COM 		ill = release_ill;
112511042SErik.Nordmark@Sun.COM 		if (!ill->ill_dl_up)
112611042SErik.Nordmark@Sun.COM 			goto done;
112711042SErik.Nordmark@Sun.COM 	}
11280Sstevel@tonic-gate 
11290Sstevel@tonic-gate 	/*
113011042SErik.Nordmark@Sun.COM 	 * In the case of IPMP and ill_dl_up not being set when we joined
113111042SErik.Nordmark@Sun.COM 	 * we didn't allocate a promiscoff_mp. In that case we have
113211042SErik.Nordmark@Sun.COM 	 * nothing to do when we leave.
113311042SErik.Nordmark@Sun.COM 	 * Ditto for PHYI_MULTI_BCAST
11340Sstevel@tonic-gate 	 */
113511042SErik.Nordmark@Sun.COM 	promiscoff_mp = ill->ill_promiscoff_mp;
113611042SErik.Nordmark@Sun.COM 	if (promiscoff_mp != NULL) {
11378023SPhil.Kirk@Sun.COM 		ill->ill_promiscoff_mp = NULL;
113811042SErik.Nordmark@Sun.COM 		ill_dlpi_queue(ill, promiscoff_mp);
11390Sstevel@tonic-gate 	}
114011042SErik.Nordmark@Sun.COM done:
114111042SErik.Nordmark@Sun.COM 	if (release_ill != NULL)
114211042SErik.Nordmark@Sun.COM 		ill_refrele(release_ill);
11438023SPhil.Kirk@Sun.COM }
11448023SPhil.Kirk@Sun.COM 
11458023SPhil.Kirk@Sun.COM int
ip_join_allmulti(uint_t ifindex,boolean_t isv6,ip_stack_t * ipst)11468023SPhil.Kirk@Sun.COM ip_join_allmulti(uint_t ifindex, boolean_t isv6, ip_stack_t *ipst)
11478023SPhil.Kirk@Sun.COM {
11488023SPhil.Kirk@Sun.COM 	ill_t		*ill;
114911042SErik.Nordmark@Sun.COM 	int		ret;
115011042SErik.Nordmark@Sun.COM 	ilm_t		*ilm;
115111042SErik.Nordmark@Sun.COM 
115211042SErik.Nordmark@Sun.COM 	ill = ill_lookup_on_ifindex(ifindex, isv6, ipst);
115311042SErik.Nordmark@Sun.COM 	if (ill == NULL)
11548023SPhil.Kirk@Sun.COM 		return (ENODEV);
11558485SPeter.Memishian@Sun.COM 
11568485SPeter.Memishian@Sun.COM 	/*
115711042SErik.Nordmark@Sun.COM 	 * The ip_addmulti() function doesn't allow IPMP underlying interfaces
11588485SPeter.Memishian@Sun.COM 	 * to join allmulti since only the nominated underlying interface in
11598485SPeter.Memishian@Sun.COM 	 * the group should receive multicast.  We silently succeed to avoid
11608485SPeter.Memishian@Sun.COM 	 * having to teach IPobs (currently the only caller of this routine)
11618485SPeter.Memishian@Sun.COM 	 * to ignore failures in this case.
11628485SPeter.Memishian@Sun.COM 	 */
116311042SErik.Nordmark@Sun.COM 	if (IS_UNDER_IPMP(ill)) {
116411042SErik.Nordmark@Sun.COM 		ill_refrele(ill);
116511042SErik.Nordmark@Sun.COM 		return (0);
116611042SErik.Nordmark@Sun.COM 	}
116711042SErik.Nordmark@Sun.COM 	mutex_enter(&ill->ill_lock);
116811042SErik.Nordmark@Sun.COM 	if (ill->ill_ipallmulti_cnt > 0) {
116911042SErik.Nordmark@Sun.COM 		/* Already joined */
117011042SErik.Nordmark@Sun.COM 		ASSERT(ill->ill_ipallmulti_ilm != NULL);
117111042SErik.Nordmark@Sun.COM 		ill->ill_ipallmulti_cnt++;
117211042SErik.Nordmark@Sun.COM 		mutex_exit(&ill->ill_lock);
117311042SErik.Nordmark@Sun.COM 		goto done;
11748023SPhil.Kirk@Sun.COM 	}
117511042SErik.Nordmark@Sun.COM 	mutex_exit(&ill->ill_lock);
117611042SErik.Nordmark@Sun.COM 
117711042SErik.Nordmark@Sun.COM 	ilm = ip_addmulti(&ipv6_all_zeros, ill, ill->ill_zoneid, &ret);
117811042SErik.Nordmark@Sun.COM 	if (ilm == NULL) {
117911042SErik.Nordmark@Sun.COM 		ASSERT(ret != 0);
118011042SErik.Nordmark@Sun.COM 		ill_refrele(ill);
118111042SErik.Nordmark@Sun.COM 		return (ret);
118211042SErik.Nordmark@Sun.COM 	}
118311042SErik.Nordmark@Sun.COM 
118411042SErik.Nordmark@Sun.COM 	mutex_enter(&ill->ill_lock);
118511042SErik.Nordmark@Sun.COM 	if (ill->ill_ipallmulti_cnt > 0) {
118611042SErik.Nordmark@Sun.COM 		/* Another thread added it concurrently */
118711042SErik.Nordmark@Sun.COM 		(void) ip_delmulti(ilm);
118811042SErik.Nordmark@Sun.COM 		mutex_exit(&ill->ill_lock);
118911042SErik.Nordmark@Sun.COM 		goto done;
119011042SErik.Nordmark@Sun.COM 	}
119111042SErik.Nordmark@Sun.COM 	ASSERT(ill->ill_ipallmulti_ilm == NULL);
119211042SErik.Nordmark@Sun.COM 	ill->ill_ipallmulti_ilm = ilm;
11938023SPhil.Kirk@Sun.COM 	ill->ill_ipallmulti_cnt++;
119411042SErik.Nordmark@Sun.COM 	mutex_exit(&ill->ill_lock);
119511042SErik.Nordmark@Sun.COM done:
119611042SErik.Nordmark@Sun.COM 	ill_refrele(ill);
119711042SErik.Nordmark@Sun.COM 	return (0);
11988023SPhil.Kirk@Sun.COM }
11998023SPhil.Kirk@Sun.COM 
12008023SPhil.Kirk@Sun.COM int
ip_leave_allmulti(uint_t ifindex,boolean_t isv6,ip_stack_t * ipst)12018023SPhil.Kirk@Sun.COM ip_leave_allmulti(uint_t ifindex, boolean_t isv6, ip_stack_t *ipst)
12028023SPhil.Kirk@Sun.COM {
12038023SPhil.Kirk@Sun.COM 	ill_t		*ill;
120411042SErik.Nordmark@Sun.COM 	ilm_t		*ilm;
120511042SErik.Nordmark@Sun.COM 
120611042SErik.Nordmark@Sun.COM 	ill = ill_lookup_on_ifindex(ifindex, isv6, ipst);
120711042SErik.Nordmark@Sun.COM 	if (ill == NULL)
12088023SPhil.Kirk@Sun.COM 		return (ENODEV);
12098485SPeter.Memishian@Sun.COM 
121011042SErik.Nordmark@Sun.COM 	if (IS_UNDER_IPMP(ill)) {
121111042SErik.Nordmark@Sun.COM 		ill_refrele(ill);
121211042SErik.Nordmark@Sun.COM 		return (0);
121311042SErik.Nordmark@Sun.COM 	}
121411042SErik.Nordmark@Sun.COM 
121511042SErik.Nordmark@Sun.COM 	mutex_enter(&ill->ill_lock);
121611042SErik.Nordmark@Sun.COM 	if (ill->ill_ipallmulti_cnt == 0) {
121711042SErik.Nordmark@Sun.COM 		/* ip_purge_allmulti could have removed them all */
121811042SErik.Nordmark@Sun.COM 		mutex_exit(&ill->ill_lock);
121911042SErik.Nordmark@Sun.COM 		goto done;
12208023SPhil.Kirk@Sun.COM 	}
122111042SErik.Nordmark@Sun.COM 	ill->ill_ipallmulti_cnt--;
122211042SErik.Nordmark@Sun.COM 	if (ill->ill_ipallmulti_cnt == 0) {
122311042SErik.Nordmark@Sun.COM 		/* Last one */
122411042SErik.Nordmark@Sun.COM 		ilm = ill->ill_ipallmulti_ilm;
122511042SErik.Nordmark@Sun.COM 		ill->ill_ipallmulti_ilm = NULL;
122611042SErik.Nordmark@Sun.COM 	} else {
122711042SErik.Nordmark@Sun.COM 		ilm = NULL;
122811042SErik.Nordmark@Sun.COM 	}
122911042SErik.Nordmark@Sun.COM 	mutex_exit(&ill->ill_lock);
123011042SErik.Nordmark@Sun.COM 	if (ilm != NULL)
123111042SErik.Nordmark@Sun.COM 		(void) ip_delmulti(ilm);
123211042SErik.Nordmark@Sun.COM 
123311042SErik.Nordmark@Sun.COM done:
123411042SErik.Nordmark@Sun.COM 	ill_refrele(ill);
12350Sstevel@tonic-gate 	return (0);
12360Sstevel@tonic-gate }
12370Sstevel@tonic-gate 
12380Sstevel@tonic-gate /*
12398023SPhil.Kirk@Sun.COM  * Delete the allmulti memberships that were added as part of
12408023SPhil.Kirk@Sun.COM  * ip_join_allmulti().
12418023SPhil.Kirk@Sun.COM  */
12428023SPhil.Kirk@Sun.COM void
ip_purge_allmulti(ill_t * ill)12438023SPhil.Kirk@Sun.COM ip_purge_allmulti(ill_t *ill)
12448023SPhil.Kirk@Sun.COM {
124511042SErik.Nordmark@Sun.COM 	ilm_t	*ilm;
124611042SErik.Nordmark@Sun.COM 
12478023SPhil.Kirk@Sun.COM 	ASSERT(IAM_WRITER_ILL(ill));
12488023SPhil.Kirk@Sun.COM 
124911042SErik.Nordmark@Sun.COM 	mutex_enter(&ill->ill_lock);
125011042SErik.Nordmark@Sun.COM 	ilm = ill->ill_ipallmulti_ilm;
125111042SErik.Nordmark@Sun.COM 	ill->ill_ipallmulti_ilm = NULL;
125211042SErik.Nordmark@Sun.COM 	ill->ill_ipallmulti_cnt = 0;
125311042SErik.Nordmark@Sun.COM 	mutex_exit(&ill->ill_lock);
125411042SErik.Nordmark@Sun.COM 
125511042SErik.Nordmark@Sun.COM 	if (ilm != NULL)
125611042SErik.Nordmark@Sun.COM 		(void) ip_delmulti(ilm);
12578023SPhil.Kirk@Sun.COM }
12588023SPhil.Kirk@Sun.COM 
12598023SPhil.Kirk@Sun.COM /*
126011042SErik.Nordmark@Sun.COM  * Create a dlpi message with room for phys+sap. Later
126111042SErik.Nordmark@Sun.COM  * we will strip the sap for those primitives which
126211042SErik.Nordmark@Sun.COM  * only need a physical address.
12630Sstevel@tonic-gate  */
12640Sstevel@tonic-gate static mblk_t *
ill_create_dl(ill_t * ill,uint32_t dl_primitive,uint32_t * addr_lenp,uint32_t * addr_offp)126511042SErik.Nordmark@Sun.COM ill_create_dl(ill_t *ill, uint32_t dl_primitive,
12660Sstevel@tonic-gate     uint32_t *addr_lenp, uint32_t *addr_offp)
12670Sstevel@tonic-gate {
12680Sstevel@tonic-gate 	mblk_t	*mp;
12690Sstevel@tonic-gate 	uint32_t	hw_addr_length;
12700Sstevel@tonic-gate 	char		*cp;
12710Sstevel@tonic-gate 	uint32_t	offset;
127211042SErik.Nordmark@Sun.COM 	uint32_t	length;
12730Sstevel@tonic-gate 	uint32_t 	size;
12740Sstevel@tonic-gate 
12750Sstevel@tonic-gate 	*addr_lenp = *addr_offp = 0;
12760Sstevel@tonic-gate 
12770Sstevel@tonic-gate 	hw_addr_length = ill->ill_phys_addr_length;
12780Sstevel@tonic-gate 	if (!hw_addr_length) {
12790Sstevel@tonic-gate 		ip0dbg(("ip_create_dl: hw addr length = 0\n"));
12800Sstevel@tonic-gate 		return (NULL);
12810Sstevel@tonic-gate 	}
12820Sstevel@tonic-gate 
12830Sstevel@tonic-gate 	switch (dl_primitive) {
12840Sstevel@tonic-gate 	case DL_ENABMULTI_REQ:
128511042SErik.Nordmark@Sun.COM 		length = sizeof (dl_enabmulti_req_t);
128611042SErik.Nordmark@Sun.COM 		size = length + hw_addr_length;
128711042SErik.Nordmark@Sun.COM 		break;
12880Sstevel@tonic-gate 	case DL_DISABMULTI_REQ:
128911042SErik.Nordmark@Sun.COM 		length = sizeof (dl_disabmulti_req_t);
129011042SErik.Nordmark@Sun.COM 		size = length + hw_addr_length;
12910Sstevel@tonic-gate 		break;
12920Sstevel@tonic-gate 	case DL_PROMISCON_REQ:
12930Sstevel@tonic-gate 	case DL_PROMISCOFF_REQ:
129411042SErik.Nordmark@Sun.COM 		size = length = sizeof (dl_promiscon_req_t);
12950Sstevel@tonic-gate 		break;
12960Sstevel@tonic-gate 	default:
12970Sstevel@tonic-gate 		return (NULL);
12980Sstevel@tonic-gate 	}
12990Sstevel@tonic-gate 	mp = allocb(size, BPRI_HI);
13000Sstevel@tonic-gate 	if (!mp)
13010Sstevel@tonic-gate 		return (NULL);
13020Sstevel@tonic-gate 	mp->b_wptr += size;
13030Sstevel@tonic-gate 	mp->b_datap->db_type = M_PROTO;
13040Sstevel@tonic-gate 
13050Sstevel@tonic-gate 	cp = (char *)mp->b_rptr;
13060Sstevel@tonic-gate 	offset = length;
13070Sstevel@tonic-gate 
13080Sstevel@tonic-gate 	switch (dl_primitive) {
13090Sstevel@tonic-gate 	case DL_ENABMULTI_REQ: {
13100Sstevel@tonic-gate 		dl_enabmulti_req_t *dl = (dl_enabmulti_req_t *)cp;
13110Sstevel@tonic-gate 
13120Sstevel@tonic-gate 		dl->dl_primitive = dl_primitive;
13130Sstevel@tonic-gate 		dl->dl_addr_offset = offset;
13140Sstevel@tonic-gate 		*addr_lenp = dl->dl_addr_length = hw_addr_length;
13150Sstevel@tonic-gate 		*addr_offp = offset;
13160Sstevel@tonic-gate 		break;
13170Sstevel@tonic-gate 	}
13180Sstevel@tonic-gate 	case DL_DISABMULTI_REQ: {
13190Sstevel@tonic-gate 		dl_disabmulti_req_t *dl = (dl_disabmulti_req_t *)cp;
13200Sstevel@tonic-gate 
13210Sstevel@tonic-gate 		dl->dl_primitive = dl_primitive;
13220Sstevel@tonic-gate 		dl->dl_addr_offset = offset;
13230Sstevel@tonic-gate 		*addr_lenp = dl->dl_addr_length = hw_addr_length;
13240Sstevel@tonic-gate 		*addr_offp = offset;
13250Sstevel@tonic-gate 		break;
13260Sstevel@tonic-gate 	}
13270Sstevel@tonic-gate 	case DL_PROMISCON_REQ:
13280Sstevel@tonic-gate 	case DL_PROMISCOFF_REQ: {
13290Sstevel@tonic-gate 		dl_promiscon_req_t *dl = (dl_promiscon_req_t *)cp;
13300Sstevel@tonic-gate 
13310Sstevel@tonic-gate 		dl->dl_primitive = dl_primitive;
13320Sstevel@tonic-gate 		dl->dl_level = DL_PROMISC_MULTI;
13330Sstevel@tonic-gate 		break;
13340Sstevel@tonic-gate 	}
13350Sstevel@tonic-gate 	}
13360Sstevel@tonic-gate 	ip1dbg(("ill_create_dl: addr_len %d, addr_off %d\n",
13374459Skcpoon 	    *addr_lenp, *addr_offp));
13380Sstevel@tonic-gate 	return (mp);
13390Sstevel@tonic-gate }
13400Sstevel@tonic-gate 
13416979Smeem /*
134211042SErik.Nordmark@Sun.COM  * Rejoin any groups for which we have ilms.
134311042SErik.Nordmark@Sun.COM  *
134411042SErik.Nordmark@Sun.COM  * This is only needed for IPMP when the cast_ill changes since that
134511042SErik.Nordmark@Sun.COM  * change is invisible to the ilm. Other interface changes are handled
134611042SErik.Nordmark@Sun.COM  * by conn_update_ill.
13470Sstevel@tonic-gate  */
13480Sstevel@tonic-gate void
ill_recover_multicast(ill_t * ill)13490Sstevel@tonic-gate ill_recover_multicast(ill_t *ill)
13500Sstevel@tonic-gate {
13510Sstevel@tonic-gate 	ilm_t	*ilm;
13520Sstevel@tonic-gate 	char    addrbuf[INET6_ADDRSTRLEN];
13530Sstevel@tonic-gate 
13548023SPhil.Kirk@Sun.COM 	ill->ill_need_recover_multicast = 0;
13558023SPhil.Kirk@Sun.COM 
135611042SErik.Nordmark@Sun.COM 	rw_enter(&ill->ill_mcast_lock, RW_WRITER);
13570Sstevel@tonic-gate 	for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
13580Sstevel@tonic-gate 		/*
135911042SErik.Nordmark@Sun.COM 		 * If we have more then one ilm for the group (e.g., with
136011042SErik.Nordmark@Sun.COM 		 * different zoneid) then we should not tell the driver
136111042SErik.Nordmark@Sun.COM 		 * to join unless this is the first ilm for the group.
13620Sstevel@tonic-gate 		 */
136311042SErik.Nordmark@Sun.COM 		if (ilm_numentries(ill, &ilm->ilm_v6addr) > 1 &&
136411042SErik.Nordmark@Sun.COM 		    ilm_lookup(ill, &ilm->ilm_v6addr, ALL_ZONES) != ilm) {
13650Sstevel@tonic-gate 			continue;
13668485SPeter.Memishian@Sun.COM 		}
13678485SPeter.Memishian@Sun.COM 
13688485SPeter.Memishian@Sun.COM 		ip1dbg(("ill_recover_multicast: %s\n", inet_ntop(AF_INET6,
13698485SPeter.Memishian@Sun.COM 		    &ilm->ilm_v6addr, addrbuf, sizeof (addrbuf))));
13708485SPeter.Memishian@Sun.COM 
13710Sstevel@tonic-gate 		if (IN6_IS_ADDR_UNSPECIFIED(&ilm->ilm_v6addr)) {
13728485SPeter.Memishian@Sun.COM 			(void) ill_join_allmulti(ill);
13730Sstevel@tonic-gate 		} else {
13748485SPeter.Memishian@Sun.COM 			if (ill->ill_isv6)
13758485SPeter.Memishian@Sun.COM 				mld_joingroup(ilm);
13768485SPeter.Memishian@Sun.COM 			else
13778485SPeter.Memishian@Sun.COM 				igmp_joingroup(ilm);
13788485SPeter.Memishian@Sun.COM 
137911042SErik.Nordmark@Sun.COM 			(void) ip_ll_multireq(ill, &ilm->ilm_v6addr,
138011042SErik.Nordmark@Sun.COM 			    DL_ENABMULTI_REQ);
13810Sstevel@tonic-gate 		}
13820Sstevel@tonic-gate 	}
138311042SErik.Nordmark@Sun.COM 	rw_exit(&ill->ill_mcast_lock);
138411042SErik.Nordmark@Sun.COM 	/* Send any deferred/queued DLPI or IP packets */
138511042SErik.Nordmark@Sun.COM 	ill_mcast_send_queued(ill);
138611042SErik.Nordmark@Sun.COM 	ill_dlpi_send_queued(ill);
138711042SErik.Nordmark@Sun.COM 	ill_mcast_timer_start(ill->ill_ipst);
13880Sstevel@tonic-gate }
13890Sstevel@tonic-gate 
13900Sstevel@tonic-gate /*
13910Sstevel@tonic-gate  * The opposite of ill_recover_multicast() -- leaves all multicast groups
13928485SPeter.Memishian@Sun.COM  * that were explicitly joined.
139311042SErik.Nordmark@Sun.COM  *
139411042SErik.Nordmark@Sun.COM  * This is only needed for IPMP when the cast_ill changes since that
139511042SErik.Nordmark@Sun.COM  * change is invisible to the ilm. Other interface changes are handled
139611042SErik.Nordmark@Sun.COM  * by conn_update_ill.
13970Sstevel@tonic-gate  */
13980Sstevel@tonic-gate void
ill_leave_multicast(ill_t * ill)13990Sstevel@tonic-gate ill_leave_multicast(ill_t *ill)
14000Sstevel@tonic-gate {
14010Sstevel@tonic-gate 	ilm_t	*ilm;
14020Sstevel@tonic-gate 	char    addrbuf[INET6_ADDRSTRLEN];
14030Sstevel@tonic-gate 
14048023SPhil.Kirk@Sun.COM 	ill->ill_need_recover_multicast = 1;
14058023SPhil.Kirk@Sun.COM 
140611042SErik.Nordmark@Sun.COM 	rw_enter(&ill->ill_mcast_lock, RW_WRITER);
14070Sstevel@tonic-gate 	for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
14080Sstevel@tonic-gate 		/*
140911042SErik.Nordmark@Sun.COM 		 * If we have more then one ilm for the group (e.g., with
141011042SErik.Nordmark@Sun.COM 		 * different zoneid) then we should not tell the driver
141111042SErik.Nordmark@Sun.COM 		 * to leave unless this is the first ilm for the group.
14120Sstevel@tonic-gate 		 */
141311042SErik.Nordmark@Sun.COM 		if (ilm_numentries(ill, &ilm->ilm_v6addr) > 1 &&
141411042SErik.Nordmark@Sun.COM 		    ilm_lookup(ill, &ilm->ilm_v6addr, ALL_ZONES) != ilm) {
14150Sstevel@tonic-gate 			continue;
14168485SPeter.Memishian@Sun.COM 		}
14178485SPeter.Memishian@Sun.COM 
14188485SPeter.Memishian@Sun.COM 		ip1dbg(("ill_leave_multicast: %s\n", inet_ntop(AF_INET6,
14198485SPeter.Memishian@Sun.COM 		    &ilm->ilm_v6addr, addrbuf, sizeof (addrbuf))));
14208485SPeter.Memishian@Sun.COM 
14210Sstevel@tonic-gate 		if (IN6_IS_ADDR_UNSPECIFIED(&ilm->ilm_v6addr)) {
14228023SPhil.Kirk@Sun.COM 			ill_leave_allmulti(ill);
14230Sstevel@tonic-gate 		} else {
14248485SPeter.Memishian@Sun.COM 			if (ill->ill_isv6)
14258485SPeter.Memishian@Sun.COM 				mld_leavegroup(ilm);
14268485SPeter.Memishian@Sun.COM 			else
14278485SPeter.Memishian@Sun.COM 				igmp_leavegroup(ilm);
14288485SPeter.Memishian@Sun.COM 
142911042SErik.Nordmark@Sun.COM 			(void) ip_ll_multireq(ill, &ilm->ilm_v6addr,
143011042SErik.Nordmark@Sun.COM 			    DL_DISABMULTI_REQ);
14310Sstevel@tonic-gate 		}
14320Sstevel@tonic-gate 	}
143311042SErik.Nordmark@Sun.COM 	rw_exit(&ill->ill_mcast_lock);
143411042SErik.Nordmark@Sun.COM 	/* Send any deferred/queued DLPI or IP packets */
143511042SErik.Nordmark@Sun.COM 	ill_mcast_send_queued(ill);
143611042SErik.Nordmark@Sun.COM 	ill_dlpi_send_queued(ill);
143711042SErik.Nordmark@Sun.COM 	ill_mcast_timer_start(ill->ill_ipst);
14380Sstevel@tonic-gate }
14390Sstevel@tonic-gate 
144011042SErik.Nordmark@Sun.COM /*
144111042SErik.Nordmark@Sun.COM  * Interface used by IP input/output.
144211042SErik.Nordmark@Sun.COM  * Returns true if there is a member on the ill for any zoneid.
144311042SErik.Nordmark@Sun.COM  */
144411042SErik.Nordmark@Sun.COM boolean_t
ill_hasmembers_v6(ill_t * ill,const in6_addr_t * v6group)144511042SErik.Nordmark@Sun.COM ill_hasmembers_v6(ill_t *ill, const in6_addr_t *v6group)
144611042SErik.Nordmark@Sun.COM {
144711042SErik.Nordmark@Sun.COM 	ilm_t		*ilm;
144811042SErik.Nordmark@Sun.COM 
144911042SErik.Nordmark@Sun.COM 	rw_enter(&ill->ill_mcast_lock, RW_READER);
145011042SErik.Nordmark@Sun.COM 	ilm = ilm_lookup(ill, v6group, ALL_ZONES);
145111042SErik.Nordmark@Sun.COM 	rw_exit(&ill->ill_mcast_lock);
145211042SErik.Nordmark@Sun.COM 	return (ilm != NULL);
145311042SErik.Nordmark@Sun.COM }
145411042SErik.Nordmark@Sun.COM 
145511042SErik.Nordmark@Sun.COM /*
145611042SErik.Nordmark@Sun.COM  * Interface used by IP input/output.
145711042SErik.Nordmark@Sun.COM  * Returns true if there is a member on the ill for any zoneid.
145811042SErik.Nordmark@Sun.COM  *
145911042SErik.Nordmark@Sun.COM  * The group and source can't be INADDR_ANY here so no need to translate to
146011042SErik.Nordmark@Sun.COM  * the unspecified IPv6 address.
146111042SErik.Nordmark@Sun.COM  */
146211042SErik.Nordmark@Sun.COM boolean_t
ill_hasmembers_v4(ill_t * ill,ipaddr_t group)146311042SErik.Nordmark@Sun.COM ill_hasmembers_v4(ill_t *ill, ipaddr_t group)
14640Sstevel@tonic-gate {
14650Sstevel@tonic-gate 	in6_addr_t	v6group;
14660Sstevel@tonic-gate 
146711042SErik.Nordmark@Sun.COM 	IN6_IPADDR_TO_V4MAPPED(group, &v6group);
146811042SErik.Nordmark@Sun.COM 	return (ill_hasmembers_v6(ill, &v6group));
146911042SErik.Nordmark@Sun.COM }
147011042SErik.Nordmark@Sun.COM 
147111042SErik.Nordmark@Sun.COM /*
147211042SErik.Nordmark@Sun.COM  * Interface used by IP input/output.
147311042SErik.Nordmark@Sun.COM  * Returns true if there is a member on the ill for any zoneid except skipzone.
147411042SErik.Nordmark@Sun.COM  */
147511042SErik.Nordmark@Sun.COM boolean_t
ill_hasmembers_otherzones_v6(ill_t * ill,const in6_addr_t * v6group,zoneid_t skipzone)147611042SErik.Nordmark@Sun.COM ill_hasmembers_otherzones_v6(ill_t *ill, const in6_addr_t *v6group,
147711042SErik.Nordmark@Sun.COM     zoneid_t skipzone)
147811042SErik.Nordmark@Sun.COM {
147911042SErik.Nordmark@Sun.COM 	ilm_t		*ilm;
148011042SErik.Nordmark@Sun.COM 
148111042SErik.Nordmark@Sun.COM 	rw_enter(&ill->ill_mcast_lock, RW_READER);
148211042SErik.Nordmark@Sun.COM 	for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
148311042SErik.Nordmark@Sun.COM 		if (IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, v6group) &&
148411042SErik.Nordmark@Sun.COM 		    ilm->ilm_zoneid != skipzone) {
148511042SErik.Nordmark@Sun.COM 			rw_exit(&ill->ill_mcast_lock);
148611042SErik.Nordmark@Sun.COM 			return (B_TRUE);
148711042SErik.Nordmark@Sun.COM 		}
148811042SErik.Nordmark@Sun.COM 	}
148911042SErik.Nordmark@Sun.COM 	rw_exit(&ill->ill_mcast_lock);
149011042SErik.Nordmark@Sun.COM 	return (B_FALSE);
14910Sstevel@tonic-gate }
14920Sstevel@tonic-gate 
14930Sstevel@tonic-gate /*
149411042SErik.Nordmark@Sun.COM  * Interface used by IP input/output.
149511042SErik.Nordmark@Sun.COM  * Returns true if there is a member on the ill for any zoneid except skipzone.
149611042SErik.Nordmark@Sun.COM  *
149711042SErik.Nordmark@Sun.COM  * The group and source can't be INADDR_ANY here so no need to translate to
149811042SErik.Nordmark@Sun.COM  * the unspecified IPv6 address.
149911042SErik.Nordmark@Sun.COM  */
150011042SErik.Nordmark@Sun.COM boolean_t
ill_hasmembers_otherzones_v4(ill_t * ill,ipaddr_t group,zoneid_t skipzone)150111042SErik.Nordmark@Sun.COM ill_hasmembers_otherzones_v4(ill_t *ill, ipaddr_t group, zoneid_t skipzone)
150211042SErik.Nordmark@Sun.COM {
150311042SErik.Nordmark@Sun.COM 	in6_addr_t	v6group;
150411042SErik.Nordmark@Sun.COM 
150511042SErik.Nordmark@Sun.COM 	IN6_IPADDR_TO_V4MAPPED(group, &v6group);
150611042SErik.Nordmark@Sun.COM 	return (ill_hasmembers_otherzones_v6(ill, &v6group, skipzone));
150711042SErik.Nordmark@Sun.COM }
150811042SErik.Nordmark@Sun.COM 
150911042SErik.Nordmark@Sun.COM /*
151011042SErik.Nordmark@Sun.COM  * Interface used by IP input.
151111042SErik.Nordmark@Sun.COM  * Returns the next numerically larger zoneid that has a member. If none exist
151211042SErik.Nordmark@Sun.COM  * then returns -1 (ALL_ZONES).
151311042SErik.Nordmark@Sun.COM  * The normal usage is for the caller to start with a -1 zoneid (ALL_ZONES)
151411042SErik.Nordmark@Sun.COM  * to find the first zoneid which has a member, and then pass that in for
151511042SErik.Nordmark@Sun.COM  * subsequent calls until ALL_ZONES is returned.
151611042SErik.Nordmark@Sun.COM  *
151711042SErik.Nordmark@Sun.COM  * The implementation of ill_hasmembers_nextzone() assumes the ilms
151811042SErik.Nordmark@Sun.COM  * are sorted by zoneid for efficiency.
15190Sstevel@tonic-gate  */
152011042SErik.Nordmark@Sun.COM zoneid_t
ill_hasmembers_nextzone_v6(ill_t * ill,const in6_addr_t * v6group,zoneid_t zoneid)152111042SErik.Nordmark@Sun.COM ill_hasmembers_nextzone_v6(ill_t *ill, const in6_addr_t *v6group,
152211042SErik.Nordmark@Sun.COM     zoneid_t zoneid)
152311042SErik.Nordmark@Sun.COM {
152411042SErik.Nordmark@Sun.COM 	ilm_t		*ilm;
152511042SErik.Nordmark@Sun.COM 
152611042SErik.Nordmark@Sun.COM 	rw_enter(&ill->ill_mcast_lock, RW_READER);
152711042SErik.Nordmark@Sun.COM 	for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
152811042SErik.Nordmark@Sun.COM 		if (IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, v6group) &&
152911042SErik.Nordmark@Sun.COM 		    ilm->ilm_zoneid > zoneid) {
153011042SErik.Nordmark@Sun.COM 			zoneid = ilm->ilm_zoneid;
153111042SErik.Nordmark@Sun.COM 			rw_exit(&ill->ill_mcast_lock);
153211042SErik.Nordmark@Sun.COM 			return (zoneid);
153311042SErik.Nordmark@Sun.COM 		}
153411042SErik.Nordmark@Sun.COM 	}
153511042SErik.Nordmark@Sun.COM 	rw_exit(&ill->ill_mcast_lock);
153611042SErik.Nordmark@Sun.COM 	return (ALL_ZONES);
153711042SErik.Nordmark@Sun.COM }
153811042SErik.Nordmark@Sun.COM 
153911042SErik.Nordmark@Sun.COM /*
154011042SErik.Nordmark@Sun.COM  * Interface used by IP input.
154111042SErik.Nordmark@Sun.COM  * Returns the next numerically larger zoneid that has a member. If none exist
154211042SErik.Nordmark@Sun.COM  * then returns -1 (ALL_ZONES).
154311042SErik.Nordmark@Sun.COM  *
154411042SErik.Nordmark@Sun.COM  * The group and source can't be INADDR_ANY here so no need to translate to
154511042SErik.Nordmark@Sun.COM  * the unspecified IPv6 address.
154611042SErik.Nordmark@Sun.COM  */
154711042SErik.Nordmark@Sun.COM zoneid_t
ill_hasmembers_nextzone_v4(ill_t * ill,ipaddr_t group,zoneid_t zoneid)154811042SErik.Nordmark@Sun.COM ill_hasmembers_nextzone_v4(ill_t *ill, ipaddr_t group, zoneid_t zoneid)
154911042SErik.Nordmark@Sun.COM {
155011042SErik.Nordmark@Sun.COM 	in6_addr_t	v6group;
155111042SErik.Nordmark@Sun.COM 
155211042SErik.Nordmark@Sun.COM 	IN6_IPADDR_TO_V4MAPPED(group, &v6group);
155311042SErik.Nordmark@Sun.COM 
155411042SErik.Nordmark@Sun.COM 	return (ill_hasmembers_nextzone_v6(ill, &v6group, zoneid));
155511042SErik.Nordmark@Sun.COM }
155611042SErik.Nordmark@Sun.COM 
155711042SErik.Nordmark@Sun.COM /*
155811042SErik.Nordmark@Sun.COM  * Find an ilm matching the ill, group, and zoneid.
155911042SErik.Nordmark@Sun.COM  */
156011042SErik.Nordmark@Sun.COM static ilm_t *
ilm_lookup(ill_t * ill,const in6_addr_t * v6group,zoneid_t zoneid)156111042SErik.Nordmark@Sun.COM ilm_lookup(ill_t *ill, const in6_addr_t *v6group, zoneid_t zoneid)
15620Sstevel@tonic-gate {
15630Sstevel@tonic-gate 	ilm_t	*ilm;
156411042SErik.Nordmark@Sun.COM 
156511042SErik.Nordmark@Sun.COM 	ASSERT(RW_LOCK_HELD(&ill->ill_mcast_lock));
156611042SErik.Nordmark@Sun.COM 
156711042SErik.Nordmark@Sun.COM 	for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
15688485SPeter.Memishian@Sun.COM 		if (!IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, v6group))
15690Sstevel@tonic-gate 			continue;
15708485SPeter.Memishian@Sun.COM 		if (zoneid != ALL_ZONES && zoneid != ilm->ilm_zoneid)
15710Sstevel@tonic-gate 			continue;
157211042SErik.Nordmark@Sun.COM 
157311042SErik.Nordmark@Sun.COM 		ASSERT(ilm->ilm_ill == ill);
157411042SErik.Nordmark@Sun.COM 		return (ilm);
15750Sstevel@tonic-gate 	}
157611042SErik.Nordmark@Sun.COM 	return (NULL);
15770Sstevel@tonic-gate }
15780Sstevel@tonic-gate 
15790Sstevel@tonic-gate /*
15800Sstevel@tonic-gate  * How many members on this ill?
158111042SErik.Nordmark@Sun.COM  * Since each shared-IP zone has a separate ilm for the same group/ill
158211042SErik.Nordmark@Sun.COM  * we can have several.
15830Sstevel@tonic-gate  */
158411042SErik.Nordmark@Sun.COM static int
ilm_numentries(ill_t * ill,const in6_addr_t * v6group)158511042SErik.Nordmark@Sun.COM ilm_numentries(ill_t *ill, const in6_addr_t *v6group)
15860Sstevel@tonic-gate {
15870Sstevel@tonic-gate 	ilm_t	*ilm;
15880Sstevel@tonic-gate 	int i = 0;
15890Sstevel@tonic-gate 
159011042SErik.Nordmark@Sun.COM 	ASSERT(RW_LOCK_HELD(&ill->ill_mcast_lock));
15910Sstevel@tonic-gate 	for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
15920Sstevel@tonic-gate 		if (IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, v6group)) {
15930Sstevel@tonic-gate 			i++;
15940Sstevel@tonic-gate 		}
15950Sstevel@tonic-gate 	}
15960Sstevel@tonic-gate 	return (i);
15970Sstevel@tonic-gate }
15980Sstevel@tonic-gate 
15990Sstevel@tonic-gate /* Caller guarantees that the group is not already on the list */
16000Sstevel@tonic-gate static ilm_t *
ilm_add(ill_t * ill,const in6_addr_t * v6group,ilg_stat_t ilgstat,mcast_record_t ilg_fmode,slist_t * ilg_flist,zoneid_t zoneid)160111042SErik.Nordmark@Sun.COM ilm_add(ill_t *ill, const in6_addr_t *v6group, ilg_stat_t ilgstat,
16028485SPeter.Memishian@Sun.COM     mcast_record_t ilg_fmode, slist_t *ilg_flist, zoneid_t zoneid)
16030Sstevel@tonic-gate {
16040Sstevel@tonic-gate 	ilm_t	*ilm;
16050Sstevel@tonic-gate 	ilm_t	*ilm_cur;
16060Sstevel@tonic-gate 	ilm_t	**ilm_ptpn;
16070Sstevel@tonic-gate 
160811042SErik.Nordmark@Sun.COM 	ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock));
16090Sstevel@tonic-gate 	ilm = GETSTRUCT(ilm_t, 1);
16100Sstevel@tonic-gate 	if (ilm == NULL)
16110Sstevel@tonic-gate 		return (NULL);
16120Sstevel@tonic-gate 	if (ilgstat != ILGSTAT_NONE && !SLIST_IS_EMPTY(ilg_flist)) {
16130Sstevel@tonic-gate 		ilm->ilm_filter = l_alloc();
16140Sstevel@tonic-gate 		if (ilm->ilm_filter == NULL) {
16150Sstevel@tonic-gate 			mi_free(ilm);
16160Sstevel@tonic-gate 			return (NULL);
16170Sstevel@tonic-gate 		}
16180Sstevel@tonic-gate 	}
16190Sstevel@tonic-gate 	ilm->ilm_v6addr = *v6group;
16200Sstevel@tonic-gate 	ilm->ilm_refcnt = 1;
16210Sstevel@tonic-gate 	ilm->ilm_zoneid = zoneid;
16220Sstevel@tonic-gate 	ilm->ilm_timer = INFINITY;
16230Sstevel@tonic-gate 	ilm->ilm_rtx.rtx_timer = INFINITY;
16241676Sjpk 
162511042SErik.Nordmark@Sun.COM 	ilm->ilm_ill = ill;
162611042SErik.Nordmark@Sun.COM 	DTRACE_PROBE3(ill__incr__cnt, (ill_t *), ill,
162711042SErik.Nordmark@Sun.COM 	    (char *), "ilm", (void *), ilm);
162811042SErik.Nordmark@Sun.COM 	ill->ill_ilm_cnt++;
16298485SPeter.Memishian@Sun.COM 
16303448Sdh155122 	ASSERT(ill->ill_ipst);
16313448Sdh155122 	ilm->ilm_ipst = ill->ill_ipst;	/* No netstack_hold */
16323448Sdh155122 
163311042SErik.Nordmark@Sun.COM 	/* The ill/ipif could have just been marked as condemned */
16340Sstevel@tonic-gate 
16350Sstevel@tonic-gate 	/*
163611042SErik.Nordmark@Sun.COM 	 * To make ill_hasmembers_nextzone_v6 work we keep the list
163711042SErik.Nordmark@Sun.COM 	 * sorted by zoneid.
16380Sstevel@tonic-gate 	 */
16390Sstevel@tonic-gate 	ilm_cur = ill->ill_ilm;
16400Sstevel@tonic-gate 	ilm_ptpn = &ill->ill_ilm;
164111042SErik.Nordmark@Sun.COM 	while (ilm_cur != NULL && ilm_cur->ilm_zoneid < ilm->ilm_zoneid) {
16420Sstevel@tonic-gate 		ilm_ptpn = &ilm_cur->ilm_next;
16430Sstevel@tonic-gate 		ilm_cur = ilm_cur->ilm_next;
16440Sstevel@tonic-gate 	}
16450Sstevel@tonic-gate 	ilm->ilm_next = ilm_cur;
16460Sstevel@tonic-gate 	*ilm_ptpn = ilm;
16470Sstevel@tonic-gate 
16480Sstevel@tonic-gate 	/*
16490Sstevel@tonic-gate 	 * If we have an associated ilg, use its filter state; if not,
16500Sstevel@tonic-gate 	 * default to (EXCLUDE, NULL) and set no_ilg_cnt to track this.
16510Sstevel@tonic-gate 	 */
16520Sstevel@tonic-gate 	if (ilgstat != ILGSTAT_NONE) {
16530Sstevel@tonic-gate 		if (!SLIST_IS_EMPTY(ilg_flist))
16540Sstevel@tonic-gate 			l_copy(ilg_flist, ilm->ilm_filter);
16550Sstevel@tonic-gate 		ilm->ilm_fmode = ilg_fmode;
16560Sstevel@tonic-gate 	} else {
16570Sstevel@tonic-gate 		ilm->ilm_no_ilg_cnt = 1;
16580Sstevel@tonic-gate 		ilm->ilm_fmode = MODE_IS_EXCLUDE;
16590Sstevel@tonic-gate 	}
16600Sstevel@tonic-gate 
16610Sstevel@tonic-gate 	return (ilm);
16620Sstevel@tonic-gate }
16630Sstevel@tonic-gate 
16646763Ssowmini void
ilm_inactive(ilm_t * ilm)16656255Ssowmini ilm_inactive(ilm_t *ilm)
16666255Ssowmini {
16676255Ssowmini 	FREE_SLIST(ilm->ilm_filter);
16686255Ssowmini 	FREE_SLIST(ilm->ilm_pendsrcs);
16696255Ssowmini 	FREE_SLIST(ilm->ilm_rtx.rtx_allow);
16706255Ssowmini 	FREE_SLIST(ilm->ilm_rtx.rtx_block);
16716255Ssowmini 	ilm->ilm_ipst = NULL;
16726255Ssowmini 	mi_free((char *)ilm);
16736255Ssowmini }
16746255Ssowmini 
16750Sstevel@tonic-gate /*
16760Sstevel@tonic-gate  * Unlink ilm and free it.
16770Sstevel@tonic-gate  */
16780Sstevel@tonic-gate static void
ilm_delete(ilm_t * ilm)16790Sstevel@tonic-gate ilm_delete(ilm_t *ilm)
16800Sstevel@tonic-gate {
168111042SErik.Nordmark@Sun.COM 	ill_t		*ill = ilm->ilm_ill;
16826255Ssowmini 	ilm_t		**ilmp;
16836255Ssowmini 	boolean_t	need_wakeup;
16846255Ssowmini 
16850Sstevel@tonic-gate 	/*
16860Sstevel@tonic-gate 	 * Delete under lock protection so that readers don't stumble
16870Sstevel@tonic-gate 	 * on bad ilm_next
16880Sstevel@tonic-gate 	 */
168911042SErik.Nordmark@Sun.COM 	ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock));
16900Sstevel@tonic-gate 
16910Sstevel@tonic-gate 	for (ilmp = &ill->ill_ilm; *ilmp != ilm; ilmp = &(*ilmp)->ilm_next)
169211042SErik.Nordmark@Sun.COM 		;
169311042SErik.Nordmark@Sun.COM 
16940Sstevel@tonic-gate 	*ilmp = ilm->ilm_next;
16956255Ssowmini 
169611042SErik.Nordmark@Sun.COM 	mutex_enter(&ill->ill_lock);
16976255Ssowmini 	/*
169811042SErik.Nordmark@Sun.COM 	 * if we are the last reference to the ill, we may need to wakeup any
169911042SErik.Nordmark@Sun.COM 	 * pending FREE or unplumb operations. This is because conn_update_ill
170011042SErik.Nordmark@Sun.COM 	 * bails if there is a ilg_delete_all in progress.
17016255Ssowmini 	 */
17026255Ssowmini 	need_wakeup = B_FALSE;
170311042SErik.Nordmark@Sun.COM 	DTRACE_PROBE3(ill__decr__cnt, (ill_t *), ill,
170411042SErik.Nordmark@Sun.COM 	    (char *), "ilm", (void *), ilm);
170511042SErik.Nordmark@Sun.COM 	ASSERT(ill->ill_ilm_cnt > 0);
170611042SErik.Nordmark@Sun.COM 	ill->ill_ilm_cnt--;
170711042SErik.Nordmark@Sun.COM 	if (ILL_FREE_OK(ill))
170811042SErik.Nordmark@Sun.COM 		need_wakeup = B_TRUE;
17096255Ssowmini 
17106255Ssowmini 	ilm_inactive(ilm); /* frees this ilm */
17116255Ssowmini 
17126255Ssowmini 	if (need_wakeup) {
17136255Ssowmini 		/* drops ill lock */
17146255Ssowmini 		ipif_ill_refrele_tail(ill);
17156255Ssowmini 	} else {
17166255Ssowmini 		mutex_exit(&ill->ill_lock);
17170Sstevel@tonic-gate 	}
17180Sstevel@tonic-gate }
17190Sstevel@tonic-gate 
17208485SPeter.Memishian@Sun.COM /*
172111042SErik.Nordmark@Sun.COM  * Lookup an ill based on the group, ifindex, ifaddr, and zoneid.
172211042SErik.Nordmark@Sun.COM  * Applies to both IPv4 and IPv6, although ifaddr is only used with
172311042SErik.Nordmark@Sun.COM  * IPv4.
172411042SErik.Nordmark@Sun.COM  * Returns an error for IS_UNDER_IPMP and VNI interfaces.
172511042SErik.Nordmark@Sun.COM  * On error it sets *errorp.
17268485SPeter.Memishian@Sun.COM  */
172711042SErik.Nordmark@Sun.COM static ill_t *
ill_mcast_lookup(const in6_addr_t * group,ipaddr_t ifaddr,uint_t ifindex,zoneid_t zoneid,ip_stack_t * ipst,int * errorp)172811042SErik.Nordmark@Sun.COM ill_mcast_lookup(const in6_addr_t *group, ipaddr_t ifaddr, uint_t ifindex,
172911042SErik.Nordmark@Sun.COM     zoneid_t zoneid, ip_stack_t *ipst, int *errorp)
17308485SPeter.Memishian@Sun.COM {
173111042SErik.Nordmark@Sun.COM 	ill_t *ill;
173211042SErik.Nordmark@Sun.COM 	ipaddr_t v4group;
173311042SErik.Nordmark@Sun.COM 
173411042SErik.Nordmark@Sun.COM 	if (IN6_IS_ADDR_V4MAPPED(group)) {
173511042SErik.Nordmark@Sun.COM 		IN6_V4MAPPED_TO_IPADDR(group, v4group);
173611042SErik.Nordmark@Sun.COM 
173711042SErik.Nordmark@Sun.COM 		if (ifindex != 0) {
173811042SErik.Nordmark@Sun.COM 			ill = ill_lookup_on_ifindex_zoneid(ifindex, zoneid,
173911042SErik.Nordmark@Sun.COM 			    B_FALSE, ipst);
174011042SErik.Nordmark@Sun.COM 		} else if (ifaddr != INADDR_ANY) {
174111042SErik.Nordmark@Sun.COM 			ipif_t *ipif;
174211042SErik.Nordmark@Sun.COM 
174311042SErik.Nordmark@Sun.COM 			ipif = ipif_lookup_addr(ifaddr, NULL, zoneid, ipst);
174411042SErik.Nordmark@Sun.COM 			if (ipif == NULL) {
174511042SErik.Nordmark@Sun.COM 				ill = NULL;
174611042SErik.Nordmark@Sun.COM 			} else {
174711042SErik.Nordmark@Sun.COM 				ill = ipif->ipif_ill;
174811042SErik.Nordmark@Sun.COM 				ill_refhold(ill);
174911042SErik.Nordmark@Sun.COM 				ipif_refrele(ipif);
175011042SErik.Nordmark@Sun.COM 			}
175111042SErik.Nordmark@Sun.COM 		} else {
175211042SErik.Nordmark@Sun.COM 			ill = ill_lookup_group_v4(v4group, zoneid, ipst, NULL,
175311042SErik.Nordmark@Sun.COM 			    NULL);
175411042SErik.Nordmark@Sun.COM 		}
175511042SErik.Nordmark@Sun.COM 	} else {
175611042SErik.Nordmark@Sun.COM 		if (ifindex != 0) {
175711042SErik.Nordmark@Sun.COM 			ill = ill_lookup_on_ifindex_zoneid(ifindex, zoneid,
175811042SErik.Nordmark@Sun.COM 			    B_TRUE, ipst);
175911042SErik.Nordmark@Sun.COM 		} else {
176011042SErik.Nordmark@Sun.COM 			ill = ill_lookup_group_v6(group, zoneid, ipst, NULL,
176111042SErik.Nordmark@Sun.COM 			    NULL);
176211042SErik.Nordmark@Sun.COM 		}
17638485SPeter.Memishian@Sun.COM 	}
176411042SErik.Nordmark@Sun.COM 	if (ill == NULL) {
176511042SErik.Nordmark@Sun.COM 		if (ifindex != 0)
176611042SErik.Nordmark@Sun.COM 			*errorp = ENXIO;
176711042SErik.Nordmark@Sun.COM 		else
176811042SErik.Nordmark@Sun.COM 			*errorp = EADDRNOTAVAIL;
176911042SErik.Nordmark@Sun.COM 		return (NULL);
177011042SErik.Nordmark@Sun.COM 	}
177111042SErik.Nordmark@Sun.COM 	/* operation not supported on the virtual network interface */
177211042SErik.Nordmark@Sun.COM 	if (IS_UNDER_IPMP(ill) || IS_VNI(ill)) {
177311042SErik.Nordmark@Sun.COM 		ill_refrele(ill);
177411042SErik.Nordmark@Sun.COM 		*errorp = EINVAL;
177511042SErik.Nordmark@Sun.COM 		return (NULL);
177611042SErik.Nordmark@Sun.COM 	}
177711042SErik.Nordmark@Sun.COM 	return (ill);
17788485SPeter.Memishian@Sun.COM }
17798485SPeter.Memishian@Sun.COM 
17808485SPeter.Memishian@Sun.COM /*
178111042SErik.Nordmark@Sun.COM  * Looks up the appropriate ill given an interface index (or interface address)
178211042SErik.Nordmark@Sun.COM  * and multicast group.  On success, returns 0, with *illpp pointing to the
178311042SErik.Nordmark@Sun.COM  * found struct.  On failure, returns an errno and *illpp is set to NULL.
178411042SErik.Nordmark@Sun.COM  *
178511042SErik.Nordmark@Sun.COM  * Returns an error for IS_UNDER_IPMP and VNI interfaces.
178611042SErik.Nordmark@Sun.COM  *
178711042SErik.Nordmark@Sun.COM  * Handles both IPv4 and IPv6. The ifaddr argument only applies in the
178811042SErik.Nordmark@Sun.COM  * case of IPv4.
17890Sstevel@tonic-gate  */
17900Sstevel@tonic-gate int
ip_opt_check(conn_t * connp,const in6_addr_t * v6group,const in6_addr_t * v6src,ipaddr_t ifaddr,uint_t ifindex,ill_t ** illpp)179111042SErik.Nordmark@Sun.COM ip_opt_check(conn_t *connp, const in6_addr_t *v6group,
179211042SErik.Nordmark@Sun.COM     const in6_addr_t *v6src, ipaddr_t ifaddr, uint_t ifindex, ill_t **illpp)
17930Sstevel@tonic-gate {
17940Sstevel@tonic-gate 	boolean_t src_unspec;
17950Sstevel@tonic-gate 	ill_t *ill = NULL;
17963448Sdh155122 	ip_stack_t *ipst = connp->conn_netstack->netstack_ip;
179711042SErik.Nordmark@Sun.COM 	int error = 0;
179811042SErik.Nordmark@Sun.COM 
179911042SErik.Nordmark@Sun.COM 	*illpp = NULL;
18000Sstevel@tonic-gate 
18010Sstevel@tonic-gate 	src_unspec = IN6_IS_ADDR_UNSPECIFIED(v6src);
18020Sstevel@tonic-gate 
18030Sstevel@tonic-gate 	if (IN6_IS_ADDR_V4MAPPED(v6group)) {
180411042SErik.Nordmark@Sun.COM 		ipaddr_t v4group;
180511042SErik.Nordmark@Sun.COM 		ipaddr_t v4src;
180611042SErik.Nordmark@Sun.COM 
18070Sstevel@tonic-gate 		if (!IN6_IS_ADDR_V4MAPPED(v6src) && !src_unspec)
18080Sstevel@tonic-gate 			return (EINVAL);
180911042SErik.Nordmark@Sun.COM 		IN6_V4MAPPED_TO_IPADDR(v6group, v4group);
18100Sstevel@tonic-gate 		if (src_unspec) {
181111042SErik.Nordmark@Sun.COM 			v4src = INADDR_ANY;
18120Sstevel@tonic-gate 		} else {
181311042SErik.Nordmark@Sun.COM 			IN6_V4MAPPED_TO_IPADDR(v6src, v4src);
18140Sstevel@tonic-gate 		}
181511042SErik.Nordmark@Sun.COM 		if (!CLASSD(v4group) || CLASSD(v4src))
18160Sstevel@tonic-gate 			return (EINVAL);
18170Sstevel@tonic-gate 	} else {
18180Sstevel@tonic-gate 		if (IN6_IS_ADDR_V4MAPPED(v6src) && !src_unspec)
18190Sstevel@tonic-gate 			return (EINVAL);
18200Sstevel@tonic-gate 		if (!IN6_IS_ADDR_MULTICAST(v6group) ||
18210Sstevel@tonic-gate 		    IN6_IS_ADDR_MULTICAST(v6src)) {
18220Sstevel@tonic-gate 			return (EINVAL);
18230Sstevel@tonic-gate 		}
18240Sstevel@tonic-gate 	}
18250Sstevel@tonic-gate 
182611042SErik.Nordmark@Sun.COM 	ill = ill_mcast_lookup(v6group, ifaddr, ifindex, IPCL_ZONEID(connp),
182711042SErik.Nordmark@Sun.COM 	    ipst, &error);
18280Sstevel@tonic-gate 	*illpp = ill;
182911042SErik.Nordmark@Sun.COM 	return (error);
18300Sstevel@tonic-gate }
18310Sstevel@tonic-gate 
18320Sstevel@tonic-gate static int
ip_get_srcfilter(conn_t * connp,struct group_filter * gf,struct ip_msfilter * imsf,const struct in6_addr * group,boolean_t issin6)18330Sstevel@tonic-gate ip_get_srcfilter(conn_t *connp, struct group_filter *gf,
183411042SErik.Nordmark@Sun.COM     struct ip_msfilter *imsf, const struct in6_addr *group, boolean_t issin6)
18350Sstevel@tonic-gate {
18360Sstevel@tonic-gate 	ilg_t *ilg;
18370Sstevel@tonic-gate 	int i, numsrc, fmode, outsrcs;
18380Sstevel@tonic-gate 	struct sockaddr_in *sin;
18390Sstevel@tonic-gate 	struct sockaddr_in6 *sin6;
18400Sstevel@tonic-gate 	struct in_addr *addrp;
18410Sstevel@tonic-gate 	slist_t *fp;
18420Sstevel@tonic-gate 	boolean_t is_v4only_api;
184311042SErik.Nordmark@Sun.COM 	ipaddr_t ifaddr;
184411042SErik.Nordmark@Sun.COM 	uint_t ifindex;
18450Sstevel@tonic-gate 
18460Sstevel@tonic-gate 	if (gf == NULL) {
18470Sstevel@tonic-gate 		ASSERT(imsf != NULL);
184811042SErik.Nordmark@Sun.COM 		ASSERT(!issin6);
18490Sstevel@tonic-gate 		is_v4only_api = B_TRUE;
18500Sstevel@tonic-gate 		outsrcs = imsf->imsf_numsrc;
185111042SErik.Nordmark@Sun.COM 		ifaddr = imsf->imsf_interface.s_addr;
185211042SErik.Nordmark@Sun.COM 		ifindex = 0;
18530Sstevel@tonic-gate 	} else {
18540Sstevel@tonic-gate 		ASSERT(imsf == NULL);
18550Sstevel@tonic-gate 		is_v4only_api = B_FALSE;
18560Sstevel@tonic-gate 		outsrcs = gf->gf_numsrc;
185711042SErik.Nordmark@Sun.COM 		ifaddr = INADDR_ANY;
185811042SErik.Nordmark@Sun.COM 		ifindex = gf->gf_interface;
185911042SErik.Nordmark@Sun.COM 	}
186011042SErik.Nordmark@Sun.COM 
186111042SErik.Nordmark@Sun.COM 	/* No need to use ill_mcast_serializer for the reader */
186211042SErik.Nordmark@Sun.COM 	rw_enter(&connp->conn_ilg_lock, RW_READER);
186311042SErik.Nordmark@Sun.COM 	ilg = ilg_lookup(connp, group, ifaddr, ifindex);
186411042SErik.Nordmark@Sun.COM 	if (ilg == NULL) {
186511042SErik.Nordmark@Sun.COM 		rw_exit(&connp->conn_ilg_lock);
186611042SErik.Nordmark@Sun.COM 		return (EADDRNOTAVAIL);
18670Sstevel@tonic-gate 	}
18680Sstevel@tonic-gate 
18690Sstevel@tonic-gate 	/*
18700Sstevel@tonic-gate 	 * In the kernel, we use the state definitions MODE_IS_[IN|EX]CLUDE
18710Sstevel@tonic-gate 	 * to identify the filter mode; but the API uses MCAST_[IN|EX]CLUDE.
18720Sstevel@tonic-gate 	 * So we need to translate here.
18730Sstevel@tonic-gate 	 */
18740Sstevel@tonic-gate 	fmode = (ilg->ilg_fmode == MODE_IS_INCLUDE) ?
18750Sstevel@tonic-gate 	    MCAST_INCLUDE : MCAST_EXCLUDE;
18760Sstevel@tonic-gate 	if ((fp = ilg->ilg_filter) == NULL) {
18770Sstevel@tonic-gate 		numsrc = 0;
18780Sstevel@tonic-gate 	} else {
18790Sstevel@tonic-gate 		for (i = 0; i < outsrcs; i++) {
18800Sstevel@tonic-gate 			if (i == fp->sl_numsrc)
18810Sstevel@tonic-gate 				break;
188211042SErik.Nordmark@Sun.COM 			if (issin6) {
18830Sstevel@tonic-gate 				sin6 = (struct sockaddr_in6 *)&gf->gf_slist[i];
18840Sstevel@tonic-gate 				sin6->sin6_family = AF_INET6;
18850Sstevel@tonic-gate 				sin6->sin6_addr = fp->sl_addr[i];
18860Sstevel@tonic-gate 			} else {
18870Sstevel@tonic-gate 				if (is_v4only_api) {
18880Sstevel@tonic-gate 					addrp = &imsf->imsf_slist[i];
18890Sstevel@tonic-gate 				} else {
18900Sstevel@tonic-gate 					sin = (struct sockaddr_in *)
18910Sstevel@tonic-gate 					    &gf->gf_slist[i];
18920Sstevel@tonic-gate 					sin->sin_family = AF_INET;
18930Sstevel@tonic-gate 					addrp = &sin->sin_addr;
18940Sstevel@tonic-gate 				}
18950Sstevel@tonic-gate 				IN6_V4MAPPED_TO_INADDR(&fp->sl_addr[i], addrp);
18960Sstevel@tonic-gate 			}
18970Sstevel@tonic-gate 		}
18980Sstevel@tonic-gate 		numsrc = fp->sl_numsrc;
18990Sstevel@tonic-gate 	}
19000Sstevel@tonic-gate 
19010Sstevel@tonic-gate 	if (is_v4only_api) {
19020Sstevel@tonic-gate 		imsf->imsf_numsrc = numsrc;
19030Sstevel@tonic-gate 		imsf->imsf_fmode = fmode;
19040Sstevel@tonic-gate 	} else {
19050Sstevel@tonic-gate 		gf->gf_numsrc = numsrc;
19060Sstevel@tonic-gate 		gf->gf_fmode = fmode;
19070Sstevel@tonic-gate 	}
19080Sstevel@tonic-gate 
190911042SErik.Nordmark@Sun.COM 	rw_exit(&connp->conn_ilg_lock);
19100Sstevel@tonic-gate 
19110Sstevel@tonic-gate 	return (0);
19120Sstevel@tonic-gate }
19130Sstevel@tonic-gate 
191411042SErik.Nordmark@Sun.COM /*
191511042SErik.Nordmark@Sun.COM  * Common for IPv4 and IPv6.
191611042SErik.Nordmark@Sun.COM  */
19170Sstevel@tonic-gate static int
ip_set_srcfilter(conn_t * connp,struct group_filter * gf,struct ip_msfilter * imsf,const struct in6_addr * group,ill_t * ill,boolean_t issin6)19180Sstevel@tonic-gate ip_set_srcfilter(conn_t *connp, struct group_filter *gf,
191911042SErik.Nordmark@Sun.COM     struct ip_msfilter *imsf, const struct in6_addr *group, ill_t *ill,
192011042SErik.Nordmark@Sun.COM     boolean_t issin6)
19210Sstevel@tonic-gate {
19220Sstevel@tonic-gate 	ilg_t *ilg;
19236827Sblu 	int i, err, infmode, new_fmode;
19246827Sblu 	uint_t insrcs;
19250Sstevel@tonic-gate 	struct sockaddr_in *sin;
19260Sstevel@tonic-gate 	struct sockaddr_in6 *sin6;
19270Sstevel@tonic-gate 	struct in_addr *addrp;
19280Sstevel@tonic-gate 	slist_t *orig_filter = NULL;
19290Sstevel@tonic-gate 	slist_t *new_filter = NULL;
19300Sstevel@tonic-gate 	mcast_record_t orig_fmode;
193111042SErik.Nordmark@Sun.COM 	boolean_t leave_group, is_v4only_api;
19320Sstevel@tonic-gate 	ilg_stat_t ilgstat;
193311042SErik.Nordmark@Sun.COM 	ilm_t *ilm;
193411042SErik.Nordmark@Sun.COM 	ipaddr_t ifaddr;
193511042SErik.Nordmark@Sun.COM 	uint_t ifindex;
19360Sstevel@tonic-gate 
19370Sstevel@tonic-gate 	if (gf == NULL) {
19380Sstevel@tonic-gate 		ASSERT(imsf != NULL);
193911042SErik.Nordmark@Sun.COM 		ASSERT(!issin6);
19400Sstevel@tonic-gate 		is_v4only_api = B_TRUE;
19410Sstevel@tonic-gate 		insrcs = imsf->imsf_numsrc;
19420Sstevel@tonic-gate 		infmode = imsf->imsf_fmode;
194311042SErik.Nordmark@Sun.COM 		ifaddr = imsf->imsf_interface.s_addr;
194411042SErik.Nordmark@Sun.COM 		ifindex = 0;
19450Sstevel@tonic-gate 	} else {
19460Sstevel@tonic-gate 		ASSERT(imsf == NULL);
19470Sstevel@tonic-gate 		is_v4only_api = B_FALSE;
19480Sstevel@tonic-gate 		insrcs = gf->gf_numsrc;
19490Sstevel@tonic-gate 		infmode = gf->gf_fmode;
195011042SErik.Nordmark@Sun.COM 		ifaddr = INADDR_ANY;
195111042SErik.Nordmark@Sun.COM 		ifindex = gf->gf_interface;
19520Sstevel@tonic-gate 	}
19530Sstevel@tonic-gate 
19540Sstevel@tonic-gate 	/* Make sure we can handle the source list */
19550Sstevel@tonic-gate 	if (insrcs > MAX_FILTER_SIZE)
19560Sstevel@tonic-gate 		return (ENOBUFS);
19570Sstevel@tonic-gate 
19580Sstevel@tonic-gate 	/*
19590Sstevel@tonic-gate 	 * setting the filter to (INCLUDE, NULL) is treated
19600Sstevel@tonic-gate 	 * as a request to leave the group.
19610Sstevel@tonic-gate 	 */
196211042SErik.Nordmark@Sun.COM 	leave_group = (infmode == MCAST_INCLUDE && insrcs == 0);
196311042SErik.Nordmark@Sun.COM 
196411042SErik.Nordmark@Sun.COM 	mutex_enter(&ill->ill_mcast_serializer);
196511042SErik.Nordmark@Sun.COM 	rw_enter(&connp->conn_ilg_lock, RW_WRITER);
196611042SErik.Nordmark@Sun.COM 	ilg = ilg_lookup(connp, group, ifaddr, ifindex);
19670Sstevel@tonic-gate 	if (ilg == NULL) {
19680Sstevel@tonic-gate 		/*
19690Sstevel@tonic-gate 		 * if the request was actually to leave, and we
19700Sstevel@tonic-gate 		 * didn't find an ilg, there's nothing to do.
19710Sstevel@tonic-gate 		 */
197211042SErik.Nordmark@Sun.COM 		if (leave_group) {
197311042SErik.Nordmark@Sun.COM 			rw_exit(&connp->conn_ilg_lock);
197411042SErik.Nordmark@Sun.COM 			mutex_exit(&ill->ill_mcast_serializer);
197511042SErik.Nordmark@Sun.COM 			return (0);
197611042SErik.Nordmark@Sun.COM 		}
197711042SErik.Nordmark@Sun.COM 		ilg = conn_ilg_alloc(connp, &err);
197811042SErik.Nordmark@Sun.COM 		if (ilg == NULL) {
197911042SErik.Nordmark@Sun.COM 			rw_exit(&connp->conn_ilg_lock);
198011042SErik.Nordmark@Sun.COM 			mutex_exit(&ill->ill_mcast_serializer);
198111042SErik.Nordmark@Sun.COM 			return (err);
19820Sstevel@tonic-gate 		}
19830Sstevel@tonic-gate 		ilgstat = ILGSTAT_NEW;
198411042SErik.Nordmark@Sun.COM 		ilg->ilg_v6group = *group;
198511042SErik.Nordmark@Sun.COM 		ilg->ilg_ill = ill;
198611042SErik.Nordmark@Sun.COM 		ilg->ilg_ifaddr = ifaddr;
198711042SErik.Nordmark@Sun.COM 		ilg->ilg_ifindex = ifindex;
198811042SErik.Nordmark@Sun.COM 	} else if (leave_group) {
198911042SErik.Nordmark@Sun.COM 		/*
199011042SErik.Nordmark@Sun.COM 		 * Make sure we have the correct serializer. The ill argument
199111042SErik.Nordmark@Sun.COM 		 * might not match ilg_ill.
199211042SErik.Nordmark@Sun.COM 		 */
199311042SErik.Nordmark@Sun.COM 		ilg_refhold(ilg);
199411042SErik.Nordmark@Sun.COM 		mutex_exit(&ill->ill_mcast_serializer);
199511042SErik.Nordmark@Sun.COM 		ill = ilg->ilg_ill;
199611042SErik.Nordmark@Sun.COM 		rw_exit(&connp->conn_ilg_lock);
199711042SErik.Nordmark@Sun.COM 
199811042SErik.Nordmark@Sun.COM 		mutex_enter(&ill->ill_mcast_serializer);
199911042SErik.Nordmark@Sun.COM 		rw_enter(&connp->conn_ilg_lock, RW_WRITER);
200011042SErik.Nordmark@Sun.COM 		ilm = ilg->ilg_ilm;
200111042SErik.Nordmark@Sun.COM 		ilg->ilg_ilm = NULL;
20020Sstevel@tonic-gate 		ilg_delete(connp, ilg, NULL);
200311042SErik.Nordmark@Sun.COM 		ilg_refrele(ilg);
200411042SErik.Nordmark@Sun.COM 		rw_exit(&connp->conn_ilg_lock);
200511042SErik.Nordmark@Sun.COM 		if (ilm != NULL)
200611042SErik.Nordmark@Sun.COM 			(void) ip_delmulti_serial(ilm, B_FALSE, B_TRUE);
200711042SErik.Nordmark@Sun.COM 		mutex_exit(&ill->ill_mcast_serializer);
200811463SSowmini.Varadhan@Sun.COM 		/*
200911463SSowmini.Varadhan@Sun.COM 		 * Now that all locks have been dropped, we can send any
201011463SSowmini.Varadhan@Sun.COM 		 * deferred/queued DLPI or IP packets
201111463SSowmini.Varadhan@Sun.COM 		 */
201211463SSowmini.Varadhan@Sun.COM 		ill_mcast_send_queued(ill);
201311463SSowmini.Varadhan@Sun.COM 		ill_dlpi_send_queued(ill);
20140Sstevel@tonic-gate 		return (0);
20150Sstevel@tonic-gate 	} else {
20160Sstevel@tonic-gate 		ilgstat = ILGSTAT_CHANGE;
20170Sstevel@tonic-gate 		/* Preserve existing state in case ip_addmulti() fails */
20180Sstevel@tonic-gate 		orig_fmode = ilg->ilg_fmode;
20190Sstevel@tonic-gate 		if (ilg->ilg_filter == NULL) {
20200Sstevel@tonic-gate 			orig_filter = NULL;
20210Sstevel@tonic-gate 		} else {
20220Sstevel@tonic-gate 			orig_filter = l_alloc_copy(ilg->ilg_filter);
20230Sstevel@tonic-gate 			if (orig_filter == NULL) {
202411042SErik.Nordmark@Sun.COM 				rw_exit(&connp->conn_ilg_lock);
202511042SErik.Nordmark@Sun.COM 				mutex_exit(&ill->ill_mcast_serializer);
20260Sstevel@tonic-gate 				return (ENOMEM);
20270Sstevel@tonic-gate 			}
20280Sstevel@tonic-gate 		}
20290Sstevel@tonic-gate 	}
20300Sstevel@tonic-gate 
20310Sstevel@tonic-gate 	/*
20320Sstevel@tonic-gate 	 * Alloc buffer to copy new state into (see below) before
20330Sstevel@tonic-gate 	 * we make any changes, so we can bail if it fails.
20340Sstevel@tonic-gate 	 */
20350Sstevel@tonic-gate 	if ((new_filter = l_alloc()) == NULL) {
203611042SErik.Nordmark@Sun.COM 		rw_exit(&connp->conn_ilg_lock);
20370Sstevel@tonic-gate 		err = ENOMEM;
20380Sstevel@tonic-gate 		goto free_and_exit;
20390Sstevel@tonic-gate 	}
20400Sstevel@tonic-gate 
20410Sstevel@tonic-gate 	if (insrcs == 0) {
20420Sstevel@tonic-gate 		CLEAR_SLIST(ilg->ilg_filter);
20430Sstevel@tonic-gate 	} else {
20440Sstevel@tonic-gate 		slist_t *fp;
20450Sstevel@tonic-gate 		if (ilg->ilg_filter == NULL) {
20460Sstevel@tonic-gate 			fp = l_alloc();
20470Sstevel@tonic-gate 			if (fp == NULL) {
20480Sstevel@tonic-gate 				if (ilgstat == ILGSTAT_NEW)
20490Sstevel@tonic-gate 					ilg_delete(connp, ilg, NULL);
205011042SErik.Nordmark@Sun.COM 				rw_exit(&connp->conn_ilg_lock);
20510Sstevel@tonic-gate 				err = ENOMEM;
20520Sstevel@tonic-gate 				goto free_and_exit;
20530Sstevel@tonic-gate 			}
20540Sstevel@tonic-gate 		} else {
20550Sstevel@tonic-gate 			fp = ilg->ilg_filter;
20560Sstevel@tonic-gate 		}
20570Sstevel@tonic-gate 		for (i = 0; i < insrcs; i++) {
205811042SErik.Nordmark@Sun.COM 			if (issin6) {
20590Sstevel@tonic-gate 				sin6 = (struct sockaddr_in6 *)&gf->gf_slist[i];
20600Sstevel@tonic-gate 				fp->sl_addr[i] = sin6->sin6_addr;
20610Sstevel@tonic-gate 			} else {
20620Sstevel@tonic-gate 				if (is_v4only_api) {
20630Sstevel@tonic-gate 					addrp = &imsf->imsf_slist[i];
20640Sstevel@tonic-gate 				} else {
20650Sstevel@tonic-gate 					sin = (struct sockaddr_in *)
20660Sstevel@tonic-gate 					    &gf->gf_slist[i];
20670Sstevel@tonic-gate 					addrp = &sin->sin_addr;
20680Sstevel@tonic-gate 				}
20690Sstevel@tonic-gate 				IN6_INADDR_TO_V4MAPPED(addrp, &fp->sl_addr[i]);
20700Sstevel@tonic-gate 			}
20710Sstevel@tonic-gate 		}
20720Sstevel@tonic-gate 		fp->sl_numsrc = insrcs;
20730Sstevel@tonic-gate 		ilg->ilg_filter = fp;
20740Sstevel@tonic-gate 	}
20750Sstevel@tonic-gate 	/*
20760Sstevel@tonic-gate 	 * In the kernel, we use the state definitions MODE_IS_[IN|EX]CLUDE
20770Sstevel@tonic-gate 	 * to identify the filter mode; but the API uses MCAST_[IN|EX]CLUDE.
20780Sstevel@tonic-gate 	 * So we need to translate here.
20790Sstevel@tonic-gate 	 */
20800Sstevel@tonic-gate 	ilg->ilg_fmode = (infmode == MCAST_INCLUDE) ?
20814459Skcpoon 	    MODE_IS_INCLUDE : MODE_IS_EXCLUDE;
20820Sstevel@tonic-gate 
20830Sstevel@tonic-gate 	/*
20840Sstevel@tonic-gate 	 * Save copy of ilg's filter state to pass to other functions,
208511042SErik.Nordmark@Sun.COM 	 * so we can release conn_ilg_lock now.
20860Sstevel@tonic-gate 	 */
20870Sstevel@tonic-gate 	new_fmode = ilg->ilg_fmode;
20880Sstevel@tonic-gate 	l_copy(ilg->ilg_filter, new_filter);
20890Sstevel@tonic-gate 
209011042SErik.Nordmark@Sun.COM 	rw_exit(&connp->conn_ilg_lock);
209111042SErik.Nordmark@Sun.COM 
209211042SErik.Nordmark@Sun.COM 	/*
209311042SErik.Nordmark@Sun.COM 	 * Now update the ill. We wait to do this until after the ilg
209411042SErik.Nordmark@Sun.COM 	 * has been updated because we need to update the src filter
209511042SErik.Nordmark@Sun.COM 	 * info for the ill, which involves looking at the status of
209611042SErik.Nordmark@Sun.COM 	 * all the ilgs associated with this group/interface pair.
209711042SErik.Nordmark@Sun.COM 	 */
209811042SErik.Nordmark@Sun.COM 	ilm = ip_addmulti_serial(group, ill, connp->conn_zoneid, ilgstat,
209911042SErik.Nordmark@Sun.COM 	    new_fmode, new_filter, &err);
210011042SErik.Nordmark@Sun.COM 
210111042SErik.Nordmark@Sun.COM 	rw_enter(&connp->conn_ilg_lock, RW_WRITER);
210211042SErik.Nordmark@Sun.COM 	/*
210311042SErik.Nordmark@Sun.COM 	 * Must look up the ilg again since we've not been holding
210411042SErik.Nordmark@Sun.COM 	 * conn_ilg_lock. The ilg could have disappeared due to an unplumb
210511042SErik.Nordmark@Sun.COM 	 * having called conn_update_ill, which can run once we dropped the
210611042SErik.Nordmark@Sun.COM 	 * conn_ilg_lock above.
210711042SErik.Nordmark@Sun.COM 	 */
210811042SErik.Nordmark@Sun.COM 	ilg = ilg_lookup(connp, group, ifaddr, ifindex);
210911042SErik.Nordmark@Sun.COM 	if (ilg == NULL) {
211011042SErik.Nordmark@Sun.COM 		rw_exit(&connp->conn_ilg_lock);
211111042SErik.Nordmark@Sun.COM 		if (ilm != NULL) {
211211042SErik.Nordmark@Sun.COM 			(void) ip_delmulti_serial(ilm, B_FALSE,
211311042SErik.Nordmark@Sun.COM 			    (ilgstat == ILGSTAT_NEW));
211411042SErik.Nordmark@Sun.COM 		}
211511042SErik.Nordmark@Sun.COM 		err = ENXIO;
211611042SErik.Nordmark@Sun.COM 		goto free_and_exit;
211711042SErik.Nordmark@Sun.COM 	}
211811042SErik.Nordmark@Sun.COM 
211911042SErik.Nordmark@Sun.COM 	if (ilm != NULL) {
212011463SSowmini.Varadhan@Sun.COM 		if (ilg->ilg_ill == NULL) {
212111463SSowmini.Varadhan@Sun.COM 			/* some other thread is re-attaching this.  */
212211463SSowmini.Varadhan@Sun.COM 			rw_exit(&connp->conn_ilg_lock);
212311463SSowmini.Varadhan@Sun.COM 			(void) ip_delmulti_serial(ilm, B_FALSE,
212411463SSowmini.Varadhan@Sun.COM 			    (ilgstat == ILGSTAT_NEW));
212511463SSowmini.Varadhan@Sun.COM 			err = 0;
212611463SSowmini.Varadhan@Sun.COM 			goto free_and_exit;
212711463SSowmini.Varadhan@Sun.COM 		}
212811042SErik.Nordmark@Sun.COM 		/* Succeeded. Update the ilg to point at the ilm */
212911042SErik.Nordmark@Sun.COM 		if (ilgstat == ILGSTAT_NEW) {
213011463SSowmini.Varadhan@Sun.COM 			if (ilg->ilg_ilm == NULL) {
213111463SSowmini.Varadhan@Sun.COM 				ilg->ilg_ilm = ilm;
213211463SSowmini.Varadhan@Sun.COM 				ilm->ilm_ifaddr = ifaddr; /* For netstat */
213311463SSowmini.Varadhan@Sun.COM 			} else {
213411463SSowmini.Varadhan@Sun.COM 				/* some other thread is re-attaching this. */
213511463SSowmini.Varadhan@Sun.COM 				rw_exit(&connp->conn_ilg_lock);
213611463SSowmini.Varadhan@Sun.COM 				(void) ip_delmulti_serial(ilm, B_FALSE, B_TRUE);
213711463SSowmini.Varadhan@Sun.COM 				err = 0;
213811463SSowmini.Varadhan@Sun.COM 				goto free_and_exit;
213911463SSowmini.Varadhan@Sun.COM 			}
214011042SErik.Nordmark@Sun.COM 		} else {
214111042SErik.Nordmark@Sun.COM 			/*
214211042SErik.Nordmark@Sun.COM 			 * ip_addmulti didn't get a held ilm for
214311042SErik.Nordmark@Sun.COM 			 * ILGSTAT_CHANGE; ilm_refcnt was unchanged.
214411042SErik.Nordmark@Sun.COM 			 */
214511042SErik.Nordmark@Sun.COM 			ASSERT(ilg->ilg_ilm == ilm);
214611042SErik.Nordmark@Sun.COM 		}
214711042SErik.Nordmark@Sun.COM 	} else {
214811042SErik.Nordmark@Sun.COM 		ASSERT(err != 0);
21490Sstevel@tonic-gate 		/*
215011042SErik.Nordmark@Sun.COM 		 * Failed to allocate the ilm.
21510Sstevel@tonic-gate 		 * Restore the original filter state, or delete the
215211042SErik.Nordmark@Sun.COM 		 * newly-created ilg.
215311042SErik.Nordmark@Sun.COM 		 * If ENETDOWN just clear ill_ilg since so that we
215411042SErik.Nordmark@Sun.COM 		 * will rejoin when the ill comes back; don't report ENETDOWN
215511042SErik.Nordmark@Sun.COM 		 * to application.
21560Sstevel@tonic-gate 		 */
21570Sstevel@tonic-gate 		if (ilgstat == ILGSTAT_NEW) {
215811042SErik.Nordmark@Sun.COM 			if (err == ENETDOWN) {
215911042SErik.Nordmark@Sun.COM 				ilg->ilg_ill = NULL;
216011042SErik.Nordmark@Sun.COM 				err = 0;
216111042SErik.Nordmark@Sun.COM 			} else {
216211042SErik.Nordmark@Sun.COM 				ilg_delete(connp, ilg, NULL);
216311042SErik.Nordmark@Sun.COM 			}
21640Sstevel@tonic-gate 		} else {
21650Sstevel@tonic-gate 			ilg->ilg_fmode = orig_fmode;
21660Sstevel@tonic-gate 			if (SLIST_IS_EMPTY(orig_filter)) {
21670Sstevel@tonic-gate 				CLEAR_SLIST(ilg->ilg_filter);
21680Sstevel@tonic-gate 			} else {
21690Sstevel@tonic-gate 				/*
21700Sstevel@tonic-gate 				 * We didn't free the filter, even if we
21710Sstevel@tonic-gate 				 * were trying to make the source list empty;
21720Sstevel@tonic-gate 				 * so if orig_filter isn't empty, the ilg
21730Sstevel@tonic-gate 				 * must still have a filter alloc'd.
21740Sstevel@tonic-gate 				 */
21750Sstevel@tonic-gate 				l_copy(orig_filter, ilg->ilg_filter);
21760Sstevel@tonic-gate 			}
21770Sstevel@tonic-gate 		}
21780Sstevel@tonic-gate 	}
217911042SErik.Nordmark@Sun.COM 	rw_exit(&connp->conn_ilg_lock);
21800Sstevel@tonic-gate 
21810Sstevel@tonic-gate free_and_exit:
218211042SErik.Nordmark@Sun.COM 	mutex_exit(&ill->ill_mcast_serializer);
218311463SSowmini.Varadhan@Sun.COM 	ill_mcast_send_queued(ill);
218411463SSowmini.Varadhan@Sun.COM 	ill_dlpi_send_queued(ill);
21850Sstevel@tonic-gate 	l_free(orig_filter);
21860Sstevel@tonic-gate 	l_free(new_filter);
21870Sstevel@tonic-gate 
21880Sstevel@tonic-gate 	return (err);
21890Sstevel@tonic-gate }
21900Sstevel@tonic-gate 
21910Sstevel@tonic-gate /*
21920Sstevel@tonic-gate  * Process the SIOC[GS]MSFILTER and SIOC[GS]IPMSFILTER ioctls.
21930Sstevel@tonic-gate  */
21940Sstevel@tonic-gate /* ARGSUSED */
21950Sstevel@tonic-gate int
ip_sioctl_msfilter(ipif_t * ipif,sin_t * dummy_sin,queue_t * q,mblk_t * mp,ip_ioctl_cmd_t * ipip,void * ifreq)21960Sstevel@tonic-gate ip_sioctl_msfilter(ipif_t *ipif, sin_t *dummy_sin, queue_t *q, mblk_t *mp,
21970Sstevel@tonic-gate     ip_ioctl_cmd_t *ipip, void *ifreq)
21980Sstevel@tonic-gate {
21990Sstevel@tonic-gate 	struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
22000Sstevel@tonic-gate 	/* existence verified in ip_wput_nondata() */
22010Sstevel@tonic-gate 	mblk_t *data_mp = mp->b_cont->b_cont;
22020Sstevel@tonic-gate 	int datalen, err, cmd, minsize;
22036827Sblu 	uint_t expsize = 0;
22040Sstevel@tonic-gate 	conn_t *connp;
22050Sstevel@tonic-gate 	boolean_t isv6, is_v4only_api, getcmd;
22060Sstevel@tonic-gate 	struct sockaddr_in *gsin;
22070Sstevel@tonic-gate 	struct sockaddr_in6 *gsin6;
220811042SErik.Nordmark@Sun.COM 	ipaddr_t v4group;
220911042SErik.Nordmark@Sun.COM 	in6_addr_t v6group;
22100Sstevel@tonic-gate 	struct group_filter *gf = NULL;
22110Sstevel@tonic-gate 	struct ip_msfilter *imsf = NULL;
22120Sstevel@tonic-gate 	mblk_t *ndp;
221311042SErik.Nordmark@Sun.COM 	ill_t *ill;
221411042SErik.Nordmark@Sun.COM 
221511042SErik.Nordmark@Sun.COM 	connp = Q_TO_CONN(q);
221611042SErik.Nordmark@Sun.COM 	err = ip_msfilter_ill(connp, mp, ipip, &ill);
221711042SErik.Nordmark@Sun.COM 	if (err != 0)
221811042SErik.Nordmark@Sun.COM 		return (err);
22190Sstevel@tonic-gate 
22200Sstevel@tonic-gate 	if (data_mp->b_cont != NULL) {
22210Sstevel@tonic-gate 		if ((ndp = msgpullup(data_mp, -1)) == NULL)
22220Sstevel@tonic-gate 			return (ENOMEM);
22230Sstevel@tonic-gate 		freemsg(data_mp);
22240Sstevel@tonic-gate 		data_mp = ndp;
22250Sstevel@tonic-gate 		mp->b_cont->b_cont = data_mp;
22260Sstevel@tonic-gate 	}
22270Sstevel@tonic-gate 
22280Sstevel@tonic-gate 	cmd = iocp->ioc_cmd;
22290Sstevel@tonic-gate 	getcmd = (cmd == SIOCGIPMSFILTER || cmd == SIOCGMSFILTER);
22300Sstevel@tonic-gate 	is_v4only_api = (cmd == SIOCGIPMSFILTER || cmd == SIOCSIPMSFILTER);
22310Sstevel@tonic-gate 	minsize = (is_v4only_api) ? IP_MSFILTER_SIZE(0) : GROUP_FILTER_SIZE(0);
22320Sstevel@tonic-gate 	datalen = MBLKL(data_mp);
22330Sstevel@tonic-gate 
22340Sstevel@tonic-gate 	if (datalen < minsize)
22350Sstevel@tonic-gate 		return (EINVAL);
22360Sstevel@tonic-gate 
22370Sstevel@tonic-gate 	/*
22380Sstevel@tonic-gate 	 * now we know we have at least have the initial structure,
22390Sstevel@tonic-gate 	 * but need to check for the source list array.
22400Sstevel@tonic-gate 	 */
22410Sstevel@tonic-gate 	if (is_v4only_api) {
22420Sstevel@tonic-gate 		imsf = (struct ip_msfilter *)data_mp->b_rptr;
22430Sstevel@tonic-gate 		isv6 = B_FALSE;
22440Sstevel@tonic-gate 		expsize = IP_MSFILTER_SIZE(imsf->imsf_numsrc);
22450Sstevel@tonic-gate 	} else {
22460Sstevel@tonic-gate 		gf = (struct group_filter *)data_mp->b_rptr;
22470Sstevel@tonic-gate 		if (gf->gf_group.ss_family == AF_INET6) {
22480Sstevel@tonic-gate 			gsin6 = (struct sockaddr_in6 *)&gf->gf_group;
22490Sstevel@tonic-gate 			isv6 = !(IN6_IS_ADDR_V4MAPPED(&gsin6->sin6_addr));
22500Sstevel@tonic-gate 		} else {
22510Sstevel@tonic-gate 			isv6 = B_FALSE;
22520Sstevel@tonic-gate 		}
22530Sstevel@tonic-gate 		expsize = GROUP_FILTER_SIZE(gf->gf_numsrc);
22540Sstevel@tonic-gate 	}
22550Sstevel@tonic-gate 	if (datalen < expsize)
22560Sstevel@tonic-gate 		return (EINVAL);
22570Sstevel@tonic-gate 
22580Sstevel@tonic-gate 	if (isv6) {
22590Sstevel@tonic-gate 		gsin6 = (struct sockaddr_in6 *)&gf->gf_group;
226011042SErik.Nordmark@Sun.COM 		v6group = gsin6->sin6_addr;
226111042SErik.Nordmark@Sun.COM 		if (getcmd) {
226211042SErik.Nordmark@Sun.COM 			err = ip_get_srcfilter(connp, gf, NULL, &v6group,
226311042SErik.Nordmark@Sun.COM 			    B_TRUE);
226411042SErik.Nordmark@Sun.COM 		} else {
226511042SErik.Nordmark@Sun.COM 			err = ip_set_srcfilter(connp, gf, NULL, &v6group, ill,
226611042SErik.Nordmark@Sun.COM 			    B_TRUE);
226711042SErik.Nordmark@Sun.COM 		}
22680Sstevel@tonic-gate 	} else {
226911042SErik.Nordmark@Sun.COM 		boolean_t issin6 = B_FALSE;
22700Sstevel@tonic-gate 		if (is_v4only_api) {
227111042SErik.Nordmark@Sun.COM 			v4group = (ipaddr_t)imsf->imsf_multiaddr.s_addr;
227211042SErik.Nordmark@Sun.COM 			IN6_IPADDR_TO_V4MAPPED(v4group, &v6group);
22730Sstevel@tonic-gate 		} else {
22740Sstevel@tonic-gate 			if (gf->gf_group.ss_family == AF_INET) {
22750Sstevel@tonic-gate 				gsin = (struct sockaddr_in *)&gf->gf_group;
227611042SErik.Nordmark@Sun.COM 				v4group = (ipaddr_t)gsin->sin_addr.s_addr;
227711042SErik.Nordmark@Sun.COM 				IN6_IPADDR_TO_V4MAPPED(v4group, &v6group);
22780Sstevel@tonic-gate 			} else {
22790Sstevel@tonic-gate 				gsin6 = (struct sockaddr_in6 *)&gf->gf_group;
22800Sstevel@tonic-gate 				IN6_V4MAPPED_TO_IPADDR(&gsin6->sin6_addr,
228111042SErik.Nordmark@Sun.COM 				    v4group);
228211042SErik.Nordmark@Sun.COM 				issin6 = B_TRUE;
22830Sstevel@tonic-gate 			}
22840Sstevel@tonic-gate 		}
228511042SErik.Nordmark@Sun.COM 		/*
228611042SErik.Nordmark@Sun.COM 		 * INADDR_ANY is represented as the IPv6 unspecifed addr.
228711042SErik.Nordmark@Sun.COM 		 */
228811042SErik.Nordmark@Sun.COM 		if (v4group == INADDR_ANY)
228911042SErik.Nordmark@Sun.COM 			v6group = ipv6_all_zeros;
22900Sstevel@tonic-gate 		else
229111042SErik.Nordmark@Sun.COM 			IN6_IPADDR_TO_V4MAPPED(v4group, &v6group);
229211042SErik.Nordmark@Sun.COM 
229311042SErik.Nordmark@Sun.COM 		if (getcmd) {
229411042SErik.Nordmark@Sun.COM 			err = ip_get_srcfilter(connp, gf, imsf, &v6group,
229511042SErik.Nordmark@Sun.COM 			    issin6);
229611042SErik.Nordmark@Sun.COM 		} else {
229711042SErik.Nordmark@Sun.COM 			err = ip_set_srcfilter(connp, gf, imsf, &v6group, ill,
229811042SErik.Nordmark@Sun.COM 			    issin6);
229911042SErik.Nordmark@Sun.COM 		}
23000Sstevel@tonic-gate 	}
230111042SErik.Nordmark@Sun.COM 	ill_refrele(ill);
23020Sstevel@tonic-gate 
23030Sstevel@tonic-gate 	return (err);
23040Sstevel@tonic-gate }
23050Sstevel@tonic-gate 
23060Sstevel@tonic-gate /*
230711042SErik.Nordmark@Sun.COM  * Determine the ill for the SIOC*MSFILTER ioctls
230811042SErik.Nordmark@Sun.COM  *
230911042SErik.Nordmark@Sun.COM  * Returns an error for IS_UNDER_IPMP interfaces.
231011042SErik.Nordmark@Sun.COM  *
231111042SErik.Nordmark@Sun.COM  * Finds the ill based on information in the ioctl headers.
23120Sstevel@tonic-gate  */
231311042SErik.Nordmark@Sun.COM static int
ip_msfilter_ill(conn_t * connp,mblk_t * mp,const ip_ioctl_cmd_t * ipip,ill_t ** illp)231411042SErik.Nordmark@Sun.COM ip_msfilter_ill(conn_t *connp, mblk_t *mp, const ip_ioctl_cmd_t *ipip,
231511042SErik.Nordmark@Sun.COM     ill_t **illp)
23160Sstevel@tonic-gate {
23174972Smeem 	int cmd = ipip->ipi_cmd;
23184972Smeem 	int err = 0;
231911042SErik.Nordmark@Sun.COM 	ill_t *ill;
23200Sstevel@tonic-gate 	/* caller has verified this mblk exists */
23210Sstevel@tonic-gate 	char *dbuf = (char *)mp->b_cont->b_cont->b_rptr;
23220Sstevel@tonic-gate 	struct ip_msfilter *imsf;
23230Sstevel@tonic-gate 	struct group_filter *gf;
232411042SErik.Nordmark@Sun.COM 	ipaddr_t v4addr, v4group;
232511042SErik.Nordmark@Sun.COM 	in6_addr_t v6group;
23260Sstevel@tonic-gate 	uint32_t index;
23273448Sdh155122 	ip_stack_t *ipst;
23280Sstevel@tonic-gate 
23293448Sdh155122 	ipst = connp->conn_netstack->netstack_ip;
23300Sstevel@tonic-gate 
233111042SErik.Nordmark@Sun.COM 	*illp = NULL;
233211042SErik.Nordmark@Sun.COM 
23330Sstevel@tonic-gate 	/* don't allow multicast operations on a tcp conn */
2334741Smasputra 	if (IPCL_IS_TCP(connp))
23350Sstevel@tonic-gate 		return (ENOPROTOOPT);
23360Sstevel@tonic-gate 
23370Sstevel@tonic-gate 	if (cmd == SIOCSIPMSFILTER || cmd == SIOCGIPMSFILTER) {
23380Sstevel@tonic-gate 		/* don't allow v4-specific ioctls on v6 socket */
233911042SErik.Nordmark@Sun.COM 		if (connp->conn_family == AF_INET6)
23400Sstevel@tonic-gate 			return (EAFNOSUPPORT);
23410Sstevel@tonic-gate 
23420Sstevel@tonic-gate 		imsf = (struct ip_msfilter *)dbuf;
23430Sstevel@tonic-gate 		v4addr = imsf->imsf_interface.s_addr;
234411042SErik.Nordmark@Sun.COM 		v4group = imsf->imsf_multiaddr.s_addr;
234511042SErik.Nordmark@Sun.COM 		IN6_IPADDR_TO_V4MAPPED(v4group, &v6group);
234611042SErik.Nordmark@Sun.COM 		ill = ill_mcast_lookup(&v6group, v4addr, 0, IPCL_ZONEID(connp),
234711042SErik.Nordmark@Sun.COM 		    ipst, &err);
234811042SErik.Nordmark@Sun.COM 		if (ill == NULL && v4addr != INADDR_ANY)
234911042SErik.Nordmark@Sun.COM 			err = ENXIO;
23500Sstevel@tonic-gate 	} else {
23510Sstevel@tonic-gate 		gf = (struct group_filter *)dbuf;
23520Sstevel@tonic-gate 		index = gf->gf_interface;
23530Sstevel@tonic-gate 		if (gf->gf_group.ss_family == AF_INET6) {
23540Sstevel@tonic-gate 			struct sockaddr_in6 *sin6;
235511042SErik.Nordmark@Sun.COM 
23560Sstevel@tonic-gate 			sin6 = (struct sockaddr_in6 *)&gf->gf_group;
235711042SErik.Nordmark@Sun.COM 			v6group = sin6->sin6_addr;
23580Sstevel@tonic-gate 		} else if (gf->gf_group.ss_family == AF_INET) {
23590Sstevel@tonic-gate 			struct sockaddr_in *sin;
236011042SErik.Nordmark@Sun.COM 
23610Sstevel@tonic-gate 			sin = (struct sockaddr_in *)&gf->gf_group;
236211042SErik.Nordmark@Sun.COM 			v4group = sin->sin_addr.s_addr;
236311042SErik.Nordmark@Sun.COM 			IN6_IPADDR_TO_V4MAPPED(v4group, &v6group);
23640Sstevel@tonic-gate 		} else {
23650Sstevel@tonic-gate 			return (EAFNOSUPPORT);
23660Sstevel@tonic-gate 		}
236711042SErik.Nordmark@Sun.COM 		ill = ill_mcast_lookup(&v6group, INADDR_ANY, index,
236811042SErik.Nordmark@Sun.COM 		    IPCL_ZONEID(connp), ipst, &err);
23690Sstevel@tonic-gate 	}
237011042SErik.Nordmark@Sun.COM 	*illp = ill;
23710Sstevel@tonic-gate 	return (err);
23720Sstevel@tonic-gate }
23730Sstevel@tonic-gate 
23740Sstevel@tonic-gate /*
23750Sstevel@tonic-gate  * The structures used for the SIOC*MSFILTER ioctls usually must be copied
23760Sstevel@tonic-gate  * in in two stages, as the first copyin tells us the size of the attached
23770Sstevel@tonic-gate  * source buffer.  This function is called by ip_wput_nondata() after the
23780Sstevel@tonic-gate  * first copyin has completed; it figures out how big the second stage
23790Sstevel@tonic-gate  * needs to be, and kicks it off.
23800Sstevel@tonic-gate  *
23810Sstevel@tonic-gate  * In some cases (numsrc < 2), the second copyin is not needed as the
23820Sstevel@tonic-gate  * first one gets a complete structure containing 1 source addr.
23830Sstevel@tonic-gate  *
23840Sstevel@tonic-gate  * The function returns 0 if a second copyin has been started (i.e. there's
23850Sstevel@tonic-gate  * no more work to be done right now), or 1 if the second copyin is not
23860Sstevel@tonic-gate  * needed and ip_wput_nondata() can continue its processing.
23870Sstevel@tonic-gate  */
23880Sstevel@tonic-gate int
ip_copyin_msfilter(queue_t * q,mblk_t * mp)23890Sstevel@tonic-gate ip_copyin_msfilter(queue_t *q, mblk_t *mp)
23900Sstevel@tonic-gate {
23910Sstevel@tonic-gate 	struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
23920Sstevel@tonic-gate 	int cmd = iocp->ioc_cmd;
23930Sstevel@tonic-gate 	/* validity of this checked in ip_wput_nondata() */
23940Sstevel@tonic-gate 	mblk_t *mp1 = mp->b_cont->b_cont;
23950Sstevel@tonic-gate 	int copysize = 0;
23960Sstevel@tonic-gate 	int offset;
23970Sstevel@tonic-gate 
23980Sstevel@tonic-gate 	if (cmd == SIOCSMSFILTER || cmd == SIOCGMSFILTER) {
23990Sstevel@tonic-gate 		struct group_filter *gf = (struct group_filter *)mp1->b_rptr;
24000Sstevel@tonic-gate 		if (gf->gf_numsrc >= 2) {
24010Sstevel@tonic-gate 			offset = sizeof (struct group_filter);
24020Sstevel@tonic-gate 			copysize = GROUP_FILTER_SIZE(gf->gf_numsrc) - offset;
24030Sstevel@tonic-gate 		}
24040Sstevel@tonic-gate 	} else {
24050Sstevel@tonic-gate 		struct ip_msfilter *imsf = (struct ip_msfilter *)mp1->b_rptr;
24060Sstevel@tonic-gate 		if (imsf->imsf_numsrc >= 2) {
24070Sstevel@tonic-gate 			offset = sizeof (struct ip_msfilter);
24080Sstevel@tonic-gate 			copysize = IP_MSFILTER_SIZE(imsf->imsf_numsrc) - offset;
24090Sstevel@tonic-gate 		}
24100Sstevel@tonic-gate 	}
24110Sstevel@tonic-gate 	if (copysize > 0) {
24120Sstevel@tonic-gate 		mi_copyin_n(q, mp, offset, copysize);
24130Sstevel@tonic-gate 		return (0);
24140Sstevel@tonic-gate 	}
24150Sstevel@tonic-gate 	return (1);
24160Sstevel@tonic-gate }
24170Sstevel@tonic-gate 
24180Sstevel@tonic-gate /*
24190Sstevel@tonic-gate  * Handle the following optmgmt:
24200Sstevel@tonic-gate  *	IP_ADD_MEMBERSHIP		must not have joined already
242111042SErik.Nordmark@Sun.COM  *	IPV6_JOIN_GROUP			must not have joined already
24220Sstevel@tonic-gate  *	MCAST_JOIN_GROUP		must not have joined already
24230Sstevel@tonic-gate  *	IP_BLOCK_SOURCE			must have joined already
24240Sstevel@tonic-gate  *	MCAST_BLOCK_SOURCE		must have joined already
24250Sstevel@tonic-gate  *	IP_JOIN_SOURCE_GROUP		may have joined already
24260Sstevel@tonic-gate  *	MCAST_JOIN_SOURCE_GROUP		may have joined already
24270Sstevel@tonic-gate  *
24280Sstevel@tonic-gate  * fmode and src parameters may be used to determine which option is
242911042SErik.Nordmark@Sun.COM  * being set, as follows (IPV6_JOIN_GROUP and MCAST_JOIN_GROUP options
24300Sstevel@tonic-gate  * are functionally equivalent):
243111042SErik.Nordmark@Sun.COM  *	opt			fmode			v6src
243211042SErik.Nordmark@Sun.COM  *	IP_ADD_MEMBERSHIP	MODE_IS_EXCLUDE		unspecified
243311042SErik.Nordmark@Sun.COM  *	IPV6_JOIN_GROUP		MODE_IS_EXCLUDE		unspecified
243411042SErik.Nordmark@Sun.COM  *	MCAST_JOIN_GROUP	MODE_IS_EXCLUDE		unspecified
243511042SErik.Nordmark@Sun.COM  *	IP_BLOCK_SOURCE		MODE_IS_EXCLUDE		IPv4-mapped addr
243611042SErik.Nordmark@Sun.COM  *	MCAST_BLOCK_SOURCE	MODE_IS_EXCLUDE		v6 addr
243711042SErik.Nordmark@Sun.COM  *	IP_JOIN_SOURCE_GROUP	MODE_IS_INCLUDE		IPv4-mapped addr
243811042SErik.Nordmark@Sun.COM  *	MCAST_JOIN_SOURCE_GROUP	MODE_IS_INCLUDE		v6 addr
24390Sstevel@tonic-gate  *
24400Sstevel@tonic-gate  * Changing the filter mode is not allowed; if a matching ilg already
24410Sstevel@tonic-gate  * exists and fmode != ilg->ilg_fmode, EINVAL is returned.
24420Sstevel@tonic-gate  *
24430Sstevel@tonic-gate  * Verifies that there is a source address of appropriate scope for
24440Sstevel@tonic-gate  * the group; if not, EADDRNOTAVAIL is returned.
24450Sstevel@tonic-gate  *
244611042SErik.Nordmark@Sun.COM  * The interface to be used may be identified by an IPv4 address or by an
244711042SErik.Nordmark@Sun.COM  * interface index.
244811042SErik.Nordmark@Sun.COM  *
244911042SErik.Nordmark@Sun.COM  * Handles IPv4-mapped IPv6 multicast addresses by associating them
245011042SErik.Nordmark@Sun.COM  * with the IPv4 address.  Assumes that if v6group is v4-mapped,
245111042SErik.Nordmark@Sun.COM  * v6src is also v4-mapped.
24520Sstevel@tonic-gate  */
24530Sstevel@tonic-gate int
ip_opt_add_group(conn_t * connp,boolean_t checkonly,const in6_addr_t * v6group,ipaddr_t ifaddr,uint_t ifindex,mcast_record_t fmode,const in6_addr_t * v6src)245411042SErik.Nordmark@Sun.COM ip_opt_add_group(conn_t *connp, boolean_t checkonly,
245511042SErik.Nordmark@Sun.COM     const in6_addr_t *v6group, ipaddr_t ifaddr, uint_t ifindex,
245611042SErik.Nordmark@Sun.COM     mcast_record_t fmode, const in6_addr_t *v6src)
24570Sstevel@tonic-gate {
245811042SErik.Nordmark@Sun.COM 	ill_t *ill;
245911042SErik.Nordmark@Sun.COM 	char buf[INET6_ADDRSTRLEN];
246011042SErik.Nordmark@Sun.COM 	int	err;
246111042SErik.Nordmark@Sun.COM 
246211042SErik.Nordmark@Sun.COM 	err = ip_opt_check(connp, v6group, v6src, ifaddr, ifindex, &ill);
24630Sstevel@tonic-gate 	if (err != 0) {
246411042SErik.Nordmark@Sun.COM 		ip1dbg(("ip_opt_add_group: no ill for group %s/"
246511042SErik.Nordmark@Sun.COM 		    "index %d\n", inet_ntop(AF_INET6, v6group, buf,
246611042SErik.Nordmark@Sun.COM 		    sizeof (buf)), ifindex));
24670Sstevel@tonic-gate 		return (err);
24680Sstevel@tonic-gate 	}
24690Sstevel@tonic-gate 
24700Sstevel@tonic-gate 	if (checkonly) {
24710Sstevel@tonic-gate 		/*
24720Sstevel@tonic-gate 		 * do not do operation, just pretend to - new T_CHECK
24730Sstevel@tonic-gate 		 * semantics. The error return case above if encountered
24740Sstevel@tonic-gate 		 * considered a good enough "check" here.
24750Sstevel@tonic-gate 		 */
247611042SErik.Nordmark@Sun.COM 		ill_refrele(ill);
24770Sstevel@tonic-gate 		return (0);
24780Sstevel@tonic-gate 	}
247911042SErik.Nordmark@Sun.COM 	mutex_enter(&ill->ill_mcast_serializer);
248011463SSowmini.Varadhan@Sun.COM 	/*
248111463SSowmini.Varadhan@Sun.COM 	 * Multicast groups may not be joined on interfaces that are either
248211463SSowmini.Varadhan@Sun.COM 	 * already underlying interfaces in an IPMP group, or in the process
248311463SSowmini.Varadhan@Sun.COM 	 * of joining the IPMP group. The latter condition is enforced by
248411463SSowmini.Varadhan@Sun.COM 	 * checking the value of ill->ill_grp_pending under the
248511463SSowmini.Varadhan@Sun.COM 	 * ill_mcast_serializer lock.  We cannot serialize the
248611463SSowmini.Varadhan@Sun.COM 	 * ill_grp_pending check on the ill_g_lock across ilg_add() because
248711463SSowmini.Varadhan@Sun.COM 	 *  ill_mcast_send_queued -> ip_output_simple -> ill_lookup_on_ifindex
248811463SSowmini.Varadhan@Sun.COM 	 * will take the ill_g_lock itself. Instead, we hold the
248911463SSowmini.Varadhan@Sun.COM 	 * ill_mcast_serializer.
249011463SSowmini.Varadhan@Sun.COM 	 */
249111463SSowmini.Varadhan@Sun.COM 	if (ill->ill_grp_pending || IS_UNDER_IPMP(ill)) {
249211463SSowmini.Varadhan@Sun.COM 		DTRACE_PROBE2(group__add__on__under, ill_t *, ill,
249311463SSowmini.Varadhan@Sun.COM 		    in6_addr_t *, v6group);
249411463SSowmini.Varadhan@Sun.COM 		mutex_exit(&ill->ill_mcast_serializer);
249511463SSowmini.Varadhan@Sun.COM 		ill_refrele(ill);
249611463SSowmini.Varadhan@Sun.COM 		return (EADDRNOTAVAIL);
249711463SSowmini.Varadhan@Sun.COM 	}
249811042SErik.Nordmark@Sun.COM 	err = ilg_add(connp, v6group, ifaddr, ifindex, ill, fmode, v6src);
249911042SErik.Nordmark@Sun.COM 	mutex_exit(&ill->ill_mcast_serializer);
250011463SSowmini.Varadhan@Sun.COM 	/*
250111463SSowmini.Varadhan@Sun.COM 	 * We have done an addmulti_impl and/or delmulti_impl.
250211463SSowmini.Varadhan@Sun.COM 	 * All locks have been dropped, we can send any
250311463SSowmini.Varadhan@Sun.COM 	 * deferred/queued DLPI or IP packets
250411463SSowmini.Varadhan@Sun.COM 	 */
250511463SSowmini.Varadhan@Sun.COM 	ill_mcast_send_queued(ill);
250611463SSowmini.Varadhan@Sun.COM 	ill_dlpi_send_queued(ill);
250711042SErik.Nordmark@Sun.COM 	ill_refrele(ill);
25080Sstevel@tonic-gate 	return (err);
25090Sstevel@tonic-gate }
25100Sstevel@tonic-gate 
25110Sstevel@tonic-gate /*
251211042SErik.Nordmark@Sun.COM  * Common for IPv6 and IPv4.
251311042SErik.Nordmark@Sun.COM  * Here we handle ilgs that are still attached to their original ill
251411042SErik.Nordmark@Sun.COM  * (the one ifaddr/ifindex points at), as well as detached ones.
251511042SErik.Nordmark@Sun.COM  * The detached ones might have been attached to some other ill.
25160Sstevel@tonic-gate  */
251711042SErik.Nordmark@Sun.COM static int
ip_opt_delete_group_excl(conn_t * connp,const in6_addr_t * v6group,ipaddr_t ifaddr,uint_t ifindex,mcast_record_t fmode,const in6_addr_t * v6src)251811042SErik.Nordmark@Sun.COM ip_opt_delete_group_excl(conn_t *connp, const in6_addr_t *v6group,
251911042SErik.Nordmark@Sun.COM     ipaddr_t ifaddr, uint_t ifindex, mcast_record_t fmode,
252011042SErik.Nordmark@Sun.COM     const in6_addr_t *v6src)
25210Sstevel@tonic-gate {
252211042SErik.Nordmark@Sun.COM 	ilg_t	*ilg;
252311042SErik.Nordmark@Sun.COM 	boolean_t leaving;
252411042SErik.Nordmark@Sun.COM 	ilm_t *ilm;
25250Sstevel@tonic-gate 	ill_t *ill;
252611042SErik.Nordmark@Sun.COM 	int err = 0;
252711042SErik.Nordmark@Sun.COM 
252811042SErik.Nordmark@Sun.COM retry:
252911042SErik.Nordmark@Sun.COM 	rw_enter(&connp->conn_ilg_lock, RW_WRITER);
253011042SErik.Nordmark@Sun.COM 	ilg = ilg_lookup(connp, v6group, ifaddr, ifindex);
253111042SErik.Nordmark@Sun.COM 	if (ilg == NULL) {
253211042SErik.Nordmark@Sun.COM 		rw_exit(&connp->conn_ilg_lock);
253311042SErik.Nordmark@Sun.COM 		/*
253411042SErik.Nordmark@Sun.COM 		 * Since we didn't have any ilg we now do the error checks
253511042SErik.Nordmark@Sun.COM 		 * to determine the best errno.
253611042SErik.Nordmark@Sun.COM 		 */
253711042SErik.Nordmark@Sun.COM 		err = ip_opt_check(connp, v6group, v6src, ifaddr, ifindex,
253811042SErik.Nordmark@Sun.COM 		    &ill);
253911042SErik.Nordmark@Sun.COM 		if (ill != NULL) {
254011042SErik.Nordmark@Sun.COM 			/* The only error was a missing ilg for the group */
254111042SErik.Nordmark@Sun.COM 			ill_refrele(ill);
254211042SErik.Nordmark@Sun.COM 			err = EADDRNOTAVAIL;
25430Sstevel@tonic-gate 		}
25440Sstevel@tonic-gate 		return (err);
25450Sstevel@tonic-gate 	}
254611042SErik.Nordmark@Sun.COM 
254711042SErik.Nordmark@Sun.COM 	/* If the ilg is attached then we serialize using that ill */
254811042SErik.Nordmark@Sun.COM 	ill = ilg->ilg_ill;
254911042SErik.Nordmark@Sun.COM 	if (ill != NULL) {
255011042SErik.Nordmark@Sun.COM 		/* Prevent the ill and ilg from being freed */
255111042SErik.Nordmark@Sun.COM 		ill_refhold(ill);
255211042SErik.Nordmark@Sun.COM 		ilg_refhold(ilg);
255311042SErik.Nordmark@Sun.COM 		rw_exit(&connp->conn_ilg_lock);
255411042SErik.Nordmark@Sun.COM 		mutex_enter(&ill->ill_mcast_serializer);
255511042SErik.Nordmark@Sun.COM 		rw_enter(&connp->conn_ilg_lock, RW_WRITER);
255611042SErik.Nordmark@Sun.COM 		if (ilg->ilg_condemned) {
255711042SErik.Nordmark@Sun.COM 			/* Disappeared */
255811042SErik.Nordmark@Sun.COM 			ilg_refrele(ilg);
255911042SErik.Nordmark@Sun.COM 			rw_exit(&connp->conn_ilg_lock);
256011042SErik.Nordmark@Sun.COM 			mutex_exit(&ill->ill_mcast_serializer);
25610Sstevel@tonic-gate 			ill_refrele(ill);
256211042SErik.Nordmark@Sun.COM 			goto retry;
256311042SErik.Nordmark@Sun.COM 		}
25640Sstevel@tonic-gate 	}
25650Sstevel@tonic-gate 
25660Sstevel@tonic-gate 	/*
25670Sstevel@tonic-gate 	 * Decide if we're actually deleting the ilg or just removing a
25680Sstevel@tonic-gate 	 * source filter address; if just removing an addr, make sure we
25690Sstevel@tonic-gate 	 * aren't trying to change the filter mode, and that the addr is
25700Sstevel@tonic-gate 	 * actually in our filter list already.  If we're removing the
25710Sstevel@tonic-gate 	 * last src in an include list, just delete the ilg.
25720Sstevel@tonic-gate 	 */
257311042SErik.Nordmark@Sun.COM 	if (IN6_IS_ADDR_UNSPECIFIED(v6src)) {
25740Sstevel@tonic-gate 		leaving = B_TRUE;
25750Sstevel@tonic-gate 	} else {
25760Sstevel@tonic-gate 		if (fmode != ilg->ilg_fmode)
25770Sstevel@tonic-gate 			err = EINVAL;
25780Sstevel@tonic-gate 		else if (ilg->ilg_filter == NULL ||
25790Sstevel@tonic-gate 		    !list_has_addr(ilg->ilg_filter, v6src))
25800Sstevel@tonic-gate 			err = EADDRNOTAVAIL;
25810Sstevel@tonic-gate 		if (err != 0) {
258211042SErik.Nordmark@Sun.COM 			if (ill != NULL)
258311042SErik.Nordmark@Sun.COM 				ilg_refrele(ilg);
258411042SErik.Nordmark@Sun.COM 			rw_exit(&connp->conn_ilg_lock);
258511042SErik.Nordmark@Sun.COM 			goto done;
25860Sstevel@tonic-gate 		}
25870Sstevel@tonic-gate 		if (fmode == MODE_IS_INCLUDE &&
258811042SErik.Nordmark@Sun.COM 		    ilg->ilg_filter->sl_numsrc == 1) {
258911042SErik.Nordmark@Sun.COM 			leaving = B_TRUE;
25900Sstevel@tonic-gate 			v6src = NULL;
259111042SErik.Nordmark@Sun.COM 		} else {
25920Sstevel@tonic-gate 			leaving = B_FALSE;
259311042SErik.Nordmark@Sun.COM 		}
25940Sstevel@tonic-gate 	}
259511042SErik.Nordmark@Sun.COM 	ilm = ilg->ilg_ilm;
259611042SErik.Nordmark@Sun.COM 	if (leaving)
259711042SErik.Nordmark@Sun.COM 		ilg->ilg_ilm = NULL;
25980Sstevel@tonic-gate 
25990Sstevel@tonic-gate 	ilg_delete(connp, ilg, v6src);
260011042SErik.Nordmark@Sun.COM 	if (ill != NULL)
260111042SErik.Nordmark@Sun.COM 		ilg_refrele(ilg);
260211042SErik.Nordmark@Sun.COM 	rw_exit(&connp->conn_ilg_lock);
260311042SErik.Nordmark@Sun.COM 
260411042SErik.Nordmark@Sun.COM 	if (ilm != NULL) {
260511042SErik.Nordmark@Sun.COM 		ASSERT(ill != NULL);
260611042SErik.Nordmark@Sun.COM 		(void) ip_delmulti_serial(ilm, B_FALSE, leaving);
260711042SErik.Nordmark@Sun.COM 	}
260811042SErik.Nordmark@Sun.COM done:
260911042SErik.Nordmark@Sun.COM 	if (ill != NULL) {
261011042SErik.Nordmark@Sun.COM 		mutex_exit(&ill->ill_mcast_serializer);
261111463SSowmini.Varadhan@Sun.COM 		/*
261211463SSowmini.Varadhan@Sun.COM 		 * Now that all locks have been dropped, we can
261311463SSowmini.Varadhan@Sun.COM 		 * send any deferred/queued DLPI or IP packets
261411463SSowmini.Varadhan@Sun.COM 		 */
261511463SSowmini.Varadhan@Sun.COM 		ill_mcast_send_queued(ill);
261611463SSowmini.Varadhan@Sun.COM 		ill_dlpi_send_queued(ill);
261711042SErik.Nordmark@Sun.COM 		ill_refrele(ill);
261811042SErik.Nordmark@Sun.COM 	}
261911042SErik.Nordmark@Sun.COM 	return (err);
26200Sstevel@tonic-gate }
26210Sstevel@tonic-gate 
26220Sstevel@tonic-gate /*
26230Sstevel@tonic-gate  * Handle the following optmgmt:
26240Sstevel@tonic-gate  *	IP_DROP_MEMBERSHIP		will leave
262511042SErik.Nordmark@Sun.COM  *	IPV6_LEAVE_GROUP		will leave
26260Sstevel@tonic-gate  *	MCAST_LEAVE_GROUP		will leave
26270Sstevel@tonic-gate  *	IP_UNBLOCK_SOURCE		will not leave
26280Sstevel@tonic-gate  *	MCAST_UNBLOCK_SOURCE		will not leave
26290Sstevel@tonic-gate  *	IP_LEAVE_SOURCE_GROUP		may leave (if leaving last source)
26300Sstevel@tonic-gate  *	MCAST_LEAVE_SOURCE_GROUP	may leave (if leaving last source)
26310Sstevel@tonic-gate  *
26320Sstevel@tonic-gate  * fmode and src parameters may be used to determine which option is
263311042SErik.Nordmark@Sun.COM  * being set, as follows:
26340Sstevel@tonic-gate  *	opt			 fmode			v6src
263511042SErik.Nordmark@Sun.COM  *	IP_DROP_MEMBERSHIP	 MODE_IS_INCLUDE	unspecified
26360Sstevel@tonic-gate  *	IPV6_LEAVE_GROUP	 MODE_IS_INCLUDE	unspecified
26370Sstevel@tonic-gate  *	MCAST_LEAVE_GROUP	 MODE_IS_INCLUDE	unspecified
263811042SErik.Nordmark@Sun.COM  *	IP_UNBLOCK_SOURCE	 MODE_IS_EXCLUDE	IPv4-mapped addr
26390Sstevel@tonic-gate  *	MCAST_UNBLOCK_SOURCE	 MODE_IS_EXCLUDE	v6 addr
264011042SErik.Nordmark@Sun.COM  *	IP_LEAVE_SOURCE_GROUP	 MODE_IS_INCLUDE	IPv4-mapped addr
26410Sstevel@tonic-gate  *	MCAST_LEAVE_SOURCE_GROUP MODE_IS_INCLUDE	v6 addr
26420Sstevel@tonic-gate  *
26430Sstevel@tonic-gate  * Changing the filter mode is not allowed; if a matching ilg already
26440Sstevel@tonic-gate  * exists and fmode != ilg->ilg_fmode, EINVAL is returned.
26450Sstevel@tonic-gate  *
264611042SErik.Nordmark@Sun.COM  * The interface to be used may be identified by an IPv4 address or by an
264711042SErik.Nordmark@Sun.COM  * interface index.
264811042SErik.Nordmark@Sun.COM  *
26490Sstevel@tonic-gate  * Handles IPv4-mapped IPv6 multicast addresses by associating them
265011042SErik.Nordmark@Sun.COM  * with the IPv4 address.  Assumes that if v6group is v4-mapped,
26510Sstevel@tonic-gate  * v6src is also v4-mapped.
26520Sstevel@tonic-gate  */
26530Sstevel@tonic-gate int
ip_opt_delete_group(conn_t * connp,boolean_t checkonly,const in6_addr_t * v6group,ipaddr_t ifaddr,uint_t ifindex,mcast_record_t fmode,const in6_addr_t * v6src)265411042SErik.Nordmark@Sun.COM ip_opt_delete_group(conn_t *connp, boolean_t checkonly,
265511042SErik.Nordmark@Sun.COM     const in6_addr_t *v6group, ipaddr_t ifaddr, uint_t ifindex,
265611042SErik.Nordmark@Sun.COM     mcast_record_t fmode, const in6_addr_t *v6src)
26570Sstevel@tonic-gate {
265811042SErik.Nordmark@Sun.COM 
265911042SErik.Nordmark@Sun.COM 	/*
266011042SErik.Nordmark@Sun.COM 	 * In the normal case below we don't check for the ill existing.
266111042SErik.Nordmark@Sun.COM 	 * Instead we look for an existing ilg in _excl.
266211042SErik.Nordmark@Sun.COM 	 * If checkonly we sanity check the arguments
266311042SErik.Nordmark@Sun.COM 	 */
266411042SErik.Nordmark@Sun.COM 	if (checkonly) {
266511042SErik.Nordmark@Sun.COM 		ill_t	*ill;
266611042SErik.Nordmark@Sun.COM 		int	err;
266711042SErik.Nordmark@Sun.COM 
266811042SErik.Nordmark@Sun.COM 		err = ip_opt_check(connp, v6group, v6src, ifaddr, ifindex,
266911042SErik.Nordmark@Sun.COM 		    &ill);
267011042SErik.Nordmark@Sun.COM 		/*
267111042SErik.Nordmark@Sun.COM 		 * do not do operation, just pretend to - new T_CHECK semantics.
267211042SErik.Nordmark@Sun.COM 		 * ip_opt_check is considered a good enough "check" here.
267311042SErik.Nordmark@Sun.COM 		 */
267411042SErik.Nordmark@Sun.COM 		if (ill != NULL)
267511042SErik.Nordmark@Sun.COM 			ill_refrele(ill);
26760Sstevel@tonic-gate 		return (err);
26770Sstevel@tonic-gate 	}
267811042SErik.Nordmark@Sun.COM 	return (ip_opt_delete_group_excl(connp, v6group, ifaddr, ifindex,
267911042SErik.Nordmark@Sun.COM 	    fmode, v6src));
26800Sstevel@tonic-gate }
26810Sstevel@tonic-gate 
26820Sstevel@tonic-gate /*
26830Sstevel@tonic-gate  * Group mgmt for upper conn that passes things down
26840Sstevel@tonic-gate  * to the interface multicast list (and DLPI)
26850Sstevel@tonic-gate  * These routines can handle new style options that specify an interface name
26860Sstevel@tonic-gate  * as opposed to an interface address (needed for general handling of
26870Sstevel@tonic-gate  * unnumbered interfaces.)
26880Sstevel@tonic-gate  */
26890Sstevel@tonic-gate 
26900Sstevel@tonic-gate /*
26910Sstevel@tonic-gate  * Add a group to an upper conn group data structure and pass things down
26920Sstevel@tonic-gate  * to the interface multicast list (and DLPI)
269311042SErik.Nordmark@Sun.COM  * Common for IPv4 and IPv6; for IPv4 we can have an ifaddr.
26940Sstevel@tonic-gate  */
26950Sstevel@tonic-gate static int
ilg_add(conn_t * connp,const in6_addr_t * v6group,ipaddr_t ifaddr,uint_t ifindex,ill_t * ill,mcast_record_t fmode,const in6_addr_t * v6src)269611042SErik.Nordmark@Sun.COM ilg_add(conn_t *connp, const in6_addr_t *v6group, ipaddr_t ifaddr,
269711042SErik.Nordmark@Sun.COM     uint_t ifindex, ill_t *ill, mcast_record_t fmode, const in6_addr_t *v6src)
26980Sstevel@tonic-gate {
26990Sstevel@tonic-gate 	int	error = 0;
27000Sstevel@tonic-gate 	ilg_t	*ilg;
27010Sstevel@tonic-gate 	ilg_stat_t ilgstat;
27020Sstevel@tonic-gate 	slist_t	*new_filter = NULL;
27030Sstevel@tonic-gate 	int	new_fmode;
270411042SErik.Nordmark@Sun.COM 	ilm_t *ilm;
27050Sstevel@tonic-gate 
27060Sstevel@tonic-gate 	if (!(ill->ill_flags & ILLF_MULTICAST))
27070Sstevel@tonic-gate 		return (EADDRNOTAVAIL);
27080Sstevel@tonic-gate 
270911042SErik.Nordmark@Sun.COM 	/* conn_ilg_lock protects the ilg list. */
271011042SErik.Nordmark@Sun.COM 	ASSERT(MUTEX_HELD(&ill->ill_mcast_serializer));
271111042SErik.Nordmark@Sun.COM 	rw_enter(&connp->conn_ilg_lock, RW_WRITER);
271211042SErik.Nordmark@Sun.COM 	ilg = ilg_lookup(connp, v6group, ifaddr, ifindex);
27130Sstevel@tonic-gate 
27140Sstevel@tonic-gate 	/*
27150Sstevel@tonic-gate 	 * Depending on the option we're handling, may or may not be okay
27160Sstevel@tonic-gate 	 * if group has already been added.  Figure out our rules based
27170Sstevel@tonic-gate 	 * on fmode and src params.  Also make sure there's enough room
27180Sstevel@tonic-gate 	 * in the filter if we're adding a source to an existing filter.
27190Sstevel@tonic-gate 	 */
27200Sstevel@tonic-gate 	if (IN6_IS_ADDR_UNSPECIFIED(v6src)) {
27210Sstevel@tonic-gate 		/* we're joining for all sources, must not have joined */
27220Sstevel@tonic-gate 		if (ilg != NULL)
27230Sstevel@tonic-gate 			error = EADDRINUSE;
27240Sstevel@tonic-gate 	} else {
27250Sstevel@tonic-gate 		if (fmode == MODE_IS_EXCLUDE) {
27260Sstevel@tonic-gate 			/* (excl {addr}) => block source, must have joined */
27270Sstevel@tonic-gate 			if (ilg == NULL)
27280Sstevel@tonic-gate 				error = EADDRNOTAVAIL;
27290Sstevel@tonic-gate 		}
27300Sstevel@tonic-gate 		/* (incl {addr}) => join source, may have joined */
27310Sstevel@tonic-gate 
27320Sstevel@tonic-gate 		if (ilg != NULL &&
27330Sstevel@tonic-gate 		    SLIST_CNT(ilg->ilg_filter) == MAX_FILTER_SIZE)
27340Sstevel@tonic-gate 			error = ENOBUFS;
27350Sstevel@tonic-gate 	}
27360Sstevel@tonic-gate 	if (error != 0) {
273711042SErik.Nordmark@Sun.COM 		rw_exit(&connp->conn_ilg_lock);
27380Sstevel@tonic-gate 		return (error);
27390Sstevel@tonic-gate 	}
27400Sstevel@tonic-gate 
27410Sstevel@tonic-gate 	/*
27420Sstevel@tonic-gate 	 * Alloc buffer to copy new state into (see below) before
27430Sstevel@tonic-gate 	 * we make any changes, so we can bail if it fails.
27440Sstevel@tonic-gate 	 */
27450Sstevel@tonic-gate 	if ((new_filter = l_alloc()) == NULL) {
274611042SErik.Nordmark@Sun.COM 		rw_exit(&connp->conn_ilg_lock);
27470Sstevel@tonic-gate 		return (ENOMEM);
27480Sstevel@tonic-gate 	}
27490Sstevel@tonic-gate 
27500Sstevel@tonic-gate 	if (ilg == NULL) {
27518485SPeter.Memishian@Sun.COM 		if ((ilg = conn_ilg_alloc(connp, &error)) == NULL) {
275211042SErik.Nordmark@Sun.COM 			rw_exit(&connp->conn_ilg_lock);
27530Sstevel@tonic-gate 			l_free(new_filter);
27548485SPeter.Memishian@Sun.COM 			return (error);
27550Sstevel@tonic-gate 		}
275611042SErik.Nordmark@Sun.COM 		ilg->ilg_ifindex = ifindex;
275711042SErik.Nordmark@Sun.COM 		ilg->ilg_ifaddr = ifaddr;
27580Sstevel@tonic-gate 		if (!IN6_IS_ADDR_UNSPECIFIED(v6src)) {
27590Sstevel@tonic-gate 			ilg->ilg_filter = l_alloc();
27600Sstevel@tonic-gate 			if (ilg->ilg_filter == NULL) {
27610Sstevel@tonic-gate 				ilg_delete(connp, ilg, NULL);
276211042SErik.Nordmark@Sun.COM 				rw_exit(&connp->conn_ilg_lock);
27630Sstevel@tonic-gate 				l_free(new_filter);
27640Sstevel@tonic-gate 				return (ENOMEM);
27650Sstevel@tonic-gate 			}
27660Sstevel@tonic-gate 			ilg->ilg_filter->sl_numsrc = 1;
27670Sstevel@tonic-gate 			ilg->ilg_filter->sl_addr[0] = *v6src;
27680Sstevel@tonic-gate 		}
27690Sstevel@tonic-gate 		ilgstat = ILGSTAT_NEW;
27700Sstevel@tonic-gate 		ilg->ilg_v6group = *v6group;
27710Sstevel@tonic-gate 		ilg->ilg_fmode = fmode;
27720Sstevel@tonic-gate 		ilg->ilg_ill = ill;
27730Sstevel@tonic-gate 	} else {
27740Sstevel@tonic-gate 		int index;
277511463SSowmini.Varadhan@Sun.COM 
27760Sstevel@tonic-gate 		if (ilg->ilg_fmode != fmode || IN6_IS_ADDR_UNSPECIFIED(v6src)) {
277711042SErik.Nordmark@Sun.COM 			rw_exit(&connp->conn_ilg_lock);
27780Sstevel@tonic-gate 			l_free(new_filter);
27790Sstevel@tonic-gate 			return (EINVAL);
27800Sstevel@tonic-gate 		}
27810Sstevel@tonic-gate 		if (ilg->ilg_filter == NULL) {
27820Sstevel@tonic-gate 			ilg->ilg_filter = l_alloc();
27830Sstevel@tonic-gate 			if (ilg->ilg_filter == NULL) {
278411042SErik.Nordmark@Sun.COM 				rw_exit(&connp->conn_ilg_lock);
27850Sstevel@tonic-gate 				l_free(new_filter);
27860Sstevel@tonic-gate 				return (ENOMEM);
27870Sstevel@tonic-gate 			}
27880Sstevel@tonic-gate 		}
27890Sstevel@tonic-gate 		if (list_has_addr(ilg->ilg_filter, v6src)) {
279011042SErik.Nordmark@Sun.COM 			rw_exit(&connp->conn_ilg_lock);
27910Sstevel@tonic-gate 			l_free(new_filter);
27920Sstevel@tonic-gate 			return (EADDRNOTAVAIL);
27930Sstevel@tonic-gate 		}
27940Sstevel@tonic-gate 		ilgstat = ILGSTAT_CHANGE;
27950Sstevel@tonic-gate 		index = ilg->ilg_filter->sl_numsrc++;
27960Sstevel@tonic-gate 		ilg->ilg_filter->sl_addr[index] = *v6src;
27970Sstevel@tonic-gate 	}
27980Sstevel@tonic-gate 
27990Sstevel@tonic-gate 	/*
28000Sstevel@tonic-gate 	 * Save copy of ilg's filter state to pass to other functions,
280111042SErik.Nordmark@Sun.COM 	 * so we can release conn_ilg_lock now.
28020Sstevel@tonic-gate 	 */
28030Sstevel@tonic-gate 	new_fmode = ilg->ilg_fmode;
28040Sstevel@tonic-gate 	l_copy(ilg->ilg_filter, new_filter);
28050Sstevel@tonic-gate 
280611042SErik.Nordmark@Sun.COM 	rw_exit(&connp->conn_ilg_lock);
28070Sstevel@tonic-gate 
28080Sstevel@tonic-gate 	/*
28090Sstevel@tonic-gate 	 * Now update the ill. We wait to do this until after the ilg
28100Sstevel@tonic-gate 	 * has been updated because we need to update the src filter
28110Sstevel@tonic-gate 	 * info for the ill, which involves looking at the status of
28120Sstevel@tonic-gate 	 * all the ilgs associated with this group/interface pair.
28130Sstevel@tonic-gate 	 */
281411042SErik.Nordmark@Sun.COM 	ilm = ip_addmulti_serial(v6group, ill, connp->conn_zoneid, ilgstat,
281511042SErik.Nordmark@Sun.COM 	    new_fmode, new_filter, &error);
281611042SErik.Nordmark@Sun.COM 
281711042SErik.Nordmark@Sun.COM 	rw_enter(&connp->conn_ilg_lock, RW_WRITER);
281811042SErik.Nordmark@Sun.COM 	/*
281911042SErik.Nordmark@Sun.COM 	 * Must look up the ilg again since we've not been holding
282011042SErik.Nordmark@Sun.COM 	 * conn_ilg_lock. The ilg could have disappeared due to an unplumb
282111042SErik.Nordmark@Sun.COM 	 * having called conn_update_ill, which can run once we dropped the
282211042SErik.Nordmark@Sun.COM 	 * conn_ilg_lock above.
282311042SErik.Nordmark@Sun.COM 	 */
282411042SErik.Nordmark@Sun.COM 	ilg = ilg_lookup(connp, v6group, ifaddr, ifindex);
282511042SErik.Nordmark@Sun.COM 	if (ilg == NULL) {
282611042SErik.Nordmark@Sun.COM 		rw_exit(&connp->conn_ilg_lock);
282711042SErik.Nordmark@Sun.COM 		if (ilm != NULL) {
282811042SErik.Nordmark@Sun.COM 			(void) ip_delmulti_serial(ilm, B_FALSE,
282911042SErik.Nordmark@Sun.COM 			    (ilgstat == ILGSTAT_NEW));
283011042SErik.Nordmark@Sun.COM 		}
283111042SErik.Nordmark@Sun.COM 		error = ENXIO;
283211042SErik.Nordmark@Sun.COM 		goto free_and_exit;
283311042SErik.Nordmark@Sun.COM 	}
283411042SErik.Nordmark@Sun.COM 	if (ilm != NULL) {
283511463SSowmini.Varadhan@Sun.COM 		if (ilg->ilg_ill == NULL) {
283611463SSowmini.Varadhan@Sun.COM 			/* some other thread is re-attaching this.  */
283711463SSowmini.Varadhan@Sun.COM 			rw_exit(&connp->conn_ilg_lock);
283811463SSowmini.Varadhan@Sun.COM 			(void) ip_delmulti_serial(ilm, B_FALSE,
283911463SSowmini.Varadhan@Sun.COM 			    (ilgstat == ILGSTAT_NEW));
284011463SSowmini.Varadhan@Sun.COM 			error = 0;
284111463SSowmini.Varadhan@Sun.COM 			goto free_and_exit;
284211463SSowmini.Varadhan@Sun.COM 		}
284311042SErik.Nordmark@Sun.COM 		/* Succeeded. Update the ilg to point at the ilm */
284411042SErik.Nordmark@Sun.COM 		if (ilgstat == ILGSTAT_NEW) {
284511463SSowmini.Varadhan@Sun.COM 			if (ilg->ilg_ilm == NULL) {
284611463SSowmini.Varadhan@Sun.COM 				ilg->ilg_ilm = ilm;
284711463SSowmini.Varadhan@Sun.COM 				ilm->ilm_ifaddr = ifaddr; /* For netstat */
284811463SSowmini.Varadhan@Sun.COM 			} else {
284911463SSowmini.Varadhan@Sun.COM 				/* some other thread is re-attaching this. */
285011463SSowmini.Varadhan@Sun.COM 				rw_exit(&connp->conn_ilg_lock);
285111463SSowmini.Varadhan@Sun.COM 				(void) ip_delmulti_serial(ilm, B_FALSE, B_TRUE);
285211463SSowmini.Varadhan@Sun.COM 				error = 0;
285311463SSowmini.Varadhan@Sun.COM 				goto free_and_exit;
285411463SSowmini.Varadhan@Sun.COM 			}
285511042SErik.Nordmark@Sun.COM 		} else {
285611042SErik.Nordmark@Sun.COM 			/*
285711042SErik.Nordmark@Sun.COM 			 * ip_addmulti didn't get a held ilm for
285811042SErik.Nordmark@Sun.COM 			 * ILGSTAT_CHANGE; ilm_refcnt was unchanged.
285911042SErik.Nordmark@Sun.COM 			 */
286011042SErik.Nordmark@Sun.COM 			ASSERT(ilg->ilg_ilm == ilm);
286111042SErik.Nordmark@Sun.COM 		}
286211042SErik.Nordmark@Sun.COM 	} else {
286311042SErik.Nordmark@Sun.COM 		ASSERT(error != 0);
28640Sstevel@tonic-gate 		/*
286511042SErik.Nordmark@Sun.COM 		 * Failed to allocate the ilm.
286611042SErik.Nordmark@Sun.COM 		 * Need to undo what we did before calling ip_addmulti()
286711042SErik.Nordmark@Sun.COM 		 * If ENETDOWN just clear ill_ilg since so that we
286811042SErik.Nordmark@Sun.COM 		 * will rejoin when the ill comes back; don't report ENETDOWN
286911042SErik.Nordmark@Sun.COM 		 * to application.
28700Sstevel@tonic-gate 		 */
287111042SErik.Nordmark@Sun.COM 		if (ilgstat == ILGSTAT_NEW && error == ENETDOWN) {
287211042SErik.Nordmark@Sun.COM 			ilg->ilg_ill = NULL;
287311042SErik.Nordmark@Sun.COM 			error = 0;
287411042SErik.Nordmark@Sun.COM 		} else {
287511042SErik.Nordmark@Sun.COM 			in6_addr_t delsrc =
287611042SErik.Nordmark@Sun.COM 			    (ilgstat == ILGSTAT_NEW) ? ipv6_all_zeros : *v6src;
287711042SErik.Nordmark@Sun.COM 
287811042SErik.Nordmark@Sun.COM 			ilg_delete(connp, ilg, &delsrc);
287911042SErik.Nordmark@Sun.COM 		}
28800Sstevel@tonic-gate 	}
288111042SErik.Nordmark@Sun.COM 	rw_exit(&connp->conn_ilg_lock);
288211042SErik.Nordmark@Sun.COM 
288311042SErik.Nordmark@Sun.COM free_and_exit:
28840Sstevel@tonic-gate 	l_free(new_filter);
288511042SErik.Nordmark@Sun.COM 	return (error);
28860Sstevel@tonic-gate }
28870Sstevel@tonic-gate 
28880Sstevel@tonic-gate /*
288911042SErik.Nordmark@Sun.COM  * Find an IPv4 ilg matching group, ill and source.
289011042SErik.Nordmark@Sun.COM  * The group and source can't be INADDR_ANY here so no need to translate to
289111042SErik.Nordmark@Sun.COM  * the unspecified IPv6 address.
28920Sstevel@tonic-gate  */
289311042SErik.Nordmark@Sun.COM boolean_t
conn_hasmembers_ill_withsrc_v4(conn_t * connp,ipaddr_t group,ipaddr_t src,ill_t * ill)289411042SErik.Nordmark@Sun.COM conn_hasmembers_ill_withsrc_v4(conn_t *connp, ipaddr_t group, ipaddr_t src,
289511042SErik.Nordmark@Sun.COM     ill_t *ill)
28960Sstevel@tonic-gate {
28970Sstevel@tonic-gate 	in6_addr_t v6group, v6src;
28980Sstevel@tonic-gate 	int i;
28990Sstevel@tonic-gate 	boolean_t isinlist;
29000Sstevel@tonic-gate 	ilg_t *ilg;
290111042SErik.Nordmark@Sun.COM 
290211042SErik.Nordmark@Sun.COM 	rw_enter(&connp->conn_ilg_lock, RW_READER);
290311042SErik.Nordmark@Sun.COM 	IN6_IPADDR_TO_V4MAPPED(group, &v6group);
290411042SErik.Nordmark@Sun.COM 	for (ilg = connp->conn_ilg; ilg != NULL; ilg = ilg->ilg_next) {
290511042SErik.Nordmark@Sun.COM 		if (ilg->ilg_condemned)
29060Sstevel@tonic-gate 			continue;
290711042SErik.Nordmark@Sun.COM 
290811042SErik.Nordmark@Sun.COM 		/* ilg_ill could be NULL if an add is in progress */
290911042SErik.Nordmark@Sun.COM 		if (ilg->ilg_ill != ill)
291011042SErik.Nordmark@Sun.COM 			continue;
291111042SErik.Nordmark@Sun.COM 
291211042SErik.Nordmark@Sun.COM 		/* The callers use upper ill for IPMP */
291311042SErik.Nordmark@Sun.COM 		ASSERT(!IS_UNDER_IPMP(ill));
291411042SErik.Nordmark@Sun.COM 		if (IN6_ARE_ADDR_EQUAL(&ilg->ilg_v6group, &v6group)) {
29150Sstevel@tonic-gate 			if (SLIST_IS_EMPTY(ilg->ilg_filter)) {
29160Sstevel@tonic-gate 				/* no source filter, so this is a match */
291711042SErik.Nordmark@Sun.COM 				rw_exit(&connp->conn_ilg_lock);
291811042SErik.Nordmark@Sun.COM 				return (B_TRUE);
29190Sstevel@tonic-gate 			}
29200Sstevel@tonic-gate 			break;
29210Sstevel@tonic-gate 		}
29220Sstevel@tonic-gate 	}
292311042SErik.Nordmark@Sun.COM 	if (ilg == NULL) {
292411042SErik.Nordmark@Sun.COM 		rw_exit(&connp->conn_ilg_lock);
292511042SErik.Nordmark@Sun.COM 		return (B_FALSE);
292611042SErik.Nordmark@Sun.COM 	}
29270Sstevel@tonic-gate 
29280Sstevel@tonic-gate 	/*
29290Sstevel@tonic-gate 	 * we have an ilg with matching ill and group; but
29300Sstevel@tonic-gate 	 * the ilg has a source list that we must check.
29310Sstevel@tonic-gate 	 */
29320Sstevel@tonic-gate 	IN6_IPADDR_TO_V4MAPPED(src, &v6src);
29330Sstevel@tonic-gate 	isinlist = B_FALSE;
29340Sstevel@tonic-gate 	for (i = 0; i < ilg->ilg_filter->sl_numsrc; i++) {
29350Sstevel@tonic-gate 		if (IN6_ARE_ADDR_EQUAL(&v6src, &ilg->ilg_filter->sl_addr[i])) {
29360Sstevel@tonic-gate 			isinlist = B_TRUE;
29370Sstevel@tonic-gate 			break;
29380Sstevel@tonic-gate 		}
29390Sstevel@tonic-gate 	}
29400Sstevel@tonic-gate 
29410Sstevel@tonic-gate 	if ((isinlist && ilg->ilg_fmode == MODE_IS_INCLUDE) ||
294211042SErik.Nordmark@Sun.COM 	    (!isinlist && ilg->ilg_fmode == MODE_IS_EXCLUDE)) {
294311042SErik.Nordmark@Sun.COM 		rw_exit(&connp->conn_ilg_lock);
294411042SErik.Nordmark@Sun.COM 		return (B_TRUE);
294511042SErik.Nordmark@Sun.COM 	}
294611042SErik.Nordmark@Sun.COM 	rw_exit(&connp->conn_ilg_lock);
294711042SErik.Nordmark@Sun.COM 	return (B_FALSE);
29480Sstevel@tonic-gate }
29490Sstevel@tonic-gate 
29500Sstevel@tonic-gate /*
29510Sstevel@tonic-gate  * Find an IPv6 ilg matching group, ill, and source
29520Sstevel@tonic-gate  */
295311042SErik.Nordmark@Sun.COM boolean_t
conn_hasmembers_ill_withsrc_v6(conn_t * connp,const in6_addr_t * v6group,const in6_addr_t * v6src,ill_t * ill)295411042SErik.Nordmark@Sun.COM conn_hasmembers_ill_withsrc_v6(conn_t *connp, const in6_addr_t *v6group,
29550Sstevel@tonic-gate     const in6_addr_t *v6src, ill_t *ill)
29560Sstevel@tonic-gate {
29570Sstevel@tonic-gate 	int i;
29580Sstevel@tonic-gate 	boolean_t isinlist;
29590Sstevel@tonic-gate 	ilg_t *ilg;
296011042SErik.Nordmark@Sun.COM 
296111042SErik.Nordmark@Sun.COM 	rw_enter(&connp->conn_ilg_lock, RW_READER);
296211042SErik.Nordmark@Sun.COM 	for (ilg = connp->conn_ilg; ilg != NULL; ilg = ilg->ilg_next) {
296311042SErik.Nordmark@Sun.COM 		if (ilg->ilg_condemned)
29640Sstevel@tonic-gate 			continue;
296511042SErik.Nordmark@Sun.COM 
296611042SErik.Nordmark@Sun.COM 		/* ilg_ill could be NULL if an add is in progress */
296711042SErik.Nordmark@Sun.COM 		if (ilg->ilg_ill != ill)
296811042SErik.Nordmark@Sun.COM 			continue;
296911042SErik.Nordmark@Sun.COM 
297011042SErik.Nordmark@Sun.COM 		/* The callers use upper ill for IPMP */
297111042SErik.Nordmark@Sun.COM 		ASSERT(!IS_UNDER_IPMP(ill));
297211042SErik.Nordmark@Sun.COM 		if (IN6_ARE_ADDR_EQUAL(&ilg->ilg_v6group, v6group)) {
29730Sstevel@tonic-gate 			if (SLIST_IS_EMPTY(ilg->ilg_filter)) {
29740Sstevel@tonic-gate 				/* no source filter, so this is a match */
297511042SErik.Nordmark@Sun.COM 				rw_exit(&connp->conn_ilg_lock);
297611042SErik.Nordmark@Sun.COM 				return (B_TRUE);
29770Sstevel@tonic-gate 			}
29780Sstevel@tonic-gate 			break;
29790Sstevel@tonic-gate 		}
29800Sstevel@tonic-gate 	}
298111042SErik.Nordmark@Sun.COM 	if (ilg == NULL) {
298211042SErik.Nordmark@Sun.COM 		rw_exit(&connp->conn_ilg_lock);
298311042SErik.Nordmark@Sun.COM 		return (B_FALSE);
298411042SErik.Nordmark@Sun.COM 	}
29850Sstevel@tonic-gate 
29860Sstevel@tonic-gate 	/*
29870Sstevel@tonic-gate 	 * we have an ilg with matching ill and group; but
29880Sstevel@tonic-gate 	 * the ilg has a source list that we must check.
29890Sstevel@tonic-gate 	 */
29900Sstevel@tonic-gate 	isinlist = B_FALSE;
29910Sstevel@tonic-gate 	for (i = 0; i < ilg->ilg_filter->sl_numsrc; i++) {
29920Sstevel@tonic-gate 		if (IN6_ARE_ADDR_EQUAL(v6src, &ilg->ilg_filter->sl_addr[i])) {
29930Sstevel@tonic-gate 			isinlist = B_TRUE;
29940Sstevel@tonic-gate 			break;
29950Sstevel@tonic-gate 		}
29960Sstevel@tonic-gate 	}
29970Sstevel@tonic-gate 
29980Sstevel@tonic-gate 	if ((isinlist && ilg->ilg_fmode == MODE_IS_INCLUDE) ||
299911042SErik.Nordmark@Sun.COM 	    (!isinlist && ilg->ilg_fmode == MODE_IS_EXCLUDE)) {
300011042SErik.Nordmark@Sun.COM 		rw_exit(&connp->conn_ilg_lock);
300111042SErik.Nordmark@Sun.COM 		return (B_TRUE);
300211042SErik.Nordmark@Sun.COM 	}
300311042SErik.Nordmark@Sun.COM 	rw_exit(&connp->conn_ilg_lock);
300411042SErik.Nordmark@Sun.COM 	return (B_FALSE);
30050Sstevel@tonic-gate }
30060Sstevel@tonic-gate 
30070Sstevel@tonic-gate /*
300811042SErik.Nordmark@Sun.COM  * Find an ilg matching group and ifaddr/ifindex.
300911042SErik.Nordmark@Sun.COM  * We check both ifaddr and ifindex even though at most one of them
301011042SErik.Nordmark@Sun.COM  * will be non-zero; that way we always find the right one.
30110Sstevel@tonic-gate  */
301211042SErik.Nordmark@Sun.COM static ilg_t *
ilg_lookup(conn_t * connp,const in6_addr_t * v6group,ipaddr_t ifaddr,uint_t ifindex)301311042SErik.Nordmark@Sun.COM ilg_lookup(conn_t *connp, const in6_addr_t *v6group, ipaddr_t ifaddr,
301411042SErik.Nordmark@Sun.COM     uint_t ifindex)
30150Sstevel@tonic-gate {
30160Sstevel@tonic-gate 	ilg_t	*ilg;
301711042SErik.Nordmark@Sun.COM 
301811042SErik.Nordmark@Sun.COM 	ASSERT(RW_LOCK_HELD(&connp->conn_ilg_lock));
301911042SErik.Nordmark@Sun.COM 
302011042SErik.Nordmark@Sun.COM 	for (ilg = connp->conn_ilg; ilg != NULL; ilg = ilg->ilg_next) {
302111042SErik.Nordmark@Sun.COM 		if (ilg->ilg_condemned)
30220Sstevel@tonic-gate 			continue;
302311042SErik.Nordmark@Sun.COM 
302411042SErik.Nordmark@Sun.COM 		if (ilg->ilg_ifaddr == ifaddr &&
302511042SErik.Nordmark@Sun.COM 		    ilg->ilg_ifindex == ifindex &&
30260Sstevel@tonic-gate 		    IN6_ARE_ADDR_EQUAL(&ilg->ilg_v6group, v6group))
30270Sstevel@tonic-gate 			return (ilg);
30280Sstevel@tonic-gate 	}
30290Sstevel@tonic-gate 	return (NULL);
30300Sstevel@tonic-gate }
30310Sstevel@tonic-gate 
30320Sstevel@tonic-gate /*
30330Sstevel@tonic-gate  * If a source address is passed in (src != NULL and src is not
30340Sstevel@tonic-gate  * unspecified), remove the specified src addr from the given ilg's
30350Sstevel@tonic-gate  * filter list, else delete the ilg.
30360Sstevel@tonic-gate  */
30370Sstevel@tonic-gate static void
ilg_delete(conn_t * connp,ilg_t * ilg,const in6_addr_t * src)30380Sstevel@tonic-gate ilg_delete(conn_t *connp, ilg_t *ilg, const in6_addr_t *src)
30390Sstevel@tonic-gate {
304011042SErik.Nordmark@Sun.COM 	ASSERT(RW_WRITE_HELD(&connp->conn_ilg_lock));
304111042SErik.Nordmark@Sun.COM 	ASSERT(ilg->ilg_ptpn != NULL);
304211042SErik.Nordmark@Sun.COM 	ASSERT(!ilg->ilg_condemned);
30430Sstevel@tonic-gate 
30440Sstevel@tonic-gate 	if (src == NULL || IN6_IS_ADDR_UNSPECIFIED(src)) {
30450Sstevel@tonic-gate 		FREE_SLIST(ilg->ilg_filter);
304611042SErik.Nordmark@Sun.COM 		ilg->ilg_filter = NULL;
304711042SErik.Nordmark@Sun.COM 
304811042SErik.Nordmark@Sun.COM 		ASSERT(ilg->ilg_ilm == NULL);
304911042SErik.Nordmark@Sun.COM 		ilg->ilg_ill = NULL;
305011042SErik.Nordmark@Sun.COM 		ilg->ilg_condemned = B_TRUE;
305111042SErik.Nordmark@Sun.COM 
305211042SErik.Nordmark@Sun.COM 		/* ilg_inactive will unlink from the list */
305311042SErik.Nordmark@Sun.COM 		ilg_refrele(ilg);
30540Sstevel@tonic-gate 	} else {
30550Sstevel@tonic-gate 		l_remove(ilg->ilg_filter, src);
30560Sstevel@tonic-gate 	}
30570Sstevel@tonic-gate }
30580Sstevel@tonic-gate 
30590Sstevel@tonic-gate /*
306011042SErik.Nordmark@Sun.COM  * Called from conn close. No new ilg can be added or removed
30610Sstevel@tonic-gate  * because CONN_CLOSING has been set by ip_close. ilg_add / ilg_delete
30620Sstevel@tonic-gate  * will return error if conn has started closing.
306311042SErik.Nordmark@Sun.COM  *
306411042SErik.Nordmark@Sun.COM  * We handle locking as follows.
306511042SErik.Nordmark@Sun.COM  * Under conn_ilg_lock we get the first ilg. As we drop the conn_ilg_lock to
306611042SErik.Nordmark@Sun.COM  * proceed with the ilm part of the delete we hold a reference on both the ill
306711042SErik.Nordmark@Sun.COM  * and the ilg. This doesn't prevent changes to the ilg, but prevents it from
306811042SErik.Nordmark@Sun.COM  * being deleted.
306911042SErik.Nordmark@Sun.COM  *
307011042SErik.Nordmark@Sun.COM  * Since the ilg_add code path uses two locks (conn_ilg_lock for the ilg part,
307111042SErik.Nordmark@Sun.COM  * and ill_mcast_lock for the ip_addmulti part) we can run at a point between
307211042SErik.Nordmark@Sun.COM  * the two. At that point ilg_ill is set, but ilg_ilm hasn't yet been set. In
307311042SErik.Nordmark@Sun.COM  * that case we delete the ilg here, which makes ilg_add discover that the ilg
307411042SErik.Nordmark@Sun.COM  * has disappeared when ip_addmulti returns, so it will discard the ilm it just
307511042SErik.Nordmark@Sun.COM  * added.
30760Sstevel@tonic-gate  */
30770Sstevel@tonic-gate void
ilg_delete_all(conn_t * connp)30780Sstevel@tonic-gate ilg_delete_all(conn_t *connp)
30790Sstevel@tonic-gate {
308011042SErik.Nordmark@Sun.COM 	ilg_t	*ilg, *next_ilg, *held_ilg;
308111042SErik.Nordmark@Sun.COM 	ilm_t	*ilm;
308211042SErik.Nordmark@Sun.COM 	ill_t	*ill;
308311042SErik.Nordmark@Sun.COM 	boolean_t need_refrele;
308411042SErik.Nordmark@Sun.COM 
308511042SErik.Nordmark@Sun.COM 	/*
308611042SErik.Nordmark@Sun.COM 	 * Can not run if there is a conn_update_ill already running.
308711042SErik.Nordmark@Sun.COM 	 * Wait for it to complete. Caller should have already set CONN_CLOSING
308811042SErik.Nordmark@Sun.COM 	 * which prevents any new threads to run in conn_update_ill.
308911042SErik.Nordmark@Sun.COM 	 */
30900Sstevel@tonic-gate 	mutex_enter(&connp->conn_lock);
309111042SErik.Nordmark@Sun.COM 	ASSERT(connp->conn_state_flags & CONN_CLOSING);
309211042SErik.Nordmark@Sun.COM 	while (connp->conn_state_flags & CONN_UPDATE_ILL)
309311042SErik.Nordmark@Sun.COM 		cv_wait(&connp->conn_cv, &connp->conn_lock);
309411042SErik.Nordmark@Sun.COM 	mutex_exit(&connp->conn_lock);
309511042SErik.Nordmark@Sun.COM 
309611042SErik.Nordmark@Sun.COM 	rw_enter(&connp->conn_ilg_lock, RW_WRITER);
309711042SErik.Nordmark@Sun.COM 	ilg = connp->conn_ilg;
309811042SErik.Nordmark@Sun.COM 	held_ilg = NULL;
309911042SErik.Nordmark@Sun.COM 	while (ilg != NULL) {
310011042SErik.Nordmark@Sun.COM 		if (ilg->ilg_condemned) {
310111042SErik.Nordmark@Sun.COM 			ilg = ilg->ilg_next;
31020Sstevel@tonic-gate 			continue;
31030Sstevel@tonic-gate 		}
310411042SErik.Nordmark@Sun.COM 		/* If the ilg is detached then no need to serialize */
310511042SErik.Nordmark@Sun.COM 		if (ilg->ilg_ilm == NULL) {
310611042SErik.Nordmark@Sun.COM 			next_ilg = ilg->ilg_next;
310711042SErik.Nordmark@Sun.COM 			ilg_delete(connp, ilg, NULL);
310811042SErik.Nordmark@Sun.COM 			ilg = next_ilg;
310911042SErik.Nordmark@Sun.COM 			continue;
311011042SErik.Nordmark@Sun.COM 		}
311111042SErik.Nordmark@Sun.COM 		ill = ilg->ilg_ilm->ilm_ill;
31128485SPeter.Memishian@Sun.COM 
31130Sstevel@tonic-gate 		/*
311411042SErik.Nordmark@Sun.COM 		 * In order to serialize on the ill we try to enter
311511042SErik.Nordmark@Sun.COM 		 * and if that fails we unlock and relock and then
311611042SErik.Nordmark@Sun.COM 		 * check that we still have an ilm.
31170Sstevel@tonic-gate 		 */
311811042SErik.Nordmark@Sun.COM 		need_refrele = B_FALSE;
311911042SErik.Nordmark@Sun.COM 		if (!mutex_tryenter(&ill->ill_mcast_serializer)) {
312011042SErik.Nordmark@Sun.COM 			ill_refhold(ill);
312111042SErik.Nordmark@Sun.COM 			need_refrele = B_TRUE;
312211042SErik.Nordmark@Sun.COM 			ilg_refhold(ilg);
312311042SErik.Nordmark@Sun.COM 			if (held_ilg != NULL)
312411042SErik.Nordmark@Sun.COM 				ilg_refrele(held_ilg);
312511042SErik.Nordmark@Sun.COM 			held_ilg = ilg;
312611042SErik.Nordmark@Sun.COM 			rw_exit(&connp->conn_ilg_lock);
312711042SErik.Nordmark@Sun.COM 			mutex_enter(&ill->ill_mcast_serializer);
312811042SErik.Nordmark@Sun.COM 			rw_enter(&connp->conn_ilg_lock, RW_WRITER);
312911042SErik.Nordmark@Sun.COM 			if (ilg->ilg_condemned) {
313011042SErik.Nordmark@Sun.COM 				ilg = ilg->ilg_next;
313111042SErik.Nordmark@Sun.COM 				goto next;
313211042SErik.Nordmark@Sun.COM 			}
31330Sstevel@tonic-gate 		}
313411042SErik.Nordmark@Sun.COM 		ilm = ilg->ilg_ilm;
313511042SErik.Nordmark@Sun.COM 		ilg->ilg_ilm = NULL;
313611042SErik.Nordmark@Sun.COM 		next_ilg = ilg->ilg_next;
31370Sstevel@tonic-gate 		ilg_delete(connp, ilg, NULL);
313811042SErik.Nordmark@Sun.COM 		ilg = next_ilg;
313911042SErik.Nordmark@Sun.COM 		rw_exit(&connp->conn_ilg_lock);
314011042SErik.Nordmark@Sun.COM 
314111042SErik.Nordmark@Sun.COM 		if (ilm != NULL)
314211042SErik.Nordmark@Sun.COM 			(void) ip_delmulti_serial(ilm, B_FALSE, B_TRUE);
314311042SErik.Nordmark@Sun.COM 
314411042SErik.Nordmark@Sun.COM 	next:
314511042SErik.Nordmark@Sun.COM 		mutex_exit(&ill->ill_mcast_serializer);
314611463SSowmini.Varadhan@Sun.COM 		/*
314711463SSowmini.Varadhan@Sun.COM 		 * Now that all locks have been dropped, we can send any
314811463SSowmini.Varadhan@Sun.COM 		 * deferred/queued DLPI or IP packets
314911463SSowmini.Varadhan@Sun.COM 		 */
315011463SSowmini.Varadhan@Sun.COM 		ill_mcast_send_queued(ill);
315111463SSowmini.Varadhan@Sun.COM 		ill_dlpi_send_queued(ill);
315211042SErik.Nordmark@Sun.COM 		if (need_refrele) {
315311042SErik.Nordmark@Sun.COM 			/* Drop ill reference while we hold no locks */
315411042SErik.Nordmark@Sun.COM 			ill_refrele(ill);
31558485SPeter.Memishian@Sun.COM 		}
315611042SErik.Nordmark@Sun.COM 		rw_enter(&connp->conn_ilg_lock, RW_WRITER);
31570Sstevel@tonic-gate 	}
315811042SErik.Nordmark@Sun.COM 	if (held_ilg != NULL)
315911042SErik.Nordmark@Sun.COM 		ilg_refrele(held_ilg);
316011042SErik.Nordmark@Sun.COM 	rw_exit(&connp->conn_ilg_lock);
31610Sstevel@tonic-gate }
31620Sstevel@tonic-gate 
31630Sstevel@tonic-gate /*
316411042SErik.Nordmark@Sun.COM  * Attach the ilg to an ilm on the ill. If it fails we leave ilg_ill as NULL so
316511463SSowmini.Varadhan@Sun.COM  * that a subsequent attempt can attach it. Drops and reacquires conn_ilg_lock.
31660Sstevel@tonic-gate  */
31670Sstevel@tonic-gate static void
ilg_attach(conn_t * connp,ilg_t * ilg,ill_t * ill)316811042SErik.Nordmark@Sun.COM ilg_attach(conn_t *connp, ilg_t *ilg, ill_t *ill)
31690Sstevel@tonic-gate {
317011042SErik.Nordmark@Sun.COM 	ilg_stat_t	ilgstat;
317111042SErik.Nordmark@Sun.COM 	slist_t		*new_filter;
317211042SErik.Nordmark@Sun.COM 	int		new_fmode;
317311042SErik.Nordmark@Sun.COM 	in6_addr_t	v6group;
317411042SErik.Nordmark@Sun.COM 	ipaddr_t	ifaddr;
317511042SErik.Nordmark@Sun.COM 	uint_t		ifindex;
317611042SErik.Nordmark@Sun.COM 	ilm_t		*ilm;
317711042SErik.Nordmark@Sun.COM 	int		error = 0;
317811042SErik.Nordmark@Sun.COM 
317911042SErik.Nordmark@Sun.COM 	ASSERT(RW_WRITE_HELD(&connp->conn_ilg_lock));
31800Sstevel@tonic-gate 	/*
318111042SErik.Nordmark@Sun.COM 	 * Alloc buffer to copy new state into (see below) before
318211042SErik.Nordmark@Sun.COM 	 * we make any changes, so we can bail if it fails.
31830Sstevel@tonic-gate 	 */
318411042SErik.Nordmark@Sun.COM 	if ((new_filter = l_alloc()) == NULL)
318511042SErik.Nordmark@Sun.COM 		return;
31860Sstevel@tonic-gate 
31870Sstevel@tonic-gate 	/*
318811042SErik.Nordmark@Sun.COM 	 * Save copy of ilg's filter state to pass to other functions, so
318911042SErik.Nordmark@Sun.COM 	 * we can release conn_ilg_lock now.
319011042SErik.Nordmark@Sun.COM 	 * Set ilg_ill so that an unplumb can find us.
31910Sstevel@tonic-gate 	 */
319211042SErik.Nordmark@Sun.COM 	new_fmode = ilg->ilg_fmode;
319311042SErik.Nordmark@Sun.COM 	l_copy(ilg->ilg_filter, new_filter);
319411042SErik.Nordmark@Sun.COM 	v6group = ilg->ilg_v6group;
319511042SErik.Nordmark@Sun.COM 	ifaddr = ilg->ilg_ifaddr;
319611042SErik.Nordmark@Sun.COM 	ifindex = ilg->ilg_ifindex;
319711042SErik.Nordmark@Sun.COM 	ilgstat = ILGSTAT_NEW;
319811042SErik.Nordmark@Sun.COM 
319911042SErik.Nordmark@Sun.COM 	ilg->ilg_ill = ill;
320011042SErik.Nordmark@Sun.COM 	ASSERT(ilg->ilg_ilm == NULL);
320111042SErik.Nordmark@Sun.COM 	rw_exit(&connp->conn_ilg_lock);
320211042SErik.Nordmark@Sun.COM 
320311042SErik.Nordmark@Sun.COM 	ilm = ip_addmulti_serial(&v6group, ill, connp->conn_zoneid, ilgstat,
320411042SErik.Nordmark@Sun.COM 	    new_fmode, new_filter, &error);
320511042SErik.Nordmark@Sun.COM 	l_free(new_filter);
320611042SErik.Nordmark@Sun.COM 
320711042SErik.Nordmark@Sun.COM 	rw_enter(&connp->conn_ilg_lock, RW_WRITER);
320811042SErik.Nordmark@Sun.COM 	/*
320911042SErik.Nordmark@Sun.COM 	 * Must look up the ilg again since we've not been holding
321011042SErik.Nordmark@Sun.COM 	 * conn_ilg_lock. The ilg could have disappeared due to an unplumb
321111042SErik.Nordmark@Sun.COM 	 * having called conn_update_ill, which can run once we dropped the
321211463SSowmini.Varadhan@Sun.COM 	 * conn_ilg_lock above. Alternatively, the ilg could have been attached
321311463SSowmini.Varadhan@Sun.COM 	 * when the lock was dropped
321411042SErik.Nordmark@Sun.COM 	 */
321511042SErik.Nordmark@Sun.COM 	ilg = ilg_lookup(connp, &v6group, ifaddr, ifindex);
321611463SSowmini.Varadhan@Sun.COM 	if (ilg == NULL || ilg->ilg_ilm != NULL) {
321711042SErik.Nordmark@Sun.COM 		if (ilm != NULL) {
321811042SErik.Nordmark@Sun.COM 			rw_exit(&connp->conn_ilg_lock);
321911042SErik.Nordmark@Sun.COM 			(void) ip_delmulti_serial(ilm, B_FALSE,
322011042SErik.Nordmark@Sun.COM 			    (ilgstat == ILGSTAT_NEW));
322111042SErik.Nordmark@Sun.COM 			rw_enter(&connp->conn_ilg_lock, RW_WRITER);
32220Sstevel@tonic-gate 		}
322311042SErik.Nordmark@Sun.COM 		return;
32240Sstevel@tonic-gate 	}
322511042SErik.Nordmark@Sun.COM 	if (ilm == NULL) {
322611042SErik.Nordmark@Sun.COM 		ilg->ilg_ill = NULL;
322711042SErik.Nordmark@Sun.COM 		return;
322811042SErik.Nordmark@Sun.COM 	}
322911042SErik.Nordmark@Sun.COM 	ilg->ilg_ilm = ilm;
323011042SErik.Nordmark@Sun.COM 	ilm->ilm_ifaddr = ifaddr;	/* For netstat */
32310Sstevel@tonic-gate }
32320Sstevel@tonic-gate 
32330Sstevel@tonic-gate /*
32340Sstevel@tonic-gate  * Called when an ill is unplumbed to make sure that there are no
323511042SErik.Nordmark@Sun.COM  * dangling conn references to that ill. In that case ill is non-NULL and
323611042SErik.Nordmark@Sun.COM  * we make sure we remove all references to it.
323711042SErik.Nordmark@Sun.COM  * Also called when we should revisit the ilg_ill used for multicast
323811042SErik.Nordmark@Sun.COM  * memberships, in which case ill is NULL.
323911042SErik.Nordmark@Sun.COM  *
324011042SErik.Nordmark@Sun.COM  * conn is held by caller.
324111042SErik.Nordmark@Sun.COM  *
324211042SErik.Nordmark@Sun.COM  * Note that ipcl_walk only walks conns that are not yet condemned.
324311042SErik.Nordmark@Sun.COM  * condemned conns can't be refheld. For this reason, conn must become clean
324411042SErik.Nordmark@Sun.COM  * first, i.e. it must not refer to any ill/ire and then only set
324511042SErik.Nordmark@Sun.COM  * condemned flag.
324611042SErik.Nordmark@Sun.COM  *
324711042SErik.Nordmark@Sun.COM  * We leave ixa_multicast_ifindex in place. We prefer dropping
324811042SErik.Nordmark@Sun.COM  * packets instead of sending them out the wrong interface.
324911042SErik.Nordmark@Sun.COM  *
325011042SErik.Nordmark@Sun.COM  * We keep the ilg around in a detached state (with ilg_ill and ilg_ilm being
325111042SErik.Nordmark@Sun.COM  * NULL) so that the application can leave it later. Also, if ilg_ifaddr and
325211042SErik.Nordmark@Sun.COM  * ilg_ifindex are zero, indicating that the system should pick the interface,
325311042SErik.Nordmark@Sun.COM  * then we attempt to reselect the ill and join on it.
325411042SErik.Nordmark@Sun.COM  *
325511042SErik.Nordmark@Sun.COM  * Locking notes:
325611042SErik.Nordmark@Sun.COM  * Under conn_ilg_lock we get the first ilg. As we drop the conn_ilg_lock to
325711042SErik.Nordmark@Sun.COM  * proceed with the ilm part of the delete we hold a reference on both the ill
325811042SErik.Nordmark@Sun.COM  * and the ilg. This doesn't prevent changes to the ilg, but prevents it from
325911042SErik.Nordmark@Sun.COM  * being deleted.
326011042SErik.Nordmark@Sun.COM  *
326111042SErik.Nordmark@Sun.COM  * Note: if this function is called when new ill/ipif's arrive or change status
326211042SErik.Nordmark@Sun.COM  * (SIOCSLIFINDEX, SIOCSLIFADDR) then we will attempt to attach any ilgs with
326311042SErik.Nordmark@Sun.COM  * a NULL ilg_ill to an ill/ilm.
32640Sstevel@tonic-gate  */
326511042SErik.Nordmark@Sun.COM static void
conn_update_ill(conn_t * connp,caddr_t arg)326611042SErik.Nordmark@Sun.COM conn_update_ill(conn_t *connp, caddr_t arg)
32670Sstevel@tonic-gate {
326811042SErik.Nordmark@Sun.COM 	ill_t	*ill = (ill_t *)arg;
326911042SErik.Nordmark@Sun.COM 
327011042SErik.Nordmark@Sun.COM 	/*
327111042SErik.Nordmark@Sun.COM 	 * We have to prevent ip_close/ilg_delete_all from running at
327211042SErik.Nordmark@Sun.COM 	 * the same time. ip_close sets CONN_CLOSING before doing the ilg_delete
327311042SErik.Nordmark@Sun.COM 	 * all, and we set CONN_UPDATE_ILL. That ensures that only one of
327411042SErik.Nordmark@Sun.COM 	 * ilg_delete_all and conn_update_ill run at a time for a given conn.
327511042SErik.Nordmark@Sun.COM 	 * If ilg_delete_all got here first, then we have nothing to do.
327611042SErik.Nordmark@Sun.COM 	 */
327711042SErik.Nordmark@Sun.COM 	mutex_enter(&connp->conn_lock);
327811042SErik.Nordmark@Sun.COM 	if (connp->conn_state_flags & (CONN_CLOSING|CONN_UPDATE_ILL)) {
327911042SErik.Nordmark@Sun.COM 		/* Caller has to wait for ill_ilm_cnt to drop to zero */
328011042SErik.Nordmark@Sun.COM 		mutex_exit(&connp->conn_lock);
328111042SErik.Nordmark@Sun.COM 		return;
32820Sstevel@tonic-gate 	}
328311042SErik.Nordmark@Sun.COM 	connp->conn_state_flags |= CONN_UPDATE_ILL;
328411042SErik.Nordmark@Sun.COM 	mutex_exit(&connp->conn_lock);
328511042SErik.Nordmark@Sun.COM 
328611042SErik.Nordmark@Sun.COM 	if (ill != NULL)
328711042SErik.Nordmark@Sun.COM 		ilg_check_detach(connp, ill);
328811042SErik.Nordmark@Sun.COM 
328911463SSowmini.Varadhan@Sun.COM 	ilg_check_reattach(connp, ill);
329011042SErik.Nordmark@Sun.COM 
329111042SErik.Nordmark@Sun.COM 	/* Do we need to wake up a thread in ilg_delete_all? */
329211042SErik.Nordmark@Sun.COM 	mutex_enter(&connp->conn_lock);
329311042SErik.Nordmark@Sun.COM 	connp->conn_state_flags &= ~CONN_UPDATE_ILL;
329411042SErik.Nordmark@Sun.COM 	if (connp->conn_state_flags & CONN_CLOSING)
329511042SErik.Nordmark@Sun.COM 		cv_broadcast(&connp->conn_cv);
329611042SErik.Nordmark@Sun.COM 	mutex_exit(&connp->conn_lock);
329711042SErik.Nordmark@Sun.COM }
329811042SErik.Nordmark@Sun.COM 
329911042SErik.Nordmark@Sun.COM /* Detach from an ill that is going away */
330011042SErik.Nordmark@Sun.COM static void
ilg_check_detach(conn_t * connp,ill_t * ill)330111042SErik.Nordmark@Sun.COM ilg_check_detach(conn_t *connp, ill_t *ill)
330211042SErik.Nordmark@Sun.COM {
330311042SErik.Nordmark@Sun.COM 	char	group_buf[INET6_ADDRSTRLEN];
330411042SErik.Nordmark@Sun.COM 	ilg_t	*ilg, *held_ilg;
330511042SErik.Nordmark@Sun.COM 	ilm_t	*ilm;
330611042SErik.Nordmark@Sun.COM 
330711042SErik.Nordmark@Sun.COM 	mutex_enter(&ill->ill_mcast_serializer);
330811042SErik.Nordmark@Sun.COM 	rw_enter(&connp->conn_ilg_lock, RW_WRITER);
330911042SErik.Nordmark@Sun.COM 	held_ilg = NULL;
331011042SErik.Nordmark@Sun.COM 	for (ilg = connp->conn_ilg; ilg != NULL; ilg = ilg->ilg_next) {
331111042SErik.Nordmark@Sun.COM 		if (ilg->ilg_condemned)
331211042SErik.Nordmark@Sun.COM 			continue;
331311042SErik.Nordmark@Sun.COM 
331411042SErik.Nordmark@Sun.COM 		if (ilg->ilg_ill != ill)
331511042SErik.Nordmark@Sun.COM 			continue;
331611042SErik.Nordmark@Sun.COM 
331711042SErik.Nordmark@Sun.COM 		/* Detach from current ill */
331811042SErik.Nordmark@Sun.COM 		ip1dbg(("ilg_check_detach: detach %s on %s\n",
331911042SErik.Nordmark@Sun.COM 		    inet_ntop(AF_INET6, &ilg->ilg_v6group,
332011042SErik.Nordmark@Sun.COM 		    group_buf, sizeof (group_buf)),
332111042SErik.Nordmark@Sun.COM 		    ilg->ilg_ill->ill_name));
332211042SErik.Nordmark@Sun.COM 
332311042SErik.Nordmark@Sun.COM 		/* Detach this ilg from the ill/ilm */
332411042SErik.Nordmark@Sun.COM 		ilm = ilg->ilg_ilm;
332511042SErik.Nordmark@Sun.COM 		ilg->ilg_ilm = NULL;
332611042SErik.Nordmark@Sun.COM 		ilg->ilg_ill = NULL;
332711042SErik.Nordmark@Sun.COM 		if (ilm == NULL)
332811042SErik.Nordmark@Sun.COM 			continue;
332911042SErik.Nordmark@Sun.COM 
333011042SErik.Nordmark@Sun.COM 		/* Prevent ilg from disappearing */
333111042SErik.Nordmark@Sun.COM 		ilg_transfer_hold(held_ilg, ilg);
333211042SErik.Nordmark@Sun.COM 		held_ilg = ilg;
333311042SErik.Nordmark@Sun.COM 		rw_exit(&connp->conn_ilg_lock);
333411042SErik.Nordmark@Sun.COM 
333511042SErik.Nordmark@Sun.COM 		(void) ip_delmulti_serial(ilm, B_FALSE, B_TRUE);
333611042SErik.Nordmark@Sun.COM 		rw_enter(&connp->conn_ilg_lock, RW_WRITER);
333711042SErik.Nordmark@Sun.COM 	}
333811042SErik.Nordmark@Sun.COM 	if (held_ilg != NULL)
333911042SErik.Nordmark@Sun.COM 		ilg_refrele(held_ilg);
334011042SErik.Nordmark@Sun.COM 	rw_exit(&connp->conn_ilg_lock);
334111042SErik.Nordmark@Sun.COM 	mutex_exit(&ill->ill_mcast_serializer);
334211463SSowmini.Varadhan@Sun.COM 	/*
334311463SSowmini.Varadhan@Sun.COM 	 * Now that all locks have been dropped, we can send any
334411463SSowmini.Varadhan@Sun.COM 	 * deferred/queued DLPI or IP packets
334511463SSowmini.Varadhan@Sun.COM 	 */
334611463SSowmini.Varadhan@Sun.COM 	ill_mcast_send_queued(ill);
334711463SSowmini.Varadhan@Sun.COM 	ill_dlpi_send_queued(ill);
33480Sstevel@tonic-gate }
33490Sstevel@tonic-gate 
33500Sstevel@tonic-gate /*
335111042SErik.Nordmark@Sun.COM  * Check if there is a place to attach the conn_ilgs. We do this for both
335211042SErik.Nordmark@Sun.COM  * detached ilgs and attached ones, since for the latter there could be
335311463SSowmini.Varadhan@Sun.COM  * a better ill to attach them to. oill is non-null if we just detached from
335411463SSowmini.Varadhan@Sun.COM  * that ill.
33550Sstevel@tonic-gate  */
335611042SErik.Nordmark@Sun.COM static void
ilg_check_reattach(conn_t * connp,ill_t * oill)335711463SSowmini.Varadhan@Sun.COM ilg_check_reattach(conn_t *connp, ill_t *oill)
33580Sstevel@tonic-gate {
335911042SErik.Nordmark@Sun.COM 	ill_t	*ill;
336011042SErik.Nordmark@Sun.COM 	char	group_buf[INET6_ADDRSTRLEN];
336111042SErik.Nordmark@Sun.COM 	ilg_t	*ilg, *held_ilg;
336211042SErik.Nordmark@Sun.COM 	ilm_t	*ilm;
336311042SErik.Nordmark@Sun.COM 	zoneid_t zoneid = IPCL_ZONEID(connp);
336411042SErik.Nordmark@Sun.COM 	int	error;
336511042SErik.Nordmark@Sun.COM 	ip_stack_t *ipst = connp->conn_netstack->netstack_ip;
336611042SErik.Nordmark@Sun.COM 
336711042SErik.Nordmark@Sun.COM 	rw_enter(&connp->conn_ilg_lock, RW_WRITER);
336811042SErik.Nordmark@Sun.COM 	held_ilg = NULL;
336911042SErik.Nordmark@Sun.COM 	for (ilg = connp->conn_ilg; ilg != NULL; ilg = ilg->ilg_next) {
337011042SErik.Nordmark@Sun.COM 		if (ilg->ilg_condemned)
337111042SErik.Nordmark@Sun.COM 			continue;
337211042SErik.Nordmark@Sun.COM 
337311042SErik.Nordmark@Sun.COM 		/* Check if the conn_ill matches what we would pick now */
337411042SErik.Nordmark@Sun.COM 		ill = ill_mcast_lookup(&ilg->ilg_v6group, ilg->ilg_ifaddr,
337511042SErik.Nordmark@Sun.COM 		    ilg->ilg_ifindex, zoneid, ipst, &error);
337611042SErik.Nordmark@Sun.COM 
337711042SErik.Nordmark@Sun.COM 		/*
337811042SErik.Nordmark@Sun.COM 		 * Make sure the ill is usable for multicast and that
337911042SErik.Nordmark@Sun.COM 		 * we can send the DL_ADDMULTI_REQ before we create an
338011042SErik.Nordmark@Sun.COM 		 * ilm.
338111042SErik.Nordmark@Sun.COM 		 */
338211042SErik.Nordmark@Sun.COM 		if (ill != NULL &&
338311042SErik.Nordmark@Sun.COM 		    (!(ill->ill_flags & ILLF_MULTICAST) || !ill->ill_dl_up)) {
338411042SErik.Nordmark@Sun.COM 			/* Drop locks across ill_refrele */
338511042SErik.Nordmark@Sun.COM 			ilg_transfer_hold(held_ilg, ilg);
338611042SErik.Nordmark@Sun.COM 			held_ilg = ilg;
338711042SErik.Nordmark@Sun.COM 			rw_exit(&connp->conn_ilg_lock);
338811042SErik.Nordmark@Sun.COM 			ill_refrele(ill);
338911042SErik.Nordmark@Sun.COM 			ill = NULL;
339011042SErik.Nordmark@Sun.COM 			rw_enter(&connp->conn_ilg_lock, RW_WRITER);
339111042SErik.Nordmark@Sun.COM 			/* Note that ilg could have become condemned */
339211042SErik.Nordmark@Sun.COM 		}
339311042SErik.Nordmark@Sun.COM 
339411463SSowmini.Varadhan@Sun.COM 		/*
339511463SSowmini.Varadhan@Sun.COM 		 * Is the ill unchanged, even if both are NULL?
339611463SSowmini.Varadhan@Sun.COM 		 * Did we just detach from that ill?
339711463SSowmini.Varadhan@Sun.COM 		 */
339811463SSowmini.Varadhan@Sun.COM 		if (ill == ilg->ilg_ill || (ill != NULL && ill == oill)) {
339911042SErik.Nordmark@Sun.COM 			if (ill != NULL) {
340011042SErik.Nordmark@Sun.COM 				/* Drop locks across ill_refrele */
340111042SErik.Nordmark@Sun.COM 				ilg_transfer_hold(held_ilg, ilg);
340211042SErik.Nordmark@Sun.COM 				held_ilg = ilg;
340311042SErik.Nordmark@Sun.COM 				rw_exit(&connp->conn_ilg_lock);
340411042SErik.Nordmark@Sun.COM 				ill_refrele(ill);
340511042SErik.Nordmark@Sun.COM 				rw_enter(&connp->conn_ilg_lock, RW_WRITER);
340611042SErik.Nordmark@Sun.COM 			}
340711042SErik.Nordmark@Sun.COM 			continue;
340811042SErik.Nordmark@Sun.COM 		}
340911042SErik.Nordmark@Sun.COM 
341011042SErik.Nordmark@Sun.COM 		/* Something changed; detach from old first if needed */
341111042SErik.Nordmark@Sun.COM 		if (ilg->ilg_ill != NULL) {
341211042SErik.Nordmark@Sun.COM 			ill_t *ill2 = ilg->ilg_ill;
341311042SErik.Nordmark@Sun.COM 			boolean_t need_refrele = B_FALSE;
341411042SErik.Nordmark@Sun.COM 
341511042SErik.Nordmark@Sun.COM 			/*
341611042SErik.Nordmark@Sun.COM 			 * In order to serialize on the ill we try to enter
341711042SErik.Nordmark@Sun.COM 			 * and if that fails we unlock and relock.
341811042SErik.Nordmark@Sun.COM 			 */
341911042SErik.Nordmark@Sun.COM 			if (!mutex_tryenter(&ill2->ill_mcast_serializer)) {
342011042SErik.Nordmark@Sun.COM 				ill_refhold(ill2);
342111042SErik.Nordmark@Sun.COM 				need_refrele = B_TRUE;
342211042SErik.Nordmark@Sun.COM 				ilg_transfer_hold(held_ilg, ilg);
342311042SErik.Nordmark@Sun.COM 				held_ilg = ilg;
342411042SErik.Nordmark@Sun.COM 				rw_exit(&connp->conn_ilg_lock);
342511042SErik.Nordmark@Sun.COM 				mutex_enter(&ill2->ill_mcast_serializer);
342611042SErik.Nordmark@Sun.COM 				rw_enter(&connp->conn_ilg_lock, RW_WRITER);
342711042SErik.Nordmark@Sun.COM 				/* Note that ilg could have become condemned */
342811042SErik.Nordmark@Sun.COM 			}
342911042SErik.Nordmark@Sun.COM 			/*
343011042SErik.Nordmark@Sun.COM 			 * Check that nobody else re-attached the ilg while we
343111042SErik.Nordmark@Sun.COM 			 * dropped the lock.
343211042SErik.Nordmark@Sun.COM 			 */
343311042SErik.Nordmark@Sun.COM 			if (ilg->ilg_ill == ill2) {
343411042SErik.Nordmark@Sun.COM 				ASSERT(!ilg->ilg_condemned);
343511042SErik.Nordmark@Sun.COM 				/* Detach from current ill */
343611042SErik.Nordmark@Sun.COM 				ip1dbg(("conn_check_reattach: detach %s/%s\n",
343711042SErik.Nordmark@Sun.COM 				    inet_ntop(AF_INET6, &ilg->ilg_v6group,
343811042SErik.Nordmark@Sun.COM 				    group_buf, sizeof (group_buf)),
343911042SErik.Nordmark@Sun.COM 				    ill2->ill_name));
344011042SErik.Nordmark@Sun.COM 
344111042SErik.Nordmark@Sun.COM 				ilm = ilg->ilg_ilm;
344211042SErik.Nordmark@Sun.COM 				ilg->ilg_ilm = NULL;
344311042SErik.Nordmark@Sun.COM 				ilg->ilg_ill = NULL;
344411042SErik.Nordmark@Sun.COM 			} else {
344511042SErik.Nordmark@Sun.COM 				ilm = NULL;
344611042SErik.Nordmark@Sun.COM 			}
344711463SSowmini.Varadhan@Sun.COM 			ilg_transfer_hold(held_ilg, ilg);
344811463SSowmini.Varadhan@Sun.COM 			held_ilg = ilg;
344911042SErik.Nordmark@Sun.COM 			rw_exit(&connp->conn_ilg_lock);
345011042SErik.Nordmark@Sun.COM 			if (ilm != NULL)
345111042SErik.Nordmark@Sun.COM 				(void) ip_delmulti_serial(ilm, B_FALSE, B_TRUE);
345211042SErik.Nordmark@Sun.COM 			mutex_exit(&ill2->ill_mcast_serializer);
345311463SSowmini.Varadhan@Sun.COM 			/*
345411463SSowmini.Varadhan@Sun.COM 			 * Now that all locks have been dropped, we can send any
345511463SSowmini.Varadhan@Sun.COM 			 * deferred/queued DLPI or IP packets
345611463SSowmini.Varadhan@Sun.COM 			 */
345711463SSowmini.Varadhan@Sun.COM 			ill_mcast_send_queued(ill2);
345811463SSowmini.Varadhan@Sun.COM 			ill_dlpi_send_queued(ill2);
345911042SErik.Nordmark@Sun.COM 			if (need_refrele) {
346011042SErik.Nordmark@Sun.COM 				/* Drop ill reference while we hold no locks */
346111042SErik.Nordmark@Sun.COM 				ill_refrele(ill2);
346211042SErik.Nordmark@Sun.COM 			}
346311042SErik.Nordmark@Sun.COM 			rw_enter(&connp->conn_ilg_lock, RW_WRITER);
346411042SErik.Nordmark@Sun.COM 			/*
346511042SErik.Nordmark@Sun.COM 			 * While we dropped conn_ilg_lock some other thread
346611042SErik.Nordmark@Sun.COM 			 * could have attached this ilg, thus we check again.
346711042SErik.Nordmark@Sun.COM 			 */
346811042SErik.Nordmark@Sun.COM 			if (ilg->ilg_ill != NULL) {
346911042SErik.Nordmark@Sun.COM 				if (ill != NULL) {
347011042SErik.Nordmark@Sun.COM 					/* Drop locks across ill_refrele */
347111042SErik.Nordmark@Sun.COM 					ilg_transfer_hold(held_ilg, ilg);
347211042SErik.Nordmark@Sun.COM 					held_ilg = ilg;
347311042SErik.Nordmark@Sun.COM 					rw_exit(&connp->conn_ilg_lock);
347411042SErik.Nordmark@Sun.COM 					ill_refrele(ill);
347511042SErik.Nordmark@Sun.COM 					rw_enter(&connp->conn_ilg_lock,
347611042SErik.Nordmark@Sun.COM 					    RW_WRITER);
347711042SErik.Nordmark@Sun.COM 				}
347811042SErik.Nordmark@Sun.COM 				continue;
34790Sstevel@tonic-gate 			}
34800Sstevel@tonic-gate 		}
348111042SErik.Nordmark@Sun.COM 		if (ill != NULL) {
348211042SErik.Nordmark@Sun.COM 			/*
348311042SErik.Nordmark@Sun.COM 			 * In order to serialize on the ill we try to enter
348411042SErik.Nordmark@Sun.COM 			 * and if that fails we unlock and relock.
348511042SErik.Nordmark@Sun.COM 			 */
348611042SErik.Nordmark@Sun.COM 			if (!mutex_tryenter(&ill->ill_mcast_serializer)) {
348711042SErik.Nordmark@Sun.COM 				/* Already have a refhold on ill */
348811042SErik.Nordmark@Sun.COM 				ilg_transfer_hold(held_ilg, ilg);
348911042SErik.Nordmark@Sun.COM 				held_ilg = ilg;
349011042SErik.Nordmark@Sun.COM 				rw_exit(&connp->conn_ilg_lock);
349111042SErik.Nordmark@Sun.COM 				mutex_enter(&ill->ill_mcast_serializer);
349211042SErik.Nordmark@Sun.COM 				rw_enter(&connp->conn_ilg_lock, RW_WRITER);
349311042SErik.Nordmark@Sun.COM 				/* Note that ilg could have become condemned */
349411042SErik.Nordmark@Sun.COM 			}
349511463SSowmini.Varadhan@Sun.COM 			ilg_transfer_hold(held_ilg, ilg);
349611463SSowmini.Varadhan@Sun.COM 			held_ilg = ilg;
349711042SErik.Nordmark@Sun.COM 			/*
349811042SErik.Nordmark@Sun.COM 			 * Check that nobody else attached the ilg and that
349911042SErik.Nordmark@Sun.COM 			 * it wasn't condemned while we dropped the lock.
350011042SErik.Nordmark@Sun.COM 			 */
350111042SErik.Nordmark@Sun.COM 			if (ilg->ilg_ill == NULL && !ilg->ilg_condemned) {
350211042SErik.Nordmark@Sun.COM 				/*
350311042SErik.Nordmark@Sun.COM 				 * Attach to the new ill. Can fail in which
350411042SErik.Nordmark@Sun.COM 				 * case ilg_ill will remain NULL. ilg_attach
350511042SErik.Nordmark@Sun.COM 				 * drops and reacquires conn_ilg_lock.
350611042SErik.Nordmark@Sun.COM 				 */
350711042SErik.Nordmark@Sun.COM 				ip1dbg(("conn_check_reattach: attach %s/%s\n",
350811042SErik.Nordmark@Sun.COM 				    inet_ntop(AF_INET6, &ilg->ilg_v6group,
350911042SErik.Nordmark@Sun.COM 				    group_buf, sizeof (group_buf)),
351011042SErik.Nordmark@Sun.COM 				    ill->ill_name));
351111042SErik.Nordmark@Sun.COM 				ilg_attach(connp, ilg, ill);
351211042SErik.Nordmark@Sun.COM 				ASSERT(RW_WRITE_HELD(&connp->conn_ilg_lock));
351311042SErik.Nordmark@Sun.COM 			}
351411463SSowmini.Varadhan@Sun.COM 			/* Drop locks across ill_refrele */
351511463SSowmini.Varadhan@Sun.COM 			rw_exit(&connp->conn_ilg_lock);
351611042SErik.Nordmark@Sun.COM 			mutex_exit(&ill->ill_mcast_serializer);
351711463SSowmini.Varadhan@Sun.COM 			/*
351811463SSowmini.Varadhan@Sun.COM 			 * Now that all locks have been
351911463SSowmini.Varadhan@Sun.COM 			 * dropped, we can send any
352011463SSowmini.Varadhan@Sun.COM 			 * deferred/queued DLPI or IP packets
352111463SSowmini.Varadhan@Sun.COM 			 */
352211463SSowmini.Varadhan@Sun.COM 			ill_mcast_send_queued(ill);
352311463SSowmini.Varadhan@Sun.COM 			ill_dlpi_send_queued(ill);
352411042SErik.Nordmark@Sun.COM 			ill_refrele(ill);
352511042SErik.Nordmark@Sun.COM 			rw_enter(&connp->conn_ilg_lock, RW_WRITER);
352611042SErik.Nordmark@Sun.COM 		}
35270Sstevel@tonic-gate 	}
352811042SErik.Nordmark@Sun.COM 	if (held_ilg != NULL)
352911042SErik.Nordmark@Sun.COM 		ilg_refrele(held_ilg);
353011042SErik.Nordmark@Sun.COM 	rw_exit(&connp->conn_ilg_lock);
35310Sstevel@tonic-gate }
353211042SErik.Nordmark@Sun.COM 
353311042SErik.Nordmark@Sun.COM /*
353411042SErik.Nordmark@Sun.COM  * Called when an ill is unplumbed to make sure that there are no
353511042SErik.Nordmark@Sun.COM  * dangling conn references to that ill. In that case ill is non-NULL and
353611042SErik.Nordmark@Sun.COM  * we make sure we remove all references to it.
353711042SErik.Nordmark@Sun.COM  * Also called when we should revisit the ilg_ill used for multicast
353811042SErik.Nordmark@Sun.COM  * memberships, in which case ill is NULL.
353911042SErik.Nordmark@Sun.COM  */
354011042SErik.Nordmark@Sun.COM void
update_conn_ill(ill_t * ill,ip_stack_t * ipst)354111042SErik.Nordmark@Sun.COM update_conn_ill(ill_t *ill, ip_stack_t *ipst)
354211042SErik.Nordmark@Sun.COM {
354311042SErik.Nordmark@Sun.COM 	ipcl_walk(conn_update_ill, (caddr_t)ill, ipst);
354411042SErik.Nordmark@Sun.COM }
3545