rework interface metrics to invert perspective; other misc. fixes
[unix-history] / usr / src / sbin / routed / tables.c
index 915f7a6..6b2e18c 100644 (file)
@@ -1,6 +1,12 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.  The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ */
+
 #ifndef lint
 #ifndef lint
-static char sccsid[] = "@(#)tables.c   4.2 %G%";
-#endif
+static char sccsid[] = "@(#)tables.c   5.10 (Berkeley) %G%";
+#endif not lint
 
 /*
  * Routing Table Management Daemon
 
 /*
  * Routing Table Management Daemon
@@ -8,6 +14,7 @@ static char sccsid[] = "@(#)tables.c   4.2 %G%";
 #include "defs.h"
 #include <sys/ioctl.h>
 #include <errno.h>
 #include "defs.h"
 #include <sys/ioctl.h>
 #include <errno.h>
+#include <syslog.h>
 
 #ifndef DEBUG
 #define        DEBUG   0
 
 #ifndef DEBUG
 #define        DEBUG   0
@@ -24,15 +31,15 @@ rtlookup(dst)
 {
        register struct rt_entry *rt;
        register struct rthash *rh;
 {
        register struct rt_entry *rt;
        register struct rthash *rh;
-       register int hash;
+       register u_int hash;
        struct afhash h;
        int doinghost = 1;
 
        struct afhash h;
        int doinghost = 1;
 
-       if (dst->sa_family >= AF_MAX)
+       if (dst->sa_family >= af_max)
                return (0);
        (*afswitch[dst->sa_family].af_hash)(dst, &h);
        hash = h.afh_hosthash;
                return (0);
        (*afswitch[dst->sa_family].af_hash)(dst, &h);
        hash = h.afh_hosthash;
-       rh = &hosthash[hash % ROUTEHASHSIZ];
+       rh = &hosthash[hash & ROUTEHASHMASK];
 again:
        for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
                if (rt->rt_hash != hash)
 again:
        for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
                if (rt->rt_hash != hash)
@@ -43,7 +50,7 @@ again:
        if (doinghost) {
                doinghost = 0;
                hash = h.afh_nethash;
        if (doinghost) {
                doinghost = 0;
                hash = h.afh_nethash;
-               rh = &nethash[hash % ROUTEHASHSIZ];
+               rh = &nethash[hash & ROUTEHASHMASK];
                goto again;
        }
        return (0);
                goto again;
        }
        return (0);
@@ -58,16 +65,16 @@ rtfind(dst)
 {
        register struct rt_entry *rt;
        register struct rthash *rh;
 {
        register struct rt_entry *rt;
        register struct rthash *rh;
-       register int hash;
+       register u_int hash;
        struct afhash h;
        int af = dst->sa_family;
        int doinghost = 1, (*match)();
 
        struct afhash h;
        int af = dst->sa_family;
        int doinghost = 1, (*match)();
 
-       if (af >= AF_MAX)
+       if (af >= af_max)
                return (0);
        (*afswitch[af].af_hash)(dst, &h);
        hash = h.afh_hosthash;
                return (0);
        (*afswitch[af].af_hash)(dst, &h);
        hash = h.afh_hosthash;
-       rh = &hosthash[hash % ROUTEHASHSIZ];
+       rh = &hosthash[hash & ROUTEHASHMASK];
 
 again:
        for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
 
 again:
        for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
@@ -85,7 +92,7 @@ again:
        if (doinghost) {
                doinghost = 0;
                hash = h.afh_nethash;
        if (doinghost) {
                doinghost = 0;
                hash = h.afh_nethash;
-               rh = &nethash[hash % ROUTEHASHSIZ];
+               rh = &nethash[hash & ROUTEHASHMASK];
                match = afswitch[af].af_netmatch;
                goto again;
        }
                match = afswitch[af].af_netmatch;
                goto again;
        }
@@ -99,18 +106,26 @@ rtadd(dst, gate, metric, state)
        struct afhash h;
        register struct rt_entry *rt;
        struct rthash *rh;
        struct afhash h;
        register struct rt_entry *rt;
        struct rthash *rh;
-       int af = dst->sa_family, flags, hash;
+       int af = dst->sa_family, flags;
+       u_int hash;
 
 
-       if (af >= AF_MAX)
+       if (af >= af_max)
                return;
        (*afswitch[af].af_hash)(dst, &h);
                return;
        (*afswitch[af].af_hash)(dst, &h);
-       flags = (*afswitch[af].af_checkhost)(dst) ? RTF_HOST : 0;
+       flags = (*afswitch[af].af_rtflags)(dst);
+       /*
+        * Subnet flag isn't visible to kernel, move to state.  XXX
+        */
+       if (flags & RTF_SUBNET) {
+               state |= RTS_SUBNET;
+               flags &= ~RTF_SUBNET;
+       }
        if (flags & RTF_HOST) {
                hash = h.afh_hosthash;
        if (flags & RTF_HOST) {
                hash = h.afh_hosthash;
-               rh = &hosthash[hash % ROUTEHASHSIZ];
+               rh = &hosthash[hash & ROUTEHASHMASK];
        } else {
                hash = h.afh_nethash;
        } else {
                hash = h.afh_nethash;
-               rh = &nethash[hash % ROUTEHASHSIZ];
+               rh = &nethash[hash & ROUTEHASHMASK];
        }
        rt = (struct rt_entry *)malloc(sizeof (*rt));
        if (rt == 0)
        }
        rt = (struct rt_entry *)malloc(sizeof (*rt));
        if (rt == 0)
@@ -118,17 +133,36 @@ rtadd(dst, gate, metric, state)
        rt->rt_hash = hash;
        rt->rt_dst = *dst;
        rt->rt_router = *gate;
        rt->rt_hash = hash;
        rt->rt_dst = *dst;
        rt->rt_router = *gate;
-       rt->rt_metric = metric;
        rt->rt_timer = 0;
        rt->rt_flags = RTF_UP | flags;
        rt->rt_state = state | RTS_CHANGED;
        rt->rt_timer = 0;
        rt->rt_flags = RTF_UP | flags;
        rt->rt_state = state | RTS_CHANGED;
-       rt->rt_ifp = if_ifwithnet(&rt->rt_router);
-       if (metric)
+       rt->rt_ifp = if_ifwithdstaddr(&rt->rt_router);
+       if (rt->rt_ifp == 0)
+               rt->rt_ifp = if_ifwithnet(&rt->rt_router);
+       if ((state & RTS_INTERFACE) == 0)
                rt->rt_flags |= RTF_GATEWAY;
                rt->rt_flags |= RTF_GATEWAY;
+       rt->rt_metric = metric;
        insque(rt, rh);
        TRACE_ACTION(ADD, rt);
        insque(rt, rh);
        TRACE_ACTION(ADD, rt);
-       if (install && ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0)
+       /*
+        * If the ioctl fails because the gateway is unreachable
+        * from this host, discard the entry.  This should only
+        * occur because of an incorrect entry in /etc/gateways.
+        */
+       if (install && (rt->rt_state & (RTS_INTERNAL | RTS_EXTERNAL)) == 0 &&
+           ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0) {
+               if (errno != EEXIST && gate->sa_family < af_max)
+                       syslog(LOG_ERR,
+                       "adding route to net/host %s through gateway %s: %m\n",
+                          (*afswitch[dst->sa_family].af_format)(dst),
+                          (*afswitch[gate->sa_family].af_format)(gate));
                perror("SIOCADDRT");
                perror("SIOCADDRT");
+               if (errno == ENETUNREACH) {
+                       TRACE_ACTION(DELETE, rt);
+                       remque(rt);
+                       free((char *)rt);
+               }
+       }
 }
 
 rtchange(rt, gate, metric)
 }
 
 rtchange(rt, gate, metric)
@@ -136,41 +170,118 @@ rtchange(rt, gate, metric)
        struct sockaddr *gate;
        short metric;
 {
        struct sockaddr *gate;
        short metric;
 {
-       int doioctl = 0, metricchanged = 0;
+       int add = 0, delete = 0, metricchanged = 0;
        struct rtentry oldroute;
 
        struct rtentry oldroute;
 
-       if (!equal(&rt->rt_router, gate))
-               doioctl++;
-       if (metric != rt->rt_metric) {
-               metricchanged++;
-               rt->rt_metric = metric;
+       if ((rt->rt_state & RTS_INTERNAL) == 0) {
+               /*
+                * If changing to different router, we need to add
+                * new route and delete old one if in the kernel.
+                * If the router is the same, we need to delete
+                * the route if has become unreachable, or re-add
+                * it if it had been unreachable.
+                */
+               if (!equal(&rt->rt_router, gate)) {
+                       add++;
+                       if (rt->rt_metric != HOPCNT_INFINITY)
+                               delete++;
+               } else if (metric == HOPCNT_INFINITY)
+                       delete++;
+               else if (rt->rt_metric == HOPCNT_INFINITY)
+                       add++;
        }
        }
-       if (doioctl || metricchanged) {
-               TRACE_ACTION(CHANGE, rt);
-               rt->rt_state |= RTS_CHANGED;
+       if (metric != rt->rt_metric)
+               metricchanged++;
+       if (delete || metricchanged)
+               TRACE_ACTION(CHANGE FROM, rt);
+       if ((rt->rt_state & RTS_INTERFACE) && delete) {
+               rt->rt_state &= ~RTS_INTERFACE;
+               if (add)
+                       rt->rt_flags |= RTF_GATEWAY;
+               if (metric > rt->rt_metric && delete &&
+                   (rt->rt_state & RTS_INTERNAL) == 0)
+                       syslog(LOG_ERR,
+                               "changing route from interface %s (timed out)",
+                               rt->rt_ifp->int_name);
        }
        }
-       if (doioctl) {
+       if (delete)
                oldroute = rt->rt_rt;
                oldroute = rt->rt_rt;
+       if (add) {
                rt->rt_router = *gate;
                rt->rt_router = *gate;
-               if (install) {
-                       if (ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0)
-                               perror("SIOCADDRT");
-                       if (ioctl(s, SIOCDELRT, (char *)&oldroute) < 0)
-                               perror("SIOCDELRT");
-               }
+               rt->rt_ifp = if_ifwithdstaddr(&rt->rt_router);
+               if (rt->rt_ifp == 0)
+                       rt->rt_ifp = if_ifwithnet(&rt->rt_router);
        }
        }
+       rt->rt_metric = metric;
+       rt->rt_state |= RTS_CHANGED;
+       if (add || metricchanged)
+               TRACE_ACTION(CHANGE TO, rt);
+       if (add && install)
+               if (ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0)
+                       perror("SIOCADDRT");
+       if (delete && install)
+               if (ioctl(s, SIOCDELRT, (char *)&oldroute) < 0)
+                       perror("SIOCDELRT");
 }
 
 rtdelete(rt)
        struct rt_entry *rt;
 {
 }
 
 rtdelete(rt)
        struct rt_entry *rt;
 {
+
+       if ((rt->rt_state & (RTS_INTERFACE|RTS_INTERNAL)) == RTS_INTERFACE)
+               syslog(LOG_ERR, "deleting route to interface %s (timed out)",
+                       rt->rt_ifp->int_name);
        TRACE_ACTION(DELETE, rt);
        TRACE_ACTION(DELETE, rt);
-       if (install && ioctl(s, SIOCDELRT, (char *)&rt->rt_rt))
+       if (install && (rt->rt_state & (RTS_INTERNAL | RTS_EXTERNAL)) == 0 &&
+           ioctl(s, SIOCDELRT, (char *)&rt->rt_rt))
                perror("SIOCDELRT");
        remque(rt);
        free((char *)rt);
 }
 
                perror("SIOCDELRT");
        remque(rt);
        free((char *)rt);
 }
 
+rtdeleteall(s)
+       int s;
+{
+       register struct rthash *rh;
+       register struct rt_entry *rt;
+       struct rthash *base = hosthash;
+       int doinghost = 1;
+
+again:
+       for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) {
+               rt = rh->rt_forw;
+               for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
+                       if (rt->rt_state & RTS_INTERFACE)
+                               continue;
+                       TRACE_ACTION(DELETE, rt);
+                       if ((rt->rt_state & (RTS_INTERNAL|RTS_EXTERNAL)) == 0 &&
+                           ioctl(s, SIOCDELRT, (char *)&rt->rt_rt))
+                               perror("SIOCDELRT");
+               }
+       }
+       if (doinghost) {
+               doinghost = 0;
+               base = nethash;
+               goto again;
+       }
+       hup(s);
+}
+
+/*
+ * If we have an interface to the wide, wide world,
+ * add an entry for an Internet default route (wildcard) to the internal
+ * tables and advertise it.  This route is not added to the kernel routes,
+ * but this entry prevents us from listening to other people's defaults
+ * and installing them in the kernel here.
+ */
+rtdefault()
+{
+       extern struct sockaddr inet_default;
+
+       rtadd(&inet_default, &inet_default, 0,
+               RTS_CHANGED | RTS_PASSIVE | RTS_INTERNAL);
+}
+
 rtinit()
 {
        register struct rthash *rh;
 rtinit()
 {
        register struct rthash *rh;