xref: /onnv-gate/usr/src/uts/common/inet/ip/ip_multi.c (revision 1676:37f4a3e2bd99)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*1676Sjpk  * Common Development and Distribution License (the "License").
6*1676Sjpk  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*1676Sjpk  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate /* Copyright (c) 1990 Mentat Inc. */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
280Sstevel@tonic-gate 
290Sstevel@tonic-gate #include <sys/types.h>
300Sstevel@tonic-gate #include <sys/stream.h>
310Sstevel@tonic-gate #include <sys/dlpi.h>
320Sstevel@tonic-gate #include <sys/stropts.h>
330Sstevel@tonic-gate #include <sys/strsun.h>
340Sstevel@tonic-gate #include <sys/ddi.h>
350Sstevel@tonic-gate #include <sys/cmn_err.h>
360Sstevel@tonic-gate #include <sys/zone.h>
370Sstevel@tonic-gate 
380Sstevel@tonic-gate #include <sys/param.h>
390Sstevel@tonic-gate #include <sys/socket.h>
40*1676Sjpk #include <sys/sockio.h>
410Sstevel@tonic-gate #include <net/if.h>
420Sstevel@tonic-gate #include <sys/systm.h>
430Sstevel@tonic-gate #include <net/route.h>
440Sstevel@tonic-gate #include <netinet/in.h>
450Sstevel@tonic-gate #include <net/if_dl.h>
460Sstevel@tonic-gate #include <netinet/ip6.h>
470Sstevel@tonic-gate #include <netinet/icmp6.h>
480Sstevel@tonic-gate 
490Sstevel@tonic-gate #include <inet/common.h>
500Sstevel@tonic-gate #include <inet/mi.h>
510Sstevel@tonic-gate #include <inet/nd.h>
520Sstevel@tonic-gate #include <inet/arp.h>
530Sstevel@tonic-gate #include <inet/ip.h>
540Sstevel@tonic-gate #include <inet/ip6.h>
550Sstevel@tonic-gate #include <inet/ip_if.h>
560Sstevel@tonic-gate #include <inet/ip_ndp.h>
570Sstevel@tonic-gate #include <inet/ip_multi.h>
580Sstevel@tonic-gate #include <inet/ipclassifier.h>
590Sstevel@tonic-gate #include <inet/ipsec_impl.h>
600Sstevel@tonic-gate #include <inet/sctp_ip.h>
610Sstevel@tonic-gate #include <inet/ip_listutils.h>
62741Smasputra #include <inet/udp_impl.h>
630Sstevel@tonic-gate 
640Sstevel@tonic-gate /* igmpv3/mldv2 source filter manipulation */
650Sstevel@tonic-gate static void	ilm_bld_flists(conn_t *conn, void *arg);
660Sstevel@tonic-gate static void	ilm_gen_filter(ilm_t *ilm, mcast_record_t *fmode,
670Sstevel@tonic-gate     slist_t *flist);
680Sstevel@tonic-gate 
690Sstevel@tonic-gate static ilm_t	*ilm_add_v6(ipif_t *ipif, const in6_addr_t *group,
700Sstevel@tonic-gate     ilg_stat_t ilgstat, mcast_record_t ilg_fmode, slist_t *ilg_flist,
710Sstevel@tonic-gate     int orig_ifindex, zoneid_t zoneid);
720Sstevel@tonic-gate static void	ilm_delete(ilm_t *ilm);
730Sstevel@tonic-gate static int	ip_ll_addmulti_v6(ipif_t *ipif, const in6_addr_t *group);
740Sstevel@tonic-gate static int	ip_ll_delmulti_v6(ipif_t *ipif, const in6_addr_t *group);
750Sstevel@tonic-gate static ilg_t	*ilg_lookup_ill_index_v6(conn_t *connp,
760Sstevel@tonic-gate     const in6_addr_t *v6group, int index);
770Sstevel@tonic-gate static ilg_t	*ilg_lookup_ipif(conn_t *connp, ipaddr_t group,
780Sstevel@tonic-gate     ipif_t *ipif);
790Sstevel@tonic-gate static int	ilg_add(conn_t *connp, ipaddr_t group, ipif_t *ipif,
800Sstevel@tonic-gate     mcast_record_t fmode, ipaddr_t src);
810Sstevel@tonic-gate static int	ilg_add_v6(conn_t *connp, const in6_addr_t *group, ill_t *ill,
820Sstevel@tonic-gate     mcast_record_t fmode, const in6_addr_t *v6src);
830Sstevel@tonic-gate static void	ilg_delete(conn_t *connp, ilg_t *ilg, const in6_addr_t *src);
840Sstevel@tonic-gate static mblk_t	*ill_create_dl(ill_t *ill, uint32_t dl_primitive,
850Sstevel@tonic-gate     uint32_t length, uint32_t *addr_lenp, uint32_t *addr_offp);
860Sstevel@tonic-gate static mblk_t	*ill_create_squery(ill_t *ill, ipaddr_t ipaddr,
870Sstevel@tonic-gate     uint32_t addrlen, uint32_t addroff, mblk_t *mp_tail);
880Sstevel@tonic-gate static void	conn_ilg_reap(conn_t *connp);
890Sstevel@tonic-gate static int	ip_opt_delete_group_excl(conn_t *connp, ipaddr_t group,
900Sstevel@tonic-gate     ipif_t *ipif, mcast_record_t fmode, ipaddr_t src);
910Sstevel@tonic-gate static int	ip_opt_delete_group_excl_v6(conn_t *connp,
920Sstevel@tonic-gate     const in6_addr_t *v6group, ill_t *ill, mcast_record_t fmode,
930Sstevel@tonic-gate     const in6_addr_t *v6src);
940Sstevel@tonic-gate 
950Sstevel@tonic-gate /*
960Sstevel@tonic-gate  * MT notes:
970Sstevel@tonic-gate  *
980Sstevel@tonic-gate  * Multicast joins operate on both the ilg and ilm structures. Multiple
990Sstevel@tonic-gate  * threads operating on an conn (socket) trying to do multicast joins
1000Sstevel@tonic-gate  * need to synchronize  when operating on the ilg. Multiple threads
1010Sstevel@tonic-gate  * potentially operating on different conn (socket endpoints) trying to
1020Sstevel@tonic-gate  * do multicast joins could eventually end up trying to manipulate the
1030Sstevel@tonic-gate  * ilm simulatenously and need to synchronize on the access to the ilm.
1040Sstevel@tonic-gate  * Both are amenable to standard Solaris MT techniques, but it would be
1050Sstevel@tonic-gate  * complex to handle a failover or failback which needs to manipulate
1060Sstevel@tonic-gate  * ilg/ilms if an applications can also simultaenously join/leave
1070Sstevel@tonic-gate  * multicast groups. Hence multicast join/leave also go through the ipsq_t
1080Sstevel@tonic-gate  * serialization.
1090Sstevel@tonic-gate  *
1100Sstevel@tonic-gate  * Multicast joins and leaves are single-threaded per phyint/IPMP group
1110Sstevel@tonic-gate  * using the ipsq serialization mechanism.
1120Sstevel@tonic-gate  *
1130Sstevel@tonic-gate  * An ilm is an IP data structure used to track multicast join/leave.
1140Sstevel@tonic-gate  * An ilm is associated with a <multicast group, ipif> tuple in IPv4 and
1150Sstevel@tonic-gate  * with just <multicast group> in IPv6. ilm_refcnt is the number of ilg's
1160Sstevel@tonic-gate  * referencing the ilm. ilms are created / destroyed only as writer. ilms
1170Sstevel@tonic-gate  * are not passed around, instead they are looked up and used under the
1180Sstevel@tonic-gate  * ill_lock or as writer. So we don't need a dynamic refcount of the number
1190Sstevel@tonic-gate  * of threads holding reference to an ilm.
1200Sstevel@tonic-gate  *
1210Sstevel@tonic-gate  * Multicast Join operation:
1220Sstevel@tonic-gate  *
1230Sstevel@tonic-gate  * The first step is to determine the ipif (v4) or ill (v6) on which
1240Sstevel@tonic-gate  * the join operation is to be done. The join is done after becoming
1250Sstevel@tonic-gate  * exclusive on the ipsq associated with the ipif or ill. The conn->conn_ilg
1260Sstevel@tonic-gate  * and ill->ill_ilm are thus accessed and modified exclusively per ill.
1270Sstevel@tonic-gate  * Multiple threads can attempt to join simultaneously on different ipif/ill
1280Sstevel@tonic-gate  * on the same conn. In this case the ipsq serialization does not help in
1290Sstevel@tonic-gate  * protecting the ilg. It is the conn_lock that is used to protect the ilg.
1300Sstevel@tonic-gate  * The conn_lock also protects all the ilg_t members.
1310Sstevel@tonic-gate  *
1320Sstevel@tonic-gate  * Leave operation.
1330Sstevel@tonic-gate  *
1340Sstevel@tonic-gate  * Similar to the join operation, the first step is to determine the ipif
1350Sstevel@tonic-gate  * or ill (v6) on which the leave operation is to be done. The leave operation
1360Sstevel@tonic-gate  * is done after becoming exclusive on the ipsq associated with the ipif or ill.
1370Sstevel@tonic-gate  * As with join ilg modification is done under the protection of the conn lock.
1380Sstevel@tonic-gate  */
1390Sstevel@tonic-gate 
1400Sstevel@tonic-gate #define	IPSQ_ENTER_IPIF(ipif, connp, first_mp, func, ipsq, type)	\
1410Sstevel@tonic-gate 	ASSERT(connp != NULL);					\
1420Sstevel@tonic-gate 	(ipsq) = ipsq_try_enter((ipif), NULL, CONNP_TO_WQ(connp),	\
1430Sstevel@tonic-gate 	    (first_mp), (func), (type), B_TRUE);		\
1440Sstevel@tonic-gate 	if ((ipsq) == NULL) {					\
1450Sstevel@tonic-gate 		ipif_refrele(ipif);				\
1460Sstevel@tonic-gate 		return (EINPROGRESS);				\
1470Sstevel@tonic-gate 	}
1480Sstevel@tonic-gate 
1490Sstevel@tonic-gate #define	IPSQ_ENTER_ILL(ill, connp, first_mp, func, ipsq, type)	\
1500Sstevel@tonic-gate 	ASSERT(connp != NULL);					\
1510Sstevel@tonic-gate 	(ipsq) = ipsq_try_enter(NULL, ill, CONNP_TO_WQ(connp),	\
1520Sstevel@tonic-gate 	    (first_mp),	(func), (type), B_TRUE);		\
1530Sstevel@tonic-gate 	if ((ipsq) == NULL) {					\
1540Sstevel@tonic-gate 		ill_refrele(ill);				\
1550Sstevel@tonic-gate 		return (EINPROGRESS);				\
1560Sstevel@tonic-gate 	}
1570Sstevel@tonic-gate 
1580Sstevel@tonic-gate #define	IPSQ_EXIT(ipsq)	\
1590Sstevel@tonic-gate 	if (ipsq != NULL)	\
1600Sstevel@tonic-gate 		ipsq_exit(ipsq, B_TRUE, B_TRUE);
1610Sstevel@tonic-gate 
1620Sstevel@tonic-gate #define	ILG_WALKER_HOLD(connp)	(connp)->conn_ilg_walker_cnt++
1630Sstevel@tonic-gate 
1640Sstevel@tonic-gate #define	ILG_WALKER_RELE(connp)				\
1650Sstevel@tonic-gate 	{						\
1660Sstevel@tonic-gate 		(connp)->conn_ilg_walker_cnt--;		\
1670Sstevel@tonic-gate 		if ((connp)->conn_ilg_walker_cnt == 0)	\
1680Sstevel@tonic-gate 			conn_ilg_reap(connp);		\
1690Sstevel@tonic-gate 	}
1700Sstevel@tonic-gate 
1710Sstevel@tonic-gate static void
1720Sstevel@tonic-gate conn_ilg_reap(conn_t *connp)
1730Sstevel@tonic-gate {
1740Sstevel@tonic-gate 	int	to;
1750Sstevel@tonic-gate 	int	from;
1760Sstevel@tonic-gate 
1770Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&connp->conn_lock));
1780Sstevel@tonic-gate 
1790Sstevel@tonic-gate 	to = 0;
1800Sstevel@tonic-gate 	from = 0;
1810Sstevel@tonic-gate 	while (from < connp->conn_ilg_inuse) {
1820Sstevel@tonic-gate 		if (connp->conn_ilg[from].ilg_flags & ILG_DELETED) {
1830Sstevel@tonic-gate 			FREE_SLIST(connp->conn_ilg[from].ilg_filter);
1840Sstevel@tonic-gate 			from++;
1850Sstevel@tonic-gate 			continue;
1860Sstevel@tonic-gate 		}
1870Sstevel@tonic-gate 		if (to != from)
1880Sstevel@tonic-gate 			connp->conn_ilg[to] = connp->conn_ilg[from];
1890Sstevel@tonic-gate 		to++;
1900Sstevel@tonic-gate 		from++;
1910Sstevel@tonic-gate 	}
1920Sstevel@tonic-gate 
1930Sstevel@tonic-gate 	connp->conn_ilg_inuse = to;
1940Sstevel@tonic-gate 
1950Sstevel@tonic-gate 	if (connp->conn_ilg_inuse == 0) {
1960Sstevel@tonic-gate 		mi_free((char *)connp->conn_ilg);
1970Sstevel@tonic-gate 		connp->conn_ilg = NULL;
1980Sstevel@tonic-gate 		cv_broadcast(&connp->conn_refcv);
1990Sstevel@tonic-gate 	}
2000Sstevel@tonic-gate }
2010Sstevel@tonic-gate 
2020Sstevel@tonic-gate #define	GETSTRUCT(structure, number)	\
2030Sstevel@tonic-gate 	((structure *)mi_zalloc(sizeof (structure) * (number)))
2040Sstevel@tonic-gate 
2050Sstevel@tonic-gate #define	ILG_ALLOC_CHUNK	16
2060Sstevel@tonic-gate 
2070Sstevel@tonic-gate /*
2080Sstevel@tonic-gate  * Returns a pointer to the next available ilg in conn_ilg.  Allocs more
2090Sstevel@tonic-gate  * buffers in size of ILG_ALLOC_CHUNK ilgs when needed, and updates conn's
2100Sstevel@tonic-gate  * ilg tracking fields appropriately (conn_ilg_inuse reflects usage of the
2110Sstevel@tonic-gate  * returned ilg).  Returns NULL on failure (ENOMEM).
2120Sstevel@tonic-gate  *
2130Sstevel@tonic-gate  * Assumes connp->conn_lock is held.
2140Sstevel@tonic-gate  */
2150Sstevel@tonic-gate static ilg_t *
2160Sstevel@tonic-gate conn_ilg_alloc(conn_t *connp)
2170Sstevel@tonic-gate {
2180Sstevel@tonic-gate 	ilg_t *new;
2190Sstevel@tonic-gate 	int curcnt;
2200Sstevel@tonic-gate 
2210Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&connp->conn_lock));
2220Sstevel@tonic-gate 	ASSERT(connp->conn_ilg_inuse <= connp->conn_ilg_allocated);
2230Sstevel@tonic-gate 
2240Sstevel@tonic-gate 	if (connp->conn_ilg == NULL) {
2250Sstevel@tonic-gate 		connp->conn_ilg = GETSTRUCT(ilg_t, ILG_ALLOC_CHUNK);
2260Sstevel@tonic-gate 		if (connp->conn_ilg == NULL)
2270Sstevel@tonic-gate 			return (NULL);
2280Sstevel@tonic-gate 		connp->conn_ilg_allocated = ILG_ALLOC_CHUNK;
2290Sstevel@tonic-gate 		connp->conn_ilg_inuse = 0;
2300Sstevel@tonic-gate 	}
2310Sstevel@tonic-gate 	if (connp->conn_ilg_inuse == connp->conn_ilg_allocated) {
2320Sstevel@tonic-gate 		curcnt = connp->conn_ilg_allocated;
2330Sstevel@tonic-gate 		new = GETSTRUCT(ilg_t, curcnt + ILG_ALLOC_CHUNK);
2340Sstevel@tonic-gate 		if (new == NULL)
2350Sstevel@tonic-gate 			return (NULL);
2360Sstevel@tonic-gate 		bcopy(connp->conn_ilg, new, sizeof (ilg_t) * curcnt);
2370Sstevel@tonic-gate 		mi_free((char *)connp->conn_ilg);
2380Sstevel@tonic-gate 		connp->conn_ilg = new;
2390Sstevel@tonic-gate 		connp->conn_ilg_allocated += ILG_ALLOC_CHUNK;
2400Sstevel@tonic-gate 	}
2410Sstevel@tonic-gate 
2420Sstevel@tonic-gate 	return (&connp->conn_ilg[connp->conn_ilg_inuse++]);
2430Sstevel@tonic-gate }
2440Sstevel@tonic-gate 
2450Sstevel@tonic-gate typedef struct ilm_fbld_s {
2460Sstevel@tonic-gate 	ilm_t		*fbld_ilm;
2470Sstevel@tonic-gate 	int		fbld_in_cnt;
2480Sstevel@tonic-gate 	int		fbld_ex_cnt;
2490Sstevel@tonic-gate 	slist_t		fbld_in;
2500Sstevel@tonic-gate 	slist_t		fbld_ex;
2510Sstevel@tonic-gate 	boolean_t	fbld_in_overflow;
2520Sstevel@tonic-gate } ilm_fbld_t;
2530Sstevel@tonic-gate 
2540Sstevel@tonic-gate static void
2550Sstevel@tonic-gate ilm_bld_flists(conn_t *conn, void *arg)
2560Sstevel@tonic-gate {
2570Sstevel@tonic-gate 	int i;
2580Sstevel@tonic-gate 	ilm_fbld_t *fbld = (ilm_fbld_t *)(arg);
2590Sstevel@tonic-gate 	ilm_t *ilm = fbld->fbld_ilm;
2600Sstevel@tonic-gate 	in6_addr_t *v6group = &ilm->ilm_v6addr;
2610Sstevel@tonic-gate 
2620Sstevel@tonic-gate 	if (conn->conn_ilg_inuse == 0)
2630Sstevel@tonic-gate 		return;
2640Sstevel@tonic-gate 
2650Sstevel@tonic-gate 	/*
2660Sstevel@tonic-gate 	 * Since we can't break out of the ipcl_walk once started, we still
2670Sstevel@tonic-gate 	 * have to look at every conn.  But if we've already found one
2680Sstevel@tonic-gate 	 * (EXCLUDE, NULL) list, there's no need to keep checking individual
2690Sstevel@tonic-gate 	 * ilgs--that will be our state.
2700Sstevel@tonic-gate 	 */
2710Sstevel@tonic-gate 	if (fbld->fbld_ex_cnt > 0 && fbld->fbld_ex.sl_numsrc == 0)
2720Sstevel@tonic-gate 		return;
2730Sstevel@tonic-gate 
2740Sstevel@tonic-gate 	/*
2750Sstevel@tonic-gate 	 * Check this conn's ilgs to see if any are interested in our
2760Sstevel@tonic-gate 	 * ilm (group, interface match).  If so, update the master
2770Sstevel@tonic-gate 	 * include and exclude lists we're building in the fbld struct
2780Sstevel@tonic-gate 	 * with this ilg's filter info.
2790Sstevel@tonic-gate 	 */
2800Sstevel@tonic-gate 	mutex_enter(&conn->conn_lock);
2810Sstevel@tonic-gate 	for (i = 0; i < conn->conn_ilg_inuse; i++) {
2820Sstevel@tonic-gate 		ilg_t *ilg = &conn->conn_ilg[i];
2830Sstevel@tonic-gate 		if ((ilg->ilg_ill == ilm->ilm_ill) &&
2840Sstevel@tonic-gate 		    (ilg->ilg_ipif == ilm->ilm_ipif) &&
2850Sstevel@tonic-gate 		    IN6_ARE_ADDR_EQUAL(&ilg->ilg_v6group, v6group)) {
2860Sstevel@tonic-gate 			if (ilg->ilg_fmode == MODE_IS_INCLUDE) {
2870Sstevel@tonic-gate 				fbld->fbld_in_cnt++;
2880Sstevel@tonic-gate 				if (!fbld->fbld_in_overflow)
2890Sstevel@tonic-gate 					l_union_in_a(&fbld->fbld_in,
2900Sstevel@tonic-gate 					    ilg->ilg_filter,
2910Sstevel@tonic-gate 					    &fbld->fbld_in_overflow);
2920Sstevel@tonic-gate 			} else {
2930Sstevel@tonic-gate 				fbld->fbld_ex_cnt++;
2940Sstevel@tonic-gate 				/*
2950Sstevel@tonic-gate 				 * On the first exclude list, don't try to do
2960Sstevel@tonic-gate 				 * an intersection, as the master exclude list
2970Sstevel@tonic-gate 				 * is intentionally empty.  If the master list
2980Sstevel@tonic-gate 				 * is still empty on later iterations, that
2990Sstevel@tonic-gate 				 * means we have at least one ilg with an empty
3000Sstevel@tonic-gate 				 * exclude list, so that should be reflected
3010Sstevel@tonic-gate 				 * when we take the intersection.
3020Sstevel@tonic-gate 				 */
3030Sstevel@tonic-gate 				if (fbld->fbld_ex_cnt == 1) {
3040Sstevel@tonic-gate 					if (ilg->ilg_filter != NULL)
3050Sstevel@tonic-gate 						l_copy(ilg->ilg_filter,
3060Sstevel@tonic-gate 						    &fbld->fbld_ex);
3070Sstevel@tonic-gate 				} else {
3080Sstevel@tonic-gate 					l_intersection_in_a(&fbld->fbld_ex,
3090Sstevel@tonic-gate 					    ilg->ilg_filter);
3100Sstevel@tonic-gate 				}
3110Sstevel@tonic-gate 			}
3120Sstevel@tonic-gate 			/* there will only be one match, so break now. */
3130Sstevel@tonic-gate 			break;
3140Sstevel@tonic-gate 		}
3150Sstevel@tonic-gate 	}
3160Sstevel@tonic-gate 	mutex_exit(&conn->conn_lock);
3170Sstevel@tonic-gate }
3180Sstevel@tonic-gate 
3190Sstevel@tonic-gate static void
3200Sstevel@tonic-gate ilm_gen_filter(ilm_t *ilm, mcast_record_t *fmode, slist_t *flist)
3210Sstevel@tonic-gate {
3220Sstevel@tonic-gate 	ilm_fbld_t fbld;
3230Sstevel@tonic-gate 
3240Sstevel@tonic-gate 	fbld.fbld_ilm = ilm;
3250Sstevel@tonic-gate 	fbld.fbld_in_cnt = fbld.fbld_ex_cnt = 0;
3260Sstevel@tonic-gate 	fbld.fbld_in.sl_numsrc = fbld.fbld_ex.sl_numsrc = 0;
3270Sstevel@tonic-gate 	fbld.fbld_in_overflow = B_FALSE;
3280Sstevel@tonic-gate 
3290Sstevel@tonic-gate 	/* first, construct our master include and exclude lists */
3300Sstevel@tonic-gate 	ipcl_walk(ilm_bld_flists, (caddr_t)&fbld);
3310Sstevel@tonic-gate 
3320Sstevel@tonic-gate 	/* now use those master lists to generate the interface filter */
3330Sstevel@tonic-gate 
3340Sstevel@tonic-gate 	/* if include list overflowed, filter is (EXCLUDE, NULL) */
3350Sstevel@tonic-gate 	if (fbld.fbld_in_overflow) {
3360Sstevel@tonic-gate 		*fmode = MODE_IS_EXCLUDE;
3370Sstevel@tonic-gate 		flist->sl_numsrc = 0;
3380Sstevel@tonic-gate 		return;
3390Sstevel@tonic-gate 	}
3400Sstevel@tonic-gate 
3410Sstevel@tonic-gate 	/* if nobody interested, interface filter is (INCLUDE, NULL) */
3420Sstevel@tonic-gate 	if (fbld.fbld_in_cnt == 0 && fbld.fbld_ex_cnt == 0) {
3430Sstevel@tonic-gate 		*fmode = MODE_IS_INCLUDE;
3440Sstevel@tonic-gate 		flist->sl_numsrc = 0;
3450Sstevel@tonic-gate 		return;
3460Sstevel@tonic-gate 	}
3470Sstevel@tonic-gate 
3480Sstevel@tonic-gate 	/*
3490Sstevel@tonic-gate 	 * If there are no exclude lists, then the interface filter
3500Sstevel@tonic-gate 	 * is INCLUDE, with its filter list equal to fbld_in.  A single
3510Sstevel@tonic-gate 	 * exclude list makes the interface filter EXCLUDE, with its
3520Sstevel@tonic-gate 	 * filter list equal to (fbld_ex - fbld_in).
3530Sstevel@tonic-gate 	 */
3540Sstevel@tonic-gate 	if (fbld.fbld_ex_cnt == 0) {
3550Sstevel@tonic-gate 		*fmode = MODE_IS_INCLUDE;
3560Sstevel@tonic-gate 		l_copy(&fbld.fbld_in, flist);
3570Sstevel@tonic-gate 	} else {
3580Sstevel@tonic-gate 		*fmode = MODE_IS_EXCLUDE;
3590Sstevel@tonic-gate 		l_difference(&fbld.fbld_ex, &fbld.fbld_in, flist);
3600Sstevel@tonic-gate 	}
3610Sstevel@tonic-gate }
3620Sstevel@tonic-gate 
3630Sstevel@tonic-gate /*
3640Sstevel@tonic-gate  * If the given interface has failed, choose a new one to join on so
3650Sstevel@tonic-gate  * that we continue to receive packets.  ilg_orig_ifindex remembers
3660Sstevel@tonic-gate  * what the application used to join on so that we know the ilg to
3670Sstevel@tonic-gate  * delete even though we change the ill here.  Callers will store the
3680Sstevel@tonic-gate  * ilg returned from this function in ilg_ill.  Thus when we receive
3690Sstevel@tonic-gate  * a packet on ilg_ill, conn_wantpacket_v6 will deliver the packets.
3700Sstevel@tonic-gate  *
3710Sstevel@tonic-gate  * This function must be called as writer so we can walk the group
3720Sstevel@tonic-gate  * list and examine flags without holding a lock.
3730Sstevel@tonic-gate  */
3740Sstevel@tonic-gate ill_t *
3750Sstevel@tonic-gate ip_choose_multi_ill(ill_t *ill, const in6_addr_t *grp)
3760Sstevel@tonic-gate {
3770Sstevel@tonic-gate 	ill_t	*till;
3780Sstevel@tonic-gate 	ill_group_t *illgrp = ill->ill_group;
3790Sstevel@tonic-gate 
3800Sstevel@tonic-gate 	ASSERT(IAM_WRITER_ILL(ill));
3810Sstevel@tonic-gate 
3820Sstevel@tonic-gate 	if (IN6_IS_ADDR_UNSPECIFIED(grp) || illgrp == NULL)
3830Sstevel@tonic-gate 		return (ill);
3840Sstevel@tonic-gate 
3850Sstevel@tonic-gate 	if ((ill->ill_phyint->phyint_flags & (PHYI_FAILED|PHYI_INACTIVE)) == 0)
3860Sstevel@tonic-gate 		return (ill);
3870Sstevel@tonic-gate 
3880Sstevel@tonic-gate 	till = illgrp->illgrp_ill;
3890Sstevel@tonic-gate 	while (till != NULL &&
3900Sstevel@tonic-gate 	    (till->ill_phyint->phyint_flags & (PHYI_FAILED|PHYI_INACTIVE))) {
3910Sstevel@tonic-gate 		till = till->ill_group_next;
3920Sstevel@tonic-gate 	}
3930Sstevel@tonic-gate 	if (till != NULL)
3940Sstevel@tonic-gate 		return (till);
3950Sstevel@tonic-gate 
3960Sstevel@tonic-gate 	return (ill);
3970Sstevel@tonic-gate }
3980Sstevel@tonic-gate 
3990Sstevel@tonic-gate static int
4000Sstevel@tonic-gate ilm_update_add(ilm_t *ilm, ilg_stat_t ilgstat, slist_t *ilg_flist,
4010Sstevel@tonic-gate     boolean_t isv6)
4020Sstevel@tonic-gate {
4030Sstevel@tonic-gate 	mcast_record_t fmode;
4040Sstevel@tonic-gate 	slist_t *flist;
4050Sstevel@tonic-gate 	boolean_t fdefault;
4060Sstevel@tonic-gate 	char buf[INET6_ADDRSTRLEN];
4070Sstevel@tonic-gate 	ill_t *ill = isv6 ? ilm->ilm_ill : ilm->ilm_ipif->ipif_ill;
4080Sstevel@tonic-gate 
4090Sstevel@tonic-gate 	/*
4100Sstevel@tonic-gate 	 * There are several cases where the ilm's filter state
4110Sstevel@tonic-gate 	 * defaults to (EXCLUDE, NULL):
4120Sstevel@tonic-gate 	 *	- we've had previous joins without associated ilgs
4130Sstevel@tonic-gate 	 *	- this join has no associated ilg
4140Sstevel@tonic-gate 	 *	- the ilg's filter state is (EXCLUDE, NULL)
4150Sstevel@tonic-gate 	 */
4160Sstevel@tonic-gate 	fdefault = (ilm->ilm_no_ilg_cnt > 0) ||
4170Sstevel@tonic-gate 	    (ilgstat == ILGSTAT_NONE) || SLIST_IS_EMPTY(ilg_flist);
4180Sstevel@tonic-gate 
4190Sstevel@tonic-gate 	/* attempt mallocs (if needed) before doing anything else */
4200Sstevel@tonic-gate 	if ((flist = l_alloc()) == NULL)
4210Sstevel@tonic-gate 		return (ENOMEM);
4220Sstevel@tonic-gate 	if (!fdefault && ilm->ilm_filter == NULL) {
4230Sstevel@tonic-gate 		ilm->ilm_filter = l_alloc();
4240Sstevel@tonic-gate 		if (ilm->ilm_filter == NULL) {
4250Sstevel@tonic-gate 			l_free(flist);
4260Sstevel@tonic-gate 			return (ENOMEM);
4270Sstevel@tonic-gate 		}
4280Sstevel@tonic-gate 	}
4290Sstevel@tonic-gate 
4300Sstevel@tonic-gate 	if (ilgstat != ILGSTAT_CHANGE)
4310Sstevel@tonic-gate 		ilm->ilm_refcnt++;
4320Sstevel@tonic-gate 
4330Sstevel@tonic-gate 	if (ilgstat == ILGSTAT_NONE)
4340Sstevel@tonic-gate 		ilm->ilm_no_ilg_cnt++;
4350Sstevel@tonic-gate 
4360Sstevel@tonic-gate 	/*
4370Sstevel@tonic-gate 	 * Determine new filter state.  If it's not the default
4380Sstevel@tonic-gate 	 * (EXCLUDE, NULL), we must walk the conn list to find
4390Sstevel@tonic-gate 	 * any ilgs interested in this group, and re-build the
4400Sstevel@tonic-gate 	 * ilm filter.
4410Sstevel@tonic-gate 	 */
4420Sstevel@tonic-gate 	if (fdefault) {
4430Sstevel@tonic-gate 		fmode = MODE_IS_EXCLUDE;
4440Sstevel@tonic-gate 		flist->sl_numsrc = 0;
4450Sstevel@tonic-gate 	} else {
4460Sstevel@tonic-gate 		ilm_gen_filter(ilm, &fmode, flist);
4470Sstevel@tonic-gate 	}
4480Sstevel@tonic-gate 
4490Sstevel@tonic-gate 	/* make sure state actually changed; nothing to do if not. */
4500Sstevel@tonic-gate 	if ((ilm->ilm_fmode == fmode) &&
4510Sstevel@tonic-gate 	    !lists_are_different(ilm->ilm_filter, flist)) {
4520Sstevel@tonic-gate 		l_free(flist);
4530Sstevel@tonic-gate 		return (0);
4540Sstevel@tonic-gate 	}
4550Sstevel@tonic-gate 
4560Sstevel@tonic-gate 	/* send the state change report */
4570Sstevel@tonic-gate 	if ((ill->ill_phyint->phyint_flags & PHYI_LOOPBACK) == 0) {
4580Sstevel@tonic-gate 		if (isv6)
4590Sstevel@tonic-gate 			mld_statechange(ilm, fmode, flist);
4600Sstevel@tonic-gate 		else
4610Sstevel@tonic-gate 			igmp_statechange(ilm, fmode, flist);
4620Sstevel@tonic-gate 	}
4630Sstevel@tonic-gate 
4640Sstevel@tonic-gate 	/* update the ilm state */
4650Sstevel@tonic-gate 	ilm->ilm_fmode = fmode;
4660Sstevel@tonic-gate 	if (flist->sl_numsrc > 0)
4670Sstevel@tonic-gate 		l_copy(flist, ilm->ilm_filter);
4680Sstevel@tonic-gate 	else
4690Sstevel@tonic-gate 		CLEAR_SLIST(ilm->ilm_filter);
4700Sstevel@tonic-gate 
4710Sstevel@tonic-gate 	ip1dbg(("ilm_update: new if filter mode %d, group %s\n", ilm->ilm_fmode,
4720Sstevel@tonic-gate 	    inet_ntop(AF_INET6, &ilm->ilm_v6addr, buf, sizeof (buf))));
4730Sstevel@tonic-gate 
4740Sstevel@tonic-gate 	l_free(flist);
4750Sstevel@tonic-gate 	return (0);
4760Sstevel@tonic-gate }
4770Sstevel@tonic-gate 
4780Sstevel@tonic-gate static int
4790Sstevel@tonic-gate ilm_update_del(ilm_t *ilm, boolean_t isv6)
4800Sstevel@tonic-gate {
4810Sstevel@tonic-gate 	mcast_record_t fmode;
4820Sstevel@tonic-gate 	slist_t *flist;
4830Sstevel@tonic-gate 	ill_t *ill = isv6 ? ilm->ilm_ill : ilm->ilm_ipif->ipif_ill;
4840Sstevel@tonic-gate 
4850Sstevel@tonic-gate 	ip1dbg(("ilm_update_del: still %d left; updating state\n",
4860Sstevel@tonic-gate 	    ilm->ilm_refcnt));
4870Sstevel@tonic-gate 
4880Sstevel@tonic-gate 	if ((flist = l_alloc()) == NULL)
4890Sstevel@tonic-gate 		return (ENOMEM);
4900Sstevel@tonic-gate 
4910Sstevel@tonic-gate 	/*
4920Sstevel@tonic-gate 	 * If present, the ilg in question has already either been
4930Sstevel@tonic-gate 	 * updated or removed from our list; so all we need to do
4940Sstevel@tonic-gate 	 * now is walk the list to update the ilm filter state.
4950Sstevel@tonic-gate 	 *
4960Sstevel@tonic-gate 	 * Skip the list walk if we have any no-ilg joins, which
4970Sstevel@tonic-gate 	 * cause the filter state to revert to (EXCLUDE, NULL).
4980Sstevel@tonic-gate 	 */
4990Sstevel@tonic-gate 	if (ilm->ilm_no_ilg_cnt != 0) {
5000Sstevel@tonic-gate 		fmode = MODE_IS_EXCLUDE;
5010Sstevel@tonic-gate 		flist->sl_numsrc = 0;
5020Sstevel@tonic-gate 	} else {
5030Sstevel@tonic-gate 		ilm_gen_filter(ilm, &fmode, flist);
5040Sstevel@tonic-gate 	}
5050Sstevel@tonic-gate 
5060Sstevel@tonic-gate 	/* check to see if state needs to be updated */
5070Sstevel@tonic-gate 	if ((ilm->ilm_fmode == fmode) &&
5080Sstevel@tonic-gate 	    (!lists_are_different(ilm->ilm_filter, flist))) {
5090Sstevel@tonic-gate 		l_free(flist);
5100Sstevel@tonic-gate 		return (0);
5110Sstevel@tonic-gate 	}
5120Sstevel@tonic-gate 
5130Sstevel@tonic-gate 	if ((ill->ill_phyint->phyint_flags & PHYI_LOOPBACK) == 0) {
5140Sstevel@tonic-gate 		if (isv6)
5150Sstevel@tonic-gate 			mld_statechange(ilm, fmode, flist);
5160Sstevel@tonic-gate 		else
5170Sstevel@tonic-gate 			igmp_statechange(ilm, fmode, flist);
5180Sstevel@tonic-gate 	}
5190Sstevel@tonic-gate 
5200Sstevel@tonic-gate 	ilm->ilm_fmode = fmode;
5210Sstevel@tonic-gate 	if (flist->sl_numsrc > 0) {
5220Sstevel@tonic-gate 		if (ilm->ilm_filter == NULL) {
5230Sstevel@tonic-gate 			ilm->ilm_filter = l_alloc();
5240Sstevel@tonic-gate 			if (ilm->ilm_filter == NULL) {
5250Sstevel@tonic-gate 				char buf[INET6_ADDRSTRLEN];
5260Sstevel@tonic-gate 				ip1dbg(("ilm_update_del: failed to alloc ilm "
5270Sstevel@tonic-gate 				    "filter; no source filtering for %s on %s",
5280Sstevel@tonic-gate 				    inet_ntop(AF_INET6, &ilm->ilm_v6addr,
5290Sstevel@tonic-gate 				    buf, sizeof (buf)), ill->ill_name));
5300Sstevel@tonic-gate 				ilm->ilm_fmode = MODE_IS_EXCLUDE;
5310Sstevel@tonic-gate 				l_free(flist);
5320Sstevel@tonic-gate 				return (0);
5330Sstevel@tonic-gate 			}
5340Sstevel@tonic-gate 		}
5350Sstevel@tonic-gate 		l_copy(flist, ilm->ilm_filter);
5360Sstevel@tonic-gate 	} else {
5370Sstevel@tonic-gate 		CLEAR_SLIST(ilm->ilm_filter);
5380Sstevel@tonic-gate 	}
5390Sstevel@tonic-gate 
5400Sstevel@tonic-gate 	l_free(flist);
5410Sstevel@tonic-gate 	return (0);
5420Sstevel@tonic-gate }
5430Sstevel@tonic-gate 
5440Sstevel@tonic-gate /*
5450Sstevel@tonic-gate  * INADDR_ANY means all multicast addresses. This is only used
5460Sstevel@tonic-gate  * by the multicast router.
5470Sstevel@tonic-gate  * INADDR_ANY is stored as IPv6 unspecified addr.
5480Sstevel@tonic-gate  */
5490Sstevel@tonic-gate int
5500Sstevel@tonic-gate ip_addmulti(ipaddr_t group, ipif_t *ipif, ilg_stat_t ilgstat,
5510Sstevel@tonic-gate     mcast_record_t ilg_fmode, slist_t *ilg_flist)
5520Sstevel@tonic-gate {
5530Sstevel@tonic-gate 	ill_t	*ill = ipif->ipif_ill;
5540Sstevel@tonic-gate 	ilm_t 	*ilm;
5550Sstevel@tonic-gate 	in6_addr_t v6group;
5560Sstevel@tonic-gate 	int	ret;
5570Sstevel@tonic-gate 
5580Sstevel@tonic-gate 	ASSERT(IAM_WRITER_IPIF(ipif));
5590Sstevel@tonic-gate 
5600Sstevel@tonic-gate 	if (!CLASSD(group) && group != INADDR_ANY)
5610Sstevel@tonic-gate 		return (EINVAL);
5620Sstevel@tonic-gate 
5630Sstevel@tonic-gate 	/*
5640Sstevel@tonic-gate 	 * INADDR_ANY is represented as the IPv6 unspecifed addr.
5650Sstevel@tonic-gate 	 */
5660Sstevel@tonic-gate 	if (group == INADDR_ANY)
5670Sstevel@tonic-gate 		v6group = ipv6_all_zeros;
5680Sstevel@tonic-gate 	else
5690Sstevel@tonic-gate 		IN6_IPADDR_TO_V4MAPPED(group, &v6group);
5700Sstevel@tonic-gate 
5710Sstevel@tonic-gate 	ilm = ilm_lookup_ipif(ipif, group);
5720Sstevel@tonic-gate 	if (ilm != NULL)
5730Sstevel@tonic-gate 		return (ilm_update_add(ilm, ilgstat, ilg_flist, B_FALSE));
5740Sstevel@tonic-gate 
5750Sstevel@tonic-gate 	/*
5760Sstevel@tonic-gate 	 * ilms are associated with ipifs in IPv4. It moves with the
5770Sstevel@tonic-gate 	 * ipif if the ipif moves to a new ill when the interface
5780Sstevel@tonic-gate 	 * fails. Thus we really don't check whether the ipif_ill
5790Sstevel@tonic-gate 	 * has failed like in IPv6. If it has FAILED the ipif
5800Sstevel@tonic-gate 	 * will move (daemon will move it) and hence the ilm, if the
5810Sstevel@tonic-gate 	 * ipif is not IPIF_NOFAILOVER. For the IPIF_NOFAILOVER ipifs,
5820Sstevel@tonic-gate 	 * we continue to receive in the same place even if the
5830Sstevel@tonic-gate 	 * interface fails.
5840Sstevel@tonic-gate 	 */
5850Sstevel@tonic-gate 	ilm = ilm_add_v6(ipif, &v6group, ilgstat, ilg_fmode, ilg_flist,
5860Sstevel@tonic-gate 	    ill->ill_phyint->phyint_ifindex, ipif->ipif_zoneid);
5870Sstevel@tonic-gate 	if (ilm == NULL)
5880Sstevel@tonic-gate 		return (ENOMEM);
5890Sstevel@tonic-gate 
5900Sstevel@tonic-gate 	if (group == INADDR_ANY) {
5910Sstevel@tonic-gate 		/*
5920Sstevel@tonic-gate 		 * Check how many ipif's have members in this group -
5930Sstevel@tonic-gate 		 * if more then one we should not tell the driver to join
5940Sstevel@tonic-gate 		 * this time
5950Sstevel@tonic-gate 		 */
5960Sstevel@tonic-gate 		if (ilm_numentries_v6(ill, &v6group) > 1)
5970Sstevel@tonic-gate 			return (0);
5980Sstevel@tonic-gate 		if (ill->ill_group == NULL)
5990Sstevel@tonic-gate 			ret = ip_join_allmulti(ipif);
6000Sstevel@tonic-gate 		else
6010Sstevel@tonic-gate 			ret = ill_nominate_mcast_rcv(ill->ill_group);
6020Sstevel@tonic-gate 		if (ret != 0)
6030Sstevel@tonic-gate 			ilm_delete(ilm);
6040Sstevel@tonic-gate 		return (ret);
6050Sstevel@tonic-gate 	}
6060Sstevel@tonic-gate 
6070Sstevel@tonic-gate 	if ((ill->ill_phyint->phyint_flags & PHYI_LOOPBACK) == 0)
6080Sstevel@tonic-gate 		igmp_joingroup(ilm);
6090Sstevel@tonic-gate 
6100Sstevel@tonic-gate 	if (ilm_numentries_v6(ill, &v6group) > 1)
6110Sstevel@tonic-gate 		return (0);
6120Sstevel@tonic-gate 
6130Sstevel@tonic-gate 	ret = ip_ll_addmulti_v6(ipif, &v6group);
6140Sstevel@tonic-gate 	if (ret != 0)
6150Sstevel@tonic-gate 		ilm_delete(ilm);
6160Sstevel@tonic-gate 	return (ret);
6170Sstevel@tonic-gate }
6180Sstevel@tonic-gate 
6190Sstevel@tonic-gate /*
6200Sstevel@tonic-gate  * The unspecified address means all multicast addresses.
6210Sstevel@tonic-gate  * This is only used by the multicast router.
6220Sstevel@tonic-gate  *
6230Sstevel@tonic-gate  * ill identifies the interface to join on; it may not match the
6240Sstevel@tonic-gate  * interface requested by the application of a failover has taken
6250Sstevel@tonic-gate  * place.  orig_ifindex always identifies the interface requested
6260Sstevel@tonic-gate  * by the app.
6270Sstevel@tonic-gate  *
6280Sstevel@tonic-gate  * ilgstat tells us if there's an ilg associated with this join,
6290Sstevel@tonic-gate  * and if so, if it's a new ilg or a change to an existing one.
6300Sstevel@tonic-gate  * ilg_fmode and ilg_flist give us the current filter state of
6310Sstevel@tonic-gate  * the ilg (and will be EXCLUDE {NULL} in the case of no ilg).
6320Sstevel@tonic-gate  */
6330Sstevel@tonic-gate int
6340Sstevel@tonic-gate ip_addmulti_v6(const in6_addr_t *v6group, ill_t *ill, int orig_ifindex,
6350Sstevel@tonic-gate     zoneid_t zoneid, ilg_stat_t ilgstat, mcast_record_t ilg_fmode,
6360Sstevel@tonic-gate     slist_t *ilg_flist)
6370Sstevel@tonic-gate {
6380Sstevel@tonic-gate 	ilm_t	*ilm;
6390Sstevel@tonic-gate 	int	ret;
6400Sstevel@tonic-gate 
6410Sstevel@tonic-gate 	ASSERT(IAM_WRITER_ILL(ill));
6420Sstevel@tonic-gate 
6430Sstevel@tonic-gate 	if (!IN6_IS_ADDR_MULTICAST(v6group) &&
6440Sstevel@tonic-gate 	    !IN6_IS_ADDR_UNSPECIFIED(v6group)) {
6450Sstevel@tonic-gate 		return (EINVAL);
6460Sstevel@tonic-gate 	}
6470Sstevel@tonic-gate 
6480Sstevel@tonic-gate 	/*
6490Sstevel@tonic-gate 	 * An ilm is uniquely identified by the tuple of (group, ill,
6500Sstevel@tonic-gate 	 * orig_ill).  group is the multicast group address, ill is
6510Sstevel@tonic-gate 	 * the interface on which it is currently joined, and orig_ill
6520Sstevel@tonic-gate 	 * is the interface on which the application requested the
6530Sstevel@tonic-gate 	 * join.  orig_ill and ill are the same unless orig_ill has
6540Sstevel@tonic-gate 	 * failed over.
6550Sstevel@tonic-gate 	 *
6560Sstevel@tonic-gate 	 * Both orig_ill and ill are required, which means we may have
6570Sstevel@tonic-gate 	 * 2 ilms on an ill for the same group, but with different
6580Sstevel@tonic-gate 	 * orig_ills.  These must be kept separate, so that when failback
6590Sstevel@tonic-gate 	 * occurs, the appropriate ilms are moved back to their orig_ill
6600Sstevel@tonic-gate 	 * without disrupting memberships on the ill to which they had
6610Sstevel@tonic-gate 	 * been moved.
6620Sstevel@tonic-gate 	 *
6630Sstevel@tonic-gate 	 * In order to track orig_ill, we store orig_ifindex in the
6640Sstevel@tonic-gate 	 * ilm and ilg.
6650Sstevel@tonic-gate 	 */
6660Sstevel@tonic-gate 	ilm = ilm_lookup_ill_index_v6(ill, v6group, orig_ifindex, zoneid);
6670Sstevel@tonic-gate 	if (ilm != NULL)
6680Sstevel@tonic-gate 		return (ilm_update_add(ilm, ilgstat, ilg_flist, B_TRUE));
6690Sstevel@tonic-gate 
6700Sstevel@tonic-gate 	/*
6710Sstevel@tonic-gate 	 * We need to remember where the application really wanted
6720Sstevel@tonic-gate 	 * to join. This will be used later if we want to failback
6730Sstevel@tonic-gate 	 * to the original interface.
6740Sstevel@tonic-gate 	 */
6750Sstevel@tonic-gate 	ilm = ilm_add_v6(ill->ill_ipif, v6group, ilgstat, ilg_fmode,
6760Sstevel@tonic-gate 	    ilg_flist, orig_ifindex, zoneid);
6770Sstevel@tonic-gate 	if (ilm == NULL)
6780Sstevel@tonic-gate 		return (ENOMEM);
6790Sstevel@tonic-gate 
6800Sstevel@tonic-gate 	if (IN6_IS_ADDR_UNSPECIFIED(v6group)) {
6810Sstevel@tonic-gate 		/*
6820Sstevel@tonic-gate 		 * Check how many ipif's that have members in this group -
6830Sstevel@tonic-gate 		 * if more then one we should not tell the driver to join
6840Sstevel@tonic-gate 		 * this time
6850Sstevel@tonic-gate 		 */
6860Sstevel@tonic-gate 		if (ilm_numentries_v6(ill, v6group) > 1)
6870Sstevel@tonic-gate 			return (0);
6880Sstevel@tonic-gate 		if (ill->ill_group == NULL)
6890Sstevel@tonic-gate 			ret = ip_join_allmulti(ill->ill_ipif);
6900Sstevel@tonic-gate 		else
6910Sstevel@tonic-gate 			ret = ill_nominate_mcast_rcv(ill->ill_group);
6920Sstevel@tonic-gate 
6930Sstevel@tonic-gate 		if (ret != 0)
6940Sstevel@tonic-gate 			ilm_delete(ilm);
6950Sstevel@tonic-gate 		return (ret);
6960Sstevel@tonic-gate 	}
6970Sstevel@tonic-gate 
6980Sstevel@tonic-gate 	if ((ill->ill_phyint->phyint_flags & PHYI_LOOPBACK) == 0)
6990Sstevel@tonic-gate 		mld_joingroup(ilm);
7000Sstevel@tonic-gate 
7010Sstevel@tonic-gate 	/*
7020Sstevel@tonic-gate 	 * If we have more then one we should not tell the driver
7030Sstevel@tonic-gate 	 * to join this time.
7040Sstevel@tonic-gate 	 */
7050Sstevel@tonic-gate 	if (ilm_numentries_v6(ill, v6group) > 1)
7060Sstevel@tonic-gate 		return (0);
7070Sstevel@tonic-gate 
7080Sstevel@tonic-gate 	ret = ip_ll_addmulti_v6(ill->ill_ipif, v6group);
7090Sstevel@tonic-gate 	if (ret != 0)
7100Sstevel@tonic-gate 		ilm_delete(ilm);
7110Sstevel@tonic-gate 	return (ret);
7120Sstevel@tonic-gate }
7130Sstevel@tonic-gate 
7140Sstevel@tonic-gate /*
7150Sstevel@tonic-gate  * Send a multicast request to the driver for enabling multicast reception
7160Sstevel@tonic-gate  * for v6groupp address. The caller has already checked whether it is
7170Sstevel@tonic-gate  * appropriate to send one or not.
7180Sstevel@tonic-gate  */
7190Sstevel@tonic-gate int
7200Sstevel@tonic-gate ip_ll_send_enabmulti_req(ill_t *ill, const in6_addr_t *v6groupp)
7210Sstevel@tonic-gate {
7220Sstevel@tonic-gate 	mblk_t	*mp;
7230Sstevel@tonic-gate 	uint32_t addrlen, addroff;
7240Sstevel@tonic-gate 	char	group_buf[INET6_ADDRSTRLEN];
7250Sstevel@tonic-gate 
7260Sstevel@tonic-gate 	ASSERT(IAM_WRITER_ILL(ill));
7270Sstevel@tonic-gate 
7280Sstevel@tonic-gate 	/*
7290Sstevel@tonic-gate 	 * Create a AR_ENTRY_SQUERY message with a dl_enabmulti_req tacked
7300Sstevel@tonic-gate 	 * on.
7310Sstevel@tonic-gate 	 */
7320Sstevel@tonic-gate 	mp = ill_create_dl(ill, DL_ENABMULTI_REQ, sizeof (dl_enabmulti_req_t),
7330Sstevel@tonic-gate 	    &addrlen, &addroff);
7340Sstevel@tonic-gate 	if (!mp)
7350Sstevel@tonic-gate 		return (ENOMEM);
7360Sstevel@tonic-gate 	if (IN6_IS_ADDR_V4MAPPED(v6groupp)) {
7370Sstevel@tonic-gate 		ipaddr_t v4group;
7380Sstevel@tonic-gate 
7390Sstevel@tonic-gate 		IN6_V4MAPPED_TO_IPADDR(v6groupp, v4group);
7400Sstevel@tonic-gate 		/*
7410Sstevel@tonic-gate 		 * NOTE!!!
7420Sstevel@tonic-gate 		 * The "addroff" passed in here was calculated by
7430Sstevel@tonic-gate 		 * ill_create_dl(), and will be used by ill_create_squery()
7440Sstevel@tonic-gate 		 * to perform some twisted coding magic. It is the offset
7450Sstevel@tonic-gate 		 * into the dl_xxx_req of the hw addr. Here, it will be
7460Sstevel@tonic-gate 		 * added to b_wptr - b_rptr to create a magic number that
7470Sstevel@tonic-gate 		 * is not an offset into this squery mblk.
7480Sstevel@tonic-gate 		 * The actual hardware address will be accessed only in the
7490Sstevel@tonic-gate 		 * dl_xxx_req, not in the squery. More importantly,
7500Sstevel@tonic-gate 		 * that hardware address can *only* be accessed in this
7510Sstevel@tonic-gate 		 * mblk chain by calling mi_offset_param_c(), which uses
7520Sstevel@tonic-gate 		 * the magic number in the squery hw offset field to go
7530Sstevel@tonic-gate 		 * to the *next* mblk (the dl_xxx_req), subtract the
7540Sstevel@tonic-gate 		 * (b_wptr - b_rptr), and find the actual offset into
7550Sstevel@tonic-gate 		 * the dl_xxx_req.
7560Sstevel@tonic-gate 		 * Any method that depends on using the
7570Sstevel@tonic-gate 		 * offset field in the dl_disabmulti_req or squery
7580Sstevel@tonic-gate 		 * to find either hardware address will similarly fail.
7590Sstevel@tonic-gate 		 *
7600Sstevel@tonic-gate 		 * Look in ar_entry_squery() in arp.c to see how this offset
7610Sstevel@tonic-gate 		 * is used.
7620Sstevel@tonic-gate 		 */
7630Sstevel@tonic-gate 		mp = ill_create_squery(ill, v4group, addrlen, addroff, mp);
7640Sstevel@tonic-gate 		if (!mp)
7650Sstevel@tonic-gate 			return (ENOMEM);
7660Sstevel@tonic-gate 		ip1dbg(("ip_ll_send_enabmulti_req: IPv4 putnext %s on %s\n",
7670Sstevel@tonic-gate 		    inet_ntop(AF_INET6, v6groupp, group_buf,
7680Sstevel@tonic-gate 		    sizeof (group_buf)),
7690Sstevel@tonic-gate 		    ill->ill_name));
7700Sstevel@tonic-gate 		putnext(ill->ill_rq, mp);
7710Sstevel@tonic-gate 	} else {
7720Sstevel@tonic-gate 		ip1dbg(("ip_ll_send_enabmulti_req: IPv6 ndp_squery_mp %s on"
7730Sstevel@tonic-gate 		    " %s\n",
7740Sstevel@tonic-gate 		    inet_ntop(AF_INET6, v6groupp, group_buf,
7750Sstevel@tonic-gate 		    sizeof (group_buf)),
7760Sstevel@tonic-gate 		    ill->ill_name));
7770Sstevel@tonic-gate 		return (ndp_mcastreq(ill, v6groupp, addrlen, addroff, mp));
7780Sstevel@tonic-gate 	}
7790Sstevel@tonic-gate 	return (0);
7800Sstevel@tonic-gate }
7810Sstevel@tonic-gate 
7820Sstevel@tonic-gate /*
7830Sstevel@tonic-gate  * Send a multicast request to the driver for enabling multicast
7840Sstevel@tonic-gate  * membership for v6group if appropriate.
7850Sstevel@tonic-gate  */
7860Sstevel@tonic-gate static int
7870Sstevel@tonic-gate ip_ll_addmulti_v6(ipif_t *ipif, const in6_addr_t *v6groupp)
7880Sstevel@tonic-gate {
7890Sstevel@tonic-gate 	ill_t	*ill = ipif->ipif_ill;
7900Sstevel@tonic-gate 
7910Sstevel@tonic-gate 	ASSERT(IAM_WRITER_IPIF(ipif));
7920Sstevel@tonic-gate 
7930Sstevel@tonic-gate 	if (ill->ill_net_type != IRE_IF_RESOLVER ||
7940Sstevel@tonic-gate 	    ipif->ipif_flags & IPIF_POINTOPOINT) {
7950Sstevel@tonic-gate 		ip1dbg(("ip_ll_addmulti_v6: not resolver\n"));
7960Sstevel@tonic-gate 		return (0);	/* Must be IRE_IF_NORESOLVER */
7970Sstevel@tonic-gate 	}
7980Sstevel@tonic-gate 
7990Sstevel@tonic-gate 	if (ill->ill_phyint->phyint_flags & PHYI_MULTI_BCAST) {
8000Sstevel@tonic-gate 		ip1dbg(("ip_ll_addmulti_v6: MULTI_BCAST\n"));
8010Sstevel@tonic-gate 		return (0);
8020Sstevel@tonic-gate 	}
8030Sstevel@tonic-gate 	if (ill->ill_ipif_up_count == 0) {
8040Sstevel@tonic-gate 		/*
8050Sstevel@tonic-gate 		 * Nobody there. All multicast addresses will be re-joined
8060Sstevel@tonic-gate 		 * when we get the DL_BIND_ACK bringing the interface up.
8070Sstevel@tonic-gate 		 */
8080Sstevel@tonic-gate 		ip1dbg(("ip_ll_addmulti_v6: nobody up\n"));
8090Sstevel@tonic-gate 		return (0);
8100Sstevel@tonic-gate 	}
8110Sstevel@tonic-gate 	return (ip_ll_send_enabmulti_req(ill, v6groupp));
8120Sstevel@tonic-gate }
8130Sstevel@tonic-gate 
8140Sstevel@tonic-gate /*
8150Sstevel@tonic-gate  * INADDR_ANY means all multicast addresses. This is only used
8160Sstevel@tonic-gate  * by the multicast router.
8170Sstevel@tonic-gate  * INADDR_ANY is stored as the IPv6 unspecifed addr.
8180Sstevel@tonic-gate  */
8190Sstevel@tonic-gate int
8200Sstevel@tonic-gate ip_delmulti(ipaddr_t group, ipif_t *ipif, boolean_t no_ilg, boolean_t leaving)
8210Sstevel@tonic-gate {
8220Sstevel@tonic-gate 	ill_t	*ill = ipif->ipif_ill;
8230Sstevel@tonic-gate 	ilm_t *ilm;
8240Sstevel@tonic-gate 	in6_addr_t v6group;
8250Sstevel@tonic-gate 	int	ret;
8260Sstevel@tonic-gate 
8270Sstevel@tonic-gate 	ASSERT(IAM_WRITER_IPIF(ipif));
8280Sstevel@tonic-gate 
8290Sstevel@tonic-gate 	if (!CLASSD(group) && group != INADDR_ANY)
8300Sstevel@tonic-gate 		return (EINVAL);
8310Sstevel@tonic-gate 
8320Sstevel@tonic-gate 	/*
8330Sstevel@tonic-gate 	 * INADDR_ANY is represented as the IPv6 unspecifed addr.
8340Sstevel@tonic-gate 	 */
8350Sstevel@tonic-gate 	if (group == INADDR_ANY)
8360Sstevel@tonic-gate 		v6group = ipv6_all_zeros;
8370Sstevel@tonic-gate 	else
8380Sstevel@tonic-gate 		IN6_IPADDR_TO_V4MAPPED(group, &v6group);
8390Sstevel@tonic-gate 
8400Sstevel@tonic-gate 	/*
8410Sstevel@tonic-gate 	 * Look for a match on the ipif.
8420Sstevel@tonic-gate 	 * (IP_DROP_MEMBERSHIP specifies an ipif using an IP address).
8430Sstevel@tonic-gate 	 */
8440Sstevel@tonic-gate 	ilm = ilm_lookup_ipif(ipif, group);
8450Sstevel@tonic-gate 	if (ilm == NULL)
8460Sstevel@tonic-gate 		return (ENOENT);
8470Sstevel@tonic-gate 
8480Sstevel@tonic-gate 	/* Update counters */
8490Sstevel@tonic-gate 	if (no_ilg)
8500Sstevel@tonic-gate 		ilm->ilm_no_ilg_cnt--;
8510Sstevel@tonic-gate 
8520Sstevel@tonic-gate 	if (leaving)
8530Sstevel@tonic-gate 		ilm->ilm_refcnt--;
8540Sstevel@tonic-gate 
8550Sstevel@tonic-gate 	if (ilm->ilm_refcnt > 0)
8560Sstevel@tonic-gate 		return (ilm_update_del(ilm, B_FALSE));
8570Sstevel@tonic-gate 
8580Sstevel@tonic-gate 	if (group == INADDR_ANY) {
8590Sstevel@tonic-gate 		ilm_delete(ilm);
8600Sstevel@tonic-gate 		/*
8610Sstevel@tonic-gate 		 * Check how many ipif's that have members in this group -
8620Sstevel@tonic-gate 		 * if there are still some left then don't tell the driver
8630Sstevel@tonic-gate 		 * to drop it.
8640Sstevel@tonic-gate 		 */
8650Sstevel@tonic-gate 		if (ilm_numentries_v6(ill, &v6group) != 0)
8660Sstevel@tonic-gate 			return (0);
8670Sstevel@tonic-gate 
8680Sstevel@tonic-gate 		/*
8690Sstevel@tonic-gate 		 * If we never joined, then don't leave.  This can happen
8700Sstevel@tonic-gate 		 * if we're in an IPMP group, since only one ill per IPMP
8710Sstevel@tonic-gate 		 * group receives all multicast packets.
8720Sstevel@tonic-gate 		 */
8730Sstevel@tonic-gate 		if (!ill->ill_join_allmulti) {
8740Sstevel@tonic-gate 			ASSERT(ill->ill_group != NULL);
8750Sstevel@tonic-gate 			return (0);
8760Sstevel@tonic-gate 		}
8770Sstevel@tonic-gate 
8780Sstevel@tonic-gate 		ret = ip_leave_allmulti(ipif);
8790Sstevel@tonic-gate 		if (ill->ill_group != NULL)
8800Sstevel@tonic-gate 			(void) ill_nominate_mcast_rcv(ill->ill_group);
8810Sstevel@tonic-gate 		return (ret);
8820Sstevel@tonic-gate 	}
8830Sstevel@tonic-gate 
8840Sstevel@tonic-gate 	if ((ill->ill_phyint->phyint_flags & PHYI_LOOPBACK) == 0)
8850Sstevel@tonic-gate 		igmp_leavegroup(ilm);
8860Sstevel@tonic-gate 
8870Sstevel@tonic-gate 	ilm_delete(ilm);
8880Sstevel@tonic-gate 	/*
8890Sstevel@tonic-gate 	 * Check how many ipif's that have members in this group -
8900Sstevel@tonic-gate 	 * if there are still some left then don't tell the driver
8910Sstevel@tonic-gate 	 * to drop it.
8920Sstevel@tonic-gate 	 */
8930Sstevel@tonic-gate 	if (ilm_numentries_v6(ill, &v6group) != 0)
8940Sstevel@tonic-gate 		return (0);
8950Sstevel@tonic-gate 	return (ip_ll_delmulti_v6(ipif, &v6group));
8960Sstevel@tonic-gate }
8970Sstevel@tonic-gate 
8980Sstevel@tonic-gate /*
8990Sstevel@tonic-gate  * The unspecified address means all multicast addresses.
9000Sstevel@tonic-gate  * This is only used by the multicast router.
9010Sstevel@tonic-gate  */
9020Sstevel@tonic-gate int
9030Sstevel@tonic-gate ip_delmulti_v6(const in6_addr_t *v6group, ill_t *ill, int orig_ifindex,
9040Sstevel@tonic-gate     zoneid_t zoneid, boolean_t no_ilg, boolean_t leaving)
9050Sstevel@tonic-gate {
9060Sstevel@tonic-gate 	ipif_t	*ipif;
9070Sstevel@tonic-gate 	ilm_t *ilm;
9080Sstevel@tonic-gate 	int	ret;
9090Sstevel@tonic-gate 
9100Sstevel@tonic-gate 	ASSERT(IAM_WRITER_ILL(ill));
9110Sstevel@tonic-gate 
9120Sstevel@tonic-gate 	if (!IN6_IS_ADDR_MULTICAST(v6group) &&
9130Sstevel@tonic-gate 	    !IN6_IS_ADDR_UNSPECIFIED(v6group))
9140Sstevel@tonic-gate 		return (EINVAL);
9150Sstevel@tonic-gate 
9160Sstevel@tonic-gate 	/*
9170Sstevel@tonic-gate 	 * Look for a match on the ill.
9180Sstevel@tonic-gate 	 * (IPV6_LEAVE_GROUP specifies an ill using an ifindex).
9190Sstevel@tonic-gate 	 *
9200Sstevel@tonic-gate 	 * Similar to ip_addmulti_v6, we should always look using
9210Sstevel@tonic-gate 	 * the orig_ifindex.
9220Sstevel@tonic-gate 	 *
9230Sstevel@tonic-gate 	 * 1) If orig_ifindex is different from ill's ifindex
9240Sstevel@tonic-gate 	 *    we should have an ilm with orig_ifindex created in
9250Sstevel@tonic-gate 	 *    ip_addmulti_v6. We should delete that here.
9260Sstevel@tonic-gate 	 *
9270Sstevel@tonic-gate 	 * 2) If orig_ifindex is same as ill's ifindex, we should
9280Sstevel@tonic-gate 	 *    not delete the ilm that is temporarily here because of
9290Sstevel@tonic-gate 	 *    a FAILOVER. Those ilms will have a ilm_orig_ifindex
9300Sstevel@tonic-gate 	 *    different from ill's ifindex.
9310Sstevel@tonic-gate 	 *
9320Sstevel@tonic-gate 	 * Thus, always lookup using orig_ifindex.
9330Sstevel@tonic-gate 	 */
9340Sstevel@tonic-gate 	ilm = ilm_lookup_ill_index_v6(ill, v6group, orig_ifindex, zoneid);
9350Sstevel@tonic-gate 	if (ilm == NULL)
9360Sstevel@tonic-gate 		return (ENOENT);
9370Sstevel@tonic-gate 
9380Sstevel@tonic-gate 	ASSERT(ilm->ilm_ill == ill);
9390Sstevel@tonic-gate 
9400Sstevel@tonic-gate 	ipif = ill->ill_ipif;
9410Sstevel@tonic-gate 
9420Sstevel@tonic-gate 	/* Update counters */
9430Sstevel@tonic-gate 	if (no_ilg)
9440Sstevel@tonic-gate 		ilm->ilm_no_ilg_cnt--;
9450Sstevel@tonic-gate 
9460Sstevel@tonic-gate 	if (leaving)
9470Sstevel@tonic-gate 		ilm->ilm_refcnt--;
9480Sstevel@tonic-gate 
9490Sstevel@tonic-gate 	if (ilm->ilm_refcnt > 0)
9500Sstevel@tonic-gate 		return (ilm_update_del(ilm, B_TRUE));
9510Sstevel@tonic-gate 
9520Sstevel@tonic-gate 	if (IN6_IS_ADDR_UNSPECIFIED(v6group)) {
9530Sstevel@tonic-gate 		ilm_delete(ilm);
9540Sstevel@tonic-gate 		/*
9550Sstevel@tonic-gate 		 * Check how many ipif's that have members in this group -
9560Sstevel@tonic-gate 		 * if there are still some left then don't tell the driver
9570Sstevel@tonic-gate 		 * to drop it.
9580Sstevel@tonic-gate 		 */
9590Sstevel@tonic-gate 		if (ilm_numentries_v6(ill, v6group) != 0)
9600Sstevel@tonic-gate 			return (0);
9610Sstevel@tonic-gate 
9620Sstevel@tonic-gate 		/*
9630Sstevel@tonic-gate 		 * If we never joined, then don't leave.  This can happen
9640Sstevel@tonic-gate 		 * if we're in an IPMP group, since only one ill per IPMP
9650Sstevel@tonic-gate 		 * group receives all multicast packets.
9660Sstevel@tonic-gate 		 */
9670Sstevel@tonic-gate 		if (!ill->ill_join_allmulti) {
9680Sstevel@tonic-gate 			ASSERT(ill->ill_group != NULL);
9690Sstevel@tonic-gate 			return (0);
9700Sstevel@tonic-gate 		}
9710Sstevel@tonic-gate 
9720Sstevel@tonic-gate 		ret = ip_leave_allmulti(ipif);
9730Sstevel@tonic-gate 		if (ill->ill_group != NULL)
9740Sstevel@tonic-gate 			(void) ill_nominate_mcast_rcv(ill->ill_group);
9750Sstevel@tonic-gate 		return (ret);
9760Sstevel@tonic-gate 	}
9770Sstevel@tonic-gate 
9780Sstevel@tonic-gate 	if ((ill->ill_phyint->phyint_flags & PHYI_LOOPBACK) == 0)
9790Sstevel@tonic-gate 		mld_leavegroup(ilm);
9800Sstevel@tonic-gate 
9810Sstevel@tonic-gate 	ilm_delete(ilm);
9820Sstevel@tonic-gate 	/*
9830Sstevel@tonic-gate 	 * Check how many ipif's that have members in this group -
9840Sstevel@tonic-gate 	 * if there are still some left then don't tell the driver
9850Sstevel@tonic-gate 	 * to drop it.
9860Sstevel@tonic-gate 	 */
9870Sstevel@tonic-gate 	if (ilm_numentries_v6(ill, v6group) != 0)
9880Sstevel@tonic-gate 		return (0);
9890Sstevel@tonic-gate 	return (ip_ll_delmulti_v6(ipif, v6group));
9900Sstevel@tonic-gate }
9910Sstevel@tonic-gate 
9920Sstevel@tonic-gate /*
9930Sstevel@tonic-gate  * Send a multicast request to the driver for disabling multicast reception
9940Sstevel@tonic-gate  * for v6groupp address. The caller has already checked whether it is
9950Sstevel@tonic-gate  * appropriate to send one or not.
9960Sstevel@tonic-gate  */
9970Sstevel@tonic-gate int
9980Sstevel@tonic-gate ip_ll_send_disabmulti_req(ill_t *ill, const in6_addr_t *v6groupp)
9990Sstevel@tonic-gate {
10000Sstevel@tonic-gate 	mblk_t	*mp;
10010Sstevel@tonic-gate 	char	group_buf[INET6_ADDRSTRLEN];
10020Sstevel@tonic-gate 	uint32_t	addrlen, addroff;
10030Sstevel@tonic-gate 
10040Sstevel@tonic-gate 	ASSERT(IAM_WRITER_ILL(ill));
10050Sstevel@tonic-gate 	/*
10060Sstevel@tonic-gate 	 * Create a AR_ENTRY_SQUERY message with a dl_disabmulti_req tacked
10070Sstevel@tonic-gate 	 * on.
10080Sstevel@tonic-gate 	 */
10090Sstevel@tonic-gate 	mp = ill_create_dl(ill, DL_DISABMULTI_REQ,
10100Sstevel@tonic-gate 	    sizeof (dl_disabmulti_req_t), &addrlen, &addroff);
10110Sstevel@tonic-gate 
10120Sstevel@tonic-gate 	if (!mp)
10130Sstevel@tonic-gate 		return (ENOMEM);
10140Sstevel@tonic-gate 
10150Sstevel@tonic-gate 	if (IN6_IS_ADDR_V4MAPPED(v6groupp)) {
10160Sstevel@tonic-gate 		ipaddr_t v4group;
10170Sstevel@tonic-gate 
10180Sstevel@tonic-gate 		IN6_V4MAPPED_TO_IPADDR(v6groupp, v4group);
10190Sstevel@tonic-gate 		/*
10200Sstevel@tonic-gate 		 * NOTE!!!
10210Sstevel@tonic-gate 		 * The "addroff" passed in here was calculated by
10220Sstevel@tonic-gate 		 * ill_create_dl(), and will be used by ill_create_squery()
10230Sstevel@tonic-gate 		 * to perform some twisted coding magic. It is the offset
10240Sstevel@tonic-gate 		 * into the dl_xxx_req of the hw addr. Here, it will be
10250Sstevel@tonic-gate 		 * added to b_wptr - b_rptr to create a magic number that
10260Sstevel@tonic-gate 		 * is not an offset into this mblk.
10270Sstevel@tonic-gate 		 *
10280Sstevel@tonic-gate 		 * Please see the comment in ip_ll_send)enabmulti_req()
10290Sstevel@tonic-gate 		 * for a complete explanation.
10300Sstevel@tonic-gate 		 *
10310Sstevel@tonic-gate 		 * Look in ar_entry_squery() in arp.c to see how this offset
10320Sstevel@tonic-gate 		 * is used.
10330Sstevel@tonic-gate 		 */
10340Sstevel@tonic-gate 		mp = ill_create_squery(ill, v4group, addrlen, addroff, mp);
10350Sstevel@tonic-gate 		if (!mp)
10360Sstevel@tonic-gate 			return (ENOMEM);
10370Sstevel@tonic-gate 		ip1dbg(("ip_ll_send_disabmulti_req: IPv4 putnext %s on %s\n",
10380Sstevel@tonic-gate 		    inet_ntop(AF_INET6, v6groupp, group_buf,
10390Sstevel@tonic-gate 		    sizeof (group_buf)),
10400Sstevel@tonic-gate 		    ill->ill_name));
10410Sstevel@tonic-gate 		putnext(ill->ill_rq, mp);
10420Sstevel@tonic-gate 	} else {
10430Sstevel@tonic-gate 		ip1dbg(("ip_ll_send_disabmulti_req: IPv6 ndp_squery_mp %s on"
10440Sstevel@tonic-gate 		    " %s\n",
10450Sstevel@tonic-gate 		    inet_ntop(AF_INET6, v6groupp, group_buf,
10460Sstevel@tonic-gate 		    sizeof (group_buf)),
10470Sstevel@tonic-gate 		    ill->ill_name));
10480Sstevel@tonic-gate 		return (ndp_mcastreq(ill, v6groupp, addrlen, addroff, mp));
10490Sstevel@tonic-gate 	}
10500Sstevel@tonic-gate 	return (0);
10510Sstevel@tonic-gate }
10520Sstevel@tonic-gate 
10530Sstevel@tonic-gate /*
10540Sstevel@tonic-gate  * Send a multicast request to the driver for disabling multicast
10550Sstevel@tonic-gate  * membership for v6group if appropriate.
10560Sstevel@tonic-gate  */
10570Sstevel@tonic-gate static int
10580Sstevel@tonic-gate ip_ll_delmulti_v6(ipif_t *ipif, const in6_addr_t *v6group)
10590Sstevel@tonic-gate {
10600Sstevel@tonic-gate 	ill_t	*ill = ipif->ipif_ill;
10610Sstevel@tonic-gate 
10620Sstevel@tonic-gate 	ASSERT(IAM_WRITER_IPIF(ipif));
10630Sstevel@tonic-gate 
10640Sstevel@tonic-gate 	if (ill->ill_net_type != IRE_IF_RESOLVER ||
10650Sstevel@tonic-gate 	    ipif->ipif_flags & IPIF_POINTOPOINT) {
10660Sstevel@tonic-gate 		return (0);	/* Must be IRE_IF_NORESOLVER */
10670Sstevel@tonic-gate 	}
10680Sstevel@tonic-gate 	if (ill->ill_phyint->phyint_flags & PHYI_MULTI_BCAST) {
10690Sstevel@tonic-gate 		ip1dbg(("ip_ll_delmulti_v6: MULTI_BCAST\n"));
10700Sstevel@tonic-gate 		return (0);
10710Sstevel@tonic-gate 	}
10720Sstevel@tonic-gate 	if (ill->ill_ipif_up_count == 0) {
10730Sstevel@tonic-gate 		/*
10740Sstevel@tonic-gate 		 * Nobody there. All multicast addresses will be re-joined
10750Sstevel@tonic-gate 		 * when we get the DL_BIND_ACK bringing the interface up.
10760Sstevel@tonic-gate 		 */
10770Sstevel@tonic-gate 		ip1dbg(("ip_ll_delmulti_v6: nobody up\n"));
10780Sstevel@tonic-gate 		return (0);
10790Sstevel@tonic-gate 	}
10800Sstevel@tonic-gate 	return (ip_ll_send_disabmulti_req(ill, v6group));
10810Sstevel@tonic-gate }
10820Sstevel@tonic-gate 
10830Sstevel@tonic-gate /*
10840Sstevel@tonic-gate  * Make the driver pass up all multicast packets
10850Sstevel@tonic-gate  *
10860Sstevel@tonic-gate  * With ill groups, the caller makes sure that there is only
10870Sstevel@tonic-gate  * one ill joining the allmulti group.
10880Sstevel@tonic-gate  */
10890Sstevel@tonic-gate int
10900Sstevel@tonic-gate ip_join_allmulti(ipif_t *ipif)
10910Sstevel@tonic-gate {
10920Sstevel@tonic-gate 	ill_t	*ill = ipif->ipif_ill;
10930Sstevel@tonic-gate 	mblk_t	*mp;
10940Sstevel@tonic-gate 	uint32_t	addrlen, addroff;
10950Sstevel@tonic-gate 
10960Sstevel@tonic-gate 	ASSERT(IAM_WRITER_IPIF(ipif));
10970Sstevel@tonic-gate 
10980Sstevel@tonic-gate 	if (ill->ill_ipif_up_count == 0) {
10990Sstevel@tonic-gate 		/*
11000Sstevel@tonic-gate 		 * Nobody there. All multicast addresses will be re-joined
11010Sstevel@tonic-gate 		 * when we get the DL_BIND_ACK bringing the interface up.
11020Sstevel@tonic-gate 		 */
11030Sstevel@tonic-gate 		return (0);
11040Sstevel@tonic-gate 	}
11050Sstevel@tonic-gate 
11060Sstevel@tonic-gate 	ASSERT(!ill->ill_join_allmulti);
11070Sstevel@tonic-gate 
11080Sstevel@tonic-gate 	/*
11090Sstevel@tonic-gate 	 * Create a DL_PROMISCON_REQ message and send it directly to
11100Sstevel@tonic-gate 	 * the DLPI provider.  We don't need to do this for certain
11110Sstevel@tonic-gate 	 * media types for which we never need to turn promiscuous
11120Sstevel@tonic-gate 	 * mode on.
11130Sstevel@tonic-gate 	 */
11140Sstevel@tonic-gate 	if ((ill->ill_net_type == IRE_IF_RESOLVER) &&
11150Sstevel@tonic-gate 	    !(ill->ill_phyint->phyint_flags & PHYI_MULTI_BCAST)) {
11160Sstevel@tonic-gate 		mp = ill_create_dl(ill, DL_PROMISCON_REQ,
11170Sstevel@tonic-gate 		    sizeof (dl_promiscon_req_t), &addrlen, &addroff);
11180Sstevel@tonic-gate 		if (mp == NULL)
11190Sstevel@tonic-gate 			return (ENOMEM);
11200Sstevel@tonic-gate 		putnext(ill->ill_wq, mp);
11210Sstevel@tonic-gate 	}
11220Sstevel@tonic-gate 
11230Sstevel@tonic-gate 	mutex_enter(&ill->ill_lock);
11240Sstevel@tonic-gate 	ill->ill_join_allmulti = B_TRUE;
11250Sstevel@tonic-gate 	mutex_exit(&ill->ill_lock);
11260Sstevel@tonic-gate 	return (0);
11270Sstevel@tonic-gate }
11280Sstevel@tonic-gate 
11290Sstevel@tonic-gate /*
11300Sstevel@tonic-gate  * Make the driver stop passing up all multicast packets
11310Sstevel@tonic-gate  *
11320Sstevel@tonic-gate  * With ill groups, we need to nominate some other ill as
11330Sstevel@tonic-gate  * this ipif->ipif_ill is leaving the group.
11340Sstevel@tonic-gate  */
11350Sstevel@tonic-gate int
11360Sstevel@tonic-gate ip_leave_allmulti(ipif_t *ipif)
11370Sstevel@tonic-gate {
11380Sstevel@tonic-gate 	ill_t	*ill = ipif->ipif_ill;
11390Sstevel@tonic-gate 	mblk_t	*mp;
11400Sstevel@tonic-gate 	uint32_t	addrlen, addroff;
11410Sstevel@tonic-gate 
11420Sstevel@tonic-gate 	ASSERT(IAM_WRITER_IPIF(ipif));
11430Sstevel@tonic-gate 
11440Sstevel@tonic-gate 	if (ill->ill_ipif_up_count == 0) {
11450Sstevel@tonic-gate 		/*
11460Sstevel@tonic-gate 		 * Nobody there. All multicast addresses will be re-joined
11470Sstevel@tonic-gate 		 * when we get the DL_BIND_ACK bringing the interface up.
11480Sstevel@tonic-gate 		 */
11490Sstevel@tonic-gate 		return (0);
11500Sstevel@tonic-gate 	}
11510Sstevel@tonic-gate 
11520Sstevel@tonic-gate 	ASSERT(ill->ill_join_allmulti);
11530Sstevel@tonic-gate 
11540Sstevel@tonic-gate 	/*
11550Sstevel@tonic-gate 	 * Create a DL_PROMISCOFF_REQ message and send it directly to
11560Sstevel@tonic-gate 	 * the DLPI provider.  We don't need to do this for certain
11570Sstevel@tonic-gate 	 * media types for which we never need to turn promiscuous
11580Sstevel@tonic-gate 	 * mode on.
11590Sstevel@tonic-gate 	 */
11600Sstevel@tonic-gate 	if ((ill->ill_net_type == IRE_IF_RESOLVER) &&
11610Sstevel@tonic-gate 	    !(ill->ill_phyint->phyint_flags & PHYI_MULTI_BCAST)) {
11620Sstevel@tonic-gate 		mp = ill_create_dl(ill, DL_PROMISCOFF_REQ,
11630Sstevel@tonic-gate 		    sizeof (dl_promiscoff_req_t), &addrlen, &addroff);
11640Sstevel@tonic-gate 		if (mp == NULL)
11650Sstevel@tonic-gate 			return (ENOMEM);
11660Sstevel@tonic-gate 		putnext(ill->ill_wq, mp);
11670Sstevel@tonic-gate 	}
11680Sstevel@tonic-gate 
11690Sstevel@tonic-gate 	mutex_enter(&ill->ill_lock);
11700Sstevel@tonic-gate 	ill->ill_join_allmulti = B_FALSE;
11710Sstevel@tonic-gate 	mutex_exit(&ill->ill_lock);
11720Sstevel@tonic-gate 	return (0);
11730Sstevel@tonic-gate }
11740Sstevel@tonic-gate 
11750Sstevel@tonic-gate /*
11760Sstevel@tonic-gate  * Copy mp_orig and pass it in as a local message.
11770Sstevel@tonic-gate  */
11780Sstevel@tonic-gate void
11790Sstevel@tonic-gate ip_multicast_loopback(queue_t *q, ill_t *ill, mblk_t *mp_orig, int fanout_flags,
11800Sstevel@tonic-gate     zoneid_t zoneid)
11810Sstevel@tonic-gate {
1182741Smasputra 	mblk_t	*mp;
1183741Smasputra 	mblk_t	*ipsec_mp;
1184741Smasputra 
1185741Smasputra 	if (DB_TYPE(mp_orig) == M_DATA &&
1186741Smasputra 	    ((ipha_t *)mp_orig->b_rptr)->ipha_protocol == IPPROTO_UDP) {
1187741Smasputra 		uint_t hdrsz;
1188741Smasputra 
1189741Smasputra 		hdrsz = IPH_HDR_LENGTH((ipha_t *)mp_orig->b_rptr) +
1190741Smasputra 		    sizeof (udpha_t);
1191741Smasputra 		ASSERT(MBLKL(mp_orig) >= hdrsz);
1192741Smasputra 
1193741Smasputra 		if (((mp = allocb(hdrsz, BPRI_MED)) != NULL) &&
1194741Smasputra 		    (mp_orig = dupmsg(mp_orig)) != NULL) {
1195741Smasputra 			bcopy(mp_orig->b_rptr, mp->b_rptr, hdrsz);
1196741Smasputra 			mp->b_wptr += hdrsz;
1197741Smasputra 			mp->b_cont = mp_orig;
1198741Smasputra 			mp_orig->b_rptr += hdrsz;
1199741Smasputra 			if (MBLKL(mp_orig) == 0) {
1200741Smasputra 				mp->b_cont = mp_orig->b_cont;
1201741Smasputra 				mp_orig->b_cont = NULL;
1202741Smasputra 				freeb(mp_orig);
1203741Smasputra 			}
1204741Smasputra 		} else if (mp != NULL) {
1205741Smasputra 			freeb(mp);
1206741Smasputra 			mp = NULL;
1207741Smasputra 		}
1208741Smasputra 	} else {
1209741Smasputra 		mp = ip_copymsg(mp_orig);
1210741Smasputra 	}
1211741Smasputra 
12120Sstevel@tonic-gate 	if (mp == NULL)
12130Sstevel@tonic-gate 		return;
1214741Smasputra 	if (DB_TYPE(mp) == M_CTL) {
12150Sstevel@tonic-gate 		ipsec_mp = mp;
12160Sstevel@tonic-gate 		mp = mp->b_cont;
12170Sstevel@tonic-gate 	} else {
12180Sstevel@tonic-gate 		ipsec_mp = mp;
12190Sstevel@tonic-gate 	}
12200Sstevel@tonic-gate 	ip_wput_local(q, ill, (ipha_t *)mp->b_rptr, ipsec_mp, NULL,
12210Sstevel@tonic-gate 	    fanout_flags, zoneid);
12220Sstevel@tonic-gate }
12230Sstevel@tonic-gate 
12240Sstevel@tonic-gate static area_t	ip_aresq_template = {
12250Sstevel@tonic-gate 	AR_ENTRY_SQUERY,		/* cmd */
12260Sstevel@tonic-gate 	sizeof (area_t)+IP_ADDR_LEN,	/* name offset */
12270Sstevel@tonic-gate 	sizeof (area_t),	/* name len (filled by ill_arp_alloc) */
12280Sstevel@tonic-gate 	IP_ARP_PROTO_TYPE,		/* protocol, from arps perspective */
12290Sstevel@tonic-gate 	sizeof (area_t),			/* proto addr offset */
12300Sstevel@tonic-gate 	IP_ADDR_LEN,			/* proto addr_length */
12310Sstevel@tonic-gate 	0,				/* proto mask offset */
12320Sstevel@tonic-gate 	/* Rest is initialized when used */
12330Sstevel@tonic-gate 	0,				/* flags */
12340Sstevel@tonic-gate 	0,				/* hw addr offset */
12350Sstevel@tonic-gate 	0,				/* hw addr length */
12360Sstevel@tonic-gate };
12370Sstevel@tonic-gate 
12380Sstevel@tonic-gate static mblk_t *
12390Sstevel@tonic-gate ill_create_squery(ill_t *ill, ipaddr_t ipaddr, uint32_t addrlen,
12400Sstevel@tonic-gate     uint32_t addroff, mblk_t *mp_tail)
12410Sstevel@tonic-gate {
12420Sstevel@tonic-gate 	mblk_t	*mp;
12430Sstevel@tonic-gate 	area_t	*area;
12440Sstevel@tonic-gate 
12450Sstevel@tonic-gate 	mp = ill_arp_alloc(ill, (uchar_t *)&ip_aresq_template,
12460Sstevel@tonic-gate 				(caddr_t)&ipaddr);
12470Sstevel@tonic-gate 	if (!mp) {
12480Sstevel@tonic-gate 		freemsg(mp_tail);
12490Sstevel@tonic-gate 		return (NULL);
12500Sstevel@tonic-gate 	}
12510Sstevel@tonic-gate 	area = (area_t *)mp->b_rptr;
12520Sstevel@tonic-gate 	area->area_hw_addr_length = addrlen;
12530Sstevel@tonic-gate 	area->area_hw_addr_offset = mp->b_wptr - mp->b_rptr + addroff;
12540Sstevel@tonic-gate 	/*
12550Sstevel@tonic-gate 	 * NOTE!
12560Sstevel@tonic-gate 	 *
12570Sstevel@tonic-gate 	 * The area_hw_addr_offset, as can be seen, does not hold the
12580Sstevel@tonic-gate 	 * actual hardware address offset. Rather, it holds the offset
12590Sstevel@tonic-gate 	 * to the hw addr in the dl_xxx_req in mp_tail, modified by
12600Sstevel@tonic-gate 	 * adding (mp->b_wptr - mp->b_rptr). This allows the function
12610Sstevel@tonic-gate 	 * mi_offset_paramc() to find the hardware address in the
12620Sstevel@tonic-gate 	 * *second* mblk (dl_xxx_req), not this mblk.
12630Sstevel@tonic-gate 	 *
12640Sstevel@tonic-gate 	 * Using mi_offset_paramc() is thus the *only* way to access
12650Sstevel@tonic-gate 	 * the dl_xxx_hw address.
12660Sstevel@tonic-gate 	 *
12670Sstevel@tonic-gate 	 * The squery hw address should *not* be accessed.
12680Sstevel@tonic-gate 	 *
12690Sstevel@tonic-gate 	 * See ar_entry_squery() in arp.c for an example of how all this works.
12700Sstevel@tonic-gate 	 */
12710Sstevel@tonic-gate 
12720Sstevel@tonic-gate 	mp->b_cont = mp_tail;
12730Sstevel@tonic-gate 	return (mp);
12740Sstevel@tonic-gate }
12750Sstevel@tonic-gate 
12760Sstevel@tonic-gate /*
12770Sstevel@tonic-gate  * Create a dlpi message with room for phys+sap. When we come back in
12780Sstevel@tonic-gate  * ip_wput_ctl() we will strip the sap for those primitives which
12790Sstevel@tonic-gate  * only need a physical address.
12800Sstevel@tonic-gate  */
12810Sstevel@tonic-gate static mblk_t *
12820Sstevel@tonic-gate ill_create_dl(ill_t *ill, uint32_t dl_primitive, uint32_t length,
12830Sstevel@tonic-gate     uint32_t *addr_lenp, uint32_t *addr_offp)
12840Sstevel@tonic-gate {
12850Sstevel@tonic-gate 	mblk_t	*mp;
12860Sstevel@tonic-gate 	uint32_t	hw_addr_length;
12870Sstevel@tonic-gate 	char		*cp;
12880Sstevel@tonic-gate 	uint32_t	offset;
12890Sstevel@tonic-gate 	uint32_t 	size;
12900Sstevel@tonic-gate 
12910Sstevel@tonic-gate 	*addr_lenp = *addr_offp = 0;
12920Sstevel@tonic-gate 
12930Sstevel@tonic-gate 	hw_addr_length = ill->ill_phys_addr_length;
12940Sstevel@tonic-gate 	if (!hw_addr_length) {
12950Sstevel@tonic-gate 		ip0dbg(("ip_create_dl: hw addr length = 0\n"));
12960Sstevel@tonic-gate 		return (NULL);
12970Sstevel@tonic-gate 	}
12980Sstevel@tonic-gate 
12990Sstevel@tonic-gate 	size = length;
13000Sstevel@tonic-gate 	switch (dl_primitive) {
13010Sstevel@tonic-gate 	case DL_ENABMULTI_REQ:
13020Sstevel@tonic-gate 	case DL_DISABMULTI_REQ:
13030Sstevel@tonic-gate 		size += hw_addr_length;
13040Sstevel@tonic-gate 		break;
13050Sstevel@tonic-gate 	case DL_PROMISCON_REQ:
13060Sstevel@tonic-gate 	case DL_PROMISCOFF_REQ:
13070Sstevel@tonic-gate 		break;
13080Sstevel@tonic-gate 	default:
13090Sstevel@tonic-gate 		return (NULL);
13100Sstevel@tonic-gate 	}
13110Sstevel@tonic-gate 	mp = allocb(size, BPRI_HI);
13120Sstevel@tonic-gate 	if (!mp)
13130Sstevel@tonic-gate 		return (NULL);
13140Sstevel@tonic-gate 	mp->b_wptr += size;
13150Sstevel@tonic-gate 	mp->b_datap->db_type = M_PROTO;
13160Sstevel@tonic-gate 
13170Sstevel@tonic-gate 	cp = (char *)mp->b_rptr;
13180Sstevel@tonic-gate 	offset = length;
13190Sstevel@tonic-gate 
13200Sstevel@tonic-gate 	switch (dl_primitive) {
13210Sstevel@tonic-gate 	case DL_ENABMULTI_REQ: {
13220Sstevel@tonic-gate 		dl_enabmulti_req_t *dl = (dl_enabmulti_req_t *)cp;
13230Sstevel@tonic-gate 
13240Sstevel@tonic-gate 		dl->dl_primitive = dl_primitive;
13250Sstevel@tonic-gate 		dl->dl_addr_offset = offset;
13260Sstevel@tonic-gate 		*addr_lenp = dl->dl_addr_length = hw_addr_length;
13270Sstevel@tonic-gate 		*addr_offp = offset;
13280Sstevel@tonic-gate 		break;
13290Sstevel@tonic-gate 	}
13300Sstevel@tonic-gate 	case DL_DISABMULTI_REQ: {
13310Sstevel@tonic-gate 		dl_disabmulti_req_t *dl = (dl_disabmulti_req_t *)cp;
13320Sstevel@tonic-gate 
13330Sstevel@tonic-gate 		dl->dl_primitive = dl_primitive;
13340Sstevel@tonic-gate 		dl->dl_addr_offset = offset;
13350Sstevel@tonic-gate 		*addr_lenp = dl->dl_addr_length = hw_addr_length;
13360Sstevel@tonic-gate 		*addr_offp = offset;
13370Sstevel@tonic-gate 		break;
13380Sstevel@tonic-gate 	}
13390Sstevel@tonic-gate 	case DL_PROMISCON_REQ:
13400Sstevel@tonic-gate 	case DL_PROMISCOFF_REQ: {
13410Sstevel@tonic-gate 		dl_promiscon_req_t *dl = (dl_promiscon_req_t *)cp;
13420Sstevel@tonic-gate 
13430Sstevel@tonic-gate 		dl->dl_primitive = dl_primitive;
13440Sstevel@tonic-gate 		dl->dl_level = DL_PROMISC_MULTI;
13450Sstevel@tonic-gate 		break;
13460Sstevel@tonic-gate 	}
13470Sstevel@tonic-gate 	}
13480Sstevel@tonic-gate 	ip1dbg(("ill_create_dl: addr_len %d, addr_off %d\n",
13490Sstevel@tonic-gate 		*addr_lenp, *addr_offp));
13500Sstevel@tonic-gate 	return (mp);
13510Sstevel@tonic-gate }
13520Sstevel@tonic-gate 
13530Sstevel@tonic-gate void
13540Sstevel@tonic-gate ip_wput_ctl(queue_t *q, mblk_t *mp_orig)
13550Sstevel@tonic-gate {
13560Sstevel@tonic-gate 	ill_t	*ill = (ill_t *)q->q_ptr;
13570Sstevel@tonic-gate 	mblk_t	*mp = mp_orig;
13580Sstevel@tonic-gate 	area_t	*area;
13590Sstevel@tonic-gate 
13600Sstevel@tonic-gate 	/* Check that we have a AR_ENTRY_SQUERY with a tacked on mblk */
13610Sstevel@tonic-gate 	if ((mp->b_wptr - mp->b_rptr) < sizeof (area_t) ||
13620Sstevel@tonic-gate 	    mp->b_cont == NULL) {
13630Sstevel@tonic-gate 		putnext(q, mp);
13640Sstevel@tonic-gate 		return;
13650Sstevel@tonic-gate 	}
13660Sstevel@tonic-gate 	area = (area_t *)mp->b_rptr;
13670Sstevel@tonic-gate 	if (area->area_cmd != AR_ENTRY_SQUERY) {
13680Sstevel@tonic-gate 		putnext(q, mp);
13690Sstevel@tonic-gate 		return;
13700Sstevel@tonic-gate 	}
13710Sstevel@tonic-gate 	mp = mp->b_cont;
13720Sstevel@tonic-gate 	/*
13730Sstevel@tonic-gate 	 * Update dl_addr_length and dl_addr_offset for primitives that
13740Sstevel@tonic-gate 	 * have physical addresses as opposed to full saps
13750Sstevel@tonic-gate 	 */
13760Sstevel@tonic-gate 	switch (((union DL_primitives *)mp->b_rptr)->dl_primitive) {
13770Sstevel@tonic-gate 	case DL_ENABMULTI_REQ:
13780Sstevel@tonic-gate 		/* Track the state if this is the first enabmulti */
13790Sstevel@tonic-gate 		if (ill->ill_dlpi_multicast_state == IDMS_UNKNOWN)
13800Sstevel@tonic-gate 			ill->ill_dlpi_multicast_state = IDMS_INPROGRESS;
13810Sstevel@tonic-gate 		ip1dbg(("ip_wput_ctl: ENABMULTI\n"));
13820Sstevel@tonic-gate 		break;
13830Sstevel@tonic-gate 	case DL_DISABMULTI_REQ:
13840Sstevel@tonic-gate 		ip1dbg(("ip_wput_ctl: DISABMULTI\n"));
13850Sstevel@tonic-gate 		break;
13860Sstevel@tonic-gate 	default:
13870Sstevel@tonic-gate 		ip1dbg(("ip_wput_ctl: default\n"));
13880Sstevel@tonic-gate 		break;
13890Sstevel@tonic-gate 	}
13900Sstevel@tonic-gate 	freeb(mp_orig);
13910Sstevel@tonic-gate 	putnext(q, mp);
13920Sstevel@tonic-gate }
13930Sstevel@tonic-gate 
13940Sstevel@tonic-gate /*
13950Sstevel@tonic-gate  * Rejoin any groups which have been explicitly joined by the application (we
13960Sstevel@tonic-gate  * left all explicitly joined groups as part of ill_leave_multicast() prior to
13970Sstevel@tonic-gate  * bringing the interface down).  Note that because groups can be joined and
13980Sstevel@tonic-gate  * left while an interface is down, this may not be the same set of groups
13990Sstevel@tonic-gate  * that we left in ill_leave_multicast().
14000Sstevel@tonic-gate  */
14010Sstevel@tonic-gate void
14020Sstevel@tonic-gate ill_recover_multicast(ill_t *ill)
14030Sstevel@tonic-gate {
14040Sstevel@tonic-gate 	ilm_t	*ilm;
14050Sstevel@tonic-gate 	char    addrbuf[INET6_ADDRSTRLEN];
14060Sstevel@tonic-gate 
14070Sstevel@tonic-gate 	ASSERT(IAM_WRITER_ILL(ill));
14080Sstevel@tonic-gate 
14090Sstevel@tonic-gate 	for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
14100Sstevel@tonic-gate 		/*
14110Sstevel@tonic-gate 		 * Check how many ipif's that have members in this group -
14120Sstevel@tonic-gate 		 * if more then one we make sure that this entry is first
14130Sstevel@tonic-gate 		 * in the list.
14140Sstevel@tonic-gate 		 */
14150Sstevel@tonic-gate 		if (ilm_numentries_v6(ill, &ilm->ilm_v6addr) > 1 &&
14160Sstevel@tonic-gate 		    ilm_lookup_ill_v6(ill, &ilm->ilm_v6addr, ALL_ZONES) != ilm)
14170Sstevel@tonic-gate 			continue;
14180Sstevel@tonic-gate 		ip1dbg(("ill_recover_multicast: %s\n",
14190Sstevel@tonic-gate 		    inet_ntop(AF_INET6, &ilm->ilm_v6addr, addrbuf,
14200Sstevel@tonic-gate 		    sizeof (addrbuf))));
14210Sstevel@tonic-gate 		if (IN6_IS_ADDR_UNSPECIFIED(&ilm->ilm_v6addr)) {
14220Sstevel@tonic-gate 			if (ill->ill_group == NULL) {
14230Sstevel@tonic-gate 				(void) ip_join_allmulti(ill->ill_ipif);
14240Sstevel@tonic-gate 			} else {
14250Sstevel@tonic-gate 				/*
14260Sstevel@tonic-gate 				 * We don't want to join on this ill,
14270Sstevel@tonic-gate 				 * if somebody else in the group has
14280Sstevel@tonic-gate 				 * already been nominated.
14290Sstevel@tonic-gate 				 */
14300Sstevel@tonic-gate 				(void) ill_nominate_mcast_rcv(ill->ill_group);
14310Sstevel@tonic-gate 			}
14320Sstevel@tonic-gate 		} else {
14330Sstevel@tonic-gate 			(void) ip_ll_addmulti_v6(ill->ill_ipif,
14340Sstevel@tonic-gate 			    &ilm->ilm_v6addr);
14350Sstevel@tonic-gate 		}
14360Sstevel@tonic-gate 	}
14370Sstevel@tonic-gate }
14380Sstevel@tonic-gate 
14390Sstevel@tonic-gate /*
14400Sstevel@tonic-gate  * The opposite of ill_recover_multicast() -- leaves all multicast groups
14410Sstevel@tonic-gate  * that were explicitly joined.  Note that both these functions could be
14420Sstevel@tonic-gate  * disposed of if we enhanced ARP to allow us to handle DL_DISABMULTI_REQ
14430Sstevel@tonic-gate  * and DL_ENABMULTI_REQ messages when an interface is down.
14440Sstevel@tonic-gate  */
14450Sstevel@tonic-gate void
14460Sstevel@tonic-gate ill_leave_multicast(ill_t *ill)
14470Sstevel@tonic-gate {
14480Sstevel@tonic-gate 	ilm_t	*ilm;
14490Sstevel@tonic-gate 	char    addrbuf[INET6_ADDRSTRLEN];
14500Sstevel@tonic-gate 
14510Sstevel@tonic-gate 	ASSERT(IAM_WRITER_ILL(ill));
14520Sstevel@tonic-gate 
14530Sstevel@tonic-gate 	for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
14540Sstevel@tonic-gate 		/*
14550Sstevel@tonic-gate 		 * Check how many ipif's that have members in this group -
14560Sstevel@tonic-gate 		 * if more then one we make sure that this entry is first
14570Sstevel@tonic-gate 		 * in the list.
14580Sstevel@tonic-gate 		 */
14590Sstevel@tonic-gate 		if (ilm_numentries_v6(ill, &ilm->ilm_v6addr) > 1 &&
14600Sstevel@tonic-gate 		    ilm_lookup_ill_v6(ill, &ilm->ilm_v6addr, ALL_ZONES) != ilm)
14610Sstevel@tonic-gate 			continue;
14620Sstevel@tonic-gate 		ip1dbg(("ill_leave_multicast: %s\n",
14630Sstevel@tonic-gate 		    inet_ntop(AF_INET6, &ilm->ilm_v6addr, addrbuf,
14640Sstevel@tonic-gate 		    sizeof (addrbuf))));
14650Sstevel@tonic-gate 		if (IN6_IS_ADDR_UNSPECIFIED(&ilm->ilm_v6addr)) {
14660Sstevel@tonic-gate 			(void) ip_leave_allmulti(ill->ill_ipif);
14670Sstevel@tonic-gate 			/*
14680Sstevel@tonic-gate 			 * If we were part of an IPMP group, then
14690Sstevel@tonic-gate 			 * ill_handoff_responsibility() has already
14700Sstevel@tonic-gate 			 * nominated a new member (so we don't).
14710Sstevel@tonic-gate 			 */
14720Sstevel@tonic-gate 			ASSERT(ill->ill_group == NULL);
14730Sstevel@tonic-gate 		} else {
14740Sstevel@tonic-gate 			(void) ip_ll_send_disabmulti_req(ill, &ilm->ilm_v6addr);
14750Sstevel@tonic-gate 		}
14760Sstevel@tonic-gate 	}
14770Sstevel@tonic-gate }
14780Sstevel@tonic-gate 
14790Sstevel@tonic-gate /*
14800Sstevel@tonic-gate  * Find an ilm for matching the ill and which has the source in its
14810Sstevel@tonic-gate  * INCLUDE list or does not have it in its EXCLUDE list
14820Sstevel@tonic-gate  */
14830Sstevel@tonic-gate ilm_t *
14840Sstevel@tonic-gate ilm_lookup_ill_withsrc(ill_t *ill, ipaddr_t group, ipaddr_t src)
14850Sstevel@tonic-gate {
14860Sstevel@tonic-gate 	in6_addr_t	v6group, v6src;
14870Sstevel@tonic-gate 
14880Sstevel@tonic-gate 	/*
14890Sstevel@tonic-gate 	 * INADDR_ANY is represented as the IPv6 unspecified addr.
14900Sstevel@tonic-gate 	 */
14910Sstevel@tonic-gate 	if (group == INADDR_ANY)
14920Sstevel@tonic-gate 		v6group = ipv6_all_zeros;
14930Sstevel@tonic-gate 	else
14940Sstevel@tonic-gate 		IN6_IPADDR_TO_V4MAPPED(group, &v6group);
14950Sstevel@tonic-gate 	IN6_IPADDR_TO_V4MAPPED(src, &v6src);
14960Sstevel@tonic-gate 
14970Sstevel@tonic-gate 	return (ilm_lookup_ill_withsrc_v6(ill, &v6group, &v6src));
14980Sstevel@tonic-gate }
14990Sstevel@tonic-gate 
15000Sstevel@tonic-gate ilm_t *
15010Sstevel@tonic-gate ilm_lookup_ill_withsrc_v6(ill_t *ill, const in6_addr_t *v6group,
15020Sstevel@tonic-gate     const in6_addr_t *v6src)
15030Sstevel@tonic-gate {
15040Sstevel@tonic-gate 	ilm_t	*ilm;
15050Sstevel@tonic-gate 	boolean_t isinlist;
15060Sstevel@tonic-gate 	int	i, numsrc;
15070Sstevel@tonic-gate 
15080Sstevel@tonic-gate 	/*
15090Sstevel@tonic-gate 	 * If the source is in any ilm's INCLUDE list, or if
15100Sstevel@tonic-gate 	 * it is not in any ilm's EXCLUDE list, we have a hit.
15110Sstevel@tonic-gate 	 */
15120Sstevel@tonic-gate 	for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
15130Sstevel@tonic-gate 		if (IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, v6group)) {
15140Sstevel@tonic-gate 
15150Sstevel@tonic-gate 			isinlist = B_FALSE;
15160Sstevel@tonic-gate 			numsrc = (ilm->ilm_filter == NULL) ?
15170Sstevel@tonic-gate 			    0 : ilm->ilm_filter->sl_numsrc;
15180Sstevel@tonic-gate 			for (i = 0; i < numsrc; i++) {
15190Sstevel@tonic-gate 				if (IN6_ARE_ADDR_EQUAL(v6src,
15200Sstevel@tonic-gate 				    &ilm->ilm_filter->sl_addr[i])) {
15210Sstevel@tonic-gate 					isinlist = B_TRUE;
15220Sstevel@tonic-gate 					break;
15230Sstevel@tonic-gate 				}
15240Sstevel@tonic-gate 			}
15250Sstevel@tonic-gate 			if ((isinlist && ilm->ilm_fmode == MODE_IS_INCLUDE) ||
15260Sstevel@tonic-gate 			    (!isinlist && ilm->ilm_fmode == MODE_IS_EXCLUDE))
15270Sstevel@tonic-gate 				return (ilm);
15280Sstevel@tonic-gate 			else
15290Sstevel@tonic-gate 				return (NULL);
15300Sstevel@tonic-gate 		}
15310Sstevel@tonic-gate 	}
15320Sstevel@tonic-gate 	return (NULL);
15330Sstevel@tonic-gate }
15340Sstevel@tonic-gate 
15350Sstevel@tonic-gate 
15360Sstevel@tonic-gate /* Find an ilm for matching the ill */
15370Sstevel@tonic-gate ilm_t *
15380Sstevel@tonic-gate ilm_lookup_ill(ill_t *ill, ipaddr_t group, zoneid_t zoneid)
15390Sstevel@tonic-gate {
15400Sstevel@tonic-gate 	in6_addr_t	v6group;
15410Sstevel@tonic-gate 
15420Sstevel@tonic-gate 	ASSERT(ill->ill_ilm_walker_cnt != 0 || MUTEX_HELD(&ill->ill_lock) ||
15430Sstevel@tonic-gate 	    IAM_WRITER_ILL(ill));
15440Sstevel@tonic-gate 	/*
15450Sstevel@tonic-gate 	 * INADDR_ANY is represented as the IPv6 unspecifed addr.
15460Sstevel@tonic-gate 	 */
15470Sstevel@tonic-gate 	if (group == INADDR_ANY)
15480Sstevel@tonic-gate 		v6group = ipv6_all_zeros;
15490Sstevel@tonic-gate 	else
15500Sstevel@tonic-gate 		IN6_IPADDR_TO_V4MAPPED(group, &v6group);
15510Sstevel@tonic-gate 
15520Sstevel@tonic-gate 	return (ilm_lookup_ill_v6(ill, &v6group, zoneid));
15530Sstevel@tonic-gate }
15540Sstevel@tonic-gate 
15550Sstevel@tonic-gate /*
15560Sstevel@tonic-gate  * Find an ilm for matching the ill. All the ilm lookup functions
15570Sstevel@tonic-gate  * ignore ILM_DELETED ilms. These have been logically deleted, and
15580Sstevel@tonic-gate  * igmp and linklayer disable multicast have been done. Only mi_free
15590Sstevel@tonic-gate  * yet to be done. Still there in the list due to ilm_walkers. The
15600Sstevel@tonic-gate  * last walker will release it.
15610Sstevel@tonic-gate  */
15620Sstevel@tonic-gate ilm_t *
15630Sstevel@tonic-gate ilm_lookup_ill_v6(ill_t *ill, const in6_addr_t *v6group, zoneid_t zoneid)
15640Sstevel@tonic-gate {
15650Sstevel@tonic-gate 	ilm_t	*ilm;
15660Sstevel@tonic-gate 
15670Sstevel@tonic-gate 	ASSERT(ill->ill_ilm_walker_cnt != 0 || MUTEX_HELD(&ill->ill_lock) ||
15680Sstevel@tonic-gate 	    IAM_WRITER_ILL(ill));
15690Sstevel@tonic-gate 
15700Sstevel@tonic-gate 	for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
15710Sstevel@tonic-gate 		if (ilm->ilm_flags & ILM_DELETED)
15720Sstevel@tonic-gate 			continue;
15730Sstevel@tonic-gate 		if (IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, v6group) &&
15740Sstevel@tonic-gate 		    (zoneid == ALL_ZONES || zoneid == ilm->ilm_zoneid))
15750Sstevel@tonic-gate 			return (ilm);
15760Sstevel@tonic-gate 	}
15770Sstevel@tonic-gate 	return (NULL);
15780Sstevel@tonic-gate }
15790Sstevel@tonic-gate 
15800Sstevel@tonic-gate ilm_t *
15810Sstevel@tonic-gate ilm_lookup_ill_index_v6(ill_t *ill, const in6_addr_t *v6group, int index,
15820Sstevel@tonic-gate     zoneid_t zoneid)
15830Sstevel@tonic-gate {
15840Sstevel@tonic-gate 	ilm_t *ilm;
15850Sstevel@tonic-gate 
15860Sstevel@tonic-gate 	ASSERT(ill->ill_ilm_walker_cnt != 0 || MUTEX_HELD(&ill->ill_lock) ||
15870Sstevel@tonic-gate 	    IAM_WRITER_ILL(ill));
15880Sstevel@tonic-gate 
15890Sstevel@tonic-gate 	for (ilm = ill->ill_ilm; ilm != NULL; ilm = ilm->ilm_next) {
15900Sstevel@tonic-gate 		if (ilm->ilm_flags & ILM_DELETED)
15910Sstevel@tonic-gate 			continue;
15920Sstevel@tonic-gate 		if (IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, v6group) &&
15930Sstevel@tonic-gate 		    (zoneid == ALL_ZONES || zoneid == ilm->ilm_zoneid) &&
15940Sstevel@tonic-gate 		    ilm->ilm_orig_ifindex == index) {
15950Sstevel@tonic-gate 			return (ilm);
15960Sstevel@tonic-gate 		}
15970Sstevel@tonic-gate 	}
15980Sstevel@tonic-gate 	return (NULL);
15990Sstevel@tonic-gate }
16000Sstevel@tonic-gate 
16010Sstevel@tonic-gate ilm_t *
16020Sstevel@tonic-gate ilm_lookup_ill_index_v4(ill_t *ill, ipaddr_t group, int index, zoneid_t zoneid)
16030Sstevel@tonic-gate {
16040Sstevel@tonic-gate 	in6_addr_t	v6group;
16050Sstevel@tonic-gate 
16060Sstevel@tonic-gate 	ASSERT(ill->ill_ilm_walker_cnt != 0 || MUTEX_HELD(&ill->ill_lock) ||
16070Sstevel@tonic-gate 	    IAM_WRITER_ILL(ill));
16080Sstevel@tonic-gate 	/*
16090Sstevel@tonic-gate 	 * INADDR_ANY is represented as the IPv6 unspecifed addr.
16100Sstevel@tonic-gate 	 */
16110Sstevel@tonic-gate 	if (group == INADDR_ANY)
16120Sstevel@tonic-gate 		v6group = ipv6_all_zeros;
16130Sstevel@tonic-gate 	else
16140Sstevel@tonic-gate 		IN6_IPADDR_TO_V4MAPPED(group, &v6group);
16150Sstevel@tonic-gate 
16160Sstevel@tonic-gate 	return (ilm_lookup_ill_index_v6(ill, &v6group, index, zoneid));
16170Sstevel@tonic-gate }
16180Sstevel@tonic-gate 
16190Sstevel@tonic-gate /*
16200Sstevel@tonic-gate  * Found an ilm for the ipif. Only needed for IPv4 which does
16210Sstevel@tonic-gate  * ipif specific socket options.
16220Sstevel@tonic-gate  */
16230Sstevel@tonic-gate ilm_t *
16240Sstevel@tonic-gate ilm_lookup_ipif(ipif_t *ipif, ipaddr_t group)
16250Sstevel@tonic-gate {
16260Sstevel@tonic-gate 	ill_t	*ill = ipif->ipif_ill;
16270Sstevel@tonic-gate 	ilm_t	*ilm;
16280Sstevel@tonic-gate 	in6_addr_t	v6group;
16290Sstevel@tonic-gate 
16300Sstevel@tonic-gate 	ASSERT(ill->ill_ilm_walker_cnt != 0 || MUTEX_HELD(&ill->ill_lock) ||
16310Sstevel@tonic-gate 	    IAM_WRITER_ILL(ill));
16320Sstevel@tonic-gate 
16330Sstevel@tonic-gate 	/*
16340Sstevel@tonic-gate 	 * INADDR_ANY is represented as the IPv6 unspecifed addr.
16350Sstevel@tonic-gate 	 */
16360Sstevel@tonic-gate 	if (group == INADDR_ANY)
16370Sstevel@tonic-gate 		v6group = ipv6_all_zeros;
16380Sstevel@tonic-gate 	else
16390Sstevel@tonic-gate 		IN6_IPADDR_TO_V4MAPPED(group, &v6group);
16400Sstevel@tonic-gate 
16410Sstevel@tonic-gate 	for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
16420Sstevel@tonic-gate 		if (ilm->ilm_flags & ILM_DELETED)
16430Sstevel@tonic-gate 			continue;
16440Sstevel@tonic-gate 		if (ilm->ilm_ipif == ipif &&
16450Sstevel@tonic-gate 		    IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, &v6group))
16460Sstevel@tonic-gate 			return (ilm);
16470Sstevel@tonic-gate 	}
16480Sstevel@tonic-gate 	return (NULL);
16490Sstevel@tonic-gate }
16500Sstevel@tonic-gate 
16510Sstevel@tonic-gate /*
16520Sstevel@tonic-gate  * How many members on this ill?
16530Sstevel@tonic-gate  */
16540Sstevel@tonic-gate int
16550Sstevel@tonic-gate ilm_numentries_v6(ill_t *ill, const in6_addr_t *v6group)
16560Sstevel@tonic-gate {
16570Sstevel@tonic-gate 	ilm_t	*ilm;
16580Sstevel@tonic-gate 	int i = 0;
16590Sstevel@tonic-gate 
16600Sstevel@tonic-gate 	ASSERT(ill->ill_ilm_walker_cnt != 0 || MUTEX_HELD(&ill->ill_lock) ||
16610Sstevel@tonic-gate 	    IAM_WRITER_ILL(ill));
16620Sstevel@tonic-gate 
16630Sstevel@tonic-gate 	for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
16640Sstevel@tonic-gate 		if (ilm->ilm_flags & ILM_DELETED)
16650Sstevel@tonic-gate 			continue;
16660Sstevel@tonic-gate 		if (IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, v6group)) {
16670Sstevel@tonic-gate 			i++;
16680Sstevel@tonic-gate 		}
16690Sstevel@tonic-gate 	}
16700Sstevel@tonic-gate 	return (i);
16710Sstevel@tonic-gate }
16720Sstevel@tonic-gate 
16730Sstevel@tonic-gate /* Caller guarantees that the group is not already on the list */
16740Sstevel@tonic-gate static ilm_t *
16750Sstevel@tonic-gate ilm_add_v6(ipif_t *ipif, const in6_addr_t *v6group, ilg_stat_t ilgstat,
16760Sstevel@tonic-gate     mcast_record_t ilg_fmode, slist_t *ilg_flist, int orig_ifindex,
16770Sstevel@tonic-gate     zoneid_t zoneid)
16780Sstevel@tonic-gate {
16790Sstevel@tonic-gate 	ill_t	*ill = ipif->ipif_ill;
16800Sstevel@tonic-gate 	ilm_t	*ilm;
16810Sstevel@tonic-gate 	ilm_t	*ilm_cur;
16820Sstevel@tonic-gate 	ilm_t	**ilm_ptpn;
16830Sstevel@tonic-gate 
16840Sstevel@tonic-gate 	ASSERT(IAM_WRITER_IPIF(ipif));
16850Sstevel@tonic-gate 
16860Sstevel@tonic-gate 	ilm = GETSTRUCT(ilm_t, 1);
16870Sstevel@tonic-gate 	if (ilm == NULL)
16880Sstevel@tonic-gate 		return (NULL);
16890Sstevel@tonic-gate 	if (ilgstat != ILGSTAT_NONE && !SLIST_IS_EMPTY(ilg_flist)) {
16900Sstevel@tonic-gate 		ilm->ilm_filter = l_alloc();
16910Sstevel@tonic-gate 		if (ilm->ilm_filter == NULL) {
16920Sstevel@tonic-gate 			mi_free(ilm);
16930Sstevel@tonic-gate 			return (NULL);
16940Sstevel@tonic-gate 		}
16950Sstevel@tonic-gate 	}
16960Sstevel@tonic-gate 	ilm->ilm_v6addr = *v6group;
16970Sstevel@tonic-gate 	ilm->ilm_refcnt = 1;
16980Sstevel@tonic-gate 	ilm->ilm_zoneid = zoneid;
16990Sstevel@tonic-gate 	ilm->ilm_timer = INFINITY;
17000Sstevel@tonic-gate 	ilm->ilm_rtx.rtx_timer = INFINITY;
1701*1676Sjpk 
17020Sstevel@tonic-gate 	/*
17030Sstevel@tonic-gate 	 * IPv4 Multicast groups are joined using ipif.
17040Sstevel@tonic-gate 	 * IPv6 Multicast groups are joined using ill.
17050Sstevel@tonic-gate 	 */
17060Sstevel@tonic-gate 	if (ill->ill_isv6) {
17070Sstevel@tonic-gate 		ilm->ilm_ill = ill;
17080Sstevel@tonic-gate 		ilm->ilm_ipif = NULL;
17090Sstevel@tonic-gate 	} else {
17100Sstevel@tonic-gate 		ASSERT(ilm->ilm_zoneid == ipif->ipif_zoneid);
17110Sstevel@tonic-gate 		ilm->ilm_ipif = ipif;
17120Sstevel@tonic-gate 		ilm->ilm_ill = NULL;
17130Sstevel@tonic-gate 	}
17140Sstevel@tonic-gate 	/*
17150Sstevel@tonic-gate 	 * After this if ilm moves to a new ill, we don't change
17160Sstevel@tonic-gate 	 * the ilm_orig_ifindex. Thus, if ill_index != ilm_orig_ifindex,
17170Sstevel@tonic-gate 	 * it has been moved. Indexes don't match even when the application
17180Sstevel@tonic-gate 	 * wants to join on a FAILED/INACTIVE interface because we choose
17190Sstevel@tonic-gate 	 * a new interface to join in. This is considered as an implicit
17200Sstevel@tonic-gate 	 * move.
17210Sstevel@tonic-gate 	 */
17220Sstevel@tonic-gate 	ilm->ilm_orig_ifindex = orig_ifindex;
17230Sstevel@tonic-gate 
17240Sstevel@tonic-gate 	ASSERT(!(ipif->ipif_state_flags & IPIF_CONDEMNED));
17250Sstevel@tonic-gate 	ASSERT(!(ill->ill_state_flags & ILL_CONDEMNED));
17260Sstevel@tonic-gate 
17270Sstevel@tonic-gate 	/*
17280Sstevel@tonic-gate 	 * Grab lock to give consistent view to readers
17290Sstevel@tonic-gate 	 */
17300Sstevel@tonic-gate 	mutex_enter(&ill->ill_lock);
17310Sstevel@tonic-gate 	/*
17320Sstevel@tonic-gate 	 * All ilms in the same zone are contiguous in the ill_ilm list.
17330Sstevel@tonic-gate 	 * The loops in ip_proto_input() and ip_wput_local() use this to avoid
17340Sstevel@tonic-gate 	 * sending duplicates up when two applications in the same zone join the
17350Sstevel@tonic-gate 	 * same group on different logical interfaces.
17360Sstevel@tonic-gate 	 */
17370Sstevel@tonic-gate 	ilm_cur = ill->ill_ilm;
17380Sstevel@tonic-gate 	ilm_ptpn = &ill->ill_ilm;
17390Sstevel@tonic-gate 	while (ilm_cur != NULL && ilm_cur->ilm_zoneid != ilm->ilm_zoneid) {
17400Sstevel@tonic-gate 		ilm_ptpn = &ilm_cur->ilm_next;
17410Sstevel@tonic-gate 		ilm_cur = ilm_cur->ilm_next;
17420Sstevel@tonic-gate 	}
17430Sstevel@tonic-gate 	ilm->ilm_next = ilm_cur;
17440Sstevel@tonic-gate 	*ilm_ptpn = ilm;
17450Sstevel@tonic-gate 
17460Sstevel@tonic-gate 	/*
17470Sstevel@tonic-gate 	 * If we have an associated ilg, use its filter state; if not,
17480Sstevel@tonic-gate 	 * default to (EXCLUDE, NULL) and set no_ilg_cnt to track this.
17490Sstevel@tonic-gate 	 */
17500Sstevel@tonic-gate 	if (ilgstat != ILGSTAT_NONE) {
17510Sstevel@tonic-gate 		if (!SLIST_IS_EMPTY(ilg_flist))
17520Sstevel@tonic-gate 			l_copy(ilg_flist, ilm->ilm_filter);
17530Sstevel@tonic-gate 		ilm->ilm_fmode = ilg_fmode;
17540Sstevel@tonic-gate 	} else {
17550Sstevel@tonic-gate 		ilm->ilm_no_ilg_cnt = 1;
17560Sstevel@tonic-gate 		ilm->ilm_fmode = MODE_IS_EXCLUDE;
17570Sstevel@tonic-gate 	}
17580Sstevel@tonic-gate 
17590Sstevel@tonic-gate 	mutex_exit(&ill->ill_lock);
17600Sstevel@tonic-gate 	return (ilm);
17610Sstevel@tonic-gate }
17620Sstevel@tonic-gate 
17630Sstevel@tonic-gate void
17640Sstevel@tonic-gate ilm_walker_cleanup(ill_t *ill)
17650Sstevel@tonic-gate {
17660Sstevel@tonic-gate 	ilm_t	**ilmp;
17670Sstevel@tonic-gate 	ilm_t	*ilm;
17680Sstevel@tonic-gate 
17690Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&ill->ill_lock));
17700Sstevel@tonic-gate 	ASSERT(ill->ill_ilm_walker_cnt == 0);
17710Sstevel@tonic-gate 
17720Sstevel@tonic-gate 	ilmp = &ill->ill_ilm;
17730Sstevel@tonic-gate 	while (*ilmp != NULL) {
17740Sstevel@tonic-gate 		if ((*ilmp)->ilm_flags & ILM_DELETED) {
17750Sstevel@tonic-gate 			ilm = *ilmp;
17760Sstevel@tonic-gate 			*ilmp = ilm->ilm_next;
17770Sstevel@tonic-gate 			FREE_SLIST(ilm->ilm_filter);
17780Sstevel@tonic-gate 			FREE_SLIST(ilm->ilm_pendsrcs);
17790Sstevel@tonic-gate 			FREE_SLIST(ilm->ilm_rtx.rtx_allow);
17800Sstevel@tonic-gate 			FREE_SLIST(ilm->ilm_rtx.rtx_block);
17810Sstevel@tonic-gate 			mi_free((char *)ilm);
17820Sstevel@tonic-gate 		} else {
17830Sstevel@tonic-gate 			ilmp = &(*ilmp)->ilm_next;
17840Sstevel@tonic-gate 		}
17850Sstevel@tonic-gate 	}
17860Sstevel@tonic-gate 	ill->ill_ilm_cleanup_reqd = 0;
17870Sstevel@tonic-gate }
17880Sstevel@tonic-gate 
17890Sstevel@tonic-gate /*
17900Sstevel@tonic-gate  * Unlink ilm and free it.
17910Sstevel@tonic-gate  */
17920Sstevel@tonic-gate static void
17930Sstevel@tonic-gate ilm_delete(ilm_t *ilm)
17940Sstevel@tonic-gate {
17950Sstevel@tonic-gate 	ill_t	*ill;
17960Sstevel@tonic-gate 	ilm_t	**ilmp;
17970Sstevel@tonic-gate 
17980Sstevel@tonic-gate 	if (ilm->ilm_ipif != NULL) {
17990Sstevel@tonic-gate 		ASSERT(IAM_WRITER_IPIF(ilm->ilm_ipif));
18000Sstevel@tonic-gate 		ASSERT(ilm->ilm_ill == NULL);
18010Sstevel@tonic-gate 		ill = ilm->ilm_ipif->ipif_ill;
18020Sstevel@tonic-gate 		ASSERT(!ill->ill_isv6);
18030Sstevel@tonic-gate 	} else {
18040Sstevel@tonic-gate 		ASSERT(IAM_WRITER_ILL(ilm->ilm_ill));
18050Sstevel@tonic-gate 		ASSERT(ilm->ilm_ipif == NULL);
18060Sstevel@tonic-gate 		ill = ilm->ilm_ill;
18070Sstevel@tonic-gate 		ASSERT(ill->ill_isv6);
18080Sstevel@tonic-gate 	}
18090Sstevel@tonic-gate 	/*
18100Sstevel@tonic-gate 	 * Delete under lock protection so that readers don't stumble
18110Sstevel@tonic-gate 	 * on bad ilm_next
18120Sstevel@tonic-gate 	 */
18130Sstevel@tonic-gate 	mutex_enter(&ill->ill_lock);
18140Sstevel@tonic-gate 	if (ill->ill_ilm_walker_cnt != 0) {
18150Sstevel@tonic-gate 		ilm->ilm_flags |= ILM_DELETED;
18160Sstevel@tonic-gate 		ill->ill_ilm_cleanup_reqd = 1;
18170Sstevel@tonic-gate 		mutex_exit(&ill->ill_lock);
18180Sstevel@tonic-gate 		return;
18190Sstevel@tonic-gate 	}
18200Sstevel@tonic-gate 
18210Sstevel@tonic-gate 	for (ilmp = &ill->ill_ilm; *ilmp != ilm; ilmp = &(*ilmp)->ilm_next)
18220Sstevel@tonic-gate 				;
18230Sstevel@tonic-gate 	*ilmp = ilm->ilm_next;
18240Sstevel@tonic-gate 	mutex_exit(&ill->ill_lock);
18250Sstevel@tonic-gate 
18260Sstevel@tonic-gate 	FREE_SLIST(ilm->ilm_filter);
18270Sstevel@tonic-gate 	FREE_SLIST(ilm->ilm_pendsrcs);
18280Sstevel@tonic-gate 	FREE_SLIST(ilm->ilm_rtx.rtx_allow);
18290Sstevel@tonic-gate 	FREE_SLIST(ilm->ilm_rtx.rtx_block);
18300Sstevel@tonic-gate 	mi_free((char *)ilm);
18310Sstevel@tonic-gate }
18320Sstevel@tonic-gate 
18330Sstevel@tonic-gate /* Free all ilms for this ipif */
18340Sstevel@tonic-gate void
18350Sstevel@tonic-gate ilm_free(ipif_t *ipif)
18360Sstevel@tonic-gate {
18370Sstevel@tonic-gate 	ill_t	*ill = ipif->ipif_ill;
18380Sstevel@tonic-gate 	ilm_t	*ilm;
18390Sstevel@tonic-gate 	ilm_t	 *next_ilm;
18400Sstevel@tonic-gate 
18410Sstevel@tonic-gate 	ASSERT(IAM_WRITER_IPIF(ipif));
18420Sstevel@tonic-gate 
18430Sstevel@tonic-gate 	for (ilm = ill->ill_ilm; ilm; ilm = next_ilm) {
18440Sstevel@tonic-gate 		next_ilm = ilm->ilm_next;
18450Sstevel@tonic-gate 		if (ilm->ilm_ipif == ipif)
18460Sstevel@tonic-gate 			ilm_delete(ilm);
18470Sstevel@tonic-gate 	}
18480Sstevel@tonic-gate }
18490Sstevel@tonic-gate 
18500Sstevel@tonic-gate /*
18510Sstevel@tonic-gate  * Looks up the appropriate ipif given a v4 multicast group and interface
18520Sstevel@tonic-gate  * address.  On success, returns 0, with *ipifpp pointing to the found
18530Sstevel@tonic-gate  * struct.  On failure, returns an errno and *ipifpp is NULL.
18540Sstevel@tonic-gate  */
18550Sstevel@tonic-gate int
18560Sstevel@tonic-gate ip_opt_check(conn_t *connp, ipaddr_t group, ipaddr_t src, ipaddr_t ifaddr,
18570Sstevel@tonic-gate     uint_t *ifindexp, mblk_t *first_mp, ipsq_func_t func, ipif_t **ipifpp)
18580Sstevel@tonic-gate {
18590Sstevel@tonic-gate 	ipif_t *ipif;
18600Sstevel@tonic-gate 	int err = 0;
18610Sstevel@tonic-gate 	zoneid_t zoneid = connp->conn_zoneid;
18620Sstevel@tonic-gate 
18630Sstevel@tonic-gate 	if (!CLASSD(group) || CLASSD(src)) {
18640Sstevel@tonic-gate 		return (EINVAL);
18650Sstevel@tonic-gate 	}
18660Sstevel@tonic-gate 	*ipifpp = NULL;
18670Sstevel@tonic-gate 
18680Sstevel@tonic-gate 	ASSERT(!(ifaddr != INADDR_ANY && ifindexp != NULL && *ifindexp != 0));
18690Sstevel@tonic-gate 	if (ifaddr != INADDR_ANY) {
18700Sstevel@tonic-gate 		ipif = ipif_lookup_addr(ifaddr, NULL, zoneid,
18710Sstevel@tonic-gate 		    CONNP_TO_WQ(connp), first_mp, func, &err);
18720Sstevel@tonic-gate 		if (err != 0 && err != EINPROGRESS)
18730Sstevel@tonic-gate 			err = EADDRNOTAVAIL;
18740Sstevel@tonic-gate 	} else if (ifindexp != NULL && *ifindexp != 0) {
18750Sstevel@tonic-gate 		ipif = ipif_lookup_on_ifindex(*ifindexp, B_FALSE, zoneid,
18760Sstevel@tonic-gate 		    CONNP_TO_WQ(connp), first_mp, func, &err);
18770Sstevel@tonic-gate 	} else {
18780Sstevel@tonic-gate 		ipif = ipif_lookup_group(group, zoneid);
18790Sstevel@tonic-gate 		if (ipif == NULL)
18800Sstevel@tonic-gate 			return (EADDRNOTAVAIL);
18810Sstevel@tonic-gate 	}
18820Sstevel@tonic-gate 	if (ipif == NULL)
18830Sstevel@tonic-gate 		return (err);
18840Sstevel@tonic-gate 
18850Sstevel@tonic-gate 	*ipifpp = ipif;
18860Sstevel@tonic-gate 	return (0);
18870Sstevel@tonic-gate }
18880Sstevel@tonic-gate 
18890Sstevel@tonic-gate /*
18900Sstevel@tonic-gate  * Looks up the appropriate ill (or ipif if v4mapped) given an interface
18910Sstevel@tonic-gate  * index and IPv6 multicast group.  On success, returns 0, with *illpp (or
18920Sstevel@tonic-gate  * *ipifpp if v4mapped) pointing to the found struct.  On failure, returns
18930Sstevel@tonic-gate  * an errno and *illpp and *ipifpp are undefined.
18940Sstevel@tonic-gate  */
18950Sstevel@tonic-gate int
18960Sstevel@tonic-gate ip_opt_check_v6(conn_t *connp, const in6_addr_t *v6group, ipaddr_t *v4group,
18970Sstevel@tonic-gate     const in6_addr_t *v6src, ipaddr_t *v4src, boolean_t *isv6, int ifindex,
18980Sstevel@tonic-gate     mblk_t *first_mp, ipsq_func_t func, ill_t **illpp, ipif_t **ipifpp)
18990Sstevel@tonic-gate {
19000Sstevel@tonic-gate 	boolean_t src_unspec;
19010Sstevel@tonic-gate 	ill_t *ill = NULL;
19020Sstevel@tonic-gate 	ipif_t *ipif = NULL;
19030Sstevel@tonic-gate 	int err;
19040Sstevel@tonic-gate 	zoneid_t zoneid = connp->conn_zoneid;
19050Sstevel@tonic-gate 	queue_t *wq = CONNP_TO_WQ(connp);
19060Sstevel@tonic-gate 
19070Sstevel@tonic-gate 	src_unspec = IN6_IS_ADDR_UNSPECIFIED(v6src);
19080Sstevel@tonic-gate 
19090Sstevel@tonic-gate 	if (IN6_IS_ADDR_V4MAPPED(v6group)) {
19100Sstevel@tonic-gate 		if (!IN6_IS_ADDR_V4MAPPED(v6src) && !src_unspec)
19110Sstevel@tonic-gate 			return (EINVAL);
19120Sstevel@tonic-gate 		IN6_V4MAPPED_TO_IPADDR(v6group, *v4group);
19130Sstevel@tonic-gate 		if (src_unspec) {
19140Sstevel@tonic-gate 			*v4src = INADDR_ANY;
19150Sstevel@tonic-gate 		} else {
19160Sstevel@tonic-gate 			IN6_V4MAPPED_TO_IPADDR(v6src, *v4src);
19170Sstevel@tonic-gate 		}
19180Sstevel@tonic-gate 		if (!CLASSD(*v4group) || CLASSD(*v4src))
19190Sstevel@tonic-gate 			return (EINVAL);
19200Sstevel@tonic-gate 		*ipifpp = NULL;
19210Sstevel@tonic-gate 		*isv6 = B_FALSE;
19220Sstevel@tonic-gate 	} else {
19230Sstevel@tonic-gate 		if (IN6_IS_ADDR_V4MAPPED(v6src) && !src_unspec)
19240Sstevel@tonic-gate 			return (EINVAL);
19250Sstevel@tonic-gate 		if (!IN6_IS_ADDR_MULTICAST(v6group) ||
19260Sstevel@tonic-gate 		    IN6_IS_ADDR_MULTICAST(v6src)) {
19270Sstevel@tonic-gate 			return (EINVAL);
19280Sstevel@tonic-gate 		}
19290Sstevel@tonic-gate 		*illpp = NULL;
19300Sstevel@tonic-gate 		*isv6 = B_TRUE;
19310Sstevel@tonic-gate 	}
19320Sstevel@tonic-gate 
19330Sstevel@tonic-gate 	if (ifindex == 0) {
19340Sstevel@tonic-gate 		if (*isv6)
19350Sstevel@tonic-gate 			ill = ill_lookup_group_v6(v6group, zoneid);
19360Sstevel@tonic-gate 		else
19370Sstevel@tonic-gate 			ipif = ipif_lookup_group(*v4group, zoneid);
19380Sstevel@tonic-gate 		if (ill == NULL && ipif == NULL)
19390Sstevel@tonic-gate 			return (EADDRNOTAVAIL);
19400Sstevel@tonic-gate 	} else {
19410Sstevel@tonic-gate 		if (*isv6) {
19420Sstevel@tonic-gate 			ill = ill_lookup_on_ifindex(ifindex, B_TRUE,
19430Sstevel@tonic-gate 			    wq, first_mp, func, &err);
19440Sstevel@tonic-gate 			if (ill != NULL &&
19450Sstevel@tonic-gate 			    !ipif_lookup_zoneid(ill, zoneid, 0, NULL)) {
19460Sstevel@tonic-gate 				ill_refrele(ill);
19470Sstevel@tonic-gate 				ill = NULL;
19480Sstevel@tonic-gate 				err = EADDRNOTAVAIL;
19490Sstevel@tonic-gate 			}
19500Sstevel@tonic-gate 		} else {
19510Sstevel@tonic-gate 			ipif = ipif_lookup_on_ifindex(ifindex, B_FALSE,
19520Sstevel@tonic-gate 			    zoneid, wq, first_mp, func, &err);
19530Sstevel@tonic-gate 		}
19540Sstevel@tonic-gate 		if (ill == NULL && ipif == NULL)
19550Sstevel@tonic-gate 			return (err);
19560Sstevel@tonic-gate 	}
19570Sstevel@tonic-gate 
19580Sstevel@tonic-gate 	*ipifpp = ipif;
19590Sstevel@tonic-gate 	*illpp = ill;
19600Sstevel@tonic-gate 	return (0);
19610Sstevel@tonic-gate }
19620Sstevel@tonic-gate 
19630Sstevel@tonic-gate static int
19640Sstevel@tonic-gate ip_get_srcfilter(conn_t *connp, struct group_filter *gf,
19650Sstevel@tonic-gate     struct ip_msfilter *imsf, ipaddr_t grp, ipif_t *ipif, boolean_t isv4mapped)
19660Sstevel@tonic-gate {
19670Sstevel@tonic-gate 	ilg_t *ilg;
19680Sstevel@tonic-gate 	int i, numsrc, fmode, outsrcs;
19690Sstevel@tonic-gate 	struct sockaddr_in *sin;
19700Sstevel@tonic-gate 	struct sockaddr_in6 *sin6;
19710Sstevel@tonic-gate 	struct in_addr *addrp;
19720Sstevel@tonic-gate 	slist_t *fp;
19730Sstevel@tonic-gate 	boolean_t is_v4only_api;
19740Sstevel@tonic-gate 
19750Sstevel@tonic-gate 	mutex_enter(&connp->conn_lock);
19760Sstevel@tonic-gate 
19770Sstevel@tonic-gate 	ilg = ilg_lookup_ipif(connp, grp, ipif);
19780Sstevel@tonic-gate 	if (ilg == NULL) {
19790Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
19800Sstevel@tonic-gate 		return (EADDRNOTAVAIL);
19810Sstevel@tonic-gate 	}
19820Sstevel@tonic-gate 
19830Sstevel@tonic-gate 	if (gf == NULL) {
19840Sstevel@tonic-gate 		ASSERT(imsf != NULL);
19850Sstevel@tonic-gate 		ASSERT(!isv4mapped);
19860Sstevel@tonic-gate 		is_v4only_api = B_TRUE;
19870Sstevel@tonic-gate 		outsrcs = imsf->imsf_numsrc;
19880Sstevel@tonic-gate 	} else {
19890Sstevel@tonic-gate 		ASSERT(imsf == NULL);
19900Sstevel@tonic-gate 		is_v4only_api = B_FALSE;
19910Sstevel@tonic-gate 		outsrcs = gf->gf_numsrc;
19920Sstevel@tonic-gate 	}
19930Sstevel@tonic-gate 
19940Sstevel@tonic-gate 	/*
19950Sstevel@tonic-gate 	 * In the kernel, we use the state definitions MODE_IS_[IN|EX]CLUDE
19960Sstevel@tonic-gate 	 * to identify the filter mode; but the API uses MCAST_[IN|EX]CLUDE.
19970Sstevel@tonic-gate 	 * So we need to translate here.
19980Sstevel@tonic-gate 	 */
19990Sstevel@tonic-gate 	fmode = (ilg->ilg_fmode == MODE_IS_INCLUDE) ?
20000Sstevel@tonic-gate 	    MCAST_INCLUDE : MCAST_EXCLUDE;
20010Sstevel@tonic-gate 	if ((fp = ilg->ilg_filter) == NULL) {
20020Sstevel@tonic-gate 		numsrc = 0;
20030Sstevel@tonic-gate 	} else {
20040Sstevel@tonic-gate 		for (i = 0; i < outsrcs; i++) {
20050Sstevel@tonic-gate 			if (i == fp->sl_numsrc)
20060Sstevel@tonic-gate 				break;
20070Sstevel@tonic-gate 			if (isv4mapped) {
20080Sstevel@tonic-gate 				sin6 = (struct sockaddr_in6 *)&gf->gf_slist[i];
20090Sstevel@tonic-gate 				sin6->sin6_family = AF_INET6;
20100Sstevel@tonic-gate 				sin6->sin6_addr = fp->sl_addr[i];
20110Sstevel@tonic-gate 			} else {
20120Sstevel@tonic-gate 				if (is_v4only_api) {
20130Sstevel@tonic-gate 					addrp = &imsf->imsf_slist[i];
20140Sstevel@tonic-gate 				} else {
20150Sstevel@tonic-gate 					sin = (struct sockaddr_in *)
20160Sstevel@tonic-gate 					    &gf->gf_slist[i];
20170Sstevel@tonic-gate 					sin->sin_family = AF_INET;
20180Sstevel@tonic-gate 					addrp = &sin->sin_addr;
20190Sstevel@tonic-gate 				}
20200Sstevel@tonic-gate 				IN6_V4MAPPED_TO_INADDR(&fp->sl_addr[i], addrp);
20210Sstevel@tonic-gate 			}
20220Sstevel@tonic-gate 		}
20230Sstevel@tonic-gate 		numsrc = fp->sl_numsrc;
20240Sstevel@tonic-gate 	}
20250Sstevel@tonic-gate 
20260Sstevel@tonic-gate 	if (is_v4only_api) {
20270Sstevel@tonic-gate 		imsf->imsf_numsrc = numsrc;
20280Sstevel@tonic-gate 		imsf->imsf_fmode = fmode;
20290Sstevel@tonic-gate 	} else {
20300Sstevel@tonic-gate 		gf->gf_numsrc = numsrc;
20310Sstevel@tonic-gate 		gf->gf_fmode = fmode;
20320Sstevel@tonic-gate 	}
20330Sstevel@tonic-gate 
20340Sstevel@tonic-gate 	mutex_exit(&connp->conn_lock);
20350Sstevel@tonic-gate 
20360Sstevel@tonic-gate 	return (0);
20370Sstevel@tonic-gate }
20380Sstevel@tonic-gate 
20390Sstevel@tonic-gate static int
20400Sstevel@tonic-gate ip_get_srcfilter_v6(conn_t *connp, struct group_filter *gf,
20410Sstevel@tonic-gate     const struct in6_addr *grp, ill_t *ill)
20420Sstevel@tonic-gate {
20430Sstevel@tonic-gate 	ilg_t *ilg;
20440Sstevel@tonic-gate 	int i;
20450Sstevel@tonic-gate 	struct sockaddr_storage *sl;
20460Sstevel@tonic-gate 	struct sockaddr_in6 *sin6;
20470Sstevel@tonic-gate 	slist_t *fp;
20480Sstevel@tonic-gate 
20490Sstevel@tonic-gate 	mutex_enter(&connp->conn_lock);
20500Sstevel@tonic-gate 
20510Sstevel@tonic-gate 	ilg = ilg_lookup_ill_v6(connp, grp, ill);
20520Sstevel@tonic-gate 	if (ilg == NULL) {
20530Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
20540Sstevel@tonic-gate 		return (EADDRNOTAVAIL);
20550Sstevel@tonic-gate 	}
20560Sstevel@tonic-gate 
20570Sstevel@tonic-gate 	/*
20580Sstevel@tonic-gate 	 * In the kernel, we use the state definitions MODE_IS_[IN|EX]CLUDE
20590Sstevel@tonic-gate 	 * to identify the filter mode; but the API uses MCAST_[IN|EX]CLUDE.
20600Sstevel@tonic-gate 	 * So we need to translate here.
20610Sstevel@tonic-gate 	 */
20620Sstevel@tonic-gate 	gf->gf_fmode = (ilg->ilg_fmode == MODE_IS_INCLUDE) ?
20630Sstevel@tonic-gate 	    MCAST_INCLUDE : MCAST_EXCLUDE;
20640Sstevel@tonic-gate 	if ((fp = ilg->ilg_filter) == NULL) {
20650Sstevel@tonic-gate 		gf->gf_numsrc = 0;
20660Sstevel@tonic-gate 	} else {
20670Sstevel@tonic-gate 		for (i = 0, sl = gf->gf_slist; i < gf->gf_numsrc; i++, sl++) {
20680Sstevel@tonic-gate 			if (i == fp->sl_numsrc)
20690Sstevel@tonic-gate 				break;
20700Sstevel@tonic-gate 			sin6 = (struct sockaddr_in6 *)sl;
20710Sstevel@tonic-gate 			sin6->sin6_family = AF_INET6;
20720Sstevel@tonic-gate 			sin6->sin6_addr = fp->sl_addr[i];
20730Sstevel@tonic-gate 		}
20740Sstevel@tonic-gate 		gf->gf_numsrc = fp->sl_numsrc;
20750Sstevel@tonic-gate 	}
20760Sstevel@tonic-gate 
20770Sstevel@tonic-gate 	mutex_exit(&connp->conn_lock);
20780Sstevel@tonic-gate 
20790Sstevel@tonic-gate 	return (0);
20800Sstevel@tonic-gate }
20810Sstevel@tonic-gate 
20820Sstevel@tonic-gate static int
20830Sstevel@tonic-gate ip_set_srcfilter(conn_t *connp, struct group_filter *gf,
20840Sstevel@tonic-gate     struct ip_msfilter *imsf, ipaddr_t grp, ipif_t *ipif, boolean_t isv4mapped)
20850Sstevel@tonic-gate {
20860Sstevel@tonic-gate 	ilg_t *ilg;
20870Sstevel@tonic-gate 	int i, err, insrcs, infmode, new_fmode;
20880Sstevel@tonic-gate 	struct sockaddr_in *sin;
20890Sstevel@tonic-gate 	struct sockaddr_in6 *sin6;
20900Sstevel@tonic-gate 	struct in_addr *addrp;
20910Sstevel@tonic-gate 	slist_t *orig_filter = NULL;
20920Sstevel@tonic-gate 	slist_t *new_filter = NULL;
20930Sstevel@tonic-gate 	mcast_record_t orig_fmode;
20940Sstevel@tonic-gate 	boolean_t leave_grp, is_v4only_api;
20950Sstevel@tonic-gate 	ilg_stat_t ilgstat;
20960Sstevel@tonic-gate 
20970Sstevel@tonic-gate 	if (gf == NULL) {
20980Sstevel@tonic-gate 		ASSERT(imsf != NULL);
20990Sstevel@tonic-gate 		ASSERT(!isv4mapped);
21000Sstevel@tonic-gate 		is_v4only_api = B_TRUE;
21010Sstevel@tonic-gate 		insrcs = imsf->imsf_numsrc;
21020Sstevel@tonic-gate 		infmode = imsf->imsf_fmode;
21030Sstevel@tonic-gate 	} else {
21040Sstevel@tonic-gate 		ASSERT(imsf == NULL);
21050Sstevel@tonic-gate 		is_v4only_api = B_FALSE;
21060Sstevel@tonic-gate 		insrcs = gf->gf_numsrc;
21070Sstevel@tonic-gate 		infmode = gf->gf_fmode;
21080Sstevel@tonic-gate 	}
21090Sstevel@tonic-gate 
21100Sstevel@tonic-gate 	/* Make sure we can handle the source list */
21110Sstevel@tonic-gate 	if (insrcs > MAX_FILTER_SIZE)
21120Sstevel@tonic-gate 		return (ENOBUFS);
21130Sstevel@tonic-gate 
21140Sstevel@tonic-gate 	/*
21150Sstevel@tonic-gate 	 * setting the filter to (INCLUDE, NULL) is treated
21160Sstevel@tonic-gate 	 * as a request to leave the group.
21170Sstevel@tonic-gate 	 */
21180Sstevel@tonic-gate 	leave_grp = (infmode == MCAST_INCLUDE && insrcs == 0);
21190Sstevel@tonic-gate 
21200Sstevel@tonic-gate 	ASSERT(IAM_WRITER_IPIF(ipif));
21210Sstevel@tonic-gate 
21220Sstevel@tonic-gate 	mutex_enter(&connp->conn_lock);
21230Sstevel@tonic-gate 
21240Sstevel@tonic-gate 	ilg = ilg_lookup_ipif(connp, grp, ipif);
21250Sstevel@tonic-gate 	if (ilg == NULL) {
21260Sstevel@tonic-gate 		/*
21270Sstevel@tonic-gate 		 * if the request was actually to leave, and we
21280Sstevel@tonic-gate 		 * didn't find an ilg, there's nothing to do.
21290Sstevel@tonic-gate 		 */
21300Sstevel@tonic-gate 		if (!leave_grp)
21310Sstevel@tonic-gate 			ilg = conn_ilg_alloc(connp);
21320Sstevel@tonic-gate 		if (leave_grp || ilg == NULL) {
21330Sstevel@tonic-gate 			mutex_exit(&connp->conn_lock);
21340Sstevel@tonic-gate 			return (leave_grp ? 0 : ENOMEM);
21350Sstevel@tonic-gate 		}
21360Sstevel@tonic-gate 		ilgstat = ILGSTAT_NEW;
21370Sstevel@tonic-gate 		IN6_IPADDR_TO_V4MAPPED(grp, &ilg->ilg_v6group);
21380Sstevel@tonic-gate 		ilg->ilg_ipif = ipif;
21390Sstevel@tonic-gate 		ilg->ilg_ill = NULL;
21400Sstevel@tonic-gate 		ilg->ilg_orig_ifindex = 0;
21410Sstevel@tonic-gate 	} else if (leave_grp) {
21420Sstevel@tonic-gate 		ilg_delete(connp, ilg, NULL);
21430Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
21440Sstevel@tonic-gate 		(void) ip_delmulti(grp, ipif, B_FALSE, B_TRUE);
21450Sstevel@tonic-gate 		return (0);
21460Sstevel@tonic-gate 	} else {
21470Sstevel@tonic-gate 		ilgstat = ILGSTAT_CHANGE;
21480Sstevel@tonic-gate 		/* Preserve existing state in case ip_addmulti() fails */
21490Sstevel@tonic-gate 		orig_fmode = ilg->ilg_fmode;
21500Sstevel@tonic-gate 		if (ilg->ilg_filter == NULL) {
21510Sstevel@tonic-gate 			orig_filter = NULL;
21520Sstevel@tonic-gate 		} else {
21530Sstevel@tonic-gate 			orig_filter = l_alloc_copy(ilg->ilg_filter);
21540Sstevel@tonic-gate 			if (orig_filter == NULL) {
21550Sstevel@tonic-gate 				mutex_exit(&connp->conn_lock);
21560Sstevel@tonic-gate 				return (ENOMEM);
21570Sstevel@tonic-gate 			}
21580Sstevel@tonic-gate 		}
21590Sstevel@tonic-gate 	}
21600Sstevel@tonic-gate 
21610Sstevel@tonic-gate 	/*
21620Sstevel@tonic-gate 	 * Alloc buffer to copy new state into (see below) before
21630Sstevel@tonic-gate 	 * we make any changes, so we can bail if it fails.
21640Sstevel@tonic-gate 	 */
21650Sstevel@tonic-gate 	if ((new_filter = l_alloc()) == NULL) {
21660Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
21670Sstevel@tonic-gate 		err = ENOMEM;
21680Sstevel@tonic-gate 		goto free_and_exit;
21690Sstevel@tonic-gate 	}
21700Sstevel@tonic-gate 
21710Sstevel@tonic-gate 	if (insrcs == 0) {
21720Sstevel@tonic-gate 		CLEAR_SLIST(ilg->ilg_filter);
21730Sstevel@tonic-gate 	} else {
21740Sstevel@tonic-gate 		slist_t *fp;
21750Sstevel@tonic-gate 		if (ilg->ilg_filter == NULL) {
21760Sstevel@tonic-gate 			fp = l_alloc();
21770Sstevel@tonic-gate 			if (fp == NULL) {
21780Sstevel@tonic-gate 				if (ilgstat == ILGSTAT_NEW)
21790Sstevel@tonic-gate 					ilg_delete(connp, ilg, NULL);
21800Sstevel@tonic-gate 				mutex_exit(&connp->conn_lock);
21810Sstevel@tonic-gate 				err = ENOMEM;
21820Sstevel@tonic-gate 				goto free_and_exit;
21830Sstevel@tonic-gate 			}
21840Sstevel@tonic-gate 		} else {
21850Sstevel@tonic-gate 			fp = ilg->ilg_filter;
21860Sstevel@tonic-gate 		}
21870Sstevel@tonic-gate 		for (i = 0; i < insrcs; i++) {
21880Sstevel@tonic-gate 			if (isv4mapped) {
21890Sstevel@tonic-gate 				sin6 = (struct sockaddr_in6 *)&gf->gf_slist[i];
21900Sstevel@tonic-gate 				fp->sl_addr[i] = sin6->sin6_addr;
21910Sstevel@tonic-gate 			} else {
21920Sstevel@tonic-gate 				if (is_v4only_api) {
21930Sstevel@tonic-gate 					addrp = &imsf->imsf_slist[i];
21940Sstevel@tonic-gate 				} else {
21950Sstevel@tonic-gate 					sin = (struct sockaddr_in *)
21960Sstevel@tonic-gate 					    &gf->gf_slist[i];
21970Sstevel@tonic-gate 					addrp = &sin->sin_addr;
21980Sstevel@tonic-gate 				}
21990Sstevel@tonic-gate 				IN6_INADDR_TO_V4MAPPED(addrp, &fp->sl_addr[i]);
22000Sstevel@tonic-gate 			}
22010Sstevel@tonic-gate 		}
22020Sstevel@tonic-gate 		fp->sl_numsrc = insrcs;
22030Sstevel@tonic-gate 		ilg->ilg_filter = fp;
22040Sstevel@tonic-gate 	}
22050Sstevel@tonic-gate 	/*
22060Sstevel@tonic-gate 	 * In the kernel, we use the state definitions MODE_IS_[IN|EX]CLUDE
22070Sstevel@tonic-gate 	 * to identify the filter mode; but the API uses MCAST_[IN|EX]CLUDE.
22080Sstevel@tonic-gate 	 * So we need to translate here.
22090Sstevel@tonic-gate 	 */
22100Sstevel@tonic-gate 	ilg->ilg_fmode = (infmode == MCAST_INCLUDE) ?
22110Sstevel@tonic-gate 		    MODE_IS_INCLUDE : MODE_IS_EXCLUDE;
22120Sstevel@tonic-gate 
22130Sstevel@tonic-gate 	/*
22140Sstevel@tonic-gate 	 * Save copy of ilg's filter state to pass to other functions,
22150Sstevel@tonic-gate 	 * so we can release conn_lock now.
22160Sstevel@tonic-gate 	 */
22170Sstevel@tonic-gate 	new_fmode = ilg->ilg_fmode;
22180Sstevel@tonic-gate 	l_copy(ilg->ilg_filter, new_filter);
22190Sstevel@tonic-gate 
22200Sstevel@tonic-gate 	mutex_exit(&connp->conn_lock);
22210Sstevel@tonic-gate 
22220Sstevel@tonic-gate 	err = ip_addmulti(grp, ipif, ilgstat, new_fmode, new_filter);
22230Sstevel@tonic-gate 	if (err != 0) {
22240Sstevel@tonic-gate 		/*
22250Sstevel@tonic-gate 		 * Restore the original filter state, or delete the
22260Sstevel@tonic-gate 		 * newly-created ilg.  We need to look up the ilg
22270Sstevel@tonic-gate 		 * again, though, since we've not been holding the
22280Sstevel@tonic-gate 		 * conn_lock.
22290Sstevel@tonic-gate 		 */
22300Sstevel@tonic-gate 		mutex_enter(&connp->conn_lock);
22310Sstevel@tonic-gate 		ilg = ilg_lookup_ipif(connp, grp, ipif);
22320Sstevel@tonic-gate 		ASSERT(ilg != NULL);
22330Sstevel@tonic-gate 		if (ilgstat == ILGSTAT_NEW) {
22340Sstevel@tonic-gate 			ilg_delete(connp, ilg, NULL);
22350Sstevel@tonic-gate 		} else {
22360Sstevel@tonic-gate 			ilg->ilg_fmode = orig_fmode;
22370Sstevel@tonic-gate 			if (SLIST_IS_EMPTY(orig_filter)) {
22380Sstevel@tonic-gate 				CLEAR_SLIST(ilg->ilg_filter);
22390Sstevel@tonic-gate 			} else {
22400Sstevel@tonic-gate 				/*
22410Sstevel@tonic-gate 				 * We didn't free the filter, even if we
22420Sstevel@tonic-gate 				 * were trying to make the source list empty;
22430Sstevel@tonic-gate 				 * so if orig_filter isn't empty, the ilg
22440Sstevel@tonic-gate 				 * must still have a filter alloc'd.
22450Sstevel@tonic-gate 				 */
22460Sstevel@tonic-gate 				l_copy(orig_filter, ilg->ilg_filter);
22470Sstevel@tonic-gate 			}
22480Sstevel@tonic-gate 		}
22490Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
22500Sstevel@tonic-gate 	}
22510Sstevel@tonic-gate 
22520Sstevel@tonic-gate free_and_exit:
22530Sstevel@tonic-gate 	l_free(orig_filter);
22540Sstevel@tonic-gate 	l_free(new_filter);
22550Sstevel@tonic-gate 
22560Sstevel@tonic-gate 	return (err);
22570Sstevel@tonic-gate }
22580Sstevel@tonic-gate 
22590Sstevel@tonic-gate static int
22600Sstevel@tonic-gate ip_set_srcfilter_v6(conn_t *connp, struct group_filter *gf,
22610Sstevel@tonic-gate     const struct in6_addr *grp, ill_t *ill)
22620Sstevel@tonic-gate {
22630Sstevel@tonic-gate 	ilg_t *ilg;
22640Sstevel@tonic-gate 	int i, orig_ifindex, orig_fmode, new_fmode, err;
22650Sstevel@tonic-gate 	slist_t *orig_filter = NULL;
22660Sstevel@tonic-gate 	slist_t *new_filter = NULL;
22670Sstevel@tonic-gate 	struct sockaddr_storage *sl;
22680Sstevel@tonic-gate 	struct sockaddr_in6 *sin6;
22690Sstevel@tonic-gate 	boolean_t leave_grp;
22700Sstevel@tonic-gate 	ilg_stat_t ilgstat;
22710Sstevel@tonic-gate 
22720Sstevel@tonic-gate 	/* Make sure we can handle the source list */
22730Sstevel@tonic-gate 	if (gf->gf_numsrc > MAX_FILTER_SIZE)
22740Sstevel@tonic-gate 		return (ENOBUFS);
22750Sstevel@tonic-gate 
22760Sstevel@tonic-gate 	/*
22770Sstevel@tonic-gate 	 * setting the filter to (INCLUDE, NULL) is treated
22780Sstevel@tonic-gate 	 * as a request to leave the group.
22790Sstevel@tonic-gate 	 */
22800Sstevel@tonic-gate 	leave_grp = (gf->gf_fmode == MCAST_INCLUDE && gf->gf_numsrc == 0);
22810Sstevel@tonic-gate 
22820Sstevel@tonic-gate 	ASSERT(IAM_WRITER_ILL(ill));
22830Sstevel@tonic-gate 
22840Sstevel@tonic-gate 	/*
22850Sstevel@tonic-gate 	 * Use the ifindex to do the lookup.  We can't use the ill
22860Sstevel@tonic-gate 	 * directly because ilg_ill could point to a different ill
22870Sstevel@tonic-gate 	 * if things have moved.
22880Sstevel@tonic-gate 	 */
22890Sstevel@tonic-gate 	orig_ifindex = ill->ill_phyint->phyint_ifindex;
22900Sstevel@tonic-gate 
22910Sstevel@tonic-gate 	mutex_enter(&connp->conn_lock);
22920Sstevel@tonic-gate 	ilg = ilg_lookup_ill_index_v6(connp, grp, orig_ifindex);
22930Sstevel@tonic-gate 	if (ilg == NULL) {
22940Sstevel@tonic-gate 		/*
22950Sstevel@tonic-gate 		 * if the request was actually to leave, and we
22960Sstevel@tonic-gate 		 * didn't find an ilg, there's nothing to do.
22970Sstevel@tonic-gate 		 */
22980Sstevel@tonic-gate 		if (!leave_grp)
22990Sstevel@tonic-gate 			ilg = conn_ilg_alloc(connp);
23000Sstevel@tonic-gate 		if (leave_grp || ilg == NULL) {
23010Sstevel@tonic-gate 			mutex_exit(&connp->conn_lock);
23020Sstevel@tonic-gate 			return (leave_grp ? 0 : ENOMEM);
23030Sstevel@tonic-gate 		}
23040Sstevel@tonic-gate 		ilgstat = ILGSTAT_NEW;
23050Sstevel@tonic-gate 		ilg->ilg_v6group = *grp;
23060Sstevel@tonic-gate 		ilg->ilg_ipif = NULL;
23070Sstevel@tonic-gate 		/*
23080Sstevel@tonic-gate 		 * Choose our target ill to join on. This might be
23090Sstevel@tonic-gate 		 * different from the ill we've been given if it's
23100Sstevel@tonic-gate 		 * currently down and part of a group.
23110Sstevel@tonic-gate 		 *
23120Sstevel@tonic-gate 		 * new ill is not refheld; we are writer.
23130Sstevel@tonic-gate 		 */
23140Sstevel@tonic-gate 		ill = ip_choose_multi_ill(ill, grp);
23150Sstevel@tonic-gate 		ASSERT(!(ill->ill_state_flags & ILL_CONDEMNED));
23160Sstevel@tonic-gate 		ilg->ilg_ill = ill;
23170Sstevel@tonic-gate 		/*
23180Sstevel@tonic-gate 		 * Remember the index that we joined on, so that we can
23190Sstevel@tonic-gate 		 * successfully delete them later on and also search for
23200Sstevel@tonic-gate 		 * duplicates if the application wants to join again.
23210Sstevel@tonic-gate 		 */
23220Sstevel@tonic-gate 		ilg->ilg_orig_ifindex = orig_ifindex;
23230Sstevel@tonic-gate 	} else if (leave_grp) {
23240Sstevel@tonic-gate 		/*
23250Sstevel@tonic-gate 		 * Use the ilg's current ill for the deletion,
23260Sstevel@tonic-gate 		 * we might have failed over.
23270Sstevel@tonic-gate 		 */
23280Sstevel@tonic-gate 		ill = ilg->ilg_ill;
23290Sstevel@tonic-gate 		ilg_delete(connp, ilg, NULL);
23300Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
23310Sstevel@tonic-gate 		(void) ip_delmulti_v6(grp, ill, orig_ifindex,
23320Sstevel@tonic-gate 		    connp->conn_zoneid, B_FALSE, B_TRUE);
23330Sstevel@tonic-gate 		return (0);
23340Sstevel@tonic-gate 	} else {
23350Sstevel@tonic-gate 		ilgstat = ILGSTAT_CHANGE;
23360Sstevel@tonic-gate 		/*
23370Sstevel@tonic-gate 		 * The current ill might be different from the one we were
23380Sstevel@tonic-gate 		 * asked to join on (if failover has occurred); we should
23390Sstevel@tonic-gate 		 * join on the ill stored in the ilg.  The original ill
23400Sstevel@tonic-gate 		 * is noted in ilg_orig_ifindex, which matched our request.
23410Sstevel@tonic-gate 		 */
23420Sstevel@tonic-gate 		ill = ilg->ilg_ill;
23430Sstevel@tonic-gate 		/* preserve existing state in case ip_addmulti() fails */
23440Sstevel@tonic-gate 		orig_fmode = ilg->ilg_fmode;
23450Sstevel@tonic-gate 		if (ilg->ilg_filter == NULL) {
23460Sstevel@tonic-gate 			orig_filter = NULL;
23470Sstevel@tonic-gate 		} else {
23480Sstevel@tonic-gate 			orig_filter = l_alloc_copy(ilg->ilg_filter);
23490Sstevel@tonic-gate 			if (orig_filter == NULL) {
23500Sstevel@tonic-gate 				mutex_exit(&connp->conn_lock);
23510Sstevel@tonic-gate 				return (ENOMEM);
23520Sstevel@tonic-gate 			}
23530Sstevel@tonic-gate 		}
23540Sstevel@tonic-gate 	}
23550Sstevel@tonic-gate 
23560Sstevel@tonic-gate 	/*
23570Sstevel@tonic-gate 	 * Alloc buffer to copy new state into (see below) before
23580Sstevel@tonic-gate 	 * we make any changes, so we can bail if it fails.
23590Sstevel@tonic-gate 	 */
23600Sstevel@tonic-gate 	if ((new_filter = l_alloc()) == NULL) {
23610Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
23620Sstevel@tonic-gate 		err = ENOMEM;
23630Sstevel@tonic-gate 		goto free_and_exit;
23640Sstevel@tonic-gate 	}
23650Sstevel@tonic-gate 
23660Sstevel@tonic-gate 	if (gf->gf_numsrc == 0) {
23670Sstevel@tonic-gate 		CLEAR_SLIST(ilg->ilg_filter);
23680Sstevel@tonic-gate 	} else {
23690Sstevel@tonic-gate 		slist_t *fp;
23700Sstevel@tonic-gate 		if (ilg->ilg_filter == NULL) {
23710Sstevel@tonic-gate 			fp = l_alloc();
23720Sstevel@tonic-gate 			if (fp == NULL) {
23730Sstevel@tonic-gate 				if (ilgstat == ILGSTAT_NEW)
23740Sstevel@tonic-gate 					ilg_delete(connp, ilg, NULL);
23750Sstevel@tonic-gate 				mutex_exit(&connp->conn_lock);
23760Sstevel@tonic-gate 				err = ENOMEM;
23770Sstevel@tonic-gate 				goto free_and_exit;
23780Sstevel@tonic-gate 			}
23790Sstevel@tonic-gate 		} else {
23800Sstevel@tonic-gate 			fp = ilg->ilg_filter;
23810Sstevel@tonic-gate 		}
23820Sstevel@tonic-gate 		for (i = 0, sl = gf->gf_slist; i < gf->gf_numsrc; i++, sl++) {
23830Sstevel@tonic-gate 			sin6 = (struct sockaddr_in6 *)sl;
23840Sstevel@tonic-gate 			fp->sl_addr[i] = sin6->sin6_addr;
23850Sstevel@tonic-gate 		}
23860Sstevel@tonic-gate 		fp->sl_numsrc = gf->gf_numsrc;
23870Sstevel@tonic-gate 		ilg->ilg_filter = fp;
23880Sstevel@tonic-gate 	}
23890Sstevel@tonic-gate 	/*
23900Sstevel@tonic-gate 	 * In the kernel, we use the state definitions MODE_IS_[IN|EX]CLUDE
23910Sstevel@tonic-gate 	 * to identify the filter mode; but the API uses MCAST_[IN|EX]CLUDE.
23920Sstevel@tonic-gate 	 * So we need to translate here.
23930Sstevel@tonic-gate 	 */
23940Sstevel@tonic-gate 	ilg->ilg_fmode = (gf->gf_fmode == MCAST_INCLUDE) ?
23950Sstevel@tonic-gate 	    MODE_IS_INCLUDE : MODE_IS_EXCLUDE;
23960Sstevel@tonic-gate 
23970Sstevel@tonic-gate 	/*
23980Sstevel@tonic-gate 	 * Save copy of ilg's filter state to pass to other functions,
23990Sstevel@tonic-gate 	 * so we can release conn_lock now.
24000Sstevel@tonic-gate 	 */
24010Sstevel@tonic-gate 	new_fmode = ilg->ilg_fmode;
24020Sstevel@tonic-gate 	l_copy(ilg->ilg_filter, new_filter);
24030Sstevel@tonic-gate 
24040Sstevel@tonic-gate 	mutex_exit(&connp->conn_lock);
24050Sstevel@tonic-gate 
24060Sstevel@tonic-gate 	err = ip_addmulti_v6(grp, ill, orig_ifindex, connp->conn_zoneid,
24070Sstevel@tonic-gate 	    ilgstat, new_fmode, new_filter);
24080Sstevel@tonic-gate 	if (err != 0) {
24090Sstevel@tonic-gate 		/*
24100Sstevel@tonic-gate 		 * Restore the original filter state, or delete the
24110Sstevel@tonic-gate 		 * newly-created ilg.  We need to look up the ilg
24120Sstevel@tonic-gate 		 * again, though, since we've not been holding the
24130Sstevel@tonic-gate 		 * conn_lock.
24140Sstevel@tonic-gate 		 */
24150Sstevel@tonic-gate 		mutex_enter(&connp->conn_lock);
24160Sstevel@tonic-gate 		ilg = ilg_lookup_ill_index_v6(connp, grp, orig_ifindex);
24170Sstevel@tonic-gate 		ASSERT(ilg != NULL);
24180Sstevel@tonic-gate 		if (ilgstat == ILGSTAT_NEW) {
24190Sstevel@tonic-gate 			ilg_delete(connp, ilg, NULL);
24200Sstevel@tonic-gate 		} else {
24210Sstevel@tonic-gate 			ilg->ilg_fmode = orig_fmode;
24220Sstevel@tonic-gate 			if (SLIST_IS_EMPTY(orig_filter)) {
24230Sstevel@tonic-gate 				CLEAR_SLIST(ilg->ilg_filter);
24240Sstevel@tonic-gate 			} else {
24250Sstevel@tonic-gate 				/*
24260Sstevel@tonic-gate 				 * We didn't free the filter, even if we
24270Sstevel@tonic-gate 				 * were trying to make the source list empty;
24280Sstevel@tonic-gate 				 * so if orig_filter isn't empty, the ilg
24290Sstevel@tonic-gate 				 * must still have a filter alloc'd.
24300Sstevel@tonic-gate 				 */
24310Sstevel@tonic-gate 				l_copy(orig_filter, ilg->ilg_filter);
24320Sstevel@tonic-gate 			}
24330Sstevel@tonic-gate 		}
24340Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
24350Sstevel@tonic-gate 	}
24360Sstevel@tonic-gate 
24370Sstevel@tonic-gate free_and_exit:
24380Sstevel@tonic-gate 	l_free(orig_filter);
24390Sstevel@tonic-gate 	l_free(new_filter);
24400Sstevel@tonic-gate 
24410Sstevel@tonic-gate 	return (err);
24420Sstevel@tonic-gate }
24430Sstevel@tonic-gate 
24440Sstevel@tonic-gate /*
24450Sstevel@tonic-gate  * Process the SIOC[GS]MSFILTER and SIOC[GS]IPMSFILTER ioctls.
24460Sstevel@tonic-gate  */
24470Sstevel@tonic-gate /* ARGSUSED */
24480Sstevel@tonic-gate int
24490Sstevel@tonic-gate ip_sioctl_msfilter(ipif_t *ipif, sin_t *dummy_sin, queue_t *q, mblk_t *mp,
24500Sstevel@tonic-gate     ip_ioctl_cmd_t *ipip, void *ifreq)
24510Sstevel@tonic-gate {
24520Sstevel@tonic-gate 	struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
24530Sstevel@tonic-gate 	/* existence verified in ip_wput_nondata() */
24540Sstevel@tonic-gate 	mblk_t *data_mp = mp->b_cont->b_cont;
24550Sstevel@tonic-gate 	int datalen, err, cmd, minsize;
24560Sstevel@tonic-gate 	int expsize = 0;
24570Sstevel@tonic-gate 	conn_t *connp;
24580Sstevel@tonic-gate 	boolean_t isv6, is_v4only_api, getcmd;
24590Sstevel@tonic-gate 	struct sockaddr_in *gsin;
24600Sstevel@tonic-gate 	struct sockaddr_in6 *gsin6;
24610Sstevel@tonic-gate 	ipaddr_t v4grp;
24620Sstevel@tonic-gate 	in6_addr_t v6grp;
24630Sstevel@tonic-gate 	struct group_filter *gf = NULL;
24640Sstevel@tonic-gate 	struct ip_msfilter *imsf = NULL;
24650Sstevel@tonic-gate 	mblk_t *ndp;
24660Sstevel@tonic-gate 
24670Sstevel@tonic-gate 	if (data_mp->b_cont != NULL) {
24680Sstevel@tonic-gate 		if ((ndp = msgpullup(data_mp, -1)) == NULL)
24690Sstevel@tonic-gate 			return (ENOMEM);
24700Sstevel@tonic-gate 		freemsg(data_mp);
24710Sstevel@tonic-gate 		data_mp = ndp;
24720Sstevel@tonic-gate 		mp->b_cont->b_cont = data_mp;
24730Sstevel@tonic-gate 	}
24740Sstevel@tonic-gate 
24750Sstevel@tonic-gate 	cmd = iocp->ioc_cmd;
24760Sstevel@tonic-gate 	getcmd = (cmd == SIOCGIPMSFILTER || cmd == SIOCGMSFILTER);
24770Sstevel@tonic-gate 	is_v4only_api = (cmd == SIOCGIPMSFILTER || cmd == SIOCSIPMSFILTER);
24780Sstevel@tonic-gate 	minsize = (is_v4only_api) ? IP_MSFILTER_SIZE(0) : GROUP_FILTER_SIZE(0);
24790Sstevel@tonic-gate 	datalen = MBLKL(data_mp);
24800Sstevel@tonic-gate 
24810Sstevel@tonic-gate 	if (datalen < minsize)
24820Sstevel@tonic-gate 		return (EINVAL);
24830Sstevel@tonic-gate 
24840Sstevel@tonic-gate 	/*
24850Sstevel@tonic-gate 	 * now we know we have at least have the initial structure,
24860Sstevel@tonic-gate 	 * but need to check for the source list array.
24870Sstevel@tonic-gate 	 */
24880Sstevel@tonic-gate 	if (is_v4only_api) {
24890Sstevel@tonic-gate 		imsf = (struct ip_msfilter *)data_mp->b_rptr;
24900Sstevel@tonic-gate 		isv6 = B_FALSE;
24910Sstevel@tonic-gate 		expsize = IP_MSFILTER_SIZE(imsf->imsf_numsrc);
24920Sstevel@tonic-gate 	} else {
24930Sstevel@tonic-gate 		gf = (struct group_filter *)data_mp->b_rptr;
24940Sstevel@tonic-gate 		if (gf->gf_group.ss_family == AF_INET6) {
24950Sstevel@tonic-gate 			gsin6 = (struct sockaddr_in6 *)&gf->gf_group;
24960Sstevel@tonic-gate 			isv6 = !(IN6_IS_ADDR_V4MAPPED(&gsin6->sin6_addr));
24970Sstevel@tonic-gate 		} else {
24980Sstevel@tonic-gate 			isv6 = B_FALSE;
24990Sstevel@tonic-gate 		}
25000Sstevel@tonic-gate 		expsize = GROUP_FILTER_SIZE(gf->gf_numsrc);
25010Sstevel@tonic-gate 	}
25020Sstevel@tonic-gate 	if (datalen < expsize)
25030Sstevel@tonic-gate 		return (EINVAL);
25040Sstevel@tonic-gate 
25050Sstevel@tonic-gate 	connp = Q_TO_CONN(q);
25060Sstevel@tonic-gate 
25070Sstevel@tonic-gate 	/* operation not supported on the virtual network interface */
25080Sstevel@tonic-gate 	if (IS_VNI(ipif->ipif_ill))
25090Sstevel@tonic-gate 		return (EINVAL);
25100Sstevel@tonic-gate 
25110Sstevel@tonic-gate 	if (isv6) {
25120Sstevel@tonic-gate 		ill_t *ill = ipif->ipif_ill;
25130Sstevel@tonic-gate 		ill_refhold(ill);
25140Sstevel@tonic-gate 
25150Sstevel@tonic-gate 		gsin6 = (struct sockaddr_in6 *)&gf->gf_group;
25160Sstevel@tonic-gate 		v6grp = gsin6->sin6_addr;
25170Sstevel@tonic-gate 		if (getcmd)
25180Sstevel@tonic-gate 			err = ip_get_srcfilter_v6(connp, gf, &v6grp, ill);
25190Sstevel@tonic-gate 		else
25200Sstevel@tonic-gate 			err = ip_set_srcfilter_v6(connp, gf, &v6grp, ill);
25210Sstevel@tonic-gate 
25220Sstevel@tonic-gate 		ill_refrele(ill);
25230Sstevel@tonic-gate 	} else {
25240Sstevel@tonic-gate 		boolean_t isv4mapped = B_FALSE;
25250Sstevel@tonic-gate 		if (is_v4only_api) {
25260Sstevel@tonic-gate 			v4grp = (ipaddr_t)imsf->imsf_multiaddr.s_addr;
25270Sstevel@tonic-gate 		} else {
25280Sstevel@tonic-gate 			if (gf->gf_group.ss_family == AF_INET) {
25290Sstevel@tonic-gate 				gsin = (struct sockaddr_in *)&gf->gf_group;
25300Sstevel@tonic-gate 				v4grp = (ipaddr_t)gsin->sin_addr.s_addr;
25310Sstevel@tonic-gate 			} else {
25320Sstevel@tonic-gate 				gsin6 = (struct sockaddr_in6 *)&gf->gf_group;
25330Sstevel@tonic-gate 				IN6_V4MAPPED_TO_IPADDR(&gsin6->sin6_addr,
25340Sstevel@tonic-gate 				    v4grp);
25350Sstevel@tonic-gate 				isv4mapped = B_TRUE;
25360Sstevel@tonic-gate 			}
25370Sstevel@tonic-gate 		}
25380Sstevel@tonic-gate 		if (getcmd)
25390Sstevel@tonic-gate 			err = ip_get_srcfilter(connp, gf, imsf, v4grp, ipif,
25400Sstevel@tonic-gate 			    isv4mapped);
25410Sstevel@tonic-gate 		else
25420Sstevel@tonic-gate 			err = ip_set_srcfilter(connp, gf, imsf, v4grp, ipif,
25430Sstevel@tonic-gate 			    isv4mapped);
25440Sstevel@tonic-gate 	}
25450Sstevel@tonic-gate 
25460Sstevel@tonic-gate 	return (err);
25470Sstevel@tonic-gate }
25480Sstevel@tonic-gate 
25490Sstevel@tonic-gate /*
25500Sstevel@tonic-gate  * Finds the ipif based on information in the ioctl headers.  Needed to make
25510Sstevel@tonic-gate  * ip_process_ioctl() happy (it needs to know the ipif for IPI_WR-flagged
25520Sstevel@tonic-gate  * ioctls prior to calling the ioctl's handler function).  Somewhat analogous
25530Sstevel@tonic-gate  * to ip_extract_lifreq_cmn() and ip_extract_tunreq().
25540Sstevel@tonic-gate  */
25550Sstevel@tonic-gate int
25560Sstevel@tonic-gate ip_extract_msfilter(queue_t *q, mblk_t *mp, ipif_t **ipifpp, ipsq_func_t func)
25570Sstevel@tonic-gate {
25580Sstevel@tonic-gate 	struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
25590Sstevel@tonic-gate 	int cmd = iocp->ioc_cmd, err = 0;
25600Sstevel@tonic-gate 	conn_t *connp;
25610Sstevel@tonic-gate 	ipif_t *ipif;
25620Sstevel@tonic-gate 	/* caller has verified this mblk exists */
25630Sstevel@tonic-gate 	char *dbuf = (char *)mp->b_cont->b_cont->b_rptr;
25640Sstevel@tonic-gate 	struct ip_msfilter *imsf;
25650Sstevel@tonic-gate 	struct group_filter *gf;
25660Sstevel@tonic-gate 	ipaddr_t v4addr, v4grp;
25670Sstevel@tonic-gate 	in6_addr_t v6grp;
25680Sstevel@tonic-gate 	uint32_t index;
25690Sstevel@tonic-gate 	zoneid_t zoneid;
25700Sstevel@tonic-gate 
25710Sstevel@tonic-gate 	connp = Q_TO_CONN(q);
25720Sstevel@tonic-gate 	zoneid = connp->conn_zoneid;
25730Sstevel@tonic-gate 
25740Sstevel@tonic-gate 	/* don't allow multicast operations on a tcp conn */
2575741Smasputra 	if (IPCL_IS_TCP(connp))
25760Sstevel@tonic-gate 		return (ENOPROTOOPT);
25770Sstevel@tonic-gate 
25780Sstevel@tonic-gate 	if (cmd == SIOCSIPMSFILTER || cmd == SIOCGIPMSFILTER) {
25790Sstevel@tonic-gate 		/* don't allow v4-specific ioctls on v6 socket */
25800Sstevel@tonic-gate 		if (connp->conn_af_isv6)
25810Sstevel@tonic-gate 			return (EAFNOSUPPORT);
25820Sstevel@tonic-gate 
25830Sstevel@tonic-gate 		imsf = (struct ip_msfilter *)dbuf;
25840Sstevel@tonic-gate 		v4addr = imsf->imsf_interface.s_addr;
25850Sstevel@tonic-gate 		v4grp = imsf->imsf_multiaddr.s_addr;
25860Sstevel@tonic-gate 		if (v4addr == INADDR_ANY) {
25870Sstevel@tonic-gate 			ipif = ipif_lookup_group(v4grp, zoneid);
25880Sstevel@tonic-gate 			if (ipif == NULL)
25890Sstevel@tonic-gate 				err = EADDRNOTAVAIL;
25900Sstevel@tonic-gate 		} else {
25910Sstevel@tonic-gate 			ipif = ipif_lookup_addr(v4addr, NULL, zoneid, q, mp,
25920Sstevel@tonic-gate 			    func, &err);
25930Sstevel@tonic-gate 		}
25940Sstevel@tonic-gate 	} else {
25950Sstevel@tonic-gate 		boolean_t isv6 = B_FALSE;
25960Sstevel@tonic-gate 		gf = (struct group_filter *)dbuf;
25970Sstevel@tonic-gate 		index = gf->gf_interface;
25980Sstevel@tonic-gate 		if (gf->gf_group.ss_family == AF_INET6) {
25990Sstevel@tonic-gate 			struct sockaddr_in6 *sin6;
26000Sstevel@tonic-gate 			sin6 = (struct sockaddr_in6 *)&gf->gf_group;
26010Sstevel@tonic-gate 			v6grp = sin6->sin6_addr;
26020Sstevel@tonic-gate 			if (IN6_IS_ADDR_V4MAPPED(&v6grp))
26030Sstevel@tonic-gate 				IN6_V4MAPPED_TO_IPADDR(&v6grp, v4grp);
26040Sstevel@tonic-gate 			else
26050Sstevel@tonic-gate 				isv6 = B_TRUE;
26060Sstevel@tonic-gate 		} else if (gf->gf_group.ss_family == AF_INET) {
26070Sstevel@tonic-gate 			struct sockaddr_in *sin;
26080Sstevel@tonic-gate 			sin = (struct sockaddr_in *)&gf->gf_group;
26090Sstevel@tonic-gate 			v4grp = sin->sin_addr.s_addr;
26100Sstevel@tonic-gate 		} else {
26110Sstevel@tonic-gate 			return (EAFNOSUPPORT);
26120Sstevel@tonic-gate 		}
26130Sstevel@tonic-gate 		if (index == 0) {
26140Sstevel@tonic-gate 			if (isv6)
26150Sstevel@tonic-gate 				ipif = ipif_lookup_group_v6(&v6grp, zoneid);
26160Sstevel@tonic-gate 			else
26170Sstevel@tonic-gate 				ipif = ipif_lookup_group(v4grp, zoneid);
26180Sstevel@tonic-gate 			if (ipif == NULL)
26190Sstevel@tonic-gate 				err = EADDRNOTAVAIL;
26200Sstevel@tonic-gate 		} else {
26210Sstevel@tonic-gate 			ipif = ipif_lookup_on_ifindex(index, isv6, zoneid,
26220Sstevel@tonic-gate 			    q, mp, func, &err);
26230Sstevel@tonic-gate 		}
26240Sstevel@tonic-gate 	}
26250Sstevel@tonic-gate 
26260Sstevel@tonic-gate 	*ipifpp = ipif;
26270Sstevel@tonic-gate 	return (err);
26280Sstevel@tonic-gate }
26290Sstevel@tonic-gate 
26300Sstevel@tonic-gate /*
26310Sstevel@tonic-gate  * The structures used for the SIOC*MSFILTER ioctls usually must be copied
26320Sstevel@tonic-gate  * in in two stages, as the first copyin tells us the size of the attached
26330Sstevel@tonic-gate  * source buffer.  This function is called by ip_wput_nondata() after the
26340Sstevel@tonic-gate  * first copyin has completed; it figures out how big the second stage
26350Sstevel@tonic-gate  * needs to be, and kicks it off.
26360Sstevel@tonic-gate  *
26370Sstevel@tonic-gate  * In some cases (numsrc < 2), the second copyin is not needed as the
26380Sstevel@tonic-gate  * first one gets a complete structure containing 1 source addr.
26390Sstevel@tonic-gate  *
26400Sstevel@tonic-gate  * The function returns 0 if a second copyin has been started (i.e. there's
26410Sstevel@tonic-gate  * no more work to be done right now), or 1 if the second copyin is not
26420Sstevel@tonic-gate  * needed and ip_wput_nondata() can continue its processing.
26430Sstevel@tonic-gate  */
26440Sstevel@tonic-gate int
26450Sstevel@tonic-gate ip_copyin_msfilter(queue_t *q, mblk_t *mp)
26460Sstevel@tonic-gate {
26470Sstevel@tonic-gate 	struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
26480Sstevel@tonic-gate 	int cmd = iocp->ioc_cmd;
26490Sstevel@tonic-gate 	/* validity of this checked in ip_wput_nondata() */
26500Sstevel@tonic-gate 	mblk_t *mp1 = mp->b_cont->b_cont;
26510Sstevel@tonic-gate 	int copysize = 0;
26520Sstevel@tonic-gate 	int offset;
26530Sstevel@tonic-gate 
26540Sstevel@tonic-gate 	if (cmd == SIOCSMSFILTER || cmd == SIOCGMSFILTER) {
26550Sstevel@tonic-gate 		struct group_filter *gf = (struct group_filter *)mp1->b_rptr;
26560Sstevel@tonic-gate 		if (gf->gf_numsrc >= 2) {
26570Sstevel@tonic-gate 			offset = sizeof (struct group_filter);
26580Sstevel@tonic-gate 			copysize = GROUP_FILTER_SIZE(gf->gf_numsrc) - offset;
26590Sstevel@tonic-gate 		}
26600Sstevel@tonic-gate 	} else {
26610Sstevel@tonic-gate 		struct ip_msfilter *imsf = (struct ip_msfilter *)mp1->b_rptr;
26620Sstevel@tonic-gate 		if (imsf->imsf_numsrc >= 2) {
26630Sstevel@tonic-gate 			offset = sizeof (struct ip_msfilter);
26640Sstevel@tonic-gate 			copysize = IP_MSFILTER_SIZE(imsf->imsf_numsrc) - offset;
26650Sstevel@tonic-gate 		}
26660Sstevel@tonic-gate 	}
26670Sstevel@tonic-gate 	if (copysize > 0) {
26680Sstevel@tonic-gate 		mi_copyin_n(q, mp, offset, copysize);
26690Sstevel@tonic-gate 		return (0);
26700Sstevel@tonic-gate 	}
26710Sstevel@tonic-gate 	return (1);
26720Sstevel@tonic-gate }
26730Sstevel@tonic-gate 
26740Sstevel@tonic-gate /*
26750Sstevel@tonic-gate  * Handle the following optmgmt:
26760Sstevel@tonic-gate  *	IP_ADD_MEMBERSHIP		must not have joined already
26770Sstevel@tonic-gate  *	MCAST_JOIN_GROUP		must not have joined already
26780Sstevel@tonic-gate  *	IP_BLOCK_SOURCE			must have joined already
26790Sstevel@tonic-gate  *	MCAST_BLOCK_SOURCE		must have joined already
26800Sstevel@tonic-gate  *	IP_JOIN_SOURCE_GROUP		may have joined already
26810Sstevel@tonic-gate  *	MCAST_JOIN_SOURCE_GROUP		may have joined already
26820Sstevel@tonic-gate  *
26830Sstevel@tonic-gate  * fmode and src parameters may be used to determine which option is
26840Sstevel@tonic-gate  * being set, as follows (the IP_* and MCAST_* versions of each option
26850Sstevel@tonic-gate  * are functionally equivalent):
26860Sstevel@tonic-gate  *	opt			fmode			src
26870Sstevel@tonic-gate  *	IP_ADD_MEMBERSHIP	MODE_IS_EXCLUDE		INADDR_ANY
26880Sstevel@tonic-gate  *	MCAST_JOIN_GROUP	MODE_IS_EXCLUDE		INADDR_ANY
26890Sstevel@tonic-gate  *	IP_BLOCK_SOURCE		MODE_IS_EXCLUDE		v4 addr
26900Sstevel@tonic-gate  *	MCAST_BLOCK_SOURCE	MODE_IS_EXCLUDE		v4 addr
26910Sstevel@tonic-gate  *	IP_JOIN_SOURCE_GROUP	MODE_IS_INCLUDE		v4 addr
26920Sstevel@tonic-gate  *	MCAST_JOIN_SOURCE_GROUP	MODE_IS_INCLUDE		v4 addr
26930Sstevel@tonic-gate  *
26940Sstevel@tonic-gate  * Changing the filter mode is not allowed; if a matching ilg already
26950Sstevel@tonic-gate  * exists and fmode != ilg->ilg_fmode, EINVAL is returned.
26960Sstevel@tonic-gate  *
26970Sstevel@tonic-gate  * Verifies that there is a source address of appropriate scope for
26980Sstevel@tonic-gate  * the group; if not, EADDRNOTAVAIL is returned.
26990Sstevel@tonic-gate  *
27000Sstevel@tonic-gate  * The interface to be used may be identified by an address or by an
27010Sstevel@tonic-gate  * index.  A pointer to the index is passed; if it is NULL, use the
27020Sstevel@tonic-gate  * address, otherwise, use the index.
27030Sstevel@tonic-gate  */
27040Sstevel@tonic-gate int
27050Sstevel@tonic-gate ip_opt_add_group(conn_t *connp, boolean_t checkonly, ipaddr_t group,
27060Sstevel@tonic-gate     ipaddr_t ifaddr, uint_t *ifindexp, mcast_record_t fmode, ipaddr_t src,
27070Sstevel@tonic-gate     mblk_t *first_mp)
27080Sstevel@tonic-gate {
27090Sstevel@tonic-gate 	ipif_t	*ipif;
27100Sstevel@tonic-gate 	ipsq_t	*ipsq;
27110Sstevel@tonic-gate 	int err = 0;
27120Sstevel@tonic-gate 	ill_t	*ill;
27130Sstevel@tonic-gate 
27140Sstevel@tonic-gate 	err = ip_opt_check(connp, group, src, ifaddr, ifindexp, first_mp,
27150Sstevel@tonic-gate 	    ip_restart_optmgmt, &ipif);
27160Sstevel@tonic-gate 	if (err != 0) {
27170Sstevel@tonic-gate 		if (err != EINPROGRESS) {
27180Sstevel@tonic-gate 			ip1dbg(("ip_opt_add_group: no ipif for group 0x%x, "
27190Sstevel@tonic-gate 			    "ifaddr 0x%x, ifindex %d\n", ntohl(group),
27200Sstevel@tonic-gate 			    ntohl(ifaddr), (ifindexp == NULL) ? 0 : *ifindexp));
27210Sstevel@tonic-gate 		}
27220Sstevel@tonic-gate 		return (err);
27230Sstevel@tonic-gate 	}
27240Sstevel@tonic-gate 	ASSERT(ipif != NULL);
27250Sstevel@tonic-gate 
27260Sstevel@tonic-gate 	ill = ipif->ipif_ill;
27270Sstevel@tonic-gate 	/* Operation not supported on a virtual network interface */
27280Sstevel@tonic-gate 	if (IS_VNI(ill)) {
27290Sstevel@tonic-gate 		ipif_refrele(ipif);
27300Sstevel@tonic-gate 		return (EINVAL);
27310Sstevel@tonic-gate 	}
27320Sstevel@tonic-gate 
27330Sstevel@tonic-gate 	if (checkonly) {
27340Sstevel@tonic-gate 		/*
27350Sstevel@tonic-gate 		 * do not do operation, just pretend to - new T_CHECK
27360Sstevel@tonic-gate 		 * semantics. The error return case above if encountered
27370Sstevel@tonic-gate 		 * considered a good enough "check" here.
27380Sstevel@tonic-gate 		 */
27390Sstevel@tonic-gate 		ipif_refrele(ipif);
27400Sstevel@tonic-gate 		return (0);
27410Sstevel@tonic-gate 	}
27420Sstevel@tonic-gate 
27430Sstevel@tonic-gate 	IPSQ_ENTER_IPIF(ipif, connp, first_mp, ip_restart_optmgmt, ipsq,
27440Sstevel@tonic-gate 	    NEW_OP);
27450Sstevel@tonic-gate 
27460Sstevel@tonic-gate 	/* unspecified source addr => no source filtering */
27470Sstevel@tonic-gate 	err = ilg_add(connp, group, ipif, fmode, src);
27480Sstevel@tonic-gate 
27490Sstevel@tonic-gate 	IPSQ_EXIT(ipsq);
27500Sstevel@tonic-gate 
27510Sstevel@tonic-gate 	ipif_refrele(ipif);
27520Sstevel@tonic-gate 	return (err);
27530Sstevel@tonic-gate }
27540Sstevel@tonic-gate 
27550Sstevel@tonic-gate /*
27560Sstevel@tonic-gate  * Handle the following optmgmt:
27570Sstevel@tonic-gate  *	IPV6_JOIN_GROUP			must not have joined already
27580Sstevel@tonic-gate  *	MCAST_JOIN_GROUP		must not have joined already
27590Sstevel@tonic-gate  *	MCAST_BLOCK_SOURCE		must have joined already
27600Sstevel@tonic-gate  *	MCAST_JOIN_SOURCE_GROUP		may have joined already
27610Sstevel@tonic-gate  *
27620Sstevel@tonic-gate  * fmode and src parameters may be used to determine which option is
27630Sstevel@tonic-gate  * being set, as follows (IPV6_JOIN_GROUP and MCAST_JOIN_GROUP options
27640Sstevel@tonic-gate  * are functionally equivalent):
27650Sstevel@tonic-gate  *	opt			fmode			v6src
27660Sstevel@tonic-gate  *	IPV6_JOIN_GROUP		MODE_IS_EXCLUDE		unspecified
27670Sstevel@tonic-gate  *	MCAST_JOIN_GROUP	MODE_IS_EXCLUDE		unspecified
27680Sstevel@tonic-gate  *	MCAST_BLOCK_SOURCE	MODE_IS_EXCLUDE		v6 addr
27690Sstevel@tonic-gate  *	MCAST_JOIN_SOURCE_GROUP	MODE_IS_INCLUDE		v6 addr
27700Sstevel@tonic-gate  *
27710Sstevel@tonic-gate  * Changing the filter mode is not allowed; if a matching ilg already
27720Sstevel@tonic-gate  * exists and fmode != ilg->ilg_fmode, EINVAL is returned.
27730Sstevel@tonic-gate  *
27740Sstevel@tonic-gate  * Verifies that there is a source address of appropriate scope for
27750Sstevel@tonic-gate  * the group; if not, EADDRNOTAVAIL is returned.
27760Sstevel@tonic-gate  *
27770Sstevel@tonic-gate  * Handles IPv4-mapped IPv6 multicast addresses by associating them
27780Sstevel@tonic-gate  * with the link-local ipif.  Assumes that if v6group is v4-mapped,
27790Sstevel@tonic-gate  * v6src is also v4-mapped.
27800Sstevel@tonic-gate  */
27810Sstevel@tonic-gate int
27820Sstevel@tonic-gate ip_opt_add_group_v6(conn_t *connp, boolean_t checkonly,
27830Sstevel@tonic-gate     const in6_addr_t *v6group, int ifindex, mcast_record_t fmode,
27840Sstevel@tonic-gate     const in6_addr_t *v6src, mblk_t *first_mp)
27850Sstevel@tonic-gate {
27860Sstevel@tonic-gate 	ill_t *ill;
27870Sstevel@tonic-gate 	ipif_t	*ipif;
27880Sstevel@tonic-gate 	char buf[INET6_ADDRSTRLEN];
27890Sstevel@tonic-gate 	ipaddr_t v4group, v4src;
27900Sstevel@tonic-gate 	boolean_t isv6;
27910Sstevel@tonic-gate 	ipsq_t	*ipsq;
27920Sstevel@tonic-gate 	int	err;
27930Sstevel@tonic-gate 
27940Sstevel@tonic-gate 	err = ip_opt_check_v6(connp, v6group, &v4group, v6src, &v4src, &isv6,
27950Sstevel@tonic-gate 	    ifindex, first_mp, ip_restart_optmgmt, &ill, &ipif);
27960Sstevel@tonic-gate 	if (err != 0) {
27970Sstevel@tonic-gate 		if (err != EINPROGRESS) {
27980Sstevel@tonic-gate 			ip1dbg(("ip_opt_add_group_v6: no ill for group %s/"
27990Sstevel@tonic-gate 			    "index %d\n", inet_ntop(AF_INET6, v6group, buf,
28000Sstevel@tonic-gate 			    sizeof (buf)), ifindex));
28010Sstevel@tonic-gate 		}
28020Sstevel@tonic-gate 		return (err);
28030Sstevel@tonic-gate 	}
28040Sstevel@tonic-gate 	ASSERT((!isv6 && ipif != NULL) || (isv6 && ill != NULL));
28050Sstevel@tonic-gate 
28060Sstevel@tonic-gate 	/* operation is not supported on the virtual network interface */
28070Sstevel@tonic-gate 	if (isv6) {
28080Sstevel@tonic-gate 		if (IS_VNI(ill)) {
28090Sstevel@tonic-gate 			ill_refrele(ill);
28100Sstevel@tonic-gate 			return (EINVAL);
28110Sstevel@tonic-gate 		}
28120Sstevel@tonic-gate 	} else {
28130Sstevel@tonic-gate 		if (IS_VNI(ipif->ipif_ill)) {
28140Sstevel@tonic-gate 			ipif_refrele(ipif);
28150Sstevel@tonic-gate 			return (EINVAL);
28160Sstevel@tonic-gate 		}
28170Sstevel@tonic-gate 	}
28180Sstevel@tonic-gate 
28190Sstevel@tonic-gate 	if (checkonly) {
28200Sstevel@tonic-gate 		/*
28210Sstevel@tonic-gate 		 * do not do operation, just pretend to - new T_CHECK
28220Sstevel@tonic-gate 		 * semantics. The error return case above if encountered
28230Sstevel@tonic-gate 		 * considered a good enough "check" here.
28240Sstevel@tonic-gate 		 */
28250Sstevel@tonic-gate 		if (isv6)
28260Sstevel@tonic-gate 			ill_refrele(ill);
28270Sstevel@tonic-gate 		else
28280Sstevel@tonic-gate 			ipif_refrele(ipif);
28290Sstevel@tonic-gate 		return (0);
28300Sstevel@tonic-gate 	}
28310Sstevel@tonic-gate 
28320Sstevel@tonic-gate 	if (!isv6) {
28330Sstevel@tonic-gate 		IPSQ_ENTER_IPIF(ipif, connp, first_mp, ip_restart_optmgmt,
28340Sstevel@tonic-gate 		    ipsq, NEW_OP);
28350Sstevel@tonic-gate 		err = ilg_add(connp, v4group, ipif, fmode, v4src);
28360Sstevel@tonic-gate 		IPSQ_EXIT(ipsq);
28370Sstevel@tonic-gate 		ipif_refrele(ipif);
28380Sstevel@tonic-gate 	} else {
28390Sstevel@tonic-gate 		IPSQ_ENTER_ILL(ill, connp, first_mp, ip_restart_optmgmt,
28400Sstevel@tonic-gate 		    ipsq, NEW_OP);
28410Sstevel@tonic-gate 		err = ilg_add_v6(connp, v6group, ill, fmode, v6src);
28420Sstevel@tonic-gate 		IPSQ_EXIT(ipsq);
28430Sstevel@tonic-gate 		ill_refrele(ill);
28440Sstevel@tonic-gate 	}
28450Sstevel@tonic-gate 
28460Sstevel@tonic-gate 	return (err);
28470Sstevel@tonic-gate }
28480Sstevel@tonic-gate 
28490Sstevel@tonic-gate static int
28500Sstevel@tonic-gate ip_opt_delete_group_excl(conn_t *connp, ipaddr_t group, ipif_t *ipif,
28510Sstevel@tonic-gate     mcast_record_t fmode, ipaddr_t src)
28520Sstevel@tonic-gate {
28530Sstevel@tonic-gate 	ilg_t	*ilg;
28540Sstevel@tonic-gate 	in6_addr_t v6src;
28550Sstevel@tonic-gate 	boolean_t leaving = B_FALSE;
28560Sstevel@tonic-gate 
28570Sstevel@tonic-gate 	ASSERT(IAM_WRITER_IPIF(ipif));
28580Sstevel@tonic-gate 
28590Sstevel@tonic-gate 	/*
28600Sstevel@tonic-gate 	 * The ilg is valid only while we hold the conn lock. Once we drop
28610Sstevel@tonic-gate 	 * the lock, another thread can locate another ilg on this connp,
28620Sstevel@tonic-gate 	 * but on a different ipif, and delete it, and cause the ilg array
28630Sstevel@tonic-gate 	 * to be reallocated and copied. Hence do the ilg_delete before
28640Sstevel@tonic-gate 	 * dropping the lock.
28650Sstevel@tonic-gate 	 */
28660Sstevel@tonic-gate 	mutex_enter(&connp->conn_lock);
28670Sstevel@tonic-gate 	ilg = ilg_lookup_ipif(connp, group, ipif);
28680Sstevel@tonic-gate 	if ((ilg == NULL) || (ilg->ilg_flags & ILG_DELETED)) {
28690Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
28700Sstevel@tonic-gate 		return (EADDRNOTAVAIL);
28710Sstevel@tonic-gate 	}
28720Sstevel@tonic-gate 
28730Sstevel@tonic-gate 	/*
28740Sstevel@tonic-gate 	 * Decide if we're actually deleting the ilg or just removing a
28750Sstevel@tonic-gate 	 * source filter address; if just removing an addr, make sure we
28760Sstevel@tonic-gate 	 * aren't trying to change the filter mode, and that the addr is
28770Sstevel@tonic-gate 	 * actually in our filter list already.  If we're removing the
28780Sstevel@tonic-gate 	 * last src in an include list, just delete the ilg.
28790Sstevel@tonic-gate 	 */
28800Sstevel@tonic-gate 	if (src == INADDR_ANY) {
28810Sstevel@tonic-gate 		v6src = ipv6_all_zeros;
28820Sstevel@tonic-gate 		leaving = B_TRUE;
28830Sstevel@tonic-gate 	} else {
28840Sstevel@tonic-gate 		int err = 0;
28850Sstevel@tonic-gate 		IN6_IPADDR_TO_V4MAPPED(src, &v6src);
28860Sstevel@tonic-gate 		if (fmode != ilg->ilg_fmode)
28870Sstevel@tonic-gate 			err = EINVAL;
28880Sstevel@tonic-gate 		else if (ilg->ilg_filter == NULL ||
28890Sstevel@tonic-gate 		    !list_has_addr(ilg->ilg_filter, &v6src))
28900Sstevel@tonic-gate 			err = EADDRNOTAVAIL;
28910Sstevel@tonic-gate 		if (err != 0) {
28920Sstevel@tonic-gate 			mutex_exit(&connp->conn_lock);
28930Sstevel@tonic-gate 			return (err);
28940Sstevel@tonic-gate 		}
28950Sstevel@tonic-gate 		if (fmode == MODE_IS_INCLUDE &&
28960Sstevel@tonic-gate 		    ilg->ilg_filter->sl_numsrc == 1) {
28970Sstevel@tonic-gate 			v6src = ipv6_all_zeros;
28980Sstevel@tonic-gate 			leaving = B_TRUE;
28990Sstevel@tonic-gate 		}
29000Sstevel@tonic-gate 	}
29010Sstevel@tonic-gate 
29020Sstevel@tonic-gate 	ilg_delete(connp, ilg, &v6src);
29030Sstevel@tonic-gate 	mutex_exit(&connp->conn_lock);
29040Sstevel@tonic-gate 
29050Sstevel@tonic-gate 	(void) ip_delmulti(group, ipif, B_FALSE, leaving);
29060Sstevel@tonic-gate 	return (0);
29070Sstevel@tonic-gate }
29080Sstevel@tonic-gate 
29090Sstevel@tonic-gate static int
29100Sstevel@tonic-gate ip_opt_delete_group_excl_v6(conn_t *connp, const in6_addr_t *v6group,
29110Sstevel@tonic-gate     ill_t *ill, mcast_record_t fmode, const in6_addr_t *v6src)
29120Sstevel@tonic-gate {
29130Sstevel@tonic-gate 	ilg_t	*ilg;
29140Sstevel@tonic-gate 	ill_t	*ilg_ill;
29150Sstevel@tonic-gate 	uint_t	ilg_orig_ifindex;
29160Sstevel@tonic-gate 	boolean_t leaving = B_TRUE;
29170Sstevel@tonic-gate 
29180Sstevel@tonic-gate 	ASSERT(IAM_WRITER_ILL(ill));
29190Sstevel@tonic-gate 
29200Sstevel@tonic-gate 	/*
29210Sstevel@tonic-gate 	 * Use the index that we originally used to join. We can't
29220Sstevel@tonic-gate 	 * use the ill directly because ilg_ill could point to
29230Sstevel@tonic-gate 	 * a new ill if things have moved.
29240Sstevel@tonic-gate 	 */
29250Sstevel@tonic-gate 	mutex_enter(&connp->conn_lock);
29260Sstevel@tonic-gate 	ilg = ilg_lookup_ill_index_v6(connp, v6group,
29270Sstevel@tonic-gate 	    ill->ill_phyint->phyint_ifindex);
29280Sstevel@tonic-gate 	if ((ilg == NULL) || (ilg->ilg_flags & ILG_DELETED)) {
29290Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
29300Sstevel@tonic-gate 		return (EADDRNOTAVAIL);
29310Sstevel@tonic-gate 	}
29320Sstevel@tonic-gate 
29330Sstevel@tonic-gate 	/*
29340Sstevel@tonic-gate 	 * Decide if we're actually deleting the ilg or just removing a
29350Sstevel@tonic-gate 	 * source filter address; if just removing an addr, make sure we
29360Sstevel@tonic-gate 	 * aren't trying to change the filter mode, and that the addr is
29370Sstevel@tonic-gate 	 * actually in our filter list already.  If we're removing the
29380Sstevel@tonic-gate 	 * last src in an include list, just delete the ilg.
29390Sstevel@tonic-gate 	 */
29400Sstevel@tonic-gate 	if (!IN6_IS_ADDR_UNSPECIFIED(v6src)) {
29410Sstevel@tonic-gate 		int err = 0;
29420Sstevel@tonic-gate 		if (fmode != ilg->ilg_fmode)
29430Sstevel@tonic-gate 			err = EINVAL;
29440Sstevel@tonic-gate 		else if (ilg->ilg_filter == NULL ||
29450Sstevel@tonic-gate 		    !list_has_addr(ilg->ilg_filter, v6src))
29460Sstevel@tonic-gate 			err = EADDRNOTAVAIL;
29470Sstevel@tonic-gate 		if (err != 0) {
29480Sstevel@tonic-gate 			mutex_exit(&connp->conn_lock);
29490Sstevel@tonic-gate 			return (err);
29500Sstevel@tonic-gate 		}
29510Sstevel@tonic-gate 		if (fmode == MODE_IS_INCLUDE &&
29520Sstevel@tonic-gate 		    ilg->ilg_filter->sl_numsrc == 1)
29530Sstevel@tonic-gate 			v6src = NULL;
29540Sstevel@tonic-gate 		else
29550Sstevel@tonic-gate 			leaving = B_FALSE;
29560Sstevel@tonic-gate 	}
29570Sstevel@tonic-gate 
29580Sstevel@tonic-gate 	ilg_ill = ilg->ilg_ill;
29590Sstevel@tonic-gate 	ilg_orig_ifindex = ilg->ilg_orig_ifindex;
29600Sstevel@tonic-gate 	ilg_delete(connp, ilg, v6src);
29610Sstevel@tonic-gate 	mutex_exit(&connp->conn_lock);
29620Sstevel@tonic-gate 	(void) ip_delmulti_v6(v6group, ilg_ill, ilg_orig_ifindex,
29630Sstevel@tonic-gate 	    connp->conn_zoneid, B_FALSE, leaving);
29640Sstevel@tonic-gate 
29650Sstevel@tonic-gate 	return (0);
29660Sstevel@tonic-gate }
29670Sstevel@tonic-gate 
29680Sstevel@tonic-gate /*
29690Sstevel@tonic-gate  * Handle the following optmgmt:
29700Sstevel@tonic-gate  *	IP_DROP_MEMBERSHIP		will leave
29710Sstevel@tonic-gate  *	MCAST_LEAVE_GROUP		will leave
29720Sstevel@tonic-gate  *	IP_UNBLOCK_SOURCE		will not leave
29730Sstevel@tonic-gate  *	MCAST_UNBLOCK_SOURCE		will not leave
29740Sstevel@tonic-gate  *	IP_LEAVE_SOURCE_GROUP		may leave (if leaving last source)
29750Sstevel@tonic-gate  *	MCAST_LEAVE_SOURCE_GROUP	may leave (if leaving last source)
29760Sstevel@tonic-gate  *
29770Sstevel@tonic-gate  * fmode and src parameters may be used to determine which option is
29780Sstevel@tonic-gate  * being set, as follows (the IP_* and MCAST_* versions of each option
29790Sstevel@tonic-gate  * are functionally equivalent):
29800Sstevel@tonic-gate  *	opt			 fmode			src
29810Sstevel@tonic-gate  *	IP_DROP_MEMBERSHIP	 MODE_IS_INCLUDE	INADDR_ANY
29820Sstevel@tonic-gate  *	MCAST_LEAVE_GROUP	 MODE_IS_INCLUDE	INADDR_ANY
29830Sstevel@tonic-gate  *	IP_UNBLOCK_SOURCE	 MODE_IS_EXCLUDE	v4 addr
29840Sstevel@tonic-gate  *	MCAST_UNBLOCK_SOURCE	 MODE_IS_EXCLUDE	v4 addr
29850Sstevel@tonic-gate  *	IP_LEAVE_SOURCE_GROUP	 MODE_IS_INCLUDE	v4 addr
29860Sstevel@tonic-gate  *	MCAST_LEAVE_SOURCE_GROUP MODE_IS_INCLUDE	v4 addr
29870Sstevel@tonic-gate  *
29880Sstevel@tonic-gate  * Changing the filter mode is not allowed; if a matching ilg already
29890Sstevel@tonic-gate  * exists and fmode != ilg->ilg_fmode, EINVAL is returned.
29900Sstevel@tonic-gate  *
29910Sstevel@tonic-gate  * The interface to be used may be identified by an address or by an
29920Sstevel@tonic-gate  * index.  A pointer to the index is passed; if it is NULL, use the
29930Sstevel@tonic-gate  * address, otherwise, use the index.
29940Sstevel@tonic-gate  */
29950Sstevel@tonic-gate int
29960Sstevel@tonic-gate ip_opt_delete_group(conn_t *connp, boolean_t checkonly, ipaddr_t group,
29970Sstevel@tonic-gate     ipaddr_t ifaddr, uint_t *ifindexp, mcast_record_t fmode, ipaddr_t src,
29980Sstevel@tonic-gate     mblk_t *first_mp)
29990Sstevel@tonic-gate {
30000Sstevel@tonic-gate 	ipif_t	*ipif;
30010Sstevel@tonic-gate 	ipsq_t	*ipsq;
30020Sstevel@tonic-gate 	int	err;
30030Sstevel@tonic-gate 	ill_t	*ill;
30040Sstevel@tonic-gate 
30050Sstevel@tonic-gate 	err = ip_opt_check(connp, group, src, ifaddr, ifindexp, first_mp,
30060Sstevel@tonic-gate 	    ip_restart_optmgmt, &ipif);
30070Sstevel@tonic-gate 	if (err != 0) {
30080Sstevel@tonic-gate 		if (err != EINPROGRESS) {
30090Sstevel@tonic-gate 			ip1dbg(("ip_opt_delete_group: no ipif for group "
30100Sstevel@tonic-gate 			    "0x%x, ifaddr 0x%x\n",
30110Sstevel@tonic-gate 			    (int)ntohl(group), (int)ntohl(ifaddr)));
30120Sstevel@tonic-gate 		}
30130Sstevel@tonic-gate 		return (err);
30140Sstevel@tonic-gate 	}
30150Sstevel@tonic-gate 	ASSERT(ipif != NULL);
30160Sstevel@tonic-gate 
30170Sstevel@tonic-gate 	ill = ipif->ipif_ill;
30180Sstevel@tonic-gate 	/* Operation not supported on a virtual network interface */
30190Sstevel@tonic-gate 	if (IS_VNI(ill)) {
30200Sstevel@tonic-gate 		ipif_refrele(ipif);
30210Sstevel@tonic-gate 		return (EINVAL);
30220Sstevel@tonic-gate 	}
30230Sstevel@tonic-gate 
30240Sstevel@tonic-gate 	if (checkonly) {
30250Sstevel@tonic-gate 		/*
30260Sstevel@tonic-gate 		 * do not do operation, just pretend to - new T_CHECK
30270Sstevel@tonic-gate 		 * semantics. The error return case above if encountered
30280Sstevel@tonic-gate 		 * considered a good enough "check" here.
30290Sstevel@tonic-gate 		 */
30300Sstevel@tonic-gate 		ipif_refrele(ipif);
30310Sstevel@tonic-gate 		return (0);
30320Sstevel@tonic-gate 	}
30330Sstevel@tonic-gate 
30340Sstevel@tonic-gate 	IPSQ_ENTER_IPIF(ipif, connp, first_mp, ip_restart_optmgmt, ipsq,
30350Sstevel@tonic-gate 	    NEW_OP);
30360Sstevel@tonic-gate 	err = ip_opt_delete_group_excl(connp, group, ipif, fmode, src);
30370Sstevel@tonic-gate 	IPSQ_EXIT(ipsq);
30380Sstevel@tonic-gate 
30390Sstevel@tonic-gate 	ipif_refrele(ipif);
30400Sstevel@tonic-gate 	return (err);
30410Sstevel@tonic-gate }
30420Sstevel@tonic-gate 
30430Sstevel@tonic-gate /*
30440Sstevel@tonic-gate  * Handle the following optmgmt:
30450Sstevel@tonic-gate  *	IPV6_LEAVE_GROUP		will leave
30460Sstevel@tonic-gate  *	MCAST_LEAVE_GROUP		will leave
30470Sstevel@tonic-gate  *	MCAST_UNBLOCK_SOURCE		will not leave
30480Sstevel@tonic-gate  *	MCAST_LEAVE_SOURCE_GROUP	may leave (if leaving last source)
30490Sstevel@tonic-gate  *
30500Sstevel@tonic-gate  * fmode and src parameters may be used to determine which option is
30510Sstevel@tonic-gate  * being set, as follows (IPV6_LEAVE_GROUP and MCAST_LEAVE_GROUP options
30520Sstevel@tonic-gate  * are functionally equivalent):
30530Sstevel@tonic-gate  *	opt			 fmode			v6src
30540Sstevel@tonic-gate  *	IPV6_LEAVE_GROUP	 MODE_IS_INCLUDE	unspecified
30550Sstevel@tonic-gate  *	MCAST_LEAVE_GROUP	 MODE_IS_INCLUDE	unspecified
30560Sstevel@tonic-gate  *	MCAST_UNBLOCK_SOURCE	 MODE_IS_EXCLUDE	v6 addr
30570Sstevel@tonic-gate  *	MCAST_LEAVE_SOURCE_GROUP MODE_IS_INCLUDE	v6 addr
30580Sstevel@tonic-gate  *
30590Sstevel@tonic-gate  * Changing the filter mode is not allowed; if a matching ilg already
30600Sstevel@tonic-gate  * exists and fmode != ilg->ilg_fmode, EINVAL is returned.
30610Sstevel@tonic-gate  *
30620Sstevel@tonic-gate  * Handles IPv4-mapped IPv6 multicast addresses by associating them
30630Sstevel@tonic-gate  * with the link-local ipif.  Assumes that if v6group is v4-mapped,
30640Sstevel@tonic-gate  * v6src is also v4-mapped.
30650Sstevel@tonic-gate  */
30660Sstevel@tonic-gate int
30670Sstevel@tonic-gate ip_opt_delete_group_v6(conn_t *connp, boolean_t checkonly,
30680Sstevel@tonic-gate     const in6_addr_t *v6group, int ifindex, mcast_record_t fmode,
30690Sstevel@tonic-gate     const in6_addr_t *v6src, mblk_t *first_mp)
30700Sstevel@tonic-gate {
30710Sstevel@tonic-gate 	ill_t *ill;
30720Sstevel@tonic-gate 	ipif_t	*ipif;
30730Sstevel@tonic-gate 	char	buf[INET6_ADDRSTRLEN];
30740Sstevel@tonic-gate 	ipaddr_t v4group, v4src;
30750Sstevel@tonic-gate 	boolean_t isv6;
30760Sstevel@tonic-gate 	ipsq_t	*ipsq;
30770Sstevel@tonic-gate 	int	err;
30780Sstevel@tonic-gate 
30790Sstevel@tonic-gate 	err = ip_opt_check_v6(connp, v6group, &v4group, v6src, &v4src, &isv6,
30800Sstevel@tonic-gate 	    ifindex, first_mp, ip_restart_optmgmt, &ill, &ipif);
30810Sstevel@tonic-gate 	if (err != 0) {
30820Sstevel@tonic-gate 		if (err != EINPROGRESS) {
30830Sstevel@tonic-gate 			ip1dbg(("ip_opt_delete_group_v6: no ill for group %s/"
30840Sstevel@tonic-gate 			    "index %d\n", inet_ntop(AF_INET6, v6group, buf,
30850Sstevel@tonic-gate 			    sizeof (buf)), ifindex));
30860Sstevel@tonic-gate 		}
30870Sstevel@tonic-gate 		return (err);
30880Sstevel@tonic-gate 	}
30890Sstevel@tonic-gate 	ASSERT((isv6 && ill != NULL) || (!isv6 && ipif != NULL));
30900Sstevel@tonic-gate 
30910Sstevel@tonic-gate 	/* operation is not supported on the virtual network interface */
30920Sstevel@tonic-gate 	if (isv6) {
30930Sstevel@tonic-gate 		if (IS_VNI(ill)) {
30940Sstevel@tonic-gate 			ill_refrele(ill);
30950Sstevel@tonic-gate 			return (EINVAL);
30960Sstevel@tonic-gate 		}
30970Sstevel@tonic-gate 	} else {
30980Sstevel@tonic-gate 		if (IS_VNI(ipif->ipif_ill)) {
30990Sstevel@tonic-gate 			ipif_refrele(ipif);
31000Sstevel@tonic-gate 			return (EINVAL);
31010Sstevel@tonic-gate 		}
31020Sstevel@tonic-gate 	}
31030Sstevel@tonic-gate 
31040Sstevel@tonic-gate 	if (checkonly) {
31050Sstevel@tonic-gate 		/*
31060Sstevel@tonic-gate 		 * do not do operation, just pretend to - new T_CHECK
31070Sstevel@tonic-gate 		 * semantics. The error return case above if encountered
31080Sstevel@tonic-gate 		 * considered a good enough "check" here.
31090Sstevel@tonic-gate 		 */
31100Sstevel@tonic-gate 		if (isv6)
31110Sstevel@tonic-gate 			ill_refrele(ill);
31120Sstevel@tonic-gate 		else
31130Sstevel@tonic-gate 			ipif_refrele(ipif);
31140Sstevel@tonic-gate 		return (0);
31150Sstevel@tonic-gate 	}
31160Sstevel@tonic-gate 
31170Sstevel@tonic-gate 	if (!isv6) {
31180Sstevel@tonic-gate 		IPSQ_ENTER_IPIF(ipif, connp, first_mp, ip_restart_optmgmt,
31190Sstevel@tonic-gate 		    ipsq, NEW_OP);
31200Sstevel@tonic-gate 		err = ip_opt_delete_group_excl(connp, v4group, ipif, fmode,
31210Sstevel@tonic-gate 		    v4src);
31220Sstevel@tonic-gate 		IPSQ_EXIT(ipsq);
31230Sstevel@tonic-gate 		ipif_refrele(ipif);
31240Sstevel@tonic-gate 	} else {
31250Sstevel@tonic-gate 		IPSQ_ENTER_ILL(ill, connp, first_mp, ip_restart_optmgmt,
31260Sstevel@tonic-gate 		    ipsq, NEW_OP);
31270Sstevel@tonic-gate 		err = ip_opt_delete_group_excl_v6(connp, v6group, ill, fmode,
31280Sstevel@tonic-gate 		    v6src);
31290Sstevel@tonic-gate 		IPSQ_EXIT(ipsq);
31300Sstevel@tonic-gate 		ill_refrele(ill);
31310Sstevel@tonic-gate 	}
31320Sstevel@tonic-gate 
31330Sstevel@tonic-gate 	return (err);
31340Sstevel@tonic-gate }
31350Sstevel@tonic-gate 
31360Sstevel@tonic-gate /*
31370Sstevel@tonic-gate  * Group mgmt for upper conn that passes things down
31380Sstevel@tonic-gate  * to the interface multicast list (and DLPI)
31390Sstevel@tonic-gate  * These routines can handle new style options that specify an interface name
31400Sstevel@tonic-gate  * as opposed to an interface address (needed for general handling of
31410Sstevel@tonic-gate  * unnumbered interfaces.)
31420Sstevel@tonic-gate  */
31430Sstevel@tonic-gate 
31440Sstevel@tonic-gate /*
31450Sstevel@tonic-gate  * Add a group to an upper conn group data structure and pass things down
31460Sstevel@tonic-gate  * to the interface multicast list (and DLPI)
31470Sstevel@tonic-gate  */
31480Sstevel@tonic-gate static int
31490Sstevel@tonic-gate ilg_add(conn_t *connp, ipaddr_t group, ipif_t *ipif, mcast_record_t fmode,
31500Sstevel@tonic-gate     ipaddr_t src)
31510Sstevel@tonic-gate {
31520Sstevel@tonic-gate 	int	error = 0;
31530Sstevel@tonic-gate 	ill_t	*ill;
31540Sstevel@tonic-gate 	ilg_t	*ilg;
31550Sstevel@tonic-gate 	ilg_stat_t ilgstat;
31560Sstevel@tonic-gate 	slist_t	*new_filter = NULL;
31570Sstevel@tonic-gate 	int	new_fmode;
31580Sstevel@tonic-gate 
31590Sstevel@tonic-gate 	ASSERT(IAM_WRITER_IPIF(ipif));
31600Sstevel@tonic-gate 
31610Sstevel@tonic-gate 	ill = ipif->ipif_ill;
31620Sstevel@tonic-gate 
31630Sstevel@tonic-gate 	if (!(ill->ill_flags & ILLF_MULTICAST))
31640Sstevel@tonic-gate 		return (EADDRNOTAVAIL);
31650Sstevel@tonic-gate 
31660Sstevel@tonic-gate 	/*
31670Sstevel@tonic-gate 	 * conn_ilg[] is protected by conn_lock. Need to hold the conn_lock
31680Sstevel@tonic-gate 	 * to walk the conn_ilg[] list in ilg_lookup_ipif(); also needed to
31690Sstevel@tonic-gate 	 * serialize 2 threads doing join (sock, group1, hme0:0) and
31700Sstevel@tonic-gate 	 * (sock, group2, hme1:0) where hme0 and hme1 map to different ipsqs,
31710Sstevel@tonic-gate 	 * but both operations happen on the same conn.
31720Sstevel@tonic-gate 	 */
31730Sstevel@tonic-gate 	mutex_enter(&connp->conn_lock);
31740Sstevel@tonic-gate 	ilg = ilg_lookup_ipif(connp, group, ipif);
31750Sstevel@tonic-gate 
31760Sstevel@tonic-gate 	/*
31770Sstevel@tonic-gate 	 * Depending on the option we're handling, may or may not be okay
31780Sstevel@tonic-gate 	 * if group has already been added.  Figure out our rules based
31790Sstevel@tonic-gate 	 * on fmode and src params.  Also make sure there's enough room
31800Sstevel@tonic-gate 	 * in the filter if we're adding a source to an existing filter.
31810Sstevel@tonic-gate 	 */
31820Sstevel@tonic-gate 	if (src == INADDR_ANY) {
31830Sstevel@tonic-gate 		/* we're joining for all sources, must not have joined */
31840Sstevel@tonic-gate 		if (ilg != NULL)
31850Sstevel@tonic-gate 			error = EADDRINUSE;
31860Sstevel@tonic-gate 	} else {
31870Sstevel@tonic-gate 		if (fmode == MODE_IS_EXCLUDE) {
31880Sstevel@tonic-gate 			/* (excl {addr}) => block source, must have joined */
31890Sstevel@tonic-gate 			if (ilg == NULL)
31900Sstevel@tonic-gate 				error = EADDRNOTAVAIL;
31910Sstevel@tonic-gate 		}
31920Sstevel@tonic-gate 		/* (incl {addr}) => join source, may have joined */
31930Sstevel@tonic-gate 
31940Sstevel@tonic-gate 		if (ilg != NULL &&
31950Sstevel@tonic-gate 		    SLIST_CNT(ilg->ilg_filter) == MAX_FILTER_SIZE)
31960Sstevel@tonic-gate 			error = ENOBUFS;
31970Sstevel@tonic-gate 	}
31980Sstevel@tonic-gate 	if (error != 0) {
31990Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
32000Sstevel@tonic-gate 		return (error);
32010Sstevel@tonic-gate 	}
32020Sstevel@tonic-gate 
32030Sstevel@tonic-gate 	ASSERT(!(ipif->ipif_state_flags & IPIF_CONDEMNED));
32040Sstevel@tonic-gate 
32050Sstevel@tonic-gate 	/*
32060Sstevel@tonic-gate 	 * Alloc buffer to copy new state into (see below) before
32070Sstevel@tonic-gate 	 * we make any changes, so we can bail if it fails.
32080Sstevel@tonic-gate 	 */
32090Sstevel@tonic-gate 	if ((new_filter = l_alloc()) == NULL) {
32100Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
32110Sstevel@tonic-gate 		return (ENOMEM);
32120Sstevel@tonic-gate 	}
32130Sstevel@tonic-gate 
32140Sstevel@tonic-gate 	if (ilg == NULL) {
32150Sstevel@tonic-gate 		ilgstat = ILGSTAT_NEW;
32160Sstevel@tonic-gate 		if ((ilg = conn_ilg_alloc(connp)) == NULL) {
32170Sstevel@tonic-gate 			mutex_exit(&connp->conn_lock);
32180Sstevel@tonic-gate 			l_free(new_filter);
32190Sstevel@tonic-gate 			return (ENOMEM);
32200Sstevel@tonic-gate 		}
32210Sstevel@tonic-gate 		if (src != INADDR_ANY) {
32220Sstevel@tonic-gate 			ilg->ilg_filter = l_alloc();
32230Sstevel@tonic-gate 			if (ilg->ilg_filter == NULL) {
32240Sstevel@tonic-gate 				ilg_delete(connp, ilg, NULL);
32250Sstevel@tonic-gate 				mutex_exit(&connp->conn_lock);
32260Sstevel@tonic-gate 				l_free(new_filter);
32270Sstevel@tonic-gate 				return (ENOMEM);
32280Sstevel@tonic-gate 			}
32290Sstevel@tonic-gate 			ilg->ilg_filter->sl_numsrc = 1;
32300Sstevel@tonic-gate 			IN6_IPADDR_TO_V4MAPPED(src,
32310Sstevel@tonic-gate 			    &ilg->ilg_filter->sl_addr[0]);
32320Sstevel@tonic-gate 		}
32330Sstevel@tonic-gate 		if (group == INADDR_ANY) {
32340Sstevel@tonic-gate 			ilg->ilg_v6group = ipv6_all_zeros;
32350Sstevel@tonic-gate 		} else {
32360Sstevel@tonic-gate 			IN6_IPADDR_TO_V4MAPPED(group, &ilg->ilg_v6group);
32370Sstevel@tonic-gate 		}
32380Sstevel@tonic-gate 		ilg->ilg_ipif = ipif;
32390Sstevel@tonic-gate 		ilg->ilg_ill = NULL;
32400Sstevel@tonic-gate 		ilg->ilg_orig_ifindex = 0;
32410Sstevel@tonic-gate 		ilg->ilg_fmode = fmode;
32420Sstevel@tonic-gate 	} else {
32430Sstevel@tonic-gate 		int index;
32440Sstevel@tonic-gate 		in6_addr_t v6src;
32450Sstevel@tonic-gate 		ilgstat = ILGSTAT_CHANGE;
32460Sstevel@tonic-gate 		if (ilg->ilg_fmode != fmode || src == INADDR_ANY) {
32470Sstevel@tonic-gate 			mutex_exit(&connp->conn_lock);
32480Sstevel@tonic-gate 			l_free(new_filter);
32490Sstevel@tonic-gate 			return (EINVAL);
32500Sstevel@tonic-gate 		}
32510Sstevel@tonic-gate 		if (ilg->ilg_filter == NULL) {
32520Sstevel@tonic-gate 			ilg->ilg_filter = l_alloc();
32530Sstevel@tonic-gate 			if (ilg->ilg_filter == NULL) {
32540Sstevel@tonic-gate 				mutex_exit(&connp->conn_lock);
32550Sstevel@tonic-gate 				l_free(new_filter);
32560Sstevel@tonic-gate 				return (ENOMEM);
32570Sstevel@tonic-gate 			}
32580Sstevel@tonic-gate 		}
32590Sstevel@tonic-gate 		IN6_IPADDR_TO_V4MAPPED(src, &v6src);
32600Sstevel@tonic-gate 		if (list_has_addr(ilg->ilg_filter, &v6src)) {
32610Sstevel@tonic-gate 			mutex_exit(&connp->conn_lock);
32620Sstevel@tonic-gate 			l_free(new_filter);
32630Sstevel@tonic-gate 			return (EADDRNOTAVAIL);
32640Sstevel@tonic-gate 		}
32650Sstevel@tonic-gate 		index = ilg->ilg_filter->sl_numsrc++;
32660Sstevel@tonic-gate 		ilg->ilg_filter->sl_addr[index] = v6src;
32670Sstevel@tonic-gate 	}
32680Sstevel@tonic-gate 
32690Sstevel@tonic-gate 	/*
32700Sstevel@tonic-gate 	 * Save copy of ilg's filter state to pass to other functions,
32710Sstevel@tonic-gate 	 * so we can release conn_lock now.
32720Sstevel@tonic-gate 	 */
32730Sstevel@tonic-gate 	new_fmode = ilg->ilg_fmode;
32740Sstevel@tonic-gate 	l_copy(ilg->ilg_filter, new_filter);
32750Sstevel@tonic-gate 
32760Sstevel@tonic-gate 	mutex_exit(&connp->conn_lock);
32770Sstevel@tonic-gate 
32780Sstevel@tonic-gate 	error = ip_addmulti(group, ipif, ilgstat, new_fmode, new_filter);
32790Sstevel@tonic-gate 	if (error != 0) {
32800Sstevel@tonic-gate 		/*
32810Sstevel@tonic-gate 		 * Need to undo what we did before calling ip_addmulti()!
32820Sstevel@tonic-gate 		 * Must look up the ilg again since we've not been holding
32830Sstevel@tonic-gate 		 * conn_lock.
32840Sstevel@tonic-gate 		 */
32850Sstevel@tonic-gate 		in6_addr_t v6src;
32860Sstevel@tonic-gate 		if (ilgstat == ILGSTAT_NEW)
32870Sstevel@tonic-gate 			v6src = ipv6_all_zeros;
32880Sstevel@tonic-gate 		else
32890Sstevel@tonic-gate 			IN6_IPADDR_TO_V4MAPPED(src, &v6src);
32900Sstevel@tonic-gate 		mutex_enter(&connp->conn_lock);
32910Sstevel@tonic-gate 		ilg = ilg_lookup_ipif(connp, group, ipif);
32920Sstevel@tonic-gate 		ASSERT(ilg != NULL);
32930Sstevel@tonic-gate 		ilg_delete(connp, ilg, &v6src);
32940Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
32950Sstevel@tonic-gate 		l_free(new_filter);
32960Sstevel@tonic-gate 		return (error);
32970Sstevel@tonic-gate 	}
32980Sstevel@tonic-gate 
32990Sstevel@tonic-gate 	l_free(new_filter);
33000Sstevel@tonic-gate 	return (0);
33010Sstevel@tonic-gate }
33020Sstevel@tonic-gate 
33030Sstevel@tonic-gate static int
33040Sstevel@tonic-gate ilg_add_v6(conn_t *connp, const in6_addr_t *v6group, ill_t *ill,
33050Sstevel@tonic-gate     mcast_record_t fmode, const in6_addr_t *v6src)
33060Sstevel@tonic-gate {
33070Sstevel@tonic-gate 	int	error = 0;
33080Sstevel@tonic-gate 	int	orig_ifindex;
33090Sstevel@tonic-gate 	ilg_t	*ilg;
33100Sstevel@tonic-gate 	ilg_stat_t ilgstat;
33110Sstevel@tonic-gate 	slist_t	*new_filter = NULL;
33120Sstevel@tonic-gate 	int	new_fmode;
33130Sstevel@tonic-gate 
33140Sstevel@tonic-gate 	ASSERT(IAM_WRITER_ILL(ill));
33150Sstevel@tonic-gate 
33160Sstevel@tonic-gate 	if (!(ill->ill_flags & ILLF_MULTICAST))
33170Sstevel@tonic-gate 		return (EADDRNOTAVAIL);
33180Sstevel@tonic-gate 
33190Sstevel@tonic-gate 	/*
33200Sstevel@tonic-gate 	 * conn_lock protects the ilg list.  Serializes 2 threads doing
33210Sstevel@tonic-gate 	 * join (sock, group1, hme0) and (sock, group2, hme1) where hme0
33220Sstevel@tonic-gate 	 * and hme1 map to different ipsq's, but both operations happen
33230Sstevel@tonic-gate 	 * on the same conn.
33240Sstevel@tonic-gate 	 */
33250Sstevel@tonic-gate 	mutex_enter(&connp->conn_lock);
33260Sstevel@tonic-gate 
33270Sstevel@tonic-gate 	/*
33280Sstevel@tonic-gate 	 * Use the ifindex to do the lookup. We can't use the ill
33290Sstevel@tonic-gate 	 * directly because ilg_ill could point to a different ill if
33300Sstevel@tonic-gate 	 * things have moved.
33310Sstevel@tonic-gate 	 */
33320Sstevel@tonic-gate 	orig_ifindex = ill->ill_phyint->phyint_ifindex;
33330Sstevel@tonic-gate 	ilg = ilg_lookup_ill_index_v6(connp, v6group, orig_ifindex);
33340Sstevel@tonic-gate 
33350Sstevel@tonic-gate 	/*
33360Sstevel@tonic-gate 	 * Depending on the option we're handling, may or may not be okay
33370Sstevel@tonic-gate 	 * if group has already been added.  Figure out our rules based
33380Sstevel@tonic-gate 	 * on fmode and src params.  Also make sure there's enough room
33390Sstevel@tonic-gate 	 * in the filter if we're adding a source to an existing filter.
33400Sstevel@tonic-gate 	 */
33410Sstevel@tonic-gate 	if (IN6_IS_ADDR_UNSPECIFIED(v6src)) {
33420Sstevel@tonic-gate 		/* we're joining for all sources, must not have joined */
33430Sstevel@tonic-gate 		if (ilg != NULL)
33440Sstevel@tonic-gate 			error = EADDRINUSE;
33450Sstevel@tonic-gate 	} else {
33460Sstevel@tonic-gate 		if (fmode == MODE_IS_EXCLUDE) {
33470Sstevel@tonic-gate 			/* (excl {addr}) => block source, must have joined */
33480Sstevel@tonic-gate 			if (ilg == NULL)
33490Sstevel@tonic-gate 				error = EADDRNOTAVAIL;
33500Sstevel@tonic-gate 		}
33510Sstevel@tonic-gate 		/* (incl {addr}) => join source, may have joined */
33520Sstevel@tonic-gate 
33530Sstevel@tonic-gate 		if (ilg != NULL &&
33540Sstevel@tonic-gate 		    SLIST_CNT(ilg->ilg_filter) == MAX_FILTER_SIZE)
33550Sstevel@tonic-gate 			error = ENOBUFS;
33560Sstevel@tonic-gate 	}
33570Sstevel@tonic-gate 	if (error != 0) {
33580Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
33590Sstevel@tonic-gate 		return (error);
33600Sstevel@tonic-gate 	}
33610Sstevel@tonic-gate 
33620Sstevel@tonic-gate 	/*
33630Sstevel@tonic-gate 	 * Alloc buffer to copy new state into (see below) before
33640Sstevel@tonic-gate 	 * we make any changes, so we can bail if it fails.
33650Sstevel@tonic-gate 	 */
33660Sstevel@tonic-gate 	if ((new_filter = l_alloc()) == NULL) {
33670Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
33680Sstevel@tonic-gate 		return (ENOMEM);
33690Sstevel@tonic-gate 	}
33700Sstevel@tonic-gate 
33710Sstevel@tonic-gate 	if (ilg == NULL) {
33720Sstevel@tonic-gate 		if ((ilg = conn_ilg_alloc(connp)) == NULL) {
33730Sstevel@tonic-gate 			mutex_exit(&connp->conn_lock);
33740Sstevel@tonic-gate 			l_free(new_filter);
33750Sstevel@tonic-gate 			return (ENOMEM);
33760Sstevel@tonic-gate 		}
33770Sstevel@tonic-gate 		if (!IN6_IS_ADDR_UNSPECIFIED(v6src)) {
33780Sstevel@tonic-gate 			ilg->ilg_filter = l_alloc();
33790Sstevel@tonic-gate 			if (ilg->ilg_filter == NULL) {
33800Sstevel@tonic-gate 				ilg_delete(connp, ilg, NULL);
33810Sstevel@tonic-gate 				mutex_exit(&connp->conn_lock);
33820Sstevel@tonic-gate 				l_free(new_filter);
33830Sstevel@tonic-gate 				return (ENOMEM);
33840Sstevel@tonic-gate 			}
33850Sstevel@tonic-gate 			ilg->ilg_filter->sl_numsrc = 1;
33860Sstevel@tonic-gate 			ilg->ilg_filter->sl_addr[0] = *v6src;
33870Sstevel@tonic-gate 		}
33880Sstevel@tonic-gate 		ilgstat = ILGSTAT_NEW;
33890Sstevel@tonic-gate 		ilg->ilg_v6group = *v6group;
33900Sstevel@tonic-gate 		ilg->ilg_fmode = fmode;
33910Sstevel@tonic-gate 		ilg->ilg_ipif = NULL;
33920Sstevel@tonic-gate 		/*
33930Sstevel@tonic-gate 		 * Choose our target ill to join on. This might be different
33940Sstevel@tonic-gate 		 * from the ill we've been given if it's currently down and
33950Sstevel@tonic-gate 		 * part of a group.
33960Sstevel@tonic-gate 		 *
33970Sstevel@tonic-gate 		 * new ill is not refheld; we are writer.
33980Sstevel@tonic-gate 		 */
33990Sstevel@tonic-gate 		ill = ip_choose_multi_ill(ill, v6group);
34000Sstevel@tonic-gate 		ASSERT(!(ill->ill_state_flags & ILL_CONDEMNED));
34010Sstevel@tonic-gate 		ilg->ilg_ill = ill;
34020Sstevel@tonic-gate 		/*
34030Sstevel@tonic-gate 		 * Remember the orig_ifindex that we joined on, so that we
34040Sstevel@tonic-gate 		 * can successfully delete them later on and also search
34050Sstevel@tonic-gate 		 * for duplicates if the application wants to join again.
34060Sstevel@tonic-gate 		 */
34070Sstevel@tonic-gate 		ilg->ilg_orig_ifindex = orig_ifindex;
34080Sstevel@tonic-gate 	} else {
34090Sstevel@tonic-gate 		int index;
34100Sstevel@tonic-gate 		if (ilg->ilg_fmode != fmode || IN6_IS_ADDR_UNSPECIFIED(v6src)) {
34110Sstevel@tonic-gate 			mutex_exit(&connp->conn_lock);
34120Sstevel@tonic-gate 			l_free(new_filter);
34130Sstevel@tonic-gate 			return (EINVAL);
34140Sstevel@tonic-gate 		}
34150Sstevel@tonic-gate 		if (ilg->ilg_filter == NULL) {
34160Sstevel@tonic-gate 			ilg->ilg_filter = l_alloc();
34170Sstevel@tonic-gate 			if (ilg->ilg_filter == NULL) {
34180Sstevel@tonic-gate 				mutex_exit(&connp->conn_lock);
34190Sstevel@tonic-gate 				l_free(new_filter);
34200Sstevel@tonic-gate 				return (ENOMEM);
34210Sstevel@tonic-gate 			}
34220Sstevel@tonic-gate 		}
34230Sstevel@tonic-gate 		if (list_has_addr(ilg->ilg_filter, v6src)) {
34240Sstevel@tonic-gate 			mutex_exit(&connp->conn_lock);
34250Sstevel@tonic-gate 			l_free(new_filter);
34260Sstevel@tonic-gate 			return (EADDRNOTAVAIL);
34270Sstevel@tonic-gate 		}
34280Sstevel@tonic-gate 		ilgstat = ILGSTAT_CHANGE;
34290Sstevel@tonic-gate 		index = ilg->ilg_filter->sl_numsrc++;
34300Sstevel@tonic-gate 		ilg->ilg_filter->sl_addr[index] = *v6src;
34310Sstevel@tonic-gate 		/*
34320Sstevel@tonic-gate 		 * The current ill might be different from the one we were
34330Sstevel@tonic-gate 		 * asked to join on (if failover has occurred); we should
34340Sstevel@tonic-gate 		 * join on the ill stored in the ilg.  The original ill
34350Sstevel@tonic-gate 		 * is noted in ilg_orig_ifindex, which matched our request.
34360Sstevel@tonic-gate 		 */
34370Sstevel@tonic-gate 		ill = ilg->ilg_ill;
34380Sstevel@tonic-gate 	}
34390Sstevel@tonic-gate 
34400Sstevel@tonic-gate 	/*
34410Sstevel@tonic-gate 	 * Save copy of ilg's filter state to pass to other functions,
34420Sstevel@tonic-gate 	 * so we can release conn_lock now.
34430Sstevel@tonic-gate 	 */
34440Sstevel@tonic-gate 	new_fmode = ilg->ilg_fmode;
34450Sstevel@tonic-gate 	l_copy(ilg->ilg_filter, new_filter);
34460Sstevel@tonic-gate 
34470Sstevel@tonic-gate 	mutex_exit(&connp->conn_lock);
34480Sstevel@tonic-gate 
34490Sstevel@tonic-gate 	/*
34500Sstevel@tonic-gate 	 * Now update the ill. We wait to do this until after the ilg
34510Sstevel@tonic-gate 	 * has been updated because we need to update the src filter
34520Sstevel@tonic-gate 	 * info for the ill, which involves looking at the status of
34530Sstevel@tonic-gate 	 * all the ilgs associated with this group/interface pair.
34540Sstevel@tonic-gate 	 */
34550Sstevel@tonic-gate 	error = ip_addmulti_v6(v6group, ill, orig_ifindex, connp->conn_zoneid,
34560Sstevel@tonic-gate 	    ilgstat, new_fmode, new_filter);
34570Sstevel@tonic-gate 	if (error != 0) {
34580Sstevel@tonic-gate 		/*
34590Sstevel@tonic-gate 		 * But because we waited, we have to undo the ilg update
34600Sstevel@tonic-gate 		 * if ip_addmulti_v6() fails.  We also must lookup ilg
34610Sstevel@tonic-gate 		 * again, since we've not been holding conn_lock.
34620Sstevel@tonic-gate 		 */
34630Sstevel@tonic-gate 		in6_addr_t delsrc =
34640Sstevel@tonic-gate 		    (ilgstat == ILGSTAT_NEW) ? ipv6_all_zeros : *v6src;
34650Sstevel@tonic-gate 		mutex_enter(&connp->conn_lock);
34660Sstevel@tonic-gate 		ilg = ilg_lookup_ill_index_v6(connp, v6group, orig_ifindex);
34670Sstevel@tonic-gate 		ASSERT(ilg != NULL);
34680Sstevel@tonic-gate 		ilg_delete(connp, ilg, &delsrc);
34690Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
34700Sstevel@tonic-gate 		l_free(new_filter);
34710Sstevel@tonic-gate 		return (error);
34720Sstevel@tonic-gate 	}
34730Sstevel@tonic-gate 
34740Sstevel@tonic-gate 	l_free(new_filter);
34750Sstevel@tonic-gate 
34760Sstevel@tonic-gate 	return (0);
34770Sstevel@tonic-gate }
34780Sstevel@tonic-gate 
34790Sstevel@tonic-gate /*
34800Sstevel@tonic-gate  * Find an IPv4 ilg matching group, ill and source
34810Sstevel@tonic-gate  */
34820Sstevel@tonic-gate ilg_t *
34830Sstevel@tonic-gate ilg_lookup_ill_withsrc(conn_t *connp, ipaddr_t group, ipaddr_t src, ill_t *ill)
34840Sstevel@tonic-gate {
34850Sstevel@tonic-gate 	in6_addr_t v6group, v6src;
34860Sstevel@tonic-gate 	int i;
34870Sstevel@tonic-gate 	boolean_t isinlist;
34880Sstevel@tonic-gate 	ilg_t *ilg;
34890Sstevel@tonic-gate 	ipif_t *ipif;
34900Sstevel@tonic-gate 	ill_t *ilg_ill;
34910Sstevel@tonic-gate 
34920Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&connp->conn_lock));
34930Sstevel@tonic-gate 
34940Sstevel@tonic-gate 	/*
34950Sstevel@tonic-gate 	 * INADDR_ANY is represented as the IPv6 unspecified addr.
34960Sstevel@tonic-gate 	 */
34970Sstevel@tonic-gate 	if (group == INADDR_ANY)
34980Sstevel@tonic-gate 		v6group = ipv6_all_zeros;
34990Sstevel@tonic-gate 	else
35000Sstevel@tonic-gate 		IN6_IPADDR_TO_V4MAPPED(group, &v6group);
35010Sstevel@tonic-gate 
35020Sstevel@tonic-gate 	for (i = 0; i < connp->conn_ilg_inuse; i++) {
35030Sstevel@tonic-gate 		/* ilg_ipif is NULL for v6; skip them */
35040Sstevel@tonic-gate 		ilg = &connp->conn_ilg[i];
35050Sstevel@tonic-gate 		if ((ipif = ilg->ilg_ipif) == NULL)
35060Sstevel@tonic-gate 			continue;
35070Sstevel@tonic-gate 		ASSERT(ilg->ilg_ill == NULL);
35080Sstevel@tonic-gate 		ilg_ill = ipif->ipif_ill;
35090Sstevel@tonic-gate 		ASSERT(!ilg_ill->ill_isv6);
35100Sstevel@tonic-gate 		if (ilg_ill == ill &&
35110Sstevel@tonic-gate 		    IN6_ARE_ADDR_EQUAL(&ilg->ilg_v6group, &v6group)) {
35120Sstevel@tonic-gate 			if (SLIST_IS_EMPTY(ilg->ilg_filter)) {
35130Sstevel@tonic-gate 				/* no source filter, so this is a match */
35140Sstevel@tonic-gate 				return (ilg);
35150Sstevel@tonic-gate 			}
35160Sstevel@tonic-gate 			break;
35170Sstevel@tonic-gate 		}
35180Sstevel@tonic-gate 	}
35190Sstevel@tonic-gate 	if (i == connp->conn_ilg_inuse)
35200Sstevel@tonic-gate 		return (NULL);
35210Sstevel@tonic-gate 
35220Sstevel@tonic-gate 	/*
35230Sstevel@tonic-gate 	 * we have an ilg with matching ill and group; but
35240Sstevel@tonic-gate 	 * the ilg has a source list that we must check.
35250Sstevel@tonic-gate 	 */
35260Sstevel@tonic-gate 	IN6_IPADDR_TO_V4MAPPED(src, &v6src);
35270Sstevel@tonic-gate 	isinlist = B_FALSE;
35280Sstevel@tonic-gate 	for (i = 0; i < ilg->ilg_filter->sl_numsrc; i++) {
35290Sstevel@tonic-gate 		if (IN6_ARE_ADDR_EQUAL(&v6src, &ilg->ilg_filter->sl_addr[i])) {
35300Sstevel@tonic-gate 			isinlist = B_TRUE;
35310Sstevel@tonic-gate 			break;
35320Sstevel@tonic-gate 		}
35330Sstevel@tonic-gate 	}
35340Sstevel@tonic-gate 
35350Sstevel@tonic-gate 	if ((isinlist && ilg->ilg_fmode == MODE_IS_INCLUDE) ||
35360Sstevel@tonic-gate 	    (!isinlist && ilg->ilg_fmode == MODE_IS_EXCLUDE))
35370Sstevel@tonic-gate 		return (ilg);
35380Sstevel@tonic-gate 
35390Sstevel@tonic-gate 	return (NULL);
35400Sstevel@tonic-gate }
35410Sstevel@tonic-gate 
35420Sstevel@tonic-gate /*
35430Sstevel@tonic-gate  * Find an IPv6 ilg matching group, ill, and source
35440Sstevel@tonic-gate  */
35450Sstevel@tonic-gate ilg_t *
35460Sstevel@tonic-gate ilg_lookup_ill_withsrc_v6(conn_t *connp, const in6_addr_t *v6group,
35470Sstevel@tonic-gate     const in6_addr_t *v6src, ill_t *ill)
35480Sstevel@tonic-gate {
35490Sstevel@tonic-gate 	int i;
35500Sstevel@tonic-gate 	boolean_t isinlist;
35510Sstevel@tonic-gate 	ilg_t *ilg;
35520Sstevel@tonic-gate 	ill_t *ilg_ill;
35530Sstevel@tonic-gate 
35540Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&connp->conn_lock));
35550Sstevel@tonic-gate 
35560Sstevel@tonic-gate 	for (i = 0; i < connp->conn_ilg_inuse; i++) {
35570Sstevel@tonic-gate 		ilg = &connp->conn_ilg[i];
35580Sstevel@tonic-gate 		if ((ilg_ill = ilg->ilg_ill) == NULL)
35590Sstevel@tonic-gate 			continue;
35600Sstevel@tonic-gate 		ASSERT(ilg->ilg_ipif == NULL);
35610Sstevel@tonic-gate 		ASSERT(ilg_ill->ill_isv6);
35620Sstevel@tonic-gate 		if (ilg_ill == ill &&
35630Sstevel@tonic-gate 		    IN6_ARE_ADDR_EQUAL(&ilg->ilg_v6group, v6group)) {
35640Sstevel@tonic-gate 			if (SLIST_IS_EMPTY(ilg->ilg_filter)) {
35650Sstevel@tonic-gate 				/* no source filter, so this is a match */
35660Sstevel@tonic-gate 				return (ilg);
35670Sstevel@tonic-gate 			}
35680Sstevel@tonic-gate 			break;
35690Sstevel@tonic-gate 		}
35700Sstevel@tonic-gate 	}
35710Sstevel@tonic-gate 	if (i == connp->conn_ilg_inuse)
35720Sstevel@tonic-gate 		return (NULL);
35730Sstevel@tonic-gate 
35740Sstevel@tonic-gate 	/*
35750Sstevel@tonic-gate 	 * we have an ilg with matching ill and group; but
35760Sstevel@tonic-gate 	 * the ilg has a source list that we must check.
35770Sstevel@tonic-gate 	 */
35780Sstevel@tonic-gate 	isinlist = B_FALSE;
35790Sstevel@tonic-gate 	for (i = 0; i < ilg->ilg_filter->sl_numsrc; i++) {
35800Sstevel@tonic-gate 		if (IN6_ARE_ADDR_EQUAL(v6src, &ilg->ilg_filter->sl_addr[i])) {
35810Sstevel@tonic-gate 			isinlist = B_TRUE;
35820Sstevel@tonic-gate 			break;
35830Sstevel@tonic-gate 		}
35840Sstevel@tonic-gate 	}
35850Sstevel@tonic-gate 
35860Sstevel@tonic-gate 	if ((isinlist && ilg->ilg_fmode == MODE_IS_INCLUDE) ||
35870Sstevel@tonic-gate 	    (!isinlist && ilg->ilg_fmode == MODE_IS_EXCLUDE))
35880Sstevel@tonic-gate 		return (ilg);
35890Sstevel@tonic-gate 
35900Sstevel@tonic-gate 	return (NULL);
35910Sstevel@tonic-gate }
35920Sstevel@tonic-gate 
35930Sstevel@tonic-gate /*
35940Sstevel@tonic-gate  * Get the ilg whose ilg_orig_ifindex is associated with ifindex.
35950Sstevel@tonic-gate  * This is useful when the interface fails and we have moved
35960Sstevel@tonic-gate  * to a new ill, but still would like to locate using the index
35970Sstevel@tonic-gate  * that we originally used to join. Used only for IPv6 currently.
35980Sstevel@tonic-gate  */
35990Sstevel@tonic-gate static ilg_t *
36000Sstevel@tonic-gate ilg_lookup_ill_index_v6(conn_t *connp, const in6_addr_t *v6group, int ifindex)
36010Sstevel@tonic-gate {
36020Sstevel@tonic-gate 	ilg_t	*ilg;
36030Sstevel@tonic-gate 	int	i;
36040Sstevel@tonic-gate 
36050Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&connp->conn_lock));
36060Sstevel@tonic-gate 	for (i = 0; i < connp->conn_ilg_inuse; i++) {
36070Sstevel@tonic-gate 		ilg = &connp->conn_ilg[i];
36080Sstevel@tonic-gate 		/* ilg_ill is NULL for V4. Skip them */
36090Sstevel@tonic-gate 		if (ilg->ilg_ill == NULL)
36100Sstevel@tonic-gate 			continue;
36110Sstevel@tonic-gate 		/* ilg_ipif is NULL for V6 */
36120Sstevel@tonic-gate 		ASSERT(ilg->ilg_ipif == NULL);
36130Sstevel@tonic-gate 		ASSERT(ilg->ilg_orig_ifindex != 0);
36140Sstevel@tonic-gate 		if (IN6_ARE_ADDR_EQUAL(&ilg->ilg_v6group, v6group) &&
36150Sstevel@tonic-gate 		    ilg->ilg_orig_ifindex == ifindex) {
36160Sstevel@tonic-gate 			return (ilg);
36170Sstevel@tonic-gate 		}
36180Sstevel@tonic-gate 	}
36190Sstevel@tonic-gate 	return (NULL);
36200Sstevel@tonic-gate }
36210Sstevel@tonic-gate 
36220Sstevel@tonic-gate /*
36230Sstevel@tonic-gate  * Find an IPv6 ilg matching group and ill
36240Sstevel@tonic-gate  */
36250Sstevel@tonic-gate ilg_t *
36260Sstevel@tonic-gate ilg_lookup_ill_v6(conn_t *connp, const in6_addr_t *v6group, ill_t *ill)
36270Sstevel@tonic-gate {
36280Sstevel@tonic-gate 	ilg_t	*ilg;
36290Sstevel@tonic-gate 	int	i;
36300Sstevel@tonic-gate 	ill_t 	*mem_ill;
36310Sstevel@tonic-gate 
36320Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&connp->conn_lock));
36330Sstevel@tonic-gate 
36340Sstevel@tonic-gate 	for (i = 0; i < connp->conn_ilg_inuse; i++) {
36350Sstevel@tonic-gate 		ilg = &connp->conn_ilg[i];
36360Sstevel@tonic-gate 		if ((mem_ill = ilg->ilg_ill) == NULL)
36370Sstevel@tonic-gate 			continue;
36380Sstevel@tonic-gate 		ASSERT(ilg->ilg_ipif == NULL);
36390Sstevel@tonic-gate 		ASSERT(mem_ill->ill_isv6);
36400Sstevel@tonic-gate 		if (mem_ill == ill &&
36410Sstevel@tonic-gate 		    IN6_ARE_ADDR_EQUAL(&ilg->ilg_v6group, v6group))
36420Sstevel@tonic-gate 			return (ilg);
36430Sstevel@tonic-gate 	}
36440Sstevel@tonic-gate 	return (NULL);
36450Sstevel@tonic-gate }
36460Sstevel@tonic-gate 
36470Sstevel@tonic-gate /*
36480Sstevel@tonic-gate  * Find an IPv4 ilg matching group and ipif
36490Sstevel@tonic-gate  */
36500Sstevel@tonic-gate static ilg_t *
36510Sstevel@tonic-gate ilg_lookup_ipif(conn_t *connp, ipaddr_t group, ipif_t *ipif)
36520Sstevel@tonic-gate {
36530Sstevel@tonic-gate 	in6_addr_t v6group;
36540Sstevel@tonic-gate 	int	i;
36550Sstevel@tonic-gate 
36560Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&connp->conn_lock));
36570Sstevel@tonic-gate 	ASSERT(!ipif->ipif_ill->ill_isv6);
36580Sstevel@tonic-gate 
36590Sstevel@tonic-gate 	if (group == INADDR_ANY)
36600Sstevel@tonic-gate 		v6group = ipv6_all_zeros;
36610Sstevel@tonic-gate 	else
36620Sstevel@tonic-gate 		IN6_IPADDR_TO_V4MAPPED(group, &v6group);
36630Sstevel@tonic-gate 
36640Sstevel@tonic-gate 	for (i = 0; i < connp->conn_ilg_inuse; i++) {
36650Sstevel@tonic-gate 		if (IN6_ARE_ADDR_EQUAL(&connp->conn_ilg[i].ilg_v6group,
36660Sstevel@tonic-gate 		    &v6group) &&
36670Sstevel@tonic-gate 		    connp->conn_ilg[i].ilg_ipif == ipif)
36680Sstevel@tonic-gate 			return (&connp->conn_ilg[i]);
36690Sstevel@tonic-gate 	}
36700Sstevel@tonic-gate 	return (NULL);
36710Sstevel@tonic-gate }
36720Sstevel@tonic-gate 
36730Sstevel@tonic-gate /*
36740Sstevel@tonic-gate  * If a source address is passed in (src != NULL and src is not
36750Sstevel@tonic-gate  * unspecified), remove the specified src addr from the given ilg's
36760Sstevel@tonic-gate  * filter list, else delete the ilg.
36770Sstevel@tonic-gate  */
36780Sstevel@tonic-gate static void
36790Sstevel@tonic-gate ilg_delete(conn_t *connp, ilg_t *ilg, const in6_addr_t *src)
36800Sstevel@tonic-gate {
36810Sstevel@tonic-gate 	int	i;
36820Sstevel@tonic-gate 
36830Sstevel@tonic-gate 	ASSERT((ilg->ilg_ipif != NULL) ^ (ilg->ilg_ill != NULL));
36840Sstevel@tonic-gate 	ASSERT(ilg->ilg_ipif == NULL || IAM_WRITER_IPIF(ilg->ilg_ipif));
36850Sstevel@tonic-gate 	ASSERT(ilg->ilg_ill == NULL || IAM_WRITER_ILL(ilg->ilg_ill));
36860Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&connp->conn_lock));
36870Sstevel@tonic-gate 	ASSERT(!(ilg->ilg_flags & ILG_DELETED));
36880Sstevel@tonic-gate 
36890Sstevel@tonic-gate 	if (src == NULL || IN6_IS_ADDR_UNSPECIFIED(src)) {
36900Sstevel@tonic-gate 		if (connp->conn_ilg_walker_cnt != 0) {
36910Sstevel@tonic-gate 			ilg->ilg_flags |= ILG_DELETED;
36920Sstevel@tonic-gate 			return;
36930Sstevel@tonic-gate 		}
36940Sstevel@tonic-gate 
36950Sstevel@tonic-gate 		FREE_SLIST(ilg->ilg_filter);
36960Sstevel@tonic-gate 
36970Sstevel@tonic-gate 		i = ilg - &connp->conn_ilg[0];
36980Sstevel@tonic-gate 		ASSERT(i >= 0 && i < connp->conn_ilg_inuse);
36990Sstevel@tonic-gate 
37000Sstevel@tonic-gate 		/* Move other entries up one step */
37010Sstevel@tonic-gate 		connp->conn_ilg_inuse--;
37020Sstevel@tonic-gate 		for (; i < connp->conn_ilg_inuse; i++)
37030Sstevel@tonic-gate 			connp->conn_ilg[i] = connp->conn_ilg[i+1];
37040Sstevel@tonic-gate 
37050Sstevel@tonic-gate 		if (connp->conn_ilg_inuse == 0) {
37060Sstevel@tonic-gate 			mi_free((char *)connp->conn_ilg);
37070Sstevel@tonic-gate 			connp->conn_ilg = NULL;
37080Sstevel@tonic-gate 			cv_broadcast(&connp->conn_refcv);
37090Sstevel@tonic-gate 		}
37100Sstevel@tonic-gate 	} else {
37110Sstevel@tonic-gate 		l_remove(ilg->ilg_filter, src);
37120Sstevel@tonic-gate 	}
37130Sstevel@tonic-gate }
37140Sstevel@tonic-gate 
37150Sstevel@tonic-gate /*
37160Sstevel@tonic-gate  * Called from conn close. No new ilg can be added or removed.
37170Sstevel@tonic-gate  * because CONN_CLOSING has been set by ip_close. ilg_add / ilg_delete
37180Sstevel@tonic-gate  * will return error if conn has started closing.
37190Sstevel@tonic-gate  */
37200Sstevel@tonic-gate void
37210Sstevel@tonic-gate ilg_delete_all(conn_t *connp)
37220Sstevel@tonic-gate {
37230Sstevel@tonic-gate 	int	i;
37240Sstevel@tonic-gate 	ipif_t	*ipif = NULL;
37250Sstevel@tonic-gate 	ill_t	*ill = NULL;
37260Sstevel@tonic-gate 	ilg_t	*ilg;
37270Sstevel@tonic-gate 	in6_addr_t v6group;
37280Sstevel@tonic-gate 	boolean_t success;
37290Sstevel@tonic-gate 	ipsq_t	*ipsq;
37300Sstevel@tonic-gate 	int	orig_ifindex;
37310Sstevel@tonic-gate 
37320Sstevel@tonic-gate 	mutex_enter(&connp->conn_lock);
37330Sstevel@tonic-gate retry:
37340Sstevel@tonic-gate 	ILG_WALKER_HOLD(connp);
37350Sstevel@tonic-gate 	for (i = connp->conn_ilg_inuse - 1; i >= 0; ) {
37360Sstevel@tonic-gate 		ilg = &connp->conn_ilg[i];
37370Sstevel@tonic-gate 		/*
37380Sstevel@tonic-gate 		 * Since this walk is not atomic (we drop the
37390Sstevel@tonic-gate 		 * conn_lock and wait in ipsq_enter) we need
37400Sstevel@tonic-gate 		 * to check for the ILG_DELETED flag.
37410Sstevel@tonic-gate 		 */
37420Sstevel@tonic-gate 		if (ilg->ilg_flags & ILG_DELETED) {
37430Sstevel@tonic-gate 			/* Go to the next ilg */
37440Sstevel@tonic-gate 			i--;
37450Sstevel@tonic-gate 			continue;
37460Sstevel@tonic-gate 		}
37470Sstevel@tonic-gate 		v6group = ilg->ilg_v6group;
37480Sstevel@tonic-gate 
37490Sstevel@tonic-gate 		if (IN6_IS_ADDR_V4MAPPED(&v6group)) {
37500Sstevel@tonic-gate 			ipif = ilg->ilg_ipif;
37510Sstevel@tonic-gate 			ill = ipif->ipif_ill;
37520Sstevel@tonic-gate 		} else {
37530Sstevel@tonic-gate 			ipif = NULL;
37540Sstevel@tonic-gate 			ill = ilg->ilg_ill;
37550Sstevel@tonic-gate 		}
37560Sstevel@tonic-gate 		/*
37570Sstevel@tonic-gate 		 * We may not be able to refhold the ill if the ill/ipif
37580Sstevel@tonic-gate 		 * is changing. But we need to make sure that the ill will
37590Sstevel@tonic-gate 		 * not vanish. So we just bump up the ill_waiter count.
37600Sstevel@tonic-gate 		 * If we are unable to do even that, then the ill is closing,
37610Sstevel@tonic-gate 		 * in which case the unplumb thread will handle the cleanup,
37620Sstevel@tonic-gate 		 * and we move on to the next ilg.
37630Sstevel@tonic-gate 		 */
37640Sstevel@tonic-gate 		if (!ill_waiter_inc(ill)) {
37650Sstevel@tonic-gate 			/* Go to the next ilg */
37660Sstevel@tonic-gate 			i--;
37670Sstevel@tonic-gate 			continue;
37680Sstevel@tonic-gate 		}
37690Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
37700Sstevel@tonic-gate 		/*
37710Sstevel@tonic-gate 		 * To prevent deadlock between ill close which waits inside
37720Sstevel@tonic-gate 		 * the perimeter, and conn close, ipsq_enter returns error,
37730Sstevel@tonic-gate 		 * the moment ILL_CONDEMNED is set, in which case ill close
37740Sstevel@tonic-gate 		 * takes responsibility to cleanup the ilgs. Note that we
37750Sstevel@tonic-gate 		 * have not yet set condemned flag, otherwise the conn can't
37760Sstevel@tonic-gate 		 * be refheld for cleanup by those routines and it would be
37770Sstevel@tonic-gate 		 * a mutual deadlock.
37780Sstevel@tonic-gate 		 */
37790Sstevel@tonic-gate 		success = ipsq_enter(ill, B_FALSE);
37800Sstevel@tonic-gate 		ipsq = ill->ill_phyint->phyint_ipsq;
37810Sstevel@tonic-gate 		ill_waiter_dcr(ill);
37820Sstevel@tonic-gate 		mutex_enter(&connp->conn_lock);
37830Sstevel@tonic-gate 		if (!success) {
37840Sstevel@tonic-gate 			/* Go to the next ilg */
37850Sstevel@tonic-gate 			i--;
37860Sstevel@tonic-gate 			continue;
37870Sstevel@tonic-gate 		}
37880Sstevel@tonic-gate 
37890Sstevel@tonic-gate 		/*
37900Sstevel@tonic-gate 		 * Make sure that nothing has changed under. For eg.
37910Sstevel@tonic-gate 		 * a failover/failback can change ilg_ill while we were
37920Sstevel@tonic-gate 		 * waiting to become exclusive above
37930Sstevel@tonic-gate 		 */
37940Sstevel@tonic-gate 		if (IN6_IS_ADDR_V4MAPPED(&v6group)) {
37950Sstevel@tonic-gate 			ipif = ilg->ilg_ipif;
37960Sstevel@tonic-gate 			ill = ipif->ipif_ill;
37970Sstevel@tonic-gate 		} else {
37980Sstevel@tonic-gate 			ipif = NULL;
37990Sstevel@tonic-gate 			ill = ilg->ilg_ill;
38000Sstevel@tonic-gate 		}
38010Sstevel@tonic-gate 		if (!IAM_WRITER_ILL(ill) || (ilg->ilg_flags & ILG_DELETED)) {
38020Sstevel@tonic-gate 			/*
38030Sstevel@tonic-gate 			 * The ilg has changed under us probably due
38040Sstevel@tonic-gate 			 * to a failover or unplumb. Retry on the same ilg.
38050Sstevel@tonic-gate 			 */
38060Sstevel@tonic-gate 			mutex_exit(&connp->conn_lock);
38070Sstevel@tonic-gate 			ipsq_exit(ipsq, B_TRUE, B_TRUE);
38080Sstevel@tonic-gate 			mutex_enter(&connp->conn_lock);
38090Sstevel@tonic-gate 			continue;
38100Sstevel@tonic-gate 		}
38110Sstevel@tonic-gate 		v6group = ilg->ilg_v6group;
38120Sstevel@tonic-gate 		orig_ifindex = ilg->ilg_orig_ifindex;
38130Sstevel@tonic-gate 		ilg_delete(connp, ilg, NULL);
38140Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
38150Sstevel@tonic-gate 
38160Sstevel@tonic-gate 		if (ipif != NULL)
38170Sstevel@tonic-gate 			(void) ip_delmulti(V4_PART_OF_V6(v6group), ipif,
38180Sstevel@tonic-gate 			    B_FALSE, B_TRUE);
38190Sstevel@tonic-gate 
38200Sstevel@tonic-gate 		else
38210Sstevel@tonic-gate 			(void) ip_delmulti_v6(&v6group, ill, orig_ifindex,
38220Sstevel@tonic-gate 			    connp->conn_zoneid, B_FALSE, B_TRUE);
38230Sstevel@tonic-gate 
38240Sstevel@tonic-gate 		ipsq_exit(ipsq, B_TRUE, B_TRUE);
38250Sstevel@tonic-gate 		mutex_enter(&connp->conn_lock);
38260Sstevel@tonic-gate 		/* Go to the next ilg */
38270Sstevel@tonic-gate 		i--;
38280Sstevel@tonic-gate 	}
38290Sstevel@tonic-gate 	ILG_WALKER_RELE(connp);
38300Sstevel@tonic-gate 
38310Sstevel@tonic-gate 	/* If any ill was skipped above wait and retry */
38320Sstevel@tonic-gate 	if (connp->conn_ilg_inuse != 0) {
38330Sstevel@tonic-gate 		cv_wait(&connp->conn_refcv, &connp->conn_lock);
38340Sstevel@tonic-gate 		goto retry;
38350Sstevel@tonic-gate 	}
38360Sstevel@tonic-gate 	mutex_exit(&connp->conn_lock);
38370Sstevel@tonic-gate }
38380Sstevel@tonic-gate 
38390Sstevel@tonic-gate /*
38400Sstevel@tonic-gate  * Called from ill close by ipcl_walk for clearing conn_ilg and
38410Sstevel@tonic-gate  * conn_multicast_ipif for a given ipif. conn is held by caller.
38420Sstevel@tonic-gate  * Note that ipcl_walk only walks conns that are not yet condemned.
38430Sstevel@tonic-gate  * condemned conns can't be refheld. For this reason, conn must become clean
38440Sstevel@tonic-gate  * first, i.e. it must not refer to any ill/ire/ipif and then only set
38450Sstevel@tonic-gate  * condemned flag.
38460Sstevel@tonic-gate  */
38470Sstevel@tonic-gate static void
38480Sstevel@tonic-gate conn_delete_ipif(conn_t *connp, caddr_t arg)
38490Sstevel@tonic-gate {
38500Sstevel@tonic-gate 	ipif_t	*ipif = (ipif_t *)arg;
38510Sstevel@tonic-gate 	int	i;
38520Sstevel@tonic-gate 	char	group_buf1[INET6_ADDRSTRLEN];
38530Sstevel@tonic-gate 	char	group_buf2[INET6_ADDRSTRLEN];
38540Sstevel@tonic-gate 	ipaddr_t group;
38550Sstevel@tonic-gate 	ilg_t	*ilg;
38560Sstevel@tonic-gate 
38570Sstevel@tonic-gate 	/*
38580Sstevel@tonic-gate 	 * Even though conn_ilg_inuse can change while we are in this loop,
38590Sstevel@tonic-gate 	 * i.e.ilgs can be created or deleted on this connp, no new ilgs can
38600Sstevel@tonic-gate 	 * be created or deleted for this connp, on this ill, since this ill
38610Sstevel@tonic-gate 	 * is the perimeter. So we won't miss any ilg in this cleanup.
38620Sstevel@tonic-gate 	 */
38630Sstevel@tonic-gate 	mutex_enter(&connp->conn_lock);
38640Sstevel@tonic-gate 
38650Sstevel@tonic-gate 	/*
38660Sstevel@tonic-gate 	 * Increment the walker count, so that ilg repacking does not
38670Sstevel@tonic-gate 	 * occur while we are in the loop.
38680Sstevel@tonic-gate 	 */
38690Sstevel@tonic-gate 	ILG_WALKER_HOLD(connp);
38700Sstevel@tonic-gate 	for (i = connp->conn_ilg_inuse - 1; i >= 0; i--) {
38710Sstevel@tonic-gate 		ilg = &connp->conn_ilg[i];
38720Sstevel@tonic-gate 		if (ilg->ilg_ipif != ipif || (ilg->ilg_flags & ILG_DELETED))
38730Sstevel@tonic-gate 			continue;
38740Sstevel@tonic-gate 		/*
38750Sstevel@tonic-gate 		 * ip_close cannot be cleaning this ilg at the same time.
38760Sstevel@tonic-gate 		 * since it also has to execute in this ill's perimeter which
38770Sstevel@tonic-gate 		 * we are now holding. Only a clean conn can be condemned.
38780Sstevel@tonic-gate 		 */
38790Sstevel@tonic-gate 		ASSERT(!(connp->conn_state_flags & CONN_CONDEMNED));
38800Sstevel@tonic-gate 
38810Sstevel@tonic-gate 		/* Blow away the membership */
38820Sstevel@tonic-gate 		ip1dbg(("conn_delete_ilg_ipif: %s on %s (%s)\n",
38830Sstevel@tonic-gate 		    inet_ntop(AF_INET6, &connp->conn_ilg[i].ilg_v6group,
38840Sstevel@tonic-gate 		    group_buf1, sizeof (group_buf1)),
38850Sstevel@tonic-gate 		    inet_ntop(AF_INET6, &ipif->ipif_v6lcl_addr,
38860Sstevel@tonic-gate 		    group_buf2, sizeof (group_buf2)),
38870Sstevel@tonic-gate 		    ipif->ipif_ill->ill_name));
38880Sstevel@tonic-gate 
38890Sstevel@tonic-gate 		/* ilg_ipif is NULL for V6, so we won't be here */
38900Sstevel@tonic-gate 		ASSERT(IN6_IS_ADDR_V4MAPPED(&ilg->ilg_v6group));
38910Sstevel@tonic-gate 
38920Sstevel@tonic-gate 		group = V4_PART_OF_V6(ilg->ilg_v6group);
38930Sstevel@tonic-gate 		ilg_delete(connp, &connp->conn_ilg[i], NULL);
38940Sstevel@tonic-gate 		mutex_exit(&connp->conn_lock);
38950Sstevel@tonic-gate 
38960Sstevel@tonic-gate 		(void) ip_delmulti(group, ipif, B_FALSE, B_TRUE);
38970Sstevel@tonic-gate 		mutex_enter(&connp->conn_lock);
38980Sstevel@tonic-gate 	}
38990Sstevel@tonic-gate 
39000Sstevel@tonic-gate 	/*
39010Sstevel@tonic-gate 	 * If we are the last walker, need to physically delete the
39020Sstevel@tonic-gate 	 * ilgs and repack.
39030Sstevel@tonic-gate 	 */
39040Sstevel@tonic-gate 	ILG_WALKER_RELE(connp);
39050Sstevel@tonic-gate 
39060Sstevel@tonic-gate 	if (connp->conn_multicast_ipif == ipif) {
39070Sstevel@tonic-gate 		/* Revert to late binding */
39080Sstevel@tonic-gate 		connp->conn_multicast_ipif = NULL;
39090Sstevel@tonic-gate 	}
39100Sstevel@tonic-gate 	mutex_exit(&connp->conn_lock);
39110Sstevel@tonic-gate 
39120Sstevel@tonic-gate 	conn_delete_ire(connp, (caddr_t)ipif);
39130Sstevel@tonic-gate }
39140Sstevel@tonic-gate 
39150Sstevel@tonic-gate /*
39160Sstevel@tonic-gate  * Called from ill close by ipcl_walk for clearing conn_ilg and
39170Sstevel@tonic-gate  * conn_multicast_ill for a given ill. conn is held by caller.
39180Sstevel@tonic-gate  * Note that ipcl_walk only walks conns that are not yet condemned.
39190Sstevel@tonic-gate  * condemned conns can't be refheld. For this reason, conn must become clean
39200Sstevel@tonic-gate  * first, i.e. it must not refer to any ill/ire/ipif and then only set
39210Sstevel@tonic-gate  * condemned flag.
39220Sstevel@tonic-gate  */
39230Sstevel@tonic-gate static void
39240Sstevel@tonic-gate conn_delete_ill(conn_t *connp, caddr_t arg)
39250Sstevel@tonic-gate {
39260Sstevel@tonic-gate 	ill_t	*ill = (ill_t *)arg;
39270Sstevel@tonic-gate 	int	i;
39280Sstevel@tonic-gate 	char	group_buf[INET6_ADDRSTRLEN];
39290Sstevel@tonic-gate 	in6_addr_t v6group;
39300Sstevel@tonic-gate 	int	orig_ifindex;
39310Sstevel@tonic-gate 	ilg_t	*ilg;
39320Sstevel@tonic-gate 
39330Sstevel@tonic-gate 	/*
39340Sstevel@tonic-gate 	 * Even though conn_ilg_inuse can change while we are in this loop,
39350Sstevel@tonic-gate 	 * no new ilgs can be created/deleted for this connp, on this
39360Sstevel@tonic-gate 	 * ill, since this ill is the perimeter. So we won't miss any ilg
39370Sstevel@tonic-gate 	 * in this cleanup.
39380Sstevel@tonic-gate 	 */
39390Sstevel@tonic-gate 	mutex_enter(&connp->conn_lock);
39400Sstevel@tonic-gate 
39410Sstevel@tonic-gate 	/*
39420Sstevel@tonic-gate 	 * Increment the walker count, so that ilg repacking does not
39430Sstevel@tonic-gate 	 * occur while we are in the loop.
39440Sstevel@tonic-gate 	 */
39450Sstevel@tonic-gate 	ILG_WALKER_HOLD(connp);
39460Sstevel@tonic-gate 	for (i = connp->conn_ilg_inuse - 1; i >= 0; i--) {
39470Sstevel@tonic-gate 		ilg = &connp->conn_ilg[i];
39480Sstevel@tonic-gate 		if ((ilg->ilg_ill == ill) && !(ilg->ilg_flags & ILG_DELETED)) {
39490Sstevel@tonic-gate 			/*
39500Sstevel@tonic-gate 			 * ip_close cannot be cleaning this ilg at the same
39510Sstevel@tonic-gate 			 * time, since it also has to execute in this ill's
39520Sstevel@tonic-gate 			 * perimeter which we are now holding. Only a clean
39530Sstevel@tonic-gate 			 * conn can be condemned.
39540Sstevel@tonic-gate 			 */
39550Sstevel@tonic-gate 			ASSERT(!(connp->conn_state_flags & CONN_CONDEMNED));
39560Sstevel@tonic-gate 
39570Sstevel@tonic-gate 			/* Blow away the membership */
39580Sstevel@tonic-gate 			ip1dbg(("conn_delete_ilg_ill: %s on %s\n",
39590Sstevel@tonic-gate 			    inet_ntop(AF_INET6, &ilg->ilg_v6group,
39600Sstevel@tonic-gate 			    group_buf, sizeof (group_buf)),
39610Sstevel@tonic-gate 			    ill->ill_name));
39620Sstevel@tonic-gate 
39630Sstevel@tonic-gate 			v6group = ilg->ilg_v6group;
39640Sstevel@tonic-gate 			orig_ifindex = ilg->ilg_orig_ifindex;
39650Sstevel@tonic-gate 			ilg_delete(connp, ilg, NULL);
39660Sstevel@tonic-gate 			mutex_exit(&connp->conn_lock);
39670Sstevel@tonic-gate 
39680Sstevel@tonic-gate 			(void) ip_delmulti_v6(&v6group, ill, orig_ifindex,
39690Sstevel@tonic-gate 			    connp->conn_zoneid, B_FALSE, B_TRUE);
39700Sstevel@tonic-gate 			mutex_enter(&connp->conn_lock);
39710Sstevel@tonic-gate 		}
39720Sstevel@tonic-gate 	}
39730Sstevel@tonic-gate 	/*
39740Sstevel@tonic-gate 	 * If we are the last walker, need to physically delete the
39750Sstevel@tonic-gate 	 * ilgs and repack.
39760Sstevel@tonic-gate 	 */
39770Sstevel@tonic-gate 	ILG_WALKER_RELE(connp);
39780Sstevel@tonic-gate 
39790Sstevel@tonic-gate 	if (connp->conn_multicast_ill == ill) {
39800Sstevel@tonic-gate 		/* Revert to late binding */
39810Sstevel@tonic-gate 		connp->conn_multicast_ill = NULL;
39820Sstevel@tonic-gate 		connp->conn_orig_multicast_ifindex = 0;
39830Sstevel@tonic-gate 	}
39840Sstevel@tonic-gate 	mutex_exit(&connp->conn_lock);
39850Sstevel@tonic-gate }
39860Sstevel@tonic-gate 
39870Sstevel@tonic-gate /*
39880Sstevel@tonic-gate  * Called when an ipif is unplumbed to make sure that there are no
39890Sstevel@tonic-gate  * dangling conn references to that ipif.
39900Sstevel@tonic-gate  * Handles ilg_ipif and conn_multicast_ipif
39910Sstevel@tonic-gate  */
39920Sstevel@tonic-gate void
39930Sstevel@tonic-gate reset_conn_ipif(ipif)
39940Sstevel@tonic-gate 	ipif_t	*ipif;
39950Sstevel@tonic-gate {
39960Sstevel@tonic-gate 	ipcl_walk(conn_delete_ipif, (caddr_t)ipif);
39970Sstevel@tonic-gate 	/* flush the SCTP ire cache for this ipif */
39980Sstevel@tonic-gate 	sctp_ire_cache_flush(ipif);
39990Sstevel@tonic-gate }
40000Sstevel@tonic-gate 
40010Sstevel@tonic-gate /*
40020Sstevel@tonic-gate  * Called when an ill is unplumbed to make sure that there are no
40030Sstevel@tonic-gate  * dangling conn references to that ill.
40040Sstevel@tonic-gate  * Handles ilg_ill, conn_multicast_ill.
40050Sstevel@tonic-gate  */
40060Sstevel@tonic-gate void
40070Sstevel@tonic-gate reset_conn_ill(ill_t *ill)
40080Sstevel@tonic-gate {
40090Sstevel@tonic-gate 	ipcl_walk(conn_delete_ill, (caddr_t)ill);
40100Sstevel@tonic-gate }
40110Sstevel@tonic-gate 
40120Sstevel@tonic-gate #ifdef DEBUG
40130Sstevel@tonic-gate /*
40140Sstevel@tonic-gate  * Walk functions walk all the interfaces in the system to make
40150Sstevel@tonic-gate  * sure that there is no refernece to the ipif or ill that is
40160Sstevel@tonic-gate  * going away.
40170Sstevel@tonic-gate  */
40180Sstevel@tonic-gate int
40190Sstevel@tonic-gate ilm_walk_ill(ill_t *ill)
40200Sstevel@tonic-gate {
40210Sstevel@tonic-gate 	int cnt = 0;
40220Sstevel@tonic-gate 	ill_t *till;
40230Sstevel@tonic-gate 	ilm_t *ilm;
40240Sstevel@tonic-gate 	ill_walk_context_t ctx;
40250Sstevel@tonic-gate 
40260Sstevel@tonic-gate 	rw_enter(&ill_g_lock, RW_READER);
40270Sstevel@tonic-gate 	till = ILL_START_WALK_ALL(&ctx);
40280Sstevel@tonic-gate 	for (; till != NULL; till = ill_next(&ctx, till)) {
40290Sstevel@tonic-gate 		for (ilm = till->ill_ilm; ilm != NULL; ilm = ilm->ilm_next) {
40300Sstevel@tonic-gate 			if (ilm->ilm_ill == ill) {
40310Sstevel@tonic-gate 				cnt++;
40320Sstevel@tonic-gate 			}
40330Sstevel@tonic-gate 		}
40340Sstevel@tonic-gate 	}
40350Sstevel@tonic-gate 	rw_exit(&ill_g_lock);
40360Sstevel@tonic-gate 
40370Sstevel@tonic-gate 	return (cnt);
40380Sstevel@tonic-gate }
40390Sstevel@tonic-gate 
40400Sstevel@tonic-gate /*
40410Sstevel@tonic-gate  * This function is called before the ipif is freed.
40420Sstevel@tonic-gate  */
40430Sstevel@tonic-gate int
40440Sstevel@tonic-gate ilm_walk_ipif(ipif_t *ipif)
40450Sstevel@tonic-gate {
40460Sstevel@tonic-gate 	int cnt = 0;
40470Sstevel@tonic-gate 	ill_t *till;
40480Sstevel@tonic-gate 	ilm_t *ilm;
40490Sstevel@tonic-gate 	ill_walk_context_t ctx;
40500Sstevel@tonic-gate 
40510Sstevel@tonic-gate 	till = ILL_START_WALK_ALL(&ctx);
40520Sstevel@tonic-gate 	for (; till != NULL; till = ill_next(&ctx, till)) {
40530Sstevel@tonic-gate 		for (ilm = till->ill_ilm; ilm != NULL; ilm = ilm->ilm_next) {
40540Sstevel@tonic-gate 			if (ilm->ilm_ipif == ipif) {
40550Sstevel@tonic-gate 					cnt++;
40560Sstevel@tonic-gate 			}
40570Sstevel@tonic-gate 		}
40580Sstevel@tonic-gate 	}
40590Sstevel@tonic-gate 	return (cnt);
40600Sstevel@tonic-gate }
40610Sstevel@tonic-gate #endif
4062