diff options
Diffstat (limited to 'sys/net/route.c')
-rw-r--r-- | sys/net/route.c | 602 |
1 files changed, 61 insertions, 541 deletions
diff --git a/sys/net/route.c b/sys/net/route.c index be71e13..1f3e096 100644 --- a/sys/net/route.c +++ b/sys/net/route.c @@ -41,6 +41,7 @@ #include <sys/param.h> #include <sys/systm.h> +#include <sys/syslog.h> #include <sys/malloc.h> #include <sys/mbuf.h> #include <sys/socket.h> @@ -53,6 +54,7 @@ #include <sys/vimage.h> #include <net/if.h> +#include <net/if_dl.h> #include <net/route.h> #ifdef RADIX_MPATH @@ -269,8 +271,7 @@ rtalloc1_fib(struct sockaddr *dst, int report, u_long ignflags, struct radix_node *rn; struct rtentry *newrt; struct rt_addrinfo info; - u_long nflags; - int needresolve = 0, err = 0, msgtype = RTM_MISS; + int err = 0, msgtype = RTM_MISS; int needlock; KASSERT((fibnum < rt_numfibs), ("rtalloc1_fib: bad fibnum")); @@ -283,10 +284,9 @@ rtalloc1_fib(struct sockaddr *dst, int report, u_long ignflags, */ if (rnh == NULL) { V_rtstat.rts_unreach++; - goto miss2; + goto miss; } needlock = !(ignflags & RTF_RNH_LOCKED); -retry: if (needlock) RADIX_NODE_HEAD_RLOCK(rnh); #ifdef INVARIANTS @@ -295,103 +295,33 @@ retry: #endif rn = rnh->rnh_matchaddr(dst, rnh); if (rn && ((rn->rn_flags & RNF_ROOT) == 0)) { - newrt = rt = RNTORT(rn); - nflags = rt->rt_flags & ~ignflags; - if (report && (nflags & RTF_CLONING)) { - if (needlock && !RADIX_NODE_HEAD_LOCK_TRY_UPGRADE(rnh)) { - RADIX_NODE_HEAD_RUNLOCK(rnh); - RADIX_NODE_HEAD_LOCK(rnh); - /* - * lookup again to make sure it wasn't changed - */ - rn = rnh->rnh_matchaddr(dst, rnh); - if (!(rn && ((rn->rn_flags & RNF_ROOT) == 0))) { - RADIX_NODE_HEAD_UNLOCK(rnh); - needresolve = 0; - log(LOG_INFO, "retrying route lookup ...\n"); - goto retry; - } - } - needresolve = 1; - } else { - RT_LOCK(newrt); - RT_ADDREF(newrt); - if (needlock) - RADIX_NODE_HEAD_RUNLOCK(rnh); - goto done; - } - } - /* - * if needresolve is set then we have the exclusive lock - * and we need to keep it held for the benefit of rtrequest_fib - */ - if (!needresolve && needlock) + RT_LOCK(newrt); + RT_ADDREF(newrt); + if (needlock) + RADIX_NODE_HEAD_RUNLOCK(rnh); + goto done; + + } else if (needlock) RADIX_NODE_HEAD_RUNLOCK(rnh); - if (needresolve) { - RADIX_NODE_HEAD_WLOCK_ASSERT(rnh); + /* + * Either we hit the root or couldn't find any match, + * Which basically means + * "caint get there frm here" + */ + V_rtstat.rts_unreach++; +miss: + if (report) { /* - * We are apparently adding (report = 0 in delete). - * If it requires that it be cloned, do so. - * (This implies it wasn't a HOST route.) + * If required, report the failure to the supervising + * Authorities. + * For a delete, this is not an error. (report == 0) */ - err = rtrequest_fib(RTM_RESOLVE, dst, NULL, - NULL, RTF_RNH_LOCKED, &newrt, fibnum); - if (err) { - /* - * If the cloning didn't succeed, maybe - * what we have will do. Return that. - */ - newrt = rt; /* existing route */ - RT_LOCK(newrt); - RT_ADDREF(newrt); - goto miss; - } - KASSERT(newrt, ("no route and no error")); - RT_LOCK(newrt); - if (newrt->rt_flags & RTF_XRESOLVE) { - /* - * If the new route specifies it be - * externally resolved, then go do that. - */ - msgtype = RTM_RESOLVE; - goto miss; - } - /* Inform listeners of the new route. */ bzero(&info, sizeof(info)); - info.rti_info[RTAX_DST] = rt_key(newrt); - info.rti_info[RTAX_NETMASK] = rt_mask(newrt); - info.rti_info[RTAX_GATEWAY] = newrt->rt_gateway; - if (newrt->rt_ifp != NULL) { - info.rti_info[RTAX_IFP] = - newrt->rt_ifp->if_addr->ifa_addr; - info.rti_info[RTAX_IFA] = newrt->rt_ifa->ifa_addr; - } - rt_missmsg(RTM_ADD, &info, newrt->rt_flags, 0); - if (needlock) - RADIX_NODE_HEAD_UNLOCK(rnh); - } else { - /* - * Either we hit the root or couldn't find any match, - * Which basically means - * "caint get there frm here" - */ - V_rtstat.rts_unreach++; - miss: - if (needlock && needresolve) - RADIX_NODE_HEAD_UNLOCK(rnh); - miss2: if (report) { - /* - * If required, report the failure to the supervising - * Authorities. - * For a delete, this is not an error. (report == 0) - */ - bzero(&info, sizeof(info)); - info.rti_info[RTAX_DST] = dst; - rt_missmsg(msgtype, &info, 0, err); - } - } + info.rti_info[RTAX_DST] = dst; + rt_missmsg(msgtype, &info, 0, err); + } done: if (newrt) RT_LOCK_ASSERT(newrt); @@ -420,7 +350,7 @@ rtfree(struct rtentry *rt) */ RT_REMREF(rt); if (rt->rt_refcnt > 0) { - printf("%s: %p has %lu refs\n", __func__, rt, rt->rt_refcnt); + log(LOG_DEBUG, "%s: %p has %d refs\t", __func__, rt, rt->rt_refcnt); goto done; } @@ -461,8 +391,6 @@ rtfree(struct rtentry *rt) */ if (rt->rt_ifa) IFAFREE(rt->rt_ifa); - rt->rt_parent = NULL; /* NB: no refcnt on parent */ - /* * The key is separatly alloc'd so free it (see rt_setgate()). * This also frees the gateway, as they are always malloc'd @@ -715,14 +643,6 @@ ifa_ifwithroute_fib(int flags, struct sockaddr *dst, struct sockaddr *gateway, return (ifa); } -static walktree_f_t rt_fixdelete; -static walktree_f_t rt_fixchange; - -struct rtfc_arg { - struct rtentry *rt0; - struct radix_node_head *rnh; -}; - /* * Do appropriate manipulations of a routing tree given * all the bits of info needed @@ -828,8 +748,13 @@ rtexpunge(struct rtentry *rt) struct ifaddr *ifa; int error = 0; + /* + * Find the correct routing tree to use for this Address Family + */ rnh = V_rt_tables[rt->rt_fibnum][rt_key(rt)->sa_family]; RT_LOCK_ASSERT(rt); + if (rnh == NULL) + return (EAFNOSUPPORT); RADIX_NODE_HEAD_LOCK_ASSERT(rnh); #if 0 /* @@ -840,13 +765,6 @@ rtexpunge(struct rtentry *rt) KASSERT(rt->rt_refcnt <= 1, ("bogus refcnt %ld", rt->rt_refcnt)); #endif /* - * Find the correct routing tree to use for this Address Family - */ - rnh = V_rt_tables[rt->rt_fibnum][rt_key(rt)->sa_family]; - if (rnh == NULL) - return (EAFNOSUPPORT); - - /* * Remove the item from the tree; it should be there, * but when callers invoke us blindly it may not (sigh). */ @@ -863,24 +781,6 @@ rtexpunge(struct rtentry *rt) rt->rt_flags &= ~RTF_UP; /* - * 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_CLONING) && rt_mask(rt)) - rnh->rnh_walktree_from(rnh, rt_key(rt), rt_mask(rt), - rt_fixdelete, rt); - - /* - * Remove any external references we may have. - * This might result in another rtentry being freed if - * we held its last reference. - */ - if (rt->rt_gwroute) { - RTFREE(rt->rt_gwroute); - rt->rt_gwroute = NULL; - } - - /* * Give the protocol a chance to keep things in sync. */ if ((ifa = rt->rt_ifa) && ifa->ifa_rtrequest) { @@ -904,12 +804,6 @@ bad: } int -rtrequest1(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt) -{ - return (rtrequest1_fib(req, info, ret_nrt, 0)); -} - -int rtrequest1_fib(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt, u_int fibnum) { @@ -941,10 +835,9 @@ rtrequest1_fib(int req, struct rt_addrinfo *info, struct rtentry **ret_nrt, * If we are adding a host route then we don't want to put * a netmask in the tree, nor do we want to clone it. */ - if (flags & RTF_HOST) { + if (flags & RTF_HOST) netmask = NULL; - flags &= ~RTF_CLONING; - } + switch (req) { case RTM_DELETE: #ifdef RADIX_MPATH @@ -1023,26 +916,6 @@ normal_rtdel: rt->rt_flags &= ~RTF_UP; /* - * 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_CLONING) && - rt_mask(rt)) { - rnh->rnh_walktree_from(rnh, dst, rt_mask(rt), - rt_fixdelete, rt); - } - - /* - * Remove any external references we may have. - * This might result in another rtentry being freed if - * we held its last reference. - */ - if (rt->rt_gwroute) { - RTFREE(rt->rt_gwroute); - rt->rt_gwroute = NULL; - } - - /* * give the protocol a chance to keep things in sync. */ if ((ifa = rt->rt_ifa) && ifa->ifa_rtrequest) @@ -1069,20 +942,12 @@ deldone: } else RTFREE_LOCKED(rt); break; - case RTM_RESOLVE: - if (ret_nrt == NULL || (rt = *ret_nrt) == NULL) - senderr(EINVAL); - ifa = rt->rt_ifa; - /* XXX locking? */ - flags = rt->rt_flags & - ~(RTF_CLONING | RTF_STATIC); - flags |= RTF_WASCLONED; - gateway = rt->rt_gateway; - if ((netmask = rt->rt_genmask) == NULL) - flags |= RTF_HOST; - goto makeroute; - + /* + * resolve was only used for route cloning + * here for compat + */ + break; case RTM_ADD: if ((flags & RTF_GATEWAY) && !gateway) senderr(EINVAL); @@ -1093,8 +958,6 @@ deldone: if (info->rti_ifa == NULL && (error = rt_getifa_fib(info, fibnum))) senderr(error); ifa = info->rti_ifa; - - makeroute: rt = uma_zalloc(rtzone, M_NOWAIT | M_ZERO); if (rt == NULL) senderr(ENOBUFS); @@ -1103,7 +966,7 @@ deldone: rt->rt_fibnum = fibnum; /* * Add the gateway. Possibly re-malloc-ing the storage for it - * also add the rt_gwroute if possible. + * */ RT_LOCK(rt); if ((error = rt_setgate(rt, dst, gateway)) != 0) { @@ -1138,8 +1001,6 @@ deldone: /* do not permit exactly the same dst/mask/gw pair */ if (rn_mpath_capable(rnh) && rt_mpath_conflict(rnh, rt, netmask)) { - if (rt->rt_gwroute) - RTFREE(rt->rt_gwroute); if (rt->rt_ifa) { IFAFREE(rt->rt_ifa); } @@ -1152,34 +1013,11 @@ deldone: /* XXX mtu manipulation will be done in rnh_addaddr -- itojun */ rn = rnh->rnh_addaddr(ndst, netmask, rnh, rt->rt_nodes); - if (rn == NULL) { - struct rtentry *rt2; - /* - * Uh-oh, we already have one of these in the tree. - * We do a special hack: if the route that's already - * there was generated by the cloning mechanism - * then we just blow it away and retry the insertion - * of the new one. - */ - rt2 = rtalloc1_fib(dst, 0, RTF_RNH_LOCKED, fibnum); - if (rt2 && rt2->rt_parent) { - rtexpunge(rt2); - RT_UNLOCK(rt2); - rn = rnh->rnh_addaddr(ndst, netmask, - rnh, rt->rt_nodes); - } else if (rt2) { - /* undo the extra ref we got */ - RTFREE_LOCKED(rt2); - } - } - /* * If it still failed to go into the tree, * then un-make it (this should be a function) */ if (rn == NULL) { - if (rt->rt_gwroute) - RTFREE(rt->rt_gwroute); if (rt->rt_ifa) IFAFREE(rt->rt_ifa); Free(rt_key(rt)); @@ -1188,33 +1026,6 @@ deldone: senderr(EEXIST); } - rt->rt_parent = NULL; - - /* - * If we got here from RESOLVE, then we are cloning - * so clone the rest, and note that we - * are a clone (and increment the parent's references) - */ - if (req == RTM_RESOLVE) { - KASSERT(ret_nrt && *ret_nrt, - ("no route to clone from")); - rt->rt_rmx = (*ret_nrt)->rt_rmx; /* copy metrics */ - rt->rt_rmx.rmx_pksent = 0; /* reset packet counter */ - if ((*ret_nrt)->rt_flags & RTF_CLONING) { - /* - * NB: We do not bump the refcnt on the parent - * entry under the assumption that it will - * remain so long as we do. This is - * important when deleting the parent route - * as this operation requires traversing - * the tree to delete all clones and futzing - * with refcnts requires us to double-lock - * parent through this back reference. - */ - rt->rt_parent = *ret_nrt; - } - } - /* * If this protocol has something to add to this then * allow it to do that as well. @@ -1223,20 +1034,6 @@ deldone: ifa->ifa_rtrequest(req, rt, info); /* - * We repeat the same procedure from rt_setgate() here because - * it doesn't fire when we call it there because the node - * hasn't been added to the tree yet. - */ - if (req == RTM_ADD && - !(rt->rt_flags & RTF_HOST) && rt_mask(rt) != NULL) { - struct rtfc_arg arg; - arg.rnh = rnh; - arg.rt0 = rt; - rnh->rnh_walktree_from(rnh, rt_key(rt), rt_mask(rt), - rt_fixchange, &arg); - } - - /* * actually return a resultant rtentry and * give the caller a single reference. */ @@ -1263,159 +1060,21 @@ bad: #undef ifpaddr #undef flags -/* - * 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 - * routine is iterated over all potential former-child-routes by way of - * rnh->rnh_walktree_from() above, and those that actually are children of - * the late parent (passed in as VP here) are themselves deleted. - */ -static int -rt_fixdelete(struct radix_node *rn, void *vp) -{ - struct rtentry *rt = RNTORT(rn); - struct rtentry *rt0 = vp; - - if (rt->rt_parent == rt0 && - !(rt->rt_flags & (RTF_PINNED | RTF_CLONING))) { - return rtrequest_fib(RTM_DELETE, rt_key(rt), NULL, rt_mask(rt), - rt->rt_flags|RTF_RNH_LOCKED, NULL, rt->rt_fibnum); - } - return 0; -} - -/* - * This routine is called from rt_setgate() to do the analogous thing for - * adds and changes. There is the added complication in this case of a - * middle insert; i.e., insertion of a new network route between an older - * 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. - * - * 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 - * changes this way. - */ - -static int -rt_fixchange(struct radix_node *rn, void *vp) -{ - struct rtentry *rt = RNTORT(rn); - struct rtfc_arg *ap = vp; - struct rtentry *rt0 = ap->rt0; - struct radix_node_head *rnh = ap->rnh; - u_char *xk1, *xm1, *xk2, *xmp; - int i, len, mlen; - - /* make sure we have a parent, and route is not pinned or cloning */ - if (!rt->rt_parent || - (rt->rt_flags & (RTF_PINNED | RTF_CLONING))) - return 0; - - if (rt->rt_parent == rt0) /* parent match */ - goto delete_rt; - /* - * There probably is a function somewhere which does this... - * if not, there should be. - */ - len = imin(rt_key(rt0)->sa_len, rt_key(rt)->sa_len); - - xk1 = (u_char *)rt_key(rt0); - xm1 = (u_char *)rt_mask(rt0); - xk2 = (u_char *)rt_key(rt); - - /* avoid applying a less specific route */ - xmp = (u_char *)rt_mask(rt->rt_parent); - mlen = rt_key(rt->rt_parent)->sa_len; - if (mlen > rt_key(rt0)->sa_len) /* less specific route */ - return 0; - for (i = rnh->rnh_treetop->rn_offset; i < mlen; i++) - if ((xmp[i] & ~(xmp[i] ^ xm1[i])) != xmp[i]) - return 0; /* less specific route */ - - for (i = rnh->rnh_treetop->rn_offset; i < len; i++) - if ((xk2[i] & xm1[i]) != xk1[i]) - return 0; /* no match */ - - /* - * OK, this node is a clone, and matches the node currently being - * changed/added under the node's mask. So, get rid of it. - */ -delete_rt: - return rtrequest_fib(RTM_DELETE, rt_key(rt), NULL, - rt_mask(rt), rt->rt_flags, NULL, rt->rt_fibnum); -} - int rt_setgate(struct rtentry *rt, struct sockaddr *dst, struct sockaddr *gate) { INIT_VNET_NET(curvnet); /* XXX dst may be overwritten, can we move this to below */ + int dlen = SA_SIZE(dst), glen = SA_SIZE(gate); +#ifdef INVARIANTS struct radix_node_head *rnh = V_rt_tables[rt->rt_fibnum][dst->sa_family]; - int dlen = SA_SIZE(dst), glen = SA_SIZE(gate); +#endif -again: RT_LOCK_ASSERT(rt); RADIX_NODE_HEAD_LOCK_ASSERT(rnh); /* - * A host route with the destination equal to the gateway - * will interfere with keeping LLINFO in the routing - * table, so disallow it. - */ - if (((rt->rt_flags & (RTF_HOST|RTF_GATEWAY|RTF_LLINFO)) == - (RTF_HOST|RTF_GATEWAY)) && - dst->sa_len == gate->sa_len && - bcmp(dst, gate, dst->sa_len) == 0) { - /* - * The route might already exist if this is an RTM_CHANGE - * or a routing redirect, so try to delete it. - */ - if (rt_key(rt)) - rtexpunge(rt); - return EADDRNOTAVAIL; - } - - /* - * Cloning loop avoidance in case of bad configuration. - */ - if (rt->rt_flags & RTF_GATEWAY) { - struct rtentry *gwrt; - - RT_UNLOCK(rt); /* XXX workaround LOR */ - gwrt = rtalloc1_fib(gate, 1, RTF_RNH_LOCKED, rt->rt_fibnum); - if (gwrt == rt) { - RT_REMREF(rt); - return (EADDRINUSE); /* failure */ - } - /* - * Try to reacquire the lock on rt, and if it fails, - * clean state and restart from scratch. - */ - if (!RT_TRYLOCK(rt)) { - RTFREE_LOCKED(gwrt); - RT_LOCK(rt); - goto again; - } - /* - * If there is already a gwroute, then drop it. If we - * are asked to replace route with itself, then do - * not leak its refcounter. - */ - if (rt->rt_gwroute != NULL) { - if (rt->rt_gwroute == gwrt) { - RT_REMREF(rt->rt_gwroute); - } else - RTFREE(rt->rt_gwroute); - } - - if ((rt->rt_gwroute = gwrt) != NULL) - RT_UNLOCK(rt->rt_gwroute); - } - - /* * Prepare to store the gateway in rt->rt_gateway. * Both dst and gateway are stored one after the other in the same * malloc'd chunk. If we have room, we can reuse the old buffer, @@ -1446,21 +1105,7 @@ again: */ bcopy(gate, rt->rt_gateway, glen); - /* - * This isn't going to do anything useful for host routes, so - * don't bother. Also make sure we have a reasonable mask - * (we don't yet have one during adds). - */ - if (!(rt->rt_flags & RTF_HOST) && rt_mask(rt) != 0) { - struct rtfc_arg arg; - - arg.rnh = rnh; - arg.rt0 = rt; - rnh->rnh_walktree_from(rnh, rt_key(rt), rt_mask(rt), - rt_fixchange, &arg); - } - - return 0; + return (0); } static void @@ -1500,6 +1145,7 @@ rtinit1(struct ifaddr *ifa, int cmd, int flags, int fibnum) char tempbuf[_SOCKADDR_TMPSIZE]; int didwork = 0; int a_failure = 0; + static struct sockaddr_dl null_sdl = {sizeof(null_sdl), AF_LINK}; if (flags & RTF_HOST) { dst = ifa->ifa_dstaddr; @@ -1604,7 +1250,14 @@ rtinit1(struct ifaddr *ifa, int cmd, int flags, int fibnum) info.rti_ifa = ifa; info.rti_flags = flags | ifa->ifa_flags; info.rti_info[RTAX_DST] = dst; - info.rti_info[RTAX_GATEWAY] = ifa->ifa_addr; + /* + * doing this for compatibility reasons + */ + if (cmd == RTM_ADD) + info.rti_info[RTAX_GATEWAY] = + (struct sockaddr *)&null_sdl; + else + info.rti_info[RTAX_GATEWAY] = ifa->ifa_addr; info.rti_info[RTAX_NETMASK] = netmask; error = rtrequest1_fib(cmd, &info, &rt, fibnum); if (error == 0 && rt != NULL) { @@ -1628,6 +1281,15 @@ rtinit1(struct ifaddr *ifa, int cmd, int flags, int fibnum) rt->rt_ifa = ifa; } #endif + /* + * doing this for compatibility reasons + */ + if (cmd == RTM_ADD) { + ((struct sockaddr_dl *)rt->rt_gateway)->sdl_type = + rt->rt_ifp->if_type; + ((struct sockaddr_dl *)rt->rt_gateway)->sdl_index = + rt->rt_ifp->if_index; + } rt_newaddrmsg(cmd, ifa, error, rt); if (cmd == RTM_DELETE) { /* @@ -1696,147 +1358,5 @@ rtinit(struct ifaddr *ifa, int cmd, int flags) return (rtinit1(ifa, cmd, flags, fib)); } -/* - * rt_check() is invoked on each layer 2 output path, prior to - * encapsulating outbound packets. - * - * The function is mostly used to find a routing entry for the gateway, - * which in some protocol families could also point to the link-level - * address for the gateway itself (the side effect of revalidating the - * route to the destination is rather pointless at this stage, we did it - * already a moment before in the pr_output() routine to locate the ifp - * and gateway to use). - * - * When we remove the layer-3 to layer-2 mapping tables from the - * routing table, this function can be removed. - * - * === On input === - * *dst is the address of the NEXT HOP (which coincides with the - * final destination if directly reachable); - * *lrt0 points to the cached route to the final destination; - * *lrt is not meaningful; - * (*lrt0 has no ref held on it by us so REMREF is not needed. - * Refs only account for major structural references and not usages, - * which is actually a bit of a problem.) - * - * === Operation === - * If the route is marked down try to find a new route. If the route - * to the gateway is gone, try to setup a new route. Otherwise, - * if the route is marked for packets to be rejected, enforce that. - * Note that rtalloc returns an rtentry with an extra REF that we may - * need to lose. - * - * === On return === - * *dst is unchanged; - * *lrt0 points to the (possibly new) route to the final destination - * *lrt points to the route to the next hop [LOCKED] - * - * Their values are meaningful ONLY if no error is returned. - * - * To follow this you have to remember that: - * RT_REMREF reduces the reference count by 1 but doesn't check it for 0 (!) - * RTFREE_LOCKED includes an RT_REMREF (or an rtfree if refs == 1) - * and an RT_UNLOCK - * RTFREE does an RT_LOCK and an RTFREE_LOCKED - * The gwroute pointer counts as a reference on the rtentry to which it points. - * so when we add it we use the ref that rtalloc gives us and when we lose it - * we need to remove the reference. - * RT_TEMP_UNLOCK does an RT_ADDREF before freeing the lock, and - * RT_RELOCK locks it (it can't have gone away due to the ref) and - * drops the ref, possibly freeing it and zeroing the pointer if - * the ref goes to 0 (unlocking in the process). - */ -int -rt_check(struct rtentry **lrt, struct rtentry **lrt0, struct sockaddr *dst) -{ - struct rtentry *rt; - struct rtentry *rt0; - u_int fibnum; - - KASSERT(*lrt0 != NULL, ("rt_check")); - rt0 = *lrt0; - rt = NULL; - fibnum = rt0->rt_fibnum; - - /* NB: the locking here is tortuous... */ - RT_LOCK(rt0); -retry: - if (rt0 && (rt0->rt_flags & RTF_UP) == 0) { - /* Current rt0 is useless, try get a replacement. */ - RT_UNLOCK(rt0); - rt0 = NULL; - } - if (rt0 == NULL) { - rt0 = rtalloc1_fib(dst, 1, 0UL, fibnum); - if (rt0 == NULL) { - return (EHOSTUNREACH); - } - RT_REMREF(rt0); /* don't need the reference. */ - } - - if (rt0->rt_flags & RTF_GATEWAY) { - if ((rt = rt0->rt_gwroute) != NULL) { - RT_LOCK(rt); /* NB: gwroute */ - if ((rt->rt_flags & RTF_UP) == 0) { - /* gw route is dud. ignore/lose it */ - RTFREE_LOCKED(rt); /* unref (&unlock) gwroute */ - rt = rt0->rt_gwroute = NULL; - } - } - - if (rt == NULL) { /* NOT AN ELSE CLAUSE */ - RT_TEMP_UNLOCK(rt0); /* MUST return to undo this */ - rt = rtalloc1_fib(rt0->rt_gateway, 1, 0UL, fibnum); - if ((rt == rt0) || (rt == NULL)) { - /* the best we can do is not good enough */ - if (rt) { - RT_REMREF(rt); /* assumes ref > 0 */ - RT_UNLOCK(rt); - } - RTFREE(rt0); /* lock, unref, (unlock) */ - return (ENETUNREACH); - } - /* - * Relock it and lose the added reference. - * All sorts of things could have happenned while we - * had no lock on it, so check for them. - */ - RT_RELOCK(rt0); - if (rt0 == NULL || ((rt0->rt_flags & RTF_UP) == 0)) - /* Ru-roh.. what we had is no longer any good */ - goto retry; - /* - * While we were away, someone replaced the gateway. - * Since a reference count is involved we can't just - * overwrite it. - */ - if (rt0->rt_gwroute) { - if (rt0->rt_gwroute != rt) { - RTFREE_LOCKED(rt); - goto retry; - } - } else { - rt0->rt_gwroute = rt; - } - } - RT_LOCK_ASSERT(rt); - RT_UNLOCK(rt0); - } else { - /* think of rt as having the lock from now on.. */ - rt = rt0; - } - /* XXX why are we inspecting rmx_expire? */ - if ((rt->rt_flags & RTF_REJECT) && - (rt->rt_rmx.rmx_expire == 0 || - time_uptime < rt->rt_rmx.rmx_expire)) { - RT_UNLOCK(rt); - return (rt == rt0 ? EHOSTDOWN : EHOSTUNREACH); - } - - *lrt = rt; - *lrt0 = rt0; - return (0); -} - /* This must be before ip6_init2(), which is now SI_ORDER_MIDDLE */ SYSINIT(route, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, route_init, 0); |