X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/f6311fb6ab21020b5f64fc0f4582a0fcf4f4c9a6..5b519e941140a2be657d68865fade9f5aa577b79:/usr/src/sys/net/route.c diff --git a/usr/src/sys/net/route.c b/usr/src/sys/net/route.c index 9692aeb384..7198fa901e 100644 --- a/usr/src/sys/net/route.c +++ b/usr/src/sys/net/route.c @@ -1,177 +1,323 @@ -/* route.c 4.4 82/03/30 */ - -#include "../h/param.h" -#include "../h/systm.h" -#include "../h/mbuf.h" -#include "../h/protosw.h" -#include "../h/socket.h" -#include "../h/ioctl.h" -#include "../net/in.h" -#include "../net/in_systm.h" -#include "../net/if.h" -#include "../net/af.h" -#include "../net/route.h" -#include +/* + * Copyright (c) 1980, 1986 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of California at Berkeley. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + * + * @(#)route.c 7.3 (Berkeley) %G% + */ + +#include "param.h" +#include "systm.h" +#include "mbuf.h" +#include "protosw.h" +#include "socket.h" +#include "dir.h" +#include "user.h" +#include "ioctl.h" +#include "errno.h" + +#include "if.h" +#include "af.h" +#include "route.h" + +int rttrash; /* routes not in table but not freed */ +struct sockaddr wildcard; /* zero valued cookie for wildcard searches */ +int rthashsize = RTHASHSIZ; /* for netstat, etc. */ /* * 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; - struct afhash h; + register u_long hash; struct sockaddr *dst = &ro->ro_dst; - int af = dst->sa_family, doinghost; + int (*match)(), doinghost, s; + struct afhash h; + u_int af = dst->sa_family; + struct mbuf **table; -COUNT(RTALLOC); - if (ro->ro_rt && ro->ro_rt->rt_ifp) /* XXX */ + if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP)) + return; /* XXX */ + if (af >= AF_MAX) return; (*afswitch[af].af_hash)(dst, &h); - hash = h.afh_hosthash; - rtmin = 0, doinghost = 1; + match = afswitch[af].af_netmatch; + hash = h.afh_hosthash, table = rthost, doinghost = 1; + s = splnet(); again: - m = routehash[hash % RTHASHSIZ]; - for (; m; m = m->m_next) { + for (m = table[RTHASHMOD(hash)]; m; m = m->m_next) { rt = mtod(m, struct rtentry *); - if (rt->rt_hash[doinghost] != hash) + if (rt->rt_hash != hash) + continue; + if ((rt->rt_flags & RTF_UP) == 0 || + (rt->rt_ifp->if_flags & IFF_UP) == 0) continue; if (doinghost) { -#define equal(a1, a2) \ - (bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof(struct sockaddr)) == 0) - if (!equal(&rt->rt_dst, dst)) + if (bcmp((caddr_t)&rt->rt_dst, (caddr_t)dst, + sizeof (*dst))) continue; } else { - if (rt->rt_dst.sa_family != af) - continue; - if ((*afswitch[af].af_netmatch)(&rt->rt_dst, dst) == 0) + if (rt->rt_dst.sa_family != af || + !(*match)(&rt->rt_dst, dst)) continue; } - if (rtmin == 0 || rt->rt_use < rtmin->rt_use) - rtmin = rt; - } - if (rtmin) { - ro->ro_rt = rt; rt->rt_refcnt++; + splx(s); + if (dst == &wildcard) + rtstat.rts_wildcard++; + ro->ro_rt = rt; return; } if (doinghost) { doinghost = 0; - hash = h.afh_nethash; + hash = h.afh_nethash, table = rtnet; goto again; } - ro->ro_rt = 0; - return; + /* + * Check for wildcard gateway, by convention network 0. + */ + if (dst != &wildcard) { + dst = &wildcard, hash = 0; + goto again; + } + splx(s); + rtstat.rts_unreach++; } rtfree(rt) register struct rtentry *rt; { -COUNT(FREEROUTE); 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 + * + */ +rtredirect(dst, gateway, flags, src) + struct sockaddr *dst, *gateway, *src; + int flags; +{ + struct route ro; + register struct rtentry *rt; + + /* verify the gateway is directly reachable */ + if (ifa_ifwithnet(gateway) == 0) { + rtstat.rts_badredirect++; + return; + } + ro.ro_dst = *dst; + ro.ro_rt = 0; + rtalloc(&ro); + rt = ro.ro_rt; +#define equal(a1, a2) \ + (bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof(struct sockaddr)) == 0) + /* + * If the redirect isn't from our current router for this dst, + * it's either old or wrong. If it redirects us to ourselves, + * we have a routing loop, perhaps as a result of an interface + * going down recently. + */ + if ((rt && !equal(src, &rt->rt_gateway)) || ifa_ifwithaddr(gateway)) { + rtstat.rts_badredirect++; + if (rt) + rtfree(rt); + return; + } + /* + * 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, (int)SIOCADDRT, + (flags & RTF_HOST) | RTF_GATEWAY | RTF_DYNAMIC); + 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) { + if (((rt->rt_flags & RTF_HOST) == 0) && (flags & RTF_HOST)) { + /* + * Changing from route to net => route to host. + * Create new route, rather than smashing route to net. + */ + rtinit(dst, gateway, (int)SIOCADDRT, + flags | RTF_DYNAMIC); + rtstat.rts_dynamic++; + } else { + /* + * Smash the current notion of the gateway to + * this destination. + */ + rt->rt_gateway = *gateway; + rt->rt_flags |= RTF_MODIFIED; + rtstat.rts_newgateway++; + } + } else + rtstat.rts_badredirect++; + rtfree(rt); +} + +/* + * 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)); } /* * 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; - struct sockaddr *sa = &new->rt_dst; + struct mbuf **mfirst; + register struct rtentry *rt; struct afhash h; - int af = sa->sa_family, doinghost, s, error = 0; + int s, error = 0, (*match)(); + u_int af; + u_long hash; + struct ifaddr *ifa; + struct ifaddr *ifa_ifwithdstaddr(); -COUNT(RTREQUEST); - (*afswitch[af].af_hash)(sa, &h); - hash = h.afh_hosthash; - doinghost = 1; + 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[RTHASHMOD(hash)]; + } else { + hash = h.afh_nethash; + mprev = &rtnet[RTHASHMOD(hash)]; + } + match = afswitch[af].af_netmatch; s = splimp(); -again: - mprev = &routehash[hash % RTHASHSIZ]; - for (; m = *mprev; mprev = &m->m_next) { + for (mfirst = mprev; m = *mprev; mprev = &m->m_next) { rt = mtod(m, struct rtentry *); - if (rt->rt_hash[doinghost] != hash) + if (rt->rt_hash != hash) continue; - if (doinghost) { - if (!equal(&rt->rt_dst, &new->rt_dst)) + if (entry->rt_flags & RTF_HOST) { + if (!equal(&rt->rt_dst, &entry->rt_dst)) continue; } else { - if (rt->rt_dst.sa_family != af) - continue; - if ((*afswitch[af].af_netmatch)(&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, &new->rt_gateway)) - continue; - /* don't keep multiple identical entries */ - if (req == SIOCADDRT && - equal(&rt->rt_gateway, &new->rt_gateway)) { - error = EEXIST; - goto bad; - } - break; - } - if (m == 0 && doinghost) { - doinghost = 0; - hash = h.afh_nethash; - goto again; - } - if (m == 0 && req != SIOCADDRT) { - error = ESRCH; - goto bad; + if (equal(&rt->rt_gateway, &entry->rt_gateway)) + break; } 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, &new->rt_gateway)) - 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; + } + if ((entry->rt_flags & RTF_GATEWAY) == 0) { + /* + * If we are adding a route to an interface, + * and the interface is a pt to pt link + * we should search for the destination + * as our clue to the interface. Otherwise + * we can use the local address. + */ + ifa = 0; + if (entry->rt_flags & RTF_HOST) + ifa = ifa_ifwithdstaddr(&entry->rt_dst); + if (ifa == 0) + ifa = ifa_ifwithaddr(&entry->rt_gateway); + } else { + /* + * If we are adding a route to a remote net + * or host, the gateway may still be on the + * other end of a pt to pt link. + */ + ifa = ifa_ifwithdstaddr(&entry->rt_gateway); + } + if (ifa == 0) { + ifa = ifa_ifwithnet(&entry->rt_gateway); + if (ifa == 0) { + error = ENETUNREACH; + goto bad; + } + } + m = m_get(M_DONTWAIT, MT_RTABLE); if (m == 0) { error = ENOBUFS; - break; + goto bad; } + m->m_next = *mfirst; + *mfirst = m; m->m_off = MMINOFF; m->m_len = sizeof (struct rtentry); - *mprev = m; rt = mtod(m, struct rtentry *); - *rt = *new; - rt->rt_hash[0] = h.afh_nethash; - rt->rt_hash[1] = h.afh_hosthash; -newneighbor: - rt->rt_ifp = if_ifwithnet(&new->rt_gateway); - if (rt->rt_ifp == 0) - rt->rt_flags &= ~RTF_UP; - 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|RTF_DYNAMIC)); rt->rt_refcnt = 0; + rt->rt_use = 0; + rt->rt_ifp = ifa->ifa_ifp; break; } bad: @@ -183,19 +329,15 @@ bad: * Set up a routing table entry, normally * for an interface. */ -rtinit(dst, gateway, flags) +rtinit(dst, gateway, cmd, flags) struct sockaddr *dst, *gateway; - int flags; + int cmd, flags; { struct rtentry route; - struct route ro; + 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); }