xref: /illumos-gate/usr/src/uts/common/inet/ip/ip2mac.c (revision 8a06b3d6)
14b7cbb46SSowmini Varadhan /*
24b7cbb46SSowmini Varadhan  * CDDL HEADER START
34b7cbb46SSowmini Varadhan  *
44b7cbb46SSowmini Varadhan  * The contents of this file are subject to the terms of the
54b7cbb46SSowmini Varadhan  * Common Development and Distribution License (the "License").
64b7cbb46SSowmini Varadhan  * You may not use this file except in compliance with the License.
74b7cbb46SSowmini Varadhan  *
84b7cbb46SSowmini Varadhan  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94b7cbb46SSowmini Varadhan  * or http://www.opensolaris.org/os/licensing.
104b7cbb46SSowmini Varadhan  * See the License for the specific language governing permissions
114b7cbb46SSowmini Varadhan  * and limitations under the License.
124b7cbb46SSowmini Varadhan  *
134b7cbb46SSowmini Varadhan  * When distributing Covered Code, include this CDDL HEADER in each
144b7cbb46SSowmini Varadhan  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154b7cbb46SSowmini Varadhan  * If applicable, add the following below this CDDL HEADER, with the
164b7cbb46SSowmini Varadhan  * fields enclosed by brackets "[]" replaced with your own identifying
174b7cbb46SSowmini Varadhan  * information: Portions Copyright [yyyy] [name of copyright owner]
184b7cbb46SSowmini Varadhan  *
194b7cbb46SSowmini Varadhan  * CDDL HEADER END
204b7cbb46SSowmini Varadhan  */
21bd670b35SErik Nordmark 
224b7cbb46SSowmini Varadhan /*
234b7cbb46SSowmini Varadhan  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
244b7cbb46SSowmini Varadhan  * Use is subject to license terms.
254b7cbb46SSowmini Varadhan  */
264b7cbb46SSowmini Varadhan 
274b7cbb46SSowmini Varadhan /*
284b7cbb46SSowmini Varadhan  * Functions to implement IP address -> link layer address (PSARC 2006/482)
294b7cbb46SSowmini Varadhan  */
304b7cbb46SSowmini Varadhan #include <inet/ip2mac.h>
314b7cbb46SSowmini Varadhan #include <inet/ip2mac_impl.h>
324b7cbb46SSowmini Varadhan #include <sys/zone.h>
334b7cbb46SSowmini Varadhan #include <inet/ip_ndp.h>
344b7cbb46SSowmini Varadhan #include <inet/ip_if.h>
354b7cbb46SSowmini Varadhan #include <inet/ip6.h>
364b7cbb46SSowmini Varadhan 
374b7cbb46SSowmini Varadhan /*
384b7cbb46SSowmini Varadhan  * dispatch pending callbacks.
394b7cbb46SSowmini Varadhan  */
404b7cbb46SSowmini Varadhan void
ncec_cb_dispatch(ncec_t * ncec)41bd670b35SErik Nordmark ncec_cb_dispatch(ncec_t *ncec)
424b7cbb46SSowmini Varadhan {
43bd670b35SErik Nordmark 	ncec_cb_t *ncec_cb;
444b7cbb46SSowmini Varadhan 	ip2mac_t ip2m;
454b7cbb46SSowmini Varadhan 
46bd670b35SErik Nordmark 	mutex_enter(&ncec->ncec_lock);
47bd670b35SErik Nordmark 	if (list_is_empty(&ncec->ncec_cb)) {
48bd670b35SErik Nordmark 		mutex_exit(&ncec->ncec_lock);
494b7cbb46SSowmini Varadhan 		return;
504b7cbb46SSowmini Varadhan 	}
51bd670b35SErik Nordmark 	ncec_ip2mac_response(&ip2m, ncec);
52bd670b35SErik Nordmark 	ncec_cb_refhold_locked(ncec);
534b7cbb46SSowmini Varadhan 	/*
544b7cbb46SSowmini Varadhan 	 * IP does not hold internal locks like nce_lock across calls to
554b7cbb46SSowmini Varadhan 	 * other subsystems for fear of recursive lock entry and lock
564b7cbb46SSowmini Varadhan 	 * hierarchy violation. The caller may be holding locks across
574b7cbb46SSowmini Varadhan 	 * the call to IP. (It would be ideal if no subsystem holds locks
584b7cbb46SSowmini Varadhan 	 * across calls into another subsystem, especially if calls can
594b7cbb46SSowmini Varadhan 	 * happen in either direction).
604b7cbb46SSowmini Varadhan 	 */
61bd670b35SErik Nordmark 	ncec_cb = list_head(&ncec->ncec_cb);
62bd670b35SErik Nordmark 	for (; ncec_cb != NULL; ncec_cb = list_next(&ncec->ncec_cb, ncec_cb)) {
63bd670b35SErik Nordmark 		if (ncec_cb->ncec_cb_flags & NCE_CB_DISPATCHED)
644b7cbb46SSowmini Varadhan 			continue;
65bd670b35SErik Nordmark 		ncec_cb->ncec_cb_flags |= NCE_CB_DISPATCHED;
66bd670b35SErik Nordmark 		mutex_exit(&ncec->ncec_lock);
67bd670b35SErik Nordmark 		(*ncec_cb->ncec_cb_func)(&ip2m, ncec_cb->ncec_cb_arg);
68bd670b35SErik Nordmark 		mutex_enter(&ncec->ncec_lock);
694b7cbb46SSowmini Varadhan 	}
70bd670b35SErik Nordmark 	ncec_cb_refrele(ncec);
71bd670b35SErik Nordmark 	mutex_exit(&ncec->ncec_lock);
724b7cbb46SSowmini Varadhan }
734b7cbb46SSowmini Varadhan 
744b7cbb46SSowmini Varadhan /*
754b7cbb46SSowmini Varadhan  * fill up the ip2m response fields with inforamation from the nce.
764b7cbb46SSowmini Varadhan  */
774b7cbb46SSowmini Varadhan void
ncec_ip2mac_response(ip2mac_t * ip2m,ncec_t * ncec)78bd670b35SErik Nordmark ncec_ip2mac_response(ip2mac_t *ip2m, ncec_t *ncec)
794b7cbb46SSowmini Varadhan {
80bd670b35SErik Nordmark 	boolean_t isv6 = (ncec->ncec_ipversion == IPV6_VERSION);
81bd670b35SErik Nordmark 	sin_t	*sin;
824b7cbb46SSowmini Varadhan 	sin6_t	*sin6;
834b7cbb46SSowmini Varadhan 	struct sockaddr_dl *sdl;
844b7cbb46SSowmini Varadhan 
85bd670b35SErik Nordmark 	ASSERT(MUTEX_HELD(&ncec->ncec_lock));
864b7cbb46SSowmini Varadhan 	bzero(ip2m, sizeof (*ip2m));
87bd670b35SErik Nordmark 	if (NCE_ISREACHABLE(ncec) && !NCE_ISCONDEMNED(ncec))
884b7cbb46SSowmini Varadhan 		ip2m->ip2mac_err = 0;
894b7cbb46SSowmini Varadhan 	else
904b7cbb46SSowmini Varadhan 		ip2m->ip2mac_err = ESRCH;
914b7cbb46SSowmini Varadhan 	if (isv6) {
924b7cbb46SSowmini Varadhan 		sin6 = (sin6_t *)&ip2m->ip2mac_pa;
934b7cbb46SSowmini Varadhan 		sin6->sin6_family = AF_INET6;
94bd670b35SErik Nordmark 		sin6->sin6_addr = ncec->ncec_addr;
95bd670b35SErik Nordmark 	} else {
96bd670b35SErik Nordmark 		sin = (sin_t *)&ip2m->ip2mac_pa;
97bd670b35SErik Nordmark 		sin->sin_family = AF_INET;
98bd670b35SErik Nordmark 		IN6_V4MAPPED_TO_INADDR(&ncec->ncec_addr, &sin->sin_addr);
994b7cbb46SSowmini Varadhan 	}
1004b7cbb46SSowmini Varadhan 	if (ip2m->ip2mac_err == 0) {
1014b7cbb46SSowmini Varadhan 		sdl = &ip2m->ip2mac_ha;
1024b7cbb46SSowmini Varadhan 		sdl->sdl_family = AF_LINK;
103bd670b35SErik Nordmark 		sdl->sdl_type = ncec->ncec_ill->ill_type;
104bd670b35SErik Nordmark 		/*
105bd670b35SErik Nordmark 		 * should we put ncec_ill->ill_name in there? why?
106bd670b35SErik Nordmark 		 * likewise for the sdl_index
107bd670b35SErik Nordmark 		 */
1084b7cbb46SSowmini Varadhan 		sdl->sdl_nlen = 0;
109bd670b35SErik Nordmark 		sdl->sdl_alen = ncec->ncec_ill->ill_phys_addr_length;
110bd670b35SErik Nordmark 		if (ncec->ncec_lladdr != NULL)
111bd670b35SErik Nordmark 			bcopy(ncec->ncec_lladdr, LLADDR(sdl), sdl->sdl_alen);
1124b7cbb46SSowmini Varadhan 	}
1134b7cbb46SSowmini Varadhan }
1144b7cbb46SSowmini Varadhan 
1154b7cbb46SSowmini Varadhan void
ncec_cb_refhold_locked(ncec_t * ncec)116bd670b35SErik Nordmark ncec_cb_refhold_locked(ncec_t *ncec)
1174b7cbb46SSowmini Varadhan {
118bd670b35SErik Nordmark 	ASSERT(MUTEX_HELD(&ncec->ncec_lock));
119bd670b35SErik Nordmark 	ncec->ncec_cb_walker_cnt++;
1204b7cbb46SSowmini Varadhan }
1214b7cbb46SSowmini Varadhan 
1224b7cbb46SSowmini Varadhan void
ncec_cb_refrele(ncec_t * ncec)123bd670b35SErik Nordmark ncec_cb_refrele(ncec_t *ncec)
1244b7cbb46SSowmini Varadhan {
125bd670b35SErik Nordmark 	ncec_cb_t *ncec_cb, *ncec_cb_next = NULL;
1264b7cbb46SSowmini Varadhan 
127bd670b35SErik Nordmark 	ASSERT(MUTEX_HELD(&ncec->ncec_lock));
128bd670b35SErik Nordmark 	if (--ncec->ncec_cb_walker_cnt == 0) {
129bd670b35SErik Nordmark 		for (ncec_cb = list_head(&ncec->ncec_cb); ncec_cb != NULL;
130bd670b35SErik Nordmark 		    ncec_cb = ncec_cb_next) {
1314b7cbb46SSowmini Varadhan 
132bd670b35SErik Nordmark 			ncec_cb_next = list_next(&ncec->ncec_cb, ncec_cb);
133bd670b35SErik Nordmark 			if ((ncec_cb->ncec_cb_flags & NCE_CB_DISPATCHED) == 0)
1344b7cbb46SSowmini Varadhan 				continue;
135bd670b35SErik Nordmark 			list_remove(&ncec->ncec_cb, ncec_cb);
136bd670b35SErik Nordmark 			kmem_free(ncec_cb, sizeof (*ncec_cb));
1374b7cbb46SSowmini Varadhan 		}
1384b7cbb46SSowmini Varadhan 	}
1394b7cbb46SSowmini Varadhan }
1404b7cbb46SSowmini Varadhan 
1414b7cbb46SSowmini Varadhan /*
1424b7cbb46SSowmini Varadhan  * add a callback to the nce, so that the callback can be invoked
1434b7cbb46SSowmini Varadhan  * after address resolution succeeds/fails.
1444b7cbb46SSowmini Varadhan  */
1454b7cbb46SSowmini Varadhan static ip2mac_id_t
ncec_add_cb(ncec_t * ncec,ip2mac_callback_t * cb,void * cbarg)146bd670b35SErik Nordmark ncec_add_cb(ncec_t *ncec, ip2mac_callback_t *cb, void *cbarg)
1474b7cbb46SSowmini Varadhan {
148bd670b35SErik Nordmark 	ncec_cb_t	*nce_cb;
1494b7cbb46SSowmini Varadhan 	ip2mac_id_t	ip2mid = NULL;
1504b7cbb46SSowmini Varadhan 
151bd670b35SErik Nordmark 	ASSERT(MUTEX_HELD(&ncec->ncec_lock));
1524b7cbb46SSowmini Varadhan 	if ((nce_cb = kmem_zalloc(sizeof (*nce_cb), KM_NOSLEEP)) == NULL)
1534b7cbb46SSowmini Varadhan 		return (ip2mid);
154bd670b35SErik Nordmark 	nce_cb->ncec_cb_func = cb;
155bd670b35SErik Nordmark 	nce_cb->ncec_cb_arg = cbarg;
1564b7cbb46SSowmini Varadhan 	/*
157bd670b35SErik Nordmark 	 * We identify the ncec_cb_t during cancellation by the address
1584b7cbb46SSowmini Varadhan 	 * of the nce_cb_t itself, and, as a short-cut for eliminating
159bd670b35SErik Nordmark 	 * clear mismatches, only look in the callback list of ncec's
1604b7cbb46SSowmini Varadhan 	 * whose address is equal to the nce_cb_id.
1614b7cbb46SSowmini Varadhan 	 */
162bd670b35SErik Nordmark 	nce_cb->ncec_cb_id = ncec; /* no refs! just an address */
163bd670b35SErik Nordmark 	list_insert_tail(&ncec->ncec_cb, nce_cb);
164bd670b35SErik Nordmark 	ip2mid = ncec;  /* this is the id to be used in ip2mac_cancel */
1654b7cbb46SSowmini Varadhan 
1664b7cbb46SSowmini Varadhan 	return (nce_cb);
1674b7cbb46SSowmini Varadhan }
1684b7cbb46SSowmini Varadhan 
1694b7cbb46SSowmini Varadhan /*
1704b7cbb46SSowmini Varadhan  * Resolve an IP address to a link-layer address using the data-structures
1714b7cbb46SSowmini Varadhan  * defined in PSARC 2006/482. If the current link-layer address for the
1724b7cbb46SSowmini Varadhan  * IP address is not known, the state-machine for resolving the resolution
1734b7cbb46SSowmini Varadhan  * will be triggered, and the callback function (*cb) will be invoked after
1744b7cbb46SSowmini Varadhan  * the resolution completes.
1754b7cbb46SSowmini Varadhan  */
1764b7cbb46SSowmini Varadhan ip2mac_id_t
ip2mac(uint_t op,ip2mac_t * ip2m,ip2mac_callback_t * cb,void * cbarg,zoneid_t zoneid)177bd670b35SErik Nordmark ip2mac(uint_t op, ip2mac_t *ip2m, ip2mac_callback_t *cb, void *cbarg,
1784b7cbb46SSowmini Varadhan     zoneid_t zoneid)
1794b7cbb46SSowmini Varadhan {
180bd670b35SErik Nordmark 	ncec_t		*ncec;
181bd670b35SErik Nordmark 	nce_t		*nce = NULL;
1824b7cbb46SSowmini Varadhan 	boolean_t	isv6;
1834b7cbb46SSowmini Varadhan 	ill_t		*ill;
1844b7cbb46SSowmini Varadhan 	netstack_t	*ns;
1854b7cbb46SSowmini Varadhan 	ip_stack_t	*ipst;
1864b7cbb46SSowmini Varadhan 	ip2mac_id_t	ip2mid = NULL;
187bd670b35SErik Nordmark 	sin_t		*sin;
1884b7cbb46SSowmini Varadhan 	sin6_t		*sin6;
1894b7cbb46SSowmini Varadhan 	int		err;
1904b7cbb46SSowmini Varadhan 	uint64_t	delta;
191bd670b35SErik Nordmark 	boolean_t	need_resolve = B_FALSE;
1924b7cbb46SSowmini Varadhan 
1934b7cbb46SSowmini Varadhan 	isv6 = (ip2m->ip2mac_pa.ss_family == AF_INET6);
1944b7cbb46SSowmini Varadhan 
1954b7cbb46SSowmini Varadhan 	ns = netstack_find_by_zoneid(zoneid);
1964b7cbb46SSowmini Varadhan 	if (ns == NULL) {
1974b7cbb46SSowmini Varadhan 		ip2m->ip2mac_err = EINVAL;
1984b7cbb46SSowmini Varadhan 		return (NULL);
1994b7cbb46SSowmini Varadhan 	}
2004b7cbb46SSowmini Varadhan 	/*
2014b7cbb46SSowmini Varadhan 	 * For exclusive stacks we reset the zoneid to zero
2024b7cbb46SSowmini Varadhan 	 * since IP uses the global zoneid in the exclusive stacks.
2034b7cbb46SSowmini Varadhan 	 */
2044b7cbb46SSowmini Varadhan 	if (ns->netstack_stackid != GLOBAL_NETSTACKID)
2054b7cbb46SSowmini Varadhan 		zoneid = GLOBAL_ZONEID;
2064b7cbb46SSowmini Varadhan 	ipst = ns->netstack_ip;
2074b7cbb46SSowmini Varadhan 	/*
2084b7cbb46SSowmini Varadhan 	 * find the ill from the ip2m->ip2mac_ifindex
2094b7cbb46SSowmini Varadhan 	 */
210bd670b35SErik Nordmark 	ill = ill_lookup_on_ifindex(ip2m->ip2mac_ifindex, isv6, ipst);
2114b7cbb46SSowmini Varadhan 	if (ill == NULL) {
2124b7cbb46SSowmini Varadhan 		ip2m->ip2mac_err = ENXIO;
2134b7cbb46SSowmini Varadhan 		netstack_rele(ns);
2144b7cbb46SSowmini Varadhan 		return (NULL);
2154b7cbb46SSowmini Varadhan 	}
2164b7cbb46SSowmini Varadhan 	if (isv6) {
2174b7cbb46SSowmini Varadhan 		sin6 = (sin6_t *)&ip2m->ip2mac_pa;
218bd670b35SErik Nordmark 		if (op == IP2MAC_LOOKUP) {
219bd670b35SErik Nordmark 			nce = nce_lookup_v6(ill, &sin6->sin6_addr);
2204b7cbb46SSowmini Varadhan 		} else {
221bd670b35SErik Nordmark 			err = nce_lookup_then_add_v6(ill, NULL,
222bd670b35SErik Nordmark 			    ill->ill_phys_addr_length,
223bd670b35SErik Nordmark 			    &sin6->sin6_addr, 0, ND_UNCHANGED, &nce);
2244b7cbb46SSowmini Varadhan 		}
2254b7cbb46SSowmini Varadhan 	} else  {
226bd670b35SErik Nordmark 		sin = (sin_t *)&ip2m->ip2mac_pa;
227bd670b35SErik Nordmark 		if (op == IP2MAC_LOOKUP) {
228bd670b35SErik Nordmark 			nce = nce_lookup_v4(ill, &sin->sin_addr.s_addr);
229bd670b35SErik Nordmark 		} else {
230bd670b35SErik Nordmark 			err = nce_lookup_then_add_v4(ill, NULL,
231bd670b35SErik Nordmark 			    ill->ill_phys_addr_length,
232bd670b35SErik Nordmark 			    &sin->sin_addr.s_addr, 0, ND_UNCHANGED, &nce);
233bd670b35SErik Nordmark 		}
2344b7cbb46SSowmini Varadhan 	}
235bd670b35SErik Nordmark 	if (op == IP2MAC_LOOKUP) {
2364b7cbb46SSowmini Varadhan 		if (nce == NULL) {
2374b7cbb46SSowmini Varadhan 			ip2m->ip2mac_err = ESRCH;
2384b7cbb46SSowmini Varadhan 			goto done;
2394b7cbb46SSowmini Varadhan 		}
240bd670b35SErik Nordmark 		ncec = nce->nce_common;
241d3d50737SRafael Vanoni 		delta = TICK_TO_MSEC(ddi_get_lbolt64()) - ncec->ncec_last;
242bd670b35SErik Nordmark 		mutex_enter(&ncec->ncec_lock);
243bd670b35SErik Nordmark 		if (NCE_ISREACHABLE(ncec) &&
244bd670b35SErik Nordmark 		    delta < (uint64_t)ill->ill_reachable_time) {
245bd670b35SErik Nordmark 			ncec_ip2mac_response(ip2m, ncec);
2464b7cbb46SSowmini Varadhan 			ip2m->ip2mac_err = 0;
2474b7cbb46SSowmini Varadhan 		} else {
2484b7cbb46SSowmini Varadhan 			ip2m->ip2mac_err = ESRCH;
2494b7cbb46SSowmini Varadhan 		}
250bd670b35SErik Nordmark 		mutex_exit(&ncec->ncec_lock);
2514b7cbb46SSowmini Varadhan 		goto done;
2524b7cbb46SSowmini Varadhan 	} else {
2534b7cbb46SSowmini Varadhan 		if (err != 0 && err != EEXIST) {
2544b7cbb46SSowmini Varadhan 			ip2m->ip2mac_err = err;
2554b7cbb46SSowmini Varadhan 			goto done;
2564b7cbb46SSowmini Varadhan 		}
2574b7cbb46SSowmini Varadhan 	}
258bd670b35SErik Nordmark 	ncec = nce->nce_common;
259d3d50737SRafael Vanoni 	delta = TICK_TO_MSEC(ddi_get_lbolt64()) - ncec->ncec_last;
260bd670b35SErik Nordmark 	mutex_enter(&ncec->ncec_lock);
261bd670b35SErik Nordmark 	if (NCE_ISCONDEMNED(ncec)) {
2624b7cbb46SSowmini Varadhan 		ip2m->ip2mac_err = ESRCH;
263bd670b35SErik Nordmark 	} else {
264bd670b35SErik Nordmark 		if (NCE_ISREACHABLE(ncec)) {
265bd670b35SErik Nordmark 			if (NCE_MYADDR(ncec) ||
266bd670b35SErik Nordmark 			    delta < (uint64_t)ill->ill_reachable_time) {
267bd670b35SErik Nordmark 				ncec_ip2mac_response(ip2m, ncec);
268bd670b35SErik Nordmark 				ip2m->ip2mac_err = 0;
269bd670b35SErik Nordmark 				mutex_exit(&ncec->ncec_lock);
270bd670b35SErik Nordmark 				goto done;
271bd670b35SErik Nordmark 			}
2724b7cbb46SSowmini Varadhan 			/*
2734b7cbb46SSowmini Varadhan 			 * Since we do not control the packet output
2744b7cbb46SSowmini Varadhan 			 * path for ip2mac() callers, we need to verify
2754b7cbb46SSowmini Varadhan 			 * if the existing information in the nce is
2764b7cbb46SSowmini Varadhan 			 * very old, and retrigger resolution if necessary.
2774b7cbb46SSowmini Varadhan 			 * We will not return the existing stale
2784b7cbb46SSowmini Varadhan 			 * information until it is verified through a
2794b7cbb46SSowmini Varadhan 			 * resolver request/response exchange.
2804b7cbb46SSowmini Varadhan 			 *
2814b7cbb46SSowmini Varadhan 			 * In the future, we may want to support extensions
2824b7cbb46SSowmini Varadhan 			 * that do additional callbacks on link-layer updates,
2834b7cbb46SSowmini Varadhan 			 * so that we can return the stale information but
2844b7cbb46SSowmini Varadhan 			 * also update the caller if the lladdr changes.
2854b7cbb46SSowmini Varadhan 			 */
286bd670b35SErik Nordmark 			ncec->ncec_rcnt = ill->ill_xmit_count;
287bd670b35SErik Nordmark 			ncec->ncec_state = ND_PROBE;
288bd670b35SErik Nordmark 			need_resolve = B_TRUE; /* reachable but very old nce */
289bd670b35SErik Nordmark 		} else if (ncec->ncec_state == ND_INITIAL) {
290bd670b35SErik Nordmark 			need_resolve = B_TRUE; /* ND_INITIAL nce */
291bd670b35SErik Nordmark 			ncec->ncec_state = ND_INCOMPLETE;
2924b7cbb46SSowmini Varadhan 		}
293bd670b35SErik Nordmark 		/*
294bd670b35SErik Nordmark 		 * NCE not known to be reachable in the recent past. We must
295bd670b35SErik Nordmark 		 * reconfirm the information before returning it to the caller
296bd670b35SErik Nordmark 		 */
297bd670b35SErik Nordmark 		if (ncec->ncec_rcnt > 0) {
2984b7cbb46SSowmini Varadhan 			/*
299bd670b35SErik Nordmark 			 * Still resolving this ncec, so we can queue the
300bd670b35SErik Nordmark 			 * callback information in ncec->ncec_cb
3014b7cbb46SSowmini Varadhan 			 */
302bd670b35SErik Nordmark 			ip2mid = ncec_add_cb(ncec, cb, cbarg);
3034b7cbb46SSowmini Varadhan 			ip2m->ip2mac_err = EINPROGRESS;
3044b7cbb46SSowmini Varadhan 		} else {
3054b7cbb46SSowmini Varadhan 			/*
306bd670b35SErik Nordmark 			 * No more retransmits allowed -- resolution failed.
3074b7cbb46SSowmini Varadhan 			 */
3084b7cbb46SSowmini Varadhan 			ip2m->ip2mac_err = ESRCH;
3094b7cbb46SSowmini Varadhan 		}
3104b7cbb46SSowmini Varadhan 	}
311bd670b35SErik Nordmark 	mutex_exit(&ncec->ncec_lock);
3124b7cbb46SSowmini Varadhan done:
313bd670b35SErik Nordmark 	/*
314bd670b35SErik Nordmark 	 * if NCE_ISREACHABLE(ncec) but very old, or if it is ND_INITIAL,
315bd670b35SErik Nordmark 	 * trigger resolve.
316bd670b35SErik Nordmark 	 */
317bd670b35SErik Nordmark 	if (need_resolve)
318bd670b35SErik Nordmark 		ip_ndp_resolve(ncec);
319bd670b35SErik Nordmark 	if (nce != NULL)
320bd670b35SErik Nordmark 		nce_refrele(nce);
3214b7cbb46SSowmini Varadhan 	netstack_rele(ns);
3224b7cbb46SSowmini Varadhan 	ill_refrele(ill);
3234b7cbb46SSowmini Varadhan 	return (ip2mid);
3244b7cbb46SSowmini Varadhan }
3254b7cbb46SSowmini Varadhan 
3264b7cbb46SSowmini Varadhan /*
327bd670b35SErik Nordmark  * data passed to ncec_walk for canceling outstanding callbacks.
3284b7cbb46SSowmini Varadhan  */
3294b7cbb46SSowmini Varadhan typedef struct ip2mac_cancel_data_s {
3304b7cbb46SSowmini Varadhan 	ip2mac_id_t ip2m_cancel_id;
3314b7cbb46SSowmini Varadhan 	int	ip2m_cancel_err;
3324b7cbb46SSowmini Varadhan } ip2mac_cancel_data_t;
3334b7cbb46SSowmini Varadhan 
3344b7cbb46SSowmini Varadhan /*
335bd670b35SErik Nordmark  * callback invoked for each active ncec. If the ip2mac_id_t corresponds
336bd670b35SErik Nordmark  * to an active nce_cb_t in the ncec's callback list, we want to remove
3374b7cbb46SSowmini Varadhan  * the callback (if there are no walkers) or return EBUSY to the caller
3384b7cbb46SSowmini Varadhan  */
339*8a06b3d6SToomas Soome static void
ip2mac_cancel_callback(ncec_t * ncec,void * arg)340bd670b35SErik Nordmark ip2mac_cancel_callback(ncec_t *ncec, void *arg)
3414b7cbb46SSowmini Varadhan {
3424b7cbb46SSowmini Varadhan 	ip2mac_cancel_data_t *ip2m_wdata = arg;
343bd670b35SErik Nordmark 	ncec_cb_t *ip2m_nce_cb = ip2m_wdata->ip2m_cancel_id;
344bd670b35SErik Nordmark 	ncec_cb_t *ncec_cb;
3454b7cbb46SSowmini Varadhan 
346bd670b35SErik Nordmark 	if (ip2m_nce_cb->ncec_cb_id != ncec)
347*8a06b3d6SToomas Soome 		return;
3484b7cbb46SSowmini Varadhan 
349bd670b35SErik Nordmark 	mutex_enter(&ncec->ncec_lock);
350bd670b35SErik Nordmark 	if (list_is_empty(&ncec->ncec_cb)) {
351bd670b35SErik Nordmark 		mutex_exit(&ncec->ncec_lock);
352*8a06b3d6SToomas Soome 		return;
3534b7cbb46SSowmini Varadhan 	}
3544b7cbb46SSowmini Varadhan 	/*
3554b7cbb46SSowmini Varadhan 	 * IP does not hold internal locks like nce_lock across calls to
3564b7cbb46SSowmini Varadhan 	 * other subsystems for fear of recursive lock entry and lock
3574b7cbb46SSowmini Varadhan 	 * hierarchy violation. The caller may be holding locks across
3584b7cbb46SSowmini Varadhan 	 * the call to IP. (It would be ideal if no subsystem holds locks
3594b7cbb46SSowmini Varadhan 	 * across calls into another subsystem, especially if calls can
3604b7cbb46SSowmini Varadhan 	 * happen in either direction).
3614b7cbb46SSowmini Varadhan 	 */
362bd670b35SErik Nordmark 	ncec_cb = list_head(&ncec->ncec_cb);
363bd670b35SErik Nordmark 	for (; ncec_cb != NULL; ncec_cb = list_next(&ncec->ncec_cb, ncec_cb)) {
364bd670b35SErik Nordmark 		if (ncec_cb != ip2m_nce_cb)
3654b7cbb46SSowmini Varadhan 			continue;
3664b7cbb46SSowmini Varadhan 		/*
3674b7cbb46SSowmini Varadhan 		 * If there are no walkers we can remove the nce_cb.
3684b7cbb46SSowmini Varadhan 		 * Otherwise the exiting walker will clean up.
3694b7cbb46SSowmini Varadhan 		 */
370bd670b35SErik Nordmark 		if (ncec->ncec_cb_walker_cnt == 0) {
371bd670b35SErik Nordmark 			list_remove(&ncec->ncec_cb, ncec_cb);
3724b7cbb46SSowmini Varadhan 		} else {
3734b7cbb46SSowmini Varadhan 			ip2m_wdata->ip2m_cancel_err = EBUSY;
3744b7cbb46SSowmini Varadhan 		}
3754b7cbb46SSowmini Varadhan 		break;
3764b7cbb46SSowmini Varadhan 	}
377bd670b35SErik Nordmark 	mutex_exit(&ncec->ncec_lock);
3784b7cbb46SSowmini Varadhan }
3794b7cbb46SSowmini Varadhan 
3804b7cbb46SSowmini Varadhan /*
3814b7cbb46SSowmini Varadhan  * cancel an outstanding timeout set up via ip2mac
3824b7cbb46SSowmini Varadhan  */
3834b7cbb46SSowmini Varadhan int
ip2mac_cancel(ip2mac_id_t ip2mid,zoneid_t zoneid)3844b7cbb46SSowmini Varadhan ip2mac_cancel(ip2mac_id_t ip2mid, zoneid_t zoneid)
3854b7cbb46SSowmini Varadhan {
3864b7cbb46SSowmini Varadhan 	netstack_t	*ns;
3874b7cbb46SSowmini Varadhan 	ip_stack_t	*ipst;
3884b7cbb46SSowmini Varadhan 	ip2mac_cancel_data_t ip2m_wdata;
3894b7cbb46SSowmini Varadhan 
3904b7cbb46SSowmini Varadhan 	ns = netstack_find_by_zoneid(zoneid);
3914b7cbb46SSowmini Varadhan 	if (ns == NULL) {
3924b7cbb46SSowmini Varadhan 		ip2m_wdata.ip2m_cancel_err = EINVAL;
3934b7cbb46SSowmini Varadhan 		return (ip2m_wdata.ip2m_cancel_err);
3944b7cbb46SSowmini Varadhan 	}
3954b7cbb46SSowmini Varadhan 	/*
3964b7cbb46SSowmini Varadhan 	 * For exclusive stacks we reset the zoneid to zero
3974b7cbb46SSowmini Varadhan 	 * since IP uses the global zoneid in the exclusive stacks.
3984b7cbb46SSowmini Varadhan 	 */
3994b7cbb46SSowmini Varadhan 	if (ns->netstack_stackid != GLOBAL_NETSTACKID)
4004b7cbb46SSowmini Varadhan 		zoneid = GLOBAL_ZONEID;
4014b7cbb46SSowmini Varadhan 	ipst = ns->netstack_ip;
4024b7cbb46SSowmini Varadhan 
4034b7cbb46SSowmini Varadhan 	ip2m_wdata.ip2m_cancel_id = ip2mid;
4044b7cbb46SSowmini Varadhan 	ip2m_wdata.ip2m_cancel_err = 0;
405bd670b35SErik Nordmark 	ncec_walk(NULL, ip2mac_cancel_callback, &ip2m_wdata, ipst);
4064b7cbb46SSowmini Varadhan 	/*
4074b7cbb46SSowmini Varadhan 	 * We may return EBUSY if a walk to dispatch callbacks is
4084b7cbb46SSowmini Varadhan 	 * in progress, in which case the caller needs to synchronize
4094b7cbb46SSowmini Varadhan 	 * with the registered callback function to make sure the
4104b7cbb46SSowmini Varadhan 	 * module does not exit when there is a callback pending.
4114b7cbb46SSowmini Varadhan 	 */
4124b7cbb46SSowmini Varadhan 	netstack_rele(ns);
4134b7cbb46SSowmini Varadhan 	return (ip2m_wdata.ip2m_cancel_err);
4144b7cbb46SSowmini Varadhan }
415