10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 51676Sjpk * Common Development and Distribution License (the "License"). 61676Sjpk * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 22*11463SSowmini.Varadhan@Sun.COM * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate /* Copyright (c) 1990 Mentat Inc. */ 260Sstevel@tonic-gate 270Sstevel@tonic-gate #include <sys/types.h> 280Sstevel@tonic-gate #include <sys/stream.h> 290Sstevel@tonic-gate #include <sys/dlpi.h> 300Sstevel@tonic-gate #include <sys/stropts.h> 310Sstevel@tonic-gate #include <sys/strsun.h> 320Sstevel@tonic-gate #include <sys/ddi.h> 330Sstevel@tonic-gate #include <sys/cmn_err.h> 342958Sdr146992 #include <sys/sdt.h> 350Sstevel@tonic-gate #include <sys/zone.h> 360Sstevel@tonic-gate 370Sstevel@tonic-gate #include <sys/param.h> 380Sstevel@tonic-gate #include <sys/socket.h> 391676Sjpk #include <sys/sockio.h> 400Sstevel@tonic-gate #include <net/if.h> 410Sstevel@tonic-gate #include <sys/systm.h> 425758Sjarrett #include <sys/strsubr.h> 430Sstevel@tonic-gate #include <net/route.h> 440Sstevel@tonic-gate #include <netinet/in.h> 450Sstevel@tonic-gate #include <net/if_dl.h> 460Sstevel@tonic-gate #include <netinet/ip6.h> 470Sstevel@tonic-gate #include <netinet/icmp6.h> 480Sstevel@tonic-gate 490Sstevel@tonic-gate #include <inet/common.h> 500Sstevel@tonic-gate #include <inet/mi.h> 510Sstevel@tonic-gate #include <inet/nd.h> 520Sstevel@tonic-gate #include <inet/arp.h> 530Sstevel@tonic-gate #include <inet/ip.h> 540Sstevel@tonic-gate #include <inet/ip6.h> 550Sstevel@tonic-gate #include <inet/ip_if.h> 560Sstevel@tonic-gate #include <inet/ip_ndp.h> 570Sstevel@tonic-gate #include <inet/ip_multi.h> 580Sstevel@tonic-gate #include <inet/ipclassifier.h> 590Sstevel@tonic-gate #include <inet/ipsec_impl.h> 600Sstevel@tonic-gate #include <inet/sctp_ip.h> 610Sstevel@tonic-gate #include <inet/ip_listutils.h> 62741Smasputra #include <inet/udp_impl.h> 630Sstevel@tonic-gate 640Sstevel@tonic-gate /* igmpv3/mldv2 source filter manipulation */ 650Sstevel@tonic-gate static void ilm_bld_flists(conn_t *conn, void *arg); 660Sstevel@tonic-gate static void ilm_gen_filter(ilm_t *ilm, mcast_record_t *fmode, 670Sstevel@tonic-gate slist_t *flist); 680Sstevel@tonic-gate 6911042SErik.Nordmark@Sun.COM static ilm_t *ilm_add(ill_t *ill, const in6_addr_t *group, 700Sstevel@tonic-gate ilg_stat_t ilgstat, mcast_record_t ilg_fmode, slist_t *ilg_flist, 718485SPeter.Memishian@Sun.COM zoneid_t zoneid); 720Sstevel@tonic-gate static void ilm_delete(ilm_t *ilm); 7311042SErik.Nordmark@Sun.COM static int ilm_numentries(ill_t *, const in6_addr_t *); 7411042SErik.Nordmark@Sun.COM 7511042SErik.Nordmark@Sun.COM static ilm_t *ip_addmulti_serial(const in6_addr_t *, ill_t *, zoneid_t, 7611042SErik.Nordmark@Sun.COM ilg_stat_t, mcast_record_t, slist_t *, int *); 7711042SErik.Nordmark@Sun.COM static ilm_t *ip_addmulti_impl(const in6_addr_t *, ill_t *, 7811042SErik.Nordmark@Sun.COM zoneid_t, ilg_stat_t, mcast_record_t, slist_t *, int *); 7911042SErik.Nordmark@Sun.COM static int ip_delmulti_serial(ilm_t *, boolean_t, boolean_t); 8011042SErik.Nordmark@Sun.COM static int ip_delmulti_impl(ilm_t *, boolean_t, boolean_t); 8111042SErik.Nordmark@Sun.COM 8211042SErik.Nordmark@Sun.COM static int ip_ll_multireq(ill_t *ill, const in6_addr_t *group, 8311042SErik.Nordmark@Sun.COM t_uscalar_t); 8411042SErik.Nordmark@Sun.COM static ilg_t *ilg_lookup(conn_t *, const in6_addr_t *, ipaddr_t ifaddr, 8511042SErik.Nordmark@Sun.COM uint_t ifindex); 8611042SErik.Nordmark@Sun.COM 8711042SErik.Nordmark@Sun.COM static int ilg_add(conn_t *connp, const in6_addr_t *group, 8811042SErik.Nordmark@Sun.COM ipaddr_t ifaddr, uint_t ifindex, ill_t *ill, mcast_record_t fmode, 8911042SErik.Nordmark@Sun.COM const in6_addr_t *v6src); 900Sstevel@tonic-gate static void ilg_delete(conn_t *connp, ilg_t *ilg, const in6_addr_t *src); 910Sstevel@tonic-gate static mblk_t *ill_create_dl(ill_t *ill, uint32_t dl_primitive, 9211042SErik.Nordmark@Sun.COM uint32_t *addr_lenp, uint32_t *addr_offp); 9311042SErik.Nordmark@Sun.COM static int ip_opt_delete_group_excl(conn_t *connp, 9411042SErik.Nordmark@Sun.COM const in6_addr_t *v6group, ipaddr_t ifaddr, uint_t ifindex, 9511042SErik.Nordmark@Sun.COM mcast_record_t fmode, const in6_addr_t *v6src); 9611042SErik.Nordmark@Sun.COM 9711042SErik.Nordmark@Sun.COM static ilm_t *ilm_lookup(ill_t *, const in6_addr_t *, zoneid_t); 9811042SErik.Nordmark@Sun.COM 9911042SErik.Nordmark@Sun.COM static int ip_msfilter_ill(conn_t *, mblk_t *, const ip_ioctl_cmd_t *, 10011042SErik.Nordmark@Sun.COM ill_t **); 10111042SErik.Nordmark@Sun.COM 10211042SErik.Nordmark@Sun.COM static void ilg_check_detach(conn_t *, ill_t *); 103*11463SSowmini.Varadhan@Sun.COM static void ilg_check_reattach(conn_t *, ill_t *); 1040Sstevel@tonic-gate 1050Sstevel@tonic-gate /* 1060Sstevel@tonic-gate * MT notes: 1070Sstevel@tonic-gate * 1080Sstevel@tonic-gate * Multicast joins operate on both the ilg and ilm structures. Multiple 1090Sstevel@tonic-gate * threads operating on an conn (socket) trying to do multicast joins 1108485SPeter.Memishian@Sun.COM * need to synchronize when operating on the ilg. Multiple threads 1110Sstevel@tonic-gate * potentially operating on different conn (socket endpoints) trying to 1120Sstevel@tonic-gate * do multicast joins could eventually end up trying to manipulate the 11311042SErik.Nordmark@Sun.COM * ilm simulatenously and need to synchronize on the access to the ilm. 11411042SErik.Nordmark@Sun.COM * The access and lookup of the ilm, as well as other ill multicast state, 11511042SErik.Nordmark@Sun.COM * is under ill_mcast_lock. 11611042SErik.Nordmark@Sun.COM * The modifications and lookup of ilg entries is serialized using conn_ilg_lock 11711042SErik.Nordmark@Sun.COM * rwlock. An ilg will not be freed until ilg_refcnt drops to zero. 11811042SErik.Nordmark@Sun.COM * 11911042SErik.Nordmark@Sun.COM * In some cases we hold ill_mcast_lock and then acquire conn_ilg_lock, but 12011042SErik.Nordmark@Sun.COM * never the other way around. 1210Sstevel@tonic-gate * 1220Sstevel@tonic-gate * An ilm is an IP data structure used to track multicast join/leave. 1230Sstevel@tonic-gate * An ilm is associated with a <multicast group, ipif> tuple in IPv4 and 1240Sstevel@tonic-gate * with just <multicast group> in IPv6. ilm_refcnt is the number of ilg's 12511042SErik.Nordmark@Sun.COM * referencing the ilm. 12611042SErik.Nordmark@Sun.COM * The modifications and lookup of ilm entries is serialized using the 12711042SErik.Nordmark@Sun.COM * ill_mcast_lock rwlock; that lock handles all the igmp/mld modifications 12811042SErik.Nordmark@Sun.COM * of the ilm state. 12911042SErik.Nordmark@Sun.COM * ilms are created / destroyed only as writer. ilms 13011042SErik.Nordmark@Sun.COM * are not passed around. The datapath (anything outside of this file 13111042SErik.Nordmark@Sun.COM * and igmp.c) use functions that do not return ilms - just the number 13211042SErik.Nordmark@Sun.COM * of members. So we don't need a dynamic refcount of the number 1330Sstevel@tonic-gate * of threads holding reference to an ilm. 1340Sstevel@tonic-gate * 13511042SErik.Nordmark@Sun.COM * In the cases where we serially access the ilg and ilm, which happens when 13611042SErik.Nordmark@Sun.COM * we handle the applications requests to join or leave groups and sources, 13711042SErik.Nordmark@Sun.COM * we use the ill_mcast_serializer mutex to ensure that a multithreaded 13811042SErik.Nordmark@Sun.COM * application which does concurrent joins and/or leaves on the same group on 13911042SErik.Nordmark@Sun.COM * the same socket always results in a consistent order for the ilg and ilm 14011042SErik.Nordmark@Sun.COM * modifications. 1410Sstevel@tonic-gate * 14211042SErik.Nordmark@Sun.COM * When a multicast operation results in needing to send a message to 14311042SErik.Nordmark@Sun.COM * the driver (to join/leave a L2 multicast address), we use ill_dlpi_queue() 14411042SErik.Nordmark@Sun.COM * which serialized the DLPI requests. The IGMP/MLD code uses ill_mcast_queue() 14511042SErik.Nordmark@Sun.COM * to send IGMP/MLD IP packet to avoid dropping the lock just to send a packet. 1460Sstevel@tonic-gate */ 1470Sstevel@tonic-gate 1480Sstevel@tonic-gate #define GETSTRUCT(structure, number) \ 1490Sstevel@tonic-gate ((structure *)mi_zalloc(sizeof (structure) * (number))) 1500Sstevel@tonic-gate 15111042SErik.Nordmark@Sun.COM /* 15211042SErik.Nordmark@Sun.COM * Caller must ensure that the ilg has not been condemned 15311042SErik.Nordmark@Sun.COM * The condemned flag is only set in ilg_delete under conn_ilg_lock. 15411042SErik.Nordmark@Sun.COM * 15511042SErik.Nordmark@Sun.COM * The caller must hold conn_ilg_lock as writer. 15611042SErik.Nordmark@Sun.COM */ 15711042SErik.Nordmark@Sun.COM static void 15811042SErik.Nordmark@Sun.COM ilg_refhold(ilg_t *ilg) 15911042SErik.Nordmark@Sun.COM { 16011042SErik.Nordmark@Sun.COM ASSERT(ilg->ilg_refcnt != 0); 16111042SErik.Nordmark@Sun.COM ASSERT(!ilg->ilg_condemned); 16211042SErik.Nordmark@Sun.COM ASSERT(RW_WRITE_HELD(&ilg->ilg_connp->conn_ilg_lock)); 16311042SErik.Nordmark@Sun.COM 16411042SErik.Nordmark@Sun.COM ilg->ilg_refcnt++; 16511042SErik.Nordmark@Sun.COM } 16611042SErik.Nordmark@Sun.COM 16711042SErik.Nordmark@Sun.COM static void 16811042SErik.Nordmark@Sun.COM ilg_inactive(ilg_t *ilg) 16911042SErik.Nordmark@Sun.COM { 17011042SErik.Nordmark@Sun.COM ASSERT(ilg->ilg_ill == NULL); 17111042SErik.Nordmark@Sun.COM ASSERT(ilg->ilg_ilm == NULL); 17211042SErik.Nordmark@Sun.COM ASSERT(ilg->ilg_filter == NULL); 17311042SErik.Nordmark@Sun.COM ASSERT(ilg->ilg_condemned); 17411042SErik.Nordmark@Sun.COM 17511042SErik.Nordmark@Sun.COM /* Unlink from list */ 17611042SErik.Nordmark@Sun.COM *ilg->ilg_ptpn = ilg->ilg_next; 17711042SErik.Nordmark@Sun.COM if (ilg->ilg_next != NULL) 17811042SErik.Nordmark@Sun.COM ilg->ilg_next->ilg_ptpn = ilg->ilg_ptpn; 17911042SErik.Nordmark@Sun.COM ilg->ilg_next = NULL; 18011042SErik.Nordmark@Sun.COM ilg->ilg_ptpn = NULL; 18111042SErik.Nordmark@Sun.COM 18211042SErik.Nordmark@Sun.COM ilg->ilg_connp = NULL; 18311042SErik.Nordmark@Sun.COM kmem_free(ilg, sizeof (*ilg)); 18411042SErik.Nordmark@Sun.COM } 1850Sstevel@tonic-gate 1860Sstevel@tonic-gate /* 18711042SErik.Nordmark@Sun.COM * The caller must hold conn_ilg_lock as writer. 18811042SErik.Nordmark@Sun.COM */ 18911042SErik.Nordmark@Sun.COM static void 19011042SErik.Nordmark@Sun.COM ilg_refrele(ilg_t *ilg) 19111042SErik.Nordmark@Sun.COM { 19211042SErik.Nordmark@Sun.COM ASSERT(RW_WRITE_HELD(&ilg->ilg_connp->conn_ilg_lock)); 19311042SErik.Nordmark@Sun.COM ASSERT(ilg->ilg_refcnt != 0); 19411042SErik.Nordmark@Sun.COM if (--ilg->ilg_refcnt == 0) 19511042SErik.Nordmark@Sun.COM ilg_inactive(ilg); 19611042SErik.Nordmark@Sun.COM } 19711042SErik.Nordmark@Sun.COM 19811042SErik.Nordmark@Sun.COM /* 19911042SErik.Nordmark@Sun.COM * Acquire reference on ilg and drop reference on held_ilg. 20011042SErik.Nordmark@Sun.COM * In the case when held_ilg is the same as ilg we already have 20111042SErik.Nordmark@Sun.COM * a reference, but the held_ilg might be condemned. In that case 20211042SErik.Nordmark@Sun.COM * we avoid the ilg_refhold/rele so that we can assert in ire_refhold 20311042SErik.Nordmark@Sun.COM * that the ilg isn't condemned. 20411042SErik.Nordmark@Sun.COM */ 20511042SErik.Nordmark@Sun.COM static void 20611042SErik.Nordmark@Sun.COM ilg_transfer_hold(ilg_t *held_ilg, ilg_t *ilg) 20711042SErik.Nordmark@Sun.COM { 20811042SErik.Nordmark@Sun.COM if (held_ilg == ilg) 20911042SErik.Nordmark@Sun.COM return; 21011042SErik.Nordmark@Sun.COM 21111042SErik.Nordmark@Sun.COM ilg_refhold(ilg); 21211042SErik.Nordmark@Sun.COM if (held_ilg != NULL) 21311042SErik.Nordmark@Sun.COM ilg_refrele(held_ilg); 21411042SErik.Nordmark@Sun.COM } 21511042SErik.Nordmark@Sun.COM 21611042SErik.Nordmark@Sun.COM /* 21711042SErik.Nordmark@Sun.COM * Allocate a new ilg_t and links it into conn_ilg. 21811042SErik.Nordmark@Sun.COM * Returns NULL on failure, in which case `*errp' will be 2198485SPeter.Memishian@Sun.COM * filled in with the reason. 2200Sstevel@tonic-gate * 22111042SErik.Nordmark@Sun.COM * Assumes connp->conn_ilg_lock is held. 2220Sstevel@tonic-gate */ 2230Sstevel@tonic-gate static ilg_t * 2248485SPeter.Memishian@Sun.COM conn_ilg_alloc(conn_t *connp, int *errp) 2250Sstevel@tonic-gate { 22611042SErik.Nordmark@Sun.COM ilg_t *ilg; 22711042SErik.Nordmark@Sun.COM 22811042SErik.Nordmark@Sun.COM ASSERT(RW_WRITE_HELD(&connp->conn_ilg_lock)); 2290Sstevel@tonic-gate 2308485SPeter.Memishian@Sun.COM /* 2318485SPeter.Memishian@Sun.COM * If CONN_CLOSING is set, conn_ilg cleanup has begun and we must not 2328485SPeter.Memishian@Sun.COM * create any ilgs. 2338485SPeter.Memishian@Sun.COM */ 2348485SPeter.Memishian@Sun.COM if (connp->conn_state_flags & CONN_CLOSING) { 2358485SPeter.Memishian@Sun.COM *errp = EINVAL; 2368485SPeter.Memishian@Sun.COM return (NULL); 2378485SPeter.Memishian@Sun.COM } 2388485SPeter.Memishian@Sun.COM 23911042SErik.Nordmark@Sun.COM ilg = kmem_zalloc(sizeof (ilg_t), KM_NOSLEEP); 24011042SErik.Nordmark@Sun.COM if (ilg == NULL) { 24111042SErik.Nordmark@Sun.COM *errp = ENOMEM; 24211042SErik.Nordmark@Sun.COM return (NULL); 2430Sstevel@tonic-gate } 24411042SErik.Nordmark@Sun.COM 24511042SErik.Nordmark@Sun.COM ilg->ilg_refcnt = 1; 24611042SErik.Nordmark@Sun.COM 24711042SErik.Nordmark@Sun.COM /* Insert at head */ 24811042SErik.Nordmark@Sun.COM if (connp->conn_ilg != NULL) 24911042SErik.Nordmark@Sun.COM connp->conn_ilg->ilg_ptpn = &ilg->ilg_next; 25011042SErik.Nordmark@Sun.COM ilg->ilg_next = connp->conn_ilg; 25111042SErik.Nordmark@Sun.COM ilg->ilg_ptpn = &connp->conn_ilg; 25211042SErik.Nordmark@Sun.COM connp->conn_ilg = ilg; 25311042SErik.Nordmark@Sun.COM 25411042SErik.Nordmark@Sun.COM ilg->ilg_connp = connp; 25511042SErik.Nordmark@Sun.COM return (ilg); 2560Sstevel@tonic-gate } 2570Sstevel@tonic-gate 2580Sstevel@tonic-gate typedef struct ilm_fbld_s { 2590Sstevel@tonic-gate ilm_t *fbld_ilm; 2600Sstevel@tonic-gate int fbld_in_cnt; 2610Sstevel@tonic-gate int fbld_ex_cnt; 2620Sstevel@tonic-gate slist_t fbld_in; 2630Sstevel@tonic-gate slist_t fbld_ex; 2640Sstevel@tonic-gate boolean_t fbld_in_overflow; 2650Sstevel@tonic-gate } ilm_fbld_t; 2660Sstevel@tonic-gate 26711042SErik.Nordmark@Sun.COM /* 26811042SErik.Nordmark@Sun.COM * Caller must hold ill_mcast_lock 26911042SErik.Nordmark@Sun.COM */ 2700Sstevel@tonic-gate static void 27111042SErik.Nordmark@Sun.COM ilm_bld_flists(conn_t *connp, void *arg) 2720Sstevel@tonic-gate { 27311042SErik.Nordmark@Sun.COM ilg_t *ilg; 2740Sstevel@tonic-gate ilm_fbld_t *fbld = (ilm_fbld_t *)(arg); 2750Sstevel@tonic-gate ilm_t *ilm = fbld->fbld_ilm; 2760Sstevel@tonic-gate in6_addr_t *v6group = &ilm->ilm_v6addr; 2770Sstevel@tonic-gate 27811042SErik.Nordmark@Sun.COM if (connp->conn_ilg == NULL) 2790Sstevel@tonic-gate return; 2800Sstevel@tonic-gate 2810Sstevel@tonic-gate /* 2820Sstevel@tonic-gate * Since we can't break out of the ipcl_walk once started, we still 2830Sstevel@tonic-gate * have to look at every conn. But if we've already found one 2840Sstevel@tonic-gate * (EXCLUDE, NULL) list, there's no need to keep checking individual 2850Sstevel@tonic-gate * ilgs--that will be our state. 2860Sstevel@tonic-gate */ 2870Sstevel@tonic-gate if (fbld->fbld_ex_cnt > 0 && fbld->fbld_ex.sl_numsrc == 0) 2880Sstevel@tonic-gate return; 2890Sstevel@tonic-gate 2900Sstevel@tonic-gate /* 2910Sstevel@tonic-gate * Check this conn's ilgs to see if any are interested in our 2920Sstevel@tonic-gate * ilm (group, interface match). If so, update the master 2930Sstevel@tonic-gate * include and exclude lists we're building in the fbld struct 2940Sstevel@tonic-gate * with this ilg's filter info. 29511042SErik.Nordmark@Sun.COM * 29611042SErik.Nordmark@Sun.COM * Note that the caller has already serialized on the ill we care 29711042SErik.Nordmark@Sun.COM * about. 2980Sstevel@tonic-gate */ 29911042SErik.Nordmark@Sun.COM ASSERT(MUTEX_HELD(&ilm->ilm_ill->ill_mcast_serializer)); 30011042SErik.Nordmark@Sun.COM 30111042SErik.Nordmark@Sun.COM rw_enter(&connp->conn_ilg_lock, RW_READER); 30211042SErik.Nordmark@Sun.COM for (ilg = connp->conn_ilg; ilg != NULL; ilg = ilg->ilg_next) { 30311042SErik.Nordmark@Sun.COM if (ilg->ilg_condemned) 30411042SErik.Nordmark@Sun.COM continue; 30511042SErik.Nordmark@Sun.COM 30611042SErik.Nordmark@Sun.COM /* 30711042SErik.Nordmark@Sun.COM * Since we are under the ill_mcast_serializer we know 30811042SErik.Nordmark@Sun.COM * that any ilg+ilm operations on this ilm have either 30911042SErik.Nordmark@Sun.COM * not started or completed, except for the last ilg 31011042SErik.Nordmark@Sun.COM * (the one that caused us to be called) which doesn't 31111042SErik.Nordmark@Sun.COM * have ilg_ilm set yet. Hence we compare using ilg_ill 31211042SErik.Nordmark@Sun.COM * and the address. 31311042SErik.Nordmark@Sun.COM */ 3140Sstevel@tonic-gate if ((ilg->ilg_ill == ilm->ilm_ill) && 3150Sstevel@tonic-gate IN6_ARE_ADDR_EQUAL(&ilg->ilg_v6group, v6group)) { 3160Sstevel@tonic-gate if (ilg->ilg_fmode == MODE_IS_INCLUDE) { 3170Sstevel@tonic-gate fbld->fbld_in_cnt++; 3180Sstevel@tonic-gate if (!fbld->fbld_in_overflow) 3190Sstevel@tonic-gate l_union_in_a(&fbld->fbld_in, 3200Sstevel@tonic-gate ilg->ilg_filter, 3210Sstevel@tonic-gate &fbld->fbld_in_overflow); 3220Sstevel@tonic-gate } else { 3230Sstevel@tonic-gate fbld->fbld_ex_cnt++; 3240Sstevel@tonic-gate /* 3250Sstevel@tonic-gate * On the first exclude list, don't try to do 3260Sstevel@tonic-gate * an intersection, as the master exclude list 3270Sstevel@tonic-gate * is intentionally empty. If the master list 3280Sstevel@tonic-gate * is still empty on later iterations, that 3290Sstevel@tonic-gate * means we have at least one ilg with an empty 3300Sstevel@tonic-gate * exclude list, so that should be reflected 3310Sstevel@tonic-gate * when we take the intersection. 3320Sstevel@tonic-gate */ 3330Sstevel@tonic-gate if (fbld->fbld_ex_cnt == 1) { 3340Sstevel@tonic-gate if (ilg->ilg_filter != NULL) 3350Sstevel@tonic-gate l_copy(ilg->ilg_filter, 3360Sstevel@tonic-gate &fbld->fbld_ex); 3370Sstevel@tonic-gate } else { 3380Sstevel@tonic-gate l_intersection_in_a(&fbld->fbld_ex, 3390Sstevel@tonic-gate ilg->ilg_filter); 3400Sstevel@tonic-gate } 3410Sstevel@tonic-gate } 3420Sstevel@tonic-gate /* there will only be one match, so break now. */ 3430Sstevel@tonic-gate break; 3440Sstevel@tonic-gate } 3450Sstevel@tonic-gate } 34611042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 3470Sstevel@tonic-gate } 3480Sstevel@tonic-gate 34911042SErik.Nordmark@Sun.COM /* 35011042SErik.Nordmark@Sun.COM * Caller must hold ill_mcast_lock 35111042SErik.Nordmark@Sun.COM */ 3520Sstevel@tonic-gate static void 3530Sstevel@tonic-gate ilm_gen_filter(ilm_t *ilm, mcast_record_t *fmode, slist_t *flist) 3540Sstevel@tonic-gate { 3550Sstevel@tonic-gate ilm_fbld_t fbld; 3563448Sdh155122 ip_stack_t *ipst = ilm->ilm_ipst; 3570Sstevel@tonic-gate 3580Sstevel@tonic-gate fbld.fbld_ilm = ilm; 3590Sstevel@tonic-gate fbld.fbld_in_cnt = fbld.fbld_ex_cnt = 0; 3600Sstevel@tonic-gate fbld.fbld_in.sl_numsrc = fbld.fbld_ex.sl_numsrc = 0; 3610Sstevel@tonic-gate fbld.fbld_in_overflow = B_FALSE; 3620Sstevel@tonic-gate 3630Sstevel@tonic-gate /* first, construct our master include and exclude lists */ 3643448Sdh155122 ipcl_walk(ilm_bld_flists, (caddr_t)&fbld, ipst); 3650Sstevel@tonic-gate 3660Sstevel@tonic-gate /* now use those master lists to generate the interface filter */ 3670Sstevel@tonic-gate 3680Sstevel@tonic-gate /* if include list overflowed, filter is (EXCLUDE, NULL) */ 3690Sstevel@tonic-gate if (fbld.fbld_in_overflow) { 3700Sstevel@tonic-gate *fmode = MODE_IS_EXCLUDE; 3710Sstevel@tonic-gate flist->sl_numsrc = 0; 3720Sstevel@tonic-gate return; 3730Sstevel@tonic-gate } 3740Sstevel@tonic-gate 3750Sstevel@tonic-gate /* if nobody interested, interface filter is (INCLUDE, NULL) */ 3760Sstevel@tonic-gate if (fbld.fbld_in_cnt == 0 && fbld.fbld_ex_cnt == 0) { 3770Sstevel@tonic-gate *fmode = MODE_IS_INCLUDE; 3780Sstevel@tonic-gate flist->sl_numsrc = 0; 3790Sstevel@tonic-gate return; 3800Sstevel@tonic-gate } 3810Sstevel@tonic-gate 3820Sstevel@tonic-gate /* 3830Sstevel@tonic-gate * If there are no exclude lists, then the interface filter 3840Sstevel@tonic-gate * is INCLUDE, with its filter list equal to fbld_in. A single 3850Sstevel@tonic-gate * exclude list makes the interface filter EXCLUDE, with its 3860Sstevel@tonic-gate * filter list equal to (fbld_ex - fbld_in). 3870Sstevel@tonic-gate */ 3880Sstevel@tonic-gate if (fbld.fbld_ex_cnt == 0) { 3890Sstevel@tonic-gate *fmode = MODE_IS_INCLUDE; 3900Sstevel@tonic-gate l_copy(&fbld.fbld_in, flist); 3910Sstevel@tonic-gate } else { 3920Sstevel@tonic-gate *fmode = MODE_IS_EXCLUDE; 3930Sstevel@tonic-gate l_difference(&fbld.fbld_ex, &fbld.fbld_in, flist); 3940Sstevel@tonic-gate } 3950Sstevel@tonic-gate } 3960Sstevel@tonic-gate 39711042SErik.Nordmark@Sun.COM /* 39811042SErik.Nordmark@Sun.COM * Caller must hold ill_mcast_lock 39911042SErik.Nordmark@Sun.COM */ 4000Sstevel@tonic-gate static int 40111042SErik.Nordmark@Sun.COM ilm_update_add(ilm_t *ilm, ilg_stat_t ilgstat, slist_t *ilg_flist) 4020Sstevel@tonic-gate { 4030Sstevel@tonic-gate mcast_record_t fmode; 4040Sstevel@tonic-gate slist_t *flist; 4050Sstevel@tonic-gate boolean_t fdefault; 4060Sstevel@tonic-gate char buf[INET6_ADDRSTRLEN]; 40711042SErik.Nordmark@Sun.COM ill_t *ill = ilm->ilm_ill; 4080Sstevel@tonic-gate 4090Sstevel@tonic-gate /* 4100Sstevel@tonic-gate * There are several cases where the ilm's filter state 4110Sstevel@tonic-gate * defaults to (EXCLUDE, NULL): 4120Sstevel@tonic-gate * - we've had previous joins without associated ilgs 4130Sstevel@tonic-gate * - this join has no associated ilg 4140Sstevel@tonic-gate * - the ilg's filter state is (EXCLUDE, NULL) 4150Sstevel@tonic-gate */ 4160Sstevel@tonic-gate fdefault = (ilm->ilm_no_ilg_cnt > 0) || 4170Sstevel@tonic-gate (ilgstat == ILGSTAT_NONE) || SLIST_IS_EMPTY(ilg_flist); 4180Sstevel@tonic-gate 4190Sstevel@tonic-gate /* attempt mallocs (if needed) before doing anything else */ 4200Sstevel@tonic-gate if ((flist = l_alloc()) == NULL) 4210Sstevel@tonic-gate return (ENOMEM); 4220Sstevel@tonic-gate if (!fdefault && ilm->ilm_filter == NULL) { 4230Sstevel@tonic-gate ilm->ilm_filter = l_alloc(); 4240Sstevel@tonic-gate if (ilm->ilm_filter == NULL) { 4250Sstevel@tonic-gate l_free(flist); 4260Sstevel@tonic-gate return (ENOMEM); 4270Sstevel@tonic-gate } 4280Sstevel@tonic-gate } 4290Sstevel@tonic-gate 4300Sstevel@tonic-gate if (ilgstat != ILGSTAT_CHANGE) 4310Sstevel@tonic-gate ilm->ilm_refcnt++; 4320Sstevel@tonic-gate 4330Sstevel@tonic-gate if (ilgstat == ILGSTAT_NONE) 4340Sstevel@tonic-gate ilm->ilm_no_ilg_cnt++; 4350Sstevel@tonic-gate 4360Sstevel@tonic-gate /* 4370Sstevel@tonic-gate * Determine new filter state. If it's not the default 4380Sstevel@tonic-gate * (EXCLUDE, NULL), we must walk the conn list to find 4390Sstevel@tonic-gate * any ilgs interested in this group, and re-build the 4400Sstevel@tonic-gate * ilm filter. 4410Sstevel@tonic-gate */ 4420Sstevel@tonic-gate if (fdefault) { 4430Sstevel@tonic-gate fmode = MODE_IS_EXCLUDE; 4440Sstevel@tonic-gate flist->sl_numsrc = 0; 4450Sstevel@tonic-gate } else { 4460Sstevel@tonic-gate ilm_gen_filter(ilm, &fmode, flist); 4470Sstevel@tonic-gate } 4480Sstevel@tonic-gate 4490Sstevel@tonic-gate /* make sure state actually changed; nothing to do if not. */ 4500Sstevel@tonic-gate if ((ilm->ilm_fmode == fmode) && 4510Sstevel@tonic-gate !lists_are_different(ilm->ilm_filter, flist)) { 4520Sstevel@tonic-gate l_free(flist); 4530Sstevel@tonic-gate return (0); 4540Sstevel@tonic-gate } 4550Sstevel@tonic-gate 4560Sstevel@tonic-gate /* send the state change report */ 4574459Skcpoon if (!IS_LOOPBACK(ill)) { 45811042SErik.Nordmark@Sun.COM if (ill->ill_isv6) 4590Sstevel@tonic-gate mld_statechange(ilm, fmode, flist); 4600Sstevel@tonic-gate else 4610Sstevel@tonic-gate igmp_statechange(ilm, fmode, flist); 4620Sstevel@tonic-gate } 4630Sstevel@tonic-gate 4640Sstevel@tonic-gate /* update the ilm state */ 4650Sstevel@tonic-gate ilm->ilm_fmode = fmode; 4660Sstevel@tonic-gate if (flist->sl_numsrc > 0) 4670Sstevel@tonic-gate l_copy(flist, ilm->ilm_filter); 4680Sstevel@tonic-gate else 4690Sstevel@tonic-gate CLEAR_SLIST(ilm->ilm_filter); 4700Sstevel@tonic-gate 4710Sstevel@tonic-gate ip1dbg(("ilm_update: new if filter mode %d, group %s\n", ilm->ilm_fmode, 4720Sstevel@tonic-gate inet_ntop(AF_INET6, &ilm->ilm_v6addr, buf, sizeof (buf)))); 4730Sstevel@tonic-gate 4740Sstevel@tonic-gate l_free(flist); 4750Sstevel@tonic-gate return (0); 4760Sstevel@tonic-gate } 4770Sstevel@tonic-gate 47811042SErik.Nordmark@Sun.COM /* 47911042SErik.Nordmark@Sun.COM * Caller must hold ill_mcast_lock 48011042SErik.Nordmark@Sun.COM */ 4810Sstevel@tonic-gate static int 48211042SErik.Nordmark@Sun.COM ilm_update_del(ilm_t *ilm) 4830Sstevel@tonic-gate { 4840Sstevel@tonic-gate mcast_record_t fmode; 4850Sstevel@tonic-gate slist_t *flist; 48611042SErik.Nordmark@Sun.COM ill_t *ill = ilm->ilm_ill; 4870Sstevel@tonic-gate 4880Sstevel@tonic-gate ip1dbg(("ilm_update_del: still %d left; updating state\n", 4890Sstevel@tonic-gate ilm->ilm_refcnt)); 4900Sstevel@tonic-gate 4910Sstevel@tonic-gate if ((flist = l_alloc()) == NULL) 4920Sstevel@tonic-gate return (ENOMEM); 4930Sstevel@tonic-gate 4940Sstevel@tonic-gate /* 4950Sstevel@tonic-gate * If present, the ilg in question has already either been 4960Sstevel@tonic-gate * updated or removed from our list; so all we need to do 4970Sstevel@tonic-gate * now is walk the list to update the ilm filter state. 4980Sstevel@tonic-gate * 4990Sstevel@tonic-gate * Skip the list walk if we have any no-ilg joins, which 5000Sstevel@tonic-gate * cause the filter state to revert to (EXCLUDE, NULL). 5010Sstevel@tonic-gate */ 5020Sstevel@tonic-gate if (ilm->ilm_no_ilg_cnt != 0) { 5030Sstevel@tonic-gate fmode = MODE_IS_EXCLUDE; 5040Sstevel@tonic-gate flist->sl_numsrc = 0; 5050Sstevel@tonic-gate } else { 5060Sstevel@tonic-gate ilm_gen_filter(ilm, &fmode, flist); 5070Sstevel@tonic-gate } 5080Sstevel@tonic-gate 5090Sstevel@tonic-gate /* check to see if state needs to be updated */ 5100Sstevel@tonic-gate if ((ilm->ilm_fmode == fmode) && 5110Sstevel@tonic-gate (!lists_are_different(ilm->ilm_filter, flist))) { 5120Sstevel@tonic-gate l_free(flist); 5130Sstevel@tonic-gate return (0); 5140Sstevel@tonic-gate } 5150Sstevel@tonic-gate 5164459Skcpoon if (!IS_LOOPBACK(ill)) { 51711042SErik.Nordmark@Sun.COM if (ill->ill_isv6) 5180Sstevel@tonic-gate mld_statechange(ilm, fmode, flist); 5190Sstevel@tonic-gate else 5200Sstevel@tonic-gate igmp_statechange(ilm, fmode, flist); 5210Sstevel@tonic-gate } 5220Sstevel@tonic-gate 5230Sstevel@tonic-gate ilm->ilm_fmode = fmode; 5240Sstevel@tonic-gate if (flist->sl_numsrc > 0) { 5250Sstevel@tonic-gate if (ilm->ilm_filter == NULL) { 5260Sstevel@tonic-gate ilm->ilm_filter = l_alloc(); 5270Sstevel@tonic-gate if (ilm->ilm_filter == NULL) { 5280Sstevel@tonic-gate char buf[INET6_ADDRSTRLEN]; 5290Sstevel@tonic-gate ip1dbg(("ilm_update_del: failed to alloc ilm " 5300Sstevel@tonic-gate "filter; no source filtering for %s on %s", 5310Sstevel@tonic-gate inet_ntop(AF_INET6, &ilm->ilm_v6addr, 5320Sstevel@tonic-gate buf, sizeof (buf)), ill->ill_name)); 5330Sstevel@tonic-gate ilm->ilm_fmode = MODE_IS_EXCLUDE; 5340Sstevel@tonic-gate l_free(flist); 5350Sstevel@tonic-gate return (0); 5360Sstevel@tonic-gate } 5370Sstevel@tonic-gate } 5380Sstevel@tonic-gate l_copy(flist, ilm->ilm_filter); 5390Sstevel@tonic-gate } else { 5400Sstevel@tonic-gate CLEAR_SLIST(ilm->ilm_filter); 5410Sstevel@tonic-gate } 5420Sstevel@tonic-gate 5430Sstevel@tonic-gate l_free(flist); 5440Sstevel@tonic-gate return (0); 5450Sstevel@tonic-gate } 5460Sstevel@tonic-gate 5470Sstevel@tonic-gate /* 54811042SErik.Nordmark@Sun.COM * Create/update the ilm for the group/ill. Used by other parts of IP to 54911042SErik.Nordmark@Sun.COM * do the ILGSTAT_NONE (no ilg), MODE_IS_EXCLUDE, with no slist join. 55011042SErik.Nordmark@Sun.COM * Returns with a refhold on the ilm. 55111042SErik.Nordmark@Sun.COM * 55211042SErik.Nordmark@Sun.COM * The unspecified address means all multicast addresses for in both the 55311042SErik.Nordmark@Sun.COM * case of IPv4 and IPv6. 55411042SErik.Nordmark@Sun.COM * 55511042SErik.Nordmark@Sun.COM * The caller should have already mapped an IPMP under ill to the upper. 5560Sstevel@tonic-gate */ 55711042SErik.Nordmark@Sun.COM ilm_t * 55811042SErik.Nordmark@Sun.COM ip_addmulti(const in6_addr_t *v6group, ill_t *ill, zoneid_t zoneid, 55911042SErik.Nordmark@Sun.COM int *errorp) 5600Sstevel@tonic-gate { 56111042SErik.Nordmark@Sun.COM ilm_t *ilm; 56211042SErik.Nordmark@Sun.COM 56311042SErik.Nordmark@Sun.COM /* Acquire serializer to keep assert in ilm_bld_flists happy */ 56411042SErik.Nordmark@Sun.COM mutex_enter(&ill->ill_mcast_serializer); 56511042SErik.Nordmark@Sun.COM ilm = ip_addmulti_serial(v6group, ill, zoneid, ILGSTAT_NONE, 56611042SErik.Nordmark@Sun.COM MODE_IS_EXCLUDE, NULL, errorp); 56711042SErik.Nordmark@Sun.COM mutex_exit(&ill->ill_mcast_serializer); 568*11463SSowmini.Varadhan@Sun.COM /* 569*11463SSowmini.Varadhan@Sun.COM * Now that all locks have been dropped, we can send any 570*11463SSowmini.Varadhan@Sun.COM * deferred/queued DLPI or IP packets 571*11463SSowmini.Varadhan@Sun.COM */ 572*11463SSowmini.Varadhan@Sun.COM ill_mcast_send_queued(ill); 573*11463SSowmini.Varadhan@Sun.COM ill_dlpi_send_queued(ill); 57411042SErik.Nordmark@Sun.COM return (ilm); 5750Sstevel@tonic-gate } 5760Sstevel@tonic-gate 5770Sstevel@tonic-gate /* 57811042SErik.Nordmark@Sun.COM * Create/update the ilm for the group/ill. If ILGSTAT_CHANGE is not set 57911042SErik.Nordmark@Sun.COM * then this returns with a refhold on the ilm. 5800Sstevel@tonic-gate * 58111042SErik.Nordmark@Sun.COM * Internal routine which assumes the caller has already acquired 582*11463SSowmini.Varadhan@Sun.COM * ill_mcast_serializer. It is the caller's responsibility to send out 583*11463SSowmini.Varadhan@Sun.COM * queued DLPI/multicast packets after all locks are dropped. 58411042SErik.Nordmark@Sun.COM * 58511042SErik.Nordmark@Sun.COM * The unspecified address means all multicast addresses for in both the 58611042SErik.Nordmark@Sun.COM * case of IPv4 and IPv6. 5870Sstevel@tonic-gate * 5880Sstevel@tonic-gate * ilgstat tells us if there's an ilg associated with this join, 5890Sstevel@tonic-gate * and if so, if it's a new ilg or a change to an existing one. 5900Sstevel@tonic-gate * ilg_fmode and ilg_flist give us the current filter state of 5910Sstevel@tonic-gate * the ilg (and will be EXCLUDE {NULL} in the case of no ilg). 59211042SErik.Nordmark@Sun.COM * 59311042SErik.Nordmark@Sun.COM * The caller should have already mapped an IPMP under ill to the upper. 5940Sstevel@tonic-gate */ 59511042SErik.Nordmark@Sun.COM static ilm_t * 59611042SErik.Nordmark@Sun.COM ip_addmulti_serial(const in6_addr_t *v6group, ill_t *ill, zoneid_t zoneid, 59711042SErik.Nordmark@Sun.COM ilg_stat_t ilgstat, mcast_record_t ilg_fmode, slist_t *ilg_flist, 59811042SErik.Nordmark@Sun.COM int *errorp) 59911042SErik.Nordmark@Sun.COM { 60011042SErik.Nordmark@Sun.COM ilm_t *ilm; 60111042SErik.Nordmark@Sun.COM 60211042SErik.Nordmark@Sun.COM ASSERT(MUTEX_HELD(&ill->ill_mcast_serializer)); 60311042SErik.Nordmark@Sun.COM 60411042SErik.Nordmark@Sun.COM if (ill->ill_isv6) { 60511042SErik.Nordmark@Sun.COM if (!IN6_IS_ADDR_MULTICAST(v6group) && 60611042SErik.Nordmark@Sun.COM !IN6_IS_ADDR_UNSPECIFIED(v6group)) { 60711042SErik.Nordmark@Sun.COM *errorp = EINVAL; 60811042SErik.Nordmark@Sun.COM return (NULL); 60911042SErik.Nordmark@Sun.COM } 61011042SErik.Nordmark@Sun.COM } else { 61111042SErik.Nordmark@Sun.COM if (IN6_IS_ADDR_V4MAPPED(v6group)) { 61211042SErik.Nordmark@Sun.COM ipaddr_t v4group; 61311042SErik.Nordmark@Sun.COM 61411042SErik.Nordmark@Sun.COM IN6_V4MAPPED_TO_IPADDR(v6group, v4group); 615*11463SSowmini.Varadhan@Sun.COM ASSERT(!IS_UNDER_IPMP(ill)); 61611042SErik.Nordmark@Sun.COM if (!CLASSD(v4group)) { 61711042SErik.Nordmark@Sun.COM *errorp = EINVAL; 61811042SErik.Nordmark@Sun.COM return (NULL); 61911042SErik.Nordmark@Sun.COM } 62011042SErik.Nordmark@Sun.COM } else if (!IN6_IS_ADDR_UNSPECIFIED(v6group)) { 62111042SErik.Nordmark@Sun.COM *errorp = EINVAL; 62211042SErik.Nordmark@Sun.COM return (NULL); 62311042SErik.Nordmark@Sun.COM } 62411042SErik.Nordmark@Sun.COM } 62511042SErik.Nordmark@Sun.COM 62611042SErik.Nordmark@Sun.COM if (IS_UNDER_IPMP(ill)) { 62711042SErik.Nordmark@Sun.COM *errorp = EINVAL; 62811042SErik.Nordmark@Sun.COM return (NULL); 62911042SErik.Nordmark@Sun.COM } 63011042SErik.Nordmark@Sun.COM 63111042SErik.Nordmark@Sun.COM rw_enter(&ill->ill_mcast_lock, RW_WRITER); 63211042SErik.Nordmark@Sun.COM /* 63311042SErik.Nordmark@Sun.COM * We do the equivalent of a lookup by checking after we get the lock 63411042SErik.Nordmark@Sun.COM * This is needed since the ill could have been condemned after 63511042SErik.Nordmark@Sun.COM * we looked it up, and we need to check condemned after we hold 63611042SErik.Nordmark@Sun.COM * ill_mcast_lock to synchronize with the unplumb code. 63711042SErik.Nordmark@Sun.COM */ 63811042SErik.Nordmark@Sun.COM if (ill->ill_state_flags & ILL_CONDEMNED) { 63911042SErik.Nordmark@Sun.COM rw_exit(&ill->ill_mcast_lock); 64011042SErik.Nordmark@Sun.COM *errorp = ENXIO; 64111042SErik.Nordmark@Sun.COM return (NULL); 64211042SErik.Nordmark@Sun.COM } 64311042SErik.Nordmark@Sun.COM ilm = ip_addmulti_impl(v6group, ill, zoneid, ilgstat, ilg_fmode, 64411042SErik.Nordmark@Sun.COM ilg_flist, errorp); 64511042SErik.Nordmark@Sun.COM rw_exit(&ill->ill_mcast_lock); 64611042SErik.Nordmark@Sun.COM 64711042SErik.Nordmark@Sun.COM ill_mcast_timer_start(ill->ill_ipst); 64811042SErik.Nordmark@Sun.COM return (ilm); 64911042SErik.Nordmark@Sun.COM } 65011042SErik.Nordmark@Sun.COM 65111042SErik.Nordmark@Sun.COM static ilm_t * 65211042SErik.Nordmark@Sun.COM ip_addmulti_impl(const in6_addr_t *v6group, ill_t *ill, zoneid_t zoneid, 65311042SErik.Nordmark@Sun.COM ilg_stat_t ilgstat, mcast_record_t ilg_fmode, slist_t *ilg_flist, 65411042SErik.Nordmark@Sun.COM int *errorp) 6550Sstevel@tonic-gate { 6560Sstevel@tonic-gate ilm_t *ilm; 65711042SErik.Nordmark@Sun.COM int ret = 0; 65811042SErik.Nordmark@Sun.COM 65911042SErik.Nordmark@Sun.COM ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock)); 66011042SErik.Nordmark@Sun.COM *errorp = 0; 6618485SPeter.Memishian@Sun.COM 6620Sstevel@tonic-gate /* 6638485SPeter.Memishian@Sun.COM * An ilm is uniquely identified by the tuple of (group, ill) where 6648485SPeter.Memishian@Sun.COM * `group' is the multicast group address, and `ill' is the interface 6658485SPeter.Memishian@Sun.COM * on which it is currently joined. 6660Sstevel@tonic-gate */ 66711042SErik.Nordmark@Sun.COM 66811042SErik.Nordmark@Sun.COM ilm = ilm_lookup(ill, v6group, zoneid); 66911042SErik.Nordmark@Sun.COM if (ilm != NULL) { 67011042SErik.Nordmark@Sun.COM /* ilm_update_add bumps ilm_refcnt unless ILGSTAT_CHANGE */ 67111042SErik.Nordmark@Sun.COM ret = ilm_update_add(ilm, ilgstat, ilg_flist); 67211042SErik.Nordmark@Sun.COM if (ret == 0) 67311042SErik.Nordmark@Sun.COM return (ilm); 67411042SErik.Nordmark@Sun.COM 67511042SErik.Nordmark@Sun.COM *errorp = ret; 67611042SErik.Nordmark@Sun.COM return (NULL); 67711042SErik.Nordmark@Sun.COM } 67811042SErik.Nordmark@Sun.COM 67911042SErik.Nordmark@Sun.COM /* 68011042SErik.Nordmark@Sun.COM * The callers checks on the ilg and the ilg+ilm consistency under 68111042SErik.Nordmark@Sun.COM * ill_mcast_serializer ensures that we can not have ILGSTAT_CHANGE 68211042SErik.Nordmark@Sun.COM * and no ilm. 68311042SErik.Nordmark@Sun.COM */ 68411042SErik.Nordmark@Sun.COM ASSERT(ilgstat != ILGSTAT_CHANGE); 68511042SErik.Nordmark@Sun.COM ilm = ilm_add(ill, v6group, ilgstat, ilg_fmode, ilg_flist, zoneid); 68611042SErik.Nordmark@Sun.COM if (ilm == NULL) { 68711042SErik.Nordmark@Sun.COM *errorp = ENOMEM; 68811042SErik.Nordmark@Sun.COM return (NULL); 68911042SErik.Nordmark@Sun.COM } 6900Sstevel@tonic-gate 6910Sstevel@tonic-gate if (IN6_IS_ADDR_UNSPECIFIED(v6group)) { 6920Sstevel@tonic-gate /* 69311042SErik.Nordmark@Sun.COM * If we have more then one we should not tell the driver 69411042SErik.Nordmark@Sun.COM * to join this time. 6950Sstevel@tonic-gate */ 69611042SErik.Nordmark@Sun.COM if (ilm_numentries(ill, v6group) == 1) { 69711042SErik.Nordmark@Sun.COM ret = ill_join_allmulti(ill); 69811042SErik.Nordmark@Sun.COM } 69911042SErik.Nordmark@Sun.COM } else { 70011042SErik.Nordmark@Sun.COM if (!IS_LOOPBACK(ill)) { 70111042SErik.Nordmark@Sun.COM if (ill->ill_isv6) 70211042SErik.Nordmark@Sun.COM mld_joingroup(ilm); 70311042SErik.Nordmark@Sun.COM else 70411042SErik.Nordmark@Sun.COM igmp_joingroup(ilm); 70511042SErik.Nordmark@Sun.COM } 70611042SErik.Nordmark@Sun.COM 70711042SErik.Nordmark@Sun.COM /* 70811042SErik.Nordmark@Sun.COM * If we have more then one we should not tell the driver 70911042SErik.Nordmark@Sun.COM * to join this time. 71011042SErik.Nordmark@Sun.COM */ 71111042SErik.Nordmark@Sun.COM if (ilm_numentries(ill, v6group) == 1) { 71211042SErik.Nordmark@Sun.COM ret = ip_ll_multireq(ill, v6group, DL_ENABMULTI_REQ); 71311042SErik.Nordmark@Sun.COM } 7140Sstevel@tonic-gate } 71511042SErik.Nordmark@Sun.COM if (ret != 0) { 71611042SErik.Nordmark@Sun.COM if (ret == ENETDOWN) { 71711042SErik.Nordmark@Sun.COM char buf[INET6_ADDRSTRLEN]; 71811042SErik.Nordmark@Sun.COM 71911042SErik.Nordmark@Sun.COM ip0dbg(("ip_addmulti: ENETDOWN for %s on %s", 72011042SErik.Nordmark@Sun.COM inet_ntop(AF_INET6, &ilm->ilm_v6addr, 72111042SErik.Nordmark@Sun.COM buf, sizeof (buf)), ill->ill_name)); 72211042SErik.Nordmark@Sun.COM } 7230Sstevel@tonic-gate ilm_delete(ilm); 72411042SErik.Nordmark@Sun.COM *errorp = ret; 72511042SErik.Nordmark@Sun.COM return (NULL); 72611042SErik.Nordmark@Sun.COM } else { 72711042SErik.Nordmark@Sun.COM return (ilm); 72811042SErik.Nordmark@Sun.COM } 7290Sstevel@tonic-gate } 7300Sstevel@tonic-gate 7310Sstevel@tonic-gate /* 73211042SErik.Nordmark@Sun.COM * Send a multicast request to the driver for enabling or disabling 73311042SErik.Nordmark@Sun.COM * multicast reception for v6groupp address. The caller has already 73411042SErik.Nordmark@Sun.COM * checked whether it is appropriate to send one or not. 73511042SErik.Nordmark@Sun.COM * 73611042SErik.Nordmark@Sun.COM * For IPMP we switch to the cast_ill since it has the right hardware 73711042SErik.Nordmark@Sun.COM * information. 7389073SCathy.Zhou@Sun.COM */ 73911042SErik.Nordmark@Sun.COM static int 74011042SErik.Nordmark@Sun.COM ip_ll_send_multireq(ill_t *ill, const in6_addr_t *v6groupp, t_uscalar_t prim) 7410Sstevel@tonic-gate { 7420Sstevel@tonic-gate mblk_t *mp; 7430Sstevel@tonic-gate uint32_t addrlen, addroff; 74411042SErik.Nordmark@Sun.COM ill_t *release_ill = NULL; 74511042SErik.Nordmark@Sun.COM int err = 0; 74611042SErik.Nordmark@Sun.COM 74711042SErik.Nordmark@Sun.COM ASSERT(RW_LOCK_HELD(&ill->ill_mcast_lock)); 74811042SErik.Nordmark@Sun.COM 74911042SErik.Nordmark@Sun.COM if (IS_IPMP(ill)) { 75011042SErik.Nordmark@Sun.COM /* On the upper IPMP ill. */ 75111042SErik.Nordmark@Sun.COM release_ill = ipmp_illgrp_hold_cast_ill(ill->ill_grp); 75211042SErik.Nordmark@Sun.COM if (release_ill == NULL) { 75311042SErik.Nordmark@Sun.COM /* 75411042SErik.Nordmark@Sun.COM * Avoid sending it down to the ipmpstub. 75511042SErik.Nordmark@Sun.COM * We will be called again once the members of the 75611042SErik.Nordmark@Sun.COM * group are in place 75711042SErik.Nordmark@Sun.COM */ 75811042SErik.Nordmark@Sun.COM ip1dbg(("ip_ll_send_multireq: no cast_ill for %s %d\n", 75911042SErik.Nordmark@Sun.COM ill->ill_name, ill->ill_isv6)); 76011042SErik.Nordmark@Sun.COM return (0); 76111042SErik.Nordmark@Sun.COM } 76211042SErik.Nordmark@Sun.COM ill = release_ill; 76311042SErik.Nordmark@Sun.COM } 76411042SErik.Nordmark@Sun.COM /* Create a DL_ENABMULTI_REQ or DL_DISABMULTI_REQ message. */ 76511042SErik.Nordmark@Sun.COM mp = ill_create_dl(ill, prim, &addrlen, &addroff); 76611042SErik.Nordmark@Sun.COM if (mp == NULL) { 76711042SErik.Nordmark@Sun.COM err = ENOMEM; 76811042SErik.Nordmark@Sun.COM goto done; 76911042SErik.Nordmark@Sun.COM } 77011042SErik.Nordmark@Sun.COM 77111042SErik.Nordmark@Sun.COM mp = ndp_mcastreq(ill, v6groupp, addrlen, addroff, mp); 77211042SErik.Nordmark@Sun.COM if (mp == NULL) { 77311042SErik.Nordmark@Sun.COM ip0dbg(("null from ndp_mcastreq(ill %s)\n", ill->ill_name)); 77411042SErik.Nordmark@Sun.COM err = ENOMEM; 77511042SErik.Nordmark@Sun.COM goto done; 77611042SErik.Nordmark@Sun.COM } 77711042SErik.Nordmark@Sun.COM 77811042SErik.Nordmark@Sun.COM switch (((union DL_primitives *)mp->b_rptr)->dl_primitive) { 77911042SErik.Nordmark@Sun.COM case DL_ENABMULTI_REQ: 78011042SErik.Nordmark@Sun.COM mutex_enter(&ill->ill_lock); 7819073SCathy.Zhou@Sun.COM /* Track the state if this is the first enabmulti */ 7829073SCathy.Zhou@Sun.COM if (ill->ill_dlpi_multicast_state == IDS_UNKNOWN) 7839073SCathy.Zhou@Sun.COM ill->ill_dlpi_multicast_state = IDS_INPROGRESS; 78411042SErik.Nordmark@Sun.COM mutex_exit(&ill->ill_lock); 78511042SErik.Nordmark@Sun.COM break; 7860Sstevel@tonic-gate } 78711042SErik.Nordmark@Sun.COM ill_dlpi_queue(ill, mp); 78811042SErik.Nordmark@Sun.COM done: 78911042SErik.Nordmark@Sun.COM if (release_ill != NULL) 79011042SErik.Nordmark@Sun.COM ill_refrele(release_ill); 79111042SErik.Nordmark@Sun.COM return (err); 7920Sstevel@tonic-gate } 7930Sstevel@tonic-gate 7940Sstevel@tonic-gate /* 7950Sstevel@tonic-gate * Send a multicast request to the driver for enabling multicast 7960Sstevel@tonic-gate * membership for v6group if appropriate. 7970Sstevel@tonic-gate */ 7980Sstevel@tonic-gate static int 79911042SErik.Nordmark@Sun.COM ip_ll_multireq(ill_t *ill, const in6_addr_t *v6groupp, t_uscalar_t prim) 8000Sstevel@tonic-gate { 8010Sstevel@tonic-gate if (ill->ill_net_type != IRE_IF_RESOLVER || 80211042SErik.Nordmark@Sun.COM ill->ill_ipif->ipif_flags & IPIF_POINTOPOINT) { 80311042SErik.Nordmark@Sun.COM ip1dbg(("ip_ll_multireq: not resolver\n")); 8040Sstevel@tonic-gate return (0); /* Must be IRE_IF_NORESOLVER */ 8050Sstevel@tonic-gate } 8060Sstevel@tonic-gate 8070Sstevel@tonic-gate if (ill->ill_phyint->phyint_flags & PHYI_MULTI_BCAST) { 80811042SErik.Nordmark@Sun.COM ip1dbg(("ip_ll_multireq: MULTI_BCAST\n")); 8090Sstevel@tonic-gate return (0); 8100Sstevel@tonic-gate } 81111042SErik.Nordmark@Sun.COM return (ip_ll_send_multireq(ill, v6groupp, prim)); 8120Sstevel@tonic-gate } 8130Sstevel@tonic-gate 8140Sstevel@tonic-gate /* 81511042SErik.Nordmark@Sun.COM * Delete the ilm. Used by other parts of IP for the case of no_ilg/leaving 81611042SErik.Nordmark@Sun.COM * being true. 8170Sstevel@tonic-gate */ 8180Sstevel@tonic-gate int 81911042SErik.Nordmark@Sun.COM ip_delmulti(ilm_t *ilm) 82011042SErik.Nordmark@Sun.COM { 82111042SErik.Nordmark@Sun.COM ill_t *ill = ilm->ilm_ill; 82211042SErik.Nordmark@Sun.COM int error; 82311042SErik.Nordmark@Sun.COM 82411042SErik.Nordmark@Sun.COM /* Acquire serializer to keep assert in ilm_bld_flists happy */ 82511042SErik.Nordmark@Sun.COM mutex_enter(&ill->ill_mcast_serializer); 82611042SErik.Nordmark@Sun.COM error = ip_delmulti_serial(ilm, B_TRUE, B_TRUE); 82711042SErik.Nordmark@Sun.COM mutex_exit(&ill->ill_mcast_serializer); 828*11463SSowmini.Varadhan@Sun.COM /* 829*11463SSowmini.Varadhan@Sun.COM * Now that all locks have been dropped, we can send any 830*11463SSowmini.Varadhan@Sun.COM * deferred/queued DLPI or IP packets 831*11463SSowmini.Varadhan@Sun.COM */ 832*11463SSowmini.Varadhan@Sun.COM ill_mcast_send_queued(ill); 833*11463SSowmini.Varadhan@Sun.COM ill_dlpi_send_queued(ill); 83411042SErik.Nordmark@Sun.COM return (error); 83511042SErik.Nordmark@Sun.COM } 83611042SErik.Nordmark@Sun.COM 83711042SErik.Nordmark@Sun.COM 83811042SErik.Nordmark@Sun.COM /* 83911042SErik.Nordmark@Sun.COM * Delete the ilm. 840*11463SSowmini.Varadhan@Sun.COM * Assumes ill_mcast_serializer is held by the caller. 841*11463SSowmini.Varadhan@Sun.COM * Caller must send out queued dlpi/multicast packets after dropping 842*11463SSowmini.Varadhan@Sun.COM * all locks. 84311042SErik.Nordmark@Sun.COM */ 84411042SErik.Nordmark@Sun.COM static int 84511042SErik.Nordmark@Sun.COM ip_delmulti_serial(ilm_t *ilm, boolean_t no_ilg, boolean_t leaving) 8460Sstevel@tonic-gate { 84711042SErik.Nordmark@Sun.COM ill_t *ill = ilm->ilm_ill; 84811042SErik.Nordmark@Sun.COM int ret; 84911042SErik.Nordmark@Sun.COM 85011042SErik.Nordmark@Sun.COM ASSERT(MUTEX_HELD(&ill->ill_mcast_serializer)); 85111042SErik.Nordmark@Sun.COM ASSERT(!(IS_UNDER_IPMP(ill))); 85211042SErik.Nordmark@Sun.COM 85311042SErik.Nordmark@Sun.COM rw_enter(&ill->ill_mcast_lock, RW_WRITER); 85411042SErik.Nordmark@Sun.COM ret = ip_delmulti_impl(ilm, no_ilg, leaving); 85511042SErik.Nordmark@Sun.COM rw_exit(&ill->ill_mcast_lock); 85611042SErik.Nordmark@Sun.COM ill_mcast_timer_start(ill->ill_ipst); 85711042SErik.Nordmark@Sun.COM return (ret); 85811042SErik.Nordmark@Sun.COM } 85911042SErik.Nordmark@Sun.COM 86011042SErik.Nordmark@Sun.COM static int 86111042SErik.Nordmark@Sun.COM ip_delmulti_impl(ilm_t *ilm, boolean_t no_ilg, boolean_t leaving) 86211042SErik.Nordmark@Sun.COM { 86311042SErik.Nordmark@Sun.COM ill_t *ill = ilm->ilm_ill; 86411042SErik.Nordmark@Sun.COM int error; 8650Sstevel@tonic-gate in6_addr_t v6group; 8660Sstevel@tonic-gate 86711042SErik.Nordmark@Sun.COM ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock)); 8680Sstevel@tonic-gate 8690Sstevel@tonic-gate /* Update counters */ 8700Sstevel@tonic-gate if (no_ilg) 8710Sstevel@tonic-gate ilm->ilm_no_ilg_cnt--; 8720Sstevel@tonic-gate 8730Sstevel@tonic-gate if (leaving) 8740Sstevel@tonic-gate ilm->ilm_refcnt--; 8750Sstevel@tonic-gate 8760Sstevel@tonic-gate if (ilm->ilm_refcnt > 0) 87711042SErik.Nordmark@Sun.COM return (ilm_update_del(ilm)); 87811042SErik.Nordmark@Sun.COM 87911042SErik.Nordmark@Sun.COM v6group = ilm->ilm_v6addr; 88011042SErik.Nordmark@Sun.COM 88111042SErik.Nordmark@Sun.COM if (IN6_IS_ADDR_UNSPECIFIED(&ilm->ilm_v6addr)) { 8820Sstevel@tonic-gate ilm_delete(ilm); 8830Sstevel@tonic-gate /* 88411042SErik.Nordmark@Sun.COM * If we have some left then one we should not tell the driver 88511042SErik.Nordmark@Sun.COM * to leave. 8860Sstevel@tonic-gate */ 88711042SErik.Nordmark@Sun.COM if (ilm_numentries(ill, &v6group) != 0) 8880Sstevel@tonic-gate return (0); 8890Sstevel@tonic-gate 89011042SErik.Nordmark@Sun.COM ill_leave_allmulti(ill); 8918485SPeter.Memishian@Sun.COM 8928023SPhil.Kirk@Sun.COM return (0); 8930Sstevel@tonic-gate } 8940Sstevel@tonic-gate 89511042SErik.Nordmark@Sun.COM if (!IS_LOOPBACK(ill)) { 89611042SErik.Nordmark@Sun.COM if (ill->ill_isv6) 89711042SErik.Nordmark@Sun.COM mld_leavegroup(ilm); 89811042SErik.Nordmark@Sun.COM else 89911042SErik.Nordmark@Sun.COM igmp_leavegroup(ilm); 9000Sstevel@tonic-gate } 9010Sstevel@tonic-gate 9020Sstevel@tonic-gate ilm_delete(ilm); 9030Sstevel@tonic-gate /* 90411042SErik.Nordmark@Sun.COM * If we have some left then one we should not tell the driver 90511042SErik.Nordmark@Sun.COM * to leave. 9060Sstevel@tonic-gate */ 90711042SErik.Nordmark@Sun.COM if (ilm_numentries(ill, &v6group) != 0) 9080Sstevel@tonic-gate return (0); 90911042SErik.Nordmark@Sun.COM 91011042SErik.Nordmark@Sun.COM error = ip_ll_multireq(ill, &v6group, DL_DISABMULTI_REQ); 91111042SErik.Nordmark@Sun.COM /* We ignore the case when ill_dl_up is not set */ 91211042SErik.Nordmark@Sun.COM if (error == ENETDOWN) { 91311042SErik.Nordmark@Sun.COM char buf[INET6_ADDRSTRLEN]; 91411042SErik.Nordmark@Sun.COM 91511042SErik.Nordmark@Sun.COM ip0dbg(("ip_delmulti: ENETDOWN for %s on %s", 91611042SErik.Nordmark@Sun.COM inet_ntop(AF_INET6, &v6group, buf, sizeof (buf)), 91711042SErik.Nordmark@Sun.COM ill->ill_name)); 91811042SErik.Nordmark@Sun.COM } 91911042SErik.Nordmark@Sun.COM return (error); 9200Sstevel@tonic-gate } 9210Sstevel@tonic-gate 9220Sstevel@tonic-gate /* 92311042SErik.Nordmark@Sun.COM * Make the driver pass up all multicast packets. 9240Sstevel@tonic-gate */ 9250Sstevel@tonic-gate int 9268023SPhil.Kirk@Sun.COM ill_join_allmulti(ill_t *ill) 9270Sstevel@tonic-gate { 92811042SErik.Nordmark@Sun.COM mblk_t *promiscon_mp, *promiscoff_mp = NULL; 9290Sstevel@tonic-gate uint32_t addrlen, addroff; 93011042SErik.Nordmark@Sun.COM ill_t *release_ill = NULL; 93111042SErik.Nordmark@Sun.COM 93211042SErik.Nordmark@Sun.COM ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock)); 9330Sstevel@tonic-gate 93411077SErik.Nordmark@Sun.COM if (IS_LOOPBACK(ill)) 93511077SErik.Nordmark@Sun.COM return (0); 93611077SErik.Nordmark@Sun.COM 9374770Smeem if (!ill->ill_dl_up) { 9380Sstevel@tonic-gate /* 9390Sstevel@tonic-gate * Nobody there. All multicast addresses will be re-joined 9400Sstevel@tonic-gate * when we get the DL_BIND_ACK bringing the interface up. 9410Sstevel@tonic-gate */ 94211042SErik.Nordmark@Sun.COM return (ENETDOWN); 9430Sstevel@tonic-gate } 9440Sstevel@tonic-gate 94511042SErik.Nordmark@Sun.COM if (IS_IPMP(ill)) { 94611042SErik.Nordmark@Sun.COM /* On the upper IPMP ill. */ 94711042SErik.Nordmark@Sun.COM release_ill = ipmp_illgrp_hold_cast_ill(ill->ill_grp); 94811042SErik.Nordmark@Sun.COM if (release_ill == NULL) { 94911042SErik.Nordmark@Sun.COM /* 95011042SErik.Nordmark@Sun.COM * Avoid sending it down to the ipmpstub. 95111042SErik.Nordmark@Sun.COM * We will be called again once the members of the 95211042SErik.Nordmark@Sun.COM * group are in place 95311042SErik.Nordmark@Sun.COM */ 95411042SErik.Nordmark@Sun.COM ip1dbg(("ill_join_allmulti: no cast_ill for %s %d\n", 95511042SErik.Nordmark@Sun.COM ill->ill_name, ill->ill_isv6)); 95611042SErik.Nordmark@Sun.COM return (0); 95711042SErik.Nordmark@Sun.COM } 95811042SErik.Nordmark@Sun.COM ill = release_ill; 95911042SErik.Nordmark@Sun.COM if (!ill->ill_dl_up) { 96011042SErik.Nordmark@Sun.COM ill_refrele(ill); 96111042SErik.Nordmark@Sun.COM return (ENETDOWN); 96211042SErik.Nordmark@Sun.COM } 96311042SErik.Nordmark@Sun.COM } 9640Sstevel@tonic-gate 9650Sstevel@tonic-gate /* 9668023SPhil.Kirk@Sun.COM * Create a DL_PROMISCON_REQ message and send it directly to the DLPI 9678023SPhil.Kirk@Sun.COM * provider. We don't need to do this for certain media types for 9688023SPhil.Kirk@Sun.COM * which we never need to turn promiscuous mode on. While we're here, 9698023SPhil.Kirk@Sun.COM * pre-allocate a DL_PROMISCOFF_REQ message to make sure that 9708023SPhil.Kirk@Sun.COM * ill_leave_allmulti() will not fail due to low memory conditions. 9710Sstevel@tonic-gate */ 9720Sstevel@tonic-gate if ((ill->ill_net_type == IRE_IF_RESOLVER) && 9730Sstevel@tonic-gate !(ill->ill_phyint->phyint_flags & PHYI_MULTI_BCAST)) { 9748023SPhil.Kirk@Sun.COM promiscon_mp = ill_create_dl(ill, DL_PROMISCON_REQ, 97511042SErik.Nordmark@Sun.COM &addrlen, &addroff); 97611042SErik.Nordmark@Sun.COM if (ill->ill_promiscoff_mp == NULL) 97711042SErik.Nordmark@Sun.COM promiscoff_mp = ill_create_dl(ill, DL_PROMISCOFF_REQ, 97811042SErik.Nordmark@Sun.COM &addrlen, &addroff); 97911042SErik.Nordmark@Sun.COM if (promiscon_mp == NULL || 98011042SErik.Nordmark@Sun.COM (ill->ill_promiscoff_mp == NULL && promiscoff_mp == NULL)) { 9818023SPhil.Kirk@Sun.COM freemsg(promiscon_mp); 9828023SPhil.Kirk@Sun.COM freemsg(promiscoff_mp); 98311042SErik.Nordmark@Sun.COM if (release_ill != NULL) 98411042SErik.Nordmark@Sun.COM ill_refrele(release_ill); 9850Sstevel@tonic-gate return (ENOMEM); 9868023SPhil.Kirk@Sun.COM } 98711042SErik.Nordmark@Sun.COM if (ill->ill_promiscoff_mp == NULL) 98811042SErik.Nordmark@Sun.COM ill->ill_promiscoff_mp = promiscoff_mp; 98911042SErik.Nordmark@Sun.COM ill_dlpi_queue(ill, promiscon_mp); 9900Sstevel@tonic-gate } 99111042SErik.Nordmark@Sun.COM if (release_ill != NULL) 99211042SErik.Nordmark@Sun.COM ill_refrele(release_ill); 9930Sstevel@tonic-gate return (0); 9940Sstevel@tonic-gate } 9950Sstevel@tonic-gate 9960Sstevel@tonic-gate /* 9970Sstevel@tonic-gate * Make the driver stop passing up all multicast packets 9980Sstevel@tonic-gate */ 9998023SPhil.Kirk@Sun.COM void 10008023SPhil.Kirk@Sun.COM ill_leave_allmulti(ill_t *ill) 10010Sstevel@tonic-gate { 10028485SPeter.Memishian@Sun.COM mblk_t *promiscoff_mp; 100311042SErik.Nordmark@Sun.COM ill_t *release_ill = NULL; 100411042SErik.Nordmark@Sun.COM 100511042SErik.Nordmark@Sun.COM ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock)); 10060Sstevel@tonic-gate 100711077SErik.Nordmark@Sun.COM if (IS_LOOPBACK(ill)) 100811077SErik.Nordmark@Sun.COM return; 100911077SErik.Nordmark@Sun.COM 10104770Smeem if (!ill->ill_dl_up) { 10110Sstevel@tonic-gate /* 10120Sstevel@tonic-gate * Nobody there. All multicast addresses will be re-joined 10130Sstevel@tonic-gate * when we get the DL_BIND_ACK bringing the interface up. 10140Sstevel@tonic-gate */ 10158023SPhil.Kirk@Sun.COM return; 10160Sstevel@tonic-gate } 10170Sstevel@tonic-gate 101811042SErik.Nordmark@Sun.COM if (IS_IPMP(ill)) { 101911042SErik.Nordmark@Sun.COM /* On the upper IPMP ill. */ 102011042SErik.Nordmark@Sun.COM release_ill = ipmp_illgrp_hold_cast_ill(ill->ill_grp); 102111042SErik.Nordmark@Sun.COM if (release_ill == NULL) { 102211042SErik.Nordmark@Sun.COM /* 102311042SErik.Nordmark@Sun.COM * Avoid sending it down to the ipmpstub. 102411042SErik.Nordmark@Sun.COM * We will be called again once the members of the 102511042SErik.Nordmark@Sun.COM * group are in place 102611042SErik.Nordmark@Sun.COM */ 102711042SErik.Nordmark@Sun.COM ip1dbg(("ill_leave_allmulti: no cast_ill on %s %d\n", 102811042SErik.Nordmark@Sun.COM ill->ill_name, ill->ill_isv6)); 102911042SErik.Nordmark@Sun.COM return; 103011042SErik.Nordmark@Sun.COM } 103111042SErik.Nordmark@Sun.COM ill = release_ill; 103211042SErik.Nordmark@Sun.COM if (!ill->ill_dl_up) 103311042SErik.Nordmark@Sun.COM goto done; 103411042SErik.Nordmark@Sun.COM } 10350Sstevel@tonic-gate 10360Sstevel@tonic-gate /* 103711042SErik.Nordmark@Sun.COM * In the case of IPMP and ill_dl_up not being set when we joined 103811042SErik.Nordmark@Sun.COM * we didn't allocate a promiscoff_mp. In that case we have 103911042SErik.Nordmark@Sun.COM * nothing to do when we leave. 104011042SErik.Nordmark@Sun.COM * Ditto for PHYI_MULTI_BCAST 10410Sstevel@tonic-gate */ 104211042SErik.Nordmark@Sun.COM promiscoff_mp = ill->ill_promiscoff_mp; 104311042SErik.Nordmark@Sun.COM if (promiscoff_mp != NULL) { 10448023SPhil.Kirk@Sun.COM ill->ill_promiscoff_mp = NULL; 104511042SErik.Nordmark@Sun.COM ill_dlpi_queue(ill, promiscoff_mp); 10460Sstevel@tonic-gate } 104711042SErik.Nordmark@Sun.COM done: 104811042SErik.Nordmark@Sun.COM if (release_ill != NULL) 104911042SErik.Nordmark@Sun.COM ill_refrele(release_ill); 10508023SPhil.Kirk@Sun.COM } 10518023SPhil.Kirk@Sun.COM 10528023SPhil.Kirk@Sun.COM int 10538023SPhil.Kirk@Sun.COM ip_join_allmulti(uint_t ifindex, boolean_t isv6, ip_stack_t *ipst) 10548023SPhil.Kirk@Sun.COM { 10558023SPhil.Kirk@Sun.COM ill_t *ill; 105611042SErik.Nordmark@Sun.COM int ret; 105711042SErik.Nordmark@Sun.COM ilm_t *ilm; 105811042SErik.Nordmark@Sun.COM 105911042SErik.Nordmark@Sun.COM ill = ill_lookup_on_ifindex(ifindex, isv6, ipst); 106011042SErik.Nordmark@Sun.COM if (ill == NULL) 10618023SPhil.Kirk@Sun.COM return (ENODEV); 10628485SPeter.Memishian@Sun.COM 10638485SPeter.Memishian@Sun.COM /* 106411042SErik.Nordmark@Sun.COM * The ip_addmulti() function doesn't allow IPMP underlying interfaces 10658485SPeter.Memishian@Sun.COM * to join allmulti since only the nominated underlying interface in 10668485SPeter.Memishian@Sun.COM * the group should receive multicast. We silently succeed to avoid 10678485SPeter.Memishian@Sun.COM * having to teach IPobs (currently the only caller of this routine) 10688485SPeter.Memishian@Sun.COM * to ignore failures in this case. 10698485SPeter.Memishian@Sun.COM */ 107011042SErik.Nordmark@Sun.COM if (IS_UNDER_IPMP(ill)) { 107111042SErik.Nordmark@Sun.COM ill_refrele(ill); 107211042SErik.Nordmark@Sun.COM return (0); 107311042SErik.Nordmark@Sun.COM } 107411042SErik.Nordmark@Sun.COM mutex_enter(&ill->ill_lock); 107511042SErik.Nordmark@Sun.COM if (ill->ill_ipallmulti_cnt > 0) { 107611042SErik.Nordmark@Sun.COM /* Already joined */ 107711042SErik.Nordmark@Sun.COM ASSERT(ill->ill_ipallmulti_ilm != NULL); 107811042SErik.Nordmark@Sun.COM ill->ill_ipallmulti_cnt++; 107911042SErik.Nordmark@Sun.COM mutex_exit(&ill->ill_lock); 108011042SErik.Nordmark@Sun.COM goto done; 10818023SPhil.Kirk@Sun.COM } 108211042SErik.Nordmark@Sun.COM mutex_exit(&ill->ill_lock); 108311042SErik.Nordmark@Sun.COM 108411042SErik.Nordmark@Sun.COM ilm = ip_addmulti(&ipv6_all_zeros, ill, ill->ill_zoneid, &ret); 108511042SErik.Nordmark@Sun.COM if (ilm == NULL) { 108611042SErik.Nordmark@Sun.COM ASSERT(ret != 0); 108711042SErik.Nordmark@Sun.COM ill_refrele(ill); 108811042SErik.Nordmark@Sun.COM return (ret); 108911042SErik.Nordmark@Sun.COM } 109011042SErik.Nordmark@Sun.COM 109111042SErik.Nordmark@Sun.COM mutex_enter(&ill->ill_lock); 109211042SErik.Nordmark@Sun.COM if (ill->ill_ipallmulti_cnt > 0) { 109311042SErik.Nordmark@Sun.COM /* Another thread added it concurrently */ 109411042SErik.Nordmark@Sun.COM (void) ip_delmulti(ilm); 109511042SErik.Nordmark@Sun.COM mutex_exit(&ill->ill_lock); 109611042SErik.Nordmark@Sun.COM goto done; 109711042SErik.Nordmark@Sun.COM } 109811042SErik.Nordmark@Sun.COM ASSERT(ill->ill_ipallmulti_ilm == NULL); 109911042SErik.Nordmark@Sun.COM ill->ill_ipallmulti_ilm = ilm; 11008023SPhil.Kirk@Sun.COM ill->ill_ipallmulti_cnt++; 110111042SErik.Nordmark@Sun.COM mutex_exit(&ill->ill_lock); 110211042SErik.Nordmark@Sun.COM done: 110311042SErik.Nordmark@Sun.COM ill_refrele(ill); 110411042SErik.Nordmark@Sun.COM return (0); 11058023SPhil.Kirk@Sun.COM } 11068023SPhil.Kirk@Sun.COM 11078023SPhil.Kirk@Sun.COM int 11088023SPhil.Kirk@Sun.COM ip_leave_allmulti(uint_t ifindex, boolean_t isv6, ip_stack_t *ipst) 11098023SPhil.Kirk@Sun.COM { 11108023SPhil.Kirk@Sun.COM ill_t *ill; 111111042SErik.Nordmark@Sun.COM ilm_t *ilm; 111211042SErik.Nordmark@Sun.COM 111311042SErik.Nordmark@Sun.COM ill = ill_lookup_on_ifindex(ifindex, isv6, ipst); 111411042SErik.Nordmark@Sun.COM if (ill == NULL) 11158023SPhil.Kirk@Sun.COM return (ENODEV); 11168485SPeter.Memishian@Sun.COM 111711042SErik.Nordmark@Sun.COM if (IS_UNDER_IPMP(ill)) { 111811042SErik.Nordmark@Sun.COM ill_refrele(ill); 111911042SErik.Nordmark@Sun.COM return (0); 112011042SErik.Nordmark@Sun.COM } 112111042SErik.Nordmark@Sun.COM 112211042SErik.Nordmark@Sun.COM mutex_enter(&ill->ill_lock); 112311042SErik.Nordmark@Sun.COM if (ill->ill_ipallmulti_cnt == 0) { 112411042SErik.Nordmark@Sun.COM /* ip_purge_allmulti could have removed them all */ 112511042SErik.Nordmark@Sun.COM mutex_exit(&ill->ill_lock); 112611042SErik.Nordmark@Sun.COM goto done; 11278023SPhil.Kirk@Sun.COM } 112811042SErik.Nordmark@Sun.COM ill->ill_ipallmulti_cnt--; 112911042SErik.Nordmark@Sun.COM if (ill->ill_ipallmulti_cnt == 0) { 113011042SErik.Nordmark@Sun.COM /* Last one */ 113111042SErik.Nordmark@Sun.COM ilm = ill->ill_ipallmulti_ilm; 113211042SErik.Nordmark@Sun.COM ill->ill_ipallmulti_ilm = NULL; 113311042SErik.Nordmark@Sun.COM } else { 113411042SErik.Nordmark@Sun.COM ilm = NULL; 113511042SErik.Nordmark@Sun.COM } 113611042SErik.Nordmark@Sun.COM mutex_exit(&ill->ill_lock); 113711042SErik.Nordmark@Sun.COM if (ilm != NULL) 113811042SErik.Nordmark@Sun.COM (void) ip_delmulti(ilm); 113911042SErik.Nordmark@Sun.COM 114011042SErik.Nordmark@Sun.COM done: 114111042SErik.Nordmark@Sun.COM ill_refrele(ill); 11420Sstevel@tonic-gate return (0); 11430Sstevel@tonic-gate } 11440Sstevel@tonic-gate 11450Sstevel@tonic-gate /* 11468023SPhil.Kirk@Sun.COM * Delete the allmulti memberships that were added as part of 11478023SPhil.Kirk@Sun.COM * ip_join_allmulti(). 11488023SPhil.Kirk@Sun.COM */ 11498023SPhil.Kirk@Sun.COM void 11508023SPhil.Kirk@Sun.COM ip_purge_allmulti(ill_t *ill) 11518023SPhil.Kirk@Sun.COM { 115211042SErik.Nordmark@Sun.COM ilm_t *ilm; 115311042SErik.Nordmark@Sun.COM 11548023SPhil.Kirk@Sun.COM ASSERT(IAM_WRITER_ILL(ill)); 11558023SPhil.Kirk@Sun.COM 115611042SErik.Nordmark@Sun.COM mutex_enter(&ill->ill_lock); 115711042SErik.Nordmark@Sun.COM ilm = ill->ill_ipallmulti_ilm; 115811042SErik.Nordmark@Sun.COM ill->ill_ipallmulti_ilm = NULL; 115911042SErik.Nordmark@Sun.COM ill->ill_ipallmulti_cnt = 0; 116011042SErik.Nordmark@Sun.COM mutex_exit(&ill->ill_lock); 116111042SErik.Nordmark@Sun.COM 116211042SErik.Nordmark@Sun.COM if (ilm != NULL) 116311042SErik.Nordmark@Sun.COM (void) ip_delmulti(ilm); 11648023SPhil.Kirk@Sun.COM } 11658023SPhil.Kirk@Sun.COM 11668023SPhil.Kirk@Sun.COM /* 116711042SErik.Nordmark@Sun.COM * Create a dlpi message with room for phys+sap. Later 116811042SErik.Nordmark@Sun.COM * we will strip the sap for those primitives which 116911042SErik.Nordmark@Sun.COM * only need a physical address. 11700Sstevel@tonic-gate */ 11710Sstevel@tonic-gate static mblk_t * 117211042SErik.Nordmark@Sun.COM ill_create_dl(ill_t *ill, uint32_t dl_primitive, 11730Sstevel@tonic-gate uint32_t *addr_lenp, uint32_t *addr_offp) 11740Sstevel@tonic-gate { 11750Sstevel@tonic-gate mblk_t *mp; 11760Sstevel@tonic-gate uint32_t hw_addr_length; 11770Sstevel@tonic-gate char *cp; 11780Sstevel@tonic-gate uint32_t offset; 117911042SErik.Nordmark@Sun.COM uint32_t length; 11800Sstevel@tonic-gate uint32_t size; 11810Sstevel@tonic-gate 11820Sstevel@tonic-gate *addr_lenp = *addr_offp = 0; 11830Sstevel@tonic-gate 11840Sstevel@tonic-gate hw_addr_length = ill->ill_phys_addr_length; 11850Sstevel@tonic-gate if (!hw_addr_length) { 11860Sstevel@tonic-gate ip0dbg(("ip_create_dl: hw addr length = 0\n")); 11870Sstevel@tonic-gate return (NULL); 11880Sstevel@tonic-gate } 11890Sstevel@tonic-gate 11900Sstevel@tonic-gate switch (dl_primitive) { 11910Sstevel@tonic-gate case DL_ENABMULTI_REQ: 119211042SErik.Nordmark@Sun.COM length = sizeof (dl_enabmulti_req_t); 119311042SErik.Nordmark@Sun.COM size = length + hw_addr_length; 119411042SErik.Nordmark@Sun.COM break; 11950Sstevel@tonic-gate case DL_DISABMULTI_REQ: 119611042SErik.Nordmark@Sun.COM length = sizeof (dl_disabmulti_req_t); 119711042SErik.Nordmark@Sun.COM size = length + hw_addr_length; 11980Sstevel@tonic-gate break; 11990Sstevel@tonic-gate case DL_PROMISCON_REQ: 12000Sstevel@tonic-gate case DL_PROMISCOFF_REQ: 120111042SErik.Nordmark@Sun.COM size = length = sizeof (dl_promiscon_req_t); 12020Sstevel@tonic-gate break; 12030Sstevel@tonic-gate default: 12040Sstevel@tonic-gate return (NULL); 12050Sstevel@tonic-gate } 12060Sstevel@tonic-gate mp = allocb(size, BPRI_HI); 12070Sstevel@tonic-gate if (!mp) 12080Sstevel@tonic-gate return (NULL); 12090Sstevel@tonic-gate mp->b_wptr += size; 12100Sstevel@tonic-gate mp->b_datap->db_type = M_PROTO; 12110Sstevel@tonic-gate 12120Sstevel@tonic-gate cp = (char *)mp->b_rptr; 12130Sstevel@tonic-gate offset = length; 12140Sstevel@tonic-gate 12150Sstevel@tonic-gate switch (dl_primitive) { 12160Sstevel@tonic-gate case DL_ENABMULTI_REQ: { 12170Sstevel@tonic-gate dl_enabmulti_req_t *dl = (dl_enabmulti_req_t *)cp; 12180Sstevel@tonic-gate 12190Sstevel@tonic-gate dl->dl_primitive = dl_primitive; 12200Sstevel@tonic-gate dl->dl_addr_offset = offset; 12210Sstevel@tonic-gate *addr_lenp = dl->dl_addr_length = hw_addr_length; 12220Sstevel@tonic-gate *addr_offp = offset; 12230Sstevel@tonic-gate break; 12240Sstevel@tonic-gate } 12250Sstevel@tonic-gate case DL_DISABMULTI_REQ: { 12260Sstevel@tonic-gate dl_disabmulti_req_t *dl = (dl_disabmulti_req_t *)cp; 12270Sstevel@tonic-gate 12280Sstevel@tonic-gate dl->dl_primitive = dl_primitive; 12290Sstevel@tonic-gate dl->dl_addr_offset = offset; 12300Sstevel@tonic-gate *addr_lenp = dl->dl_addr_length = hw_addr_length; 12310Sstevel@tonic-gate *addr_offp = offset; 12320Sstevel@tonic-gate break; 12330Sstevel@tonic-gate } 12340Sstevel@tonic-gate case DL_PROMISCON_REQ: 12350Sstevel@tonic-gate case DL_PROMISCOFF_REQ: { 12360Sstevel@tonic-gate dl_promiscon_req_t *dl = (dl_promiscon_req_t *)cp; 12370Sstevel@tonic-gate 12380Sstevel@tonic-gate dl->dl_primitive = dl_primitive; 12390Sstevel@tonic-gate dl->dl_level = DL_PROMISC_MULTI; 12400Sstevel@tonic-gate break; 12410Sstevel@tonic-gate } 12420Sstevel@tonic-gate } 12430Sstevel@tonic-gate ip1dbg(("ill_create_dl: addr_len %d, addr_off %d\n", 12444459Skcpoon *addr_lenp, *addr_offp)); 12450Sstevel@tonic-gate return (mp); 12460Sstevel@tonic-gate } 12470Sstevel@tonic-gate 12486979Smeem /* 124911042SErik.Nordmark@Sun.COM * Rejoin any groups for which we have ilms. 125011042SErik.Nordmark@Sun.COM * 125111042SErik.Nordmark@Sun.COM * This is only needed for IPMP when the cast_ill changes since that 125211042SErik.Nordmark@Sun.COM * change is invisible to the ilm. Other interface changes are handled 125311042SErik.Nordmark@Sun.COM * by conn_update_ill. 12540Sstevel@tonic-gate */ 12550Sstevel@tonic-gate void 12560Sstevel@tonic-gate ill_recover_multicast(ill_t *ill) 12570Sstevel@tonic-gate { 12580Sstevel@tonic-gate ilm_t *ilm; 12590Sstevel@tonic-gate char addrbuf[INET6_ADDRSTRLEN]; 12600Sstevel@tonic-gate 12618023SPhil.Kirk@Sun.COM ill->ill_need_recover_multicast = 0; 12628023SPhil.Kirk@Sun.COM 126311042SErik.Nordmark@Sun.COM rw_enter(&ill->ill_mcast_lock, RW_WRITER); 12640Sstevel@tonic-gate for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) { 12650Sstevel@tonic-gate /* 126611042SErik.Nordmark@Sun.COM * If we have more then one ilm for the group (e.g., with 126711042SErik.Nordmark@Sun.COM * different zoneid) then we should not tell the driver 126811042SErik.Nordmark@Sun.COM * to join unless this is the first ilm for the group. 12690Sstevel@tonic-gate */ 127011042SErik.Nordmark@Sun.COM if (ilm_numentries(ill, &ilm->ilm_v6addr) > 1 && 127111042SErik.Nordmark@Sun.COM ilm_lookup(ill, &ilm->ilm_v6addr, ALL_ZONES) != ilm) { 12720Sstevel@tonic-gate continue; 12738485SPeter.Memishian@Sun.COM } 12748485SPeter.Memishian@Sun.COM 12758485SPeter.Memishian@Sun.COM ip1dbg(("ill_recover_multicast: %s\n", inet_ntop(AF_INET6, 12768485SPeter.Memishian@Sun.COM &ilm->ilm_v6addr, addrbuf, sizeof (addrbuf)))); 12778485SPeter.Memishian@Sun.COM 12780Sstevel@tonic-gate if (IN6_IS_ADDR_UNSPECIFIED(&ilm->ilm_v6addr)) { 12798485SPeter.Memishian@Sun.COM (void) ill_join_allmulti(ill); 12800Sstevel@tonic-gate } else { 12818485SPeter.Memishian@Sun.COM if (ill->ill_isv6) 12828485SPeter.Memishian@Sun.COM mld_joingroup(ilm); 12838485SPeter.Memishian@Sun.COM else 12848485SPeter.Memishian@Sun.COM igmp_joingroup(ilm); 12858485SPeter.Memishian@Sun.COM 128611042SErik.Nordmark@Sun.COM (void) ip_ll_multireq(ill, &ilm->ilm_v6addr, 128711042SErik.Nordmark@Sun.COM DL_ENABMULTI_REQ); 12880Sstevel@tonic-gate } 12890Sstevel@tonic-gate } 129011042SErik.Nordmark@Sun.COM rw_exit(&ill->ill_mcast_lock); 129111042SErik.Nordmark@Sun.COM /* Send any deferred/queued DLPI or IP packets */ 129211042SErik.Nordmark@Sun.COM ill_mcast_send_queued(ill); 129311042SErik.Nordmark@Sun.COM ill_dlpi_send_queued(ill); 129411042SErik.Nordmark@Sun.COM ill_mcast_timer_start(ill->ill_ipst); 12950Sstevel@tonic-gate } 12960Sstevel@tonic-gate 12970Sstevel@tonic-gate /* 12980Sstevel@tonic-gate * The opposite of ill_recover_multicast() -- leaves all multicast groups 12998485SPeter.Memishian@Sun.COM * that were explicitly joined. 130011042SErik.Nordmark@Sun.COM * 130111042SErik.Nordmark@Sun.COM * This is only needed for IPMP when the cast_ill changes since that 130211042SErik.Nordmark@Sun.COM * change is invisible to the ilm. Other interface changes are handled 130311042SErik.Nordmark@Sun.COM * by conn_update_ill. 13040Sstevel@tonic-gate */ 13050Sstevel@tonic-gate void 13060Sstevel@tonic-gate ill_leave_multicast(ill_t *ill) 13070Sstevel@tonic-gate { 13080Sstevel@tonic-gate ilm_t *ilm; 13090Sstevel@tonic-gate char addrbuf[INET6_ADDRSTRLEN]; 13100Sstevel@tonic-gate 13118023SPhil.Kirk@Sun.COM ill->ill_need_recover_multicast = 1; 13128023SPhil.Kirk@Sun.COM 131311042SErik.Nordmark@Sun.COM rw_enter(&ill->ill_mcast_lock, RW_WRITER); 13140Sstevel@tonic-gate for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) { 13150Sstevel@tonic-gate /* 131611042SErik.Nordmark@Sun.COM * If we have more then one ilm for the group (e.g., with 131711042SErik.Nordmark@Sun.COM * different zoneid) then we should not tell the driver 131811042SErik.Nordmark@Sun.COM * to leave unless this is the first ilm for the group. 13190Sstevel@tonic-gate */ 132011042SErik.Nordmark@Sun.COM if (ilm_numentries(ill, &ilm->ilm_v6addr) > 1 && 132111042SErik.Nordmark@Sun.COM ilm_lookup(ill, &ilm->ilm_v6addr, ALL_ZONES) != ilm) { 13220Sstevel@tonic-gate continue; 13238485SPeter.Memishian@Sun.COM } 13248485SPeter.Memishian@Sun.COM 13258485SPeter.Memishian@Sun.COM ip1dbg(("ill_leave_multicast: %s\n", inet_ntop(AF_INET6, 13268485SPeter.Memishian@Sun.COM &ilm->ilm_v6addr, addrbuf, sizeof (addrbuf)))); 13278485SPeter.Memishian@Sun.COM 13280Sstevel@tonic-gate if (IN6_IS_ADDR_UNSPECIFIED(&ilm->ilm_v6addr)) { 13298023SPhil.Kirk@Sun.COM ill_leave_allmulti(ill); 13300Sstevel@tonic-gate } else { 13318485SPeter.Memishian@Sun.COM if (ill->ill_isv6) 13328485SPeter.Memishian@Sun.COM mld_leavegroup(ilm); 13338485SPeter.Memishian@Sun.COM else 13348485SPeter.Memishian@Sun.COM igmp_leavegroup(ilm); 13358485SPeter.Memishian@Sun.COM 133611042SErik.Nordmark@Sun.COM (void) ip_ll_multireq(ill, &ilm->ilm_v6addr, 133711042SErik.Nordmark@Sun.COM DL_DISABMULTI_REQ); 13380Sstevel@tonic-gate } 13390Sstevel@tonic-gate } 134011042SErik.Nordmark@Sun.COM rw_exit(&ill->ill_mcast_lock); 134111042SErik.Nordmark@Sun.COM /* Send any deferred/queued DLPI or IP packets */ 134211042SErik.Nordmark@Sun.COM ill_mcast_send_queued(ill); 134311042SErik.Nordmark@Sun.COM ill_dlpi_send_queued(ill); 134411042SErik.Nordmark@Sun.COM ill_mcast_timer_start(ill->ill_ipst); 13450Sstevel@tonic-gate } 13460Sstevel@tonic-gate 134711042SErik.Nordmark@Sun.COM /* 134811042SErik.Nordmark@Sun.COM * Interface used by IP input/output. 134911042SErik.Nordmark@Sun.COM * Returns true if there is a member on the ill for any zoneid. 135011042SErik.Nordmark@Sun.COM */ 135111042SErik.Nordmark@Sun.COM boolean_t 135211042SErik.Nordmark@Sun.COM ill_hasmembers_v6(ill_t *ill, const in6_addr_t *v6group) 135311042SErik.Nordmark@Sun.COM { 135411042SErik.Nordmark@Sun.COM ilm_t *ilm; 135511042SErik.Nordmark@Sun.COM 135611042SErik.Nordmark@Sun.COM rw_enter(&ill->ill_mcast_lock, RW_READER); 135711042SErik.Nordmark@Sun.COM ilm = ilm_lookup(ill, v6group, ALL_ZONES); 135811042SErik.Nordmark@Sun.COM rw_exit(&ill->ill_mcast_lock); 135911042SErik.Nordmark@Sun.COM return (ilm != NULL); 136011042SErik.Nordmark@Sun.COM } 136111042SErik.Nordmark@Sun.COM 136211042SErik.Nordmark@Sun.COM /* 136311042SErik.Nordmark@Sun.COM * Interface used by IP input/output. 136411042SErik.Nordmark@Sun.COM * Returns true if there is a member on the ill for any zoneid. 136511042SErik.Nordmark@Sun.COM * 136611042SErik.Nordmark@Sun.COM * The group and source can't be INADDR_ANY here so no need to translate to 136711042SErik.Nordmark@Sun.COM * the unspecified IPv6 address. 136811042SErik.Nordmark@Sun.COM */ 136911042SErik.Nordmark@Sun.COM boolean_t 137011042SErik.Nordmark@Sun.COM ill_hasmembers_v4(ill_t *ill, ipaddr_t group) 13710Sstevel@tonic-gate { 13720Sstevel@tonic-gate in6_addr_t v6group; 13730Sstevel@tonic-gate 137411042SErik.Nordmark@Sun.COM IN6_IPADDR_TO_V4MAPPED(group, &v6group); 137511042SErik.Nordmark@Sun.COM return (ill_hasmembers_v6(ill, &v6group)); 137611042SErik.Nordmark@Sun.COM } 137711042SErik.Nordmark@Sun.COM 137811042SErik.Nordmark@Sun.COM /* 137911042SErik.Nordmark@Sun.COM * Interface used by IP input/output. 138011042SErik.Nordmark@Sun.COM * Returns true if there is a member on the ill for any zoneid except skipzone. 138111042SErik.Nordmark@Sun.COM */ 138211042SErik.Nordmark@Sun.COM boolean_t 138311042SErik.Nordmark@Sun.COM ill_hasmembers_otherzones_v6(ill_t *ill, const in6_addr_t *v6group, 138411042SErik.Nordmark@Sun.COM zoneid_t skipzone) 138511042SErik.Nordmark@Sun.COM { 138611042SErik.Nordmark@Sun.COM ilm_t *ilm; 138711042SErik.Nordmark@Sun.COM 138811042SErik.Nordmark@Sun.COM rw_enter(&ill->ill_mcast_lock, RW_READER); 138911042SErik.Nordmark@Sun.COM for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) { 139011042SErik.Nordmark@Sun.COM if (IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, v6group) && 139111042SErik.Nordmark@Sun.COM ilm->ilm_zoneid != skipzone) { 139211042SErik.Nordmark@Sun.COM rw_exit(&ill->ill_mcast_lock); 139311042SErik.Nordmark@Sun.COM return (B_TRUE); 139411042SErik.Nordmark@Sun.COM } 139511042SErik.Nordmark@Sun.COM } 139611042SErik.Nordmark@Sun.COM rw_exit(&ill->ill_mcast_lock); 139711042SErik.Nordmark@Sun.COM return (B_FALSE); 13980Sstevel@tonic-gate } 13990Sstevel@tonic-gate 14000Sstevel@tonic-gate /* 140111042SErik.Nordmark@Sun.COM * Interface used by IP input/output. 140211042SErik.Nordmark@Sun.COM * Returns true if there is a member on the ill for any zoneid except skipzone. 140311042SErik.Nordmark@Sun.COM * 140411042SErik.Nordmark@Sun.COM * The group and source can't be INADDR_ANY here so no need to translate to 140511042SErik.Nordmark@Sun.COM * the unspecified IPv6 address. 140611042SErik.Nordmark@Sun.COM */ 140711042SErik.Nordmark@Sun.COM boolean_t 140811042SErik.Nordmark@Sun.COM ill_hasmembers_otherzones_v4(ill_t *ill, ipaddr_t group, zoneid_t skipzone) 140911042SErik.Nordmark@Sun.COM { 141011042SErik.Nordmark@Sun.COM in6_addr_t v6group; 141111042SErik.Nordmark@Sun.COM 141211042SErik.Nordmark@Sun.COM IN6_IPADDR_TO_V4MAPPED(group, &v6group); 141311042SErik.Nordmark@Sun.COM return (ill_hasmembers_otherzones_v6(ill, &v6group, skipzone)); 141411042SErik.Nordmark@Sun.COM } 141511042SErik.Nordmark@Sun.COM 141611042SErik.Nordmark@Sun.COM /* 141711042SErik.Nordmark@Sun.COM * Interface used by IP input. 141811042SErik.Nordmark@Sun.COM * Returns the next numerically larger zoneid that has a member. If none exist 141911042SErik.Nordmark@Sun.COM * then returns -1 (ALL_ZONES). 142011042SErik.Nordmark@Sun.COM * The normal usage is for the caller to start with a -1 zoneid (ALL_ZONES) 142111042SErik.Nordmark@Sun.COM * to find the first zoneid which has a member, and then pass that in for 142211042SErik.Nordmark@Sun.COM * subsequent calls until ALL_ZONES is returned. 142311042SErik.Nordmark@Sun.COM * 142411042SErik.Nordmark@Sun.COM * The implementation of ill_hasmembers_nextzone() assumes the ilms 142511042SErik.Nordmark@Sun.COM * are sorted by zoneid for efficiency. 14260Sstevel@tonic-gate */ 142711042SErik.Nordmark@Sun.COM zoneid_t 142811042SErik.Nordmark@Sun.COM ill_hasmembers_nextzone_v6(ill_t *ill, const in6_addr_t *v6group, 142911042SErik.Nordmark@Sun.COM zoneid_t zoneid) 143011042SErik.Nordmark@Sun.COM { 143111042SErik.Nordmark@Sun.COM ilm_t *ilm; 143211042SErik.Nordmark@Sun.COM 143311042SErik.Nordmark@Sun.COM rw_enter(&ill->ill_mcast_lock, RW_READER); 143411042SErik.Nordmark@Sun.COM for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) { 143511042SErik.Nordmark@Sun.COM if (IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, v6group) && 143611042SErik.Nordmark@Sun.COM ilm->ilm_zoneid > zoneid) { 143711042SErik.Nordmark@Sun.COM zoneid = ilm->ilm_zoneid; 143811042SErik.Nordmark@Sun.COM rw_exit(&ill->ill_mcast_lock); 143911042SErik.Nordmark@Sun.COM return (zoneid); 144011042SErik.Nordmark@Sun.COM } 144111042SErik.Nordmark@Sun.COM } 144211042SErik.Nordmark@Sun.COM rw_exit(&ill->ill_mcast_lock); 144311042SErik.Nordmark@Sun.COM return (ALL_ZONES); 144411042SErik.Nordmark@Sun.COM } 144511042SErik.Nordmark@Sun.COM 144611042SErik.Nordmark@Sun.COM /* 144711042SErik.Nordmark@Sun.COM * Interface used by IP input. 144811042SErik.Nordmark@Sun.COM * Returns the next numerically larger zoneid that has a member. If none exist 144911042SErik.Nordmark@Sun.COM * then returns -1 (ALL_ZONES). 145011042SErik.Nordmark@Sun.COM * 145111042SErik.Nordmark@Sun.COM * The group and source can't be INADDR_ANY here so no need to translate to 145211042SErik.Nordmark@Sun.COM * the unspecified IPv6 address. 145311042SErik.Nordmark@Sun.COM */ 145411042SErik.Nordmark@Sun.COM zoneid_t 145511042SErik.Nordmark@Sun.COM ill_hasmembers_nextzone_v4(ill_t *ill, ipaddr_t group, zoneid_t zoneid) 145611042SErik.Nordmark@Sun.COM { 145711042SErik.Nordmark@Sun.COM in6_addr_t v6group; 145811042SErik.Nordmark@Sun.COM 145911042SErik.Nordmark@Sun.COM IN6_IPADDR_TO_V4MAPPED(group, &v6group); 146011042SErik.Nordmark@Sun.COM 146111042SErik.Nordmark@Sun.COM return (ill_hasmembers_nextzone_v6(ill, &v6group, zoneid)); 146211042SErik.Nordmark@Sun.COM } 146311042SErik.Nordmark@Sun.COM 146411042SErik.Nordmark@Sun.COM /* 146511042SErik.Nordmark@Sun.COM * Find an ilm matching the ill, group, and zoneid. 146611042SErik.Nordmark@Sun.COM */ 146711042SErik.Nordmark@Sun.COM static ilm_t * 146811042SErik.Nordmark@Sun.COM ilm_lookup(ill_t *ill, const in6_addr_t *v6group, zoneid_t zoneid) 14690Sstevel@tonic-gate { 14700Sstevel@tonic-gate ilm_t *ilm; 147111042SErik.Nordmark@Sun.COM 147211042SErik.Nordmark@Sun.COM ASSERT(RW_LOCK_HELD(&ill->ill_mcast_lock)); 147311042SErik.Nordmark@Sun.COM 147411042SErik.Nordmark@Sun.COM for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) { 14758485SPeter.Memishian@Sun.COM if (!IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, v6group)) 14760Sstevel@tonic-gate continue; 14778485SPeter.Memishian@Sun.COM if (zoneid != ALL_ZONES && zoneid != ilm->ilm_zoneid) 14780Sstevel@tonic-gate continue; 147911042SErik.Nordmark@Sun.COM 148011042SErik.Nordmark@Sun.COM ASSERT(ilm->ilm_ill == ill); 148111042SErik.Nordmark@Sun.COM return (ilm); 14820Sstevel@tonic-gate } 148311042SErik.Nordmark@Sun.COM return (NULL); 14840Sstevel@tonic-gate } 14850Sstevel@tonic-gate 14860Sstevel@tonic-gate /* 14870Sstevel@tonic-gate * How many members on this ill? 148811042SErik.Nordmark@Sun.COM * Since each shared-IP zone has a separate ilm for the same group/ill 148911042SErik.Nordmark@Sun.COM * we can have several. 14900Sstevel@tonic-gate */ 149111042SErik.Nordmark@Sun.COM static int 149211042SErik.Nordmark@Sun.COM ilm_numentries(ill_t *ill, const in6_addr_t *v6group) 14930Sstevel@tonic-gate { 14940Sstevel@tonic-gate ilm_t *ilm; 14950Sstevel@tonic-gate int i = 0; 14960Sstevel@tonic-gate 149711042SErik.Nordmark@Sun.COM ASSERT(RW_LOCK_HELD(&ill->ill_mcast_lock)); 14980Sstevel@tonic-gate for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) { 14990Sstevel@tonic-gate if (IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, v6group)) { 15000Sstevel@tonic-gate i++; 15010Sstevel@tonic-gate } 15020Sstevel@tonic-gate } 15030Sstevel@tonic-gate return (i); 15040Sstevel@tonic-gate } 15050Sstevel@tonic-gate 15060Sstevel@tonic-gate /* Caller guarantees that the group is not already on the list */ 15070Sstevel@tonic-gate static ilm_t * 150811042SErik.Nordmark@Sun.COM ilm_add(ill_t *ill, const in6_addr_t *v6group, ilg_stat_t ilgstat, 15098485SPeter.Memishian@Sun.COM mcast_record_t ilg_fmode, slist_t *ilg_flist, zoneid_t zoneid) 15100Sstevel@tonic-gate { 15110Sstevel@tonic-gate ilm_t *ilm; 15120Sstevel@tonic-gate ilm_t *ilm_cur; 15130Sstevel@tonic-gate ilm_t **ilm_ptpn; 15140Sstevel@tonic-gate 151511042SErik.Nordmark@Sun.COM ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock)); 15160Sstevel@tonic-gate ilm = GETSTRUCT(ilm_t, 1); 15170Sstevel@tonic-gate if (ilm == NULL) 15180Sstevel@tonic-gate return (NULL); 15190Sstevel@tonic-gate if (ilgstat != ILGSTAT_NONE && !SLIST_IS_EMPTY(ilg_flist)) { 15200Sstevel@tonic-gate ilm->ilm_filter = l_alloc(); 15210Sstevel@tonic-gate if (ilm->ilm_filter == NULL) { 15220Sstevel@tonic-gate mi_free(ilm); 15230Sstevel@tonic-gate return (NULL); 15240Sstevel@tonic-gate } 15250Sstevel@tonic-gate } 15260Sstevel@tonic-gate ilm->ilm_v6addr = *v6group; 15270Sstevel@tonic-gate ilm->ilm_refcnt = 1; 15280Sstevel@tonic-gate ilm->ilm_zoneid = zoneid; 15290Sstevel@tonic-gate ilm->ilm_timer = INFINITY; 15300Sstevel@tonic-gate ilm->ilm_rtx.rtx_timer = INFINITY; 15311676Sjpk 153211042SErik.Nordmark@Sun.COM ilm->ilm_ill = ill; 153311042SErik.Nordmark@Sun.COM DTRACE_PROBE3(ill__incr__cnt, (ill_t *), ill, 153411042SErik.Nordmark@Sun.COM (char *), "ilm", (void *), ilm); 153511042SErik.Nordmark@Sun.COM ill->ill_ilm_cnt++; 15368485SPeter.Memishian@Sun.COM 15373448Sdh155122 ASSERT(ill->ill_ipst); 15383448Sdh155122 ilm->ilm_ipst = ill->ill_ipst; /* No netstack_hold */ 15393448Sdh155122 154011042SErik.Nordmark@Sun.COM /* The ill/ipif could have just been marked as condemned */ 15410Sstevel@tonic-gate 15420Sstevel@tonic-gate /* 154311042SErik.Nordmark@Sun.COM * To make ill_hasmembers_nextzone_v6 work we keep the list 154411042SErik.Nordmark@Sun.COM * sorted by zoneid. 15450Sstevel@tonic-gate */ 15460Sstevel@tonic-gate ilm_cur = ill->ill_ilm; 15470Sstevel@tonic-gate ilm_ptpn = &ill->ill_ilm; 154811042SErik.Nordmark@Sun.COM while (ilm_cur != NULL && ilm_cur->ilm_zoneid < ilm->ilm_zoneid) { 15490Sstevel@tonic-gate ilm_ptpn = &ilm_cur->ilm_next; 15500Sstevel@tonic-gate ilm_cur = ilm_cur->ilm_next; 15510Sstevel@tonic-gate } 15520Sstevel@tonic-gate ilm->ilm_next = ilm_cur; 15530Sstevel@tonic-gate *ilm_ptpn = ilm; 15540Sstevel@tonic-gate 15550Sstevel@tonic-gate /* 15560Sstevel@tonic-gate * If we have an associated ilg, use its filter state; if not, 15570Sstevel@tonic-gate * default to (EXCLUDE, NULL) and set no_ilg_cnt to track this. 15580Sstevel@tonic-gate */ 15590Sstevel@tonic-gate if (ilgstat != ILGSTAT_NONE) { 15600Sstevel@tonic-gate if (!SLIST_IS_EMPTY(ilg_flist)) 15610Sstevel@tonic-gate l_copy(ilg_flist, ilm->ilm_filter); 15620Sstevel@tonic-gate ilm->ilm_fmode = ilg_fmode; 15630Sstevel@tonic-gate } else { 15640Sstevel@tonic-gate ilm->ilm_no_ilg_cnt = 1; 15650Sstevel@tonic-gate ilm->ilm_fmode = MODE_IS_EXCLUDE; 15660Sstevel@tonic-gate } 15670Sstevel@tonic-gate 15680Sstevel@tonic-gate return (ilm); 15690Sstevel@tonic-gate } 15700Sstevel@tonic-gate 15716763Ssowmini void 15726255Ssowmini ilm_inactive(ilm_t *ilm) 15736255Ssowmini { 15746255Ssowmini FREE_SLIST(ilm->ilm_filter); 15756255Ssowmini FREE_SLIST(ilm->ilm_pendsrcs); 15766255Ssowmini FREE_SLIST(ilm->ilm_rtx.rtx_allow); 15776255Ssowmini FREE_SLIST(ilm->ilm_rtx.rtx_block); 15786255Ssowmini ilm->ilm_ipst = NULL; 15796255Ssowmini mi_free((char *)ilm); 15806255Ssowmini } 15816255Ssowmini 15820Sstevel@tonic-gate /* 15830Sstevel@tonic-gate * Unlink ilm and free it. 15840Sstevel@tonic-gate */ 15850Sstevel@tonic-gate static void 15860Sstevel@tonic-gate ilm_delete(ilm_t *ilm) 15870Sstevel@tonic-gate { 158811042SErik.Nordmark@Sun.COM ill_t *ill = ilm->ilm_ill; 15896255Ssowmini ilm_t **ilmp; 15906255Ssowmini boolean_t need_wakeup; 15916255Ssowmini 15920Sstevel@tonic-gate /* 15930Sstevel@tonic-gate * Delete under lock protection so that readers don't stumble 15940Sstevel@tonic-gate * on bad ilm_next 15950Sstevel@tonic-gate */ 159611042SErik.Nordmark@Sun.COM ASSERT(RW_WRITE_HELD(&ill->ill_mcast_lock)); 15970Sstevel@tonic-gate 15980Sstevel@tonic-gate for (ilmp = &ill->ill_ilm; *ilmp != ilm; ilmp = &(*ilmp)->ilm_next) 159911042SErik.Nordmark@Sun.COM ; 160011042SErik.Nordmark@Sun.COM 16010Sstevel@tonic-gate *ilmp = ilm->ilm_next; 16026255Ssowmini 160311042SErik.Nordmark@Sun.COM mutex_enter(&ill->ill_lock); 16046255Ssowmini /* 160511042SErik.Nordmark@Sun.COM * if we are the last reference to the ill, we may need to wakeup any 160611042SErik.Nordmark@Sun.COM * pending FREE or unplumb operations. This is because conn_update_ill 160711042SErik.Nordmark@Sun.COM * bails if there is a ilg_delete_all in progress. 16086255Ssowmini */ 16096255Ssowmini need_wakeup = B_FALSE; 161011042SErik.Nordmark@Sun.COM DTRACE_PROBE3(ill__decr__cnt, (ill_t *), ill, 161111042SErik.Nordmark@Sun.COM (char *), "ilm", (void *), ilm); 161211042SErik.Nordmark@Sun.COM ASSERT(ill->ill_ilm_cnt > 0); 161311042SErik.Nordmark@Sun.COM ill->ill_ilm_cnt--; 161411042SErik.Nordmark@Sun.COM if (ILL_FREE_OK(ill)) 161511042SErik.Nordmark@Sun.COM need_wakeup = B_TRUE; 16166255Ssowmini 16176255Ssowmini ilm_inactive(ilm); /* frees this ilm */ 16186255Ssowmini 16196255Ssowmini if (need_wakeup) { 16206255Ssowmini /* drops ill lock */ 16216255Ssowmini ipif_ill_refrele_tail(ill); 16226255Ssowmini } else { 16236255Ssowmini mutex_exit(&ill->ill_lock); 16240Sstevel@tonic-gate } 16250Sstevel@tonic-gate } 16260Sstevel@tonic-gate 16278485SPeter.Memishian@Sun.COM /* 162811042SErik.Nordmark@Sun.COM * Lookup an ill based on the group, ifindex, ifaddr, and zoneid. 162911042SErik.Nordmark@Sun.COM * Applies to both IPv4 and IPv6, although ifaddr is only used with 163011042SErik.Nordmark@Sun.COM * IPv4. 163111042SErik.Nordmark@Sun.COM * Returns an error for IS_UNDER_IPMP and VNI interfaces. 163211042SErik.Nordmark@Sun.COM * On error it sets *errorp. 16338485SPeter.Memishian@Sun.COM */ 163411042SErik.Nordmark@Sun.COM static ill_t * 163511042SErik.Nordmark@Sun.COM ill_mcast_lookup(const in6_addr_t *group, ipaddr_t ifaddr, uint_t ifindex, 163611042SErik.Nordmark@Sun.COM zoneid_t zoneid, ip_stack_t *ipst, int *errorp) 16378485SPeter.Memishian@Sun.COM { 163811042SErik.Nordmark@Sun.COM ill_t *ill; 163911042SErik.Nordmark@Sun.COM ipaddr_t v4group; 164011042SErik.Nordmark@Sun.COM 164111042SErik.Nordmark@Sun.COM if (IN6_IS_ADDR_V4MAPPED(group)) { 164211042SErik.Nordmark@Sun.COM IN6_V4MAPPED_TO_IPADDR(group, v4group); 164311042SErik.Nordmark@Sun.COM 164411042SErik.Nordmark@Sun.COM if (ifindex != 0) { 164511042SErik.Nordmark@Sun.COM ill = ill_lookup_on_ifindex_zoneid(ifindex, zoneid, 164611042SErik.Nordmark@Sun.COM B_FALSE, ipst); 164711042SErik.Nordmark@Sun.COM } else if (ifaddr != INADDR_ANY) { 164811042SErik.Nordmark@Sun.COM ipif_t *ipif; 164911042SErik.Nordmark@Sun.COM 165011042SErik.Nordmark@Sun.COM ipif = ipif_lookup_addr(ifaddr, NULL, zoneid, ipst); 165111042SErik.Nordmark@Sun.COM if (ipif == NULL) { 165211042SErik.Nordmark@Sun.COM ill = NULL; 165311042SErik.Nordmark@Sun.COM } else { 165411042SErik.Nordmark@Sun.COM ill = ipif->ipif_ill; 165511042SErik.Nordmark@Sun.COM ill_refhold(ill); 165611042SErik.Nordmark@Sun.COM ipif_refrele(ipif); 165711042SErik.Nordmark@Sun.COM } 165811042SErik.Nordmark@Sun.COM } else { 165911042SErik.Nordmark@Sun.COM ill = ill_lookup_group_v4(v4group, zoneid, ipst, NULL, 166011042SErik.Nordmark@Sun.COM NULL); 166111042SErik.Nordmark@Sun.COM } 166211042SErik.Nordmark@Sun.COM } else { 166311042SErik.Nordmark@Sun.COM if (ifindex != 0) { 166411042SErik.Nordmark@Sun.COM ill = ill_lookup_on_ifindex_zoneid(ifindex, zoneid, 166511042SErik.Nordmark@Sun.COM B_TRUE, ipst); 166611042SErik.Nordmark@Sun.COM } else { 166711042SErik.Nordmark@Sun.COM ill = ill_lookup_group_v6(group, zoneid, ipst, NULL, 166811042SErik.Nordmark@Sun.COM NULL); 166911042SErik.Nordmark@Sun.COM } 16708485SPeter.Memishian@Sun.COM } 167111042SErik.Nordmark@Sun.COM if (ill == NULL) { 167211042SErik.Nordmark@Sun.COM if (ifindex != 0) 167311042SErik.Nordmark@Sun.COM *errorp = ENXIO; 167411042SErik.Nordmark@Sun.COM else 167511042SErik.Nordmark@Sun.COM *errorp = EADDRNOTAVAIL; 167611042SErik.Nordmark@Sun.COM return (NULL); 167711042SErik.Nordmark@Sun.COM } 167811042SErik.Nordmark@Sun.COM /* operation not supported on the virtual network interface */ 167911042SErik.Nordmark@Sun.COM if (IS_UNDER_IPMP(ill) || IS_VNI(ill)) { 168011042SErik.Nordmark@Sun.COM ill_refrele(ill); 168111042SErik.Nordmark@Sun.COM *errorp = EINVAL; 168211042SErik.Nordmark@Sun.COM return (NULL); 168311042SErik.Nordmark@Sun.COM } 168411042SErik.Nordmark@Sun.COM return (ill); 16858485SPeter.Memishian@Sun.COM } 16868485SPeter.Memishian@Sun.COM 16878485SPeter.Memishian@Sun.COM /* 168811042SErik.Nordmark@Sun.COM * Looks up the appropriate ill given an interface index (or interface address) 168911042SErik.Nordmark@Sun.COM * and multicast group. On success, returns 0, with *illpp pointing to the 169011042SErik.Nordmark@Sun.COM * found struct. On failure, returns an errno and *illpp is set to NULL. 169111042SErik.Nordmark@Sun.COM * 169211042SErik.Nordmark@Sun.COM * Returns an error for IS_UNDER_IPMP and VNI interfaces. 169311042SErik.Nordmark@Sun.COM * 169411042SErik.Nordmark@Sun.COM * Handles both IPv4 and IPv6. The ifaddr argument only applies in the 169511042SErik.Nordmark@Sun.COM * case of IPv4. 16960Sstevel@tonic-gate */ 16970Sstevel@tonic-gate int 169811042SErik.Nordmark@Sun.COM ip_opt_check(conn_t *connp, const in6_addr_t *v6group, 169911042SErik.Nordmark@Sun.COM const in6_addr_t *v6src, ipaddr_t ifaddr, uint_t ifindex, ill_t **illpp) 17000Sstevel@tonic-gate { 17010Sstevel@tonic-gate boolean_t src_unspec; 17020Sstevel@tonic-gate ill_t *ill = NULL; 17033448Sdh155122 ip_stack_t *ipst = connp->conn_netstack->netstack_ip; 170411042SErik.Nordmark@Sun.COM int error = 0; 170511042SErik.Nordmark@Sun.COM 170611042SErik.Nordmark@Sun.COM *illpp = NULL; 17070Sstevel@tonic-gate 17080Sstevel@tonic-gate src_unspec = IN6_IS_ADDR_UNSPECIFIED(v6src); 17090Sstevel@tonic-gate 17100Sstevel@tonic-gate if (IN6_IS_ADDR_V4MAPPED(v6group)) { 171111042SErik.Nordmark@Sun.COM ipaddr_t v4group; 171211042SErik.Nordmark@Sun.COM ipaddr_t v4src; 171311042SErik.Nordmark@Sun.COM 17140Sstevel@tonic-gate if (!IN6_IS_ADDR_V4MAPPED(v6src) && !src_unspec) 17150Sstevel@tonic-gate return (EINVAL); 171611042SErik.Nordmark@Sun.COM IN6_V4MAPPED_TO_IPADDR(v6group, v4group); 17170Sstevel@tonic-gate if (src_unspec) { 171811042SErik.Nordmark@Sun.COM v4src = INADDR_ANY; 17190Sstevel@tonic-gate } else { 172011042SErik.Nordmark@Sun.COM IN6_V4MAPPED_TO_IPADDR(v6src, v4src); 17210Sstevel@tonic-gate } 172211042SErik.Nordmark@Sun.COM if (!CLASSD(v4group) || CLASSD(v4src)) 17230Sstevel@tonic-gate return (EINVAL); 17240Sstevel@tonic-gate } else { 17250Sstevel@tonic-gate if (IN6_IS_ADDR_V4MAPPED(v6src) && !src_unspec) 17260Sstevel@tonic-gate return (EINVAL); 17270Sstevel@tonic-gate if (!IN6_IS_ADDR_MULTICAST(v6group) || 17280Sstevel@tonic-gate IN6_IS_ADDR_MULTICAST(v6src)) { 17290Sstevel@tonic-gate return (EINVAL); 17300Sstevel@tonic-gate } 17310Sstevel@tonic-gate } 17320Sstevel@tonic-gate 173311042SErik.Nordmark@Sun.COM ill = ill_mcast_lookup(v6group, ifaddr, ifindex, IPCL_ZONEID(connp), 173411042SErik.Nordmark@Sun.COM ipst, &error); 17350Sstevel@tonic-gate *illpp = ill; 173611042SErik.Nordmark@Sun.COM return (error); 17370Sstevel@tonic-gate } 17380Sstevel@tonic-gate 17390Sstevel@tonic-gate static int 17400Sstevel@tonic-gate ip_get_srcfilter(conn_t *connp, struct group_filter *gf, 174111042SErik.Nordmark@Sun.COM struct ip_msfilter *imsf, const struct in6_addr *group, boolean_t issin6) 17420Sstevel@tonic-gate { 17430Sstevel@tonic-gate ilg_t *ilg; 17440Sstevel@tonic-gate int i, numsrc, fmode, outsrcs; 17450Sstevel@tonic-gate struct sockaddr_in *sin; 17460Sstevel@tonic-gate struct sockaddr_in6 *sin6; 17470Sstevel@tonic-gate struct in_addr *addrp; 17480Sstevel@tonic-gate slist_t *fp; 17490Sstevel@tonic-gate boolean_t is_v4only_api; 175011042SErik.Nordmark@Sun.COM ipaddr_t ifaddr; 175111042SErik.Nordmark@Sun.COM uint_t ifindex; 17520Sstevel@tonic-gate 17530Sstevel@tonic-gate if (gf == NULL) { 17540Sstevel@tonic-gate ASSERT(imsf != NULL); 175511042SErik.Nordmark@Sun.COM ASSERT(!issin6); 17560Sstevel@tonic-gate is_v4only_api = B_TRUE; 17570Sstevel@tonic-gate outsrcs = imsf->imsf_numsrc; 175811042SErik.Nordmark@Sun.COM ifaddr = imsf->imsf_interface.s_addr; 175911042SErik.Nordmark@Sun.COM ifindex = 0; 17600Sstevel@tonic-gate } else { 17610Sstevel@tonic-gate ASSERT(imsf == NULL); 17620Sstevel@tonic-gate is_v4only_api = B_FALSE; 17630Sstevel@tonic-gate outsrcs = gf->gf_numsrc; 176411042SErik.Nordmark@Sun.COM ifaddr = INADDR_ANY; 176511042SErik.Nordmark@Sun.COM ifindex = gf->gf_interface; 176611042SErik.Nordmark@Sun.COM } 176711042SErik.Nordmark@Sun.COM 176811042SErik.Nordmark@Sun.COM /* No need to use ill_mcast_serializer for the reader */ 176911042SErik.Nordmark@Sun.COM rw_enter(&connp->conn_ilg_lock, RW_READER); 177011042SErik.Nordmark@Sun.COM ilg = ilg_lookup(connp, group, ifaddr, ifindex); 177111042SErik.Nordmark@Sun.COM if (ilg == NULL) { 177211042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 177311042SErik.Nordmark@Sun.COM return (EADDRNOTAVAIL); 17740Sstevel@tonic-gate } 17750Sstevel@tonic-gate 17760Sstevel@tonic-gate /* 17770Sstevel@tonic-gate * In the kernel, we use the state definitions MODE_IS_[IN|EX]CLUDE 17780Sstevel@tonic-gate * to identify the filter mode; but the API uses MCAST_[IN|EX]CLUDE. 17790Sstevel@tonic-gate * So we need to translate here. 17800Sstevel@tonic-gate */ 17810Sstevel@tonic-gate fmode = (ilg->ilg_fmode == MODE_IS_INCLUDE) ? 17820Sstevel@tonic-gate MCAST_INCLUDE : MCAST_EXCLUDE; 17830Sstevel@tonic-gate if ((fp = ilg->ilg_filter) == NULL) { 17840Sstevel@tonic-gate numsrc = 0; 17850Sstevel@tonic-gate } else { 17860Sstevel@tonic-gate for (i = 0; i < outsrcs; i++) { 17870Sstevel@tonic-gate if (i == fp->sl_numsrc) 17880Sstevel@tonic-gate break; 178911042SErik.Nordmark@Sun.COM if (issin6) { 17900Sstevel@tonic-gate sin6 = (struct sockaddr_in6 *)&gf->gf_slist[i]; 17910Sstevel@tonic-gate sin6->sin6_family = AF_INET6; 17920Sstevel@tonic-gate sin6->sin6_addr = fp->sl_addr[i]; 17930Sstevel@tonic-gate } else { 17940Sstevel@tonic-gate if (is_v4only_api) { 17950Sstevel@tonic-gate addrp = &imsf->imsf_slist[i]; 17960Sstevel@tonic-gate } else { 17970Sstevel@tonic-gate sin = (struct sockaddr_in *) 17980Sstevel@tonic-gate &gf->gf_slist[i]; 17990Sstevel@tonic-gate sin->sin_family = AF_INET; 18000Sstevel@tonic-gate addrp = &sin->sin_addr; 18010Sstevel@tonic-gate } 18020Sstevel@tonic-gate IN6_V4MAPPED_TO_INADDR(&fp->sl_addr[i], addrp); 18030Sstevel@tonic-gate } 18040Sstevel@tonic-gate } 18050Sstevel@tonic-gate numsrc = fp->sl_numsrc; 18060Sstevel@tonic-gate } 18070Sstevel@tonic-gate 18080Sstevel@tonic-gate if (is_v4only_api) { 18090Sstevel@tonic-gate imsf->imsf_numsrc = numsrc; 18100Sstevel@tonic-gate imsf->imsf_fmode = fmode; 18110Sstevel@tonic-gate } else { 18120Sstevel@tonic-gate gf->gf_numsrc = numsrc; 18130Sstevel@tonic-gate gf->gf_fmode = fmode; 18140Sstevel@tonic-gate } 18150Sstevel@tonic-gate 181611042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 18170Sstevel@tonic-gate 18180Sstevel@tonic-gate return (0); 18190Sstevel@tonic-gate } 18200Sstevel@tonic-gate 182111042SErik.Nordmark@Sun.COM /* 182211042SErik.Nordmark@Sun.COM * Common for IPv4 and IPv6. 182311042SErik.Nordmark@Sun.COM */ 18240Sstevel@tonic-gate static int 18250Sstevel@tonic-gate ip_set_srcfilter(conn_t *connp, struct group_filter *gf, 182611042SErik.Nordmark@Sun.COM struct ip_msfilter *imsf, const struct in6_addr *group, ill_t *ill, 182711042SErik.Nordmark@Sun.COM boolean_t issin6) 18280Sstevel@tonic-gate { 18290Sstevel@tonic-gate ilg_t *ilg; 18306827Sblu int i, err, infmode, new_fmode; 18316827Sblu uint_t insrcs; 18320Sstevel@tonic-gate struct sockaddr_in *sin; 18330Sstevel@tonic-gate struct sockaddr_in6 *sin6; 18340Sstevel@tonic-gate struct in_addr *addrp; 18350Sstevel@tonic-gate slist_t *orig_filter = NULL; 18360Sstevel@tonic-gate slist_t *new_filter = NULL; 18370Sstevel@tonic-gate mcast_record_t orig_fmode; 183811042SErik.Nordmark@Sun.COM boolean_t leave_group, is_v4only_api; 18390Sstevel@tonic-gate ilg_stat_t ilgstat; 184011042SErik.Nordmark@Sun.COM ilm_t *ilm; 184111042SErik.Nordmark@Sun.COM ipaddr_t ifaddr; 184211042SErik.Nordmark@Sun.COM uint_t ifindex; 18430Sstevel@tonic-gate 18440Sstevel@tonic-gate if (gf == NULL) { 18450Sstevel@tonic-gate ASSERT(imsf != NULL); 184611042SErik.Nordmark@Sun.COM ASSERT(!issin6); 18470Sstevel@tonic-gate is_v4only_api = B_TRUE; 18480Sstevel@tonic-gate insrcs = imsf->imsf_numsrc; 18490Sstevel@tonic-gate infmode = imsf->imsf_fmode; 185011042SErik.Nordmark@Sun.COM ifaddr = imsf->imsf_interface.s_addr; 185111042SErik.Nordmark@Sun.COM ifindex = 0; 18520Sstevel@tonic-gate } else { 18530Sstevel@tonic-gate ASSERT(imsf == NULL); 18540Sstevel@tonic-gate is_v4only_api = B_FALSE; 18550Sstevel@tonic-gate insrcs = gf->gf_numsrc; 18560Sstevel@tonic-gate infmode = gf->gf_fmode; 185711042SErik.Nordmark@Sun.COM ifaddr = INADDR_ANY; 185811042SErik.Nordmark@Sun.COM ifindex = gf->gf_interface; 18590Sstevel@tonic-gate } 18600Sstevel@tonic-gate 18610Sstevel@tonic-gate /* Make sure we can handle the source list */ 18620Sstevel@tonic-gate if (insrcs > MAX_FILTER_SIZE) 18630Sstevel@tonic-gate return (ENOBUFS); 18640Sstevel@tonic-gate 18650Sstevel@tonic-gate /* 18660Sstevel@tonic-gate * setting the filter to (INCLUDE, NULL) is treated 18670Sstevel@tonic-gate * as a request to leave the group. 18680Sstevel@tonic-gate */ 186911042SErik.Nordmark@Sun.COM leave_group = (infmode == MCAST_INCLUDE && insrcs == 0); 187011042SErik.Nordmark@Sun.COM 187111042SErik.Nordmark@Sun.COM mutex_enter(&ill->ill_mcast_serializer); 187211042SErik.Nordmark@Sun.COM rw_enter(&connp->conn_ilg_lock, RW_WRITER); 187311042SErik.Nordmark@Sun.COM ilg = ilg_lookup(connp, group, ifaddr, ifindex); 18740Sstevel@tonic-gate if (ilg == NULL) { 18750Sstevel@tonic-gate /* 18760Sstevel@tonic-gate * if the request was actually to leave, and we 18770Sstevel@tonic-gate * didn't find an ilg, there's nothing to do. 18780Sstevel@tonic-gate */ 187911042SErik.Nordmark@Sun.COM if (leave_group) { 188011042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 188111042SErik.Nordmark@Sun.COM mutex_exit(&ill->ill_mcast_serializer); 188211042SErik.Nordmark@Sun.COM return (0); 188311042SErik.Nordmark@Sun.COM } 188411042SErik.Nordmark@Sun.COM ilg = conn_ilg_alloc(connp, &err); 188511042SErik.Nordmark@Sun.COM if (ilg == NULL) { 188611042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 188711042SErik.Nordmark@Sun.COM mutex_exit(&ill->ill_mcast_serializer); 188811042SErik.Nordmark@Sun.COM return (err); 18890Sstevel@tonic-gate } 18900Sstevel@tonic-gate ilgstat = ILGSTAT_NEW; 189111042SErik.Nordmark@Sun.COM ilg->ilg_v6group = *group; 189211042SErik.Nordmark@Sun.COM ilg->ilg_ill = ill; 189311042SErik.Nordmark@Sun.COM ilg->ilg_ifaddr = ifaddr; 189411042SErik.Nordmark@Sun.COM ilg->ilg_ifindex = ifindex; 189511042SErik.Nordmark@Sun.COM } else if (leave_group) { 189611042SErik.Nordmark@Sun.COM /* 189711042SErik.Nordmark@Sun.COM * Make sure we have the correct serializer. The ill argument 189811042SErik.Nordmark@Sun.COM * might not match ilg_ill. 189911042SErik.Nordmark@Sun.COM */ 190011042SErik.Nordmark@Sun.COM ilg_refhold(ilg); 190111042SErik.Nordmark@Sun.COM mutex_exit(&ill->ill_mcast_serializer); 190211042SErik.Nordmark@Sun.COM ill = ilg->ilg_ill; 190311042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 190411042SErik.Nordmark@Sun.COM 190511042SErik.Nordmark@Sun.COM mutex_enter(&ill->ill_mcast_serializer); 190611042SErik.Nordmark@Sun.COM rw_enter(&connp->conn_ilg_lock, RW_WRITER); 190711042SErik.Nordmark@Sun.COM ilm = ilg->ilg_ilm; 190811042SErik.Nordmark@Sun.COM ilg->ilg_ilm = NULL; 19090Sstevel@tonic-gate ilg_delete(connp, ilg, NULL); 191011042SErik.Nordmark@Sun.COM ilg_refrele(ilg); 191111042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 191211042SErik.Nordmark@Sun.COM if (ilm != NULL) 191311042SErik.Nordmark@Sun.COM (void) ip_delmulti_serial(ilm, B_FALSE, B_TRUE); 191411042SErik.Nordmark@Sun.COM mutex_exit(&ill->ill_mcast_serializer); 1915*11463SSowmini.Varadhan@Sun.COM /* 1916*11463SSowmini.Varadhan@Sun.COM * Now that all locks have been dropped, we can send any 1917*11463SSowmini.Varadhan@Sun.COM * deferred/queued DLPI or IP packets 1918*11463SSowmini.Varadhan@Sun.COM */ 1919*11463SSowmini.Varadhan@Sun.COM ill_mcast_send_queued(ill); 1920*11463SSowmini.Varadhan@Sun.COM ill_dlpi_send_queued(ill); 19210Sstevel@tonic-gate return (0); 19220Sstevel@tonic-gate } else { 19230Sstevel@tonic-gate ilgstat = ILGSTAT_CHANGE; 19240Sstevel@tonic-gate /* Preserve existing state in case ip_addmulti() fails */ 19250Sstevel@tonic-gate orig_fmode = ilg->ilg_fmode; 19260Sstevel@tonic-gate if (ilg->ilg_filter == NULL) { 19270Sstevel@tonic-gate orig_filter = NULL; 19280Sstevel@tonic-gate } else { 19290Sstevel@tonic-gate orig_filter = l_alloc_copy(ilg->ilg_filter); 19300Sstevel@tonic-gate if (orig_filter == NULL) { 193111042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 193211042SErik.Nordmark@Sun.COM mutex_exit(&ill->ill_mcast_serializer); 19330Sstevel@tonic-gate return (ENOMEM); 19340Sstevel@tonic-gate } 19350Sstevel@tonic-gate } 19360Sstevel@tonic-gate } 19370Sstevel@tonic-gate 19380Sstevel@tonic-gate /* 19390Sstevel@tonic-gate * Alloc buffer to copy new state into (see below) before 19400Sstevel@tonic-gate * we make any changes, so we can bail if it fails. 19410Sstevel@tonic-gate */ 19420Sstevel@tonic-gate if ((new_filter = l_alloc()) == NULL) { 194311042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 19440Sstevel@tonic-gate err = ENOMEM; 19450Sstevel@tonic-gate goto free_and_exit; 19460Sstevel@tonic-gate } 19470Sstevel@tonic-gate 19480Sstevel@tonic-gate if (insrcs == 0) { 19490Sstevel@tonic-gate CLEAR_SLIST(ilg->ilg_filter); 19500Sstevel@tonic-gate } else { 19510Sstevel@tonic-gate slist_t *fp; 19520Sstevel@tonic-gate if (ilg->ilg_filter == NULL) { 19530Sstevel@tonic-gate fp = l_alloc(); 19540Sstevel@tonic-gate if (fp == NULL) { 19550Sstevel@tonic-gate if (ilgstat == ILGSTAT_NEW) 19560Sstevel@tonic-gate ilg_delete(connp, ilg, NULL); 195711042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 19580Sstevel@tonic-gate err = ENOMEM; 19590Sstevel@tonic-gate goto free_and_exit; 19600Sstevel@tonic-gate } 19610Sstevel@tonic-gate } else { 19620Sstevel@tonic-gate fp = ilg->ilg_filter; 19630Sstevel@tonic-gate } 19640Sstevel@tonic-gate for (i = 0; i < insrcs; i++) { 196511042SErik.Nordmark@Sun.COM if (issin6) { 19660Sstevel@tonic-gate sin6 = (struct sockaddr_in6 *)&gf->gf_slist[i]; 19670Sstevel@tonic-gate fp->sl_addr[i] = sin6->sin6_addr; 19680Sstevel@tonic-gate } else { 19690Sstevel@tonic-gate if (is_v4only_api) { 19700Sstevel@tonic-gate addrp = &imsf->imsf_slist[i]; 19710Sstevel@tonic-gate } else { 19720Sstevel@tonic-gate sin = (struct sockaddr_in *) 19730Sstevel@tonic-gate &gf->gf_slist[i]; 19740Sstevel@tonic-gate addrp = &sin->sin_addr; 19750Sstevel@tonic-gate } 19760Sstevel@tonic-gate IN6_INADDR_TO_V4MAPPED(addrp, &fp->sl_addr[i]); 19770Sstevel@tonic-gate } 19780Sstevel@tonic-gate } 19790Sstevel@tonic-gate fp->sl_numsrc = insrcs; 19800Sstevel@tonic-gate ilg->ilg_filter = fp; 19810Sstevel@tonic-gate } 19820Sstevel@tonic-gate /* 19830Sstevel@tonic-gate * In the kernel, we use the state definitions MODE_IS_[IN|EX]CLUDE 19840Sstevel@tonic-gate * to identify the filter mode; but the API uses MCAST_[IN|EX]CLUDE. 19850Sstevel@tonic-gate * So we need to translate here. 19860Sstevel@tonic-gate */ 19870Sstevel@tonic-gate ilg->ilg_fmode = (infmode == MCAST_INCLUDE) ? 19884459Skcpoon MODE_IS_INCLUDE : MODE_IS_EXCLUDE; 19890Sstevel@tonic-gate 19900Sstevel@tonic-gate /* 19910Sstevel@tonic-gate * Save copy of ilg's filter state to pass to other functions, 199211042SErik.Nordmark@Sun.COM * so we can release conn_ilg_lock now. 19930Sstevel@tonic-gate */ 19940Sstevel@tonic-gate new_fmode = ilg->ilg_fmode; 19950Sstevel@tonic-gate l_copy(ilg->ilg_filter, new_filter); 19960Sstevel@tonic-gate 199711042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 199811042SErik.Nordmark@Sun.COM 199911042SErik.Nordmark@Sun.COM /* 200011042SErik.Nordmark@Sun.COM * Now update the ill. We wait to do this until after the ilg 200111042SErik.Nordmark@Sun.COM * has been updated because we need to update the src filter 200211042SErik.Nordmark@Sun.COM * info for the ill, which involves looking at the status of 200311042SErik.Nordmark@Sun.COM * all the ilgs associated with this group/interface pair. 200411042SErik.Nordmark@Sun.COM */ 200511042SErik.Nordmark@Sun.COM ilm = ip_addmulti_serial(group, ill, connp->conn_zoneid, ilgstat, 200611042SErik.Nordmark@Sun.COM new_fmode, new_filter, &err); 200711042SErik.Nordmark@Sun.COM 200811042SErik.Nordmark@Sun.COM rw_enter(&connp->conn_ilg_lock, RW_WRITER); 200911042SErik.Nordmark@Sun.COM /* 201011042SErik.Nordmark@Sun.COM * Must look up the ilg again since we've not been holding 201111042SErik.Nordmark@Sun.COM * conn_ilg_lock. The ilg could have disappeared due to an unplumb 201211042SErik.Nordmark@Sun.COM * having called conn_update_ill, which can run once we dropped the 201311042SErik.Nordmark@Sun.COM * conn_ilg_lock above. 201411042SErik.Nordmark@Sun.COM */ 201511042SErik.Nordmark@Sun.COM ilg = ilg_lookup(connp, group, ifaddr, ifindex); 201611042SErik.Nordmark@Sun.COM if (ilg == NULL) { 201711042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 201811042SErik.Nordmark@Sun.COM if (ilm != NULL) { 201911042SErik.Nordmark@Sun.COM (void) ip_delmulti_serial(ilm, B_FALSE, 202011042SErik.Nordmark@Sun.COM (ilgstat == ILGSTAT_NEW)); 202111042SErik.Nordmark@Sun.COM } 202211042SErik.Nordmark@Sun.COM err = ENXIO; 202311042SErik.Nordmark@Sun.COM goto free_and_exit; 202411042SErik.Nordmark@Sun.COM } 202511042SErik.Nordmark@Sun.COM 202611042SErik.Nordmark@Sun.COM if (ilm != NULL) { 2027*11463SSowmini.Varadhan@Sun.COM if (ilg->ilg_ill == NULL) { 2028*11463SSowmini.Varadhan@Sun.COM /* some other thread is re-attaching this. */ 2029*11463SSowmini.Varadhan@Sun.COM rw_exit(&connp->conn_ilg_lock); 2030*11463SSowmini.Varadhan@Sun.COM (void) ip_delmulti_serial(ilm, B_FALSE, 2031*11463SSowmini.Varadhan@Sun.COM (ilgstat == ILGSTAT_NEW)); 2032*11463SSowmini.Varadhan@Sun.COM err = 0; 2033*11463SSowmini.Varadhan@Sun.COM goto free_and_exit; 2034*11463SSowmini.Varadhan@Sun.COM } 203511042SErik.Nordmark@Sun.COM /* Succeeded. Update the ilg to point at the ilm */ 203611042SErik.Nordmark@Sun.COM if (ilgstat == ILGSTAT_NEW) { 2037*11463SSowmini.Varadhan@Sun.COM if (ilg->ilg_ilm == NULL) { 2038*11463SSowmini.Varadhan@Sun.COM ilg->ilg_ilm = ilm; 2039*11463SSowmini.Varadhan@Sun.COM ilm->ilm_ifaddr = ifaddr; /* For netstat */ 2040*11463SSowmini.Varadhan@Sun.COM } else { 2041*11463SSowmini.Varadhan@Sun.COM /* some other thread is re-attaching this. */ 2042*11463SSowmini.Varadhan@Sun.COM rw_exit(&connp->conn_ilg_lock); 2043*11463SSowmini.Varadhan@Sun.COM (void) ip_delmulti_serial(ilm, B_FALSE, B_TRUE); 2044*11463SSowmini.Varadhan@Sun.COM err = 0; 2045*11463SSowmini.Varadhan@Sun.COM goto free_and_exit; 2046*11463SSowmini.Varadhan@Sun.COM } 204711042SErik.Nordmark@Sun.COM } else { 204811042SErik.Nordmark@Sun.COM /* 204911042SErik.Nordmark@Sun.COM * ip_addmulti didn't get a held ilm for 205011042SErik.Nordmark@Sun.COM * ILGSTAT_CHANGE; ilm_refcnt was unchanged. 205111042SErik.Nordmark@Sun.COM */ 205211042SErik.Nordmark@Sun.COM ASSERT(ilg->ilg_ilm == ilm); 205311042SErik.Nordmark@Sun.COM } 205411042SErik.Nordmark@Sun.COM } else { 205511042SErik.Nordmark@Sun.COM ASSERT(err != 0); 20560Sstevel@tonic-gate /* 205711042SErik.Nordmark@Sun.COM * Failed to allocate the ilm. 20580Sstevel@tonic-gate * Restore the original filter state, or delete the 205911042SErik.Nordmark@Sun.COM * newly-created ilg. 206011042SErik.Nordmark@Sun.COM * If ENETDOWN just clear ill_ilg since so that we 206111042SErik.Nordmark@Sun.COM * will rejoin when the ill comes back; don't report ENETDOWN 206211042SErik.Nordmark@Sun.COM * to application. 20630Sstevel@tonic-gate */ 20640Sstevel@tonic-gate if (ilgstat == ILGSTAT_NEW) { 206511042SErik.Nordmark@Sun.COM if (err == ENETDOWN) { 206611042SErik.Nordmark@Sun.COM ilg->ilg_ill = NULL; 206711042SErik.Nordmark@Sun.COM err = 0; 206811042SErik.Nordmark@Sun.COM } else { 206911042SErik.Nordmark@Sun.COM ilg_delete(connp, ilg, NULL); 207011042SErik.Nordmark@Sun.COM } 20710Sstevel@tonic-gate } else { 20720Sstevel@tonic-gate ilg->ilg_fmode = orig_fmode; 20730Sstevel@tonic-gate if (SLIST_IS_EMPTY(orig_filter)) { 20740Sstevel@tonic-gate CLEAR_SLIST(ilg->ilg_filter); 20750Sstevel@tonic-gate } else { 20760Sstevel@tonic-gate /* 20770Sstevel@tonic-gate * We didn't free the filter, even if we 20780Sstevel@tonic-gate * were trying to make the source list empty; 20790Sstevel@tonic-gate * so if orig_filter isn't empty, the ilg 20800Sstevel@tonic-gate * must still have a filter alloc'd. 20810Sstevel@tonic-gate */ 20820Sstevel@tonic-gate l_copy(orig_filter, ilg->ilg_filter); 20830Sstevel@tonic-gate } 20840Sstevel@tonic-gate } 20850Sstevel@tonic-gate } 208611042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 20870Sstevel@tonic-gate 20880Sstevel@tonic-gate free_and_exit: 208911042SErik.Nordmark@Sun.COM mutex_exit(&ill->ill_mcast_serializer); 2090*11463SSowmini.Varadhan@Sun.COM ill_mcast_send_queued(ill); 2091*11463SSowmini.Varadhan@Sun.COM ill_dlpi_send_queued(ill); 20920Sstevel@tonic-gate l_free(orig_filter); 20930Sstevel@tonic-gate l_free(new_filter); 20940Sstevel@tonic-gate 20950Sstevel@tonic-gate return (err); 20960Sstevel@tonic-gate } 20970Sstevel@tonic-gate 20980Sstevel@tonic-gate /* 20990Sstevel@tonic-gate * Process the SIOC[GS]MSFILTER and SIOC[GS]IPMSFILTER ioctls. 21000Sstevel@tonic-gate */ 21010Sstevel@tonic-gate /* ARGSUSED */ 21020Sstevel@tonic-gate int 21030Sstevel@tonic-gate ip_sioctl_msfilter(ipif_t *ipif, sin_t *dummy_sin, queue_t *q, mblk_t *mp, 21040Sstevel@tonic-gate ip_ioctl_cmd_t *ipip, void *ifreq) 21050Sstevel@tonic-gate { 21060Sstevel@tonic-gate struct iocblk *iocp = (struct iocblk *)mp->b_rptr; 21070Sstevel@tonic-gate /* existence verified in ip_wput_nondata() */ 21080Sstevel@tonic-gate mblk_t *data_mp = mp->b_cont->b_cont; 21090Sstevel@tonic-gate int datalen, err, cmd, minsize; 21106827Sblu uint_t expsize = 0; 21110Sstevel@tonic-gate conn_t *connp; 21120Sstevel@tonic-gate boolean_t isv6, is_v4only_api, getcmd; 21130Sstevel@tonic-gate struct sockaddr_in *gsin; 21140Sstevel@tonic-gate struct sockaddr_in6 *gsin6; 211511042SErik.Nordmark@Sun.COM ipaddr_t v4group; 211611042SErik.Nordmark@Sun.COM in6_addr_t v6group; 21170Sstevel@tonic-gate struct group_filter *gf = NULL; 21180Sstevel@tonic-gate struct ip_msfilter *imsf = NULL; 21190Sstevel@tonic-gate mblk_t *ndp; 212011042SErik.Nordmark@Sun.COM ill_t *ill; 212111042SErik.Nordmark@Sun.COM 212211042SErik.Nordmark@Sun.COM connp = Q_TO_CONN(q); 212311042SErik.Nordmark@Sun.COM err = ip_msfilter_ill(connp, mp, ipip, &ill); 212411042SErik.Nordmark@Sun.COM if (err != 0) 212511042SErik.Nordmark@Sun.COM return (err); 21260Sstevel@tonic-gate 21270Sstevel@tonic-gate if (data_mp->b_cont != NULL) { 21280Sstevel@tonic-gate if ((ndp = msgpullup(data_mp, -1)) == NULL) 21290Sstevel@tonic-gate return (ENOMEM); 21300Sstevel@tonic-gate freemsg(data_mp); 21310Sstevel@tonic-gate data_mp = ndp; 21320Sstevel@tonic-gate mp->b_cont->b_cont = data_mp; 21330Sstevel@tonic-gate } 21340Sstevel@tonic-gate 21350Sstevel@tonic-gate cmd = iocp->ioc_cmd; 21360Sstevel@tonic-gate getcmd = (cmd == SIOCGIPMSFILTER || cmd == SIOCGMSFILTER); 21370Sstevel@tonic-gate is_v4only_api = (cmd == SIOCGIPMSFILTER || cmd == SIOCSIPMSFILTER); 21380Sstevel@tonic-gate minsize = (is_v4only_api) ? IP_MSFILTER_SIZE(0) : GROUP_FILTER_SIZE(0); 21390Sstevel@tonic-gate datalen = MBLKL(data_mp); 21400Sstevel@tonic-gate 21410Sstevel@tonic-gate if (datalen < minsize) 21420Sstevel@tonic-gate return (EINVAL); 21430Sstevel@tonic-gate 21440Sstevel@tonic-gate /* 21450Sstevel@tonic-gate * now we know we have at least have the initial structure, 21460Sstevel@tonic-gate * but need to check for the source list array. 21470Sstevel@tonic-gate */ 21480Sstevel@tonic-gate if (is_v4only_api) { 21490Sstevel@tonic-gate imsf = (struct ip_msfilter *)data_mp->b_rptr; 21500Sstevel@tonic-gate isv6 = B_FALSE; 21510Sstevel@tonic-gate expsize = IP_MSFILTER_SIZE(imsf->imsf_numsrc); 21520Sstevel@tonic-gate } else { 21530Sstevel@tonic-gate gf = (struct group_filter *)data_mp->b_rptr; 21540Sstevel@tonic-gate if (gf->gf_group.ss_family == AF_INET6) { 21550Sstevel@tonic-gate gsin6 = (struct sockaddr_in6 *)&gf->gf_group; 21560Sstevel@tonic-gate isv6 = !(IN6_IS_ADDR_V4MAPPED(&gsin6->sin6_addr)); 21570Sstevel@tonic-gate } else { 21580Sstevel@tonic-gate isv6 = B_FALSE; 21590Sstevel@tonic-gate } 21600Sstevel@tonic-gate expsize = GROUP_FILTER_SIZE(gf->gf_numsrc); 21610Sstevel@tonic-gate } 21620Sstevel@tonic-gate if (datalen < expsize) 21630Sstevel@tonic-gate return (EINVAL); 21640Sstevel@tonic-gate 21650Sstevel@tonic-gate if (isv6) { 21660Sstevel@tonic-gate gsin6 = (struct sockaddr_in6 *)&gf->gf_group; 216711042SErik.Nordmark@Sun.COM v6group = gsin6->sin6_addr; 216811042SErik.Nordmark@Sun.COM if (getcmd) { 216911042SErik.Nordmark@Sun.COM err = ip_get_srcfilter(connp, gf, NULL, &v6group, 217011042SErik.Nordmark@Sun.COM B_TRUE); 217111042SErik.Nordmark@Sun.COM } else { 217211042SErik.Nordmark@Sun.COM err = ip_set_srcfilter(connp, gf, NULL, &v6group, ill, 217311042SErik.Nordmark@Sun.COM B_TRUE); 217411042SErik.Nordmark@Sun.COM } 21750Sstevel@tonic-gate } else { 217611042SErik.Nordmark@Sun.COM boolean_t issin6 = B_FALSE; 21770Sstevel@tonic-gate if (is_v4only_api) { 217811042SErik.Nordmark@Sun.COM v4group = (ipaddr_t)imsf->imsf_multiaddr.s_addr; 217911042SErik.Nordmark@Sun.COM IN6_IPADDR_TO_V4MAPPED(v4group, &v6group); 21800Sstevel@tonic-gate } else { 21810Sstevel@tonic-gate if (gf->gf_group.ss_family == AF_INET) { 21820Sstevel@tonic-gate gsin = (struct sockaddr_in *)&gf->gf_group; 218311042SErik.Nordmark@Sun.COM v4group = (ipaddr_t)gsin->sin_addr.s_addr; 218411042SErik.Nordmark@Sun.COM IN6_IPADDR_TO_V4MAPPED(v4group, &v6group); 21850Sstevel@tonic-gate } else { 21860Sstevel@tonic-gate gsin6 = (struct sockaddr_in6 *)&gf->gf_group; 21870Sstevel@tonic-gate IN6_V4MAPPED_TO_IPADDR(&gsin6->sin6_addr, 218811042SErik.Nordmark@Sun.COM v4group); 218911042SErik.Nordmark@Sun.COM issin6 = B_TRUE; 21900Sstevel@tonic-gate } 21910Sstevel@tonic-gate } 219211042SErik.Nordmark@Sun.COM /* 219311042SErik.Nordmark@Sun.COM * INADDR_ANY is represented as the IPv6 unspecifed addr. 219411042SErik.Nordmark@Sun.COM */ 219511042SErik.Nordmark@Sun.COM if (v4group == INADDR_ANY) 219611042SErik.Nordmark@Sun.COM v6group = ipv6_all_zeros; 21970Sstevel@tonic-gate else 219811042SErik.Nordmark@Sun.COM IN6_IPADDR_TO_V4MAPPED(v4group, &v6group); 219911042SErik.Nordmark@Sun.COM 220011042SErik.Nordmark@Sun.COM if (getcmd) { 220111042SErik.Nordmark@Sun.COM err = ip_get_srcfilter(connp, gf, imsf, &v6group, 220211042SErik.Nordmark@Sun.COM issin6); 220311042SErik.Nordmark@Sun.COM } else { 220411042SErik.Nordmark@Sun.COM err = ip_set_srcfilter(connp, gf, imsf, &v6group, ill, 220511042SErik.Nordmark@Sun.COM issin6); 220611042SErik.Nordmark@Sun.COM } 22070Sstevel@tonic-gate } 220811042SErik.Nordmark@Sun.COM ill_refrele(ill); 22090Sstevel@tonic-gate 22100Sstevel@tonic-gate return (err); 22110Sstevel@tonic-gate } 22120Sstevel@tonic-gate 22130Sstevel@tonic-gate /* 221411042SErik.Nordmark@Sun.COM * Determine the ill for the SIOC*MSFILTER ioctls 221511042SErik.Nordmark@Sun.COM * 221611042SErik.Nordmark@Sun.COM * Returns an error for IS_UNDER_IPMP interfaces. 221711042SErik.Nordmark@Sun.COM * 221811042SErik.Nordmark@Sun.COM * Finds the ill based on information in the ioctl headers. 22190Sstevel@tonic-gate */ 222011042SErik.Nordmark@Sun.COM static int 222111042SErik.Nordmark@Sun.COM ip_msfilter_ill(conn_t *connp, mblk_t *mp, const ip_ioctl_cmd_t *ipip, 222211042SErik.Nordmark@Sun.COM ill_t **illp) 22230Sstevel@tonic-gate { 22244972Smeem int cmd = ipip->ipi_cmd; 22254972Smeem int err = 0; 222611042SErik.Nordmark@Sun.COM ill_t *ill; 22270Sstevel@tonic-gate /* caller has verified this mblk exists */ 22280Sstevel@tonic-gate char *dbuf = (char *)mp->b_cont->b_cont->b_rptr; 22290Sstevel@tonic-gate struct ip_msfilter *imsf; 22300Sstevel@tonic-gate struct group_filter *gf; 223111042SErik.Nordmark@Sun.COM ipaddr_t v4addr, v4group; 223211042SErik.Nordmark@Sun.COM in6_addr_t v6group; 22330Sstevel@tonic-gate uint32_t index; 22343448Sdh155122 ip_stack_t *ipst; 22350Sstevel@tonic-gate 22363448Sdh155122 ipst = connp->conn_netstack->netstack_ip; 22370Sstevel@tonic-gate 223811042SErik.Nordmark@Sun.COM *illp = NULL; 223911042SErik.Nordmark@Sun.COM 22400Sstevel@tonic-gate /* don't allow multicast operations on a tcp conn */ 2241741Smasputra if (IPCL_IS_TCP(connp)) 22420Sstevel@tonic-gate return (ENOPROTOOPT); 22430Sstevel@tonic-gate 22440Sstevel@tonic-gate if (cmd == SIOCSIPMSFILTER || cmd == SIOCGIPMSFILTER) { 22450Sstevel@tonic-gate /* don't allow v4-specific ioctls on v6 socket */ 224611042SErik.Nordmark@Sun.COM if (connp->conn_family == AF_INET6) 22470Sstevel@tonic-gate return (EAFNOSUPPORT); 22480Sstevel@tonic-gate 22490Sstevel@tonic-gate imsf = (struct ip_msfilter *)dbuf; 22500Sstevel@tonic-gate v4addr = imsf->imsf_interface.s_addr; 225111042SErik.Nordmark@Sun.COM v4group = imsf->imsf_multiaddr.s_addr; 225211042SErik.Nordmark@Sun.COM IN6_IPADDR_TO_V4MAPPED(v4group, &v6group); 225311042SErik.Nordmark@Sun.COM ill = ill_mcast_lookup(&v6group, v4addr, 0, IPCL_ZONEID(connp), 225411042SErik.Nordmark@Sun.COM ipst, &err); 225511042SErik.Nordmark@Sun.COM if (ill == NULL && v4addr != INADDR_ANY) 225611042SErik.Nordmark@Sun.COM err = ENXIO; 22570Sstevel@tonic-gate } else { 22580Sstevel@tonic-gate gf = (struct group_filter *)dbuf; 22590Sstevel@tonic-gate index = gf->gf_interface; 22600Sstevel@tonic-gate if (gf->gf_group.ss_family == AF_INET6) { 22610Sstevel@tonic-gate struct sockaddr_in6 *sin6; 226211042SErik.Nordmark@Sun.COM 22630Sstevel@tonic-gate sin6 = (struct sockaddr_in6 *)&gf->gf_group; 226411042SErik.Nordmark@Sun.COM v6group = sin6->sin6_addr; 22650Sstevel@tonic-gate } else if (gf->gf_group.ss_family == AF_INET) { 22660Sstevel@tonic-gate struct sockaddr_in *sin; 226711042SErik.Nordmark@Sun.COM 22680Sstevel@tonic-gate sin = (struct sockaddr_in *)&gf->gf_group; 226911042SErik.Nordmark@Sun.COM v4group = sin->sin_addr.s_addr; 227011042SErik.Nordmark@Sun.COM IN6_IPADDR_TO_V4MAPPED(v4group, &v6group); 22710Sstevel@tonic-gate } else { 22720Sstevel@tonic-gate return (EAFNOSUPPORT); 22730Sstevel@tonic-gate } 227411042SErik.Nordmark@Sun.COM ill = ill_mcast_lookup(&v6group, INADDR_ANY, index, 227511042SErik.Nordmark@Sun.COM IPCL_ZONEID(connp), ipst, &err); 22760Sstevel@tonic-gate } 227711042SErik.Nordmark@Sun.COM *illp = ill; 22780Sstevel@tonic-gate return (err); 22790Sstevel@tonic-gate } 22800Sstevel@tonic-gate 22810Sstevel@tonic-gate /* 22820Sstevel@tonic-gate * The structures used for the SIOC*MSFILTER ioctls usually must be copied 22830Sstevel@tonic-gate * in in two stages, as the first copyin tells us the size of the attached 22840Sstevel@tonic-gate * source buffer. This function is called by ip_wput_nondata() after the 22850Sstevel@tonic-gate * first copyin has completed; it figures out how big the second stage 22860Sstevel@tonic-gate * needs to be, and kicks it off. 22870Sstevel@tonic-gate * 22880Sstevel@tonic-gate * In some cases (numsrc < 2), the second copyin is not needed as the 22890Sstevel@tonic-gate * first one gets a complete structure containing 1 source addr. 22900Sstevel@tonic-gate * 22910Sstevel@tonic-gate * The function returns 0 if a second copyin has been started (i.e. there's 22920Sstevel@tonic-gate * no more work to be done right now), or 1 if the second copyin is not 22930Sstevel@tonic-gate * needed and ip_wput_nondata() can continue its processing. 22940Sstevel@tonic-gate */ 22950Sstevel@tonic-gate int 22960Sstevel@tonic-gate ip_copyin_msfilter(queue_t *q, mblk_t *mp) 22970Sstevel@tonic-gate { 22980Sstevel@tonic-gate struct iocblk *iocp = (struct iocblk *)mp->b_rptr; 22990Sstevel@tonic-gate int cmd = iocp->ioc_cmd; 23000Sstevel@tonic-gate /* validity of this checked in ip_wput_nondata() */ 23010Sstevel@tonic-gate mblk_t *mp1 = mp->b_cont->b_cont; 23020Sstevel@tonic-gate int copysize = 0; 23030Sstevel@tonic-gate int offset; 23040Sstevel@tonic-gate 23050Sstevel@tonic-gate if (cmd == SIOCSMSFILTER || cmd == SIOCGMSFILTER) { 23060Sstevel@tonic-gate struct group_filter *gf = (struct group_filter *)mp1->b_rptr; 23070Sstevel@tonic-gate if (gf->gf_numsrc >= 2) { 23080Sstevel@tonic-gate offset = sizeof (struct group_filter); 23090Sstevel@tonic-gate copysize = GROUP_FILTER_SIZE(gf->gf_numsrc) - offset; 23100Sstevel@tonic-gate } 23110Sstevel@tonic-gate } else { 23120Sstevel@tonic-gate struct ip_msfilter *imsf = (struct ip_msfilter *)mp1->b_rptr; 23130Sstevel@tonic-gate if (imsf->imsf_numsrc >= 2) { 23140Sstevel@tonic-gate offset = sizeof (struct ip_msfilter); 23150Sstevel@tonic-gate copysize = IP_MSFILTER_SIZE(imsf->imsf_numsrc) - offset; 23160Sstevel@tonic-gate } 23170Sstevel@tonic-gate } 23180Sstevel@tonic-gate if (copysize > 0) { 23190Sstevel@tonic-gate mi_copyin_n(q, mp, offset, copysize); 23200Sstevel@tonic-gate return (0); 23210Sstevel@tonic-gate } 23220Sstevel@tonic-gate return (1); 23230Sstevel@tonic-gate } 23240Sstevel@tonic-gate 23250Sstevel@tonic-gate /* 23260Sstevel@tonic-gate * Handle the following optmgmt: 23270Sstevel@tonic-gate * IP_ADD_MEMBERSHIP must not have joined already 232811042SErik.Nordmark@Sun.COM * IPV6_JOIN_GROUP must not have joined already 23290Sstevel@tonic-gate * MCAST_JOIN_GROUP must not have joined already 23300Sstevel@tonic-gate * IP_BLOCK_SOURCE must have joined already 23310Sstevel@tonic-gate * MCAST_BLOCK_SOURCE must have joined already 23320Sstevel@tonic-gate * IP_JOIN_SOURCE_GROUP may have joined already 23330Sstevel@tonic-gate * MCAST_JOIN_SOURCE_GROUP may have joined already 23340Sstevel@tonic-gate * 23350Sstevel@tonic-gate * fmode and src parameters may be used to determine which option is 233611042SErik.Nordmark@Sun.COM * being set, as follows (IPV6_JOIN_GROUP and MCAST_JOIN_GROUP options 23370Sstevel@tonic-gate * are functionally equivalent): 233811042SErik.Nordmark@Sun.COM * opt fmode v6src 233911042SErik.Nordmark@Sun.COM * IP_ADD_MEMBERSHIP MODE_IS_EXCLUDE unspecified 234011042SErik.Nordmark@Sun.COM * IPV6_JOIN_GROUP MODE_IS_EXCLUDE unspecified 234111042SErik.Nordmark@Sun.COM * MCAST_JOIN_GROUP MODE_IS_EXCLUDE unspecified 234211042SErik.Nordmark@Sun.COM * IP_BLOCK_SOURCE MODE_IS_EXCLUDE IPv4-mapped addr 234311042SErik.Nordmark@Sun.COM * MCAST_BLOCK_SOURCE MODE_IS_EXCLUDE v6 addr 234411042SErik.Nordmark@Sun.COM * IP_JOIN_SOURCE_GROUP MODE_IS_INCLUDE IPv4-mapped addr 234511042SErik.Nordmark@Sun.COM * MCAST_JOIN_SOURCE_GROUP MODE_IS_INCLUDE v6 addr 23460Sstevel@tonic-gate * 23470Sstevel@tonic-gate * Changing the filter mode is not allowed; if a matching ilg already 23480Sstevel@tonic-gate * exists and fmode != ilg->ilg_fmode, EINVAL is returned. 23490Sstevel@tonic-gate * 23500Sstevel@tonic-gate * Verifies that there is a source address of appropriate scope for 23510Sstevel@tonic-gate * the group; if not, EADDRNOTAVAIL is returned. 23520Sstevel@tonic-gate * 235311042SErik.Nordmark@Sun.COM * The interface to be used may be identified by an IPv4 address or by an 235411042SErik.Nordmark@Sun.COM * interface index. 235511042SErik.Nordmark@Sun.COM * 235611042SErik.Nordmark@Sun.COM * Handles IPv4-mapped IPv6 multicast addresses by associating them 235711042SErik.Nordmark@Sun.COM * with the IPv4 address. Assumes that if v6group is v4-mapped, 235811042SErik.Nordmark@Sun.COM * v6src is also v4-mapped. 23590Sstevel@tonic-gate */ 23600Sstevel@tonic-gate int 236111042SErik.Nordmark@Sun.COM ip_opt_add_group(conn_t *connp, boolean_t checkonly, 236211042SErik.Nordmark@Sun.COM const in6_addr_t *v6group, ipaddr_t ifaddr, uint_t ifindex, 236311042SErik.Nordmark@Sun.COM mcast_record_t fmode, const in6_addr_t *v6src) 23640Sstevel@tonic-gate { 236511042SErik.Nordmark@Sun.COM ill_t *ill; 236611042SErik.Nordmark@Sun.COM char buf[INET6_ADDRSTRLEN]; 236711042SErik.Nordmark@Sun.COM int err; 236811042SErik.Nordmark@Sun.COM 236911042SErik.Nordmark@Sun.COM err = ip_opt_check(connp, v6group, v6src, ifaddr, ifindex, &ill); 23700Sstevel@tonic-gate if (err != 0) { 237111042SErik.Nordmark@Sun.COM ip1dbg(("ip_opt_add_group: no ill for group %s/" 237211042SErik.Nordmark@Sun.COM "index %d\n", inet_ntop(AF_INET6, v6group, buf, 237311042SErik.Nordmark@Sun.COM sizeof (buf)), ifindex)); 23740Sstevel@tonic-gate return (err); 23750Sstevel@tonic-gate } 23760Sstevel@tonic-gate 23770Sstevel@tonic-gate if (checkonly) { 23780Sstevel@tonic-gate /* 23790Sstevel@tonic-gate * do not do operation, just pretend to - new T_CHECK 23800Sstevel@tonic-gate * semantics. The error return case above if encountered 23810Sstevel@tonic-gate * considered a good enough "check" here. 23820Sstevel@tonic-gate */ 238311042SErik.Nordmark@Sun.COM ill_refrele(ill); 23840Sstevel@tonic-gate return (0); 23850Sstevel@tonic-gate } 238611042SErik.Nordmark@Sun.COM mutex_enter(&ill->ill_mcast_serializer); 2387*11463SSowmini.Varadhan@Sun.COM /* 2388*11463SSowmini.Varadhan@Sun.COM * Multicast groups may not be joined on interfaces that are either 2389*11463SSowmini.Varadhan@Sun.COM * already underlying interfaces in an IPMP group, or in the process 2390*11463SSowmini.Varadhan@Sun.COM * of joining the IPMP group. The latter condition is enforced by 2391*11463SSowmini.Varadhan@Sun.COM * checking the value of ill->ill_grp_pending under the 2392*11463SSowmini.Varadhan@Sun.COM * ill_mcast_serializer lock. We cannot serialize the 2393*11463SSowmini.Varadhan@Sun.COM * ill_grp_pending check on the ill_g_lock across ilg_add() because 2394*11463SSowmini.Varadhan@Sun.COM * ill_mcast_send_queued -> ip_output_simple -> ill_lookup_on_ifindex 2395*11463SSowmini.Varadhan@Sun.COM * will take the ill_g_lock itself. Instead, we hold the 2396*11463SSowmini.Varadhan@Sun.COM * ill_mcast_serializer. 2397*11463SSowmini.Varadhan@Sun.COM */ 2398*11463SSowmini.Varadhan@Sun.COM if (ill->ill_grp_pending || IS_UNDER_IPMP(ill)) { 2399*11463SSowmini.Varadhan@Sun.COM DTRACE_PROBE2(group__add__on__under, ill_t *, ill, 2400*11463SSowmini.Varadhan@Sun.COM in6_addr_t *, v6group); 2401*11463SSowmini.Varadhan@Sun.COM mutex_exit(&ill->ill_mcast_serializer); 2402*11463SSowmini.Varadhan@Sun.COM ill_refrele(ill); 2403*11463SSowmini.Varadhan@Sun.COM return (EADDRNOTAVAIL); 2404*11463SSowmini.Varadhan@Sun.COM } 240511042SErik.Nordmark@Sun.COM err = ilg_add(connp, v6group, ifaddr, ifindex, ill, fmode, v6src); 240611042SErik.Nordmark@Sun.COM mutex_exit(&ill->ill_mcast_serializer); 2407*11463SSowmini.Varadhan@Sun.COM /* 2408*11463SSowmini.Varadhan@Sun.COM * We have done an addmulti_impl and/or delmulti_impl. 2409*11463SSowmini.Varadhan@Sun.COM * All locks have been dropped, we can send any 2410*11463SSowmini.Varadhan@Sun.COM * deferred/queued DLPI or IP packets 2411*11463SSowmini.Varadhan@Sun.COM */ 2412*11463SSowmini.Varadhan@Sun.COM ill_mcast_send_queued(ill); 2413*11463SSowmini.Varadhan@Sun.COM ill_dlpi_send_queued(ill); 241411042SErik.Nordmark@Sun.COM ill_refrele(ill); 24150Sstevel@tonic-gate return (err); 24160Sstevel@tonic-gate } 24170Sstevel@tonic-gate 24180Sstevel@tonic-gate /* 241911042SErik.Nordmark@Sun.COM * Common for IPv6 and IPv4. 242011042SErik.Nordmark@Sun.COM * Here we handle ilgs that are still attached to their original ill 242111042SErik.Nordmark@Sun.COM * (the one ifaddr/ifindex points at), as well as detached ones. 242211042SErik.Nordmark@Sun.COM * The detached ones might have been attached to some other ill. 24230Sstevel@tonic-gate */ 242411042SErik.Nordmark@Sun.COM static int 242511042SErik.Nordmark@Sun.COM ip_opt_delete_group_excl(conn_t *connp, const in6_addr_t *v6group, 242611042SErik.Nordmark@Sun.COM ipaddr_t ifaddr, uint_t ifindex, mcast_record_t fmode, 242711042SErik.Nordmark@Sun.COM const in6_addr_t *v6src) 24280Sstevel@tonic-gate { 242911042SErik.Nordmark@Sun.COM ilg_t *ilg; 243011042SErik.Nordmark@Sun.COM boolean_t leaving; 243111042SErik.Nordmark@Sun.COM ilm_t *ilm; 24320Sstevel@tonic-gate ill_t *ill; 243311042SErik.Nordmark@Sun.COM int err = 0; 243411042SErik.Nordmark@Sun.COM 243511042SErik.Nordmark@Sun.COM retry: 243611042SErik.Nordmark@Sun.COM rw_enter(&connp->conn_ilg_lock, RW_WRITER); 243711042SErik.Nordmark@Sun.COM ilg = ilg_lookup(connp, v6group, ifaddr, ifindex); 243811042SErik.Nordmark@Sun.COM if (ilg == NULL) { 243911042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 244011042SErik.Nordmark@Sun.COM /* 244111042SErik.Nordmark@Sun.COM * Since we didn't have any ilg we now do the error checks 244211042SErik.Nordmark@Sun.COM * to determine the best errno. 244311042SErik.Nordmark@Sun.COM */ 244411042SErik.Nordmark@Sun.COM err = ip_opt_check(connp, v6group, v6src, ifaddr, ifindex, 244511042SErik.Nordmark@Sun.COM &ill); 244611042SErik.Nordmark@Sun.COM if (ill != NULL) { 244711042SErik.Nordmark@Sun.COM /* The only error was a missing ilg for the group */ 244811042SErik.Nordmark@Sun.COM ill_refrele(ill); 244911042SErik.Nordmark@Sun.COM err = EADDRNOTAVAIL; 24500Sstevel@tonic-gate } 24510Sstevel@tonic-gate return (err); 24520Sstevel@tonic-gate } 245311042SErik.Nordmark@Sun.COM 245411042SErik.Nordmark@Sun.COM /* If the ilg is attached then we serialize using that ill */ 245511042SErik.Nordmark@Sun.COM ill = ilg->ilg_ill; 245611042SErik.Nordmark@Sun.COM if (ill != NULL) { 245711042SErik.Nordmark@Sun.COM /* Prevent the ill and ilg from being freed */ 245811042SErik.Nordmark@Sun.COM ill_refhold(ill); 245911042SErik.Nordmark@Sun.COM ilg_refhold(ilg); 246011042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 246111042SErik.Nordmark@Sun.COM mutex_enter(&ill->ill_mcast_serializer); 246211042SErik.Nordmark@Sun.COM rw_enter(&connp->conn_ilg_lock, RW_WRITER); 246311042SErik.Nordmark@Sun.COM if (ilg->ilg_condemned) { 246411042SErik.Nordmark@Sun.COM /* Disappeared */ 246511042SErik.Nordmark@Sun.COM ilg_refrele(ilg); 246611042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 246711042SErik.Nordmark@Sun.COM mutex_exit(&ill->ill_mcast_serializer); 24680Sstevel@tonic-gate ill_refrele(ill); 246911042SErik.Nordmark@Sun.COM goto retry; 247011042SErik.Nordmark@Sun.COM } 24710Sstevel@tonic-gate } 24720Sstevel@tonic-gate 24730Sstevel@tonic-gate /* 24740Sstevel@tonic-gate * Decide if we're actually deleting the ilg or just removing a 24750Sstevel@tonic-gate * source filter address; if just removing an addr, make sure we 24760Sstevel@tonic-gate * aren't trying to change the filter mode, and that the addr is 24770Sstevel@tonic-gate * actually in our filter list already. If we're removing the 24780Sstevel@tonic-gate * last src in an include list, just delete the ilg. 24790Sstevel@tonic-gate */ 248011042SErik.Nordmark@Sun.COM if (IN6_IS_ADDR_UNSPECIFIED(v6src)) { 24810Sstevel@tonic-gate leaving = B_TRUE; 24820Sstevel@tonic-gate } else { 24830Sstevel@tonic-gate if (fmode != ilg->ilg_fmode) 24840Sstevel@tonic-gate err = EINVAL; 24850Sstevel@tonic-gate else if (ilg->ilg_filter == NULL || 24860Sstevel@tonic-gate !list_has_addr(ilg->ilg_filter, v6src)) 24870Sstevel@tonic-gate err = EADDRNOTAVAIL; 24880Sstevel@tonic-gate if (err != 0) { 248911042SErik.Nordmark@Sun.COM if (ill != NULL) 249011042SErik.Nordmark@Sun.COM ilg_refrele(ilg); 249111042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 249211042SErik.Nordmark@Sun.COM goto done; 24930Sstevel@tonic-gate } 24940Sstevel@tonic-gate if (fmode == MODE_IS_INCLUDE && 249511042SErik.Nordmark@Sun.COM ilg->ilg_filter->sl_numsrc == 1) { 249611042SErik.Nordmark@Sun.COM leaving = B_TRUE; 24970Sstevel@tonic-gate v6src = NULL; 249811042SErik.Nordmark@Sun.COM } else { 24990Sstevel@tonic-gate leaving = B_FALSE; 250011042SErik.Nordmark@Sun.COM } 25010Sstevel@tonic-gate } 250211042SErik.Nordmark@Sun.COM ilm = ilg->ilg_ilm; 250311042SErik.Nordmark@Sun.COM if (leaving) 250411042SErik.Nordmark@Sun.COM ilg->ilg_ilm = NULL; 25050Sstevel@tonic-gate 25060Sstevel@tonic-gate ilg_delete(connp, ilg, v6src); 250711042SErik.Nordmark@Sun.COM if (ill != NULL) 250811042SErik.Nordmark@Sun.COM ilg_refrele(ilg); 250911042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 251011042SErik.Nordmark@Sun.COM 251111042SErik.Nordmark@Sun.COM if (ilm != NULL) { 251211042SErik.Nordmark@Sun.COM ASSERT(ill != NULL); 251311042SErik.Nordmark@Sun.COM (void) ip_delmulti_serial(ilm, B_FALSE, leaving); 251411042SErik.Nordmark@Sun.COM } 251511042SErik.Nordmark@Sun.COM done: 251611042SErik.Nordmark@Sun.COM if (ill != NULL) { 251711042SErik.Nordmark@Sun.COM mutex_exit(&ill->ill_mcast_serializer); 2518*11463SSowmini.Varadhan@Sun.COM /* 2519*11463SSowmini.Varadhan@Sun.COM * Now that all locks have been dropped, we can 2520*11463SSowmini.Varadhan@Sun.COM * send any deferred/queued DLPI or IP packets 2521*11463SSowmini.Varadhan@Sun.COM */ 2522*11463SSowmini.Varadhan@Sun.COM ill_mcast_send_queued(ill); 2523*11463SSowmini.Varadhan@Sun.COM ill_dlpi_send_queued(ill); 252411042SErik.Nordmark@Sun.COM ill_refrele(ill); 252511042SErik.Nordmark@Sun.COM } 252611042SErik.Nordmark@Sun.COM return (err); 25270Sstevel@tonic-gate } 25280Sstevel@tonic-gate 25290Sstevel@tonic-gate /* 25300Sstevel@tonic-gate * Handle the following optmgmt: 25310Sstevel@tonic-gate * IP_DROP_MEMBERSHIP will leave 253211042SErik.Nordmark@Sun.COM * IPV6_LEAVE_GROUP will leave 25330Sstevel@tonic-gate * MCAST_LEAVE_GROUP will leave 25340Sstevel@tonic-gate * IP_UNBLOCK_SOURCE will not leave 25350Sstevel@tonic-gate * MCAST_UNBLOCK_SOURCE will not leave 25360Sstevel@tonic-gate * IP_LEAVE_SOURCE_GROUP may leave (if leaving last source) 25370Sstevel@tonic-gate * MCAST_LEAVE_SOURCE_GROUP may leave (if leaving last source) 25380Sstevel@tonic-gate * 25390Sstevel@tonic-gate * fmode and src parameters may be used to determine which option is 254011042SErik.Nordmark@Sun.COM * being set, as follows: 25410Sstevel@tonic-gate * opt fmode v6src 254211042SErik.Nordmark@Sun.COM * IP_DROP_MEMBERSHIP MODE_IS_INCLUDE unspecified 25430Sstevel@tonic-gate * IPV6_LEAVE_GROUP MODE_IS_INCLUDE unspecified 25440Sstevel@tonic-gate * MCAST_LEAVE_GROUP MODE_IS_INCLUDE unspecified 254511042SErik.Nordmark@Sun.COM * IP_UNBLOCK_SOURCE MODE_IS_EXCLUDE IPv4-mapped addr 25460Sstevel@tonic-gate * MCAST_UNBLOCK_SOURCE MODE_IS_EXCLUDE v6 addr 254711042SErik.Nordmark@Sun.COM * IP_LEAVE_SOURCE_GROUP MODE_IS_INCLUDE IPv4-mapped addr 25480Sstevel@tonic-gate * MCAST_LEAVE_SOURCE_GROUP MODE_IS_INCLUDE v6 addr 25490Sstevel@tonic-gate * 25500Sstevel@tonic-gate * Changing the filter mode is not allowed; if a matching ilg already 25510Sstevel@tonic-gate * exists and fmode != ilg->ilg_fmode, EINVAL is returned. 25520Sstevel@tonic-gate * 255311042SErik.Nordmark@Sun.COM * The interface to be used may be identified by an IPv4 address or by an 255411042SErik.Nordmark@Sun.COM * interface index. 255511042SErik.Nordmark@Sun.COM * 25560Sstevel@tonic-gate * Handles IPv4-mapped IPv6 multicast addresses by associating them 255711042SErik.Nordmark@Sun.COM * with the IPv4 address. Assumes that if v6group is v4-mapped, 25580Sstevel@tonic-gate * v6src is also v4-mapped. 25590Sstevel@tonic-gate */ 25600Sstevel@tonic-gate int 256111042SErik.Nordmark@Sun.COM ip_opt_delete_group(conn_t *connp, boolean_t checkonly, 256211042SErik.Nordmark@Sun.COM const in6_addr_t *v6group, ipaddr_t ifaddr, uint_t ifindex, 256311042SErik.Nordmark@Sun.COM mcast_record_t fmode, const in6_addr_t *v6src) 25640Sstevel@tonic-gate { 256511042SErik.Nordmark@Sun.COM 256611042SErik.Nordmark@Sun.COM /* 256711042SErik.Nordmark@Sun.COM * In the normal case below we don't check for the ill existing. 256811042SErik.Nordmark@Sun.COM * Instead we look for an existing ilg in _excl. 256911042SErik.Nordmark@Sun.COM * If checkonly we sanity check the arguments 257011042SErik.Nordmark@Sun.COM */ 257111042SErik.Nordmark@Sun.COM if (checkonly) { 257211042SErik.Nordmark@Sun.COM ill_t *ill; 257311042SErik.Nordmark@Sun.COM int err; 257411042SErik.Nordmark@Sun.COM 257511042SErik.Nordmark@Sun.COM err = ip_opt_check(connp, v6group, v6src, ifaddr, ifindex, 257611042SErik.Nordmark@Sun.COM &ill); 257711042SErik.Nordmark@Sun.COM /* 257811042SErik.Nordmark@Sun.COM * do not do operation, just pretend to - new T_CHECK semantics. 257911042SErik.Nordmark@Sun.COM * ip_opt_check is considered a good enough "check" here. 258011042SErik.Nordmark@Sun.COM */ 258111042SErik.Nordmark@Sun.COM if (ill != NULL) 258211042SErik.Nordmark@Sun.COM ill_refrele(ill); 25830Sstevel@tonic-gate return (err); 25840Sstevel@tonic-gate } 258511042SErik.Nordmark@Sun.COM return (ip_opt_delete_group_excl(connp, v6group, ifaddr, ifindex, 258611042SErik.Nordmark@Sun.COM fmode, v6src)); 25870Sstevel@tonic-gate } 25880Sstevel@tonic-gate 25890Sstevel@tonic-gate /* 25900Sstevel@tonic-gate * Group mgmt for upper conn that passes things down 25910Sstevel@tonic-gate * to the interface multicast list (and DLPI) 25920Sstevel@tonic-gate * These routines can handle new style options that specify an interface name 25930Sstevel@tonic-gate * as opposed to an interface address (needed for general handling of 25940Sstevel@tonic-gate * unnumbered interfaces.) 25950Sstevel@tonic-gate */ 25960Sstevel@tonic-gate 25970Sstevel@tonic-gate /* 25980Sstevel@tonic-gate * Add a group to an upper conn group data structure and pass things down 25990Sstevel@tonic-gate * to the interface multicast list (and DLPI) 260011042SErik.Nordmark@Sun.COM * Common for IPv4 and IPv6; for IPv4 we can have an ifaddr. 26010Sstevel@tonic-gate */ 26020Sstevel@tonic-gate static int 260311042SErik.Nordmark@Sun.COM ilg_add(conn_t *connp, const in6_addr_t *v6group, ipaddr_t ifaddr, 260411042SErik.Nordmark@Sun.COM uint_t ifindex, ill_t *ill, mcast_record_t fmode, const in6_addr_t *v6src) 26050Sstevel@tonic-gate { 26060Sstevel@tonic-gate int error = 0; 26070Sstevel@tonic-gate ilg_t *ilg; 26080Sstevel@tonic-gate ilg_stat_t ilgstat; 26090Sstevel@tonic-gate slist_t *new_filter = NULL; 26100Sstevel@tonic-gate int new_fmode; 261111042SErik.Nordmark@Sun.COM ilm_t *ilm; 26120Sstevel@tonic-gate 26130Sstevel@tonic-gate if (!(ill->ill_flags & ILLF_MULTICAST)) 26140Sstevel@tonic-gate return (EADDRNOTAVAIL); 26150Sstevel@tonic-gate 261611042SErik.Nordmark@Sun.COM /* conn_ilg_lock protects the ilg list. */ 261711042SErik.Nordmark@Sun.COM ASSERT(MUTEX_HELD(&ill->ill_mcast_serializer)); 261811042SErik.Nordmark@Sun.COM rw_enter(&connp->conn_ilg_lock, RW_WRITER); 261911042SErik.Nordmark@Sun.COM ilg = ilg_lookup(connp, v6group, ifaddr, ifindex); 26200Sstevel@tonic-gate 26210Sstevel@tonic-gate /* 26220Sstevel@tonic-gate * Depending on the option we're handling, may or may not be okay 26230Sstevel@tonic-gate * if group has already been added. Figure out our rules based 26240Sstevel@tonic-gate * on fmode and src params. Also make sure there's enough room 26250Sstevel@tonic-gate * in the filter if we're adding a source to an existing filter. 26260Sstevel@tonic-gate */ 26270Sstevel@tonic-gate if (IN6_IS_ADDR_UNSPECIFIED(v6src)) { 26280Sstevel@tonic-gate /* we're joining for all sources, must not have joined */ 26290Sstevel@tonic-gate if (ilg != NULL) 26300Sstevel@tonic-gate error = EADDRINUSE; 26310Sstevel@tonic-gate } else { 26320Sstevel@tonic-gate if (fmode == MODE_IS_EXCLUDE) { 26330Sstevel@tonic-gate /* (excl {addr}) => block source, must have joined */ 26340Sstevel@tonic-gate if (ilg == NULL) 26350Sstevel@tonic-gate error = EADDRNOTAVAIL; 26360Sstevel@tonic-gate } 26370Sstevel@tonic-gate /* (incl {addr}) => join source, may have joined */ 26380Sstevel@tonic-gate 26390Sstevel@tonic-gate if (ilg != NULL && 26400Sstevel@tonic-gate SLIST_CNT(ilg->ilg_filter) == MAX_FILTER_SIZE) 26410Sstevel@tonic-gate error = ENOBUFS; 26420Sstevel@tonic-gate } 26430Sstevel@tonic-gate if (error != 0) { 264411042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 26450Sstevel@tonic-gate return (error); 26460Sstevel@tonic-gate } 26470Sstevel@tonic-gate 26480Sstevel@tonic-gate /* 26490Sstevel@tonic-gate * Alloc buffer to copy new state into (see below) before 26500Sstevel@tonic-gate * we make any changes, so we can bail if it fails. 26510Sstevel@tonic-gate */ 26520Sstevel@tonic-gate if ((new_filter = l_alloc()) == NULL) { 265311042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 26540Sstevel@tonic-gate return (ENOMEM); 26550Sstevel@tonic-gate } 26560Sstevel@tonic-gate 26570Sstevel@tonic-gate if (ilg == NULL) { 26588485SPeter.Memishian@Sun.COM if ((ilg = conn_ilg_alloc(connp, &error)) == NULL) { 265911042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 26600Sstevel@tonic-gate l_free(new_filter); 26618485SPeter.Memishian@Sun.COM return (error); 26620Sstevel@tonic-gate } 266311042SErik.Nordmark@Sun.COM ilg->ilg_ifindex = ifindex; 266411042SErik.Nordmark@Sun.COM ilg->ilg_ifaddr = ifaddr; 26650Sstevel@tonic-gate if (!IN6_IS_ADDR_UNSPECIFIED(v6src)) { 26660Sstevel@tonic-gate ilg->ilg_filter = l_alloc(); 26670Sstevel@tonic-gate if (ilg->ilg_filter == NULL) { 26680Sstevel@tonic-gate ilg_delete(connp, ilg, NULL); 266911042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 26700Sstevel@tonic-gate l_free(new_filter); 26710Sstevel@tonic-gate return (ENOMEM); 26720Sstevel@tonic-gate } 26730Sstevel@tonic-gate ilg->ilg_filter->sl_numsrc = 1; 26740Sstevel@tonic-gate ilg->ilg_filter->sl_addr[0] = *v6src; 26750Sstevel@tonic-gate } 26760Sstevel@tonic-gate ilgstat = ILGSTAT_NEW; 26770Sstevel@tonic-gate ilg->ilg_v6group = *v6group; 26780Sstevel@tonic-gate ilg->ilg_fmode = fmode; 26790Sstevel@tonic-gate ilg->ilg_ill = ill; 26800Sstevel@tonic-gate } else { 26810Sstevel@tonic-gate int index; 2682*11463SSowmini.Varadhan@Sun.COM 26830Sstevel@tonic-gate if (ilg->ilg_fmode != fmode || IN6_IS_ADDR_UNSPECIFIED(v6src)) { 268411042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 26850Sstevel@tonic-gate l_free(new_filter); 26860Sstevel@tonic-gate return (EINVAL); 26870Sstevel@tonic-gate } 26880Sstevel@tonic-gate if (ilg->ilg_filter == NULL) { 26890Sstevel@tonic-gate ilg->ilg_filter = l_alloc(); 26900Sstevel@tonic-gate if (ilg->ilg_filter == NULL) { 269111042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 26920Sstevel@tonic-gate l_free(new_filter); 26930Sstevel@tonic-gate return (ENOMEM); 26940Sstevel@tonic-gate } 26950Sstevel@tonic-gate } 26960Sstevel@tonic-gate if (list_has_addr(ilg->ilg_filter, v6src)) { 269711042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 26980Sstevel@tonic-gate l_free(new_filter); 26990Sstevel@tonic-gate return (EADDRNOTAVAIL); 27000Sstevel@tonic-gate } 27010Sstevel@tonic-gate ilgstat = ILGSTAT_CHANGE; 27020Sstevel@tonic-gate index = ilg->ilg_filter->sl_numsrc++; 27030Sstevel@tonic-gate ilg->ilg_filter->sl_addr[index] = *v6src; 27040Sstevel@tonic-gate } 27050Sstevel@tonic-gate 27060Sstevel@tonic-gate /* 27070Sstevel@tonic-gate * Save copy of ilg's filter state to pass to other functions, 270811042SErik.Nordmark@Sun.COM * so we can release conn_ilg_lock now. 27090Sstevel@tonic-gate */ 27100Sstevel@tonic-gate new_fmode = ilg->ilg_fmode; 27110Sstevel@tonic-gate l_copy(ilg->ilg_filter, new_filter); 27120Sstevel@tonic-gate 271311042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 27140Sstevel@tonic-gate 27150Sstevel@tonic-gate /* 27160Sstevel@tonic-gate * Now update the ill. We wait to do this until after the ilg 27170Sstevel@tonic-gate * has been updated because we need to update the src filter 27180Sstevel@tonic-gate * info for the ill, which involves looking at the status of 27190Sstevel@tonic-gate * all the ilgs associated with this group/interface pair. 27200Sstevel@tonic-gate */ 272111042SErik.Nordmark@Sun.COM ilm = ip_addmulti_serial(v6group, ill, connp->conn_zoneid, ilgstat, 272211042SErik.Nordmark@Sun.COM new_fmode, new_filter, &error); 272311042SErik.Nordmark@Sun.COM 272411042SErik.Nordmark@Sun.COM rw_enter(&connp->conn_ilg_lock, RW_WRITER); 272511042SErik.Nordmark@Sun.COM /* 272611042SErik.Nordmark@Sun.COM * Must look up the ilg again since we've not been holding 272711042SErik.Nordmark@Sun.COM * conn_ilg_lock. The ilg could have disappeared due to an unplumb 272811042SErik.Nordmark@Sun.COM * having called conn_update_ill, which can run once we dropped the 272911042SErik.Nordmark@Sun.COM * conn_ilg_lock above. 273011042SErik.Nordmark@Sun.COM */ 273111042SErik.Nordmark@Sun.COM ilg = ilg_lookup(connp, v6group, ifaddr, ifindex); 273211042SErik.Nordmark@Sun.COM if (ilg == NULL) { 273311042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 273411042SErik.Nordmark@Sun.COM if (ilm != NULL) { 273511042SErik.Nordmark@Sun.COM (void) ip_delmulti_serial(ilm, B_FALSE, 273611042SErik.Nordmark@Sun.COM (ilgstat == ILGSTAT_NEW)); 273711042SErik.Nordmark@Sun.COM } 273811042SErik.Nordmark@Sun.COM error = ENXIO; 273911042SErik.Nordmark@Sun.COM goto free_and_exit; 274011042SErik.Nordmark@Sun.COM } 274111042SErik.Nordmark@Sun.COM if (ilm != NULL) { 2742*11463SSowmini.Varadhan@Sun.COM if (ilg->ilg_ill == NULL) { 2743*11463SSowmini.Varadhan@Sun.COM /* some other thread is re-attaching this. */ 2744*11463SSowmini.Varadhan@Sun.COM rw_exit(&connp->conn_ilg_lock); 2745*11463SSowmini.Varadhan@Sun.COM (void) ip_delmulti_serial(ilm, B_FALSE, 2746*11463SSowmini.Varadhan@Sun.COM (ilgstat == ILGSTAT_NEW)); 2747*11463SSowmini.Varadhan@Sun.COM error = 0; 2748*11463SSowmini.Varadhan@Sun.COM goto free_and_exit; 2749*11463SSowmini.Varadhan@Sun.COM } 275011042SErik.Nordmark@Sun.COM /* Succeeded. Update the ilg to point at the ilm */ 275111042SErik.Nordmark@Sun.COM if (ilgstat == ILGSTAT_NEW) { 2752*11463SSowmini.Varadhan@Sun.COM if (ilg->ilg_ilm == NULL) { 2753*11463SSowmini.Varadhan@Sun.COM ilg->ilg_ilm = ilm; 2754*11463SSowmini.Varadhan@Sun.COM ilm->ilm_ifaddr = ifaddr; /* For netstat */ 2755*11463SSowmini.Varadhan@Sun.COM } else { 2756*11463SSowmini.Varadhan@Sun.COM /* some other thread is re-attaching this. */ 2757*11463SSowmini.Varadhan@Sun.COM rw_exit(&connp->conn_ilg_lock); 2758*11463SSowmini.Varadhan@Sun.COM (void) ip_delmulti_serial(ilm, B_FALSE, B_TRUE); 2759*11463SSowmini.Varadhan@Sun.COM error = 0; 2760*11463SSowmini.Varadhan@Sun.COM goto free_and_exit; 2761*11463SSowmini.Varadhan@Sun.COM } 276211042SErik.Nordmark@Sun.COM } else { 276311042SErik.Nordmark@Sun.COM /* 276411042SErik.Nordmark@Sun.COM * ip_addmulti didn't get a held ilm for 276511042SErik.Nordmark@Sun.COM * ILGSTAT_CHANGE; ilm_refcnt was unchanged. 276611042SErik.Nordmark@Sun.COM */ 276711042SErik.Nordmark@Sun.COM ASSERT(ilg->ilg_ilm == ilm); 276811042SErik.Nordmark@Sun.COM } 276911042SErik.Nordmark@Sun.COM } else { 277011042SErik.Nordmark@Sun.COM ASSERT(error != 0); 27710Sstevel@tonic-gate /* 277211042SErik.Nordmark@Sun.COM * Failed to allocate the ilm. 277311042SErik.Nordmark@Sun.COM * Need to undo what we did before calling ip_addmulti() 277411042SErik.Nordmark@Sun.COM * If ENETDOWN just clear ill_ilg since so that we 277511042SErik.Nordmark@Sun.COM * will rejoin when the ill comes back; don't report ENETDOWN 277611042SErik.Nordmark@Sun.COM * to application. 27770Sstevel@tonic-gate */ 277811042SErik.Nordmark@Sun.COM if (ilgstat == ILGSTAT_NEW && error == ENETDOWN) { 277911042SErik.Nordmark@Sun.COM ilg->ilg_ill = NULL; 278011042SErik.Nordmark@Sun.COM error = 0; 278111042SErik.Nordmark@Sun.COM } else { 278211042SErik.Nordmark@Sun.COM in6_addr_t delsrc = 278311042SErik.Nordmark@Sun.COM (ilgstat == ILGSTAT_NEW) ? ipv6_all_zeros : *v6src; 278411042SErik.Nordmark@Sun.COM 278511042SErik.Nordmark@Sun.COM ilg_delete(connp, ilg, &delsrc); 278611042SErik.Nordmark@Sun.COM } 27870Sstevel@tonic-gate } 278811042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 278911042SErik.Nordmark@Sun.COM 279011042SErik.Nordmark@Sun.COM free_and_exit: 27910Sstevel@tonic-gate l_free(new_filter); 279211042SErik.Nordmark@Sun.COM return (error); 27930Sstevel@tonic-gate } 27940Sstevel@tonic-gate 27950Sstevel@tonic-gate /* 279611042SErik.Nordmark@Sun.COM * Find an IPv4 ilg matching group, ill and source. 279711042SErik.Nordmark@Sun.COM * The group and source can't be INADDR_ANY here so no need to translate to 279811042SErik.Nordmark@Sun.COM * the unspecified IPv6 address. 27990Sstevel@tonic-gate */ 280011042SErik.Nordmark@Sun.COM boolean_t 280111042SErik.Nordmark@Sun.COM conn_hasmembers_ill_withsrc_v4(conn_t *connp, ipaddr_t group, ipaddr_t src, 280211042SErik.Nordmark@Sun.COM ill_t *ill) 28030Sstevel@tonic-gate { 28040Sstevel@tonic-gate in6_addr_t v6group, v6src; 28050Sstevel@tonic-gate int i; 28060Sstevel@tonic-gate boolean_t isinlist; 28070Sstevel@tonic-gate ilg_t *ilg; 280811042SErik.Nordmark@Sun.COM 280911042SErik.Nordmark@Sun.COM rw_enter(&connp->conn_ilg_lock, RW_READER); 281011042SErik.Nordmark@Sun.COM IN6_IPADDR_TO_V4MAPPED(group, &v6group); 281111042SErik.Nordmark@Sun.COM for (ilg = connp->conn_ilg; ilg != NULL; ilg = ilg->ilg_next) { 281211042SErik.Nordmark@Sun.COM if (ilg->ilg_condemned) 28130Sstevel@tonic-gate continue; 281411042SErik.Nordmark@Sun.COM 281511042SErik.Nordmark@Sun.COM /* ilg_ill could be NULL if an add is in progress */ 281611042SErik.Nordmark@Sun.COM if (ilg->ilg_ill != ill) 281711042SErik.Nordmark@Sun.COM continue; 281811042SErik.Nordmark@Sun.COM 281911042SErik.Nordmark@Sun.COM /* The callers use upper ill for IPMP */ 282011042SErik.Nordmark@Sun.COM ASSERT(!IS_UNDER_IPMP(ill)); 282111042SErik.Nordmark@Sun.COM if (IN6_ARE_ADDR_EQUAL(&ilg->ilg_v6group, &v6group)) { 28220Sstevel@tonic-gate if (SLIST_IS_EMPTY(ilg->ilg_filter)) { 28230Sstevel@tonic-gate /* no source filter, so this is a match */ 282411042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 282511042SErik.Nordmark@Sun.COM return (B_TRUE); 28260Sstevel@tonic-gate } 28270Sstevel@tonic-gate break; 28280Sstevel@tonic-gate } 28290Sstevel@tonic-gate } 283011042SErik.Nordmark@Sun.COM if (ilg == NULL) { 283111042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 283211042SErik.Nordmark@Sun.COM return (B_FALSE); 283311042SErik.Nordmark@Sun.COM } 28340Sstevel@tonic-gate 28350Sstevel@tonic-gate /* 28360Sstevel@tonic-gate * we have an ilg with matching ill and group; but 28370Sstevel@tonic-gate * the ilg has a source list that we must check. 28380Sstevel@tonic-gate */ 28390Sstevel@tonic-gate IN6_IPADDR_TO_V4MAPPED(src, &v6src); 28400Sstevel@tonic-gate isinlist = B_FALSE; 28410Sstevel@tonic-gate for (i = 0; i < ilg->ilg_filter->sl_numsrc; i++) { 28420Sstevel@tonic-gate if (IN6_ARE_ADDR_EQUAL(&v6src, &ilg->ilg_filter->sl_addr[i])) { 28430Sstevel@tonic-gate isinlist = B_TRUE; 28440Sstevel@tonic-gate break; 28450Sstevel@tonic-gate } 28460Sstevel@tonic-gate } 28470Sstevel@tonic-gate 28480Sstevel@tonic-gate if ((isinlist && ilg->ilg_fmode == MODE_IS_INCLUDE) || 284911042SErik.Nordmark@Sun.COM (!isinlist && ilg->ilg_fmode == MODE_IS_EXCLUDE)) { 285011042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 285111042SErik.Nordmark@Sun.COM return (B_TRUE); 285211042SErik.Nordmark@Sun.COM } 285311042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 285411042SErik.Nordmark@Sun.COM return (B_FALSE); 28550Sstevel@tonic-gate } 28560Sstevel@tonic-gate 28570Sstevel@tonic-gate /* 28580Sstevel@tonic-gate * Find an IPv6 ilg matching group, ill, and source 28590Sstevel@tonic-gate */ 286011042SErik.Nordmark@Sun.COM boolean_t 286111042SErik.Nordmark@Sun.COM conn_hasmembers_ill_withsrc_v6(conn_t *connp, const in6_addr_t *v6group, 28620Sstevel@tonic-gate const in6_addr_t *v6src, ill_t *ill) 28630Sstevel@tonic-gate { 28640Sstevel@tonic-gate int i; 28650Sstevel@tonic-gate boolean_t isinlist; 28660Sstevel@tonic-gate ilg_t *ilg; 286711042SErik.Nordmark@Sun.COM 286811042SErik.Nordmark@Sun.COM rw_enter(&connp->conn_ilg_lock, RW_READER); 286911042SErik.Nordmark@Sun.COM for (ilg = connp->conn_ilg; ilg != NULL; ilg = ilg->ilg_next) { 287011042SErik.Nordmark@Sun.COM if (ilg->ilg_condemned) 28710Sstevel@tonic-gate continue; 287211042SErik.Nordmark@Sun.COM 287311042SErik.Nordmark@Sun.COM /* ilg_ill could be NULL if an add is in progress */ 287411042SErik.Nordmark@Sun.COM if (ilg->ilg_ill != ill) 287511042SErik.Nordmark@Sun.COM continue; 287611042SErik.Nordmark@Sun.COM 287711042SErik.Nordmark@Sun.COM /* The callers use upper ill for IPMP */ 287811042SErik.Nordmark@Sun.COM ASSERT(!IS_UNDER_IPMP(ill)); 287911042SErik.Nordmark@Sun.COM if (IN6_ARE_ADDR_EQUAL(&ilg->ilg_v6group, v6group)) { 28800Sstevel@tonic-gate if (SLIST_IS_EMPTY(ilg->ilg_filter)) { 28810Sstevel@tonic-gate /* no source filter, so this is a match */ 288211042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 288311042SErik.Nordmark@Sun.COM return (B_TRUE); 28840Sstevel@tonic-gate } 28850Sstevel@tonic-gate break; 28860Sstevel@tonic-gate } 28870Sstevel@tonic-gate } 288811042SErik.Nordmark@Sun.COM if (ilg == NULL) { 288911042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 289011042SErik.Nordmark@Sun.COM return (B_FALSE); 289111042SErik.Nordmark@Sun.COM } 28920Sstevel@tonic-gate 28930Sstevel@tonic-gate /* 28940Sstevel@tonic-gate * we have an ilg with matching ill and group; but 28950Sstevel@tonic-gate * the ilg has a source list that we must check. 28960Sstevel@tonic-gate */ 28970Sstevel@tonic-gate isinlist = B_FALSE; 28980Sstevel@tonic-gate for (i = 0; i < ilg->ilg_filter->sl_numsrc; i++) { 28990Sstevel@tonic-gate if (IN6_ARE_ADDR_EQUAL(v6src, &ilg->ilg_filter->sl_addr[i])) { 29000Sstevel@tonic-gate isinlist = B_TRUE; 29010Sstevel@tonic-gate break; 29020Sstevel@tonic-gate } 29030Sstevel@tonic-gate } 29040Sstevel@tonic-gate 29050Sstevel@tonic-gate if ((isinlist && ilg->ilg_fmode == MODE_IS_INCLUDE) || 290611042SErik.Nordmark@Sun.COM (!isinlist && ilg->ilg_fmode == MODE_IS_EXCLUDE)) { 290711042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 290811042SErik.Nordmark@Sun.COM return (B_TRUE); 290911042SErik.Nordmark@Sun.COM } 291011042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 291111042SErik.Nordmark@Sun.COM return (B_FALSE); 29120Sstevel@tonic-gate } 29130Sstevel@tonic-gate 29140Sstevel@tonic-gate /* 291511042SErik.Nordmark@Sun.COM * Find an ilg matching group and ifaddr/ifindex. 291611042SErik.Nordmark@Sun.COM * We check both ifaddr and ifindex even though at most one of them 291711042SErik.Nordmark@Sun.COM * will be non-zero; that way we always find the right one. 29180Sstevel@tonic-gate */ 291911042SErik.Nordmark@Sun.COM static ilg_t * 292011042SErik.Nordmark@Sun.COM ilg_lookup(conn_t *connp, const in6_addr_t *v6group, ipaddr_t ifaddr, 292111042SErik.Nordmark@Sun.COM uint_t ifindex) 29220Sstevel@tonic-gate { 29230Sstevel@tonic-gate ilg_t *ilg; 292411042SErik.Nordmark@Sun.COM 292511042SErik.Nordmark@Sun.COM ASSERT(RW_LOCK_HELD(&connp->conn_ilg_lock)); 292611042SErik.Nordmark@Sun.COM 292711042SErik.Nordmark@Sun.COM for (ilg = connp->conn_ilg; ilg != NULL; ilg = ilg->ilg_next) { 292811042SErik.Nordmark@Sun.COM if (ilg->ilg_condemned) 29290Sstevel@tonic-gate continue; 293011042SErik.Nordmark@Sun.COM 293111042SErik.Nordmark@Sun.COM if (ilg->ilg_ifaddr == ifaddr && 293211042SErik.Nordmark@Sun.COM ilg->ilg_ifindex == ifindex && 29330Sstevel@tonic-gate IN6_ARE_ADDR_EQUAL(&ilg->ilg_v6group, v6group)) 29340Sstevel@tonic-gate return (ilg); 29350Sstevel@tonic-gate } 29360Sstevel@tonic-gate return (NULL); 29370Sstevel@tonic-gate } 29380Sstevel@tonic-gate 29390Sstevel@tonic-gate /* 29400Sstevel@tonic-gate * If a source address is passed in (src != NULL and src is not 29410Sstevel@tonic-gate * unspecified), remove the specified src addr from the given ilg's 29420Sstevel@tonic-gate * filter list, else delete the ilg. 29430Sstevel@tonic-gate */ 29440Sstevel@tonic-gate static void 29450Sstevel@tonic-gate ilg_delete(conn_t *connp, ilg_t *ilg, const in6_addr_t *src) 29460Sstevel@tonic-gate { 294711042SErik.Nordmark@Sun.COM ASSERT(RW_WRITE_HELD(&connp->conn_ilg_lock)); 294811042SErik.Nordmark@Sun.COM ASSERT(ilg->ilg_ptpn != NULL); 294911042SErik.Nordmark@Sun.COM ASSERT(!ilg->ilg_condemned); 29500Sstevel@tonic-gate 29510Sstevel@tonic-gate if (src == NULL || IN6_IS_ADDR_UNSPECIFIED(src)) { 29520Sstevel@tonic-gate FREE_SLIST(ilg->ilg_filter); 295311042SErik.Nordmark@Sun.COM ilg->ilg_filter = NULL; 295411042SErik.Nordmark@Sun.COM 295511042SErik.Nordmark@Sun.COM ASSERT(ilg->ilg_ilm == NULL); 295611042SErik.Nordmark@Sun.COM ilg->ilg_ill = NULL; 295711042SErik.Nordmark@Sun.COM ilg->ilg_condemned = B_TRUE; 295811042SErik.Nordmark@Sun.COM 295911042SErik.Nordmark@Sun.COM /* ilg_inactive will unlink from the list */ 296011042SErik.Nordmark@Sun.COM ilg_refrele(ilg); 29610Sstevel@tonic-gate } else { 29620Sstevel@tonic-gate l_remove(ilg->ilg_filter, src); 29630Sstevel@tonic-gate } 29640Sstevel@tonic-gate } 29650Sstevel@tonic-gate 29660Sstevel@tonic-gate /* 296711042SErik.Nordmark@Sun.COM * Called from conn close. No new ilg can be added or removed 29680Sstevel@tonic-gate * because CONN_CLOSING has been set by ip_close. ilg_add / ilg_delete 29690Sstevel@tonic-gate * will return error if conn has started closing. 297011042SErik.Nordmark@Sun.COM * 297111042SErik.Nordmark@Sun.COM * We handle locking as follows. 297211042SErik.Nordmark@Sun.COM * Under conn_ilg_lock we get the first ilg. As we drop the conn_ilg_lock to 297311042SErik.Nordmark@Sun.COM * proceed with the ilm part of the delete we hold a reference on both the ill 297411042SErik.Nordmark@Sun.COM * and the ilg. This doesn't prevent changes to the ilg, but prevents it from 297511042SErik.Nordmark@Sun.COM * being deleted. 297611042SErik.Nordmark@Sun.COM * 297711042SErik.Nordmark@Sun.COM * Since the ilg_add code path uses two locks (conn_ilg_lock for the ilg part, 297811042SErik.Nordmark@Sun.COM * and ill_mcast_lock for the ip_addmulti part) we can run at a point between 297911042SErik.Nordmark@Sun.COM * the two. At that point ilg_ill is set, but ilg_ilm hasn't yet been set. In 298011042SErik.Nordmark@Sun.COM * that case we delete the ilg here, which makes ilg_add discover that the ilg 298111042SErik.Nordmark@Sun.COM * has disappeared when ip_addmulti returns, so it will discard the ilm it just 298211042SErik.Nordmark@Sun.COM * added. 29830Sstevel@tonic-gate */ 29840Sstevel@tonic-gate void 29850Sstevel@tonic-gate ilg_delete_all(conn_t *connp) 29860Sstevel@tonic-gate { 298711042SErik.Nordmark@Sun.COM ilg_t *ilg, *next_ilg, *held_ilg; 298811042SErik.Nordmark@Sun.COM ilm_t *ilm; 298911042SErik.Nordmark@Sun.COM ill_t *ill; 299011042SErik.Nordmark@Sun.COM boolean_t need_refrele; 299111042SErik.Nordmark@Sun.COM 299211042SErik.Nordmark@Sun.COM /* 299311042SErik.Nordmark@Sun.COM * Can not run if there is a conn_update_ill already running. 299411042SErik.Nordmark@Sun.COM * Wait for it to complete. Caller should have already set CONN_CLOSING 299511042SErik.Nordmark@Sun.COM * which prevents any new threads to run in conn_update_ill. 299611042SErik.Nordmark@Sun.COM */ 29970Sstevel@tonic-gate mutex_enter(&connp->conn_lock); 299811042SErik.Nordmark@Sun.COM ASSERT(connp->conn_state_flags & CONN_CLOSING); 299911042SErik.Nordmark@Sun.COM while (connp->conn_state_flags & CONN_UPDATE_ILL) 300011042SErik.Nordmark@Sun.COM cv_wait(&connp->conn_cv, &connp->conn_lock); 300111042SErik.Nordmark@Sun.COM mutex_exit(&connp->conn_lock); 300211042SErik.Nordmark@Sun.COM 300311042SErik.Nordmark@Sun.COM rw_enter(&connp->conn_ilg_lock, RW_WRITER); 300411042SErik.Nordmark@Sun.COM ilg = connp->conn_ilg; 300511042SErik.Nordmark@Sun.COM held_ilg = NULL; 300611042SErik.Nordmark@Sun.COM while (ilg != NULL) { 300711042SErik.Nordmark@Sun.COM if (ilg->ilg_condemned) { 300811042SErik.Nordmark@Sun.COM ilg = ilg->ilg_next; 30090Sstevel@tonic-gate continue; 30100Sstevel@tonic-gate } 301111042SErik.Nordmark@Sun.COM /* If the ilg is detached then no need to serialize */ 301211042SErik.Nordmark@Sun.COM if (ilg->ilg_ilm == NULL) { 301311042SErik.Nordmark@Sun.COM next_ilg = ilg->ilg_next; 301411042SErik.Nordmark@Sun.COM ilg_delete(connp, ilg, NULL); 301511042SErik.Nordmark@Sun.COM ilg = next_ilg; 301611042SErik.Nordmark@Sun.COM continue; 301711042SErik.Nordmark@Sun.COM } 301811042SErik.Nordmark@Sun.COM ill = ilg->ilg_ilm->ilm_ill; 30198485SPeter.Memishian@Sun.COM 30200Sstevel@tonic-gate /* 302111042SErik.Nordmark@Sun.COM * In order to serialize on the ill we try to enter 302211042SErik.Nordmark@Sun.COM * and if that fails we unlock and relock and then 302311042SErik.Nordmark@Sun.COM * check that we still have an ilm. 30240Sstevel@tonic-gate */ 302511042SErik.Nordmark@Sun.COM need_refrele = B_FALSE; 302611042SErik.Nordmark@Sun.COM if (!mutex_tryenter(&ill->ill_mcast_serializer)) { 302711042SErik.Nordmark@Sun.COM ill_refhold(ill); 302811042SErik.Nordmark@Sun.COM need_refrele = B_TRUE; 302911042SErik.Nordmark@Sun.COM ilg_refhold(ilg); 303011042SErik.Nordmark@Sun.COM if (held_ilg != NULL) 303111042SErik.Nordmark@Sun.COM ilg_refrele(held_ilg); 303211042SErik.Nordmark@Sun.COM held_ilg = ilg; 303311042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 303411042SErik.Nordmark@Sun.COM mutex_enter(&ill->ill_mcast_serializer); 303511042SErik.Nordmark@Sun.COM rw_enter(&connp->conn_ilg_lock, RW_WRITER); 303611042SErik.Nordmark@Sun.COM if (ilg->ilg_condemned) { 303711042SErik.Nordmark@Sun.COM ilg = ilg->ilg_next; 303811042SErik.Nordmark@Sun.COM goto next; 303911042SErik.Nordmark@Sun.COM } 30400Sstevel@tonic-gate } 304111042SErik.Nordmark@Sun.COM ilm = ilg->ilg_ilm; 304211042SErik.Nordmark@Sun.COM ilg->ilg_ilm = NULL; 304311042SErik.Nordmark@Sun.COM next_ilg = ilg->ilg_next; 30440Sstevel@tonic-gate ilg_delete(connp, ilg, NULL); 304511042SErik.Nordmark@Sun.COM ilg = next_ilg; 304611042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 304711042SErik.Nordmark@Sun.COM 304811042SErik.Nordmark@Sun.COM if (ilm != NULL) 304911042SErik.Nordmark@Sun.COM (void) ip_delmulti_serial(ilm, B_FALSE, B_TRUE); 305011042SErik.Nordmark@Sun.COM 305111042SErik.Nordmark@Sun.COM next: 305211042SErik.Nordmark@Sun.COM mutex_exit(&ill->ill_mcast_serializer); 3053*11463SSowmini.Varadhan@Sun.COM /* 3054*11463SSowmini.Varadhan@Sun.COM * Now that all locks have been dropped, we can send any 3055*11463SSowmini.Varadhan@Sun.COM * deferred/queued DLPI or IP packets 3056*11463SSowmini.Varadhan@Sun.COM */ 3057*11463SSowmini.Varadhan@Sun.COM ill_mcast_send_queued(ill); 3058*11463SSowmini.Varadhan@Sun.COM ill_dlpi_send_queued(ill); 305911042SErik.Nordmark@Sun.COM if (need_refrele) { 306011042SErik.Nordmark@Sun.COM /* Drop ill reference while we hold no locks */ 306111042SErik.Nordmark@Sun.COM ill_refrele(ill); 30628485SPeter.Memishian@Sun.COM } 306311042SErik.Nordmark@Sun.COM rw_enter(&connp->conn_ilg_lock, RW_WRITER); 30640Sstevel@tonic-gate } 306511042SErik.Nordmark@Sun.COM if (held_ilg != NULL) 306611042SErik.Nordmark@Sun.COM ilg_refrele(held_ilg); 306711042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 30680Sstevel@tonic-gate } 30690Sstevel@tonic-gate 30700Sstevel@tonic-gate /* 307111042SErik.Nordmark@Sun.COM * Attach the ilg to an ilm on the ill. If it fails we leave ilg_ill as NULL so 3072*11463SSowmini.Varadhan@Sun.COM * that a subsequent attempt can attach it. Drops and reacquires conn_ilg_lock. 30730Sstevel@tonic-gate */ 30740Sstevel@tonic-gate static void 307511042SErik.Nordmark@Sun.COM ilg_attach(conn_t *connp, ilg_t *ilg, ill_t *ill) 30760Sstevel@tonic-gate { 307711042SErik.Nordmark@Sun.COM ilg_stat_t ilgstat; 307811042SErik.Nordmark@Sun.COM slist_t *new_filter; 307911042SErik.Nordmark@Sun.COM int new_fmode; 308011042SErik.Nordmark@Sun.COM in6_addr_t v6group; 308111042SErik.Nordmark@Sun.COM ipaddr_t ifaddr; 308211042SErik.Nordmark@Sun.COM uint_t ifindex; 308311042SErik.Nordmark@Sun.COM ilm_t *ilm; 308411042SErik.Nordmark@Sun.COM int error = 0; 308511042SErik.Nordmark@Sun.COM 308611042SErik.Nordmark@Sun.COM ASSERT(RW_WRITE_HELD(&connp->conn_ilg_lock)); 30870Sstevel@tonic-gate /* 308811042SErik.Nordmark@Sun.COM * Alloc buffer to copy new state into (see below) before 308911042SErik.Nordmark@Sun.COM * we make any changes, so we can bail if it fails. 30900Sstevel@tonic-gate */ 309111042SErik.Nordmark@Sun.COM if ((new_filter = l_alloc()) == NULL) 309211042SErik.Nordmark@Sun.COM return; 30930Sstevel@tonic-gate 30940Sstevel@tonic-gate /* 309511042SErik.Nordmark@Sun.COM * Save copy of ilg's filter state to pass to other functions, so 309611042SErik.Nordmark@Sun.COM * we can release conn_ilg_lock now. 309711042SErik.Nordmark@Sun.COM * Set ilg_ill so that an unplumb can find us. 30980Sstevel@tonic-gate */ 309911042SErik.Nordmark@Sun.COM new_fmode = ilg->ilg_fmode; 310011042SErik.Nordmark@Sun.COM l_copy(ilg->ilg_filter, new_filter); 310111042SErik.Nordmark@Sun.COM v6group = ilg->ilg_v6group; 310211042SErik.Nordmark@Sun.COM ifaddr = ilg->ilg_ifaddr; 310311042SErik.Nordmark@Sun.COM ifindex = ilg->ilg_ifindex; 310411042SErik.Nordmark@Sun.COM ilgstat = ILGSTAT_NEW; 310511042SErik.Nordmark@Sun.COM 310611042SErik.Nordmark@Sun.COM ilg->ilg_ill = ill; 310711042SErik.Nordmark@Sun.COM ASSERT(ilg->ilg_ilm == NULL); 310811042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 310911042SErik.Nordmark@Sun.COM 311011042SErik.Nordmark@Sun.COM ilm = ip_addmulti_serial(&v6group, ill, connp->conn_zoneid, ilgstat, 311111042SErik.Nordmark@Sun.COM new_fmode, new_filter, &error); 311211042SErik.Nordmark@Sun.COM l_free(new_filter); 311311042SErik.Nordmark@Sun.COM 311411042SErik.Nordmark@Sun.COM rw_enter(&connp->conn_ilg_lock, RW_WRITER); 311511042SErik.Nordmark@Sun.COM /* 311611042SErik.Nordmark@Sun.COM * Must look up the ilg again since we've not been holding 311711042SErik.Nordmark@Sun.COM * conn_ilg_lock. The ilg could have disappeared due to an unplumb 311811042SErik.Nordmark@Sun.COM * having called conn_update_ill, which can run once we dropped the 3119*11463SSowmini.Varadhan@Sun.COM * conn_ilg_lock above. Alternatively, the ilg could have been attached 3120*11463SSowmini.Varadhan@Sun.COM * when the lock was dropped 312111042SErik.Nordmark@Sun.COM */ 312211042SErik.Nordmark@Sun.COM ilg = ilg_lookup(connp, &v6group, ifaddr, ifindex); 3123*11463SSowmini.Varadhan@Sun.COM if (ilg == NULL || ilg->ilg_ilm != NULL) { 312411042SErik.Nordmark@Sun.COM if (ilm != NULL) { 312511042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 312611042SErik.Nordmark@Sun.COM (void) ip_delmulti_serial(ilm, B_FALSE, 312711042SErik.Nordmark@Sun.COM (ilgstat == ILGSTAT_NEW)); 312811042SErik.Nordmark@Sun.COM rw_enter(&connp->conn_ilg_lock, RW_WRITER); 31290Sstevel@tonic-gate } 313011042SErik.Nordmark@Sun.COM return; 31310Sstevel@tonic-gate } 313211042SErik.Nordmark@Sun.COM if (ilm == NULL) { 313311042SErik.Nordmark@Sun.COM ilg->ilg_ill = NULL; 313411042SErik.Nordmark@Sun.COM return; 313511042SErik.Nordmark@Sun.COM } 313611042SErik.Nordmark@Sun.COM ilg->ilg_ilm = ilm; 313711042SErik.Nordmark@Sun.COM ilm->ilm_ifaddr = ifaddr; /* For netstat */ 31380Sstevel@tonic-gate } 31390Sstevel@tonic-gate 31400Sstevel@tonic-gate /* 31410Sstevel@tonic-gate * Called when an ill is unplumbed to make sure that there are no 314211042SErik.Nordmark@Sun.COM * dangling conn references to that ill. In that case ill is non-NULL and 314311042SErik.Nordmark@Sun.COM * we make sure we remove all references to it. 314411042SErik.Nordmark@Sun.COM * Also called when we should revisit the ilg_ill used for multicast 314511042SErik.Nordmark@Sun.COM * memberships, in which case ill is NULL. 314611042SErik.Nordmark@Sun.COM * 314711042SErik.Nordmark@Sun.COM * conn is held by caller. 314811042SErik.Nordmark@Sun.COM * 314911042SErik.Nordmark@Sun.COM * Note that ipcl_walk only walks conns that are not yet condemned. 315011042SErik.Nordmark@Sun.COM * condemned conns can't be refheld. For this reason, conn must become clean 315111042SErik.Nordmark@Sun.COM * first, i.e. it must not refer to any ill/ire and then only set 315211042SErik.Nordmark@Sun.COM * condemned flag. 315311042SErik.Nordmark@Sun.COM * 315411042SErik.Nordmark@Sun.COM * We leave ixa_multicast_ifindex in place. We prefer dropping 315511042SErik.Nordmark@Sun.COM * packets instead of sending them out the wrong interface. 315611042SErik.Nordmark@Sun.COM * 315711042SErik.Nordmark@Sun.COM * We keep the ilg around in a detached state (with ilg_ill and ilg_ilm being 315811042SErik.Nordmark@Sun.COM * NULL) so that the application can leave it later. Also, if ilg_ifaddr and 315911042SErik.Nordmark@Sun.COM * ilg_ifindex are zero, indicating that the system should pick the interface, 316011042SErik.Nordmark@Sun.COM * then we attempt to reselect the ill and join on it. 316111042SErik.Nordmark@Sun.COM * 316211042SErik.Nordmark@Sun.COM * Locking notes: 316311042SErik.Nordmark@Sun.COM * Under conn_ilg_lock we get the first ilg. As we drop the conn_ilg_lock to 316411042SErik.Nordmark@Sun.COM * proceed with the ilm part of the delete we hold a reference on both the ill 316511042SErik.Nordmark@Sun.COM * and the ilg. This doesn't prevent changes to the ilg, but prevents it from 316611042SErik.Nordmark@Sun.COM * being deleted. 316711042SErik.Nordmark@Sun.COM * 316811042SErik.Nordmark@Sun.COM * Note: if this function is called when new ill/ipif's arrive or change status 316911042SErik.Nordmark@Sun.COM * (SIOCSLIFINDEX, SIOCSLIFADDR) then we will attempt to attach any ilgs with 317011042SErik.Nordmark@Sun.COM * a NULL ilg_ill to an ill/ilm. 31710Sstevel@tonic-gate */ 317211042SErik.Nordmark@Sun.COM static void 317311042SErik.Nordmark@Sun.COM conn_update_ill(conn_t *connp, caddr_t arg) 31740Sstevel@tonic-gate { 317511042SErik.Nordmark@Sun.COM ill_t *ill = (ill_t *)arg; 317611042SErik.Nordmark@Sun.COM 317711042SErik.Nordmark@Sun.COM /* 317811042SErik.Nordmark@Sun.COM * We have to prevent ip_close/ilg_delete_all from running at 317911042SErik.Nordmark@Sun.COM * the same time. ip_close sets CONN_CLOSING before doing the ilg_delete 318011042SErik.Nordmark@Sun.COM * all, and we set CONN_UPDATE_ILL. That ensures that only one of 318111042SErik.Nordmark@Sun.COM * ilg_delete_all and conn_update_ill run at a time for a given conn. 318211042SErik.Nordmark@Sun.COM * If ilg_delete_all got here first, then we have nothing to do. 318311042SErik.Nordmark@Sun.COM */ 318411042SErik.Nordmark@Sun.COM mutex_enter(&connp->conn_lock); 318511042SErik.Nordmark@Sun.COM if (connp->conn_state_flags & (CONN_CLOSING|CONN_UPDATE_ILL)) { 318611042SErik.Nordmark@Sun.COM /* Caller has to wait for ill_ilm_cnt to drop to zero */ 318711042SErik.Nordmark@Sun.COM mutex_exit(&connp->conn_lock); 318811042SErik.Nordmark@Sun.COM return; 31890Sstevel@tonic-gate } 319011042SErik.Nordmark@Sun.COM connp->conn_state_flags |= CONN_UPDATE_ILL; 319111042SErik.Nordmark@Sun.COM mutex_exit(&connp->conn_lock); 319211042SErik.Nordmark@Sun.COM 319311042SErik.Nordmark@Sun.COM if (ill != NULL) 319411042SErik.Nordmark@Sun.COM ilg_check_detach(connp, ill); 319511042SErik.Nordmark@Sun.COM 3196*11463SSowmini.Varadhan@Sun.COM ilg_check_reattach(connp, ill); 319711042SErik.Nordmark@Sun.COM 319811042SErik.Nordmark@Sun.COM /* Do we need to wake up a thread in ilg_delete_all? */ 319911042SErik.Nordmark@Sun.COM mutex_enter(&connp->conn_lock); 320011042SErik.Nordmark@Sun.COM connp->conn_state_flags &= ~CONN_UPDATE_ILL; 320111042SErik.Nordmark@Sun.COM if (connp->conn_state_flags & CONN_CLOSING) 320211042SErik.Nordmark@Sun.COM cv_broadcast(&connp->conn_cv); 320311042SErik.Nordmark@Sun.COM mutex_exit(&connp->conn_lock); 320411042SErik.Nordmark@Sun.COM } 320511042SErik.Nordmark@Sun.COM 320611042SErik.Nordmark@Sun.COM /* Detach from an ill that is going away */ 320711042SErik.Nordmark@Sun.COM static void 320811042SErik.Nordmark@Sun.COM ilg_check_detach(conn_t *connp, ill_t *ill) 320911042SErik.Nordmark@Sun.COM { 321011042SErik.Nordmark@Sun.COM char group_buf[INET6_ADDRSTRLEN]; 321111042SErik.Nordmark@Sun.COM ilg_t *ilg, *held_ilg; 321211042SErik.Nordmark@Sun.COM ilm_t *ilm; 321311042SErik.Nordmark@Sun.COM 321411042SErik.Nordmark@Sun.COM mutex_enter(&ill->ill_mcast_serializer); 321511042SErik.Nordmark@Sun.COM rw_enter(&connp->conn_ilg_lock, RW_WRITER); 321611042SErik.Nordmark@Sun.COM held_ilg = NULL; 321711042SErik.Nordmark@Sun.COM for (ilg = connp->conn_ilg; ilg != NULL; ilg = ilg->ilg_next) { 321811042SErik.Nordmark@Sun.COM if (ilg->ilg_condemned) 321911042SErik.Nordmark@Sun.COM continue; 322011042SErik.Nordmark@Sun.COM 322111042SErik.Nordmark@Sun.COM if (ilg->ilg_ill != ill) 322211042SErik.Nordmark@Sun.COM continue; 322311042SErik.Nordmark@Sun.COM 322411042SErik.Nordmark@Sun.COM /* Detach from current ill */ 322511042SErik.Nordmark@Sun.COM ip1dbg(("ilg_check_detach: detach %s on %s\n", 322611042SErik.Nordmark@Sun.COM inet_ntop(AF_INET6, &ilg->ilg_v6group, 322711042SErik.Nordmark@Sun.COM group_buf, sizeof (group_buf)), 322811042SErik.Nordmark@Sun.COM ilg->ilg_ill->ill_name)); 322911042SErik.Nordmark@Sun.COM 323011042SErik.Nordmark@Sun.COM /* Detach this ilg from the ill/ilm */ 323111042SErik.Nordmark@Sun.COM ilm = ilg->ilg_ilm; 323211042SErik.Nordmark@Sun.COM ilg->ilg_ilm = NULL; 323311042SErik.Nordmark@Sun.COM ilg->ilg_ill = NULL; 323411042SErik.Nordmark@Sun.COM if (ilm == NULL) 323511042SErik.Nordmark@Sun.COM continue; 323611042SErik.Nordmark@Sun.COM 323711042SErik.Nordmark@Sun.COM /* Prevent ilg from disappearing */ 323811042SErik.Nordmark@Sun.COM ilg_transfer_hold(held_ilg, ilg); 323911042SErik.Nordmark@Sun.COM held_ilg = ilg; 324011042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 324111042SErik.Nordmark@Sun.COM 324211042SErik.Nordmark@Sun.COM (void) ip_delmulti_serial(ilm, B_FALSE, B_TRUE); 324311042SErik.Nordmark@Sun.COM rw_enter(&connp->conn_ilg_lock, RW_WRITER); 324411042SErik.Nordmark@Sun.COM } 324511042SErik.Nordmark@Sun.COM if (held_ilg != NULL) 324611042SErik.Nordmark@Sun.COM ilg_refrele(held_ilg); 324711042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 324811042SErik.Nordmark@Sun.COM mutex_exit(&ill->ill_mcast_serializer); 3249*11463SSowmini.Varadhan@Sun.COM /* 3250*11463SSowmini.Varadhan@Sun.COM * Now that all locks have been dropped, we can send any 3251*11463SSowmini.Varadhan@Sun.COM * deferred/queued DLPI or IP packets 3252*11463SSowmini.Varadhan@Sun.COM */ 3253*11463SSowmini.Varadhan@Sun.COM ill_mcast_send_queued(ill); 3254*11463SSowmini.Varadhan@Sun.COM ill_dlpi_send_queued(ill); 32550Sstevel@tonic-gate } 32560Sstevel@tonic-gate 32570Sstevel@tonic-gate /* 325811042SErik.Nordmark@Sun.COM * Check if there is a place to attach the conn_ilgs. We do this for both 325911042SErik.Nordmark@Sun.COM * detached ilgs and attached ones, since for the latter there could be 3260*11463SSowmini.Varadhan@Sun.COM * a better ill to attach them to. oill is non-null if we just detached from 3261*11463SSowmini.Varadhan@Sun.COM * that ill. 32620Sstevel@tonic-gate */ 326311042SErik.Nordmark@Sun.COM static void 3264*11463SSowmini.Varadhan@Sun.COM ilg_check_reattach(conn_t *connp, ill_t *oill) 32650Sstevel@tonic-gate { 326611042SErik.Nordmark@Sun.COM ill_t *ill; 326711042SErik.Nordmark@Sun.COM char group_buf[INET6_ADDRSTRLEN]; 326811042SErik.Nordmark@Sun.COM ilg_t *ilg, *held_ilg; 326911042SErik.Nordmark@Sun.COM ilm_t *ilm; 327011042SErik.Nordmark@Sun.COM zoneid_t zoneid = IPCL_ZONEID(connp); 327111042SErik.Nordmark@Sun.COM int error; 327211042SErik.Nordmark@Sun.COM ip_stack_t *ipst = connp->conn_netstack->netstack_ip; 327311042SErik.Nordmark@Sun.COM 327411042SErik.Nordmark@Sun.COM rw_enter(&connp->conn_ilg_lock, RW_WRITER); 327511042SErik.Nordmark@Sun.COM held_ilg = NULL; 327611042SErik.Nordmark@Sun.COM for (ilg = connp->conn_ilg; ilg != NULL; ilg = ilg->ilg_next) { 327711042SErik.Nordmark@Sun.COM if (ilg->ilg_condemned) 327811042SErik.Nordmark@Sun.COM continue; 327911042SErik.Nordmark@Sun.COM 328011042SErik.Nordmark@Sun.COM /* Check if the conn_ill matches what we would pick now */ 328111042SErik.Nordmark@Sun.COM ill = ill_mcast_lookup(&ilg->ilg_v6group, ilg->ilg_ifaddr, 328211042SErik.Nordmark@Sun.COM ilg->ilg_ifindex, zoneid, ipst, &error); 328311042SErik.Nordmark@Sun.COM 328411042SErik.Nordmark@Sun.COM /* 328511042SErik.Nordmark@Sun.COM * Make sure the ill is usable for multicast and that 328611042SErik.Nordmark@Sun.COM * we can send the DL_ADDMULTI_REQ before we create an 328711042SErik.Nordmark@Sun.COM * ilm. 328811042SErik.Nordmark@Sun.COM */ 328911042SErik.Nordmark@Sun.COM if (ill != NULL && 329011042SErik.Nordmark@Sun.COM (!(ill->ill_flags & ILLF_MULTICAST) || !ill->ill_dl_up)) { 329111042SErik.Nordmark@Sun.COM /* Drop locks across ill_refrele */ 329211042SErik.Nordmark@Sun.COM ilg_transfer_hold(held_ilg, ilg); 329311042SErik.Nordmark@Sun.COM held_ilg = ilg; 329411042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 329511042SErik.Nordmark@Sun.COM ill_refrele(ill); 329611042SErik.Nordmark@Sun.COM ill = NULL; 329711042SErik.Nordmark@Sun.COM rw_enter(&connp->conn_ilg_lock, RW_WRITER); 329811042SErik.Nordmark@Sun.COM /* Note that ilg could have become condemned */ 329911042SErik.Nordmark@Sun.COM } 330011042SErik.Nordmark@Sun.COM 3301*11463SSowmini.Varadhan@Sun.COM /* 3302*11463SSowmini.Varadhan@Sun.COM * Is the ill unchanged, even if both are NULL? 3303*11463SSowmini.Varadhan@Sun.COM * Did we just detach from that ill? 3304*11463SSowmini.Varadhan@Sun.COM */ 3305*11463SSowmini.Varadhan@Sun.COM if (ill == ilg->ilg_ill || (ill != NULL && ill == oill)) { 330611042SErik.Nordmark@Sun.COM if (ill != NULL) { 330711042SErik.Nordmark@Sun.COM /* Drop locks across ill_refrele */ 330811042SErik.Nordmark@Sun.COM ilg_transfer_hold(held_ilg, ilg); 330911042SErik.Nordmark@Sun.COM held_ilg = ilg; 331011042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 331111042SErik.Nordmark@Sun.COM ill_refrele(ill); 331211042SErik.Nordmark@Sun.COM rw_enter(&connp->conn_ilg_lock, RW_WRITER); 331311042SErik.Nordmark@Sun.COM } 331411042SErik.Nordmark@Sun.COM continue; 331511042SErik.Nordmark@Sun.COM } 331611042SErik.Nordmark@Sun.COM 331711042SErik.Nordmark@Sun.COM /* Something changed; detach from old first if needed */ 331811042SErik.Nordmark@Sun.COM if (ilg->ilg_ill != NULL) { 331911042SErik.Nordmark@Sun.COM ill_t *ill2 = ilg->ilg_ill; 332011042SErik.Nordmark@Sun.COM boolean_t need_refrele = B_FALSE; 332111042SErik.Nordmark@Sun.COM 332211042SErik.Nordmark@Sun.COM /* 332311042SErik.Nordmark@Sun.COM * In order to serialize on the ill we try to enter 332411042SErik.Nordmark@Sun.COM * and if that fails we unlock and relock. 332511042SErik.Nordmark@Sun.COM */ 332611042SErik.Nordmark@Sun.COM if (!mutex_tryenter(&ill2->ill_mcast_serializer)) { 332711042SErik.Nordmark@Sun.COM ill_refhold(ill2); 332811042SErik.Nordmark@Sun.COM need_refrele = B_TRUE; 332911042SErik.Nordmark@Sun.COM ilg_transfer_hold(held_ilg, ilg); 333011042SErik.Nordmark@Sun.COM held_ilg = ilg; 333111042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 333211042SErik.Nordmark@Sun.COM mutex_enter(&ill2->ill_mcast_serializer); 333311042SErik.Nordmark@Sun.COM rw_enter(&connp->conn_ilg_lock, RW_WRITER); 333411042SErik.Nordmark@Sun.COM /* Note that ilg could have become condemned */ 333511042SErik.Nordmark@Sun.COM } 333611042SErik.Nordmark@Sun.COM /* 333711042SErik.Nordmark@Sun.COM * Check that nobody else re-attached the ilg while we 333811042SErik.Nordmark@Sun.COM * dropped the lock. 333911042SErik.Nordmark@Sun.COM */ 334011042SErik.Nordmark@Sun.COM if (ilg->ilg_ill == ill2) { 334111042SErik.Nordmark@Sun.COM ASSERT(!ilg->ilg_condemned); 334211042SErik.Nordmark@Sun.COM /* Detach from current ill */ 334311042SErik.Nordmark@Sun.COM ip1dbg(("conn_check_reattach: detach %s/%s\n", 334411042SErik.Nordmark@Sun.COM inet_ntop(AF_INET6, &ilg->ilg_v6group, 334511042SErik.Nordmark@Sun.COM group_buf, sizeof (group_buf)), 334611042SErik.Nordmark@Sun.COM ill2->ill_name)); 334711042SErik.Nordmark@Sun.COM 334811042SErik.Nordmark@Sun.COM ilm = ilg->ilg_ilm; 334911042SErik.Nordmark@Sun.COM ilg->ilg_ilm = NULL; 335011042SErik.Nordmark@Sun.COM ilg->ilg_ill = NULL; 335111042SErik.Nordmark@Sun.COM } else { 335211042SErik.Nordmark@Sun.COM ilm = NULL; 335311042SErik.Nordmark@Sun.COM } 3354*11463SSowmini.Varadhan@Sun.COM ilg_transfer_hold(held_ilg, ilg); 3355*11463SSowmini.Varadhan@Sun.COM held_ilg = ilg; 335611042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 335711042SErik.Nordmark@Sun.COM if (ilm != NULL) 335811042SErik.Nordmark@Sun.COM (void) ip_delmulti_serial(ilm, B_FALSE, B_TRUE); 335911042SErik.Nordmark@Sun.COM mutex_exit(&ill2->ill_mcast_serializer); 3360*11463SSowmini.Varadhan@Sun.COM /* 3361*11463SSowmini.Varadhan@Sun.COM * Now that all locks have been dropped, we can send any 3362*11463SSowmini.Varadhan@Sun.COM * deferred/queued DLPI or IP packets 3363*11463SSowmini.Varadhan@Sun.COM */ 3364*11463SSowmini.Varadhan@Sun.COM ill_mcast_send_queued(ill2); 3365*11463SSowmini.Varadhan@Sun.COM ill_dlpi_send_queued(ill2); 336611042SErik.Nordmark@Sun.COM if (need_refrele) { 336711042SErik.Nordmark@Sun.COM /* Drop ill reference while we hold no locks */ 336811042SErik.Nordmark@Sun.COM ill_refrele(ill2); 336911042SErik.Nordmark@Sun.COM } 337011042SErik.Nordmark@Sun.COM rw_enter(&connp->conn_ilg_lock, RW_WRITER); 337111042SErik.Nordmark@Sun.COM /* 337211042SErik.Nordmark@Sun.COM * While we dropped conn_ilg_lock some other thread 337311042SErik.Nordmark@Sun.COM * could have attached this ilg, thus we check again. 337411042SErik.Nordmark@Sun.COM */ 337511042SErik.Nordmark@Sun.COM if (ilg->ilg_ill != NULL) { 337611042SErik.Nordmark@Sun.COM if (ill != NULL) { 337711042SErik.Nordmark@Sun.COM /* Drop locks across ill_refrele */ 337811042SErik.Nordmark@Sun.COM ilg_transfer_hold(held_ilg, ilg); 337911042SErik.Nordmark@Sun.COM held_ilg = ilg; 338011042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 338111042SErik.Nordmark@Sun.COM ill_refrele(ill); 338211042SErik.Nordmark@Sun.COM rw_enter(&connp->conn_ilg_lock, 338311042SErik.Nordmark@Sun.COM RW_WRITER); 338411042SErik.Nordmark@Sun.COM } 338511042SErik.Nordmark@Sun.COM continue; 33860Sstevel@tonic-gate } 33870Sstevel@tonic-gate } 338811042SErik.Nordmark@Sun.COM if (ill != NULL) { 338911042SErik.Nordmark@Sun.COM /* 339011042SErik.Nordmark@Sun.COM * In order to serialize on the ill we try to enter 339111042SErik.Nordmark@Sun.COM * and if that fails we unlock and relock. 339211042SErik.Nordmark@Sun.COM */ 339311042SErik.Nordmark@Sun.COM if (!mutex_tryenter(&ill->ill_mcast_serializer)) { 339411042SErik.Nordmark@Sun.COM /* Already have a refhold on ill */ 339511042SErik.Nordmark@Sun.COM ilg_transfer_hold(held_ilg, ilg); 339611042SErik.Nordmark@Sun.COM held_ilg = ilg; 339711042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 339811042SErik.Nordmark@Sun.COM mutex_enter(&ill->ill_mcast_serializer); 339911042SErik.Nordmark@Sun.COM rw_enter(&connp->conn_ilg_lock, RW_WRITER); 340011042SErik.Nordmark@Sun.COM /* Note that ilg could have become condemned */ 340111042SErik.Nordmark@Sun.COM } 3402*11463SSowmini.Varadhan@Sun.COM ilg_transfer_hold(held_ilg, ilg); 3403*11463SSowmini.Varadhan@Sun.COM held_ilg = ilg; 340411042SErik.Nordmark@Sun.COM /* 340511042SErik.Nordmark@Sun.COM * Check that nobody else attached the ilg and that 340611042SErik.Nordmark@Sun.COM * it wasn't condemned while we dropped the lock. 340711042SErik.Nordmark@Sun.COM */ 340811042SErik.Nordmark@Sun.COM if (ilg->ilg_ill == NULL && !ilg->ilg_condemned) { 340911042SErik.Nordmark@Sun.COM /* 341011042SErik.Nordmark@Sun.COM * Attach to the new ill. Can fail in which 341111042SErik.Nordmark@Sun.COM * case ilg_ill will remain NULL. ilg_attach 341211042SErik.Nordmark@Sun.COM * drops and reacquires conn_ilg_lock. 341311042SErik.Nordmark@Sun.COM */ 341411042SErik.Nordmark@Sun.COM ip1dbg(("conn_check_reattach: attach %s/%s\n", 341511042SErik.Nordmark@Sun.COM inet_ntop(AF_INET6, &ilg->ilg_v6group, 341611042SErik.Nordmark@Sun.COM group_buf, sizeof (group_buf)), 341711042SErik.Nordmark@Sun.COM ill->ill_name)); 341811042SErik.Nordmark@Sun.COM ilg_attach(connp, ilg, ill); 341911042SErik.Nordmark@Sun.COM ASSERT(RW_WRITE_HELD(&connp->conn_ilg_lock)); 342011042SErik.Nordmark@Sun.COM } 3421*11463SSowmini.Varadhan@Sun.COM /* Drop locks across ill_refrele */ 3422*11463SSowmini.Varadhan@Sun.COM rw_exit(&connp->conn_ilg_lock); 342311042SErik.Nordmark@Sun.COM mutex_exit(&ill->ill_mcast_serializer); 3424*11463SSowmini.Varadhan@Sun.COM /* 3425*11463SSowmini.Varadhan@Sun.COM * Now that all locks have been 3426*11463SSowmini.Varadhan@Sun.COM * dropped, we can send any 3427*11463SSowmini.Varadhan@Sun.COM * deferred/queued DLPI or IP packets 3428*11463SSowmini.Varadhan@Sun.COM */ 3429*11463SSowmini.Varadhan@Sun.COM ill_mcast_send_queued(ill); 3430*11463SSowmini.Varadhan@Sun.COM ill_dlpi_send_queued(ill); 343111042SErik.Nordmark@Sun.COM ill_refrele(ill); 343211042SErik.Nordmark@Sun.COM rw_enter(&connp->conn_ilg_lock, RW_WRITER); 343311042SErik.Nordmark@Sun.COM } 34340Sstevel@tonic-gate } 343511042SErik.Nordmark@Sun.COM if (held_ilg != NULL) 343611042SErik.Nordmark@Sun.COM ilg_refrele(held_ilg); 343711042SErik.Nordmark@Sun.COM rw_exit(&connp->conn_ilg_lock); 34380Sstevel@tonic-gate } 343911042SErik.Nordmark@Sun.COM 344011042SErik.Nordmark@Sun.COM /* 344111042SErik.Nordmark@Sun.COM * Called when an ill is unplumbed to make sure that there are no 344211042SErik.Nordmark@Sun.COM * dangling conn references to that ill. In that case ill is non-NULL and 344311042SErik.Nordmark@Sun.COM * we make sure we remove all references to it. 344411042SErik.Nordmark@Sun.COM * Also called when we should revisit the ilg_ill used for multicast 344511042SErik.Nordmark@Sun.COM * memberships, in which case ill is NULL. 344611042SErik.Nordmark@Sun.COM */ 344711042SErik.Nordmark@Sun.COM void 344811042SErik.Nordmark@Sun.COM update_conn_ill(ill_t *ill, ip_stack_t *ipst) 344911042SErik.Nordmark@Sun.COM { 345011042SErik.Nordmark@Sun.COM ipcl_walk(conn_update_ill, (caddr_t)ill, ipst); 345111042SErik.Nordmark@Sun.COM } 3452