Home | History | Annotate | Download | only in ip
      1      0    stevel /*
      2      0    stevel  * CDDL HEADER START
      3      0    stevel  *
      4      0    stevel  * The contents of this file are subject to the terms of the
      5   1676       jpk  * Common Development and Distribution License (the "License").
      6   1676       jpk  * You may not use this file except in compliance with the License.
      7      0    stevel  *
      8      0    stevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9      0    stevel  * or http://www.opensolaris.org/os/licensing.
     10      0    stevel  * See the License for the specific language governing permissions
     11      0    stevel  * and limitations under the License.
     12      0    stevel  *
     13      0    stevel  * When distributing Covered Code, include this CDDL HEADER in each
     14      0    stevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15      0    stevel  * If applicable, add the following below this CDDL HEADER, with the
     16      0    stevel  * fields enclosed by brackets "[]" replaced with your own identifying
     17      0    stevel  * information: Portions Copyright [yyyy] [name of copyright owner]
     18      0    stevel  *
     19      0    stevel  * CDDL HEADER END
     20      0    stevel  */
     21      0    stevel /*
     22   8485     Peter  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23      0    stevel  * Use is subject to license terms.
     24      0    stevel  */
     25      0    stevel /* Copyright (c) 1990 Mentat Inc. */
     26      0    stevel 
     27      0    stevel #include <sys/types.h>
     28      0    stevel #include <sys/stream.h>
     29      0    stevel #include <sys/dlpi.h>
     30      0    stevel #include <sys/stropts.h>
     31      0    stevel #include <sys/strsun.h>
     32      0    stevel #include <sys/ddi.h>
     33      0    stevel #include <sys/cmn_err.h>
     34   2958  dr146992 #include <sys/sdt.h>
     35      0    stevel #include <sys/zone.h>
     36      0    stevel 
     37      0    stevel #include <sys/param.h>
     38      0    stevel #include <sys/socket.h>
     39   1676       jpk #include <sys/sockio.h>
     40      0    stevel #include <net/if.h>
     41      0    stevel #include <sys/systm.h>
     42   5758   jarrett #include <sys/strsubr.h>
     43      0    stevel #include <net/route.h>
     44      0    stevel #include <netinet/in.h>
     45      0    stevel #include <net/if_dl.h>
     46      0    stevel #include <netinet/ip6.h>
     47      0    stevel #include <netinet/icmp6.h>
     48      0    stevel 
     49      0    stevel #include <inet/common.h>
     50      0    stevel #include <inet/mi.h>
     51      0    stevel #include <inet/nd.h>
     52      0    stevel #include <inet/arp.h>
     53      0    stevel #include <inet/ip.h>
     54      0    stevel #include <inet/ip6.h>
     55      0    stevel #include <inet/ip_if.h>
     56      0    stevel #include <inet/ip_ndp.h>
     57      0    stevel #include <inet/ip_multi.h>
     58      0    stevel #include <inet/ipclassifier.h>
     59      0    stevel #include <inet/ipsec_impl.h>
     60      0    stevel #include <inet/sctp_ip.h>
     61      0    stevel #include <inet/ip_listutils.h>
     62    741  masputra #include <inet/udp_impl.h>
     63      0    stevel 
     64      0    stevel /* igmpv3/mldv2 source filter manipulation */
     65      0    stevel static void	ilm_bld_flists(conn_t *conn, void *arg);
     66      0    stevel static void	ilm_gen_filter(ilm_t *ilm, mcast_record_t *fmode,
     67      0    stevel     slist_t *flist);
     68      0    stevel 
     69  11042      Erik static ilm_t	*ilm_add(ill_t *ill, const in6_addr_t *group,
     70      0    stevel     ilg_stat_t ilgstat, mcast_record_t ilg_fmode, slist_t *ilg_flist,
     71   8485     Peter     zoneid_t zoneid);
     72      0    stevel static void	ilm_delete(ilm_t *ilm);
     73  11042      Erik static int	ilm_numentries(ill_t *, const in6_addr_t *);
     74  11042      Erik 
     75  11042      Erik static ilm_t	*ip_addmulti_serial(const in6_addr_t *, ill_t *, zoneid_t,
     76  11042      Erik     ilg_stat_t, mcast_record_t, slist_t *, int *);
     77  11042      Erik static ilm_t	*ip_addmulti_impl(const in6_addr_t *, ill_t *,
     78  11042      Erik     zoneid_t, ilg_stat_t, mcast_record_t, slist_t *, int *);
     79  11042      Erik static int	ip_delmulti_serial(ilm_t *, boolean_t, boolean_t);
     80  11042      Erik static int	ip_delmulti_impl(ilm_t *, boolean_t, boolean_t);
     81  11042      Erik 
     82  11042      Erik static int	ip_ll_multireq(ill_t *ill, const in6_addr_t *group,
     83  11042      Erik     t_uscalar_t);
     84  11042      Erik static ilg_t	*ilg_lookup(conn_t *, const in6_addr_t *, ipaddr_t ifaddr,
     85  11042      Erik     uint_t ifindex);
     86  11042      Erik 
     87  11042      Erik static int	ilg_add(conn_t *connp, const in6_addr_t *group,
     88  11042      Erik     ipaddr_t ifaddr, uint_t ifindex, ill_t *ill, mcast_record_t fmode,
     89  11042      Erik     const in6_addr_t *v6src);
     90      0    stevel static void	ilg_delete(conn_t *connp, ilg_t *ilg, const in6_addr_t *src);
     91      0    stevel static mblk_t	*ill_create_dl(ill_t *ill, uint32_t dl_primitive,
     92  11042      Erik     uint32_t *addr_lenp, uint32_t *addr_offp);
     93  11042      Erik static int	ip_opt_delete_group_excl(conn_t *connp,
     94  11042      Erik     const in6_addr_t *v6group, ipaddr_t ifaddr, uint_t ifindex,
     95  11042      Erik     mcast_record_t fmode, const in6_addr_t *v6src);
     96  11042      Erik 
     97  11042      Erik static	ilm_t	*ilm_lookup(ill_t *, const in6_addr_t *, zoneid_t);
     98  11042      Erik 
     99  11042      Erik static int	ip_msfilter_ill(conn_t *, mblk_t *, const ip_ioctl_cmd_t *,
    100  11042      Erik     ill_t **);
    101  11042      Erik 
    102  11042      Erik static void	ilg_check_detach(conn_t *, ill_t *);
    103  11042      Erik static void	ilg_check_reattach(conn_t *);
    104      0    stevel 
    105      0    stevel /*
    106      0    stevel  * MT notes:
    107      0    stevel  *
    108      0    stevel  * Multicast joins operate on both the ilg and ilm structures. Multiple
    109      0    stevel  * threads operating on an conn (socket) trying to do multicast joins
    110   8485     Peter  * need to synchronize when operating on the ilg. Multiple threads
    111      0    stevel  * potentially operating on different conn (socket endpoints) trying to
    112      0    stevel  * do multicast joins could eventually end up trying to manipulate the
    113  11042      Erik  * ilm simulatenously and need to synchronize on the access to the ilm.
    114  11042      Erik  * The access and lookup of the ilm, as well as other ill multicast state,
    115  11042      Erik  * is under ill_mcast_lock.
    116  11042      Erik  * The modifications and lookup of ilg entries is serialized using conn_ilg_lock
    117  11042      Erik  * rwlock. An ilg will not be freed until ilg_refcnt drops to zero.
    118  11042      Erik  *
    119  11042      Erik  * In some cases we hold ill_mcast_lock and then acquire conn_ilg_lock, but
    120  11042      Erik  * never the other way around.
    121      0    stevel  *
    122      0    stevel  * An ilm is an IP data structure used to track multicast join/leave.
    123      0    stevel  * An ilm is associated with a <multicast group, ipif> tuple in IPv4 and
    124      0    stevel  * with just <multicast group> in IPv6. ilm_refcnt is the number of ilg's
    125  11042      Erik  * referencing the ilm.
    126  11042      Erik  * The modifications and lookup of ilm entries is serialized using the
    127  11042      Erik  * ill_mcast_lock rwlock; that lock handles all the igmp/mld modifications
    128  11042      Erik  * of the ilm state.
    129  11042      Erik  * ilms are created / destroyed only as writer. ilms
    130  11042      Erik  * are not passed around. The datapath (anything outside of this file
    131  11042      Erik  * and igmp.c) use functions that do not return ilms - just the number
    132  11042      Erik  * of members. So we don't need a dynamic refcount of the number
    133      0    stevel  * of threads holding reference to an ilm.
    134      0    stevel  *
    135  11042      Erik  * In the cases where we serially access the ilg and ilm, which happens when
    136  11042      Erik  * we handle the applications requests to join or leave groups and sources,
    137  11042      Erik  * we use the ill_mcast_serializer mutex to ensure that a multithreaded
    138  11042      Erik  * application which does concurrent joins and/or leaves on the same group on
    139  11042      Erik  * the same socket always results in a consistent order for the ilg and ilm
    140  11042      Erik  * modifications.
    141      0    stevel  *
    142  11042      Erik  * When a multicast operation results in needing to send a message to
    143  11042      Erik  * the driver (to join/leave a L2 multicast address), we use ill_dlpi_queue()
    144  11042      Erik  * which serialized the DLPI requests. The IGMP/MLD code uses ill_mcast_queue()
    145  11042      Erik  * to send IGMP/MLD IP packet to avoid dropping the lock just to send a packet.
    146      0    stevel  */
    147      0    stevel 
    148      0    stevel #define	GETSTRUCT(structure, number)	\
    149      0    stevel 	((structure *)mi_zalloc(sizeof (structure) * (number)))
    150      0    stevel 
    151  11042      Erik /*
    152  11042      Erik  * Caller must ensure that the ilg has not been condemned
    153  11042      Erik  * The condemned flag is only set in ilg_delete under conn_ilg_lock.
    154  11042      Erik  *
    155  11042      Erik  * The caller must hold conn_ilg_lock as writer.
    156  11042      Erik  */
    157  11042      Erik static void
    158  11042      Erik ilg_refhold(ilg_t *ilg)
    159  11042      Erik {
    160  11042      Erik 	ASSERT(ilg->ilg_refcnt != 0);
    161  11042      Erik 	ASSERT(!ilg->ilg_condemned);
    162  11042      Erik 	ASSERT(RW_WRITE_HELD(&ilg->ilg_connp->conn_ilg_lock));
    163  11042      Erik 
    164  11042      Erik 	ilg->ilg_refcnt++;
    165  11042      Erik }
    166  11042      Erik 
    167  11042      Erik static void
    168  11042      Erik ilg_inactive(ilg_t *ilg)
    169  11042      Erik {
    170  11042      Erik 	ASSERT(ilg->ilg_ill == NULL);
    171  11042      Erik 	ASSERT(ilg->ilg_ilm == NULL);
    172  11042      Erik 	ASSERT(ilg->ilg_filter == NULL);
    173  11042      Erik 	ASSERT(ilg->ilg_condemned);
    174  11042      Erik 
    175  11042      Erik 	/* Unlink from list */
    176  11042      Erik 	*ilg->ilg_ptpn = ilg->ilg_next;
    177  11042      Erik 	if (ilg->ilg_next != NULL)
    178  11042      Erik 		ilg->ilg_next->ilg_ptpn = ilg->ilg_ptpn;
    179  11042      Erik 	ilg->ilg_next = NULL;
    180  11042      Erik 	ilg->ilg_ptpn = NULL;
    181  11042      Erik 
    182  11042      Erik 	ilg->ilg_connp = NULL;
    183  11042      Erik 	kmem_free(ilg, sizeof (*ilg));
    184  11042      Erik }
    185      0    stevel 
    186      0    stevel /*
    187  11042      Erik  * The caller must hold conn_ilg_lock as writer.
    188  11042      Erik  */
    189  11042      Erik static void
    190  11042      Erik ilg_refrele(ilg_t *ilg)
    191  11042      Erik {
    192  11042      Erik 	ASSERT(RW_WRITE_HELD(&ilg->ilg_connp->conn_ilg_lock));
    193  11042      Erik 	ASSERT(ilg->ilg_refcnt != 0);
    194  11042      Erik 	if (--ilg->ilg_refcnt == 0)
    195  11042      Erik 		ilg_inactive(ilg);
    196  11042      Erik }
    197  11042      Erik 
    198  11042      Erik /*
    199  11042      Erik  * Acquire reference on ilg and drop reference on held_ilg.
    200  11042      Erik  * In the case when held_ilg is the same as ilg we already have
    201  11042      Erik  * a reference, but the held_ilg might be condemned. In that case
    202  11042      Erik  * we avoid the ilg_refhold/rele so that we can assert in ire_refhold
    203  11042      Erik  * that the ilg isn't condemned.
    204  11042      Erik  */
    205  11042      Erik static void
    206  11042      Erik ilg_transfer_hold(ilg_t *held_ilg, ilg_t *ilg)
    207  11042      Erik {
    208  11042      Erik 	if (held_ilg == ilg)
    209  11042      Erik 		return;
    210  11042      Erik 
    211  11042      Erik 	ilg_refhold(ilg);
    212  11042      Erik 	if (held_ilg != NULL)
    213  11042      Erik 		ilg_refrele(held_ilg);
    214  11042      Erik }
    215  11042      Erik 
    216  11042      Erik /*
    217  11042      Erik  * Allocate a new ilg_t and links it into conn_ilg.
    218  11042      Erik  * Returns NULL on failure, in which case `*errp' will be
    219   8485     Peter  * filled in with the reason.
    220      0    stevel  *
    221  11042      Erik  * Assumes connp->conn_ilg_lock is held.
    222      0    stevel  */
    223      0    stevel static ilg_t *
    224   8485     Peter conn_ilg_alloc(conn_t *connp, int *errp)
    225      0    stevel {
    226  11042      Erik 	ilg_t *ilg;
    227      0    stevel 
    228  11042      Erik 	ASSERT(RW_WRITE_HELD(&connp->conn_ilg_lock));
    229      0    stevel 
    230   8485     Peter 	/*
    231   8485     Peter 	 * If CONN_CLOSING is set, conn_ilg cleanup has begun and we must not
    232   8485     Peter 	 * create any ilgs.
    233   8485     Peter 	 */
    234   8485     Peter 	if (connp->conn_state_flags & CONN_CLOSING) {
    235   8485     Peter 		*errp = EINVAL;
    236   8485     Peter 		return (NULL);
    237   8485     Peter 	}
    238   8485     Peter 
    239  11042      Erik 	ilg = kmem_zalloc(sizeof (ilg_t), KM_NOSLEEP);
    240  11042      Erik 	if (ilg == NULL) {
    241  11042      Erik 		*errp = ENOMEM;
    242  11042      Erik 		return (NULL);
    243      0    stevel 	}
    244      0    stevel 
    245  11042      Erik 	ilg->ilg_refcnt = 1;
    246  11042      Erik 
    247  11042      Erik 	/* Insert at head */
    248  11042      Erik 	if (connp->conn_ilg != NULL)
    249  11042      Erik 		connp->conn_ilg->ilg_ptpn = &ilg->ilg_next;
    250  11042      Erik 	ilg->ilg_next = connp->conn_ilg;
    251  11042      Erik 	ilg->ilg_ptpn = &connp->conn_ilg;
    252  11042      Erik 	connp->conn_ilg = ilg;
    253  11042      Erik 
    254  11042      Erik 	ilg->ilg_connp = connp;
    255  11042      Erik 	return (ilg);
    256      0    stevel }
    257      0    stevel 
    258      0    stevel typedef struct ilm_fbld_s {
    259      0    stevel 	ilm_t		*fbld_ilm;
    260      0    stevel 	int		fbld_in_cnt;
    261      0    stevel 	int		fbld_ex_cnt;
    262      0    stevel 	slist_t		fbld_in;
    263      0    stevel 	slist_t		fbld_ex;
    264      0    stevel 	boolean_t	fbld_in_overflow;
    265      0    stevel } ilm_fbld_t;
    266      0    stevel 
    267  11042      Erik /*
    268  11042      Erik  * Caller must hold ill_mcast_lock
    269  11042      Erik  */
    270      0    stevel static void
    271  11042      Erik ilm_bld_flists(conn_t *connp, void *arg)
    272      0    stevel {
    273  11042      Erik 	ilg_t *ilg;
    274      0    stevel 	ilm_fbld_t *fbld = (ilm_fbld_t *)(arg);
    275      0    stevel 	ilm_t *ilm = fbld->fbld_ilm;
    276      0    stevel 	in6_addr_t *v6group = &ilm->ilm_v6addr;
    277      0    stevel 
    278  11042      Erik 	if (connp->conn_ilg == NULL)
    279      0    stevel 		return;
    280      0    stevel 
    281      0    stevel 	/*
    282      0    stevel 	 * Since we can't break out of the ipcl_walk once started, we still
    283      0    stevel 	 * have to look at every conn.  But if we've already found one
    284      0    stevel 	 * (EXCLUDE, NULL) list, there's no need to keep checking individual
    285      0    stevel 	 * ilgs--that will be our state.
    286      0    stevel 	 */
    287      0    stevel 	if (fbld->fbld_ex_cnt > 0 && fbld->fbld_ex.sl_numsrc == 0)
    288      0    stevel 		return;
    289      0    stevel 
    290      0    stevel 	/*
    291      0    stevel 	 * Check this conn's ilgs to see if any are interested in our
    292      0    stevel 	 * ilm (group, interface match).  If so, update the master
    293      0    stevel 	 * include and exclude lists we're building in the fbld struct
    294      0    stevel 	 * with this ilg's filter info.
    295  11042      Erik 	 *
    296  11042      Erik 	 * Note that the caller has already serialized on the ill we care
    297  11042      Erik 	 * about.
    298      0    stevel 	 */
    299  11042      Erik 	ASSERT(MUTEX_HELD(&ilm->ilm_ill->ill_mcast_serializer));
    300  11042      Erik 
    301  11042      Erik 	rw_enter(&connp->conn_ilg_lock, RW_READER);
    302  11042      Erik 	for (ilg = connp->conn_ilg; ilg != NULL; ilg = ilg->ilg_next) {
    303  11042      Erik 		if (ilg->ilg_condemned)
    304  11042      Erik 			continue;
    305  11042      Erik 
    306  11042      Erik 		/*
    307  11042      Erik 		 * Since we are under the ill_mcast_serializer we know
    308  11042      Erik 		 * that any ilg+ilm operations on this ilm have either
    309  11042      Erik 		 * not started or completed, except for the last ilg
    310  11042      Erik 		 * (the one that caused us to be called) which doesn't
    311  11042      Erik 		 * have ilg_ilm set yet. Hence we compare using ilg_ill
    312  11042      Erik 		 * and the address.
    313  11042      Erik 		 */
    314      0    stevel 		if ((ilg->ilg_ill == ilm->ilm_ill) &&
    315      0    stevel 		    IN6_ARE_ADDR_EQUAL(&ilg->ilg_v6group, v6group)) {
    316      0    stevel 			if (ilg->ilg_fmode == MODE_IS_INCLUDE) {
    317      0    stevel 				fbld->fbld_in_cnt++;
    318      0    stevel 				if (!fbld->fbld_in_overflow)
    319      0    stevel 					l_union_in_a(&fbld->fbld_in,
    320      0    stevel 					    ilg->ilg_filter,
    321      0    stevel 					    &fbld->fbld_in_overflow);
    322      0    stevel 			} else {
    323      0    stevel 				fbld->fbld_ex_cnt++;
    324      0    stevel 				/*
    325      0    stevel 				 * On the first exclude list, don't try to do
    326      0    stevel 				 * an intersection, as the master exclude list
    327      0    stevel 				 * is intentionally empty.  If the master list
    328      0    stevel 				 * is still empty on later iterations, that
    329      0    stevel 				 * means we have at least one ilg with an empty
    330      0    stevel 				 * exclude list, so that should be reflected
    331      0    stevel 				 * when we take the intersection.
    332      0    stevel 				 */
    333      0    stevel 				if (fbld->fbld_ex_cnt == 1) {
    334      0    stevel 					if (ilg->ilg_filter != NULL)
    335      0    stevel 						l_copy(ilg->ilg_filter,
    336      0    stevel 						    &fbld->fbld_ex);
    337      0    stevel 				} else {
    338      0    stevel 					l_intersection_in_a(&fbld->fbld_ex,
    339      0    stevel 					    ilg->ilg_filter);
    340      0    stevel 				}
    341      0    stevel 			}
    342      0    stevel 			/* there will only be one match, so break now. */
    343      0    stevel 			break;
    344      0    stevel 		}
    345      0    stevel 	}
    346  11042      Erik 	rw_exit(&connp->conn_ilg_lock);
    347      0    stevel }
    348      0    stevel 
    349  11042      Erik /*
    350  11042      Erik  * Caller must hold ill_mcast_lock
    351  11042      Erik  */
    352      0    stevel static void
    353      0    stevel ilm_gen_filter(ilm_t *ilm, mcast_record_t *fmode, slist_t *flist)
    354      0    stevel {
    355      0    stevel 	ilm_fbld_t fbld;
    356   3448  dh155122 	ip_stack_t *ipst = ilm->ilm_ipst;
    357      0    stevel 
    358      0    stevel 	fbld.fbld_ilm = ilm;
    359      0    stevel 	fbld.fbld_in_cnt = fbld.fbld_ex_cnt = 0;
    360      0    stevel 	fbld.fbld_in.sl_numsrc = fbld.fbld_ex.sl_numsrc = 0;
    361      0    stevel 	fbld.fbld_in_overflow = B_FALSE;
    362      0    stevel 
    363      0    stevel 	/* first, construct our master include and exclude lists */
    364   3448  dh155122 	ipcl_walk(ilm_bld_flists, (caddr_t)&fbld, ipst);
    365      0    stevel 
    366      0    stevel 	/* now use those master lists to generate the interface filter */
    367      0    stevel 
    368      0    stevel 	/* if include list overflowed, filter is (EXCLUDE, NULL) */
    369      0    stevel 	if (fbld.fbld_in_overflow) {
    370      0    stevel 		*fmode = MODE_IS_EXCLUDE;
    371      0    stevel 		flist->sl_numsrc = 0;
    372      0    stevel 		return;
    373      0    stevel 	}
    374      0    stevel 
    375      0    stevel 	/* if nobody interested, interface filter is (INCLUDE, NULL) */
    376      0    stevel 	if (fbld.fbld_in_cnt == 0 && fbld.fbld_ex_cnt == 0) {
    377      0    stevel 		*fmode = MODE_IS_INCLUDE;
    378      0    stevel 		flist->sl_numsrc = 0;
    379      0    stevel 		return;
    380      0    stevel 	}
    381      0    stevel 
    382      0    stevel 	/*
    383      0    stevel 	 * If there are no exclude lists, then the interface filter
    384      0    stevel 	 * is INCLUDE, with its filter list equal to fbld_in.  A single
    385      0    stevel 	 * exclude list makes the interface filter EXCLUDE, with its
    386      0    stevel 	 * filter list equal to (fbld_ex - fbld_in).
    387      0    stevel 	 */
    388      0    stevel 	if (fbld.fbld_ex_cnt == 0) {
    389      0    stevel 		*fmode = MODE_IS_INCLUDE;
    390      0    stevel 		l_copy(&fbld.fbld_in, flist);
    391      0    stevel 	} else {
    392      0    stevel 		*fmode = MODE_IS_EXCLUDE;
    393      0    stevel 		l_difference(&fbld.fbld_ex, &fbld.fbld_in, flist);
    394      0    stevel 	}
    395      0    stevel }
    396      0    stevel 
    397  11042      Erik /*
    398  11042      Erik  * Caller must hold ill_mcast_lock
    399  11042      Erik  */
    400      0    stevel static int
    401  11042      Erik ilm_update_add(ilm_t *ilm, ilg_stat_t ilgstat, slist_t *ilg_flist)
    402      0    stevel {
    403      0    stevel 	mcast_record_t fmode;
    404      0    stevel 	slist_t *flist;
    405      0    stevel 	boolean_t fdefault;
    406      0    stevel 	char buf[INET6_ADDRSTRLEN];
    407  11042      Erik 	ill_t *ill = ilm->ilm_ill;
    408      0    stevel 
    409      0    stevel 	/*
    410      0    stevel 	 * There are several cases where the ilm's filter state
    411      0    stevel 	 * defaults to (EXCLUDE, NULL):
    412      0    stevel 	 *	- we've had previous joins without associated ilgs
    413      0    stevel 	 *	- this join has no associated ilg
    414      0    stevel 	 *	- the ilg's filter state is (EXCLUDE, NULL)
    415      0    stevel 	 */
    416      0    stevel 	fdefault = (ilm->ilm_no_ilg_cnt > 0) ||
    417      0    stevel 	    (ilgstat == ILGSTAT_NONE) || SLIST_IS_EMPTY(ilg_flist);
    418      0    stevel 
    419      0    stevel 	/* attempt mallocs (if needed) before doing anything else */
    420      0    stevel 	if ((flist = l_alloc()) == NULL)
    421      0    stevel 		return (ENOMEM);
    422      0    stevel 	if (!fdefault && ilm->ilm_filter == NULL) {
    423      0    stevel 		ilm->ilm_filter = l_alloc();
    424      0    stevel 		if (ilm->ilm_filter == NULL) {
    425      0    stevel 			l_free(flist);
    426      0    stevel 			return (ENOMEM);
    427      0    stevel 		}
    428      0    stevel 	}
    429      0    stevel 
    430      0    stevel 	if (ilgstat != ILGSTAT_CHANGE)
    431      0    stevel 		ilm->ilm_refcnt++;
    432      0    stevel 
    433      0    stevel 	if (ilgstat == ILGSTAT_NONE)
    434      0    stevel 		ilm->ilm_no_ilg_cnt++;
    435      0    stevel 
    436      0    stevel 	/*
    437      0    stevel 	 * Determine new filter state.  If it's not the default
    438      0    stevel 	 * (EXCLUDE, NULL), we must walk the conn list to find
    439      0    stevel 	 * any ilgs interested in this group, and re-build the
    440      0    stevel 	 * ilm filter.
    441      0    stevel 	 */
    442      0    stevel 	if (fdefault) {
    443      0    stevel 		fmode = MODE_IS_EXCLUDE;
    444      0    stevel 		flist->sl_numsrc = 0;
    445      0    stevel 	} else {
    446      0    stevel 		ilm_gen_filter(ilm, &fmode, flist);
    447      0    stevel 	}
    448      0    stevel 
    449      0    stevel 	/* make sure state actually changed; nothing to do if not. */
    450      0    stevel 	if ((ilm->ilm_fmode == fmode) &&
    451      0    stevel 	    !lists_are_different(ilm->ilm_filter, flist)) {
    452      0    stevel 		l_free(flist);
    453      0    stevel 		return (0);
    454      0    stevel 	}
    455      0    stevel 
    456      0    stevel 	/* send the state change report */
    457   4459    kcpoon 	if (!IS_LOOPBACK(ill)) {
    458  11042      Erik 		if (ill->ill_isv6)
    459      0    stevel 			mld_statechange(ilm, fmode, flist);
    460      0    stevel 		else
    461      0    stevel 			igmp_statechange(ilm, fmode, flist);
    462      0    stevel 	}
    463      0    stevel 
    464      0    stevel 	/* update the ilm state */
    465      0    stevel 	ilm->ilm_fmode = fmode;
    466      0    stevel 	if (flist->sl_numsrc > 0)
    467      0    stevel 		l_copy(flist, ilm->ilm_filter);
    468      0    stevel 	else
    469      0    stevel 		CLEAR_SLIST(ilm->ilm_filter);
    470      0    stevel 
    471      0    stevel 	ip1dbg(("ilm_update: new if filter mode %d, group %s\n", ilm->ilm_fmode,
    472      0    stevel 	    inet_ntop(AF_INET6, &ilm->ilm_v6addr, buf, sizeof (buf))));
    473      0    stevel 
    474      0    stevel 	l_free(flist);
    475      0    stevel 	return (0);
    476      0    stevel }
    477      0    stevel 
    478  11042      Erik /*
    479  11042      Erik  * Caller must hold ill_mcast_lock
    480  11042      Erik  */
    481      0    stevel static int
    482  11042      Erik ilm_update_del(ilm_t *ilm)
    483      0    stevel {
    484      0    stevel 	mcast_record_t fmode;
    485      0    stevel 	slist_t *flist;
    486  11042      Erik 	ill_t *ill = ilm->ilm_ill;
    487      0    stevel 
    488      0    stevel 	ip1dbg(("ilm_update_del: still %d left; updating state\n",
    489      0    stevel 	    ilm->ilm_refcnt));
    490      0    stevel 
    491      0    stevel 	if ((flist = l_alloc()) == NULL)
    492      0    stevel 		return (ENOMEM);
    493      0    stevel 
    494      0    stevel 	/*
    495      0    stevel 	 * If present, the ilg in question has already either been
    496      0    stevel 	 * updated or removed from our list; so all we need to do
    497      0    stevel 	 * now is walk the list to update the ilm filter state.
    498      0    stevel 	 *
    499      0    stevel 	 * Skip the list walk if we have any no-ilg joins, which
    500      0    stevel 	 * cause the filter state to revert to (EXCLUDE, NULL).
    501      0    stevel 	 */
    502      0    stevel 	if (ilm->ilm_no_ilg_cnt != 0) {
    503      0    stevel 		fmode = MODE_IS_EXCLUDE;
    504      0    stevel 		flist->sl_numsrc = 0;
    505      0    stevel 	} else {
    506      0    stevel 		ilm_gen_filter(ilm, &fmode, flist);
    507      0    stevel 	}
    508      0    stevel 
    509      0    stevel 	/* check to see if state needs to be updated */
    510      0    stevel 	if ((ilm->ilm_fmode == fmode) &&
    511      0    stevel 	    (!lists_are_different(ilm->ilm_filter, flist))) {
    512      0    stevel 		l_free(flist);
    513      0    stevel 		return (0);
    514      0    stevel 	}
    515      0    stevel 
    516   4459    kcpoon 	if (!IS_LOOPBACK(ill)) {
    517  11042      Erik 		if (ill->ill_isv6)
    518      0    stevel 			mld_statechange(ilm, fmode, flist);
    519      0    stevel 		else
    520      0    stevel 			igmp_statechange(ilm, fmode, flist);
    521      0    stevel 	}
    522      0    stevel 
    523      0    stevel 	ilm->ilm_fmode = fmode;
    524      0    stevel 	if (flist->sl_numsrc > 0) {
    525      0    stevel 		if (ilm->ilm_filter == NULL) {
    526      0    stevel 			ilm->ilm_filter = l_alloc();
    527      0    stevel 			if (ilm->ilm_filter == NULL) {
    528      0    stevel 				char buf[INET6_ADDRSTRLEN];
    529      0    stevel 				ip1dbg(("ilm_update_del: failed to alloc ilm "
    530      0    stevel 				    "filter; no source filtering for %s on %s",
    531      0    stevel 				    inet_ntop(AF_INET6, &ilm->ilm_v6addr,
    532      0    stevel 				    buf, sizeof (buf)), ill->ill_name));
    533      0    stevel 				ilm->ilm_fmode = MODE_IS_EXCLUDE;
    534      0    stevel 				l_free(flist);
    535      0    stevel 				return (0);
    536      0    stevel 			}
    537      0    stevel 		}
    538      0    stevel 		l_copy(flist, ilm->ilm_filter);
    539      0    stevel 	} else {
    540      0    stevel 		CLEAR_SLIST(ilm->ilm_filter);
    541      0    stevel 	}
    542      0    stevel 
    543      0    stevel 	l_free(flist);
    544      0    stevel 	return (0);
    545      0    stevel }
    546      0    stevel 
    547      0    stevel /*
    548  11042      Erik  * Create/update the ilm for the group/ill. Used by other parts of IP to
    549  11042      Erik  * do the ILGSTAT_NONE (no ilg), MODE_IS_EXCLUDE, with no slist join.
    550  11042      Erik  * Returns with a refhold on the ilm.
    551  11042      Erik  *
    552  11042      Erik  * The unspecified address means all multicast addresses for in both the
    553  11042      Erik  * case of IPv4 and IPv6.
    554  11042      Erik  *
    555  11042      Erik  * The caller should have already mapped an IPMP under ill to the upper.
    556      0    stevel  */
    557  11042      Erik ilm_t *
    558  11042      Erik ip_addmulti(const in6_addr_t *v6group, ill_t *ill, zoneid_t zoneid,
    559  11042      Erik     int *errorp)
    560      0    stevel {
    561  11042      Erik 	ilm_t *ilm;
    562      0    stevel 
    563  11042      Erik 	/* Acquire serializer to keep assert in ilm_bld_flists happy */
    564  11042      Erik 	mutex_enter(&ill->ill_mcast_serializer);
    565  11042      Erik 	ilm = ip_addmulti_serial(v6group, ill, zoneid, ILGSTAT_NONE,
    566  11042      Erik 	    MODE_IS_EXCLUDE, NULL, errorp);
    567  11042      Erik 	mutex_exit(&ill->ill_mcast_serializer);
    568  11042      Erik 	return (ilm);
    569      0    stevel }
    570      0    stevel 
    571      0    stevel /*
    572  11042      Erik  * Create/update the ilm for the group/ill. If ILGSTAT_CHANGE is not set
    573  11042      Erik  * then this returns with a refhold on the ilm.
    574   8485     Peter  *
    575  11042      Erik  * Internal routine which assumes the caller has already acquired
    576  11042      Erik  * ill_multi_serializer.
    577  11042      Erik  *
    578  11042      Erik  * The unspecified address means all multicast addresses for in both the
    579  11042      Erik  * case of IPv4 and IPv6.
    580      0    stevel  *
    581      0    stevel  * ilgstat tells us if there's an ilg associated with this join,
    582      0    stevel  * and if so, if it's a new ilg or a change to an existing one.
    583      0    stevel  * ilg_fmode and ilg_flist give us the current filter state of
    584      0    stevel  * the ilg (and will be EXCLUDE {NULL} in the case of no ilg).
    585  11042      Erik  *
    586  11042      Erik  * The caller should have already mapped an IPMP under ill to the upper.
    587      0    stevel  */
    588  11042      Erik static ilm_t *
    589  11042      Erik ip_addmulti_serial(const in6_addr_t *v6group, ill_t *ill, zoneid_t zoneid,
    590  11042      Erik     ilg_stat_t ilgstat, mcast_record_t ilg_fmode, slist_t *ilg_flist,
    591  11042      Erik     int *errorp)
    592  11042      Erik {
    593  11042      Erik 	ilm_t *ilm;
    594  11042      Erik 
    595  11042      Erik 	ASSERT(MUTEX_HELD(&ill->ill_mcast_serializer));
    596  11042      Erik 
    597  11042      Erik 	if (ill->ill_isv6) {
    598  11042      Erik 		if (!IN6_IS_ADDR_MULTICAST(v6group) &&
    599  11042      Erik 		    !IN6_IS_ADDR_UNSPECIFIED(v6group)) {
    600  11042      Erik 			*errorp = EINVAL;
    601  11042      Erik 			return (NULL);
    602  11042      Erik 		}
    603  11042      Erik 	} else {
    604  11042      Erik 		if (IN6_IS_ADDR_V4MAPPED(v6group)) {
    605  11042      Erik 			ipaddr_t v4group;
    606  11042      Erik 
    607  11042      Erik 			IN6_V4MAPPED_TO_IPADDR(v6group, v4group);
    608  11042      Erik 			if (!CLASSD(v4group)) {
    609  11042      Erik 				*errorp = EINVAL;
    610  11042      Erik 				return (NULL);
    611  11042      Erik 			}
    612  11042      Erik 		} else if (!IN6_IS_ADDR_UNSPECIFIED(v6group)) {
    613  11042      Erik 			*errorp = EINVAL;
    614  11042      Erik 			return (NULL);
    615  11042      Erik 		}
    616  11042      Erik 	}
    617  11042      Erik 
    618  11042      Erik 	if (IS_UNDER_IPMP(ill)) {
    619  11042      Erik 		*errorp = EINVAL;
    620  11042      Erik 		return (NULL);
    621  11042      Erik 	}
    622  11042      Erik 
    623  11042      Erik 	rw_enter(&ill->ill_mcast_lock, RW_WRITER);
    624  11042      Erik 	/*
    625  11042      Erik 	 * We do the equivalent of a lookup by checking after we get the lock
    626  11042      Erik 	 * This is needed since the ill could have been condemned after
    627  11042      Erik 	 * we looked it up, and we need to check condemned after we hold
    628  11042      Erik 	 * ill_mcast_lock to synchronize with the unplumb code.
    629  11042      Erik 	 */
    630  11042      Erik 	if (ill->ill_state_flags & ILL_CONDEMNED) {
    631  11042      Erik 		rw_exit(&ill->ill_mcast_lock);
    632  11042      Erik 		*errorp = ENXIO;
    633  11042      Erik 		return (NULL);
    634  11042      Erik 	}
    635  11042      Erik 	ilm = ip_addmulti_impl(v6group, ill, zoneid, ilgstat, ilg_fmode,
    636  11042      Erik 	    ilg_flist, errorp);
    637  11042      Erik 	rw_exit(&ill->ill_mcast_lock);
    638  11042      Erik 
    639  11042      Erik 	/* Send any deferred/queued DLPI or IP packets */
    640  11042      Erik 	ill_mcast_send_queued(ill);
    641  11042      Erik 	ill_dlpi_send_queued(ill);
    642  11042      Erik 	ill_mcast_timer_start(ill->ill_ipst);
    643  11042      Erik 	return (ilm);
    644  11042      Erik }
    645  11042      Erik 
    646  11042      Erik static ilm_t *
    647  11042      Erik ip_addmulti_impl(const in6_addr_t *v6group, ill_t *ill, zoneid_t zoneid,
    648  11042      Erik     ilg_stat_t ilgstat, mcast_record_t ilg_fmode, slist_t *ilg_flist,
    649  11042      Erik     int *errorp)
    650      0    stevel {
    651      0    stevel 	ilm_t	*ilm;
    652  11042      Erik 	int	ret = 0;
    653      0    stevel 
    654  11042      Erik 	ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock));
    655  11042      Erik 	*errorp = 0;
    656   8485     Peter 
    657   8485     Peter 	/*
    658   8485     Peter 	 * An ilm is uniquely identified by the tuple of (group, ill) where
    659   8485     Peter 	 * `group' is the multicast group address, and `ill' is the interface
    660   8485     Peter 	 * on which it is currently joined.
    661   8485     Peter 	 */
    662      0    stevel 
    663  11042      Erik 	ilm = ilm_lookup(ill, v6group, zoneid);
    664  11042      Erik 	if (ilm != NULL) {
    665  11042      Erik 		/* ilm_update_add bumps ilm_refcnt unless ILGSTAT_CHANGE */
    666  11042      Erik 		ret = ilm_update_add(ilm, ilgstat, ilg_flist);
    667  11042      Erik 		if (ret == 0)
    668  11042      Erik 			return (ilm);
    669  11042      Erik 
    670  11042      Erik 		*errorp = ret;
    671  11042      Erik 		return (NULL);
    672  11042      Erik 	}
    673  11042      Erik 
    674  11042      Erik 	/*
    675  11042      Erik 	 * The callers checks on the ilg and the ilg+ilm consistency under
    676  11042      Erik 	 * ill_mcast_serializer ensures that we can not have ILGSTAT_CHANGE
    677  11042      Erik 	 * and no ilm.
    678  11042      Erik 	 */
    679  11042      Erik 	ASSERT(ilgstat != ILGSTAT_CHANGE);
    680  11042      Erik 	ilm = ilm_add(ill, v6group, ilgstat, ilg_fmode, ilg_flist, zoneid);
    681  11042      Erik 	if (ilm == NULL) {
    682  11042      Erik 		*errorp = ENOMEM;
    683  11042      Erik 		return (NULL);
    684  11042      Erik 	}
    685      0    stevel 
    686      0    stevel 	if (IN6_IS_ADDR_UNSPECIFIED(v6group)) {
    687      0    stevel 		/*
    688  11042      Erik 		 * If we have more then one we should not tell the driver
    689  11042      Erik 		 * to join this time.
    690      0    stevel 		 */
    691  11042      Erik 		if (ilm_numentries(ill, v6group) == 1) {
    692  11042      Erik 			ret = ill_join_allmulti(ill);
    693  11042      Erik 		}
    694  11042      Erik 	} else {
    695  11042      Erik 		if (!IS_LOOPBACK(ill)) {
    696  11042      Erik 			if (ill->ill_isv6)
    697  11042      Erik 				mld_joingroup(ilm);
    698  11042      Erik 			else
    699  11042      Erik 				igmp_joingroup(ilm);
    700  11042      Erik 		}
    701  11042      Erik 
    702  11042      Erik 		/*
    703  11042      Erik 		 * If we have more then one we should not tell the driver
    704  11042      Erik 		 * to join this time.
    705  11042      Erik 		 */
    706  11042      Erik 		if (ilm_numentries(ill, v6group) == 1) {
    707  11042      Erik 			ret = ip_ll_multireq(ill, v6group, DL_ENABMULTI_REQ);
    708  11042      Erik 		}
    709      0    stevel 	}
    710  11042      Erik 	if (ret != 0) {
    711  11042      Erik 		if (ret == ENETDOWN) {
    712  11042      Erik 			char buf[INET6_ADDRSTRLEN];
    713      0    stevel 
    714  11042      Erik 			ip0dbg(("ip_addmulti: ENETDOWN for %s on %s",
    715  11042      Erik 			    inet_ntop(AF_INET6, &ilm->ilm_v6addr,
    716  11042      Erik 			    buf, sizeof (buf)), ill->ill_name));
    717  11042      Erik 		}
    718      0    stevel 		ilm_delete(ilm);
    719  11042      Erik 		*errorp = ret;
    720  11042      Erik 		return (NULL);
    721  11042      Erik 	} else {
    722  11042      Erik 		return (ilm);
    723  11042      Erik 	}
    724      0    stevel }
    725      0    stevel 
    726      0    stevel /*
    727  11042      Erik  * Send a multicast request to the driver for enabling or disabling
    728  11042      Erik  * multicast reception for v6groupp address. The caller has already
    729  11042      Erik  * checked whether it is appropriate to send one or not.
    730  11042      Erik  *
    731  11042      Erik  * For IPMP we switch to the cast_ill since it has the right hardware
    732  11042      Erik  * information.
    733   9073     Cathy  */
    734  11042      Erik static int
    735  11042      Erik ip_ll_send_multireq(ill_t *ill, const in6_addr_t *v6groupp, t_uscalar_t prim)
    736      0    stevel {
    737      0    stevel 	mblk_t	*mp;
    738      0    stevel 	uint32_t addrlen, addroff;
    739  11042      Erik 	ill_t *release_ill = NULL;
    740  11042      Erik 	int err = 0;
    741      0    stevel 
    742  11042      Erik 	ASSERT(RW_LOCK_HELD(&ill->ill_mcast_lock));
    743   8485     Peter 
    744  11042      Erik 	if (IS_IPMP(ill)) {
    745  11042      Erik 		/* On the upper IPMP ill. */
    746  11042      Erik 		release_ill = ipmp_illgrp_hold_cast_ill(ill->ill_grp);
    747  11042      Erik 		if (release_ill == NULL) {
    748  11042      Erik 			/*
    749  11042      Erik 			 * Avoid sending it down to the ipmpstub.
    750  11042      Erik 			 * We will be called again once the members of the
    751  11042      Erik 			 * group are in place
    752  11042      Erik 			 */
    753  11042      Erik 			ip1dbg(("ip_ll_send_multireq: no cast_ill for %s %d\n",
    754  11042      Erik 			    ill->ill_name, ill->ill_isv6));
    755  11042      Erik 			return (0);
    756  11042      Erik 		}
    757  11042      Erik 		ill = release_ill;
    758  11042      Erik 	}
    759  11042      Erik 	/* Create a DL_ENABMULTI_REQ or DL_DISABMULTI_REQ message. */
    760  11042      Erik 	mp = ill_create_dl(ill, prim, &addrlen, &addroff);
    761  11042      Erik 	if (mp == NULL) {
    762  11042      Erik 		err = ENOMEM;
    763  11042      Erik 		goto done;
    764  11042      Erik 	}
    765      0    stevel 
    766  11042      Erik 	mp = ndp_mcastreq(ill, v6groupp, addrlen, addroff, mp);
    767  11042      Erik 	if (mp == NULL) {
    768  11042      Erik 		ip0dbg(("null from ndp_mcastreq(ill %s)\n", ill->ill_name));
    769  11042      Erik 		err = ENOMEM;
    770  11042      Erik 		goto done;
    771  11042      Erik 	}
    772   9073     Cathy 
    773  11042      Erik 	switch (((union DL_primitives *)mp->b_rptr)->dl_primitive) {
    774  11042      Erik 	case DL_ENABMULTI_REQ:
    775  11042      Erik 		mutex_enter(&ill->ill_lock);
    776   9073     Cathy 		/* Track the state if this is the first enabmulti */
    777   9073     Cathy 		if (ill->ill_dlpi_multicast_state == IDS_UNKNOWN)
    778   9073     Cathy 			ill->ill_dlpi_multicast_state = IDS_INPROGRESS;
    779  11042      Erik 		mutex_exit(&ill->ill_lock);
    780  11042      Erik 		break;
    781      0    stevel 	}
    782  11042      Erik 	ill_dlpi_queue(ill, mp);
    783  11042      Erik done:
    784  11042      Erik 	if (release_ill != NULL)
    785  11042      Erik 		ill_refrele(release_ill);
    786  11042      Erik 	return (err);
    787      0    stevel }
    788      0    stevel 
    789      0    stevel /*
    790      0    stevel  * Send a multicast request to the driver for enabling multicast
    791      0    stevel  * membership for v6group if appropriate.
    792      0    stevel  */
    793      0    stevel static int
    794  11042      Erik ip_ll_multireq(ill_t *ill, const in6_addr_t *v6groupp, t_uscalar_t prim)
    795      0    stevel {
    796      0    stevel 	if (ill->ill_net_type != IRE_IF_RESOLVER ||
    797  11042      Erik 	    ill->ill_ipif->ipif_flags & IPIF_POINTOPOINT) {
    798  11042      Erik 		ip1dbg(("ip_ll_multireq: not resolver\n"));
    799      0    stevel 		return (0);	/* Must be IRE_IF_NORESOLVER */
    800      0    stevel 	}
    801      0    stevel 
    802      0    stevel 	if (ill->ill_phyint->phyint_flags & PHYI_MULTI_BCAST) {
    803  11042      Erik 		ip1dbg(("ip_ll_multireq: MULTI_BCAST\n"));
    804      0    stevel 		return (0);
    805      0    stevel 	}
    806  11042      Erik 	return (ip_ll_send_multireq(ill, v6groupp, prim));
    807      0    stevel }
    808      0    stevel 
    809      0    stevel /*
    810  11042      Erik  * Delete the ilm. Used by other parts of IP for the case of no_ilg/leaving
    811  11042      Erik  * being true.
    812      0    stevel  */
    813      0    stevel int
    814  11042      Erik ip_delmulti(ilm_t *ilm)
    815      0    stevel {
    816  11042      Erik 	ill_t *ill = ilm->ilm_ill;
    817  11042      Erik 	int error;
    818  11042      Erik 
    819  11042      Erik 	/* Acquire serializer to keep assert in ilm_bld_flists happy */
    820  11042      Erik 	mutex_enter(&ill->ill_mcast_serializer);
    821  11042      Erik 	error = ip_delmulti_serial(ilm, B_TRUE, B_TRUE);
    822  11042      Erik 	mutex_exit(&ill->ill_mcast_serializer);
    823  11042      Erik 	return (error);
    824  11042      Erik }
    825  11042      Erik 
    826  11042      Erik 
    827  11042      Erik /*
    828  11042      Erik  * Delete the ilm.
    829  11042      Erik  * Assumes ill_multi_serializer is held by the caller.
    830  11042      Erik  */
    831  11042      Erik static int
    832  11042      Erik ip_delmulti_serial(ilm_t *ilm, boolean_t no_ilg, boolean_t leaving)
    833  11042      Erik {
    834  11042      Erik 	ill_t *ill = ilm->ilm_ill;
    835  11042      Erik 	int ret;
    836  11042      Erik 
    837  11042      Erik 	ASSERT(MUTEX_HELD(&ill->ill_mcast_serializer));
    838  11042      Erik 	ASSERT(!(IS_UNDER_IPMP(ill)));
    839  11042      Erik 
    840  11042      Erik 	rw_enter(&ill->ill_mcast_lock, RW_WRITER);
    841  11042      Erik 	ret = ip_delmulti_impl(ilm, no_ilg, leaving);
    842  11042      Erik 	rw_exit(&ill->ill_mcast_lock);
    843  11042      Erik 	/* Send any deferred/queued DLPI or IP packets */
    844  11042      Erik 	ill_mcast_send_queued(ill);
    845  11042      Erik 	ill_dlpi_send_queued(ill);
    846  11042      Erik 	ill_mcast_timer_start(ill->ill_ipst);
    847  11042      Erik 
    848  11042      Erik 	return (ret);
    849  11042      Erik }
    850  11042      Erik 
    851  11042      Erik static int
    852  11042      Erik ip_delmulti_impl(ilm_t *ilm, boolean_t no_ilg, boolean_t leaving)
    853  11042      Erik {
    854  11042      Erik 	ill_t *ill = ilm->ilm_ill;
    855  11042      Erik 	int error;
    856      0    stevel 	in6_addr_t v6group;
    857      0    stevel 
    858  11042      Erik 	ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock));
    859      0    stevel 
    860      0    stevel 	/* Update counters */
    861      0    stevel 	if (no_ilg)
    862      0    stevel 		ilm->ilm_no_ilg_cnt--;
    863      0    stevel 
    864      0    stevel 	if (leaving)
    865      0    stevel 		ilm->ilm_refcnt--;
    866      0    stevel 
    867      0    stevel 	if (ilm->ilm_refcnt > 0)
    868  11042      Erik 		return (ilm_update_del(ilm));
    869      0    stevel 
    870  11042      Erik 	v6group = ilm->ilm_v6addr;
    871  11042      Erik 
    872  11042      Erik 	if (IN6_IS_ADDR_UNSPECIFIED(&ilm->ilm_v6addr)) {
    873      0    stevel 		ilm_delete(ilm);
    874      0    stevel 		/*
    875  11042      Erik 		 * If we have some left then one we should not tell the driver
    876  11042      Erik 		 * to leave.
    877      0    stevel 		 */
    878  11042      Erik 		if (ilm_numentries(ill, &v6group) != 0)
    879      0    stevel 			return (0);
    880      0    stevel 
    881  11042      Erik 		ill_leave_allmulti(ill);
    882   8485     Peter 
    883   8023      Phil 		return (0);
    884      0    stevel 	}
    885      0    stevel 
    886  11042      Erik 	if (!IS_LOOPBACK(ill)) {
    887  11042      Erik 		if (ill->ill_isv6)
    888  11042      Erik 			mld_leavegroup(ilm);
    889  11042      Erik 		else
    890  11042      Erik 			igmp_leavegroup(ilm);
    891  11042      Erik 	}
    892      0    stevel 
    893      0    stevel 	ilm_delete(ilm);
    894      0    stevel 	/*
    895  11042      Erik 	 * If we have some left then one we should not tell the driver
    896  11042      Erik 	 * to leave.
    897      0    stevel 	 */
    898  11042      Erik 	if (ilm_numentries(ill, &v6group) != 0)
    899      0    stevel 		return (0);
    900  11042      Erik 
    901  11042      Erik 	error = ip_ll_multireq(ill, &v6group, DL_DISABMULTI_REQ);
    902  11042      Erik 	/* We ignore the case when ill_dl_up is not set */
    903  11042      Erik 	if (error == ENETDOWN) {
    904  11042      Erik 		char buf[INET6_ADDRSTRLEN];
    905  11042      Erik 
    906  11042      Erik 		ip0dbg(("ip_delmulti: ENETDOWN for %s on %s",
    907  11042      Erik 		    inet_ntop(AF_INET6, &v6group, buf, sizeof (buf)),
    908  11042      Erik 		    ill->ill_name));
    909  11042      Erik 	}
    910  11042      Erik 	return (error);
    911      0    stevel }
    912      0    stevel 
    913      0    stevel /*
    914  11042      Erik  * Make the driver pass up all multicast packets.
    915      0    stevel  */
    916      0    stevel int
    917   8023      Phil ill_join_allmulti(ill_t *ill)
    918   8023      Phil {
    919  11042      Erik 	mblk_t		*promiscon_mp, *promiscoff_mp = NULL;
    920      0    stevel 	uint32_t	addrlen, addroff;
    921  11042      Erik 	ill_t		*release_ill = NULL;
    922      0    stevel 
    923  11042      Erik 	ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock));
    924      0    stevel 
    925  11077      Erik 	if (IS_LOOPBACK(ill))
    926  11077      Erik 		return (0);
    927  11077      Erik 
    928   4770      meem 	if (!ill->ill_dl_up) {
    929      0    stevel 		/*
    930      0    stevel 		 * Nobody there. All multicast addresses will be re-joined
    931      0    stevel 		 * when we get the DL_BIND_ACK bringing the interface up.
    932      0    stevel 		 */
    933  11042      Erik 		return (ENETDOWN);
    934      0    stevel 	}
    935      0    stevel 
    936  11042      Erik 	if (IS_IPMP(ill)) {
    937  11042      Erik 		/* On the upper IPMP ill. */
    938  11042      Erik 		release_ill = ipmp_illgrp_hold_cast_ill(ill->ill_grp);
    939  11042      Erik 		if (release_ill == NULL) {
    940  11042      Erik 			/*
    941  11042      Erik 			 * Avoid sending it down to the ipmpstub.
    942  11042      Erik 			 * We will be called again once the members of the
    943  11042      Erik 			 * group are in place
    944  11042      Erik 			 */
    945  11042      Erik 			ip1dbg(("ill_join_allmulti: no cast_ill for %s %d\n",
    946  11042      Erik 			    ill->ill_name, ill->ill_isv6));
    947  11042      Erik 			return (0);
    948  11042      Erik 		}
    949  11042      Erik 		ill = release_ill;
    950  11042      Erik 		if (!ill->ill_dl_up) {
    951  11042      Erik 			ill_refrele(ill);
    952  11042      Erik 			return (ENETDOWN);
    953  11042      Erik 		}
    954  11042      Erik 	}
    955      0    stevel 
    956      0    stevel 	/*
    957   8023      Phil 	 * Create a DL_PROMISCON_REQ message and send it directly to the DLPI
    958   8023      Phil 	 * provider.  We don't need to do this for certain media types for
    959   8023      Phil 	 * which we never need to turn promiscuous mode on.  While we're here,
    960   8023      Phil 	 * pre-allocate a DL_PROMISCOFF_REQ message to make sure that
    961   8023      Phil 	 * ill_leave_allmulti() will not fail due to low memory conditions.
    962      0    stevel 	 */
    963      0    stevel 	if ((ill->ill_net_type == IRE_IF_RESOLVER) &&
    964      0    stevel 	    !(ill->ill_phyint->phyint_flags & PHYI_MULTI_BCAST)) {
    965   8023      Phil 		promiscon_mp = ill_create_dl(ill, DL_PROMISCON_REQ,
    966  11042      Erik 		    &addrlen, &addroff);
    967  11042      Erik 		if (ill->ill_promiscoff_mp == NULL)
    968  11042      Erik 			promiscoff_mp = ill_create_dl(ill, DL_PROMISCOFF_REQ,
    969  11042      Erik 			    &addrlen, &addroff);
    970  11042      Erik 		if (promiscon_mp == NULL ||
    971  11042      Erik 		    (ill->ill_promiscoff_mp == NULL && promiscoff_mp == NULL)) {
    972   8023      Phil 			freemsg(promiscon_mp);
    973   8023      Phil 			freemsg(promiscoff_mp);
    974  11042      Erik 			if (release_ill != NULL)
    975  11042      Erik 				ill_refrele(release_ill);
    976   8023      Phil 			return (ENOMEM);
    977   8023      Phil 		}
    978  11042      Erik 		if (ill->ill_promiscoff_mp == NULL)
    979  11042      Erik 			ill->ill_promiscoff_mp = promiscoff_mp;
    980  11042      Erik 		ill_dlpi_queue(ill, promiscon_mp);
    981      0    stevel 	}
    982  11042      Erik 	if (release_ill != NULL)
    983  11042      Erik 		ill_refrele(release_ill);
    984      0    stevel 	return (0);
    985      0    stevel }
    986      0    stevel 
    987      0    stevel /*
    988      0    stevel  * Make the driver stop passing up all multicast packets
    989      0    stevel  */
    990   8023      Phil void
    991   8023      Phil ill_leave_allmulti(ill_t *ill)
    992   8023      Phil {
    993   8485     Peter 	mblk_t	*promiscoff_mp;
    994  11042      Erik 	ill_t	*release_ill = NULL;
    995   8023      Phil 
    996  11042      Erik 	ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock));
    997  11077      Erik 
    998  11077      Erik 	if (IS_LOOPBACK(ill))
    999  11077      Erik 		return;
   1000      0    stevel 
   1001   4770      meem 	if (!ill->ill_dl_up) {
   1002      0    stevel 		/*
   1003      0    stevel 		 * Nobody there. All multicast addresses will be re-joined
   1004      0    stevel 		 * when we get the DL_BIND_ACK bringing the interface up.
   1005      0    stevel 		 */
   1006   8023      Phil 		return;
   1007      0    stevel 	}
   1008      0    stevel 
   1009  11042      Erik 	if (IS_IPMP(ill)) {
   1010  11042      Erik 		/* On the upper IPMP ill. */
   1011  11042      Erik 		release_ill = ipmp_illgrp_hold_cast_ill(ill->ill_grp);
   1012  11042      Erik 		if (release_ill == NULL) {
   1013  11042      Erik 			/*
   1014  11042      Erik 			 * Avoid sending it down to the ipmpstub.
   1015  11042      Erik 			 * We will be called again once the members of the
   1016  11042      Erik 			 * group are in place
   1017  11042      Erik 			 */
   1018  11042      Erik 			ip1dbg(("ill_leave_allmulti: no cast_ill on %s %d\n",
   1019  11042      Erik 			    ill->ill_name, ill->ill_isv6));
   1020  11042      Erik 			return;
   1021  11042      Erik 		}
   1022  11042      Erik 		ill = release_ill;
   1023  11042      Erik 		if (!ill->ill_dl_up)
   1024  11042      Erik 			goto done;
   1025  11042      Erik 	}
   1026      0    stevel 
   1027      0    stevel 	/*
   1028  11042      Erik 	 * In the case of IPMP and ill_dl_up not being set when we joined
   1029  11042      Erik 	 * we didn't allocate a promiscoff_mp. In that case we have
   1030  11042      Erik 	 * nothing to do when we leave.
   1031  11042      Erik 	 * Ditto for PHYI_MULTI_BCAST
   1032      0    stevel 	 */
   1033  11042      Erik 	promiscoff_mp = ill->ill_promiscoff_mp;
   1034  11042      Erik 	if (promiscoff_mp != NULL) {
   1035   8023      Phil 		ill->ill_promiscoff_mp = NULL;
   1036  11042      Erik 		ill_dlpi_queue(ill, promiscoff_mp);
   1037      0    stevel 	}
   1038  11042      Erik done:
   1039  11042      Erik 	if (release_ill != NULL)
   1040  11042      Erik 		ill_refrele(release_ill);
   1041   8023      Phil }
   1042   8023      Phil 
   1043   8023      Phil int
   1044   8023      Phil ip_join_allmulti(uint_t ifindex, boolean_t isv6, ip_stack_t *ipst)
   1045   8023      Phil {
   1046   8023      Phil 	ill_t		*ill;
   1047  11042      Erik 	int		ret;
   1048  11042      Erik 	ilm_t		*ilm;
   1049   8023      Phil 
   1050  11042      Erik 	ill = ill_lookup_on_ifindex(ifindex, isv6, ipst);
   1051  11042      Erik 	if (ill == NULL)
   1052   8023      Phil 		return (ENODEV);
   1053   8485     Peter 
   1054   8485     Peter 	/*
   1055  11042      Erik 	 * The ip_addmulti() function doesn't allow IPMP underlying interfaces
   1056   8485     Peter 	 * to join allmulti since only the nominated underlying interface in
   1057   8485     Peter 	 * the group should receive multicast.  We silently succeed to avoid
   1058   8485     Peter 	 * having to teach IPobs (currently the only caller of this routine)
   1059   8485     Peter 	 * to ignore failures in this case.
   1060   8485     Peter 	 */
   1061  11042      Erik 	if (IS_UNDER_IPMP(ill)) {
   1062  11042      Erik 		ill_refrele(ill);
   1063  11042      Erik 		return (0);
   1064  11042      Erik 	}
   1065  11042      Erik 	mutex_enter(&ill->ill_lock);
   1066  11042      Erik 	if (ill->ill_ipallmulti_cnt > 0) {
   1067  11042      Erik 		/* Already joined */
   1068  11042      Erik 		ASSERT(ill->ill_ipallmulti_ilm != NULL);
   1069  11042      Erik 		ill->ill_ipallmulti_cnt++;
   1070  11042      Erik 		mutex_exit(&ill->ill_lock);
   1071  11042      Erik 		goto done;
   1072  11042      Erik 	}
   1073  11042      Erik 	mutex_exit(&ill->ill_lock);
   1074   8485     Peter 
   1075  11042      Erik 	ilm = ip_addmulti(&ipv6_all_zeros, ill, ill->ill_zoneid, &ret);
   1076  11042      Erik 	if (ilm == NULL) {
   1077  11042      Erik 		ASSERT(ret != 0);
   1078  11042      Erik 		ill_refrele(ill);
   1079  11042      Erik 		return (ret);
   1080   8023      Phil 	}
   1081  11042      Erik 
   1082  11042      Erik 	mutex_enter(&ill->ill_lock);
   1083  11042      Erik 	if (ill->ill_ipallmulti_cnt > 0) {
   1084  11042      Erik 		/* Another thread added it concurrently */
   1085  11042      Erik 		(void) ip_delmulti(ilm);
   1086  11042      Erik 		mutex_exit(&ill->ill_lock);
   1087  11042      Erik 		goto done;
   1088  11042      Erik 	}
   1089  11042      Erik 	ASSERT(ill->ill_ipallmulti_ilm == NULL);
   1090  11042      Erik 	ill->ill_ipallmulti_ilm = ilm;
   1091   8023      Phil 	ill->ill_ipallmulti_cnt++;
   1092  11042      Erik 	mutex_exit(&ill->ill_lock);
   1093  11042      Erik done:
   1094  11042      Erik 	ill_refrele(ill);
   1095  11042      Erik 	return (0);
   1096   8023      Phil }
   1097   8485     Peter 
   1098   8023      Phil int
   1099   8023      Phil ip_leave_allmulti(uint_t ifindex, boolean_t isv6, ip_stack_t *ipst)
   1100   8023      Phil {
   1101   8023      Phil 	ill_t		*ill;
   1102  11042      Erik 	ilm_t		*ilm;
   1103   8023      Phil 
   1104  11042      Erik 	ill = ill_lookup_on_ifindex(ifindex, isv6, ipst);
   1105  11042      Erik 	if (ill == NULL)
   1106   8023      Phil 		return (ENODEV);
   1107   8485     Peter 
   1108  11042      Erik 	if (IS_UNDER_IPMP(ill)) {
   1109  11042      Erik 		ill_refrele(ill);
   1110  11042      Erik 		return (0);
   1111   8485     Peter 	}
   1112  11042      Erik 
   1113  11042      Erik 	mutex_enter(&ill->ill_lock);
   1114  11042      Erik 	if (ill->ill_ipallmulti_cnt == 0) {
   1115  11042      Erik 		/* ip_purge_allmulti could have removed them all */
   1116  11042      Erik 		mutex_exit(&ill->ill_lock);
   1117  11042      Erik 		goto done;
   1118  11042      Erik 	}
   1119  11042      Erik 	ill->ill_ipallmulti_cnt--;
   1120  11042      Erik 	if (ill->ill_ipallmulti_cnt == 0) {
   1121  11042      Erik 		/* Last one */
   1122  11042      Erik 		ilm = ill->ill_ipallmulti_ilm;
   1123  11042      Erik 		ill->ill_ipallmulti_ilm = NULL;
   1124  11042      Erik 	} else {
   1125  11042      Erik 		ilm = NULL;
   1126  11042      Erik 	}
   1127  11042      Erik 	mutex_exit(&ill->ill_lock);
   1128  11042      Erik 	if (ilm != NULL)
   1129  11042      Erik 		(void) ip_delmulti(ilm);
   1130  11042      Erik 
   1131  11042      Erik done:
   1132  11042      Erik 	ill_refrele(ill);
   1133   8023      Phil 	return (0);
   1134   8023      Phil }
   1135   8023      Phil 
   1136   8023      Phil /*
   1137   8023      Phil  * Delete the allmulti memberships that were added as part of
   1138   8023      Phil  * ip_join_allmulti().
   1139   8023      Phil  */
   1140   8023      Phil void
   1141   8023      Phil ip_purge_allmulti(ill_t *ill)
   1142   8023      Phil {
   1143  11042      Erik 	ilm_t	*ilm;
   1144  11042      Erik 
   1145   8023      Phil 	ASSERT(IAM_WRITER_ILL(ill));
   1146   8023      Phil 
   1147  11042      Erik 	mutex_enter(&ill->ill_lock);
   1148  11042      Erik 	ilm = ill->ill_ipallmulti_ilm;
   1149  11042      Erik 	ill->ill_ipallmulti_ilm = NULL;
   1150  11042      Erik 	ill->ill_ipallmulti_cnt = 0;
   1151  11042      Erik 	mutex_exit(&ill->ill_lock);
   1152  11042      Erik 
   1153  11042      Erik 	if (ilm != NULL)
   1154  11042      Erik 		(void) ip_delmulti(ilm);
   1155      0    stevel }
   1156      0    stevel 
   1157      0    stevel /*
   1158  11042      Erik  * Create a dlpi message with room for phys+sap. Later
   1159  11042      Erik  * we will strip the sap for those primitives which
   1160  11042      Erik  * only need a physical address.
   1161      0    stevel  */
   1162      0    stevel static mblk_t *
   1163  11042      Erik ill_create_dl(ill_t *ill, uint32_t dl_primitive,
   1164      0    stevel     uint32_t *addr_lenp, uint32_t *addr_offp)
   1165      0    stevel {
   1166      0    stevel 	mblk_t	*mp;
   1167      0    stevel 	uint32_t	hw_addr_length;
   1168      0    stevel 	char		*cp;
   1169      0    stevel 	uint32_t	offset;
   1170  11042      Erik 	uint32_t	length;
   1171      0    stevel 	uint32_t 	size;
   1172      0    stevel 
   1173      0    stevel 	*addr_lenp = *addr_offp = 0;
   1174      0    stevel 
   1175      0    stevel 	hw_addr_length = ill->ill_phys_addr_length;
   1176      0    stevel 	if (!hw_addr_length) {
   1177      0    stevel 		ip0dbg(("ip_create_dl: hw addr length = 0\n"));
   1178      0    stevel 		return (NULL);
   1179      0    stevel 	}
   1180      0    stevel 
   1181      0    stevel 	switch (dl_primitive) {
   1182      0    stevel 	case DL_ENABMULTI_REQ:
   1183  11042      Erik 		length = sizeof (dl_enabmulti_req_t);
   1184  11042      Erik 		size = length + hw_addr_length;
   1185  11042      Erik 		break;
   1186      0    stevel 	case DL_DISABMULTI_REQ:
   1187  11042      Erik 		length = sizeof (dl_disabmulti_req_t);
   1188  11042      Erik 		size = length + hw_addr_length;
   1189      0    stevel 		break;
   1190      0    stevel 	case DL_PROMISCON_REQ:
   1191      0    stevel 	case DL_PROMISCOFF_REQ:
   1192  11042      Erik 		size = length = sizeof (dl_promiscon_req_t);
   1193      0    stevel 		break;
   1194      0    stevel 	default:
   1195      0    stevel 		return (NULL);
   1196      0    stevel 	}
   1197      0    stevel 	mp = allocb(size, BPRI_HI);
   1198      0    stevel 	if (!mp)
   1199      0    stevel 		return (NULL);
   1200      0    stevel 	mp->b_wptr += size;
   1201      0    stevel 	mp->b_datap->db_type = M_PROTO;
   1202      0    stevel 
   1203      0    stevel 	cp = (char *)mp->b_rptr;
   1204      0    stevel 	offset = length;
   1205      0    stevel 
   1206      0    stevel 	switch (dl_primitive) {
   1207      0    stevel 	case DL_ENABMULTI_REQ: {
   1208      0    stevel 		dl_enabmulti_req_t *dl = (dl_enabmulti_req_t *)cp;
   1209      0    stevel 
   1210      0    stevel 		dl->dl_primitive = dl_primitive;
   1211      0    stevel 		dl->dl_addr_offset = offset;
   1212      0    stevel 		*addr_lenp = dl->dl_addr_length = hw_addr_length;
   1213      0    stevel 		*addr_offp = offset;
   1214      0    stevel 		break;
   1215      0    stevel 	}
   1216      0    stevel 	case DL_DISABMULTI_REQ: {
   1217      0    stevel 		dl_disabmulti_req_t *dl = (dl_disabmulti_req_t *)cp;
   1218      0    stevel 
   1219      0    stevel 		dl->dl_primitive = dl_primitive;
   1220      0    stevel 		dl->dl_addr_offset = offset;
   1221      0    stevel 		*addr_lenp = dl->dl_addr_length = hw_addr_length;
   1222      0    stevel 		*addr_offp = offset;
   1223      0    stevel 		break;
   1224      0    stevel 	}
   1225      0    stevel 	case DL_PROMISCON_REQ:
   1226      0    stevel 	case DL_PROMISCOFF_REQ: {
   1227      0    stevel 		dl_promiscon_req_t *dl = (dl_promiscon_req_t *)cp;
   1228      0    stevel 
   1229      0    stevel 		dl->dl_primitive = dl_primitive;
   1230      0    stevel 		dl->dl_level = DL_PROMISC_MULTI;
   1231      0    stevel 		break;
   1232      0    stevel 	}
   1233      0    stevel 	}
   1234      0    stevel 	ip1dbg(("ill_create_dl: addr_len %d, addr_off %d\n",
   1235   4459    kcpoon 	    *addr_lenp, *addr_offp));
   1236      0    stevel 	return (mp);
   1237      0    stevel }
   1238      0    stevel 
   1239      0    stevel /*
   1240  11042      Erik  * Rejoin any groups for which we have ilms.
   1241  11042      Erik  *
   1242  11042      Erik  * This is only needed for IPMP when the cast_ill changes since that
   1243  11042      Erik  * change is invisible to the ilm. Other interface changes are handled
   1244  11042      Erik  * by conn_update_ill.
   1245      0    stevel  */
   1246      0    stevel void
   1247      0    stevel ill_recover_multicast(ill_t *ill)
   1248      0    stevel {
   1249      0    stevel 	ilm_t	*ilm;
   1250      0    stevel 	char    addrbuf[INET6_ADDRSTRLEN];
   1251   8023      Phil 
   1252   8023      Phil 	ill->ill_need_recover_multicast = 0;
   1253   8023      Phil 
   1254  11042      Erik 	rw_enter(&ill->ill_mcast_lock, RW_WRITER);
   1255      0    stevel 	for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
   1256      0    stevel 		/*
   1257  11042      Erik 		 * If we have more then one ilm for the group (e.g., with
   1258  11042      Erik 		 * different zoneid) then we should not tell the driver
   1259  11042      Erik 		 * to join unless this is the first ilm for the group.
   1260      0    stevel 		 */
   1261  11042      Erik 		if (ilm_numentries(ill, &ilm->ilm_v6addr) > 1 &&
   1262  11042      Erik 		    ilm_lookup(ill, &ilm->ilm_v6addr, ALL_ZONES) != ilm) {
   1263   8485     Peter 			continue;
   1264   8485     Peter 		}
   1265   8485     Peter 
   1266   8485     Peter 		ip1dbg(("ill_recover_multicast: %s\n", inet_ntop(AF_INET6,
   1267   8485     Peter 		    &ilm->ilm_v6addr, addrbuf, sizeof (addrbuf))));
   1268   8485     Peter 
   1269      0    stevel 		if (IN6_IS_ADDR_UNSPECIFIED(&ilm->ilm_v6addr)) {
   1270   8485     Peter 			(void) ill_join_allmulti(ill);
   1271   8485     Peter 		} else {
   1272   8485     Peter 			if (ill->ill_isv6)
   1273   8485     Peter 				mld_joingroup(ilm);
   1274   8485     Peter 			else
   1275   8485     Peter 				igmp_joingroup(ilm);
   1276   8485     Peter 
   1277  11042      Erik 			(void) ip_ll_multireq(ill, &ilm->ilm_v6addr,
   1278  11042      Erik 			    DL_ENABMULTI_REQ);
   1279   8485     Peter 		}
   1280   8485     Peter 	}
   1281  11042      Erik 	rw_exit(&ill->ill_mcast_lock);
   1282  11042      Erik 	/* Send any deferred/queued DLPI or IP packets */
   1283  11042      Erik 	ill_mcast_send_queued(ill);
   1284  11042      Erik 	ill_dlpi_send_queued(ill);
   1285  11042      Erik 	ill_mcast_timer_start(ill->ill_ipst);
   1286      0    stevel }
   1287      0    stevel 
   1288      0    stevel /*
   1289      0    stevel  * The opposite of ill_recover_multicast() -- leaves all multicast groups
   1290   8485     Peter  * that were explicitly joined.
   1291  11042      Erik  *
   1292  11042      Erik  * This is only needed for IPMP when the cast_ill changes since that
   1293  11042      Erik  * change is invisible to the ilm. Other interface changes are handled
   1294  11042      Erik  * by conn_update_ill.
   1295      0    stevel  */
   1296      0    stevel void
   1297      0    stevel ill_leave_multicast(ill_t *ill)
   1298      0    stevel {
   1299      0    stevel 	ilm_t	*ilm;
   1300      0    stevel 	char    addrbuf[INET6_ADDRSTRLEN];
   1301   8023      Phil 
   1302   8023      Phil 	ill->ill_need_recover_multicast = 1;
   1303   8023      Phil 
   1304  11042      Erik 	rw_enter(&ill->ill_mcast_lock, RW_WRITER);
   1305      0    stevel 	for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
   1306      0    stevel 		/*
   1307  11042      Erik 		 * If we have more then one ilm for the group (e.g., with
   1308  11042      Erik 		 * different zoneid) then we should not tell the driver
   1309  11042      Erik 		 * to leave unless this is the first ilm for the group.
   1310      0    stevel 		 */
   1311  11042      Erik 		if (ilm_numentries(ill, &ilm->ilm_v6addr) > 1 &&
   1312  11042      Erik 		    ilm_lookup(ill, &ilm->ilm_v6addr, ALL_ZONES) != ilm) {
   1313   8485     Peter 			continue;
   1314   8485     Peter 		}
   1315   8485     Peter 
   1316   8485     Peter 		ip1dbg(("ill_leave_multicast: %s\n", inet_ntop(AF_INET6,
   1317   8485     Peter 		    &ilm->ilm_v6addr, addrbuf, sizeof (addrbuf))));
   1318   8485     Peter 
   1319      0    stevel 		if (IN6_IS_ADDR_UNSPECIFIED(&ilm->ilm_v6addr)) {
   1320   8023      Phil 			ill_leave_allmulti(ill);
   1321   8485     Peter 		} else {
   1322   8485     Peter 			if (ill->ill_isv6)
   1323   8485     Peter 				mld_leavegroup(ilm);
   1324   8485     Peter 			else
   1325   8485     Peter 				igmp_leavegroup(ilm);
   1326   8485     Peter 
   1327  11042      Erik 			(void) ip_ll_multireq(ill, &ilm->ilm_v6addr,
   1328  11042      Erik 			    DL_DISABMULTI_REQ);
   1329   8485     Peter 		}
   1330   8485     Peter 	}
   1331  11042      Erik 	rw_exit(&ill->ill_mcast_lock);
   1332  11042      Erik 	/* Send any deferred/queued DLPI or IP packets */
   1333  11042      Erik 	ill_mcast_send_queued(ill);
   1334  11042      Erik 	ill_dlpi_send_queued(ill);
   1335  11042      Erik 	ill_mcast_timer_start(ill->ill_ipst);
   1336   4770      meem }
   1337      0    stevel 
   1338  11042      Erik /*
   1339  11042      Erik  * Interface used by IP input/output.
   1340  11042      Erik  * Returns true if there is a member on the ill for any zoneid.
   1341  11042      Erik  */
   1342  11042      Erik boolean_t
   1343  11042      Erik ill_hasmembers_v6(ill_t *ill, const in6_addr_t *v6group)
   1344  11042      Erik {
   1345  11042      Erik 	ilm_t		*ilm;
   1346  11042      Erik 
   1347  11042      Erik 	rw_enter(&ill->ill_mcast_lock, RW_READER);
   1348  11042      Erik 	ilm = ilm_lookup(ill, v6group, ALL_ZONES);
   1349  11042      Erik 	rw_exit(&ill->ill_mcast_lock);
   1350  11042      Erik 	return (ilm != NULL);
   1351  11042      Erik }
   1352  11042      Erik 
   1353  11042      Erik /*
   1354  11042      Erik  * Interface used by IP input/output.
   1355  11042      Erik  * Returns true if there is a member on the ill for any zoneid.
   1356  11042      Erik  *
   1357  11042      Erik  * The group and source can't be INADDR_ANY here so no need to translate to
   1358  11042      Erik  * the unspecified IPv6 address.
   1359  11042      Erik  */
   1360  11042      Erik boolean_t
   1361  11042      Erik ill_hasmembers_v4(ill_t *ill, ipaddr_t group)
   1362      0    stevel {
   1363      0    stevel 	in6_addr_t	v6group;
   1364      0    stevel 
   1365  11042      Erik 	IN6_IPADDR_TO_V4MAPPED(group, &v6group);
   1366  11042      Erik 	return (ill_hasmembers_v6(ill, &v6group));
   1367   8485     Peter }
   1368   8485     Peter 
   1369   8485     Peter /*
   1370  11042      Erik  * Interface used by IP input/output.
   1371  11042      Erik  * Returns true if there is a member on the ill for any zoneid except skipzone.
   1372   8485     Peter  */
   1373  11042      Erik boolean_t
   1374  11042      Erik ill_hasmembers_otherzones_v6(ill_t *ill, const in6_addr_t *v6group,
   1375  11042      Erik     zoneid_t skipzone)
   1376  11042      Erik {
   1377  11042      Erik 	ilm_t		*ilm;
   1378  11042      Erik 
   1379  11042      Erik 	rw_enter(&ill->ill_mcast_lock, RW_READER);
   1380  11042      Erik 	for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
   1381  11042      Erik 		if (IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, v6group) &&
   1382  11042      Erik 		    ilm->ilm_zoneid != skipzone) {
   1383  11042      Erik 			rw_exit(&ill->ill_mcast_lock);
   1384  11042      Erik 			return (B_TRUE);
   1385  11042      Erik 		}
   1386  11042      Erik 	}
   1387  11042      Erik 	rw_exit(&ill->ill_mcast_lock);
   1388  11042      Erik 	return (B_FALSE);
   1389  11042      Erik }
   1390  11042      Erik 
   1391  11042      Erik /*
   1392  11042      Erik  * Interface used by IP input/output.
   1393  11042      Erik  * Returns true if there is a member on the ill for any zoneid except skipzone.
   1394  11042      Erik  *
   1395  11042      Erik  * The group and source can't be INADDR_ANY here so no need to translate to
   1396  11042      Erik  * the unspecified IPv6 address.
   1397  11042      Erik  */
   1398  11042      Erik boolean_t
   1399  11042      Erik ill_hasmembers_otherzones_v4(ill_t *ill, ipaddr_t group, zoneid_t skipzone)
   1400  11042      Erik {
   1401  11042      Erik 	in6_addr_t	v6group;
   1402  11042      Erik 
   1403  11042      Erik 	IN6_IPADDR_TO_V4MAPPED(group, &v6group);
   1404  11042      Erik 	return (ill_hasmembers_otherzones_v6(ill, &v6group, skipzone));
   1405  11042      Erik }
   1406  11042      Erik 
   1407  11042      Erik /*
   1408  11042      Erik  * Interface used by IP input.
   1409  11042      Erik  * Returns the next numerically larger zoneid that has a member. If none exist
   1410  11042      Erik  * then returns -1 (ALL_ZONES).
   1411  11042      Erik  * The normal usage is for the caller to start with a -1 zoneid (ALL_ZONES)
   1412  11042      Erik  * to find the first zoneid which has a member, and then pass that in for
   1413  11042      Erik  * subsequent calls until ALL_ZONES is returned.
   1414  11042      Erik  *
   1415  11042      Erik  * The implementation of ill_hasmembers_nextzone() assumes the ilms
   1416  11042      Erik  * are sorted by zoneid for efficiency.
   1417  11042      Erik  */
   1418  11042      Erik zoneid_t
   1419  11042      Erik ill_hasmembers_nextzone_v6(ill_t *ill, const in6_addr_t *v6group,
   1420  11042      Erik     zoneid_t zoneid)
   1421  11042      Erik {
   1422  11042      Erik 	ilm_t		*ilm;
   1423  11042      Erik 
   1424  11042      Erik 	rw_enter(&ill->ill_mcast_lock, RW_READER);
   1425  11042      Erik 	for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
   1426  11042      Erik 		if (IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, v6group) &&
   1427  11042      Erik 		    ilm->ilm_zoneid > zoneid) {
   1428  11042      Erik 			zoneid = ilm->ilm_zoneid;
   1429  11042      Erik 			rw_exit(&ill->ill_mcast_lock);
   1430  11042      Erik 			return (zoneid);
   1431  11042      Erik 		}
   1432  11042      Erik 	}
   1433  11042      Erik 	rw_exit(&ill->ill_mcast_lock);
   1434  11042      Erik 	return (ALL_ZONES);
   1435  11042      Erik }
   1436  11042      Erik 
   1437  11042      Erik /*
   1438  11042      Erik  * Interface used by IP input.
   1439  11042      Erik  * Returns the next numerically larger zoneid that has a member. If none exist
   1440  11042      Erik  * then returns -1 (ALL_ZONES).
   1441  11042      Erik  *
   1442  11042      Erik  * The group and source can't be INADDR_ANY here so no need to translate to
   1443  11042      Erik  * the unspecified IPv6 address.
   1444  11042      Erik  */
   1445  11042      Erik zoneid_t
   1446  11042      Erik ill_hasmembers_nextzone_v4(ill_t *ill, ipaddr_t group, zoneid_t zoneid)
   1447  11042      Erik {
   1448  11042      Erik 	in6_addr_t	v6group;
   1449  11042      Erik 
   1450  11042      Erik 	IN6_IPADDR_TO_V4MAPPED(group, &v6group);
   1451  11042      Erik 
   1452  11042      Erik 	return (ill_hasmembers_nextzone_v6(ill, &v6group, zoneid));
   1453  11042      Erik }
   1454  11042      Erik 
   1455  11042      Erik /*
   1456  11042      Erik  * Find an ilm matching the ill, group, and zoneid.
   1457  11042      Erik  */
   1458  11042      Erik static ilm_t *
   1459  11042      Erik ilm_lookup(ill_t *ill, const in6_addr_t *v6group, zoneid_t zoneid)
   1460   8485     Peter {
   1461   8485     Peter 	ilm_t	*ilm;
   1462   8485     Peter 
   1463  11042      Erik 	ASSERT(RW_LOCK_HELD(&ill->ill_mcast_lock));
   1464   8485     Peter 
   1465  11042      Erik 	for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
   1466   8485     Peter 		if (!IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, v6group))
   1467   8485     Peter 			continue;
   1468   8485     Peter 		if (zoneid != ALL_ZONES && zoneid != ilm->ilm_zoneid)
   1469   8485     Peter 			continue;
   1470  11042      Erik 
   1471  11042      Erik 		ASSERT(ilm->ilm_ill == ill);
   1472  11042      Erik 		return (ilm);
   1473   8485     Peter 	}
   1474  11042      Erik 	return (NULL);
   1475      0    stevel }
   1476      0    stevel 
   1477      0    stevel /*
   1478      0    stevel  * How many members on this ill?
   1479  11042      Erik  * Since each shared-IP zone has a separate ilm for the same group/ill
   1480  11042      Erik  * we can have several.
   1481      0    stevel  */
   1482  11042      Erik static int
   1483  11042      Erik ilm_numentries(ill_t *ill, const in6_addr_t *v6group)
   1484      0    stevel {
   1485      0    stevel 	ilm_t	*ilm;
   1486      0    stevel 	int i = 0;
   1487      0    stevel 
   1488  11042      Erik 	ASSERT(RW_LOCK_HELD(&ill->ill_mcast_lock));
   1489      0    stevel 	for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
   1490      0    stevel 		if (IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, v6group)) {
   1491      0    stevel 			i++;
   1492      0    stevel 		}
   1493      0    stevel 	}
   1494      0    stevel 	return (i);
   1495      0    stevel }
   1496      0    stevel 
   1497      0    stevel /* Caller guarantees that the group is not already on the list */
   1498      0    stevel static ilm_t *
   1499  11042      Erik ilm_add(ill_t *ill, const in6_addr_t *v6group, ilg_stat_t ilgstat,
   1500   8485     Peter     mcast_record_t ilg_fmode, slist_t *ilg_flist, zoneid_t zoneid)
   1501      0    stevel {
   1502      0    stevel 	ilm_t	*ilm;
   1503      0    stevel 	ilm_t	*ilm_cur;
   1504      0    stevel 	ilm_t	**ilm_ptpn;
   1505      0    stevel 
   1506  11042      Erik 	ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock));
   1507      0    stevel 	ilm = GETSTRUCT(ilm_t, 1);
   1508      0    stevel 	if (ilm == NULL)
   1509      0    stevel 		return (NULL);
   1510      0    stevel 	if (ilgstat != ILGSTAT_NONE && !SLIST_IS_EMPTY(ilg_flist)) {
   1511      0    stevel 		ilm->ilm_filter = l_alloc();
   1512      0    stevel 		if (ilm->ilm_filter == NULL) {
   1513      0    stevel 			mi_free(ilm);
   1514      0    stevel 			return (NULL);
   1515      0    stevel 		}
   1516      0    stevel 	}
   1517      0    stevel 	ilm->ilm_v6addr = *v6group;
   1518      0    stevel 	ilm->ilm_refcnt = 1;
   1519      0    stevel 	ilm->ilm_zoneid = zoneid;
   1520      0    stevel 	ilm->ilm_timer = INFINITY;
   1521      0    stevel 	ilm->ilm_rtx.rtx_timer = INFINITY;
   1522   1676       jpk 
   1523  11042      Erik 	ilm->ilm_ill = ill;
   1524  11042      Erik 	DTRACE_PROBE3(ill__incr__cnt, (ill_t *), ill,
   1525  11042      Erik 	    (char *), "ilm", (void *), ilm);
   1526  11042      Erik 	ill->ill_ilm_cnt++;
   1527   8485     Peter 
   1528   3448  dh155122 	ASSERT(ill->ill_ipst);
   1529   3448  dh155122 	ilm->ilm_ipst = ill->ill_ipst;	/* No netstack_hold */
   1530      0    stevel 
   1531  11042      Erik 	/* The ill/ipif could have just been marked as condemned */
   1532      0    stevel 
   1533      0    stevel 	/*
   1534  11042      Erik 	 * To make ill_hasmembers_nextzone_v6 work we keep the list
   1535  11042      Erik 	 * sorted by zoneid.
   1536      0    stevel 	 */
   1537      0    stevel 	ilm_cur = ill->ill_ilm;
   1538      0    stevel 	ilm_ptpn = &ill->ill_ilm;
   1539  11042      Erik 	while (ilm_cur != NULL && ilm_cur->ilm_zoneid < ilm->ilm_zoneid) {
   1540      0    stevel 		ilm_ptpn = &ilm_cur->ilm_next;
   1541      0    stevel 		ilm_cur = ilm_cur->ilm_next;
   1542      0    stevel 	}
   1543      0    stevel 	ilm->ilm_next = ilm_cur;
   1544      0    stevel 	*ilm_ptpn = ilm;
   1545      0    stevel 
   1546      0    stevel 	/*
   1547      0    stevel 	 * If we have an associated ilg, use its filter state; if not,
   1548      0    stevel 	 * default to (EXCLUDE, NULL) and set no_ilg_cnt to track this.
   1549      0    stevel 	 */
   1550      0    stevel 	if (ilgstat != ILGSTAT_NONE) {
   1551      0    stevel 		if (!SLIST_IS_EMPTY(ilg_flist))
   1552      0    stevel 			l_copy(ilg_flist, ilm->ilm_filter);
   1553      0    stevel 		ilm->ilm_fmode = ilg_fmode;
   1554      0    stevel 	} else {
   1555      0    stevel 		ilm->ilm_no_ilg_cnt = 1;
   1556      0    stevel 		ilm->ilm_fmode = MODE_IS_EXCLUDE;
   1557      0    stevel 	}
   1558      0    stevel 
   1559      0    stevel 	return (ilm);
   1560      0    stevel }
   1561      0    stevel 
   1562   6763   sowmini void
   1563   6255   sowmini ilm_inactive(ilm_t *ilm)
   1564   6255   sowmini {
   1565      0    stevel 	FREE_SLIST(ilm->ilm_filter);
   1566      0    stevel 	FREE_SLIST(ilm->ilm_pendsrcs);
   1567      0    stevel 	FREE_SLIST(ilm->ilm_rtx.rtx_allow);
   1568      0    stevel 	FREE_SLIST(ilm->ilm_rtx.rtx_block);
   1569   3448  dh155122 	ilm->ilm_ipst = NULL;
   1570      0    stevel 	mi_free((char *)ilm);
   1571      0    stevel }
   1572      0    stevel 
   1573   6255   sowmini /*
   1574   6255   sowmini  * Unlink ilm and free it.
   1575   6255   sowmini  */
   1576   6255   sowmini static void
   1577   6255   sowmini ilm_delete(ilm_t *ilm)
   1578   6255   sowmini {
   1579  11042      Erik 	ill_t		*ill = ilm->ilm_ill;
   1580   6255   sowmini 	ilm_t		**ilmp;
   1581   6255   sowmini 	boolean_t	need_wakeup;
   1582   6255   sowmini 
   1583   6255   sowmini 	/*
   1584   6255   sowmini 	 * Delete under lock protection so that readers don't stumble
   1585   6255   sowmini 	 * on bad ilm_next
   1586   6255   sowmini 	 */
   1587  11042      Erik 	ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock));
   1588   6255   sowmini 
   1589   6255   sowmini 	for (ilmp = &ill->ill_ilm; *ilmp != ilm; ilmp = &(*ilmp)->ilm_next)
   1590  11042      Erik 		;
   1591  11042      Erik 
   1592   6255   sowmini 	*ilmp = ilm->ilm_next;
   1593   6255   sowmini 
   1594  11042      Erik 	mutex_enter(&ill->ill_lock);
   1595   6255   sowmini 	/*
   1596  11042      Erik 	 * if we are the last reference to the ill, we may need to wakeup any
   1597  11042      Erik 	 * pending FREE or unplumb operations. This is because conn_update_ill
   1598  11042      Erik 	 * bails if there is a ilg_delete_all in progress.
   1599   6255   sowmini 	 */
   1600   6255   sowmini 	need_wakeup = B_FALSE;
   1601  11042      Erik 	DTRACE_PROBE3(ill__decr__cnt, (ill_t *), ill,
   1602  11042      Erik 	    (char *), "ilm", (void *), ilm);
   1603  11042      Erik 	ASSERT(ill->ill_ilm_cnt > 0);
   1604  11042      Erik 	ill->ill_ilm_cnt--;
   1605  11042      Erik 	if (ILL_FREE_OK(ill))
   1606  11042      Erik 		need_wakeup = B_TRUE;
   1607   6255   sowmini 
   1608   6255   sowmini 	ilm_inactive(ilm); /* frees this ilm */
   1609   6255   sowmini 
   1610   6255   sowmini 	if (need_wakeup) {
   1611   6255   sowmini 		/* drops ill lock */
   1612   6255   sowmini 		ipif_ill_refrele_tail(ill);
   1613   6255   sowmini 	} else {
   1614   6255   sowmini 		mutex_exit(&ill->ill_lock);
   1615   6255   sowmini 	}
   1616   6255   sowmini }
   1617   6255   sowmini 
   1618  11042      Erik /*
   1619  11042      Erik  * Lookup an ill based on the group, ifindex, ifaddr, and zoneid.
   1620  11042      Erik  * Applies to both IPv4 and IPv6, although ifaddr is only used with
   1621  11042      Erik  * IPv4.
   1622  11042      Erik  * Returns an error for IS_UNDER_IPMP and VNI interfaces.
   1623  11042      Erik  * On error it sets *errorp.
   1624  11042      Erik  */
   1625  11042      Erik static ill_t *
   1626  11042      Erik ill_mcast_lookup(const in6_addr_t *group, ipaddr_t ifaddr, uint_t ifindex,
   1627  11042      Erik     zoneid_t zoneid, ip_stack_t *ipst, int *errorp)
   1628   8485     Peter {
   1629  11042      Erik 	ill_t *ill;
   1630  11042      Erik 	ipaddr_t v4group;
   1631   8485     Peter 
   1632  11042      Erik 	if (IN6_IS_ADDR_V4MAPPED(group)) {
   1633  11042      Erik 		IN6_V4MAPPED_TO_IPADDR(group, v4group);
   1634  11042      Erik 
   1635  11042      Erik 		if (ifindex != 0) {
   1636  11042      Erik 			ill = ill_lookup_on_ifindex_zoneid(ifindex, zoneid,
   1637  11042      Erik 			    B_FALSE, ipst);
   1638  11042      Erik 		} else if (ifaddr != INADDR_ANY) {
   1639  11042      Erik 			ipif_t *ipif;
   1640  11042      Erik 
   1641  11042      Erik 			ipif = ipif_lookup_addr(ifaddr, NULL, zoneid, ipst);
   1642  11042      Erik 			if (ipif == NULL) {
   1643  11042      Erik 				ill = NULL;
   1644  11042      Erik 			} else {
   1645  11042      Erik 				ill = ipif->ipif_ill;
   1646  11042      Erik 				ill_refhold(ill);
   1647  11042      Erik 				ipif_refrele(ipif);
   1648  11042      Erik 			}
   1649  11042      Erik 		} else {
   1650  11042      Erik 			ill = ill_lookup_group_v4(v4group, zoneid, ipst, NULL,
   1651  11042      Erik 			    NULL);
   1652  11042      Erik 		}
   1653  11042      Erik 	} else {
   1654  11042      Erik 		if (ifindex != 0) {
   1655  11042      Erik 			ill = ill_lookup_on_ifindex_zoneid(ifindex, zoneid,
   1656  11042      Erik 			    B_TRUE, ipst);
   1657  11042      Erik 		} else {
   1658  11042      Erik 			ill = ill_lookup_group_v6(group, zoneid, ipst, NULL,
   1659  11042      Erik 			    NULL);
   1660  11042      Erik 		}
   1661  11042      Erik 	}
   1662  11042      Erik 	if (ill == NULL) {
   1663  11042      Erik 		if (ifindex != 0)
   1664  11042      Erik 			*errorp = ENXIO;
   1665  11042      Erik 		else
   1666  11042      Erik 			*errorp = EADDRNOTAVAIL;
   1667  11042      Erik 		return (NULL);
   1668  11042      Erik 	}
   1669  11042      Erik 	/* operation not supported on the virtual network interface */
   1670  11042      Erik 	if (IS_UNDER_IPMP(ill) || IS_VNI(ill)) {
   1671  11042      Erik 		ill_refrele(ill);
   1672  11042      Erik 		*errorp = EINVAL;
   1673  11042      Erik 		return (NULL);
   1674  11042      Erik 	}
   1675  11042      Erik 	return (ill);
   1676   8485     Peter }
   1677   8485     Peter 
   1678   8485     Peter /*
   1679  11042      Erik  * Looks up the appropriate ill given an interface index (or interface address)
   1680  11042      Erik  * and multicast group.  On success, returns 0, with *illpp pointing to the
   1681  11042      Erik  * found struct.  On failure, returns an errno and *illpp is set to NULL.
   1682  11042      Erik  *
   1683  11042      Erik  * Returns an error for IS_UNDER_IPMP and VNI interfaces.
   1684  11042      Erik  *
   1685  11042      Erik  * Handles both IPv4 and IPv6. The ifaddr argument only applies in the
   1686  11042      Erik  * case of IPv4.
   1687      0    stevel  */
   1688      0    stevel int
   1689  11042      Erik ip_opt_check(conn_t *connp, const in6_addr_t *v6group,
   1690  11042      Erik     const in6_addr_t *v6src, ipaddr_t ifaddr, uint_t ifindex, ill_t **illpp)
   1691      0    stevel {
   1692      0    stevel 	boolean_t src_unspec;
   1693      0    stevel 	ill_t *ill = NULL;
   1694   3448  dh155122 	ip_stack_t *ipst = connp->conn_netstack->netstack_ip;
   1695  11042      Erik 	int error = 0;
   1696  11042      Erik 
   1697  11042      Erik 	*illpp = NULL;
   1698      0    stevel 
   1699      0    stevel 	src_unspec = IN6_IS_ADDR_UNSPECIFIED(v6src);
   1700      0    stevel 
   1701      0    stevel 	if (IN6_IS_ADDR_V4MAPPED(v6group)) {
   1702  11042      Erik 		ipaddr_t v4group;
   1703  11042      Erik 		ipaddr_t v4src;
   1704  11042      Erik 
   1705      0    stevel 		if (!IN6_IS_ADDR_V4MAPPED(v6src) && !src_unspec)
   1706      0    stevel 			return (EINVAL);
   1707  11042      Erik 		IN6_V4MAPPED_TO_IPADDR(v6group, v4group);
   1708      0    stevel 		if (src_unspec) {
   1709  11042      Erik 			v4src = INADDR_ANY;
   1710      0    stevel 		} else {
   1711  11042      Erik 			IN6_V4MAPPED_TO_IPADDR(v6src, v4src);
   1712      0    stevel 		}
   1713  11042      Erik 		if (!CLASSD(v4group) || CLASSD(v4src))
   1714      0    stevel 			return (EINVAL);
   1715      0    stevel 	} else {
   1716      0    stevel 		if (IN6_IS_ADDR_V4MAPPED(v6src) && !src_unspec)
   1717      0    stevel 			return (EINVAL);
   1718      0    stevel 		if (!IN6_IS_ADDR_MULTICAST(v6group) ||
   1719      0    stevel 		    IN6_IS_ADDR_MULTICAST(v6src)) {
   1720      0    stevel 			return (EINVAL);
   1721      0    stevel 		}
   1722      0    stevel 	}
   1723      0    stevel 
   1724  11042      Erik 	ill = ill_mcast_lookup(v6group, ifaddr, ifindex, IPCL_ZONEID(connp),
   1725  11042      Erik 	    ipst, &error);
   1726      0    stevel 	*illpp = ill;
   1727  11042      Erik 	return (error);
   1728      0    stevel }
   1729      0    stevel 
   1730      0    stevel static int
   1731      0    stevel ip_get_srcfilter(conn_t *connp, struct group_filter *gf,
   1732  11042      Erik     struct ip_msfilter *imsf, const struct in6_addr *group, boolean_t issin6)
   1733      0    stevel {
   1734      0    stevel 	ilg_t *ilg;
   1735      0    stevel 	int i, numsrc, fmode, outsrcs;
   1736      0    stevel 	struct sockaddr_in *sin;
   1737      0    stevel 	struct sockaddr_in6 *sin6;
   1738      0    stevel 	struct in_addr *addrp;
   1739      0    stevel 	slist_t *fp;
   1740      0    stevel 	boolean_t is_v4only_api;
   1741  11042      Erik 	ipaddr_t ifaddr;
   1742  11042      Erik 	uint_t ifindex;
   1743      0    stevel 
   1744      0    stevel 	if (gf == NULL) {
   1745      0    stevel 		ASSERT(imsf != NULL);
   1746  11042      Erik 		ASSERT(!issin6);
   1747      0    stevel 		is_v4only_api = B_TRUE;
   1748      0    stevel 		outsrcs = imsf->imsf_numsrc;
   1749  11042      Erik 		ifaddr = imsf->imsf_interface.s_addr;
   1750  11042      Erik 		ifindex = 0;
   1751      0    stevel 	} else {
   1752      0    stevel 		ASSERT(imsf == NULL);
   1753      0    stevel 		is_v4only_api = B_FALSE;
   1754      0    stevel 		outsrcs = gf->gf_numsrc;
   1755  11042      Erik 		ifaddr = INADDR_ANY;
   1756  11042      Erik 		ifindex = gf->gf_interface;
   1757  11042      Erik 	}
   1758  11042      Erik 
   1759  11042      Erik 	/* No need to use ill_mcast_serializer for the reader */
   1760  11042      Erik 	rw_enter(&connp->conn_ilg_lock, RW_READER);
   1761  11042      Erik 	ilg = ilg_lookup(connp, group, ifaddr, ifindex);
   1762  11042      Erik 	if (ilg == NULL) {
   1763  11042      Erik 		rw_exit(&connp->conn_ilg_lock);
   1764  11042      Erik 		return (EADDRNOTAVAIL);
   1765      0    stevel 	}
   1766      0    stevel 
   1767      0    stevel 	/*
   1768      0    stevel 	 * In the kernel, we use the state definitions MODE_IS_[IN|EX]CLUDE
   1769      0    stevel 	 * to identify the filter mode; but the API uses MCAST_[IN|EX]CLUDE.
   1770      0    stevel 	 * So we need to translate here.
   1771      0    stevel 	 */
   1772      0    stevel 	fmode = (ilg->ilg_fmode == MODE_IS_INCLUDE) ?
   1773      0    stevel 	    MCAST_INCLUDE : MCAST_EXCLUDE;
   1774      0    stevel 	if ((fp = ilg->ilg_filter) == NULL) {
   1775      0    stevel 		numsrc = 0;
   1776      0    stevel 	} else {
   1777      0    stevel 		for (i = 0; i < outsrcs; i++) {
   1778      0    stevel 			if (i == fp->sl_numsrc)
   1779      0    stevel 				break;
   1780  11042      Erik 			if (issin6) {
   1781      0    stevel 				sin6 = (struct sockaddr_in6 *)&gf->gf_slist[i];
   1782      0    stevel 				sin6->sin6_family = AF_INET6;
   1783      0    stevel 				sin6->sin6_addr = fp->sl_addr[i];
   1784      0    stevel 			} else {
   1785      0    stevel 				if (is_v4only_api) {
   1786      0    stevel 					addrp = &imsf->imsf_slist[i];
   1787      0    stevel 				} else {
   1788      0    stevel 					sin = (struct sockaddr_in *)
   1789      0    stevel 					    &gf->gf_slist[i];
   1790      0    stevel 					sin->sin_family = AF_INET;
   1791      0    stevel 					addrp = &sin->sin_addr;
   1792      0    stevel 				}
   1793      0    stevel 				IN6_V4MAPPED_TO_INADDR(&fp->sl_addr[i], addrp);
   1794      0    stevel 			}
   1795      0    stevel 		}
   1796      0    stevel 		numsrc = fp->sl_numsrc;
   1797      0    stevel 	}
   1798      0    stevel 
   1799      0    stevel 	if (is_v4only_api) {
   1800      0    stevel 		imsf->imsf_numsrc = numsrc;
   1801      0    stevel 		imsf->imsf_fmode = fmode;
   1802      0    stevel 	} else {
   1803      0    stevel 		gf->gf_numsrc = numsrc;
   1804      0    stevel 		gf->gf_fmode = fmode;
   1805      0    stevel 	}
   1806      0    stevel 
   1807  11042      Erik 	rw_exit(&connp->conn_ilg_lock);
   1808      0    stevel 
   1809      0    stevel 	return (0);
   1810      0    stevel }
   1811      0    stevel 
   1812  11042      Erik /*
   1813  11042      Erik  * Common for IPv4 and IPv6.
   1814  11042      Erik  */
   1815      0    stevel static int
   1816      0    stevel ip_set_srcfilter(conn_t *connp, struct group_filter *gf,
   1817  11042      Erik     struct ip_msfilter *imsf, const struct in6_addr *group, ill_t *ill,
   1818  11042      Erik     boolean_t issin6)
   1819      0    stevel {
   1820      0    stevel 	ilg_t *ilg;
   1821   6827       blu 	int i, err, infmode, new_fmode;
   1822   6827       blu 	uint_t insrcs;
   1823      0    stevel 	struct sockaddr_in *sin;
   1824      0    stevel 	struct sockaddr_in6 *sin6;
   1825      0    stevel 	struct in_addr *addrp;
   1826      0    stevel 	slist_t *orig_filter = NULL;
   1827      0    stevel 	slist_t *new_filter = NULL;
   1828      0    stevel 	mcast_record_t orig_fmode;
   1829  11042      Erik 	boolean_t leave_group, is_v4only_api;
   1830      0    stevel 	ilg_stat_t ilgstat;
   1831  11042      Erik 	ilm_t *ilm;
   1832  11042      Erik 	ipaddr_t ifaddr;
   1833  11042      Erik 	uint_t ifindex;
   1834      0    stevel 
   1835      0    stevel 	if (gf == NULL) {
   1836      0    stevel 		ASSERT(imsf != NULL);
   1837  11042      Erik 		ASSERT(!issin6);
   1838      0    stevel 		is_v4only_api = B_TRUE;
   1839      0    stevel 		insrcs = imsf->imsf_numsrc;
   1840      0    stevel 		infmode = imsf->imsf_fmode;
   1841  11042      Erik 		ifaddr = imsf->imsf_interface.s_addr;
   1842  11042      Erik 		ifindex = 0;
   1843      0    stevel 	} else {
   1844      0    stevel 		ASSERT(imsf == NULL);
   1845      0    stevel 		is_v4only_api = B_FALSE;
   1846      0    stevel 		insrcs = gf->gf_numsrc;
   1847      0    stevel 		infmode = gf->gf_fmode;
   1848  11042      Erik 		ifaddr = INADDR_ANY;
   1849  11042      Erik 		ifindex = gf->gf_interface;
   1850      0    stevel 	}
   1851      0    stevel 
   1852      0    stevel 	/* Make sure we can handle the source list */
   1853      0    stevel 	if (insrcs > MAX_FILTER_SIZE)
   1854      0    stevel 		return (ENOBUFS);
   1855      0    stevel 
   1856      0    stevel 	/*
   1857      0    stevel 	 * setting the filter to (INCLUDE, NULL) is treated
   1858      0    stevel 	 * as a request to leave the group.
   1859      0    stevel 	 */
   1860  11042      Erik 	leave_group = (infmode == MCAST_INCLUDE && insrcs == 0);
   1861      0    stevel 
   1862  11042      Erik 	mutex_enter(&ill->ill_mcast_serializer);
   1863  11042      Erik 	rw_enter(&connp->conn_ilg_lock, RW_WRITER);
   1864  11042      Erik 	ilg = ilg_lookup(connp, group, ifaddr, ifindex);
   1865      0    stevel 	if (ilg == NULL) {
   1866      0    stevel 		/*
   1867      0    stevel 		 * if the request was actually to leave, and we
   1868      0    stevel 		 * didn't find an ilg, there's nothing to do.
   1869      0    stevel 		 */
   1870  11042      Erik 		if (leave_group) {
   1871  11042      Erik 			rw_exit(&connp->conn_ilg_lock);
   1872  11042      Erik 			mutex_exit(&ill->ill_mcast_serializer);
   1873  11042      Erik 			return (0);
   1874  11042      Erik 		}
   1875  11042      Erik 		ilg = conn_ilg_alloc(connp, &err);
   1876  11042      Erik 		if (ilg == NULL) {
   1877  11042      Erik 			rw_exit(&connp->conn_ilg_lock);
   1878  11042      Erik 			mutex_exit(&ill->ill_mcast_serializer);
   1879  11042      Erik 			return (err);
   1880      0    stevel 		}
   1881      0    stevel 		ilgstat = ILGSTAT_NEW;
   1882  11042      Erik 		ilg->ilg_v6group = *group;
   1883  11042      Erik 		ilg->ilg_ill = ill;
   1884  11042      Erik 		ilg->ilg_ifaddr = ifaddr;
   1885  11042      Erik 		ilg->ilg_ifindex = ifindex;
   1886  11042      Erik 	} else if (leave_group) {
   1887  11042      Erik 		/*
   1888  11042      Erik 		 * Make sure we have the correct serializer. The ill argument
   1889  11042      Erik 		 * might not match ilg_ill.
   1890  11042      Erik 		 */
   1891  11042      Erik 		ilg_refhold(ilg);
   1892  11042      Erik 		mutex_exit(&ill->ill_mcast_serializer);
   1893  11042      Erik 		ill = ilg->ilg_ill;
   1894  11042      Erik 		rw_exit(&connp->conn_ilg_lock);
   1895  11042      Erik 
   1896  11042      Erik 		mutex_enter(&ill->ill_mcast_serializer);
   1897  11042      Erik 		rw_enter(&connp->conn_ilg_lock, RW_WRITER);
   1898  11042      Erik 		ilm = ilg->ilg_ilm;
   1899  11042      Erik 		ilg->ilg_ilm = NULL;
   1900      0    stevel 		ilg_delete(connp, ilg, NULL);
   1901  11042      Erik 		ilg_refrele(ilg);
   1902  11042      Erik 		rw_exit(&connp->conn_ilg_lock);
   1903  11042      Erik 		if (ilm != NULL)
   1904  11042      Erik 			(void) ip_delmulti_serial(ilm, B_FALSE, B_TRUE);
   1905  11042      Erik 		mutex_exit(&ill->ill_mcast_serializer);
   1906      0    stevel 		return (0);
   1907      0    stevel 	} else {
   1908      0    stevel 		ilgstat = ILGSTAT_CHANGE;
   1909      0    stevel 		/* Preserve existing state in case ip_addmulti() fails */
   1910      0    stevel 		orig_fmode = ilg->ilg_fmode;
   1911      0    stevel 		if (ilg->ilg_filter == NULL) {
   1912      0    stevel 			orig_filter = NULL;
   1913      0    stevel 		} else {
   1914      0    stevel 			orig_filter = l_alloc_copy(ilg->ilg_filter);
   1915      0    stevel 			if (orig_filter == NULL) {
   1916  11042      Erik 				rw_exit(&connp->conn_ilg_lock);
   1917  11042      Erik 				mutex_exit(&ill->ill_mcast_serializer);
   1918      0    stevel 				return (ENOMEM);
   1919      0    stevel 			}
   1920      0    stevel 		}
   1921      0    stevel 	}
   1922      0    stevel 
   1923      0    stevel 	/*
   1924      0    stevel 	 * Alloc buffer to copy new state into (see below) before
   1925      0    stevel 	 * we make any changes, so we can bail if it fails.
   1926      0    stevel 	 */
   1927      0    stevel 	if ((new_filter = l_alloc()) == NULL) {
   1928  11042      Erik 		rw_exit(&connp->conn_ilg_lock);
   1929      0    stevel 		err = ENOMEM;
   1930      0    stevel 		goto free_and_exit;
   1931      0    stevel 	}
   1932      0    stevel 
   1933      0    stevel 	if (insrcs == 0) {
   1934      0    stevel 		CLEAR_SLIST(ilg->ilg_filter);
   1935      0    stevel 	} else {
   1936      0    stevel 		slist_t *fp;
   1937      0    stevel 		if (ilg->ilg_filter == NULL) {
   1938      0    stevel 			fp = l_alloc();
   1939      0    stevel 			if (fp == NULL) {
   1940      0    stevel 				if (ilgstat == ILGSTAT_NEW)
   1941      0    stevel 					ilg_delete(connp, ilg, NULL);
   1942  11042      Erik 				rw_exit(&connp->conn_ilg_lock);
   1943      0    stevel 				err = ENOMEM;
   1944      0    stevel 				goto free_and_exit;
   1945      0    stevel 			}
   1946      0    stevel 		} else {
   1947      0    stevel 			fp = ilg->ilg_filter;
   1948      0    stevel 		}
   1949      0    stevel 		for (i = 0; i < insrcs; i++) {
   1950  11042      Erik 			if (issin6) {
   1951      0    stevel 				sin6 = (struct sockaddr_in6 *)&gf->gf_slist[i];
   1952      0    stevel 				fp->sl_addr[i] = sin6->sin6_addr;
   1953      0    stevel 			} else {
   1954      0    stevel 				if (is_v4only_api) {
   1955      0    stevel 					addrp = &imsf->imsf_slist[i];
   1956      0    stevel 				} else {
   1957      0    stevel 					sin = (struct sockaddr_in *)
   1958      0    stevel 					    &gf->gf_slist[i];
   1959      0    stevel 					addrp = &sin->sin_addr;
   1960      0    stevel 				}
   1961      0    stevel 				IN6_INADDR_TO_V4MAPPED(addrp, &fp->sl_addr[i]);
   1962      0    stevel 			}
   1963      0    stevel 		}
   1964      0    stevel 		fp->sl_numsrc = insrcs;
   1965      0    stevel 		ilg->ilg_filter = fp;
   1966      0    stevel 	}
   1967      0    stevel 	/*
   1968      0    stevel 	 * In the kernel, we use the state definitions MODE_IS_[IN|EX]CLUDE
   1969      0    stevel 	 * to identify the filter mode; but the API uses MCAST_[IN|EX]CLUDE.
   1970      0    stevel 	 * So we need to translate here.
   1971      0    stevel 	 */
   1972      0    stevel 	ilg->ilg_fmode = (infmode == MCAST_INCLUDE) ?
   1973   4459    kcpoon 	    MODE_IS_INCLUDE : MODE_IS_EXCLUDE;
   1974      0    stevel 
   1975      0    stevel 	/*
   1976      0    stevel 	 * Save copy of ilg's filter state to pass to other functions,
   1977  11042      Erik 	 * so we can release conn_ilg_lock now.
   1978      0    stevel 	 */
   1979      0    stevel 	new_fmode = ilg->ilg_fmode;
   1980      0    stevel 	l_copy(ilg->ilg_filter, new_filter);
   1981      0    stevel 
   1982  11042      Erik 	rw_exit(&connp->conn_ilg_lock);
   1983      0    stevel 
   1984  11042      Erik 	/*
   1985  11042      Erik 	 * Now update the ill. We wait to do this until after the ilg
   1986  11042      Erik 	 * has been updated because we need to update the src filter
   1987  11042      Erik 	 * info for the ill, which involves looking at the status of
   1988  11042      Erik 	 * all the ilgs associated with this group/interface pair.
   1989  11042      Erik 	 */
   1990  11042      Erik 	ilm = ip_addmulti_serial(group, ill, connp->conn_zoneid, ilgstat,
   1991  11042      Erik 	    new_fmode, new_filter, &err);
   1992  11042      Erik 
   1993  11042      Erik 	rw_enter(&connp->conn_ilg_lock, RW_WRITER);
   1994  11042      Erik 	/*
   1995  11042      Erik 	 * Must look up the ilg again since we've not been holding
   1996  11042      Erik 	 * conn_ilg_lock. The ilg could have disappeared due to an unplumb
   1997  11042      Erik 	 * having called conn_update_ill, which can run once we dropped the
   1998  11042      Erik 	 * conn_ilg_lock above.
   1999  11042      Erik 	 */
   2000  11042      Erik 	ilg = ilg_lookup(connp, group, ifaddr, ifindex);
   2001  11042      Erik 	if (ilg == NULL) {
   2002  11042      Erik 		rw_exit(&connp->conn_ilg_lock);
   2003  11042      Erik 		if (ilm != NULL) {
   2004  11042      Erik 			(void) ip_delmulti_serial(ilm, B_FALSE,
   2005  11042      Erik 			    (ilgstat == ILGSTAT_NEW));
   2006  11042      Erik 		}
   2007  11042      Erik 		err = ENXIO;
   2008  11042      Erik 		goto free_and_exit;
   2009  11042      Erik 	}
   2010  11042      Erik 
   2011  11042      Erik 	if (ilm != NULL) {
   2012  11042      Erik 		/* Succeeded. Update the ilg to point at the ilm */
   2013  11042      Erik 		if (ilgstat == ILGSTAT_NEW) {
   2014  11042      Erik 			ASSERT(ilg->ilg_ilm == NULL);
   2015  11042      Erik 			ilg->ilg_ilm = ilm;
   2016  11042      Erik 			ilm->ilm_ifaddr = ifaddr;	/* For netstat */
   2017  11042      Erik 		} else {
   2018  11042      Erik 			/*
   2019  11042      Erik 			 * ip_addmulti didn't get a held ilm for
   2020  11042      Erik 			 * ILGSTAT_CHANGE; ilm_refcnt was unchanged.
   2021  11042      Erik 			 */
   2022  11042      Erik 			ASSERT(ilg->ilg_ilm == ilm);
   2023  11042      Erik 		}
   2024  11042      Erik 	} else {
   2025  11042      Erik 		ASSERT(err != 0);
   2026      0    stevel 		/*
   2027  11042      Erik 		 * Failed to allocate the ilm.
   2028      0    stevel 		 * Restore the original filter state, or delete the
   2029  11042      Erik 		 * newly-created ilg.
   2030  11042      Erik 		 * If ENETDOWN just clear ill_ilg since so that we
   2031  11042      Erik 		 * will rejoin when the ill comes back; don't report ENETDOWN
   2032  11042      Erik 		 * to application.
   2033      0    stevel 		 */
   2034      0    stevel 		if (ilgstat == ILGSTAT_NEW) {
   2035  11042      Erik 			if (err == ENETDOWN) {
   2036  11042      Erik 				ilg->ilg_ill = NULL;
   2037  11042      Erik 				err = 0;
   2038  11042      Erik 			} else {
   2039  11042      Erik 				ilg_delete(connp, ilg, NULL);
   2040  11042      Erik 			}
   2041      0    stevel 		} else {
   2042      0    stevel 			ilg->ilg_fmode = orig_fmode;
   2043      0    stevel 			if (SLIST_IS_EMPTY(orig_filter)) {
   2044      0    stevel 				CLEAR_SLIST(ilg->ilg_filter);
   2045      0    stevel 			} else {
   2046      0    stevel 				/*
   2047      0    stevel 				 * We didn't free the filter, even if we
   2048      0    stevel 				 * were trying to make the source list empty;
   2049      0    stevel 				 * so if orig_filter isn't empty, the ilg
   2050      0    stevel 				 * must still have a filter alloc'd.
   2051      0    stevel 				 */
   2052      0    stevel 				l_copy(orig_filter, ilg->ilg_filter);
   2053      0    stevel 			}
   2054      0    stevel 		}
   2055      0    stevel 	}
   2056  11042      Erik 	rw_exit(&connp->conn_ilg_lock);
   2057      0    stevel 
   2058      0    stevel free_and_exit:
   2059  11042      Erik 	mutex_exit(&ill->ill_mcast_serializer);
   2060      0    stevel 	l_free(orig_filter);
   2061      0    stevel 	l_free(new_filter);
   2062      0    stevel 
   2063      0    stevel 	return (err);
   2064      0    stevel }
   2065      0    stevel 
   2066      0    stevel /*
   2067      0    stevel  * Process the SIOC[GS]MSFILTER and SIOC[GS]IPMSFILTER ioctls.
   2068      0    stevel  */
   2069      0    stevel /* ARGSUSED */
   2070      0    stevel int
   2071      0    stevel ip_sioctl_msfilter(ipif_t *ipif, sin_t *dummy_sin, queue_t *q, mblk_t *mp,
   2072      0    stevel     ip_ioctl_cmd_t *ipip, void *ifreq)
   2073      0    stevel {
   2074      0    stevel 	struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
   2075      0    stevel 	/* existence verified in ip_wput_nondata() */
   2076      0    stevel 	mblk_t *data_mp = mp->b_cont->b_cont;
   2077      0    stevel 	int datalen, err, cmd, minsize;
   2078   6827       blu 	uint_t expsize = 0;
   2079      0    stevel 	conn_t *connp;
   2080      0    stevel 	boolean_t isv6, is_v4only_api, getcmd;
   2081      0    stevel 	struct sockaddr_in *gsin;
   2082      0    stevel 	struct sockaddr_in6 *gsin6;
   2083  11042      Erik 	ipaddr_t v4group;
   2084  11042      Erik 	in6_addr_t v6group;
   2085      0    stevel 	struct group_filter *gf = NULL;
   2086      0    stevel 	struct ip_msfilter *imsf = NULL;
   2087      0    stevel 	mblk_t *ndp;
   2088  11042      Erik 	ill_t *ill;
   2089  11042      Erik 
   2090  11042      Erik 	connp = Q_TO_CONN(q);
   2091  11042      Erik 	err = ip_msfilter_ill(connp, mp, ipip, &ill);
   2092  11042      Erik 	if (err != 0)
   2093  11042      Erik 		return (err);
   2094      0    stevel 
   2095      0    stevel 	if (data_mp->b_cont != NULL) {
   2096      0    stevel 		if ((ndp = msgpullup(data_mp, -1)) == NULL)
   2097      0    stevel 			return (ENOMEM);
   2098      0    stevel 		freemsg(data_mp);
   2099      0    stevel 		data_mp = ndp;
   2100      0    stevel 		mp->b_cont->b_cont = data_mp;
   2101      0    stevel 	}
   2102      0    stevel 
   2103      0    stevel 	cmd = iocp->ioc_cmd;
   2104      0    stevel 	getcmd = (cmd == SIOCGIPMSFILTER || cmd == SIOCGMSFILTER);
   2105      0    stevel 	is_v4only_api = (cmd == SIOCGIPMSFILTER || cmd == SIOCSIPMSFILTER);
   2106      0    stevel 	minsize = (is_v4only_api) ? IP_MSFILTER_SIZE(0) : GROUP_FILTER_SIZE(0);
   2107      0    stevel 	datalen = MBLKL(data_mp);
   2108      0    stevel 
   2109      0    stevel 	if (datalen < minsize)
   2110      0    stevel 		return (EINVAL);
   2111      0    stevel 
   2112      0    stevel 	/*
   2113      0    stevel 	 * now we know we have at least have the initial structure,
   2114      0    stevel 	 * but need to check for the source list array.
   2115      0    stevel 	 */
   2116      0    stevel 	if (is_v4only_api) {
   2117      0    stevel 		imsf = (struct ip_msfilter *)data_mp->b_rptr;
   2118      0    stevel 		isv6 = B_FALSE;
   2119      0    stevel 		expsize = IP_MSFILTER_SIZE(imsf->imsf_numsrc);
   2120      0    stevel 	} else {
   2121      0    stevel 		gf = (struct group_filter *)data_mp->b_rptr;
   2122      0    stevel 		if (gf->gf_group.ss_family == AF_INET6) {
   2123      0    stevel 			gsin6 = (struct sockaddr_in6 *)&gf->gf_group;
   2124      0    stevel 			isv6 = !(IN6_IS_ADDR_V4MAPPED(&gsin6->sin6_addr));
   2125      0    stevel 		} else {
   2126      0    stevel 			isv6 = B_FALSE;
   2127      0    stevel 		}
   2128      0    stevel 		expsize = GROUP_FILTER_SIZE(gf->gf_numsrc);
   2129      0    stevel 	}
   2130      0    stevel 	if (datalen < expsize)
   2131      0    stevel 		return (EINVAL);
   2132      0    stevel 
   2133      0    stevel 	if (isv6) {
   2134      0    stevel 		gsin6 = (struct sockaddr_in6 *)&gf->gf_group;
   2135  11042      Erik 		v6group = gsin6->sin6_addr;
   2136  11042      Erik 		if (getcmd) {
   2137  11042      Erik 			err = ip_get_srcfilter(connp, gf, NULL, &v6group,
   2138  11042      Erik 			    B_TRUE);
   2139  11042      Erik 		} else {
   2140  11042      Erik 			err = ip_set_srcfilter(connp, gf, NULL, &v6group, ill,
   2141  11042      Erik 			    B_TRUE);
   2142  11042      Erik 		}
   2143      0    stevel 	} else {
   2144  11042      Erik 		boolean_t issin6 = B_FALSE;
   2145      0    stevel 		if (is_v4only_api) {
   2146  11042      Erik 			v4group = (ipaddr_t)imsf->imsf_multiaddr.s_addr;
   2147  11042      Erik 			IN6_IPADDR_TO_V4MAPPED(v4group, &v6group);
   2148      0    stevel 		} else {
   2149      0    stevel 			if (gf->gf_group.ss_family == AF_INET) {
   2150      0    stevel 				gsin = (struct sockaddr_in *)&gf->gf_group;
   2151  11042      Erik 				v4group = (ipaddr_t)gsin->sin_addr.s_addr;
   2152  11042      Erik 				IN6_IPADDR_TO_V4MAPPED(v4group, &v6group);
   2153      0    stevel 			} else {
   2154      0    stevel 				gsin6 = (struct sockaddr_in6 *)&gf->gf_group;
   2155      0    stevel 				IN6_V4MAPPED_TO_IPADDR(&gsin6->sin6_addr,
   2156  11042      Erik 				    v4group);
   2157  11042      Erik 				issin6 = B_TRUE;
   2158      0    stevel 			}
   2159      0    stevel 		}
   2160  11042      Erik 		/*
   2161  11042      Erik 		 * INADDR_ANY is represented as the IPv6 unspecifed addr.
   2162  11042      Erik 		 */
   2163  11042      Erik 		if (v4group == INADDR_ANY)
   2164  11042      Erik 			v6group = ipv6_all_zeros;
   2165      0    stevel 		else
   2166  11042      Erik 			IN6_IPADDR_TO_V4MAPPED(v4group, &v6group);
   2167  11042      Erik 
   2168  11042      Erik 		if (getcmd) {
   2169  11042      Erik 			err = ip_get_srcfilter(connp, gf, imsf, &v6group,
   2170  11042      Erik 			    issin6);
   2171  11042      Erik 		} else {
   2172  11042      Erik 			err = ip_set_srcfilter(connp, gf, imsf, &v6group, ill,
   2173  11042      Erik 			    issin6);
   2174  11042      Erik 		}
   2175      0    stevel 	}
   2176  11042      Erik 	ill_refrele(ill);
   2177      0    stevel 
   2178      0    stevel 	return (err);
   2179      0    stevel }
   2180      0    stevel 
   2181      0    stevel /*
   2182  11042      Erik  * Determine the ill for the SIOC*MSFILTER ioctls
   2183  11042      Erik  *
   2184  11042      Erik  * Returns an error for IS_UNDER_IPMP interfaces.
   2185  11042      Erik  *
   2186  11042      Erik  * Finds the ill based on information in the ioctl headers.
   2187   4972      meem  */
   2188  11042      Erik static int
   2189  11042      Erik ip_msfilter_ill(conn_t *connp, mblk_t *mp, const ip_ioctl_cmd_t *ipip,
   2190  11042      Erik     ill_t **illp)
   2191   4972      meem {
   2192   4972      meem 	int cmd = ipip->ipi_cmd;
   2193   4972      meem 	int err = 0;
   2194  11042      Erik 	ill_t *ill;
   2195      0    stevel 	/* caller has verified this mblk exists */
   2196      0    stevel 	char *dbuf = (char *)mp->b_cont->b_cont->b_rptr;
   2197      0    stevel 	struct ip_msfilter *imsf;
   2198      0    stevel 	struct group_filter *gf;
   2199  11042      Erik 	ipaddr_t v4addr, v4group;
   2200  11042      Erik 	in6_addr_t v6group;
   2201      0    stevel 	uint32_t index;
   2202   3448  dh155122 	ip_stack_t *ipst;
   2203      0    stevel 
   2204   3448  dh155122 	ipst = connp->conn_netstack->netstack_ip;
   2205  11042      Erik 
   2206  11042      Erik 	*illp = NULL;
   2207      0    stevel 
   2208      0    stevel 	/* don't allow multicast operations on a tcp conn */
   2209    741  masputra 	if (IPCL_IS_TCP(connp))
   2210      0    stevel 		return (ENOPROTOOPT);
   2211      0    stevel 
   2212      0    stevel 	if (cmd == SIOCSIPMSFILTER || cmd == SIOCGIPMSFILTER) {
   2213      0    stevel 		/* don't allow v4-specific ioctls on v6 socket */
   2214  11042      Erik 		if (connp->conn_family == AF_INET6)
   2215      0    stevel 			return (EAFNOSUPPORT);
   2216      0    stevel 
   2217      0    stevel 		imsf = (struct ip_msfilter *)dbuf;
   2218      0    stevel 		v4addr = imsf->imsf_interface.s_addr;
   2219  11042      Erik 		v4group = imsf->imsf_multiaddr.s_addr;
   2220  11042      Erik 		IN6_IPADDR_TO_V4MAPPED(v4group, &v6group);
   2221  11042      Erik 		ill = ill_mcast_lookup(&v6group, v4addr, 0, IPCL_ZONEID(connp),
   2222  11042      Erik 		    ipst, &err);
   2223  11042      Erik 		if (ill == NULL && v4addr != INADDR_ANY)
   2224  11042      Erik 			err = ENXIO;
   2225      0    stevel 	} else {
   2226      0    stevel 		gf = (struct group_filter *)dbuf;
   2227      0    stevel 		index = gf->gf_interface;
   2228      0    stevel 		if (gf->gf_group.ss_family == AF_INET6) {
   2229      0    stevel 			struct sockaddr_in6 *sin6;
   2230  11042      Erik 
   2231      0    stevel 			sin6 = (struct sockaddr_in6 *)&gf->gf_group;
   2232  11042      Erik 			v6group = sin6->sin6_addr;
   2233      0    stevel 		} else if (gf->gf_group.ss_family == AF_INET) {
   2234      0    stevel 			struct sockaddr_in *sin;
   2235  11042      Erik 
   2236      0    stevel 			sin = (struct sockaddr_in *)&gf->gf_group;
   2237  11042      Erik 			v4group = sin->sin_addr.s_addr;
   2238  11042      Erik 			IN6_IPADDR_TO_V4MAPPED(v4group, &v6group);
   2239      0    stevel 		} else {
   2240      0    stevel 			return (EAFNOSUPPORT);
   2241      0    stevel 		}
   2242  11042      Erik 		ill = ill_mcast_lookup(&v6group, INADDR_ANY, index,
   2243  11042      Erik 		    IPCL_ZONEID(connp), ipst, &err);
   2244      0    stevel 	}
   2245  11042      Erik 	*illp = ill;
   2246      0    stevel 	return (err);
   2247      0    stevel }
   2248      0    stevel 
   2249      0    stevel /*
   2250      0    stevel  * The structures used for the SIOC*MSFILTER ioctls usually must be copied
   2251      0    stevel  * in in two stages, as the first copyin tells us the size of the attached
   2252      0    stevel  * source buffer.  This function is called by ip_wput_nondata() after the
   2253      0    stevel  * first copyin has completed; it figures out how big the second stage
   2254      0    stevel  * needs to be, and kicks it off.
   2255      0    stevel  *
   2256      0    stevel  * In some cases (numsrc < 2), the second copyin is not needed as the
   2257      0    stevel  * first one gets a complete structure containing 1 source addr.
   2258      0    stevel  *
   2259      0    stevel  * The function returns 0 if a second copyin has been started (i.e. there's
   2260      0    stevel  * no more work to be done right now), or 1 if the second copyin is not
   2261      0    stevel  * needed and ip_wput_nondata() can continue its processing.
   2262      0    stevel  */
   2263      0    stevel int
   2264      0    stevel ip_copyin_msfilter(queue_t *q, mblk_t *mp)
   2265      0    stevel {
   2266      0    stevel 	struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
   2267      0    stevel 	int cmd = iocp->ioc_cmd;
   2268      0    stevel 	/* validity of this checked in ip_wput_nondata() */
   2269      0    stevel 	mblk_t *mp1 = mp->b_cont->b_cont;
   2270      0    stevel 	int copysize = 0;
   2271      0    stevel 	int offset;
   2272      0    stevel 
   2273      0    stevel 	if (cmd == SIOCSMSFILTER || cmd == SIOCGMSFILTER) {
   2274      0    stevel 		struct group_filter *gf = (struct group_filter *)mp1->b_rptr;
   2275      0    stevel 		if (gf->gf_numsrc >= 2) {
   2276      0    stevel 			offset = sizeof (struct group_filter);
   2277      0    stevel 			copysize = GROUP_FILTER_SIZE(gf->gf_numsrc) - offset;
   2278      0    stevel 		}
   2279      0    stevel 	} else {
   2280      0    stevel 		struct ip_msfilter *imsf = (struct ip_msfilter *)mp1->b_rptr;
   2281      0    stevel 		if (imsf->imsf_numsrc >= 2) {
   2282      0    stevel 			offset = sizeof (struct ip_msfilter);
   2283      0    stevel 			copysize = IP_MSFILTER_SIZE(imsf->imsf_numsrc) - offset;
   2284      0    stevel 		}
   2285      0    stevel 	}
   2286      0    stevel 	if (copysize > 0) {
   2287      0    stevel 		mi_copyin_n(q, mp, offset, copysize);
   2288      0    stevel 		return (0);
   2289      0    stevel 	}
   2290      0    stevel 	return (1);
   2291      0    stevel }
   2292      0    stevel 
   2293      0    stevel /*
   2294      0    stevel  * Handle the following optmgmt:
   2295      0    stevel  *	IP_ADD_MEMBERSHIP		must not have joined already
   2296  11042      Erik  *	IPV6_JOIN_GROUP			must not have joined already
   2297      0    stevel  *	MCAST_JOIN_GROUP		must not have joined already
   2298      0    stevel  *	IP_BLOCK_SOURCE			must have joined already
   2299      0    stevel  *	MCAST_BLOCK_SOURCE		must have joined already
   2300      0    stevel  *	IP_JOIN_SOURCE_GROUP		may have joined already
   2301      0    stevel  *	MCAST_JOIN_SOURCE_GROUP		may have joined already
   2302      0    stevel  *
   2303      0    stevel  * fmode and src parameters may be used to determine which option is
   2304  11042      Erik  * being set, as follows (IPV6_JOIN_GROUP and MCAST_JOIN_GROUP options
   2305      0    stevel  * are functionally equivalent):
   2306  11042      Erik  *	opt			fmode			v6src
   2307  11042      Erik  *	IP_ADD_MEMBERSHIP	MODE_IS_EXCLUDE		unspecified
   2308  11042      Erik  *	IPV6_JOIN_GROUP		MODE_IS_EXCLUDE		unspecified
   2309  11042      Erik  *	MCAST_JOIN_GROUP	MODE_IS_EXCLUDE		unspecified
   2310  11042      Erik  *	IP_BLOCK_SOURCE		MODE_IS_EXCLUDE		IPv4-mapped addr
   2311  11042      Erik  *	MCAST_BLOCK_SOURCE	MODE_IS_EXCLUDE		v6 addr
   2312  11042      Erik  *	IP_JOIN_SOURCE_GROUP	MODE_IS_INCLUDE		IPv4-mapped addr
   2313  11042      Erik  *	MCAST_JOIN_SOURCE_GROUP	MODE_IS_INCLUDE		v6 addr
   2314      0    stevel  *
   2315      0    stevel  * Changing the filter mode is not allowed; if a matching ilg already
   2316      0    stevel  * exists and fmode != ilg->ilg_fmode, EINVAL is returned.
   2317      0    stevel  *
   2318      0    stevel  * Verifies that there is a source address of appropriate scope for
   2319      0    stevel  * the group; if not, EADDRNOTAVAIL is returned.
   2320      0    stevel  *
   2321  11042      Erik  * The interface to be used may be identified by an IPv4 address or by an
   2322  11042      Erik  * interface index.
   2323  11042      Erik  *
   2324  11042      Erik  * Handles IPv4-mapped IPv6 multicast addresses by associating them
   2325  11042      Erik  * with the IPv4 address.  Assumes that if v6group is v4-mapped,
   2326  11042      Erik  * v6src is also v4-mapped.
   2327      0    stevel  */
   2328      0    stevel int
   2329  11042      Erik ip_opt_add_group(conn_t *connp, boolean_t checkonly,
   2330  11042      Erik     const in6_addr_t *v6group, ipaddr_t ifaddr, uint_t ifindex,
   2331  11042      Erik     mcast_record_t fmode, const in6_addr_t *v6src)
   2332      0    stevel {
   2333  11042      Erik 	ill_t *ill;
   2334  11042      Erik 	char buf[INET6_ADDRSTRLEN];
   2335  11042      Erik 	int	err;
   2336      0    stevel 
   2337  11042      Erik 	err = ip_opt_check(connp, v6group, v6src, ifaddr, ifindex, &ill);
   2338      0    stevel 	if (err != 0) {
   2339  11042      Erik 		ip1dbg(("ip_opt_add_group: no ill for group %s/"
   2340  11042      Erik 		    "index %d\n", inet_ntop(AF_INET6, v6group, buf,
   2341  11042      Erik 		    sizeof (buf)), ifindex));
   2342      0    stevel 		return (err);
   2343      0    stevel 	}
   2344      0    stevel 
   2345      0    stevel 	if (checkonly) {
   2346      0    stevel 		/*
   2347      0    stevel 		 * do not do operation, just pretend to - new T_CHECK
   2348      0    stevel 		 * semantics. The error return case above if encountered
   2349      0    stevel 		 * considered a good enough "check" here.
   2350      0    stevel 		 */
   2351  11042      Erik 		ill_refrele(ill);
   2352      0    stevel 		return (0);
   2353      0    stevel 	}
   2354      0    stevel 
   2355  11042      Erik 	mutex_enter(&ill->ill_mcast_serializer);
   2356  11042      Erik 	err = ilg_add(connp, v6group, ifaddr, ifindex, ill, fmode, v6src);
   2357  11042      Erik 	mutex_exit(&ill->ill_mcast_serializer);
   2358  11042      Erik 	ill_refrele(ill);
   2359      0    stevel 	return (err);
   2360      0    stevel }
   2361      0    stevel 
   2362      0    stevel /*
   2363  11042      Erik  * Common for IPv6 and IPv4.
   2364  11042      Erik  * Here we handle ilgs that are still attached to their original ill
   2365  11042      Erik  * (the one ifaddr/ifindex points at), as well as detached ones.
   2366  11042      Erik  * The detached ones might have been attached to some other ill.
   2367      0    stevel  */
   2368  11042      Erik static int
   2369  11042      Erik ip_opt_delete_group_excl(conn_t *connp, const in6_addr_t *v6group,
   2370  11042      Erik     ipaddr_t ifaddr, uint_t ifindex, mcast_record_t fmode,
   2371  11042      Erik     const in6_addr_t *v6src)
   2372      0    stevel {
   2373  11042      Erik 	ilg_t	*ilg;
   2374  11042      Erik 	boolean_t leaving;
   2375  11042      Erik 	ilm_t *ilm;
   2376      0    stevel 	ill_t *ill;
   2377  11042      Erik 	int err = 0;
   2378      0    stevel 
   2379  11042      Erik retry:
   2380  11042      Erik 	rw_enter(&connp->conn_ilg_lock, RW_WRITER);
   2381  11042      Erik 	ilg = ilg_lookup(connp, v6group, ifaddr, ifindex);
   2382  11042      Erik 	if (ilg == NULL) {
   2383  11042      Erik 		rw_exit(&connp->conn_ilg_lock);
   2384  11042      Erik 		/*
   2385  11042      Erik 		 * Since we didn't have any ilg we now do the error checks
   2386  11042      Erik 		 * to determine the best errno.
   2387  11042      Erik 		 */
   2388  11042      Erik 		err = ip_opt_check(connp, v6group, v6src, ifaddr, ifindex,
   2389  11042      Erik 		    &ill);
   2390  11042      Erik 		if (ill != NULL) {
   2391  11042      Erik 			/* The only error was a missing ilg for the group */
   2392  11042      Erik 			ill_refrele(ill);
   2393  11042      Erik 			err = EADDRNOTAVAIL;
   2394      0    stevel 		}
   2395      0    stevel 		return (err);
   2396      0    stevel 	}
   2397      0    stevel 
   2398  11042      Erik 	/* If the ilg is attached then we serialize using that ill */
   2399  11042      Erik 	ill = ilg->ilg_ill;
   2400  11042      Erik 	if (ill != NULL) {
   2401  11042      Erik 		/* Prevent the ill and ilg from being freed */
   2402  11042      Erik 		ill_refhold(ill);
   2403  11042      Erik 		ilg_refhold(ilg);
   2404  11042      Erik 		rw_exit(&connp->conn_ilg_lock);
   2405  11042      Erik 		mutex_enter(&ill->ill_mcast_serializer);
   2406  11042      Erik 		rw_enter(&connp->conn_ilg_lock, RW_WRITER);
   2407  11042      Erik 		if (ilg->ilg_condemned) {
   2408  11042      Erik 			/* Disappeared */
   2409  11042      Erik 			ilg_refrele(ilg);
   2410  11042      Erik 			rw_exit(&connp->conn_ilg_lock);
   2411  11042      Erik 			mutex_exit(&ill->ill_mcast_serializer);
   2412      0    stevel 			ill_refrele(ill);
   2413  11042      Erik 			goto retry;
   2414      0    stevel 		}
   2415      0    stevel 	}
   2416      0    stevel 
   2417      0    stevel 	/*
   2418      0    stevel 	 * Decide if we're actually deleting the ilg or just removing a
   2419      0    stevel 	 * source filter address; if just removing an addr, make sure we
   2420      0    stevel 	 * aren't trying to change the filter mode, and that the addr is
   2421      0    stevel 	 * actually in our filter list already.  If we're removing the
   2422      0    stevel 	 * last src in an include list, just delete the ilg.
   2423      0    stevel 	 */
   2424  11042      Erik 	if (IN6_IS_ADDR_UNSPECIFIED(v6src)) {
   2425      0    stevel 		leaving = B_TRUE;
   2426      0    stevel 	} else {
   2427      0    stevel 		if (fmode != ilg->ilg_fmode)
   2428      0    stevel 			err = EINVAL;
   2429      0    stevel 		else if (ilg->ilg_filter == NULL ||
   2430      0    stevel 		    !list_has_addr(ilg->ilg_filter, v6src))
   2431      0    stevel 			err = EADDRNOTAVAIL;
   2432      0    stevel 		if (err != 0) {
   2433  11042      Erik 			if (ill != NULL)
   2434  11042      Erik 				ilg_refrele(ilg);
   2435  11042      Erik 			rw_exit(&connp->conn_ilg_lock);
   2436  11042      Erik 			goto done;
   2437      0    stevel 		}
   2438      0    stevel 		if (fmode == MODE_IS_INCLUDE &&
   2439  11042      Erik 		    ilg->ilg_filter->sl_numsrc == 1) {
   2440  11042      Erik 			leaving = B_TRUE;
   2441      0    stevel 			v6src = NULL;
   2442  11042      Erik 		} else {
   2443      0    stevel 			leaving = B_FALSE;
   2444  11042      Erik 		}
   2445      0    stevel 	}
   2446  11042      Erik 	ilm = ilg->ilg_ilm;
   2447  11042      Erik 	if (leaving)
   2448  11042      Erik 		ilg->ilg_ilm = NULL;
   2449      0    stevel 
   2450      0    stevel 	ilg_delete(connp, ilg, v6src);
   2451  11042      Erik 	if (ill != NULL)
   2452  11042      Erik 		ilg_refrele(ilg);
   2453  11042      Erik 	rw_exit(&connp->conn_ilg_lock);
   2454      0    stevel 
   2455  11042      Erik 	if (ilm != NULL) {
   2456  11042      Erik 		ASSERT(ill != NULL);
   2457  11042      Erik 		(void) ip_delmulti_serial(ilm, B_FALSE, leaving);
   2458  11042      Erik 	}
   2459  11042      Erik done:
   2460  11042      Erik 	if (ill != NULL) {
   2461  11042      Erik 		mutex_exit(&ill->ill_mcast_serializer);
   2462  11042      Erik 		ill_refrele(ill);
   2463  11042      Erik 	}
   2464  11042      Erik 	return (err);
   2465      0    stevel }
   2466      0    stevel 
   2467      0    stevel /*
   2468      0    stevel  * Handle the following optmgmt:
   2469      0    stevel  *	IP_DROP_MEMBERSHIP		will leave
   2470  11042      Erik  *	IPV6_LEAVE_GROUP		will leave
   2471      0    stevel  *	MCAST_LEAVE_GROUP		will leave
   2472      0    stevel  *	IP_UNBLOCK_SOURCE		will not leave
   2473      0    stevel  *	MCAST_UNBLOCK_SOURCE		will not leave
   2474      0    stevel  *	IP_LEAVE_SOURCE_GROUP		may leave (if leaving last source)
   2475      0    stevel  *	MCAST_LEAVE_SOURCE_GROUP	may leave (if leaving last source)
   2476      0    stevel  *
   2477      0    stevel  * fmode and src parameters may be used to determine which option is
   2478  11042      Erik  * being set, as follows:
   2479      0    stevel  *	opt			 fmode			v6src
   2480  11042      Erik  *	IP_DROP_MEMBERSHIP	 MODE_IS_INCLUDE	unspecified
   2481      0    stevel  *	IPV6_LEAVE_GROUP	 MODE_IS_INCLUDE	unspecified
   2482      0    stevel  *	MCAST_LEAVE_GROUP	 MODE_IS_INCLUDE	unspecified
   2483  11042      Erik  *	IP_UNBLOCK_SOURCE	 MODE_IS_EXCLUDE	IPv4-mapped addr
   2484      0    stevel  *	MCAST_UNBLOCK_SOURCE	 MODE_IS_EXCLUDE	v6 addr
   2485  11042      Erik  *	IP_LEAVE_SOURCE_GROUP	 MODE_IS_INCLUDE	IPv4-mapped addr
   2486      0    stevel  *	MCAST_LEAVE_SOURCE_GROUP MODE_IS_INCLUDE	v6 addr
   2487      0    stevel  *
   2488      0    stevel  * Changing the filter mode is not allowed; if a matching ilg already
   2489      0    stevel  * exists and fmode != ilg->ilg_fmode, EINVAL is returned.
   2490      0    stevel  *
   2491  11042      Erik  * The interface to be used may be identified by an IPv4 address or by an
   2492  11042      Erik  * interface index.
   2493  11042      Erik  *
   2494      0    stevel  * Handles IPv4-mapped IPv6 multicast addresses by associating them
   2495  11042      Erik  * with the IPv4 address.  Assumes that if v6group is v4-mapped,
   2496      0    stevel  * v6src is also v4-mapped.
   2497      0    stevel  */
   2498      0    stevel int
   2499  11042      Erik ip_opt_delete_group(conn_t *connp, boolean_t checkonly,
   2500  11042      Erik     const in6_addr_t *v6group, ipaddr_t ifaddr, uint_t ifindex,
   2501  11042      Erik     mcast_record_t fmode, const in6_addr_t *v6src)
   2502      0    stevel {
   2503      0    stevel 
   2504  11042      Erik 	/*
   2505  11042      Erik 	 * In the normal case below we don't check for the ill existing.
   2506  11042      Erik 	 * Instead we look for an existing ilg in _excl.
   2507  11042      Erik 	 * If checkonly we sanity check the arguments
   2508  11042      Erik 	 */
   2509  11042      Erik 	if (checkonly) {
   2510  11042      Erik 		ill_t	*ill;
   2511  11042      Erik 		int	err;
   2512  11042      Erik 
   2513  11042      Erik 		err = ip_opt_check(connp, v6group, v6src, ifaddr, ifindex,
   2514  11042      Erik 		    &ill);
   2515  11042      Erik 		/*
   2516  11042      Erik 		 * do not do operation, just pretend to - new T_CHECK semantics.
   2517  11042      Erik 		 * ip_opt_check is considered a good enough "check" here.
   2518  11042      Erik 		 */
   2519  11042      Erik 		if (ill != NULL)
   2520  11042      Erik 			ill_refrele(ill);
   2521      0    stevel 		return (err);
   2522      0    stevel 	}
   2523  11042      Erik 	return (ip_opt_delete_group_excl(connp, v6group, ifaddr, ifindex,
   2524  11042      Erik 	    fmode, v6src));
   2525      0    stevel }
   2526      0    stevel 
   2527      0    stevel /*
   2528      0    stevel  * Group mgmt for upper conn that passes things down
   2529      0    stevel  * to the interface multicast list (and DLPI)
   2530      0    stevel  * These routines can handle new style options that specify an interface name
   2531      0    stevel  * as opposed to an interface address (needed for general handling of
   2532      0    stevel  * unnumbered interfaces.)
   2533      0    stevel  */
   2534      0    stevel 
   2535      0    stevel /*
   2536      0    stevel  * Add a group to an upper conn group data structure and pass things down
   2537      0    stevel  * to the interface multicast list (and DLPI)
   2538  11042      Erik  * Common for IPv4 and IPv6; for IPv4 we can have an ifaddr.
   2539      0    stevel  */
   2540      0    stevel static int
   2541  11042      Erik ilg_add(conn_t *connp, const in6_addr_t *v6group, ipaddr_t ifaddr,
   2542  11042      Erik     uint_t ifindex, ill_t *ill, mcast_record_t fmode, const in6_addr_t *v6src)
   2543      0    stevel {
   2544      0    stevel 	int	error = 0;
   2545      0    stevel 	ilg_t	*ilg;
   2546      0    stevel 	ilg_stat_t ilgstat;
   2547      0    stevel 	slist_t	*new_filter = NULL;
   2548      0    stevel 	int	new_fmode;
   2549  11042      Erik 	ilm_t *ilm;
   2550      0    stevel 
   2551      0    stevel 	if (!(ill->ill_flags & ILLF_MULTICAST))
   2552      0    stevel 		return (EADDRNOTAVAIL);
   2553      0    stevel 
   2554  11042      Erik 	/* conn_ilg_lock protects the ilg list. */
   2555  11042      Erik 	ASSERT(MUTEX_HELD(&ill->ill_mcast_serializer));
   2556  11042      Erik 	rw_enter(&connp->conn_ilg_lock, RW_WRITER);
   2557  11042      Erik 	ilg = ilg_lookup(connp, v6group, ifaddr, ifindex);
   2558      0    stevel 
   2559      0    stevel 	/*
   2560      0    stevel 	 * Depending on the option we're handling, may or may not be okay
   2561      0    stevel 	 * if group has already been added.  Figure out our rules based
   2562      0    stevel 	 * on fmode and src params.  Also make sure there's enough room
   2563      0    stevel 	 * in the filter if we're adding a source to an existing filter.
   2564      0    stevel 	 */
   2565      0    stevel 	if (IN6_IS_ADDR_UNSPECIFIED(v6src)) {
   2566      0    stevel 		/* we're joining for all sources, must not have joined */
   2567      0    stevel 		if (ilg != NULL)
   2568      0    stevel 			error = EADDRINUSE;
   2569      0    stevel 	} else {
   2570      0    stevel 		if (fmode == MODE_IS_EXCLUDE) {
   2571      0    stevel 			/* (excl {addr}) => block source, must have joined */
   2572      0    stevel 			if (ilg == NULL)
   2573      0    stevel 				error = EADDRNOTAVAIL;
   2574      0    stevel 		}
   2575      0    stevel 		/* (incl {addr}) => join source, may have joined */
   2576      0    stevel 
   2577      0    stevel 		if (ilg != NULL &&
   2578      0    stevel 		    SLIST_CNT(ilg->ilg_filter) == MAX_FILTER_SIZE)
   2579      0    stevel 			error = ENOBUFS;
   2580      0    stevel 	}
   2581      0    stevel 	if (error != 0) {
   2582  11042      Erik 		rw_exit(&connp->conn_ilg_lock);
   2583      0    stevel 		return (error);
   2584      0    stevel 	}
   2585      0    stevel 
   2586      0    stevel 	/*
   2587      0    stevel 	 * Alloc buffer to copy new state into (see below) before
   2588      0    stevel 	 * we make any changes, so we can bail if it fails.
   2589      0    stevel 	 */
   2590      0    stevel 	if ((new_filter = l_alloc()) == NULL) {
   2591  11042      Erik 		rw_exit(&connp->conn_ilg_lock);
   2592      0    stevel 		return (ENOMEM);
   2593      0    stevel 	}
   2594      0    stevel 
   2595      0    stevel 	if (ilg == NULL) {
   2596   8485     Peter 		if ((ilg = conn_ilg_alloc(connp, &error)) == NULL) {
   2597  11042      Erik 			rw_exit(&connp->conn_ilg_lock);
   2598   8485     Peter 			l_free(new_filter);
   2599   8485     Peter 			return (error);
   2600      0    stevel 		}
   2601  11042      Erik 		ilg->ilg_ifindex = ifindex;
   2602  11042      Erik 		ilg->ilg_ifaddr = ifaddr;
   2603      0    stevel 		if (!IN6_IS_ADDR_UNSPECIFIED(v6src)) {
   2604      0    stevel 			ilg->ilg_filter = l_alloc();
   2605      0    stevel 			if (ilg->ilg_filter == NULL) {
   2606      0    stevel 				ilg_delete(connp, ilg, NULL);
   2607  11042      Erik 				rw_exit(&connp->conn_ilg_lock);
   2608      0    stevel 				l_free(new_filter);
   2609      0    stevel 				return (ENOMEM);
   2610      0    stevel 			}
   2611      0    stevel 			ilg->ilg_filter->sl_numsrc = 1;
   2612      0    stevel 			ilg->ilg_filter->sl_addr[0] = *v6src;
   2613      0    stevel 		}
   2614      0    stevel 		ilgstat = ILGSTAT_NEW;
   2615      0    stevel 		ilg->ilg_v6group = *v6group;
   2616      0    stevel 		ilg->ilg_fmode = fmode;
   2617      0    stevel 		ilg->ilg_ill = ill;
   2618      0    stevel 	} else {
   2619      0    stevel 		int index;
   2620      0    stevel 		if (ilg->ilg_fmode != fmode || IN6_IS_ADDR_UNSPECIFIED(v6src)) {
   2621  11042      Erik 			rw_exit(&connp->conn_ilg_lock);
   2622      0    stevel 			l_free(new_filter);
   2623      0    stevel 			return (EINVAL);
   2624      0    stevel 		}
   2625      0    stevel 		if (ilg->ilg_filter == NULL) {
   2626      0    stevel 			ilg->ilg_filter = l_alloc();
   2627      0    stevel 			if (ilg->ilg_filter == NULL) {
   2628  11042      Erik 				rw_exit(&connp->conn_ilg_lock);
   2629      0    stevel 				l_free(new_filter);
   2630      0    stevel 				return (ENOMEM);
   2631      0    stevel 			}
   2632      0    stevel 		}
   2633      0    stevel 		if (list_has_addr(ilg->ilg_filter, v6src)) {
   2634  11042      Erik 			rw_exit(&connp->conn_ilg_lock);
   2635      0    stevel 			l_free(new_filter);
   2636      0    stevel 			return (EADDRNOTAVAIL);
   2637      0    stevel 		}
   2638      0    stevel 		ilgstat = ILGSTAT_CHANGE;
   2639      0    stevel 		index = ilg->ilg_filter->sl_numsrc++;
   2640      0    stevel 		ilg->ilg_filter->sl_addr[index] = *v6src;
   2641      0    stevel 	}
   2642      0    stevel 
   2643      0    stevel 	/*
   2644      0    stevel 	 * Save copy of ilg's filter state to pass to other functions,
   2645  11042      Erik 	 * so we can release conn_ilg_lock now.
   2646      0    stevel 	 */
   2647      0    stevel 	new_fmode = ilg->ilg_fmode;
   2648      0    stevel 	l_copy(ilg->ilg_filter, new_filter);
   2649      0    stevel 
   2650  11042      Erik 	rw_exit(&connp->conn_ilg_lock);
   2651      0    stevel 
   2652      0    stevel 	/*
   2653      0    stevel 	 * Now update the ill. We wait to do this until after the ilg
   2654      0    stevel 	 * has been updated because we need to update the src filter
   2655      0    stevel 	 * info for the ill, which involves looking at the status of
   2656      0    stevel 	 * all the ilgs associated with this group/interface pair.
   2657      0    stevel 	 */
   2658  11042      Erik 	ilm = ip_addmulti_serial(v6group, ill, connp->conn_zoneid, ilgstat,
   2659  11042      Erik 	    new_fmode, new_filter, &error);
   2660  11042      Erik 
   2661  11042      Erik 	rw_enter(&connp->conn_ilg_lock, RW_WRITER);
   2662  11042      Erik 	/*
   2663  11042      Erik 	 * Must look up the ilg again since we've not been holding
   2664  11042      Erik 	 * conn_ilg_lock. The ilg could have disappeared due to an unplumb
   2665  11042      Erik 	 * having called conn_update_ill, which can run once we dropped the
   2666  11042      Erik 	 * conn_ilg_lock above.
   2667  11042      Erik 	 */
   2668  11042      Erik 	ilg = ilg_lookup(connp, v6group, ifaddr, ifindex);
   2669  11042      Erik 	if (ilg == NULL) {
   2670  11042      Erik 		rw_exit(&connp->conn_ilg_lock);
   2671  11042      Erik 		if (ilm != NULL) {
   2672  11042      Erik 			(void) ip_delmulti_serial(ilm, B_FALSE,
   2673  11042      Erik 			    (ilgstat == ILGSTAT_NEW));
   2674  11042      Erik 		}
   2675  11042      Erik 		error = ENXIO;
   2676  11042      Erik 		goto free_and_exit;
   2677      0    stevel 	}
   2678      0    stevel 
   2679  11042      Erik 	if (ilm != NULL) {
   2680  11042      Erik 		/* Succeeded. Update the ilg to point at the ilm */
   2681  11042      Erik 		if (ilgstat == ILGSTAT_NEW) {
   2682  11042      Erik 			ASSERT(ilg->ilg_ilm == NULL);
   2683  11042      Erik 			ilg->ilg_ilm = ilm;
   2684  11042      Erik 			ilm->ilm_ifaddr = ifaddr;	/* For netstat */
   2685  11042      Erik 		} else {
   2686  11042      Erik 			/*
   2687  11042      Erik 			 * ip_addmulti didn't get a held ilm for
   2688  11042      Erik 			 * ILGSTAT_CHANGE; ilm_refcnt was unchanged.
   2689  11042      Erik 			 */
   2690  11042      Erik 			ASSERT(ilg->ilg_ilm == ilm);
   2691  11042      Erik 		}
   2692  11042      Erik 	} else {
   2693  11042      Erik 		ASSERT(error != 0);
   2694  11042      Erik 		/*
   2695  11042      Erik 		 * Failed to allocate the ilm.
   2696  11042      Erik 		 * Need to undo what we did before calling ip_addmulti()
   2697  11042      Erik 		 * If ENETDOWN just clear ill_ilg since so that we
   2698  11042      Erik 		 * will rejoin when the ill comes back; don't report ENETDOWN
   2699  11042      Erik 		 * to application.
   2700  11042      Erik 		 */
   2701  11042      Erik 		if (ilgstat == ILGSTAT_NEW && error == ENETDOWN) {
   2702  11042      Erik 			ilg->ilg_ill = NULL;
   2703  11042      Erik 			error = 0;
   2704  11042      Erik 		} else {
   2705  11042      Erik 			in6_addr_t delsrc =
   2706  11042      Erik 			    (ilgstat == ILGSTAT_NEW) ? ipv6_all_zeros : *v6src;
   2707  11042      Erik 
   2708  11042      Erik 			ilg_delete(connp, ilg, &delsrc);
   2709  11042      Erik 		}
   2710  11042      Erik 	}
   2711  11042      Erik 	rw_exit(&connp->conn_ilg_lock);
   2712  11042      Erik 
   2713  11042      Erik free_and_exit:
   2714      0    stevel 	l_free(new_filter);
   2715  11042      Erik 	return (error);
   2716      0    stevel }
   2717      0    stevel 
   2718      0    stevel /*
   2719  11042      Erik  * Find an IPv4 ilg matching group, ill and source.
   2720  11042      Erik  * The group and source can't be INADDR_ANY here so no need to translate to
   2721  11042      Erik  * the unspecified IPv6 address.
   2722      0    stevel  */
   2723  11042      Erik boolean_t
   2724  11042      Erik conn_hasmembers_ill_withsrc_v4(conn_t *connp, ipaddr_t group, ipaddr_t src,
   2725  11042      Erik     ill_t *ill)
   2726      0    stevel {
   2727      0    stevel 	in6_addr_t v6group, v6src;
   2728      0    stevel 	int i;
   2729      0    stevel 	boolean_t isinlist;
   2730      0    stevel 	ilg_t *ilg;
   2731      0    stevel 
   2732  11042      Erik 	rw_enter(&connp->conn_ilg_lock, RW_READER);
   2733  11042      Erik 	IN6_IPADDR_TO_V4MAPPED(group, &v6group);
   2734  11042      Erik 	for (ilg = connp->conn_ilg; ilg != NULL; ilg = ilg->ilg_next) {
   2735  11042      Erik 		if (ilg->ilg_condemned)
   2736  11042      Erik 			continue;
   2737      0    stevel 
   2738  11042      Erik 		/* ilg_ill could be NULL if an add is in progress */
   2739  11042      Erik 		if (ilg->ilg_ill != ill)
   2740  11042      Erik 			continue;
   2741      0    stevel 
   2742  11042      Erik 		/* The callers use upper ill for IPMP */
   2743  11042      Erik 		ASSERT(!IS_UNDER_IPMP(ill));
   2744  11042      Erik 		if (IN6_ARE_ADDR_EQUAL(&ilg->ilg_v6group, &v6group)) {
   2745      0    stevel 			if (SLIST_IS_EMPTY(ilg->ilg_filter)) {
   2746      0    stevel 				/* no source filter, so this is a match */
   2747  11042      Erik 				rw_exit(&connp->conn_ilg_lock);
   2748  11042      Erik 				return (B_TRUE);
   2749      0    stevel 			}
   2750      0    stevel 			break;
   2751      0    stevel 		}
   2752      0    stevel 	}
   2753  11042      Erik 	if (ilg == NULL) {
   2754  11042      Erik 		rw_exit(&connp->conn_ilg_lock);
   2755  11042      Erik 		return (B_FALSE);
   2756  11042      Erik 	}
   2757      0    stevel 
   2758      0    stevel 	/*
   2759      0    stevel 	 * we have an ilg with matching ill and group; but
   2760      0    stevel 	 * the ilg has a source list that we must check.
   2761      0    stevel 	 */
   2762      0    stevel 	IN6_IPADDR_TO_V4MAPPED(src, &v6src);
   2763      0    stevel 	isinlist = B_FALSE;
   2764      0    stevel 	for (i = 0; i < ilg->ilg_filter->sl_numsrc; i++) {
   2765      0    stevel 		if (IN6_ARE_ADDR_EQUAL(&v6src, &ilg->ilg_filter->sl_addr[i])) {
   2766      0    stevel 			isinlist = B_TRUE;
   2767      0    stevel 			break;
   2768      0    stevel 		}
   2769      0    stevel 	}
   2770      0    stevel 
   2771      0    stevel 	if ((isinlist && ilg->ilg_fmode == MODE_IS_INCLUDE) ||
   2772  11042      Erik 	    (!isinlist && ilg->ilg_fmode == MODE_IS_EXCLUDE)) {
   2773  11042      Erik 		rw_exit(&connp->conn_ilg_lock);
   2774  11042      Erik 		return (B_TRUE);
   2775  11042      Erik 	}
   2776  11042      Erik 	rw_exit(&connp->conn_ilg_lock);
   2777  11042      Erik 	return (B_FALSE);
   2778      0    stevel }
   2779      0    stevel 
   2780      0    stevel /*
   2781      0    stevel  * Find an IPv6 ilg matching group, ill, and source
   2782      0    stevel  */
   2783  11042      Erik boolean_t
   2784  11042      Erik conn_hasmembers_ill_withsrc_v6(conn_t *connp, const in6_addr_t *v6group,
   2785      0    stevel     const in6_addr_t *v6src, ill_t *ill)
   2786      0    stevel {
   2787      0    stevel 	int i;
   2788      0    stevel 	boolean_t isinlist;
   2789      0    stevel 	ilg_t *ilg;
   2790      0    stevel 
   2791  11042      Erik 	rw_enter(&connp->conn_ilg_lock, RW_READER);
   2792  11042      Erik 	for (ilg = connp->conn_ilg; ilg != NULL; ilg = ilg->ilg_next) {
   2793  11042      Erik 		if (ilg->ilg_condemned)
   2794  11042      Erik 			continue;
   2795      0    stevel 
   2796  11042      Erik 		/* ilg_ill could be NULL if an add is in progress */
   2797  11042      Erik 		if (ilg->ilg_ill != ill)
   2798      0    stevel 			continue;
   2799  11042      Erik 
   2800  11042      Erik 		/* The callers use upper ill for IPMP */
   2801  11042      Erik 		ASSERT(!IS_UNDER_IPMP(ill));
   2802  11042      Erik 		if (IN6_ARE_ADDR_EQUAL(&ilg->ilg_v6group, v6group)) {
   2803      0    stevel 			if (SLIST_IS_EMPTY(ilg->ilg_filter)) {
   2804      0    stevel 				/* no source filter, so this is a match */
   2805  11042      Erik 				rw_exit(&connp->conn_ilg_lock);
   2806  11042      Erik 				return (B_TRUE);
   2807      0    stevel 			}
   2808      0    stevel 			break;
   2809      0    stevel 		}
   2810      0    stevel 	}
   2811  11042      Erik 	if (ilg == NULL) {
   2812  11042      Erik 		rw_exit(&connp->conn_ilg_lock);
   2813  11042      Erik 		return (B_FALSE);
   2814  11042      Erik 	}
   2815      0    stevel 
   2816      0    stevel 	/*
   2817      0    stevel 	 * we have an ilg with matching ill and group; but
   2818      0    stevel 	 * the ilg has a source list that we must check.
   2819      0    stevel 	 */
   2820      0    stevel 	isinlist = B_FALSE;
   2821      0    stevel 	for (i = 0; i < ilg->ilg_filter->sl_numsrc; i++) {
   2822      0    stevel 		if (IN6_ARE_ADDR_EQUAL(v6src, &ilg->ilg_filter->sl_addr[i])) {
   2823      0    stevel 			isinlist = B_TRUE;
   2824      0    stevel 			break;
   2825      0    stevel 		}
   2826      0    stevel 	}
   2827      0    stevel 
   2828      0    stevel 	if ((isinlist && ilg->ilg_fmode == MODE_IS_INCLUDE) ||
   2829  11042      Erik 	    (!isinlist && ilg->ilg_fmode == MODE_IS_EXCLUDE)) {
   2830  11042      Erik 		rw_exit(&connp->conn_ilg_lock);
   2831  11042      Erik 		return (B_TRUE);
   2832  11042      Erik 	}
   2833  11042      Erik 	rw_exit(&connp->conn_ilg_lock);
   2834  11042      Erik 	return (B_FALSE);
   2835      0    stevel }
   2836      0    stevel 
   2837      0    stevel /*
   2838  11042      Erik  * Find an ilg matching group and ifaddr/ifindex.
   2839  11042      Erik  * We check both ifaddr and ifindex even though at most one of them
   2840  11042      Erik  * will be non-zero; that way we always find the right one.
   2841      0    stevel  */
   2842  11042      Erik static ilg_t *
   2843  11042      Erik ilg_lookup(conn_t *connp, const in6_addr_t *v6group, ipaddr_t ifaddr,
   2844  11042      Erik     uint_t ifindex)
   2845      0    stevel {
   2846      0    stevel 	ilg_t	*ilg;
   2847      0    stevel 
   2848  11042      Erik 	ASSERT(RW_LOCK_HELD(&connp->conn_ilg_lock));
   2849      0    stevel 
   2850  11042      Erik 	for (ilg = connp->conn_ilg; ilg != NULL; ilg = ilg->ilg_next) {
   2851  11042      Erik 		if (ilg->ilg_condemned)
   2852      0    stevel 			continue;
   2853  11042      Erik 
   2854  11042      Erik 		if (ilg->ilg_ifaddr == ifaddr &&
   2855  11042      Erik 		    ilg->ilg_ifindex == ifindex &&
   2856      0    stevel 		    IN6_ARE_ADDR_EQUAL(&ilg->ilg_v6group, v6group))
   2857   6379   sowmini 			return (ilg);
   2858      0    stevel 	}
   2859      0    stevel 	return (NULL);
   2860      0    stevel }
   2861      0    stevel 
   2862      0    stevel /*
   2863      0    stevel  * If a source address is passed in (src != NULL and src is not
   2864      0    stevel  * unspecified), remove the specified src addr from the given ilg's
   2865      0    stevel  * filter list, else delete the ilg.
   2866      0    stevel  */
   2867      0    stevel static void
   2868      0    stevel ilg_delete(conn_t *connp, ilg_t *ilg, const in6_addr_t *src)
   2869      0    stevel {
   2870  11042      Erik 	ASSERT(RW_WRITE_HELD(&connp->conn_ilg_lock));
   2871  11042      Erik 	ASSERT(ilg->ilg_ptpn != NULL);
   2872  11042      Erik 	ASSERT(!ilg->ilg_condemned);
   2873      0    stevel 
   2874      0    stevel 	if (src == NULL || IN6_IS_ADDR_UNSPECIFIED(src)) {
   2875  11042      Erik 		FREE_SLIST(ilg->ilg_filter);
   2876  11042      Erik 		ilg->ilg_filter = NULL;
   2877      0    stevel 
   2878  11042      Erik 		ASSERT(ilg->ilg_ilm == NULL);
   2879  11042      Erik 		ilg->ilg_ill = NULL;
   2880  11042      Erik 		ilg->ilg_condemned = B_TRUE;
   2881      0    stevel 
   2882  11042      Erik 		/* ilg_inactive will unlink from the list */
   2883  11042      Erik 		ilg_refrele(ilg);
   2884      0    stevel 	} else {
   2885      0    stevel 		l_remove(ilg->ilg_filter, src);
   2886      0    stevel 	}
   2887      0    stevel }
   2888      0    stevel 
   2889      0    stevel /*
   2890  11042      Erik  * Called from conn close. No new ilg can be added or removed
   2891      0    stevel  * because CONN_CLOSING has been set by ip_close. ilg_add / ilg_delete
   2892      0    stevel  * will return error if conn has started closing.
   2893  11042      Erik  *
   2894  11042      Erik  * We handle locking as follows.
   2895  11042      Erik  * Under conn_ilg_lock we get the first ilg. As we drop the conn_ilg_lock to
   2896  11042      Erik  * proceed with the ilm part of the delete we hold a reference on both the ill
   2897  11042      Erik  * and the ilg. This doesn't prevent changes to the ilg, but prevents it from
   2898  11042      Erik  * being deleted.
   2899  11042      Erik  *
   2900  11042      Erik  * Since the ilg_add code path uses two locks (conn_ilg_lock for the ilg part,
   2901  11042      Erik  * and ill_mcast_lock for the ip_addmulti part) we can run at a point between
   2902  11042      Erik  * the two. At that point ilg_ill is set, but ilg_ilm hasn't yet been set. In
   2903  11042      Erik  * that case we delete the ilg here, which makes ilg_add discover that the ilg
   2904  11042      Erik  * has disappeared when ip_addmulti returns, so it will discard the ilm it just
   2905  11042      Erik  * added.
   2906      0    stevel  */
   2907      0    stevel void
   2908      0    stevel ilg_delete_all(conn_t *connp)
   2909      0    stevel {
   2910  11042      Erik 	ilg_t	*ilg, *next_ilg, *held_ilg;
   2911  11042      Erik 	ilm_t	*ilm;
   2912  11042      Erik 	ill_t	*ill;
   2913  11042      Erik 	boolean_t need_refrele;
   2914      0    stevel 
   2915  11042      Erik 	/*
   2916  11042      Erik 	 * Can not run if there is a conn_update_ill already running.
   2917  11042      Erik 	 * Wait for it to complete. Caller should have already set CONN_CLOSING
   2918  11042      Erik 	 * which prevents any new threads to run in conn_update_ill.
   2919  11042      Erik 	 */
   2920      0    stevel 	mutex_enter(&connp->conn_lock);
   2921  11042      Erik 	ASSERT(connp->conn_state_flags & CONN_CLOSING);
   2922  11042      Erik 	while (connp->conn_state_flags & CONN_UPDATE_ILL)
   2923  11042      Erik 		cv_wait(&connp->conn_cv, &connp->conn_lock);
   2924  11042      Erik 	mutex_exit(&connp->conn_lock);
   2925  11042      Erik 
   2926  11042      Erik 	rw_enter(&connp->conn_ilg_lock, RW_WRITER);
   2927  11042      Erik 	ilg = connp->conn_ilg;
   2928  11042      Erik 	held_ilg = NULL;
   2929  11042      Erik 	while (ilg != NULL) {
   2930  11042      Erik 		if (ilg->ilg_condemned) {
   2931  11042      Erik 			ilg = ilg->ilg_next;
   2932   8485     Peter 			continue;
   2933      0    stevel 		}
   2934  11042      Erik 		/* If the ilg is detached then no need to serialize */
   2935  11042      Erik 		if (ilg->ilg_ilm == NULL) {
   2936  11042      Erik 			next_ilg = ilg->ilg_next;
   2937  11042      Erik 			ilg_delete(connp, ilg, NULL);
   2938  11042      Erik 			ilg = next_ilg;
   2939  11042      Erik 			continue;
   2940  11042      Erik 		}
   2941  11042      Erik 		ill = ilg->ilg_ilm->ilm_ill;
   2942   8485     Peter 
   2943      0    stevel 		/*
   2944  11042      Erik 		 * In order to serialize on the ill we try to enter
   2945  11042      Erik 		 * and if that fails we unlock and relock and then
   2946  11042      Erik 		 * check that we still have an ilm.
   2947      0    stevel 		 */
   2948  11042      Erik 		need_refrele = B_FALSE;
   2949  11042      Erik 		if (!mutex_tryenter(&ill->ill_mcast_serializer)) {
   2950  11042      Erik 			ill_refhold(ill);
   2951  11042      Erik 			need_refrele = B_TRUE;
   2952  11042      Erik 			ilg_refhold(ilg);
   2953  11042      Erik 			if (held_ilg != NULL)
   2954  11042      Erik 				ilg_refrele(held_ilg);
   2955  11042      Erik 			held_ilg = ilg;
   2956  11042      Erik 			rw_exit(&connp->conn_ilg_lock);
   2957  11042      Erik 			mutex_enter(&ill->ill_mcast_serializer);
   2958  11042      Erik 			rw_enter(&connp->conn_ilg_lock, RW_WRITER);
   2959  11042      Erik 			if (ilg->ilg_condemned) {
   2960  11042      Erik 				ilg = ilg->ilg_next;
   2961  11042      Erik 				goto next;
   2962  11042      Erik 			}
   2963  11042      Erik 		}
   2964  11042      Erik 		ilm = ilg->ilg_ilm;
   2965  11042      Erik 		ilg->ilg_ilm = NULL;
   2966  11042      Erik 		next_ilg = ilg->ilg_next;
   2967  11042      Erik 		ilg_delete(connp, ilg, NULL);
   2968  11042      Erik 		ilg = next_ilg;
   2969  11042      Erik 		rw_exit(&connp->conn_ilg_lock);
   2970   8485     Peter 
   2971  11042      Erik 		if (ilm != NULL)
   2972  11042      Erik 			(void) ip_delmulti_serial(ilm, B_FALSE, B_TRUE);
   2973   8485     Peter 
   2974  11042      Erik 	next:
   2975  11042      Erik 		mutex_exit(&ill->ill_mcast_serializer);
   2976  11042      Erik 		if (need_refrele) {
   2977  11042      Erik 			/* Drop ill reference while we hold no locks */
   2978  11042      Erik 			ill_refrele(ill);
   2979      0    stevel 		}
   2980  11042      Erik 		rw_enter(&connp->conn_ilg_lock, RW_WRITER);
   2981      0    stevel 	}
   2982  11042      Erik 	if (held_ilg != NULL)
   2983  11042      Erik 		ilg_refrele(held_ilg);
   2984  11042      Erik 	rw_exit(&connp->conn_ilg_lock);
   2985      0    stevel }
   2986      0    stevel 
   2987      0    stevel /*
   2988  11042      Erik  * Attach the ilg to an ilm on the ill. If it fails we leave ilg_ill as NULL so
   2989  11042      Erik  * that a subsequent attempt can attach it.
   2990  11042      Erik  * Drops and reacquires conn_ilg_lock.
   2991      0    stevel  */
   2992      0    stevel static void
   2993  11042      Erik ilg_attach(conn_t *connp, ilg_t *ilg, ill_t *ill)
   2994      0    stevel {
   2995  11042      Erik 	ilg_stat_t	ilgstat;
   2996  11042      Erik 	slist_t		*new_filter;
   2997  11042      Erik 	int		new_fmode;
   2998  11042      Erik 	in6_addr_t	v6group;
   2999  11042      Erik 	ipaddr_t	ifaddr;
   3000  11042      Erik 	uint_t		ifindex;
   3001  11042      Erik 	ilm_t		*ilm;
   3002  11042      Erik 	int		error = 0;
   3003  11042      Erik 
   3004  11042      Erik 	ASSERT(RW_WRITE_HELD(&connp->conn_ilg_lock));
   3005  11042      Erik 	/*
   3006  11042      Erik 	 * Alloc buffer to copy new state into (see below) before
   3007  11042      Erik 	 * we make any changes, so we can bail if it fails.
   3008  11042      Erik 	 */
   3009  11042      Erik 	if ((new_filter = l_alloc()) == NULL)
   3010  11042      Erik 		return;
   3011      0    stevel 
   3012      0    stevel 	/*
   3013  11042      Erik 	 * Save copy of ilg's filter state to pass to other functions, so
   3014  11042      Erik 	 * we can release conn_ilg_lock now.
   3015  11042      Erik 	 * Set ilg_ill so that an unplumb can find us.
   3016      0    stevel 	 */
   3017  11042      Erik 	new_fmode = ilg->ilg_fmode;
   3018  11042      Erik 	l_copy(ilg->ilg_filter, new_filter);
   3019  11042      Erik 	v6group = ilg->ilg_v6group;
   3020  11042      Erik 	ifaddr = ilg->ilg_ifaddr;
   3021  11042      Erik 	ifindex = ilg->ilg_ifindex;
   3022  11042      Erik 	ilgstat = ILGSTAT_NEW;
   3023      0    stevel 
   3024  11042      Erik 	ilg->ilg_ill = ill;
   3025  11042      Erik 	ASSERT(ilg->ilg_ilm == NULL);
   3026  11042      Erik 	rw_exit(&connp->conn_ilg_lock);
   3027  11042      Erik 
   3028  11042      Erik 	ilm = ip_addmulti_serial(&v6group, ill, connp->conn_zoneid, ilgstat,
   3029  11042      Erik 	    new_fmode, new_filter, &error);
   3030  11042      Erik 	l_free(new_filter);
   3031  11042      Erik 
   3032  11042      Erik 	rw_enter(&connp->conn_ilg_lock, RW_WRITER);
   3033      0    stevel 	/*
   3034  11042      Erik 	 * Must look up the ilg again since we've not been holding
   3035  11042      Erik 	 * conn_ilg_lock. The ilg could have disappeared due to an unplumb
   3036  11042      Erik 	 * having called conn_update_ill, which can run once we dropped the
   3037  11042      Erik 	 * conn_ilg_lock above.
   3038      0    stevel 	 */
   3039  11042      Erik 	ilg = ilg_lookup(connp, &v6group, ifaddr, ifindex);
   3040  11042      Erik 	if (ilg == NULL) {
   3041  11042      Erik 		if (ilm != NULL) {
   3042  11042      Erik 			rw_exit(&connp->conn_ilg_lock);
   3043  11042      Erik 			(void) ip_delmulti_serial(ilm, B_FALSE,
   3044  11042      Erik 			    (ilgstat == ILGSTAT_NEW));
   3045  11042      Erik 			rw_enter(&connp->conn_ilg_lock, RW_WRITER);
   3046  11042      Erik 		}
   3047  11042      Erik 		return;
   3048      0    stevel 	}
   3049  11042      Erik 	if (ilm == NULL) {
   3050  11042      Erik 		ilg->ilg_ill = NULL;
   3051  11042      Erik 		return;
   3052      0    stevel 	}
   3053  11042      Erik 	ASSERT(ilg->ilg_ilm == NULL);
   3054  11042      Erik 	ilg->ilg_ilm = ilm;
   3055  11042      Erik 	ilm->ilm_ifaddr = ifaddr;	/* For netstat */
   3056      0    stevel }
   3057      0    stevel 
   3058      0    stevel /*
   3059      0    stevel  * Called when an ill is unplumbed to make sure that there are no
   3060  11042      Erik  * dangling conn references to that ill. In that case ill is non-NULL and
   3061  11042      Erik  * we make sure we remove all references to it.
   3062  11042      Erik  * Also called when we should revisit the ilg_ill used for multicast
   3063  11042      Erik  * memberships, in which case ill is NULL.
   3064  11042      Erik  *
   3065  11042      Erik  * conn is held by caller.
   3066  11042      Erik  *
   3067  11042      Erik  * Note that ipcl_walk only walks conns that are not yet condemned.
   3068  11042      Erik  * condemned conns can't be refheld. For this reason, conn must become clean
   3069  11042      Erik  * first, i.e. it must not refer to any ill/ire and then only set
   3070  11042      Erik  * condemned flag.
   3071  11042      Erik  *
   3072