diff options
Diffstat (limited to 'sys/net/route.c')
-rw-r--r-- | sys/net/route.c | 373 |
1 files changed, 203 insertions, 170 deletions
diff --git a/sys/net/route.c b/sys/net/route.c index 675e0ee..a09fdf4 100644 --- a/sys/net/route.c +++ b/sys/net/route.c @@ -63,8 +63,7 @@ static void rt_maskedcopy(struct sockaddr *, static void rtable_init(void **); static void -rtable_init(table) - void **table; +rtable_init(void **table) { struct domain *dom; for (dom = domains; dom; dom = dom->dom_next) @@ -84,50 +83,45 @@ route_init() * Packet routing routines. */ void -rtalloc(ro) - register struct route *ro; +rtalloc(struct route *ro) { rtalloc_ign(ro, 0UL); } void -rtalloc_ign(ro, ignore) - register struct route *ro; - u_long ignore; +rtalloc_ign(struct route *ro, u_long ignore) { struct rtentry *rt; - int s; 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. */ - s = splnet(); RTFREE(rt); ro->ro_rt = NULL; - splx(s); } ro->ro_rt = rtalloc1(&ro->ro_dst, 1, ignore); + if (ro->ro_rt) + RT_UNLOCK(ro->ro_rt); } /* * Look up the route that matches the address given * Or, at least try.. Create a cloned route if needed. + * + * The returned route, if any, is locked. */ struct rtentry * -rtalloc1(dst, report, ignflags) - register struct sockaddr *dst; - int report; - u_long ignflags; +rtalloc1(struct sockaddr *dst, int report, u_long ignflags) { - register struct radix_node_head *rnh = rt_tables[dst->sa_family]; - register struct rtentry *rt; - register struct radix_node *rn; - struct rtentry *newrt = 0; + struct radix_node_head *rnh = rt_tables[dst->sa_family]; + struct rtentry *rt; + struct radix_node *rn; + struct rtentry *newrt; struct rt_addrinfo info; u_long nflags; - int s = splnet(), err = 0, msgtype = RTM_MISS; + int err = 0, msgtype = RTM_MISS; + newrt = 0; /* * Look up the address in the table for that Address Family */ @@ -135,9 +129,10 @@ rtalloc1(dst, report, ignflags) rtstat.rts_unreach++; goto miss2; } + bzero(&info, sizeof(info)); RADIX_NODE_HEAD_LOCK(rnh); - if ((rn = rnh->rnh_matchaddr((caddr_t)dst, rnh)) && - ((rn->rn_flags & RNF_ROOT) == 0)) { + if ((rn = rnh->rnh_matchaddr(dst, rnh)) && + (rn->rn_flags & RNF_ROOT) == 0) { /* * If we find it and it's not the root node, then * get a refernce on the rtentry associated. @@ -157,11 +152,14 @@ rtalloc1(dst, report, ignflags) * If the cloning didn't succeed, maybe * what we have will do. Return that. */ - newrt = rt; - rt->rt_refcnt++; + newrt = rt; /* existing route */ + RT_LOCK(newrt); + newrt->rt_refcnt++; goto miss; } - if ((rt = newrt) && (rt->rt_flags & RTF_XRESOLVE)) { + 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. @@ -170,18 +168,20 @@ rtalloc1(dst, report, ignflags) goto miss; } /* Inform listeners of the new route. */ - bzero(&info, sizeof(info)); - info.rti_info[RTAX_DST] = rt_key(rt); - info.rti_info[RTAX_NETMASK] = rt_mask(rt); - info.rti_info[RTAX_GATEWAY] = rt->rt_gateway; - if (rt->rt_ifp != NULL) { + 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] = - TAILQ_FIRST(&rt->rt_ifp->if_addrhead)->ifa_addr; - info.rti_info[RTAX_IFA] = rt->rt_ifa->ifa_addr; + TAILQ_FIRST(&newrt->rt_ifp->if_addrhead)->ifa_addr; + info.rti_info[RTAX_IFA] = newrt->rt_ifa->ifa_addr; } - rt_missmsg(RTM_ADD, &info, rt->rt_flags, 0); - } else - rt->rt_refcnt++; + rt_missmsg(RTM_ADD, &info, newrt->rt_flags, 0); + } else { + KASSERT(rt == newrt, ("locking wrong route")); + RT_LOCK(newrt); + newrt->rt_refcnt++; + } RADIX_NODE_HEAD_UNLOCK(rnh); } else { /* @@ -198,12 +198,12 @@ rtalloc1(dst, report, ignflags) * Authorities. * For a delete, this is not an error. (report == 0) */ - bzero((caddr_t)&info, sizeof(info)); info.rti_info[RTAX_DST] = dst; rt_missmsg(msgtype, &info, 0, err); } } - splx(s); + if (newrt) + RT_LOCK_ASSERT(newrt); return (newrt); } @@ -212,8 +212,7 @@ rtalloc1(dst, report, ignflags) * If the count gets low enough, take it out of the routing table */ void -rtfree(rt) - register struct rtentry *rt; +rtfree(struct rtentry *rt) { /* * find the tree for that address family @@ -223,21 +222,24 @@ rtfree(rt) if (rt == 0 || rnh == 0) panic("rtfree"); + RT_LOCK_ASSERT(rt); + /* * 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 (--rt->rt_refcnt > 0) + goto done; + /* XXX refcount==0? */ + if (rt->rt_refcnt == 0 && rnh->rnh_close) rnh->rnh_close((struct radix_node *)rt, rnh); - } /* * If we are no longer "up" (and ref == 0) * then we can free the resources associated * with the route. */ - if (rt->rt_refcnt <= 0 && (rt->rt_flags & RTF_UP) == 0) { + if ((rt->rt_flags & RTF_UP) == 0) { if (rt->rt_nodes->rn_flags & (RNF_ACTIVE | RNF_ROOT)) panic ("rtfree 2"); /* @@ -245,22 +247,19 @@ rtfree(rt) * so it is represented in rttrash.. remove that now. */ rttrash--; - #ifdef DIAGNOSTIC if (rt->rt_refcnt < 0) { printf("rtfree: %p not freed (neg refs)\n", rt); - return; + goto done; } #endif - /* * release references on items we hold them on.. * e.g other routes and ifaddrs. */ if (rt->rt_ifa) IFAFREE(rt->rt_ifa); - if (rt->rt_parent) - RTFREE(rt->rt_parent); + rt->rt_parent = NULL; /* NB: no refcnt on parent */ /* * The key is separatly alloc'd so free it (see rt_setgate()). @@ -272,8 +271,12 @@ rtfree(rt) /* * and the rtentry itself of course */ + RT_LOCK_DESTROY(rt); Free(rt); + return; } +done: + RT_UNLOCK(rt); } /* compare two sockaddr structures */ @@ -284,15 +287,13 @@ rtfree(rt) * destination to go through the given gateway. * Normally called as a result of a routing redirect * message from the network layer. - * - * N.B.: must be called at splnet - * */ void -rtredirect(dst, gateway, netmask, flags, src, rtp) - struct sockaddr *dst, *gateway, *netmask, *src; - int flags; - struct rtentry **rtp; +rtredirect(struct sockaddr *dst, + struct sockaddr *gateway, + struct sockaddr *netmask, + int flags, + struct sockaddr *src) { struct rtentry *rt; int error = 0; @@ -305,7 +306,7 @@ rtredirect(dst, gateway, netmask, flags, src, rtp) error = ENETUNREACH; goto out; } - rt = rtalloc1(dst, 0, 0UL); + rt = rtalloc1(dst, 0, 0UL); /* NB: rt is locked */ /* * If the redirect isn't from our current router for this dst, * it's either old or wrong. If it redirects us to ourselves, @@ -325,7 +326,7 @@ rtredirect(dst, gateway, netmask, flags, src, rtp) * which use routing redirects generated by smart gateways * to dynamically build the routing tables. */ - if ((rt == 0) || (rt_mask(rt) && rt_mask(rt)->sa_len < 2)) + if (rt == 0 || (rt_mask(rt) && rt_mask(rt)->sa_len < 2)) goto create; /* * Don't listen to the redirect if it's @@ -349,8 +350,10 @@ rtredirect(dst, gateway, netmask, flags, src, rtp) info.rti_flags = flags; rt = NULL; error = rtrequest1(RTM_ADD, &info, &rt); - if (rt != NULL) + if (rt != NULL) { + RT_UNLOCK(rt); flags = rt->rt_flags; + } stat = &rtstat.rts_dynamic; } else { /* @@ -368,12 +371,8 @@ rtredirect(dst, gateway, netmask, flags, src, rtp) } else error = EHOSTUNREACH; done: - if (rt) { - if (rtp && !error) - *rtp = rt; - else - rtfree(rt); - } + if (rt) + rtfree(rt); out: if (error) rtstat.rts_badredirect++; @@ -391,9 +390,7 @@ out: * Routing table ioctl interface. */ int -rtioctl(req, data) - u_long req; - caddr_t data; +rtioctl(u_long req, caddr_t data) { #ifdef INET /* Multicast goop, grrr... */ @@ -404,11 +401,10 @@ rtioctl(req, data) } struct ifaddr * -ifa_ifwithroute(flags, dst, gateway) - int flags; - struct sockaddr *dst, *gateway; +ifa_ifwithroute(int flags, struct sockaddr *dst, struct sockaddr *gateway) { register struct ifaddr *ifa; + if ((flags & RTF_GATEWAY) == 0) { /* * If we are adding a route to an interface, @@ -438,6 +434,7 @@ ifa_ifwithroute(flags, dst, gateway) if (rt == 0) return (0); --rt->rt_refcnt; + RT_UNLOCK(rt); if ((ifa = rt->rt_ifa) == 0) return (0); } @@ -463,10 +460,12 @@ struct rtfc_arg { * 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(int req, + struct sockaddr *dst, + struct sockaddr *gateway, + struct sockaddr *netmask, + int flags, + struct rtentry **ret_nrt) { struct rt_addrinfo info; @@ -490,8 +489,7 @@ rtrequest(req, dst, gateway, netmask, flags, ret_nrt) #define flags info->rti_flags int -rt_getifa(info) - struct rt_addrinfo *info; +rt_getifa(struct rt_addrinfo *info) { struct ifaddr *ifa; int error = 0; @@ -527,12 +525,9 @@ rt_getifa(info) } int -rtrequest1(req, info, ret_nrt) - int req; - struct rt_addrinfo *info; - struct rtentry **ret_nrt; +rtrequest1(int req, struct rt_addrinfo *info, 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; @@ -543,10 +538,9 @@ rtrequest1(req, info, ret_nrt) /* * Find the correct routing tree to use for this Address Family */ - if ((rnh = rt_tables[dst->sa_family]) == 0) { - splx(s); + rnh = rt_tables[dst->sa_family]; + if (rnh == 0) return (EAFNOSUPPORT); - } RADIX_NODE_HEAD_LOCK(rnh); /* * If we are adding a host route then we don't want to put @@ -562,11 +556,13 @@ rtrequest1(req, info, ret_nrt) * Remove the item from the tree and return it. * Complain if it is not there and do no more processing. */ - if ((rn = rnh->rnh_deladdr(dst, netmask, rnh)) == 0) + rn = rnh->rnh_deladdr(dst, netmask, rnh); + if (rn == 0) senderr(ESRCH); if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT)) panic ("rtrequest delete"); rt = (struct rtentry *)rn; + RT_LOCK(rt); rt->rt_refcnt++; rt->rt_flags &= ~RTF_UP; @@ -586,9 +582,9 @@ rtrequest1(req, info, ret_nrt) * we held its last reference. */ if (rt->rt_gwroute) { - rt = rt->rt_gwroute; - RTFREE(rt); - (rt = (struct rtentry *)rn)->rt_gwroute = 0; + struct rtentry *gwrt = rt->rt_gwroute; + RTFREE(gwrt); + rt->rt_gwroute = 0; } /* @@ -608,16 +604,18 @@ rtrequest1(req, info, ret_nrt) * but it's up to it to free the rtentry as we won't be * doing it. */ - if (ret_nrt) + if (ret_nrt) { *ret_nrt = rt; - else - RTFREE(rt); + RT_UNLOCK(rt); + } else + RTFREE_LOCKED(rt); break; case RTM_RESOLVE: if (ret_nrt == 0 || (rt = *ret_nrt) == 0) senderr(EINVAL); ifa = rt->rt_ifa; + /* XXX locking? */ flags = rt->rt_flags & ~(RTF_CLONING | RTF_PRCLONING | RTF_STATIC); flags |= RTF_WASCLONED; @@ -635,16 +633,18 @@ rtrequest1(req, info, ret_nrt) ifa = info->rti_ifa; makeroute: - R_Malloc(rt, struct rtentry *, sizeof(*rt)); + R_Zalloc(rt, struct rtentry *, sizeof(*rt)); if (rt == 0) senderr(ENOBUFS); - Bzero(rt, sizeof(*rt)); + RT_LOCK_INIT(rt); rt->rt_flags = RTF_UP | flags; /* * 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) { + RT_LOCK_DESTROY(rt); Free(rt); senderr(error); } @@ -652,7 +652,7 @@ rtrequest1(req, info, ret_nrt) /* * point to the (possibly newly malloc'd) dest address. */ - ndst = rt_key(rt); + ndst = (struct sockaddr *)rt_key(rt); /* * make sure it contains the value we want (masked if needed). @@ -670,10 +670,9 @@ rtrequest1(req, info, ret_nrt) IFAREF(ifa); rt->rt_ifa = ifa; rt->rt_ifp = 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); + /* XXX mtu manipulation will be done in rnh_addaddr -- itojun */ + rn = rnh->rnh_addaddr(ndst, netmask, rnh, rt->rt_nodes); if (rn == 0) { struct rtentry *rt2; /* @@ -686,16 +685,15 @@ rtrequest1(req, info, ret_nrt) rt2 = rtalloc1(dst, 0, RTF_PRCLONING); if (rt2 && rt2->rt_parent) { rtrequest(RTM_DELETE, - (struct sockaddr *)rt_key(rt2), + rt_key(rt2), rt2->rt_gateway, rt_mask(rt2), rt2->rt_flags, 0); - RTFREE(rt2); - rn = rnh->rnh_addaddr((caddr_t)ndst, - (caddr_t)netmask, + RTFREE_LOCKED(rt2); + rn = rnh->rnh_addaddr(ndst, netmask, rnh, rt->rt_nodes); } else if (rt2) { /* undo the extra ref we got */ - RTFREE(rt2); + RTFREE_LOCKED(rt2); } } @@ -705,11 +703,11 @@ rtrequest1(req, info, ret_nrt) */ if (rn == 0) { if (rt->rt_gwroute) - rtfree(rt->rt_gwroute); - if (rt->rt_ifa) { + RTFREE(rt->rt_gwroute); + if (rt->rt_ifa) IFAFREE(rt->rt_ifa); - } Free(rt_key(rt)); + RT_LOCK_DESTROY(rt); Free(rt); senderr(EEXIST); } @@ -722,11 +720,22 @@ rtrequest1(req, info, ret_nrt) * 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 | RTF_PRCLONING)) { - rt->rt_parent = (*ret_nrt); - (*ret_nrt)->rt_refcnt++; + /* + * 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; } } @@ -759,21 +768,23 @@ rtrequest1(req, info, ret_nrt) *ret_nrt = rt; rt->rt_refcnt++; } + RT_UNLOCK(rt); break; default: error = EOPNOTSUPP; } bad: RADIX_NODE_HEAD_UNLOCK(rnh); - splx(s); return (error); +#undef senderr +} + #undef dst #undef gateway #undef netmask #undef ifaaddr #undef ifpaddr #undef flags -} /* * Called from rtrequest(RTM_DELETE, ...) to fix up the route's ``family'' @@ -783,9 +794,7 @@ bad: * the late parent (passed in as VP here) are themselves deleted. */ static int -rt_fixdelete(rn, vp) - struct radix_node *rn; - void *vp; +rt_fixdelete(struct radix_node *rn, void *vp) { struct rtentry *rt = (struct rtentry *)rn; struct rtentry *rt0 = vp; @@ -817,9 +826,7 @@ static int rtfcdebug = 0; #endif static int -rt_fixchange(rn, vp) - struct radix_node *rn; - void *vp; +rt_fixchange(struct radix_node *rn, void *vp) { struct rtentry *rt = (struct rtentry *)rn; struct rtfc_arg *ap = vp; @@ -854,8 +861,7 @@ rt_fixchange(rn, vp) * There probably is a function somewhere which does this... * if not, there should be. */ - len = imin(((struct sockaddr *)rt_key(rt0))->sa_len, - ((struct sockaddr *)rt_key(rt))->sa_len); + len = imin(rt_key(rt0)->sa_len, rt_key(rt)->sa_len); xk1 = (u_char *)rt_key(rt0); xm1 = (u_char *)rt_mask(rt0); @@ -863,8 +869,8 @@ rt_fixchange(rn, vp) /* 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) { + mlen = rt_key(rt->rt_parent)->sa_len; + if (mlen > rt_key(rt0)->sa_len) { #ifdef DEBUG if (rtfcdebug) printf("rt_fixchange: inserting a less " @@ -906,31 +912,31 @@ rt_fixchange(rn, vp) #define ROUNDUP(a) (a>0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) int -rt_setgate(rt0, dst, gate) - struct rtentry *rt0; - struct sockaddr *dst, *gate; +rt_setgate(struct rtentry *rt, struct sockaddr *dst, struct sockaddr *gate) { + /* XXX dst may be overwritten, can we move this to below */ + struct radix_node_head *rnh = rt_tables[dst->sa_family]; caddr_t new, old; 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]; + + RT_LOCK_ASSERT(rt); /* * A host route with the destination equal to the gateway * will interfere with keeping LLINFO in the routing * table, so disallow it. */ - if (((rt0->rt_flags & (RTF_HOST|RTF_GATEWAY|RTF_LLINFO)) == + 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)) { + 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(rt0)) - rtrequest(RTM_DELETE, (struct sockaddr *)rt_key(rt0), - rt0->rt_gateway, rt_mask(rt0), rt0->rt_flags, 0); + if (rt_key(rt)) + rtrequest(RTM_DELETE, rt_key(rt), + rt->rt_gateway, rt_mask(rt), rt->rt_flags, 0); return EADDRNOTAVAIL; } @@ -945,12 +951,12 @@ rt_setgate(rt0, dst, gate) R_Malloc(new, caddr_t, dlen + glen); if (new == 0) return ENOBUFS; - rt->rt_nodes->rn_key = new; + rt_key(rt) = new; } else { /* * otherwise just overwrite the old one */ - new = rt->rt_nodes->rn_key; + new = (caddr_t)rt_key(rt); old = 0; } @@ -966,6 +972,7 @@ rt_setgate(rt0, dst, gate) if (old) { Bcopy(dst, new, dlen); Free(old); + dst = gate = 0; /* XXX??? */ } /* @@ -989,10 +996,11 @@ rt_setgate(rt0, dst, gate) if (rt->rt_flags & RTF_GATEWAY) { rt->rt_gwroute = rtalloc1(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 */ } + RT_UNLOCK(rt->rt_gwroute); } /* @@ -1002,6 +1010,7 @@ rt_setgate(rt0, dst, gate) */ if (!(rt->rt_flags & RTF_HOST) && rt_mask(rt) != 0) { struct rtfc_arg arg; + arg.rnh = rnh; arg.rt0 = rt; RADIX_NODE_HEAD_LOCK(rnh); @@ -1014,8 +1023,7 @@ rt_setgate(rt0, dst, gate) } static void -rt_maskedcopy(src, dst, netmask) - struct sockaddr *src, *dst, *netmask; +rt_maskedcopy(struct sockaddr *src, struct sockaddr *dst, struct sockaddr *netmask) { register u_char *cp1 = (u_char *)src; register u_char *cp2 = (u_char *)dst; @@ -1038,9 +1046,7 @@ rt_maskedcopy(src, dst, netmask) * for an interface. */ int -rtinit(ifa, cmd, flags) - register struct ifaddr *ifa; - int cmd, flags; +rtinit(struct ifaddr *ifa, int cmd, int flags) { register struct rtentry *rt; register struct sockaddr *dst; @@ -1112,19 +1118,23 @@ bad: /* * notify any listening routing agents of the change */ + RT_LOCK(rt); rt_newaddrmsg(cmd, ifa, error, rt); if (cmd == RTM_DELETE) { /* * If we are deleting, and we found an entry, then * it's been removed from the tree.. now throw it away. */ - RTFREE(rt); - } else if (cmd == RTM_ADD) { - /* - * We just wanted to add it.. we don't actually - * need a reference. - */ - rt->rt_refcnt--; + RTFREE_LOCKED(rt); + } else { + if (cmd == RTM_ADD) { + /* + * We just wanted to add it.. we don't actually + * need a reference. + */ + rt->rt_refcnt--; + } + RT_UNLOCK(rt); } } if (m) @@ -1132,55 +1142,78 @@ bad: return (error); } +/* + * Validate the route rt0 to the specified destination. 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. + * + * On return lrt contains the route to the destination and lrt0 + * contains the route to the next hop. Their values are meaningul + * ONLY if no error is returned. + * + * This routine is invoked on each layer 2 output path, prior to + * encapsulating outbound packets. + */ int -rt_check(lrt, lrt0, dst) - struct rtentry **lrt; - struct rtentry **lrt0; - struct sockaddr *dst; +rt_check(struct rtentry **lrt, struct rtentry **lrt0, struct sockaddr *dst) { +#define senderr(x) { error = x ; goto bad; } struct rtentry *rt; struct rtentry *rt0; int error; - rt = *lrt; rt0 = *lrt0; - error = 0; - rt = rt0; - - if (rt != NULL) { + if (rt) { + /* NB: the locking here is tortuous... */ + RT_LOCK(rt); if ((rt->rt_flags & RTF_UP) == 0) { - rt0 = rt = rtalloc1(dst, 1, 0UL); - if (rt0 != NULL) + RT_UNLOCK(rt); + rt = rtalloc1(dst, 1, 0UL); + if (rt != NULL) { rt->rt_refcnt--; - else + RT_UNLOCK(rt); + } else senderr(EHOSTUNREACH); + rt0 = rt; } + /* XXX BSD/OS checks dst->sa_family != AF_NS */ if (rt->rt_flags & RTF_GATEWAY) { - if (rt->rt_gwroute == NULL) + if (rt->rt_gwroute == 0) goto lookup; - rt = rt->rt_gwroute; + RT_LOCK(rt); /* NB: gwroute */ if ((rt->rt_flags & RTF_UP) == 0) { - rtfree(rt); + rtfree(rt); /* unlock gwroute */ rt = rt0; lookup: - rt->rt_gwroute = rtalloc1(rt->rt_gateway, 1, 0UL); - rt = rt->rt_gwroute; - if (rt == NULL) + RT_UNLOCK(rt0); + rt = rtalloc1(rt->rt_gateway, 1, 0UL); + RT_LOCK(rt0); + rt0->rt_gwroute = rt; + if (rt == 0) { + RT_UNLOCK(rt0); senderr(EHOSTUNREACH); + } } + RT_UNLOCK(rt0); } - if (rt->rt_flags & RTF_REJECT) - if (rt->rt_rmx.rmx_expire == 0 || - time_second < rt->rt_rmx.rmx_expire) - senderr(rt == rt0 ? EHOSTDOWN : EHOSTUNREACH); + /* XXX why are we inspecting rmx_expire? */ + error = (rt->rt_flags & RTF_REJECT) && + (rt->rt_rmx.rmx_expire == 0 || + time_second < rt->rt_rmx.rmx_expire); + RT_UNLOCK(rt); + if (error) + senderr(rt == rt0 ? EHOSTDOWN : EHOSTUNREACH); } - -bad: - *lrt = rt; + *lrt = rt; /* NB: return unlocked */ *lrt0 = rt0; + return (0); +bad: + /* NB: lrt and lrt0 should not be interpreted if error is non-zero */ return (error); +#undef senderr } /* This must be before ip6_init2(), which is now SI_ORDER_MIDDLE */ |