X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/a9f3e17433e07b2aa0bc51e8194a7fd7952fe44f..274c3d56636e19a3069b6b5263a9f5e7affe53ea:/usr/src/sys/net/route.c diff --git a/usr/src/sys/net/route.c b/usr/src/sys/net/route.c index f9b9e232e5..9b44703256 100644 --- a/usr/src/sys/net/route.c +++ b/usr/src/sys/net/route.c @@ -1,64 +1,86 @@ -/* route.c 4.6 82/03/31 */ +/* route.c 6.1 83/07/29 */ #include "../h/param.h" #include "../h/systm.h" #include "../h/mbuf.h" #include "../h/protosw.h" #include "../h/socket.h" +#include "../h/dir.h" +#include "../h/user.h" #include "../h/ioctl.h" -#include "../net/in.h" -#include "../net/in_systm.h" +#include "../h/errno.h" + #include "../net/if.h" #include "../net/af.h" #include "../net/route.h" -#include + +int rttrash; /* routes not in table but not freed */ +struct sockaddr wildcard; /* zero valued cookie for wildcard searches */ /* * Packet routing routines. */ - rtalloc(ro) register struct route *ro; { - register struct rtentry *rt, *rtmin; + register struct rtentry *rt; register struct mbuf *m; - register int hash, (*match)(); - struct afhash h; + register unsigned hash; struct sockaddr *dst = &ro->ro_dst; - int af = dst->sa_family; + int (*match)(), doinghost; + struct afhash h; + u_int af = dst->sa_family; + struct rtentry *rtmin; + struct mbuf **table; -COUNT(RTALLOC); if (ro->ro_rt && ro->ro_rt->rt_ifp) /* XXX */ return; + if (af >= AF_MAX) + return; (*afswitch[af].af_hash)(dst, &h); - rtmin = 0, hash = h.afh_hosthash; - for (m = rthost[hash % RTHASHSIZ]; m; m = m->m_next) { - rt = mtod(m, struct rtentry *); - if (rt->rt_hash != hash) - continue; - if (bcmp((caddr_t)&rt->rt_dst, (caddr_t)dst, sizeof (*dst))) - continue; - if (rtmin == 0 || rt->rt_use < rtmin->rt_use) - rtmin = rt; - } - if (rtmin) - goto found; - - hash = h.afh_nethash; + rtmin = 0; match = afswitch[af].af_netmatch; - for (m = rtnet[hash % RTHASHSIZ]; m; m = m->m_next) { + hash = h.afh_hosthash, table = rthost, doinghost = 1; +again: + for (m = table[hash % RTHASHSIZ]; m; m = m->m_next) { rt = mtod(m, struct rtentry *); if (rt->rt_hash != hash) continue; - if (rt->rt_dst.sa_family != af || !(*match)(&rt->rt_dst, dst)) + if ((rt->rt_flags & RTF_UP) == 0 || + (rt->rt_ifp->if_flags & IFF_UP) == 0) continue; + if (doinghost) { + if (bcmp((caddr_t)&rt->rt_dst, (caddr_t)dst, + sizeof (*dst))) + continue; + } else { + if (rt->rt_dst.sa_family != af || + !(*match)(&rt->rt_dst, dst)) + continue; + } if (rtmin == 0 || rt->rt_use < rtmin->rt_use) rtmin = rt; } -found: + if (rtmin == 0 && doinghost) { + doinghost = 0; + hash = h.afh_nethash, table = rtnet; + goto again; + } + /* + * Check for wildcard gateway, by convention network 0. + */ + if (rtmin == 0 && dst != &wildcard) { + dst = &wildcard, hash = 0; + goto again; + } ro->ro_rt = rtmin; - if (rtmin) - rtmin->rt_refcnt++; + if (rtmin == 0) { + rtstat.rts_unreach++; + return; + } + rtmin->rt_refcnt++; + if (dst == &wildcard) + rtstat.rts_wildcard++; } rtfree(rt) @@ -66,115 +88,184 @@ rtfree(rt) { if (rt == 0) - panic("freeroute"); + panic("rtfree"); rt->rt_refcnt--; - /* on refcnt == 0 reclaim? notify someone? */ + if (rt->rt_refcnt == 0 && (rt->rt_flags&RTF_UP) == 0) { + rttrash--; + (void) m_free(dtom(rt)); + } +} + +/* + * Force a routing table entry to the specified + * 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 or higher + * + * Should notify all parties with a reference to + * the route that it's changed (so, for instance, + * current round trip time estimates could be flushed), + * but we have no back pointers at the moment. + */ +rtredirect(dst, gateway) + struct sockaddr *dst, *gateway; +{ + struct route ro; + register struct rtentry *rt; + + /* verify the gateway is directly reachable */ + if (if_ifwithnet(gateway) == 0) { + rtstat.rts_badredirect++; + return; + } + ro.ro_dst = *dst; + ro.ro_rt = 0; + rtalloc(&ro); + rt = ro.ro_rt; + /* + * Create a new entry if we just got back a wildcard entry + * or the the lookup failed. This is necessary for hosts + * which use routing redirects generated by smart gateways + * to dynamically build the routing tables. + */ + if (rt && + (*afswitch[dst->sa_family].af_netmatch)(&wildcard, &rt->rt_dst)) { + rtfree(rt); + rt = 0; + } + if (rt == 0) { + rtinit(dst, gateway, RTF_GATEWAY); + rtstat.rts_dynamic++; + return; + } + /* + * Don't listen to the redirect if it's + * for a route to an interface. + */ + if (rt->rt_flags & RTF_GATEWAY) { + /* + * Smash the current notion of the gateway to + * this destination. This is probably not right, + * as it's conceivable a flurry of redirects could + * cause the gateway value to fluctuate wildly during + * dynamic routing reconfiguration. + */ + rt->rt_gateway = *gateway; + rtfree(rt); + rtstat.rts_newgateway++; + return; + } +} + +/* + * Routing table ioctl interface. + */ +rtioctl(cmd, data) + int cmd; + caddr_t data; +{ + + if (cmd != SIOCADDRT && cmd != SIOCDELRT) + return (EINVAL); + if (!suser()) + return (u.u_error); + return (rtrequest(cmd, (struct rtentry *)data)); } -#define equal(a1, a2) \ - (bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof (struct sockaddr)) == 0) /* * Carry out a request to change the routing table. Called by - * interfaces at boot time to make their ``local routes'' known - * and for ioctl's. + * interfaces at boot time to make their ``local routes'' known, + * for ioctl's, and as the result of routing redirects. */ -rtrequest(req, new) +rtrequest(req, entry) int req; - register struct rtentry *new; + register struct rtentry *entry; { - register struct rtentry *rt; register struct mbuf *m, **mprev; - register int hash, (*match)(); - register struct sockaddr *sa = &new->rt_dst; - register struct sockaddr *gate = &new->rt_gateway; + register struct rtentry *rt; struct afhash h; - struct mbuf **oldmprev; - int af = sa->sa_family, doinghost, s, error = 0; - -COUNT(RTREQUEST); - (*afswitch[af].af_hash)(sa, &h); - hash = h.afh_hosthash; - mprev = &rthost[hash % RTHASHSIZ]; - doinghost = 1; + int s, error = 0, hash, (*match)(); + u_int af; + struct ifnet *ifp; + + af = entry->rt_dst.sa_family; + if (af >= AF_MAX) + return (EAFNOSUPPORT); + (*afswitch[af].af_hash)(&entry->rt_dst, &h); + if (entry->rt_flags & RTF_HOST) { + hash = h.afh_hosthash; + mprev = &rthost[hash % RTHASHSIZ]; + } else { + hash = h.afh_nethash; + mprev = &rtnet[hash % RTHASHSIZ]; + } + match = afswitch[af].af_netmatch; s = splimp(); -again: for (; m = *mprev; mprev = &m->m_next) { rt = mtod(m, struct rtentry *); if (rt->rt_hash != hash) continue; - if (doinghost) { - if (!equal(&rt->rt_dst, sa)) + if (entry->rt_flags & RTF_HOST) { +#define equal(a1, a2) \ + (bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof (struct sockaddr)) == 0) + if (!equal(&rt->rt_dst, &entry->rt_dst)) continue; } else { - if (rt->rt_dst.sa_family != sa->sa_family || - (*match)(&rt->rt_dst, sa) == 0) + if (rt->rt_dst.sa_family != entry->rt_dst.sa_family || + (*match)(&rt->rt_dst, &entry->rt_dst) == 0) continue; } - /* require full match on deletions */ - if (req == SIOCDELRT && !equal(&rt->rt_gateway, gate)) - continue; - /* don't keep multiple identical entries */ - if (req == SIOCADDRT && equal(&rt->rt_gateway, gate)) { - error = EEXIST; - goto bad; - } - break; - } - if (m == 0 && doinghost) { - hash = h.afh_nethash; - oldmprev = mprev; - mprev = &rtnet[hash % RTHASHSIZ]; - match = afswitch[af].af_netmatch; - doinghost = 0; - goto again; - } - - if (m == 0 && req != SIOCADDRT) { - error = ESRCH; - goto bad; + if (equal(&rt->rt_gateway, &entry->rt_gateway)) + break; } -found: switch (req) { case SIOCDELRT: - rt->rt_flags &= ~RTF_UP; - if (rt->rt_refcnt > 0) /* should we notify protocols? */ - error = EBUSY; - else - *mprev = m_free(m); - break; - - case SIOCCHGRT: - rt->rt_flags = new->rt_flags; - if (rt->rt_refcnt > 0) - error = EBUSY; - else if (!equal(&rt->rt_gateway, gate)) - goto newneighbor; + if (m == 0) { + error = ESRCH; + goto bad; + } + *mprev = m->m_next; + if (rt->rt_refcnt > 0) { + rt->rt_flags &= ~RTF_UP; + rttrash++; + m->m_next = 0; + } else + (void) m_free(m); break; case SIOCADDRT: - m = m_get(M_DONTWAIT); + if (m) { + error = EEXIST; + goto bad; + } + ifp = if_ifwithaddr(&entry->rt_gateway); + if (ifp == 0) { + ifp = if_ifwithnet(&entry->rt_gateway); + if (ifp == 0) { + error = ENETUNREACH; + goto bad; + } + } + m = m_get(M_DONTWAIT, MT_RTABLE); if (m == 0) { error = ENOBUFS; - break; + goto bad; } + *mprev = m; m->m_off = MMINOFF; m->m_len = sizeof (struct rtentry); rt = mtod(m, struct rtentry *); - *rt = *new; - if (new->rt_flags & RTF_HOST) { - rt->rt_hash = h.afh_hosthash; - *oldmprev = m; - } else { - rt->rt_hash = h.afh_nethash; - *mprev = m; - } - rt->rt_use = 0; + rt->rt_hash = hash; + rt->rt_dst = entry->rt_dst; + rt->rt_gateway = entry->rt_gateway; + rt->rt_flags = + RTF_UP | (entry->rt_flags & (RTF_HOST|RTF_GATEWAY)); rt->rt_refcnt = 0; -newneighbor: - rt->rt_ifp = if_ifwithnet(gate); - if (rt->rt_ifp == 0) - rt->rt_flags &= ~RTF_UP; + rt->rt_use = 0; + rt->rt_ifp = ifp; break; } bad: @@ -191,14 +282,17 @@ rtinit(dst, gateway, flags) int flags; { struct rtentry route; - struct route ro; + int cmd; + if (flags == -1) { + cmd = (int)SIOCDELRT; + flags = 0; + } else { + cmd = (int)SIOCADDRT; + } + bzero((caddr_t)&route, sizeof (route)); route.rt_dst = *dst; route.rt_gateway = *gateway; route.rt_flags = flags; - route.rt_use = 0; - (void) rtrequest(SIOCADDRT, &route); - ro.ro_rt = 0; - ro.ro_dst = *dst; - rtalloc(&ro); + (void) rtrequest(cmd, &route); }