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