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