X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/4ab982ac5b2e8d4f2ed1b4212717ec799a3d35e5..0fe7c40f5f0f6af89c8c7dc61cc7eebf454d0a84:/usr/src/sys/net/route.c diff --git a/usr/src/sys/net/route.c b/usr/src/sys/net/route.c index 475b5102d9..ea93452f8f 100644 --- a/usr/src/sys/net/route.c +++ b/usr/src/sys/net/route.c @@ -1,175 +1,448 @@ -/* route.c 4.15 83/02/02 */ - -#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/if.h" -#include "../net/af.h" -#include "../net/route.h" -#include +/* + * Copyright (c) 1980, 1986, 1991 Regents of the University of California. + * All rights reserved. + * + * %sccs.include.redist.c% + * + * @(#)route.c 7.32 (Berkeley) %G% + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#ifdef NS +#include +#endif + +#define SA(p) ((struct sockaddr *)(p)) int rttrash; /* routes not in table but not freed */ +struct sockaddr wildcard; /* zero valued cookie for wildcard searches */ + +rtable_init(table) +void **table; +{ + struct domain *dom; + for (dom = domains; dom; dom = dom->dom_next) + if (dom->dom_rtattach) + dom->dom_rtattach(&table[dom->dom_family], + dom->dom_rtoffset); +} + +route_init() +{ + rn_init(); /* initialize all zeroes, all ones, mask table */ + rtable_init((void **)rt_tables); +} + /* * Packet routing routines. */ rtalloc(ro) register struct route *ro; { - register struct rtentry *rt, *rtmin; - register struct mbuf *m; - register int hash, (*match)(); - struct afhash h; - struct sockaddr *dst = &ro->ro_dst; - int af = dst->sa_family; - - 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 ((rt->rt_flags & RTF_UP) == 0 || - (rt->rt_ifp->if_flags & IFF_UP) == 0) - 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; - match = afswitch[af].af_netmatch; - for (m = rtnet[hash % RTHASHSIZ]; m; m = m->m_next) { - rt = mtod(m, struct rtentry *); - if (rt->rt_hash != hash) - continue; - if ((rt->rt_flags & RTF_UP) == 0 || - (rt->rt_ifp->if_flags & IFF_UP) == 0) - continue; - 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: - ro->ro_rt = rtmin; - if (rtmin) - rtmin->rt_refcnt++; + 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); +} + +struct rtentry * +rtalloc1(dst, report) + register struct sockaddr *dst; + int report; +{ + 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 rt_addrinfo info; + int s = splnet(), err = 0, msgtype = RTM_MISS; + + if (rnh && rnh->rnh_treetop && + (rn = rnh->rnh_match((caddr_t)dst, rnh->rnh_treetop)) && + ((rn->rn_flags & RNF_ROOT) == 0)) { + newrt = rt = (struct rtentry *)rn; + if (report && (rt->rt_flags & RTF_CLONING)) { + err = rtrequest(RTM_RESOLVE, dst, SA(0), + SA(0), 0, &newrt); + if (err) { + newrt = rt; + rt->rt_refcnt++; + goto miss; + } + if ((rt = newrt) && (rt->rt_flags & RTF_XRESOLVE)) { + msgtype = RTM_RESOLVE; + goto miss; + } + } else + rt->rt_refcnt++; + } else { + rtstat.rts_unreach++; + miss: if (report) { + bzero((caddr_t)&info, sizeof(info)); + info.rti_info[RTAX_DST] = dst; + rt_missmsg(msgtype, &info, 0, err); + } + } + splx(s); + return (newrt); } rtfree(rt) register struct rtentry *rt; { + register struct ifaddr *ifa; if (rt == 0) panic("rtfree"); rt->rt_refcnt--; - if (rt->rt_refcnt == 0 && (rt->rt_flags&RTF_UP) == 0) { + if (rt->rt_refcnt <= 0 && (rt->rt_flags & RTF_UP) == 0) { + if (rt->rt_nodes->rn_flags & (RNF_ACTIVE | RNF_ROOT)) + panic ("rtfree 2"); rttrash--; - (void) m_free(dtom(rt)); + if (rt->rt_refcnt < 0) { + printf("rtfree: %x not freed (neg refs)\n", rt); + return; + } + ifa = rt->rt_ifa; + IFAFREE(ifa); + Free(rt_key(rt)); + Free(rt); } } +int ifafree_verbose = 1; /* - * 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. + * We are still debugging potential overfreeing of ifaddr's */ -rtrequest(req, entry) - int req; - register struct rtentry *entry; +void +ifafree(ifa) + register struct ifaddr *ifa; +{ + if (ifa == 0) + panic("ifafree"); + if (ifa->ifa_refcnt < 0) + printf("ifafree: %x ref %d\n", ifa, ifa->ifa_refcnt); + if (ifa->ifa_refcnt == 0 && ifafree_verbose) + printf("ifafree: %x not freed.\n", ifa); + ifa->ifa_refcnt--; +} + +/* + * 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 + * + */ +rtredirect(dst, gateway, netmask, flags, src, rtp) + struct sockaddr *dst, *gateway, *netmask, *src; + int flags; + struct rtentry **rtp; { - register struct mbuf *m, **mprev; register struct rtentry *rt; - struct afhash h; - int af, s, error = 0, hash, (*match)(); - 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(); - for (; m = *mprev; mprev = &m->m_next) { - rt = mtod(m, struct rtentry *); - if (rt->rt_hash != hash) - continue; - 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; + int error = 0; + short *stat = 0; + struct rt_addrinfo info; + + /* verify the gateway is directly reachable */ + if (ifa_ifwithnet(gateway) == 0) { + error = ENETUNREACH; + goto out; + } + rt = rtalloc1(dst, 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. + */ +#define equal(a1, a2) (bcmp((caddr_t)(a1), (caddr_t)(a2), (a1)->sa_len) == 0) + if (!(flags & RTF_DONE) && rt && !equal(src, rt->rt_gateway)) + error = EINVAL; + else if (ifa_ifwithaddr(gateway)) + error = EHOSTUNREACH; + if (error) + goto done; + /* + * 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 == 0) || (rt_mask(rt) && rt_mask(rt)->sa_len < 2)) + goto create; + /* + * 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. + */ + create: + flags |= RTF_GATEWAY | RTF_DYNAMIC; + error = rtrequest((int)RTM_ADD, dst, gateway, + SA(0), flags, + (struct rtentry **)0); + stat = &rtstat.rts_dynamic; } else { - if (rt->rt_dst.sa_family != entry->rt_dst.sa_family || - (*match)(&rt->rt_dst, &entry->rt_dst) == 0) - continue; + /* + * Smash the current notion of the gateway to + * this destination. Should check about netmask!!! + */ + rt->rt_flags |= RTF_MODIFIED; + flags |= RTF_MODIFIED; + stat = &rtstat.rts_newgateway; + rt_setgate(rt, rt_key(rt), gateway); } - if (equal(&rt->rt_gateway, &entry->rt_gateway)) + } else + error = EHOSTUNREACH; +done: + if (rt) { + if (rtp && !error) + *rtp = rt; + else + rtfree(rt); + } +out: + if (error) + rtstat.rts_badredirect++; + else if (stat != NULL) + (*stat)++; + bzero((caddr_t)&info, sizeof(info)); + info.rti_info[RTAX_DST] = dst; + info.rti_info[RTAX_GATEWAY] = gateway; + info.rti_info[RTAX_NETMASK] = netmask; + info.rti_info[RTAX_AUTHOR] = src; + rt_missmsg(RTM_REDIRECT, &info, flags, error); +} + +/* +* Routing table ioctl interface. +*/ +rtioctl(req, data, p) + int req; + caddr_t data; + struct proc *p; +{ +#ifndef COMPAT_43 + return (EOPNOTSUPP); +#else + register struct ortentry *entry = (struct ortentry *)data; + int error; + struct sockaddr *netmask = 0; + + if (req == SIOCADDRT) + req = RTM_ADD; + else if (req == SIOCDELRT) + req = RTM_DELETE; + else + return (EINVAL); + + if (error = suser(p->p_ucred, &p->p_acflag)) + return (error); +#if BYTE_ORDER != BIG_ENDIAN + if (entry->rt_dst.sa_family == 0 && entry->rt_dst.sa_len < 16) { + entry->rt_dst.sa_family = entry->rt_dst.sa_len; + entry->rt_dst.sa_len = 16; + } + if (entry->rt_gateway.sa_family == 0 && entry->rt_gateway.sa_len < 16) { + entry->rt_gateway.sa_family = entry->rt_gateway.sa_len; + entry->rt_gateway.sa_len = 16; + } +#else + if (entry->rt_dst.sa_len == 0) + entry->rt_dst.sa_len = 16; + if (entry->rt_gateway.sa_len == 0) + entry->rt_gateway.sa_len = 16; +#endif + if ((entry->rt_flags & RTF_HOST) == 0) + switch (entry->rt_dst.sa_family) { +#ifdef INET + case AF_INET: + { + extern struct sockaddr_in icmpmask; + struct sockaddr_in *dst_in = + (struct sockaddr_in *)&entry->rt_dst; + + in_sockmaskof(dst_in->sin_addr, &icmpmask); + netmask = (struct sockaddr *)&icmpmask; + } break; +#endif +#ifdef NS + case AF_NS: + { + extern struct sockaddr_ns ns_netmask; + netmask = (struct sockaddr *)&ns_netmask; + } +#endif + } + error = rtrequest(req, &(entry->rt_dst), &(entry->rt_gateway), netmask, + entry->rt_flags, (struct rtentry **)0); + /* rt_missmsg((req == RTM_ADD ? RTM_OLDADD : RTM_OLDDEL), + &(entry->rt_dst), &(entry->rt_gateway), + netmask, SA(0), entry->rt_flags, error); */ + return (error); +#endif +} + +struct ifaddr * +ifa_ifwithroute(flags, dst, gateway) +int flags; +struct sockaddr *dst, *gateway; +{ + register struct ifaddr *ifa; + if ((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 (flags & RTF_HOST) + ifa = ifa_ifwithdstaddr(dst); + if (ifa == 0) + ifa = ifa_ifwithaddr(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(gateway); } - switch (req) { + if (ifa == 0) + ifa = ifa_ifwithnet(gateway); + if (ifa == 0) { + struct rtentry *rt = rtalloc1(dst, 0); + if (rt == 0) + return (0); + rt->rt_refcnt--; + if ((ifa = rt->rt_ifa) == 0) + return (0); + } + if (ifa->ifa_addr->sa_family != dst->sa_family) { + struct ifaddr *oifa = ifa, *ifaof_ifpforaddr(); + ifa = ifaof_ifpforaddr(dst, ifa->ifa_ifp); + if (ifa == 0) + ifa = oifa; + } + return (ifa); +} + +#define ROUNDUP(a) (a>0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) + +rtrequest(req, dst, gateway, netmask, flags, ret_nrt) + int req, flags; + struct sockaddr *dst, *gateway, *netmask; + struct rtentry **ret_nrt; +{ + int s = splnet(); int error = 0; + register struct rtentry *rt; + register struct radix_node *rn; + register struct radix_node_head *rnh; + struct ifaddr *ifa, *ifa_ifwithdstaddr(); + struct sockaddr *ndst; +#define senderr(x) { error = x ; goto bad; } - case SIOCDELRT: - if (m == 0) { - error = ESRCH; - goto bad; + if ((rnh = rt_tables[dst->sa_family]) == 0) + senderr(ESRCH); + if (flags & RTF_HOST) + netmask = 0; + switch (req) { + case RTM_DELETE: + if ((rn = rnh->rnh_delete((caddr_t)dst, (caddr_t)netmask, + rnh->rnh_treetop)) == 0) + senderr(ESRCH); + if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT)) + panic ("rtrequest delete"); + rt = (struct rtentry *)rn; + rt->rt_flags &= ~RTF_UP; + if (rt->rt_gwroute) { + rt = rt->rt_gwroute; RTFREE(rt); + (rt = (struct rtentry *)rn)->rt_gwroute = 0; + } + if ((ifa = rt->rt_ifa) && ifa->ifa_rtrequest) + ifa->ifa_rtrequest(RTM_DELETE, rt, SA(0)); + rttrash++; + if (ret_nrt) + *ret_nrt = rt; + else if (rt->rt_refcnt <= 0) { + rt->rt_refcnt++; + rtfree(rt); } - *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: - if (m) { - error = EEXIST; - goto bad; + case RTM_RESOLVE: + if (ret_nrt == 0 || (rt = *ret_nrt) == 0) + senderr(EINVAL); + ifa = rt->rt_ifa; + flags = rt->rt_flags & ~RTF_CLONING; + gateway = rt->rt_gateway; + if ((netmask = rt->rt_genmask) == 0) + flags |= RTF_HOST; + goto makeroute; + + case RTM_ADD: + if ((ifa = ifa_ifwithroute(flags, dst, gateway)) == 0) + senderr(ENETUNREACH); + makeroute: + R_Malloc(rt, struct rtentry *, sizeof(*rt)); + if (rt == 0) + senderr(ENOBUFS); + Bzero(rt, sizeof(*rt)); + rt->rt_flags = RTF_UP | flags; + if (rt_setgate(rt, dst, gateway)) { + Free(rt); + senderr(ENOBUFS); } - ifp = if_ifwithaddr(&entry->rt_gateway); - if (ifp == 0) { - ifp = if_ifwithnet(&entry->rt_gateway); - if (ifp == 0) { - error = ENETUNREACH; - goto bad; - } + ndst = rt_key(rt); + if (netmask) { + rt_maskedcopy(dst, ndst, netmask); + } else + Bcopy(dst, ndst, dst->sa_len); + rn = rnh->rnh_add((caddr_t)ndst, (caddr_t)netmask, + rnh->rnh_treetop, rt->rt_nodes); + if (rn == 0) { + if (rt->rt_gwroute) + rtfree(rt->rt_gwroute); + Free(rt_key(rt)); + Free(rt); + senderr(EEXIST); } - m = m_get(M_DONTWAIT, MT_RTABLE); - if (m == 0) { - error = ENOBUFS; - goto bad; + ifa->ifa_refcnt++; + rt->rt_ifa = ifa; + rt->rt_ifp = ifa->ifa_ifp; + if (req == RTM_RESOLVE) + rt->rt_rmx = (*ret_nrt)->rt_rmx; /* copy metrics */ + if (ifa->ifa_rtrequest) + ifa->ifa_rtrequest(req, rt, SA(ret_nrt ? *ret_nrt : 0)); + if (ret_nrt) { + *ret_nrt = rt; + rt->rt_refcnt++; } - *mprev = m; - m->m_off = MMINOFF; - m->m_len = sizeof (struct rtentry); - rt = mtod(m, struct rtentry *); - 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; - rt->rt_use = 0; - rt->rt_ifp = ifp; break; } bad: @@ -177,19 +450,116 @@ bad: return (error); } +rt_setgate(rt0, dst, gate) +struct rtentry *rt0; +struct sockaddr *dst, *gate; +{ + caddr_t new, old; + int dlen = ROUNDUP(dst->sa_len), glen = ROUNDUP(gate->sa_len); + register struct rtentry *rt = rt0; + + if (rt->rt_gateway == 0 || glen > ROUNDUP(rt->rt_gateway->sa_len)) { + old = (caddr_t)rt_key(rt); + R_Malloc(new, caddr_t, dlen + glen); + if (new == 0) + return 1; + rt->rt_nodes->rn_key = new; + } else { + new = rt->rt_nodes->rn_key; + old = 0; + } + Bcopy(gate, (rt->rt_gateway = (struct sockaddr *)(new + dlen)), glen); + if (old) { + Bcopy(dst, new, dlen); + Free(old); + } + if (rt->rt_gwroute) { + rt = rt->rt_gwroute; RTFREE(rt); + rt = rt0; rt->rt_gwroute = 0; + } + if (rt->rt_flags & RTF_GATEWAY) { + rt->rt_gwroute = rtalloc1(gate, 1); + } + return 0; +} + +rt_maskedcopy(src, dst, netmask) +struct sockaddr *src, *dst, *netmask; +{ + register u_char *cp1 = (u_char *)src; + register u_char *cp2 = (u_char *)dst; + register u_char *cp3 = (u_char *)netmask; + u_char *cplim = cp2 + *cp3; + u_char *cplim2 = cp2 + *cp1; + + *cp2++ = *cp1++; *cp2++ = *cp1++; /* copies sa_len & sa_family */ + cp3 += 2; + if (cplim > cplim2) + cplim = cplim2; + while (cp2 < cplim) + *cp2++ = *cp1++ & *cp3++; + if (cp2 < cplim2) + bzero((caddr_t)cp2, (unsigned)(cplim2 - cp2)); +} /* * Set up a routing table entry, normally * for an interface. */ -rtinit(dst, gateway, flags) - struct sockaddr *dst, *gateway; - int flags; +rtinit(ifa, cmd, flags) + register struct ifaddr *ifa; + int cmd, flags; { - struct rtentry route; + register struct rtentry *rt; + register struct sockaddr *dst; + register struct sockaddr *deldst; + struct mbuf *m = 0; + struct rtentry *nrt = 0; + int error; - bzero((caddr_t)&route, sizeof (route)); - route.rt_dst = *dst; - route.rt_gateway = *gateway; - route.rt_flags = flags; - (void) rtrequest((int)SIOCADDRT, &route); + dst = flags & RTF_HOST ? ifa->ifa_dstaddr : ifa->ifa_addr; + if (cmd == RTM_DELETE) { + if ((flags & RTF_HOST) == 0 && ifa->ifa_netmask) { + m = m_get(M_WAIT, MT_SONAME); + deldst = mtod(m, struct sockaddr *); + rt_maskedcopy(dst, deldst, ifa->ifa_netmask); + dst = deldst; + } + if (rt = rtalloc1(dst, 0)) { + rt->rt_refcnt--; + if (rt->rt_ifa != ifa) { + if (m) + (void) m_free(m); + return (flags & RTF_HOST ? EHOSTUNREACH + : ENETUNREACH); + } + } + } + error = rtrequest(cmd, dst, ifa->ifa_addr, ifa->ifa_netmask, + flags | ifa->ifa_flags, &nrt); + if (m) + (void) m_free(m); + if (cmd == RTM_DELETE && error == 0 && (rt = nrt)) { + rt_newaddrmsg(cmd, ifa, error, nrt); + if (rt->rt_refcnt <= 0) { + rt->rt_refcnt++; + rtfree(rt); + } + } + if (cmd == RTM_ADD && error == 0 && (rt = nrt)) { + rt->rt_refcnt--; + if (rt->rt_ifa != ifa) { + printf("rtinit: wrong ifa (%x) was (%x)\n", ifa, + rt->rt_ifa); + if (rt->rt_ifa->ifa_rtrequest) + rt->rt_ifa->ifa_rtrequest(RTM_DELETE, rt, SA(0)); + IFAFREE(rt->rt_ifa); + rt->rt_ifa = ifa; + rt->rt_ifp = ifa->ifa_ifp; + ifa->ifa_refcnt++; + if (ifa->ifa_rtrequest) + ifa->ifa_rtrequest(RTM_ADD, rt, SA(0)); + } + rt_newaddrmsg(cmd, ifa, error, nrt); + } + return (error); }