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