ip_multi.c revision de8c4a14ec9a49bad5e62b2cfa6c1ba21de1c708
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1990 Mentat Inc. */
#include <inet/ip_multi.h>
#include <inet/ipclassifier.h>
#include <inet/ipsec_impl.h>
#include <inet/ip_listutils.h>
#include <inet/udp_impl.h>
const in6_addr_t *v6src);
/*
* 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
* potentially operating on different conn (socket endpoints) trying to
* do multicast joins could eventually end up trying to manipulate the
* ilm simultaneously and need to synchronize access to the ilm. Currently,
* serialization.
*
* 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
* referencing the ilm. ilms are created / destroyed only as writer. ilms
* are not passed around, instead they are looked up and used under the
* ill_lock or as writer. So we don't need a dynamic refcount of the number
* of threads holding reference to an ilm.
*
* Multicast Join operation:
*
* The first step is to determine the ipif (v4) or ill (v6) on which
* the join operation is to be done. The join is done after becoming
* exclusive on the ipsq associated with the ipif or ill. The conn->conn_ilg
* and ill->ill_ilm are thus accessed and modified exclusively per ill.
* on the same conn. In this case the ipsq serialization does not help in
* protecting the ilg. It is the conn_lock that is used to protect the ilg.
* The conn_lock also protects all the ilg_t members.
*
* Leave operation.
*
* Similar to the join operation, the first step is to determine the ipif
* or ill (v6) on which the leave operation is to be done. The leave operation
* is done after becoming exclusive on the ipsq associated with the ipif or ill.
* As with join ilg modification is done under the protection of the conn lock.
*/
ipif_refrele(ipif); \
return (EINPROGRESS); \
}
ill_refrele(ill); \
return (EINPROGRESS); \
}
#define ILG_WALKER_RELE(connp) \
{ \
(connp)->conn_ilg_walker_cnt--; \
if ((connp)->conn_ilg_walker_cnt == 0) \
conn_ilg_reap(connp); \
}
static void
{
int to;
int from;
to = 0;
from = 0;
from++;
continue;
}
to++;
from++;
}
if (connp->conn_ilg_inuse == 0) {
}
}
#define ILG_ALLOC_CHUNK 16
/*
* 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, in which case `*errp' will be
* filled in with the reason.
*
* Assumes connp->conn_lock is held.
*/
static ilg_t *
{
int curcnt;
/*
* If CONN_CLOSING is set, conn_ilg cleanup has begun and we must not
* create any ilgs.
*/
return (NULL);
}
return (NULL);
}
connp->conn_ilg_inuse = 0;
}
if (connp->conn_ilg_walker_cnt != 0) {
/*
* XXX We cannot grow the array at this point
* because a list walker could be in progress, and
* we cannot wipe out the existing array until the
* walker is done. Just return NULL for now.
* ilg_delete_all() will have to be changed when
* this logic is changed.
*/
return (NULL);
}
return (NULL);
}
}
return (ret);
}
typedef struct ilm_fbld_s {
int fbld_in_cnt;
int fbld_ex_cnt;
} ilm_fbld_t;
static void
{
int i;
if (conn->conn_ilg_inuse == 0)
return;
/*
* Since we can't break out of the ipcl_walk once started, we still
* have to look at every conn. But if we've already found one
* (EXCLUDE, NULL) list, there's no need to keep checking individual
* ilgs--that will be our state.
*/
return;
/*
* Check this conn's ilgs to see if any are interested in our
* ilm (group, interface match). If so, update the master
* include and exclude lists we're building in the fbld struct
* with this ilg's filter info.
*/
for (i = 0; i < conn->conn_ilg_inuse; i++) {
fbld->fbld_in_cnt++;
if (!fbld->fbld_in_overflow)
&fbld->fbld_in_overflow);
} else {
fbld->fbld_ex_cnt++;
/*
* On the first exclude list, don't try to do
* an intersection, as the master exclude list
* is intentionally empty. If the master list
* is still empty on later iterations, that
* means we have at least one ilg with an empty
* exclude list, so that should be reflected
* when we take the intersection.
*/
} else {
ilg->ilg_filter);
}
}
/* there will only be one match, so break now. */
break;
}
}
}
static void
{
/* first, construct our master include and exclude lists */
/* now use those master lists to generate the interface filter */
/* if include list overflowed, filter is (EXCLUDE, NULL) */
if (fbld.fbld_in_overflow) {
*fmode = MODE_IS_EXCLUDE;
return;
}
/* if nobody interested, interface filter is (INCLUDE, NULL) */
*fmode = MODE_IS_INCLUDE;
return;
}
/*
* If there are no exclude lists, then the interface filter
* is INCLUDE, with its filter list equal to fbld_in. A single
* exclude list makes the interface filter EXCLUDE, with its
* filter list equal to (fbld_ex - fbld_in).
*/
if (fbld.fbld_ex_cnt == 0) {
*fmode = MODE_IS_INCLUDE;
} else {
*fmode = MODE_IS_EXCLUDE;
}
}
static int
{
char buf[INET6_ADDRSTRLEN];
/*
* There are several cases where the ilm's filter state
* defaults to (EXCLUDE, NULL):
* - we've had previous joins without associated ilgs
* - this join has no associated ilg
* - the ilg's filter state is (EXCLUDE, NULL)
*/
/* attempt mallocs (if needed) before doing anything else */
return (ENOMEM);
return (ENOMEM);
}
}
if (ilgstat != ILGSTAT_CHANGE)
ilm->ilm_refcnt++;
if (ilgstat == ILGSTAT_NONE)
ilm->ilm_no_ilg_cnt++;
/*
* Determine new filter state. If it's not the default
* (EXCLUDE, NULL), we must walk the conn list to find
* any ilgs interested in this group, and re-build the
* ilm filter.
*/
if (fdefault) {
} else {
}
/* make sure state actually changed; nothing to do if not. */
return (0);
}
/* send the state change report */
if (!IS_LOOPBACK(ill)) {
if (isv6)
else
}
/* update the ilm state */
else
return (0);
}
static int
{
ip1dbg(("ilm_update_del: still %d left; updating state\n",
ilm->ilm_refcnt));
return (ENOMEM);
/*
* If present, the ilg in question has already either been
* updated or removed from our list; so all we need to do
* now is walk the list to update the ilm filter state.
*
* Skip the list walk if we have any no-ilg joins, which
* cause the filter state to revert to (EXCLUDE, NULL).
*/
if (ilm->ilm_no_ilg_cnt != 0) {
} else {
}
/* check to see if state needs to be updated */
return (0);
}
if (!IS_LOOPBACK(ill)) {
if (isv6)
else
}
char buf[INET6_ADDRSTRLEN];
ip1dbg(("ilm_update_del: failed to alloc ilm "
"filter; no source filtering for %s on %s",
return (0);
}
}
} else {
}
return (0);
}
/*
* INADDR_ANY means all multicast addresses.
* INADDR_ANY is stored as IPv6 unspecified addr.
*/
int
{
int ret;
return (EINVAL);
if (IS_UNDER_IPMP(ill))
return (EINVAL);
/*
* INADDR_ANY is represented as the IPv6 unspecified addr.
*/
if (group == INADDR_ANY)
else
/*
* 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->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).
*/
ipif->ipif_zoneid);
return (ENOMEM);
if (group == INADDR_ANY) {
/*
* Check how many ipif's have members in this group -
* if more then one we should not tell the driver to join
* this time
*/
return (0);
if (ret != 0)
return (ret);
}
if (!IS_LOOPBACK(ill))
return (0);
if (ret != 0)
return (ret);
}
/*
* The unspecified address means all multicast addresses.
*
* 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.
* ilg_fmode and ilg_flist give us the current filter state of
* the ilg (and will be EXCLUDE {NULL} in the case of no ilg).
*/
int
{
int ret;
if (!IN6_IS_ADDR_MULTICAST(v6group) &&
return (EINVAL);
}
return (EINVAL);
/*
* 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.
*/
return (ENOMEM);
if (IN6_IS_ADDR_UNSPECIFIED(v6group)) {
/*
* Check how many ipif's that have members in this group -
* if more then one we should not tell the driver to join
* this time
*/
return (0);
if (ret != 0)
return (ret);
}
if (!IS_LOOPBACK(ill))
/*
* If we have more then one we should not tell the driver
* to join this time.
*/
return (0);
if (ret != 0)
return (ret);
}
/*
* Send a multicast request to the driver for enabling multicast reception
* for v6groupp address. The caller has already checked whether it is
* appropriate to send one or not.
*/
int
{
char group_buf[INET6_ADDRSTRLEN];
/*
* 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.)
*/
return (0);
/*
* Create a AR_ENTRY_SQUERY message with a dl_enabmulti_req tacked
* on.
*/
if (!mp)
return (ENOMEM);
if (IN6_IS_ADDR_V4MAPPED(v6groupp)) {
/*
* NOTE!!!
* The "addroff" passed in here was calculated by
* ill_create_dl(), and will be used by ill_create_squery()
* to perform some twisted coding magic. It is the offset
* into the dl_xxx_req of the hw addr. Here, it will be
* added to b_wptr - b_rptr to create a magic number that
* is not an offset into this squery mblk.
* The actual hardware address will be accessed only in the
* dl_xxx_req, not in the squery. More importantly,
* that hardware address can *only* be accessed in this
* mblk chain by calling mi_offset_param_c(), which uses
* the magic number in the squery hw offset field to go
* to the *next* mblk (the dl_xxx_req), subtract the
* (b_wptr - b_rptr), and find the actual offset into
* the dl_xxx_req.
* Any method that depends on using the
* offset field in the dl_disabmulti_req or squery
* to find either hardware address will similarly fail.
*
* Look in ar_entry_squery() in arp.c to see how this offset
* is used.
*/
if (!mp)
return (ENOMEM);
ip1dbg(("ip_ll_send_enabmulti_req: IPv4 putnext %s on %s\n",
sizeof (group_buf)),
} else {
ip1dbg(("ip_ll_send_enabmulti_req: IPv6 ndp_mcastreq %s on"
" %s\n",
sizeof (group_buf)),
}
return (0);
}
/*
* Send a multicast request to the driver for enabling multicast
* membership for v6group if appropriate.
*/
static int
{
ip1dbg(("ip_ll_addmulti_v6: not resolver\n"));
return (0); /* Must be IRE_IF_NORESOLVER */
}
ip1dbg(("ip_ll_addmulti_v6: MULTI_BCAST\n"));
return (0);
}
/*
* Nobody there. All multicast addresses will be re-joined
* when we get the DL_BIND_ACK bringing the interface up.
*/
ip1dbg(("ip_ll_addmulti_v6: nobody up\n"));
return (0);
}
}
/*
* INADDR_ANY means all multicast addresses.
* INADDR_ANY is stored as the IPv6 unspecified addr.
*/
int
{
return (EINVAL);
/*
* INADDR_ANY is represented as the IPv6 unspecified addr.
*/
if (group == INADDR_ANY)
else
/*
* Look for a match on the ipif.
* (IP_DROP_MEMBERSHIP specifies an ipif using an IP address).
*/
return (ENOENT);
/* Update counters */
if (no_ilg)
ilm->ilm_no_ilg_cnt--;
if (leaving)
ilm->ilm_refcnt--;
if (ilm->ilm_refcnt > 0)
if (group == INADDR_ANY) {
/*
* Check how many ipif's that have members in this group -
* if there are still some left then don't tell the driver
* to drop it.
*/
return (0);
/* If we never joined, then don't leave. */
if (ill->ill_join_allmulti)
return (0);
}
if (!IS_LOOPBACK(ill))
/*
* Check how many ipif's that have members in this group -
* if there are still some left then don't tell the driver
* to drop it.
*/
return (0);
}
/*
* The unspecified address means all multicast addresses.
*/
int
{
if (!IN6_IS_ADDR_MULTICAST(v6group) &&
return (EINVAL);
/*
* Look for a match on the ill.
*/
return (ENOENT);
/* Update counters */
if (no_ilg)
ilm->ilm_no_ilg_cnt--;
if (leaving)
ilm->ilm_refcnt--;
if (ilm->ilm_refcnt > 0)
if (IN6_IS_ADDR_UNSPECIFIED(v6group)) {
/*
* Check how many ipif's that have members in this group -
* if there are still some left then don't tell the driver
* to drop it.
*/
return (0);
/* If we never joined, then don't leave. */
if (ill->ill_join_allmulti)
return (0);
}
if (!IS_LOOPBACK(ill))
/*
* Check how many ipif's that have members in this group -
* if there are still some left then don't tell the driver
* to drop it.
*/
return (0);
}
/*
* Send a multicast request to the driver for disabling multicast reception
* for v6groupp address. The caller has already checked whether it is
* appropriate to send one or not.
*/
int
{
char group_buf[INET6_ADDRSTRLEN];
/*
* See comment in ip_ll_send_enabmulti_req().
*/
return (0);
/*
* Create a AR_ENTRY_SQUERY message with a dl_disabmulti_req tacked
* on.
*/
if (!mp)
return (ENOMEM);
if (IN6_IS_ADDR_V4MAPPED(v6groupp)) {
/*
* NOTE!!!
* The "addroff" passed in here was calculated by
* ill_create_dl(), and will be used by ill_create_squery()
* to perform some twisted coding magic. It is the offset
* into the dl_xxx_req of the hw addr. Here, it will be
* added to b_wptr - b_rptr to create a magic number that
* is not an offset into this mblk.
*
* Please see the comment in ip_ll_send)enabmulti_req()
* for a complete explanation.
*
* Look in ar_entry_squery() in arp.c to see how this offset
* is used.
*/
if (!mp)
return (ENOMEM);
ip1dbg(("ip_ll_send_disabmulti_req: IPv4 putnext %s on %s\n",
sizeof (group_buf)),
} else {
ip1dbg(("ip_ll_send_disabmulti_req: IPv6 ndp_mcastreq %s on"
" %s\n",
sizeof (group_buf)),
}
return (0);
}
/*
* Send a multicast request to the driver for disabling multicast
* membership for v6group if appropriate.
*/
static int
{
return (0); /* Must be IRE_IF_NORESOLVER */
}
ip1dbg(("ip_ll_delmulti_v6: MULTI_BCAST\n"));
return (0);
}
/*
* Nobody there. All multicast addresses will be re-joined
* when we get the DL_BIND_ACK bringing the interface up.
*/
ip1dbg(("ip_ll_delmulti_v6: nobody up\n"));
return (0);
}
}
/*
* 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
{
/*
* Nobody there. All multicast addresses will be re-joined
* when we get the DL_BIND_ACK bringing the interface up.
*/
return (0);
}
/*
* See comment in ip_ll_send_enabmulti_req().
*/
return (0);
/*
* Create a DL_PROMISCON_REQ message and send it directly to the DLPI
* provider. We don't need to do this for certain media types for
* which we never need to turn promiscuous mode on. While we're here,
* pre-allocate a DL_PROMISCOFF_REQ message to make sure that
* ill_leave_allmulti() will not fail due to low memory conditions.
*/
return (ENOMEM);
}
}
return (0);
}
/*
* Make the driver stop passing up all multicast packets
*/
void
{
/*
* Nobody there. All multicast addresses will be re-joined
* when we get the DL_BIND_ACK bringing the interface up.
*/
return;
}
/*
* See comment in ip_ll_send_enabmulti_req().
*/
return;
/*
* Create a DL_PROMISCOFF_REQ message and send it directly to
* the DLPI provider. We don't need to do this for certain
* media types for which we never need to turn promiscuous
* mode on.
*/
}
}
static ill_t *
{
ipst);
if (!ill_waiter_inc(ill)) {
return (NULL);
}
if (!in_ipsq)
}
return (ill);
}
int
{
int ret = 0;
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) {
} else {
}
out:
return (ret);
}
int
{
return (ENODEV);
if (ill->ill_ipallmulti_cnt > 0) {
if (isv6) {
} else {
B_TRUE);
}
}
return (0);
}
/*
* Delete the allmulti memberships that were added as part of
* ip_join_allmulti().
*/
void
{
} else {
B_TRUE);
}
}
}
/*
* Copy mp_orig and pass it in as a local message.
*/
void
{
sizeof (udpha_t);
if (is_system_labeled() &&
}
}
} else {
}
return;
} else {
}
/*
* DTrace this as ip:::send. A blocked packet will fire the send
* probe, but not the receive probe.
*/
}
static area_t ip_aresq_template = {
AR_ENTRY_SQUERY, /* cmd */
sizeof (area_t), /* name len (filled by ill_arp_alloc) */
IP_ARP_PROTO_TYPE, /* protocol, from arps perspective */
sizeof (area_t), /* proto addr offset */
IP_ADDR_LEN, /* proto addr_length */
0, /* proto mask offset */
/* Rest is initialized when used */
0, /* flags */
0, /* hw addr offset */
0, /* hw addr length */
};
static mblk_t *
{
if (!mp) {
return (NULL);
}
/*
* NOTE!
*
* The area_hw_addr_offset, as can be seen, does not hold the
* actual hardware address offset. Rather, it holds the offset
* to the hw addr in the dl_xxx_req in mp_tail, modified by
* adding (mp->b_wptr - mp->b_rptr). This allows the function
* mi_offset_paramc() to find the hardware address in the
* *second* mblk (dl_xxx_req), not this mblk.
*
* Using mi_offset_paramc() is thus the *only* way to access
* the dl_xxx_hw address.
*
* The squery hw address should *not* be accessed.
*
* See ar_entry_squery() in arp.c for an example of how all this works.
*/
return (mp);
}
/*
* Create a DLPI message; for DL_{ENAB,DISAB}MULTI_REQ, room is left for
* the hardware address.
*/
static mblk_t *
{
char *cp;
if (!hw_addr_length) {
ip0dbg(("ip_create_dl: hw addr length = 0\n"));
return (NULL);
}
switch (dl_primitive) {
case DL_ENABMULTI_REQ:
case DL_DISABMULTI_REQ:
size += hw_addr_length;
break;
case DL_PROMISCON_REQ:
case DL_PROMISCOFF_REQ:
break;
default:
return (NULL);
}
if (!mp)
return (NULL);
switch (dl_primitive) {
case DL_ENABMULTI_REQ: {
break;
}
case DL_DISABMULTI_REQ: {
break;
}
case DL_PROMISCON_REQ:
case DL_PROMISCOFF_REQ: {
break;
}
}
ip1dbg(("ill_create_dl: addr_len %d, addr_off %d\n",
return (mp);
}
/*
* Writer processing for ip_wput_ctl(): send the DL_{ENAB,DISAB}MULTI_REQ
* messages that had been delayed until we'd heard back from ARP. One catch:
* we need to ensure that no one else becomes writer on the IPSQ before we've
* received the replies, or they'll incorrectly process our replies as part of
* their unrelated IPSQ operation. To do this, we start a new IPSQ operation,
* which will complete when we process the reply in ip_rput_dlpi_writer().
*/
/* ARGSUSED */
static void
{
if (prim == DL_ENABMULTI_REQ) {
/* Track the state if this is the first enabmulti */
}
}
void
{
/* Check that we have an AR_ENTRY_SQUERY with a tacked on mblk */
return;
}
/* Check that the tacked on mblk is a DL_{DISAB,ENAB}MULTI_REQ */
return;
}
/* See comments above ip_wput_ctl_writer() for details */
}
/*
* Rejoin any groups which have been explicitly joined by the application (we
* left all explicitly joined groups as part of ill_leave_multicast() prior to
* bringing the interface down). Note that because groups can be joined and
* left while an interface is down, this may not be the same set of groups
* that we left in ill_leave_multicast().
*/
void
{
char addrbuf[INET6_ADDRSTRLEN];
/*
* Check how many ipif's that have members in this group -
* if more then one we make sure that this entry is first
* in the list.
*/
continue;
}
(void) ill_join_allmulti(ill);
} else {
else
}
}
}
/*
* The opposite of ill_recover_multicast() -- leaves all multicast groups
* that were explicitly joined.
*/
void
{
char addrbuf[INET6_ADDRSTRLEN];
/*
* Check how many ipif's that have members in this group -
* if more then one we make sure that this entry is first
* in the list.
*/
continue;
}
} else {
else
}
}
}
/* Find an ilm for matching the ill */
ilm_t *
{
/*
* INADDR_ANY is represented as the IPv6 unspecified addr.
*/
if (group == INADDR_ANY)
else
}
/*
* 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 *
{
/*
* 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.
*/
continue;
continue;
break;
}
}
return (ilm);
}
/*
* Find an ilm for the ipif. Only needed for IPv4 which does
* ipif specific socket options.
*/
ilm_t *
{
break;
}
return (ilm);
}
/*
* How many members on this ill?
*/
int
{
int i = 0;
continue;
i++;
}
}
return (i);
}
/* Caller guarantees that the group is not already on the list */
static ilm_t *
{
return (NULL);
return (NULL);
}
}
/*
* IPv4 Multicast groups are joined using ipif.
* IPv6 Multicast groups are joined using ill.
*/
(char *), "ilm", (void *), ilm);
ill->ill_ilm_cnt++;
} else {
(char *), "ilm", (void *), ilm);
ipif->ipif_ilm_cnt++;
}
/*
* Grab lock to give consistent view to readers
*/
/*
* All ilms in the same zone are contiguous in the ill_ilm list.
* The loops in ip_proto_input() and ip_wput_local() use this to avoid
* sending duplicates up when two applications in the same zone join the
* same group on different logical interfaces.
*/
}
/*
* If we have an associated ilg, use its filter state; if not,
* default to (EXCLUDE, NULL) and set no_ilg_cnt to track this.
*/
if (ilgstat != ILGSTAT_NONE) {
if (!SLIST_IS_EMPTY(ilg_flist))
} else {
}
return (ilm);
}
void
{
}
void
{
/*
* check if there are any pending FREE or unplumb
* operations that need to be restarted.
*/
/*
* IPv4 ilms hold a ref on the ipif.
*/
(char *), "ilm", (void *), ilm);
} else {
/*
* IPv6 ilms hold a ref on the ill.
*/
(char *), "ilm", (void *), ilm);
ill->ill_ilm_cnt--;
if (ILL_FREE_OK(ill))
}
} else {
}
}
ill->ill_ilm_cleanup_reqd = 0;
if (need_wakeup)
else
}
/*
* Unlink ilm and free it.
*/
static void
{
} else {
}
/*
* Delete under lock protection so that readers don't stumble
* on bad ilm_next
*/
if (ill->ill_ilm_walker_cnt != 0) {
return;
}
;
/*
* if we are the last reference to the ipif (for IPv4 ilms)
* or the ill (for IPv6 ilms), we may need to wakeup any
* pending FREE or unplumb operations.
*/
(char *), "ilm", (void *), ilm);
} else {
(char *), "ilm", (void *), ilm);
ill->ill_ilm_cnt--;
if (ILL_FREE_OK(ill))
}
if (need_wakeup) {
/* drops ill lock */
} else {
}
}
/* Increment the ILM walker count for `ill' */
static void
{
}
/* Decrement the ILM walker count for `ill' */
static void
{
else
}
/*
* 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 *
{
if (IS_UNDER_IPMP(ill))
else
else
}
/*
* Helper function for ilm_walker_step() that returns the next ILM
* associated with `ilw', regardless of whether it's deleted.
*/
static ilm_t *
{
/*
* 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 (NULL);
}
/*
* Step to the next ILM associated with `ilw'.
*/
ilm_t *
{
break;
}
return (ilm);
}
/*
* Finish the ILM walk associated with `ilw'.
*/
void
{
}
}
/*
* Looks up the appropriate ipif given a v4 multicast group and interface
* address. On success, returns 0, with *ipifpp pointing to the found
* struct. On failure, returns an errno and *ipifpp is NULL.
*/
int
{
int err = 0;
return (EINVAL);
}
if (ifaddr != INADDR_ANY) {
err = EADDRNOTAVAIL;
} else {
return (EADDRNOTAVAIL);
}
return (err);
return (0);
}
/*
* Looks up the appropriate ill (or ipif if v4mapped) given an interface
* index and IPv6 multicast group. On success, returns 0, with *illpp (or
* *ipifpp if v4mapped) pointing to the found struct. On failure, returns
* an errno and *illpp and *ipifpp are undefined.
*/
int
{
int err;
if (IN6_IS_ADDR_V4MAPPED(v6group)) {
return (EINVAL);
if (src_unspec) {
*v4src = INADDR_ANY;
} else {
}
return (EINVAL);
} else {
return (EINVAL);
if (!IN6_IS_ADDR_MULTICAST(v6group) ||
return (EINVAL);
}
}
if (ifindex == 0) {
if (*isv6)
else
return (EADDRNOTAVAIL);
} else {
if (*isv6) {
err = EADDRNOTAVAIL;
}
} else {
}
return (err);
}
return (0);
}
static int
{
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
return (EADDRNOTAVAIL);
}
ASSERT(!isv4mapped);
} else {
}
/*
* In the kernel, we use the state definitions MODE_IS_[IN|EX]CLUDE
* to identify the filter mode; but the API uses MCAST_[IN|EX]CLUDE.
* So we need to translate here.
*/
numsrc = 0;
} else {
for (i = 0; i < outsrcs; i++) {
break;
if (isv4mapped) {
} else {
if (is_v4only_api) {
} else {
sin = (struct sockaddr_in *)
}
}
}
}
if (is_v4only_api) {
} else {
}
return (0);
}
static int
{
int i;
struct sockaddr_storage *sl;
struct sockaddr_in6 *sin6;
return (EADDRNOTAVAIL);
}
/*
* In the kernel, we use the state definitions MODE_IS_[IN|EX]CLUDE
* to identify the filter mode; but the API uses MCAST_[IN|EX]CLUDE.
* So we need to translate here.
*/
} else {
break;
}
}
return (0);
}
static int
{
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
ASSERT(!isv4mapped);
} else {
}
/* Make sure we can handle the source list */
if (insrcs > MAX_FILTER_SIZE)
return (ENOBUFS);
/*
* setting the filter to (INCLUDE, NULL) is treated
* as a request to leave the group.
*/
/*
* if the request was actually to leave, and we
* didn't find an ilg, there's nothing to do.
*/
if (!leave_grp)
}
} else if (leave_grp) {
return (0);
} else {
/* Preserve existing state in case ip_addmulti() fails */
orig_filter = NULL;
} else {
if (orig_filter == NULL) {
return (ENOMEM);
}
}
}
/*
* Alloc buffer to copy new state into (see below) before
* we make any changes, so we can bail if it fails.
*/
goto free_and_exit;
}
if (insrcs == 0) {
} else {
if (ilgstat == ILGSTAT_NEW)
goto free_and_exit;
}
} else {
}
for (i = 0; i < insrcs; i++) {
if (isv4mapped) {
} else {
if (is_v4only_api) {
} else {
sin = (struct sockaddr_in *)
}
}
}
}
/*
* In the kernel, we use the state definitions MODE_IS_[IN|EX]CLUDE
* to identify the filter mode; but the API uses MCAST_[IN|EX]CLUDE.
* So we need to translate here.
*/
/*
* Save copy of ilg's filter state to pass to other functions,
* so we can release conn_lock now.
*/
if (err != 0) {
/*
* Restore the original filter state, or delete the
* newly-created ilg. We need to look up the ilg
* again, though, since we've not been holding the
* conn_lock.
*/
if (ilgstat == ILGSTAT_NEW) {
} else {
if (SLIST_IS_EMPTY(orig_filter)) {
} else {
/*
* We didn't free the filter, even if we
* were trying to make the source list empty;
* so if orig_filter isn't empty, the ilg
* must still have a filter alloc'd.
*/
}
}
}
return (err);
}
static int
{
struct sockaddr_storage *sl;
struct sockaddr_in6 *sin6;
/* Make sure we can handle the source list */
return (ENOBUFS);
/*
* setting the filter to (INCLUDE, NULL) is treated
* as a request to leave the group.
*/
/*
* if the request was actually to leave, and we
* didn't find an ilg, there's nothing to do.
*/
if (!leave_grp)
}
} else if (leave_grp) {
B_TRUE);
return (0);
} else {
/* preserve existing state in case ip_addmulti() fails */
orig_filter = NULL;
} else {
if (orig_filter == NULL) {
return (ENOMEM);
}
}
}
/*
* Alloc buffer to copy new state into (see below) before
* we make any changes, so we can bail if it fails.
*/
goto free_and_exit;
}
} else {
if (ilgstat == ILGSTAT_NEW)
goto free_and_exit;
}
} else {
}
}
}
/*
* In the kernel, we use the state definitions MODE_IS_[IN|EX]CLUDE
* to identify the filter mode; but the API uses MCAST_[IN|EX]CLUDE.
* So we need to translate here.
*/
/*
* Save copy of ilg's filter state to pass to other functions,
* so we can release conn_lock now.
*/
if (err != 0) {
/*
* Restore the original filter state, or delete the
* newly-created ilg. We need to look up the ilg
* again, though, since we've not been holding the
* conn_lock.
*/
if (ilgstat == ILGSTAT_NEW) {
} else {
if (SLIST_IS_EMPTY(orig_filter)) {
} else {
/*
* We didn't free the filter, even if we
* were trying to make the source list empty;
* so if orig_filter isn't empty, the ilg
* must still have a filter alloc'd.
*/
}
}
}
return (err);
}
/*
* Process the SIOC[GS]MSFILTER and SIOC[GS]IPMSFILTER ioctls.
*/
/* ARGSUSED */
int
{
/* existence verified in ip_wput_nondata() */
struct sockaddr_in *gsin;
struct sockaddr_in6 *gsin6;
return (ENOMEM);
}
return (EINVAL);
/*
* now we know we have at least have the initial structure,
* but need to check for the source list array.
*/
if (is_v4only_api) {
} else {
} else {
}
}
return (EINVAL);
/* operation not supported on the virtual network interface */
return (EINVAL);
if (isv6) {
if (getcmd)
else
} else {
if (is_v4only_api) {
} else {
} else {
v4grp);
isv4mapped = B_TRUE;
}
}
if (getcmd)
else
}
return (err);
}
/*
* Finds the ipif based on information in the ioctl headers. Needed to make
* ip_process_ioctl() happy (it needs to know the ipif for IPI_WR-flagged
* ioctls prior to calling the ioctl's handler function).
*/
int
{
int err = 0;
/* caller has verified this mblk exists */
struct ip_msfilter *imsf;
struct group_filter *gf;
/* don't allow multicast operations on a tcp conn */
if (IPCL_IS_TCP(connp))
return (ENOPROTOOPT);
/* don't allow v4-specific ioctls on v6 socket */
if (connp->conn_af_isv6)
return (EAFNOSUPPORT);
if (v4addr == INADDR_ANY) {
err = EADDRNOTAVAIL;
} else {
}
} else {
struct sockaddr_in6 *sin6;
if (IN6_IS_ADDR_V4MAPPED(&v6grp))
else
struct sockaddr_in *sin;
} else {
return (EAFNOSUPPORT);
}
if (index == 0) {
if (isv6) {
ipst);
} else {
}
err = EADDRNOTAVAIL;
} else {
}
}
return (err);
}
/*
* The structures used for the SIOC*MSFILTER ioctls usually must be copied
* in in two stages, as the first copyin tells us the size of the attached
* source buffer. This function is called by ip_wput_nondata() after the
* first copyin has completed; it figures out how big the second stage
* needs to be, and kicks it off.
*
* In some cases (numsrc < 2), the second copyin is not needed as the
* first one gets a complete structure containing 1 source addr.
*
* The function returns 0 if a second copyin has been started (i.e. there's
* no more work to be done right now), or 1 if the second copyin is not
* needed and ip_wput_nondata() can continue its processing.
*/
int
{
/* validity of this checked in ip_wput_nondata() */
int copysize = 0;
int offset;
offset = sizeof (struct group_filter);
}
} else {
offset = sizeof (struct ip_msfilter);
}
}
if (copysize > 0) {
return (0);
}
return (1);
}
/*
* Handle the following optmgmt:
* IP_ADD_MEMBERSHIP must not have joined already
* MCAST_JOIN_GROUP must not have joined already
* IP_BLOCK_SOURCE must have joined already
* MCAST_BLOCK_SOURCE must have joined already
* IP_JOIN_SOURCE_GROUP may have joined already
* MCAST_JOIN_SOURCE_GROUP may have joined already
*
* fmode and src parameters may be used to determine which option is
* being set, as follows (the IP_* and MCAST_* versions of each option
* are functionally equivalent):
* opt fmode src
* IP_ADD_MEMBERSHIP MODE_IS_EXCLUDE INADDR_ANY
* MCAST_JOIN_GROUP MODE_IS_EXCLUDE INADDR_ANY
* IP_BLOCK_SOURCE MODE_IS_EXCLUDE v4 addr
* MCAST_BLOCK_SOURCE MODE_IS_EXCLUDE v4 addr
* IP_JOIN_SOURCE_GROUP MODE_IS_INCLUDE v4 addr
* MCAST_JOIN_SOURCE_GROUP MODE_IS_INCLUDE v4 addr
*
* Changing the filter mode is not allowed; if a matching ilg already
* exists and fmode != ilg->ilg_fmode, EINVAL is returned.
*
* Verifies that there is a source address of appropriate scope for
* the group; if not, EADDRNOTAVAIL is returned.
*
* The interface to be used may be identified by an address or by an
* index. A pointer to the index is passed; if it is NULL, use the
* address, otherwise, use the index.
*/
int
{
int err = 0;
if (err != 0) {
if (err != EINPROGRESS) {
ip1dbg(("ip_opt_add_group: no ipif for group 0x%x, "
}
return (err);
}
/* Operation not supported on a virtual network interface */
return (EINVAL);
}
if (checkonly) {
/*
* do not do operation, just pretend to - new T_CHECK
* semantics. The error return case above if encountered
* considered a good enough "check" here.
*/
return (0);
}
NEW_OP);
/* unspecified source addr => no source filtering */
return (err);
}
/*
* Handle the following optmgmt:
* IPV6_JOIN_GROUP must not have joined already
* MCAST_JOIN_GROUP must not have joined already
* MCAST_BLOCK_SOURCE must have joined already
* MCAST_JOIN_SOURCE_GROUP may have joined already
*
* fmode and src parameters may be used to determine which option is
* being set, as follows (IPV6_JOIN_GROUP and MCAST_JOIN_GROUP options
* are functionally equivalent):
* opt fmode v6src
* IPV6_JOIN_GROUP MODE_IS_EXCLUDE unspecified
* MCAST_JOIN_GROUP MODE_IS_EXCLUDE unspecified
* MCAST_BLOCK_SOURCE MODE_IS_EXCLUDE v6 addr
* MCAST_JOIN_SOURCE_GROUP MODE_IS_INCLUDE v6 addr
*
* Changing the filter mode is not allowed; if a matching ilg already
* exists and fmode != ilg->ilg_fmode, EINVAL is returned.
*
* Verifies that there is a source address of appropriate scope for
* the group; if not, EADDRNOTAVAIL is returned.
*
* Handles IPv4-mapped IPv6 multicast addresses by associating them
* with the link-local ipif. Assumes that if v6group is v4-mapped,
* v6src is also v4-mapped.
*/
int
{
char buf[INET6_ADDRSTRLEN];
int err;
if (err != 0) {
if (err != EINPROGRESS) {
ip1dbg(("ip_opt_add_group_v6: no ill for group %s/"
}
return (err);
}
/* operation is not supported on the virtual network interface */
if (isv6) {
return (EINVAL);
}
} else {
return (EINVAL);
}
}
if (checkonly) {
/*
* do not do operation, just pretend to - new T_CHECK
* semantics. The error return case above if encountered
* considered a good enough "check" here.
*/
if (isv6)
else
return (0);
}
if (!isv6) {
} else {
}
return (err);
}
static int
{
/*
* The ilg is valid only while we hold the conn lock. Once we drop
* the lock, another thread can locate another ilg on this connp,
* but on a different ipif, and delete it, and cause the ilg array
* to be reallocated and copied. Hence do the ilg_delete before
* dropping the lock.
*/
return (EADDRNOTAVAIL);
}
/*
* Decide if we're actually deleting the ilg or just removing a
* source filter address; if just removing an addr, make sure we
* aren't trying to change the filter mode, and that the addr is
* actually in our filter list already. If we're removing the
* last src in an include list, just delete the ilg.
*/
if (src == INADDR_ANY) {
} else {
int err = 0;
err = EADDRNOTAVAIL;
if (err != 0) {
return (err);
}
if (fmode == MODE_IS_INCLUDE &&
}
}
return (0);
}
static int
{
return (EADDRNOTAVAIL);
}
/*
* Decide if we're actually deleting the ilg or just removing a
* source filter address; if just removing an addr, make sure we
* aren't trying to change the filter mode, and that the addr is
* actually in our filter list already. If we're removing the
* last src in an include list, just delete the ilg.
*/
if (!IN6_IS_ADDR_UNSPECIFIED(v6src)) {
int err = 0;
err = EADDRNOTAVAIL;
if (err != 0) {
return (err);
}
if (fmode == MODE_IS_INCLUDE &&
else
}
leaving);
return (0);
}
/*
* Handle the following optmgmt:
* IP_DROP_MEMBERSHIP will leave
* MCAST_LEAVE_GROUP will leave
* IP_UNBLOCK_SOURCE will not leave
* MCAST_UNBLOCK_SOURCE will not leave
* IP_LEAVE_SOURCE_GROUP may leave (if leaving last source)
* MCAST_LEAVE_SOURCE_GROUP may leave (if leaving last source)
*
* fmode and src parameters may be used to determine which option is
* being set, as follows (the IP_* and MCAST_* versions of each option
* are functionally equivalent):
* opt fmode src
* IP_DROP_MEMBERSHIP MODE_IS_INCLUDE INADDR_ANY
* MCAST_LEAVE_GROUP MODE_IS_INCLUDE INADDR_ANY
* IP_UNBLOCK_SOURCE MODE_IS_EXCLUDE v4 addr
* MCAST_UNBLOCK_SOURCE MODE_IS_EXCLUDE v4 addr
* IP_LEAVE_SOURCE_GROUP MODE_IS_INCLUDE v4 addr
* MCAST_LEAVE_SOURCE_GROUP MODE_IS_INCLUDE v4 addr
*
* Changing the filter mode is not allowed; if a matching ilg already
* exists and fmode != ilg->ilg_fmode, EINVAL is returned.
*
* The interface to be used may be identified by an address or by an
* index. A pointer to the index is passed; if it is NULL, use the
* address, otherwise, use the index.
*/
int
{
int err;
if (err != 0) {
if (err != EINPROGRESS) {
ip1dbg(("ip_opt_delete_group: no ipif for group "
"0x%x, ifaddr 0x%x\n",
}
return (err);
}
/* Operation not supported on a virtual network interface */
return (EINVAL);
}
if (checkonly) {
/*
* do not do operation, just pretend to - new T_CHECK
* semantics. The error return case above if encountered
* considered a good enough "check" here.
*/
return (0);
}
NEW_OP);
return (err);
}
/*
* Handle the following optmgmt:
* IPV6_LEAVE_GROUP will leave
* MCAST_LEAVE_GROUP will leave
* MCAST_UNBLOCK_SOURCE will not leave
* MCAST_LEAVE_SOURCE_GROUP may leave (if leaving last source)
*
* fmode and src parameters may be used to determine which option is
* being set, as follows (IPV6_LEAVE_GROUP and MCAST_LEAVE_GROUP options
* are functionally equivalent):
* opt fmode v6src
* IPV6_LEAVE_GROUP MODE_IS_INCLUDE unspecified
* MCAST_LEAVE_GROUP MODE_IS_INCLUDE unspecified
* MCAST_UNBLOCK_SOURCE MODE_IS_EXCLUDE v6 addr
* MCAST_LEAVE_SOURCE_GROUP MODE_IS_INCLUDE v6 addr
*
* Changing the filter mode is not allowed; if a matching ilg already
* exists and fmode != ilg->ilg_fmode, EINVAL is returned.
*
* Handles IPv4-mapped IPv6 multicast addresses by associating them
* with the link-local ipif. Assumes that if v6group is v4-mapped,
* v6src is also v4-mapped.
*/
int
{
char buf[INET6_ADDRSTRLEN];
int err;
if (err != 0) {
if (err != EINPROGRESS) {
ip1dbg(("ip_opt_delete_group_v6: no ill for group %s/"
}
return (err);
}
/* operation is not supported on the virtual network interface */
if (isv6) {
return (EINVAL);
}
} else {
return (EINVAL);
}
}
if (checkonly) {
/*
* do not do operation, just pretend to - new T_CHECK
* semantics. The error return case above if encountered
* considered a good enough "check" here.
*/
if (isv6)
else
return (0);
}
if (!isv6) {
v4src);
} else {
v6src);
}
return (err);
}
/*
* Group mgmt for upper conn that passes things down
* to the interface multicast list (and DLPI)
* These routines can handle new style options that specify an interface name
* as opposed to an interface address (needed for general handling of
* unnumbered interfaces.)
*/
/*
* Add a group to an upper conn group data structure and pass things down
* to the interface multicast list (and DLPI)
*/
static int
{
int error = 0;
int new_fmode;
return (EADDRNOTAVAIL);
/*
* conn_ilg[] is protected by conn_lock. Need to hold the conn_lock
* to walk the conn_ilg[] list in ilg_lookup_ipif(); also needed to
* serialize 2 threads doing join (sock, group1, hme0:0) and
* (sock, group2, hme1:0) where hme0 and hme1 map to different ipsqs,
* but both operations happen on the same conn.
*/
/*
* Depending on the option we're handling, may or may not be okay
* if group has already been added. Figure out our rules based
* on fmode and src params. Also make sure there's enough room
* in the filter if we're adding a source to an existing filter.
*/
if (src == INADDR_ANY) {
/* we're joining for all sources, must not have joined */
error = EADDRINUSE;
} else {
if (fmode == MODE_IS_EXCLUDE) {
/* (excl {addr}) => block source, must have joined */
}
/* (incl {addr}) => join source, may have joined */
}
if (error != 0) {
return (error);
}
/*
* Alloc buffer to copy new state into (see below) before
* we make any changes, so we can bail if it fails.
*/
return (ENOMEM);
}
return (error);
}
if (src != INADDR_ANY) {
return (ENOMEM);
}
}
if (group == INADDR_ANY) {
} else {
}
} else {
int index;
return (EINVAL);
}
return (ENOMEM);
}
}
return (EADDRNOTAVAIL);
}
}
/*
* Save copy of ilg's filter state to pass to other functions,
* so we can release conn_lock now.
*/
if (error != 0) {
/*
* Need to undo what we did before calling ip_addmulti()!
* Must look up the ilg again since we've not been holding
* conn_lock.
*/
if (ilgstat == ILGSTAT_NEW)
else
return (error);
}
return (0);
}
static int
{
int error = 0;
int new_fmode;
return (EADDRNOTAVAIL);
/*
* conn_lock protects the ilg list. Serializes 2 threads doing
* join (sock, group1, hme0) and (sock, group2, hme1) where hme0
* and hme1 map to different ipsq's, but both operations happen
* on the same conn.
*/
/*
* Depending on the option we're handling, may or may not be okay
* if group has already been added. Figure out our rules based
* on fmode and src params. Also make sure there's enough room
* in the filter if we're adding a source to an existing filter.
*/
if (IN6_IS_ADDR_UNSPECIFIED(v6src)) {
/* we're joining for all sources, must not have joined */
error = EADDRINUSE;
} else {
if (fmode == MODE_IS_EXCLUDE) {
/* (excl {addr}) => block source, must have joined */
}
/* (incl {addr}) => join source, may have joined */
}
if (error != 0) {
return (error);
}
/*
* Alloc buffer to copy new state into (see below) before
* we make any changes, so we can bail if it fails.
*/
return (ENOMEM);
}
return (error);
}
if (!IN6_IS_ADDR_UNSPECIFIED(v6src)) {
return (ENOMEM);
}
}
} else {
int index;
return (EINVAL);
}
return (ENOMEM);
}
}
return (EADDRNOTAVAIL);
}
}
/*
* Save copy of ilg's filter state to pass to other functions,
* so we can release conn_lock now.
*/
/*
* Now update the ill. We wait to do this until after the ilg
* has been updated because we need to update the src filter
* info for the ill, which involves looking at the status of
*/
if (error != 0) {
/*
* But because we waited, we have to undo the ilg update
* if ip_addmulti_v6() fails. We also must lookup ilg
* again, since we've not been holding conn_lock.
*/
return (error);
}
return (0);
}
/*
* Find an IPv4 ilg matching group, ill and source
*/
ilg_t *
{
int i;
/*
* INADDR_ANY is represented as the IPv6 unspecified addr.
*/
if (group == INADDR_ANY)
else
for (i = 0; i < connp->conn_ilg_inuse; i++) {
continue;
/* no source filter, so this is a match */
return (ilg);
}
break;
}
}
if (i == connp->conn_ilg_inuse)
return (NULL);
/*
* we have an ilg with matching ill and group; but
* the ilg has a source list that we must check.
*/
break;
}
}
return (ilg);
return (NULL);
}
/*
* Find an IPv6 ilg matching group, ill, and source
*/
ilg_t *
{
int i;
for (i = 0; i < connp->conn_ilg_inuse; i++) {
continue;
/* no source filter, so this is a match */
return (ilg);
}
break;
}
}
if (i == connp->conn_ilg_inuse)
return (NULL);
/*
* we have an ilg with matching ill and group; but
* the ilg has a source list that we must check.
*/
break;
}
}
return (ilg);
return (NULL);
}
/*
* Find an IPv6 ilg matching group and ill
*/
ilg_t *
{
int i;
for (i = 0; i < connp->conn_ilg_inuse; i++) {
continue;
return (ilg);
}
return (NULL);
}
/*
* Find an IPv4 ilg matching group and ipif
*/
static ilg_t *
{
int i;
if (group == INADDR_ANY)
else
for (i = 0; i < connp->conn_ilg_inuse; i++) {
return (ilg);
}
return (NULL);
}
/*
* If a source address is passed in (src != NULL and src is not
* unspecified), remove the specified src addr from the given ilg's
* filter list, else delete the ilg.
*/
static void
{
int i;
if (connp->conn_ilg_walker_cnt != 0) {
return;
}
/* Move other entries up one step */
connp->conn_ilg_inuse--;
for (; i < connp->conn_ilg_inuse; i++)
if (connp->conn_ilg_inuse == 0) {
}
} else {
}
}
/*
* Called from conn close. No new ilg can be added or removed.
* because CONN_CLOSING has been set by ip_close. ilg_add / ilg_delete
* will return error if conn has started closing.
*/
void
{
int 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.
*/
continue;
} else {
}
/*
* is changing. But we need to make sure that the ill will
* not vanish. So we just bump up the ill_waiter count.
* If we are unable to do even that, then the ill is closing,
* in which case the unplumb thread will handle the cleanup,
* and we move on to the next ilg.
*/
if (!ill_waiter_inc(ill))
continue;
/*
* To prevent deadlock between ill close which waits inside
* the perimeter, and conn close, ipsq_enter returns error,
* the moment ILL_CONDEMNED is set, in which case ill close
* takes responsibility to cleanup the ilgs. Note that we
* have not yet set condemned flag, otherwise the conn can't
* be refheld for cleanup by those routines and it would be
* a mutual deadlock.
*/
if (!success)
continue;
/*
* Move on if the ilg was deleted while conn_lock was dropped.
*/
continue;
}
} else {
}
}
/* If any ill was skipped above wait and retry */
if (connp->conn_ilg_inuse != 0) {
goto retry;
}
}
/*
* Called from ill close by ipcl_walk for clearing conn_ilg and
* conn_multicast_ipif for a given ipif. conn is held by caller.
* Note that ipcl_walk only walks conns that are not yet condemned.
* condemned conns can't be refheld. For this reason, conn must become clean
* condemned flag.
*/
static void
{
int i;
char group_buf1[INET6_ADDRSTRLEN];
char group_buf2[INET6_ADDRSTRLEN];
/*
* Even though conn_ilg_inuse can change while we are in this loop,
* i.e.ilgs can be created or deleted on this connp, no new ilgs can
* be created or deleted for this connp, on this ill, since this ill
* is the perimeter. So we won't miss any ilg in this cleanup.
*/
/*
* Increment the walker count, so that ilg repacking does not
* occur while we are in the loop.
*/
continue;
/*
* ip_close cannot be cleaning this ilg at the same time.
* since it also has to execute in this ill's perimeter which
* we are now holding. Only a clean conn can be condemned.
*/
/* Blow away the membership */
ip1dbg(("conn_delete_ilg_ipif: %s on %s (%s)\n",
group_buf1, sizeof (group_buf1)),
group_buf2, sizeof (group_buf2)),
/* ilg_ipif is NULL for V6, so we won't be here */
}
/*
* If we are the last walker, need to physically delete the
* ilgs and repack.
*/
/* Revert to late binding */
}
}
/*
* Called from ill close by ipcl_walk for clearing conn_ilg and
* conn_multicast_ill for a given ill. conn is held by caller.
* Note that ipcl_walk only walks conns that are not yet condemned.
* condemned conns can't be refheld. For this reason, conn must become clean
* condemned flag.
*/
static void
{
int i;
char group_buf[INET6_ADDRSTRLEN];
/*
* Even though conn_ilg_inuse can change while we are in this loop,
* ill, since this ill is the perimeter. So we won't miss any ilg
* in this cleanup.
*/
/*
* Increment the walker count, so that ilg repacking does not
* occur while we are in the loop.
*/
/*
* ip_close cannot be cleaning this ilg at the same
* time, since it also has to execute in this ill's
* perimeter which we are now holding. Only a clean
* conn can be condemned.
*/
/* Blow away the membership */
ip1dbg(("conn_delete_ilg_ill: %s on %s\n",
}
}
/*
* If we are the last walker, need to physically delete the
* ilgs and repack.
*/
/* Revert to late binding */
}
}
/*
* Called when an ipif is unplumbed to make sure that there are no
* dangling conn references to that ipif.
* Handles ilg_ipif and conn_multicast_ipif
*/
void
{
}
/*
* Called when an ill is unplumbed to make sure that there are no
* dangling conn references to that ill.
* Handles ilg_ill, conn_multicast_ill.
*/
void
{
}
#ifdef DEBUG
/*
* Walk functions walk all the interfaces in the system to make
* sure that there is no refernece to the ipif or ill that is
* going away.
*/
int
{
int cnt = 0;
cnt++;
}
}
}
return (cnt);
}
/*
* This function is called before the ipif is freed.
*/
int
{
int cnt = 0;
cnt++;
}
}
}
return (cnt);
}
#endif