17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 545916cd2Sjpk * Common Development and Distribution License (the "License"). 645916cd2Sjpk * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22*7f125a53SRavi Chandra Nallan * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved. 23*7f125a53SRavi Chandra Nallan * Copyright (c) 1990 Mentat Inc. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate #include <sys/types.h> 277c478bd9Sstevel@tonic-gate #include <sys/stream.h> 287c478bd9Sstevel@tonic-gate #include <sys/dlpi.h> 297c478bd9Sstevel@tonic-gate #include <sys/stropts.h> 307c478bd9Sstevel@tonic-gate #include <sys/strsun.h> 317c478bd9Sstevel@tonic-gate #include <sys/ddi.h> 327c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h> 33381a2a9aSdr146992 #include <sys/sdt.h> 347c478bd9Sstevel@tonic-gate #include <sys/zone.h> 357c478bd9Sstevel@tonic-gate 367c478bd9Sstevel@tonic-gate #include <sys/param.h> 377c478bd9Sstevel@tonic-gate #include <sys/socket.h> 387c478bd9Sstevel@tonic-gate #include <sys/sockio.h> 3945916cd2Sjpk #include <net/if.h> 407c478bd9Sstevel@tonic-gate #include <sys/systm.h> 41a1d6a1e6Sjarrett #include <sys/strsubr.h> 427c478bd9Sstevel@tonic-gate #include <net/route.h> 437c478bd9Sstevel@tonic-gate #include <netinet/in.h> 447c478bd9Sstevel@tonic-gate #include <net/if_dl.h> 457c478bd9Sstevel@tonic-gate #include <netinet/ip6.h> 467c478bd9Sstevel@tonic-gate #include <netinet/icmp6.h> 477c478bd9Sstevel@tonic-gate 487c478bd9Sstevel@tonic-gate #include <inet/common.h> 497c478bd9Sstevel@tonic-gate #include <inet/mi.h> 507c478bd9Sstevel@tonic-gate #include <inet/nd.h> 517c478bd9Sstevel@tonic-gate #include <inet/arp.h> 527c478bd9Sstevel@tonic-gate #include <inet/ip.h> 537c478bd9Sstevel@tonic-gate #include <inet/ip6.h> 547c478bd9Sstevel@tonic-gate #include <inet/ip_if.h> 557c478bd9Sstevel@tonic-gate #include <inet/ip_ndp.h> 567c478bd9Sstevel@tonic-gate #include <inet/ip_multi.h> 577c478bd9Sstevel@tonic-gate #include <inet/ipclassifier.h> 587c478bd9Sstevel@tonic-gate #include <inet/ipsec_impl.h> 597c478bd9Sstevel@tonic-gate #include <inet/sctp_ip.h> 607c478bd9Sstevel@tonic-gate #include <inet/ip_listutils.h> 61ff550d0eSmasputra #include <inet/udp_impl.h> 627c478bd9Sstevel@tonic-gate 637c478bd9Sstevel@tonic-gate /* igmpv3/mldv2 source filter manipulation */ 647c478bd9Sstevel@tonic-gate static void ilm_bld_flists(conn_t *conn, void *arg); 657c478bd9Sstevel@tonic-gate static void ilm_gen_filter(ilm_t *ilm, mcast_record_t *fmode, 667c478bd9Sstevel@tonic-gate slist_t *flist); 677c478bd9Sstevel@tonic-gate 68bd670b35SErik Nordmark static ilm_t *ilm_add(ill_t *ill, const in6_addr_t *group, 697c478bd9Sstevel@tonic-gate ilg_stat_t ilgstat, mcast_record_t ilg_fmode, slist_t *ilg_flist, 70e11c3f44Smeem zoneid_t zoneid); 717c478bd9Sstevel@tonic-gate static void ilm_delete(ilm_t *ilm); 72bd670b35SErik Nordmark static int ilm_numentries(ill_t *, const in6_addr_t *); 73bd670b35SErik Nordmark 74bd670b35SErik Nordmark static ilm_t *ip_addmulti_serial(const in6_addr_t *, ill_t *, zoneid_t, 75bd670b35SErik Nordmark ilg_stat_t, mcast_record_t, slist_t *, int *); 76bd670b35SErik Nordmark static ilm_t *ip_addmulti_impl(const in6_addr_t *, ill_t *, 77bd670b35SErik Nordmark zoneid_t, ilg_stat_t, mcast_record_t, slist_t *, int *); 78bd670b35SErik Nordmark static int ip_delmulti_serial(ilm_t *, boolean_t, boolean_t); 79bd670b35SErik Nordmark static int ip_delmulti_impl(ilm_t *, boolean_t, boolean_t); 80bd670b35SErik Nordmark 81bd670b35SErik Nordmark static int ip_ll_multireq(ill_t *ill, const in6_addr_t *group, 82bd670b35SErik Nordmark t_uscalar_t); 83bd670b35SErik Nordmark static ilg_t *ilg_lookup(conn_t *, const in6_addr_t *, ipaddr_t ifaddr, 84bd670b35SErik Nordmark uint_t ifindex); 85bd670b35SErik Nordmark 86bd670b35SErik Nordmark static int ilg_add(conn_t *connp, const in6_addr_t *group, 87bd670b35SErik Nordmark ipaddr_t ifaddr, uint_t ifindex, ill_t *ill, mcast_record_t fmode, 88bd670b35SErik Nordmark const in6_addr_t *v6src); 897c478bd9Sstevel@tonic-gate static void ilg_delete(conn_t *connp, ilg_t *ilg, const in6_addr_t *src); 907c478bd9Sstevel@tonic-gate static mblk_t *ill_create_dl(ill_t *ill, uint32_t dl_primitive, 91bd670b35SErik Nordmark uint32_t *addr_lenp, uint32_t *addr_offp); 92bd670b35SErik Nordmark static int ip_opt_delete_group_excl(conn_t *connp, 93bd670b35SErik Nordmark const in6_addr_t *v6group, ipaddr_t ifaddr, uint_t ifindex, 94bd670b35SErik Nordmark mcast_record_t fmode, const in6_addr_t *v6src); 95bd670b35SErik Nordmark 96bd670b35SErik Nordmark static ilm_t *ilm_lookup(ill_t *, const in6_addr_t *, zoneid_t); 97bd670b35SErik Nordmark 98bd670b35SErik Nordmark static int ip_msfilter_ill(conn_t *, mblk_t *, const ip_ioctl_cmd_t *, 99bd670b35SErik Nordmark ill_t **); 100bd670b35SErik Nordmark 101bd670b35SErik Nordmark static void ilg_check_detach(conn_t *, ill_t *); 102f1c454b4SSowmini Varadhan static void ilg_check_reattach(conn_t *, ill_t *); 1037c478bd9Sstevel@tonic-gate 1047c478bd9Sstevel@tonic-gate /* 1057c478bd9Sstevel@tonic-gate * MT notes: 1067c478bd9Sstevel@tonic-gate * 1077c478bd9Sstevel@tonic-gate * Multicast joins operate on both the ilg and ilm structures. Multiple 1087c478bd9Sstevel@tonic-gate * threads operating on an conn (socket) trying to do multicast joins 1097c478bd9Sstevel@tonic-gate * need to synchronize when operating on the ilg. Multiple threads 1107c478bd9Sstevel@tonic-gate * potentially operating on different conn (socket endpoints) trying to 1117c478bd9Sstevel@tonic-gate * do multicast joins could eventually end up trying to manipulate the 112bd670b35SErik Nordmark * ilm simulatenously and need to synchronize on the access to the ilm. 113bd670b35SErik Nordmark * The access and lookup of the ilm, as well as other ill multicast state, 114bd670b35SErik Nordmark * is under ill_mcast_lock. 115bd670b35SErik Nordmark * The modifications and lookup of ilg entries is serialized using conn_ilg_lock 116bd670b35SErik Nordmark * rwlock. An ilg will not be freed until ilg_refcnt drops to zero. 117bd670b35SErik Nordmark * 118bd670b35SErik Nordmark * In some cases we hold ill_mcast_lock and then acquire conn_ilg_lock, but 119bd670b35SErik Nordmark * never the other way around. 1207c478bd9Sstevel@tonic-gate * 1217c478bd9Sstevel@tonic-gate * An ilm is an IP data structure used to track multicast join/leave. 1227c478bd9Sstevel@tonic-gate * An ilm is associated with a <multicast group, ipif> tuple in IPv4 and 1237c478bd9Sstevel@tonic-gate * with just <multicast group> in IPv6. ilm_refcnt is the number of ilg's 124bd670b35SErik Nordmark * referencing the ilm. 125bd670b35SErik Nordmark * The modifications and lookup of ilm entries is serialized using the 126bd670b35SErik Nordmark * ill_mcast_lock rwlock; that lock handles all the igmp/mld modifications 127bd670b35SErik Nordmark * of the ilm state. 128bd670b35SErik Nordmark * ilms are created / destroyed only as writer. ilms 129bd670b35SErik Nordmark * are not passed around. The datapath (anything outside of this file 130bd670b35SErik Nordmark * and igmp.c) use functions that do not return ilms - just the number 131bd670b35SErik Nordmark * of members. So we don't need a dynamic refcount of the number 1327c478bd9Sstevel@tonic-gate * of threads holding reference to an ilm. 1337c478bd9Sstevel@tonic-gate * 134bd670b35SErik Nordmark * In the cases where we serially access the ilg and ilm, which happens when 135bd670b35SErik Nordmark * we handle the applications requests to join or leave groups and sources, 136bd670b35SErik Nordmark * we use the ill_mcast_serializer mutex to ensure that a multithreaded 137bd670b35SErik Nordmark * application which does concurrent joins and/or leaves on the same group on 138bd670b35SErik Nordmark * the same socket always results in a consistent order for the ilg and ilm 139bd670b35SErik Nordmark * modifications. 1407c478bd9Sstevel@tonic-gate * 141bd670b35SErik Nordmark * When a multicast operation results in needing to send a message to 142bd670b35SErik Nordmark * the driver (to join/leave a L2 multicast address), we use ill_dlpi_queue() 143bd670b35SErik Nordmark * which serialized the DLPI requests. The IGMP/MLD code uses ill_mcast_queue() 144bd670b35SErik Nordmark * to send IGMP/MLD IP packet to avoid dropping the lock just to send a packet. 1457c478bd9Sstevel@tonic-gate */ 1467c478bd9Sstevel@tonic-gate 1477c478bd9Sstevel@tonic-gate #define GETSTRUCT(structure, number) \ 1487c478bd9Sstevel@tonic-gate ((structure *)mi_zalloc(sizeof (structure) * (number))) 1497c478bd9Sstevel@tonic-gate 150bd670b35SErik Nordmark /* 151bd670b35SErik Nordmark * Caller must ensure that the ilg has not been condemned 152bd670b35SErik Nordmark * The condemned flag is only set in ilg_delete under conn_ilg_lock. 153bd670b35SErik Nordmark * 154bd670b35SErik Nordmark * The caller must hold conn_ilg_lock as writer. 155bd670b35SErik Nordmark */ 156bd670b35SErik Nordmark static void 157bd670b35SErik Nordmark ilg_refhold(ilg_t *ilg) 158bd670b35SErik Nordmark { 159bd670b35SErik Nordmark ASSERT(ilg->ilg_refcnt != 0); 160bd670b35SErik Nordmark ASSERT(!ilg->ilg_condemned); 161bd670b35SErik Nordmark ASSERT(RW_WRITE_HELD(&ilg->ilg_connp->conn_ilg_lock)); 162bd670b35SErik Nordmark 163bd670b35SErik Nordmark ilg->ilg_refcnt++; 164bd670b35SErik Nordmark } 165bd670b35SErik Nordmark 166bd670b35SErik Nordmark static void 167bd670b35SErik Nordmark ilg_inactive(ilg_t *ilg) 168bd670b35SErik Nordmark { 169bd670b35SErik Nordmark ASSERT(ilg->ilg_ill == NULL); 170bd670b35SErik Nordmark ASSERT(ilg->ilg_ilm == NULL); 171bd670b35SErik Nordmark ASSERT(ilg->ilg_filter == NULL); 172bd670b35SErik Nordmark ASSERT(ilg->ilg_condemned); 173bd670b35SErik Nordmark 174bd670b35SErik Nordmark /* Unlink from list */ 175bd670b35SErik Nordmark *ilg->ilg_ptpn = ilg->ilg_next; 176bd670b35SErik Nordmark if (ilg->ilg_next != NULL) 177bd670b35SErik Nordmark ilg->ilg_next->ilg_ptpn = ilg->ilg_ptpn; 178bd670b35SErik Nordmark ilg->ilg_next = NULL; 179bd670b35SErik Nordmark ilg->ilg_ptpn = NULL; 180bd670b35SErik Nordmark 181bd670b35SErik Nordmark ilg->ilg_connp = NULL; 182bd670b35SErik Nordmark kmem_free(ilg, sizeof (*ilg)); 183bd670b35SErik Nordmark } 1847c478bd9Sstevel@tonic-gate 1857c478bd9Sstevel@tonic-gate /* 186bd670b35SErik Nordmark * The caller must hold conn_ilg_lock as writer. 187bd670b35SErik Nordmark */ 188bd670b35SErik Nordmark static void 189bd670b35SErik Nordmark ilg_refrele(ilg_t *ilg) 190bd670b35SErik Nordmark { 191bd670b35SErik Nordmark ASSERT(RW_WRITE_HELD(&ilg->ilg_connp->conn_ilg_lock)); 192bd670b35SErik Nordmark ASSERT(ilg->ilg_refcnt != 0); 193bd670b35SErik Nordmark if (--ilg->ilg_refcnt == 0) 194bd670b35SErik Nordmark ilg_inactive(ilg); 195bd670b35SErik Nordmark } 196bd670b35SErik Nordmark 197bd670b35SErik Nordmark /* 198bd670b35SErik Nordmark * Acquire reference on ilg and drop reference on held_ilg. 199bd670b35SErik Nordmark * In the case when held_ilg is the same as ilg we already have 200bd670b35SErik Nordmark * a reference, but the held_ilg might be condemned. In that case 201bd670b35SErik Nordmark * we avoid the ilg_refhold/rele so that we can assert in ire_refhold 202bd670b35SErik Nordmark * that the ilg isn't condemned. 203bd670b35SErik Nordmark */ 204bd670b35SErik Nordmark static void 205bd670b35SErik Nordmark ilg_transfer_hold(ilg_t *held_ilg, ilg_t *ilg) 206bd670b35SErik Nordmark { 207bd670b35SErik Nordmark if (held_ilg == ilg) 208bd670b35SErik Nordmark return; 209bd670b35SErik Nordmark 210bd670b35SErik Nordmark ilg_refhold(ilg); 211bd670b35SErik Nordmark if (held_ilg != NULL) 212bd670b35SErik Nordmark ilg_refrele(held_ilg); 213bd670b35SErik Nordmark } 214bd670b35SErik Nordmark 215bd670b35SErik Nordmark /* 216bd670b35SErik Nordmark * Allocate a new ilg_t and links it into conn_ilg. 217bd670b35SErik Nordmark * Returns NULL on failure, in which case `*errp' will be 218e11c3f44Smeem * filled in with the reason. 2197c478bd9Sstevel@tonic-gate * 220bd670b35SErik Nordmark * Assumes connp->conn_ilg_lock is held. 2217c478bd9Sstevel@tonic-gate */ 2227c478bd9Sstevel@tonic-gate static ilg_t * 223e11c3f44Smeem conn_ilg_alloc(conn_t *connp, int *errp) 2247c478bd9Sstevel@tonic-gate { 225bd670b35SErik Nordmark ilg_t *ilg; 2267c478bd9Sstevel@tonic-gate 227bd670b35SErik Nordmark ASSERT(RW_WRITE_HELD(&connp->conn_ilg_lock)); 2287c478bd9Sstevel@tonic-gate 229e11c3f44Smeem /* 230e11c3f44Smeem * If CONN_CLOSING is set, conn_ilg cleanup has begun and we must not 231e11c3f44Smeem * create any ilgs. 232e11c3f44Smeem */ 233e11c3f44Smeem if (connp->conn_state_flags & CONN_CLOSING) { 234e11c3f44Smeem *errp = EINVAL; 235e11c3f44Smeem return (NULL); 236e11c3f44Smeem } 237e11c3f44Smeem 238bd670b35SErik Nordmark ilg = kmem_zalloc(sizeof (ilg_t), KM_NOSLEEP); 239bd670b35SErik Nordmark if (ilg == NULL) { 240e11c3f44Smeem *errp = ENOMEM; 2417c478bd9Sstevel@tonic-gate return (NULL); 242e11c3f44Smeem } 2437c478bd9Sstevel@tonic-gate 244bd670b35SErik Nordmark ilg->ilg_refcnt = 1; 245bd670b35SErik Nordmark 246bd670b35SErik Nordmark /* Insert at head */ 247bd670b35SErik Nordmark if (connp->conn_ilg != NULL) 248bd670b35SErik Nordmark connp->conn_ilg->ilg_ptpn = &ilg->ilg_next; 249bd670b35SErik Nordmark ilg->ilg_next = connp->conn_ilg; 250bd670b35SErik Nordmark ilg->ilg_ptpn = &connp->conn_ilg; 251bd670b35SErik Nordmark connp->conn_ilg = ilg; 252bd670b35SErik Nordmark 253bd670b35SErik Nordmark ilg->ilg_connp = connp; 254bd670b35SErik Nordmark return (ilg); 2557c478bd9Sstevel@tonic-gate } 2567c478bd9Sstevel@tonic-gate 2577c478bd9Sstevel@tonic-gate typedef struct ilm_fbld_s { 2587c478bd9Sstevel@tonic-gate ilm_t *fbld_ilm; 2597c478bd9Sstevel@tonic-gate int fbld_in_cnt; 2607c478bd9Sstevel@tonic-gate int fbld_ex_cnt; 2617c478bd9Sstevel@tonic-gate slist_t fbld_in; 2627c478bd9Sstevel@tonic-gate slist_t fbld_ex; 2637c478bd9Sstevel@tonic-gate boolean_t fbld_in_overflow; 2647c478bd9Sstevel@tonic-gate } ilm_fbld_t; 2657c478bd9Sstevel@tonic-gate 266bd670b35SErik Nordmark /* 267bd670b35SErik Nordmark * Caller must hold ill_mcast_lock 268bd670b35SErik Nordmark */ 2697c478bd9Sstevel@tonic-gate static void 270bd670b35SErik Nordmark ilm_bld_flists(conn_t *connp, void *arg) 2717c478bd9Sstevel@tonic-gate { 272bd670b35SErik Nordmark ilg_t *ilg; 2737c478bd9Sstevel@tonic-gate ilm_fbld_t *fbld = (ilm_fbld_t *)(arg); 2747c478bd9Sstevel@tonic-gate ilm_t *ilm = fbld->fbld_ilm; 2757c478bd9Sstevel@tonic-gate in6_addr_t *v6group = &ilm->ilm_v6addr; 2767c478bd9Sstevel@tonic-gate 277bd670b35SErik Nordmark if (connp->conn_ilg == NULL) 2787c478bd9Sstevel@tonic-gate return; 2797c478bd9Sstevel@tonic-gate 2807c478bd9Sstevel@tonic-gate /* 2817c478bd9Sstevel@tonic-gate * Since we can't break out of the ipcl_walk once started, we still 2827c478bd9Sstevel@tonic-gate * have to look at every conn. But if we've already found one 2837c478bd9Sstevel@tonic-gate * (EXCLUDE, NULL) list, there's no need to keep checking individual 2847c478bd9Sstevel@tonic-gate * ilgs--that will be our state. 2857c478bd9Sstevel@tonic-gate */ 2867c478bd9Sstevel@tonic-gate if (fbld->fbld_ex_cnt > 0 && fbld->fbld_ex.sl_numsrc == 0) 2877c478bd9Sstevel@tonic-gate return; 2887c478bd9Sstevel@tonic-gate 2897c478bd9Sstevel@tonic-gate /* 2907c478bd9Sstevel@tonic-gate * Check this conn's ilgs to see if any are interested in our 2917c478bd9Sstevel@tonic-gate * ilm (group, interface match). If so, update the master 2927c478bd9Sstevel@tonic-gate * include and exclude lists we're building in the fbld struct 2937c478bd9Sstevel@tonic-gate * with this ilg's filter info. 294bd670b35SErik Nordmark * 295bd670b35SErik Nordmark * Note that the caller has already serialized on the ill we care 296bd670b35SErik Nordmark * about. 2977c478bd9Sstevel@tonic-gate */ 298bd670b35SErik Nordmark ASSERT(MUTEX_HELD(&ilm->ilm_ill->ill_mcast_serializer)); 299bd670b35SErik Nordmark 300bd670b35SErik Nordmark rw_enter(&connp->conn_ilg_lock, RW_READER); 301bd670b35SErik Nordmark for (ilg = connp->conn_ilg; ilg != NULL; ilg = ilg->ilg_next) { 302bd670b35SErik Nordmark if (ilg->ilg_condemned) 303bd670b35SErik Nordmark continue; 304bd670b35SErik Nordmark 305bd670b35SErik Nordmark /* 306bd670b35SErik Nordmark * Since we are under the ill_mcast_serializer we know 307bd670b35SErik Nordmark * that any ilg+ilm operations on this ilm have either 308bd670b35SErik Nordmark * not started or completed, except for the last ilg 309bd670b35SErik Nordmark * (the one that caused us to be called) which doesn't 310bd670b35SErik Nordmark * have ilg_ilm set yet. Hence we compare using ilg_ill 311bd670b35SErik Nordmark * and the address. 312bd670b35SErik Nordmark */ 3137c478bd9Sstevel@tonic-gate if ((ilg->ilg_ill == ilm->ilm_ill) && 3147c478bd9Sstevel@tonic-gate IN6_ARE_ADDR_EQUAL(&ilg->ilg_v6group, v6group)) { 3157c478bd9Sstevel@tonic-gate if (ilg->ilg_fmode == MODE_IS_INCLUDE) { 3167c478bd9Sstevel@tonic-gate fbld->fbld_in_cnt++; 3177c478bd9Sstevel@tonic-gate if (!fbld->fbld_in_overflow) 3187c478bd9Sstevel@tonic-gate l_union_in_a(&fbld->fbld_in, 3197c478bd9Sstevel@tonic-gate ilg->ilg_filter, 3207c478bd9Sstevel@tonic-gate &fbld->fbld_in_overflow); 3217c478bd9Sstevel@tonic-gate } else { 3227c478bd9Sstevel@tonic-gate fbld->fbld_ex_cnt++; 3237c478bd9Sstevel@tonic-gate /* 3247c478bd9Sstevel@tonic-gate * On the first exclude list, don't try to do 3257c478bd9Sstevel@tonic-gate * an intersection, as the master exclude list 3267c478bd9Sstevel@tonic-gate * is intentionally empty. If the master list 3277c478bd9Sstevel@tonic-gate * is still empty on later iterations, that 3287c478bd9Sstevel@tonic-gate * means we have at least one ilg with an empty 3297c478bd9Sstevel@tonic-gate * exclude list, so that should be reflected 3307c478bd9Sstevel@tonic-gate * when we take the intersection. 3317c478bd9Sstevel@tonic-gate */ 3327c478bd9Sstevel@tonic-gate if (fbld->fbld_ex_cnt == 1) { 3337c478bd9Sstevel@tonic-gate if (ilg->ilg_filter != NULL) 3347c478bd9Sstevel@tonic-gate l_copy(ilg->ilg_filter, 3357c478bd9Sstevel@tonic-gate &fbld->fbld_ex); 3367c478bd9Sstevel@tonic-gate } else { 3377c478bd9Sstevel@tonic-gate l_intersection_in_a(&fbld->fbld_ex, 3387c478bd9Sstevel@tonic-gate ilg->ilg_filter); 3397c478bd9Sstevel@tonic-gate } 3407c478bd9Sstevel@tonic-gate } 3417c478bd9Sstevel@tonic-gate /* there will only be one match, so break now. */ 3427c478bd9Sstevel@tonic-gate break; 3437c478bd9Sstevel@tonic-gate } 3447c478bd9Sstevel@tonic-gate } 345bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 3467c478bd9Sstevel@tonic-gate } 3477c478bd9Sstevel@tonic-gate 348bd670b35SErik Nordmark /* 349bd670b35SErik Nordmark * Caller must hold ill_mcast_lock 350bd670b35SErik Nordmark */ 3517c478bd9Sstevel@tonic-gate static void 3527c478bd9Sstevel@tonic-gate ilm_gen_filter(ilm_t *ilm, mcast_record_t *fmode, slist_t *flist) 3537c478bd9Sstevel@tonic-gate { 3547c478bd9Sstevel@tonic-gate ilm_fbld_t fbld; 355f4b3ec61Sdh155122 ip_stack_t *ipst = ilm->ilm_ipst; 3567c478bd9Sstevel@tonic-gate 3577c478bd9Sstevel@tonic-gate fbld.fbld_ilm = ilm; 3587c478bd9Sstevel@tonic-gate fbld.fbld_in_cnt = fbld.fbld_ex_cnt = 0; 3597c478bd9Sstevel@tonic-gate fbld.fbld_in.sl_numsrc = fbld.fbld_ex.sl_numsrc = 0; 3607c478bd9Sstevel@tonic-gate fbld.fbld_in_overflow = B_FALSE; 3617c478bd9Sstevel@tonic-gate 3627c478bd9Sstevel@tonic-gate /* first, construct our master include and exclude lists */ 363f4b3ec61Sdh155122 ipcl_walk(ilm_bld_flists, (caddr_t)&fbld, ipst); 3647c478bd9Sstevel@tonic-gate 3657c478bd9Sstevel@tonic-gate /* now use those master lists to generate the interface filter */ 3667c478bd9Sstevel@tonic-gate 3677c478bd9Sstevel@tonic-gate /* if include list overflowed, filter is (EXCLUDE, NULL) */ 3687c478bd9Sstevel@tonic-gate if (fbld.fbld_in_overflow) { 3697c478bd9Sstevel@tonic-gate *fmode = MODE_IS_EXCLUDE; 3707c478bd9Sstevel@tonic-gate flist->sl_numsrc = 0; 3717c478bd9Sstevel@tonic-gate return; 3727c478bd9Sstevel@tonic-gate } 3737c478bd9Sstevel@tonic-gate 3747c478bd9Sstevel@tonic-gate /* if nobody interested, interface filter is (INCLUDE, NULL) */ 3757c478bd9Sstevel@tonic-gate if (fbld.fbld_in_cnt == 0 && fbld.fbld_ex_cnt == 0) { 3767c478bd9Sstevel@tonic-gate *fmode = MODE_IS_INCLUDE; 3777c478bd9Sstevel@tonic-gate flist->sl_numsrc = 0; 3787c478bd9Sstevel@tonic-gate return; 3797c478bd9Sstevel@tonic-gate } 3807c478bd9Sstevel@tonic-gate 3817c478bd9Sstevel@tonic-gate /* 3827c478bd9Sstevel@tonic-gate * If there are no exclude lists, then the interface filter 3837c478bd9Sstevel@tonic-gate * is INCLUDE, with its filter list equal to fbld_in. A single 3847c478bd9Sstevel@tonic-gate * exclude list makes the interface filter EXCLUDE, with its 3857c478bd9Sstevel@tonic-gate * filter list equal to (fbld_ex - fbld_in). 3867c478bd9Sstevel@tonic-gate */ 3877c478bd9Sstevel@tonic-gate if (fbld.fbld_ex_cnt == 0) { 3887c478bd9Sstevel@tonic-gate *fmode = MODE_IS_INCLUDE; 3897c478bd9Sstevel@tonic-gate l_copy(&fbld.fbld_in, flist); 3907c478bd9Sstevel@tonic-gate } else { 3917c478bd9Sstevel@tonic-gate *fmode = MODE_IS_EXCLUDE; 3927c478bd9Sstevel@tonic-gate l_difference(&fbld.fbld_ex, &fbld.fbld_in, flist); 3937c478bd9Sstevel@tonic-gate } 3947c478bd9Sstevel@tonic-gate } 3957c478bd9Sstevel@tonic-gate 396bd670b35SErik Nordmark /* 397bd670b35SErik Nordmark * Caller must hold ill_mcast_lock 398bd670b35SErik Nordmark */ 3997c478bd9Sstevel@tonic-gate static int 400bd670b35SErik Nordmark ilm_update_add(ilm_t *ilm, ilg_stat_t ilgstat, slist_t *ilg_flist) 4017c478bd9Sstevel@tonic-gate { 4027c478bd9Sstevel@tonic-gate mcast_record_t fmode; 4037c478bd9Sstevel@tonic-gate slist_t *flist; 4047c478bd9Sstevel@tonic-gate boolean_t fdefault; 4057c478bd9Sstevel@tonic-gate char buf[INET6_ADDRSTRLEN]; 406bd670b35SErik Nordmark ill_t *ill = ilm->ilm_ill; 4077c478bd9Sstevel@tonic-gate 4087c478bd9Sstevel@tonic-gate /* 4097c478bd9Sstevel@tonic-gate * There are several cases where the ilm's filter state 4107c478bd9Sstevel@tonic-gate * defaults to (EXCLUDE, NULL): 4117c478bd9Sstevel@tonic-gate * - we've had previous joins without associated ilgs 4127c478bd9Sstevel@tonic-gate * - this join has no associated ilg 4137c478bd9Sstevel@tonic-gate * - the ilg's filter state is (EXCLUDE, NULL) 4147c478bd9Sstevel@tonic-gate */ 4157c478bd9Sstevel@tonic-gate fdefault = (ilm->ilm_no_ilg_cnt > 0) || 4167c478bd9Sstevel@tonic-gate (ilgstat == ILGSTAT_NONE) || SLIST_IS_EMPTY(ilg_flist); 4177c478bd9Sstevel@tonic-gate 4187c478bd9Sstevel@tonic-gate /* attempt mallocs (if needed) before doing anything else */ 4197c478bd9Sstevel@tonic-gate if ((flist = l_alloc()) == NULL) 4207c478bd9Sstevel@tonic-gate return (ENOMEM); 4217c478bd9Sstevel@tonic-gate if (!fdefault && ilm->ilm_filter == NULL) { 4227c478bd9Sstevel@tonic-gate ilm->ilm_filter = l_alloc(); 4237c478bd9Sstevel@tonic-gate if (ilm->ilm_filter == NULL) { 4247c478bd9Sstevel@tonic-gate l_free(flist); 4257c478bd9Sstevel@tonic-gate return (ENOMEM); 4267c478bd9Sstevel@tonic-gate } 4277c478bd9Sstevel@tonic-gate } 4287c478bd9Sstevel@tonic-gate 4297c478bd9Sstevel@tonic-gate if (ilgstat != ILGSTAT_CHANGE) 4307c478bd9Sstevel@tonic-gate ilm->ilm_refcnt++; 4317c478bd9Sstevel@tonic-gate 4327c478bd9Sstevel@tonic-gate if (ilgstat == ILGSTAT_NONE) 4337c478bd9Sstevel@tonic-gate ilm->ilm_no_ilg_cnt++; 4347c478bd9Sstevel@tonic-gate 4357c478bd9Sstevel@tonic-gate /* 4367c478bd9Sstevel@tonic-gate * Determine new filter state. If it's not the default 4377c478bd9Sstevel@tonic-gate * (EXCLUDE, NULL), we must walk the conn list to find 4387c478bd9Sstevel@tonic-gate * any ilgs interested in this group, and re-build the 4397c478bd9Sstevel@tonic-gate * ilm filter. 4407c478bd9Sstevel@tonic-gate */ 4417c478bd9Sstevel@tonic-gate if (fdefault) { 4427c478bd9Sstevel@tonic-gate fmode = MODE_IS_EXCLUDE; 4437c478bd9Sstevel@tonic-gate flist->sl_numsrc = 0; 4447c478bd9Sstevel@tonic-gate } else { 4457c478bd9Sstevel@tonic-gate ilm_gen_filter(ilm, &fmode, flist); 4467c478bd9Sstevel@tonic-gate } 4477c478bd9Sstevel@tonic-gate 4487c478bd9Sstevel@tonic-gate /* make sure state actually changed; nothing to do if not. */ 4497c478bd9Sstevel@tonic-gate if ((ilm->ilm_fmode == fmode) && 4507c478bd9Sstevel@tonic-gate !lists_are_different(ilm->ilm_filter, flist)) { 4517c478bd9Sstevel@tonic-gate l_free(flist); 4527c478bd9Sstevel@tonic-gate return (0); 4537c478bd9Sstevel@tonic-gate } 4547c478bd9Sstevel@tonic-gate 4557c478bd9Sstevel@tonic-gate /* send the state change report */ 45648de1bd2Skcpoon if (!IS_LOOPBACK(ill)) { 457bd670b35SErik Nordmark if (ill->ill_isv6) 4587c478bd9Sstevel@tonic-gate mld_statechange(ilm, fmode, flist); 4597c478bd9Sstevel@tonic-gate else 4607c478bd9Sstevel@tonic-gate igmp_statechange(ilm, fmode, flist); 4617c478bd9Sstevel@tonic-gate } 4627c478bd9Sstevel@tonic-gate 4637c478bd9Sstevel@tonic-gate /* update the ilm state */ 4647c478bd9Sstevel@tonic-gate ilm->ilm_fmode = fmode; 4657c478bd9Sstevel@tonic-gate if (flist->sl_numsrc > 0) 4667c478bd9Sstevel@tonic-gate l_copy(flist, ilm->ilm_filter); 4677c478bd9Sstevel@tonic-gate else 4687c478bd9Sstevel@tonic-gate CLEAR_SLIST(ilm->ilm_filter); 4697c478bd9Sstevel@tonic-gate 4707c478bd9Sstevel@tonic-gate ip1dbg(("ilm_update: new if filter mode %d, group %s\n", ilm->ilm_fmode, 4717c478bd9Sstevel@tonic-gate inet_ntop(AF_INET6, &ilm->ilm_v6addr, buf, sizeof (buf)))); 4727c478bd9Sstevel@tonic-gate 4737c478bd9Sstevel@tonic-gate l_free(flist); 4747c478bd9Sstevel@tonic-gate return (0); 4757c478bd9Sstevel@tonic-gate } 4767c478bd9Sstevel@tonic-gate 477bd670b35SErik Nordmark /* 478bd670b35SErik Nordmark * Caller must hold ill_mcast_lock 479bd670b35SErik Nordmark */ 4807c478bd9Sstevel@tonic-gate static int 481bd670b35SErik Nordmark ilm_update_del(ilm_t *ilm) 4827c478bd9Sstevel@tonic-gate { 4837c478bd9Sstevel@tonic-gate mcast_record_t fmode; 4847c478bd9Sstevel@tonic-gate slist_t *flist; 485bd670b35SErik Nordmark ill_t *ill = ilm->ilm_ill; 4867c478bd9Sstevel@tonic-gate 4877c478bd9Sstevel@tonic-gate ip1dbg(("ilm_update_del: still %d left; updating state\n", 4887c478bd9Sstevel@tonic-gate ilm->ilm_refcnt)); 4897c478bd9Sstevel@tonic-gate 4907c478bd9Sstevel@tonic-gate if ((flist = l_alloc()) == NULL) 4917c478bd9Sstevel@tonic-gate return (ENOMEM); 4927c478bd9Sstevel@tonic-gate 4937c478bd9Sstevel@tonic-gate /* 4947c478bd9Sstevel@tonic-gate * If present, the ilg in question has already either been 4957c478bd9Sstevel@tonic-gate * updated or removed from our list; so all we need to do 4967c478bd9Sstevel@tonic-gate * now is walk the list to update the ilm filter state. 4977c478bd9Sstevel@tonic-gate * 4987c478bd9Sstevel@tonic-gate * Skip the list walk if we have any no-ilg joins, which 4997c478bd9Sstevel@tonic-gate * cause the filter state to revert to (EXCLUDE, NULL). 5007c478bd9Sstevel@tonic-gate */ 5017c478bd9Sstevel@tonic-gate if (ilm->ilm_no_ilg_cnt != 0) { 5027c478bd9Sstevel@tonic-gate fmode = MODE_IS_EXCLUDE; 5037c478bd9Sstevel@tonic-gate flist->sl_numsrc = 0; 5047c478bd9Sstevel@tonic-gate } else { 5057c478bd9Sstevel@tonic-gate ilm_gen_filter(ilm, &fmode, flist); 5067c478bd9Sstevel@tonic-gate } 5077c478bd9Sstevel@tonic-gate 5087c478bd9Sstevel@tonic-gate /* check to see if state needs to be updated */ 5097c478bd9Sstevel@tonic-gate if ((ilm->ilm_fmode == fmode) && 5107c478bd9Sstevel@tonic-gate (!lists_are_different(ilm->ilm_filter, flist))) { 5117c478bd9Sstevel@tonic-gate l_free(flist); 5127c478bd9Sstevel@tonic-gate return (0); 5137c478bd9Sstevel@tonic-gate } 5147c478bd9Sstevel@tonic-gate 51548de1bd2Skcpoon if (!IS_LOOPBACK(ill)) { 516bd670b35SErik Nordmark if (ill->ill_isv6) 5177c478bd9Sstevel@tonic-gate mld_statechange(ilm, fmode, flist); 5187c478bd9Sstevel@tonic-gate else 5197c478bd9Sstevel@tonic-gate igmp_statechange(ilm, fmode, flist); 5207c478bd9Sstevel@tonic-gate } 5217c478bd9Sstevel@tonic-gate 5227c478bd9Sstevel@tonic-gate ilm->ilm_fmode = fmode; 5237c478bd9Sstevel@tonic-gate if (flist->sl_numsrc > 0) { 5247c478bd9Sstevel@tonic-gate if (ilm->ilm_filter == NULL) { 5257c478bd9Sstevel@tonic-gate ilm->ilm_filter = l_alloc(); 5267c478bd9Sstevel@tonic-gate if (ilm->ilm_filter == NULL) { 5277c478bd9Sstevel@tonic-gate char buf[INET6_ADDRSTRLEN]; 5287c478bd9Sstevel@tonic-gate ip1dbg(("ilm_update_del: failed to alloc ilm " 5297c478bd9Sstevel@tonic-gate "filter; no source filtering for %s on %s", 5307c478bd9Sstevel@tonic-gate inet_ntop(AF_INET6, &ilm->ilm_v6addr, 5317c478bd9Sstevel@tonic-gate buf, sizeof (buf)), ill->ill_name)); 5327c478bd9Sstevel@tonic-gate ilm->ilm_fmode = MODE_IS_EXCLUDE; 5337c478bd9Sstevel@tonic-gate l_free(flist); 5347c478bd9Sstevel@tonic-gate return (0); 5357c478bd9Sstevel@tonic-gate } 5367c478bd9Sstevel@tonic-gate } 5377c478bd9Sstevel@tonic-gate l_copy(flist, ilm->ilm_filter); 5387c478bd9Sstevel@tonic-gate } else { 5397c478bd9Sstevel@tonic-gate CLEAR_SLIST(ilm->ilm_filter); 5407c478bd9Sstevel@tonic-gate } 5417c478bd9Sstevel@tonic-gate 5427c478bd9Sstevel@tonic-gate l_free(flist); 5437c478bd9Sstevel@tonic-gate return (0); 5447c478bd9Sstevel@tonic-gate } 5457c478bd9Sstevel@tonic-gate 5467c478bd9Sstevel@tonic-gate /* 547bd670b35SErik Nordmark * Create/update the ilm for the group/ill. Used by other parts of IP to 548bd670b35SErik Nordmark * do the ILGSTAT_NONE (no ilg), MODE_IS_EXCLUDE, with no slist join. 549bd670b35SErik Nordmark * Returns with a refhold on the ilm. 5507c478bd9Sstevel@tonic-gate * 551bd670b35SErik Nordmark * The unspecified address means all multicast addresses for in both the 552bd670b35SErik Nordmark * case of IPv4 and IPv6. 553bd670b35SErik Nordmark * 554bd670b35SErik Nordmark * The caller should have already mapped an IPMP under ill to the upper. 555bd670b35SErik Nordmark */ 556bd670b35SErik Nordmark ilm_t * 557bd670b35SErik Nordmark ip_addmulti(const in6_addr_t *v6group, ill_t *ill, zoneid_t zoneid, 558bd670b35SErik Nordmark int *errorp) 559bd670b35SErik Nordmark { 560bd670b35SErik Nordmark ilm_t *ilm; 561bd670b35SErik Nordmark 562bd670b35SErik Nordmark /* Acquire serializer to keep assert in ilm_bld_flists happy */ 563bd670b35SErik Nordmark mutex_enter(&ill->ill_mcast_serializer); 564bd670b35SErik Nordmark ilm = ip_addmulti_serial(v6group, ill, zoneid, ILGSTAT_NONE, 565bd670b35SErik Nordmark MODE_IS_EXCLUDE, NULL, errorp); 566bd670b35SErik Nordmark mutex_exit(&ill->ill_mcast_serializer); 567f1c454b4SSowmini Varadhan /* 568f1c454b4SSowmini Varadhan * Now that all locks have been dropped, we can send any 569f1c454b4SSowmini Varadhan * deferred/queued DLPI or IP packets 570f1c454b4SSowmini Varadhan */ 571f1c454b4SSowmini Varadhan ill_mcast_send_queued(ill); 572f1c454b4SSowmini Varadhan ill_dlpi_send_queued(ill); 573bd670b35SErik Nordmark return (ilm); 574bd670b35SErik Nordmark } 575bd670b35SErik Nordmark 576bd670b35SErik Nordmark /* 577bd670b35SErik Nordmark * Create/update the ilm for the group/ill. If ILGSTAT_CHANGE is not set 578bd670b35SErik Nordmark * then this returns with a refhold on the ilm. 579bd670b35SErik Nordmark * 580bd670b35SErik Nordmark * Internal routine which assumes the caller has already acquired 581f1c454b4SSowmini Varadhan * ill_mcast_serializer. It is the caller's responsibility to send out 582f1c454b4SSowmini Varadhan * queued DLPI/multicast packets after all locks are dropped. 583bd670b35SErik Nordmark * 584bd670b35SErik Nordmark * The unspecified address means all multicast addresses for in both the 585bd670b35SErik Nordmark * case of IPv4 and IPv6. 5867c478bd9Sstevel@tonic-gate * 5877c478bd9Sstevel@tonic-gate * ilgstat tells us if there's an ilg associated with this join, 5887c478bd9Sstevel@tonic-gate * and if so, if it's a new ilg or a change to an existing one. 5897c478bd9Sstevel@tonic-gate * ilg_fmode and ilg_flist give us the current filter state of 5907c478bd9Sstevel@tonic-gate * the ilg (and will be EXCLUDE {NULL} in the case of no ilg). 591bd670b35SErik Nordmark * 592bd670b35SErik Nordmark * The caller should have already mapped an IPMP under ill to the upper. 5937c478bd9Sstevel@tonic-gate */ 594bd670b35SErik Nordmark static ilm_t * 595bd670b35SErik Nordmark ip_addmulti_serial(const in6_addr_t *v6group, ill_t *ill, zoneid_t zoneid, 596bd670b35SErik Nordmark ilg_stat_t ilgstat, mcast_record_t ilg_fmode, slist_t *ilg_flist, 597bd670b35SErik Nordmark int *errorp) 5987c478bd9Sstevel@tonic-gate { 5997c478bd9Sstevel@tonic-gate ilm_t *ilm; 6007c478bd9Sstevel@tonic-gate 601bd670b35SErik Nordmark ASSERT(MUTEX_HELD(&ill->ill_mcast_serializer)); 6027c478bd9Sstevel@tonic-gate 603bd670b35SErik Nordmark if (ill->ill_isv6) { 6047c478bd9Sstevel@tonic-gate if (!IN6_IS_ADDR_MULTICAST(v6group) && 6057c478bd9Sstevel@tonic-gate !IN6_IS_ADDR_UNSPECIFIED(v6group)) { 606bd670b35SErik Nordmark *errorp = EINVAL; 607bd670b35SErik Nordmark return (NULL); 608bd670b35SErik Nordmark } 609bd670b35SErik Nordmark } else { 610bd670b35SErik Nordmark if (IN6_IS_ADDR_V4MAPPED(v6group)) { 611bd670b35SErik Nordmark ipaddr_t v4group; 612bd670b35SErik Nordmark 613bd670b35SErik Nordmark IN6_V4MAPPED_TO_IPADDR(v6group, v4group); 614f1c454b4SSowmini Varadhan ASSERT(!IS_UNDER_IPMP(ill)); 615bd670b35SErik Nordmark if (!CLASSD(v4group)) { 616bd670b35SErik Nordmark *errorp = EINVAL; 617bd670b35SErik Nordmark return (NULL); 618bd670b35SErik Nordmark } 619bd670b35SErik Nordmark } else if (!IN6_IS_ADDR_UNSPECIFIED(v6group)) { 620bd670b35SErik Nordmark *errorp = EINVAL; 621bd670b35SErik Nordmark return (NULL); 622bd670b35SErik Nordmark } 6237c478bd9Sstevel@tonic-gate } 6247c478bd9Sstevel@tonic-gate 625bd670b35SErik Nordmark if (IS_UNDER_IPMP(ill)) { 626bd670b35SErik Nordmark *errorp = EINVAL; 627bd670b35SErik Nordmark return (NULL); 628bd670b35SErik Nordmark } 629bd670b35SErik Nordmark 630bd670b35SErik Nordmark rw_enter(&ill->ill_mcast_lock, RW_WRITER); 631bd670b35SErik Nordmark /* 632bd670b35SErik Nordmark * We do the equivalent of a lookup by checking after we get the lock 633bd670b35SErik Nordmark * This is needed since the ill could have been condemned after 634bd670b35SErik Nordmark * we looked it up, and we need to check condemned after we hold 635bd670b35SErik Nordmark * ill_mcast_lock to synchronize with the unplumb code. 636bd670b35SErik Nordmark */ 637bd670b35SErik Nordmark if (ill->ill_state_flags & ILL_CONDEMNED) { 638bd670b35SErik Nordmark rw_exit(&ill->ill_mcast_lock); 639bd670b35SErik Nordmark *errorp = ENXIO; 640bd670b35SErik Nordmark return (NULL); 641bd670b35SErik Nordmark } 642bd670b35SErik Nordmark ilm = ip_addmulti_impl(v6group, ill, zoneid, ilgstat, ilg_fmode, 643bd670b35SErik Nordmark ilg_flist, errorp); 644bd670b35SErik Nordmark rw_exit(&ill->ill_mcast_lock); 645bd670b35SErik Nordmark 646bd670b35SErik Nordmark ill_mcast_timer_start(ill->ill_ipst); 647bd670b35SErik Nordmark return (ilm); 648bd670b35SErik Nordmark } 649bd670b35SErik Nordmark 650bd670b35SErik Nordmark static ilm_t * 651bd670b35SErik Nordmark ip_addmulti_impl(const in6_addr_t *v6group, ill_t *ill, zoneid_t zoneid, 652bd670b35SErik Nordmark ilg_stat_t ilgstat, mcast_record_t ilg_fmode, slist_t *ilg_flist, 653bd670b35SErik Nordmark int *errorp) 654bd670b35SErik Nordmark { 655bd670b35SErik Nordmark ilm_t *ilm; 656bd670b35SErik Nordmark int ret = 0; 657bd670b35SErik Nordmark 658bd670b35SErik Nordmark ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock)); 659bd670b35SErik Nordmark *errorp = 0; 660e11c3f44Smeem 6617c478bd9Sstevel@tonic-gate /* 662e11c3f44Smeem * An ilm is uniquely identified by the tuple of (group, ill) where 663e11c3f44Smeem * `group' is the multicast group address, and `ill' is the interface 664e11c3f44Smeem * on which it is currently joined. 6657c478bd9Sstevel@tonic-gate */ 6667c478bd9Sstevel@tonic-gate 667bd670b35SErik Nordmark ilm = ilm_lookup(ill, v6group, zoneid); 668bd670b35SErik Nordmark if (ilm != NULL) { 669bd670b35SErik Nordmark /* ilm_update_add bumps ilm_refcnt unless ILGSTAT_CHANGE */ 670bd670b35SErik Nordmark ret = ilm_update_add(ilm, ilgstat, ilg_flist); 671bd670b35SErik Nordmark if (ret == 0) 672bd670b35SErik Nordmark return (ilm); 673bd670b35SErik Nordmark 674bd670b35SErik Nordmark *errorp = ret; 675bd670b35SErik Nordmark return (NULL); 676bd670b35SErik Nordmark } 677bd670b35SErik Nordmark 678bd670b35SErik Nordmark /* 679bd670b35SErik Nordmark * The callers checks on the ilg and the ilg+ilm consistency under 680bd670b35SErik Nordmark * ill_mcast_serializer ensures that we can not have ILGSTAT_CHANGE 681bd670b35SErik Nordmark * and no ilm. 682bd670b35SErik Nordmark */ 683bd670b35SErik Nordmark ASSERT(ilgstat != ILGSTAT_CHANGE); 684bd670b35SErik Nordmark ilm = ilm_add(ill, v6group, ilgstat, ilg_fmode, ilg_flist, zoneid); 685bd670b35SErik Nordmark if (ilm == NULL) { 686bd670b35SErik Nordmark *errorp = ENOMEM; 687bd670b35SErik Nordmark return (NULL); 688bd670b35SErik Nordmark } 6897c478bd9Sstevel@tonic-gate 6907c478bd9Sstevel@tonic-gate if (IN6_IS_ADDR_UNSPECIFIED(v6group)) { 6917c478bd9Sstevel@tonic-gate /* 692bd670b35SErik Nordmark * If we have more then one we should not tell the driver 693bd670b35SErik Nordmark * to join this time. 6947c478bd9Sstevel@tonic-gate */ 695bd670b35SErik Nordmark if (ilm_numentries(ill, v6group) == 1) { 696b127ac41SPhilip Kirk ret = ill_join_allmulti(ill); 6977c478bd9Sstevel@tonic-gate } 698bd670b35SErik Nordmark } else { 699bd670b35SErik Nordmark if (!IS_LOOPBACK(ill)) { 700bd670b35SErik Nordmark if (ill->ill_isv6) 7017c478bd9Sstevel@tonic-gate mld_joingroup(ilm); 702bd670b35SErik Nordmark else 703bd670b35SErik Nordmark igmp_joingroup(ilm); 704bd670b35SErik Nordmark } 7057c478bd9Sstevel@tonic-gate 7067c478bd9Sstevel@tonic-gate /* 7077c478bd9Sstevel@tonic-gate * If we have more then one we should not tell the driver 7087c478bd9Sstevel@tonic-gate * to join this time. 7097c478bd9Sstevel@tonic-gate */ 710bd670b35SErik Nordmark if (ilm_numentries(ill, v6group) == 1) { 711bd670b35SErik Nordmark ret = ip_ll_multireq(ill, v6group, DL_ENABMULTI_REQ); 712bd670b35SErik Nordmark } 713bd670b35SErik Nordmark } 714bd670b35SErik Nordmark if (ret != 0) { 715bd670b35SErik Nordmark if (ret == ENETDOWN) { 716bd670b35SErik Nordmark char buf[INET6_ADDRSTRLEN]; 7177c478bd9Sstevel@tonic-gate 718bd670b35SErik Nordmark ip0dbg(("ip_addmulti: ENETDOWN for %s on %s", 719bd670b35SErik Nordmark inet_ntop(AF_INET6, &ilm->ilm_v6addr, 720bd670b35SErik Nordmark buf, sizeof (buf)), ill->ill_name)); 721bd670b35SErik Nordmark } 7227c478bd9Sstevel@tonic-gate ilm_delete(ilm); 723bd670b35SErik Nordmark *errorp = ret; 724bd670b35SErik Nordmark return (NULL); 725bd670b35SErik Nordmark } else { 726bd670b35SErik Nordmark return (ilm); 727bd670b35SErik Nordmark } 7287c478bd9Sstevel@tonic-gate } 7297c478bd9Sstevel@tonic-gate 7307c478bd9Sstevel@tonic-gate /* 731*7f125a53SRavi Chandra Nallan * Looks up the list of multicast physical addresses this interface 732*7f125a53SRavi Chandra Nallan * listens to. Add to the list if not present already. 733*7f125a53SRavi Chandra Nallan */ 734*7f125a53SRavi Chandra Nallan boolean_t 735*7f125a53SRavi Chandra Nallan ip_mphysaddr_add(ill_t *ill, uchar_t *hw_addr) 736*7f125a53SRavi Chandra Nallan { 737*7f125a53SRavi Chandra Nallan multiphysaddr_t *mpa = NULL; 738*7f125a53SRavi Chandra Nallan int hw_addr_length = ill->ill_phys_addr_length; 739*7f125a53SRavi Chandra Nallan 740*7f125a53SRavi Chandra Nallan mutex_enter(&ill->ill_lock); 741*7f125a53SRavi Chandra Nallan for (mpa = ill->ill_mphysaddr_list; mpa != NULL; mpa = mpa->mpa_next) { 742*7f125a53SRavi Chandra Nallan if (bcmp(hw_addr, &(mpa->mpa_addr[0]), hw_addr_length) == 0) { 743*7f125a53SRavi Chandra Nallan mpa->mpa_refcnt++; 744*7f125a53SRavi Chandra Nallan mutex_exit(&ill->ill_lock); 745*7f125a53SRavi Chandra Nallan return (B_FALSE); 746*7f125a53SRavi Chandra Nallan } 747*7f125a53SRavi Chandra Nallan } 748*7f125a53SRavi Chandra Nallan 749*7f125a53SRavi Chandra Nallan mpa = kmem_zalloc(sizeof (multiphysaddr_t), KM_NOSLEEP); 750*7f125a53SRavi Chandra Nallan if (mpa == NULL) { 751*7f125a53SRavi Chandra Nallan /* 752*7f125a53SRavi Chandra Nallan * We risk not having the multiphysadd structure. At this 753*7f125a53SRavi Chandra Nallan * point we can't fail. We can't afford to not send a 754*7f125a53SRavi Chandra Nallan * DL_ENABMULTI_REQ also. It is better than pre-allocating 755*7f125a53SRavi Chandra Nallan * the structure and having the code to track it also. 756*7f125a53SRavi Chandra Nallan */ 757*7f125a53SRavi Chandra Nallan ip0dbg(("ip_mphysaddr_add: ENOMEM. Some multicast apps" 758*7f125a53SRavi Chandra Nallan " may have issues. hw_addr: %p ill_name: %s\n", 759*7f125a53SRavi Chandra Nallan (void *)hw_addr, ill->ill_name)); 760*7f125a53SRavi Chandra Nallan mutex_exit(&ill->ill_lock); 761*7f125a53SRavi Chandra Nallan return (B_TRUE); 762*7f125a53SRavi Chandra Nallan } 763*7f125a53SRavi Chandra Nallan bcopy(hw_addr, &(mpa->mpa_addr[0]), hw_addr_length); 764*7f125a53SRavi Chandra Nallan mpa->mpa_refcnt = 1; 765*7f125a53SRavi Chandra Nallan mpa->mpa_next = ill->ill_mphysaddr_list; 766*7f125a53SRavi Chandra Nallan ill->ill_mphysaddr_list = mpa; 767*7f125a53SRavi Chandra Nallan mutex_exit(&ill->ill_lock); 768*7f125a53SRavi Chandra Nallan return (B_TRUE); 769*7f125a53SRavi Chandra Nallan } 770*7f125a53SRavi Chandra Nallan 771*7f125a53SRavi Chandra Nallan /* 772*7f125a53SRavi Chandra Nallan * Look up hw_addr from the list of physical multicast addresses this interface 773*7f125a53SRavi Chandra Nallan * listens to. 774*7f125a53SRavi Chandra Nallan * Remove the entry if the refcnt is 0 775*7f125a53SRavi Chandra Nallan */ 776*7f125a53SRavi Chandra Nallan boolean_t 777*7f125a53SRavi Chandra Nallan ip_mphysaddr_del(ill_t *ill, uchar_t *hw_addr) 778*7f125a53SRavi Chandra Nallan { 779*7f125a53SRavi Chandra Nallan multiphysaddr_t *mpap = NULL, **mpapp = NULL; 780*7f125a53SRavi Chandra Nallan int hw_addr_length = ill->ill_phys_addr_length; 781*7f125a53SRavi Chandra Nallan boolean_t ret = B_FALSE; 782*7f125a53SRavi Chandra Nallan 783*7f125a53SRavi Chandra Nallan mutex_enter(&ill->ill_lock); 784*7f125a53SRavi Chandra Nallan for (mpapp = &ill->ill_mphysaddr_list; (mpap = *mpapp) != NULL; 785*7f125a53SRavi Chandra Nallan mpapp = &(mpap->mpa_next)) { 786*7f125a53SRavi Chandra Nallan if (bcmp(hw_addr, &(mpap->mpa_addr[0]), hw_addr_length) == 0) 787*7f125a53SRavi Chandra Nallan break; 788*7f125a53SRavi Chandra Nallan } 789*7f125a53SRavi Chandra Nallan if (mpap == NULL) { 790*7f125a53SRavi Chandra Nallan /* 791*7f125a53SRavi Chandra Nallan * Should be coming here only when there was a memory 792*7f125a53SRavi Chandra Nallan * exhaustion and we were not able to allocate 793*7f125a53SRavi Chandra Nallan * a multiphysaddr_t. We still send a DL_DISABMULTI_REQ down. 794*7f125a53SRavi Chandra Nallan */ 795*7f125a53SRavi Chandra Nallan 796*7f125a53SRavi Chandra Nallan ip0dbg(("ip_mphysaddr_del: No entry for this addr. Some " 797*7f125a53SRavi Chandra Nallan "multicast apps might have had issues. hw_addr: %p " 798*7f125a53SRavi Chandra Nallan " ill_name: %s\n", (void *)hw_addr, ill->ill_name)); 799*7f125a53SRavi Chandra Nallan ret = B_TRUE; 800*7f125a53SRavi Chandra Nallan } else if (--mpap->mpa_refcnt == 0) { 801*7f125a53SRavi Chandra Nallan *mpapp = mpap->mpa_next; 802*7f125a53SRavi Chandra Nallan kmem_free(mpap, sizeof (multiphysaddr_t)); 803*7f125a53SRavi Chandra Nallan ret = B_TRUE; 804*7f125a53SRavi Chandra Nallan } 805*7f125a53SRavi Chandra Nallan mutex_exit(&ill->ill_lock); 806*7f125a53SRavi Chandra Nallan return (ret); 807*7f125a53SRavi Chandra Nallan } 808*7f125a53SRavi Chandra Nallan 809*7f125a53SRavi Chandra Nallan /* 810bd670b35SErik Nordmark * Send a multicast request to the driver for enabling or disabling 811bd670b35SErik Nordmark * multicast reception for v6groupp address. The caller has already 812bd670b35SErik Nordmark * checked whether it is appropriate to send one or not. 813bd670b35SErik Nordmark * 814bd670b35SErik Nordmark * For IPMP we switch to the cast_ill since it has the right hardware 815bd670b35SErik Nordmark * information. 8165d460eafSCathy Zhou */ 817bd670b35SErik Nordmark static int 818bd670b35SErik Nordmark ip_ll_send_multireq(ill_t *ill, const in6_addr_t *v6groupp, t_uscalar_t prim) 8197c478bd9Sstevel@tonic-gate { 8207c478bd9Sstevel@tonic-gate mblk_t *mp; 8217c478bd9Sstevel@tonic-gate uint32_t addrlen, addroff; 822bd670b35SErik Nordmark ill_t *release_ill = NULL; 823*7f125a53SRavi Chandra Nallan uchar_t *cp; 824bd670b35SErik Nordmark int err = 0; 8257c478bd9Sstevel@tonic-gate 826bd670b35SErik Nordmark ASSERT(RW_LOCK_HELD(&ill->ill_mcast_lock)); 8277c478bd9Sstevel@tonic-gate 828bd670b35SErik Nordmark if (IS_IPMP(ill)) { 829bd670b35SErik Nordmark /* On the upper IPMP ill. */ 830bd670b35SErik Nordmark release_ill = ipmp_illgrp_hold_cast_ill(ill->ill_grp); 831bd670b35SErik Nordmark if (release_ill == NULL) { 8327c478bd9Sstevel@tonic-gate /* 833bd670b35SErik Nordmark * Avoid sending it down to the ipmpstub. 834bd670b35SErik Nordmark * We will be called again once the members of the 835bd670b35SErik Nordmark * group are in place 836e11c3f44Smeem */ 837bd670b35SErik Nordmark ip1dbg(("ip_ll_send_multireq: no cast_ill for %s %d\n", 838bd670b35SErik Nordmark ill->ill_name, ill->ill_isv6)); 839e11c3f44Smeem return (0); 840bd670b35SErik Nordmark } 841bd670b35SErik Nordmark ill = release_ill; 842bd670b35SErik Nordmark } 843bd670b35SErik Nordmark /* Create a DL_ENABMULTI_REQ or DL_DISABMULTI_REQ message. */ 844bd670b35SErik Nordmark mp = ill_create_dl(ill, prim, &addrlen, &addroff); 845bd670b35SErik Nordmark if (mp == NULL) { 846bd670b35SErik Nordmark err = ENOMEM; 847bd670b35SErik Nordmark goto done; 848bd670b35SErik Nordmark } 849e11c3f44Smeem 850bd670b35SErik Nordmark mp = ndp_mcastreq(ill, v6groupp, addrlen, addroff, mp); 851bd670b35SErik Nordmark if (mp == NULL) { 852bd670b35SErik Nordmark ip0dbg(("null from ndp_mcastreq(ill %s)\n", ill->ill_name)); 853bd670b35SErik Nordmark err = ENOMEM; 854bd670b35SErik Nordmark goto done; 855bd670b35SErik Nordmark } 856*7f125a53SRavi Chandra Nallan cp = mp->b_rptr; 8575d460eafSCathy Zhou 858*7f125a53SRavi Chandra Nallan switch (((union DL_primitives *)cp)->dl_primitive) { 859bd670b35SErik Nordmark case DL_ENABMULTI_REQ: 860*7f125a53SRavi Chandra Nallan cp += ((dl_enabmulti_req_t *)cp)->dl_addr_offset; 861*7f125a53SRavi Chandra Nallan if (!ip_mphysaddr_add(ill, cp)) { 862*7f125a53SRavi Chandra Nallan freemsg(mp); 863*7f125a53SRavi Chandra Nallan err = 0; 864*7f125a53SRavi Chandra Nallan goto done; 865*7f125a53SRavi Chandra Nallan } 866bd670b35SErik Nordmark mutex_enter(&ill->ill_lock); 8675d460eafSCathy Zhou /* Track the state if this is the first enabmulti */ 8685d460eafSCathy Zhou if (ill->ill_dlpi_multicast_state == IDS_UNKNOWN) 8695d460eafSCathy Zhou ill->ill_dlpi_multicast_state = IDS_INPROGRESS; 870bd670b35SErik Nordmark mutex_exit(&ill->ill_lock); 871bd670b35SErik Nordmark break; 872*7f125a53SRavi Chandra Nallan case DL_DISABMULTI_REQ: 873*7f125a53SRavi Chandra Nallan cp += ((dl_disabmulti_req_t *)cp)->dl_addr_offset; 874*7f125a53SRavi Chandra Nallan if (!ip_mphysaddr_del(ill, cp)) { 875*7f125a53SRavi Chandra Nallan freemsg(mp); 876*7f125a53SRavi Chandra Nallan err = 0; 877*7f125a53SRavi Chandra Nallan goto done; 878*7f125a53SRavi Chandra Nallan } 8797c478bd9Sstevel@tonic-gate } 880bd670b35SErik Nordmark ill_dlpi_queue(ill, mp); 881bd670b35SErik Nordmark done: 882bd670b35SErik Nordmark if (release_ill != NULL) 883bd670b35SErik Nordmark ill_refrele(release_ill); 884bd670b35SErik Nordmark return (err); 8857c478bd9Sstevel@tonic-gate } 8867c478bd9Sstevel@tonic-gate 8877c478bd9Sstevel@tonic-gate /* 8887c478bd9Sstevel@tonic-gate * Send a multicast request to the driver for enabling multicast 8897c478bd9Sstevel@tonic-gate * membership for v6group if appropriate. 8907c478bd9Sstevel@tonic-gate */ 8917c478bd9Sstevel@tonic-gate static int 892bd670b35SErik Nordmark ip_ll_multireq(ill_t *ill, const in6_addr_t *v6groupp, t_uscalar_t prim) 8937c478bd9Sstevel@tonic-gate { 8947c478bd9Sstevel@tonic-gate if (ill->ill_net_type != IRE_IF_RESOLVER || 895bd670b35SErik Nordmark ill->ill_ipif->ipif_flags & IPIF_POINTOPOINT) { 896bd670b35SErik Nordmark ip1dbg(("ip_ll_multireq: not resolver\n")); 8977c478bd9Sstevel@tonic-gate return (0); /* Must be IRE_IF_NORESOLVER */ 8987c478bd9Sstevel@tonic-gate } 8997c478bd9Sstevel@tonic-gate 9007c478bd9Sstevel@tonic-gate if (ill->ill_phyint->phyint_flags & PHYI_MULTI_BCAST) { 901bd670b35SErik Nordmark ip1dbg(("ip_ll_multireq: MULTI_BCAST\n")); 9027c478bd9Sstevel@tonic-gate return (0); 9037c478bd9Sstevel@tonic-gate } 904bd670b35SErik Nordmark return (ip_ll_send_multireq(ill, v6groupp, prim)); 9057c478bd9Sstevel@tonic-gate } 9067c478bd9Sstevel@tonic-gate 9077c478bd9Sstevel@tonic-gate /* 908bd670b35SErik Nordmark * Delete the ilm. Used by other parts of IP for the case of no_ilg/leaving 909bd670b35SErik Nordmark * being true. 9107c478bd9Sstevel@tonic-gate */ 9117c478bd9Sstevel@tonic-gate int 912bd670b35SErik Nordmark ip_delmulti(ilm_t *ilm) 9137c478bd9Sstevel@tonic-gate { 914bd670b35SErik Nordmark ill_t *ill = ilm->ilm_ill; 915bd670b35SErik Nordmark int error; 9167c478bd9Sstevel@tonic-gate 917bd670b35SErik Nordmark /* Acquire serializer to keep assert in ilm_bld_flists happy */ 918bd670b35SErik Nordmark mutex_enter(&ill->ill_mcast_serializer); 919bd670b35SErik Nordmark error = ip_delmulti_serial(ilm, B_TRUE, B_TRUE); 920bd670b35SErik Nordmark mutex_exit(&ill->ill_mcast_serializer); 921f1c454b4SSowmini Varadhan /* 922f1c454b4SSowmini Varadhan * Now that all locks have been dropped, we can send any 923f1c454b4SSowmini Varadhan * deferred/queued DLPI or IP packets 924f1c454b4SSowmini Varadhan */ 925f1c454b4SSowmini Varadhan ill_mcast_send_queued(ill); 926f1c454b4SSowmini Varadhan ill_dlpi_send_queued(ill); 927bd670b35SErik Nordmark return (error); 9287c478bd9Sstevel@tonic-gate } 9297c478bd9Sstevel@tonic-gate 9307c478bd9Sstevel@tonic-gate 9317c478bd9Sstevel@tonic-gate /* 932bd670b35SErik Nordmark * Delete the ilm. 933f1c454b4SSowmini Varadhan * Assumes ill_mcast_serializer is held by the caller. 934f1c454b4SSowmini Varadhan * Caller must send out queued dlpi/multicast packets after dropping 935f1c454b4SSowmini Varadhan * all locks. 9367c478bd9Sstevel@tonic-gate */ 9377c478bd9Sstevel@tonic-gate static int 938bd670b35SErik Nordmark ip_delmulti_serial(ilm_t *ilm, boolean_t no_ilg, boolean_t leaving) 9397c478bd9Sstevel@tonic-gate { 940bd670b35SErik Nordmark ill_t *ill = ilm->ilm_ill; 941bd670b35SErik Nordmark int ret; 9427c478bd9Sstevel@tonic-gate 943bd670b35SErik Nordmark ASSERT(MUTEX_HELD(&ill->ill_mcast_serializer)); 944bd670b35SErik Nordmark ASSERT(!(IS_UNDER_IPMP(ill))); 9457c478bd9Sstevel@tonic-gate 946bd670b35SErik Nordmark rw_enter(&ill->ill_mcast_lock, RW_WRITER); 947bd670b35SErik Nordmark ret = ip_delmulti_impl(ilm, no_ilg, leaving); 948bd670b35SErik Nordmark rw_exit(&ill->ill_mcast_lock); 949bd670b35SErik Nordmark ill_mcast_timer_start(ill->ill_ipst); 950bd670b35SErik Nordmark return (ret); 9517c478bd9Sstevel@tonic-gate } 952bd670b35SErik Nordmark 953bd670b35SErik Nordmark static int 954bd670b35SErik Nordmark ip_delmulti_impl(ilm_t *ilm, boolean_t no_ilg, boolean_t leaving) 955bd670b35SErik Nordmark { 956bd670b35SErik Nordmark ill_t *ill = ilm->ilm_ill; 957bd670b35SErik Nordmark int error; 958bd670b35SErik Nordmark in6_addr_t v6group; 959bd670b35SErik Nordmark 960bd670b35SErik Nordmark ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock)); 961bd670b35SErik Nordmark 962bd670b35SErik Nordmark /* Update counters */ 963bd670b35SErik Nordmark if (no_ilg) 964bd670b35SErik Nordmark ilm->ilm_no_ilg_cnt--; 965bd670b35SErik Nordmark 966bd670b35SErik Nordmark if (leaving) 967bd670b35SErik Nordmark ilm->ilm_refcnt--; 968bd670b35SErik Nordmark 969bd670b35SErik Nordmark if (ilm->ilm_refcnt > 0) 970bd670b35SErik Nordmark return (ilm_update_del(ilm)); 971bd670b35SErik Nordmark 972bd670b35SErik Nordmark v6group = ilm->ilm_v6addr; 973bd670b35SErik Nordmark 974bd670b35SErik Nordmark if (IN6_IS_ADDR_UNSPECIFIED(&ilm->ilm_v6addr)) { 975bd670b35SErik Nordmark ilm_delete(ilm); 9767c478bd9Sstevel@tonic-gate /* 977bd670b35SErik Nordmark * If we have some left then one we should not tell the driver 978bd670b35SErik Nordmark * to leave. 9797c478bd9Sstevel@tonic-gate */ 980bd670b35SErik Nordmark if (ilm_numentries(ill, &v6group) != 0) 981bd670b35SErik Nordmark return (0); 982bd670b35SErik Nordmark 983bd670b35SErik Nordmark ill_leave_allmulti(ill); 984bd670b35SErik Nordmark 9857c478bd9Sstevel@tonic-gate return (0); 9867c478bd9Sstevel@tonic-gate } 987bd670b35SErik Nordmark 988bd670b35SErik Nordmark if (!IS_LOOPBACK(ill)) { 989bd670b35SErik Nordmark if (ill->ill_isv6) 990bd670b35SErik Nordmark mld_leavegroup(ilm); 991bd670b35SErik Nordmark else 992bd670b35SErik Nordmark igmp_leavegroup(ilm); 993bd670b35SErik Nordmark } 994bd670b35SErik Nordmark 995bd670b35SErik Nordmark ilm_delete(ilm); 996bd670b35SErik Nordmark /* 997bd670b35SErik Nordmark * If we have some left then one we should not tell the driver 998bd670b35SErik Nordmark * to leave. 999bd670b35SErik Nordmark */ 1000bd670b35SErik Nordmark if (ilm_numentries(ill, &v6group) != 0) 1001bd670b35SErik Nordmark return (0); 1002bd670b35SErik Nordmark 1003bd670b35SErik Nordmark error = ip_ll_multireq(ill, &v6group, DL_DISABMULTI_REQ); 1004bd670b35SErik Nordmark /* We ignore the case when ill_dl_up is not set */ 1005bd670b35SErik Nordmark if (error == ENETDOWN) { 1006bd670b35SErik Nordmark char buf[INET6_ADDRSTRLEN]; 1007bd670b35SErik Nordmark 1008bd670b35SErik Nordmark ip0dbg(("ip_delmulti: ENETDOWN for %s on %s", 1009bd670b35SErik Nordmark inet_ntop(AF_INET6, &v6group, buf, sizeof (buf)), 1010bd670b35SErik Nordmark ill->ill_name)); 1011bd670b35SErik Nordmark } 1012bd670b35SErik Nordmark return (error); 10137c478bd9Sstevel@tonic-gate } 10147c478bd9Sstevel@tonic-gate 10157c478bd9Sstevel@tonic-gate /* 1016bd670b35SErik Nordmark * Make the driver pass up all multicast packets. 10177c478bd9Sstevel@tonic-gate */ 10187c478bd9Sstevel@tonic-gate int 1019b127ac41SPhilip Kirk ill_join_allmulti(ill_t *ill) 10207c478bd9Sstevel@tonic-gate { 1021bd670b35SErik Nordmark mblk_t *promiscon_mp, *promiscoff_mp = NULL; 10227c478bd9Sstevel@tonic-gate uint32_t addrlen, addroff; 1023bd670b35SErik Nordmark ill_t *release_ill = NULL; 10247c478bd9Sstevel@tonic-gate 1025bd670b35SErik Nordmark ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock)); 10267c478bd9Sstevel@tonic-gate 10270e0e37a8SErik Nordmark if (IS_LOOPBACK(ill)) 10280e0e37a8SErik Nordmark return (0); 10290e0e37a8SErik Nordmark 1030e6ed03fcSmeem if (!ill->ill_dl_up) { 10317c478bd9Sstevel@tonic-gate /* 10327c478bd9Sstevel@tonic-gate * Nobody there. All multicast addresses will be re-joined 10337c478bd9Sstevel@tonic-gate * when we get the DL_BIND_ACK bringing the interface up. 10347c478bd9Sstevel@tonic-gate */ 1035bd670b35SErik Nordmark return (ENETDOWN); 10367c478bd9Sstevel@tonic-gate } 10377c478bd9Sstevel@tonic-gate 1038bd670b35SErik Nordmark if (IS_IPMP(ill)) { 1039bd670b35SErik Nordmark /* On the upper IPMP ill. */ 1040bd670b35SErik Nordmark release_ill = ipmp_illgrp_hold_cast_ill(ill->ill_grp); 1041bd670b35SErik Nordmark if (release_ill == NULL) { 1042e11c3f44Smeem /* 1043bd670b35SErik Nordmark * Avoid sending it down to the ipmpstub. 1044bd670b35SErik Nordmark * We will be called again once the members of the 1045bd670b35SErik Nordmark * group are in place 1046e11c3f44Smeem */ 1047bd670b35SErik Nordmark ip1dbg(("ill_join_allmulti: no cast_ill for %s %d\n", 1048bd670b35SErik Nordmark ill->ill_name, ill->ill_isv6)); 1049e11c3f44Smeem return (0); 1050bd670b35SErik Nordmark } 1051bd670b35SErik Nordmark ill = release_ill; 1052bd670b35SErik Nordmark if (!ill->ill_dl_up) { 1053bd670b35SErik Nordmark ill_refrele(ill); 1054bd670b35SErik Nordmark return (ENETDOWN); 1055bd670b35SErik Nordmark } 1056bd670b35SErik Nordmark } 10577c478bd9Sstevel@tonic-gate 10587c478bd9Sstevel@tonic-gate /* 1059b127ac41SPhilip Kirk * Create a DL_PROMISCON_REQ message and send it directly to the DLPI 1060b127ac41SPhilip Kirk * provider. We don't need to do this for certain media types for 1061b127ac41SPhilip Kirk * which we never need to turn promiscuous mode on. While we're here, 1062b127ac41SPhilip Kirk * pre-allocate a DL_PROMISCOFF_REQ message to make sure that 1063b127ac41SPhilip Kirk * ill_leave_allmulti() will not fail due to low memory conditions. 10647c478bd9Sstevel@tonic-gate */ 10657c478bd9Sstevel@tonic-gate if ((ill->ill_net_type == IRE_IF_RESOLVER) && 10667c478bd9Sstevel@tonic-gate !(ill->ill_phyint->phyint_flags & PHYI_MULTI_BCAST)) { 1067b127ac41SPhilip Kirk promiscon_mp = ill_create_dl(ill, DL_PROMISCON_REQ, 1068bd670b35SErik Nordmark &addrlen, &addroff); 1069bd670b35SErik Nordmark if (ill->ill_promiscoff_mp == NULL) 1070b127ac41SPhilip Kirk promiscoff_mp = ill_create_dl(ill, DL_PROMISCOFF_REQ, 1071bd670b35SErik Nordmark &addrlen, &addroff); 1072bd670b35SErik Nordmark if (promiscon_mp == NULL || 1073bd670b35SErik Nordmark (ill->ill_promiscoff_mp == NULL && promiscoff_mp == NULL)) { 1074b127ac41SPhilip Kirk freemsg(promiscon_mp); 1075b127ac41SPhilip Kirk freemsg(promiscoff_mp); 1076bd670b35SErik Nordmark if (release_ill != NULL) 1077bd670b35SErik Nordmark ill_refrele(release_ill); 10787c478bd9Sstevel@tonic-gate return (ENOMEM); 1079b127ac41SPhilip Kirk } 1080bd670b35SErik Nordmark if (ill->ill_promiscoff_mp == NULL) 1081b127ac41SPhilip Kirk ill->ill_promiscoff_mp = promiscoff_mp; 1082bd670b35SErik Nordmark ill_dlpi_queue(ill, promiscon_mp); 10837c478bd9Sstevel@tonic-gate } 1084bd670b35SErik Nordmark if (release_ill != NULL) 1085bd670b35SErik Nordmark ill_refrele(release_ill); 10867c478bd9Sstevel@tonic-gate return (0); 10877c478bd9Sstevel@tonic-gate } 10887c478bd9Sstevel@tonic-gate 10897c478bd9Sstevel@tonic-gate /* 10907c478bd9Sstevel@tonic-gate * Make the driver stop passing up all multicast packets 10917c478bd9Sstevel@tonic-gate */ 1092b127ac41SPhilip Kirk void 1093b127ac41SPhilip Kirk ill_leave_allmulti(ill_t *ill) 10947c478bd9Sstevel@tonic-gate { 1095e11c3f44Smeem mblk_t *promiscoff_mp; 1096bd670b35SErik Nordmark ill_t *release_ill = NULL; 10977c478bd9Sstevel@tonic-gate 1098bd670b35SErik Nordmark ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock)); 10997c478bd9Sstevel@tonic-gate 11000e0e37a8SErik Nordmark if (IS_LOOPBACK(ill)) 11010e0e37a8SErik Nordmark return; 11020e0e37a8SErik Nordmark 1103e6ed03fcSmeem if (!ill->ill_dl_up) { 11047c478bd9Sstevel@tonic-gate /* 11057c478bd9Sstevel@tonic-gate * Nobody there. All multicast addresses will be re-joined 11067c478bd9Sstevel@tonic-gate * when we get the DL_BIND_ACK bringing the interface up. 11077c478bd9Sstevel@tonic-gate */ 1108b127ac41SPhilip Kirk return; 11097c478bd9Sstevel@tonic-gate } 11107c478bd9Sstevel@tonic-gate 1111bd670b35SErik Nordmark if (IS_IPMP(ill)) { 1112bd670b35SErik Nordmark /* On the upper IPMP ill. */ 1113bd670b35SErik Nordmark release_ill = ipmp_illgrp_hold_cast_ill(ill->ill_grp); 1114bd670b35SErik Nordmark if (release_ill == NULL) { 1115e11c3f44Smeem /* 1116bd670b35SErik Nordmark * Avoid sending it down to the ipmpstub. 1117bd670b35SErik Nordmark * We will be called again once the members of the 1118bd670b35SErik Nordmark * group are in place 1119e11c3f44Smeem */ 1120bd670b35SErik Nordmark ip1dbg(("ill_leave_allmulti: no cast_ill on %s %d\n", 1121bd670b35SErik Nordmark ill->ill_name, ill->ill_isv6)); 1122e11c3f44Smeem return; 1123bd670b35SErik Nordmark } 1124bd670b35SErik Nordmark ill = release_ill; 1125bd670b35SErik Nordmark if (!ill->ill_dl_up) 1126bd670b35SErik Nordmark goto done; 1127bd670b35SErik Nordmark } 11287c478bd9Sstevel@tonic-gate 11297c478bd9Sstevel@tonic-gate /* 1130bd670b35SErik Nordmark * In the case of IPMP and ill_dl_up not being set when we joined 1131bd670b35SErik Nordmark * we didn't allocate a promiscoff_mp. In that case we have 1132bd670b35SErik Nordmark * nothing to do when we leave. 1133bd670b35SErik Nordmark * Ditto for PHYI_MULTI_BCAST 11347c478bd9Sstevel@tonic-gate */ 1135e11c3f44Smeem promiscoff_mp = ill->ill_promiscoff_mp; 1136bd670b35SErik Nordmark if (promiscoff_mp != NULL) { 1137b127ac41SPhilip Kirk ill->ill_promiscoff_mp = NULL; 1138bd670b35SErik Nordmark ill_dlpi_queue(ill, promiscoff_mp); 11397c478bd9Sstevel@tonic-gate } 1140bd670b35SErik Nordmark done: 1141bd670b35SErik Nordmark if (release_ill != NULL) 1142bd670b35SErik Nordmark ill_refrele(release_ill); 1143b127ac41SPhilip Kirk } 1144b127ac41SPhilip Kirk 1145b127ac41SPhilip Kirk int 1146b127ac41SPhilip Kirk ip_join_allmulti(uint_t ifindex, boolean_t isv6, ip_stack_t *ipst) 1147b127ac41SPhilip Kirk { 1148b127ac41SPhilip Kirk ill_t *ill; 1149bd670b35SErik Nordmark int ret; 1150bd670b35SErik Nordmark ilm_t *ilm; 1151b127ac41SPhilip Kirk 1152bd670b35SErik Nordmark ill = ill_lookup_on_ifindex(ifindex, isv6, ipst); 1153bd670b35SErik Nordmark if (ill == NULL) 1154b127ac41SPhilip Kirk return (ENODEV); 1155e11c3f44Smeem 1156e11c3f44Smeem /* 1157bd670b35SErik Nordmark * The ip_addmulti() function doesn't allow IPMP underlying interfaces 1158e11c3f44Smeem * to join allmulti since only the nominated underlying interface in 1159e11c3f44Smeem * the group should receive multicast. We silently succeed to avoid 1160e11c3f44Smeem * having to teach IPobs (currently the only caller of this routine) 1161e11c3f44Smeem * to ignore failures in this case. 1162e11c3f44Smeem */ 1163bd670b35SErik Nordmark if (IS_UNDER_IPMP(ill)) { 1164bd670b35SErik Nordmark ill_refrele(ill); 1165bd670b35SErik Nordmark return (0); 1166b127ac41SPhilip Kirk } 1167bd670b35SErik Nordmark mutex_enter(&ill->ill_lock); 1168bd670b35SErik Nordmark if (ill->ill_ipallmulti_cnt > 0) { 1169bd670b35SErik Nordmark /* Already joined */ 1170bd670b35SErik Nordmark ASSERT(ill->ill_ipallmulti_ilm != NULL); 1171b127ac41SPhilip Kirk ill->ill_ipallmulti_cnt++; 1172bd670b35SErik Nordmark mutex_exit(&ill->ill_lock); 1173bd670b35SErik Nordmark goto done; 1174bd670b35SErik Nordmark } 1175bd670b35SErik Nordmark mutex_exit(&ill->ill_lock); 1176bd670b35SErik Nordmark 1177bd670b35SErik Nordmark ilm = ip_addmulti(&ipv6_all_zeros, ill, ill->ill_zoneid, &ret); 1178bd670b35SErik Nordmark if (ilm == NULL) { 1179bd670b35SErik Nordmark ASSERT(ret != 0); 1180bd670b35SErik Nordmark ill_refrele(ill); 1181b127ac41SPhilip Kirk return (ret); 1182b127ac41SPhilip Kirk } 1183b127ac41SPhilip Kirk 1184bd670b35SErik Nordmark mutex_enter(&ill->ill_lock); 1185bd670b35SErik Nordmark if (ill->ill_ipallmulti_cnt > 0) { 1186bd670b35SErik Nordmark /* Another thread added it concurrently */ 1187bd670b35SErik Nordmark (void) ip_delmulti(ilm); 1188bd670b35SErik Nordmark mutex_exit(&ill->ill_lock); 1189bd670b35SErik Nordmark goto done; 1190bd670b35SErik Nordmark } 1191bd670b35SErik Nordmark ASSERT(ill->ill_ipallmulti_ilm == NULL); 1192bd670b35SErik Nordmark ill->ill_ipallmulti_ilm = ilm; 1193bd670b35SErik Nordmark ill->ill_ipallmulti_cnt++; 1194bd670b35SErik Nordmark mutex_exit(&ill->ill_lock); 1195bd670b35SErik Nordmark done: 1196bd670b35SErik Nordmark ill_refrele(ill); 1197bd670b35SErik Nordmark return (0); 1198bd670b35SErik Nordmark } 1199e11c3f44Smeem 1200b127ac41SPhilip Kirk int 1201b127ac41SPhilip Kirk ip_leave_allmulti(uint_t ifindex, boolean_t isv6, ip_stack_t *ipst) 1202b127ac41SPhilip Kirk { 1203b127ac41SPhilip Kirk ill_t *ill; 1204bd670b35SErik Nordmark ilm_t *ilm; 1205b127ac41SPhilip Kirk 1206bd670b35SErik Nordmark ill = ill_lookup_on_ifindex(ifindex, isv6, ipst); 1207bd670b35SErik Nordmark if (ill == NULL) 1208b127ac41SPhilip Kirk return (ENODEV); 1209e11c3f44Smeem 1210bd670b35SErik Nordmark if (IS_UNDER_IPMP(ill)) { 1211bd670b35SErik Nordmark ill_refrele(ill); 1212bd670b35SErik Nordmark return (0); 1213bd670b35SErik Nordmark } 1214bd670b35SErik Nordmark 1215bd670b35SErik Nordmark mutex_enter(&ill->ill_lock); 1216bd670b35SErik Nordmark if (ill->ill_ipallmulti_cnt == 0) { 1217bd670b35SErik Nordmark /* ip_purge_allmulti could have removed them all */ 1218bd670b35SErik Nordmark mutex_exit(&ill->ill_lock); 1219bd670b35SErik Nordmark goto done; 1220b127ac41SPhilip Kirk } 1221b127ac41SPhilip Kirk ill->ill_ipallmulti_cnt--; 1222bd670b35SErik Nordmark if (ill->ill_ipallmulti_cnt == 0) { 1223bd670b35SErik Nordmark /* Last one */ 1224bd670b35SErik Nordmark ilm = ill->ill_ipallmulti_ilm; 1225bd670b35SErik Nordmark ill->ill_ipallmulti_ilm = NULL; 1226bd670b35SErik Nordmark } else { 1227bd670b35SErik Nordmark ilm = NULL; 1228e11c3f44Smeem } 1229bd670b35SErik Nordmark mutex_exit(&ill->ill_lock); 1230bd670b35SErik Nordmark if (ilm != NULL) 1231bd670b35SErik Nordmark (void) ip_delmulti(ilm); 1232bd670b35SErik Nordmark 1233bd670b35SErik Nordmark done: 1234bd670b35SErik Nordmark ill_refrele(ill); 12357c478bd9Sstevel@tonic-gate return (0); 12367c478bd9Sstevel@tonic-gate } 12377c478bd9Sstevel@tonic-gate 12387c478bd9Sstevel@tonic-gate /* 1239b127ac41SPhilip Kirk * Delete the allmulti memberships that were added as part of 1240b127ac41SPhilip Kirk * ip_join_allmulti(). 1241b127ac41SPhilip Kirk */ 1242b127ac41SPhilip Kirk void 1243b127ac41SPhilip Kirk ip_purge_allmulti(ill_t *ill) 1244b127ac41SPhilip Kirk { 1245bd670b35SErik Nordmark ilm_t *ilm; 1246bd670b35SErik Nordmark 1247b127ac41SPhilip Kirk ASSERT(IAM_WRITER_ILL(ill)); 1248b127ac41SPhilip Kirk 1249bd670b35SErik Nordmark mutex_enter(&ill->ill_lock); 1250bd670b35SErik Nordmark ilm = ill->ill_ipallmulti_ilm; 1251bd670b35SErik Nordmark ill->ill_ipallmulti_ilm = NULL; 1252bd670b35SErik Nordmark ill->ill_ipallmulti_cnt = 0; 1253bd670b35SErik Nordmark mutex_exit(&ill->ill_lock); 1254bd670b35SErik Nordmark 1255bd670b35SErik Nordmark if (ilm != NULL) 1256bd670b35SErik Nordmark (void) ip_delmulti(ilm); 1257b127ac41SPhilip Kirk } 1258b127ac41SPhilip Kirk 1259b127ac41SPhilip Kirk /* 1260bd670b35SErik Nordmark * Create a dlpi message with room for phys+sap. Later 1261bd670b35SErik Nordmark * we will strip the sap for those primitives which 1262bd670b35SErik Nordmark * only need a physical address. 12637c478bd9Sstevel@tonic-gate */ 12647c478bd9Sstevel@tonic-gate static mblk_t * 1265bd670b35SErik Nordmark ill_create_dl(ill_t *ill, uint32_t dl_primitive, 12667c478bd9Sstevel@tonic-gate uint32_t *addr_lenp, uint32_t *addr_offp) 12677c478bd9Sstevel@tonic-gate { 12687c478bd9Sstevel@tonic-gate mblk_t *mp; 12697c478bd9Sstevel@tonic-gate uint32_t hw_addr_length; 12707c478bd9Sstevel@tonic-gate char *cp; 12717c478bd9Sstevel@tonic-gate uint32_t offset; 1272bd670b35SErik Nordmark uint32_t length; 12737c478bd9Sstevel@tonic-gate uint32_t size; 12747c478bd9Sstevel@tonic-gate 12757c478bd9Sstevel@tonic-gate *addr_lenp = *addr_offp = 0; 12767c478bd9Sstevel@tonic-gate 12777c478bd9Sstevel@tonic-gate hw_addr_length = ill->ill_phys_addr_length; 12787c478bd9Sstevel@tonic-gate if (!hw_addr_length) { 12797c478bd9Sstevel@tonic-gate ip0dbg(("ip_create_dl: hw addr length = 0\n")); 12807c478bd9Sstevel@tonic-gate return (NULL); 12817c478bd9Sstevel@tonic-gate } 12827c478bd9Sstevel@tonic-gate 12837c478bd9Sstevel@tonic-gate switch (dl_primitive) { 12847c478bd9Sstevel@tonic-gate case DL_ENABMULTI_REQ: 1285bd670b35SErik Nordmark length = sizeof (dl_enabmulti_req_t); 1286bd670b35SErik Nordmark size = length + hw_addr_length; 1287bd670b35SErik Nordmark break; 12887c478bd9Sstevel@tonic-gate case DL_DISABMULTI_REQ: 1289bd670b35SErik Nordmark length = sizeof (dl_disabmulti_req_t); 1290bd670b35SErik Nordmark size = length + hw_addr_length; 12917c478bd9Sstevel@tonic-gate break; 12927c478bd9Sstevel@tonic-gate case DL_PROMISCON_REQ: 12937c478bd9Sstevel@tonic-gate case DL_PROMISCOFF_REQ: 1294bd670b35SErik Nordmark size = length = sizeof (dl_promiscon_req_t); 12957c478bd9Sstevel@tonic-gate break; 12967c478bd9Sstevel@tonic-gate default: 12977c478bd9Sstevel@tonic-gate return (NULL); 12987c478bd9Sstevel@tonic-gate } 12997c478bd9Sstevel@tonic-gate mp = allocb(size, BPRI_HI); 13007c478bd9Sstevel@tonic-gate if (!mp) 13017c478bd9Sstevel@tonic-gate return (NULL); 13027c478bd9Sstevel@tonic-gate mp->b_wptr += size; 13037c478bd9Sstevel@tonic-gate mp->b_datap->db_type = M_PROTO; 13047c478bd9Sstevel@tonic-gate 13057c478bd9Sstevel@tonic-gate cp = (char *)mp->b_rptr; 13067c478bd9Sstevel@tonic-gate offset = length; 13077c478bd9Sstevel@tonic-gate 13087c478bd9Sstevel@tonic-gate switch (dl_primitive) { 13097c478bd9Sstevel@tonic-gate case DL_ENABMULTI_REQ: { 13107c478bd9Sstevel@tonic-gate dl_enabmulti_req_t *dl = (dl_enabmulti_req_t *)cp; 13117c478bd9Sstevel@tonic-gate 13127c478bd9Sstevel@tonic-gate dl->dl_primitive = dl_primitive; 13137c478bd9Sstevel@tonic-gate dl->dl_addr_offset = offset; 13147c478bd9Sstevel@tonic-gate *addr_lenp = dl->dl_addr_length = hw_addr_length; 13157c478bd9Sstevel@tonic-gate *addr_offp = offset; 13167c478bd9Sstevel@tonic-gate break; 13177c478bd9Sstevel@tonic-gate } 13187c478bd9Sstevel@tonic-gate case DL_DISABMULTI_REQ: { 13197c478bd9Sstevel@tonic-gate dl_disabmulti_req_t *dl = (dl_disabmulti_req_t *)cp; 13207c478bd9Sstevel@tonic-gate 13217c478bd9Sstevel@tonic-gate dl->dl_primitive = dl_primitive; 13227c478bd9Sstevel@tonic-gate dl->dl_addr_offset = offset; 13237c478bd9Sstevel@tonic-gate *addr_lenp = dl->dl_addr_length = hw_addr_length; 13247c478bd9Sstevel@tonic-gate *addr_offp = offset; 13257c478bd9Sstevel@tonic-gate break; 13267c478bd9Sstevel@tonic-gate } 13277c478bd9Sstevel@tonic-gate case DL_PROMISCON_REQ: 13287c478bd9Sstevel@tonic-gate case DL_PROMISCOFF_REQ: { 13297c478bd9Sstevel@tonic-gate dl_promiscon_req_t *dl = (dl_promiscon_req_t *)cp; 13307c478bd9Sstevel@tonic-gate 13317c478bd9Sstevel@tonic-gate dl->dl_primitive = dl_primitive; 13327c478bd9Sstevel@tonic-gate dl->dl_level = DL_PROMISC_MULTI; 13337c478bd9Sstevel@tonic-gate break; 13347c478bd9Sstevel@tonic-gate } 13357c478bd9Sstevel@tonic-gate } 13367c478bd9Sstevel@tonic-gate ip1dbg(("ill_create_dl: addr_len %d, addr_off %d\n", 13377c478bd9Sstevel@tonic-gate *addr_lenp, *addr_offp)); 13387c478bd9Sstevel@tonic-gate return (mp); 13397c478bd9Sstevel@tonic-gate } 13407c478bd9Sstevel@tonic-gate 13417c478bd9Sstevel@tonic-gate /* 1342bd670b35SErik Nordmark * Rejoin any groups for which we have ilms. 1343bd670b35SErik Nordmark * 1344bd670b35SErik Nordmark * This is only needed for IPMP when the cast_ill changes since that 1345bd670b35SErik Nordmark * change is invisible to the ilm. Other interface changes are handled 1346bd670b35SErik Nordmark * by conn_update_ill. 13477c478bd9Sstevel@tonic-gate */ 13487c478bd9Sstevel@tonic-gate void 13497c478bd9Sstevel@tonic-gate ill_recover_multicast(ill_t *ill) 13507c478bd9Sstevel@tonic-gate { 13517c478bd9Sstevel@tonic-gate ilm_t *ilm; 13527c478bd9Sstevel@tonic-gate char addrbuf[INET6_ADDRSTRLEN]; 13537c478bd9Sstevel@tonic-gate 1354b127ac41SPhilip Kirk ill->ill_need_recover_multicast = 0; 1355b127ac41SPhilip Kirk 1356bd670b35SErik Nordmark rw_enter(&ill->ill_mcast_lock, RW_WRITER); 13577c478bd9Sstevel@tonic-gate for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) { 13587c478bd9Sstevel@tonic-gate /* 1359bd670b35SErik Nordmark * If we have more then one ilm for the group (e.g., with 1360bd670b35SErik Nordmark * different zoneid) then we should not tell the driver 1361bd670b35SErik Nordmark * to join unless this is the first ilm for the group. 13627c478bd9Sstevel@tonic-gate */ 1363bd670b35SErik Nordmark if (ilm_numentries(ill, &ilm->ilm_v6addr) > 1 && 1364bd670b35SErik Nordmark ilm_lookup(ill, &ilm->ilm_v6addr, ALL_ZONES) != ilm) { 13657c478bd9Sstevel@tonic-gate continue; 1366e11c3f44Smeem } 1367e11c3f44Smeem 1368e11c3f44Smeem ip1dbg(("ill_recover_multicast: %s\n", inet_ntop(AF_INET6, 1369e11c3f44Smeem &ilm->ilm_v6addr, addrbuf, sizeof (addrbuf)))); 1370e11c3f44Smeem 13717c478bd9Sstevel@tonic-gate if (IN6_IS_ADDR_UNSPECIFIED(&ilm->ilm_v6addr)) { 1372b127ac41SPhilip Kirk (void) ill_join_allmulti(ill); 13737c478bd9Sstevel@tonic-gate } else { 1374e11c3f44Smeem if (ill->ill_isv6) 1375e11c3f44Smeem mld_joingroup(ilm); 1376e11c3f44Smeem else 1377e11c3f44Smeem igmp_joingroup(ilm); 1378e11c3f44Smeem 1379bd670b35SErik Nordmark (void) ip_ll_multireq(ill, &ilm->ilm_v6addr, 1380bd670b35SErik Nordmark DL_ENABMULTI_REQ); 13817c478bd9Sstevel@tonic-gate } 13827c478bd9Sstevel@tonic-gate } 1383bd670b35SErik Nordmark rw_exit(&ill->ill_mcast_lock); 1384bd670b35SErik Nordmark /* Send any deferred/queued DLPI or IP packets */ 1385bd670b35SErik Nordmark ill_mcast_send_queued(ill); 1386bd670b35SErik Nordmark ill_dlpi_send_queued(ill); 1387bd670b35SErik Nordmark ill_mcast_timer_start(ill->ill_ipst); 13887c478bd9Sstevel@tonic-gate } 13897c478bd9Sstevel@tonic-gate 13907c478bd9Sstevel@tonic-gate /* 13917c478bd9Sstevel@tonic-gate * The opposite of ill_recover_multicast() -- leaves all multicast groups 1392e11c3f44Smeem * that were explicitly joined. 1393bd670b35SErik Nordmark * 1394bd670b35SErik Nordmark * This is only needed for IPMP when the cast_ill changes since that 1395bd670b35SErik Nordmark * change is invisible to the ilm. Other interface changes are handled 1396bd670b35SErik Nordmark * by conn_update_ill. 13977c478bd9Sstevel@tonic-gate */ 13987c478bd9Sstevel@tonic-gate void 13997c478bd9Sstevel@tonic-gate ill_leave_multicast(ill_t *ill) 14007c478bd9Sstevel@tonic-gate { 14017c478bd9Sstevel@tonic-gate ilm_t *ilm; 14027c478bd9Sstevel@tonic-gate char addrbuf[INET6_ADDRSTRLEN]; 14037c478bd9Sstevel@tonic-gate 1404b127ac41SPhilip Kirk ill->ill_need_recover_multicast = 1; 1405b127ac41SPhilip Kirk 1406bd670b35SErik Nordmark rw_enter(&ill->ill_mcast_lock, RW_WRITER); 14077c478bd9Sstevel@tonic-gate for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) { 14087c478bd9Sstevel@tonic-gate /* 1409bd670b35SErik Nordmark * If we have more then one ilm for the group (e.g., with 1410bd670b35SErik Nordmark * different zoneid) then we should not tell the driver 1411bd670b35SErik Nordmark * to leave unless this is the first ilm for the group. 14127c478bd9Sstevel@tonic-gate */ 1413bd670b35SErik Nordmark if (ilm_numentries(ill, &ilm->ilm_v6addr) > 1 && 1414bd670b35SErik Nordmark ilm_lookup(ill, &ilm->ilm_v6addr, ALL_ZONES) != ilm) { 14157c478bd9Sstevel@tonic-gate continue; 1416e11c3f44Smeem } 1417e11c3f44Smeem 1418e11c3f44Smeem ip1dbg(("ill_leave_multicast: %s\n", inet_ntop(AF_INET6, 1419e11c3f44Smeem &ilm->ilm_v6addr, addrbuf, sizeof (addrbuf)))); 1420e11c3f44Smeem 14217c478bd9Sstevel@tonic-gate if (IN6_IS_ADDR_UNSPECIFIED(&ilm->ilm_v6addr)) { 1422b127ac41SPhilip Kirk ill_leave_allmulti(ill); 14237c478bd9Sstevel@tonic-gate } else { 1424e11c3f44Smeem if (ill->ill_isv6) 1425e11c3f44Smeem mld_leavegroup(ilm); 1426e11c3f44Smeem else 1427e11c3f44Smeem igmp_leavegroup(ilm); 1428e11c3f44Smeem 1429bd670b35SErik Nordmark (void) ip_ll_multireq(ill, &ilm->ilm_v6addr, 1430bd670b35SErik Nordmark DL_DISABMULTI_REQ); 14317c478bd9Sstevel@tonic-gate } 14327c478bd9Sstevel@tonic-gate } 1433bd670b35SErik Nordmark rw_exit(&ill->ill_mcast_lock); 1434bd670b35SErik Nordmark /* Send any deferred/queued DLPI or IP packets */ 1435bd670b35SErik Nordmark ill_mcast_send_queued(ill); 1436bd670b35SErik Nordmark ill_dlpi_send_queued(ill); 1437bd670b35SErik Nordmark ill_mcast_timer_start(ill->ill_ipst); 14387c478bd9Sstevel@tonic-gate } 14397c478bd9Sstevel@tonic-gate 1440bd670b35SErik Nordmark /* 1441bd670b35SErik Nordmark * Interface used by IP input/output. 1442bd670b35SErik Nordmark * Returns true if there is a member on the ill for any zoneid. 1443bd670b35SErik Nordmark */ 1444bd670b35SErik Nordmark boolean_t 1445bd670b35SErik Nordmark ill_hasmembers_v6(ill_t *ill, const in6_addr_t *v6group) 1446bd670b35SErik Nordmark { 1447bd670b35SErik Nordmark ilm_t *ilm; 1448bd670b35SErik Nordmark 1449bd670b35SErik Nordmark rw_enter(&ill->ill_mcast_lock, RW_READER); 1450bd670b35SErik Nordmark ilm = ilm_lookup(ill, v6group, ALL_ZONES); 1451bd670b35SErik Nordmark rw_exit(&ill->ill_mcast_lock); 1452bd670b35SErik Nordmark return (ilm != NULL); 1453bd670b35SErik Nordmark } 1454bd670b35SErik Nordmark 1455bd670b35SErik Nordmark /* 1456bd670b35SErik Nordmark * Interface used by IP input/output. 1457bd670b35SErik Nordmark * Returns true if there is a member on the ill for any zoneid. 1458bd670b35SErik Nordmark * 1459bd670b35SErik Nordmark * The group and source can't be INADDR_ANY here so no need to translate to 1460bd670b35SErik Nordmark * the unspecified IPv6 address. 1461bd670b35SErik Nordmark */ 1462bd670b35SErik Nordmark boolean_t 1463bd670b35SErik Nordmark ill_hasmembers_v4(ill_t *ill, ipaddr_t group) 14647c478bd9Sstevel@tonic-gate { 14657c478bd9Sstevel@tonic-gate in6_addr_t v6group; 14667c478bd9Sstevel@tonic-gate 14677c478bd9Sstevel@tonic-gate IN6_IPADDR_TO_V4MAPPED(group, &v6group); 1468bd670b35SErik Nordmark return (ill_hasmembers_v6(ill, &v6group)); 14697c478bd9Sstevel@tonic-gate } 14707c478bd9Sstevel@tonic-gate 14717c478bd9Sstevel@tonic-gate /* 1472bd670b35SErik Nordmark * Interface used by IP input/output. 1473bd670b35SErik Nordmark * Returns true if there is a member on the ill for any zoneid except skipzone. 14747c478bd9Sstevel@tonic-gate */ 1475bd670b35SErik Nordmark boolean_t 1476bd670b35SErik Nordmark ill_hasmembers_otherzones_v6(ill_t *ill, const in6_addr_t *v6group, 1477bd670b35SErik Nordmark zoneid_t skipzone) 14787c478bd9Sstevel@tonic-gate { 14797c478bd9Sstevel@tonic-gate ilm_t *ilm; 1480bd670b35SErik Nordmark 1481bd670b35SErik Nordmark rw_enter(&ill->ill_mcast_lock, RW_READER); 1482bd670b35SErik Nordmark for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) { 1483bd670b35SErik Nordmark if (IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, v6group) && 1484bd670b35SErik Nordmark ilm->ilm_zoneid != skipzone) { 1485bd670b35SErik Nordmark rw_exit(&ill->ill_mcast_lock); 1486bd670b35SErik Nordmark return (B_TRUE); 1487bd670b35SErik Nordmark } 1488bd670b35SErik Nordmark } 1489bd670b35SErik Nordmark rw_exit(&ill->ill_mcast_lock); 1490bd670b35SErik Nordmark return (B_FALSE); 1491bd670b35SErik Nordmark } 14927c478bd9Sstevel@tonic-gate 14937c478bd9Sstevel@tonic-gate /* 1494bd670b35SErik Nordmark * Interface used by IP input/output. 1495bd670b35SErik Nordmark * Returns true if there is a member on the ill for any zoneid except skipzone. 1496bd670b35SErik Nordmark * 1497bd670b35SErik Nordmark * The group and source can't be INADDR_ANY here so no need to translate to 1498bd670b35SErik Nordmark * the unspecified IPv6 address. 1499e11c3f44Smeem */ 1500bd670b35SErik Nordmark boolean_t 1501bd670b35SErik Nordmark ill_hasmembers_otherzones_v4(ill_t *ill, ipaddr_t group, zoneid_t skipzone) 1502bd670b35SErik Nordmark { 1503bd670b35SErik Nordmark in6_addr_t v6group; 1504e11c3f44Smeem 1505bd670b35SErik Nordmark IN6_IPADDR_TO_V4MAPPED(group, &v6group); 1506bd670b35SErik Nordmark return (ill_hasmembers_otherzones_v6(ill, &v6group, skipzone)); 1507bd670b35SErik Nordmark } 1508bd670b35SErik Nordmark 1509bd670b35SErik Nordmark /* 1510bd670b35SErik Nordmark * Interface used by IP input. 1511bd670b35SErik Nordmark * Returns the next numerically larger zoneid that has a member. If none exist 1512bd670b35SErik Nordmark * then returns -1 (ALL_ZONES). 1513bd670b35SErik Nordmark * The normal usage is for the caller to start with a -1 zoneid (ALL_ZONES) 1514bd670b35SErik Nordmark * to find the first zoneid which has a member, and then pass that in for 1515bd670b35SErik Nordmark * subsequent calls until ALL_ZONES is returned. 1516bd670b35SErik Nordmark * 1517bd670b35SErik Nordmark * The implementation of ill_hasmembers_nextzone() assumes the ilms 1518bd670b35SErik Nordmark * are sorted by zoneid for efficiency. 1519bd670b35SErik Nordmark */ 1520bd670b35SErik Nordmark zoneid_t 1521bd670b35SErik Nordmark ill_hasmembers_nextzone_v6(ill_t *ill, const in6_addr_t *v6group, 1522bd670b35SErik Nordmark zoneid_t zoneid) 1523bd670b35SErik Nordmark { 1524bd670b35SErik Nordmark ilm_t *ilm; 1525bd670b35SErik Nordmark 1526bd670b35SErik Nordmark rw_enter(&ill->ill_mcast_lock, RW_READER); 1527bd670b35SErik Nordmark for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) { 1528bd670b35SErik Nordmark if (IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, v6group) && 1529bd670b35SErik Nordmark ilm->ilm_zoneid > zoneid) { 1530bd670b35SErik Nordmark zoneid = ilm->ilm_zoneid; 1531bd670b35SErik Nordmark rw_exit(&ill->ill_mcast_lock); 1532bd670b35SErik Nordmark return (zoneid); 1533bd670b35SErik Nordmark } 1534bd670b35SErik Nordmark } 1535bd670b35SErik Nordmark rw_exit(&ill->ill_mcast_lock); 1536bd670b35SErik Nordmark return (ALL_ZONES); 1537bd670b35SErik Nordmark } 1538bd670b35SErik Nordmark 1539bd670b35SErik Nordmark /* 1540bd670b35SErik Nordmark * Interface used by IP input. 1541bd670b35SErik Nordmark * Returns the next numerically larger zoneid that has a member. If none exist 1542bd670b35SErik Nordmark * then returns -1 (ALL_ZONES). 1543bd670b35SErik Nordmark * 1544bd670b35SErik Nordmark * The group and source can't be INADDR_ANY here so no need to translate to 1545bd670b35SErik Nordmark * the unspecified IPv6 address. 1546bd670b35SErik Nordmark */ 1547bd670b35SErik Nordmark zoneid_t 1548bd670b35SErik Nordmark ill_hasmembers_nextzone_v4(ill_t *ill, ipaddr_t group, zoneid_t zoneid) 1549bd670b35SErik Nordmark { 1550bd670b35SErik Nordmark in6_addr_t v6group; 1551bd670b35SErik Nordmark 1552bd670b35SErik Nordmark IN6_IPADDR_TO_V4MAPPED(group, &v6group); 1553bd670b35SErik Nordmark 1554bd670b35SErik Nordmark return (ill_hasmembers_nextzone_v6(ill, &v6group, zoneid)); 1555bd670b35SErik Nordmark } 1556bd670b35SErik Nordmark 1557bd670b35SErik Nordmark /* 1558bd670b35SErik Nordmark * Find an ilm matching the ill, group, and zoneid. 1559bd670b35SErik Nordmark */ 1560bd670b35SErik Nordmark static ilm_t * 1561bd670b35SErik Nordmark ilm_lookup(ill_t *ill, const in6_addr_t *v6group, zoneid_t zoneid) 1562bd670b35SErik Nordmark { 1563bd670b35SErik Nordmark ilm_t *ilm; 1564bd670b35SErik Nordmark 1565bd670b35SErik Nordmark ASSERT(RW_LOCK_HELD(&ill->ill_mcast_lock)); 1566bd670b35SErik Nordmark 1567bd670b35SErik Nordmark for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) { 1568e11c3f44Smeem if (!IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, v6group)) 1569e11c3f44Smeem continue; 1570e11c3f44Smeem if (zoneid != ALL_ZONES && zoneid != ilm->ilm_zoneid) 1571e11c3f44Smeem continue; 1572bd670b35SErik Nordmark 1573bd670b35SErik Nordmark ASSERT(ilm->ilm_ill == ill); 1574e11c3f44Smeem return (ilm); 1575e11c3f44Smeem } 1576bd670b35SErik Nordmark return (NULL); 15777c478bd9Sstevel@tonic-gate } 15787c478bd9Sstevel@tonic-gate 15797c478bd9Sstevel@tonic-gate /* 15807c478bd9Sstevel@tonic-gate * How many members on this ill? 1581bd670b35SErik Nordmark * Since each shared-IP zone has a separate ilm for the same group/ill 1582bd670b35SErik Nordmark * we can have several. 15837c478bd9Sstevel@tonic-gate */ 1584bd670b35SErik Nordmark static int 1585bd670b35SErik Nordmark ilm_numentries(ill_t *ill, const in6_addr_t *v6group) 15867c478bd9Sstevel@tonic-gate { 15877c478bd9Sstevel@tonic-gate ilm_t *ilm; 15887c478bd9Sstevel@tonic-gate int i = 0; 15897c478bd9Sstevel@tonic-gate 1590bd670b35SErik Nordmark ASSERT(RW_LOCK_HELD(&ill->ill_mcast_lock)); 15917c478bd9Sstevel@tonic-gate for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) { 15927c478bd9Sstevel@tonic-gate if (IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, v6group)) { 15937c478bd9Sstevel@tonic-gate i++; 15947c478bd9Sstevel@tonic-gate } 15957c478bd9Sstevel@tonic-gate } 15967c478bd9Sstevel@tonic-gate return (i); 15977c478bd9Sstevel@tonic-gate } 15987c478bd9Sstevel@tonic-gate 15997c478bd9Sstevel@tonic-gate /* Caller guarantees that the group is not already on the list */ 16007c478bd9Sstevel@tonic-gate static ilm_t * 1601bd670b35SErik Nordmark ilm_add(ill_t *ill, const in6_addr_t *v6group, ilg_stat_t ilgstat, 1602e11c3f44Smeem mcast_record_t ilg_fmode, slist_t *ilg_flist, zoneid_t zoneid) 16037c478bd9Sstevel@tonic-gate { 16047c478bd9Sstevel@tonic-gate ilm_t *ilm; 16057c478bd9Sstevel@tonic-gate ilm_t *ilm_cur; 16067c478bd9Sstevel@tonic-gate ilm_t **ilm_ptpn; 16077c478bd9Sstevel@tonic-gate 1608bd670b35SErik Nordmark ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock)); 16097c478bd9Sstevel@tonic-gate ilm = GETSTRUCT(ilm_t, 1); 16107c478bd9Sstevel@tonic-gate if (ilm == NULL) 16117c478bd9Sstevel@tonic-gate return (NULL); 16127c478bd9Sstevel@tonic-gate if (ilgstat != ILGSTAT_NONE && !SLIST_IS_EMPTY(ilg_flist)) { 16137c478bd9Sstevel@tonic-gate ilm->ilm_filter = l_alloc(); 16147c478bd9Sstevel@tonic-gate if (ilm->ilm_filter == NULL) { 16157c478bd9Sstevel@tonic-gate mi_free(ilm); 16167c478bd9Sstevel@tonic-gate return (NULL); 16177c478bd9Sstevel@tonic-gate } 16187c478bd9Sstevel@tonic-gate } 16197c478bd9Sstevel@tonic-gate ilm->ilm_v6addr = *v6group; 16207c478bd9Sstevel@tonic-gate ilm->ilm_refcnt = 1; 16217c478bd9Sstevel@tonic-gate ilm->ilm_zoneid = zoneid; 16227c478bd9Sstevel@tonic-gate ilm->ilm_timer = INFINITY; 16237c478bd9Sstevel@tonic-gate ilm->ilm_rtx.rtx_timer = INFINITY; 162445916cd2Sjpk 16257c478bd9Sstevel@tonic-gate ilm->ilm_ill = ill; 1626968d2fd1Ssowmini DTRACE_PROBE3(ill__incr__cnt, (ill_t *), ill, 1627968d2fd1Ssowmini (char *), "ilm", (void *), ilm); 1628384ad179Ssowmini ill->ill_ilm_cnt++; 1629e11c3f44Smeem 1630f4b3ec61Sdh155122 ASSERT(ill->ill_ipst); 1631f4b3ec61Sdh155122 ilm->ilm_ipst = ill->ill_ipst; /* No netstack_hold */ 1632f4b3ec61Sdh155122 1633bd670b35SErik Nordmark /* The ill/ipif could have just been marked as condemned */ 16347c478bd9Sstevel@tonic-gate 16357c478bd9Sstevel@tonic-gate /* 1636bd670b35SErik Nordmark * To make ill_hasmembers_nextzone_v6 work we keep the list 1637bd670b35SErik Nordmark * sorted by zoneid. 16387c478bd9Sstevel@tonic-gate */ 16397c478bd9Sstevel@tonic-gate ilm_cur = ill->ill_ilm; 16407c478bd9Sstevel@tonic-gate ilm_ptpn = &ill->ill_ilm; 1641bd670b35SErik Nordmark while (ilm_cur != NULL && ilm_cur->ilm_zoneid < ilm->ilm_zoneid) { 16427c478bd9Sstevel@tonic-gate ilm_ptpn = &ilm_cur->ilm_next; 16437c478bd9Sstevel@tonic-gate ilm_cur = ilm_cur->ilm_next; 16447c478bd9Sstevel@tonic-gate } 16457c478bd9Sstevel@tonic-gate ilm->ilm_next = ilm_cur; 16467c478bd9Sstevel@tonic-gate *ilm_ptpn = ilm; 16477c478bd9Sstevel@tonic-gate 16487c478bd9Sstevel@tonic-gate /* 16497c478bd9Sstevel@tonic-gate * If we have an associated ilg, use its filter state; if not, 16507c478bd9Sstevel@tonic-gate * default to (EXCLUDE, NULL) and set no_ilg_cnt to track this. 16517c478bd9Sstevel@tonic-gate */ 16527c478bd9Sstevel@tonic-gate if (ilgstat != ILGSTAT_NONE) { 16537c478bd9Sstevel@tonic-gate if (!SLIST_IS_EMPTY(ilg_flist)) 16547c478bd9Sstevel@tonic-gate l_copy(ilg_flist, ilm->ilm_filter); 16557c478bd9Sstevel@tonic-gate ilm->ilm_fmode = ilg_fmode; 16567c478bd9Sstevel@tonic-gate } else { 16577c478bd9Sstevel@tonic-gate ilm->ilm_no_ilg_cnt = 1; 16587c478bd9Sstevel@tonic-gate ilm->ilm_fmode = MODE_IS_EXCLUDE; 16597c478bd9Sstevel@tonic-gate } 16607c478bd9Sstevel@tonic-gate 16617c478bd9Sstevel@tonic-gate return (ilm); 16627c478bd9Sstevel@tonic-gate } 16637c478bd9Sstevel@tonic-gate 1664a7acbe81Ssowmini void 1665968d2fd1Ssowmini ilm_inactive(ilm_t *ilm) 1666968d2fd1Ssowmini { 1667968d2fd1Ssowmini FREE_SLIST(ilm->ilm_filter); 1668968d2fd1Ssowmini FREE_SLIST(ilm->ilm_pendsrcs); 1669968d2fd1Ssowmini FREE_SLIST(ilm->ilm_rtx.rtx_allow); 1670968d2fd1Ssowmini FREE_SLIST(ilm->ilm_rtx.rtx_block); 1671968d2fd1Ssowmini ilm->ilm_ipst = NULL; 1672968d2fd1Ssowmini mi_free((char *)ilm); 1673968d2fd1Ssowmini } 1674968d2fd1Ssowmini 16757c478bd9Sstevel@tonic-gate /* 16767c478bd9Sstevel@tonic-gate * Unlink ilm and free it. 16777c478bd9Sstevel@tonic-gate */ 16787c478bd9Sstevel@tonic-gate static void 16797c478bd9Sstevel@tonic-gate ilm_delete(ilm_t *ilm) 16807c478bd9Sstevel@tonic-gate { 1681bd670b35SErik Nordmark ill_t *ill = ilm->ilm_ill; 16827c478bd9Sstevel@tonic-gate ilm_t **ilmp; 1683968d2fd1Ssowmini boolean_t need_wakeup; 1684968d2fd1Ssowmini 16857c478bd9Sstevel@tonic-gate /* 16867c478bd9Sstevel@tonic-gate * Delete under lock protection so that readers don't stumble 16877c478bd9Sstevel@tonic-gate * on bad ilm_next 16887c478bd9Sstevel@tonic-gate */ 1689bd670b35SErik Nordmark ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock)); 16907c478bd9Sstevel@tonic-gate 16917c478bd9Sstevel@tonic-gate for (ilmp = &ill->ill_ilm; *ilmp != ilm; ilmp = &(*ilmp)->ilm_next) 16927c478bd9Sstevel@tonic-gate ; 1693bd670b35SErik Nordmark 16947c478bd9Sstevel@tonic-gate *ilmp = ilm->ilm_next; 1695968d2fd1Ssowmini 1696bd670b35SErik Nordmark mutex_enter(&ill->ill_lock); 1697968d2fd1Ssowmini /* 1698bd670b35SErik Nordmark * if we are the last reference to the ill, we may need to wakeup any 1699bd670b35SErik Nordmark * pending FREE or unplumb operations. This is because conn_update_ill 1700bd670b35SErik Nordmark * bails if there is a ilg_delete_all in progress. 1701968d2fd1Ssowmini */ 1702968d2fd1Ssowmini need_wakeup = B_FALSE; 1703968d2fd1Ssowmini DTRACE_PROBE3(ill__decr__cnt, (ill_t *), ill, 1704968d2fd1Ssowmini (char *), "ilm", (void *), ilm); 1705a7acbe81Ssowmini ASSERT(ill->ill_ilm_cnt > 0); 1706384ad179Ssowmini ill->ill_ilm_cnt--; 1707968d2fd1Ssowmini if (ILL_FREE_OK(ill)) 1708968d2fd1Ssowmini need_wakeup = B_TRUE; 1709968d2fd1Ssowmini 1710968d2fd1Ssowmini ilm_inactive(ilm); /* frees this ilm */ 1711968d2fd1Ssowmini 1712968d2fd1Ssowmini if (need_wakeup) { 1713968d2fd1Ssowmini /* drops ill lock */ 1714968d2fd1Ssowmini ipif_ill_refrele_tail(ill); 1715968d2fd1Ssowmini } else { 17167c478bd9Sstevel@tonic-gate mutex_exit(&ill->ill_lock); 17177c478bd9Sstevel@tonic-gate } 17187c478bd9Sstevel@tonic-gate } 17197c478bd9Sstevel@tonic-gate 1720e11c3f44Smeem /* 1721bd670b35SErik Nordmark * Lookup an ill based on the group, ifindex, ifaddr, and zoneid. 1722bd670b35SErik Nordmark * Applies to both IPv4 and IPv6, although ifaddr is only used with 1723bd670b35SErik Nordmark * IPv4. 1724bd670b35SErik Nordmark * Returns an error for IS_UNDER_IPMP and VNI interfaces. 1725bd670b35SErik Nordmark * On error it sets *errorp. 1726e11c3f44Smeem */ 1727bd670b35SErik Nordmark static ill_t * 1728bd670b35SErik Nordmark ill_mcast_lookup(const in6_addr_t *group, ipaddr_t ifaddr, uint_t ifindex, 1729bd670b35SErik Nordmark zoneid_t zoneid, ip_stack_t *ipst, int *errorp) 1730e11c3f44Smeem { 1731bd670b35SErik Nordmark ill_t *ill; 1732bd670b35SErik Nordmark ipaddr_t v4group; 1733e11c3f44Smeem 1734bd670b35SErik Nordmark if (IN6_IS_ADDR_V4MAPPED(group)) { 1735bd670b35SErik Nordmark IN6_V4MAPPED_TO_IPADDR(group, v4group); 1736e11c3f44Smeem 1737bd670b35SErik Nordmark if (ifindex != 0) { 1738bd670b35SErik Nordmark ill = ill_lookup_on_ifindex_zoneid(ifindex, zoneid, 1739bd670b35SErik Nordmark B_FALSE, ipst); 1740bd670b35SErik Nordmark } else if (ifaddr != INADDR_ANY) { 1741bd670b35SErik Nordmark ipif_t *ipif; 1742e11c3f44Smeem 1743bd670b35SErik Nordmark ipif = ipif_lookup_addr(ifaddr, NULL, zoneid, ipst); 1744bd670b35SErik Nordmark if (ipif == NULL) { 1745bd670b35SErik Nordmark ill = NULL; 1746bd670b35SErik Nordmark } else { 1747bd670b35SErik Nordmark ill = ipif->ipif_ill; 1748bd670b35SErik Nordmark ill_refhold(ill); 1749bd670b35SErik Nordmark ipif_refrele(ipif); 1750e11c3f44Smeem } 1751bd670b35SErik Nordmark } else { 1752bd670b35SErik Nordmark ill = ill_lookup_group_v4(v4group, zoneid, ipst, NULL, 1753bd670b35SErik Nordmark NULL); 1754e11c3f44Smeem } 1755bd670b35SErik Nordmark } else { 1756bd670b35SErik Nordmark if (ifindex != 0) { 1757bd670b35SErik Nordmark ill = ill_lookup_on_ifindex_zoneid(ifindex, zoneid, 1758bd670b35SErik Nordmark B_TRUE, ipst); 1759bd670b35SErik Nordmark } else { 1760bd670b35SErik Nordmark ill = ill_lookup_group_v6(group, zoneid, ipst, NULL, 1761bd670b35SErik Nordmark NULL); 1762bd670b35SErik Nordmark } 1763bd670b35SErik Nordmark } 1764bd670b35SErik Nordmark if (ill == NULL) { 1765bd670b35SErik Nordmark if (ifindex != 0) 1766bd670b35SErik Nordmark *errorp = ENXIO; 1767bd670b35SErik Nordmark else 1768bd670b35SErik Nordmark *errorp = EADDRNOTAVAIL; 1769e11c3f44Smeem return (NULL); 1770e11c3f44Smeem } 1771bd670b35SErik Nordmark /* operation not supported on the virtual network interface */ 1772bd670b35SErik Nordmark if (IS_UNDER_IPMP(ill) || IS_VNI(ill)) { 1773bd670b35SErik Nordmark ill_refrele(ill); 1774bd670b35SErik Nordmark *errorp = EINVAL; 1775bd670b35SErik Nordmark return (NULL); 1776e11c3f44Smeem } 1777bd670b35SErik Nordmark return (ill); 1778e11c3f44Smeem } 1779e11c3f44Smeem 1780e11c3f44Smeem /* 1781bd670b35SErik Nordmark * Looks up the appropriate ill given an interface index (or interface address) 1782bd670b35SErik Nordmark * and multicast group. On success, returns 0, with *illpp pointing to the 1783bd670b35SErik Nordmark * found struct. On failure, returns an errno and *illpp is set to NULL. 1784bd670b35SErik Nordmark * 1785bd670b35SErik Nordmark * Returns an error for IS_UNDER_IPMP and VNI interfaces. 1786bd670b35SErik Nordmark * 1787bd670b35SErik Nordmark * Handles both IPv4 and IPv6. The ifaddr argument only applies in the 1788bd670b35SErik Nordmark * case of IPv4. 17897c478bd9Sstevel@tonic-gate */ 17907c478bd9Sstevel@tonic-gate int 1791bd670b35SErik Nordmark ip_opt_check(conn_t *connp, const in6_addr_t *v6group, 1792bd670b35SErik Nordmark const in6_addr_t *v6src, ipaddr_t ifaddr, uint_t ifindex, ill_t **illpp) 17937c478bd9Sstevel@tonic-gate { 17947c478bd9Sstevel@tonic-gate boolean_t src_unspec; 17957c478bd9Sstevel@tonic-gate ill_t *ill = NULL; 1796f4b3ec61Sdh155122 ip_stack_t *ipst = connp->conn_netstack->netstack_ip; 1797bd670b35SErik Nordmark int error = 0; 1798bd670b35SErik Nordmark 1799bd670b35SErik Nordmark *illpp = NULL; 18007c478bd9Sstevel@tonic-gate 18017c478bd9Sstevel@tonic-gate src_unspec = IN6_IS_ADDR_UNSPECIFIED(v6src); 18027c478bd9Sstevel@tonic-gate 18037c478bd9Sstevel@tonic-gate if (IN6_IS_ADDR_V4MAPPED(v6group)) { 1804bd670b35SErik Nordmark ipaddr_t v4group; 1805bd670b35SErik Nordmark ipaddr_t v4src; 1806bd670b35SErik Nordmark 18077c478bd9Sstevel@tonic-gate if (!IN6_IS_ADDR_V4MAPPED(v6src) && !src_unspec) 18087c478bd9Sstevel@tonic-gate return (EINVAL); 1809bd670b35SErik Nordmark IN6_V4MAPPED_TO_IPADDR(v6group, v4group); 18107c478bd9Sstevel@tonic-gate if (src_unspec) { 1811bd670b35SErik Nordmark v4src = INADDR_ANY; 18127c478bd9Sstevel@tonic-gate } else { 1813bd670b35SErik Nordmark IN6_V4MAPPED_TO_IPADDR(v6src, v4src); 18147c478bd9Sstevel@tonic-gate } 1815bd670b35SErik Nordmark if (!CLASSD(v4group) || CLASSD(v4src)) 18167c478bd9Sstevel@tonic-gate return (EINVAL); 18177c478bd9Sstevel@tonic-gate } else { 18187c478bd9Sstevel@tonic-gate if (IN6_IS_ADDR_V4MAPPED(v6src) && !src_unspec) 18197c478bd9Sstevel@tonic-gate return (EINVAL); 18207c478bd9Sstevel@tonic-gate if (!IN6_IS_ADDR_MULTICAST(v6group) || 18217c478bd9Sstevel@tonic-gate IN6_IS_ADDR_MULTICAST(v6src)) { 18227c478bd9Sstevel@tonic-gate return (EINVAL); 18237c478bd9Sstevel@tonic-gate } 18247c478bd9Sstevel@tonic-gate } 18257c478bd9Sstevel@tonic-gate 1826bd670b35SErik Nordmark ill = ill_mcast_lookup(v6group, ifaddr, ifindex, IPCL_ZONEID(connp), 1827bd670b35SErik Nordmark ipst, &error); 18287c478bd9Sstevel@tonic-gate *illpp = ill; 1829bd670b35SErik Nordmark return (error); 18307c478bd9Sstevel@tonic-gate } 18317c478bd9Sstevel@tonic-gate 18327c478bd9Sstevel@tonic-gate static int 18337c478bd9Sstevel@tonic-gate ip_get_srcfilter(conn_t *connp, struct group_filter *gf, 1834bd670b35SErik Nordmark struct ip_msfilter *imsf, const struct in6_addr *group, boolean_t issin6) 18357c478bd9Sstevel@tonic-gate { 18367c478bd9Sstevel@tonic-gate ilg_t *ilg; 18377c478bd9Sstevel@tonic-gate int i, numsrc, fmode, outsrcs; 18387c478bd9Sstevel@tonic-gate struct sockaddr_in *sin; 18397c478bd9Sstevel@tonic-gate struct sockaddr_in6 *sin6; 18407c478bd9Sstevel@tonic-gate struct in_addr *addrp; 18417c478bd9Sstevel@tonic-gate slist_t *fp; 18427c478bd9Sstevel@tonic-gate boolean_t is_v4only_api; 1843bd670b35SErik Nordmark ipaddr_t ifaddr; 1844bd670b35SErik Nordmark uint_t ifindex; 18457c478bd9Sstevel@tonic-gate 18467c478bd9Sstevel@tonic-gate if (gf == NULL) { 18477c478bd9Sstevel@tonic-gate ASSERT(imsf != NULL); 1848bd670b35SErik Nordmark ASSERT(!issin6); 18497c478bd9Sstevel@tonic-gate is_v4only_api = B_TRUE; 18507c478bd9Sstevel@tonic-gate outsrcs = imsf->imsf_numsrc; 1851bd670b35SErik Nordmark ifaddr = imsf->imsf_interface.s_addr; 1852bd670b35SErik Nordmark ifindex = 0; 18537c478bd9Sstevel@tonic-gate } else { 18547c478bd9Sstevel@tonic-gate ASSERT(imsf == NULL); 18557c478bd9Sstevel@tonic-gate is_v4only_api = B_FALSE; 18567c478bd9Sstevel@tonic-gate outsrcs = gf->gf_numsrc; 1857bd670b35SErik Nordmark ifaddr = INADDR_ANY; 1858bd670b35SErik Nordmark ifindex = gf->gf_interface; 1859bd670b35SErik Nordmark } 1860bd670b35SErik Nordmark 1861bd670b35SErik Nordmark /* No need to use ill_mcast_serializer for the reader */ 1862bd670b35SErik Nordmark rw_enter(&connp->conn_ilg_lock, RW_READER); 1863bd670b35SErik Nordmark ilg = ilg_lookup(connp, group, ifaddr, ifindex); 1864bd670b35SErik Nordmark if (ilg == NULL) { 1865bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 1866bd670b35SErik Nordmark return (EADDRNOTAVAIL); 18677c478bd9Sstevel@tonic-gate } 18687c478bd9Sstevel@tonic-gate 18697c478bd9Sstevel@tonic-gate /* 18707c478bd9Sstevel@tonic-gate * In the kernel, we use the state definitions MODE_IS_[IN|EX]CLUDE 18717c478bd9Sstevel@tonic-gate * to identify the filter mode; but the API uses MCAST_[IN|EX]CLUDE. 18727c478bd9Sstevel@tonic-gate * So we need to translate here. 18737c478bd9Sstevel@tonic-gate */ 18747c478bd9Sstevel@tonic-gate fmode = (ilg->ilg_fmode == MODE_IS_INCLUDE) ? 18757c478bd9Sstevel@tonic-gate MCAST_INCLUDE : MCAST_EXCLUDE; 18767c478bd9Sstevel@tonic-gate if ((fp = ilg->ilg_filter) == NULL) { 18777c478bd9Sstevel@tonic-gate numsrc = 0; 18787c478bd9Sstevel@tonic-gate } else { 18797c478bd9Sstevel@tonic-gate for (i = 0; i < outsrcs; i++) { 18807c478bd9Sstevel@tonic-gate if (i == fp->sl_numsrc) 18817c478bd9Sstevel@tonic-gate break; 1882bd670b35SErik Nordmark if (issin6) { 18837c478bd9Sstevel@tonic-gate sin6 = (struct sockaddr_in6 *)&gf->gf_slist[i]; 18847c478bd9Sstevel@tonic-gate sin6->sin6_family = AF_INET6; 18857c478bd9Sstevel@tonic-gate sin6->sin6_addr = fp->sl_addr[i]; 18867c478bd9Sstevel@tonic-gate } else { 18877c478bd9Sstevel@tonic-gate if (is_v4only_api) { 18887c478bd9Sstevel@tonic-gate addrp = &imsf->imsf_slist[i]; 18897c478bd9Sstevel@tonic-gate } else { 18907c478bd9Sstevel@tonic-gate sin = (struct sockaddr_in *) 18917c478bd9Sstevel@tonic-gate &gf->gf_slist[i]; 18927c478bd9Sstevel@tonic-gate sin->sin_family = AF_INET; 18937c478bd9Sstevel@tonic-gate addrp = &sin->sin_addr; 18947c478bd9Sstevel@tonic-gate } 18957c478bd9Sstevel@tonic-gate IN6_V4MAPPED_TO_INADDR(&fp->sl_addr[i], addrp); 18967c478bd9Sstevel@tonic-gate } 18977c478bd9Sstevel@tonic-gate } 18987c478bd9Sstevel@tonic-gate numsrc = fp->sl_numsrc; 18997c478bd9Sstevel@tonic-gate } 19007c478bd9Sstevel@tonic-gate 19017c478bd9Sstevel@tonic-gate if (is_v4only_api) { 19027c478bd9Sstevel@tonic-gate imsf->imsf_numsrc = numsrc; 19037c478bd9Sstevel@tonic-gate imsf->imsf_fmode = fmode; 19047c478bd9Sstevel@tonic-gate } else { 19057c478bd9Sstevel@tonic-gate gf->gf_numsrc = numsrc; 19067c478bd9Sstevel@tonic-gate gf->gf_fmode = fmode; 19077c478bd9Sstevel@tonic-gate } 19087c478bd9Sstevel@tonic-gate 1909bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 19107c478bd9Sstevel@tonic-gate 19117c478bd9Sstevel@tonic-gate return (0); 19127c478bd9Sstevel@tonic-gate } 19137c478bd9Sstevel@tonic-gate 19147c478bd9Sstevel@tonic-gate /* 1915bd670b35SErik Nordmark * Common for IPv4 and IPv6. 19167c478bd9Sstevel@tonic-gate */ 19177c478bd9Sstevel@tonic-gate static int 19187c478bd9Sstevel@tonic-gate ip_set_srcfilter(conn_t *connp, struct group_filter *gf, 1919bd670b35SErik Nordmark struct ip_msfilter *imsf, const struct in6_addr *group, ill_t *ill, 1920bd670b35SErik Nordmark boolean_t issin6) 19217c478bd9Sstevel@tonic-gate { 19227c478bd9Sstevel@tonic-gate ilg_t *ilg; 1923fda3a3a5Sblu int i, err, infmode, new_fmode; 1924fda3a3a5Sblu uint_t insrcs; 19257c478bd9Sstevel@tonic-gate struct sockaddr_in *sin; 19267c478bd9Sstevel@tonic-gate struct sockaddr_in6 *sin6; 19277c478bd9Sstevel@tonic-gate struct in_addr *addrp; 19287c478bd9Sstevel@tonic-gate slist_t *orig_filter = NULL; 19297c478bd9Sstevel@tonic-gate slist_t *new_filter = NULL; 19307c478bd9Sstevel@tonic-gate mcast_record_t orig_fmode; 1931bd670b35SErik Nordmark boolean_t leave_group, is_v4only_api; 19327c478bd9Sstevel@tonic-gate ilg_stat_t ilgstat; 1933bd670b35SErik Nordmark ilm_t *ilm; 1934bd670b35SErik Nordmark ipaddr_t ifaddr; 1935bd670b35SErik Nordmark uint_t ifindex; 19367c478bd9Sstevel@tonic-gate 19377c478bd9Sstevel@tonic-gate if (gf == NULL) { 19387c478bd9Sstevel@tonic-gate ASSERT(imsf != NULL); 1939bd670b35SErik Nordmark ASSERT(!issin6); 19407c478bd9Sstevel@tonic-gate is_v4only_api = B_TRUE; 19417c478bd9Sstevel@tonic-gate insrcs = imsf->imsf_numsrc; 19427c478bd9Sstevel@tonic-gate infmode = imsf->imsf_fmode; 1943bd670b35SErik Nordmark ifaddr = imsf->imsf_interface.s_addr; 1944bd670b35SErik Nordmark ifindex = 0; 19457c478bd9Sstevel@tonic-gate } else { 19467c478bd9Sstevel@tonic-gate ASSERT(imsf == NULL); 19477c478bd9Sstevel@tonic-gate is_v4only_api = B_FALSE; 19487c478bd9Sstevel@tonic-gate insrcs = gf->gf_numsrc; 19497c478bd9Sstevel@tonic-gate infmode = gf->gf_fmode; 1950bd670b35SErik Nordmark ifaddr = INADDR_ANY; 1951bd670b35SErik Nordmark ifindex = gf->gf_interface; 19527c478bd9Sstevel@tonic-gate } 19537c478bd9Sstevel@tonic-gate 19547c478bd9Sstevel@tonic-gate /* Make sure we can handle the source list */ 19557c478bd9Sstevel@tonic-gate if (insrcs > MAX_FILTER_SIZE) 19567c478bd9Sstevel@tonic-gate return (ENOBUFS); 19577c478bd9Sstevel@tonic-gate 19587c478bd9Sstevel@tonic-gate /* 19597c478bd9Sstevel@tonic-gate * setting the filter to (INCLUDE, NULL) is treated 19607c478bd9Sstevel@tonic-gate * as a request to leave the group. 19617c478bd9Sstevel@tonic-gate */ 1962bd670b35SErik Nordmark leave_group = (infmode == MCAST_INCLUDE && insrcs == 0); 19637c478bd9Sstevel@tonic-gate 1964bd670b35SErik Nordmark mutex_enter(&ill->ill_mcast_serializer); 1965bd670b35SErik Nordmark rw_enter(&connp->conn_ilg_lock, RW_WRITER); 1966bd670b35SErik Nordmark ilg = ilg_lookup(connp, group, ifaddr, ifindex); 19677c478bd9Sstevel@tonic-gate if (ilg == NULL) { 19687c478bd9Sstevel@tonic-gate /* 19697c478bd9Sstevel@tonic-gate * if the request was actually to leave, and we 19707c478bd9Sstevel@tonic-gate * didn't find an ilg, there's nothing to do. 19717c478bd9Sstevel@tonic-gate */ 1972bd670b35SErik Nordmark if (leave_group) { 1973bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 1974bd670b35SErik Nordmark mutex_exit(&ill->ill_mcast_serializer); 1975bd670b35SErik Nordmark return (0); 1976bd670b35SErik Nordmark } 1977e11c3f44Smeem ilg = conn_ilg_alloc(connp, &err); 1978bd670b35SErik Nordmark if (ilg == NULL) { 1979bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 1980bd670b35SErik Nordmark mutex_exit(&ill->ill_mcast_serializer); 1981bd670b35SErik Nordmark return (err); 19827c478bd9Sstevel@tonic-gate } 19837c478bd9Sstevel@tonic-gate ilgstat = ILGSTAT_NEW; 1984bd670b35SErik Nordmark ilg->ilg_v6group = *group; 1985bd670b35SErik Nordmark ilg->ilg_ill = ill; 1986bd670b35SErik Nordmark ilg->ilg_ifaddr = ifaddr; 1987bd670b35SErik Nordmark ilg->ilg_ifindex = ifindex; 1988bd670b35SErik Nordmark } else if (leave_group) { 1989bd670b35SErik Nordmark /* 1990bd670b35SErik Nordmark * Make sure we have the correct serializer. The ill argument 1991bd670b35SErik Nordmark * might not match ilg_ill. 1992bd670b35SErik Nordmark */ 1993bd670b35SErik Nordmark ilg_refhold(ilg); 1994bd670b35SErik Nordmark mutex_exit(&ill->ill_mcast_serializer); 1995bd670b35SErik Nordmark ill = ilg->ilg_ill; 1996bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 1997bd670b35SErik Nordmark 1998bd670b35SErik Nordmark mutex_enter(&ill->ill_mcast_serializer); 1999bd670b35SErik Nordmark rw_enter(&connp->conn_ilg_lock, RW_WRITER); 2000bd670b35SErik Nordmark ilm = ilg->ilg_ilm; 2001bd670b35SErik Nordmark ilg->ilg_ilm = NULL; 20027c478bd9Sstevel@tonic-gate ilg_delete(connp, ilg, NULL); 2003bd670b35SErik Nordmark ilg_refrele(ilg); 2004bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 2005bd670b35SErik Nordmark if (ilm != NULL) 2006bd670b35SErik Nordmark (void) ip_delmulti_serial(ilm, B_FALSE, B_TRUE); 2007bd670b35SErik Nordmark mutex_exit(&ill->ill_mcast_serializer); 2008f1c454b4SSowmini Varadhan /* 2009f1c454b4SSowmini Varadhan * Now that all locks have been dropped, we can send any 2010f1c454b4SSowmini Varadhan * deferred/queued DLPI or IP packets 2011f1c454b4SSowmini Varadhan */ 2012f1c454b4SSowmini Varadhan ill_mcast_send_queued(ill); 2013f1c454b4SSowmini Varadhan ill_dlpi_send_queued(ill); 20147c478bd9Sstevel@tonic-gate return (0); 20157c478bd9Sstevel@tonic-gate } else { 20167c478bd9Sstevel@tonic-gate ilgstat = ILGSTAT_CHANGE; 20177c478bd9Sstevel@tonic-gate /* Preserve existing state in case ip_addmulti() fails */ 20187c478bd9Sstevel@tonic-gate orig_fmode = ilg->ilg_fmode; 20197c478bd9Sstevel@tonic-gate if (ilg->ilg_filter == NULL) { 20207c478bd9Sstevel@tonic-gate orig_filter = NULL; 20217c478bd9Sstevel@tonic-gate } else { 20227c478bd9Sstevel@tonic-gate orig_filter = l_alloc_copy(ilg->ilg_filter); 20237c478bd9Sstevel@tonic-gate if (orig_filter == NULL) { 2024bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 2025bd670b35SErik Nordmark mutex_exit(&ill->ill_mcast_serializer); 20267c478bd9Sstevel@tonic-gate return (ENOMEM); 20277c478bd9Sstevel@tonic-gate } 20287c478bd9Sstevel@tonic-gate } 20297c478bd9Sstevel@tonic-gate } 20307c478bd9Sstevel@tonic-gate 20317c478bd9Sstevel@tonic-gate /* 20327c478bd9Sstevel@tonic-gate * Alloc buffer to copy new state into (see below) before 20337c478bd9Sstevel@tonic-gate * we make any changes, so we can bail if it fails. 20347c478bd9Sstevel@tonic-gate */ 20357c478bd9Sstevel@tonic-gate if ((new_filter = l_alloc()) == NULL) { 2036bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 20377c478bd9Sstevel@tonic-gate err = ENOMEM; 20387c478bd9Sstevel@tonic-gate goto free_and_exit; 20397c478bd9Sstevel@tonic-gate } 20407c478bd9Sstevel@tonic-gate 20417c478bd9Sstevel@tonic-gate if (insrcs == 0) { 20427c478bd9Sstevel@tonic-gate CLEAR_SLIST(ilg->ilg_filter); 20437c478bd9Sstevel@tonic-gate } else { 20447c478bd9Sstevel@tonic-gate slist_t *fp; 20457c478bd9Sstevel@tonic-gate if (ilg->ilg_filter == NULL) { 20467c478bd9Sstevel@tonic-gate fp = l_alloc(); 20477c478bd9Sstevel@tonic-gate if (fp == NULL) { 20487c478bd9Sstevel@tonic-gate if (ilgstat == ILGSTAT_NEW) 20497c478bd9Sstevel@tonic-gate ilg_delete(connp, ilg, NULL); 2050bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 20517c478bd9Sstevel@tonic-gate err = ENOMEM; 20527c478bd9Sstevel@tonic-gate goto free_and_exit; 20537c478bd9Sstevel@tonic-gate } 20547c478bd9Sstevel@tonic-gate } else { 20557c478bd9Sstevel@tonic-gate fp = ilg->ilg_filter; 20567c478bd9Sstevel@tonic-gate } 20577c478bd9Sstevel@tonic-gate for (i = 0; i < insrcs; i++) { 2058bd670b35SErik Nordmark if (issin6) { 20597c478bd9Sstevel@tonic-gate sin6 = (struct sockaddr_in6 *)&gf->gf_slist[i]; 20607c478bd9Sstevel@tonic-gate fp->sl_addr[i] = sin6->sin6_addr; 20617c478bd9Sstevel@tonic-gate } else { 20627c478bd9Sstevel@tonic-gate if (is_v4only_api) { 20637c478bd9Sstevel@tonic-gate addrp = &imsf->imsf_slist[i]; 20647c478bd9Sstevel@tonic-gate } else { 20657c478bd9Sstevel@tonic-gate sin = (struct sockaddr_in *) 20667c478bd9Sstevel@tonic-gate &gf->gf_slist[i]; 20677c478bd9Sstevel@tonic-gate addrp = &sin->sin_addr; 20687c478bd9Sstevel@tonic-gate } 20697c478bd9Sstevel@tonic-gate IN6_INADDR_TO_V4MAPPED(addrp, &fp->sl_addr[i]); 20707c478bd9Sstevel@tonic-gate } 20717c478bd9Sstevel@tonic-gate } 20727c478bd9Sstevel@tonic-gate fp->sl_numsrc = insrcs; 20737c478bd9Sstevel@tonic-gate ilg->ilg_filter = fp; 20747c478bd9Sstevel@tonic-gate } 20757c478bd9Sstevel@tonic-gate /* 20767c478bd9Sstevel@tonic-gate * In the kernel, we use the state definitions MODE_IS_[IN|EX]CLUDE 20777c478bd9Sstevel@tonic-gate * to identify the filter mode; but the API uses MCAST_[IN|EX]CLUDE. 20787c478bd9Sstevel@tonic-gate * So we need to translate here. 20797c478bd9Sstevel@tonic-gate */ 20807c478bd9Sstevel@tonic-gate ilg->ilg_fmode = (infmode == MCAST_INCLUDE) ? 20817c478bd9Sstevel@tonic-gate MODE_IS_INCLUDE : MODE_IS_EXCLUDE; 20827c478bd9Sstevel@tonic-gate 20837c478bd9Sstevel@tonic-gate /* 20847c478bd9Sstevel@tonic-gate * Save copy of ilg's filter state to pass to other functions, 2085bd670b35SErik Nordmark * so we can release conn_ilg_lock now. 20867c478bd9Sstevel@tonic-gate */ 20877c478bd9Sstevel@tonic-gate new_fmode = ilg->ilg_fmode; 20887c478bd9Sstevel@tonic-gate l_copy(ilg->ilg_filter, new_filter); 20897c478bd9Sstevel@tonic-gate 2090bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 20917c478bd9Sstevel@tonic-gate 20927c478bd9Sstevel@tonic-gate /* 2093bd670b35SErik Nordmark * Now update the ill. We wait to do this until after the ilg 2094bd670b35SErik Nordmark * has been updated because we need to update the src filter 2095bd670b35SErik Nordmark * info for the ill, which involves looking at the status of 2096bd670b35SErik Nordmark * all the ilgs associated with this group/interface pair. 20977c478bd9Sstevel@tonic-gate */ 2098bd670b35SErik Nordmark ilm = ip_addmulti_serial(group, ill, connp->conn_zoneid, ilgstat, 2099bd670b35SErik Nordmark new_fmode, new_filter, &err); 21007c478bd9Sstevel@tonic-gate 2101bd670b35SErik Nordmark rw_enter(&connp->conn_ilg_lock, RW_WRITER); 2102bd670b35SErik Nordmark /* 2103bd670b35SErik Nordmark * Must look up the ilg again since we've not been holding 2104bd670b35SErik Nordmark * conn_ilg_lock. The ilg could have disappeared due to an unplumb 2105bd670b35SErik Nordmark * having called conn_update_ill, which can run once we dropped the 2106bd670b35SErik Nordmark * conn_ilg_lock above. 2107bd670b35SErik Nordmark */ 2108bd670b35SErik Nordmark ilg = ilg_lookup(connp, group, ifaddr, ifindex); 21097c478bd9Sstevel@tonic-gate if (ilg == NULL) { 2110bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 2111bd670b35SErik Nordmark if (ilm != NULL) { 2112bd670b35SErik Nordmark (void) ip_delmulti_serial(ilm, B_FALSE, 2113bd670b35SErik Nordmark (ilgstat == ILGSTAT_NEW)); 21147c478bd9Sstevel@tonic-gate } 2115bd670b35SErik Nordmark err = ENXIO; 21167c478bd9Sstevel@tonic-gate goto free_and_exit; 21177c478bd9Sstevel@tonic-gate } 21187c478bd9Sstevel@tonic-gate 2119bd670b35SErik Nordmark if (ilm != NULL) { 2120f1c454b4SSowmini Varadhan if (ilg->ilg_ill == NULL) { 2121f1c454b4SSowmini Varadhan /* some other thread is re-attaching this. */ 2122f1c454b4SSowmini Varadhan rw_exit(&connp->conn_ilg_lock); 2123f1c454b4SSowmini Varadhan (void) ip_delmulti_serial(ilm, B_FALSE, 2124f1c454b4SSowmini Varadhan (ilgstat == ILGSTAT_NEW)); 2125f1c454b4SSowmini Varadhan err = 0; 2126f1c454b4SSowmini Varadhan goto free_and_exit; 2127f1c454b4SSowmini Varadhan } 2128bd670b35SErik Nordmark /* Succeeded. Update the ilg to point at the ilm */ 21297c478bd9Sstevel@tonic-gate if (ilgstat == ILGSTAT_NEW) { 2130f1c454b4SSowmini Varadhan if (ilg->ilg_ilm == NULL) { 2131bd670b35SErik Nordmark ilg->ilg_ilm = ilm; 2132bd670b35SErik Nordmark ilm->ilm_ifaddr = ifaddr; /* For netstat */ 2133bd670b35SErik Nordmark } else { 2134f1c454b4SSowmini Varadhan /* some other thread is re-attaching this. */ 2135f1c454b4SSowmini Varadhan rw_exit(&connp->conn_ilg_lock); 2136f1c454b4SSowmini Varadhan (void) ip_delmulti_serial(ilm, B_FALSE, B_TRUE); 2137f1c454b4SSowmini Varadhan err = 0; 2138f1c454b4SSowmini Varadhan goto free_and_exit; 2139f1c454b4SSowmini Varadhan } 2140f1c454b4SSowmini Varadhan } else { 2141bd670b35SErik Nordmark /* 2142bd670b35SErik Nordmark * ip_addmulti didn't get a held ilm for 2143bd670b35SErik Nordmark * ILGSTAT_CHANGE; ilm_refcnt was unchanged. 2144bd670b35SErik Nordmark */ 2145bd670b35SErik Nordmark ASSERT(ilg->ilg_ilm == ilm); 2146bd670b35SErik Nordmark } 2147bd670b35SErik Nordmark } else { 2148bd670b35SErik Nordmark ASSERT(err != 0); 2149bd670b35SErik Nordmark /* 2150bd670b35SErik Nordmark * Failed to allocate the ilm. 2151bd670b35SErik Nordmark * Restore the original filter state, or delete the 2152bd670b35SErik Nordmark * newly-created ilg. 2153bd670b35SErik Nordmark * If ENETDOWN just clear ill_ilg since so that we 2154bd670b35SErik Nordmark * will rejoin when the ill comes back; don't report ENETDOWN 2155bd670b35SErik Nordmark * to application. 2156bd670b35SErik Nordmark */ 2157bd670b35SErik Nordmark if (ilgstat == ILGSTAT_NEW) { 2158bd670b35SErik Nordmark if (err == ENETDOWN) { 2159bd670b35SErik Nordmark ilg->ilg_ill = NULL; 2160bd670b35SErik Nordmark err = 0; 2161bd670b35SErik Nordmark } else { 21627c478bd9Sstevel@tonic-gate ilg_delete(connp, ilg, NULL); 2163bd670b35SErik Nordmark } 21647c478bd9Sstevel@tonic-gate } else { 21657c478bd9Sstevel@tonic-gate ilg->ilg_fmode = orig_fmode; 21667c478bd9Sstevel@tonic-gate if (SLIST_IS_EMPTY(orig_filter)) { 21677c478bd9Sstevel@tonic-gate CLEAR_SLIST(ilg->ilg_filter); 21687c478bd9Sstevel@tonic-gate } else { 21697c478bd9Sstevel@tonic-gate /* 21707c478bd9Sstevel@tonic-gate * We didn't free the filter, even if we 21717c478bd9Sstevel@tonic-gate * were trying to make the source list empty; 21727c478bd9Sstevel@tonic-gate * so if orig_filter isn't empty, the ilg 21737c478bd9Sstevel@tonic-gate * must still have a filter alloc'd. 21747c478bd9Sstevel@tonic-gate */ 21757c478bd9Sstevel@tonic-gate l_copy(orig_filter, ilg->ilg_filter); 21767c478bd9Sstevel@tonic-gate } 21777c478bd9Sstevel@tonic-gate } 21787c478bd9Sstevel@tonic-gate } 2179bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 21807c478bd9Sstevel@tonic-gate 21817c478bd9Sstevel@tonic-gate free_and_exit: 2182bd670b35SErik Nordmark mutex_exit(&ill->ill_mcast_serializer); 2183f1c454b4SSowmini Varadhan ill_mcast_send_queued(ill); 2184f1c454b4SSowmini Varadhan ill_dlpi_send_queued(ill); 21857c478bd9Sstevel@tonic-gate l_free(orig_filter); 21867c478bd9Sstevel@tonic-gate l_free(new_filter); 21877c478bd9Sstevel@tonic-gate 21887c478bd9Sstevel@tonic-gate return (err); 21897c478bd9Sstevel@tonic-gate } 21907c478bd9Sstevel@tonic-gate 21917c478bd9Sstevel@tonic-gate /* 21927c478bd9Sstevel@tonic-gate * Process the SIOC[GS]MSFILTER and SIOC[GS]IPMSFILTER ioctls. 21937c478bd9Sstevel@tonic-gate */ 21947c478bd9Sstevel@tonic-gate /* ARGSUSED */ 21957c478bd9Sstevel@tonic-gate int 21967c478bd9Sstevel@tonic-gate ip_sioctl_msfilter(ipif_t *ipif, sin_t *dummy_sin, queue_t *q, mblk_t *mp, 21977c478bd9Sstevel@tonic-gate ip_ioctl_cmd_t *ipip, void *ifreq) 21987c478bd9Sstevel@tonic-gate { 21997c478bd9Sstevel@tonic-gate struct iocblk *iocp = (struct iocblk *)mp->b_rptr; 22007c478bd9Sstevel@tonic-gate /* existence verified in ip_wput_nondata() */ 22017c478bd9Sstevel@tonic-gate mblk_t *data_mp = mp->b_cont->b_cont; 22027c478bd9Sstevel@tonic-gate int datalen, err, cmd, minsize; 2203fda3a3a5Sblu uint_t expsize = 0; 22047c478bd9Sstevel@tonic-gate conn_t *connp; 22057c478bd9Sstevel@tonic-gate boolean_t isv6, is_v4only_api, getcmd; 22067c478bd9Sstevel@tonic-gate struct sockaddr_in *gsin; 22077c478bd9Sstevel@tonic-gate struct sockaddr_in6 *gsin6; 2208bd670b35SErik Nordmark ipaddr_t v4group; 2209bd670b35SErik Nordmark in6_addr_t v6group; 22107c478bd9Sstevel@tonic-gate struct group_filter *gf = NULL; 22117c478bd9Sstevel@tonic-gate struct ip_msfilter *imsf = NULL; 22127c478bd9Sstevel@tonic-gate mblk_t *ndp; 2213bd670b35SErik Nordmark ill_t *ill; 2214bd670b35SErik Nordmark 2215bd670b35SErik Nordmark connp = Q_TO_CONN(q); 2216bd670b35SErik Nordmark err = ip_msfilter_ill(connp, mp, ipip, &ill); 2217bd670b35SErik Nordmark if (err != 0) 2218bd670b35SErik Nordmark return (err); 22197c478bd9Sstevel@tonic-gate 22207c478bd9Sstevel@tonic-gate if (data_mp->b_cont != NULL) { 22217c478bd9Sstevel@tonic-gate if ((ndp = msgpullup(data_mp, -1)) == NULL) 22227c478bd9Sstevel@tonic-gate return (ENOMEM); 22237c478bd9Sstevel@tonic-gate freemsg(data_mp); 22247c478bd9Sstevel@tonic-gate data_mp = ndp; 22257c478bd9Sstevel@tonic-gate mp->b_cont->b_cont = data_mp; 22267c478bd9Sstevel@tonic-gate } 22277c478bd9Sstevel@tonic-gate 22287c478bd9Sstevel@tonic-gate cmd = iocp->ioc_cmd; 22297c478bd9Sstevel@tonic-gate getcmd = (cmd == SIOCGIPMSFILTER || cmd == SIOCGMSFILTER); 22307c478bd9Sstevel@tonic-gate is_v4only_api = (cmd == SIOCGIPMSFILTER || cmd == SIOCSIPMSFILTER); 22317c478bd9Sstevel@tonic-gate minsize = (is_v4only_api) ? IP_MSFILTER_SIZE(0) : GROUP_FILTER_SIZE(0); 22327c478bd9Sstevel@tonic-gate datalen = MBLKL(data_mp); 22337c478bd9Sstevel@tonic-gate 22347c478bd9Sstevel@tonic-gate if (datalen < minsize) 22357c478bd9Sstevel@tonic-gate return (EINVAL); 22367c478bd9Sstevel@tonic-gate 22377c478bd9Sstevel@tonic-gate /* 22387c478bd9Sstevel@tonic-gate * now we know we have at least have the initial structure, 22397c478bd9Sstevel@tonic-gate * but need to check for the source list array. 22407c478bd9Sstevel@tonic-gate */ 22417c478bd9Sstevel@tonic-gate if (is_v4only_api) { 22427c478bd9Sstevel@tonic-gate imsf = (struct ip_msfilter *)data_mp->b_rptr; 22437c478bd9Sstevel@tonic-gate isv6 = B_FALSE; 22447c478bd9Sstevel@tonic-gate expsize = IP_MSFILTER_SIZE(imsf->imsf_numsrc); 22457c478bd9Sstevel@tonic-gate } else { 22467c478bd9Sstevel@tonic-gate gf = (struct group_filter *)data_mp->b_rptr; 22477c478bd9Sstevel@tonic-gate if (gf->gf_group.ss_family == AF_INET6) { 22487c478bd9Sstevel@tonic-gate gsin6 = (struct sockaddr_in6 *)&gf->gf_group; 22497c478bd9Sstevel@tonic-gate isv6 = !(IN6_IS_ADDR_V4MAPPED(&gsin6->sin6_addr)); 22507c478bd9Sstevel@tonic-gate } else { 22517c478bd9Sstevel@tonic-gate isv6 = B_FALSE; 22527c478bd9Sstevel@tonic-gate } 22537c478bd9Sstevel@tonic-gate expsize = GROUP_FILTER_SIZE(gf->gf_numsrc); 22547c478bd9Sstevel@tonic-gate } 22557c478bd9Sstevel@tonic-gate if (datalen < expsize) 22567c478bd9Sstevel@tonic-gate return (EINVAL); 22577c478bd9Sstevel@tonic-gate 22587c478bd9Sstevel@tonic-gate if (isv6) { 22597c478bd9Sstevel@tonic-gate gsin6 = (struct sockaddr_in6 *)&gf->gf_group; 2260bd670b35SErik Nordmark v6group = gsin6->sin6_addr; 2261bd670b35SErik Nordmark if (getcmd) { 2262bd670b35SErik Nordmark err = ip_get_srcfilter(connp, gf, NULL, &v6group, 2263bd670b35SErik Nordmark B_TRUE); 22647c478bd9Sstevel@tonic-gate } else { 2265bd670b35SErik Nordmark err = ip_set_srcfilter(connp, gf, NULL, &v6group, ill, 2266bd670b35SErik Nordmark B_TRUE); 2267bd670b35SErik Nordmark } 2268bd670b35SErik Nordmark } else { 2269bd670b35SErik Nordmark boolean_t issin6 = B_FALSE; 22707c478bd9Sstevel@tonic-gate if (is_v4only_api) { 2271bd670b35SErik Nordmark v4group = (ipaddr_t)imsf->imsf_multiaddr.s_addr; 2272bd670b35SErik Nordmark IN6_IPADDR_TO_V4MAPPED(v4group, &v6group); 22737c478bd9Sstevel@tonic-gate } else { 22747c478bd9Sstevel@tonic-gate if (gf->gf_group.ss_family == AF_INET) { 22757c478bd9Sstevel@tonic-gate gsin = (struct sockaddr_in *)&gf->gf_group; 2276bd670b35SErik Nordmark v4group = (ipaddr_t)gsin->sin_addr.s_addr; 2277bd670b35SErik Nordmark IN6_IPADDR_TO_V4MAPPED(v4group, &v6group); 22787c478bd9Sstevel@tonic-gate } else { 22797c478bd9Sstevel@tonic-gate gsin6 = (struct sockaddr_in6 *)&gf->gf_group; 22807c478bd9Sstevel@tonic-gate IN6_V4MAPPED_TO_IPADDR(&gsin6->sin6_addr, 2281bd670b35SErik Nordmark v4group); 2282bd670b35SErik Nordmark issin6 = B_TRUE; 22837c478bd9Sstevel@tonic-gate } 22847c478bd9Sstevel@tonic-gate } 2285bd670b35SErik Nordmark /* 2286bd670b35SErik Nordmark * INADDR_ANY is represented as the IPv6 unspecifed addr. 2287bd670b35SErik Nordmark */ 2288bd670b35SErik Nordmark if (v4group == INADDR_ANY) 2289bd670b35SErik Nordmark v6group = ipv6_all_zeros; 22907c478bd9Sstevel@tonic-gate else 2291bd670b35SErik Nordmark IN6_IPADDR_TO_V4MAPPED(v4group, &v6group); 2292bd670b35SErik Nordmark 2293bd670b35SErik Nordmark if (getcmd) { 2294bd670b35SErik Nordmark err = ip_get_srcfilter(connp, gf, imsf, &v6group, 2295bd670b35SErik Nordmark issin6); 2296bd670b35SErik Nordmark } else { 2297bd670b35SErik Nordmark err = ip_set_srcfilter(connp, gf, imsf, &v6group, ill, 2298bd670b35SErik Nordmark issin6); 22997c478bd9Sstevel@tonic-gate } 2300bd670b35SErik Nordmark } 2301bd670b35SErik Nordmark ill_refrele(ill); 23027c478bd9Sstevel@tonic-gate 23037c478bd9Sstevel@tonic-gate return (err); 23047c478bd9Sstevel@tonic-gate } 23057c478bd9Sstevel@tonic-gate 23067c478bd9Sstevel@tonic-gate /* 2307bd670b35SErik Nordmark * Determine the ill for the SIOC*MSFILTER ioctls 2308bd670b35SErik Nordmark * 2309bd670b35SErik Nordmark * Returns an error for IS_UNDER_IPMP interfaces. 2310bd670b35SErik Nordmark * 2311bd670b35SErik Nordmark * Finds the ill based on information in the ioctl headers. 23127c478bd9Sstevel@tonic-gate */ 2313bd670b35SErik Nordmark static int 2314bd670b35SErik Nordmark ip_msfilter_ill(conn_t *connp, mblk_t *mp, const ip_ioctl_cmd_t *ipip, 2315bd670b35SErik Nordmark ill_t **illp) 23167c478bd9Sstevel@tonic-gate { 231798e93c29Smeem int cmd = ipip->ipi_cmd; 231898e93c29Smeem int err = 0; 2319bd670b35SErik Nordmark ill_t *ill; 23207c478bd9Sstevel@tonic-gate /* caller has verified this mblk exists */ 23217c478bd9Sstevel@tonic-gate char *dbuf = (char *)mp->b_cont->b_cont->b_rptr; 23227c478bd9Sstevel@tonic-gate struct ip_msfilter *imsf; 23237c478bd9Sstevel@tonic-gate struct group_filter *gf; 2324bd670b35SErik Nordmark ipaddr_t v4addr, v4group; 2325bd670b35SErik Nordmark in6_addr_t v6group; 23267c478bd9Sstevel@tonic-gate uint32_t index; 2327f4b3ec61Sdh155122 ip_stack_t *ipst; 23287c478bd9Sstevel@tonic-gate 2329f4b3ec61Sdh155122 ipst = connp->conn_netstack->netstack_ip; 23307c478bd9Sstevel@tonic-gate 2331bd670b35SErik Nordmark *illp = NULL; 2332bd670b35SErik Nordmark 23337c478bd9Sstevel@tonic-gate /* don't allow multicast operations on a tcp conn */ 2334ff550d0eSmasputra if (IPCL_IS_TCP(connp)) 23357c478bd9Sstevel@tonic-gate return (ENOPROTOOPT); 23367c478bd9Sstevel@tonic-gate 23377c478bd9Sstevel@tonic-gate if (cmd == SIOCSIPMSFILTER || cmd == SIOCGIPMSFILTER) { 23387c478bd9Sstevel@tonic-gate /* don't allow v4-specific ioctls on v6 socket */ 2339bd670b35SErik Nordmark if (connp->conn_family == AF_INET6) 23407c478bd9Sstevel@tonic-gate return (EAFNOSUPPORT); 23417c478bd9Sstevel@tonic-gate 23427c478bd9Sstevel@tonic-gate imsf = (struct ip_msfilter *)dbuf; 23437c478bd9Sstevel@tonic-gate v4addr = imsf->imsf_interface.s_addr; 2344bd670b35SErik Nordmark v4group = imsf->imsf_multiaddr.s_addr; 2345bd670b35SErik Nordmark IN6_IPADDR_TO_V4MAPPED(v4group, &v6group); 2346bd670b35SErik Nordmark ill = ill_mcast_lookup(&v6group, v4addr, 0, IPCL_ZONEID(connp), 2347bd670b35SErik Nordmark ipst, &err); 2348bd670b35SErik Nordmark if (ill == NULL && v4addr != INADDR_ANY) 2349bd670b35SErik Nordmark err = ENXIO; 23507c478bd9Sstevel@tonic-gate } else { 23517c478bd9Sstevel@tonic-gate gf = (struct group_filter *)dbuf; 23527c478bd9Sstevel@tonic-gate index = gf->gf_interface; 23537c478bd9Sstevel@tonic-gate if (gf->gf_group.ss_family == AF_INET6) { 23547c478bd9Sstevel@tonic-gate struct sockaddr_in6 *sin6; 2355bd670b35SErik Nordmark 23567c478bd9Sstevel@tonic-gate sin6 = (struct sockaddr_in6 *)&gf->gf_group; 2357bd670b35SErik Nordmark v6group = sin6->sin6_addr; 23587c478bd9Sstevel@tonic-gate } else if (gf->gf_group.ss_family == AF_INET) { 23597c478bd9Sstevel@tonic-gate struct sockaddr_in *sin; 2360bd670b35SErik Nordmark 23617c478bd9Sstevel@tonic-gate sin = (struct sockaddr_in *)&gf->gf_group; 2362bd670b35SErik Nordmark v4group = sin->sin_addr.s_addr; 2363bd670b35SErik Nordmark IN6_IPADDR_TO_V4MAPPED(v4group, &v6group); 23647c478bd9Sstevel@tonic-gate } else { 23657c478bd9Sstevel@tonic-gate return (EAFNOSUPPORT); 23667c478bd9Sstevel@tonic-gate } 2367bd670b35SErik Nordmark ill = ill_mcast_lookup(&v6group, INADDR_ANY, index, 2368bd670b35SErik Nordmark IPCL_ZONEID(connp), ipst, &err); 2369f4b3ec61Sdh155122 } 2370bd670b35SErik Nordmark *illp = ill; 23717c478bd9Sstevel@tonic-gate return (err); 23727c478bd9Sstevel@tonic-gate } 23737c478bd9Sstevel@tonic-gate 23747c478bd9Sstevel@tonic-gate /* 23757c478bd9Sstevel@tonic-gate * The structures used for the SIOC*MSFILTER ioctls usually must be copied 23767c478bd9Sstevel@tonic-gate * in in two stages, as the first copyin tells us the size of the attached 23777c478bd9Sstevel@tonic-gate * source buffer. This function is called by ip_wput_nondata() after the 23787c478bd9Sstevel@tonic-gate * first copyin has completed; it figures out how big the second stage 23797c478bd9Sstevel@tonic-gate * needs to be, and kicks it off. 23807c478bd9Sstevel@tonic-gate * 23817c478bd9Sstevel@tonic-gate * In some cases (numsrc < 2), the second copyin is not needed as the 23827c478bd9Sstevel@tonic-gate * first one gets a complete structure containing 1 source addr. 23837c478bd9Sstevel@tonic-gate * 23847c478bd9Sstevel@tonic-gate * The function returns 0 if a second copyin has been started (i.e. there's 23857c478bd9Sstevel@tonic-gate * no more work to be done right now), or 1 if the second copyin is not 23867c478bd9Sstevel@tonic-gate * needed and ip_wput_nondata() can continue its processing. 23877c478bd9Sstevel@tonic-gate */ 23887c478bd9Sstevel@tonic-gate int 23897c478bd9Sstevel@tonic-gate ip_copyin_msfilter(queue_t *q, mblk_t *mp) 23907c478bd9Sstevel@tonic-gate { 23917c478bd9Sstevel@tonic-gate struct iocblk *iocp = (struct iocblk *)mp->b_rptr; 23927c478bd9Sstevel@tonic-gate int cmd = iocp->ioc_cmd; 23937c478bd9Sstevel@tonic-gate /* validity of this checked in ip_wput_nondata() */ 23947c478bd9Sstevel@tonic-gate mblk_t *mp1 = mp->b_cont->b_cont; 23957c478bd9Sstevel@tonic-gate int copysize = 0; 23967c478bd9Sstevel@tonic-gate int offset; 23977c478bd9Sstevel@tonic-gate 23987c478bd9Sstevel@tonic-gate if (cmd == SIOCSMSFILTER || cmd == SIOCGMSFILTER) { 23997c478bd9Sstevel@tonic-gate struct group_filter *gf = (struct group_filter *)mp1->b_rptr; 24007c478bd9Sstevel@tonic-gate if (gf->gf_numsrc >= 2) { 24017c478bd9Sstevel@tonic-gate offset = sizeof (struct group_filter); 24027c478bd9Sstevel@tonic-gate copysize = GROUP_FILTER_SIZE(gf->gf_numsrc) - offset; 24037c478bd9Sstevel@tonic-gate } 24047c478bd9Sstevel@tonic-gate } else { 24057c478bd9Sstevel@tonic-gate struct ip_msfilter *imsf = (struct ip_msfilter *)mp1->b_rptr; 24067c478bd9Sstevel@tonic-gate if (imsf->imsf_numsrc >= 2) { 24077c478bd9Sstevel@tonic-gate offset = sizeof (struct ip_msfilter); 24087c478bd9Sstevel@tonic-gate copysize = IP_MSFILTER_SIZE(imsf->imsf_numsrc) - offset; 24097c478bd9Sstevel@tonic-gate } 24107c478bd9Sstevel@tonic-gate } 24117c478bd9Sstevel@tonic-gate if (copysize > 0) { 24127c478bd9Sstevel@tonic-gate mi_copyin_n(q, mp, offset, copysize); 24137c478bd9Sstevel@tonic-gate return (0); 24147c478bd9Sstevel@tonic-gate } 24157c478bd9Sstevel@tonic-gate return (1); 24167c478bd9Sstevel@tonic-gate } 24177c478bd9Sstevel@tonic-gate 24187c478bd9Sstevel@tonic-gate /* 24197c478bd9Sstevel@tonic-gate * Handle the following optmgmt: 24207c478bd9Sstevel@tonic-gate * IP_ADD_MEMBERSHIP must not have joined already 2421bd670b35SErik Nordmark * IPV6_JOIN_GROUP must not have joined already 24227c478bd9Sstevel@tonic-gate * MCAST_JOIN_GROUP must not have joined already 24237c478bd9Sstevel@tonic-gate * IP_BLOCK_SOURCE must have joined already 24247c478bd9Sstevel@tonic-gate * MCAST_BLOCK_SOURCE must have joined already 24257c478bd9Sstevel@tonic-gate * IP_JOIN_SOURCE_GROUP may have joined already 24267c478bd9Sstevel@tonic-gate * MCAST_JOIN_SOURCE_GROUP may have joined already 24277c478bd9Sstevel@tonic-gate * 24287c478bd9Sstevel@tonic-gate * fmode and src parameters may be used to determine which option is 24297c478bd9Sstevel@tonic-gate * being set, as follows (IPV6_JOIN_GROUP and MCAST_JOIN_GROUP options 24307c478bd9Sstevel@tonic-gate * are functionally equivalent): 24317c478bd9Sstevel@tonic-gate * opt fmode v6src 2432bd670b35SErik Nordmark * IP_ADD_MEMBERSHIP MODE_IS_EXCLUDE unspecified 24337c478bd9Sstevel@tonic-gate * IPV6_JOIN_GROUP MODE_IS_EXCLUDE unspecified 24347c478bd9Sstevel@tonic-gate * MCAST_JOIN_GROUP MODE_IS_EXCLUDE unspecified 2435bd670b35SErik Nordmark * IP_BLOCK_SOURCE MODE_IS_EXCLUDE IPv4-mapped addr 24367c478bd9Sstevel@tonic-gate * MCAST_BLOCK_SOURCE MODE_IS_EXCLUDE v6 addr 2437bd670b35SErik Nordmark * IP_JOIN_SOURCE_GROUP MODE_IS_INCLUDE IPv4-mapped addr 24387c478bd9Sstevel@tonic-gate * MCAST_JOIN_SOURCE_GROUP MODE_IS_INCLUDE v6 addr 24397c478bd9Sstevel@tonic-gate * 24407c478bd9Sstevel@tonic-gate * Changing the filter mode is not allowed; if a matching ilg already 24417c478bd9Sstevel@tonic-gate * exists and fmode != ilg->ilg_fmode, EINVAL is returned. 24427c478bd9Sstevel@tonic-gate * 24437c478bd9Sstevel@tonic-gate * Verifies that there is a source address of appropriate scope for 24447c478bd9Sstevel@tonic-gate * the group; if not, EADDRNOTAVAIL is returned. 24457c478bd9Sstevel@tonic-gate * 2446bd670b35SErik Nordmark * The interface to be used may be identified by an IPv4 address or by an 2447bd670b35SErik Nordmark * interface index. 2448bd670b35SErik Nordmark * 24497c478bd9Sstevel@tonic-gate * Handles IPv4-mapped IPv6 multicast addresses by associating them 2450bd670b35SErik Nordmark * with the IPv4 address. Assumes that if v6group is v4-mapped, 24517c478bd9Sstevel@tonic-gate * v6src is also v4-mapped. 24527c478bd9Sstevel@tonic-gate */ 24537c478bd9Sstevel@tonic-gate int 2454bd670b35SErik Nordmark ip_opt_add_group(conn_t *connp, boolean_t checkonly, 2455bd670b35SErik Nordmark const in6_addr_t *v6group, ipaddr_t ifaddr, uint_t ifindex, 2456bd670b35SErik Nordmark mcast_record_t fmode, const in6_addr_t *v6src) 24577c478bd9Sstevel@tonic-gate { 24587c478bd9Sstevel@tonic-gate ill_t *ill; 24597c478bd9Sstevel@tonic-gate char buf[INET6_ADDRSTRLEN]; 24607c478bd9Sstevel@tonic-gate int err; 24617c478bd9Sstevel@tonic-gate 2462bd670b35SErik Nordmark err = ip_opt_check(connp, v6group, v6src, ifaddr, ifindex, &ill); 24637c478bd9Sstevel@tonic-gate if (err != 0) { 2464bd670b35SErik Nordmark ip1dbg(("ip_opt_add_group: no ill for group %s/" 24657c478bd9Sstevel@tonic-gate "index %d\n", inet_ntop(AF_INET6, v6group, buf, 24667c478bd9Sstevel@tonic-gate sizeof (buf)), ifindex)); 24677c478bd9Sstevel@tonic-gate return (err); 24687c478bd9Sstevel@tonic-gate } 24697c478bd9Sstevel@tonic-gate 24707c478bd9Sstevel@tonic-gate if (checkonly) { 24717c478bd9Sstevel@tonic-gate /* 24727c478bd9Sstevel@tonic-gate * do not do operation, just pretend to - new T_CHECK 24737c478bd9Sstevel@tonic-gate * semantics. The error return case above if encountered 24747c478bd9Sstevel@tonic-gate * considered a good enough "check" here. 24757c478bd9Sstevel@tonic-gate */ 24767c478bd9Sstevel@tonic-gate ill_refrele(ill); 24777c478bd9Sstevel@tonic-gate return (0); 24787c478bd9Sstevel@tonic-gate } 2479bd670b35SErik Nordmark mutex_enter(&ill->ill_mcast_serializer); 2480f1c454b4SSowmini Varadhan /* 2481f1c454b4SSowmini Varadhan * Multicast groups may not be joined on interfaces that are either 2482f1c454b4SSowmini Varadhan * already underlying interfaces in an IPMP group, or in the process 2483f1c454b4SSowmini Varadhan * of joining the IPMP group. The latter condition is enforced by 2484f1c454b4SSowmini Varadhan * checking the value of ill->ill_grp_pending under the 2485f1c454b4SSowmini Varadhan * ill_mcast_serializer lock. We cannot serialize the 2486f1c454b4SSowmini Varadhan * ill_grp_pending check on the ill_g_lock across ilg_add() because 2487f1c454b4SSowmini Varadhan * ill_mcast_send_queued -> ip_output_simple -> ill_lookup_on_ifindex 2488f1c454b4SSowmini Varadhan * will take the ill_g_lock itself. Instead, we hold the 2489f1c454b4SSowmini Varadhan * ill_mcast_serializer. 2490f1c454b4SSowmini Varadhan */ 2491f1c454b4SSowmini Varadhan if (ill->ill_grp_pending || IS_UNDER_IPMP(ill)) { 2492f1c454b4SSowmini Varadhan DTRACE_PROBE2(group__add__on__under, ill_t *, ill, 2493f1c454b4SSowmini Varadhan in6_addr_t *, v6group); 2494f1c454b4SSowmini Varadhan mutex_exit(&ill->ill_mcast_serializer); 2495f1c454b4SSowmini Varadhan ill_refrele(ill); 2496f1c454b4SSowmini Varadhan return (EADDRNOTAVAIL); 2497f1c454b4SSowmini Varadhan } 2498bd670b35SErik Nordmark err = ilg_add(connp, v6group, ifaddr, ifindex, ill, fmode, v6src); 2499bd670b35SErik Nordmark mutex_exit(&ill->ill_mcast_serializer); 2500f1c454b4SSowmini Varadhan /* 2501f1c454b4SSowmini Varadhan * We have done an addmulti_impl and/or delmulti_impl. 2502f1c454b4SSowmini Varadhan * All locks have been dropped, we can send any 2503f1c454b4SSowmini Varadhan * deferred/queued DLPI or IP packets 2504f1c454b4SSowmini Varadhan */ 2505f1c454b4SSowmini Varadhan ill_mcast_send_queued(ill); 2506f1c454b4SSowmini Varadhan ill_dlpi_send_queued(ill); 25077c478bd9Sstevel@tonic-gate ill_refrele(ill); 25087c478bd9Sstevel@tonic-gate return (err); 25097c478bd9Sstevel@tonic-gate } 25107c478bd9Sstevel@tonic-gate 2511bd670b35SErik Nordmark /* 2512bd670b35SErik Nordmark * Common for IPv6 and IPv4. 2513bd670b35SErik Nordmark * Here we handle ilgs that are still attached to their original ill 2514bd670b35SErik Nordmark * (the one ifaddr/ifindex points at), as well as detached ones. 2515bd670b35SErik Nordmark * The detached ones might have been attached to some other ill. 2516bd670b35SErik Nordmark */ 25177c478bd9Sstevel@tonic-gate static int 2518bd670b35SErik Nordmark ip_opt_delete_group_excl(conn_t *connp, const in6_addr_t *v6group, 2519bd670b35SErik Nordmark ipaddr_t ifaddr, uint_t ifindex, mcast_record_t fmode, 2520bd670b35SErik Nordmark const in6_addr_t *v6src) 25217c478bd9Sstevel@tonic-gate { 25227c478bd9Sstevel@tonic-gate ilg_t *ilg; 2523bd670b35SErik Nordmark boolean_t leaving; 2524bd670b35SErik Nordmark ilm_t *ilm; 2525bd670b35SErik Nordmark ill_t *ill; 25267c478bd9Sstevel@tonic-gate int err = 0; 2527bd670b35SErik Nordmark 2528bd670b35SErik Nordmark retry: 2529bd670b35SErik Nordmark rw_enter(&connp->conn_ilg_lock, RW_WRITER); 2530bd670b35SErik Nordmark ilg = ilg_lookup(connp, v6group, ifaddr, ifindex); 2531bd670b35SErik Nordmark if (ilg == NULL) { 2532bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 2533bd670b35SErik Nordmark /* 2534bd670b35SErik Nordmark * Since we didn't have any ilg we now do the error checks 2535bd670b35SErik Nordmark * to determine the best errno. 2536bd670b35SErik Nordmark */ 2537bd670b35SErik Nordmark err = ip_opt_check(connp, v6group, v6src, ifaddr, ifindex, 2538bd670b35SErik Nordmark &ill); 2539bd670b35SErik Nordmark if (ill != NULL) { 2540bd670b35SErik Nordmark /* The only error was a missing ilg for the group */ 2541bd670b35SErik Nordmark ill_refrele(ill); 25427c478bd9Sstevel@tonic-gate err = EADDRNOTAVAIL; 2543bd670b35SErik Nordmark } 25447c478bd9Sstevel@tonic-gate return (err); 25457c478bd9Sstevel@tonic-gate } 2546bd670b35SErik Nordmark 2547bd670b35SErik Nordmark /* If the ilg is attached then we serialize using that ill */ 2548bd670b35SErik Nordmark ill = ilg->ilg_ill; 2549bd670b35SErik Nordmark if (ill != NULL) { 2550bd670b35SErik Nordmark /* Prevent the ill and ilg from being freed */ 2551bd670b35SErik Nordmark ill_refhold(ill); 2552bd670b35SErik Nordmark ilg_refhold(ilg); 2553bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 2554bd670b35SErik Nordmark mutex_enter(&ill->ill_mcast_serializer); 2555bd670b35SErik Nordmark rw_enter(&connp->conn_ilg_lock, RW_WRITER); 2556bd670b35SErik Nordmark if (ilg->ilg_condemned) { 2557bd670b35SErik Nordmark /* Disappeared */ 2558bd670b35SErik Nordmark ilg_refrele(ilg); 2559bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 2560bd670b35SErik Nordmark mutex_exit(&ill->ill_mcast_serializer); 2561bd670b35SErik Nordmark ill_refrele(ill); 2562bd670b35SErik Nordmark goto retry; 25637c478bd9Sstevel@tonic-gate } 25647c478bd9Sstevel@tonic-gate } 25657c478bd9Sstevel@tonic-gate 25667c478bd9Sstevel@tonic-gate /* 25677c478bd9Sstevel@tonic-gate * Decide if we're actually deleting the ilg or just removing a 25687c478bd9Sstevel@tonic-gate * source filter address; if just removing an addr, make sure we 25697c478bd9Sstevel@tonic-gate * aren't trying to change the filter mode, and that the addr is 25707c478bd9Sstevel@tonic-gate * actually in our filter list already. If we're removing the 25717c478bd9Sstevel@tonic-gate * last src in an include list, just delete the ilg. 25727c478bd9Sstevel@tonic-gate */ 2573bd670b35SErik Nordmark if (IN6_IS_ADDR_UNSPECIFIED(v6src)) { 2574bd670b35SErik Nordmark leaving = B_TRUE; 2575bd670b35SErik Nordmark } else { 25767c478bd9Sstevel@tonic-gate if (fmode != ilg->ilg_fmode) 25777c478bd9Sstevel@tonic-gate err = EINVAL; 25787c478bd9Sstevel@tonic-gate else if (ilg->ilg_filter == NULL || 25797c478bd9Sstevel@tonic-gate !list_has_addr(ilg->ilg_filter, v6src)) 25807c478bd9Sstevel@tonic-gate err = EADDRNOTAVAIL; 25817c478bd9Sstevel@tonic-gate if (err != 0) { 2582bd670b35SErik Nordmark if (ill != NULL) 2583bd670b35SErik Nordmark ilg_refrele(ilg); 2584bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 2585bd670b35SErik Nordmark goto done; 25867c478bd9Sstevel@tonic-gate } 25877c478bd9Sstevel@tonic-gate if (fmode == MODE_IS_INCLUDE && 2588bd670b35SErik Nordmark ilg->ilg_filter->sl_numsrc == 1) { 2589bd670b35SErik Nordmark leaving = B_TRUE; 25907c478bd9Sstevel@tonic-gate v6src = NULL; 2591bd670b35SErik Nordmark } else { 25927c478bd9Sstevel@tonic-gate leaving = B_FALSE; 25937c478bd9Sstevel@tonic-gate } 2594bd670b35SErik Nordmark } 2595bd670b35SErik Nordmark ilm = ilg->ilg_ilm; 2596bd670b35SErik Nordmark if (leaving) 2597bd670b35SErik Nordmark ilg->ilg_ilm = NULL; 25987c478bd9Sstevel@tonic-gate 25997c478bd9Sstevel@tonic-gate ilg_delete(connp, ilg, v6src); 2600bd670b35SErik Nordmark if (ill != NULL) 2601bd670b35SErik Nordmark ilg_refrele(ilg); 2602bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 26037c478bd9Sstevel@tonic-gate 2604bd670b35SErik Nordmark if (ilm != NULL) { 2605bd670b35SErik Nordmark ASSERT(ill != NULL); 2606bd670b35SErik Nordmark (void) ip_delmulti_serial(ilm, B_FALSE, leaving); 2607bd670b35SErik Nordmark } 2608bd670b35SErik Nordmark done: 2609bd670b35SErik Nordmark if (ill != NULL) { 2610bd670b35SErik Nordmark mutex_exit(&ill->ill_mcast_serializer); 2611f1c454b4SSowmini Varadhan /* 2612f1c454b4SSowmini Varadhan * Now that all locks have been dropped, we can 2613f1c454b4SSowmini Varadhan * send any deferred/queued DLPI or IP packets 2614f1c454b4SSowmini Varadhan */ 2615f1c454b4SSowmini Varadhan ill_mcast_send_queued(ill); 2616f1c454b4SSowmini Varadhan ill_dlpi_send_queued(ill); 2617bd670b35SErik Nordmark ill_refrele(ill); 2618bd670b35SErik Nordmark } 2619bd670b35SErik Nordmark return (err); 26207c478bd9Sstevel@tonic-gate } 26217c478bd9Sstevel@tonic-gate 26227c478bd9Sstevel@tonic-gate /* 26237c478bd9Sstevel@tonic-gate * Handle the following optmgmt: 26247c478bd9Sstevel@tonic-gate * IP_DROP_MEMBERSHIP will leave 2625bd670b35SErik Nordmark * IPV6_LEAVE_GROUP will leave 26267c478bd9Sstevel@tonic-gate * MCAST_LEAVE_GROUP will leave 26277c478bd9Sstevel@tonic-gate * IP_UNBLOCK_SOURCE will not leave 26287c478bd9Sstevel@tonic-gate * MCAST_UNBLOCK_SOURCE will not leave 26297c478bd9Sstevel@tonic-gate * IP_LEAVE_SOURCE_GROUP may leave (if leaving last source) 26307c478bd9Sstevel@tonic-gate * MCAST_LEAVE_SOURCE_GROUP may leave (if leaving last source) 26317c478bd9Sstevel@tonic-gate * 26327c478bd9Sstevel@tonic-gate * fmode and src parameters may be used to determine which option is 2633bd670b35SErik Nordmark * being set, as follows: 26347c478bd9Sstevel@tonic-gate * opt fmode v6src 2635bd670b35SErik Nordmark * IP_DROP_MEMBERSHIP MODE_IS_INCLUDE unspecified 26367c478bd9Sstevel@tonic-gate * IPV6_LEAVE_GROUP MODE_IS_INCLUDE unspecified 26377c478bd9Sstevel@tonic-gate * MCAST_LEAVE_GROUP MODE_IS_INCLUDE unspecified 2638bd670b35SErik Nordmark * IP_UNBLOCK_SOURCE MODE_IS_EXCLUDE IPv4-mapped addr 26397c478bd9Sstevel@tonic-gate * MCAST_UNBLOCK_SOURCE MODE_IS_EXCLUDE v6 addr 2640bd670b35SErik Nordmark * IP_LEAVE_SOURCE_GROUP MODE_IS_INCLUDE IPv4-mapped addr 26417c478bd9Sstevel@tonic-gate * MCAST_LEAVE_SOURCE_GROUP MODE_IS_INCLUDE v6 addr 26427c478bd9Sstevel@tonic-gate * 26437c478bd9Sstevel@tonic-gate * Changing the filter mode is not allowed; if a matching ilg already 26447c478bd9Sstevel@tonic-gate * exists and fmode != ilg->ilg_fmode, EINVAL is returned. 26457c478bd9Sstevel@tonic-gate * 2646bd670b35SErik Nordmark * The interface to be used may be identified by an IPv4 address or by an 2647bd670b35SErik Nordmark * interface index. 2648bd670b35SErik Nordmark * 26497c478bd9Sstevel@tonic-gate * Handles IPv4-mapped IPv6 multicast addresses by associating them 2650bd670b35SErik Nordmark * with the IPv4 address. Assumes that if v6group is v4-mapped, 26517c478bd9Sstevel@tonic-gate * v6src is also v4-mapped. 26527c478bd9Sstevel@tonic-gate */ 26537c478bd9Sstevel@tonic-gate int 2654bd670b35SErik Nordmark ip_opt_delete_group(conn_t *connp, boolean_t checkonly, 2655bd670b35SErik Nordmark const in6_addr_t *v6group, ipaddr_t ifaddr, uint_t ifindex, 2656bd670b35SErik Nordmark mcast_record_t fmode, const in6_addr_t *v6src) 26577c478bd9Sstevel@tonic-gate { 2658bd670b35SErik Nordmark 2659bd670b35SErik Nordmark /* 2660bd670b35SErik Nordmark * In the normal case below we don't check for the ill existing. 2661bd670b35SErik Nordmark * Instead we look for an existing ilg in _excl. 2662bd670b35SErik Nordmark * If checkonly we sanity check the arguments 2663bd670b35SErik Nordmark */ 2664bd670b35SErik Nordmark if (checkonly) { 26657c478bd9Sstevel@tonic-gate ill_t *ill; 26667c478bd9Sstevel@tonic-gate int err; 26677c478bd9Sstevel@tonic-gate 2668bd670b35SErik Nordmark err = ip_opt_check(connp, v6group, v6src, ifaddr, ifindex, 2669bd670b35SErik Nordmark &ill); 26707c478bd9Sstevel@tonic-gate /* 2671bd670b35SErik Nordmark * do not do operation, just pretend to - new T_CHECK semantics. 2672bd670b35SErik Nordmark * ip_opt_check is considered a good enough "check" here. 26737c478bd9Sstevel@tonic-gate */ 2674bd670b35SErik Nordmark if (ill != NULL) 26757c478bd9Sstevel@tonic-gate ill_refrele(ill); 26767c478bd9Sstevel@tonic-gate return (err); 26777c478bd9Sstevel@tonic-gate } 2678bd670b35SErik Nordmark return (ip_opt_delete_group_excl(connp, v6group, ifaddr, ifindex, 2679bd670b35SErik Nordmark fmode, v6src)); 2680bd670b35SErik Nordmark } 26817c478bd9Sstevel@tonic-gate 26827c478bd9Sstevel@tonic-gate /* 26837c478bd9Sstevel@tonic-gate * Group mgmt for upper conn that passes things down 26847c478bd9Sstevel@tonic-gate * to the interface multicast list (and DLPI) 26857c478bd9Sstevel@tonic-gate * These routines can handle new style options that specify an interface name 26867c478bd9Sstevel@tonic-gate * as opposed to an interface address (needed for general handling of 26877c478bd9Sstevel@tonic-gate * unnumbered interfaces.) 26887c478bd9Sstevel@tonic-gate */ 26897c478bd9Sstevel@tonic-gate 26907c478bd9Sstevel@tonic-gate /* 26917c478bd9Sstevel@tonic-gate * Add a group to an upper conn group data structure and pass things down 26927c478bd9Sstevel@tonic-gate * to the interface multicast list (and DLPI) 2693bd670b35SErik Nordmark * Common for IPv4 and IPv6; for IPv4 we can have an ifaddr. 26947c478bd9Sstevel@tonic-gate */ 26957c478bd9Sstevel@tonic-gate static int 2696bd670b35SErik Nordmark ilg_add(conn_t *connp, const in6_addr_t *v6group, ipaddr_t ifaddr, 2697bd670b35SErik Nordmark uint_t ifindex, ill_t *ill, mcast_record_t fmode, const in6_addr_t *v6src) 26987c478bd9Sstevel@tonic-gate { 26997c478bd9Sstevel@tonic-gate int error = 0; 27007c478bd9Sstevel@tonic-gate ilg_t *ilg; 27017c478bd9Sstevel@tonic-gate ilg_stat_t ilgstat; 27027c478bd9Sstevel@tonic-gate slist_t *new_filter = NULL; 27037c478bd9Sstevel@tonic-gate int new_fmode; 2704bd670b35SErik Nordmark ilm_t *ilm; 27057c478bd9Sstevel@tonic-gate 27067c478bd9Sstevel@tonic-gate if (!(ill->ill_flags & ILLF_MULTICAST)) 27077c478bd9Sstevel@tonic-gate return (EADDRNOTAVAIL); 27087c478bd9Sstevel@tonic-gate 2709bd670b35SErik Nordmark /* conn_ilg_lock protects the ilg list. */ 2710bd670b35SErik Nordmark ASSERT(MUTEX_HELD(&ill->ill_mcast_serializer)); 2711bd670b35SErik Nordmark rw_enter(&connp->conn_ilg_lock, RW_WRITER); 2712bd670b35SErik Nordmark ilg = ilg_lookup(connp, v6group, ifaddr, ifindex); 27137c478bd9Sstevel@tonic-gate 27147c478bd9Sstevel@tonic-gate /* 27157c478bd9Sstevel@tonic-gate * Depending on the option we're handling, may or may not be okay 27167c478bd9Sstevel@tonic-gate * if group has already been added. Figure out our rules based 27177c478bd9Sstevel@tonic-gate * on fmode and src params. Also make sure there's enough room 27187c478bd9Sstevel@tonic-gate * in the filter if we're adding a source to an existing filter. 27197c478bd9Sstevel@tonic-gate */ 27207c478bd9Sstevel@tonic-gate if (IN6_IS_ADDR_UNSPECIFIED(v6src)) { 27217c478bd9Sstevel@tonic-gate /* we're joining for all sources, must not have joined */ 27227c478bd9Sstevel@tonic-gate if (ilg != NULL) 27237c478bd9Sstevel@tonic-gate error = EADDRINUSE; 27247c478bd9Sstevel@tonic-gate } else { 27257c478bd9Sstevel@tonic-gate if (fmode == MODE_IS_EXCLUDE) { 27267c478bd9Sstevel@tonic-gate /* (excl {addr}) => block source, must have joined */ 27277c478bd9Sstevel@tonic-gate if (ilg == NULL) 27287c478bd9Sstevel@tonic-gate error = EADDRNOTAVAIL; 27297c478bd9Sstevel@tonic-gate } 27307c478bd9Sstevel@tonic-gate /* (incl {addr}) => join source, may have joined */ 27317c478bd9Sstevel@tonic-gate 27327c478bd9Sstevel@tonic-gate if (ilg != NULL && 27337c478bd9Sstevel@tonic-gate SLIST_CNT(ilg->ilg_filter) == MAX_FILTER_SIZE) 27347c478bd9Sstevel@tonic-gate error = ENOBUFS; 27357c478bd9Sstevel@tonic-gate } 27367c478bd9Sstevel@tonic-gate if (error != 0) { 2737bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 27387c478bd9Sstevel@tonic-gate return (error); 27397c478bd9Sstevel@tonic-gate } 27407c478bd9Sstevel@tonic-gate 27417c478bd9Sstevel@tonic-gate /* 27427c478bd9Sstevel@tonic-gate * Alloc buffer to copy new state into (see below) before 27437c478bd9Sstevel@tonic-gate * we make any changes, so we can bail if it fails. 27447c478bd9Sstevel@tonic-gate */ 27457c478bd9Sstevel@tonic-gate if ((new_filter = l_alloc()) == NULL) { 2746bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 27477c478bd9Sstevel@tonic-gate return (ENOMEM); 27487c478bd9Sstevel@tonic-gate } 27497c478bd9Sstevel@tonic-gate 27507c478bd9Sstevel@tonic-gate if (ilg == NULL) { 2751e11c3f44Smeem if ((ilg = conn_ilg_alloc(connp, &error)) == NULL) { 2752bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 27537c478bd9Sstevel@tonic-gate l_free(new_filter); 2754e11c3f44Smeem return (error); 27557c478bd9Sstevel@tonic-gate } 2756bd670b35SErik Nordmark ilg->ilg_ifindex = ifindex; 2757bd670b35SErik Nordmark ilg->ilg_ifaddr = ifaddr; 27587c478bd9Sstevel@tonic-gate if (!IN6_IS_ADDR_UNSPECIFIED(v6src)) { 27597c478bd9Sstevel@tonic-gate ilg->ilg_filter = l_alloc(); 27607c478bd9Sstevel@tonic-gate if (ilg->ilg_filter == NULL) { 27617c478bd9Sstevel@tonic-gate ilg_delete(connp, ilg, NULL); 2762bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 27637c478bd9Sstevel@tonic-gate l_free(new_filter); 27647c478bd9Sstevel@tonic-gate return (ENOMEM); 27657c478bd9Sstevel@tonic-gate } 27667c478bd9Sstevel@tonic-gate ilg->ilg_filter->sl_numsrc = 1; 27677c478bd9Sstevel@tonic-gate ilg->ilg_filter->sl_addr[0] = *v6src; 27687c478bd9Sstevel@tonic-gate } 27697c478bd9Sstevel@tonic-gate ilgstat = ILGSTAT_NEW; 27707c478bd9Sstevel@tonic-gate ilg->ilg_v6group = *v6group; 27717c478bd9Sstevel@tonic-gate ilg->ilg_fmode = fmode; 27727c478bd9Sstevel@tonic-gate ilg->ilg_ill = ill; 27737c478bd9Sstevel@tonic-gate } else { 27747c478bd9Sstevel@tonic-gate int index; 2775f1c454b4SSowmini Varadhan 27767c478bd9Sstevel@tonic-gate if (ilg->ilg_fmode != fmode || IN6_IS_ADDR_UNSPECIFIED(v6src)) { 2777bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 27787c478bd9Sstevel@tonic-gate l_free(new_filter); 27797c478bd9Sstevel@tonic-gate return (EINVAL); 27807c478bd9Sstevel@tonic-gate } 27817c478bd9Sstevel@tonic-gate if (ilg->ilg_filter == NULL) { 27827c478bd9Sstevel@tonic-gate ilg->ilg_filter = l_alloc(); 27837c478bd9Sstevel@tonic-gate if (ilg->ilg_filter == NULL) { 2784bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 27857c478bd9Sstevel@tonic-gate l_free(new_filter); 27867c478bd9Sstevel@tonic-gate return (ENOMEM); 27877c478bd9Sstevel@tonic-gate } 27887c478bd9Sstevel@tonic-gate } 27897c478bd9Sstevel@tonic-gate if (list_has_addr(ilg->ilg_filter, v6src)) { 2790bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 27917c478bd9Sstevel@tonic-gate l_free(new_filter); 27927c478bd9Sstevel@tonic-gate return (EADDRNOTAVAIL); 27937c478bd9Sstevel@tonic-gate } 27947c478bd9Sstevel@tonic-gate ilgstat = ILGSTAT_CHANGE; 27957c478bd9Sstevel@tonic-gate index = ilg->ilg_filter->sl_numsrc++; 27967c478bd9Sstevel@tonic-gate ilg->ilg_filter->sl_addr[index] = *v6src; 27977c478bd9Sstevel@tonic-gate } 27987c478bd9Sstevel@tonic-gate 27997c478bd9Sstevel@tonic-gate /* 28007c478bd9Sstevel@tonic-gate * Save copy of ilg's filter state to pass to other functions, 2801bd670b35SErik Nordmark * so we can release conn_ilg_lock now. 28027c478bd9Sstevel@tonic-gate */ 28037c478bd9Sstevel@tonic-gate new_fmode = ilg->ilg_fmode; 28047c478bd9Sstevel@tonic-gate l_copy(ilg->ilg_filter, new_filter); 28057c478bd9Sstevel@tonic-gate 2806bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 28077c478bd9Sstevel@tonic-gate 28087c478bd9Sstevel@tonic-gate /* 28097c478bd9Sstevel@tonic-gate * Now update the ill. We wait to do this until after the ilg 28107c478bd9Sstevel@tonic-gate * has been updated because we need to update the src filter 28117c478bd9Sstevel@tonic-gate * info for the ill, which involves looking at the status of 28127c478bd9Sstevel@tonic-gate * all the ilgs associated with this group/interface pair. 28137c478bd9Sstevel@tonic-gate */ 2814bd670b35SErik Nordmark ilm = ip_addmulti_serial(v6group, ill, connp->conn_zoneid, ilgstat, 2815bd670b35SErik Nordmark new_fmode, new_filter, &error); 2816bd670b35SErik Nordmark 2817bd670b35SErik Nordmark rw_enter(&connp->conn_ilg_lock, RW_WRITER); 28187c478bd9Sstevel@tonic-gate /* 2819bd670b35SErik Nordmark * Must look up the ilg again since we've not been holding 2820bd670b35SErik Nordmark * conn_ilg_lock. The ilg could have disappeared due to an unplumb 2821bd670b35SErik Nordmark * having called conn_update_ill, which can run once we dropped the 2822bd670b35SErik Nordmark * conn_ilg_lock above. 28237c478bd9Sstevel@tonic-gate */ 2824bd670b35SErik Nordmark ilg = ilg_lookup(connp, v6group, ifaddr, ifindex); 2825bd670b35SErik Nordmark if (ilg == NULL) { 2826bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 2827bd670b35SErik Nordmark if (ilm != NULL) { 2828bd670b35SErik Nordmark (void) ip_delmulti_serial(ilm, B_FALSE, 2829bd670b35SErik Nordmark (ilgstat == ILGSTAT_NEW)); 2830bd670b35SErik Nordmark } 2831bd670b35SErik Nordmark error = ENXIO; 2832bd670b35SErik Nordmark goto free_and_exit; 2833bd670b35SErik Nordmark } 2834bd670b35SErik Nordmark if (ilm != NULL) { 2835f1c454b4SSowmini Varadhan if (ilg->ilg_ill == NULL) { 2836f1c454b4SSowmini Varadhan /* some other thread is re-attaching this. */ 2837f1c454b4SSowmini Varadhan rw_exit(&connp->conn_ilg_lock); 2838f1c454b4SSowmini Varadhan (void) ip_delmulti_serial(ilm, B_FALSE, 2839f1c454b4SSowmini Varadhan (ilgstat == ILGSTAT_NEW)); 2840f1c454b4SSowmini Varadhan error = 0; 2841f1c454b4SSowmini Varadhan goto free_and_exit; 2842f1c454b4SSowmini Varadhan } 2843bd670b35SErik Nordmark /* Succeeded. Update the ilg to point at the ilm */ 2844bd670b35SErik Nordmark if (ilgstat == ILGSTAT_NEW) { 2845f1c454b4SSowmini Varadhan if (ilg->ilg_ilm == NULL) { 2846bd670b35SErik Nordmark ilg->ilg_ilm = ilm; 2847bd670b35SErik Nordmark ilm->ilm_ifaddr = ifaddr; /* For netstat */ 2848bd670b35SErik Nordmark } else { 2849f1c454b4SSowmini Varadhan /* some other thread is re-attaching this. */ 2850f1c454b4SSowmini Varadhan rw_exit(&connp->conn_ilg_lock); 2851f1c454b4SSowmini Varadhan (void) ip_delmulti_serial(ilm, B_FALSE, B_TRUE); 2852f1c454b4SSowmini Varadhan error = 0; 2853f1c454b4SSowmini Varadhan goto free_and_exit; 2854f1c454b4SSowmini Varadhan } 2855f1c454b4SSowmini Varadhan } else { 2856bd670b35SErik Nordmark /* 2857bd670b35SErik Nordmark * ip_addmulti didn't get a held ilm for 2858bd670b35SErik Nordmark * ILGSTAT_CHANGE; ilm_refcnt was unchanged. 2859bd670b35SErik Nordmark */ 2860bd670b35SErik Nordmark ASSERT(ilg->ilg_ilm == ilm); 2861bd670b35SErik Nordmark } 2862bd670b35SErik Nordmark } else { 2863bd670b35SErik Nordmark ASSERT(error != 0); 2864bd670b35SErik Nordmark /* 2865bd670b35SErik Nordmark * Failed to allocate the ilm. 2866bd670b35SErik Nordmark * Need to undo what we did before calling ip_addmulti() 2867bd670b35SErik Nordmark * If ENETDOWN just clear ill_ilg since so that we 2868bd670b35SErik Nordmark * will rejoin when the ill comes back; don't report ENETDOWN 2869bd670b35SErik Nordmark * to application. 2870bd670b35SErik Nordmark */ 2871bd670b35SErik Nordmark if (ilgstat == ILGSTAT_NEW && error == ENETDOWN) { 2872bd670b35SErik Nordmark ilg->ilg_ill = NULL; 2873bd670b35SErik Nordmark error = 0; 2874bd670b35SErik Nordmark } else { 28757c478bd9Sstevel@tonic-gate in6_addr_t delsrc = 28767c478bd9Sstevel@tonic-gate (ilgstat == ILGSTAT_NEW) ? ipv6_all_zeros : *v6src; 2877bd670b35SErik Nordmark 28787c478bd9Sstevel@tonic-gate ilg_delete(connp, ilg, &delsrc); 2879bd670b35SErik Nordmark } 2880bd670b35SErik Nordmark } 2881bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 2882bd670b35SErik Nordmark 2883bd670b35SErik Nordmark free_and_exit: 28847c478bd9Sstevel@tonic-gate l_free(new_filter); 28857c478bd9Sstevel@tonic-gate return (error); 28867c478bd9Sstevel@tonic-gate } 28877c478bd9Sstevel@tonic-gate 28887c478bd9Sstevel@tonic-gate /* 2889bd670b35SErik Nordmark * Find an IPv4 ilg matching group, ill and source. 2890bd670b35SErik Nordmark * The group and source can't be INADDR_ANY here so no need to translate to 2891bd670b35SErik Nordmark * the unspecified IPv6 address. 28927c478bd9Sstevel@tonic-gate */ 2893bd670b35SErik Nordmark boolean_t 2894bd670b35SErik Nordmark conn_hasmembers_ill_withsrc_v4(conn_t *connp, ipaddr_t group, ipaddr_t src, 2895bd670b35SErik Nordmark ill_t *ill) 28967c478bd9Sstevel@tonic-gate { 28977c478bd9Sstevel@tonic-gate in6_addr_t v6group, v6src; 28987c478bd9Sstevel@tonic-gate int i; 28997c478bd9Sstevel@tonic-gate boolean_t isinlist; 29007c478bd9Sstevel@tonic-gate ilg_t *ilg; 29017c478bd9Sstevel@tonic-gate 2902bd670b35SErik Nordmark rw_enter(&connp->conn_ilg_lock, RW_READER); 29037c478bd9Sstevel@tonic-gate IN6_IPADDR_TO_V4MAPPED(group, &v6group); 2904bd670b35SErik Nordmark for (ilg = connp->conn_ilg; ilg != NULL; ilg = ilg->ilg_next) { 2905bd670b35SErik Nordmark if (ilg->ilg_condemned) 29067c478bd9Sstevel@tonic-gate continue; 2907bd670b35SErik Nordmark 2908bd670b35SErik Nordmark /* ilg_ill could be NULL if an add is in progress */ 2909bd670b35SErik Nordmark if (ilg->ilg_ill != ill) 2910bd670b35SErik Nordmark continue; 2911bd670b35SErik Nordmark 2912bd670b35SErik Nordmark /* The callers use upper ill for IPMP */ 2913bd670b35SErik Nordmark ASSERT(!IS_UNDER_IPMP(ill)); 2914bd670b35SErik Nordmark if (IN6_ARE_ADDR_EQUAL(&ilg->ilg_v6group, &v6group)) { 29157c478bd9Sstevel@tonic-gate if (SLIST_IS_EMPTY(ilg->ilg_filter)) { 29167c478bd9Sstevel@tonic-gate /* no source filter, so this is a match */ 2917bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 2918bd670b35SErik Nordmark return (B_TRUE); 29197c478bd9Sstevel@tonic-gate } 29207c478bd9Sstevel@tonic-gate break; 29217c478bd9Sstevel@tonic-gate } 29227c478bd9Sstevel@tonic-gate } 2923bd670b35SErik Nordmark if (ilg == NULL) { 2924bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 2925bd670b35SErik Nordmark return (B_FALSE); 2926bd670b35SErik Nordmark } 29277c478bd9Sstevel@tonic-gate 29287c478bd9Sstevel@tonic-gate /* 29297c478bd9Sstevel@tonic-gate * we have an ilg with matching ill and group; but 29307c478bd9Sstevel@tonic-gate * the ilg has a source list that we must check. 29317c478bd9Sstevel@tonic-gate */ 29327c478bd9Sstevel@tonic-gate IN6_IPADDR_TO_V4MAPPED(src, &v6src); 29337c478bd9Sstevel@tonic-gate isinlist = B_FALSE; 29347c478bd9Sstevel@tonic-gate for (i = 0; i < ilg->ilg_filter->sl_numsrc; i++) { 29357c478bd9Sstevel@tonic-gate if (IN6_ARE_ADDR_EQUAL(&v6src, &ilg->ilg_filter->sl_addr[i])) { 29367c478bd9Sstevel@tonic-gate isinlist = B_TRUE; 29377c478bd9Sstevel@tonic-gate break; 29387c478bd9Sstevel@tonic-gate } 29397c478bd9Sstevel@tonic-gate } 29407c478bd9Sstevel@tonic-gate 29417c478bd9Sstevel@tonic-gate if ((isinlist && ilg->ilg_fmode == MODE_IS_INCLUDE) || 2942bd670b35SErik Nordmark (!isinlist && ilg->ilg_fmode == MODE_IS_EXCLUDE)) { 2943bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 2944bd670b35SErik Nordmark return (B_TRUE); 2945bd670b35SErik Nordmark } 2946bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 2947bd670b35SErik Nordmark return (B_FALSE); 29487c478bd9Sstevel@tonic-gate } 29497c478bd9Sstevel@tonic-gate 29507c478bd9Sstevel@tonic-gate /* 29517c478bd9Sstevel@tonic-gate * Find an IPv6 ilg matching group, ill, and source 29527c478bd9Sstevel@tonic-gate */ 2953bd670b35SErik Nordmark boolean_t 2954bd670b35SErik Nordmark conn_hasmembers_ill_withsrc_v6(conn_t *connp, const in6_addr_t *v6group, 29557c478bd9Sstevel@tonic-gate const in6_addr_t *v6src, ill_t *ill) 29567c478bd9Sstevel@tonic-gate { 29577c478bd9Sstevel@tonic-gate int i; 29587c478bd9Sstevel@tonic-gate boolean_t isinlist; 29597c478bd9Sstevel@tonic-gate ilg_t *ilg; 29607c478bd9Sstevel@tonic-gate 2961bd670b35SErik Nordmark rw_enter(&connp->conn_ilg_lock, RW_READER); 2962bd670b35SErik Nordmark for (ilg = connp->conn_ilg; ilg != NULL; ilg = ilg->ilg_next) { 2963bd670b35SErik Nordmark if (ilg->ilg_condemned) 29647c478bd9Sstevel@tonic-gate continue; 2965bd670b35SErik Nordmark 2966bd670b35SErik Nordmark /* ilg_ill could be NULL if an add is in progress */ 2967bd670b35SErik Nordmark if (ilg->ilg_ill != ill) 2968bd670b35SErik Nordmark continue; 2969bd670b35SErik Nordmark 2970bd670b35SErik Nordmark /* The callers use upper ill for IPMP */ 2971bd670b35SErik Nordmark ASSERT(!IS_UNDER_IPMP(ill)); 2972bd670b35SErik Nordmark if (IN6_ARE_ADDR_EQUAL(&ilg->ilg_v6group, v6group)) { 29737c478bd9Sstevel@tonic-gate if (SLIST_IS_EMPTY(ilg->ilg_filter)) { 29747c478bd9Sstevel@tonic-gate /* no source filter, so this is a match */ 2975bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 2976bd670b35SErik Nordmark return (B_TRUE); 29777c478bd9Sstevel@tonic-gate } 29787c478bd9Sstevel@tonic-gate break; 29797c478bd9Sstevel@tonic-gate } 29807c478bd9Sstevel@tonic-gate } 2981bd670b35SErik Nordmark if (ilg == NULL) { 2982bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 2983bd670b35SErik Nordmark return (B_FALSE); 2984bd670b35SErik Nordmark } 29857c478bd9Sstevel@tonic-gate 29867c478bd9Sstevel@tonic-gate /* 29877c478bd9Sstevel@tonic-gate * we have an ilg with matching ill and group; but 29887c478bd9Sstevel@tonic-gate * the ilg has a source list that we must check. 29897c478bd9Sstevel@tonic-gate */ 29907c478bd9Sstevel@tonic-gate isinlist = B_FALSE; 29917c478bd9Sstevel@tonic-gate for (i = 0; i < ilg->ilg_filter->sl_numsrc; i++) { 29927c478bd9Sstevel@tonic-gate if (IN6_ARE_ADDR_EQUAL(v6src, &ilg->ilg_filter->sl_addr[i])) { 29937c478bd9Sstevel@tonic-gate isinlist = B_TRUE; 29947c478bd9Sstevel@tonic-gate break; 29957c478bd9Sstevel@tonic-gate } 29967c478bd9Sstevel@tonic-gate } 29977c478bd9Sstevel@tonic-gate 29987c478bd9Sstevel@tonic-gate if ((isinlist && ilg->ilg_fmode == MODE_IS_INCLUDE) || 2999bd670b35SErik Nordmark (!isinlist && ilg->ilg_fmode == MODE_IS_EXCLUDE)) { 3000bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 3001bd670b35SErik Nordmark return (B_TRUE); 3002bd670b35SErik Nordmark } 3003bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 3004bd670b35SErik Nordmark return (B_FALSE); 30057c478bd9Sstevel@tonic-gate } 30067c478bd9Sstevel@tonic-gate 30077c478bd9Sstevel@tonic-gate /* 3008bd670b35SErik Nordmark * Find an ilg matching group and ifaddr/ifindex. 3009bd670b35SErik Nordmark * We check both ifaddr and ifindex even though at most one of them 3010bd670b35SErik Nordmark * will be non-zero; that way we always find the right one. 30117c478bd9Sstevel@tonic-gate */ 30127c478bd9Sstevel@tonic-gate static ilg_t * 3013bd670b35SErik Nordmark ilg_lookup(conn_t *connp, const in6_addr_t *v6group, ipaddr_t ifaddr, 3014bd670b35SErik Nordmark uint_t ifindex) 30157c478bd9Sstevel@tonic-gate { 3016384ad179Ssowmini ilg_t *ilg; 30177c478bd9Sstevel@tonic-gate 3018bd670b35SErik Nordmark ASSERT(RW_LOCK_HELD(&connp->conn_ilg_lock)); 30197c478bd9Sstevel@tonic-gate 3020bd670b35SErik Nordmark for (ilg = connp->conn_ilg; ilg != NULL; ilg = ilg->ilg_next) { 3021bd670b35SErik Nordmark if (ilg->ilg_condemned) 3022bd670b35SErik Nordmark continue; 30237c478bd9Sstevel@tonic-gate 3024bd670b35SErik Nordmark if (ilg->ilg_ifaddr == ifaddr && 3025bd670b35SErik Nordmark ilg->ilg_ifindex == ifindex && 3026bd670b35SErik Nordmark IN6_ARE_ADDR_EQUAL(&ilg->ilg_v6group, v6group)) 3027384ad179Ssowmini return (ilg); 30287c478bd9Sstevel@tonic-gate } 30297c478bd9Sstevel@tonic-gate return (NULL); 30307c478bd9Sstevel@tonic-gate } 30317c478bd9Sstevel@tonic-gate 30327c478bd9Sstevel@tonic-gate /* 30337c478bd9Sstevel@tonic-gate * If a source address is passed in (src != NULL and src is not 30347c478bd9Sstevel@tonic-gate * unspecified), remove the specified src addr from the given ilg's 30357c478bd9Sstevel@tonic-gate * filter list, else delete the ilg. 30367c478bd9Sstevel@tonic-gate */ 30377c478bd9Sstevel@tonic-gate static void 30387c478bd9Sstevel@tonic-gate ilg_delete(conn_t *connp, ilg_t *ilg, const in6_addr_t *src) 30397c478bd9Sstevel@tonic-gate { 3040bd670b35SErik Nordmark ASSERT(RW_WRITE_HELD(&connp->conn_ilg_lock)); 3041bd670b35SErik Nordmark ASSERT(ilg->ilg_ptpn != NULL); 3042bd670b35SErik Nordmark ASSERT(!ilg->ilg_condemned); 30437c478bd9Sstevel@tonic-gate 30447c478bd9Sstevel@tonic-gate if (src == NULL || IN6_IS_ADDR_UNSPECIFIED(src)) { 30457c478bd9Sstevel@tonic-gate FREE_SLIST(ilg->ilg_filter); 3046bd670b35SErik Nordmark ilg->ilg_filter = NULL; 30477c478bd9Sstevel@tonic-gate 3048bd670b35SErik Nordmark ASSERT(ilg->ilg_ilm == NULL); 3049bd670b35SErik Nordmark ilg->ilg_ill = NULL; 3050bd670b35SErik Nordmark ilg->ilg_condemned = B_TRUE; 30517c478bd9Sstevel@tonic-gate 3052bd670b35SErik Nordmark /* ilg_inactive will unlink from the list */ 3053bd670b35SErik Nordmark ilg_refrele(ilg); 30547c478bd9Sstevel@tonic-gate } else { 30557c478bd9Sstevel@tonic-gate l_remove(ilg->ilg_filter, src); 30567c478bd9Sstevel@tonic-gate } 30577c478bd9Sstevel@tonic-gate } 30587c478bd9Sstevel@tonic-gate 30597c478bd9Sstevel@tonic-gate /* 3060bd670b35SErik Nordmark * Called from conn close. No new ilg can be added or removed 30617c478bd9Sstevel@tonic-gate * because CONN_CLOSING has been set by ip_close. ilg_add / ilg_delete 30627c478bd9Sstevel@tonic-gate * will return error if conn has started closing. 3063bd670b35SErik Nordmark * 3064bd670b35SErik Nordmark * We handle locking as follows. 3065bd670b35SErik Nordmark * Under conn_ilg_lock we get the first ilg. As we drop the conn_ilg_lock to 3066bd670b35SErik Nordmark * proceed with the ilm part of the delete we hold a reference on both the ill 3067bd670b35SErik Nordmark * and the ilg. This doesn't prevent changes to the ilg, but prevents it from 3068bd670b35SErik Nordmark * being deleted. 3069bd670b35SErik Nordmark * 3070bd670b35SErik Nordmark * Since the ilg_add code path uses two locks (conn_ilg_lock for the ilg part, 3071bd670b35SErik Nordmark * and ill_mcast_lock for the ip_addmulti part) we can run at a point between 3072bd670b35SErik Nordmark * the two. At that point ilg_ill is set, but ilg_ilm hasn't yet been set. In 3073bd670b35SErik Nordmark * that case we delete the ilg here, which makes ilg_add discover that the ilg 3074bd670b35SErik Nordmark * has disappeared when ip_addmulti returns, so it will discard the ilm it just 3075bd670b35SErik Nordmark * added. 30767c478bd9Sstevel@tonic-gate */ 30777c478bd9Sstevel@tonic-gate void 30787c478bd9Sstevel@tonic-gate ilg_delete_all(conn_t *connp) 30797c478bd9Sstevel@tonic-gate { 3080bd670b35SErik Nordmark ilg_t *ilg, *next_ilg, *held_ilg; 3081bd670b35SErik Nordmark ilm_t *ilm; 3082bd670b35SErik Nordmark ill_t *ill; 3083bd670b35SErik Nordmark boolean_t need_refrele; 30847c478bd9Sstevel@tonic-gate 3085bd670b35SErik Nordmark /* 3086bd670b35SErik Nordmark * Can not run if there is a conn_update_ill already running. 3087bd670b35SErik Nordmark * Wait for it to complete. Caller should have already set CONN_CLOSING 3088bd670b35SErik Nordmark * which prevents any new threads to run in conn_update_ill. 3089bd670b35SErik Nordmark */ 30907c478bd9Sstevel@tonic-gate mutex_enter(&connp->conn_lock); 3091bd670b35SErik Nordmark ASSERT(connp->conn_state_flags & CONN_CLOSING); 3092bd670b35SErik Nordmark while (connp->conn_state_flags & CONN_UPDATE_ILL) 3093bd670b35SErik Nordmark cv_wait(&connp->conn_cv, &connp->conn_lock); 30947c478bd9Sstevel@tonic-gate mutex_exit(&connp->conn_lock); 30957c478bd9Sstevel@tonic-gate 3096bd670b35SErik Nordmark rw_enter(&connp->conn_ilg_lock, RW_WRITER); 3097bd670b35SErik Nordmark ilg = connp->conn_ilg; 3098bd670b35SErik Nordmark held_ilg = NULL; 3099bd670b35SErik Nordmark while (ilg != NULL) { 3100bd670b35SErik Nordmark if (ilg->ilg_condemned) { 3101bd670b35SErik Nordmark ilg = ilg->ilg_next; 31027c478bd9Sstevel@tonic-gate continue; 31037c478bd9Sstevel@tonic-gate } 3104bd670b35SErik Nordmark /* If the ilg is detached then no need to serialize */ 3105bd670b35SErik Nordmark if (ilg->ilg_ilm == NULL) { 3106bd670b35SErik Nordmark next_ilg = ilg->ilg_next; 31077c478bd9Sstevel@tonic-gate ilg_delete(connp, ilg, NULL); 3108bd670b35SErik Nordmark ilg = next_ilg; 3109bd670b35SErik Nordmark continue; 3110bd670b35SErik Nordmark } 3111bd670b35SErik Nordmark ill = ilg->ilg_ilm->ilm_ill; 31127c478bd9Sstevel@tonic-gate 3113bd670b35SErik Nordmark /* 3114bd670b35SErik Nordmark * In order to serialize on the ill we try to enter 3115bd670b35SErik Nordmark * and if that fails we unlock and relock and then 3116bd670b35SErik Nordmark * check that we still have an ilm. 3117bd670b35SErik Nordmark */ 3118bd670b35SErik Nordmark need_refrele = B_FALSE; 3119bd670b35SErik Nordmark if (!mutex_tryenter(&ill->ill_mcast_serializer)) { 3120bd670b35SErik Nordmark ill_refhold(ill); 3121bd670b35SErik Nordmark need_refrele = B_TRUE; 3122bd670b35SErik Nordmark ilg_refhold(ilg); 3123bd670b35SErik Nordmark if (held_ilg != NULL) 3124bd670b35SErik Nordmark ilg_refrele(held_ilg); 3125bd670b35SErik Nordmark held_ilg = ilg; 3126bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 3127bd670b35SErik Nordmark mutex_enter(&ill->ill_mcast_serializer); 3128bd670b35SErik Nordmark rw_enter(&connp->conn_ilg_lock, RW_WRITER); 3129bd670b35SErik Nordmark if (ilg->ilg_condemned) { 3130bd670b35SErik Nordmark ilg = ilg->ilg_next; 3131bd670b35SErik Nordmark goto next; 3132e11c3f44Smeem } 31337c478bd9Sstevel@tonic-gate } 3134bd670b35SErik Nordmark ilm = ilg->ilg_ilm; 3135bd670b35SErik Nordmark ilg->ilg_ilm = NULL; 3136bd670b35SErik Nordmark next_ilg = ilg->ilg_next; 3137bd670b35SErik Nordmark ilg_delete(connp, ilg, NULL); 3138bd670b35SErik Nordmark ilg = next_ilg; 3139bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 31407c478bd9Sstevel@tonic-gate 3141bd670b35SErik Nordmark if (ilm != NULL) 3142bd670b35SErik Nordmark (void) ip_delmulti_serial(ilm, B_FALSE, B_TRUE); 3143bd670b35SErik Nordmark 3144bd670b35SErik Nordmark next: 3145bd670b35SErik Nordmark mutex_exit(&ill->ill_mcast_serializer); 3146f1c454b4SSowmini Varadhan /* 3147f1c454b4SSowmini Varadhan * Now that all locks have been dropped, we can send any 3148f1c454b4SSowmini Varadhan * deferred/queued DLPI or IP packets 3149f1c454b4SSowmini Varadhan */ 3150f1c454b4SSowmini Varadhan ill_mcast_send_queued(ill); 3151f1c454b4SSowmini Varadhan ill_dlpi_send_queued(ill); 3152bd670b35SErik Nordmark if (need_refrele) { 3153bd670b35SErik Nordmark /* Drop ill reference while we hold no locks */ 3154bd670b35SErik Nordmark ill_refrele(ill); 31557c478bd9Sstevel@tonic-gate } 3156bd670b35SErik Nordmark rw_enter(&connp->conn_ilg_lock, RW_WRITER); 3157bd670b35SErik Nordmark } 3158bd670b35SErik Nordmark if (held_ilg != NULL) 3159bd670b35SErik Nordmark ilg_refrele(held_ilg); 3160bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 31617c478bd9Sstevel@tonic-gate } 31627c478bd9Sstevel@tonic-gate 31637c478bd9Sstevel@tonic-gate /* 3164bd670b35SErik Nordmark * Attach the ilg to an ilm on the ill. If it fails we leave ilg_ill as NULL so 3165f1c454b4SSowmini Varadhan * that a subsequent attempt can attach it. Drops and reacquires conn_ilg_lock. 31667c478bd9Sstevel@tonic-gate */ 31677c478bd9Sstevel@tonic-gate static void 3168bd670b35SErik Nordmark ilg_attach(conn_t *connp, ilg_t *ilg, ill_t *ill) 31697c478bd9Sstevel@tonic-gate { 3170bd670b35SErik Nordmark ilg_stat_t ilgstat; 3171bd670b35SErik Nordmark slist_t *new_filter; 3172bd670b35SErik Nordmark int new_fmode; 31737c478bd9Sstevel@tonic-gate in6_addr_t v6group; 3174bd670b35SErik Nordmark ipaddr_t ifaddr; 3175bd670b35SErik Nordmark uint_t ifindex; 3176bd670b35SErik Nordmark ilm_t *ilm; 3177bd670b35SErik Nordmark int error = 0; 3178bd670b35SErik Nordmark 3179bd670b35SErik Nordmark ASSERT(RW_WRITE_HELD(&connp->conn_ilg_lock)); 3180bd670b35SErik Nordmark /* 3181bd670b35SErik Nordmark * Alloc buffer to copy new state into (see below) before 3182bd670b35SErik Nordmark * we make any changes, so we can bail if it fails. 3183bd670b35SErik Nordmark */ 3184bd670b35SErik Nordmark if ((new_filter = l_alloc()) == NULL) 3185bd670b35SErik Nordmark return; 31867c478bd9Sstevel@tonic-gate 31877c478bd9Sstevel@tonic-gate /* 3188bd670b35SErik Nordmark * Save copy of ilg's filter state to pass to other functions, so 3189bd670b35SErik Nordmark * we can release conn_ilg_lock now. 3190bd670b35SErik Nordmark * Set ilg_ill so that an unplumb can find us. 31917c478bd9Sstevel@tonic-gate */ 3192bd670b35SErik Nordmark new_fmode = ilg->ilg_fmode; 3193bd670b35SErik Nordmark l_copy(ilg->ilg_filter, new_filter); 31947c478bd9Sstevel@tonic-gate v6group = ilg->ilg_v6group; 3195bd670b35SErik Nordmark ifaddr = ilg->ilg_ifaddr; 3196bd670b35SErik Nordmark ifindex = ilg->ilg_ifindex; 3197bd670b35SErik Nordmark ilgstat = ILGSTAT_NEW; 31987c478bd9Sstevel@tonic-gate 3199bd670b35SErik Nordmark ilg->ilg_ill = ill; 3200bd670b35SErik Nordmark ASSERT(ilg->ilg_ilm == NULL); 3201bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 3202bd670b35SErik Nordmark 3203bd670b35SErik Nordmark ilm = ip_addmulti_serial(&v6group, ill, connp->conn_zoneid, ilgstat, 3204bd670b35SErik Nordmark new_fmode, new_filter, &error); 3205bd670b35SErik Nordmark l_free(new_filter); 3206bd670b35SErik Nordmark 3207bd670b35SErik Nordmark rw_enter(&connp->conn_ilg_lock, RW_WRITER); 32087c478bd9Sstevel@tonic-gate /* 3209bd670b35SErik Nordmark * Must look up the ilg again since we've not been holding 3210bd670b35SErik Nordmark * conn_ilg_lock. The ilg could have disappeared due to an unplumb 3211bd670b35SErik Nordmark * having called conn_update_ill, which can run once we dropped the 3212f1c454b4SSowmini Varadhan * conn_ilg_lock above. Alternatively, the ilg could have been attached 3213f1c454b4SSowmini Varadhan * when the lock was dropped 32147c478bd9Sstevel@tonic-gate */ 3215bd670b35SErik Nordmark ilg = ilg_lookup(connp, &v6group, ifaddr, ifindex); 3216f1c454b4SSowmini Varadhan if (ilg == NULL || ilg->ilg_ilm != NULL) { 3217bd670b35SErik Nordmark if (ilm != NULL) { 3218bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 3219bd670b35SErik Nordmark (void) ip_delmulti_serial(ilm, B_FALSE, 3220bd670b35SErik Nordmark (ilgstat == ILGSTAT_NEW)); 3221bd670b35SErik Nordmark rw_enter(&connp->conn_ilg_lock, RW_WRITER); 32227c478bd9Sstevel@tonic-gate } 3223bd670b35SErik Nordmark return; 32247c478bd9Sstevel@tonic-gate } 3225bd670b35SErik Nordmark if (ilm == NULL) { 3226bd670b35SErik Nordmark ilg->ilg_ill = NULL; 3227bd670b35SErik Nordmark return; 3228bd670b35SErik Nordmark } 3229bd670b35SErik Nordmark ilg->ilg_ilm = ilm; 3230bd670b35SErik Nordmark ilm->ilm_ifaddr = ifaddr; /* For netstat */ 32317c478bd9Sstevel@tonic-gate } 32327c478bd9Sstevel@tonic-gate 32337c478bd9Sstevel@tonic-gate /* 32347c478bd9Sstevel@tonic-gate * Called when an ill is unplumbed to make sure that there are no 3235bd670b35SErik Nordmark * dangling conn references to that ill. In that case ill is non-NULL and 3236bd670b35SErik Nordmark * we make sure we remove all references to it. 3237bd670b35SErik Nordmark * Also called when we should revisit the ilg_ill used for multicast 3238bd670b35SErik Nordmark * memberships, in which case ill is NULL. 3239bd670b35SErik Nordmark * 3240bd670b35SErik Nordmark * conn is held by caller. 3241bd670b35SErik Nordmark * 3242bd670b35SErik Nordmark * Note that ipcl_walk only walks conns that are not yet condemned. 3243bd670b35SErik Nordmark * condemned conns can't be refheld. For this reason, conn must become clean 3244bd670b35SErik Nordmark * first, i.e. it must not refer to any ill/ire and then only set 3245bd670b35SErik Nordmark * condemned flag. 3246bd670b35SErik Nordmark * 3247bd670b35SErik Nordmark * We leave ixa_multicast_ifindex in place. We prefer dropping 3248bd670b35SErik Nordmark * packets instead of sending them out the wrong interface. 3249bd670b35SErik Nordmark * 3250bd670b35SErik Nordmark * We keep the ilg around in a detached state (with ilg_ill and ilg_ilm being 3251bd670b35SErik Nordmark * NULL) so that the application can leave it later. Also, if ilg_ifaddr and 3252bd670b35SErik Nordmark * ilg_ifindex are zero, indicating that the system should pick the interface, 3253bd670b35SErik Nordmark * then we attempt to reselect the ill and join on it. 3254bd670b35SErik Nordmark * 3255bd670b35SErik Nordmark * Locking notes: 3256bd670b35SErik Nordmark * Under conn_ilg_lock we get the first ilg. As we drop the conn_ilg_lock to 3257bd670b35SErik Nordmark * proceed with the ilm part of the delete we hold a reference on both the ill 3258bd670b35SErik Nordmark * and the ilg. This doesn't prevent changes to the ilg, but prevents it from 3259bd670b35SErik Nordmark * being deleted. 3260bd670b35SErik Nordmark * 3261bd670b35SErik Nordmark * Note: if this function is called when new ill/ipif's arrive or change status 3262bd670b35SErik Nordmark * (SIOCSLIFINDEX, SIOCSLIFADDR) then we will attempt to attach any ilgs with 3263bd670b35SErik Nordmark * a NULL ilg_ill to an ill/ilm. 3264bd670b35SErik Nordmark */ 3265bd670b35SErik Nordmark static void 3266bd670b35SErik Nordmark conn_update_ill(conn_t *connp, caddr_t arg) 3267bd670b35SErik Nordmark { 3268bd670b35SErik Nordmark ill_t *ill = (ill_t *)arg; 3269bd670b35SErik Nordmark 3270bd670b35SErik Nordmark /* 3271bd670b35SErik Nordmark * We have to prevent ip_close/ilg_delete_all from running at 3272bd670b35SErik Nordmark * the same time. ip_close sets CONN_CLOSING before doing the ilg_delete 3273bd670b35SErik Nordmark * all, and we set CONN_UPDATE_ILL. That ensures that only one of 3274bd670b35SErik Nordmark * ilg_delete_all and conn_update_ill run at a time for a given conn. 3275bd670b35SErik Nordmark * If ilg_delete_all got here first, then we have nothing to do. 3276bd670b35SErik Nordmark */ 3277bd670b35SErik Nordmark mutex_enter(&connp->conn_lock); 3278bd670b35SErik Nordmark if (connp->conn_state_flags & (CONN_CLOSING|CONN_UPDATE_ILL)) { 3279bd670b35SErik Nordmark /* Caller has to wait for ill_ilm_cnt to drop to zero */ 3280bd670b35SErik Nordmark mutex_exit(&connp->conn_lock); 3281bd670b35SErik Nordmark return; 3282bd670b35SErik Nordmark } 3283bd670b35SErik Nordmark connp->conn_state_flags |= CONN_UPDATE_ILL; 3284bd670b35SErik Nordmark mutex_exit(&connp->conn_lock); 3285bd670b35SErik Nordmark 3286bd670b35SErik Nordmark if (ill != NULL) 3287bd670b35SErik Nordmark ilg_check_detach(connp, ill); 3288bd670b35SErik Nordmark 3289f1c454b4SSowmini Varadhan ilg_check_reattach(connp, ill); 3290bd670b35SErik Nordmark 3291bd670b35SErik Nordmark /* Do we need to wake up a thread in ilg_delete_all? */ 3292bd670b35SErik Nordmark mutex_enter(&connp->conn_lock); 3293bd670b35SErik Nordmark connp->conn_state_flags &= ~CONN_UPDATE_ILL; 3294bd670b35SErik Nordmark if (connp->conn_state_flags & CONN_CLOSING) 3295bd670b35SErik Nordmark cv_broadcast(&connp->conn_cv); 3296bd670b35SErik Nordmark mutex_exit(&connp->conn_lock); 3297bd670b35SErik Nordmark } 3298bd670b35SErik Nordmark 3299bd670b35SErik Nordmark /* Detach from an ill that is going away */ 3300bd670b35SErik Nordmark static void 3301bd670b35SErik Nordmark ilg_check_detach(conn_t *connp, ill_t *ill) 3302bd670b35SErik Nordmark { 3303bd670b35SErik Nordmark char group_buf[INET6_ADDRSTRLEN]; 3304bd670b35SErik Nordmark ilg_t *ilg, *held_ilg; 3305bd670b35SErik Nordmark ilm_t *ilm; 3306bd670b35SErik Nordmark 3307bd670b35SErik Nordmark mutex_enter(&ill->ill_mcast_serializer); 3308bd670b35SErik Nordmark rw_enter(&connp->conn_ilg_lock, RW_WRITER); 3309bd670b35SErik Nordmark held_ilg = NULL; 3310bd670b35SErik Nordmark for (ilg = connp->conn_ilg; ilg != NULL; ilg = ilg->ilg_next) { 3311bd670b35SErik Nordmark if (ilg->ilg_condemned) 3312bd670b35SErik Nordmark continue; 3313bd670b35SErik Nordmark 3314bd670b35SErik Nordmark if (ilg->ilg_ill != ill) 3315bd670b35SErik Nordmark continue; 3316bd670b35SErik Nordmark 3317bd670b35SErik Nordmark /* Detach from current ill */ 3318bd670b35SErik Nordmark ip1dbg(("ilg_check_detach: detach %s on %s\n", 3319bd670b35SErik Nordmark inet_ntop(AF_INET6, &ilg->ilg_v6group, 3320bd670b35SErik Nordmark group_buf, sizeof (group_buf)), 3321bd670b35SErik Nordmark ilg->ilg_ill->ill_name)); 3322bd670b35SErik Nordmark 3323bd670b35SErik Nordmark /* Detach this ilg from the ill/ilm */ 3324bd670b35SErik Nordmark ilm = ilg->ilg_ilm; 3325bd670b35SErik Nordmark ilg->ilg_ilm = NULL; 3326bd670b35SErik Nordmark ilg->ilg_ill = NULL; 3327bd670b35SErik Nordmark if (ilm == NULL) 3328bd670b35SErik Nordmark continue; 3329bd670b35SErik Nordmark 3330bd670b35SErik Nordmark /* Prevent ilg from disappearing */ 3331bd670b35SErik Nordmark ilg_transfer_hold(held_ilg, ilg); 3332bd670b35SErik Nordmark held_ilg = ilg; 3333bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 3334bd670b35SErik Nordmark 3335bd670b35SErik Nordmark (void) ip_delmulti_serial(ilm, B_FALSE, B_TRUE); 3336bd670b35SErik Nordmark rw_enter(&connp->conn_ilg_lock, RW_WRITER); 3337bd670b35SErik Nordmark } 3338bd670b35SErik Nordmark if (held_ilg != NULL) 3339bd670b35SErik Nordmark ilg_refrele(held_ilg); 3340bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 3341bd670b35SErik Nordmark mutex_exit(&ill->ill_mcast_serializer); 3342f1c454b4SSowmini Varadhan /* 3343f1c454b4SSowmini Varadhan * Now that all locks have been dropped, we can send any 3344f1c454b4SSowmini Varadhan * deferred/queued DLPI or IP packets 3345f1c454b4SSowmini Varadhan */ 3346f1c454b4SSowmini Varadhan ill_mcast_send_queued(ill); 3347f1c454b4SSowmini Varadhan ill_dlpi_send_queued(ill); 3348bd670b35SErik Nordmark } 3349bd670b35SErik Nordmark 3350bd670b35SErik Nordmark /* 3351bd670b35SErik Nordmark * Check if there is a place to attach the conn_ilgs. We do this for both 3352bd670b35SErik Nordmark * detached ilgs and attached ones, since for the latter there could be 3353f1c454b4SSowmini Varadhan * a better ill to attach them to. oill is non-null if we just detached from 3354f1c454b4SSowmini Varadhan * that ill. 3355bd670b35SErik Nordmark */ 3356bd670b35SErik Nordmark static void 3357f1c454b4SSowmini Varadhan ilg_check_reattach(conn_t *connp, ill_t *oill) 3358bd670b35SErik Nordmark { 3359bd670b35SErik Nordmark ill_t *ill; 3360bd670b35SErik Nordmark char group_buf[INET6_ADDRSTRLEN]; 3361bd670b35SErik Nordmark ilg_t *ilg, *held_ilg; 3362bd670b35SErik Nordmark ilm_t *ilm; 3363bd670b35SErik Nordmark zoneid_t zoneid = IPCL_ZONEID(connp); 3364bd670b35SErik Nordmark int error; 3365bd670b35SErik Nordmark ip_stack_t *ipst = connp->conn_netstack->netstack_ip; 3366bd670b35SErik Nordmark 3367bd670b35SErik Nordmark rw_enter(&connp->conn_ilg_lock, RW_WRITER); 3368bd670b35SErik Nordmark held_ilg = NULL; 3369bd670b35SErik Nordmark for (ilg = connp->conn_ilg; ilg != NULL; ilg = ilg->ilg_next) { 3370bd670b35SErik Nordmark if (ilg->ilg_condemned) 3371bd670b35SErik Nordmark continue; 3372bd670b35SErik Nordmark 3373bd670b35SErik Nordmark /* Check if the conn_ill matches what we would pick now */ 3374bd670b35SErik Nordmark ill = ill_mcast_lookup(&ilg->ilg_v6group, ilg->ilg_ifaddr, 3375bd670b35SErik Nordmark ilg->ilg_ifindex, zoneid, ipst, &error); 3376bd670b35SErik Nordmark 3377bd670b35SErik Nordmark /* 3378bd670b35SErik Nordmark * Make sure the ill is usable for multicast and that 3379bd670b35SErik Nordmark * we can send the DL_ADDMULTI_REQ before we create an 3380bd670b35SErik Nordmark * ilm. 3381bd670b35SErik Nordmark */ 3382bd670b35SErik Nordmark if (ill != NULL && 3383bd670b35SErik Nordmark (!(ill->ill_flags & ILLF_MULTICAST) || !ill->ill_dl_up)) { 3384bd670b35SErik Nordmark /* Drop locks across ill_refrele */ 3385bd670b35SErik Nordmark ilg_transfer_hold(held_ilg, ilg); 3386bd670b35SErik Nordmark held_ilg = ilg; 3387bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 3388bd670b35SErik Nordmark ill_refrele(ill); 3389bd670b35SErik Nordmark ill = NULL; 3390bd670b35SErik Nordmark rw_enter(&connp->conn_ilg_lock, RW_WRITER); 3391bd670b35SErik Nordmark /* Note that ilg could have become condemned */ 3392bd670b35SErik Nordmark } 3393bd670b35SErik Nordmark 3394f1c454b4SSowmini Varadhan /* 3395f1c454b4SSowmini Varadhan * Is the ill unchanged, even if both are NULL? 3396f1c454b4SSowmini Varadhan * Did we just detach from that ill? 3397f1c454b4SSowmini Varadhan */ 3398f1c454b4SSowmini Varadhan if (ill == ilg->ilg_ill || (ill != NULL && ill == oill)) { 3399bd670b35SErik Nordmark if (ill != NULL) { 3400bd670b35SErik Nordmark /* Drop locks across ill_refrele */ 3401bd670b35SErik Nordmark ilg_transfer_hold(held_ilg, ilg); 3402bd670b35SErik Nordmark held_ilg = ilg; 3403bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 3404bd670b35SErik Nordmark ill_refrele(ill); 3405bd670b35SErik Nordmark rw_enter(&connp->conn_ilg_lock, RW_WRITER); 3406bd670b35SErik Nordmark } 3407bd670b35SErik Nordmark continue; 3408bd670b35SErik Nordmark } 3409bd670b35SErik Nordmark 3410bd670b35SErik Nordmark /* Something changed; detach from old first if needed */ 3411bd670b35SErik Nordmark if (ilg->ilg_ill != NULL) { 3412bd670b35SErik Nordmark ill_t *ill2 = ilg->ilg_ill; 3413bd670b35SErik Nordmark boolean_t need_refrele = B_FALSE; 3414bd670b35SErik Nordmark 3415bd670b35SErik Nordmark /* 3416bd670b35SErik Nordmark * In order to serialize on the ill we try to enter 3417bd670b35SErik Nordmark * and if that fails we unlock and relock. 3418bd670b35SErik Nordmark */ 3419bd670b35SErik Nordmark if (!mutex_tryenter(&ill2->ill_mcast_serializer)) { 3420bd670b35SErik Nordmark ill_refhold(ill2); 3421bd670b35SErik Nordmark need_refrele = B_TRUE; 3422bd670b35SErik Nordmark ilg_transfer_hold(held_ilg, ilg); 3423bd670b35SErik Nordmark held_ilg = ilg; 3424bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 3425bd670b35SErik Nordmark mutex_enter(&ill2->ill_mcast_serializer); 3426bd670b35SErik Nordmark rw_enter(&connp->conn_ilg_lock, RW_WRITER); 3427bd670b35SErik Nordmark /* Note that ilg could have become condemned */ 3428bd670b35SErik Nordmark } 3429bd670b35SErik Nordmark /* 3430bd670b35SErik Nordmark * Check that nobody else re-attached the ilg while we 3431bd670b35SErik Nordmark * dropped the lock. 3432bd670b35SErik Nordmark */ 3433bd670b35SErik Nordmark if (ilg->ilg_ill == ill2) { 3434bd670b35SErik Nordmark ASSERT(!ilg->ilg_condemned); 3435bd670b35SErik Nordmark /* Detach from current ill */ 3436bd670b35SErik Nordmark ip1dbg(("conn_check_reattach: detach %s/%s\n", 3437bd670b35SErik Nordmark inet_ntop(AF_INET6, &ilg->ilg_v6group, 3438bd670b35SErik Nordmark group_buf, sizeof (group_buf)), 3439bd670b35SErik Nordmark ill2->ill_name)); 3440bd670b35SErik Nordmark 3441bd670b35SErik Nordmark ilm = ilg->ilg_ilm; 3442bd670b35SErik Nordmark ilg->ilg_ilm = NULL; 3443bd670b35SErik Nordmark ilg->ilg_ill = NULL; 3444bd670b35SErik Nordmark } else { 3445bd670b35SErik Nordmark ilm = NULL; 3446bd670b35SErik Nordmark } 3447f1c454b4SSowmini Varadhan ilg_transfer_hold(held_ilg, ilg); 3448f1c454b4SSowmini Varadhan held_ilg = ilg; 3449bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 3450bd670b35SErik Nordmark if (ilm != NULL) 3451bd670b35SErik Nordmark (void) ip_delmulti_serial(ilm, B_FALSE, B_TRUE); 3452bd670b35SErik Nordmark mutex_exit(&ill2->ill_mcast_serializer); 3453f1c454b4SSowmini Varadhan /* 3454f1c454b4SSowmini Varadhan * Now that all locks have been dropped, we can send any 3455f1c454b4SSowmini Varadhan * deferred/queued DLPI or IP packets 3456f1c454b4SSowmini Varadhan */ 3457f1c454b4SSowmini Varadhan ill_mcast_send_queued(ill2); 3458f1c454b4SSowmini Varadhan ill_dlpi_send_queued(ill2); 3459bd670b35SErik Nordmark if (need_refrele) { 3460bd670b35SErik Nordmark /* Drop ill reference while we hold no locks */ 3461bd670b35SErik Nordmark ill_refrele(ill2); 3462bd670b35SErik Nordmark } 3463bd670b35SErik Nordmark rw_enter(&connp->conn_ilg_lock, RW_WRITER); 3464bd670b35SErik Nordmark /* 3465bd670b35SErik Nordmark * While we dropped conn_ilg_lock some other thread 3466bd670b35SErik Nordmark * could have attached this ilg, thus we check again. 3467bd670b35SErik Nordmark */ 3468bd670b35SErik Nordmark if (ilg->ilg_ill != NULL) { 3469bd670b35SErik Nordmark if (ill != NULL) { 3470bd670b35SErik Nordmark /* Drop locks across ill_refrele */ 3471bd670b35SErik Nordmark ilg_transfer_hold(held_ilg, ilg); 3472bd670b35SErik Nordmark held_ilg = ilg; 3473bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 3474bd670b35SErik Nordmark ill_refrele(ill); 3475bd670b35SErik Nordmark rw_enter(&connp->conn_ilg_lock, 3476bd670b35SErik Nordmark RW_WRITER); 3477bd670b35SErik Nordmark } 3478bd670b35SErik Nordmark continue; 3479bd670b35SErik Nordmark } 3480bd670b35SErik Nordmark } 3481bd670b35SErik Nordmark if (ill != NULL) { 3482bd670b35SErik Nordmark /* 3483bd670b35SErik Nordmark * In order to serialize on the ill we try to enter 3484bd670b35SErik Nordmark * and if that fails we unlock and relock. 3485bd670b35SErik Nordmark */ 3486bd670b35SErik Nordmark if (!mutex_tryenter(&ill->ill_mcast_serializer)) { 3487bd670b35SErik Nordmark /* Already have a refhold on ill */ 3488bd670b35SErik Nordmark ilg_transfer_hold(held_ilg, ilg); 3489bd670b35SErik Nordmark held_ilg = ilg; 3490bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 3491bd670b35SErik Nordmark mutex_enter(&ill->ill_mcast_serializer); 3492bd670b35SErik Nordmark rw_enter(&connp->conn_ilg_lock, RW_WRITER); 3493bd670b35SErik Nordmark /* Note that ilg could have become condemned */ 3494bd670b35SErik Nordmark } 3495f1c454b4SSowmini Varadhan ilg_transfer_hold(held_ilg, ilg); 3496f1c454b4SSowmini Varadhan held_ilg = ilg; 3497bd670b35SErik Nordmark /* 3498bd670b35SErik Nordmark * Check that nobody else attached the ilg and that 3499bd670b35SErik Nordmark * it wasn't condemned while we dropped the lock. 3500bd670b35SErik Nordmark */ 3501bd670b35SErik Nordmark if (ilg->ilg_ill == NULL && !ilg->ilg_condemned) { 3502bd670b35SErik Nordmark /* 3503bd670b35SErik Nordmark * Attach to the new ill. Can fail in which 3504bd670b35SErik Nordmark * case ilg_ill will remain NULL. ilg_attach 3505bd670b35SErik Nordmark * drops and reacquires conn_ilg_lock. 3506bd670b35SErik Nordmark */ 3507bd670b35SErik Nordmark ip1dbg(("conn_check_reattach: attach %s/%s\n", 3508bd670b35SErik Nordmark inet_ntop(AF_INET6, &ilg->ilg_v6group, 3509bd670b35SErik Nordmark group_buf, sizeof (group_buf)), 3510bd670b35SErik Nordmark ill->ill_name)); 3511bd670b35SErik Nordmark ilg_attach(connp, ilg, ill); 3512bd670b35SErik Nordmark ASSERT(RW_WRITE_HELD(&connp->conn_ilg_lock)); 3513bd670b35SErik Nordmark } 3514bd670b35SErik Nordmark /* Drop locks across ill_refrele */ 3515bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 3516f1c454b4SSowmini Varadhan mutex_exit(&ill->ill_mcast_serializer); 3517f1c454b4SSowmini Varadhan /* 3518f1c454b4SSowmini Varadhan * Now that all locks have been 3519f1c454b4SSowmini Varadhan * dropped, we can send any 3520f1c454b4SSowmini Varadhan * deferred/queued DLPI or IP packets 3521f1c454b4SSowmini Varadhan */ 3522f1c454b4SSowmini Varadhan ill_mcast_send_queued(ill); 3523f1c454b4SSowmini Varadhan ill_dlpi_send_queued(ill); 3524bd670b35SErik Nordmark ill_refrele(ill); 3525bd670b35SErik Nordmark rw_enter(&connp->conn_ilg_lock, RW_WRITER); 3526bd670b35SErik Nordmark } 3527bd670b35SErik Nordmark } 3528bd670b35SErik Nordmark if (held_ilg != NULL) 3529bd670b35SErik Nordmark ilg_refrele(held_ilg); 3530bd670b35SErik Nordmark rw_exit(&connp->conn_ilg_lock); 3531bd670b35SErik Nordmark } 3532bd670b35SErik Nordmark 3533bd670b35SErik Nordmark /* 3534bd670b35SErik Nordmark * Called when an ill is unplumbed to make sure that there are no 3535bd670b35SErik Nordmark * dangling conn references to that ill. In that case ill is non-NULL and 3536bd670b35SErik Nordmark * we make sure we remove all references to it. 3537bd670b35SErik Nordmark * Also called when we should revisit the ilg_ill used for multicast 3538bd670b35SErik Nordmark * memberships, in which case ill is NULL. 35397c478bd9Sstevel@tonic-gate */ 35407c478bd9Sstevel@tonic-gate void 3541bd670b35SErik Nordmark update_conn_ill(ill_t *ill, ip_stack_t *ipst) 35427c478bd9Sstevel@tonic-gate { 3543bd670b35SErik Nordmark ipcl_walk(conn_update_ill, (caddr_t)ill, ipst); 35447c478bd9Sstevel@tonic-gate } 3545