MULTICAST is now standard
[unix-history] / usr / src / sys / net / route.c
index 7954ac8..ea93452 100644 (file)
@@ -1,49 +1,52 @@
 /*
 /*
- * Copyright (c) 1980, 1986 Regents of the University of California.
+ * Copyright (c) 1980, 1986, 1991 Regents of the University of California.
  * All rights reserved.
  *
  * %sccs.include.redist.c%
  *
  * All rights reserved.
  *
  * %sccs.include.redist.c%
  *
- *     @(#)route.c     7.20 (Berkeley) %G%
+ *     @(#)route.c     7.32 (Berkeley) %G%
  */
  */
-#include "param.h"
-#include "systm.h"
-#include "proc.h"
-#include "mbuf.h"
-#include "socket.h"
-#include "socketvar.h"
-#include "domain.h"
-#include "protosw.h"
-#include "ioctl.h"
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/ioctl.h>
 
 
-#include "if.h"
-#include "af.h"
-#include "route.h"
-#include "raw_cb.h"
+#include <net/if.h>
+#include <net/af.h>
+#include <net/route.h>
+#include <net/raw_cb.h>
 
 
-#include "../netinet/in.h"
-#include "../netinet/in_var.h"
+#include <netinet/in.h>
+#include <netinet/in_var.h>
 
 
-#include "../netns/ns.h"
-#include "machine/mtpr.h"
-#include "netisr.h"
+#ifdef NS
+#include <netns/ns.h>
+#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 */
 
 #define        SA(p) ((struct sockaddr *)(p))
 
 int    rttrash;                /* routes not in table but not freed */
 struct sockaddr wildcard;      /* zero valued cookie for wildcard searches */
-int    rthashsize = RTHASHSIZ; /* for netstat, etc. */
 
 
-static int rtinits_done = 0;
-struct radix_node_head *ns_rnhead, *in_rnhead;
-struct radix_node *rn_match(), *rn_delete(), *rn_addroute();
+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);
+}
 
 
-rtinitheads()
+route_init()
 {
 {
-       if (rtinits_done == 0 &&
-           rn_inithead(&ns_rnhead, 16, AF_NS) &&
-           rn_inithead(&in_rnhead, 32, AF_INET))
-               rtinits_done = 1;
+       rn_init();      /* initialize all zeroes, all ones, mask table */
+       rtable_init((void **)rt_tables);
 }
 
 /*
 }
 
 /*
@@ -62,30 +65,38 @@ rtalloc1(dst, report)
        register struct sockaddr *dst;
        int  report;
 {
        register struct sockaddr *dst;
        int  report;
 {
-       register struct radix_node_head *rnh;
+       register struct radix_node_head *rnh = rt_tables[dst->sa_family];
        register struct rtentry *rt;
        register struct radix_node *rn;
        struct rtentry *newrt = 0;
        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;
 
        int  s = splnet(), err = 0, msgtype = RTM_MISS;
 
-       for (rnh = radix_node_head; rnh && (dst->sa_family != rnh->rnh_af); )
-               rnh = rnh->rnh_next;
        if (rnh && rnh->rnh_treetop &&
        if (rnh && rnh->rnh_treetop &&
-           (rn = rn_match((caddr_t)dst, 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)) {
            ((rn->rn_flags & RNF_ROOT) == 0)) {
                newrt = rt = (struct rtentry *)rn;
                if (report && (rt->rt_flags & RTF_CLONING)) {
-                       if ((err = rtrequest(RTM_RESOLVE, dst, SA(0),
-                                             SA(0), 0, &newrt)) ||
-                           ((rt->rt_flags & RTF_XRESOLVE)
-                             && (msgtype = RTM_RESOLVE))) /* intended! */
-                           goto miss;
+                       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++;
                } else
                        rt->rt_refcnt++;
        } else {
                rtstat.rts_unreach++;
-       miss:   if (report)
-                       rt_missmsg(msgtype, dst, SA(0), SA(0), SA(0), 0, err);
+       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);
        }
        splx(s);
        return (newrt);
@@ -95,17 +106,42 @@ rtfree(rt)
        register struct rtentry *rt;
 {
        register struct ifaddr *ifa;
        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 == 0)
                panic("rtfree");
        rt->rt_refcnt--;
        if (rt->rt_refcnt <= 0 && (rt->rt_flags & RTF_UP) == 0) {
-               rttrash--;
                if (rt->rt_nodes->rn_flags & (RNF_ACTIVE | RNF_ROOT))
                        panic ("rtfree 2");
                if (rt->rt_nodes->rn_flags & (RNF_ACTIVE | RNF_ROOT))
                        panic ("rtfree 2");
-               free((caddr_t)rt, M_RTABLE);
+               rttrash--;
+               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;
+/*
+ * We are still debugging potential overfreeing of ifaddr's
+ */
+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.
 /*
  * Force a routing table entry to the specified
  * destination to go through the given gateway.
@@ -123,11 +159,12 @@ rtredirect(dst, gateway, netmask, flags, src, rtp)
        register struct rtentry *rt;
        int error = 0;
        short *stat = 0;
        register struct rtentry *rt;
        int error = 0;
        short *stat = 0;
+       struct rt_addrinfo info;
 
        /* verify the gateway is directly reachable */
        if (ifa_ifwithnet(gateway) == 0) {
                error = ENETUNREACH;
 
        /* verify the gateway is directly reachable */
        if (ifa_ifwithnet(gateway) == 0) {
                error = ENETUNREACH;
-               goto done;
+               goto out;
        }
        rt = rtalloc1(dst, 0);
        /*
        }
        rt = rtalloc1(dst, 0);
        /*
@@ -172,13 +209,10 @@ rtredirect(dst, gateway, netmask, flags, src, rtp)
                         * Smash the current notion of the gateway to
                         * this destination.  Should check about netmask!!!
                         */
                         * Smash the current notion of the gateway to
                         * this destination.  Should check about netmask!!!
                         */
-                       if (gateway->sa_len <= rt->rt_gateway->sa_len) {
-                               Bcopy(gateway, rt->rt_gateway, gateway->sa_len);
-                               rt->rt_flags |= RTF_MODIFIED;
-                               flags |= RTF_MODIFIED;
-                               stat = &rtstat.rts_newgateway;
-                       } else
-                               error = ENOSPC;
+                       rt->rt_flags |= RTF_MODIFIED;
+                       flags |= RTF_MODIFIED;
+                       stat = &rtstat.rts_newgateway;
+                       rt_setgate(rt, rt_key(rt), gateway);
                }
        } else
                error = EHOSTUNREACH;
                }
        } else
                error = EHOSTUNREACH;
@@ -189,11 +223,17 @@ done:
                else
                        rtfree(rt);
        }
                else
                        rtfree(rt);
        }
+out:
        if (error)
                rtstat.rts_badredirect++;
        if (error)
                rtstat.rts_badredirect++;
-       else
-               (stat && (*stat)++);
-       rt_missmsg(RTM_REDIRECT, dst, gateway, netmask, src, flags, error);
+       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);
 }
 
 /*
 }
 
 /*
@@ -259,9 +299,9 @@ rtioctl(req, data, p)
                }
        error =  rtrequest(req, &(entry->rt_dst), &(entry->rt_gateway), netmask,
                                entry->rt_flags, (struct rtentry **)0);
                }
        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),
+       /* rt_missmsg((req == RTM_ADD ? RTM_OLDADD : RTM_OLDDEL),
                   &(entry->rt_dst), &(entry->rt_gateway),
                   &(entry->rt_dst), &(entry->rt_gateway),
-                  netmask, SA(0), entry->rt_flags, error);
+                  netmask, SA(0), entry->rt_flags, error); */
        return (error);
 #endif
 }
        return (error);
 #endif
 }
@@ -271,7 +311,7 @@ ifa_ifwithroute(flags, dst, gateway)
 int    flags;
 struct sockaddr        *dst, *gateway;
 {
 int    flags;
 struct sockaddr        *dst, *gateway;
 {
-       struct ifaddr *ifa;
+       register struct ifaddr *ifa;
        if ((flags & RTF_GATEWAY) == 0) {
                /*
                 * If we are adding a route to an interface,
        if ((flags & RTF_GATEWAY) == 0) {
                /*
                 * If we are adding a route to an interface,
@@ -295,6 +335,20 @@ struct sockaddr    *dst, *gateway;
        }
        if (ifa == 0)
                ifa = ifa_ifwithnet(gateway);
        }
        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);
 }
 
        return (ifa);
 }
 
@@ -305,41 +359,40 @@ rtrequest(req, dst, gateway, netmask, flags, ret_nrt)
        struct sockaddr *dst, *gateway, *netmask;
        struct rtentry **ret_nrt;
 {
        struct sockaddr *dst, *gateway, *netmask;
        struct rtentry **ret_nrt;
 {
-       int s = splnet(), len, error = 0;
+       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;
        register struct rtentry *rt;
        register struct radix_node *rn;
        register struct radix_node_head *rnh;
        struct ifaddr *ifa, *ifa_ifwithdstaddr();
        struct sockaddr *ndst;
-       u_char af = dst->sa_family;
 #define senderr(x) { error = x ; goto bad; }
 
 #define senderr(x) { error = x ; goto bad; }
 
-       if (rtinits_done == 0)
-               rtinitheads();
-       for (rnh = radix_node_head; rnh && (af != rnh->rnh_af); )
-               rnh = rnh->rnh_next;
-       if (rnh == 0)
+       if ((rnh = rt_tables[dst->sa_family]) == 0)
                senderr(ESRCH);
        if (flags & RTF_HOST)
                netmask = 0;
        switch (req) {
        case RTM_DELETE:
                senderr(ESRCH);
        if (flags & RTF_HOST)
                netmask = 0;
        switch (req) {
        case RTM_DELETE:
-               if (ret_nrt && (rt = *ret_nrt)) {
-                       RTFREE(rt);
-                       *ret_nrt = 0;
-               }
-               if ((rn = rn_delete((caddr_t)dst, (caddr_t)netmask, 
+               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;
                                        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 ((ifa = rt->rt_ifa) && ifa->ifa_rtrequest)
                        ifa->ifa_rtrequest(RTM_DELETE, rt, SA(0));
                rttrash++;
-               if (rt->rt_refcnt <= 0)
+               if (ret_nrt)
+                       *ret_nrt = rt;
+               else if (rt->rt_refcnt <= 0) {
+                       rt->rt_refcnt++;
                        rtfree(rt);
                        rtfree(rt);
+               }
                break;
 
        case RTM_RESOLVE:
                break;
 
        case RTM_RESOLVE:
@@ -356,29 +409,32 @@ rtrequest(req, dst, gateway, netmask, flags, ret_nrt)
                if ((ifa = ifa_ifwithroute(flags, dst, gateway)) == 0)
                        senderr(ENETUNREACH);
        makeroute:
                if ((ifa = ifa_ifwithroute(flags, dst, gateway)) == 0)
                        senderr(ENETUNREACH);
        makeroute:
-               len = sizeof (*rt) + ROUNDUP(gateway->sa_len)
-                   + ROUNDUP(dst->sa_len);
-               R_Malloc(rt, struct rtentry *, len);
+               R_Malloc(rt, struct rtentry *, sizeof(*rt));
                if (rt == 0)
                        senderr(ENOBUFS);
                if (rt == 0)
                        senderr(ENOBUFS);
-               Bzero(rt, len);
-               ndst = (struct sockaddr *)(rt + 1);
+               Bzero(rt, sizeof(*rt));
+               rt->rt_flags = RTF_UP | flags;
+               if (rt_setgate(rt, dst, gateway)) {
+                       Free(rt);
+                       senderr(ENOBUFS);
+               }
+               ndst = rt_key(rt);
                if (netmask) {
                        rt_maskedcopy(dst, ndst, netmask);
                } else
                        Bcopy(dst, ndst, dst->sa_len);
                if (netmask) {
                        rt_maskedcopy(dst, ndst, netmask);
                } else
                        Bcopy(dst, ndst, dst->sa_len);
-               rn = rn_addroute((caddr_t)ndst, (caddr_t)netmask,
+               rn = rnh->rnh_add((caddr_t)ndst, (caddr_t)netmask,
                                        rnh->rnh_treetop, rt->rt_nodes);
                if (rn == 0) {
                                        rnh->rnh_treetop, rt->rt_nodes);
                if (rn == 0) {
-                       free((caddr_t)rt, M_RTABLE);
+                       if (rt->rt_gwroute)
+                               rtfree(rt->rt_gwroute);
+                       Free(rt_key(rt));
+                       Free(rt);
                        senderr(EEXIST);
                }
                        senderr(EEXIST);
                }
+               ifa->ifa_refcnt++;
                rt->rt_ifa = ifa;
                rt->rt_ifp = ifa->ifa_ifp;
                rt->rt_ifa = ifa;
                rt->rt_ifp = ifa->ifa_ifp;
-               rt->rt_flags = RTF_UP | flags;
-               rt->rt_gateway = (struct sockaddr *)
-                                       (rn->rn_key + ROUNDUP(dst->sa_len));
-               Bcopy(gateway, rt->rt_gateway, gateway->sa_len);
                if (req == RTM_RESOLVE)
                        rt->rt_rmx = (*ret_nrt)->rt_rmx; /* copy metrics */
                if (ifa->ifa_rtrequest)
                if (req == RTM_RESOLVE)
                        rt->rt_rmx = (*ret_nrt)->rt_rmx; /* copy metrics */
                if (ifa->ifa_rtrequest)
@@ -394,6 +450,39 @@ bad:
        return (error);
 }
 
        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;
 {
 rt_maskedcopy(src, dst, netmask)
 struct sockaddr *src, *dst, *netmask;
 {
@@ -424,15 +513,10 @@ rtinit(ifa, cmd, flags)
        register struct sockaddr *dst;
        register struct sockaddr *deldst;
        struct mbuf *m = 0;
        register struct sockaddr *dst;
        register struct sockaddr *deldst;
        struct mbuf *m = 0;
+       struct rtentry *nrt = 0;
        int error;
 
        dst = flags & RTF_HOST ? ifa->ifa_dstaddr : ifa->ifa_addr;
        int error;
 
        dst = flags & RTF_HOST ? ifa->ifa_dstaddr : ifa->ifa_addr;
-       if (ifa->ifa_flags & IFA_ROUTE) {
-               if ((rt = ifa->ifa_rt) && (rt->rt_flags & RTF_UP) == 0) {
-                       RTFREE(rt);
-                       ifa->ifa_rt = 0;
-               }
-       }
        if (cmd == RTM_DELETE) {
                if ((flags & RTF_HOST) == 0 && ifa->ifa_netmask) {
                        m = m_get(M_WAIT, MT_SONAME);
        if (cmd == RTM_DELETE) {
                if ((flags & RTF_HOST) == 0 && ifa->ifa_netmask) {
                        m = m_get(M_WAIT, MT_SONAME);
@@ -451,13 +535,31 @@ rtinit(ifa, cmd, flags)
                }
        }
        error = rtrequest(cmd, dst, ifa->ifa_addr, ifa->ifa_netmask,
                }
        }
        error = rtrequest(cmd, dst, ifa->ifa_addr, ifa->ifa_netmask,
-                       flags | ifa->ifa_flags, &ifa->ifa_rt);
+                       flags | ifa->ifa_flags, &nrt);
        if (m)
                (void) m_free(m);
        if (m)
                (void) m_free(m);
-       if (cmd == RTM_ADD && error == 0 && (rt = ifa->ifa_rt)
-                                               && rt->rt_ifa != ifa) {
-               rt->rt_ifa = ifa;
-               rt->rt_ifp = ifa->ifa_ifp;
+       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);
 }
        }
        return (error);
 }