summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/inet/ip/ip_multi.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/inet/ip/ip_multi.c')
-rw-r--r--usr/src/uts/common/inet/ip/ip_multi.c787
1 files changed, 342 insertions, 445 deletions
diff --git a/usr/src/uts/common/inet/ip/ip_multi.c b/usr/src/uts/common/inet/ip/ip_multi.c
index f3c95ae362..cbea9be165 100644
--- a/usr/src/uts/common/inet/ip/ip_multi.c
+++ b/usr/src/uts/common/inet/ip/ip_multi.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1990 Mentat Inc. */
@@ -68,12 +68,10 @@ static void ilm_gen_filter(ilm_t *ilm, mcast_record_t *fmode,
static ilm_t *ilm_add_v6(ipif_t *ipif, const in6_addr_t *group,
ilg_stat_t ilgstat, mcast_record_t ilg_fmode, slist_t *ilg_flist,
- int orig_ifindex, zoneid_t zoneid);
+ zoneid_t zoneid);
static void ilm_delete(ilm_t *ilm);
static int ip_ll_addmulti_v6(ipif_t *ipif, const in6_addr_t *group);
static int ip_ll_delmulti_v6(ipif_t *ipif, const in6_addr_t *group);
-static ilg_t *ilg_lookup_ill_index_v6(conn_t *connp,
- const in6_addr_t *v6group, int index);
static ilg_t *ilg_lookup_ipif(conn_t *connp, ipaddr_t group,
ipif_t *ipif);
static int ilg_add(conn_t *connp, ipaddr_t group, ipif_t *ipif,
@@ -91,25 +89,21 @@ static int ip_opt_delete_group_excl(conn_t *connp, ipaddr_t group,
static int ip_opt_delete_group_excl_v6(conn_t *connp,
const in6_addr_t *v6group, ill_t *ill, mcast_record_t fmode,
const in6_addr_t *v6src);
+static void ill_ilm_walker_hold(ill_t *ill);
+static void ill_ilm_walker_rele(ill_t *ill);
/*
* MT notes:
*
* Multicast joins operate on both the ilg and ilm structures. Multiple
* threads operating on an conn (socket) trying to do multicast joins
- * need to synchronize when operating on the ilg. Multiple threads
+ * need to synchronize when operating on the ilg. Multiple threads
* potentially operating on different conn (socket endpoints) trying to
* do multicast joins could eventually end up trying to manipulate the
- * ilm simulatenously and need to synchronize on the access to the ilm.
- * Both are amenable to standard Solaris MT techniques, but it would be
- * complex to handle a failover or failback which needs to manipulate
- * ilg/ilms if an applications can also simultaenously join/leave
- * multicast groups. Hence multicast join/leave also go through the ipsq_t
+ * ilm simultaneously and need to synchronize access to the ilm. Currently,
+ * this is done by synchronizing join/leave via per-phyint ipsq_t
* serialization.
*
- * Multicast joins and leaves are single-threaded per phyint/IPMP group
- * using the ipsq serialization mechanism.
- *
* An ilm is an IP data structure used to track multicast join/leave.
* An ilm is associated with a <multicast group, ipif> tuple in IPv4 and
* with just <multicast group> in IPv6. ilm_refcnt is the number of ilg's
@@ -211,12 +205,13 @@ conn_ilg_reap(conn_t *connp)
* Returns a pointer to the next available ilg in conn_ilg. Allocs more
* buffers in size of ILG_ALLOC_CHUNK ilgs when needed, and updates conn's
* ilg tracking fields appropriately (conn_ilg_inuse reflects usage of the
- * returned ilg). Returns NULL on failure (ENOMEM).
+ * returned ilg). Returns NULL on failure, in which case `*errp' will be
+ * filled in with the reason.
*
* Assumes connp->conn_lock is held.
*/
static ilg_t *
-conn_ilg_alloc(conn_t *connp)
+conn_ilg_alloc(conn_t *connp, int *errp)
{
ilg_t *new, *ret;
int curcnt;
@@ -224,10 +219,21 @@ conn_ilg_alloc(conn_t *connp)
ASSERT(MUTEX_HELD(&connp->conn_lock));
ASSERT(connp->conn_ilg_inuse <= connp->conn_ilg_allocated);
+ /*
+ * If CONN_CLOSING is set, conn_ilg cleanup has begun and we must not
+ * create any ilgs.
+ */
+ if (connp->conn_state_flags & CONN_CLOSING) {
+ *errp = EINVAL;
+ return (NULL);
+ }
+
if (connp->conn_ilg == NULL) {
connp->conn_ilg = GETSTRUCT(ilg_t, ILG_ALLOC_CHUNK);
- if (connp->conn_ilg == NULL)
+ if (connp->conn_ilg == NULL) {
+ *errp = ENOMEM;
return (NULL);
+ }
connp->conn_ilg_allocated = ILG_ALLOC_CHUNK;
connp->conn_ilg_inuse = 0;
}
@@ -241,12 +247,15 @@ conn_ilg_alloc(conn_t *connp)
* ilg_delete_all() will have to be changed when
* this logic is changed.
*/
+ *errp = EBUSY;
return (NULL);
}
curcnt = connp->conn_ilg_allocated;
new = GETSTRUCT(ilg_t, curcnt + ILG_ALLOC_CHUNK);
- if (new == NULL)
+ if (new == NULL) {
+ *errp = ENOMEM;
return (NULL);
+ }
bcopy(connp->conn_ilg, new, sizeof (ilg_t) * curcnt);
mi_free((char *)connp->conn_ilg);
connp->conn_ilg = new;
@@ -378,42 +387,6 @@ ilm_gen_filter(ilm_t *ilm, mcast_record_t *fmode, slist_t *flist)
}
}
-/*
- * If the given interface has failed, choose a new one to join on so
- * that we continue to receive packets. ilg_orig_ifindex remembers
- * what the application used to join on so that we know the ilg to
- * delete even though we change the ill here. Callers will store the
- * ilg returned from this function in ilg_ill. Thus when we receive
- * a packet on ilg_ill, conn_wantpacket_v6 will deliver the packets.
- *
- * This function must be called as writer so we can walk the group
- * list and examine flags without holding a lock.
- */
-ill_t *
-ip_choose_multi_ill(ill_t *ill, const in6_addr_t *grp)
-{
- ill_t *till;
- ill_group_t *illgrp = ill->ill_group;
-
- ASSERT(IAM_WRITER_ILL(ill));
-
- if (IN6_IS_ADDR_UNSPECIFIED(grp) || illgrp == NULL)
- return (ill);
-
- if ((ill->ill_phyint->phyint_flags & (PHYI_FAILED|PHYI_INACTIVE)) == 0)
- return (ill);
-
- till = illgrp->illgrp_ill;
- while (till != NULL &&
- (till->ill_phyint->phyint_flags & (PHYI_FAILED|PHYI_INACTIVE))) {
- till = till->ill_group_next;
- }
- if (till != NULL)
- return (till);
-
- return (ill);
-}
-
static int
ilm_update_add(ilm_t *ilm, ilg_stat_t ilgstat, slist_t *ilg_flist,
boolean_t isv6)
@@ -560,8 +533,7 @@ ilm_update_del(ilm_t *ilm, boolean_t isv6)
}
/*
- * INADDR_ANY means all multicast addresses. This is only used
- * by the multicast router.
+ * INADDR_ANY means all multicast addresses.
* INADDR_ANY is stored as IPv6 unspecified addr.
*/
int
@@ -578,40 +550,31 @@ ip_addmulti(ipaddr_t group, ipif_t *ipif, ilg_stat_t ilgstat,
if (!CLASSD(group) && group != INADDR_ANY)
return (EINVAL);
+ if (IS_UNDER_IPMP(ill))
+ return (EINVAL);
+
/*
- * INADDR_ANY is represented as the IPv6 unspecifed addr.
+ * INADDR_ANY is represented as the IPv6 unspecified addr.
*/
if (group == INADDR_ANY)
v6group = ipv6_all_zeros;
else
IN6_IPADDR_TO_V4MAPPED(group, &v6group);
- mutex_enter(&ill->ill_lock);
ilm = ilm_lookup_ipif(ipif, group);
- mutex_exit(&ill->ill_lock);
/*
* Since we are writer, we know the ilm_flags itself cannot
* change at this point, and ilm_lookup_ipif would not have
* returned a DELETED ilm. However, the data path can free
- * ilm->next via ilm_walker_cleanup() so we can safely
+ * ilm->ilm_next via ilm_walker_cleanup() so we can safely
* access anything in ilm except ilm_next (for safe access to
- * ilm_next we'd have to take the ill_lock).
+ * ilm_next we'd have to take the ill_lock).
*/
if (ilm != NULL)
return (ilm_update_add(ilm, ilgstat, ilg_flist, B_FALSE));
- /*
- * ilms are associated with ipifs in IPv4. It moves with the
- * ipif if the ipif moves to a new ill when the interface
- * fails. Thus we really don't check whether the ipif_ill
- * has failed like in IPv6. If it has FAILED the ipif
- * will move (daemon will move it) and hence the ilm, if the
- * ipif is not IPIF_NOFAILOVER. For the IPIF_NOFAILOVER ipifs,
- * we continue to receive in the same place even if the
- * interface fails.
- */
ilm = ilm_add_v6(ipif, &v6group, ilgstat, ilg_fmode, ilg_flist,
- ill->ill_phyint->phyint_ifindex, ipif->ipif_zoneid);
+ ipif->ipif_zoneid);
if (ilm == NULL)
return (ENOMEM);
@@ -623,10 +586,7 @@ ip_addmulti(ipaddr_t group, ipif_t *ipif, ilg_stat_t ilgstat,
*/
if (ilm_numentries_v6(ill, &v6group) > 1)
return (0);
- if (ill->ill_group == NULL)
- ret = ill_join_allmulti(ill);
- else
- ret = ill_nominate_mcast_rcv(ill->ill_group);
+ ret = ill_join_allmulti(ill);
if (ret != 0)
ilm_delete(ilm);
return (ret);
@@ -646,12 +606,8 @@ ip_addmulti(ipaddr_t group, ipif_t *ipif, ilg_stat_t ilgstat,
/*
* The unspecified address means all multicast addresses.
- * This is only used by the multicast router.
*
- * ill identifies the interface to join on; it may not match the
- * interface requested by the application of a failover has taken
- * place. orig_ifindex always identifies the interface requested
- * by the app.
+ * ill identifies the interface to join on.
*
* ilgstat tells us if there's an ilg associated with this join,
* and if so, if it's a new ilg or a change to an existing one.
@@ -659,9 +615,8 @@ ip_addmulti(ipaddr_t group, ipif_t *ipif, ilg_stat_t ilgstat,
* the ilg (and will be EXCLUDE {NULL} in the case of no ilg).
*/
int
-ip_addmulti_v6(const in6_addr_t *v6group, ill_t *ill, int orig_ifindex,
- zoneid_t zoneid, ilg_stat_t ilgstat, mcast_record_t ilg_fmode,
- slist_t *ilg_flist)
+ip_addmulti_v6(const in6_addr_t *v6group, ill_t *ill, zoneid_t zoneid,
+ ilg_stat_t ilgstat, mcast_record_t ilg_fmode, slist_t *ilg_flist)
{
ilm_t *ilm;
int ret;
@@ -673,37 +628,20 @@ ip_addmulti_v6(const in6_addr_t *v6group, ill_t *ill, int orig_ifindex,
return (EINVAL);
}
+ if (IS_UNDER_IPMP(ill) && !IN6_IS_ADDR_MC_SOLICITEDNODE(v6group))
+ return (EINVAL);
+
/*
- * An ilm is uniquely identified by the tuple of (group, ill,
- * orig_ill). group is the multicast group address, ill is
- * the interface on which it is currently joined, and orig_ill
- * is the interface on which the application requested the
- * join. orig_ill and ill are the same unless orig_ill has
- * failed over.
- *
- * Both orig_ill and ill are required, which means we may have
- * 2 ilms on an ill for the same group, but with different
- * orig_ills. These must be kept separate, so that when failback
- * occurs, the appropriate ilms are moved back to their orig_ill
- * without disrupting memberships on the ill to which they had
- * been moved.
- *
- * In order to track orig_ill, we store orig_ifindex in the
- * ilm and ilg.
+ * An ilm is uniquely identified by the tuple of (group, ill) where
+ * `group' is the multicast group address, and `ill' is the interface
+ * on which it is currently joined.
*/
- mutex_enter(&ill->ill_lock);
- ilm = ilm_lookup_ill_index_v6(ill, v6group, orig_ifindex, zoneid);
- mutex_exit(&ill->ill_lock);
+ ilm = ilm_lookup_ill_v6(ill, v6group, B_TRUE, zoneid);
if (ilm != NULL)
return (ilm_update_add(ilm, ilgstat, ilg_flist, B_TRUE));
- /*
- * We need to remember where the application really wanted
- * to join. This will be used later if we want to failback
- * to the original interface.
- */
ilm = ilm_add_v6(ill->ill_ipif, v6group, ilgstat, ilg_fmode,
- ilg_flist, orig_ifindex, zoneid);
+ ilg_flist, zoneid);
if (ilm == NULL)
return (ENOMEM);
@@ -715,11 +653,7 @@ ip_addmulti_v6(const in6_addr_t *v6group, ill_t *ill, int orig_ifindex,
*/
if (ilm_numentries_v6(ill, v6group) > 1)
return (0);
- if (ill->ill_group == NULL)
- ret = ill_join_allmulti(ill);
- else
- ret = ill_nominate_mcast_rcv(ill->ill_group);
-
+ ret = ill_join_allmulti(ill);
if (ret != 0)
ilm_delete(ilm);
return (ret);
@@ -756,6 +690,14 @@ ip_ll_send_enabmulti_req(ill_t *ill, const in6_addr_t *v6groupp)
ASSERT(IAM_WRITER_ILL(ill));
/*
+ * If we're on the IPMP ill, use the nominated multicast interface to
+ * send and receive DLPI messages, if one exists. (If none exists,
+ * there are no usable interfaces and thus nothing to do.)
+ */
+ if (IS_IPMP(ill) && (ill = ipmp_illgrp_cast_ill(ill->ill_grp)) == NULL)
+ return (0);
+
+ /*
* Create a AR_ENTRY_SQUERY message with a dl_enabmulti_req tacked
* on.
*/
@@ -842,9 +784,8 @@ ip_ll_addmulti_v6(ipif_t *ipif, const in6_addr_t *v6groupp)
}
/*
- * INADDR_ANY means all multicast addresses. This is only used
- * by the multicast router.
- * INADDR_ANY is stored as the IPv6 unspecifed addr.
+ * INADDR_ANY means all multicast addresses.
+ * INADDR_ANY is stored as the IPv6 unspecified addr.
*/
int
ip_delmulti(ipaddr_t group, ipif_t *ipif, boolean_t no_ilg, boolean_t leaving)
@@ -859,7 +800,7 @@ ip_delmulti(ipaddr_t group, ipif_t *ipif, boolean_t no_ilg, boolean_t leaving)
return (EINVAL);
/*
- * INADDR_ANY is represented as the IPv6 unspecifed addr.
+ * INADDR_ANY is represented as the IPv6 unspecified addr.
*/
if (group == INADDR_ANY)
v6group = ipv6_all_zeros;
@@ -870,9 +811,7 @@ ip_delmulti(ipaddr_t group, ipif_t *ipif, boolean_t no_ilg, boolean_t leaving)
* Look for a match on the ipif.
* (IP_DROP_MEMBERSHIP specifies an ipif using an IP address).
*/
- mutex_enter(&ill->ill_lock);
ilm = ilm_lookup_ipif(ipif, group);
- mutex_exit(&ill->ill_lock);
if (ilm == NULL)
return (ENOENT);
@@ -897,11 +836,9 @@ ip_delmulti(ipaddr_t group, ipif_t *ipif, boolean_t no_ilg, boolean_t leaving)
return (0);
/* If we never joined, then don't leave. */
- if (ill->ill_join_allmulti) {
+ if (ill->ill_join_allmulti)
ill_leave_allmulti(ill);
- if (ill->ill_group != NULL)
- (void) ill_nominate_mcast_rcv(ill->ill_group);
- }
+
return (0);
}
@@ -921,11 +858,10 @@ ip_delmulti(ipaddr_t group, ipif_t *ipif, boolean_t no_ilg, boolean_t leaving)
/*
* The unspecified address means all multicast addresses.
- * This is only used by the multicast router.
*/
int
-ip_delmulti_v6(const in6_addr_t *v6group, ill_t *ill, int orig_ifindex,
- zoneid_t zoneid, boolean_t no_ilg, boolean_t leaving)
+ip_delmulti_v6(const in6_addr_t *v6group, ill_t *ill, zoneid_t zoneid,
+ boolean_t no_ilg, boolean_t leaving)
{
ipif_t *ipif;
ilm_t *ilm;
@@ -938,25 +874,8 @@ ip_delmulti_v6(const in6_addr_t *v6group, ill_t *ill, int orig_ifindex,
/*
* Look for a match on the ill.
- * (IPV6_LEAVE_GROUP specifies an ill using an ifindex).
- *
- * Similar to ip_addmulti_v6, we should always look using
- * the orig_ifindex.
- *
- * 1) If orig_ifindex is different from ill's ifindex
- * we should have an ilm with orig_ifindex created in
- * ip_addmulti_v6. We should delete that here.
- *
- * 2) If orig_ifindex is same as ill's ifindex, we should
- * not delete the ilm that is temporarily here because of
- * a FAILOVER. Those ilms will have a ilm_orig_ifindex
- * different from ill's ifindex.
- *
- * Thus, always lookup using orig_ifindex.
*/
- mutex_enter(&ill->ill_lock);
- ilm = ilm_lookup_ill_index_v6(ill, v6group, orig_ifindex, zoneid);
- mutex_exit(&ill->ill_lock);
+ ilm = ilm_lookup_ill_v6(ill, v6group, B_TRUE, zoneid);
if (ilm == NULL)
return (ENOENT);
@@ -985,11 +904,9 @@ ip_delmulti_v6(const in6_addr_t *v6group, ill_t *ill, int orig_ifindex,
return (0);
/* If we never joined, then don't leave. */
- if (ill->ill_join_allmulti) {
+ if (ill->ill_join_allmulti)
ill_leave_allmulti(ill);
- if (ill->ill_group != NULL)
- (void) ill_nominate_mcast_rcv(ill->ill_group);
- }
+
return (0);
}
@@ -1020,6 +937,13 @@ ip_ll_send_disabmulti_req(ill_t *ill, const in6_addr_t *v6groupp)
uint32_t addrlen, addroff;
ASSERT(IAM_WRITER_ILL(ill));
+
+ /*
+ * See comment in ip_ll_send_enabmulti_req().
+ */
+ if (IS_IPMP(ill) && (ill = ipmp_illgrp_cast_ill(ill->ill_grp)) == NULL)
+ return (0);
+
/*
* Create a AR_ENTRY_SQUERY message with a dl_disabmulti_req tacked
* on.
@@ -1099,16 +1023,16 @@ ip_ll_delmulti_v6(ipif_t *ipif, const in6_addr_t *v6group)
}
/*
- * Make the driver pass up all multicast packets
- *
- * With ill groups, the caller makes sure that there is only
- * one ill joining the allmulti group.
+ * Make the driver pass up all multicast packets. NOTE: to keep callers
+ * IPMP-unaware, if an IPMP ill is passed in, the ill_join_allmulti flag is
+ * set on it (rather than the cast ill).
*/
int
ill_join_allmulti(ill_t *ill)
{
mblk_t *promiscon_mp, *promiscoff_mp;
uint32_t addrlen, addroff;
+ ill_t *join_ill = ill;
ASSERT(IAM_WRITER_ILL(ill));
@@ -1120,7 +1044,13 @@ ill_join_allmulti(ill_t *ill)
return (0);
}
- ASSERT(!ill->ill_join_allmulti);
+ /*
+ * See comment in ip_ll_send_enabmulti_req().
+ */
+ if (IS_IPMP(ill) && (ill = ipmp_illgrp_cast_ill(ill->ill_grp)) == NULL)
+ return (0);
+
+ ASSERT(!join_ill->ill_join_allmulti);
/*
* Create a DL_PROMISCON_REQ message and send it directly to the DLPI
@@ -1144,20 +1074,18 @@ ill_join_allmulti(ill_t *ill)
ill_dlpi_send(ill, promiscon_mp);
}
- ill->ill_join_allmulti = B_TRUE;
+ join_ill->ill_join_allmulti = B_TRUE;
return (0);
}
/*
* Make the driver stop passing up all multicast packets
- *
- * With ill groups, we need to nominate some other ill as
- * this ipif->ipif_ill is leaving the group.
*/
void
ill_leave_allmulti(ill_t *ill)
{
- mblk_t *promiscoff_mp = ill->ill_promiscoff_mp;
+ mblk_t *promiscoff_mp;
+ ill_t *leave_ill = ill;
ASSERT(IAM_WRITER_ILL(ill));
@@ -1169,7 +1097,13 @@ ill_leave_allmulti(ill_t *ill)
return;
}
- ASSERT(ill->ill_join_allmulti);
+ /*
+ * See comment in ip_ll_send_enabmulti_req().
+ */
+ if (IS_IPMP(ill) && (ill = ipmp_illgrp_cast_ill(ill->ill_grp)) == NULL)
+ return;
+
+ ASSERT(leave_ill->ill_join_allmulti);
/*
* Create a DL_PROMISCOFF_REQ message and send it directly to
@@ -1179,12 +1113,13 @@ ill_leave_allmulti(ill_t *ill)
*/
if ((ill->ill_net_type == IRE_IF_RESOLVER) &&
!(ill->ill_phyint->phyint_flags & PHYI_MULTI_BCAST)) {
+ promiscoff_mp = ill->ill_promiscoff_mp;
ASSERT(promiscoff_mp != NULL);
ill->ill_promiscoff_mp = NULL;
ill_dlpi_send(ill, promiscoff_mp);
}
- ill->ill_join_allmulti = B_FALSE;
+ leave_ill->ill_join_allmulti = B_FALSE;
}
static ill_t *
@@ -1213,22 +1148,35 @@ int
ip_join_allmulti(uint_t ifindex, boolean_t isv6, ip_stack_t *ipst)
{
ill_t *ill;
- int ret;
+ int ret = 0;
if ((ill = ipsq_enter_byifindex(ifindex, isv6, ipst)) == NULL)
return (ENODEV);
+
+ /*
+ * The ip_addmulti*() functions won't allow IPMP underlying interfaces
+ * to join allmulti since only the nominated underlying interface in
+ * the group should receive multicast. We silently succeed to avoid
+ * having to teach IPobs (currently the only caller of this routine)
+ * to ignore failures in this case.
+ */
+ if (IS_UNDER_IPMP(ill))
+ goto out;
+
if (isv6) {
- ret = ip_addmulti_v6(&ipv6_all_zeros, ill, ifindex,
- ill->ill_zoneid, ILGSTAT_NONE, MODE_IS_EXCLUDE, NULL);
+ ret = ip_addmulti_v6(&ipv6_all_zeros, ill, ill->ill_zoneid,
+ ILGSTAT_NONE, MODE_IS_EXCLUDE, NULL);
} else {
ret = ip_addmulti(INADDR_ANY, ill->ill_ipif, ILGSTAT_NONE,
MODE_IS_EXCLUDE, NULL);
}
ill->ill_ipallmulti_cnt++;
+out:
ipsq_exit(ill->ill_phyint->phyint_ipsq);
return (ret);
}
+
int
ip_leave_allmulti(uint_t ifindex, boolean_t isv6, ip_stack_t *ipst)
{
@@ -1236,14 +1184,17 @@ ip_leave_allmulti(uint_t ifindex, boolean_t isv6, ip_stack_t *ipst)
if ((ill = ipsq_enter_byifindex(ifindex, isv6, ipst)) == NULL)
return (ENODEV);
- ASSERT(ill->ill_ipallmulti_cnt != 0);
- if (isv6) {
- (void) ip_delmulti_v6(&ipv6_all_zeros, ill, ifindex,
- ill->ill_zoneid, B_TRUE, B_TRUE);
- } else {
- (void) ip_delmulti(INADDR_ANY, ill->ill_ipif, B_TRUE, B_TRUE);
+
+ if (ill->ill_ipallmulti_cnt > 0) {
+ if (isv6) {
+ (void) ip_delmulti_v6(&ipv6_all_zeros, ill,
+ ill->ill_zoneid, B_TRUE, B_TRUE);
+ } else {
+ (void) ip_delmulti(INADDR_ANY, ill->ill_ipif, B_TRUE,
+ B_TRUE);
+ }
+ ill->ill_ipallmulti_cnt--;
}
- ill->ill_ipallmulti_cnt--;
ipsq_exit(ill->ill_phyint->phyint_ipsq);
return (0);
}
@@ -1260,8 +1211,7 @@ ip_purge_allmulti(ill_t *ill)
for (; ill->ill_ipallmulti_cnt > 0; ill->ill_ipallmulti_cnt--) {
if (ill->ill_isv6) {
(void) ip_delmulti_v6(&ipv6_all_zeros, ill,
- ill->ill_phyint->phyint_ifindex, ill->ill_zoneid,
- B_TRUE, B_TRUE);
+ ill->ill_zoneid, B_TRUE, B_TRUE);
} else {
(void) ip_delmulti(INADDR_ANY, ill->ill_ipif, B_TRUE,
B_TRUE);
@@ -1539,13 +1489,14 @@ void
ill_recover_multicast(ill_t *ill)
{
ilm_t *ilm;
+ ipif_t *ipif = ill->ill_ipif;
char addrbuf[INET6_ADDRSTRLEN];
ASSERT(IAM_WRITER_ILL(ill));
ill->ill_need_recover_multicast = 0;
- ILM_WALKER_HOLD(ill);
+ ill_ilm_walker_hold(ill);
for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
/*
* Check how many ipif's that have members in this group -
@@ -1553,47 +1504,45 @@ ill_recover_multicast(ill_t *ill)
* in the list.
*/
if (ilm_numentries_v6(ill, &ilm->ilm_v6addr) > 1 &&
- ilm_lookup_ill_v6(ill, &ilm->ilm_v6addr, ALL_ZONES) != ilm)
+ ilm_lookup_ill_v6(ill, &ilm->ilm_v6addr, B_TRUE,
+ ALL_ZONES) != ilm) {
continue;
- ip1dbg(("ill_recover_multicast: %s\n",
- inet_ntop(AF_INET6, &ilm->ilm_v6addr, addrbuf,
- sizeof (addrbuf))));
+ }
+
+ ip1dbg(("ill_recover_multicast: %s\n", inet_ntop(AF_INET6,
+ &ilm->ilm_v6addr, addrbuf, sizeof (addrbuf))));
+
if (IN6_IS_ADDR_UNSPECIFIED(&ilm->ilm_v6addr)) {
- if (ill->ill_group == NULL) {
- (void) ill_join_allmulti(ill);
- } else {
- /*
- * We don't want to join on this ill,
- * if somebody else in the group has
- * already been nominated.
- */
- (void) ill_nominate_mcast_rcv(ill->ill_group);
- }
+ (void) ill_join_allmulti(ill);
} else {
- (void) ip_ll_addmulti_v6(ill->ill_ipif,
- &ilm->ilm_v6addr);
+ if (ill->ill_isv6)
+ mld_joingroup(ilm);
+ else
+ igmp_joingroup(ilm);
+
+ (void) ip_ll_addmulti_v6(ipif, &ilm->ilm_v6addr);
}
}
- ILM_WALKER_RELE(ill);
+ ill_ilm_walker_rele(ill);
+
}
/*
* The opposite of ill_recover_multicast() -- leaves all multicast groups
- * that were explicitly joined. Note that both these functions could be
- * disposed of if we enhanced ARP to allow us to handle DL_DISABMULTI_REQ
- * and DL_ENABMULTI_REQ messages when an interface is down.
+ * that were explicitly joined.
*/
void
ill_leave_multicast(ill_t *ill)
{
ilm_t *ilm;
+ ipif_t *ipif = ill->ill_ipif;
char addrbuf[INET6_ADDRSTRLEN];
ASSERT(IAM_WRITER_ILL(ill));
ill->ill_need_recover_multicast = 1;
- ILM_WALKER_HOLD(ill);
+ ill_ilm_walker_hold(ill);
for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
/*
* Check how many ipif's that have members in this group -
@@ -1601,25 +1550,26 @@ ill_leave_multicast(ill_t *ill)
* in the list.
*/
if (ilm_numentries_v6(ill, &ilm->ilm_v6addr) > 1 &&
- ilm_lookup_ill_v6(ill, &ilm->ilm_v6addr, ALL_ZONES) != ilm)
+ ilm_lookup_ill_v6(ill, &ilm->ilm_v6addr, B_TRUE,
+ ALL_ZONES) != ilm) {
continue;
- ip1dbg(("ill_leave_multicast: %s\n",
- inet_ntop(AF_INET6, &ilm->ilm_v6addr, addrbuf,
- sizeof (addrbuf))));
+ }
+
+ ip1dbg(("ill_leave_multicast: %s\n", inet_ntop(AF_INET6,
+ &ilm->ilm_v6addr, addrbuf, sizeof (addrbuf))));
+
if (IN6_IS_ADDR_UNSPECIFIED(&ilm->ilm_v6addr)) {
ill_leave_allmulti(ill);
- /*
- * If we were part of an IPMP group, then
- * ill_handoff_responsibility() has already
- * nominated a new member (so we don't).
- */
- ASSERT(ill->ill_group == NULL);
} else {
- (void) ip_ll_delmulti_v6(ill->ill_ipif,
- &ilm->ilm_v6addr);
+ if (ill->ill_isv6)
+ mld_leavegroup(ilm);
+ else
+ igmp_leavegroup(ilm);
+
+ (void) ip_ll_delmulti_v6(ipif, &ilm->ilm_v6addr);
}
}
- ILM_WALKER_RELE(ill);
+ ill_ilm_walker_rele(ill);
}
/* Find an ilm for matching the ill */
@@ -1628,91 +1578,79 @@ ilm_lookup_ill(ill_t *ill, ipaddr_t group, zoneid_t zoneid)
{
in6_addr_t v6group;
- ASSERT(ill->ill_ilm_walker_cnt != 0 || MUTEX_HELD(&ill->ill_lock));
/*
- * INADDR_ANY is represented as the IPv6 unspecifed addr.
+ * INADDR_ANY is represented as the IPv6 unspecified addr.
*/
if (group == INADDR_ANY)
v6group = ipv6_all_zeros;
else
IN6_IPADDR_TO_V4MAPPED(group, &v6group);
- return (ilm_lookup_ill_v6(ill, &v6group, zoneid));
+ return (ilm_lookup_ill_v6(ill, &v6group, B_TRUE, zoneid));
}
/*
- * Find an ilm for matching the ill. All the ilm lookup functions
- * ignore ILM_DELETED ilms. These have been logically deleted, and
- * igmp and linklayer disable multicast have been done. Only mi_free
- * yet to be done. Still there in the list due to ilm_walkers. The
- * last walker will release it.
+ * Find an ilm for address `v6group' on `ill' and zone `zoneid' (which may be
+ * ALL_ZONES). In general, if `ill' is in an IPMP group, we will match
+ * against any ill in the group. However, if `restrict_solicited' is set,
+ * then specifically for IPv6 solicited-node multicast, the match will be
+ * restricted to the specified `ill'.
*/
ilm_t *
-ilm_lookup_ill_v6(ill_t *ill, const in6_addr_t *v6group, zoneid_t zoneid)
+ilm_lookup_ill_v6(ill_t *ill, const in6_addr_t *v6group,
+ boolean_t restrict_solicited, zoneid_t zoneid)
{
ilm_t *ilm;
+ ilm_walker_t ilw;
+ boolean_t restrict_ill = B_FALSE;
- ASSERT(ill->ill_ilm_walker_cnt != 0 || MUTEX_HELD(&ill->ill_lock));
+ /*
+ * In general, underlying interfaces cannot have multicast memberships
+ * and thus lookups always match across the illgrp. However, we must
+ * allow IPv6 solicited-node multicast memberships on underlying
+ * interfaces, and thus an IPMP meta-interface and one of its
+ * underlying ills may have the same solicited-node multicast address.
+ * In that case, we need to restrict the lookup to the requested ill.
+ * However, we may receive packets on an underlying interface that
+ * are for the corresponding IPMP interface's solicited-node multicast
+ * address, and thus in that case we need to match across the group --
+ * hence the unfortunate `restrict_solicited' argument.
+ */
+ if (IN6_IS_ADDR_MC_SOLICITEDNODE(v6group) && restrict_solicited)
+ restrict_ill = (IS_IPMP(ill) || IS_UNDER_IPMP(ill));
- for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
- if (ilm->ilm_flags & ILM_DELETED)
+ ilm = ilm_walker_start(&ilw, ill);
+ for (; ilm != NULL; ilm = ilm_walker_step(&ilw, ilm)) {
+ if (!IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, v6group))
continue;
- if (IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, v6group) &&
- (zoneid == ALL_ZONES || zoneid == ilm->ilm_zoneid))
- return (ilm);
- }
- return (NULL);
-}
-
-ilm_t *
-ilm_lookup_ill_index_v6(ill_t *ill, const in6_addr_t *v6group, int index,
- zoneid_t zoneid)
-{
- ilm_t *ilm;
-
- ASSERT(ill->ill_ilm_walker_cnt != 0 || MUTEX_HELD(&ill->ill_lock));
-
- for (ilm = ill->ill_ilm; ilm != NULL; ilm = ilm->ilm_next) {
- if (ilm->ilm_flags & ILM_DELETED)
+ if (zoneid != ALL_ZONES && zoneid != ilm->ilm_zoneid)
continue;
- if (IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, v6group) &&
- (zoneid == ALL_ZONES || zoneid == ilm->ilm_zoneid) &&
- ilm->ilm_orig_ifindex == index) {
- return (ilm);
+ if (!restrict_ill || ill == (ill->ill_isv6 ?
+ ilm->ilm_ill : ilm->ilm_ipif->ipif_ill)) {
+ break;
}
}
- return (NULL);
+ ilm_walker_finish(&ilw);
+ return (ilm);
}
-
/*
- * Found an ilm for the ipif. Only needed for IPv4 which does
+ * Find an ilm for the ipif. Only needed for IPv4 which does
* ipif specific socket options.
*/
ilm_t *
ilm_lookup_ipif(ipif_t *ipif, ipaddr_t group)
{
- ill_t *ill = ipif->ipif_ill;
- ilm_t *ilm;
- in6_addr_t v6group;
-
- ASSERT(ill->ill_ilm_walker_cnt != 0 || MUTEX_HELD(&ill->ill_lock));
- /*
- * INADDR_ANY is represented as the IPv6 unspecifed addr.
- */
- if (group == INADDR_ANY)
- v6group = ipv6_all_zeros;
- else
- IN6_IPADDR_TO_V4MAPPED(group, &v6group);
+ ilm_t *ilm;
+ ilm_walker_t ilw;
- for (ilm = ill->ill_ilm; ilm; ilm = ilm->ilm_next) {
- if (ilm->ilm_flags & ILM_DELETED)
- continue;
- if (ilm->ilm_ipif == ipif &&
- IN6_ARE_ADDR_EQUAL(&ilm->ilm_v6addr, &v6group))
- return (ilm);
+ ilm = ilm_walker_start(&ilw, ipif->ipif_ill);
+ for (; ilm != NULL; ilm = ilm_walker_step(&ilw, ilm)) {
+ if (ilm->ilm_ipif == ipif && ilm->ilm_addr == group)
+ break;
}
- return (NULL);
+ ilm_walker_finish(&ilw);
+ return (ilm);
}
/*
@@ -1739,8 +1677,7 @@ ilm_numentries_v6(ill_t *ill, const in6_addr_t *v6group)
/* Caller guarantees that the group is not already on the list */
static ilm_t *
ilm_add_v6(ipif_t *ipif, const in6_addr_t *v6group, ilg_stat_t ilgstat,
- mcast_record_t ilg_fmode, slist_t *ilg_flist, int orig_ifindex,
- zoneid_t zoneid)
+ mcast_record_t ilg_fmode, slist_t *ilg_flist, zoneid_t zoneid)
{
ill_t *ill = ipif->ipif_ill;
ilm_t *ilm;
@@ -1783,19 +1720,10 @@ ilm_add_v6(ipif_t *ipif, const in6_addr_t *v6group, ilg_stat_t ilgstat,
(char *), "ilm", (void *), ilm);
ipif->ipif_ilm_cnt++;
}
+
ASSERT(ill->ill_ipst);
ilm->ilm_ipst = ill->ill_ipst; /* No netstack_hold */
- /*
- * After this if ilm moves to a new ill, we don't change
- * the ilm_orig_ifindex. Thus, if ill_index != ilm_orig_ifindex,
- * it has been moved. Indexes don't match even when the application
- * wants to join on a FAILED/INACTIVE interface because we choose
- * a new interface to join in. This is considered as an implicit
- * move.
- */
- ilm->ilm_orig_ifindex = orig_ifindex;
-
ASSERT(!(ipif->ipif_state_flags & IPIF_CONDEMNED));
ASSERT(!(ill->ill_state_flags & ILL_CONDEMNED));
@@ -1969,6 +1897,108 @@ ilm_delete(ilm_t *ilm)
}
}
+/* Increment the ILM walker count for `ill' */
+static void
+ill_ilm_walker_hold(ill_t *ill)
+{
+ mutex_enter(&ill->ill_lock);
+ ill->ill_ilm_walker_cnt++;
+ mutex_exit(&ill->ill_lock);
+}
+
+/* Decrement the ILM walker count for `ill' */
+static void
+ill_ilm_walker_rele(ill_t *ill)
+{
+ mutex_enter(&ill->ill_lock);
+ ill->ill_ilm_walker_cnt--;
+ if (ill->ill_ilm_walker_cnt == 0 && ill->ill_ilm_cleanup_reqd)
+ ilm_walker_cleanup(ill); /* drops ill_lock */
+ else
+ mutex_exit(&ill->ill_lock);
+}
+
+/*
+ * Start walking the ILMs associated with `ill'; the first ILM in the walk
+ * (if any) is returned. State associated with the walk is stored in `ilw'.
+ * Note that walks associated with interfaces under IPMP also walk the ILMs
+ * on the associated IPMP interface; this is handled transparently to callers
+ * via ilm_walker_step(). (Usually with IPMP all ILMs will be on the IPMP
+ * interface; the only exception is to support IPv6 test addresses, which
+ * require ILMs for their associated solicited-node multicast addresses.)
+ */
+ilm_t *
+ilm_walker_start(ilm_walker_t *ilw, ill_t *ill)
+{
+ ilw->ilw_ill = ill;
+ if (IS_UNDER_IPMP(ill))
+ ilw->ilw_ipmp_ill = ipmp_ill_hold_ipmp_ill(ill);
+ else
+ ilw->ilw_ipmp_ill = NULL;
+
+ ill_ilm_walker_hold(ill);
+ if (ilw->ilw_ipmp_ill != NULL)
+ ill_ilm_walker_hold(ilw->ilw_ipmp_ill);
+
+ if (ilw->ilw_ipmp_ill != NULL && ilw->ilw_ipmp_ill->ill_ilm != NULL)
+ ilw->ilw_walk_ill = ilw->ilw_ipmp_ill;
+ else
+ ilw->ilw_walk_ill = ilw->ilw_ill;
+
+ return (ilm_walker_step(ilw, NULL));
+}
+
+/*
+ * Helper function for ilm_walker_step() that returns the next ILM
+ * associated with `ilw', regardless of whether it's deleted.
+ */
+static ilm_t *
+ilm_walker_step_all(ilm_walker_t *ilw, ilm_t *ilm)
+{
+ if (ilm == NULL)
+ return (ilw->ilw_walk_ill->ill_ilm);
+
+ if (ilm->ilm_next != NULL)
+ return (ilm->ilm_next);
+
+ if (ilw->ilw_ipmp_ill != NULL && IS_IPMP(ilw->ilw_walk_ill)) {
+ ilw->ilw_walk_ill = ilw->ilw_ill;
+ /*
+ * It's possible that ilw_ill left the group during our walk,
+ * so we can't ASSERT() that it's under IPMP. Callers that
+ * care will be writer on the IPSQ anyway.
+ */
+ return (ilw->ilw_walk_ill->ill_ilm);
+ }
+ return (NULL);
+}
+
+/*
+ * Step to the next ILM associated with `ilw'.
+ */
+ilm_t *
+ilm_walker_step(ilm_walker_t *ilw, ilm_t *ilm)
+{
+ while ((ilm = ilm_walker_step_all(ilw, ilm)) != NULL) {
+ if (!(ilm->ilm_flags & ILM_DELETED))
+ break;
+ }
+ return (ilm);
+}
+
+/*
+ * Finish the ILM walk associated with `ilw'.
+ */
+void
+ilm_walker_finish(ilm_walker_t *ilw)
+{
+ ill_ilm_walker_rele(ilw->ilw_ill);
+ if (ilw->ilw_ipmp_ill != NULL) {
+ ill_ilm_walker_rele(ilw->ilw_ipmp_ill);
+ ill_refrele(ilw->ilw_ipmp_ill);
+ }
+ bzero(&ilw, sizeof (ilw));
+}
/*
* Looks up the appropriate ipif given a v4 multicast group and interface
@@ -2256,16 +2286,15 @@ ip_set_srcfilter(conn_t *connp, struct group_filter *gf,
* didn't find an ilg, there's nothing to do.
*/
if (!leave_grp)
- ilg = conn_ilg_alloc(connp);
+ ilg = conn_ilg_alloc(connp, &err);
if (leave_grp || ilg == NULL) {
mutex_exit(&connp->conn_lock);
- return (leave_grp ? 0 : ENOMEM);
+ return (leave_grp ? 0 : err);
}
ilgstat = ILGSTAT_NEW;
IN6_IPADDR_TO_V4MAPPED(grp, &ilg->ilg_v6group);
ilg->ilg_ipif = ipif;
ilg->ilg_ill = NULL;
- ilg->ilg_orig_ifindex = 0;
} else if (leave_grp) {
ilg_delete(connp, ilg, NULL);
mutex_exit(&connp->conn_lock);
@@ -2389,7 +2418,7 @@ ip_set_srcfilter_v6(conn_t *connp, struct group_filter *gf,
const struct in6_addr *grp, ill_t *ill)
{
ilg_t *ilg;
- int i, orig_ifindex, orig_fmode, new_fmode, err;
+ int i, orig_fmode, new_fmode, err;
slist_t *orig_filter = NULL;
slist_t *new_filter = NULL;
struct sockaddr_storage *sl;
@@ -2409,65 +2438,31 @@ ip_set_srcfilter_v6(conn_t *connp, struct group_filter *gf,
ASSERT(IAM_WRITER_ILL(ill));
- /*
- * Use the ifindex to do the lookup. We can't use the ill
- * directly because ilg_ill could point to a different ill
- * if things have moved.
- */
- orig_ifindex = ill->ill_phyint->phyint_ifindex;
-
mutex_enter(&connp->conn_lock);
- ilg = ilg_lookup_ill_index_v6(connp, grp, orig_ifindex);
+ ilg = ilg_lookup_ill_v6(connp, grp, ill);
if (ilg == NULL) {
/*
* if the request was actually to leave, and we
* didn't find an ilg, there's nothing to do.
*/
if (!leave_grp)
- ilg = conn_ilg_alloc(connp);
+ ilg = conn_ilg_alloc(connp, &err);
if (leave_grp || ilg == NULL) {
mutex_exit(&connp->conn_lock);
- return (leave_grp ? 0 : ENOMEM);
+ return (leave_grp ? 0 : err);
}
ilgstat = ILGSTAT_NEW;
ilg->ilg_v6group = *grp;
ilg->ilg_ipif = NULL;
- /*
- * Choose our target ill to join on. This might be
- * different from the ill we've been given if it's
- * currently down and part of a group.
- *
- * new ill is not refheld; we are writer.
- */
- ill = ip_choose_multi_ill(ill, grp);
- ASSERT(!(ill->ill_state_flags & ILL_CONDEMNED));
ilg->ilg_ill = ill;
- /*
- * Remember the index that we joined on, so that we can
- * successfully delete them later on and also search for
- * duplicates if the application wants to join again.
- */
- ilg->ilg_orig_ifindex = orig_ifindex;
} else if (leave_grp) {
- /*
- * Use the ilg's current ill for the deletion,
- * we might have failed over.
- */
- ill = ilg->ilg_ill;
ilg_delete(connp, ilg, NULL);
mutex_exit(&connp->conn_lock);
- (void) ip_delmulti_v6(grp, ill, orig_ifindex,
- connp->conn_zoneid, B_FALSE, B_TRUE);
+ (void) ip_delmulti_v6(grp, ill, connp->conn_zoneid, B_FALSE,
+ B_TRUE);
return (0);
} else {
ilgstat = ILGSTAT_CHANGE;
- /*
- * The current ill might be different from the one we were
- * asked to join on (if failover has occurred); we should
- * join on the ill stored in the ilg. The original ill
- * is noted in ilg_orig_ifindex, which matched our request.
- */
- ill = ilg->ilg_ill;
/* preserve existing state in case ip_addmulti() fails */
orig_fmode = ilg->ilg_fmode;
if (ilg->ilg_filter == NULL) {
@@ -2531,8 +2526,8 @@ ip_set_srcfilter_v6(conn_t *connp, struct group_filter *gf,
mutex_exit(&connp->conn_lock);
- err = ip_addmulti_v6(grp, ill, orig_ifindex, connp->conn_zoneid,
- ilgstat, new_fmode, new_filter);
+ err = ip_addmulti_v6(grp, ill, connp->conn_zoneid, ilgstat, new_fmode,
+ new_filter);
if (err != 0) {
/*
* Restore the original filter state, or delete the
@@ -2541,7 +2536,7 @@ ip_set_srcfilter_v6(conn_t *connp, struct group_filter *gf,
* conn_lock.
*/
mutex_enter(&connp->conn_lock);
- ilg = ilg_lookup_ill_index_v6(connp, grp, orig_ifindex);
+ ilg = ilg_lookup_ill_v6(connp, grp, ill);
ASSERT(ilg != NULL);
if (ilgstat == ILGSTAT_NEW) {
ilg_delete(connp, ilg, NULL);
@@ -3043,20 +3038,12 @@ ip_opt_delete_group_excl_v6(conn_t *connp, const in6_addr_t *v6group,
ill_t *ill, mcast_record_t fmode, const in6_addr_t *v6src)
{
ilg_t *ilg;
- ill_t *ilg_ill;
- uint_t ilg_orig_ifindex;
boolean_t leaving = B_TRUE;
ASSERT(IAM_WRITER_ILL(ill));
- /*
- * Use the index that we originally used to join. We can't
- * use the ill directly because ilg_ill could point to
- * a new ill if things have moved.
- */
mutex_enter(&connp->conn_lock);
- ilg = ilg_lookup_ill_index_v6(connp, v6group,
- ill->ill_phyint->phyint_ifindex);
+ ilg = ilg_lookup_ill_v6(connp, v6group, ill);
if ((ilg == NULL) || (ilg->ilg_flags & ILG_DELETED)) {
mutex_exit(&connp->conn_lock);
return (EADDRNOTAVAIL);
@@ -3087,12 +3074,10 @@ ip_opt_delete_group_excl_v6(conn_t *connp, const in6_addr_t *v6group,
leaving = B_FALSE;
}
- ilg_ill = ilg->ilg_ill;
- ilg_orig_ifindex = ilg->ilg_orig_ifindex;
ilg_delete(connp, ilg, v6src);
mutex_exit(&connp->conn_lock);
- (void) ip_delmulti_v6(v6group, ilg_ill, ilg_orig_ifindex,
- connp->conn_zoneid, B_FALSE, leaving);
+ (void) ip_delmulti_v6(v6group, ill, connp->conn_zoneid, B_FALSE,
+ leaving);
return (0);
}
@@ -3345,10 +3330,10 @@ ilg_add(conn_t *connp, ipaddr_t group, ipif_t *ipif, mcast_record_t fmode,
if (ilg == NULL) {
ilgstat = ILGSTAT_NEW;
- if ((ilg = conn_ilg_alloc(connp)) == NULL) {
+ if ((ilg = conn_ilg_alloc(connp, &error)) == NULL) {
mutex_exit(&connp->conn_lock);
l_free(new_filter);
- return (ENOMEM);
+ return (error);
}
if (src != INADDR_ANY) {
ilg->ilg_filter = l_alloc();
@@ -3369,7 +3354,6 @@ ilg_add(conn_t *connp, ipaddr_t group, ipif_t *ipif, mcast_record_t fmode,
}
ilg->ilg_ipif = ipif;
ilg->ilg_ill = NULL;
- ilg->ilg_orig_ifindex = 0;
ilg->ilg_fmode = fmode;
} else {
int index;
@@ -3437,7 +3421,6 @@ ilg_add_v6(conn_t *connp, const in6_addr_t *v6group, ill_t *ill,
mcast_record_t fmode, const in6_addr_t *v6src)
{
int error = 0;
- int orig_ifindex;
ilg_t *ilg;
ilg_stat_t ilgstat;
slist_t *new_filter = NULL;
@@ -3456,13 +3439,7 @@ ilg_add_v6(conn_t *connp, const in6_addr_t *v6group, ill_t *ill,
*/
mutex_enter(&connp->conn_lock);
- /*
- * Use the ifindex to do the lookup. We can't use the ill
- * directly because ilg_ill could point to a different ill if
- * things have moved.
- */
- orig_ifindex = ill->ill_phyint->phyint_ifindex;
- ilg = ilg_lookup_ill_index_v6(connp, v6group, orig_ifindex);
+ ilg = ilg_lookup_ill_v6(connp, v6group, ill);
/*
* Depending on the option we're handling, may or may not be okay
@@ -3501,10 +3478,10 @@ ilg_add_v6(conn_t *connp, const in6_addr_t *v6group, ill_t *ill,
}
if (ilg == NULL) {
- if ((ilg = conn_ilg_alloc(connp)) == NULL) {
+ if ((ilg = conn_ilg_alloc(connp, &error)) == NULL) {
mutex_exit(&connp->conn_lock);
l_free(new_filter);
- return (ENOMEM);
+ return (error);
}
if (!IN6_IS_ADDR_UNSPECIFIED(v6src)) {
ilg->ilg_filter = l_alloc();
@@ -3521,22 +3498,7 @@ ilg_add_v6(conn_t *connp, const in6_addr_t *v6group, ill_t *ill,
ilg->ilg_v6group = *v6group;
ilg->ilg_fmode = fmode;
ilg->ilg_ipif = NULL;
- /*
- * Choose our target ill to join on. This might be different
- * from the ill we've been given if it's currently down and
- * part of a group.
- *
- * new ill is not refheld; we are writer.
- */
- ill = ip_choose_multi_ill(ill, v6group);
- ASSERT(!(ill->ill_state_flags & ILL_CONDEMNED));
ilg->ilg_ill = ill;
- /*
- * Remember the orig_ifindex that we joined on, so that we
- * can successfully delete them later on and also search
- * for duplicates if the application wants to join again.
- */
- ilg->ilg_orig_ifindex = orig_ifindex;
} else {
int index;
if (ilg->ilg_fmode != fmode || IN6_IS_ADDR_UNSPECIFIED(v6src)) {
@@ -3560,13 +3522,6 @@ ilg_add_v6(conn_t *connp, const in6_addr_t *v6group, ill_t *ill,
ilgstat = ILGSTAT_CHANGE;
index = ilg->ilg_filter->sl_numsrc++;
ilg->ilg_filter->sl_addr[index] = *v6src;
- /*
- * The current ill might be different from the one we were
- * asked to join on (if failover has occurred); we should
- * join on the ill stored in the ilg. The original ill
- * is noted in ilg_orig_ifindex, which matched our request.
- */
- ill = ilg->ilg_ill;
}
/*
@@ -3584,8 +3539,8 @@ ilg_add_v6(conn_t *connp, const in6_addr_t *v6group, ill_t *ill,
* info for the ill, which involves looking at the status of
* all the ilgs associated with this group/interface pair.
*/
- error = ip_addmulti_v6(v6group, ill, orig_ifindex, connp->conn_zoneid,
- ilgstat, new_fmode, new_filter);
+ error = ip_addmulti_v6(v6group, ill, connp->conn_zoneid, ilgstat,
+ new_fmode, new_filter);
if (error != 0) {
/*
* But because we waited, we have to undo the ilg update
@@ -3595,7 +3550,7 @@ ilg_add_v6(conn_t *connp, const in6_addr_t *v6group, ill_t *ill,
in6_addr_t delsrc =
(ilgstat == ILGSTAT_NEW) ? ipv6_all_zeros : *v6src;
mutex_enter(&connp->conn_lock);
- ilg = ilg_lookup_ill_index_v6(connp, v6group, orig_ifindex);
+ ilg = ilg_lookup_ill_v6(connp, v6group, ill);
ASSERT(ilg != NULL);
ilg_delete(connp, ilg, &delsrc);
mutex_exit(&connp->conn_lock);
@@ -3639,7 +3594,7 @@ ilg_lookup_ill_withsrc(conn_t *connp, ipaddr_t group, ipaddr_t src, ill_t *ill)
ASSERT(ilg->ilg_ill == NULL);
ilg_ill = ipif->ipif_ill;
ASSERT(!ilg_ill->ill_isv6);
- if (ilg_ill == ill &&
+ if (IS_ON_SAME_LAN(ilg_ill, ill) &&
IN6_ARE_ADDR_EQUAL(&ilg->ilg_v6group, &v6group)) {
if (SLIST_IS_EMPTY(ilg->ilg_filter)) {
/* no source filter, so this is a match */
@@ -3692,7 +3647,7 @@ ilg_lookup_ill_withsrc_v6(conn_t *connp, const in6_addr_t *v6group,
continue;
ASSERT(ilg->ilg_ipif == NULL);
ASSERT(ilg_ill->ill_isv6);
- if (ilg_ill == ill &&
+ if (IS_ON_SAME_LAN(ilg_ill, ill) &&
IN6_ARE_ADDR_EQUAL(&ilg->ilg_v6group, v6group)) {
if (SLIST_IS_EMPTY(ilg->ilg_filter)) {
/* no source filter, so this is a match */
@@ -3724,35 +3679,6 @@ ilg_lookup_ill_withsrc_v6(conn_t *connp, const in6_addr_t *v6group,
}
/*
- * Get the ilg whose ilg_orig_ifindex is associated with ifindex.
- * This is useful when the interface fails and we have moved
- * to a new ill, but still would like to locate using the index
- * that we originally used to join. Used only for IPv6 currently.
- */
-static ilg_t *
-ilg_lookup_ill_index_v6(conn_t *connp, const in6_addr_t *v6group, int ifindex)
-{
- ilg_t *ilg;
- int i;
-
- ASSERT(MUTEX_HELD(&connp->conn_lock));
- for (i = 0; i < connp->conn_ilg_inuse; i++) {
- ilg = &connp->conn_ilg[i];
- if (ilg->ilg_ill == NULL ||
- (ilg->ilg_flags & ILG_DELETED) != 0)
- continue;
- /* ilg_ipif is NULL for V6 */
- ASSERT(ilg->ilg_ipif == NULL);
- ASSERT(ilg->ilg_orig_ifindex != 0);
- if (IN6_ARE_ADDR_EQUAL(&ilg->ilg_v6group, v6group) &&
- ilg->ilg_orig_ifindex == ifindex) {
- return (ilg);
- }
- }
- return (NULL);
-}
-
-/*
* Find an IPv6 ilg matching group and ill
*/
ilg_t *
@@ -3863,32 +3789,28 @@ ilg_delete_all(conn_t *connp)
in6_addr_t v6group;
boolean_t success;
ipsq_t *ipsq;
- int orig_ifindex;
mutex_enter(&connp->conn_lock);
retry:
ILG_WALKER_HOLD(connp);
- for (i = connp->conn_ilg_inuse - 1; i >= 0; ) {
+ for (i = connp->conn_ilg_inuse - 1; i >= 0; i--) {
ilg = &connp->conn_ilg[i];
/*
* Since this walk is not atomic (we drop the
* conn_lock and wait in ipsq_enter) we need
* to check for the ILG_DELETED flag.
*/
- if (ilg->ilg_flags & ILG_DELETED) {
- /* Go to the next ilg */
- i--;
+ if (ilg->ilg_flags & ILG_DELETED)
continue;
- }
- v6group = ilg->ilg_v6group;
- if (IN6_IS_ADDR_V4MAPPED(&v6group)) {
+ if (IN6_IS_ADDR_V4MAPPED(&ilg->ilg_v6group)) {
ipif = ilg->ilg_ipif;
ill = ipif->ipif_ill;
} else {
ipif = NULL;
ill = ilg->ilg_ill;
}
+
/*
* We may not be able to refhold the ill if the ill/ipif
* is changing. But we need to make sure that the ill will
@@ -3897,11 +3819,9 @@ retry:
* in which case the unplumb thread will handle the cleanup,
* and we move on to the next ilg.
*/
- if (!ill_waiter_inc(ill)) {
- /* Go to the next ilg */
- i--;
+ if (!ill_waiter_inc(ill))
continue;
- }
+
mutex_exit(&connp->conn_lock);
/*
* To prevent deadlock between ill close which waits inside
@@ -3916,51 +3836,31 @@ retry:
ipsq = ill->ill_phyint->phyint_ipsq;
ill_waiter_dcr(ill);
mutex_enter(&connp->conn_lock);
- if (!success) {
- /* Go to the next ilg */
- i--;
+ if (!success)
continue;
- }
/*
- * Make sure that nothing has changed under. For eg.
- * a failover/failback can change ilg_ill while we were
- * waiting to become exclusive above
+ * Move on if the ilg was deleted while conn_lock was dropped.
*/
- if (IN6_IS_ADDR_V4MAPPED(&v6group)) {
- ipif = ilg->ilg_ipif;
- ill = ipif->ipif_ill;
- } else {
- ipif = NULL;
- ill = ilg->ilg_ill;
- }
- if (!IAM_WRITER_ILL(ill) || (ilg->ilg_flags & ILG_DELETED)) {
- /*
- * The ilg has changed under us probably due
- * to a failover or unplumb. Retry on the same ilg.
- */
+ if (ilg->ilg_flags & ILG_DELETED) {
mutex_exit(&connp->conn_lock);
ipsq_exit(ipsq);
mutex_enter(&connp->conn_lock);
continue;
}
v6group = ilg->ilg_v6group;
- orig_ifindex = ilg->ilg_orig_ifindex;
ilg_delete(connp, ilg, NULL);
mutex_exit(&connp->conn_lock);
- if (ipif != NULL)
+ if (ipif != NULL) {
(void) ip_delmulti(V4_PART_OF_V6(v6group), ipif,
B_FALSE, B_TRUE);
-
- else
- (void) ip_delmulti_v6(&v6group, ill, orig_ifindex,
+ } else {
+ (void) ip_delmulti_v6(&v6group, ill,
connp->conn_zoneid, B_FALSE, B_TRUE);
-
+ }
ipsq_exit(ipsq);
mutex_enter(&connp->conn_lock);
- /* Go to the next ilg */
- i--;
}
ILG_WALKER_RELE(connp);
@@ -4063,7 +3963,6 @@ conn_delete_ill(conn_t *connp, caddr_t arg)
int i;
char group_buf[INET6_ADDRSTRLEN];
in6_addr_t v6group;
- int orig_ifindex;
ilg_t *ilg;
/*
@@ -4097,11 +3996,10 @@ conn_delete_ill(conn_t *connp, caddr_t arg)
ill->ill_name));
v6group = ilg->ilg_v6group;
- orig_ifindex = ilg->ilg_orig_ifindex;
ilg_delete(connp, ilg, NULL);
mutex_exit(&connp->conn_lock);
- (void) ip_delmulti_v6(&v6group, ill, orig_ifindex,
+ (void) ip_delmulti_v6(&v6group, ill,
connp->conn_zoneid, B_FALSE, B_TRUE);
mutex_enter(&connp->conn_lock);
}
@@ -4115,7 +4013,6 @@ conn_delete_ill(conn_t *connp, caddr_t arg)
if (connp->conn_multicast_ill == ill) {
/* Revert to late binding */
connp->conn_multicast_ill = NULL;
- connp->conn_orig_multicast_ifindex = 0;
}
mutex_exit(&connp->conn_lock);
}