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); }
/* * 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); }
__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); }