/*
* 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
*
*/
#include <inet/ip_multi.h>
#include <net/if_types.h>
/*
* Convenience macros for getting the ip_stack_t associated with an
* ipmp_illgrp_t or ipmp_grp_t.
*/
/*
* Assorted constants that aren't important enough to be tunable.
*/
/*
* IPMP meta-interface kstats (based on those in PSARC/1997/198).
*/
{ "obytes", KSTAT_DATA_UINT32 },
{ "obytes64", KSTAT_DATA_UINT64 },
{ "rbytes", KSTAT_DATA_UINT32 },
{ "rbytes64", KSTAT_DATA_UINT64 },
{ "opackets", KSTAT_DATA_UINT32 },
{ "opackets64", KSTAT_DATA_UINT64 },
{ "oerrors", KSTAT_DATA_UINT32 },
{ "ipackets", KSTAT_DATA_UINT32 },
{ "ipackets64", KSTAT_DATA_UINT64 },
{ "ierrors", KSTAT_DATA_UINT32 },
{ "multircv", KSTAT_DATA_UINT32 },
{ "multixmt", KSTAT_DATA_UINT32 },
{ "brdcstrcv", KSTAT_DATA_UINT32 },
{ "brdcstxmt", KSTAT_DATA_UINT32 },
{ "link_up", KSTAT_DATA_UINT32 }
};
static int ipmp_grp_create_kstats(ipmp_grp_t *);
static int ipmp_grp_update_kstats(kstat_t *, int);
static void ipmp_grp_destroy_kstats(ipmp_grp_t *);
static void ipmp_ill_deactivate(ill_t *);
static void ipmp_ill_ire_mark_testhidden(ire_t *, char *);
static void ipmp_ill_ire_clear_testhidden(ire_t *, char *);
static void ipmp_ill_refresh_active_timer_start(ill_t *);
static void ipmp_ill_rtsaddrmsg(ill_t *, int);
/*
* Initialize IPMP state for IP stack `ipst'; called from ip_stack_init().
*/
void
{
}
/*
* Destroy IPMP state for IP stack `ipst'; called from ip_stack_fini().
*/
void
{
}
/*
* Create an IPMP group named `grname', associate it with IPMP phyint `phyi',
* and add it to the hash. On success, return a pointer to the created group.
* Caller must ensure `grname' is not yet in the hash. Assumes that the IPMP
* meta-interface associated with the group also has the same name (but they
* may differ later via ipmp_grp_rename()).
*/
{
return (NULL);
/*
* Cache the group's phyint. This is safe since a phyint_t will
* outlive its ipmp_grp_t.
*/
/*
* Create IPMP group kstats.
*/
if (ipmp_grp_create_kstats(grp) != 0) {
return (NULL);
}
/*
* Insert the group into the hash.
*/
return (NULL);
}
return (grp);
}
/*
* Create IPMP kstat structures for `grp'. Return an errno upon failure.
*/
static int
{
return (ENOMEM);
return (0);
}
/*
* Update the IPMP kstats tracked by `ksp'; called by the kstats framework.
*/
static int
{
uint_t i;
if (rw == KSTAT_WRITE)
return (EACCES);
/*
* Start with the group's baseline values.
*/
for (i = 0; i < IPMP_KSTAT_MAX; i++) {
} else {
}
}
/*
* Add in the stats of each phyint currently in the group. Since we
* don't directly track the phyints in a group, we cheat by walking
* the IPSQ set under ill_g_lock. (The IPSQ list cannot change while
* ill_g_lock is held.)
*/
/*
* If a phyint in a group is being unplumbed, it's possible
* that ill_glist_delete() -> phyint_free() already freed the
* phyint (and set ipsq_phyint to NULL), but the unplumb
* operation has yet to complete (and thus ipsq_dq() has yet
* to remove the phyint's IPSQ from the group IPSQ's phyint
* list). We skip those phyints here (note that their kstats
* have already been added to gr_kstats0[]).
*/
continue;
for (i = 0; i < IPMP_KSTAT_MAX; i++) {
else
}
}
return (0);
}
/*
* Destroy IPMP kstat structures for `grp'.
*/
static void
{
}
/*
* Look up an IPMP group named `grname' on IP stack `ipst'. Return NULL if it
* does not exist.
*/
{
(mod_hash_val_t *)&grp) == 0)
return (grp);
return (NULL);
}
/*
* Place information about group `grp' into `lifgr'.
*/
void
{
}
}
/*
* Insert `grp' into the hash using the reserved hash entry `mh'.
* Caller must ensure `grp' is not yet in the hash.
*/
static void
{
int err;
/*
* Since grp->gr_name will exist at least as long as `grp' is in the
* hash, we use it directly as the key.
*/
if (err != 0) {
/*
* This should never happen since `mh' was preallocated.
*/
panic("cannot insert IPMP group \"%s\" (err %d)",
}
}
/*
* Remove `grp' from the hash. Caller must ensure `grp' is in it.
*/
static void
{
int err;
panic("cannot remove IPMP group \"%s\" (err %d)",
}
}
/*
* Attempt to rename `grp' to new name `grname'. Return an errno if the new
* group name already exists or is invalid, or if there isn't enough memory.
*/
int
{
if (grname[0] == '\0')
return (EINVAL);
return (EEXIST);
/*
* Before we remove the group from the hash, ensure we'll be able to
* re-insert it by reserving space.
*/
return (ENOMEM);
return (0);
}
/*
* Destroy `grp' and remove it from the hash. Caller must ensure `grp' is in
* the hash, and that there are no interfaces on it.
*/
void
{
/*
* If there are still interfaces using this group, panic before things
* go really off the rails.
*/
}
/*
* Check whether `ill' is suitable for inclusion into `grp', and return an
* errno describing the problem (if any). NOTE: many of these errno values
* are interpreted by ifconfig, which will take corrective action and retry
* the SIOCSLIFGROUPNAME, so please exercise care when changing them.
*/
static int
{
/*
* To sidestep complicated address migration logic in the kernel and
* to force the kernel's all-hosts multicast memberships to be blown
* away, all addresses that had been brought up must be brought back
* down prior to adding an interface to a group. (This includes
* addresses currently down due to DAD.) Once the interface has been
* added to the group, its addresses can then be brought back up, at
* which point they will be moved to the IPMP meta-interface.
* NOTE: we do this before ill_appaddr_cnt() since bringing down the
* link-local causes in.ndpd to remove its ADDRCONF'd addresses.
*/
return (EADDRINUSE);
/*
* To avoid confusing applications by changing addresses that are
* under their control, all such control must be removed prior to
* adding an interface into a group.
*/
if (ill_appaddr_cnt(ill) != 0)
return (EADDRNOTAVAIL);
/*
* Since PTP addresses do not share the same broadcast domain, they
* are not allowed to be in an IPMP group.
*/
if (ill_ptpaddr_cnt(ill) != 0)
return (EINVAL);
/*
* An ill must support multicast to be allowed into a group.
*/
return (ENOTSUP);
/*
* resolution for it to be allowed into a group.
*/
return (ENOTSUP);
/*
* An ill cannot also be using usesrc groups. (Although usesrc uses
* ill_g_usesrc_lock, we don't need to grab it since usesrc also does
* all its modifications as writer.)
*/
return (ENOTSUP);
/*
* All ills in a group must be the same mactype.
*/
return (EINVAL);
return (0);
}
/*
* Check whether `phyi' is suitable for inclusion into `grp', and return an
* errno describing the problem (if any). See comment above ipmp_grp_vet_ill()
* regarding errno values.
*/
int
{
int err = 0;
/*
* An interface cannot have address families plumbed that are not
* configured in the group.
*/
return (EAFNOSUPPORT);
return (err);
}
/*
* Create a new illgrp on IPMP meta-interface `ill'.
*/
{
return (NULL);
return (illg);
}
/*
* Destroy illgrp `illg', and disconnect it from its IPMP meta-interface.
*/
void
{
/*
* Verify `illg' is empty.
*/
/*
* Destroy `illg'.
*/
}
/*
* Add `ipif' to the pool of usable data addresses on `illg' and attempt to
* bind it to an underlying ill, while keeping an even address distribution.
* If the bind is successful, return a pointer to the bound ill.
*/
ill_t *
{
/*
* IPMP data address mappings are internally managed by IP itself, so
* delete any existing ARP entries associated with the address.
*/
}
}
/*
* Delete `ipif' from the pool of usable data addresses on `illg'. If it's
* bound, unbind it from the underlying ill while keeping an even address
* distribution.
*/
void
{
}
}
}
/*
* Return the active ill with the greatest number of data addresses in `illg'.
*/
static ill_t *
{
}
}
return (bestill);
}
/*
* Return the active ill with the fewest number of data addresses in `illg'.
*/
static ill_t *
{
if (ill->ill_bound_cnt == 0)
return (ill); /* can't get better */
}
}
return (bestill);
}
/*
* Return a pointer to IPMP meta-interface for `illg' (which must exist).
* Since ig_ipmp_ill never changes for a given illg, no locks are needed.
*/
ill_t *
{
return (illg->ig_ipmp_ill);
}
/*
* Return a pointer to the next available underlying ill in `illg', or NULL if
* one doesn't exist. Caller must be inside the IPSQ.
*/
ill_t *
{
}
return (ill);
}
/*
* Return a held pointer to the next available underlying ill in `illg', or
* NULL if one doesn't exist. Caller need not be inside the IPSQ.
*/
ill_t *
{
uint_t i;
if (ill_check_and_refhold(ill)) {
return (ill);
}
}
return (NULL);
}
/*
* Return a held pointer to the nominated multicast ill in `illg', or NULL if
* one doesn't exist. Caller need not be inside the IPSQ.
*/
ill_t *
{
return (castill);
}
return (NULL);
}
/*
* Set the nominated cast ill on `illg' to `castill'. If `castill' is NULL,
* any existing nomination is removed. Caller must be inside the IPSQ.
*/
static void
{
/*
* Disable old nominated ill (if any).
*/
/*
* If the IPMP meta-interface is down, we never did the join,
* so we must not try to leave.
*/
/*
* Delete any NCEs tied to the old nomination. We must do this
* last since ill_leave_multicast() may trigger IREs to be
* built using ig_cast_ill.
*/
}
/*
* Set new nomination.
*/
/*
* Enable new nominated ill (if any).
*/
/*
* If the IPMP meta-interface is down, the attempt to recover
* will silently fail but ill_need_recover_multicast will be
* erroneously cleared -- so check first.
*/
}
}
/*
* Create an IPMP ARP entry and add it to the set tracked on `illg'. If an
* entry for the same IP address already exists, destroy it first. Return the
* created IPMP ARP entry, or NULL on failure.
*/
{
KM_NOSLEEP)) == NULL)
return (NULL);
/*
* Delete any existing ARP entry for this address.
*/
/*
* Prepend the new entry.
*/
return (entp);
}
/*
* Remove IPMP ARP entry `entp' from the set tracked on `illg' and destroy it.
*/
void
{
}
/*
* Mark that ARP has been notified about the IP address on `entp'; `illg' is
* taken as a debugging aid for DTrace FBT probes.
*/
/* ARGSUSED */
void
{
}
/*
* Look up the IPMP ARP entry for IP address `addrp' on `illg'; if `addrp' is
* NULL, any IPMP ARP entry is requested. Return NULL if it does not exist.
*/
{
return (entp);
break;
return (entp);
}
/*
* Refresh ARP entries on `illg' to be distributed across its active
* interfaces. Entries that cannot be refreshed (e.g., because there are no
* active interfaces) are marked so that subsequent calls can try again.
*/
void
{
continue;
}
/*
* If this is a proxy ARP entry, we can skip notifying ARP if
* the entry is already up-to-date. If it has changed, we
* update the entry's hardware address before notifying ARP.
*/
if (entp->ia_proxyarp) {
continue;
}
&nce);
continue;
}
}
}
/*
* Return an interface in `illg' with the specified `physaddr', or NULL if one
* doesn't exist. Caller must hold ill_g_lock if it's not inside the IPSQ.
*/
ill_t *
{
return (ill);
}
return (NULL);
}
/*
* Asynchronously update the MTU for an IPMP ill by injecting a DL_NOTIFY_IND.
* Caller must be inside the IPSQ unless this is initialization.
*/
static void
{
/*
* If allocation fails, we have bigger problems than MTU.
*/
}
}
/*
* Recalculate the IPMP group MTU for `illg', and update its associated IPMP
* ill MTU if necessary.
*/
void
{
/*
* Since ill_mtu can only change under ill_lock, we hold ill_lock
* for each ill as we iterate through the list. Any changes to the
* ill_mtu will also trigger an update, so even if we missed it
* this time around, the update will catch it.
*/
}
/*
* MTU must be at least the minimum MTU.
*/
}
/*
* Link illgrp `illg' to IPMP group `grp'. To simplify the caller, silently
* allow the same link to be established more than once.
*/
void
{
} else {
}
}
/*
* Unlink illgrp `illg' from its IPMP group. Return an errno if the illgrp
* cannot be unlinked (e.g., because there are still interfaces using it).
*/
int
{
return (EBUSY);
} else {
return (EBUSY);
}
return (0);
}
/*
* Place `ill' into `illg', and rebalance the data addresses on `illg'
* to be spread evenly across the ills now in it. Also, adjust the IPMP
* ill as necessary to account for `ill' (e.g., MTU).
*/
void
{
/* IS_UNDER_IPMP() requires ill_grp to be non-NULL */
/*
* Account for `ill' joining the illgrp.
*/
else
/*
* Ensure the ILLF_ROUTER flag remains consistent across the group.
*/
else
/*
* Blow away all multicast memberships that currently exist on `ill'.
* This may seem odd, but it's consistent with the application view
* that `ill' no longer exists (e.g., due to ipmp_ill_rtsaddrmsg()).
* The ill_grp_pending bit prevents multicast group joins after
* update_conn_ill() and before ill_grp assignment.
*/
} else {
}
}
/*
* Borrow the first ill's ill_phys_addr_length value for the illgrp's
* physical address length. All other ills must have the same value,
* since they are required to all be the same mactype. Also update
* the IPMP ill's MTU and CoS marking, if necessary.
*/
/*
* NOTE: we leave ill_phys_addr NULL since the IPMP group
* doesn't have a physical address. This means that code must
* not assume that ill_phys_addr is non-NULL just because
* ill_phys_addr_length is non-zero. Likewise for ill_nd_lla.
*/
}
} else {
}
ill->ill_mc_mtu);
}
}
ill->ill_grp_pending = 0;
/*
* Hide the IREs on `ill' so that we don't accidentally find them when
* sending data traffic.
*/
}
/*
* Remove `ill' from its illgrp, and rebalance the data addresses in that
* illgrp to be spread evenly across the remaining ills. Also, adjust the
* IPMP ill as necessary now that `ill' is removed (e.g., MTU).
*/
void
{
/*
* Cancel IPMP-specific ill timeouts.
*/
/*
* Expose any previously-hidden IREs on `ill'.
*/
/*
* Ensure the multicast state for each ipif on `ill' is down so that
* our ipif_multicast_up() (once `ill' leaves the group) will rejoin
* all eligible groups.
*/
/*
* Account for `ill' leaving the illgrp.
*/
else
/*
* Pull `ill' out of the interface lists.
*/
/*
* Re-establish multicast memberships that were previously being
* handled by the IPMP meta-interface.
*/
/*
* Refresh the group MTU based on the new interface list.
*/
/*
* No ills left in the illgrp; we no longer have a physical
* address length, nor can we support ARP, CoS, or anything
* else that depends on knowing the link layer type.
*/
ipmp_ill->ill_phys_addr_length = 0;
ipmp_ill->ill_nd_lla_len = 0;
} else {
/*
* If `ill' didn't support CoS, see if it can now be enabled.
*/
do {
break;
}
}
}
}
/*
* Check if `ill' should be active, and activate or deactivate if need be.
* Return B_FALSE if a refresh was necessary but could not be performed.
*/
static boolean_t
{
if (ipmp_ill_is_active(ill)) {
} else {
}
return (refreshed);
}
/*
* Check if `ill' should be active, and activate or deactivate if need be.
* If the refresh fails, schedule a timer to try again later.
*/
void
{
if (!ipmp_ill_try_refresh_active(ill))
}
/*
* Retry ipmp_ill_try_refresh_active() on the ill named by `ill_arg'.
*/
static void
{
/*
* Clear ill_refresh_tid to indicate that no timeout is pending
* (another thread could schedule a new timeout while we're still
* running, but that's harmless). If the ill is going away, bail.
*/
ill->ill_refresh_tid = 0;
return;
}
}
/*
* If the refresh failed, schedule another attempt.
*/
if (!refreshed)
}
/*
* Retry an ipmp_ill_try_refresh_active() on the ill named by `arg'.
*/
static void
{
/*
* If the ill is going away or a refresh is already scheduled, bail.
*/
if (ill->ill_refresh_tid != 0 ||
return;
}
}
/*
* Activate `ill' so it will be used to send and receive data traffic. Return
* B_FALSE if `ill' cannot be activated. Note that we allocate any messages
* needed to deactivate `ill' here as well so that deactivation cannot fail.
*/
static boolean_t
{
/*
* If this will be the first active interface in the group, allocate
* the link-up and link-down messages.
*/
goto fail;
}
/*
* Now that we have an active ill, nominate it for multicast
* and broadcast duties. Do this before ipmp_ill_bind_ipif()
* since that may need to send multicast packets (e.g., IPv6
* neighbor discovery probes).
*/
/*
* This is the first active ill in the illgrp -- add 'em all.
* writer on its IPSQ as well.
*/
if (ipmp_ipif_is_up_dataaddr(ipif))
} else {
/*
* Redistribute the addresses by moving them from the ill with
* the most addresses until the ill being activated is at the
* same level as the rest of the ills.
*/
for (;;) {
break;
}
}
/*
* Put the interface in the active list.
*/
/*
*/
/*
* Finally, mark the group link up, if necessary.
*/
}
return (B_TRUE);
fail:
return (B_FALSE);
}
/*
* Deactivate `ill' so it will not be used to send or receive data traffic.
*/
static void
{
/*
* Pull the interface out of the active list.
*/
/*
* If the ill that's being deactivated had been nominated for
*/
/*
* Delete all nce_t entries using this ill, so that the next attempt
* to send data traffic will revalidate cached nce's.
*/
/*
* Unbind all of the ipifs bound to this ill, and save 'em in a list;
* we'll rebind them after we tell the resolver the ill is no longer
* active. We must do things in this order or the resolver could
* accidentally rebind to the ill we're trying to remove if multiple
* ills in the group have the same hardware address (which is
* unsupported, but shouldn't lead to a wedged machine).
*/
ubheadipif = ipif;
}
/*
*/
}
/*
* Rebind each ipif from the deactivated ill to the active ill with
* the fewest ipifs. If there are no active ills, the ipifs will
* remain unbound.
*/
}
/*
* Remove any IRE_IF_CLONEs for this ill since they might have an
* ire_nce_cache/nce_common which refers to another ill in the group.
*/
ill);
/*
* Finally, if there are no longer any active interfaces, then delete
* any NCECs associated with the group and mark the group link down.
*/
}
}
/*
* Send the routing socket messages needed to make `ill' "appear" (RTM_ADD)
* or "disappear" (RTM_DELETE) to non-IPMP-aware routing socket listeners.
*/
static void
{
/*
* If `ill' is truly down, there are no messages to generate since:
*
* 1. If cmd == RTM_DELETE, then we're supposed to hide the interface
* and its addresses by bringing them down. But that's already
* true, so there's nothing to hide.
*
* 2. If cmd == RTM_ADD, then we're supposed to generate messages
* indicating that any previously-hidden up addresses are again
* back up (along with the interface). But they aren't, so
* there's nothing to expose.
*/
if (ill->ill_ipif_up_count == 0)
return;
if (cmd == RTM_DELETE)
}
/*
* Bind the address named by `ipif' to the underlying ill named by `ill'.
* If `act' is Res_act_none, don't notify the resolver. Otherwise, `act'
* will indicate to the resolver whether this is an initial bringup of
* `ipif', or just a rebind to another ill.
*/
static void
{
int err = 0;
ill->ill_bound_cnt++;
/*
* ipif_resolver_up() cannot fail for IPv6 ills.
*/
if (act != Res_act_none) {
} else {
}
/*
* Since ipif_ndp_up() never returns EINPROGRESS and
* ipif_resolver_up() only returns EINPROGRESS when the
* associated ill is not up, we should never be here with
* EINPROGRESS. We rely on this to simplify the design.
*/
}
/* TODO: retry binding on failure? when? */
}
/*
* Unbind the address named by `ipif' from the underlying ill named by `ill'.
* If `ipif' is NULL, then an arbitrary ipif on `ill' is unbound and returned.
* If no ipifs are bound to `ill', NULL is returned. If `notifyres' is
* B_TRUE, notify the resolver about the change.
*/
static ipif_t *
{
/*
* If necessary, find an ipif to unbind.
*/
return (NULL);
}
}
/*
* Unbind it.
*/
ill->ill_bound_cnt--;
} else {
}
/*
* If requested, notify the resolvers (provided we're bound).
*/
else
(void) ipif_arp_down(ipif);
}
return (ipif);
}
/*
* Check if `ill' is active. Caller must hold ill_lock and phyint_lock if
* it's not inside the IPSQ. Since ipmp_ill_try_refresh_active() calls this
* to determine whether an ill should be considered active, other consumers
* may race and learn about an ill that should be deactivated/activated before
* IPMP has performed the activation/deactivation. This should be safe though
* since at worst e.g. ire_atomic_start() will prematurely delete an IRE that
* would've been cleaned up by ipmp_ill_deactivate().
*/
{
/*
* Note that PHYI_RUNNING isn't checked since we rely on in.mpathd to
* set PHYI_FAILED whenever PHYI_RUNNING is cleared. This allows the
* link flapping logic to be just in in.mpathd and allows us to ignore
* changes to PHYI_RUNNING.
*/
return (!(ill->ill_ipif_up_count == 0 ||
}
/*
* IRE walker callback: set ire_testhidden on IRE_HIDDEN_TYPE IREs associated
* with `ill_arg'.
*/
static void
{
return;
}
}
/*
* IRE walker callback: clear ire_testhidden if the IRE has a source address
* on `ill_arg'.
*/
static void
{
}
}
/*
* Return a held pointer to the IPMP ill for underlying interface `ill', or
* NULL if one doesn't exist. (Unfortunately, this function needs to take an
* underlying ill rather than an ipmp_illgrp_t because an underlying ill's
* ill_grp pointer may become stale when not inside an IPSQ and not holding
* ipmp_lock.) Caller need not be inside the IPSQ.
*/
ill_t *
{
return (illg->ig_ipmp_ill);
}
/*
* Assume `ill' was removed from the illgrp in the meantime.
*/
return (NULL);
}
/*
* Return a held pointer to the appropriate underlying ill for sending the
* specified type of packet. (Unfortunately, this function needs to take an
* underlying ill rather than an ipmp_illgrp_t because an underlying ill's
* ill_grp pointer may become stale when not inside an IPSQ and not holding
* ipmp_lock.) Caller need not be inside the IPSQ.
*/
ill_t *
{
/*
* The ill was taken out of the group, so just send on it.
*/
return (ill);
}
if (is_unicast)
else
return (xmit_ill);
}
/*
* Return the interface index for the IPMP ill tied to underlying interface
* `ill', or zero if one doesn't exist. Caller need not be inside the IPSQ.
*/
{
return (ifindex);
}
/*
* Place phyint `phyi' into IPMP group `grp'.
*/
void
{
/*
* Send routing socket messages indicating that the phyint's ills
* and ipifs vanished.
*/
}
}
/*
* Snapshot the phyint's initial kstats as a baseline.
*/
else
/*
* Now that we're in the group, request a switch to the group's xop
* when we ipsq_exit(). All future operations will be exclusive on
* the group xop until ipmp_phyint_leave_grp() is called.
*/
}
/*
* Remove phyint `phyi' from its current IPMP group.
*/
void
{
uint_t i;
/*
* If any of the phyint's ills are still in an illgrp, kick 'em out.
*/
/*
* Send routing socket messages indicating that the phyint's ills
* and ipifs have reappeared.
*/
/*
* Calculate the phyint's cumulative kstats while it was in the group,
* and add that to the group's baseline.
*/
for (i = 0; i < IPMP_KSTAT_MAX; i++) {
}
/*
* As our final act in leaving the group, request a switch back to our
* IPSQ's own xop when we ipsq_exit().
*/
}
/*
* Store the IPMP-related kstats for `phyi' into the array named by `kstats'.
* Assumes that `kstats' has at least IPMP_KSTAT_MAX elements.
*/
static void
{
uint_t i, j;
const char *name;
return;
/*
* Bring kstats up-to-date before recording.
*/
for (i = 0; i < IPMP_KSTAT_MAX; i++) {
kstats[i] = 0;
continue;
case KSTAT_DATA_INT32:
case KSTAT_DATA_UINT32:
break;
#ifdef _LP64
case KSTAT_DATA_LONG:
case KSTAT_DATA_ULONG:
break;
#endif
case KSTAT_DATA_INT64:
case KSTAT_DATA_UINT64:
break;
}
break;
}
}
}
}
/*
* Refresh the active state of all ills on `phyi'.
*/
void
{
}
/*
* Return a held pointer to the underlying ill bound to `ipif', or NULL if one
* doesn't exist. Caller need not be inside the IPSQ.
*/
ill_t *
{
return (boundill);
}
return (NULL);
}
/*
* Return a pointer to the underlying ill bound to `ipif', or NULL if one
* doesn't exist. Caller must be inside the IPSQ.
*/
ill_t *
{
return (ipif->ipif_bound_ill);
}
/*
* Check if `ipif' is a "stub" (placeholder address not being used).
*/
{
return (B_FALSE);
else
}
/*
* Check if `ipif' is an IPMP data address.
*/
{
return (B_FALSE);
else
}
/*
* Check if `ipif' is an IPIF_UP IPMP data address.
*/
static boolean_t
{
}
/*
* Check if `mp' contains a probe packet by checking if the IP source address
* is a test address on underlying interface `ill'. Caller need not be inside
* the IPSQ.
*/
{
if (!IS_UNDER_IPMP(ill))
return (B_FALSE);
return (B_TRUE);
} else {
return (B_TRUE);
}
return (B_FALSE);
}
/*
* NCEC walker callback: delete `ncec' if it is associated with `ill_arg' and
* is not one of our local addresses. Caller must be inside the IPSQ.
*/
static void
{
}
/*
* Delete any NCEs tied to the illgrp associated with `ncec'. Caller need not
* be inside the IPSQ.
*/
void
{
/*
* For each underlying interface, delete `ncec' from its ill_nce list
* via nce_fastpath_list_delete(). Defer the actual nce_refrele()
* until we've dropped ill_g_lock.
*/
list_destroy(&dead);
}
/*
* Refresh any NCE entries tied to the illgrp associated with `ncec' to
* use the information in `ncec'. Caller need not be inside the IPSQ.
*/
void
{
/*
* If `ncec' is not reachable, there is no use in refreshing NCEs.
*/
if (!NCE_ISREACHABLE(ncec))
return;
/*
* Find all the NCEs matching ncec->ncec_addr. We cannot update them
* in-situ because we're holding ipmp_lock to prevent changes to IPMP
* group membership and updating indirectly calls nce_fastpath_probe()
* -> putnext() which cannot hold locks. Thus, move the NCEs to a
* separate list and process that list after dropping ipmp_lock.
*/
}
}
}
/*
* Process the list; nce_lookup_then_add_v* ensures that nce->nce_ill
* is still in the group for ncec->ncec_ill.
*/
NULL);
} else {
}
}
}