xref: /onnv-gate/usr/src/uts/common/inet/ip/ip_multi.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate /* Copyright (c) 1990 Mentat Inc. */
27*0Sstevel@tonic-gate 
28*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
29*0Sstevel@tonic-gate 
30*0Sstevel@tonic-gate #include <sys/types.h>
31*0Sstevel@tonic-gate #include <sys/stream.h>
32*0Sstevel@tonic-gate #include <sys/dlpi.h>
33*0Sstevel@tonic-gate #include <sys/stropts.h>
34*0Sstevel@tonic-gate #include <sys/strsun.h>
35*0Sstevel@tonic-gate #include <sys/strlog.h>
36*0Sstevel@tonic-gate #include <sys/ddi.h>
37*0Sstevel@tonic-gate #include <sys/cmn_err.h>
38*0Sstevel@tonic-gate #include <sys/zone.h>
39*0Sstevel@tonic-gate 
40*0Sstevel@tonic-gate #include <sys/param.h>
41*0Sstevel@tonic-gate #include <sys/socket.h>
42*0Sstevel@tonic-gate #define	_SUN_TPI_VERSION	2
43*0Sstevel@tonic-gate #include <sys/tihdr.h>
44*0Sstevel@tonic-gate #include <net/if.h>
45*0Sstevel@tonic-gate #include <net/if_arp.h>
46*0Sstevel@tonic-gate #include <sys/sockio.h>
47*0Sstevel@tonic-gate #include <sys/systm.h>
48*0Sstevel@tonic-gate #include <net/route.h>
49*0Sstevel@tonic-gate #include <netinet/in.h>
50*0Sstevel@tonic-gate #include <net/if_dl.h>
51*0Sstevel@tonic-gate #include <netinet/ip6.h>
52*0Sstevel@tonic-gate #include <netinet/icmp6.h>
53*0Sstevel@tonic-gate 
54*0Sstevel@tonic-gate #include <inet/common.h>
55*0Sstevel@tonic-gate #include <inet/mi.h>
56*0Sstevel@tonic-gate #include <inet/nd.h>
57*0Sstevel@tonic-gate #include <inet/arp.h>
58*0Sstevel@tonic-gate #include <inet/ip.h>
59*0Sstevel@tonic-gate #include <inet/ip6.h>
60*0Sstevel@tonic-gate #include <inet/ip_if.h>
61*0Sstevel@tonic-gate #include <inet/ip_ire.h>
62*0Sstevel@tonic-gate #include <inet/ip_ndp.h>
63*0Sstevel@tonic-gate #include <inet/ip_multi.h>
64*0Sstevel@tonic-gate #include <inet/ipclassifier.h>
65*0Sstevel@tonic-gate #include <inet/ipsec_impl.h>
66*0Sstevel@tonic-gate #include <inet/sctp_ip.h>
67*0Sstevel@tonic-gate #include <inet/ip_listutils.h>
68*0Sstevel@tonic-gate 
69*0Sstevel@tonic-gate #include <netinet/igmp.h>
70*0Sstevel@tonic-gate 
71*0Sstevel@tonic-gate /* igmpv3/mldv2 source filter manipulation */
72*0Sstevel@tonic-gate static void	ilm_bld_flists(conn_t *conn, void *arg);
73*0Sstevel@tonic-gate static void	ilm_gen_filter(ilm_t *ilm, mcast_record_t *fmode,
74*0Sstevel@tonic-gate     slist_t *flist);
75*0Sstevel@tonic-gate 
76*0Sstevel@tonic-gate static ilm_t	*ilm_add_v6(ipif_t *ipif, const in6_addr_t *group,
77*0Sstevel@tonic-gate     ilg_stat_t ilgstat, mcast_record_t ilg_fmode, slist_t *ilg_flist,
78*0Sstevel@tonic-gate     int orig_ifindex, zoneid_t zoneid);
79*0Sstevel@tonic-gate static void	ilm_delete(ilm_t *ilm);
80*0Sstevel@tonic-gate static int	ip_ll_addmulti_v6(ipif_t *ipif, const in6_addr_t *group);
81*0Sstevel@tonic-gate static int	ip_ll_delmulti_v6(ipif_t *ipif, const in6_addr_t *group);
82*0Sstevel@tonic-gate static ilg_t	*ilg_lookup_ill_index_v6(conn_t *connp,
83*0Sstevel@tonic-gate     const in6_addr_t *v6group, int index);
84*0Sstevel@tonic-gate static ilg_t	*ilg_lookup_ipif(conn_t *connp, ipaddr_t group,
85*0Sstevel@tonic-gate     ipif_t *ipif);
86*0Sstevel@tonic-gate static int	ilg_add(conn_t *connp, ipaddr_t group, ipif_t *ipif,
87*0Sstevel@tonic-gate     mcast_record_t fmode, ipaddr_t src);
88*0Sstevel@tonic-gate static int	ilg_add_v6(conn_t *connp, const in6_addr_t *group, ill_t *ill,
89*0Sstevel@tonic-gate     mcast_record_t fmode, const in6_addr_t *v6src);
90*0Sstevel@tonic-gate static void	ilg_delete(conn_t *connp, ilg_t *ilg, const in6_addr_t *src);
91*0Sstevel@tonic-gate static mblk_t	*ill_create_dl(ill_t *ill, uint32_t dl_primitive,
92*0Sstevel@tonic-gate     uint32_t length, uint32_t *addr_lenp, uint32_t *addr_offp);
93*0Sstevel@tonic-gate static mblk_t	*ill_create_squery(ill_t *ill, ipaddr_t ipaddr,
94*0Sstevel@tonic-gate     uint32_t addrlen, uint32_t addroff, mblk_t *mp_tail);
95*0Sstevel@tonic-gate static void	conn_ilg_reap(conn_t *connp);
96*0Sstevel@tonic-gate static int	ip_opt_delete_group_excl(conn_t *connp, ipaddr_t group,
97*0Sstevel@tonic-gate     ipif_t *ipif, mcast_record_t fmode, ipaddr_t src);
98*0Sstevel@tonic-gate static int	ip_opt_delete_group_excl_v6(conn_t *connp,
99*0Sstevel@tonic-gate     const in6_addr_t *v6group, ill_t *ill, mcast_record_t fmode,
100*0Sstevel@tonic-gate     const in6_addr_t *v6src);
101*0Sstevel@tonic-gate 
102*0Sstevel@tonic-gate /*
103*0Sstevel@tonic-gate  * MT notes:
104*0Sstevel@tonic-gate  *
105*0Sstevel@tonic-gate  * Multicast joins operate on both the ilg and ilm structures. Multiple
106*0Sstevel@tonic-gate  * threads operating on an conn (socket) trying to do multicast joins
107*0Sstevel@tonic-gate  * need to synchronize  when operating on the ilg. Multiple threads
108*0Sstevel@tonic-gate  * potentially operating on different conn (socket endpoints) trying to
109*0Sstevel@tonic-gate  * do multicast joins could eventually end up trying to manipulate the
110*0Sstevel@tonic-gate  * ilm simulatenously and need to synchronize on the access to the ilm.
111*0Sstevel@tonic-gate  * Both are amenable to standard Solaris MT techniques, but it would be
112*0Sstevel@tonic-gate  * complex to handle a failover or failback which needs to manipulate
113*0Sstevel@tonic-gate  * ilg/ilms if an applications can also simultaenously join/leave
114*0Sstevel@tonic-gate  * multicast groups. Hence multicast join/leave also go through the ipsq_t
115*0Sstevel@tonic-gate  * serialization.
116*0Sstevel@tonic-gate  *
117*0Sstevel@tonic-gate  * Multicast joins and leaves are single-threaded per phyint/IPMP group
118*0Sstevel@tonic-gate  * using the ipsq serialization mechanism.
119*0Sstevel@tonic-gate  *
120*0Sstevel@tonic-gate  * An ilm is an IP data structure used to track multicast join/leave.
121*0Sstevel@tonic-gate  * An ilm is associated with a <multicast group, ipif> tuple in IPv4 and
122*0Sstevel@tonic-gate  * with just <multicast group> in IPv6. ilm_refcnt is the number of ilg's
123*0Sstevel@tonic-gate  * referencing the ilm. ilms are created / destroyed only as writer. ilms
124*0Sstevel@tonic-gate  * are not passed around, instead they are looked up and used under the
125*0Sstevel@tonic-gate  * ill_lock or as writer. So we don't need a dynamic refcount of the number
126*0Sstevel@tonic-gate  * of threads holding reference to an ilm.
127*0Sstevel@tonic-gate  *
128*0Sstevel@tonic-gate  * Multicast Join operation:
129*0Sstevel@tonic-gate  *
130*0Sstevel@tonic-gate  * The first step is to determine the ipif (v4) or ill (v6) on which
131*0Sstevel@tonic-gate  * the join operation is to be done. The join is done after becoming
132*0Sstevel@tonic-gate  * exclusive on the ipsq associated with the ipif or ill. The conn->conn_ilg
133*0Sstevel@tonic-gate  * and ill->ill_ilm are thus accessed and modified exclusively per ill.
134*0Sstevel@tonic-gate  * Multiple threads can attempt to join simultaneously on different ipif/ill
135*0Sstevel@tonic-gate  * on the same conn. In this case the ipsq serialization does not help in
136*0Sstevel@tonic-gate  * protecting the ilg. It is the conn_lock that is used to protect the ilg.
137*0Sstevel@tonic-gate  * The conn_lock also protects all the ilg_t members.
138*0Sstevel@tonic-gate  *
139*0Sstevel@tonic-gate  * Leave operation.
140*0Sstevel@tonic-gate  *
141*0Sstevel@tonic-gate  * Similar to the join operation, the first step is to determine the ipif
142*0Sstevel@tonic-gate  * or ill (v6) on which the leave operation is to be done. The leave operation
143*0Sstevel@tonic-gate  * is done after becoming exclusive on the ipsq associated with the ipif or ill.
144*0Sstevel@tonic-gate  * As with join ilg modification is done under the protection of the conn lock.
145*0Sstevel@tonic-gate  */
146*0Sstevel@tonic-gate 
147*0Sstevel@tonic-gate #define	IPSQ_ENTER_IPIF(ipif, connp, first_mp, func, ipsq, type)	\
148*0Sstevel@tonic-gate 	ASSERT(connp != NULL);					\
149*0Sstevel@tonic-gate 	(ipsq) = ipsq_try_enter((ipif), NULL, CONNP_TO_WQ(connp),	\
150*0Sstevel@tonic-gate 	    (first_mp), (func), (type), B_TRUE);		\
151*0Sstevel@tonic-gate 	if ((ipsq) == NULL) {					\
152*0Sstevel@tonic-gate 		ipif_refrele(ipif);				\
153*0Sstevel@tonic-gate 		return (EINPROGRESS);				\
154*0Sstevel@tonic-gate 	}
155*0Sstevel@tonic-gate 
156*0Sstevel@tonic-gate #define	IPSQ_ENTER_ILL(ill, connp, first_mp, func, ipsq, type)	\
157*0Sstevel@tonic-gate 	ASSERT(connp != NULL);					\
158*0Sstevel@tonic-gate 	(ipsq) = ipsq_try_enter(NULL, ill, CONNP_TO_WQ(connp),	\
159*0Sstevel@tonic-gate 	    (first_mp),	(func), (type), B_TRUE);		\
160*0Sstevel@tonic-gate 	if ((ipsq) == NULL) {					\
161*0Sstevel@tonic-gate 		ill_refrele(ill);				\
162*0Sstevel@tonic-gate 		return (EINPROGRESS);				\
163*0Sstevel@tonic-gate 	}
164*0Sstevel@tonic-gate 
165*0Sstevel@tonic-gate #define	IPSQ_EXIT(ipsq)	\
166*0Sstevel@tonic-gate 	if (ipsq != NULL)	\
167*0Sstevel@tonic-gate 		ipsq_exit(ipsq, B_TRUE, B_TRUE);
168*0Sstevel@tonic-gate 
169*0Sstevel@tonic-gate #define	ILG_WALKER_HOLD(connp)	(connp)->conn_ilg_walker_cnt++
170*0Sstevel@tonic-gate 
171*0Sstevel@tonic-gate #define	ILG_WALKER_RELE(connp)				\
172*0Sstevel@tonic-gate 	{						\
173*0Sstevel@tonic-gate 		(connp)->conn_ilg_walker_cnt--;		\
174*0Sstevel@tonic-gate 		if ((connp)->conn_ilg_walker_cnt == 0)	\
175*0Sstevel@tonic-gate 			conn_ilg_reap(connp);		\
176*0Sstevel@tonic-gate 	}
177*0Sstevel@tonic-gate 
178*0Sstevel@tonic-gate static void
179*0Sstevel@tonic-gate conn_ilg_reap(conn_t *connp)
180*0Sstevel@tonic-gate {
181*0Sstevel@tonic-gate 	int	to;
182*0Sstevel@tonic-gate 	int	from;
183*0Sstevel@tonic-gate 
184*0Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&connp->conn_lock));
185*0Sstevel@tonic-gate 
186*0Sstevel@tonic-gate 	to = 0;
187*0Sstevel@tonic-gate 	from = 0;
188*0Sstevel@tonic-gate 	while (from < connp->conn_ilg_inuse) {
189*0Sstevel@tonic-gate 		if (connp->conn_ilg[from].ilg_flags & ILG_DELETED) {
190*0Sstevel@tonic-gate 			FREE_SLIST(connp->conn_ilg[from].ilg_filter);
191*0Sstevel@tonic-gate 			from++;
192*0Sstevel@tonic-gate 			continue;
193*0Sstevel@tonic-gate 		}
194*0Sstevel@tonic-gate 		if (to != from)
195*0Sstevel@tonic-gate 			connp->conn_ilg[to] = connp->conn_ilg[from];
196*0Sstevel@tonic-gate 		to++;
197*0Sstevel@tonic-gate 		from++;
198*0Sstevel@tonic-gate 	}
199*0Sstevel@tonic-gate 
200*0Sstevel@tonic-gate 	connp->conn_ilg_inuse = to;
201*0Sstevel@tonic-gate 
202*0Sstevel@tonic-gate 	if (connp->conn_ilg_inuse == 0) {
203*0Sstevel@tonic-gate 		mi_free((char *)connp->conn_ilg);
204*0Sstevel@tonic-gate 		connp->conn_ilg = NULL;
205*0Sstevel@tonic-gate 		cv_broadcast(&connp->conn_refcv);
206*0Sstevel@tonic-gate 	}
207*0Sstevel@tonic-gate }
208*0Sstevel@tonic-gate 
209*0Sstevel@tonic-gate #define	GETSTRUCT(structure, number)	\
210*0Sstevel@tonic-gate 	((structure *)mi_zalloc(sizeof (structure) * (number)))
211*0Sstevel@tonic-gate 
212*0Sstevel@tonic-gate #define	ILG_ALLOC_CHUNK	16
213*0Sstevel@tonic-gate 
214*0Sstevel@tonic-gate /*
215*0Sstevel@tonic-gate  * Returns a pointer to the next available ilg in conn_ilg.  Allocs more
216*0Sstevel@tonic-gate  * buffers in size of ILG_ALLOC_CHUNK ilgs when needed, and updates conn's
217*0Sstevel@tonic-gate  * ilg tracking fields appropriately (conn_ilg_inuse reflects usage of the
218*0Sstevel@tonic-gate  * returned ilg).  Returns NULL on failure (ENOMEM).
219*0Sstevel@tonic-gate  *
220*0Sstevel@tonic-gate  * Assumes connp->conn_lock is held.
221*0Sstevel@tonic-gate  */
222*0Sstevel@tonic-gate static ilg_t *
223*0Sstevel@tonic-gate conn_ilg_alloc(conn_t *connp)
224*0Sstevel@tonic-gate {
225*0Sstevel@tonic-gate 	ilg_t *new;
226*0Sstevel@tonic-gate 	int curcnt;
227*0Sstevel@tonic-gate 
228*0Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&connp->conn_lock));
229*0Sstevel@tonic-gate 	ASSERT(connp->conn_ilg_inuse <= connp->conn_ilg_allocated);
230*0Sstevel@tonic-gate 
231*0Sstevel@tonic-gate 	if (connp->conn_ilg == NULL) {
232*0Sstevel@tonic-gate 		connp->conn_ilg = GETSTRUCT(ilg_t, ILG_ALLOC_CHUNK);
233*0Sstevel@tonic-gate 		if (connp->conn_ilg == NULL)
234*0Sstevel@tonic-gate 			return (NULL);
235*0Sstevel@tonic-gate 		connp->conn_ilg_allocated = ILG_ALLOC_CHUNK;
236*0Sstevel@tonic-gate 		connp->conn_ilg_inuse = 0;
237*0Sstevel@tonic-gate 	}
238*0Sstevel@tonic-gate 	if (connp->conn_ilg_inuse == connp->conn_ilg_allocated) {
239*0Sstevel@tonic-gate 		curcnt = connp->conn_ilg_allocated;
240*0Sstevel@tonic-gate 		new = GETSTRUCT(ilg_t, curcnt + ILG_ALLOC_CHUNK);
241*0Sstevel@tonic-gate 		if (new == NULL)
242*0Sstevel@tonic-gate 			return (NULL);
243*0Sstevel@tonic-gate 		bcopy(connp->conn_ilg, new, sizeof (ilg_t) * curcnt);
244*0Sstevel@tonic-gate 		mi_free((char *)connp->conn_ilg);
245*0Sstevel@tonic-gate 		connp->conn_ilg = new;
246*0Sstevel@tonic-gate 		connp->conn_ilg_allocated += ILG_ALLOC_CHUNK;
247*0Sstevel@tonic-gate 	}
248*0Sstevel@tonic-gate 
249*0Sstevel@tonic-gate 	return (&connp->conn_ilg[connp->conn_ilg_inuse++]);
250*0Sstevel@tonic-gate }
251*0Sstevel@tonic-gate 
252*0Sstevel@tonic-gate typedef struct ilm_fbld_s {
253*0Sstevel@tonic-gate 	ilm_t		*fbld_ilm;
254*0Sstevel@tonic-gate 	int		fbld_in_cnt;
255*0Sstevel@tonic-gate 	int		fbld_ex_cnt;
256*0Sstevel@tonic-gate 	slist_t		fbld_in;
257*0Sstevel@tonic-gate 	slist_t		fbld_ex;
258*0Sstevel@tonic-gate 	boolean_t	fbld_in_overflow;
259*0Sstevel@tonic-gate } ilm_fbld_t;
260*0Sstevel@tonic-gate 
261*0Sstevel@tonic-gate static void
262*0Sstevel@tonic-gate ilm_bld_flists(conn_t *conn, void *arg)
263*0Sstevel@tonic-gate {
264*0Sstevel@tonic-gate 	int i;
265*0Sstevel@tonic-gate 	ilm_fbld_t *fbld = (ilm_fbld_t *)(arg);
266*0Sstevel@tonic-gate 	ilm_t *ilm = fbld->fbld_ilm;
267*0Sstevel@tonic-gate 	in6_addr_t *v6group = &ilm->ilm_v6addr;
268*0Sstevel@tonic-gate 
269*0Sstevel@tonic-gate 	if (conn->conn_ilg_inuse == 0)
270*0Sstevel@tonic-gate 		return;
271*0Sstevel@tonic-gate 
272*0Sstevel@tonic-gate 	/*
273*0Sstevel@tonic-gate 	 * Since we can't break out of the ipcl_walk once started, we still
274*0Sstevel@tonic-gate 	 * have to look at every conn.  But if we've already found one
275*0Sstevel@tonic-gate 	 * (EXCLUDE, NULL) list, there's no need to keep checking individual
276*0Sstevel@tonic-gate 	 * ilgs--that will be our state.
277*0Sstevel@tonic-gate 	 */
278*0Sstevel@tonic-gate 	if (fbld->fbld_ex_cnt > 0 && fbld->fbld_ex.sl_numsrc == 0)
279*0Sstevel@tonic-gate 		return;
280*0Sstevel@tonic-gate 
281*0Sstevel@tonic-gate 	/*
282*0Sstevel@tonic-gate 	 * Check this conn's ilgs to see if any are interested in our
283*0Sstevel@tonic-gate 	 * ilm (group, interface match).  If so, update the master
284*0Sstevel@tonic-gate 	 * include and exclude lists we're building in the fbld struct
285*0Sstevel@tonic-gate 	 * with this ilg's filter info.
286*0Sstevel@tonic-gate 	 */
287*0Sstevel@tonic-gate 	mutex_enter(&conn->conn_lock);
288*0Sstevel@tonic-gate 	for (i = 0; i < conn->conn_ilg_inuse; i++) {
289*0Sstevel@tonic-gate 		ilg_t *ilg = &conn->conn_ilg[i];
290*0Sstevel@tonic-gate 		if ((ilg->ilg_ill == ilm->ilm_ill) &&
291*0Sstevel@tonic-gate 		    (ilg->ilg_ipif == ilm->ilm_ipif) &&
292*0Sstevel@tonic-gate 		    IN6_ARE_ADDR_EQUAL(&ilg->ilg_v6group, v6group)) {
293*0Sstevel@tonic-gate 			if (ilg->ilg_fmode == MODE_IS_INCLUDE) {
294*0Sstevel@tonic-gate 				fbld->fbld_in_cnt++;
295*0Sstevel@tonic-gate 				if (!fbld->fbld_in_overflow)
296*0Sstevel@tonic-gate 					l_union_in_a(&fbld->fbld_in,
297*0Sstevel@tonic-gate 					    ilg->ilg_filter,
298*0Sstevel@tonic-gate 					    &fbld->fbld_in_overflow);
299*0Sstevel@tonic-gate 			} else {
300*0Sstevel@tonic-gate 				fbld->fbld_ex_cnt++;
301*0Sstevel@tonic-gate 				/*
302*0Sstevel@tonic-gate 				 * On the first exclude list, don't try to do
303*0Sstevel@tonic-gate 				 * an intersection, as the master exclude list
304*0Sstevel@tonic-gate 				 * is intentionally empty.  If the master list
305*0Sstevel@tonic-gate 				 * is still empty on later iterations, that
306*0Sstevel@tonic-gate 				 * means we have at least one ilg with an empty
307*0Sstevel@tonic-gate 				 * exclude list, so that should be reflected
308*0Sstevel@tonic-gate 				 * when we take the intersection.
309*0Sstevel@tonic-gate 				 */
310*0Sstevel@tonic-gate 				if (fbld->fbld_ex_cnt == 1) {
311*0Sstevel@tonic-gate 					if (ilg->ilg_filter != NULL)
312*0Sstevel@tonic-gate 						l_copy(ilg->ilg_filter,
313*0Sstevel@tonic-gate 						    &fbld->fbld_ex);
314*0Sstevel@tonic-gate 				} else {
315*0Sstevel@tonic-gate 					l_intersection_in_a(&fbld->fbld_ex,
316*0Sstevel@tonic-gate 					    ilg->ilg_filter);
317*0Sstevel@tonic-gate 				}
318*0Sstevel@tonic-gate 			}
319*0Sstevel@tonic-gate 			/* there will only be one match, so break now. */
320*0Sstevel@tonic-gate 			break;
321*0Sstevel@tonic-gate 		}
322*0Sstevel@tonic-gate 	}
323*0Sstevel@tonic-gate 	mutex_exit(&conn->conn_lock);
324*0Sstevel@tonic-gate }
325*0Sstevel@tonic-gate 
326*0Sstevel@tonic-gate static void
327*0Sstevel@tonic-gate ilm_gen_filter(ilm_t *ilm, mcast_record_t *fmode, slist_t *flist)
328*0Sstevel@tonic-gate {
329*0Sstevel@tonic-gate 	ilm_fbld_t fbld;
330*0Sstevel@tonic-gate 
331*0Sstevel@tonic-gate 	fbld.fbld_ilm = ilm;
332*0Sstevel@tonic-gate 	fbld.fbld_in_cnt = fbld.fbld_ex_cnt = 0;
333*0Sstevel@tonic-gate 	fbld.fbld_in.sl_numsrc = fbld.fbld_ex.sl_numsrc = 0;
334*0Sstevel@tonic-gate 	fbld.fbld_in_overflow = B_FALSE;
335*0Sstevel@tonic-gate 
336*0Sstevel@tonic-gate 	/* first, construct our master include and exclude lists */
337*0Sstevel@tonic-gate 	ipcl_walk(ilm_bld_flists, (caddr_t)&fbld);
338*0Sstevel@tonic-gate 
339*0Sstevel@tonic-gate 	/* now use those master lists to generate the interface filter */
340*0Sstevel@tonic-gate 
341*0Sstevel@tonic-gate 	/* if include list overflowed, filter is (EXCLUDE, NULL) */
342*0Sstevel@tonic-gate 	if (fbld.fbld_in_overflow) {
343*0Sstevel@tonic-gate 		*fmode = MODE_IS_EXCLUDE;
344*0Sstevel@tonic-gate 		flist->sl_numsrc = 0;
345*0Sstevel@tonic-gate 		return;
346*0Sstevel@tonic-gate 	}
347*0Sstevel@tonic-gate 
348*0Sstevel@tonic-gate 	/* if nobody interested, interface filter is (INCLUDE, NULL) */
349*0Sstevel@tonic-gate 	if (fbld.fbld_in_cnt == 0 && fbld.fbld_ex_cnt == 0) {
350*0Sstevel@tonic-gate 		*fmode = MODE_IS_INCLUDE;
351*0Sstevel@tonic-gate 		flist->sl_numsrc = 0;
352*0Sstevel@tonic-gate 		return;
353*0Sstevel@tonic-gate 	}
354*0Sstevel@tonic-gate 
355*0Sstevel@tonic-gate 	/*
356*0Sstevel@tonic-gate 	 * If there are no exclude lists, then the interface filter
357*0Sstevel@tonic-gate 	 * is INCLUDE, with its filter list equal to fbld_in.  A single
358*0Sstevel@tonic-gate 	 * exclude list makes the interface filter EXCLUDE, with its
359*0Sstevel@tonic-gate 	 * filter list equal to (fbld_ex - fbld_in).
360*0Sstevel@tonic-gate 	 */
361*0Sstevel@tonic-gate 	if (fbld.fbld_ex_cnt == 0) {
362*0Sstevel@tonic-gate 		*fmode = MODE_IS_INCLUDE;
363*0Sstevel@tonic-gate 		l_copy(&fbld.fbld_in, flist);
364*0Sstevel@tonic-gate 	} else {
365*0Sstevel@tonic-gate 		*fmode = MODE_IS_EXCLUDE;
366*0Sstevel@tonic-gate 		l_difference(&fbld.fbld_ex, &fbld.fbld_in, flist);
367*0Sstevel@tonic-gate 	}
368*0Sstevel@tonic-gate }
369*0Sstevel@tonic-gate 
370*0Sstevel@tonic-gate /*
371*0Sstevel@tonic-gate  * If the given interface has failed, choose a new one to join on so
372*0Sstevel@tonic-gate  * that we continue to receive packets.  ilg_orig_ifindex remembers
373*0Sstevel@tonic-gate  * what the application used to join on so that we know the ilg to
374*0Sstevel@tonic-gate  * delete even though we change the ill here.  Callers will store the
375*0Sstevel@tonic-gate  * ilg returned from this function in ilg_ill.  Thus when we receive
376*0Sstevel@tonic-gate  * a packet on ilg_ill, conn_wantpacket_v6 will deliver the packets.
377*0Sstevel@tonic-gate  *
378*0Sstevel@tonic-gate  * This function must be called as writer so we can walk the group
379*0Sstevel@tonic-gate  * list and examine flags without holding a lock.
380*0Sstevel@tonic-gate  */
381*0Sstevel@tonic-gate ill_t *
382*0Sstevel@tonic-gate ip_choose_multi_ill(ill_t *ill, const in6_addr_t *grp)
383*0Sstevel@tonic-gate {
384*0Sstevel@tonic-gate 	ill_t	*till;
385*0Sstevel@tonic-gate 	ill_group_t *illgrp = ill->ill_group;
386*0Sstevel@tonic-gate 
387*0Sstevel@tonic-gate 	ASSERT(IAM_WRITER_ILL(ill));
388*0Sstevel@tonic-gate 
389*0Sstevel@tonic-gate 	if (IN6_IS_ADDR_UNSPECIFIED(grp) || illgrp == NULL)
390*0Sstevel@tonic-gate 		return (ill);
391*0Sstevel@tonic-gate 
392*0Sstevel@tonic-gate 	if ((ill->ill_phyint->phyint_flags & (PHYI_FAILED|PHYI_INACTIVE)) == 0)
393*0Sstevel@tonic-gate 		return (ill);
394*0Sstevel@tonic-gate 
395*0Sstevel@tonic-gate 	till = illgrp->illgrp_ill;
396*0Sstevel@tonic-gate 	while (till != NULL &&
397*0Sstevel@tonic-gate 	    (till->ill_phyint->phyint_flags & (PHYI_FAILED|PHYI_INACTIVE))) {
398*0Sstevel@tonic-gate 		till = till->ill_group_next;
399*0Sstevel@tonic-gate 	}
400*0Sstevel@tonic-gate 	if (till != NULL)
401*0Sstevel@tonic-gate 		return (till);
402*0Sstevel@tonic-gate 
403*0Sstevel@tonic-gate 	return (ill);
404*0Sstevel@tonic-gate }
405*0Sstevel@tonic-gate 
406*0Sstevel@tonic-gate static int
407*0Sstevel@tonic-gate ilm_update_add(ilm_t *ilm, ilg_stat_t ilgstat, slist_t *ilg_flist,
408*0Sstevel@tonic-gate     boolean_t isv6)
409*0Sstevel@tonic-gate {
410*0Sstevel@tonic-gate 	mcast_record_t fmode;
411*0Sstevel@tonic-gate 	slist_t *flist;
412*0Sstevel@tonic-gate 	boolean_t fdefault;
413*0Sstevel@tonic-gate 	char buf[INET6_ADDRSTRLEN];
414*0Sstevel@tonic-gate 	ill_t *ill = isv6 ? ilm->ilm_ill : ilm->ilm_ipif->ipif_ill;
415*0Sstevel@tonic-gate 
416*0Sstevel@tonic-gate 	/*
417*0Sstevel@tonic-gate 	 * There are several cases where the ilm's filter state
418*0Sstevel@tonic-gate 	 * defaults to (EXCLUDE, NULL):
419*0Sstevel@tonic-gate 	 *	- we've had previous joins without associated ilgs
420*0Sstevel@tonic-gate 	 *	- this join has no associated ilg
421*0Sstevel@tonic-gate 	 *	- the ilg's filter state is (EXCLUDE, NULL)
422*0Sstevel@tonic-gate 	 */
423*0Sstevel@tonic-gate 	fdefault = (ilm->ilm_no_ilg_cnt > 0) ||
424*0Sstevel@tonic-gate 	    (ilgstat == ILGSTAT_NONE) || SLIST_IS_EMPTY(ilg_flist);
425*0Sstevel@tonic-gate 
426*0Sstevel@tonic-gate 	/* attempt mallocs (if needed) before doing anything else */
427*0Sstevel@tonic-gate 	if ((flist = l_alloc()) == NULL)
428*0Sstevel@tonic-gate 		return (ENOMEM);
429*0Sstevel@tonic-gate 	if (!fdefault && ilm->ilm_filter == NULL) {
430*0Sstevel@tonic-gate 		ilm->ilm_filter = l_alloc();
431*0Sstevel@tonic-gate 		if (ilm->ilm_filter == NULL) {
432*0Sstevel@tonic-gate 			l_free(flist);
433*0Sstevel@tonic-gate 			return (ENOMEM);
434*0Sstevel@tonic-gate 		}
435*0Sstevel@tonic-gate 	}
436*0Sstevel@tonic-gate 
437*0Sstevel@tonic-gate 	if (ilgstat != ILGSTAT_CHANGE)
438*0Sstevel@tonic-gate 		ilm->ilm_refcnt++;
439*0Sstevel@tonic-gate 
440*0Sstevel@tonic-gate 	if (ilgstat == ILGSTAT_NONE)
441*0Sstevel@tonic-gate 		ilm->ilm_no_ilg_cnt++;
442*0Sstevel@tonic-gate 
443*0Sstevel@tonic-gate 	/*
444*0Sstevel@tonic-gate 	 * Determine new filter state.  If it's not the default
445*0Sstevel@tonic-gate 	 * (EXCLUDE, NULL), we must walk the conn list to find
446*0Sstevel@tonic-gate 	 * any ilgs interested in this group, and re-build the
447*0Sstevel@tonic-gate 	 * ilm filter.
448*0Sstevel@tonic-gate 	 */
449*0Sstevel@tonic-gate 	if (fdefault) {
450*0Sstevel@tonic-gate 		fmode = MODE_IS_EXCLUDE;
451*0Sstevel@tonic-gate 		flist->sl_numsrc = 0;
452*0Sstevel@tonic-gate 	} else {
453*0Sstevel@tonic-gate 		ilm_gen_filter(ilm, &fmode, flist);
454*0Sstevel@tonic-gate 	}
455*0Sstevel@tonic-gate 
456*0Sstevel@tonic-gate 	/* make sure state actually changed; nothing to do if not. */
457*0Sstevel@tonic-gate 	if ((ilm->ilm_fmode == fmode) &&
458*0Sstevel@tonic-gate 	    !lists_are_different(ilm->ilm_filter, flist)) {
459*0Sstevel@tonic-gate 		l_free(flist);
460*0Sstevel@tonic-gate 		return (0);
461*0Sstevel@tonic-gate 	}
462*0Sstevel@tonic-gate 
463*0Sstevel@tonic-gate 	/* send the state change report */
464*0Sstevel@tonic-gate 	if ((ill->ill_phyint->phyint_flags & PHYI_LOOPBACK) == 0) {
465*0Sstevel@tonic-gate 		if (isv6)
466*0Sstevel@tonic-gate 			mld_statechange(ilm, fmode, flist);
467*0Sstevel@tonic-gate 		else
468*0Sstevel@tonic-gate 			igmp_statechange(ilm, fmode, flist);
469*0Sstevel@tonic-gate 	}
470*0Sstevel@tonic-gate 
471*0Sstevel@tonic-gate 	/* update the ilm state */
472*0Sstevel@tonic-gate 	ilm->ilm_fmode = fmode;
473*0Sstevel@tonic-gate 	if (flist->sl_numsrc > 0)
474*0Sstevel@tonic-gate 		l_copy(flist, ilm->ilm_filter);
475*0Sstevel@tonic-gate 	else
476*0Sstevel@tonic-gate 		CLEAR_SLIST(ilm->ilm_filter);
477*0Sstevel@tonic-gate 
478*0Sstevel@tonic-gate 	ip1dbg(("ilm_update: new if filter mode %d, group %s\n", ilm->ilm_fmode,
479*0Sstevel@tonic-gate 	    inet_ntop(AF_INET6, &ilm->ilm_v6addr, buf, sizeof (buf))));
480*0Sstevel@tonic-gate 
481*0Sstevel@tonic-gate 	l_free(flist);
482*0Sstevel@tonic-gate 	return (0);
483*0Sstevel@tonic-gate }
484*0Sstevel@tonic-gate 
485*0Sstevel@tonic-gate static int
486*0Sstevel@tonic-gate ilm_update_del(ilm_t *ilm, boolean_t isv6)
487*0Sstevel@tonic-gate {
488*0Sstevel@tonic-gate 	mcast_record_t fmode;
489*0Sstevel@tonic-gate 	slist_t *flist;
490*0Sstevel@tonic-gate 	ill_t *ill = isv6 ? ilm->ilm_ill : ilm->ilm_ipif->ipif_ill;
491*0Sstevel@tonic-gate 
492*0Sstevel@tonic-gate 	ip1dbg(("ilm_update_del: still %d left; updating state\n",
493*0Sstevel@tonic-gate 	    ilm->ilm_refcnt));
494*0Sstevel@tonic-gate 
495*0Sstevel@tonic-gate 	if ((flist = l_alloc()) == NULL)
496*0Sstevel@tonic-gate 		return (ENOMEM);
497*0Sstevel@tonic-gate 
498*0Sstevel@tonic-gate 	/*
499*0Sstevel@tonic-gate 	 * If present, the ilg in question has already either been
500*0Sstevel@tonic-gate 	 * updated or removed from our list; so all we need to do
501*0Sstevel@tonic-gate 	 * now is walk the list to update the ilm filter state.
502*0Sstevel@tonic-gate 	 *
503*0Sstevel@tonic-gate 	 * Skip the list walk if we have any no-ilg joins, which
504*0Sstevel@tonic-gate 	 * cause the filter state to revert to (EXCLUDE, NULL).
505*0Sstevel@tonic-gate 	 */
506*0Sstevel@tonic-gate 	if (ilm->ilm_no_ilg_cnt != 0) {
507*0Sstevel@tonic-gate 		fmode = MODE_IS_EXCLUDE;
508*0Sstevel@tonic-gate 		flist->sl_numsrc = 0;
509*0Sstevel@tonic-gate 	} else {
510*0Sstevel@tonic-gate 		ilm_gen_filter(ilm, &fmode, flist);
511*0Sstevel@tonic-gate 	}
512*0Sstevel@tonic-gate 
513*0Sstevel@tonic-gate 	/* check to see if state needs to be updated */
514*0Sstevel@tonic-gate 	if ((ilm->ilm_fmode == fmode) &&
515*0Sstevel@tonic-gate 	    (!lists_are_different(ilm->ilm_filter, flist))) {
516*0Sstevel@tonic-gate 		l_free(flist);
517*0Sstevel@tonic-gate 		return (0);
518*0Sstevel@tonic-gate 	}
519*0Sstevel@tonic-gate 
520*0Sstevel@tonic-gate 	if ((ill->ill_phyint->phyint_flags & PHYI_LOOPBACK) == 0) {
521*0Sstevel@tonic-gate 		if (isv6)
522*0Sstevel@tonic-gate 			mld_statechange(ilm, fmode, flist);
523*0Sstevel@tonic-gate 		else
524*0Sstevel@tonic-gate 			igmp_statechange(ilm, fmode, flist);
525*0Sstevel@tonic-gate 	}
526*0Sstevel@tonic-gate 
527*0Sstevel@tonic-gate 	ilm->ilm_fmode = fmode;
528*0Sstevel@tonic-gate 	if (flist->sl_numsrc > 0) {
529*0Sstevel@tonic-gate 		if (ilm->ilm_filter == NULL) {
530*0Sstevel@tonic-gate 			ilm->ilm_filter = l_alloc();
531*0Sstevel@tonic-gate 			if (ilm->ilm_filter == NULL) {
532*0Sstevel@tonic-gate 				char buf[INET6_ADDRSTRLEN];
533*0Sstevel@tonic-gate 				ip1dbg(("ilm_update_del: failed to alloc ilm "
534*0Sstevel@tonic-gate 				    "filter; no source filtering for %s on %s",
535*0Sstevel@tonic-gate 				    inet_ntop(AF_INET6, &ilm->ilm_v6addr,
536*0Sstevel@tonic-gate 				    buf, sizeof (buf)), ill->ill_name));
537*0Sstevel@tonic-gate 				ilm->ilm_fmode = MODE_IS_EXCLUDE;
538*0Sstevel@tonic-gate 				l_free(flist);
539*0Sstevel@tonic-gate 				return (0);
540*0Sstevel@tonic-gate 			}
541*0Sstevel@tonic-gate 		}
542*0Sstevel@tonic-gate 		l_copy(flist, ilm->ilm_filter);
543*0Sstevel@tonic-gate 	} else {
544*0Sstevel@tonic-gate 		CLEAR_SLIST(ilm->ilm_filter);
545*0Sstevel@tonic-gate 	}
546*0Sstevel@tonic-gate 
547*0Sstevel@tonic-gate 	l_free(flist);
548*0Sstevel@tonic-gate 	return (0);
549*0Sstevel@tonic-gate }
550*0Sstevel@tonic-gate 
551*0Sstevel@tonic-gate /*
552*0Sstevel@tonic-gate  * INADDR_ANY means all multicast addresses. This is only used
553*0Sstevel@tonic-gate  * by the multicast router.
554*0Sstevel@tonic-gate  * INADDR_ANY is stored as IPv6 unspecified addr.
555*0Sstevel@tonic-gate  */
556*0Sstevel@tonic-gate int
557*0Sstevel@tonic-gate ip_addmulti(ipaddr_t group, ipif_t *ipif, ilg_stat_t ilgstat,
558*0Sstevel@tonic-gate     mcast_record_t ilg_fmode, slist_t *ilg_flist)
559*0Sstevel@tonic-gate {
560*0Sstevel@tonic-gate 	ill_t	*ill = ipif->ipif_ill;
561*0Sstevel@tonic-gate 	ilm_t 	*ilm;
562*0Sstevel@tonic-gate 	in6_addr_t v6group;
563*0Sstevel@tonic-gate 	int	ret;
564*0Sstevel@tonic-gate 
565*0Sstevel@tonic-gate 	ASSERT(IAM_WRITER_IPIF(ipif));
566*0Sstevel@tonic-gate 
567*0Sstevel@tonic-gate 	if (!CLASSD(group) && group != INADDR_ANY)
568*0Sstevel@tonic-gate 		return (EINVAL);
569*0Sstevel@tonic-gate 
570*0Sstevel@tonic-gate 	/*
571*0Sstevel@tonic-gate 	 * INADDR_ANY is represented as the IPv6 unspecifed addr.
572*0Sstevel@tonic-gate 	 */
573*0Sstevel@tonic-gate 	if (group == INADDR_ANY)
574*0Sstevel@tonic-gate 		v6group = ipv6_all_zeros;
575*0Sstevel@tonic-gate 	else
576*0Sstevel@tonic-gate 		IN6_IPADDR_TO_V4MAPPED(group, &v6group);
577*0Sstevel@tonic-gate 
578*0Sstevel@tonic-gate 	ilm = ilm_lookup_ipif(ipif, group);
579*0Sstevel@tonic-gate 	if (ilm != NULL)
580*0Sstevel@tonic-gate 		return (ilm_update_add(ilm, ilgstat, ilg_flist, B_FALSE));
581*0Sstevel@tonic-gate 
582*0Sstevel@tonic-gate 	/*
583*0Sstevel@tonic-gate 	 * ilms are associated with ipifs in IPv4. It moves with the
584*0Sstevel@tonic-gate 	 * ipif if the ipif moves to a new ill when the interface
585*0Sstevel@tonic-gate 	 * fails. Thus we really don't check whether the ipif_ill
586*0Sstevel@tonic-gate 	 * has failed like in IPv6. If it has FAILED the ipif
587*0Sstevel@tonic-gate 	 * will move (daemon will move it) and hence the ilm, if the
588*0Sstevel@tonic-gate 	 * ipif is not IPIF_NOFAILOVER. For the IPIF_NOFAILOVER ipifs,
589*0Sstevel@tonic-gate 	 * we continue to receive in the same place even if the
590*0Sstevel@tonic-gate 	 * interface fails.
591*0Sstevel@tonic-gate 	 */
592*0Sstevel@tonic-gate 	ilm = ilm_add_v6(ipif, &v6group, ilgstat, ilg_fmode, ilg_flist,
593*0Sstevel@tonic-gate 	    ill->ill_phyint->phyint_ifindex, ipif->ipif_zoneid);
594*0Sstevel@tonic-gate 	if (ilm == NULL)
595*0Sstevel@tonic-gate 		return (ENOMEM);
596*0Sstevel@tonic-gate 
597*0Sstevel@tonic-gate 	if (group == INADDR_ANY) {
598*0Sstevel@tonic-gate 		/*
599*0Sstevel@tonic-gate 		 * Check how many ipif's have members in this group -
600*0Sstevel@tonic-gate 		 * if more then one we should not tell the driver to join
601*0Sstevel@tonic-gate 		 * this time
602*0Sstevel@tonic-gate 		 */
603*0Sstevel@tonic-gate 		if (ilm_numentries_v6(ill, &v6group) > 1)
604*0Sstevel@tonic-gate 			return (0);
605*0Sstevel@tonic-gate 		if (ill->ill_group == NULL)
606*0Sstevel@tonic-gate 			ret = ip_join_allmulti(ipif);
607*0Sstevel@tonic-gate 		else
608*0Sstevel@tonic-gate 			ret = ill_nominate_mcast_rcv(ill->ill_group);
609*0Sstevel@tonic-gate 		if (ret != 0)
610*0Sstevel@tonic-gate 			ilm_delete(ilm);
611*0Sstevel@tonic-gate 		return (ret);
612*0Sstevel@tonic-gate 	}
613*0Sstevel@tonic-gate 
614*0Sstevel@tonic-gate 	if ((ill->ill_phyint->phyint_flags & PHYI_LOOPBACK) == 0)
615*0Sstevel@tonic-gate 		igmp_joingroup(ilm);
616*0Sstevel@tonic-gate 
617*0Sstevel@tonic-gate 	if (ilm_numentries_v6(ill, &v6group) > 1)
618*0Sstevel@tonic-gate 		return (0);
619*0Sstevel@tonic-gate 
620*0Sstevel@tonic-gate 	ret = ip_ll_addmulti_v6(ipif, &v6group);
621*0Sstevel@tonic-gate 	if (ret != 0)
622*0Sstevel@tonic-gate 		ilm_delete(ilm);
623*0Sstevel@tonic-gate 	return (ret);
624*0Sstevel@tonic-gate }
625*0Sstevel@tonic-gate 
626*0Sstevel@tonic-gate /*
627*0Sstevel@tonic-gate  * The unspecified address means all multicast addresses.
628*0Sstevel@tonic-gate  * This is only used by the multicast router.
629*0Sstevel@tonic-gate  *
630*0Sstevel@tonic-gate  * ill identifies the interface to join on; it may not match the
631*0Sstevel@tonic-gate  * interface requested by the application of a failover has taken
632*0Sstevel@tonic-gate  * place.  orig_ifindex always identifies the interface requested
633*0Sstevel@tonic-gate  * by the app.
634*0Sstevel@tonic-gate  *
635*0Sstevel@tonic-gate  * ilgstat tells us if there's an ilg associated with this join,
636*0Sstevel@tonic-gate  * and if so, if it's a new ilg or a change to an existing one.
637*0Sstevel@tonic-gate  * ilg_fmode and ilg_flist give us the current filter state of
638*0Sstevel@tonic-gate  * the ilg (and will be EXCLUDE {NULL} in the case of no ilg).
639*0Sstevel@tonic-gate  */
640*0Sstevel@tonic-gate int
641*0Sstevel@tonic-gate ip_addmulti_v6(const in6_addr_t *v6group, ill_t *ill, int orig_ifindex,
642*0Sstevel@tonic-gate     zoneid_t zoneid, ilg_stat_t ilgstat, mcast_record_t ilg_fmode,
643*0Sstevel@tonic-gate     slist_t *ilg_flist)
644*0Sstevel@tonic-gate {
645*0Sstevel@tonic-gate 	ilm_t	*ilm;
646*0Sstevel@tonic-gate 	int	ret;
647*0Sstevel@tonic-gate 
648*0Sstevel@tonic-gate 	ASSERT(IAM_WRITER_ILL(ill));
649*0Sstevel@tonic-gate 
650*0Sstevel@tonic-gate 	if (!IN6_IS_ADDR_MULTICAST(v6group) &&
651*0Sstevel@tonic-gate 	    !IN6_IS_ADDR_UNSPECIFIED(v6group)) {
652*0Sstevel@tonic-gate 		return (EINVAL);
653*0Sstevel@tonic-gate 	}
654*0Sstevel@tonic-gate 
655*0Sstevel@tonic-gate 	/*
656*0Sstevel@tonic-gate 	 * An ilm is uniquely identified by the tuple of (group, ill,
657*0Sstevel@tonic-gate 	 * orig_ill).  group is the multicast group address, ill is
658*0Sstevel@tonic-gate 	 * the interface on which it is currently joined, and orig_ill
659*0Sstevel@tonic-gate 	 * is the interface on which the application requested the
660*0Sstevel@tonic-gate 	 * join.  orig_ill and ill are the same unless orig_ill has
661*0Sstevel@tonic-gate 	 * failed over.
662*0Sstevel@tonic-gate 	 *
663*0Sstevel@tonic-gate 	 * Both orig_ill and ill are required, which means we may have
664*0Sstevel@tonic-gate 	 * 2 ilms on an ill for the same group, but with different
665*0Sstevel@tonic-gate 	 * orig_ills.  These must be kept separate, so that when failback
666*0Sstevel@tonic-gate 	 * occurs, the appropriate ilms are moved back to their orig_ill
667*0Sstevel@tonic-gate 	 * without disrupting memberships on the ill to which they had
668*0Sstevel@tonic-gate 	 * been moved.
669*0Sstevel@tonic-gate 	 *
670*0Sstevel@tonic-gate 	 * In order to track orig_ill, we store orig_ifindex in the
671*0Sstevel@tonic-gate 	 * ilm and ilg.
672*0Sstevel@tonic-gate 	 */
673*0Sstevel@tonic-gate 	ilm = ilm_lookup_ill_index_v6(ill, v6group, orig_ifindex, zoneid);
674*0Sstevel@tonic-gate 	if (ilm != NULL)
675*0Sstevel@tonic-gate 		return (ilm_update_add(ilm, ilgstat, ilg_flist, B_TRUE));
676*0Sstevel@tonic-gate 
677*0Sstevel@tonic-gate 	/*
678*0Sstevel@tonic-gate 	 * We need to remember where the application really wanted
679*0Sstevel@tonic-gate 	 * to join. This will be used later if we want to failback
680*0Sstevel@tonic-gate 	 * to the original interface.
681*0Sstevel@tonic-gate 	 */
682*0Sstevel@tonic-gate 	ilm = ilm_add_v6(ill->ill_ipif, v6group, ilgstat, ilg_fmode,
683*0Sstevel@tonic-gate 	    ilg_flist, orig_ifindex, zoneid);
684*0Sstevel@tonic-gate 	if (ilm == NULL)
685*0Sstevel@tonic-gate 		return (ENOMEM);
686*0Sstevel@tonic-gate 
687*0Sstevel@tonic-gate 	if (IN6_IS_ADDR_UNSPECIFIED(v6group)) {
688*0Sstevel@tonic-gate 		/*
689*0Sstevel@tonic-gate 		 * Check how many ipif's that have members in this group -
690*0Sstevel@tonic-gate 		 * if more then one we should not tell the driver to join
691*0Sstevel@tonic-gate 		 * this time
692*0Sstevel@tonic-gate 		 */
693*0Sstevel@tonic-gate 		if (ilm_numentries_v6(ill, v6group) > 1)
694*0Sstevel@tonic-gate 			return (0);
695*0Sstevel@tonic-gate 		if (ill->ill_group == NULL)
696*0Sstevel@tonic-gate 			ret = ip_join_allmulti(ill->ill_ipif);
697*0Sstevel@tonic-gate 		else
698*0Sstevel@tonic-gate 			ret = ill_nominate_mcast_rcv(ill->ill_group);
699*0Sstevel@tonic-gate 
700*0Sstevel@tonic-gate 		if (ret != 0)
701*0Sstevel@tonic-gate 			ilm_delete(ilm);
702*0Sstevel@tonic-gate 		return (ret);
703*0Sstevel@tonic-gate 	}
704*0Sstevel@tonic-gate 
705*0Sstevel@tonic-gate 	if ((ill->ill_phyint->phyint_flags & PHYI_LOOPBACK) == 0)
706*0Sstevel@tonic-gate 		mld_joingroup(ilm);
707*0Sstevel@tonic-gate 
708*0Sstevel@tonic-gate 	/*
709*0Sstevel@tonic-gate 	 * If we have more then one we should not tell the driver
710*0Sstevel@tonic-gate 	 * to join this time.
711*0Sstevel@tonic-gate 	 */
712*0Sstevel@tonic-gate 	if (ilm_numentries_v6(ill, v6group) > 1)
713*0Sstevel@tonic-gate 		return (0);
714*0Sstevel@tonic-gate 
715*0Sstevel@tonic-gate 	ret = ip_ll_addmulti_v6(ill->ill_ipif, v6group);
716*0Sstevel@tonic-gate 	if (ret != 0)
717*0Sstevel@tonic-gate 		ilm_delete(ilm);
718*0Sstevel@tonic-gate 	return (ret);
719*0Sstevel@tonic-gate }
720*0Sstevel@tonic-gate 
721*0Sstevel@tonic-gate /*
722*0Sstevel@tonic-gate  * Send a multicast request to the driver for enabling multicast reception
723*0Sstevel@tonic-gate  * for v6groupp address. The caller has already checked whether it is
724*0Sstevel@tonic-gate  * appropriate to send one or not.
725*0Sstevel@tonic-gate  */
726*0Sstevel@tonic-gate int
727*0Sstevel@tonic-gate ip_ll_send_enabmulti_req(ill_t *ill, const in6_addr_t *v6groupp)
728*0Sstevel@tonic-gate {
729*0Sstevel@tonic-gate 	mblk_t	*mp;
730*0Sstevel@tonic-gate 	uint32_t addrlen, addroff;
731*0Sstevel@tonic-gate 	char	group_buf[INET6_ADDRSTRLEN];
732*0Sstevel@tonic-gate 
733*0Sstevel@tonic-gate 	ASSERT(IAM_WRITER_ILL(ill));
734*0Sstevel@tonic-gate 
735*0Sstevel@tonic-gate 	/*
736*0Sstevel@tonic-gate 	 * Create a AR_ENTRY_SQUERY message with a dl_enabmulti_req tacked
737*0Sstevel@tonic-gate 	 * on.
738*0Sstevel@tonic-gate 	 */
739*0Sstevel@tonic-gate 	mp = ill_create_dl(ill, DL_ENABMULTI_REQ, sizeof (dl_enabmulti_req_t),
740*0Sstevel@tonic-gate 	    &addrlen, &addroff);
741*0Sstevel@tonic-gate 	if (!mp)
742*0Sstevel@tonic-gate 		return (ENOMEM);
743*0Sstevel@tonic-gate 	if (IN6_IS_ADDR_V4MAPPED(v6groupp)) {
744*0Sstevel@tonic-gate 		ipaddr_t v4group;
745*0Sstevel@tonic-gate 
746*0Sstevel@tonic-gate 		IN6_V4MAPPED_TO_IPADDR(v6groupp, v4group);
747*0Sstevel@tonic-gate 		/*
748*0Sstevel@tonic-gate 		 * NOTE!!!
749*0Sstevel@tonic-gate 		 * The "addroff" passed in here was calculated by
750*0Sstevel@tonic-gate 		 * ill_create_dl(), and will be used by ill_create_squery()
751*0Sstevel@tonic-gate 		 * to perform some twisted coding magic. It is the offset
752*0Sstevel@tonic-gate 		 * into the dl_xxx_req of the hw addr. Here, it will be
753*0Sstevel@tonic-gate 		 * added to b_wptr - b_rptr to create a magic number that
754*0Sstevel@tonic-gate 		 * is not an offset into this squery mblk.
755*0Sstevel@tonic-gate 		 * The actual hardware address will be accessed only in the
756*0Sstevel@tonic-gate 		 * dl_xxx_req, not in the squery. More importantly,
757*0Sstevel@tonic-gate 		 * that hardware address can *only* be accessed in this
758*0Sstevel@tonic-gate 		 * mblk chain by calling mi_offset_param_c(), which uses
759*0Sstevel@tonic-gate 		 * the magic number in the squery hw offset field to go
760*0Sstevel@tonic-gate 		 * to the *next* mblk (the dl_xxx_req), subtract the
761*0Sstevel@tonic-gate 		 * (b_wptr - b_rptr), and find the actual offset into
762*0Sstevel@tonic-gate 		 * the dl_xxx_req.
763*0Sstevel@tonic-gate 		 * Any method that depends on using the
764*0Sstevel@tonic-gate 		 * offset field in the dl_disabmulti_req or squery
765*0Sstevel@tonic-gate 		 * to find either hardware address will similarly fail.
766*0Sstevel@tonic-gate 		 *
767*0Sstevel@tonic-gate 		 * Look in ar_entry_squery() in arp.c to see how this offset
768*0Sstevel@tonic-gate 		 * is used.
769*0Sstevel@tonic-gate 		 */
770*0Sstevel@tonic-gate 		mp = ill_create_squery(ill, v4group, addrlen, addroff, mp);
771*0Sstevel@tonic-gate 		if (!mp)
772*0Sstevel@tonic-gate 			return (ENOMEM);
773*0Sstevel@tonic-gate 		ip1dbg(("ip_ll_send_enabmulti_req: IPv4 putnext %s on %s\n",
774*0Sstevel@tonic-gate 		    inet_ntop(AF_INET6, v6groupp, group_buf,
775*0Sstevel@tonic-gate 		    sizeof (group_buf)),
776*0Sstevel@tonic-gate 		    ill->ill_name));
777*0Sstevel@tonic-gate 		putnext(ill->ill_rq, mp);
778*0Sstevel@tonic-gate 	} else {
779*0Sstevel@tonic-gate 		ip1dbg(("ip_ll_send_enabmulti_req: IPv6 ndp_squery_mp %s on"
780*0Sstevel@tonic-gate 		    " %s\n",
781*0Sstevel@tonic-gate 		    inet_ntop(AF_INET6, v6groupp, group_buf,
782*0Sstevel@tonic-gate 		    sizeof (group_buf)),
783*0Sstevel@tonic-gate 		    ill->ill_name));
784*0Sstevel@tonic-gate 		return (ndp_mcastreq(ill, v6groupp, addrlen, addroff, mp));
785*0Sstevel@tonic-gate 	}
786*0Sstevel@tonic-gate 	return (0);
787*0Sstevel@tonic-gate }
788*0Sstevel@tonic-gate 
789*0Sstevel@tonic-gate /*
790*0Sstevel@tonic-gate  * Send a multicast request to the driver for enabling multicast
791*0Sstevel@tonic-gate  * membership for v6group if appropriate.
792*0Sstevel@tonic-gate  */
793*0Sstevel@tonic-gate static int
794*0Sstevel@tonic-gate ip_ll_addmulti_v6(ipif_t *ipif, const in6_addr_t *v6groupp)
795*0Sstevel@tonic-gate {
796*0Sstevel@tonic-gate 	ill_t	*ill = ipif->ipif_ill;
797*0Sstevel@tonic-gate 
798*0Sstevel@tonic-gate 	ASSERT(IAM_WRITER_IPIF(ipif));
799*0Sstevel@tonic-gate 
800*0Sstevel@tonic-gate 	if (ill->ill_net_type != IRE_IF_RESOLVER ||
801*0Sstevel@tonic-gate 	    ipif->ipif_flags & IPIF_POINTOPOINT) {
802*0Sstevel@tonic-gate 		ip1dbg(("ip_ll_addmulti_v6: not resolver\n"));
803*0Sstevel@tonic-gate 		return (0);	/* Must be IRE_IF_NORESOLVER */
804*0Sstevel@tonic-gate 	}
805*0Sstevel@tonic-gate 
806*0Sstevel@tonic-gate 	if (ill->ill_phyint->phyint_flags & PHYI_MULTI_BCAST) {
807*0Sstevel@tonic-gate 		ip1dbg(("ip_ll_addmulti_v6: MULTI_BCAST\n"));
808*0Sstevel@tonic-gate 		return (0);
809*0Sstevel@tonic-gate 	}
810*0Sstevel@tonic-gate 	if (ill->ill_ipif_up_count == 0) {
811*0Sstevel@tonic-gate 		/*
812*0Sstevel@tonic-gate 		 * Nobody there. All multicast addresses will be re-joined
813*0Sstevel@tonic-gate 		 * when we get the DL_BIND_ACK bringing the interface up.
814*0Sstevel@tonic-gate 		 */
815*0Sstevel@tonic-gate 		ip1dbg(("ip_ll_addmulti_v6: nobody up\n"));
816*0Sstevel@tonic-gate 		return (0);
817*0Sstevel@tonic-gate 	}
818*0Sstevel@tonic-gate 	return (ip_ll_send_enabmulti_req(ill, v6groupp));
819*0Sstevel@tonic-gate }
820*0Sstevel@tonic-gate 
821*0Sstevel@tonic-gate /*
822*0Sstevel@tonic-gate  * INADDR_ANY means all multicast addresses. This is only used
823*0Sstevel@tonic-gate  * by the multicast router.
824*0Sstevel@tonic-gate  * INADDR_ANY is stored as the IPv6 unspecifed addr.
825*0Sstevel@tonic-gate  */
826*0Sstevel@tonic-gate int
827*0Sstevel@tonic-gate ip_delmulti(ipaddr_t group, ipif_t *ipif, boolean_t no_ilg, boolean_t leaving)
828*0Sstevel@tonic-gate {
829*0Sstevel@tonic-gate 	ill_t	*ill = ipif->ipif_ill;
830*0Sstevel@tonic-gate 	ilm_t *ilm;
831*0Sstevel@tonic-gate 	in6_addr_t v6group;
832*0Sstevel@tonic-gate 	int	ret;
833*0Sstevel@tonic-gate 
834*0Sstevel@tonic-gate 	ASSERT(IAM_WRITER_IPIF(ipif));
835*0Sstevel@tonic-gate 
836*0Sstevel@tonic-gate 	if (!CLASSD(group) && group != INADDR_ANY)
837*0Sstevel@tonic-gate 		return (EINVAL);
838*0Sstevel@tonic-gate 
839*0Sstevel@tonic-gate 	/*
840*0Sstevel@tonic-gate 	 * INADDR_ANY is represented as the IPv6 unspecifed addr.
841*0Sstevel@tonic-gate 	 */
842*0Sstevel@tonic-gate 	if (group == INADDR_ANY)
843*0Sstevel@tonic-gate 		v6group = ipv6_all_zeros;
844*0Sstevel@tonic-gate 	else
845*0Sstevel@tonic-gate 		IN6_IPADDR_TO_V4MAPPED(group, &v6group);
846*0Sstevel@tonic-gate 
847*0Sstevel@tonic-gate 	/*
848*0Sstevel@tonic-gate 	 * Look for a match on the ipif.
849*0Sstevel@tonic-gate 	 * (IP_DROP_MEMBERSHIP specifies an ipif using an IP address).
850*0Sstevel@tonic-gate 	 */
851*0Sstevel@tonic-gate 	ilm = ilm_lookup_ipif(ipif, group);
852*0Sstevel@tonic-gate 	if (ilm == NULL)
853*0Sstevel@tonic-gate 		return (ENOENT);
854*0Sstevel@tonic-gate 
855*0Sstevel@tonic-gate 	/* Update counters */
856*0Sstevel@tonic-gate 	if (no_ilg)
857*0Sstevel@tonic-gate 		ilm->ilm_no_ilg_cnt--;
858*0Sstevel@tonic-gate 
859*0Sstevel@tonic-gate 	if (leaving)
860*0Sstevel@tonic-gate 		ilm->ilm_refcnt--;
861*0Sstevel@tonic-gate 
862*0Sstevel@tonic-gate 	if (ilm->ilm_refcnt > 0)
863*0Sstevel@tonic-gate 		return (ilm_update_del(ilm, B_FALSE));
864*0Sstevel@tonic-gate 
865*0Sstevel@tonic-gate 	if (group == INADDR_ANY) {
866*0Sstevel@tonic-gate 		ilm_delete(ilm);
867*0Sstevel@tonic-gate 		/*
868*0Sstevel@tonic-gate 		 * Check how many ipif's that have members in this group -
869*0Sstevel@tonic-gate 		 * if there are still some left then don't tell the driver
870*0Sstevel@tonic-gate 		 * to drop it.
871*0Sstevel@tonic-gate 		 */
872*0Sstevel@tonic-gate 		if (ilm_numentries_v6(ill, &v6group) != 0)
873*0Sstevel@tonic-gate 			return (0);
874*0Sstevel@tonic-gate 
875*0Sstevel@tonic-gate 		/*
876*0Sstevel@tonic-gate 		 * If we never joined, then don't leave.  This can happen
877*0Sstevel@tonic-gate 		 * if we're in an IPMP group, since only one ill per IPMP
878*0Sstevel@tonic-gate 		 * group receives all multicast packets.
879*0Sstevel@tonic-gate 		 */
880*0Sstevel@tonic-gate 		if (!ill->ill_join_allmulti) {
881*0Sstevel@tonic-gate 			ASSERT(ill->ill_group != NULL);
882*0Sstevel@tonic-gate 			return (0);
883*0Sstevel@tonic-gate 		}
884*0Sstevel@tonic-gate 
885*0Sstevel@tonic-gate 		ret = ip_leave_allmulti(ipif);
886*0Sstevel@tonic-gate 		if (ill->ill_group != NULL)
887*0Sstevel@tonic-gate 			(void) ill_nominate_mcast_rcv(ill->ill_group);
888*0Sstevel@tonic-gate 		return (ret);
889*0Sstevel@tonic-gate 	}
890*0Sstevel@tonic-gate 
891*0Sstevel@tonic-gate 	if ((ill->ill_phyint->phyint_flags & PHYI_LOOPBACK) == 0)
892*0Sstevel@tonic-gate 		igmp_leavegroup(ilm);
893*0Sstevel@tonic-gate 
894*0Sstevel@tonic-gate 	ilm_delete(ilm);
895*0Sstevel@tonic-gate 	/*
896*0Sstevel@tonic-gate 	 * Check how many ipif's that have members in this group -
897*0Sstevel@tonic-gate 	 * if there are still some left then don't tell the driver
898*0Sstevel@tonic-gate 	 * to drop it.
899*0Sstevel@tonic-gate 	 */
900*0Sstevel@tonic-gate 	if (ilm_numentries_v6(ill, &v6group) != 0)
901*0Sstevel@tonic-gate 		return (0);
902*0Sstevel@tonic-gate 	return (ip_ll_delmulti_v6(ipif, &v6group));
903*0Sstevel@tonic-gate }
904*0Sstevel@tonic-gate 
905*0Sstevel@tonic-gate /*
906*0Sstevel@tonic-gate  * The unspecified address means all multicast addresses.
907*0Sstevel@tonic-gate  * This is only used by the multicast router.
908*0Sstevel@tonic-gate  */
909*0Sstevel@tonic-gate int
910*0Sstevel@tonic-gate ip_delmulti_v6(const in6_addr_t *v6group, ill_t *ill, int orig_ifindex,
911*0Sstevel@tonic-gate     zoneid_t zoneid, boolean_t no_ilg, boolean_t leaving)
912*0Sstevel@tonic-gate {
913*0Sstevel@tonic-gate 	ipif_t	*ipif;
914*0Sstevel@tonic-gate 	ilm_t *ilm;
915*0Sstevel@tonic-gate 	int	ret;
916*0Sstevel@tonic-gate 
917*0Sstevel@tonic-gate 	ASSERT(IAM_WRITER_ILL(ill));
918*0Sstevel@tonic-gate 
919*0Sstevel@tonic-gate 	if (!IN6_IS_ADDR_MULTICAST(v6group) &&
920*0Sstevel@tonic-gate 	    !IN6_IS_ADDR_UNSPECIFIED(v6group))
921*0Sstevel@tonic-gate 		return (EINVAL);
922*0Sstevel@tonic-gate 
923*0Sstevel@tonic-gate 	/*
924*0Sstevel@tonic-gate 	 * Look for a match on the ill.
925*0Sstevel@tonic-gate 	 * (IPV6_LEAVE_GROUP specifies an ill using an ifindex).
926*0Sstevel@tonic-gate 	 *
927*0Sstevel@tonic-gate 	 * Similar to ip_addmulti_v6, we should always look using
928*0Sstevel@tonic-gate 	 * the orig_ifindex.
929*0Sstevel@tonic-gate 	 *
930*0Sstevel@tonic-gate 	 * 1) If orig_ifindex is different from ill's ifindex
931*0Sstevel@tonic-gate 	 *    we should have an ilm with orig_ifindex created in
932*0Sstevel@tonic-gate 	 *    ip_addmulti_v6. We should delete that here.
933*0Sstevel@tonic-gate 	 *
934*0Sstevel@tonic-gate 	 * 2) If orig_ifindex is same as ill's ifindex, we should
935*0Sstevel@tonic-gate 	 *    not delete the ilm that is temporarily here because of
936*0Sstevel@tonic-gate 	 *    a FAILOVER. Those ilms will have a ilm_orig_ifindex
937*0Sstevel@tonic-gate 	 *    different from ill's ifindex.
938*0Sstevel@tonic-gate 	 *
939*0Sstevel@tonic-gate 	 * Thus, always lookup using orig_ifindex.
940*0Sstevel@tonic-gate 	 */
941*0Sstevel@tonic-gate 	ilm = ilm_lookup_ill_index_v6(ill, v6group, orig_ifindex, zoneid);
942*0Sstevel@tonic-gate 	if (ilm == NULL)
943*0Sstevel@tonic-gate 		return (ENOENT);
944*0Sstevel@tonic-gate 
945*0Sstevel@tonic-gate 	ASSERT(ilm->ilm_ill == ill);
946*0Sstevel@tonic-gate 
947*0Sstevel@tonic-gate 	ipif = ill->ill_ipif;
948*0Sstevel@tonic-gate 
949*0Sstevel@tonic-gate 	/* Update counters */
950*0Sstevel@tonic-gate 	if (no_ilg)
951*0Sstevel@tonic-gate 		ilm->ilm_no_ilg_cnt--;
952*0Sstevel@tonic-gate 
953*0Sstevel@tonic-gate 	if (leaving)
954*0Sstevel@tonic-gate 		ilm->ilm_refcnt--;
955*0Sstevel@tonic-gate 
956*0Sstevel@tonic-gate 	if (ilm->ilm_refcnt > 0)
957*0Sstevel@tonic-gate 		return (ilm_update_del(ilm, B_TRUE));
958*0Sstevel@tonic-gate 
959*0Sstevel@tonic-gate 	if (IN6_IS_ADDR_UNSPECIFIED(v6group)) {
960*0Sstevel@tonic-gate 		ilm_delete(ilm);
961*0Sstevel@tonic-gate 		/*
962*0Sstevel@tonic-gate 		 * Check how many ipif's that have members in this group -
963*0Sstevel@tonic-gate 		 * if there are still some left then don't tell the driver
964*0Sstevel@tonic-gate 		 * to drop it.
965*0Sstevel@tonic-gate 		 */
966*0Sstevel@tonic-gate 		if (ilm_numentries_v6(ill, v6group) != 0)
967*0Sstevel@tonic-gate 			return (0);
968*0Sstevel@tonic-gate 
969*0Sstevel@tonic-gate 		/*
970*0Sstevel@tonic-gate 		 * If we never joined, then don't leave.  This can happen
971*0Sstevel@tonic-gate 		 * if we're in an IPMP group, since only one ill per IPMP
972*0Sstevel@tonic-gate 		 * group receives all multicast packets.
973*0Sstevel@tonic-gate 		 */
974*0Sstevel@tonic-gate 		if (!ill->ill_join_allmulti) {
975*0Sstevel@tonic-gate 			ASSERT(ill->ill_group != NULL);
976*0Sstevel@tonic-gate 			return (0);
977*0Sstevel@tonic-gate 		}
978*0Sstevel@tonic-gate 
979*0Sstevel@tonic-gate 		ret = ip_leave_allmulti(ipif);
980*0Sstevel@tonic-gate 		if (ill->ill_group != NULL)
981*0Sstevel@tonic-gate 			(void) ill_nominate_mcast_rcv(ill->ill_group);
982*0Sstevel@tonic-gate 		return (ret);
983*0Sstevel@tonic-gate 	}
984*0Sstevel@tonic-gate 
985*0Sstevel@tonic-gate 	if ((ill->ill_phyint->phyint_flags & PHYI_LOOPBACK) == 0)
986*0Sstevel@tonic-gate 		mld_leavegroup(ilm);
987*0Sstevel@tonic-gate 
988*0Sstevel@tonic-gate 	ilm_delete(ilm);
989*0Sstevel@tonic-gate 	/*
990*0Sstevel@tonic-gate 	 * Check how many ipif's that have members in this group -
991*0Sstevel@tonic-gate 	 * if there are still some left then don't tell the driver
992*0Sstevel@tonic-gate 	 * to drop it.
993*0Sstevel@tonic-gate 	 */
994*0Sstevel@tonic-gate 	if (ilm_numentries_v6(ill, v6group) != 0)
995*0Sstevel@tonic-gate 		return (0);
996*0Sstevel@tonic-gate 	return (ip_ll_delmulti_v6(ipif, v6group));
997*0Sstevel@tonic-gate }
998*0Sstevel@tonic-gate 
999*0Sstevel@tonic-gate /*
1000*0Sstevel@tonic-gate  * Send a multicast request to the driver for disabling multicast reception
1001*0Sstevel@tonic-gate  * for v6groupp address. The caller has already checked whether it is
1002*0Sstevel@tonic-gate  * appropriate to send one or not.
1003*0Sstevel@tonic-gate  */
1004*0Sstevel@tonic-gate int
1005*0Sstevel@tonic-gate ip_ll_send_disabmulti_req(ill_t *ill, const in6_addr_t *v6groupp)
1006*0Sstevel@tonic-gate {
1007*0Sstevel@tonic-gate 	mblk_t	*mp;
1008*0Sstevel@tonic-gate 	char	group_buf[INET6_ADDRSTRLEN];
1009*0Sstevel@tonic-gate 	uint32_t	addrlen, addroff;
1010*0Sstevel@tonic-gate 
1011*0Sstevel@tonic-gate 	ASSERT(IAM_WRITER_ILL(ill));
1012*0Sstevel@tonic-gate 	/*
1013*0Sstevel@tonic-gate 	 * Create a AR_ENTRY_SQUERY message with a dl_disabmulti_req tacked
1014*0Sstevel@tonic-gate 	 * on.
1015*0Sstevel@tonic-gate 	 */
1016*0Sstevel@tonic-gate 	mp = ill_create_dl(ill, DL_DISABMULTI_REQ,
1017*0Sstevel@tonic-gate 	    sizeof (dl_disabmulti_req_t), &addrlen, &addroff);
1018*0Sstevel@tonic-gate 
1019*0Sstevel@tonic-gate 	if (!mp)
1020*0Sstevel@tonic-gate 		return (ENOMEM);
1021*0Sstevel@tonic-gate 
1022*0Sstevel@tonic-gate 	if (IN6_IS_ADDR_V4MAPPED(v6groupp)) {
1023*0Sstevel@tonic-gate 		ipaddr_t v4group;
1024*0Sstevel@tonic-gate 
1025*0Sstevel@tonic-gate 		IN6_V4MAPPED_TO_IPADDR(v6groupp, v4group);
1026*0Sstevel@tonic-gate 		/*
1027*0Sstevel@tonic-gate 		 * NOTE!!!
1028*0Sstevel@tonic-gate 		 * The "addroff" passed in here was calculated by
1029*0Sstevel@tonic-gate 		 * ill_create_dl(), and will be used by ill_create_squery()
1030*0Sstevel@tonic-gate 		 * to perform some twisted coding magic. It is the offset
1031*0Sstevel@tonic-gate 		 * into the dl_xxx_req of the hw addr. Here, it will be
1032*0Sstevel@tonic-gate 		 * added to b_wptr - b_rptr to create a magic number that
1033*0Sstevel@tonic-gate 		 * is not an offset into this mblk.
1034*0Sstevel@tonic-gate 		 *
1035*0Sstevel@tonic-gate 		 * Please see the comment in ip_ll_send)enabmulti_req()
1036*0Sstevel@tonic-gate 		 * for a complete explanation.
1037*0Sstevel@tonic-gate 		 *
1038*0Sstevel@tonic-gate 		 * Look in ar_entry_squery() in arp.c to see how this offset
1039*0Sstevel@tonic-gate 		 * is used.
1040*0Sstevel@tonic-gate 		 */
1041*0Sstevel@tonic-gate 		mp = ill_create_squery(ill, v4group, addrlen, addroff, mp);
1042*0Sstevel@tonic-gate 		if (!mp)
1043*0Sstevel@tonic-gate 			return (ENOMEM);
1044*0Sstevel@tonic-gate 		ip1dbg(("ip_ll_send_disabmulti_req: IPv4 putnext %s on %s\n",
1045*0Sstevel@tonic-gate 		    inet_ntop(AF_INET6, v6groupp, group_buf,
1046*0Sstevel@tonic-gate 		    sizeof (group_buf)),
1047*0Sstevel@tonic-gate 		    ill->ill_name));
1048*0Sstevel@tonic-gate 		putnext(ill->ill_rq, mp);
1049*0Sstevel@tonic-gate 	} else {
1050*0Sstevel@tonic-gate 		ip1dbg(("ip_ll_send_disabmulti_req: IPv6 ndp_squery_mp %s on"
1051*0Sstevel@tonic-gate 		    " %s\n",
1052*0Sstevel@tonic-gate 		    inet_ntop(AF_INET6, v6groupp, group_buf,
1053*0Sstevel@tonic-gate 		    sizeof (group_buf)),
1054*0Sstevel@tonic-gate 		    ill->ill_name));
1055*0Sstevel@tonic-gate 		return (ndp_mcastreq(ill, v6groupp, addrlen, addroff, mp));
1056*0Sstevel@tonic-gate 	}
1057*0Sstevel@tonic-gate 	return (0);
1058*0Sstevel@tonic-gate }
1059*0Sstevel@tonic-gate 
1060*0Sstevel@tonic-gate /*
1061*0Sstevel@tonic-gate  * Send a multicast request to the driver for disabling multicast
1062*0Sstevel@tonic-gate  * membership for v6group if appropriate.
1063*0Sstevel@tonic-gate  */
1064*0Sstevel@tonic-gate static int
1065*0Sstevel@tonic-gate ip_ll_delmulti_v6(ipif_t *ipif, const in6_addr_t *v6group)
1066*0Sstevel@tonic-gate {
1067*0Sstevel@tonic-gate 	ill_t	*ill = ipif->ipif_ill;
1068*0Sstevel@tonic-gate 
1069*0Sstevel@tonic-gate 	ASSERT(IAM_WRITER_IPIF(ipif));
1070*0Sstevel@tonic-gate 
1071*0Sstevel@tonic-gate 	if (ill->ill_net_type != IRE_IF_RESOLVER ||
1072*0Sstevel@tonic-gate 	    ipif->ipif_flags & IPIF_POINTOPOINT) {
1073*0Sstevel@tonic-gate 		return (0);	/* Must be IRE_IF_NORESOLVER */
1074*0Sstevel@tonic-gate 	}
1075*0Sstevel@tonic-gate 	if (ill->ill_phyint->phyint_flags & PHYI_MULTI_BCAST) {
1076*0Sstevel@tonic-gate 		ip1dbg(("ip_ll_delmulti_v6: MULTI_BCAST\n"));
1077*0Sstevel@tonic-gate 		return (0);
1078*0Sstevel@tonic-gate 	}
1079*0Sstevel@tonic-gate 	if (ill->ill_ipif_up_count == 0) {
1080*0Sstevel@tonic-gate 		/*
1081*0Sstevel@tonic-gate 		 * Nobody there. All multicast addresses will be re-joined
1082*0Sstevel@tonic-gate 		 * when we get the DL_BIND_ACK bringing the interface up.
1083*0Sstevel@tonic-gate 		 */
1084*0Sstevel@tonic-gate 		ip1dbg(("ip_ll_delmulti_v6: nobody up\n"));
1085*0Sstevel@tonic-gate 		return (0);
1086*0Sstevel@tonic-gate 	}
1087*0Sstevel@tonic-gate 	return (ip_ll_send_disabmulti_req(ill, v6group));
1088*0Sstevel@tonic-gate }
1089*0Sstevel@tonic-gate 
1090*0Sstevel@tonic-gate /*
1091*0Sstevel@tonic-gate  * Make the driver pass up all multicast packets
1092*0Sstevel@tonic-gate  *
1093*0Sstevel@tonic-gate  * With ill groups, the caller makes sure that there is only
1094*0Sstevel@tonic-gate  * one ill joining the allmulti group.
1095*0Sstevel@tonic-gate  */
1096*0Sstevel@tonic-gate int
1097*0Sstevel@tonic-gate ip_join_allmulti(ipif_t *ipif)
1098*0Sstevel@tonic-gate {
1099*0Sstevel@tonic-gate 	ill_t	*ill = ipif->ipif_ill;
1100*0Sstevel@tonic-gate 	mblk_t	*mp;
1101*0Sstevel@tonic-gate 	uint32_t	addrlen, addroff;
1102*0Sstevel@tonic-gate 
1103*0Sstevel@tonic-gate 	ASSERT(IAM_WRITER_IPIF(ipif));
1104*0Sstevel@tonic-gate 
1105*0Sstevel@tonic-gate 	if (ill->ill_ipif_up_count == 0) {
1106*0Sstevel@tonic-gate 		/*
1107*0Sstevel@tonic-gate 		 * Nobody there. All multicast addresses will be re-joined
1108*0Sstevel@tonic-gate 		 * when we get the DL_BIND_ACK bringing the interface up.
1109*0Sstevel@tonic-gate 		 */
1110*0Sstevel@tonic-gate 		return (0);
1111*0Sstevel@tonic-gate 	}
1112*0Sstevel@tonic-gate 
1113*0Sstevel@tonic-gate 	ASSERT(!ill->ill_join_allmulti);
1114*0Sstevel@tonic-gate 
1115*0Sstevel@tonic-gate 	/*
1116*0Sstevel@tonic-gate 	 * Create a DL_PROMISCON_REQ message and send it directly to
1117*0Sstevel@tonic-gate 	 * the DLPI provider.  We don't need to do this for certain
1118*0Sstevel@tonic-gate 	 * media types for which we never need to turn promiscuous
1119*0Sstevel@tonic-gate 	 * mode on.
1120*0Sstevel@tonic-gate 	 */
1121*0Sstevel@tonic-gate 	if ((ill->ill_net_type == IRE_IF_RESOLVER) &&
1122*0Sstevel@tonic-gate 	    !(ill->ill_phyint->phyint_flags & PHYI_MULTI_BCAST)) {
1123*0Sstevel@tonic-gate 		mp = ill_create_dl(ill, DL_PROMISCON_REQ,
1124*0Sstevel@tonic-gate 		    sizeof (dl_promiscon_req_t), &addrlen, &addroff);
1125*0Sstevel@tonic-gate 		if (mp == NULL)
1126*0Sstevel@tonic-gate 			return (ENOMEM);
1127*0Sstevel@tonic-gate 		putnext(ill->ill_wq, mp);
1128*0Sstevel@tonic-gate 	}
1129*0Sstevel@tonic-gate 
1130*0Sstevel@tonic-gate 	mutex_enter(&ill->ill_lock);
1131*0Sstevel@tonic-gate 	ill->ill_join_allmulti = B_TRUE;
1132*0Sstevel@tonic-gate 	mutex_exit(&ill->ill_lock);
1133*0Sstevel@tonic-gate 	return (0);
1134*0Sstevel@tonic-gate }
1135*0Sstevel@tonic-gate 
1136*0Sstevel@tonic-gate /*
1137*0Sstevel@tonic-gate  * Make the driver stop passing up all multicast packets
1138*0Sstevel@tonic-gate  *
1139*0Sstevel@tonic-gate  * With ill groups, we need to nominate some other ill as
1140*0Sstevel@tonic-gate  * this ipif->ipif_ill is leaving the group.
1141*0Sstevel@tonic-gate  */
1142*0Sstevel@tonic-gate int
1143*0Sstevel@tonic-gate ip_leave_allmulti(ipif_t *ipif)
1144*0Sstevel@tonic-gate {
1145*0Sstevel@tonic-gate 	ill_t	*ill = ipif->ipif_ill;
1146*0Sstevel@tonic-gate 	mblk_t	*mp;
1147*0Sstevel@tonic-gate 	uint32_t	addrlen, addroff;
1148*0Sstevel@tonic-gate 
1149*0Sstevel@tonic-gate 	ASSERT(IAM_WRITER_IPIF(ipif));
1150*0Sstevel@tonic-gate 
1151*0Sstevel@tonic-gate 	if (ill->ill_ipif_up_count == 0) {
1152*0Sstevel@tonic-gate 		/*
1153*0Sstevel@tonic-gate 		 * Nobody there. All multicast addresses will be re-joined
1154*0Sstevel@tonic-gate 		 * when we get the DL_BIND_ACK bringing the interface up.
1155*0Sstevel@tonic-gate 		 */
1156*0Sstevel@tonic-gate 		return (0);
1157*0Sstevel@tonic-gate 	}
1158*0Sstevel@tonic-gate 
1159*0Sstevel@tonic-gate 	ASSERT(ill->ill_join_allmulti);
1160*0Sstevel@tonic-gate 
1161*0Sstevel@tonic-gate 	/*
1162*0Sstevel@tonic-gate 	 * Create a DL_PROMISCOFF_REQ message and send it directly to
1163*0Sstevel@tonic-gate 	 * the DLPI provider.  We don't need to do this for certain
1164*0Sstevel@tonic-gate 	 * media types for which we never need to turn promiscuous
1165*0Sstevel@tonic-gate 	 * mode on.
1166*0Sstevel@tonic-gate 	 */
1167*0Sstevel@tonic-gate 	if ((ill->ill_net_type == IRE_IF_RESOLVER) &&
1168*0Sstevel@tonic-gate 	    !(ill->ill_phyint->phyint_flags & PHYI_MULTI_BCAST)) {
1169*0Sstevel@tonic-gate 		mp = ill_create_dl(ill, DL_PROMISCOFF_REQ,
1170*0Sstevel@tonic-gate 		    sizeof (dl_promiscoff_req_t), &addrlen, &addroff);
1171*0Sstevel@tonic-gate 		if (mp == NULL)
1172*0Sstevel@tonic-gate 			return (ENOMEM);
1173*0Sstevel@tonic-gate 		putnext(ill->ill_wq, mp);
1174*0Sstevel@tonic-gate 	}
1175*0Sstevel@tonic-gate 
1176*0Sstevel@tonic-gate 	mutex_enter(&ill->ill_lock);
1177*0Sstevel@tonic-gate 	ill->ill_join_allmulti = B_FALSE;
1178*0Sstevel@tonic-gate 	mutex_exit(&ill->ill_lock);
1179*0Sstevel@tonic-gate 	return (0);
1180*0Sstevel@tonic-gate }
1181*0Sstevel@tonic-gate 
1182*0Sstevel@tonic-gate /*
1183*0Sstevel@tonic-gate  * Copy mp_orig and pass it in as a local message.
1184*0Sstevel@tonic-gate  */
1185*0Sstevel@tonic-gate void
1186*0Sstevel@tonic-gate ip_multicast_loopback(queue_t *q, ill_t *ill, mblk_t *mp_orig, int fanout_flags,
1187*0Sstevel@tonic-gate     zoneid_t zoneid)
1188*0Sstevel@tonic-gate {
1189*0Sstevel@tonic-gate 	mblk_t		*mp;
1190*0Sstevel@tonic-gate 	mblk_t		*ipsec_mp;
1191*0Sstevel@tonic-gate 
1192*0Sstevel@tonic-gate 	/* TODO this could use dup'ed messages except for the IP header. */
1193*0Sstevel@tonic-gate 	mp = ip_copymsg(mp_orig);
1194*0Sstevel@tonic-gate 	if (mp == NULL)
1195*0Sstevel@tonic-gate 		return;
1196*0Sstevel@tonic-gate 	if (mp->b_datap->db_type == M_CTL) {
1197*0Sstevel@tonic-gate 		ipsec_mp = mp;
1198*0Sstevel@tonic-gate 		mp = mp->b_cont;
1199*0Sstevel@tonic-gate 	} else {
1200*0Sstevel@tonic-gate 		ipsec_mp = mp;
1201*0Sstevel@tonic-gate 	}
1202*0Sstevel@tonic-gate 	ip_wput_local(q, ill, (ipha_t *)mp->b_rptr, ipsec_mp, NULL,
1203*0Sstevel@tonic-gate 	    fanout_flags, zoneid);
1204*0Sstevel@tonic-gate }
1205*0Sstevel@tonic-gate 
1206*0Sstevel@tonic-gate static area_t	ip_aresq_template = {
1207*0Sstevel@tonic-gate 	AR_ENTRY_SQUERY,		/* cmd */
1208*0Sstevel@tonic-gate 	sizeof (area_t)+IP_ADDR_LEN,	/* name offset */
1209*0Sstevel@tonic-gate 	sizeof (area_t),	/* name len (filled by ill_arp_alloc) */
1210*0Sstevel@tonic-gate 	IP_ARP_PROTO_TYPE,		/* protocol, from arps perspective */
1211*0Sstevel@tonic-gate 	sizeof (area_t),			/* proto addr offset */
1212*0Sstevel@tonic-gate 	IP_ADDR_LEN,			/* proto addr_length */
1213*0Sstevel@tonic-gate 	0,				/* proto mask offset */
1214*0Sstevel@tonic-gate 	/* Rest is initialized when used */
1215*0Sstevel@tonic-gate 	0,				/* flags */
1216*0Sstevel@tonic-gate 	0,				/* hw addr offset */
1217*0Sstevel@tonic-gate 	0,				/* hw addr length */
1218*0Sstevel@tonic-gate };
1219*0Sstevel@tonic-gate 
1220*0Sstevel@tonic-gate static mblk_t *
1221*0Sstevel@tonic-gate ill_create_squery(ill_t *ill, ipaddr_t ipaddr, uint32_t addrlen,
1222*0Sstevel@tonic-gate     uint32_t addroff, mblk_t *mp_tail)
1223*0Sstevel@tonic-gate {
1224*0Sstevel@tonic-gate 	mblk_t	*mp;
1225*0Sstevel@tonic-gate 	area_t	*area;
1226*0Sstevel@tonic-gate 
1227*0Sstevel@tonic-gate 	mp = ill_arp_alloc(ill, (uchar_t *)&ip_aresq_template,
1228*0Sstevel@tonic-gate 				(caddr_t)&ipaddr);
1229*0Sstevel@tonic-gate 	if (!mp) {
1230*0Sstevel@tonic-gate 		freemsg(mp_tail);
1231*0Sstevel@tonic-gate 		return (NULL);
1232*0Sstevel@tonic-gate 	}
1233*0Sstevel@tonic-gate 	area = (area_t *)mp->b_rptr;
1234*0Sstevel@tonic-gate 	area->area_hw_addr_length = addrlen;
1235*0Sstevel@tonic-gate 	area->area_hw_addr_offset = mp->b_wptr - mp->b_rptr + addroff;
1236*0Sstevel@tonic-gate 	/*
1237*0Sstevel@tonic-gate 	 * NOTE!
1238*0Sstevel@tonic-gate 	 *
1239*0Sstevel@tonic-gate 	 * The area_hw_addr_offset, as can be seen, does not hold the
1240*0Sstevel@tonic-gate 	 * actual hardware address offset. Rather, it holds the offset
1241*0Sstevel@tonic-gate 	 * to the hw addr in the dl_xxx_req in mp_tail, modified by
1242*0Sstevel@tonic-gate 	 * adding (mp->b_wptr - mp->b_rptr). This allows the function
1243*0Sstevel@tonic-gate 	 * mi_offset_paramc() to find the hardware address in the
1244*0Sstevel@tonic-gate 	 * *second* mblk (dl_xxx_req), not this mblk.
1245*0Sstevel@tonic-gate 	 *
1246*0Sstevel@tonic-gate 	 * Using mi_offset_paramc() is thus the *only* way to access
1247*0Sstevel@tonic-gate 	 * the dl_xxx_hw address.
1248*0Sstevel@tonic-gate 	 *
1249*0Sstevel@tonic-gate 	 * The squery hw address should *not* be accessed.
1250*0Sstevel@tonic-gate 	 *
1251*0Sstevel@tonic-gate 	 * See ar_entry_squery() in arp.c for an example of how all this works.
1252*0Sstevel@tonic-gate 	 */
1253*0Sstevel@tonic-gate 
1254*0Sstevel@tonic-gate 	mp->b_cont = mp_tail;
1255*0Sstevel@tonic-gate 	return (mp);
1256*0Sstevel@tonic-gate }
1257*0Sstevel@tonic-gate 
1258*0Sstevel@tonic-gate /*
1259*0Sstevel@tonic-gate  * Create a dlpi message with room for phys+sap. When we come back in
1260*0Sstevel@tonic-gate  * ip_wput_ctl() we will strip the sap for those primitives which
1261*0Sstevel@tonic-gate  * only need a physical address.
1262*0Sstevel@tonic-gate  */
1263*0Sstevel@tonic-gate static mblk_t *
1264*0Sstevel@tonic-gate ill_create_dl(ill_t *ill, uint32_t dl_primitive, uint32_t length,
1265*0Sstevel@tonic-gate     uint32_t *addr_lenp, uint32_t *addr_offp)
1266*0Sstevel@tonic-gate {
1267*0Sstevel@tonic-gate 	mblk_t	*mp;
1268*0Sstevel@tonic-gate 	uint32_t	hw_addr_length;
1269*0Sstevel@tonic-gate 	char		*cp;
1270*0Sstevel@tonic-gate 	uint32_t	offset;
1271*0Sstevel@tonic-gate 	uint32_t 	size;
1272*0Sstevel@tonic-gate 
1273*0Sstevel@tonic-gate 	*addr_lenp = *addr_offp = 0;
1274*0Sstevel@tonic-gate 
1275*0Sstevel@tonic-gate 	hw_addr_length = ill->ill_phys_addr_length;
1276*0Sstevel@tonic-gate 	if (!hw_addr_length) {
1277*0Sstevel@tonic-gate 		ip0dbg(("ip_create_dl: hw addr length = 0\n"));
1278*0Sstevel@tonic-gate 		return (NULL);
1279*0Sstevel@tonic-gate 	}
1280*0Sstevel@tonic-gate 
1281*0Sstevel@tonic-gate 	size = length;
1282*0Sstevel@tonic-gate 	switch (dl_primitive) {
1283*0Sstevel@tonic-gate 	case DL_ENABMULTI_REQ:
1284*0Sstevel@tonic-gate 	case DL_DISABMULTI_REQ:
1285*0Sstevel@tonic-gate 		size += hw_addr_length;
1286*0Sstevel@tonic-gate 		break;
1287*0Sstevel@tonic-gate 	case DL_PROMISCON_REQ:
1288*0Sstevel@tonic-gate 	case DL_PROMISCOFF_REQ:
1289*0Sstevel@tonic-gate 		break;
1290*0Sstevel@tonic-gate 	default:
1291*0Sstevel@tonic-gate 		return (NULL);
1292*0Sstevel@tonic-gate 	}
1293*0Sstevel@tonic-gate 	mp = allocb(size, BPRI_HI);
1294*0Sstevel@tonic-gate 	if (!mp)
1295*0Sstevel@tonic-gate 		return (NULL);
1296*0Sstevel@tonic-gate 	mp->b_wptr += size;
1297*0Sstevel@tonic-gate 	mp->b_datap->db_type = M_PROTO;
1298*0Sstevel@tonic-gate 
1299*0Sstevel@tonic-gate 	cp = (char *)mp->b_rptr;
1300*0Sstevel@tonic-gate 	offset = length;
1301*0Sstevel@tonic-gate 
1302*0Sstevel@tonic-gate 	switch (dl_primitive) {
1303*0Sstevel@tonic-gate 	case DL_ENABMULTI_REQ: {
1304*0Sstevel@tonic-gate 		dl_enabmulti_req_t *dl = (dl_enabmulti_req_t *)cp;
1305*0Sstevel@tonic-gate 
1306*0Sstevel@tonic-gate 		dl->dl_primitive = dl_primitive;
1307*0Sstevel@tonic-gate 		dl->dl_addr_offset = offset;
1308*0Sstevel@tonic-gate 		*addr_lenp = dl->dl_addr_length = hw_addr_length;
1309*0Sstevel@tonic-gate 		*addr_offp = offset;
1310*0Sstevel@tonic-gate 		break;
1311*0Sstevel@tonic-gate 	}
1312*0Sstevel@tonic-gate 	case DL_DISABMULTI_REQ: {
1313*0Sstevel@tonic-gate 		dl_disabmulti_req_t *dl = (dl_disabmulti_req_t *)cp;
1314*0Sstevel@tonic-gate 
1315*0Sstevel@tonic-gate 		dl->dl_primitive = dl_primitive;
1316*0Sstevel@tonic-gate 		dl->dl_addr_offset = offset;
1317*0Sstevel@tonic-gate 		*addr_lenp = dl->dl_addr_length = hw_addr_length;
1318*0Sstevel@tonic-gate 		*addr_offp = offset;
1319*0Sstevel@tonic-gate 		break;
1320*0Sstevel@tonic-gate 	}
1321*0Sstevel@tonic-gate 	case DL_PROMISCON_REQ:
1322*0Sstevel@tonic-gate 	case DL_PROMISCOFF_REQ: {
1323*0Sstevel@tonic-gate 		dl_promiscon_req_t *dl = (dl_promiscon_req_t *)cp;
1324*0Sstevel@tonic-gate 
1325*0Sstevel@tonic-gate 		dl->dl_primitive = dl_primitive;
1326*0Sstevel@tonic-gate 		dl->dl_level = DL_PROMISC_MULTI;
1327*0Sstevel@tonic-gate 		break;
1328*0Sstevel@tonic-gate 	}
1329*0Sstevel@tonic-gate 	}
1330*0Sstevel@tonic-gate 	ip1dbg(("ill_create_dl: addr_len %d, addr_off %d\n",
1331*0Sstevel@tonic-gate 		*addr_lenp, *addr_offp));
1332*0Sstevel@tonic-gate 	return (mp);
1333*0Sstevel@tonic-gate }
1334*0Sstevel@tonic-gate 
1335*0Sstevel@tonic-gate void
1336*0Sstevel@tonic-gate ip_wput_ctl(queue_t *q, mblk_t *mp_orig)
1337*0Sstevel@tonic-gate {
1338*0Sstevel@tonic-gate 	ill_t	*ill = (ill_t *)q->q_ptr;
1339*0Sstevel@tonic-gate 	mblk_t	*mp = mp_orig;
1340*0Sstevel@tonic-gate 	area_t	*area;
1341*0Sstevel@tonic-gate 
1342*0Sstevel@tonic-gate 	/* Check that we have a AR_ENTRY_SQUERY with a tacked on mblk */
1343*0Sstevel@tonic-gate 	if ((mp->b_wptr - mp->b_rptr) < sizeof (area_t) ||
1344*0Sstevel@tonic-gate 	    mp->b_cont == NULL) {
1345*0Sstevel@tonic-gate 		putnext(q, mp);
1346*0Sstevel@tonic-gate 		return;
1347*0Sstevel@tonic-gate 	}
1348*0Sstevel@tonic-gate 	area = (area_t *)mp->b_rptr;
1349*0Sstevel@tonic-gate 	if (area->area_cmd != AR_ENTRY_SQUERY) {
1350*0Sstevel@tonic-gate 		putnext(q, mp);
1351*0Sstevel@tonic-gate 		return;
1352*0Sstevel@tonic-gate 	}
1353*0Sstevel@tonic-gate 	mp = mp->b_cont;
1354*0Sstevel@tonic-gate 	/*
1355*0Sstevel@tonic-gate 	 * Update dl_addr_length and dl_addr_offset for primitives that
1356*0Sstevel@tonic-gate 	 * have physical addresses as opposed to full saps
1357*0Sstevel@tonic-gate 	 */
1358*0Sstevel@tonic-gate 	switch (((union DL_primitives *)mp->b_rptr)->dl_primitive) {
1359*0Sstevel@tonic-gate 	case DL_ENABMULTI_REQ:
1360*0Sstevel@tonic-gate 		/* Track the state if this is the first enabmulti */
1361*0Sstevel@tonic-gate 		if (ill->ill_dlpi_multicast_state == IDMS_UNKNOWN)
1362*0Sstevel@tonic-gate 			ill->ill_dlpi_multicast_state = IDMS_INPROGRESS;
1363*0Sstevel@tonic-gate 		ip1dbg(("ip_wput_ctl: ENABMULTI\n"));
1364*0Sstevel@tonic-gate 		break;
1365*0Sstevel@tonic-gate 	case DL_DISABMULTI_REQ:
1366*0Sstevel@tonic-gate 		ip1dbg(("ip_wput_ctl: DISABMULTI\n"));
1367*0Sstevel@tonic-gate 		break;
1368*0Sstevel@tonic-gate 	default:
1369*0Sstevel@tonic-gate 		ip1dbg(("ip_wput_ctl: default\n"));
1370*0Sstevel@tonic-gate 		break;
1371*0Sstevel@tonic-gate 	}
1372*0Sstevel@tonic-gate 	freeb(mp_orig);
1373*0Sstevel@tonic-gate 	putnext(q, mp);
1374*0Sstevel@tonic-gate }
1375*0Sstevel@tonic-gate 
1376*0Sstevel@tonic-gate /*
1377*0Sstevel@tonic-gate  * Rejoin any groups which have been explicitly joined by the application (we
1378*0Sstevel@tonic-gate  * left all explicitly joined groups as part of ill_leave_multicast() prior to
1379*0Sstevel@tonic-gate  * bringing the interface down).  Note that because groups can be joined and
1380*0Sstevel@tonic-gate  * left while an interface is down, this may not be the same set of groups
1381*0Sstevel@tonic-gate  * that we left in ill_leave_multicast().
1382*0Sstevel@tonic-gate  */
1383*0Sstevel@tonic-gate void
1384*0Sstevel@tonic-gate ill_recover_multicast(ill_t *ill)
1385*0Sstevel@tonic-gate {
1386*0Sstevel@tonic-gate 	ilm_t	*ilm;
1387*0Sstevel@tonic-gate 	char    addrbuf[INET6_ADDRSTRLEN];
1388*0Sstevel@tonic-gate 
1389*0Sstevel@tonic-gate 	ASSERT(IAM_WRITER_ILL(ill));
1390*0Sstevel@tonic-gate 
1391*0Sstevel@tonic-gate 	for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
1392*0Sstevel@tonic-gate 		/*
1393*0Sstevel@tonic-gate 		 * Check how many ipif's that have members in this group -
1394*0Sstevel@tonic-gate 		 * if more then one we make sure that this entry is first
1395*0Sstevel@tonic-gate 		 * in the list.
1396*0Sstevel@tonic-gate 		 */
1397*0Sstevel@tonic-gate 		if (ilm_numentries_v6(ill, &ilm->ilm_v6addr) > 1 &&
1398*0Sstevel@tonic-gate 		    ilm_lookup_ill_v6(ill, &ilm->ilm_v6addr, ALL_ZONES) != ilm)
1399*0Sstevel@tonic-gate 			continue;
1400*0Sstevel@tonic-gate 		ip1dbg(("ill_recover_multicast: %s\n",
1401*0Sstevel@tonic-gate 		    inet_ntop(AF_INET6, &ilm->ilm_v6addr, addrbuf,
1402*0Sstevel@tonic-gate 		    sizeof (addrbuf))));
1403*0Sstevel@tonic-gate 		if (IN6_IS_ADDR_UNSPECIFIED(&ilm->ilm_v6addr)) {
1404*0Sstevel@tonic-gate 			if (ill->ill_group == NULL) {
1405*0Sstevel@tonic-gate 				(void) ip_join_allmulti(ill->ill_ipif);
1406*0Sstevel@tonic-gate 			} else {
1407*0Sstevel@tonic-gate 				/*
1408*0Sstevel@tonic-gate 				 * We don't want to join on this ill,
1409*0Sstevel@tonic-gate 				 * if somebody else in the group has
1410*0Sstevel@tonic-gate 				 * already been nominated.
1411*0Sstevel@tonic-gate 				 */
1412*0Sstevel@tonic-gate 				(void) ill_nominate_mcast_rcv(ill->ill_group);
1413*0Sstevel@tonic-gate 			}
1414*0Sstevel@tonic-gate 		} else {
1415*0Sstevel@tonic-gate 			(void) ip_ll_addmulti_v6(ill->ill_ipif,
1416*0Sstevel@tonic-gate 			    &ilm->ilm_v6addr);
1417*0Sstevel@tonic-gate 		}
1418*0Sstevel@tonic-gate 	}
1419*0Sstevel@tonic-gate }
1420*0Sstevel@tonic-gate 
1421*0Sstevel@tonic-gate /*
1422*0Sstevel@tonic-gate  * The opposite of ill_recover_multicast() -- leaves all multicast groups
1423*0Sstevel@tonic-gate  * that were explicitly joined.  Note that both these functions could be
1424*0Sstevel@tonic-gate  * disposed of if we enhanced ARP to allow us to handle DL_DISABMULTI_REQ
1425*0Sstevel@tonic-gate  * and DL_ENABMULTI_REQ messages when an interface is down.
1426*0Sstevel@tonic-gate  */
1427*0Sstevel@tonic-gate void
1428*0Sstevel@tonic-gate ill_leave_multicast(ill_t *ill)
1429*0Sstevel@tonic-gate {
1430*0Sstevel@tonic-gate 	ilm_t	*ilm;
1431*0Sstevel@tonic-gate 	char    addrbuf[INET6_ADDRSTRLEN];
1432*0Sstevel@tonic-gate 
1433*0Sstevel@tonic-gate 	ASSERT(IAM_WRITER_ILL(ill));
1434*0Sstevel@tonic-gate 
1435*0Sstevel@tonic-gate 	for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
1436*0Sstevel@tonic-gate 		/*
1437*0Sstevel@tonic-gate 		 * Check how many ipif's that have members in this group -
1438*0Sstevel@tonic-gate 		 * if more then one we make sure that this entry is first
1439*0Sstevel@tonic-gate 		 * in the list.
1440*0Sstevel@tonic-gate 		 */
1441*0Sstevel@tonic-gate 		if (ilm_numentries_v6(ill, &ilm->ilm_v6addr) > 1 &&
1442*0Sstevel@tonic-gate 		    ilm_lookup_ill_v6(ill, &ilm->ilm_v6addr, ALL_ZONES) != ilm)
1443*0Sstevel@tonic-gate 			continue;
1444*0Sstevel@tonic-gate 		ip1dbg(("ill_leave_multicast: %s\n",
1445*0Sstevel@tonic-gate 		    inet_ntop(AF_INET6, &ilm->ilm_v6addr, addrbuf,
1446*0Sstevel@tonic-gate 		    sizeof (addrbuf))));
1447*0Sstevel@tonic-gate 		if (IN6_IS_ADDR_UNSPECIFIED(&ilm->ilm_v6addr)) {
1448*0Sstevel@tonic-gate 			(void) ip_leave_allmulti(ill->ill_ipif);
1449*0Sstevel@tonic-gate 			/*
1450*0Sstevel@tonic-gate 			 * If we were part of an IPMP group, then
1451*0Sstevel@tonic-gate 			 * ill_handoff_responsibility() has already
1452*0Sstevel@tonic-gate 			 * nominated a new member (so we don't).
1453*0Sstevel@tonic-gate 			 */
1454*0Sstevel@tonic-gate 			ASSERT(ill->ill_group == NULL);
1455*0Sstevel@tonic-gate 		} else {
1456*0Sstevel@tonic-gate 			(void) ip_ll_send_disabmulti_req(ill, &ilm->ilm_v6addr);
1457*0Sstevel@tonic-gate 		}
1458*0Sstevel@tonic-gate 	}
1459*0Sstevel@tonic-gate }
1460*0Sstevel@tonic-gate 
1461*0Sstevel@tonic-gate /*
1462*0Sstevel@tonic-gate  * Find an ilm for matching the ill and which has the source in its
1463*0Sstevel@tonic-gate  * INCLUDE list or does not have it in its EXCLUDE list
1464*0Sstevel@tonic-gate  */
1465*0Sstevel@tonic-gate ilm_t *
1466*0Sstevel@tonic-gate ilm_lookup_ill_withsrc(ill_t *ill, ipaddr_t group, ipaddr_t src)
1467*0Sstevel@tonic-gate {
1468*0Sstevel@tonic-gate 	in6_addr_t	v6group, v6src;
1469*0Sstevel@tonic-gate 
1470*0Sstevel@tonic-gate 	/*
1471*0Sstevel@tonic-gate 	 * INADDR_ANY is represented as the IPv6 unspecified addr.
1472*0Sstevel@tonic-gate 	 */
1473*0Sstevel@tonic-gate 	if (group == INADDR_ANY)
1474*0Sstevel@tonic-gate 		v6group = ipv6_all_zeros;
1475*0Sstevel@tonic-gate 	else
1476*0Sstevel@tonic-gate 		IN6_IPADDR_TO_V4MAPPED(group, &v6group);
1477*0Sstevel@tonic-gate 	IN6_IPADDR_TO_V4MAPPED(src, &v6src);
1478*0Sstevel@tonic-gate 
1479*0Sstevel@tonic-gate 	return (ilm_lookup_ill_withsrc_v6(ill, &v6group, &v6src));
1480*0Sstevel@tonic-gate }
1481*0Sstevel@tonic-gate 
1482*0Sstevel@tonic-gate ilm_t *
1483*0Sstevel@tonic-gate ilm_lookup_ill_withsrc_v6(ill_t *ill, const in6_addr_t *v6group,
1484*0Sstevel@tonic-gate     const in6_addr_t *v6src)
1485*0Sstevel@tonic-gate {
1486*0Sstevel@tonic-gate 	ilm_t	*ilm;
1487*0Sstevel@tonic-gate 	boolean_t isinlist;
1488*0Sstevel@tonic-gate 	int	i, numsrc;
1489*0Sstevel@tonic-gate 
1490*0Sstevel@tonic-gate 	/*
1491*0Sstevel@tonic-gate 	 * If the source is in any ilm's INCLUDE list, or if
1492*0Sstevel@tonic-gate 	 * it is not in any ilm's EXCLUDE list, we have a hit.
1493*0Sstevel@tonic-gate 	 */
1494*0Sstevel@tonic-gate 	for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
1495*0Sstevel@tonic-gate 		if (IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, v6group)) {
1496*0Sstevel@tonic-gate 
1497*0Sstevel@tonic-gate 			isinlist = B_FALSE;
1498*0Sstevel@tonic-gate 			numsrc = (ilm->ilm_filter == NULL) ?
1499*0Sstevel@tonic-gate 			    0 : ilm->ilm_filter->sl_numsrc;
1500*0Sstevel@tonic-gate 			for (i = 0; i < numsrc; i++) {
1501*0Sstevel@tonic-gate 				if (IN6_ARE_ADDR_EQUAL(v6src,
1502*0Sstevel@tonic-gate 				    &ilm->ilm_filter->sl_addr[i])) {
1503*0Sstevel@tonic-gate 					isinlist = B_TRUE;
1504*0Sstevel@tonic-gate 					break;
1505*0Sstevel@tonic-gate 				}
1506*0Sstevel@tonic-gate 			}
1507*0Sstevel@tonic-gate 			if ((isinlist && ilm->ilm_fmode == MODE_IS_INCLUDE) ||
1508*0Sstevel@tonic-gate 			    (!isinlist && ilm->ilm_fmode == MODE_IS_EXCLUDE))
1509*0Sstevel@tonic-gate 				return (ilm);
1510*0Sstevel@tonic-gate 			else
1511*0Sstevel@tonic-gate 				return (NULL);
1512*0Sstevel@tonic-gate 		}
1513*0Sstevel@tonic-gate 	}
1514*0Sstevel@tonic-gate 	return (NULL);
1515*0Sstevel@tonic-gate }
1516*0Sstevel@tonic-gate 
1517*0Sstevel@tonic-gate 
1518*0Sstevel@tonic-gate /* Find an ilm for matching the ill */
1519*0Sstevel@tonic-gate ilm_t *
1520*0Sstevel@tonic-gate ilm_lookup_ill(ill_t *ill, ipaddr_t group, zoneid_t zoneid)
1521*0Sstevel@tonic-gate {
1522*0Sstevel@tonic-gate 	in6_addr_t	v6group;
1523*0Sstevel@tonic-gate 
1524*0Sstevel@tonic-gate 	ASSERT(ill->ill_ilm_walker_cnt != 0 || MUTEX_HELD(&ill->ill_lock) ||
1525*0Sstevel@tonic-gate 	    IAM_WRITER_ILL(ill));
1526*0Sstevel@tonic-gate 	/*
1527*0Sstevel@tonic-gate 	 * INADDR_ANY is represented as the IPv6 unspecifed addr.
1528*0Sstevel@tonic-gate 	 */
1529*0Sstevel@tonic-gate 	if (group == INADDR_ANY)
1530*0Sstevel@tonic-gate 		v6group = ipv6_all_zeros;
1531*0Sstevel@tonic-gate 	else
1532*0Sstevel@tonic-gate 		IN6_IPADDR_TO_V4MAPPED(group, &v6group);
1533*0Sstevel@tonic-gate 
1534*0Sstevel@tonic-gate 	return (ilm_lookup_ill_v6(ill, &v6group, zoneid));
1535*0Sstevel@tonic-gate }
1536*0Sstevel@tonic-gate 
1537*0Sstevel@tonic-gate /*
1538*0Sstevel@tonic-gate  * Find an ilm for matching the ill. All the ilm lookup functions
1539*0Sstevel@tonic-gate  * ignore ILM_DELETED ilms. These have been logically deleted, and
1540*0Sstevel@tonic-gate  * igmp and linklayer disable multicast have been done. Only mi_free
1541*0Sstevel@tonic-gate  * yet to be done. Still there in the list due to ilm_walkers. The
1542*0Sstevel@tonic-gate  * last walker will release it.
1543*0Sstevel@tonic-gate  */
1544*0Sstevel@tonic-gate ilm_t *
1545*0Sstevel@tonic-gate ilm_lookup_ill_v6(ill_t *ill, const in6_addr_t *v6group, zoneid_t zoneid)
1546*0Sstevel@tonic-gate {
1547*0Sstevel@tonic-gate 	ilm_t	*ilm;
1548*0Sstevel@tonic-gate 
1549*0Sstevel@tonic-gate 	ASSERT(ill->ill_ilm_walker_cnt != 0 || MUTEX_HELD(&ill->ill_lock) ||
1550*0Sstevel@tonic-gate 	    IAM_WRITER_ILL(ill));
1551*0Sstevel@tonic-gate 
1552*0Sstevel@tonic-gate 	for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
1553*0Sstevel@tonic-gate 		if (ilm->ilm_flags & ILM_DELETED)
1554*0Sstevel@tonic-gate 			continue;
1555*0Sstevel@tonic-gate 		if (IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, v6group) &&
1556*0Sstevel@tonic-gate 		    (zoneid == ALL_ZONES || zoneid == ilm->ilm_zoneid))
1557*0Sstevel@tonic-gate 			return (ilm);
1558*0Sstevel@tonic-gate 	}
1559*0Sstevel@tonic-gate 	return (NULL);
1560*0Sstevel@tonic-gate }
1561*0Sstevel@tonic-gate 
1562*0Sstevel@tonic-gate ilm_t *
1563*0Sstevel@tonic-gate ilm_lookup_ill_index_v6(ill_t *ill, const in6_addr_t *v6group, int index,
1564*0Sstevel@tonic-gate     zoneid_t zoneid)
1565*0Sstevel@tonic-gate {
1566*0Sstevel@tonic-gate 	ilm_t *ilm;
1567*0Sstevel@tonic-gate 
1568*0Sstevel@tonic-gate 	ASSERT(ill->ill_ilm_walker_cnt != 0 || MUTEX_HELD(&ill->ill_lock) ||
1569*0Sstevel@tonic-gate 	    IAM_WRITER_ILL(ill));
1570*0Sstevel@tonic-gate 
1571*0Sstevel@tonic-gate 	for (ilm = ill->ill_ilm; ilm != NULL; ilm = ilm->ilm_next) {
1572*0Sstevel@tonic-gate 		if (ilm->ilm_flags & ILM_DELETED)
1573*0Sstevel@tonic-gate 			continue;
1574*0Sstevel@tonic-gate 		if (IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, v6group) &&
1575*0Sstevel@tonic-gate 		    (zoneid == ALL_ZONES || zoneid == ilm->ilm_zoneid) &&
1576*0Sstevel@tonic-gate 		    ilm->ilm_orig_ifindex == index) {
1577*0Sstevel@tonic-gate 			return (ilm);
1578*0Sstevel@tonic-gate 		}
1579*0Sstevel@tonic-gate 	}
1580*0Sstevel@tonic-gate 	return (NULL);
1581*0Sstevel@tonic-gate }
1582*0Sstevel@tonic-gate 
1583*0Sstevel@tonic-gate ilm_t *
1584*0Sstevel@tonic-gate ilm_lookup_ill_index_v4(ill_t *ill, ipaddr_t group, int index, zoneid_t zoneid)
1585*0Sstevel@tonic-gate {
1586*0Sstevel@tonic-gate 	in6_addr_t	v6group;
1587*0Sstevel@tonic-gate 
1588*0Sstevel@tonic-gate 	ASSERT(ill->ill_ilm_walker_cnt != 0 || MUTEX_HELD(&ill->ill_lock) ||
1589*0Sstevel@tonic-gate 	    IAM_WRITER_ILL(ill));
1590*0Sstevel@tonic-gate 	/*
1591*0Sstevel@tonic-gate 	 * INADDR_ANY is represented as the IPv6 unspecifed addr.
1592*0Sstevel@tonic-gate 	 */
1593*0Sstevel@tonic-gate 	if (group == INADDR_ANY)
1594*0Sstevel@tonic-gate 		v6group = ipv6_all_zeros;
1595*0Sstevel@tonic-gate 	else
1596*0Sstevel@tonic-gate 		IN6_IPADDR_TO_V4MAPPED(group, &v6group);
1597*0Sstevel@tonic-gate 
1598*0Sstevel@tonic-gate 	return (ilm_lookup_ill_index_v6(ill, &v6group, index, zoneid));
1599*0Sstevel@tonic-gate }
1600*0Sstevel@tonic-gate 
1601*0Sstevel@tonic-gate /*
1602*0Sstevel@tonic-gate  * Found an ilm for the ipif. Only needed for IPv4 which does
1603*0Sstevel@tonic-gate  * ipif specific socket options.
1604*0Sstevel@tonic-gate  */
1605*0Sstevel@tonic-gate ilm_t *
1606*0Sstevel@tonic-gate ilm_lookup_ipif(ipif_t *ipif, ipaddr_t group)
1607*0Sstevel@tonic-gate {
1608*0Sstevel@tonic-gate 	ill_t	*ill = ipif->ipif_ill;
1609*0Sstevel@tonic-gate 	ilm_t	*ilm;
1610*0Sstevel@tonic-gate 	in6_addr_t	v6group;
1611*0Sstevel@tonic-gate 
1612*0Sstevel@tonic-gate 	ASSERT(ill->ill_ilm_walker_cnt != 0 || MUTEX_HELD(&ill->ill_lock) ||
1613*0Sstevel@tonic-gate 	    IAM_WRITER_ILL(ill));
1614*0Sstevel@tonic-gate 
1615*0Sstevel@tonic-gate 	/*
1616*0Sstevel@tonic-gate 	 * INADDR_ANY is represented as the IPv6 unspecifed addr.
1617*0Sstevel@tonic-gate 	 */
1618*0Sstevel@tonic-gate 	if (group == INADDR_ANY)
1619*0Sstevel@tonic-gate 		v6group = ipv6_all_zeros;
1620*0Sstevel@tonic-gate 	else
1621*0Sstevel@tonic-gate 		IN6_IPADDR_TO_V4MAPPED(group, &v6group);
1622*0Sstevel@tonic-gate 
1623*0Sstevel@tonic-gate 	for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
1624*0Sstevel@tonic-gate 		if (ilm->ilm_flags & ILM_DELETED)
1625*0Sstevel@tonic-gate 			continue;
1626*0Sstevel@tonic-gate 		if (ilm->ilm_ipif == ipif &&
1627*0Sstevel@tonic-gate 		    IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, &v6group))
1628*0Sstevel@tonic-gate 			return (ilm);
1629*0Sstevel@tonic-gate 	}
1630*0Sstevel@tonic-gate 	return (NULL);
1631*0Sstevel@tonic-gate }
1632*0Sstevel@tonic-gate 
1633*0Sstevel@tonic-gate /*
1634*0Sstevel@tonic-gate  * How many members on this ill?
1635*0Sstevel@tonic-gate  */
1636*0Sstevel@tonic-gate int
1637*0Sstevel@tonic-gate ilm_numentries_v6(ill_t *ill, const in6_addr_t *v6group)
1638*0Sstevel@tonic-gate {
1639*0Sstevel@tonic-gate 	ilm_t	*ilm;
1640*0Sstevel@tonic-gate 	int i = 0;
1641*0Sstevel@tonic-gate 
1642*0Sstevel@tonic-gate 	ASSERT(ill->ill_ilm_walker_cnt != 0 || MUTEX_HELD(&ill->ill_lock) ||
1643*0Sstevel@tonic-gate 	    IAM_WRITER_ILL(ill));
1644*0Sstevel@tonic-gate 
1645*0Sstevel@tonic-gate 	for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
1646*0Sstevel@tonic-gate 		if (ilm->ilm_flags & ILM_DELETED)
1647*0Sstevel@tonic-gate 			continue;
1648*0Sstevel@tonic-gate 		if (IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, v6group)) {
1649*0Sstevel@tonic-gate 			i++;
1650*0Sstevel@tonic-gate 		}
1651*0Sstevel@tonic-gate 	}
1652*0Sstevel@tonic-gate 	return (i);
1653*0Sstevel@tonic-gate }
1654*0Sstevel@tonic-gate 
1655*0Sstevel@tonic-gate /* Caller guarantees that the group is not already on the list */
1656*0Sstevel@tonic-gate static ilm_t *
1657*0Sstevel@tonic-gate ilm_add_v6(ipif_t *ipif, const in6_addr_t *v6group, ilg_stat_t ilgstat,
1658*0Sstevel@tonic-gate     mcast_record_t ilg_fmode, slist_t *ilg_flist, int orig_ifindex,
1659*0Sstevel@tonic-gate     zoneid_t zoneid)
1660*0Sstevel@tonic-gate {
1661*0Sstevel@tonic-gate 	ill_t	*ill = ipif->ipif_ill;
1662*0Sstevel@tonic-gate 	ilm_t	*ilm;
1663*0Sstevel@tonic-gate 	ilm_t	*ilm_cur;
1664*0Sstevel@tonic-gate 	ilm_t	**ilm_ptpn;
1665*0Sstevel@tonic-gate 
1666*0Sstevel@tonic-gate 	ASSERT(IAM_WRITER_IPIF(ipif));
1667*0Sstevel@tonic-gate 
1668*0Sstevel@tonic-gate 	ilm = GETSTRUCT(ilm_t, 1);
1669*0Sstevel@tonic-gate 	if (ilm == NULL)
1670*0Sstevel@tonic-gate 		return (NULL);
1671*0Sstevel@tonic-gate 	if (ilgstat != ILGSTAT_NONE && !SLIST_IS_EMPTY(ilg_flist)) {
1672*0Sstevel@tonic-gate 		ilm->ilm_filter = l_alloc();
1673*0Sstevel@tonic-gate 		if (ilm->ilm_filter == NULL) {
1674*0Sstevel@tonic-gate 			mi_free(ilm);
1675*0Sstevel@tonic-gate 			return (NULL);
1676*0Sstevel@tonic-gate 		}
1677*0Sstevel@tonic-gate 	}
1678*0Sstevel@tonic-gate 	ilm->ilm_v6addr = *v6group;
1679*0Sstevel@tonic-gate 	ilm->ilm_refcnt = 1;
1680*0Sstevel@tonic-gate 	ilm->ilm_zoneid = zoneid;
1681*0Sstevel@tonic-gate 	ilm->ilm_timer = INFINITY;
1682*0Sstevel@tonic-gate 	ilm->ilm_rtx.rtx_timer = INFINITY;
1683*0Sstevel@tonic-gate 	/*
1684*0Sstevel@tonic-gate 	 * IPv4 Multicast groups are joined using ipif.
1685*0Sstevel@tonic-gate 	 * IPv6 Multicast groups are joined using ill.
1686*0Sstevel@tonic-gate 	 */
1687*0Sstevel@tonic-gate 	if (ill->ill_isv6) {
1688*0Sstevel@tonic-gate 		ilm->ilm_ill = ill;
1689*0Sstevel@tonic-gate 		ilm->ilm_ipif = NULL;
1690*0Sstevel@tonic-gate 	} else {
1691*0Sstevel@tonic-gate 		ASSERT(ilm->ilm_zoneid == ipif->ipif_zoneid);
1692*0Sstevel@tonic-gate 		ilm->ilm_ipif = ipif;
1693*0Sstevel@tonic-gate 		ilm->ilm_ill = NULL;
1694*0Sstevel@tonic-gate 	}
1695*0Sstevel@tonic-gate 	/*
1696*0Sstevel@tonic-gate 	 * After this if ilm moves to a new ill, we don't change
1697*0Sstevel@tonic-gate 	 * the ilm_orig_ifindex. Thus, if ill_index != ilm_orig_ifindex,
1698*0Sstevel@tonic-gate 	 * it has been moved. Indexes don't match even when the application
1699*0Sstevel@tonic-gate 	 * wants to join on a FAILED/INACTIVE interface because we choose
1700*0Sstevel@tonic-gate 	 * a new interface to join in. This is considered as an implicit
1701*0Sstevel@tonic-gate 	 * move.
1702*0Sstevel@tonic-gate 	 */
1703*0Sstevel@tonic-gate 	ilm->ilm_orig_ifindex = orig_ifindex;
1704*0Sstevel@tonic-gate 
1705*0Sstevel@tonic-gate 	ASSERT(!(ipif->ipif_state_flags & IPIF_CONDEMNED));
1706*0Sstevel@tonic-gate 	ASSERT(!(ill->ill_state_flags & ILL_CONDEMNED));
1707*0Sstevel@tonic-gate 
1708*0Sstevel@tonic-gate 	/*
1709*0Sstevel@tonic-gate 	 * Grab lock to give consistent view to readers
1710*0Sstevel@tonic-gate 	 */
1711*0Sstevel@tonic-gate 	mutex_enter(&ill->ill_lock);
1712*0Sstevel@tonic-gate 	/*
1713*0Sstevel@tonic-gate 	 * All ilms in the same zone are contiguous in the ill_ilm list.
1714*0Sstevel@tonic-gate 	 * The loops in ip_proto_input() and ip_wput_local() use this to avoid
1715*0Sstevel@tonic-gate 	 * sending duplicates up when two applications in the same zone join the
1716*0Sstevel@tonic-gate 	 * same group on different logical interfaces.
1717*0Sstevel@tonic-gate 	 */
1718*0Sstevel@tonic-gate 	ilm_cur = ill->ill_ilm;
1719*0Sstevel@tonic-gate 	ilm_ptpn = &ill->ill_ilm;
1720*0Sstevel@tonic-gate 	while (ilm_cur != NULL && ilm_cur->ilm_zoneid != ilm->ilm_zoneid) {
1721*0Sstevel@tonic-gate 		ilm_ptpn = &ilm_cur->ilm_next;
1722*0Sstevel@tonic-gate 		ilm_cur = ilm_cur->ilm_next;
1723*0Sstevel@tonic-gate 	}
1724*0Sstevel@tonic-gate 	ilm->ilm_next = ilm_cur;
1725*0Sstevel@tonic-gate 	*ilm_ptpn = ilm;
1726*0Sstevel@tonic-gate 
1727*0Sstevel@tonic-gate 	/*
1728*0Sstevel@tonic-gate 	 * If we have an associated ilg, use its filter state; if not,
1729*0Sstevel@tonic-gate 	 * default to (EXCLUDE, NULL) and set no_ilg_cnt to track this.
1730*0Sstevel@tonic-gate 	 */
1731*0Sstevel@tonic-gate 	if (ilgstat != ILGSTAT_NONE) {
1732*0Sstevel@tonic-gate 		if (!SLIST_IS_EMPTY(ilg_flist))
1733*0Sstevel@tonic-gate 			l_copy(ilg_flist, ilm->ilm_filter);
1734*0Sstevel@tonic-gate 		ilm->ilm_fmode = ilg_fmode;
1735*0Sstevel@tonic-gate 	} else {
1736*0Sstevel@tonic-gate 		ilm->ilm_no_ilg_cnt = 1;
1737*0Sstevel@tonic-gate 		ilm->ilm_fmode = MODE_IS_EXCLUDE;
1738*0Sstevel@tonic-gate 	}
1739*0Sstevel@tonic-gate 
1740*0Sstevel@tonic-gate 	mutex_exit(&ill->ill_lock);
1741*0Sstevel@tonic-gate 	return (ilm);
1742*0Sstevel@tonic-gate }
1743*0Sstevel@tonic-gate 
1744*0Sstevel@tonic-gate void
1745*0Sstevel@tonic-gate ilm_walker_cleanup(ill_t *ill)
1746*0Sstevel@tonic-gate {
1747*0Sstevel@tonic-gate 	ilm_t	**ilmp;
1748*0Sstevel@tonic-gate 	ilm_t	*ilm;
1749*0Sstevel@tonic-gate 
1750*0Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&ill->ill_lock));
1751*0Sstevel@tonic-gate 	ASSERT(ill->ill_ilm_walker_cnt == 0);
1752*0Sstevel@tonic-gate 
1753*0Sstevel@tonic-gate 	ilmp = &ill->ill_ilm;
1754*0Sstevel@tonic-gate 	while (*ilmp != NULL) {
1755*0Sstevel@tonic-gate 		if ((*ilmp)->ilm_flags & ILM_DELETED) {
1756*0Sstevel@tonic-gate 			ilm = *ilmp;
1757*0Sstevel@tonic-gate 			*ilmp = ilm->ilm_next;
1758*0Sstevel@tonic-gate 			FREE_SLIST(ilm->ilm_filter);
1759*0Sstevel@tonic-gate 			FREE_SLIST(ilm->ilm_pendsrcs);
1760*0Sstevel@tonic-gate 			FREE_SLIST(ilm->ilm_rtx.rtx_allow);
1761*0Sstevel@tonic-gate 			FREE_SLIST(ilm->ilm_rtx.rtx_block);
1762*0Sstevel@tonic-gate 			mi_free((char *)ilm);
1763*0Sstevel@tonic-gate 		} else {
1764*0Sstevel@tonic-gate 			ilmp = &(*ilmp)->ilm_next;
1765*0Sstevel@tonic-gate 		}
1766*0Sstevel@tonic-gate 	}
1767*0Sstevel@tonic-gate 	ill->ill_ilm_cleanup_reqd = 0;
1768*0Sstevel@tonic-gate }
1769*0Sstevel@tonic-gate 
1770*0Sstevel@tonic-gate /*
1771*0Sstevel@tonic-gate  * Unlink ilm and free it.
1772*0Sstevel@tonic-gate  */
1773*0Sstevel@tonic-gate static void
1774*0Sstevel@tonic-gate ilm_delete(ilm_t *ilm)
1775*0Sstevel@tonic-gate {
1776*0Sstevel@tonic-gate 	ill_t	*ill;
1777*0Sstevel@tonic-gate 	ilm_t	**ilmp;
1778*0Sstevel@tonic-gate 
1779*0Sstevel@tonic-gate 	if (ilm->ilm_ipif != NULL) {
1780*0Sstevel@tonic-gate 		ASSERT(IAM_WRITER_IPIF(ilm->ilm_ipif));
1781*0Sstevel@tonic-gate 		ASSERT(ilm->ilm_ill == NULL);
1782*0Sstevel@tonic-gate 		ill = ilm->ilm_ipif->ipif_ill;
1783*0Sstevel@tonic-gate 		ASSERT(!ill->ill_isv6);
1784*0Sstevel@tonic-gate 	} else {
1785*0Sstevel@tonic-gate 		ASSERT(IAM_WRITER_ILL(ilm->ilm_ill));
1786*0Sstevel@tonic-gate 		ASSERT(ilm->ilm_ipif == NULL);
1787*0Sstevel@tonic-gate 		ill = ilm->ilm_ill;
1788*0Sstevel@tonic-gate 		ASSERT(ill->ill_isv6);
1789*0Sstevel@tonic-gate 	}
1790*0Sstevel@tonic-gate 	/*
1791*0Sstevel@tonic-gate 	 * Delete under lock protection so that readers don't stumble
1792*0Sstevel@tonic-gate 	 * on bad ilm_next
1793*0Sstevel@tonic-gate 	 */
1794*0Sstevel@tonic-gate 	mutex_enter(&ill->ill_lock);
1795*0Sstevel@tonic-gate 	if (ill->ill_ilm_walker_cnt != 0) {
1796*0Sstevel@tonic-gate 		ilm->ilm_flags |= ILM_DELETED;
1797*0Sstevel@tonic-gate 		ill->ill_ilm_cleanup_reqd = 1;
1798*0Sstevel@tonic-gate 		mutex_exit(&ill->ill_lock);
1799*0Sstevel@tonic-gate 		return;
1800*0Sstevel@tonic-gate 	}
1801*0Sstevel@tonic-gate 
1802*0Sstevel@tonic-gate 	for (ilmp = &ill->ill_ilm; *ilmp != ilm; ilmp = &(*ilmp)->ilm_next)
1803*0Sstevel@tonic-gate 				;
1804*0Sstevel@tonic-gate 	*ilmp = ilm->ilm_next;
1805*0Sstevel@tonic-gate 	mutex_exit(&ill->ill_lock);
1806*0Sstevel@tonic-gate 
1807*0Sstevel@tonic-gate 	FREE_SLIST(ilm->ilm_filter);
1808*0Sstevel@tonic-gate 	FREE_SLIST(ilm->ilm_pendsrcs);
1809*0Sstevel@tonic-gate 	FREE_SLIST(ilm->ilm_rtx.rtx_allow);
1810*0Sstevel@tonic-gate 	FREE_SLIST(ilm->ilm_rtx.rtx_block);
1811*0Sstevel@tonic-gate 	mi_free((char *)ilm);
1812*0Sstevel@tonic-gate }
1813*0Sstevel@tonic-gate 
1814*0Sstevel@tonic-gate /* Free all ilms for this ipif */
1815*0Sstevel@tonic-gate void
1816*0Sstevel@tonic-gate ilm_free(ipif_t *ipif)
1817*0Sstevel@tonic-gate {
1818*0Sstevel@tonic-gate 	ill_t	*ill = ipif->ipif_ill;
1819*0Sstevel@tonic-gate 	ilm_t	*ilm;
1820*0Sstevel@tonic-gate 	ilm_t	 *next_ilm;
1821*0Sstevel@tonic-gate 
1822*0Sstevel@tonic-gate 	ASSERT(IAM_WRITER_IPIF(ipif));
1823*0Sstevel@tonic-gate 
1824*0Sstevel@tonic-gate 	for (ilm = ill->ill_ilm; ilm; ilm = next_ilm) {
1825*0Sstevel@tonic-gate 		next_ilm = ilm->ilm_next;
1826*0Sstevel@tonic-gate 		if (ilm->ilm_ipif == ipif)
1827*0Sstevel@tonic-gate 			ilm_delete(ilm);
1828*0Sstevel@tonic-gate 	}
1829*0Sstevel@tonic-gate }
1830*0Sstevel@tonic-gate 
1831*0Sstevel@tonic-gate /*
1832*0Sstevel@tonic-gate  * Looks up the appropriate ipif given a v4 multicast group and interface
1833*0Sstevel@tonic-gate  * address.  On success, returns 0, with *ipifpp pointing to the found
1834*0Sstevel@tonic-gate  * struct.  On failure, returns an errno and *ipifpp is NULL.
1835*0Sstevel@tonic-gate  */
1836*0Sstevel@tonic-gate int
1837*0Sstevel@tonic-gate ip_opt_check(conn_t *connp, ipaddr_t group, ipaddr_t src, ipaddr_t ifaddr,
1838*0Sstevel@tonic-gate     uint_t *ifindexp, mblk_t *first_mp, ipsq_func_t func, ipif_t **ipifpp)
1839*0Sstevel@tonic-gate {
1840*0Sstevel@tonic-gate 	ipif_t *ipif;
1841*0Sstevel@tonic-gate 	int err = 0;
1842*0Sstevel@tonic-gate 	zoneid_t zoneid = connp->conn_zoneid;
1843*0Sstevel@tonic-gate 
1844*0Sstevel@tonic-gate 	if (!CLASSD(group) || CLASSD(src)) {
1845*0Sstevel@tonic-gate 		return (EINVAL);
1846*0Sstevel@tonic-gate 	}
1847*0Sstevel@tonic-gate 	*ipifpp = NULL;
1848*0Sstevel@tonic-gate 
1849*0Sstevel@tonic-gate 	ASSERT(!(ifaddr != INADDR_ANY && ifindexp != NULL && *ifindexp != 0));
1850*0Sstevel@tonic-gate 	if (ifaddr != INADDR_ANY) {
1851*0Sstevel@tonic-gate 		ipif = ipif_lookup_addr(ifaddr, NULL, zoneid,
1852*0Sstevel@tonic-gate 		    CONNP_TO_WQ(connp), first_mp, func, &err);
1853*0Sstevel@tonic-gate 		if (err != 0 && err != EINPROGRESS)
1854*0Sstevel@tonic-gate 			err = EADDRNOTAVAIL;
1855*0Sstevel@tonic-gate 	} else if (ifindexp != NULL && *ifindexp != 0) {
1856*0Sstevel@tonic-gate 		ipif = ipif_lookup_on_ifindex(*ifindexp, B_FALSE, zoneid,
1857*0Sstevel@tonic-gate 		    CONNP_TO_WQ(connp), first_mp, func, &err);
1858*0Sstevel@tonic-gate 	} else {
1859*0Sstevel@tonic-gate 		ipif = ipif_lookup_group(group, zoneid);
1860*0Sstevel@tonic-gate 		if (ipif == NULL)
1861*0Sstevel@tonic-gate 			return (EADDRNOTAVAIL);
1862*0Sstevel@tonic-gate 	}
1863*0Sstevel@tonic-gate 	if (ipif == NULL)
1864*0Sstevel@tonic-gate 		return (err);
1865*0Sstevel@tonic-gate 
1866*0Sstevel@tonic-gate 	*ipifpp = ipif;
1867*0Sstevel@tonic-gate 	return (0);
1868*0Sstevel@tonic-gate }
1869*0Sstevel@tonic-gate 
1870*0Sstevel@tonic-gate /*
1871*0Sstevel@tonic-gate  * Looks up the appropriate ill (or ipif if v4mapped) given an interface
1872*0Sstevel@tonic-gate  * index and IPv6 multicast group.  On success, returns 0, with *illpp (or
1873*0Sstevel@tonic-gate  * *ipifpp if v4mapped) pointing to the found struct.  On failure, returns
1874*0Sstevel@tonic-gate  * an errno and *illpp and *ipifpp are undefined.
1875*0Sstevel@tonic-gate  */
1876*0Sstevel@tonic-gate int
1877*0Sstevel@tonic-gate ip_opt_check_v6(conn_t *connp, const in6_addr_t *v6group, ipaddr_t *v4group,
1878*0Sstevel@tonic-gate     const in6_addr_t *v6src, ipaddr_t *v4src, boolean_t *isv6, int ifindex,
1879*0Sstevel@tonic-gate     mblk_t *first_mp, ipsq_func_t func, ill_t **illpp, ipif_t **ipifpp)
1880*0Sstevel@tonic-gate {
1881*0Sstevel@tonic-gate 	boolean_t src_unspec;
1882*0Sstevel@tonic-gate 	ill_t *ill = NULL;
1883*0Sstevel@tonic-gate 	ipif_t *ipif = NULL;
1884*0Sstevel@tonic-gate 	int err;
1885*0Sstevel@tonic-gate 	zoneid_t zoneid = connp->conn_zoneid;
1886*0Sstevel@tonic-gate 	queue_t *wq = CONNP_TO_WQ(connp);
1887*0Sstevel@tonic-gate 
1888*0Sstevel@tonic-gate 	src_unspec = IN6_IS_ADDR_UNSPECIFIED(v6src);
1889*0Sstevel@tonic-gate 
1890*0Sstevel@tonic-gate 	if (IN6_IS_ADDR_V4MAPPED(v6group)) {
1891*0Sstevel@tonic-gate 		if (!IN6_IS_ADDR_V4MAPPED(v6src) && !src_unspec)
1892*0Sstevel@tonic-gate 			return (EINVAL);
1893*0Sstevel@tonic-gate 		IN6_V4MAPPED_TO_IPADDR(v6group, *v4group);
1894*0Sstevel@tonic-gate 		if (src_unspec) {
1895*0Sstevel@tonic-gate 			*v4src = INADDR_ANY;
1896*0Sstevel@tonic-gate 		} else {
1897*0Sstevel@tonic-gate 			IN6_V4MAPPED_TO_IPADDR(v6src, *v4src);
1898*0Sstevel@tonic-gate 		}
1899*0Sstevel@tonic-gate 		if (!CLASSD(*v4group) || CLASSD(*v4src))
1900*0Sstevel@tonic-gate 			return (EINVAL);
1901*0Sstevel@tonic-gate 		*ipifpp = NULL;
1902*0Sstevel@tonic-gate 		*isv6 = B_FALSE;
1903*0Sstevel@tonic-gate 	} else {
1904*0Sstevel@tonic-gate 		if (IN6_IS_ADDR_V4MAPPED(v6src) && !src_unspec)
1905*0Sstevel@tonic-gate 			return (EINVAL);
1906*0Sstevel@tonic-gate 		if (!IN6_IS_ADDR_MULTICAST(v6group) ||
1907*0Sstevel@tonic-gate 		    IN6_IS_ADDR_MULTICAST(v6src)) {
1908*0Sstevel@tonic-gate 			return (EINVAL);
1909*0Sstevel@tonic-gate 		}
1910*0Sstevel@tonic-gate 		*illpp = NULL;
1911*0Sstevel@tonic-gate 		*isv6 = B_TRUE;
1912*0Sstevel@tonic-gate 	}
1913*0Sstevel@tonic-gate 
1914*0Sstevel@tonic-gate 	if (ifindex == 0) {
1915*0Sstevel@tonic-gate 		if (*isv6)
1916*0Sstevel@tonic-gate 			ill = ill_lookup_group_v6(v6group, zoneid);
1917*0Sstevel@tonic-gate 		else
1918*0Sstevel@tonic-gate 			ipif = ipif_lookup_group(*v4group, zoneid);
1919*0Sstevel@tonic-gate 		if (ill == NULL && ipif == NULL)
1920*0Sstevel@tonic-gate 			return (EADDRNOTAVAIL);
1921*0Sstevel@tonic-gate 	} else {
1922*0Sstevel@tonic-gate 		if (*isv6) {
1923*0Sstevel@tonic-gate 			ill = ill_lookup_on_ifindex(ifindex, B_TRUE,
1924*0Sstevel@tonic-gate 			    wq, first_mp, func, &err);
1925*0Sstevel@tonic-gate 			if (ill != NULL &&
1926*0Sstevel@tonic-gate 			    !ipif_lookup_zoneid(ill, zoneid, 0, NULL)) {
1927*0Sstevel@tonic-gate 				ill_refrele(ill);
1928*0Sstevel@tonic-gate 				ill = NULL;
1929*0Sstevel@tonic-gate 				err = EADDRNOTAVAIL;
1930*0Sstevel@tonic-gate 			}
1931*0Sstevel@tonic-gate 		} else {
1932*0Sstevel@tonic-gate 			ipif = ipif_lookup_on_ifindex(ifindex, B_FALSE,
1933*0Sstevel@tonic-gate 			    zoneid, wq, first_mp, func, &err);
1934*0Sstevel@tonic-gate 		}
1935*0Sstevel@tonic-gate 		if (ill == NULL && ipif == NULL)
1936*0Sstevel@tonic-gate 			return (err);
1937*0Sstevel@tonic-gate 	}
1938*0Sstevel@tonic-gate 
1939*0Sstevel@tonic-gate 	*ipifpp = ipif;
1940*0Sstevel@tonic-gate 	*illpp = ill;
1941*0Sstevel@tonic-gate 	return (0);
1942*0Sstevel@tonic-gate }
1943*0Sstevel@tonic-gate 
1944*0Sstevel@tonic-gate static int
1945*0Sstevel@tonic-gate ip_get_srcfilter(conn_t *connp, struct group_filter *gf,
1946*0Sstevel@tonic-gate     struct ip_msfilter *imsf, ipaddr_t grp, ipif_t *ipif, boolean_t isv4mapped)
1947*0Sstevel@tonic-gate {
1948*0Sstevel@tonic-gate 	ilg_t *ilg;
1949*0Sstevel@tonic-gate 	int i, numsrc, fmode, outsrcs;
1950*0Sstevel@tonic-gate 	struct sockaddr_in *sin;
1951*0Sstevel@tonic-gate 	struct sockaddr_in6 *sin6;
1952*0Sstevel@tonic-gate 	struct in_addr *addrp;
1953*0Sstevel@tonic-gate 	slist_t *fp;
1954*0Sstevel@tonic-gate 	boolean_t is_v4only_api;
1955*0Sstevel@tonic-gate 
1956*0Sstevel@tonic-gate 	mutex_enter(&connp->conn_lock);
1957*0Sstevel@tonic-gate 
1958*0Sstevel@tonic-gate 	ilg = ilg_lookup_ipif(connp, grp, ipif);
1959*0Sstevel@tonic-gate 	if (ilg == NULL) {
1960*0Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
1961*0Sstevel@tonic-gate 		return (EADDRNOTAVAIL);
1962*0Sstevel@tonic-gate 	}
1963*0Sstevel@tonic-gate 
1964*0Sstevel@tonic-gate 	if (gf == NULL) {
1965*0Sstevel@tonic-gate 		ASSERT(imsf != NULL);
1966*0Sstevel@tonic-gate 		ASSERT(!isv4mapped);
1967*0Sstevel@tonic-gate 		is_v4only_api = B_TRUE;
1968*0Sstevel@tonic-gate 		outsrcs = imsf->imsf_numsrc;
1969*0Sstevel@tonic-gate 	} else {
1970*0Sstevel@tonic-gate 		ASSERT(imsf == NULL);
1971*0Sstevel@tonic-gate 		is_v4only_api = B_FALSE;
1972*0Sstevel@tonic-gate 		outsrcs = gf->gf_numsrc;
1973*0Sstevel@tonic-gate 	}
1974*0Sstevel@tonic-gate 
1975*0Sstevel@tonic-gate 	/*
1976*0Sstevel@tonic-gate 	 * In the kernel, we use the state definitions MODE_IS_[IN|EX]CLUDE
1977*0Sstevel@tonic-gate 	 * to identify the filter mode; but the API uses MCAST_[IN|EX]CLUDE.
1978*0Sstevel@tonic-gate 	 * So we need to translate here.
1979*0Sstevel@tonic-gate 	 */
1980*0Sstevel@tonic-gate 	fmode = (ilg->ilg_fmode == MODE_IS_INCLUDE) ?
1981*0Sstevel@tonic-gate 	    MCAST_INCLUDE : MCAST_EXCLUDE;
1982*0Sstevel@tonic-gate 	if ((fp = ilg->ilg_filter) == NULL) {
1983*0Sstevel@tonic-gate 		numsrc = 0;
1984*0Sstevel@tonic-gate 	} else {
1985*0Sstevel@tonic-gate 		for (i = 0; i < outsrcs; i++) {
1986*0Sstevel@tonic-gate 			if (i == fp->sl_numsrc)
1987*0Sstevel@tonic-gate 				break;
1988*0Sstevel@tonic-gate 			if (isv4mapped) {
1989*0Sstevel@tonic-gate 				sin6 = (struct sockaddr_in6 *)&gf->gf_slist[i];
1990*0Sstevel@tonic-gate 				sin6->sin6_family = AF_INET6;
1991*0Sstevel@tonic-gate 				sin6->sin6_addr = fp->sl_addr[i];
1992*0Sstevel@tonic-gate 			} else {
1993*0Sstevel@tonic-gate 				if (is_v4only_api) {
1994*0Sstevel@tonic-gate 					addrp = &imsf->imsf_slist[i];
1995*0Sstevel@tonic-gate 				} else {
1996*0Sstevel@tonic-gate 					sin = (struct sockaddr_in *)
1997*0Sstevel@tonic-gate 					    &gf->gf_slist[i];
1998*0Sstevel@tonic-gate 					sin->sin_family = AF_INET;
1999*0Sstevel@tonic-gate 					addrp = &sin->sin_addr;
2000*0Sstevel@tonic-gate 				}
2001*0Sstevel@tonic-gate 				IN6_V4MAPPED_TO_INADDR(&fp->sl_addr[i], addrp);
2002*0Sstevel@tonic-gate 			}
2003*0Sstevel@tonic-gate 		}
2004*0Sstevel@tonic-gate 		numsrc = fp->sl_numsrc;
2005*0Sstevel@tonic-gate 	}
2006*0Sstevel@tonic-gate 
2007*0Sstevel@tonic-gate 	if (is_v4only_api) {
2008*0Sstevel@tonic-gate 		imsf->imsf_numsrc = numsrc;
2009*0Sstevel@tonic-gate 		imsf->imsf_fmode = fmode;
2010*0Sstevel@tonic-gate 	} else {
2011*0Sstevel@tonic-gate 		gf->gf_numsrc = numsrc;
2012*0Sstevel@tonic-gate 		gf->gf_fmode = fmode;
2013*0Sstevel@tonic-gate 	}
2014*0Sstevel@tonic-gate 
2015*0Sstevel@tonic-gate 	mutex_exit(&connp->conn_lock);
2016*0Sstevel@tonic-gate 
2017*0Sstevel@tonic-gate 	return (0);
2018*0Sstevel@tonic-gate }
2019*0Sstevel@tonic-gate 
2020*0Sstevel@tonic-gate static int
2021*0Sstevel@tonic-gate ip_get_srcfilter_v6(conn_t *connp, struct group_filter *gf,
2022*0Sstevel@tonic-gate     const struct in6_addr *grp, ill_t *ill)
2023*0Sstevel@tonic-gate {
2024*0Sstevel@tonic-gate 	ilg_t *ilg;
2025*0Sstevel@tonic-gate 	int i;
2026*0Sstevel@tonic-gate 	struct sockaddr_storage *sl;
2027*0Sstevel@tonic-gate 	struct sockaddr_in6 *sin6;
2028*0Sstevel@tonic-gate 	slist_t *fp;
2029*0Sstevel@tonic-gate 
2030*0Sstevel@tonic-gate 	mutex_enter(&connp->conn_lock);
2031*0Sstevel@tonic-gate 
2032*0Sstevel@tonic-gate 	ilg = ilg_lookup_ill_v6(connp, grp, ill);
2033*0Sstevel@tonic-gate 	if (ilg == NULL) {
2034*0Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
2035*0Sstevel@tonic-gate 		return (EADDRNOTAVAIL);
2036*0Sstevel@tonic-gate 	}
2037*0Sstevel@tonic-gate 
2038*0Sstevel@tonic-gate 	/*
2039*0Sstevel@tonic-gate 	 * In the kernel, we use the state definitions MODE_IS_[IN|EX]CLUDE
2040*0Sstevel@tonic-gate 	 * to identify the filter mode; but the API uses MCAST_[IN|EX]CLUDE.
2041*0Sstevel@tonic-gate 	 * So we need to translate here.
2042*0Sstevel@tonic-gate 	 */
2043*0Sstevel@tonic-gate 	gf->gf_fmode = (ilg->ilg_fmode == MODE_IS_INCLUDE) ?
2044*0Sstevel@tonic-gate 	    MCAST_INCLUDE : MCAST_EXCLUDE;
2045*0Sstevel@tonic-gate 	if ((fp = ilg->ilg_filter) == NULL) {
2046*0Sstevel@tonic-gate 		gf->gf_numsrc = 0;
2047*0Sstevel@tonic-gate 	} else {
2048*0Sstevel@tonic-gate 		for (i = 0, sl = gf->gf_slist; i < gf->gf_numsrc; i++, sl++) {
2049*0Sstevel@tonic-gate 			if (i == fp->sl_numsrc)
2050*0Sstevel@tonic-gate 				break;
2051*0Sstevel@tonic-gate 			sin6 = (struct sockaddr_in6 *)sl;
2052*0Sstevel@tonic-gate 			sin6->sin6_family = AF_INET6;
2053*0Sstevel@tonic-gate 			sin6->sin6_addr = fp->sl_addr[i];
2054*0Sstevel@tonic-gate 		}
2055*0Sstevel@tonic-gate 		gf->gf_numsrc = fp->sl_numsrc;
2056*0Sstevel@tonic-gate 	}
2057*0Sstevel@tonic-gate 
2058*0Sstevel@tonic-gate 	mutex_exit(&connp->conn_lock);
2059*0Sstevel@tonic-gate 
2060*0Sstevel@tonic-gate 	return (0);
2061*0Sstevel@tonic-gate }
2062*0Sstevel@tonic-gate 
2063*0Sstevel@tonic-gate static int
2064*0Sstevel@tonic-gate ip_set_srcfilter(conn_t *connp, struct group_filter *gf,
2065*0Sstevel@tonic-gate     struct ip_msfilter *imsf, ipaddr_t grp, ipif_t *ipif, boolean_t isv4mapped)
2066*0Sstevel@tonic-gate {
2067*0Sstevel@tonic-gate 	ilg_t *ilg;
2068*0Sstevel@tonic-gate 	int i, err, insrcs, infmode, new_fmode;
2069*0Sstevel@tonic-gate 	struct sockaddr_in *sin;
2070*0Sstevel@tonic-gate 	struct sockaddr_in6 *sin6;
2071*0Sstevel@tonic-gate 	struct in_addr *addrp;
2072*0Sstevel@tonic-gate 	slist_t *orig_filter = NULL;
2073*0Sstevel@tonic-gate 	slist_t *new_filter = NULL;
2074*0Sstevel@tonic-gate 	mcast_record_t orig_fmode;
2075*0Sstevel@tonic-gate 	boolean_t leave_grp, is_v4only_api;
2076*0Sstevel@tonic-gate 	ilg_stat_t ilgstat;
2077*0Sstevel@tonic-gate 
2078*0Sstevel@tonic-gate 	if (gf == NULL) {
2079*0Sstevel@tonic-gate 		ASSERT(imsf != NULL);
2080*0Sstevel@tonic-gate 		ASSERT(!isv4mapped);
2081*0Sstevel@tonic-gate 		is_v4only_api = B_TRUE;
2082*0Sstevel@tonic-gate 		insrcs = imsf->imsf_numsrc;
2083*0Sstevel@tonic-gate 		infmode = imsf->imsf_fmode;
2084*0Sstevel@tonic-gate 	} else {
2085*0Sstevel@tonic-gate 		ASSERT(imsf == NULL);
2086*0Sstevel@tonic-gate 		is_v4only_api = B_FALSE;
2087*0Sstevel@tonic-gate 		insrcs = gf->gf_numsrc;
2088*0Sstevel@tonic-gate 		infmode = gf->gf_fmode;
2089*0Sstevel@tonic-gate 	}
2090*0Sstevel@tonic-gate 
2091*0Sstevel@tonic-gate 	/* Make sure we can handle the source list */
2092*0Sstevel@tonic-gate 	if (insrcs > MAX_FILTER_SIZE)
2093*0Sstevel@tonic-gate 		return (ENOBUFS);
2094*0Sstevel@tonic-gate 
2095*0Sstevel@tonic-gate 	/*
2096*0Sstevel@tonic-gate 	 * setting the filter to (INCLUDE, NULL) is treated
2097*0Sstevel@tonic-gate 	 * as a request to leave the group.
2098*0Sstevel@tonic-gate 	 */
2099*0Sstevel@tonic-gate 	leave_grp = (infmode == MCAST_INCLUDE && insrcs == 0);
2100*0Sstevel@tonic-gate 
2101*0Sstevel@tonic-gate 	ASSERT(IAM_WRITER_IPIF(ipif));
2102*0Sstevel@tonic-gate 
2103*0Sstevel@tonic-gate 	mutex_enter(&connp->conn_lock);
2104*0Sstevel@tonic-gate 
2105*0Sstevel@tonic-gate 	ilg = ilg_lookup_ipif(connp, grp, ipif);
2106*0Sstevel@tonic-gate 	if (ilg == NULL) {
2107*0Sstevel@tonic-gate 		/*
2108*0Sstevel@tonic-gate 		 * if the request was actually to leave, and we
2109*0Sstevel@tonic-gate 		 * didn't find an ilg, there's nothing to do.
2110*0Sstevel@tonic-gate 		 */
2111*0Sstevel@tonic-gate 		if (!leave_grp)
2112*0Sstevel@tonic-gate 			ilg = conn_ilg_alloc(connp);
2113*0Sstevel@tonic-gate 		if (leave_grp || ilg == NULL) {
2114*0Sstevel@tonic-gate 			mutex_exit(&connp->conn_lock);
2115*0Sstevel@tonic-gate 			return (leave_grp ? 0 : ENOMEM);
2116*0Sstevel@tonic-gate 		}
2117*0Sstevel@tonic-gate 		ilgstat = ILGSTAT_NEW;
2118*0Sstevel@tonic-gate 		IN6_IPADDR_TO_V4MAPPED(grp, &ilg->ilg_v6group);
2119*0Sstevel@tonic-gate 		ilg->ilg_ipif = ipif;
2120*0Sstevel@tonic-gate 		ilg->ilg_ill = NULL;
2121*0Sstevel@tonic-gate 		ilg->ilg_orig_ifindex = 0;
2122*0Sstevel@tonic-gate 	} else if (leave_grp) {
2123*0Sstevel@tonic-gate 		ilg_delete(connp, ilg, NULL);
2124*0Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
2125*0Sstevel@tonic-gate 		(void) ip_delmulti(grp, ipif, B_FALSE, B_TRUE);
2126*0Sstevel@tonic-gate 		return (0);
2127*0Sstevel@tonic-gate 	} else {
2128*0Sstevel@tonic-gate 		ilgstat = ILGSTAT_CHANGE;
2129*0Sstevel@tonic-gate 		/* Preserve existing state in case ip_addmulti() fails */
2130*0Sstevel@tonic-gate 		orig_fmode = ilg->ilg_fmode;
2131*0Sstevel@tonic-gate 		if (ilg->ilg_filter == NULL) {
2132*0Sstevel@tonic-gate 			orig_filter = NULL;
2133*0Sstevel@tonic-gate 		} else {
2134*0Sstevel@tonic-gate 			orig_filter = l_alloc_copy(ilg->ilg_filter);
2135*0Sstevel@tonic-gate 			if (orig_filter == NULL) {
2136*0Sstevel@tonic-gate 				mutex_exit(&connp->conn_lock);
2137*0Sstevel@tonic-gate 				return (ENOMEM);
2138*0Sstevel@tonic-gate 			}
2139*0Sstevel@tonic-gate 		}
2140*0Sstevel@tonic-gate 	}
2141*0Sstevel@tonic-gate 
2142*0Sstevel@tonic-gate 	/*
2143*0Sstevel@tonic-gate 	 * Alloc buffer to copy new state into (see below) before
2144*0Sstevel@tonic-gate 	 * we make any changes, so we can bail if it fails.
2145*0Sstevel@tonic-gate 	 */
2146*0Sstevel@tonic-gate 	if ((new_filter = l_alloc()) == NULL) {
2147*0Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
2148*0Sstevel@tonic-gate 		err = ENOMEM;
2149*0Sstevel@tonic-gate 		goto free_and_exit;
2150*0Sstevel@tonic-gate 	}
2151*0Sstevel@tonic-gate 
2152*0Sstevel@tonic-gate 	if (insrcs == 0) {
2153*0Sstevel@tonic-gate 		CLEAR_SLIST(ilg->ilg_filter);
2154*0Sstevel@tonic-gate 	} else {
2155*0Sstevel@tonic-gate 		slist_t *fp;
2156*0Sstevel@tonic-gate 		if (ilg->ilg_filter == NULL) {
2157*0Sstevel@tonic-gate 			fp = l_alloc();
2158*0Sstevel@tonic-gate 			if (fp == NULL) {
2159*0Sstevel@tonic-gate 				if (ilgstat == ILGSTAT_NEW)
2160*0Sstevel@tonic-gate 					ilg_delete(connp, ilg, NULL);
2161*0Sstevel@tonic-gate 				mutex_exit(&connp->conn_lock);
2162*0Sstevel@tonic-gate 				err = ENOMEM;
2163*0Sstevel@tonic-gate 				goto free_and_exit;
2164*0Sstevel@tonic-gate 			}
2165*0Sstevel@tonic-gate 		} else {
2166*0Sstevel@tonic-gate 			fp = ilg->ilg_filter;
2167*0Sstevel@tonic-gate 		}
2168*0Sstevel@tonic-gate 		for (i = 0; i < insrcs; i++) {
2169*0Sstevel@tonic-gate 			if (isv4mapped) {
2170*0Sstevel@tonic-gate 				sin6 = (struct sockaddr_in6 *)&gf->gf_slist[i];
2171*0Sstevel@tonic-gate 				fp->sl_addr[i] = sin6->sin6_addr;
2172*0Sstevel@tonic-gate 			} else {
2173*0Sstevel@tonic-gate 				if (is_v4only_api) {
2174*0Sstevel@tonic-gate 					addrp = &imsf->imsf_slist[i];
2175*0Sstevel@tonic-gate 				} else {
2176*0Sstevel@tonic-gate 					sin = (struct sockaddr_in *)
2177*0Sstevel@tonic-gate 					    &gf->gf_slist[i];
2178*0Sstevel@tonic-gate 					addrp = &sin->sin_addr;
2179*0Sstevel@tonic-gate 				}
2180*0Sstevel@tonic-gate 				IN6_INADDR_TO_V4MAPPED(addrp, &fp->sl_addr[i]);
2181*0Sstevel@tonic-gate 			}
2182*0Sstevel@tonic-gate 		}
2183*0Sstevel@tonic-gate 		fp->sl_numsrc = insrcs;
2184*0Sstevel@tonic-gate 		ilg->ilg_filter = fp;
2185*0Sstevel@tonic-gate 	}
2186*0Sstevel@tonic-gate 	/*
2187*0Sstevel@tonic-gate 	 * In the kernel, we use the state definitions MODE_IS_[IN|EX]CLUDE
2188*0Sstevel@tonic-gate 	 * to identify the filter mode; but the API uses MCAST_[IN|EX]CLUDE.
2189*0Sstevel@tonic-gate 	 * So we need to translate here.
2190*0Sstevel@tonic-gate 	 */
2191*0Sstevel@tonic-gate 	ilg->ilg_fmode = (infmode == MCAST_INCLUDE) ?
2192*0Sstevel@tonic-gate 		    MODE_IS_INCLUDE : MODE_IS_EXCLUDE;
2193*0Sstevel@tonic-gate 
2194*0Sstevel@tonic-gate 	/*
2195*0Sstevel@tonic-gate 	 * Save copy of ilg's filter state to pass to other functions,
2196*0Sstevel@tonic-gate 	 * so we can release conn_lock now.
2197*0Sstevel@tonic-gate 	 */
2198*0Sstevel@tonic-gate 	new_fmode = ilg->ilg_fmode;
2199*0Sstevel@tonic-gate 	l_copy(ilg->ilg_filter, new_filter);
2200*0Sstevel@tonic-gate 
2201*0Sstevel@tonic-gate 	mutex_exit(&connp->conn_lock);
2202*0Sstevel@tonic-gate 
2203*0Sstevel@tonic-gate 	err = ip_addmulti(grp, ipif, ilgstat, new_fmode, new_filter);
2204*0Sstevel@tonic-gate 	if (err != 0) {
2205*0Sstevel@tonic-gate 		/*
2206*0Sstevel@tonic-gate 		 * Restore the original filter state, or delete the
2207*0Sstevel@tonic-gate 		 * newly-created ilg.  We need to look up the ilg
2208*0Sstevel@tonic-gate 		 * again, though, since we've not been holding the
2209*0Sstevel@tonic-gate 		 * conn_lock.
2210*0Sstevel@tonic-gate 		 */
2211*0Sstevel@tonic-gate 		mutex_enter(&connp->conn_lock);
2212*0Sstevel@tonic-gate 		ilg = ilg_lookup_ipif(connp, grp, ipif);
2213*0Sstevel@tonic-gate 		ASSERT(ilg != NULL);
2214*0Sstevel@tonic-gate 		if (ilgstat == ILGSTAT_NEW) {
2215*0Sstevel@tonic-gate 			ilg_delete(connp, ilg, NULL);
2216*0Sstevel@tonic-gate 		} else {
2217*0Sstevel@tonic-gate 			ilg->ilg_fmode = orig_fmode;
2218*0Sstevel@tonic-gate 			if (SLIST_IS_EMPTY(orig_filter)) {
2219*0Sstevel@tonic-gate 				CLEAR_SLIST(ilg->ilg_filter);
2220*0Sstevel@tonic-gate 			} else {
2221*0Sstevel@tonic-gate 				/*
2222*0Sstevel@tonic-gate 				 * We didn't free the filter, even if we
2223*0Sstevel@tonic-gate 				 * were trying to make the source list empty;
2224*0Sstevel@tonic-gate 				 * so if orig_filter isn't empty, the ilg
2225*0Sstevel@tonic-gate 				 * must still have a filter alloc'd.
2226*0Sstevel@tonic-gate 				 */
2227*0Sstevel@tonic-gate 				l_copy(orig_filter, ilg->ilg_filter);
2228*0Sstevel@tonic-gate 			}
2229*0Sstevel@tonic-gate 		}
2230*0Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
2231*0Sstevel@tonic-gate 	}
2232*0Sstevel@tonic-gate 
2233*0Sstevel@tonic-gate free_and_exit:
2234*0Sstevel@tonic-gate 	l_free(orig_filter);
2235*0Sstevel@tonic-gate 	l_free(new_filter);
2236*0Sstevel@tonic-gate 
2237*0Sstevel@tonic-gate 	return (err);
2238*0Sstevel@tonic-gate }
2239*0Sstevel@tonic-gate 
2240*0Sstevel@tonic-gate static int
2241*0Sstevel@tonic-gate ip_set_srcfilter_v6(conn_t *connp, struct group_filter *gf,
2242*0Sstevel@tonic-gate     const struct in6_addr *grp, ill_t *ill)
2243*0Sstevel@tonic-gate {
2244*0Sstevel@tonic-gate 	ilg_t *ilg;
2245*0Sstevel@tonic-gate 	int i, orig_ifindex, orig_fmode, new_fmode, err;
2246*0Sstevel@tonic-gate 	slist_t *orig_filter = NULL;
2247*0Sstevel@tonic-gate 	slist_t *new_filter = NULL;
2248*0Sstevel@tonic-gate 	struct sockaddr_storage *sl;
2249*0Sstevel@tonic-gate 	struct sockaddr_in6 *sin6;
2250*0Sstevel@tonic-gate 	boolean_t leave_grp;
2251*0Sstevel@tonic-gate 	ilg_stat_t ilgstat;
2252*0Sstevel@tonic-gate 
2253*0Sstevel@tonic-gate 	/* Make sure we can handle the source list */
2254*0Sstevel@tonic-gate 	if (gf->gf_numsrc > MAX_FILTER_SIZE)
2255*0Sstevel@tonic-gate 		return (ENOBUFS);
2256*0Sstevel@tonic-gate 
2257*0Sstevel@tonic-gate 	/*
2258*0Sstevel@tonic-gate 	 * setting the filter to (INCLUDE, NULL) is treated
2259*0Sstevel@tonic-gate 	 * as a request to leave the group.
2260*0Sstevel@tonic-gate 	 */
2261*0Sstevel@tonic-gate 	leave_grp = (gf->gf_fmode == MCAST_INCLUDE && gf->gf_numsrc == 0);
2262*0Sstevel@tonic-gate 
2263*0Sstevel@tonic-gate 	ASSERT(IAM_WRITER_ILL(ill));
2264*0Sstevel@tonic-gate 
2265*0Sstevel@tonic-gate 	/*
2266*0Sstevel@tonic-gate 	 * Use the ifindex to do the lookup.  We can't use the ill
2267*0Sstevel@tonic-gate 	 * directly because ilg_ill could point to a different ill
2268*0Sstevel@tonic-gate 	 * if things have moved.
2269*0Sstevel@tonic-gate 	 */
2270*0Sstevel@tonic-gate 	orig_ifindex = ill->ill_phyint->phyint_ifindex;
2271*0Sstevel@tonic-gate 
2272*0Sstevel@tonic-gate 	mutex_enter(&connp->conn_lock);
2273*0Sstevel@tonic-gate 	ilg = ilg_lookup_ill_index_v6(connp, grp, orig_ifindex);
2274*0Sstevel@tonic-gate 	if (ilg == NULL) {
2275*0Sstevel@tonic-gate 		/*
2276*0Sstevel@tonic-gate 		 * if the request was actually to leave, and we
2277*0Sstevel@tonic-gate 		 * didn't find an ilg, there's nothing to do.
2278*0Sstevel@tonic-gate 		 */
2279*0Sstevel@tonic-gate 		if (!leave_grp)
2280*0Sstevel@tonic-gate 			ilg = conn_ilg_alloc(connp);
2281*0Sstevel@tonic-gate 		if (leave_grp || ilg == NULL) {
2282*0Sstevel@tonic-gate 			mutex_exit(&connp->conn_lock);
2283*0Sstevel@tonic-gate 			return (leave_grp ? 0 : ENOMEM);
2284*0Sstevel@tonic-gate 		}
2285*0Sstevel@tonic-gate 		ilgstat = ILGSTAT_NEW;
2286*0Sstevel@tonic-gate 		ilg->ilg_v6group = *grp;
2287*0Sstevel@tonic-gate 		ilg->ilg_ipif = NULL;
2288*0Sstevel@tonic-gate 		/*
2289*0Sstevel@tonic-gate 		 * Choose our target ill to join on. This might be
2290*0Sstevel@tonic-gate 		 * different from the ill we've been given if it's
2291*0Sstevel@tonic-gate 		 * currently down and part of a group.
2292*0Sstevel@tonic-gate 		 *
2293*0Sstevel@tonic-gate 		 * new ill is not refheld; we are writer.
2294*0Sstevel@tonic-gate 		 */
2295*0Sstevel@tonic-gate 		ill = ip_choose_multi_ill(ill, grp);
2296*0Sstevel@tonic-gate 		ASSERT(!(ill->ill_state_flags & ILL_CONDEMNED));
2297*0Sstevel@tonic-gate 		ilg->ilg_ill = ill;
2298*0Sstevel@tonic-gate 		/*
2299*0Sstevel@tonic-gate 		 * Remember the index that we joined on, so that we can
2300*0Sstevel@tonic-gate 		 * successfully delete them later on and also search for
2301*0Sstevel@tonic-gate 		 * duplicates if the application wants to join again.
2302*0Sstevel@tonic-gate 		 */
2303*0Sstevel@tonic-gate 		ilg->ilg_orig_ifindex = orig_ifindex;
2304*0Sstevel@tonic-gate 	} else if (leave_grp) {
2305*0Sstevel@tonic-gate 		/*
2306*0Sstevel@tonic-gate 		 * Use the ilg's current ill for the deletion,
2307*0Sstevel@tonic-gate 		 * we might have failed over.
2308*0Sstevel@tonic-gate 		 */
2309*0Sstevel@tonic-gate 		ill = ilg->ilg_ill;
2310*0Sstevel@tonic-gate 		ilg_delete(connp, ilg, NULL);
2311*0Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
2312*0Sstevel@tonic-gate 		(void) ip_delmulti_v6(grp, ill, orig_ifindex,
2313*0Sstevel@tonic-gate 		    connp->conn_zoneid, B_FALSE, B_TRUE);
2314*0Sstevel@tonic-gate 		return (0);
2315*0Sstevel@tonic-gate 	} else {
2316*0Sstevel@tonic-gate 		ilgstat = ILGSTAT_CHANGE;
2317*0Sstevel@tonic-gate 		/*
2318*0Sstevel@tonic-gate 		 * The current ill might be different from the one we were
2319*0Sstevel@tonic-gate 		 * asked to join on (if failover has occurred); we should
2320*0Sstevel@tonic-gate 		 * join on the ill stored in the ilg.  The original ill
2321*0Sstevel@tonic-gate 		 * is noted in ilg_orig_ifindex, which matched our request.
2322*0Sstevel@tonic-gate 		 */
2323*0Sstevel@tonic-gate 		ill = ilg->ilg_ill;
2324*0Sstevel@tonic-gate 		/* preserve existing state in case ip_addmulti() fails */
2325*0Sstevel@tonic-gate 		orig_fmode = ilg->ilg_fmode;
2326*0Sstevel@tonic-gate 		if (ilg->ilg_filter == NULL) {
2327*0Sstevel@tonic-gate 			orig_filter = NULL;
2328*0Sstevel@tonic-gate 		} else {
2329*0Sstevel@tonic-gate 			orig_filter = l_alloc_copy(ilg->ilg_filter);
2330*0Sstevel@tonic-gate 			if (orig_filter == NULL) {
2331*0Sstevel@tonic-gate 				mutex_exit(&connp->conn_lock);
2332*0Sstevel@tonic-gate 				return (ENOMEM);
2333*0Sstevel@tonic-gate 			}
2334*0Sstevel@tonic-gate 		}
2335*0Sstevel@tonic-gate 	}
2336*0Sstevel@tonic-gate 
2337*0Sstevel@tonic-gate 	/*
2338*0Sstevel@tonic-gate 	 * Alloc buffer to copy new state into (see below) before
2339*0Sstevel@tonic-gate 	 * we make any changes, so we can bail if it fails.
2340*0Sstevel@tonic-gate 	 */
2341*0Sstevel@tonic-gate 	if ((new_filter = l_alloc()) == NULL) {
2342*0Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
2343*0Sstevel@tonic-gate 		err = ENOMEM;
2344*0Sstevel@tonic-gate 		goto free_and_exit;
2345*0Sstevel@tonic-gate 	}
2346*0Sstevel@tonic-gate 
2347*0Sstevel@tonic-gate 	if (gf->gf_numsrc == 0) {
2348*0Sstevel@tonic-gate 		CLEAR_SLIST(ilg->ilg_filter);
2349*0Sstevel@tonic-gate 	} else {
2350*0Sstevel@tonic-gate 		slist_t *fp;
2351*0Sstevel@tonic-gate 		if (ilg->ilg_filter == NULL) {
2352*0Sstevel@tonic-gate 			fp = l_alloc();
2353*0Sstevel@tonic-gate 			if (fp == NULL) {
2354*0Sstevel@tonic-gate 				if (ilgstat == ILGSTAT_NEW)
2355*0Sstevel@tonic-gate 					ilg_delete(connp, ilg, NULL);
2356*0Sstevel@tonic-gate 				mutex_exit(&connp->conn_lock);
2357*0Sstevel@tonic-gate 				err = ENOMEM;
2358*0Sstevel@tonic-gate 				goto free_and_exit;
2359*0Sstevel@tonic-gate 			}
2360*0Sstevel@tonic-gate 		} else {
2361*0Sstevel@tonic-gate 			fp = ilg->ilg_filter;
2362*0Sstevel@tonic-gate 		}
2363*0Sstevel@tonic-gate 		for (i = 0, sl = gf->gf_slist; i < gf->gf_numsrc; i++, sl++) {
2364*0Sstevel@tonic-gate 			sin6 = (struct sockaddr_in6 *)sl;
2365*0Sstevel@tonic-gate 			fp->sl_addr[i] = sin6->sin6_addr;
2366*0Sstevel@tonic-gate 		}
2367*0Sstevel@tonic-gate 		fp->sl_numsrc = gf->gf_numsrc;
2368*0Sstevel@tonic-gate 		ilg->ilg_filter = fp;
2369*0Sstevel@tonic-gate 	}
2370*0Sstevel@tonic-gate 	/*
2371*0Sstevel@tonic-gate 	 * In the kernel, we use the state definitions MODE_IS_[IN|EX]CLUDE
2372*0Sstevel@tonic-gate 	 * to identify the filter mode; but the API uses MCAST_[IN|EX]CLUDE.
2373*0Sstevel@tonic-gate 	 * So we need to translate here.
2374*0Sstevel@tonic-gate 	 */
2375*0Sstevel@tonic-gate 	ilg->ilg_fmode = (gf->gf_fmode == MCAST_INCLUDE) ?
2376*0Sstevel@tonic-gate 	    MODE_IS_INCLUDE : MODE_IS_EXCLUDE;
2377*0Sstevel@tonic-gate 
2378*0Sstevel@tonic-gate 	/*
2379*0Sstevel@tonic-gate 	 * Save copy of ilg's filter state to pass to other functions,
2380*0Sstevel@tonic-gate 	 * so we can release conn_lock now.
2381*0Sstevel@tonic-gate 	 */
2382*0Sstevel@tonic-gate 	new_fmode = ilg->ilg_fmode;
2383*0Sstevel@tonic-gate 	l_copy(ilg->ilg_filter, new_filter);
2384*0Sstevel@tonic-gate 
2385*0Sstevel@tonic-gate 	mutex_exit(&connp->conn_lock);
2386*0Sstevel@tonic-gate 
2387*0Sstevel@tonic-gate 	err = ip_addmulti_v6(grp, ill, orig_ifindex, connp->conn_zoneid,
2388*0Sstevel@tonic-gate 	    ilgstat, new_fmode, new_filter);
2389*0Sstevel@tonic-gate 	if (err != 0) {
2390*0Sstevel@tonic-gate 		/*
2391*0Sstevel@tonic-gate 		 * Restore the original filter state, or delete the
2392*0Sstevel@tonic-gate 		 * newly-created ilg.  We need to look up the ilg
2393*0Sstevel@tonic-gate 		 * again, though, since we've not been holding the
2394*0Sstevel@tonic-gate 		 * conn_lock.
2395*0Sstevel@tonic-gate 		 */
2396*0Sstevel@tonic-gate 		mutex_enter(&connp->conn_lock);
2397*0Sstevel@tonic-gate 		ilg = ilg_lookup_ill_index_v6(connp, grp, orig_ifindex);
2398*0Sstevel@tonic-gate 		ASSERT(ilg != NULL);
2399*0Sstevel@tonic-gate 		if (ilgstat == ILGSTAT_NEW) {
2400*0Sstevel@tonic-gate 			ilg_delete(connp, ilg, NULL);
2401*0Sstevel@tonic-gate 		} else {
2402*0Sstevel@tonic-gate 			ilg->ilg_fmode = orig_fmode;
2403*0Sstevel@tonic-gate 			if (SLIST_IS_EMPTY(orig_filter)) {
2404*0Sstevel@tonic-gate 				CLEAR_SLIST(ilg->ilg_filter);
2405*0Sstevel@tonic-gate 			} else {
2406*0Sstevel@tonic-gate 				/*
2407*0Sstevel@tonic-gate 				 * We didn't free the filter, even if we
2408*0Sstevel@tonic-gate 				 * were trying to make the source list empty;
2409*0Sstevel@tonic-gate 				 * so if orig_filter isn't empty, the ilg
2410*0Sstevel@tonic-gate 				 * must still have a filter alloc'd.
2411*0Sstevel@tonic-gate 				 */
2412*0Sstevel@tonic-gate 				l_copy(orig_filter, ilg->ilg_filter);
2413*0Sstevel@tonic-gate 			}
2414*0Sstevel@tonic-gate 		}
2415*0Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
2416*0Sstevel@tonic-gate 	}
2417*0Sstevel@tonic-gate 
2418*0Sstevel@tonic-gate free_and_exit:
2419*0Sstevel@tonic-gate 	l_free(orig_filter);
2420*0Sstevel@tonic-gate 	l_free(new_filter);
2421*0Sstevel@tonic-gate 
2422*0Sstevel@tonic-gate 	return (err);
2423*0Sstevel@tonic-gate }
2424*0Sstevel@tonic-gate 
2425*0Sstevel@tonic-gate /*
2426*0Sstevel@tonic-gate  * Process the SIOC[GS]MSFILTER and SIOC[GS]IPMSFILTER ioctls.
2427*0Sstevel@tonic-gate  */
2428*0Sstevel@tonic-gate /* ARGSUSED */
2429*0Sstevel@tonic-gate int
2430*0Sstevel@tonic-gate ip_sioctl_msfilter(ipif_t *ipif, sin_t *dummy_sin, queue_t *q, mblk_t *mp,
2431*0Sstevel@tonic-gate     ip_ioctl_cmd_t *ipip, void *ifreq)
2432*0Sstevel@tonic-gate {
2433*0Sstevel@tonic-gate 	struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
2434*0Sstevel@tonic-gate 	/* existence verified in ip_wput_nondata() */
2435*0Sstevel@tonic-gate 	mblk_t *data_mp = mp->b_cont->b_cont;
2436*0Sstevel@tonic-gate 	int datalen, err, cmd, minsize;
2437*0Sstevel@tonic-gate 	int expsize = 0;
2438*0Sstevel@tonic-gate 	conn_t *connp;
2439*0Sstevel@tonic-gate 	boolean_t isv6, is_v4only_api, getcmd;
2440*0Sstevel@tonic-gate 	struct sockaddr_in *gsin;
2441*0Sstevel@tonic-gate 	struct sockaddr_in6 *gsin6;
2442*0Sstevel@tonic-gate 	ipaddr_t v4grp;
2443*0Sstevel@tonic-gate 	in6_addr_t v6grp;
2444*0Sstevel@tonic-gate 	struct group_filter *gf = NULL;
2445*0Sstevel@tonic-gate 	struct ip_msfilter *imsf = NULL;
2446*0Sstevel@tonic-gate 	mblk_t *ndp;
2447*0Sstevel@tonic-gate 
2448*0Sstevel@tonic-gate 	if (data_mp->b_cont != NULL) {
2449*0Sstevel@tonic-gate 		if ((ndp = msgpullup(data_mp, -1)) == NULL)
2450*0Sstevel@tonic-gate 			return (ENOMEM);
2451*0Sstevel@tonic-gate 		freemsg(data_mp);
2452*0Sstevel@tonic-gate 		data_mp = ndp;
2453*0Sstevel@tonic-gate 		mp->b_cont->b_cont = data_mp;
2454*0Sstevel@tonic-gate 	}
2455*0Sstevel@tonic-gate 
2456*0Sstevel@tonic-gate 	cmd = iocp->ioc_cmd;
2457*0Sstevel@tonic-gate 	getcmd = (cmd == SIOCGIPMSFILTER || cmd == SIOCGMSFILTER);
2458*0Sstevel@tonic-gate 	is_v4only_api = (cmd == SIOCGIPMSFILTER || cmd == SIOCSIPMSFILTER);
2459*0Sstevel@tonic-gate 	minsize = (is_v4only_api) ? IP_MSFILTER_SIZE(0) : GROUP_FILTER_SIZE(0);
2460*0Sstevel@tonic-gate 	datalen = MBLKL(data_mp);
2461*0Sstevel@tonic-gate 
2462*0Sstevel@tonic-gate 	if (datalen < minsize)
2463*0Sstevel@tonic-gate 		return (EINVAL);
2464*0Sstevel@tonic-gate 
2465*0Sstevel@tonic-gate 	/*
2466*0Sstevel@tonic-gate 	 * now we know we have at least have the initial structure,
2467*0Sstevel@tonic-gate 	 * but need to check for the source list array.
2468*0Sstevel@tonic-gate 	 */
2469*0Sstevel@tonic-gate 	if (is_v4only_api) {
2470*0Sstevel@tonic-gate 		imsf = (struct ip_msfilter *)data_mp->b_rptr;
2471*0Sstevel@tonic-gate 		isv6 = B_FALSE;
2472*0Sstevel@tonic-gate 		expsize = IP_MSFILTER_SIZE(imsf->imsf_numsrc);
2473*0Sstevel@tonic-gate 	} else {
2474*0Sstevel@tonic-gate 		gf = (struct group_filter *)data_mp->b_rptr;
2475*0Sstevel@tonic-gate 		if (gf->gf_group.ss_family == AF_INET6) {
2476*0Sstevel@tonic-gate 			gsin6 = (struct sockaddr_in6 *)&gf->gf_group;
2477*0Sstevel@tonic-gate 			isv6 = !(IN6_IS_ADDR_V4MAPPED(&gsin6->sin6_addr));
2478*0Sstevel@tonic-gate 		} else {
2479*0Sstevel@tonic-gate 			isv6 = B_FALSE;
2480*0Sstevel@tonic-gate 		}
2481*0Sstevel@tonic-gate 		expsize = GROUP_FILTER_SIZE(gf->gf_numsrc);
2482*0Sstevel@tonic-gate 	}
2483*0Sstevel@tonic-gate 	if (datalen < expsize)
2484*0Sstevel@tonic-gate 		return (EINVAL);
2485*0Sstevel@tonic-gate 
2486*0Sstevel@tonic-gate 	connp = Q_TO_CONN(q);
2487*0Sstevel@tonic-gate 
2488*0Sstevel@tonic-gate 	/* operation not supported on the virtual network interface */
2489*0Sstevel@tonic-gate 	if (IS_VNI(ipif->ipif_ill))
2490*0Sstevel@tonic-gate 		return (EINVAL);
2491*0Sstevel@tonic-gate 
2492*0Sstevel@tonic-gate 	if (isv6) {
2493*0Sstevel@tonic-gate 		ill_t *ill = ipif->ipif_ill;
2494*0Sstevel@tonic-gate 		ill_refhold(ill);
2495*0Sstevel@tonic-gate 
2496*0Sstevel@tonic-gate 		gsin6 = (struct sockaddr_in6 *)&gf->gf_group;
2497*0Sstevel@tonic-gate 		v6grp = gsin6->sin6_addr;
2498*0Sstevel@tonic-gate 		if (getcmd)
2499*0Sstevel@tonic-gate 			err = ip_get_srcfilter_v6(connp, gf, &v6grp, ill);
2500*0Sstevel@tonic-gate 		else
2501*0Sstevel@tonic-gate 			err = ip_set_srcfilter_v6(connp, gf, &v6grp, ill);
2502*0Sstevel@tonic-gate 
2503*0Sstevel@tonic-gate 		ill_refrele(ill);
2504*0Sstevel@tonic-gate 	} else {
2505*0Sstevel@tonic-gate 		boolean_t isv4mapped = B_FALSE;
2506*0Sstevel@tonic-gate 		if (is_v4only_api) {
2507*0Sstevel@tonic-gate 			v4grp = (ipaddr_t)imsf->imsf_multiaddr.s_addr;
2508*0Sstevel@tonic-gate 		} else {
2509*0Sstevel@tonic-gate 			if (gf->gf_group.ss_family == AF_INET) {
2510*0Sstevel@tonic-gate 				gsin = (struct sockaddr_in *)&gf->gf_group;
2511*0Sstevel@tonic-gate 				v4grp = (ipaddr_t)gsin->sin_addr.s_addr;
2512*0Sstevel@tonic-gate 			} else {
2513*0Sstevel@tonic-gate 				gsin6 = (struct sockaddr_in6 *)&gf->gf_group;
2514*0Sstevel@tonic-gate 				IN6_V4MAPPED_TO_IPADDR(&gsin6->sin6_addr,
2515*0Sstevel@tonic-gate 				    v4grp);
2516*0Sstevel@tonic-gate 				isv4mapped = B_TRUE;
2517*0Sstevel@tonic-gate 			}
2518*0Sstevel@tonic-gate 		}
2519*0Sstevel@tonic-gate 		if (getcmd)
2520*0Sstevel@tonic-gate 			err = ip_get_srcfilter(connp, gf, imsf, v4grp, ipif,
2521*0Sstevel@tonic-gate 			    isv4mapped);
2522*0Sstevel@tonic-gate 		else
2523*0Sstevel@tonic-gate 			err = ip_set_srcfilter(connp, gf, imsf, v4grp, ipif,
2524*0Sstevel@tonic-gate 			    isv4mapped);
2525*0Sstevel@tonic-gate 	}
2526*0Sstevel@tonic-gate 
2527*0Sstevel@tonic-gate 	return (err);
2528*0Sstevel@tonic-gate }
2529*0Sstevel@tonic-gate 
2530*0Sstevel@tonic-gate /*
2531*0Sstevel@tonic-gate  * Finds the ipif based on information in the ioctl headers.  Needed to make
2532*0Sstevel@tonic-gate  * ip_process_ioctl() happy (it needs to know the ipif for IPI_WR-flagged
2533*0Sstevel@tonic-gate  * ioctls prior to calling the ioctl's handler function).  Somewhat analogous
2534*0Sstevel@tonic-gate  * to ip_extract_lifreq_cmn() and ip_extract_tunreq().
2535*0Sstevel@tonic-gate  */
2536*0Sstevel@tonic-gate int
2537*0Sstevel@tonic-gate ip_extract_msfilter(queue_t *q, mblk_t *mp, ipif_t **ipifpp, ipsq_func_t func)
2538*0Sstevel@tonic-gate {
2539*0Sstevel@tonic-gate 	struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
2540*0Sstevel@tonic-gate 	int cmd = iocp->ioc_cmd, err = 0;
2541*0Sstevel@tonic-gate 	conn_t *connp;
2542*0Sstevel@tonic-gate 	ipif_t *ipif;
2543*0Sstevel@tonic-gate 	/* caller has verified this mblk exists */
2544*0Sstevel@tonic-gate 	char *dbuf = (char *)mp->b_cont->b_cont->b_rptr;
2545*0Sstevel@tonic-gate 	struct ip_msfilter *imsf;
2546*0Sstevel@tonic-gate 	struct group_filter *gf;
2547*0Sstevel@tonic-gate 	ipaddr_t v4addr, v4grp;
2548*0Sstevel@tonic-gate 	in6_addr_t v6grp;
2549*0Sstevel@tonic-gate 	uint32_t index;
2550*0Sstevel@tonic-gate 	zoneid_t zoneid;
2551*0Sstevel@tonic-gate 
2552*0Sstevel@tonic-gate 	connp = Q_TO_CONN(q);
2553*0Sstevel@tonic-gate 	zoneid = connp->conn_zoneid;
2554*0Sstevel@tonic-gate 
2555*0Sstevel@tonic-gate 	/* don't allow multicast operations on a tcp conn */
2556*0Sstevel@tonic-gate 	if (IS_TCP_CONN(connp))
2557*0Sstevel@tonic-gate 		return (ENOPROTOOPT);
2558*0Sstevel@tonic-gate 
2559*0Sstevel@tonic-gate 	if (cmd == SIOCSIPMSFILTER || cmd == SIOCGIPMSFILTER) {
2560*0Sstevel@tonic-gate 		/* don't allow v4-specific ioctls on v6 socket */
2561*0Sstevel@tonic-gate 		if (connp->conn_af_isv6)
2562*0Sstevel@tonic-gate 			return (EAFNOSUPPORT);
2563*0Sstevel@tonic-gate 
2564*0Sstevel@tonic-gate 		imsf = (struct ip_msfilter *)dbuf;
2565*0Sstevel@tonic-gate 		v4addr = imsf->imsf_interface.s_addr;
2566*0Sstevel@tonic-gate 		v4grp = imsf->imsf_multiaddr.s_addr;
2567*0Sstevel@tonic-gate 		if (v4addr == INADDR_ANY) {
2568*0Sstevel@tonic-gate 			ipif = ipif_lookup_group(v4grp, zoneid);
2569*0Sstevel@tonic-gate 			if (ipif == NULL)
2570*0Sstevel@tonic-gate 				err = EADDRNOTAVAIL;
2571*0Sstevel@tonic-gate 		} else {
2572*0Sstevel@tonic-gate 			ipif = ipif_lookup_addr(v4addr, NULL, zoneid, q, mp,
2573*0Sstevel@tonic-gate 			    func, &err);
2574*0Sstevel@tonic-gate 		}
2575*0Sstevel@tonic-gate 	} else {
2576*0Sstevel@tonic-gate 		boolean_t isv6 = B_FALSE;
2577*0Sstevel@tonic-gate 		gf = (struct group_filter *)dbuf;
2578*0Sstevel@tonic-gate 		index = gf->gf_interface;
2579*0Sstevel@tonic-gate 		if (gf->gf_group.ss_family == AF_INET6) {
2580*0Sstevel@tonic-gate 			struct sockaddr_in6 *sin6;
2581*0Sstevel@tonic-gate 			sin6 = (struct sockaddr_in6 *)&gf->gf_group;
2582*0Sstevel@tonic-gate 			v6grp = sin6->sin6_addr;
2583*0Sstevel@tonic-gate 			if (IN6_IS_ADDR_V4MAPPED(&v6grp))
2584*0Sstevel@tonic-gate 				IN6_V4MAPPED_TO_IPADDR(&v6grp, v4grp);
2585*0Sstevel@tonic-gate 			else
2586*0Sstevel@tonic-gate 				isv6 = B_TRUE;
2587*0Sstevel@tonic-gate 		} else if (gf->gf_group.ss_family == AF_INET) {
2588*0Sstevel@tonic-gate 			struct sockaddr_in *sin;
2589*0Sstevel@tonic-gate 			sin = (struct sockaddr_in *)&gf->gf_group;
2590*0Sstevel@tonic-gate 			v4grp = sin->sin_addr.s_addr;
2591*0Sstevel@tonic-gate 		} else {
2592*0Sstevel@tonic-gate 			return (EAFNOSUPPORT);
2593*0Sstevel@tonic-gate 		}
2594*0Sstevel@tonic-gate 		if (index == 0) {
2595*0Sstevel@tonic-gate 			if (isv6)
2596*0Sstevel@tonic-gate 				ipif = ipif_lookup_group_v6(&v6grp, zoneid);
2597*0Sstevel@tonic-gate 			else
2598*0Sstevel@tonic-gate 				ipif = ipif_lookup_group(v4grp, zoneid);
2599*0Sstevel@tonic-gate 			if (ipif == NULL)
2600*0Sstevel@tonic-gate 				err = EADDRNOTAVAIL;
2601*0Sstevel@tonic-gate 		} else {
2602*0Sstevel@tonic-gate 			ipif = ipif_lookup_on_ifindex(index, isv6, zoneid,
2603*0Sstevel@tonic-gate 			    q, mp, func, &err);
2604*0Sstevel@tonic-gate 		}
2605*0Sstevel@tonic-gate 	}
2606*0Sstevel@tonic-gate 
2607*0Sstevel@tonic-gate 	*ipifpp = ipif;
2608*0Sstevel@tonic-gate 	return (err);
2609*0Sstevel@tonic-gate }
2610*0Sstevel@tonic-gate 
2611*0Sstevel@tonic-gate /*
2612*0Sstevel@tonic-gate  * The structures used for the SIOC*MSFILTER ioctls usually must be copied
2613*0Sstevel@tonic-gate  * in in two stages, as the first copyin tells us the size of the attached
2614*0Sstevel@tonic-gate  * source buffer.  This function is called by ip_wput_nondata() after the
2615*0Sstevel@tonic-gate  * first copyin has completed; it figures out how big the second stage
2616*0Sstevel@tonic-gate  * needs to be, and kicks it off.
2617*0Sstevel@tonic-gate  *
2618*0Sstevel@tonic-gate  * In some cases (numsrc < 2), the second copyin is not needed as the
2619*0Sstevel@tonic-gate  * first one gets a complete structure containing 1 source addr.
2620*0Sstevel@tonic-gate  *
2621*0Sstevel@tonic-gate  * The function returns 0 if a second copyin has been started (i.e. there's
2622*0Sstevel@tonic-gate  * no more work to be done right now), or 1 if the second copyin is not
2623*0Sstevel@tonic-gate  * needed and ip_wput_nondata() can continue its processing.
2624*0Sstevel@tonic-gate  */
2625*0Sstevel@tonic-gate int
2626*0Sstevel@tonic-gate ip_copyin_msfilter(queue_t *q, mblk_t *mp)
2627*0Sstevel@tonic-gate {
2628*0Sstevel@tonic-gate 	struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
2629*0Sstevel@tonic-gate 	int cmd = iocp->ioc_cmd;
2630*0Sstevel@tonic-gate 	/* validity of this checked in ip_wput_nondata() */
2631*0Sstevel@tonic-gate 	mblk_t *mp1 = mp->b_cont->b_cont;
2632*0Sstevel@tonic-gate 	int copysize = 0;
2633*0Sstevel@tonic-gate 	int offset;
2634*0Sstevel@tonic-gate 
2635*0Sstevel@tonic-gate 	if (cmd == SIOCSMSFILTER || cmd == SIOCGMSFILTER) {
2636*0Sstevel@tonic-gate 		struct group_filter *gf = (struct group_filter *)mp1->b_rptr;
2637*0Sstevel@tonic-gate 		if (gf->gf_numsrc >= 2) {
2638*0Sstevel@tonic-gate 			offset = sizeof (struct group_filter);
2639*0Sstevel@tonic-gate 			copysize = GROUP_FILTER_SIZE(gf->gf_numsrc) - offset;
2640*0Sstevel@tonic-gate 		}
2641*0Sstevel@tonic-gate 	} else {
2642*0Sstevel@tonic-gate 		struct ip_msfilter *imsf = (struct ip_msfilter *)mp1->b_rptr;
2643*0Sstevel@tonic-gate 		if (imsf->imsf_numsrc >= 2) {
2644*0Sstevel@tonic-gate 			offset = sizeof (struct ip_msfilter);
2645*0Sstevel@tonic-gate 			copysize = IP_MSFILTER_SIZE(imsf->imsf_numsrc) - offset;
2646*0Sstevel@tonic-gate 		}
2647*0Sstevel@tonic-gate 	}
2648*0Sstevel@tonic-gate 	if (copysize > 0) {
2649*0Sstevel@tonic-gate 		mi_copyin_n(q, mp, offset, copysize);
2650*0Sstevel@tonic-gate 		return (0);
2651*0Sstevel@tonic-gate 	}
2652*0Sstevel@tonic-gate 	return (1);
2653*0Sstevel@tonic-gate }
2654*0Sstevel@tonic-gate 
2655*0Sstevel@tonic-gate /*
2656*0Sstevel@tonic-gate  * Handle the following optmgmt:
2657*0Sstevel@tonic-gate  *	IP_ADD_MEMBERSHIP		must not have joined already
2658*0Sstevel@tonic-gate  *	MCAST_JOIN_GROUP		must not have joined already
2659*0Sstevel@tonic-gate  *	IP_BLOCK_SOURCE			must have joined already
2660*0Sstevel@tonic-gate  *	MCAST_BLOCK_SOURCE		must have joined already
2661*0Sstevel@tonic-gate  *	IP_JOIN_SOURCE_GROUP		may have joined already
2662*0Sstevel@tonic-gate  *	MCAST_JOIN_SOURCE_GROUP		may have joined already
2663*0Sstevel@tonic-gate  *
2664*0Sstevel@tonic-gate  * fmode and src parameters may be used to determine which option is
2665*0Sstevel@tonic-gate  * being set, as follows (the IP_* and MCAST_* versions of each option
2666*0Sstevel@tonic-gate  * are functionally equivalent):
2667*0Sstevel@tonic-gate  *	opt			fmode			src
2668*0Sstevel@tonic-gate  *	IP_ADD_MEMBERSHIP	MODE_IS_EXCLUDE		INADDR_ANY
2669*0Sstevel@tonic-gate  *	MCAST_JOIN_GROUP	MODE_IS_EXCLUDE		INADDR_ANY
2670*0Sstevel@tonic-gate  *	IP_BLOCK_SOURCE		MODE_IS_EXCLUDE		v4 addr
2671*0Sstevel@tonic-gate  *	MCAST_BLOCK_SOURCE	MODE_IS_EXCLUDE		v4 addr
2672*0Sstevel@tonic-gate  *	IP_JOIN_SOURCE_GROUP	MODE_IS_INCLUDE		v4 addr
2673*0Sstevel@tonic-gate  *	MCAST_JOIN_SOURCE_GROUP	MODE_IS_INCLUDE		v4 addr
2674*0Sstevel@tonic-gate  *
2675*0Sstevel@tonic-gate  * Changing the filter mode is not allowed; if a matching ilg already
2676*0Sstevel@tonic-gate  * exists and fmode != ilg->ilg_fmode, EINVAL is returned.
2677*0Sstevel@tonic-gate  *
2678*0Sstevel@tonic-gate  * Verifies that there is a source address of appropriate scope for
2679*0Sstevel@tonic-gate  * the group; if not, EADDRNOTAVAIL is returned.
2680*0Sstevel@tonic-gate  *
2681*0Sstevel@tonic-gate  * The interface to be used may be identified by an address or by an
2682*0Sstevel@tonic-gate  * index.  A pointer to the index is passed; if it is NULL, use the
2683*0Sstevel@tonic-gate  * address, otherwise, use the index.
2684*0Sstevel@tonic-gate  */
2685*0Sstevel@tonic-gate int
2686*0Sstevel@tonic-gate ip_opt_add_group(conn_t *connp, boolean_t checkonly, ipaddr_t group,
2687*0Sstevel@tonic-gate     ipaddr_t ifaddr, uint_t *ifindexp, mcast_record_t fmode, ipaddr_t src,
2688*0Sstevel@tonic-gate     mblk_t *first_mp)
2689*0Sstevel@tonic-gate {
2690*0Sstevel@tonic-gate 	ipif_t	*ipif;
2691*0Sstevel@tonic-gate 	ipsq_t	*ipsq;
2692*0Sstevel@tonic-gate 	int err = 0;
2693*0Sstevel@tonic-gate 	ill_t	*ill;
2694*0Sstevel@tonic-gate 
2695*0Sstevel@tonic-gate 	err = ip_opt_check(connp, group, src, ifaddr, ifindexp, first_mp,
2696*0Sstevel@tonic-gate 	    ip_restart_optmgmt, &ipif);
2697*0Sstevel@tonic-gate 	if (err != 0) {
2698*0Sstevel@tonic-gate 		if (err != EINPROGRESS) {
2699*0Sstevel@tonic-gate 			ip1dbg(("ip_opt_add_group: no ipif for group 0x%x, "
2700*0Sstevel@tonic-gate 			    "ifaddr 0x%x, ifindex %d\n", ntohl(group),
2701*0Sstevel@tonic-gate 			    ntohl(ifaddr), (ifindexp == NULL) ? 0 : *ifindexp));
2702*0Sstevel@tonic-gate 		}
2703*0Sstevel@tonic-gate 		return (err);
2704*0Sstevel@tonic-gate 	}
2705*0Sstevel@tonic-gate 	ASSERT(ipif != NULL);
2706*0Sstevel@tonic-gate 
2707*0Sstevel@tonic-gate 	ill = ipif->ipif_ill;
2708*0Sstevel@tonic-gate 	/* Operation not supported on a virtual network interface */
2709*0Sstevel@tonic-gate 	if (IS_VNI(ill)) {
2710*0Sstevel@tonic-gate 		ipif_refrele(ipif);
2711*0Sstevel@tonic-gate 		return (EINVAL);
2712*0Sstevel@tonic-gate 	}
2713*0Sstevel@tonic-gate 
2714*0Sstevel@tonic-gate 	if (checkonly) {
2715*0Sstevel@tonic-gate 		/*
2716*0Sstevel@tonic-gate 		 * do not do operation, just pretend to - new T_CHECK
2717*0Sstevel@tonic-gate 		 * semantics. The error return case above if encountered
2718*0Sstevel@tonic-gate 		 * considered a good enough "check" here.
2719*0Sstevel@tonic-gate 		 */
2720*0Sstevel@tonic-gate 		ipif_refrele(ipif);
2721*0Sstevel@tonic-gate 		return (0);
2722*0Sstevel@tonic-gate 	}
2723*0Sstevel@tonic-gate 
2724*0Sstevel@tonic-gate 	IPSQ_ENTER_IPIF(ipif, connp, first_mp, ip_restart_optmgmt, ipsq,
2725*0Sstevel@tonic-gate 	    NEW_OP);
2726*0Sstevel@tonic-gate 
2727*0Sstevel@tonic-gate 	/* unspecified source addr => no source filtering */
2728*0Sstevel@tonic-gate 	err = ilg_add(connp, group, ipif, fmode, src);
2729*0Sstevel@tonic-gate 
2730*0Sstevel@tonic-gate 	IPSQ_EXIT(ipsq);
2731*0Sstevel@tonic-gate 
2732*0Sstevel@tonic-gate 	ipif_refrele(ipif);
2733*0Sstevel@tonic-gate 	return (err);
2734*0Sstevel@tonic-gate }
2735*0Sstevel@tonic-gate 
2736*0Sstevel@tonic-gate /*
2737*0Sstevel@tonic-gate  * Handle the following optmgmt:
2738*0Sstevel@tonic-gate  *	IPV6_JOIN_GROUP			must not have joined already
2739*0Sstevel@tonic-gate  *	MCAST_JOIN_GROUP		must not have joined already
2740*0Sstevel@tonic-gate  *	MCAST_BLOCK_SOURCE		must have joined already
2741*0Sstevel@tonic-gate  *	MCAST_JOIN_SOURCE_GROUP		may have joined already
2742*0Sstevel@tonic-gate  *
2743*0Sstevel@tonic-gate  * fmode and src parameters may be used to determine which option is
2744*0Sstevel@tonic-gate  * being set, as follows (IPV6_JOIN_GROUP and MCAST_JOIN_GROUP options
2745*0Sstevel@tonic-gate  * are functionally equivalent):
2746*0Sstevel@tonic-gate  *	opt			fmode			v6src
2747*0Sstevel@tonic-gate  *	IPV6_JOIN_GROUP		MODE_IS_EXCLUDE		unspecified
2748*0Sstevel@tonic-gate  *	MCAST_JOIN_GROUP	MODE_IS_EXCLUDE		unspecified
2749*0Sstevel@tonic-gate  *	MCAST_BLOCK_SOURCE	MODE_IS_EXCLUDE		v6 addr
2750*0Sstevel@tonic-gate  *	MCAST_JOIN_SOURCE_GROUP	MODE_IS_INCLUDE		v6 addr
2751*0Sstevel@tonic-gate  *
2752*0Sstevel@tonic-gate  * Changing the filter mode is not allowed; if a matching ilg already
2753*0Sstevel@tonic-gate  * exists and fmode != ilg->ilg_fmode, EINVAL is returned.
2754*0Sstevel@tonic-gate  *
2755*0Sstevel@tonic-gate  * Verifies that there is a source address of appropriate scope for
2756*0Sstevel@tonic-gate  * the group; if not, EADDRNOTAVAIL is returned.
2757*0Sstevel@tonic-gate  *
2758*0Sstevel@tonic-gate  * Handles IPv4-mapped IPv6 multicast addresses by associating them
2759*0Sstevel@tonic-gate  * with the link-local ipif.  Assumes that if v6group is v4-mapped,
2760*0Sstevel@tonic-gate  * v6src is also v4-mapped.
2761*0Sstevel@tonic-gate  */
2762*0Sstevel@tonic-gate int
2763*0Sstevel@tonic-gate ip_opt_add_group_v6(conn_t *connp, boolean_t checkonly,
2764*0Sstevel@tonic-gate     const in6_addr_t *v6group, int ifindex, mcast_record_t fmode,
2765*0Sstevel@tonic-gate     const in6_addr_t *v6src, mblk_t *first_mp)
2766*0Sstevel@tonic-gate {
2767*0Sstevel@tonic-gate 	ill_t *ill;
2768*0Sstevel@tonic-gate 	ipif_t	*ipif;
2769*0Sstevel@tonic-gate 	char buf[INET6_ADDRSTRLEN];
2770*0Sstevel@tonic-gate 	ipaddr_t v4group, v4src;
2771*0Sstevel@tonic-gate 	boolean_t isv6;
2772*0Sstevel@tonic-gate 	ipsq_t	*ipsq;
2773*0Sstevel@tonic-gate 	int	err;
2774*0Sstevel@tonic-gate 
2775*0Sstevel@tonic-gate 	err = ip_opt_check_v6(connp, v6group, &v4group, v6src, &v4src, &isv6,
2776*0Sstevel@tonic-gate 	    ifindex, first_mp, ip_restart_optmgmt, &ill, &ipif);
2777*0Sstevel@tonic-gate 	if (err != 0) {
2778*0Sstevel@tonic-gate 		if (err != EINPROGRESS) {
2779*0Sstevel@tonic-gate 			ip1dbg(("ip_opt_add_group_v6: no ill for group %s/"
2780*0Sstevel@tonic-gate 			    "index %d\n", inet_ntop(AF_INET6, v6group, buf,
2781*0Sstevel@tonic-gate 			    sizeof (buf)), ifindex));
2782*0Sstevel@tonic-gate 		}
2783*0Sstevel@tonic-gate 		return (err);
2784*0Sstevel@tonic-gate 	}
2785*0Sstevel@tonic-gate 	ASSERT((!isv6 && ipif != NULL) || (isv6 && ill != NULL));
2786*0Sstevel@tonic-gate 
2787*0Sstevel@tonic-gate 	/* operation is not supported on the virtual network interface */
2788*0Sstevel@tonic-gate 	if (isv6) {
2789*0Sstevel@tonic-gate 		if (IS_VNI(ill)) {
2790*0Sstevel@tonic-gate 			ill_refrele(ill);
2791*0Sstevel@tonic-gate 			return (EINVAL);
2792*0Sstevel@tonic-gate 		}
2793*0Sstevel@tonic-gate 	} else {
2794*0Sstevel@tonic-gate 		if (IS_VNI(ipif->ipif_ill)) {
2795*0Sstevel@tonic-gate 			ipif_refrele(ipif);
2796*0Sstevel@tonic-gate 			return (EINVAL);
2797*0Sstevel@tonic-gate 		}
2798*0Sstevel@tonic-gate 	}
2799*0Sstevel@tonic-gate 
2800*0Sstevel@tonic-gate 	if (checkonly) {
2801*0Sstevel@tonic-gate 		/*
2802*0Sstevel@tonic-gate 		 * do not do operation, just pretend to - new T_CHECK
2803*0Sstevel@tonic-gate 		 * semantics. The error return case above if encountered
2804*0Sstevel@tonic-gate 		 * considered a good enough "check" here.
2805*0Sstevel@tonic-gate 		 */
2806*0Sstevel@tonic-gate 		if (isv6)
2807*0Sstevel@tonic-gate 			ill_refrele(ill);
2808*0Sstevel@tonic-gate 		else
2809*0Sstevel@tonic-gate 			ipif_refrele(ipif);
2810*0Sstevel@tonic-gate 		return (0);
2811*0Sstevel@tonic-gate 	}
2812*0Sstevel@tonic-gate 
2813*0Sstevel@tonic-gate 	if (!isv6) {
2814*0Sstevel@tonic-gate 		IPSQ_ENTER_IPIF(ipif, connp, first_mp, ip_restart_optmgmt,
2815*0Sstevel@tonic-gate 		    ipsq, NEW_OP);
2816*0Sstevel@tonic-gate 		err = ilg_add(connp, v4group, ipif, fmode, v4src);
2817*0Sstevel@tonic-gate 		IPSQ_EXIT(ipsq);
2818*0Sstevel@tonic-gate 		ipif_refrele(ipif);
2819*0Sstevel@tonic-gate 	} else {
2820*0Sstevel@tonic-gate 		IPSQ_ENTER_ILL(ill, connp, first_mp, ip_restart_optmgmt,
2821*0Sstevel@tonic-gate 		    ipsq, NEW_OP);
2822*0Sstevel@tonic-gate 		err = ilg_add_v6(connp, v6group, ill, fmode, v6src);
2823*0Sstevel@tonic-gate 		IPSQ_EXIT(ipsq);
2824*0Sstevel@tonic-gate 		ill_refrele(ill);
2825*0Sstevel@tonic-gate 	}
2826*0Sstevel@tonic-gate 
2827*0Sstevel@tonic-gate 	return (err);
2828*0Sstevel@tonic-gate }
2829*0Sstevel@tonic-gate 
2830*0Sstevel@tonic-gate static int
2831*0Sstevel@tonic-gate ip_opt_delete_group_excl(conn_t *connp, ipaddr_t group, ipif_t *ipif,
2832*0Sstevel@tonic-gate     mcast_record_t fmode, ipaddr_t src)
2833*0Sstevel@tonic-gate {
2834*0Sstevel@tonic-gate 	ilg_t	*ilg;
2835*0Sstevel@tonic-gate 	in6_addr_t v6src;
2836*0Sstevel@tonic-gate 	boolean_t leaving = B_FALSE;
2837*0Sstevel@tonic-gate 
2838*0Sstevel@tonic-gate 	ASSERT(IAM_WRITER_IPIF(ipif));
2839*0Sstevel@tonic-gate 
2840*0Sstevel@tonic-gate 	/*
2841*0Sstevel@tonic-gate 	 * The ilg is valid only while we hold the conn lock. Once we drop
2842*0Sstevel@tonic-gate 	 * the lock, another thread can locate another ilg on this connp,
2843*0Sstevel@tonic-gate 	 * but on a different ipif, and delete it, and cause the ilg array
2844*0Sstevel@tonic-gate 	 * to be reallocated and copied. Hence do the ilg_delete before
2845*0Sstevel@tonic-gate 	 * dropping the lock.
2846*0Sstevel@tonic-gate 	 */
2847*0Sstevel@tonic-gate 	mutex_enter(&connp->conn_lock);
2848*0Sstevel@tonic-gate 	ilg = ilg_lookup_ipif(connp, group, ipif);
2849*0Sstevel@tonic-gate 	if ((ilg == NULL) || (ilg->ilg_flags & ILG_DELETED)) {
2850*0Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
2851*0Sstevel@tonic-gate 		return (EADDRNOTAVAIL);
2852*0Sstevel@tonic-gate 	}
2853*0Sstevel@tonic-gate 
2854*0Sstevel@tonic-gate 	/*
2855*0Sstevel@tonic-gate 	 * Decide if we're actually deleting the ilg or just removing a
2856*0Sstevel@tonic-gate 	 * source filter address; if just removing an addr, make sure we
2857*0Sstevel@tonic-gate 	 * aren't trying to change the filter mode, and that the addr is
2858*0Sstevel@tonic-gate 	 * actually in our filter list already.  If we're removing the
2859*0Sstevel@tonic-gate 	 * last src in an include list, just delete the ilg.
2860*0Sstevel@tonic-gate 	 */
2861*0Sstevel@tonic-gate 	if (src == INADDR_ANY) {
2862*0Sstevel@tonic-gate 		v6src = ipv6_all_zeros;
2863*0Sstevel@tonic-gate 		leaving = B_TRUE;
2864*0Sstevel@tonic-gate 	} else {
2865*0Sstevel@tonic-gate 		int err = 0;
2866*0Sstevel@tonic-gate 		IN6_IPADDR_TO_V4MAPPED(src, &v6src);
2867*0Sstevel@tonic-gate 		if (fmode != ilg->ilg_fmode)
2868*0Sstevel@tonic-gate 			err = EINVAL;
2869*0Sstevel@tonic-gate 		else if (ilg->ilg_filter == NULL ||
2870*0Sstevel@tonic-gate 		    !list_has_addr(ilg->ilg_filter, &v6src))
2871*0Sstevel@tonic-gate 			err = EADDRNOTAVAIL;
2872*0Sstevel@tonic-gate 		if (err != 0) {
2873*0Sstevel@tonic-gate 			mutex_exit(&connp->conn_lock);
2874*0Sstevel@tonic-gate 			return (err);
2875*0Sstevel@tonic-gate 		}
2876*0Sstevel@tonic-gate 		if (fmode == MODE_IS_INCLUDE &&
2877*0Sstevel@tonic-gate 		    ilg->ilg_filter->sl_numsrc == 1) {
2878*0Sstevel@tonic-gate 			v6src = ipv6_all_zeros;
2879*0Sstevel@tonic-gate 			leaving = B_TRUE;
2880*0Sstevel@tonic-gate 		}
2881*0Sstevel@tonic-gate 	}
2882*0Sstevel@tonic-gate 
2883*0Sstevel@tonic-gate 	ilg_delete(connp, ilg, &v6src);
2884*0Sstevel@tonic-gate 	mutex_exit(&connp->conn_lock);
2885*0Sstevel@tonic-gate 
2886*0Sstevel@tonic-gate 	(void) ip_delmulti(group, ipif, B_FALSE, leaving);
2887*0Sstevel@tonic-gate 	return (0);
2888*0Sstevel@tonic-gate }
2889*0Sstevel@tonic-gate 
2890*0Sstevel@tonic-gate static int
2891*0Sstevel@tonic-gate ip_opt_delete_group_excl_v6(conn_t *connp, const in6_addr_t *v6group,
2892*0Sstevel@tonic-gate     ill_t *ill, mcast_record_t fmode, const in6_addr_t *v6src)
2893*0Sstevel@tonic-gate {
2894*0Sstevel@tonic-gate 	ilg_t	*ilg;
2895*0Sstevel@tonic-gate 	ill_t	*ilg_ill;
2896*0Sstevel@tonic-gate 	uint_t	ilg_orig_ifindex;
2897*0Sstevel@tonic-gate 	boolean_t leaving = B_TRUE;
2898*0Sstevel@tonic-gate 
2899*0Sstevel@tonic-gate 	ASSERT(IAM_WRITER_ILL(ill));
2900*0Sstevel@tonic-gate 
2901*0Sstevel@tonic-gate 	/*
2902*0Sstevel@tonic-gate 	 * Use the index that we originally used to join. We can't
2903*0Sstevel@tonic-gate 	 * use the ill directly because ilg_ill could point to
2904*0Sstevel@tonic-gate 	 * a new ill if things have moved.
2905*0Sstevel@tonic-gate 	 */
2906*0Sstevel@tonic-gate 	mutex_enter(&connp->conn_lock);
2907*0Sstevel@tonic-gate 	ilg = ilg_lookup_ill_index_v6(connp, v6group,
2908*0Sstevel@tonic-gate 	    ill->ill_phyint->phyint_ifindex);
2909*0Sstevel@tonic-gate 	if ((ilg == NULL) || (ilg->ilg_flags & ILG_DELETED)) {
2910*0Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
2911*0Sstevel@tonic-gate 		return (EADDRNOTAVAIL);
2912*0Sstevel@tonic-gate 	}
2913*0Sstevel@tonic-gate 
2914*0Sstevel@tonic-gate 	/*
2915*0Sstevel@tonic-gate 	 * Decide if we're actually deleting the ilg or just removing a
2916*0Sstevel@tonic-gate 	 * source filter address; if just removing an addr, make sure we
2917*0Sstevel@tonic-gate 	 * aren't trying to change the filter mode, and that the addr is
2918*0Sstevel@tonic-gate 	 * actually in our filter list already.  If we're removing the
2919*0Sstevel@tonic-gate 	 * last src in an include list, just delete the ilg.
2920*0Sstevel@tonic-gate 	 */
2921*0Sstevel@tonic-gate 	if (!IN6_IS_ADDR_UNSPECIFIED(v6src)) {
2922*0Sstevel@tonic-gate 		int err = 0;
2923*0Sstevel@tonic-gate 		if (fmode != ilg->ilg_fmode)
2924*0Sstevel@tonic-gate 			err = EINVAL;
2925*0Sstevel@tonic-gate 		else if (ilg->ilg_filter == NULL ||
2926*0Sstevel@tonic-gate 		    !list_has_addr(ilg->ilg_filter, v6src))
2927*0Sstevel@tonic-gate 			err = EADDRNOTAVAIL;
2928*0Sstevel@tonic-gate 		if (err != 0) {
2929*0Sstevel@tonic-gate 			mutex_exit(&connp->conn_lock);
2930*0Sstevel@tonic-gate 			return (err);
2931*0Sstevel@tonic-gate 		}
2932*0Sstevel@tonic-gate 		if (fmode == MODE_IS_INCLUDE &&
2933*0Sstevel@tonic-gate 		    ilg->ilg_filter->sl_numsrc == 1)
2934*0Sstevel@tonic-gate 			v6src = NULL;
2935*0Sstevel@tonic-gate 		else
2936*0Sstevel@tonic-gate 			leaving = B_FALSE;
2937*0Sstevel@tonic-gate 	}
2938*0Sstevel@tonic-gate 
2939*0Sstevel@tonic-gate 	ilg_ill = ilg->ilg_ill;
2940*0Sstevel@tonic-gate 	ilg_orig_ifindex = ilg->ilg_orig_ifindex;
2941*0Sstevel@tonic-gate 	ilg_delete(connp, ilg, v6src);
2942*0Sstevel@tonic-gate 	mutex_exit(&connp->conn_lock);
2943*0Sstevel@tonic-gate 	(void) ip_delmulti_v6(v6group, ilg_ill, ilg_orig_ifindex,
2944*0Sstevel@tonic-gate 	    connp->conn_zoneid, B_FALSE, leaving);
2945*0Sstevel@tonic-gate 
2946*0Sstevel@tonic-gate 	return (0);
2947*0Sstevel@tonic-gate }
2948*0Sstevel@tonic-gate 
2949*0Sstevel@tonic-gate /*
2950*0Sstevel@tonic-gate  * Handle the following optmgmt:
2951*0Sstevel@tonic-gate  *	IP_DROP_MEMBERSHIP		will leave
2952*0Sstevel@tonic-gate  *	MCAST_LEAVE_GROUP		will leave
2953*0Sstevel@tonic-gate  *	IP_UNBLOCK_SOURCE		will not leave
2954*0Sstevel@tonic-gate  *	MCAST_UNBLOCK_SOURCE		will not leave
2955*0Sstevel@tonic-gate  *	IP_LEAVE_SOURCE_GROUP		may leave (if leaving last source)
2956*0Sstevel@tonic-gate  *	MCAST_LEAVE_SOURCE_GROUP	may leave (if leaving last source)
2957*0Sstevel@tonic-gate  *
2958*0Sstevel@tonic-gate  * fmode and src parameters may be used to determine which option is
2959*0Sstevel@tonic-gate  * being set, as follows (the IP_* and MCAST_* versions of each option
2960*0Sstevel@tonic-gate  * are functionally equivalent):
2961*0Sstevel@tonic-gate  *	opt			 fmode			src
2962*0Sstevel@tonic-gate  *	IP_DROP_MEMBERSHIP	 MODE_IS_INCLUDE	INADDR_ANY
2963*0Sstevel@tonic-gate  *	MCAST_LEAVE_GROUP	 MODE_IS_INCLUDE	INADDR_ANY
2964*0Sstevel@tonic-gate  *	IP_UNBLOCK_SOURCE	 MODE_IS_EXCLUDE	v4 addr
2965*0Sstevel@tonic-gate  *	MCAST_UNBLOCK_SOURCE	 MODE_IS_EXCLUDE	v4 addr
2966*0Sstevel@tonic-gate  *	IP_LEAVE_SOURCE_GROUP	 MODE_IS_INCLUDE	v4 addr
2967*0Sstevel@tonic-gate  *	MCAST_LEAVE_SOURCE_GROUP MODE_IS_INCLUDE	v4 addr
2968*0Sstevel@tonic-gate  *
2969*0Sstevel@tonic-gate  * Changing the filter mode is not allowed; if a matching ilg already
2970*0Sstevel@tonic-gate  * exists and fmode != ilg->ilg_fmode, EINVAL is returned.
2971*0Sstevel@tonic-gate  *
2972*0Sstevel@tonic-gate  * The interface to be used may be identified by an address or by an
2973*0Sstevel@tonic-gate  * index.  A pointer to the index is passed; if it is NULL, use the
2974*0Sstevel@tonic-gate  * address, otherwise, use the index.
2975*0Sstevel@tonic-gate  */
2976*0Sstevel@tonic-gate int
2977*0Sstevel@tonic-gate ip_opt_delete_group(conn_t *connp, boolean_t checkonly, ipaddr_t group,
2978*0Sstevel@tonic-gate     ipaddr_t ifaddr, uint_t *ifindexp, mcast_record_t fmode, ipaddr_t src,
2979*0Sstevel@tonic-gate     mblk_t *first_mp)
2980*0Sstevel@tonic-gate {
2981*0Sstevel@tonic-gate 	ipif_t	*ipif;
2982*0Sstevel@tonic-gate 	ipsq_t	*ipsq;
2983*0Sstevel@tonic-gate 	int	err;
2984*0Sstevel@tonic-gate 	ill_t	*ill;
2985*0Sstevel@tonic-gate 
2986*0Sstevel@tonic-gate 	err = ip_opt_check(connp, group, src, ifaddr, ifindexp, first_mp,
2987*0Sstevel@tonic-gate 	    ip_restart_optmgmt, &ipif);
2988*0Sstevel@tonic-gate 	if (err != 0) {
2989*0Sstevel@tonic-gate 		if (err != EINPROGRESS) {
2990*0Sstevel@tonic-gate 			ip1dbg(("ip_opt_delete_group: no ipif for group "
2991*0Sstevel@tonic-gate 			    "0x%x, ifaddr 0x%x\n",
2992*0Sstevel@tonic-gate 			    (int)ntohl(group), (int)ntohl(ifaddr)));
2993*0Sstevel@tonic-gate 		}
2994*0Sstevel@tonic-gate 		return (err);
2995*0Sstevel@tonic-gate 	}
2996*0Sstevel@tonic-gate 	ASSERT(ipif != NULL);
2997*0Sstevel@tonic-gate 
2998*0Sstevel@tonic-gate 	ill = ipif->ipif_ill;
2999*0Sstevel@tonic-gate 	/* Operation not supported on a virtual network interface */
3000*0Sstevel@tonic-gate 	if (IS_VNI(ill)) {
3001*0Sstevel@tonic-gate 		ipif_refrele(ipif);
3002*0Sstevel@tonic-gate 		return (EINVAL);
3003*0Sstevel@tonic-gate 	}
3004*0Sstevel@tonic-gate 
3005*0Sstevel@tonic-gate 	if (checkonly) {
3006*0Sstevel@tonic-gate 		/*
3007*0Sstevel@tonic-gate 		 * do not do operation, just pretend to - new T_CHECK
3008*0Sstevel@tonic-gate 		 * semantics. The error return case above if encountered
3009*0Sstevel@tonic-gate 		 * considered a good enough "check" here.
3010*0Sstevel@tonic-gate 		 */
3011*0Sstevel@tonic-gate 		ipif_refrele(ipif);
3012*0Sstevel@tonic-gate 		return (0);
3013*0Sstevel@tonic-gate 	}
3014*0Sstevel@tonic-gate 
3015*0Sstevel@tonic-gate 	IPSQ_ENTER_IPIF(ipif, connp, first_mp, ip_restart_optmgmt, ipsq,
3016*0Sstevel@tonic-gate 	    NEW_OP);
3017*0Sstevel@tonic-gate 	err = ip_opt_delete_group_excl(connp, group, ipif, fmode, src);
3018*0Sstevel@tonic-gate 	IPSQ_EXIT(ipsq);
3019*0Sstevel@tonic-gate 
3020*0Sstevel@tonic-gate 	ipif_refrele(ipif);
3021*0Sstevel@tonic-gate 	return (err);
3022*0Sstevel@tonic-gate }
3023*0Sstevel@tonic-gate 
3024*0Sstevel@tonic-gate /*
3025*0Sstevel@tonic-gate  * Handle the following optmgmt:
3026*0Sstevel@tonic-gate  *	IPV6_LEAVE_GROUP		will leave
3027*0Sstevel@tonic-gate  *	MCAST_LEAVE_GROUP		will leave
3028*0Sstevel@tonic-gate  *	MCAST_UNBLOCK_SOURCE		will not leave
3029*0Sstevel@tonic-gate  *	MCAST_LEAVE_SOURCE_GROUP	may leave (if leaving last source)
3030*0Sstevel@tonic-gate  *
3031*0Sstevel@tonic-gate  * fmode and src parameters may be used to determine which option is
3032*0Sstevel@tonic-gate  * being set, as follows (IPV6_LEAVE_GROUP and MCAST_LEAVE_GROUP options
3033*0Sstevel@tonic-gate  * are functionally equivalent):
3034*0Sstevel@tonic-gate  *	opt			 fmode			v6src
3035*0Sstevel@tonic-gate  *	IPV6_LEAVE_GROUP	 MODE_IS_INCLUDE	unspecified
3036*0Sstevel@tonic-gate  *	MCAST_LEAVE_GROUP	 MODE_IS_INCLUDE	unspecified
3037*0Sstevel@tonic-gate  *	MCAST_UNBLOCK_SOURCE	 MODE_IS_EXCLUDE	v6 addr
3038*0Sstevel@tonic-gate  *	MCAST_LEAVE_SOURCE_GROUP MODE_IS_INCLUDE	v6 addr
3039*0Sstevel@tonic-gate  *
3040*0Sstevel@tonic-gate  * Changing the filter mode is not allowed; if a matching ilg already
3041*0Sstevel@tonic-gate  * exists and fmode != ilg->ilg_fmode, EINVAL is returned.
3042*0Sstevel@tonic-gate  *
3043*0Sstevel@tonic-gate  * Handles IPv4-mapped IPv6 multicast addresses by associating them
3044*0Sstevel@tonic-gate  * with the link-local ipif.  Assumes that if v6group is v4-mapped,
3045*0Sstevel@tonic-gate  * v6src is also v4-mapped.
3046*0Sstevel@tonic-gate  */
3047*0Sstevel@tonic-gate int
3048*0Sstevel@tonic-gate ip_opt_delete_group_v6(conn_t *connp, boolean_t checkonly,
3049*0Sstevel@tonic-gate     const in6_addr_t *v6group, int ifindex, mcast_record_t fmode,
3050*0Sstevel@tonic-gate     const in6_addr_t *v6src, mblk_t *first_mp)
3051*0Sstevel@tonic-gate {
3052*0Sstevel@tonic-gate 	ill_t *ill;
3053*0Sstevel@tonic-gate 	ipif_t	*ipif;
3054*0Sstevel@tonic-gate 	char	buf[INET6_ADDRSTRLEN];
3055*0Sstevel@tonic-gate 	ipaddr_t v4group, v4src;
3056*0Sstevel@tonic-gate 	boolean_t isv6;
3057*0Sstevel@tonic-gate 	ipsq_t	*ipsq;
3058*0Sstevel@tonic-gate 	int	err;
3059*0Sstevel@tonic-gate 
3060*0Sstevel@tonic-gate 	err = ip_opt_check_v6(connp, v6group, &v4group, v6src, &v4src, &isv6,
3061*0Sstevel@tonic-gate 	    ifindex, first_mp, ip_restart_optmgmt, &ill, &ipif);
3062*0Sstevel@tonic-gate 	if (err != 0) {
3063*0Sstevel@tonic-gate 		if (err != EINPROGRESS) {
3064*0Sstevel@tonic-gate 			ip1dbg(("ip_opt_delete_group_v6: no ill for group %s/"
3065*0Sstevel@tonic-gate 			    "index %d\n", inet_ntop(AF_INET6, v6group, buf,
3066*0Sstevel@tonic-gate 			    sizeof (buf)), ifindex));
3067*0Sstevel@tonic-gate 		}
3068*0Sstevel@tonic-gate 		return (err);
3069*0Sstevel@tonic-gate 	}
3070*0Sstevel@tonic-gate 	ASSERT((isv6 && ill != NULL) || (!isv6 && ipif != NULL));
3071*0Sstevel@tonic-gate 
3072*0Sstevel@tonic-gate 	/* operation is not supported on the virtual network interface */
3073*0Sstevel@tonic-gate 	if (isv6) {
3074*0Sstevel@tonic-gate 		if (IS_VNI(ill)) {
3075*0Sstevel@tonic-gate 			ill_refrele(ill);
3076*0Sstevel@tonic-gate 			return (EINVAL);
3077*0Sstevel@tonic-gate 		}
3078*0Sstevel@tonic-gate 	} else {
3079*0Sstevel@tonic-gate 		if (IS_VNI(ipif->ipif_ill)) {
3080*0Sstevel@tonic-gate 			ipif_refrele(ipif);
3081*0Sstevel@tonic-gate 			return (EINVAL);
3082*0Sstevel@tonic-gate 		}
3083*0Sstevel@tonic-gate 	}
3084*0Sstevel@tonic-gate 
3085*0Sstevel@tonic-gate 	if (checkonly) {
3086*0Sstevel@tonic-gate 		/*
3087*0Sstevel@tonic-gate 		 * do not do operation, just pretend to - new T_CHECK
3088*0Sstevel@tonic-gate 		 * semantics. The error return case above if encountered
3089*0Sstevel@tonic-gate 		 * considered a good enough "check" here.
3090*0Sstevel@tonic-gate 		 */
3091*0Sstevel@tonic-gate 		if (isv6)
3092*0Sstevel@tonic-gate 			ill_refrele(ill);
3093*0Sstevel@tonic-gate 		else
3094*0Sstevel@tonic-gate 			ipif_refrele(ipif);
3095*0Sstevel@tonic-gate 		return (0);
3096*0Sstevel@tonic-gate 	}
3097*0Sstevel@tonic-gate 
3098*0Sstevel@tonic-gate 	if (!isv6) {
3099*0Sstevel@tonic-gate 		IPSQ_ENTER_IPIF(ipif, connp, first_mp, ip_restart_optmgmt,
3100*0Sstevel@tonic-gate 		    ipsq, NEW_OP);
3101*0Sstevel@tonic-gate 		err = ip_opt_delete_group_excl(connp, v4group, ipif, fmode,
3102*0Sstevel@tonic-gate 		    v4src);
3103*0Sstevel@tonic-gate 		IPSQ_EXIT(ipsq);
3104*0Sstevel@tonic-gate 		ipif_refrele(ipif);
3105*0Sstevel@tonic-gate 	} else {
3106*0Sstevel@tonic-gate 		IPSQ_ENTER_ILL(ill, connp, first_mp, ip_restart_optmgmt,
3107*0Sstevel@tonic-gate 		    ipsq, NEW_OP);
3108*0Sstevel@tonic-gate 		err = ip_opt_delete_group_excl_v6(connp, v6group, ill, fmode,
3109*0Sstevel@tonic-gate 		    v6src);
3110*0Sstevel@tonic-gate 		IPSQ_EXIT(ipsq);
3111*0Sstevel@tonic-gate 		ill_refrele(ill);
3112*0Sstevel@tonic-gate 	}
3113*0Sstevel@tonic-gate 
3114*0Sstevel@tonic-gate 	return (err);
3115*0Sstevel@tonic-gate }
3116*0Sstevel@tonic-gate 
3117*0Sstevel@tonic-gate /*
3118*0Sstevel@tonic-gate  * Group mgmt for upper conn that passes things down
3119*0Sstevel@tonic-gate  * to the interface multicast list (and DLPI)
3120*0Sstevel@tonic-gate  * These routines can handle new style options that specify an interface name
3121*0Sstevel@tonic-gate  * as opposed to an interface address (needed for general handling of
3122*0Sstevel@tonic-gate  * unnumbered interfaces.)
3123*0Sstevel@tonic-gate  */
3124*0Sstevel@tonic-gate 
3125*0Sstevel@tonic-gate /*
3126*0Sstevel@tonic-gate  * Add a group to an upper conn group data structure and pass things down
3127*0Sstevel@tonic-gate  * to the interface multicast list (and DLPI)
3128*0Sstevel@tonic-gate  */
3129*0Sstevel@tonic-gate static int
3130*0Sstevel@tonic-gate ilg_add(conn_t *connp, ipaddr_t group, ipif_t *ipif, mcast_record_t fmode,
3131*0Sstevel@tonic-gate     ipaddr_t src)
3132*0Sstevel@tonic-gate {
3133*0Sstevel@tonic-gate 	int	error = 0;
3134*0Sstevel@tonic-gate 	ill_t	*ill;
3135*0Sstevel@tonic-gate 	ilg_t	*ilg;
3136*0Sstevel@tonic-gate 	ilg_stat_t ilgstat;
3137*0Sstevel@tonic-gate 	slist_t	*new_filter = NULL;
3138*0Sstevel@tonic-gate 	int	new_fmode;
3139*0Sstevel@tonic-gate 
3140*0Sstevel@tonic-gate 	ASSERT(IAM_WRITER_IPIF(ipif));
3141*0Sstevel@tonic-gate 
3142*0Sstevel@tonic-gate 	ill = ipif->ipif_ill;
3143*0Sstevel@tonic-gate 
3144*0Sstevel@tonic-gate 	if (!(ill->ill_flags & ILLF_MULTICAST))
3145*0Sstevel@tonic-gate 		return (EADDRNOTAVAIL);
3146*0Sstevel@tonic-gate 
3147*0Sstevel@tonic-gate 	/*
3148*0Sstevel@tonic-gate 	 * conn_ilg[] is protected by conn_lock. Need to hold the conn_lock
3149*0Sstevel@tonic-gate 	 * to walk the conn_ilg[] list in ilg_lookup_ipif(); also needed to
3150*0Sstevel@tonic-gate 	 * serialize 2 threads doing join (sock, group1, hme0:0) and
3151*0Sstevel@tonic-gate 	 * (sock, group2, hme1:0) where hme0 and hme1 map to different ipsqs,
3152*0Sstevel@tonic-gate 	 * but both operations happen on the same conn.
3153*0Sstevel@tonic-gate 	 */
3154*0Sstevel@tonic-gate 	mutex_enter(&connp->conn_lock);
3155*0Sstevel@tonic-gate 	ilg = ilg_lookup_ipif(connp, group, ipif);
3156*0Sstevel@tonic-gate 
3157*0Sstevel@tonic-gate 	/*
3158*0Sstevel@tonic-gate 	 * Depending on the option we're handling, may or may not be okay
3159*0Sstevel@tonic-gate 	 * if group has already been added.  Figure out our rules based
3160*0Sstevel@tonic-gate 	 * on fmode and src params.  Also make sure there's enough room
3161*0Sstevel@tonic-gate 	 * in the filter if we're adding a source to an existing filter.
3162*0Sstevel@tonic-gate 	 */
3163*0Sstevel@tonic-gate 	if (src == INADDR_ANY) {
3164*0Sstevel@tonic-gate 		/* we're joining for all sources, must not have joined */
3165*0Sstevel@tonic-gate 		if (ilg != NULL)
3166*0Sstevel@tonic-gate 			error = EADDRINUSE;
3167*0Sstevel@tonic-gate 	} else {
3168*0Sstevel@tonic-gate 		if (fmode == MODE_IS_EXCLUDE) {
3169*0Sstevel@tonic-gate 			/* (excl {addr}) => block source, must have joined */
3170*0Sstevel@tonic-gate 			if (ilg == NULL)
3171*0Sstevel@tonic-gate 				error = EADDRNOTAVAIL;
3172*0Sstevel@tonic-gate 		}
3173*0Sstevel@tonic-gate 		/* (incl {addr}) => join source, may have joined */
3174*0Sstevel@tonic-gate 
3175*0Sstevel@tonic-gate 		if (ilg != NULL &&
3176*0Sstevel@tonic-gate 		    SLIST_CNT(ilg->ilg_filter) == MAX_FILTER_SIZE)
3177*0Sstevel@tonic-gate 			error = ENOBUFS;
3178*0Sstevel@tonic-gate 	}
3179*0Sstevel@tonic-gate 	if (error != 0) {
3180*0Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
3181*0Sstevel@tonic-gate 		return (error);
3182*0Sstevel@tonic-gate 	}
3183*0Sstevel@tonic-gate 
3184*0Sstevel@tonic-gate 	ASSERT(!(ipif->ipif_state_flags & IPIF_CONDEMNED));
3185*0Sstevel@tonic-gate 
3186*0Sstevel@tonic-gate 	/*
3187*0Sstevel@tonic-gate 	 * Alloc buffer to copy new state into (see below) before
3188*0Sstevel@tonic-gate 	 * we make any changes, so we can bail if it fails.
3189*0Sstevel@tonic-gate 	 */
3190*0Sstevel@tonic-gate 	if ((new_filter = l_alloc()) == NULL) {
3191*0Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
3192*0Sstevel@tonic-gate 		return (ENOMEM);
3193*0Sstevel@tonic-gate 	}
3194*0Sstevel@tonic-gate 
3195*0Sstevel@tonic-gate 	if (ilg == NULL) {
3196*0Sstevel@tonic-gate 		ilgstat = ILGSTAT_NEW;
3197*0Sstevel@tonic-gate 		if ((ilg = conn_ilg_alloc(connp)) == NULL) {
3198*0Sstevel@tonic-gate 			mutex_exit(&connp->conn_lock);
3199*0Sstevel@tonic-gate 			l_free(new_filter);
3200*0Sstevel@tonic-gate 			return (ENOMEM);
3201*0Sstevel@tonic-gate 		}
3202*0Sstevel@tonic-gate 		if (src != INADDR_ANY) {
3203*0Sstevel@tonic-gate 			ilg->ilg_filter = l_alloc();
3204*0Sstevel@tonic-gate 			if (ilg->ilg_filter == NULL) {
3205*0Sstevel@tonic-gate 				ilg_delete(connp, ilg, NULL);
3206*0Sstevel@tonic-gate 				mutex_exit(&connp->conn_lock);
3207*0Sstevel@tonic-gate 				l_free(new_filter);
3208*0Sstevel@tonic-gate 				return (ENOMEM);
3209*0Sstevel@tonic-gate 			}
3210*0Sstevel@tonic-gate 			ilg->ilg_filter->sl_numsrc = 1;
3211*0Sstevel@tonic-gate 			IN6_IPADDR_TO_V4MAPPED(src,
3212*0Sstevel@tonic-gate 			    &ilg->ilg_filter->sl_addr[0]);
3213*0Sstevel@tonic-gate 		}
3214*0Sstevel@tonic-gate 		if (group == INADDR_ANY) {
3215*0Sstevel@tonic-gate 			ilg->ilg_v6group = ipv6_all_zeros;
3216*0Sstevel@tonic-gate 		} else {
3217*0Sstevel@tonic-gate 			IN6_IPADDR_TO_V4MAPPED(group, &ilg->ilg_v6group);
3218*0Sstevel@tonic-gate 		}
3219*0Sstevel@tonic-gate 		ilg->ilg_ipif = ipif;
3220*0Sstevel@tonic-gate 		ilg->ilg_ill = NULL;
3221*0Sstevel@tonic-gate 		ilg->ilg_orig_ifindex = 0;
3222*0Sstevel@tonic-gate 		ilg->ilg_fmode = fmode;
3223*0Sstevel@tonic-gate 	} else {
3224*0Sstevel@tonic-gate 		int index;
3225*0Sstevel@tonic-gate 		in6_addr_t v6src;
3226*0Sstevel@tonic-gate 		ilgstat = ILGSTAT_CHANGE;
3227*0Sstevel@tonic-gate 		if (ilg->ilg_fmode != fmode || src == INADDR_ANY) {
3228*0Sstevel@tonic-gate 			mutex_exit(&connp->conn_lock);
3229*0Sstevel@tonic-gate 			l_free(new_filter);
3230*0Sstevel@tonic-gate 			return (EINVAL);
3231*0Sstevel@tonic-gate 		}
3232*0Sstevel@tonic-gate 		if (ilg->ilg_filter == NULL) {
3233*0Sstevel@tonic-gate 			ilg->ilg_filter = l_alloc();
3234*0Sstevel@tonic-gate 			if (ilg->ilg_filter == NULL) {
3235*0Sstevel@tonic-gate 				mutex_exit(&connp->conn_lock);
3236*0Sstevel@tonic-gate 				l_free(new_filter);
3237*0Sstevel@tonic-gate 				return (ENOMEM);
3238*0Sstevel@tonic-gate 			}
3239*0Sstevel@tonic-gate 		}
3240*0Sstevel@tonic-gate 		IN6_IPADDR_TO_V4MAPPED(src, &v6src);
3241*0Sstevel@tonic-gate 		if (list_has_addr(ilg->ilg_filter, &v6src)) {
3242*0Sstevel@tonic-gate 			mutex_exit(&connp->conn_lock);
3243*0Sstevel@tonic-gate 			l_free(new_filter);
3244*0Sstevel@tonic-gate 			return (EADDRNOTAVAIL);
3245*0Sstevel@tonic-gate 		}
3246*0Sstevel@tonic-gate 		index = ilg->ilg_filter->sl_numsrc++;
3247*0Sstevel@tonic-gate 		ilg->ilg_filter->sl_addr[index] = v6src;
3248*0Sstevel@tonic-gate 	}
3249*0Sstevel@tonic-gate 
3250*0Sstevel@tonic-gate 	/*
3251*0Sstevel@tonic-gate 	 * Save copy of ilg's filter state to pass to other functions,
3252*0Sstevel@tonic-gate 	 * so we can release conn_lock now.
3253*0Sstevel@tonic-gate 	 */
3254*0Sstevel@tonic-gate 	new_fmode = ilg->ilg_fmode;
3255*0Sstevel@tonic-gate 	l_copy(ilg->ilg_filter, new_filter);
3256*0Sstevel@tonic-gate 
3257*0Sstevel@tonic-gate 	mutex_exit(&connp->conn_lock);
3258*0Sstevel@tonic-gate 
3259*0Sstevel@tonic-gate 	error = ip_addmulti(group, ipif, ilgstat, new_fmode, new_filter);
3260*0Sstevel@tonic-gate 	if (error != 0) {
3261*0Sstevel@tonic-gate 		/*
3262*0Sstevel@tonic-gate 		 * Need to undo what we did before calling ip_addmulti()!
3263*0Sstevel@tonic-gate 		 * Must look up the ilg again since we've not been holding
3264*0Sstevel@tonic-gate 		 * conn_lock.
3265*0Sstevel@tonic-gate 		 */
3266*0Sstevel@tonic-gate 		in6_addr_t v6src;
3267*0Sstevel@tonic-gate 		if (ilgstat == ILGSTAT_NEW)
3268*0Sstevel@tonic-gate 			v6src = ipv6_all_zeros;
3269*0Sstevel@tonic-gate 		else
3270*0Sstevel@tonic-gate 			IN6_IPADDR_TO_V4MAPPED(src, &v6src);
3271*0Sstevel@tonic-gate 		mutex_enter(&connp->conn_lock);
3272*0Sstevel@tonic-gate 		ilg = ilg_lookup_ipif(connp, group, ipif);
3273*0Sstevel@tonic-gate 		ASSERT(ilg != NULL);
3274*0Sstevel@tonic-gate 		ilg_delete(connp, ilg, &v6src);
3275*0Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
3276*0Sstevel@tonic-gate 		l_free(new_filter);
3277*0Sstevel@tonic-gate 		return (error);
3278*0Sstevel@tonic-gate 	}
3279*0Sstevel@tonic-gate 
3280*0Sstevel@tonic-gate 	l_free(new_filter);
3281*0Sstevel@tonic-gate 	return (0);
3282*0Sstevel@tonic-gate }
3283*0Sstevel@tonic-gate 
3284*0Sstevel@tonic-gate static int
3285*0Sstevel@tonic-gate ilg_add_v6(conn_t *connp, const in6_addr_t *v6group, ill_t *ill,
3286*0Sstevel@tonic-gate     mcast_record_t fmode, const in6_addr_t *v6src)
3287*0Sstevel@tonic-gate {
3288*0Sstevel@tonic-gate 	int	error = 0;
3289*0Sstevel@tonic-gate 	int	orig_ifindex;
3290*0Sstevel@tonic-gate 	ilg_t	*ilg;
3291*0Sstevel@tonic-gate 	ilg_stat_t ilgstat;
3292*0Sstevel@tonic-gate 	slist_t	*new_filter = NULL;
3293*0Sstevel@tonic-gate 	int	new_fmode;
3294*0Sstevel@tonic-gate 
3295*0Sstevel@tonic-gate 	ASSERT(IAM_WRITER_ILL(ill));
3296*0Sstevel@tonic-gate 
3297*0Sstevel@tonic-gate 	if (!(ill->ill_flags & ILLF_MULTICAST))
3298*0Sstevel@tonic-gate 		return (EADDRNOTAVAIL);
3299*0Sstevel@tonic-gate 
3300*0Sstevel@tonic-gate 	/*
3301*0Sstevel@tonic-gate 	 * conn_lock protects the ilg list.  Serializes 2 threads doing
3302*0Sstevel@tonic-gate 	 * join (sock, group1, hme0) and (sock, group2, hme1) where hme0
3303*0Sstevel@tonic-gate 	 * and hme1 map to different ipsq's, but both operations happen
3304*0Sstevel@tonic-gate 	 * on the same conn.
3305*0Sstevel@tonic-gate 	 */
3306*0Sstevel@tonic-gate 	mutex_enter(&connp->conn_lock);
3307*0Sstevel@tonic-gate 
3308*0Sstevel@tonic-gate 	/*
3309*0Sstevel@tonic-gate 	 * Use the ifindex to do the lookup. We can't use the ill
3310*0Sstevel@tonic-gate 	 * directly because ilg_ill could point to a different ill if
3311*0Sstevel@tonic-gate 	 * things have moved.
3312*0Sstevel@tonic-gate 	 */
3313*0Sstevel@tonic-gate 	orig_ifindex = ill->ill_phyint->phyint_ifindex;
3314*0Sstevel@tonic-gate 	ilg = ilg_lookup_ill_index_v6(connp, v6group, orig_ifindex);
3315*0Sstevel@tonic-gate 
3316*0Sstevel@tonic-gate 	/*
3317*0Sstevel@tonic-gate 	 * Depending on the option we're handling, may or may not be okay
3318*0Sstevel@tonic-gate 	 * if group has already been added.  Figure out our rules based
3319*0Sstevel@tonic-gate 	 * on fmode and src params.  Also make sure there's enough room
3320*0Sstevel@tonic-gate 	 * in the filter if we're adding a source to an existing filter.
3321*0Sstevel@tonic-gate 	 */
3322*0Sstevel@tonic-gate 	if (IN6_IS_ADDR_UNSPECIFIED(v6src)) {
3323*0Sstevel@tonic-gate 		/* we're joining for all sources, must not have joined */
3324*0Sstevel@tonic-gate 		if (ilg != NULL)
3325*0Sstevel@tonic-gate 			error = EADDRINUSE;
3326*0Sstevel@tonic-gate 	} else {
3327*0Sstevel@tonic-gate 		if (fmode == MODE_IS_EXCLUDE) {
3328*0Sstevel@tonic-gate 			/* (excl {addr}) => block source, must have joined */
3329*0Sstevel@tonic-gate 			if (ilg == NULL)
3330*0Sstevel@tonic-gate 				error = EADDRNOTAVAIL;
3331*0Sstevel@tonic-gate 		}
3332*0Sstevel@tonic-gate 		/* (incl {addr}) => join source, may have joined */
3333*0Sstevel@tonic-gate 
3334*0Sstevel@tonic-gate 		if (ilg != NULL &&
3335*0Sstevel@tonic-gate 		    SLIST_CNT(ilg->ilg_filter) == MAX_FILTER_SIZE)
3336*0Sstevel@tonic-gate 			error = ENOBUFS;
3337*0Sstevel@tonic-gate 	}
3338*0Sstevel@tonic-gate 	if (error != 0) {
3339*0Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
3340*0Sstevel@tonic-gate 		return (error);
3341*0Sstevel@tonic-gate 	}
3342*0Sstevel@tonic-gate 
3343*0Sstevel@tonic-gate 	/*
3344*0Sstevel@tonic-gate 	 * Alloc buffer to copy new state into (see below) before
3345*0Sstevel@tonic-gate 	 * we make any changes, so we can bail if it fails.
3346*0Sstevel@tonic-gate 	 */
3347*0Sstevel@tonic-gate 	if ((new_filter = l_alloc()) == NULL) {
3348*0Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
3349*0Sstevel@tonic-gate 		return (ENOMEM);
3350*0Sstevel@tonic-gate 	}
3351*0Sstevel@tonic-gate 
3352*0Sstevel@tonic-gate 	if (ilg == NULL) {
3353*0Sstevel@tonic-gate 		if ((ilg = conn_ilg_alloc(connp)) == NULL) {
3354*0Sstevel@tonic-gate 			mutex_exit(&connp->conn_lock);
3355*0Sstevel@tonic-gate 			l_free(new_filter);
3356*0Sstevel@tonic-gate 			return (ENOMEM);
3357*0Sstevel@tonic-gate 		}
3358*0Sstevel@tonic-gate 		if (!IN6_IS_ADDR_UNSPECIFIED(v6src)) {
3359*0Sstevel@tonic-gate 			ilg->ilg_filter = l_alloc();
3360*0Sstevel@tonic-gate 			if (ilg->ilg_filter == NULL) {
3361*0Sstevel@tonic-gate 				ilg_delete(connp, ilg, NULL);
3362*0Sstevel@tonic-gate 				mutex_exit(&connp->conn_lock);
3363*0Sstevel@tonic-gate 				l_free(new_filter);
3364*0Sstevel@tonic-gate 				return (ENOMEM);
3365*0Sstevel@tonic-gate 			}
3366*0Sstevel@tonic-gate 			ilg->ilg_filter->sl_numsrc = 1;
3367*0Sstevel@tonic-gate 			ilg->ilg_filter->sl_addr[0] = *v6src;
3368*0Sstevel@tonic-gate 		}
3369*0Sstevel@tonic-gate 		ilgstat = ILGSTAT_NEW;
3370*0Sstevel@tonic-gate 		ilg->ilg_v6group = *v6group;
3371*0Sstevel@tonic-gate 		ilg->ilg_fmode = fmode;
3372*0Sstevel@tonic-gate 		ilg->ilg_ipif = NULL;
3373*0Sstevel@tonic-gate 		/*
3374*0Sstevel@tonic-gate 		 * Choose our target ill to join on. This might be different
3375*0Sstevel@tonic-gate 		 * from the ill we've been given if it's currently down and
3376*0Sstevel@tonic-gate 		 * part of a group.
3377*0Sstevel@tonic-gate 		 *
3378*0Sstevel@tonic-gate 		 * new ill is not refheld; we are writer.
3379*0Sstevel@tonic-gate 		 */
3380*0Sstevel@tonic-gate 		ill = ip_choose_multi_ill(ill, v6group);
3381*0Sstevel@tonic-gate 		ASSERT(!(ill->ill_state_flags & ILL_CONDEMNED));
3382*0Sstevel@tonic-gate 		ilg->ilg_ill = ill;
3383*0Sstevel@tonic-gate 		/*
3384*0Sstevel@tonic-gate 		 * Remember the orig_ifindex that we joined on, so that we
3385*0Sstevel@tonic-gate 		 * can successfully delete them later on and also search
3386*0Sstevel@tonic-gate 		 * for duplicates if the application wants to join again.
3387*0Sstevel@tonic-gate 		 */
3388*0Sstevel@tonic-gate 		ilg->ilg_orig_ifindex = orig_ifindex;
3389*0Sstevel@tonic-gate 	} else {
3390*0Sstevel@tonic-gate 		int index;
3391*0Sstevel@tonic-gate 		if (ilg->ilg_fmode != fmode || IN6_IS_ADDR_UNSPECIFIED(v6src)) {
3392*0Sstevel@tonic-gate 			mutex_exit(&connp->conn_lock);
3393*0Sstevel@tonic-gate 			l_free(new_filter);
3394*0Sstevel@tonic-gate 			return (EINVAL);
3395*0Sstevel@tonic-gate 		}
3396*0Sstevel@tonic-gate 		if (ilg->ilg_filter == NULL) {
3397*0Sstevel@tonic-gate 			ilg->ilg_filter = l_alloc();
3398*0Sstevel@tonic-gate 			if (ilg->ilg_filter == NULL) {
3399*0Sstevel@tonic-gate 				mutex_exit(&connp->conn_lock);
3400*0Sstevel@tonic-gate 				l_free(new_filter);
3401*0Sstevel@tonic-gate 				return (ENOMEM);
3402*0Sstevel@tonic-gate 			}
3403*0Sstevel@tonic-gate 		}
3404*0Sstevel@tonic-gate 		if (list_has_addr(ilg->ilg_filter, v6src)) {
3405*0Sstevel@tonic-gate 			mutex_exit(&connp->conn_lock);
3406*0Sstevel@tonic-gate 			l_free(new_filter);
3407*0Sstevel@tonic-gate 			return (EADDRNOTAVAIL);
3408*0Sstevel@tonic-gate 		}
3409*0Sstevel@tonic-gate 		ilgstat = ILGSTAT_CHANGE;
3410*0Sstevel@tonic-gate 		index = ilg->ilg_filter->sl_numsrc++;
3411*0Sstevel@tonic-gate 		ilg->ilg_filter->sl_addr[index] = *v6src;
3412*0Sstevel@tonic-gate 		/*
3413*0Sstevel@tonic-gate 		 * The current ill might be different from the one we were
3414*0Sstevel@tonic-gate 		 * asked to join on (if failover has occurred); we should
3415*0Sstevel@tonic-gate 		 * join on the ill stored in the ilg.  The original ill
3416*0Sstevel@tonic-gate 		 * is noted in ilg_orig_ifindex, which matched our request.
3417*0Sstevel@tonic-gate 		 */
3418*0Sstevel@tonic-gate 		ill = ilg->ilg_ill;
3419*0Sstevel@tonic-gate 	}
3420*0Sstevel@tonic-gate 
3421*0Sstevel@tonic-gate 	/*
3422*0Sstevel@tonic-gate 	 * Save copy of ilg's filter state to pass to other functions,
3423*0Sstevel@tonic-gate 	 * so we can release conn_lock now.
3424*0Sstevel@tonic-gate 	 */
3425*0Sstevel@tonic-gate 	new_fmode = ilg->ilg_fmode;
3426*0Sstevel@tonic-gate 	l_copy(ilg->ilg_filter, new_filter);
3427*0Sstevel@tonic-gate 
3428*0Sstevel@tonic-gate 	mutex_exit(&connp->conn_lock);
3429*0Sstevel@tonic-gate 
3430*0Sstevel@tonic-gate 	/*
3431*0Sstevel@tonic-gate 	 * Now update the ill. We wait to do this until after the ilg
3432*0Sstevel@tonic-gate 	 * has been updated because we need to update the src filter
3433*0Sstevel@tonic-gate 	 * info for the ill, which involves looking at the status of
3434*0Sstevel@tonic-gate 	 * all the ilgs associated with this group/interface pair.
3435*0Sstevel@tonic-gate 	 */
3436*0Sstevel@tonic-gate 	error = ip_addmulti_v6(v6group, ill, orig_ifindex, connp->conn_zoneid,
3437*0Sstevel@tonic-gate 	    ilgstat, new_fmode, new_filter);
3438*0Sstevel@tonic-gate 	if (error != 0) {
3439*0Sstevel@tonic-gate 		/*
3440*0Sstevel@tonic-gate 		 * But because we waited, we have to undo the ilg update
3441*0Sstevel@tonic-gate 		 * if ip_addmulti_v6() fails.  We also must lookup ilg
3442*0Sstevel@tonic-gate 		 * again, since we've not been holding conn_lock.
3443*0Sstevel@tonic-gate 		 */
3444*0Sstevel@tonic-gate 		in6_addr_t delsrc =
3445*0Sstevel@tonic-gate 		    (ilgstat == ILGSTAT_NEW) ? ipv6_all_zeros : *v6src;
3446*0Sstevel@tonic-gate 		mutex_enter(&connp->conn_lock);
3447*0Sstevel@tonic-gate 		ilg = ilg_lookup_ill_index_v6(connp, v6group, orig_ifindex);
3448*0Sstevel@tonic-gate 		ASSERT(ilg != NULL);
3449*0Sstevel@tonic-gate 		ilg_delete(connp, ilg, &delsrc);
3450*0Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
3451*0Sstevel@tonic-gate 		l_free(new_filter);
3452*0Sstevel@tonic-gate 		return (error);
3453*0Sstevel@tonic-gate 	}
3454*0Sstevel@tonic-gate 
3455*0Sstevel@tonic-gate 	l_free(new_filter);
3456*0Sstevel@tonic-gate 
3457*0Sstevel@tonic-gate 	return (0);
3458*0Sstevel@tonic-gate }
3459*0Sstevel@tonic-gate 
3460*0Sstevel@tonic-gate /*
3461*0Sstevel@tonic-gate  * Find an IPv4 ilg matching group, ill and source
3462*0Sstevel@tonic-gate  */
3463*0Sstevel@tonic-gate ilg_t *
3464*0Sstevel@tonic-gate ilg_lookup_ill_withsrc(conn_t *connp, ipaddr_t group, ipaddr_t src, ill_t *ill)
3465*0Sstevel@tonic-gate {
3466*0Sstevel@tonic-gate 	in6_addr_t v6group, v6src;
3467*0Sstevel@tonic-gate 	int i;
3468*0Sstevel@tonic-gate 	boolean_t isinlist;
3469*0Sstevel@tonic-gate 	ilg_t *ilg;
3470*0Sstevel@tonic-gate 	ipif_t *ipif;
3471*0Sstevel@tonic-gate 	ill_t *ilg_ill;
3472*0Sstevel@tonic-gate 
3473*0Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&connp->conn_lock));
3474*0Sstevel@tonic-gate 
3475*0Sstevel@tonic-gate 	/*
3476*0Sstevel@tonic-gate 	 * INADDR_ANY is represented as the IPv6 unspecified addr.
3477*0Sstevel@tonic-gate 	 */
3478*0Sstevel@tonic-gate 	if (group == INADDR_ANY)
3479*0Sstevel@tonic-gate 		v6group = ipv6_all_zeros;
3480*0Sstevel@tonic-gate 	else
3481*0Sstevel@tonic-gate 		IN6_IPADDR_TO_V4MAPPED(group, &v6group);
3482*0Sstevel@tonic-gate 
3483*0Sstevel@tonic-gate 	for (i = 0; i < connp->conn_ilg_inuse; i++) {
3484*0Sstevel@tonic-gate 		/* ilg_ipif is NULL for v6; skip them */
3485*0Sstevel@tonic-gate 		ilg = &connp->conn_ilg[i];
3486*0Sstevel@tonic-gate 		if ((ipif = ilg->ilg_ipif) == NULL)
3487*0Sstevel@tonic-gate 			continue;
3488*0Sstevel@tonic-gate 		ASSERT(ilg->ilg_ill == NULL);
3489*0Sstevel@tonic-gate 		ilg_ill = ipif->ipif_ill;
3490*0Sstevel@tonic-gate 		ASSERT(!ilg_ill->ill_isv6);
3491*0Sstevel@tonic-gate 		if (ilg_ill == ill &&
3492*0Sstevel@tonic-gate 		    IN6_ARE_ADDR_EQUAL(&ilg->ilg_v6group, &v6group)) {
3493*0Sstevel@tonic-gate 			if (SLIST_IS_EMPTY(ilg->ilg_filter)) {
3494*0Sstevel@tonic-gate 				/* no source filter, so this is a match */
3495*0Sstevel@tonic-gate 				return (ilg);
3496*0Sstevel@tonic-gate 			}
3497*0Sstevel@tonic-gate 			break;
3498*0Sstevel@tonic-gate 		}
3499*0Sstevel@tonic-gate 	}
3500*0Sstevel@tonic-gate 	if (i == connp->conn_ilg_inuse)
3501*0Sstevel@tonic-gate 		return (NULL);
3502*0Sstevel@tonic-gate 
3503*0Sstevel@tonic-gate 	/*
3504*0Sstevel@tonic-gate 	 * we have an ilg with matching ill and group; but
3505*0Sstevel@tonic-gate 	 * the ilg has a source list that we must check.
3506*0Sstevel@tonic-gate 	 */
3507*0Sstevel@tonic-gate 	IN6_IPADDR_TO_V4MAPPED(src, &v6src);
3508*0Sstevel@tonic-gate 	isinlist = B_FALSE;
3509*0Sstevel@tonic-gate 	for (i = 0; i < ilg->ilg_filter->sl_numsrc; i++) {
3510*0Sstevel@tonic-gate 		if (IN6_ARE_ADDR_EQUAL(&v6src, &ilg->ilg_filter->sl_addr[i])) {
3511*0Sstevel@tonic-gate 			isinlist = B_TRUE;
3512*0Sstevel@tonic-gate 			break;
3513*0Sstevel@tonic-gate 		}
3514*0Sstevel@tonic-gate 	}
3515*0Sstevel@tonic-gate 
3516*0Sstevel@tonic-gate 	if ((isinlist && ilg->ilg_fmode == MODE_IS_INCLUDE) ||
3517*0Sstevel@tonic-gate 	    (!isinlist && ilg->ilg_fmode == MODE_IS_EXCLUDE))
3518*0Sstevel@tonic-gate 		return (ilg);
3519*0Sstevel@tonic-gate 
3520*0Sstevel@tonic-gate 	return (NULL);
3521*0Sstevel@tonic-gate }
3522*0Sstevel@tonic-gate 
3523*0Sstevel@tonic-gate /*
3524*0Sstevel@tonic-gate  * Find an IPv6 ilg matching group, ill, and source
3525*0Sstevel@tonic-gate  */
3526*0Sstevel@tonic-gate ilg_t *
3527*0Sstevel@tonic-gate ilg_lookup_ill_withsrc_v6(conn_t *connp, const in6_addr_t *v6group,
3528*0Sstevel@tonic-gate     const in6_addr_t *v6src, ill_t *ill)
3529*0Sstevel@tonic-gate {
3530*0Sstevel@tonic-gate 	int i;
3531*0Sstevel@tonic-gate 	boolean_t isinlist;
3532*0Sstevel@tonic-gate 	ilg_t *ilg;
3533*0Sstevel@tonic-gate 	ill_t *ilg_ill;
3534*0Sstevel@tonic-gate 
3535*0Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&connp->conn_lock));
3536*0Sstevel@tonic-gate 
3537*0Sstevel@tonic-gate 	for (i = 0; i < connp->conn_ilg_inuse; i++) {
3538*0Sstevel@tonic-gate 		ilg = &connp->conn_ilg[i];
3539*0Sstevel@tonic-gate 		if ((ilg_ill = ilg->ilg_ill) == NULL)
3540*0Sstevel@tonic-gate 			continue;
3541*0Sstevel@tonic-gate 		ASSERT(ilg->ilg_ipif == NULL);
3542*0Sstevel@tonic-gate 		ASSERT(ilg_ill->ill_isv6);
3543*0Sstevel@tonic-gate 		if (ilg_ill == ill &&
3544*0Sstevel@tonic-gate 		    IN6_ARE_ADDR_EQUAL(&ilg->ilg_v6group, v6group)) {
3545*0Sstevel@tonic-gate 			if (SLIST_IS_EMPTY(ilg->ilg_filter)) {
3546*0Sstevel@tonic-gate 				/* no source filter, so this is a match */
3547*0Sstevel@tonic-gate 				return (ilg);
3548*0Sstevel@tonic-gate 			}
3549*0Sstevel@tonic-gate 			break;
3550*0Sstevel@tonic-gate 		}
3551*0Sstevel@tonic-gate 	}
3552*0Sstevel@tonic-gate 	if (i == connp->conn_ilg_inuse)
3553*0Sstevel@tonic-gate 		return (NULL);
3554*0Sstevel@tonic-gate 
3555*0Sstevel@tonic-gate 	/*
3556*0Sstevel@tonic-gate 	 * we have an ilg with matching ill and group; but
3557*0Sstevel@tonic-gate 	 * the ilg has a source list that we must check.
3558*0Sstevel@tonic-gate 	 */
3559*0Sstevel@tonic-gate 	isinlist = B_FALSE;
3560*0Sstevel@tonic-gate 	for (i = 0; i < ilg->ilg_filter->sl_numsrc; i++) {
3561*0Sstevel@tonic-gate 		if (IN6_ARE_ADDR_EQUAL(v6src, &ilg->ilg_filter->sl_addr[i])) {
3562*0Sstevel@tonic-gate 			isinlist = B_TRUE;
3563*0Sstevel@tonic-gate 			break;
3564*0Sstevel@tonic-gate 		}
3565*0Sstevel@tonic-gate 	}
3566*0Sstevel@tonic-gate 
3567*0Sstevel@tonic-gate 	if ((isinlist && ilg->ilg_fmode == MODE_IS_INCLUDE) ||
3568*0Sstevel@tonic-gate 	    (!isinlist && ilg->ilg_fmode == MODE_IS_EXCLUDE))
3569*0Sstevel@tonic-gate 		return (ilg);
3570*0Sstevel@tonic-gate 
3571*0Sstevel@tonic-gate 	return (NULL);
3572*0Sstevel@tonic-gate }
3573*0Sstevel@tonic-gate 
3574*0Sstevel@tonic-gate /*
3575*0Sstevel@tonic-gate  * Get the ilg whose ilg_orig_ifindex is associated with ifindex.
3576*0Sstevel@tonic-gate  * This is useful when the interface fails and we have moved
3577*0Sstevel@tonic-gate  * to a new ill, but still would like to locate using the index
3578*0Sstevel@tonic-gate  * that we originally used to join. Used only for IPv6 currently.
3579*0Sstevel@tonic-gate  */
3580*0Sstevel@tonic-gate static ilg_t *
3581*0Sstevel@tonic-gate ilg_lookup_ill_index_v6(conn_t *connp, const in6_addr_t *v6group, int ifindex)
3582*0Sstevel@tonic-gate {
3583*0Sstevel@tonic-gate 	ilg_t	*ilg;
3584*0Sstevel@tonic-gate 	int	i;
3585*0Sstevel@tonic-gate 
3586*0Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&connp->conn_lock));
3587*0Sstevel@tonic-gate 	for (i = 0; i < connp->conn_ilg_inuse; i++) {
3588*0Sstevel@tonic-gate 		ilg = &connp->conn_ilg[i];
3589*0Sstevel@tonic-gate 		/* ilg_ill is NULL for V4. Skip them */
3590*0Sstevel@tonic-gate 		if (ilg->ilg_ill == NULL)
3591*0Sstevel@tonic-gate 			continue;
3592*0Sstevel@tonic-gate 		/* ilg_ipif is NULL for V6 */
3593*0Sstevel@tonic-gate 		ASSERT(ilg->ilg_ipif == NULL);
3594*0Sstevel@tonic-gate 		ASSERT(ilg->ilg_orig_ifindex != 0);
3595*0Sstevel@tonic-gate 		if (IN6_ARE_ADDR_EQUAL(&ilg->ilg_v6group, v6group) &&
3596*0Sstevel@tonic-gate 		    ilg->ilg_orig_ifindex == ifindex) {
3597*0Sstevel@tonic-gate 			return (ilg);
3598*0Sstevel@tonic-gate 		}
3599*0Sstevel@tonic-gate 	}
3600*0Sstevel@tonic-gate 	return (NULL);
3601*0Sstevel@tonic-gate }
3602*0Sstevel@tonic-gate 
3603*0Sstevel@tonic-gate /*
3604*0Sstevel@tonic-gate  * Find an IPv6 ilg matching group and ill
3605*0Sstevel@tonic-gate  */
3606*0Sstevel@tonic-gate ilg_t *
3607*0Sstevel@tonic-gate ilg_lookup_ill_v6(conn_t *connp, const in6_addr_t *v6group, ill_t *ill)
3608*0Sstevel@tonic-gate {
3609*0Sstevel@tonic-gate 	ilg_t	*ilg;
3610*0Sstevel@tonic-gate 	int	i;
3611*0Sstevel@tonic-gate 	ill_t 	*mem_ill;
3612*0Sstevel@tonic-gate 
3613*0Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&connp->conn_lock));
3614*0Sstevel@tonic-gate 
3615*0Sstevel@tonic-gate 	for (i = 0; i < connp->conn_ilg_inuse; i++) {
3616*0Sstevel@tonic-gate 		ilg = &connp->conn_ilg[i];
3617*0Sstevel@tonic-gate 		if ((mem_ill = ilg->ilg_ill) == NULL)
3618*0Sstevel@tonic-gate 			continue;
3619*0Sstevel@tonic-gate 		ASSERT(ilg->ilg_ipif == NULL);
3620*0Sstevel@tonic-gate 		ASSERT(mem_ill->ill_isv6);
3621*0Sstevel@tonic-gate 		if (mem_ill == ill &&
3622*0Sstevel@tonic-gate 		    IN6_ARE_ADDR_EQUAL(&ilg->ilg_v6group, v6group))
3623*0Sstevel@tonic-gate 			return (ilg);
3624*0Sstevel@tonic-gate 	}
3625*0Sstevel@tonic-gate 	return (NULL);
3626*0Sstevel@tonic-gate }
3627*0Sstevel@tonic-gate 
3628*0Sstevel@tonic-gate /*
3629*0Sstevel@tonic-gate  * Find an IPv4 ilg matching group and ipif
3630*0Sstevel@tonic-gate  */
3631*0Sstevel@tonic-gate static ilg_t *
3632*0Sstevel@tonic-gate ilg_lookup_ipif(conn_t *connp, ipaddr_t group, ipif_t *ipif)
3633*0Sstevel@tonic-gate {
3634*0Sstevel@tonic-gate 	in6_addr_t v6group;
3635*0Sstevel@tonic-gate 	int	i;
3636*0Sstevel@tonic-gate 
3637*0Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&connp->conn_lock));
3638*0Sstevel@tonic-gate 	ASSERT(!ipif->ipif_ill->ill_isv6);
3639*0Sstevel@tonic-gate 
3640*0Sstevel@tonic-gate 	if (group == INADDR_ANY)
3641*0Sstevel@tonic-gate 		v6group = ipv6_all_zeros;
3642*0Sstevel@tonic-gate 	else
3643*0Sstevel@tonic-gate 		IN6_IPADDR_TO_V4MAPPED(group, &v6group);
3644*0Sstevel@tonic-gate 
3645*0Sstevel@tonic-gate 	for (i = 0; i < connp->conn_ilg_inuse; i++) {
3646*0Sstevel@tonic-gate 		if (IN6_ARE_ADDR_EQUAL(&connp->conn_ilg[i].ilg_v6group,
3647*0Sstevel@tonic-gate 		    &v6group) &&
3648*0Sstevel@tonic-gate 		    connp->conn_ilg[i].ilg_ipif == ipif)
3649*0Sstevel@tonic-gate 			return (&connp->conn_ilg[i]);
3650*0Sstevel@tonic-gate 	}
3651*0Sstevel@tonic-gate 	return (NULL);
3652*0Sstevel@tonic-gate }
3653*0Sstevel@tonic-gate 
3654*0Sstevel@tonic-gate /*
3655*0Sstevel@tonic-gate  * If a source address is passed in (src != NULL and src is not
3656*0Sstevel@tonic-gate  * unspecified), remove the specified src addr from the given ilg's
3657*0Sstevel@tonic-gate  * filter list, else delete the ilg.
3658*0Sstevel@tonic-gate  */
3659*0Sstevel@tonic-gate static void
3660*0Sstevel@tonic-gate ilg_delete(conn_t *connp, ilg_t *ilg, const in6_addr_t *src)
3661*0Sstevel@tonic-gate {
3662*0Sstevel@tonic-gate 	int	i;
3663*0Sstevel@tonic-gate 
3664*0Sstevel@tonic-gate 	ASSERT((ilg->ilg_ipif != NULL) ^ (ilg->ilg_ill != NULL));
3665*0Sstevel@tonic-gate 	ASSERT(ilg->ilg_ipif == NULL || IAM_WRITER_IPIF(ilg->ilg_ipif));
3666*0Sstevel@tonic-gate 	ASSERT(ilg->ilg_ill == NULL || IAM_WRITER_ILL(ilg->ilg_ill));
3667*0Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&connp->conn_lock));
3668*0Sstevel@tonic-gate 	ASSERT(!(ilg->ilg_flags & ILG_DELETED));
3669*0Sstevel@tonic-gate 
3670*0Sstevel@tonic-gate 	if (src == NULL || IN6_IS_ADDR_UNSPECIFIED(src)) {
3671*0Sstevel@tonic-gate 		if (connp->conn_ilg_walker_cnt != 0) {
3672*0Sstevel@tonic-gate 			ilg->ilg_flags |= ILG_DELETED;
3673*0Sstevel@tonic-gate 			return;
3674*0Sstevel@tonic-gate 		}
3675*0Sstevel@tonic-gate 
3676*0Sstevel@tonic-gate 		FREE_SLIST(ilg->ilg_filter);
3677*0Sstevel@tonic-gate 
3678*0Sstevel@tonic-gate 		i = ilg - &connp->conn_ilg[0];
3679*0Sstevel@tonic-gate 		ASSERT(i >= 0 && i < connp->conn_ilg_inuse);
3680*0Sstevel@tonic-gate 
3681*0Sstevel@tonic-gate 		/* Move other entries up one step */
3682*0Sstevel@tonic-gate 		connp->conn_ilg_inuse--;
3683*0Sstevel@tonic-gate 		for (; i < connp->conn_ilg_inuse; i++)
3684*0Sstevel@tonic-gate 			connp->conn_ilg[i] = connp->conn_ilg[i+1];
3685*0Sstevel@tonic-gate 
3686*0Sstevel@tonic-gate 		if (connp->conn_ilg_inuse == 0) {
3687*0Sstevel@tonic-gate 			mi_free((char *)connp->conn_ilg);
3688*0Sstevel@tonic-gate 			connp->conn_ilg = NULL;
3689*0Sstevel@tonic-gate 			cv_broadcast(&connp->conn_refcv);
3690*0Sstevel@tonic-gate 		}
3691*0Sstevel@tonic-gate 	} else {
3692*0Sstevel@tonic-gate 		l_remove(ilg->ilg_filter, src);
3693*0Sstevel@tonic-gate 	}
3694*0Sstevel@tonic-gate }
3695*0Sstevel@tonic-gate 
3696*0Sstevel@tonic-gate /*
3697*0Sstevel@tonic-gate  * Called from conn close. No new ilg can be added or removed.
3698*0Sstevel@tonic-gate  * because CONN_CLOSING has been set by ip_close. ilg_add / ilg_delete
3699*0Sstevel@tonic-gate  * will return error if conn has started closing.
3700*0Sstevel@tonic-gate  */
3701*0Sstevel@tonic-gate void
3702*0Sstevel@tonic-gate ilg_delete_all(conn_t *connp)
3703*0Sstevel@tonic-gate {
3704*0Sstevel@tonic-gate 	int	i;
3705*0Sstevel@tonic-gate 	ipif_t	*ipif = NULL;
3706*0Sstevel@tonic-gate 	ill_t	*ill = NULL;
3707*0Sstevel@tonic-gate 	ilg_t	*ilg;
3708*0Sstevel@tonic-gate 	in6_addr_t v6group;
3709*0Sstevel@tonic-gate 	boolean_t success;
3710*0Sstevel@tonic-gate 	ipsq_t	*ipsq;
3711*0Sstevel@tonic-gate 	int	orig_ifindex;
3712*0Sstevel@tonic-gate 
3713*0Sstevel@tonic-gate 	mutex_enter(&connp->conn_lock);
3714*0Sstevel@tonic-gate retry:
3715*0Sstevel@tonic-gate 	ILG_WALKER_HOLD(connp);
3716*0Sstevel@tonic-gate 	for (i = connp->conn_ilg_inuse - 1; i >= 0; ) {
3717*0Sstevel@tonic-gate 		ilg = &connp->conn_ilg[i];
3718*0Sstevel@tonic-gate 		/*
3719*0Sstevel@tonic-gate 		 * Since this walk is not atomic (we drop the
3720*0Sstevel@tonic-gate 		 * conn_lock and wait in ipsq_enter) we need
3721*0Sstevel@tonic-gate 		 * to check for the ILG_DELETED flag.
3722*0Sstevel@tonic-gate 		 */
3723*0Sstevel@tonic-gate 		if (ilg->ilg_flags & ILG_DELETED) {
3724*0Sstevel@tonic-gate 			/* Go to the next ilg */
3725*0Sstevel@tonic-gate 			i--;
3726*0Sstevel@tonic-gate 			continue;
3727*0Sstevel@tonic-gate 		}
3728*0Sstevel@tonic-gate 		v6group = ilg->ilg_v6group;
3729*0Sstevel@tonic-gate 
3730*0Sstevel@tonic-gate 		if (IN6_IS_ADDR_V4MAPPED(&v6group)) {
3731*0Sstevel@tonic-gate 			ipif = ilg->ilg_ipif;
3732*0Sstevel@tonic-gate 			ill = ipif->ipif_ill;
3733*0Sstevel@tonic-gate 		} else {
3734*0Sstevel@tonic-gate 			ipif = NULL;
3735*0Sstevel@tonic-gate 			ill = ilg->ilg_ill;
3736*0Sstevel@tonic-gate 		}
3737*0Sstevel@tonic-gate 		/*
3738*0Sstevel@tonic-gate 		 * We may not be able to refhold the ill if the ill/ipif
3739*0Sstevel@tonic-gate 		 * is changing. But we need to make sure that the ill will
3740*0Sstevel@tonic-gate 		 * not vanish. So we just bump up the ill_waiter count.
3741*0Sstevel@tonic-gate 		 * If we are unable to do even that, then the ill is closing,
3742*0Sstevel@tonic-gate 		 * in which case the unplumb thread will handle the cleanup,
3743*0Sstevel@tonic-gate 		 * and we move on to the next ilg.
3744*0Sstevel@tonic-gate 		 */
3745*0Sstevel@tonic-gate 		if (!ill_waiter_inc(ill)) {
3746*0Sstevel@tonic-gate 			/* Go to the next ilg */
3747*0Sstevel@tonic-gate 			i--;
3748*0Sstevel@tonic-gate 			continue;
3749*0Sstevel@tonic-gate 		}
3750*0Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
3751*0Sstevel@tonic-gate 		/*
3752*0Sstevel@tonic-gate 		 * To prevent deadlock between ill close which waits inside
3753*0Sstevel@tonic-gate 		 * the perimeter, and conn close, ipsq_enter returns error,
3754*0Sstevel@tonic-gate 		 * the moment ILL_CONDEMNED is set, in which case ill close
3755*0Sstevel@tonic-gate 		 * takes responsibility to cleanup the ilgs. Note that we
3756*0Sstevel@tonic-gate 		 * have not yet set condemned flag, otherwise the conn can't
3757*0Sstevel@tonic-gate 		 * be refheld for cleanup by those routines and it would be
3758*0Sstevel@tonic-gate 		 * a mutual deadlock.
3759*0Sstevel@tonic-gate 		 */
3760*0Sstevel@tonic-gate 		success = ipsq_enter(ill, B_FALSE);
3761*0Sstevel@tonic-gate 		ipsq = ill->ill_phyint->phyint_ipsq;
3762*0Sstevel@tonic-gate 		ill_waiter_dcr(ill);
3763*0Sstevel@tonic-gate 		mutex_enter(&connp->conn_lock);
3764*0Sstevel@tonic-gate 		if (!success) {
3765*0Sstevel@tonic-gate 			/* Go to the next ilg */
3766*0Sstevel@tonic-gate 			i--;
3767*0Sstevel@tonic-gate 			continue;
3768*0Sstevel@tonic-gate 		}
3769*0Sstevel@tonic-gate 
3770*0Sstevel@tonic-gate 		/*
3771*0Sstevel@tonic-gate 		 * Make sure that nothing has changed under. For eg.
3772*0Sstevel@tonic-gate 		 * a failover/failback can change ilg_ill while we were
3773*0Sstevel@tonic-gate 		 * waiting to become exclusive above
3774*0Sstevel@tonic-gate 		 */
3775*0Sstevel@tonic-gate 		if (IN6_IS_ADDR_V4MAPPED(&v6group)) {
3776*0Sstevel@tonic-gate 			ipif = ilg->ilg_ipif;
3777*0Sstevel@tonic-gate 			ill = ipif->ipif_ill;
3778*0Sstevel@tonic-gate 		} else {
3779*0Sstevel@tonic-gate 			ipif = NULL;
3780*0Sstevel@tonic-gate 			ill = ilg->ilg_ill;
3781*0Sstevel@tonic-gate 		}
3782*0Sstevel@tonic-gate 		if (!IAM_WRITER_ILL(ill) || (ilg->ilg_flags & ILG_DELETED)) {
3783*0Sstevel@tonic-gate 			/*
3784*0Sstevel@tonic-gate 			 * The ilg has changed under us probably due
3785*0Sstevel@tonic-gate 			 * to a failover or unplumb. Retry on the same ilg.
3786*0Sstevel@tonic-gate 			 */
3787*0Sstevel@tonic-gate 			mutex_exit(&connp->conn_lock);
3788*0Sstevel@tonic-gate 			ipsq_exit(ipsq, B_TRUE, B_TRUE);
3789*0Sstevel@tonic-gate 			mutex_enter(&connp->conn_lock);
3790*0Sstevel@tonic-gate 			continue;
3791*0Sstevel@tonic-gate 		}
3792*0Sstevel@tonic-gate 		v6group = ilg->ilg_v6group;
3793*0Sstevel@tonic-gate 		orig_ifindex = ilg->ilg_orig_ifindex;
3794*0Sstevel@tonic-gate 		ilg_delete(connp, ilg, NULL);
3795*0Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
3796*0Sstevel@tonic-gate 
3797*0Sstevel@tonic-gate 		if (ipif != NULL)
3798*0Sstevel@tonic-gate 			(void) ip_delmulti(V4_PART_OF_V6(v6group), ipif,
3799*0Sstevel@tonic-gate 			    B_FALSE, B_TRUE);
3800*0Sstevel@tonic-gate 
3801*0Sstevel@tonic-gate 		else
3802*0Sstevel@tonic-gate 			(void) ip_delmulti_v6(&v6group, ill, orig_ifindex,
3803*0Sstevel@tonic-gate 			    connp->conn_zoneid, B_FALSE, B_TRUE);
3804*0Sstevel@tonic-gate 
3805*0Sstevel@tonic-gate 		ipsq_exit(ipsq, B_TRUE, B_TRUE);
3806*0Sstevel@tonic-gate 		mutex_enter(&connp->conn_lock);
3807*0Sstevel@tonic-gate 		/* Go to the next ilg */
3808*0Sstevel@tonic-gate 		i--;
3809*0Sstevel@tonic-gate 	}
3810*0Sstevel@tonic-gate 	ILG_WALKER_RELE(connp);
3811*0Sstevel@tonic-gate 
3812*0Sstevel@tonic-gate 	/* If any ill was skipped above wait and retry */
3813*0Sstevel@tonic-gate 	if (connp->conn_ilg_inuse != 0) {
3814*0Sstevel@tonic-gate 		cv_wait(&connp->conn_refcv, &connp->conn_lock);
3815*0Sstevel@tonic-gate 		goto retry;
3816*0Sstevel@tonic-gate 	}
3817*0Sstevel@tonic-gate 	mutex_exit(&connp->conn_lock);
3818*0Sstevel@tonic-gate }
3819*0Sstevel@tonic-gate 
3820*0Sstevel@tonic-gate /*
3821*0Sstevel@tonic-gate  * Called from ill close by ipcl_walk for clearing conn_ilg and
3822*0Sstevel@tonic-gate  * conn_multicast_ipif for a given ipif. conn is held by caller.
3823*0Sstevel@tonic-gate  * Note that ipcl_walk only walks conns that are not yet condemned.
3824*0Sstevel@tonic-gate  * condemned conns can't be refheld. For this reason, conn must become clean
3825*0Sstevel@tonic-gate  * first, i.e. it must not refer to any ill/ire/ipif and then only set
3826*0Sstevel@tonic-gate  * condemned flag.
3827*0Sstevel@tonic-gate  */
3828*0Sstevel@tonic-gate static void
3829*0Sstevel@tonic-gate conn_delete_ipif(conn_t *connp, caddr_t arg)
3830*0Sstevel@tonic-gate {
3831*0Sstevel@tonic-gate 	ipif_t	*ipif = (ipif_t *)arg;
3832*0Sstevel@tonic-gate 	int	i;
3833*0Sstevel@tonic-gate 	char	group_buf1[INET6_ADDRSTRLEN];
3834*0Sstevel@tonic-gate 	char	group_buf2[INET6_ADDRSTRLEN];
3835*0Sstevel@tonic-gate 	ipaddr_t group;
3836*0Sstevel@tonic-gate 	ilg_t	*ilg;
3837*0Sstevel@tonic-gate 
3838*0Sstevel@tonic-gate 	/*
3839*0Sstevel@tonic-gate 	 * Even though conn_ilg_inuse can change while we are in this loop,
3840*0Sstevel@tonic-gate 	 * i.e.ilgs can be created or deleted on this connp, no new ilgs can
3841*0Sstevel@tonic-gate 	 * be created or deleted for this connp, on this ill, since this ill
3842*0Sstevel@tonic-gate 	 * is the perimeter. So we won't miss any ilg in this cleanup.
3843*0Sstevel@tonic-gate 	 */
3844*0Sstevel@tonic-gate 	mutex_enter(&connp->conn_lock);
3845*0Sstevel@tonic-gate 
3846*0Sstevel@tonic-gate 	/*
3847*0Sstevel@tonic-gate 	 * Increment the walker count, so that ilg repacking does not
3848*0Sstevel@tonic-gate 	 * occur while we are in the loop.
3849*0Sstevel@tonic-gate 	 */
3850*0Sstevel@tonic-gate 	ILG_WALKER_HOLD(connp);
3851*0Sstevel@tonic-gate 	for (i = connp->conn_ilg_inuse - 1; i >= 0; i--) {
3852*0Sstevel@tonic-gate 		ilg = &connp->conn_ilg[i];
3853*0Sstevel@tonic-gate 		if (ilg->ilg_ipif != ipif || (ilg->ilg_flags & ILG_DELETED))
3854*0Sstevel@tonic-gate 			continue;
3855*0Sstevel@tonic-gate 		/*
3856*0Sstevel@tonic-gate 		 * ip_close cannot be cleaning this ilg at the same time.
3857*0Sstevel@tonic-gate 		 * since it also has to execute in this ill's perimeter which
3858*0Sstevel@tonic-gate 		 * we are now holding. Only a clean conn can be condemned.
3859*0Sstevel@tonic-gate 		 */
3860*0Sstevel@tonic-gate 		ASSERT(!(connp->conn_state_flags & CONN_CONDEMNED));
3861*0Sstevel@tonic-gate 
3862*0Sstevel@tonic-gate 		/* Blow away the membership */
3863*0Sstevel@tonic-gate 		ip1dbg(("conn_delete_ilg_ipif: %s on %s (%s)\n",
3864*0Sstevel@tonic-gate 		    inet_ntop(AF_INET6, &connp->conn_ilg[i].ilg_v6group,
3865*0Sstevel@tonic-gate 		    group_buf1, sizeof (group_buf1)),
3866*0Sstevel@tonic-gate 		    inet_ntop(AF_INET6, &ipif->ipif_v6lcl_addr,
3867*0Sstevel@tonic-gate 		    group_buf2, sizeof (group_buf2)),
3868*0Sstevel@tonic-gate 		    ipif->ipif_ill->ill_name));
3869*0Sstevel@tonic-gate 
3870*0Sstevel@tonic-gate 		/* ilg_ipif is NULL for V6, so we won't be here */
3871*0Sstevel@tonic-gate 		ASSERT(IN6_IS_ADDR_V4MAPPED(&ilg->ilg_v6group));
3872*0Sstevel@tonic-gate 
3873*0Sstevel@tonic-gate 		group = V4_PART_OF_V6(ilg->ilg_v6group);
3874*0Sstevel@tonic-gate 		ilg_delete(connp, &connp->conn_ilg[i], NULL);
3875*0Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
3876*0Sstevel@tonic-gate 
3877*0Sstevel@tonic-gate 		(void) ip_delmulti(group, ipif, B_FALSE, B_TRUE);
3878*0Sstevel@tonic-gate 		mutex_enter(&connp->conn_lock);
3879*0Sstevel@tonic-gate 	}
3880*0Sstevel@tonic-gate 
3881*0Sstevel@tonic-gate 	/*
3882*0Sstevel@tonic-gate 	 * If we are the last walker, need to physically delete the
3883*0Sstevel@tonic-gate 	 * ilgs and repack.
3884*0Sstevel@tonic-gate 	 */
3885*0Sstevel@tonic-gate 	ILG_WALKER_RELE(connp);
3886*0Sstevel@tonic-gate 
3887*0Sstevel@tonic-gate 	if (connp->conn_multicast_ipif == ipif) {
3888*0Sstevel@tonic-gate 		/* Revert to late binding */
3889*0Sstevel@tonic-gate 		connp->conn_multicast_ipif = NULL;
3890*0Sstevel@tonic-gate 	}
3891*0Sstevel@tonic-gate 	mutex_exit(&connp->conn_lock);
3892*0Sstevel@tonic-gate 
3893*0Sstevel@tonic-gate 	conn_delete_ire(connp, (caddr_t)ipif);
3894*0Sstevel@tonic-gate }
3895*0Sstevel@tonic-gate 
3896*0Sstevel@tonic-gate /*
3897*0Sstevel@tonic-gate  * Called from ill close by ipcl_walk for clearing conn_ilg and
3898*0Sstevel@tonic-gate  * conn_multicast_ill for a given ill. conn is held by caller.
3899*0Sstevel@tonic-gate  * Note that ipcl_walk only walks conns that are not yet condemned.
3900*0Sstevel@tonic-gate  * condemned conns can't be refheld. For this reason, conn must become clean
3901*0Sstevel@tonic-gate  * first, i.e. it must not refer to any ill/ire/ipif and then only set
3902*0Sstevel@tonic-gate  * condemned flag.
3903*0Sstevel@tonic-gate  */
3904*0Sstevel@tonic-gate static void
3905*0Sstevel@tonic-gate conn_delete_ill(conn_t *connp, caddr_t arg)
3906*0Sstevel@tonic-gate {
3907*0Sstevel@tonic-gate 	ill_t	*ill = (ill_t *)arg;
3908*0Sstevel@tonic-gate 	int	i;
3909*0Sstevel@tonic-gate 	char	group_buf[INET6_ADDRSTRLEN];
3910*0Sstevel@tonic-gate 	in6_addr_t v6group;
3911*0Sstevel@tonic-gate 	int	orig_ifindex;
3912*0Sstevel@tonic-gate 	ilg_t	*ilg;
3913*0Sstevel@tonic-gate 
3914*0Sstevel@tonic-gate 	/*
3915*0Sstevel@tonic-gate 	 * Even though conn_ilg_inuse can change while we are in this loop,
3916*0Sstevel@tonic-gate 	 * no new ilgs can be created/deleted for this connp, on this
3917*0Sstevel@tonic-gate 	 * ill, since this ill is the perimeter. So we won't miss any ilg
3918*0Sstevel@tonic-gate 	 * in this cleanup.
3919*0Sstevel@tonic-gate 	 */
3920*0Sstevel@tonic-gate 	mutex_enter(&connp->conn_lock);
3921*0Sstevel@tonic-gate 
3922*0Sstevel@tonic-gate 	/*
3923*0Sstevel@tonic-gate 	 * Increment the walker count, so that ilg repacking does not
3924*0Sstevel@tonic-gate 	 * occur while we are in the loop.
3925*0Sstevel@tonic-gate 	 */
3926*0Sstevel@tonic-gate 	ILG_WALKER_HOLD(connp);
3927*0Sstevel@tonic-gate 	for (i = connp->conn_ilg_inuse - 1; i >= 0; i--) {
3928*0Sstevel@tonic-gate 		ilg = &connp->conn_ilg[i];
3929*0Sstevel@tonic-gate 		if ((ilg->ilg_ill == ill) && !(ilg->ilg_flags & ILG_DELETED)) {
3930*0Sstevel@tonic-gate 			/*
3931*0Sstevel@tonic-gate 			 * ip_close cannot be cleaning this ilg at the same
3932*0Sstevel@tonic-gate 			 * time, since it also has to execute in this ill's
3933*0Sstevel@tonic-gate 			 * perimeter which we are now holding. Only a clean
3934*0Sstevel@tonic-gate 			 * conn can be condemned.
3935*0Sstevel@tonic-gate 			 */
3936*0Sstevel@tonic-gate 			ASSERT(!(connp->conn_state_flags & CONN_CONDEMNED));
3937*0Sstevel@tonic-gate 
3938*0Sstevel@tonic-gate 			/* Blow away the membership */
3939*0Sstevel@tonic-gate 			ip1dbg(("conn_delete_ilg_ill: %s on %s\n",
3940*0Sstevel@tonic-gate 			    inet_ntop(AF_INET6, &ilg->ilg_v6group,
3941*0Sstevel@tonic-gate 			    group_buf, sizeof (group_buf)),
3942*0Sstevel@tonic-gate 			    ill->ill_name));
3943*0Sstevel@tonic-gate 
3944*0Sstevel@tonic-gate 			v6group = ilg->ilg_v6group;
3945*0Sstevel@tonic-gate 			orig_ifindex = ilg->ilg_orig_ifindex;
3946*0Sstevel@tonic-gate 			ilg_delete(connp, ilg, NULL);
3947*0Sstevel@tonic-gate 			mutex_exit(&connp->conn_lock);
3948*0Sstevel@tonic-gate 
3949*0Sstevel@tonic-gate 			(void) ip_delmulti_v6(&v6group, ill, orig_ifindex,
3950*0Sstevel@tonic-gate 			    connp->conn_zoneid, B_FALSE, B_TRUE);
3951*0Sstevel@tonic-gate 			mutex_enter(&connp->conn_lock);
3952*0Sstevel@tonic-gate 		}
3953*0Sstevel@tonic-gate 	}
3954*0Sstevel@tonic-gate 	/*
3955*0Sstevel@tonic-gate 	 * If we are the last walker, need to physically delete the
3956*0Sstevel@tonic-gate 	 * ilgs and repack.
3957*0Sstevel@tonic-gate 	 */
3958*0Sstevel@tonic-gate 	ILG_WALKER_RELE(connp);
3959*0Sstevel@tonic-gate 
3960*0Sstevel@tonic-gate 	if (connp->conn_multicast_ill == ill) {
3961*0Sstevel@tonic-gate 		/* Revert to late binding */
3962*0Sstevel@tonic-gate 		connp->conn_multicast_ill = NULL;
3963*0Sstevel@tonic-gate 		connp->conn_orig_multicast_ifindex = 0;
3964*0Sstevel@tonic-gate 	}
3965*0Sstevel@tonic-gate 	mutex_exit(&connp->conn_lock);
3966*0Sstevel@tonic-gate }
3967*0Sstevel@tonic-gate 
3968*0Sstevel@tonic-gate /*
3969*0Sstevel@tonic-gate  * Called when an ipif is unplumbed to make sure that there are no
3970*0Sstevel@tonic-gate  * dangling conn references to that ipif.
3971*0Sstevel@tonic-gate  * Handles ilg_ipif and conn_multicast_ipif
3972*0Sstevel@tonic-gate  */
3973*0Sstevel@tonic-gate void
3974*0Sstevel@tonic-gate reset_conn_ipif(ipif)
3975*0Sstevel@tonic-gate 	ipif_t	*ipif;
3976*0Sstevel@tonic-gate {
3977*0Sstevel@tonic-gate 	ipcl_walk(conn_delete_ipif, (caddr_t)ipif);
3978*0Sstevel@tonic-gate 	/* flush the SCTP ire cache for this ipif */
3979*0Sstevel@tonic-gate 	sctp_ire_cache_flush(ipif);
3980*0Sstevel@tonic-gate }
3981*0Sstevel@tonic-gate 
3982*0Sstevel@tonic-gate /*
3983*0Sstevel@tonic-gate  * Called when an ill is unplumbed to make sure that there are no
3984*0Sstevel@tonic-gate  * dangling conn references to that ill.
3985*0Sstevel@tonic-gate  * Handles ilg_ill, conn_multicast_ill.
3986*0Sstevel@tonic-gate  */
3987*0Sstevel@tonic-gate void
3988*0Sstevel@tonic-gate reset_conn_ill(ill_t *ill)
3989*0Sstevel@tonic-gate {
3990*0Sstevel@tonic-gate 	ipcl_walk(conn_delete_ill, (caddr_t)ill);
3991*0Sstevel@tonic-gate }
3992*0Sstevel@tonic-gate 
3993*0Sstevel@tonic-gate #ifdef DEBUG
3994*0Sstevel@tonic-gate /*
3995*0Sstevel@tonic-gate  * Walk functions walk all the interfaces in the system to make
3996*0Sstevel@tonic-gate  * sure that there is no refernece to the ipif or ill that is
3997*0Sstevel@tonic-gate  * going away.
3998*0Sstevel@tonic-gate  */
3999*0Sstevel@tonic-gate int
4000*0Sstevel@tonic-gate ilm_walk_ill(ill_t *ill)
4001*0Sstevel@tonic-gate {
4002*0Sstevel@tonic-gate 	int cnt = 0;
4003*0Sstevel@tonic-gate 	ill_t *till;
4004*0Sstevel@tonic-gate 	ilm_t *ilm;
4005*0Sstevel@tonic-gate 	ill_walk_context_t ctx;
4006*0Sstevel@tonic-gate 
4007*0Sstevel@tonic-gate 	rw_enter(&ill_g_lock, RW_READER);
4008*0Sstevel@tonic-gate 	till = ILL_START_WALK_ALL(&ctx);
4009*0Sstevel@tonic-gate 	for (; till != NULL; till = ill_next(&ctx, till)) {
4010*0Sstevel@tonic-gate 		for (ilm = till->ill_ilm; ilm != NULL; ilm = ilm->ilm_next) {
4011*0Sstevel@tonic-gate 			if (ilm->ilm_ill == ill) {
4012*0Sstevel@tonic-gate 				cnt++;
4013*0Sstevel@tonic-gate 			}
4014*0Sstevel@tonic-gate 		}
4015*0Sstevel@tonic-gate 	}
4016*0Sstevel@tonic-gate 	rw_exit(&ill_g_lock);
4017*0Sstevel@tonic-gate 
4018*0Sstevel@tonic-gate 	return (cnt);
4019*0Sstevel@tonic-gate }
4020*0Sstevel@tonic-gate 
4021*0Sstevel@tonic-gate /*
4022*0Sstevel@tonic-gate  * This function is called before the ipif is freed.
4023*0Sstevel@tonic-gate  */
4024*0Sstevel@tonic-gate int
4025*0Sstevel@tonic-gate ilm_walk_ipif(ipif_t *ipif)
4026*0Sstevel@tonic-gate {
4027*0Sstevel@tonic-gate 	int cnt = 0;
4028*0Sstevel@tonic-gate 	ill_t *till;
4029*0Sstevel@tonic-gate 	ilm_t *ilm;
4030*0Sstevel@tonic-gate 	ill_walk_context_t ctx;
4031*0Sstevel@tonic-gate 
4032*0Sstevel@tonic-gate 	till = ILL_START_WALK_ALL(&ctx);
4033*0Sstevel@tonic-gate 	for (; till != NULL; till = ill_next(&ctx, till)) {
4034*0Sstevel@tonic-gate 		for (ilm = till->ill_ilm; ilm != NULL; ilm = ilm->ilm_next) {
4035*0Sstevel@tonic-gate 			if (ilm->ilm_ipif == ipif) {
4036*0Sstevel@tonic-gate 					cnt++;
4037*0Sstevel@tonic-gate 			}
4038*0Sstevel@tonic-gate 		}
4039*0Sstevel@tonic-gate 	}
4040*0Sstevel@tonic-gate 	return (cnt);
4041*0Sstevel@tonic-gate }
4042*0Sstevel@tonic-gate #endif
4043