ip_ndp.c revision 0e0e37a8f0c38eb919c913bbb67030114a6b74a9
/*
* 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.
*/
#include <sys/sysmacros.h>
#include <sys/ethernet.h>
#include <net/if_types.h>
#include <inet/ipclassifier.h>
#include <inet/ip2mac_impl.h>
#define ANNOUNCE_INTERVAL(isv6) \
#define DEFENSE_INTERVAL(isv6) \
/* Non-tunable probe interval, based on link capabilities */
/*
* The IPv4 Link Local address space is special; we do extra duplicate checking
* there, as the entire assignment mechanism rests on random numbers.
*/
/*
* NCE_EXTERNAL_FLAGS_MASK defines the set of ncec_flags that may be passed
* in to the ncec*add* functions.
*
* NCE_F_AUTHORITY means that we ignore any incoming adverts for that
* mapping (though DAD is performed for the mapping). NCE_F_PUBLISH means
* that we will respond to requests for the protocol address.
*/
#define NCE_EXTERNAL_FLAGS_MASK \
/*
* Lock ordering:
*
* ndp_g_lock -> ill_lock -> ncec_lock
*
* The ndp_g_lock protects the NCE hash (nce_hash_tbl, NCE_HASH_PTR) and
* ncec_next. ncec_lock protects the contents of the NCE (particularly
* ncec_refcnt).
*/
ncec_t *);
static void ncec_refhold_locked(ncec_t *);
static void nce_inactive(nce_t *);
static int nce_add_v6_postprocess(nce_t *);
static int nce_add_v4_postprocess(nce_t *);
static void nce_resolv_ipmp_ok(ncec_t *);
static void nce_fastpath_trigger(nce_t *);
#ifdef DEBUG
static void ncec_trace_cleanup(const ncec_t *);
#endif
NCE_TABLE_SIZE)]))
extern kmem_cache_t *ncec_cache;
extern kmem_cache_t *nce_cache;
/*
* Send out a IPv6 (unicast) or IPv4 (broadcast) DAD probe
* If src_ill is not null, the ncec_addr is bound to src_ill. The
* src_ill is ignored by nce_dad for IPv4 Neighbor Cache entries where
* the probe is sent on the ncec_ill (in the non-IPMP case) or the
* IPMP cast_ill (in the IPMP case).
*
* Note that the probe interval is based on ncec->ncec_ill which
* may be the ipmp_ill.
*/
static void
{
} else {
/* IPv4 DAD delay the initial probe. */
if (send_probe)
else
!send_probe);
}
if (!dropped) {
}
}
/*
* Compute default flags to use for an advertisement of this ncec's address.
*/
static int
{
int flag = 0;
flag |= NDP_ISROUTER;
return (flag);
}
/*
* NDP Cache Entry creation routine.
* This routine must always be called with ndp6->ndp_g_lock held.
*/
int
{
int err;
&nce);
if (err != 0)
return (err);
return (err);
}
/*
* Post-processing routine to be executed after nce_add_v6(). This function
* triggers fastpath (if appropriate) and DAD on the newly added nce entry
* and must be called without any locks held.
*/
int
{
int err = 0;
/*
* If the hw_addr is NULL, typically for ND_INCOMPLETE nces, then
* we call nce_fastpath as soon as the ncec is resolved in nce_process.
* We call nce_fastpath from nce_update if the link layer address of
* the peer changes from nce_update
*/
if (trigger_fastpath)
/*
* Unicast entry that needs DAD.
*/
} else {
hwaddr_ill = ill;
}
err = EINPROGRESS;
} else if (flags & NCE_F_UNSOL_ADV) {
/*
* We account for the transmit below by assigning one
* less than the ndd variable. Subsequent decrements
* are done in nce_timer.
*/
&ipv6_all_hosts_mcast, /* Destination of the packet */
if (dropped)
else
if (ncec->ncec_unsolicit_count != 0) {
}
}
return (err);
}
/*
* Atomically lookup and add (if needed) Neighbor Cache information for
* an address.
*
* IPMP notes: the ncec for non-local (i.e., !NCE_MYADDR(ncec) addresses
* are always added pointing at the ipmp_ill. Thus, when the ill passed
* to nce_add_v6 is an under_ill (i.e., IS_UNDER_IPMP(ill)) two nce_t
* entries will be created, both pointing at the same ncec_t. The nce_t
* entries will have their nce_ill set to the ipmp_ill and the under_ill
* respectively, with the ncec_t having its ncec_ill pointing at the ipmp_ill.
* Local addresses are always created on the ill passed to nce_add_v6.
*/
int
{
int err = 0;
if (flags & NCE_F_MCAST) {
/*
* hw_addr will be figured out in nce_set_multicast_v6;
* caller has to select the cast_ill
*/
return (err);
}
return (ENXIO);
}
&nce);
} else {
}
if (err == 0)
/*
* in_ill was the under_ill. Try to create the under_nce.
* Hold the ill_g_lock to prevent changes to group membership
* until we are done.
*/
nce->nce_common);
}
}
else
}
/* nce_refrele is deferred until the lock is dropped */
if (need_ill_refrele)
return (err);
}
/*
* Remove all the CONDEMNED nces from the appropriate hash table.
* We create a private list of NCEs, these may have ires pointing
* to them, so the list will be passed through to clean up dependent
* ires and only then we can do ncec_refrele() which can make NCE inactive.
*/
static void
{
if (NCE_ISCONDEMNED(ncec)) {
*free_nce_list = ncec;
}
}
}
/*
* 1. Mark the entry CONDEMNED. This ensures that no new nce_lookup()
* will return this NCE. Also no new timeouts will
* be started (See nce_restart_timer).
* 2. Cancel any currently running timeouts.
* 3. If there is an ndp walker, return. The walker will do the cleanup.
* This ensures that walkers see a consistent list of NCEs while walking.
* 4. Otherwise remove the NCE from the list of NCEs
*/
void
{
if (ipversion == IPV4_VERSION)
else
/* Serialize deletes */
if (NCE_ISCONDEMNED(ncec)) {
/* Some other thread is doing the delete */
return;
}
/*
* Caller has a refhold. Also 1 ref for being in the list. Thus
* refcnt has to be >= 2
*/
/* Count how many condemned ires for kmem_cache callback */
/* Complete any waiting callbacks */
/*
* Cancel any running timer. Timeout can't be restarted
* since CONDEMNED is set. Can't hold ncec_lock across untimeout.
* Passing invalid timeout id is fine.
*/
if (ncec->ncec_timeout_id != 0) {
ncec->ncec_timeout_id = 0;
}
/*
* The last ndp walker has already removed this ncec from
* the list after we marked the ncec CONDEMNED and before
* we grabbed the global lock.
*/
return;
}
if (ndp->ndp_g_walker > 0) {
/*
* Can't unlink. The walker will clean up
*/
return;
}
/*
* Now remove the ncec from the list. nce_restart_timer won't restart
* the timer since it is marked CONDEMNED.
*/
}
void
{
/* Count how many condemned nces for kmem_cache callback */
if (NCE_ISCONDEMNED(ncec))
/* Free all allocated messages */
}
/*
* must have been cleaned up in ncec_delete
*/
/*
* free the ncec_lladdr if one was allocated in nce_add_common()
*/
if (ncec->ncec_lladdr_length > 0)
#ifdef DEBUG
#endif
(char *), "ncec", (void *), ncec);
ill->ill_ncec_cnt--;
/*
* If the number of ncec's associated with this ill have dropped
* to zero, check whether we need to restart any operation that
* is waiting for this to happen.
*/
if (ILL_DOWN_OK(ill)) {
/* ipif_ill_refrele_tail drops the ill_lock */
} else {
}
}
/*
* ncec_walk routine. Delete the ncec if it is associated with the ill
* that is going away. Always called as a writer.
*/
void
{
}
}
/*
* Neighbor Cache cleanup logic for a list of ncec_t entries.
*/
static void
{
/*
* It is possible for the last ndp walker (this thread)
* to come here after ncec_delete has marked the ncec CONDEMNED
* and before it has removed the ncec from the fastpath list
* or called untimeout. So we need to do it here. It is safe
* for both ncec_delete and this thread to do it twice or
* even simultaneously since each of the threads has a
* reference on the ncec.
*/
/*
* Cancel any running timer. Timeout can't be restarted
* since CONDEMNED is set. The ncec_lock can't be
* held across untimeout though passing invalid timeout
* id is fine.
*/
if (ncec->ncec_timeout_id != 0) {
ncec->ncec_timeout_id = 0;
}
}
}
/*
* Restart DAD on given NCE. Returns B_TRUE if DAD has been restarted.
*/
{
return (B_FALSE);
/*
* Slight cheat here: we don't use the initial probe delay
* for IPv4 in this obscure case.
*/
} else {
hwaddr_ill = ill;
}
} else {
}
return (started);
}
/*
* IPv6 Cache entry lookup. Try to find an ncec matching the parameters passed.
* If one is found, the refcnt on the ncec will be incremented.
*/
ncec_t *
{
/* Get head of v6 hash table */
return (ncec);
}
/*
* IPv4 Cache entry lookup. Try to find an ncec matching the parameters passed.
* If one is found, the refcnt on the ncec will be incremented.
*/
ncec_t *
{
/* Get head of v4 hash table */
return (ncec);
}
/*
* Cache entry lookup. Try to find an ncec matching the parameters passed.
* If an ncec is found, increment the hold count on that ncec.
* The caller passes in the start of the appropriate hash table, and must
* be holding the appropriate global lock (ndp_g_lock). In addition, since
* this function matches ncec_t entries across the illgrp, the ips_ill_g_lock
* must be held as reader.
*
* This function always matches across the ipmp group.
*/
ncec_t *
{
else
if (IN6_IS_ADDR_UNSPECIFIED(addr))
return (NULL);
if (!NCE_ISCONDEMNED(ncec)) {
break;
}
}
}
}
return (ncec);
}
/*
* Find an nce_t on ill with nce_addr == addr. Lookup the nce_t
* entries for ill only, i.e., when ill is part of an ipmp group,
* nce_lookup_v4 will never try to match across the group.
*/
nce_t *
{
return (nce);
}
/*
* Find an nce_t on ill with nce_addr == addr. Lookup the nce_t
* entries for ill only, i.e., when ill is part of an ipmp group,
* nce_lookup_v6 will never try to match across the group.
*/
nce_t *
{
return (nce);
}
static nce_t *
{
#ifdef DEBUG
else
#endif
return (nce);
}
/*
* Router turned to host. We need to make sure that cached copies of the ncec
* are not used for forwarding packets if they were derived from the default
* route, and that the default route itself is removed, as required by
* section 7.2.5 of RFC 2461.
*
* Note that the ncec itself probably has valid link-layer information for the
* nexthop, so that there is no reason to delete the ncec, as long as the
* ISROUTER flag is turned off.
*/
static void
{
}
}
/*
* Process passed in parameters either from an incoming packet or via
* user ioctl.
*/
void
{
/*
* No updates of link layer address or the neighbor state is
* allowed, when the cache is in NONUD state. This still
* allows for responding to reachability solicitation.
*/
return;
}
/*
* Update ncec state and send the queued packets
* back to ip this time ire will be added.
*/
if (flag & ND_NA_FLAG_SOLICITED) {
} else {
}
return;
}
if (!is_adv) {
/* If this is a SOLICITATION request only */
if (ll_changed)
return;
}
/* If in any other state than REACHABLE, ignore */
}
return;
} else {
if (ll_changed) {
ll_updated = B_TRUE;
}
if (flag & ND_NA_FLAG_SOLICITED) {
} else {
if (ll_updated) {
}
}
NCE_F_ISROUTER)) {
} else {
}
}
}
/*
* Pass arg1 to the pfi supplied, along with each ncec in existence.
* ncec_walk() places a REFHOLD on the ncec and drops the lock when
* walking the hash list.
*/
void
{
/* Prevent ncec_delete from unlink and free of NCE */
ndp->ndp_g_walker++;
if (trace) {
} else {
}
}
}
}
ndp->ndp_g_walker--;
/* Time to delete condemned entries */
}
}
}
if (free_nce_list != NULL) {
}
}
/*
* Walk everything.
* Note that ill can be NULL hence can't derive the ipst from it.
*/
void
{
}
/*
* For each interface an entry is added for the unspecified multicast group.
* Here that mapping is used to form the multicast cache entry for a particular
* multicast destination.
*/
static int
{
int err = 0;
goto done;
}
/*
* For IRE_IF_RESOLVER a hardware mapping can be
* generated.
*/
return (ENOMEM);
}
} else {
/* No hw_addr is needed for IRE_IF_NORESOLVER. */
}
/* nce_state will be computed by nce_add_common() */
ND_UNCHANGED, &nce);
if (err == 0)
if (err != 0) {
return (err);
}
done:
else
return (0);
}
/*
* Return the link layer address, and any flags of a ncec.
*/
int
{
/*
* NOTE: if the ill is an IPMP interface, then match against the whole
* illgrp. This e.g. allows in.ndpd to retrieve the link layer
* addresses for the data addresses on an IPMP interface even though
* ipif_ndp_up() created them with an ncec_ill of ipif_bound_ill.
*/
return (ESRCH);
/* If no link layer address is available yet, return ESRCH */
if (!NCE_ISREACHABLE(ncec)) {
return (ESRCH);
}
lnr->lnr_hdw_len);
return (0);
}
/*
*/
mblk_t *
{
if (IN6_IS_ADDR_V4MAPPED(v6group)) {
} else {
}
ip0dbg(("ndp_mcastreq NULL hw_addr\n"));
return (NULL);
}
return (mp);
}
void
{
/* Make sure we try again later */
return;
}
else
if (ms == 0) {
else
}
} else {
}
done:
}
/*
* Send an IPv6 neighbor solicitation.
* Returns number of milliseconds after which we should either rexmit or abort.
* Return of zero means we should abort.
* The caller holds the ncec_lock to protect ncec_qd_mp and ncec_rcnt.
* The optional source address is used as a hint to ndp_solicit for
* which source to use in the packet.
*
* NOTE: This routine drops ncec_lock (and later reacquires it) when sending
* the packet.
*/
{
return (0);
if (dropped)
}
/*
* Attempt to recover an address on an interface that's been marked as a
* duplicate. Because NCEs are destroyed when the interface goes down, there's
* no easy way to just probe the address and have the right thing happen if
* it's no longer in use. Instead, we just bring it up normally and allow the
* regular interface start-up logic to probe for a remaining duplicate and take
* us back down if necessary.
* Neither DHCP nor temporary addresses arrive here; they're excluded by
* ip_ndp_excl.
*/
/* ARGSUSED */
void
{
/*
* We do not support recovery of proxy ARP'd interfaces,
* because the system lacks a complete proxy ARP mechanism.
*/
addr6);
} else {
}
continue;
/*
* If we have already recovered or if the interface is going
* away, then ignore.
*/
continue;
}
(void) ipif_up_done_v6(ipif);
} else {
(void) ipif_up_done(ipif);
}
}
}
/*
* Attempt to recover an IPv6 interface that's been shut down as a duplicate.
* As long as someone else holds the address, the interface will stay down.
* When that conflict goes away, the interface is brought back up. This is
* done so that accidental shutdowns of addresses aren't made permanent. Your
* server will recover from a failure.
*
* For DHCP and temporary addresses, recovery is not done in the kernel.
* Instead, it's handled by user space processes (dhcpagent and in.ndpd).
*
* This function is entered on a timer expiry; the ID is in ipif_recovery_id.
*/
void
ipif_dup_recovery(void *arg)
{
ipif->ipif_recovery_id = 0;
return;
/*
* No lock, because this is just an optimization.
*/
return;
/* If the link is down, we'll retry this later */
return;
}
/*
* Perform interface recovery by forcing the duplicate interfaces up and
* allowing the system to determine which ones should stay up.
*
* Called both by recovery timer expiry and link-up notification.
*/
void
{
else
if (ipst->ips_ip_dup_recovery > 0 &&
ipif->ipif_recovery_id == 0 &&
}
} else {
/*
* A recovery timer may still be running if we got here from
* ill_restart_dad(); cancel that timer.
*/
if (ipif->ipif_recovery_id != 0)
ipif->ipif_recovery_id = 0;
sizeof (ipif->ipif_v6lcl_addr));
} else {
sizeof (ipif->ipif_lcl_addr));
}
B_FALSE);
}
}
/*
*/
static void
{
int alen;
/* icmp_inbound_v6 ensures this */
if (alen > 0) {
} else {
*haddrlenp = 0;
}
/* nd_ns_target and nd_na_target are at the same offset, so we cheat */
}
/*
* This is for exclusive changes due to NDP duplicate address detection
* failure.
*/
/* ARGSUSED */
static void
{
/* The ill or ip_stack_t disappeared on us */
return;
}
/*
* Ignore conflicts generated by misbehaving switches that
* just reflect our own messages back to us. For IPMP, we may
* see reflections across any ill in the illgrp.
*
* RFC2462 and revisions tried to detect both the case
* when a statically configured IPv6 address is a duplicate,
* and the case when the L2 address itself is a duplicate. The
* later is important because, with stateles address autoconf,
* if the L2 address is a duplicate, the resulting IPv6
* address(es) would also be duplicates. We rely on DAD of the
* IPv6 address itself to detect the latter case.
*/
/* For an under ill_grp can change under lock */
IS_UNDER_IPMP(ill) &&
goto ignore_conflict;
}
}
/*
* Look up the appropriate ipif.
*/
goto ignore_conflict;
/* Reload the ill to match the ipif */
/* If it's already duplicate or ineligible, then don't do anything. */
goto ignore_conflict;
}
/*
* If this is a failure during duplicate recovery, then don't
* complain. It may take a long time to recover.
*/
if (!ipif->ipif_was_dup) {
char hbuf[MAC_STR_LEN];
char sbuf[INET6_ADDRSTRLEN];
" disabled", ibuf,
}
(void) ipif_down_tail(ipif);
ipst->ips_ip_dup_recovery > 0) {
}
}
/*
* Handle failure by tearing down the ipifs with the specified address. Note
* that tearing down the ipif also means deleting the ncec through ipif_down, so
* it's not possible to do recovery by just restarting the ncec timer. Instead,
* we start a timer on the ipif.
* Caller has to free mp;
*/
static void
{
/*
* Ignore conflicts generated by misbehaving switches that just
* reflect our own messages back to us.
*/
/* icmp_inbound_v6 ensures this */
return;
}
} else {
B_FALSE);
}
}
}
/*
* Handle a discovered conflict: some other system is advertising that it owns
* one of our IP addresses. We need to defend ourselves, or just shut down the
* interface.
*
* Handles both IPv4 and IPv6
*/
{
if (isv6) {
ipst);
} else {
if (arp_no_defense) {
/*
* Yes, there is a conflict, but no, we do not
* defend ourself.
*/
return (B_TRUE);
}
ipst);
}
return (B_FALSE);
/*
* First, figure out if this address is disposable.
*/
else
/*
* Now figure out how many times we've defended ourselves. Ignore
* defenses that happened long in the past.
*/
now = ddi_get_lbolt();
/*
* ip_defend_interval has elapsed.
* reset the defense count.
*/
}
/*
* If we've defended ourselves too many times already, then give up and
* tear down the interface(s) using this address.
* Otherwise, caller has to defend by sending out an announce.
*/
if (defs >= maxdefense) {
if (isv6)
else
} else {
return (B_TRUE); /* caller must defend this address */
}
return (B_FALSE);
}
/*
* Handle reception of Neighbor Solicitation messages.
*/
static void
{
int len;
int flag = 0;
if (IN6_IS_ADDR_MULTICAST(&target)) {
if (ip_debug > 2) {
/* ip1dbg */
pr_addr_dbg("ndp_input_solicit: Target is"
}
goto done;
}
if (len > sizeof (nd_neighbor_solicit_t)) {
/* Options present */
len -= sizeof (nd_neighbor_solicit_t);
ip1dbg(("ndp_input_solicit: Bad opt len\n"));
goto done;
}
}
if (IN6_IS_ADDR_UNSPECIFIED(&src)) {
/* Check to see if this is a valid DAD solicitation */
if (ip_debug > 2) {
/* ip1dbg */
pr_addr_dbg("ndp_input_solicit: IPv6 "
"Destination is not solicited node "
"multicast %s\n", AF_INET6,
}
goto done;
}
}
/*
* NOTE: with IPMP, it's possible the nominated multicast ill (which
* received this packet if it's multicast) is not the ill tied to
* e.g. the IPMP ill's data link-local. So we match across the illgrp
* to ensure we find the associated NCE.
*/
/*
* If this is a valid Solicitation for an address we are publishing,
* then a PUBLISH entry should exist in the cache
*/
ip1dbg(("ndp_input_solicit: Wrong target in NS?!"
if (ip_debug > 2) {
/* ip1dbg */
}
goto done;
}
/* At this point we should have a verified NS per spec */
hlen == 0) {
ip1dbg(("ndp_input_advert: bad SLLA\n"));
goto done;
}
}
}
/* If sending directly to peer, set the unicast flag */
flag |= NDP_UNICAST;
/*
* or respond to outstanding queries, don't if
* the source is unspecified address.
*/
if (!IN6_IS_ADDR_UNSPECIFIED(&src)) {
int err;
/*
* Regular solicitations *must* include the Source Link-Layer
* Address option. Ignore messages that do not.
*/
ip1dbg(("ndp_input_solicit: source link-layer address "
"option missing with a specified source.\n"));
goto done;
}
/*
* This is a regular solicitation. If we're still in the
* process of verifying the address, then don't respond at all
* and don't keep track of the sender.
*/
goto done;
/*
* If the solicitation doesn't have sender hardware address
* (legal for unicast solicitation), then process without
* installing the return NCE. Either we already know it, or
* we'll be forced to look it up when (and if) we reply to the
* packet.
*/
goto no_source;
if (IS_UNDER_IPMP(under_ill)) {
else
}
&src, /* Soliciting nodes address */
0,
&nnce);
if (need_ill_refrele) {
}
switch (err) {
case 0:
/* done with this entry */
break;
case EEXIST:
/*
* B_FALSE indicates this is not an an advertisement.
*/
break;
default:
ip1dbg(("ndp_input_solicit: Can't create NCE %d\n",
err));
goto done;
}
flag |= NDP_SOLICITED;
} else {
/*
* No source link layer address option should be present in a
* valid DAD request.
*/
ip1dbg(("ndp_input_solicit: source link-layer address "
"option present with an unspecified source.\n"));
goto done;
}
/*
* Internally looped-back probes will have
* IRAF_L2SRC_LOOPBACK set so we can ignore our own
* transmissions.
*/
/*
* If someone else is probing our address, then
* we've crossed wires. Declare failure.
*/
}
goto done;
}
/*
* This is a DAD probe. Multicast the advertisement to the
* all-nodes address.
*/
}
&target, /* Source and target of the advertisement pkt */
&src, /* IP Destination (source of original pkt) */
flag);
done:
if (bad_solicit)
}
/*
* Handle reception of Neighbor Solicitation messages
*/
void
{
int len;
ip1dbg(("ndp_input_advert: Target is multicast but the "
"solicited flag is not zero\n"));
return;
}
if (IN6_IS_ADDR_MULTICAST(&target)) {
ip1dbg(("ndp_input_advert: Target is multicast!\n"));
return;
}
if (len > sizeof (nd_neighbor_advert_t)) {
if (!ndp_verify_optlen(opt,
len - sizeof (nd_neighbor_advert_t))) {
ip1dbg(("ndp_input_advert: cannot verify SLLA\n"));
return;
}
/* At this point we have a verified NA per spec */
len -= sizeof (nd_neighbor_advert_t);
hlen == 0) {
ip1dbg(("ndp_input_advert: bad SLLA\n"));
return;
}
}
}
/*
* NOTE: we match across the illgrp since we need to do DAD for all of
* our local addresses, and those are spread across all the active
* ills in the group.
*/
return;
if (NCE_PUBLISH(dst_ncec)) {
/*
* Someone just advertised an addresses that we publish. First,
* check it it was us -- if so, we can safely ignore it.
* We don't get the haddr from the ira_l2src because, in the
* case that the packet originated from us, on an IPMP group,
* the ira_l2src may would be the link-layer address of the
* cast_ill used to send the packet, which may not be the same
* as the dst_ncec->ncec_lladdr of the address.
*/
goto out;
goto out; /* from us -- no conflict */
/*
* If we're in an IPMP group, check if this is an echo
* from another ill in the group. Use the double-
* checked locking pattern to avoid grabbing
* ill_g_lock in the non-IPMP case.
*/
if (IS_UNDER_IPMP(ill)) {
goto out;
}
}
}
/*
* This appears to be a real conflict. If we're trying to
* configure this NCE (ND_PROBE), then shut it down.
* Otherwise, handle the discovered conflict.
*/
} else {
char hbuf[MAC_STR_LEN];
char sbuf[INET6_ADDRSTRLEN];
"node '%s' is using %s on %s",
sizeof (sbuf)),
/*
* RFC 4862, Section 5.4.4 does not mandate
* any specific behavior when an NA matches
* a non-tentative address assigned to the
* receiver. We make the choice of defending
* our address, based on the assumption that
* the sender has not detected the Duplicate.
*
* ncec_last_time_defended has been adjusted
* in ip_nce_conflict()
*/
(void) ndp_announce(dst_ncec);
}
}
} else {
/* B_TRUE indicates this an advertisement */
}
out:
}
/*
* Process NDP neighbor solicitation/advertisement messages.
* The checksum has already checked o.k before reaching here.
* Information about the datalink header is contained in ira_l2src, but
* that should be ignored for loopback packets.
*/
void
{
int len;
/*
* Since ira_ill is where the IRE_LOCAL was hosted we use ira_rill
* and make it be the IPMP upper so avoid being confused by a packet
* addressed to a unicast address on a different ill.
*/
if (IS_UNDER_IPMP(ill)) {
ip_drop_input("ipIfStatsInDiscards - IPMP ill",
return;
}
}
ip1dbg(("ndp_input: pullupmsg failed\n"));
goto done;
}
ip1dbg(("ndp_input: hoplimit != IPV6_MAX_HOPS\n"));
goto done;
}
/*
* NDP does not accept any extension headers between the
* IP header and the ICMP header since e.g. a routing
* header could be dangerous.
* This assumes that any AH or ESP headers are removed
* by ip prior to passing the packet to ndp_input.
*/
ip1dbg(("ndp_input: Wrong next header 0x%x\n",
goto done;
}
if (icmp_nd->icmp6_code != 0) {
ip1dbg(("ndp_input: icmp6 code != 0 \n"));
goto done;
}
/*
* Make sure packet length is large enough for either
* a NS or a NA icmp packet.
*/
ip1dbg(("ndp_input: packet too short\n"));
goto done;
}
} else {
}
done:
}
}
/*
* ndp_xmit is called to form and transmit a ND solicitation or
* advertisement ICMP packet.
*
* If the source address is unspecified and this isn't a probe (used for
* duplicate address detection), an appropriate source address and link layer
* address will be chosen here. The link layer address option is included if
* the source is specified (i.e., all non-probe packets), and omitted (per the
* specification) otherwise.
*
* It returns B_FALSE only if it does a successful put() to the
* corresponding ill's ill_wq otherwise returns B_TRUE.
*/
static boolean_t
{
if (IS_UNDER_IPMP(ill)) {
/*
* We send non-probe packets on the upper IPMP interface.
* ip_output_simple() will use cast_ill for sending any
* multicast packets. Note that we can't follow the same
* logic for probe packets because all interfaces in the ipmp
* group may have failed, so that we really want to only try
* to send the ND packet on the ill corresponding to the src
* address.
*/
if (!probe) {
else
ill = hwaddr_ill;
}
}
/*
* If we have a unspecified source(sender) address, select a
* proper source address for the solicitation here itself so
* that we can initialize the h/w address correctly.
*
* If the sender is specified then we use this address in order
* to lookup the zoneid before calling ip_output_v6(). This is to
* enable unicast ND_NEIGHBOR_ADVERT packets to be routed correctly
* by IP (we cannot guarantee that the global zone has an interface
* route to the destination).
*
* Note that the NA never comes here with the unspecified source
* address.
*/
/*
* Probes will have unspec src at this point.
*/
if (!(IN6_IS_ADDR_UNSPECIFIED(sender))) {
/*
* It's possible for ipif_lookup_addr_zoneid_v6() to return
* ALL_ZONES if it cannot find a matching ipif for the address
* we are trying to use. In this case we err on the side of
* trying to send the packet by defaulting to the GLOBAL_ZONEID.
*/
}
if (need_refrele)
return (B_TRUE);
}
if (hw_addr_len != 0) {
sizeof (nd_neighbor_advert_t));
} else {
}
if (operation == ND_NEIGHBOR_SOLICIT) {
/*
* Note that we don't send out SLLA for ND probes
* per RFC 4862, even though we do send out the src
* haddr for IPv4 DAD probes, even though both IPv4
* and IPv6 go out with the unspecified/INADDR_ANY
* src IP addr.
*/
}
if (!(flag & NDP_UNICAST)) {
/* Form multicast address of the target */
}
} else {
if (flag & NDP_ISROUTER)
if (flag & NDP_SOLICITED)
}
/* Fill in link layer address and option len */
}
}
/* If there's no link layer address option, then strip it. */
}
icmp6->icmp6_code = 0;
/*
* Prepare for checksum by putting icmp length in the icmp
* checksum field. The checksum is calculated in ip_output.c.
*/
ixa_cleanup(&ixas);
if (need_refrele)
return (B_FALSE);
}
/*
* Used to set ND_UNREACHBLE before ncec_delete sets it NCE_F_CONDEMNED.
* The datapath uses this as an indication that there
* is a problem (as opposed to a NCE that was just
* reclaimed due to lack of memory.
* Note that static ARP entries never become unreachable.
*/
void
{
}
/*
* NCE retransmit timer. Common to IPv4 and IPv6.
* This timer goes off when:
* a. It is time to retransmit a resolution for resolver.
* b. It is time to send reachability probes.
*/
void
{
char addrbuf[INET6_ADDRSTRLEN];
/*
* The timer has to be cancelled by ncec_delete before doing the final
* refrele. So the NCE is guaranteed to exist when the timer runs
* until it clears the timeout_id. Before clearing the timeout_id
* bump up the refcnt so that we can continue to use the ncec
*/
ncec->ncec_timeout_id = 0;
/* if we could not find a sender address, return */
if (!isv6) {
} else {
}
return;
}
if (!isv6)
/*
* Check the reachability state.
*/
switch (ncec->ncec_state) {
case ND_DELAY:
if (isv6) {
} else {
}
if (!dropped) {
}
if (ip_debug > 3) {
/* ip2dbg */
pr_addr_dbg("nce_timer: state for %s changed "
}
break;
case ND_PROBE:
/* must be retransmit timer */
/*
* As per RFC2461, the ncec gets deleted after
* MAX_UNICAST_SOLICIT unsuccessful re-transmissions.
* Note that the first unicast solicitation is sent
* during the DELAY state.
*/
ip2dbg(("nce_timer: pcount=%x dst %s\n",
if (NCE_PUBLISH(ncec)) {
/*
* send out a probe; note that src_ill
* is ignored by nce_dad() for all
* DAD message types other than IPv6
* unicast probes
*/
} else {
if (isv6) {
} else {
/*
* since the nce is REACHABLE,
* the ARP request will be sent out
* as a link-layer unicast.
*/
src_ill);
}
if (!dropped) {
}
}
/* No hope, delete the ncec */
/* Tell datapath it went bad */
if (ip_debug > 2) {
/* ip1dbg */
pr_addr_dbg("nce_timer: Delete NCE for"
}
/* if static ARP can't delete. */
} else if (!NCE_PUBLISH(ncec)) {
/*
* Probe count is 0 for a dynamic entry (one that we
* ourselves are not publishing). We should never get
* here if NONUD was requested, hence the ASSERT below.
*/
ip2dbg(("nce_timer: pcount=%x dst %s\n",
/* Wait one interval before killing */
/*
* We're done probing, and we can now declare this
* address to be usable. Let IP know that it's ok to
* use.
*/
if (isv6) {
} else {
ipst);
}
if (ipif->ipif_was_dup) {
char sbuf[INET6_ADDRSTRLEN];
sizeof (ibuf));
}
}
if (!isv6 && arp_no_defense)
break;
/* Begin defending our new address */
if (ncec->ncec_unsolicit_count > 0) {
if (isv6) {
} else {
}
if (dropped)
else
}
if (ncec->ncec_unsolicit_count > 0) {
} else if (DEFENSE_INTERVAL(isv6) != 0) {
}
} else {
/*
* This is an address we're probing to be our own, but
* the ill is down. Wait until it comes back before
* doing anything, but switch to reachable state so
* that the restart will work.
*/
}
break;
case ND_INCOMPLETE: {
/*
* Per case (2) in the nce_queue_mp() comments, scan ncec_qd_mp
* for any IPMP probe packets, and toss them. IPMP probe
* packets will always be at the head of ncec_qd_mp, so that
* we can stop at the first queued ND packet that is
* not a probe packet.
*/
ncec->ncec_nprobes--;
} else {
}
}
/*
* Must be resolver's retransmit timer.
*/
break;
}
case ND_REACHABLE:
ncec->ncec_unsolicit_count != 0) ||
if (ncec->ncec_unsolicit_count > 0) {
/*
* When we get to zero announcements left,
* switch to address defense
*/
} else {
if (rate_limit) {
break;
}
}
if (isv6) {
} else {
}
if (dropped) {
} else {
}
if (ncec->ncec_unsolicit_count != 0) {
} else {
}
} else {
}
break;
default:
break;
}
done:
}
/*
* Set a link layer address from the ll_addr passed in.
* Copy SAP from ill.
*/
static void
{
if (ill->ill_phys_addr_length > 0) {
/*
* The bcopy() below used to be called for the physical address
* length rather than the link layer address length. For
* ethernet and many other media, the phys_addr and lla are
* identical.
*
* The phys_addr and lla may not be the same for devices that
* support DL_IPV6_LINK_LAYER_ADDR, though there are currently
* no known instances of these.
*
* For PPP or other interfaces with a zero length
* physical address, don't do anything here.
* The bcopy() with a zero phys_addr length was previously
* a no-op for interfaces with a zero-length physical address.
* Using the lla for them would change the way they operate.
* Doing nothing in such cases preserves expected behavior.
*/
}
}
{
return (B_FALSE);
return (B_TRUE);
return (B_FALSE);
}
/*
* Updates the link layer address or the reachability state of
* a cache entry. Reset probe counter if needed.
*/
void
{
/*
* If this interface does not do NUD, there is no point
* in allowing an update to the cache entry. Although
* we will respond to NS.
* The only time we accept an update for a resolver when
* NUD is turned off is when it has just been created.
* Non-Resolvers will always be created as REACHABLE.
*/
if (new_state != ND_UNCHANGED) {
return;
if (new_state == ND_REACHABLE)
else {
/* We force NUD in this case */
}
new_state == ND_INCOMPLETE);
}
ncec->ncec_timeout_id = 0;
}
/*
* Re-trigger fastpath probe and
* overwrite the DL_UNITDATA_REQ data, noting we'll lose
* whatever packets that happens to be transmitting at the time.
*/
if (new_ll_addr != NULL) {
}
if (tid != 0)
}
if (need_fastpath_update) {
/*
* Delete any existing existing dlur_mp and fp_mp information.
* For IPMP interfaces, all underlying ill's must be checked
* and purged.
*/
/*
* add the new dlur_mp and fp_mp
*/
}
}
static void
{
/*
* if we never create data addrs on the under_ill
* does this matter?
*/
}
}
if (head_insert) {
ncec->ncec_nprobes++;
} else {
}
}
/*
* nce_queue_mp will queue the packet into the ncec_qd_mp. The packet will be
* queued at the head or tail of the queue based on the input argument
* 'head_insert'. The caller should specify this argument as B_TRUE if this
* packet is an IPMP probe packet, in which case the following happens:
*
* 1. Insert it at the head of the ncec_qd_mp list. Consider the normal
* (non-ipmp_probe) load-speading case where the source address of the ND
* packet is not tied to ncec_ill. If the ill bound to the source address
* cannot receive, the response to the ND packet will not be received.
* However, if ND packets for ncec_ill's probes are queued behind that ND
* packet, those probes will also fail to be sent, and thus in.mpathd will
* erroneously conclude that ncec_ill has also failed.
*
* 2. Drop the ipmp_probe packet in ndp_timer() if the ND did not succeed on
* the first attempt. This ensures that ND problems do not manifest as
* probe RTT spikes.
*
* We achieve this by inserting ipmp_probe() packets at the head of the
* nce_queue.
*
* The ncec for the probe target is created with ncec_ill set to the ipmp_ill,
* but the caller needs to set head_insert to B_TRUE if this is a probe packet.
*/
void
{
}
/*
* Called when address resolution failed due to a timeout.
* Send an ICMP unreachable in response to all queued packets.
*/
void
{
char buf[INET6_ADDRSTRLEN];
/*
* we are setting the ira_rill to the ipmp_ill (instead of
* the actual ill on which the packet was received), but this
* is ok because we don't actually need the real ira_rill.
* to send the icmp unreachable to the sender.
*/
ip1dbg(("ndp_resolv_failed: dst %s\n",
ncec->ncec_nprobes = 0;
ip_drop_output("ipIfStatsOutDiscards - address unreachable",
}
}
/*
* Handle the completion of NDP and ARP resolution.
*/
void
{
return;
}
/* non IPMP case */
} else {
ixaflags |= IXAF_IS_IPV4;
}
/*
* IXAF_NO_DEV_FLOW_CTL information for TCP packets is no
* longer available, but it's ok to drop this flag because TCP
* has its own flow-control in effect, so TCP packets
* are not likely to get here when flow-control is in effect.
*/
if (isv6) {
} else {
}
ip_drop_output("ipIfStatsOutDiscards - no nce",
} else {
/*
* We don't know the zoneid, but
* ip_xmit does not care since IXAF_NO_TRACE
* is set. (We traced the packet the first
* time through ip_xmit.)
*/
}
}
}
/*
* and the corresponding attributes.
* Disallow states other than ND_REACHABLE or ND_STALE.
*/
int
{
int err = 0;
return (EINVAL);
case NDF_ISROUTER_ON:
break;
case NDF_ISROUTER_OFF:
new_flags &= ~NCE_F_ISROUTER;
break;
case (NDF_ISROUTER_OFF|NDF_ISROUTER_ON):
return (EINVAL);
}
case NDF_ANYCAST_ON:
break;
case NDF_ANYCAST_OFF:
new_flags &= ~NCE_F_ANYCAST;
break;
case (NDF_ANYCAST_OFF|NDF_ANYCAST_ON):
return (EINVAL);
}
addr,
&nce);
if (err != 0) {
return (err);
} else {
}
}
if (do_postprocess)
return (0);
}
if (do_postprocess)
/*
* err cannot be anything other than 0 because we don't support
* proxy arp of static addresses.
*/
/*
* Note that we ignore the state at this point, which
* should be either STALE or REACHABLE. Instead we let
* the link layer address passed in to determine the state
* much like incoming packets.
*/
return (0);
}
/*
* Create an nce_t structure for ill using the ncec->ncec_lladdr to set up
* the nce_dlur_mp. If ill != ncec->ncec_ill, then the ips_ill_g_lock must
* be held to ensure that they are in the same group.
*/
static nce_t *
{
return (nce);
/*
* hold the ncec_lock to synchronize with nce_update() so that,
* at the end of this function, the contents of nce_dlur_mp are
* consistent with ncec->ncec_lladdr, even though some intermediate
* packet may have been sent out with a mangled address, which would
* only be a transient condition.
*/
} else {
}
return (nce);
}
/*
* we make nce_fp_mp to have an M_DATA prepend.
* The caller ensures there is hold on ncec for this function.
* Note that since ill_fastpath_probe() copies the mblk there is
* no need to hold the nce or ncec beyond this function.
*
* If the caller has passed in a non-null ncec_nce to nce_faspath() that
* ncec_nce must correspond to the nce for ncec with nce_ill == ncec->ncec_ill
* and will be returned back by this function, so that no extra nce_refrele
* is required for the caller. The calls from nce_add_common() use this
* method. All other callers (that pass in NULL ncec_nce) will have to do a
* nce_refrele of the returned nce (when it is non-null).
*/
nce_t *
{
}
/*
* If the caller already has the nce corresponding to the ill, use
* nce_add_common() fall in the former category, and have just done
*/
else
return (nce);
if (trigger_fp_req)
return (nce);
}
/*
* Trigger fastpath on nce. No locks may be held.
*/
static void
{
int res;
/*
* EAGAIN is an indication of a transient error
* i.e. allocation failure etc. leave the ncec in the list it
* will be updated when another probe happens for another ire
* if not it will be taken out of the list when the ire is
* deleted.
*/
}
/*
* Add ncec to the nce fastpath list on ill.
*/
static nce_t *
{
/*
* Atomically ensure that the ill is not CONDEMNED and is not going
* down, before adding the NCE.
*/
return (NULL);
/*
* if ncec has not been deleted and
* is not already in the list add it.
*/
if (!NCE_ISCONDEMNED(ncec)) {
goto done;
}
done:
return (nce);
}
nce_t *
{
return (nce);
}
/*
* remove ncec from the ill_nce list. If 'dead' is non-null, the deleted
* nce is added to the 'dead' list, and the caller must nce_refrele() the
* entry after all locks have been dropped.
*/
void
{
/* first clean out any nce pointers in the under_ills */
/* now the ill itself */
break;
}
}
else
}
}
/*
* when the fastpath response does not fit in the datab
* associated with the existing nce_fp_mp, we delete and
* add the nce to retrigger fastpath based on the information
* in the ncec_t.
*/
static nce_t *
{
ip0dbg(("nce_delete_then_add nce %p ill %s\n",
/*
* Make sure that ncec is not condemned before adding. We hold the
* ill_lock and ncec_lock to synchronize with ncec_delete() and
* ipmp_ncec_flush_nce()
*/
return (newnce); /* could be null if nomem */
}
typedef struct nce_fp_match_s {
/* ARGSUSED */
static int
{
/*
* mp is the mp associated with the fastpath ack.
* ud_mp is the outstanding DL_UNITDATA_REQ on the nce_t
* under consideration. If the contents match, then the
* fastpath ack is used to update the nce.
*/
return (0);
/*
* The ncec is locked here to prevent any other threads from accessing
* and changing nce_dlur_mp when the address becomes resolved to an
* lla while we're in the middle of looking at and comparing the
* hardware address (lla). It is also locked to prevent multiple
* threads in nce_fastpath() from examining nce_dlur_mp at the same
* time.
*/
return (1);
}
return (0);
}
/*
* Update all NCE's that are not in fastpath mode and
* have an nce_fp_mp that matches mp. mp->b_cont contains
* the fastpath header.
*
* Returns TRUE if entry should be dequeued, or FALSE otherwise.
*/
void
{
return;
return;
}
}
}
/* Matched - install mp as the fastpath mp */
} else {
}
}
/*
* Return a pointer to a given option in the packet.
* Assumes that option part of the packet have already been validated.
*/
{
while (optlen > 0) {
return (opt);
}
return (NULL);
}
/*
* Verify all option lengths present are > 0, also check to see
* if the option lengths and packet length are consistent.
*/
{
while (optlen > 0) {
if (opt->nd_opt_len == 0)
return (B_FALSE);
if (optlen < 0)
return (B_FALSE);
}
return (B_TRUE);
}
/*
* ncec_walk function.
* Free a fraction of the NCE cache entries.
*
* A possible optimization here would be to use ncec_last where possible, and
* delete the least-frequently used entry, which would require more complex
* computation as we walk through the ncec's (e.g., track ncec entries by
*/
static void
{
if ((ncec->ncec_flags &
return;
}
}
}
/*
* kmem_cache callback to free up memory.
*
* For now we just delete a fixed fraction.
*/
static void
{
/*
* Walk all CONNs that can have a reference on an ire, ncec or dce.
* Get them to update any stale references to drop any refholds they
* have.
*/
}
/*
* Called by the memory allocator subsystem directly, when the system
* is running low on memory.
*/
/* ARGSUSED */
void
ip_nce_reclaim(void *args)
{
netstack_t *ns;
}
}
#ifdef DEBUG
void
{
if (ncec->ncec_trace_disable)
return;
}
}
void
{
if (!ncec->ncec_trace_disable)
}
static void
{
}
#endif
/*
* Called when address resolution fails due to a timeout.
* Send an ICMP unreachable in response to all queued packets.
*/
void
{
char buf[INET6_ADDRSTRLEN];
/*
* we are setting the ira_rill to the ipmp_ill (instead of
* the actual ill on which the packet was received), but this
* is ok because we don't actually need the real ira_rill.
* to send the icmp unreachable to the sender.
*/
ip3dbg(("arp_resolv_failed: dst %s\n",
ncec->ncec_nprobes = 0;
ip_drop_output("ipIfStatsOutDiscards - address unreachable",
if (ipst->ips_ip_arp_icmp_error) {
ip3dbg(("arp_resolv_failed: "
"Calling icmp_unreachable\n"));
} else {
}
}
}
/*
* if ill is an under_ill, translate it to the ipmp_ill and add the
* nce on the ipmp_ill. Two nce_t entries (one on the ipmp_ill, and
* one on the underlying in_ill) will be created for the
* ncec_t in this case. The ncec_t itself will be created on the ipmp_ill.
*/
int
{
int err;
if (flags & NCE_F_MCAST) {
/*
* hw_addr will be figured out in nce_set_multicast_v4;
* caller needs to pass in the cast_ill for ipmp
*/
return (err);
}
return (ENXIO);
}
if ((flags & NCE_F_BCAST) != 0) {
/*
* IPv4 broadcast ncec: compute the hwaddr.
*/
if (need_ill_refrele)
return (ENETDOWN);
}
} else {
}
}
} else {
}
if (err == 0)
/*
* in_ill was the under_ill. Try to create the under_nce.
* Hold the ill_g_lock to prevent changes to group membership
* until we are done.
*/
nce->nce_common);
}
}
else
}
if (need_ill_refrele)
return (err);
}
/*
* NDP Cache Entry creation routine for IPv4.
* This routine must always be called with ndp4->ndp_g_lock held.
* Prior to return, ncec_refcnt is incremented.
*
* IPMP notes: the ncec for non-local (i.e., !NCE_MYADDR(ncec) addresses
* are always added pointing at the ipmp_ill. Thus, when the ill passed
* to nce_add_v4 is an under_ill (i.e., IS_UNDER_IPMP(ill)) two nce_t
* entries will be created, both pointing at the same ncec_t. The nce_t
* entries will have their nce_ill set to the ipmp_ill and the under_ill
* respectively, with the ncec_t having its ncec_ill pointing at the ipmp_ill.
* Local addresses are always created on the ill passed to nce_add_v4.
*/
int
{
int err;
&nce);
return (err);
}
/*
* Post-processing routine to be executed after nce_add_v4(). This function
* triggers fastpath (if appropriate) and DAD on the newly added nce entry
* and must be called without any locks held.
*
* Always returns 0, but we return an int to keep this symmetric with the
* IPv6 counter-part.
*/
int
{
/*
* If the hw_addr is NULL, typically for ND_INCOMPLETE nces, then
* we call nce_fastpath as soon as the ncec is resolved in nce_process.
* We call nce_fastpath from nce_update if the link layer address of
* the peer changes from nce_update
*/
if (trigger_fastpath)
/*
* Either the caller (by passing in ND_PROBE)
* or nce_add_common() (by the internally computed state
* based on ncec_addr and ill_net_type) has determined
* that this unicast entry needs DAD. Trigger DAD.
*/
} else if (flags & NCE_F_UNSOL_ADV) {
/*
* We account for the transmit below by assigning one
* less than the ndd variable. Subsequent decrements
* are done in nce_timer.
*/
if (dropped)
else
if (ncec->ncec_unsolicit_count != 0) {
}
}
/*
* If ncec_xmit_interval is 0, user has configured us to send the first
* probe right away. Do so, and set up for the subsequent probes.
*/
if (ndp_need_dad) {
/*
* DAD probes and announce can be
* administratively disabled by setting the
* probe_count to zero. Restart the timer in
* this case to mark the ipif as ready.
*/
ncec->ncec_unsolicit_count = 0;
nce_restart_timer(ncec, 0);
} else {
}
}
return (0);
}
/*
* ncec_walk routine to update all entries that have a given destination or
* gateway address and cached link layer (MAC) address. This is used when ARP
* informs us that a network-to-link-layer mapping may have changed.
*/
void
{
return;
return;
}
void
{
(ncec)->ncec_refcnt++;
#ifdef DEBUG
#endif
}
void
{
(ncec)->ncec_refcnt++;
}
static void
{
(ncec)->ncec_refcnt++;
#ifdef DEBUG
#endif
}
/* ncec_inactive destroys the mutex thus no mutex_exit is needed */
void
{
#ifdef DEBUG
#endif
if (--(ncec)->ncec_refcnt == 0) {
} else {
}
}
void
{
if (--(ncec)->ncec_refcnt == 0) {
} else {
}
}
/*
* Common to IPv4 and IPv6.
*/
void
{
/* First cancel any running timer */
ncec->ncec_timeout_id = 0;
if (tid != 0) {
}
/* Restart timer */
}
static void
{
/*
* Don't start the timer if the ncec has been deleted, or if the timer
* is already running
*/
}
}
int
{
int err = 0;
goto done;
}
/*
* For IRE_IF_RESOLVER a hardware mapping can be
* generated, for IRE_IF_NORESOLVER, resolution cookie
* in the ill is copied in nce_add_v4().
*/
return (ENOMEM);
}
} else {
/*
* IRE_IF_NORESOLVER type simply copies the resolution
* cookie passed in. So no hw_addr is needed.
*/
}
/* nce_state will be computed by nce_add_common() */
ND_UNCHANGED, &nce);
if (err == 0)
if (err != 0) {
return (err);
}
done:
else
return (0);
}
/*
* This is used when scanning for "old" (least recently broadcast) NCEs. We
* don't want to have to walk the list for every single one, so we gather up
* batches at a time.
*/
#define NCE_RESCHED_LIST_LEN 8
typedef struct {
/*
* Pick the longest waiting NCEs for defense.
*/
/* ARGSUSED */
static int
{
/*
* Only reachable entries that are ready for announcement are eligible.
*/
return (0);
} else {
if ((*ncecs)->ncec_last_time_defended >
}
}
}
return (0);
}
/*
* Reschedule the ARP defense of any long-waiting NCEs. It's assumed that this
* doesn't happen very often (if at all), and thus it needn't be highly
* optimized. (Note, though, that it's actually O(N) complexity, because the
* outer loop is bounded by a constant rather than by the length of the list.)
*/
static void
{
uint_t i, defend_rate;
i = ill->ill_defend_count;
ill->ill_defend_count = 0;
else
/* If none could be sitting around, then don't reschedule */
if (i < defend_rate) {
return;
}
/*
* we plan to schedule this ncec, so incr the
* defend_count in anticipation.
*/
break;
}
break;
}
}
/*
* Check if the current rate-limiting parameters permit the sending
* of another address defense announcement for both IPv4 and IPv6.
* Returns B_TRUE if rate-limiting is in effect (i.e., send is not
* permitted), and B_FALSE otherwise. The `defend_rate' parameter
* determines how many address defense announcements are permitted
* in any `defense_perio' interval.
*/
static boolean_t
{
int i;
} else {
}
if (defend_rate == 0)
return (B_TRUE);
if (start > 0) {
/*
* nce_ill_reschedule will attempt to
* prevent starvation by reschduling the
* oldest entries, which are marked with
* the NCE_F_DELAYED flag.
*/
}
} else {
}
/*
* This ncec was rescheduled as one of the really old
* entries needing on-going defense. The
* ill_defend_count was already incremented in
* nce_ill_reschedule. Go ahead and send the announce.
*/
goto done;
}
ill->ill_defend_count++;
/*
* we are no longer allowed to send unbidden defense
* messages. Wait for rescheduling.
*/
} else {
}
done:
/*
* After all the locks have been dropped we can restart nce timer,
* and refrele the delayed ncecs
*/
B_FALSE);
}
return (ret);
}
{
nce_advert_flags(ncec)));
}
ill_t *
{
if (is_myaddr) {
if (!isv6)
} else {
/*
* try to find one from the outgoing packet.
*/
if (isv6) {
} else {
}
}
}
/*
* For outgoing packets, if the src of outgoing packet is one
* of the assigned interface addresses use it, otherwise we
* will pick the source address below.
* For local addresses (is_myaddr) doing DAD, NDP announce
* messages are mcast. So we use the (IPMP) cast_ill or the
* (non-IPMP) ncec_ill for these message types. The only case
* of unicast DAD messages are for IPv6 ND probes, for which
* we find the ipif_bound_ill corresponding to the ncec_addr.
*/
if (isv6) {
} else {
}
/*
* If no relevant ipif can be found, then it's not one of our
* addresses. Reset to :: and try to find a src for the NS or
* ARP request using ipif_select_source_v[4,6] below.
* If an ipif can be found, but it's not yet done with
* DAD verification, and we are not being invoked for
* DAD (i.e., !is_myaddr), then just postpone this
* transmission until later.
*/
src4 = INADDR_ANY;
return (NULL);
}
}
/*
* Pick a source address for this solicitation, but
* restrict the selection to addresses assigned to the
* output interface. We do this because the destination will
* create a neighbor cache entry for the source address of
* this packet, so the source address had better be a valid
* neighbor.
*/
if (isv6) {
} else {
}
if (isv6) {
} else {
src4);
}
}
}
char buf[INET6_ADDRSTRLEN];
ip1dbg(("nce_resolve_src: No source ipif for dst %s\n",
return (NULL);
}
}
else
}
return (src_ill);
}
void
{
/*
* only one ncec is possible
*/
if (NCE_ISREACHABLE(ncec))
else
return;
}
} else {
/*
* ill is wildcard; clean up all ncec's and ire's
* that match on addr.
*/
}
}
/*
* Common function to add ncec entries.
* we always add the ncec with ncec_ill == ill, and always create
* nce_t on ncec_ill. A dlpi fastpath message may be triggered if the
* ncec is !reachable.
*
* When the caller passes in an nce_state of ND_UNCHANGED,
* nce_add_common() will determine the state of the created nce based
* on the ill_net_type and nce_flags used. Otherwise, the nce will
* be created with state set to the passed in nce_state.
*/
static int
{
int err;
else
if (IN6_IS_ADDR_UNSPECIFIED(addr)) {
ip0dbg(("nce_add_common: no addr\n"));
return (EINVAL);
}
if ((flags & ~NCE_EXTERNAL_FLAGS_MASK)) {
return (EINVAL);
}
} else {
}
/*
* The caller has ensured that there is no nce on ill, but there could
* still be an nce_common_t for the address, so that we find exisiting
* ncec_t strucutures first, and atomically add a new nce_t if
* one is found. The ndp_g_lock ensures that we don't cross threads
* with an ncec_delete(). Unlike ncec_lookup_illgrp() we do not
* compare for matches across the illgrp because this function is
* called via nce_lookup_then_add_v* -> nce_add_v* -> nce_add_common,
* with the nce_lookup_then_add_v* passing in the ipmp_ill where
* appropriate.
*/
break;
}
}
}
/*
* We should never find *retnce to be MYADDR, since the caller
* may then incorrectly restart a DAD timer that's already
* running.
*/
/* caller must trigger fastpath on nce */
return (0);
}
return (ENOMEM);
/*
* DAD probe interval and probe count are set based on
* with IPv4 169.254.0.0/16 Link Local Address space, then
* don't use the fast timers. Otherwise, use them.
*/
if (fastprobe) {
} else {
}
if (NCE_PUBLISH(ncec)) {
}
} else {
/*
* probe interval is constant: ILL_PROBE_INTERVAL
* probe count is constant: ND_MAX_UNICAST_SOLICIT
*/
if (NCE_PUBLISH(ncec)) {
}
}
/*
* ncec_lladdr holds link layer address
*/
if (hw_addr_len > 0) {
goto err_ret;
}
}
if ((flags & NCE_F_BCAST) != 0) {
ASSERT(hw_addr_len > 0);
state = ND_INITIAL;
/*
* NORESOLVER entries are always created in the REACHABLE
* state.
*/
/*
* We create a nce_res_mp with the IP nexthop address
* as the destination address if the physical length
* is exactly 4 bytes for point-to-multipoint links
* that do their own resolution from IP to link-layer
* address (e.g. IP over X.25).
*/
}
/*
* We create a nce_res_mp with the IP nexthop address
* as the destination address if the physical legnth
* is exactly 16 bytes for point-to-multipoint links
* that do their own resolution from IP to link-layer
* address.
*/
}
/*
* Since NUD is not part of the base IPv4 protocol definition,
* IPv4 neighbor entries on NORESOLVER interfaces will never
* age, and are marked NCE_F_NONUD.
*/
}
/*
* We are adding an ncec with a deterministic hw_addr,
* so the state can only be one of {REACHABLE, STALE, PROBE}.
*
* if we are adding a unicast ncec for the local address
* it would be REACHABLE; we would be adding a ND_STALE entry
* for the requestor of an ARP_REQUEST/ND_SOLICIT. Our own
* addresses are added in PROBE to trigger DAD.
*/
else if (!NCE_PUBLISH(ncec))
else
}
/* caller overrides internally computed state */
if (nce_state != ND_UNCHANGED)
if (state == ND_REACHABLE) {
} else {
if (state == ND_INITIAL)
}
/*
* have all the memory allocations out of the way before taking locks
* and adding the nce.
*/
goto err_ret;
}
goto err_ret;
}
}
/*
* Atomically ensure that the ill is not CONDEMNED, before
* adding the NCE.
*/
goto err_ret;
}
if (!NCE_MYADDR(ncec) &&
goto err_ret;
}
/*
* Acquire the ncec_lock even before adding the ncec to the list
* so that it cannot get deleted after the ncec is added, but
* before we add the nce.
*/
/* Bump up the number of ncec's referencing this ill */
(char *), "ncec", (void *), ncec);
ill->ill_ncec_cnt++;
/*
* Since we hold the ncec_lock at this time, the ncec cannot be
* condemned, and we can safely add the nce.
*/
/* caller must trigger fastpath on *retnce */
return (0);
return (err);
}
/*
* take a ref on the nce
*/
void
{
nce->nce_refcnt++;
}
/*
* release a ref on the nce; In general, this
* cannot be called with locks held because nce_inactive
* may result in nce_inactive which will take the ill_lock,
* do ipif_ill_refrele_tail etc. Thus the one exception
* where this can be called with locks held is when the caller
* is certain that the nce_refcnt is sufficient to prevent
* the invocation of nce_inactive.
*/
void
{
if (--nce->nce_refcnt == 0)
else
}
/*
* free the nce after all refs have gone away.
*/
static void
{
(char *), "nce", (void *), nce);
ill->ill_nce_cnt--;
/*
* If the number of ncec's associated with this ill have dropped
* to zero, check whether we need to restart any operation that
* is waiting for this to happen.
*/
if (ILL_DOWN_OK(ill)) {
/* ipif_ill_refrele_tail drops the ill_lock */
} else {
}
}
/*
* Add an nce to the ill_nce list.
*/
static nce_t *
{
(char *), "nce", (void *), nce);
ill->ill_nce_cnt++;
/* add nce to the ill's fastpath list. */
return (nce);
}
static nce_t *
{
return (NULL);
return (NULL);
}
}
}
/*
* remove the nce from the ill_faspath list
*/
void
{
if (nce->nce_is_condemned) {
/*
* some other thread has removed this nce from the ill_nce list
*/
return;
}
/*
* even though we are holding the ill_lock, it is ok to
* call nce_refrele here because we know that we should have
* at least 2 refs on the nce: one for the thread, and one
* for the list. The refrele below will release the one for
* the list.
*/
}
nce_t *
{
break;
}
/*
* if we found the nce on the ill_nce list while holding
* the ill_lock, then it cannot be condemned yet.
*/
}
return (nce);
}
/*
* Walk the ill_nce list on ill. The callback function func() cannot perform
* any destructive actions.
*/
static void
{
break;
}
}
void
{
}
void
{
continue;
}
/*
* nce_delete requires that the caller should either not
* be holding locks, or should hold a ref to ensure that
* we wont hit ncec_inactive. So take a ref and clean up
* after the list is flushed.
*/
}
}
list_destroy(&dead);
}
/* Return an interval that is anywhere in the [1 .. intv] range */
static clock_t
{
/* Note that clock_t is signed; must chop off bits */
if (initial_time) {
if (intv <= 0)
intv = 1;
else
} else {
/* Compute 'frac' as 20% of the configured interval */
frac = 2;
/* Set intv randomly in the range [intv-frac .. intv+frac] */
intv = 1;
}
return (intv);
}
void
{
ncec->ncec_nprobes = 0;
if (isv6) {
} else {
ixaflags |= IXAF_IS_IPV4;
}
/*
* find a new nce based on an under_ill. The first IPMP probe
* packet gets queued, so we could still find a src_ipif that
* matches an IPMP test address.
*/
/*
* if src_ipif is null, this could be either a
* forwarded packet or a probe whose src got deleted.
* We identify the former case by looking for the
* ncec_nprobes: the first ncec_nprobes packets are
* probes;
*/
goto drop_pkt;
/*
* For forwarded packets, we use the ipmp rotor
* to find send_ill.
*/
B_TRUE);
} else {
}
goto drop_pkt;
}
/* create an under_nce on send_ill */
else
if (nprobes > 0)
nprobes--;
continue;
}
if (isv6) {
} else {
}
if (nprobes > 0)
nprobes--;
}
}