X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/1c79356b52d46aa6b508fb032f5ae709b1f2897b..8ad349bb6ed4a0be06e34c92be0d98b92e078db4:/bsd/net/route.c diff --git a/bsd/net/route.c b/bsd/net/route.c index 22d8678d3..aac44e4dd 100644 --- a/bsd/net/route.c +++ b/bsd/net/route.c @@ -1,23 +1,31 @@ /* - * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. + * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved. * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. + * @APPLE_LICENSE_OSREFERENCE_HEADER_START@ * - * @APPLE_LICENSE_HEADER_END@ + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the + * License may not be used to create, or enable the creation or + * redistribution of, unlawful or unlicensed copies of an Apple operating + * system, or to circumvent, violate, or enable the circumvention or + * violation of, any terms of an Apple operating system software license + * agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_OSREFERENCE_HEADER_END@ */ /* * Copyright (c) 1980, 1986, 1991, 1993 @@ -52,19 +60,17 @@ * SUCH DAMAGE. * * @(#)route.c 8.2 (Berkeley) 11/15/93 + * $FreeBSD: src/sys/net/route.c,v 1.59.2.3 2001/07/29 19:18:02 ume Exp $ */ - -#if NOTFB31 -#include "opt_inet.h" -#include "opt_mrouting.h" -#endif - + #include #include #include #include #include #include +#include +#include #include #include @@ -72,17 +78,30 @@ #include #include +#include + #define SA(p) ((struct sockaddr *)(p)) +extern struct domain routedomain; struct route_cb route_cb; -static struct rtstat rtstat; +__private_extern__ struct rtstat rtstat = { 0, 0, 0, 0, 0 }; struct radix_node_head *rt_tables[AF_MAX+1]; -static int rttrash; /* routes not in table but not freed */ +lck_mtx_t *rt_mtx; /*### global routing tables mutex for now */ +lck_attr_t *rt_mtx_attr; +lck_grp_t *rt_mtx_grp; +lck_grp_attr_t *rt_mtx_grp_attr; + +lck_mtx_t *route_domain_mtx; /*### global routing tables mutex for now */ +__private_extern__ int rttrash = 0; /* routes not in table but not freed */ + +static void rt_maskedcopy(struct sockaddr *, + struct sockaddr *, struct sockaddr *); +static void rtable_init(void **); + +__private_extern__ u_long route_generation = 0; +extern int use_routegenid; -static void rt_maskedcopy __P((struct sockaddr *, - struct sockaddr *, struct sockaddr *)); -static void rtable_init __P((void **)); static void rtable_init(table) @@ -98,8 +117,26 @@ rtable_init(table) void route_init() { + rt_mtx_grp_attr = lck_grp_attr_alloc_init(); + + lck_grp_attr_setdefault(rt_mtx_grp_attr); + + rt_mtx_grp = lck_grp_alloc_init("route", rt_mtx_grp_attr); + + rt_mtx_attr = lck_attr_alloc_init(); + + lck_attr_setdefault(rt_mtx_attr); + + if ((rt_mtx = lck_mtx_alloc_init(rt_mtx_grp, rt_mtx_attr)) == NULL) { + printf("route_init: can't alloc rt_mtx\n"); + return; + } + + lck_mtx_lock(rt_mtx); rn_init(); /* initialize all zeroes, all ones, mask table */ + lck_mtx_unlock(rt_mtx); rtable_init((void **)rt_tables); + route_domain_mtx = routedomain.dom_mtx; } /* @@ -109,19 +146,36 @@ void rtalloc(ro) register struct route *ro; { - if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP)) - return; /* XXX */ - ro->ro_rt = rtalloc1(&ro->ro_dst, 1, 0UL); + rtalloc_ign(ro, 0UL); } +void +rtalloc_ign_locked(ro, ignore) + register struct route *ro; + u_long ignore; +{ + struct rtentry *rt; + + if ((rt = ro->ro_rt) != NULL) { + if (rt->rt_ifp != NULL && rt->rt_flags & RTF_UP) + return; + /* XXX - We are probably always at splnet here already. */ + rtfree_locked(rt); + ro->ro_rt = NULL; + } + ro->ro_rt = rtalloc1_locked(&ro->ro_dst, 1, ignore); + if (ro->ro_rt) + ro->ro_rt->generation_id = route_generation; +} void rtalloc_ign(ro, ignore) register struct route *ro; u_long ignore; { - if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP)) - return; /* XXX */ - ro->ro_rt = rtalloc1(&ro->ro_dst, 1, ignore); + lck_mtx_assert(rt_mtx, LCK_MTX_ASSERT_NOTOWNED); + lck_mtx_lock(rt_mtx); + rtalloc_ign_locked(ro, ignore); + lck_mtx_unlock(rt_mtx); } /* @@ -129,8 +183,8 @@ rtalloc_ign(ro, ignore) * Or, at least try.. Create a cloned route if needed. */ struct rtentry * -rtalloc1(dst, report, ignflags) - register struct sockaddr *dst; +rtalloc1_locked(dst, report, ignflags) + const struct sockaddr *dst; int report; u_long ignflags; { @@ -140,9 +194,8 @@ rtalloc1(dst, report, ignflags) struct rtentry *newrt = 0; struct rt_addrinfo info; u_long nflags; - int s = splnet(), err = 0, msgtype = RTM_MISS; - - /* + int err = 0, msgtype = RTM_MISS; + /* * Look up the address in the table for that Address Family */ if (rnh && (rn = rnh->rnh_matchaddr((caddr_t)dst, rnh)) && @@ -159,7 +212,7 @@ rtalloc1(dst, report, ignflags) * If it requires that it be cloned, do so. * (This implies it wasn't a HOST route.) */ - err = rtrequest(RTM_RESOLVE, dst, SA(0), + err = rtrequest_locked(RTM_RESOLVE, dst, SA(0), SA(0), 0, &newrt); if (err) { /* @@ -167,19 +220,19 @@ rtalloc1(dst, report, ignflags) * what we have will do. Return that. */ newrt = rt; - rt->rt_refcnt++; + rtref(rt); goto miss; } if ((rt = newrt) && (rt->rt_flags & RTF_XRESOLVE)) { /* - * If the new route specifies it be + * If the new route specifies it be * externally resolved, then go do that. */ msgtype = RTM_RESOLVE; goto miss; } } else - rt->rt_refcnt++; + rtref(rt); } else { /* * Either we hit the root or couldn't find any match, @@ -198,34 +251,53 @@ rtalloc1(dst, report, ignflags) rt_missmsg(msgtype, &info, 0, err); } } - splx(s); return (newrt); } +struct rtentry * +rtalloc1(dst, report, ignflags) + register struct sockaddr *dst; + int report; + u_long ignflags; +{ + struct rtentry * entry; + lck_mtx_assert(rt_mtx, LCK_MTX_ASSERT_NOTOWNED); + lck_mtx_lock(rt_mtx); + entry = rtalloc1_locked(dst, report, ignflags); + lck_mtx_unlock(rt_mtx); + return (entry); +} + /* * Remove a reference count from an rtentry. * If the count gets low enough, take it out of the routing table */ void -rtfree(rt) +rtfree_locked(rt) register struct rtentry *rt; { /* * find the tree for that address family + * Note: in the case of igmp packets, there might not be an rnh */ - register struct radix_node_head *rnh = - rt_tables[rt_key(rt)->sa_family]; - register struct ifaddr *ifa; + register struct radix_node_head *rnh; + + lck_mtx_assert(rt_mtx, LCK_MTX_ASSERT_OWNED); - if (rt == 0 || rnh == 0) - panic("rtfree"); + /* See 3582620 - We hit this during the transition from funnels to locks */ + if (rt == 0) { + printf("rtfree - rt is NULL\n"); + return; + } + + rnh = rt_tables[rt_key(rt)->sa_family]; /* * decrement the reference count by one and if it reaches 0, * and there is a close function defined, call the close function */ rt->rt_refcnt--; - if(rnh->rnh_close && rt->rt_refcnt == 0) { + if(rnh && rnh->rnh_close && rt->rt_refcnt == 0) { rnh->rnh_close((struct radix_node *)rt, rnh); } @@ -237,7 +309,7 @@ rtfree(rt) if (rt->rt_refcnt <= 0 && (rt->rt_flags & RTF_UP) == 0) { if (rt->rt_nodes->rn_flags & (RNF_ACTIVE | RNF_ROOT)) panic ("rtfree 2"); - /* + /* * the rtentry must have been removed from the routing table * so it is represented in rttrash.. remove that now. */ @@ -245,19 +317,21 @@ rtfree(rt) #ifdef DIAGNOSTIC if (rt->rt_refcnt < 0) { - printf("rtfree: %p not freed (neg refs)\n", rt); + printf("rtfree: %p not freed (neg refs) cnt=%d\n", rt, rt->rt_refcnt); return; } #endif - /* + /* * release references on items we hold them on.. * e.g other routes and ifaddrs. */ - if((ifa = rt->rt_ifa)) - IFAFREE(ifa); - if (rt->rt_parent) { - RTFREE(rt->rt_parent); + if (rt->rt_parent) + rtfree_locked(rt->rt_parent); + + if(rt->rt_ifa) { + ifafree(rt->rt_ifa); + rt->rt_ifa = NULL; } /* @@ -265,25 +339,107 @@ rtfree(rt) * This also frees the gateway, as they are always malloc'd * together. */ - Free(rt_key(rt)); + R_Free(rt_key(rt)); /* * and the rtentry itself of course */ - Free(rt); + R_Free(rt); } } +void +rtfree(rt) + register struct rtentry *rt; +{ + lck_mtx_assert(rt_mtx, LCK_MTX_ASSERT_NOTOWNED); + lck_mtx_lock(rt_mtx); + rtfree_locked(rt); + lck_mtx_unlock(rt_mtx); +} + +/* + * Decrements the refcount but does not free the route when + * the refcount reaches zero. Unless you have really good reason, + * use rtfree not rtunref. + */ +void +rtunref(struct rtentry* rt) +{ + lck_mtx_assert(rt_mtx, LCK_MTX_ASSERT_OWNED); + + if (rt == NULL) + panic("rtunref"); + rt->rt_refcnt--; +#if DEBUG + if (rt->rt_refcnt <= 0 && (rt->rt_flags & RTF_UP) == 0) + printf("rtunref - if rtfree were called, we would have freed route\n"); +#endif +} + +/* + * Add a reference count from an rtentry. + */ +void +rtref(struct rtentry* rt) +{ + lck_mtx_assert(rt_mtx, LCK_MTX_ASSERT_OWNED); + + if (rt == NULL) + panic("rtref"); + + rt->rt_refcnt++; +} + +void +rtsetifa(struct rtentry *rt, struct ifaddr* ifa) +{ + if (rt == NULL) + panic("rtsetifa"); + + if (rt->rt_ifa == ifa) + return; + + /* Release the old ifa */ + if (rt->rt_ifa) + ifafree(rt->rt_ifa); + + /* Set rt_ifa */ + rt->rt_ifa = ifa; + + /* Take a reference to the ifa */ + if (rt->rt_ifa) + ifaref(rt->rt_ifa); +} + void ifafree(ifa) register struct ifaddr *ifa; { + int i, oldval; + u_char *ptr = (u_char*)ifa; + if (ifa == NULL) panic("ifafree"); - if (ifa->ifa_refcnt == 0) + + oldval = OSAddAtomic(-1, &ifa->ifa_refcnt); + + if (oldval == 0) { + if ((ifa->ifa_flags & IFA_ATTACHED) != 0) { + panic("ifa attached to ifp is being freed\n"); + } FREE(ifa, M_IFADDR); - else - ifa->ifa_refcnt--; + } +} + +void +ifaref(struct ifaddr *ifa) +{ + if (ifa == NULL) + panic("ifaref"); + + if (OSAddAtomic(1, &ifa->ifa_refcnt) == 0xffffffff) + panic("ifaref - reference count rolled over!"); } /* @@ -305,14 +461,18 @@ rtredirect(dst, gateway, netmask, flags, src, rtp) int error = 0; short *stat = 0; struct rt_addrinfo info; - struct ifaddr *ifa; + struct ifaddr *ifa = NULL; + + lck_mtx_assert(rt_mtx, LCK_MTX_ASSERT_NOTOWNED); + lck_mtx_lock(rt_mtx); /* verify the gateway is directly reachable */ if ((ifa = ifa_ifwithnet(gateway)) == 0) { error = ENETUNREACH; goto out; } - rt = rtalloc1(dst, 0, 0UL); + + rt = rtalloc1_locked(dst, 0, 0UL); /* * If the redirect isn't from our current router for this dst, * it's either old or wrong. If it redirects us to ourselves, @@ -323,8 +483,20 @@ rtredirect(dst, gateway, netmask, flags, src, rtp) if (!(flags & RTF_DONE) && rt && (!equal(src, rt->rt_gateway) || rt->rt_ifa != ifa)) error = EINVAL; - else if (ifa_ifwithaddr(gateway)) - error = EHOSTUNREACH; + else { + ifafree(ifa); + if ((ifa = ifa_ifwithaddr(gateway))) { + ifafree(ifa); + ifa = NULL; + error = EHOSTUNREACH; + } + } + + if (ifa) { + ifafree(ifa); + ifa = NULL; + } + if (error) goto done; /* @@ -347,7 +519,7 @@ rtredirect(dst, gateway, netmask, flags, src, rtp) */ create: flags |= RTF_GATEWAY | RTF_DYNAMIC; - error = rtrequest((int)RTM_ADD, dst, gateway, + error = rtrequest_locked((int)RTM_ADD, dst, gateway, netmask, flags, (struct rtentry **)0); stat = &rtstat.rts_dynamic; @@ -371,7 +543,7 @@ done: if (rtp && !error) *rtp = rt; else - rtfree(rt); + rtfree_locked(rt); } out: if (error) @@ -384,6 +556,7 @@ out: info.rti_info[RTAX_NETMASK] = netmask; info.rti_info[RTAX_AUTHOR] = src; rt_missmsg(RTM_REDIRECT, &info, flags, error); + lck_mtx_unlock(rt_mtx); } /* @@ -408,11 +581,15 @@ rtioctl(req, data, p) } struct ifaddr * -ifa_ifwithroute(flags, dst, gateway) - int flags; - struct sockaddr *dst, *gateway; +ifa_ifwithroute( + int flags, + const struct sockaddr *dst, + const struct sockaddr *gateway) { - register struct ifaddr *ifa; + + lck_mtx_assert(rt_mtx, LCK_MTX_ASSERT_OWNED); + + struct ifaddr *ifa = 0; if ((flags & RTF_GATEWAY) == 0) { /* * If we are adding a route to an interface, @@ -421,7 +598,6 @@ ifa_ifwithroute(flags, dst, gateway) * as our clue to the interface. Otherwise * we can use the local address. */ - ifa = 0; if (flags & RTF_HOST) { ifa = ifa_ifwithdstaddr(dst); } @@ -438,18 +614,23 @@ ifa_ifwithroute(flags, dst, gateway) if (ifa == 0) ifa = ifa_ifwithnet(gateway); if (ifa == 0) { - struct rtentry *rt = rtalloc1(dst, 0, 0UL); + struct rtentry *rt = rtalloc1_locked(dst, 0, 0UL); if (rt == 0) return (0); - rt->rt_refcnt--; - if ((ifa = rt->rt_ifa) == 0) - return (0); + ifa = rt->rt_ifa; + if (ifa) + ifaref(ifa); + rtunref(rt); + if (ifa == 0) + return 0; } if (ifa->ifa_addr->sa_family != dst->sa_family) { - struct ifaddr *oifa = ifa; - ifa = ifaof_ifpforaddr(dst, ifa->ifa_ifp); - if (ifa == 0) - ifa = oifa; + struct ifaddr *newifa; + newifa = ifaof_ifpforaddr(dst, ifa->ifa_ifp); + if (newifa != 0) { + ifafree(ifa); + ifa = newifa; + } } return (ifa); } @@ -468,20 +649,23 @@ struct rtfc_arg { * Do appropriate manipulations of a routing tree given * all the bits of info needed */ -int -rtrequest(req, dst, gateway, netmask, flags, ret_nrt) - int req, flags; - struct sockaddr *dst, *gateway, *netmask; - struct rtentry **ret_nrt; +rtrequest_locked( + int req, + struct sockaddr *dst, + struct sockaddr *gateway, + struct sockaddr *netmask, + int flags, + struct rtentry **ret_nrt) { - int s = splnet(); int error = 0; + int error = 0; register struct rtentry *rt; register struct radix_node *rn; register struct radix_node_head *rnh; - struct ifaddr *ifa; + struct ifaddr *ifa = NULL; struct sockaddr *ndst; #define senderr(x) { error = x ; goto bad; } + lck_mtx_assert(rt_mtx, LCK_MTX_ASSERT_OWNED); /* * Find the correct routing tree to use for this Address Family */ @@ -509,8 +693,9 @@ rtrequest(req, dst, gateway, netmask, flags, ret_nrt) * Now search what's left of the subtree for any cloned * routes which might have been formed from this node. */ - if ((rt->rt_flags & RTF_PRCLONING) && netmask) { - rnh->rnh_walktree_from(rnh, dst, netmask, + if ((rt->rt_flags & (RTF_CLONING | RTF_PRCLONING)) && + rt_mask(rt)) { + rnh->rnh_walktree_from(rnh, dst, rt_mask(rt), rt_fixdelete, rt); } @@ -521,7 +706,7 @@ rtrequest(req, dst, gateway, netmask, flags, ret_nrt) */ if (rt->rt_gwroute) { rt = rt->rt_gwroute; - RTFREE(rt); + rtfree_locked(rt); (rt = (struct rtentry *)rn)->rt_gwroute = 0; } @@ -534,11 +719,12 @@ rtrequest(req, dst, gateway, netmask, flags, ret_nrt) */ rt->rt_flags &= ~RTF_UP; - /* + /* * give the protocol a chance to keep things in sync. */ if ((ifa = rt->rt_ifa) && ifa->ifa_rtrequest) ifa->ifa_rtrequest(RTM_DELETE, rt, SA(0)); + ifa = NULL; /* * one more rtentry floating around that is not @@ -554,8 +740,8 @@ rtrequest(req, dst, gateway, netmask, flags, ret_nrt) if (ret_nrt) *ret_nrt = rt; else if (rt->rt_refcnt <= 0) { - rt->rt_refcnt++; - rtfree(rt); + rt->rt_refcnt++; /* make a 1->0 transition */ + rtfree_locked(rt); } break; @@ -563,6 +749,7 @@ rtrequest(req, dst, gateway, netmask, flags, ret_nrt) if (ret_nrt == 0 || (rt = *ret_nrt) == 0) senderr(EINVAL); ifa = rt->rt_ifa; + ifaref(ifa); flags = rt->rt_flags & ~(RTF_CLONING | RTF_PRCLONING | RTF_STATIC); flags |= RTF_WASCLONED; @@ -588,8 +775,8 @@ rtrequest(req, dst, gateway, netmask, flags, ret_nrt) * Add the gateway. Possibly re-malloc-ing the storage for it * also add the rt_gwroute if possible. */ - if (error = rt_setgate(rt, dst, gateway)) { - Free(rt); + if ((error = rt_setgate(rt, dst, gateway)) != 0) { + R_Free(rt); senderr(error); } @@ -611,10 +798,11 @@ rtrequest(req, dst, gateway, netmask, flags, ret_nrt) * This moved from below so that rnh->rnh_addaddr() can * examine the ifa and ifa->ifa_ifp if it so desires. */ - ifa->ifa_refcnt++; - rt->rt_ifa = ifa; - rt->rt_ifp = ifa->ifa_ifp; - rt->rt_dlt = ifa->ifa_dlt; + rtsetifa(rt, ifa); + rt->rt_ifp = rt->rt_ifa->ifa_ifp; + + /* XXX mtu manipulation will be done in rnh_addaddr -- itojun */ + rn = rnh->rnh_addaddr((caddr_t)ndst, (caddr_t)netmask, rnh, rt->rt_nodes); if (rn == 0) { @@ -626,19 +814,19 @@ rtrequest(req, dst, gateway, netmask, flags, ret_nrt) * mechanism, then we just blow it away and retry * the insertion of the new one. */ - rt2 = rtalloc1(dst, 0, RTF_PRCLONING); + rt2 = rtalloc1_locked(dst, 0, RTF_PRCLONING); if (rt2 && rt2->rt_parent) { - rtrequest(RTM_DELETE, + rtrequest_locked(RTM_DELETE, (struct sockaddr *)rt_key(rt2), rt2->rt_gateway, rt_mask(rt2), rt2->rt_flags, 0); - RTFREE(rt2); + rtfree_locked(rt2); rn = rnh->rnh_addaddr((caddr_t)ndst, (caddr_t)netmask, rnh, rt->rt_nodes); } else if (rt2) { /* undo the extra ref we got */ - RTFREE(rt2); + rtfree_locked(rt2); } } @@ -648,27 +836,27 @@ rtrequest(req, dst, gateway, netmask, flags, ret_nrt) */ if (rn == 0) { if (rt->rt_gwroute) - rtfree(rt->rt_gwroute); + rtfree_locked(rt->rt_gwroute); if (rt->rt_ifa) { - IFAFREE(rt->rt_ifa); + ifafree(rt->rt_ifa); } - Free(rt_key(rt)); - Free(rt); + R_Free(rt_key(rt)); + R_Free(rt); senderr(EEXIST); } rt->rt_parent = 0; - /* + /* * If we got here from RESOLVE, then we are cloning - * so clone the rest, and note that we + * so clone the rest, and note that we * are a clone (and increment the parent's references) */ if (req == RTM_RESOLVE) { rt->rt_rmx = (*ret_nrt)->rt_rmx; /* copy metrics */ - if ((*ret_nrt)->rt_flags & RTF_PRCLONING) { + if ((*ret_nrt)->rt_flags & (RTF_CLONING | RTF_PRCLONING)) { rt->rt_parent = (*ret_nrt); - (*ret_nrt)->rt_refcnt++; + rtref(*ret_nrt); } } @@ -678,6 +866,8 @@ rtrequest(req, dst, gateway, netmask, flags, ret_nrt) */ if (ifa->ifa_rtrequest) ifa->ifa_rtrequest(req, rt, SA(ret_nrt ? *ret_nrt : 0)); + ifafree(ifa); + ifa = 0; /* * We repeat the same procedure from rt_setgate() here because @@ -698,15 +888,32 @@ rtrequest(req, dst, gateway, netmask, flags, ret_nrt) */ if (ret_nrt) { *ret_nrt = rt; - rt->rt_refcnt++; + rtref(rt); } break; } bad: - splx(s); + if (ifa) + ifafree(ifa); return (error); } +int +rtrequest( + int req, + struct sockaddr *dst, + struct sockaddr *gateway, + struct sockaddr *netmask, + int flags, + struct rtentry **ret_nrt) +{ + int error; + lck_mtx_assert(rt_mtx, LCK_MTX_ASSERT_NOTOWNED); + lck_mtx_lock(rt_mtx); + error = rtrequest_locked(req, dst, gateway, netmask, flags, ret_nrt); + lck_mtx_unlock(rt_mtx); + return (error); +} /* * Called from rtrequest(RTM_DELETE, ...) to fix up the route's ``family'' * (i.e., the routes related to it by the operation of cloning). This @@ -722,8 +929,10 @@ rt_fixdelete(rn, vp) struct rtentry *rt = (struct rtentry *)rn; struct rtentry *rt0 = vp; + lck_mtx_assert(rt_mtx, LCK_MTX_ASSERT_OWNED); + if (rt->rt_parent == rt0 && !(rt->rt_flags & RTF_PINNED)) { - return rtrequest(RTM_DELETE, rt_key(rt), + return rtrequest_locked(RTM_DELETE, rt_key(rt), (struct sockaddr *)0, rt_mask(rt), rt->rt_flags, (struct rtentry **)0); } @@ -737,9 +946,7 @@ rt_fixdelete(rn, vp) * network route and (cloned) host routes. For this reason, a simple check * of rt->rt_parent is insufficient; each candidate route must be tested * against the (mask, value) of the new route (passed as before in vp) - * to see if the new route matches it. Unfortunately, this has the obnoxious - * property of also triggering for insertion /above/ a pre-existing network - * route and clones. Sigh. This may be fixed some day. + * to see if the new route matches it. * * XXX - it may be possible to do fixdelete() for changes and reserve this * routine just for adds. I'm not sure why I thought it was necessary to do @@ -758,14 +965,16 @@ rt_fixchange(rn, vp) struct rtfc_arg *ap = vp; struct rtentry *rt0 = ap->rt0; struct radix_node_head *rnh = ap->rnh; - u_char *xk1, *xm1, *xk2; - int i, len; + u_char *xk1, *xm1, *xk2, *xmp; + int i, len, mlen; #ifdef DEBUG if (rtfcdebug) printf("rt_fixchange: rt %p, rt0 %p\n", rt, rt0); #endif + lck_mtx_assert(rt_mtx, LCK_MTX_ASSERT_OWNED); + if (!rt->rt_parent || (rt->rt_flags & RTF_PINNED)) { #ifdef DEBUG if(rtfcdebug) printf("no parent or pinned\n"); @@ -777,7 +986,7 @@ rt_fixchange(rn, vp) #ifdef DEBUG if(rtfcdebug) printf("parent match\n"); #endif - return rtrequest(RTM_DELETE, rt_key(rt), + return rtrequest_locked(RTM_DELETE, rt_key(rt), (struct sockaddr *)0, rt_mask(rt), rt->rt_flags, (struct rtentry **)0); } @@ -793,7 +1002,29 @@ rt_fixchange(rn, vp) xm1 = (u_char *)rt_mask(rt0); xk2 = (u_char *)rt_key(rt); - for (i = rnh->rnh_treetop->rn_off; i < len; i++) { + /* avoid applying a less specific route */ + xmp = (u_char *)rt_mask(rt->rt_parent); + mlen = ((struct sockaddr *)rt_key(rt->rt_parent))->sa_len; + if (mlen > ((struct sockaddr *)rt_key(rt0))->sa_len) { +#if DEBUG + if (rtfcdebug) + printf("rt_fixchange: inserting a less " + "specific route\n"); +#endif + return 0; + } + for (i = rnh->rnh_treetop->rn_offset; i < mlen; i++) { + if ((xmp[i] & ~(xmp[i] ^ xm1[i])) != xmp[i]) { +#if DEBUG + if (rtfcdebug) + printf("rt_fixchange: inserting a less " + "specific route\n"); +#endif + return 0; + } + } + + for (i = rnh->rnh_treetop->rn_offset; i < len; i++) { if ((xk2[i] & xm1[i]) != xk1[i]) { #ifdef DEBUG if(rtfcdebug) printf("no match\n"); @@ -809,7 +1040,7 @@ rt_fixchange(rn, vp) #ifdef DEBUG if(rtfcdebug) printf("deleting\n"); #endif - return rtrequest(RTM_DELETE, rt_key(rt), (struct sockaddr *)0, + return rtrequest_locked(RTM_DELETE, rt_key(rt), (struct sockaddr *)0, rt_mask(rt), rt->rt_flags, (struct rtentry **)0); } @@ -822,12 +1053,15 @@ rt_setgate(rt0, dst, gate) int dlen = ROUNDUP(dst->sa_len), glen = ROUNDUP(gate->sa_len); register struct rtentry *rt = rt0; struct radix_node_head *rnh = rt_tables[dst->sa_family]; - + extern void kdp_set_gateway_mac (void *gatewaymac); /* * A host route with the destination equal to the gateway * will interfere with keeping LLINFO in the routing * table, so disallow it. */ + + lck_mtx_assert(rt_mtx, LCK_MTX_ASSERT_OWNED); + if (((rt0->rt_flags & (RTF_HOST|RTF_GATEWAY|RTF_LLINFO)) == (RTF_HOST|RTF_GATEWAY)) && (dst->sa_len == gate->sa_len) && @@ -837,7 +1071,7 @@ rt_setgate(rt0, dst, gate) * or a routing redirect, so try to delete it. */ if (rt_key(rt0)) - rtrequest(RTM_DELETE, (struct sockaddr *)rt_key(rt0), + rtrequest_locked(RTM_DELETE, (struct sockaddr *)rt_key(rt0), rt0->rt_gateway, rt_mask(rt0), rt0->rt_flags, 0); return EADDRNOTAVAIL; } @@ -867,13 +1101,13 @@ rt_setgate(rt0, dst, gate) */ Bcopy(gate, (rt->rt_gateway = (struct sockaddr *)(new + dlen)), glen); - /* - * if we are replacing the chunk (or it's new) we need to + /* + * if we are replacing the chunk (or it's new) we need to * replace the dst as well */ if (old) { Bcopy(dst, new, dlen); - Free(old); + R_Free(old); } /* @@ -881,7 +1115,7 @@ rt_setgate(rt0, dst, gate) * so drop it. */ if (rt->rt_gwroute) { - rt = rt->rt_gwroute; RTFREE(rt); + rt = rt->rt_gwroute; rtfree_locked(rt); rt = rt0; rt->rt_gwroute = 0; } /* @@ -895,12 +1129,18 @@ rt_setgate(rt0, dst, gate) * This is obviously mandatory when we get rt->rt_output(). */ if (rt->rt_flags & RTF_GATEWAY) { - rt->rt_gwroute = rtalloc1(gate, 1, RTF_PRCLONING); + rt->rt_gwroute = rtalloc1_locked(gate, 1, RTF_PRCLONING); if (rt->rt_gwroute == rt) { - RTFREE(rt->rt_gwroute); + rtfree_locked(rt->rt_gwroute); rt->rt_gwroute = 0; return EDQUOT; /* failure */ } + /* Tell the kernel debugger about the new default gateway */ + if ((AF_INET == rt->rt_gateway->sa_family) && + rt->rt_gwroute && rt->rt_gwroute->rt_gateway && + (AF_LINK == rt->rt_gwroute->rt_gateway->sa_family)) { + kdp_set_gateway_mac(((struct sockaddr_dl *)rt0->rt_gwroute->rt_gateway)->sdl_data); + } } /* @@ -947,6 +1187,19 @@ int rtinit(ifa, cmd, flags) register struct ifaddr *ifa; int cmd, flags; +{ + int error; + lck_mtx_assert(rt_mtx, LCK_MTX_ASSERT_NOTOWNED); + lck_mtx_lock(rt_mtx); + error = rtinit_locked(ifa, cmd, flags); + lck_mtx_unlock(rt_mtx); + return (error); +} + +int +rtinit_locked(ifa, cmd, flags) + register struct ifaddr *ifa; + int cmd, flags; { register struct rtentry *rt; register struct sockaddr *dst; @@ -962,13 +1215,16 @@ rtinit(ifa, cmd, flags) * be confusing at best and possibly worse. */ if (cmd == RTM_DELETE) { - /* + /* * It's a delete, so it should already exist.. * If it's a net, mask off the host bits * (Assuming we have a mask) */ if ((flags & RTF_HOST) == 0 && ifa->ifa_netmask) { - m = m_get(M_WAIT, MT_SONAME); + m = m_get(M_DONTWAIT, MT_SONAME); + if (m == NULL) { + return(ENOBUFS); + } deldst = mtod(m, struct sockaddr *); rt_maskedcopy(dst, deldst, ifa->ifa_netmask); dst = deldst; @@ -979,20 +1235,20 @@ rtinit(ifa, cmd, flags) * We set "report" to FALSE so that if it doesn't exist, * it doesn't report an error or clone a route, etc. etc. */ - rt = rtalloc1(dst, 0, 0UL); + rt = rtalloc1_locked(dst, 0, 0UL); if (rt) { /* * Ok so we found the rtentry. it has an extra reference * for us at this stage. we won't need that so * lop that off now. */ - rt->rt_refcnt--; + rtunref(rt); if (rt->rt_ifa != ifa) { /* * If the interface in the rtentry doesn't match * the interface we are using, then we don't * want to delete it, so return an error. - * This seems to be the only point of + * This seems to be the only point of * this whole RTM_DELETE clause. */ if (m) @@ -1004,11 +1260,12 @@ rtinit(ifa, cmd, flags) /* XXX */ #if 0 else { - /* + /* * One would think that as we are deleting, and we know * it doesn't exist, we could just return at this point * with an "ELSE" clause, but apparently not.. */ + lck_mtx_unlock(rt_mtx); return (flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH); } @@ -1017,7 +1274,7 @@ rtinit(ifa, cmd, flags) /* * Do the actual request */ - error = rtrequest(cmd, dst, ifa->ifa_addr, ifa->ifa_netmask, + error = rtrequest_locked(cmd, dst, ifa->ifa_addr, ifa->ifa_netmask, flags | ifa->ifa_flags, &nrt); if (m) (void) m_free(m); @@ -1030,9 +1287,11 @@ rtinit(ifa, cmd, flags) * notify any listenning routing agents of the change */ rt_newaddrmsg(cmd, ifa, error, nrt); + if (use_routegenid) + route_generation++; if (rt->rt_refcnt <= 0) { - rt->rt_refcnt++; - rtfree(rt); + rt->rt_refcnt++; /* need a 1->0 transition to free */ + rtfree_locked(rt); } } @@ -1044,14 +1303,16 @@ rtinit(ifa, cmd, flags) /* * We just wanted to add it.. we don't actually need a reference */ - rt->rt_refcnt--; + rtunref(rt); /* - * If it came back with an unexpected interface, then it must + * If it came back with an unexpected interface, then it must * have already existed or something. (XXX) */ if (rt->rt_ifa != ifa) { - printf("rtinit: wrong ifa (%p) was (%p)\n", ifa, - rt->rt_ifa); + if (!(rt->rt_ifa->ifa_ifp->if_flags & + (IFF_POINTOPOINT|IFF_LOOPBACK))) + printf("rtinit: wrong ifa (%p) was (%p)\n", + ifa, rt->rt_ifa); /* * Ask that the protocol in question * remove anything it has associated with @@ -1059,19 +1320,16 @@ rtinit(ifa, cmd, flags) */ if (rt->rt_ifa->ifa_rtrequest) rt->rt_ifa->ifa_rtrequest(RTM_DELETE, rt, SA(0)); - /* - * Remove the referenve to the it's ifaddr. + /* + * Set the route's ifa. */ - IFAFREE(rt->rt_ifa); + rtsetifa(rt, ifa); /* * And substitute in references to the ifaddr * we are adding. */ - rt->rt_ifa = ifa; rt->rt_ifp = ifa->ifa_ifp; - rt->rt_dlt = ifa->ifa_dlt; - rt->rt_rmx.rmx_mtu = ifa->ifa_ifp->if_mtu; - ifa->ifa_refcnt++; + rt->rt_rmx.rmx_mtu = ifa->ifa_ifp->if_mtu; /*XXX*/ /* * Now ask the protocol to check if it needs * any special processing in its new form. @@ -1083,6 +1341,8 @@ rtinit(ifa, cmd, flags) * notify any listenning routing agents of the change */ rt_newaddrmsg(cmd, ifa, error, nrt); - } + if (use_routegenid) + route_generation++; + } return (error); }