xref: /onnv-gate/usr/src/uts/common/inet/ip/ip2mac.c (revision 11066:cebb50cbe4f9)
19175SSowmini.Varadhan@Sun.COM /*
29175SSowmini.Varadhan@Sun.COM  * CDDL HEADER START
39175SSowmini.Varadhan@Sun.COM  *
49175SSowmini.Varadhan@Sun.COM  * The contents of this file are subject to the terms of the
59175SSowmini.Varadhan@Sun.COM  * Common Development and Distribution License (the "License").
69175SSowmini.Varadhan@Sun.COM  * You may not use this file except in compliance with the License.
79175SSowmini.Varadhan@Sun.COM  *
89175SSowmini.Varadhan@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
99175SSowmini.Varadhan@Sun.COM  * or http://www.opensolaris.org/os/licensing.
109175SSowmini.Varadhan@Sun.COM  * See the License for the specific language governing permissions
119175SSowmini.Varadhan@Sun.COM  * and limitations under the License.
129175SSowmini.Varadhan@Sun.COM  *
139175SSowmini.Varadhan@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
149175SSowmini.Varadhan@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
159175SSowmini.Varadhan@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
169175SSowmini.Varadhan@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
179175SSowmini.Varadhan@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
189175SSowmini.Varadhan@Sun.COM  *
199175SSowmini.Varadhan@Sun.COM  * CDDL HEADER END
209175SSowmini.Varadhan@Sun.COM  */
2111042SErik.Nordmark@Sun.COM 
229175SSowmini.Varadhan@Sun.COM /*
239175SSowmini.Varadhan@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
249175SSowmini.Varadhan@Sun.COM  * Use is subject to license terms.
259175SSowmini.Varadhan@Sun.COM  */
269175SSowmini.Varadhan@Sun.COM 
279175SSowmini.Varadhan@Sun.COM /*
289175SSowmini.Varadhan@Sun.COM  * Functions to implement IP address -> link layer address (PSARC 2006/482)
299175SSowmini.Varadhan@Sun.COM  */
309175SSowmini.Varadhan@Sun.COM #include <inet/ip2mac.h>
319175SSowmini.Varadhan@Sun.COM #include <inet/ip2mac_impl.h>
329175SSowmini.Varadhan@Sun.COM #include <sys/zone.h>
339175SSowmini.Varadhan@Sun.COM #include <inet/ip_ndp.h>
349175SSowmini.Varadhan@Sun.COM #include <inet/ip_if.h>
359175SSowmini.Varadhan@Sun.COM #include <inet/ip6.h>
369175SSowmini.Varadhan@Sun.COM 
379175SSowmini.Varadhan@Sun.COM /*
389175SSowmini.Varadhan@Sun.COM  * dispatch pending callbacks.
399175SSowmini.Varadhan@Sun.COM  */
409175SSowmini.Varadhan@Sun.COM void
ncec_cb_dispatch(ncec_t * ncec)4111042SErik.Nordmark@Sun.COM ncec_cb_dispatch(ncec_t *ncec)
429175SSowmini.Varadhan@Sun.COM {
4311042SErik.Nordmark@Sun.COM 	ncec_cb_t *ncec_cb;
449175SSowmini.Varadhan@Sun.COM 	ip2mac_t ip2m;
459175SSowmini.Varadhan@Sun.COM 
4611042SErik.Nordmark@Sun.COM 	mutex_enter(&ncec->ncec_lock);
4711042SErik.Nordmark@Sun.COM 	if (list_is_empty(&ncec->ncec_cb)) {
4811042SErik.Nordmark@Sun.COM 		mutex_exit(&ncec->ncec_lock);
499175SSowmini.Varadhan@Sun.COM 		return;
509175SSowmini.Varadhan@Sun.COM 	}
5111042SErik.Nordmark@Sun.COM 	ncec_ip2mac_response(&ip2m, ncec);
5211042SErik.Nordmark@Sun.COM 	ncec_cb_refhold_locked(ncec);
539175SSowmini.Varadhan@Sun.COM 	/*
549175SSowmini.Varadhan@Sun.COM 	 * IP does not hold internal locks like nce_lock across calls to
559175SSowmini.Varadhan@Sun.COM 	 * other subsystems for fear of recursive lock entry and lock
569175SSowmini.Varadhan@Sun.COM 	 * hierarchy violation. The caller may be holding locks across
579175SSowmini.Varadhan@Sun.COM 	 * the call to IP. (It would be ideal if no subsystem holds locks
589175SSowmini.Varadhan@Sun.COM 	 * across calls into another subsystem, especially if calls can
599175SSowmini.Varadhan@Sun.COM 	 * happen in either direction).
609175SSowmini.Varadhan@Sun.COM 	 */
6111042SErik.Nordmark@Sun.COM 	ncec_cb = list_head(&ncec->ncec_cb);
6211042SErik.Nordmark@Sun.COM 	for (; ncec_cb != NULL; ncec_cb = list_next(&ncec->ncec_cb, ncec_cb)) {
6311042SErik.Nordmark@Sun.COM 		if (ncec_cb->ncec_cb_flags & NCE_CB_DISPATCHED)
649175SSowmini.Varadhan@Sun.COM 			continue;
6511042SErik.Nordmark@Sun.COM 		ncec_cb->ncec_cb_flags |= NCE_CB_DISPATCHED;
6611042SErik.Nordmark@Sun.COM 		mutex_exit(&ncec->ncec_lock);
6711042SErik.Nordmark@Sun.COM 		(*ncec_cb->ncec_cb_func)(&ip2m, ncec_cb->ncec_cb_arg);
6811042SErik.Nordmark@Sun.COM 		mutex_enter(&ncec->ncec_lock);
699175SSowmini.Varadhan@Sun.COM 	}
7011042SErik.Nordmark@Sun.COM 	ncec_cb_refrele(ncec);
7111042SErik.Nordmark@Sun.COM 	mutex_exit(&ncec->ncec_lock);
729175SSowmini.Varadhan@Sun.COM }
739175SSowmini.Varadhan@Sun.COM 
749175SSowmini.Varadhan@Sun.COM /*
759175SSowmini.Varadhan@Sun.COM  * fill up the ip2m response fields with inforamation from the nce.
769175SSowmini.Varadhan@Sun.COM  */
779175SSowmini.Varadhan@Sun.COM void
ncec_ip2mac_response(ip2mac_t * ip2m,ncec_t * ncec)7811042SErik.Nordmark@Sun.COM ncec_ip2mac_response(ip2mac_t *ip2m, ncec_t *ncec)
799175SSowmini.Varadhan@Sun.COM {
8011042SErik.Nordmark@Sun.COM 	boolean_t isv6 = (ncec->ncec_ipversion == IPV6_VERSION);
8111042SErik.Nordmark@Sun.COM 	sin_t	*sin;
829175SSowmini.Varadhan@Sun.COM 	sin6_t	*sin6;
839175SSowmini.Varadhan@Sun.COM 	struct sockaddr_dl *sdl;
849175SSowmini.Varadhan@Sun.COM 
8511042SErik.Nordmark@Sun.COM 	ASSERT(MUTEX_HELD(&ncec->ncec_lock));
869175SSowmini.Varadhan@Sun.COM 	bzero(ip2m, sizeof (*ip2m));
8711042SErik.Nordmark@Sun.COM 	if (NCE_ISREACHABLE(ncec) && !NCE_ISCONDEMNED(ncec))
889175SSowmini.Varadhan@Sun.COM 		ip2m->ip2mac_err = 0;
899175SSowmini.Varadhan@Sun.COM 	else
909175SSowmini.Varadhan@Sun.COM 		ip2m->ip2mac_err = ESRCH;
919175SSowmini.Varadhan@Sun.COM 	if (isv6) {
929175SSowmini.Varadhan@Sun.COM 		sin6 = (sin6_t *)&ip2m->ip2mac_pa;
939175SSowmini.Varadhan@Sun.COM 		sin6->sin6_family = AF_INET6;
9411042SErik.Nordmark@Sun.COM 		sin6->sin6_addr = ncec->ncec_addr;
9511042SErik.Nordmark@Sun.COM 	} else {
9611042SErik.Nordmark@Sun.COM 		sin = (sin_t *)&ip2m->ip2mac_pa;
9711042SErik.Nordmark@Sun.COM 		sin->sin_family = AF_INET;
9811042SErik.Nordmark@Sun.COM 		IN6_V4MAPPED_TO_INADDR(&ncec->ncec_addr, &sin->sin_addr);
999175SSowmini.Varadhan@Sun.COM 	}
1009175SSowmini.Varadhan@Sun.COM 	if (ip2m->ip2mac_err == 0) {
1019175SSowmini.Varadhan@Sun.COM 		sdl = &ip2m->ip2mac_ha;
1029175SSowmini.Varadhan@Sun.COM 		sdl->sdl_family = AF_LINK;
10311042SErik.Nordmark@Sun.COM 		sdl->sdl_type = ncec->ncec_ill->ill_type;
10411042SErik.Nordmark@Sun.COM 		/*
10511042SErik.Nordmark@Sun.COM 		 * should we put ncec_ill->ill_name in there? why?
10611042SErik.Nordmark@Sun.COM 		 * likewise for the sdl_index
10711042SErik.Nordmark@Sun.COM 		 */
1089175SSowmini.Varadhan@Sun.COM 		sdl->sdl_nlen = 0;
10911042SErik.Nordmark@Sun.COM 		sdl->sdl_alen = ncec->ncec_ill->ill_phys_addr_length;
11011042SErik.Nordmark@Sun.COM 		if (ncec->ncec_lladdr != NULL)
11111042SErik.Nordmark@Sun.COM 			bcopy(ncec->ncec_lladdr, LLADDR(sdl), sdl->sdl_alen);
1129175SSowmini.Varadhan@Sun.COM 	}
1139175SSowmini.Varadhan@Sun.COM }
1149175SSowmini.Varadhan@Sun.COM 
1159175SSowmini.Varadhan@Sun.COM void
ncec_cb_refhold_locked(ncec_t * ncec)11611042SErik.Nordmark@Sun.COM ncec_cb_refhold_locked(ncec_t *ncec)
1179175SSowmini.Varadhan@Sun.COM {
11811042SErik.Nordmark@Sun.COM 	ASSERT(MUTEX_HELD(&ncec->ncec_lock));
11911042SErik.Nordmark@Sun.COM 	ncec->ncec_cb_walker_cnt++;
1209175SSowmini.Varadhan@Sun.COM }
1219175SSowmini.Varadhan@Sun.COM 
1229175SSowmini.Varadhan@Sun.COM void
ncec_cb_refrele(ncec_t * ncec)12311042SErik.Nordmark@Sun.COM ncec_cb_refrele(ncec_t *ncec)
1249175SSowmini.Varadhan@Sun.COM {
12511042SErik.Nordmark@Sun.COM 	ncec_cb_t *ncec_cb, *ncec_cb_next = NULL;
1269175SSowmini.Varadhan@Sun.COM 
12711042SErik.Nordmark@Sun.COM 	ASSERT(MUTEX_HELD(&ncec->ncec_lock));
12811042SErik.Nordmark@Sun.COM 	if (--ncec->ncec_cb_walker_cnt == 0) {
12911042SErik.Nordmark@Sun.COM 		for (ncec_cb = list_head(&ncec->ncec_cb); ncec_cb != NULL;
13011042SErik.Nordmark@Sun.COM 		    ncec_cb = ncec_cb_next) {
1319175SSowmini.Varadhan@Sun.COM 
13211042SErik.Nordmark@Sun.COM 			ncec_cb_next = list_next(&ncec->ncec_cb, ncec_cb);
13311042SErik.Nordmark@Sun.COM 			if ((ncec_cb->ncec_cb_flags & NCE_CB_DISPATCHED) == 0)
1349175SSowmini.Varadhan@Sun.COM 				continue;
13511042SErik.Nordmark@Sun.COM 			list_remove(&ncec->ncec_cb, ncec_cb);
13611042SErik.Nordmark@Sun.COM 			kmem_free(ncec_cb, sizeof (*ncec_cb));
1379175SSowmini.Varadhan@Sun.COM 		}
1389175SSowmini.Varadhan@Sun.COM 	}
1399175SSowmini.Varadhan@Sun.COM }
1409175SSowmini.Varadhan@Sun.COM 
1419175SSowmini.Varadhan@Sun.COM /*
1429175SSowmini.Varadhan@Sun.COM  * add a callback to the nce, so that the callback can be invoked
1439175SSowmini.Varadhan@Sun.COM  * after address resolution succeeds/fails.
1449175SSowmini.Varadhan@Sun.COM  */
1459175SSowmini.Varadhan@Sun.COM static ip2mac_id_t
ncec_add_cb(ncec_t * ncec,ip2mac_callback_t * cb,void * cbarg)14611042SErik.Nordmark@Sun.COM ncec_add_cb(ncec_t *ncec, ip2mac_callback_t *cb, void *cbarg)
1479175SSowmini.Varadhan@Sun.COM {
14811042SErik.Nordmark@Sun.COM 	ncec_cb_t	*nce_cb;
1499175SSowmini.Varadhan@Sun.COM 	ip2mac_id_t	ip2mid = NULL;
1509175SSowmini.Varadhan@Sun.COM 
15111042SErik.Nordmark@Sun.COM 	ASSERT(MUTEX_HELD(&ncec->ncec_lock));
1529175SSowmini.Varadhan@Sun.COM 	if ((nce_cb = kmem_zalloc(sizeof (*nce_cb), KM_NOSLEEP)) == NULL)
1539175SSowmini.Varadhan@Sun.COM 		return (ip2mid);
15411042SErik.Nordmark@Sun.COM 	nce_cb->ncec_cb_func = cb;
15511042SErik.Nordmark@Sun.COM 	nce_cb->ncec_cb_arg = cbarg;
1569175SSowmini.Varadhan@Sun.COM 	/*
15711042SErik.Nordmark@Sun.COM 	 * We identify the ncec_cb_t during cancellation by the address
1589175SSowmini.Varadhan@Sun.COM 	 * of the nce_cb_t itself, and, as a short-cut for eliminating
15911042SErik.Nordmark@Sun.COM 	 * clear mismatches, only look in the callback list of ncec's
1609175SSowmini.Varadhan@Sun.COM 	 * whose address is equal to the nce_cb_id.
1619175SSowmini.Varadhan@Sun.COM 	 */
16211042SErik.Nordmark@Sun.COM 	nce_cb->ncec_cb_id = ncec; /* no refs! just an address */
16311042SErik.Nordmark@Sun.COM 	list_insert_tail(&ncec->ncec_cb, nce_cb);
16411042SErik.Nordmark@Sun.COM 	ip2mid = ncec;  /* this is the id to be used in ip2mac_cancel */
1659175SSowmini.Varadhan@Sun.COM 
1669175SSowmini.Varadhan@Sun.COM 	return (nce_cb);
1679175SSowmini.Varadhan@Sun.COM }
1689175SSowmini.Varadhan@Sun.COM 
1699175SSowmini.Varadhan@Sun.COM /*
1709175SSowmini.Varadhan@Sun.COM  * Resolve an IP address to a link-layer address using the data-structures
1719175SSowmini.Varadhan@Sun.COM  * defined in PSARC 2006/482. If the current link-layer address for the
1729175SSowmini.Varadhan@Sun.COM  * IP address is not known, the state-machine for resolving the resolution
1739175SSowmini.Varadhan@Sun.COM  * will be triggered, and the callback function (*cb) will be invoked after
1749175SSowmini.Varadhan@Sun.COM  * the resolution completes.
1759175SSowmini.Varadhan@Sun.COM  */
1769175SSowmini.Varadhan@Sun.COM ip2mac_id_t
ip2mac(uint_t op,ip2mac_t * ip2m,ip2mac_callback_t * cb,void * cbarg,zoneid_t zoneid)17711042SErik.Nordmark@Sun.COM ip2mac(uint_t op, ip2mac_t *ip2m, ip2mac_callback_t *cb, void *cbarg,
1789175SSowmini.Varadhan@Sun.COM     zoneid_t zoneid)
1799175SSowmini.Varadhan@Sun.COM {
18011042SErik.Nordmark@Sun.COM 	ncec_t		*ncec;
18111042SErik.Nordmark@Sun.COM 	nce_t		*nce = NULL;
1829175SSowmini.Varadhan@Sun.COM 	boolean_t	isv6;
1839175SSowmini.Varadhan@Sun.COM 	ill_t		*ill;
1849175SSowmini.Varadhan@Sun.COM 	netstack_t	*ns;
1859175SSowmini.Varadhan@Sun.COM 	ip_stack_t	*ipst;
1869175SSowmini.Varadhan@Sun.COM 	ip2mac_id_t	ip2mid = NULL;
18711042SErik.Nordmark@Sun.COM 	sin_t		*sin;
1889175SSowmini.Varadhan@Sun.COM 	sin6_t		*sin6;
1899175SSowmini.Varadhan@Sun.COM 	int		err;
1909175SSowmini.Varadhan@Sun.COM 	uint64_t	delta;
19111042SErik.Nordmark@Sun.COM 	boolean_t	need_resolve = B_FALSE;
1929175SSowmini.Varadhan@Sun.COM 
1939175SSowmini.Varadhan@Sun.COM 	isv6 = (ip2m->ip2mac_pa.ss_family == AF_INET6);
1949175SSowmini.Varadhan@Sun.COM 
1959175SSowmini.Varadhan@Sun.COM 	ns = netstack_find_by_zoneid(zoneid);
1969175SSowmini.Varadhan@Sun.COM 	if (ns == NULL) {
1979175SSowmini.Varadhan@Sun.COM 		ip2m->ip2mac_err = EINVAL;
1989175SSowmini.Varadhan@Sun.COM 		return (NULL);
1999175SSowmini.Varadhan@Sun.COM 	}
2009175SSowmini.Varadhan@Sun.COM 	/*
2019175SSowmini.Varadhan@Sun.COM 	 * For exclusive stacks we reset the zoneid to zero
2029175SSowmini.Varadhan@Sun.COM 	 * since IP uses the global zoneid in the exclusive stacks.
2039175SSowmini.Varadhan@Sun.COM 	 */
2049175SSowmini.Varadhan@Sun.COM 	if (ns->netstack_stackid != GLOBAL_NETSTACKID)
2059175SSowmini.Varadhan@Sun.COM 		zoneid = GLOBAL_ZONEID;
2069175SSowmini.Varadhan@Sun.COM 	ipst = ns->netstack_ip;
2079175SSowmini.Varadhan@Sun.COM 	/*
2089175SSowmini.Varadhan@Sun.COM 	 * find the ill from the ip2m->ip2mac_ifindex
2099175SSowmini.Varadhan@Sun.COM 	 */
21011042SErik.Nordmark@Sun.COM 	ill = ill_lookup_on_ifindex(ip2m->ip2mac_ifindex, isv6, ipst);
2119175SSowmini.Varadhan@Sun.COM 	if (ill == NULL) {
2129175SSowmini.Varadhan@Sun.COM 		ip2m->ip2mac_err = ENXIO;
2139175SSowmini.Varadhan@Sun.COM 		netstack_rele(ns);
2149175SSowmini.Varadhan@Sun.COM 		return (NULL);
2159175SSowmini.Varadhan@Sun.COM 	}
2169175SSowmini.Varadhan@Sun.COM 	if (isv6) {
2179175SSowmini.Varadhan@Sun.COM 		sin6 = (sin6_t *)&ip2m->ip2mac_pa;
21811042SErik.Nordmark@Sun.COM 		if (op == IP2MAC_LOOKUP) {
21911042SErik.Nordmark@Sun.COM 			nce = nce_lookup_v6(ill, &sin6->sin6_addr);
2209175SSowmini.Varadhan@Sun.COM 		} else {
22111042SErik.Nordmark@Sun.COM 			err = nce_lookup_then_add_v6(ill, NULL,
22211042SErik.Nordmark@Sun.COM 			    ill->ill_phys_addr_length,
22311042SErik.Nordmark@Sun.COM 			    &sin6->sin6_addr, 0, ND_UNCHANGED, &nce);
2249175SSowmini.Varadhan@Sun.COM 		}
2259175SSowmini.Varadhan@Sun.COM 	} else  {
22611042SErik.Nordmark@Sun.COM 		sin = (sin_t *)&ip2m->ip2mac_pa;
22711042SErik.Nordmark@Sun.COM 		if (op == IP2MAC_LOOKUP) {
22811042SErik.Nordmark@Sun.COM 			nce = nce_lookup_v4(ill, &sin->sin_addr.s_addr);
22911042SErik.Nordmark@Sun.COM 		} else {
23011042SErik.Nordmark@Sun.COM 			err = nce_lookup_then_add_v4(ill, NULL,
23111042SErik.Nordmark@Sun.COM 			    ill->ill_phys_addr_length,
23211042SErik.Nordmark@Sun.COM 			    &sin->sin_addr.s_addr, 0, ND_UNCHANGED, &nce);
23311042SErik.Nordmark@Sun.COM 		}
2349175SSowmini.Varadhan@Sun.COM 	}
23511042SErik.Nordmark@Sun.COM 	if (op == IP2MAC_LOOKUP) {
2369175SSowmini.Varadhan@Sun.COM 		if (nce == NULL) {
2379175SSowmini.Varadhan@Sun.COM 			ip2m->ip2mac_err = ESRCH;
2389175SSowmini.Varadhan@Sun.COM 			goto done;
2399175SSowmini.Varadhan@Sun.COM 		}
24011042SErik.Nordmark@Sun.COM 		ncec = nce->nce_common;
241*11066Srafael.vanoni@sun.com 		delta = TICK_TO_MSEC(ddi_get_lbolt64()) - ncec->ncec_last;
24211042SErik.Nordmark@Sun.COM 		mutex_enter(&ncec->ncec_lock);
24311042SErik.Nordmark@Sun.COM 		if (NCE_ISREACHABLE(ncec) &&
24411042SErik.Nordmark@Sun.COM 		    delta < (uint64_t)ill->ill_reachable_time) {
24511042SErik.Nordmark@Sun.COM 			ncec_ip2mac_response(ip2m, ncec);
2469175SSowmini.Varadhan@Sun.COM 			ip2m->ip2mac_err = 0;
2479175SSowmini.Varadhan@Sun.COM 		} else {
2489175SSowmini.Varadhan@Sun.COM 			ip2m->ip2mac_err = ESRCH;
2499175SSowmini.Varadhan@Sun.COM 		}
25011042SErik.Nordmark@Sun.COM 		mutex_exit(&ncec->ncec_lock);
2519175SSowmini.Varadhan@Sun.COM 		goto done;
2529175SSowmini.Varadhan@Sun.COM 	} else {
2539175SSowmini.Varadhan@Sun.COM 		if (err != 0 && err != EEXIST) {
2549175SSowmini.Varadhan@Sun.COM 			ip2m->ip2mac_err = err;
2559175SSowmini.Varadhan@Sun.COM 			goto done;
2569175SSowmini.Varadhan@Sun.COM 		}
2579175SSowmini.Varadhan@Sun.COM 	}
25811042SErik.Nordmark@Sun.COM 	ncec = nce->nce_common;
259*11066Srafael.vanoni@sun.com 	delta = TICK_TO_MSEC(ddi_get_lbolt64()) - ncec->ncec_last;
26011042SErik.Nordmark@Sun.COM 	mutex_enter(&ncec->ncec_lock);
26111042SErik.Nordmark@Sun.COM 	if (NCE_ISCONDEMNED(ncec)) {
2629175SSowmini.Varadhan@Sun.COM 		ip2m->ip2mac_err = ESRCH;
26311042SErik.Nordmark@Sun.COM 	} else {
26411042SErik.Nordmark@Sun.COM 		if (NCE_ISREACHABLE(ncec)) {
26511042SErik.Nordmark@Sun.COM 			if (NCE_MYADDR(ncec) ||
26611042SErik.Nordmark@Sun.COM 			    delta < (uint64_t)ill->ill_reachable_time) {
26711042SErik.Nordmark@Sun.COM 				ncec_ip2mac_response(ip2m, ncec);
26811042SErik.Nordmark@Sun.COM 				ip2m->ip2mac_err = 0;
26911042SErik.Nordmark@Sun.COM 				mutex_exit(&ncec->ncec_lock);
27011042SErik.Nordmark@Sun.COM 				goto done;
27111042SErik.Nordmark@Sun.COM 			}
2729175SSowmini.Varadhan@Sun.COM 			/*
2739175SSowmini.Varadhan@Sun.COM 			 * Since we do not control the packet output
2749175SSowmini.Varadhan@Sun.COM 			 * path for ip2mac() callers, we need to verify
2759175SSowmini.Varadhan@Sun.COM 			 * if the existing information in the nce is
2769175SSowmini.Varadhan@Sun.COM 			 * very old, and retrigger resolution if necessary.
2779175SSowmini.Varadhan@Sun.COM 			 * We will not return the existing stale
2789175SSowmini.Varadhan@Sun.COM 			 * information until it is verified through a
2799175SSowmini.Varadhan@Sun.COM 			 * resolver request/response exchange.
2809175SSowmini.Varadhan@Sun.COM 			 *
2819175SSowmini.Varadhan@Sun.COM 			 * In the future, we may want to support extensions
2829175SSowmini.Varadhan@Sun.COM 			 * that do additional callbacks on link-layer updates,
2839175SSowmini.Varadhan@Sun.COM 			 * so that we can return the stale information but
2849175SSowmini.Varadhan@Sun.COM 			 * also update the caller if the lladdr changes.
2859175SSowmini.Varadhan@Sun.COM 			 */
28611042SErik.Nordmark@Sun.COM 			ncec->ncec_rcnt = ill->ill_xmit_count;
28711042SErik.Nordmark@Sun.COM 			ncec->ncec_state = ND_PROBE;
28811042SErik.Nordmark@Sun.COM 			need_resolve = B_TRUE; /* reachable but very old nce */
28911042SErik.Nordmark@Sun.COM 		} else if (ncec->ncec_state == ND_INITIAL) {
29011042SErik.Nordmark@Sun.COM 			need_resolve = B_TRUE; /* ND_INITIAL nce */
29111042SErik.Nordmark@Sun.COM 			ncec->ncec_state = ND_INCOMPLETE;
2929175SSowmini.Varadhan@Sun.COM 		}
29311042SErik.Nordmark@Sun.COM 		/*
29411042SErik.Nordmark@Sun.COM 		 * NCE not known to be reachable in the recent past. We must
29511042SErik.Nordmark@Sun.COM 		 * reconfirm the information before returning it to the caller
29611042SErik.Nordmark@Sun.COM 		 */
29711042SErik.Nordmark@Sun.COM 		if (ncec->ncec_rcnt > 0) {
2989175SSowmini.Varadhan@Sun.COM 			/*
29911042SErik.Nordmark@Sun.COM 			 * Still resolving this ncec, so we can queue the
30011042SErik.Nordmark@Sun.COM 			 * callback information in ncec->ncec_cb
3019175SSowmini.Varadhan@Sun.COM 			 */
30211042SErik.Nordmark@Sun.COM 			ip2mid = ncec_add_cb(ncec, cb, cbarg);
3039175SSowmini.Varadhan@Sun.COM 			ip2m->ip2mac_err = EINPROGRESS;
3049175SSowmini.Varadhan@Sun.COM 		} else {
3059175SSowmini.Varadhan@Sun.COM 			/*
30611042SErik.Nordmark@Sun.COM 			 * No more retransmits allowed -- resolution failed.
3079175SSowmini.Varadhan@Sun.COM 			 */
3089175SSowmini.Varadhan@Sun.COM 			ip2m->ip2mac_err = ESRCH;
3099175SSowmini.Varadhan@Sun.COM 		}
3109175SSowmini.Varadhan@Sun.COM 	}
31111042SErik.Nordmark@Sun.COM 	mutex_exit(&ncec->ncec_lock);
3129175SSowmini.Varadhan@Sun.COM done:
31311042SErik.Nordmark@Sun.COM 	/*
31411042SErik.Nordmark@Sun.COM 	 * if NCE_ISREACHABLE(ncec) but very old, or if it is ND_INITIAL,
31511042SErik.Nordmark@Sun.COM 	 * trigger resolve.
31611042SErik.Nordmark@Sun.COM 	 */
31711042SErik.Nordmark@Sun.COM 	if (need_resolve)
31811042SErik.Nordmark@Sun.COM 		ip_ndp_resolve(ncec);
31911042SErik.Nordmark@Sun.COM 	if (nce != NULL)
32011042SErik.Nordmark@Sun.COM 		nce_refrele(nce);
3219175SSowmini.Varadhan@Sun.COM 	netstack_rele(ns);
3229175SSowmini.Varadhan@Sun.COM 	ill_refrele(ill);
3239175SSowmini.Varadhan@Sun.COM 	return (ip2mid);
3249175SSowmini.Varadhan@Sun.COM }
3259175SSowmini.Varadhan@Sun.COM 
3269175SSowmini.Varadhan@Sun.COM /*
32711042SErik.Nordmark@Sun.COM  * data passed to ncec_walk for canceling outstanding callbacks.
3289175SSowmini.Varadhan@Sun.COM  */
3299175SSowmini.Varadhan@Sun.COM typedef struct ip2mac_cancel_data_s {
3309175SSowmini.Varadhan@Sun.COM 	ip2mac_id_t ip2m_cancel_id;
3319175SSowmini.Varadhan@Sun.COM 	int	ip2m_cancel_err;
3329175SSowmini.Varadhan@Sun.COM } ip2mac_cancel_data_t;
3339175SSowmini.Varadhan@Sun.COM 
3349175SSowmini.Varadhan@Sun.COM /*
33511042SErik.Nordmark@Sun.COM  * callback invoked for each active ncec. If the ip2mac_id_t corresponds
33611042SErik.Nordmark@Sun.COM  * to an active nce_cb_t in the ncec's callback list, we want to remove
3379175SSowmini.Varadhan@Sun.COM  * the callback (if there are no walkers) or return EBUSY to the caller
3389175SSowmini.Varadhan@Sun.COM  */
3399175SSowmini.Varadhan@Sun.COM static int
ip2mac_cancel_callback(ncec_t * ncec,void * arg)34011042SErik.Nordmark@Sun.COM ip2mac_cancel_callback(ncec_t *ncec, void *arg)
3419175SSowmini.Varadhan@Sun.COM {
3429175SSowmini.Varadhan@Sun.COM 	ip2mac_cancel_data_t *ip2m_wdata = arg;
34311042SErik.Nordmark@Sun.COM 	ncec_cb_t *ip2m_nce_cb = ip2m_wdata->ip2m_cancel_id;
34411042SErik.Nordmark@Sun.COM 	ncec_cb_t *ncec_cb;
3459175SSowmini.Varadhan@Sun.COM 
34611042SErik.Nordmark@Sun.COM 	if (ip2m_nce_cb->ncec_cb_id != ncec)
3479175SSowmini.Varadhan@Sun.COM 		return (0);
3489175SSowmini.Varadhan@Sun.COM 
34911042SErik.Nordmark@Sun.COM 	mutex_enter(&ncec->ncec_lock);
35011042SErik.Nordmark@Sun.COM 	if (list_is_empty(&ncec->ncec_cb)) {
35111042SErik.Nordmark@Sun.COM 		mutex_exit(&ncec->ncec_lock);
3529175SSowmini.Varadhan@Sun.COM 		return (0);
3539175SSowmini.Varadhan@Sun.COM 	}
3549175SSowmini.Varadhan@Sun.COM 	/*
3559175SSowmini.Varadhan@Sun.COM 	 * IP does not hold internal locks like nce_lock across calls to
3569175SSowmini.Varadhan@Sun.COM 	 * other subsystems for fear of recursive lock entry and lock
3579175SSowmini.Varadhan@Sun.COM 	 * hierarchy violation. The caller may be holding locks across
3589175SSowmini.Varadhan@Sun.COM 	 * the call to IP. (It would be ideal if no subsystem holds locks
3599175SSowmini.Varadhan@Sun.COM 	 * across calls into another subsystem, especially if calls can
3609175SSowmini.Varadhan@Sun.COM 	 * happen in either direction).
3619175SSowmini.Varadhan@Sun.COM 	 */
36211042SErik.Nordmark@Sun.COM 	ncec_cb = list_head(&ncec->ncec_cb);
36311042SErik.Nordmark@Sun.COM 	for (; ncec_cb != NULL; ncec_cb = list_next(&ncec->ncec_cb, ncec_cb)) {
36411042SErik.Nordmark@Sun.COM 		if (ncec_cb != ip2m_nce_cb)
3659175SSowmini.Varadhan@Sun.COM 			continue;
3669175SSowmini.Varadhan@Sun.COM 		/*
3679175SSowmini.Varadhan@Sun.COM 		 * If there are no walkers we can remove the nce_cb.
3689175SSowmini.Varadhan@Sun.COM 		 * Otherwise the exiting walker will clean up.
3699175SSowmini.Varadhan@Sun.COM 		 */
37011042SErik.Nordmark@Sun.COM 		if (ncec->ncec_cb_walker_cnt == 0) {
37111042SErik.Nordmark@Sun.COM 			list_remove(&ncec->ncec_cb, ncec_cb);
3729175SSowmini.Varadhan@Sun.COM 		} else {
3739175SSowmini.Varadhan@Sun.COM 			ip2m_wdata->ip2m_cancel_err = EBUSY;
3749175SSowmini.Varadhan@Sun.COM 		}
3759175SSowmini.Varadhan@Sun.COM 		break;
3769175SSowmini.Varadhan@Sun.COM 	}
37711042SErik.Nordmark@Sun.COM 	mutex_exit(&ncec->ncec_lock);
3789175SSowmini.Varadhan@Sun.COM 	return (0);
3799175SSowmini.Varadhan@Sun.COM }
3809175SSowmini.Varadhan@Sun.COM 
3819175SSowmini.Varadhan@Sun.COM /*
3829175SSowmini.Varadhan@Sun.COM  * cancel an outstanding timeout set up via ip2mac
3839175SSowmini.Varadhan@Sun.COM  */
3849175SSowmini.Varadhan@Sun.COM int
ip2mac_cancel(ip2mac_id_t ip2mid,zoneid_t zoneid)3859175SSowmini.Varadhan@Sun.COM ip2mac_cancel(ip2mac_id_t ip2mid, zoneid_t zoneid)
3869175SSowmini.Varadhan@Sun.COM {
3879175SSowmini.Varadhan@Sun.COM 	netstack_t	*ns;
3889175SSowmini.Varadhan@Sun.COM 	ip_stack_t	*ipst;
3899175SSowmini.Varadhan@Sun.COM 	ip2mac_cancel_data_t ip2m_wdata;
3909175SSowmini.Varadhan@Sun.COM 
3919175SSowmini.Varadhan@Sun.COM 	ns = netstack_find_by_zoneid(zoneid);
3929175SSowmini.Varadhan@Sun.COM 	if (ns == NULL) {
3939175SSowmini.Varadhan@Sun.COM 		ip2m_wdata.ip2m_cancel_err = EINVAL;
3949175SSowmini.Varadhan@Sun.COM 		return (ip2m_wdata.ip2m_cancel_err);
3959175SSowmini.Varadhan@Sun.COM 	}
3969175SSowmini.Varadhan@Sun.COM 	/*
3979175SSowmini.Varadhan@Sun.COM 	 * For exclusive stacks we reset the zoneid to zero
3989175SSowmini.Varadhan@Sun.COM 	 * since IP uses the global zoneid in the exclusive stacks.
3999175SSowmini.Varadhan@Sun.COM 	 */
4009175SSowmini.Varadhan@Sun.COM 	if (ns->netstack_stackid != GLOBAL_NETSTACKID)
4019175SSowmini.Varadhan@Sun.COM 		zoneid = GLOBAL_ZONEID;
4029175SSowmini.Varadhan@Sun.COM 	ipst = ns->netstack_ip;
4039175SSowmini.Varadhan@Sun.COM 
4049175SSowmini.Varadhan@Sun.COM 	ip2m_wdata.ip2m_cancel_id = ip2mid;
4059175SSowmini.Varadhan@Sun.COM 	ip2m_wdata.ip2m_cancel_err = 0;
40611042SErik.Nordmark@Sun.COM 	ncec_walk(NULL, ip2mac_cancel_callback, &ip2m_wdata, ipst);
4079175SSowmini.Varadhan@Sun.COM 	/*
4089175SSowmini.Varadhan@Sun.COM 	 * We may return EBUSY if a walk to dispatch callbacks is
4099175SSowmini.Varadhan@Sun.COM 	 * in progress, in which case the caller needs to synchronize
4109175SSowmini.Varadhan@Sun.COM 	 * with the registered callback function to make sure the
4119175SSowmini.Varadhan@Sun.COM 	 * module does not exit when there is a callback pending.
4129175SSowmini.Varadhan@Sun.COM 	 */
4139175SSowmini.Varadhan@Sun.COM 	netstack_rele(ns);
4149175SSowmini.Varadhan@Sun.COM 	return (ip2m_wdata.ip2m_cancel_err);
4159175SSowmini.Varadhan@Sun.COM }
416