xref: /titanic_52/usr/src/uts/common/inet/ip/ip_multi.c (revision 7f125a531d922638f1af62a3d69f9ffc00bc2ee7)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
545916cd2Sjpk  * Common Development and Distribution License (the "License").
645916cd2Sjpk  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22*7f125a53SRavi Chandra Nallan  * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
23*7f125a53SRavi Chandra Nallan  * Copyright (c) 1990 Mentat Inc.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #include <sys/types.h>
277c478bd9Sstevel@tonic-gate #include <sys/stream.h>
287c478bd9Sstevel@tonic-gate #include <sys/dlpi.h>
297c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
307c478bd9Sstevel@tonic-gate #include <sys/strsun.h>
317c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
327c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
33381a2a9aSdr146992 #include <sys/sdt.h>
347c478bd9Sstevel@tonic-gate #include <sys/zone.h>
357c478bd9Sstevel@tonic-gate 
367c478bd9Sstevel@tonic-gate #include <sys/param.h>
377c478bd9Sstevel@tonic-gate #include <sys/socket.h>
387c478bd9Sstevel@tonic-gate #include <sys/sockio.h>
3945916cd2Sjpk #include <net/if.h>
407c478bd9Sstevel@tonic-gate #include <sys/systm.h>
41a1d6a1e6Sjarrett #include <sys/strsubr.h>
427c478bd9Sstevel@tonic-gate #include <net/route.h>
437c478bd9Sstevel@tonic-gate #include <netinet/in.h>
447c478bd9Sstevel@tonic-gate #include <net/if_dl.h>
457c478bd9Sstevel@tonic-gate #include <netinet/ip6.h>
467c478bd9Sstevel@tonic-gate #include <netinet/icmp6.h>
477c478bd9Sstevel@tonic-gate 
487c478bd9Sstevel@tonic-gate #include <inet/common.h>
497c478bd9Sstevel@tonic-gate #include <inet/mi.h>
507c478bd9Sstevel@tonic-gate #include <inet/nd.h>
517c478bd9Sstevel@tonic-gate #include <inet/arp.h>
527c478bd9Sstevel@tonic-gate #include <inet/ip.h>
537c478bd9Sstevel@tonic-gate #include <inet/ip6.h>
547c478bd9Sstevel@tonic-gate #include <inet/ip_if.h>
557c478bd9Sstevel@tonic-gate #include <inet/ip_ndp.h>
567c478bd9Sstevel@tonic-gate #include <inet/ip_multi.h>
577c478bd9Sstevel@tonic-gate #include <inet/ipclassifier.h>
587c478bd9Sstevel@tonic-gate #include <inet/ipsec_impl.h>
597c478bd9Sstevel@tonic-gate #include <inet/sctp_ip.h>
607c478bd9Sstevel@tonic-gate #include <inet/ip_listutils.h>
61ff550d0eSmasputra #include <inet/udp_impl.h>
627c478bd9Sstevel@tonic-gate 
637c478bd9Sstevel@tonic-gate /* igmpv3/mldv2 source filter manipulation */
647c478bd9Sstevel@tonic-gate static void	ilm_bld_flists(conn_t *conn, void *arg);
657c478bd9Sstevel@tonic-gate static void	ilm_gen_filter(ilm_t *ilm, mcast_record_t *fmode,
667c478bd9Sstevel@tonic-gate     slist_t *flist);
677c478bd9Sstevel@tonic-gate 
68bd670b35SErik Nordmark static ilm_t	*ilm_add(ill_t *ill, const in6_addr_t *group,
697c478bd9Sstevel@tonic-gate     ilg_stat_t ilgstat, mcast_record_t ilg_fmode, slist_t *ilg_flist,
70e11c3f44Smeem     zoneid_t zoneid);
717c478bd9Sstevel@tonic-gate static void	ilm_delete(ilm_t *ilm);
72bd670b35SErik Nordmark static int	ilm_numentries(ill_t *, const in6_addr_t *);
73bd670b35SErik Nordmark 
74bd670b35SErik Nordmark static ilm_t	*ip_addmulti_serial(const in6_addr_t *, ill_t *, zoneid_t,
75bd670b35SErik Nordmark     ilg_stat_t, mcast_record_t, slist_t *, int *);
76bd670b35SErik Nordmark static ilm_t	*ip_addmulti_impl(const in6_addr_t *, ill_t *,
77bd670b35SErik Nordmark     zoneid_t, ilg_stat_t, mcast_record_t, slist_t *, int *);
78bd670b35SErik Nordmark static int	ip_delmulti_serial(ilm_t *, boolean_t, boolean_t);
79bd670b35SErik Nordmark static int	ip_delmulti_impl(ilm_t *, boolean_t, boolean_t);
80bd670b35SErik Nordmark 
81bd670b35SErik Nordmark static int	ip_ll_multireq(ill_t *ill, const in6_addr_t *group,
82bd670b35SErik Nordmark     t_uscalar_t);
83bd670b35SErik Nordmark static ilg_t	*ilg_lookup(conn_t *, const in6_addr_t *, ipaddr_t ifaddr,
84bd670b35SErik Nordmark     uint_t ifindex);
85bd670b35SErik Nordmark 
86bd670b35SErik Nordmark static int	ilg_add(conn_t *connp, const in6_addr_t *group,
87bd670b35SErik Nordmark     ipaddr_t ifaddr, uint_t ifindex, ill_t *ill, mcast_record_t fmode,
88bd670b35SErik Nordmark     const in6_addr_t *v6src);
897c478bd9Sstevel@tonic-gate static void	ilg_delete(conn_t *connp, ilg_t *ilg, const in6_addr_t *src);
907c478bd9Sstevel@tonic-gate static mblk_t	*ill_create_dl(ill_t *ill, uint32_t dl_primitive,
91bd670b35SErik Nordmark     uint32_t *addr_lenp, uint32_t *addr_offp);
92bd670b35SErik Nordmark static int	ip_opt_delete_group_excl(conn_t *connp,
93bd670b35SErik Nordmark     const in6_addr_t *v6group, ipaddr_t ifaddr, uint_t ifindex,
94bd670b35SErik Nordmark     mcast_record_t fmode, const in6_addr_t *v6src);
95bd670b35SErik Nordmark 
96bd670b35SErik Nordmark static	ilm_t	*ilm_lookup(ill_t *, const in6_addr_t *, zoneid_t);
97bd670b35SErik Nordmark 
98bd670b35SErik Nordmark static int	ip_msfilter_ill(conn_t *, mblk_t *, const ip_ioctl_cmd_t *,
99bd670b35SErik Nordmark     ill_t **);
100bd670b35SErik Nordmark 
101bd670b35SErik Nordmark static void	ilg_check_detach(conn_t *, ill_t *);
102f1c454b4SSowmini Varadhan static void	ilg_check_reattach(conn_t *, ill_t *);
1037c478bd9Sstevel@tonic-gate 
1047c478bd9Sstevel@tonic-gate /*
1057c478bd9Sstevel@tonic-gate  * MT notes:
1067c478bd9Sstevel@tonic-gate  *
1077c478bd9Sstevel@tonic-gate  * Multicast joins operate on both the ilg and ilm structures. Multiple
1087c478bd9Sstevel@tonic-gate  * threads operating on an conn (socket) trying to do multicast joins
1097c478bd9Sstevel@tonic-gate  * need to synchronize when operating on the ilg. Multiple threads
1107c478bd9Sstevel@tonic-gate  * potentially operating on different conn (socket endpoints) trying to
1117c478bd9Sstevel@tonic-gate  * do multicast joins could eventually end up trying to manipulate the
112bd670b35SErik Nordmark  * ilm simulatenously and need to synchronize on the access to the ilm.
113bd670b35SErik Nordmark  * The access and lookup of the ilm, as well as other ill multicast state,
114bd670b35SErik Nordmark  * is under ill_mcast_lock.
115bd670b35SErik Nordmark  * The modifications and lookup of ilg entries is serialized using conn_ilg_lock
116bd670b35SErik Nordmark  * rwlock. An ilg will not be freed until ilg_refcnt drops to zero.
117bd670b35SErik Nordmark  *
118bd670b35SErik Nordmark  * In some cases we hold ill_mcast_lock and then acquire conn_ilg_lock, but
119bd670b35SErik Nordmark  * never the other way around.
1207c478bd9Sstevel@tonic-gate  *
1217c478bd9Sstevel@tonic-gate  * An ilm is an IP data structure used to track multicast join/leave.
1227c478bd9Sstevel@tonic-gate  * An ilm is associated with a <multicast group, ipif> tuple in IPv4 and
1237c478bd9Sstevel@tonic-gate  * with just <multicast group> in IPv6. ilm_refcnt is the number of ilg's
124bd670b35SErik Nordmark  * referencing the ilm.
125bd670b35SErik Nordmark  * The modifications and lookup of ilm entries is serialized using the
126bd670b35SErik Nordmark  * ill_mcast_lock rwlock; that lock handles all the igmp/mld modifications
127bd670b35SErik Nordmark  * of the ilm state.
128bd670b35SErik Nordmark  * ilms are created / destroyed only as writer. ilms
129bd670b35SErik Nordmark  * are not passed around. The datapath (anything outside of this file
130bd670b35SErik Nordmark  * and igmp.c) use functions that do not return ilms - just the number
131bd670b35SErik Nordmark  * of members. So we don't need a dynamic refcount of the number
1327c478bd9Sstevel@tonic-gate  * of threads holding reference to an ilm.
1337c478bd9Sstevel@tonic-gate  *
134bd670b35SErik Nordmark  * In the cases where we serially access the ilg and ilm, which happens when
135bd670b35SErik Nordmark  * we handle the applications requests to join or leave groups and sources,
136bd670b35SErik Nordmark  * we use the ill_mcast_serializer mutex to ensure that a multithreaded
137bd670b35SErik Nordmark  * application which does concurrent joins and/or leaves on the same group on
138bd670b35SErik Nordmark  * the same socket always results in a consistent order for the ilg and ilm
139bd670b35SErik Nordmark  * modifications.
1407c478bd9Sstevel@tonic-gate  *
141bd670b35SErik Nordmark  * When a multicast operation results in needing to send a message to
142bd670b35SErik Nordmark  * the driver (to join/leave a L2 multicast address), we use ill_dlpi_queue()
143bd670b35SErik Nordmark  * which serialized the DLPI requests. The IGMP/MLD code uses ill_mcast_queue()
144bd670b35SErik Nordmark  * to send IGMP/MLD IP packet to avoid dropping the lock just to send a packet.
1457c478bd9Sstevel@tonic-gate  */
1467c478bd9Sstevel@tonic-gate 
1477c478bd9Sstevel@tonic-gate #define	GETSTRUCT(structure, number)	\
1487c478bd9Sstevel@tonic-gate 	((structure *)mi_zalloc(sizeof (structure) * (number)))
1497c478bd9Sstevel@tonic-gate 
150bd670b35SErik Nordmark /*
151bd670b35SErik Nordmark  * Caller must ensure that the ilg has not been condemned
152bd670b35SErik Nordmark  * The condemned flag is only set in ilg_delete under conn_ilg_lock.
153bd670b35SErik Nordmark  *
154bd670b35SErik Nordmark  * The caller must hold conn_ilg_lock as writer.
155bd670b35SErik Nordmark  */
156bd670b35SErik Nordmark static void
157bd670b35SErik Nordmark ilg_refhold(ilg_t *ilg)
158bd670b35SErik Nordmark {
159bd670b35SErik Nordmark 	ASSERT(ilg->ilg_refcnt != 0);
160bd670b35SErik Nordmark 	ASSERT(!ilg->ilg_condemned);
161bd670b35SErik Nordmark 	ASSERT(RW_WRITE_HELD(&ilg->ilg_connp->conn_ilg_lock));
162bd670b35SErik Nordmark 
163bd670b35SErik Nordmark 	ilg->ilg_refcnt++;
164bd670b35SErik Nordmark }
165bd670b35SErik Nordmark 
166bd670b35SErik Nordmark static void
167bd670b35SErik Nordmark ilg_inactive(ilg_t *ilg)
168bd670b35SErik Nordmark {
169bd670b35SErik Nordmark 	ASSERT(ilg->ilg_ill == NULL);
170bd670b35SErik Nordmark 	ASSERT(ilg->ilg_ilm == NULL);
171bd670b35SErik Nordmark 	ASSERT(ilg->ilg_filter == NULL);
172bd670b35SErik Nordmark 	ASSERT(ilg->ilg_condemned);
173bd670b35SErik Nordmark 
174bd670b35SErik Nordmark 	/* Unlink from list */
175bd670b35SErik Nordmark 	*ilg->ilg_ptpn = ilg->ilg_next;
176bd670b35SErik Nordmark 	if (ilg->ilg_next != NULL)
177bd670b35SErik Nordmark 		ilg->ilg_next->ilg_ptpn = ilg->ilg_ptpn;
178bd670b35SErik Nordmark 	ilg->ilg_next = NULL;
179bd670b35SErik Nordmark 	ilg->ilg_ptpn = NULL;
180bd670b35SErik Nordmark 
181bd670b35SErik Nordmark 	ilg->ilg_connp = NULL;
182bd670b35SErik Nordmark 	kmem_free(ilg, sizeof (*ilg));
183bd670b35SErik Nordmark }
1847c478bd9Sstevel@tonic-gate 
1857c478bd9Sstevel@tonic-gate /*
186bd670b35SErik Nordmark  * The caller must hold conn_ilg_lock as writer.
187bd670b35SErik Nordmark  */
188bd670b35SErik Nordmark static void
189bd670b35SErik Nordmark ilg_refrele(ilg_t *ilg)
190bd670b35SErik Nordmark {
191bd670b35SErik Nordmark 	ASSERT(RW_WRITE_HELD(&ilg->ilg_connp->conn_ilg_lock));
192bd670b35SErik Nordmark 	ASSERT(ilg->ilg_refcnt != 0);
193bd670b35SErik Nordmark 	if (--ilg->ilg_refcnt == 0)
194bd670b35SErik Nordmark 		ilg_inactive(ilg);
195bd670b35SErik Nordmark }
196bd670b35SErik Nordmark 
197bd670b35SErik Nordmark /*
198bd670b35SErik Nordmark  * Acquire reference on ilg and drop reference on held_ilg.
199bd670b35SErik Nordmark  * In the case when held_ilg is the same as ilg we already have
200bd670b35SErik Nordmark  * a reference, but the held_ilg might be condemned. In that case
201bd670b35SErik Nordmark  * we avoid the ilg_refhold/rele so that we can assert in ire_refhold
202bd670b35SErik Nordmark  * that the ilg isn't condemned.
203bd670b35SErik Nordmark  */
204bd670b35SErik Nordmark static void
205bd670b35SErik Nordmark ilg_transfer_hold(ilg_t *held_ilg, ilg_t *ilg)
206bd670b35SErik Nordmark {
207bd670b35SErik Nordmark 	if (held_ilg == ilg)
208bd670b35SErik Nordmark 		return;
209bd670b35SErik Nordmark 
210bd670b35SErik Nordmark 	ilg_refhold(ilg);
211bd670b35SErik Nordmark 	if (held_ilg != NULL)
212bd670b35SErik Nordmark 		ilg_refrele(held_ilg);
213bd670b35SErik Nordmark }
214bd670b35SErik Nordmark 
215bd670b35SErik Nordmark /*
216bd670b35SErik Nordmark  * Allocate a new ilg_t and links it into conn_ilg.
217bd670b35SErik Nordmark  * Returns NULL on failure, in which case `*errp' will be
218e11c3f44Smeem  * filled in with the reason.
2197c478bd9Sstevel@tonic-gate  *
220bd670b35SErik Nordmark  * Assumes connp->conn_ilg_lock is held.
2217c478bd9Sstevel@tonic-gate  */
2227c478bd9Sstevel@tonic-gate static ilg_t *
223e11c3f44Smeem conn_ilg_alloc(conn_t *connp, int *errp)
2247c478bd9Sstevel@tonic-gate {
225bd670b35SErik Nordmark 	ilg_t *ilg;
2267c478bd9Sstevel@tonic-gate 
227bd670b35SErik Nordmark 	ASSERT(RW_WRITE_HELD(&connp->conn_ilg_lock));
2287c478bd9Sstevel@tonic-gate 
229e11c3f44Smeem 	/*
230e11c3f44Smeem 	 * If CONN_CLOSING is set, conn_ilg cleanup has begun and we must not
231e11c3f44Smeem 	 * create any ilgs.
232e11c3f44Smeem 	 */
233e11c3f44Smeem 	if (connp->conn_state_flags & CONN_CLOSING) {
234e11c3f44Smeem 		*errp = EINVAL;
235e11c3f44Smeem 		return (NULL);
236e11c3f44Smeem 	}
237e11c3f44Smeem 
238bd670b35SErik Nordmark 	ilg = kmem_zalloc(sizeof (ilg_t), KM_NOSLEEP);
239bd670b35SErik Nordmark 	if (ilg == NULL) {
240e11c3f44Smeem 		*errp = ENOMEM;
2417c478bd9Sstevel@tonic-gate 		return (NULL);
242e11c3f44Smeem 	}
2437c478bd9Sstevel@tonic-gate 
244bd670b35SErik Nordmark 	ilg->ilg_refcnt = 1;
245bd670b35SErik Nordmark 
246bd670b35SErik Nordmark 	/* Insert at head */
247bd670b35SErik Nordmark 	if (connp->conn_ilg != NULL)
248bd670b35SErik Nordmark 		connp->conn_ilg->ilg_ptpn = &ilg->ilg_next;
249bd670b35SErik Nordmark 	ilg->ilg_next = connp->conn_ilg;
250bd670b35SErik Nordmark 	ilg->ilg_ptpn = &connp->conn_ilg;
251bd670b35SErik Nordmark 	connp->conn_ilg = ilg;
252bd670b35SErik Nordmark 
253bd670b35SErik Nordmark 	ilg->ilg_connp = connp;
254bd670b35SErik Nordmark 	return (ilg);
2557c478bd9Sstevel@tonic-gate }
2567c478bd9Sstevel@tonic-gate 
2577c478bd9Sstevel@tonic-gate typedef struct ilm_fbld_s {
2587c478bd9Sstevel@tonic-gate 	ilm_t		*fbld_ilm;
2597c478bd9Sstevel@tonic-gate 	int		fbld_in_cnt;
2607c478bd9Sstevel@tonic-gate 	int		fbld_ex_cnt;
2617c478bd9Sstevel@tonic-gate 	slist_t		fbld_in;
2627c478bd9Sstevel@tonic-gate 	slist_t		fbld_ex;
2637c478bd9Sstevel@tonic-gate 	boolean_t	fbld_in_overflow;
2647c478bd9Sstevel@tonic-gate } ilm_fbld_t;
2657c478bd9Sstevel@tonic-gate 
266bd670b35SErik Nordmark /*
267bd670b35SErik Nordmark  * Caller must hold ill_mcast_lock
268bd670b35SErik Nordmark  */
2697c478bd9Sstevel@tonic-gate static void
270bd670b35SErik Nordmark ilm_bld_flists(conn_t *connp, void *arg)
2717c478bd9Sstevel@tonic-gate {
272bd670b35SErik Nordmark 	ilg_t *ilg;
2737c478bd9Sstevel@tonic-gate 	ilm_fbld_t *fbld = (ilm_fbld_t *)(arg);
2747c478bd9Sstevel@tonic-gate 	ilm_t *ilm = fbld->fbld_ilm;
2757c478bd9Sstevel@tonic-gate 	in6_addr_t *v6group = &ilm->ilm_v6addr;
2767c478bd9Sstevel@tonic-gate 
277bd670b35SErik Nordmark 	if (connp->conn_ilg == NULL)
2787c478bd9Sstevel@tonic-gate 		return;
2797c478bd9Sstevel@tonic-gate 
2807c478bd9Sstevel@tonic-gate 	/*
2817c478bd9Sstevel@tonic-gate 	 * Since we can't break out of the ipcl_walk once started, we still
2827c478bd9Sstevel@tonic-gate 	 * have to look at every conn.  But if we've already found one
2837c478bd9Sstevel@tonic-gate 	 * (EXCLUDE, NULL) list, there's no need to keep checking individual
2847c478bd9Sstevel@tonic-gate 	 * ilgs--that will be our state.
2857c478bd9Sstevel@tonic-gate 	 */
2867c478bd9Sstevel@tonic-gate 	if (fbld->fbld_ex_cnt > 0 && fbld->fbld_ex.sl_numsrc == 0)
2877c478bd9Sstevel@tonic-gate 		return;
2887c478bd9Sstevel@tonic-gate 
2897c478bd9Sstevel@tonic-gate 	/*
2907c478bd9Sstevel@tonic-gate 	 * Check this conn's ilgs to see if any are interested in our
2917c478bd9Sstevel@tonic-gate 	 * ilm (group, interface match).  If so, update the master
2927c478bd9Sstevel@tonic-gate 	 * include and exclude lists we're building in the fbld struct
2937c478bd9Sstevel@tonic-gate 	 * with this ilg's filter info.
294bd670b35SErik Nordmark 	 *
295bd670b35SErik Nordmark 	 * Note that the caller has already serialized on the ill we care
296bd670b35SErik Nordmark 	 * about.
2977c478bd9Sstevel@tonic-gate 	 */
298bd670b35SErik Nordmark 	ASSERT(MUTEX_HELD(&ilm->ilm_ill->ill_mcast_serializer));
299bd670b35SErik Nordmark 
300bd670b35SErik Nordmark 	rw_enter(&connp->conn_ilg_lock, RW_READER);
301bd670b35SErik Nordmark 	for (ilg = connp->conn_ilg; ilg != NULL; ilg = ilg->ilg_next) {
302bd670b35SErik Nordmark 		if (ilg->ilg_condemned)
303bd670b35SErik Nordmark 			continue;
304bd670b35SErik Nordmark 
305bd670b35SErik Nordmark 		/*
306bd670b35SErik Nordmark 		 * Since we are under the ill_mcast_serializer we know
307bd670b35SErik Nordmark 		 * that any ilg+ilm operations on this ilm have either
308bd670b35SErik Nordmark 		 * not started or completed, except for the last ilg
309bd670b35SErik Nordmark 		 * (the one that caused us to be called) which doesn't
310bd670b35SErik Nordmark 		 * have ilg_ilm set yet. Hence we compare using ilg_ill
311bd670b35SErik Nordmark 		 * and the address.
312bd670b35SErik Nordmark 		 */
3137c478bd9Sstevel@tonic-gate 		if ((ilg->ilg_ill == ilm->ilm_ill) &&
3147c478bd9Sstevel@tonic-gate 		    IN6_ARE_ADDR_EQUAL(&ilg->ilg_v6group, v6group)) {
3157c478bd9Sstevel@tonic-gate 			if (ilg->ilg_fmode == MODE_IS_INCLUDE) {
3167c478bd9Sstevel@tonic-gate 				fbld->fbld_in_cnt++;
3177c478bd9Sstevel@tonic-gate 				if (!fbld->fbld_in_overflow)
3187c478bd9Sstevel@tonic-gate 					l_union_in_a(&fbld->fbld_in,
3197c478bd9Sstevel@tonic-gate 					    ilg->ilg_filter,
3207c478bd9Sstevel@tonic-gate 					    &fbld->fbld_in_overflow);
3217c478bd9Sstevel@tonic-gate 			} else {
3227c478bd9Sstevel@tonic-gate 				fbld->fbld_ex_cnt++;
3237c478bd9Sstevel@tonic-gate 				/*
3247c478bd9Sstevel@tonic-gate 				 * On the first exclude list, don't try to do
3257c478bd9Sstevel@tonic-gate 				 * an intersection, as the master exclude list
3267c478bd9Sstevel@tonic-gate 				 * is intentionally empty.  If the master list
3277c478bd9Sstevel@tonic-gate 				 * is still empty on later iterations, that
3287c478bd9Sstevel@tonic-gate 				 * means we have at least one ilg with an empty
3297c478bd9Sstevel@tonic-gate 				 * exclude list, so that should be reflected
3307c478bd9Sstevel@tonic-gate 				 * when we take the intersection.
3317c478bd9Sstevel@tonic-gate 				 */
3327c478bd9Sstevel@tonic-gate 				if (fbld->fbld_ex_cnt == 1) {
3337c478bd9Sstevel@tonic-gate 					if (ilg->ilg_filter != NULL)
3347c478bd9Sstevel@tonic-gate 						l_copy(ilg->ilg_filter,
3357c478bd9Sstevel@tonic-gate 						    &fbld->fbld_ex);
3367c478bd9Sstevel@tonic-gate 				} else {
3377c478bd9Sstevel@tonic-gate 					l_intersection_in_a(&fbld->fbld_ex,
3387c478bd9Sstevel@tonic-gate 					    ilg->ilg_filter);
3397c478bd9Sstevel@tonic-gate 				}
3407c478bd9Sstevel@tonic-gate 			}
3417c478bd9Sstevel@tonic-gate 			/* there will only be one match, so break now. */
3427c478bd9Sstevel@tonic-gate 			break;
3437c478bd9Sstevel@tonic-gate 		}
3447c478bd9Sstevel@tonic-gate 	}
345bd670b35SErik Nordmark 	rw_exit(&connp->conn_ilg_lock);
3467c478bd9Sstevel@tonic-gate }
3477c478bd9Sstevel@tonic-gate 
348bd670b35SErik Nordmark /*
349bd670b35SErik Nordmark  * Caller must hold ill_mcast_lock
350bd670b35SErik Nordmark  */
3517c478bd9Sstevel@tonic-gate static void
3527c478bd9Sstevel@tonic-gate ilm_gen_filter(ilm_t *ilm, mcast_record_t *fmode, slist_t *flist)
3537c478bd9Sstevel@tonic-gate {
3547c478bd9Sstevel@tonic-gate 	ilm_fbld_t fbld;
355f4b3ec61Sdh155122 	ip_stack_t *ipst = ilm->ilm_ipst;
3567c478bd9Sstevel@tonic-gate 
3577c478bd9Sstevel@tonic-gate 	fbld.fbld_ilm = ilm;
3587c478bd9Sstevel@tonic-gate 	fbld.fbld_in_cnt = fbld.fbld_ex_cnt = 0;
3597c478bd9Sstevel@tonic-gate 	fbld.fbld_in.sl_numsrc = fbld.fbld_ex.sl_numsrc = 0;
3607c478bd9Sstevel@tonic-gate 	fbld.fbld_in_overflow = B_FALSE;
3617c478bd9Sstevel@tonic-gate 
3627c478bd9Sstevel@tonic-gate 	/* first, construct our master include and exclude lists */
363f4b3ec61Sdh155122 	ipcl_walk(ilm_bld_flists, (caddr_t)&fbld, ipst);
3647c478bd9Sstevel@tonic-gate 
3657c478bd9Sstevel@tonic-gate 	/* now use those master lists to generate the interface filter */
3667c478bd9Sstevel@tonic-gate 
3677c478bd9Sstevel@tonic-gate 	/* if include list overflowed, filter is (EXCLUDE, NULL) */
3687c478bd9Sstevel@tonic-gate 	if (fbld.fbld_in_overflow) {
3697c478bd9Sstevel@tonic-gate 		*fmode = MODE_IS_EXCLUDE;
3707c478bd9Sstevel@tonic-gate 		flist->sl_numsrc = 0;
3717c478bd9Sstevel@tonic-gate 		return;
3727c478bd9Sstevel@tonic-gate 	}
3737c478bd9Sstevel@tonic-gate 
3747c478bd9Sstevel@tonic-gate 	/* if nobody interested, interface filter is (INCLUDE, NULL) */
3757c478bd9Sstevel@tonic-gate 	if (fbld.fbld_in_cnt == 0 && fbld.fbld_ex_cnt == 0) {
3767c478bd9Sstevel@tonic-gate 		*fmode = MODE_IS_INCLUDE;
3777c478bd9Sstevel@tonic-gate 		flist->sl_numsrc = 0;
3787c478bd9Sstevel@tonic-gate 		return;
3797c478bd9Sstevel@tonic-gate 	}
3807c478bd9Sstevel@tonic-gate 
3817c478bd9Sstevel@tonic-gate 	/*
3827c478bd9Sstevel@tonic-gate 	 * If there are no exclude lists, then the interface filter
3837c478bd9Sstevel@tonic-gate 	 * is INCLUDE, with its filter list equal to fbld_in.  A single
3847c478bd9Sstevel@tonic-gate 	 * exclude list makes the interface filter EXCLUDE, with its
3857c478bd9Sstevel@tonic-gate 	 * filter list equal to (fbld_ex - fbld_in).
3867c478bd9Sstevel@tonic-gate 	 */
3877c478bd9Sstevel@tonic-gate 	if (fbld.fbld_ex_cnt == 0) {
3887c478bd9Sstevel@tonic-gate 		*fmode = MODE_IS_INCLUDE;
3897c478bd9Sstevel@tonic-gate 		l_copy(&fbld.fbld_in, flist);
3907c478bd9Sstevel@tonic-gate 	} else {
3917c478bd9Sstevel@tonic-gate 		*fmode = MODE_IS_EXCLUDE;
3927c478bd9Sstevel@tonic-gate 		l_difference(&fbld.fbld_ex, &fbld.fbld_in, flist);
3937c478bd9Sstevel@tonic-gate 	}
3947c478bd9Sstevel@tonic-gate }
3957c478bd9Sstevel@tonic-gate 
396bd670b35SErik Nordmark /*
397bd670b35SErik Nordmark  * Caller must hold ill_mcast_lock
398bd670b35SErik Nordmark  */
3997c478bd9Sstevel@tonic-gate static int
400bd670b35SErik Nordmark ilm_update_add(ilm_t *ilm, ilg_stat_t ilgstat, slist_t *ilg_flist)
4017c478bd9Sstevel@tonic-gate {
4027c478bd9Sstevel@tonic-gate 	mcast_record_t fmode;
4037c478bd9Sstevel@tonic-gate 	slist_t *flist;
4047c478bd9Sstevel@tonic-gate 	boolean_t fdefault;
4057c478bd9Sstevel@tonic-gate 	char buf[INET6_ADDRSTRLEN];
406bd670b35SErik Nordmark 	ill_t *ill = ilm->ilm_ill;
4077c478bd9Sstevel@tonic-gate 
4087c478bd9Sstevel@tonic-gate 	/*
4097c478bd9Sstevel@tonic-gate 	 * There are several cases where the ilm's filter state
4107c478bd9Sstevel@tonic-gate 	 * defaults to (EXCLUDE, NULL):
4117c478bd9Sstevel@tonic-gate 	 *	- we've had previous joins without associated ilgs
4127c478bd9Sstevel@tonic-gate 	 *	- this join has no associated ilg
4137c478bd9Sstevel@tonic-gate 	 *	- the ilg's filter state is (EXCLUDE, NULL)
4147c478bd9Sstevel@tonic-gate 	 */
4157c478bd9Sstevel@tonic-gate 	fdefault = (ilm->ilm_no_ilg_cnt > 0) ||
4167c478bd9Sstevel@tonic-gate 	    (ilgstat == ILGSTAT_NONE) || SLIST_IS_EMPTY(ilg_flist);
4177c478bd9Sstevel@tonic-gate 
4187c478bd9Sstevel@tonic-gate 	/* attempt mallocs (if needed) before doing anything else */
4197c478bd9Sstevel@tonic-gate 	if ((flist = l_alloc()) == NULL)
4207c478bd9Sstevel@tonic-gate 		return (ENOMEM);
4217c478bd9Sstevel@tonic-gate 	if (!fdefault && ilm->ilm_filter == NULL) {
4227c478bd9Sstevel@tonic-gate 		ilm->ilm_filter = l_alloc();
4237c478bd9Sstevel@tonic-gate 		if (ilm->ilm_filter == NULL) {
4247c478bd9Sstevel@tonic-gate 			l_free(flist);
4257c478bd9Sstevel@tonic-gate 			return (ENOMEM);
4267c478bd9Sstevel@tonic-gate 		}
4277c478bd9Sstevel@tonic-gate 	}
4287c478bd9Sstevel@tonic-gate 
4297c478bd9Sstevel@tonic-gate 	if (ilgstat != ILGSTAT_CHANGE)
4307c478bd9Sstevel@tonic-gate 		ilm->ilm_refcnt++;
4317c478bd9Sstevel@tonic-gate 
4327c478bd9Sstevel@tonic-gate 	if (ilgstat == ILGSTAT_NONE)
4337c478bd9Sstevel@tonic-gate 		ilm->ilm_no_ilg_cnt++;
4347c478bd9Sstevel@tonic-gate 
4357c478bd9Sstevel@tonic-gate 	/*
4367c478bd9Sstevel@tonic-gate 	 * Determine new filter state.  If it's not the default
4377c478bd9Sstevel@tonic-gate 	 * (EXCLUDE, NULL), we must walk the conn list to find
4387c478bd9Sstevel@tonic-gate 	 * any ilgs interested in this group, and re-build the
4397c478bd9Sstevel@tonic-gate 	 * ilm filter.
4407c478bd9Sstevel@tonic-gate 	 */
4417c478bd9Sstevel@tonic-gate 	if (fdefault) {
4427c478bd9Sstevel@tonic-gate 		fmode = MODE_IS_EXCLUDE;
4437c478bd9Sstevel@tonic-gate 		flist->sl_numsrc = 0;
4447c478bd9Sstevel@tonic-gate 	} else {
4457c478bd9Sstevel@tonic-gate 		ilm_gen_filter(ilm, &fmode, flist);
4467c478bd9Sstevel@tonic-gate 	}
4477c478bd9Sstevel@tonic-gate 
4487c478bd9Sstevel@tonic-gate 	/* make sure state actually changed; nothing to do if not. */
4497c478bd9Sstevel@tonic-gate 	if ((ilm->ilm_fmode == fmode) &&
4507c478bd9Sstevel@tonic-gate 	    !lists_are_different(ilm->ilm_filter, flist)) {
4517c478bd9Sstevel@tonic-gate 		l_free(flist);
4527c478bd9Sstevel@tonic-gate 		return (0);
4537c478bd9Sstevel@tonic-gate 	}
4547c478bd9Sstevel@tonic-gate 
4557c478bd9Sstevel@tonic-gate 	/* send the state change report */
45648de1bd2Skcpoon 	if (!IS_LOOPBACK(ill)) {
457bd670b35SErik Nordmark 		if (ill->ill_isv6)
4587c478bd9Sstevel@tonic-gate 			mld_statechange(ilm, fmode, flist);
4597c478bd9Sstevel@tonic-gate 		else
4607c478bd9Sstevel@tonic-gate 			igmp_statechange(ilm, fmode, flist);
4617c478bd9Sstevel@tonic-gate 	}
4627c478bd9Sstevel@tonic-gate 
4637c478bd9Sstevel@tonic-gate 	/* update the ilm state */
4647c478bd9Sstevel@tonic-gate 	ilm->ilm_fmode = fmode;
4657c478bd9Sstevel@tonic-gate 	if (flist->sl_numsrc > 0)
4667c478bd9Sstevel@tonic-gate 		l_copy(flist, ilm->ilm_filter);
4677c478bd9Sstevel@tonic-gate 	else
4687c478bd9Sstevel@tonic-gate 		CLEAR_SLIST(ilm->ilm_filter);
4697c478bd9Sstevel@tonic-gate 
4707c478bd9Sstevel@tonic-gate 	ip1dbg(("ilm_update: new if filter mode %d, group %s\n", ilm->ilm_fmode,
4717c478bd9Sstevel@tonic-gate 	    inet_ntop(AF_INET6, &ilm->ilm_v6addr, buf, sizeof (buf))));
4727c478bd9Sstevel@tonic-gate 
4737c478bd9Sstevel@tonic-gate 	l_free(flist);
4747c478bd9Sstevel@tonic-gate 	return (0);
4757c478bd9Sstevel@tonic-gate }
4767c478bd9Sstevel@tonic-gate 
477bd670b35SErik Nordmark /*
478bd670b35SErik Nordmark  * Caller must hold ill_mcast_lock
479bd670b35SErik Nordmark  */
4807c478bd9Sstevel@tonic-gate static int
481bd670b35SErik Nordmark ilm_update_del(ilm_t *ilm)
4827c478bd9Sstevel@tonic-gate {
4837c478bd9Sstevel@tonic-gate 	mcast_record_t fmode;
4847c478bd9Sstevel@tonic-gate 	slist_t *flist;
485bd670b35SErik Nordmark 	ill_t *ill = ilm->ilm_ill;
4867c478bd9Sstevel@tonic-gate 
4877c478bd9Sstevel@tonic-gate 	ip1dbg(("ilm_update_del: still %d left; updating state\n",
4887c478bd9Sstevel@tonic-gate 	    ilm->ilm_refcnt));
4897c478bd9Sstevel@tonic-gate 
4907c478bd9Sstevel@tonic-gate 	if ((flist = l_alloc()) == NULL)
4917c478bd9Sstevel@tonic-gate 		return (ENOMEM);
4927c478bd9Sstevel@tonic-gate 
4937c478bd9Sstevel@tonic-gate 	/*
4947c478bd9Sstevel@tonic-gate 	 * If present, the ilg in question has already either been
4957c478bd9Sstevel@tonic-gate 	 * updated or removed from our list; so all we need to do
4967c478bd9Sstevel@tonic-gate 	 * now is walk the list to update the ilm filter state.
4977c478bd9Sstevel@tonic-gate 	 *
4987c478bd9Sstevel@tonic-gate 	 * Skip the list walk if we have any no-ilg joins, which
4997c478bd9Sstevel@tonic-gate 	 * cause the filter state to revert to (EXCLUDE, NULL).
5007c478bd9Sstevel@tonic-gate 	 */
5017c478bd9Sstevel@tonic-gate 	if (ilm->ilm_no_ilg_cnt != 0) {
5027c478bd9Sstevel@tonic-gate 		fmode = MODE_IS_EXCLUDE;
5037c478bd9Sstevel@tonic-gate 		flist->sl_numsrc = 0;
5047c478bd9Sstevel@tonic-gate 	} else {
5057c478bd9Sstevel@tonic-gate 		ilm_gen_filter(ilm, &fmode, flist);
5067c478bd9Sstevel@tonic-gate 	}
5077c478bd9Sstevel@tonic-gate 
5087c478bd9Sstevel@tonic-gate 	/* check to see if state needs to be updated */
5097c478bd9Sstevel@tonic-gate 	if ((ilm->ilm_fmode == fmode) &&
5107c478bd9Sstevel@tonic-gate 	    (!lists_are_different(ilm->ilm_filter, flist))) {
5117c478bd9Sstevel@tonic-gate 		l_free(flist);
5127c478bd9Sstevel@tonic-gate 		return (0);
5137c478bd9Sstevel@tonic-gate 	}
5147c478bd9Sstevel@tonic-gate 
51548de1bd2Skcpoon 	if (!IS_LOOPBACK(ill)) {
516bd670b35SErik Nordmark 		if (ill->ill_isv6)
5177c478bd9Sstevel@tonic-gate 			mld_statechange(ilm, fmode, flist);
5187c478bd9Sstevel@tonic-gate 		else
5197c478bd9Sstevel@tonic-gate 			igmp_statechange(ilm, fmode, flist);
5207c478bd9Sstevel@tonic-gate 	}
5217c478bd9Sstevel@tonic-gate 
5227c478bd9Sstevel@tonic-gate 	ilm->ilm_fmode = fmode;
5237c478bd9Sstevel@tonic-gate 	if (flist->sl_numsrc > 0) {
5247c478bd9Sstevel@tonic-gate 		if (ilm->ilm_filter == NULL) {
5257c478bd9Sstevel@tonic-gate 			ilm->ilm_filter = l_alloc();
5267c478bd9Sstevel@tonic-gate 			if (ilm->ilm_filter == NULL) {
5277c478bd9Sstevel@tonic-gate 				char buf[INET6_ADDRSTRLEN];
5287c478bd9Sstevel@tonic-gate 				ip1dbg(("ilm_update_del: failed to alloc ilm "
5297c478bd9Sstevel@tonic-gate 				    "filter; no source filtering for %s on %s",
5307c478bd9Sstevel@tonic-gate 				    inet_ntop(AF_INET6, &ilm->ilm_v6addr,
5317c478bd9Sstevel@tonic-gate 				    buf, sizeof (buf)), ill->ill_name));
5327c478bd9Sstevel@tonic-gate 				ilm->ilm_fmode = MODE_IS_EXCLUDE;
5337c478bd9Sstevel@tonic-gate 				l_free(flist);
5347c478bd9Sstevel@tonic-gate 				return (0);
5357c478bd9Sstevel@tonic-gate 			}
5367c478bd9Sstevel@tonic-gate 		}
5377c478bd9Sstevel@tonic-gate 		l_copy(flist, ilm->ilm_filter);
5387c478bd9Sstevel@tonic-gate 	} else {
5397c478bd9Sstevel@tonic-gate 		CLEAR_SLIST(ilm->ilm_filter);
5407c478bd9Sstevel@tonic-gate 	}
5417c478bd9Sstevel@tonic-gate 
5427c478bd9Sstevel@tonic-gate 	l_free(flist);
5437c478bd9Sstevel@tonic-gate 	return (0);
5447c478bd9Sstevel@tonic-gate }
5457c478bd9Sstevel@tonic-gate 
5467c478bd9Sstevel@tonic-gate /*
547bd670b35SErik Nordmark  * Create/update the ilm for the group/ill. Used by other parts of IP to
548bd670b35SErik Nordmark  * do the ILGSTAT_NONE (no ilg), MODE_IS_EXCLUDE, with no slist join.
549bd670b35SErik Nordmark  * Returns with a refhold on the ilm.
5507c478bd9Sstevel@tonic-gate  *
551bd670b35SErik Nordmark  * The unspecified address means all multicast addresses for in both the
552bd670b35SErik Nordmark  * case of IPv4 and IPv6.
553bd670b35SErik Nordmark  *
554bd670b35SErik Nordmark  * The caller should have already mapped an IPMP under ill to the upper.
555bd670b35SErik Nordmark  */
556bd670b35SErik Nordmark ilm_t *
557bd670b35SErik Nordmark ip_addmulti(const in6_addr_t *v6group, ill_t *ill, zoneid_t zoneid,
558bd670b35SErik Nordmark     int *errorp)
559bd670b35SErik Nordmark {
560bd670b35SErik Nordmark 	ilm_t *ilm;
561bd670b35SErik Nordmark 
562bd670b35SErik Nordmark 	/* Acquire serializer to keep assert in ilm_bld_flists happy */
563bd670b35SErik Nordmark 	mutex_enter(&ill->ill_mcast_serializer);
564bd670b35SErik Nordmark 	ilm = ip_addmulti_serial(v6group, ill, zoneid, ILGSTAT_NONE,
565bd670b35SErik Nordmark 	    MODE_IS_EXCLUDE, NULL, errorp);
566bd670b35SErik Nordmark 	mutex_exit(&ill->ill_mcast_serializer);
567f1c454b4SSowmini Varadhan 	/*
568f1c454b4SSowmini Varadhan 	 * Now that all locks have been dropped, we can send any
569f1c454b4SSowmini Varadhan 	 * deferred/queued DLPI or IP packets
570f1c454b4SSowmini Varadhan 	 */
571f1c454b4SSowmini Varadhan 	ill_mcast_send_queued(ill);
572f1c454b4SSowmini Varadhan 	ill_dlpi_send_queued(ill);
573bd670b35SErik Nordmark 	return (ilm);
574bd670b35SErik Nordmark }
575bd670b35SErik Nordmark 
576bd670b35SErik Nordmark /*
577bd670b35SErik Nordmark  * Create/update the ilm for the group/ill. If ILGSTAT_CHANGE is not set
578bd670b35SErik Nordmark  * then this returns with a refhold on the ilm.
579bd670b35SErik Nordmark  *
580bd670b35SErik Nordmark  * Internal routine which assumes the caller has already acquired
581f1c454b4SSowmini Varadhan  * ill_mcast_serializer. It is the caller's responsibility to send out
582f1c454b4SSowmini Varadhan  * queued DLPI/multicast packets after all locks are dropped.
583bd670b35SErik Nordmark  *
584bd670b35SErik Nordmark  * The unspecified address means all multicast addresses for in both the
585bd670b35SErik Nordmark  * case of IPv4 and IPv6.
5867c478bd9Sstevel@tonic-gate  *
5877c478bd9Sstevel@tonic-gate  * ilgstat tells us if there's an ilg associated with this join,
5887c478bd9Sstevel@tonic-gate  * and if so, if it's a new ilg or a change to an existing one.
5897c478bd9Sstevel@tonic-gate  * ilg_fmode and ilg_flist give us the current filter state of
5907c478bd9Sstevel@tonic-gate  * the ilg (and will be EXCLUDE {NULL} in the case of no ilg).
591bd670b35SErik Nordmark  *
592bd670b35SErik Nordmark  * The caller should have already mapped an IPMP under ill to the upper.
5937c478bd9Sstevel@tonic-gate  */
594bd670b35SErik Nordmark static ilm_t *
595bd670b35SErik Nordmark ip_addmulti_serial(const in6_addr_t *v6group, ill_t *ill, zoneid_t zoneid,
596bd670b35SErik Nordmark     ilg_stat_t ilgstat, mcast_record_t ilg_fmode, slist_t *ilg_flist,
597bd670b35SErik Nordmark     int *errorp)
5987c478bd9Sstevel@tonic-gate {
5997c478bd9Sstevel@tonic-gate 	ilm_t *ilm;
6007c478bd9Sstevel@tonic-gate 
601bd670b35SErik Nordmark 	ASSERT(MUTEX_HELD(&ill->ill_mcast_serializer));
6027c478bd9Sstevel@tonic-gate 
603bd670b35SErik Nordmark 	if (ill->ill_isv6) {
6047c478bd9Sstevel@tonic-gate 		if (!IN6_IS_ADDR_MULTICAST(v6group) &&
6057c478bd9Sstevel@tonic-gate 		    !IN6_IS_ADDR_UNSPECIFIED(v6group)) {
606bd670b35SErik Nordmark 			*errorp = EINVAL;
607bd670b35SErik Nordmark 			return (NULL);
608bd670b35SErik Nordmark 		}
609bd670b35SErik Nordmark 	} else {
610bd670b35SErik Nordmark 		if (IN6_IS_ADDR_V4MAPPED(v6group)) {
611bd670b35SErik Nordmark 			ipaddr_t v4group;
612bd670b35SErik Nordmark 
613bd670b35SErik Nordmark 			IN6_V4MAPPED_TO_IPADDR(v6group, v4group);
614f1c454b4SSowmini Varadhan 			ASSERT(!IS_UNDER_IPMP(ill));
615bd670b35SErik Nordmark 			if (!CLASSD(v4group)) {
616bd670b35SErik Nordmark 				*errorp = EINVAL;
617bd670b35SErik Nordmark 				return (NULL);
618bd670b35SErik Nordmark 			}
619bd670b35SErik Nordmark 		} else if (!IN6_IS_ADDR_UNSPECIFIED(v6group)) {
620bd670b35SErik Nordmark 			*errorp = EINVAL;
621bd670b35SErik Nordmark 			return (NULL);
622bd670b35SErik Nordmark 		}
6237c478bd9Sstevel@tonic-gate 	}
6247c478bd9Sstevel@tonic-gate 
625bd670b35SErik Nordmark 	if (IS_UNDER_IPMP(ill)) {
626bd670b35SErik Nordmark 		*errorp = EINVAL;
627bd670b35SErik Nordmark 		return (NULL);
628bd670b35SErik Nordmark 	}
629bd670b35SErik Nordmark 
630bd670b35SErik Nordmark 	rw_enter(&ill->ill_mcast_lock, RW_WRITER);
631bd670b35SErik Nordmark 	/*
632bd670b35SErik Nordmark 	 * We do the equivalent of a lookup by checking after we get the lock
633bd670b35SErik Nordmark 	 * This is needed since the ill could have been condemned after
634bd670b35SErik Nordmark 	 * we looked it up, and we need to check condemned after we hold
635bd670b35SErik Nordmark 	 * ill_mcast_lock to synchronize with the unplumb code.
636bd670b35SErik Nordmark 	 */
637bd670b35SErik Nordmark 	if (ill->ill_state_flags & ILL_CONDEMNED) {
638bd670b35SErik Nordmark 		rw_exit(&ill->ill_mcast_lock);
639bd670b35SErik Nordmark 		*errorp = ENXIO;
640bd670b35SErik Nordmark 		return (NULL);
641bd670b35SErik Nordmark 	}
642bd670b35SErik Nordmark 	ilm = ip_addmulti_impl(v6group, ill, zoneid, ilgstat, ilg_fmode,
643bd670b35SErik Nordmark 	    ilg_flist, errorp);
644bd670b35SErik Nordmark 	rw_exit(&ill->ill_mcast_lock);
645bd670b35SErik Nordmark 
646bd670b35SErik Nordmark 	ill_mcast_timer_start(ill->ill_ipst);
647bd670b35SErik Nordmark 	return (ilm);
648bd670b35SErik Nordmark }
649bd670b35SErik Nordmark 
650bd670b35SErik Nordmark static ilm_t *
651bd670b35SErik Nordmark ip_addmulti_impl(const in6_addr_t *v6group, ill_t *ill, zoneid_t zoneid,
652bd670b35SErik Nordmark     ilg_stat_t ilgstat, mcast_record_t ilg_fmode, slist_t *ilg_flist,
653bd670b35SErik Nordmark     int *errorp)
654bd670b35SErik Nordmark {
655bd670b35SErik Nordmark 	ilm_t	*ilm;
656bd670b35SErik Nordmark 	int	ret = 0;
657bd670b35SErik Nordmark 
658bd670b35SErik Nordmark 	ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock));
659bd670b35SErik Nordmark 	*errorp = 0;
660e11c3f44Smeem 
6617c478bd9Sstevel@tonic-gate 	/*
662e11c3f44Smeem 	 * An ilm is uniquely identified by the tuple of (group, ill) where
663e11c3f44Smeem 	 * `group' is the multicast group address, and `ill' is the interface
664e11c3f44Smeem 	 * on which it is currently joined.
6657c478bd9Sstevel@tonic-gate 	 */
6667c478bd9Sstevel@tonic-gate 
667bd670b35SErik Nordmark 	ilm = ilm_lookup(ill, v6group, zoneid);
668bd670b35SErik Nordmark 	if (ilm != NULL) {
669bd670b35SErik Nordmark 		/* ilm_update_add bumps ilm_refcnt unless ILGSTAT_CHANGE */
670bd670b35SErik Nordmark 		ret = ilm_update_add(ilm, ilgstat, ilg_flist);
671bd670b35SErik Nordmark 		if (ret == 0)
672bd670b35SErik Nordmark 			return (ilm);
673bd670b35SErik Nordmark 
674bd670b35SErik Nordmark 		*errorp = ret;
675bd670b35SErik Nordmark 		return (NULL);
676bd670b35SErik Nordmark 	}
677bd670b35SErik Nordmark 
678bd670b35SErik Nordmark 	/*
679bd670b35SErik Nordmark 	 * The callers checks on the ilg and the ilg+ilm consistency under
680bd670b35SErik Nordmark 	 * ill_mcast_serializer ensures that we can not have ILGSTAT_CHANGE
681bd670b35SErik Nordmark 	 * and no ilm.
682bd670b35SErik Nordmark 	 */
683bd670b35SErik Nordmark 	ASSERT(ilgstat != ILGSTAT_CHANGE);
684bd670b35SErik Nordmark 	ilm = ilm_add(ill, v6group, ilgstat, ilg_fmode, ilg_flist, zoneid);
685bd670b35SErik Nordmark 	if (ilm == NULL) {
686bd670b35SErik Nordmark 		*errorp = ENOMEM;
687bd670b35SErik Nordmark 		return (NULL);
688bd670b35SErik Nordmark 	}
6897c478bd9Sstevel@tonic-gate 
6907c478bd9Sstevel@tonic-gate 	if (IN6_IS_ADDR_UNSPECIFIED(v6group)) {
6917c478bd9Sstevel@tonic-gate 		/*
692bd670b35SErik Nordmark 		 * If we have more then one we should not tell the driver
693bd670b35SErik Nordmark 		 * to join this time.
6947c478bd9Sstevel@tonic-gate 		 */
695bd670b35SErik Nordmark 		if (ilm_numentries(ill, v6group) == 1) {
696b127ac41SPhilip Kirk 			ret = ill_join_allmulti(ill);
6977c478bd9Sstevel@tonic-gate 		}
698bd670b35SErik Nordmark 	} else {
699bd670b35SErik Nordmark 		if (!IS_LOOPBACK(ill)) {
700bd670b35SErik Nordmark 			if (ill->ill_isv6)
7017c478bd9Sstevel@tonic-gate 				mld_joingroup(ilm);
702bd670b35SErik Nordmark 			else
703bd670b35SErik Nordmark 				igmp_joingroup(ilm);
704bd670b35SErik Nordmark 		}
7057c478bd9Sstevel@tonic-gate 
7067c478bd9Sstevel@tonic-gate 		/*
7077c478bd9Sstevel@tonic-gate 		 * If we have more then one we should not tell the driver
7087c478bd9Sstevel@tonic-gate 		 * to join this time.
7097c478bd9Sstevel@tonic-gate 		 */
710bd670b35SErik Nordmark 		if (ilm_numentries(ill, v6group) == 1) {
711bd670b35SErik Nordmark 			ret = ip_ll_multireq(ill, v6group, DL_ENABMULTI_REQ);
712bd670b35SErik Nordmark 		}
713bd670b35SErik Nordmark 	}
714bd670b35SErik Nordmark 	if (ret != 0) {
715bd670b35SErik Nordmark 		if (ret == ENETDOWN) {
716bd670b35SErik Nordmark 			char buf[INET6_ADDRSTRLEN];
7177c478bd9Sstevel@tonic-gate 
718bd670b35SErik Nordmark 			ip0dbg(("ip_addmulti: ENETDOWN for %s on %s",
719bd670b35SErik Nordmark 			    inet_ntop(AF_INET6, &ilm->ilm_v6addr,
720bd670b35SErik Nordmark 			    buf, sizeof (buf)), ill->ill_name));
721bd670b35SErik Nordmark 		}
7227c478bd9Sstevel@tonic-gate 		ilm_delete(ilm);
723bd670b35SErik Nordmark 		*errorp = ret;
724bd670b35SErik Nordmark 		return (NULL);
725bd670b35SErik Nordmark 	} else {
726bd670b35SErik Nordmark 		return (ilm);
727bd670b35SErik Nordmark 	}
7287c478bd9Sstevel@tonic-gate }
7297c478bd9Sstevel@tonic-gate 
7307c478bd9Sstevel@tonic-gate /*
731*7f125a53SRavi Chandra Nallan  * Looks up the list of multicast physical addresses this interface
732*7f125a53SRavi Chandra Nallan  * listens to. Add to the list if not present already.
733*7f125a53SRavi Chandra Nallan  */
734*7f125a53SRavi Chandra Nallan boolean_t
735*7f125a53SRavi Chandra Nallan ip_mphysaddr_add(ill_t *ill, uchar_t *hw_addr)
736*7f125a53SRavi Chandra Nallan {
737*7f125a53SRavi Chandra Nallan 	multiphysaddr_t *mpa = NULL;
738*7f125a53SRavi Chandra Nallan 	int	hw_addr_length = ill->ill_phys_addr_length;
739*7f125a53SRavi Chandra Nallan 
740*7f125a53SRavi Chandra Nallan 	mutex_enter(&ill->ill_lock);
741*7f125a53SRavi Chandra Nallan 	for (mpa = ill->ill_mphysaddr_list; mpa != NULL; mpa = mpa->mpa_next) {
742*7f125a53SRavi Chandra Nallan 		if (bcmp(hw_addr, &(mpa->mpa_addr[0]), hw_addr_length) == 0) {
743*7f125a53SRavi Chandra Nallan 			mpa->mpa_refcnt++;
744*7f125a53SRavi Chandra Nallan 			mutex_exit(&ill->ill_lock);
745*7f125a53SRavi Chandra Nallan 			return (B_FALSE);
746*7f125a53SRavi Chandra Nallan 		}
747*7f125a53SRavi Chandra Nallan 	}
748*7f125a53SRavi Chandra Nallan 
749*7f125a53SRavi Chandra Nallan 	mpa = kmem_zalloc(sizeof (multiphysaddr_t), KM_NOSLEEP);
750*7f125a53SRavi Chandra Nallan 	if (mpa == NULL) {
751*7f125a53SRavi Chandra Nallan 		/*
752*7f125a53SRavi Chandra Nallan 		 * We risk not having the multiphysadd structure. At this
753*7f125a53SRavi Chandra Nallan 		 * point we can't fail. We can't afford to not send a
754*7f125a53SRavi Chandra Nallan 		 * DL_ENABMULTI_REQ also. It is better than pre-allocating
755*7f125a53SRavi Chandra Nallan 		 * the structure and having the code to track it also.
756*7f125a53SRavi Chandra Nallan 		 */
757*7f125a53SRavi Chandra Nallan 		ip0dbg(("ip_mphysaddr_add: ENOMEM. Some multicast apps"
758*7f125a53SRavi Chandra Nallan 		    " may have issues. hw_addr: %p ill_name: %s\n",
759*7f125a53SRavi Chandra Nallan 		    (void *)hw_addr, ill->ill_name));
760*7f125a53SRavi Chandra Nallan 		mutex_exit(&ill->ill_lock);
761*7f125a53SRavi Chandra Nallan 		return (B_TRUE);
762*7f125a53SRavi Chandra Nallan 	}
763*7f125a53SRavi Chandra Nallan 	bcopy(hw_addr, &(mpa->mpa_addr[0]), hw_addr_length);
764*7f125a53SRavi Chandra Nallan 	mpa->mpa_refcnt = 1;
765*7f125a53SRavi Chandra Nallan 	mpa->mpa_next = ill->ill_mphysaddr_list;
766*7f125a53SRavi Chandra Nallan 	ill->ill_mphysaddr_list = mpa;
767*7f125a53SRavi Chandra Nallan 	mutex_exit(&ill->ill_lock);
768*7f125a53SRavi Chandra Nallan 	return (B_TRUE);
769*7f125a53SRavi Chandra Nallan }
770*7f125a53SRavi Chandra Nallan 
771*7f125a53SRavi Chandra Nallan /*
772*7f125a53SRavi Chandra Nallan  * Look up hw_addr from the list of physical multicast addresses this interface
773*7f125a53SRavi Chandra Nallan  * listens to.
774*7f125a53SRavi Chandra Nallan  * Remove the entry if the refcnt is 0
775*7f125a53SRavi Chandra Nallan  */
776*7f125a53SRavi Chandra Nallan boolean_t
777*7f125a53SRavi Chandra Nallan ip_mphysaddr_del(ill_t *ill, uchar_t *hw_addr)
778*7f125a53SRavi Chandra Nallan {
779*7f125a53SRavi Chandra Nallan 	multiphysaddr_t *mpap = NULL, **mpapp = NULL;
780*7f125a53SRavi Chandra Nallan 	int hw_addr_length = ill->ill_phys_addr_length;
781*7f125a53SRavi Chandra Nallan 	boolean_t ret = B_FALSE;
782*7f125a53SRavi Chandra Nallan 
783*7f125a53SRavi Chandra Nallan 	mutex_enter(&ill->ill_lock);
784*7f125a53SRavi Chandra Nallan 	for (mpapp = &ill->ill_mphysaddr_list; (mpap = *mpapp) != NULL;
785*7f125a53SRavi Chandra Nallan 	    mpapp = &(mpap->mpa_next)) {
786*7f125a53SRavi Chandra Nallan 		if (bcmp(hw_addr, &(mpap->mpa_addr[0]), hw_addr_length) == 0)
787*7f125a53SRavi Chandra Nallan 			break;
788*7f125a53SRavi Chandra Nallan 	}
789*7f125a53SRavi Chandra Nallan 	if (mpap == NULL) {
790*7f125a53SRavi Chandra Nallan 		/*
791*7f125a53SRavi Chandra Nallan 		 * Should be coming here only when there was a memory
792*7f125a53SRavi Chandra Nallan 		 * exhaustion and we were not able to allocate
793*7f125a53SRavi Chandra Nallan 		 * a multiphysaddr_t. We still send a DL_DISABMULTI_REQ down.
794*7f125a53SRavi Chandra Nallan 		 */
795*7f125a53SRavi Chandra Nallan 
796*7f125a53SRavi Chandra Nallan 		ip0dbg(("ip_mphysaddr_del: No entry for this addr. Some "
797*7f125a53SRavi Chandra Nallan 		    "multicast apps might have had issues. hw_addr: %p "
798*7f125a53SRavi Chandra Nallan 		    " ill_name: %s\n", (void *)hw_addr, ill->ill_name));
799*7f125a53SRavi Chandra Nallan 		ret = B_TRUE;
800*7f125a53SRavi Chandra Nallan 	} else if (--mpap->mpa_refcnt == 0) {
801*7f125a53SRavi Chandra Nallan 		*mpapp = mpap->mpa_next;
802*7f125a53SRavi Chandra Nallan 		kmem_free(mpap, sizeof (multiphysaddr_t));
803*7f125a53SRavi Chandra Nallan 		ret = B_TRUE;
804*7f125a53SRavi Chandra Nallan 	}
805*7f125a53SRavi Chandra Nallan 	mutex_exit(&ill->ill_lock);
806*7f125a53SRavi Chandra Nallan 	return (ret);
807*7f125a53SRavi Chandra Nallan }
808*7f125a53SRavi Chandra Nallan 
809*7f125a53SRavi Chandra Nallan /*
810bd670b35SErik Nordmark  * Send a multicast request to the driver for enabling or disabling
811bd670b35SErik Nordmark  * multicast reception for v6groupp address. The caller has already
812bd670b35SErik Nordmark  * checked whether it is appropriate to send one or not.
813bd670b35SErik Nordmark  *
814bd670b35SErik Nordmark  * For IPMP we switch to the cast_ill since it has the right hardware
815bd670b35SErik Nordmark  * information.
8165d460eafSCathy Zhou  */
817bd670b35SErik Nordmark static int
818bd670b35SErik Nordmark ip_ll_send_multireq(ill_t *ill, const in6_addr_t *v6groupp, t_uscalar_t prim)
8197c478bd9Sstevel@tonic-gate {
8207c478bd9Sstevel@tonic-gate 	mblk_t	*mp;
8217c478bd9Sstevel@tonic-gate 	uint32_t addrlen, addroff;
822bd670b35SErik Nordmark 	ill_t *release_ill = NULL;
823*7f125a53SRavi Chandra Nallan 	uchar_t *cp;
824bd670b35SErik Nordmark 	int err = 0;
8257c478bd9Sstevel@tonic-gate 
826bd670b35SErik Nordmark 	ASSERT(RW_LOCK_HELD(&ill->ill_mcast_lock));
8277c478bd9Sstevel@tonic-gate 
828bd670b35SErik Nordmark 	if (IS_IPMP(ill)) {
829bd670b35SErik Nordmark 		/* On the upper IPMP ill. */
830bd670b35SErik Nordmark 		release_ill = ipmp_illgrp_hold_cast_ill(ill->ill_grp);
831bd670b35SErik Nordmark 		if (release_ill == NULL) {
8327c478bd9Sstevel@tonic-gate 			/*
833bd670b35SErik Nordmark 			 * Avoid sending it down to the ipmpstub.
834bd670b35SErik Nordmark 			 * We will be called again once the members of the
835bd670b35SErik Nordmark 			 * group are in place
836e11c3f44Smeem 			 */
837bd670b35SErik Nordmark 			ip1dbg(("ip_ll_send_multireq: no cast_ill for %s %d\n",
838bd670b35SErik Nordmark 			    ill->ill_name, ill->ill_isv6));
839e11c3f44Smeem 			return (0);
840bd670b35SErik Nordmark 		}
841bd670b35SErik Nordmark 		ill = release_ill;
842bd670b35SErik Nordmark 	}
843bd670b35SErik Nordmark 	/* Create a DL_ENABMULTI_REQ or DL_DISABMULTI_REQ message. */
844bd670b35SErik Nordmark 	mp = ill_create_dl(ill, prim, &addrlen, &addroff);
845bd670b35SErik Nordmark 	if (mp == NULL) {
846bd670b35SErik Nordmark 		err = ENOMEM;
847bd670b35SErik Nordmark 		goto done;
848bd670b35SErik Nordmark 	}
849e11c3f44Smeem 
850bd670b35SErik Nordmark 	mp = ndp_mcastreq(ill, v6groupp, addrlen, addroff, mp);
851bd670b35SErik Nordmark 	if (mp == NULL) {
852bd670b35SErik Nordmark 		ip0dbg(("null from ndp_mcastreq(ill %s)\n", ill->ill_name));
853bd670b35SErik Nordmark 		err = ENOMEM;
854bd670b35SErik Nordmark 		goto done;
855bd670b35SErik Nordmark 	}
856*7f125a53SRavi Chandra Nallan 	cp = mp->b_rptr;
8575d460eafSCathy Zhou 
858*7f125a53SRavi Chandra Nallan 	switch (((union DL_primitives *)cp)->dl_primitive) {
859bd670b35SErik Nordmark 	case DL_ENABMULTI_REQ:
860*7f125a53SRavi Chandra Nallan 		cp += ((dl_enabmulti_req_t *)cp)->dl_addr_offset;
861*7f125a53SRavi Chandra Nallan 		if (!ip_mphysaddr_add(ill, cp)) {
862*7f125a53SRavi Chandra Nallan 			freemsg(mp);
863*7f125a53SRavi Chandra Nallan 			err = 0;
864*7f125a53SRavi Chandra Nallan 			goto done;
865*7f125a53SRavi Chandra Nallan 		}
866bd670b35SErik Nordmark 		mutex_enter(&ill->ill_lock);
8675d460eafSCathy Zhou 		/* Track the state if this is the first enabmulti */
8685d460eafSCathy Zhou 		if (ill->ill_dlpi_multicast_state == IDS_UNKNOWN)
8695d460eafSCathy Zhou 			ill->ill_dlpi_multicast_state = IDS_INPROGRESS;
870bd670b35SErik Nordmark 		mutex_exit(&ill->ill_lock);
871bd670b35SErik Nordmark 		break;
872*7f125a53SRavi Chandra Nallan 	case DL_DISABMULTI_REQ:
873*7f125a53SRavi Chandra Nallan 		cp += ((dl_disabmulti_req_t *)cp)->dl_addr_offset;
874*7f125a53SRavi Chandra Nallan 		if (!ip_mphysaddr_del(ill, cp)) {
875*7f125a53SRavi Chandra Nallan 			freemsg(mp);
876*7f125a53SRavi Chandra Nallan 			err = 0;
877*7f125a53SRavi Chandra Nallan 			goto done;
878*7f125a53SRavi Chandra Nallan 		}
8797c478bd9Sstevel@tonic-gate 	}
880bd670b35SErik Nordmark 	ill_dlpi_queue(ill, mp);
881bd670b35SErik Nordmark done:
882bd670b35SErik Nordmark 	if (release_ill != NULL)
883bd670b35SErik Nordmark 		ill_refrele(release_ill);
884bd670b35SErik Nordmark 	return (err);
8857c478bd9Sstevel@tonic-gate }
8867c478bd9Sstevel@tonic-gate 
8877c478bd9Sstevel@tonic-gate /*
8887c478bd9Sstevel@tonic-gate  * Send a multicast request to the driver for enabling multicast
8897c478bd9Sstevel@tonic-gate  * membership for v6group if appropriate.
8907c478bd9Sstevel@tonic-gate  */
8917c478bd9Sstevel@tonic-gate static int
892bd670b35SErik Nordmark ip_ll_multireq(ill_t *ill, const in6_addr_t *v6groupp, t_uscalar_t prim)
8937c478bd9Sstevel@tonic-gate {
8947c478bd9Sstevel@tonic-gate 	if (ill->ill_net_type != IRE_IF_RESOLVER ||
895bd670b35SErik Nordmark 	    ill->ill_ipif->ipif_flags & IPIF_POINTOPOINT) {
896bd670b35SErik Nordmark 		ip1dbg(("ip_ll_multireq: not resolver\n"));
8977c478bd9Sstevel@tonic-gate 		return (0);	/* Must be IRE_IF_NORESOLVER */
8987c478bd9Sstevel@tonic-gate 	}
8997c478bd9Sstevel@tonic-gate 
9007c478bd9Sstevel@tonic-gate 	if (ill->ill_phyint->phyint_flags & PHYI_MULTI_BCAST) {
901bd670b35SErik Nordmark 		ip1dbg(("ip_ll_multireq: MULTI_BCAST\n"));
9027c478bd9Sstevel@tonic-gate 		return (0);
9037c478bd9Sstevel@tonic-gate 	}
904bd670b35SErik Nordmark 	return (ip_ll_send_multireq(ill, v6groupp, prim));
9057c478bd9Sstevel@tonic-gate }
9067c478bd9Sstevel@tonic-gate 
9077c478bd9Sstevel@tonic-gate /*
908bd670b35SErik Nordmark  * Delete the ilm. Used by other parts of IP for the case of no_ilg/leaving
909bd670b35SErik Nordmark  * being true.
9107c478bd9Sstevel@tonic-gate  */
9117c478bd9Sstevel@tonic-gate int
912bd670b35SErik Nordmark ip_delmulti(ilm_t *ilm)
9137c478bd9Sstevel@tonic-gate {
914bd670b35SErik Nordmark 	ill_t *ill = ilm->ilm_ill;
915bd670b35SErik Nordmark 	int error;
9167c478bd9Sstevel@tonic-gate 
917bd670b35SErik Nordmark 	/* Acquire serializer to keep assert in ilm_bld_flists happy */
918bd670b35SErik Nordmark 	mutex_enter(&ill->ill_mcast_serializer);
919bd670b35SErik Nordmark 	error = ip_delmulti_serial(ilm, B_TRUE, B_TRUE);
920bd670b35SErik Nordmark 	mutex_exit(&ill->ill_mcast_serializer);
921f1c454b4SSowmini Varadhan 	/*
922f1c454b4SSowmini Varadhan 	 * Now that all locks have been dropped, we can send any
923f1c454b4SSowmini Varadhan 	 * deferred/queued DLPI or IP packets
924f1c454b4SSowmini Varadhan 	 */
925f1c454b4SSowmini Varadhan 	ill_mcast_send_queued(ill);
926f1c454b4SSowmini Varadhan 	ill_dlpi_send_queued(ill);
927bd670b35SErik Nordmark 	return (error);
9287c478bd9Sstevel@tonic-gate }
9297c478bd9Sstevel@tonic-gate 
9307c478bd9Sstevel@tonic-gate 
9317c478bd9Sstevel@tonic-gate /*
932bd670b35SErik Nordmark  * Delete the ilm.
933f1c454b4SSowmini Varadhan  * Assumes ill_mcast_serializer is held by the caller.
934f1c454b4SSowmini Varadhan  * Caller must send out queued dlpi/multicast packets after dropping
935f1c454b4SSowmini Varadhan  * all locks.
9367c478bd9Sstevel@tonic-gate  */
9377c478bd9Sstevel@tonic-gate static int
938bd670b35SErik Nordmark ip_delmulti_serial(ilm_t *ilm, boolean_t no_ilg, boolean_t leaving)
9397c478bd9Sstevel@tonic-gate {
940bd670b35SErik Nordmark 	ill_t *ill = ilm->ilm_ill;
941bd670b35SErik Nordmark 	int ret;
9427c478bd9Sstevel@tonic-gate 
943bd670b35SErik Nordmark 	ASSERT(MUTEX_HELD(&ill->ill_mcast_serializer));
944bd670b35SErik Nordmark 	ASSERT(!(IS_UNDER_IPMP(ill)));
9457c478bd9Sstevel@tonic-gate 
946bd670b35SErik Nordmark 	rw_enter(&ill->ill_mcast_lock, RW_WRITER);
947bd670b35SErik Nordmark 	ret = ip_delmulti_impl(ilm, no_ilg, leaving);
948bd670b35SErik Nordmark 	rw_exit(&ill->ill_mcast_lock);
949bd670b35SErik Nordmark 	ill_mcast_timer_start(ill->ill_ipst);
950bd670b35SErik Nordmark 	return (ret);
9517c478bd9Sstevel@tonic-gate }
952bd670b35SErik Nordmark 
953bd670b35SErik Nordmark static int
954bd670b35SErik Nordmark ip_delmulti_impl(ilm_t *ilm, boolean_t no_ilg, boolean_t leaving)
955bd670b35SErik Nordmark {
956bd670b35SErik Nordmark 	ill_t *ill = ilm->ilm_ill;
957bd670b35SErik Nordmark 	int error;
958bd670b35SErik Nordmark 	in6_addr_t v6group;
959bd670b35SErik Nordmark 
960bd670b35SErik Nordmark 	ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock));
961bd670b35SErik Nordmark 
962bd670b35SErik Nordmark 	/* Update counters */
963bd670b35SErik Nordmark 	if (no_ilg)
964bd670b35SErik Nordmark 		ilm->ilm_no_ilg_cnt--;
965bd670b35SErik Nordmark 
966bd670b35SErik Nordmark 	if (leaving)
967bd670b35SErik Nordmark 		ilm->ilm_refcnt--;
968bd670b35SErik Nordmark 
969bd670b35SErik Nordmark 	if (ilm->ilm_refcnt > 0)
970bd670b35SErik Nordmark 		return (ilm_update_del(ilm));
971bd670b35SErik Nordmark 
972bd670b35SErik Nordmark 	v6group = ilm->ilm_v6addr;
973bd670b35SErik Nordmark 
974bd670b35SErik Nordmark 	if (IN6_IS_ADDR_UNSPECIFIED(&ilm->ilm_v6addr)) {
975bd670b35SErik Nordmark 		ilm_delete(ilm);
9767c478bd9Sstevel@tonic-gate 		/*
977bd670b35SErik Nordmark 		 * If we have some left then one we should not tell the driver
978bd670b35SErik Nordmark 		 * to leave.
9797c478bd9Sstevel@tonic-gate 		 */
980bd670b35SErik Nordmark 		if (ilm_numentries(ill, &v6group) != 0)
981bd670b35SErik Nordmark 			return (0);
982bd670b35SErik Nordmark 
983bd670b35SErik Nordmark 		ill_leave_allmulti(ill);
984bd670b35SErik Nordmark 
9857c478bd9Sstevel@tonic-gate 		return (0);
9867c478bd9Sstevel@tonic-gate 	}
987bd670b35SErik Nordmark 
988bd670b35SErik Nordmark 	if (!IS_LOOPBACK(ill)) {
989bd670b35SErik Nordmark 		if (ill->ill_isv6)
990bd670b35SErik Nordmark 			mld_leavegroup(ilm);
991bd670b35SErik Nordmark 		else
992bd670b35SErik Nordmark 			igmp_leavegroup(ilm);
993bd670b35SErik Nordmark 	}
994bd670b35SErik Nordmark 
995bd670b35SErik Nordmark 	ilm_delete(ilm);
996bd670b35SErik Nordmark 	/*
997bd670b35SErik Nordmark 	 * If we have some left then one we should not tell the driver
998bd670b35SErik Nordmark 	 * to leave.
999bd670b35SErik Nordmark 	 */
1000bd670b35SErik Nordmark 	if (ilm_numentries(ill, &v6group) != 0)
1001bd670b35SErik Nordmark 		return (0);
1002bd670b35SErik Nordmark 
1003bd670b35SErik Nordmark 	error = ip_ll_multireq(ill, &v6group, DL_DISABMULTI_REQ);
1004bd670b35SErik Nordmark 	/* We ignore the case when ill_dl_up is not set */
1005bd670b35SErik Nordmark 	if (error == ENETDOWN) {
1006bd670b35SErik Nordmark 		char buf[INET6_ADDRSTRLEN];
1007bd670b35SErik Nordmark 
1008bd670b35SErik Nordmark 		ip0dbg(("ip_delmulti: ENETDOWN for %s on %s",
1009bd670b35SErik Nordmark 		    inet_ntop(AF_INET6, &v6group, buf, sizeof (buf)),
1010bd670b35SErik Nordmark 		    ill->ill_name));
1011bd670b35SErik Nordmark 	}
1012bd670b35SErik Nordmark 	return (error);
10137c478bd9Sstevel@tonic-gate }
10147c478bd9Sstevel@tonic-gate 
10157c478bd9Sstevel@tonic-gate /*
1016bd670b35SErik Nordmark  * Make the driver pass up all multicast packets.
10177c478bd9Sstevel@tonic-gate  */
10187c478bd9Sstevel@tonic-gate int
1019b127ac41SPhilip Kirk ill_join_allmulti(ill_t *ill)
10207c478bd9Sstevel@tonic-gate {
1021bd670b35SErik Nordmark 	mblk_t		*promiscon_mp, *promiscoff_mp = NULL;
10227c478bd9Sstevel@tonic-gate 	uint32_t	addrlen, addroff;
1023bd670b35SErik Nordmark 	ill_t		*release_ill = NULL;
10247c478bd9Sstevel@tonic-gate 
1025bd670b35SErik Nordmark 	ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock));
10267c478bd9Sstevel@tonic-gate 
10270e0e37a8SErik Nordmark 	if (IS_LOOPBACK(ill))
10280e0e37a8SErik Nordmark 		return (0);
10290e0e37a8SErik Nordmark 
1030e6ed03fcSmeem 	if (!ill->ill_dl_up) {
10317c478bd9Sstevel@tonic-gate 		/*
10327c478bd9Sstevel@tonic-gate 		 * Nobody there. All multicast addresses will be re-joined
10337c478bd9Sstevel@tonic-gate 		 * when we get the DL_BIND_ACK bringing the interface up.
10347c478bd9Sstevel@tonic-gate 		 */
1035bd670b35SErik Nordmark 		return (ENETDOWN);
10367c478bd9Sstevel@tonic-gate 	}
10377c478bd9Sstevel@tonic-gate 
1038bd670b35SErik Nordmark 	if (IS_IPMP(ill)) {
1039bd670b35SErik Nordmark 		/* On the upper IPMP ill. */
1040bd670b35SErik Nordmark 		release_ill = ipmp_illgrp_hold_cast_ill(ill->ill_grp);
1041bd670b35SErik Nordmark 		if (release_ill == NULL) {
1042e11c3f44Smeem 			/*
1043bd670b35SErik Nordmark 			 * Avoid sending it down to the ipmpstub.
1044bd670b35SErik Nordmark 			 * We will be called again once the members of the
1045bd670b35SErik Nordmark 			 * group are in place
1046e11c3f44Smeem 			 */
1047bd670b35SErik Nordmark 			ip1dbg(("ill_join_allmulti: no cast_ill for %s %d\n",
1048bd670b35SErik Nordmark 			    ill->ill_name, ill->ill_isv6));
1049e11c3f44Smeem 			return (0);
1050bd670b35SErik Nordmark 		}
1051bd670b35SErik Nordmark 		ill = release_ill;
1052bd670b35SErik Nordmark 		if (!ill->ill_dl_up) {
1053bd670b35SErik Nordmark 			ill_refrele(ill);
1054bd670b35SErik Nordmark 			return (ENETDOWN);
1055bd670b35SErik Nordmark 		}
1056bd670b35SErik Nordmark 	}
10577c478bd9Sstevel@tonic-gate 
10587c478bd9Sstevel@tonic-gate 	/*
1059b127ac41SPhilip Kirk 	 * Create a DL_PROMISCON_REQ message and send it directly to the DLPI
1060b127ac41SPhilip Kirk 	 * provider.  We don't need to do this for certain media types for
1061b127ac41SPhilip Kirk 	 * which we never need to turn promiscuous mode on.  While we're here,
1062b127ac41SPhilip Kirk 	 * pre-allocate a DL_PROMISCOFF_REQ message to make sure that
1063b127ac41SPhilip Kirk 	 * ill_leave_allmulti() will not fail due to low memory conditions.
10647c478bd9Sstevel@tonic-gate 	 */
10657c478bd9Sstevel@tonic-gate 	if ((ill->ill_net_type == IRE_IF_RESOLVER) &&
10667c478bd9Sstevel@tonic-gate 	    !(ill->ill_phyint->phyint_flags & PHYI_MULTI_BCAST)) {
1067b127ac41SPhilip Kirk 		promiscon_mp = ill_create_dl(ill, DL_PROMISCON_REQ,
1068bd670b35SErik Nordmark 		    &addrlen, &addroff);
1069bd670b35SErik Nordmark 		if (ill->ill_promiscoff_mp == NULL)
1070b127ac41SPhilip Kirk 			promiscoff_mp = ill_create_dl(ill, DL_PROMISCOFF_REQ,
1071bd670b35SErik Nordmark 			    &addrlen, &addroff);
1072bd670b35SErik Nordmark 		if (promiscon_mp == NULL ||
1073bd670b35SErik Nordmark 		    (ill->ill_promiscoff_mp == NULL && promiscoff_mp == NULL)) {
1074b127ac41SPhilip Kirk 			freemsg(promiscon_mp);
1075b127ac41SPhilip Kirk 			freemsg(promiscoff_mp);
1076bd670b35SErik Nordmark 			if (release_ill != NULL)
1077bd670b35SErik Nordmark 				ill_refrele(release_ill);
10787c478bd9Sstevel@tonic-gate 			return (ENOMEM);
1079b127ac41SPhilip Kirk 		}
1080bd670b35SErik Nordmark 		if (ill->ill_promiscoff_mp == NULL)
1081b127ac41SPhilip Kirk 			ill->ill_promiscoff_mp = promiscoff_mp;
1082bd670b35SErik Nordmark 		ill_dlpi_queue(ill, promiscon_mp);
10837c478bd9Sstevel@tonic-gate 	}
1084bd670b35SErik Nordmark 	if (release_ill != NULL)
1085bd670b35SErik Nordmark 		ill_refrele(release_ill);
10867c478bd9Sstevel@tonic-gate 	return (0);
10877c478bd9Sstevel@tonic-gate }
10887c478bd9Sstevel@tonic-gate 
10897c478bd9Sstevel@tonic-gate /*
10907c478bd9Sstevel@tonic-gate  * Make the driver stop passing up all multicast packets
10917c478bd9Sstevel@tonic-gate  */
1092b127ac41SPhilip Kirk void
1093b127ac41SPhilip Kirk ill_leave_allmulti(ill_t *ill)
10947c478bd9Sstevel@tonic-gate {
1095e11c3f44Smeem 	mblk_t	*promiscoff_mp;
1096bd670b35SErik Nordmark 	ill_t	*release_ill = NULL;
10977c478bd9Sstevel@tonic-gate 
1098bd670b35SErik Nordmark 	ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock));
10997c478bd9Sstevel@tonic-gate 
11000e0e37a8SErik Nordmark 	if (IS_LOOPBACK(ill))
11010e0e37a8SErik Nordmark 		return;
11020e0e37a8SErik Nordmark 
1103e6ed03fcSmeem 	if (!ill->ill_dl_up) {
11047c478bd9Sstevel@tonic-gate 		/*
11057c478bd9Sstevel@tonic-gate 		 * Nobody there. All multicast addresses will be re-joined
11067c478bd9Sstevel@tonic-gate 		 * when we get the DL_BIND_ACK bringing the interface up.
11077c478bd9Sstevel@tonic-gate 		 */
1108b127ac41SPhilip Kirk 		return;
11097c478bd9Sstevel@tonic-gate 	}
11107c478bd9Sstevel@tonic-gate 
1111bd670b35SErik Nordmark 	if (IS_IPMP(ill)) {
1112bd670b35SErik Nordmark 		/* On the upper IPMP ill. */
1113bd670b35SErik Nordmark 		release_ill = ipmp_illgrp_hold_cast_ill(ill->ill_grp);
1114bd670b35SErik Nordmark 		if (release_ill == NULL) {
1115e11c3f44Smeem 			/*
1116bd670b35SErik Nordmark 			 * Avoid sending it down to the ipmpstub.
1117bd670b35SErik Nordmark 			 * We will be called again once the members of the
1118bd670b35SErik Nordmark 			 * group are in place
1119e11c3f44Smeem 			 */
1120bd670b35SErik Nordmark 			ip1dbg(("ill_leave_allmulti: no cast_ill on %s %d\n",
1121bd670b35SErik Nordmark 			    ill->ill_name, ill->ill_isv6));
1122e11c3f44Smeem 			return;
1123bd670b35SErik Nordmark 		}
1124bd670b35SErik Nordmark 		ill = release_ill;
1125bd670b35SErik Nordmark 		if (!ill->ill_dl_up)
1126bd670b35SErik Nordmark 			goto done;
1127bd670b35SErik Nordmark 	}
11287c478bd9Sstevel@tonic-gate 
11297c478bd9Sstevel@tonic-gate 	/*
1130bd670b35SErik Nordmark 	 * In the case of IPMP and ill_dl_up not being set when we joined
1131bd670b35SErik Nordmark 	 * we didn't allocate a promiscoff_mp. In that case we have
1132bd670b35SErik Nordmark 	 * nothing to do when we leave.
1133bd670b35SErik Nordmark 	 * Ditto for PHYI_MULTI_BCAST
11347c478bd9Sstevel@tonic-gate 	 */
1135e11c3f44Smeem 	promiscoff_mp = ill->ill_promiscoff_mp;
1136bd670b35SErik Nordmark 	if (promiscoff_mp != NULL) {
1137b127ac41SPhilip Kirk 		ill->ill_promiscoff_mp = NULL;
1138bd670b35SErik Nordmark 		ill_dlpi_queue(ill, promiscoff_mp);
11397c478bd9Sstevel@tonic-gate 	}
1140bd670b35SErik Nordmark done:
1141bd670b35SErik Nordmark 	if (release_ill != NULL)
1142bd670b35SErik Nordmark 		ill_refrele(release_ill);
1143b127ac41SPhilip Kirk }
1144b127ac41SPhilip Kirk 
1145b127ac41SPhilip Kirk int
1146b127ac41SPhilip Kirk ip_join_allmulti(uint_t ifindex, boolean_t isv6, ip_stack_t *ipst)
1147b127ac41SPhilip Kirk {
1148b127ac41SPhilip Kirk 	ill_t		*ill;
1149bd670b35SErik Nordmark 	int		ret;
1150bd670b35SErik Nordmark 	ilm_t		*ilm;
1151b127ac41SPhilip Kirk 
1152bd670b35SErik Nordmark 	ill = ill_lookup_on_ifindex(ifindex, isv6, ipst);
1153bd670b35SErik Nordmark 	if (ill == NULL)
1154b127ac41SPhilip Kirk 		return (ENODEV);
1155e11c3f44Smeem 
1156e11c3f44Smeem 	/*
1157bd670b35SErik Nordmark 	 * The ip_addmulti() function doesn't allow IPMP underlying interfaces
1158e11c3f44Smeem 	 * to join allmulti since only the nominated underlying interface in
1159e11c3f44Smeem 	 * the group should receive multicast.  We silently succeed to avoid
1160e11c3f44Smeem 	 * having to teach IPobs (currently the only caller of this routine)
1161e11c3f44Smeem 	 * to ignore failures in this case.
1162e11c3f44Smeem 	 */
1163bd670b35SErik Nordmark 	if (IS_UNDER_IPMP(ill)) {
1164bd670b35SErik Nordmark 		ill_refrele(ill);
1165bd670b35SErik Nordmark 		return (0);
1166b127ac41SPhilip Kirk 	}
1167bd670b35SErik Nordmark 	mutex_enter(&ill->ill_lock);
1168bd670b35SErik Nordmark 	if (ill->ill_ipallmulti_cnt > 0) {
1169bd670b35SErik Nordmark 		/* Already joined */
1170bd670b35SErik Nordmark 		ASSERT(ill->ill_ipallmulti_ilm != NULL);
1171b127ac41SPhilip Kirk 		ill->ill_ipallmulti_cnt++;
1172bd670b35SErik Nordmark 		mutex_exit(&ill->ill_lock);
1173bd670b35SErik Nordmark 		goto done;
1174bd670b35SErik Nordmark 	}
1175bd670b35SErik Nordmark 	mutex_exit(&ill->ill_lock);
1176bd670b35SErik Nordmark 
1177bd670b35SErik Nordmark 	ilm = ip_addmulti(&ipv6_all_zeros, ill, ill->ill_zoneid, &ret);
1178bd670b35SErik Nordmark 	if (ilm == NULL) {
1179bd670b35SErik Nordmark 		ASSERT(ret != 0);
1180bd670b35SErik Nordmark 		ill_refrele(ill);
1181b127ac41SPhilip Kirk 		return (ret);
1182b127ac41SPhilip Kirk 	}
1183b127ac41SPhilip Kirk 
1184bd670b35SErik Nordmark 	mutex_enter(&ill->ill_lock);
1185bd670b35SErik Nordmark 	if (ill->ill_ipallmulti_cnt > 0) {
1186bd670b35SErik Nordmark 		/* Another thread added it concurrently */
1187bd670b35SErik Nordmark 		(void) ip_delmulti(ilm);
1188bd670b35SErik Nordmark 		mutex_exit(&ill->ill_lock);
1189bd670b35SErik Nordmark 		goto done;
1190bd670b35SErik Nordmark 	}
1191bd670b35SErik Nordmark 	ASSERT(ill->ill_ipallmulti_ilm == NULL);
1192bd670b35SErik Nordmark 	ill->ill_ipallmulti_ilm = ilm;
1193bd670b35SErik Nordmark 	ill->ill_ipallmulti_cnt++;
1194bd670b35SErik Nordmark 	mutex_exit(&ill->ill_lock);
1195bd670b35SErik Nordmark done:
1196bd670b35SErik Nordmark 	ill_refrele(ill);
1197bd670b35SErik Nordmark 	return (0);
1198bd670b35SErik Nordmark }
1199e11c3f44Smeem 
1200b127ac41SPhilip Kirk int
1201b127ac41SPhilip Kirk ip_leave_allmulti(uint_t ifindex, boolean_t isv6, ip_stack_t *ipst)
1202b127ac41SPhilip Kirk {
1203b127ac41SPhilip Kirk 	ill_t		*ill;
1204bd670b35SErik Nordmark 	ilm_t		*ilm;
1205b127ac41SPhilip Kirk 
1206bd670b35SErik Nordmark 	ill = ill_lookup_on_ifindex(ifindex, isv6, ipst);
1207bd670b35SErik Nordmark 	if (ill == NULL)
1208b127ac41SPhilip Kirk 		return (ENODEV);
1209e11c3f44Smeem 
1210bd670b35SErik Nordmark 	if (IS_UNDER_IPMP(ill)) {
1211bd670b35SErik Nordmark 		ill_refrele(ill);
1212bd670b35SErik Nordmark 		return (0);
1213bd670b35SErik Nordmark 	}
1214bd670b35SErik Nordmark 
1215bd670b35SErik Nordmark 	mutex_enter(&ill->ill_lock);
1216bd670b35SErik Nordmark 	if (ill->ill_ipallmulti_cnt == 0) {
1217bd670b35SErik Nordmark 		/* ip_purge_allmulti could have removed them all */
1218bd670b35SErik Nordmark 		mutex_exit(&ill->ill_lock);
1219bd670b35SErik Nordmark 		goto done;
1220b127ac41SPhilip Kirk 	}
1221b127ac41SPhilip Kirk 	ill->ill_ipallmulti_cnt--;
1222bd670b35SErik Nordmark 	if (ill->ill_ipallmulti_cnt == 0) {
1223bd670b35SErik Nordmark 		/* Last one */
1224bd670b35SErik Nordmark 		ilm = ill->ill_ipallmulti_ilm;
1225bd670b35SErik Nordmark 		ill->ill_ipallmulti_ilm = NULL;
1226bd670b35SErik Nordmark 	} else {
1227bd670b35SErik Nordmark 		ilm = NULL;
1228e11c3f44Smeem 	}
1229bd670b35SErik Nordmark 	mutex_exit(&ill->ill_lock);
1230bd670b35SErik Nordmark 	if (ilm != NULL)
1231bd670b35SErik Nordmark 		(void) ip_delmulti(ilm);
1232bd670b35SErik Nordmark 
1233bd670b35SErik Nordmark done:
1234bd670b35SErik Nordmark 	ill_refrele(ill);
12357c478bd9Sstevel@tonic-gate 	return (0);
12367c478bd9Sstevel@tonic-gate }
12377c478bd9Sstevel@tonic-gate 
12387c478bd9Sstevel@tonic-gate /*
1239b127ac41SPhilip Kirk  * Delete the allmulti memberships that were added as part of
1240b127ac41SPhilip Kirk  * ip_join_allmulti().
1241b127ac41SPhilip Kirk  */
1242b127ac41SPhilip Kirk void
1243b127ac41SPhilip Kirk ip_purge_allmulti(ill_t *ill)
1244b127ac41SPhilip Kirk {
1245bd670b35SErik Nordmark 	ilm_t	*ilm;
1246bd670b35SErik Nordmark 
1247b127ac41SPhilip Kirk 	ASSERT(IAM_WRITER_ILL(ill));
1248b127ac41SPhilip Kirk 
1249bd670b35SErik Nordmark 	mutex_enter(&ill->ill_lock);
1250bd670b35SErik Nordmark 	ilm = ill->ill_ipallmulti_ilm;
1251bd670b35SErik Nordmark 	ill->ill_ipallmulti_ilm = NULL;
1252bd670b35SErik Nordmark 	ill->ill_ipallmulti_cnt = 0;
1253bd670b35SErik Nordmark 	mutex_exit(&ill->ill_lock);
1254bd670b35SErik Nordmark 
1255bd670b35SErik Nordmark 	if (ilm != NULL)
1256bd670b35SErik Nordmark 		(void) ip_delmulti(ilm);
1257b127ac41SPhilip Kirk }
1258b127ac41SPhilip Kirk 
1259b127ac41SPhilip Kirk /*
1260bd670b35SErik Nordmark  * Create a dlpi message with room for phys+sap. Later
1261bd670b35SErik Nordmark  * we will strip the sap for those primitives which
1262bd670b35SErik Nordmark  * only need a physical address.
12637c478bd9Sstevel@tonic-gate  */
12647c478bd9Sstevel@tonic-gate static mblk_t *
1265bd670b35SErik Nordmark ill_create_dl(ill_t *ill, uint32_t dl_primitive,
12667c478bd9Sstevel@tonic-gate     uint32_t *addr_lenp, uint32_t *addr_offp)
12677c478bd9Sstevel@tonic-gate {
12687c478bd9Sstevel@tonic-gate 	mblk_t	*mp;
12697c478bd9Sstevel@tonic-gate 	uint32_t	hw_addr_length;
12707c478bd9Sstevel@tonic-gate 	char		*cp;
12717c478bd9Sstevel@tonic-gate 	uint32_t	offset;
1272bd670b35SErik Nordmark 	uint32_t	length;
12737c478bd9Sstevel@tonic-gate 	uint32_t 	size;
12747c478bd9Sstevel@tonic-gate 
12757c478bd9Sstevel@tonic-gate 	*addr_lenp = *addr_offp = 0;
12767c478bd9Sstevel@tonic-gate 
12777c478bd9Sstevel@tonic-gate 	hw_addr_length = ill->ill_phys_addr_length;
12787c478bd9Sstevel@tonic-gate 	if (!hw_addr_length) {
12797c478bd9Sstevel@tonic-gate 		ip0dbg(("ip_create_dl: hw addr length = 0\n"));
12807c478bd9Sstevel@tonic-gate 		return (NULL);
12817c478bd9Sstevel@tonic-gate 	}
12827c478bd9Sstevel@tonic-gate 
12837c478bd9Sstevel@tonic-gate 	switch (dl_primitive) {
12847c478bd9Sstevel@tonic-gate 	case DL_ENABMULTI_REQ:
1285bd670b35SErik Nordmark 		length = sizeof (dl_enabmulti_req_t);
1286bd670b35SErik Nordmark 		size = length + hw_addr_length;
1287bd670b35SErik Nordmark 		break;
12887c478bd9Sstevel@tonic-gate 	case DL_DISABMULTI_REQ:
1289bd670b35SErik Nordmark 		length = sizeof (dl_disabmulti_req_t);
1290bd670b35SErik Nordmark 		size = length + hw_addr_length;
12917c478bd9Sstevel@tonic-gate 		break;
12927c478bd9Sstevel@tonic-gate 	case DL_PROMISCON_REQ:
12937c478bd9Sstevel@tonic-gate 	case DL_PROMISCOFF_REQ:
1294bd670b35SErik Nordmark 		size = length = sizeof (dl_promiscon_req_t);
12957c478bd9Sstevel@tonic-gate 		break;
12967c478bd9Sstevel@tonic-gate 	default:
12977c478bd9Sstevel@tonic-gate 		return (NULL);
12987c478bd9Sstevel@tonic-gate 	}
12997c478bd9Sstevel@tonic-gate 	mp = allocb(size, BPRI_HI);
13007c478bd9Sstevel@tonic-gate 	if (!mp)
13017c478bd9Sstevel@tonic-gate 		return (NULL);
13027c478bd9Sstevel@tonic-gate 	mp->b_wptr += size;
13037c478bd9Sstevel@tonic-gate 	mp->b_datap->db_type = M_PROTO;
13047c478bd9Sstevel@tonic-gate 
13057c478bd9Sstevel@tonic-gate 	cp = (char *)mp->b_rptr;
13067c478bd9Sstevel@tonic-gate 	offset = length;
13077c478bd9Sstevel@tonic-gate 
13087c478bd9Sstevel@tonic-gate 	switch (dl_primitive) {
13097c478bd9Sstevel@tonic-gate 	case DL_ENABMULTI_REQ: {
13107c478bd9Sstevel@tonic-gate 		dl_enabmulti_req_t *dl = (dl_enabmulti_req_t *)cp;
13117c478bd9Sstevel@tonic-gate 
13127c478bd9Sstevel@tonic-gate 		dl->dl_primitive = dl_primitive;
13137c478bd9Sstevel@tonic-gate 		dl->dl_addr_offset = offset;
13147c478bd9Sstevel@tonic-gate 		*addr_lenp = dl->dl_addr_length = hw_addr_length;
13157c478bd9Sstevel@tonic-gate 		*addr_offp = offset;
13167c478bd9Sstevel@tonic-gate 		break;
13177c478bd9Sstevel@tonic-gate 	}
13187c478bd9Sstevel@tonic-gate 	case DL_DISABMULTI_REQ: {
13197c478bd9Sstevel@tonic-gate 		dl_disabmulti_req_t *dl = (dl_disabmulti_req_t *)cp;
13207c478bd9Sstevel@tonic-gate 
13217c478bd9Sstevel@tonic-gate 		dl->dl_primitive = dl_primitive;
13227c478bd9Sstevel@tonic-gate 		dl->dl_addr_offset = offset;
13237c478bd9Sstevel@tonic-gate 		*addr_lenp = dl->dl_addr_length = hw_addr_length;
13247c478bd9Sstevel@tonic-gate 		*addr_offp = offset;
13257c478bd9Sstevel@tonic-gate 		break;
13267c478bd9Sstevel@tonic-gate 	}
13277c478bd9Sstevel@tonic-gate 	case DL_PROMISCON_REQ:
13287c478bd9Sstevel@tonic-gate 	case DL_PROMISCOFF_REQ: {
13297c478bd9Sstevel@tonic-gate 		dl_promiscon_req_t *dl = (dl_promiscon_req_t *)cp;
13307c478bd9Sstevel@tonic-gate 
13317c478bd9Sstevel@tonic-gate 		dl->dl_primitive = dl_primitive;
13327c478bd9Sstevel@tonic-gate 		dl->dl_level = DL_PROMISC_MULTI;
13337c478bd9Sstevel@tonic-gate 		break;
13347c478bd9Sstevel@tonic-gate 	}
13357c478bd9Sstevel@tonic-gate 	}
13367c478bd9Sstevel@tonic-gate 	ip1dbg(("ill_create_dl: addr_len %d, addr_off %d\n",
13377c478bd9Sstevel@tonic-gate 	    *addr_lenp, *addr_offp));
13387c478bd9Sstevel@tonic-gate 	return (mp);
13397c478bd9Sstevel@tonic-gate }
13407c478bd9Sstevel@tonic-gate 
13417c478bd9Sstevel@tonic-gate /*
1342bd670b35SErik Nordmark  * Rejoin any groups for which we have ilms.
1343bd670b35SErik Nordmark  *
1344bd670b35SErik Nordmark  * This is only needed for IPMP when the cast_ill changes since that
1345bd670b35SErik Nordmark  * change is invisible to the ilm. Other interface changes are handled
1346bd670b35SErik Nordmark  * by conn_update_ill.
13477c478bd9Sstevel@tonic-gate  */
13487c478bd9Sstevel@tonic-gate void
13497c478bd9Sstevel@tonic-gate ill_recover_multicast(ill_t *ill)
13507c478bd9Sstevel@tonic-gate {
13517c478bd9Sstevel@tonic-gate 	ilm_t	*ilm;
13527c478bd9Sstevel@tonic-gate 	char    addrbuf[INET6_ADDRSTRLEN];
13537c478bd9Sstevel@tonic-gate 
1354b127ac41SPhilip Kirk 	ill->ill_need_recover_multicast = 0;
1355b127ac41SPhilip Kirk 
1356bd670b35SErik Nordmark 	rw_enter(&ill->ill_mcast_lock, RW_WRITER);
13577c478bd9Sstevel@tonic-gate 	for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
13587c478bd9Sstevel@tonic-gate 		/*
1359bd670b35SErik Nordmark 		 * If we have more then one ilm for the group (e.g., with
1360bd670b35SErik Nordmark 		 * different zoneid) then we should not tell the driver
1361bd670b35SErik Nordmark 		 * to join unless this is the first ilm for the group.
13627c478bd9Sstevel@tonic-gate 		 */
1363bd670b35SErik Nordmark 		if (ilm_numentries(ill, &ilm->ilm_v6addr) > 1 &&
1364bd670b35SErik Nordmark 		    ilm_lookup(ill, &ilm->ilm_v6addr, ALL_ZONES) != ilm) {
13657c478bd9Sstevel@tonic-gate 			continue;
1366e11c3f44Smeem 		}
1367e11c3f44Smeem 
1368e11c3f44Smeem 		ip1dbg(("ill_recover_multicast: %s\n", inet_ntop(AF_INET6,
1369e11c3f44Smeem 		    &ilm->ilm_v6addr, addrbuf, sizeof (addrbuf))));
1370e11c3f44Smeem 
13717c478bd9Sstevel@tonic-gate 		if (IN6_IS_ADDR_UNSPECIFIED(&ilm->ilm_v6addr)) {
1372b127ac41SPhilip Kirk 			(void) ill_join_allmulti(ill);
13737c478bd9Sstevel@tonic-gate 		} else {
1374e11c3f44Smeem 			if (ill->ill_isv6)
1375e11c3f44Smeem 				mld_joingroup(ilm);
1376e11c3f44Smeem 			else
1377e11c3f44Smeem 				igmp_joingroup(ilm);
1378e11c3f44Smeem 
1379bd670b35SErik Nordmark 			(void) ip_ll_multireq(ill, &ilm->ilm_v6addr,
1380bd670b35SErik Nordmark 			    DL_ENABMULTI_REQ);
13817c478bd9Sstevel@tonic-gate 		}
13827c478bd9Sstevel@tonic-gate 	}
1383bd670b35SErik Nordmark 	rw_exit(&ill->ill_mcast_lock);
1384bd670b35SErik Nordmark 	/* Send any deferred/queued DLPI or IP packets */
1385bd670b35SErik Nordmark 	ill_mcast_send_queued(ill);
1386bd670b35SErik Nordmark 	ill_dlpi_send_queued(ill);
1387bd670b35SErik Nordmark 	ill_mcast_timer_start(ill->ill_ipst);
13887c478bd9Sstevel@tonic-gate }
13897c478bd9Sstevel@tonic-gate 
13907c478bd9Sstevel@tonic-gate /*
13917c478bd9Sstevel@tonic-gate  * The opposite of ill_recover_multicast() -- leaves all multicast groups
1392e11c3f44Smeem  * that were explicitly joined.
1393bd670b35SErik Nordmark  *
1394bd670b35SErik Nordmark  * This is only needed for IPMP when the cast_ill changes since that
1395bd670b35SErik Nordmark  * change is invisible to the ilm. Other interface changes are handled
1396bd670b35SErik Nordmark  * by conn_update_ill.
13977c478bd9Sstevel@tonic-gate  */
13987c478bd9Sstevel@tonic-gate void
13997c478bd9Sstevel@tonic-gate ill_leave_multicast(ill_t *ill)
14007c478bd9Sstevel@tonic-gate {
14017c478bd9Sstevel@tonic-gate 	ilm_t	*ilm;
14027c478bd9Sstevel@tonic-gate 	char    addrbuf[INET6_ADDRSTRLEN];
14037c478bd9Sstevel@tonic-gate 
1404b127ac41SPhilip Kirk 	ill->ill_need_recover_multicast = 1;
1405b127ac41SPhilip Kirk 
1406bd670b35SErik Nordmark 	rw_enter(&ill->ill_mcast_lock, RW_WRITER);
14077c478bd9Sstevel@tonic-gate 	for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
14087c478bd9Sstevel@tonic-gate 		/*
1409bd670b35SErik Nordmark 		 * If we have more then one ilm for the group (e.g., with
1410bd670b35SErik Nordmark 		 * different zoneid) then we should not tell the driver
1411bd670b35SErik Nordmark 		 * to leave unless this is the first ilm for the group.
14127c478bd9Sstevel@tonic-gate 		 */
1413bd670b35SErik Nordmark 		if (ilm_numentries(ill, &ilm->ilm_v6addr) > 1 &&
1414bd670b35SErik Nordmark 		    ilm_lookup(ill, &ilm->ilm_v6addr, ALL_ZONES) != ilm) {
14157c478bd9Sstevel@tonic-gate 			continue;
1416e11c3f44Smeem 		}
1417e11c3f44Smeem 
1418e11c3f44Smeem 		ip1dbg(("ill_leave_multicast: %s\n", inet_ntop(AF_INET6,
1419e11c3f44Smeem 		    &ilm->ilm_v6addr, addrbuf, sizeof (addrbuf))));
1420e11c3f44Smeem 
14217c478bd9Sstevel@tonic-gate 		if (IN6_IS_ADDR_UNSPECIFIED(&ilm->ilm_v6addr)) {
1422b127ac41SPhilip Kirk 			ill_leave_allmulti(ill);
14237c478bd9Sstevel@tonic-gate 		} else {
1424e11c3f44Smeem 			if (ill->ill_isv6)
1425e11c3f44Smeem 				mld_leavegroup(ilm);
1426e11c3f44Smeem 			else
1427e11c3f44Smeem 				igmp_leavegroup(ilm);
1428e11c3f44Smeem 
1429bd670b35SErik Nordmark 			(void) ip_ll_multireq(ill, &ilm->ilm_v6addr,
1430bd670b35SErik Nordmark 			    DL_DISABMULTI_REQ);
14317c478bd9Sstevel@tonic-gate 		}
14327c478bd9Sstevel@tonic-gate 	}
1433bd670b35SErik Nordmark 	rw_exit(&ill->ill_mcast_lock);
1434bd670b35SErik Nordmark 	/* Send any deferred/queued DLPI or IP packets */
1435bd670b35SErik Nordmark 	ill_mcast_send_queued(ill);
1436bd670b35SErik Nordmark 	ill_dlpi_send_queued(ill);
1437bd670b35SErik Nordmark 	ill_mcast_timer_start(ill->ill_ipst);
14387c478bd9Sstevel@tonic-gate }
14397c478bd9Sstevel@tonic-gate 
1440bd670b35SErik Nordmark /*
1441bd670b35SErik Nordmark  * Interface used by IP input/output.
1442bd670b35SErik Nordmark  * Returns true if there is a member on the ill for any zoneid.
1443bd670b35SErik Nordmark  */
1444bd670b35SErik Nordmark boolean_t
1445bd670b35SErik Nordmark ill_hasmembers_v6(ill_t *ill, const in6_addr_t *v6group)
1446bd670b35SErik Nordmark {
1447bd670b35SErik Nordmark 	ilm_t		*ilm;
1448bd670b35SErik Nordmark 
1449bd670b35SErik Nordmark 	rw_enter(&ill->ill_mcast_lock, RW_READER);
1450bd670b35SErik Nordmark 	ilm = ilm_lookup(ill, v6group, ALL_ZONES);
1451bd670b35SErik Nordmark 	rw_exit(&ill->ill_mcast_lock);
1452bd670b35SErik Nordmark 	return (ilm != NULL);
1453bd670b35SErik Nordmark }
1454bd670b35SErik Nordmark 
1455bd670b35SErik Nordmark /*
1456bd670b35SErik Nordmark  * Interface used by IP input/output.
1457bd670b35SErik Nordmark  * Returns true if there is a member on the ill for any zoneid.
1458bd670b35SErik Nordmark  *
1459bd670b35SErik Nordmark  * The group and source can't be INADDR_ANY here so no need to translate to
1460bd670b35SErik Nordmark  * the unspecified IPv6 address.
1461bd670b35SErik Nordmark  */
1462bd670b35SErik Nordmark boolean_t
1463bd670b35SErik Nordmark ill_hasmembers_v4(ill_t *ill, ipaddr_t group)
14647c478bd9Sstevel@tonic-gate {
14657c478bd9Sstevel@tonic-gate 	in6_addr_t	v6group;
14667c478bd9Sstevel@tonic-gate 
14677c478bd9Sstevel@tonic-gate 	IN6_IPADDR_TO_V4MAPPED(group, &v6group);
1468bd670b35SErik Nordmark 	return (ill_hasmembers_v6(ill, &v6group));
14697c478bd9Sstevel@tonic-gate }
14707c478bd9Sstevel@tonic-gate 
14717c478bd9Sstevel@tonic-gate /*
1472bd670b35SErik Nordmark  * Interface used by IP input/output.
1473bd670b35SErik Nordmark  * Returns true if there is a member on the ill for any zoneid except skipzone.
14747c478bd9Sstevel@tonic-gate  */
1475bd670b35SErik Nordmark boolean_t
1476bd670b35SErik Nordmark ill_hasmembers_otherzones_v6(ill_t *ill, const in6_addr_t *v6group,
1477bd670b35SErik Nordmark     zoneid_t skipzone)
14787c478bd9Sstevel@tonic-gate {
14797c478bd9Sstevel@tonic-gate 	ilm_t		*ilm;
1480bd670b35SErik Nordmark 
1481bd670b35SErik Nordmark 	rw_enter(&ill->ill_mcast_lock, RW_READER);
1482bd670b35SErik Nordmark 	for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
1483bd670b35SErik Nordmark 		if (IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, v6group) &&
1484bd670b35SErik Nordmark 		    ilm->ilm_zoneid != skipzone) {
1485bd670b35SErik Nordmark 			rw_exit(&ill->ill_mcast_lock);
1486bd670b35SErik Nordmark 			return (B_TRUE);
1487bd670b35SErik Nordmark 		}
1488bd670b35SErik Nordmark 	}
1489bd670b35SErik Nordmark 	rw_exit(&ill->ill_mcast_lock);
1490bd670b35SErik Nordmark 	return (B_FALSE);
1491bd670b35SErik Nordmark }
14927c478bd9Sstevel@tonic-gate 
14937c478bd9Sstevel@tonic-gate /*
1494bd670b35SErik Nordmark  * Interface used by IP input/output.
1495bd670b35SErik Nordmark  * Returns true if there is a member on the ill for any zoneid except skipzone.
1496bd670b35SErik Nordmark  *
1497bd670b35SErik Nordmark  * The group and source can't be INADDR_ANY here so no need to translate to
1498bd670b35SErik Nordmark  * the unspecified IPv6 address.
1499e11c3f44Smeem  */
1500bd670b35SErik Nordmark boolean_t
1501bd670b35SErik Nordmark ill_hasmembers_otherzones_v4(ill_t *ill, ipaddr_t group, zoneid_t skipzone)
1502bd670b35SErik Nordmark {
1503bd670b35SErik Nordmark 	in6_addr_t	v6group;
1504e11c3f44Smeem 
1505bd670b35SErik Nordmark 	IN6_IPADDR_TO_V4MAPPED(group, &v6group);
1506bd670b35SErik Nordmark 	return (ill_hasmembers_otherzones_v6(ill, &v6group, skipzone));
1507bd670b35SErik Nordmark }
1508bd670b35SErik Nordmark 
1509bd670b35SErik Nordmark /*
1510bd670b35SErik Nordmark  * Interface used by IP input.
1511bd670b35SErik Nordmark  * Returns the next numerically larger zoneid that has a member. If none exist
1512bd670b35SErik Nordmark  * then returns -1 (ALL_ZONES).
1513bd670b35SErik Nordmark  * The normal usage is for the caller to start with a -1 zoneid (ALL_ZONES)
1514bd670b35SErik Nordmark  * to find the first zoneid which has a member, and then pass that in for
1515bd670b35SErik Nordmark  * subsequent calls until ALL_ZONES is returned.
1516bd670b35SErik Nordmark  *
1517bd670b35SErik Nordmark  * The implementation of ill_hasmembers_nextzone() assumes the ilms
1518bd670b35SErik Nordmark  * are sorted by zoneid for efficiency.
1519bd670b35SErik Nordmark  */
1520bd670b35SErik Nordmark zoneid_t
1521bd670b35SErik Nordmark ill_hasmembers_nextzone_v6(ill_t *ill, const in6_addr_t *v6group,
1522bd670b35SErik Nordmark     zoneid_t zoneid)
1523bd670b35SErik Nordmark {
1524bd670b35SErik Nordmark 	ilm_t		*ilm;
1525bd670b35SErik Nordmark 
1526bd670b35SErik Nordmark 	rw_enter(&ill->ill_mcast_lock, RW_READER);
1527bd670b35SErik Nordmark 	for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
1528bd670b35SErik Nordmark 		if (IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, v6group) &&
1529bd670b35SErik Nordmark 		    ilm->ilm_zoneid > zoneid) {
1530bd670b35SErik Nordmark 			zoneid = ilm->ilm_zoneid;
1531bd670b35SErik Nordmark 			rw_exit(&ill->ill_mcast_lock);
1532bd670b35SErik Nordmark 			return (zoneid);
1533bd670b35SErik Nordmark 		}
1534bd670b35SErik Nordmark 	}
1535bd670b35SErik Nordmark 	rw_exit(&ill->ill_mcast_lock);
1536bd670b35SErik Nordmark 	return (ALL_ZONES);
1537bd670b35SErik Nordmark }
1538bd670b35SErik Nordmark 
1539bd670b35SErik Nordmark /*
1540bd670b35SErik Nordmark  * Interface used by IP input.
1541bd670b35SErik Nordmark  * Returns the next numerically larger zoneid that has a member. If none exist
1542bd670b35SErik Nordmark  * then returns -1 (ALL_ZONES).
1543bd670b35SErik Nordmark  *
1544bd670b35SErik Nordmark  * The group and source can't be INADDR_ANY here so no need to translate to
1545bd670b35SErik Nordmark  * the unspecified IPv6 address.
1546bd670b35SErik Nordmark  */
1547bd670b35SErik Nordmark zoneid_t
1548bd670b35SErik Nordmark ill_hasmembers_nextzone_v4(ill_t *ill, ipaddr_t group, zoneid_t zoneid)
1549bd670b35SErik Nordmark {
1550bd670b35SErik Nordmark 	in6_addr_t	v6group;
1551bd670b35SErik Nordmark 
1552bd670b35SErik Nordmark 	IN6_IPADDR_TO_V4MAPPED(group, &v6group);
1553bd670b35SErik Nordmark 
1554bd670b35SErik Nordmark 	return (ill_hasmembers_nextzone_v6(ill, &v6group, zoneid));
1555bd670b35SErik Nordmark }
1556bd670b35SErik Nordmark 
1557bd670b35SErik Nordmark /*
1558bd670b35SErik Nordmark  * Find an ilm matching the ill, group, and zoneid.
1559bd670b35SErik Nordmark  */
1560bd670b35SErik Nordmark static ilm_t *
1561bd670b35SErik Nordmark ilm_lookup(ill_t *ill, const in6_addr_t *v6group, zoneid_t zoneid)
1562bd670b35SErik Nordmark {
1563bd670b35SErik Nordmark 	ilm_t	*ilm;
1564bd670b35SErik Nordmark 
1565bd670b35SErik Nordmark 	ASSERT(RW_LOCK_HELD(&ill->ill_mcast_lock));
1566bd670b35SErik Nordmark 
1567bd670b35SErik Nordmark 	for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
1568e11c3f44Smeem 		if (!IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, v6group))
1569e11c3f44Smeem 			continue;
1570e11c3f44Smeem 		if (zoneid != ALL_ZONES && zoneid != ilm->ilm_zoneid)
1571e11c3f44Smeem 			continue;
1572bd670b35SErik Nordmark 
1573bd670b35SErik Nordmark 		ASSERT(ilm->ilm_ill == ill);
1574e11c3f44Smeem 		return (ilm);
1575e11c3f44Smeem 	}
1576bd670b35SErik Nordmark 	return (NULL);
15777c478bd9Sstevel@tonic-gate }
15787c478bd9Sstevel@tonic-gate 
15797c478bd9Sstevel@tonic-gate /*
15807c478bd9Sstevel@tonic-gate  * How many members on this ill?
1581bd670b35SErik Nordmark  * Since each shared-IP zone has a separate ilm for the same group/ill
1582bd670b35SErik Nordmark  * we can have several.
15837c478bd9Sstevel@tonic-gate  */
1584bd670b35SErik Nordmark static int
1585bd670b35SErik Nordmark ilm_numentries(ill_t *ill, const in6_addr_t *v6group)
15867c478bd9Sstevel@tonic-gate {
15877c478bd9Sstevel@tonic-gate 	ilm_t	*ilm;
15887c478bd9Sstevel@tonic-gate 	int i = 0;
15897c478bd9Sstevel@tonic-gate 
1590bd670b35SErik Nordmark 	ASSERT(RW_LOCK_HELD(&ill->ill_mcast_lock));
15917c478bd9Sstevel@tonic-gate 	for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
15927c478bd9Sstevel@tonic-gate 		if (IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, v6group)) {
15937c478bd9Sstevel@tonic-gate 			i++;
15947c478bd9Sstevel@tonic-gate 		}
15957c478bd9Sstevel@tonic-gate 	}
15967c478bd9Sstevel@tonic-gate 	return (i);
15977c478bd9Sstevel@tonic-gate }
15987c478bd9Sstevel@tonic-gate 
15997c478bd9Sstevel@tonic-gate /* Caller guarantees that the group is not already on the list */
16007c478bd9Sstevel@tonic-gate static ilm_t *
1601bd670b35SErik Nordmark ilm_add(ill_t *ill, const in6_addr_t *v6group, ilg_stat_t ilgstat,
1602e11c3f44Smeem     mcast_record_t ilg_fmode, slist_t *ilg_flist, zoneid_t zoneid)
16037c478bd9Sstevel@tonic-gate {
16047c478bd9Sstevel@tonic-gate 	ilm_t	*ilm;
16057c478bd9Sstevel@tonic-gate 	ilm_t	*ilm_cur;
16067c478bd9Sstevel@tonic-gate 	ilm_t	**ilm_ptpn;
16077c478bd9Sstevel@tonic-gate 
1608bd670b35SErik Nordmark 	ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock));
16097c478bd9Sstevel@tonic-gate 	ilm = GETSTRUCT(ilm_t, 1);
16107c478bd9Sstevel@tonic-gate 	if (ilm == NULL)
16117c478bd9Sstevel@tonic-gate 		return (NULL);
16127c478bd9Sstevel@tonic-gate 	if (ilgstat != ILGSTAT_NONE && !SLIST_IS_EMPTY(ilg_flist)) {
16137c478bd9Sstevel@tonic-gate 		ilm->ilm_filter = l_alloc();
16147c478bd9Sstevel@tonic-gate 		if (ilm->ilm_filter == NULL) {
16157c478bd9Sstevel@tonic-gate 			mi_free(ilm);
16167c478bd9Sstevel@tonic-gate 			return (NULL);
16177c478bd9Sstevel@tonic-gate 		}
16187c478bd9Sstevel@tonic-gate 	}
16197c478bd9Sstevel@tonic-gate 	ilm->ilm_v6addr = *v6group;
16207c478bd9Sstevel@tonic-gate 	ilm->ilm_refcnt = 1;
16217c478bd9Sstevel@tonic-gate 	ilm->ilm_zoneid = zoneid;
16227c478bd9Sstevel@tonic-gate 	ilm->ilm_timer = INFINITY;
16237c478bd9Sstevel@tonic-gate 	ilm->ilm_rtx.rtx_timer = INFINITY;
162445916cd2Sjpk 
16257c478bd9Sstevel@tonic-gate 	ilm->ilm_ill = ill;
1626968d2fd1Ssowmini 	DTRACE_PROBE3(ill__incr__cnt, (ill_t *), ill,
1627968d2fd1Ssowmini 	    (char *), "ilm", (void *), ilm);
1628384ad179Ssowmini 	ill->ill_ilm_cnt++;
1629e11c3f44Smeem 
1630f4b3ec61Sdh155122 	ASSERT(ill->ill_ipst);
1631f4b3ec61Sdh155122 	ilm->ilm_ipst = ill->ill_ipst;	/* No netstack_hold */
1632f4b3ec61Sdh155122 
1633bd670b35SErik Nordmark 	/* The ill/ipif could have just been marked as condemned */
16347c478bd9Sstevel@tonic-gate 
16357c478bd9Sstevel@tonic-gate 	/*
1636bd670b35SErik Nordmark 	 * To make ill_hasmembers_nextzone_v6 work we keep the list
1637bd670b35SErik Nordmark 	 * sorted by zoneid.
16387c478bd9Sstevel@tonic-gate 	 */
16397c478bd9Sstevel@tonic-gate 	ilm_cur = ill->ill_ilm;
16407c478bd9Sstevel@tonic-gate 	ilm_ptpn = &ill->ill_ilm;
1641bd670b35SErik Nordmark 	while (ilm_cur != NULL && ilm_cur->ilm_zoneid < ilm->ilm_zoneid) {
16427c478bd9Sstevel@tonic-gate 		ilm_ptpn = &ilm_cur->ilm_next;
16437c478bd9Sstevel@tonic-gate 		ilm_cur = ilm_cur->ilm_next;
16447c478bd9Sstevel@tonic-gate 	}
16457c478bd9Sstevel@tonic-gate 	ilm->ilm_next = ilm_cur;
16467c478bd9Sstevel@tonic-gate 	*ilm_ptpn = ilm;
16477c478bd9Sstevel@tonic-gate 
16487c478bd9Sstevel@tonic-gate 	/*
16497c478bd9Sstevel@tonic-gate 	 * If we have an associated ilg, use its filter state; if not,
16507c478bd9Sstevel@tonic-gate 	 * default to (EXCLUDE, NULL) and set no_ilg_cnt to track this.
16517c478bd9Sstevel@tonic-gate 	 */
16527c478bd9Sstevel@tonic-gate 	if (ilgstat != ILGSTAT_NONE) {
16537c478bd9Sstevel@tonic-gate 		if (!SLIST_IS_EMPTY(ilg_flist))
16547c478bd9Sstevel@tonic-gate 			l_copy(ilg_flist, ilm->ilm_filter);
16557c478bd9Sstevel@tonic-gate 		ilm->ilm_fmode = ilg_fmode;
16567c478bd9Sstevel@tonic-gate 	} else {
16577c478bd9Sstevel@tonic-gate 		ilm->ilm_no_ilg_cnt = 1;
16587c478bd9Sstevel@tonic-gate 		ilm->ilm_fmode = MODE_IS_EXCLUDE;
16597c478bd9Sstevel@tonic-gate 	}
16607c478bd9Sstevel@tonic-gate 
16617c478bd9Sstevel@tonic-gate 	return (ilm);
16627c478bd9Sstevel@tonic-gate }
16637c478bd9Sstevel@tonic-gate 
1664a7acbe81Ssowmini void
1665968d2fd1Ssowmini ilm_inactive(ilm_t *ilm)
1666968d2fd1Ssowmini {
1667968d2fd1Ssowmini 	FREE_SLIST(ilm->ilm_filter);
1668968d2fd1Ssowmini 	FREE_SLIST(ilm->ilm_pendsrcs);
1669968d2fd1Ssowmini 	FREE_SLIST(ilm->ilm_rtx.rtx_allow);
1670968d2fd1Ssowmini 	FREE_SLIST(ilm->ilm_rtx.rtx_block);
1671968d2fd1Ssowmini 	ilm->ilm_ipst = NULL;
1672968d2fd1Ssowmini 	mi_free((char *)ilm);
1673968d2fd1Ssowmini }
1674968d2fd1Ssowmini 
16757c478bd9Sstevel@tonic-gate /*
16767c478bd9Sstevel@tonic-gate  * Unlink ilm and free it.
16777c478bd9Sstevel@tonic-gate  */
16787c478bd9Sstevel@tonic-gate static void
16797c478bd9Sstevel@tonic-gate ilm_delete(ilm_t *ilm)
16807c478bd9Sstevel@tonic-gate {
1681bd670b35SErik Nordmark 	ill_t		*ill = ilm->ilm_ill;
16827c478bd9Sstevel@tonic-gate 	ilm_t		**ilmp;
1683968d2fd1Ssowmini 	boolean_t	need_wakeup;
1684968d2fd1Ssowmini 
16857c478bd9Sstevel@tonic-gate 	/*
16867c478bd9Sstevel@tonic-gate 	 * Delete under lock protection so that readers don't stumble
16877c478bd9Sstevel@tonic-gate 	 * on bad ilm_next
16887c478bd9Sstevel@tonic-gate 	 */
1689bd670b35SErik Nordmark 	ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock));
16907c478bd9Sstevel@tonic-gate 
16917c478bd9Sstevel@tonic-gate 	for (ilmp = &ill->ill_ilm; *ilmp != ilm; ilmp = &(*ilmp)->ilm_next)
16927c478bd9Sstevel@tonic-gate 		;
1693bd670b35SErik Nordmark 
16947c478bd9Sstevel@tonic-gate 	*ilmp = ilm->ilm_next;
1695968d2fd1Ssowmini 
1696bd670b35SErik Nordmark 	mutex_enter(&ill->ill_lock);
1697968d2fd1Ssowmini 	/*
1698bd670b35SErik Nordmark 	 * if we are the last reference to the ill, we may need to wakeup any
1699bd670b35SErik Nordmark 	 * pending FREE or unplumb operations. This is because conn_update_ill
1700bd670b35SErik Nordmark 	 * bails if there is a ilg_delete_all in progress.
1701968d2fd1Ssowmini 	 */
1702968d2fd1Ssowmini 	need_wakeup = B_FALSE;
1703968d2fd1Ssowmini 	DTRACE_PROBE3(ill__decr__cnt, (ill_t *), ill,
1704968d2fd1Ssowmini 	    (char *), "ilm", (void *), ilm);
1705a7acbe81Ssowmini 	ASSERT(ill->ill_ilm_cnt > 0);
1706384ad179Ssowmini 	ill->ill_ilm_cnt--;
1707968d2fd1Ssowmini 	if (ILL_FREE_OK(ill))
1708968d2fd1Ssowmini 		need_wakeup = B_TRUE;
1709968d2fd1Ssowmini 
1710968d2fd1Ssowmini 	ilm_inactive(ilm); /* frees this ilm */
1711968d2fd1Ssowmini 
1712968d2fd1Ssowmini 	if (need_wakeup) {
1713968d2fd1Ssowmini 		/* drops ill lock */
1714968d2fd1Ssowmini 		ipif_ill_refrele_tail(ill);
1715968d2fd1Ssowmini 	} else {
17167c478bd9Sstevel@tonic-gate 		mutex_exit(&ill->ill_lock);
17177c478bd9Sstevel@tonic-gate 	}
17187c478bd9Sstevel@tonic-gate }
17197c478bd9Sstevel@tonic-gate 
1720e11c3f44Smeem /*
1721bd670b35SErik Nordmark  * Lookup an ill based on the group, ifindex, ifaddr, and zoneid.
1722bd670b35SErik Nordmark  * Applies to both IPv4 and IPv6, although ifaddr is only used with
1723bd670b35SErik Nordmark  * IPv4.
1724bd670b35SErik Nordmark  * Returns an error for IS_UNDER_IPMP and VNI interfaces.
1725bd670b35SErik Nordmark  * On error it sets *errorp.
1726e11c3f44Smeem  */
1727bd670b35SErik Nordmark static ill_t *
1728bd670b35SErik Nordmark ill_mcast_lookup(const in6_addr_t *group, ipaddr_t ifaddr, uint_t ifindex,
1729bd670b35SErik Nordmark     zoneid_t zoneid, ip_stack_t *ipst, int *errorp)
1730e11c3f44Smeem {
1731bd670b35SErik Nordmark 	ill_t *ill;
1732bd670b35SErik Nordmark 	ipaddr_t v4group;
1733e11c3f44Smeem 
1734bd670b35SErik Nordmark 	if (IN6_IS_ADDR_V4MAPPED(group)) {
1735bd670b35SErik Nordmark 		IN6_V4MAPPED_TO_IPADDR(group, v4group);
1736e11c3f44Smeem 
1737bd670b35SErik Nordmark 		if (ifindex != 0) {
1738bd670b35SErik Nordmark 			ill = ill_lookup_on_ifindex_zoneid(ifindex, zoneid,
1739bd670b35SErik Nordmark 			    B_FALSE, ipst);
1740bd670b35SErik Nordmark 		} else if (ifaddr != INADDR_ANY) {
1741bd670b35SErik Nordmark 			ipif_t *ipif;
1742e11c3f44Smeem 
1743bd670b35SErik Nordmark 			ipif = ipif_lookup_addr(ifaddr, NULL, zoneid, ipst);
1744bd670b35SErik Nordmark 			if (ipif == NULL) {
1745bd670b35SErik Nordmark 				ill = NULL;
1746bd670b35SErik Nordmark 			} else {
1747bd670b35SErik Nordmark 				ill = ipif->ipif_ill;
1748bd670b35SErik Nordmark 				ill_refhold(ill);
1749bd670b35SErik Nordmark 				ipif_refrele(ipif);
1750e11c3f44Smeem 			}
1751bd670b35SErik Nordmark 		} else {
1752bd670b35SErik Nordmark 			ill = ill_lookup_group_v4(v4group, zoneid, ipst, NULL,
1753bd670b35SErik Nordmark 			    NULL);
1754e11c3f44Smeem 		}
1755bd670b35SErik Nordmark 	} else {
1756bd670b35SErik Nordmark 		if (ifindex != 0) {
1757bd670b35SErik Nordmark 			ill = ill_lookup_on_ifindex_zoneid(ifindex, zoneid,
1758bd670b35SErik Nordmark 			    B_TRUE, ipst);
1759bd670b35SErik Nordmark 		} else {
1760bd670b35SErik Nordmark 			ill = ill_lookup_group_v6(group, zoneid, ipst, NULL,
1761bd670b35SErik Nordmark 			    NULL);
1762bd670b35SErik Nordmark 		}
1763bd670b35SErik Nordmark 	}
1764bd670b35SErik Nordmark 	if (ill == NULL) {
1765bd670b35SErik Nordmark 		if (ifindex != 0)
1766bd670b35SErik Nordmark 			*errorp = ENXIO;
1767bd670b35SErik Nordmark 		else
1768bd670b35SErik Nordmark 			*errorp = EADDRNOTAVAIL;
1769e11c3f44Smeem 		return (NULL);
1770e11c3f44Smeem 	}
1771bd670b35SErik Nordmark 	/* operation not supported on the virtual network interface */
1772bd670b35SErik Nordmark 	if (IS_UNDER_IPMP(ill) || IS_VNI(ill)) {
1773bd670b35SErik Nordmark 		ill_refrele(ill);
1774bd670b35SErik Nordmark 		*errorp = EINVAL;
1775bd670b35SErik Nordmark 		return (NULL);
1776e11c3f44Smeem 	}
1777bd670b35SErik Nordmark 	return (ill);
1778e11c3f44Smeem }
1779e11c3f44Smeem 
1780e11c3f44Smeem /*
1781bd670b35SErik Nordmark  * Looks up the appropriate ill given an interface index (or interface address)
1782bd670b35SErik Nordmark  * and multicast group.  On success, returns 0, with *illpp pointing to the
1783bd670b35SErik Nordmark  * found struct.  On failure, returns an errno and *illpp is set to NULL.
1784bd670b35SErik Nordmark  *
1785bd670b35SErik Nordmark  * Returns an error for IS_UNDER_IPMP and VNI interfaces.
1786bd670b35SErik Nordmark  *
1787bd670b35SErik Nordmark  * Handles both IPv4 and IPv6. The ifaddr argument only applies in the
1788bd670b35SErik Nordmark  * case of IPv4.
17897c478bd9Sstevel@tonic-gate  */
17907c478bd9Sstevel@tonic-gate int
1791bd670b35SErik Nordmark ip_opt_check(conn_t *connp, const in6_addr_t *v6group,
1792bd670b35SErik Nordmark     const in6_addr_t *v6src, ipaddr_t ifaddr, uint_t ifindex, ill_t **illpp)
17937c478bd9Sstevel@tonic-gate {
17947c478bd9Sstevel@tonic-gate 	boolean_t src_unspec;
17957c478bd9Sstevel@tonic-gate 	ill_t *ill = NULL;
1796f4b3ec61Sdh155122 	ip_stack_t *ipst = connp->conn_netstack->netstack_ip;
1797bd670b35SErik Nordmark 	int error = 0;
1798bd670b35SErik Nordmark 
1799bd670b35SErik Nordmark 	*illpp = NULL;
18007c478bd9Sstevel@tonic-gate 
18017c478bd9Sstevel@tonic-gate 	src_unspec = IN6_IS_ADDR_UNSPECIFIED(v6src);
18027c478bd9Sstevel@tonic-gate 
18037c478bd9Sstevel@tonic-gate 	if (IN6_IS_ADDR_V4MAPPED(v6group)) {
1804bd670b35SErik Nordmark 		ipaddr_t v4group;
1805bd670b35SErik Nordmark 		ipaddr_t v4src;
1806bd670b35SErik Nordmark 
18077c478bd9Sstevel@tonic-gate 		if (!IN6_IS_ADDR_V4MAPPED(v6src) && !src_unspec)
18087c478bd9Sstevel@tonic-gate 			return (EINVAL);
1809bd670b35SErik Nordmark 		IN6_V4MAPPED_TO_IPADDR(v6group, v4group);
18107c478bd9Sstevel@tonic-gate 		if (src_unspec) {
1811bd670b35SErik Nordmark 			v4src = INADDR_ANY;
18127c478bd9Sstevel@tonic-gate 		} else {
1813bd670b35SErik Nordmark 			IN6_V4MAPPED_TO_IPADDR(v6src, v4src);
18147c478bd9Sstevel@tonic-gate 		}
1815bd670b35SErik Nordmark 		if (!CLASSD(v4group) || CLASSD(v4src))
18167c478bd9Sstevel@tonic-gate 			return (EINVAL);
18177c478bd9Sstevel@tonic-gate 	} else {
18187c478bd9Sstevel@tonic-gate 		if (IN6_IS_ADDR_V4MAPPED(v6src) && !src_unspec)
18197c478bd9Sstevel@tonic-gate 			return (EINVAL);
18207c478bd9Sstevel@tonic-gate 		if (!IN6_IS_ADDR_MULTICAST(v6group) ||
18217c478bd9Sstevel@tonic-gate 		    IN6_IS_ADDR_MULTICAST(v6src)) {
18227c478bd9Sstevel@tonic-gate 			return (EINVAL);
18237c478bd9Sstevel@tonic-gate 		}
18247c478bd9Sstevel@tonic-gate 	}
18257c478bd9Sstevel@tonic-gate 
1826bd670b35SErik Nordmark 	ill = ill_mcast_lookup(v6group, ifaddr, ifindex, IPCL_ZONEID(connp),
1827bd670b35SErik Nordmark 	    ipst, &error);
18287c478bd9Sstevel@tonic-gate 	*illpp = ill;
1829bd670b35SErik Nordmark 	return (error);
18307c478bd9Sstevel@tonic-gate }
18317c478bd9Sstevel@tonic-gate 
18327c478bd9Sstevel@tonic-gate static int
18337c478bd9Sstevel@tonic-gate ip_get_srcfilter(conn_t *connp, struct group_filter *gf,
1834bd670b35SErik Nordmark     struct ip_msfilter *imsf, const struct in6_addr *group, boolean_t issin6)
18357c478bd9Sstevel@tonic-gate {
18367c478bd9Sstevel@tonic-gate 	ilg_t *ilg;
18377c478bd9Sstevel@tonic-gate 	int i, numsrc, fmode, outsrcs;
18387c478bd9Sstevel@tonic-gate 	struct sockaddr_in *sin;
18397c478bd9Sstevel@tonic-gate 	struct sockaddr_in6 *sin6;
18407c478bd9Sstevel@tonic-gate 	struct in_addr *addrp;
18417c478bd9Sstevel@tonic-gate 	slist_t *fp;
18427c478bd9Sstevel@tonic-gate 	boolean_t is_v4only_api;
1843bd670b35SErik Nordmark 	ipaddr_t ifaddr;
1844bd670b35SErik Nordmark 	uint_t ifindex;
18457c478bd9Sstevel@tonic-gate 
18467c478bd9Sstevel@tonic-gate 	if (gf == NULL) {
18477c478bd9Sstevel@tonic-gate 		ASSERT(imsf != NULL);
1848bd670b35SErik Nordmark 		ASSERT(!issin6);
18497c478bd9Sstevel@tonic-gate 		is_v4only_api = B_TRUE;
18507c478bd9Sstevel@tonic-gate 		outsrcs = imsf->imsf_numsrc;
1851bd670b35SErik Nordmark 		ifaddr = imsf->imsf_interface.s_addr;
1852bd670b35SErik Nordmark 		ifindex = 0;
18537c478bd9Sstevel@tonic-gate 	} else {
18547c478bd9Sstevel@tonic-gate 		ASSERT(imsf == NULL);
18557c478bd9Sstevel@tonic-gate 		is_v4only_api = B_FALSE;
18567c478bd9Sstevel@tonic-gate 		outsrcs = gf->gf_numsrc;
1857bd670b35SErik Nordmark 		ifaddr = INADDR_ANY;
1858bd670b35SErik Nordmark 		ifindex = gf->gf_interface;
1859bd670b35SErik Nordmark 	}
1860bd670b35SErik Nordmark 
1861bd670b35SErik Nordmark 	/* No need to use ill_mcast_serializer for the reader */
1862bd670b35SErik Nordmark 	rw_enter(&connp->conn_ilg_lock, RW_READER);
1863bd670b35SErik Nordmark 	ilg = ilg_lookup(connp, group, ifaddr, ifindex);
1864bd670b35SErik Nordmark 	if (ilg == NULL) {
1865bd670b35SErik Nordmark 		rw_exit(&connp->conn_ilg_lock);
1866bd670b35SErik Nordmark 		return (EADDRNOTAVAIL);
18677c478bd9Sstevel@tonic-gate 	}
18687c478bd9Sstevel@tonic-gate 
18697c478bd9Sstevel@tonic-gate 	/*
18707c478bd9Sstevel@tonic-gate 	 * In the kernel, we use the state definitions MODE_IS_[IN|EX]CLUDE
18717c478bd9Sstevel@tonic-gate 	 * to identify the filter mode; but the API uses MCAST_[IN|EX]CLUDE.
18727c478bd9Sstevel@tonic-gate 	 * So we need to translate here.
18737c478bd9Sstevel@tonic-gate 	 */
18747c478bd9Sstevel@tonic-gate 	fmode = (ilg->ilg_fmode == MODE_IS_INCLUDE) ?
18757c478bd9Sstevel@tonic-gate 	    MCAST_INCLUDE : MCAST_EXCLUDE;
18767c478bd9Sstevel@tonic-gate 	if ((fp = ilg->ilg_filter) == NULL) {
18777c478bd9Sstevel@tonic-gate 		numsrc = 0;
18787c478bd9Sstevel@tonic-gate 	} else {
18797c478bd9Sstevel@tonic-gate 		for (i = 0; i < outsrcs; i++) {
18807c478bd9Sstevel@tonic-gate 			if (i == fp->sl_numsrc)
18817c478bd9Sstevel@tonic-gate 				break;
1882bd670b35SErik Nordmark 			if (issin6) {
18837c478bd9Sstevel@tonic-gate 				sin6 = (struct sockaddr_in6 *)&gf->gf_slist[i];
18847c478bd9Sstevel@tonic-gate 				sin6->sin6_family = AF_INET6;
18857c478bd9Sstevel@tonic-gate 				sin6->sin6_addr = fp->sl_addr[i];
18867c478bd9Sstevel@tonic-gate 			} else {
18877c478bd9Sstevel@tonic-gate 				if (is_v4only_api) {
18887c478bd9Sstevel@tonic-gate 					addrp = &imsf->imsf_slist[i];
18897c478bd9Sstevel@tonic-gate 				} else {
18907c478bd9Sstevel@tonic-gate 					sin = (struct sockaddr_in *)
18917c478bd9Sstevel@tonic-gate 					    &gf->gf_slist[i];
18927c478bd9Sstevel@tonic-gate 					sin->sin_family = AF_INET;
18937c478bd9Sstevel@tonic-gate 					addrp = &sin->sin_addr;
18947c478bd9Sstevel@tonic-gate 				}
18957c478bd9Sstevel@tonic-gate 				IN6_V4MAPPED_TO_INADDR(&fp->sl_addr[i], addrp);
18967c478bd9Sstevel@tonic-gate 			}
18977c478bd9Sstevel@tonic-gate 		}
18987c478bd9Sstevel@tonic-gate 		numsrc = fp->sl_numsrc;
18997c478bd9Sstevel@tonic-gate 	}
19007c478bd9Sstevel@tonic-gate 
19017c478bd9Sstevel@tonic-gate 	if (is_v4only_api) {
19027c478bd9Sstevel@tonic-gate 		imsf->imsf_numsrc = numsrc;
19037c478bd9Sstevel@tonic-gate 		imsf->imsf_fmode = fmode;
19047c478bd9Sstevel@tonic-gate 	} else {
19057c478bd9Sstevel@tonic-gate 		gf->gf_numsrc = numsrc;
19067c478bd9Sstevel@tonic-gate 		gf->gf_fmode = fmode;
19077c478bd9Sstevel@tonic-gate 	}
19087c478bd9Sstevel@tonic-gate 
1909bd670b35SErik Nordmark 	rw_exit(&connp->conn_ilg_lock);
19107c478bd9Sstevel@tonic-gate 
19117c478bd9Sstevel@tonic-gate 	return (0);
19127c478bd9Sstevel@tonic-gate }
19137c478bd9Sstevel@tonic-gate 
19147c478bd9Sstevel@tonic-gate /*
1915bd670b35SErik Nordmark  * Common for IPv4 and IPv6.
19167c478bd9Sstevel@tonic-gate  */
19177c478bd9Sstevel@tonic-gate static int
19187c478bd9Sstevel@tonic-gate ip_set_srcfilter(conn_t *connp, struct group_filter *gf,
1919bd670b35SErik Nordmark     struct ip_msfilter *imsf, const struct in6_addr *group, ill_t *ill,
1920bd670b35SErik Nordmark     boolean_t issin6)
19217c478bd9Sstevel@tonic-gate {
19227c478bd9Sstevel@tonic-gate 	ilg_t *ilg;
1923fda3a3a5Sblu 	int i, err, infmode, new_fmode;
1924fda3a3a5Sblu 	uint_t insrcs;
19257c478bd9Sstevel@tonic-gate 	struct sockaddr_in *sin;
19267c478bd9Sstevel@tonic-gate 	struct sockaddr_in6 *sin6;
19277c478bd9Sstevel@tonic-gate 	struct in_addr *addrp;
19287c478bd9Sstevel@tonic-gate 	slist_t *orig_filter = NULL;
19297c478bd9Sstevel@tonic-gate 	slist_t *new_filter = NULL;
19307c478bd9Sstevel@tonic-gate 	mcast_record_t orig_fmode;
1931bd670b35SErik Nordmark 	boolean_t leave_group, is_v4only_api;
19327c478bd9Sstevel@tonic-gate 	ilg_stat_t ilgstat;
1933bd670b35SErik Nordmark 	ilm_t *ilm;
1934bd670b35SErik Nordmark 	ipaddr_t ifaddr;
1935bd670b35SErik Nordmark 	uint_t ifindex;
19367c478bd9Sstevel@tonic-gate 
19377c478bd9Sstevel@tonic-gate 	if (gf == NULL) {
19387c478bd9Sstevel@tonic-gate 		ASSERT(imsf != NULL);
1939bd670b35SErik Nordmark 		ASSERT(!issin6);
19407c478bd9Sstevel@tonic-gate 		is_v4only_api = B_TRUE;
19417c478bd9Sstevel@tonic-gate 		insrcs = imsf->imsf_numsrc;
19427c478bd9Sstevel@tonic-gate 		infmode = imsf->imsf_fmode;
1943bd670b35SErik Nordmark 		ifaddr = imsf->imsf_interface.s_addr;
1944bd670b35SErik Nordmark 		ifindex = 0;
19457c478bd9Sstevel@tonic-gate 	} else {
19467c478bd9Sstevel@tonic-gate 		ASSERT(imsf == NULL);
19477c478bd9Sstevel@tonic-gate 		is_v4only_api = B_FALSE;
19487c478bd9Sstevel@tonic-gate 		insrcs = gf->gf_numsrc;
19497c478bd9Sstevel@tonic-gate 		infmode = gf->gf_fmode;
1950bd670b35SErik Nordmark 		ifaddr = INADDR_ANY;
1951bd670b35SErik Nordmark 		ifindex = gf->gf_interface;
19527c478bd9Sstevel@tonic-gate 	}
19537c478bd9Sstevel@tonic-gate 
19547c478bd9Sstevel@tonic-gate 	/* Make sure we can handle the source list */
19557c478bd9Sstevel@tonic-gate 	if (insrcs > MAX_FILTER_SIZE)
19567c478bd9Sstevel@tonic-gate 		return (ENOBUFS);
19577c478bd9Sstevel@tonic-gate 
19587c478bd9Sstevel@tonic-gate 	/*
19597c478bd9Sstevel@tonic-gate 	 * setting the filter to (INCLUDE, NULL) is treated
19607c478bd9Sstevel@tonic-gate 	 * as a request to leave the group.
19617c478bd9Sstevel@tonic-gate 	 */
1962bd670b35SErik Nordmark 	leave_group = (infmode == MCAST_INCLUDE && insrcs == 0);
19637c478bd9Sstevel@tonic-gate 
1964bd670b35SErik Nordmark 	mutex_enter(&ill->ill_mcast_serializer);
1965bd670b35SErik Nordmark 	rw_enter(&connp->conn_ilg_lock, RW_WRITER);
1966bd670b35SErik Nordmark 	ilg = ilg_lookup(connp, group, ifaddr, ifindex);
19677c478bd9Sstevel@tonic-gate 	if (ilg == NULL) {
19687c478bd9Sstevel@tonic-gate 		/*
19697c478bd9Sstevel@tonic-gate 		 * if the request was actually to leave, and we
19707c478bd9Sstevel@tonic-gate 		 * didn't find an ilg, there's nothing to do.
19717c478bd9Sstevel@tonic-gate 		 */
1972bd670b35SErik Nordmark 		if (leave_group) {
1973bd670b35SErik Nordmark 			rw_exit(&connp->conn_ilg_lock);
1974bd670b35SErik Nordmark 			mutex_exit(&ill->ill_mcast_serializer);
1975bd670b35SErik Nordmark 			return (0);
1976bd670b35SErik Nordmark 		}
1977e11c3f44Smeem 		ilg = conn_ilg_alloc(connp, &err);
1978bd670b35SErik Nordmark 		if (ilg == NULL) {
1979bd670b35SErik Nordmark 			rw_exit(&connp->conn_ilg_lock);
1980bd670b35SErik Nordmark 			mutex_exit(&ill->ill_mcast_serializer);
1981bd670b35SErik Nordmark 			return (err);
19827c478bd9Sstevel@tonic-gate 		}
19837c478bd9Sstevel@tonic-gate 		ilgstat = ILGSTAT_NEW;
1984bd670b35SErik Nordmark 		ilg->ilg_v6group = *group;
1985bd670b35SErik Nordmark 		ilg->ilg_ill = ill;
1986bd670b35SErik Nordmark 		ilg->ilg_ifaddr = ifaddr;
1987bd670b35SErik Nordmark 		ilg->ilg_ifindex = ifindex;
1988bd670b35SErik Nordmark 	} else if (leave_group) {
1989bd670b35SErik Nordmark 		/*
1990bd670b35SErik Nordmark 		 * Make sure we have the correct serializer. The ill argument
1991bd670b35SErik Nordmark 		 * might not match ilg_ill.
1992bd670b35SErik Nordmark 		 */
1993bd670b35SErik Nordmark 		ilg_refhold(ilg);
1994bd670b35SErik Nordmark 		mutex_exit(&ill->ill_mcast_serializer);
1995bd670b35SErik Nordmark 		ill = ilg->ilg_ill;
1996bd670b35SErik Nordmark 		rw_exit(&connp->conn_ilg_lock);
1997bd670b35SErik Nordmark 
1998bd670b35SErik Nordmark 		mutex_enter(&ill->ill_mcast_serializer);
1999bd670b35SErik Nordmark 		rw_enter(&connp->conn_ilg_lock, RW_WRITER);
2000bd670b35SErik Nordmark 		ilm = ilg->ilg_ilm;
2001bd670b35SErik Nordmark 		ilg->ilg_ilm = NULL;
20027c478bd9Sstevel@tonic-gate 		ilg_delete(connp, ilg, NULL);
2003bd670b35SErik Nordmark 		ilg_refrele(ilg);
2004bd670b35SErik Nordmark 		rw_exit(&connp->conn_ilg_lock);
2005bd670b35SErik Nordmark 		if (ilm != NULL)
2006bd670b35SErik Nordmark 			(void) ip_delmulti_serial(ilm, B_FALSE, B_TRUE);
2007bd670b35SErik Nordmark 		mutex_exit(&ill->ill_mcast_serializer);
2008f1c454b4SSowmini Varadhan 		/*
2009f1c454b4SSowmini Varadhan 		 * Now that all locks have been dropped, we can send any
2010f1c454b4SSowmini Varadhan 		 * deferred/queued DLPI or IP packets
2011f1c454b4SSowmini Varadhan 		 */
2012f1c454b4SSowmini Varadhan 		ill_mcast_send_queued(ill);
2013f1c454b4SSowmini Varadhan 		ill_dlpi_send_queued(ill);
20147c478bd9Sstevel@tonic-gate 		return (0);
20157c478bd9Sstevel@tonic-gate 	} else {
20167c478bd9Sstevel@tonic-gate 		ilgstat = ILGSTAT_CHANGE;
20177c478bd9Sstevel@tonic-gate 		/* Preserve existing state in case ip_addmulti() fails */
20187c478bd9Sstevel@tonic-gate 		orig_fmode = ilg->ilg_fmode;
20197c478bd9Sstevel@tonic-gate 		if (ilg->ilg_filter == NULL) {
20207c478bd9Sstevel@tonic-gate 			orig_filter = NULL;
20217c478bd9Sstevel@tonic-gate 		} else {
20227c478bd9Sstevel@tonic-gate 			orig_filter = l_alloc_copy(ilg->ilg_filter);
20237c478bd9Sstevel@tonic-gate 			if (orig_filter == NULL) {
2024bd670b35SErik Nordmark 				rw_exit(&connp->conn_ilg_lock);
2025bd670b35SErik Nordmark 				mutex_exit(&ill->ill_mcast_serializer);
20267c478bd9Sstevel@tonic-gate 				return (ENOMEM);
20277c478bd9Sstevel@tonic-gate 			}
20287c478bd9Sstevel@tonic-gate 		}
20297c478bd9Sstevel@tonic-gate 	}
20307c478bd9Sstevel@tonic-gate 
20317c478bd9Sstevel@tonic-gate 	/*
20327c478bd9Sstevel@tonic-gate 	 * Alloc buffer to copy new state into (see below) before
20337c478bd9Sstevel@tonic-gate 	 * we make any changes, so we can bail if it fails.
20347c478bd9Sstevel@tonic-gate 	 */
20357c478bd9Sstevel@tonic-gate 	if ((new_filter = l_alloc()) == NULL) {
2036bd670b35SErik Nordmark 		rw_exit(&connp->conn_ilg_lock);
20377c478bd9Sstevel@tonic-gate 		err = ENOMEM;
20387c478bd9Sstevel@tonic-gate 		goto free_and_exit;
20397c478bd9Sstevel@tonic-gate 	}
20407c478bd9Sstevel@tonic-gate 
20417c478bd9Sstevel@tonic-gate 	if (insrcs == 0) {
20427c478bd9Sstevel@tonic-gate 		CLEAR_SLIST(ilg->ilg_filter);
20437c478bd9Sstevel@tonic-gate 	} else {
20447c478bd9Sstevel@tonic-gate 		slist_t *fp;
20457c478bd9Sstevel@tonic-gate 		if (ilg->ilg_filter == NULL) {
20467c478bd9Sstevel@tonic-gate 			fp = l_alloc();
20477c478bd9Sstevel@tonic-gate 			if (fp == NULL) {
20487c478bd9Sstevel@tonic-gate 				if (ilgstat == ILGSTAT_NEW)
20497c478bd9Sstevel@tonic-gate 					ilg_delete(connp, ilg, NULL);
2050bd670b35SErik Nordmark 				rw_exit(&connp->conn_ilg_lock);
20517c478bd9Sstevel@tonic-gate 				err = ENOMEM;
20527c478bd9Sstevel@tonic-gate 				goto free_and_exit;
20537c478bd9Sstevel@tonic-gate 			}
20547c478bd9Sstevel@tonic-gate 		} else {
20557c478bd9Sstevel@tonic-gate 			fp = ilg->ilg_filter;
20567c478bd9Sstevel@tonic-gate 		}
20577c478bd9Sstevel@tonic-gate 		for (i = 0; i < insrcs; i++) {
2058bd670b35SErik Nordmark 			if (issin6) {
20597c478bd9Sstevel@tonic-gate 				sin6 = (struct sockaddr_in6 *)&gf->gf_slist[i];
20607c478bd9Sstevel@tonic-gate 				fp->sl_addr[i] = sin6->sin6_addr;
20617c478bd9Sstevel@tonic-gate 			} else {
20627c478bd9Sstevel@tonic-gate 				if (is_v4only_api) {
20637c478bd9Sstevel@tonic-gate 					addrp = &imsf->imsf_slist[i];
20647c478bd9Sstevel@tonic-gate 				} else {
20657c478bd9Sstevel@tonic-gate 					sin = (struct sockaddr_in *)
20667c478bd9Sstevel@tonic-gate 					    &gf->gf_slist[i];
20677c478bd9Sstevel@tonic-gate 					addrp = &sin->sin_addr;
20687c478bd9Sstevel@tonic-gate 				}
20697c478bd9Sstevel@tonic-gate 				IN6_INADDR_TO_V4MAPPED(addrp, &fp->sl_addr[i]);
20707c478bd9Sstevel@tonic-gate 			}
20717c478bd9Sstevel@tonic-gate 		}
20727c478bd9Sstevel@tonic-gate 		fp->sl_numsrc = insrcs;
20737c478bd9Sstevel@tonic-gate 		ilg->ilg_filter = fp;
20747c478bd9Sstevel@tonic-gate 	}
20757c478bd9Sstevel@tonic-gate 	/*
20767c478bd9Sstevel@tonic-gate 	 * In the kernel, we use the state definitions MODE_IS_[IN|EX]CLUDE
20777c478bd9Sstevel@tonic-gate 	 * to identify the filter mode; but the API uses MCAST_[IN|EX]CLUDE.
20787c478bd9Sstevel@tonic-gate 	 * So we need to translate here.
20797c478bd9Sstevel@tonic-gate 	 */
20807c478bd9Sstevel@tonic-gate 	ilg->ilg_fmode = (infmode == MCAST_INCLUDE) ?
20817c478bd9Sstevel@tonic-gate 	    MODE_IS_INCLUDE : MODE_IS_EXCLUDE;
20827c478bd9Sstevel@tonic-gate 
20837c478bd9Sstevel@tonic-gate 	/*
20847c478bd9Sstevel@tonic-gate 	 * Save copy of ilg's filter state to pass to other functions,
2085bd670b35SErik Nordmark 	 * so we can release conn_ilg_lock now.
20867c478bd9Sstevel@tonic-gate 	 */
20877c478bd9Sstevel@tonic-gate 	new_fmode = ilg->ilg_fmode;
20887c478bd9Sstevel@tonic-gate 	l_copy(ilg->ilg_filter, new_filter);
20897c478bd9Sstevel@tonic-gate 
2090bd670b35SErik Nordmark 	rw_exit(&connp->conn_ilg_lock);
20917c478bd9Sstevel@tonic-gate 
20927c478bd9Sstevel@tonic-gate 	/*
2093bd670b35SErik Nordmark 	 * Now update the ill. We wait to do this until after the ilg
2094bd670b35SErik Nordmark 	 * has been updated because we need to update the src filter
2095bd670b35SErik Nordmark 	 * info for the ill, which involves looking at the status of
2096bd670b35SErik Nordmark 	 * all the ilgs associated with this group/interface pair.
20977c478bd9Sstevel@tonic-gate 	 */
2098bd670b35SErik Nordmark 	ilm = ip_addmulti_serial(group, ill, connp->conn_zoneid, ilgstat,
2099bd670b35SErik Nordmark 	    new_fmode, new_filter, &err);
21007c478bd9Sstevel@tonic-gate 
2101bd670b35SErik Nordmark 	rw_enter(&connp->conn_ilg_lock, RW_WRITER);
2102bd670b35SErik Nordmark 	/*
2103bd670b35SErik Nordmark 	 * Must look up the ilg again since we've not been holding
2104bd670b35SErik Nordmark 	 * conn_ilg_lock. The ilg could have disappeared due to an unplumb
2105bd670b35SErik Nordmark 	 * having called conn_update_ill, which can run once we dropped the
2106bd670b35SErik Nordmark 	 * conn_ilg_lock above.
2107bd670b35SErik Nordmark 	 */
2108bd670b35SErik Nordmark 	ilg = ilg_lookup(connp, group, ifaddr, ifindex);
21097c478bd9Sstevel@tonic-gate 	if (ilg == NULL) {
2110bd670b35SErik Nordmark 		rw_exit(&connp->conn_ilg_lock);
2111bd670b35SErik Nordmark 		if (ilm != NULL) {
2112bd670b35SErik Nordmark 			(void) ip_delmulti_serial(ilm, B_FALSE,
2113bd670b35SErik Nordmark 			    (ilgstat == ILGSTAT_NEW));
21147c478bd9Sstevel@tonic-gate 		}
2115bd670b35SErik Nordmark 		err = ENXIO;
21167c478bd9Sstevel@tonic-gate 		goto free_and_exit;
21177c478bd9Sstevel@tonic-gate 	}
21187c478bd9Sstevel@tonic-gate 
2119bd670b35SErik Nordmark 	if (ilm != NULL) {
2120f1c454b4SSowmini Varadhan 		if (ilg->ilg_ill == NULL) {
2121f1c454b4SSowmini Varadhan 			/* some other thread is re-attaching this.  */
2122f1c454b4SSowmini Varadhan 			rw_exit(&connp->conn_ilg_lock);
2123f1c454b4SSowmini Varadhan 			(void) ip_delmulti_serial(ilm, B_FALSE,
2124f1c454b4SSowmini Varadhan 			    (ilgstat == ILGSTAT_NEW));
2125f1c454b4SSowmini Varadhan 			err = 0;
2126f1c454b4SSowmini Varadhan 			goto free_and_exit;
2127f1c454b4SSowmini Varadhan 		}
2128bd670b35SErik Nordmark 		/* Succeeded. Update the ilg to point at the ilm */
21297c478bd9Sstevel@tonic-gate 		if (ilgstat == ILGSTAT_NEW) {
2130f1c454b4SSowmini Varadhan 			if (ilg->ilg_ilm == NULL) {
2131bd670b35SErik Nordmark 				ilg->ilg_ilm = ilm;
2132bd670b35SErik Nordmark 				ilm->ilm_ifaddr = ifaddr; /* For netstat */
2133bd670b35SErik Nordmark 			} else {
2134f1c454b4SSowmini Varadhan 				/* some other thread is re-attaching this. */
2135f1c454b4SSowmini Varadhan 				rw_exit(&connp->conn_ilg_lock);
2136f1c454b4SSowmini Varadhan 				(void) ip_delmulti_serial(ilm, B_FALSE, B_TRUE);
2137f1c454b4SSowmini Varadhan 				err = 0;
2138f1c454b4SSowmini Varadhan 				goto free_and_exit;
2139f1c454b4SSowmini Varadhan 			}
2140f1c454b4SSowmini Varadhan 		} else {
2141bd670b35SErik Nordmark 			/*
2142bd670b35SErik Nordmark 			 * ip_addmulti didn't get a held ilm for
2143bd670b35SErik Nordmark 			 * ILGSTAT_CHANGE; ilm_refcnt was unchanged.
2144bd670b35SErik Nordmark 			 */
2145bd670b35SErik Nordmark 			ASSERT(ilg->ilg_ilm == ilm);
2146bd670b35SErik Nordmark 		}
2147bd670b35SErik Nordmark 	} else {
2148bd670b35SErik Nordmark 		ASSERT(err != 0);
2149bd670b35SErik Nordmark 		/*
2150bd670b35SErik Nordmark 		 * Failed to allocate the ilm.
2151bd670b35SErik Nordmark 		 * Restore the original filter state, or delete the
2152bd670b35SErik Nordmark 		 * newly-created ilg.
2153bd670b35SErik Nordmark 		 * If ENETDOWN just clear ill_ilg since so that we
2154bd670b35SErik Nordmark 		 * will rejoin when the ill comes back; don't report ENETDOWN
2155bd670b35SErik Nordmark 		 * to application.
2156bd670b35SErik Nordmark 		 */
2157bd670b35SErik Nordmark 		if (ilgstat == ILGSTAT_NEW) {
2158bd670b35SErik Nordmark 			if (err == ENETDOWN) {
2159bd670b35SErik Nordmark 				ilg->ilg_ill = NULL;
2160bd670b35SErik Nordmark 				err = 0;
2161bd670b35SErik Nordmark 			} else {
21627c478bd9Sstevel@tonic-gate 				ilg_delete(connp, ilg, NULL);
2163bd670b35SErik Nordmark 			}
21647c478bd9Sstevel@tonic-gate 		} else {
21657c478bd9Sstevel@tonic-gate 			ilg->ilg_fmode = orig_fmode;
21667c478bd9Sstevel@tonic-gate 			if (SLIST_IS_EMPTY(orig_filter)) {
21677c478bd9Sstevel@tonic-gate 				CLEAR_SLIST(ilg->ilg_filter);
21687c478bd9Sstevel@tonic-gate 			} else {
21697c478bd9Sstevel@tonic-gate 				/*
21707c478bd9Sstevel@tonic-gate 				 * We didn't free the filter, even if we
21717c478bd9Sstevel@tonic-gate 				 * were trying to make the source list empty;
21727c478bd9Sstevel@tonic-gate 				 * so if orig_filter isn't empty, the ilg
21737c478bd9Sstevel@tonic-gate 				 * must still have a filter alloc'd.
21747c478bd9Sstevel@tonic-gate 				 */
21757c478bd9Sstevel@tonic-gate 				l_copy(orig_filter, ilg->ilg_filter);
21767c478bd9Sstevel@tonic-gate 			}
21777c478bd9Sstevel@tonic-gate 		}
21787c478bd9Sstevel@tonic-gate 	}
2179bd670b35SErik Nordmark 	rw_exit(&connp->conn_ilg_lock);
21807c478bd9Sstevel@tonic-gate 
21817c478bd9Sstevel@tonic-gate free_and_exit:
2182bd670b35SErik Nordmark 	mutex_exit(&ill->ill_mcast_serializer);
2183f1c454b4SSowmini Varadhan 	ill_mcast_send_queued(ill);
2184f1c454b4SSowmini Varadhan 	ill_dlpi_send_queued(ill);
21857c478bd9Sstevel@tonic-gate 	l_free(orig_filter);
21867c478bd9Sstevel@tonic-gate 	l_free(new_filter);
21877c478bd9Sstevel@tonic-gate 
21887c478bd9Sstevel@tonic-gate 	return (err);
21897c478bd9Sstevel@tonic-gate }
21907c478bd9Sstevel@tonic-gate 
21917c478bd9Sstevel@tonic-gate /*
21927c478bd9Sstevel@tonic-gate  * Process the SIOC[GS]MSFILTER and SIOC[GS]IPMSFILTER ioctls.
21937c478bd9Sstevel@tonic-gate  */
21947c478bd9Sstevel@tonic-gate /* ARGSUSED */
21957c478bd9Sstevel@tonic-gate int
21967c478bd9Sstevel@tonic-gate ip_sioctl_msfilter(ipif_t *ipif, sin_t *dummy_sin, queue_t *q, mblk_t *mp,
21977c478bd9Sstevel@tonic-gate     ip_ioctl_cmd_t *ipip, void *ifreq)
21987c478bd9Sstevel@tonic-gate {
21997c478bd9Sstevel@tonic-gate 	struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
22007c478bd9Sstevel@tonic-gate 	/* existence verified in ip_wput_nondata() */
22017c478bd9Sstevel@tonic-gate 	mblk_t *data_mp = mp->b_cont->b_cont;
22027c478bd9Sstevel@tonic-gate 	int datalen, err, cmd, minsize;
2203fda3a3a5Sblu 	uint_t expsize = 0;
22047c478bd9Sstevel@tonic-gate 	conn_t *connp;
22057c478bd9Sstevel@tonic-gate 	boolean_t isv6, is_v4only_api, getcmd;
22067c478bd9Sstevel@tonic-gate 	struct sockaddr_in *gsin;
22077c478bd9Sstevel@tonic-gate 	struct sockaddr_in6 *gsin6;
2208bd670b35SErik Nordmark 	ipaddr_t v4group;
2209bd670b35SErik Nordmark 	in6_addr_t v6group;
22107c478bd9Sstevel@tonic-gate 	struct group_filter *gf = NULL;
22117c478bd9Sstevel@tonic-gate 	struct ip_msfilter *imsf = NULL;
22127c478bd9Sstevel@tonic-gate 	mblk_t *ndp;
2213bd670b35SErik Nordmark 	ill_t *ill;
2214bd670b35SErik Nordmark 
2215bd670b35SErik Nordmark 	connp = Q_TO_CONN(q);
2216bd670b35SErik Nordmark 	err = ip_msfilter_ill(connp, mp, ipip, &ill);
2217bd670b35SErik Nordmark 	if (err != 0)
2218bd670b35SErik Nordmark 		return (err);
22197c478bd9Sstevel@tonic-gate 
22207c478bd9Sstevel@tonic-gate 	if (data_mp->b_cont != NULL) {
22217c478bd9Sstevel@tonic-gate 		if ((ndp = msgpullup(data_mp, -1)) == NULL)
22227c478bd9Sstevel@tonic-gate 			return (ENOMEM);
22237c478bd9Sstevel@tonic-gate 		freemsg(data_mp);
22247c478bd9Sstevel@tonic-gate 		data_mp = ndp;
22257c478bd9Sstevel@tonic-gate 		mp->b_cont->b_cont = data_mp;
22267c478bd9Sstevel@tonic-gate 	}
22277c478bd9Sstevel@tonic-gate 
22287c478bd9Sstevel@tonic-gate 	cmd = iocp->ioc_cmd;
22297c478bd9Sstevel@tonic-gate 	getcmd = (cmd == SIOCGIPMSFILTER || cmd == SIOCGMSFILTER);
22307c478bd9Sstevel@tonic-gate 	is_v4only_api = (cmd == SIOCGIPMSFILTER || cmd == SIOCSIPMSFILTER);
22317c478bd9Sstevel@tonic-gate 	minsize = (is_v4only_api) ? IP_MSFILTER_SIZE(0) : GROUP_FILTER_SIZE(0);
22327c478bd9Sstevel@tonic-gate 	datalen = MBLKL(data_mp);
22337c478bd9Sstevel@tonic-gate 
22347c478bd9Sstevel@tonic-gate 	if (datalen < minsize)
22357c478bd9Sstevel@tonic-gate 		return (EINVAL);
22367c478bd9Sstevel@tonic-gate 
22377c478bd9Sstevel@tonic-gate 	/*
22387c478bd9Sstevel@tonic-gate 	 * now we know we have at least have the initial structure,
22397c478bd9Sstevel@tonic-gate 	 * but need to check for the source list array.
22407c478bd9Sstevel@tonic-gate 	 */
22417c478bd9Sstevel@tonic-gate 	if (is_v4only_api) {
22427c478bd9Sstevel@tonic-gate 		imsf = (struct ip_msfilter *)data_mp->b_rptr;
22437c478bd9Sstevel@tonic-gate 		isv6 = B_FALSE;
22447c478bd9Sstevel@tonic-gate 		expsize = IP_MSFILTER_SIZE(imsf->imsf_numsrc);
22457c478bd9Sstevel@tonic-gate 	} else {
22467c478bd9Sstevel@tonic-gate 		gf = (struct group_filter *)data_mp->b_rptr;
22477c478bd9Sstevel@tonic-gate 		if (gf->gf_group.ss_family == AF_INET6) {
22487c478bd9Sstevel@tonic-gate 			gsin6 = (struct sockaddr_in6 *)&gf->gf_group;
22497c478bd9Sstevel@tonic-gate 			isv6 = !(IN6_IS_ADDR_V4MAPPED(&gsin6->sin6_addr));
22507c478bd9Sstevel@tonic-gate 		} else {
22517c478bd9Sstevel@tonic-gate 			isv6 = B_FALSE;
22527c478bd9Sstevel@tonic-gate 		}
22537c478bd9Sstevel@tonic-gate 		expsize = GROUP_FILTER_SIZE(gf->gf_numsrc);
22547c478bd9Sstevel@tonic-gate 	}
22557c478bd9Sstevel@tonic-gate 	if (datalen < expsize)
22567c478bd9Sstevel@tonic-gate 		return (EINVAL);
22577c478bd9Sstevel@tonic-gate 
22587c478bd9Sstevel@tonic-gate 	if (isv6) {
22597c478bd9Sstevel@tonic-gate 		gsin6 = (struct sockaddr_in6 *)&gf->gf_group;
2260bd670b35SErik Nordmark 		v6group = gsin6->sin6_addr;
2261bd670b35SErik Nordmark 		if (getcmd) {
2262bd670b35SErik Nordmark 			err = ip_get_srcfilter(connp, gf, NULL, &v6group,
2263bd670b35SErik Nordmark 			    B_TRUE);
22647c478bd9Sstevel@tonic-gate 		} else {
2265bd670b35SErik Nordmark 			err = ip_set_srcfilter(connp, gf, NULL, &v6group, ill,
2266bd670b35SErik Nordmark 			    B_TRUE);
2267bd670b35SErik Nordmark 		}
2268bd670b35SErik Nordmark 	} else {
2269bd670b35SErik Nordmark 		boolean_t issin6 = B_FALSE;
22707c478bd9Sstevel@tonic-gate 		if (is_v4only_api) {
2271bd670b35SErik Nordmark 			v4group = (ipaddr_t)imsf->imsf_multiaddr.s_addr;
2272bd670b35SErik Nordmark 			IN6_IPADDR_TO_V4MAPPED(v4group, &v6group);
22737c478bd9Sstevel@tonic-gate 		} else {
22747c478bd9Sstevel@tonic-gate 			if (gf->gf_group.ss_family == AF_INET) {
22757c478bd9Sstevel@tonic-gate 				gsin = (struct sockaddr_in *)&gf->gf_group;
2276bd670b35SErik Nordmark 				v4group = (ipaddr_t)gsin->sin_addr.s_addr;
2277bd670b35SErik Nordmark 				IN6_IPADDR_TO_V4MAPPED(v4group, &v6group);
22787c478bd9Sstevel@tonic-gate 			} else {
22797c478bd9Sstevel@tonic-gate 				gsin6 = (struct sockaddr_in6 *)&gf->gf_group;
22807c478bd9Sstevel@tonic-gate 				IN6_V4MAPPED_TO_IPADDR(&gsin6->sin6_addr,
2281bd670b35SErik Nordmark 				    v4group);
2282bd670b35SErik Nordmark 				issin6 = B_TRUE;
22837c478bd9Sstevel@tonic-gate 			}
22847c478bd9Sstevel@tonic-gate 		}
2285bd670b35SErik Nordmark 		/*
2286bd670b35SErik Nordmark 		 * INADDR_ANY is represented as the IPv6 unspecifed addr.
2287bd670b35SErik Nordmark 		 */
2288bd670b35SErik Nordmark 		if (v4group == INADDR_ANY)
2289bd670b35SErik Nordmark 			v6group = ipv6_all_zeros;
22907c478bd9Sstevel@tonic-gate 		else
2291bd670b35SErik Nordmark 			IN6_IPADDR_TO_V4MAPPED(v4group, &v6group);
2292bd670b35SErik Nordmark 
2293bd670b35SErik Nordmark 		if (getcmd) {
2294bd670b35SErik Nordmark 			err = ip_get_srcfilter(connp, gf, imsf, &v6group,
2295bd670b35SErik Nordmark 			    issin6);
2296bd670b35SErik Nordmark 		} else {
2297bd670b35SErik Nordmark 			err = ip_set_srcfilter(connp, gf, imsf, &v6group, ill,
2298bd670b35SErik Nordmark 			    issin6);
22997c478bd9Sstevel@tonic-gate 		}
2300bd670b35SErik Nordmark 	}
2301bd670b35SErik Nordmark 	ill_refrele(ill);
23027c478bd9Sstevel@tonic-gate 
23037c478bd9Sstevel@tonic-gate 	return (err);
23047c478bd9Sstevel@tonic-gate }
23057c478bd9Sstevel@tonic-gate 
23067c478bd9Sstevel@tonic-gate /*
2307bd670b35SErik Nordmark  * Determine the ill for the SIOC*MSFILTER ioctls
2308bd670b35SErik Nordmark  *
2309bd670b35SErik Nordmark  * Returns an error for IS_UNDER_IPMP interfaces.
2310bd670b35SErik Nordmark  *
2311bd670b35SErik Nordmark  * Finds the ill based on information in the ioctl headers.
23127c478bd9Sstevel@tonic-gate  */
2313bd670b35SErik Nordmark static int
2314bd670b35SErik Nordmark ip_msfilter_ill(conn_t *connp, mblk_t *mp, const ip_ioctl_cmd_t *ipip,
2315bd670b35SErik Nordmark     ill_t **illp)
23167c478bd9Sstevel@tonic-gate {
231798e93c29Smeem 	int cmd = ipip->ipi_cmd;
231898e93c29Smeem 	int err = 0;
2319bd670b35SErik Nordmark 	ill_t *ill;
23207c478bd9Sstevel@tonic-gate 	/* caller has verified this mblk exists */
23217c478bd9Sstevel@tonic-gate 	char *dbuf = (char *)mp->b_cont->b_cont->b_rptr;
23227c478bd9Sstevel@tonic-gate 	struct ip_msfilter *imsf;
23237c478bd9Sstevel@tonic-gate 	struct group_filter *gf;
2324bd670b35SErik Nordmark 	ipaddr_t v4addr, v4group;
2325bd670b35SErik Nordmark 	in6_addr_t v6group;
23267c478bd9Sstevel@tonic-gate 	uint32_t index;
2327f4b3ec61Sdh155122 	ip_stack_t *ipst;
23287c478bd9Sstevel@tonic-gate 
2329f4b3ec61Sdh155122 	ipst = connp->conn_netstack->netstack_ip;
23307c478bd9Sstevel@tonic-gate 
2331bd670b35SErik Nordmark 	*illp = NULL;
2332bd670b35SErik Nordmark 
23337c478bd9Sstevel@tonic-gate 	/* don't allow multicast operations on a tcp conn */
2334ff550d0eSmasputra 	if (IPCL_IS_TCP(connp))
23357c478bd9Sstevel@tonic-gate 		return (ENOPROTOOPT);
23367c478bd9Sstevel@tonic-gate 
23377c478bd9Sstevel@tonic-gate 	if (cmd == SIOCSIPMSFILTER || cmd == SIOCGIPMSFILTER) {
23387c478bd9Sstevel@tonic-gate 		/* don't allow v4-specific ioctls on v6 socket */
2339bd670b35SErik Nordmark 		if (connp->conn_family == AF_INET6)
23407c478bd9Sstevel@tonic-gate 			return (EAFNOSUPPORT);
23417c478bd9Sstevel@tonic-gate 
23427c478bd9Sstevel@tonic-gate 		imsf = (struct ip_msfilter *)dbuf;
23437c478bd9Sstevel@tonic-gate 		v4addr = imsf->imsf_interface.s_addr;
2344bd670b35SErik Nordmark 		v4group = imsf->imsf_multiaddr.s_addr;
2345bd670b35SErik Nordmark 		IN6_IPADDR_TO_V4MAPPED(v4group, &v6group);
2346bd670b35SErik Nordmark 		ill = ill_mcast_lookup(&v6group, v4addr, 0, IPCL_ZONEID(connp),
2347bd670b35SErik Nordmark 		    ipst, &err);
2348bd670b35SErik Nordmark 		if (ill == NULL && v4addr != INADDR_ANY)
2349bd670b35SErik Nordmark 			err = ENXIO;
23507c478bd9Sstevel@tonic-gate 	} else {
23517c478bd9Sstevel@tonic-gate 		gf = (struct group_filter *)dbuf;
23527c478bd9Sstevel@tonic-gate 		index = gf->gf_interface;
23537c478bd9Sstevel@tonic-gate 		if (gf->gf_group.ss_family == AF_INET6) {
23547c478bd9Sstevel@tonic-gate 			struct sockaddr_in6 *sin6;
2355bd670b35SErik Nordmark 
23567c478bd9Sstevel@tonic-gate 			sin6 = (struct sockaddr_in6 *)&gf->gf_group;
2357bd670b35SErik Nordmark 			v6group = sin6->sin6_addr;
23587c478bd9Sstevel@tonic-gate 		} else if (gf->gf_group.ss_family == AF_INET) {
23597c478bd9Sstevel@tonic-gate 			struct sockaddr_in *sin;
2360bd670b35SErik Nordmark 
23617c478bd9Sstevel@tonic-gate 			sin = (struct sockaddr_in *)&gf->gf_group;
2362bd670b35SErik Nordmark 			v4group = sin->sin_addr.s_addr;
2363bd670b35SErik Nordmark 			IN6_IPADDR_TO_V4MAPPED(v4group, &v6group);
23647c478bd9Sstevel@tonic-gate 		} else {
23657c478bd9Sstevel@tonic-gate 			return (EAFNOSUPPORT);
23667c478bd9Sstevel@tonic-gate 		}
2367bd670b35SErik Nordmark 		ill = ill_mcast_lookup(&v6group, INADDR_ANY, index,
2368bd670b35SErik Nordmark 		    IPCL_ZONEID(connp), ipst, &err);
2369f4b3ec61Sdh155122 	}
2370bd670b35SErik Nordmark 	*illp = ill;
23717c478bd9Sstevel@tonic-gate 	return (err);
23727c478bd9Sstevel@tonic-gate }
23737c478bd9Sstevel@tonic-gate 
23747c478bd9Sstevel@tonic-gate /*
23757c478bd9Sstevel@tonic-gate  * The structures used for the SIOC*MSFILTER ioctls usually must be copied
23767c478bd9Sstevel@tonic-gate  * in in two stages, as the first copyin tells us the size of the attached
23777c478bd9Sstevel@tonic-gate  * source buffer.  This function is called by ip_wput_nondata() after the
23787c478bd9Sstevel@tonic-gate  * first copyin has completed; it figures out how big the second stage
23797c478bd9Sstevel@tonic-gate  * needs to be, and kicks it off.
23807c478bd9Sstevel@tonic-gate  *
23817c478bd9Sstevel@tonic-gate  * In some cases (numsrc < 2), the second copyin is not needed as the
23827c478bd9Sstevel@tonic-gate  * first one gets a complete structure containing 1 source addr.
23837c478bd9Sstevel@tonic-gate  *
23847c478bd9Sstevel@tonic-gate  * The function returns 0 if a second copyin has been started (i.e. there's
23857c478bd9Sstevel@tonic-gate  * no more work to be done right now), or 1 if the second copyin is not
23867c478bd9Sstevel@tonic-gate  * needed and ip_wput_nondata() can continue its processing.
23877c478bd9Sstevel@tonic-gate  */
23887c478bd9Sstevel@tonic-gate int
23897c478bd9Sstevel@tonic-gate ip_copyin_msfilter(queue_t *q, mblk_t *mp)
23907c478bd9Sstevel@tonic-gate {
23917c478bd9Sstevel@tonic-gate 	struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
23927c478bd9Sstevel@tonic-gate 	int cmd = iocp->ioc_cmd;
23937c478bd9Sstevel@tonic-gate 	/* validity of this checked in ip_wput_nondata() */
23947c478bd9Sstevel@tonic-gate 	mblk_t *mp1 = mp->b_cont->b_cont;
23957c478bd9Sstevel@tonic-gate 	int copysize = 0;
23967c478bd9Sstevel@tonic-gate 	int offset;
23977c478bd9Sstevel@tonic-gate 
23987c478bd9Sstevel@tonic-gate 	if (cmd == SIOCSMSFILTER || cmd == SIOCGMSFILTER) {
23997c478bd9Sstevel@tonic-gate 		struct group_filter *gf = (struct group_filter *)mp1->b_rptr;
24007c478bd9Sstevel@tonic-gate 		if (gf->gf_numsrc >= 2) {
24017c478bd9Sstevel@tonic-gate 			offset = sizeof (struct group_filter);
24027c478bd9Sstevel@tonic-gate 			copysize = GROUP_FILTER_SIZE(gf->gf_numsrc) - offset;
24037c478bd9Sstevel@tonic-gate 		}
24047c478bd9Sstevel@tonic-gate 	} else {
24057c478bd9Sstevel@tonic-gate 		struct ip_msfilter *imsf = (struct ip_msfilter *)mp1->b_rptr;
24067c478bd9Sstevel@tonic-gate 		if (imsf->imsf_numsrc >= 2) {
24077c478bd9Sstevel@tonic-gate 			offset = sizeof (struct ip_msfilter);
24087c478bd9Sstevel@tonic-gate 			copysize = IP_MSFILTER_SIZE(imsf->imsf_numsrc) - offset;
24097c478bd9Sstevel@tonic-gate 		}
24107c478bd9Sstevel@tonic-gate 	}
24117c478bd9Sstevel@tonic-gate 	if (copysize > 0) {
24127c478bd9Sstevel@tonic-gate 		mi_copyin_n(q, mp, offset, copysize);
24137c478bd9Sstevel@tonic-gate 		return (0);
24147c478bd9Sstevel@tonic-gate 	}
24157c478bd9Sstevel@tonic-gate 	return (1);
24167c478bd9Sstevel@tonic-gate }
24177c478bd9Sstevel@tonic-gate 
24187c478bd9Sstevel@tonic-gate /*
24197c478bd9Sstevel@tonic-gate  * Handle the following optmgmt:
24207c478bd9Sstevel@tonic-gate  *	IP_ADD_MEMBERSHIP		must not have joined already
2421bd670b35SErik Nordmark  *	IPV6_JOIN_GROUP			must not have joined already
24227c478bd9Sstevel@tonic-gate  *	MCAST_JOIN_GROUP		must not have joined already
24237c478bd9Sstevel@tonic-gate  *	IP_BLOCK_SOURCE			must have joined already
24247c478bd9Sstevel@tonic-gate  *	MCAST_BLOCK_SOURCE		must have joined already
24257c478bd9Sstevel@tonic-gate  *	IP_JOIN_SOURCE_GROUP		may have joined already
24267c478bd9Sstevel@tonic-gate  *	MCAST_JOIN_SOURCE_GROUP		may have joined already
24277c478bd9Sstevel@tonic-gate  *
24287c478bd9Sstevel@tonic-gate  * fmode and src parameters may be used to determine which option is
24297c478bd9Sstevel@tonic-gate  * being set, as follows (IPV6_JOIN_GROUP and MCAST_JOIN_GROUP options
24307c478bd9Sstevel@tonic-gate  * are functionally equivalent):
24317c478bd9Sstevel@tonic-gate  *	opt			fmode			v6src
2432bd670b35SErik Nordmark  *	IP_ADD_MEMBERSHIP	MODE_IS_EXCLUDE		unspecified
24337c478bd9Sstevel@tonic-gate  *	IPV6_JOIN_GROUP		MODE_IS_EXCLUDE		unspecified
24347c478bd9Sstevel@tonic-gate  *	MCAST_JOIN_GROUP	MODE_IS_EXCLUDE		unspecified
2435bd670b35SErik Nordmark  *	IP_BLOCK_SOURCE		MODE_IS_EXCLUDE		IPv4-mapped addr
24367c478bd9Sstevel@tonic-gate  *	MCAST_BLOCK_SOURCE	MODE_IS_EXCLUDE		v6 addr
2437bd670b35SErik Nordmark  *	IP_JOIN_SOURCE_GROUP	MODE_IS_INCLUDE		IPv4-mapped addr
24387c478bd9Sstevel@tonic-gate  *	MCAST_JOIN_SOURCE_GROUP	MODE_IS_INCLUDE		v6 addr
24397c478bd9Sstevel@tonic-gate  *
24407c478bd9Sstevel@tonic-gate  * Changing the filter mode is not allowed; if a matching ilg already
24417c478bd9Sstevel@tonic-gate  * exists and fmode != ilg->ilg_fmode, EINVAL is returned.
24427c478bd9Sstevel@tonic-gate  *
24437c478bd9Sstevel@tonic-gate  * Verifies that there is a source address of appropriate scope for
24447c478bd9Sstevel@tonic-gate  * the group; if not, EADDRNOTAVAIL is returned.
24457c478bd9Sstevel@tonic-gate  *
2446bd670b35SErik Nordmark  * The interface to be used may be identified by an IPv4 address or by an
2447bd670b35SErik Nordmark  * interface index.
2448bd670b35SErik Nordmark  *
24497c478bd9Sstevel@tonic-gate  * Handles IPv4-mapped IPv6 multicast addresses by associating them
2450bd670b35SErik Nordmark  * with the IPv4 address.  Assumes that if v6group is v4-mapped,
24517c478bd9Sstevel@tonic-gate  * v6src is also v4-mapped.
24527c478bd9Sstevel@tonic-gate  */
24537c478bd9Sstevel@tonic-gate int
2454bd670b35SErik Nordmark ip_opt_add_group(conn_t *connp, boolean_t checkonly,
2455bd670b35SErik Nordmark     const in6_addr_t *v6group, ipaddr_t ifaddr, uint_t ifindex,
2456bd670b35SErik Nordmark     mcast_record_t fmode, const in6_addr_t *v6src)
24577c478bd9Sstevel@tonic-gate {
24587c478bd9Sstevel@tonic-gate 	ill_t *ill;
24597c478bd9Sstevel@tonic-gate 	char buf[INET6_ADDRSTRLEN];
24607c478bd9Sstevel@tonic-gate 	int	err;
24617c478bd9Sstevel@tonic-gate 
2462bd670b35SErik Nordmark 	err = ip_opt_check(connp, v6group, v6src, ifaddr, ifindex, &ill);
24637c478bd9Sstevel@tonic-gate 	if (err != 0) {
2464bd670b35SErik Nordmark 		ip1dbg(("ip_opt_add_group: no ill for group %s/"
24657c478bd9Sstevel@tonic-gate 		    "index %d\n", inet_ntop(AF_INET6, v6group, buf,
24667c478bd9Sstevel@tonic-gate 		    sizeof (buf)), ifindex));
24677c478bd9Sstevel@tonic-gate 		return (err);
24687c478bd9Sstevel@tonic-gate 	}
24697c478bd9Sstevel@tonic-gate 
24707c478bd9Sstevel@tonic-gate 	if (checkonly) {
24717c478bd9Sstevel@tonic-gate 		/*
24727c478bd9Sstevel@tonic-gate 		 * do not do operation, just pretend to - new T_CHECK
24737c478bd9Sstevel@tonic-gate 		 * semantics. The error return case above if encountered
24747c478bd9Sstevel@tonic-gate 		 * considered a good enough "check" here.
24757c478bd9Sstevel@tonic-gate 		 */
24767c478bd9Sstevel@tonic-gate 		ill_refrele(ill);
24777c478bd9Sstevel@tonic-gate 		return (0);
24787c478bd9Sstevel@tonic-gate 	}
2479bd670b35SErik Nordmark 	mutex_enter(&ill->ill_mcast_serializer);
2480f1c454b4SSowmini Varadhan 	/*
2481f1c454b4SSowmini Varadhan 	 * Multicast groups may not be joined on interfaces that are either
2482f1c454b4SSowmini Varadhan 	 * already underlying interfaces in an IPMP group, or in the process
2483f1c454b4SSowmini Varadhan 	 * of joining the IPMP group. The latter condition is enforced by
2484f1c454b4SSowmini Varadhan 	 * checking the value of ill->ill_grp_pending under the
2485f1c454b4SSowmini Varadhan 	 * ill_mcast_serializer lock.  We cannot serialize the
2486f1c454b4SSowmini Varadhan 	 * ill_grp_pending check on the ill_g_lock across ilg_add() because
2487f1c454b4SSowmini Varadhan 	 *  ill_mcast_send_queued -> ip_output_simple -> ill_lookup_on_ifindex
2488f1c454b4SSowmini Varadhan 	 * will take the ill_g_lock itself. Instead, we hold the
2489f1c454b4SSowmini Varadhan 	 * ill_mcast_serializer.
2490f1c454b4SSowmini Varadhan 	 */
2491f1c454b4SSowmini Varadhan 	if (ill->ill_grp_pending || IS_UNDER_IPMP(ill)) {
2492f1c454b4SSowmini Varadhan 		DTRACE_PROBE2(group__add__on__under, ill_t *, ill,
2493f1c454b4SSowmini Varadhan 		    in6_addr_t *, v6group);
2494f1c454b4SSowmini Varadhan 		mutex_exit(&ill->ill_mcast_serializer);
2495f1c454b4SSowmini Varadhan 		ill_refrele(ill);
2496f1c454b4SSowmini Varadhan 		return (EADDRNOTAVAIL);
2497f1c454b4SSowmini Varadhan 	}
2498bd670b35SErik Nordmark 	err = ilg_add(connp, v6group, ifaddr, ifindex, ill, fmode, v6src);
2499bd670b35SErik Nordmark 	mutex_exit(&ill->ill_mcast_serializer);
2500f1c454b4SSowmini Varadhan 	/*
2501f1c454b4SSowmini Varadhan 	 * We have done an addmulti_impl and/or delmulti_impl.
2502f1c454b4SSowmini Varadhan 	 * All locks have been dropped, we can send any
2503f1c454b4SSowmini Varadhan 	 * deferred/queued DLPI or IP packets
2504f1c454b4SSowmini Varadhan 	 */
2505f1c454b4SSowmini Varadhan 	ill_mcast_send_queued(ill);
2506f1c454b4SSowmini Varadhan 	ill_dlpi_send_queued(ill);
25077c478bd9Sstevel@tonic-gate 	ill_refrele(ill);
25087c478bd9Sstevel@tonic-gate 	return (err);
25097c478bd9Sstevel@tonic-gate }
25107c478bd9Sstevel@tonic-gate 
2511bd670b35SErik Nordmark /*
2512bd670b35SErik Nordmark  * Common for IPv6 and IPv4.
2513bd670b35SErik Nordmark  * Here we handle ilgs that are still attached to their original ill
2514bd670b35SErik Nordmark  * (the one ifaddr/ifindex points at), as well as detached ones.
2515bd670b35SErik Nordmark  * The detached ones might have been attached to some other ill.
2516bd670b35SErik Nordmark  */
25177c478bd9Sstevel@tonic-gate static int
2518bd670b35SErik Nordmark ip_opt_delete_group_excl(conn_t *connp, const in6_addr_t *v6group,
2519bd670b35SErik Nordmark     ipaddr_t ifaddr, uint_t ifindex, mcast_record_t fmode,
2520bd670b35SErik Nordmark     const in6_addr_t *v6src)
25217c478bd9Sstevel@tonic-gate {
25227c478bd9Sstevel@tonic-gate 	ilg_t	*ilg;
2523bd670b35SErik Nordmark 	boolean_t leaving;
2524bd670b35SErik Nordmark 	ilm_t *ilm;
2525bd670b35SErik Nordmark 	ill_t *ill;
25267c478bd9Sstevel@tonic-gate 	int err = 0;
2527bd670b35SErik Nordmark 
2528bd670b35SErik Nordmark retry:
2529bd670b35SErik Nordmark 	rw_enter(&connp->conn_ilg_lock, RW_WRITER);
2530bd670b35SErik Nordmark 	ilg = ilg_lookup(connp, v6group, ifaddr, ifindex);
2531bd670b35SErik Nordmark 	if (ilg == NULL) {
2532bd670b35SErik Nordmark 		rw_exit(&connp->conn_ilg_lock);
2533bd670b35SErik Nordmark 		/*
2534bd670b35SErik Nordmark 		 * Since we didn't have any ilg we now do the error checks
2535bd670b35SErik Nordmark 		 * to determine the best errno.
2536bd670b35SErik Nordmark 		 */
2537bd670b35SErik Nordmark 		err = ip_opt_check(connp, v6group, v6src, ifaddr, ifindex,
2538bd670b35SErik Nordmark 		    &ill);
2539bd670b35SErik Nordmark 		if (ill != NULL) {
2540bd670b35SErik Nordmark 			/* The only error was a missing ilg for the group */
2541bd670b35SErik Nordmark 			ill_refrele(ill);
25427c478bd9Sstevel@tonic-gate 			err = EADDRNOTAVAIL;
2543bd670b35SErik Nordmark 		}
25447c478bd9Sstevel@tonic-gate 		return (err);
25457c478bd9Sstevel@tonic-gate 	}
2546bd670b35SErik Nordmark 
2547bd670b35SErik Nordmark 	/* If the ilg is attached then we serialize using that ill */
2548bd670b35SErik Nordmark 	ill = ilg->ilg_ill;
2549bd670b35SErik Nordmark 	if (ill != NULL) {
2550bd670b35SErik Nordmark 		/* Prevent the ill and ilg from being freed */
2551bd670b35SErik Nordmark 		ill_refhold(ill);
2552bd670b35SErik Nordmark 		ilg_refhold(ilg);
2553bd670b35SErik Nordmark 		rw_exit(&connp->conn_ilg_lock);
2554bd670b35SErik Nordmark 		mutex_enter(&ill->ill_mcast_serializer);
2555bd670b35SErik Nordmark 		rw_enter(&connp->conn_ilg_lock, RW_WRITER);
2556bd670b35SErik Nordmark 		if (ilg->ilg_condemned) {
2557bd670b35SErik Nordmark 			/* Disappeared */
2558bd670b35SErik Nordmark 			ilg_refrele(ilg);
2559bd670b35SErik Nordmark 			rw_exit(&connp->conn_ilg_lock);
2560bd670b35SErik Nordmark 			mutex_exit(&ill->ill_mcast_serializer);
2561bd670b35SErik Nordmark 			ill_refrele(ill);
2562bd670b35SErik Nordmark 			goto retry;
25637c478bd9Sstevel@tonic-gate 		}
25647c478bd9Sstevel@tonic-gate 	}
25657c478bd9Sstevel@tonic-gate 
25667c478bd9Sstevel@tonic-gate 	/*
25677c478bd9Sstevel@tonic-gate 	 * Decide if we're actually deleting the ilg or just removing a
25687c478bd9Sstevel@tonic-gate 	 * source filter address; if just removing an addr, make sure we
25697c478bd9Sstevel@tonic-gate 	 * aren't trying to change the filter mode, and that the addr is
25707c478bd9Sstevel@tonic-gate 	 * actually in our filter list already.  If we're removing the
25717c478bd9Sstevel@tonic-gate 	 * last src in an include list, just delete the ilg.
25727c478bd9Sstevel@tonic-gate 	 */
2573bd670b35SErik Nordmark 	if (IN6_IS_ADDR_UNSPECIFIED(v6src)) {
2574bd670b35SErik Nordmark 		leaving = B_TRUE;
2575bd670b35SErik Nordmark 	} else {
25767c478bd9Sstevel@tonic-gate 		if (fmode != ilg->ilg_fmode)
25777c478bd9Sstevel@tonic-gate 			err = EINVAL;
25787c478bd9Sstevel@tonic-gate 		else if (ilg->ilg_filter == NULL ||
25797c478bd9Sstevel@tonic-gate 		    !list_has_addr(ilg->ilg_filter, v6src))
25807c478bd9Sstevel@tonic-gate 			err = EADDRNOTAVAIL;
25817c478bd9Sstevel@tonic-gate 		if (err != 0) {
2582bd670b35SErik Nordmark 			if (ill != NULL)
2583bd670b35SErik Nordmark 				ilg_refrele(ilg);
2584bd670b35SErik Nordmark 			rw_exit(&connp->conn_ilg_lock);
2585bd670b35SErik Nordmark 			goto done;
25867c478bd9Sstevel@tonic-gate 		}
25877c478bd9Sstevel@tonic-gate 		if (fmode == MODE_IS_INCLUDE &&
2588bd670b35SErik Nordmark 		    ilg->ilg_filter->sl_numsrc == 1) {
2589bd670b35SErik Nordmark 			leaving = B_TRUE;
25907c478bd9Sstevel@tonic-gate 			v6src = NULL;
2591bd670b35SErik Nordmark 		} else {
25927c478bd9Sstevel@tonic-gate 			leaving = B_FALSE;
25937c478bd9Sstevel@tonic-gate 		}
2594bd670b35SErik Nordmark 	}
2595bd670b35SErik Nordmark 	ilm = ilg->ilg_ilm;
2596bd670b35SErik Nordmark 	if (leaving)
2597bd670b35SErik Nordmark 		ilg->ilg_ilm = NULL;
25987c478bd9Sstevel@tonic-gate 
25997c478bd9Sstevel@tonic-gate 	ilg_delete(connp, ilg, v6src);
2600bd670b35SErik Nordmark 	if (ill != NULL)
2601bd670b35SErik Nordmark 		ilg_refrele(ilg);
2602bd670b35SErik Nordmark 	rw_exit(&connp->conn_ilg_lock);
26037c478bd9Sstevel@tonic-gate 
2604bd670b35SErik Nordmark 	if (ilm != NULL) {
2605bd670b35SErik Nordmark 		ASSERT(ill != NULL);
2606bd670b35SErik Nordmark 		(void) ip_delmulti_serial(ilm, B_FALSE, leaving);
2607bd670b35SErik Nordmark 	}
2608bd670b35SErik Nordmark done:
2609bd670b35SErik Nordmark 	if (ill != NULL) {
2610bd670b35SErik Nordmark 		mutex_exit(&ill->ill_mcast_serializer);
2611f1c454b4SSowmini Varadhan 		/*
2612f1c454b4SSowmini Varadhan 		 * Now that all locks have been dropped, we can
2613f1c454b4SSowmini Varadhan 		 * send any deferred/queued DLPI or IP packets
2614f1c454b4SSowmini Varadhan 		 */
2615f1c454b4SSowmini Varadhan 		ill_mcast_send_queued(ill);
2616f1c454b4SSowmini Varadhan 		ill_dlpi_send_queued(ill);
2617bd670b35SErik Nordmark 		ill_refrele(ill);
2618bd670b35SErik Nordmark 	}
2619bd670b35SErik Nordmark 	return (err);
26207c478bd9Sstevel@tonic-gate }
26217c478bd9Sstevel@tonic-gate 
26227c478bd9Sstevel@tonic-gate /*
26237c478bd9Sstevel@tonic-gate  * Handle the following optmgmt:
26247c478bd9Sstevel@tonic-gate  *	IP_DROP_MEMBERSHIP		will leave
2625bd670b35SErik Nordmark  *	IPV6_LEAVE_GROUP		will leave
26267c478bd9Sstevel@tonic-gate  *	MCAST_LEAVE_GROUP		will leave
26277c478bd9Sstevel@tonic-gate  *	IP_UNBLOCK_SOURCE		will not leave
26287c478bd9Sstevel@tonic-gate  *	MCAST_UNBLOCK_SOURCE		will not leave
26297c478bd9Sstevel@tonic-gate  *	IP_LEAVE_SOURCE_GROUP		may leave (if leaving last source)
26307c478bd9Sstevel@tonic-gate  *	MCAST_LEAVE_SOURCE_GROUP	may leave (if leaving last source)
26317c478bd9Sstevel@tonic-gate  *
26327c478bd9Sstevel@tonic-gate  * fmode and src parameters may be used to determine which option is
2633bd670b35SErik Nordmark  * being set, as follows:
26347c478bd9Sstevel@tonic-gate  *	opt			 fmode			v6src
2635bd670b35SErik Nordmark  *	IP_DROP_MEMBERSHIP	 MODE_IS_INCLUDE	unspecified
26367c478bd9Sstevel@tonic-gate  *	IPV6_LEAVE_GROUP	 MODE_IS_INCLUDE	unspecified
26377c478bd9Sstevel@tonic-gate  *	MCAST_LEAVE_GROUP	 MODE_IS_INCLUDE	unspecified
2638bd670b35SErik Nordmark  *	IP_UNBLOCK_SOURCE	 MODE_IS_EXCLUDE	IPv4-mapped addr
26397c478bd9Sstevel@tonic-gate  *	MCAST_UNBLOCK_SOURCE	 MODE_IS_EXCLUDE	v6 addr
2640bd670b35SErik Nordmark  *	IP_LEAVE_SOURCE_GROUP	 MODE_IS_INCLUDE	IPv4-mapped addr
26417c478bd9Sstevel@tonic-gate  *	MCAST_LEAVE_SOURCE_GROUP MODE_IS_INCLUDE	v6 addr
26427c478bd9Sstevel@tonic-gate  *
26437c478bd9Sstevel@tonic-gate  * Changing the filter mode is not allowed; if a matching ilg already
26447c478bd9Sstevel@tonic-gate  * exists and fmode != ilg->ilg_fmode, EINVAL is returned.
26457c478bd9Sstevel@tonic-gate  *
2646bd670b35SErik Nordmark  * The interface to be used may be identified by an IPv4 address or by an
2647bd670b35SErik Nordmark  * interface index.
2648bd670b35SErik Nordmark  *
26497c478bd9Sstevel@tonic-gate  * Handles IPv4-mapped IPv6 multicast addresses by associating them
2650bd670b35SErik Nordmark  * with the IPv4 address.  Assumes that if v6group is v4-mapped,
26517c478bd9Sstevel@tonic-gate  * v6src is also v4-mapped.
26527c478bd9Sstevel@tonic-gate  */
26537c478bd9Sstevel@tonic-gate int
2654bd670b35SErik Nordmark ip_opt_delete_group(conn_t *connp, boolean_t checkonly,
2655bd670b35SErik Nordmark     const in6_addr_t *v6group, ipaddr_t ifaddr, uint_t ifindex,
2656bd670b35SErik Nordmark     mcast_record_t fmode, const in6_addr_t *v6src)
26577c478bd9Sstevel@tonic-gate {
2658bd670b35SErik Nordmark 
2659bd670b35SErik Nordmark 	/*
2660bd670b35SErik Nordmark 	 * In the normal case below we don't check for the ill existing.
2661bd670b35SErik Nordmark 	 * Instead we look for an existing ilg in _excl.
2662bd670b35SErik Nordmark 	 * If checkonly we sanity check the arguments
2663bd670b35SErik Nordmark 	 */
2664bd670b35SErik Nordmark 	if (checkonly) {
26657c478bd9Sstevel@tonic-gate 		ill_t	*ill;
26667c478bd9Sstevel@tonic-gate 		int	err;
26677c478bd9Sstevel@tonic-gate 
2668bd670b35SErik Nordmark 		err = ip_opt_check(connp, v6group, v6src, ifaddr, ifindex,
2669bd670b35SErik Nordmark 		    &ill);
26707c478bd9Sstevel@tonic-gate 		/*
2671bd670b35SErik Nordmark 		 * do not do operation, just pretend to - new T_CHECK semantics.
2672bd670b35SErik Nordmark 		 * ip_opt_check is considered a good enough "check" here.
26737c478bd9Sstevel@tonic-gate 		 */
2674bd670b35SErik Nordmark 		if (ill != NULL)
26757c478bd9Sstevel@tonic-gate 			ill_refrele(ill);
26767c478bd9Sstevel@tonic-gate 		return (err);
26777c478bd9Sstevel@tonic-gate 	}
2678bd670b35SErik Nordmark 	return (ip_opt_delete_group_excl(connp, v6group, ifaddr, ifindex,
2679bd670b35SErik Nordmark 	    fmode, v6src));
2680bd670b35SErik Nordmark }
26817c478bd9Sstevel@tonic-gate 
26827c478bd9Sstevel@tonic-gate /*
26837c478bd9Sstevel@tonic-gate  * Group mgmt for upper conn that passes things down
26847c478bd9Sstevel@tonic-gate  * to the interface multicast list (and DLPI)
26857c478bd9Sstevel@tonic-gate  * These routines can handle new style options that specify an interface name
26867c478bd9Sstevel@tonic-gate  * as opposed to an interface address (needed for general handling of
26877c478bd9Sstevel@tonic-gate  * unnumbered interfaces.)
26887c478bd9Sstevel@tonic-gate  */
26897c478bd9Sstevel@tonic-gate 
26907c478bd9Sstevel@tonic-gate /*
26917c478bd9Sstevel@tonic-gate  * Add a group to an upper conn group data structure and pass things down
26927c478bd9Sstevel@tonic-gate  * to the interface multicast list (and DLPI)
2693bd670b35SErik Nordmark  * Common for IPv4 and IPv6; for IPv4 we can have an ifaddr.
26947c478bd9Sstevel@tonic-gate  */
26957c478bd9Sstevel@tonic-gate static int
2696bd670b35SErik Nordmark ilg_add(conn_t *connp, const in6_addr_t *v6group, ipaddr_t ifaddr,
2697bd670b35SErik Nordmark     uint_t ifindex, ill_t *ill, mcast_record_t fmode, const in6_addr_t *v6src)
26987c478bd9Sstevel@tonic-gate {
26997c478bd9Sstevel@tonic-gate 	int	error = 0;
27007c478bd9Sstevel@tonic-gate 	ilg_t	*ilg;
27017c478bd9Sstevel@tonic-gate 	ilg_stat_t ilgstat;
27027c478bd9Sstevel@tonic-gate 	slist_t	*new_filter = NULL;
27037c478bd9Sstevel@tonic-gate 	int	new_fmode;
2704bd670b35SErik Nordmark 	ilm_t *ilm;
27057c478bd9Sstevel@tonic-gate 
27067c478bd9Sstevel@tonic-gate 	if (!(ill->ill_flags & ILLF_MULTICAST))
27077c478bd9Sstevel@tonic-gate 		return (EADDRNOTAVAIL);
27087c478bd9Sstevel@tonic-gate 
2709bd670b35SErik Nordmark 	/* conn_ilg_lock protects the ilg list. */
2710bd670b35SErik Nordmark 	ASSERT(MUTEX_HELD(&ill->ill_mcast_serializer));
2711bd670b35SErik Nordmark 	rw_enter(&connp->conn_ilg_lock, RW_WRITER);
2712bd670b35SErik Nordmark 	ilg = ilg_lookup(connp, v6group, ifaddr, ifindex);
27137c478bd9Sstevel@tonic-gate 
27147c478bd9Sstevel@tonic-gate 	/*
27157c478bd9Sstevel@tonic-gate 	 * Depending on the option we're handling, may or may not be okay
27167c478bd9Sstevel@tonic-gate 	 * if group has already been added.  Figure out our rules based
27177c478bd9Sstevel@tonic-gate 	 * on fmode and src params.  Also make sure there's enough room
27187c478bd9Sstevel@tonic-gate 	 * in the filter if we're adding a source to an existing filter.
27197c478bd9Sstevel@tonic-gate 	 */
27207c478bd9Sstevel@tonic-gate 	if (IN6_IS_ADDR_UNSPECIFIED(v6src)) {
27217c478bd9Sstevel@tonic-gate 		/* we're joining for all sources, must not have joined */
27227c478bd9Sstevel@tonic-gate 		if (ilg != NULL)
27237c478bd9Sstevel@tonic-gate 			error = EADDRINUSE;
27247c478bd9Sstevel@tonic-gate 	} else {
27257c478bd9Sstevel@tonic-gate 		if (fmode == MODE_IS_EXCLUDE) {
27267c478bd9Sstevel@tonic-gate 			/* (excl {addr}) => block source, must have joined */
27277c478bd9Sstevel@tonic-gate 			if (ilg == NULL)
27287c478bd9Sstevel@tonic-gate 				error = EADDRNOTAVAIL;
27297c478bd9Sstevel@tonic-gate 		}
27307c478bd9Sstevel@tonic-gate 		/* (incl {addr}) => join source, may have joined */
27317c478bd9Sstevel@tonic-gate 
27327c478bd9Sstevel@tonic-gate 		if (ilg != NULL &&
27337c478bd9Sstevel@tonic-gate 		    SLIST_CNT(ilg->ilg_filter) == MAX_FILTER_SIZE)
27347c478bd9Sstevel@tonic-gate 			error = ENOBUFS;
27357c478bd9Sstevel@tonic-gate 	}
27367c478bd9Sstevel@tonic-gate 	if (error != 0) {
2737bd670b35SErik Nordmark 		rw_exit(&connp->conn_ilg_lock);
27387c478bd9Sstevel@tonic-gate 		return (error);
27397c478bd9Sstevel@tonic-gate 	}
27407c478bd9Sstevel@tonic-gate 
27417c478bd9Sstevel@tonic-gate 	/*
27427c478bd9Sstevel@tonic-gate 	 * Alloc buffer to copy new state into (see below) before
27437c478bd9Sstevel@tonic-gate 	 * we make any changes, so we can bail if it fails.
27447c478bd9Sstevel@tonic-gate 	 */
27457c478bd9Sstevel@tonic-gate 	if ((new_filter = l_alloc()) == NULL) {
2746bd670b35SErik Nordmark 		rw_exit(&connp->conn_ilg_lock);
27477c478bd9Sstevel@tonic-gate 		return (ENOMEM);
27487c478bd9Sstevel@tonic-gate 	}
27497c478bd9Sstevel@tonic-gate 
27507c478bd9Sstevel@tonic-gate 	if (ilg == NULL) {
2751e11c3f44Smeem 		if ((ilg = conn_ilg_alloc(connp, &error)) == NULL) {
2752bd670b35SErik Nordmark 			rw_exit(&connp->conn_ilg_lock);
27537c478bd9Sstevel@tonic-gate 			l_free(new_filter);
2754e11c3f44Smeem 			return (error);
27557c478bd9Sstevel@tonic-gate 		}
2756bd670b35SErik Nordmark 		ilg->ilg_ifindex = ifindex;
2757bd670b35SErik Nordmark 		ilg->ilg_ifaddr = ifaddr;
27587c478bd9Sstevel@tonic-gate 		if (!IN6_IS_ADDR_UNSPECIFIED(v6src)) {
27597c478bd9Sstevel@tonic-gate 			ilg->ilg_filter = l_alloc();
27607c478bd9Sstevel@tonic-gate 			if (ilg->ilg_filter == NULL) {
27617c478bd9Sstevel@tonic-gate 				ilg_delete(connp, ilg, NULL);
2762bd670b35SErik Nordmark 				rw_exit(&connp->conn_ilg_lock);
27637c478bd9Sstevel@tonic-gate 				l_free(new_filter);
27647c478bd9Sstevel@tonic-gate 				return (ENOMEM);
27657c478bd9Sstevel@tonic-gate 			}
27667c478bd9Sstevel@tonic-gate 			ilg->ilg_filter->sl_numsrc = 1;
27677c478bd9Sstevel@tonic-gate 			ilg->ilg_filter->sl_addr[0] = *v6src;
27687c478bd9Sstevel@tonic-gate 		}
27697c478bd9Sstevel@tonic-gate 		ilgstat = ILGSTAT_NEW;
27707c478bd9Sstevel@tonic-gate 		ilg->ilg_v6group = *v6group;
27717c478bd9Sstevel@tonic-gate 		ilg->ilg_fmode = fmode;
27727c478bd9Sstevel@tonic-gate 		ilg->ilg_ill = ill;
27737c478bd9Sstevel@tonic-gate 	} else {
27747c478bd9Sstevel@tonic-gate 		int index;
2775f1c454b4SSowmini Varadhan 
27767c478bd9Sstevel@tonic-gate 		if (ilg->ilg_fmode != fmode || IN6_IS_ADDR_UNSPECIFIED(v6src)) {
2777bd670b35SErik Nordmark 			rw_exit(&connp->conn_ilg_lock);
27787c478bd9Sstevel@tonic-gate 			l_free(new_filter);
27797c478bd9Sstevel@tonic-gate 			return (EINVAL);
27807c478bd9Sstevel@tonic-gate 		}
27817c478bd9Sstevel@tonic-gate 		if (ilg->ilg_filter == NULL) {
27827c478bd9Sstevel@tonic-gate 			ilg->ilg_filter = l_alloc();
27837c478bd9Sstevel@tonic-gate 			if (ilg->ilg_filter == NULL) {
2784bd670b35SErik Nordmark 				rw_exit(&connp->conn_ilg_lock);
27857c478bd9Sstevel@tonic-gate 				l_free(new_filter);
27867c478bd9Sstevel@tonic-gate 				return (ENOMEM);
27877c478bd9Sstevel@tonic-gate 			}
27887c478bd9Sstevel@tonic-gate 		}
27897c478bd9Sstevel@tonic-gate 		if (list_has_addr(ilg->ilg_filter, v6src)) {
2790bd670b35SErik Nordmark 			rw_exit(&connp->conn_ilg_lock);
27917c478bd9Sstevel@tonic-gate 			l_free(new_filter);
27927c478bd9Sstevel@tonic-gate 			return (EADDRNOTAVAIL);
27937c478bd9Sstevel@tonic-gate 		}
27947c478bd9Sstevel@tonic-gate 		ilgstat = ILGSTAT_CHANGE;
27957c478bd9Sstevel@tonic-gate 		index = ilg->ilg_filter->sl_numsrc++;
27967c478bd9Sstevel@tonic-gate 		ilg->ilg_filter->sl_addr[index] = *v6src;
27977c478bd9Sstevel@tonic-gate 	}
27987c478bd9Sstevel@tonic-gate 
27997c478bd9Sstevel@tonic-gate 	/*
28007c478bd9Sstevel@tonic-gate 	 * Save copy of ilg's filter state to pass to other functions,
2801bd670b35SErik Nordmark 	 * so we can release conn_ilg_lock now.
28027c478bd9Sstevel@tonic-gate 	 */
28037c478bd9Sstevel@tonic-gate 	new_fmode = ilg->ilg_fmode;
28047c478bd9Sstevel@tonic-gate 	l_copy(ilg->ilg_filter, new_filter);
28057c478bd9Sstevel@tonic-gate 
2806bd670b35SErik Nordmark 	rw_exit(&connp->conn_ilg_lock);
28077c478bd9Sstevel@tonic-gate 
28087c478bd9Sstevel@tonic-gate 	/*
28097c478bd9Sstevel@tonic-gate 	 * Now update the ill. We wait to do this until after the ilg
28107c478bd9Sstevel@tonic-gate 	 * has been updated because we need to update the src filter
28117c478bd9Sstevel@tonic-gate 	 * info for the ill, which involves looking at the status of
28127c478bd9Sstevel@tonic-gate 	 * all the ilgs associated with this group/interface pair.
28137c478bd9Sstevel@tonic-gate 	 */
2814bd670b35SErik Nordmark 	ilm = ip_addmulti_serial(v6group, ill, connp->conn_zoneid, ilgstat,
2815bd670b35SErik Nordmark 	    new_fmode, new_filter, &error);
2816bd670b35SErik Nordmark 
2817bd670b35SErik Nordmark 	rw_enter(&connp->conn_ilg_lock, RW_WRITER);
28187c478bd9Sstevel@tonic-gate 	/*
2819bd670b35SErik Nordmark 	 * Must look up the ilg again since we've not been holding
2820bd670b35SErik Nordmark 	 * conn_ilg_lock. The ilg could have disappeared due to an unplumb
2821bd670b35SErik Nordmark 	 * having called conn_update_ill, which can run once we dropped the
2822bd670b35SErik Nordmark 	 * conn_ilg_lock above.
28237c478bd9Sstevel@tonic-gate 	 */
2824bd670b35SErik Nordmark 	ilg = ilg_lookup(connp, v6group, ifaddr, ifindex);
2825bd670b35SErik Nordmark 	if (ilg == NULL) {
2826bd670b35SErik Nordmark 		rw_exit(&connp->conn_ilg_lock);
2827bd670b35SErik Nordmark 		if (ilm != NULL) {
2828bd670b35SErik Nordmark 			(void) ip_delmulti_serial(ilm, B_FALSE,
2829bd670b35SErik Nordmark 			    (ilgstat == ILGSTAT_NEW));
2830bd670b35SErik Nordmark 		}
2831bd670b35SErik Nordmark 		error = ENXIO;
2832bd670b35SErik Nordmark 		goto free_and_exit;
2833bd670b35SErik Nordmark 	}
2834bd670b35SErik Nordmark 	if (ilm != NULL) {
2835f1c454b4SSowmini Varadhan 		if (ilg->ilg_ill == NULL) {
2836f1c454b4SSowmini Varadhan 			/* some other thread is re-attaching this.  */
2837f1c454b4SSowmini Varadhan 			rw_exit(&connp->conn_ilg_lock);
2838f1c454b4SSowmini Varadhan 			(void) ip_delmulti_serial(ilm, B_FALSE,
2839f1c454b4SSowmini Varadhan 			    (ilgstat == ILGSTAT_NEW));
2840f1c454b4SSowmini Varadhan 			error = 0;
2841f1c454b4SSowmini Varadhan 			goto free_and_exit;
2842f1c454b4SSowmini Varadhan 		}
2843bd670b35SErik Nordmark 		/* Succeeded. Update the ilg to point at the ilm */
2844bd670b35SErik Nordmark 		if (ilgstat == ILGSTAT_NEW) {
2845f1c454b4SSowmini Varadhan 			if (ilg->ilg_ilm == NULL) {
2846bd670b35SErik Nordmark 				ilg->ilg_ilm = ilm;
2847bd670b35SErik Nordmark 				ilm->ilm_ifaddr = ifaddr; /* For netstat */
2848bd670b35SErik Nordmark 			} else {
2849f1c454b4SSowmini Varadhan 				/* some other thread is re-attaching this. */
2850f1c454b4SSowmini Varadhan 				rw_exit(&connp->conn_ilg_lock);
2851f1c454b4SSowmini Varadhan 				(void) ip_delmulti_serial(ilm, B_FALSE, B_TRUE);
2852f1c454b4SSowmini Varadhan 				error = 0;
2853f1c454b4SSowmini Varadhan 				goto free_and_exit;
2854f1c454b4SSowmini Varadhan 			}
2855f1c454b4SSowmini Varadhan 		} else {
2856bd670b35SErik Nordmark 			/*
2857bd670b35SErik Nordmark 			 * ip_addmulti didn't get a held ilm for
2858bd670b35SErik Nordmark 			 * ILGSTAT_CHANGE; ilm_refcnt was unchanged.
2859bd670b35SErik Nordmark 			 */
2860bd670b35SErik Nordmark 			ASSERT(ilg->ilg_ilm == ilm);
2861bd670b35SErik Nordmark 		}
2862bd670b35SErik Nordmark 	} else {
2863bd670b35SErik Nordmark 		ASSERT(error != 0);
2864bd670b35SErik Nordmark 		/*
2865bd670b35SErik Nordmark 		 * Failed to allocate the ilm.
2866bd670b35SErik Nordmark 		 * Need to undo what we did before calling ip_addmulti()
2867bd670b35SErik Nordmark 		 * If ENETDOWN just clear ill_ilg since so that we
2868bd670b35SErik Nordmark 		 * will rejoin when the ill comes back; don't report ENETDOWN
2869bd670b35SErik Nordmark 		 * to application.
2870bd670b35SErik Nordmark 		 */
2871bd670b35SErik Nordmark 		if (ilgstat == ILGSTAT_NEW && error == ENETDOWN) {
2872bd670b35SErik Nordmark 			ilg->ilg_ill = NULL;
2873bd670b35SErik Nordmark 			error = 0;
2874bd670b35SErik Nordmark 		} else {
28757c478bd9Sstevel@tonic-gate 			in6_addr_t delsrc =
28767c478bd9Sstevel@tonic-gate 			    (ilgstat == ILGSTAT_NEW) ? ipv6_all_zeros : *v6src;
2877bd670b35SErik Nordmark 
28787c478bd9Sstevel@tonic-gate 			ilg_delete(connp, ilg, &delsrc);
2879bd670b35SErik Nordmark 		}
2880bd670b35SErik Nordmark 	}
2881bd670b35SErik Nordmark 	rw_exit(&connp->conn_ilg_lock);
2882bd670b35SErik Nordmark 
2883bd670b35SErik Nordmark free_and_exit:
28847c478bd9Sstevel@tonic-gate 	l_free(new_filter);
28857c478bd9Sstevel@tonic-gate 	return (error);
28867c478bd9Sstevel@tonic-gate }
28877c478bd9Sstevel@tonic-gate 
28887c478bd9Sstevel@tonic-gate /*
2889bd670b35SErik Nordmark  * Find an IPv4 ilg matching group, ill and source.
2890bd670b35SErik Nordmark  * The group and source can't be INADDR_ANY here so no need to translate to
2891bd670b35SErik Nordmark  * the unspecified IPv6 address.
28927c478bd9Sstevel@tonic-gate  */
2893bd670b35SErik Nordmark boolean_t
2894bd670b35SErik Nordmark conn_hasmembers_ill_withsrc_v4(conn_t *connp, ipaddr_t group, ipaddr_t src,
2895bd670b35SErik Nordmark     ill_t *ill)
28967c478bd9Sstevel@tonic-gate {
28977c478bd9Sstevel@tonic-gate 	in6_addr_t v6group, v6src;
28987c478bd9Sstevel@tonic-gate 	int i;
28997c478bd9Sstevel@tonic-gate 	boolean_t isinlist;
29007c478bd9Sstevel@tonic-gate 	ilg_t *ilg;
29017c478bd9Sstevel@tonic-gate 
2902bd670b35SErik Nordmark 	rw_enter(&connp->conn_ilg_lock, RW_READER);
29037c478bd9Sstevel@tonic-gate 	IN6_IPADDR_TO_V4MAPPED(group, &v6group);
2904bd670b35SErik Nordmark 	for (ilg = connp->conn_ilg; ilg != NULL; ilg = ilg->ilg_next) {
2905bd670b35SErik Nordmark 		if (ilg->ilg_condemned)
29067c478bd9Sstevel@tonic-gate 			continue;
2907bd670b35SErik Nordmark 
2908bd670b35SErik Nordmark 		/* ilg_ill could be NULL if an add is in progress */
2909bd670b35SErik Nordmark 		if (ilg->ilg_ill != ill)
2910bd670b35SErik Nordmark 			continue;
2911bd670b35SErik Nordmark 
2912bd670b35SErik Nordmark 		/* The callers use upper ill for IPMP */
2913bd670b35SErik Nordmark 		ASSERT(!IS_UNDER_IPMP(ill));
2914bd670b35SErik Nordmark 		if (IN6_ARE_ADDR_EQUAL(&ilg->ilg_v6group, &v6group)) {
29157c478bd9Sstevel@tonic-gate 			if (SLIST_IS_EMPTY(ilg->ilg_filter)) {
29167c478bd9Sstevel@tonic-gate 				/* no source filter, so this is a match */
2917bd670b35SErik Nordmark 				rw_exit(&connp->conn_ilg_lock);
2918bd670b35SErik Nordmark 				return (B_TRUE);
29197c478bd9Sstevel@tonic-gate 			}
29207c478bd9Sstevel@tonic-gate 			break;
29217c478bd9Sstevel@tonic-gate 		}
29227c478bd9Sstevel@tonic-gate 	}
2923bd670b35SErik Nordmark 	if (ilg == NULL) {
2924bd670b35SErik Nordmark 		rw_exit(&connp->conn_ilg_lock);
2925bd670b35SErik Nordmark 		return (B_FALSE);
2926bd670b35SErik Nordmark 	}
29277c478bd9Sstevel@tonic-gate 
29287c478bd9Sstevel@tonic-gate 	/*
29297c478bd9Sstevel@tonic-gate 	 * we have an ilg with matching ill and group; but
29307c478bd9Sstevel@tonic-gate 	 * the ilg has a source list that we must check.
29317c478bd9Sstevel@tonic-gate 	 */
29327c478bd9Sstevel@tonic-gate 	IN6_IPADDR_TO_V4MAPPED(src, &v6src);
29337c478bd9Sstevel@tonic-gate 	isinlist = B_FALSE;
29347c478bd9Sstevel@tonic-gate 	for (i = 0; i < ilg->ilg_filter->sl_numsrc; i++) {
29357c478bd9Sstevel@tonic-gate 		if (IN6_ARE_ADDR_EQUAL(&v6src, &ilg->ilg_filter->sl_addr[i])) {
29367c478bd9Sstevel@tonic-gate 			isinlist = B_TRUE;
29377c478bd9Sstevel@tonic-gate 			break;
29387c478bd9Sstevel@tonic-gate 		}
29397c478bd9Sstevel@tonic-gate 	}
29407c478bd9Sstevel@tonic-gate 
29417c478bd9Sstevel@tonic-gate 	if ((isinlist && ilg->ilg_fmode == MODE_IS_INCLUDE) ||
2942bd670b35SErik Nordmark 	    (!isinlist && ilg->ilg_fmode == MODE_IS_EXCLUDE)) {
2943bd670b35SErik Nordmark 		rw_exit(&connp->conn_ilg_lock);
2944bd670b35SErik Nordmark 		return (B_TRUE);
2945bd670b35SErik Nordmark 	}
2946bd670b35SErik Nordmark 	rw_exit(&connp->conn_ilg_lock);
2947bd670b35SErik Nordmark 	return (B_FALSE);
29487c478bd9Sstevel@tonic-gate }
29497c478bd9Sstevel@tonic-gate 
29507c478bd9Sstevel@tonic-gate /*
29517c478bd9Sstevel@tonic-gate  * Find an IPv6 ilg matching group, ill, and source
29527c478bd9Sstevel@tonic-gate  */
2953bd670b35SErik Nordmark boolean_t
2954bd670b35SErik Nordmark conn_hasmembers_ill_withsrc_v6(conn_t *connp, const in6_addr_t *v6group,
29557c478bd9Sstevel@tonic-gate     const in6_addr_t *v6src, ill_t *ill)
29567c478bd9Sstevel@tonic-gate {
29577c478bd9Sstevel@tonic-gate 	int i;
29587c478bd9Sstevel@tonic-gate 	boolean_t isinlist;
29597c478bd9Sstevel@tonic-gate 	ilg_t *ilg;
29607c478bd9Sstevel@tonic-gate 
2961bd670b35SErik Nordmark 	rw_enter(&connp->conn_ilg_lock, RW_READER);
2962bd670b35SErik Nordmark 	for (ilg = connp->conn_ilg; ilg != NULL; ilg = ilg->ilg_next) {
2963bd670b35SErik Nordmark 		if (ilg->ilg_condemned)
29647c478bd9Sstevel@tonic-gate 			continue;
2965bd670b35SErik Nordmark 
2966bd670b35SErik Nordmark 		/* ilg_ill could be NULL if an add is in progress */
2967bd670b35SErik Nordmark 		if (ilg->ilg_ill != ill)
2968bd670b35SErik Nordmark 			continue;
2969bd670b35SErik Nordmark 
2970bd670b35SErik Nordmark 		/* The callers use upper ill for IPMP */
2971bd670b35SErik Nordmark 		ASSERT(!IS_UNDER_IPMP(ill));
2972bd670b35SErik Nordmark 		if (IN6_ARE_ADDR_EQUAL(&ilg->ilg_v6group, v6group)) {
29737c478bd9Sstevel@tonic-gate 			if (SLIST_IS_EMPTY(ilg->ilg_filter)) {
29747c478bd9Sstevel@tonic-gate 				/* no source filter, so this is a match */
2975bd670b35SErik Nordmark 				rw_exit(&connp->conn_ilg_lock);
2976bd670b35SErik Nordmark 				return (B_TRUE);
29777c478bd9Sstevel@tonic-gate 			}
29787c478bd9Sstevel@tonic-gate 			break;
29797c478bd9Sstevel@tonic-gate 		}
29807c478bd9Sstevel@tonic-gate 	}
2981bd670b35SErik Nordmark 	if (ilg == NULL) {
2982bd670b35SErik Nordmark 		rw_exit(&connp->conn_ilg_lock);
2983bd670b35SErik Nordmark 		return (B_FALSE);
2984bd670b35SErik Nordmark 	}
29857c478bd9Sstevel@tonic-gate 
29867c478bd9Sstevel@tonic-gate 	/*
29877c478bd9Sstevel@tonic-gate 	 * we have an ilg with matching ill and group; but
29887c478bd9Sstevel@tonic-gate 	 * the ilg has a source list that we must check.
29897c478bd9Sstevel@tonic-gate 	 */
29907c478bd9Sstevel@tonic-gate 	isinlist = B_FALSE;
29917c478bd9Sstevel@tonic-gate 	for (i = 0; i < ilg->ilg_filter->sl_numsrc; i++) {
29927c478bd9Sstevel@tonic-gate 		if (IN6_ARE_ADDR_EQUAL(v6src, &ilg->ilg_filter->sl_addr[i])) {
29937c478bd9Sstevel@tonic-gate 			isinlist = B_TRUE;
29947c478bd9Sstevel@tonic-gate 			break;
29957c478bd9Sstevel@tonic-gate 		}
29967c478bd9Sstevel@tonic-gate 	}
29977c478bd9Sstevel@tonic-gate 
29987c478bd9Sstevel@tonic-gate 	if ((isinlist && ilg->ilg_fmode == MODE_IS_INCLUDE) ||
2999bd670b35SErik Nordmark 	    (!isinlist && ilg->ilg_fmode == MODE_IS_EXCLUDE)) {
3000bd670b35SErik Nordmark 		rw_exit(&connp->conn_ilg_lock);
3001bd670b35SErik Nordmark 		return (B_TRUE);
3002bd670b35SErik Nordmark 	}
3003bd670b35SErik Nordmark 	rw_exit(&connp->conn_ilg_lock);
3004bd670b35SErik Nordmark 	return (B_FALSE);
30057c478bd9Sstevel@tonic-gate }
30067c478bd9Sstevel@tonic-gate 
30077c478bd9Sstevel@tonic-gate /*
3008bd670b35SErik Nordmark  * Find an ilg matching group and ifaddr/ifindex.
3009bd670b35SErik Nordmark  * We check both ifaddr and ifindex even though at most one of them
3010bd670b35SErik Nordmark  * will be non-zero; that way we always find the right one.
30117c478bd9Sstevel@tonic-gate  */
30127c478bd9Sstevel@tonic-gate static ilg_t *
3013bd670b35SErik Nordmark ilg_lookup(conn_t *connp, const in6_addr_t *v6group, ipaddr_t ifaddr,
3014bd670b35SErik Nordmark     uint_t ifindex)
30157c478bd9Sstevel@tonic-gate {
3016384ad179Ssowmini 	ilg_t	*ilg;
30177c478bd9Sstevel@tonic-gate 
3018bd670b35SErik Nordmark 	ASSERT(RW_LOCK_HELD(&connp->conn_ilg_lock));
30197c478bd9Sstevel@tonic-gate 
3020bd670b35SErik Nordmark 	for (ilg = connp->conn_ilg; ilg != NULL; ilg = ilg->ilg_next) {
3021bd670b35SErik Nordmark 		if (ilg->ilg_condemned)
3022bd670b35SErik Nordmark 			continue;
30237c478bd9Sstevel@tonic-gate 
3024bd670b35SErik Nordmark 		if (ilg->ilg_ifaddr == ifaddr &&
3025bd670b35SErik Nordmark 		    ilg->ilg_ifindex == ifindex &&
3026bd670b35SErik Nordmark 		    IN6_ARE_ADDR_EQUAL(&ilg->ilg_v6group, v6group))
3027384ad179Ssowmini 			return (ilg);
30287c478bd9Sstevel@tonic-gate 	}
30297c478bd9Sstevel@tonic-gate 	return (NULL);
30307c478bd9Sstevel@tonic-gate }
30317c478bd9Sstevel@tonic-gate 
30327c478bd9Sstevel@tonic-gate /*
30337c478bd9Sstevel@tonic-gate  * If a source address is passed in (src != NULL and src is not
30347c478bd9Sstevel@tonic-gate  * unspecified), remove the specified src addr from the given ilg's
30357c478bd9Sstevel@tonic-gate  * filter list, else delete the ilg.
30367c478bd9Sstevel@tonic-gate  */
30377c478bd9Sstevel@tonic-gate static void
30387c478bd9Sstevel@tonic-gate ilg_delete(conn_t *connp, ilg_t *ilg, const in6_addr_t *src)
30397c478bd9Sstevel@tonic-gate {
3040bd670b35SErik Nordmark 	ASSERT(RW_WRITE_HELD(&connp->conn_ilg_lock));
3041bd670b35SErik Nordmark 	ASSERT(ilg->ilg_ptpn != NULL);
3042bd670b35SErik Nordmark 	ASSERT(!ilg->ilg_condemned);
30437c478bd9Sstevel@tonic-gate 
30447c478bd9Sstevel@tonic-gate 	if (src == NULL || IN6_IS_ADDR_UNSPECIFIED(src)) {
30457c478bd9Sstevel@tonic-gate 		FREE_SLIST(ilg->ilg_filter);
3046bd670b35SErik Nordmark 		ilg->ilg_filter = NULL;
30477c478bd9Sstevel@tonic-gate 
3048bd670b35SErik Nordmark 		ASSERT(ilg->ilg_ilm == NULL);
3049bd670b35SErik Nordmark 		ilg->ilg_ill = NULL;
3050bd670b35SErik Nordmark 		ilg->ilg_condemned = B_TRUE;
30517c478bd9Sstevel@tonic-gate 
3052bd670b35SErik Nordmark 		/* ilg_inactive will unlink from the list */
3053bd670b35SErik Nordmark 		ilg_refrele(ilg);
30547c478bd9Sstevel@tonic-gate 	} else {
30557c478bd9Sstevel@tonic-gate 		l_remove(ilg->ilg_filter, src);
30567c478bd9Sstevel@tonic-gate 	}
30577c478bd9Sstevel@tonic-gate }
30587c478bd9Sstevel@tonic-gate 
30597c478bd9Sstevel@tonic-gate /*
3060bd670b35SErik Nordmark  * Called from conn close. No new ilg can be added or removed
30617c478bd9Sstevel@tonic-gate  * because CONN_CLOSING has been set by ip_close. ilg_add / ilg_delete
30627c478bd9Sstevel@tonic-gate  * will return error if conn has started closing.
3063bd670b35SErik Nordmark  *
3064bd670b35SErik Nordmark  * We handle locking as follows.
3065bd670b35SErik Nordmark  * Under conn_ilg_lock we get the first ilg. As we drop the conn_ilg_lock to
3066bd670b35SErik Nordmark  * proceed with the ilm part of the delete we hold a reference on both the ill
3067bd670b35SErik Nordmark  * and the ilg. This doesn't prevent changes to the ilg, but prevents it from
3068bd670b35SErik Nordmark  * being deleted.
3069bd670b35SErik Nordmark  *
3070bd670b35SErik Nordmark  * Since the ilg_add code path uses two locks (conn_ilg_lock for the ilg part,
3071bd670b35SErik Nordmark  * and ill_mcast_lock for the ip_addmulti part) we can run at a point between
3072bd670b35SErik Nordmark  * the two. At that point ilg_ill is set, but ilg_ilm hasn't yet been set. In
3073bd670b35SErik Nordmark  * that case we delete the ilg here, which makes ilg_add discover that the ilg
3074bd670b35SErik Nordmark  * has disappeared when ip_addmulti returns, so it will discard the ilm it just
3075bd670b35SErik Nordmark  * added.
30767c478bd9Sstevel@tonic-gate  */
30777c478bd9Sstevel@tonic-gate void
30787c478bd9Sstevel@tonic-gate ilg_delete_all(conn_t *connp)
30797c478bd9Sstevel@tonic-gate {
3080bd670b35SErik Nordmark 	ilg_t	*ilg, *next_ilg, *held_ilg;
3081bd670b35SErik Nordmark 	ilm_t	*ilm;
3082bd670b35SErik Nordmark 	ill_t	*ill;
3083bd670b35SErik Nordmark 	boolean_t need_refrele;
30847c478bd9Sstevel@tonic-gate 
3085bd670b35SErik Nordmark 	/*
3086bd670b35SErik Nordmark 	 * Can not run if there is a conn_update_ill already running.
3087bd670b35SErik Nordmark 	 * Wait for it to complete. Caller should have already set CONN_CLOSING
3088bd670b35SErik Nordmark 	 * which prevents any new threads to run in conn_update_ill.
3089bd670b35SErik Nordmark 	 */
30907c478bd9Sstevel@tonic-gate 	mutex_enter(&connp->conn_lock);
3091bd670b35SErik Nordmark 	ASSERT(connp->conn_state_flags & CONN_CLOSING);
3092bd670b35SErik Nordmark 	while (connp->conn_state_flags & CONN_UPDATE_ILL)
3093bd670b35SErik Nordmark 		cv_wait(&connp->conn_cv, &connp->conn_lock);
30947c478bd9Sstevel@tonic-gate 	mutex_exit(&connp->conn_lock);
30957c478bd9Sstevel@tonic-gate 
3096bd670b35SErik Nordmark 	rw_enter(&connp->conn_ilg_lock, RW_WRITER);
3097bd670b35SErik Nordmark 	ilg = connp->conn_ilg;
3098bd670b35SErik Nordmark 	held_ilg = NULL;
3099bd670b35SErik Nordmark 	while (ilg != NULL) {
3100bd670b35SErik Nordmark 		if (ilg->ilg_condemned) {
3101bd670b35SErik Nordmark 			ilg = ilg->ilg_next;
31027c478bd9Sstevel@tonic-gate 			continue;
31037c478bd9Sstevel@tonic-gate 		}
3104bd670b35SErik Nordmark 		/* If the ilg is detached then no need to serialize */
3105bd670b35SErik Nordmark 		if (ilg->ilg_ilm == NULL) {
3106bd670b35SErik Nordmark 			next_ilg = ilg->ilg_next;
31077c478bd9Sstevel@tonic-gate 			ilg_delete(connp, ilg, NULL);
3108bd670b35SErik Nordmark 			ilg = next_ilg;
3109bd670b35SErik Nordmark 			continue;
3110bd670b35SErik Nordmark 		}
3111bd670b35SErik Nordmark 		ill = ilg->ilg_ilm->ilm_ill;
31127c478bd9Sstevel@tonic-gate 
3113bd670b35SErik Nordmark 		/*
3114bd670b35SErik Nordmark 		 * In order to serialize on the ill we try to enter
3115bd670b35SErik Nordmark 		 * and if that fails we unlock and relock and then
3116bd670b35SErik Nordmark 		 * check that we still have an ilm.
3117bd670b35SErik Nordmark 		 */
3118bd670b35SErik Nordmark 		need_refrele = B_FALSE;
3119bd670b35SErik Nordmark 		if (!mutex_tryenter(&ill->ill_mcast_serializer)) {
3120bd670b35SErik Nordmark 			ill_refhold(ill);
3121bd670b35SErik Nordmark 			need_refrele = B_TRUE;
3122bd670b35SErik Nordmark 			ilg_refhold(ilg);
3123bd670b35SErik Nordmark 			if (held_ilg != NULL)
3124bd670b35SErik Nordmark 				ilg_refrele(held_ilg);
3125bd670b35SErik Nordmark 			held_ilg = ilg;
3126bd670b35SErik Nordmark 			rw_exit(&connp->conn_ilg_lock);
3127bd670b35SErik Nordmark 			mutex_enter(&ill->ill_mcast_serializer);
3128bd670b35SErik Nordmark 			rw_enter(&connp->conn_ilg_lock, RW_WRITER);
3129bd670b35SErik Nordmark 			if (ilg->ilg_condemned) {
3130bd670b35SErik Nordmark 				ilg = ilg->ilg_next;
3131bd670b35SErik Nordmark 				goto next;
3132e11c3f44Smeem 			}
31337c478bd9Sstevel@tonic-gate 		}
3134bd670b35SErik Nordmark 		ilm = ilg->ilg_ilm;
3135bd670b35SErik Nordmark 		ilg->ilg_ilm = NULL;
3136bd670b35SErik Nordmark 		next_ilg = ilg->ilg_next;
3137bd670b35SErik Nordmark 		ilg_delete(connp, ilg, NULL);
3138bd670b35SErik Nordmark 		ilg = next_ilg;
3139bd670b35SErik Nordmark 		rw_exit(&connp->conn_ilg_lock);
31407c478bd9Sstevel@tonic-gate 
3141bd670b35SErik Nordmark 		if (ilm != NULL)
3142bd670b35SErik Nordmark 			(void) ip_delmulti_serial(ilm, B_FALSE, B_TRUE);
3143bd670b35SErik Nordmark 
3144bd670b35SErik Nordmark 	next:
3145bd670b35SErik Nordmark 		mutex_exit(&ill->ill_mcast_serializer);
3146f1c454b4SSowmini Varadhan 		/*
3147f1c454b4SSowmini Varadhan 		 * Now that all locks have been dropped, we can send any
3148f1c454b4SSowmini Varadhan 		 * deferred/queued DLPI or IP packets
3149f1c454b4SSowmini Varadhan 		 */
3150f1c454b4SSowmini Varadhan 		ill_mcast_send_queued(ill);
3151f1c454b4SSowmini Varadhan 		ill_dlpi_send_queued(ill);
3152bd670b35SErik Nordmark 		if (need_refrele) {
3153bd670b35SErik Nordmark 			/* Drop ill reference while we hold no locks */
3154bd670b35SErik Nordmark 			ill_refrele(ill);
31557c478bd9Sstevel@tonic-gate 		}
3156bd670b35SErik Nordmark 		rw_enter(&connp->conn_ilg_lock, RW_WRITER);
3157bd670b35SErik Nordmark 	}
3158bd670b35SErik Nordmark 	if (held_ilg != NULL)
3159bd670b35SErik Nordmark 		ilg_refrele(held_ilg);
3160bd670b35SErik Nordmark 	rw_exit(&connp->conn_ilg_lock);
31617c478bd9Sstevel@tonic-gate }
31627c478bd9Sstevel@tonic-gate 
31637c478bd9Sstevel@tonic-gate /*
3164bd670b35SErik Nordmark  * Attach the ilg to an ilm on the ill. If it fails we leave ilg_ill as NULL so
3165f1c454b4SSowmini Varadhan  * that a subsequent attempt can attach it. Drops and reacquires conn_ilg_lock.
31667c478bd9Sstevel@tonic-gate  */
31677c478bd9Sstevel@tonic-gate static void
3168bd670b35SErik Nordmark ilg_attach(conn_t *connp, ilg_t *ilg, ill_t *ill)
31697c478bd9Sstevel@tonic-gate {
3170bd670b35SErik Nordmark 	ilg_stat_t	ilgstat;
3171bd670b35SErik Nordmark 	slist_t		*new_filter;
3172bd670b35SErik Nordmark 	int		new_fmode;
31737c478bd9Sstevel@tonic-gate 	in6_addr_t	v6group;
3174bd670b35SErik Nordmark 	ipaddr_t	ifaddr;
3175bd670b35SErik Nordmark 	uint_t		ifindex;
3176bd670b35SErik Nordmark 	ilm_t		*ilm;
3177bd670b35SErik Nordmark 	int		error = 0;
3178bd670b35SErik Nordmark 
3179bd670b35SErik Nordmark 	ASSERT(RW_WRITE_HELD(&connp->conn_ilg_lock));
3180bd670b35SErik Nordmark 	/*
3181bd670b35SErik Nordmark 	 * Alloc buffer to copy new state into (see below) before
3182bd670b35SErik Nordmark 	 * we make any changes, so we can bail if it fails.
3183bd670b35SErik Nordmark 	 */
3184bd670b35SErik Nordmark 	if ((new_filter = l_alloc()) == NULL)
3185bd670b35SErik Nordmark 		return;
31867c478bd9Sstevel@tonic-gate 
31877c478bd9Sstevel@tonic-gate 	/*
3188bd670b35SErik Nordmark 	 * Save copy of ilg's filter state to pass to other functions, so
3189bd670b35SErik Nordmark 	 * we can release conn_ilg_lock now.
3190bd670b35SErik Nordmark 	 * Set ilg_ill so that an unplumb can find us.
31917c478bd9Sstevel@tonic-gate 	 */
3192bd670b35SErik Nordmark 	new_fmode = ilg->ilg_fmode;
3193bd670b35SErik Nordmark 	l_copy(ilg->ilg_filter, new_filter);
31947c478bd9Sstevel@tonic-gate 	v6group = ilg->ilg_v6group;
3195bd670b35SErik Nordmark 	ifaddr = ilg->ilg_ifaddr;
3196bd670b35SErik Nordmark 	ifindex = ilg->ilg_ifindex;
3197bd670b35SErik Nordmark 	ilgstat = ILGSTAT_NEW;
31987c478bd9Sstevel@tonic-gate 
3199bd670b35SErik Nordmark 	ilg->ilg_ill = ill;
3200bd670b35SErik Nordmark 	ASSERT(ilg->ilg_ilm == NULL);
3201bd670b35SErik Nordmark 	rw_exit(&connp->conn_ilg_lock);
3202bd670b35SErik Nordmark 
3203bd670b35SErik Nordmark 	ilm = ip_addmulti_serial(&v6group, ill, connp->conn_zoneid, ilgstat,
3204bd670b35SErik Nordmark 	    new_fmode, new_filter, &error);
3205bd670b35SErik Nordmark 	l_free(new_filter);
3206bd670b35SErik Nordmark 
3207bd670b35SErik Nordmark 	rw_enter(&connp->conn_ilg_lock, RW_WRITER);
32087c478bd9Sstevel@tonic-gate 	/*
3209bd670b35SErik Nordmark 	 * Must look up the ilg again since we've not been holding
3210bd670b35SErik Nordmark 	 * conn_ilg_lock. The ilg could have disappeared due to an unplumb
3211bd670b35SErik Nordmark 	 * having called conn_update_ill, which can run once we dropped the
3212f1c454b4SSowmini Varadhan 	 * conn_ilg_lock above. Alternatively, the ilg could have been attached
3213f1c454b4SSowmini Varadhan 	 * when the lock was dropped
32147c478bd9Sstevel@tonic-gate 	 */
3215bd670b35SErik Nordmark 	ilg = ilg_lookup(connp, &v6group, ifaddr, ifindex);
3216f1c454b4SSowmini Varadhan 	if (ilg == NULL || ilg->ilg_ilm != NULL) {
3217bd670b35SErik Nordmark 		if (ilm != NULL) {
3218bd670b35SErik Nordmark 			rw_exit(&connp->conn_ilg_lock);
3219bd670b35SErik Nordmark 			(void) ip_delmulti_serial(ilm, B_FALSE,
3220bd670b35SErik Nordmark 			    (ilgstat == ILGSTAT_NEW));
3221bd670b35SErik Nordmark 			rw_enter(&connp->conn_ilg_lock, RW_WRITER);
32227c478bd9Sstevel@tonic-gate 		}
3223bd670b35SErik Nordmark 		return;
32247c478bd9Sstevel@tonic-gate 	}
3225bd670b35SErik Nordmark 	if (ilm == NULL) {
3226bd670b35SErik Nordmark 		ilg->ilg_ill = NULL;
3227bd670b35SErik Nordmark 		return;
3228bd670b35SErik Nordmark 	}
3229bd670b35SErik Nordmark 	ilg->ilg_ilm = ilm;
3230bd670b35SErik Nordmark 	ilm->ilm_ifaddr = ifaddr;	/* For netstat */
32317c478bd9Sstevel@tonic-gate }
32327c478bd9Sstevel@tonic-gate 
32337c478bd9Sstevel@tonic-gate /*
32347c478bd9Sstevel@tonic-gate  * Called when an ill is unplumbed to make sure that there are no
3235bd670b35SErik Nordmark  * dangling conn references to that ill. In that case ill is non-NULL and
3236bd670b35SErik Nordmark  * we make sure we remove all references to it.
3237bd670b35SErik Nordmark  * Also called when we should revisit the ilg_ill used for multicast
3238bd670b35SErik Nordmark  * memberships, in which case ill is NULL.
3239bd670b35SErik Nordmark  *
3240bd670b35SErik Nordmark  * conn is held by caller.
3241bd670b35SErik Nordmark  *
3242bd670b35SErik Nordmark  * Note that ipcl_walk only walks conns that are not yet condemned.
3243bd670b35SErik Nordmark  * condemned conns can't be refheld. For this reason, conn must become clean
3244bd670b35SErik Nordmark  * first, i.e. it must not refer to any ill/ire and then only set
3245bd670b35SErik Nordmark  * condemned flag.
3246bd670b35SErik Nordmark  *
3247bd670b35SErik Nordmark  * We leave ixa_multicast_ifindex in place. We prefer dropping
3248bd670b35SErik Nordmark  * packets instead of sending them out the wrong interface.
3249bd670b35SErik Nordmark  *
3250bd670b35SErik Nordmark  * We keep the ilg around in a detached state (with ilg_ill and ilg_ilm being
3251bd670b35SErik Nordmark  * NULL) so that the application can leave it later. Also, if ilg_ifaddr and
3252bd670b35SErik Nordmark  * ilg_ifindex are zero, indicating that the system should pick the interface,
3253bd670b35SErik Nordmark  * then we attempt to reselect the ill and join on it.
3254bd670b35SErik Nordmark  *
3255bd670b35SErik Nordmark  * Locking notes:
3256bd670b35SErik Nordmark  * Under conn_ilg_lock we get the first ilg. As we drop the conn_ilg_lock to
3257bd670b35SErik Nordmark  * proceed with the ilm part of the delete we hold a reference on both the ill
3258bd670b35SErik Nordmark  * and the ilg. This doesn't prevent changes to the ilg, but prevents it from
3259bd670b35SErik Nordmark  * being deleted.
3260bd670b35SErik Nordmark  *
3261bd670b35SErik Nordmark  * Note: if this function is called when new ill/ipif's arrive or change status
3262bd670b35SErik Nordmark  * (SIOCSLIFINDEX, SIOCSLIFADDR) then we will attempt to attach any ilgs with
3263bd670b35SErik Nordmark  * a NULL ilg_ill to an ill/ilm.
3264bd670b35SErik Nordmark  */
3265bd670b35SErik Nordmark static void
3266bd670b35SErik Nordmark conn_update_ill(conn_t *connp, caddr_t arg)
3267bd670b35SErik Nordmark {
3268bd670b35SErik Nordmark 	ill_t	*ill = (ill_t *)arg;
3269bd670b35SErik Nordmark 
3270bd670b35SErik Nordmark 	/*
3271bd670b35SErik Nordmark 	 * We have to prevent ip_close/ilg_delete_all from running at
3272bd670b35SErik Nordmark 	 * the same time. ip_close sets CONN_CLOSING before doing the ilg_delete
3273bd670b35SErik Nordmark 	 * all, and we set CONN_UPDATE_ILL. That ensures that only one of
3274bd670b35SErik Nordmark 	 * ilg_delete_all and conn_update_ill run at a time for a given conn.
3275bd670b35SErik Nordmark 	 * If ilg_delete_all got here first, then we have nothing to do.
3276bd670b35SErik Nordmark 	 */
3277bd670b35SErik Nordmark 	mutex_enter(&connp->conn_lock);
3278bd670b35SErik Nordmark 	if (connp->conn_state_flags & (CONN_CLOSING|CONN_UPDATE_ILL)) {
3279bd670b35SErik Nordmark 		/* Caller has to wait for ill_ilm_cnt to drop to zero */
3280bd670b35SErik Nordmark 		mutex_exit(&connp->conn_lock);
3281bd670b35SErik Nordmark 		return;
3282bd670b35SErik Nordmark 	}
3283bd670b35SErik Nordmark 	connp->conn_state_flags |= CONN_UPDATE_ILL;
3284bd670b35SErik Nordmark 	mutex_exit(&connp->conn_lock);
3285bd670b35SErik Nordmark 
3286bd670b35SErik Nordmark 	if (ill != NULL)
3287bd670b35SErik Nordmark 		ilg_check_detach(connp, ill);
3288bd670b35SErik Nordmark 
3289f1c454b4SSowmini Varadhan 	ilg_check_reattach(connp, ill);
3290bd670b35SErik Nordmark 
3291bd670b35SErik Nordmark 	/* Do we need to wake up a thread in ilg_delete_all? */
3292bd670b35SErik Nordmark 	mutex_enter(&connp->conn_lock);
3293bd670b35SErik Nordmark 	connp->conn_state_flags &= ~CONN_UPDATE_ILL;
3294bd670b35SErik Nordmark 	if (connp->conn_state_flags & CONN_CLOSING)
3295bd670b35SErik Nordmark 		cv_broadcast(&connp->conn_cv);
3296bd670b35SErik Nordmark 	mutex_exit(&connp->conn_lock);
3297bd670b35SErik Nordmark }
3298bd670b35SErik Nordmark 
3299bd670b35SErik Nordmark /* Detach from an ill that is going away */
3300bd670b35SErik Nordmark static void
3301bd670b35SErik Nordmark ilg_check_detach(conn_t *connp, ill_t *ill)
3302bd670b35SErik Nordmark {
3303bd670b35SErik Nordmark 	char	group_buf[INET6_ADDRSTRLEN];
3304bd670b35SErik Nordmark 	ilg_t	*ilg, *held_ilg;
3305bd670b35SErik Nordmark 	ilm_t	*ilm;
3306bd670b35SErik Nordmark 
3307bd670b35SErik Nordmark 	mutex_enter(&ill->ill_mcast_serializer);
3308bd670b35SErik Nordmark 	rw_enter(&connp->conn_ilg_lock, RW_WRITER);
3309bd670b35SErik Nordmark 	held_ilg = NULL;
3310bd670b35SErik Nordmark 	for (ilg = connp->conn_ilg; ilg != NULL; ilg = ilg->ilg_next) {
3311bd670b35SErik Nordmark 		if (ilg->ilg_condemned)
3312bd670b35SErik Nordmark 			continue;
3313bd670b35SErik Nordmark 
3314bd670b35SErik Nordmark 		if (ilg->ilg_ill != ill)
3315bd670b35SErik Nordmark 			continue;
3316bd670b35SErik Nordmark 
3317bd670b35SErik Nordmark 		/* Detach from current ill */
3318bd670b35SErik Nordmark 		ip1dbg(("ilg_check_detach: detach %s on %s\n",
3319bd670b35SErik Nordmark 		    inet_ntop(AF_INET6, &ilg->ilg_v6group,
3320bd670b35SErik Nordmark 		    group_buf, sizeof (group_buf)),
3321bd670b35SErik Nordmark 		    ilg->ilg_ill->ill_name));
3322bd670b35SErik Nordmark 
3323bd670b35SErik Nordmark 		/* Detach this ilg from the ill/ilm */
3324bd670b35SErik Nordmark 		ilm = ilg->ilg_ilm;
3325bd670b35SErik Nordmark 		ilg->ilg_ilm = NULL;
3326bd670b35SErik Nordmark 		ilg->ilg_ill = NULL;
3327bd670b35SErik Nordmark 		if (ilm == NULL)
3328bd670b35SErik Nordmark 			continue;
3329bd670b35SErik Nordmark 
3330bd670b35SErik Nordmark 		/* Prevent ilg from disappearing */
3331bd670b35SErik Nordmark 		ilg_transfer_hold(held_ilg, ilg);
3332bd670b35SErik Nordmark 		held_ilg = ilg;
3333bd670b35SErik Nordmark 		rw_exit(&connp->conn_ilg_lock);
3334bd670b35SErik Nordmark 
3335bd670b35SErik Nordmark 		(void) ip_delmulti_serial(ilm, B_FALSE, B_TRUE);
3336bd670b35SErik Nordmark 		rw_enter(&connp->conn_ilg_lock, RW_WRITER);
3337bd670b35SErik Nordmark 	}
3338bd670b35SErik Nordmark 	if (held_ilg != NULL)
3339bd670b35SErik Nordmark 		ilg_refrele(held_ilg);
3340bd670b35SErik Nordmark 	rw_exit(&connp->conn_ilg_lock);
3341bd670b35SErik Nordmark 	mutex_exit(&ill->ill_mcast_serializer);
3342f1c454b4SSowmini Varadhan 	/*
3343f1c454b4SSowmini Varadhan 	 * Now that all locks have been dropped, we can send any
3344f1c454b4SSowmini Varadhan 	 * deferred/queued DLPI or IP packets
3345f1c454b4SSowmini Varadhan 	 */
3346f1c454b4SSowmini Varadhan 	ill_mcast_send_queued(ill);
3347f1c454b4SSowmini Varadhan 	ill_dlpi_send_queued(ill);
3348bd670b35SErik Nordmark }
3349bd670b35SErik Nordmark 
3350bd670b35SErik Nordmark /*
3351bd670b35SErik Nordmark  * Check if there is a place to attach the conn_ilgs. We do this for both
3352bd670b35SErik Nordmark  * detached ilgs and attached ones, since for the latter there could be
3353f1c454b4SSowmini Varadhan  * a better ill to attach them to. oill is non-null if we just detached from
3354f1c454b4SSowmini Varadhan  * that ill.
3355bd670b35SErik Nordmark  */
3356bd670b35SErik Nordmark static void
3357f1c454b4SSowmini Varadhan ilg_check_reattach(conn_t *connp, ill_t *oill)
3358bd670b35SErik Nordmark {
3359bd670b35SErik Nordmark 	ill_t	*ill;
3360bd670b35SErik Nordmark 	char	group_buf[INET6_ADDRSTRLEN];
3361bd670b35SErik Nordmark 	ilg_t	*ilg, *held_ilg;
3362bd670b35SErik Nordmark 	ilm_t	*ilm;
3363bd670b35SErik Nordmark 	zoneid_t zoneid = IPCL_ZONEID(connp);
3364bd670b35SErik Nordmark 	int	error;
3365bd670b35SErik Nordmark 	ip_stack_t *ipst = connp->conn_netstack->netstack_ip;
3366bd670b35SErik Nordmark 
3367bd670b35SErik Nordmark 	rw_enter(&connp->conn_ilg_lock, RW_WRITER);
3368bd670b35SErik Nordmark 	held_ilg = NULL;
3369bd670b35SErik Nordmark 	for (ilg = connp->conn_ilg; ilg != NULL; ilg = ilg->ilg_next) {
3370bd670b35SErik Nordmark 		if (ilg->ilg_condemned)
3371bd670b35SErik Nordmark 			continue;
3372bd670b35SErik Nordmark 
3373bd670b35SErik Nordmark 		/* Check if the conn_ill matches what we would pick now */
3374bd670b35SErik Nordmark 		ill = ill_mcast_lookup(&ilg->ilg_v6group, ilg->ilg_ifaddr,
3375bd670b35SErik Nordmark 		    ilg->ilg_ifindex, zoneid, ipst, &error);
3376bd670b35SErik Nordmark 
3377bd670b35SErik Nordmark 		/*
3378bd670b35SErik Nordmark 		 * Make sure the ill is usable for multicast and that
3379bd670b35SErik Nordmark 		 * we can send the DL_ADDMULTI_REQ before we create an
3380bd670b35SErik Nordmark 		 * ilm.
3381bd670b35SErik Nordmark 		 */
3382bd670b35SErik Nordmark 		if (ill != NULL &&
3383bd670b35SErik Nordmark 		    (!(ill->ill_flags & ILLF_MULTICAST) || !ill->ill_dl_up)) {
3384bd670b35SErik Nordmark 			/* Drop locks across ill_refrele */
3385bd670b35SErik Nordmark 			ilg_transfer_hold(held_ilg, ilg);
3386bd670b35SErik Nordmark 			held_ilg = ilg;
3387bd670b35SErik Nordmark 			rw_exit(&connp->conn_ilg_lock);
3388bd670b35SErik Nordmark 			ill_refrele(ill);
3389bd670b35SErik Nordmark 			ill = NULL;
3390bd670b35SErik Nordmark 			rw_enter(&connp->conn_ilg_lock, RW_WRITER);
3391bd670b35SErik Nordmark 			/* Note that ilg could have become condemned */
3392bd670b35SErik Nordmark 		}
3393bd670b35SErik Nordmark 
3394f1c454b4SSowmini Varadhan 		/*
3395f1c454b4SSowmini Varadhan 		 * Is the ill unchanged, even if both are NULL?
3396f1c454b4SSowmini Varadhan 		 * Did we just detach from that ill?
3397f1c454b4SSowmini Varadhan 		 */
3398f1c454b4SSowmini Varadhan 		if (ill == ilg->ilg_ill || (ill != NULL && ill == oill)) {
3399bd670b35SErik Nordmark 			if (ill != NULL) {
3400bd670b35SErik Nordmark 				/* Drop locks across ill_refrele */
3401bd670b35SErik Nordmark 				ilg_transfer_hold(held_ilg, ilg);
3402bd670b35SErik Nordmark 				held_ilg = ilg;
3403bd670b35SErik Nordmark 				rw_exit(&connp->conn_ilg_lock);
3404bd670b35SErik Nordmark 				ill_refrele(ill);
3405bd670b35SErik Nordmark 				rw_enter(&connp->conn_ilg_lock, RW_WRITER);
3406bd670b35SErik Nordmark 			}
3407bd670b35SErik Nordmark 			continue;
3408bd670b35SErik Nordmark 		}
3409bd670b35SErik Nordmark 
3410bd670b35SErik Nordmark 		/* Something changed; detach from old first if needed */
3411bd670b35SErik Nordmark 		if (ilg->ilg_ill != NULL) {
3412bd670b35SErik Nordmark 			ill_t *ill2 = ilg->ilg_ill;
3413bd670b35SErik Nordmark 			boolean_t need_refrele = B_FALSE;
3414bd670b35SErik Nordmark 
3415bd670b35SErik Nordmark 			/*
3416bd670b35SErik Nordmark 			 * In order to serialize on the ill we try to enter
3417bd670b35SErik Nordmark 			 * and if that fails we unlock and relock.
3418bd670b35SErik Nordmark 			 */
3419bd670b35SErik Nordmark 			if (!mutex_tryenter(&ill2->ill_mcast_serializer)) {
3420bd670b35SErik Nordmark 				ill_refhold(ill2);
3421bd670b35SErik Nordmark 				need_refrele = B_TRUE;
3422bd670b35SErik Nordmark 				ilg_transfer_hold(held_ilg, ilg);
3423bd670b35SErik Nordmark 				held_ilg = ilg;
3424bd670b35SErik Nordmark 				rw_exit(&connp->conn_ilg_lock);
3425bd670b35SErik Nordmark 				mutex_enter(&ill2->ill_mcast_serializer);
3426bd670b35SErik Nordmark 				rw_enter(&connp->conn_ilg_lock, RW_WRITER);
3427bd670b35SErik Nordmark 				/* Note that ilg could have become condemned */
3428bd670b35SErik Nordmark 			}
3429bd670b35SErik Nordmark 			/*
3430bd670b35SErik Nordmark 			 * Check that nobody else re-attached the ilg while we
3431bd670b35SErik Nordmark 			 * dropped the lock.
3432bd670b35SErik Nordmark 			 */
3433bd670b35SErik Nordmark 			if (ilg->ilg_ill == ill2) {
3434bd670b35SErik Nordmark 				ASSERT(!ilg->ilg_condemned);
3435bd670b35SErik Nordmark 				/* Detach from current ill */
3436bd670b35SErik Nordmark 				ip1dbg(("conn_check_reattach: detach %s/%s\n",
3437bd670b35SErik Nordmark 				    inet_ntop(AF_INET6, &ilg->ilg_v6group,
3438bd670b35SErik Nordmark 				    group_buf, sizeof (group_buf)),
3439bd670b35SErik Nordmark 				    ill2->ill_name));
3440bd670b35SErik Nordmark 
3441bd670b35SErik Nordmark 				ilm = ilg->ilg_ilm;
3442bd670b35SErik Nordmark 				ilg->ilg_ilm = NULL;
3443bd670b35SErik Nordmark 				ilg->ilg_ill = NULL;
3444bd670b35SErik Nordmark 			} else {
3445bd670b35SErik Nordmark 				ilm = NULL;
3446bd670b35SErik Nordmark 			}
3447f1c454b4SSowmini Varadhan 			ilg_transfer_hold(held_ilg, ilg);
3448f1c454b4SSowmini Varadhan 			held_ilg = ilg;
3449bd670b35SErik Nordmark 			rw_exit(&connp->conn_ilg_lock);
3450bd670b35SErik Nordmark 			if (ilm != NULL)
3451bd670b35SErik Nordmark 				(void) ip_delmulti_serial(ilm, B_FALSE, B_TRUE);
3452bd670b35SErik Nordmark 			mutex_exit(&ill2->ill_mcast_serializer);
3453f1c454b4SSowmini Varadhan 			/*
3454f1c454b4SSowmini Varadhan 			 * Now that all locks have been dropped, we can send any
3455f1c454b4SSowmini Varadhan 			 * deferred/queued DLPI or IP packets
3456f1c454b4SSowmini Varadhan 			 */
3457f1c454b4SSowmini Varadhan 			ill_mcast_send_queued(ill2);
3458f1c454b4SSowmini Varadhan 			ill_dlpi_send_queued(ill2);
3459bd670b35SErik Nordmark 			if (need_refrele) {
3460bd670b35SErik Nordmark 				/* Drop ill reference while we hold no locks */
3461bd670b35SErik Nordmark 				ill_refrele(ill2);
3462bd670b35SErik Nordmark 			}
3463bd670b35SErik Nordmark 			rw_enter(&connp->conn_ilg_lock, RW_WRITER);
3464bd670b35SErik Nordmark 			/*
3465bd670b35SErik Nordmark 			 * While we dropped conn_ilg_lock some other thread
3466bd670b35SErik Nordmark 			 * could have attached this ilg, thus we check again.
3467bd670b35SErik Nordmark 			 */
3468bd670b35SErik Nordmark 			if (ilg->ilg_ill != NULL) {
3469bd670b35SErik Nordmark 				if (ill != NULL) {
3470bd670b35SErik Nordmark 					/* Drop locks across ill_refrele */
3471bd670b35SErik Nordmark 					ilg_transfer_hold(held_ilg, ilg);
3472bd670b35SErik Nordmark 					held_ilg = ilg;
3473bd670b35SErik Nordmark 					rw_exit(&connp->conn_ilg_lock);
3474bd670b35SErik Nordmark 					ill_refrele(ill);
3475bd670b35SErik Nordmark 					rw_enter(&connp->conn_ilg_lock,
3476bd670b35SErik Nordmark 					    RW_WRITER);
3477bd670b35SErik Nordmark 				}
3478bd670b35SErik Nordmark 				continue;
3479bd670b35SErik Nordmark 			}
3480bd670b35SErik Nordmark 		}
3481bd670b35SErik Nordmark 		if (ill != NULL) {
3482bd670b35SErik Nordmark 			/*
3483bd670b35SErik Nordmark 			 * In order to serialize on the ill we try to enter
3484bd670b35SErik Nordmark 			 * and if that fails we unlock and relock.
3485bd670b35SErik Nordmark 			 */
3486bd670b35SErik Nordmark 			if (!mutex_tryenter(&ill->ill_mcast_serializer)) {
3487bd670b35SErik Nordmark 				/* Already have a refhold on ill */
3488bd670b35SErik Nordmark 				ilg_transfer_hold(held_ilg, ilg);
3489bd670b35SErik Nordmark 				held_ilg = ilg;
3490bd670b35SErik Nordmark 				rw_exit(&connp->conn_ilg_lock);
3491bd670b35SErik Nordmark 				mutex_enter(&ill->ill_mcast_serializer);
3492bd670b35SErik Nordmark 				rw_enter(&connp->conn_ilg_lock, RW_WRITER);
3493bd670b35SErik Nordmark 				/* Note that ilg could have become condemned */
3494bd670b35SErik Nordmark 			}
3495f1c454b4SSowmini Varadhan 			ilg_transfer_hold(held_ilg, ilg);
3496f1c454b4SSowmini Varadhan 			held_ilg = ilg;
3497bd670b35SErik Nordmark 			/*
3498bd670b35SErik Nordmark 			 * Check that nobody else attached the ilg and that
3499bd670b35SErik Nordmark 			 * it wasn't condemned while we dropped the lock.
3500bd670b35SErik Nordmark 			 */
3501bd670b35SErik Nordmark 			if (ilg->ilg_ill == NULL && !ilg->ilg_condemned) {
3502bd670b35SErik Nordmark 				/*
3503bd670b35SErik Nordmark 				 * Attach to the new ill. Can fail in which
3504bd670b35SErik Nordmark 				 * case ilg_ill will remain NULL. ilg_attach
3505bd670b35SErik Nordmark 				 * drops and reacquires conn_ilg_lock.
3506bd670b35SErik Nordmark 				 */
3507bd670b35SErik Nordmark 				ip1dbg(("conn_check_reattach: attach %s/%s\n",
3508bd670b35SErik Nordmark 				    inet_ntop(AF_INET6, &ilg->ilg_v6group,
3509bd670b35SErik Nordmark 				    group_buf, sizeof (group_buf)),
3510bd670b35SErik Nordmark 				    ill->ill_name));
3511bd670b35SErik Nordmark 				ilg_attach(connp, ilg, ill);
3512bd670b35SErik Nordmark 				ASSERT(RW_WRITE_HELD(&connp->conn_ilg_lock));
3513bd670b35SErik Nordmark 			}
3514bd670b35SErik Nordmark 			/* Drop locks across ill_refrele */
3515bd670b35SErik Nordmark 			rw_exit(&connp->conn_ilg_lock);
3516f1c454b4SSowmini Varadhan 			mutex_exit(&ill->ill_mcast_serializer);
3517f1c454b4SSowmini Varadhan 			/*
3518f1c454b4SSowmini Varadhan 			 * Now that all locks have been
3519f1c454b4SSowmini Varadhan 			 * dropped, we can send any
3520f1c454b4SSowmini Varadhan 			 * deferred/queued DLPI or IP packets
3521f1c454b4SSowmini Varadhan 			 */
3522f1c454b4SSowmini Varadhan 			ill_mcast_send_queued(ill);
3523f1c454b4SSowmini Varadhan 			ill_dlpi_send_queued(ill);
3524bd670b35SErik Nordmark 			ill_refrele(ill);
3525bd670b35SErik Nordmark 			rw_enter(&connp->conn_ilg_lock, RW_WRITER);
3526bd670b35SErik Nordmark 		}
3527bd670b35SErik Nordmark 	}
3528bd670b35SErik Nordmark 	if (held_ilg != NULL)
3529bd670b35SErik Nordmark 		ilg_refrele(held_ilg);
3530bd670b35SErik Nordmark 	rw_exit(&connp->conn_ilg_lock);
3531bd670b35SErik Nordmark }
3532bd670b35SErik Nordmark 
3533bd670b35SErik Nordmark /*
3534bd670b35SErik Nordmark  * Called when an ill is unplumbed to make sure that there are no
3535bd670b35SErik Nordmark  * dangling conn references to that ill. In that case ill is non-NULL and
3536bd670b35SErik Nordmark  * we make sure we remove all references to it.
3537bd670b35SErik Nordmark  * Also called when we should revisit the ilg_ill used for multicast
3538bd670b35SErik Nordmark  * memberships, in which case ill is NULL.
35397c478bd9Sstevel@tonic-gate  */
35407c478bd9Sstevel@tonic-gate void
3541bd670b35SErik Nordmark update_conn_ill(ill_t *ill, ip_stack_t *ipst)
35427c478bd9Sstevel@tonic-gate {
3543bd670b35SErik Nordmark 	ipcl_walk(conn_update_ill, (caddr_t)ill, ipst);
35447c478bd9Sstevel@tonic-gate }
3545