Exemple #1
0
static struct radix_node *
in6_deleteroute(void * v_arg, void *netmask_arg, struct radix_node_head *head)
{
	struct radix_node *rn;

	lck_mtx_assert(rnh_lock, LCK_MTX_ASSERT_OWNED);

	rn = rn_delete(v_arg, netmask_arg, head);
	if (rn != NULL) {
		struct rtentry *rt = (struct rtentry *)rn;
		RT_LOCK_SPIN(rt);
		if ((rt->rt_flags & RTF_DYNAMIC) != 0)
			in6dynroutes--;
		RT_UNLOCK(rt);
	}

	return (rn);
}
Exemple #2
0
/*
 * This code is the inverse of in6_clsroute: on first reference, if we
 * were managing the route, stop doing so and set the expiration timer
 * back off again.
 */
static struct radix_node *
in6_matroute_args(void *v_arg, struct radix_node_head *head,
    rn_matchf_t *f, void *w)
{
	struct radix_node *rn = rn_match_args(v_arg, head, f, w);
	struct rtentry *rt = (struct rtentry *)rn;

	/* This is first reference? */
	if (rt != NULL) {
		RT_LOCK_SPIN(rt);
		if (rt->rt_refcnt == 0 && (rt->rt_flags & RTPRF_OURS)) {
			rt->rt_flags &= ~RTPRF_OURS;
			rt->rt_rmx.rmx_expire = 0;
		}
		RT_UNLOCK(rt);
	}
	return (rn);
}
Exemple #3
0
__private_extern__ errno_t
arp_route_to_gateway_route(const struct sockaddr *net_dest, route_t hint0,
     route_t *out_route)
{
	struct timeval timenow;
	route_t rt = hint0, hint = hint0;
	errno_t error = 0;

	*out_route = NULL;

	/*
	 * Next hop determination.  Because we may involve the gateway route
	 * in addition to the original route, locking is rather complicated.
	 * The general concept is that regardless of whether the route points
	 * to the original route or to the gateway route, this routine takes
	 * an extra reference on such a route.  This extra reference will be
	 * released at the end.
	 *
	 * Care must be taken to ensure that the "hint0" route never gets freed
	 * via rtfree(), since the caller may have stored it inside a struct
	 * route with a reference held for that placeholder.
	 */
	if (rt != NULL) {
		unsigned int ifindex;

		RT_LOCK_SPIN(rt);
		ifindex = rt->rt_ifp->if_index;
		RT_ADDREF_LOCKED(rt);
		if (!(rt->rt_flags & RTF_UP)) {
			RT_REMREF_LOCKED(rt);
			RT_UNLOCK(rt);
			/* route is down, find a new one */
			hint = rt = rtalloc1_scoped((struct sockaddr *)
			    (size_t)net_dest, 1, 0, ifindex);
			if (hint != NULL) {
				RT_LOCK_SPIN(rt);
				ifindex = rt->rt_ifp->if_index;
			} else {
				senderr(EHOSTUNREACH);
			}
		}

		/*
		 * We have a reference to "rt" by now; it will either
		 * be released or freed at the end of this routine.
		 */
		RT_LOCK_ASSERT_HELD(rt);
		if (rt->rt_flags & RTF_GATEWAY) {
			struct rtentry *gwrt = rt->rt_gwroute;
			struct sockaddr_in gw;

			/* If there's no gateway rt, look it up */
			if (gwrt == NULL) {
				gw = *((struct sockaddr_in *)rt->rt_gateway);
				RT_UNLOCK(rt);
				goto lookup;
			}
			/* Become a regular mutex */
			RT_CONVERT_LOCK(rt);

			/*
			 * Take gwrt's lock while holding route's lock;
			 * this is okay since gwrt never points back
			 * to "rt", so no lock ordering issues.
			 */
			RT_LOCK_SPIN(gwrt);
			if (!(gwrt->rt_flags & RTF_UP)) {
				struct rtentry *ogwrt;

				rt->rt_gwroute = NULL;
				RT_UNLOCK(gwrt);
				gw = *((struct sockaddr_in *)rt->rt_gateway);
				RT_UNLOCK(rt);
				rtfree(gwrt);
lookup:
				gwrt = rtalloc1_scoped(
				    (struct sockaddr *)&gw, 1, 0, ifindex);

				RT_LOCK(rt);
				/*
				 * Bail out if the route is down, no route
				 * to gateway, circular route, or if the
				 * gateway portion of "rt" has changed.
				 */
				if (!(rt->rt_flags & RTF_UP) ||
				    gwrt == NULL || gwrt == rt ||
				    !equal(SA(&gw), rt->rt_gateway)) {
					if (gwrt == rt) {
						RT_REMREF_LOCKED(gwrt);
						gwrt = NULL;
					}
					RT_UNLOCK(rt);
					if (gwrt != NULL)
						rtfree(gwrt);
					senderr(EHOSTUNREACH);
				}

				/* Remove any existing gwrt */
				ogwrt = rt->rt_gwroute;
				if ((rt->rt_gwroute = gwrt) != NULL)
					RT_ADDREF(gwrt);

				/* Clean up "rt" now while we can */
				if (rt == hint0) {
					RT_REMREF_LOCKED(rt);
					RT_UNLOCK(rt);
				} else {
					RT_UNLOCK(rt);
					rtfree(rt);
				}
				rt = gwrt;
				/* Now free the replaced gwrt */
				if (ogwrt != NULL)
					rtfree(ogwrt);
				/* If still no route to gateway, bail out */
				if (rt == NULL)
					senderr(EHOSTUNREACH);
			} else {
				RT_ADDREF_LOCKED(gwrt);
				RT_UNLOCK(gwrt);
				/* Clean up "rt" now while we can */
				if (rt == hint0) {
					RT_REMREF_LOCKED(rt);
					RT_UNLOCK(rt);
				} else {
					RT_UNLOCK(rt);
					rtfree(rt);
				}
				rt = gwrt;
			}

			/* rt == gwrt; if it is now down, give up */
			RT_LOCK_SPIN(rt);
			if (!(rt->rt_flags & RTF_UP)) {
				RT_UNLOCK(rt);
				senderr(EHOSTUNREACH);
			}
		}

		if (rt->rt_flags & RTF_REJECT) {
			getmicrotime(&timenow);
			if (rt->rt_rmx.rmx_expire == 0 ||
			    timenow.tv_sec < rt->rt_rmx.rmx_expire) {
				RT_UNLOCK(rt);
				senderr(rt == hint ? EHOSTDOWN : EHOSTUNREACH);
			}
		}

		/* Become a regular mutex */
		RT_CONVERT_LOCK(rt);

		/* Caller is responsible for cleaning up "rt" */
		*out_route = rt;
	}
	return (0);

bad:
	/* Clean up route (either it is "rt" or "gwrt") */
	if (rt != NULL) {
		RT_LOCK_SPIN(rt);
		if (rt == hint0) {
			RT_REMREF_LOCKED(rt);
			RT_UNLOCK(rt);
		} else {
			RT_UNLOCK(rt);
			rtfree(rt);
		}
	}
	return (error);
}